encrypting any bytes covertible now via hashmap
This commit is contained in:
parent
67058123ba
commit
26e992ad2e
@ -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),*) => {
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user