use core::mem::size_of_val;
use rand_core::block::{BlockRng, BlockRngCore, CryptoBlockRng};
use rand_core::{CryptoRng, RngCore, SeedableRng, TryCryptoRng, TryRngCore};
#[derive(Debug)]
pub struct ReseedingRng<R, Rsdr>(BlockRng<ReseedingCore<R, Rsdr>>)
where
R: BlockRngCore + SeedableRng,
Rsdr: TryRngCore;
impl<R, Rsdr> ReseedingRng<R, Rsdr>
where
R: BlockRngCore + SeedableRng,
Rsdr: TryRngCore,
{
pub fn new(threshold: u64, reseeder: Rsdr) -> Result<Self, Rsdr::Error> {
Ok(ReseedingRng(BlockRng::new(ReseedingCore::new(
threshold, reseeder,
)?)))
}
pub fn reseed(&mut self) -> Result<(), Rsdr::Error> {
self.0.reset();
self.0.core.reseed()
}
}
impl<R, Rsdr> RngCore for ReseedingRng<R, Rsdr>
where
R: BlockRngCore<Item = u32> + SeedableRng,
Rsdr: TryRngCore,
{
#[inline(always)]
fn next_u32(&mut self) -> u32 {
self.0.next_u32()
}
#[inline(always)]
fn next_u64(&mut self) -> u64 {
self.0.next_u64()
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.0.fill_bytes(dest)
}
}
impl<R, Rsdr> Clone for ReseedingRng<R, Rsdr>
where
R: BlockRngCore + SeedableRng + Clone,
Rsdr: TryRngCore + Clone,
{
fn clone(&self) -> ReseedingRng<R, Rsdr> {
ReseedingRng(BlockRng::new(self.0.core.clone()))
}
}
impl<R, Rsdr> CryptoRng for ReseedingRng<R, Rsdr>
where
R: BlockRngCore<Item = u32> + SeedableRng + CryptoBlockRng,
Rsdr: TryCryptoRng,
{
}
#[derive(Debug)]
struct ReseedingCore<R, Rsdr> {
inner: R,
reseeder: Rsdr,
threshold: i64,
bytes_until_reseed: i64,
}
impl<R, Rsdr> BlockRngCore for ReseedingCore<R, Rsdr>
where
R: BlockRngCore + SeedableRng,
Rsdr: TryRngCore,
{
type Item = <R as BlockRngCore>::Item;
type Results = <R as BlockRngCore>::Results;
fn generate(&mut self, results: &mut Self::Results) {
if self.bytes_until_reseed <= 0 {
return self.reseed_and_generate(results);
}
let num_bytes = size_of_val(results.as_ref());
self.bytes_until_reseed -= num_bytes as i64;
self.inner.generate(results);
}
}
impl<R, Rsdr> ReseedingCore<R, Rsdr>
where
R: BlockRngCore + SeedableRng,
Rsdr: TryRngCore,
{
fn new(threshold: u64, mut reseeder: Rsdr) -> Result<Self, Rsdr::Error> {
let threshold = if threshold == 0 {
i64::MAX
} else if threshold <= i64::MAX as u64 {
threshold as i64
} else {
i64::MAX
};
let inner = R::try_from_rng(&mut reseeder)?;
Ok(ReseedingCore {
inner,
reseeder,
threshold,
bytes_until_reseed: threshold,
})
}
fn reseed(&mut self) -> Result<(), Rsdr::Error> {
R::try_from_rng(&mut self.reseeder).map(|result| {
self.bytes_until_reseed = self.threshold;
self.inner = result
})
}
#[inline(never)]
fn reseed_and_generate(&mut self, results: &mut <Self as BlockRngCore>::Results) {
let num_bytes = size_of_val(results.as_ref());
if let Err(e) = self.reseed() {
panic!("Reseeding RNG failed: {e}");
}
self.bytes_until_reseed = self.threshold - num_bytes as i64;
self.inner.generate(results);
}
}
impl<R, Rsdr> Clone for ReseedingCore<R, Rsdr>
where
R: BlockRngCore + SeedableRng + Clone,
Rsdr: TryRngCore + Clone,
{
fn clone(&self) -> ReseedingCore<R, Rsdr> {
ReseedingCore {
inner: self.inner.clone(),
reseeder: self.reseeder.clone(),
threshold: self.threshold,
bytes_until_reseed: 0, }
}
}
impl<R, Rsdr> CryptoBlockRng for ReseedingCore<R, Rsdr>
where
R: BlockRngCore<Item = u32> + SeedableRng + CryptoBlockRng,
Rsdr: TryCryptoRng,
{
}
#[cfg(feature = "std_rng")]
#[cfg(test)]
mod test {
use crate::rngs::std::Core;
use crate::test::const_rng;
use crate::Rng;
use super::ReseedingRng;
#[test]
fn test_reseeding() {
let zero = const_rng(0);
let thresh = 1; let mut reseeding = ReseedingRng::<Core, _>::new(thresh, zero).unwrap();
let mut buf = ([0u32; 32], [0u32; 32]);
reseeding.fill(&mut buf.0);
reseeding.fill(&mut buf.1);
let seq = buf;
for _ in 0..10 {
reseeding.fill(&mut buf.0);
reseeding.fill(&mut buf.1);
assert_eq!(buf, seq);
}
}
#[test]
#[allow(clippy::redundant_clone)]
fn test_clone_reseeding() {
let zero = const_rng(0);
let mut rng1 = ReseedingRng::<Core, _>::new(32 * 4, zero).unwrap();
let first: u32 = rng1.random();
for _ in 0..10 {
let _ = rng1.random::<u32>();
}
let mut rng2 = rng1.clone();
assert_eq!(first, rng2.random::<u32>());
}
}