diff options
Diffstat (limited to 'rand/src/rngs/thread.rs')
-rw-r--r-- | rand/src/rngs/thread.rs | 91 |
1 files changed, 37 insertions, 54 deletions
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); } |