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>"]
|
||||
|
||||
[dependencies]
|
||||
asn1_der = "0.5"
|
||||
bytes = "0.4"
|
||||
futures = "0.1"
|
||||
libp2p-core = { path = "../core" }
|
||||
@ -13,6 +14,7 @@ rand = "0.3.17"
|
||||
ring = { version = "0.12.1", features = ["rsa_signing"] }
|
||||
rust-crypto = "^0.2"
|
||||
rw-stream-sink = { path = "../rw-stream-sink" }
|
||||
secp256k1 = "0.9"
|
||||
tokio-io = "0.1.0"
|
||||
untrusted = "0.5.1"
|
||||
|
||||
|
@ -36,6 +36,7 @@ use ring::rand::SecureRandom;
|
||||
use ring::signature::verify as signature_verify;
|
||||
use ring::signature::{RSASigningState, RSA_PKCS1_2048_8192_SHA256, RSA_PKCS1_SHA256, ED25519};
|
||||
use ring::{agreement, digest, rand};
|
||||
use secp256k1;
|
||||
use std::cmp::{self, Ordering};
|
||||
use std::io::{Error as IoError, ErrorKind as IoErrorKind};
|
||||
use std::mem;
|
||||
@ -66,7 +67,7 @@ where
|
||||
// throughout the various parts of the handshake.
|
||||
struct HandshakeContext {
|
||||
// Filled with this function's parameters.
|
||||
local_key: SecioKeyPairInner,
|
||||
local_key: SecioKeyPair,
|
||||
|
||||
rng: rand::SystemRandom,
|
||||
// Locally-generated random number. The array size can be changed without any repercussion.
|
||||
@ -108,7 +109,7 @@ where
|
||||
}
|
||||
|
||||
let context = HandshakeContext {
|
||||
local_key: local_key.inner,
|
||||
local_key: local_key,
|
||||
rng: rand::SystemRandom::new(),
|
||||
local_nonce: Default::default(),
|
||||
local_public_key_in_protobuf_bytes: Vec::new(),
|
||||
@ -143,14 +144,16 @@ where
|
||||
// Send our proposition with our nonce, public key and supported protocols.
|
||||
.and_then(|mut context| {
|
||||
let mut public_key = PublicKeyProtobuf::new();
|
||||
match context.local_key {
|
||||
SecioKeyPairInner::Rsa { ref public, .. } => {
|
||||
public_key.set_Data(context.local_key.to_public_key().into_raw().0);
|
||||
match context.local_key.inner {
|
||||
SecioKeyPairInner::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_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();
|
||||
@ -214,11 +217,8 @@ where
|
||||
KeyTypeProtobuf::Ed25519 => {
|
||||
SecioPublicKey::Ed25519(pubkey.take_Data())
|
||||
},
|
||||
format => {
|
||||
// TODO: support secp255k1
|
||||
let err = IoError::new(IoErrorKind::Other, "unsupported protocol");
|
||||
debug!("unsupported remote pubkey format {:?}", format);
|
||||
return Err(err.into());
|
||||
KeyTypeProtobuf::Secp256k1 => {
|
||||
SecioPublicKey::Secp256k1(pubkey.take_Data())
|
||||
},
|
||||
});
|
||||
trace!("received proposition from remote ; pubkey = {:?} ; nonce = {:?}",
|
||||
@ -309,7 +309,7 @@ where
|
||||
let mut exchange = Exchange::new();
|
||||
exchange.set_epubkey(local_tmp_pub_key.to_vec());
|
||||
exchange.set_signature({
|
||||
match context.local_key {
|
||||
match context.local_key.inner {
|
||||
SecioKeyPairInner::Rsa { ref private, .. } => {
|
||||
let mut state = match RSASigningState::new(private.clone()) {
|
||||
Ok(s) => s,
|
||||
@ -335,6 +335,16 @@ where
|
||||
let signature = key_pair.sign(&data_to_sign);
|
||||
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
|
||||
@ -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 \
|
||||
this point")
|
||||
};
|
||||
@ -565,8 +595,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn handshake_with_self_succeeds_rsa() {
|
||||
let mut core = Core::new().unwrap();
|
||||
|
||||
let key1 = {
|
||||
let private = include_bytes!("../tests/test-rsa-private-key.pk8");
|
||||
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();
|
||||
SecioKeyPair::rsa_from_pkcs8(private, public).unwrap()
|
||||
};
|
||||
|
||||
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap();
|
||||
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();
|
||||
|
||||
handshake_with_self_succeeds(key1, key2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn handshake_with_self_succeeds_ed25519() {
|
||||
let mut core = Core::new().unwrap();
|
||||
|
||||
let key1 = 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_addr = listener.local_addr().unwrap();
|
||||
|
@ -81,6 +81,7 @@
|
||||
//! `SecioMiddleware` that implements `Sink` and `Stream` and can be used to send packets of data.
|
||||
//!
|
||||
|
||||
extern crate asn1_der;
|
||||
extern crate bytes;
|
||||
extern crate crypto;
|
||||
extern crate futures;
|
||||
@ -91,11 +92,13 @@ extern crate protobuf;
|
||||
extern crate rand;
|
||||
extern crate ring;
|
||||
extern crate rw_stream_sink;
|
||||
extern crate secp256k1;
|
||||
extern crate tokio_io;
|
||||
extern crate untrusted;
|
||||
|
||||
pub use self::error::SecioError;
|
||||
|
||||
use asn1_der::{DerObject, traits::FromDerEncoded, traits::FromDerObject};
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use futures::stream::MapErr as StreamMapErr;
|
||||
use futures::{Future, Poll, Sink, StartSend, Stream};
|
||||
@ -194,6 +197,33 @@ impl SecioKeyPair {
|
||||
.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.
|
||||
pub fn to_public_key(&self) -> SecioPublicKey {
|
||||
match self.inner {
|
||||
@ -203,6 +233,12 @@ impl SecioKeyPair {
|
||||
SecioKeyPairInner::Ed25519 { ref key_pair } => {
|
||||
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 } => {
|
||||
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.
|
||||
key_pair: Arc<Ed25519KeyPair>,
|
||||
},
|
||||
Secp256k1 {
|
||||
private: secp256k1::key::SecretKey,
|
||||
},
|
||||
}
|
||||
|
||||
/// Public key used by the remote.
|
||||
@ -243,6 +289,9 @@ pub enum SecioPublicKey {
|
||||
/// Format = ???
|
||||
// TODO: ^
|
||||
Ed25519(Vec<u8>),
|
||||
/// Format = ???
|
||||
// TODO: ^
|
||||
Secp256k1(Vec<u8>),
|
||||
}
|
||||
|
||||
impl SecioPublicKey {
|
||||
@ -252,6 +301,7 @@ impl SecioPublicKey {
|
||||
match self {
|
||||
SecioPublicKey::Rsa(ref data) => PublicKeyBytesSlice(data),
|
||||
SecioPublicKey::Ed25519(ref data) => PublicKeyBytesSlice(data),
|
||||
SecioPublicKey::Secp256k1(ref data) => PublicKeyBytesSlice(data),
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,6 +311,7 @@ impl SecioPublicKey {
|
||||
match self {
|
||||
SecioPublicKey::Rsa(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