From b0d462c6c95f4ad0afe73115837a8d37f08162d5 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 24 Aug 2016 18:35:21 +0200 Subject: [PATCH] Signature cleanup (#1921) * Address renamed to H160 at bigint library level * moved uint specific test from util to bigint library * naming * unifing hashes in progress * unifing hashes * cleanup redundant unwraps in tests * Removing util/crypto in progress. * fixed compiling * signature cleanup in progress * new module - ethcrypto used by ethstore and ethcore-network * fixed compiling * fixed compiling * fixed merge --- Cargo.lock | 16 + ethcore/Cargo.toml | 1 + ethcore/src/builtin.rs | 13 +- ethcore/src/client/test_client.rs | 3 +- ethcore/src/engines/basic_authority.rs | 12 +- ethcore/src/engines/instant_seal.rs | 2 +- ethcore/src/error.rs | 16 +- ethcore/src/executive.rs | 9 +- ethcore/src/lib.rs | 1 + ethcore/src/miner/miner.rs | 5 +- ethcore/src/miner/transaction_queue.rs | 26 +- ethcore/src/tests/helpers.rs | 3 +- ethcore/src/types/transaction.rs | 42 +- ethcore/src/verification/verification.rs | 3 +- ethcrypto/Cargo.toml | 12 + ethcrypto/src/lib.rs | 246 ++++++++++ ethkey/src/lib.rs | 2 +- ethkey/src/primitive.rs | 17 - ethkey/src/signature.rs | 25 +- ethstore/Cargo.toml | 1 + ethstore/src/crypto.rs | 95 ---- ethstore/src/lib.rs | 2 +- rpc/Cargo.toml | 1 + rpc/src/lib.rs | 1 + rpc/src/v1/impls/ethcore.rs | 5 +- rpc/src/v1/impls/personal.rs | 5 +- rpc/src/v1/tests/eth.rs | 5 +- sync/src/api.rs | 4 +- util/network/Cargo.toml | 2 + util/network/src/connection.rs | 2 +- util/network/src/discovery.rs | 18 +- util/network/src/error.rs | 11 +- util/network/src/handshake.rs | 60 ++- util/network/src/host.rs | 5 +- util/network/src/lib.rs | 2 + util/network/src/tests.rs | 6 +- util/src/crypto.rs | 560 ----------------------- util/src/error.rs | 9 - util/src/lib.rs | 4 +- 39 files changed, 444 insertions(+), 808 deletions(-) create mode 100644 ethcrypto/Cargo.toml create mode 100644 ethcrypto/src/lib.rs delete mode 100644 ethkey/src/primitive.rs delete mode 100644 ethstore/src/crypto.rs delete mode 100644 util/src/crypto.rs diff --git a/Cargo.lock b/Cargo.lock index d659c4bd7..5bd5e8f32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -269,6 +269,7 @@ dependencies = [ "ethcore-ipc-nano 1.4.0", "ethcore-util 1.4.0", "ethjson 0.1.0", + "ethkey 0.2.0", "ethstore 0.1.0", "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.9.4 (git+https://github.com/ethcore/hyper)", @@ -406,6 +407,8 @@ dependencies = [ "ethcore-devtools 1.4.0", "ethcore-io 1.4.0", "ethcore-util 1.4.0", + "ethcrypto 0.1.0", + "ethkey 0.2.0", "igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -431,6 +434,7 @@ dependencies = [ "ethcore-ipc 1.4.0", "ethcore-util 1.4.0", "ethjson 0.1.0", + "ethkey 0.2.0", "ethstore 0.1.0", "ethsync 1.4.0", "json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)", @@ -508,6 +512,17 @@ dependencies = [ "vergen 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ethcrypto" +version = "0.1.0" +dependencies = [ + "bigint 0.1.0", + "eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)", + "ethkey 0.2.0", + "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ethjson" version = "0.1.0" @@ -535,6 +550,7 @@ dependencies = [ name = "ethstore" version = "0.1.0" dependencies = [ + "ethcrypto 0.1.0", "ethkey 0.2.0", "itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 729f9c268..a34116df5 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -33,6 +33,7 @@ ethcore-devtools = { path = "../devtools" } ethjson = { path = "../json" } ethcore-ipc = { path = "../ipc/rpc" } ethstore = { path = "../ethstore" } +ethkey = { path = "../ethkey" } ethcore-ipc-nano = { path = "../ipc/nano" } rand = "0.3" diff --git a/ethcore/src/builtin.rs b/ethcore/src/builtin.rs index 891f321a1..d4ea5e30e 100644 --- a/ethcore/src/builtin.rs +++ b/ethcore/src/builtin.rs @@ -14,10 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use util::*; use crypto::sha2::Sha256; use crypto::ripemd160::Ripemd160; use crypto::digest::Digest; +use util::*; +use ethkey::{Signature, recover}; use ethjson; /// Definition of a contract whose implementation is built-in. @@ -92,19 +93,19 @@ pub fn new_builtin_exec(name: &str) -> Box { }), "ecrecover" => Box::new(move|input: &[u8], output: &mut[u8]| { #[repr(packed)] - #[derive(Debug)] + #[derive(Debug, Default)] struct InType { hash: H256, v: H256, r: H256, s: H256, } - let mut it: InType = InType { hash: H256::new(), v: H256::new(), r: H256::new(), s: H256::new() }; + let mut it = InType::default(); it.copy_raw(input); if it.v == H256::from(&U256::from(27)) || it.v == H256::from(&U256::from(28)) { - let s = signature_from_rsv(&it.r, &it.s, it.v[31] - 27); - if ec::is_valid(&s) { - if let Ok(p) = ec::recover(&s, &it.hash) { + let s = Signature::from_rsv(&it.r, &it.s, it.v[31] - 27); + if s.is_valid() { + if let Ok(p) = recover(&s, &it.hash) { let r = p.as_slice().sha3(); // NICE: optimise and separate out into populate-like function for i in 0..min(32, output.len()) { diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 410a90347..8e26a6b0c 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -18,6 +18,7 @@ use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder}; use util::*; +use ethkey::{Generator, Random}; use devtools::*; use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action}; use blockchain::TreeRoute; @@ -188,7 +189,7 @@ impl TestBlockChainClient { let txs = match with { EachBlockWith::Transaction | EachBlockWith::UncleAndTransaction => { let mut txs = RlpStream::new_list(1); - let keypair = KeyPair::create().unwrap(); + let keypair = Random.generate().unwrap(); // Update nonces value self.nonces.write().insert(keypair.address(), U256::one()); let tx = Transaction { diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 7ec5a66a4..926399d7b 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -17,6 +17,7 @@ //! A blockchain engine that supports a basic, non-BFT proof-of-authority. use common::*; +use ethkey::{recover, public_to_address}; use account_provider::AccountProvider; use block::*; use spec::CommonParams; @@ -133,7 +134,7 @@ impl Engine for BasicAuthority { fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { // check the signature is legit. let sig = try!(UntrustedRlp::new(&header.seal[0]).as_val::()); - let signer = Address::from(try!(ec::recover(&sig, &header.bare_hash())).sha3()); + let signer = public_to_address(&try!(recover(&sig.into(), &header.bare_hash()))); if !self.our_params.authorities.contains(&signer) { return try!(Err(BlockError::InvalidSeal)); } @@ -228,15 +229,10 @@ mod tests { fn can_do_signature_verification_fail() { let engine = new_test_authority().engine; let mut header: Header = Header::default(); - header.set_seal(vec![rlp::encode(&Signature::zero()).to_vec()]); + header.set_seal(vec![rlp::encode(&H520::default()).to_vec()]); let verify_result = engine.verify_block_unordered(&header, None); - - match verify_result { - Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature))) => {}, - Err(_) => { panic!("should be block difficulty error (got {:?})", verify_result); }, - _ => { panic!("Should be error, got Ok"); }, - } + assert!(verify_result.is_err()); } #[test] diff --git a/ethcore/src/engines/instant_seal.rs b/ethcore/src/engines/instant_seal.rs index 6a3d3c700..e98e87bf5 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/src/engines/instant_seal.rs @@ -100,7 +100,7 @@ mod tests { assert!(engine.verify_block_basic(&header, None).is_ok()); - header.set_seal(vec![rlp::encode(&Signature::zero()).to_vec()]); + header.set_seal(vec![rlp::encode(&H520::default()).to_vec()]); assert!(engine.verify_block_unordered(&header, None).is_ok()); } diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index 449303732..5c26e2f78 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -24,6 +24,7 @@ use client::Error as ClientError; use ipc::binary::{BinaryConvertError, BinaryConvertable}; use types::block_import_error::BlockImportError; use snapshot::Error as SnapshotError; +use ethkey::Error as EthkeyError; pub use types::executed::{ExecutionError, CallError}; @@ -238,6 +239,8 @@ pub enum Error { Snappy(::util::snappy::InvalidInput), /// Snapshot error. Snapshot(SnapshotError), + /// Ethkey error. + Ethkey(EthkeyError), } impl fmt::Display for Error { @@ -258,6 +261,7 @@ impl fmt::Display for Error { Error::StdIo(ref err) => err.fmt(f), Error::Snappy(ref err) => err.fmt(f), Error::Snapshot(ref err) => err.fmt(f), + Error::Ethkey(ref err) => err.fmt(f), } } } @@ -298,12 +302,6 @@ impl From for Error { } } -impl From for Error { - fn from(err: CryptoError) -> Error { - Error::Util(UtilError::Crypto(err)) - } -} - impl From for Error { fn from(err: DecoderError) -> Error { Error::Util(UtilError::Decoder(err)) @@ -361,6 +359,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: EthkeyError) -> Error { + Error::Ethkey(err) + } +} + impl From> for Error where Error: From { fn from(err: Box) -> Error { Error::from(*err) diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 4cceb137b..53d5460ad 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -483,6 +483,7 @@ impl<'a> Executive<'a> { #[cfg(test)] #[allow(dead_code)] mod tests { + use ethkey::{Generator, Random}; use super::*; use common::*; use evm::{Factory, VMType}; @@ -1002,7 +1003,7 @@ mod tests { // TODO: fix (preferred) or remove evm_test_ignore!{test_transact_simple: test_transact_simple_jit, test_transact_simple_int} fn test_transact_simple(factory: Factory) { - let keypair = KeyPair::create().unwrap(); + let keypair = Random.generate().unwrap(); let t = Transaction { action: Action::Create, value: U256::from(17), @@ -1069,7 +1070,7 @@ mod tests { evm_test!{test_transact_invalid_nonce: test_transact_invalid_nonce_jit, test_transact_invalid_nonce_int} fn test_transact_invalid_nonce(factory: Factory) { - let keypair = KeyPair::create().unwrap(); + let keypair = Random.generate().unwrap(); let t = Transaction { action: Action::Create, value: U256::from(17), @@ -1102,7 +1103,7 @@ mod tests { evm_test!{test_transact_gas_limit_reached: test_transact_gas_limit_reached_jit, test_transact_gas_limit_reached_int} fn test_transact_gas_limit_reached(factory: Factory) { - let keypair = KeyPair::create().unwrap(); + let keypair = Random.generate().unwrap(); let t = Transaction { action: Action::Create, value: U256::from(17), @@ -1137,7 +1138,7 @@ mod tests { evm_test!{test_not_enough_cash: test_not_enough_cash_jit, test_not_enough_cash_int} fn test_not_enough_cash(factory: Factory) { - let keypair = KeyPair::create().unwrap(); + let keypair = Random.generate().unwrap(); let t = Transaction { action: Action::Create, value: U256::from(18), diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 2da6abe3d..b8233ea26 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -96,6 +96,7 @@ extern crate bloomchain; extern crate rayon; extern crate hyper; extern crate ethash; +extern crate ethkey; pub extern crate ethstore; extern crate semver; extern crate ethcore_ipc_nano as nanoipc; diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 3ca3f0d74..a2533ecde 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -911,6 +911,7 @@ mod tests { use super::super::MinerService; use super::*; use util::*; + use ethkey::{Generator, Random}; use client::{TestBlockChainClient, EachBlockWith}; use client::{TransactionImportResult}; use types::transaction::{Transaction, Action}; @@ -975,7 +976,7 @@ mod tests { let client = TestBlockChainClient::default(); let miner = miner(); let transaction = { - let keypair = KeyPair::create().unwrap(); + let keypair = Random.generate().unwrap(); Transaction { action: Action::Create, value: U256::zero(), @@ -1005,7 +1006,7 @@ mod tests { let client = TestBlockChainClient::default(); let miner = miner(); let transaction = { - let keypair = KeyPair::create().unwrap(); + let keypair = Random.generate().unwrap(); Transaction { action: Action::Create, value: U256::zero(), diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index 8a2a37145..5e610da24 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -26,16 +26,17 @@ //! ```rust //! extern crate ethcore_util as util; //! extern crate ethcore; +//! extern crate ethkey; //! extern crate rustc_serialize; //! -//! use util::crypto::KeyPair; //! use util::{Uint, U256, Address}; +//! use ethkey::{Random, Generator}; //! use ethcore::miner::{TransactionQueue, AccountDetails, TransactionOrigin}; //! use ethcore::transaction::*; //! use rustc_serialize::hex::FromHex; //! //! fn main() { -//! let key = KeyPair::create().unwrap(); +//! let key = Random.generate().unwrap(); //! let t1 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(), //! gas: U256::from(100_000), gas_price: U256::one(), nonce: U256::from(10) }; //! let t2 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(), @@ -233,7 +234,7 @@ struct TransactionSet { impl TransactionSet { /// Inserts `TransactionOrder` to this set. Transaction does not need to be unique - /// the same transaction may be validly inserted twice. Any previous transaction that - /// it replaces (i.e. with the same `sender` and `nonce`) should be returned. + /// it replaces (i.e. with the same `sender` and `nonce`) should be returned. fn insert(&mut self, sender: Address, nonce: U256, order: TransactionOrder) -> Option { if !self.by_priority.insert(order.clone()) { return Some(order.clone()); @@ -313,7 +314,7 @@ impl TransactionSet { } /// Get the minimum gas price that we can accept into this queue that wouldn't cause the transaction to - /// immediately be dropped. 0 if the queue isn't at capacity; 1 plus the lowest if it is. + /// immediately be dropped. 0 if the queue isn't at capacity; 1 plus the lowest if it is. fn gas_price_entry_limit(&self) -> U256 { match self.by_gas_price.keys().next() { Some(k) if self.by_priority.len() >= self.limit => *k + 1.into(), @@ -340,7 +341,7 @@ impl TransactionSet { return false; } } else { - // Operation failed: gas-price not found in Map. + // Operation failed: gas-price not found in Map. return false; } // Operation maybe ok: only if hash not found in gas-price Set. @@ -869,6 +870,7 @@ mod test { extern crate rustc_serialize; use util::table::*; use util::*; + use ethkey::{Random, Generator}; use transaction::*; use error::{Error, TransactionError}; use super::*; @@ -897,7 +899,7 @@ mod test { } fn new_tx(nonce: U256, gas_price: U256) -> SignedTransaction { - let keypair = KeyPair::create().unwrap(); + let keypair = Random.generate().unwrap(); new_unsigned_tx(nonce, gas_price).sign(keypair.secret()) } @@ -916,7 +918,7 @@ mod test { let tx1 = new_unsigned_tx(nonce, gas_price); let tx2 = new_unsigned_tx(nonce + nonce_increment, gas_price + gas_price_increment); - let keypair = KeyPair::create().unwrap(); + let keypair = Random.generate().unwrap(); let secret = &keypair.secret(); (tx1.sign(secret), tx2.sign(secret)) } @@ -1373,7 +1375,7 @@ mod test { fn should_move_transactions_if_gap_filled() { // given let mut txq = TransactionQueue::new(); - let kp = KeyPair::create().unwrap(); + let kp = Random.generate().unwrap(); let secret = kp.secret(); let tx = new_unsigned_tx(123.into(), 1.into()).sign(secret); let tx1 = new_unsigned_tx(124.into(), 1.into()).sign(secret); @@ -1397,7 +1399,7 @@ mod test { fn should_remove_transaction() { // given let mut txq2 = TransactionQueue::new(); - let (tx, tx2) = new_tx_pair_default(3.into(), 0.into()); + let (tx, tx2) = new_tx_pair_default(3.into(), 0.into()); txq2.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap(); txq2.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); assert_eq!(txq2.status().pending, 1); @@ -1582,7 +1584,7 @@ mod test { init_log(); // given let mut txq = TransactionQueue::new(); - let keypair = KeyPair::create().unwrap(); + let keypair = Random.generate().unwrap(); let tx = new_unsigned_tx(123.into(), 1.into()).sign(keypair.secret()); let tx2 = { let mut tx2 = (*tx).clone(); @@ -1605,7 +1607,7 @@ mod test { fn should_replace_same_transaction_when_importing_to_futures() { // given let mut txq = TransactionQueue::new(); - let keypair = KeyPair::create().unwrap(); + let keypair = Random.generate().unwrap(); let tx0 = new_unsigned_tx(123.into(), 1.into()).sign(keypair.secret()); let tx1 = { let mut tx1 = (*tx0).clone(); @@ -1758,7 +1760,7 @@ mod test { // given let mut txq = TransactionQueue::new(); let (tx1, tx2, tx2_2, tx3) = { - let keypair = KeyPair::create().unwrap(); + let keypair = Random.generate().unwrap(); let secret = &keypair.secret(); let nonce = 123.into(); let tx = new_unsigned_tx(nonce, 1.into()); diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index ff35e7023..03a1a2232 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use ethkey::KeyPair; use io::*; use client::{BlockChainClient, Client, ClientConfig}; use common::*; @@ -145,7 +146,7 @@ pub fn generate_dummy_client_with_spec_and_data(get_test_spec: F, block_numbe let mut last_hashes = vec![]; let mut last_header = genesis_header.clone(); - let kp = KeyPair::from_secret("".sha3()).unwrap() ; + let kp = KeyPair::from_secret("".sha3()).unwrap(); let author = kp.address(); let mut n = 0; diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index 4b08a5118..02d3da30a 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -16,18 +16,16 @@ //! Transaction data structure. -use util::{H256, Address, U256, H520}; use std::ops::Deref; -use util::rlp::*; -use util::sha3::*; -use util::{UtilError, CryptoError, Bytes, Signature, Secret, ec}; -use util::crypto::{signature_from_rsv, signature_to_rsv}; use std::cell::*; +use util::rlp::*; +use util::sha3::Hashable; +use util::{H256, Address, U256, Bytes}; +use ethkey::{Signature, sign, Secret, recover, public_to_address, Error as EthkeyError}; use error::*; use evm::Schedule; use header::BlockNumber; use ethjson; -use ethstore::ethkey::Signature as EthkeySignature; #[derive(Debug, Clone, PartialEq, Eq, Binary)] /// Transaction action type. @@ -139,19 +137,17 @@ impl Transaction { /// Signs the transaction as coming from `sender`. pub fn sign(self, secret: &Secret) -> SignedTransaction { - let sig = ec::sign(secret, &self.hash()).unwrap(); - self.with_signature(sig.into()) + let sig = sign(secret, &self.hash()).unwrap(); + self.with_signature(sig) } /// Signs the transaction with signature. - pub fn with_signature(self, sig: EthkeySignature) -> SignedTransaction { - let sig: H520 = sig.into(); - let (r, s, v) = signature_to_rsv(&sig); + pub fn with_signature(self, sig: Signature) -> SignedTransaction { SignedTransaction { unsigned: self, - r: r, - s: s, - v: v + 27, + r: sig.r().into(), + s: sig.s().into(), + v: sig.v() + 27, hash: Cell::new(None), sender: Cell::new(None), } @@ -290,12 +286,14 @@ impl SignedTransaction { pub fn standard_v(&self) -> u8 { match self.v { 27 => 0, 28 => 1, _ => 4 } } /// Construct a signature object from the sig. - pub fn signature(&self) -> Signature { signature_from_rsv(&From::from(&self.r), &From::from(&self.s), self.standard_v()) } + pub fn signature(&self) -> Signature { + Signature::from_rsv(&self.r.into(), &self.s.into(), self.standard_v()) + } /// Checks whether the signature has a low 's' value. pub fn check_low_s(&self) -> Result<(), Error> { - if !ec::is_low_s(&self.s) { - Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature))) + if !self.signature().is_low_s() { + Err(EthkeyError::InvalidSignature.into()) } else { Ok(()) } @@ -307,7 +305,7 @@ impl SignedTransaction { match sender { Some(s) => Ok(s), None => { - let s = Address::from(try!(ec::recover(&self.signature(), &self.unsigned.hash())).sha3()); + let s = public_to_address(&try!(recover(&self.signature(), &self.unsigned.hash()))); self.sender.set(Some(s)); Ok(s) } @@ -319,8 +317,8 @@ impl SignedTransaction { #[cfg(test)] #[cfg(feature = "json-tests")] pub fn validate(self, schedule: &Schedule, require_low: bool) -> Result { - if require_low && !ec::is_low_s(&self.s) { - return Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature))); + if require_low && !self.signature().is_low_s() { + return Err(EthkeyError::InvalidSignature.into()) } try!(self.sender()); if self.gas < U256::from(self.gas_required(&schedule)) { @@ -368,7 +366,9 @@ fn sender_test() { #[test] fn signing() { - let key = ::util::crypto::KeyPair::create().unwrap(); + use ethkey::{Random, Generator}; + + let key = Random.generate().unwrap(); let t = Transaction { action: Action::Create, nonce: U256::from(42), diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index ae7a141b4..9cea3bede 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -228,6 +228,7 @@ fn verify_block_integrity(block: &[u8], transactions_root: &H256, uncles_hash: & #[cfg(test)] mod tests { use util::*; + use ethkey::{Random, Generator}; use header::*; use verification::*; use blockchain::extras::*; @@ -355,7 +356,7 @@ mod tests { good.timestamp = 40; good.number = 10; - let keypair = KeyPair::create().unwrap(); + let keypair = Random.generate().unwrap(); let tr1 = Transaction { action: Action::Create, diff --git a/ethcrypto/Cargo.toml b/ethcrypto/Cargo.toml new file mode 100644 index 000000000..85298266d --- /dev/null +++ b/ethcrypto/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ethcrypto" +version = "0.1.0" +authors = ["debris "] + +[dependencies] +rust-crypto = "0.2.36" +tiny-keccak = "1.0" +eth-secp256k1 = { git = "https://github.com/ethcore/rust-secp256k1" } +ethkey = { path = "../ethkey" } +bigint = { path = "../util/bigint" } + diff --git a/ethcrypto/src/lib.rs b/ethcrypto/src/lib.rs new file mode 100644 index 000000000..4e3c3c1fc --- /dev/null +++ b/ethcrypto/src/lib.rs @@ -0,0 +1,246 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity 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 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. If not, see . + +//! Crypto utils used ethstore and network. + +extern crate bigint; +extern crate tiny_keccak; +extern crate crypto as rcrypto; +extern crate secp256k1; +extern crate ethkey; + +use tiny_keccak::Keccak; +use rcrypto::pbkdf2::pbkdf2; +use rcrypto::scrypt::{scrypt, ScryptParams}; +use rcrypto::sha2::Sha256; +use rcrypto::hmac::Hmac; +use secp256k1::Error as SecpError; + +pub const KEY_LENGTH: usize = 32; +pub const KEY_ITERATIONS: usize = 10240; +pub const KEY_LENGTH_AES: usize = KEY_LENGTH / 2; + +pub enum Error { + Secp(SecpError), + InvalidMessage, +} + +impl From for Error { + fn from(e: SecpError) -> Self { + Error::Secp(e) + } +} + +pub trait Keccak256 { + fn keccak256(&self) -> T where T: Sized; +} + +impl Keccak256<[u8; 32]> for [u8] { + fn keccak256(&self) -> [u8; 32] { + let mut keccak = Keccak::new_keccak256(); + let mut result = [0u8; 32]; + keccak.update(self); + keccak.finalize(&mut result); + result + } +} + +pub fn derive_key_iterations(password: &str, salt: &[u8; 32], c: u32) -> (Vec, Vec) { + let mut h_mac = Hmac::new(Sha256::new(), password.as_bytes()); + let mut derived_key = vec![0u8; KEY_LENGTH]; + pbkdf2(&mut h_mac, salt, c, &mut derived_key); + let derived_right_bits = &derived_key[0..KEY_LENGTH_AES]; + let derived_left_bits = &derived_key[KEY_LENGTH_AES..KEY_LENGTH]; + (derived_right_bits.to_vec(), derived_left_bits.to_vec()) +} + +pub fn derive_key_scrypt(password: &str, salt: &[u8; 32], n: u32, p: u32, r: u32) -> (Vec, Vec) { + let mut derived_key = vec![0u8; KEY_LENGTH]; + let scrypt_params = ScryptParams::new(n.trailing_zeros() as u8, r, p); + scrypt(password.as_bytes(), salt, &scrypt_params, &mut derived_key); + let derived_right_bits = &derived_key[0..KEY_LENGTH_AES]; + let derived_left_bits = &derived_key[KEY_LENGTH_AES..KEY_LENGTH]; + (derived_right_bits.to_vec(), derived_left_bits.to_vec()) +} + +pub fn derive_mac(derived_left_bits: &[u8], cipher_text: &[u8]) -> Vec { + let mut mac = vec![0u8; KEY_LENGTH_AES + cipher_text.len()]; + mac[0..KEY_LENGTH_AES].copy_from_slice(derived_left_bits); + mac[KEY_LENGTH_AES..cipher_text.len() + KEY_LENGTH_AES].copy_from_slice(cipher_text); + mac +} + +/// AES encryption +pub mod aes { + use rcrypto::blockmodes::{CtrMode, CbcDecryptor, PkcsPadding}; + use rcrypto::aessafe::{AesSafe128Encryptor, AesSafe128Decryptor}; + use rcrypto::symmetriccipher::{Encryptor, Decryptor, SymmetricCipherError}; + use rcrypto::buffer::{RefReadBuffer, RefWriteBuffer, WriteBuffer}; + + /// Encrypt a message + pub fn encrypt(k: &[u8], iv: &[u8], plain: &[u8], dest: &mut [u8]) { + let mut encryptor = CtrMode::new(AesSafe128Encryptor::new(k), iv.to_vec()); + encryptor.encrypt(&mut RefReadBuffer::new(plain), &mut RefWriteBuffer::new(dest), true).expect("Invalid length or padding"); + } + + /// Decrypt a message + pub fn decrypt(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) { + let mut encryptor = CtrMode::new(AesSafe128Encryptor::new(k), iv.to_vec()); + encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut RefWriteBuffer::new(dest), true).expect("Invalid length or padding"); + } + + + /// Decrypt a message using cbc mode + pub fn decrypt_cbc(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) -> Result { + let mut encryptor = CbcDecryptor::new(AesSafe128Decryptor::new(k), PkcsPadding, iv.to_vec()); + let len = dest.len(); + let mut buffer = RefWriteBuffer::new(dest); + try!(encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut buffer, true)); + Ok(len - buffer.remaining()) + } +} + +/// ECDH functions +#[cfg_attr(feature="dev", allow(similar_names))] +pub mod ecdh { + use secp256k1::{ecdh, key}; + use ethkey::{Secret, Public, SECP256K1}; + use Error; + + /// 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 = try!(key::PublicKey::from_slice(context, &pdata)); + // no way to create SecretKey from raw byte array. + let sec: &key::SecretKey = unsafe { ::std::mem::transmute(secret) }; + let shared = ecdh::SharedSecret::new_raw(context, &publ, sec); + + let mut s = Secret::default(); + s.copy_from_slice(&shared[0..32]); + Ok(s) + } +} + +/// ECIES function +#[cfg_attr(feature="dev", allow(similar_names))] +pub mod ecies { + use rcrypto::digest::Digest; + use rcrypto::sha2::Sha256; + use rcrypto::hmac::Hmac; + use rcrypto::mac::Mac; + use bigint::hash::FixedHash; + use ethkey::{Random, Generator, Public, Secret}; + use {Error, ecdh, aes}; + + /// Encrypt a message with a public key + pub fn encrypt(public: &Public, shared_mac: &[u8], plain: &[u8]) -> Result, Error> { + let r = Random.generate().unwrap(); + let z = try!(ecdh::agree(r.secret(), public)); + let mut key = [0u8; 32]; + let mut mkey = [0u8; 32]; + kdf(&z, &[0u8; 0], &mut key); + let mut hasher = Sha256::new(); + let mkey_material = &key[16..32]; + hasher.input(mkey_material); + hasher.result(&mut mkey); + let ekey = &key[0..16]; + + let mut msg = vec![0u8; (1 + 64 + 16 + plain.len() + 32)]; + msg[0] = 0x04u8; + { + let msgd = &mut msg[1..]; + msgd[0..64].copy_from_slice(r.public()); + { + let cipher = &mut msgd[(64 + 16)..(64 + 16 + plain.len())]; + aes::encrypt(ekey, &[0u8; 16], plain, cipher); + } + let mut hmac = Hmac::new(Sha256::new(), &mkey); + { + let cipher_iv = &msgd[64..(64 + 16 + plain.len())]; + hmac.input(cipher_iv); + } + hmac.input(shared_mac); + hmac.raw_result(&mut msgd[(64 + 16 + plain.len())..]); + } + Ok(msg) + } + + /// Decrypt a message with a secret key + pub fn decrypt(secret: &Secret, shared_mac: &[u8], encrypted: &[u8]) -> Result, Error> { + let meta_len = 1 + 64 + 16 + 32; + if encrypted.len() < meta_len || encrypted[0] < 2 || encrypted[0] > 4 { + return Err(Error::InvalidMessage); //invalid message: publickey + } + + let e = &encrypted[1..]; + let p = Public::from_slice(&e[0..64]); + let z = try!(ecdh::agree(secret, &p)); + let mut key = [0u8; 32]; + kdf(&z, &[0u8; 0], &mut key); + let ekey = &key[0..16]; + let mkey_material = &key[16..32]; + let mut hasher = Sha256::new(); + let mut mkey = [0u8; 32]; + hasher.input(mkey_material); + hasher.result(&mut mkey); + + let clen = encrypted.len() - meta_len; + let cipher_with_iv = &e[64..(64+16+clen)]; + let cipher_iv = &cipher_with_iv[0..16]; + let cipher_no_iv = &cipher_with_iv[16..]; + let msg_mac = &e[(64+16+clen)..]; + + // Verify tag + let mut hmac = Hmac::new(Sha256::new(), &mkey); + hmac.input(cipher_with_iv); + hmac.input(shared_mac); + let mut mac = [0u8; 32]; + hmac.raw_result(&mut mac); + if &mac[..] != msg_mac { + return Err(Error::InvalidMessage); + } + + let mut msg = vec![0u8; clen]; + aes::decrypt(ekey, cipher_iv, cipher_no_iv, &mut msg[..]); + Ok(msg) + } + + fn kdf(secret: &Secret, s1: &[u8], dest: &mut [u8]) { + let mut hasher = Sha256::new(); + // 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 ctrs = [(ctr >> 24) as u8, (ctr >> 16) as u8, (ctr >> 8) as u8, ctr as u8]; + hasher.input(&ctrs); + hasher.input(secret); + hasher.input(s1); + hasher.result(&mut dest[written..(written + 32)]); + hasher.reset(); + written += 32; + ctr += 1; + } + } +} + diff --git a/ethkey/src/lib.rs b/ethkey/src/lib.rs index 41f53de69..0bce090a5 100644 --- a/ethkey/src/lib.rs +++ b/ethkey/src/lib.rs @@ -31,7 +31,7 @@ mod random; mod signature; lazy_static! { - static ref SECP256K1: secp256k1::Secp256k1 = secp256k1::Secp256k1::new(); + pub static ref SECP256K1: secp256k1::Secp256k1 = secp256k1::Secp256k1::new(); } /// Generates new keypair. diff --git a/ethkey/src/primitive.rs b/ethkey/src/primitive.rs deleted file mode 100644 index 05ceaf3d3..000000000 --- a/ethkey/src/primitive.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2015, 2016 Ethcore (UK) Ltd. -// This file is part of Parity. - -// Parity 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 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. If not, see . - - diff --git a/ethkey/src/signature.rs b/ethkey/src/signature.rs index 7d652172c..eec0fbf47 100644 --- a/ethkey/src/signature.rs +++ b/ethkey/src/signature.rs @@ -21,7 +21,7 @@ use std::str::FromStr; use secp256k1::{Message as SecpMessage, RecoverableSignature, RecoveryId, Error as SecpError}; use secp256k1::key::{SecretKey, PublicKey}; use rustc_serialize::hex::{ToHex, FromHex}; -use bigint::hash::H520; +use bigint::hash::{H520, H256, FixedHash}; use {Secret, Public, SECP256K1, Error, Message, public_to_address, Address}; #[repr(C)] @@ -43,6 +43,29 @@ impl Signature { pub fn v(&self) -> u8 { self.0[64] } + + /// Create a signature object from the sig. + pub fn from_rsv(r: &H256, s: &H256, v: u8) -> Signature { + let mut sig = [0u8; 65]; + sig[0..32].copy_from_slice(&r); + sig[32..64].copy_from_slice(&s); + sig[64] = v; + Signature(sig) + } + + /// Check if this is a "low" signature. + pub fn is_low_s(&self) -> bool { + H256::from_slice(self.s()) <= "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0".into() + } + + /// Check if each component of the signature is in range. + pub fn is_valid(&self) -> bool { + self.v() <= 1 && + H256::from_slice(self.r()) < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into() && + H256::from_slice(self.r()) >= 1.into() && + H256::from_slice(self.s()) < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into() && + H256::from_slice(self.s()) >= 1.into() + } } // manual implementation large arrays don't have trait impls by default. diff --git a/ethstore/Cargo.toml b/ethstore/Cargo.toml index 691cfd969..1842456fd 100644 --- a/ethstore/Cargo.toml +++ b/ethstore/Cargo.toml @@ -18,6 +18,7 @@ docopt = { version = "0.6", optional = true } time = "0.1.34" lazy_static = "0.2" itertools = "0.4" +ethcrypto = { path = "../ethcrypto" } [build-dependencies] serde_codegen = { version = "0.7", optional = true } diff --git a/ethstore/src/crypto.rs b/ethstore/src/crypto.rs deleted file mode 100644 index a220a7ca1..000000000 --- a/ethstore/src/crypto.rs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2015, 2016 Ethcore (UK) Ltd. -// This file is part of Parity. - -// Parity 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 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. If not, see . - -use tiny_keccak::Keccak; -use rcrypto::pbkdf2::pbkdf2; -use rcrypto::scrypt::{scrypt, ScryptParams}; -use rcrypto::sha2::Sha256; -use rcrypto::hmac::Hmac; - -pub const KEY_LENGTH: usize = 32; -pub const KEY_ITERATIONS: usize = 10240; -pub const KEY_LENGTH_AES: usize = KEY_LENGTH / 2; - -pub fn derive_key_iterations(password: &str, salt: &[u8; 32], c: u32) -> (Vec, Vec) { - let mut h_mac = Hmac::new(Sha256::new(), password.as_bytes()); - let mut derived_key = vec![0u8; KEY_LENGTH]; - pbkdf2(&mut h_mac, salt, c, &mut derived_key); - let derived_right_bits = &derived_key[0..KEY_LENGTH_AES]; - let derived_left_bits = &derived_key[KEY_LENGTH_AES..KEY_LENGTH]; - (derived_right_bits.to_vec(), derived_left_bits.to_vec()) -} - -pub fn derive_key_scrypt(password: &str, salt: &[u8; 32], n: u32, p: u32, r: u32) -> (Vec, Vec) { - let mut derived_key = vec![0u8; KEY_LENGTH]; - let scrypt_params = ScryptParams::new(n.trailing_zeros() as u8, r, p); - scrypt(password.as_bytes(), salt, &scrypt_params, &mut derived_key); - let derived_right_bits = &derived_key[0..KEY_LENGTH_AES]; - let derived_left_bits = &derived_key[KEY_LENGTH_AES..KEY_LENGTH]; - (derived_right_bits.to_vec(), derived_left_bits.to_vec()) -} - -pub fn derive_mac(derived_left_bits: &[u8], cipher_text: &[u8]) -> Vec { - let mut mac = vec![0u8; KEY_LENGTH_AES + cipher_text.len()]; - mac[0..KEY_LENGTH_AES].copy_from_slice(derived_left_bits); - mac[KEY_LENGTH_AES..cipher_text.len() + KEY_LENGTH_AES].copy_from_slice(cipher_text); - mac -} - -pub trait Keccak256 { - fn keccak256(&self) -> T where T: Sized; -} - -impl Keccak256<[u8; 32]> for [u8] { - fn keccak256(&self) -> [u8; 32] { - let mut keccak = Keccak::new_keccak256(); - let mut result = [0u8; 32]; - keccak.update(self); - keccak.finalize(&mut result); - result - } -} - -/// AES encryption -pub mod aes { - use rcrypto::blockmodes::{CtrMode, CbcDecryptor, PkcsPadding}; - use rcrypto::aessafe::{AesSafe128Encryptor, AesSafe128Decryptor}; - use rcrypto::symmetriccipher::{Encryptor, Decryptor, SymmetricCipherError}; - use rcrypto::buffer::{RefReadBuffer, RefWriteBuffer, WriteBuffer}; - - /// Encrypt a message - pub fn encrypt(k: &[u8], iv: &[u8], plain: &[u8], dest: &mut [u8]) { - let mut encryptor = CtrMode::new(AesSafe128Encryptor::new(k), iv.to_vec()); - encryptor.encrypt(&mut RefReadBuffer::new(plain), &mut RefWriteBuffer::new(dest), true).expect("Invalid length or padding"); - } - - /// Decrypt a message - pub fn decrypt(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) { - let mut encryptor = CtrMode::new(AesSafe128Encryptor::new(k), iv.to_vec()); - encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut RefWriteBuffer::new(dest), true).expect("Invalid length or padding"); - } - - /// Decrypt a message using cbc mode - pub fn decrypt_cbc(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) -> Result { - let mut encryptor = CbcDecryptor::new(AesSafe128Decryptor::new(k), PkcsPadding, iv.to_vec()); - let len = dest.len(); - let mut buffer = RefWriteBuffer::new(dest); - try!(encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut buffer, true)); - Ok(len - buffer.remaining()) - } - -} - diff --git a/ethstore/src/lib.rs b/ethstore/src/lib.rs index 982a47c5a..302e165cf 100644 --- a/ethstore/src/lib.rs +++ b/ethstore/src/lib.rs @@ -30,13 +30,13 @@ extern crate tiny_keccak; extern crate lazy_static; // reexport it nicely extern crate ethkey as _ethkey; +extern crate ethcrypto as crypto; pub mod dir; pub mod ethkey; mod account; mod json; -mod crypto; mod error; mod ethstore; diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 7a70f52c7..ac1fd96c5 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -17,6 +17,7 @@ jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.gi ethcore-io = { path = "../util/io" } ethcore-util = { path = "../util" } ethcore = { path = "../ethcore" } +ethkey = { path = "../ethkey" } ethstore = { path = "../ethstore" } ethash = { path = "../ethash" } ethsync = { path = "../sync" } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 01a901732..6edc72d41 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -30,6 +30,7 @@ extern crate jsonrpc_http_server; extern crate ethcore_util as util; extern crate ethcore_io as io; extern crate ethcore; +extern crate ethkey; extern crate ethstore; extern crate ethsync; extern crate transient_hashmap; diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index 6a5b2e629..16b0f1931 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -18,9 +18,10 @@ use std::sync::{Arc, Weak}; use std::str::FromStr; use std::collections::{BTreeMap}; -use util::{RotatingLogger, KeyPair, Address}; +use util::{RotatingLogger, Address}; use util::misc::version_data; +use ethkey::{Brain, Generator}; use ethstore::random_phrase; use ethsync::{SyncProvider, ManageNetwork}; use ethcore::miner::MinerService; @@ -213,7 +214,7 @@ impl Ethcore for EthcoreClient where M: MinerService + fn phrase_to_address(&self, params: Params) -> Result { try!(self.active()); from_params::<(String,)>(params).and_then(|(phrase,)| - to_value(&H160::from(KeyPair::from_phrase(&phrase).address())) + to_value(&H160::from(Brain::new(phrase).generate().unwrap().address())) ) } } diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index c0a44e437..4d7008e09 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -17,14 +17,15 @@ //! Account management (personal) rpc implementation use std::sync::{Arc, Weak}; use std::collections::{BTreeMap}; +use util::{Address}; use jsonrpc_core::*; +use ethkey::{Brain, Generator}; use v1::traits::Personal; use v1::types::{H160 as RpcH160, TransactionRequest}; use v1::helpers::{errors, TransactionRequest as TRequest}; use v1::helpers::params::expect_no_params; use v1::helpers::dispatch::unlock_sign_and_dispatch; use ethcore::account_provider::AccountProvider; -use util::{Address, KeyPair}; use ethcore::client::MiningBlockChainClient; use ethcore::miner::MinerService; @@ -94,7 +95,7 @@ impl Personal for PersonalClient where C: MiningBl from_params::<(String, String, )>(params).and_then( |(phrase, pass, )| { let store = take_weak!(self.accounts); - match store.insert_account(*KeyPair::from_phrase(&phrase).secret(), &pass) { + match store.insert_account(*Brain::new(phrase).generate().unwrap().secret(), &pass) { Ok(address) => to_value(&RpcH160::from(address)), Err(e) => Err(errors::account("Could not create account.", e)), } diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 6ae923829..301492540 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -16,7 +16,6 @@ //! rpc integration tests. use std::sync::Arc; -use std::str::FromStr; use std::time::Duration; use ethcore::client::{BlockChainClient, Client, ClientConfig}; @@ -286,9 +285,7 @@ const POSITIVE_NONCE_SPEC: &'static [u8] = br#"{ #[test] fn eth_transaction_count() { - use util::crypto::Secret; - - let secret = Secret::from_str("8a283037bb19c4fed7b1c569e40c7dcff366165eb869110a1b11532963eb9cb2").unwrap(); + let secret = "8a283037bb19c4fed7b1c569e40c7dcff366165eb869110a1b11532963eb9cb2".into(); let tester = EthTester::from_spec(Spec::load(TRANSACTION_COUNT_SPEC)); let address = tester.accounts.insert_account(secret, "").unwrap(); tester.accounts.unlock_account_permanently(address, "".into()).unwrap(); diff --git a/sync/src/api.rs b/sync/src/api.rs index 1ded32367..921eb1007 100644 --- a/sync/src/api.rs +++ b/sync/src/api.rs @@ -17,7 +17,7 @@ use std::sync::Arc; use network::{NetworkProtocolHandler, NetworkService, NetworkContext, PeerId, NetworkConfiguration as BasicNetworkConfiguration, NonReservedPeerMode, NetworkError}; -use util::{U256, H256, Secret, Populatable}; +use util::{U256, H256, Populatable}; use io::{TimerToken}; use ethcore::client::{BlockChainClient, ChainNotify}; use ethcore::header::BlockNumber; @@ -232,7 +232,7 @@ pub struct NetworkConfiguration { /// List of initial node addresses pub boot_nodes: Vec, /// Use provided node key instead of default - pub use_secret: Option, + pub use_secret: Option, /// Max number of connected peers to maintain pub max_peers: u32, /// Min number of connected peers to maintain diff --git a/util/network/Cargo.toml b/util/network/Cargo.toml index 661d25c51..caad5c0d5 100644 --- a/util/network/Cargo.toml +++ b/util/network/Cargo.toml @@ -23,6 +23,8 @@ rustc-serialize = "0.3" ethcore-io = { path = "../io" } ethcore-util = { path = ".." } ethcore-devtools = { path = "../../devtools" } +ethkey = { path = "../../ethkey" } +ethcrypto = { path = "../../ethcrypto" } [features] default = [] diff --git a/util/network/src/connection.rs b/util/network/src/connection.rs index 8fd0d0948..d3e54393b 100644 --- a/util/network/src/connection.rs +++ b/util/network/src/connection.rs @@ -29,12 +29,12 @@ use error::*; use io::{IoContext, StreamToken}; use handshake::Handshake; use stats::NetworkStats; -use util::crypto; use rcrypto::blockmodes::*; use rcrypto::aessafe::*; use rcrypto::symmetriccipher::*; use rcrypto::buffer::*; use tiny_keccak::Keccak; +use crypto; const ENCRYPTED_HEADER_LEN: usize = 32; const RECIEVE_PAYLOAD_TIMEOUT: u64 = 30000; diff --git a/util/network/src/discovery.rs b/util/network/src/discovery.rs index a78f05804..48d16be48 100644 --- a/util/network/src/discovery.rs +++ b/util/network/src/discovery.rs @@ -24,11 +24,11 @@ use mio::udp::*; use util::sha3::*; use time; use util::hash::*; -use util::crypto::*; use util::rlp::*; use node_table::*; use error::NetworkError; use io::{StreamToken, IoContext}; +use ethkey::{Secret, KeyPair, sign, recover}; use PROTOCOL_VERSION; @@ -252,7 +252,7 @@ impl Discovery { let bytes = rlp.drain(); let hash = bytes.as_ref().sha3(); - let signature = match ec::sign(&self.secret, &hash) { + let signature = match sign(&self.secret, &hash) { Ok(s) => s, Err(_) => { warn!("Error signing UDP packet"); @@ -361,8 +361,8 @@ impl Discovery { } let signed = &packet[(32 + 65)..]; - let signature = Signature::from_slice(&packet[32..(32 + 65)]); - let node_id = try!(ec::recover(&signature, &signed.sha3())); + let signature = H520::from_slice(&packet[32..(32 + 65)]); + let node_id = try!(recover(&signature.into(), &signed.sha3())); let packet_id = signed[0]; let rlp = UntrustedRlp::new(&signed[1..]); @@ -536,9 +536,9 @@ mod tests { use util::hash::*; use std::net::*; use node_table::*; - use util::crypto::KeyPair; use std::str::FromStr; use rustc_serialize::hex::FromHex; + use ethkey::{Random, Generator}; #[test] fn find_node() { @@ -559,8 +559,8 @@ mod tests { #[test] fn discovery() { - let key1 = KeyPair::create().unwrap(); - let key2 = KeyPair::create().unwrap(); + let key1 = Random.generate().unwrap(); + let key2 = Random.generate().unwrap(); let ep1 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40444").unwrap(), udp_port: 40444 }; let ep2 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40445").unwrap(), udp_port: 40445 }; let mut discovery1 = Discovery::new(&key1, ep1.address.clone(), ep1.clone(), 0); @@ -594,7 +594,7 @@ mod tests { #[test] fn removes_expired() { - let key = KeyPair::create().unwrap(); + let key = Random.generate().unwrap(); let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40446").unwrap(), udp_port: 40447 }; let mut discovery = Discovery::new(&key, ep.address.clone(), ep.clone(), 0); for _ in 0..1200 { @@ -622,7 +622,7 @@ mod tests { #[test] fn packets() { - let key = KeyPair::create().unwrap(); + let key = Random.generate().unwrap(); let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40447").unwrap(), udp_port: 40447 }; let mut discovery = Discovery::new(&key, ep.address.clone(), ep.clone(), 0); discovery.check_timestamps = false; diff --git a/util/network/src/error.rs b/util/network/src/error.rs index cbe1638ea..99bfb4500 100644 --- a/util/network/src/error.rs +++ b/util/network/src/error.rs @@ -15,10 +15,11 @@ // along with Parity. If not, see . use io::IoError; -use util::crypto::CryptoError; use util::rlp::*; use util::UtilError; use std::fmt; +use ethkey::Error as KeyError; +use crypto::Error as CryptoError; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum DisconnectReason @@ -153,6 +154,12 @@ impl From for NetworkError { } } +impl From for NetworkError { + fn from(_err: KeyError) -> Self { + NetworkError::Auth + } +} + impl From for NetworkError { fn from(_err: CryptoError) -> NetworkError { NetworkError::Auth @@ -179,7 +186,7 @@ fn test_errors() { _ => panic!("Unexpeceted error"), } - match >::from(CryptoError::InvalidSecret) { + match >::from(CryptoError::InvalidMessage) { NetworkError::Auth => {}, _ => panic!("Unexpeceted error"), } diff --git a/util/network/src/handshake.rs b/util/network/src/handshake.rs index e87197683..403079de4 100644 --- a/util/network/src/handshake.rs +++ b/util/network/src/handshake.rs @@ -21,14 +21,14 @@ use util::hash::*; use util::rlp::*; use util::sha3::Hashable; use util::bytes::Bytes; -use util::crypto::*; -use util::crypto; use connection::{Connection}; use host::{HostInfo}; use node_table::NodeId; use error::*; use stats::NetworkStats; use io::{IoContext, StreamToken}; +use ethkey::{KeyPair, Public, Secret, recover, sign, Generator, Random}; +use crypto::{ecdh, ecies}; #[derive(PartialEq, Eq, Debug)] enum HandshakeState { @@ -89,7 +89,7 @@ impl Handshake { connection: Connection::new(token, socket, stats), originated: false, state: HandshakeState::New, - ecdhe: try!(KeyPair::create()), + ecdhe: try!(Random.generate()), nonce: nonce.clone(), remote_ephemeral: Public::new(), remote_nonce: H256::new(), @@ -166,8 +166,8 @@ impl Handshake { self.remote_nonce.clone_from_slice(remote_nonce); self.remote_version = remote_version; let shared = try!(ecdh::agree(host_secret, &self.id)); - let signature = Signature::from_slice(sig); - self.remote_ephemeral = try!(ec::recover(&signature, &(&shared ^ &self.remote_nonce))); + let signature = H520::from_slice(sig); + self.remote_ephemeral = try!(recover(&signature.into(), &(&shared ^ &self.remote_nonce))); Ok(()) } @@ -208,7 +208,7 @@ impl Handshake { self.auth_cipher.extend_from_slice(data); let auth = try!(ecies::decrypt(secret, &self.auth_cipher[0..2], &self.auth_cipher[2..])); let rlp = UntrustedRlp::new(&auth); - let signature: Signature = try!(rlp.val_at(0)); + let signature: H520 = try!(rlp.val_at(0)); let remote_public: Public = try!(rlp.val_at(1)); let remote_nonce: H256 = try!(rlp.val_at(2)); let remote_version: u64 = try!(rlp.val_at(3)); @@ -271,13 +271,13 @@ impl Handshake { let (nonce, _) = rest.split_at_mut(32); // E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) - let shared = try!(crypto::ecdh::agree(secret, &self.id)); - try!(crypto::ec::sign(self.ecdhe.secret(), &(&shared ^ &self.nonce))).copy_to(sig); + let shared = try!(ecdh::agree(secret, &self.id)); + sig.copy_from_slice(&*try!(sign(self.ecdhe.secret(), &(&shared ^ &self.nonce)))); self.ecdhe.public().sha3_into(hepubk); - public.copy_to(pubk); - self.nonce.copy_to(nonce); + pubk.copy_from_slice(public); + nonce.copy_from_slice(&self.nonce); } - let message = try!(crypto::ecies::encrypt(&self.id, &[], &data)); + let message = try!(ecies::encrypt(&self.id, &[], &data)); self.auth_cipher = message.clone(); self.connection.send(io, message); self.connection.expect(V4_ACK_PACKET_SIZE); @@ -297,7 +297,7 @@ impl Handshake { self.ecdhe.public().copy_to(epubk); self.nonce.copy_to(nonce); } - let message = try!(crypto::ecies::encrypt(&self.id, &[], &data)); + let message = try!(ecies::encrypt(&self.id, &[], &data)); self.ack_cipher = message.clone(); self.connection.send(io, message); self.state = HandshakeState::StartSession; @@ -319,7 +319,7 @@ impl Handshake { let encoded = rlp.drain(); let len = (encoded.len() + ECIES_OVERHEAD) as u16; let prefix = [ (len >> 8) as u8, (len & 0xff) as u8 ]; - let message = try!(crypto::ecies::encrypt(&self.id, &prefix, &encoded)); + let message = try!(ecies::encrypt(&self.id, &prefix, &encoded)); self.ack_cipher.extend_from_slice(&prefix); self.ack_cipher.extend_from_slice(&message); self.connection.send(io, self.ack_cipher.clone()); @@ -331,31 +331,29 @@ impl Handshake { #[cfg(test)] mod test { use std::sync::Arc; - use std::str::FromStr; use rustc_serialize::hex::FromHex; use super::*; - use util::crypto::*; use util::hash::*; use io::*; - use std::net::SocketAddr; use mio::tcp::TcpStream; use stats::NetworkStats; + use ethkey::Public; fn check_auth(h: &Handshake, version: u64) { - assert_eq!(h.id, Public::from_str("fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877").unwrap()); - assert_eq!(h.remote_nonce, H256::from_str("7e968bba13b6c50e2c4cd7f241cc0d64d1ac25c7f5952df231ac6a2bda8ee5d6").unwrap()); - assert_eq!(h.remote_ephemeral, Public::from_str("654d1044b69c577a44e5f01a1209523adb4026e70c62d1c13a067acabc09d2667a49821a0ad4b634554d330a15a58fe61f8a8e0544b310c6de7b0c8da7528a8d").unwrap()); + assert_eq!(h.id, "fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877".into()); + assert_eq!(h.remote_nonce, "7e968bba13b6c50e2c4cd7f241cc0d64d1ac25c7f5952df231ac6a2bda8ee5d6".into()); + assert_eq!(h.remote_ephemeral, "654d1044b69c577a44e5f01a1209523adb4026e70c62d1c13a067acabc09d2667a49821a0ad4b634554d330a15a58fe61f8a8e0544b310c6de7b0c8da7528a8d".into()); assert_eq!(h.remote_version, version); } fn check_ack(h: &Handshake, version: u64) { - assert_eq!(h.remote_nonce, H256::from_str("559aead08264d5795d3909718cdd05abd49572e84fe55590eef31a88a08fdffd").unwrap()); - assert_eq!(h.remote_ephemeral, Public::from_str("b6d82fa3409da933dbf9cb0140c5dde89f4e64aec88d476af648880f4a10e1e49fe35ef3e69e93dd300b4797765a747c6384a6ecf5db9c2690398607a86181e4").unwrap()); + assert_eq!(h.remote_nonce, "559aead08264d5795d3909718cdd05abd49572e84fe55590eef31a88a08fdffd".into()); + assert_eq!(h.remote_ephemeral, "b6d82fa3409da933dbf9cb0140c5dde89f4e64aec88d476af648880f4a10e1e49fe35ef3e69e93dd300b4797765a747c6384a6ecf5db9c2690398607a86181e4".into()); assert_eq!(h.remote_version, version); } fn create_handshake(to: Option<&Public>) -> Handshake { - let addr = SocketAddr::from_str("127.0.0.1:50556").unwrap(); + let addr = "127.0.0.1:50556".parse().unwrap(); let socket = TcpStream::connect(&addr).unwrap(); let nonce = H256::new(); Handshake::new(0, to, socket, &nonce, Arc::new(NetworkStats::new())).unwrap() @@ -368,7 +366,7 @@ mod test { #[test] fn test_handshake_auth_plain() { let mut h = create_handshake(None); - let secret = Secret::from_str("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291").unwrap(); + let secret = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291".into(); let auth = "\ 048ca79ad18e4b0659fab4853fe5bc58eb83992980f4c9cc147d2aa31532efd29a3d3dc6a3d89eaf\ @@ -389,7 +387,7 @@ mod test { #[test] fn test_handshake_auth_eip8() { let mut h = create_handshake(None); - let secret = Secret::from_str("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291").unwrap(); + let secret = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291".into(); let auth = "\ 01b304ab7578555167be8154d5cc456f567d5ba302662433674222360f08d5f1534499d3678b513b\ @@ -415,7 +413,7 @@ mod test { #[test] fn test_handshake_auth_eip8_2() { let mut h = create_handshake(None); - let secret = Secret::from_str("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291").unwrap(); + let secret = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291".into(); let auth = "\ 01b8044c6c312173685d1edd268aa95e1d495474c6959bcdd10067ba4c9013df9e40ff45f5bfd6f7\ @@ -444,9 +442,9 @@ mod test { #[test] fn test_handshake_ack_plain() { - let remote = Public::from_str("fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877").unwrap(); + let remote = "fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877".into(); let mut h = create_handshake(Some(&remote)); - let secret = Secret::from_str("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee").unwrap(); + let secret = "49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee".into(); let ack = "\ 049f8abcfa9c0dc65b982e98af921bc0ba6e4243169348a236abe9df5f93aa69d99cadddaa387662\ @@ -464,9 +462,9 @@ mod test { #[test] fn test_handshake_ack_eip8() { - let remote = Public::from_str("fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877").unwrap(); + let remote = "fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877".into(); let mut h = create_handshake(Some(&remote)); - let secret = Secret::from_str("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee").unwrap(); + let secret = "49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee".into(); let ack = "\ 01ea0451958701280a56482929d3b0757da8f7fbe5286784beead59d95089c217c9b917788989470\ @@ -493,9 +491,9 @@ mod test { #[test] fn test_handshake_ack_eip8_2() { - let remote = Public::from_str("fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877").unwrap(); + let remote = "fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877".into(); let mut h = create_handshake(Some(&remote)); - let secret = Secret::from_str("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee").unwrap(); + let secret = "49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee".into(); let ack = "\ 01f004076e58aae772bb101ab1a8e64e01ee96e64857ce82b1113817c6cdd52c09d26f7b90981cd7\ diff --git a/util/network/src/host.rs b/util/network/src/host.rs index a414ade40..a1d320c58 100644 --- a/util/network/src/host.rs +++ b/util/network/src/host.rs @@ -23,12 +23,11 @@ use std::ops::*; use std::cmp::min; use std::path::{Path, PathBuf}; use std::io::{Read, Write}; -use std::default::Default; use std::fs; +use ethkey::{KeyPair, Secret, Random, Generator}; use mio::*; use mio::tcp::*; use util::hash::*; -use util::crypto::*; use util::Hashable; use util::rlp::*; use util::version; @@ -362,7 +361,7 @@ impl Host { } else { config.config_path.clone().and_then(|ref p| load_key(Path::new(&p))) .map_or_else(|| { - let key = KeyPair::create().unwrap(); + let key = Random.generate().unwrap(); if let Some(path) = config.config_path.clone() { save_key(Path::new(&path), key.secret()); } diff --git a/util/network/src/lib.rs b/util/network/src/lib.rs index 47e3a1256..e36861f37 100644 --- a/util/network/src/lib.rs +++ b/util/network/src/lib.rs @@ -70,6 +70,8 @@ extern crate rustc_serialize; extern crate igd; extern crate libc; extern crate slab; +extern crate ethkey; +extern crate ethcrypto as crypto; #[cfg(test)] extern crate ethcore_devtools as devtools; diff --git a/util/network/src/tests.rs b/util/network/src/tests.rs index 3a19cbbab..4186e549a 100644 --- a/util/network/src/tests.rs +++ b/util/network/src/tests.rs @@ -20,7 +20,7 @@ use std::thread; use std::time::*; use util::common::*; use io::TimerToken; -use util::crypto::KeyPair; +use ethkey::{Random, Generator}; pub struct TestProtocol { drop_session: bool, @@ -99,7 +99,7 @@ fn net_service() { #[test] fn net_connect() { ::util::log::init_log(); - let key1 = KeyPair::create().unwrap(); + let key1 = Random.generate().unwrap(); let mut config1 = NetworkConfiguration::new_local(); config1.use_secret = Some(key1.secret().clone()); config1.boot_nodes = vec![ ]; @@ -130,7 +130,7 @@ fn net_start_stop() { #[test] fn net_disconnect() { - let key1 = KeyPair::create().unwrap(); + let key1 = Random.generate().unwrap(); let mut config1 = NetworkConfiguration::new_local(); config1.use_secret = Some(key1.secret().clone()); config1.boot_nodes = vec![ ]; diff --git a/util/src/crypto.rs b/util/src/crypto.rs deleted file mode 100644 index 298bc205e..000000000 --- a/util/src/crypto.rs +++ /dev/null @@ -1,560 +0,0 @@ -// Copyright 2015, 2016 Ethcore (UK) Ltd. -// This file is part of Parity. - -// Parity 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 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. If not, see . - -//! Ethcore crypto. - -use bigint::uint::*; -use bigint::hash::*; -use bytes::*; -use secp256k1::{key, Secp256k1}; -use rand::os::OsRng; -use sha3::Hashable; -use std::fmt; -use Address; - -/// Secret key for secp256k1 EC operations. 256 bit generic "hash" data. -pub type Secret = H256; -/// Public key for secp256k1 EC operations. 512 bit generic "hash" data. -pub type Public = H512; -/// Signature for secp256k1 EC operations; encodes two 256-bit curve points -/// and a third sign bit. 520 bit generic "hash" data. -pub type Signature = H520; - -lazy_static! { - static ref SECP256K1: Secp256k1 = Secp256k1::new(); -} - -/// Create a new signature from the R, S and V componenets. -pub fn signature_from_rsv(r: &H256, s: &H256, v: u8) -> Signature { - let mut ret: Signature = Signature::new(); - (&mut ret[0..32]).copy_from_slice(r); - (&mut ret[32..64]).copy_from_slice(s); - - ret[64] = v; - ret -} - -/// Convert transaction to R, S and V components. -pub fn signature_to_rsv(s: &Signature) -> (U256, U256, u8) { - (U256::from(&s.as_slice()[0..32]), U256::from(&s.as_slice()[32..64]), s[64]) -} - -#[derive(Debug)] -/// Crypto error -pub enum CryptoError { - /// Invalid secret key - InvalidSecret, - /// Invalid public key - InvalidPublic, - /// Invalid EC signature - InvalidSignature, - /// Invalid AES message - InvalidMessage, - /// IO Error - Io(::std::io::Error), -} - -impl fmt::Display for CryptoError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let msg = match *self { - CryptoError::InvalidSecret => "Invalid secret key".into(), - CryptoError::InvalidPublic => "Invalid public key".into(), - CryptoError::InvalidSignature => "Invalid EC signature".into(), - CryptoError::InvalidMessage => "Invalid AES message".into(), - CryptoError::Io(ref err) => format!("I/O error: {}", err), - }; - - f.write_fmt(format_args!("Crypto error ({})", msg)) - } -} - -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::InvalidSecretKey => CryptoError::InvalidSecret, - _ => CryptoError::InvalidSignature, - } - } -} - -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 = ec::sign(pair.secret(), &message).unwrap(); -/// -/// assert!(ec::verify(pair.public(), &signature, &message).unwrap()); -/// assert_eq!(ec::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; - 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, - }) - } - - - // TODO: move to ethstore/secret.rs once @debris has refactored necessary dependencies into own crate - /// Convert the given phrase into a secret as per brain-wallet spec. - /// Taken from https://github.com/ethereum/wiki/wiki/Brain-Wallet - /// Note particularly secure for low-entropy keys. - pub fn from_phrase(phrase: &str) -> KeyPair { - let mut h = phrase.as_bytes().sha3(); - for _ in 0..16384 { - h = h.sha3(); - } - loop { - let r = KeyPair::from_secret(h); - if r.is_ok() { - let r = r.unwrap(); - if r.address()[0] == 0 { - return r; - } - } - h = h.sha3(); - } - } - - /// Create a new random key pair - pub fn create() -> Result { - let context = &SECP256K1; - 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 mut s = Secret::new(); - s.copy_from_slice(&sec[0..32]); - - 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 - } - - /// Returns address. - pub fn address(&self) -> Address { - Address::from(self.public.sha3()) - } - - /// Sign a message with our secret key. - pub fn sign(&self, message: &H256) -> Result { ec::sign(&self.secret, message) } -} - -/// EC functions -#[cfg_attr(feature="dev", allow(similar_names))] -pub mod ec { - use bigint::hash::*; - use bigint::uint::*; - use standard::*; - use crypto::*; - use crypto::{self}; - - /// Recovers Public key from signed message hash. - pub fn recover(signature: &Signature, message: &H256) -> Result { - use secp256k1::*; - let context = &crypto::SECP256K1; - 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]); - //TODO: check if it's the zero key and fail if so. - Ok(p) - } - /// Returns siganture of message hash. - pub fn sign(secret: &Secret, message: &H256) -> Result { - // TODO: allow creation of only low-s signatures. - use secp256k1::{Message, key}; - - let context = &crypto::SECP256K1; - // no way to create from raw byte array. - 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::new(); - signature.clone_from_slice(&data); - signature[64] = rec_id.to_i32() as u8; - - let (_, s, v) = signature_to_rsv(&signature); - let secp256k1n = U256::from_str("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141").unwrap(); - if !is_low_s(&s) { - signature = super::signature_from_rsv(&H256::from_slice(&signature[0..32]), &H256::from(secp256k1n - s), v ^ 1); - } - Ok(signature) - } - - /// Verify signature. - pub fn verify(public: &Public, signature: &Signature, message: &H256) -> Result { - use secp256k1::*; - let context = &crypto::SECP256K1; - 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 pdata: [u8; 65] = { - let mut temp = [4u8; 65]; - (&mut temp[1..65]).copy_from_slice(public); - temp - }; - - 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(CryptoError::from(x)) - } - } - - /// Check if this is a "low" signature. - pub fn is_low(sig: &Signature) -> bool { - H256::from_slice(&sig[32..64]) <= "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0".into() - } - - /// Check if this is a "low" signature. - pub fn is_low_s(s: &U256) -> bool { - s <= &U256::from_str("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0").unwrap() - } - - /// Check if each component of the signature is in range. - pub fn is_valid(sig: &Signature) -> bool { - sig[64] <= 1 && - H256::from_slice(&sig[0..32]) < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into() && - H256::from_slice(&sig[32..64]) < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into() && - H256::from_slice(&sig[32..64]) >= 1.into() && - H256::from_slice(&sig[0..32]) >= 1.into() - } -} - -/// ECDH functions -#[cfg_attr(feature="dev", allow(similar_names))] -pub mod ecdh { - use hash::FixedHash; - use crypto::{self, Secret, Public, CryptoError}; - - /// Agree on a shared secret - pub fn agree(secret: &Secret, public: &Public) -> Result { - use secp256k1::{ecdh, key}; - - let context = &crypto::SECP256K1; - let pdata = { - let mut temp = [4u8; 65]; - (&mut temp[1..65]).copy_from_slice(&public[0..64]); - temp - }; - - let publ = try!(key::PublicKey::from_slice(context, &pdata)); - // no way to create SecretKey from raw byte array. - let sec: &key::SecretKey = unsafe { ::std::mem::transmute(secret) }; - let shared = ecdh::SharedSecret::new_raw(context, &publ, sec); - - let mut s = crypto::Secret::new(); - s.copy_from_slice(&shared[0..32]); - Ok(s) - } -} - -/// ECIES function -#[cfg_attr(feature="dev", allow(similar_names))] -pub mod ecies { - use hash::*; - use bytes::*; - use crypto::*; - use sha3::Hashable; - - /// Encrypt a message with a public key - pub fn encrypt(public: &Public, shared_mac: &[u8], plain: &[u8]) -> Result { - use ::rcrypto::digest::Digest; - use ::rcrypto::sha2::Sha256; - use ::rcrypto::hmac::Hmac; - use ::rcrypto::mac::Mac; - let r = try!(KeyPair::create()); - let z = try!(ecdh::agree(r.secret(), public)); - let mut key = [0u8; 32]; - let mut mkey = [0u8; 32]; - kdf(&z, &[0u8; 0], &mut key); - let mut hasher = Sha256::new(); - let mkey_material = &key[16..32]; - hasher.input(mkey_material); - hasher.result(&mut mkey); - let ekey = &key[0..16]; - - let mut msg = vec![0u8; (1 + 64 + 16 + plain.len() + 32)]; - msg[0] = 0x04u8; - { - let msgd = &mut msg[1..]; - r.public().copy_to(&mut msgd[0..64]); - let iv = H128::random(); - iv.copy_to(&mut msgd[64..(64+16)]); - { - let cipher = &mut msgd[(64 + 16)..(64 + 16 + plain.len())]; - aes::encrypt(ekey, &iv, plain, cipher); - } - let mut hmac = Hmac::new(Sha256::new(), &mkey); - { - let cipher_iv = &msgd[64..(64 + 16 + plain.len())]; - hmac.input(cipher_iv); - } - hmac.input(shared_mac); - hmac.raw_result(&mut msgd[(64 + 16 + plain.len())..]); - } - Ok(msg) - } - - /// Encrypt a message with a public key - pub fn encrypt_single_message(public: &Public, plain: &[u8]) -> Result { - use ::rcrypto::digest::Digest; - use ::rcrypto::sha2::Sha256; - let r = try!(KeyPair::create()); - let z = try!(ecdh::agree(r.secret(), public)); - let mut key = [0u8; 32]; - let mut mkey = [0u8; 32]; - kdf(&z, &[0u8; 0], &mut key); - let mut hasher = Sha256::new(); - let mkey_material = &key[16..32]; - hasher.input(mkey_material); - hasher.result(&mut mkey); - let ekey = &key[0..16]; - - let mut msgd = vec![0u8; (64 + plain.len())]; - { - r.public().copy_to(&mut msgd[0..64]); - let iv = H128::from_slice(&z.sha3()[0..16]); - { - let cipher = &mut msgd[64..(64 + plain.len())]; - aes::encrypt(ekey, &iv, plain, cipher); - } - } - Ok(msgd) - } - - /// Decrypt a message with a secret key - pub fn decrypt(secret: &Secret, shared_mac: &[u8], encrypted: &[u8]) -> Result { - use ::rcrypto::digest::Digest; - use ::rcrypto::sha2::Sha256; - use ::rcrypto::hmac::Hmac; - use ::rcrypto::mac::Mac; - - let meta_len = 1 + 64 + 16 + 32; - if encrypted.len() < meta_len || encrypted[0] < 2 || encrypted[0] > 4 { - return Err(CryptoError::InvalidMessage); //invalid message: publickey - } - - let e = &encrypted[1..]; - let p = Public::from_slice(&e[0..64]); - let z = try!(ecdh::agree(secret, &p)); - let mut key = [0u8; 32]; - kdf(&z, &[0u8; 0], &mut key); - let ekey = &key[0..16]; - let mkey_material = &key[16..32]; - let mut hasher = Sha256::new(); - let mut mkey = [0u8; 32]; - hasher.input(mkey_material); - hasher.result(&mut mkey); - - let clen = encrypted.len() - meta_len; - let cipher_with_iv = &e[64..(64+16+clen)]; - let cipher_iv = &cipher_with_iv[0..16]; - let cipher_no_iv = &cipher_with_iv[16..]; - let msg_mac = &e[(64+16+clen)..]; - - // Verify tag - let mut hmac = Hmac::new(Sha256::new(), &mkey); - hmac.input(cipher_with_iv); - hmac.input(shared_mac); - let mut mac = H256::new(); - hmac.raw_result(&mut mac); - if &mac[..] != msg_mac { - return Err(CryptoError::InvalidMessage); - } - - let mut msg = vec![0u8; clen]; - aes::decrypt(ekey, cipher_iv, cipher_no_iv, &mut msg[..]); - Ok(msg) - } - - /// Decrypt single message with a secret key - pub fn decrypt_single_message(secret: &Secret, encrypted: &[u8]) -> Result { - use ::rcrypto::digest::Digest; - use ::rcrypto::sha2::Sha256; - - let meta_len = 64; - if encrypted.len() < meta_len { - return Err(CryptoError::InvalidMessage); //invalid message: publickey - } - - let e = encrypted; - let p = Public::from_slice(&e[0..64]); - let z = try!(ecdh::agree(secret, &p)); - let mut key = [0u8; 32]; - kdf(&z, &[0u8; 0], &mut key); - let ekey = &key[0..16]; - let mkey_material = &key[16..32]; - let mut hasher = Sha256::new(); - let mut mkey = [0u8; 32]; - hasher.input(mkey_material); - hasher.result(&mut mkey); - - let clen = encrypted.len() - meta_len; - let cipher = &e[64..(64+clen)]; - let mut msg = vec![0u8; clen]; - let iv = H128::from_slice(&z.sha3()[0..16]); - aes::decrypt(ekey, &iv, cipher, &mut msg[..]); - Ok(msg) - } - - fn kdf(secret: &Secret, s1: &[u8], dest: &mut [u8]) { - use ::rcrypto::digest::Digest; - use ::rcrypto::sha2::Sha256; - let mut hasher = Sha256::new(); - // 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 ctrs = [(ctr >> 24) as u8, (ctr >> 16) as u8, (ctr >> 8) as u8, ctr as u8]; - hasher.input(&ctrs); - hasher.input(secret); - hasher.input(s1); - hasher.result(&mut dest[written..(written + 32)]); - hasher.reset(); - written += 32; - ctr += 1; - } - } -} - -/// AES encryption -pub mod aes { - use ::rcrypto::blockmodes::*; - use ::rcrypto::aessafe::*; - use ::rcrypto::symmetriccipher::*; - use ::rcrypto::buffer::*; - - /// Encrypt a message - pub fn encrypt(k: &[u8], iv: &[u8], plain: &[u8], dest: &mut [u8]) { - let mut encryptor = CtrMode::new(AesSafe128Encryptor::new(k), iv.to_vec()); - encryptor.encrypt(&mut RefReadBuffer::new(plain), &mut RefWriteBuffer::new(dest), true).expect("Invalid length or padding"); - } - - /// Decrypt a message - pub fn decrypt(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) { - let mut encryptor = CtrMode::new(AesSafe128Encryptor::new(k), iv.to_vec()); - encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut RefWriteBuffer::new(dest), true).expect("Invalid length or padding"); - } -} - - -#[cfg(test)] -mod tests { - use hash::*; - use crypto::*; - - // TODO: tests for sign/recover roundtrip, at least. - - #[test] - fn test_signature() { - let pair = KeyPair::create().unwrap(); - let message = H256::random(); - let signature = ec::sign(pair.secret(), &message).unwrap(); - - assert!(ec::verify(pair.public(), &signature, &message).unwrap()); - assert_eq!(ec::recover(&signature, &message).unwrap(), *pair.public()); - } - - #[test] - fn test_invalid_key() { - assert!(KeyPair::from_secret("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".into()).is_err()); - assert!(KeyPair::from_secret("0000000000000000000000000000000000000000000000000000000000000000".into()).is_err()); - assert!(KeyPair::from_secret("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into()).is_err()); - } - - #[test] - fn test_key() { - let pair = KeyPair::from_secret("6f7b0d801bc7b5ce7bbd930b84fd0369b3eb25d09be58d64ba811091046f3aa2".into()).unwrap(); - assert_eq!(pair.public().hex(), "101b3ef5a4ea7a1c7928e24c4c75fd053c235d7b80c22ae5c03d145d0ac7396e2a4ffff9adee3133a7b05044a5cee08115fd65145e5165d646bde371010d803c"); - } - - #[test] - fn test_key_from_phrase() { - assert_eq!(KeyPair::from_phrase("correct horse battery staple").address(), "0021f80b7f29b9c84e8099c2c6c74a46ed2268c4".into()); - } - - #[test] - fn ecies_shared() { - let kp = KeyPair::create().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[..]); - } - - #[test] - fn ecies_shared_single() { - let kp = KeyPair::create().unwrap(); - let message = b"So many books, so little time"; - let encrypted = ecies::encrypt_single_message(kp.public(), message).unwrap(); - assert!(encrypted[..] != message[..]); - let decrypted = ecies::decrypt_single_message(kp.secret(), &encrypted).unwrap(); - assert_eq!(decrypted[..message.len()], message[..]); - } -} diff --git a/util/src/error.rs b/util/src/error.rs index afc49b27c..e1bf7b8a7 100644 --- a/util/src/error.rs +++ b/util/src/error.rs @@ -44,8 +44,6 @@ impl fmt::Display for BaseDataError { #[derive(Debug)] /// General error type which should be capable of representing all errors in ethcore. pub enum UtilError { - /// Error concerning the crypto utility subsystem. - Crypto(::crypto::CryptoError), /// Error concerning the Rust standard library's IO subsystem. StdIo(::std::io::Error), /// Error concerning the hex conversion logic. @@ -65,7 +63,6 @@ pub enum UtilError { impl fmt::Display for UtilError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - UtilError::Crypto(ref err) => f.write_fmt(format_args!("{}", err)), UtilError::StdIo(ref err) => f.write_fmt(format_args!("{}", err)), UtilError::FromHex(ref err) => f.write_fmt(format_args!("{}", err)), UtilError::BaseData(ref err) => f.write_fmt(format_args!("{}", err)), @@ -134,12 +131,6 @@ impl From<::std::io::Error> for UtilError { } } -impl From<::crypto::CryptoError> for UtilError { - fn from(err: ::crypto::CryptoError) -> UtilError { - UtilError::Crypto(err) - } -} - impl From<::rlp::DecoderError> for UtilError { fn from(err: ::rlp::DecoderError) -> UtilError { UtilError::Decoder(err) diff --git a/util/src/lib.rs b/util/src/lib.rs index f8dc34af8..f7459615e 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -132,7 +132,6 @@ pub mod migration; pub mod overlaydb; pub mod journaldb; pub mod kvdb; -pub mod crypto; pub mod triehash; pub mod trie; pub mod nibbleslice; @@ -150,7 +149,6 @@ pub use hashdb::*; pub use memorydb::*; pub use overlaydb::*; pub use journaldb::JournalDB; -pub use crypto::*; pub use triehash::*; pub use trie::{Trie, TrieMut, TrieDB, TrieDBMut, TrieFactory, TrieError, SecTrieDB, SecTrieDBMut}; pub use nibbleslice::*; @@ -162,3 +160,5 @@ pub use timer::*; /// 160-bit integer representing account address pub type Address = H160; +/// Secret +pub type Secret = H256;