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 }
|
fn bytes(&self) -> &[u8] { self }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BytesConvertable for String {
|
||||||
|
fn bytes(&self) -> &[u8] { &self.as_bytes() }
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! impl_bytes_convertable_for_array {
|
macro_rules! impl_bytes_convertable_for_array {
|
||||||
($zero: expr) => ();
|
($zero: expr) => ();
|
||||||
($len: expr, $($idx: expr),*) => {
|
($len: expr, $($idx: expr),*) => {
|
||||||
|
@ -27,7 +27,7 @@ const MAX_CACHE_USAGE_TRACK: usize = 128;
|
|||||||
#[derive(PartialEq, Debug, Clone)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
pub enum CryptoCipherType {
|
pub enum CryptoCipherType {
|
||||||
/// aes-128-ctr with 128-bit initialisation vector(iv)
|
/// aes-128-ctr with 128-bit initialisation vector(iv)
|
||||||
Aes128Ctr(U128)
|
Aes128Ctr(H128)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Clone)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
@ -182,7 +182,7 @@ impl KeyFileCrypto {
|
|||||||
Some("aes-128-ctr") => CryptoCipherType::Aes128Ctr(
|
Some("aes-128-ctr") => CryptoCipherType::Aes128Ctr(
|
||||||
match try!(as_object.get("cipherparams").ok_or(CryptoParseError::NoCipherParameters)).as_object() {
|
match try!(as_object.get("cipherparams").ok_or(CryptoParseError::NoCipherParameters)).as_object() {
|
||||||
None => { return Err(CryptoParseError::NoCipherParameters); },
|
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); },
|
None => { return Err(CryptoParseError::NoInitialVector); },
|
||||||
Some(iv_hex_string) => iv_hex_string
|
Some(iv_hex_string) => iv_hex_string
|
||||||
})
|
})
|
||||||
@ -231,7 +231,7 @@ impl KeyFileCrypto {
|
|||||||
fn to_json(&self) -> Json {
|
fn to_json(&self) -> Json {
|
||||||
let mut map = BTreeMap::new();
|
let mut map = BTreeMap::new();
|
||||||
match self.cipher_type {
|
match self.cipher_type {
|
||||||
CryptoCipherType::Aes128Ctr(iv) => {
|
CryptoCipherType::Aes128Ctr(ref iv) => {
|
||||||
map.insert("cipher".to_owned(), Json::String("aes-128-ctr".to_owned()));
|
map.insert("cipher".to_owned(), Json::String("aes-128-ctr".to_owned()));
|
||||||
let mut cipher_params = BTreeMap::new();
|
let mut cipher_params = BTreeMap::new();
|
||||||
cipher_params.insert("iv".to_owned(), Json::String(format!("{:?}", iv)));
|
cipher_params.insert("iv".to_owned(), Json::String(format!("{:?}", iv)));
|
||||||
@ -260,7 +260,7 @@ impl KeyFileCrypto {
|
|||||||
/// `c` - number of iterations for derived key.
|
/// `c` - number of iterations for derived key.
|
||||||
/// `salt` - cryptographic site, random 256-bit hash (ensure it's crypto-random).
|
/// `salt` - cryptographic site, random 256-bit hash (ensure it's crypto-random).
|
||||||
/// `iv` - initialisation vector.
|
/// `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 {
|
KeyFileCrypto {
|
||||||
cipher_type: CryptoCipherType::Aes128Ctr(iv),
|
cipher_type: CryptoCipherType::Aes128Ctr(iv),
|
||||||
cipher_text: cipher_text,
|
cipher_text: cipher_text,
|
||||||
@ -331,7 +331,7 @@ enum CryptoParseError {
|
|||||||
InvalidCipherType(Mismatch<String>),
|
InvalidCipherType(Mismatch<String>),
|
||||||
NoInitialVector,
|
NoInitialVector,
|
||||||
NoCipherParameters,
|
NoCipherParameters,
|
||||||
InvalidInitialVector(FromHexError),
|
InvalidInitialVector(UtilError),
|
||||||
NoKdf,
|
NoKdf,
|
||||||
NoKdfType,
|
NoKdfType,
|
||||||
Scrypt(ScryptParseError),
|
Scrypt(ScryptParseError),
|
||||||
@ -832,14 +832,14 @@ mod file_tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn can_create_key_with_new_id() {
|
fn can_create_key_with_new_id() {
|
||||||
let cipher_text: Bytes = FromHex::from_hex("a0f05555").unwrap();
|
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());
|
assert!(!uuid_to_string(&key.id).is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_load_json_from_itself() {
|
fn can_load_json_from_itself() {
|
||||||
let cipher_text: Bytes = FromHex::from_hex("aaaaaaaaaaaaaaaaaaaaaaaaaaa22222222222222222222222").unwrap();
|
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 json = key.to_json();
|
||||||
|
|
||||||
let loaded_key = KeyFileContent::from_json(&json).unwrap();
|
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 cipher_text: Bytes = FromHex::from_hex("a0f05555").unwrap();
|
||||||
let temp_path = RandomTempPath::create_dir();
|
let temp_path = RandomTempPath::create_dir();
|
||||||
let mut directory = KeyDirectory::new(&temp_path.as_path());
|
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 path = directory.key_path(&uuid);
|
||||||
|
|
||||||
let key = KeyDirectory::load_key(&path).unwrap();
|
let key = KeyDirectory::load_key(&path).unwrap();
|
||||||
@ -1013,7 +1013,7 @@ mod directory_tests {
|
|||||||
let cipher_text: Bytes = FromHex::from_hex("a0f05555").unwrap();
|
let cipher_text: Bytes = FromHex::from_hex("a0f05555").unwrap();
|
||||||
let mut keys = Vec::new();
|
let mut keys = Vec::new();
|
||||||
for _ in 0..1000 {
|
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());
|
keys.push(directory.save(key).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1033,7 +1033,7 @@ mod directory_tests {
|
|||||||
let cipher_text: Bytes = FromHex::from_hex("a0f05555").unwrap();
|
let cipher_text: Bytes = FromHex::from_hex("a0f05555").unwrap();
|
||||||
let mut keys = Vec::new();
|
let mut keys = Vec::new();
|
||||||
for _ in 0..1000 {
|
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());
|
keys.push(directory.save(key).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1066,7 +1066,7 @@ mod specs {
|
|||||||
let temp_path = RandomTempPath::create_dir();
|
let temp_path = RandomTempPath::create_dir();
|
||||||
let mut directory = KeyDirectory::new(&temp_path.as_path());
|
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());
|
assert!(uuid.is_ok());
|
||||||
}
|
}
|
||||||
@ -1076,7 +1076,7 @@ mod specs {
|
|||||||
let cipher_text: Bytes = FromHex::from_hex("a0f05555").unwrap();
|
let cipher_text: Bytes = FromHex::from_hex("a0f05555").unwrap();
|
||||||
let temp_path = RandomTempPath::create_dir();
|
let temp_path = RandomTempPath::create_dir();
|
||||||
let mut directory = KeyDirectory::new(&temp_path.as_path());
|
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();
|
let key = directory.get(&uuid).unwrap();
|
||||||
|
|
||||||
@ -1091,7 +1091,7 @@ mod specs {
|
|||||||
let cipher_text: Bytes = FromHex::from_hex("a0f05555").unwrap();
|
let cipher_text: Bytes = FromHex::from_hex("a0f05555").unwrap();
|
||||||
let mut keys = Vec::new();
|
let mut keys = Vec::new();
|
||||||
for _ in 0..10 {
|
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());
|
keys.push(directory.save(key).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,17 +18,25 @@
|
|||||||
|
|
||||||
use keys::directory::*;
|
use keys::directory::*;
|
||||||
use common::*;
|
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> {
|
pub trait EncryptedHashMap<Key: Hash + Eq> {
|
||||||
// Returns existing value for the key, if any
|
// 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
|
// 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
|
// 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
|
// Deletes key-value by key and returns if the key-value existed
|
||||||
fn delete(&mut self, key: &Key) -> bool {
|
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
|
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 {
|
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) {
|
match self.directory.get(key) {
|
||||||
Some(key_file) => {
|
Some(key_file) => {
|
||||||
let mut instance = Value::default();
|
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>{
|
fn insert<Value: Populatable + Default + BytesConvertable>(&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 };
|
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
|
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) {
|
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
|
(_, _) => None
|
||||||
};
|
};
|
||||||
previous
|
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