mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-05-18 21:51:19 +00:00
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:
parent
26df15641c
commit
2c66f82b11
@ -10,8 +10,11 @@ keywords = ["peer-to-peer", "libp2p", "networking"]
|
||||
categories = ["network-programming", "asynchronous"]
|
||||
|
||||
[dependencies]
|
||||
asn1_der = "0.6.1"
|
||||
bs58 = "0.2.0"
|
||||
bytes = "0.4"
|
||||
ed25519-dalek = "1.0.0-pre.1"
|
||||
failure = "0.1"
|
||||
fnv = "1.0"
|
||||
lazy_static = "1.2"
|
||||
log = "0.4"
|
||||
@ -24,12 +27,19 @@ protobuf = "2.3"
|
||||
quick-error = "1.2"
|
||||
rand = "0.6"
|
||||
rw-stream-sink = { version = "0.1.0", path = "../misc/rw-stream-sink" }
|
||||
secp256k1 = { version = "0.12", features = ["rand"], optional = true }
|
||||
sha2 = "0.8.0"
|
||||
smallvec = "0.6"
|
||||
tokio-executor = "0.1.4"
|
||||
tokio-io = "0.1"
|
||||
tokio-timer = "0.2"
|
||||
unsigned-varint = "0.2"
|
||||
void = "1"
|
||||
zeroize = "0.5"
|
||||
|
||||
[target.'cfg(not(any(target_os = "emscripten", target_os = "unknown")))'.dependencies]
|
||||
ring = { version = "0.14", features = ["use_heap"], default-features = false }
|
||||
untrusted = { version = "0.6" }
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.6"
|
||||
@ -38,8 +48,12 @@ libp2p-tcp = { version = "0.4.0", path = "../transports/tcp" }
|
||||
libp2p-mplex = { version = "0.4.0", path = "../muxers/mplex" }
|
||||
libp2p-secio = { version = "0.4.0", path = "../protocols/secio" }
|
||||
rand = "0.6"
|
||||
quickcheck = "0.8"
|
||||
tokio = "0.1"
|
||||
tokio-codec = "0.1"
|
||||
tokio-timer = "0.2"
|
||||
assert_matches = "1.3"
|
||||
tokio-mock-task = "0.1"
|
||||
|
||||
[features]
|
||||
default = ["secp256k1"]
|
||||
|
216
core/src/identity.rs
Normal file
216
core/src/identity.rs
Normal file
@ -0,0 +1,216 @@
|
||||
// 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.
|
||||
|
||||
//! A node's network identity keys.
|
||||
|
||||
pub mod ed25519;
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
pub mod rsa;
|
||||
#[cfg(feature = "secp256k1")]
|
||||
pub mod secp256k1;
|
||||
|
||||
pub mod error;
|
||||
|
||||
use self::error::*;
|
||||
use crate::{PeerId, keys_proto};
|
||||
|
||||
/// Identity keypair of a node.
|
||||
///
|
||||
/// # Example: Generating RSA keys with OpenSSL
|
||||
///
|
||||
/// ```text
|
||||
/// openssl genrsa -out private.pem 2048
|
||||
/// openssl pkcs8 -in private.pem -inform PEM -topk8 -out private.pk8 -outform DER -nocrypt
|
||||
/// rm private.pem # optional
|
||||
/// ```
|
||||
///
|
||||
/// Loading the keys:
|
||||
///
|
||||
/// ```text
|
||||
/// let mut bytes = std::fs::read("private.pem").unwrap();
|
||||
/// let keypair = Keypair::rsa_from_pkcs8(&mut bytes);
|
||||
/// ```
|
||||
///
|
||||
#[derive(Clone)]
|
||||
pub enum Keypair {
|
||||
/// An Ed25519 keypair.
|
||||
Ed25519(ed25519::Keypair),
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
/// An RSA keypair.
|
||||
Rsa(rsa::Keypair),
|
||||
/// A Secp256k1 keypair.
|
||||
#[cfg(feature = "secp256k1")]
|
||||
Secp256k1(secp256k1::Keypair)
|
||||
}
|
||||
|
||||
impl Keypair {
|
||||
/// Generate a new Ed25519 keypair.
|
||||
pub fn generate_ed25519() -> Keypair {
|
||||
Keypair::Ed25519(ed25519::Keypair::generate())
|
||||
}
|
||||
|
||||
/// Generate a new Secp256k1 keypair.
|
||||
#[cfg(feature = "secp256k1")]
|
||||
pub fn generate_secp256k1() -> Keypair {
|
||||
Keypair::Secp256k1(secp256k1::Keypair::generate())
|
||||
}
|
||||
|
||||
/// Decode an keypair from a DER-encoded secret key in PKCS#8 PrivateKeyInfo
|
||||
/// format (i.e. unencrypted) as defined in [RFC5208].
|
||||
///
|
||||
/// [RFC5208]: https://tools.ietf.org/html/rfc5208#section-5
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
pub fn rsa_from_pkcs8(pkcs8_der: &mut [u8]) -> Result<Keypair, DecodingError> {
|
||||
rsa::Keypair::from_pkcs8(pkcs8_der).map(Keypair::Rsa)
|
||||
}
|
||||
|
||||
/// Decode a keypair from a DER-encoded Secp256k1 secret key in an ECPrivateKey
|
||||
/// structure as defined in [RFC5915].
|
||||
///
|
||||
/// [RFC5915]: https://tools.ietf.org/html/rfc5915
|
||||
#[cfg(feature = "secp256k1")]
|
||||
pub fn secp256k1_from_der(der: &mut [u8]) -> Result<Keypair, DecodingError> {
|
||||
secp256k1::SecretKey::from_der(der)
|
||||
.map(|sk| Keypair::Secp256k1(secp256k1::Keypair::from(sk)))
|
||||
}
|
||||
|
||||
/// Sign a message using the private key of this keypair, producing
|
||||
/// a signature that can be verified using the corresponding public key.
|
||||
pub fn sign(&self, msg: &[u8]) -> Result<Vec<u8>, SigningError> {
|
||||
use Keypair::*;
|
||||
match self {
|
||||
Ed25519(ref pair) => Ok(pair.sign(msg)),
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
Rsa(ref pair) => pair.sign(msg),
|
||||
#[cfg(feature = "secp256k1")]
|
||||
Secp256k1(ref pair) => Ok(pair.secret().sign(msg)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the public key of this keypair.
|
||||
pub fn public(&self) -> PublicKey {
|
||||
use Keypair::*;
|
||||
match self {
|
||||
Ed25519(pair) => PublicKey::Ed25519(pair.public()),
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
Rsa(pair) => PublicKey::Rsa(pair.public()),
|
||||
#[cfg(feature = "secp256k1")]
|
||||
Secp256k1(pair) => PublicKey::Secp256k1(pair.public().clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The public key of a node's identity keypair.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum PublicKey {
|
||||
/// A public Ed25519 key.
|
||||
Ed25519(ed25519::PublicKey),
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
/// A public RSA key.
|
||||
Rsa(rsa::PublicKey),
|
||||
#[cfg(feature = "secp256k1")]
|
||||
/// A public Secp256k1 key.
|
||||
Secp256k1(secp256k1::PublicKey)
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
/// Verify a signature for a message using this public key, i.e. check
|
||||
/// that the signature has been produced by the corresponding
|
||||
/// private key (authenticity), and that the message has not been
|
||||
/// tampered with (integrity).
|
||||
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool {
|
||||
use PublicKey::*;
|
||||
match self {
|
||||
Ed25519(pk) => pk.verify(msg, sig),
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
Rsa(pk) => pk.verify(msg, sig),
|
||||
#[cfg(feature = "secp256k1")]
|
||||
Secp256k1(pk) => pk.verify(msg, sig)
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode the public key into a protobuf structure for storage or
|
||||
/// exchange with other nodes.
|
||||
pub fn into_protobuf_encoding(self) -> Vec<u8> {
|
||||
use protobuf::Message;
|
||||
let mut public_key = keys_proto::PublicKey::new();
|
||||
match self {
|
||||
PublicKey::Ed25519(key) => {
|
||||
public_key.set_Type(keys_proto::KeyType::Ed25519);
|
||||
public_key.set_Data(key.encode().to_vec());
|
||||
},
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
PublicKey::Rsa(key) => {
|
||||
public_key.set_Type(keys_proto::KeyType::RSA);
|
||||
public_key.set_Data(key.encode_x509());
|
||||
},
|
||||
#[cfg(feature = "secp256k1")]
|
||||
PublicKey::Secp256k1(key) => {
|
||||
public_key.set_Type(keys_proto::KeyType::Secp256k1);
|
||||
public_key.set_Data(key.encode().to_vec());
|
||||
},
|
||||
};
|
||||
|
||||
public_key
|
||||
.write_to_bytes()
|
||||
.expect("Encoding public key into protobuf failed.")
|
||||
}
|
||||
|
||||
/// Decode a public key from a protobuf structure, e.g. read from storage
|
||||
/// or received from another node.
|
||||
pub fn from_protobuf_encoding(bytes: &[u8]) -> Result<PublicKey, DecodingError> {
|
||||
#[allow(unused_mut)] // Due to conditional compilation.
|
||||
let mut pubkey = protobuf::parse_from_bytes::<keys_proto::PublicKey>(bytes)
|
||||
.map_err(|e| DecodingError::new("Protobuf", e))?;
|
||||
|
||||
match pubkey.get_Type() {
|
||||
keys_proto::KeyType::Ed25519 => {
|
||||
ed25519::PublicKey::decode(pubkey.get_Data())
|
||||
.map(PublicKey::Ed25519)
|
||||
},
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
keys_proto::KeyType::RSA => {
|
||||
rsa::PublicKey::decode_x509(&pubkey.take_Data())
|
||||
.map(PublicKey::Rsa)
|
||||
}
|
||||
#[cfg(any(target_os = "emscripten", target_os = "unknown"))]
|
||||
keys_proto::KeyType::RSA => {
|
||||
log::debug!("support for RSA was disabled at compile-time");
|
||||
Err("Unsupported".to_string().into())
|
||||
},
|
||||
#[cfg(feature = "secp256k1")]
|
||||
keys_proto::KeyType::Secp256k1 => {
|
||||
secp256k1::PublicKey::decode(pubkey.get_Data())
|
||||
.map(PublicKey::Secp256k1)
|
||||
}
|
||||
#[cfg(not(feature = "secp256k1"))]
|
||||
keys_proto::KeyType::Secp256k1 => {
|
||||
log::debug!("support for secp256k1 was disabled at compile-time");
|
||||
Err("Unsupported".to_string().into())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the `PublicKey` into the corresponding `PeerId`.
|
||||
pub fn into_peer_id(self) -> PeerId {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
193
core/src/identity/ed25519.rs
Normal file
193
core/src/identity/ed25519.rs
Normal file
@ -0,0 +1,193 @@
|
||||
// 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.
|
||||
|
||||
//! Ed25519 keys.
|
||||
|
||||
use ed25519_dalek as ed25519;
|
||||
use failure::Fail;
|
||||
use super::error::DecodingError;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
/// An Ed25519 keypair.
|
||||
pub struct Keypair(ed25519::Keypair);
|
||||
|
||||
impl Keypair {
|
||||
/// Generate a new Ed25519 keypair.
|
||||
pub fn generate() -> Keypair {
|
||||
Keypair(ed25519::Keypair::generate(&mut rand::thread_rng()))
|
||||
}
|
||||
|
||||
/// Encode the keypair into a byte array by concatenating the bytes
|
||||
/// of the secret scalar and the compressed public point,
|
||||
/// an informal standard for encoding Ed25519 keypairs.
|
||||
pub fn encode(&self) -> [u8; 64] {
|
||||
self.0.to_bytes()
|
||||
}
|
||||
|
||||
/// Decode a keypair from the format produced by `encode`,
|
||||
/// zeroing the input on success.
|
||||
pub fn decode(kp: &mut [u8]) -> Result<Keypair, DecodingError> {
|
||||
ed25519::Keypair::from_bytes(kp)
|
||||
.map(|k| { kp.zeroize(); Keypair(k) })
|
||||
.map_err(|e| DecodingError::new("Ed25519 keypair", e.compat()))
|
||||
}
|
||||
|
||||
/// Sign a message using the private key of this keypair.
|
||||
pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
|
||||
self.0.sign(msg).to_bytes().to_vec()
|
||||
}
|
||||
|
||||
/// Get the public key of this keypair.
|
||||
pub fn public(&self) -> PublicKey {
|
||||
PublicKey(self.0.public)
|
||||
}
|
||||
|
||||
/// Get the secret key of this keypair.
|
||||
pub fn secret(&self) -> SecretKey {
|
||||
SecretKey::from_bytes(&mut self.0.secret.to_bytes())
|
||||
.expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k")
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Keypair {
|
||||
fn clone(&self) -> Keypair {
|
||||
let mut sk_bytes = self.0.secret.to_bytes();
|
||||
let secret = SecretKey::from_bytes(&mut sk_bytes)
|
||||
.expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k").0;
|
||||
let public = ed25519::PublicKey::from_bytes(&self.0.public.to_bytes())
|
||||
.expect("ed25519::PublicKey::from_bytes(to_bytes(k)) != k");
|
||||
Keypair(ed25519::Keypair { secret, public })
|
||||
}
|
||||
}
|
||||
|
||||
/// Demote an Ed25519 keypair to a secret key.
|
||||
impl From<Keypair> for SecretKey {
|
||||
fn from(kp: Keypair) -> SecretKey {
|
||||
SecretKey(kp.0.secret)
|
||||
}
|
||||
}
|
||||
|
||||
/// Promote an Ed25519 secret key into a keypair.
|
||||
impl From<SecretKey> for Keypair {
|
||||
fn from(sk: SecretKey) -> Keypair {
|
||||
let secret = sk.0;
|
||||
let public = ed25519::PublicKey::from(&secret);
|
||||
Keypair(ed25519::Keypair { secret, public })
|
||||
}
|
||||
}
|
||||
|
||||
/// An Ed25519 public key.
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
pub struct PublicKey(ed25519::PublicKey);
|
||||
|
||||
impl PublicKey {
|
||||
/// Verify the Ed25519 signature on a message using the public key.
|
||||
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool {
|
||||
ed25519::Signature::from_bytes(sig).map(|s| self.0.verify(msg, &s)).is_ok()
|
||||
}
|
||||
|
||||
/// Encode the public key into a byte array in compressed form, i.e.
|
||||
/// where one coordinate is represented by a single bit.
|
||||
pub fn encode(&self) -> [u8; 32] {
|
||||
self.0.to_bytes()
|
||||
}
|
||||
|
||||
/// Decode a public key from a byte array as produced by `encode`.
|
||||
pub fn decode(k: &[u8]) -> Result<PublicKey, DecodingError> {
|
||||
ed25519::PublicKey::from_bytes(k)
|
||||
.map_err(|e| DecodingError::new("Ed25519 public key", e.compat()))
|
||||
.map(PublicKey)
|
||||
}
|
||||
}
|
||||
|
||||
/// An Ed25519 secret key.
|
||||
pub struct SecretKey(ed25519::SecretKey);
|
||||
|
||||
/// View the bytes of the secret key.
|
||||
impl AsRef<[u8]> for SecretKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for SecretKey {
|
||||
fn clone(&self) -> SecretKey {
|
||||
let mut sk_bytes = self.0.to_bytes();
|
||||
Self::from_bytes(&mut sk_bytes)
|
||||
.expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k")
|
||||
}
|
||||
}
|
||||
|
||||
impl SecretKey {
|
||||
/// Generate a new Ed25519 secret key.
|
||||
pub fn generate() -> SecretKey {
|
||||
SecretKey(ed25519::SecretKey::generate(&mut rand::thread_rng()))
|
||||
}
|
||||
|
||||
/// Create an Ed25519 secret key from a byte slice, zeroing the input on success.
|
||||
/// If the bytes do not constitute a valid Ed25519 secret key, an error is
|
||||
/// returned.
|
||||
pub fn from_bytes(mut sk_bytes: impl AsMut<[u8]>) -> Result<SecretKey, DecodingError> {
|
||||
let sk_bytes = sk_bytes.as_mut();
|
||||
let secret = ed25519::SecretKey::from_bytes(&*sk_bytes)
|
||||
.map_err(|e| DecodingError::new("Ed25519 secret key", e.compat()))?;
|
||||
sk_bytes.zeroize();
|
||||
Ok(SecretKey(secret))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use quickcheck::*;
|
||||
|
||||
fn eq_keypairs(kp1: &Keypair, kp2: &Keypair) -> bool {
|
||||
kp1.public() == kp2.public()
|
||||
&&
|
||||
kp1.0.secret.as_bytes() == kp2.0.secret.as_bytes()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ed25519_keypair_encode_decode() {
|
||||
fn prop() -> bool {
|
||||
let kp1 = Keypair::generate();
|
||||
let mut kp1_enc = kp1.encode();
|
||||
let kp2 = Keypair::decode(&mut kp1_enc).unwrap();
|
||||
eq_keypairs(&kp1, &kp2)
|
||||
&&
|
||||
kp1_enc.iter().all(|b| *b == 0)
|
||||
}
|
||||
QuickCheck::new().tests(10).quickcheck(prop as fn() -> _);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ed25519_keypair_from_secret() {
|
||||
fn prop() -> bool {
|
||||
let kp1 = Keypair::generate();
|
||||
let mut sk = kp1.0.secret.to_bytes();
|
||||
let kp2 = Keypair::from(SecretKey::from_bytes(&mut sk).unwrap());
|
||||
eq_keypairs(&kp1, &kp2)
|
||||
&&
|
||||
sk == [0u8; 32]
|
||||
}
|
||||
QuickCheck::new().tests(10).quickcheck(prop as fn() -> _);
|
||||
}
|
||||
}
|
||||
|
88
core/src/identity/error.rs
Normal file
88
core/src/identity/error.rs
Normal file
@ -0,0 +1,88 @@
|
||||
// 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.
|
||||
|
||||
//! Errors during identity key operations.
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
/// An error during decoding of key material.
|
||||
#[derive(Debug)]
|
||||
pub struct DecodingError {
|
||||
msg: String,
|
||||
source: Option<Box<dyn Error + Send + Sync>>
|
||||
}
|
||||
|
||||
impl DecodingError {
|
||||
pub(crate) fn new(msg: &str, source: impl Error + Send + Sync + 'static) -> DecodingError {
|
||||
DecodingError { msg: msg.to_string(), source: Some(Box::new(source)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for DecodingError {
|
||||
fn from(s: String) -> DecodingError {
|
||||
DecodingError { msg: s, source: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DecodingError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Key decoding error: {}", self.msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for DecodingError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
self.source.as_ref().map(|s| &**s as &dyn Error)
|
||||
}
|
||||
}
|
||||
|
||||
/// An error during signing of a message.
|
||||
#[derive(Debug)]
|
||||
pub struct SigningError {
|
||||
msg: String,
|
||||
source: Option<Box<dyn Error + Send + Sync>>
|
||||
}
|
||||
|
||||
/// An error during encoding of key material.
|
||||
impl SigningError {
|
||||
pub(crate) fn new(msg: &str, source: impl Error + Send + Sync + 'static) -> SigningError {
|
||||
SigningError { msg: msg.to_string(), source: Some(Box::new(source)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SigningError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Key signing error: {}", self.msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for SigningError {
|
||||
fn from(s: String) -> SigningError {
|
||||
SigningError { msg: s, source: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for SigningError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
self.source.as_ref().map(|s| &**s as &dyn Error)
|
||||
}
|
||||
}
|
||||
|
259
core/src/identity/rsa.rs
Normal file
259
core/src/identity/rsa.rs
Normal file
@ -0,0 +1,259 @@
|
||||
// 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.
|
||||
|
||||
//! RSA keys.
|
||||
|
||||
use asn1_der::{Asn1Der, FromDerObject, IntoDerObject, DerObject, DerTag, DerValue, Asn1DerError};
|
||||
use lazy_static::lazy_static;
|
||||
use super::error::*;
|
||||
use ring::rand::SystemRandom;
|
||||
use ring::signature::{self, RsaKeyPair, RSA_PKCS1_SHA256, RSA_PKCS1_2048_8192_SHA256};
|
||||
use ring::signature::KeyPair;
|
||||
use std::sync::Arc;
|
||||
use untrusted::Input;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
/// An RSA keypair.
|
||||
#[derive(Clone)]
|
||||
pub struct Keypair(Arc<RsaKeyPair>);
|
||||
|
||||
impl Keypair {
|
||||
/// Decode an RSA keypair from a DER-encoded private key in PKCS#8 PrivateKeyInfo
|
||||
/// format (i.e. unencrypted) as defined in [RFC5208].
|
||||
///
|
||||
/// [RFC5208]: https://tools.ietf.org/html/rfc5208#section-5
|
||||
pub fn from_pkcs8(der: &mut [u8]) -> Result<Keypair, DecodingError> {
|
||||
let kp = RsaKeyPair::from_pkcs8(Input::from(&der[..]))
|
||||
.map_err(|e| DecodingError::new("RSA PKCS#8 PrivateKeyInfo", e))?;
|
||||
der.zeroize();
|
||||
Ok(Keypair(Arc::new(kp)))
|
||||
}
|
||||
|
||||
/// Get the public key from the keypair.
|
||||
pub fn public(&self) -> PublicKey {
|
||||
PublicKey(self.0.public_key().as_ref().to_vec())
|
||||
}
|
||||
|
||||
/// Sign a message with this keypair.
|
||||
pub fn sign(&self, data: &[u8]) -> Result<Vec<u8>, SigningError> {
|
||||
let mut signature = vec![0; self.0.public_modulus_len()];
|
||||
let rng = SystemRandom::new();
|
||||
match self.0.sign(&RSA_PKCS1_SHA256, &rng, &data, &mut signature) {
|
||||
Ok(()) => Ok(signature),
|
||||
Err(e) => Err(SigningError::new("RSA", e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An RSA public key.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct PublicKey(Vec<u8>);
|
||||
|
||||
impl PublicKey {
|
||||
/// Verify an RSA signature on a message using the public key.
|
||||
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool {
|
||||
signature::verify(&RSA_PKCS1_2048_8192_SHA256,
|
||||
Input::from(&self.0),
|
||||
Input::from(msg),
|
||||
Input::from(sig)).is_ok()
|
||||
}
|
||||
|
||||
/// Encode the RSA public key in DER as a PKCS#1 RSAPublicKey structure,
|
||||
/// as defined in [RFC3447].
|
||||
///
|
||||
/// [RFC3447]: https://tools.ietf.org/html/rfc3447#appendix-A.1.1
|
||||
pub fn encode_pkcs1(&self) -> Vec<u8> {
|
||||
// This is the encoding currently used in-memory, so it is trivial.
|
||||
self.0.clone()
|
||||
}
|
||||
|
||||
/// Encode the RSA public key in DER as a X.509 SubjectPublicKeyInfo structure,
|
||||
/// as defined in [RFC5280].
|
||||
///
|
||||
/// [RFC5280] https://tools.ietf.org/html/rfc5280#section-4.1
|
||||
pub fn encode_x509(&self) -> Vec<u8> {
|
||||
let spki = Asn1SubjectPublicKeyInfo {
|
||||
algorithmIdentifier: Asn1RsaEncryption {
|
||||
algorithm: Asn1OidRsaEncryption(),
|
||||
parameters: ()
|
||||
},
|
||||
subjectPublicKey: Asn1SubjectPublicKey(self.clone())
|
||||
};
|
||||
let mut buf = vec![0u8; spki.serialized_len()];
|
||||
spki.serialize(buf.iter_mut()).map(|_| buf)
|
||||
.expect("RSA X.509 public key encoding failed.")
|
||||
}
|
||||
|
||||
/// Decode an RSA public key from a DER-encoded X.509 SubjectPublicKeyInfo
|
||||
/// structure. See also `encode_x509`.
|
||||
pub fn decode_x509(pk: &[u8]) -> Result<PublicKey, DecodingError> {
|
||||
Asn1SubjectPublicKeyInfo::deserialize(pk.iter())
|
||||
.map_err(|e| DecodingError::new("RSA X.509", e))
|
||||
.map(|spki| spki.subjectPublicKey.0)
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// DER encoding / decoding of public keys
|
||||
//
|
||||
// Primer: http://luca.ntop.org/Teaching/Appunti/asn1.html
|
||||
// Playground: https://lapo.it/asn1js/
|
||||
|
||||
lazy_static! {
|
||||
/// The DER encoding of the object identifier (OID) 'rsaEncryption' for
|
||||
/// RSA public keys defined for X.509 in [RFC-3279] and used in
|
||||
/// SubjectPublicKeyInfo structures defined in [RFC-5280].
|
||||
///
|
||||
/// [RFC-3279]: https://tools.ietf.org/html/rfc3279#section-2.3.1
|
||||
/// [RFC-5280]: https://tools.ietf.org/html/rfc5280#section-4.1
|
||||
static ref OID_RSA_ENCRYPTION_DER: DerObject =
|
||||
DerObject {
|
||||
tag: DerTag::x06,
|
||||
value: DerValue {
|
||||
data: vec![ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 ]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// The ASN.1 OID for "rsaEncryption".
|
||||
#[derive(Clone)]
|
||||
struct Asn1OidRsaEncryption();
|
||||
|
||||
impl IntoDerObject for Asn1OidRsaEncryption {
|
||||
fn into_der_object(self) -> DerObject {
|
||||
OID_RSA_ENCRYPTION_DER.clone()
|
||||
}
|
||||
fn serialized_len(&self) -> usize {
|
||||
OID_RSA_ENCRYPTION_DER.serialized_len()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromDerObject for Asn1OidRsaEncryption {
|
||||
fn from_der_object(o: DerObject) -> Result<Self, Asn1DerError> {
|
||||
if o.tag != DerTag::x06 {
|
||||
return Err(Asn1DerError::InvalidTag)
|
||||
}
|
||||
if o.value != OID_RSA_ENCRYPTION_DER.value {
|
||||
return Err(Asn1DerError::InvalidEncoding)
|
||||
}
|
||||
Ok(Asn1OidRsaEncryption())
|
||||
}
|
||||
}
|
||||
|
||||
/// The ASN.1 AlgorithmIdentifier for "rsaEncryption".
|
||||
#[derive(Asn1Der)]
|
||||
struct Asn1RsaEncryption {
|
||||
algorithm: Asn1OidRsaEncryption,
|
||||
parameters: ()
|
||||
}
|
||||
|
||||
/// The ASN.1 SubjectPublicKey inside a SubjectPublicKeyInfo,
|
||||
/// i.e. encoded as a DER BIT STRING.
|
||||
struct Asn1SubjectPublicKey(PublicKey);
|
||||
|
||||
impl IntoDerObject for Asn1SubjectPublicKey {
|
||||
fn into_der_object(self) -> DerObject {
|
||||
let pk_der = (self.0).0;
|
||||
let mut bit_string = Vec::with_capacity(pk_der.len() + 1);
|
||||
// The number of bits in pk_der is trivially always a multiple of 8,
|
||||
// so there are always 0 "unused bits" signaled by the first byte.
|
||||
bit_string.push(0u8);
|
||||
bit_string.extend(pk_der);
|
||||
DerObject::new(DerTag::x03, bit_string.into())
|
||||
}
|
||||
fn serialized_len(&self) -> usize {
|
||||
DerObject::compute_serialized_len((self.0).0.len() + 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromDerObject for Asn1SubjectPublicKey {
|
||||
fn from_der_object(o: DerObject) -> Result<Self, Asn1DerError> {
|
||||
if o.tag != DerTag::x03 {
|
||||
return Err(Asn1DerError::InvalidTag)
|
||||
}
|
||||
let pk_der: Vec<u8> = o.value.data.into_iter().skip(1).collect();
|
||||
// We don't parse pk_der further as an ASN.1 RsaPublicKey, since
|
||||
// we only need the DER encoding for `verify`.
|
||||
Ok(Asn1SubjectPublicKey(PublicKey(pk_der)))
|
||||
}
|
||||
}
|
||||
|
||||
/// ASN.1 SubjectPublicKeyInfo
|
||||
#[derive(Asn1Der)]
|
||||
#[allow(non_snake_case)]
|
||||
struct Asn1SubjectPublicKeyInfo {
|
||||
algorithmIdentifier: Asn1RsaEncryption,
|
||||
subjectPublicKey: Asn1SubjectPublicKey
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use quickcheck::*;
|
||||
use rand::seq::SliceRandom;
|
||||
use std::fmt;
|
||||
|
||||
const KEY1: &'static [u8] = include_bytes!("test/rsa-2048.pk8");
|
||||
const KEY2: &'static [u8] = include_bytes!("test/rsa-3072.pk8");
|
||||
const KEY3: &'static [u8] = include_bytes!("test/rsa-4096.pk8");
|
||||
|
||||
#[derive(Clone)]
|
||||
struct SomeKeypair(Keypair);
|
||||
|
||||
impl fmt::Debug for SomeKeypair {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "SomeKeypair")
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for SomeKeypair {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> SomeKeypair {
|
||||
let mut key = [KEY1, KEY2, KEY3].choose(g).unwrap().to_vec();
|
||||
SomeKeypair(Keypair::from_pkcs8(&mut key).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rsa_from_pkcs8() {
|
||||
assert!(Keypair::from_pkcs8(&mut KEY1.to_vec()).is_ok());
|
||||
assert!(Keypair::from_pkcs8(&mut KEY2.to_vec()).is_ok());
|
||||
assert!(Keypair::from_pkcs8(&mut KEY3.to_vec()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rsa_x509_encode_decode() {
|
||||
fn prop(SomeKeypair(kp): SomeKeypair) -> Result<bool, String> {
|
||||
let pk = kp.public();
|
||||
PublicKey::decode_x509(&pk.encode_x509())
|
||||
.map_err(|e| e.to_string())
|
||||
.map(|pk2| pk2 == pk)
|
||||
}
|
||||
QuickCheck::new().tests(10).quickcheck(prop as fn(_) -> _);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rsa_sign_verify() {
|
||||
fn prop(SomeKeypair(kp): SomeKeypair, msg: Vec<u8>) -> Result<bool, SigningError> {
|
||||
kp.sign(&msg).map(|s| kp.public().verify(&msg, &s))
|
||||
}
|
||||
QuickCheck::new().tests(10).quickcheck(prop as fn(_,_) -> _);
|
||||
}
|
||||
}
|
||||
|
158
core/src/identity/secp256k1.rs
Normal file
158
core/src/identity/secp256k1.rs
Normal file
@ -0,0 +1,158 @@
|
||||
// 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.
|
||||
|
||||
//! Secp256k1 keys.
|
||||
|
||||
use asn1_der::{FromDerObject, DerObject};
|
||||
use lazy_static::lazy_static;
|
||||
use sha2::{Digest as ShaDigestTrait, Sha256};
|
||||
use secp256k1 as secp;
|
||||
use secp::{Message, Signature};
|
||||
use super::error::DecodingError;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
// Cached `Secp256k1` context, to avoid recreating it every time.
|
||||
lazy_static! {
|
||||
static ref SECP: secp::Secp256k1<secp::All> = secp::Secp256k1::new();
|
||||
}
|
||||
|
||||
/// A Secp256k1 keypair.
|
||||
#[derive(Clone)]
|
||||
pub struct Keypair {
|
||||
secret: SecretKey,
|
||||
public: PublicKey
|
||||
}
|
||||
|
||||
impl Keypair {
|
||||
/// Generate a new sec256k1 `Keypair`.
|
||||
pub fn generate() -> Keypair {
|
||||
Keypair::from(SecretKey::generate())
|
||||
}
|
||||
|
||||
/// Get the public key of this keypair.
|
||||
pub fn public(&self) -> &PublicKey {
|
||||
&self.public
|
||||
}
|
||||
|
||||
/// Get the secret key of this keypair.
|
||||
pub fn secret(&self) -> &SecretKey {
|
||||
&self.secret
|
||||
}
|
||||
}
|
||||
|
||||
/// Promote a Secp256k1 secret key into a keypair.
|
||||
impl From<SecretKey> for Keypair {
|
||||
fn from(secret: SecretKey) -> Keypair {
|
||||
let public = PublicKey(secp::key::PublicKey::from_secret_key(&SECP, &secret.0));
|
||||
Keypair { secret, public }
|
||||
}
|
||||
}
|
||||
|
||||
/// Demote a Secp256k1 keypair into a secret key.
|
||||
impl From<Keypair> for SecretKey {
|
||||
fn from(kp: Keypair) -> SecretKey {
|
||||
kp.secret
|
||||
}
|
||||
}
|
||||
|
||||
/// A Secp256k1 secret key.
|
||||
#[derive(Clone)]
|
||||
pub struct SecretKey(secp::key::SecretKey);
|
||||
|
||||
/// View the bytes of the secret key.
|
||||
impl AsRef<[u8]> for SecretKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl SecretKey {
|
||||
/// Generate a new Secp256k1 secret key.
|
||||
pub fn generate() -> SecretKey {
|
||||
SecretKey(secp::key::SecretKey::new(&mut secp::rand::thread_rng()))
|
||||
}
|
||||
|
||||
/// Create a secret key from a byte slice, zeroing the slice on success.
|
||||
/// If the bytes do not constitute a valid Secp256k1 secret key, an
|
||||
/// error is returned.
|
||||
pub fn from_bytes(mut sk: impl AsMut<[u8]>) -> Result<SecretKey, DecodingError> {
|
||||
let sk_bytes = sk.as_mut();
|
||||
let secret = secp::key::SecretKey::from_slice(&*sk_bytes)
|
||||
.map_err(|e| DecodingError::new("Secp256k1 secret key", e))?;
|
||||
Ok(SecretKey(secret))
|
||||
}
|
||||
|
||||
/// Decode a DER-encoded Secp256k1 secret key in an ECPrivateKey
|
||||
/// structure as defined in [RFC5915].
|
||||
///
|
||||
/// [RFC5915]: https://tools.ietf.org/html/rfc5915
|
||||
pub fn from_der(mut der: impl AsMut<[u8]>) -> Result<SecretKey, DecodingError> {
|
||||
// TODO: Stricter parsing.
|
||||
let der_obj = der.as_mut();
|
||||
let obj: Vec<DerObject> = FromDerObject::deserialize((&*der_obj).iter())
|
||||
.map_err(|e| DecodingError::new("Secp256k1 DER ECPrivateKey", e))?;
|
||||
der_obj.zeroize();
|
||||
let sk_obj = obj.into_iter().nth(1)
|
||||
.ok_or_else(|| "Not enough elements in DER".to_string())?;
|
||||
let mut sk_bytes: Vec<u8> = FromDerObject::from_der_object(sk_obj)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let sk = SecretKey::from_bytes(&mut sk_bytes)?;
|
||||
sk_bytes.zeroize();
|
||||
Ok(sk)
|
||||
}
|
||||
|
||||
/// Sign a message with this secret key, producing a DER-encoded
|
||||
/// ECDSA signature, as defined in [RFC3278].
|
||||
///
|
||||
/// [RFC3278]: https://tools.ietf.org/html/rfc3278#section-8.2
|
||||
pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
|
||||
let m = Message::from_slice(Sha256::digest(&msg).as_ref())
|
||||
.expect("digest output length doesn't match secp256k1 input length");
|
||||
SECP.sign(&m, &self.0).serialize_der()
|
||||
}
|
||||
}
|
||||
|
||||
/// A Secp256k1 public key.
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct PublicKey(secp::key::PublicKey);
|
||||
|
||||
impl PublicKey {
|
||||
/// Verify the Secp256k1 signature on a message using the public key.
|
||||
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool {
|
||||
Message::from_slice(&Sha256::digest(msg))
|
||||
.and_then(|m| Signature::from_der(sig)
|
||||
.and_then(|s| SECP.verify(&m, &s, &self.0))).is_ok()
|
||||
}
|
||||
|
||||
/// Encode the public key in compressed form, i.e. with one coordinate
|
||||
/// represented by a single bit.
|
||||
pub fn encode(&self) -> [u8; 33] {
|
||||
self.0.serialize()
|
||||
}
|
||||
|
||||
/// Decode a public key from a byte slice in the the format produced
|
||||
/// by `encode`.
|
||||
pub fn decode(k: &[u8]) -> Result<PublicKey, DecodingError> {
|
||||
secp256k1::PublicKey::from_slice(k)
|
||||
.map_err(|e| DecodingError::new("Secp256k1 public key", e))
|
||||
.map(PublicKey)
|
||||
}
|
||||
}
|
||||
|
BIN
core/src/identity/test/rsa-2048.pk8
Normal file
BIN
core/src/identity/test/rsa-2048.pk8
Normal file
Binary file not shown.
BIN
core/src/identity/test/rsa-3072.pk8
Normal file
BIN
core/src/identity/test/rsa-3072.pk8
Normal file
Binary file not shown.
BIN
core/src/identity/test/rsa-4096.pk8
Normal file
BIN
core/src/identity/test/rsa-4096.pk8
Normal file
Binary file not shown.
@ -67,12 +67,12 @@ pub use multiaddr;
|
||||
|
||||
mod keys_proto;
|
||||
mod peer_id;
|
||||
mod public_key;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub mod either;
|
||||
pub mod identity;
|
||||
pub mod muxing;
|
||||
pub mod nodes;
|
||||
pub mod protocols_handler;
|
||||
@ -84,7 +84,7 @@ pub use self::multiaddr::Multiaddr;
|
||||
pub use self::muxing::StreamMuxer;
|
||||
pub use self::peer_id::PeerId;
|
||||
pub use self::protocols_handler::{ProtocolsHandler, ProtocolsHandlerEvent};
|
||||
pub use self::public_key::PublicKey;
|
||||
pub use self::identity::PublicKey;
|
||||
pub use self::swarm::Swarm;
|
||||
pub use self::transport::Transport;
|
||||
pub use self::upgrade::{InboundUpgrade, OutboundUpgrade, UpgradeInfo, UpgradeError, ProtocolName};
|
||||
|
@ -42,9 +42,9 @@ impl fmt::Debug for PeerId {
|
||||
impl PeerId {
|
||||
/// Builds a `PeerId` from a public key.
|
||||
#[inline]
|
||||
pub fn from_public_key(public_key: PublicKey) -> PeerId {
|
||||
let protobuf = public_key.into_protobuf_encoding();
|
||||
let multihash = multihash::encode(multihash::Hash::SHA2256, &protobuf)
|
||||
pub fn from_public_key(key: PublicKey) -> PeerId {
|
||||
let key_enc = key.into_protobuf_encoding();
|
||||
let multihash = multihash::encode(multihash::Hash::SHA2256, &key_enc)
|
||||
.expect("sha2-256 is always supported");
|
||||
PeerId { multihash }
|
||||
}
|
||||
@ -120,9 +120,10 @@ impl PeerId {
|
||||
/// given public key, otherwise `Some` boolean as the result of an equality check.
|
||||
pub fn is_public_key(&self, public_key: &PublicKey) -> Option<bool> {
|
||||
let alg = self.multihash.algorithm();
|
||||
match multihash::encode(alg, &public_key.clone().into_protobuf_encoding()) {
|
||||
Ok(compare) => Some(compare == self.multihash),
|
||||
Err(multihash::EncodeError::UnsupportedType) => None,
|
||||
let enc = public_key.clone().into_protobuf_encoding();
|
||||
match multihash::encode(alg, &enc) {
|
||||
Ok(h) => Some(h == self.multihash),
|
||||
Err(multihash::EncodeError::UnsupportedType) => None
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -195,26 +196,25 @@ impl FromStr for PeerId {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rand::random;
|
||||
use crate::{PeerId, PublicKey};
|
||||
use crate::{PeerId, identity};
|
||||
|
||||
#[test]
|
||||
fn peer_id_is_public_key() {
|
||||
let key = PublicKey::Rsa((0 .. 2048).map(|_| -> u8 { random() }).collect());
|
||||
let peer_id = PeerId::from_public_key(key.clone());
|
||||
let key = identity::Keypair::generate_ed25519().public();
|
||||
let peer_id = key.clone().into_peer_id();
|
||||
assert_eq!(peer_id.is_public_key(&key), Some(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn peer_id_into_bytes_then_from_bytes() {
|
||||
let peer_id = PublicKey::Rsa((0 .. 2048).map(|_| -> u8 { random() }).collect()).into_peer_id();
|
||||
let peer_id = identity::Keypair::generate_ed25519().public().into_peer_id();
|
||||
let second = PeerId::from_bytes(peer_id.clone().into_bytes()).unwrap();
|
||||
assert_eq!(peer_id, second);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn peer_id_to_base58_then_back() {
|
||||
let peer_id = PublicKey::Rsa((0 .. 2048).map(|_| -> u8 { random() }).collect()).into_peer_id();
|
||||
let peer_id = identity::Keypair::generate_ed25519().public().into_peer_id();
|
||||
let second: PeerId = peer_id.to_base58().parse().unwrap();
|
||||
assert_eq!(peer_id, second);
|
||||
}
|
||||
|
@ -1,108 +0,0 @@
|
||||
// Copyright 2018 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_proto, PeerId};
|
||||
use log::debug;
|
||||
use protobuf::{self, Message};
|
||||
use std::io::{Error as IoError, ErrorKind as IoErrorKind};
|
||||
|
||||
/// Public key used by the remote.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum PublicKey {
|
||||
/// DER format.
|
||||
Rsa(Vec<u8>),
|
||||
/// Format = ???
|
||||
// TODO: ^
|
||||
Ed25519(Vec<u8>),
|
||||
/// Format = ???
|
||||
// TODO: ^
|
||||
Secp256k1(Vec<u8>),
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
/// Encodes the public key as a protobuf message.
|
||||
///
|
||||
/// Used at various locations in the wire protocol of libp2p.
|
||||
#[inline]
|
||||
pub fn into_protobuf_encoding(self) -> Vec<u8> {
|
||||
let mut public_key = keys_proto::PublicKey::new();
|
||||
match self {
|
||||
PublicKey::Rsa(data) => {
|
||||
public_key.set_Type(keys_proto::KeyType::RSA);
|
||||
public_key.set_Data(data);
|
||||
},
|
||||
PublicKey::Ed25519(data) => {
|
||||
public_key.set_Type(keys_proto::KeyType::Ed25519);
|
||||
public_key.set_Data(data);
|
||||
},
|
||||
PublicKey::Secp256k1(data) => {
|
||||
public_key.set_Type(keys_proto::KeyType::Secp256k1);
|
||||
public_key.set_Data(data);
|
||||
},
|
||||
};
|
||||
|
||||
public_key
|
||||
.write_to_bytes()
|
||||
.expect("protobuf writing should always be valid")
|
||||
}
|
||||
|
||||
/// Decodes the public key from a protobuf message.
|
||||
///
|
||||
/// Used at various locations in the wire protocol of libp2p.
|
||||
#[inline]
|
||||
pub fn from_protobuf_encoding(bytes: &[u8]) -> Result<PublicKey, IoError> {
|
||||
let mut pubkey = protobuf::parse_from_bytes::<keys_proto::PublicKey>(bytes)
|
||||
.map_err(|err| {
|
||||
debug!("failed to parse public key's protobuf encoding");
|
||||
IoError::new(IoErrorKind::InvalidData, err)
|
||||
})?;
|
||||
|
||||
Ok(match pubkey.get_Type() {
|
||||
keys_proto::KeyType::RSA => {
|
||||
PublicKey::Rsa(pubkey.take_Data())
|
||||
},
|
||||
keys_proto::KeyType::Ed25519 => {
|
||||
PublicKey::Ed25519(pubkey.take_Data())
|
||||
},
|
||||
keys_proto::KeyType::Secp256k1 => {
|
||||
PublicKey::Secp256k1(pubkey.take_Data())
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Builds a `PeerId` corresponding to the public key of the node.
|
||||
#[inline]
|
||||
pub fn into_peer_id(self) -> PeerId {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rand::random;
|
||||
use crate::PublicKey;
|
||||
|
||||
#[test]
|
||||
fn key_into_protobuf_then_back() {
|
||||
let key = PublicKey::Rsa((0 .. 2048).map(|_| -> u8 { random() }).collect());
|
||||
let second = PublicKey::from_protobuf_encoding(&key.clone().into_protobuf_encoding()).unwrap();
|
||||
assert_eq!(key, second);
|
||||
}
|
||||
}
|
@ -549,13 +549,11 @@ where TBehaviour: NetworkBehaviour,
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::peer_id::PeerId;
|
||||
use crate::{identity, PeerId, PublicKey};
|
||||
use crate::protocols_handler::{DummyProtocolsHandler, ProtocolsHandler};
|
||||
use crate::public_key::PublicKey;
|
||||
use crate::tests::dummy_transport::DummyTransport;
|
||||
use futures::prelude::*;
|
||||
use multiaddr::Multiaddr;
|
||||
use rand::random;
|
||||
use std::marker::PhantomData;
|
||||
use super::{ConnectedPoint, NetworkBehaviour, NetworkBehaviourAction,
|
||||
PollParameters, SwarmBuilder};
|
||||
@ -601,10 +599,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn get_random_id() -> PublicKey {
|
||||
PublicKey::Rsa((0 .. 2048)
|
||||
.map(|_| -> u8 { random() })
|
||||
.collect()
|
||||
)
|
||||
identity::Keypair::generate_ed25519().public()
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -612,8 +607,8 @@ mod tests {
|
||||
let id = get_random_id();
|
||||
let transport = DummyTransport::new();
|
||||
let behaviour = DummyBehaviour{marker: PhantomData};
|
||||
let swarm = SwarmBuilder::new(transport, behaviour,
|
||||
id.into_peer_id()).incoming_limit(Some(4)).build();
|
||||
let swarm = SwarmBuilder::new(transport, behaviour, id.into())
|
||||
.incoming_limit(Some(4)).build();
|
||||
assert_eq!(swarm.raw_swarm.incoming_limit(), Some(4));
|
||||
}
|
||||
|
||||
@ -622,8 +617,7 @@ mod tests {
|
||||
let id = get_random_id();
|
||||
let transport = DummyTransport::new();
|
||||
let behaviour = DummyBehaviour{marker: PhantomData};
|
||||
let swarm = SwarmBuilder::new(transport, behaviour, id.into_peer_id())
|
||||
.build();
|
||||
let swarm = SwarmBuilder::new(transport, behaviour, id.into()).build();
|
||||
assert!(swarm.raw_swarm.incoming_limit().is_none())
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use futures::{future, prelude::*};
|
||||
use libp2p_core::identity;
|
||||
use libp2p_core::multiaddr::multiaddr;
|
||||
use libp2p_core::nodes::raw_swarm::{RawSwarm, RawSwarmEvent, RawSwarmReachError, PeerState, UnknownPeerDialErr, IncomingError};
|
||||
use libp2p_core::{PeerId, Transport, upgrade, upgrade::InboundUpgradeExt, upgrade::OutboundUpgradeExt};
|
||||
@ -92,8 +93,8 @@ fn deny_incoming_connec() {
|
||||
// TODO: make creating the transport more elegant ; literaly half of the code of the test
|
||||
// is about creating the transport
|
||||
let mut swarm1: RawSwarm<_, _, _, NodeHandlerWrapperBuilder<TestHandler<_>>, _> = {
|
||||
let local_key = libp2p_secio::SecioKeyPair::ed25519_generated().unwrap();
|
||||
let local_public_key = local_key.to_public_key();
|
||||
let local_key = identity::Keypair::generate_ed25519();
|
||||
let local_public_key = local_key.public();
|
||||
let transport = libp2p_tcp::TcpConfig::new()
|
||||
.with_upgrade(libp2p_secio::SecioConfig::new(local_key))
|
||||
.and_then(move |out, endpoint| {
|
||||
@ -104,12 +105,12 @@ fn deny_incoming_connec() {
|
||||
.map_inbound(move |muxer| (peer_id2, muxer));
|
||||
upgrade::apply(out.stream, upgrade, endpoint)
|
||||
});
|
||||
RawSwarm::new(transport, local_public_key.into_peer_id())
|
||||
RawSwarm::new(transport, local_public_key.into())
|
||||
};
|
||||
|
||||
let mut swarm2 = {
|
||||
let local_key = libp2p_secio::SecioKeyPair::ed25519_generated().unwrap();
|
||||
let local_public_key = local_key.to_public_key();
|
||||
let local_key = identity::Keypair::generate_ed25519();
|
||||
let local_public_key = local_key.public();
|
||||
let transport = libp2p_tcp::TcpConfig::new()
|
||||
.with_upgrade(libp2p_secio::SecioConfig::new(local_key))
|
||||
.and_then(move |out, endpoint| {
|
||||
@ -120,7 +121,7 @@ fn deny_incoming_connec() {
|
||||
.map_inbound(move |muxer| (peer_id2, muxer));
|
||||
upgrade::apply(out.stream, upgrade, endpoint)
|
||||
});
|
||||
RawSwarm::new(transport, local_public_key.into_peer_id())
|
||||
RawSwarm::new(transport, local_public_key.into())
|
||||
};
|
||||
|
||||
let listen = swarm1.listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap()).unwrap();
|
||||
@ -173,8 +174,8 @@ fn dial_self() {
|
||||
// TODO: make creating the transport more elegant ; literaly half of the code of the test
|
||||
// is about creating the transport
|
||||
let mut swarm = {
|
||||
let local_key = libp2p_secio::SecioKeyPair::ed25519_generated().unwrap();
|
||||
let local_public_key = local_key.to_public_key();
|
||||
let local_key = identity::Keypair::generate_ed25519();
|
||||
let local_public_key = local_key.public();
|
||||
let transport = libp2p_tcp::TcpConfig::new()
|
||||
.with_upgrade(libp2p_secio::SecioConfig::new(local_key))
|
||||
.and_then(move |out, endpoint| {
|
||||
@ -185,7 +186,7 @@ fn dial_self() {
|
||||
.map_inbound(move |muxer| (peer_id2, muxer));
|
||||
upgrade::apply(out.stream, upgrade, endpoint)
|
||||
});
|
||||
RawSwarm::new(transport, local_public_key.into_peer_id())
|
||||
RawSwarm::new(transport, local_public_key.into())
|
||||
};
|
||||
|
||||
let listen = swarm.listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap()).unwrap();
|
||||
@ -241,8 +242,8 @@ fn dial_self_by_id() {
|
||||
// TODO: make creating the transport more elegant ; literaly half of the code of the test
|
||||
// is about creating the transport
|
||||
let mut swarm: RawSwarm<_, _, _, NodeHandlerWrapperBuilder<TestHandler<_>>, _> = {
|
||||
let local_key = libp2p_secio::SecioKeyPair::ed25519_generated().unwrap();
|
||||
let local_public_key = local_key.to_public_key();
|
||||
let local_key = identity::Keypair::generate_ed25519();
|
||||
let local_public_key = local_key.public();
|
||||
let transport = libp2p_tcp::TcpConfig::new()
|
||||
.with_upgrade(libp2p_secio::SecioConfig::new(local_key))
|
||||
.and_then(move |out, endpoint| {
|
||||
@ -253,7 +254,7 @@ fn dial_self_by_id() {
|
||||
.map_inbound(move |muxer| (peer_id2, muxer));
|
||||
upgrade::apply(out.stream, upgrade, endpoint)
|
||||
});
|
||||
RawSwarm::new(transport, local_public_key.into_peer_id())
|
||||
RawSwarm::new(transport, local_public_key.into())
|
||||
};
|
||||
|
||||
let peer_id = swarm.local_peer_id().clone();
|
||||
@ -267,8 +268,8 @@ fn multiple_addresses_err() {
|
||||
// TODO: make creating the transport more elegant ; literaly half of the code of the test
|
||||
// is about creating the transport
|
||||
let mut swarm = {
|
||||
let local_key = libp2p_secio::SecioKeyPair::ed25519_generated().unwrap();
|
||||
let local_public_key = local_key.to_public_key();
|
||||
let local_key = identity::Keypair::generate_ed25519();
|
||||
let local_public_key = local_key.public();
|
||||
let transport = libp2p_tcp::TcpConfig::new()
|
||||
.with_upgrade(libp2p_secio::SecioConfig::new(local_key))
|
||||
.and_then(move |out, endpoint| {
|
||||
@ -279,7 +280,7 @@ fn multiple_addresses_err() {
|
||||
.map_inbound(move |muxer| (peer_id2, muxer));
|
||||
upgrade::apply(out.stream, upgrade, endpoint)
|
||||
});
|
||||
RawSwarm::new(transport, local_public_key.into_peer_id())
|
||||
RawSwarm::new(transport, local_public_key.into())
|
||||
};
|
||||
|
||||
let mut addresses = Vec::new();
|
||||
|
@ -19,6 +19,7 @@
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use futures::{future, prelude::*};
|
||||
use libp2p_core::identity;
|
||||
use libp2p_core::nodes::raw_swarm::{RawSwarm, RawSwarmEvent, IncomingError};
|
||||
use libp2p_core::{Transport, upgrade, upgrade::OutboundUpgradeExt, upgrade::InboundUpgradeExt};
|
||||
use libp2p_core::protocols_handler::{ProtocolsHandler, KeepAlive, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr};
|
||||
@ -108,8 +109,8 @@ fn raw_swarm_simultaneous_connect() {
|
||||
// TODO: make creating the transport more elegant ; literaly half of the code of the test
|
||||
// is about creating the transport
|
||||
let mut swarm1 = {
|
||||
let local_key = libp2p_secio::SecioKeyPair::ed25519_generated().unwrap();
|
||||
let local_public_key = local_key.to_public_key();
|
||||
let local_key = identity::Keypair::generate_ed25519();
|
||||
let local_public_key = local_key.public();
|
||||
let transport = libp2p_tcp::TcpConfig::new()
|
||||
.with_upgrade(libp2p_secio::SecioConfig::new(local_key))
|
||||
.and_then(move |out, endpoint| {
|
||||
@ -124,8 +125,8 @@ fn raw_swarm_simultaneous_connect() {
|
||||
};
|
||||
|
||||
let mut swarm2 = {
|
||||
let local_key = libp2p_secio::SecioKeyPair::ed25519_generated().unwrap();
|
||||
let local_public_key = local_key.to_public_key();
|
||||
let local_key = identity::Keypair::generate_ed25519();
|
||||
let local_public_key = local_key.public();
|
||||
let transport = libp2p_tcp::TcpConfig::new()
|
||||
.with_upgrade(libp2p_secio::SecioConfig::new(local_key))
|
||||
.and_then(move |out, endpoint| {
|
||||
|
@ -51,8 +51,9 @@
|
||||
|
||||
use futures::prelude::*;
|
||||
use libp2p::{
|
||||
PeerId,
|
||||
NetworkBehaviour,
|
||||
secio,
|
||||
identity,
|
||||
tokio_codec::{FramedRead, LinesCodec}
|
||||
};
|
||||
|
||||
@ -60,8 +61,8 @@ fn main() {
|
||||
env_logger::init();
|
||||
|
||||
// Create a random PeerId
|
||||
let local_key = secio::SecioKeyPair::ed25519_generated().unwrap();
|
||||
let local_peer_id = local_key.to_peer_id();
|
||||
let local_key = identity::Keypair::generate_ed25519();
|
||||
let local_peer_id = PeerId::from(local_key.public());
|
||||
println!("Local peer id: {:?}", local_peer_id);
|
||||
|
||||
// Set up a an encrypted DNS-enabled TCP Transport over the Mplex and Yamux protocols
|
||||
|
@ -25,14 +25,14 @@
|
||||
|
||||
use futures::prelude::*;
|
||||
use libp2p::{
|
||||
core::PublicKey,
|
||||
secio,
|
||||
PeerId,
|
||||
identity
|
||||
};
|
||||
|
||||
fn main() {
|
||||
// Create a random key for ourselves.
|
||||
let local_key = secio::SecioKeyPair::ed25519_generated().unwrap();
|
||||
let local_peer_id = local_key.to_peer_id();
|
||||
let local_key = identity::Keypair::generate_ed25519();
|
||||
let local_peer_id = PeerId::from(local_key.public());
|
||||
|
||||
// Set up a an encrypted DNS-enabled TCP Transport over the Mplex protocol
|
||||
let transport = libp2p::build_development_transport(local_key);
|
||||
@ -58,10 +58,10 @@ fn main() {
|
||||
};
|
||||
|
||||
// Order Kademlia to search for a peer.
|
||||
let to_search = if let Some(peer_id) = std::env::args().nth(1) {
|
||||
let to_search: PeerId = if let Some(peer_id) = std::env::args().nth(1) {
|
||||
peer_id.parse().expect("Failed to parse peer ID to find")
|
||||
} else {
|
||||
PublicKey::Secp256k1((0..32).map(|_| -> u8 { rand::random() }).collect()).into_peer_id()
|
||||
identity::Keypair::generate_ed25519().public().into()
|
||||
};
|
||||
println!("Searching for {:?}", to_search);
|
||||
swarm.find_node(to_search);
|
||||
|
@ -337,7 +337,7 @@ impl error::Error for MdnsResponseError {}
|
||||
mod tests {
|
||||
use super::*;
|
||||
use dns_parser::Packet;
|
||||
use libp2p_core::{PeerId, PublicKey};
|
||||
use libp2p_core::identity;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
@ -348,7 +348,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn build_query_response_correct() {
|
||||
let my_peer_id = PeerId::from_public_key(PublicKey::Rsa(vec![1, 2, 3, 4]));
|
||||
let my_peer_id = identity::Keypair::generate_ed25519().public().into_peer_id();
|
||||
let addr1 = "/ip4/1.2.3.4/tcp/5000".parse().unwrap();
|
||||
let addr2 = "/ip6/::1/udp/10000".parse().unwrap();
|
||||
let query = build_query_response(
|
||||
|
@ -53,10 +53,11 @@ pub use dns::MdnsResponseError;
|
||||
///
|
||||
/// ```rust
|
||||
/// # use futures::prelude::*;
|
||||
/// # use libp2p_core::{identity, PeerId};
|
||||
/// # use libp2p_mdns::service::{MdnsService, MdnsPacket};
|
||||
/// # use std::{io, time::Duration};
|
||||
/// # fn main() {
|
||||
/// # let my_peer_id = libp2p_core::PublicKey::Rsa(vec![1, 2, 3, 4]).into_peer_id();
|
||||
/// # let my_peer_id = PeerId::from(identity::Keypair::generate_ed25519().public());
|
||||
/// # let my_listened_addrs = Vec::new();
|
||||
/// let mut service = MdnsService::new().expect("Error while creating mDNS service");
|
||||
/// let _future_to_poll = futures::stream::poll_fn(move || -> Poll<Option<()>, io::Error> {
|
||||
@ -536,7 +537,7 @@ impl<'a> fmt::Debug for MdnsPeer<'a> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use libp2p_core::PublicKey;
|
||||
use libp2p_core::PeerId;
|
||||
use std::{io, time::Duration};
|
||||
use tokio::{self, prelude::*};
|
||||
use crate::service::{MdnsPacket, MdnsService};
|
||||
@ -544,8 +545,7 @@ mod tests {
|
||||
#[test]
|
||||
fn discover_ourselves() {
|
||||
let mut service = MdnsService::new().unwrap();
|
||||
let peer_id =
|
||||
PublicKey::Rsa((0..32).map(|_| rand::random::<u8>()).collect()).into_peer_id();
|
||||
let peer_id = PeerId::random();
|
||||
let stream = stream::poll_fn(move || -> Poll<Option<()>, io::Error> {
|
||||
loop {
|
||||
let packet = match service.poll() {
|
||||
|
@ -18,8 +18,7 @@
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use libp2p_core::PeerId;
|
||||
use libp2p_secio::SecioKeyPair;
|
||||
use libp2p_core::identity;
|
||||
use std::{env, str, thread, time::Duration};
|
||||
|
||||
fn main() {
|
||||
@ -67,13 +66,13 @@ fn main() {
|
||||
for _ in 0..num_cpus::get() {
|
||||
let prefix = prefix.clone();
|
||||
thread::spawn(move || loop {
|
||||
let private_key: [u8; 32] = rand::random();
|
||||
let generated = SecioKeyPair::secp256k1_raw_key(private_key).unwrap();
|
||||
let peer_id: PeerId = generated.to_public_key().into_peer_id();
|
||||
let keypair = identity::ed25519::Keypair::generate();
|
||||
let secret = keypair.secret();
|
||||
let peer_id = identity::PublicKey::Ed25519(keypair.public()).into_peer_id();
|
||||
let base58 = peer_id.to_base58();
|
||||
if base58[2..].starts_with(&prefix) {
|
||||
println!("Found {:?}", peer_id);
|
||||
println!("=> Private key = {:?}", private_key);
|
||||
println!("=> Private key = {:?}", secret.as_ref());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -221,15 +221,16 @@ pub enum IdentifyEvent {
|
||||
mod tests {
|
||||
use crate::{Identify, IdentifyEvent};
|
||||
use futures::prelude::*;
|
||||
use libp2p_core::identity;
|
||||
use libp2p_core::{upgrade, upgrade::OutboundUpgradeExt, upgrade::InboundUpgradeExt, Swarm, Transport};
|
||||
use std::io;
|
||||
|
||||
#[test]
|
||||
fn periodic_id_works() {
|
||||
let node1_key = libp2p_secio::SecioKeyPair::ed25519_generated().unwrap();
|
||||
let node1_public_key = node1_key.to_public_key();
|
||||
let node2_key = libp2p_secio::SecioKeyPair::ed25519_generated().unwrap();
|
||||
let node2_public_key = node2_key.to_public_key();
|
||||
let node1_key = identity::Keypair::generate_ed25519();
|
||||
let node1_public_key = node1_key.public();
|
||||
let node2_key = identity::Keypair::generate_ed25519();
|
||||
let node2_public_key = node2_key.public();
|
||||
|
||||
let mut swarm1 = {
|
||||
// TODO: make creating the transport more elegant ; literaly half of the code of the test
|
||||
@ -253,7 +254,7 @@ mod tests {
|
||||
let mut swarm2 = {
|
||||
// TODO: make creating the transport more elegant ; literaly half of the code of the test
|
||||
// is about creating the transport
|
||||
let local_peer_id = node2_public_key.clone().into_peer_id();
|
||||
let local_peer_id = node2_public_key.clone().into();
|
||||
let transport = libp2p_tcp::TcpConfig::new()
|
||||
.with_upgrade(libp2p_secio::SecioConfig::new(node2_key))
|
||||
.and_then(move |out, endpoint| {
|
||||
|
@ -67,10 +67,12 @@ impl<T> IdentifySender<T> where T: AsyncWrite {
|
||||
.map(|addr| addr.into_bytes())
|
||||
.collect();
|
||||
|
||||
let pubkey_bytes = info.public_key.into_protobuf_encoding();
|
||||
|
||||
let mut message = structs_proto::Identify::new();
|
||||
message.set_agentVersion(info.agent_version);
|
||||
message.set_protocolVersion(info.protocol_version);
|
||||
message.set_publicKey(info.public_key.into_protobuf_encoding());
|
||||
message.set_publicKey(pubkey_bytes);
|
||||
message.set_listenAddrs(listen_addrs);
|
||||
message.set_observedAddr(observed_addr.to_bytes());
|
||||
message.set_protocols(RepeatedField::from_vec(info.protocols));
|
||||
@ -244,9 +246,12 @@ fn parse_proto_msg(msg: BytesMut) -> Result<(IdentifyInfo, Multiaddr), IoError>
|
||||
addrs
|
||||
};
|
||||
|
||||
let public_key = PublicKey::from_protobuf_encoding(msg.get_publicKey())
|
||||
.map_err(|e| IoError::new(IoErrorKind::InvalidData, e))?;
|
||||
|
||||
let observed_addr = bytes_to_multiaddr(msg.take_observedAddr())?;
|
||||
let info = IdentifyInfo {
|
||||
public_key: PublicKey::from_protobuf_encoding(msg.get_publicKey())?,
|
||||
public_key,
|
||||
protocol_version: msg.take_protocolVersion(),
|
||||
agent_version: msg.take_agentVersion(),
|
||||
listen_addrs,
|
||||
@ -266,13 +271,15 @@ mod tests {
|
||||
use tokio::runtime::current_thread::Runtime;
|
||||
use libp2p_tcp::TcpConfig;
|
||||
use futures::{Future, Stream};
|
||||
use libp2p_core::{PublicKey, Transport, upgrade::{apply_outbound, apply_inbound}};
|
||||
use libp2p_core::{identity, Transport, upgrade::{apply_outbound, apply_inbound}};
|
||||
use std::{io, sync::mpsc, thread};
|
||||
|
||||
#[test]
|
||||
fn correct_transfer() {
|
||||
// We open a server and a client, send info from the server to the client, and check that
|
||||
// they were successfully received.
|
||||
let send_pubkey = identity::Keypair::generate_ed25519().public();
|
||||
let recv_pubkey = send_pubkey.clone();
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
@ -296,7 +303,7 @@ mod tests {
|
||||
.and_then(|sender| {
|
||||
sender.send(
|
||||
IdentifyInfo {
|
||||
public_key: PublicKey::Ed25519(vec![1, 2, 3, 4, 5, 7]),
|
||||
public_key: send_pubkey,
|
||||
protocol_version: "proto_version".to_owned(),
|
||||
agent_version: "agent_version".to_owned(),
|
||||
listen_addrs: vec![
|
||||
@ -322,7 +329,7 @@ mod tests {
|
||||
})
|
||||
.and_then(|RemoteInfo { info, observed_addr, .. }| {
|
||||
assert_eq!(observed_addr, "/ip4/100.101.102.103/tcp/5000".parse().unwrap());
|
||||
assert_eq!(info.public_key, PublicKey::Ed25519(vec![1, 2, 3, 4, 5, 7]));
|
||||
assert_eq!(info.public_key, recv_pubkey);
|
||||
assert_eq!(info.protocol_version, "proto_version");
|
||||
assert_eq!(info.agent_version, "agent_version");
|
||||
assert_eq!(info.listen_addrs,
|
||||
|
@ -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"
|
||||
|
@ -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 }))
|
||||
}
|
||||
}
|
||||
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
173
protocols/noise/src/protocol.rs
Normal file
173
protocols/noise/src/protocol.rs
Normal 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 {}
|
||||
|
307
protocols/noise/src/protocol/x25519.rs
Normal file
307
protocols/noise/src/protocol/x25519.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -26,7 +26,6 @@ lazy_static = "1.2.0"
|
||||
rw-stream-sink = { version = "0.1.0", path = "../../misc/rw-stream-sink" }
|
||||
tokio-io = "0.1.0"
|
||||
sha2 = "0.8.0"
|
||||
ed25519-dalek = "1.0.0-pre.1"
|
||||
hmac = "0.7.0"
|
||||
|
||||
[target.'cfg(not(any(target_os = "emscripten", target_os = "unknown")))'.dependencies]
|
||||
|
@ -33,7 +33,7 @@ pub enum SecioError {
|
||||
IoError(IoError),
|
||||
|
||||
/// Protocol buffer error.
|
||||
Protobuf(ProtobufError),
|
||||
ProtobufError(ProtobufError),
|
||||
|
||||
/// Failed to parse one of the handshake protobuf messages.
|
||||
HandshakeParsingFailure,
|
||||
@ -79,7 +79,7 @@ impl error::Error for SecioError {
|
||||
fn cause(&self) -> Option<&dyn error::Error> {
|
||||
match *self {
|
||||
SecioError::IoError(ref err) => Some(err),
|
||||
SecioError::Protobuf(ref err) => Some(err),
|
||||
SecioError::ProtobufError(ref err) => Some(err),
|
||||
// TODO: The type doesn't implement `Error`
|
||||
/*SecioError::CipherError(ref err) => {
|
||||
Some(err)
|
||||
@ -95,8 +95,8 @@ impl fmt::Display for SecioError {
|
||||
match self {
|
||||
SecioError::IoError(e) =>
|
||||
write!(f, "I/O error: {}", e),
|
||||
SecioError::Protobuf(e) =>
|
||||
write!(f, "protobuf error: {}", e),
|
||||
SecioError::ProtobufError(e) =>
|
||||
write!(f, "Protobuf error: {}", e),
|
||||
SecioError::HandshakeParsingFailure =>
|
||||
f.write_str("Failed to parse one of the handshake protobuf messages"),
|
||||
SecioError::NoSupportIntersection =>
|
||||
@ -144,6 +144,6 @@ impl From<IoError> for SecioError {
|
||||
impl From<ProtobufError> for SecioError {
|
||||
#[inline]
|
||||
fn from(err: ProtobufError) -> SecioError {
|
||||
SecioError::Protobuf(err)
|
||||
SecioError::ProtobufError(err)
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ use crate::algo_support;
|
||||
use bytes::BytesMut;
|
||||
use crate::codec::{full_codec, FullCodec, Hmac};
|
||||
use crate::stream_cipher::{Cipher, ctr};
|
||||
use ed25519_dalek::{PublicKey as Ed25519PublicKey, Signature as Ed25519Signature};
|
||||
use crate::error::SecioError;
|
||||
use crate::exchange;
|
||||
use futures::future;
|
||||
@ -34,23 +33,13 @@ use log::{debug, trace};
|
||||
use protobuf::parse_from_bytes as protobuf_parse_from_bytes;
|
||||
use protobuf::Message as ProtobufMessage;
|
||||
use rand::{self, RngCore};
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
use ring::signature::{RSA_PKCS1_2048_8192_SHA256, RSA_PKCS1_SHA256, verify as ring_verify};
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
use ring::rand::SystemRandom;
|
||||
#[cfg(feature = "secp256k1")]
|
||||
use secp256k1;
|
||||
use sha2::{Digest as ShaDigestTrait, Sha256};
|
||||
use std::cmp::{self, Ordering};
|
||||
use std::io::{Error as IoError, ErrorKind as IoErrorKind};
|
||||
use crate::structs_proto::{Exchange, Propose};
|
||||
use tokio_io::codec::length_delimited;
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
use untrusted::Input as UntrustedInput;
|
||||
use crate::{KeyAgreement, SecioConfig, SecioKeyPairInner};
|
||||
#[cfg(feature = "secp256k1")]
|
||||
use crate::SECP256K1;
|
||||
use crate::{KeyAgreement, SecioConfig};
|
||||
|
||||
// This struct contains the whole context of a handshake, and is filled progressively
|
||||
// throughout the various parts of the handshake.
|
||||
@ -63,8 +52,8 @@ struct HandshakeContext<T> {
|
||||
struct Local {
|
||||
// Locally-generated random number. The array size can be changed without any repercussion.
|
||||
nonce: [u8; 16],
|
||||
// Our local public key protobuf structure encoded in bytes:
|
||||
public_key_in_protobuf_bytes: Vec<u8>,
|
||||
// Our encoded local public key
|
||||
public_key_encoded: Vec<u8>,
|
||||
// Our local proposition's raw bytes:
|
||||
proposition_bytes: Vec<u8>
|
||||
}
|
||||
@ -124,13 +113,12 @@ impl HandshakeContext<()> {
|
||||
.try_fill_bytes(&mut nonce)
|
||||
.map_err(|_| SecioError::NonceGenerationFailed)?;
|
||||
|
||||
let public_key_in_protobuf_bytes =
|
||||
self.config.key.to_public_key().into_protobuf_encoding();
|
||||
let public_key_encoded = self.config.key.public().into_protobuf_encoding();
|
||||
|
||||
// Send our proposition with our nonce, public key and supported protocols.
|
||||
let mut proposition = Propose::new();
|
||||
proposition.set_rand(nonce.to_vec());
|
||||
proposition.set_pubkey(public_key_in_protobuf_bytes.clone());
|
||||
proposition.set_pubkey(public_key_encoded.clone());
|
||||
|
||||
if let Some(ref p) = self.config.agreements_prop {
|
||||
trace!("agreements proposition: {}", p);
|
||||
@ -162,7 +150,7 @@ impl HandshakeContext<()> {
|
||||
config: self.config,
|
||||
state: Local {
|
||||
nonce,
|
||||
public_key_in_protobuf_bytes,
|
||||
public_key_encoded,
|
||||
proposition_bytes
|
||||
}
|
||||
})
|
||||
@ -180,10 +168,10 @@ impl HandshakeContext<Local> {
|
||||
}
|
||||
};
|
||||
|
||||
let public_key_in_protobuf_bytes = prop.take_pubkey();
|
||||
let public_key_encoded = prop.take_pubkey();
|
||||
let nonce = prop.take_rand();
|
||||
|
||||
let pubkey = match PublicKey::from_protobuf_encoding(&public_key_in_protobuf_bytes) {
|
||||
let pubkey = match PublicKey::from_protobuf_encoding(&public_key_encoded) {
|
||||
Ok(p) => p,
|
||||
Err(_) => {
|
||||
debug!("failed to parse remote's proposition's pubkey protobuf");
|
||||
@ -196,14 +184,14 @@ impl HandshakeContext<Local> {
|
||||
let hashes_ordering = {
|
||||
let oh1 = {
|
||||
let mut ctx = Sha256::new();
|
||||
ctx.input(&public_key_in_protobuf_bytes);
|
||||
ctx.input(&public_key_encoded);
|
||||
ctx.input(&self.state.nonce);
|
||||
ctx.result()
|
||||
};
|
||||
|
||||
let oh2 = {
|
||||
let mut ctx = Sha256::new();
|
||||
ctx.input(&self.state.public_key_in_protobuf_bytes);
|
||||
ctx.input(&self.state.public_key_encoded);
|
||||
ctx.input(&nonce);
|
||||
ctx.result()
|
||||
};
|
||||
@ -368,37 +356,10 @@ where
|
||||
|
||||
let mut exchange = Exchange::new();
|
||||
exchange.set_epubkey(tmp_pub_key);
|
||||
exchange.set_signature({
|
||||
match context.config.key.inner {
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
SecioKeyPairInner::Rsa { ref private, .. } => {
|
||||
let mut signature = vec![0; private.public_modulus_len()];
|
||||
let rng = SystemRandom::new();
|
||||
match private.sign(&RSA_PKCS1_SHA256, &rng, &data_to_sign, &mut signature) {
|
||||
Ok(_) => (),
|
||||
Err(_) => {
|
||||
debug!("failed to sign local exchange");
|
||||
return Err(SecioError::SigningFailure);
|
||||
},
|
||||
};
|
||||
|
||||
signature
|
||||
},
|
||||
SecioKeyPairInner::Ed25519 { ref key_pair } => {
|
||||
let signature = key_pair.sign(&data_to_sign);
|
||||
signature.to_bytes().to_vec()
|
||||
},
|
||||
#[cfg(feature = "secp256k1")]
|
||||
SecioKeyPairInner::Secp256k1 { ref private } => {
|
||||
let data_to_sign = Sha256::digest(&data_to_sign);
|
||||
let message = secp256k1::Message::from_slice(data_to_sign.as_ref())
|
||||
.expect("digest output length doesn't match secp256k1 input length");
|
||||
SECP256K1
|
||||
.sign(&message, private)
|
||||
.serialize_der()
|
||||
},
|
||||
match context.config.key.sign(&data_to_sign) {
|
||||
Ok(sig) => exchange.set_signature(sig),
|
||||
Err(_) => return Err(SecioError::SigningFailure)
|
||||
}
|
||||
});
|
||||
exchange
|
||||
};
|
||||
let local_exch = exchange.write_to_bytes()?;
|
||||
@ -445,72 +406,9 @@ where
|
||||
data_to_verify.extend_from_slice(&context.state.remote.local.proposition_bytes);
|
||||
data_to_verify.extend_from_slice(remote_exch.get_epubkey());
|
||||
|
||||
match context.state.remote.public_key {
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
PublicKey::Rsa(ref remote_public_key) => {
|
||||
// TODO: The ring library doesn't like some stuff in our DER public key,
|
||||
// therefore we scrap the first 24 bytes of the key. A proper fix would
|
||||
// be to write a DER parser, but that's not trivial.
|
||||
match ring_verify(&RSA_PKCS1_2048_8192_SHA256,
|
||||
UntrustedInput::from(&remote_public_key[24..]),
|
||||
UntrustedInput::from(&data_to_verify),
|
||||
UntrustedInput::from(remote_exch.get_signature()))
|
||||
{
|
||||
Ok(()) => (),
|
||||
Err(_) => {
|
||||
debug!("failed to verify the remote's signature");
|
||||
return Err(SecioError::SignatureVerificationFailed)
|
||||
},
|
||||
}
|
||||
},
|
||||
PublicKey::Ed25519(ref remote_public_key) => {
|
||||
let signature = Ed25519Signature::from_bytes(remote_exch.get_signature());
|
||||
let pubkey = Ed25519PublicKey::from_bytes(remote_public_key);
|
||||
|
||||
if let (Ok(signature), Ok(pubkey)) = (signature, pubkey) {
|
||||
match pubkey.verify(&data_to_verify, &signature) {
|
||||
Ok(()) => (),
|
||||
Err(_) => {
|
||||
debug!("failed to verify the remote's signature");
|
||||
if !context.state.remote.public_key.verify(&data_to_verify, remote_exch.get_signature()) {
|
||||
return Err(SecioError::SignatureVerificationFailed)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug!("the remote's signature or publickey are in the wrong format");
|
||||
return Err(SecioError::SignatureVerificationFailed)
|
||||
}
|
||||
},
|
||||
#[cfg(feature = "secp256k1")]
|
||||
PublicKey::Secp256k1(ref remote_public_key) => {
|
||||
let data_to_verify = Sha256::digest(&data_to_verify);
|
||||
let message = secp256k1::Message::from_slice(data_to_verify.as_ref())
|
||||
.expect("digest output length doesn't match secp256k1 input length");
|
||||
let signature = secp256k1::Signature::from_der(remote_exch.get_signature());
|
||||
let remote_public_key = secp256k1::key::PublicKey::from_slice(remote_public_key);
|
||||
if let (Ok(signature), Ok(remote_public_key)) = (signature, remote_public_key) {
|
||||
match SECP256K1.verify(&message, &signature, &remote_public_key) {
|
||||
Ok(()) => (),
|
||||
Err(_) => {
|
||||
debug!("failed to verify the remote's signature");
|
||||
return Err(SecioError::SignatureVerificationFailed)
|
||||
},
|
||||
}
|
||||
} else {
|
||||
debug!("remote's secp256k1 signature has wrong format");
|
||||
return Err(SecioError::SignatureVerificationFailed)
|
||||
}
|
||||
},
|
||||
#[cfg(any(target_os = "emscripten", target_os = "unknown"))]
|
||||
PublicKey::Rsa(_) => {
|
||||
debug!("support for RSA was disabled at compile-time");
|
||||
return Err(SecioError::SignatureVerificationFailed);
|
||||
},
|
||||
#[cfg(not(feature = "secp256k1"))]
|
||||
PublicKey::Secp256k1(_) => {
|
||||
debug!("support for secp256k1 was disabled at compile-time");
|
||||
return Err(SecioError::SignatureVerificationFailed);
|
||||
}
|
||||
};
|
||||
|
||||
trace!("successfully verified the remote's signature");
|
||||
Ok((remote_exch, socket, context))
|
||||
@ -625,29 +523,27 @@ where D: ::hmac::digest::Input + ::hmac::digest::BlockInput +
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use bytes::BytesMut;
|
||||
use libp2p_core::identity;
|
||||
use tokio::runtime::current_thread::Runtime;
|
||||
use tokio_tcp::{TcpListener, TcpStream};
|
||||
use crate::SecioError;
|
||||
use crate::{SecioConfig, SecioError};
|
||||
use super::handshake;
|
||||
use super::stretch_key;
|
||||
use crate::algo_support::Digest;
|
||||
use crate::codec::Hmac;
|
||||
use futures::prelude::*;
|
||||
use crate::{SecioConfig, SecioKeyPair};
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
fn handshake_with_self_succeeds_rsa() {
|
||||
let key1 = {
|
||||
let private = include_bytes!("../tests/test-rsa-private-key.pk8");
|
||||
let public = include_bytes!("../tests/test-rsa-public-key.der").to_vec();
|
||||
SecioKeyPair::rsa_from_pkcs8(private, public).unwrap()
|
||||
let mut private = include_bytes!("../tests/test-rsa-private-key.pk8").to_vec();
|
||||
identity::Keypair::rsa_from_pkcs8(&mut private).unwrap()
|
||||
};
|
||||
|
||||
let key2 = {
|
||||
let private = include_bytes!("../tests/test-rsa-private-key-2.pk8");
|
||||
let public = include_bytes!("../tests/test-rsa-public-key-2.der").to_vec();
|
||||
SecioKeyPair::rsa_from_pkcs8(private, public).unwrap()
|
||||
let mut private = include_bytes!("../tests/test-rsa-private-key-2.pk8").to_vec();
|
||||
identity::Keypair::rsa_from_pkcs8(&mut private).unwrap()
|
||||
};
|
||||
|
||||
handshake_with_self_succeeds(SecioConfig::new(key1), SecioConfig::new(key2));
|
||||
@ -655,8 +551,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn handshake_with_self_succeeds_ed25519() {
|
||||
let key1 = SecioKeyPair::ed25519_generated().unwrap();
|
||||
let key2 = SecioKeyPair::ed25519_generated().unwrap();
|
||||
let key1 = identity::Keypair::generate_ed25519();
|
||||
let key2 = identity::Keypair::generate_ed25519();
|
||||
handshake_with_self_succeeds(SecioConfig::new(key1), SecioConfig::new(key2));
|
||||
}
|
||||
|
||||
@ -664,13 +560,13 @@ mod tests {
|
||||
#[cfg(feature = "secp256k1")]
|
||||
fn handshake_with_self_succeeds_secp256k1() {
|
||||
let key1 = {
|
||||
let key = include_bytes!("../tests/test-secp256k1-private-key.der");
|
||||
SecioKeyPair::secp256k1_from_der(&key[..]).unwrap()
|
||||
let mut key = include_bytes!("../tests/test-secp256k1-private-key.der").to_vec();
|
||||
identity::Keypair::secp256k1_from_der(&mut key).unwrap()
|
||||
};
|
||||
|
||||
let key2 = {
|
||||
let key = include_bytes!("../tests/test-secp256k1-private-key-2.der");
|
||||
SecioKeyPair::secp256k1_from_der(&key[..]).unwrap()
|
||||
let mut key = include_bytes!("../tests/test-secp256k1-private-key-2.der").to_vec();
|
||||
identity::Keypair::secp256k1_from_der(&mut key).unwrap()
|
||||
};
|
||||
|
||||
handshake_with_self_succeeds(SecioConfig::new(key1), SecioConfig::new(key2));
|
||||
|
@ -31,8 +31,8 @@
|
||||
//! ```no_run
|
||||
//! # fn main() {
|
||||
//! use futures::Future;
|
||||
//! use libp2p_secio::{SecioConfig, SecioKeyPair, SecioOutput};
|
||||
//! use libp2p_core::{Multiaddr, upgrade::apply_inbound};
|
||||
//! use libp2p_secio::{SecioConfig, SecioOutput};
|
||||
//! use libp2p_core::{Multiaddr, identity, upgrade::apply_inbound};
|
||||
//! use libp2p_core::transport::Transport;
|
||||
//! use libp2p_tcp::TcpConfig;
|
||||
//! use tokio_io::io::write_all;
|
||||
@ -40,12 +40,9 @@
|
||||
//!
|
||||
//! let dialer = TcpConfig::new()
|
||||
//! .with_upgrade({
|
||||
//! # let private_key = b"";
|
||||
//! //let private_key = include_bytes!("test-rsa-private-key.pk8");
|
||||
//! # let public_key = vec![];
|
||||
//! //let public_key = include_bytes!("test-rsa-public-key.der").to_vec();
|
||||
//! // See the documentation of `SecioKeyPair`.
|
||||
//! let keypair = SecioKeyPair::rsa_from_pkcs8(private_key, public_key).unwrap();
|
||||
//! # let private_key = &mut [];
|
||||
//! // See the documentation of `identity::Keypair`.
|
||||
//! let keypair = identity::Keypair::rsa_from_pkcs8(private_key).unwrap();
|
||||
//! SecioConfig::new(keypair)
|
||||
//! })
|
||||
//! .map(|out: SecioOutput<_>, _| out.stream);
|
||||
@ -84,25 +81,15 @@ extern crate stdweb;
|
||||
|
||||
pub use self::error::SecioError;
|
||||
|
||||
#[cfg(feature = "secp256k1")]
|
||||
use asn1_der::{FromDerObject, DerObject};
|
||||
use bytes::BytesMut;
|
||||
use ed25519_dalek::Keypair as Ed25519KeyPair;
|
||||
use futures::stream::MapErr as StreamMapErr;
|
||||
use futures::{Future, Poll, Sink, StartSend, Stream};
|
||||
use lazy_static::lazy_static;
|
||||
use libp2p_core::{PeerId, PublicKey, upgrade::{UpgradeInfo, InboundUpgrade, OutboundUpgrade}};
|
||||
use libp2p_core::{PublicKey, identity, upgrade::{UpgradeInfo, InboundUpgrade, OutboundUpgrade}};
|
||||
use log::debug;
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
use ring::signature::RsaKeyPair;
|
||||
use rw_stream_sink::RwStreamSink;
|
||||
use std::error::Error;
|
||||
use std::io::{Error as IoError, ErrorKind as IoErrorKind};
|
||||
use std::iter;
|
||||
use std::sync::Arc;
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
use untrusted::Input;
|
||||
|
||||
mod algo_support;
|
||||
mod codec;
|
||||
@ -116,18 +103,12 @@ pub use crate::algo_support::Digest;
|
||||
pub use crate::exchange::KeyAgreement;
|
||||
pub use crate::stream_cipher::Cipher;
|
||||
|
||||
// Cached `Secp256k1` context, to avoid recreating it every time.
|
||||
#[cfg(feature = "secp256k1")]
|
||||
lazy_static! {
|
||||
static ref SECP256K1: secp256k1::Secp256k1<secp256k1::All> = secp256k1::Secp256k1::new();
|
||||
}
|
||||
|
||||
/// Implementation of the `ConnectionUpgrade` trait of `libp2p_core`. Automatically applies
|
||||
/// secio on any connection.
|
||||
#[derive(Clone)]
|
||||
pub struct SecioConfig {
|
||||
/// Private and public keys of the local node.
|
||||
pub(crate) key: SecioKeyPair,
|
||||
pub(crate) key: identity::Keypair,
|
||||
pub(crate) agreements_prop: Option<String>,
|
||||
pub(crate) ciphers_prop: Option<String>,
|
||||
pub(crate) digests_prop: Option<String>
|
||||
@ -135,7 +116,7 @@ pub struct SecioConfig {
|
||||
|
||||
impl SecioConfig {
|
||||
/// Create a new `SecioConfig` with the given keypair.
|
||||
pub fn new(kp: SecioKeyPair) -> Self {
|
||||
pub fn new(kp: identity::Keypair) -> Self {
|
||||
SecioConfig {
|
||||
key: kp,
|
||||
agreements_prop: None,
|
||||
@ -188,163 +169,6 @@ impl SecioConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Private and public keys of the local node.
|
||||
///
|
||||
/// # Generating offline keys with OpenSSL
|
||||
///
|
||||
/// ## RSA
|
||||
///
|
||||
/// Generating the keys:
|
||||
///
|
||||
/// ```text
|
||||
/// openssl genrsa -out private.pem 2048
|
||||
/// openssl rsa -in private.pem -outform DER -pubout -out public.der
|
||||
/// openssl pkcs8 -in private.pem -topk8 -nocrypt -out private.pk8
|
||||
/// rm private.pem # optional
|
||||
/// ```
|
||||
///
|
||||
/// Loading the keys:
|
||||
///
|
||||
/// ```ignore
|
||||
/// let key_pair = SecioKeyPair::rsa_from_pkcs8(include_bytes!("private.pk8"),
|
||||
/// include_bytes!("public.der"));
|
||||
/// ```
|
||||
///
|
||||
#[derive(Clone)]
|
||||
pub struct SecioKeyPair {
|
||||
inner: SecioKeyPairInner,
|
||||
}
|
||||
|
||||
impl SecioKeyPair {
|
||||
/// Builds a `SecioKeyPair` from a PKCS8 private key and public key.
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
pub fn rsa_from_pkcs8<P>(
|
||||
private: &[u8],
|
||||
public: P,
|
||||
) -> Result<SecioKeyPair, Box<dyn Error + Send + Sync>>
|
||||
where
|
||||
P: Into<Vec<u8>>,
|
||||
{
|
||||
let private = RsaKeyPair::from_pkcs8(Input::from(&private[..])).map_err(Box::new)?;
|
||||
|
||||
Ok(SecioKeyPair {
|
||||
inner: SecioKeyPairInner::Rsa {
|
||||
public: public.into(),
|
||||
private: Arc::new(private),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Generates a new Ed25519 key pair and uses it.
|
||||
pub fn ed25519_generated() -> Result<SecioKeyPair, Box<dyn Error + Send + Sync>> {
|
||||
let mut csprng = rand::thread_rng();
|
||||
let keypair: Ed25519KeyPair = Ed25519KeyPair::generate::<_>(&mut csprng);
|
||||
Ok(SecioKeyPair {
|
||||
inner: SecioKeyPairInner::Ed25519 {
|
||||
key_pair: Arc::new(keypair),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Builds a `SecioKeyPair` from a raw ed25519 32 bytes private key.
|
||||
///
|
||||
/// Returns an error if the slice doesn't have the correct length.
|
||||
pub fn ed25519_raw_key(key: impl AsRef<[u8]>) -> Result<SecioKeyPair, Box<dyn Error + Send + Sync>> {
|
||||
let secret = ed25519_dalek::SecretKey::from_bytes(key.as_ref())
|
||||
.map_err(|err| err.to_string())?;
|
||||
let public = ed25519_dalek::PublicKey::from(&secret);
|
||||
|
||||
Ok(SecioKeyPair {
|
||||
inner: SecioKeyPairInner::Ed25519 {
|
||||
key_pair: Arc::new(Ed25519KeyPair {
|
||||
secret,
|
||||
public,
|
||||
}),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Generates a new random sec256k1 key pair.
|
||||
#[cfg(feature = "secp256k1")]
|
||||
pub fn secp256k1_generated() -> Result<SecioKeyPair, Box<dyn Error + Send + Sync>> {
|
||||
let private = secp256k1::key::SecretKey::new(&mut secp256k1::rand::thread_rng());
|
||||
Ok(SecioKeyPair {
|
||||
inner: SecioKeyPairInner::Secp256k1 { private },
|
||||
})
|
||||
}
|
||||
|
||||
/// Builds a `SecioKeyPair` from a raw secp256k1 32 bytes private key.
|
||||
#[cfg(feature = "secp256k1")]
|
||||
pub fn secp256k1_raw_key<K>(key: K) -> Result<SecioKeyPair, Box<dyn Error + Send + Sync>>
|
||||
where
|
||||
K: AsRef<[u8]>,
|
||||
{
|
||||
let private = secp256k1::key::SecretKey::from_slice(key.as_ref())?;
|
||||
|
||||
Ok(SecioKeyPair {
|
||||
inner: SecioKeyPairInner::Secp256k1 { private },
|
||||
})
|
||||
}
|
||||
|
||||
/// Builds a `SecioKeyPair` from a secp256k1 private key in DER format.
|
||||
#[cfg(feature = "secp256k1")]
|
||||
pub fn secp256k1_from_der<K>(key: K) -> Result<SecioKeyPair, Box<dyn Error + Send + Sync>>
|
||||
where
|
||||
K: AsRef<[u8]>,
|
||||
{
|
||||
// See ECPrivateKey in https://tools.ietf.org/html/rfc5915
|
||||
let obj: Vec<DerObject> =
|
||||
FromDerObject::deserialize(key.as_ref().iter()).map_err(|err| err.to_string())?;
|
||||
let priv_key_obj = obj.into_iter()
|
||||
.nth(1)
|
||||
.ok_or_else(|| "Not enough elements in DER".to_string())?;
|
||||
let private_key: Vec<u8> =
|
||||
FromDerObject::from_der_object(priv_key_obj).map_err(|err| err.to_string())?;
|
||||
SecioKeyPair::secp256k1_raw_key(&private_key)
|
||||
}
|
||||
|
||||
/// Returns the public key corresponding to this key pair.
|
||||
pub fn to_public_key(&self) -> PublicKey {
|
||||
match self.inner {
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
SecioKeyPairInner::Rsa { ref public, .. } => PublicKey::Rsa(public.clone()),
|
||||
SecioKeyPairInner::Ed25519 { ref key_pair } => {
|
||||
PublicKey::Ed25519(key_pair.public.as_bytes().to_vec())
|
||||
}
|
||||
#[cfg(feature = "secp256k1")]
|
||||
SecioKeyPairInner::Secp256k1 { ref private } => {
|
||||
let pubkey = secp256k1::key::PublicKey::from_secret_key(&SECP256K1, private);
|
||||
PublicKey::Secp256k1(pubkey.serialize().to_vec())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `PeerId` corresponding to the public key of this key pair.
|
||||
#[inline]
|
||||
pub fn to_peer_id(&self) -> PeerId {
|
||||
self.to_public_key().into_peer_id()
|
||||
}
|
||||
|
||||
// TODO: method to save generated key on disk?
|
||||
}
|
||||
|
||||
// Inner content of `SecioKeyPair`.
|
||||
#[derive(Clone)]
|
||||
enum SecioKeyPairInner {
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "unknown")))]
|
||||
Rsa {
|
||||
public: Vec<u8>,
|
||||
// We use an `Arc` so that we can clone the enum.
|
||||
private: Arc<RsaKeyPair>,
|
||||
},
|
||||
Ed25519 {
|
||||
// We use an `Arc` so that we can clone the enum.
|
||||
key_pair: Arc<Ed25519KeyPair>,
|
||||
},
|
||||
#[cfg(feature = "secp256k1")]
|
||||
Secp256k1 { private: secp256k1::key::SecretKey },
|
||||
}
|
||||
|
||||
/// Output of the secio protocol.
|
||||
pub struct SecioOutput<S>
|
||||
where
|
||||
|
13
src/lib.rs
13
src/lib.rs
@ -59,7 +59,7 @@
|
||||
//! Example:
|
||||
//!
|
||||
//! ```rust
|
||||
//! let key = libp2p::secio::SecioKeyPair::ed25519_generated().unwrap();
|
||||
//! let key = libp2p::identity::Keypair::generate_ed25519();
|
||||
//! let _transport = libp2p::build_development_transport(key);
|
||||
//! // _transport.dial(...);
|
||||
//! ```
|
||||
@ -185,6 +185,7 @@ pub mod bandwidth;
|
||||
pub mod simple;
|
||||
|
||||
pub use self::core::{
|
||||
identity,
|
||||
Transport, PeerId, Swarm,
|
||||
transport::TransportError,
|
||||
upgrade::{InboundUpgrade, InboundUpgradeExt, OutboundUpgrade, OutboundUpgradeExt}
|
||||
@ -202,10 +203,10 @@ use std::{error, time::Duration};
|
||||
/// > **Note**: This `Transport` is not suitable for production usage, as its implementation
|
||||
/// > reserves the right to support additional protocols or remove deprecated protocols.
|
||||
#[inline]
|
||||
pub fn build_development_transport(local_private_key: secio::SecioKeyPair)
|
||||
pub fn build_development_transport(keypair: identity::Keypair)
|
||||
-> impl Transport<Output = (PeerId, impl core::muxing::StreamMuxer<OutboundSubstream = impl Send, Substream = impl Send> + Send + Sync), Error = impl error::Error + Send, Listener = impl Send, Dial = impl Send, ListenerUpgrade = impl Send> + Clone
|
||||
{
|
||||
build_tcp_ws_secio_mplex_yamux(local_private_key)
|
||||
build_tcp_ws_secio_mplex_yamux(keypair)
|
||||
}
|
||||
|
||||
/// Builds an implementation of `Transport` that is suitable for usage with the `Swarm`.
|
||||
@ -214,13 +215,13 @@ pub fn build_development_transport(local_private_key: secio::SecioKeyPair)
|
||||
/// and mplex or yamux as the multiplexing layer.
|
||||
///
|
||||
/// > **Note**: If you ever need to express the type of this `Transport`.
|
||||
pub fn build_tcp_ws_secio_mplex_yamux(local_private_key: secio::SecioKeyPair)
|
||||
pub fn build_tcp_ws_secio_mplex_yamux(keypair: identity::Keypair)
|
||||
-> impl Transport<Output = (PeerId, impl core::muxing::StreamMuxer<OutboundSubstream = impl Send, Substream = impl Send> + Send + Sync), Error = impl error::Error + Send, Listener = impl Send, Dial = impl Send, ListenerUpgrade = impl Send> + Clone
|
||||
{
|
||||
CommonTransport::new()
|
||||
.with_upgrade(secio::SecioConfig::new(local_private_key))
|
||||
.with_upgrade(secio::SecioConfig::new(keypair))
|
||||
.and_then(move |out, endpoint| {
|
||||
let peer_id = out.remote_key.into_peer_id();
|
||||
let peer_id = PeerId::from(out.remote_key);
|
||||
let peer_id2 = peer_id.clone();
|
||||
let upgrade = core::upgrade::SelectUpgrade::new(yamux::Config::default(), mplex::MplexConfig::new())
|
||||
// TODO: use a single `.map` instead of two maps
|
||||
|
Loading…
x
Reference in New Issue
Block a user