mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-26 16:21:39 +00:00
Add support for noise IX, XX, and IK handshakes. (#888)
This commit is contained in:
@ -38,6 +38,7 @@ tokio-io = "0.1"
|
|||||||
[target.'cfg(not(any(target_os = "emscripten", target_os = "unknown")))'.dependencies]
|
[target.'cfg(not(any(target_os = "emscripten", target_os = "unknown")))'.dependencies]
|
||||||
libp2p-dns = { version = "0.2.0", path = "./transports/dns" }
|
libp2p-dns = { version = "0.2.0", path = "./transports/dns" }
|
||||||
libp2p-mdns = { version = "0.2.0", path = "./misc/mdns" }
|
libp2p-mdns = { version = "0.2.0", path = "./misc/mdns" }
|
||||||
|
libp2p-noise = { version = "0.1.0", path = "./protocols/noise" }
|
||||||
libp2p-tcp = { version = "0.2.0", path = "./transports/tcp" }
|
libp2p-tcp = { version = "0.2.0", path = "./transports/tcp" }
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "emscripten", target_os = "unknown"))'.dependencies]
|
[target.'cfg(any(target_os = "emscripten", target_os = "unknown"))'.dependencies]
|
||||||
@ -65,6 +66,7 @@ members = [
|
|||||||
"protocols/floodsub",
|
"protocols/floodsub",
|
||||||
"protocols/identify",
|
"protocols/identify",
|
||||||
"protocols/kad",
|
"protocols/kad",
|
||||||
|
"protocols/noise",
|
||||||
"protocols/observed",
|
"protocols/observed",
|
||||||
"protocols/ping",
|
"protocols/ping",
|
||||||
"protocols/plaintext",
|
"protocols/plaintext",
|
||||||
|
24
protocols/noise/Cargo.toml
Normal file
24
protocols/noise/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "libp2p-noise"
|
||||||
|
description = "Cryptographic handshake protocol using the noise framework."
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
license = "MIT"
|
||||||
|
repository = "https://github.com/libp2p/rust-libp2p"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
curve25519-dalek = "1"
|
||||||
|
futures = "0.1"
|
||||||
|
lazy_static = "1.2"
|
||||||
|
libp2p-core = { version = "0.2.0", path = "../../core" }
|
||||||
|
log = "0.4"
|
||||||
|
rand = "0.6"
|
||||||
|
snow = { version = "0.5.0-alpha1", default-features = false, features = ["ring-resolver"] }
|
||||||
|
tokio-io = "0.1"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
env_logger = "0.6"
|
||||||
|
libp2p-tcp = { version = "0.2.0", path = "../../transports/tcp" }
|
||||||
|
quickcheck = "0.8"
|
||||||
|
tokio = "0.1"
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
137
protocols/noise/tests/smoke.rs
Normal file
137
protocols/noise/tests/smoke.rs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
// 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 futures::{future::Either, prelude::*};
|
||||||
|
use libp2p_core::{Transport, upgrade::{apply_inbound, apply_outbound}};
|
||||||
|
use libp2p_noise::{Keypair, PublicKey, Curve25519, NoiseConfig};
|
||||||
|
use libp2p_tcp::TcpConfig;
|
||||||
|
use log::info;
|
||||||
|
use quickcheck::QuickCheck;
|
||||||
|
use tokio::{self, io};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn xx() {
|
||||||
|
let _ = env_logger::try_init();
|
||||||
|
fn prop(message: Vec<u8>) -> bool {
|
||||||
|
let server_keypair = Keypair::gen_curve25519();
|
||||||
|
let server_transport = TcpConfig::new().with_upgrade(NoiseConfig::xx(server_keypair));
|
||||||
|
|
||||||
|
let client_keypair = Keypair::gen_curve25519();
|
||||||
|
let client_transport = TcpConfig::new().with_upgrade(NoiseConfig::xx(client_keypair));
|
||||||
|
|
||||||
|
run(server_transport, client_transport, message);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
QuickCheck::new().max_tests(30).quickcheck(prop as fn(Vec<u8>) -> bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ix() {
|
||||||
|
let _ = env_logger::try_init();
|
||||||
|
fn prop(message: Vec<u8>) -> bool {
|
||||||
|
|
||||||
|
let server_keypair = Keypair::gen_curve25519();
|
||||||
|
let server_transport = TcpConfig::new().with_upgrade(NoiseConfig::ix(server_keypair));
|
||||||
|
|
||||||
|
let client_keypair = Keypair::gen_curve25519();
|
||||||
|
let client_transport = TcpConfig::new().with_upgrade(NoiseConfig::ix(client_keypair));
|
||||||
|
|
||||||
|
run(server_transport, client_transport, message);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
QuickCheck::new().max_tests(30).quickcheck(prop as fn(Vec<u8>) -> bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ik_xx() {
|
||||||
|
let _ = env_logger::try_init();
|
||||||
|
fn prop(message: Vec<u8>) -> bool {
|
||||||
|
let server_keypair = Keypair::gen_curve25519();
|
||||||
|
let server_public = server_keypair.public().clone();
|
||||||
|
let server_transport = TcpConfig::new()
|
||||||
|
.and_then(move |output, endpoint| {
|
||||||
|
if endpoint.is_listener() {
|
||||||
|
Either::A(apply_inbound(output, NoiseConfig::ik_listener(server_keypair)))
|
||||||
|
} else {
|
||||||
|
Either::B(apply_outbound(output, NoiseConfig::xx(server_keypair)))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let client_keypair = Keypair::gen_curve25519();
|
||||||
|
let client_transport = TcpConfig::new()
|
||||||
|
.and_then(move |output, endpoint| {
|
||||||
|
if endpoint.is_dialer() {
|
||||||
|
Either::A(apply_outbound(output, NoiseConfig::ik_dialer(client_keypair, server_public)))
|
||||||
|
} else {
|
||||||
|
Either::B(apply_inbound(output, NoiseConfig::xx(client_keypair)))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
run(server_transport, client_transport, message);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
QuickCheck::new().max_tests(30).quickcheck(prop as fn(Vec<u8>) -> bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run<T, A, U, B>(server_transport: T, client_transport: U, message1: Vec<u8>)
|
||||||
|
where
|
||||||
|
T: Transport<Output = (PublicKey<Curve25519>, A)>,
|
||||||
|
T::Dial: Send + 'static,
|
||||||
|
T::Listener: Send + 'static,
|
||||||
|
T::ListenerUpgrade: Send + 'static,
|
||||||
|
A: io::AsyncRead + io::AsyncWrite + Send + 'static,
|
||||||
|
U: Transport<Output = (PublicKey<Curve25519>, B)>,
|
||||||
|
U::Dial: Send + 'static,
|
||||||
|
U::Listener: Send + 'static,
|
||||||
|
U::ListenerUpgrade: Send + 'static,
|
||||||
|
B: io::AsyncRead + io::AsyncWrite + Send + 'static
|
||||||
|
{
|
||||||
|
let message2 = message1.clone();
|
||||||
|
|
||||||
|
let (server, server_address) = server_transport
|
||||||
|
.listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let server = server.take(1)
|
||||||
|
.and_then(|client| client.0)
|
||||||
|
.map_err(|e| panic!("server error: {}", e))
|
||||||
|
.and_then(|(_, client)| {
|
||||||
|
info!("server: reading message");
|
||||||
|
io::read_to_end(client, Vec::new())
|
||||||
|
})
|
||||||
|
.for_each(move |msg| {
|
||||||
|
assert_eq!(msg.1, message1);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
let client = client_transport.dial(server_address).unwrap()
|
||||||
|
.map_err(|e| panic!("client error: {}", e))
|
||||||
|
.and_then(move |(_, server)| {
|
||||||
|
io::write_all(server, message2).and_then(|(client, _)| io::flush(client))
|
||||||
|
})
|
||||||
|
.map(|_| ());
|
||||||
|
|
||||||
|
let future = client.join(server)
|
||||||
|
.map_err(|e| panic!("{:?}", e))
|
||||||
|
.map(|_| ());
|
||||||
|
|
||||||
|
tokio::run(future)
|
||||||
|
}
|
||||||
|
|
@ -158,6 +158,9 @@ pub use libp2p_mplex as mplex;
|
|||||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use libp2p_mdns as mdns;
|
pub use libp2p_mdns as mdns;
|
||||||
|
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use libp2p_noise as noise;
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use libp2p_ping as ping;
|
pub use libp2p_ping as ping;
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
|
Reference in New Issue
Block a user