use crate::{digest, error, hmac};
#[derive(Debug)]
pub struct Salt(hmac::Key);
impl Salt {
pub fn new(digest_algorithm: &'static digest::Algorithm, value: &[u8]) -> Self {
Salt(hmac::Key::new(digest_algorithm, value))
}
pub fn extract(&self, secret: &[u8]) -> Prk {
let salt = &self.0;
let prk = hmac::sign(salt, secret);
Prk(hmac::Key::new(salt.digest_algorithm(), prk.as_ref()))
}
}
#[derive(Debug)]
pub struct Prk(hmac::Key);
impl Prk {
#[inline]
pub fn expand<'a>(&'a self, info: &'a [u8]) -> Okm<'a> {
Okm { prk: self, info }
}
}
#[derive(Debug)]
pub struct Okm<'a> {
prk: &'a Prk,
info: &'a [u8],
}
impl Okm<'_> {
pub fn fill(self, out: &mut [u8]) -> Result<(), error::Unspecified> {
let digest_alg = self.prk.0.digest_algorithm();
assert!(digest_alg.block_len >= digest_alg.output_len);
let mut ctx = hmac::Context::with_key(&self.prk.0);
let mut n = 1u8;
let mut out = out;
loop {
ctx.update(self.info);
ctx.update(&[n]);
let t = ctx.sign();
let t = t.as_ref();
out = if out.len() < digest_alg.output_len {
let len = out.len();
out.copy_from_slice(&t[..len]);
&mut []
} else {
let (this_chunk, rest) = out.split_at_mut(digest_alg.output_len);
this_chunk.copy_from_slice(t);
rest
};
if out.is_empty() {
return Ok(());
}
ctx = hmac::Context::with_key(&self.prk.0);
ctx.update(t);
n = n.checked_add(1).ok_or(error::Unspecified)?;
}
}
}
#[deprecated(note = "Will be removed in the next release.")]
pub fn extract_and_expand(salt: &Salt, secret: &[u8], info: &[u8], out: &mut [u8]) {
salt.extract(secret).expand(info).fill(out).unwrap()
}