diff --git a/Cargo.lock b/Cargo.lock index 6420679f2..63597b890 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -672,6 +672,7 @@ dependencies = [ "ethcore-bigint 0.1.3", "ethkey 0.2.0", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2461,6 +2462,14 @@ name = "strsim" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "subtle" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "0.11.11" @@ -3100,6 +3109,7 @@ dependencies = [ "checksum spmc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93bdab61c1a413e591c4d17388ffa859eaff2df27f1e13a5ec8b716700605adf" "checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" +"checksum subtle 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b811576c12506ff3f6da145585dc833edc32ee34c9fc021127d90e8134cc05c" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum syntex 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)" = "35f3cc9d446323ef8fefad933b65cd6de271d29fa14a2e9d036a084770c6d6d5" diff --git a/ethcrypto/Cargo.toml b/ethcrypto/Cargo.toml index 7d749d9ca..8e5131974 100644 --- a/ethcrypto/Cargo.toml +++ b/ethcrypto/Cargo.toml @@ -9,4 +9,4 @@ tiny-keccak = "1.2" eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" } ethkey = { path = "../ethkey" } ethcore-bigint = { path = "../util/bigint" } - +subtle = "0.1" diff --git a/ethcrypto/src/lib.rs b/ethcrypto/src/lib.rs index 209162bba..3d34a7975 100644 --- a/ethcrypto/src/lib.rs +++ b/ethcrypto/src/lib.rs @@ -16,11 +16,12 @@ //! Crypto utils used ethstore and network. -extern crate ethcore_bigint as bigint; -extern crate tiny_keccak; extern crate crypto as rcrypto; -extern crate secp256k1; +extern crate ethcore_bigint as bigint; extern crate ethkey; +extern crate secp256k1; +extern crate subtle; +extern crate tiny_keccak; use std::fmt; use tiny_keccak::Keccak; @@ -34,7 +35,7 @@ pub const KEY_LENGTH: usize = 32; pub const KEY_ITERATIONS: usize = 10240; pub const KEY_LENGTH_AES: usize = KEY_LENGTH / 2; -/// Default MAC to use (in RPC). +/// Default authenticated data to use (in RPC). pub const DEFAULT_MAC: [u8; 2] = [0, 0]; #[derive(PartialEq, Debug)] @@ -149,13 +150,13 @@ pub mod aes { use rcrypto::symmetriccipher::{Encryptor, Decryptor, SymmetricCipherError}; use rcrypto::buffer::{RefReadBuffer, RefWriteBuffer, WriteBuffer}; - /// Encrypt a message + /// Encrypt a message (CTR mode) 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 + /// Decrypt a message (CTR mode) 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"); @@ -208,8 +209,11 @@ pub mod ecies { use ethkey::{Random, Generator, Public, Secret}; use {Error, ecdh, aes, Keccak256}; - /// Encrypt a message with a public key - pub fn encrypt(public: &Public, shared_mac: &[u8], plain: &[u8]) -> Result, Error> { + /// Encrypt a message with a public key, writing an HMAC covering both + /// the plaintext and authenticated data. + /// + /// Authenticated data may be empty. + pub fn encrypt(public: &Public, auth_data: &[u8], plain: &[u8]) -> Result, Error> { let r = Random.generate() .expect("context known to have key-generation capabilities; qed"); @@ -239,13 +243,13 @@ pub mod ecies { let cipher_iv = &msgd[64..(64 + 16 + plain.len())]; hmac.input(cipher_iv); } - hmac.input(shared_mac); + hmac.input(auth_data); hmac.raw_result(&mut msgd[(64 + 16 + plain.len())..]); } Ok(msg) } - /// Encrypt a message with a public key + /// Encrypt a message with a public key and no HMAC pub fn encrypt_single_message(public: &Public, plain: &[u8]) -> Result, Error> { let r = Random.generate() .expect("context known to have key-generation capabilities"); @@ -272,8 +276,9 @@ pub mod ecies { Ok(msgd) } - /// Decrypt a message with a secret key - pub fn decrypt(secret: &Secret, shared_mac: &[u8], encrypted: &[u8]) -> Result, Error> { + /// Decrypt a message with a secret key, checking HMAC for ciphertext + /// and authenticated data validity. + pub fn decrypt(secret: &Secret, auth_data: &[u8], encrypted: &[u8]) -> Result, Error> { 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 @@ -300,10 +305,12 @@ pub mod ecies { // Verify tag let mut hmac = Hmac::new(Sha256::new(), &mkey); hmac.input(cipher_with_iv); - hmac.input(shared_mac); + hmac.input(auth_data); let mut mac = [0u8; 32]; hmac.raw_result(&mut mac); - if &mac[..] != msg_mac { + + // constant time compare to avoid timing attack. + if ::subtle::arrays_equal(&mac[..], msg_mac) != 1 { return Err(Error::InvalidMessage); } @@ -312,7 +319,7 @@ pub mod ecies { Ok(msg) } - /// Decrypt single message with a secret key + /// Decrypt single message with a secret key and no HMAC. pub fn decrypt_single_message(secret: &Secret, encrypted: &[u8]) -> Result, Error> { let meta_len = 64; if encrypted.len() < meta_len {