mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-22 06:11:34 +00:00
feat(noise): remove deprecated legacy handshakes
This patch removes all deprecated legacy code from `libp2p-noise` and attempts to collapse all the abstraction layers into something a lot simpler. Pull-Request: #3511.
This commit is contained in:
@ -58,7 +58,7 @@ pub trait TransportExt: Transport {
|
|||||||
/// let transport = tcp::tokio::Transport::new(tcp::Config::default().nodelay(true))
|
/// let transport = tcp::tokio::Transport::new(tcp::Config::default().nodelay(true))
|
||||||
/// .upgrade(upgrade::Version::V1)
|
/// .upgrade(upgrade::Version::V1)
|
||||||
/// .authenticate(
|
/// .authenticate(
|
||||||
/// noise::NoiseAuthenticated::xx(&id_keys)
|
/// noise::Config::new(&id_keys)
|
||||||
/// .expect("Signing libp2p-noise static DH keypair failed."),
|
/// .expect("Signing libp2p-noise static DH keypair failed."),
|
||||||
/// )
|
/// )
|
||||||
/// .multiplex(mplex::MplexConfig::new())
|
/// .multiplex(mplex::MplexConfig::new())
|
||||||
|
@ -92,17 +92,18 @@
|
|||||||
//! An example of initialising a gossipsub compatible swarm:
|
//! An example of initialising a gossipsub compatible swarm:
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use libp2p_gossipsub::Event;
|
//! # use libp2p_gossipsub::Event;
|
||||||
//! use libp2p_core::{identity::Keypair,transport::{Transport, MemoryTransport}, Multiaddr};
|
//! # use libp2p_core::{transport::{Transport, MemoryTransport}, Multiaddr};
|
||||||
//! use libp2p_gossipsub::MessageAuthenticity;
|
//! # use libp2p_gossipsub::MessageAuthenticity;
|
||||||
//! let local_key = Keypair::generate_ed25519();
|
//! # use libp2p_identity as identity;
|
||||||
//! let local_peer_id = libp2p_core::PeerId::from(local_key.public());
|
//! let local_key = identity::Keypair::generate_ed25519();
|
||||||
|
//! let local_peer_id = local_key.public().to_peer_id();
|
||||||
//!
|
//!
|
||||||
//! // Set up an encrypted TCP Transport over the Mplex
|
//! // Set up an encrypted TCP Transport over the Mplex
|
||||||
//! // This is test transport (memory).
|
//! // This is test transport (memory).
|
||||||
//! let transport = MemoryTransport::default()
|
//! let transport = MemoryTransport::default()
|
||||||
//! .upgrade(libp2p_core::upgrade::Version::V1)
|
//! .upgrade(libp2p_core::upgrade::Version::V1)
|
||||||
//! .authenticate(libp2p_noise::NoiseAuthenticated::xx(&local_key).unwrap())
|
//! .authenticate(libp2p_noise::Config::new(&local_key).unwrap())
|
||||||
//! .multiplex(libp2p_mplex::MplexConfig::new())
|
//! .multiplex(libp2p_mplex::MplexConfig::new())
|
||||||
//! .boxed();
|
//! .boxed();
|
||||||
//!
|
//!
|
||||||
@ -123,11 +124,11 @@
|
|||||||
//! // subscribe to the topic
|
//! // subscribe to the topic
|
||||||
//! gossipsub.subscribe(&topic);
|
//! gossipsub.subscribe(&topic);
|
||||||
//! // create the swarm (use an executor in a real example)
|
//! // create the swarm (use an executor in a real example)
|
||||||
//! libp2p_swarm::Swarm::without_executor(
|
//! libp2p_swarm::SwarmBuilder::without_executor(
|
||||||
//! transport,
|
//! transport,
|
||||||
//! gossipsub,
|
//! gossipsub,
|
||||||
//! local_peer_id,
|
//! local_peer_id,
|
||||||
//! )
|
//! ).build()
|
||||||
//! };
|
//! };
|
||||||
//!
|
//!
|
||||||
//! // Listen on a memory transport.
|
//! // Listen on a memory transport.
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
## 0.42.0 - unreleased
|
## 0.43.0 - unreleased
|
||||||
|
|
||||||
- Raise MSRV to 1.65.
|
- Raise MSRV to 1.65.
|
||||||
See [PR 3715].
|
See [PR 3715].
|
||||||
|
|
||||||
|
- Remove deprecated APIs. See [PR 3511].
|
||||||
|
|
||||||
|
[PR 3511]: https://github.com/libp2p/rust-libp2p/pull/3511
|
||||||
[PR 3715]: https://github.com/libp2p/rust-libp2p/pull/3715
|
[PR 3715]: https://github.com/libp2p/rust-libp2p/pull/3715
|
||||||
|
|
||||||
## 0.42.2
|
## 0.42.2
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
//! Noise protocol messages in form of [`NoiseFramed`].
|
//! Noise protocol messages in form of [`NoiseFramed`].
|
||||||
|
|
||||||
use crate::io::Output;
|
use crate::io::Output;
|
||||||
use crate::{Error, Protocol, PublicKey};
|
use crate::{protocol::PublicKey, Error};
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use futures::ready;
|
use futures::ready;
|
||||||
@ -89,15 +89,15 @@ impl<T> NoiseFramed<T, snow::HandshakeState> {
|
|||||||
/// transitioning to transport mode because the handshake is incomplete,
|
/// transitioning to transport mode because the handshake is incomplete,
|
||||||
/// an error is returned. Similarly if the remote's static DH key, if
|
/// an error is returned. Similarly if the remote's static DH key, if
|
||||||
/// present, cannot be parsed.
|
/// present, cannot be parsed.
|
||||||
pub(crate) fn into_transport<C>(self) -> Result<(Option<PublicKey<C>>, Output<T>), Error>
|
pub(crate) fn into_transport(self) -> Result<(PublicKey, Output<T>), Error> {
|
||||||
where
|
let dh_remote_pubkey = self.session.get_remote_static().ok_or_else(|| {
|
||||||
C: Protocol<C> + AsRef<[u8]>,
|
Error::Io(io::Error::new(
|
||||||
{
|
io::ErrorKind::Other,
|
||||||
let dh_remote_pubkey = self
|
"expect key to always be present at end of XX session",
|
||||||
.session
|
))
|
||||||
.get_remote_static()
|
})?;
|
||||||
.map(C::public_from_bytes)
|
|
||||||
.transpose()?;
|
let dh_remote_pubkey = PublicKey::from_slice(dh_remote_pubkey)?;
|
||||||
|
|
||||||
let io = NoiseFramed {
|
let io = NoiseFramed {
|
||||||
session: self.session.into_transport_mode()?,
|
session: self.session.into_transport_mode()?,
|
||||||
|
@ -27,46 +27,14 @@ mod proto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
use crate::io::{framed::NoiseFramed, Output};
|
use crate::io::{framed::NoiseFramed, Output};
|
||||||
use crate::protocol::{KeypairIdentity, Protocol, PublicKey};
|
use crate::protocol::{KeypairIdentity, STATIC_KEY_DOMAIN};
|
||||||
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use crate::LegacyConfig;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use libp2p_identity as identity;
|
use libp2p_identity as identity;
|
||||||
use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer};
|
use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
/// The identity of the remote established during a handshake.
|
|
||||||
#[deprecated(
|
|
||||||
note = "This type will be made private in the future. Use `libp2p_noise::Config::new` instead to use the noise protocol."
|
|
||||||
)]
|
|
||||||
pub enum RemoteIdentity<C> {
|
|
||||||
/// The remote provided no identifying information.
|
|
||||||
///
|
|
||||||
/// The identity of the remote is unknown and must be obtained through
|
|
||||||
/// a different, out-of-band channel.
|
|
||||||
Unknown,
|
|
||||||
|
|
||||||
/// The remote provided a static DH public key.
|
|
||||||
///
|
|
||||||
/// The static DH public key is authentic in the sense that a successful
|
|
||||||
/// handshake implies that the remote possesses a corresponding secret key.
|
|
||||||
///
|
|
||||||
/// > **Note**: To rule out active attacks like a MITM, trust in the public key must
|
|
||||||
/// > still be established, e.g. by comparing the key against an expected or
|
|
||||||
/// > otherwise known public key.
|
|
||||||
StaticDhKey(PublicKey<C>),
|
|
||||||
|
|
||||||
/// The remote provided a public identity key in addition to a static DH
|
|
||||||
/// public key and the latter is authentic w.r.t. the former.
|
|
||||||
///
|
|
||||||
/// > **Note**: To rule out active attacks like a MITM, trust in the public key must
|
|
||||||
/// > still be established, e.g. by comparing the key against an expected or
|
|
||||||
/// > otherwise known public key.
|
|
||||||
IdentityKey(identity::PublicKey),
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
// Internal
|
// Internal
|
||||||
|
|
||||||
@ -81,8 +49,6 @@ pub(crate) struct State<T> {
|
|||||||
dh_remote_pubkey_sig: Option<Vec<u8>>,
|
dh_remote_pubkey_sig: Option<Vec<u8>>,
|
||||||
/// The known or received public identity key of the remote, if any.
|
/// The known or received public identity key of the remote, if any.
|
||||||
id_remote_pubkey: Option<identity::PublicKey>,
|
id_remote_pubkey: Option<identity::PublicKey>,
|
||||||
/// Legacy configuration parameters.
|
|
||||||
legacy: LegacyConfig,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> State<T> {
|
impl<T> State<T> {
|
||||||
@ -97,14 +63,12 @@ impl<T> State<T> {
|
|||||||
session: snow::HandshakeState,
|
session: snow::HandshakeState,
|
||||||
identity: KeypairIdentity,
|
identity: KeypairIdentity,
|
||||||
expected_remote_key: Option<identity::PublicKey>,
|
expected_remote_key: Option<identity::PublicKey>,
|
||||||
legacy: LegacyConfig,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
identity,
|
identity,
|
||||||
io: NoiseFramed::new(io, session),
|
io: NoiseFramed::new(io, session),
|
||||||
dh_remote_pubkey_sig: None,
|
dh_remote_pubkey_sig: None,
|
||||||
id_remote_pubkey: expected_remote_key,
|
id_remote_pubkey: expected_remote_key,
|
||||||
legacy,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,23 +76,22 @@ impl<T> State<T> {
|
|||||||
impl<T> State<T> {
|
impl<T> State<T> {
|
||||||
/// Finish a handshake, yielding the established remote identity and the
|
/// Finish a handshake, yielding the established remote identity and the
|
||||||
/// [`Output`] for communicating on the encrypted channel.
|
/// [`Output`] for communicating on the encrypted channel.
|
||||||
pub(crate) fn finish<C>(self) -> Result<(RemoteIdentity<C>, Output<T>), Error>
|
pub(crate) fn finish(self) -> Result<(identity::PublicKey, Output<T>), Error> {
|
||||||
where
|
|
||||||
C: Protocol<C> + AsRef<[u8]>,
|
|
||||||
{
|
|
||||||
let (pubkey, io) = self.io.into_transport()?;
|
let (pubkey, io) = self.io.into_transport()?;
|
||||||
let remote = match (self.id_remote_pubkey, pubkey) {
|
|
||||||
(_, None) => RemoteIdentity::Unknown,
|
let id_pk = self
|
||||||
(None, Some(dh_pk)) => RemoteIdentity::StaticDhKey(dh_pk),
|
.id_remote_pubkey
|
||||||
(Some(id_pk), Some(dh_pk)) => {
|
.ok_or_else(|| Error::AuthenticationFailed)?;
|
||||||
if C::verify(&id_pk, &dh_pk, &self.dh_remote_pubkey_sig) {
|
|
||||||
RemoteIdentity::IdentityKey(id_pk)
|
let is_valid_signature = self.dh_remote_pubkey_sig.as_ref().map_or(false, |s| {
|
||||||
} else {
|
id_pk.verify(&[STATIC_KEY_DOMAIN.as_bytes(), pubkey.as_ref()].concat(), s)
|
||||||
return Err(Error::BadSignature);
|
});
|
||||||
}
|
|
||||||
}
|
if !is_valid_signature {
|
||||||
};
|
return Err(Error::BadSignature);
|
||||||
Ok((remote, io))
|
}
|
||||||
|
|
||||||
|
Ok((id_pk, io))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,60 +133,16 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A future for receiving a Noise handshake message with a payload
|
/// A future for receiving a Noise handshake message with a payload identifying the remote.
|
||||||
/// identifying the remote.
|
|
||||||
///
|
|
||||||
/// 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(crate) async fn recv_identity<T>(state: &mut State<T>) -> Result<(), Error>
|
pub(crate) async fn recv_identity<T>(state: &mut State<T>) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: AsyncRead + Unpin,
|
T: AsyncRead + Unpin,
|
||||||
{
|
{
|
||||||
let msg = recv(state).await?;
|
let msg = recv(state).await?;
|
||||||
|
|
||||||
let mut reader = BytesReader::from_bytes(&msg[..]);
|
let mut reader = BytesReader::from_bytes(&msg[..]);
|
||||||
let mut pb_result = proto::NoiseHandshakePayload::from_reader(&mut reader, &msg[..]);
|
let pb = proto::NoiseHandshakePayload::from_reader(&mut reader, &msg[..])?;
|
||||||
|
|
||||||
if pb_result.is_err() && state.legacy.recv_legacy_handshake {
|
state.id_remote_pubkey = Some(identity::PublicKey::try_decode_protobuf(&pb.identity_key)?);
|
||||||
// NOTE: This is support for legacy handshake payloads. As long as
|
|
||||||
// the frame length is less than 256 bytes, which is the case for
|
|
||||||
// all protobuf payloads not containing RSA keys, there is no room
|
|
||||||
// for misinterpretation, since if a two-bytes length prefix is present
|
|
||||||
// the first byte will be 0, which is always an unexpected protobuf tag
|
|
||||||
// value because the fields in the .proto file start with 1 and decoding
|
|
||||||
// thus expects a non-zero first byte. We will therefore always correctly
|
|
||||||
// fall back to the legacy protobuf parsing in these cases (again, not
|
|
||||||
// considering RSA keys, for which there may be a probabilistically
|
|
||||||
// very small chance of misinterpretation).
|
|
||||||
pb_result = pb_result.or_else(|e| {
|
|
||||||
if msg.len() > 2 {
|
|
||||||
let mut buf = [0, 0];
|
|
||||||
buf.copy_from_slice(&msg[..2]);
|
|
||||||
// If there is a second length it must be 2 bytes shorter than the
|
|
||||||
// frame length, because each length is encoded as a `u16`.
|
|
||||||
if usize::from(u16::from_be_bytes(buf)) + 2 == msg.len() {
|
|
||||||
log::debug!("Attempting fallback legacy protobuf decoding.");
|
|
||||||
let mut reader = BytesReader::from_bytes(&msg[2..]);
|
|
||||||
proto::NoiseHandshakePayload::from_reader(&mut reader, &msg[2..])
|
|
||||||
} else {
|
|
||||||
Err(e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(e)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let pb = pb_result?;
|
|
||||||
|
|
||||||
if !pb.identity_key.is_empty() {
|
|
||||||
let pk = identity::PublicKey::try_decode_protobuf(&pb.identity_key)?;
|
|
||||||
if let Some(ref k) = state.id_remote_pubkey {
|
|
||||||
if k != &pk {
|
|
||||||
return Err(Error::UnexpectedKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state.id_remote_pubkey = Some(pk);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !pb.identity_sig.is_empty() {
|
if !pb.identity_sig.is_empty() {
|
||||||
state.dh_remote_pubkey_sig = Some(pb.identity_sig);
|
state.dh_remote_pubkey_sig = Some(pb.identity_sig);
|
||||||
@ -242,43 +161,9 @@ where
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(ref sig) = state.identity.signature {
|
pb.identity_sig = state.identity.signature.clone();
|
||||||
pb.identity_sig = sig.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut msg = if state.legacy.send_legacy_handshake {
|
let mut msg = Vec::with_capacity(pb.get_size());
|
||||||
let mut msg = Vec::with_capacity(2 + pb.get_size());
|
|
||||||
msg.extend_from_slice(&(pb.get_size() as u16).to_be_bytes());
|
|
||||||
msg
|
|
||||||
} else {
|
|
||||||
Vec::with_capacity(pb.get_size())
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut writer = Writer::new(&mut msg);
|
|
||||||
pb.write_message(&mut writer).expect("Encoding to succeed");
|
|
||||||
state.io.send(&msg).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send a Noise handshake message with a payload identifying the local node to the remote.
|
|
||||||
pub(crate) async fn send_signature_only<T>(state: &mut State<T>) -> Result<(), Error>
|
|
||||||
where
|
|
||||||
T: AsyncWrite + Unpin,
|
|
||||||
{
|
|
||||||
let mut pb = proto::NoiseHandshakePayload::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.get_size());
|
|
||||||
msg.extend_from_slice(&(pb.get_size() as u16).to_be_bytes());
|
|
||||||
msg
|
|
||||||
} else {
|
|
||||||
Vec::with_capacity(pb.get_size())
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut writer = Writer::new(&mut msg);
|
let mut writer = Writer::new(&mut msg);
|
||||||
pb.write_message(&mut writer).expect("Encoding to succeed");
|
pb.write_message(&mut writer).expect("Encoding to succeed");
|
||||||
|
@ -59,110 +59,81 @@
|
|||||||
mod io;
|
mod io;
|
||||||
mod protocol;
|
mod protocol;
|
||||||
|
|
||||||
pub use io::handshake::RemoteIdentity;
|
|
||||||
pub use io::Output;
|
pub use io::Output;
|
||||||
|
|
||||||
pub use protocol::Protocol;
|
|
||||||
|
|
||||||
#[deprecated(
|
|
||||||
note = "This type will be made private in the future. Use `libp2p_noise::Config::new` instead to use the noise protocol."
|
|
||||||
)]
|
|
||||||
pub type X25519Spec = protocol::x25519_spec::X25519Spec;
|
|
||||||
|
|
||||||
#[deprecated(
|
|
||||||
note = "This type will be made private in the future. Use `libp2p_noise::Config::new` instead to use the noise protocol."
|
|
||||||
)]
|
|
||||||
pub type X25519 = protocol::x25519::X25519;
|
|
||||||
|
|
||||||
#[deprecated(
|
|
||||||
note = "This type will be made private in the future. Use `libp2p_noise::Config::new` instead to use the noise protocol."
|
|
||||||
)]
|
|
||||||
pub type AuthenticKeypair<T> = protocol::AuthenticKeypair<T>;
|
|
||||||
|
|
||||||
#[deprecated(
|
|
||||||
note = "This type will be made private in the future. Use `libp2p_noise::Config::new` instead to use the noise protocol."
|
|
||||||
)]
|
|
||||||
pub type Keypair<T> = protocol::Keypair<T>;
|
|
||||||
|
|
||||||
#[deprecated(
|
|
||||||
note = "This type will be made private in the future. Use `libp2p_noise::Config::new` instead to use the noise protocol."
|
|
||||||
)]
|
|
||||||
pub type KeypairIdentity = protocol::KeypairIdentity;
|
|
||||||
|
|
||||||
#[deprecated(
|
|
||||||
note = "This type will be made private in the future. Use `libp2p_noise::Config::new` instead to use the noise protocol."
|
|
||||||
)]
|
|
||||||
pub type PublicKey<T> = protocol::PublicKey<T>;
|
|
||||||
|
|
||||||
#[deprecated(
|
|
||||||
note = "This type will be made private in the future. Use `libp2p_noise::Config::new` instead to use the noise protocol."
|
|
||||||
)]
|
|
||||||
pub type SecretKey<T> = protocol::SecretKey<T>;
|
|
||||||
|
|
||||||
#[deprecated(
|
|
||||||
note = "This type will be made private in the future. Use `libp2p_noise::Config::new` instead to use the noise protocol."
|
|
||||||
)]
|
|
||||||
pub type ProtocolParams = protocol::ProtocolParams;
|
|
||||||
|
|
||||||
#[deprecated(
|
|
||||||
note = "This type will be made private in the future. Use `libp2p_noise::Config::new` instead to use the noise protocol."
|
|
||||||
)]
|
|
||||||
pub type IK = protocol::IK;
|
|
||||||
|
|
||||||
#[deprecated(
|
|
||||||
note = "This type will be made private in the future. Use `libp2p_noise::Config::new` instead to use the noise protocol."
|
|
||||||
)]
|
|
||||||
pub type IX = protocol::IX;
|
|
||||||
|
|
||||||
#[deprecated(
|
|
||||||
note = "This type will be made private in the future. Use `libp2p_noise::Config::new` instead to use the noise protocol."
|
|
||||||
)]
|
|
||||||
pub type XX = protocol::XX;
|
|
||||||
|
|
||||||
#[deprecated(
|
|
||||||
note = "This type has been renamed to drop the `Noise` prefix, refer to it as `noise::Error` instead."
|
|
||||||
)]
|
|
||||||
pub type NoiseError = Error;
|
|
||||||
|
|
||||||
#[deprecated(
|
|
||||||
note = "This type has been renamed to drop the `Noise` prefix, refer to it as `noise::Output` instead."
|
|
||||||
)]
|
|
||||||
pub type NoiseOutput<T> = Output<T>;
|
|
||||||
|
|
||||||
use crate::handshake::State;
|
use crate::handshake::State;
|
||||||
use crate::io::handshake;
|
use crate::io::handshake;
|
||||||
use futures::future::BoxFuture;
|
use crate::protocol::{noise_params_into_builder, AuthenticKeypair, Keypair, PARAMS_XX};
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use libp2p_core::{InboundUpgrade, OutboundUpgrade, UpgradeInfo};
|
use libp2p_core::{InboundUpgrade, OutboundUpgrade, UpgradeInfo};
|
||||||
use libp2p_identity as identity;
|
use libp2p_identity as identity;
|
||||||
use libp2p_identity::PeerId;
|
use libp2p_identity::PeerId;
|
||||||
|
use snow::params::NoiseParams;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use zeroize::Zeroize;
|
|
||||||
|
|
||||||
/// The configuration for the noise handshake.
|
/// The configuration for the noise handshake.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
inner: NoiseAuthenticated<XX, X25519Spec, ()>,
|
dh_keys: AuthenticKeypair,
|
||||||
|
params: NoiseParams,
|
||||||
|
|
||||||
|
/// 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 <https://noiseprotocol.org/noise.html#prologue>.
|
||||||
|
prologue: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
/// Construct a new configuration for the noise handshake using the XX handshake pattern.
|
/// Construct a new configuration for the noise handshake using the XX handshake pattern.
|
||||||
|
|
||||||
pub fn new(identity: &identity::Keypair) -> Result<Self, Error> {
|
pub fn new(identity: &identity::Keypair) -> Result<Self, Error> {
|
||||||
Ok(Config {
|
let noise_keys = Keypair::new().into_authentic(identity)?;
|
||||||
inner: NoiseAuthenticated::xx(identity)?,
|
|
||||||
|
Ok(Self {
|
||||||
|
dh_keys: noise_keys,
|
||||||
|
params: PARAMS_XX.clone(),
|
||||||
|
prologue: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the noise prologue.
|
/// Set the noise prologue.
|
||||||
|
|
||||||
pub fn with_prologue(mut self, prologue: Vec<u8>) -> Self {
|
pub fn with_prologue(mut self, prologue: Vec<u8>) -> Self {
|
||||||
self.inner.config.prologue = prologue;
|
self.prologue = prologue;
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn into_responder<S>(self, socket: S) -> Result<State<S>, Error> {
|
||||||
|
let session = noise_params_into_builder(
|
||||||
|
self.params,
|
||||||
|
&self.prologue,
|
||||||
|
self.dh_keys.keypair.secret(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.build_responder()?;
|
||||||
|
|
||||||
|
let state = State::new(socket, session, self.dh_keys.identity, None);
|
||||||
|
|
||||||
|
Ok(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_initiator<S>(self, socket: S) -> Result<State<S>, Error> {
|
||||||
|
let session = noise_params_into_builder(
|
||||||
|
self.params,
|
||||||
|
&self.prologue,
|
||||||
|
self.dh_keys.keypair.secret(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.build_initiator()?;
|
||||||
|
|
||||||
|
let state = State::new(socket, session, self.dh_keys.identity, None);
|
||||||
|
|
||||||
|
Ok(state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpgradeInfo for Config {
|
impl UpgradeInfo for Config {
|
||||||
@ -182,8 +153,19 @@ where
|
|||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>> + Send>>;
|
type Future = Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>> + Send>>;
|
||||||
|
|
||||||
fn upgrade_inbound(self, socket: T, info: Self::Info) -> Self::Future {
|
fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future {
|
||||||
self.inner.upgrade_inbound(socket, info)
|
async move {
|
||||||
|
let mut state = self.into_responder(socket)?;
|
||||||
|
|
||||||
|
handshake::recv_empty(&mut state).await?;
|
||||||
|
handshake::send_identity(&mut state).await?;
|
||||||
|
handshake::recv_identity(&mut state).await?;
|
||||||
|
|
||||||
|
let (pk, io) = state.finish()?;
|
||||||
|
|
||||||
|
Ok((pk.to_peer_id(), io))
|
||||||
|
}
|
||||||
|
.boxed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,185 +177,19 @@ where
|
|||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>> + Send>>;
|
type Future = Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>> + Send>>;
|
||||||
|
|
||||||
fn upgrade_outbound(self, socket: T, info: Self::Info) -> Self::Future {
|
fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future {
|
||||||
self.inner.upgrade_outbound(socket, info)
|
async move {
|
||||||
}
|
let mut state = self.into_initiator(socket)?;
|
||||||
}
|
|
||||||
|
|
||||||
/// The protocol upgrade configuration.
|
handshake::send_empty(&mut state).await?;
|
||||||
#[deprecated(
|
handshake::recv_identity(&mut state).await?;
|
||||||
note = "Use `libp2p_noise::Config` instead. All other handshake patterns are deprecated and will be removed."
|
handshake::send_identity(&mut state).await?;
|
||||||
)]
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct NoiseConfig<P, C: Zeroize, R = ()> {
|
|
||||||
dh_keys: AuthenticKeypair<C>,
|
|
||||||
params: ProtocolParams,
|
|
||||||
|
|
||||||
legacy: LegacyConfig,
|
let (pk, io) = state.finish()?;
|
||||||
remote: R,
|
|
||||||
_marker: std::marker::PhantomData<P>,
|
|
||||||
|
|
||||||
/// Prologue to use in the noise handshake.
|
Ok((pk.to_peer_id(), io))
|
||||||
///
|
|
||||||
/// 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 <https://noiseprotocol.org/noise.html#prologue>.
|
|
||||||
prologue: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<H, C: Zeroize, R> NoiseConfig<H, C, R> {
|
|
||||||
/// Turn the `NoiseConfig` into an authenticated upgrade for use
|
|
||||||
/// with a `Swarm`.
|
|
||||||
pub fn into_authenticated(self) -> NoiseAuthenticated<H, C, R> {
|
|
||||||
NoiseAuthenticated { config: self }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the noise prologue.
|
|
||||||
pub fn with_prologue(self, prologue: Vec<u8>) -> Self {
|
|
||||||
Self { prologue, ..self }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the legacy configuration options to use, if any.
|
|
||||||
#[deprecated(
|
|
||||||
since = "0.42.0",
|
|
||||||
note = "`LegacyConfig` will be removed without replacement."
|
|
||||||
)]
|
|
||||||
|
|
||||||
pub fn set_legacy_config(&mut self, cfg: LegacyConfig) -> &mut Self {
|
|
||||||
self.legacy = cfg;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 + Protocol<C> + AsRef<[u8]>,
|
|
||||||
{
|
|
||||||
fn into_responder<S>(self, socket: S) -> Result<State<S>, Error> {
|
|
||||||
let session = self
|
|
||||||
.params
|
|
||||||
.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<S>(self, socket: S) -> Result<State<S>, Error> {
|
|
||||||
let session = self
|
|
||||||
.params
|
|
||||||
.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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C> NoiseConfig<IX, C>
|
|
||||||
where
|
|
||||||
C: Protocol<C> + Zeroize,
|
|
||||||
{
|
|
||||||
/// Create a new `NoiseConfig` for the `IX` handshake pattern.
|
|
||||||
pub fn ix(dh_keys: AuthenticKeypair<C>) -> Self {
|
|
||||||
NoiseConfig {
|
|
||||||
dh_keys,
|
|
||||||
params: C::params_ix(),
|
|
||||||
legacy: { LegacyConfig::default() },
|
|
||||||
remote: (),
|
|
||||||
_marker: std::marker::PhantomData,
|
|
||||||
prologue: Vec::default(),
|
|
||||||
}
|
}
|
||||||
}
|
.boxed()
|
||||||
}
|
|
||||||
|
|
||||||
impl<C> NoiseConfig<XX, C>
|
|
||||||
where
|
|
||||||
C: Protocol<C> + Zeroize,
|
|
||||||
{
|
|
||||||
/// Create a new `NoiseConfig` for the `XX` handshake pattern.
|
|
||||||
pub fn xx(dh_keys: AuthenticKeypair<C>) -> Self {
|
|
||||||
NoiseConfig {
|
|
||||||
dh_keys,
|
|
||||||
params: C::params_xx(),
|
|
||||||
legacy: { LegacyConfig::default() },
|
|
||||||
remote: (),
|
|
||||||
_marker: std::marker::PhantomData,
|
|
||||||
prologue: Vec::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C> NoiseConfig<IK, C>
|
|
||||||
where
|
|
||||||
C: Protocol<C> + Zeroize,
|
|
||||||
{
|
|
||||||
/// Create a new `NoiseConfig` for the `IK` handshake pattern (recipient side).
|
|
||||||
///
|
|
||||||
/// Since the identity of the local node is known to the remote, this configuration
|
|
||||||
/// does not transmit a static DH public key or public identity key to the remote.
|
|
||||||
pub fn ik_listener(dh_keys: AuthenticKeypair<C>) -> Self {
|
|
||||||
NoiseConfig {
|
|
||||||
dh_keys,
|
|
||||||
params: C::params_ik(),
|
|
||||||
legacy: { LegacyConfig::default() },
|
|
||||||
remote: (),
|
|
||||||
_marker: std::marker::PhantomData,
|
|
||||||
prologue: Vec::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C> NoiseConfig<IK, C, (PublicKey<C>, identity::PublicKey)>
|
|
||||||
where
|
|
||||||
C: Protocol<C> + Zeroize + AsRef<[u8]>,
|
|
||||||
{
|
|
||||||
/// Create a new `NoiseConfig` for the `IK` handshake pattern (initiator side).
|
|
||||||
///
|
|
||||||
/// In this configuration, the remote identity is known to the local node,
|
|
||||||
/// but the local node still needs to transmit its own public identity.
|
|
||||||
pub fn ik_dialer(
|
|
||||||
dh_keys: AuthenticKeypair<C>,
|
|
||||||
remote_id: identity::PublicKey,
|
|
||||||
remote_dh: PublicKey<C>,
|
|
||||||
) -> Self {
|
|
||||||
NoiseConfig {
|
|
||||||
dh_keys,
|
|
||||||
params: C::params_ik(),
|
|
||||||
legacy: { LegacyConfig::default() },
|
|
||||||
remote: (remote_dh, remote_id),
|
|
||||||
_marker: std::marker::PhantomData,
|
|
||||||
prologue: Vec::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Specialised implementation of `into_initiator` for the `IK` handshake where `R != ()`.
|
|
||||||
fn into_initiator<S>(self, socket: S) -> Result<State<S>, Error> {
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,328 +237,3 @@ impl From<quick_protobuf::Error> for Error {
|
|||||||
Error::InvalidPayload(e.into())
|
Error::InvalidPayload(e.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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,
|
|
||||||
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
|
||||||
C: Protocol<C> + AsRef<[u8]> + Zeroize + Clone + Send + 'static,
|
|
||||||
{
|
|
||||||
type Output = (RemoteIdentity<C>, Output<T>);
|
|
||||||
type Error = Error;
|
|
||||||
type Future = BoxFuture<'static, Result<(RemoteIdentity<C>, Output<T>), Error>>;
|
|
||||||
|
|
||||||
fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future {
|
|
||||||
async move {
|
|
||||||
let mut state = self.into_responder(socket)?;
|
|
||||||
|
|
||||||
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,
|
|
||||||
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
|
||||||
C: Protocol<C> + AsRef<[u8]> + Zeroize + Clone + Send + 'static,
|
|
||||||
{
|
|
||||||
type Output = (RemoteIdentity<C>, Output<T>);
|
|
||||||
type Error = Error;
|
|
||||||
type Future = BoxFuture<'static, Result<(RemoteIdentity<C>, Output<T>), Error>>;
|
|
||||||
|
|
||||||
fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future {
|
|
||||||
async move {
|
|
||||||
let mut state = self.into_initiator(socket)?;
|
|
||||||
|
|
||||||
handshake::send_identity(&mut state).await?;
|
|
||||||
handshake::recv_identity(&mut state).await?;
|
|
||||||
|
|
||||||
state.finish()
|
|
||||||
}
|
|
||||||
.boxed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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,
|
|
||||||
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
|
||||||
C: Protocol<C> + AsRef<[u8]> + Zeroize + Clone + Send + 'static,
|
|
||||||
{
|
|
||||||
type Output = (RemoteIdentity<C>, Output<T>);
|
|
||||||
type Error = Error;
|
|
||||||
type Future = BoxFuture<'static, Result<(RemoteIdentity<C>, Output<T>), Error>>;
|
|
||||||
|
|
||||||
fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future {
|
|
||||||
async move {
|
|
||||||
let mut state = self.into_responder(socket)?;
|
|
||||||
|
|
||||||
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,
|
|
||||||
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
|
||||||
C: Protocol<C> + AsRef<[u8]> + Zeroize + Clone + Send + 'static,
|
|
||||||
{
|
|
||||||
type Output = (RemoteIdentity<C>, Output<T>);
|
|
||||||
type Error = Error;
|
|
||||||
type Future = BoxFuture<'static, Result<(RemoteIdentity<C>, Output<T>), Error>>;
|
|
||||||
|
|
||||||
fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future {
|
|
||||||
async move {
|
|
||||||
let mut state = self.into_initiator(socket)?;
|
|
||||||
|
|
||||||
handshake::send_empty(&mut state).await?;
|
|
||||||
handshake::recv_identity(&mut state).await?;
|
|
||||||
handshake::send_identity(&mut state).await?;
|
|
||||||
|
|
||||||
state.finish()
|
|
||||||
}
|
|
||||||
.boxed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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>: UpgradeInfo,
|
|
||||||
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
|
||||||
C: Protocol<C> + AsRef<[u8]> + Zeroize + Clone + Send + 'static,
|
|
||||||
{
|
|
||||||
type Output = (RemoteIdentity<C>, Output<T>);
|
|
||||||
type Error = Error;
|
|
||||||
type Future = BoxFuture<'static, Result<(RemoteIdentity<C>, Output<T>), Error>>;
|
|
||||||
|
|
||||||
fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future {
|
|
||||||
async move {
|
|
||||||
let mut state = self.into_responder(socket)?;
|
|
||||||
|
|
||||||
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,
|
|
||||||
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
|
||||||
C: Protocol<C> + AsRef<[u8]> + Zeroize + Clone + Send + 'static,
|
|
||||||
{
|
|
||||||
type Output = (RemoteIdentity<C>, Output<T>);
|
|
||||||
type Error = Error;
|
|
||||||
type Future = BoxFuture<'static, Result<(RemoteIdentity<C>, Output<T>), Error>>;
|
|
||||||
|
|
||||||
fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future {
|
|
||||||
async move {
|
|
||||||
let mut state = self.into_initiator(socket)?;
|
|
||||||
|
|
||||||
handshake::send_identity(&mut state).await?;
|
|
||||||
handshake::recv_identity(&mut state).await?;
|
|
||||||
|
|
||||||
state.finish()
|
|
||||||
}
|
|
||||||
.boxed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authenticated Upgrades /////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/// A `NoiseAuthenticated` transport upgrade that wraps around any
|
|
||||||
/// `NoiseConfig` handshake and verifies that the remote identified with a
|
|
||||||
/// [`RemoteIdentity::IdentityKey`], aborting otherwise.
|
|
||||||
///
|
|
||||||
/// See [`NoiseConfig::into_authenticated`].
|
|
||||||
///
|
|
||||||
/// On success, the upgrade yields the [`PeerId`] obtained from the
|
|
||||||
/// `RemoteIdentity`. The output of this upgrade is thus directly suitable
|
|
||||||
/// for creating an [`authenticated`](libp2p_core::transport::upgrade::Authenticate)
|
|
||||||
/// transport for use with a `Swarm`.
|
|
||||||
#[derive(Clone)]
|
|
||||||
#[deprecated(
|
|
||||||
note = "Use `libp2p_noise::Config` instead. All other handshake patterns are deprecated and will be removed."
|
|
||||||
)]
|
|
||||||
pub struct NoiseAuthenticated<P, C: Zeroize, R> {
|
|
||||||
config: NoiseConfig<P, C, R>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NoiseAuthenticated<XX, X25519Spec, ()> {
|
|
||||||
/// Create a new [`NoiseAuthenticated`] for the `XX` handshake pattern using X25519 DH keys.
|
|
||||||
///
|
|
||||||
/// For now, this is the only combination that is guaranteed to be compatible with other libp2p implementations.
|
|
||||||
#[deprecated(note = "Use `libp2p_noise::Config::new` instead.")]
|
|
||||||
pub fn xx(id_keys: &identity::Keypair) -> Result<Self, Error> {
|
|
||||||
let dh_keys = Keypair::<X25519Spec>::new();
|
|
||||||
let noise_keys = dh_keys.into_authentic(id_keys)?;
|
|
||||||
let config = NoiseConfig::xx(noise_keys);
|
|
||||||
|
|
||||||
Ok(config.into_authenticated())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, C: Zeroize, R> UpgradeInfo for NoiseAuthenticated<P, C, R>
|
|
||||||
where
|
|
||||||
NoiseConfig<P, C, R>: UpgradeInfo,
|
|
||||||
{
|
|
||||||
type Info = <NoiseConfig<P, C, R> as UpgradeInfo>::Info;
|
|
||||||
type InfoIter = <NoiseConfig<P, C, R> as UpgradeInfo>::InfoIter;
|
|
||||||
|
|
||||||
fn protocol_info(&self) -> Self::InfoIter {
|
|
||||||
self.config.protocol_info()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, P, C, R> InboundUpgrade<T> for NoiseAuthenticated<P, C, R>
|
|
||||||
where
|
|
||||||
NoiseConfig<P, C, R>: UpgradeInfo
|
|
||||||
+ InboundUpgrade<T, Output = (RemoteIdentity<C>, Output<T>), Error = Error>
|
|
||||||
+ 'static,
|
|
||||||
<NoiseConfig<P, C, R> as InboundUpgrade<T>>::Future: Send,
|
|
||||||
T: AsyncRead + AsyncWrite + Send + 'static,
|
|
||||||
C: Protocol<C> + AsRef<[u8]> + Zeroize + Send + 'static,
|
|
||||||
{
|
|
||||||
type Output = (PeerId, Output<T>);
|
|
||||||
type Error = Error;
|
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>> + Send>>;
|
|
||||||
|
|
||||||
fn upgrade_inbound(self, socket: T, info: Self::Info) -> Self::Future {
|
|
||||||
Box::pin(
|
|
||||||
self.config
|
|
||||||
.upgrade_inbound(socket, info)
|
|
||||||
.and_then(|(remote, io)| match remote {
|
|
||||||
RemoteIdentity::IdentityKey(pk) => future::ok((pk.to_peer_id(), io)),
|
|
||||||
_ => future::err(Error::AuthenticationFailed),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, P, C, R> OutboundUpgrade<T> for NoiseAuthenticated<P, C, R>
|
|
||||||
where
|
|
||||||
NoiseConfig<P, C, R>: UpgradeInfo
|
|
||||||
+ OutboundUpgrade<T, Output = (RemoteIdentity<C>, Output<T>), Error = Error>
|
|
||||||
+ 'static,
|
|
||||||
<NoiseConfig<P, C, R> as OutboundUpgrade<T>>::Future: Send,
|
|
||||||
T: AsyncRead + AsyncWrite + Send + 'static,
|
|
||||||
C: Protocol<C> + AsRef<[u8]> + Zeroize + Send + 'static,
|
|
||||||
{
|
|
||||||
type Output = (PeerId, Output<T>);
|
|
||||||
type Error = Error;
|
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>> + Send>>;
|
|
||||||
|
|
||||||
fn upgrade_outbound(self, socket: T, info: Self::Info) -> Self::Future {
|
|
||||||
Box::pin(
|
|
||||||
self.config
|
|
||||||
.upgrade_outbound(socket, info)
|
|
||||||
.and_then(|(remote, io)| match remote {
|
|
||||||
RemoteIdentity::IdentityKey(pk) => future::ok((pk.to_peer_id(), io)),
|
|
||||||
_ => future::err(Error::AuthenticationFailed),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Legacy configuration options.
|
|
||||||
#[derive(Clone, Copy, Default)]
|
|
||||||
#[deprecated(
|
|
||||||
since = "0.42.0",
|
|
||||||
note = "`LegacyConfig` will be removed without replacement."
|
|
||||||
)]
|
|
||||||
pub struct LegacyConfig {
|
|
||||||
/// Whether to continue sending legacy handshake payloads,
|
|
||||||
/// i.e. length-prefixed protobuf payloads inside a length-prefixed
|
|
||||||
/// noise frame. These payloads are not interoperable with other
|
|
||||||
/// libp2p implementations.
|
|
||||||
pub send_legacy_handshake: bool,
|
|
||||||
/// Whether to support receiving legacy handshake payloads,
|
|
||||||
/// i.e. length-prefixed protobuf payloads inside a length-prefixed
|
|
||||||
/// noise frame. These payloads are not interoperable with other
|
|
||||||
/// libp2p implementations.
|
|
||||||
pub recv_legacy_handshake: bool,
|
|
||||||
}
|
|
||||||
|
@ -20,174 +20,80 @@
|
|||||||
|
|
||||||
//! Components of a Noise protocol.
|
//! Components of a Noise protocol.
|
||||||
|
|
||||||
pub(crate) mod x25519;
|
|
||||||
pub(crate) mod x25519_spec;
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use libp2p_identity as identity;
|
use libp2p_identity as identity;
|
||||||
use rand::SeedableRng;
|
use once_cell::sync::Lazy;
|
||||||
|
use rand::{Rng as _, SeedableRng};
|
||||||
|
use snow::params::NoiseParams;
|
||||||
|
use x25519_dalek::{x25519, X25519_BASEPOINT_BYTES};
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
/// The parameters of a Noise protocol, consisting of a choice
|
/// Prefix of static key signatures for domain separation.
|
||||||
/// for a handshake pattern as well as DH, cipher and hash functions.
|
pub(crate) const STATIC_KEY_DOMAIN: &str = "noise-libp2p-static-key:";
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ProtocolParams(snow::params::NoiseParams);
|
|
||||||
|
|
||||||
impl ProtocolParams {
|
pub(crate) static PARAMS_XX: Lazy<NoiseParams> = Lazy::new(|| {
|
||||||
pub(crate) fn into_builder<'b, C>(
|
"Noise_XX_25519_ChaChaPoly_SHA256"
|
||||||
self,
|
.parse()
|
||||||
prologue: &'b [u8],
|
.expect("Invalid protocol name")
|
||||||
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 {
|
pub(crate) fn noise_params_into_builder<'b>(
|
||||||
builder = builder.remote_public_key(remote_public_key.as_ref());
|
params: NoiseParams,
|
||||||
}
|
prologue: &'b [u8],
|
||||||
|
private_key: &'b SecretKey,
|
||||||
|
remote_public_key: Option<&'b PublicKey>,
|
||||||
|
) -> snow::Builder<'b> {
|
||||||
|
let mut builder = snow::Builder::with_resolver(params, Box::new(Resolver))
|
||||||
|
.prologue(prologue.as_ref())
|
||||||
|
.local_private_key(private_key.as_ref());
|
||||||
|
|
||||||
builder
|
if let Some(remote_public_key) = remote_public_key {
|
||||||
}
|
builder = builder.remote_public_key(remote_public_key.as_ref());
|
||||||
}
|
|
||||||
|
|
||||||
/// Type tag for the IK handshake pattern.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum IK {}
|
|
||||||
|
|
||||||
/// Type tag for the IX handshake pattern.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum IX {}
|
|
||||||
|
|
||||||
/// Type tag for the XX handshake pattern.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum XX {}
|
|
||||||
|
|
||||||
/// A Noise protocol over DH keys of type `C`. The choice of `C` determines the
|
|
||||||
/// protocol parameters for each handshake pattern.
|
|
||||||
#[deprecated(
|
|
||||||
note = "This type will be made private in the future. Use `libp2p_noise::Config::new` instead to use the noise protocol."
|
|
||||||
)]
|
|
||||||
pub trait Protocol<C> {
|
|
||||||
/// The protocol parameters for the IK handshake pattern.
|
|
||||||
fn params_ik() -> ProtocolParams;
|
|
||||||
/// The protocol parameters for the IX handshake pattern.
|
|
||||||
fn params_ix() -> ProtocolParams;
|
|
||||||
/// The protocol parameters for the XX handshake pattern.
|
|
||||||
fn params_xx() -> ProtocolParams;
|
|
||||||
|
|
||||||
/// Construct a DH public key from a byte slice.
|
|
||||||
fn public_from_bytes(s: &[u8]) -> Result<PublicKey<C>, Error>;
|
|
||||||
|
|
||||||
/// Determines whether the authenticity of the given DH static public key
|
|
||||||
/// and public identity key is linked, i.e. that proof of ownership of a
|
|
||||||
/// secret key for the static DH public key implies that the key is
|
|
||||||
/// authentic w.r.t. the given public identity key.
|
|
||||||
///
|
|
||||||
/// The trivial case is when the keys are byte for byte identical.
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
#[deprecated]
|
|
||||||
fn linked(id_pk: &identity::PublicKey, dh_pk: &PublicKey<C>) -> bool {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verifies that a given static DH public key is authentic w.r.t. a
|
builder
|
||||||
/// given public identity key in the context of an optional signature.
|
|
||||||
///
|
|
||||||
/// The given static DH public key is assumed to already be authentic
|
|
||||||
/// in the sense that possession of a corresponding secret key has been
|
|
||||||
/// established, as is the case at the end of a Noise handshake involving
|
|
||||||
/// static DH keys.
|
|
||||||
///
|
|
||||||
/// If the public keys are [`linked`](Protocol::linked), verification succeeds
|
|
||||||
/// without a signature, otherwise a signature over the static DH public key
|
|
||||||
/// must be given and is verified with the public identity key, establishing
|
|
||||||
/// the authenticity of the static DH public key w.r.t. the public identity key.
|
|
||||||
|
|
||||||
fn verify(id_pk: &identity::PublicKey, dh_pk: &PublicKey<C>, sig: &Option<Vec<u8>>) -> bool
|
|
||||||
where
|
|
||||||
C: AsRef<[u8]>,
|
|
||||||
{
|
|
||||||
Self::linked(id_pk, dh_pk)
|
|
||||||
|| sig
|
|
||||||
.as_ref()
|
|
||||||
.map_or(false, |s| id_pk.verify(dh_pk.as_ref(), s))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sign(id_keys: &identity::Keypair, dh_pk: &PublicKey<C>) -> Result<Vec<u8>, Error>
|
|
||||||
where
|
|
||||||
C: AsRef<[u8]>,
|
|
||||||
{
|
|
||||||
Ok(id_keys.sign(dh_pk.as_ref())?)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// DH keypair.
|
/// DH keypair.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Keypair<T: Zeroize> {
|
pub(crate) struct Keypair {
|
||||||
secret: SecretKey<T>,
|
secret: SecretKey,
|
||||||
public: PublicKey<T>,
|
public: PublicKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A DH keypair that is authentic w.r.t. a [`identity::PublicKey`].
|
/// A DH keypair that is authentic w.r.t. a [`identity::PublicKey`].
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AuthenticKeypair<T: Zeroize> {
|
pub(crate) struct AuthenticKeypair {
|
||||||
pub(crate) keypair: Keypair<T>,
|
pub(crate) keypair: Keypair,
|
||||||
pub(crate) identity: KeypairIdentity,
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The associated public identity of a DH keypair.
|
/// The associated public identity of a DH keypair.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct KeypairIdentity {
|
pub(crate) struct KeypairIdentity {
|
||||||
/// The public identity key.
|
/// The public identity key.
|
||||||
pub public: identity::PublicKey,
|
pub(crate) public: identity::PublicKey,
|
||||||
/// The signature over the public DH key.
|
/// The signature over the public DH key.
|
||||||
pub signature: Option<Vec<u8>>,
|
pub(crate) signature: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Zeroize> Keypair<T> {
|
impl Keypair {
|
||||||
/// The public key of the DH keypair.
|
|
||||||
pub fn public(&self) -> &PublicKey<T> {
|
|
||||||
&self.public
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The secret key of the DH keypair.
|
/// The secret key of the DH keypair.
|
||||||
pub fn secret(&self) -> &SecretKey<T> {
|
pub(crate) fn secret(&self) -> &SecretKey {
|
||||||
&self.secret
|
&self.secret
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turn this DH keypair into a [`AuthenticKeypair`], i.e. a DH keypair that
|
/// Turn this DH keypair into a [`AuthenticKeypair`], i.e. a DH keypair that
|
||||||
/// is authentic w.r.t. the given identity keypair, by signing the DH public key.
|
/// is authentic w.r.t. the given identity keypair, by signing the DH public key.
|
||||||
pub fn into_authentic(self, id_keys: &identity::Keypair) -> Result<AuthenticKeypair<T>, Error>
|
pub(crate) fn into_authentic(
|
||||||
where
|
self,
|
||||||
T: AsRef<[u8]>,
|
id_keys: &identity::Keypair,
|
||||||
T: Protocol<T>,
|
) -> Result<AuthenticKeypair, Error> {
|
||||||
{
|
let sig = id_keys.sign(&[STATIC_KEY_DOMAIN.as_bytes(), self.public.as_ref()].concat())?;
|
||||||
let sig = T::sign(id_keys, &self.public)?;
|
|
||||||
|
|
||||||
let identity = KeypairIdentity {
|
let identity = KeypairIdentity {
|
||||||
public: id_keys.public(),
|
public: id_keys.public(),
|
||||||
signature: Some(sig),
|
signature: sig,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(AuthenticKeypair {
|
Ok(AuthenticKeypair {
|
||||||
@ -195,37 +101,59 @@ impl<T: Zeroize> Keypair<T> {
|
|||||||
identity,
|
identity,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An "empty" keypair as a starting state for DH computations in `snow`,
|
||||||
|
/// which get manipulated through the `snow::types::Dh` interface.
|
||||||
|
pub(crate) fn empty() -> Self {
|
||||||
|
Keypair {
|
||||||
|
secret: SecretKey([0u8; 32]),
|
||||||
|
public: PublicKey([0u8; 32]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new X25519 keypair.
|
||||||
|
pub(crate) fn new() -> Keypair {
|
||||||
|
let mut sk_bytes = [0u8; 32];
|
||||||
|
rand::thread_rng().fill(&mut sk_bytes);
|
||||||
|
let sk = SecretKey(sk_bytes); // Copy
|
||||||
|
sk_bytes.zeroize();
|
||||||
|
Self::from(sk)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// DH secret key.
|
/// DH secret key.
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Default)]
|
||||||
pub struct SecretKey<T: Zeroize>(T);
|
pub(crate) struct SecretKey([u8; 32]);
|
||||||
|
|
||||||
impl<T: Zeroize> Drop for SecretKey<T> {
|
impl Drop for SecretKey {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.0.zeroize()
|
self.0.zeroize()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsRef<[u8]> + Zeroize> AsRef<[u8]> for SecretKey<T> {
|
impl AsRef<[u8]> for SecretKey {
|
||||||
fn as_ref(&self) -> &[u8] {
|
fn as_ref(&self) -> &[u8] {
|
||||||
self.0.as_ref()
|
self.0.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// DH public key.
|
/// DH public key.
|
||||||
#[derive(Clone)]
|
#[derive(Clone, PartialEq, Default)]
|
||||||
pub struct PublicKey<T>(T);
|
pub(crate) struct PublicKey([u8; 32]);
|
||||||
|
|
||||||
impl<T: AsRef<[u8]>> PartialEq for PublicKey<T> {
|
impl PublicKey {
|
||||||
fn eq(&self, other: &PublicKey<T>) -> bool {
|
pub(crate) fn from_slice(slice: &[u8]) -> Result<Self, Error> {
|
||||||
self.as_ref() == other.as_ref()
|
if slice.len() != 32 {
|
||||||
|
return Err(Error::InvalidLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut key = [0u8; 32];
|
||||||
|
key.copy_from_slice(slice);
|
||||||
|
Ok(PublicKey(key))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsRef<[u8]>> Eq for PublicKey<T> {}
|
impl AsRef<[u8]> for PublicKey {
|
||||||
|
|
||||||
impl<T: AsRef<[u8]>> AsRef<[u8]> for PublicKey<T> {
|
|
||||||
fn as_ref(&self) -> &[u8] {
|
fn as_ref(&self) -> &[u8] {
|
||||||
self.0.as_ref()
|
self.0.as_ref()
|
||||||
}
|
}
|
||||||
@ -244,7 +172,7 @@ impl snow::resolvers::CryptoResolver for Resolver {
|
|||||||
|
|
||||||
fn resolve_dh(&self, choice: &snow::params::DHChoice) -> Option<Box<dyn snow::types::Dh>> {
|
fn resolve_dh(&self, choice: &snow::params::DHChoice) -> Option<Box<dyn snow::types::Dh>> {
|
||||||
if let snow::params::DHChoice::Curve25519 = choice {
|
if let snow::params::DHChoice::Curve25519 = choice {
|
||||||
Some(Box::new(Keypair::<x25519_spec::X25519Spec>::default()))
|
Some(Box::new(Keypair::empty()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -304,10 +232,67 @@ impl rand::CryptoRng for Rng {}
|
|||||||
|
|
||||||
impl snow::types::Random for Rng {}
|
impl snow::types::Random for Rng {}
|
||||||
|
|
||||||
|
impl Default for Keypair {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Promote a X25519 secret key into a keypair.
|
||||||
|
impl From<SecretKey> for Keypair {
|
||||||
|
fn from(secret: SecretKey) -> Keypair {
|
||||||
|
let public = PublicKey(x25519(secret.0, X25519_BASEPOINT_BYTES));
|
||||||
|
Keypair { secret, public }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
impl snow::types::Dh for Keypair {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"25519"
|
||||||
|
}
|
||||||
|
fn pub_len(&self) -> usize {
|
||||||
|
32
|
||||||
|
}
|
||||||
|
fn priv_len(&self) -> usize {
|
||||||
|
32
|
||||||
|
}
|
||||||
|
fn pubkey(&self) -> &[u8] {
|
||||||
|
self.public.as_ref()
|
||||||
|
}
|
||||||
|
fn privkey(&self) -> &[u8] {
|
||||||
|
self.secret.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(&mut self, sk: &[u8]) {
|
||||||
|
let mut secret = [0u8; 32];
|
||||||
|
secret.copy_from_slice(sk);
|
||||||
|
self.secret = SecretKey(secret); // Copy
|
||||||
|
self.public = PublicKey(x25519(secret, X25519_BASEPOINT_BYTES));
|
||||||
|
secret.zeroize();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate(&mut self, rng: &mut dyn snow::types::Random) {
|
||||||
|
let mut secret = [0u8; 32];
|
||||||
|
rng.fill_bytes(&mut secret);
|
||||||
|
self.secret = SecretKey(secret); // Copy
|
||||||
|
self.public = PublicKey(x25519(secret, X25519_BASEPOINT_BYTES));
|
||||||
|
secret.zeroize();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dh(&self, pk: &[u8], shared_secret: &mut [u8]) -> Result<(), snow::Error> {
|
||||||
|
let mut p = [0; 32];
|
||||||
|
p.copy_from_slice(&pk[..32]);
|
||||||
|
let ss = x25519(self.secret.0, p);
|
||||||
|
shared_secret[..32].copy_from_slice(&ss[..]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::X25519Spec;
|
use crate::protocol::PARAMS_XX;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -333,9 +318,9 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn xx_builder(prologue: &'static [u8]) -> snow::Builder<'static> {
|
fn xx_builder(prologue: &'static [u8]) -> snow::Builder<'static> {
|
||||||
X25519Spec::params_xx().into_builder(prologue, TEST_KEY.secret(), None)
|
noise_params_into_builder(PARAMS_XX.clone(), prologue, TEST_KEY.secret(), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hack to work around borrow-checker.
|
// Hack to work around borrow-checker.
|
||||||
static TEST_KEY: Lazy<Keypair<X25519Spec>> = Lazy::new(Keypair::<X25519Spec>::new);
|
static TEST_KEY: Lazy<Keypair> = Lazy::new(Keypair::new);
|
||||||
}
|
}
|
||||||
|
@ -1,292 +0,0 @@
|
|||||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
// copy of this software and associated documentation files (the "Software"),
|
|
||||||
// to deal in the Software without restriction, including without limitation
|
|
||||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
// and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
// Software is furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
||||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
// DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
//! Legacy Noise protocols based on X25519.
|
|
||||||
//!
|
|
||||||
//! **Note**: This set of protocols is not interoperable with other
|
|
||||||
//! libp2p implementations.
|
|
||||||
|
|
||||||
use crate::{Error, NoiseConfig, Protocol, ProtocolParams};
|
|
||||||
use curve25519_dalek::edwards::CompressedEdwardsY;
|
|
||||||
use libp2p_core::UpgradeInfo;
|
|
||||||
use libp2p_identity as identity;
|
|
||||||
use libp2p_identity::ed25519;
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use rand::Rng;
|
|
||||||
use sha2::{Digest, Sha512};
|
|
||||||
use x25519_dalek::{x25519, X25519_BASEPOINT_BYTES};
|
|
||||||
use zeroize::Zeroize;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
static PARAMS_IK: Lazy<ProtocolParams> = Lazy::new(|| {
|
|
||||||
"Noise_IK_25519_ChaChaPoly_SHA256"
|
|
||||||
.parse()
|
|
||||||
.map(ProtocolParams)
|
|
||||||
.expect("Invalid protocol name")
|
|
||||||
});
|
|
||||||
static PARAMS_IX: Lazy<ProtocolParams> = Lazy::new(|| {
|
|
||||||
"Noise_IX_25519_ChaChaPoly_SHA256"
|
|
||||||
.parse()
|
|
||||||
.map(ProtocolParams)
|
|
||||||
.expect("Invalid protocol name")
|
|
||||||
});
|
|
||||||
static PARAMS_XX: Lazy<ProtocolParams> = Lazy::new(|| {
|
|
||||||
"Noise_XX_25519_ChaChaPoly_SHA256"
|
|
||||||
.parse()
|
|
||||||
.map(ProtocolParams)
|
|
||||||
.expect("Invalid protocol name")
|
|
||||||
});
|
|
||||||
|
|
||||||
/// A X25519 key.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct X25519([u8; 32]);
|
|
||||||
|
|
||||||
impl AsRef<[u8]> for X25519 {
|
|
||||||
fn as_ref(&self) -> &[u8] {
|
|
||||||
self.0.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Zeroize for X25519 {
|
|
||||||
fn zeroize(&mut self) {
|
|
||||||
self.0.zeroize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UpgradeInfo for NoiseConfig<IX, X25519> {
|
|
||||||
type Info = &'static str;
|
|
||||||
type InfoIter = std::iter::Once<Self::Info>;
|
|
||||||
|
|
||||||
fn protocol_info(&self) -> Self::InfoIter {
|
|
||||||
std::iter::once("/noise/ix/25519/chachapoly/sha256/0.1.0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UpgradeInfo for NoiseConfig<XX, X25519> {
|
|
||||||
type Info = &'static str;
|
|
||||||
type InfoIter = std::iter::Once<Self::Info>;
|
|
||||||
|
|
||||||
fn protocol_info(&self) -> Self::InfoIter {
|
|
||||||
std::iter::once("/noise/xx/25519/chachapoly/sha256/0.1.0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R> UpgradeInfo for NoiseConfig<IK, X25519, R> {
|
|
||||||
type Info = &'static str;
|
|
||||||
type InfoIter = std::iter::Once<Self::Info>;
|
|
||||||
|
|
||||||
fn protocol_info(&self) -> Self::InfoIter {
|
|
||||||
std::iter::once("/noise/ik/25519/chachapoly/sha256/0.1.0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Legacy Noise protocol for X25519.
|
|
||||||
///
|
|
||||||
/// **Note**: This `Protocol` provides no configuration that
|
|
||||||
/// is interoperable with other libp2p implementations.
|
|
||||||
/// See [`crate::X25519Spec`] instead.
|
|
||||||
impl Protocol<X25519> for X25519 {
|
|
||||||
fn params_ik() -> ProtocolParams {
|
|
||||||
PARAMS_IK.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn params_ix() -> ProtocolParams {
|
|
||||||
PARAMS_IX.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn params_xx() -> ProtocolParams {
|
|
||||||
PARAMS_XX.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn public_from_bytes(bytes: &[u8]) -> Result<PublicKey<X25519>, Error> {
|
|
||||||
if bytes.len() != 32 {
|
|
||||||
return Err(Error::InvalidLength);
|
|
||||||
}
|
|
||||||
let mut pk = [0u8; 32];
|
|
||||||
pk.copy_from_slice(bytes);
|
|
||||||
Ok(PublicKey(X25519(pk)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(irrefutable_let_patterns)]
|
|
||||||
fn linked(id_pk: &identity::PublicKey, dh_pk: &PublicKey<X25519>) -> bool {
|
|
||||||
if let Ok(p) = identity::PublicKey::try_into_ed25519(id_pk.clone()) {
|
|
||||||
PublicKey::from_ed25519(&p).as_ref() == dh_pk.as_ref()
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Keypair<X25519> {
|
|
||||||
/// Create a new X25519 keypair.
|
|
||||||
pub fn new() -> Keypair<X25519> {
|
|
||||||
let mut sk_bytes = [0u8; 32];
|
|
||||||
rand::thread_rng().fill(&mut sk_bytes);
|
|
||||||
let sk = SecretKey(X25519(sk_bytes)); // Copy
|
|
||||||
sk_bytes.zeroize();
|
|
||||||
Self::from(sk)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an X25519 `Keypair` from an [`identity::Keypair`], if possible.
|
|
||||||
///
|
|
||||||
/// The returned keypair will be [associated with](KeypairIdentity) the
|
|
||||||
/// given identity keypair.
|
|
||||||
///
|
|
||||||
/// Returns `None` if the given identity keypair cannot be used as an X25519 keypair.
|
|
||||||
///
|
|
||||||
/// > **Note**: If the identity keypair is already used in the context
|
|
||||||
/// > of other cryptographic protocols outside of Noise, it should be preferred to
|
|
||||||
/// > create a new static X25519 keypair for use in the Noise protocol.
|
|
||||||
/// >
|
|
||||||
/// > See also:
|
|
||||||
/// >
|
|
||||||
/// > * [Noise: Static Key Reuse](http://www.noiseprotocol.org/noise.html#security-considerations)
|
|
||||||
#[allow(unreachable_patterns)]
|
|
||||||
pub fn from_identity(id_keys: &identity::Keypair) -> Option<AuthenticKeypair<X25519>> {
|
|
||||||
let ed25519_keypair = id_keys.clone().try_into_ed25519().ok()?;
|
|
||||||
let kp = Keypair::from(SecretKey::from_ed25519(&ed25519_keypair.secret()));
|
|
||||||
let id = KeypairIdentity {
|
|
||||||
public: id_keys.public(),
|
|
||||||
signature: None,
|
|
||||||
};
|
|
||||||
Some(AuthenticKeypair {
|
|
||||||
keypair: kp,
|
|
||||||
identity: id,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Keypair<X25519> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Promote a X25519 secret key into a keypair.
|
|
||||||
impl From<SecretKey<X25519>> for Keypair<X25519> {
|
|
||||||
fn from(secret: SecretKey<X25519>) -> Keypair<X25519> {
|
|
||||||
let public = PublicKey(X25519(x25519((secret.0).0, X25519_BASEPOINT_BYTES)));
|
|
||||||
Keypair { secret, public }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PublicKey<X25519> {
|
|
||||||
/// Construct a curve25519 public key from an Ed25519 public key.
|
|
||||||
pub fn from_ed25519(pk: &ed25519::PublicKey) -> Self {
|
|
||||||
PublicKey(X25519(
|
|
||||||
CompressedEdwardsY(pk.encode())
|
|
||||||
.decompress()
|
|
||||||
.expect("An Ed25519 public key is a valid point by construction.")
|
|
||||||
.to_montgomery()
|
|
||||||
.0,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SecretKey<X25519> {
|
|
||||||
/// Construct a X25519 secret key from a Ed25519 secret key.
|
|
||||||
///
|
|
||||||
/// > **Note**: If the Ed25519 secret key is already used in the context
|
|
||||||
/// > of other cryptographic protocols outside of Noise, it should be preferred
|
|
||||||
/// > to create a new keypair for use in the Noise protocol.
|
|
||||||
/// >
|
|
||||||
/// > See also:
|
|
||||||
/// >
|
|
||||||
/// > * [Noise: Static Key Reuse](http://www.noiseprotocol.org/noise.html#security-considerations)
|
|
||||||
/// > * [Ed25519 to Curve25519](https://libsodium.gitbook.io/doc/advanced/ed25519-curve25519)
|
|
||||||
pub fn from_ed25519(ed25519_sk: &ed25519::SecretKey) -> Self {
|
|
||||||
// An Ed25519 public key is derived off the left half of the SHA512 of the
|
|
||||||
// secret scalar, hence a matching conversion of the secret key must do
|
|
||||||
// the same to yield a Curve25519 keypair with the same public key.
|
|
||||||
// let ed25519_sk = ed25519::SecretKey::from(ed);
|
|
||||||
let mut curve25519_sk: [u8; 32] = [0; 32];
|
|
||||||
let hash = Sha512::digest(ed25519_sk.as_ref());
|
|
||||||
curve25519_sk.copy_from_slice(&hash[..32]);
|
|
||||||
let sk = SecretKey(X25519(curve25519_sk)); // Copy
|
|
||||||
curve25519_sk.zeroize();
|
|
||||||
sk
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
impl snow::types::Dh for Keypair<X25519> {
|
|
||||||
fn name(&self) -> &'static str {
|
|
||||||
"25519"
|
|
||||||
}
|
|
||||||
fn pub_len(&self) -> usize {
|
|
||||||
32
|
|
||||||
}
|
|
||||||
fn priv_len(&self) -> usize {
|
|
||||||
32
|
|
||||||
}
|
|
||||||
fn pubkey(&self) -> &[u8] {
|
|
||||||
self.public.as_ref()
|
|
||||||
}
|
|
||||||
fn privkey(&self) -> &[u8] {
|
|
||||||
self.secret.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set(&mut self, sk: &[u8]) {
|
|
||||||
let mut secret = [0u8; 32];
|
|
||||||
secret.copy_from_slice(sk);
|
|
||||||
self.secret = SecretKey(X25519(secret)); // Copy
|
|
||||||
self.public = PublicKey(X25519(x25519(secret, X25519_BASEPOINT_BYTES)));
|
|
||||||
secret.zeroize();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate(&mut self, rng: &mut dyn snow::types::Random) {
|
|
||||||
let mut secret = [0u8; 32];
|
|
||||||
rng.fill_bytes(&mut secret);
|
|
||||||
self.secret = SecretKey(X25519(secret)); // Copy
|
|
||||||
self.public = PublicKey(X25519(x25519(secret, X25519_BASEPOINT_BYTES)));
|
|
||||||
secret.zeroize();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dh(&self, pk: &[u8], shared_secret: &mut [u8]) -> Result<(), snow::Error> {
|
|
||||||
let mut p = [0; 32];
|
|
||||||
p.copy_from_slice(&pk[..32]);
|
|
||||||
let ss = x25519((self.secret.0).0, p);
|
|
||||||
shared_secret[..32].copy_from_slice(&ss[..]);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use libp2p_identity::ed25519;
|
|
||||||
use quickcheck::*;
|
|
||||||
|
|
||||||
// The x25519 public key obtained through ed25519 keypair conversion
|
|
||||||
// (and thus derived from the converted secret key) must match the x25519
|
|
||||||
// public key derived directly from the ed25519 public key.
|
|
||||||
#[test]
|
|
||||||
fn prop_public_ed25519_to_x25519_matches() {
|
|
||||||
fn prop() -> bool {
|
|
||||||
let ed25519 = ed25519::Keypair::generate();
|
|
||||||
let x25519 = Keypair::from(SecretKey::from_ed25519(&ed25519.secret()));
|
|
||||||
let x25519_public = PublicKey::from_ed25519(&ed25519.public());
|
|
||||||
x25519.public == x25519_public
|
|
||||||
}
|
|
||||||
|
|
||||||
quickcheck(prop as fn() -> _);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,200 +0,0 @@
|
|||||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
// copy of this software and associated documentation files (the "Software"),
|
|
||||||
// to deal in the Software without restriction, including without limitation
|
|
||||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
// and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
// Software is furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
||||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
// DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
//! [libp2p-noise-spec] compliant Noise protocols based on X25519.
|
|
||||||
//!
|
|
||||||
//! [libp2p-noise-spec]: https://github.com/libp2p/specs/tree/master/noise
|
|
||||||
|
|
||||||
use crate::{Error, NoiseConfig, Protocol, ProtocolParams};
|
|
||||||
use libp2p_core::UpgradeInfo;
|
|
||||||
use libp2p_identity as identity;
|
|
||||||
use rand::Rng;
|
|
||||||
use x25519_dalek::{x25519, X25519_BASEPOINT_BYTES};
|
|
||||||
use zeroize::Zeroize;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
/// Prefix of static key signatures for domain separation.
|
|
||||||
const STATIC_KEY_DOMAIN: &str = "noise-libp2p-static-key:";
|
|
||||||
|
|
||||||
/// A X25519 key.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct X25519Spec([u8; 32]);
|
|
||||||
|
|
||||||
impl AsRef<[u8]> for X25519Spec {
|
|
||||||
fn as_ref(&self) -> &[u8] {
|
|
||||||
self.0.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Zeroize for X25519Spec {
|
|
||||||
fn zeroize(&mut self) {
|
|
||||||
self.0.zeroize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Keypair<X25519Spec> {
|
|
||||||
/// An "empty" keypair as a starting state for DH computations in `snow`,
|
|
||||||
/// which get manipulated through the `snow::types::Dh` interface.
|
|
||||||
pub(super) fn default() -> Self {
|
|
||||||
Keypair {
|
|
||||||
secret: SecretKey(X25519Spec([0u8; 32])),
|
|
||||||
public: PublicKey(X25519Spec([0u8; 32])),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new X25519 keypair.
|
|
||||||
pub fn new() -> Keypair<X25519Spec> {
|
|
||||||
let mut sk_bytes = [0u8; 32];
|
|
||||||
rand::thread_rng().fill(&mut sk_bytes);
|
|
||||||
let sk = SecretKey(X25519Spec(sk_bytes)); // Copy
|
|
||||||
sk_bytes.zeroize();
|
|
||||||
Self::from(sk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Keypair<X25519Spec> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Promote a X25519 secret key into a keypair.
|
|
||||||
impl From<SecretKey<X25519Spec>> for Keypair<X25519Spec> {
|
|
||||||
fn from(secret: SecretKey<X25519Spec>) -> Keypair<X25519Spec> {
|
|
||||||
let public = PublicKey(X25519Spec(x25519((secret.0).0, X25519_BASEPOINT_BYTES)));
|
|
||||||
Keypair { secret, public }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UpgradeInfo for NoiseConfig<XX, X25519Spec> {
|
|
||||||
type Info = &'static str;
|
|
||||||
type InfoIter = std::iter::Once<Self::Info>;
|
|
||||||
|
|
||||||
fn protocol_info(&self) -> Self::InfoIter {
|
|
||||||
std::iter::once("/noise")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// **Note**: This is not currentlyy a standardised upgrade.
|
|
||||||
|
|
||||||
impl UpgradeInfo for NoiseConfig<IX, X25519Spec> {
|
|
||||||
type Info = &'static str;
|
|
||||||
type InfoIter = std::iter::Once<Self::Info>;
|
|
||||||
|
|
||||||
fn protocol_info(&self) -> Self::InfoIter {
|
|
||||||
std::iter::once("/noise/ix/25519/chachapoly/sha256/0.1.0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// **Note**: This is not currently a standardised upgrade.
|
|
||||||
|
|
||||||
impl<R> UpgradeInfo for NoiseConfig<IK, X25519Spec, R> {
|
|
||||||
type Info = &'static str;
|
|
||||||
type InfoIter = std::iter::Once<Self::Info>;
|
|
||||||
|
|
||||||
fn protocol_info(&self) -> Self::InfoIter {
|
|
||||||
std::iter::once("/noise/ik/25519/chachapoly/sha256/0.1.0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Noise protocols for X25519 with libp2p-spec compliant signatures.
|
|
||||||
///
|
|
||||||
/// **Note**: Only the XX handshake pattern is currently guaranteed to be
|
|
||||||
/// interoperable with other libp2p implementations.
|
|
||||||
impl Protocol<X25519Spec> for X25519Spec {
|
|
||||||
fn params_ik() -> ProtocolParams {
|
|
||||||
x25519::X25519::params_ik()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn params_ix() -> ProtocolParams {
|
|
||||||
x25519::X25519::params_ix()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn params_xx() -> ProtocolParams {
|
|
||||||
x25519::X25519::params_xx()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn public_from_bytes(bytes: &[u8]) -> Result<PublicKey<X25519Spec>, Error> {
|
|
||||||
if bytes.len() != 32 {
|
|
||||||
return Err(Error::InvalidLength);
|
|
||||||
}
|
|
||||||
let mut pk = [0u8; 32];
|
|
||||||
pk.copy_from_slice(bytes);
|
|
||||||
Ok(PublicKey(X25519Spec(pk)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify(
|
|
||||||
id_pk: &identity::PublicKey,
|
|
||||||
dh_pk: &PublicKey<X25519Spec>,
|
|
||||||
sig: &Option<Vec<u8>>,
|
|
||||||
) -> bool {
|
|
||||||
sig.as_ref().map_or(false, |s| {
|
|
||||||
id_pk.verify(&[STATIC_KEY_DOMAIN.as_bytes(), dh_pk.as_ref()].concat(), s)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sign(id_keys: &identity::Keypair, dh_pk: &PublicKey<X25519Spec>) -> Result<Vec<u8>, Error> {
|
|
||||||
Ok(id_keys.sign(&[STATIC_KEY_DOMAIN.as_bytes(), dh_pk.as_ref()].concat())?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
impl snow::types::Dh for Keypair<X25519Spec> {
|
|
||||||
fn name(&self) -> &'static str {
|
|
||||||
"25519"
|
|
||||||
}
|
|
||||||
fn pub_len(&self) -> usize {
|
|
||||||
32
|
|
||||||
}
|
|
||||||
fn priv_len(&self) -> usize {
|
|
||||||
32
|
|
||||||
}
|
|
||||||
fn pubkey(&self) -> &[u8] {
|
|
||||||
self.public.as_ref()
|
|
||||||
}
|
|
||||||
fn privkey(&self) -> &[u8] {
|
|
||||||
self.secret.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set(&mut self, sk: &[u8]) {
|
|
||||||
let mut secret = [0u8; 32];
|
|
||||||
secret.copy_from_slice(sk);
|
|
||||||
self.secret = SecretKey(X25519Spec(secret)); // Copy
|
|
||||||
self.public = PublicKey(X25519Spec(x25519(secret, X25519_BASEPOINT_BYTES)));
|
|
||||||
secret.zeroize();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate(&mut self, rng: &mut dyn snow::types::Random) {
|
|
||||||
let mut secret = [0u8; 32];
|
|
||||||
rng.fill_bytes(&mut secret);
|
|
||||||
self.secret = SecretKey(X25519Spec(secret)); // Copy
|
|
||||||
self.public = PublicKey(X25519Spec(x25519(secret, X25519_BASEPOINT_BYTES)));
|
|
||||||
secret.zeroize();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dh(&self, pk: &[u8], shared_secret: &mut [u8]) -> Result<(), snow::Error> {
|
|
||||||
let mut p = [0; 32];
|
|
||||||
p.copy_from_slice(&pk[..32]);
|
|
||||||
let ss = x25519((self.secret.0).0, p);
|
|
||||||
shared_secret[..32].copy_from_slice(&ss[..]);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user