use crate::primitives::hash::{sha256_hmac, sha512_hmac};
#[derive(Clone)]
pub struct HmacDrbg {
k: Vec<u8>,
v: Vec<u8>,
hash_len: usize,
use_sha512: bool,
}
impl HmacDrbg {
pub fn new(entropy: &[u8], nonce: &[u8], personalization: &[u8]) -> Self {
Self::new_with_hash(entropy, nonce, personalization, false)
}
pub fn new_with_hash(
entropy: &[u8],
nonce: &[u8],
personalization: &[u8],
use_sha512: bool,
) -> Self {
let hash_len = if use_sha512 { 64 } else { 32 };
let k = vec![0x00u8; hash_len];
let v = vec![0x01u8; hash_len];
let mut drbg = Self {
k,
v,
hash_len,
use_sha512,
};
let mut seed_material =
Vec::with_capacity(entropy.len() + nonce.len() + personalization.len());
seed_material.extend_from_slice(entropy);
seed_material.extend_from_slice(nonce);
seed_material.extend_from_slice(personalization);
drbg.update(Some(&seed_material));
drbg
}
fn update(&mut self, provided_data: Option<&[u8]>) {
let mut input =
Vec::with_capacity(self.hash_len + 1 + provided_data.map_or(0, |d| d.len()));
input.extend_from_slice(&self.v);
input.push(0x00);
if let Some(data) = provided_data {
input.extend_from_slice(data);
}
self.k = self.hmac(&self.k, &input);
self.v = self.hmac(&self.k, &self.v);
if let Some(data) = provided_data {
if !data.is_empty() {
let mut input = Vec::with_capacity(self.hash_len + 1 + data.len());
input.extend_from_slice(&self.v);
input.push(0x01);
input.extend_from_slice(data);
self.k = self.hmac(&self.k, &input);
self.v = self.hmac(&self.k, &self.v);
}
}
}
pub fn generate(&mut self, num_bytes: usize) -> Vec<u8> {
let mut output = Vec::with_capacity(num_bytes);
while output.len() < num_bytes {
self.v = self.hmac(&self.k, &self.v);
output.extend_from_slice(&self.v);
}
output.truncate(num_bytes);
self.update(None);
output
}
pub fn reseed(&mut self, entropy: &[u8], additional_input: &[u8]) {
let mut seed_material = Vec::with_capacity(entropy.len() + additional_input.len());
seed_material.extend_from_slice(entropy);
seed_material.extend_from_slice(additional_input);
self.update(Some(&seed_material));
}
fn hmac(&self, key: &[u8], data: &[u8]) -> Vec<u8> {
if self.use_sha512 {
sha512_hmac(key, data).to_vec()
} else {
sha256_hmac(key, data).to_vec()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_drbg_deterministic() {
let entropy = b"test entropy value for testing";
let nonce = b"nonce value";
let mut drbg1 = HmacDrbg::new(entropy, nonce, &[]);
let mut drbg2 = HmacDrbg::new(entropy, nonce, &[]);
assert_eq!(drbg1.generate(64), drbg2.generate(64));
}
#[test]
fn test_drbg_state_advances() {
let mut drbg = HmacDrbg::new(b"entropy", b"nonce", &[]);
let output1 = drbg.generate(32);
let output2 = drbg.generate(32);
assert_ne!(output1, output2);
}
#[test]
fn test_drbg_personalization_affects_output() {
let entropy = b"entropy";
let nonce = b"nonce";
let mut drbg1 = HmacDrbg::new(entropy, nonce, b"personalization1");
let mut drbg2 = HmacDrbg::new(entropy, nonce, b"personalization2");
assert_ne!(drbg1.generate(32), drbg2.generate(32));
}
#[test]
fn test_drbg_reseed() {
let mut drbg1 = HmacDrbg::new(b"entropy", b"nonce", &[]);
let mut drbg2 = HmacDrbg::new(b"entropy", b"nonce", &[]);
let _ = drbg1.generate(32);
let _ = drbg2.generate(32);
drbg1.reseed(b"new entropy", &[]);
assert_ne!(drbg1.generate(32), drbg2.generate(32));
}
}