mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-05-29 02:31:20 +00:00
Add support for secp256k1 in secio (#258)
This commit is contained in:
parent
fd4c5fad44
commit
1607fcb3f4
@ -4,6 +4,7 @@ version = "0.1.0"
|
|||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
asn1_der = "0.5"
|
||||||
bytes = "0.4"
|
bytes = "0.4"
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
libp2p-core = { path = "../core" }
|
libp2p-core = { path = "../core" }
|
||||||
@ -13,6 +14,7 @@ rand = "0.3.17"
|
|||||||
ring = { version = "0.12.1", features = ["rsa_signing"] }
|
ring = { version = "0.12.1", features = ["rsa_signing"] }
|
||||||
rust-crypto = "^0.2"
|
rust-crypto = "^0.2"
|
||||||
rw-stream-sink = { path = "../rw-stream-sink" }
|
rw-stream-sink = { path = "../rw-stream-sink" }
|
||||||
|
secp256k1 = "0.9"
|
||||||
tokio-io = "0.1.0"
|
tokio-io = "0.1.0"
|
||||||
untrusted = "0.5.1"
|
untrusted = "0.5.1"
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ use ring::rand::SecureRandom;
|
|||||||
use ring::signature::verify as signature_verify;
|
use ring::signature::verify as signature_verify;
|
||||||
use ring::signature::{RSASigningState, RSA_PKCS1_2048_8192_SHA256, RSA_PKCS1_SHA256, ED25519};
|
use ring::signature::{RSASigningState, RSA_PKCS1_2048_8192_SHA256, RSA_PKCS1_SHA256, ED25519};
|
||||||
use ring::{agreement, digest, rand};
|
use ring::{agreement, digest, rand};
|
||||||
|
use secp256k1;
|
||||||
use std::cmp::{self, Ordering};
|
use std::cmp::{self, Ordering};
|
||||||
use std::io::{Error as IoError, ErrorKind as IoErrorKind};
|
use std::io::{Error as IoError, ErrorKind as IoErrorKind};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
@ -66,7 +67,7 @@ where
|
|||||||
// throughout the various parts of the handshake.
|
// throughout the various parts of the handshake.
|
||||||
struct HandshakeContext {
|
struct HandshakeContext {
|
||||||
// Filled with this function's parameters.
|
// Filled with this function's parameters.
|
||||||
local_key: SecioKeyPairInner,
|
local_key: SecioKeyPair,
|
||||||
|
|
||||||
rng: rand::SystemRandom,
|
rng: rand::SystemRandom,
|
||||||
// Locally-generated random number. The array size can be changed without any repercussion.
|
// Locally-generated random number. The array size can be changed without any repercussion.
|
||||||
@ -108,7 +109,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
let context = HandshakeContext {
|
let context = HandshakeContext {
|
||||||
local_key: local_key.inner,
|
local_key: local_key,
|
||||||
rng: rand::SystemRandom::new(),
|
rng: rand::SystemRandom::new(),
|
||||||
local_nonce: Default::default(),
|
local_nonce: Default::default(),
|
||||||
local_public_key_in_protobuf_bytes: Vec::new(),
|
local_public_key_in_protobuf_bytes: Vec::new(),
|
||||||
@ -143,14 +144,16 @@ where
|
|||||||
// Send our proposition with our nonce, public key and supported protocols.
|
// Send our proposition with our nonce, public key and supported protocols.
|
||||||
.and_then(|mut context| {
|
.and_then(|mut context| {
|
||||||
let mut public_key = PublicKeyProtobuf::new();
|
let mut public_key = PublicKeyProtobuf::new();
|
||||||
match context.local_key {
|
public_key.set_Data(context.local_key.to_public_key().into_raw().0);
|
||||||
SecioKeyPairInner::Rsa { ref public, .. } => {
|
match context.local_key.inner {
|
||||||
|
SecioKeyPairInner::Rsa { .. } => {
|
||||||
public_key.set_Type(KeyTypeProtobuf::RSA);
|
public_key.set_Type(KeyTypeProtobuf::RSA);
|
||||||
public_key.set_Data(public.clone());
|
|
||||||
},
|
},
|
||||||
SecioKeyPairInner::Ed25519 { ref key_pair } => {
|
SecioKeyPairInner::Ed25519 { .. } => {
|
||||||
public_key.set_Type(KeyTypeProtobuf::Ed25519);
|
public_key.set_Type(KeyTypeProtobuf::Ed25519);
|
||||||
public_key.set_Data(key_pair.public_key_bytes().to_owned());
|
},
|
||||||
|
SecioKeyPairInner::Secp256k1 { .. } => {
|
||||||
|
public_key.set_Type(KeyTypeProtobuf::Secp256k1);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
context.local_public_key_in_protobuf_bytes = public_key.write_to_bytes().unwrap();
|
context.local_public_key_in_protobuf_bytes = public_key.write_to_bytes().unwrap();
|
||||||
@ -214,11 +217,8 @@ where
|
|||||||
KeyTypeProtobuf::Ed25519 => {
|
KeyTypeProtobuf::Ed25519 => {
|
||||||
SecioPublicKey::Ed25519(pubkey.take_Data())
|
SecioPublicKey::Ed25519(pubkey.take_Data())
|
||||||
},
|
},
|
||||||
format => {
|
KeyTypeProtobuf::Secp256k1 => {
|
||||||
// TODO: support secp255k1
|
SecioPublicKey::Secp256k1(pubkey.take_Data())
|
||||||
let err = IoError::new(IoErrorKind::Other, "unsupported protocol");
|
|
||||||
debug!("unsupported remote pubkey format {:?}", format);
|
|
||||||
return Err(err.into());
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
trace!("received proposition from remote ; pubkey = {:?} ; nonce = {:?}",
|
trace!("received proposition from remote ; pubkey = {:?} ; nonce = {:?}",
|
||||||
@ -309,7 +309,7 @@ where
|
|||||||
let mut exchange = Exchange::new();
|
let mut exchange = Exchange::new();
|
||||||
exchange.set_epubkey(local_tmp_pub_key.to_vec());
|
exchange.set_epubkey(local_tmp_pub_key.to_vec());
|
||||||
exchange.set_signature({
|
exchange.set_signature({
|
||||||
match context.local_key {
|
match context.local_key.inner {
|
||||||
SecioKeyPairInner::Rsa { ref private, .. } => {
|
SecioKeyPairInner::Rsa { ref private, .. } => {
|
||||||
let mut state = match RSASigningState::new(private.clone()) {
|
let mut state = match RSASigningState::new(private.clone()) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
@ -335,6 +335,16 @@ where
|
|||||||
let signature = key_pair.sign(&data_to_sign);
|
let signature = key_pair.sign(&data_to_sign);
|
||||||
signature.as_ref().to_owned()
|
signature.as_ref().to_owned()
|
||||||
},
|
},
|
||||||
|
SecioKeyPairInner::Secp256k1 { ref private } => {
|
||||||
|
let data_to_sign = digest::digest(&digest::SHA256, &data_to_sign);
|
||||||
|
let message = secp256k1::Message::from_slice(data_to_sign.as_ref())
|
||||||
|
.expect("digest output length doesn't match secp256k1 input length");
|
||||||
|
let secp256k1 = secp256k1::Secp256k1::with_caps(secp256k1::ContextFlag::SignOnly);
|
||||||
|
secp256k1
|
||||||
|
.sign(&message, private)
|
||||||
|
.expect("failed to sign message")
|
||||||
|
.serialize_der(&secp256k1)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
exchange
|
exchange
|
||||||
@ -419,6 +429,26 @@ where
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Some(SecioPublicKey::Secp256k1(ref remote_public_key)) => {
|
||||||
|
let data_to_verify = digest::digest(&digest::SHA256, &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 secp256k1 = secp256k1::Secp256k1::with_caps(secp256k1::ContextFlag::VerifyOnly);
|
||||||
|
let signature = secp256k1::Signature::from_der(&secp256k1, remote_exch.get_signature());
|
||||||
|
let remote_public_key = secp256k1::key::PublicKey::from_slice(&secp256k1, 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)
|
||||||
|
}
|
||||||
|
},
|
||||||
None => unreachable!("we store a Some in the remote public key before reaching \
|
None => unreachable!("we store a Some in the remote public key before reaching \
|
||||||
this point")
|
this point")
|
||||||
};
|
};
|
||||||
@ -565,8 +595,6 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn handshake_with_self_succeeds_rsa() {
|
fn handshake_with_self_succeeds_rsa() {
|
||||||
let mut core = Core::new().unwrap();
|
|
||||||
|
|
||||||
let key1 = {
|
let key1 = {
|
||||||
let private = include_bytes!("../tests/test-rsa-private-key.pk8");
|
let private = include_bytes!("../tests/test-rsa-private-key.pk8");
|
||||||
let public = include_bytes!("../tests/test-rsa-public-key.der").to_vec();
|
let public = include_bytes!("../tests/test-rsa-public-key.der").to_vec();
|
||||||
@ -578,29 +606,34 @@ mod tests {
|
|||||||
let public = include_bytes!("../tests/test-rsa-public-key-2.der").to_vec();
|
let public = include_bytes!("../tests/test-rsa-public-key-2.der").to_vec();
|
||||||
SecioKeyPair::rsa_from_pkcs8(private, public).unwrap()
|
SecioKeyPair::rsa_from_pkcs8(private, public).unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap();
|
handshake_with_self_succeeds(key1, key2);
|
||||||
let listener_addr = listener.local_addr().unwrap();
|
|
||||||
|
|
||||||
let server = listener
|
|
||||||
.incoming()
|
|
||||||
.into_future()
|
|
||||||
.map_err(|(e, _)| e.into())
|
|
||||||
.and_then(move |(connec, _)| handshake(connec.unwrap().0, key1));
|
|
||||||
|
|
||||||
let client = TcpStream::connect(&listener_addr, &core.handle())
|
|
||||||
.map_err(|e| e.into())
|
|
||||||
.and_then(move |stream| handshake(stream, key2));
|
|
||||||
|
|
||||||
core.run(server.join(client)).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn handshake_with_self_succeeds_ed25519() {
|
fn handshake_with_self_succeeds_ed25519() {
|
||||||
let mut core = Core::new().unwrap();
|
|
||||||
|
|
||||||
let key1 = SecioKeyPair::ed25519_generated().unwrap();
|
let key1 = SecioKeyPair::ed25519_generated().unwrap();
|
||||||
let key2 = SecioKeyPair::ed25519_generated().unwrap();
|
let key2 = SecioKeyPair::ed25519_generated().unwrap();
|
||||||
|
handshake_with_self_succeeds(key1, key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
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 key2 = {
|
||||||
|
let key = include_bytes!("../tests/test-secp256k1-private-key-2.der");
|
||||||
|
SecioKeyPair::secp256k1_from_der(&key[..]).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
handshake_with_self_succeeds(key1, key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handshake_with_self_succeeds(key1: SecioKeyPair, key2: SecioKeyPair) {
|
||||||
|
let mut core = Core::new().unwrap();
|
||||||
|
|
||||||
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap();
|
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap();
|
||||||
let listener_addr = listener.local_addr().unwrap();
|
let listener_addr = listener.local_addr().unwrap();
|
||||||
|
@ -81,6 +81,7 @@
|
|||||||
//! `SecioMiddleware` that implements `Sink` and `Stream` and can be used to send packets of data.
|
//! `SecioMiddleware` that implements `Sink` and `Stream` and can be used to send packets of data.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
extern crate asn1_der;
|
||||||
extern crate bytes;
|
extern crate bytes;
|
||||||
extern crate crypto;
|
extern crate crypto;
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
@ -91,11 +92,13 @@ extern crate protobuf;
|
|||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate ring;
|
extern crate ring;
|
||||||
extern crate rw_stream_sink;
|
extern crate rw_stream_sink;
|
||||||
|
extern crate secp256k1;
|
||||||
extern crate tokio_io;
|
extern crate tokio_io;
|
||||||
extern crate untrusted;
|
extern crate untrusted;
|
||||||
|
|
||||||
pub use self::error::SecioError;
|
pub use self::error::SecioError;
|
||||||
|
|
||||||
|
use asn1_der::{DerObject, traits::FromDerEncoded, traits::FromDerObject};
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use futures::stream::MapErr as StreamMapErr;
|
use futures::stream::MapErr as StreamMapErr;
|
||||||
use futures::{Future, Poll, Sink, StartSend, Stream};
|
use futures::{Future, Poll, Sink, StartSend, Stream};
|
||||||
@ -194,6 +197,33 @@ impl SecioKeyPair {
|
|||||||
.expect("failed to parse generated Ed25519 key"))
|
.expect("failed to parse generated Ed25519 key"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builds a `SecioKeyPair` from a raw secp256k1 32 bytes private key.
|
||||||
|
pub fn secp256k1_raw_key<K>(key: K) -> Result<SecioKeyPair, Box<Error + Send + Sync>>
|
||||||
|
where K: AsRef<[u8]>
|
||||||
|
{
|
||||||
|
let secp = secp256k1::Secp256k1::with_caps(secp256k1::ContextFlag::None);
|
||||||
|
let private = secp256k1::key::SecretKey::from_slice(&secp, key.as_ref())?;
|
||||||
|
|
||||||
|
Ok(SecioKeyPair {
|
||||||
|
inner: SecioKeyPairInner::Secp256k1 {
|
||||||
|
private,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds a `SecioKeyPair` from a secp256k1 private key in DER format.
|
||||||
|
pub fn secp256k1_from_der<K>(key: K) -> Result<SecioKeyPair, Box<Error + Send + Sync>>
|
||||||
|
where K: AsRef<[u8]>
|
||||||
|
{
|
||||||
|
// See ECPrivateKey in https://tools.ietf.org/html/rfc5915
|
||||||
|
let obj: Vec<DerObject> = FromDerEncoded::with_der_encoded(key.as_ref())
|
||||||
|
.map_err(|err| err.to_string())?;
|
||||||
|
let priv_key_obj = obj.into_iter().nth(1).ok_or("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.
|
/// Returns the public key corresponding to this key pair.
|
||||||
pub fn to_public_key(&self) -> SecioPublicKey {
|
pub fn to_public_key(&self) -> SecioPublicKey {
|
||||||
match self.inner {
|
match self.inner {
|
||||||
@ -203,6 +233,12 @@ impl SecioKeyPair {
|
|||||||
SecioKeyPairInner::Ed25519 { ref key_pair } => {
|
SecioKeyPairInner::Ed25519 { ref key_pair } => {
|
||||||
SecioPublicKey::Ed25519(key_pair.public_key_bytes().to_vec())
|
SecioPublicKey::Ed25519(key_pair.public_key_bytes().to_vec())
|
||||||
},
|
},
|
||||||
|
SecioKeyPairInner::Secp256k1 { ref private } => {
|
||||||
|
let secp = secp256k1::Secp256k1::with_caps(secp256k1::ContextFlag::SignOnly);
|
||||||
|
let pubkey = secp256k1::key::PublicKey::from_secret_key(&secp, private)
|
||||||
|
.expect("wrong secp256k1 private key ; type safety violated");
|
||||||
|
SecioPublicKey::Secp256k1(pubkey.serialize().to_vec())
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,6 +251,13 @@ impl SecioKeyPair {
|
|||||||
SecioKeyPairInner::Ed25519 { ref key_pair } => {
|
SecioKeyPairInner::Ed25519 { ref key_pair } => {
|
||||||
PublicKeyBytesSlice(key_pair.public_key_bytes()).into()
|
PublicKeyBytesSlice(key_pair.public_key_bytes()).into()
|
||||||
},
|
},
|
||||||
|
SecioKeyPairInner::Secp256k1 { ref private } => {
|
||||||
|
let secp = secp256k1::Secp256k1::with_caps(secp256k1::ContextFlag::None);
|
||||||
|
let pubkey = secp256k1::key::PublicKey::from_secret_key(&secp, private)
|
||||||
|
.expect("wrong secp256k1 private key ; type safety violated");
|
||||||
|
let pubkey_bytes = pubkey.serialize();
|
||||||
|
PublicKeyBytesSlice(&pubkey_bytes).into()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,6 +276,9 @@ enum SecioKeyPairInner {
|
|||||||
// We use an `Arc` so that we can clone the enum.
|
// We use an `Arc` so that we can clone the enum.
|
||||||
key_pair: Arc<Ed25519KeyPair>,
|
key_pair: Arc<Ed25519KeyPair>,
|
||||||
},
|
},
|
||||||
|
Secp256k1 {
|
||||||
|
private: secp256k1::key::SecretKey,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Public key used by the remote.
|
/// Public key used by the remote.
|
||||||
@ -243,6 +289,9 @@ pub enum SecioPublicKey {
|
|||||||
/// Format = ???
|
/// Format = ???
|
||||||
// TODO: ^
|
// TODO: ^
|
||||||
Ed25519(Vec<u8>),
|
Ed25519(Vec<u8>),
|
||||||
|
/// Format = ???
|
||||||
|
// TODO: ^
|
||||||
|
Secp256k1(Vec<u8>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SecioPublicKey {
|
impl SecioPublicKey {
|
||||||
@ -252,6 +301,7 @@ impl SecioPublicKey {
|
|||||||
match self {
|
match self {
|
||||||
SecioPublicKey::Rsa(ref data) => PublicKeyBytesSlice(data),
|
SecioPublicKey::Rsa(ref data) => PublicKeyBytesSlice(data),
|
||||||
SecioPublicKey::Ed25519(ref data) => PublicKeyBytesSlice(data),
|
SecioPublicKey::Ed25519(ref data) => PublicKeyBytesSlice(data),
|
||||||
|
SecioPublicKey::Secp256k1(ref data) => PublicKeyBytesSlice(data),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,6 +311,7 @@ impl SecioPublicKey {
|
|||||||
match self {
|
match self {
|
||||||
SecioPublicKey::Rsa(data) => PublicKeyBytes(data),
|
SecioPublicKey::Rsa(data) => PublicKeyBytes(data),
|
||||||
SecioPublicKey::Ed25519(data) => PublicKeyBytes(data),
|
SecioPublicKey::Ed25519(data) => PublicKeyBytes(data),
|
||||||
|
SecioPublicKey::Secp256k1(data) => PublicKeyBytes(data),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BIN
secio/tests/test-secp256k1-private-key-2.der
Normal file
BIN
secio/tests/test-secp256k1-private-key-2.der
Normal file
Binary file not shown.
BIN
secio/tests/test-secp256k1-private-key.der
Normal file
BIN
secio/tests/test-secp256k1-private-key.der
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user