#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::vec;
#[cfg(feature = "random")]
use lib_q_random::traits::EntropySource;
use rand_core::{
TryCryptoRng,
TryRng,
};
pub struct Shake256KatPrng {
xof: crate::internal::shake256::Shake256Xof,
}
impl Shake256KatPrng {
pub fn new(seed: &[u8; 48]) -> Self {
let mut xof = crate::internal::shake256::Shake256Xof::new();
xof.absorb(seed).expect("SHAKE256 absorb seed failed");
xof.absorb(&[])
.expect("SHAKE256 absorb personalization failed");
const HQC_PRNG_DOMAIN: u8 = 0;
xof.absorb(&[HQC_PRNG_DOMAIN])
.expect("SHAKE256 absorb domain failed");
xof.finalize_absorb().expect("SHAKE256 finalize failed");
Self { xof }
}
pub fn skip(&mut self, count: usize) {
let mut tmp = vec![0u8; count];
self.xof.squeeze(&mut tmp).expect("SHAKE256 squeeze failed");
}
}
impl TryRng for Shake256KatPrng {
type Error = core::convert::Infallible;
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
let mut bytes = [0u8; 4];
self.try_fill_bytes(&mut bytes)?;
Ok(u32::from_le_bytes(bytes))
}
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
let mut bytes = [0u8; 8];
self.try_fill_bytes(&mut bytes)?;
Ok(u64::from_le_bytes(bytes))
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
self.xof.squeeze(dest).expect("SHAKE256 squeeze failed");
Ok(())
}
}
impl TryCryptoRng for Shake256KatPrng {}
pub struct KatPrng {
entropy_input: [u8; 48],
bytes_consumed: usize,
}
impl KatPrng {
pub fn new(entropy_input: [u8; 48]) -> Self {
Self {
entropy_input,
bytes_consumed: 0,
}
}
}
#[cfg(feature = "random")]
impl EntropySource for KatPrng {
fn get_entropy(&mut self, dest: &mut [u8]) -> lib_q_random::Result<()> {
let remaining = self.entropy_input.len() - self.bytes_consumed;
let to_copy = dest.len().min(remaining);
if to_copy > 0 {
dest[..to_copy].copy_from_slice(
&self.entropy_input[self.bytes_consumed..self.bytes_consumed + to_copy],
);
self.bytes_consumed += to_copy;
}
if to_copy < dest.len() {
dest[to_copy..].fill(0);
}
Ok(())
}
}
impl TryRng for KatPrng {
type Error = core::convert::Infallible;
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
let mut bytes = [0u8; 4];
self.try_fill_bytes(&mut bytes)?;
Ok(u32::from_le_bytes(bytes))
}
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
let mut bytes = [0u8; 8];
self.try_fill_bytes(&mut bytes)?;
Ok(u64::from_le_bytes(bytes))
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
#[cfg(feature = "random")]
{
self.get_entropy(dest)
.expect("KAT PRNG entropy generation failed");
}
#[cfg(not(feature = "random"))]
{
let remaining = self.entropy_input.len() - self.bytes_consumed;
let to_copy = dest.len().min(remaining);
if to_copy > 0 {
dest[..to_copy].copy_from_slice(
&self.entropy_input[self.bytes_consumed..self.bytes_consumed + to_copy],
);
self.bytes_consumed += to_copy;
}
if to_copy < dest.len() {
dest[to_copy..].fill(0);
}
}
Ok(())
}
}
impl TryCryptoRng for KatPrng {}
pub fn create_kat_prng_rng(entropy_input: [u8; 48]) -> KatPrng {
KatPrng::new(entropy_input)
}
#[cfg(test)]
mod tests {
use rand_core::Rng;
use super::*;
#[test]
fn test_kat_prng_matches_reference() {
let seed = hex::decode("9EF877FDDBE8891C6E4E79EAF022E563DEFACA6B152161B9A423E8FE96A403E774B2D352CF74C934069C9DE74757F505").unwrap();
let mut entropy_input = [0u8; 48];
entropy_input.copy_from_slice(&seed);
let mut rng = create_kat_prng_rng(entropy_input);
let mut seed_kem = [0u8; 32];
rng.fill_bytes(&mut seed_kem);
let expected =
hex::decode("9ef877fddbe8891c6e4e79eaf022e563defaca6b152161b9a423e8fe96a403e7")
.unwrap();
assert_eq!(
&seed_kem[..],
&expected[..],
"KAT PRNG output doesn't match reference implementation"
);
}
#[test]
fn test_kat_prng_deterministic() {
let seed = [0x42u8; 48];
let mut rng1 = create_kat_prng_rng(seed);
let mut rng2 = create_kat_prng_rng(seed);
let mut output1 = [0u8; 32];
let mut output2 = [0u8; 32];
rng1.fill_bytes(&mut output1);
rng2.fill_bytes(&mut output2);
assert_eq!(output1, output2, "KAT PRNG is not deterministic");
}
#[test]
fn test_kat_prng_exhausts_entropy() {
let seed = [0x42u8; 48];
let mut rng = create_kat_prng_rng(seed);
let mut first_32 = [0u8; 32];
rng.fill_bytes(&mut first_32);
assert_eq!(&first_32[..], &seed[..32]);
let mut next_16 = [0u8; 16];
rng.fill_bytes(&mut next_16);
assert_eq!(&next_16[..], &seed[32..48]);
let mut zeros = [0u8; 32];
rng.fill_bytes(&mut zeros);
assert_eq!(zeros, [0u8; 32]);
}
}