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
This commit is contained in:
Marek Kotewicz 2016-08-24 18:35:21 +02:00 committed by Gav Wood
parent f07a1e6baf
commit b0d462c6c9
39 changed files with 444 additions and 808 deletions

16
Cargo.lock generated
View File

@ -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)",

View File

@ -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"

View File

@ -14,10 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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<Fn(&[u8], &mut [u8])> {
}),
"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()) {

View File

@ -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 {

View File

@ -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::<H520>());
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]

View File

@ -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());
}

View File

@ -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<ExecutionError> for Error {
}
}
impl From<CryptoError> for Error {
fn from(err: CryptoError) -> Error {
Error::Util(UtilError::Crypto(err))
}
}
impl From<DecoderError> for Error {
fn from(err: DecoderError) -> Error {
Error::Util(UtilError::Decoder(err))
@ -361,6 +359,12 @@ impl From<SnapshotError> for Error {
}
}
impl From<EthkeyError> for Error {
fn from(err: EthkeyError) -> Error {
Error::Ethkey(err)
}
}
impl<E> From<Box<E>> for Error where Error: From<E> {
fn from(err: Box<E>) -> Error {
Error::from(*err)

View File

@ -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),

View File

@ -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;

View File

@ -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(),

View File

@ -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(),
@ -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);
@ -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());

View File

@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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<F>(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;

View File

@ -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<SignedTransaction, Error> {
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),

View File

@ -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,

12
ethcrypto/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "ethcrypto"
version = "0.1.0"
authors = ["debris <marek.kotewicz@gmail.com>"]
[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" }

246
ethcrypto/src/lib.rs Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
//! 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<SecpError> for Error {
fn from(e: SecpError) -> Self {
Error::Secp(e)
}
}
pub trait Keccak256<T> {
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<u8>, Vec<u8>) {
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<u8>, Vec<u8>) {
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<u8> {
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<usize, SymmetricCipherError> {
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<Secret, Error> {
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<Vec<u8>, 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<Vec<u8>, 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;
}
}
}

View File

@ -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.

View File

@ -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 <http://www.gnu.org/licenses/>.

View File

@ -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.

View File

@ -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 }

View File

@ -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 <http://www.gnu.org/licenses/>.
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<u8>, Vec<u8>) {
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<u8>, Vec<u8>) {
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<u8> {
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<T> {
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<usize, SymmetricCipherError> {
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())
}
}

View File

@ -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;

View File

@ -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" }

View File

@ -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;

View File

@ -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<C, M, S: ?Sized> Ethcore for EthcoreClient<C, M, S> where M: MinerService +
fn phrase_to_address(&self, params: Params) -> Result<Value, Error> {
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()))
)
}
}

View File

@ -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<C: 'static, M: 'static> Personal for PersonalClient<C, M> 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)),
}

View File

@ -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();

View File

@ -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<String>,
/// Use provided node key instead of default
pub use_secret: Option<Secret>,
pub use_secret: Option<H256>,
/// Max number of connected peers to maintain
pub max_peers: u32,
/// Min number of connected peers to maintain

View File

@ -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 = []

View File

@ -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;

View File

@ -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;

View File

@ -15,10 +15,11 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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<UtilError> for NetworkError {
}
}
impl From<KeyError> for NetworkError {
fn from(_err: KeyError) -> Self {
NetworkError::Auth
}
}
impl From<CryptoError> for NetworkError {
fn from(_err: CryptoError) -> NetworkError {
NetworkError::Auth
@ -179,7 +186,7 @@ fn test_errors() {
_ => panic!("Unexpeceted error"),
}
match <NetworkError as From<CryptoError>>::from(CryptoError::InvalidSecret) {
match <NetworkError as From<CryptoError>>::from(CryptoError::InvalidMessage) {
NetworkError::Auth => {},
_ => panic!("Unexpeceted error"),
}

View File

@ -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\

View File

@ -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());
}

View File

@ -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;

View File

@ -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![ ];

View File

@ -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 <http://www.gnu.org/licenses/>.
//! 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<KeyPair, CryptoError> {
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<KeyPair, CryptoError> {
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<Signature, CryptoError> { 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<Public, CryptoError> {
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<Signature, CryptoError> {
// 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<bool, CryptoError> {
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<Secret, CryptoError> {
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<Bytes, CryptoError> {
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<Bytes, CryptoError> {
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<Bytes, CryptoError> {
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<Bytes, CryptoError> {
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[..]);
}
}

View File

@ -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)

View File

@ -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;