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:
committed by
Marek Kotewicz
parent
a4c7843a07
commit
e30839e85f
@@ -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, ¶ms.salt, params.c),
|
||||
Kdf::Scrypt(ref params) => crypto::derive_key_scrypt(password, ¶ms.salt, params.n, params.p, params.r)?,
|
||||
Kdf::Scrypt(ref params) => crypto::scrypt::derive_key(password, ¶ms.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, ¶ms.iv, &self.ciphertext, &mut plain[from..]);
|
||||
crypto::aes::decrypt_128_ctr(&derived_left_bits, ¶ms.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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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())?;
|
||||
|
||||
Reference in New Issue
Block a user