aboutsummaryrefslogtreecommitdiff
path: root/rand/src/rngs/adapter/reseeding.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rand/src/rngs/adapter/reseeding.rs')
-rw-r--r--rand/src/rngs/adapter/reseeding.rs357
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>());
- }
-}