encrypting any bytes covertible now via hashmap

This commit is contained in:
Nikolay Volf 2016-02-16 11:30:22 +03:00
parent 67058123ba
commit 26e992ad2e
3 changed files with 82 additions and 22 deletions

View File

@ -178,6 +178,10 @@ impl BytesConvertable for Vec<u8> {
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),*) => {

View File

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

View File

@ -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<Key: Hash + Eq> {
// Returns existing value for the key, if any
fn get<Value: Populatable + Default>(&self, key: &Key, password: &str) -> Option<Value>;
fn get<Value: Populatable + Default + BytesConvertable>(&self, key: &Key, password: &str) -> Option<Value>;
// Insert new encrypted key-value and returns previous if there was any
fn insert<Value: Populatable + Default>(&mut self, key: Key, value: Value, password: &str) -> Option<Value>;
fn insert<Value: Populatable + Default + BytesConvertable>(&mut self, key: Key, value: Value, password: &str) -> Option<Value>;
// Removes key-value by key and returns the removed one, if any exists and password was provided
fn remove<Value: Populatable + Default> (&mut self, key: &Key, password: Option<&str>) -> Option<Value>;
fn remove<Value: Populatable + Default + BytesConvertable> (&mut self, key: &Key, password: Option<&str>) -> Option<Value>;
// 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<H128> for SecretStore {
fn get<Value: Populatable + Default>(&self, key: &H128, password: &str) -> Option<Value> {
fn get<Value: Populatable + Default + BytesConvertable>(&self, key: &H128, password: &str) -> Option<Value> {
match self.directory.get(key) {
Some(key_file) => {
let mut instance = Value::default();
@ -49,17 +74,48 @@ impl EncryptedHashMap<H128> for SecretStore {
}
fn insert<Value: Populatable + Default>(&mut self, key: H128, value: Value, password: &str) -> Option<Value>{
let previous = if let Some(key_file) = self.directory.get(&key) { self.get(&key, password) } else { None };
fn insert<Value: Populatable + Default + BytesConvertable>(&mut self, key: H128, value: Value, password: &str) -> Option<Value> {
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<Value: Populatable + Default>(&mut self, key: &H128, password: Option<&str>) -> Option<Value> {
fn remove<Value: Populatable + Default + BytesConvertable>(&mut self, key: &H128, password: Option<&str>) -> Option<Value> {
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");
}
}