use oxicrypto::{argon2id_derive, new_rng, Argon2Params};
use crate::error::EncryptError;
#[derive(Clone)]
pub struct KeyVersion {
pub version: u32,
pub kek: [u8; 32],
}
impl core::fmt::Debug for KeyVersion {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("KeyVersion")
.field("version", &self.version)
.field("kek", &"[REDACTED]")
.finish()
}
}
#[derive(Clone, Debug)]
pub struct Keyring {
versions: Vec<KeyVersion>,
}
impl Keyring {
pub fn new(kek: [u8; 32]) -> Self {
Self {
versions: vec![KeyVersion { version: 1, kek }],
}
}
pub fn from_passphrase(passphrase: &[u8], salt: &[u8; 32]) -> Result<Self, EncryptError> {
Self::from_passphrase_with_params(
passphrase,
salt,
Argon2Params {
m_cost: 65_536,
t_cost: 3,
p_cost: 1,
},
)
}
pub fn from_passphrase_with_params(
passphrase: &[u8],
salt: &[u8; 32],
params: Argon2Params,
) -> Result<Self, EncryptError> {
let mut kek = [0u8; 32];
argon2id_derive(passphrase, salt.as_ref(), params, &mut kek)
.map_err(|e| EncryptError::KeyDerivationFailed(e.to_string()))?;
Ok(Self::new(kek))
}
pub fn active_version(&self) -> Result<u32, EncryptError> {
self.versions
.last()
.map(|kv| kv.version)
.ok_or(EncryptError::KeyringEmpty)
}
pub fn active_kek(&self) -> Result<&[u8; 32], EncryptError> {
self.versions
.last()
.map(|kv| &kv.kek)
.ok_or(EncryptError::KeyringEmpty)
}
pub fn kek_for_version(&self, version: u32) -> Option<&[u8; 32]> {
self.versions
.iter()
.find(|kv| kv.version == version)
.map(|kv| &kv.kek)
}
pub fn rotate(&mut self, new_kek: [u8; 32]) -> Result<u32, EncryptError> {
let next_version = self.active_version()? + 1;
self.versions.push(KeyVersion {
version: next_version,
kek: new_kek,
});
Ok(next_version)
}
pub fn version_numbers(&self) -> Vec<u32> {
self.versions.iter().map(|kv| kv.version).collect()
}
}
pub fn generate_salt() -> Result<[u8; 32], EncryptError> {
let mut rng = new_rng().map_err(|_| EncryptError::RngFailed)?;
let mut salt = [0u8; 32];
rng.fill(&mut salt).map_err(|_| EncryptError::RngFailed)?;
Ok(salt)
}
pub fn generate_dek() -> Result<[u8; 32], EncryptError> {
let mut rng = new_rng().map_err(|_| EncryptError::RngFailed)?;
let mut dek = [0u8; 32];
rng.fill(&mut dek).map_err(|_| EncryptError::RngFailed)?;
Ok(dek)
}