1 // Copyright 2017-2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // https://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10 
11 //! Thread-local random number generator
12 
13 use std::cell::UnsafeCell;
14 use std::rc::Rc;
15 
16 use {RngCore, CryptoRng, SeedableRng, Error};
17 use rngs::adapter::ReseedingRng;
18 use rngs::EntropyRng;
19 use prng::hc128::Hc128Core;
20 
21 // Rationale for using `UnsafeCell` in `ThreadRng`:
22 //
23 // Previously we used a `RefCell`, with an overhead of ~15%. There will only
24 // ever be one mutable reference to the interior of the `UnsafeCell`, because
25 // we only have such a reference inside `next_u32`, `next_u64`, etc. Within a
26 // single thread (which is the definition of `ThreadRng`), there will only ever
27 // be one of these methods active at a time.
28 //
29 // A possible scenario where there could be multiple mutable references is if
30 // `ThreadRng` is used inside `next_u32` and co. But the implementation is
31 // completely under our control. We just have to ensure none of them use
32 // `ThreadRng` internally, which is nonsensical anyway. We should also never run
33 // `ThreadRng` in destructors of its implementation, which is also nonsensical.
34 //
35 // The additional `Rc` is not strictly neccesary, and could be removed. For now
36 // it ensures `ThreadRng` stays `!Send` and `!Sync`, and implements `Clone`.
37 
38 
39 // Number of generated bytes after which to reseed `TreadRng`.
40 //
41 // The time it takes to reseed HC-128 is roughly equivalent to generating 7 KiB.
42 // We pick a treshold here that is large enough to not reduce the average
43 // performance too much, but also small enough to not make reseeding something
44 // that basically never happens.
45 const THREAD_RNG_RESEED_THRESHOLD: u64 = 32*1024*1024; // 32 MiB
46 
47 /// The type returned by [`thread_rng`], essentially just a reference to the
48 /// PRNG in thread-local memory.
49 ///
50 /// `ThreadRng` uses [`ReseedingRng`] wrapping the same PRNG as [`StdRng`],
51 /// which is reseeded after generating 32 MiB of random data. A single instance
52 /// is cached per thread and the returned `ThreadRng` is a reference to this
53 /// instance — hence `ThreadRng` is neither `Send` nor `Sync` but is safe to use
54 /// within a single thread. This RNG is seeded and reseeded via [`EntropyRng`]
55 /// as required.
56 ///
57 /// Note that the reseeding is done as an extra precaution against entropy
58 /// leaks and is in theory unnecessary — to predict `ThreadRng`'s output, an
59 /// attacker would have to either determine most of the RNG's seed or internal
60 /// state, or crack the algorithm used.
61 ///
62 /// Like [`StdRng`], `ThreadRng` is a cryptographically secure PRNG. The current
63 /// algorithm used is [HC-128], which is an array-based PRNG that trades memory
64 /// usage for better performance. This makes it similar to ISAAC, the algorithm
65 /// used in `ThreadRng` before rand 0.5.
66 ///
67 /// Cloning this handle just produces a new reference to the same thread-local
68 /// generator.
69 ///
70 /// [`thread_rng`]: ../fn.thread_rng.html
71 /// [`ReseedingRng`]: adapter/struct.ReseedingRng.html
72 /// [`StdRng`]: struct.StdRng.html
73 /// [`EntropyRng`]: struct.EntropyRng.html
74 /// [HC-128]: ../prng/hc128/struct.Hc128Rng.html
75 #[derive(Clone, Debug)]
76 pub struct ThreadRng {
77     rng: Rc<UnsafeCell<ReseedingRng<Hc128Core, EntropyRng>>>,
78 }
79 
80 thread_local!(
81     static THREAD_RNG_KEY: Rc<UnsafeCell<ReseedingRng<Hc128Core, EntropyRng>>> = {
82         let mut entropy_source = EntropyRng::new();
83         let r = Hc128Core::from_rng(&mut entropy_source).unwrap_or_else(|err|
84                 panic!("could not initialize thread_rng: {}", err));
85         let rng = ReseedingRng::new(r,
86                                     THREAD_RNG_RESEED_THRESHOLD,
87                                     entropy_source);
88         Rc::new(UnsafeCell::new(rng))
89     }
90 );
91 
92 /// Retrieve the lazily-initialized thread-local random number
93 /// generator, seeded by the system. Intended to be used in method
94 /// chaining style, e.g. `thread_rng().gen::<i32>()`, or cached locally, e.g.
95 /// `let mut rng = thread_rng();`.
96 ///
97 /// For more information see [`ThreadRng`].
98 ///
99 /// [`ThreadRng`]: rngs/struct.ThreadRng.html
thread_rng() -> ThreadRng100 pub fn thread_rng() -> ThreadRng {
101     ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.clone()) }
102 }
103 
104 impl RngCore for ThreadRng {
105     #[inline(always)]
next_u32(&mut self) -> u32106     fn next_u32(&mut self) -> u32 {
107         unsafe { (*self.rng.get()).next_u32() }
108     }
109 
110     #[inline(always)]
next_u64(&mut self) -> u64111     fn next_u64(&mut self) -> u64 {
112         unsafe { (*self.rng.get()).next_u64() }
113     }
114 
fill_bytes(&mut self, dest: &mut [u8])115     fn fill_bytes(&mut self, dest: &mut [u8]) {
116         unsafe { (*self.rng.get()).fill_bytes(dest) }
117     }
118 
try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error>119     fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
120         unsafe { (*self.rng.get()).try_fill_bytes(dest) }
121     }
122 }
123 
124 impl CryptoRng for ThreadRng {}
125 
126 
127 #[cfg(test)]
128 mod test {
129     #[test]
130     #[cfg(not(feature="stdweb"))]
test_thread_rng()131     fn test_thread_rng() {
132         use Rng;
133         let mut r = ::thread_rng();
134         r.gen::<i32>();
135         let mut v = [1, 1, 1];
136         r.shuffle(&mut v);
137         let b: &[_] = &[1, 1, 1];
138         assert_eq!(v, b);
139         assert_eq!(r.gen_range(0, 1), 0);
140     }
141 }
142