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