diff options
Diffstat (limited to 'rand/src/rngs/adapter/reseeding.rs')
-rw-r--r-- | rand/src/rngs/adapter/reseeding.rs | 357 |
1 files changed, 0 insertions, 357 deletions
diff --git a/rand/src/rngs/adapter/reseeding.rs b/rand/src/rngs/adapter/reseeding.rs deleted file mode 100644 index ec88efe..0000000 --- a/rand/src/rngs/adapter/reseeding.rs +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// Copyright 2013 The Rust Project Developers. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A wrapper around another PRNG that reseeds it after it -//! generates a certain number of random bytes. - -use core::mem::size_of; - -use rand_core::{RngCore, CryptoRng, SeedableRng, Error}; -use rand_core::block::{BlockRngCore, BlockRng}; - -/// A wrapper around any PRNG that implements [`BlockRngCore`], that adds the -/// ability to reseed it. -/// -/// `ReseedingRng` reseeds the underlying PRNG in the following cases: -/// -/// - On a manual call to [`reseed()`]. -/// - After `clone()`, the clone will be reseeded on first use. -/// - After a process is forked, the RNG in the child process is reseeded within -/// the next few generated values, depending on the block size of the -/// underlying PRNG. For ChaCha and Hc128 this is a maximum of -/// 15 `u32` values before reseeding. -/// - After the PRNG has generated a configurable number of random bytes. -/// -/// # When should reseeding after a fixed number of generated bytes be used? -/// -/// Reseeding after a fixed number of generated bytes is never strictly -/// *necessary*. Cryptographic PRNGs don't have a limited number of bytes they -/// can output, or at least not a limit reachable in any practical way. There is -/// no such thing as 'running out of entropy'. -/// -/// Occasionally reseeding can be seen as some form of 'security in depth'. Even -/// if in the future a cryptographic weakness is found in the CSPRNG being used, -/// or a flaw in the implementation, occasionally reseeding should make -/// exploiting it much more difficult or even impossible. -/// -/// Use [`ReseedingRng::new`] with a `threshold` of `0` to disable reseeding -/// after a fixed number of generated bytes. -/// -/// # Error handling -/// -/// Although unlikely, reseeding the wrapped PRNG can fail. `ReseedingRng` will -/// never panic but try to handle the error intelligently through some -/// combination of retrying and delaying reseeding until later. -/// If handling the source error fails `ReseedingRng` will continue generating -/// data from the wrapped PRNG without reseeding. -/// -/// Manually calling [`reseed()`] will not have this retry or delay logic, but -/// reports the error. -/// -/// # Example -/// -/// ``` -/// use rand::prelude::*; -/// use rand_chacha::ChaCha20Core; // Internal part of ChaChaRng that -/// // implements BlockRngCore -/// use rand::rngs::OsRng; -/// use rand::rngs::adapter::ReseedingRng; -/// -/// let prng = ChaCha20Core::from_entropy(); -/// let mut reseeding_rng = ReseedingRng::new(prng, 0, OsRng); -/// -/// println!("{}", reseeding_rng.gen::<u64>()); -/// -/// let mut cloned_rng = reseeding_rng.clone(); -/// assert!(reseeding_rng.gen::<u64>() != cloned_rng.gen::<u64>()); -/// ``` -/// -/// [`BlockRngCore`]: rand_core::block::BlockRngCore -/// [`ReseedingRng::new`]: ReseedingRng::new -/// [`reseed()`]: ReseedingRng::reseed -#[derive(Debug)] -pub struct ReseedingRng<R, Rsdr>(BlockRng<ReseedingCore<R, Rsdr>>) -where R: BlockRngCore + SeedableRng, - Rsdr: RngCore; - -impl<R, Rsdr> ReseedingRng<R, Rsdr> -where R: BlockRngCore + SeedableRng, - Rsdr: RngCore -{ - /// Create a new `ReseedingRng` from an existing PRNG, combined with a RNG - /// to use as reseeder. - /// - /// `threshold` sets the number of generated bytes after which to reseed the - /// PRNG. Set it to zero to never reseed based on the number of generated - /// values. - pub fn new(rng: R, threshold: u64, reseeder: Rsdr) -> Self { - ReseedingRng(BlockRng::new(ReseedingCore::new(rng, threshold, reseeder))) - } - - /// Reseed the internal PRNG. - pub fn reseed(&mut self) -> Result<(), Error> { - self.0.core.reseed() - } -} - -// TODO: this should be implemented for any type where the inner type -// implements RngCore, but we can't specify that because ReseedingCore is private -impl<R, Rsdr: RngCore> RngCore for ReseedingRng<R, Rsdr> -where R: BlockRngCore<Item = u32> + SeedableRng, - <R as BlockRngCore>::Results: AsRef<[u32]> + AsMut<[u32]> -{ - #[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) - } - - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -impl<R, Rsdr> Clone for ReseedingRng<R, Rsdr> -where R: BlockRngCore + SeedableRng + Clone, - Rsdr: RngCore + Clone -{ - fn clone(&self) -> ReseedingRng<R, Rsdr> { - // Recreating `BlockRng` seems easier than cloning it and resetting - // the index. - ReseedingRng(BlockRng::new(self.0.core.clone())) - } -} - -impl<R, Rsdr> CryptoRng for ReseedingRng<R, Rsdr> -where R: BlockRngCore + SeedableRng + CryptoRng, - Rsdr: RngCore + CryptoRng {} - -#[derive(Debug)] -struct ReseedingCore<R, Rsdr> { - inner: R, - reseeder: Rsdr, - threshold: i64, - bytes_until_reseed: i64, - fork_counter: usize, -} - -impl<R, Rsdr> BlockRngCore for ReseedingCore<R, Rsdr> -where R: BlockRngCore + SeedableRng, - Rsdr: RngCore -{ - type Item = <R as BlockRngCore>::Item; - type Results = <R as BlockRngCore>::Results; - - fn generate(&mut self, results: &mut Self::Results) { - let global_fork_counter = fork::get_fork_counter(); - if self.bytes_until_reseed <= 0 || - self.is_forked(global_fork_counter) { - // We get better performance by not calling only `reseed` here - // and continuing with the rest of the function, but by directly - // returning from a non-inlined function. - return self.reseed_and_generate(results, global_fork_counter); - } - let num_bytes = results.as_ref().len() * size_of::<Self::Item>(); - self.bytes_until_reseed -= num_bytes as i64; - self.inner.generate(results); - } -} - -impl<R, Rsdr> ReseedingCore<R, Rsdr> -where R: BlockRngCore + SeedableRng, - Rsdr: RngCore -{ - /// Create a new `ReseedingCore`. - fn new(rng: R, threshold: u64, reseeder: Rsdr) -> Self { - use ::core::i64::MAX; - fork::register_fork_handler(); - - // Because generating more values than `i64::MAX` takes centuries on - // current hardware, we just clamp to that value. - // Also we set a threshold of 0, which indicates no limit, to that - // value. - let threshold = - if threshold == 0 { MAX } - else if threshold <= MAX as u64 { threshold as i64 } - else { MAX }; - - ReseedingCore { - inner: rng, - reseeder, - threshold: threshold as i64, - bytes_until_reseed: threshold as i64, - fork_counter: 0, - } - } - - /// Reseed the internal PRNG. - fn reseed(&mut self) -> Result<(), Error> { - R::from_rng(&mut self.reseeder).map(|result| { - self.bytes_until_reseed = self.threshold; - self.inner = result - }) - } - - fn is_forked(&self, global_fork_counter: usize) -> bool { - // In theory, on 32-bit platforms, it is possible for - // `global_fork_counter` to wrap around after ~4e9 forks. - // - // This check will detect a fork in the normal case where - // `fork_counter < global_fork_counter`, and also when the difference - // between both is greater than `isize::MAX` (wrapped around). - // - // It will still fail to detect a fork if there have been more than - // `isize::MAX` forks, without any reseed in between. Seems unlikely - // enough. - (self.fork_counter.wrapping_sub(global_fork_counter) as isize) < 0 - } - - #[inline(never)] - fn reseed_and_generate(&mut self, - results: &mut <Self as BlockRngCore>::Results, - global_fork_counter: usize) - { - #![allow(clippy::if_same_then_else)] // false positive - if self.is_forked(global_fork_counter) { - info!("Fork detected, reseeding RNG"); - } else { - trace!("Reseeding RNG (periodic reseed)"); - } - - let num_bytes = - results.as_ref().len() * size_of::<<R as BlockRngCore>::Item>(); - - if let Err(e) = self.reseed() { - warn!("Reseeding RNG failed: {}", e); - let _ = e; - } - self.fork_counter = global_fork_counter; - - 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: RngCore + Clone -{ - fn clone(&self) -> ReseedingCore<R, Rsdr> { - ReseedingCore { - inner: self.inner.clone(), - reseeder: self.reseeder.clone(), - threshold: self.threshold, - bytes_until_reseed: 0, // reseed clone on first use - fork_counter: self.fork_counter, - } - } -} - -impl<R, Rsdr> CryptoRng for ReseedingCore<R, Rsdr> -where R: BlockRngCore + SeedableRng + CryptoRng, - Rsdr: RngCore + CryptoRng {} - - -#[cfg(all(unix, not(target_os="emscripten")))] -mod fork { - use core::sync::atomic::{AtomicUsize, AtomicBool, Ordering}; - #[allow(deprecated)] // Required for compatibility with Rust < 1.24. - use core::sync::atomic::{ATOMIC_USIZE_INIT, ATOMIC_BOOL_INIT}; - - // Fork protection - // - // We implement fork protection on Unix using `pthread_atfork`. - // When the process is forked, we increment `RESEEDING_RNG_FORK_COUNTER`. - // Every `ReseedingRng` stores the last known value of the static in - // `fork_counter`. If the cached `fork_counter` is less than - // `RESEEDING_RNG_FORK_COUNTER`, it is time to reseed this RNG. - // - // If reseeding fails, we don't deal with this by setting a delay, but just - // don't update `fork_counter`, so a reseed is attempted as soon as - // possible. - - #[allow(deprecated)] - static RESEEDING_RNG_FORK_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT; - - pub fn get_fork_counter() -> usize { - RESEEDING_RNG_FORK_COUNTER.load(Ordering::Relaxed) - } - - #[allow(deprecated)] - static FORK_HANDLER_REGISTERED: AtomicBool = ATOMIC_BOOL_INIT; - - extern fn fork_handler() { - // Note: fetch_add is defined to wrap on overflow - // (which is what we want). - RESEEDING_RNG_FORK_COUNTER.fetch_add(1, Ordering::Relaxed); - } - - pub fn register_fork_handler() { - if !FORK_HANDLER_REGISTERED.load(Ordering::Relaxed) { - unsafe { libc::pthread_atfork(None, None, Some(fork_handler)) }; - FORK_HANDLER_REGISTERED.store(true, Ordering::Relaxed); - } - } -} - -#[cfg(not(all(unix, not(target_os="emscripten"))))] -mod fork { - pub fn get_fork_counter() -> usize { 0 } - pub fn register_fork_handler() {} -} - - -#[cfg(test)] -mod test { - use crate::{Rng, SeedableRng}; - use crate::rngs::std::Core; - use crate::rngs::mock::StepRng; - use super::ReseedingRng; - - #[test] - fn test_reseeding() { - let mut zero = StepRng::new(0, 0); - let rng = Core::from_rng(&mut zero).unwrap(); - let thresh = 1; // reseed every time the buffer is exhausted - let mut reseeding = ReseedingRng::new(rng, thresh, zero); - - // RNG buffer size is [u32; 64] - // Debug is only implemented up to length 32 so use two arrays - 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] - fn test_clone_reseeding() { - let mut zero = StepRng::new(0, 0); - let rng = Core::from_rng(&mut zero).unwrap(); - let mut rng1 = ReseedingRng::new(rng, 32*4, zero); - - let first: u32 = rng1.gen(); - for _ in 0..10 { let _ = rng1.gen::<u32>(); } - - let mut rng2 = rng1.clone(); - assert_eq!(first, rng2.gen::<u32>()); - } -} |