mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-25 15:51:34 +00:00
Add support for noise IX, XX, and IK handshakes. (#888)
This commit is contained in:
69
protocols/noise/src/error.rs
Normal file
69
protocols/noise/src/error.rs
Normal file
@ -0,0 +1,69 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use snow::SnowError;
|
||||
use std::{error::Error, fmt, io};
|
||||
|
||||
/// libp2p_noise error type.
|
||||
#[derive(Debug)]
|
||||
pub enum NoiseError {
|
||||
/// An I/O error has been encountered.
|
||||
Io(io::Error),
|
||||
/// An noise framework error has been encountered.
|
||||
Noise(SnowError),
|
||||
/// A public key is invalid.
|
||||
InvalidKey,
|
||||
#[doc(hidden)]
|
||||
__Nonexhaustive
|
||||
}
|
||||
|
||||
impl fmt::Display for NoiseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
NoiseError::Io(e) => write!(f, "{}", e),
|
||||
NoiseError::Noise(e) => write!(f, "{}", e),
|
||||
NoiseError::InvalidKey => f.write_str("invalid public key"),
|
||||
NoiseError::__Nonexhaustive => f.write_str("__Nonexhaustive")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for NoiseError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
match self {
|
||||
NoiseError::Io(e) => Some(e),
|
||||
NoiseError::Noise(_) => None, // TODO: `SnowError` should implement `Error`.
|
||||
NoiseError::InvalidKey => None,
|
||||
NoiseError::__Nonexhaustive => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for NoiseError {
|
||||
fn from(e: io::Error) -> Self {
|
||||
NoiseError::Io(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SnowError> for NoiseError {
|
||||
fn from(e: SnowError) -> Self {
|
||||
NoiseError::Noise(e)
|
||||
}
|
||||
}
|
375
protocols/noise/src/io.rs
Normal file
375
protocols/noise/src/io.rs
Normal file
@ -0,0 +1,375 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use crate::{NoiseError, keys::{PublicKey, Curve25519}, util::to_array};
|
||||
use futures::Poll;
|
||||
use log::{debug, trace};
|
||||
use snow;
|
||||
use std::{fmt, io};
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
const MAX_NOISE_PKG_LEN: usize = 65535;
|
||||
const MAX_WRITE_BUF_LEN: usize = 16384;
|
||||
const TOTAL_BUFFER_LEN: usize = 2 * MAX_NOISE_PKG_LEN + 3 * MAX_WRITE_BUF_LEN;
|
||||
|
||||
/// A single `Buffer` contains multiple non-overlapping byte buffers.
|
||||
struct Buffer {
|
||||
inner: Box<[u8; TOTAL_BUFFER_LEN]>
|
||||
}
|
||||
|
||||
/// A mutable borrow of all byte byffers, backed by `Buffer`.
|
||||
struct BufferBorrow<'a> {
|
||||
read: &'a mut [u8],
|
||||
read_crypto: &'a mut [u8],
|
||||
write: &'a mut [u8],
|
||||
write_crypto: &'a mut [u8]
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
/// Create a mutable borrow by splitting the buffer slice.
|
||||
fn borrow_mut(&mut self) -> BufferBorrow {
|
||||
let (r, w) = self.inner.split_at_mut(2 * MAX_NOISE_PKG_LEN);
|
||||
let (read, read_crypto) = r.split_at_mut(MAX_NOISE_PKG_LEN);
|
||||
let (write, write_crypto) = w.split_at_mut(MAX_WRITE_BUF_LEN);
|
||||
BufferBorrow { read, read_crypto, write, write_crypto }
|
||||
}
|
||||
}
|
||||
|
||||
/// A type used during handshake phase, exchanging key material with the remote.
|
||||
pub(super) struct Handshake<T>(NoiseOutput<T>);
|
||||
|
||||
impl<T> Handshake<T> {
|
||||
pub(super) fn new(io: T, session: snow::Session) -> Self {
|
||||
Handshake(NoiseOutput::new(io, session))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + AsyncWrite> Handshake<T> {
|
||||
/// Send handshake message to remote.
|
||||
pub(super) fn send(&mut self) -> Poll<(), io::Error> {
|
||||
Ok(self.0.poll_write(&[])?.map(|_| ()))
|
||||
}
|
||||
|
||||
/// Flush handshake message to remote.
|
||||
pub(super) fn flush(&mut self) -> Poll<(), io::Error> {
|
||||
self.0.poll_flush()
|
||||
}
|
||||
|
||||
/// Receive handshake message from remote.
|
||||
pub(super) fn receive(&mut self) -> Poll<(), io::Error> {
|
||||
Ok(self.0.poll_read(&mut [])?.map(|_| ()))
|
||||
}
|
||||
|
||||
/// Finish the handshake.
|
||||
///
|
||||
/// This turns the noise session into handshake mode and returns the remote's static
|
||||
/// public key as well as the established session for further communication.
|
||||
pub(super) fn finish(self) -> Result<(PublicKey<Curve25519>, NoiseOutput<T>), NoiseError> {
|
||||
let s = self.0.session.into_transport_mode()?;
|
||||
let p = s.get_remote_static()
|
||||
.ok_or(NoiseError::InvalidKey)
|
||||
.and_then(to_array)
|
||||
.map(PublicKey::new)?;
|
||||
Ok((p, NoiseOutput { session: s, .. self.0 }))
|
||||
}
|
||||
}
|
||||
|
||||
/// A noise session to a remote.
|
||||
pub struct NoiseOutput<T> {
|
||||
io: T,
|
||||
session: snow::Session,
|
||||
buffer: Buffer,
|
||||
read_state: ReadState,
|
||||
write_state: WriteState
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for NoiseOutput<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("NoiseOutput")
|
||||
.field("read_state", &self.read_state)
|
||||
.field("write_state", &self.write_state)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> NoiseOutput<T> {
|
||||
fn new(io: T, session: snow::Session) -> Self {
|
||||
NoiseOutput {
|
||||
io, session,
|
||||
buffer: Buffer { inner: Box::new([0; TOTAL_BUFFER_LEN]) },
|
||||
read_state: ReadState::Init,
|
||||
write_state: WriteState::Init
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The various states of reading a noise session transitions through.
|
||||
#[derive(Debug)]
|
||||
enum ReadState {
|
||||
/// initial state
|
||||
Init,
|
||||
/// read encrypted frame data
|
||||
ReadData { len: usize, off: usize },
|
||||
/// copy decrypted frame data
|
||||
CopyData { len: usize, off: usize },
|
||||
/// end of file has been reached (terminal state)
|
||||
/// The associated result signals if the EOF was unexpected or not.
|
||||
Eof(Result<(), ()>),
|
||||
/// decryption error (terminal state)
|
||||
DecErr
|
||||
}
|
||||
|
||||
/// The various states of writing a noise session transitions through.
|
||||
#[derive(Debug)]
|
||||
enum WriteState {
|
||||
/// initial state
|
||||
Init,
|
||||
/// accumulate write data
|
||||
BufferData { off: usize },
|
||||
/// write frame length
|
||||
WriteLen { len: usize },
|
||||
/// write out encrypted data
|
||||
WriteData { len: usize, off: usize },
|
||||
/// end of file has been reached (terminal state)
|
||||
Eof,
|
||||
/// encryption error (terminal state)
|
||||
EncErr
|
||||
}
|
||||
|
||||
impl<T: io::Read> io::Read for NoiseOutput<T> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let buffer = self.buffer.borrow_mut();
|
||||
loop {
|
||||
trace!("read state: {:?}", self.read_state);
|
||||
match self.read_state {
|
||||
ReadState::Init => {
|
||||
let n = match read_frame_len(&mut self.io)? {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
trace!("read: eof");
|
||||
self.read_state = ReadState::Eof(Ok(()));
|
||||
return Ok(0)
|
||||
}
|
||||
};
|
||||
trace!("read: next frame len = {}", n);
|
||||
if n == 0 {
|
||||
trace!("read: empty frame");
|
||||
continue
|
||||
}
|
||||
self.read_state = ReadState::ReadData { len: usize::from(n), off: 0 }
|
||||
}
|
||||
ReadState::ReadData { len, ref mut off } => {
|
||||
let n = self.io.read(&mut buffer.read[*off .. len])?;
|
||||
trace!("read: read {}/{} bytes", *off + n, len);
|
||||
if n == 0 {
|
||||
trace!("read: eof");
|
||||
self.read_state = ReadState::Eof(Err(()));
|
||||
return Err(io::ErrorKind::UnexpectedEof.into())
|
||||
}
|
||||
*off += n;
|
||||
if len == *off {
|
||||
trace!("read: decrypting {} bytes", len);
|
||||
if let Ok(n) = self.session.read_message(&buffer.read[.. len], buffer.read_crypto) {
|
||||
trace!("read: payload len = {} bytes", n);
|
||||
self.read_state = ReadState::CopyData { len: n, off: 0 }
|
||||
} else {
|
||||
debug!("decryption error");
|
||||
self.read_state = ReadState::DecErr;
|
||||
return Err(io::ErrorKind::InvalidData.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
ReadState::CopyData { len, ref mut off } => {
|
||||
let n = std::cmp::min(len - *off, buf.len());
|
||||
buf[.. n].copy_from_slice(&buffer.read_crypto[*off .. *off + n]);
|
||||
trace!("read: copied {}/{} bytes", *off + n, len);
|
||||
*off += n;
|
||||
if len == *off {
|
||||
self.read_state = ReadState::Init
|
||||
}
|
||||
return Ok(n)
|
||||
}
|
||||
ReadState::Eof(Ok(())) => {
|
||||
trace!("read: eof");
|
||||
return Ok(0)
|
||||
}
|
||||
ReadState::Eof(Err(())) => {
|
||||
trace!("read: eof (unexpected)");
|
||||
return Err(io::ErrorKind::UnexpectedEof.into())
|
||||
}
|
||||
ReadState::DecErr => return Err(io::ErrorKind::InvalidData.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: io::Write> io::Write for NoiseOutput<T> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let buffer = self.buffer.borrow_mut();
|
||||
loop {
|
||||
trace!("write state: {:?}", self.write_state);
|
||||
match self.write_state {
|
||||
WriteState::Init => {
|
||||
self.write_state = WriteState::BufferData { off: 0 }
|
||||
}
|
||||
WriteState::BufferData { ref mut off } => {
|
||||
let n = std::cmp::min(MAX_WRITE_BUF_LEN - *off, buf.len());
|
||||
buffer.write[*off .. *off + n].copy_from_slice(&buf[.. n]);
|
||||
trace!("write: buffered {} bytes", *off + n);
|
||||
*off += n;
|
||||
if *off == MAX_WRITE_BUF_LEN {
|
||||
trace!("write: encrypting {} bytes", *off);
|
||||
if let Ok(n) = self.session.write_message(buffer.write, buffer.write_crypto) {
|
||||
trace!("write: cipher text len = {} bytes", n);
|
||||
self.write_state = WriteState::WriteLen { len: n }
|
||||
} else {
|
||||
debug!("encryption error");
|
||||
self.write_state = WriteState::EncErr;
|
||||
return Err(io::ErrorKind::InvalidData.into())
|
||||
}
|
||||
}
|
||||
return Ok(n)
|
||||
}
|
||||
WriteState::WriteLen { len } => {
|
||||
trace!("write: writing len ({})", len);
|
||||
if !write_frame_len(&mut self.io, len as u16)? {
|
||||
trace!("write: eof");
|
||||
self.write_state = WriteState::Eof;
|
||||
return Err(io::ErrorKind::WriteZero.into())
|
||||
}
|
||||
self.write_state = WriteState::WriteData { len, off: 0 }
|
||||
}
|
||||
WriteState::WriteData { len, ref mut off } => {
|
||||
let n = self.io.write(&buffer.write_crypto[*off .. len])?;
|
||||
trace!("write: wrote {}/{} bytes", *off + n, len);
|
||||
if n == 0 {
|
||||
trace!("write: eof");
|
||||
self.write_state = WriteState::Eof;
|
||||
return Err(io::ErrorKind::WriteZero.into())
|
||||
}
|
||||
*off += n;
|
||||
if len == *off {
|
||||
trace!("write: finished writing {} bytes", len);
|
||||
self.write_state = WriteState::Init
|
||||
}
|
||||
}
|
||||
WriteState::Eof => {
|
||||
trace!("write: eof");
|
||||
return Err(io::ErrorKind::WriteZero.into())
|
||||
}
|
||||
WriteState::EncErr => return Err(io::ErrorKind::InvalidData.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
let buffer = self.buffer.borrow_mut();
|
||||
loop {
|
||||
match self.write_state {
|
||||
WriteState::Init => return Ok(()),
|
||||
WriteState::BufferData { off } => {
|
||||
trace!("flush: encrypting {} bytes", off);
|
||||
if let Ok(n) = self.session.write_message(&buffer.write[.. off], buffer.write_crypto) {
|
||||
trace!("flush: cipher text len = {} bytes", n);
|
||||
self.write_state = WriteState::WriteLen { len: n }
|
||||
} else {
|
||||
debug!("encryption error");
|
||||
self.write_state = WriteState::EncErr;
|
||||
return Err(io::ErrorKind::InvalidData.into())
|
||||
}
|
||||
}
|
||||
WriteState::WriteLen { len } => {
|
||||
trace!("flush: writing len ({})", len);
|
||||
if !write_frame_len(&mut self.io, len as u16)? {
|
||||
trace!("write: eof");
|
||||
self.write_state = WriteState::Eof;
|
||||
return Err(io::ErrorKind::WriteZero.into())
|
||||
}
|
||||
self.write_state = WriteState::WriteData { len, off: 0 }
|
||||
}
|
||||
WriteState::WriteData { len, ref mut off } => {
|
||||
let n = self.io.write(&buffer.write_crypto[*off .. len])?;
|
||||
trace!("flush: wrote {}/{} bytes", *off + n, len);
|
||||
if n == 0 {
|
||||
trace!("flush: eof");
|
||||
self.write_state = WriteState::Eof;
|
||||
return Err(io::ErrorKind::WriteZero.into())
|
||||
}
|
||||
*off += n;
|
||||
if len == *off {
|
||||
trace!("flush: finished writing {} bytes", len);
|
||||
self.write_state = WriteState::Init;
|
||||
return Ok(())
|
||||
}
|
||||
}
|
||||
WriteState::Eof => {
|
||||
trace!("flush: eof");
|
||||
return Err(io::ErrorKind::WriteZero.into())
|
||||
}
|
||||
WriteState::EncErr => return Err(io::ErrorKind::InvalidData.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead> AsyncRead for NoiseOutput<T> {}
|
||||
|
||||
impl<T: AsyncWrite> AsyncWrite for NoiseOutput<T> {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
self.io.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
/// Read 2 bytes as frame length.
|
||||
///
|
||||
/// Returns `None` if EOF has been encountered.
|
||||
fn read_frame_len<R: io::Read>(io: &mut R) -> io::Result<Option<u16>> {
|
||||
let mut buf = [0, 0];
|
||||
let mut off = 0;
|
||||
loop {
|
||||
let n = io.read(&mut buf[off ..])?;
|
||||
if n == 0 {
|
||||
return Ok(None)
|
||||
}
|
||||
off += n;
|
||||
if off == 2 {
|
||||
return Ok(Some(u16::from_be_bytes(buf)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Write frame length.
|
||||
///
|
||||
/// Returns `false` if EOF has been encountered.
|
||||
fn write_frame_len<W: io::Write>(io: &mut W, len: u16) -> io::Result<bool> {
|
||||
let buf = len.to_be_bytes();
|
||||
let mut off = 0;
|
||||
loop {
|
||||
let n = io.write(&buf[off ..])?;
|
||||
if n == 0 {
|
||||
return Ok(false)
|
||||
}
|
||||
off += n;
|
||||
if off == 2 {
|
||||
return Ok(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
173
protocols/noise/src/keys.rs
Normal file
173
protocols/noise/src/keys.rs
Normal file
@ -0,0 +1,173 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use crate::NoiseError;
|
||||
use curve25519_dalek::{
|
||||
constants::{X25519_BASEPOINT, ED25519_BASEPOINT_POINT},
|
||||
edwards::CompressedEdwardsY,
|
||||
montgomery::MontgomeryPoint,
|
||||
scalar::Scalar
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Curve25519 {}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Ed25519 {}
|
||||
|
||||
/// ECC public key.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct PublicKey<T> {
|
||||
bytes: [u8; 32],
|
||||
_marker: std::marker::PhantomData<T>
|
||||
}
|
||||
|
||||
impl<T> PublicKey<T> {
|
||||
pub(crate) fn new(bytes: [u8; 32]) -> Self {
|
||||
PublicKey { bytes, _marker: std::marker::PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl PublicKey<Ed25519> {
|
||||
/// Attempt to construct an Ed25519 public key from a `libp2p_core::PublicKey`.
|
||||
pub fn from_core(key: &libp2p_core::PublicKey) -> Result<PublicKey<Ed25519>, NoiseError> {
|
||||
if let libp2p_core::PublicKey::Ed25519(k) = key {
|
||||
if k.len() == 32 {
|
||||
let cp = CompressedEdwardsY::from_slice(k);
|
||||
cp.decompress().ok_or(NoiseError::InvalidKey)?;
|
||||
return Ok(PublicKey::new(cp.0))
|
||||
}
|
||||
}
|
||||
Err(NoiseError::InvalidKey)
|
||||
}
|
||||
|
||||
/// Convert this Edwards 25519 public key into a Curve 25519 public key.
|
||||
pub fn into_curve_25519(self) -> PublicKey<Curve25519> {
|
||||
let m = CompressedEdwardsY(self.bytes)
|
||||
.decompress()
|
||||
.expect("Constructing a PublicKey<Ed25519> ensures this is a valid y-coordinate.")
|
||||
.to_montgomery();
|
||||
PublicKey::new(m.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<[u8]> for PublicKey<T> {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.bytes
|
||||
}
|
||||
}
|
||||
|
||||
/// ECC secret key.
|
||||
#[derive(Clone)]
|
||||
pub struct SecretKey {
|
||||
scalar: Scalar
|
||||
}
|
||||
|
||||
impl SecretKey {
|
||||
pub(crate) fn new(bytes: [u8; 32]) -> Self {
|
||||
SecretKey {
|
||||
scalar: Scalar::from_bytes_mod_order(bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SecretKey {
|
||||
/// Compute the public key from this secret key.
|
||||
///
|
||||
/// Performs scalar multiplication with Curve25519's base point.
|
||||
pub fn public(&self) -> PublicKey<Curve25519> {
|
||||
PublicKey::new(self.x25519(&X25519_BASEPOINT).0)
|
||||
}
|
||||
|
||||
/// Elliptic-Curve Diffie-Hellman key agreement.
|
||||
///
|
||||
/// Performs scalar multiplication with the given public key.
|
||||
pub fn ecdh(&self, pk: &PublicKey<Curve25519>) -> [u8; 32] {
|
||||
self.x25519(&MontgomeryPoint(pk.bytes)).0
|
||||
}
|
||||
|
||||
/// The actual scalar multiplication with a u-coordinate of Curve25519.
|
||||
fn x25519(&self, p: &MontgomeryPoint) -> MontgomeryPoint {
|
||||
let mut s = self.scalar.to_bytes();
|
||||
// Cf. RFC 7748 section 5 (page 7)
|
||||
s[0] &= 248;
|
||||
s[31] &= 127;
|
||||
s[31] |= 64;
|
||||
Scalar::from_bits(s) * p
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for SecretKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.scalar.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
/// ECC secret and public key.
|
||||
#[derive(Clone)]
|
||||
pub struct Keypair<T> {
|
||||
secret: SecretKey,
|
||||
public: PublicKey<T>
|
||||
}
|
||||
|
||||
impl<T> Keypair<T> {
|
||||
/// Create a new keypair.
|
||||
pub fn new(s: SecretKey, p: PublicKey<T>) -> Self {
|
||||
Keypair { secret: s, public: p }
|
||||
}
|
||||
|
||||
/// Access this keypair's secret key.
|
||||
pub fn secret(&self) -> &SecretKey {
|
||||
&self.secret
|
||||
}
|
||||
|
||||
/// Access this keypair's public key.
|
||||
pub fn public(&self) -> &PublicKey<T> {
|
||||
&self.public
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Into<(SecretKey, PublicKey<T>)> for Keypair<T> {
|
||||
fn into(self) -> (SecretKey, PublicKey<T>) {
|
||||
(self.secret, self.public)
|
||||
}
|
||||
}
|
||||
|
||||
impl Keypair<Curve25519> {
|
||||
/// Create a fresh Curve25519 keypair.
|
||||
pub fn gen_curve25519() -> Self {
|
||||
let secret = SecretKey {
|
||||
scalar: Scalar::random(&mut rand::thread_rng())
|
||||
};
|
||||
let public = secret.public();
|
||||
Keypair { secret, public }
|
||||
}
|
||||
}
|
||||
|
||||
impl Keypair<Ed25519> {
|
||||
/// Create a fresh Ed25519 keypair.
|
||||
pub fn gen_ed25519() -> Self {
|
||||
let scalar = Scalar::random(&mut rand::thread_rng());
|
||||
let public = PublicKey::new((scalar * ED25519_BASEPOINT_POINT).compress().0);
|
||||
let secret = SecretKey { scalar };
|
||||
Keypair { secret, public }
|
||||
}
|
||||
}
|
||||
|
290
protocols/noise/src/lib.rs
Normal file
290
protocols/noise/src/lib.rs
Normal file
@ -0,0 +1,290 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
//! [Noise protocol framework][noise] support for libp2p.
|
||||
//!
|
||||
//! This crate provides `libp2p_core::InboundUpgrade` and `libp2p_core::OutboundUpgrade`
|
||||
//! implementations for various noise handshake patterns, currently IK, IX, and XX.
|
||||
//!
|
||||
//! All upgrades produce as output a pair, consisting of the remote's static public key
|
||||
//! and a `NoiseOutput` which represents the established cryptographic session with the
|
||||
//! remote, implementing `tokio_io::AsyncRead` and `tokio_io::AsyncWrite`.
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! Example:
|
||||
//!
|
||||
//! ```
|
||||
//! use libp2p_core::Transport;
|
||||
//! use libp2p_tcp::TcpConfig;
|
||||
//! use libp2p_noise::{Keypair, NoiseConfig};
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let keypair = Keypair::gen_curve25519();
|
||||
//! let transport = TcpConfig::new().with_upgrade(NoiseConfig::xx(keypair));
|
||||
//! // ...
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! [noise]: http://noiseprotocol.org/
|
||||
|
||||
mod error;
|
||||
mod io;
|
||||
mod keys;
|
||||
mod util;
|
||||
|
||||
pub mod rt1;
|
||||
pub mod rt15;
|
||||
|
||||
pub use error::NoiseError;
|
||||
pub use io::NoiseOutput;
|
||||
pub use keys::{Curve25519, PublicKey, SecretKey, Keypair};
|
||||
|
||||
use libp2p_core::{UpgradeInfo, InboundUpgrade, OutboundUpgrade};
|
||||
use lazy_static::lazy_static;
|
||||
use snow;
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use util::Resolver;
|
||||
|
||||
lazy_static! {
|
||||
static ref PARAMS_IK: snow::params::NoiseParams = "Noise_IK_25519_ChaChaPoly_SHA256"
|
||||
.parse()
|
||||
.expect("valid pattern");
|
||||
|
||||
static ref PARAMS_IX: snow::params::NoiseParams = "Noise_IX_25519_ChaChaPoly_SHA256"
|
||||
.parse()
|
||||
.expect("valid pattern");
|
||||
|
||||
static ref PARAMS_XX: snow::params::NoiseParams = "Noise_XX_25519_ChaChaPoly_SHA256"
|
||||
.parse()
|
||||
.expect("valid pattern");
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum IK {}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum IX {}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum XX {}
|
||||
|
||||
/// The protocol upgrade configuration.
|
||||
#[derive(Clone)]
|
||||
pub struct NoiseConfig<P, R = ()> {
|
||||
keypair: Keypair<Curve25519>,
|
||||
params: snow::params::NoiseParams,
|
||||
remote: R,
|
||||
_marker: std::marker::PhantomData<P>
|
||||
}
|
||||
|
||||
impl NoiseConfig<IX> {
|
||||
/// Create a new `NoiseConfig` for the IX handshake pattern.
|
||||
pub fn ix(kp: Keypair<Curve25519>) -> Self {
|
||||
NoiseConfig {
|
||||
keypair: kp,
|
||||
params: PARAMS_IX.clone(),
|
||||
remote: (),
|
||||
_marker: std::marker::PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NoiseConfig<XX> {
|
||||
/// Create a new `NoiseConfig` for the XX handshake pattern.
|
||||
pub fn xx(kp: Keypair<Curve25519>) -> Self {
|
||||
NoiseConfig {
|
||||
keypair: kp,
|
||||
params: PARAMS_XX.clone(),
|
||||
remote: (),
|
||||
_marker: std::marker::PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NoiseConfig<IK> {
|
||||
/// Create a new `NoiseConfig` for the IK handshake pattern (recipient side).
|
||||
pub fn ik_listener(kp: Keypair<Curve25519>) -> Self {
|
||||
NoiseConfig {
|
||||
keypair: kp,
|
||||
params: PARAMS_IK.clone(),
|
||||
remote: (),
|
||||
_marker: std::marker::PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NoiseConfig<IK, PublicKey<Curve25519>> {
|
||||
/// Create a new `NoiseConfig` for the IK handshake pattern (initiator side).
|
||||
pub fn ik_dialer(kp: Keypair<Curve25519>, remote: PublicKey<Curve25519>) -> Self {
|
||||
NoiseConfig {
|
||||
keypair: kp,
|
||||
params: PARAMS_IK.clone(),
|
||||
remote,
|
||||
_marker: std::marker::PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handshake pattern IX /////////////////////////////////////////////////////
|
||||
|
||||
impl UpgradeInfo for NoiseConfig<IX> {
|
||||
type Info = &'static [u8];
|
||||
type InfoIter = std::iter::Once<Self::Info>;
|
||||
|
||||
fn protocol_info(&self) -> Self::InfoIter {
|
||||
std::iter::once(b"/noise/ix/25519/chachapoly/sha256/0.1.0")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> InboundUpgrade<T> for NoiseConfig<IX>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite
|
||||
{
|
||||
type Output = (PublicKey<Curve25519>, NoiseOutput<T>);
|
||||
type Error = NoiseError;
|
||||
type Future = rt1::NoiseInboundFuture<T>;
|
||||
|
||||
fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future {
|
||||
let session = snow::Builder::with_resolver(self.params, Box::new(Resolver))
|
||||
.local_private_key(self.keypair.secret().as_ref())
|
||||
.build_responder()
|
||||
.map_err(NoiseError::from);
|
||||
rt1::NoiseInboundFuture::new(socket, session)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> OutboundUpgrade<T> for NoiseConfig<IX>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite
|
||||
{
|
||||
type Output = (PublicKey<Curve25519>, NoiseOutput<T>);
|
||||
type Error = NoiseError;
|
||||
type Future = rt1::NoiseOutboundFuture<T>;
|
||||
|
||||
fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future {
|
||||
let session = snow::Builder::with_resolver(self.params, Box::new(Resolver))
|
||||
.local_private_key(self.keypair.secret().as_ref())
|
||||
.build_initiator()
|
||||
.map_err(NoiseError::from);
|
||||
rt1::NoiseOutboundFuture::new(socket, session)
|
||||
}
|
||||
}
|
||||
|
||||
// Handshake pattern XX /////////////////////////////////////////////////////
|
||||
|
||||
impl UpgradeInfo for NoiseConfig<XX> {
|
||||
type Info = &'static [u8];
|
||||
type InfoIter = std::iter::Once<Self::Info>;
|
||||
|
||||
fn protocol_info(&self) -> Self::InfoIter {
|
||||
std::iter::once(b"/noise/xx/25519/chachapoly/sha256/0.1.0")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> InboundUpgrade<T> for NoiseConfig<XX>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite
|
||||
{
|
||||
type Output = (PublicKey<Curve25519>, NoiseOutput<T>);
|
||||
type Error = NoiseError;
|
||||
type Future = rt15::NoiseInboundFuture<T>;
|
||||
|
||||
fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future {
|
||||
let session = snow::Builder::with_resolver(self.params, Box::new(Resolver))
|
||||
.local_private_key(self.keypair.secret().as_ref())
|
||||
.build_responder()
|
||||
.map_err(NoiseError::from);
|
||||
rt15::NoiseInboundFuture::new(socket, session)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> OutboundUpgrade<T> for NoiseConfig<XX>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite
|
||||
{
|
||||
type Output = (PublicKey<Curve25519>, NoiseOutput<T>);
|
||||
type Error = NoiseError;
|
||||
type Future = rt15::NoiseOutboundFuture<T>;
|
||||
|
||||
fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future {
|
||||
let session = snow::Builder::with_resolver(self.params, Box::new(Resolver))
|
||||
.local_private_key(self.keypair.secret().as_ref())
|
||||
.build_initiator()
|
||||
.map_err(NoiseError::from);
|
||||
rt15::NoiseOutboundFuture::new(socket, session)
|
||||
}
|
||||
}
|
||||
|
||||
// Handshake pattern IK /////////////////////////////////////////////////////
|
||||
|
||||
impl UpgradeInfo for NoiseConfig<IK> {
|
||||
type Info = &'static [u8];
|
||||
type InfoIter = std::iter::Once<Self::Info>;
|
||||
|
||||
fn protocol_info(&self) -> Self::InfoIter {
|
||||
std::iter::once(b"/noise/ik/25519/chachapoly/sha256/0.1.0")
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeInfo for NoiseConfig<IK, PublicKey<Curve25519>> {
|
||||
type Info = &'static [u8];
|
||||
type InfoIter = std::iter::Once<Self::Info>;
|
||||
|
||||
fn protocol_info(&self) -> Self::InfoIter {
|
||||
std::iter::once(b"/noise/ik/25519/chachapoly/sha256/0.1.0")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> InboundUpgrade<T> for NoiseConfig<IK>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite
|
||||
{
|
||||
type Output = (PublicKey<Curve25519>, NoiseOutput<T>);
|
||||
type Error = NoiseError;
|
||||
type Future = rt1::NoiseInboundFuture<T>;
|
||||
|
||||
fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future {
|
||||
let session = snow::Builder::with_resolver(self.params, Box::new(Resolver))
|
||||
.local_private_key(self.keypair.secret().as_ref())
|
||||
.build_responder()
|
||||
.map_err(NoiseError::from);
|
||||
rt1::NoiseInboundFuture::new(socket, session)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> OutboundUpgrade<T> for NoiseConfig<IK, PublicKey<Curve25519>>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite
|
||||
{
|
||||
type Output = (PublicKey<Curve25519>, NoiseOutput<T>);
|
||||
type Error = NoiseError;
|
||||
type Future = rt1::NoiseOutboundFuture<T>;
|
||||
|
||||
fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future {
|
||||
let session = snow::Builder::with_resolver(self.params, Box::new(Resolver))
|
||||
.local_private_key(self.keypair.secret().as_ref())
|
||||
.remote_public_key(self.remote.as_ref())
|
||||
.build_initiator()
|
||||
.map_err(NoiseError::from);
|
||||
rt1::NoiseOutboundFuture::new(socket, session)
|
||||
}
|
||||
}
|
||||
|
167
protocols/noise/src/rt1.rs
Normal file
167
protocols/noise/src/rt1.rs
Normal file
@ -0,0 +1,167 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
//! Futures performing 1 round trip.
|
||||
|
||||
use crate::{
|
||||
error::NoiseError,
|
||||
io::{Handshake, NoiseOutput},
|
||||
keys::{Curve25519, PublicKey}
|
||||
};
|
||||
use futures::prelude::*;
|
||||
use snow;
|
||||
use std::mem;
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
/// A future for inbound upgrades.
|
||||
///
|
||||
/// It will perform the following steps:
|
||||
///
|
||||
/// 1. receive message
|
||||
/// 2. send message
|
||||
pub struct NoiseInboundFuture<T>(InboundState<T>);
|
||||
|
||||
impl<T> NoiseInboundFuture<T> {
|
||||
pub(super) fn new(io: T, session: Result<snow::Session, NoiseError>) -> Self {
|
||||
match session {
|
||||
Ok(s) => Self(InboundState::RecvHandshake(Handshake::new(io, s))),
|
||||
Err(e) => Self(InboundState::Err(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum InboundState<T> {
|
||||
RecvHandshake(Handshake<T>),
|
||||
SendHandshake(Handshake<T>),
|
||||
Flush(Handshake<T>),
|
||||
Err(NoiseError),
|
||||
Done
|
||||
}
|
||||
|
||||
impl<T> Future for NoiseInboundFuture<T>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite
|
||||
{
|
||||
type Item = (PublicKey<Curve25519>, NoiseOutput<T>);
|
||||
type Error = NoiseError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
loop {
|
||||
match mem::replace(&mut self.0, InboundState::Done) {
|
||||
InboundState::RecvHandshake(mut io) => {
|
||||
if io.receive()?.is_ready() {
|
||||
self.0 = InboundState::SendHandshake(io)
|
||||
} else {
|
||||
self.0 = InboundState::RecvHandshake(io);
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
InboundState::SendHandshake(mut io) => {
|
||||
if io.send()?.is_ready() {
|
||||
self.0 = InboundState::Flush(io)
|
||||
} else {
|
||||
self.0 = InboundState::SendHandshake(io);
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
InboundState::Flush(mut io) => {
|
||||
if io.flush()?.is_ready() {
|
||||
let result = io.finish()?;
|
||||
self.0 = InboundState::Done;
|
||||
return Ok(Async::Ready(result))
|
||||
} else {
|
||||
self.0 = InboundState::Flush(io);
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
InboundState::Err(e) => return Err(e),
|
||||
InboundState::Done => panic!("NoiseInboundFuture::poll called after completion")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A future for outbound upgrades.
|
||||
///
|
||||
/// It will perform the following steps:
|
||||
///
|
||||
/// 1. send message
|
||||
/// 2. receive message
|
||||
pub struct NoiseOutboundFuture<T>(OutboundState<T>);
|
||||
|
||||
impl<T> NoiseOutboundFuture<T> {
|
||||
pub(super) fn new(io: T, session: Result<snow::Session, NoiseError>) -> Self {
|
||||
match session {
|
||||
Ok(s) => Self(OutboundState::SendHandshake(Handshake::new(io, s))),
|
||||
Err(e) => Self(OutboundState::Err(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum OutboundState<T> {
|
||||
SendHandshake(Handshake<T>),
|
||||
Flush(Handshake<T>),
|
||||
RecvHandshake(Handshake<T>),
|
||||
Err(NoiseError),
|
||||
Done
|
||||
}
|
||||
|
||||
impl<T> Future for NoiseOutboundFuture<T>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite
|
||||
{
|
||||
type Item = (PublicKey<Curve25519>, NoiseOutput<T>);
|
||||
type Error = NoiseError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
loop {
|
||||
match mem::replace(&mut self.0, OutboundState::Done) {
|
||||
OutboundState::SendHandshake(mut io) => {
|
||||
if io.send()?.is_ready() {
|
||||
self.0 = OutboundState::Flush(io)
|
||||
} else {
|
||||
self.0 = OutboundState::SendHandshake(io);
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
OutboundState::Flush(mut io) => {
|
||||
if io.flush()?.is_ready() {
|
||||
self.0 = OutboundState::RecvHandshake(io)
|
||||
} else {
|
||||
self.0 = OutboundState::Flush(io);
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
OutboundState::RecvHandshake(mut io) => {
|
||||
if io.receive()?.is_ready() {
|
||||
let result = io.finish()?;
|
||||
self.0 = OutboundState::Done;
|
||||
return Ok(Async::Ready(result))
|
||||
} else {
|
||||
self.0 = OutboundState::RecvHandshake(io);
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
OutboundState::Err(e) => return Err(e),
|
||||
OutboundState::Done => panic!("NoiseOutboundFuture::poll called after completion")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
196
protocols/noise/src/rt15.rs
Normal file
196
protocols/noise/src/rt15.rs
Normal file
@ -0,0 +1,196 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
//! Futures performing 1.5 round trips.
|
||||
|
||||
use crate::{
|
||||
error::NoiseError,
|
||||
io::{Handshake, NoiseOutput},
|
||||
keys::{Curve25519, PublicKey}
|
||||
};
|
||||
use futures::prelude::*;
|
||||
use snow;
|
||||
use std::mem;
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
/// A future for inbound upgrades.
|
||||
///
|
||||
/// It will perform the following steps:
|
||||
///
|
||||
/// 1. receive message
|
||||
/// 2. send message
|
||||
/// 3. receive message
|
||||
pub struct NoiseInboundFuture<T>(InboundState<T>);
|
||||
|
||||
impl<T> NoiseInboundFuture<T> {
|
||||
pub(super) fn new(io: T, session: Result<snow::Session, NoiseError>) -> Self {
|
||||
match session {
|
||||
Ok(s) => Self(InboundState::RecvHandshake1(Handshake::new(io, s))),
|
||||
Err(e) => Self(InboundState::Err(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum InboundState<T> {
|
||||
RecvHandshake1(Handshake<T>),
|
||||
SendHandshake(Handshake<T>),
|
||||
Flush(Handshake<T>),
|
||||
RecvHandshake2(Handshake<T>),
|
||||
Err(NoiseError),
|
||||
Done
|
||||
}
|
||||
|
||||
impl<T> Future for NoiseInboundFuture<T>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite
|
||||
{
|
||||
type Item = (PublicKey<Curve25519>, NoiseOutput<T>);
|
||||
type Error = NoiseError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
loop {
|
||||
match mem::replace(&mut self.0, InboundState::Done) {
|
||||
InboundState::RecvHandshake1(mut io) => {
|
||||
if io.receive()?.is_ready() {
|
||||
self.0 = InboundState::SendHandshake(io)
|
||||
} else {
|
||||
self.0 = InboundState::RecvHandshake1(io);
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
InboundState::SendHandshake(mut io) => {
|
||||
if io.send()?.is_ready() {
|
||||
self.0 = InboundState::Flush(io)
|
||||
} else {
|
||||
self.0 = InboundState::SendHandshake(io);
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
InboundState::Flush(mut io) => {
|
||||
if io.flush()?.is_ready() {
|
||||
self.0 = InboundState::RecvHandshake2(io)
|
||||
} else {
|
||||
self.0 = InboundState::Flush(io);
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
InboundState::RecvHandshake2(mut io) => {
|
||||
if io.receive()?.is_ready() {
|
||||
let result = io.finish()?;
|
||||
self.0 = InboundState::Done;
|
||||
return Ok(Async::Ready(result))
|
||||
} else {
|
||||
self.0 = InboundState::RecvHandshake2(io);
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
InboundState::Err(e) => return Err(e),
|
||||
InboundState::Done => panic!("NoiseInboundFuture::poll called after completion")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A future for outbound upgrades.
|
||||
///
|
||||
/// It will perform the following steps:
|
||||
///
|
||||
/// 1. send message
|
||||
/// 2. receive message
|
||||
/// 3. send message
|
||||
pub struct NoiseOutboundFuture<T>(OutboundState<T>);
|
||||
|
||||
impl<T> NoiseOutboundFuture<T> {
|
||||
pub(super) fn new(io: T, session: Result<snow::Session, NoiseError>) -> Self {
|
||||
match session {
|
||||
Ok(s) => Self(OutboundState::SendHandshake1(Handshake::new(io, s))),
|
||||
Err(e) => Self(OutboundState::Err(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum OutboundState<T> {
|
||||
SendHandshake1(Handshake<T>),
|
||||
Flush1(Handshake<T>),
|
||||
RecvHandshake(Handshake<T>),
|
||||
SendHandshake2(Handshake<T>),
|
||||
Flush2(Handshake<T>),
|
||||
Err(NoiseError),
|
||||
Done
|
||||
}
|
||||
|
||||
impl<T> Future for NoiseOutboundFuture<T>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite
|
||||
{
|
||||
type Item = (PublicKey<Curve25519>, NoiseOutput<T>);
|
||||
type Error = NoiseError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
loop {
|
||||
match mem::replace(&mut self.0, OutboundState::Done) {
|
||||
OutboundState::SendHandshake1(mut io) => {
|
||||
if io.send()?.is_ready() {
|
||||
self.0 = OutboundState::Flush1(io)
|
||||
} else {
|
||||
self.0 = OutboundState::SendHandshake1(io);
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
OutboundState::Flush1(mut io) => {
|
||||
if io.flush()?.is_ready() {
|
||||
self.0 = OutboundState::RecvHandshake(io)
|
||||
} else {
|
||||
self.0 = OutboundState::Flush1(io);
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
OutboundState::RecvHandshake(mut io) => {
|
||||
if io.receive()?.is_ready() {
|
||||
self.0 = OutboundState::SendHandshake2(io)
|
||||
} else {
|
||||
self.0 = OutboundState::RecvHandshake(io);
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
OutboundState::SendHandshake2(mut io) => {
|
||||
if io.send()?.is_ready() {
|
||||
self.0 = OutboundState::Flush2(io)
|
||||
} else {
|
||||
self.0 = OutboundState::SendHandshake2(io);
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
OutboundState::Flush2(mut io) => {
|
||||
if io.flush()?.is_ready() {
|
||||
let result = io.finish()?;
|
||||
self.0 = OutboundState::Done;
|
||||
return Ok(Async::Ready(result))
|
||||
} else {
|
||||
self.0 = OutboundState::Flush2(io);
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
OutboundState::Err(e) => return Err(e),
|
||||
OutboundState::Done => panic!("NoiseOutboundFuture::poll called after completion")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
128
protocols/noise/src/util.rs
Normal file
128
protocols/noise/src/util.rs
Normal file
@ -0,0 +1,128 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use crate::{keys::{Curve25519, Keypair, SecretKey, PublicKey}, NoiseError};
|
||||
use rand::FromEntropy;
|
||||
|
||||
pub(crate) fn to_array(bytes: &[u8]) -> Result<[u8; 32], NoiseError> {
|
||||
if bytes.len() != 32 {
|
||||
return Err(NoiseError::InvalidKey)
|
||||
}
|
||||
let mut m = [0; 32];
|
||||
m.copy_from_slice(bytes);
|
||||
Ok(m)
|
||||
}
|
||||
|
||||
/// Custom `snow::CryptoResolver` which uses `ring` as much as possible.
|
||||
pub(crate) struct Resolver;
|
||||
|
||||
impl snow::resolvers::CryptoResolver for Resolver {
|
||||
fn resolve_rng(&self) -> Option<Box<dyn snow::types::Random>> {
|
||||
Some(Box::new(Rng(rand::rngs::StdRng::from_entropy())))
|
||||
}
|
||||
|
||||
fn resolve_dh(&self, choice: &snow::params::DHChoice) -> Option<Box<dyn snow::types::Dh>> {
|
||||
if let snow::params::DHChoice::Curve25519 = choice {
|
||||
Some(Box::new(X25519 { keypair: Keypair::gen_curve25519() }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_hash(&self, choice: &snow::params::HashChoice) -> Option<Box<dyn snow::types::Hash>> {
|
||||
snow::resolvers::RingResolver.resolve_hash(choice)
|
||||
}
|
||||
|
||||
fn resolve_cipher(&self, choice: &snow::params::CipherChoice) -> Option<Box<dyn snow::types::Cipher>> {
|
||||
snow::resolvers::RingResolver.resolve_cipher(choice)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around a CPRNG to implement `snow::Random` trait for.
|
||||
struct Rng(rand::rngs::StdRng);
|
||||
|
||||
impl rand::RngCore for Rng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.0.next_u32()
|
||||
}
|
||||
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
self.0.next_u64()
|
||||
}
|
||||
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
self.0.fill_bytes(dest)
|
||||
}
|
||||
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
|
||||
self.0.try_fill_bytes(dest)
|
||||
}
|
||||
}
|
||||
|
||||
impl rand::CryptoRng for Rng {}
|
||||
|
||||
impl snow::types::Random for Rng {}
|
||||
|
||||
/// X25519 Diffie-Hellman key agreement.
|
||||
struct X25519 {
|
||||
keypair: Keypair<Curve25519>
|
||||
}
|
||||
|
||||
impl snow::types::Dh for X25519 {
|
||||
fn name(&self) -> &'static str { "25519" }
|
||||
|
||||
fn pub_len(&self) -> usize { 32 }
|
||||
|
||||
fn priv_len(&self) -> usize { 32 }
|
||||
|
||||
fn pubkey(&self) -> &[u8] {
|
||||
&self.keypair.public().as_ref()
|
||||
}
|
||||
|
||||
fn privkey(&self) -> &[u8] {
|
||||
&self.keypair.secret().as_ref()
|
||||
}
|
||||
|
||||
fn set(&mut self, privkey: &[u8]) {
|
||||
let mut s = [0; 32];
|
||||
s.copy_from_slice(privkey);
|
||||
let secret = SecretKey::new(s);
|
||||
let public = secret.public();
|
||||
self.keypair = Keypair::new(secret, public)
|
||||
}
|
||||
|
||||
fn generate(&mut self, rng: &mut snow::types::Random) {
|
||||
let mut s = [0; 32];
|
||||
rng.fill_bytes(&mut s);
|
||||
let secret = SecretKey::new(s);
|
||||
let public = secret.public();
|
||||
self.keypair = Keypair::new(secret, public)
|
||||
}
|
||||
|
||||
fn dh(&self, pubkey: &[u8], out: &mut [u8]) -> Result<(), ()> {
|
||||
let mut p = [0; 32];
|
||||
p.copy_from_slice(&pubkey[.. 32]);
|
||||
let public = PublicKey::new(p);
|
||||
let result = self.keypair.secret().ecdh(&public);
|
||||
out[.. 32].copy_from_slice(&result[..]);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user