From 7d528bb45d82ff232fae457cc76de845155b1c9e Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 14 Sep 2022 14:24:25 +1000 Subject: [PATCH 1/5] Add `NoiseConfig::with_prologue` --- transports/noise/CHANGELOG.md | 6 ++++++ transports/noise/src/lib.rs | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/transports/noise/CHANGELOG.md b/transports/noise/CHANGELOG.md index de2c1034a9e..b830b7a93d5 100644 --- a/transports/noise/CHANGELOG.md +++ b/transports/noise/CHANGELOG.md @@ -1,3 +1,9 @@ +# 0.39.1 + +- Add `NoiseConfig::with_prologue` which allows users to set the noise prologue of the handshake. See [PR XXXX]. + +[PR XXXX]: https://github.com/libp2p/rust-libp2p/pull/XXX + # 0.39.0 - Update to `libp2p-core` `v0.36.0`. diff --git a/transports/noise/src/lib.rs b/transports/noise/src/lib.rs index ee609fd028d..5f7e9a0a5ee 100644 --- a/transports/noise/src/lib.rs +++ b/transports/noise/src/lib.rs @@ -79,6 +79,7 @@ pub struct NoiseConfig { legacy: LegacyConfig, remote: R, _marker: std::marker::PhantomData

, + prologue: Vec, } impl NoiseConfig { @@ -88,6 +89,15 @@ impl NoiseConfig { NoiseAuthenticated { config: self } } + /// Set the noise prologue. + /// + /// The prologue can contain arbitrary data and will be hashed into the noise handshake. + /// For the handshake to succeed, both parties must set the same prologue. + pub fn with_prologue(&mut self, prologue: Vec) -> &mut Self { + self.prologue = 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; @@ -107,6 +117,7 @@ where legacy: LegacyConfig::default(), remote: (), _marker: std::marker::PhantomData, + prologue: Vec::default(), } } } @@ -123,6 +134,7 @@ where legacy: LegacyConfig::default(), remote: (), _marker: std::marker::PhantomData, + prologue: Vec::default(), } } } @@ -142,6 +154,7 @@ where legacy: LegacyConfig::default(), remote: (), _marker: std::marker::PhantomData, + prologue: Vec::default(), } } } @@ -165,6 +178,7 @@ where legacy: LegacyConfig::default(), remote: (remote_dh, remote_id), _marker: std::marker::PhantomData, + prologue: Vec::default(), } } } @@ -185,6 +199,7 @@ where 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); @@ -212,6 +227,7 @@ where 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); @@ -241,6 +257,7 @@ where 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); @@ -268,6 +285,7 @@ where 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); @@ -297,6 +315,7 @@ where 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); @@ -324,6 +343,7 @@ 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() From 19485680a9d7aa1d57790da6bc7f95710785d3ac Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 14 Sep 2022 14:28:00 +1000 Subject: [PATCH 2/5] Bump version in manifest and update changelog --- CHANGELOG.md | 2 ++ Cargo.toml | 2 +- transports/noise/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d28138e1e7e..429ea473a42 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). # 0.48.0 diff --git a/Cargo.toml b/Cargo.toml index 89e63a4e5cd..9f8107869f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,7 +88,7 @@ libp2p-identify = { version = "0.39.0", path = "protocols/identify", optional = libp2p-kad = { version = "0.40.0", path = "protocols/kad", optional = true } libp2p-metrics = { version = "0.9.0", path = "misc/metrics", optional = true } libp2p-mplex = { version = "0.36.0", path = "muxers/mplex", optional = true } -libp2p-noise = { version = "0.39.0", path = "transports/noise", optional = true } +libp2p-noise = { version = "0.39.1", path = "transports/noise", optional = true } libp2p-ping = { version = "0.39.0", path = "protocols/ping", optional = true } libp2p-plaintext = { version = "0.36.0", path = "transports/plaintext", optional = true } libp2p-pnet = { version = "0.22.0", path = "transports/pnet", optional = true } diff --git a/transports/noise/Cargo.toml b/transports/noise/Cargo.toml index 5ee9330818d..8fef520cb9a 100644 --- a/transports/noise/Cargo.toml +++ b/transports/noise/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-noise" edition = "2021" rust-version = "1.56.1" description = "Cryptographic handshake protocol using the noise framework." -version = "0.39.0" +version = "0.39.1" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" From 8f519d4ae20b99f3d0d08c05b4748b2667872ce5 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 16 Sep 2022 15:03:53 +1000 Subject: [PATCH 3/5] Update docs --- transports/noise/src/lib.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/transports/noise/src/lib.rs b/transports/noise/src/lib.rs index 5f7e9a0a5ee..ee5537c6b43 100644 --- a/transports/noise/src/lib.rs +++ b/transports/noise/src/lib.rs @@ -79,6 +79,13 @@ 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, } @@ -90,9 +97,6 @@ impl NoiseConfig { } /// Set the noise prologue. - /// - /// The prologue can contain arbitrary data and will be hashed into the noise handshake. - /// For the handshake to succeed, both parties must set the same prologue. pub fn with_prologue(&mut self, prologue: Vec) -> &mut Self { self.prologue = prologue; self From 45269801b22820c26ce7bb9a39cf8983e50a6925 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 16 Sep 2022 15:04:53 +1000 Subject: [PATCH 4/5] Update changelog with PR number --- transports/noise/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/transports/noise/CHANGELOG.md b/transports/noise/CHANGELOG.md index 5464514ef78..8dcb8926c32 100644 --- a/transports/noise/CHANGELOG.md +++ b/transports/noise/CHANGELOG.md @@ -2,10 +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 XXXX]. +- 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 XXXX]: https://github.com/libp2p/rust-libp2p/pull/XXX +[PR 2903]: https://github.com/libp2p/rust-libp2p/pull/2903 # 0.39.0 From 5b0c4cbdb85021ad2d9b77b596742833df0a8272 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 16 Sep 2022 15:29:52 +1000 Subject: [PATCH 5/5] Add unit tests for handshake hashes --- transports/noise/src/lib.rs | 178 ++++++++++++++++++++++++------------ 1 file changed, 118 insertions(+), 60 deletions(-) diff --git a/transports/noise/src/lib.rs b/transports/noise/src/lib.rs index 453a5791b16..8c63613bd5e 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; @@ -96,9 +97,8 @@ impl NoiseConfig { } /// Set the noise prologue. - pub fn with_prologue(&mut self, prologue: Vec) -> &mut Self { - self.prologue = prologue; - self + pub fn with_prologue(self, prologue: Vec) -> Self { + Self { prologue, ..self } } /// Sets the legacy configuration options to use, if any. @@ -108,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, @@ -192,26 +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() - .prologue(self.prologue.as_ref()) - .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, ) } } @@ -220,26 +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() - .prologue(self.prologue.as_ref()) - .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, ) } } @@ -250,26 +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() - .prologue(self.prologue.as_ref()) - .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, ) } } @@ -278,26 +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() - .prologue(self.prologue.as_ref()) - .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, ) } } @@ -308,26 +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() - .prologue(self.prologue.as_ref()) - .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, ) } } @@ -336,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; @@ -351,6 +360,7 @@ where .remote_public_key(self.remote.0.as_ref()) .build_initiator() .map_err(NoiseError::from); + handshake::rt1_initiator( socket, session, @@ -456,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 @@ -469,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) + } +}