use hmac::{Hmac, Mac};
use rand::RngCore;
use sha2::Sha256;
use zeroize::{Zeroize, ZeroizeOnDrop};
use crate::crypto::entropy_health::HealthMonitoredRng;
use crate::error::{HsmError, HsmResult};
type HmacSha256 = Hmac<Sha256>;
const RESEED_INTERVAL: u64 = 1u64 << 48;
#[derive(ZeroizeOnDrop)]
pub struct HmacDrbg {
#[zeroize(drop)]
key: [u8; 32],
#[zeroize(drop)]
value: [u8; 32],
reseed_counter: u64,
#[zeroize(drop)]
last_output: [u8; 32],
has_output: bool,
}
impl std::fmt::Debug for HmacDrbg {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HmacDrbg")
.field("reseed_counter", &self.reseed_counter)
.field("has_output", &self.has_output)
.finish()
}
}
impl HmacDrbg {
pub fn new() -> HsmResult<Self> {
let mut drbg = Self {
key: [0u8; 32],
value: [0x01u8; 32],
reseed_counter: 0,
last_output: [0u8; 32],
has_output: false,
};
let mut seed = [0u8; 48];
HealthMonitoredRng::new().fill_bytes(&mut seed);
drbg.update(Some(&seed));
seed.zeroize();
drbg.reseed_counter = 1;
Ok(drbg)
}
#[cfg(test)]
pub fn new_deterministic(entropy: &[u8], nonce: &[u8]) -> Self {
let mut drbg = Self {
key: [0u8; 32],
value: [0x01u8; 32],
reseed_counter: 0,
last_output: [0u8; 32],
has_output: false,
};
let mut seed_material = Vec::new();
seed_material.extend_from_slice(entropy);
seed_material.extend_from_slice(nonce);
drbg.update(Some(&seed_material));
seed_material.zeroize();
drbg.reseed_counter = 1;
drbg
}
fn update(&mut self, provided_data: Option<&[u8]>) {
let mut mac =
HmacSha256::new_from_slice(&self.key).expect("HMAC key length is always valid");
mac.update(&self.value);
mac.update(&[0x00]);
if let Some(data) = provided_data {
mac.update(data);
}
let result = mac.finalize().into_bytes();
self.key.copy_from_slice(&result);
let mut mac =
HmacSha256::new_from_slice(&self.key).expect("HMAC key length is always valid");
mac.update(&self.value);
let result = mac.finalize().into_bytes();
self.value.copy_from_slice(&result);
if let Some(data) = provided_data {
if !data.is_empty() {
let mut mac =
HmacSha256::new_from_slice(&self.key).expect("HMAC key length is always valid");
mac.update(&self.value);
mac.update(&[0x01]);
mac.update(data);
let result = mac.finalize().into_bytes();
self.key.copy_from_slice(&result);
let mut mac =
HmacSha256::new_from_slice(&self.key).expect("HMAC key length is always valid");
mac.update(&self.value);
let result = mac.finalize().into_bytes();
self.value.copy_from_slice(&result);
}
}
}
pub fn reseed(&mut self) -> HsmResult<()> {
let mut entropy = [0u8; 32];
HealthMonitoredRng::new().fill_bytes(&mut entropy);
self.update(Some(&entropy));
entropy.zeroize();
self.reseed_counter = 1;
Ok(())
}
#[cfg(test)]
pub fn reseed_deterministic(&mut self, entropy: &[u8]) {
self.update(Some(entropy));
self.reseed_counter = 1;
}
pub fn generate(&mut self, out: &mut [u8]) -> HsmResult<()> {
if self.reseed_counter > RESEED_INTERVAL {
self.reseed()?;
}
self.reseed()?;
let mut pos = 0;
while pos < out.len() {
let mut mac =
HmacSha256::new_from_slice(&self.key).expect("HMAC key length is always valid");
mac.update(&self.value);
let result = mac.finalize().into_bytes();
self.value.copy_from_slice(&result);
let current_block = zeroize::Zeroizing::new(self.value);
if self.has_output {
self.health_check(current_block.as_slice())?;
}
self.last_output.copy_from_slice(current_block.as_slice());
self.has_output = true;
let remaining = out.len() - pos;
let copy_len = remaining.min(32);
out[pos..pos + copy_len].copy_from_slice(&self.value[..copy_len]);
pos += copy_len;
}
self.update(None);
self.reseed_counter += 1;
Ok(())
}
fn health_check(&self, current: &[u8]) -> HsmResult<()> {
if current == self.last_output.as_slice() {
return Err(HsmError::GeneralError);
}
Ok(())
}
}
pub struct DrbgRng {
drbg: HmacDrbg,
}
impl DrbgRng {
pub fn new() -> HsmResult<Self> {
Ok(Self {
drbg: HmacDrbg::new()?,
})
}
}
impl rand::RngCore for DrbgRng {
fn next_u32(&mut self) -> u32 {
let mut buf = [0u8; 4];
self.fill_bytes(&mut buf);
u32::from_le_bytes(buf)
}
fn next_u64(&mut self) -> u64 {
let mut buf = [0u8; 8];
self.fill_bytes(&mut buf);
u64::from_le_bytes(buf)
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
if let Err(e) = self.drbg.generate(dest) {
dest.fill(0);
tracing::error!(
"DRBG catastrophic failure in fill_bytes: {:?} — \
cannot produce safe randomness, aborting to prevent \
weak key generation or signature forgery",
e
);
std::process::abort();
}
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
self.drbg
.generate(dest)
.map_err(|_| rand::Error::new("DRBG generate failed"))
}
}
impl rand::CryptoRng for DrbgRng {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_drbg_basic() {
let mut drbg = HmacDrbg::new().unwrap();
let mut buf1 = [0u8; 32];
let mut buf2 = [0u8; 32];
drbg.generate(&mut buf1).unwrap();
drbg.generate(&mut buf2).unwrap();
assert_ne!(buf1, buf2, "Consecutive DRBG outputs should differ");
assert_ne!(buf1, [0u8; 32], "DRBG output should not be all zeros");
}
#[test]
fn test_drbg_various_lengths() {
let mut drbg = HmacDrbg::new().unwrap();
for &len in &[4, 16, 32, 48, 64, 100, 256] {
let mut buf = vec![0u8; len];
drbg.generate(&mut buf).unwrap();
assert_ne!(
buf,
vec![0u8; len],
"DRBG output of {} bytes should not be all zeros",
len
);
}
let mut one = vec![0u8; 1];
drbg.generate(&mut one).unwrap();
}
#[test]
fn test_drbg_reseed() {
let mut drbg = HmacDrbg::new().unwrap();
let mut buf1 = [0u8; 32];
drbg.generate(&mut buf1).unwrap();
drbg.reseed().unwrap();
let mut buf2 = [0u8; 32];
drbg.generate(&mut buf2).unwrap();
assert_ne!(buf1, buf2, "Output after reseed should differ");
}
#[test]
fn test_drbg_deterministic() {
let entropy = [0x42u8; 32];
let nonce = [0x13u8; 16];
let drbg1 = HmacDrbg::new_deterministic(&entropy, &nonce);
let drbg2 = HmacDrbg::new_deterministic(&entropy, &nonce);
assert_eq!(drbg1.key, drbg2.key, "Deterministic DRBG keys should match");
assert_eq!(
drbg1.value, drbg2.value,
"Deterministic DRBG values should match"
);
}
}