Consolidate crypto functionality in ethcore-crypto. (#8432)

* Consolidate crypto functionality in `ethcore-crypto`.

- Move `ecdh`/`ecies` modules to `ethkey`.
- Refactor `ethcore-crypto` to use file per module.
- Replace `subtle` with `ethcore_crypto::is_equal`.
- Add `aes_gcm` module to `ethcore-crypto`.

* Rename `aes::{encrypt,decrypt,decrypt_cbc}` ...

... to `aes::{encrypt_128_ctr,decrypt_128_ctr,decrypt_128_cbc}`.
This commit is contained in:
Toralf Wittner
2018-05-05 11:02:33 +02:00
committed by Marek Kotewicz
parent a4c7843a07
commit e30839e85f
50 changed files with 1003 additions and 542 deletions

View File

@@ -21,7 +21,6 @@ use crypto::Keccak256;
use random::Random;
use smallvec::SmallVec;
use account::{Cipher, Kdf, Aes128Ctr, Pbkdf2, Prf};
use subtle;
/// Encrypted data
#[derive(Debug, PartialEq, Clone)]
@@ -74,12 +73,12 @@ impl From<Crypto> for String {
impl Crypto {
/// Encrypt account secret
pub fn with_secret(secret: &Secret, password: &str, iterations: u32) -> Self {
pub fn with_secret(secret: &Secret, password: &str, iterations: u32) -> Result<Self, crypto::Error> {
Crypto::with_plain(&*secret, password, iterations)
}
/// Encrypt custom plain data
pub fn with_plain(plain: &[u8], password: &str, iterations: u32) -> Self {
pub fn with_plain(plain: &[u8], password: &str, iterations: u32) -> Result<Self, crypto::Error> {
let salt: [u8; 32] = Random::random();
let iv: [u8; 16] = Random::random();
@@ -93,12 +92,12 @@ impl Crypto {
let mut ciphertext: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; plain_len]);
// aes-128-ctr with initial vector of iv
crypto::aes::encrypt(&derived_left_bits, &iv, plain, &mut *ciphertext);
crypto::aes::encrypt_128_ctr(&derived_left_bits, &iv, plain, &mut *ciphertext)?;
// KECCAK(DK[16..31] ++ <ciphertext>), where DK[16..31] - derived_right_bits
let mac = crypto::derive_mac(&derived_right_bits, &*ciphertext).keccak256();
Crypto {
Ok(Crypto {
cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: iv,
}),
@@ -110,7 +109,7 @@ impl Crypto {
prf: Prf::HmacSha256,
}),
mac: mac,
}
})
}
/// Try to decrypt and convert result to account secret
@@ -132,13 +131,13 @@ impl Crypto {
fn do_decrypt(&self, password: &str, expected_len: usize) -> Result<Vec<u8>, Error> {
let (derived_left_bits, derived_right_bits) = match self.kdf {
Kdf::Pbkdf2(ref params) => crypto::derive_key_iterations(password, &params.salt, params.c),
Kdf::Scrypt(ref params) => crypto::derive_key_scrypt(password, &params.salt, params.n, params.p, params.r)?,
Kdf::Scrypt(ref params) => crypto::scrypt::derive_key(password, &params.salt, params.n, params.p, params.r)?,
};
let mac = crypto::derive_mac(&derived_right_bits, &self.ciphertext).keccak256();
if subtle::slices_equal(&mac, &self.mac) == 0 {
return Err(Error::InvalidPassword);
if !crypto::is_equal(&mac, &self.mac) {
return Err(Error::InvalidPassword)
}
let mut plain: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; expected_len]);
@@ -149,7 +148,7 @@ impl Crypto {
debug_assert!(expected_len >= self.ciphertext.len());
let from = expected_len - self.ciphertext.len();
crypto::aes::decrypt(&derived_left_bits, &params.iv, &self.ciphertext, &mut plain[from..]);
crypto::aes::decrypt_128_ctr(&derived_left_bits, &params.iv, &self.ciphertext, &mut plain[from..])?;
Ok(plain.into_iter().collect())
},
}
@@ -164,7 +163,7 @@ mod tests {
#[test]
fn crypto_with_secret_create() {
let keypair = Random.generate().unwrap();
let crypto = Crypto::with_secret(keypair.secret(), "this is sparta", 10240);
let crypto = Crypto::with_secret(keypair.secret(), "this is sparta", 10240).unwrap();
let secret = crypto.secret("this is sparta").unwrap();
assert_eq!(keypair.secret(), &secret);
}
@@ -172,14 +171,14 @@ mod tests {
#[test]
fn crypto_with_secret_invalid_password() {
let keypair = Random.generate().unwrap();
let crypto = Crypto::with_secret(keypair.secret(), "this is sparta", 10240);
let crypto = Crypto::with_secret(keypair.secret(), "this is sparta", 10240).unwrap();
assert_matches!(crypto.secret("this is sparta!"), Err(Error::InvalidPassword))
}
#[test]
fn crypto_with_null_plain_data() {
let original_data = b"";
let crypto = Crypto::with_plain(&original_data[..], "this is sparta", 10240);
let crypto = Crypto::with_plain(&original_data[..], "this is sparta", 10240).unwrap();
let decrypted_data = crypto.decrypt("this is sparta").unwrap();
assert_eq!(original_data[..], *decrypted_data);
}
@@ -187,7 +186,7 @@ mod tests {
#[test]
fn crypto_with_tiny_plain_data() {
let original_data = b"{}";
let crypto = Crypto::with_plain(&original_data[..], "this is sparta", 10240);
let crypto = Crypto::with_plain(&original_data[..], "this is sparta", 10240).unwrap();
let decrypted_data = crypto.decrypt("this is sparta").unwrap();
assert_eq!(original_data[..], *decrypted_data);
}
@@ -195,7 +194,7 @@ mod tests {
#[test]
fn crypto_with_huge_plain_data() {
let original_data: Vec<_> = (1..65536).map(|i| (i % 256) as u8).collect();
let crypto = Crypto::with_plain(&original_data, "this is sparta", 10240);
let crypto = Crypto::with_plain(&original_data, "this is sparta", 10240).unwrap();
let decrypted_data = crypto.decrypt("this is sparta").unwrap();
assert_eq!(&original_data, &decrypted_data);
}

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 ethkey::{KeyPair, sign, Address, Signature, Message, Public, Secret};
use crypto::ecdh::agree;
use {json, Error, crypto};
use ethkey::{self, KeyPair, sign, Address, Signature, Message, Public, Secret};
use ethkey::crypto::ecdh::agree;
use {json, Error};
use account::Version;
use crypto;
use super::crypto::Crypto;
/// Account representation.
@@ -61,16 +62,16 @@ impl SafeAccount {
iterations: u32,
name: String,
meta: String
) -> Self {
SafeAccount {
) -> Result<Self, crypto::Error> {
Ok(SafeAccount {
id: id,
version: Version::V3,
crypto: Crypto::with_secret(keypair.secret(), password, iterations),
crypto: Crypto::with_secret(keypair.secret(), password, iterations)?,
address: keypair.address(),
filename: None,
name: name,
meta: meta,
}
})
}
/// Create a new `SafeAccount` from the given `json`; if it was read from a
@@ -114,7 +115,7 @@ impl SafeAccount {
meta: Some(self.meta),
};
let meta_plain = meta_plain.write().map_err(|e| Error::Custom(format!("{:?}", e)))?;
let meta_crypto = Crypto::with_plain(&meta_plain, password, iterations);
let meta_crypto = Crypto::with_plain(&meta_plain, password, iterations)?;
Ok(json::VaultKeyFile {
id: self.id.into(),
@@ -133,7 +134,7 @@ impl SafeAccount {
/// Decrypt a message.
pub fn decrypt(&self, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
let secret = self.crypto.secret(password)?;
crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from)
ethkey::crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from)
}
/// Agree on shared key.
@@ -154,7 +155,7 @@ impl SafeAccount {
let result = SafeAccount {
id: self.id.clone(),
version: self.version.clone(),
crypto: Crypto::with_secret(&secret, new_password, iterations),
crypto: Crypto::with_secret(&secret, new_password, iterations)?,
address: self.address.clone(),
filename: self.filename.clone(),
name: self.name.clone(),
@@ -180,7 +181,7 @@ mod tests {
let password = "hello world";
let message = Message::default();
let account = SafeAccount::create(&keypair, [0u8; 16], password, 10240, "Test".to_owned(), "{}".to_owned());
let signature = account.sign(password, &message).unwrap();
let signature = account.unwrap().sign(password, &message).unwrap();
assert!(verify_public(keypair.public(), &signature, &message).unwrap());
}
@@ -191,7 +192,7 @@ mod tests {
let sec_password = "this is sparta";
let i = 10240;
let message = Message::default();
let account = SafeAccount::create(&keypair, [0u8; 16], first_password, i, "Test".to_owned(), "{}".to_owned());
let account = SafeAccount::create(&keypair, [0u8; 16], first_password, i, "Test".to_owned(), "{}".to_owned()).unwrap();
let new_account = account.change_password(first_password, sec_password, i).unwrap();
assert!(account.sign(first_password, &message).is_ok());
assert!(account.sign(sec_password, &message).is_err());

View File

@@ -319,7 +319,7 @@ mod test {
// when
let account = SafeAccount::create(&keypair, [0u8; 16], password, 1024, "Test".to_owned(), "{}".to_owned());
let res = directory.insert(account);
let res = directory.insert(account.unwrap());
// then
assert!(res.is_ok(), "Should save account succesfuly.");
@@ -339,7 +339,7 @@ mod test {
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
// when
let account = SafeAccount::create(&keypair, [0u8; 16], password, 1024, "Test".to_owned(), "{}".to_owned());
let account = SafeAccount::create(&keypair, [0u8; 16], password, 1024, "Test".to_owned(), "{}".to_owned()).unwrap();
let filename = "test".to_string();
let dedup = true;
@@ -424,7 +424,7 @@ mod test {
let keypair = Random.generate().unwrap();
let password = "test pass";
let account = SafeAccount::create(&keypair, [0u8; 16], password, 1024, "Test".to_owned(), "{}".to_owned());
directory.insert(account).expect("Account should be inserted ok");
directory.insert(account.unwrap()).expect("Account should be inserted ok");
let new_hash = directory.files_hash().expect("New files hash should be calculated ok");

View File

@@ -235,7 +235,7 @@ fn check_vault_name(name: &str) -> bool {
/// Vault can be empty, but still must be pluggable => we store vault password in separate file
fn create_vault_file<P>(vault_dir_path: P, key: &VaultKey, meta: &str) -> Result<(), Error> where P: AsRef<Path> {
let password_hash = key.password.keccak256();
let crypto = Crypto::with_plain(&password_hash, &key.password, key.iterations);
let crypto = Crypto::with_plain(&password_hash, &key.password, key.iterations)?;
let mut vault_file_path: PathBuf = vault_dir_path.as_ref().into();
vault_file_path.push(VAULT_FILE_NAME);

View File

@@ -16,8 +16,8 @@
use std::fmt;
use std::io::Error as IoError;
use ethkey::Error as EthKeyError;
use crypto::Error as EthCryptoError;
use ethkey::{self, Error as EthKeyError};
use crypto::{self, Error as EthCryptoError};
use ethkey::DerivationError;
/// Account-related errors.
@@ -49,6 +49,8 @@ pub enum Error {
CreationFailed,
/// `EthKey` error
EthKey(EthKeyError),
/// `ethkey::crypto::Error`
EthKeyCrypto(ethkey::crypto::Error),
/// `EthCrypto` error
EthCrypto(EthCryptoError),
/// Derivation error
@@ -73,6 +75,7 @@ impl fmt::Display for Error {
Error::VaultNotFound => "Vault not found".into(),
Error::CreationFailed => "Account creation failed".into(),
Error::EthKey(ref err) => err.to_string(),
Error::EthKeyCrypto(ref err) => err.to_string(),
Error::EthCrypto(ref err) => err.to_string(),
Error::Derivation(ref err) => format!("Derivation error: {:?}", err),
Error::Custom(ref s) => s.clone(),
@@ -94,12 +97,30 @@ impl From<EthKeyError> for Error {
}
}
impl From<ethkey::crypto::Error> for Error {
fn from(err: ethkey::crypto::Error) -> Self {
Error::EthKeyCrypto(err)
}
}
impl From<EthCryptoError> for Error {
fn from(err: EthCryptoError) -> Self {
Error::EthCrypto(err)
}
}
impl From<crypto::error::ScryptError> for Error {
fn from(err: crypto::error::ScryptError) -> Self {
Error::EthCrypto(err.into())
}
}
impl From<crypto::error::SymmError> for Error {
fn from(err: crypto::error::SymmError) -> Self {
Error::EthCrypto(err.into())
}
}
impl From<DerivationError> for Error {
fn from(err: DerivationError) -> Self {
Error::Derivation(err)

View File

@@ -458,7 +458,7 @@ impl SimpleSecretStore for EthMultiStore {
fn insert_account(&self, vault: SecretVaultRef, secret: Secret, password: &str) -> Result<StoreAccountRef, Error> {
let keypair = KeyPair::from_secret(secret).map_err(|_| Error::CreationFailed)?;
let id: [u8; 16] = Random::random();
let account = SafeAccount::create(&keypair, id, password, self.iterations, "".to_owned(), "{}".to_owned());
let account = SafeAccount::create(&keypair, id, password, self.iterations, "".to_owned(), "{}".to_owned())?;
self.import(vault, account)
}

View File

@@ -18,7 +18,6 @@
#![warn(missing_docs)]
extern crate crypto as rcrypto;
extern crate dir;
extern crate itertools;
extern crate libc;
@@ -28,7 +27,6 @@ extern crate rustc_hex;
extern crate serde;
extern crate serde_json;
extern crate smallvec;
extern crate subtle;
extern crate time;
extern crate tiny_keccak;
extern crate tempdir;

View File

@@ -1,11 +1,8 @@
use std::fs;
use std::path::Path;
use rcrypto::pbkdf2::pbkdf2;
use rcrypto::sha2::Sha256;
use rcrypto::hmac::Hmac;
use json;
use ethkey::{Address, Secret, KeyPair};
use crypto::Keccak256;
use crypto::{Keccak256, pbkdf2};
use {crypto, Error};
/// Pre-sale wallet.
@@ -42,12 +39,14 @@ impl PresaleWallet {
/// Decrypt the wallet.
pub fn decrypt(&self, password: &str) -> Result<KeyPair, Error> {
let mut h_mac = Hmac::new(Sha256::new(), password.as_bytes());
let mut derived_key = vec![0u8; 16];
pbkdf2(&mut h_mac, password.as_bytes(), 2000, &mut derived_key);
let mut derived_key = [0u8; 32];
let salt = pbkdf2::Salt(password.as_bytes());
let sec = pbkdf2::Secret(password.as_bytes());
pbkdf2::sha256(2000, salt, sec, &mut derived_key);
let mut key = vec![0; self.ciphertext.len()];
let len = crypto::aes::decrypt_cbc(&derived_key, &self.iv, &self.ciphertext, &mut key).map_err(|_| Error::InvalidPassword)?;
let len = crypto::aes::decrypt_128_cbc(&derived_key[0..16], &self.iv, &self.ciphertext, &mut key)
.map_err(|_| Error::InvalidPassword)?;
let unpadded = &key[..len];
let secret = Secret::from_unsafe_slice(&unpadded.keccak256())?;