diff --git a/Cargo.toml b/Cargo.toml index 31932a137..0a6d5776b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,9 +11,13 @@ log = "0.3" env_logger = "0.3" rustc-serialize = "0.3" arrayvec = "0.3" -mio = "0.*" -rand = "0.*" +mio = "0.4.4" +rand = "0.3.12" +time = "0.1.34" tiny-keccak = "0.3" rocksdb = "0.2.1" num = "0.1" lazy_static = "0.1.*" +ifaces = { git = "https://github.com/dlevy47/rust-interfaces.git" } +secp256k1 = "0.5.1" +rust-crypto = "0.2.34" diff --git a/src/crypto.rs b/src/crypto.rs new file mode 100644 index 000000000..0d1ae9c05 --- /dev/null +++ b/src/crypto.rs @@ -0,0 +1,167 @@ +use hash::*; +use secp256k1::Secp256k1; +use secp256k1::key; +use rand::os::OsRng; + +pub type Secret=H256; +pub type Public=H512; +pub type Signature=H520; + +#[derive(Debug)] +pub enum CryptoError { + InvalidSecret, + InvalidPublic, + InvalidSignature, + InvalidMessage, + Io(::std::io::Error), +} + +impl From<::secp256k1::Error> for CryptoError { + fn from(e: ::secp256k1::Error) -> CryptoError { + match e { + ::secp256k1::Error::InvalidMessage => CryptoError::InvalidMessage, + ::secp256k1::Error::InvalidPublicKey => CryptoError::InvalidPublic, + ::secp256k1::Error::InvalidSignature => CryptoError::InvalidSignature, + ::secp256k1::Error::InvalidSecretKey => CryptoError::InvalidSecret, + _ => panic!("Crypto error: {:?}", e), + } + } +} + +impl From<::std::io::Error> for CryptoError { + fn from(err: ::std::io::Error) -> CryptoError { + CryptoError::Io(err) + } +} + +#[derive(Debug, PartialEq, Eq)] +/// secp256k1 Key pair +/// +/// Use `create()` to create a new random key pair. +/// +/// # Example +/// ```rust +/// extern crate ethcore_util; +/// use ethcore_util::crypto::*; +/// use ethcore_util::hash::*; +/// fn main() { +/// let pair = KeyPair::create().unwrap(); +/// let message = H256::random(); +/// let signature = sign(pair.secret(), &message).unwrap(); +/// +/// assert!(verify(pair.public(), &signature, &message).unwrap()); +/// assert_eq!(recover(&signature, &message).unwrap(), *pair.public()); +/// } +/// ``` +pub struct KeyPair { + secret: Secret, + public: Public, +} + +impl KeyPair { + /// Create a pair from secret key + pub fn from_secret(secret: Secret) -> Result { + let context = Secp256k1::new(); + let s: key::SecretKey = try!(key::SecretKey::from_slice(&context, &secret)); + let pub_key = try!(key::PublicKey::from_secret_key(&context, &s)); + let serialized = pub_key.serialize_vec(&context, false); + let p: Public = Public::from_slice(&serialized[1..65]); + Ok(KeyPair { + secret: secret, + public: p, + }) + } + /// Create a new random key pair + pub fn create() -> Result { + let context = Secp256k1::new(); + let mut rng = try!(OsRng::new()); + let (sec, publ) = try!(context.generate_keypair(&mut rng)); + let serialized = publ.serialize_vec(&context, false); + let p: Public = Public::from_slice(&serialized[1..65]); + let s: Secret = unsafe { ::std::mem::transmute(sec) }; + Ok(KeyPair { + secret: s, + public: p, + }) + } + /// Returns public key + pub fn public(&self) -> &Public { + &self.public + } + /// Returns private key + pub fn secret(&self) -> &Secret { + &self.secret + } +} + +/// Recovers Public key from signed message hash. +pub fn recover(signature: &Signature, message: &H256) -> Result { + use secp256k1::*; + let context = Secp256k1::new(); + let rsig = try!(RecoverableSignature::from_compact(&context, &signature[0..64], try!(RecoveryId::from_i32(signature[64] as i32)))); + let publ = try!(context.recover(&try!(Message::from_slice(&message)), &rsig)); + let serialized = publ.serialize_vec(&context, false); + let p: Public = Public::from_slice(&serialized[1..65]); + Ok(p) +} +/// Returns siganture of message hash. +pub fn sign(secret: &Secret, message: &H256) -> Result { + use secp256k1::*; + let context = Secp256k1::new(); + let sec: &key::SecretKey = unsafe { ::std::mem::transmute(secret) }; + let s = try!(context.sign_recoverable(&try!(Message::from_slice(&message)), sec)); + let (rec_id, data) = s.serialize_compact(&context); + let mut signature: ::crypto::Signature = unsafe { ::std::mem::uninitialized() }; + signature.clone_from_slice(&data); + signature[64] = rec_id.to_i32() as u8; + Ok(signature) +} +/// Verify signature. +pub fn verify(public: &Public, signature: &Signature, message: &H256) -> Result { + use secp256k1::*; + let context = Secp256k1::new(); + let rsig = try!(RecoverableSignature::from_compact(&context, &signature[0..64], try!(RecoveryId::from_i32(signature[64] as i32)))); + let sig = rsig.to_standard(&context); + + let mut pdata: [u8; 65] = [4u8; 65]; + let ptr = pdata[1..].as_mut_ptr(); + let src = public.as_ptr(); + unsafe { ::std::ptr::copy_nonoverlapping(src, ptr, 64) }; + let publ = try!(key::PublicKey::from_slice(&context, &pdata)); + match context.verify(&try!(Message::from_slice(&message)), &sig, &publ) { + Ok(_) => Ok(true), + Err(Error::IncorrectSignature) => Ok(false), + Err(x) => Err(>::from(x)) + } +} + +#[cfg(test)] +mod tests { + + use std::str::FromStr; + use hash::*; + use crypto::*; + + #[test] + fn test_signature() { + let pair = KeyPair::create().unwrap(); + let message = H256::random(); + let signature = sign(pair.secret(), &message).unwrap(); + + assert!(verify(pair.public(), &signature, &message).unwrap()); + assert_eq!(recover(&signature, &message).unwrap(), *pair.public()); + } + + #[test] + fn test_invalid_key() { + assert!(KeyPair::from_secret(Secret::from_str("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()).is_err()); + assert!(KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap()).is_err()); + assert!(KeyPair::from_secret(Secret::from_str("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141").unwrap()).is_err()); + } + + #[test] + fn test_key() { + let pair = KeyPair::from_secret(Secret::from_str("6f7b0d801bc7b5ce7bbd930b84fd0369b3eb25d09be58d64ba811091046f3aa2").unwrap()).unwrap(); + assert_eq!(pair.public().hex(), "101b3ef5a4ea7a1c7928e24c4c75fd053c235d7b80c22ae5c03d145d0ac7396e2a4ffff9adee3133a7b05044a5cee08115fd65145e5165d646bde371010d803c"); + } +} diff --git a/src/hash.rs b/src/hash.rs index 08166559a..ad918ef3d 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -1,7 +1,8 @@ use std::str::FromStr; use std::fmt; +use std::ops; use std::hash::{Hash, Hasher}; -use std::ops::{Index, IndexMut, BitOr, BitAnd}; +use std::ops::{Index, IndexMut, Deref, DerefMut, BitOr, BitAnd}; use rustc_serialize::hex::*; use error::EthcoreError; use rand::Rng; @@ -16,6 +17,8 @@ pub trait FixedHash: Sized + BytesConvertable { fn randomize(&mut self); fn size() -> usize; fn mut_bytes(&mut self) -> &mut [u8]; + fn from_slice(src: &[u8]) -> Self; + fn clone_from_slice(&mut self, src: &[u8]) -> usize; fn shift_bloom<'a, T>(&'a mut self, b: &T) -> &'a mut Self where T: FixedHash; fn bloom_part(&self, m: usize) -> T where T: FixedHash; fn contains_bloom(&self, b: &T) -> bool where T: FixedHash; @@ -25,7 +28,7 @@ pub trait FixedHash: Sized + BytesConvertable { macro_rules! impl_hash { ($from: ident, $size: expr) => { #[derive(Eq)] - pub struct $from ([u8; $size]); + pub struct $from (pub [u8; $size]); impl BytesConvertable for $from { fn bytes(&self) -> &[u8] { @@ -33,6 +36,24 @@ macro_rules! impl_hash { } } + impl Deref for $from { + type Target = [u8]; + #[inline] + fn deref(&self) -> &[u8] { + unsafe { + ::std::slice::from_raw_parts(self.0.as_ptr(), $size) + } + } + } + impl DerefMut for $from { + #[inline] + fn deref_mut(&mut self) -> &mut [u8] { + unsafe { + ::std::slice::from_raw_parts_mut(self.0.as_mut_ptr(), $size) + } + } + } + impl FixedHash for $from { fn new() -> $from { $from([0; $size]) @@ -57,6 +78,23 @@ macro_rules! impl_hash { &mut self.0 } + // TODO: remove once slice::clone_from_slice is stable + #[inline] + fn clone_from_slice(&mut self, src: &[u8]) -> usize { + let min = ::std::cmp::min($size, src.len()); + let dst = &mut self.deref_mut()[.. min]; + let src = &src[.. min]; + for i in 0..min { + dst[i] = src[i]; + } + min + } + fn from_slice(src: &[u8]) -> Self { + let mut r = Self::new(); + r.clone_from_slice(src); + r + } + fn shift_bloom<'a, T>(&'a mut self, b: &T) -> &'a mut Self where T: FixedHash { let bp: Self = b.bloom_part($size); let new_self = &bp | self; @@ -186,6 +224,30 @@ macro_rules! impl_hash { &mut self.0[index] } } + impl Index> for $from { + type Output = [u8]; + + fn index<'a>(&'a self, index: ops::Range) -> &'a [u8] { + &self.0[index] + } + } + impl IndexMut> for $from { + fn index_mut<'a>(&'a mut self, index: ops::Range) -> &'a mut [u8] { + &mut self.0[index] + } + } + impl Index for $from { + type Output = [u8]; + + fn index<'a>(&'a self, _index: ops::RangeFull) -> &'a [u8] { + &self.0 + } + } + impl IndexMut for $from { + fn index_mut<'a>(&'a mut self, _index: ops::RangeFull) -> &'a mut [u8] { + &mut self.0 + } + } /// BitOr on references impl<'a> BitOr for &'a $from { diff --git a/src/lib.rs b/src/lib.rs index 0a7c14ca9..462a4035a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,8 +14,13 @@ extern crate log; extern crate lazy_static; #[macro_use] -pub mod macros; +extern crate ifaces; +extern crate time; +extern crate crypto as rcrypto; +extern crate secp256k1; +extern crate arrayvec; +pub mod macros; pub mod error; pub mod hash; pub mod uint; @@ -28,9 +33,9 @@ pub mod hashdb; pub mod memorydb; pub mod overlaydb; pub mod math; -//pub mod filter; pub mod chainfilter; pub mod trie; +pub mod crypto; //pub mod network;