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))
|
||||
/// .upgrade(upgrade::Version::V1)
|
||||
/// .authenticate(
|
||||
/// noise::NoiseAuthenticated::xx(&id_keys)
|
||||
/// noise::Config::new(&id_keys)
|
||||
/// .expect("Signing libp2p-noise static DH keypair failed."),
|
||||
/// )
|
||||
/// .multiplex(mplex::MplexConfig::new())
|
||||
|
@ -92,17 +92,18 @@
|
||||
//! An example of initialising a gossipsub compatible swarm:
|
||||
//!
|
||||
//! ```
|
||||
//! use libp2p_gossipsub::Event;
|
||||
//! use libp2p_core::{identity::Keypair,transport::{Transport, MemoryTransport}, Multiaddr};
|
||||
//! use libp2p_gossipsub::MessageAuthenticity;
|
||||
//! let local_key = Keypair::generate_ed25519();
|
||||
//! let local_peer_id = libp2p_core::PeerId::from(local_key.public());
|
||||
//! # use libp2p_gossipsub::Event;
|
||||
//! # use libp2p_core::{transport::{Transport, MemoryTransport}, Multiaddr};
|
||||
//! # use libp2p_gossipsub::MessageAuthenticity;
|
||||
//! # use libp2p_identity as identity;
|
||||
//! 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
|
||||
//! // This is test transport (memory).
|
||||
//! let transport = MemoryTransport::default()
|
||||
//! .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())
|
||||
//! .boxed();
|
||||
//!
|
||||
@ -123,11 +124,11 @@
|
||||
//! // subscribe to the topic
|
||||
//! gossipsub.subscribe(&topic);
|
||||
//! // create the swarm (use an executor in a real example)
|
||||
//! libp2p_swarm::Swarm::without_executor(
|
||||
//! libp2p_swarm::SwarmBuilder::without_executor(
|
||||
//! transport,
|
||||
//! gossipsub,
|
||||
//! local_peer_id,
|
||||
//! )
|
||||
//! ).build()
|
||||
//! };
|
||||
//!
|
||||
//! // Listen on a memory transport.
|
||||
|
@ -1,8 +1,11 @@
|
||||
## 0.42.0 - unreleased
|
||||
## 0.43.0 - unreleased
|
||||
|
||||
- Raise MSRV to 1.65.
|
||||
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
|
||||
|
||||
## 0.42.2
|
||||
|
@ -22,7 +22,7 @@
|
||||
//! Noise protocol messages in form of [`NoiseFramed`].
|
||||
|
||||
use crate::io::Output;
|
||||
use crate::{Error, Protocol, PublicKey};
|
||||
use crate::{protocol::PublicKey, Error};
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use futures::prelude::*;
|
||||
use futures::ready;
|
||||
@ -89,15 +89,15 @@ impl<T> NoiseFramed<T, snow::HandshakeState> {
|
||||
/// transitioning to transport mode because the handshake is incomplete,
|
||||
/// an error is returned. Similarly if the remote's static DH key, if
|
||||
/// present, cannot be parsed.
|
||||
pub(crate) fn into_transport<C>(self) -> Result<(Option<PublicKey<C>>, Output<T>), Error>
|
||||
where
|
||||
C: Protocol<C> + AsRef<[u8]>,
|
||||
{
|
||||
let dh_remote_pubkey = self
|
||||
.session
|
||||
.get_remote_static()
|
||||
.map(C::public_from_bytes)
|
||||
.transpose()?;
|
||||
pub(crate) fn into_transport(self) -> Result<(PublicKey, Output<T>), Error> {
|
||||
let dh_remote_pubkey = self.session.get_remote_static().ok_or_else(|| {
|
||||
Error::Io(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"expect key to always be present at end of XX session",
|
||||
))
|
||||
})?;
|
||||
|
||||
let dh_remote_pubkey = PublicKey::from_slice(dh_remote_pubkey)?;
|
||||
|
||||
let io = NoiseFramed {
|
||||
session: self.session.into_transport_mode()?,
|
||||
|
@ -27,46 +27,14 @@ mod proto {
|
||||
}
|
||||
|
||||
use crate::io::{framed::NoiseFramed, Output};
|
||||
use crate::protocol::{KeypairIdentity, Protocol, PublicKey};
|
||||
|
||||
use crate::protocol::{KeypairIdentity, STATIC_KEY_DOMAIN};
|
||||
use crate::Error;
|
||||
use crate::LegacyConfig;
|
||||
use bytes::Bytes;
|
||||
use futures::prelude::*;
|
||||
use libp2p_identity as identity;
|
||||
use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer};
|
||||
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
|
||||
|
||||
@ -81,8 +49,6 @@ pub(crate) struct State<T> {
|
||||
dh_remote_pubkey_sig: Option<Vec<u8>>,
|
||||
/// The known or received public identity key of the remote, if any.
|
||||
id_remote_pubkey: Option<identity::PublicKey>,
|
||||
/// Legacy configuration parameters.
|
||||
legacy: LegacyConfig,
|
||||
}
|
||||
|
||||
impl<T> State<T> {
|
||||
@ -97,14 +63,12 @@ impl<T> State<T> {
|
||||
session: snow::HandshakeState,
|
||||
identity: KeypairIdentity,
|
||||
expected_remote_key: Option<identity::PublicKey>,
|
||||
legacy: LegacyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
identity,
|
||||
io: NoiseFramed::new(io, session),
|
||||
dh_remote_pubkey_sig: None,
|
||||
id_remote_pubkey: expected_remote_key,
|
||||
legacy,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -112,23 +76,22 @@ impl<T> State<T> {
|
||||
impl<T> State<T> {
|
||||
/// Finish a handshake, yielding the established remote identity and the
|
||||
/// [`Output`] for communicating on the encrypted channel.
|
||||
pub(crate) fn finish<C>(self) -> Result<(RemoteIdentity<C>, Output<T>), Error>
|
||||
where
|
||||
C: Protocol<C> + AsRef<[u8]>,
|
||||
{
|
||||
pub(crate) fn finish(self) -> Result<(identity::PublicKey, Output<T>), Error> {
|
||||
let (pubkey, io) = self.io.into_transport()?;
|
||||
let remote = match (self.id_remote_pubkey, pubkey) {
|
||||
(_, None) => RemoteIdentity::Unknown,
|
||||
(None, Some(dh_pk)) => RemoteIdentity::StaticDhKey(dh_pk),
|
||||
(Some(id_pk), Some(dh_pk)) => {
|
||||
if C::verify(&id_pk, &dh_pk, &self.dh_remote_pubkey_sig) {
|
||||
RemoteIdentity::IdentityKey(id_pk)
|
||||
} else {
|
||||
return Err(Error::BadSignature);
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok((remote, io))
|
||||
|
||||
let id_pk = self
|
||||
.id_remote_pubkey
|
||||
.ok_or_else(|| Error::AuthenticationFailed)?;
|
||||
|
||||
let is_valid_signature = self.dh_remote_pubkey_sig.as_ref().map_or(false, |s| {
|
||||
id_pk.verify(&[STATIC_KEY_DOMAIN.as_bytes(), pubkey.as_ref()].concat(), s)
|
||||
});
|
||||
|
||||
if !is_valid_signature {
|
||||
return Err(Error::BadSignature);
|
||||
}
|
||||
|
||||
Ok((id_pk, io))
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,60 +133,16 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A future for receiving a Noise handshake message with a payload
|
||||
/// 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.
|
||||
/// A future for receiving a Noise handshake message with a payload identifying the remote.
|
||||
pub(crate) async fn recv_identity<T>(state: &mut State<T>) -> Result<(), Error>
|
||||
where
|
||||
T: AsyncRead + Unpin,
|
||||
{
|
||||
let msg = recv(state).await?;
|
||||
|
||||
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 {
|
||||
// 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);
|
||||
}
|
||||
state.id_remote_pubkey = Some(identity::PublicKey::try_decode_protobuf(&pb.identity_key)?);
|
||||
|
||||
if !pb.identity_sig.is_empty() {
|
||||
state.dh_remote_pubkey_sig = Some(pb.identity_sig);
|
||||
@ -242,43 +161,9 @@ where
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Some(ref sig) = state.identity.signature {
|
||||
pb.identity_sig = sig.clone()
|
||||
}
|
||||
pb.identity_sig = state.identity.signature.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);
|
||||
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 msg = Vec::with_capacity(pb.get_size());
|
||||
|
||||
let mut writer = Writer::new(&mut msg);
|
||||
pb.write_message(&mut writer).expect("Encoding to succeed");
|
||||
|
@ -59,110 +59,81 @@
|
||||
mod io;
|
||||
mod protocol;
|
||||
|
||||
pub use io::handshake::RemoteIdentity;
|
||||
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::io::handshake;
|
||||
use futures::future::BoxFuture;
|
||||
use crate::protocol::{noise_params_into_builder, AuthenticKeypair, Keypair, PARAMS_XX};
|
||||
use futures::prelude::*;
|
||||
use libp2p_core::{InboundUpgrade, OutboundUpgrade, UpgradeInfo};
|
||||
use libp2p_identity as identity;
|
||||
use libp2p_identity::PeerId;
|
||||
use snow::params::NoiseParams;
|
||||
use std::fmt;
|
||||
use std::fmt::Formatter;
|
||||
use std::pin::Pin;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
/// The configuration for the noise handshake.
|
||||
#[derive(Clone)]
|
||||
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 {
|
||||
/// Construct a new configuration for the noise handshake using the XX handshake pattern.
|
||||
|
||||
pub fn new(identity: &identity::Keypair) -> Result<Self, Error> {
|
||||
Ok(Config {
|
||||
inner: NoiseAuthenticated::xx(identity)?,
|
||||
let noise_keys = Keypair::new().into_authentic(identity)?;
|
||||
|
||||
Ok(Self {
|
||||
dh_keys: noise_keys,
|
||||
params: PARAMS_XX.clone(),
|
||||
prologue: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the noise prologue.
|
||||
|
||||
pub fn with_prologue(mut self, prologue: Vec<u8>) -> Self {
|
||||
self.inner.config.prologue = prologue;
|
||||
self.prologue = prologue;
|
||||
|
||||
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 {
|
||||
@ -182,8 +153,19 @@ where
|
||||
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 {
|
||||
self.inner.upgrade_inbound(socket, info)
|
||||
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?;
|
||||
|
||||
let (pk, io) = state.finish()?;
|
||||
|
||||
Ok((pk.to_peer_id(), io))
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,185 +177,19 @@ where
|
||||
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 {
|
||||
self.inner.upgrade_outbound(socket, info)
|
||||
}
|
||||
}
|
||||
fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future {
|
||||
async move {
|
||||
let mut state = self.into_initiator(socket)?;
|
||||
|
||||
/// The protocol upgrade configuration.
|
||||
#[deprecated(
|
||||
note = "Use `libp2p_noise::Config` instead. All other handshake patterns are deprecated and will be removed."
|
||||
)]
|
||||
#[derive(Clone)]
|
||||
pub struct NoiseConfig<P, C: Zeroize, R = ()> {
|
||||
dh_keys: AuthenticKeypair<C>,
|
||||
params: ProtocolParams,
|
||||
handshake::send_empty(&mut state).await?;
|
||||
handshake::recv_identity(&mut state).await?;
|
||||
handshake::send_identity(&mut state).await?;
|
||||
|
||||
legacy: LegacyConfig,
|
||||
remote: R,
|
||||
_marker: std::marker::PhantomData<P>,
|
||||
let (pk, io) = state.finish()?;
|
||||
|
||||
/// 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<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(),
|
||||
Ok((pk.to_peer_id(), io))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
@ -421,328 +237,3 @@ impl From<quick_protobuf::Error> for Error {
|
||||
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.
|
||||
|
||||
pub(crate) mod x25519;
|
||||
pub(crate) mod x25519_spec;
|
||||
use crate::Error;
|
||||
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;
|
||||
|
||||
/// The parameters of a Noise protocol, consisting of a choice
|
||||
/// for a handshake pattern as well as DH, cipher and hash functions.
|
||||
#[derive(Clone)]
|
||||
pub struct ProtocolParams(snow::params::NoiseParams);
|
||||
/// Prefix of static key signatures for domain separation.
|
||||
pub(crate) const STATIC_KEY_DOMAIN: &str = "noise-libp2p-static-key:";
|
||||
|
||||
impl ProtocolParams {
|
||||
pub(crate) fn into_builder<'b, C>(
|
||||
self,
|
||||
prologue: &'b [u8],
|
||||
private_key: &'b SecretKey<C>,
|
||||
remote_public_key: Option<&'b PublicKey<C>>,
|
||||
) -> snow::Builder<'b>
|
||||
where
|
||||
C: Zeroize + AsRef<[u8]> + Protocol<C>,
|
||||
{
|
||||
let mut builder = snow::Builder::with_resolver(self.0, Box::new(Resolver))
|
||||
.prologue(prologue.as_ref())
|
||||
.local_private_key(private_key.as_ref());
|
||||
pub(crate) static PARAMS_XX: Lazy<NoiseParams> = Lazy::new(|| {
|
||||
"Noise_XX_25519_ChaChaPoly_SHA256"
|
||||
.parse()
|
||||
.expect("Invalid protocol name")
|
||||
});
|
||||
|
||||
if let Some(remote_public_key) = remote_public_key {
|
||||
builder = builder.remote_public_key(remote_public_key.as_ref());
|
||||
}
|
||||
pub(crate) fn noise_params_into_builder<'b>(
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
if let Some(remote_public_key) = remote_public_key {
|
||||
builder = builder.remote_public_key(remote_public_key.as_ref());
|
||||
}
|
||||
|
||||
/// Verifies that a given static DH public key is authentic w.r.t. a
|
||||
/// 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())?)
|
||||
}
|
||||
builder
|
||||
}
|
||||
|
||||
/// DH keypair.
|
||||
#[derive(Clone)]
|
||||
pub struct Keypair<T: Zeroize> {
|
||||
secret: SecretKey<T>,
|
||||
public: PublicKey<T>,
|
||||
pub(crate) struct Keypair {
|
||||
secret: SecretKey,
|
||||
public: PublicKey,
|
||||
}
|
||||
|
||||
/// A DH keypair that is authentic w.r.t. a [`identity::PublicKey`].
|
||||
#[derive(Clone)]
|
||||
pub struct AuthenticKeypair<T: Zeroize> {
|
||||
pub(crate) keypair: Keypair<T>,
|
||||
pub(crate) struct AuthenticKeypair {
|
||||
pub(crate) keypair: Keypair,
|
||||
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.
|
||||
#[derive(Clone)]
|
||||
pub struct KeypairIdentity {
|
||||
pub(crate) struct KeypairIdentity {
|
||||
/// The public identity key.
|
||||
pub public: identity::PublicKey,
|
||||
pub(crate) public: identity::PublicKey,
|
||||
/// The signature over the public DH key.
|
||||
pub signature: Option<Vec<u8>>,
|
||||
pub(crate) signature: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<T: Zeroize> Keypair<T> {
|
||||
/// The public key of the DH keypair.
|
||||
pub fn public(&self) -> &PublicKey<T> {
|
||||
&self.public
|
||||
}
|
||||
|
||||
impl Keypair {
|
||||
/// The secret key of the DH keypair.
|
||||
pub fn secret(&self) -> &SecretKey<T> {
|
||||
pub(crate) fn secret(&self) -> &SecretKey {
|
||||
&self.secret
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn into_authentic(self, id_keys: &identity::Keypair) -> Result<AuthenticKeypair<T>, Error>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
T: Protocol<T>,
|
||||
{
|
||||
let sig = T::sign(id_keys, &self.public)?;
|
||||
pub(crate) fn into_authentic(
|
||||
self,
|
||||
id_keys: &identity::Keypair,
|
||||
) -> Result<AuthenticKeypair, Error> {
|
||||
let sig = id_keys.sign(&[STATIC_KEY_DOMAIN.as_bytes(), self.public.as_ref()].concat())?;
|
||||
|
||||
let identity = KeypairIdentity {
|
||||
public: id_keys.public(),
|
||||
signature: Some(sig),
|
||||
signature: sig,
|
||||
};
|
||||
|
||||
Ok(AuthenticKeypair {
|
||||
@ -195,37 +101,59 @@ impl<T: Zeroize> Keypair<T> {
|
||||
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.
|
||||
#[derive(Clone)]
|
||||
pub struct SecretKey<T: Zeroize>(T);
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct SecretKey([u8; 32]);
|
||||
|
||||
impl<T: Zeroize> Drop for SecretKey<T> {
|
||||
impl Drop for SecretKey {
|
||||
fn drop(&mut self) {
|
||||
self.0.zeroize()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]> + Zeroize> AsRef<[u8]> for SecretKey<T> {
|
||||
impl AsRef<[u8]> for SecretKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// DH public key.
|
||||
#[derive(Clone)]
|
||||
pub struct PublicKey<T>(T);
|
||||
#[derive(Clone, PartialEq, Default)]
|
||||
pub(crate) struct PublicKey([u8; 32]);
|
||||
|
||||
impl<T: AsRef<[u8]>> PartialEq for PublicKey<T> {
|
||||
fn eq(&self, other: &PublicKey<T>) -> bool {
|
||||
self.as_ref() == other.as_ref()
|
||||
impl PublicKey {
|
||||
pub(crate) fn from_slice(slice: &[u8]) -> Result<Self, Error> {
|
||||
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<T: AsRef<[u8]>> AsRef<[u8]> for PublicKey<T> {
|
||||
impl AsRef<[u8]> for PublicKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
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>> {
|
||||
if let snow::params::DHChoice::Curve25519 = choice {
|
||||
Some(Box::new(Keypair::<x25519_spec::X25519Spec>::default()))
|
||||
Some(Box::new(Keypair::empty()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -304,10 +232,67 @@ impl rand::CryptoRng 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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::X25519Spec;
|
||||
use crate::protocol::PARAMS_XX;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
#[test]
|
||||
@ -333,9 +318,9 @@ mod tests {
|
||||
}
|
||||
|
||||
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.
|
||||
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