use core::marker::PhantomData;
use super::hash::{Hash, HashOutput};
use super::hmac::Hmac;
use crate::Error;
use crate::mid::rng::RandomSource;
pub(crate) struct HmacDrbg<H: Hash> {
k: HashOutput,
v: HashOutput,
reseed_counter: usize,
_h: PhantomData<H>,
}
impl<H: Hash> HmacDrbg<H> {
pub(crate) fn new(entropy_input: &[u8], nonce: &[u8], personalization_string: &[u8]) -> Self {
let seed_material = &[entropy_input, nonce, personalization_string];
let k = H::zeroed_output();
let mut v = H::zeroed_output();
v.as_mut().fill(1u8);
let mut r = Self {
k,
v,
reseed_counter: 0,
_h: PhantomData,
};
r.update(seed_material);
r
}
fn update(&mut self, additional_input: &[&[u8]]) {
let mut ctx = Hmac::<H>::new(self.k.as_ref());
ctx.update(self.v.as_ref());
ctx.update([0u8]);
for item in additional_input {
ctx.update(item);
}
self.k = ctx.finish();
let mut ctx = Hmac::<H>::new(self.k.as_ref());
ctx.update(self.v.as_ref());
self.v = ctx.finish();
self.reseed_counter += 1;
if additional_input.is_empty() {
return;
}
let mut ctx = Hmac::<H>::new(self.k.as_ref());
ctx.update(self.v.as_ref());
ctx.update([1u8]);
for item in additional_input {
ctx.update(item);
}
self.k = ctx.finish();
let mut ctx = Hmac::<H>::new(self.k.as_ref());
ctx.update(self.v.as_ref());
self.v = ctx.finish();
}
fn generate_block(&mut self) {
let mut ctx = Hmac::<H>::new(self.k.as_ref());
ctx.update(self.v.as_ref());
self.v = ctx.finish();
}
}
impl<H: Hash> RandomSource for HmacDrbg<H> {
fn fill(&mut self, out: &mut [u8]) -> Result<(), Error> {
let hashlen = self.v.as_ref().len();
assert!(self.reseed_counter < 0x1_0000_0000_0000);
for chunk in out.chunks_mut(hashlen) {
self.generate_block();
chunk.copy_from_slice(&self.v.as_ref()[..chunk.len()]);
}
self.update(&[]);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::high::hash::Sha256;
#[test]
fn rfc6979_example() {
let x =
b"\x00\x9A\x4D\x67\x92\x29\x5A\x7F\x73\x0F\xC3\xF2\xB4\x9C\xBC\x0F\x62\xE8\x62\x27\x2F";
let h1 =
b"\x01\x79\x5E\xDF\x0D\x54\xDB\x76\x0F\x15\x6D\x0D\xAC\x04\xC0\x32\x2B\x3A\x20\x42\x24";
let mut ctx = HmacDrbg::<Sha256>::new(x, h1, &[]);
let mut t1 = [0u8; 32];
ctx.fill(&mut t1).unwrap();
assert_eq!(&t1, b"\x93\x05\xA4\x6D\xE7\xFF\x8E\xB1\x07\x19\x4D\xEB\xD3\xFD\x48\xAA\x20\xD5\xE7\x65\x6C\xBE\x0E\xA6\x9D\x2A\x8D\x4E\x7C\x67\x31\x4A");
let mut t2 = [0u8; 32];
ctx.fill(&mut t2).unwrap();
assert_eq!(&t2, b"\xC7\x0C\x78\x60\x8A\x3B\x5B\xE9\x28\x9B\xE9\x0E\xF6\xE8\x1A\x9E\x2C\x15\x16\xD5\x75\x1D\x2F\x75\xF5\x00\x33\xE4\x5F\x73\xBD\xEB");
}
}