use crate::{error::KdfError, AttrId, Error, KdfAttrView, KdfView, Multikey, Views};
use multi_codec::Codec;
use multi_util::Varuint;
use zeroize::Zeroizing;
pub const SALT_LENGTH: usize = 32;
pub(crate) struct View<'a> {
mk: &'a Multikey,
kdf: Option<&'a Multikey>,
}
impl<'a> View<'a> {
pub fn new(mk: &'a Multikey, kdf: &'a Multikey) -> Self {
Self { mk, kdf: Some(kdf) }
}
}
impl<'a> TryFrom<&'a Multikey> for View<'a> {
type Error = Error;
fn try_from(mk: &'a Multikey) -> Result<Self, Self::Error> {
Ok(Self { mk, kdf: None })
}
}
impl<'a> KdfView for View<'a> {
fn derive_key(&self, passphrase: &[u8]) -> Result<Multikey, Error> {
let kdf = self.kdf.ok_or(KdfError::MissingCodec)?;
let (salt, rounds) = {
let kattr = kdf.kdf_attr_view()?;
(kattr.salt_bytes()?, kattr.rounds()?)
};
let key_length = {
let cattr = self.mk.cipher_attr_view()?;
cattr.key_length()?
};
let mut key: Zeroizing<Vec<u8>> = vec![0; key_length].into();
bcrypt_pbkdf::bcrypt_pbkdf(passphrase, &salt, rounds as u32, key.as_mut_slice())
.map_err(KdfError::Bcrypt)?;
let kdf_codec: Vec<u8> = kdf.codec.into();
let rounds: Vec<u8> = Varuint(rounds).into();
let mut res = self.mk.clone();
res.attributes.insert(AttrId::KeyData, key.clone());
let _ = res.attributes.remove(&AttrId::KeyIsEncrypted);
res.attributes.insert(AttrId::KdfCodec, kdf_codec.into());
res.attributes.insert(AttrId::KdfSalt, salt.clone());
res.attributes.insert(AttrId::KdfRounds, rounds.into());
Ok(res)
}
}
impl<'a> KdfAttrView for View<'a> {
fn kdf_codec(&self) -> Result<Codec, Error> {
Ok(Codec::BcryptPbkdf)
}
fn salt_bytes(&self) -> Result<Zeroizing<Vec<u8>>, Error> {
let salt = self
.mk
.attributes
.get(&AttrId::KdfSalt)
.ok_or(KdfError::MissingSalt)?;
if salt.len() != SALT_LENGTH {
Err(KdfError::InvalidSaltLen.into())
} else {
Ok(salt.clone())
}
}
fn rounds(&self) -> Result<usize, Error> {
let rounds = self
.mk
.attributes
.get(&AttrId::KdfRounds)
.ok_or(KdfError::MissingRounds)?;
Ok(Varuint::<usize>::try_from(rounds.as_slice())?.to_inner())
}
}