Add support for noise IX, XX, and IK handshakes. (#888)

This commit is contained in:
Toralf Wittner
2019-01-30 11:36:00 +01:00
committed by GitHub
parent 35ec7f053c
commit 62fd5cd514
11 changed files with 1564 additions and 0 deletions

View 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
View 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
View 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
View 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
View 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
View 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
View 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(())
}
}