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:
Thomas Eizinger 2022-10-12 04:48:16 +11:00 committed by GitHub
parent 31a45f2d76
commit eba2efe29a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 283 additions and 376 deletions

View File

@ -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

View File

@ -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()

View File

@ -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)
}
}

View File

@ -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();
}
}

View File

@ -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() {