aboutsummaryrefslogtreecommitdiff
path: root/rand/src/rngs
diff options
context:
space:
mode:
Diffstat (limited to 'rand/src/rngs')
-rw-r--r--rand/src/rngs/adapter/mod.rs4
-rw-r--r--rand/src/rngs/adapter/read.rs53
-rw-r--r--rand/src/rngs/adapter/reseeding.rs89
-rw-r--r--rand/src/rngs/entropy.rs201
-rw-r--r--rand/src/rngs/jitter.rs885
-rw-r--r--rand/src/rngs/mock.rs7
-rw-r--r--rand/src/rngs/mod.rs253
-rw-r--r--rand/src/rngs/small.rs60
-rw-r--r--rand/src/rngs/std.rs59
-rw-r--r--rand/src/rngs/thread.rs91
10 files changed, 298 insertions, 1404 deletions
diff --git a/rand/src/rngs/adapter/mod.rs b/rand/src/rngs/adapter/mod.rs
index 60b832e..659ff26 100644
--- a/rand/src/rngs/adapter/mod.rs
+++ b/rand/src/rngs/adapter/mod.rs
@@ -8,8 +8,8 @@
//! Wrappers / adapters forming RNGs
-#[cfg(feature="std")] #[doc(hidden)] pub mod read;
+#[cfg(feature="std")] mod read;
mod reseeding;
-#[cfg(feature="std")] pub use self::read::ReadRng;
+#[cfg(feature="std")] pub use self::read::{ReadRng, ReadError};
pub use self::reseeding::ReseedingRng;
diff --git a/rand/src/rngs/adapter/read.rs b/rand/src/rngs/adapter/read.rs
index 30b6de6..901462e 100644
--- a/rand/src/rngs/adapter/read.rs
+++ b/rand/src/rngs/adapter/read.rs
@@ -10,12 +10,13 @@
//! A wrapper around any Read to treat it as an RNG.
use std::io::Read;
+use std::fmt;
-use rand_core::{RngCore, Error, ErrorKind, impls};
+use rand_core::{RngCore, Error, impls};
/// An RNG that reads random bytes straight from any type supporting
-/// `std::io::Read`, for example files.
+/// [`std::io::Read`], for example files.
///
/// This will work best with an infinite reader, but that is not required.
///
@@ -24,10 +25,10 @@ use rand_core::{RngCore, Error, ErrorKind, impls};
///
/// # Panics
///
-/// `ReadRng` uses `std::io::read_exact`, which retries on interrupts. All other
-/// errors from the underlying reader, including when it does not have enough
-/// data, will only be reported through [`try_fill_bytes`]. The other
-/// [`RngCore`] methods will panic in case of an error.
+/// `ReadRng` uses [`std::io::Read::read_exact`], which retries on interrupts.
+/// All other errors from the underlying reader, including when it does not
+/// have enough data, will only be reported through [`try_fill_bytes`].
+/// The other [`RngCore`] methods will panic in case of an error.
///
/// # Example
///
@@ -40,9 +41,8 @@ use rand_core::{RngCore, Error, ErrorKind, impls};
/// println!("{:x}", rng.gen::<u32>());
/// ```
///
-/// [`OsRng`]: ../struct.OsRng.html
-/// [`RngCore`]: ../../trait.RngCore.html
-/// [`try_fill_bytes`]: ../../trait.RngCore.html#method.tymethod.try_fill_bytes
+/// [`OsRng`]: crate::rngs::OsRng
+/// [`try_fill_bytes`]: RngCore::try_fill_bytes
#[derive(Debug)]
pub struct ReadRng<R> {
reader: R
@@ -72,24 +72,33 @@ impl<R: Read> RngCore for ReadRng<R> {
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
- if dest.len() == 0 { return Ok(()); }
+ if dest.is_empty() { return Ok(()); }
// Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`.
- self.reader.read_exact(dest).map_err(|err| {
- match err.kind() {
- ::std::io::ErrorKind::UnexpectedEof => Error::with_cause(
- ErrorKind::Unavailable,
- "not enough bytes available, reached end of source", err),
- _ => Error::with_cause(ErrorKind::Unavailable,
- "error reading from Read source", err)
- }
- })
+ self.reader.read_exact(dest).map_err(|e| Error::new(ReadError(e)))
}
}
+/// `ReadRng` error type
+#[derive(Debug)]
+pub struct ReadError(std::io::Error);
+
+impl fmt::Display for ReadError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "ReadError: {}", self.0)
+ }
+}
+
+impl std::error::Error for ReadError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ Some(&self.0)
+ }
+}
+
+
#[cfg(test)]
mod test {
use super::ReadRng;
- use {RngCore, ErrorKind};
+ use crate::RngCore;
#[test]
fn test_reader_rng_u64() {
@@ -132,6 +141,8 @@ mod test {
let mut rng = ReadRng::new(&v[..]);
- assert!(rng.try_fill_bytes(&mut w).err().unwrap().kind == ErrorKind::Unavailable);
+ let result = rng.try_fill_bytes(&mut w);
+ assert!(result.is_err());
+ println!("Error: {}", result.unwrap_err());
}
}
diff --git a/rand/src/rngs/adapter/reseeding.rs b/rand/src/rngs/adapter/reseeding.rs
index 016afab..ec88efe 100644
--- a/rand/src/rngs/adapter/reseeding.rs
+++ b/rand/src/rngs/adapter/reseeding.rs
@@ -12,7 +12,7 @@
use core::mem::size_of;
-use rand_core::{RngCore, CryptoRng, SeedableRng, Error, ErrorKind};
+use rand_core::{RngCore, CryptoRng, SeedableRng, Error};
use rand_core::block::{BlockRngCore, BlockRng};
/// A wrapper around any PRNG that implements [`BlockRngCore`], that adds the
@@ -24,7 +24,7 @@ use rand_core::block::{BlockRngCore, BlockRng};
/// - 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
+/// 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.
///
@@ -57,33 +57,24 @@ use rand_core::block::{BlockRngCore, BlockRng};
/// # Example
///
/// ```
-/// # extern crate rand;
-/// # extern crate rand_chacha;
-/// # fn main() {
/// use rand::prelude::*;
-/// use rand_chacha::ChaChaCore; // Internal part of ChaChaRng that
+/// use rand_chacha::ChaCha20Core; // 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);
+/// 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>());
-/// # }
/// ```
///
-/// [`ChaChaCore`]: ../../../rand_chacha/struct.ChaChaCore.html
-/// [`Hc128Core`]: ../../../rand_hc/struct.Hc128Core.html
-/// [`BlockRngCore`]: ../../../rand_core/block/trait.BlockRngCore.html
-/// [`ReseedingRng::new`]: struct.ReseedingRng.html#method.new
-/// [`reseed()`]: struct.ReseedingRng.html#method.reseed
+/// [`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,
@@ -234,6 +225,7 @@ where R: BlockRngCore + SeedableRng,
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 {
@@ -243,21 +235,13 @@ where R: BlockRngCore + SeedableRng,
let num_bytes =
results.as_ref().len() * size_of::<<R as BlockRngCore>::Item>();
- let threshold = if let Err(e) = self.reseed() {
- let delay = match e.kind {
- ErrorKind::Transient => num_bytes as i64,
- kind @ _ if kind.should_retry() => self.threshold >> 8,
- _ => self.threshold,
- };
- warn!("Reseeding RNG delayed reseeding by {} bytes due to \
- error from source: {}", delay, e);
- delay
- } else {
- self.fork_counter = global_fork_counter;
- self.threshold
- };
+ if let Err(e) = self.reseed() {
+ warn!("Reseeding RNG failed: {}", e);
+ let _ = e;
+ }
+ self.fork_counter = global_fork_counter;
- self.bytes_until_reseed = threshold - num_bytes as i64;
+ self.bytes_until_reseed = self.threshold - num_bytes as i64;
self.inner.generate(results);
}
}
@@ -282,12 +266,11 @@ where R: BlockRngCore + SeedableRng + CryptoRng,
Rsdr: RngCore + CryptoRng {}
-#[cfg(all(feature="std", unix, not(target_os="emscripten")))]
+#[cfg(all(unix, not(target_os="emscripten")))]
mod fork {
- extern crate libc;
-
- use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
- use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT};
+ 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
//
@@ -301,12 +284,14 @@ mod fork {
// 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() {
@@ -316,14 +301,14 @@ mod fork {
}
pub fn register_fork_handler() {
- if FORK_HANDLER_REGISTERED.load(Ordering::Relaxed) == false {
+ 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(feature="std", unix, not(target_os="emscripten"))))]
+#[cfg(not(all(unix, not(target_os="emscripten"))))]
mod fork {
pub fn get_fork_counter() -> usize { 0 }
pub fn register_fork_handler() {}
@@ -332,25 +317,27 @@ mod fork {
#[cfg(test)]
mod test {
- use {Rng, SeedableRng};
- use rand_chacha::ChaChaCore;
- use rngs::mock::StepRng;
+ 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 = ChaChaCore::from_rng(&mut zero).unwrap();
- let mut reseeding = ReseedingRng::new(rng, 32*4, zero);
-
- // Currently we only support for arrays up to length 32.
- // TODO: cannot generate seq via Rng::gen because it uses different alg
- let mut buf = [0u32; 32]; // Needs to be a multiple of the RNGs result
- // size to test exactly.
- reseeding.fill(&mut buf);
+ 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);
+ reseeding.fill(&mut buf.0);
+ reseeding.fill(&mut buf.1);
assert_eq!(buf, seq);
}
}
@@ -358,7 +345,7 @@ mod test {
#[test]
fn test_clone_reseeding() {
let mut zero = StepRng::new(0, 0);
- let rng = ChaChaCore::from_rng(&mut zero).unwrap();
+ let rng = Core::from_rng(&mut zero).unwrap();
let mut rng1 = ReseedingRng::new(rng, 32*4, zero);
let first: u32 = rng1.gen();
diff --git a/rand/src/rngs/entropy.rs b/rand/src/rngs/entropy.rs
index 372b4d7..1ed59ab 100644
--- a/rand/src/rngs/entropy.rs
+++ b/rand/src/rngs/entropy.rs
@@ -8,52 +8,21 @@
//! Entropy generator, or wrapper around external generators
-use rand_core::{RngCore, CryptoRng, Error, ErrorKind, impls};
-#[allow(unused)]
-use rngs;
+#![allow(deprecated)] // whole module is deprecated
+
+use rand_core::{RngCore, CryptoRng, Error};
+use crate::rngs::OsRng;
/// An interface returning random data from external source(s), provided
/// specifically for securely seeding algorithmic generators (PRNGs).
///
-/// Where possible, `EntropyRng` retrieves random data from the operating
-/// system's interface for random numbers ([`OsRng`]); if that fails it will
-/// fall back to the [`JitterRng`] entropy collector. In the latter case it will
-/// still try to use [`OsRng`] on the next usage.
-///
-/// If no secure source of entropy is available `EntropyRng` will panic on use;
-/// i.e. it should never output predictable data.
-///
-/// This is either a little slow ([`OsRng`] requires a system call) or extremely
-/// slow ([`JitterRng`] must use significant CPU time to generate sufficient
-/// jitter); for better performance it is common to seed a local PRNG from
-/// external entropy then primarily use the local PRNG ([`thread_rng`] is
-/// provided as a convenient, local, automatically-seeded CSPRNG).
-///
-/// # Panics
-///
-/// On most systems, like Windows, Linux, macOS and *BSD on common hardware, it
-/// is highly unlikely for both [`OsRng`] and [`JitterRng`] to fail. But on
-/// combinations like webassembly without Emscripten or stdweb both sources are
-/// unavailable. If both sources fail, only [`try_fill_bytes`] is able to
-/// report the error, and only the one from `OsRng`. The other [`RngCore`]
-/// methods will panic in case of an error.
-///
-/// [`OsRng`]: struct.OsRng.html
-/// [`JitterRng`]: jitter/struct.JitterRng.html
-/// [`thread_rng`]: ../fn.thread_rng.html
-/// [`RngCore`]: ../trait.RngCore.html
-/// [`try_fill_bytes`]: ../trait.RngCore.html#method.tymethod.try_fill_bytes
+/// This is deprecated. It is suggested you use [`rngs::OsRng`] instead.
+///
+/// [`rngs::OsRng`]: crate::rngs::OsRng
#[derive(Debug)]
+#[deprecated(since="0.7.0", note="use rngs::OsRng instead")]
pub struct EntropyRng {
- source: Source,
-}
-
-#[derive(Debug)]
-enum Source {
- Os(Os),
- Custom(Custom),
- Jitter(Jitter),
- None,
+ source: OsRng,
}
impl EntropyRng {
@@ -63,7 +32,7 @@ impl EntropyRng {
/// those are done on first use. This is done to make `new` infallible,
/// and `try_fill_bytes` the only place to report errors.
pub fn new() -> Self {
- EntropyRng { source: Source::None }
+ EntropyRng { source: OsRng }
}
}
@@ -75,167 +44,25 @@ impl Default for EntropyRng {
impl RngCore for EntropyRng {
fn next_u32(&mut self) -> u32 {
- impls::next_u32_via_fill(self)
+ self.source.next_u32()
}
fn next_u64(&mut self) -> u64 {
- impls::next_u64_via_fill(self)
+ self.source.next_u64()
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
- self.try_fill_bytes(dest).unwrap_or_else(|err|
- panic!("all entropy sources failed; first error: {}", err))
+ self.source.fill_bytes(dest)
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
- let mut reported_error = None;
-
- if let Source::Os(ref mut os_rng) = self.source {
- match os_rng.fill(dest) {
- Ok(()) => return Ok(()),
- Err(err) => {
- warn!("EntropyRng: OsRng failed \
- [trying other entropy sources]: {}", err);
- reported_error = Some(err);
- },
- }
- } else if Os::is_supported() {
- match Os::new_and_fill(dest) {
- Ok(os_rng) => {
- debug!("EntropyRng: using OsRng");
- self.source = Source::Os(os_rng);
- return Ok(());
- },
- Err(err) => { reported_error = reported_error.or(Some(err)) },
- }
- }
-
- if let Source::Custom(ref mut rng) = self.source {
- match rng.fill(dest) {
- Ok(()) => return Ok(()),
- Err(err) => {
- warn!("EntropyRng: custom entropy source failed \
- [trying other entropy sources]: {}", err);
- reported_error = Some(err);
- },
- }
- } else if Custom::is_supported() {
- match Custom::new_and_fill(dest) {
- Ok(custom) => {
- debug!("EntropyRng: using custom entropy source");
- self.source = Source::Custom(custom);
- return Ok(());
- },
- Err(err) => { reported_error = reported_error.or(Some(err)) },
- }
- }
-
- if let Source::Jitter(ref mut jitter_rng) = self.source {
- match jitter_rng.fill(dest) {
- Ok(()) => return Ok(()),
- Err(err) => {
- warn!("EntropyRng: JitterRng failed: {}", err);
- reported_error = Some(err);
- },
- }
- } else if Jitter::is_supported() {
- match Jitter::new_and_fill(dest) {
- Ok(jitter_rng) => {
- debug!("EntropyRng: using JitterRng");
- self.source = Source::Jitter(jitter_rng);
- return Ok(());
- },
- Err(err) => { reported_error = reported_error.or(Some(err)) },
- }
- }
-
- if let Some(err) = reported_error {
- Err(Error::with_cause(ErrorKind::Unavailable,
- "All entropy sources failed",
- err))
- } else {
- Err(Error::new(ErrorKind::Unavailable,
- "No entropy sources available"))
- }
+ self.source.try_fill_bytes(dest)
}
}
impl CryptoRng for EntropyRng {}
-
-trait EntropySource {
- fn new_and_fill(dest: &mut [u8]) -> Result<Self, Error>
- where Self: Sized;
-
- fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error>;
-
- fn is_supported() -> bool { true }
-}
-
-#[allow(unused)]
-#[derive(Clone, Debug)]
-struct NoSource;
-
-#[allow(unused)]
-impl EntropySource for NoSource {
- fn new_and_fill(dest: &mut [u8]) -> Result<Self, Error> {
- Err(Error::new(ErrorKind::Unavailable, "Source not supported"))
- }
-
- fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> {
- unreachable!()
- }
-
- fn is_supported() -> bool { false }
-}
-
-
-#[cfg(feature="rand_os")]
-#[derive(Clone, Debug)]
-pub struct Os(rngs::OsRng);
-
-#[cfg(feature="rand_os")]
-impl EntropySource for Os {
- fn new_and_fill(dest: &mut [u8]) -> Result<Self, Error> {
- let mut rng = rngs::OsRng::new()?;
- rng.try_fill_bytes(dest)?;
- Ok(Os(rng))
- }
-
- fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> {
- self.0.try_fill_bytes(dest)
- }
-}
-
-#[cfg(not(feature="std"))]
-type Os = NoSource;
-
-
-type Custom = NoSource;
-
-
-#[cfg(not(target_arch = "wasm32"))]
-#[derive(Clone, Debug)]
-pub struct Jitter(rngs::JitterRng);
-
-#[cfg(not(target_arch = "wasm32"))]
-impl EntropySource for Jitter {
- fn new_and_fill(dest: &mut [u8]) -> Result<Self, Error> {
- let mut rng = rngs::JitterRng::new()?;
- rng.try_fill_bytes(dest)?;
- Ok(Jitter(rng))
- }
-
- fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> {
- self.0.try_fill_bytes(dest)
- }
-}
-
-#[cfg(target_arch = "wasm32")]
-type Jitter = NoSource;
-
-
#[cfg(test)]
mod test {
use super::*;
diff --git a/rand/src/rngs/jitter.rs b/rand/src/rngs/jitter.rs
deleted file mode 100644
index 3e93477..0000000
--- a/rand/src/rngs/jitter.rs
+++ /dev/null
@@ -1,885 +0,0 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// 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.
-//
-// Based on jitterentropy-library, http://www.chronox.de/jent.html.
-// Copyright Stephan Mueller <smueller@chronox.de>, 2014 - 2017.
-//
-// With permission from Stephan Mueller to relicense the Rust translation under
-// the MIT license.
-
-//! Non-physical true random number generator based on timing jitter.
-
-// Note: the C implementation of `Jitterentropy` relies on being compiled
-// without optimizations. This implementation goes through lengths to make the
-// compiler not optimize out code which does influence timing jitter, but is
-// technically dead code.
-
-use rand_core::{RngCore, CryptoRng, Error, ErrorKind, impls};
-
-use core::{fmt, mem, ptr};
-#[cfg(all(feature="std", not(target_arch = "wasm32")))]
-use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
-
-const MEMORY_BLOCKS: usize = 64;
-const MEMORY_BLOCKSIZE: usize = 32;
-const MEMORY_SIZE: usize = MEMORY_BLOCKS * MEMORY_BLOCKSIZE;
-
-/// A true random number generator based on jitter in the CPU execution time,
-/// and jitter in memory access time.
-///
-/// This is a true random number generator, as opposed to pseudo-random
-/// generators. Random numbers generated by `JitterRng` can be seen as fresh
-/// entropy. A consequence is that is orders of magnitude slower than [`OsRng`]
-/// and PRNGs (about 10<sup>3</sup>..10<sup>6</sup> slower).
-///
-/// There are very few situations where using this RNG is appropriate. Only very
-/// few applications require true entropy. A normal PRNG can be statistically
-/// indistinguishable, and a cryptographic PRNG should also be as impossible to
-/// predict.
-///
-/// Use of `JitterRng` is recommended for initializing cryptographic PRNGs when
-/// [`OsRng`] is not available.
-///
-/// `JitterRng` can be used without the standard library, but not conveniently,
-/// you must provide a high-precision timer and carefully have to follow the
-/// instructions of [`new_with_timer`].
-///
-/// This implementation is based on
-/// [Jitterentropy](http://www.chronox.de/jent.html) version 2.1.0.
-///
-/// Note: There is no accurate timer available on Wasm platforms, to help
-/// prevent fingerprinting or timing side-channel attacks. Therefore
-/// [`JitterRng::new()`] is not available on Wasm.
-///
-/// # Quality testing
-///
-/// [`JitterRng::new()`] has build-in, but limited, quality testing, however
-/// before using `JitterRng` on untested hardware, or after changes that could
-/// effect how the code is optimized (such as a new LLVM version), it is
-/// recommend to run the much more stringent
-/// [NIST SP 800-90B Entropy Estimation Suite](
-/// https://github.com/usnistgov/SP800-90B_EntropyAssessment).
-///
-/// Use the following code using [`timer_stats`] to collect the data:
-///
-/// ```no_run
-/// use rand::rngs::JitterRng;
-/// #
-/// # use std::error::Error;
-/// # use std::fs::File;
-/// # use std::io::Write;
-/// #
-/// # fn try_main() -> Result<(), Box<Error>> {
-/// let mut rng = JitterRng::new()?;
-///
-/// // 1_000_000 results are required for the
-/// // NIST SP 800-90B Entropy Estimation Suite
-/// const ROUNDS: usize = 1_000_000;
-/// let mut deltas_variable: Vec<u8> = Vec::with_capacity(ROUNDS);
-/// let mut deltas_minimal: Vec<u8> = Vec::with_capacity(ROUNDS);
-///
-/// for _ in 0..ROUNDS {
-/// deltas_variable.push(rng.timer_stats(true) as u8);
-/// deltas_minimal.push(rng.timer_stats(false) as u8);
-/// }
-///
-/// // Write out after the statistics collection loop, to not disturb the
-/// // test results.
-/// File::create("jitter_rng_var.bin")?.write(&deltas_variable)?;
-/// File::create("jitter_rng_min.bin")?.write(&deltas_minimal)?;
-/// #
-/// # Ok(())
-/// # }
-/// #
-/// # fn main() {
-/// # try_main().unwrap();
-/// # }
-/// ```
-///
-/// This will produce two files: `jitter_rng_var.bin` and `jitter_rng_min.bin`.
-/// Run the Entropy Estimation Suite in three configurations, as outlined below.
-/// Every run has two steps. One step to produce an estimation, another to
-/// validate the estimation.
-///
-/// 1. Estimate the expected amount of entropy that is at least available with
-/// each round of the entropy collector. This number should be greater than
-/// the amount estimated with `64 / test_timer()`.
-/// ```sh
-/// python noniid_main.py -v jitter_rng_var.bin 8
-/// restart.py -v jitter_rng_var.bin 8 <min-entropy>
-/// ```
-/// 2. Estimate the expected amount of entropy that is available in the last 4
-/// bits of the timer delta after running noice sources. Note that a value of
-/// `3.70` is the minimum estimated entropy for true randomness.
-/// ```sh
-/// python noniid_main.py -v -u 4 jitter_rng_var.bin 4
-/// restart.py -v -u 4 jitter_rng_var.bin 4 <min-entropy>
-/// ```
-/// 3. Estimate the expected amount of entropy that is available to the entropy
-/// collector if both noice sources only run their minimal number of times.
-/// This measures the absolute worst-case, and gives a lower bound for the
-/// available entropy.
-/// ```sh
-/// python noniid_main.py -v -u 4 jitter_rng_min.bin 4
-/// restart.py -v -u 4 jitter_rng_min.bin 4 <min-entropy>
-/// ```
-///
-/// [`OsRng`]: struct.OsRng.html
-/// [`JitterRng::new()`]: struct.JitterRng.html#method.new
-/// [`new_with_timer`]: struct.JitterRng.html#method.new_with_timer
-/// [`timer_stats`]: struct.JitterRng.html#method.timer_stats
-pub struct JitterRng {
- data: u64, // Actual random number
- // Number of rounds to run the entropy collector per 64 bits
- rounds: u8,
- // Timer used by `measure_jitter`
- timer: fn() -> u64,
- // Memory for the Memory Access noise source
- mem_prev_index: u16,
- // Make `next_u32` not waste 32 bits
- data_half_used: bool,
-}
-
-// Note: `JitterRng` maintains a small 64-bit entropy pool. With every
-// `generate` 64 new bits should be integrated in the pool. If a round of
-// `generate` were to collect less than the expected 64 bit, then the returned
-// value, and the new state of the entropy pool, would be in some way related to
-// the initial state. It is therefore better if the initial state of the entropy
-// pool is different on each call to `generate`. This has a few implications:
-// - `generate` should be called once before using `JitterRng` to produce the
-// first usable value (this is done by default in `new`);
-// - We do not zero the entropy pool after generating a result. The reference
-// implementation also does not support zeroing, but recommends generating a
-// new value without using it if you want to protect a previously generated
-// 'secret' value from someone inspecting the memory;
-// - Implementing `Clone` seems acceptable, as it would not cause the systematic
-// bias a constant might cause. Only instead of one value that could be
-// potentially related to the same initial state, there are now two.
-
-// Entropy collector state.
-// These values are not necessary to preserve across runs.
-struct EcState {
- // Previous time stamp to determine the timer delta
- prev_time: u64,
- // Deltas used for the stuck test
- last_delta: i32,
- last_delta2: i32,
- // Memory for the Memory Access noise source
- mem: [u8; MEMORY_SIZE],
-}
-
-impl EcState {
- // Stuck test by checking the:
- // - 1st derivation of the jitter measurement (time delta)
- // - 2nd derivation of the jitter measurement (delta of time deltas)
- // - 3rd derivation of the jitter measurement (delta of delta of time
- // deltas)
- //
- // All values must always be non-zero.
- // This test is a heuristic to see whether the last measurement holds
- // entropy.
- fn stuck(&mut self, current_delta: i32) -> bool {
- let delta2 = self.last_delta - current_delta;
- let delta3 = delta2 - self.last_delta2;
-
- self.last_delta = current_delta;
- self.last_delta2 = delta2;
-
- current_delta == 0 || delta2 == 0 || delta3 == 0
- }
-}
-
-// Custom Debug implementation that does not expose the internal state
-impl fmt::Debug for JitterRng {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "JitterRng {{}}")
- }
-}
-
-impl Clone for JitterRng {
- fn clone(&self) -> JitterRng {
- JitterRng {
- data: self.data,
- rounds: self.rounds,
- timer: self.timer,
- mem_prev_index: self.mem_prev_index,
- // The 32 bits that may still be unused from the previous round are
- // for the original to use, not for the clone.
- data_half_used: false,
- }
- }
-}
-
-/// An error that can occur when [`JitterRng::test_timer`] fails.
-///
-/// [`JitterRng::test_timer`]: struct.JitterRng.html#method.test_timer
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum TimerError {
- /// No timer available.
- NoTimer,
- /// Timer too coarse to use as an entropy source.
- CoarseTimer,
- /// Timer is not monotonically increasing.
- NotMonotonic,
- /// Variations of deltas of time too small.
- TinyVariantions,
- /// Too many stuck results (indicating no added entropy).
- TooManyStuck,
- #[doc(hidden)]
- __Nonexhaustive,
-}
-
-impl TimerError {
- fn description(&self) -> &'static str {
- match *self {
- TimerError::NoTimer => "no timer available",
- TimerError::CoarseTimer => "coarse timer",
- TimerError::NotMonotonic => "timer not monotonic",
- TimerError::TinyVariantions => "time delta variations too small",
- TimerError::TooManyStuck => "too many stuck results",
- TimerError::__Nonexhaustive => unreachable!(),
- }
- }
-}
-
-impl fmt::Display for TimerError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}", self.description())
- }
-}
-
-#[cfg(feature="std")]
-impl ::std::error::Error for TimerError {
- fn description(&self) -> &str {
- self.description()
- }
-}
-
-impl From<TimerError> for Error {
- fn from(err: TimerError) -> Error {
- // Timer check is already quite permissive of failures so we don't
- // expect false-positive failures, i.e. any error is irrecoverable.
- Error::with_cause(ErrorKind::Unavailable,
- "timer jitter failed basic quality tests", err)
- }
-}
-
-// Initialise to zero; must be positive
-#[cfg(all(feature="std", not(target_arch = "wasm32")))]
-static JITTER_ROUNDS: AtomicUsize = ATOMIC_USIZE_INIT;
-
-impl JitterRng {
- /// Create a new `JitterRng`. Makes use of `std::time` for a timer, or a
- /// platform-specific function with higher accuracy if necessary and
- /// available.
- ///
- /// During initialization CPU execution timing jitter is measured a few
- /// hundred times. If this does not pass basic quality tests, an error is
- /// returned. The test result is cached to make subsequent calls faster.
- #[cfg(all(feature="std", not(target_arch = "wasm32")))]
- pub fn new() -> Result<JitterRng, TimerError> {
- let mut state = JitterRng::new_with_timer(platform::get_nstime);
- let mut rounds = JITTER_ROUNDS.load(Ordering::Relaxed) as u8;
- if rounds == 0 {
- // No result yet: run test.
- // This allows the timer test to run multiple times; we don't care.
- rounds = state.test_timer()?;
- JITTER_ROUNDS.store(rounds as usize, Ordering::Relaxed);
- info!("JitterRng: using {} rounds per u64 output", rounds);
- }
- state.set_rounds(rounds);
-
- // Fill `data` with a non-zero value.
- state.gen_entropy();
- Ok(state)
- }
-
- /// Create a new `JitterRng`.
- /// A custom timer can be supplied, making it possible to use `JitterRng` in
- /// `no_std` environments.
- ///
- /// The timer must have nanosecond precision.
- ///
- /// This method is more low-level than `new()`. It is the responsibility of
- /// the caller to run [`test_timer`] before using any numbers generated with
- /// `JitterRng`, and optionally call [`set_rounds`]. Also it is important to
- /// consume at least one `u64` before using the first result to initialize
- /// the entropy collection pool.
- ///
- /// # Example
- ///
- /// ```
- /// # use rand::{Rng, Error};
- /// use rand::rngs::JitterRng;
- ///
- /// # fn try_inner() -> Result<(), Error> {
- /// fn get_nstime() -> u64 {
- /// use std::time::{SystemTime, UNIX_EPOCH};
- ///
- /// let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
- /// // The correct way to calculate the current time is
- /// // `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64`
- /// // But this is faster, and the difference in terms of entropy is
- /// // negligible (log2(10^9) == 29.9).
- /// dur.as_secs() << 30 | dur.subsec_nanos() as u64
- /// }
- ///
- /// let mut rng = JitterRng::new_with_timer(get_nstime);
- /// let rounds = rng.test_timer()?;
- /// rng.set_rounds(rounds); // optional
- /// let _ = rng.gen::<u64>();
- ///
- /// // Ready for use
- /// let v: u64 = rng.gen();
- /// # Ok(())
- /// # }
- ///
- /// # let _ = try_inner();
- /// ```
- ///
- /// [`test_timer`]: struct.JitterRng.html#method.test_timer
- /// [`set_rounds`]: struct.JitterRng.html#method.set_rounds
- pub fn new_with_timer(timer: fn() -> u64) -> JitterRng {
- JitterRng {
- data: 0,
- rounds: 64,
- timer,
- mem_prev_index: 0,
- data_half_used: false,
- }
- }
-
- /// Configures how many rounds are used to generate each 64-bit value.
- /// This must be greater than zero, and has a big impact on performance
- /// and output quality.
- ///
- /// [`new_with_timer`] conservatively uses 64 rounds, but often less rounds
- /// can be used. The `test_timer()` function returns the minimum number of
- /// rounds required for full strength (platform dependent), so one may use
- /// `rng.set_rounds(rng.test_timer()?);` or cache the value.
- ///
- /// [`new_with_timer`]: struct.JitterRng.html#method.new_with_timer
- pub fn set_rounds(&mut self, rounds: u8) {
- assert!(rounds > 0);
- self.rounds = rounds;
- }
-
- // Calculate a random loop count used for the next round of an entropy
- // collection, based on bits from a fresh value from the timer.
- //
- // The timer is folded to produce a number that contains at most `n_bits`
- // bits.
- //
- // Note: A constant should be added to the resulting random loop count to
- // prevent loops that run 0 times.
- #[inline(never)]
- fn random_loop_cnt(&mut self, n_bits: u32) -> u32 {
- let mut rounds = 0;
-
- let mut time = (self.timer)();
- // Mix with the current state of the random number balance the random
- // loop counter a bit more.
- time ^= self.data;
-
- // We fold the time value as much as possible to ensure that as many
- // bits of the time stamp are included as possible.
- let folds = (64 + n_bits - 1) / n_bits;
- let mask = (1 << n_bits) - 1;
- for _ in 0..folds {
- rounds ^= time & mask;
- time >>= n_bits;
- }
-
- rounds as u32
- }
-
- // CPU jitter noise source
- // Noise source based on the CPU execution time jitter
- //
- // This function injects the individual bits of the time value into the
- // entropy pool using an LFSR.
- //
- // The code is deliberately inefficient with respect to the bit shifting.
- // This function not only acts as folding operation, but this function's
- // execution is used to measure the CPU execution time jitter. Any change to
- // the loop in this function implies that careful retesting must be done.
- #[inline(never)]
- fn lfsr_time(&mut self, time: u64, var_rounds: bool) {
- fn lfsr(mut data: u64, time: u64) -> u64{
- for i in 1..65 {
- let mut tmp = time << (64 - i);
- tmp >>= 64 - 1;
-
- // Fibonacci LSFR with polynomial of
- // x^64 + x^61 + x^56 + x^31 + x^28 + x^23 + 1 which is
- // primitive according to
- // http://poincare.matf.bg.ac.rs/~ezivkovm/publications/primpol1.pdf
- // (the shift values are the polynomial values minus one
- // due to counting bits from 0 to 63). As the current
- // position is always the LSB, the polynomial only needs
- // to shift data in from the left without wrap.
- data ^= tmp;
- data ^= (data >> 63) & 1;
- data ^= (data >> 60) & 1;
- data ^= (data >> 55) & 1;
- data ^= (data >> 30) & 1;
- data ^= (data >> 27) & 1;
- data ^= (data >> 22) & 1;
- data = data.rotate_left(1);
- }
- data
- }
-
- // Note: in the reference implementation only the last round effects
- // `self.data`, all the other results are ignored. To make sure the
- // other rounds are not optimised out, we first run all but the last
- // round on a throw-away value instead of the real `self.data`.
- let mut lfsr_loop_cnt = 0;
- if var_rounds { lfsr_loop_cnt = self.random_loop_cnt(4) };
-
- let mut throw_away: u64 = 0;
- for _ in 0..lfsr_loop_cnt {
- throw_away = lfsr(throw_away, time);
- }
- black_box(throw_away);
-
- self.data = lfsr(self.data, time);
- }
-
- // Memory Access noise source
- // This is a noise source based on variations in memory access times
- //
- // This function performs memory accesses which will add to the timing
- // variations due to an unknown amount of CPU wait states that need to be
- // added when accessing memory. The memory size should be larger than the L1
- // caches as outlined in the documentation and the associated testing.
- //
- // The L1 cache has a very high bandwidth, albeit its access rate is usually
- // slower than accessing CPU registers. Therefore, L1 accesses only add
- // minimal variations as the CPU has hardly to wait. Starting with L2,
- // significant variations are added because L2 typically does not belong to
- // the CPU any more and therefore a wider range of CPU wait states is
- // necessary for accesses. L3 and real memory accesses have even a wider
- // range of wait states. However, to reliably access either L3 or memory,
- // the `self.mem` memory must be quite large which is usually not desirable.
- #[inline(never)]
- fn memaccess(&mut self, mem: &mut [u8; MEMORY_SIZE], var_rounds: bool) {
- let mut acc_loop_cnt = 128;
- if var_rounds { acc_loop_cnt += self.random_loop_cnt(4) };
-
- let mut index = self.mem_prev_index as usize;
- for _ in 0..acc_loop_cnt {
- // Addition of memblocksize - 1 to index with wrap around logic to
- // ensure that every memory location is hit evenly.
- // The modulus also allows the compiler to remove the indexing
- // bounds check.
- index = (index + MEMORY_BLOCKSIZE - 1) % MEMORY_SIZE;
-
- // memory access: just add 1 to one byte
- // memory access implies read from and write to memory location
- mem[index] = mem[index].wrapping_add(1);
- }
- self.mem_prev_index = index as u16;
- }
-
- // This is the heart of the entropy generation: calculate time deltas and
- // use the CPU jitter in the time deltas. The jitter is injected into the
- // entropy pool.
- //
- // Ensure that `ec.prev_time` is primed before using the output of this
- // function. This can be done by calling this function and not using its
- // result.
- fn measure_jitter(&mut self, ec: &mut EcState) -> Option<()> {
- // Invoke one noise source before time measurement to add variations
- self.memaccess(&mut ec.mem, true);
-
- // Get time stamp and calculate time delta to previous
- // invocation to measure the timing variations
- let time = (self.timer)();
- // Note: wrapping_sub combined with a cast to `i64` generates a correct
- // delta, even in the unlikely case this is a timer that is not strictly
- // monotonic.
- let current_delta = time.wrapping_sub(ec.prev_time) as i64 as i32;
- ec.prev_time = time;
-
- // Call the next noise source which also injects the data
- self.lfsr_time(current_delta as u64, true);
-
- // Check whether we have a stuck measurement (i.e. does the last
- // measurement holds entropy?).
- if ec.stuck(current_delta) { return None };
-
- // Rotate the data buffer by a prime number (any odd number would
- // do) to ensure that every bit position of the input time stamp
- // has an even chance of being merged with a bit position in the
- // entropy pool. We do not use one here as the adjacent bits in
- // successive time deltas may have some form of dependency. The
- // chosen value of 7 implies that the low 7 bits of the next
- // time delta value is concatenated with the current time delta.
- self.data = self.data.rotate_left(7);
-
- Some(())
- }
-
- // Shuffle the pool a bit by mixing some value with a bijective function
- // (XOR) into the pool.
- //
- // The function generates a mixer value that depends on the bits set and
- // the location of the set bits in the random number generated by the
- // entropy source. Therefore, based on the generated random number, this
- // mixer value can have 2^64 different values. That mixer value is
- // initialized with the first two SHA-1 constants. After obtaining the
- // mixer value, it is XORed into the random number.
- //
- // The mixer value is not assumed to contain any entropy. But due to the
- // XOR operation, it can also not destroy any entropy present in the
- // entropy pool.
- #[inline(never)]
- fn stir_pool(&mut self) {
- // This constant is derived from the first two 32 bit initialization
- // vectors of SHA-1 as defined in FIPS 180-4 section 5.3.1
- // The order does not really matter as we do not rely on the specific
- // numbers. We just pick the SHA-1 constants as they have a good mix of
- // bit set and unset.
- const CONSTANT: u64 = 0x67452301efcdab89;
-
- // The start value of the mixer variable is derived from the third
- // and fourth 32 bit initialization vector of SHA-1 as defined in
- // FIPS 180-4 section 5.3.1
- let mut mixer = 0x98badcfe10325476;
-
- // This is a constant time function to prevent leaking timing
- // information about the random number.
- // The normal code is:
- // ```
- // for i in 0..64 {
- // if ((self.data >> i) & 1) == 1 { mixer ^= CONSTANT; }
- // }
- // ```
- // This is a bit fragile, as LLVM really wants to use branches here, and
- // we rely on it to not recognise the opportunity.
- for i in 0..64 {
- let apply = (self.data >> i) & 1;
- let mask = !apply.wrapping_sub(1);
- mixer ^= CONSTANT & mask;
- mixer = mixer.rotate_left(1);
- }
-
- self.data ^= mixer;
- }
-
- fn gen_entropy(&mut self) -> u64 {
- trace!("JitterRng: collecting entropy");
-
- // Prime `ec.prev_time`, and run the noice sources to make sure the
- // first loop round collects the expected entropy.
- let mut ec = EcState {
- prev_time: (self.timer)(),
- last_delta: 0,
- last_delta2: 0,
- mem: [0; MEMORY_SIZE],
- };
- let _ = self.measure_jitter(&mut ec);
-
- for _ in 0..self.rounds {
- // If a stuck measurement is received, repeat measurement
- // Note: we do not guard against an infinite loop, that would mean
- // the timer suddenly became broken.
- while self.measure_jitter(&mut ec).is_none() {}
- }
-
- // Do a single read from `self.mem` to make sure the Memory Access noise
- // source is not optimised out.
- black_box(ec.mem[0]);
-
- self.stir_pool();
- self.data
- }
-
- /// Basic quality tests on the timer, by measuring CPU timing jitter a few
- /// hundred times.
- ///
- /// If succesful, this will return the estimated number of rounds necessary
- /// to collect 64 bits of entropy. Otherwise a [`TimerError`] with the cause
- /// of the failure will be returned.
- ///
- /// [`TimerError`]: enum.TimerError.html
- pub fn test_timer(&mut self) -> Result<u8, TimerError> {
- debug!("JitterRng: testing timer ...");
- // We could add a check for system capabilities such as `clock_getres`
- // or check for `CONFIG_X86_TSC`, but it does not make much sense as the
- // following sanity checks verify that we have a high-resolution timer.
-
- let mut delta_sum = 0;
- let mut old_delta = 0;
-
- let mut time_backwards = 0;
- let mut count_mod = 0;
- let mut count_stuck = 0;
-
- let mut ec = EcState {
- prev_time: (self.timer)(),
- last_delta: 0,
- last_delta2: 0,
- mem: [0; MEMORY_SIZE],
- };
-
- // TESTLOOPCOUNT needs some loops to identify edge systems.
- // 100 is definitely too little.
- const TESTLOOPCOUNT: u64 = 300;
- const CLEARCACHE: u64 = 100;
-
- for i in 0..(CLEARCACHE + TESTLOOPCOUNT) {
- // Measure time delta of core entropy collection logic
- let time = (self.timer)();
- self.memaccess(&mut ec.mem, true);
- self.lfsr_time(time, true);
- let time2 = (self.timer)();
-
- // Test whether timer works
- if time == 0 || time2 == 0 {
- return Err(TimerError::NoTimer);
- }
- let delta = time2.wrapping_sub(time) as i64 as i32;
-
- // Test whether timer is fine grained enough to provide delta even
- // when called shortly after each other -- this implies that we also
- // have a high resolution timer
- if delta == 0 {
- return Err(TimerError::CoarseTimer);
- }
-
- // Up to here we did not modify any variable that will be
- // evaluated later, but we already performed some work. Thus we
- // already have had an impact on the caches, branch prediction,
- // etc. with the goal to clear it to get the worst case
- // measurements.
- if i < CLEARCACHE { continue; }
-
- if ec.stuck(delta) { count_stuck += 1; }
-
- // Test whether we have an increasing timer.
- if !(time2 > time) { time_backwards += 1; }
-
- // Count the number of times the counter increases in steps of 100ns
- // or greater.
- if (delta % 100) == 0 { count_mod += 1; }
-
- // Ensure that we have a varying delta timer which is necessary for
- // the calculation of entropy -- perform this check only after the
- // first loop is executed as we need to prime the old_delta value
- delta_sum += (delta - old_delta).abs() as u64;
- old_delta = delta;
- }
-
- // Do a single read from `self.mem` to make sure the Memory Access noise
- // source is not optimised out.
- black_box(ec.mem[0]);
-
- // We allow the time to run backwards for up to three times.
- // This can happen if the clock is being adjusted by NTP operations.
- // If such an operation just happens to interfere with our test, it
- // should not fail. The value of 3 should cover the NTP case being
- // performed during our test run.
- if time_backwards > 3 {
- return Err(TimerError::NotMonotonic);
- }
-
- // Test that the available amount of entropy per round does not get to
- // low. We expect 1 bit of entropy per round as a reasonable minimum
- // (although less is possible, it means the collector loop has to run
- // much more often).
- // `assert!(delta_average >= log2(1))`
- // `assert!(delta_sum / TESTLOOPCOUNT >= 1)`
- // `assert!(delta_sum >= TESTLOOPCOUNT)`
- if delta_sum < TESTLOOPCOUNT {
- return Err(TimerError::TinyVariantions);
- }
-
- // Ensure that we have variations in the time stamp below 100 for at
- // least 10% of all checks -- on some platforms, the counter increments
- // in multiples of 100, but not always
- if count_mod > (TESTLOOPCOUNT * 9 / 10) {
- return Err(TimerError::CoarseTimer);
- }
-
- // If we have more than 90% stuck results, then this Jitter RNG is
- // likely to not work well.
- if count_stuck > (TESTLOOPCOUNT * 9 / 10) {
- return Err(TimerError::TooManyStuck);
- }
-
- // Estimate the number of `measure_jitter` rounds necessary for 64 bits
- // of entropy.
- //
- // We don't try very hard to come up with a good estimate of the
- // available bits of entropy per round here for two reasons:
- // 1. Simple estimates of the available bits (like Shannon entropy) are
- // too optimistic.
- // 2. Unless we want to waste a lot of time during intialization, there
- // only a small number of samples are available.
- //
- // Therefore we use a very simple and conservative estimate:
- // `let bits_of_entropy = log2(delta_average) / 2`.
- //
- // The number of rounds `measure_jitter` should run to collect 64 bits
- // of entropy is `64 / bits_of_entropy`.
- let delta_average = delta_sum / TESTLOOPCOUNT;
-
- if delta_average >= 16 {
- let log2 = 64 - delta_average.leading_zeros();
- // Do something similar to roundup(64/(log2/2)):
- Ok( ((64u32 * 2 + log2 - 1) / log2) as u8)
- } else {
- // For values < 16 the rounding error becomes too large, use a
- // lookup table.
- // Values 0 and 1 are invalid, and filtered out by the
- // `delta_sum < TESTLOOPCOUNT` test above.
- let log2_lookup = [0, 0, 128, 81, 64, 56, 50, 46,
- 43, 41, 39, 38, 36, 35, 34, 33];
- Ok(log2_lookup[delta_average as usize])
- }
- }
-
- /// Statistical test: return the timer delta of one normal run of the
- /// `JitterRng` entropy collector.
- ///
- /// Setting `var_rounds` to `true` will execute the memory access and the
- /// CPU jitter noice sources a variable amount of times (just like a real
- /// `JitterRng` round).
- ///
- /// Setting `var_rounds` to `false` will execute the noice sources the
- /// minimal number of times. This can be used to measure the minimum amount
- /// of entropy one round of the entropy collector can collect in the worst
- /// case.
- ///
- /// See [Quality testing](struct.JitterRng.html#quality-testing) on how to
- /// use `timer_stats` to test the quality of `JitterRng`.
- pub fn timer_stats(&mut self, var_rounds: bool) -> i64 {
- let mut mem = [0; MEMORY_SIZE];
-
- let time = (self.timer)();
- self.memaccess(&mut mem, var_rounds);
- self.lfsr_time(time, var_rounds);
- let time2 = (self.timer)();
- time2.wrapping_sub(time) as i64
- }
-}
-
-#[cfg(feature="std")]
-mod platform {
- #[cfg(not(any(target_os = "macos", target_os = "ios",
- target_os = "windows",
- target_arch = "wasm32")))]
- pub fn get_nstime() -> u64 {
- use std::time::{SystemTime, UNIX_EPOCH};
-
- let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
- // The correct way to calculate the current time is
- // `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64`
- // But this is faster, and the difference in terms of entropy is
- // negligible (log2(10^9) == 29.9).
- dur.as_secs() << 30 | dur.subsec_nanos() as u64
- }
-
- #[cfg(any(target_os = "macos", target_os = "ios"))]
- pub fn get_nstime() -> u64 {
- extern crate libc;
- // On Mac OS and iOS std::time::SystemTime only has 1000ns resolution.
- // We use `mach_absolute_time` instead. This provides a CPU dependent
- // unit, to get real nanoseconds the result should by multiplied by
- // numer/denom from `mach_timebase_info`.
- // But we are not interested in the exact nanoseconds, just entropy. So
- // we use the raw result.
- unsafe { libc::mach_absolute_time() }
- }
-
- #[cfg(target_os = "windows")]
- pub fn get_nstime() -> u64 {
- extern crate winapi;
- unsafe {
- let mut t = super::mem::zeroed();
- winapi::um::profileapi::QueryPerformanceCounter(&mut t);
- *t.QuadPart() as u64
- }
- }
-}
-
-// A function that is opaque to the optimizer to assist in avoiding dead-code
-// elimination. Taken from `bencher`.
-fn black_box<T>(dummy: T) -> T {
- unsafe {
- let ret = ptr::read_volatile(&dummy);
- mem::forget(dummy);
- ret
- }
-}
-
-impl RngCore for JitterRng {
- fn next_u32(&mut self) -> u32 {
- // We want to use both parts of the generated entropy
- if self.data_half_used {
- self.data_half_used = false;
- (self.data >> 32) as u32
- } else {
- self.data = self.next_u64();
- self.data_half_used = true;
- self.data as u32
- }
- }
-
- fn next_u64(&mut self) -> u64 {
- self.data_half_used = false;
- self.gen_entropy()
- }
-
- fn fill_bytes(&mut self, dest: &mut [u8]) {
- // Fill using `next_u32`. This is faster for filling small slices (four
- // bytes or less), while the overhead is negligible.
- //
- // This is done especially for wrappers that implement `next_u32`
- // themselves via `fill_bytes`.
- impls::fill_bytes_via_next(self, dest)
- }
-
- fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
- Ok(self.fill_bytes(dest))
- }
-}
-
-impl CryptoRng for JitterRng {}
-
-#[cfg(test)]
-mod test_jitter_init {
- use super::JitterRng;
-
- #[cfg(all(feature="std", not(target_arch = "wasm32")))]
- #[test]
- fn test_jitter_init() {
- use RngCore;
- // Because this is a debug build, measurements here are not representive
- // of the final release build.
- // Don't fail this test if initializing `JitterRng` fails because of a
- // bad timer (the timer from the standard library may not have enough
- // accuracy on all platforms).
- match JitterRng::new() {
- Ok(ref mut rng) => {
- // false positives are possible, but extremely unlikely
- assert!(rng.next_u32() | rng.next_u32() != 0);
- },
- Err(_) => {},
- }
- }
-
- #[test]
- fn test_jitter_bad_timer() {
- fn bad_timer() -> u64 { 0 }
- let mut rng = JitterRng::new_with_timer(bad_timer);
- assert!(rng.test_timer().is_err());
- }
-}
diff --git a/rand/src/rngs/mock.rs b/rand/src/rngs/mock.rs
index 3c9a994..b4081da 100644
--- a/rand/src/rngs/mock.rs
+++ b/rand/src/rngs/mock.rs
@@ -39,21 +39,26 @@ impl StepRng {
}
impl RngCore for StepRng {
+ #[inline]
fn next_u32(&mut self) -> u32 {
self.next_u64() as u32
}
+ #[inline]
fn next_u64(&mut self) -> u64 {
let result = self.v;
self.v = self.v.wrapping_add(self.a);
result
}
+ #[inline]
fn fill_bytes(&mut self, dest: &mut [u8]) {
impls::fill_bytes_via_next(self, dest);
}
+ #[inline]
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
- Ok(self.fill_bytes(dest))
+ self.fill_bytes(dest);
+ Ok(())
}
}
diff --git a/rand/src/rngs/mod.rs b/rand/src/rngs/mod.rs
index 847fc94..abf3243 100644
--- a/rand/src/rngs/mod.rs
+++ b/rand/src/rngs/mod.rs
@@ -6,177 +6,114 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-//! Random number generators and adapters for common usage:
-//!
-//! - [`ThreadRng`], a fast, secure, auto-seeded thread-local generator
-//! - [`StdRng`] and [`SmallRng`], algorithms to cover typical usage
-//! - [`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)
-//!
-//! Computers are inherently deterministic, so to get *random* numbers one
-//! either has to use a hardware generator or collect bits of *entropy* from
-//! various sources (e.g. event timestamps, or jitter). This is a relatively
-//! slow and complicated operation.
-//!
-//! Generally the operating system will collect some entropy, remove bias, and
-//! use that to seed its own PRNG; [`OsRng`] provides an interface to this.
-//! [`JitterRng`] is an entropy collector included with Rand that measures
-//! jitter in the CPU execution time, and jitter in memory access time.
-//! [`EntropyRng`] is a wrapper that uses the best entropy source that is
-//! available.
-//!
-//! ## Pseudo-random number generators
-//!
-//! What is commonly used instead of "true" random number renerators, are
-//! *pseudo-random number generators* (PRNGs), deterministic algorithms that
-//! produce an infinite stream of pseudo-random numbers from a small random
-//! seed. PRNGs are faster, and have better provable properties. The numbers
-//! produced can be statistically of very high quality and can be impossible to
-//! predict. (They can also have obvious correlations and be trivial to predict;
-//! quality varies.)
-//!
-//! There are two different types of PRNGs: those developed for simulations
-//! and statistics, and those developed for use in cryptography; the latter are
-//! called Cryptographically Secure PRNGs (CSPRNG or CPRNG). Both types can
-//! have good statistical quality but the latter also have to be impossible to
-//! predict, even after seeing many previous output values. Rand provides a good
-//! default algorithm from each class:
-//!
-//! - [`SmallRng`] is a PRNG chosen for low memory usage, high performance and
-//! good statistical quality.
-//! - [`StdRng`] is a CSPRNG chosen for good performance and trust of security
-//! (based on reviews, maturity and usage). The current algorithm is HC-128,
-//! which is one of the recommendations by ECRYPT's eSTREAM project.
-//!
-//! The above PRNGs do not cover all use-cases; more algorithms can be found in
-//! the [`prng` module], as well as in several other crates. For example, you
-//! may wish a CSPRNG with significantly lower memory usage than [`StdRng`]
-//! while being less concerned about performance, in which case [`ChaChaRng`]
-//! is a good choice.
-//!
-//! One complexity is that the internal state of a PRNG must change with every
-//! generated number. For APIs this generally means a mutable reference to the
-//! state of the PRNG has to be passed around.
-//!
-//! A solution is [`ThreadRng`]. This is a thread-local implementation of
-//! [`StdRng`] with automatic seeding on first use. It is the best choice if you
-//! "just" want a convenient, secure, fast random number source. Use via the
-//! [`thread_rng`] function, which gets a reference to the current thread's
-//! local instance.
-//!
-//! ## Seeding
-//!
-//! As mentioned above, PRNGs require a random seed in order to produce random
-//! output. This is especially important for CSPRNGs, which are still
-//! deterministic algorithms, thus can only be secure if their seed value is
-//! also secure. To seed a PRNG, use one of:
-//!
-//! - [`FromEntropy::from_entropy`]; this is the most convenient way to seed
-//! with fresh, secure random data.
-//! - [`SeedableRng::from_rng`]; this allows seeding from another PRNG or
-//! from an entropy source such as [`EntropyRng`].
-//! - [`SeedableRng::from_seed`]; this is mostly useful if you wish to be able
-//! to reproduce the output sequence by using a fixed seed. (Don't use
-//! [`StdRng`] or [`SmallRng`] in this case since different algorithms may be
-//! used by future versions of Rand; use an algorithm from the
-//! [`prng` module].)
-//!
-//! ## Conclusion
-//!
-//! - [`thread_rng`] is what you often want to use.
-//! - If you want more control, flexibility, or better performance, use
-//! [`StdRng`], [`SmallRng`] or an algorithm from the [`prng` module].
-//! - Use [`FromEntropy::from_entropy`] to seed new PRNGs.
-//! - If you need reproducibility, use [`SeedableRng::from_seed`] combined with
-//! a named PRNG.
-//!
-//! More information and notes on cryptographic security can be found
-//! in the [`prng` module].
-//!
-//! ## Examples
-//!
-//! Examples of seeding PRNGs:
-//!
-//! ```
-//! use rand::prelude::*;
-//! # use rand::Error;
-//!
-//! // StdRng seeded securely by the OS or local entropy collector:
-//! let mut rng = StdRng::from_entropy();
-//! # let v: u32 = rng.gen();
-//!
-//! // SmallRng seeded from thread_rng:
-//! # fn try_inner() -> Result<(), Error> {
-//! let mut rng = SmallRng::from_rng(thread_rng())?;
-//! # let v: u32 = rng.gen();
-//! # Ok(())
-//! # }
-//! # try_inner().unwrap();
-//!
-//! // SmallRng seeded by a constant, for deterministic results:
-//! let seed = [1,2,3,4, 5,6,7,8, 9,10,11,12, 13,14,15,16]; // byte array
-//! let mut rng = SmallRng::from_seed(seed);
-//! # let v: u32 = rng.gen();
-//! ```
-//!
-//!
-//! # Implementing custom RNGs
-//!
-//! If you want to implement custom RNG, see the [`rand_core`] crate. The RNG
-//! will have to implement the [`RngCore`] trait, where the [`Rng`] trait is
-//! build on top of.
-//!
-//! If the RNG needs seeding, also implement the [`SeedableRng`] trait.
-//!
-//! [`CryptoRng`] is a marker trait cryptographically secure PRNGs can
-//! implement.
-//!
-//!
-// This module:
-//! [`ThreadRng`]: struct.ThreadRng.html
-//! [`StdRng`]: struct.StdRng.html
-//! [`SmallRng`]: struct.SmallRng.html
-//! [`EntropyRng`]: struct.EntropyRng.html
-//! [`OsRng`]: struct.OsRng.html
-//! [`JitterRng`]: struct.JitterRng.html
-// Other traits and functions:
-//! [`rand_core`]: https://crates.io/crates/rand_core
-//! [`prng` module]: ../prng/index.html
-//! [`CryptoRng`]: ../trait.CryptoRng.html
-//! [`FromEntropy`]: ../trait.FromEntropy.html
-//! [`FromEntropy::from_entropy`]: ../trait.FromEntropy.html#tymethod.from_entropy
-//! [`RngCore`]: ../trait.RngCore.html
-//! [`Rng`]: ../trait.Rng.html
-//! [`SeedableRng`]: ../trait.SeedableRng.html
-//! [`SeedableRng::from_rng`]: ../trait.SeedableRng.html#tymethod.from_rng
-//! [`SeedableRng::from_seed`]: ../trait.SeedableRng.html#tymethod.from_seed
-//! [`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`]: ../../rand_chacha/struct.ChaChaRng.html
+//! Random number generators and adapters
+//!
+//! ## Background: Random number generators (RNGs)
+//!
+//! Computers cannot produce random numbers from nowhere. We classify
+//! random number generators as follows:
+//!
+//! - "True" random number generators (TRNGs) use hard-to-predict data sources
+//! (e.g. the high-resolution parts of event timings and sensor jitter) to
+//! harvest random bit-sequences, apply algorithms to remove bias and
+//! estimate available entropy, then combine these bits into a byte-sequence
+//! or an entropy pool. This job is usually done by the operating system or
+//! a hardware generator (HRNG).
+//! - "Pseudo"-random number generators (PRNGs) use algorithms to transform a
+//! seed into a sequence of pseudo-random numbers. These generators can be
+//! fast and produce well-distributed unpredictable random numbers (or not).
+//! They are usually deterministic: given algorithm and seed, the output
+//! sequence can be reproduced. They have finite period and eventually loop;
+//! with many algorithms this period is fixed and can be proven sufficiently
+//! long, while others are chaotic and the period depends on the seed.
+//! - "Cryptographically secure" pseudo-random number generators (CSPRNGs)
+//! are the sub-set of PRNGs which are secure. Security of the generator
+//! relies both on hiding the internal state and using a strong algorithm.
+//!
+//! ## Traits and functionality
+//!
+//! All RNGs implement the [`RngCore`] trait, as a consequence of which the
+//! [`Rng`] extension trait is automatically implemented. Secure RNGs may
+//! additionally implement the [`CryptoRng`] trait.
+//!
+//! All PRNGs require a seed to produce their random number sequence. The
+//! [`SeedableRng`] trait provides three ways of constructing PRNGs:
+//!
+//! - `from_seed` accepts a type specific to the PRNG
+//! - `from_rng` allows a PRNG to be seeded from any other RNG
+//! - `seed_from_u64` allows any PRNG to be seeded from a `u64` insecurely
+//! - `from_entropy` securely seeds a PRNG from fresh entropy
+//!
+//! Use the [`rand_core`] crate when implementing your own RNGs.
+//!
+//! ## Our generators
+//!
+//! This crate provides several random number generators:
+//!
+//! - [`OsRng`] is an interface to the operating system's random number
+//! source. Typically the operating system uses a CSPRNG with entropy
+//! provided by a TRNG and some type of on-going re-seeding.
+//! - [`ThreadRng`], provided by the [`thread_rng`] function, is a handle to a
+//! thread-local CSPRNG with periodic seeding from [`OsRng`]. Because this
+//! is local, it is typically much faster than [`OsRng`]. It should be
+//! secure, though the paranoid may prefer [`OsRng`].
+//! - [`StdRng`] is a CSPRNG chosen for good performance and trust of security
+//! (based on reviews, maturity and usage). The current algorithm is ChaCha20,
+//! which is well established and rigorously analysed.
+//! [`StdRng`] provides the algorithm used by [`ThreadRng`] but without
+//! periodic reseeding.
+//! - [`SmallRng`] is an **insecure** PRNG designed to be fast, simple, require
+//! little memory, and have good output quality.
+//!
+//! The algorithms selected for [`StdRng`] and [`SmallRng`] may change in any
+//! release and may be platform-dependent, therefore they should be considered
+//! **not reproducible**.
+//!
+//! ## Additional generators
+//!
+//! **TRNGs**: The [`rdrand`] crate provides an interface to the RDRAND and
+//! RDSEED instructions available in modern Intel and AMD CPUs.
+//! The [`rand_jitter`] crate provides a user-space implementation of
+//! entropy harvesting from CPU timer jitter, but is very slow and has
+//! [security issues](https://github.com/rust-random/rand/issues/699).
+//!
+//! **PRNGs**: Several companion crates are available, providing individual or
+//! families of PRNG algorithms. These provide the implementations behind
+//! [`StdRng`] and [`SmallRng`] but can also be used directly, indeed *should*
+//! be used directly when **reproducibility** matters.
+//! Some suggestions are: [`rand_chacha`], [`rand_pcg`], [`rand_xoshiro`].
+//! A full list can be found by searching for crates with the [`rng` tag].
+//!
+//! [`SmallRng`]: rngs::SmallRng
+//! [`StdRng`]: rngs::StdRng
+//! [`OsRng`]: rngs::OsRng
+//! [`ThreadRng`]: rngs::ThreadRng
+//! [`mock::StepRng`]: rngs::mock::StepRng
+//! [`adapter::ReadRng`]: rngs::adapter::ReadRng
+//! [`adapter::ReseedingRng`]: rngs::adapter::ReseedingRng
+//! [`rdrand`]: https://crates.io/crates/rdrand
+//! [`rand_jitter`]: https://crates.io/crates/rand_jitter
+//! [`rand_chacha`]: https://crates.io/crates/rand_chacha
+//! [`rand_pcg`]: https://crates.io/crates/rand_pcg
+//! [`rand_xoshiro`]: https://crates.io/crates/rand_xoshiro
+//! [`rng` tag]: https://crates.io/keywords/rng
pub mod adapter;
#[cfg(feature="std")] mod entropy;
-mod jitter;
pub mod mock; // Public so we don't export `StepRng` directly, making it a bit
// more clear it is intended for testing.
+#[cfg(feature="small_rng")]
mod small;
mod std;
#[cfg(feature="std")] pub(crate) mod thread;
-
-pub use self::jitter::{JitterRng, TimerError};
+#[allow(deprecated)]
#[cfg(feature="std")] pub use self::entropy::EntropyRng;
+#[cfg(feature="small_rng")]
pub use self::small::SmallRng;
pub use self::std::StdRng;
#[cfg(feature="std")] pub use self::thread::ThreadRng;
-#[cfg(feature="rand_os")]
-pub use rand_os::OsRng;
+#[cfg(feature="getrandom")] pub use rand_core::OsRng;
diff --git a/rand/src/rngs/small.rs b/rand/src/rngs/small.rs
index b652c8c..6571363 100644
--- a/rand/src/rngs/small.rs
+++ b/rand/src/rngs/small.rs
@@ -8,35 +8,42 @@
//! A small fast RNG
-use {RngCore, SeedableRng, Error};
+use rand_core::{RngCore, SeedableRng, Error};
-#[cfg(all(all(rustc_1_26, not(target_os = "emscripten")), target_pointer_width = "64"))]
-type Rng = ::rand_pcg::Pcg64Mcg;
-#[cfg(not(all(all(rustc_1_26, not(target_os = "emscripten")), target_pointer_width = "64")))]
-type Rng = ::rand_pcg::Pcg32;
+#[cfg(all(not(target_os = "emscripten"), target_pointer_width = "64"))]
+type Rng = rand_pcg::Pcg64Mcg;
+#[cfg(not(all(not(target_os = "emscripten"), target_pointer_width = "64")))]
+type Rng = rand_pcg::Pcg32;
-/// An RNG recommended when small state, cheap initialization and good
-/// performance are required. The PRNG algorithm in `SmallRng` is chosen to be
-/// efficient on the current platform, **without consideration for cryptography
-/// or security**. The size of its state is much smaller than for [`StdRng`].
+/// A small-state, fast non-crypto PRNG
///
-/// Reproducibility of output from this generator is however not required, thus
-/// future library versions may use a different internal generator with
-/// different output. Further, this generator may not be portable and can
-/// produce different output depending on the architecture. If you require
-/// reproducible output, use a named RNG. Refer to the documentation on the
-/// [`prng` module](../prng/index.html).
+/// `SmallRng` may be a good choice when a PRNG with small state, cheap
+/// initialization, good statistical quality and good performance are required.
+/// It is **not** a good choice when security against prediction or
+/// reproducibility are important.
///
-/// The current algorithm is [`Pcg64Mcg`] on 64-bit platforms with Rust version
-/// 1.26 and later, or [`Pcg32`] otherwise.
+/// This PRNG is **feature-gated**: to use, you must enable the crate feature
+/// `small_rng`.
+///
+/// The algorithm is deterministic but should not be considered reproducible
+/// due to dependence on platform and possible replacement in future
+/// library versions. For a reproducible generator, use a named PRNG from an
+/// external crate, e.g. [rand_pcg] or [rand_chacha].
+/// Refer also to [The Book](https://rust-random.github.io/book/guide-rngs.html).
+///
+/// The PRNG algorithm in `SmallRng` is chosen to be
+/// efficient on the current platform, without consideration for cryptography
+/// or security. The size of its state is much smaller than [`StdRng`].
+/// The current algorithm is [`Pcg64Mcg`](rand_pcg::Pcg64Mcg) on 64-bit
+/// platforms and [`Pcg32`](rand_pcg::Pcg32) on 32-bit platforms. Both are
+/// implemented by the [rand_pcg] crate.
///
/// # Examples
///
-/// Initializing `SmallRng` with a random seed can be done using [`FromEntropy`]:
+/// Initializing `SmallRng` with a random seed can be done using [`SeedableRng::from_entropy`]:
///
/// ```
-/// # use rand::Rng;
-/// use rand::FromEntropy;
+/// use rand::{Rng, SeedableRng};
/// use rand::rngs::SmallRng;
///
/// // Create small, cheap to initialize and fast RNG with a random seed.
@@ -64,11 +71,10 @@ type Rng = ::rand_pcg::Pcg32;
/// .collect();
/// ```
///
-/// [`FromEntropy`]: ../trait.FromEntropy.html
-/// [`StdRng`]: struct.StdRng.html
-/// [`thread_rng`]: ../fn.thread_rng.html
-/// [`Pcg64Mcg`]: ../../rand_pcg/type.Pcg64Mcg.html
-/// [`Pcg32`]: ../../rand_pcg/type.Pcg32.html
+/// [`StdRng`]: crate::rngs::StdRng
+/// [`thread_rng`]: crate::thread_rng
+/// [rand_chacha]: https://crates.io/crates/rand_chacha
+/// [rand_pcg]: https://crates.io/crates/rand_pcg
#[derive(Clone, Debug)]
pub struct SmallRng(Rng);
@@ -83,10 +89,12 @@ impl RngCore for SmallRng {
self.0.next_u64()
}
+ #[inline(always)]
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.0.fill_bytes(dest);
}
+ #[inline(always)]
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
self.0.try_fill_bytes(dest)
}
@@ -95,10 +103,12 @@ impl RngCore for SmallRng {
impl SeedableRng for SmallRng {
type Seed = <Rng as SeedableRng>::Seed;
+ #[inline(always)]
fn from_seed(seed: Self::Seed) -> Self {
SmallRng(Rng::from_seed(seed))
}
+ #[inline(always)]
fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> {
Rng::from_rng(rng).map(SmallRng)
}
diff --git a/rand/src/rngs/std.rs b/rand/src/rngs/std.rs
index ce1658b..22e08ae 100644
--- a/rand/src/rngs/std.rs
+++ b/rand/src/rngs/std.rs
@@ -8,25 +8,30 @@
//! The standard RNG
-use {RngCore, CryptoRng, Error, SeedableRng};
-use rand_hc::Hc128Rng;
+use crate::{RngCore, CryptoRng, Error, SeedableRng};
+
+#[cfg(target_os = "emscripten")] pub(crate) use rand_hc::Hc128Core as Core;
+#[cfg(not(target_os = "emscripten"))] pub(crate) use rand_chacha::ChaCha20Core as Core;
+#[cfg(target_os = "emscripten")] use rand_hc::Hc128Rng as Rng;
+#[cfg(not(target_os = "emscripten"))] use rand_chacha::ChaCha20Rng as Rng;
/// The standard RNG. The PRNG algorithm in `StdRng` is chosen to be efficient
/// on the current platform, to be statistically strong and unpredictable
/// (meaning a cryptographically secure PRNG).
///
-/// The current algorithm used on all platforms is [HC-128].
+/// The current algorithm used is the ChaCha block cipher with either 20 or 12
+/// rounds (see the `stdrng_*` feature flags, documented in the README).
+/// This may change as new evidence of cipher security and performance
+/// becomes available.
///
-/// Reproducibility of output from this generator is however not required, thus
-/// future library versions may use a different internal generator with
-/// different output. Further, this generator may not be portable and can
-/// produce different output depending on the architecture. If you require
-/// reproducible output, use a named RNG, for example [`ChaChaRng`].
+/// The algorithm is deterministic but should not be considered reproducible
+/// due to dependence on configuration and possible replacement in future
+/// library versions. For a secure reproducible generator, we recommend use of
+/// the [rand_chacha] crate directly.
///
-/// [HC-128]: ../../rand_hc/struct.Hc128Rng.html
-/// [`ChaChaRng`]: ../../rand_chacha/struct.ChaChaRng.html
+/// [rand_chacha]: https://crates.io/crates/rand_chacha
#[derive(Clone, Debug)]
-pub struct StdRng(Hc128Rng);
+pub struct StdRng(Rng);
impl RngCore for StdRng {
#[inline(always)]
@@ -39,24 +44,28 @@ impl RngCore for StdRng {
self.0.next_u64()
}
+ #[inline(always)]
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.0.fill_bytes(dest);
}
+ #[inline(always)]
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
self.0.try_fill_bytes(dest)
}
}
impl SeedableRng for StdRng {
- type Seed = <Hc128Rng as SeedableRng>::Seed;
+ type Seed = <Rng as SeedableRng>::Seed;
+ #[inline(always)]
fn from_seed(seed: Self::Seed) -> Self {
- StdRng(Hc128Rng::from_seed(seed))
+ StdRng(Rng::from_seed(seed))
}
+ #[inline(always)]
fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> {
- Hc128Rng::from_rng(rng).map(StdRng)
+ Rng::from_rng(rng).map(StdRng)
}
}
@@ -65,17 +74,27 @@ impl CryptoRng for StdRng {}
#[cfg(test)]
mod test {
- use {RngCore, SeedableRng};
- use rngs::StdRng;
+ use crate::{RngCore, SeedableRng};
+ use crate::rngs::StdRng;
#[test]
fn test_stdrng_construction() {
+ // Test value-stability of StdRng. This is expected to break any time
+ // the algorithm is changed.
let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0];
- let mut rng1 = StdRng::from_seed(seed);
- assert_eq!(rng1.next_u64(), 15759097995037006553);
- let mut rng2 = StdRng::from_rng(rng1).unwrap();
- assert_eq!(rng2.next_u64(), 6766915756997287454);
+ #[cfg(any(feature="stdrng_strong", not(feature="stdrng_fast")))]
+ let target = [3950704604716924505, 5573172343717151650];
+ #[cfg(all(not(feature="stdrng_strong"), feature="stdrng_fast"))]
+ let target = [10719222850664546238, 14064965282130556830];
+
+ let mut rng0 = StdRng::from_seed(seed);
+ let x0 = rng0.next_u64();
+
+ let mut rng1 = StdRng::from_rng(rng0).unwrap();
+ let x1 = rng1.next_u64();
+
+ assert_eq!([x0, x1], target);
}
}
diff --git a/rand/src/rngs/thread.rs b/rand/src/rngs/thread.rs
index 7977d85..2006f41 100644
--- a/rand/src/rngs/thread.rs
+++ b/rand/src/rngs/thread.rs
@@ -9,11 +9,12 @@
//! Thread-local random number generator
use std::cell::UnsafeCell;
+use std::ptr::NonNull;
-use {RngCore, CryptoRng, SeedableRng, Error};
-use rngs::adapter::ReseedingRng;
-use rngs::EntropyRng;
-use rand_hc::Hc128Core;
+use crate::{RngCore, CryptoRng, SeedableRng, Error};
+use crate::rngs::adapter::ReseedingRng;
+use crate::rngs::OsRng;
+use super::std::Core;
// Rationale for using `UnsafeCell` in `ThreadRng`:
//
@@ -28,61 +29,43 @@ use rand_hc::Hc128Core;
// completely under our control. We just have to ensure none of them use
// `ThreadRng` internally, which is nonsensical anyway. We should also never run
// `ThreadRng` in destructors of its implementation, which is also nonsensical.
-//
-// The additional `Rc` is not strictly neccesary, and could be removed. For now
-// it ensures `ThreadRng` stays `!Send` and `!Sync`, and implements `Clone`.
-// Number of generated bytes after which to reseed `TreadRng`.
-//
-// The time it takes to reseed HC-128 is roughly equivalent to generating 7 KiB.
-// We pick a treshold here that is large enough to not reduce the average
-// performance too much, but also small enough to not make reseeding something
-// that basically never happens.
-const THREAD_RNG_RESEED_THRESHOLD: u64 = 32*1024*1024; // 32 MiB
+// Number of generated bytes after which to reseed `ThreadRng`.
+// According to benchmarks, reseeding has a noticable impact with thresholds
+// of 32 kB and less. We choose 64 kB to avoid significant overhead.
+const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64;
/// The type returned by [`thread_rng`], essentially just a reference to the
/// PRNG in thread-local memory.
///
-/// `ThreadRng` uses [`ReseedingRng`] wrapping the same PRNG as [`StdRng`],
-/// which is reseeded after generating 32 MiB of random data. A single instance
-/// is cached per thread and the returned `ThreadRng` is a reference to this
-/// instance — hence `ThreadRng` is neither `Send` nor `Sync` but is safe to use
-/// within a single thread. This RNG is seeded and reseeded via [`EntropyRng`]
-/// as required.
+/// `ThreadRng` uses the same PRNG as [`StdRng`] for security and performance.
+/// As hinted by the name, the generator is thread-local. `ThreadRng` is a
+/// handle to this generator and thus supports `Copy`, but not `Send` or `Sync`.
///
-/// Note that the reseeding is done as an extra precaution against entropy
-/// leaks and is in theory unnecessary — to predict `ThreadRng`'s output, an
-/// attacker would have to either determine most of the RNG's seed or internal
-/// state, or crack the algorithm used.
+/// Unlike `StdRng`, `ThreadRng` uses the [`ReseedingRng`] wrapper to reseed
+/// the PRNG from fresh entropy every 64 kiB of random data.
+/// [`OsRng`] is used to provide seed data.
///
-/// Like [`StdRng`], `ThreadRng` is a cryptographically secure PRNG. The current
-/// algorithm used is [HC-128], which is an array-based PRNG that trades memory
-/// usage for better performance. This makes it similar to ISAAC, the algorithm
-/// used in `ThreadRng` before rand 0.5.
+/// Note that the reseeding is done as an extra precaution against side-channel
+/// attacks and mis-use (e.g. if somehow weak entropy were supplied initially).
+/// The PRNG algorithms used are assumed to be secure.
///
-/// Cloning this handle just produces a new reference to the same thread-local
-/// generator.
-///
-/// [`thread_rng`]: ../fn.thread_rng.html
-/// [`ReseedingRng`]: adapter/struct.ReseedingRng.html
-/// [`StdRng`]: struct.StdRng.html
-/// [`EntropyRng`]: struct.EntropyRng.html
-/// [HC-128]: ../../rand_hc/struct.Hc128Rng.html
-#[derive(Clone, Debug)]
+/// [`ReseedingRng`]: crate::rngs::adapter::ReseedingRng
+/// [`StdRng`]: crate::rngs::StdRng
+#[derive(Copy, Clone, Debug)]
pub struct ThreadRng {
- // use of raw pointer implies type is neither Send nor Sync
- rng: *mut ReseedingRng<Hc128Core, EntropyRng>,
+ // inner raw pointer implies type is neither Send nor Sync
+ rng: NonNull<ReseedingRng<Core, OsRng>>,
}
thread_local!(
- static THREAD_RNG_KEY: UnsafeCell<ReseedingRng<Hc128Core, EntropyRng>> = {
- let mut entropy_source = EntropyRng::new();
- let r = Hc128Core::from_rng(&mut entropy_source).unwrap_or_else(|err|
+ static THREAD_RNG_KEY: UnsafeCell<ReseedingRng<Core, OsRng>> = {
+ let r = Core::from_rng(OsRng).unwrap_or_else(|err|
panic!("could not initialize thread_rng: {}", err));
let rng = ReseedingRng::new(r,
THREAD_RNG_RESEED_THRESHOLD,
- entropy_source);
+ OsRng);
UnsafeCell::new(rng)
}
);
@@ -91,38 +74,38 @@ thread_local!(
/// seeded by the system. Intended to be used in method chaining style,
/// e.g. `thread_rng().gen::<i32>()`, or cached locally, e.g.
/// `let mut rng = thread_rng();`. Invoked by the `Default` trait, making
-/// `ThreadRng::default()` equivelent.
+/// `ThreadRng::default()` equivalent.
///
/// For more information see [`ThreadRng`].
-///
-/// [`ThreadRng`]: rngs/struct.ThreadRng.html
pub fn thread_rng() -> ThreadRng {
- ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.get()) }
+ let raw = THREAD_RNG_KEY.with(|t| t.get());
+ let nn = NonNull::new(raw).unwrap();
+ ThreadRng { rng: nn }
}
impl Default for ThreadRng {
fn default() -> ThreadRng {
- ::prelude::thread_rng()
+ crate::prelude::thread_rng()
}
}
impl RngCore for ThreadRng {
#[inline(always)]
fn next_u32(&mut self) -> u32 {
- unsafe { (*self.rng).next_u32() }
+ unsafe { self.rng.as_mut().next_u32() }
}
#[inline(always)]
fn next_u64(&mut self) -> u64 {
- unsafe { (*self.rng).next_u64() }
+ unsafe { self.rng.as_mut().next_u64() }
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
- unsafe { (*self.rng).fill_bytes(dest) }
+ unsafe { self.rng.as_mut().fill_bytes(dest) }
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
- unsafe { (*self.rng).try_fill_bytes(dest) }
+ unsafe { self.rng.as_mut().try_fill_bytes(dest) }
}
}
@@ -133,8 +116,8 @@ impl CryptoRng for ThreadRng {}
mod test {
#[test]
fn test_thread_rng() {
- use Rng;
- let mut r = ::thread_rng();
+ use crate::Rng;
+ let mut r = crate::thread_rng();
r.gen::<i32>();
assert_eq!(r.gen_range(0, 1), 0);
}