mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-01 12:11:20 +00:00
protocols/noise: Inline handshake
functions into upgrade traits (#2909)
- Remove `Deref` implementation on `AuthenticKeypair`. - Make `handshake` module private. - Deprecate `AuthenticKeypair::into_identity`.
This commit is contained in:
parent
31a45f2d76
commit
eba2efe29a
@ -5,9 +5,13 @@
|
||||
- Introduce `NoiseAuthenticated::xx` constructor, assuming a X25519 DH key exchange. An XX key exchange and X25519 keys
|
||||
are the most common way of using noise in libp2p and thus deserve a convenience constructor. See [PR 2887].
|
||||
- Add `NoiseConfig::with_prologue` which allows users to set the noise prologue of the handshake. See [PR 2903].
|
||||
- Remove `Deref` implementation on `AuthenticKeypair`. See [PR 2909].
|
||||
- Make `handshake` module private. See [PR 2909].
|
||||
- Deprecate `AuthenticKeypair::into_identity`. See [PR 2909].
|
||||
|
||||
[PR 2887]: https://github.com/libp2p/rust-libp2p/pull/2887
|
||||
[PR 2903]: https://github.com/libp2p/rust-libp2p/pull/2903
|
||||
[PR 2909]: https://github.com/libp2p/rust-libp2p/pull/2909
|
||||
|
||||
# 0.39.0
|
||||
|
||||
|
@ -31,10 +31,9 @@ use crate::protocol::{KeypairIdentity, Protocol, PublicKey};
|
||||
use crate::LegacyConfig;
|
||||
use bytes::Bytes;
|
||||
use futures::prelude::*;
|
||||
use futures::task;
|
||||
use libp2p_core::identity;
|
||||
use prost::Message;
|
||||
use std::{io, pin::Pin, task::Context};
|
||||
use std::io;
|
||||
|
||||
/// The identity of the remote established during a handshake.
|
||||
pub enum RemoteIdentity<C> {
|
||||
@ -63,199 +62,11 @@ pub enum RemoteIdentity<C> {
|
||||
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 },
|
||||
}
|
||||
|
||||
/// A future performing a Noise handshake pattern.
|
||||
pub struct Handshake<T, C>(
|
||||
Pin<Box<dyn Future<Output = Result<(RemoteIdentity<C>, NoiseOutput<T>), NoiseError>> + Send>>,
|
||||
);
|
||||
|
||||
impl<T, C> Future for Handshake<T, C> {
|
||||
type Output = Result<(RemoteIdentity<C>, NoiseOutput<T>), NoiseError>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> task::Poll<Self::Output> {
|
||||
Pin::new(&mut self.0).poll(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<T, C>(
|
||||
io: T,
|
||||
session: Result<snow::HandshakeState, NoiseError>,
|
||||
identity: KeypairIdentity,
|
||||
identity_x: IdentityExchange,
|
||||
legacy: LegacyConfig,
|
||||
) -> Handshake<T, C>
|
||||
where
|
||||
T: AsyncWrite + AsyncRead + Send + Unpin + 'static,
|
||||
C: Protocol<C> + AsRef<[u8]>,
|
||||
{
|
||||
Handshake(Box::pin(async move {
|
||||
let mut state = State::new(io, session, identity, identity_x, legacy)?;
|
||||
send_identity(&mut state).await?;
|
||||
recv_identity(&mut state).await?;
|
||||
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<T, C>(
|
||||
io: T,
|
||||
session: Result<snow::HandshakeState, NoiseError>,
|
||||
identity: KeypairIdentity,
|
||||
identity_x: IdentityExchange,
|
||||
legacy: LegacyConfig,
|
||||
) -> Handshake<T, C>
|
||||
where
|
||||
T: AsyncWrite + AsyncRead + Send + Unpin + 'static,
|
||||
C: Protocol<C> + AsRef<[u8]>,
|
||||
{
|
||||
Handshake(Box::pin(async move {
|
||||
let mut state = State::new(io, session, identity, identity_x, legacy)?;
|
||||
recv_identity(&mut state).await?;
|
||||
send_identity(&mut state).await?;
|
||||
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<T, C>(
|
||||
io: T,
|
||||
session: Result<snow::HandshakeState, NoiseError>,
|
||||
identity: KeypairIdentity,
|
||||
identity_x: IdentityExchange,
|
||||
legacy: LegacyConfig,
|
||||
) -> Handshake<T, C>
|
||||
where
|
||||
T: AsyncWrite + AsyncRead + Unpin + Send + 'static,
|
||||
C: Protocol<C> + AsRef<[u8]>,
|
||||
{
|
||||
Handshake(Box::pin(async move {
|
||||
let mut state = State::new(io, session, identity, identity_x, legacy)?;
|
||||
send_empty(&mut state).await?;
|
||||
recv_identity(&mut state).await?;
|
||||
send_identity(&mut state).await?;
|
||||
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<T, C>(
|
||||
io: T,
|
||||
session: Result<snow::HandshakeState, NoiseError>,
|
||||
identity: KeypairIdentity,
|
||||
identity_x: IdentityExchange,
|
||||
legacy: LegacyConfig,
|
||||
) -> Handshake<T, C>
|
||||
where
|
||||
T: AsyncWrite + AsyncRead + Unpin + Send + 'static,
|
||||
C: Protocol<C> + AsRef<[u8]>,
|
||||
{
|
||||
Handshake(Box::pin(async move {
|
||||
let mut state = State::new(io, session, identity, identity_x, legacy)?;
|
||||
recv_empty(&mut state).await?;
|
||||
send_identity(&mut state).await?;
|
||||
recv_identity(&mut state).await?;
|
||||
state.finish()
|
||||
}))
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Internal
|
||||
|
||||
/// Handshake state.
|
||||
struct State<T> {
|
||||
pub struct State<T> {
|
||||
/// The underlying I/O resource.
|
||||
io: NoiseFramed<T, snow::HandshakeState>,
|
||||
/// The associated public identity of the local node's static DH keypair,
|
||||
@ -265,8 +76,6 @@ struct State<T> {
|
||||
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,
|
||||
/// Legacy configuration parameters.
|
||||
legacy: LegacyConfig,
|
||||
}
|
||||
@ -277,34 +86,27 @@ impl<T> State<T> {
|
||||
/// 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(
|
||||
pub fn new(
|
||||
io: T,
|
||||
session: Result<snow::HandshakeState, NoiseError>,
|
||||
session: snow::HandshakeState,
|
||||
identity: KeypairIdentity,
|
||||
identity_x: IdentityExchange,
|
||||
expected_remote_key: Option<identity::PublicKey>,
|
||||
legacy: LegacyConfig,
|
||||
) -> Result<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),
|
||||
};
|
||||
session.map(|s| State {
|
||||
) -> Self {
|
||||
Self {
|
||||
identity,
|
||||
io: NoiseFramed::new(io, s),
|
||||
io: NoiseFramed::new(io, session),
|
||||
dh_remote_pubkey_sig: None,
|
||||
id_remote_pubkey,
|
||||
send_identity,
|
||||
id_remote_pubkey: expected_remote_key,
|
||||
legacy,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) -> Result<(RemoteIdentity<C>, NoiseOutput<T>), NoiseError>
|
||||
pub fn finish<C>(self) -> Result<(RemoteIdentity<C>, NoiseOutput<T>), NoiseError>
|
||||
where
|
||||
C: Protocol<C> + AsRef<[u8]>,
|
||||
{
|
||||
@ -340,7 +142,7 @@ where
|
||||
}
|
||||
|
||||
/// A future for receiving a Noise handshake message with an empty payload.
|
||||
async fn recv_empty<T>(state: &mut State<T>) -> Result<(), NoiseError>
|
||||
pub async fn recv_empty<T>(state: &mut State<T>) -> Result<(), NoiseError>
|
||||
where
|
||||
T: AsyncRead + Unpin,
|
||||
{
|
||||
@ -354,7 +156,7 @@ where
|
||||
}
|
||||
|
||||
/// A future for sending a Noise handshake message with an empty payload.
|
||||
async fn send_empty<T>(state: &mut State<T>) -> Result<(), NoiseError>
|
||||
pub async fn send_empty<T>(state: &mut State<T>) -> Result<(), NoiseError>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
{
|
||||
@ -364,7 +166,10 @@ where
|
||||
|
||||
/// A future for receiving a Noise handshake message with a payload
|
||||
/// identifying the remote.
|
||||
async fn recv_identity<T>(state: &mut State<T>) -> Result<(), NoiseError>
|
||||
///
|
||||
/// In case `expected_key` is passed, this function will fail if the received key does not match the expected key.
|
||||
/// In case the remote does not send us a key, the expected key is assumed to be the remote's key.
|
||||
pub async fn recv_identity<T>(state: &mut State<T>) -> Result<(), NoiseError>
|
||||
where
|
||||
T: AsyncRead + Unpin,
|
||||
{
|
||||
@ -421,15 +226,40 @@ where
|
||||
}
|
||||
|
||||
/// Send a Noise handshake message with a payload identifying the local node to the remote.
|
||||
async fn send_identity<T>(state: &mut State<T>) -> Result<(), NoiseError>
|
||||
pub async fn send_identity<T>(state: &mut State<T>) -> Result<(), NoiseError>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
{
|
||||
let mut pb = payload_proto::NoiseHandshakePayload::default();
|
||||
|
||||
if state.send_identity {
|
||||
pb.identity_key = state.identity.public.to_protobuf_encoding()
|
||||
}
|
||||
let mut pb = payload_proto::NoiseHandshakePayload {
|
||||
identity_key: state.identity.public.to_protobuf_encoding(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Some(ref sig) = state.identity.signature {
|
||||
pb.identity_sig = sig.clone()
|
||||
}
|
||||
|
||||
let mut msg = if state.legacy.send_legacy_handshake {
|
||||
let mut msg = Vec::with_capacity(2 + pb.encoded_len());
|
||||
msg.extend_from_slice(&(pb.encoded_len() as u16).to_be_bytes());
|
||||
msg
|
||||
} else {
|
||||
Vec::with_capacity(pb.encoded_len())
|
||||
};
|
||||
|
||||
pb.encode(&mut msg)
|
||||
.expect("Vec<u8> provides capacity as needed");
|
||||
state.io.send(&msg).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send a Noise handshake message with a payload identifying the local node to the remote.
|
||||
pub async fn send_signature_only<T>(state: &mut State<T>) -> Result<(), NoiseError>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
{
|
||||
let mut pb = payload_proto::NoiseHandshakePayload::default();
|
||||
|
||||
if let Some(ref sig) = state.identity.signature {
|
||||
pb.identity_sig = sig.clone()
|
||||
|
@ -58,16 +58,17 @@ mod io;
|
||||
mod protocol;
|
||||
|
||||
pub use error::NoiseError;
|
||||
pub use io::handshake;
|
||||
pub use io::handshake::{Handshake, IdentityExchange, RemoteIdentity};
|
||||
pub use io::handshake::RemoteIdentity;
|
||||
pub use io::NoiseOutput;
|
||||
pub use protocol::{x25519::X25519, x25519_spec::X25519Spec};
|
||||
pub use protocol::{AuthenticKeypair, Keypair, KeypairIdentity, PublicKey, SecretKey};
|
||||
pub use protocol::{Protocol, ProtocolParams, IK, IX, XX};
|
||||
|
||||
use crate::handshake::State;
|
||||
use crate::io::handshake;
|
||||
use futures::future::BoxFuture;
|
||||
use futures::prelude::*;
|
||||
use libp2p_core::{identity, InboundUpgrade, OutboundUpgrade, PeerId, UpgradeInfo};
|
||||
use snow::HandshakeState;
|
||||
use std::pin::Pin;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
@ -108,30 +109,31 @@ impl<H, C: Zeroize, R> NoiseConfig<H, C, R> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, C, R> NoiseConfig<H, C, R>
|
||||
/// Implement `into_responder` and `into_initiator` for all configs where `R = ()`.
|
||||
///
|
||||
/// This allows us to ignore the `remote` field.
|
||||
impl<H, C> NoiseConfig<H, C, ()>
|
||||
where
|
||||
C: Zeroize + AsRef<[u8]>,
|
||||
C: Zeroize + Protocol<C> + AsRef<[u8]>,
|
||||
{
|
||||
fn into_responder(self) -> Result<HandshakeState, NoiseError> {
|
||||
let state = self
|
||||
fn into_responder<S>(self, socket: S) -> Result<State<S>, NoiseError> {
|
||||
let session = self
|
||||
.params
|
||||
.into_builder()
|
||||
.prologue(self.prologue.as_ref())
|
||||
.local_private_key(self.dh_keys.secret().as_ref())
|
||||
.build_responder()
|
||||
.map_err(NoiseError::from)?;
|
||||
.into_builder(&self.prologue, self.dh_keys.keypair.secret(), None)
|
||||
.build_responder()?;
|
||||
|
||||
let state = State::new(socket, session, self.dh_keys.identity, None, self.legacy);
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
fn into_initiator(self) -> Result<HandshakeState, NoiseError> {
|
||||
let state = self
|
||||
fn into_initiator<S>(self, socket: S) -> Result<State<S>, NoiseError> {
|
||||
let session = self
|
||||
.params
|
||||
.into_builder()
|
||||
.prologue(self.prologue.as_ref())
|
||||
.local_private_key(self.dh_keys.secret().as_ref())
|
||||
.build_initiator()
|
||||
.map_err(NoiseError::from)?;
|
||||
.into_builder(&self.prologue, self.dh_keys.keypair.secret(), None)
|
||||
.build_initiator()?;
|
||||
|
||||
let state = State::new(socket, session, self.dh_keys.identity, None, self.legacy);
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
@ -193,7 +195,7 @@ where
|
||||
|
||||
impl<C> NoiseConfig<IK, C, (PublicKey<C>, identity::PublicKey)>
|
||||
where
|
||||
C: Protocol<C> + Zeroize,
|
||||
C: Protocol<C> + Zeroize + AsRef<[u8]>,
|
||||
{
|
||||
/// Create a new `NoiseConfig` for the `IK` handshake pattern (initiator side).
|
||||
///
|
||||
@ -213,10 +215,38 @@ where
|
||||
prologue: Vec::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Specialised implementation of `into_initiator` for the `IK` handshake where `R != ()`.
|
||||
fn into_initiator<S>(self, socket: S) -> Result<State<S>, NoiseError> {
|
||||
let session = self
|
||||
.params
|
||||
.into_builder(
|
||||
&self.prologue,
|
||||
self.dh_keys.keypair.secret(),
|
||||
Some(&self.remote.0),
|
||||
)
|
||||
.build_initiator()?;
|
||||
|
||||
let state = State::new(
|
||||
socket,
|
||||
session,
|
||||
self.dh_keys.identity,
|
||||
Some(self.remote.1),
|
||||
self.legacy,
|
||||
);
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
}
|
||||
|
||||
// Handshake pattern IX /////////////////////////////////////////////////////
|
||||
|
||||
/// Implements the responder part of the `IX` noise handshake pattern.
|
||||
///
|
||||
/// `IX` is a single round-trip (2 messages) handshake in which each party sends their identity over to the other party.
|
||||
///
|
||||
/// ```raw
|
||||
/// initiator -{id}-> responder
|
||||
/// initiator <-{id}- responder
|
||||
/// ```
|
||||
impl<T, C> InboundUpgrade<T> for NoiseConfig<IX, C>
|
||||
where
|
||||
NoiseConfig<IX, C>: UpgradeInfo,
|
||||
@ -225,22 +255,29 @@ where
|
||||
{
|
||||
type Output = (RemoteIdentity<C>, NoiseOutput<T>);
|
||||
type Error = NoiseError;
|
||||
type Future = Handshake<T, C>;
|
||||
type Future = BoxFuture<'static, Result<(RemoteIdentity<C>, NoiseOutput<T>), NoiseError>>;
|
||||
|
||||
fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future {
|
||||
let config = self.legacy;
|
||||
let identity = self.dh_keys.clone().into_identity();
|
||||
async move {
|
||||
let mut state = self.into_responder(socket)?;
|
||||
|
||||
handshake::rt1_responder(
|
||||
socket,
|
||||
self.into_responder(),
|
||||
identity,
|
||||
IdentityExchange::Mutual,
|
||||
config,
|
||||
)
|
||||
handshake::recv_identity(&mut state).await?;
|
||||
handshake::send_identity(&mut state).await?;
|
||||
|
||||
state.finish()
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
/// Implements the initiator part of the `IX` noise handshake pattern.
|
||||
///
|
||||
/// `IX` is a single round-trip (2 messages) handshake in which each party sends their identity over to the other party.
|
||||
///
|
||||
/// ```raw
|
||||
/// initiator -{id}-> responder
|
||||
/// initiator <-{id}- responder
|
||||
/// ```
|
||||
impl<T, C> OutboundUpgrade<T> for NoiseConfig<IX, C>
|
||||
where
|
||||
NoiseConfig<IX, C>: UpgradeInfo,
|
||||
@ -249,24 +286,33 @@ where
|
||||
{
|
||||
type Output = (RemoteIdentity<C>, NoiseOutput<T>);
|
||||
type Error = NoiseError;
|
||||
type Future = Handshake<T, C>;
|
||||
type Future = BoxFuture<'static, Result<(RemoteIdentity<C>, NoiseOutput<T>), NoiseError>>;
|
||||
|
||||
fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future {
|
||||
let legacy = self.legacy;
|
||||
let identity = self.dh_keys.clone().into_identity();
|
||||
async move {
|
||||
let mut state = self.into_initiator(socket)?;
|
||||
|
||||
handshake::rt1_initiator(
|
||||
socket,
|
||||
self.into_initiator(),
|
||||
identity,
|
||||
IdentityExchange::Mutual,
|
||||
legacy,
|
||||
)
|
||||
handshake::send_identity(&mut state).await?;
|
||||
handshake::recv_identity(&mut state).await?;
|
||||
|
||||
state.finish()
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
// Handshake pattern XX /////////////////////////////////////////////////////
|
||||
|
||||
/// Implements the responder part of the `XX` noise handshake pattern.
|
||||
///
|
||||
/// `XX` is a 1.5 round-trip (3 messages) handshake.
|
||||
/// The first message in a noise handshake is unencrypted. In the `XX` handshake pattern, that message
|
||||
/// is empty and thus does not leak any information. The identities are then exchanged in the second
|
||||
/// and third message.
|
||||
///
|
||||
/// ```raw
|
||||
/// initiator --{}--> responder
|
||||
/// initiator <-{id}- responder
|
||||
/// initiator -{id}-> responder
|
||||
/// ```
|
||||
impl<T, C> InboundUpgrade<T> for NoiseConfig<XX, C>
|
||||
where
|
||||
NoiseConfig<XX, C>: UpgradeInfo,
|
||||
@ -275,22 +321,34 @@ where
|
||||
{
|
||||
type Output = (RemoteIdentity<C>, NoiseOutput<T>);
|
||||
type Error = NoiseError;
|
||||
type Future = Handshake<T, C>;
|
||||
type Future = BoxFuture<'static, Result<(RemoteIdentity<C>, NoiseOutput<T>), NoiseError>>;
|
||||
|
||||
fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future {
|
||||
let legacy = self.legacy;
|
||||
let identity = self.dh_keys.clone().into_identity();
|
||||
async move {
|
||||
let mut state = self.into_responder(socket)?;
|
||||
|
||||
handshake::rt15_responder(
|
||||
socket,
|
||||
self.into_responder(),
|
||||
identity,
|
||||
IdentityExchange::Mutual,
|
||||
legacy,
|
||||
)
|
||||
handshake::recv_empty(&mut state).await?;
|
||||
handshake::send_identity(&mut state).await?;
|
||||
handshake::recv_identity(&mut state).await?;
|
||||
|
||||
state.finish()
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
/// Implements the initiator part of the `XX` noise handshake pattern.
|
||||
///
|
||||
/// `XX` is a 1.5 round-trip (3 messages) handshake.
|
||||
/// The first message in a noise handshake is unencrypted. In the `XX` handshake pattern, that message
|
||||
/// is empty and thus does not leak any information. The identities are then exchanged in the second
|
||||
/// and third message.
|
||||
///
|
||||
/// ```raw
|
||||
/// initiator --{}--> responder
|
||||
/// initiator <-{id}- responder
|
||||
/// initiator -{id}-> responder
|
||||
/// ```
|
||||
impl<T, C> OutboundUpgrade<T> for NoiseConfig<XX, C>
|
||||
where
|
||||
NoiseConfig<XX, C>: UpgradeInfo,
|
||||
@ -299,48 +357,67 @@ where
|
||||
{
|
||||
type Output = (RemoteIdentity<C>, NoiseOutput<T>);
|
||||
type Error = NoiseError;
|
||||
type Future = Handshake<T, C>;
|
||||
type Future = BoxFuture<'static, Result<(RemoteIdentity<C>, NoiseOutput<T>), NoiseError>>;
|
||||
|
||||
fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future {
|
||||
let legacy = self.legacy;
|
||||
let identity = self.dh_keys.clone().into_identity();
|
||||
async move {
|
||||
let mut state = self.into_initiator(socket)?;
|
||||
|
||||
handshake::rt15_initiator(
|
||||
socket,
|
||||
self.into_initiator(),
|
||||
identity,
|
||||
IdentityExchange::Mutual,
|
||||
legacy,
|
||||
)
|
||||
handshake::send_empty(&mut state).await?;
|
||||
handshake::recv_identity(&mut state).await?;
|
||||
handshake::send_identity(&mut state).await?;
|
||||
|
||||
state.finish()
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
// Handshake pattern IK /////////////////////////////////////////////////////
|
||||
|
||||
impl<T, C, R> InboundUpgrade<T> for NoiseConfig<IK, C, R>
|
||||
/// Implements the responder part of the `IK` handshake pattern.
|
||||
///
|
||||
/// `IK` is a single round-trip (2 messages) handshake.
|
||||
///
|
||||
/// In the `IK` handshake, the initiator is expected to know the responder's identity already, which
|
||||
/// is why the responder does not send it in the second message.
|
||||
///
|
||||
/// ```raw
|
||||
/// initiator -{id}-> responder
|
||||
/// initiator <-{id}- responder
|
||||
/// ```
|
||||
impl<T, C> InboundUpgrade<T> for NoiseConfig<IK, C>
|
||||
where
|
||||
NoiseConfig<IK, C, R>: UpgradeInfo,
|
||||
NoiseConfig<IK, C>: UpgradeInfo,
|
||||
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||
C: Protocol<C> + AsRef<[u8]> + Zeroize + Clone + Send + 'static,
|
||||
{
|
||||
type Output = (RemoteIdentity<C>, NoiseOutput<T>);
|
||||
type Error = NoiseError;
|
||||
type Future = Handshake<T, C>;
|
||||
type Future = BoxFuture<'static, Result<(RemoteIdentity<C>, NoiseOutput<T>), NoiseError>>;
|
||||
|
||||
fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future {
|
||||
let legacy = self.legacy;
|
||||
let identity = self.dh_keys.clone().into_identity();
|
||||
async move {
|
||||
let mut state = self.into_responder(socket)?;
|
||||
|
||||
handshake::rt1_responder(
|
||||
socket,
|
||||
self.into_responder(),
|
||||
identity,
|
||||
IdentityExchange::Receive,
|
||||
legacy,
|
||||
)
|
||||
handshake::recv_identity(&mut state).await?;
|
||||
handshake::send_signature_only(&mut state).await?;
|
||||
|
||||
state.finish()
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
/// Implements the initiator part of the `IK` handshake pattern.
|
||||
///
|
||||
/// `IK` is a single round-trip (2 messages) handshake.
|
||||
///
|
||||
/// In the `IK` handshake, the initiator knows and pre-configures the remote's identity in the
|
||||
/// [`HandshakeState`](snow::HandshakeState).
|
||||
///
|
||||
/// ```raw
|
||||
/// initiator -{id}-> responder
|
||||
/// initiator <-{id}- responder
|
||||
/// ```
|
||||
impl<T, C> OutboundUpgrade<T> for NoiseConfig<IK, C, (PublicKey<C>, identity::PublicKey)>
|
||||
where
|
||||
NoiseConfig<IK, C, (PublicKey<C>, identity::PublicKey)>: UpgradeInfo,
|
||||
@ -349,27 +426,18 @@ where
|
||||
{
|
||||
type Output = (RemoteIdentity<C>, NoiseOutput<T>);
|
||||
type Error = NoiseError;
|
||||
type Future = Handshake<T, C>;
|
||||
type Future = BoxFuture<'static, Result<(RemoteIdentity<C>, NoiseOutput<T>), NoiseError>>;
|
||||
|
||||
fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future {
|
||||
let session = self
|
||||
.params
|
||||
.into_builder()
|
||||
.prologue(self.prologue.as_ref())
|
||||
.local_private_key(self.dh_keys.secret().as_ref())
|
||||
.remote_public_key(self.remote.0.as_ref())
|
||||
.build_initiator()
|
||||
.map_err(NoiseError::from);
|
||||
async move {
|
||||
let mut state = self.into_initiator(socket)?;
|
||||
|
||||
handshake::rt1_initiator(
|
||||
socket,
|
||||
session,
|
||||
self.dh_keys.into_identity(),
|
||||
IdentityExchange::Send {
|
||||
remote: self.remote.1,
|
||||
},
|
||||
self.legacy,
|
||||
)
|
||||
handshake::send_identity(&mut state).await?;
|
||||
handshake::recv_identity(&mut state).await?;
|
||||
|
||||
state.finish()
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
@ -479,51 +547,3 @@ pub struct LegacyConfig {
|
||||
/// libp2p implementations.
|
||||
pub recv_legacy_handshake: bool,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn handshake_hashes_disagree_if_prologue_differs() {
|
||||
let alice = new_xx_config()
|
||||
.with_prologue(b"alice prologue".to_vec())
|
||||
.into_initiator()
|
||||
.unwrap();
|
||||
let bob = new_xx_config()
|
||||
.with_prologue(b"bob prologue".to_vec())
|
||||
.into_responder()
|
||||
.unwrap();
|
||||
|
||||
let alice_handshake_hash = alice.get_handshake_hash();
|
||||
let bob_handshake_hash = bob.get_handshake_hash();
|
||||
|
||||
assert_ne!(alice_handshake_hash, bob_handshake_hash)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn handshake_hashes_agree_if_prologue_is_the_same() {
|
||||
let alice = new_xx_config()
|
||||
.with_prologue(b"shared knowledge".to_vec())
|
||||
.into_initiator()
|
||||
.unwrap();
|
||||
let bob = new_xx_config()
|
||||
.with_prologue(b"shared knowledge".to_vec())
|
||||
.into_responder()
|
||||
.unwrap();
|
||||
|
||||
let alice_handshake_hash = alice.get_handshake_hash();
|
||||
let bob_handshake_hash = bob.get_handshake_hash();
|
||||
|
||||
assert_eq!(alice_handshake_hash, bob_handshake_hash)
|
||||
}
|
||||
|
||||
fn new_xx_config() -> NoiseConfig<XX, X25519> {
|
||||
let dh_keys = Keypair::<X25519>::new();
|
||||
let noise_keys = dh_keys
|
||||
.into_authentic(&identity::Keypair::generate_ed25519())
|
||||
.unwrap();
|
||||
|
||||
NoiseConfig::xx(noise_keys)
|
||||
}
|
||||
}
|
||||
|
@ -34,9 +34,24 @@ use zeroize::Zeroize;
|
||||
pub struct ProtocolParams(snow::params::NoiseParams);
|
||||
|
||||
impl ProtocolParams {
|
||||
/// Turn the protocol parameters into a session builder.
|
||||
pub(crate) fn into_builder(self) -> snow::Builder<'static> {
|
||||
snow::Builder::with_resolver(self.0, Box::new(Resolver))
|
||||
pub(crate) fn into_builder<'b, C>(
|
||||
self,
|
||||
prologue: &'b [u8],
|
||||
private_key: &'b SecretKey<C>,
|
||||
remote_public_key: Option<&'b PublicKey<C>>,
|
||||
) -> snow::Builder<'b>
|
||||
where
|
||||
C: Zeroize + AsRef<[u8]> + Protocol<C>,
|
||||
{
|
||||
let mut builder = snow::Builder::with_resolver(self.0, Box::new(Resolver))
|
||||
.prologue(prologue.as_ref())
|
||||
.local_private_key(private_key.as_ref());
|
||||
|
||||
if let Some(remote_public_key) = remote_public_key {
|
||||
builder = builder.remote_public_key(remote_public_key.as_ref());
|
||||
}
|
||||
|
||||
builder
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,26 +133,27 @@ pub struct Keypair<T: Zeroize> {
|
||||
/// A DH keypair that is authentic w.r.t. a [`identity::PublicKey`].
|
||||
#[derive(Clone)]
|
||||
pub struct AuthenticKeypair<T: Zeroize> {
|
||||
keypair: Keypair<T>,
|
||||
identity: KeypairIdentity,
|
||||
pub(crate) keypair: Keypair<T>,
|
||||
pub(crate) identity: KeypairIdentity,
|
||||
}
|
||||
|
||||
impl<T: Zeroize> AuthenticKeypair<T> {
|
||||
/// Returns the public DH key of this keypair.
|
||||
pub fn public_dh_key(&self) -> &PublicKey<T> {
|
||||
&self.keypair.public
|
||||
}
|
||||
|
||||
/// Extract the public [`KeypairIdentity`] from this `AuthenticKeypair`,
|
||||
/// dropping the DH `Keypair`.
|
||||
#[deprecated(
|
||||
since = "0.40.0",
|
||||
note = "This function was only used internally and will be removed in the future unless more usecases come up."
|
||||
)]
|
||||
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 {
|
||||
@ -288,3 +304,40 @@ impl rand::RngCore for Rng {
|
||||
impl rand::CryptoRng for Rng {}
|
||||
|
||||
impl snow::types::Random for Rng {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::X25519;
|
||||
|
||||
#[test]
|
||||
fn handshake_hashes_disagree_if_prologue_differs() {
|
||||
let alice = xx_builder(b"alice prologue").build_initiator().unwrap();
|
||||
let bob = xx_builder(b"bob prologue").build_responder().unwrap();
|
||||
|
||||
let alice_handshake_hash = alice.get_handshake_hash();
|
||||
let bob_handshake_hash = bob.get_handshake_hash();
|
||||
|
||||
assert_ne!(alice_handshake_hash, bob_handshake_hash)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn handshake_hashes_agree_if_prologue_is_the_same() {
|
||||
let alice = xx_builder(b"shared knowledge").build_initiator().unwrap();
|
||||
let bob = xx_builder(b"shared knowledge").build_responder().unwrap();
|
||||
|
||||
let alice_handshake_hash = alice.get_handshake_hash();
|
||||
let bob_handshake_hash = bob.get_handshake_hash();
|
||||
|
||||
assert_eq!(alice_handshake_hash, bob_handshake_hash)
|
||||
}
|
||||
|
||||
fn xx_builder(prologue: &'static [u8]) -> snow::Builder<'static> {
|
||||
X25519::params_xx().into_builder(prologue, TEST_KEY.secret(), None)
|
||||
}
|
||||
|
||||
// Hack to work around borrow-checker.
|
||||
lazy_static::lazy_static! {
|
||||
static ref TEST_KEY: Keypair<X25519> = Keypair::<X25519>::new();
|
||||
}
|
||||
}
|
||||
|
@ -197,7 +197,7 @@ fn ik_xx() {
|
||||
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_dh_public = server_dh.public_dh_key().clone();
|
||||
let server_transport = TcpTransport::default()
|
||||
.and_then(move |output, endpoint| {
|
||||
if endpoint.is_listener() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user