use rand_core::{
block::{BlockRng, BlockRngCore},
CryptoRng, Error, RngCore, SeedableRng,
};
use crate::{
backend::{Core, BUFFER_SIZE},
rounds::{R12, R20, R8},
KEY_SIZE,
};
use core::convert::TryInto;
macro_rules! impl_chacha_rng {
($name:ident, $core:ident, $rounds:ident, $doc:expr) => {
#[doc = $doc]
#[cfg_attr(docsrs, doc(cfg(feature = "rng")))]
pub struct $name(BlockRng<$core>);
impl SeedableRng for $name {
type Seed = [u8; KEY_SIZE];
#[inline]
fn from_seed(seed: Self::Seed) -> Self {
let core = $core::from_seed(seed);
Self(BlockRng::new(core))
}
}
impl RngCore for $name {
#[inline]
fn next_u32(&mut self) -> u32 {
self.0.next_u32()
}
#[inline]
fn next_u64(&mut self) -> u64 {
self.0.next_u64()
}
#[inline]
fn fill_bytes(&mut self, bytes: &mut [u8]) {
self.0.fill_bytes(bytes)
}
#[inline]
fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> Result<(), Error> {
self.0.try_fill_bytes(bytes)
}
}
impl CryptoRng for $name {}
#[doc = "Core random number generator, for use with [`rand_core::block::BlockRng`]"]
#[cfg_attr(docsrs, doc(cfg(feature = "rng")))]
pub struct $core {
block: Core<$rounds>,
counter: u64,
}
impl SeedableRng for $core {
type Seed = [u8; KEY_SIZE];
#[inline]
fn from_seed(seed: Self::Seed) -> Self {
let block = Core::new(&seed, Default::default());
Self { block, counter: 0 }
}
}
impl BlockRngCore for $core {
type Item = u32;
type Results = [u32; BUFFER_SIZE / 4];
fn generate(&mut self, results: &mut Self::Results) {
assert!(
self.counter < u32::MAX as u64,
"maximum number of allowed ChaCha blocks exceeded"
);
let mut buffer = [0u8; BUFFER_SIZE];
self.block.generate(self.counter, &mut buffer);
for (n, chunk) in results.iter_mut().zip(buffer.chunks_exact(4)) {
*n = u32::from_le_bytes(chunk.try_into().unwrap());
}
self.counter += 1;
}
}
impl CryptoRng for $core {}
};
}
impl_chacha_rng!(
ChaCha8Rng,
ChaCha8RngCore,
R8,
"Random number generator over the ChaCha8 stream cipher."
);
impl_chacha_rng!(
ChaCha12Rng,
ChaCha12RngCore,
R12,
"Random number generator over the ChaCha12 stream cipher."
);
impl_chacha_rng!(
ChaCha20Rng,
ChaCha20RngCore,
R20,
"Random number generator over the ChaCha20 stream cipher."
);
#[cfg(test)]
mod tests {
use super::ChaCha20Rng;
use crate::KEY_SIZE;
use rand_core::{RngCore, SeedableRng};
const KEY: [u8; KEY_SIZE] = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31, 32,
];
#[test]
fn test_rng_output() {
let mut rng = ChaCha20Rng::from_seed(KEY);
let mut bytes = [0u8; 13];
rng.fill_bytes(&mut bytes);
assert_eq!(
bytes,
[177, 105, 126, 159, 198, 70, 30, 25, 131, 209, 49, 207, 105]
);
rng.fill_bytes(&mut bytes);
assert_eq!(
bytes,
[167, 163, 252, 19, 79, 20, 152, 128, 232, 187, 43, 93, 35]
);
}
}