diff --git a/CHANGELOG.md b/CHANGELOG.md index df174a78a3..c341f0e50a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,9 +28,13 @@ You may also find the [Upgrade Guide](https://rust-random.github.io/book/update. - Pub export `Xoshiro128PlusPlus`, `Xoshiro256PlusPlus` prngs (#1649) - Pub export `ChaCha8Rng`, `ChaCha12Rng`, `ChaCha20Rng` behind `chacha` feature (#1659) +### Removals +- Removed `ReseedingRng` ([#1722]) + [#1695]: https://github.com/rust-random/rand/pull/1695 [#1697]: https://github.com/rust-random/rand/pull/1697 [#1717]: https://github.com/rust-random/rand/pull/1717 +[#1722]: https://github.com/rust-random/rand/pull/1722 ## [0.9.2] - 2025-07-20 ### Deprecated diff --git a/src/rngs/mod.rs b/src/rngs/mod.rs index 70f9f9dd2c..a9b5566b18 100644 --- a/src/rngs/mod.rs +++ b/src/rngs/mod.rs @@ -94,9 +94,6 @@ //! [PCG]: https://www.pcg-random.org/ //! [xoshiro]: https://prng.di.unimi.it/ -mod reseeding; -pub use reseeding::ReseedingRng; - #[cfg(feature = "small_rng")] mod small; #[cfg(feature = "small_rng")] diff --git a/src/rngs/reseeding.rs b/src/rngs/reseeding.rs deleted file mode 100644 index 8e14548d35..0000000000 --- a/src/rngs/reseeding.rs +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// Copyright 2013 The Rust Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , 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::convert::Infallible; -use core::mem::size_of_val; - -use rand_core::block::{BlockRng, CryptoGenerator, Generator}; -use rand_core::{SeedableRng, TryCryptoRng, TryRng}; - -/// A wrapper around any PRNG that implements [`Generator`], that adds the -/// ability to reseed it. -/// -/// `ReseedingRng` reseeds the underlying PRNG in the following cases: -/// -/// - On a manual call to [`reseed()`]. -/// - 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 chacha20::ChaCha20Core; // Internal part of ChaChaRng that -/// // implements Generator -/// use rand::prelude::*; -/// use rand::rngs::SysRng; -/// use rand::rngs::ReseedingRng; -/// -/// let mut reseeding_rng = ReseedingRng::::new(0, SysRng).unwrap(); -/// -/// println!("{}", reseeding_rng.random::()); -/// ``` -/// -/// [`Generator`]: rand_core::block::Generator -/// [`ReseedingRng::new`]: ReseedingRng::new -/// [`reseed()`]: ReseedingRng::reseed -#[derive(Debug)] -pub struct ReseedingRng(BlockRng>) -where - G: Generator + SeedableRng, - Rsdr: TryRng; - -impl ReseedingRng -where - G: Generator + SeedableRng, - Rsdr: TryRng, -{ - /// 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(threshold: u64, reseeder: Rsdr) -> Result { - Ok(ReseedingRng(BlockRng::new(ReseedingCore::new( - threshold, reseeder, - )?))) - } - - /// Immediately reseed the generator - /// - /// This discards any remaining random data in the cache. - pub fn reseed(&mut self) -> Result<(), Rsdr::Error> { - self.0.reset_and_skip(0); - self.0.core.reseed() - } -} - -// TODO: this should be implemented for any type where the inner type -// implements TryRng, but we can't specify that because ReseedingCore is private -impl TryRng for ReseedingRng -where - G: Generator + SeedableRng, - Rsdr: TryRng, -{ - type Error = Infallible; - - #[inline(always)] - fn try_next_u32(&mut self) -> Result { - Ok(self.0.next_word()) - } - - #[inline(always)] - fn try_next_u64(&mut self) -> Result { - Ok(self.0.next_u64_from_u32()) - } - - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Infallible> { - self.0.fill_bytes(dest); - Ok(()) - } -} - -impl TryCryptoRng for ReseedingRng -where - G: Generator + SeedableRng + CryptoGenerator, - Rsdr: TryCryptoRng, -{ -} - -#[derive(Debug)] -struct ReseedingCore { - inner: G, - reseeder: Rsdr, - threshold: i64, - bytes_until_reseed: i64, -} - -impl Generator for ReseedingCore -where - G: Generator + SeedableRng, - Rsdr: TryRng, -{ - type Output = ::Output; - - fn generate(&mut self, results: &mut Self::Output) { - if self.bytes_until_reseed <= 0 { - // 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); - } - let num_bytes = size_of_val(results); - self.bytes_until_reseed -= num_bytes as i64; - self.inner.generate(results); - } -} - -impl ReseedingCore -where - G: Generator + SeedableRng, - Rsdr: TryRng, -{ - /// Create a new `ReseedingCore`. - /// - /// `threshold` is the maximum number of bytes produced by - /// [`Generator::generate`] before attempting reseeding. - fn new(threshold: u64, mut reseeder: Rsdr) -> Result { - // 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 { - i64::MAX - } else if threshold <= i64::MAX as u64 { - threshold as i64 - } else { - i64::MAX - }; - - let inner = G::try_from_rng(&mut reseeder)?; - - Ok(ReseedingCore { - inner, - reseeder, - threshold, - bytes_until_reseed: threshold, - }) - } - - /// Reseed the internal PRNG. - fn reseed(&mut self) -> Result<(), Rsdr::Error> { - G::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 G::Output) { - trace!("Reseeding RNG (periodic reseed)"); - - let num_bytes = size_of_val(results); - - if let Err(e) = self.reseed() { - warn!("Reseeding RNG failed: {}", e); - let _ = e; - } - - self.bytes_until_reseed = self.threshold - num_bytes as i64; - self.inner.generate(results); - } -} - -impl CryptoGenerator for ReseedingCore -where - G: Generator + SeedableRng + CryptoGenerator, - Rsdr: TryCryptoRng, -{ -} - -#[cfg(feature = "std_rng")] -#[cfg(test)] -mod test { - use crate::RngExt; - use crate::rngs::std::Core; - use crate::test::const_rng; - - use super::ReseedingRng; - - #[test] - fn test_reseeding() { - let zero = const_rng(0); - let thresh = 1; // reseed every time the buffer is exhausted - let mut reseeding = ReseedingRng::::new(thresh, zero).unwrap(); - - // 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); - } - } -} diff --git a/src/rngs/thread.rs b/src/rngs/thread.rs index 529b004901..5008d34b4c 100644 --- a/src/rngs/thread.rs +++ b/src/rngs/thread.rs @@ -8,12 +8,16 @@ //! Thread-local random number generator +use core::mem::size_of_val; use core::{cell::UnsafeCell, convert::Infallible}; use std::fmt; use std::rc::Rc; use std::thread_local; -use super::{ReseedingRng, SysError, SysRng, std::Core}; +use super::std::Core; +use super::{SysError, SysRng}; +use rand_core::SeedableRng; +use rand_core::block::{BlockRng, Generator}; use rand_core::{TryCryptoRng, TryRng}; // Rationale for using `UnsafeCell` in `ThreadRng`: @@ -33,7 +37,55 @@ use rand_core::{TryCryptoRng, TryRng}; // Number of generated bytes after which to reseed `ThreadRng`. // According to benchmarks, reseeding has a noticeable impact with thresholds // of 32 kB and less. We choose 64 kB to avoid significant overhead. -const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64; +const THREAD_RNG_RESEED_THRESHOLD: i64 = 1024 * 64; + +type Results = ::Output; + +struct ReseedingCore { + inner: Core, + bytes_until_reseed: i64, +} + +impl Generator for ReseedingCore { + type Output = Results; + + fn generate(&mut self, results: &mut Results) { + if self.bytes_until_reseed <= 0 { + // 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); + } + let num_bytes = size_of_val(results); + self.bytes_until_reseed -= num_bytes as i64; + self.inner.generate(results); + } +} + +impl ReseedingCore { + /// Reseed the internal PRNG. + fn reseed(&mut self) -> Result<(), SysError> { + Core::try_from_rng(&mut SysRng).map(|result| { + self.bytes_until_reseed = THREAD_RNG_RESEED_THRESHOLD; + self.inner = result + }) + } + + #[inline(never)] + fn reseed_and_generate(&mut self, results: &mut Results) { + trace!("Reseeding RNG (periodic reseed)"); + + let num_bytes = size_of_val(results); + + if let Err(e) = self.reseed() { + warn!("Reseeding RNG failed: {}", e); + let _ = e; + } + + self.bytes_until_reseed = THREAD_RNG_RESEED_THRESHOLD - num_bytes as i64; + self.inner.generate(results); + } +} /// A reference to the thread-local generator /// @@ -48,9 +100,8 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64; /// requirements. The Rand project can provide no guarantee of fitness for /// purpose. The design criteria for `ThreadRng` are as follows: /// -/// - Automatic seeding via [`SysRng`] and periodically thereafter (see -/// ([`ReseedingRng`] documentation). Limitation: there is no automatic -/// reseeding on process fork (see [below](#fork)). +/// - Automatic seeding via [`SysRng`] and after every 64 kB of output. +/// Limitation: there is no automatic reseeding on process fork (see [below](#fork)). /// - A rigorusly analyzed, unpredictable (cryptographic) pseudo-random generator /// (see [the book on security](https://rust-random.github.io/book/guide-rngs.html#security)). /// The currently selected algorithm is ChaCha (12-rounds). @@ -85,12 +136,11 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64; /// from an interrupt (e.g. a fork handler) unless it can be guaranteed that no /// other method on the same `ThreadRng` is currently executing. /// -/// [`ReseedingRng`]: crate::rngs::ReseedingRng /// [`StdRng`]: crate::rngs::StdRng #[derive(Clone)] pub struct ThreadRng { // Rc is explicitly !Send and !Sync - rng: Rc>>, + rng: Rc>>, } impl ThreadRng { @@ -101,7 +151,8 @@ impl ThreadRng { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference let rng = unsafe { &mut *self.rng.get() }; - rng.reseed() + rng.reset_and_skip(0); + rng.core.reseed() } } @@ -115,11 +166,13 @@ impl fmt::Debug for ThreadRng { thread_local!( // We require Rc<..> to avoid premature freeing when ThreadRng is used // within thread-local destructors. See #968. - static THREAD_RNG_KEY: Rc>> = { - let rng = ReseedingRng::new(THREAD_RNG_RESEED_THRESHOLD, - SysRng).unwrap_or_else(|err| - panic!("could not initialize ThreadRng: {}", err)); - Rc::new(UnsafeCell::new(rng)) + static THREAD_RNG_KEY: Rc>> = { + Rc::new(UnsafeCell::new(BlockRng::new(ReseedingCore { + inner: Core::try_from_rng(&mut SysRng).unwrap_or_else(|err| { + panic!("could not initialize ThreadRng: {}", err) + }), + bytes_until_reseed: THREAD_RNG_RESEED_THRESHOLD, + }))) } ); @@ -170,7 +223,7 @@ impl TryRng for ThreadRng { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference let rng = unsafe { &mut *self.rng.get() }; - rng.try_next_u32() + Ok(rng.next_word()) } #[inline(always)] @@ -178,7 +231,7 @@ impl TryRng for ThreadRng { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference let rng = unsafe { &mut *self.rng.get() }; - rng.try_next_u64() + Ok(rng.next_u64_from_u32()) } #[inline(always)] @@ -186,7 +239,8 @@ impl TryRng for ThreadRng { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference let rng = unsafe { &mut *self.rng.get() }; - rng.try_fill_bytes(dest) + rng.fill_bytes(dest); + Ok(()) } }