Consolidate keypairs in core. (#972)

* Consolidate keypairs in core.

Introduce the concept of a node's identity keypair in libp2p-core,
instead of only the public key:

  * New module: libp2p_core::identity with submodules for the currently
    supported key types. An identity::Keypair and identity::PublicKey
    support the creation and verification of signatures. The public key
    supports encoding/decoding according to the libp2p specs.

  * The secio protocol is simplified as a result of moving code to libp2p-core.

  * The noise protocol is slightly simplified by consolidating ed25519
    keypairs in libp2p-core and using x25519-dalek for DH. Furthermore,
    Ed25519 to X25519 keypair conversion is now complete and tested.

Generalise over the DH keys in the noise protocol.

Generalise over the DH keys and thus DH parameter in handshake patterns
of the Noise protocol, such that it is easy to support other DH schemes
in the future, e.g. X448.

* Address new review comments.
This commit is contained in:
Roman Borschel
2019-03-11 13:42:53 +01:00
committed by GitHub
parent 26df15641c
commit 2c66f82b11
37 changed files with 1742 additions and 1020 deletions

View File

@ -14,11 +14,15 @@ lazy_static = "1.2"
libp2p-core = { version = "0.4.0", path = "../../core" }
log = "0.4"
rand = "0.6.5"
snow = { version = "0.5.0-alpha1", default-features = false, features = ["ring-resolver"] }
ring = { version = "0.14", features = ["use_heap"], default-features = false }
snow = { version = "0.5.1", features = ["ring-resolver"], default-features = false }
tokio-io = "0.1"
x25519-dalek = "0.5"
zeroize = "0.5"
[dev-dependencies]
env_logger = "0.6"
libp2p-tcp = { version = "0.4.0", path = "../../transports/tcp" }
quickcheck = "0.8"
tokio = "0.1"
sodiumoxide = "0.2"

View File

@ -18,7 +18,7 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
use crate::{NoiseError, keys::{PublicKey, Curve25519}, util::to_array};
use crate::{NoiseError, Protocol, PublicKey};
use futures::Poll;
use log::{debug, trace};
use snow;
@ -52,7 +52,7 @@ impl Buffer {
}
}
/// A type used during handshake phase, exchanging key material with the remote.
/// A type used during the handshake phase, exchanging key material with the remote.
pub(super) struct Handshake<T>(NoiseOutput<T>);
impl<T> Handshake<T> {
@ -79,14 +79,16 @@ impl<T: AsyncRead + AsyncWrite> Handshake<T> {
/// Finish the handshake.
///
/// This turns the noise session into handshake mode and returns the remote's static
/// This turns the noise session into transport mode and returns the remote's static
/// public key as well as the established session for further communication.
pub(super) fn finish(self) -> Result<(PublicKey<Curve25519>, NoiseOutput<T>), NoiseError> {
pub(super) fn finish<C>(self) -> Result<(PublicKey<C>, NoiseOutput<T>), NoiseError>
where
C: Protocol<C>
{
let s = self.0.session.into_transport_mode()?;
let p = s.get_remote_static()
.ok_or(NoiseError::InvalidKey)
.and_then(to_array)
.map(PublicKey::new)?;
.and_then(C::public_from_bytes)?;
Ok((p, NoiseOutput { session: s, .. self.0 }))
}
}

View File

@ -1,173 +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.
use crate::NoiseError;
use curve25519_dalek::{
constants::{X25519_BASEPOINT, ED25519_BASEPOINT_POINT},
edwards::CompressedEdwardsY,
montgomery::MontgomeryPoint,
scalar::Scalar
};
#[derive(Debug, Clone)]
pub enum Curve25519 {}
#[derive(Debug, Clone)]
pub enum Ed25519 {}
/// ECC public key.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PublicKey<T> {
bytes: [u8; 32],
_marker: std::marker::PhantomData<T>
}
impl<T> PublicKey<T> {
pub(crate) fn new(bytes: [u8; 32]) -> Self {
PublicKey { bytes, _marker: std::marker::PhantomData }
}
}
impl PublicKey<Ed25519> {
/// Attempt to construct an Ed25519 public key from a `libp2p_core::PublicKey`.
pub fn from_core(key: &libp2p_core::PublicKey) -> Result<PublicKey<Ed25519>, NoiseError> {
if let libp2p_core::PublicKey::Ed25519(k) = key {
if k.len() == 32 {
let cp = CompressedEdwardsY::from_slice(k);
cp.decompress().ok_or(NoiseError::InvalidKey)?;
return Ok(PublicKey::new(cp.0))
}
}
Err(NoiseError::InvalidKey)
}
/// Convert this Edwards 25519 public key into a Curve 25519 public key.
pub fn into_curve_25519(self) -> PublicKey<Curve25519> {
let m = CompressedEdwardsY(self.bytes)
.decompress()
.expect("Constructing a PublicKey<Ed25519> ensures this is a valid y-coordinate.")
.to_montgomery();
PublicKey::new(m.0)
}
}
impl<T> AsRef<[u8]> for PublicKey<T> {
fn as_ref(&self) -> &[u8] {
&self.bytes
}
}
/// ECC secret key.
#[derive(Clone)]
pub struct SecretKey {
scalar: Scalar
}
impl SecretKey {
pub(crate) fn new(bytes: [u8; 32]) -> Self {
SecretKey {
scalar: Scalar::from_bytes_mod_order(bytes)
}
}
}
impl SecretKey {
/// Compute the public key from this secret key.
///
/// Performs scalar multiplication with Curve25519's base point.
pub fn public(&self) -> PublicKey<Curve25519> {
PublicKey::new(self.x25519(&X25519_BASEPOINT).0)
}
/// Elliptic-Curve Diffie-Hellman key agreement.
///
/// Performs scalar multiplication with the given public key.
pub fn ecdh(&self, pk: &PublicKey<Curve25519>) -> [u8; 32] {
self.x25519(&MontgomeryPoint(pk.bytes)).0
}
/// The actual scalar multiplication with a u-coordinate of Curve25519.
fn x25519(&self, p: &MontgomeryPoint) -> MontgomeryPoint {
let mut s = self.scalar.to_bytes();
// Cf. RFC 7748 section 5 (page 7)
s[0] &= 248;
s[31] &= 127;
s[31] |= 64;
Scalar::from_bits(s) * p
}
}
impl AsRef<[u8]> for SecretKey {
fn as_ref(&self) -> &[u8] {
self.scalar.as_bytes()
}
}
/// ECC secret and public key.
#[derive(Clone)]
pub struct Keypair<T> {
secret: SecretKey,
public: PublicKey<T>
}
impl<T> Keypair<T> {
/// Create a new keypair.
pub fn new(s: SecretKey, p: PublicKey<T>) -> Self {
Keypair { secret: s, public: p }
}
/// Access this keypair's secret key.
pub fn secret(&self) -> &SecretKey {
&self.secret
}
/// Access this keypair's public key.
pub fn public(&self) -> &PublicKey<T> {
&self.public
}
}
impl<T> Into<(SecretKey, PublicKey<T>)> for Keypair<T> {
fn into(self) -> (SecretKey, PublicKey<T>) {
(self.secret, self.public)
}
}
impl Keypair<Curve25519> {
/// Create a fresh Curve25519 keypair.
pub fn gen_curve25519() -> Self {
let secret = SecretKey {
scalar: Scalar::random(&mut rand::thread_rng())
};
let public = secret.public();
Keypair { secret, public }
}
}
impl Keypair<Ed25519> {
/// Create a fresh Ed25519 keypair.
pub fn gen_ed25519() -> Self {
let scalar = Scalar::random(&mut rand::thread_rng());
let public = PublicKey::new((scalar * ED25519_BASEPOINT_POINT).compress().0);
let secret = SecretKey { scalar };
Keypair { secret, public }
}
}

View File

@ -21,7 +21,8 @@
//! [Noise protocol framework][noise] support for libp2p.
//!
//! This crate provides `libp2p_core::InboundUpgrade` and `libp2p_core::OutboundUpgrade`
//! implementations for various noise handshake patterns, currently IK, IX, and XX.
//! implementations for various noise handshake patterns (currently IK, IX, and XX)
//! over a particular choice of DH key agreement (currently only X25519).
//!
//! All upgrades produce as output a pair, consisting of the remote's static public key
//! and a `NoiseOutput` which represents the established cryptographic session with the
@ -34,11 +35,11 @@
//! ```
//! use libp2p_core::Transport;
//! use libp2p_tcp::TcpConfig;
//! use libp2p_noise::{Keypair, NoiseConfig};
//! use libp2p_noise::{Keypair, X25519, NoiseConfig};
//!
//! # fn main() {
//! let keypair = Keypair::gen_curve25519();
//! let transport = TcpConfig::new().with_upgrade(NoiseConfig::xx(keypair));
//! let keys = Keypair::<X25519>::new();
//! let transport = TcpConfig::new().with_upgrade(NoiseConfig::xx(keys));
//! // ...
//! # }
//! ```
@ -47,96 +48,71 @@
mod error;
mod io;
mod keys;
mod util;
mod protocol;
pub mod rt1;
pub mod rt15;
pub use error::NoiseError;
pub use io::NoiseOutput;
pub use keys::{Curve25519, PublicKey, SecretKey, Keypair};
pub use protocol::{Keypair, PublicKey, Protocol, ProtocolParams, IX, IK, XX};
pub use protocol::x25519::X25519;
use libp2p_core::{UpgradeInfo, InboundUpgrade, OutboundUpgrade};
use lazy_static::lazy_static;
use snow;
use tokio_io::{AsyncRead, AsyncWrite};
use util::Resolver;
lazy_static! {
static ref PARAMS_IK: snow::params::NoiseParams = "Noise_IK_25519_ChaChaPoly_SHA256"
.parse()
.expect("valid pattern");
static ref PARAMS_IX: snow::params::NoiseParams = "Noise_IX_25519_ChaChaPoly_SHA256"
.parse()
.expect("valid pattern");
static ref PARAMS_XX: snow::params::NoiseParams = "Noise_XX_25519_ChaChaPoly_SHA256"
.parse()
.expect("valid pattern");
}
#[derive(Debug, Clone)]
pub enum IK {}
#[derive(Debug, Clone)]
pub enum IX {}
#[derive(Debug, Clone)]
pub enum XX {}
use zeroize::Zeroize;
/// The protocol upgrade configuration.
#[derive(Clone)]
pub struct NoiseConfig<P, R = ()> {
keypair: Keypair<Curve25519>,
params: snow::params::NoiseParams,
pub struct NoiseConfig<P, C: Zeroize, R = ()> {
keys: Keypair<C>,
params: ProtocolParams,
remote: R,
_marker: std::marker::PhantomData<P>
}
impl NoiseConfig<IX> {
impl<C: Protocol<C> + Zeroize> NoiseConfig<IX, C> {
/// Create a new `NoiseConfig` for the IX handshake pattern.
pub fn ix(kp: Keypair<Curve25519>) -> Self {
pub fn ix(keys: Keypair<C>) -> Self {
NoiseConfig {
keypair: kp,
params: PARAMS_IX.clone(),
keys,
params: C::params_ix(),
remote: (),
_marker: std::marker::PhantomData
}
}
}
impl NoiseConfig<XX> {
impl<C: Protocol<C> + Zeroize> NoiseConfig<XX, C> {
/// Create a new `NoiseConfig` for the XX handshake pattern.
pub fn xx(kp: Keypair<Curve25519>) -> Self {
pub fn xx(keys: Keypair<C>) -> Self {
NoiseConfig {
keypair: kp,
params: PARAMS_XX.clone(),
keys,
params: C::params_xx(),
remote: (),
_marker: std::marker::PhantomData
}
}
}
impl NoiseConfig<IK> {
impl<C: Protocol<C> + Zeroize> NoiseConfig<IK, C> {
/// Create a new `NoiseConfig` for the IK handshake pattern (recipient side).
pub fn ik_listener(kp: Keypair<Curve25519>) -> Self {
pub fn ik_listener(keys: Keypair<C>) -> Self {
NoiseConfig {
keypair: kp,
params: PARAMS_IK.clone(),
keys,
params: C::params_ik(),
remote: (),
_marker: std::marker::PhantomData
}
}
}
impl NoiseConfig<IK, PublicKey<Curve25519>> {
impl<C: Protocol<C> + Zeroize> NoiseConfig<IK, C, PublicKey<C>> {
/// Create a new `NoiseConfig` for the IK handshake pattern (initiator side).
pub fn ik_dialer(kp: Keypair<Curve25519>, remote: PublicKey<Curve25519>) -> Self {
pub fn ik_dialer(keys: Keypair<C>, remote: PublicKey<C>) -> Self {
NoiseConfig {
keypair: kp,
params: PARAMS_IK.clone(),
keys,
params: C::params_ik(),
remote,
_marker: std::marker::PhantomData
}
@ -145,43 +121,38 @@ impl NoiseConfig<IK, PublicKey<Curve25519>> {
// Handshake pattern IX /////////////////////////////////////////////////////
impl UpgradeInfo for NoiseConfig<IX> {
type Info = &'static [u8];
type InfoIter = std::iter::Once<Self::Info>;
fn protocol_info(&self) -> Self::InfoIter {
std::iter::once(b"/noise/ix/25519/chachapoly/sha256/0.1.0")
}
}
impl<T> InboundUpgrade<T> for NoiseConfig<IX>
impl<T, C> InboundUpgrade<T> for NoiseConfig<IX, C>
where
T: AsyncRead + AsyncWrite
T: AsyncRead + AsyncWrite,
NoiseConfig<IX, C>: UpgradeInfo,
C: Protocol<C> + AsRef<[u8]> + Zeroize
{
type Output = (PublicKey<Curve25519>, NoiseOutput<T>);
type Output = (PublicKey<C>, NoiseOutput<T>);
type Error = NoiseError;
type Future = rt1::NoiseInboundFuture<T>;
type Future = rt1::NoiseInboundFuture<T, C>;
fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future {
let session = snow::Builder::with_resolver(self.params, Box::new(Resolver))
.local_private_key(self.keypair.secret().as_ref())
let session = self.params.into_builder()
.local_private_key(self.keys.secret().as_ref())
.build_responder()
.map_err(NoiseError::from);
rt1::NoiseInboundFuture::new(socket, session)
}
}
impl<T> OutboundUpgrade<T> for NoiseConfig<IX>
impl<T, C> OutboundUpgrade<T> for NoiseConfig<IX, C>
where
T: AsyncRead + AsyncWrite
T: AsyncRead + AsyncWrite,
NoiseConfig<IX, C>: UpgradeInfo,
C: Protocol<C> + AsRef<[u8]> + Zeroize
{
type Output = (PublicKey<Curve25519>, NoiseOutput<T>);
type Output = (PublicKey<C>, NoiseOutput<T>);
type Error = NoiseError;
type Future = rt1::NoiseOutboundFuture<T>;
type Future = rt1::NoiseOutboundFuture<T, C>;
fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future {
let session = snow::Builder::with_resolver(self.params, Box::new(Resolver))
.local_private_key(self.keypair.secret().as_ref())
let session = self.params.into_builder()
.local_private_key(self.keys.secret().as_ref())
.build_initiator()
.map_err(NoiseError::from);
rt1::NoiseOutboundFuture::new(socket, session)
@ -190,43 +161,38 @@ where
// Handshake pattern XX /////////////////////////////////////////////////////
impl UpgradeInfo for NoiseConfig<XX> {
type Info = &'static [u8];
type InfoIter = std::iter::Once<Self::Info>;
fn protocol_info(&self) -> Self::InfoIter {
std::iter::once(b"/noise/xx/25519/chachapoly/sha256/0.1.0")
}
}
impl<T> InboundUpgrade<T> for NoiseConfig<XX>
impl<T, C> InboundUpgrade<T> for NoiseConfig<XX, C>
where
T: AsyncRead + AsyncWrite
T: AsyncRead + AsyncWrite,
NoiseConfig<XX, C>: UpgradeInfo,
C: Protocol<C> + AsRef<[u8]> + Zeroize
{
type Output = (PublicKey<Curve25519>, NoiseOutput<T>);
type Output = (PublicKey<C>, NoiseOutput<T>);
type Error = NoiseError;
type Future = rt15::NoiseInboundFuture<T>;
type Future = rt15::NoiseInboundFuture<T, C>;
fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future {
let session = snow::Builder::with_resolver(self.params, Box::new(Resolver))
.local_private_key(self.keypair.secret().as_ref())
let session = self.params.into_builder()
.local_private_key(self.keys.secret().as_ref())
.build_responder()
.map_err(NoiseError::from);
rt15::NoiseInboundFuture::new(socket, session)
}
}
impl<T> OutboundUpgrade<T> for NoiseConfig<XX>
impl<T, C> OutboundUpgrade<T> for NoiseConfig<XX, C>
where
T: AsyncRead + AsyncWrite
T: AsyncRead + AsyncWrite,
NoiseConfig<XX, C>: UpgradeInfo,
C: Protocol<C> + AsRef<[u8]> + Zeroize
{
type Output = (PublicKey<Curve25519>, NoiseOutput<T>);
type Output = (PublicKey<C>, NoiseOutput<T>);
type Error = NoiseError;
type Future = rt15::NoiseOutboundFuture<T>;
type Future = rt15::NoiseOutboundFuture<T, C>;
fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future {
let session = snow::Builder::with_resolver(self.params, Box::new(Resolver))
.local_private_key(self.keypair.secret().as_ref())
let session = self.params.into_builder()
.local_private_key(self.keys.secret().as_ref())
.build_initiator()
.map_err(NoiseError::from);
rt15::NoiseOutboundFuture::new(socket, session)
@ -235,52 +201,38 @@ where
// Handshake pattern IK /////////////////////////////////////////////////////
impl UpgradeInfo for NoiseConfig<IK> {
type Info = &'static [u8];
type InfoIter = std::iter::Once<Self::Info>;
fn protocol_info(&self) -> Self::InfoIter {
std::iter::once(b"/noise/ik/25519/chachapoly/sha256/0.1.0")
}
}
impl UpgradeInfo for NoiseConfig<IK, PublicKey<Curve25519>> {
type Info = &'static [u8];
type InfoIter = std::iter::Once<Self::Info>;
fn protocol_info(&self) -> Self::InfoIter {
std::iter::once(b"/noise/ik/25519/chachapoly/sha256/0.1.0")
}
}
impl<T> InboundUpgrade<T> for NoiseConfig<IK>
impl<T, C> InboundUpgrade<T> for NoiseConfig<IK, C>
where
T: AsyncRead + AsyncWrite
T: AsyncRead + AsyncWrite,
NoiseConfig<IK, C>: UpgradeInfo,
C: Protocol<C> + AsRef<[u8]> + Zeroize
{
type Output = (PublicKey<Curve25519>, NoiseOutput<T>);
type Output = (PublicKey<C>, NoiseOutput<T>);
type Error = NoiseError;
type Future = rt1::NoiseInboundFuture<T>;
type Future = rt1::NoiseInboundFuture<T, C>;
fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future {
let session = snow::Builder::with_resolver(self.params, Box::new(Resolver))
.local_private_key(self.keypair.secret().as_ref())
let session = self.params.into_builder()
.local_private_key(self.keys.secret().as_ref())
.build_responder()
.map_err(NoiseError::from);
rt1::NoiseInboundFuture::new(socket, session)
}
}
impl<T> OutboundUpgrade<T> for NoiseConfig<IK, PublicKey<Curve25519>>
impl<T, C> OutboundUpgrade<T> for NoiseConfig<IK, C, PublicKey<C>>
where
T: AsyncRead + AsyncWrite
T: AsyncRead + AsyncWrite,
NoiseConfig<IK, C, PublicKey<C>>: UpgradeInfo,
C: Protocol<C> + AsRef<[u8]> + Zeroize
{
type Output = (PublicKey<Curve25519>, NoiseOutput<T>);
type Output = (PublicKey<C>, NoiseOutput<T>);
type Error = NoiseError;
type Future = rt1::NoiseOutboundFuture<T>;
type Future = rt1::NoiseOutboundFuture<T, C>;
fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future {
let session = snow::Builder::with_resolver(self.params, Box::new(Resolver))
.local_private_key(self.keypair.secret().as_ref())
let session = self.params.into_builder()
.local_private_key(self.keys.secret().as_ref())
.remote_public_key(self.remote.as_ref())
.build_initiator()
.map_err(NoiseError::from);

View File

@ -0,0 +1,173 @@
// 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.
//! Components of a Noise protocol.
pub mod x25519;
use crate::NoiseError;
use rand::FromEntropy;
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);
impl ProtocolParams {
/// Turn the protocol parameters into a session builder.
pub(crate) fn into_builder(self) -> snow::Builder<'static> {
snow::Builder::with_resolver(self.0, Box::new(Resolver))
}
}
/// 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.
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>, NoiseError>;
}
/// DH keypair.
#[derive(Clone)]
pub struct Keypair<T: Zeroize> {
secret: SecretKey<T>,
public: PublicKey<T>
}
impl<T: Zeroize> Keypair<T> {
/// The public key of the DH keypair.
pub fn public(&self) -> &PublicKey<T> {
&self.public
}
/// The secret key of the DH keypair.
pub fn secret(&self) -> &SecretKey<T> {
&self.secret
}
}
/// DH secret key.
#[derive(Clone)]
pub struct SecretKey<T: Zeroize>(T);
impl<T: Zeroize> Drop for SecretKey<T> {
fn drop(&mut self) {
self.0.zeroize()
}
}
impl<T: AsRef<[u8]> + Zeroize> AsRef<[u8]> for SecretKey<T> {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
/// DH public key.
#[derive(Clone)]
pub struct PublicKey<T>(T);
impl<T: AsRef<[u8]>> PartialEq for PublicKey<T> {
fn eq(&self, other: &PublicKey<T>) -> bool {
self.as_ref() == other.as_ref()
}
}
impl<T: AsRef<[u8]>> Eq for PublicKey<T> {}
impl<T: AsRef<[u8]>> AsRef<[u8]> for PublicKey<T> {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
/// Custom `snow::CryptoResolver` which delegates to the `RingResolver`
/// for hash functions and symmetric ciphers, while using x25519-dalek
/// for Curve25519 DH. We do not use the default resolver for any of
/// the choices, because it comes with unwanted additional dependencies,
/// notably rust-crypto, and to avoid being affected by changes to
/// the defaults.
struct Resolver;
impl snow::resolvers::CryptoResolver for Resolver {
fn resolve_rng(&self) -> Option<Box<dyn snow::types::Random>> {
Some(Box::new(Rng(rand::rngs::StdRng::from_entropy())))
}
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::X25519>::default()))
} else {
None
}
}
fn resolve_hash(&self, choice: &snow::params::HashChoice) -> Option<Box<dyn snow::types::Hash>> {
snow::resolvers::RingResolver.resolve_hash(choice)
}
fn resolve_cipher(&self, choice: &snow::params::CipherChoice) -> Option<Box<dyn snow::types::Cipher>> {
snow::resolvers::RingResolver.resolve_cipher(choice)
}
}
/// Wrapper around a CSPRNG to implement `snow::Random` trait for.
struct Rng(rand::rngs::StdRng);
impl rand::RngCore for Rng {
fn next_u32(&mut self) -> u32 {
self.0.next_u32()
}
fn next_u64(&mut self) -> u64 {
self.0.next_u64()
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.0.fill_bytes(dest)
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
self.0.try_fill_bytes(dest)
}
}
impl rand::CryptoRng for Rng {}
impl snow::types::Random for Rng {}

View File

@ -0,0 +1,307 @@
// 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.
//! Noise protocols based on X25519.
use crate::{NoiseConfig, NoiseError, Protocol, ProtocolParams};
use curve25519_dalek::edwards::CompressedEdwardsY;
use lazy_static::lazy_static;
use libp2p_core::UpgradeInfo;
use libp2p_core::identity::ed25519;
use rand::Rng;
use ring::digest::{SHA512, digest};
use x25519_dalek::{X25519_BASEPOINT_BYTES, x25519};
use zeroize::Zeroize;
use super::*;
lazy_static! {
static ref PARAMS_IK: ProtocolParams = "Noise_IK_25519_ChaChaPoly_SHA256"
.parse()
.map(ProtocolParams)
.expect("Invalid protocol name");
static ref PARAMS_IX: ProtocolParams = "Noise_IX_25519_ChaChaPoly_SHA256"
.parse()
.map(ProtocolParams)
.expect("Invalid protocol name");
static ref PARAMS_XX: ProtocolParams = "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 [u8];
type InfoIter = std::iter::Once<Self::Info>;
fn protocol_info(&self) -> Self::InfoIter {
std::iter::once(b"/noise/ix/25519/chachapoly/sha256/0.1.0")
}
}
impl UpgradeInfo for NoiseConfig<XX, X25519> {
type Info = &'static [u8];
type InfoIter = std::iter::Once<Self::Info>;
fn protocol_info(&self) -> Self::InfoIter {
std::iter::once(b"/noise/xx/25519/chachapoly/sha256/0.1.0")
}
}
impl UpgradeInfo for NoiseConfig<IK, X25519> {
type Info = &'static [u8];
type InfoIter = std::iter::Once<Self::Info>;
fn protocol_info(&self) -> Self::InfoIter {
std::iter::once(b"/noise/ik/25519/chachapoly/sha256/0.1.0")
}
}
impl UpgradeInfo for NoiseConfig<IK, X25519, PublicKey<X25519>> {
type Info = &'static [u8];
type InfoIter = std::iter::Once<Self::Info>;
fn protocol_info(&self) -> Self::InfoIter {
std::iter::once(b"/noise/ik/25519/chachapoly/sha256/0.1.0")
}
}
/// Noise protocols for X25519.
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>, NoiseError> {
if bytes.len() != 32 {
return Err(NoiseError::InvalidKey)
}
let mut pk = [0u8; 32];
pk.copy_from_slice(bytes);
Ok(PublicKey(X25519(pk)))
}
}
impl Keypair<X25519> {
/// 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(X25519([0u8; 32])),
public: PublicKey(X25519([0u8; 32]))
}
}
/// 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)
}
}
/// 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, e.g. for
/// signing in the `secio` protocol, 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 = digest(&SHA512, ed25519_sk.as_ref());
curve25519_sk.copy_from_slice(&hash.as_ref()[..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<(), ()> {
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 libp2p_core::identity::ed25519;
use quickcheck::*;
use sodiumoxide::crypto::sign;
use std::os::raw::c_int;
use super::*;
use x25519_dalek::StaticSecret;
// ed25519 to x25519 keypair conversion must yield the same results as
// obtained through libsodium.
#[test]
fn prop_ed25519_to_x25519_matches_libsodium() {
fn prop() -> bool {
let ed25519 = ed25519::Keypair::generate();
let x25519 = Keypair::from(SecretKey::from_ed25519(&ed25519.secret()));
let sodium_sec = ed25519_sk_to_curve25519(&sign::SecretKey(ed25519.encode()));
let sodium_pub = ed25519_pk_to_curve25519(&sign::PublicKey(ed25519.public().encode().clone()));
let our_pub = x25519.public.0;
// libsodium does the [clamping] of the scalar upon key construction,
// just like x25519-dalek, but this module uses the raw byte-oriented x25519
// function from x25519-dalek, as defined in RFC7748, so "our" secret scalar
// must be clamped before comparing it to the one computed by libsodium.
// That happens in `StaticSecret::from`.
//
// [clamping]: http://www.lix.polytechnique.fr/~smith/ECC/#scalar-clamping
let our_sec = StaticSecret::from((x25519.secret.0).0).to_bytes();
sodium_sec.as_ref() == Some(&our_sec) &&
sodium_pub.as_ref() == Some(&our_pub.0)
}
quickcheck(prop as fn() -> _);
}
// 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() -> _);
}
// Bindings to libsodium's ed25519 to curve25519 key conversions, to check that
// they agree with the conversions performed in this module.
extern "C" {
pub fn crypto_sign_ed25519_pk_to_curve25519(c: *mut u8, e: *const u8) -> c_int;
pub fn crypto_sign_ed25519_sk_to_curve25519(c: *mut u8, e: *const u8) -> c_int;
}
pub fn ed25519_pk_to_curve25519(k: &sign::PublicKey) -> Option<[u8; 32]> {
let mut out = [0u8; 32];
unsafe {
if crypto_sign_ed25519_pk_to_curve25519(out.as_mut_ptr(), (&k.0).as_ptr()) == 0 {
Some(out)
} else {
None
}
}
}
pub fn ed25519_sk_to_curve25519(k: &sign::SecretKey) -> Option<[u8; 32]> {
let mut out = [0u8; 32];
unsafe {
if crypto_sign_ed25519_sk_to_curve25519(out.as_mut_ptr(), (&k.0).as_ptr()) == 0 {
Some(out)
} else {
None
}
}
}
}

View File

@ -21,13 +21,15 @@
//! Futures performing 1 round trip.
use crate::{
error::NoiseError,
Protocol,
PublicKey,
NoiseError,
io::{Handshake, NoiseOutput},
keys::{Curve25519, PublicKey}
};
use futures::prelude::*;
use snow;
use std::mem;
use std::marker::PhantomData;
use tokio_io::{AsyncRead, AsyncWrite};
/// A future for inbound upgrades.
@ -36,13 +38,22 @@ use tokio_io::{AsyncRead, AsyncWrite};
///
/// 1. receive message
/// 2. send message
pub struct NoiseInboundFuture<T>(InboundState<T>);
pub struct NoiseInboundFuture<T, C> {
state: InboundState<T>,
_phantom: PhantomData<C>
}
impl<T> NoiseInboundFuture<T> {
impl<T, C> NoiseInboundFuture<T, C> {
pub(super) fn new(io: T, session: Result<snow::Session, NoiseError>) -> Self {
match session {
Ok(s) => Self(InboundState::RecvHandshake(Handshake::new(io, s))),
Err(e) => Self(InboundState::Err(e))
Ok(s) => NoiseInboundFuture {
state: InboundState::RecvHandshake(Handshake::new(io, s)),
_phantom: PhantomData
},
Err(e) => NoiseInboundFuture {
state: InboundState::Err(e),
_phantom: PhantomData
}
}
}
}
@ -55,39 +66,39 @@ enum InboundState<T> {
Done
}
impl<T> Future for NoiseInboundFuture<T>
impl<T, C: Protocol<C>> Future for NoiseInboundFuture<T, C>
where
T: AsyncRead + AsyncWrite
{
type Item = (PublicKey<Curve25519>, NoiseOutput<T>);
type Item = (PublicKey<C>, NoiseOutput<T>);
type Error = NoiseError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
match mem::replace(&mut self.0, InboundState::Done) {
match mem::replace(&mut self.state, InboundState::Done) {
InboundState::RecvHandshake(mut io) => {
if io.receive()?.is_ready() {
self.0 = InboundState::SendHandshake(io)
self.state = InboundState::SendHandshake(io)
} else {
self.0 = InboundState::RecvHandshake(io);
self.state = InboundState::RecvHandshake(io);
return Ok(Async::NotReady)
}
}
InboundState::SendHandshake(mut io) => {
if io.send()?.is_ready() {
self.0 = InboundState::Flush(io)
self.state = InboundState::Flush(io)
} else {
self.0 = InboundState::SendHandshake(io);
self.state = InboundState::SendHandshake(io);
return Ok(Async::NotReady)
}
}
InboundState::Flush(mut io) => {
if io.flush()?.is_ready() {
let result = io.finish()?;
self.0 = InboundState::Done;
let result = io.finish::<C>()?;
self.state = InboundState::Done;
return Ok(Async::Ready(result))
} else {
self.0 = InboundState::Flush(io);
self.state = InboundState::Flush(io);
return Ok(Async::NotReady)
}
}
@ -104,13 +115,22 @@ where
///
/// 1. send message
/// 2. receive message
pub struct NoiseOutboundFuture<T>(OutboundState<T>);
pub struct NoiseOutboundFuture<T, C> {
state: OutboundState<T>,
_phantom: PhantomData<C>
}
impl<T> NoiseOutboundFuture<T> {
impl<T, C> NoiseOutboundFuture<T, C> {
pub(super) fn new(io: T, session: Result<snow::Session, NoiseError>) -> Self {
match session {
Ok(s) => Self(OutboundState::SendHandshake(Handshake::new(io, s))),
Err(e) => Self(OutboundState::Err(e))
Ok(s) => NoiseOutboundFuture {
state: OutboundState::SendHandshake(Handshake::new(io, s)),
_phantom: PhantomData
},
Err(e) => NoiseOutboundFuture {
state: OutboundState::Err(e),
_phantom: PhantomData
}
}
}
}
@ -123,39 +143,39 @@ enum OutboundState<T> {
Done
}
impl<T> Future for NoiseOutboundFuture<T>
impl<T, C: Protocol<C>> Future for NoiseOutboundFuture<T, C>
where
T: AsyncRead + AsyncWrite
{
type Item = (PublicKey<Curve25519>, NoiseOutput<T>);
type Item = (PublicKey<C>, NoiseOutput<T>);
type Error = NoiseError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
match mem::replace(&mut self.0, OutboundState::Done) {
match mem::replace(&mut self.state, OutboundState::Done) {
OutboundState::SendHandshake(mut io) => {
if io.send()?.is_ready() {
self.0 = OutboundState::Flush(io)
self.state = OutboundState::Flush(io)
} else {
self.0 = OutboundState::SendHandshake(io);
self.state = OutboundState::SendHandshake(io);
return Ok(Async::NotReady)
}
}
OutboundState::Flush(mut io) => {
if io.flush()?.is_ready() {
self.0 = OutboundState::RecvHandshake(io)
self.state = OutboundState::RecvHandshake(io)
} else {
self.0 = OutboundState::Flush(io);
self.state = OutboundState::Flush(io);
return Ok(Async::NotReady)
}
}
OutboundState::RecvHandshake(mut io) => {
if io.receive()?.is_ready() {
let result = io.finish()?;
self.0 = OutboundState::Done;
let result = io.finish::<C>()?;
self.state = OutboundState::Done;
return Ok(Async::Ready(result))
} else {
self.0 = OutboundState::RecvHandshake(io);
self.state = OutboundState::RecvHandshake(io);
return Ok(Async::NotReady)
}
}

View File

@ -21,13 +21,15 @@
//! Futures performing 1.5 round trips.
use crate::{
error::NoiseError,
Protocol,
PublicKey,
NoiseError,
io::{Handshake, NoiseOutput},
keys::{Curve25519, PublicKey}
};
use futures::prelude::*;
use snow;
use std::mem;
use std::marker::PhantomData;
use tokio_io::{AsyncRead, AsyncWrite};
/// A future for inbound upgrades.
@ -37,13 +39,22 @@ use tokio_io::{AsyncRead, AsyncWrite};
/// 1. receive message
/// 2. send message
/// 3. receive message
pub struct NoiseInboundFuture<T>(InboundState<T>);
pub struct NoiseInboundFuture<T, C> {
state: InboundState<T>,
_phantom: PhantomData<C>
}
impl<T> NoiseInboundFuture<T> {
impl<T, C> NoiseInboundFuture<T, C> {
pub(super) fn new(io: T, session: Result<snow::Session, NoiseError>) -> Self {
match session {
Ok(s) => Self(InboundState::RecvHandshake1(Handshake::new(io, s))),
Err(e) => Self(InboundState::Err(e))
Ok(s) => NoiseInboundFuture {
state: InboundState::RecvHandshake1(Handshake::new(io, s)),
_phantom: PhantomData
},
Err(e) => NoiseInboundFuture {
state: InboundState::Err(e),
_phantom: PhantomData
}
}
}
}
@ -57,47 +68,47 @@ enum InboundState<T> {
Done
}
impl<T> Future for NoiseInboundFuture<T>
impl<T, C: Protocol<C>> Future for NoiseInboundFuture<T, C>
where
T: AsyncRead + AsyncWrite
{
type Item = (PublicKey<Curve25519>, NoiseOutput<T>);
type Item = (PublicKey<C>, NoiseOutput<T>);
type Error = NoiseError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
match mem::replace(&mut self.0, InboundState::Done) {
match mem::replace(&mut self.state, InboundState::Done) {
InboundState::RecvHandshake1(mut io) => {
if io.receive()?.is_ready() {
self.0 = InboundState::SendHandshake(io)
self.state = InboundState::SendHandshake(io)
} else {
self.0 = InboundState::RecvHandshake1(io);
self.state = InboundState::RecvHandshake1(io);
return Ok(Async::NotReady)
}
}
InboundState::SendHandshake(mut io) => {
if io.send()?.is_ready() {
self.0 = InboundState::Flush(io)
self.state = InboundState::Flush(io)
} else {
self.0 = InboundState::SendHandshake(io);
self.state = InboundState::SendHandshake(io);
return Ok(Async::NotReady)
}
}
InboundState::Flush(mut io) => {
if io.flush()?.is_ready() {
self.0 = InboundState::RecvHandshake2(io)
self.state = InboundState::RecvHandshake2(io)
} else {
self.0 = InboundState::Flush(io);
self.state = InboundState::Flush(io);
return Ok(Async::NotReady)
}
}
InboundState::RecvHandshake2(mut io) => {
if io.receive()?.is_ready() {
let result = io.finish()?;
self.0 = InboundState::Done;
let result = io.finish::<C>()?;
self.state = InboundState::Done;
return Ok(Async::Ready(result))
} else {
self.0 = InboundState::RecvHandshake2(io);
self.state = InboundState::RecvHandshake2(io);
return Ok(Async::NotReady)
}
}
@ -115,13 +126,22 @@ where
/// 1. send message
/// 2. receive message
/// 3. send message
pub struct NoiseOutboundFuture<T>(OutboundState<T>);
pub struct NoiseOutboundFuture<T, C> {
state: OutboundState<T>,
_phantom: PhantomData<C>
}
impl<T> NoiseOutboundFuture<T> {
impl<T, C> NoiseOutboundFuture<T, C> {
pub(super) fn new(io: T, session: Result<snow::Session, NoiseError>) -> Self {
match session {
Ok(s) => Self(OutboundState::SendHandshake1(Handshake::new(io, s))),
Err(e) => Self(OutboundState::Err(e))
Ok(s) => NoiseOutboundFuture {
state: OutboundState::SendHandshake1(Handshake::new(io, s)),
_phantom: PhantomData
},
Err(e) => NoiseOutboundFuture {
state: OutboundState::Err(e),
_phantom: PhantomData
}
}
}
}
@ -136,55 +156,55 @@ enum OutboundState<T> {
Done
}
impl<T> Future for NoiseOutboundFuture<T>
impl<T, C: Protocol<C>> Future for NoiseOutboundFuture<T, C>
where
T: AsyncRead + AsyncWrite
{
type Item = (PublicKey<Curve25519>, NoiseOutput<T>);
type Item = (PublicKey<C>, NoiseOutput<T>);
type Error = NoiseError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
match mem::replace(&mut self.0, OutboundState::Done) {
match mem::replace(&mut self.state, OutboundState::Done) {
OutboundState::SendHandshake1(mut io) => {
if io.send()?.is_ready() {
self.0 = OutboundState::Flush1(io)
self.state = OutboundState::Flush1(io)
} else {
self.0 = OutboundState::SendHandshake1(io);
self.state = OutboundState::SendHandshake1(io);
return Ok(Async::NotReady)
}
}
OutboundState::Flush1(mut io) => {
if io.flush()?.is_ready() {
self.0 = OutboundState::RecvHandshake(io)
self.state = OutboundState::RecvHandshake(io)
} else {
self.0 = OutboundState::Flush1(io);
self.state = OutboundState::Flush1(io);
return Ok(Async::NotReady)
}
}
OutboundState::RecvHandshake(mut io) => {
if io.receive()?.is_ready() {
self.0 = OutboundState::SendHandshake2(io)
self.state = OutboundState::SendHandshake2(io)
} else {
self.0 = OutboundState::RecvHandshake(io);
self.state = OutboundState::RecvHandshake(io);
return Ok(Async::NotReady)
}
}
OutboundState::SendHandshake2(mut io) => {
if io.send()?.is_ready() {
self.0 = OutboundState::Flush2(io)
self.state = OutboundState::Flush2(io)
} else {
self.0 = OutboundState::SendHandshake2(io);
self.state = OutboundState::SendHandshake2(io);
return Ok(Async::NotReady)
}
}
OutboundState::Flush2(mut io) => {
if io.flush()?.is_ready() {
let result = io.finish()?;
self.0 = OutboundState::Done;
let result = io.finish::<C>()?;
self.state = OutboundState::Done;
return Ok(Async::Ready(result))
} else {
self.0 = OutboundState::Flush2(io);
self.state = OutboundState::Flush2(io);
return Ok(Async::NotReady)
}
}

View File

@ -1,128 +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.
use crate::{keys::{Curve25519, Keypair, SecretKey, PublicKey}, NoiseError};
use rand::FromEntropy;
pub(crate) fn to_array(bytes: &[u8]) -> Result<[u8; 32], NoiseError> {
if bytes.len() != 32 {
return Err(NoiseError::InvalidKey)
}
let mut m = [0; 32];
m.copy_from_slice(bytes);
Ok(m)
}
/// Custom `snow::CryptoResolver` which uses `ring` as much as possible.
pub(crate) struct Resolver;
impl snow::resolvers::CryptoResolver for Resolver {
fn resolve_rng(&self) -> Option<Box<dyn snow::types::Random>> {
Some(Box::new(Rng(rand::rngs::StdRng::from_entropy())))
}
fn resolve_dh(&self, choice: &snow::params::DHChoice) -> Option<Box<dyn snow::types::Dh>> {
if let snow::params::DHChoice::Curve25519 = choice {
Some(Box::new(X25519 { keypair: Keypair::gen_curve25519() }))
} else {
None
}
}
fn resolve_hash(&self, choice: &snow::params::HashChoice) -> Option<Box<dyn snow::types::Hash>> {
snow::resolvers::RingResolver.resolve_hash(choice)
}
fn resolve_cipher(&self, choice: &snow::params::CipherChoice) -> Option<Box<dyn snow::types::Cipher>> {
snow::resolvers::RingResolver.resolve_cipher(choice)
}
}
/// Wrapper around a CPRNG to implement `snow::Random` trait for.
struct Rng(rand::rngs::StdRng);
impl rand::RngCore for Rng {
fn next_u32(&mut self) -> u32 {
self.0.next_u32()
}
fn next_u64(&mut self) -> u64 {
self.0.next_u64()
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.0.fill_bytes(dest)
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
self.0.try_fill_bytes(dest)
}
}
impl rand::CryptoRng for Rng {}
impl snow::types::Random for Rng {}
/// X25519 Diffie-Hellman key agreement.
struct X25519 {
keypair: Keypair<Curve25519>
}
impl snow::types::Dh for X25519 {
fn name(&self) -> &'static str { "25519" }
fn pub_len(&self) -> usize { 32 }
fn priv_len(&self) -> usize { 32 }
fn pubkey(&self) -> &[u8] {
&self.keypair.public().as_ref()
}
fn privkey(&self) -> &[u8] {
&self.keypair.secret().as_ref()
}
fn set(&mut self, privkey: &[u8]) {
let mut s = [0; 32];
s.copy_from_slice(privkey);
let secret = SecretKey::new(s);
let public = secret.public();
self.keypair = Keypair::new(secret, public)
}
fn generate(&mut self, rng: &mut dyn snow::types::Random) {
let mut s = [0; 32];
rng.fill_bytes(&mut s);
let secret = SecretKey::new(s);
let public = secret.public();
self.keypair = Keypair::new(secret, public)
}
fn dh(&self, pubkey: &[u8], out: &mut [u8]) -> Result<(), ()> {
let mut p = [0; 32];
p.copy_from_slice(&pubkey[.. 32]);
let public = PublicKey::new(p);
let result = self.keypair.secret().ecdh(&public);
out[.. 32].copy_from_slice(&result[..]);
Ok(())
}
}

View File

@ -20,7 +20,7 @@
use futures::{future::Either, prelude::*};
use libp2p_core::{Transport, upgrade::{apply_inbound, apply_outbound}};
use libp2p_noise::{Keypair, PublicKey, Curve25519, NoiseConfig};
use libp2p_noise::{Keypair, X25519, NoiseConfig};
use libp2p_tcp::TcpConfig;
use log::info;
use quickcheck::QuickCheck;
@ -30,10 +30,11 @@ use tokio::{self, io};
fn xx() {
let _ = env_logger::try_init();
fn prop(message: Vec<u8>) -> bool {
let server_keypair = Keypair::gen_curve25519();
let server_keypair = Keypair::<X25519>::new();
let server_transport = TcpConfig::new().with_upgrade(NoiseConfig::xx(server_keypair));
let client_keypair = Keypair::gen_curve25519();
let client_keypair = Keypair::<X25519>::new();
let client_transport = TcpConfig::new().with_upgrade(NoiseConfig::xx(client_keypair));
run(server_transport, client_transport, message);
@ -47,10 +48,10 @@ fn ix() {
let _ = env_logger::try_init();
fn prop(message: Vec<u8>) -> bool {
let server_keypair = Keypair::gen_curve25519();
let server_keypair = Keypair::<X25519>::new();
let server_transport = TcpConfig::new().with_upgrade(NoiseConfig::ix(server_keypair));
let client_keypair = Keypair::gen_curve25519();
let client_keypair = Keypair::<X25519>::new();
let client_transport = TcpConfig::new().with_upgrade(NoiseConfig::ix(client_keypair));
run(server_transport, client_transport, message);
@ -63,7 +64,7 @@ fn ix() {
fn ik_xx() {
let _ = env_logger::try_init();
fn prop(message: Vec<u8>) -> bool {
let server_keypair = Keypair::gen_curve25519();
let server_keypair = Keypair::<X25519>::new();
let server_public = server_keypair.public().clone();
let server_transport = TcpConfig::new()
.and_then(move |output, endpoint| {
@ -74,7 +75,7 @@ fn ik_xx() {
}
});
let client_keypair = Keypair::gen_curve25519();
let client_keypair = Keypair::<X25519>::new();
let client_transport = TcpConfig::new()
.and_then(move |output, endpoint| {
if endpoint.is_dialer() {
@ -90,14 +91,14 @@ fn ik_xx() {
QuickCheck::new().max_tests(30).quickcheck(prop as fn(Vec<u8>) -> bool)
}
fn run<T, A, U, B>(server_transport: T, client_transport: U, message1: Vec<u8>)
fn run<T, A, U, B, P>(server_transport: T, client_transport: U, message1: Vec<u8>)
where
T: Transport<Output = (PublicKey<Curve25519>, A)>,
T: Transport<Output = (P, A)>,
T::Dial: Send + 'static,
T::Listener: Send + 'static,
T::ListenerUpgrade: Send + 'static,
A: io::AsyncRead + io::AsyncWrite + Send + 'static,
U: Transport<Output = (PublicKey<Curve25519>, B)>,
U: Transport<Output = (P, B)>,
U::Dial: Send + 'static,
U::Listener: Send + 'static,
U::ListenerUpgrade: Send + 'static,