diff --git a/parity-crypto/Cargo.toml b/parity-crypto/Cargo.toml index 5bfda0aca..9b4ecc7e1 100644 --- a/parity-crypto/Cargo.toml +++ b/parity-crypto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parity-crypto" -version = "0.4.1" +version = "0.4.2" authors = ["Parity Technologies "] repository = "https://github.com/paritytech/parity-common" description = "Crypto utils used by ethstore and network." @@ -11,10 +11,13 @@ edition = "2018" [[bench]] name = "bench" harness = false - +required-features = ["publickey"] [dependencies] tiny-keccak = "1.4" +eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1", rev = "a96ad75", optional = true } +ethereum-types = { version = "0.8.0", optional = true } +lazy_static = { version = "1.0", optional = true } scrypt = { version = "0.2", default-features = false } ripemd160 = "0.8.0" sha2 = "0.8.0" @@ -24,9 +27,17 @@ aes = "0.3.2" aes-ctr = "0.3.0" block-modes = "0.3.3" pbkdf2 = "0.3.0" +rand = "0.6" +rustc-hex = "2.0" subtle = "2.1" zeroize = "0.9.1" [dev-dependencies] criterion = "0.2" hex-literal = "0.2" + +[features] +default = [] +# public key crypto utils +# moved from ethkey module in parity ethereum repository +publickey = ["eth-secp256k1", "lazy_static", "ethereum-types"] \ No newline at end of file diff --git a/parity-crypto/benches/bench.rs b/parity-crypto/benches/bench.rs index f9f68dfd1..33e7184a1 100644 --- a/parity-crypto/benches/bench.rs +++ b/parity-crypto/benches/bench.rs @@ -21,8 +21,13 @@ extern crate parity_crypto; extern crate criterion; use criterion::{Criterion, Bencher}; +use crate::parity_crypto::publickey::Generator; -criterion_group!(benches, input_len); +criterion_group!( + benches, + input_len, + ecdh_agree, +); criterion_main!(benches); @@ -43,7 +48,7 @@ fn input_len(c: &mut Criterion) { let mut dest = vec![0; *size]; let k = [0; 16]; let iv = [0; 16]; - + b.iter(||{ parity_crypto::aes::encrypt_128_ctr(&k[..], &iv[..], &data[..], &mut dest[..]).unwrap(); // same as encrypt but add it just in case @@ -54,3 +59,11 @@ fn input_len(c: &mut Criterion) { ); } + +fn ecdh_agree(c: &mut Criterion) { + let keypair = parity_crypto::publickey::Random.generate().unwrap(); + let public = keypair.public().clone(); + let secret = keypair.secret().clone(); + + c.bench_function("ecdh_agree", move |b| b.iter(|| parity_crypto::publickey::ecdh::agree(&secret, &public))); +} \ No newline at end of file diff --git a/parity-crypto/src/lib.rs b/parity-crypto/src/lib.rs index ce680f929..309c4c803 100644 --- a/parity-crypto/src/lib.rs +++ b/parity-crypto/src/lib.rs @@ -22,6 +22,8 @@ pub mod scrypt; pub mod digest; pub mod hmac; pub mod pbkdf2; +#[cfg(feature = "publickey")] +pub mod publickey; pub use crate::error::Error; diff --git a/parity-crypto/src/publickey/ec_math_utils.rs b/parity-crypto/src/publickey/ec_math_utils.rs new file mode 100644 index 000000000..cbc2e3f81 --- /dev/null +++ b/parity-crypto/src/publickey/ec_math_utils.rs @@ -0,0 +1,174 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Multiple primitives for work with public and secret keys and with secp256k1 curve points + +use super::{SECP256K1, Public, Secret, Error}; +use secp256k1::key; +use secp256k1::constants::{CURVE_ORDER as SECP256K1_CURVE_ORDER}; +use ethereum_types::{BigEndianHash as _, U256, H256}; +use lazy_static::lazy_static; + +/// Generation point array combined from X and Y coordinates +/// Equivalent to uncompressed form, see https://tools.ietf.org/id/draft-jivsov-ecc-compact-05.html#rfc.section.3 +pub const BASE_POINT_BYTES: [u8; 65] = [ + 0x4, + // The X coordinate of the generator + 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, + 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87, 0x0b, 0x07, + 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, + 0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98, + // The Y coordinate of the generator + 0x48, 0x3a, 0xda, 0x77, 0x26, 0xa3, 0xc4, 0x65, + 0x5d, 0xa4, 0xfb, 0xfc, 0x0e, 0x11, 0x08, 0xa8, + 0xfd, 0x17, 0xb4, 0x48, 0xa6, 0x85, 0x54, 0x19, + 0x9c, 0x47, 0xd0, 0x8f, 0xfb, 0x10, 0xd4, 0xb8, +]; + +lazy_static! { + pub static ref CURVE_ORDER: U256 = H256::from_slice(&SECP256K1_CURVE_ORDER).into_uint(); +} + +/// Whether the public key is valid. +pub fn public_is_valid(public: &Public) -> bool { + to_secp256k1_public(public).ok() + .map_or(false, |p| p.is_valid()) +} + +/// In-place multiply public key by secret key (EC point * scalar) +pub fn public_mul_secret(public: &mut Public, secret: &Secret) -> Result<(), Error> { + let key_secret = secret.to_secp256k1_secret()?; + let mut key_public = to_secp256k1_public(public)?; + key_public.mul_assign(&SECP256K1, &key_secret)?; + set_public(public, &key_public); + Ok(()) +} + +/// In-place add one public key to another (EC point + EC point) +pub fn public_add(public: &mut Public, other: &Public) -> Result<(), Error> { + let mut key_public = to_secp256k1_public(public)?; + let other_public = to_secp256k1_public(other)?; + key_public.add_assign(&SECP256K1, &other_public)?; + set_public(public, &key_public); + Ok(()) +} + +/// In-place sub one public key from another (EC point - EC point) +pub fn public_sub(public: &mut Public, other: &Public) -> Result<(), Error> { + let mut key_neg_other = to_secp256k1_public(other)?; + key_neg_other.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; + + let mut key_public = to_secp256k1_public(public)?; + key_public.add_assign(&SECP256K1, &key_neg_other)?; + set_public(public, &key_public); + Ok(()) +} + +/// Replace a public key with its additive inverse (EC point = - EC point) +pub fn public_negate(public: &mut Public) -> Result<(), Error> { + let mut key_public = to_secp256k1_public(public)?; + key_public.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; + set_public(public, &key_public); + Ok(()) +} + +/// Return the generation point (aka base point) of secp256k1 +pub fn generation_point() -> Public { + let public_key = key::PublicKey::from_slice(&SECP256K1, &BASE_POINT_BYTES) + .expect("constructed using constants; qed"); + let mut public = Public::default(); + set_public(&mut public, &public_key); + public +} + +fn to_secp256k1_public(public: &Public) -> Result { + let public_data = { + let mut temp = [4u8; 65]; + (&mut temp[1..65]).copy_from_slice(&public[0..64]); + temp + }; + + Ok(key::PublicKey::from_slice(&SECP256K1, &public_data)?) +} + +fn set_public(public: &mut Public, key_public: &key::PublicKey) { + let key_public_serialized = key_public.serialize_vec(&SECP256K1, false); + public.as_bytes_mut().copy_from_slice(&key_public_serialized[1..65]); +} + +#[cfg(test)] +mod tests { + use super::super::{Random, Generator, Secret}; + use super::{public_add, public_sub, public_negate, public_is_valid, generation_point, public_mul_secret}; + use std::str::FromStr; + + #[test] + fn public_addition_is_commutative() { + let public1 = Random.generate().unwrap().public().clone(); + let public2 = Random.generate().unwrap().public().clone(); + + let mut left = public1.clone(); + public_add(&mut left, &public2).unwrap(); + + let mut right = public2.clone(); + public_add(&mut right, &public1).unwrap(); + + assert_eq!(left, right); + } + + #[test] + fn public_addition_is_reversible_with_subtraction() { + let public1 = Random.generate().unwrap().public().clone(); + let public2 = Random.generate().unwrap().public().clone(); + + let mut sum = public1.clone(); + public_add(&mut sum, &public2).unwrap(); + public_sub(&mut sum, &public2).unwrap(); + + assert_eq!(sum, public1); + } + + #[test] + fn public_negation_is_involutory() { + let public = Random.generate().unwrap().public().clone(); + let mut negation = public.clone(); + public_negate(&mut negation).unwrap(); + public_negate(&mut negation).unwrap(); + + assert_eq!(negation, public); + } + + #[test] + fn known_public_is_valid() { + let public = Random.generate().unwrap().public().clone(); + assert!(public_is_valid(&public)); + } + + #[test] + fn generation_point_expected() { + let point = generation_point(); + // Check the returned value equal to uncompressed form for sec2561k1 + assert_eq!(format!("{:x}", point), "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"); + } + + #[test] + fn public_multiplication_verification() { + let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); + let mut public = generation_point(); + public_mul_secret(&mut public, &secret).unwrap(); + assert_eq!(format!("{:x}", public), "8ce0db0b0359ffc5866ba61903cc2518c3675ef2cf380a7e54bde7ea20e6fa1ab45b7617346cd11b7610001ee6ae5b0155c41cad9527cbcdff44ec67848943a4"); + } +} diff --git a/parity-crypto/src/publickey/ecdh.rs b/parity-crypto/src/publickey/ecdh.rs new file mode 100644 index 000000000..73d25491c --- /dev/null +++ b/parity-crypto/src/publickey/ecdh.rs @@ -0,0 +1,37 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! ECDH key agreement scheme implemented as a free function. + +use secp256k1::{self, ecdh, key}; +use super::{Error, Secret, Public, SECP256K1}; + +/// Agree on a shared secret +pub fn agree(secret: &Secret, public: &Public) -> Result { + let context = &SECP256K1; + let pdata = { + let mut temp = [4u8; 65]; + (&mut temp[1..65]).copy_from_slice(&public[0..64]); + temp + }; + + let publ = key::PublicKey::from_slice(context, &pdata)?; + let sec = key::SecretKey::from_slice(context, secret.as_bytes())?; + let shared = ecdh::SharedSecret::new_raw(context, &publ, &sec); + + Secret::import_key(&shared[0..32]) + .map_err(|_| Error::Secp(secp256k1::Error::InvalidSecretKey)) +} diff --git a/parity-crypto/src/publickey/ecdsa_signature.rs b/parity-crypto/src/publickey/ecdsa_signature.rs new file mode 100644 index 000000000..421fa9b61 --- /dev/null +++ b/parity-crypto/src/publickey/ecdsa_signature.rs @@ -0,0 +1,326 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Signature based on ECDSA, algorithm's description: https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm + +use std::ops::{Deref, DerefMut}; +use std::cmp::PartialEq; +use std::fmt; +use std::str::FromStr; +use std::hash::{Hash, Hasher}; +use secp256k1::{Message as SecpMessage, RecoverableSignature, RecoveryId, Error as SecpError}; +use secp256k1::key::{SecretKey, PublicKey}; +use rustc_hex::{ToHex, FromHex}; +use ethereum_types::{H520, H256}; +use super::{Secret, Public, SECP256K1, Message, public_to_address, Address, Error}; + +/// Signature encoded as RSV components +#[repr(C)] +pub struct Signature([u8; 65]); + +impl Signature { + /// Get a slice into the 'r' portion of the data. + pub fn r(&self) -> &[u8] { + &self.0[0..32] + } + + /// Get a slice into the 's' portion of the data. + pub fn s(&self) -> &[u8] { + &self.0[32..64] + } + + /// Get the recovery byte. + pub fn v(&self) -> u8 { + self.0[64] + } + + /// Encode the signature into RSV array (V altered to be in "Electrum" notation). + pub fn into_electrum(mut self) -> [u8; 65] { + self.0[64] += 27; + self.0 + } + + /// Parse bytes as a signature encoded as RSV (V in "Electrum" notation). + /// May return empty (invalid) signature if given data has invalid length. + pub fn from_electrum(data: &[u8]) -> Self { + if data.len() != 65 || data[64] < 27 { + // fallback to empty (invalid) signature + return Signature::default(); + } + + let mut sig = [0u8; 65]; + sig.copy_from_slice(data); + sig[64] -= 27; + Signature(sig) + } + + /// Create a signature object from the RSV triple. + pub fn from_rsv(r: &H256, s: &H256, v: u8) -> Self { + let mut sig = [0u8; 65]; + sig[0..32].copy_from_slice(r.as_ref()); + sig[32..64].copy_from_slice(s.as_ref()); + sig[64] = v; + Signature(sig) + } + + /// Check if this is a "low" signature (that s part of the signature is in range + /// 0x1 and 0x7FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 5D576E73 57A4501D DFE92F46 681B20A0 (inclusive)). + /// This condition may be required by some verification algorithms + pub fn is_low_s(&self) -> bool { + const LOW_SIG_THRESHOLD: H256 = H256([ + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, + 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0, + ]); + H256::from_slice(self.s()) <= LOW_SIG_THRESHOLD + } + + /// Check if each component of the signature is in valid range. + /// r is in range 0x1 and 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 (inclusive) + /// s is in range 0x1 and fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 (inclusive) + /// v is 0 or 1 + /// Group order for secp256k1 defined as 'n' in "Standards for Efficient Cryptography" (SEC2) 2.7.1; + /// used here as the upper bound for a valid (r, s, v) tuple + pub fn is_valid(&self) -> bool { + const UPPER_BOUND: H256 = H256([ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41, + ]); + const ONE: H256 = H256([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + ]); + let r = H256::from_slice(self.r()); + let s = H256::from_slice(self.s()); + self.v() <= 1 && + r < UPPER_BOUND && r >= ONE && + s < UPPER_BOUND && s >= ONE + } +} + +// manual implementation large arrays don't have trait impls by default. +// TODO[grbIzl] remove when integer generics exist +impl PartialEq for Signature { + fn eq(&self, other: &Self) -> bool { + &self.0[..] == &other.0[..] + } +} + +// manual implementation required in Rust 1.13+, see `std::cmp::AssertParamIsEq`. +impl Eq for Signature { } + +// also manual for the same reason, but the pretty printing might be useful. +impl fmt::Debug for Signature { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + f.debug_struct("Signature") + .field("r", &self.0[0..32].to_hex::()) + .field("s", &self.0[32..64].to_hex::()) + .field("v", &self.0[64..65].to_hex::()) + .finish() + } +} + +impl fmt::Display for Signature { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", self.to_hex::()) + } +} + +impl FromStr for Signature { + type Err = Error; + + fn from_str(s: &str) -> Result { + match s.from_hex::>() { + Ok(ref hex) if hex.len() == 65 => { + let mut data = [0; 65]; + data.copy_from_slice(&hex[0..65]); + Ok(Signature(data)) + }, + _ => Err(Error::InvalidSignature) + } + } +} + +impl Default for Signature { + fn default() -> Self { + Signature([0; 65]) + } +} + +impl Hash for Signature { + fn hash(&self, state: &mut H) { + H520::from(self.0).hash(state); + } +} + +impl Clone for Signature { + fn clone(&self) -> Self { + Signature(self.0.clone()) + } +} + +impl From<[u8; 65]> for Signature { + fn from(s: [u8; 65]) -> Self { + Signature(s) + } +} + +impl Into<[u8; 65]> for Signature { + fn into(self) -> [u8; 65] { + self.0 + } +} + +impl From for H520 { + fn from(s: Signature) -> Self { + H520::from(s.0) + } +} + +impl From for Signature { + fn from(bytes: H520) -> Self { + Signature(bytes.into()) + } +} + +impl Deref for Signature { + type Target = [u8; 65]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Signature { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// Signs message with the given secret key. +/// Returns the corresponding signature +pub fn sign(secret: &Secret, message: &Message) -> Result { + let context = &SECP256K1; + let sec = SecretKey::from_slice(context, secret.as_ref())?; + let s = context.sign_recoverable(&SecpMessage::from_slice(&message[..])?, &sec)?; + let (rec_id, data) = s.serialize_compact(context); + let mut data_arr = [0; 65]; + + // no need to check if s is low, it always is + data_arr[0..64].copy_from_slice(&data[0..64]); + data_arr[64] = rec_id.to_i32() as u8; + Ok(Signature(data_arr)) +} + +/// Performs verification of the signature for the given message with corresponding public key +pub fn verify_public(public: &Public, signature: &Signature, message: &Message) -> Result { + let context = &SECP256K1; + let rsig = RecoverableSignature::from_compact(context, &signature[0..64], RecoveryId::from_i32(signature[64] as i32)?)?; + let sig = rsig.to_standard(context); + + let pdata: [u8; 65] = { + let mut temp = [4u8; 65]; + temp[1..65].copy_from_slice(public.as_bytes()); + temp + }; + + let publ = PublicKey::from_slice(context, &pdata)?; + match context.verify(&SecpMessage::from_slice(&message[..])?, &sig, &publ) { + Ok(_) => Ok(true), + Err(SecpError::IncorrectSignature) => Ok(false), + Err(x) => Err(Error::from(x)) + } +} + +/// Checks if the address corresponds to the public key from the signature for the message +pub fn verify_address(address: &Address, signature: &Signature, message: &Message) -> Result { + let public = recover(signature, message)?; + let recovered_address = public_to_address(&public); + Ok(address == &recovered_address) +} + +/// Recovers the public key from the signature for the message +pub fn recover(signature: &Signature, message: &Message) -> Result { + let context = &SECP256K1; + let rsig = RecoverableSignature::from_compact(context, &signature[0..64], RecoveryId::from_i32(signature[64] as i32)?)?; + let pubkey = context.recover(&SecpMessage::from_slice(&message[..])?, &rsig)?; + let serialized = pubkey.serialize_vec(context, false); + + let mut public = Public::default(); + public.as_bytes_mut().copy_from_slice(&serialized[1..65]); + Ok(public) +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + use super::super::{Generator, Random, Message}; + use super::{sign, verify_public, verify_address, recover, Signature}; + + #[test] + fn vrs_conversion() { + // given + let keypair = Random.generate().unwrap(); + let message = Message::default(); + let signature = sign(keypair.secret(), &message).unwrap(); + + // when + let vrs = signature.clone().into_electrum(); + let from_vrs = Signature::from_electrum(&vrs); + + // then + assert_eq!(signature, from_vrs); + } + + #[test] + fn signature_to_and_from_str() { + let keypair = Random.generate().unwrap(); + let message = Message::default(); + let signature = sign(keypair.secret(), &message).unwrap(); + let string = format!("{}", signature); + let deserialized = Signature::from_str(&string).unwrap(); + assert_eq!(signature, deserialized); + } + + #[test] + fn sign_and_recover_public() { + let keypair = Random.generate().unwrap(); + let message = Message::default(); + let signature = sign(keypair.secret(), &message).unwrap(); + assert_eq!(keypair.public(), &recover(&signature, &message).unwrap()); + } + + #[test] + fn sign_and_verify_public() { + let keypair = Random.generate().unwrap(); + let message = Message::default(); + let signature = sign(keypair.secret(), &message).unwrap(); + assert!(verify_public(keypair.public(), &signature, &message).unwrap()); + } + + #[test] + fn sign_and_verify_address() { + let keypair = Random.generate().unwrap(); + let message = Message::default(); + let signature = sign(keypair.secret(), &message).unwrap(); + assert!(verify_address(&keypair.address(), &signature, &message).unwrap()); + } +} diff --git a/parity-crypto/src/publickey/ecies.rs b/parity-crypto/src/publickey/ecies.rs new file mode 100644 index 000000000..b54ce717e --- /dev/null +++ b/parity-crypto/src/publickey/ecies.rs @@ -0,0 +1,138 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Functions for ECIES scheme encryption and decryption + +use ethereum_types::H128; +use super::{Error, Random, Generator, Public, Secret, ecdh}; +use crate::{aes, digest, hmac, is_equal}; + +const ENC_VERSION: u8 = 0x04; + +/// Encrypt a message with a public key, writing an HMAC covering both +/// the plaintext and authenticated data. +/// +/// Authenticated data may be empty. +pub fn encrypt(public: &Public, auth_data: &[u8], plain: &[u8]) -> Result, Error> { + let r = Random.generate()?; + let z = ecdh::agree(r.secret(), public)?; + let mut key = [0u8; 32]; + kdf(&z, &[0u8; 0], &mut key); + + let ekey = &key[0..16]; + let mkey = hmac::SigKey::sha256(&digest::sha256(&key[16..32])); + + let mut msg = vec![0u8; 1 + 64 + 16 + plain.len() + 32]; + msg[0] = ENC_VERSION; + { + let result_msg = &mut msg[1..]; + result_msg[0..64].copy_from_slice(r.public().as_bytes()); + let iv = H128::random(); + result_msg[64..80].copy_from_slice(iv.as_bytes()); + { + let cipher = &mut result_msg[(64 + 16)..(64 + 16 + plain.len())]; + aes::encrypt_128_ctr(ekey, iv.as_bytes(), plain, cipher)?; + } + let mut hmac = hmac::Signer::with(&mkey); + { + let cipher_iv = &result_msg[64..(64 + 16 + plain.len())]; + hmac.update(cipher_iv); + } + hmac.update(auth_data); + let sig = hmac.sign(); + result_msg[(64 + 16 + plain.len())..].copy_from_slice(&sig); + } + Ok(msg) +} + +/// Decrypt a message with a secret key, checking HMAC for ciphertext +/// and authenticated data validity. +pub fn decrypt(secret: &Secret, auth_data: &[u8], encrypted: &[u8]) -> Result, Error> { + const META_LEN: usize = 1 + 64 + 16 + 32; + let enc_version = encrypted[0]; + if encrypted.len() < META_LEN || enc_version < 2 || enc_version > 4 { + return Err(Error::InvalidMessage); + } + + let e = &encrypted[1..]; + let p = Public::from_slice(&e[0..64]); + let z = ecdh::agree(secret, &p)?; + let mut key = [0u8; 32]; + kdf(&z, &[0u8; 0], &mut key); + + let ekey = &key[0..16]; + let mkey = hmac::SigKey::sha256(&digest::sha256(&key[16..32])); + + let cipher_text_len = encrypted.len() - META_LEN; + let cipher_with_iv = &e[64..(64 + 16 + cipher_text_len)]; + let cipher_iv = &cipher_with_iv[0..16]; + let cipher_no_iv = &cipher_with_iv[16..]; + let msg_mac = &e[(64 + 16 + cipher_text_len)..]; + + // Verify tag + let mut hmac = hmac::Signer::with(&mkey); + hmac.update(cipher_with_iv); + hmac.update(auth_data); + let mac = hmac.sign(); + + if !is_equal(&mac.as_ref()[..], msg_mac) { + return Err(Error::InvalidMessage); + } + + let mut msg = vec![0u8; cipher_text_len]; + aes::decrypt_128_ctr(ekey, cipher_iv, cipher_no_iv, &mut msg[..])?; + Ok(msg) +} + +fn kdf(secret: &Secret, s1: &[u8], dest: &mut [u8]) { + // SEC/ISO/Shoup specify counter size SHOULD be equivalent + // to size of hash output, however, it also notes that + // the 4 bytes is okay. NIST specifies 4 bytes. + let mut ctr = 1u32; + let mut written = 0usize; + while written < dest.len() { + let mut hasher = digest::Hasher::sha256(); + let ctrs = [(ctr >> 24) as u8, (ctr >> 16) as u8, (ctr >> 8) as u8, ctr as u8]; + hasher.update(&ctrs); + hasher.update(secret.as_bytes()); + hasher.update(s1); + let d = hasher.finish(); + &mut dest[written..(written + 32)].copy_from_slice(&d); + written += 32; + ctr += 1; + } +} + +#[cfg(test)] +mod tests { + use super::super::{ecies, Random, Generator}; + + #[test] + fn ecies_shared() { + let kp = Random.generate().unwrap(); + let message = b"So many books, so little time"; + + let shared = b"shared"; + let wrong_shared = b"incorrect"; + let encrypted = ecies::encrypt(kp.public(), shared, message).unwrap(); + assert!(encrypted[..] != message[..]); + assert_eq!(encrypted[0], 0x04); + + assert!(ecies::decrypt(kp.secret(), wrong_shared, &encrypted).is_err()); + let decrypted = ecies::decrypt(kp.secret(), shared, &encrypted).unwrap(); + assert_eq!(decrypted[..message.len()], message[..]); + } +} diff --git a/parity-crypto/src/publickey/error.rs b/parity-crypto/src/publickey/error.rs new file mode 100644 index 000000000..2cc66f733 --- /dev/null +++ b/parity-crypto/src/publickey/error.rs @@ -0,0 +1,99 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Module specific errors + +use std::{fmt, result, error::Error as StdError}; +use crate::error::SymmError; + +/// Module specific errors +#[derive(Debug)] +pub enum Error { + /// secp256k1 enc error + Secp(secp256k1::Error), + /// Invalid secret key + InvalidSecretKey, + /// Invalid public key + InvalidPublicKey, + /// Invalid address + InvalidAddress, + /// Invalid EC signature + InvalidSignature, + /// Invalid AES message + InvalidMessage, + /// IO Error + Io(std::io::Error), + /// Symmetric encryption error + Symm(SymmError), + /// Custom + Custom(String), +} + +impl StdError for Error { + fn source(&self) -> Option<&(StdError + 'static)> { + match self { + Error::Secp(secp_err) => Some(secp_err), + Error::Io(err) => Some(err), + Error::Symm(symm_err) => Some(symm_err), + _ => None, + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { + match self { + Error::Secp(err) => write!(f, "secp error: {}", err), + Error::InvalidSecretKey => write!(f, "invalid secret key"), + Error::InvalidPublicKey => write!(f, "invalid public key"), + Error::InvalidAddress => write!(f, "invalid address"), + Error::InvalidSignature => write!(f, "invalid EC signature"), + Error::InvalidMessage => write!(f, "invalid AES message"), + Error::Io(err) => write!(f, "I/O error: {}", err), + Error::Symm(err) => write!(f, "symmetric encryption error: {}", err), + Error::Custom(err) => write!(f, "custom crypto error: {}", err), + } + } +} + +impl Into for Error { + fn into(self) -> String { + format!("{}", self) + } +} + +impl From for Error { + fn from(err: std::io::Error) -> Error { + Error::Io(err) + } +} + +impl From for Error { + fn from(err: SymmError) -> Error { + Error::Symm(err) + } +} + +impl From for Error { + fn from(e: secp256k1::Error) -> Error { + match e { + secp256k1::Error::InvalidMessage => Error::InvalidMessage, + secp256k1::Error::InvalidPublicKey => Error::InvalidPublicKey, + secp256k1::Error::InvalidSecretKey => Error::InvalidSecretKey, + _ => Error::InvalidSignature, + } + } +} diff --git a/parity-crypto/src/publickey/extended_keys.rs b/parity-crypto/src/publickey/extended_keys.rs new file mode 100644 index 000000000..e585672e8 --- /dev/null +++ b/parity-crypto/src/publickey/extended_keys.rs @@ -0,0 +1,521 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Secret, public keys extended with the entropy (aka chain code), that allows further key derivation +//! Each extended key has 2^31 normal child keys, and 2^31 hardened child keys. +//! Each of these child keys has an index. The normal child keys use indices 0 through 2^31 - 1. +//! The hardened child keys use indices 2^31 through 2^32 - 1. +//! See more details about derivation in https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki + +use super::{Secret, Public}; +use ethereum_types::H256; +pub use self::derivation::Error as DerivationError; + +/// Represents label that can be stored as a part of key derivation +pub trait Label { + /// Length of the data that label occupies + fn len() -> usize; + + /// Store label data to the key derivation sequence + /// Must not use more than `len()` bytes from slice + fn store(&self, target: &mut [u8]); +} + +impl Label for u32 { + fn len() -> usize { 4 } + + fn store(&self, target: &mut [u8]) { + let bytes = self.to_be_bytes(); + target[0..4].copy_from_slice(&bytes); + } +} + +/// Key derivation over generic label `T` +pub enum Derivation { + /// Soft key derivation (allow proof of parent) + Soft(T), + /// Hard key derivation (does not allow proof of parent) + Hard(T), +} + +impl From for Derivation { + fn from(index: u32) -> Self { + // Type of the derived key is defined by it index + // See module's documentation for more details + if index < (2 << 30) { + Derivation::Soft(index) + } + else { + Derivation::Hard(index) + } + } +} + +impl Label for H256 { + fn len() -> usize { 32 } + + fn store(&self, target: &mut [u8]) { + (&mut target[0..32]).copy_from_slice(self.as_bytes()); + } +} + +/// Extended secret key, allows deterministic derivation of subsequent keys. +pub struct ExtendedSecret { + secret: Secret, + chain_code: H256, +} + +impl ExtendedSecret { + /// New extended key from given secret and chain code. + pub fn with_code(secret: Secret, chain_code: H256) -> ExtendedSecret { + ExtendedSecret { + secret: secret, + chain_code: chain_code, + } + } + + /// New extended key from given secret with the random chain code. + pub fn new_random(secret: Secret) -> ExtendedSecret { + ExtendedSecret::with_code(secret, H256::random()) + } + + /// New extended key from given secret. + /// Chain code will be derived from the secret itself (in a deterministic way). + pub fn new(secret: Secret) -> ExtendedSecret { + let chain_code = derivation::chain_code(*secret); + ExtendedSecret::with_code(secret, chain_code) + } + + /// Derive new private key + pub fn derive(&self, index: Derivation) -> ExtendedSecret where T: Label { + let (derived_key, next_chain_code) = derivation::private(*self.secret, self.chain_code, index); + + let derived_secret = Secret::from(derived_key.0); + + ExtendedSecret::with_code(derived_secret, next_chain_code) + } + + /// Private key component of the extended key. + pub fn as_raw(&self) -> &Secret { + &self.secret + } +} + +/// Extended public key, allows deterministic derivation of subsequent keys. +pub struct ExtendedPublic { + public: Public, + chain_code: H256, +} + +impl ExtendedPublic { + /// New extended public key from known parent and chain code + pub fn new(public: Public, chain_code: H256) -> Self { + ExtendedPublic { public: public, chain_code: chain_code } + } + + /// Create new extended public key from known secret + pub fn from_secret(secret: &ExtendedSecret) -> Result { + Ok( + ExtendedPublic::new( + derivation::point(**secret.as_raw())?, + secret.chain_code.clone(), + ) + ) + } + + /// Derive new public key + /// Operation is defined only for index belongs [0..2^31) + pub fn derive(&self, index: Derivation) -> Result where T: Label { + let (derived_key, next_chain_code) = derivation::public(self.public, self.chain_code, index)?; + Ok(ExtendedPublic::new(derived_key, next_chain_code)) + } + + pub fn public(&self) -> &Public { + &self.public + } +} + +pub struct ExtendedKeyPair { + secret: ExtendedSecret, + public: ExtendedPublic, +} + +impl ExtendedKeyPair { + pub fn new(secret: Secret) -> Self { + let extended_secret = ExtendedSecret::new(secret); + let extended_public = ExtendedPublic::from_secret(&extended_secret) + .expect("Valid `Secret` always produces valid public; qed"); + ExtendedKeyPair { + secret: extended_secret, + public: extended_public, + } + } + + pub fn with_code(secret: Secret, public: Public, chain_code: H256) -> Self { + ExtendedKeyPair { + secret: ExtendedSecret::with_code(secret, chain_code.clone()), + public: ExtendedPublic::new(public, chain_code), + } + } + + pub fn with_secret(secret: Secret, chain_code: H256) -> Self { + let extended_secret = ExtendedSecret::with_code(secret, chain_code); + let extended_public = ExtendedPublic::from_secret(&extended_secret) + .expect("Valid `Secret` always produces valid public; qed"); + ExtendedKeyPair { + secret: extended_secret, + public: extended_public, + } + } + + pub fn with_seed(seed: &[u8]) -> Result { + let (master_key, chain_code) = derivation::seed_pair(seed); + Ok(ExtendedKeyPair::with_secret( + Secret::import_key(master_key.as_bytes()).map_err(|_| DerivationError::InvalidSeed)?, + chain_code, + )) + } + + pub fn secret(&self) -> &ExtendedSecret { + &self.secret + } + + pub fn public(&self) -> &ExtendedPublic { + &self.public + } + + pub fn derive(&self, index: Derivation) -> Result where T: Label { + let derived = self.secret.derive(index); + + Ok(ExtendedKeyPair { + public: ExtendedPublic::from_secret(&derived)?, + secret: derived, + }) + } +} + +// Derivation functions for private and public keys +// Work is based on BIP0032 +// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki +mod derivation { + use crate::{hmac, Keccak256}; + use super::super::SECP256K1; + use ethereum_types::{BigEndianHash, U256, U512, H512, H256}; + use secp256k1::key::{SecretKey, PublicKey}; + use super::super::ec_math_utils::CURVE_ORDER; + use super::{Label, Derivation}; + use std::convert::TryInto; + + #[derive(Debug)] + pub enum Error { + InvalidHardenedUse, + InvalidPoint, + MissingIndex, + InvalidSeed, + } + + // Deterministic derivation of the key using secp256k1 elliptic curve. + // Derivation can be either hardened or not. + // For hardened derivation, pass u32 index at least 2^31 or custom Derivation::Hard(T) enum + // + // Can panic if passed `private_key` is not a valid secp256k1 private key + // (outside of (0..curve_order()]) field + pub fn private(private_key: H256, chain_code: H256, index: Derivation) -> (H256, H256) where T: Label { + match index { + Derivation::Soft(index) => private_soft(private_key, chain_code, index), + Derivation::Hard(index) => private_hard(private_key, chain_code, index), + } + } + + fn hmac_pair(data: &[u8], private_key: H256, chain_code: H256) -> (H256, H256) { + let private: U256 = private_key.into_uint(); + + // produces 512-bit derived hmac (I) + let skey = hmac::SigKey::sha512(chain_code.as_bytes()); + let i_512 = hmac::sign(&skey, &data[..]); + + // left most 256 bits are later added to original private key + let hmac_key: U256 = H256::from_slice(&i_512[0..32]).into_uint(); + // right most 256 bits are new chain code for later derivations + let next_chain_code = H256::from_slice(&i_512[32..64]); + + let child_key = BigEndianHash::from_uint(&private_add(hmac_key, private)); + (child_key, next_chain_code) + } + + // Can panic if passed `private_key` is not a valid secp256k1 private key + // (outside of (0..curve_order()]) field + fn private_soft(private_key: H256, chain_code: H256, index: T) -> (H256, H256) where T: Label { + let mut data = vec![0u8; 33 + T::len()]; + + let sec_private = SecretKey::from_slice(&SECP256K1, private_key.as_bytes()) + .expect("Caller should provide valid private key"); + let sec_public = PublicKey::from_secret_key(&SECP256K1, &sec_private) + .expect("Caller should provide valid private key"); + let public_serialized = sec_public.serialize_vec(&SECP256K1, true); + + // curve point (compressed public key) -- index + // 0.33 -- 33..end + data[0..33].copy_from_slice(&public_serialized); + index.store(&mut data[33..]); + + hmac_pair(&data, private_key, chain_code) + } + + // Deterministic derivation of the key using secp256k1 elliptic curve + // This is hardened derivation and does not allow to associate + // corresponding public keys of the original and derived private keys + fn private_hard(private_key: H256, chain_code: H256, index: T) -> (H256, H256) where T: Label { + let mut data: Vec = vec![0u8; 33 + T::len()]; + let private: U256 = private_key.into_uint(); + + // 0x00 (padding) -- private_key -- index + // 0 -- 1..33 -- 33..end + private.to_big_endian(&mut data[1..33]); + index.store(&mut data[33..(33 + T::len())]); + + hmac_pair(&data, private_key, chain_code) + } + + fn private_add(k1: U256, k2: U256) -> U256 { + let sum = U512::from(k1) + U512::from(k2); + modulo(sum, *CURVE_ORDER) + } + + // todo: surely can be optimized + fn modulo(u1: U512, u2: U256) -> U256 { + let m = u1 % U512::from(u2); + m.try_into().expect("U512 modulo U256 should fit into U256; qed") + } + + pub fn public(public_key: H512, chain_code: H256, derivation: Derivation) -> Result<(H512, H256), Error> where T: Label { + let index = match derivation { + Derivation::Soft(index) => index, + Derivation::Hard(_) => { return Err(Error::InvalidHardenedUse); } + }; + + let mut public_sec_raw = [0u8; 65]; + public_sec_raw[0] = 4; + public_sec_raw[1..65].copy_from_slice(public_key.as_bytes()); + let public_sec = PublicKey::from_slice(&SECP256K1, &public_sec_raw).map_err(|_| Error::InvalidPoint)?; + let public_serialized = public_sec.serialize_vec(&SECP256K1, true); + + let mut data = vec![0u8; 33 + T::len()]; + // curve point (compressed public key) -- index + // 0.33 -- 33..end + data[0..33].copy_from_slice(&public_serialized); + index.store(&mut data[33..(33 + T::len())]); + + // HMAC512SHA produces [derived private(256); new chain code(256)] + let skey = hmac::SigKey::sha512(chain_code.as_bytes()); + let i_512 = hmac::sign(&skey, &data[..]); + + let new_private = H256::from_slice(&i_512[0..32]); + let new_chain_code = H256::from_slice(&i_512[32..64]); + + // Generated private key can (extremely rarely) be out of secp256k1 key field + if *CURVE_ORDER <= new_private.into_uint() { return Err(Error::MissingIndex); } + let new_private_sec = SecretKey::from_slice(&SECP256K1, new_private.as_bytes()) + .expect("Private key belongs to the field [0..CURVE_ORDER) (checked above); So initializing can never fail; qed"); + let mut new_public = PublicKey::from_secret_key(&SECP256K1, &new_private_sec) + .expect("Valid private key produces valid public key"); + + // Adding two points on the elliptic curves (combining two public keys) + new_public.add_assign(&SECP256K1, &public_sec) + .expect("Addition of two valid points produce valid point"); + + let serialized = new_public.serialize_vec(&SECP256K1, false); + + Ok(( + H512::from_slice(&serialized[1..65]), + new_chain_code, + )) + } + + fn sha3(slc: &[u8]) -> H256 { + slc.keccak256().into() + } + + pub fn chain_code(secret: H256) -> H256 { + // 10,000 rounds of sha3 + let mut running_sha3 = sha3(secret.as_bytes()); + for _ in 0..99999 { running_sha3 = sha3(running_sha3.as_bytes()); } + running_sha3 + } + + pub fn point(secret: H256) -> Result { + let sec = SecretKey::from_slice(&SECP256K1, secret.as_bytes()) + .map_err(|_| Error::InvalidPoint)?; + let public_sec = PublicKey::from_secret_key(&SECP256K1, &sec) + .map_err(|_| Error::InvalidPoint)?; + let serialized = public_sec.serialize_vec(&SECP256K1, false); + Ok(H512::from_slice(&serialized[1..65])) + } + + pub fn seed_pair(seed: &[u8]) -> (H256, H256) { + let skey = hmac::SigKey::sha512(b"Bitcoin seed"); + let i_512 = hmac::sign(&skey, seed); + + let master_key = H256::from_slice(&i_512[0..32]); + let chain_code = H256::from_slice(&i_512[32..64]); + + (master_key, chain_code) + } +} + +#[cfg(test)] +mod tests { + use super::{ExtendedSecret, ExtendedPublic, ExtendedKeyPair}; + use super::super::Secret; + use std::str::FromStr; + use ethereum_types::{H128, H256, H512}; + use super::{derivation, Derivation}; + + fn master_chain_basic() -> (H256, H256) { + let seed = H128::from_str("000102030405060708090a0b0c0d0e0f") + .expect("Seed should be valid H128") + .as_bytes() + .to_vec(); + + derivation::seed_pair(&*seed) + } + + fn test_extended(f: F, test_private: H256) where F: Fn(ExtendedSecret) -> ExtendedSecret { + let (private_seed, chain_code) = master_chain_basic(); + let extended_secret = ExtendedSecret::with_code(Secret::from(private_seed.0), chain_code); + let derived = f(extended_secret); + assert_eq!(**derived.as_raw(), test_private); + } + + #[test] + fn smoky() { + let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); + let extended_secret = ExtendedSecret::with_code(secret.clone(), H256::zero()); + + // hardened + assert_eq!(&**extended_secret.as_raw(), &*secret); + assert_eq!( + **extended_secret.derive(2147483648.into()).as_raw(), + H256::from_str("0927453daed47839608e414a3738dfad10aed17c459bbd9ab53f89b026c834b6").unwrap(), + ); + assert_eq!( + **extended_secret.derive(2147483649.into()).as_raw(), + H256::from_str("44238b6a29c6dcbe9b401364141ba11e2198c289a5fed243a1c11af35c19dc0f").unwrap(), + ); + + // normal + assert_eq!(**extended_secret.derive(0.into()).as_raw(), H256::from_str("bf6a74e3f7b36fc4c96a1e12f31abc817f9f5904f5a8fc27713163d1f0b713f6").unwrap()); + assert_eq!(**extended_secret.derive(1.into()).as_raw(), H256::from_str("bd4fca9eb1f9c201e9448c1eecd66e302d68d4d313ce895b8c134f512205c1bc").unwrap()); + assert_eq!(**extended_secret.derive(2.into()).as_raw(), H256::from_str("86932b542d6cab4d9c65490c7ef502d89ecc0e2a5f4852157649e3251e2a3268").unwrap()); + + let extended_public = ExtendedPublic::from_secret(&extended_secret).expect("Extended public should be created"); + let derived_public = extended_public.derive(0.into()).expect("First derivation of public should succeed"); + assert_eq!( + *derived_public.public(), + H512::from_str("f7b3244c96688f92372bfd4def26dc4151529747bab9f188a4ad34e141d47bd66522ff048bc6f19a0a4429b04318b1a8796c000265b4fa200dae5f6dda92dd94").unwrap(), + ); + + let keypair = ExtendedKeyPair::with_secret( + Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(), + H256::from_low_u64_be(64), + ); + assert_eq!( + **keypair.derive(2147483648u32.into()).expect("Derivation of keypair should succeed").secret().as_raw(), + H256::from_str("edef54414c03196557cf73774bc97a645c9a1df2164ed34f0c2a78d1375a930c").unwrap(), + ); + } + + #[test] + fn h256_soft_match() { + let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); + let derivation_secret = H256::from_str("51eaf04f9dbbc1417dc97e789edd0c37ecda88bac490434e367ea81b71b7b015").unwrap(); + + let extended_secret = ExtendedSecret::with_code(secret.clone(), H256::zero()); + let extended_public = ExtendedPublic::from_secret(&extended_secret).expect("Extended public should be created"); + + let derived_secret0 = extended_secret.derive(Derivation::Soft(derivation_secret)); + let derived_public0 = extended_public.derive(Derivation::Soft(derivation_secret)).expect("First derivation of public should succeed"); + + let public_from_secret0 = ExtendedPublic::from_secret(&derived_secret0).expect("Extended public should be created"); + + assert_eq!(public_from_secret0.public(), derived_public0.public()); + } + + #[test] + fn h256_hard() { + let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); + let derivation_secret = H256::from_str("51eaf04f9dbbc1417dc97e789edd0c37ecda88bac490434e367ea81b71b7b015").unwrap(); + let extended_secret = ExtendedSecret::with_code(secret.clone(), H256::from_low_u64_be(1)); + + assert_eq!( + **extended_secret.derive(Derivation::Hard(derivation_secret)).as_raw(), + H256::from_str("2bc2d696fb744d77ff813b4a1ef0ad64e1e5188b622c54ba917acc5ebc7c5486").unwrap(), + ); + } + + #[test] + fn match_() { + let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); + let extended_secret = ExtendedSecret::with_code(secret.clone(), H256::from_low_u64_be(1)); + let extended_public = ExtendedPublic::from_secret(&extended_secret).expect("Extended public should be created"); + + let derived_secret0 = extended_secret.derive(0.into()); + let derived_public0 = extended_public.derive(0.into()).expect("First derivation of public should succeed"); + + let public_from_secret0 = ExtendedPublic::from_secret(&derived_secret0).expect("Extended public should be created"); + + assert_eq!(public_from_secret0.public(), derived_public0.public()); + } + + #[test] + fn test_seeds() { + let seed = H128::from_str("000102030405060708090a0b0c0d0e0f") + .expect("Seed should be valid H128") + .as_bytes() + .to_vec(); + + // private key from bitcoin test vector + // xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs + let test_private = H256::from_str("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35") + .expect("Private should be decoded ok"); + + let (private_seed, _) = derivation::seed_pair(&*seed); + + assert_eq!(private_seed, test_private); + } + + #[test] + fn test_vector_1() { + // xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7 + // H(0) + test_extended( + |secret| secret.derive(2147483648.into()), + H256::from_str("edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea") + .expect("Private should be decoded ok") + ); + } + + #[test] + fn test_vector_2() { + // xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs + // H(0)/1 + test_extended( + |secret| secret.derive(2147483648.into()).derive(1.into()), + H256::from_str("3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368") + .expect("Private should be decoded ok") + ); + } +} diff --git a/parity-crypto/src/publickey/keypair.rs b/parity-crypto/src/publickey/keypair.rs new file mode 100644 index 000000000..04a9dbadd --- /dev/null +++ b/parity-crypto/src/publickey/keypair.rs @@ -0,0 +1,122 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Key pair (public + secrect) description + +use std::fmt; +use secp256k1::key; +use super::{Secret, Public, Address, SECP256K1, Error}; +use crate::Keccak256; + +/// Convert public key into the address +pub fn public_to_address(public: &Public) -> Address { + let hash = public.keccak256(); + let mut result = Address::zero(); + result.as_bytes_mut().copy_from_slice(&hash[12..]); + result +} + +#[derive(Debug, Clone, PartialEq)] +/// secp256k1 key pair +pub struct KeyPair { + secret: Secret, + public: Public, +} + +impl fmt::Display for KeyPair { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + writeln!(f, "secret: {:x}", self.secret)?; + writeln!(f, "public: {:x}", self.public)?; + write!(f, "address: {:x}", self.address()) + } +} + +impl KeyPair { + /// Create a pair from secret key + pub fn from_secret(secret: Secret) -> Result { + let context = &SECP256K1; + let s: key::SecretKey = key::SecretKey::from_slice(context, &secret[..])?; + let pub_key = key::PublicKey::from_secret_key(context, &s)?; + let serialized = pub_key.serialize_vec(context, false); + + let mut public = Public::default(); + public.as_bytes_mut().copy_from_slice(&serialized[1..65]); + + let keypair = KeyPair { + secret: secret, + public: public, + }; + + Ok(keypair) + } + + /// Create a pair from the slice, which imported and verified as secret key + pub fn from_secret_slice(slice: &[u8]) -> Result { + Self::from_secret(Secret::import_key(slice)?) + } + + /// Copies a pair from another one + pub fn from_keypair(sec: key::SecretKey, publ: key::PublicKey) -> Self { + let context = &SECP256K1; + let serialized = publ.serialize_vec(context, false); + let secret = Secret::from(sec); + let mut public = Public::default(); + public.as_bytes_mut().copy_from_slice(&serialized[1..65]); + + KeyPair { + secret, + public, + } + } + + /// Returns secret part of the keypair + pub fn secret(&self) -> &Secret { + &self.secret + } + + /// Returns public part of the keypair + pub fn public(&self) -> &Public { + &self.public + } + + /// Returns public part of the keypair converted into Address + pub fn address(&self) -> Address { + public_to_address(&self.public) + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + use super::{KeyPair, Secret}; + + #[test] + fn from_secret() { + let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); + let _ = KeyPair::from_secret(secret).unwrap(); + } + + #[test] + fn keypair_display() { + let expected = +"secret: a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65 +public: 8ce0db0b0359ffc5866ba61903cc2518c3675ef2cf380a7e54bde7ea20e6fa1ab45b7617346cd11b7610001ee6ae5b0155c41cad9527cbcdff44ec67848943a4 +address: 5b073e9233944b5e729e46d618f0d8edf3d9c34a".to_owned(); + let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); + let kp = KeyPair::from_secret(secret).unwrap(); + assert_eq!(format!("{}", kp), expected); + } +} diff --git a/parity-crypto/src/publickey/keypair_generator.rs b/parity-crypto/src/publickey/keypair_generator.rs new file mode 100644 index 000000000..d8d6bc77e --- /dev/null +++ b/parity-crypto/src/publickey/keypair_generator.rs @@ -0,0 +1,47 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Random key pair generator. Relies on the secp256k1 C-library to generate random data. + +use rand::rngs::OsRng; +use std::convert::Infallible; +use super::{Generator, KeyPair, SECP256K1}; + +/// Randomly generates new keypair, instantiating the RNG each time. +pub struct Random; + +impl Generator for Random { + type Error = std::io::Error; + + fn generate(&mut self) -> Result { + let mut rng = OsRng::new()?; + match rng.generate() { + Ok(pair) => Ok(pair), + Err(void) => match void {}, // LLVM unreachable + } + } +} + +impl Generator for OsRng { + type Error = Infallible; + + fn generate(&mut self) -> Result { + let (sec, publ) = SECP256K1.generate_keypair(self) + .expect("context always created with full capabilities; qed"); + + Ok(KeyPair::from_keypair(sec, publ)) + } +} diff --git a/parity-crypto/src/publickey/mod.rs b/parity-crypto/src/publickey/mod.rs new file mode 100644 index 000000000..c983f0112 --- /dev/null +++ b/parity-crypto/src/publickey/mod.rs @@ -0,0 +1,55 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Submodule of crypto utils for working with public key crypto primitives +//! If you are looking for git history please refer to the `ethkey` crate in the `parity-ethereum` repository. + +mod keypair; +mod keypair_generator; +mod ecdsa_signature; +mod secret_key; +mod extended_keys; + +pub mod ecdh; +pub mod ecies; +pub mod ec_math_utils; +pub mod error; + +pub use self::keypair::{KeyPair, public_to_address}; +pub use self::ec_math_utils::public_is_valid; +pub use self::keypair_generator::Random; +pub use self::error::Error; +pub use self::ecdsa_signature::{sign, verify_public, verify_address, recover, Signature}; +pub use self::secret_key::Secret; +pub use self::extended_keys::{ExtendedPublic, ExtendedSecret, ExtendedKeyPair, DerivationError, Derivation}; + +use ethereum_types::H256; +use lazy_static::lazy_static; + +pub use ethereum_types::{Address, Public}; +pub type Message = H256; + +lazy_static! { + pub static ref SECP256K1: secp256k1::Secp256k1 = secp256k1::Secp256k1::new(); +} + +/// Generates new keypair. +pub trait Generator { + type Error; + + /// Should be called to generate new keypair. + fn generate(&mut self) -> Result; +} diff --git a/parity-crypto/src/publickey/secret_key.rs b/parity-crypto/src/publickey/secret_key.rs new file mode 100644 index 000000000..ef8590c1d --- /dev/null +++ b/parity-crypto/src/publickey/secret_key.rs @@ -0,0 +1,307 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Secret key implementation + +use std::fmt; +use std::ops::Deref; +use std::str::FromStr; +use std::convert::TryFrom; +use secp256k1::constants::{SECRET_KEY_SIZE as SECP256K1_SECRET_KEY_SIZE}; +use secp256k1::key; +use ethereum_types::H256; +use zeroize::Zeroize; +use super::{SECP256K1, Error}; + +/// Represents secret key +#[derive(Clone, PartialEq, Eq)] +pub struct Secret { + inner: H256, +} + +impl Drop for Secret { + fn drop(&mut self) { + self.inner.0.zeroize() + } +} + +impl fmt::LowerHex for Secret { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + self.inner.fmt(fmt) + } +} + +impl fmt::Debug for Secret { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + self.inner.fmt(fmt) + } +} + +impl fmt::Display for Secret { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "Secret: 0x{:x}{:x}..{:x}{:x}", self.inner[0], self.inner[1], self.inner[30], self.inner[31]) + } +} + +impl Secret { + /// Creates a `Secret` from the given slice, returning `None` if the slice length != 32. + pub fn copy_from_slice(key: &[u8]) -> Option { + if key.len() != 32 { + return None + } + let mut h = H256::zero(); + h.as_bytes_mut().copy_from_slice(&key[0..32]); + Some(Secret { inner: h }) + } + + /// Creates zero key, which is invalid for crypto operations, but valid for math operation. + pub fn zero() -> Self { + Secret { inner: H256::zero() } + } + + /// Imports and validates the key. + pub fn import_key(key: &[u8]) -> Result { + let secret = key::SecretKey::from_slice(&super::SECP256K1, key)?; + Ok(secret.into()) + } + + /// Checks validity of this key. + pub fn check_validity(&self) -> Result<(), Error> { + self.to_secp256k1_secret().map(|_| ()) + } + + /// Wrapper over hex conversion + pub fn to_hex(&self) -> String { + format!("{:x}", self.inner) + } + + /// Inplace add one secret key to another (scalar + scalar) + pub fn add(&mut self, other: &Secret) -> Result<(), Error> { + match (self.is_zero(), other.is_zero()) { + (true, true) | (false, true) => Ok(()), + (true, false) => { + *self = other.clone(); + Ok(()) + }, + (false, false) => { + let mut key_secret = self.to_secp256k1_secret()?; + let other_secret = other.to_secp256k1_secret()?; + key_secret.add_assign(&SECP256K1, &other_secret)?; + + *self = key_secret.into(); + Ok(()) + }, + } + } + + /// Inplace subtract one secret key from another (scalar - scalar) + pub fn sub(&mut self, other: &Secret) -> Result<(), Error> { + match (self.is_zero(), other.is_zero()) { + (true, true) | (false, true) => Ok(()), + (true, false) => { + *self = other.clone(); + self.neg() + }, + (false, false) => { + let mut key_secret = self.to_secp256k1_secret()?; + let mut other_secret = other.to_secp256k1_secret()?; + other_secret.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; + key_secret.add_assign(&SECP256K1, &other_secret)?; + + *self = key_secret.into(); + Ok(()) + }, + } + } + + /// Inplace decrease secret key (scalar - 1) + pub fn dec(&mut self) -> Result<(), Error> { + match self.is_zero() { + true => { + *self = key::MINUS_ONE_KEY.into(); + Ok(()) + }, + false => { + let mut key_secret = self.to_secp256k1_secret()?; + key_secret.add_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; + + *self = key_secret.into(); + Ok(()) + }, + } + } + + /// Inplace multiply one secret key to another (scalar * scalar) + pub fn mul(&mut self, other: &Secret) -> Result<(), Error> { + match (self.is_zero(), other.is_zero()) { + (true, true) | (true, false) => Ok(()), + (false, true) => { + *self = Self::zero(); + Ok(()) + }, + (false, false) => { + let mut key_secret = self.to_secp256k1_secret()?; + let other_secret = other.to_secp256k1_secret()?; + key_secret.mul_assign(&SECP256K1, &other_secret)?; + + *self = key_secret.into(); + Ok(()) + }, + } + } + + /// Inplace negate secret key (-scalar) + pub fn neg(&mut self) -> Result<(), Error> { + match self.is_zero() { + true => Ok(()), + false => { + let mut key_secret = self.to_secp256k1_secret()?; + key_secret.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; + + *self = key_secret.into(); + Ok(()) + }, + } + } + + /// Inplace inverse secret key (1 / scalar) + pub fn inv(&mut self) -> Result<(), Error> { + let mut key_secret = self.to_secp256k1_secret()?; + key_secret.inv_assign(&SECP256K1)?; + + *self = key_secret.into(); + Ok(()) + } + + /// Compute power of secret key inplace (secret ^ pow). + pub fn pow(&mut self, pow: usize) -> Result<(), Error> { + if self.is_zero() { + return Ok(()); + } + + match pow { + 0 => *self = key::ONE_KEY.into(), + 1 => (), + _ => { + let c = self.clone(); + for _ in 1..pow { + self.mul(&c)?; + } + }, + } + + Ok(()) + } + + /// Create `secp256k1::key::SecretKey` based on this secret + pub fn to_secp256k1_secret(&self) -> Result { + Ok(key::SecretKey::from_slice(&SECP256K1, &self[..])?) + } +} + +impl FromStr for Secret { + type Err = Error; + fn from_str(s: &str) -> Result { + Ok(H256::from_str(s).map_err(|e| Error::Custom(format!("{:?}", e)))?.into()) + } +} + +impl From<[u8; 32]> for Secret { + fn from(k: [u8; 32]) -> Self { + Secret { inner: H256(k) } + } +} + +impl From for Secret { + fn from(s: H256) -> Self { + s.0.into() + } +} + +impl TryFrom<&str> for Secret { + type Error = Error; + + fn try_from(s: &str) -> Result { + s.parse().map_err(|e| Error::Custom(format!("{:?}", e))) + } +} + +impl From for Secret { + fn from(key: key::SecretKey) -> Self { + let mut a = [0; SECP256K1_SECRET_KEY_SIZE]; + a.copy_from_slice(&key[0 .. SECP256K1_SECRET_KEY_SIZE]); + a.into() + } +} + +impl Deref for Secret { + type Target = H256; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + use super::super::{Random, Generator}; + use super::Secret; + + #[test] + fn multiplicating_secret_inversion_with_secret_gives_one() { + let secret = Random.generate().unwrap().secret().clone(); + let mut inversion = secret.clone(); + inversion.inv().unwrap(); + inversion.mul(&secret).unwrap(); + assert_eq!(inversion, Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap()); + } + + #[test] + fn secret_inversion_is_reversible_with_inversion() { + let secret = Random.generate().unwrap().secret().clone(); + let mut inversion = secret.clone(); + inversion.inv().unwrap(); + inversion.inv().unwrap(); + assert_eq!(inversion, secret); + } + + #[test] + fn secret_pow() { + let secret = Random.generate().unwrap().secret().clone(); + + let mut pow0 = secret.clone(); + pow0.pow(0).unwrap(); + assert_eq!(pow0, Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap()); + + let mut pow1 = secret.clone(); + pow1.pow(1).unwrap(); + assert_eq!(pow1, secret); + + let mut pow2 = secret.clone(); + pow2.pow(2).unwrap(); + let mut pow2_expected = secret.clone(); + pow2_expected.mul(&secret).unwrap(); + assert_eq!(pow2, pow2_expected); + + let mut pow3 = secret.clone(); + pow3.pow(3).unwrap(); + let mut pow3_expected = secret.clone(); + pow3_expected.mul(&secret).unwrap(); + pow3_expected.mul(&secret).unwrap(); + assert_eq!(pow3, pow3_expected); + } +}