use hkdf::Hkdf;
use sha2::Sha256;
use argon2::{Argon2, Algorithm, Version, Params};
use zeroize::Zeroizing;
use crate::crypto::primitives::{SecretKey, LAMBDA};
use crate::error::VaultError;
const ARGON2_M_COST: u32 = 65_536; const ARGON2_T_COST: u32 = 3; const ARGON2_P_COST: u32 = 1; const ARGON2_OUTPUT: usize = 32;
const INFO_TAG: &[u8] = b"rose2:k_tag:v1";
const INFO_VAL: &[u8] = b"rose2:k_val:v1";
const INFO_STATE: &[u8] = b"rose2:k_state:v1";
const INFO_SRCH: &[u8] = b"rose2:k_srch:v1";
pub struct MasterKeySet {
pub k_tag: SecretKey,
pub k_val: SecretKey,
pub k_state: SecretKey,
pub k_srch: SecretKey,
}
impl MasterKeySet {
pub fn derive(password: &str, salt: &[u8; 16]) -> Result<Self, VaultError> {
let params = Params::new(ARGON2_M_COST, ARGON2_T_COST, ARGON2_P_COST, Some(ARGON2_OUTPUT))
.map_err(|e| VaultError::Kdf(e.to_string()))?;
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
let mut root_key = Zeroizing::new([0u8; 32]);
argon2
.hash_password_into(password.as_bytes(), salt, root_key.as_mut())
.map_err(|e| VaultError::Kdf(e.to_string()))?;
let hk = Hkdf::<Sha256>::new(Some(salt), root_key.as_ref());
Ok(Self {
k_tag: derive_subkey(&hk, INFO_TAG)?,
k_val: derive_subkey(&hk, INFO_VAL)?,
k_state: derive_subkey(&hk, INFO_STATE)?,
k_srch: derive_subkey(&hk, INFO_SRCH)?,
})
}
}
fn derive_subkey(hk: &Hkdf<Sha256>, info: &[u8]) -> Result<SecretKey, VaultError> {
let mut out = [0u8; LAMBDA];
hk.expand(info, &mut out)
.map_err(|_| VaultError::Kdf("HKDF expand failed".into()))?;
Ok(SecretKey::from_bytes(out))
}