From 10e28ca3fc5b34a98a48c1cbbaec871b0c9deeb3 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 27 Feb 2018 17:13:50 +0000 Subject: [PATCH 1/5] Remove next_f32/next_f64 from RngCore --- src/distributions/float.rs | 66 ++++++++++++++++++++++++++++---------- src/distributions/mod.rs | 7 ---- src/lib.rs | 66 -------------------------------------- 3 files changed, 49 insertions(+), 90 deletions(-) diff --git a/src/distributions/float.rs b/src/distributions/float.rs index a18f3dbf241..4f927fbd164 100644 --- a/src/distributions/float.rs +++ b/src/distributions/float.rs @@ -10,6 +10,10 @@ //! Basic floating-point number distributions +use core::mem; +use Rng; +use distributions::{Distribution, Uniform}; + /// A distribution to sample floating point numbers uniformly in the open /// interval `(0, 1)` (not including either endpoint). @@ -52,33 +56,60 @@ pub struct Open01; pub struct Closed01; +// Return the next random f32 selected from the half-open +// interval `[0, 1)`. +// +// This uses a technique described by Saito and Matsumoto at +// MCQMC'08. Given that the IEEE floating point numbers are +// uniformly distributed over [1,2), we generate a number in +// this range and then offset it onto the range [0,1). Our +// choice of bits (masking v. shifting) is arbitrary and +// should be immaterial for high quality generators. For low +// quality generators (ex. LCG), prefer bitshifting due to +// correlation between sequential low order bits. +// +// See: +// A PRNG specialized in double precision floating point numbers using +// an affine transition +// +// * +// * +impl Distribution for Uniform { + fn sample(&self, rng: &mut R) -> f32 { + const UPPER_MASK: u32 = 0x3F800000; + const LOWER_MASK: u32 = 0x7FFFFF; + let tmp = UPPER_MASK | (rng.next_u32() & LOWER_MASK); + let result: f32 = unsafe { mem::transmute(tmp) }; + result - 1.0 + } +} +impl Distribution for Uniform { + fn sample(&self, rng: &mut R) -> f64 { + const UPPER_MASK: u64 = 0x3FF0000000000000; + const LOWER_MASK: u64 = 0xFFFFFFFFFFFFF; + let tmp = UPPER_MASK | (rng.next_u64() & LOWER_MASK); + let result: f64 = unsafe { mem::transmute(tmp) }; + result - 1.0 + } +} + macro_rules! float_impls { - ($mod_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident) => { + ($mod_name:ident, $ty:ty, $mantissa_bits:expr) => { mod $mod_name { use Rng; - use distributions::{Distribution, Uniform}; + use distributions::{Distribution}; use super::{Open01, Closed01}; const SCALE: $ty = (1u64 << $mantissa_bits) as $ty; - impl Distribution<$ty> for Uniform { - /// Generate a floating point number in the half-open - /// interval `[0,1)`. - /// - /// See `Closed01` for the closed interval `[0,1]`, - /// and `Open01` for the open interval `(0,1)`. - #[inline] - fn sample(&self, rng: &mut R) -> $ty { - rng.$method_name() - } - } impl Distribution<$ty> for Open01 { #[inline] fn sample(&self, rng: &mut R) -> $ty { // add 0.5 * epsilon, so that smallest number is // greater than 0, and largest number is still // less than 1, specifically 1 - 0.5 * epsilon. - rng.$method_name() + 0.5 / SCALE + let x: $ty = rng.gen(); + x + 0.5 / SCALE } } impl Distribution<$ty> for Closed01 { @@ -86,14 +117,15 @@ macro_rules! float_impls { fn sample(&self, rng: &mut R) -> $ty { // rescale so that 1.0 - epsilon becomes 1.0 // precisely. - rng.$method_name() * SCALE / (SCALE - 1.0) + let x: $ty = rng.gen(); + x * SCALE / (SCALE - 1.0) } } } } } -float_impls! { f64_rand_impls, f64, 52, next_f64 } -float_impls! { f32_rand_impls, f32, 23, next_f32 } +float_impls! { f64_rand_impls, f64, 52 } +float_impls! { f32_rand_impls, f32, 23 } #[cfg(test)] diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 85c78e18481..3ff5700a376 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -352,13 +352,6 @@ fn ziggurat( // creating a f64), so we might as well reuse some to save // generating a whole extra random number. (Seems to be 15% // faster.) - // - // This unfortunately misses out on the benefits of direct - // floating point generation if an RNG like dSMFT is - // used. (That is, such RNGs create floats directly, highly - // efficiently and overload next_f32/f64, so by not calling it - // this may be slower than it would be otherwise.) - // FIXME: investigate/optimise for the above. let bits: u64 = rng.gen(); let i = (bits & 0xff) as usize; let f = (bits >> 11) as f64 / SCALE; diff --git a/src/lib.rs b/src/lib.rs index caedb4d842b..928fb7ed1d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -391,52 +391,6 @@ pub trait RngCore { impls::next_u64_via_u32(self) } - /// Return the next random f32 selected from the half-open - /// interval `[0, 1)`. - /// - /// This uses a technique described by Saito and Matsumoto at - /// MCQMC'08. Given that the IEEE floating point numbers are - /// uniformly distributed over [1,2), we generate a number in - /// this range and then offset it onto the range [0,1). Our - /// choice of bits (masking v. shifting) is arbitrary and - /// should be immaterial for high quality generators. For low - /// quality generators (ex. LCG), prefer bitshifting due to - /// correlation between sequential low order bits. - /// - /// See: - /// A PRNG specialized in double precision floating point numbers using - /// an affine transition - /// - /// * - /// * - /// - /// By default this is implemented in terms of `next_u32`, but a - /// random number generator which can generate numbers satisfying - /// the requirements directly can overload this for performance. - /// It is required that the return value lies in `[0, 1)`. - fn next_f32(&mut self) -> f32 { - const UPPER_MASK: u32 = 0x3F800000; - const LOWER_MASK: u32 = 0x7FFFFF; - let tmp = UPPER_MASK | (self.next_u32() & LOWER_MASK); - let result: f32 = unsafe { mem::transmute(tmp) }; - result - 1.0 - } - - /// Return the next random f64 selected from the half-open - /// interval `[0, 1)`. - /// - /// By default this is implemented in terms of `next_u64`, but a - /// random number generator which can generate numbers satisfying - /// the requirements directly can overload this for performance. - /// It is required that the return value lies in `[0, 1)`. - fn next_f64(&mut self) -> f64 { - const UPPER_MASK: u64 = 0x3FF0000000000000; - const LOWER_MASK: u64 = 0xFFFFFFFFFFFFF; - let tmp = UPPER_MASK | (self.next_u64() & LOWER_MASK); - let result: f64 = unsafe { mem::transmute(tmp) }; - result - 1.0 - } - /// Fill `dest` with random data. /// /// Implementations of this trait must implement at least one of @@ -758,16 +712,6 @@ impl<'a, R: RngCore + ?Sized> RngCore for &'a mut R { (**self).next_u64() } - #[inline] - fn next_f32(&mut self) -> f32 { - (**self).next_f32() - } - - #[inline] - fn next_f64(&mut self) -> f64 { - (**self).next_f64() - } - #[inline] fn fill_bytes(&mut self, dest: &mut [u8]) { (**self).fill_bytes(dest) @@ -791,16 +735,6 @@ impl RngCore for Box { (**self).next_u64() } - #[inline] - fn next_f32(&mut self) -> f32 { - (**self).next_f32() - } - - #[inline] - fn next_f64(&mut self) -> f64 { - (**self).next_f64() - } - #[inline] fn fill_bytes(&mut self, dest: &mut [u8]) { (**self).fill_bytes(dest) From c3e3462a98ddaf2cbd74bb07ef6139fbc15bc25b Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 27 Feb 2018 17:21:23 +0000 Subject: [PATCH 2/5] Move thread_rng and co to new module Rationale: lib.rs should not contain a big mess of unrelated code --- src/lib.rs | 129 ++----------------------------------------- src/thread_rng.rs | 136 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 125 deletions(-) create mode 100644 src/thread_rng.rs diff --git a/src/lib.rs b/src/lib.rs index 928fb7ed1d6..1ec38d3c3ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -266,8 +266,6 @@ use core::{marker, mem, slice}; -#[cfg(feature="std")] use std::cell::RefCell; -#[cfg(feature="std")] use std::rc::Rc; #[cfg(all(feature="alloc", not(feature="std")))] use alloc::boxed::Box; // external rngs @@ -283,6 +281,9 @@ pub use prng::Hc128Rng; // error types pub use error::{ErrorKind, Error}; +// convenience and derived rngs +#[cfg(feature="std")] pub use thread_rng::{ThreadRng, thread_rng, random}; + // local use declarations #[cfg(target_pointer_width = "32")] use prng::IsaacRng as IsaacWordRng; @@ -291,7 +292,6 @@ use prng::Isaac64Rng as IsaacWordRng; use distributions::{Distribution, Uniform, Range}; use distributions::range::SampleRange; -#[cfg(feature="std")] use reseeding::ReseedingRng; // public modules pub mod distributions; @@ -317,6 +317,7 @@ pub mod isaac { mod le; mod error; mod prng; +#[cfg(feature="std")] mod thread_rng; /// A type that can be randomly generated using an `Rng`. @@ -1034,79 +1035,6 @@ pub fn weak_rng() -> XorShiftRng { } -/// The type returned by [`thread_rng`], essentially just a reference to the -/// PRNG in thread-local memory. -/// -/// [`thread_rng`]: fn.thread_rng.html -#[cfg(feature="std")] -#[derive(Clone, Debug)] -pub struct ThreadRng { - rng: Rc>>, -} - -#[cfg(feature="std")] -thread_local!( - static THREAD_RNG_KEY: Rc>> = { - const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768; - let mut entropy_source = EntropyRng::new(); - let r = StdRng::from_rng(&mut entropy_source).unwrap_or_else(|err| - panic!("could not initialize thread_rng: {}", err)); - let rng = ReseedingRng::new(r, - THREAD_RNG_RESEED_THRESHOLD, - entropy_source); - Rc::new(RefCell::new(rng)) - } -); - -/// Retrieve the lazily-initialized thread-local random number -/// generator, seeded by the system. Intended to be used in method -/// chaining style, e.g. `thread_rng().gen::()`, or cached locally, e.g. -/// `let mut rng = thread_rng();`. -/// -/// `ThreadRng` uses [`ReseedingRng`] wrapping a [`StdRng`] which is reseeded -/// after generating 32KiB 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. -/// -/// Note that the reseeding is done as an extra precaution against entropy -/// leaks and is in theory unnecessary — to predict `thread_rng`'s output, an -/// attacker would have to either determine most of the RNG's seed or internal -/// state, or crack the algorithm used (ISAAC, which has not been proven -/// cryptographically secure, but has no known attack despite a 20-year old -/// challenge). -/// -/// [`ReseedingRng`]: reseeding/struct.ReseedingRng.html -/// [`StdRng`]: struct.StdRng.html -/// [`EntropyRng`]: struct.EntropyRng.html -#[cfg(feature="std")] -pub fn thread_rng() -> ThreadRng { - ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.clone()) } -} - -#[cfg(feature="std")] -impl RngCore for ThreadRng { - #[inline] - fn next_u32(&mut self) -> u32 { - self.rng.borrow_mut().next_u32() - } - - #[inline] - fn next_u64(&mut self) -> u64 { - self.rng.borrow_mut().next_u64() - } - - #[inline] - fn fill_bytes(&mut self, bytes: &mut [u8]) { - self.rng.borrow_mut().fill_bytes(bytes) - } - - #[inline] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.rng.borrow_mut().try_fill_bytes(dest) - } -} - /// An RNG provided specifically for seeding PRNGs. /// /// Where possible, `EntropyRng` retrieves random data from the operating @@ -1238,55 +1166,6 @@ impl RngCore for EntropyRng { } } -/// Generates a random value using the thread-local random number generator. -/// -/// This is simply a shortcut for `thread_rng().gen()`. See [`thread_rng`] for -/// documentation of the entropy source and [`Rand`] for documentation of -/// distributions and type-specific generation. -/// -/// # Examples -/// -/// ``` -/// let x = rand::random::(); -/// println!("{}", x); -/// -/// let y = rand::random::(); -/// println!("{}", y); -/// -/// if rand::random() { // generates a boolean -/// println!("Better lucky than good!"); -/// } -/// ``` -/// -/// If you're calling `random()` in a loop, caching the generator as in the -/// following example can increase performance. -/// -/// ``` -/// use rand::Rng; -/// -/// let mut v = vec![1, 2, 3]; -/// -/// for x in v.iter_mut() { -/// *x = rand::random() -/// } -/// -/// // can be made faster by caching thread_rng -/// -/// let mut rng = rand::thread_rng(); -/// -/// for x in v.iter_mut() { -/// *x = rng.gen(); -/// } -/// ``` -/// -/// [`thread_rng`]: fn.thread_rng.html -/// [`Rand`]: trait.Rand.html -#[cfg(feature="std")] -#[inline] -pub fn random() -> T where Uniform: Distribution { - thread_rng().gen() -} - /// DEPRECATED: use `seq::sample_iter` instead. /// /// Randomly sample up to `amount` elements from a finite iterator. diff --git a/src/thread_rng.rs b/src/thread_rng.rs new file mode 100644 index 00000000000..c2d04e8f48b --- /dev/null +++ b/src/thread_rng.rs @@ -0,0 +1,136 @@ +// Copyright 2017-2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// https://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Thread-local random number generator + +use std::cell::RefCell; +use std::rc::Rc; + +use {RngCore, StdRng, SeedableRng, EntropyRng}; +use {Distribution, Uniform, Rng, Error}; +use reseeding::ReseedingRng; + + +/// The type returned by [`thread_rng`], essentially just a reference to the +/// PRNG in thread-local memory. +/// +/// [`thread_rng`]: fn.thread_rng.html +#[derive(Clone, Debug)] +pub struct ThreadRng { + rng: Rc>>, +} + +thread_local!( + static THREAD_RNG_KEY: Rc>> = { + const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768; + let mut entropy_source = EntropyRng::new(); + let r = StdRng::from_rng(&mut entropy_source).unwrap_or_else(|err| + panic!("could not initialize thread_rng: {}", err)); + let rng = ReseedingRng::new(r, + THREAD_RNG_RESEED_THRESHOLD, + entropy_source); + Rc::new(RefCell::new(rng)) + } +); + +/// Retrieve the lazily-initialized thread-local random number +/// generator, seeded by the system. Intended to be used in method +/// chaining style, e.g. `thread_rng().gen::()`, or cached locally, e.g. +/// `let mut rng = thread_rng();`. +/// +/// `ThreadRng` uses [`ReseedingRng`] wrapping a [`StdRng`] which is reseeded +/// after generating 32KiB 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. +/// +/// Note that the reseeding is done as an extra precaution against entropy +/// leaks and is in theory unnecessary — to predict `thread_rng`'s output, an +/// attacker would have to either determine most of the RNG's seed or internal +/// state, or crack the algorithm used (ISAAC, which has not been proven +/// cryptographically secure, but has no known attack despite a 20-year old +/// challenge). +/// +/// [`ReseedingRng`]: reseeding/struct.ReseedingRng.html +/// [`StdRng`]: struct.StdRng.html +/// [`EntropyRng`]: struct.EntropyRng.html +pub fn thread_rng() -> ThreadRng { + ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.clone()) } +} + +impl RngCore for ThreadRng { + #[inline] + fn next_u32(&mut self) -> u32 { + self.rng.borrow_mut().next_u32() + } + + #[inline] + fn next_u64(&mut self) -> u64 { + self.rng.borrow_mut().next_u64() + } + + #[inline] + fn fill_bytes(&mut self, bytes: &mut [u8]) { + self.rng.borrow_mut().fill_bytes(bytes) + } + + #[inline] + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + self.rng.borrow_mut().try_fill_bytes(dest) + } +} + +/// Generates a random value using the thread-local random number generator. +/// +/// This is simply a shortcut for `thread_rng().gen()`. See [`thread_rng`] for +/// documentation of the entropy source and [`Rand`] for documentation of +/// distributions and type-specific generation. +/// +/// # Examples +/// +/// ``` +/// let x = rand::random::(); +/// println!("{}", x); +/// +/// let y = rand::random::(); +/// println!("{}", y); +/// +/// if rand::random() { // generates a boolean +/// println!("Better lucky than good!"); +/// } +/// ``` +/// +/// If you're calling `random()` in a loop, caching the generator as in the +/// following example can increase performance. +/// +/// ``` +/// use rand::Rng; +/// +/// let mut v = vec![1, 2, 3]; +/// +/// for x in v.iter_mut() { +/// *x = rand::random() +/// } +/// +/// // can be made faster by caching thread_rng +/// +/// let mut rng = rand::thread_rng(); +/// +/// for x in v.iter_mut() { +/// *x = rng.gen(); +/// } +/// ``` +/// +/// [`thread_rng`]: fn.thread_rng.html +/// [`Rand`]: trait.Rand.html +#[inline] +pub fn random() -> T where Uniform: Distribution { + thread_rng().gen() +} From 9fa0ff61f953674b3a123cf5cc79fee090a95cf9 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 27 Feb 2018 17:28:32 +0000 Subject: [PATCH 3/5] Move EntropyRng to a new module --- src/entropy_rng.rs | 141 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 133 +----------------------------------------- 2 files changed, 143 insertions(+), 131 deletions(-) create mode 100644 src/entropy_rng.rs diff --git a/src/entropy_rng.rs b/src/entropy_rng.rs new file mode 100644 index 00000000000..1a7aadba7f2 --- /dev/null +++ b/src/entropy_rng.rs @@ -0,0 +1,141 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// https://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Entropy generator, or wrapper around external generators + +use {RngCore, Error, impls}; +use {OsRng, JitterRng}; + +/// An RNG provided specifically for seeding 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. +/// +/// This is either a little slow ([`OsRng`] requires a system call) or extremely +/// slow ([`JitterRng`] must use significant CPU time to generate sufficient +/// jitter). It is recommended to only use `EntropyRng` to seed a PRNG (as in +/// [`thread_rng`]) or to generate a small key. +/// +/// [`OsRng`]: os/struct.OsRng.html +/// [`JitterRng`]: jitter/struct.JitterRng.html +/// [`thread_rng`]: fn.thread_rng.html +#[derive(Debug)] +pub struct EntropyRng { + rng: EntropySource, +} + +#[derive(Debug)] +enum EntropySource { + Os(OsRng), + Jitter(JitterRng), + None, +} + +impl EntropyRng { + /// Create a new `EntropyRng`. + /// + /// This method will do no system calls or other initialization routines, + /// 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 { rng: EntropySource::None } + } +} + +impl RngCore for EntropyRng { + fn next_u32(&mut self) -> u32 { + impls::next_u32_via_fill(self) + } + + fn next_u64(&mut self) -> u64 { + impls::next_u64_via_fill(self) + } + + 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)) + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + fn try_os_new(dest: &mut [u8]) -> Result + { + let mut rng = OsRng::new()?; + rng.try_fill_bytes(dest)?; + Ok(rng) + } + + fn try_jitter_new(dest: &mut [u8]) -> Result + { + let mut rng = JitterRng::new()?; + rng.try_fill_bytes(dest)?; + Ok(rng) + } + + let mut switch_rng = None; + match self.rng { + EntropySource::None => { + let os_rng_result = try_os_new(dest); + match os_rng_result { + Ok(os_rng) => { + debug!("EntropyRng: using OsRng"); + switch_rng = Some(EntropySource::Os(os_rng)); + } + Err(os_rng_error) => { + warn!("EntropyRng: OsRng failed [falling back to JitterRng]: {}", + os_rng_error); + match try_jitter_new(dest) { + Ok(jitter_rng) => { + debug!("EntropyRng: using JitterRng"); + switch_rng = Some(EntropySource::Jitter(jitter_rng)); + } + Err(_jitter_error) => { + warn!("EntropyRng: JitterRng failed: {}", + _jitter_error); + return Err(os_rng_error); + } + } + } + } + } + EntropySource::Os(ref mut rng) => { + let os_rng_result = rng.try_fill_bytes(dest); + if let Err(os_rng_error) = os_rng_result { + warn!("EntropyRng: OsRng failed [falling back to JitterRng]: {}", + os_rng_error); + match try_jitter_new(dest) { + Ok(jitter_rng) => { + debug!("EntropyRng: using JitterRng"); + switch_rng = Some(EntropySource::Jitter(jitter_rng)); + } + Err(_jitter_error) => { + warn!("EntropyRng: JitterRng failed: {}", + _jitter_error); + return Err(os_rng_error); + } + } + } + } + EntropySource::Jitter(ref mut rng) => { + if let Ok(os_rng) = try_os_new(dest) { + debug!("EntropyRng: using OsRng"); + switch_rng = Some(EntropySource::Os(os_rng)); + } else { + return rng.try_fill_bytes(dest); // use JitterRng + } + } + } + if let Some(rng) = switch_rng { + self.rng = rng; + } + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 1ec38d3c3ba..138893fe333 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -282,6 +282,7 @@ pub use prng::Hc128Rng; pub use error::{ErrorKind, Error}; // convenience and derived rngs +#[cfg(feature="std")] pub use entropy_rng::EntropyRng; #[cfg(feature="std")] pub use thread_rng::{ThreadRng, thread_rng, random}; // local use declarations @@ -315,6 +316,7 @@ pub mod isaac { // private modules mod le; +#[cfg(feature="std")] mod entropy_rng; mod error; mod prng; #[cfg(feature="std")] mod thread_rng; @@ -1035,137 +1037,6 @@ pub fn weak_rng() -> XorShiftRng { } -/// An RNG provided specifically for seeding 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. -/// -/// This is either a little slow ([`OsRng`] requires a system call) or extremely -/// slow ([`JitterRng`] must use significant CPU time to generate sufficient -/// jitter). It is recommended to only use `EntropyRng` to seed a PRNG (as in -/// [`thread_rng`]) or to generate a small key. -/// -/// [`OsRng`]: os/struct.OsRng.html -/// [`JitterRng`]: jitter/struct.JitterRng.html -/// [`thread_rng`]: fn.thread_rng.html -#[cfg(feature="std")] -#[derive(Debug)] -pub struct EntropyRng { - rng: EntropySource, -} - -#[cfg(feature="std")] -#[derive(Debug)] -enum EntropySource { - Os(OsRng), - Jitter(JitterRng), - None, -} - -#[cfg(feature="std")] -impl EntropyRng { - /// Create a new `EntropyRng`. - /// - /// This method will do no system calls or other initialization routines, - /// 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 { rng: EntropySource::None } - } -} - -#[cfg(feature="std")] -impl RngCore for EntropyRng { - fn next_u32(&mut self) -> u32 { - impls::next_u32_via_fill(self) - } - - fn next_u64(&mut self) -> u64 { - impls::next_u64_via_fill(self) - } - - 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)) - } - - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - fn try_os_new(dest: &mut [u8]) -> Result - { - let mut rng = OsRng::new()?; - rng.try_fill_bytes(dest)?; - Ok(rng) - } - - fn try_jitter_new(dest: &mut [u8]) -> Result - { - let mut rng = JitterRng::new()?; - rng.try_fill_bytes(dest)?; - Ok(rng) - } - - let mut switch_rng = None; - match self.rng { - EntropySource::None => { - let os_rng_result = try_os_new(dest); - match os_rng_result { - Ok(os_rng) => { - debug!("EntropyRng: using OsRng"); - switch_rng = Some(EntropySource::Os(os_rng)); - } - Err(os_rng_error) => { - warn!("EntropyRng: OsRng failed [falling back to JitterRng]: {}", - os_rng_error); - match try_jitter_new(dest) { - Ok(jitter_rng) => { - debug!("EntropyRng: using JitterRng"); - switch_rng = Some(EntropySource::Jitter(jitter_rng)); - } - Err(_jitter_error) => { - warn!("EntropyRng: JitterRng failed: {}", - _jitter_error); - return Err(os_rng_error); - } - } - } - } - } - EntropySource::Os(ref mut rng) => { - let os_rng_result = rng.try_fill_bytes(dest); - if let Err(os_rng_error) = os_rng_result { - warn!("EntropyRng: OsRng failed [falling back to JitterRng]: {}", - os_rng_error); - match try_jitter_new(dest) { - Ok(jitter_rng) => { - debug!("EntropyRng: using JitterRng"); - switch_rng = Some(EntropySource::Jitter(jitter_rng)); - } - Err(_jitter_error) => { - warn!("EntropyRng: JitterRng failed: {}", - _jitter_error); - return Err(os_rng_error); - } - } - } - } - EntropySource::Jitter(ref mut rng) => { - if let Ok(os_rng) = try_os_new(dest) { - debug!("EntropyRng: using OsRng"); - switch_rng = Some(EntropySource::Os(os_rng)); - } else { - return rng.try_fill_bytes(dest); // use JitterRng - } - } - } - if let Some(rng) = switch_rng { - self.rng = rng; - } - Ok(()) - } -} - /// DEPRECATED: use `seq::sample_iter` instead. /// /// Randomly sample up to `amount` elements from a finite iterator. From 4a24d00d1267af8f4b9e1e59ce7ff41587c0cd0e Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 27 Feb 2018 17:36:33 +0000 Subject: [PATCH 4/5] Distributions: add simple tests, remove duplicate tests Changes come from dhardy/master branch --- src/distributions/exponential.rs | 1 - src/distributions/gamma.rs | 5 ----- src/distributions/integer.rs | 28 ++++++++++++++++++++++++++++ src/distributions/normal.rs | 2 -- src/distributions/other.rs | 14 ++++++++++++++ 5 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/distributions/exponential.rs b/src/distributions/exponential.rs index bbdebacab71..b246c3502b1 100644 --- a/src/distributions/exponential.rs +++ b/src/distributions/exponential.rs @@ -107,7 +107,6 @@ mod test { let mut rng = ::test::rng(221); for _ in 0..1000 { assert!(exp.sample(&mut rng) >= 0.0); - assert!(exp.sample(&mut rng) >= 0.0); } } #[test] diff --git a/src/distributions/gamma.rs b/src/distributions/gamma.rs index f9dbbb96139..361e12ed6a2 100644 --- a/src/distributions/gamma.rs +++ b/src/distributions/gamma.rs @@ -318,7 +318,6 @@ mod test { let mut rng = ::test::rng(201); for _ in 0..1000 { chi.sample(&mut rng); - chi.sample(&mut rng); } } #[test] @@ -327,7 +326,6 @@ mod test { let mut rng = ::test::rng(202); for _ in 0..1000 { chi.sample(&mut rng); - chi.sample(&mut rng); } } #[test] @@ -336,7 +334,6 @@ mod test { let mut rng = ::test::rng(203); for _ in 0..1000 { chi.sample(&mut rng); - chi.sample(&mut rng); } } #[test] @@ -351,7 +348,6 @@ mod test { let mut rng = ::test::rng(204); for _ in 0..1000 { f.sample(&mut rng); - f.sample(&mut rng); } } @@ -361,7 +357,6 @@ mod test { let mut rng = ::test::rng(205); for _ in 0..1000 { t.sample(&mut rng); - t.sample(&mut rng); } } } diff --git a/src/distributions/integer.rs b/src/distributions/integer.rs index 8bac2bbfdfd..6b3a903bedf 100644 --- a/src/distributions/integer.rs +++ b/src/distributions/integer.rs @@ -108,3 +108,31 @@ impl Distribution for Uniform { ((rng.next_u64() as u128) << 64) | (rng.next_u64() as u128) } } + + +#[cfg(test)] +mod tests { + use Rng; + use distributions::{Uniform}; + + #[test] + fn test_integers() { + let mut rng = ::test::rng(806); + + rng.sample::(Uniform); + rng.sample::(Uniform); + rng.sample::(Uniform); + rng.sample::(Uniform); + rng.sample::(Uniform); + #[cfg(feature = "i128_support")] + rng.sample::(Uniform); + + rng.sample::(Uniform); + rng.sample::(Uniform); + rng.sample::(Uniform); + rng.sample::(Uniform); + rng.sample::(Uniform); + #[cfg(feature = "i128_support")] + rng.sample::(Uniform); + } +} diff --git a/src/distributions/normal.rs b/src/distributions/normal.rs index bd3ba24d463..089cb9046d5 100644 --- a/src/distributions/normal.rs +++ b/src/distributions/normal.rs @@ -167,7 +167,6 @@ mod tests { let mut rng = ::test::rng(210); for _ in 0..1000 { norm.sample(&mut rng); - norm.sample(&mut rng); } } #[test] @@ -183,7 +182,6 @@ mod tests { let mut rng = ::test::rng(211); for _ in 0..1000 { lnorm.sample(&mut rng); - lnorm.sample(&mut rng); } } #[test] diff --git a/src/distributions/other.rs b/src/distributions/other.rs index 75cf2f20465..873043addac 100644 --- a/src/distributions/other.rs +++ b/src/distributions/other.rs @@ -113,3 +113,17 @@ impl Distribution> for Uniform where Uniform: Distribution { } } } + + +#[cfg(test)] +mod tests { + use {Rng, RngCore, Uniform}; + + #[test] + fn test_misc() { + let mut rng: &mut RngCore = &mut ::test::rng(820); + + rng.sample::(Uniform); + rng.sample::(Uniform); + } +} From 39666314679d8967b74d520aecf06946e15dfda1 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 27 Feb 2018 17:44:32 +0000 Subject: [PATCH 5/5] Add CryptoRng marker trait --- src/jitter.rs | 4 +++- src/lib.rs | 22 ++++++++++++++++++++++ src/prng/chacha.rs | 4 +++- src/prng/hc128.rs | 4 +++- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/jitter.rs b/src/jitter.rs index 2a5e5015c24..fb5dec521e5 100644 --- a/src/jitter.rs +++ b/src/jitter.rs @@ -16,7 +16,7 @@ //! Non-physical true random number generator based on timing jitter. -use {RngCore, Error, ErrorKind, impls}; +use {RngCore, CryptoRng, Error, ErrorKind, impls}; use core::{fmt, mem, ptr}; #[cfg(feature="std")] @@ -776,5 +776,7 @@ impl RngCore for JitterRng { } } +impl CryptoRng for JitterRng {} + // There are no tests included because (1) this is an "external" RNG, so output // is not reproducible and (2) `test_timer` *will* fail on some platforms. diff --git a/src/lib.rs b/src/lib.rs index 138893fe333..ad7612118e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -446,6 +446,28 @@ pub trait RngCore { } } +/// A marker trait for an `Rng` which may be considered for use in +/// cryptography. +/// +/// *Cryptographically secure generators*, also known as *CSPRNGs*, should +/// satisfy an additional properties over other generators: given the first +/// *k* bits of an algorithm's output +/// sequence, it should not be possible using polynomial-time algorithms to +/// predict the next bit with probability significantly greater than 50%. +/// +/// Some generators may satisfy an additional property, however this is not +/// required: if the CSPRNG's state is revealed, it should not be +/// computationally-feasible to reconstruct output prior to this. Some other +/// generators allow backwards-computation and are consided *reversible*. +/// +/// Note that this trait is provided for guidance only and cannot guarantee +/// suitability for cryptographic applications. In general it should only be +/// implemented for well-reviewed code implementing well-regarded algorithms. +/// +/// Note also that use of a `CryptoRng` does not protect against other +/// weaknesses such as seeding from a weak entropy source or leaking state. +pub trait CryptoRng: RngCore {} + /// An automatically-implemented extension trait on [`RngCore`] providing high-level /// generic methods for sampling values and other convenience methods. /// diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 12311382469..93ea639a206 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -11,7 +11,7 @@ //! The ChaCha random number generator. use core::fmt; -use {RngCore, SeedableRng}; +use {RngCore, CryptoRng, SeedableRng}; use {impls, le}; const SEED_WORDS: usize = 8; // 8 words for the 256-bit key @@ -253,6 +253,8 @@ impl RngCore for ChaChaRng { } } +impl CryptoRng for ChaChaRng {} + impl SeedableRng for ChaChaRng { type Seed = [u8; SEED_WORDS*4]; fn from_seed(seed: Self::Seed) -> Self { diff --git a/src/prng/hc128.rs b/src/prng/hc128.rs index d60f31cd54a..a587fbcecc3 100644 --- a/src/prng/hc128.rs +++ b/src/prng/hc128.rs @@ -11,7 +11,7 @@ //! The HC-128 random number generator. use core::fmt; -use {RngCore, SeedableRng}; +use {RngCore, CryptoRng, SeedableRng}; use {impls, le}; const SEED_WORDS: usize = 8; // 128 bit key followed by 128 bit iv @@ -394,6 +394,8 @@ impl RngCore for Hc128Rng { } } +impl CryptoRng for Hc128Rng {} + impl SeedableRng for Hc128Rng { type Seed = [u8; SEED_WORDS*4];