Update ReseedingRng documentation
And support setting no threshold with zero
This commit is contained in:
@@ -16,44 +16,71 @@ use core::mem::size_of;
|
||||
use rand_core::{RngCore, CryptoRng, SeedableRng, Error, ErrorKind};
|
||||
use rand_core::block::{BlockRngCore, BlockRng};
|
||||
|
||||
/// A wrapper around any PRNG which reseeds the underlying PRNG after it has
|
||||
/// generated a certain number of random bytes.
|
||||
/// A wrapper around any PRNG that implements [`BlockRngCore`], that adds the
|
||||
/// ability to reseed it.
|
||||
///
|
||||
/// When the RNG gets cloned, the clone is reseeded on first use.
|
||||
/// `ReseedingRng` reseeds the underlying PRNG in the following cases:
|
||||
///
|
||||
/// Reseeding 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'.
|
||||
/// - 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 [`ChaChaCore`] and [`Hc128Core`] this is a maximum of
|
||||
/// 15 `u32` values before reseeding.
|
||||
/// - After the PRNG has generated a configurable number of random bytes.
|
||||
///
|
||||
/// Some small non-cryptographic PRNGs can have very small periods, for
|
||||
/// example less than 2<sup>64</sup>. Would reseeding help to ensure that you do
|
||||
/// not wrap around at the end of the period? A period of 2<sup>64</sup> still
|
||||
/// takes several centuries of CPU-years on current hardware. Reseeding will
|
||||
/// actually make things worse, because the reseeded PRNG will just continue
|
||||
/// somewhere else *in the same period*, with a high chance of overlapping with
|
||||
/// previously used parts of it.
|
||||
/// # When should reseeding after a fixed number of generated bytes be used?
|
||||
///
|
||||
/// # When should you use `ReseedingRng`?
|
||||
/// 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'.
|
||||
///
|
||||
/// - 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,
|
||||
/// occasionally reseeding should make exploiting it much more difficult or
|
||||
/// even impossible.
|
||||
/// - It can be used as a poor man's cryptography (not recommended, just use a
|
||||
/// good CSPRNG). Previous implementations of `thread_rng` for example used
|
||||
/// `ReseedingRng` with the ISAAC RNG. That algorithm, although apparently
|
||||
/// strong and with no known attack, does not come with any proof of security
|
||||
/// and does not meet the current standards for a cryptographically secure
|
||||
/// PRNG. By reseeding it frequently (every 32 kiB) it seems safe to assume
|
||||
/// there is no attack that can operate on the tiny window between reseeds.
|
||||
/// 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 extremely 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.
|
||||
/// 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::prng::chacha::ChaChaCore; // Internal part of ChaChaRng that
|
||||
/// // implements BlockRngCore
|
||||
/// use rand::rngs::OsRng;
|
||||
/// use rand::rngs::adapter::ReseedingRng;
|
||||
///
|
||||
/// let prng = ChaChaCore::from_entropy();
|
||||
// FIXME: it is better to use EntropyRng as reseeder, but that doesn't implement
|
||||
// clone yet.
|
||||
/// let reseeder = OsRng::new().unwrap();
|
||||
/// let mut reseeding_rng = ReseedingRng::new(prng, 0, reseeder);
|
||||
///
|
||||
/// println!("{}", reseeding_rng.gen::<u64>());
|
||||
///
|
||||
/// let mut cloned_rng = reseeding_rng.clone();
|
||||
/// assert!(reseeding_rng.gen::<u64>() != cloned_rng.gen::<u64>());
|
||||
/// ```
|
||||
///
|
||||
/// [`ChaChaCore`]: ../../prng/chacha/struct.ChaChaCore.html
|
||||
/// [`Hc128Core`]: ../../prng/hc128/struct.Hc128Core.html
|
||||
/// [`BlockRngCore`]: ../../../rand_core/block/trait.BlockRngCore.html
|
||||
/// [`ReseedingRng::new`]: struct.ReseedingRng.html#method.new
|
||||
/// [`reseed()`]: struct.ReseedingRng.html#method.reseed
|
||||
#[derive(Debug)]
|
||||
pub struct ReseedingRng<R, Rsdr>(BlockRng<ReseedingCore<R, Rsdr>>)
|
||||
where R: BlockRngCore + SeedableRng,
|
||||
@@ -63,13 +90,12 @@ impl<R, Rsdr> ReseedingRng<R, Rsdr>
|
||||
where R: BlockRngCore + SeedableRng,
|
||||
Rsdr: RngCore
|
||||
{
|
||||
/// Create a new `ReseedingRng` with the given parameters.
|
||||
/// Create a new `ReseedingRng` from an existing PRNG, combined with a RNG
|
||||
/// to use as reseeder.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `rng`: the random number generator to use.
|
||||
/// * `threshold`: the number of generated bytes after which to reseed the RNG.
|
||||
/// * `reseeder`: the RNG to use for reseeding.
|
||||
/// `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)))
|
||||
}
|
||||
@@ -155,17 +181,20 @@ impl<R, Rsdr> ReseedingCore<R, Rsdr>
|
||||
where R: BlockRngCore + SeedableRng,
|
||||
Rsdr: RngCore
|
||||
{
|
||||
/// Create a new `ReseedingCore` with the given parameters.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `rng`: the random number generator to use.
|
||||
/// * `threshold`: the number of generated bytes after which to reseed the RNG.
|
||||
/// * `reseeder`: the RNG to use for reseeding.
|
||||
pub fn new(rng: R, threshold: u64, reseeder: Rsdr) -> Self {
|
||||
assert!(threshold <= ::core::i64::MAX as u64);
|
||||
/// 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,
|
||||
@@ -205,10 +234,7 @@ where R: BlockRngCore + SeedableRng,
|
||||
if self.is_forked(global_fork_counter) {
|
||||
info!("Fork detected, reseeding RNG");
|
||||
} else {
|
||||
// FIXME: the amount logged here may not be correct if the intial
|
||||
// `bytes_until_reseed` was set by a delay instead of `threshold`.
|
||||
trace!("Reseeding RNG after {} generated bytes",
|
||||
self.threshold - self.bytes_until_reseed);
|
||||
trace!("Reseeding RNG (periodic reseed)");
|
||||
}
|
||||
|
||||
let num_bytes =
|
||||
@@ -269,7 +295,7 @@ mod fork {
|
||||
// `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 a
|
||||
// don't update `fork_counter`, so a reseed is attempted as soon as
|
||||
// possible.
|
||||
|
||||
static RESEEDING_RNG_FORK_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
//! - [`EntropyRng`], [`OsRng`] and [`JitterRng`] as entropy sources
|
||||
//! - [`mock::StepRng`] as a simple counter for tests
|
||||
//! - [`adapter::ReadRng`] to read from a file/stream
|
||||
//! - [`adapter::ReseedingRng`] to reseed a PRNG on clone / process fork etc.
|
||||
//!
|
||||
//! # Background — Random number generators (RNGs)
|
||||
//!
|
||||
@@ -161,6 +162,7 @@
|
||||
//! [`thread_rng`]: ../fn.thread_rng.html
|
||||
//! [`mock::StepRng`]: mock/struct.StepRng.html
|
||||
//! [`adapter::ReadRng`]: adapter/struct.ReadRng.html
|
||||
//! [`adapter::ReseedingRng`]: adapter/struct.ReseedingRng.html
|
||||
//! [`ChaChaRng`]: ../prng/chacha/struct.ChaChaRng.html
|
||||
|
||||
pub mod adapter;
|
||||
|
||||
Reference in New Issue
Block a user