use error::Error;
use list::List;
use ring::pbkdf2;
use ring::rand::{SecureRandom, SystemRandom};
use std::collections::HashMap;
use vault::{Vault, NONCE_LEN};
static PRF: &'static pbkdf2::PRF = &pbkdf2::HMAC_SHA256;
const ITERATIONS: u32 = 100000;
const SALT_LEN: usize = 16;
const KEY_LEN: usize = 32;
const DERIVED_KEY_LEN: usize = 32;
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct Data {
pub salt: [u8; SALT_LEN],
pub key: Vault,
pub entries: HashMap<String, Vault>,
}
pub struct Unlocked<'a> {
key: [u8; KEY_LEN],
data: &'a mut Data,
}
impl Data {
pub fn new(pwd: String) -> Result<Data, Error> {
let mut salt = [0; SALT_LEN];
let mut key = vec![0; KEY_LEN];
randomize(&mut salt)?;
randomize(&mut key[..])?;
Ok(Data {
salt: salt,
key: Vault::new(
&derive_key(&pwd, &salt),
random_nonce()?,
key
)?,
entries: HashMap::new(),
})
}
pub fn change_password(
&mut self,
old: &str,
new: &str
) -> Result<(), Error> {
let mut salt = [0; SALT_LEN];
let mut nonce = [0; NONCE_LEN];
randomize(&mut salt)?;
randomize(&mut nonce)?;
let old_pbk = derive_key(old, &self.salt);
let new_pbk = derive_key(new, &salt);
let mut unlocked = self.key.unlock(&old_pbk)?;
unlocked.rekey(&new_pbk, nonce);
self.salt = salt;
Ok(())
}
pub fn unlock<'a>(
&'a mut self,
pwd: &str
) -> Result<Unlocked<'a>, Error> {
let key = {
let pbk = derive_key(pwd, &self.salt);
let unlocked = self.key.unlock(&pbk)?;
if unlocked.len() != KEY_LEN {
return Err(Error::BadEncryptionKey);
}
let mut key = [0; KEY_LEN];
key.copy_from_slice(&unlocked);
key
};
Ok(Unlocked {
key: key,
data: self,
})
}
pub fn list(&self) -> List {
List(self.entries.keys())
}
pub fn delete(&mut self, key: &str) -> bool {
self.entries.remove(key).is_some()
}
}
impl<'a> Unlocked<'a> {
pub fn get(
&mut self,
name: &str
) -> Result<Option<String>, Error> {
let bytes = match self.data.entries.get_mut(name) {
Some(vault) => vault.unlock(&self.key)?.to_vec(),
None => return Ok(None),
};
match String::from_utf8(bytes) {
Ok(k) => Ok(Some(k)),
Err(_) => Err(Error::DecodeString),
}
}
pub fn set(
&mut self,
name: String,
pwd: String
) -> Result<bool, Error> {
let vault = Vault::new(
&self.key,
random_nonce()?,
pwd.into_bytes()
)?;
Ok(self.data.entries.insert(name, vault).is_some())
}
pub fn delete(
&mut self,
name: &str
) -> bool {
self.data.entries.remove(name).is_some()
}
}
fn derive_key(pwd: &str, salt: &[u8]) -> [u8; DERIVED_KEY_LEN] {
let mut pbk = [0; DERIVED_KEY_LEN];
pbkdf2::derive(
PRF,
ITERATIONS,
salt,
pwd.as_bytes(),
&mut pbk
);
pbk
}
fn random_nonce() -> Result<[u8; NONCE_LEN], Error> {
let mut nonce = [0; NONCE_LEN];
randomize(&mut nonce)?;
Ok(nonce)
}
fn randomize(buf: &mut [u8]) -> Result<(), Error> {
match SystemRandom.fill(buf) {
Ok(_) => Ok(()),
Err(_) => Err(Error::Randomize),
}
}