use serde::{Deserialize, Serialize};
use zeroize::Zeroizing;
use crate::crypto::{self, Key};
use crate::error::{Error, Result};
use crate::kdf::KdfParams;
pub(crate) const CONFIG_VERSION: u32 = 2;
#[derive(Serialize, Deserialize)]
pub(crate) struct VaultConfig {
pub version: u32,
pub kdf: KdfParams,
pub salt: Vec<u8>,
pub wrapped_master_key: Vec<u8>,
}
pub(crate) const VAULT_CONFIG_AAD: &[u8] = b"krypton-vault-config-v2";
fn config_nonce() -> [u8; 12] {
let mut n = [0xFFu8; 12];
n[0] = 0x01;
n
}
impl VaultConfig {
pub(crate) fn parse(json: &str) -> Result<Self> {
let config: VaultConfig = serde_json::from_str(json).map_err(|_| Error::InvalidVault)?;
if config.version != CONFIG_VERSION {
return Err(Error::UnsupportedVersion(config.version));
}
Ok(config)
}
pub(crate) fn unwrap_master_key(&self, password: &str) -> Result<Key> {
if self.salt.len() < 8 {
return Err(Error::InvalidVault);
}
let kek = crypto::derive_key(password.as_bytes(), &self.salt, self.kdf)?;
let mut combined = Zeroizing::new(self.wrapped_master_key.clone());
crypto::open_in_place(&config_nonce(), &mut combined, &kek, VAULT_CONFIG_AAD)?;
if combined.len() != 32 {
return Err(Error::InvalidVault);
}
let mut key_bytes = [0u8; 32];
key_bytes.copy_from_slice(combined.as_slice());
Ok(Key::from_bytes(key_bytes))
}
}
#[derive(Default)]
pub(crate) struct KeyStore {
master_key: Option<Key>,
}
impl KeyStore {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn is_unlocked(&self) -> bool {
self.master_key.is_some()
}
pub(crate) fn master_key(&self) -> Result<&Key> {
self.master_key.as_ref().ok_or(Error::VaultLocked)
}
pub(crate) fn init(&mut self, password: &str) -> Result<VaultConfig> {
let master = Key::generate();
let config = wrap_for_password(&master, password)?;
self.master_key = Some(master);
Ok(config)
}
pub(crate) fn unlock(&mut self, password: &str, config: &VaultConfig) -> Result<()> {
if self.is_unlocked() {
return Err(Error::VaultUnlocked);
}
let master = config.unwrap_master_key(password)?;
self.master_key = Some(master);
Ok(())
}
pub(crate) fn lock(&mut self) {
self.master_key = None; }
pub(crate) fn rotate_password(&mut self, new: &str) -> Result<VaultConfig> {
let master = self.master_key()?.clone();
wrap_for_password(&master, new)
}
}
fn wrap_for_password(master: &Key, password: &str) -> Result<VaultConfig> {
let mut salt = [0u8; 32];
crypto::fill_random(&mut salt);
let params = KdfParams::new();
let kek = crypto::derive_key(password.as_bytes(), &salt, params)?;
let mut buf = Zeroizing::new(master.expose().to_vec());
crypto::seal_in_place(&config_nonce(), &mut buf, &kek, VAULT_CONFIG_AAD)?;
Ok(VaultConfig {
version: CONFIG_VERSION,
kdf: params,
salt: salt.to_vec(),
wrapped_master_key: buf.to_vec(),
})
}