use cryptography::Sha256;
use super::{OsRng, Rng};
const SEEDLEN: usize = 55; const OUTLEN: usize = 32; const GENERATE_BLOCKS: usize = 8;
const GENERATE_SIZE: usize = OUTLEN * GENERATE_BLOCKS;
pub struct HashDrbg {
v: [u8; SEEDLEN],
c: [u8; SEEDLEN],
reseed_counter: u64,
buf: [u8; GENERATE_SIZE], offset: usize,
}
impl HashDrbg {
#[must_use]
pub fn from_os_rng() -> Self {
let mut os = OsRng::new();
let mut seed = [0u8; SEEDLEN + 16];
for chunk in seed.chunks_exact_mut(4) {
chunk.copy_from_slice(&os.next_u32().to_le_bytes());
}
let v = hash_df(&seed, SEEDLEN);
let c = {
let mut input = [0u8; 1 + SEEDLEN];
input[0] = 0x00;
input[1..].copy_from_slice(&v);
hash_df(&input, SEEDLEN)
};
Self {
v,
c,
reseed_counter: 1,
buf: [0u8; GENERATE_SIZE],
offset: GENERATE_SIZE, }
}
fn refill(&mut self) {
assert!(
self.reseed_counter < (1u64 << 48),
"Hash_DRBG: reseed interval (2⁴⁸) exceeded (SP 800-90A §10.1.1 Table 2)"
);
let mut data = self.v;
for i in 0..GENERATE_BLOCKS {
let block = Sha256::digest(&data);
self.buf[i * OUTLEN..(i + 1) * OUTLEN].copy_from_slice(&block);
add1_mod2seedlen(&mut data);
}
self.offset = 0;
self.finalise_generate();
}
fn finalise_generate(&mut self) {
let h = {
let mut input = [0u8; 1 + SEEDLEN];
input[0] = 0x03;
input[1..].copy_from_slice(&self.v);
Sha256::digest(&input)
};
add_bytes_mod(&mut self.v, &h);
add_bytes_mod(&mut self.v, &self.c.clone());
add_u64_mod(&mut self.v, self.reseed_counter);
self.reseed_counter = self.reseed_counter.wrapping_add(1);
}
fn take_bytes<const N: usize>(&mut self) -> [u8; N] {
const { assert!(N <= OUTLEN, "chunk larger than SHA-256 output") }
if self.offset + N > GENERATE_SIZE {
self.refill();
}
let out = self.buf[self.offset..self.offset + N].try_into().unwrap();
self.offset += N;
out
}
}
fn hash_df(input: &[u8], out_bytes: usize) -> [u8; SEEDLEN] {
let bits = (out_bytes * 8) as u32;
let num_blocks = out_bytes.div_ceil(OUTLEN);
let mut temp = [0u8; OUTLEN * 2]; for i in 0..num_blocks {
let counter = (i + 1) as u8;
let mut msg = Vec::with_capacity(5 + input.len());
msg.push(counter);
msg.extend_from_slice(&bits.to_be_bytes());
msg.extend_from_slice(input);
let block = Sha256::digest(&msg);
let start = i * OUTLEN;
let end = start + OUTLEN;
temp[start..end].copy_from_slice(&block);
}
let mut out = [0u8; SEEDLEN];
out.copy_from_slice(&temp[..SEEDLEN]);
out
}
fn add1_mod2seedlen(data: &mut [u8; SEEDLEN]) {
let mut carry = 1u16;
for b in data.iter_mut().rev() {
carry += *b as u16;
*b = carry as u8;
carry >>= 8;
}
}
fn add_bytes_mod(data: &mut [u8; SEEDLEN], addend: &[u8]) {
let mut carry = 0u16;
let data_len = data.len();
let add_len = addend.len();
for i in (0..data_len).rev() {
let add_byte = if i + add_len >= data_len {
addend[i + add_len - data_len]
} else {
0
};
carry += data[i] as u16 + add_byte as u16;
data[i] = carry as u8;
carry >>= 8;
}
}
fn add_u64_mod(data: &mut [u8; SEEDLEN], n: u64) {
let bytes = n.to_be_bytes();
add_bytes_mod(data, &bytes);
}
impl Default for HashDrbg {
fn default() -> Self {
Self::from_os_rng()
}
}
impl Rng for HashDrbg {
fn next_u32(&mut self) -> u32 {
u32::from_le_bytes(self.take_bytes::<4>())
}
fn next_u64(&mut self) -> u64 {
u64::from_le_bytes(self.take_bytes::<8>())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hash_drbg_nonzero() {
let mut rng = HashDrbg::from_os_rng();
let v: u64 = (0..8).map(|_| rng.next_u64()).fold(0, |a, b| a | b);
assert_ne!(v, 0);
}
#[test]
fn hash_drbg_advances() {
let mut rng = HashDrbg::from_os_rng();
let v0 = rng.next_u64();
let v1 = rng.next_u64();
assert_ne!(v0, v1);
}
#[test]
fn hash_df_length() {
let out = hash_df(b"test input", SEEDLEN);
assert_eq!(out.len(), SEEDLEN);
}
#[test]
fn add1_wraps() {
let mut v = [0xffu8; SEEDLEN];
add1_mod2seedlen(&mut v);
assert!(v.iter().all(|&b| b == 0)); }
}