use cryptography::BlockCipher;
use super::Rng;
pub struct BlockCtrRng<C: BlockCipher> {
cipher: C,
counter: u128,
buf: Vec<u8>, pos: usize,
}
impl<C: BlockCipher> BlockCtrRng<C> {
pub fn new(cipher: C, counter: u128) -> Self {
let block_len = C::BLOCK_LEN;
assert!(
block_len >= 4,
"BlockCtrRng requires BLOCK_LEN >= 4, got {block_len}"
);
Self {
cipher,
counter,
buf: vec![0u8; block_len],
pos: block_len,
}
}
fn refill(&mut self) {
let block_len = C::BLOCK_LEN;
let ctr_bytes = self.counter.to_be_bytes(); let copy_len = block_len.min(16);
for b in &mut self.buf {
*b = 0;
}
self.buf[block_len - copy_len..].copy_from_slice(&ctr_bytes[16 - copy_len..]);
self.cipher.encrypt(&mut self.buf);
self.counter = self.counter.wrapping_add(1);
self.pos = 0;
}
}
impl<C: BlockCipher> Rng for BlockCtrRng<C> {
fn next_u32(&mut self) -> u32 {
if self.pos + 4 > self.buf.len() {
self.refill();
}
let w = u32::from_le_bytes(self.buf[self.pos..self.pos + 4].try_into().unwrap());
self.pos += 4;
w
}
}
#[cfg(test)]
mod tests {
use super::*;
use cryptography::Aes128;
#[test]
fn block_ctr_rng_kat_fips197() {
let key = [0u8; 16];
let cipher = Aes128::new(&key);
let mut rng = BlockCtrRng::new(cipher, 0);
assert_eq!(
rng.next_u32(),
0xd44b_e966,
"First u32 must match FIPS 197 AES-128 KAT: AES(key=0, ctr=0)[0..4] LE"
);
}
#[test]
fn block_ctr_rng_advances_across_blocks() {
let key = [0u8; 16];
let cipher = Aes128::new(&key);
let mut rng = BlockCtrRng::new(cipher, 0);
let block0: Vec<u32> = (0..4).map(|_| rng.next_u32()).collect();
let block1: Vec<u32> = (0..4).map(|_| rng.next_u32()).collect();
assert_ne!(
block0, block1,
"consecutive CTR blocks must differ"
);
assert_eq!(block0[0], 0xd44b_e966);
}
}