Integrate identity keys with libp2p-noise for authentication. (#1027)

* Integrate use of identity keys into libp2p-noise.

In order to make libp2p-noise usable with a `Swarm`, which requires a
`Transport::Output` that is a pair of a peer ID and an implementation
of `StreamMuxer`, it is necessary to bridge the gap between static
DH public keys and public identity keys from which peer IDs are derived.

Because the DH static keys and the identity keys need not be
related, it is thus generally necessary that the public identity keys are
exchanged as part of the Noise handshake, which the Noise protocol
accomodates for through the use of handshake message payloads.

The implementation of the existing (IK, IX, XX) handshake patterns is thus
changed to send the public identity keys in the handshake payloads.
Additionally, to facilitate the use of any identity keypair with Noise
handshakes, the static DH public keys are signed using the identity
keypairs and the signatures sent alongside the public identity key
in handshake payloads, unless the static DH public key is "linked"
to the public identity key by other means, e.g. when an Ed25519 identity
keypair is (re)used as an X25519 keypair.

* libp2p-noise doesn't build for wasm.

Thus the development transport needs to be still constructed with secio
for transport security when building for wasm.

* Documentation tweaks.

* For consistency, avoid wildcard enum imports.

* For consistency, avoid wildcard enum imports.

* Slightly simplify io:🤝:State::finish.

* Simplify creation of 2-byte arrays.

* Remove unnecessary cast and obey 100 char line limit.

* Update protocols/noise/src/protocol.rs

Co-Authored-By: romanb <romanb@users.noreply.github.com>

* Address more review comments.

* Cosmetics

* Cosmetics

* Give authentic DH keypairs a distinct type.

This has a couple of advantages:

  * Signing the DH public key only needs to happen once, before
    creating a `NoiseConfig` for an authenticated handshake.

  * The identity keypair only needs to be borrowed and can be
    dropped if it is not used further outside of the Noise
    protocol, since it is no longer needed during Noise handshakes.

  * It is explicit in the construction of a `NoiseConfig` for
    a handshake pattern, whether it operates with a plain `Keypair`
    or a keypair that is authentic w.r.t. a public identity key
    and future handshake patterns may be built with either.

  * The function signatures for constructing `NoiseConfig`s for
    handshake patterns are simplified and a few unnecessary trait
    bounds removed.

* Post-merge corrections.

* Add note on experimental status of libp2p-noise.
This commit is contained in:
Roman Borschel
2019-05-07 10:22:42 +02:00
committed by GitHub
parent e44b443b91
commit 8537eb38b9
16 changed files with 1205 additions and 558 deletions

View File

@ -994,8 +994,8 @@ where
TConnInfo: Clone, TConnInfo: Clone,
TPeerId: AsRef<[u8]> + Send + 'static, TPeerId: AsRef<[u8]> + Send + 'static,
{ {
// Start by polling the listeners for events, but only // Start by polling the listeners for events, but only if the number
// if numer of incoming connection does not exceed the limit. // of incoming connections does not exceed the limit.
match self.incoming_limit { match self.incoming_limit {
Some(x) if self.incoming_negotiated().count() >= (x as usize) Some(x) if self.incoming_negotiated().count() >= (x as usize)
=> (), => (),

View File

@ -13,9 +13,10 @@ futures = "0.1"
lazy_static = "1.2" lazy_static = "1.2"
libp2p-core = { version = "0.7.0", path = "../../core" } libp2p-core = { version = "0.7.0", path = "../../core" }
log = "0.4" log = "0.4"
protobuf = "2.3"
rand = "0.6.5" rand = "0.6.5"
ring = { version = "0.14", features = ["use_heap"], default-features = false } ring = { version = "0.14", features = ["use_heap"], default-features = false }
snow = { version = "0.5.1", features = ["ring-resolver"], default-features = false } snow = { version = "0.5.2", features = ["ring-resolver"], default-features = false }
tokio-io = "0.1" tokio-io = "0.1"
x25519-dalek = "0.5" x25519-dalek = "0.5"
zeroize = "0.5" zeroize = "0.5"

9
protocols/noise/make_proto.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/sh
sudo docker run --rm -v `pwd`:/usr/code:z -w /usr/code rust /bin/bash -c " \
apt-get update; \
apt-get install -y protobuf-compiler; \
cargo install --version 2.3.0 protobuf-codegen; \
protoc --rust_out ./src/io/handshake/ ./src/io/handshake/payload.proto"
sudo chown $USER:$USER ./src/io/handshake/payload.rs

View File

@ -18,6 +18,7 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
use libp2p_core::identity;
use snow::SnowError; use snow::SnowError;
use std::{error::Error, fmt, io}; use std::{error::Error, fmt, io};
@ -30,6 +31,10 @@ pub enum NoiseError {
Noise(SnowError), Noise(SnowError),
/// A public key is invalid. /// A public key is invalid.
InvalidKey, InvalidKey,
/// A handshake payload is invalid.
InvalidPayload(protobuf::ProtobufError),
/// A signature was required and could not be created.
SigningError(identity::error::SigningError),
#[doc(hidden)] #[doc(hidden)]
__Nonexhaustive __Nonexhaustive
} }
@ -40,6 +45,8 @@ impl fmt::Display for NoiseError {
NoiseError::Io(e) => write!(f, "{}", e), NoiseError::Io(e) => write!(f, "{}", e),
NoiseError::Noise(e) => write!(f, "{}", e), NoiseError::Noise(e) => write!(f, "{}", e),
NoiseError::InvalidKey => f.write_str("invalid public key"), NoiseError::InvalidKey => f.write_str("invalid public key"),
NoiseError::InvalidPayload(e) => write!(f, "{}", e),
NoiseError::SigningError(e) => write!(f, "{}", e),
NoiseError::__Nonexhaustive => f.write_str("__Nonexhaustive") NoiseError::__Nonexhaustive => f.write_str("__Nonexhaustive")
} }
} }
@ -51,6 +58,8 @@ impl Error for NoiseError {
NoiseError::Io(e) => Some(e), NoiseError::Io(e) => Some(e),
NoiseError::Noise(_) => None, // TODO: `SnowError` should implement `Error`. NoiseError::Noise(_) => None, // TODO: `SnowError` should implement `Error`.
NoiseError::InvalidKey => None, NoiseError::InvalidKey => None,
NoiseError::InvalidPayload(e) => Some(e),
NoiseError::SigningError(e) => Some(e),
NoiseError::__Nonexhaustive => None NoiseError::__Nonexhaustive => None
} }
} }
@ -67,3 +76,16 @@ impl From<SnowError> for NoiseError {
NoiseError::Noise(e) NoiseError::Noise(e)
} }
} }
impl From<protobuf::ProtobufError> for NoiseError {
fn from(e: protobuf::ProtobufError) -> Self {
NoiseError::InvalidPayload(e)
}
}
impl From<identity::error::SigningError> for NoiseError {
fn from(e: identity::error::SigningError) -> Self {
NoiseError::SigningError(e)
}
}

View File

@ -18,7 +18,10 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
use crate::{NoiseError, Protocol, PublicKey}; //! Noise protocol I/O.
pub mod handshake;
use futures::Poll; use futures::Poll;
use log::{debug, trace}; use log::{debug, trace};
use snow; use snow;
@ -34,7 +37,7 @@ struct Buffer {
inner: Box<[u8; TOTAL_BUFFER_LEN]> inner: Box<[u8; TOTAL_BUFFER_LEN]>
} }
/// A mutable borrow of all byte byffers, backed by `Buffer`. /// A mutable borrow of all byte buffers, backed by `Buffer`.
struct BufferBorrow<'a> { struct BufferBorrow<'a> {
read: &'a mut [u8], read: &'a mut [u8],
read_crypto: &'a mut [u8], read_crypto: &'a mut [u8],
@ -52,47 +55,6 @@ impl Buffer {
} }
} }
/// A type used during the 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 transport mode and returns the remote's static
/// public key as well as the established session for further communication.
pub(super) fn finish<C>(self) -> Result<(PublicKey<C>, NoiseOutput<T>), NoiseError>
where
C: Protocol<C>
{
let s = self.0.session.into_transport_mode()?;
let p = s.get_remote_static()
.ok_or(NoiseError::InvalidKey)
.and_then(C::public_from_bytes)?;
Ok((p, NoiseOutput { session: s, .. self.0 }))
}
}
/// A noise session to a remote. /// A noise session to a remote.
/// ///
/// `T` is the type of the underlying I/O resource. /// `T` is the type of the underlying I/O resource.
@ -388,6 +350,8 @@ impl<T: AsyncWrite> AsyncWrite for NoiseOutput<T> {
/// When [`io::ErrorKind::WouldBlock`] is returned, the given buffer and offset /// When [`io::ErrorKind::WouldBlock`] is returned, the given buffer and offset
/// may have been updated (i.e. a byte may have been read) and must be preserved /// may have been updated (i.e. a byte may have been read) and must be preserved
/// for the next invocation. /// for the next invocation.
///
/// Returns `None` if EOF has been encountered.
fn read_frame_len<R: io::Read>(io: &mut R, buf: &mut [u8; 2], off: &mut usize) fn read_frame_len<R: io::Read>(io: &mut R, buf: &mut [u8; 2], off: &mut usize)
-> io::Result<Option<u16>> -> io::Result<Option<u16>>
{ {
@ -410,6 +374,8 @@ fn read_frame_len<R: io::Read>(io: &mut R, buf: &mut [u8; 2], off: &mut usize)
/// When [`io::ErrorKind::WouldBlock`] is returned, the given offset /// When [`io::ErrorKind::WouldBlock`] is returned, the given offset
/// may have been updated (i.e. a byte may have been written) and must /// may have been updated (i.e. a byte may have been written) and must
/// be preserved for the next invocation. /// be preserved for the next invocation.
///
/// Returns `false` if EOF has been encountered.
fn write_frame_len<W: io::Write>(io: &mut W, buf: &[u8; 2], off: &mut usize) fn write_frame_len<W: io::Write>(io: &mut W, buf: &[u8; 2], off: &mut usize)
-> io::Result<bool> -> io::Result<bool>
{ {

View File

@ -0,0 +1,586 @@
// 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 handshake I/O.
mod payload;
use crate::error::NoiseError;
use crate::protocol::{Protocol, PublicKey, KeypairIdentity};
use libp2p_core::identity;
use futures::{future, Async, Future, future::FutureResult, Poll};
use std::{mem, io};
use tokio_io::{io as nio, AsyncWrite, AsyncRead};
use protobuf::Message;
use super::NoiseOutput;
/// A future performing a Noise handshake pattern.
pub struct Handshake<T, C>(
Box<dyn Future<
Item = <Handshake<T, C> as Future>::Item,
Error = <Handshake<T, C> as Future>::Error
> + Send>
);
impl<T, C> Future for Handshake<T, C> {
type Error = NoiseError;
type Item = (RemoteIdentity<C>, NoiseOutput<T>);
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
self.0.poll()
}
}
/// The identity of the remote established during a handshake.
pub enum RemoteIdentity<C> {
/// The remote provided no identifying information.
///
/// The identity of the remote is unknown and must be obtained through
/// a different, out-of-band channel.
Unknown,
/// The remote provided a static DH public key.
///
/// The static DH public key is authentic in the sense that a successful
/// handshake implies that the remote possesses a corresponding secret key.
///
/// > **Note**: To rule out active attacks like a MITM, trust in the public key must
/// > still be established, e.g. by comparing the key against an expected or
/// > otherwise known public key.
StaticDhKey(PublicKey<C>),
/// The remote provided a public identity key in addition to a static DH
/// public key and the latter is authentic w.r.t. the former.
///
/// > **Note**: To rule out active attacks like a MITM, trust in the public key must
/// > still be established, e.g. by comparing the key against an expected or
/// > otherwise known public key.
IdentityKey(identity::PublicKey)
}
/// The options for identity exchange in an authenticated handshake.
///
/// > **Note**: Even if a remote's public identity key is known a priori,
/// > unless the authenticity of the key is [linked](Protocol::linked) to
/// > the authenticity of a remote's static DH public key, an authenticated
/// > handshake will still send the associated signature of the provided
/// > local [`KeypairIdentity`] in order for the remote to verify that the static
/// > DH public key is authentic w.r.t. the known public identity key.
pub enum IdentityExchange {
/// Send the local public identity to the remote.
///
/// The remote identity is unknown (i.e. expected to be received).
Mutual,
/// Send the local public identity to the remote.
///
/// The remote identity is known.
Send { remote: identity::PublicKey },
/// Don't send the local public identity to the remote.
///
/// The remote identity is unknown, i.e. expected to be received.
Receive,
/// Don't send the local public identity to the remote.
///
/// The remote identity is known, thus identities must be mutually known
/// in order for the handshake to succeed.
None { remote: identity::PublicKey }
}
impl<T, C> Handshake<T, C>
where
T: AsyncRead + AsyncWrite + Send + 'static,
C: Protocol<C> + AsRef<[u8]> + Send + 'static,
{
/// Creates an authenticated Noise handshake for the initiator of a
/// single roundtrip (2 message) handshake pattern.
///
/// Subject to the chosen [`IdentityExchange`], this message sequence
/// identifies the local node to the remote with the first message payload
/// (i.e. unencrypted) and expects the remote to identify itself in the
/// second message payload.
///
/// This message sequence is suitable for authenticated 2-message Noise handshake
/// patterns where the static keys of the initiator and responder are either
/// known (i.e. appear in the pre-message pattern) or are sent with
/// the first and second message, respectively (e.g. `IK` or `IX`).
///
/// ```raw
/// initiator -{id}-> responder
/// initiator <-{id}- responder
/// ```
pub fn rt1_initiator(
io: T,
session: Result<snow::Session, NoiseError>,
identity: KeypairIdentity,
identity_x: IdentityExchange
) -> Handshake<T, C> {
Handshake(Box::new(
State::new(io, session, identity, identity_x)
.and_then(State::send_identity)
.and_then(State::recv_identity)
.and_then(State::finish)))
}
/// Creates an authenticated Noise handshake for the responder of a
/// single roundtrip (2 message) handshake pattern.
///
/// Subject to the chosen [`IdentityExchange`], this message sequence expects the
/// remote to identify itself in the first message payload (i.e. unencrypted)
/// and identifies the local node to the remote in the second message payload.
///
/// This message sequence is suitable for authenticated 2-message Noise handshake
/// patterns where the static keys of the initiator and responder are either
/// known (i.e. appear in the pre-message pattern) or are sent with the first
/// and second message, respectively (e.g. `IK` or `IX`).
///
/// ```raw
/// initiator -{id}-> responder
/// initiator <-{id}- responder
/// ```
pub fn rt1_responder(
io: T,
session: Result<snow::Session, NoiseError>,
identity: KeypairIdentity,
identity_x: IdentityExchange,
) -> Handshake<T, C> {
Handshake(Box::new(
State::new(io, session, identity, identity_x)
.and_then(State::recv_identity)
.and_then(State::send_identity)
.and_then(State::finish)))
}
/// Creates an authenticated Noise handshake for the initiator of a
/// 1.5-roundtrip (3 message) handshake pattern.
///
/// Subject to the chosen [`IdentityExchange`], this message sequence expects
/// the remote to identify itself in the second message payload and
/// identifies the local node to the remote in the third message payload.
/// The first (unencrypted) message payload is always empty.
///
/// This message sequence is suitable for authenticated 3-message Noise handshake
/// patterns where the static keys of the responder and initiator are either known
/// (i.e. appear in the pre-message pattern) or are sent with the second and third
/// message, respectively (e.g. `XX`).
///
/// ```raw
/// initiator --{}--> responder
/// initiator <-{id}- responder
/// initiator -{id}-> responder
/// ```
pub fn rt15_initiator(
io: T,
session: Result<snow::Session, NoiseError>,
identity: KeypairIdentity,
identity_x: IdentityExchange
) -> Handshake<T, C> {
Handshake(Box::new(
State::new(io, session, identity, identity_x)
.and_then(State::send_empty)
.and_then(State::recv_identity)
.and_then(State::send_identity)
.and_then(State::finish)))
}
/// Creates an authenticated Noise handshake for the responder of a
/// 1.5-roundtrip (3 message) handshake pattern.
///
/// Subject to the chosen [`IdentityExchange`], this message sequence
/// identifies the local node in the second message payload and expects
/// the remote to identify itself in the third message payload. The first
/// (unencrypted) message payload is always empty.
///
/// This message sequence is suitable for authenticated 3-message Noise handshake
/// patterns where the static keys of the responder and initiator are either known
/// (i.e. appear in the pre-message pattern) or are sent with the second and third
/// message, respectively (e.g. `XX`).
///
/// ```raw
/// initiator --{}--> responder
/// initiator <-{id}- responder
/// initiator -{id}-> responder
/// ```
pub fn rt15_responder(
io: T,
session: Result<snow::Session, NoiseError>,
identity: KeypairIdentity,
identity_x: IdentityExchange
) -> Handshake<T, C> {
Handshake(Box::new(
State::new(io, session, identity, identity_x)
.and_then(State::recv_empty)
.and_then(State::send_identity)
.and_then(State::recv_identity)
.and_then(State::finish)))
}
}
//////////////////////////////////////////////////////////////////////////////
// Internal
/// Handshake state.
struct State<T> {
/// The underlying I/O resource.
io: NoiseOutput<T>,
/// The associated public identity of the local node's static DH keypair,
/// which can be sent to the remote as part of an authenticated handshake.
identity: KeypairIdentity,
/// The received signature over the remote's static DH public key, if any.
dh_remote_pubkey_sig: Option<Vec<u8>>,
/// The known or received public identity key of the remote, if any.
id_remote_pubkey: Option<identity::PublicKey>,
/// Whether to send the public identity key of the local node to the remote.
send_identity: bool,
}
impl<T: io::Read> io::Read for State<T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.io.read(buf)
}
}
impl<T: io::Write> io::Write for State<T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.io.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.io.flush()
}
}
impl<T: AsyncRead> AsyncRead for State<T> {}
impl<T: AsyncWrite> AsyncWrite for State<T> {
fn shutdown(&mut self) -> Poll<(), io::Error> {
self.io.shutdown()
}
}
impl<T> State<T> {
/// Initializes the state for a new Noise handshake, using the given local
/// identity keypair and local DH static public key. The handshake messages
/// will be sent and received on the given I/O resource and using the
/// provided session for cryptographic operations according to the chosen
/// Noise handshake pattern.
fn new(
io: T,
session: Result<snow::Session, NoiseError>,
identity: KeypairIdentity,
identity_x: IdentityExchange
) -> FutureResult<Self, NoiseError> {
let (id_remote_pubkey, send_identity) = match identity_x {
IdentityExchange::Mutual => (None, true),
IdentityExchange::Send { remote } => (Some(remote), true),
IdentityExchange::Receive => (None, false),
IdentityExchange::None { remote } => (Some(remote), false)
};
future::result(session.map(|s|
State {
identity,
io: NoiseOutput::new(io, s),
dh_remote_pubkey_sig: None,
id_remote_pubkey,
send_identity
}
))
}
}
impl<T> State<T>
{
/// Finish a handshake, yielding the established remote identity and the
/// [`NoiseOutput`] for communicating on the encrypted channel.
fn finish<C>(self) -> FutureResult<(RemoteIdentity<C>, NoiseOutput<T>), NoiseError>
where
C: Protocol<C> + AsRef<[u8]>
{
let dh_remote_pubkey = match self.io.session.get_remote_static() {
None => None,
Some(k) => match C::public_from_bytes(k) {
Err(e) => return future::err(e),
Ok(dh_pk) => Some(dh_pk)
}
};
match self.io.session.into_transport_mode() {
Err(e) => future::err(e.into()),
Ok(s) => {
let remote = match (self.id_remote_pubkey, dh_remote_pubkey) {
(_, None) => RemoteIdentity::Unknown,
(None, Some(dh_pk)) => RemoteIdentity::StaticDhKey(dh_pk),
(Some(id_pk), Some(dh_pk)) => {
if C::verify(&id_pk, &dh_pk, &self.dh_remote_pubkey_sig) {
RemoteIdentity::IdentityKey(id_pk)
} else {
return future::err(NoiseError::InvalidKey)
}
}
};
future::ok((remote, NoiseOutput { session: s, .. self.io }))
}
}
}
}
impl<T> State<T> {
/// Creates a future that sends a Noise handshake message with an empty payload.
fn send_empty(self) -> SendEmpty<T> {
SendEmpty { state: SendState::Write(self) }
}
/// Creates a future that expects to receive a Noise handshake message with an empty payload.
fn recv_empty(self) -> RecvEmpty<T> {
RecvEmpty { state: RecvState::Read(self) }
}
/// Creates a future that sends a Noise handshake message with a payload identifying
/// the local node to the remote.
fn send_identity(self) -> SendIdentity<T> {
SendIdentity { state: SendIdentityState::Init(self) }
}
/// Creates a future that expects to receive a Noise handshake message with a
/// payload identifying the remote.
fn recv_identity(self) -> RecvIdentity<T> {
RecvIdentity { state: RecvIdentityState::Init(self) }
}
}
//////////////////////////////////////////////////////////////////////////////
// Handshake Message Futures
// RecvEmpty -----------------------------------------------------------------
/// A future for receiving a Noise handshake message with an empty payload.
///
/// Obtained from [`Handshake::recv_empty`].
struct RecvEmpty<T> {
state: RecvState<T>
}
enum RecvState<T> {
Read(State<T>),
Done
}
impl<T> Future for RecvEmpty<T>
where
T: AsyncRead
{
type Error = NoiseError;
type Item = State<T>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match mem::replace(&mut self.state, RecvState::Done) {
RecvState::Read(mut st) => {
if !st.io.poll_read(&mut [])?.is_ready() {
self.state = RecvState::Read(st);
return Ok(Async::NotReady)
}
Ok(Async::Ready(st))
},
RecvState::Done => panic!("RecvEmpty polled after completion")
}
}
}
// SendEmpty -----------------------------------------------------------------
/// A future for sending a Noise handshake message with an empty payload.
///
/// Obtained from [`Handshake::send_empty`].
struct SendEmpty<T> {
state: SendState<T>
}
enum SendState<T> {
Write(State<T>),
Flush(State<T>),
Done
}
impl<T> Future for SendEmpty<T>
where
T: AsyncWrite
{
type Error = NoiseError;
type Item = State<T>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
match mem::replace(&mut self.state, SendState::Done) {
SendState::Write(mut st) => {
if !st.io.poll_write(&mut [])?.is_ready() {
self.state = SendState::Write(st);
return Ok(Async::NotReady)
}
self.state = SendState::Flush(st);
},
SendState::Flush(mut st) => {
if !st.io.poll_flush()?.is_ready() {
self.state = SendState::Flush(st);
return Ok(Async::NotReady)
}
return Ok(Async::Ready(st))
}
SendState::Done => panic!("SendEmpty polled after completion")
}
}
}
}
// RecvIdentity --------------------------------------------------------------
/// A future for receiving a Noise handshake message with a payload
/// identifying the remote.
///
/// Obtained from [`Handshake::recv_identity`].
struct RecvIdentity<T> {
state: RecvIdentityState<T>
}
enum RecvIdentityState<T> {
Init(State<T>),
ReadPayloadLen(nio::ReadExact<State<T>, [u8; 2]>),
ReadPayload(nio::ReadExact<State<T>, Vec<u8>>),
Done
}
impl<T> Future for RecvIdentity<T>
where
T: AsyncRead,
{
type Error = NoiseError;
type Item = State<T>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
match mem::replace(&mut self.state, RecvIdentityState::Done) {
RecvIdentityState::Init(st) => {
self.state = RecvIdentityState::ReadPayloadLen(nio::read_exact(st, [0, 0]));
},
RecvIdentityState::ReadPayloadLen(mut read_len) => {
if let Async::Ready((st, bytes)) = read_len.poll()? {
let len = u16::from_be_bytes(bytes) as usize;
let buf = vec![0; len];
self.state = RecvIdentityState::ReadPayload(nio::read_exact(st, buf));
} else {
self.state = RecvIdentityState::ReadPayloadLen(read_len);
return Ok(Async::NotReady);
}
},
RecvIdentityState::ReadPayload(mut read_payload) => {
if let Async::Ready((mut st, bytes)) = read_payload.poll()? {
let pb: payload::Identity = protobuf::parse_from_bytes(&bytes)?;
if !pb.pubkey.is_empty() {
let pk = identity::PublicKey::from_protobuf_encoding(pb.get_pubkey())
.map_err(|_| NoiseError::InvalidKey)?;
if let Some(ref k) = st.id_remote_pubkey {
if k != &pk {
return Err(NoiseError::InvalidKey)
}
}
st.id_remote_pubkey = Some(pk);
}
if !pb.signature.is_empty() {
st.dh_remote_pubkey_sig = Some(pb.signature)
}
return Ok(Async::Ready(st))
} else {
self.state = RecvIdentityState::ReadPayload(read_payload);
return Ok(Async::NotReady)
}
},
RecvIdentityState::Done => panic!("RecvIdentity polled after completion")
}
}
}
}
// SendIdentity --------------------------------------------------------------
/// A future for sending a Noise handshake message with a payload
/// identifying the local node to the remote.
///
/// Obtained from [`Handshake::send_identity`].
struct SendIdentity<T> {
state: SendIdentityState<T>
}
enum SendIdentityState<T> {
Init(State<T>),
WritePayloadLen(nio::WriteAll<State<T>, [u8; 2]>, Vec<u8>),
WritePayload(nio::WriteAll<State<T>, Vec<u8>>),
Flush(State<T>),
Done
}
impl<T> Future for SendIdentity<T>
where
T: AsyncWrite,
{
type Error = NoiseError;
type Item = State<T>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
match mem::replace(&mut self.state, SendIdentityState::Done) {
SendIdentityState::Init(st) => {
let mut pb = payload::Identity::new();
if st.send_identity {
pb.set_pubkey(st.identity.public.clone().into_protobuf_encoding());
}
if let Some(ref sig) = st.identity.signature {
pb.set_signature(sig.clone());
}
let pb_bytes = pb.write_to_bytes()?;
let len = (pb_bytes.len() as u16).to_be_bytes();
let write_len = nio::write_all(st, len);
self.state = SendIdentityState::WritePayloadLen(write_len, pb_bytes);
},
SendIdentityState::WritePayloadLen(mut write_len, payload) => {
if let Async::Ready((st, _)) = write_len.poll()? {
self.state = SendIdentityState::WritePayload(nio::write_all(st, payload));
} else {
self.state = SendIdentityState::WritePayloadLen(write_len, payload);
return Ok(Async::NotReady)
}
},
SendIdentityState::WritePayload(mut write_payload) => {
if let Async::Ready((st, _)) = write_payload.poll()? {
self.state = SendIdentityState::Flush(st);
} else {
self.state = SendIdentityState::WritePayload(write_payload);
return Ok(Async::NotReady)
}
},
SendIdentityState::Flush(mut st) => {
if !st.poll_flush()?.is_ready() {
self.state = SendIdentityState::Flush(st);
return Ok(Async::NotReady)
}
return Ok(Async::Ready(st))
},
SendIdentityState::Done => panic!("SendIdentity polled after completion")
}
}
}
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
// Payloads for Noise handshake messages.
message Identity {
bytes pubkey = 1;
bytes signature = 2;
}

View File

@ -0,0 +1,260 @@
// This file is generated by rust-protobuf 2.3.0. Do not edit
// @generated
// https://github.com/Manishearth/rust-clippy/issues/702
#![allow(unknown_lints)]
#![allow(clippy)]
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(trivial_casts)]
#![allow(unsafe_code)]
#![allow(unused_imports)]
#![allow(unused_results)]
use protobuf::Message as Message_imported_for_functions;
use protobuf::ProtobufEnum as ProtobufEnum_imported_for_functions;
#[derive(PartialEq,Clone,Default)]
pub struct Identity {
// message fields
pub pubkey: ::std::vec::Vec<u8>,
pub signature: ::std::vec::Vec<u8>,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl Identity {
pub fn new() -> Identity {
::std::default::Default::default()
}
// bytes pubkey = 1;
pub fn clear_pubkey(&mut self) {
self.pubkey.clear();
}
// Param is passed by value, moved
pub fn set_pubkey(&mut self, v: ::std::vec::Vec<u8>) {
self.pubkey = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_pubkey(&mut self) -> &mut ::std::vec::Vec<u8> {
&mut self.pubkey
}
// Take field
pub fn take_pubkey(&mut self) -> ::std::vec::Vec<u8> {
::std::mem::replace(&mut self.pubkey, ::std::vec::Vec::new())
}
pub fn get_pubkey(&self) -> &[u8] {
&self.pubkey
}
// bytes signature = 2;
pub fn clear_signature(&mut self) {
self.signature.clear();
}
// Param is passed by value, moved
pub fn set_signature(&mut self, v: ::std::vec::Vec<u8>) {
self.signature = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_signature(&mut self) -> &mut ::std::vec::Vec<u8> {
&mut self.signature
}
// Take field
pub fn take_signature(&mut self) -> ::std::vec::Vec<u8> {
::std::mem::replace(&mut self.signature, ::std::vec::Vec::new())
}
pub fn get_signature(&self) -> &[u8] {
&self.signature
}
}
impl ::protobuf::Message for Identity {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.pubkey)?;
},
2 => {
::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.signature)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if !self.pubkey.is_empty() {
my_size += ::protobuf::rt::bytes_size(1, &self.pubkey);
}
if !self.signature.is_empty() {
my_size += ::protobuf::rt::bytes_size(2, &self.signature);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream) -> ::protobuf::ProtobufResult<()> {
if !self.pubkey.is_empty() {
os.write_bytes(1, &self.pubkey)?;
}
if !self.signature.is_empty() {
os.write_bytes(2, &self.signature)?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &::std::any::Any {
self as &::std::any::Any
}
fn as_any_mut(&mut self) -> &mut ::std::any::Any {
self as &mut ::std::any::Any
}
fn into_any(self: Box<Self>) -> ::std::boxed::Box<::std::any::Any> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> Identity {
Identity::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const ::protobuf::reflect::MessageDescriptor,
};
unsafe {
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>(
"pubkey",
|m: &Identity| { &m.pubkey },
|m: &mut Identity| { &mut m.pubkey },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>(
"signature",
|m: &Identity| { &m.signature },
|m: &mut Identity| { &mut m.signature },
));
::protobuf::reflect::MessageDescriptor::new::<Identity>(
"Identity",
fields,
file_descriptor_proto()
)
})
}
}
fn default_instance() -> &'static Identity {
static mut instance: ::protobuf::lazy::Lazy<Identity> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const Identity,
};
unsafe {
instance.get(Identity::new)
}
}
}
impl ::protobuf::Clear for Identity {
fn clear(&mut self) {
self.clear_pubkey();
self.clear_signature();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for Identity {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for Identity {
fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef {
::protobuf::reflect::ProtobufValueRef::Message(self)
}
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x1esrc/io/handshake/payload.proto\"@\n\x08Identity\x12\x16\n\x06pubke\
y\x18\x01\x20\x01(\x0cR\x06pubkey\x12\x1c\n\tsignature\x18\x02\x20\x01(\
\x0cR\tsignatureJ\xe0\x01\n\x06\x12\x04\0\0\x07\x01\n\x08\n\x01\x0c\x12\
\x03\0\0\x12\n4\n\x02\x04\0\x12\x04\x04\0\x07\x012(\x20Payloads\x20for\
\x20Noise\x20handshake\x20messages.\n\n\n\n\x03\x04\0\x01\x12\x03\x04\
\x08\x10\n\x0b\n\x04\x04\0\x02\0\x12\x03\x05\x08\x19\n\r\n\x05\x04\0\x02\
\0\x04\x12\x04\x05\x08\x04\x12\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\x05\
\x08\r\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x05\x0e\x14\n\x0c\n\x05\x04\0\
\x02\0\x03\x12\x03\x05\x17\x18\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x06\x08\
\x1c\n\r\n\x05\x04\0\x02\x01\x04\x12\x04\x06\x08\x05\x19\n\x0c\n\x05\x04\
\0\x02\x01\x05\x12\x03\x06\x08\r\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\
\x06\x0e\x17\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x06\x1a\x1bb\x06proto\
3\
";
static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const ::protobuf::descriptor::FileDescriptorProto,
};
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
::protobuf::parse_from_bytes(file_descriptor_proto_data).unwrap()
}
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
unsafe {
file_descriptor_proto_lazy.get(|| {
parse_descriptor_proto()
})
}
}

View File

@ -20,8 +20,11 @@
//! [Noise protocol framework][noise] support for libp2p. //! [Noise protocol framework][noise] support for libp2p.
//! //!
//! > **Note**: This crate is still experimental and subject to major breaking changes
//! > both on the API and the wire protocol.
//!
//! This crate provides `libp2p_core::InboundUpgrade` and `libp2p_core::OutboundUpgrade` //! This crate provides `libp2p_core::InboundUpgrade` and `libp2p_core::OutboundUpgrade`
//! implementations for various noise handshake patterns (currently IK, IX, and XX) //! implementations for various noise handshake patterns (currently `IK`, `IX`, and `XX`)
//! over a particular choice of DH key agreement (currently only X25519). //! over a particular choice of DH key agreement (currently only X25519).
//! //!
//! All upgrades produce as output a pair, consisting of the remote's static public key //! All upgrades produce as output a pair, consisting of the remote's static public key
@ -33,13 +36,15 @@
//! Example: //! Example:
//! //!
//! ``` //! ```
//! use libp2p_core::Transport; //! use libp2p_core::{identity, Transport};
//! use libp2p_tcp::TcpConfig; //! use libp2p_tcp::TcpConfig;
//! use libp2p_noise::{Keypair, X25519, NoiseConfig}; //! use libp2p_noise::{Keypair, X25519, NoiseConfig};
//! //!
//! # fn main() { //! # fn main() {
//! let keys = Keypair::<X25519>::new(); //! let id_keys = identity::Keypair::generate_ed25519();
//! let transport = TcpConfig::new().with_upgrade(NoiseConfig::xx(keys)); //! let dh_keys = Keypair::<X25519>::new().into_authentic(&id_keys).unwrap();
//! let noise = NoiseConfig::xx(dh_keys);
//! let transport = TcpConfig::new().with_upgrade(noise);
//! // ... //! // ...
//! # } //! # }
//! ``` //! ```
@ -50,32 +55,33 @@ mod error;
mod io; mod io;
mod protocol; mod protocol;
pub mod rt1;
pub mod rt15;
pub use error::NoiseError; pub use error::NoiseError;
pub use io::NoiseOutput; pub use io::NoiseOutput;
pub use protocol::{Keypair, PublicKey, Protocol, ProtocolParams, IX, IK, XX}; pub use io::handshake::{Handshake, RemoteIdentity, IdentityExchange};
pub use protocol::x25519::X25519; pub use protocol::{Keypair, AuthenticKeypair, KeypairIdentity, PublicKey, SecretKey};
pub use protocol::{Protocol, ProtocolParams, x25519::X25519, IX, IK, XX};
use libp2p_core::{UpgradeInfo, InboundUpgrade, OutboundUpgrade, upgrade::Negotiated}; use libp2p_core::{identity, UpgradeInfo, InboundUpgrade, OutboundUpgrade, upgrade::Negotiated};
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
use zeroize::Zeroize; use zeroize::Zeroize;
/// The protocol upgrade configuration. /// The protocol upgrade configuration.
#[derive(Clone)] #[derive(Clone)]
pub struct NoiseConfig<P, C: Zeroize, R = ()> { pub struct NoiseConfig<P, C: Zeroize, R = ()> {
keys: Keypair<C>, dh_keys: AuthenticKeypair<C>,
params: ProtocolParams, params: ProtocolParams,
remote: R, remote: R,
_marker: std::marker::PhantomData<P> _marker: std::marker::PhantomData<P>
} }
impl<C: Protocol<C> + Zeroize> NoiseConfig<IX, C> { impl<C> NoiseConfig<IX, C>
/// Create a new `NoiseConfig` for the IX handshake pattern. where
pub fn ix(keys: Keypair<C>) -> Self { C: Protocol<C> + Zeroize
{
/// Create a new `NoiseConfig` for the `IX` handshake pattern.
pub fn ix(dh_keys: AuthenticKeypair<C>) -> Self {
NoiseConfig { NoiseConfig {
keys, dh_keys,
params: C::params_ix(), params: C::params_ix(),
remote: (), remote: (),
_marker: std::marker::PhantomData _marker: std::marker::PhantomData
@ -83,11 +89,14 @@ impl<C: Protocol<C> + Zeroize> NoiseConfig<IX, C> {
} }
} }
impl<C: Protocol<C> + Zeroize> NoiseConfig<XX, C> { impl<C> NoiseConfig<XX, C>
/// Create a new `NoiseConfig` for the XX handshake pattern. where
pub fn xx(keys: Keypair<C>) -> Self { C: Protocol<C> + Zeroize
{
/// Create a new `NoiseConfig` for the `XX` handshake pattern.
pub fn xx(dh_keys: AuthenticKeypair<C>) -> Self {
NoiseConfig { NoiseConfig {
keys, dh_keys,
params: C::params_xx(), params: C::params_xx(),
remote: (), remote: (),
_marker: std::marker::PhantomData _marker: std::marker::PhantomData
@ -95,11 +104,17 @@ impl<C: Protocol<C> + Zeroize> NoiseConfig<XX, C> {
} }
} }
impl<C: Protocol<C> + Zeroize> NoiseConfig<IK, C> { impl<C> NoiseConfig<IK, C>
/// Create a new `NoiseConfig` for the IK handshake pattern (recipient side). where
pub fn ik_listener(keys: Keypair<C>) -> Self { C: Protocol<C> + Zeroize
{
/// Create a new `NoiseConfig` for the `IK` handshake pattern (recipient side).
///
/// Since the identity of the local node is known to the remote, this configuration
/// does not transmit a static DH public key or public identity key to the remote.
pub fn ik_listener(dh_keys: AuthenticKeypair<C>) -> Self {
NoiseConfig { NoiseConfig {
keys, dh_keys,
params: C::params_ik(), params: C::params_ik(),
remote: (), remote: (),
_marker: std::marker::PhantomData _marker: std::marker::PhantomData
@ -107,13 +122,23 @@ impl<C: Protocol<C> + Zeroize> NoiseConfig<IK, C> {
} }
} }
impl<C: Protocol<C> + Zeroize> NoiseConfig<IK, C, PublicKey<C>> { impl<C> NoiseConfig<IK, C, (PublicKey<C>, identity::PublicKey)>
/// Create a new `NoiseConfig` for the IK handshake pattern (initiator side). where
pub fn ik_dialer(keys: Keypair<C>, remote: PublicKey<C>) -> Self { C: Protocol<C> + Zeroize
{
/// Create a new `NoiseConfig` for the `IK` handshake pattern (initiator side).
///
/// In this configuration, the remote identity is known to the local node,
/// but the local node still needs to transmit its own public identity.
pub fn ik_dialer(
dh_keys: AuthenticKeypair<C>,
remote_id: identity::PublicKey,
remote_dh: PublicKey<C>
) -> Self {
NoiseConfig { NoiseConfig {
keys, dh_keys,
params: C::params_ik(), params: C::params_ik(),
remote, remote: (remote_dh, remote_id),
_marker: std::marker::PhantomData _marker: std::marker::PhantomData
} }
} }
@ -123,39 +148,43 @@ impl<C: Protocol<C> + Zeroize> NoiseConfig<IK, C, PublicKey<C>> {
impl<T, C> InboundUpgrade<T> for NoiseConfig<IX, C> impl<T, C> InboundUpgrade<T> for NoiseConfig<IX, C>
where where
T: AsyncRead + AsyncWrite,
NoiseConfig<IX, C>: UpgradeInfo, NoiseConfig<IX, C>: UpgradeInfo,
C: Protocol<C> + AsRef<[u8]> + Zeroize T: AsyncRead + AsyncWrite + Send + 'static,
C: Protocol<C> + AsRef<[u8]> + Zeroize + Send + 'static,
{ {
type Output = (PublicKey<C>, NoiseOutput<Negotiated<T>>); type Output = (RemoteIdentity<C>, NoiseOutput<Negotiated<T>>);
type Error = NoiseError; type Error = NoiseError;
type Future = rt1::NoiseInboundFuture<Negotiated<T>, C>; type Future = Handshake<Negotiated<T>, C>;
fn upgrade_inbound(self, socket: Negotiated<T>, _: Self::Info) -> Self::Future { fn upgrade_inbound(self, socket: Negotiated<T>, _: Self::Info) -> Self::Future {
let session = self.params.into_builder() let session = self.params.into_builder()
.local_private_key(self.keys.secret().as_ref()) .local_private_key(self.dh_keys.secret().as_ref())
.build_responder() .build_responder()
.map_err(NoiseError::from); .map_err(NoiseError::from);
rt1::NoiseInboundFuture::new(socket, session) Handshake::rt1_responder(socket, session,
self.dh_keys.into_identity(),
IdentityExchange::Mutual)
} }
} }
impl<T, C> OutboundUpgrade<T> for NoiseConfig<IX, C> impl<T, C> OutboundUpgrade<T> for NoiseConfig<IX, C>
where where
T: AsyncRead + AsyncWrite,
NoiseConfig<IX, C>: UpgradeInfo, NoiseConfig<IX, C>: UpgradeInfo,
C: Protocol<C> + AsRef<[u8]> + Zeroize T: AsyncRead + AsyncWrite + Send + 'static,
C: Protocol<C> + AsRef<[u8]> + Zeroize + Send + 'static,
{ {
type Output = (PublicKey<C>, NoiseOutput<Negotiated<T>>); type Output = (RemoteIdentity<C>, NoiseOutput<Negotiated<T>>);
type Error = NoiseError; type Error = NoiseError;
type Future = rt1::NoiseOutboundFuture<Negotiated<T>, C>; type Future = Handshake<Negotiated<T>, C>;
fn upgrade_outbound(self, socket: Negotiated<T>, _: Self::Info) -> Self::Future { fn upgrade_outbound(self, socket: Negotiated<T>, _: Self::Info) -> Self::Future {
let session = self.params.into_builder() let session = self.params.into_builder()
.local_private_key(self.keys.secret().as_ref()) .local_private_key(self.dh_keys.secret().as_ref())
.build_initiator() .build_initiator()
.map_err(NoiseError::from); .map_err(NoiseError::from);
rt1::NoiseOutboundFuture::new(socket, session) Handshake::rt1_initiator(socket, session,
self.dh_keys.into_identity(),
IdentityExchange::Mutual)
} }
} }
@ -163,39 +192,43 @@ where
impl<T, C> InboundUpgrade<T> for NoiseConfig<XX, C> impl<T, C> InboundUpgrade<T> for NoiseConfig<XX, C>
where where
T: AsyncRead + AsyncWrite,
NoiseConfig<XX, C>: UpgradeInfo, NoiseConfig<XX, C>: UpgradeInfo,
C: Protocol<C> + AsRef<[u8]> + Zeroize T: AsyncRead + AsyncWrite + Send + 'static,
C: Protocol<C> + AsRef<[u8]> + Zeroize + Send + 'static,
{ {
type Output = (PublicKey<C>, NoiseOutput<Negotiated<T>>); type Output = (RemoteIdentity<C>, NoiseOutput<Negotiated<T>>);
type Error = NoiseError; type Error = NoiseError;
type Future = rt15::NoiseInboundFuture<Negotiated<T>, C>; type Future = Handshake<Negotiated<T>, C>;
fn upgrade_inbound(self, socket: Negotiated<T>, _: Self::Info) -> Self::Future { fn upgrade_inbound(self, socket: Negotiated<T>, _: Self::Info) -> Self::Future {
let session = self.params.into_builder() let session = self.params.into_builder()
.local_private_key(self.keys.secret().as_ref()) .local_private_key(self.dh_keys.secret().as_ref())
.build_responder() .build_responder()
.map_err(NoiseError::from); .map_err(NoiseError::from);
rt15::NoiseInboundFuture::new(socket, session) Handshake::rt15_responder(socket, session,
self.dh_keys.into_identity(),
IdentityExchange::Mutual)
} }
} }
impl<T, C> OutboundUpgrade<T> for NoiseConfig<XX, C> impl<T, C> OutboundUpgrade<T> for NoiseConfig<XX, C>
where where
T: AsyncRead + AsyncWrite,
NoiseConfig<XX, C>: UpgradeInfo, NoiseConfig<XX, C>: UpgradeInfo,
C: Protocol<C> + AsRef<[u8]> + Zeroize T: AsyncRead + AsyncWrite + Send + 'static,
C: Protocol<C> + AsRef<[u8]> + Zeroize + Send + 'static,
{ {
type Output = (PublicKey<C>, NoiseOutput<Negotiated<T>>); type Output = (RemoteIdentity<C>, NoiseOutput<Negotiated<T>>);
type Error = NoiseError; type Error = NoiseError;
type Future = rt15::NoiseOutboundFuture<Negotiated<T>, C>; type Future = Handshake<Negotiated<T>, C>;
fn upgrade_outbound(self, socket: Negotiated<T>, _: Self::Info) -> Self::Future { fn upgrade_outbound(self, socket: Negotiated<T>, _: Self::Info) -> Self::Future {
let session = self.params.into_builder() let session = self.params.into_builder()
.local_private_key(self.keys.secret().as_ref()) .local_private_key(self.dh_keys.secret().as_ref())
.build_initiator() .build_initiator()
.map_err(NoiseError::from); .map_err(NoiseError::from);
rt15::NoiseOutboundFuture::new(socket, session) Handshake::rt15_initiator(socket, session,
self.dh_keys.into_identity(),
IdentityExchange::Mutual)
} }
} }
@ -203,40 +236,44 @@ where
impl<T, C> InboundUpgrade<T> for NoiseConfig<IK, C> impl<T, C> InboundUpgrade<T> for NoiseConfig<IK, C>
where where
T: AsyncRead + AsyncWrite,
NoiseConfig<IK, C>: UpgradeInfo, NoiseConfig<IK, C>: UpgradeInfo,
C: Protocol<C> + AsRef<[u8]> + Zeroize T: AsyncRead + AsyncWrite + Send + 'static,
C: Protocol<C> + AsRef<[u8]> + Zeroize + Send + 'static,
{ {
type Output = (PublicKey<C>, NoiseOutput<Negotiated<T>>); type Output = (RemoteIdentity<C>, NoiseOutput<Negotiated<T>>);
type Error = NoiseError; type Error = NoiseError;
type Future = rt1::NoiseInboundFuture<Negotiated<T>, C>; type Future = Handshake<Negotiated<T>, C>;
fn upgrade_inbound(self, socket: Negotiated<T>, _: Self::Info) -> Self::Future { fn upgrade_inbound(self, socket: Negotiated<T>, _: Self::Info) -> Self::Future {
let session = self.params.into_builder() let session = self.params.into_builder()
.local_private_key(self.keys.secret().as_ref()) .local_private_key(self.dh_keys.secret().as_ref())
.build_responder() .build_responder()
.map_err(NoiseError::from); .map_err(NoiseError::from);
rt1::NoiseInboundFuture::new(socket, session) Handshake::rt1_responder(socket, session,
self.dh_keys.into_identity(),
IdentityExchange::Receive)
} }
} }
impl<T, C> OutboundUpgrade<T> for NoiseConfig<IK, C, PublicKey<C>> impl<T, C> OutboundUpgrade<T> for NoiseConfig<IK, C, (PublicKey<C>, identity::PublicKey)>
where where
T: AsyncRead + AsyncWrite, NoiseConfig<IK, C, (PublicKey<C>, identity::PublicKey)>: UpgradeInfo,
NoiseConfig<IK, C, PublicKey<C>>: UpgradeInfo, T: AsyncRead + AsyncWrite + Send + 'static,
C: Protocol<C> + AsRef<[u8]> + Zeroize C: Protocol<C> + AsRef<[u8]> + Zeroize + Send + 'static,
{ {
type Output = (PublicKey<C>, NoiseOutput<Negotiated<T>>); type Output = (RemoteIdentity<C>, NoiseOutput<Negotiated<T>>);
type Error = NoiseError; type Error = NoiseError;
type Future = rt1::NoiseOutboundFuture<Negotiated<T>, C>; type Future = Handshake<Negotiated<T>, C>;
fn upgrade_outbound(self, socket: Negotiated<T>, _: Self::Info) -> Self::Future { fn upgrade_outbound(self, socket: Negotiated<T>, _: Self::Info) -> Self::Future {
let session = self.params.into_builder() let session = self.params.into_builder()
.local_private_key(self.keys.secret().as_ref()) .local_private_key(self.dh_keys.secret().as_ref())
.remote_public_key(self.remote.as_ref()) .remote_public_key(self.remote.0.as_ref())
.build_initiator() .build_initiator()
.map_err(NoiseError::from); .map_err(NoiseError::from);
rt1::NoiseOutboundFuture::new(socket, session) Handshake::rt1_initiator(socket, session,
self.dh_keys.into_identity(),
IdentityExchange::Send { remote: self.remote.1 })
} }
} }

View File

@ -23,6 +23,7 @@
pub mod x25519; pub mod x25519;
use crate::NoiseError; use crate::NoiseError;
use libp2p_core::identity;
use rand::FromEntropy; use rand::FromEntropy;
use zeroize::Zeroize; use zeroize::Zeroize;
@ -59,15 +60,80 @@ pub trait Protocol<C> {
fn params_ix() -> ProtocolParams; fn params_ix() -> ProtocolParams;
/// The protocol parameters for the XX handshake pattern. /// The protocol parameters for the XX handshake pattern.
fn params_xx() -> ProtocolParams; fn params_xx() -> ProtocolParams;
/// Construct a DH public key from a byte slice. /// Construct a DH public key from a byte slice.
fn public_from_bytes(s: &[u8]) -> Result<PublicKey<C>, NoiseError>; fn public_from_bytes(s: &[u8]) -> Result<PublicKey<C>, NoiseError>;
/// Determines whether the authenticity of the given DH static public key
/// and public identity key is linked, i.e. that proof of ownership of a
/// secret key for the static DH public key implies that the key is
/// authentic w.r.t. the given public identity key.
///
/// The trivial case is when the keys are byte for byte identical.
#[allow(unused_variables)]
fn linked(id_pk: &identity::PublicKey, dh_pk: &PublicKey<C>) -> bool {
false
}
/// Verifies that a given static DH public key is authentic w.r.t. a
/// given public identity key in the context of an optional signature.
///
/// The given static DH public key is assumed to already be authentic
/// in the sense that possession of a corresponding secret key has been
/// established, as is the case at the end of a Noise handshake involving
/// static DH keys.
///
/// If the public keys are [`linked`](Protocol::linked), verification succeeds
/// without a signature, otherwise a signature over the static DH public key
/// must be given and is verified with the public identity key, establishing
/// the authenticity of the static DH public key w.r.t. the public identity key.
fn verify(id_pk: &identity::PublicKey, dh_pk: &PublicKey<C>, sig: &Option<Vec<u8>>) -> bool
where
C: AsRef<[u8]>
{
Self::linked(id_pk, dh_pk)
||
sig.as_ref().map_or(false, |s| id_pk.verify(dh_pk.as_ref(), s))
}
} }
/// DH keypair. /// DH keypair.
#[derive(Clone)] #[derive(Clone)]
pub struct Keypair<T: Zeroize> { pub struct Keypair<T: Zeroize> {
secret: SecretKey<T>, secret: SecretKey<T>,
public: PublicKey<T> public: PublicKey<T>,
}
/// A DH keypair that is authentic w.r.t. a [`identity::PublicKey`].
#[derive(Clone)]
pub struct AuthenticKeypair<T: Zeroize> {
keypair: Keypair<T>,
identity: KeypairIdentity
}
impl<T: Zeroize> AuthenticKeypair<T> {
/// Extract the public [`KeypairIdentity`] from this `AuthenticKeypair`,
/// dropping the DH `Keypair`.
pub fn into_identity(self) -> KeypairIdentity {
self.identity
}
}
impl<T: Zeroize> std::ops::Deref for AuthenticKeypair<T> {
type Target = Keypair<T>;
fn deref(&self) -> &Self::Target {
&self.keypair
}
}
/// The associated public identity of a DH keypair.
#[derive(Clone)]
pub struct KeypairIdentity {
/// The public identity key.
pub public: identity::PublicKey,
/// The signature over the public DH key.
pub signature: Option<Vec<u8>>
} }
impl<T: Zeroize> Keypair<T> { impl<T: Zeroize> Keypair<T> {
@ -80,6 +146,22 @@ impl<T: Zeroize> Keypair<T> {
pub fn secret(&self) -> &SecretKey<T> { pub fn secret(&self) -> &SecretKey<T> {
&self.secret &self.secret
} }
/// Turn this DH keypair into a [`AuthenticKeypair`], i.e. a DH keypair that
/// is authentic w.r.t. the given identity keypair, by signing the DH public key.
pub fn into_authentic(self, id_keys: &identity::Keypair) -> Result<AuthenticKeypair<T>, NoiseError>
where
T: AsRef<[u8]>
{
let sig = id_keys.sign(self.public.as_ref())?;
let identity = KeypairIdentity {
public: id_keys.public(),
signature: Some(sig)
};
Ok(AuthenticKeypair { keypair: self, identity })
}
} }
/// DH secret key. /// DH secret key.

View File

@ -24,7 +24,7 @@ use crate::{NoiseConfig, NoiseError, Protocol, ProtocolParams};
use curve25519_dalek::edwards::CompressedEdwardsY; use curve25519_dalek::edwards::CompressedEdwardsY;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use libp2p_core::UpgradeInfo; use libp2p_core::UpgradeInfo;
use libp2p_core::identity::ed25519; use libp2p_core::{identity, identity::ed25519};
use rand::Rng; use rand::Rng;
use ring::digest::{SHA512, digest}; use ring::digest::{SHA512, digest};
use x25519_dalek::{X25519_BASEPOINT_BYTES, x25519}; use x25519_dalek::{X25519_BASEPOINT_BYTES, x25519};
@ -92,7 +92,7 @@ impl UpgradeInfo for NoiseConfig<IK, X25519> {
} }
} }
impl UpgradeInfo for NoiseConfig<IK, X25519, PublicKey<X25519>> { impl UpgradeInfo for NoiseConfig<IK, X25519, (PublicKey<X25519>, identity::PublicKey)> {
type Info = &'static [u8]; type Info = &'static [u8];
type InfoIter = std::iter::Once<Self::Info>; type InfoIter = std::iter::Once<Self::Info>;
@ -123,6 +123,14 @@ impl Protocol<X25519> for X25519 {
pk.copy_from_slice(bytes); pk.copy_from_slice(bytes);
Ok(PublicKey(X25519(pk))) Ok(PublicKey(X25519(pk)))
} }
fn linked(id_pk: &identity::PublicKey, dh_pk: &PublicKey<X25519>) -> bool {
if let identity::PublicKey::Ed25519(ref p) = id_pk {
PublicKey::from_ed25519(p).as_ref() == dh_pk.as_ref()
} else {
false
}
}
} }
impl Keypair<X25519> { impl Keypair<X25519> {
@ -143,6 +151,38 @@ impl Keypair<X25519> {
sk_bytes.zeroize(); sk_bytes.zeroize();
Self::from(sk) Self::from(sk)
} }
/// Creates an X25519 `Keypair` from an [`identity::Keypair`], if possible.
///
/// The returned keypair will be [associated with](KeypairIdentity) the
/// given identity keypair.
///
/// Returns `None` if the given identity keypair cannot be used as an X25519 keypair.
///
/// > **Note**: If the identity keypair is already used in the context
/// > of other cryptographic protocols outside of Noise, e.g. for
/// > signing in the `secio` protocol, it should be preferred to
/// > create a new static X25519 keypair for use in the Noise protocol.
/// >
/// > See also:
/// >
/// > * [Noise: Static Key Reuse](http://www.noiseprotocol.org/noise.html#security-considerations)
pub fn from_identity(id_keys: &identity::Keypair) -> Option<AuthenticKeypair<X25519>> {
match id_keys {
identity::Keypair::Ed25519(p) => {
let kp = Keypair::from(SecretKey::from_ed25519(&p.secret()));
let id = KeypairIdentity {
public: id_keys.public(),
signature: None
};
Some(AuthenticKeypair {
keypair: kp,
identity: id
})
}
_ => None
}
}
} }
/// Promote a X25519 secret key into a keypair. /// Promote a X25519 secret key into a keypair.
@ -166,15 +206,15 @@ impl PublicKey<X25519> {
impl SecretKey<X25519> { impl SecretKey<X25519> {
/// Construct a X25519 secret key from a Ed25519 secret key. /// Construct a X25519 secret key from a Ed25519 secret key.
/// ///
/// *Note*: If the Ed25519 secret key is already used in the context /// > **Note**: If the Ed25519 secret key is already used in the context
/// of other cryptographic protocols outside of Noise, e.g. for /// > of other cryptographic protocols outside of Noise, e.g. for
/// signing in the `secio` protocol, it should be preferred to /// > signing in the `secio` protocol, it should be preferred to
/// create a new keypair for use in the Noise protocol. /// > create a new keypair for use in the Noise protocol.
/// /// >
/// See also: /// > See also:
/// /// >
/// [Noise: Static Key Reuse](http://www.noiseprotocol.org/noise.html#security-considerations) /// > * [Noise: Static Key Reuse](http://www.noiseprotocol.org/noise.html#security-considerations)
/// [Ed25519 to Curve25519](https://libsodium.gitbook.io/doc/advanced/ed25519-curve25519) /// > * [Ed25519 to Curve25519](https://libsodium.gitbook.io/doc/advanced/ed25519-curve25519)
pub fn from_ed25519(ed25519_sk: &ed25519::SecretKey) -> Self { pub fn from_ed25519(ed25519_sk: &ed25519::SecretKey) -> Self {
// An Ed25519 public key is derived off the left half of the SHA512 of the // An Ed25519 public key is derived off the left half of the SHA512 of the
// secret scalar, hence a matching conversion of the secret key must do // secret scalar, hence a matching conversion of the secret key must do

View File

@ -1,187 +0,0 @@
// 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::{
Protocol,
PublicKey,
NoiseError,
io::{Handshake, NoiseOutput},
};
use futures::prelude::*;
use snow;
use std::mem;
use std::marker::PhantomData;
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, C> {
state: InboundState<T>,
_phantom: PhantomData<C>
}
impl<T, C> NoiseInboundFuture<T, C> {
pub(super) fn new(io: T, session: Result<snow::Session, NoiseError>) -> Self {
match session {
Ok(s) => NoiseInboundFuture {
state: InboundState::RecvHandshake(Handshake::new(io, s)),
_phantom: PhantomData
},
Err(e) => NoiseInboundFuture {
state: InboundState::Err(e),
_phantom: PhantomData
}
}
}
}
enum InboundState<T> {
RecvHandshake(Handshake<T>),
SendHandshake(Handshake<T>),
Flush(Handshake<T>),
Err(NoiseError),
Done
}
impl<T, C: Protocol<C>> Future for NoiseInboundFuture<T, C>
where
T: AsyncRead + AsyncWrite
{
type Item = (PublicKey<C>, NoiseOutput<T>);
type Error = NoiseError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
match mem::replace(&mut self.state, InboundState::Done) {
InboundState::RecvHandshake(mut io) => {
if io.receive()?.is_ready() {
self.state = InboundState::SendHandshake(io)
} else {
self.state = InboundState::RecvHandshake(io);
return Ok(Async::NotReady)
}
}
InboundState::SendHandshake(mut io) => {
if io.send()?.is_ready() {
self.state = InboundState::Flush(io)
} else {
self.state = InboundState::SendHandshake(io);
return Ok(Async::NotReady)
}
}
InboundState::Flush(mut io) => {
if io.flush()?.is_ready() {
let result = io.finish::<C>()?;
self.state = InboundState::Done;
return Ok(Async::Ready(result))
} else {
self.state = 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, C> {
state: OutboundState<T>,
_phantom: PhantomData<C>
}
impl<T, C> NoiseOutboundFuture<T, C> {
pub(super) fn new(io: T, session: Result<snow::Session, NoiseError>) -> Self {
match session {
Ok(s) => NoiseOutboundFuture {
state: OutboundState::SendHandshake(Handshake::new(io, s)),
_phantom: PhantomData
},
Err(e) => NoiseOutboundFuture {
state: OutboundState::Err(e),
_phantom: PhantomData
}
}
}
}
enum OutboundState<T> {
SendHandshake(Handshake<T>),
Flush(Handshake<T>),
RecvHandshake(Handshake<T>),
Err(NoiseError),
Done
}
impl<T, C: Protocol<C>> Future for NoiseOutboundFuture<T, C>
where
T: AsyncRead + AsyncWrite
{
type Item = (PublicKey<C>, NoiseOutput<T>);
type Error = NoiseError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
match mem::replace(&mut self.state, OutboundState::Done) {
OutboundState::SendHandshake(mut io) => {
if io.send()?.is_ready() {
self.state = OutboundState::Flush(io)
} else {
self.state = OutboundState::SendHandshake(io);
return Ok(Async::NotReady)
}
}
OutboundState::Flush(mut io) => {
if io.flush()?.is_ready() {
self.state = OutboundState::RecvHandshake(io)
} else {
self.state = OutboundState::Flush(io);
return Ok(Async::NotReady)
}
}
OutboundState::RecvHandshake(mut io) => {
if io.receive()?.is_ready() {
let result = io.finish::<C>()?;
self.state = OutboundState::Done;
return Ok(Async::Ready(result))
} else {
self.state = OutboundState::RecvHandshake(io);
return Ok(Async::NotReady)
}
}
OutboundState::Err(e) => return Err(e),
OutboundState::Done => panic!("NoiseOutboundFuture::poll called after completion")
}
}
}
}

View File

@ -1,216 +0,0 @@
// 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::{
Protocol,
PublicKey,
NoiseError,
io::{Handshake, NoiseOutput},
};
use futures::prelude::*;
use snow;
use std::mem;
use std::marker::PhantomData;
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, C> {
state: InboundState<T>,
_phantom: PhantomData<C>
}
impl<T, C> NoiseInboundFuture<T, C> {
pub(super) fn new(io: T, session: Result<snow::Session, NoiseError>) -> Self {
match session {
Ok(s) => NoiseInboundFuture {
state: InboundState::RecvHandshake1(Handshake::new(io, s)),
_phantom: PhantomData
},
Err(e) => NoiseInboundFuture {
state: InboundState::Err(e),
_phantom: PhantomData
}
}
}
}
enum InboundState<T> {
RecvHandshake1(Handshake<T>),
SendHandshake(Handshake<T>),
Flush(Handshake<T>),
RecvHandshake2(Handshake<T>),
Err(NoiseError),
Done
}
impl<T, C: Protocol<C>> Future for NoiseInboundFuture<T, C>
where
T: AsyncRead + AsyncWrite
{
type Item = (PublicKey<C>, NoiseOutput<T>);
type Error = NoiseError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
match mem::replace(&mut self.state, InboundState::Done) {
InboundState::RecvHandshake1(mut io) => {
if io.receive()?.is_ready() {
self.state = InboundState::SendHandshake(io)
} else {
self.state = InboundState::RecvHandshake1(io);
return Ok(Async::NotReady)
}
}
InboundState::SendHandshake(mut io) => {
if io.send()?.is_ready() {
self.state = InboundState::Flush(io)
} else {
self.state = InboundState::SendHandshake(io);
return Ok(Async::NotReady)
}
}
InboundState::Flush(mut io) => {
if io.flush()?.is_ready() {
self.state = InboundState::RecvHandshake2(io)
} else {
self.state = InboundState::Flush(io);
return Ok(Async::NotReady)
}
}
InboundState::RecvHandshake2(mut io) => {
if io.receive()?.is_ready() {
let result = io.finish::<C>()?;
self.state = InboundState::Done;
return Ok(Async::Ready(result))
} else {
self.state = 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, C> {
state: OutboundState<T>,
_phantom: PhantomData<C>
}
impl<T, C> NoiseOutboundFuture<T, C> {
pub(super) fn new(io: T, session: Result<snow::Session, NoiseError>) -> Self {
match session {
Ok(s) => NoiseOutboundFuture {
state: OutboundState::SendHandshake1(Handshake::new(io, s)),
_phantom: PhantomData
},
Err(e) => NoiseOutboundFuture {
state: OutboundState::Err(e),
_phantom: PhantomData
}
}
}
}
enum OutboundState<T> {
SendHandshake1(Handshake<T>),
Flush1(Handshake<T>),
RecvHandshake(Handshake<T>),
SendHandshake2(Handshake<T>),
Flush2(Handshake<T>),
Err(NoiseError),
Done
}
impl<T, C: Protocol<C>> Future for NoiseOutboundFuture<T, C>
where
T: AsyncRead + AsyncWrite
{
type Item = (PublicKey<C>, NoiseOutput<T>);
type Error = NoiseError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
match mem::replace(&mut self.state, OutboundState::Done) {
OutboundState::SendHandshake1(mut io) => {
if io.send()?.is_ready() {
self.state = OutboundState::Flush1(io)
} else {
self.state = OutboundState::SendHandshake1(io);
return Ok(Async::NotReady)
}
}
OutboundState::Flush1(mut io) => {
if io.flush()?.is_ready() {
self.state = OutboundState::RecvHandshake(io)
} else {
self.state = OutboundState::Flush1(io);
return Ok(Async::NotReady)
}
}
OutboundState::RecvHandshake(mut io) => {
if io.receive()?.is_ready() {
self.state = OutboundState::SendHandshake2(io)
} else {
self.state = OutboundState::RecvHandshake(io);
return Ok(Async::NotReady)
}
}
OutboundState::SendHandshake2(mut io) => {
if io.send()?.is_ready() {
self.state = OutboundState::Flush2(io)
} else {
self.state = OutboundState::SendHandshake2(io);
return Ok(Async::NotReady)
}
}
OutboundState::Flush2(mut io) => {
if io.flush()?.is_ready() {
let result = io.finish::<C>()?;
self.state = OutboundState::Done;
return Ok(Async::Ready(result))
} else {
self.state = OutboundState::Flush2(io);
return Ok(Async::NotReady)
}
}
OutboundState::Err(e) => return Err(e),
OutboundState::Done => panic!("NoiseOutboundFuture::poll called after completion")
}
}
}
}

View File

@ -18,10 +18,12 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
use futures::{future::Either, prelude::*}; use futures::{future::{self, Either}, prelude::*};
use libp2p_core::{Transport, transport::ListenerEvent, upgrade::{apply_inbound, apply_outbound}}; use libp2p_core::identity;
use libp2p_noise::{Keypair, X25519, NoiseConfig}; use libp2p_core::upgrade::{Negotiated, apply_inbound, apply_outbound};
use libp2p_tcp::TcpConfig; use libp2p_core::transport::{Transport, ListenerEvent};
use libp2p_noise::{Keypair, X25519, NoiseConfig, RemoteIdentity, NoiseError, NoiseOutput};
use libp2p_tcp::{TcpConfig, TcpTransStream};
use log::info; use log::info;
use quickcheck::QuickCheck; use quickcheck::QuickCheck;
use tokio::{self, io}; use tokio::{self, io};
@ -30,12 +32,21 @@ use tokio::{self, io};
fn xx() { fn xx() {
let _ = env_logger::try_init(); let _ = env_logger::try_init();
fn prop(message: Vec<u8>) -> bool { fn prop(message: Vec<u8>) -> bool {
let server_id = identity::Keypair::generate_ed25519();
let client_id = identity::Keypair::generate_ed25519();
let server_keypair = Keypair::<X25519>::new(); let server_id_public = server_id.public();
let server_transport = TcpConfig::new().with_upgrade(NoiseConfig::xx(server_keypair)); let client_id_public = client_id.public();
let client_keypair = Keypair::<X25519>::new(); let server_dh = Keypair::<X25519>::new().into_authentic(&server_id).unwrap();
let client_transport = TcpConfig::new().with_upgrade(NoiseConfig::xx(client_keypair)); let server_transport = TcpConfig::new()
.with_upgrade(NoiseConfig::xx(server_dh))
.and_then(move |out, _| expect_identity(out, &client_id_public));
let client_dh = Keypair::<X25519>::new().into_authentic(&client_id).unwrap();
let client_transport = TcpConfig::new()
.with_upgrade(NoiseConfig::xx(client_dh))
.and_then(move |out, _| expect_identity(out, &server_id_public));
run(server_transport, client_transport, message); run(server_transport, client_transport, message);
true true
@ -47,12 +58,21 @@ fn xx() {
fn ix() { fn ix() {
let _ = env_logger::try_init(); let _ = env_logger::try_init();
fn prop(message: Vec<u8>) -> bool { fn prop(message: Vec<u8>) -> bool {
let server_id = identity::Keypair::generate_ed25519();
let client_id = identity::Keypair::generate_ed25519();
let server_keypair = Keypair::<X25519>::new(); let server_id_public = server_id.public();
let server_transport = TcpConfig::new().with_upgrade(NoiseConfig::ix(server_keypair)); let client_id_public = client_id.public();
let client_keypair = Keypair::<X25519>::new(); let server_dh = Keypair::<X25519>::new().into_authentic(&server_id).unwrap();
let client_transport = TcpConfig::new().with_upgrade(NoiseConfig::ix(client_keypair)); let server_transport = TcpConfig::new()
.with_upgrade(NoiseConfig::ix(server_dh))
.and_then(move |out, _| expect_identity(out, &client_id_public));
let client_dh = Keypair::<X25519>::new().into_authentic(&client_id).unwrap();
let client_transport = TcpConfig::new()
.with_upgrade(NoiseConfig::ix(client_dh))
.and_then(move |out, _| expect_identity(out, &server_id_public));
run(server_transport, client_transport, message); run(server_transport, client_transport, message);
true true
@ -64,26 +84,36 @@ fn ix() {
fn ik_xx() { fn ik_xx() {
let _ = env_logger::try_init(); let _ = env_logger::try_init();
fn prop(message: Vec<u8>) -> bool { fn prop(message: Vec<u8>) -> bool {
let server_keypair = Keypair::<X25519>::new(); let server_id = identity::Keypair::generate_ed25519();
let server_public = server_keypair.public().clone(); let server_id_public = server_id.public();
let client_id = identity::Keypair::generate_ed25519();
let client_id_public = client_id.public();
let server_dh = Keypair::<X25519>::new().into_authentic(&server_id).unwrap();
let server_dh_public = server_dh.public().clone();
let server_transport = TcpConfig::new() let server_transport = TcpConfig::new()
.and_then(move |output, endpoint| { .and_then(move |output, endpoint| {
if endpoint.is_listener() { if endpoint.is_listener() {
Either::A(apply_inbound(output, NoiseConfig::ik_listener(server_keypair))) Either::A(apply_inbound(output, NoiseConfig::ik_listener(server_dh)))
} else { } else {
Either::B(apply_outbound(output, NoiseConfig::xx(server_keypair))) Either::B(apply_outbound(output, NoiseConfig::xx(server_dh)))
} }
}); })
.and_then(move |out, _| expect_identity(out, &client_id_public));
let client_keypair = Keypair::<X25519>::new(); let client_dh = Keypair::<X25519>::new().into_authentic(&client_id).unwrap();
let server_id_public2 = server_id_public.clone();
let client_transport = TcpConfig::new() let client_transport = TcpConfig::new()
.and_then(move |output, endpoint| { .and_then(move |output, endpoint| {
if endpoint.is_dialer() { if endpoint.is_dialer() {
Either::A(apply_outbound(output, NoiseConfig::ik_dialer(client_keypair, server_public))) Either::A(apply_outbound(output,
NoiseConfig::ik_dialer(client_dh, server_id_public, server_dh_public)))
} else { } else {
Either::B(apply_inbound(output, NoiseConfig::xx(client_keypair))) Either::B(apply_inbound(output, NoiseConfig::xx(client_dh)))
} }
}); })
.and_then(move |out, _| expect_identity(out, &server_id_public2));
run(server_transport, client_transport, message); run(server_transport, client_transport, message);
true true
@ -91,18 +121,18 @@ fn ik_xx() {
QuickCheck::new().max_tests(30).quickcheck(prop as fn(Vec<u8>) -> bool) QuickCheck::new().max_tests(30).quickcheck(prop as fn(Vec<u8>) -> bool)
} }
fn run<T, A, U, B, P>(server_transport: T, client_transport: U, message1: Vec<u8>) type Output = (RemoteIdentity<X25519>, NoiseOutput<Negotiated<TcpTransStream>>);
fn run<T, U>(server_transport: T, client_transport: U, message1: Vec<u8>)
where where
T: Transport<Output = (P, A)>, T: Transport<Output = Output>,
T::Dial: Send + 'static, T::Dial: Send + 'static,
T::Listener: Send + 'static, T::Listener: Send + 'static,
T::ListenerUpgrade: Send + 'static, T::ListenerUpgrade: Send + 'static,
A: io::AsyncRead + io::AsyncWrite + Send + 'static, U: Transport<Output = Output>,
U: Transport<Output = (P, B)>,
U::Dial: Send + 'static, U::Dial: Send + 'static,
U::Listener: Send + 'static, U::Listener: Send + 'static,
U::ListenerUpgrade: Send + 'static, U::ListenerUpgrade: Send + 'static,
B: io::AsyncRead + io::AsyncWrite + Send + 'static
{ {
let message2 = message1.clone(); let message2 = message1.clone();
@ -144,3 +174,11 @@ where
tokio::run(future) tokio::run(future)
} }
fn expect_identity(output: Output, pk: &identity::PublicKey)
-> impl Future<Item = Output, Error = NoiseError>
{
match output.0 {
RemoteIdentity::IdentityKey(ref k) if k == pk => future::ok(output),
_ => panic!("Unexpected remote identity")
}
}

View File

@ -88,6 +88,7 @@ mod codec;
mod error; mod error;
mod exchange; mod exchange;
mod handshake; mod handshake;
// #[allow(rust_2018_idioms)]
mod structs_proto; mod structs_proto;
mod stream_cipher; mod stream_cipher;

View File

@ -241,15 +241,14 @@ pub fn build_tcp_ws_secio_mplex_yamux(keypair: identity::Keypair)
{ {
CommonTransport::new() CommonTransport::new()
.with_upgrade(secio::SecioConfig::new(keypair)) .with_upgrade(secio::SecioConfig::new(keypair))
.and_then(move |out, endpoint| { .and_then(move |output, endpoint| {
let peer_id = PeerId::from(out.remote_key); let peer_id = output.remote_key.into_peer_id();
let peer_id2 = peer_id.clone(); let peer_id2 = peer_id.clone();
let upgrade = core::upgrade::SelectUpgrade::new(yamux::Config::default(), mplex::MplexConfig::new()) let upgrade = core::upgrade::SelectUpgrade::new(yamux::Config::default(), mplex::MplexConfig::new())
// TODO: use a single `.map` instead of two maps // TODO: use a single `.map` instead of two maps
.map_inbound(move |muxer| (peer_id, muxer)) .map_inbound(move |muxer| (peer_id, muxer))
.map_outbound(move |muxer| (peer_id2, muxer)); .map_outbound(move |muxer| (peer_id2, muxer));
core::upgrade::apply(output.stream, upgrade, endpoint)
core::upgrade::apply(out.stream, upgrade, endpoint)
.map(|(id, muxer)| (id, core::muxing::StreamMuxerBox::new(muxer))) .map(|(id, muxer)| (id, core::muxing::StreamMuxerBox::new(muxer)))
}) })
.with_timeout(Duration::from_secs(20)) .with_timeout(Duration::from_secs(20))