From 26e992ad2ed4381762f15c3544b63eae951d40b8 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Tue, 16 Feb 2016 11:30:22 +0300 Subject: [PATCH] encrypting any bytes covertible now via hashmap --- util/src/bytes.rs | 4 +++ util/src/keys/directory.rs | 26 +++++++------- util/src/keys/encryptor.rs | 74 +++++++++++++++++++++++++++++++++----- 3 files changed, 82 insertions(+), 22 deletions(-) diff --git a/util/src/bytes.rs b/util/src/bytes.rs index 5ad2660e8..0006827e8 100644 --- a/util/src/bytes.rs +++ b/util/src/bytes.rs @@ -178,6 +178,10 @@ impl BytesConvertable for Vec { fn bytes(&self) -> &[u8] { self } } +impl BytesConvertable for String { + fn bytes(&self) -> &[u8] { &self.as_bytes() } +} + macro_rules! impl_bytes_convertable_for_array { ($zero: expr) => (); ($len: expr, $($idx: expr),*) => { diff --git a/util/src/keys/directory.rs b/util/src/keys/directory.rs index 23e82d010..ccd7d8fb4 100644 --- a/util/src/keys/directory.rs +++ b/util/src/keys/directory.rs @@ -27,7 +27,7 @@ const MAX_CACHE_USAGE_TRACK: usize = 128; #[derive(PartialEq, Debug, Clone)] pub enum CryptoCipherType { /// aes-128-ctr with 128-bit initialisation vector(iv) - Aes128Ctr(U128) + Aes128Ctr(H128) } #[derive(PartialEq, Debug, Clone)] @@ -182,7 +182,7 @@ impl KeyFileCrypto { Some("aes-128-ctr") => CryptoCipherType::Aes128Ctr( match try!(as_object.get("cipherparams").ok_or(CryptoParseError::NoCipherParameters)).as_object() { None => { return Err(CryptoParseError::NoCipherParameters); }, - Some(cipher_param) => match U128::from_str(match cipher_param["iv"].as_string() { + Some(cipher_param) => match H128::from_str(match cipher_param["iv"].as_string() { None => { return Err(CryptoParseError::NoInitialVector); }, Some(iv_hex_string) => iv_hex_string }) @@ -231,7 +231,7 @@ impl KeyFileCrypto { fn to_json(&self) -> Json { let mut map = BTreeMap::new(); match self.cipher_type { - CryptoCipherType::Aes128Ctr(iv) => { + CryptoCipherType::Aes128Ctr(ref iv) => { map.insert("cipher".to_owned(), Json::String("aes-128-ctr".to_owned())); let mut cipher_params = BTreeMap::new(); cipher_params.insert("iv".to_owned(), Json::String(format!("{:?}", iv))); @@ -260,7 +260,7 @@ impl KeyFileCrypto { /// `c` - number of iterations for derived key. /// `salt` - cryptographic site, random 256-bit hash (ensure it's crypto-random). /// `iv` - initialisation vector. - pub fn new_pbkdf2(cipher_text: Bytes, iv: U128, salt: H256, c: u32, dk_len: u32) -> KeyFileCrypto { + pub fn new_pbkdf2(cipher_text: Bytes, iv: H128, salt: H256, c: u32, dk_len: u32) -> KeyFileCrypto { KeyFileCrypto { cipher_type: CryptoCipherType::Aes128Ctr(iv), cipher_text: cipher_text, @@ -331,7 +331,7 @@ enum CryptoParseError { InvalidCipherType(Mismatch), NoInitialVector, NoCipherParameters, - InvalidInitialVector(FromHexError), + InvalidInitialVector(UtilError), NoKdf, NoKdfType, Scrypt(ScryptParseError), @@ -832,14 +832,14 @@ mod file_tests { #[test] fn can_create_key_with_new_id() { let cipher_text: Bytes = FromHex::from_hex("a0f05555").unwrap(); - let key = KeyFileContent::new(KeyFileCrypto::new_pbkdf2(cipher_text, U128::zero(), H256::random(), 32, 32)); + let key = KeyFileContent::new(KeyFileCrypto::new_pbkdf2(cipher_text, H128::zero(), H256::random(), 32, 32)); assert!(!uuid_to_string(&key.id).is_empty()); } #[test] fn can_load_json_from_itself() { let cipher_text: Bytes = FromHex::from_hex("aaaaaaaaaaaaaaaaaaaaaaaaaaa22222222222222222222222").unwrap(); - let key = KeyFileContent::new(KeyFileCrypto::new_pbkdf2(cipher_text, U128::zero(), H256::random(), 32, 32)); + let key = KeyFileContent::new(KeyFileCrypto::new_pbkdf2(cipher_text, H128::zero(), H256::random(), 32, 32)); let json = key.to_json(); let loaded_key = KeyFileContent::from_json(&json).unwrap(); @@ -997,7 +997,7 @@ mod directory_tests { let cipher_text: Bytes = FromHex::from_hex("a0f05555").unwrap(); let temp_path = RandomTempPath::create_dir(); let mut directory = KeyDirectory::new(&temp_path.as_path()); - let uuid = directory.save(KeyFileContent::new(KeyFileCrypto::new_pbkdf2(cipher_text, U128::zero(), H256::random(), 32, 32))).unwrap(); + let uuid = directory.save(KeyFileContent::new(KeyFileCrypto::new_pbkdf2(cipher_text, H128::zero(), H256::random(), 32, 32))).unwrap(); let path = directory.key_path(&uuid); let key = KeyDirectory::load_key(&path).unwrap(); @@ -1013,7 +1013,7 @@ mod directory_tests { let cipher_text: Bytes = FromHex::from_hex("a0f05555").unwrap(); let mut keys = Vec::new(); for _ in 0..1000 { - let key = KeyFileContent::new(KeyFileCrypto::new_pbkdf2(cipher_text.clone(), U128::zero(), H256::random(), 32, 32)); + let key = KeyFileContent::new(KeyFileCrypto::new_pbkdf2(cipher_text.clone(), H128::zero(), H256::random(), 32, 32)); keys.push(directory.save(key).unwrap()); } @@ -1033,7 +1033,7 @@ mod directory_tests { let cipher_text: Bytes = FromHex::from_hex("a0f05555").unwrap(); let mut keys = Vec::new(); for _ in 0..1000 { - let key = KeyFileContent::new(KeyFileCrypto::new_pbkdf2(cipher_text.clone(), U128::zero(), H256::random(), 32, 32)); + let key = KeyFileContent::new(KeyFileCrypto::new_pbkdf2(cipher_text.clone(), H128::zero(), H256::random(), 32, 32)); keys.push(directory.save(key).unwrap()); } @@ -1066,7 +1066,7 @@ mod specs { let temp_path = RandomTempPath::create_dir(); let mut directory = KeyDirectory::new(&temp_path.as_path()); - let uuid = directory.save(KeyFileContent::new(KeyFileCrypto::new_pbkdf2(cipher_text, U128::zero(), H256::random(), 32, 32))); + let uuid = directory.save(KeyFileContent::new(KeyFileCrypto::new_pbkdf2(cipher_text, H128::zero(), H256::random(), 32, 32))); assert!(uuid.is_ok()); } @@ -1076,7 +1076,7 @@ mod specs { let cipher_text: Bytes = FromHex::from_hex("a0f05555").unwrap(); let temp_path = RandomTempPath::create_dir(); let mut directory = KeyDirectory::new(&temp_path.as_path()); - let uuid = directory.save(KeyFileContent::new(KeyFileCrypto::new_pbkdf2(cipher_text.clone(), U128::zero(), H256::random(), 32, 32))).unwrap(); + let uuid = directory.save(KeyFileContent::new(KeyFileCrypto::new_pbkdf2(cipher_text.clone(), H128::zero(), H256::random(), 32, 32))).unwrap(); let key = directory.get(&uuid).unwrap(); @@ -1091,7 +1091,7 @@ mod specs { let cipher_text: Bytes = FromHex::from_hex("a0f05555").unwrap(); let mut keys = Vec::new(); for _ in 0..10 { - let key = KeyFileContent::new(KeyFileCrypto::new_pbkdf2(cipher_text.clone(), U128::zero(), H256::random(), 32, 32)); + let key = KeyFileContent::new(KeyFileCrypto::new_pbkdf2(cipher_text.clone(), H128::zero(), H256::random(), 32, 32)); keys.push(directory.save(key).unwrap()); } diff --git a/util/src/keys/encryptor.rs b/util/src/keys/encryptor.rs index cbc926dc6..7645a2b84 100644 --- a/util/src/keys/encryptor.rs +++ b/util/src/keys/encryptor.rs @@ -18,17 +18,25 @@ use keys::directory::*; use common::*; +use rcrypto::pbkdf2::*; +use rcrypto::aes; +use rcrypto::hmac::*; +use crypto; + +const KEY_LENGTH: u32 = 32; +const KEY_ITERATIONS: u32 = 4096; +const KEY_LENGTH_AES: u32 = KEY_LENGTH/2; pub trait EncryptedHashMap { // Returns existing value for the key, if any - fn get(&self, key: &Key, password: &str) -> Option; + fn get(&self, key: &Key, password: &str) -> Option; // Insert new encrypted key-value and returns previous if there was any - fn insert(&mut self, key: Key, value: Value, password: &str) -> Option; + fn insert(&mut self, key: Key, value: Value, password: &str) -> Option; // Removes key-value by key and returns the removed one, if any exists and password was provided - fn remove (&mut self, key: &Key, password: Option<&str>) -> Option; + fn remove (&mut self, key: &Key, password: Option<&str>) -> Option; // Deletes key-value by key and returns if the key-value existed fn delete(&mut self, key: &Key) -> bool { - self.remove::<()>(key, None).is_some() + self.remove::<&[u8]>(key, None).is_some() } } @@ -36,8 +44,25 @@ pub struct SecretStore { directory: KeyDirectory } +impl SecretStore { + fn new() -> SecretStore { + let mut path = ::std::env::home_dir().expect("Failed to get home dir"); + path.push(".keys"); + SecretStore { + directory: KeyDirectory::new(&path) + } + } + + #[cfg(test)] + fn new_test(path: &::tests::helpers::RandomTempPath) -> SecretStore { + SecretStore { + directory: KeyDirectory::new(path.as_path()) + } + } +} + impl EncryptedHashMap for SecretStore { - fn get(&self, key: &H128, password: &str) -> Option { + fn get(&self, key: &H128, password: &str) -> Option { match self.directory.get(key) { Some(key_file) => { let mut instance = Value::default(); @@ -49,17 +74,48 @@ impl EncryptedHashMap for SecretStore { } - fn insert(&mut self, key: H128, value: Value, password: &str) -> Option{ - let previous = if let Some(key_file) = self.directory.get(&key) { self.get(&key, password) } else { None }; + fn insert(&mut self, key: H128, value: Value, password: &str) -> Option { + let previous = if let Some(_) = self.directory.get(&key) { self.get(&key, password) } else { None }; + + let salt = H256::random(); + let iv = H128::random(); + let mut key_file = KeyFileContent::new(KeyFileCrypto::new_pbkdf2(vec![], iv.clone(), salt.clone(), KEY_ITERATIONS, KEY_LENGTH)); + + let mut mac = Hmac::new(::rcrypto::sha2::Sha256::new(), password.as_bytes()); + let mut derived_key = vec![0u8; KEY_LENGTH as usize]; + pbkdf2(&mut mac, &salt.as_slice(), KEY_ITERATIONS, &mut derived_key); + let key = &derived_key[KEY_LENGTH_AES as usize..KEY_LENGTH as usize]; + + let mut cipher_text = vec![0u8; value.as_slice().len()]; + crypto::aes::encrypt(&key, &iv.as_slice(), &value.as_slice(), &mut cipher_text); + key_file.crypto.cipher_text = cipher_text; + previous } - fn remove(&mut self, key: &H128, password: Option<&str>) -> Option { + fn remove(&mut self, key: &H128, password: Option<&str>) -> Option { let previous = match (self.directory.get(&key), password) { - (Some(key_file), Some(pass)) => self.get(&key, pass), + (Some(_), Some(pass)) => self.get(&key, pass), (_, _) => None }; previous } } + + +#[cfg(test)] +mod tests { + use super::*; + use tests::helpers::*; + use common::*; + + #[test] + fn secret_store_insert() { + let temp = RandomTempPath::create_dir(); + let mut sstore = SecretStore::new_test(&temp); + + sstore.insert(H128::random(), "Cat".to_owned(), "pass"); + } + +}