diff --git a/CHANGELOG.md b/CHANGELOG.md index 2aa74dae1df..b4da5be9da4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,8 @@ # 0.49.0 - [unreleased] - Update to [`libp2p-tcp` `v0.37.0`](transports/tcp/CHANGELOG.md#0370). +- +- Update to [`libp2p-noise` `v0.39.1`](transports/noise/CHANGELOG.md#0391). - Update to [`libp2p-swarm-derive` `v0.30.1`](swarm-derive/CHANGELOG.md#0301). diff --git a/transports/noise/CHANGELOG.md b/transports/noise/CHANGELOG.md index 1416aab4e30..8dcb8926c32 100644 --- a/transports/noise/CHANGELOG.md +++ b/transports/noise/CHANGELOG.md @@ -2,8 +2,10 @@ - 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]. [PR 2887]: https://github.com/libp2p/rust-libp2p/pull/2887 +[PR 2903]: https://github.com/libp2p/rust-libp2p/pull/2903 # 0.39.0 diff --git a/transports/noise/src/lib.rs b/transports/noise/src/lib.rs index 84c0972d0a9..d793e1983bb 100644 --- a/transports/noise/src/lib.rs +++ b/transports/noise/src/lib.rs @@ -67,6 +67,7 @@ pub use protocol::{Protocol, ProtocolParams, IK, IX, XX}; use futures::prelude::*; use libp2p_core::{identity, InboundUpgrade, OutboundUpgrade, PeerId, UpgradeInfo}; +use snow::HandshakeState; use std::pin::Pin; use zeroize::Zeroize; @@ -78,6 +79,14 @@ pub struct NoiseConfig { legacy: LegacyConfig, remote: R, _marker: std::marker::PhantomData

, + + /// Prologue to use in the noise handshake. + /// + /// The prologue can contain arbitrary data that will be hashed into the noise handshake. + /// For the handshake to succeed, both parties must set the same prologue. + /// + /// For further information, see . + prologue: Vec, } impl NoiseConfig { @@ -87,6 +96,11 @@ impl NoiseConfig { NoiseAuthenticated { config: self } } + /// Set the noise prologue. + pub fn with_prologue(self, prologue: Vec) -> Self { + Self { prologue, ..self } + } + /// Sets the legacy configuration options to use, if any. pub fn set_legacy_config(&mut self, cfg: LegacyConfig) -> &mut Self { self.legacy = cfg; @@ -94,6 +108,35 @@ impl NoiseConfig { } } +impl NoiseConfig +where + C: Zeroize + AsRef<[u8]>, +{ + fn into_responder(self) -> Result { + let state = self + .params + .into_builder() + .prologue(self.prologue.as_ref()) + .local_private_key(self.dh_keys.secret().as_ref()) + .build_responder() + .map_err(NoiseError::from)?; + + Ok(state) + } + + fn into_initiator(self) -> Result { + let state = self + .params + .into_builder() + .prologue(self.prologue.as_ref()) + .local_private_key(self.dh_keys.secret().as_ref()) + .build_initiator() + .map_err(NoiseError::from)?; + + Ok(state) + } +} + impl NoiseConfig where C: Protocol + Zeroize, @@ -106,6 +149,7 @@ where legacy: LegacyConfig::default(), remote: (), _marker: std::marker::PhantomData, + prologue: Vec::default(), } } } @@ -122,6 +166,7 @@ where legacy: LegacyConfig::default(), remote: (), _marker: std::marker::PhantomData, + prologue: Vec::default(), } } } @@ -141,6 +186,7 @@ where legacy: LegacyConfig::default(), remote: (), _marker: std::marker::PhantomData, + prologue: Vec::default(), } } } @@ -164,6 +210,7 @@ where legacy: LegacyConfig::default(), remote: (remote_dh, remote_id), _marker: std::marker::PhantomData, + prologue: Vec::default(), } } } @@ -174,25 +221,22 @@ impl InboundUpgrade for NoiseConfig where NoiseConfig: UpgradeInfo, T: AsyncRead + AsyncWrite + Unpin + Send + 'static, - C: Protocol + AsRef<[u8]> + Zeroize + Send + 'static, + C: Protocol + AsRef<[u8]> + Zeroize + Clone + Send + 'static, { type Output = (RemoteIdentity, NoiseOutput); type Error = NoiseError; type Future = Handshake; fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future { - let session = self - .params - .into_builder() - .local_private_key(self.dh_keys.secret().as_ref()) - .build_responder() - .map_err(NoiseError::from); + let config = self.legacy; + let identity = self.dh_keys.clone().into_identity(); + handshake::rt1_responder( socket, - session, - self.dh_keys.into_identity(), + self.into_responder(), + identity, IdentityExchange::Mutual, - self.legacy, + config, ) } } @@ -201,25 +245,22 @@ impl OutboundUpgrade for NoiseConfig where NoiseConfig: UpgradeInfo, T: AsyncRead + AsyncWrite + Unpin + Send + 'static, - C: Protocol + AsRef<[u8]> + Zeroize + Send + 'static, + C: Protocol + AsRef<[u8]> + Zeroize + Clone + Send + 'static, { type Output = (RemoteIdentity, NoiseOutput); type Error = NoiseError; type Future = Handshake; fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future { - let session = self - .params - .into_builder() - .local_private_key(self.dh_keys.secret().as_ref()) - .build_initiator() - .map_err(NoiseError::from); + let legacy = self.legacy; + let identity = self.dh_keys.clone().into_identity(); + handshake::rt1_initiator( socket, - session, - self.dh_keys.into_identity(), + self.into_initiator(), + identity, IdentityExchange::Mutual, - self.legacy, + legacy, ) } } @@ -230,25 +271,22 @@ impl InboundUpgrade for NoiseConfig where NoiseConfig: UpgradeInfo, T: AsyncRead + AsyncWrite + Unpin + Send + 'static, - C: Protocol + AsRef<[u8]> + Zeroize + Send + 'static, + C: Protocol + AsRef<[u8]> + Zeroize + Clone + Send + 'static, { type Output = (RemoteIdentity, NoiseOutput); type Error = NoiseError; type Future = Handshake; fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future { - let session = self - .params - .into_builder() - .local_private_key(self.dh_keys.secret().as_ref()) - .build_responder() - .map_err(NoiseError::from); + let legacy = self.legacy; + let identity = self.dh_keys.clone().into_identity(); + handshake::rt15_responder( socket, - session, - self.dh_keys.into_identity(), + self.into_responder(), + identity, IdentityExchange::Mutual, - self.legacy, + legacy, ) } } @@ -257,25 +295,22 @@ impl OutboundUpgrade for NoiseConfig where NoiseConfig: UpgradeInfo, T: AsyncRead + AsyncWrite + Unpin + Send + 'static, - C: Protocol + AsRef<[u8]> + Zeroize + Send + 'static, + C: Protocol + AsRef<[u8]> + Zeroize + Clone + Send + 'static, { type Output = (RemoteIdentity, NoiseOutput); type Error = NoiseError; type Future = Handshake; fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future { - let session = self - .params - .into_builder() - .local_private_key(self.dh_keys.secret().as_ref()) - .build_initiator() - .map_err(NoiseError::from); + let legacy = self.legacy; + let identity = self.dh_keys.clone().into_identity(); + handshake::rt15_initiator( socket, - session, - self.dh_keys.into_identity(), + self.into_initiator(), + identity, IdentityExchange::Mutual, - self.legacy, + legacy, ) } } @@ -286,25 +321,22 @@ impl InboundUpgrade for NoiseConfig where NoiseConfig: UpgradeInfo, T: AsyncRead + AsyncWrite + Unpin + Send + 'static, - C: Protocol + AsRef<[u8]> + Zeroize + Send + 'static, + C: Protocol + AsRef<[u8]> + Zeroize + Clone + Send + 'static, { type Output = (RemoteIdentity, NoiseOutput); type Error = NoiseError; type Future = Handshake; fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future { - let session = self - .params - .into_builder() - .local_private_key(self.dh_keys.secret().as_ref()) - .build_responder() - .map_err(NoiseError::from); + let legacy = self.legacy; + let identity = self.dh_keys.clone().into_identity(); + handshake::rt1_responder( socket, - session, - self.dh_keys.into_identity(), + self.into_responder(), + identity, IdentityExchange::Receive, - self.legacy, + legacy, ) } } @@ -313,7 +345,7 @@ impl OutboundUpgrade for NoiseConfig, identity::Pu where NoiseConfig, identity::PublicKey)>: UpgradeInfo, T: AsyncRead + AsyncWrite + Unpin + Send + 'static, - C: Protocol + AsRef<[u8]> + Zeroize + Send + 'static, + C: Protocol + AsRef<[u8]> + Zeroize + Clone + Send + 'static, { type Output = (RemoteIdentity, NoiseOutput); type Error = NoiseError; @@ -323,10 +355,12 @@ where 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); + handshake::rt1_initiator( socket, session, @@ -432,7 +466,7 @@ where } /// Legacy configuration options. -#[derive(Clone, Default)] +#[derive(Clone, Copy, Default)] pub struct LegacyConfig { /// Whether to continue sending legacy handshake payloads, /// i.e. length-prefixed protobuf payloads inside a length-prefixed @@ -445,3 +479,51 @@ 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 { + let dh_keys = Keypair::::new(); + let noise_keys = dh_keys + .into_authentic(&identity::Keypair::generate_ed25519()) + .unwrap(); + + NoiseConfig::xx(noise_keys) + } +}