88// option. This file may not be copied, modified, or distributed
99// except according to those terms.
1010
11- //! A wrapper around another RNG that reseeds it after it
11+ //! A wrapper around another PRNG that reseeds it after it
1212//! generates a certain number of random bytes.
1313
14- use { Rng , SeedableRng , Error } ;
15-
16- /// A wrapper around any RNG which reseeds the underlying RNG after it
17- /// has generated a certain number of random bytes.
14+ use { Rng , SeedableRng , Error , ErrorKind } ;
15+
16+ /// A wrapper around any PRNG which reseeds the underlying PRNG after it has
17+ /// generated a certain number of random bytes.
18+ ///
19+ /// Reseeding is never strictly *necessary*. Cryptographic PRNGs don't have a
20+ /// limited number of bytes they can output, or at least not a limit reachable
21+ /// in any practical way. There is no such thing as 'running out of entropy'.
22+ ///
23+ /// Some small non-cryptographic PRNGs can have very small periods, for
24+ /// example less than 2<sup>64</sup>. Would reseeding help to ensure that you do
25+ /// not wrap around at the end of the period? A period of 2<sup>64</sup> still
26+ /// takes several centuries of CPU-years on current hardware. Reseeding will
27+ /// actually make things worse, because the reseeded PRNG will just continue
28+ /// somewhere else *in the same period*, with a high chance of overlapping with
29+ /// previously used parts of it.
30+ ///
31+ /// # When should you use `ReseedingRng`?
32+ ///
33+ /// - Reseeding can be seen as some form of 'security in depth'. Even if in the
34+ /// future a cryptographic weakness is found in the CSPRNG being used,
35+ /// occasionally reseeding should make exploiting it much more difficult or
36+ /// even impossible.
37+ /// - It can be used as a poor man's cryptography (not recommended, just use a
38+ /// good CSPRNG). Previous implementations of `thread_rng` for example used
39+ /// `ReseedingRng` with the ISAAC RNG. That algorithm, although apparently
40+ /// strong and with no known attack, does not come with any proof of security
41+ /// and does not meet the current standards for a cryptographically secure
42+ /// PRNG. By reseeding it frequently (every 32 MiB) it seems safe to assume
43+ /// there is no attack that can operate on the tiny window between reseeds.
44+ ///
45+ /// # Error handling
46+ ///
47+ /// If reseeding fails, `try_fill_bytes` is the only `Rng` method to report it.
48+ /// For all other `Rng` methods, `ReseedingRng` will not panic but try to
49+ /// handle the error intelligently; if handling the source error fails these
50+ /// methods will continue generating data from the wrapped PRNG without
51+ /// reseeding.
52+ ///
53+ /// It is usually best to use the infallible methods `next_u32`, `next_u64` and
54+ /// `fill_bytes` because they can make use of this error handling strategy.
55+ /// Use `try_fill_bytes` and possibly `try_reseed` if you want to handle
56+ /// reseeding errors explicitly.
1857#[ derive( Debug ) ]
1958pub struct ReseedingRng < R , Rsdr > {
2059 rng : R ,
21- generation_threshold : u64 ,
22- bytes_generated : u64 ,
23- /// Controls the behaviour when reseeding the RNG.
2460 reseeder : Rsdr ,
61+ threshold : i64 ,
62+ bytes_until_reseed : i64 ,
2563}
2664
2765impl < R : Rng +SeedableRng , Rsdr : Rng > ReseedingRng < R , Rsdr > {
@@ -30,53 +68,115 @@ impl<R: Rng+SeedableRng, Rsdr: Rng> ReseedingRng<R, Rsdr> {
3068 /// # Arguments
3169 ///
3270 /// * `rng`: the random number generator to use.
33- /// * `generation_threshold`: the number of bytes of entropy at which to reseed the RNG.
34- /// * `reseeder`: the reseeding object to use.
35- pub fn new ( rng : R , generation_threshold : u64 , reseeder : Rsdr ) -> ReseedingRng < R , Rsdr > {
71+ /// * `threshold`: the number of generated bytes after which to reseed the RNG.
72+ /// * `reseeder`: the RNG to use for reseeding.
73+ pub fn new ( rng : R , threshold : u64 , reseeder : Rsdr ) -> ReseedingRng < R , Rsdr > {
74+ assert ! ( threshold <= :: core:: i64 :: MAX as u64 ) ;
3675 ReseedingRng {
3776 rng : rng,
38- generation_threshold : generation_threshold,
39- bytes_generated : 0 ,
40- reseeder : reseeder
77+ reseeder : reseeder,
78+ threshold : threshold as i64 ,
79+ bytes_until_reseed : threshold as i64 ,
80+ }
81+ }
82+
83+ /// Reseed the internal PRNG.
84+ ///
85+ /// This will try to work around errors in the RNG used for reseeding
86+ /// intelligently. If the error kind indicates retrying might help, it will
87+ /// immediately retry a couple of times. If the error kind indicates the
88+ /// seeding RNG is not ready, it will retry later, after `threshold / 256`
89+ /// generated bytes. On other errors in the source RNG, this will skip
90+ /// reseeding and continue using the internal PRNG, until another
91+ /// `threshold` bytes have been generated (at which point it will try
92+ /// reseeding again).
93+ #[ inline( never) ]
94+ pub fn reseed ( & mut self ) {
95+ trace ! ( "Reseeding RNG after generating {} bytes" ,
96+ self . threshold - self . bytes_until_reseed) ;
97+ self . bytes_until_reseed = self . threshold ;
98+ let mut err_count = 0 ;
99+ loop {
100+ if let Err ( e) = R :: from_rng ( & mut self . reseeder )
101+ . map ( |result| self . rng = result) {
102+ let kind = e. kind ( ) ;
103+ if kind. should_wait ( ) {
104+ self . bytes_until_reseed = self . threshold >> 8 ;
105+ warn ! ( "Reseeding RNG delayed for {} bytes" ,
106+ self . bytes_until_reseed) ;
107+ } else if kind. should_retry ( ) {
108+ err_count += 1 ;
109+ // Retry immediately for 5 times (arbitrary limit)
110+ if err_count <= 5 { continue ; }
111+ }
112+ warn ! ( "Reseeding RNG failed; continuing without reseeding. Error: {}" , e) ;
113+ }
114+ break ; // Successfully reseeded, delayed, or given up.
41115 }
42116 }
43117
44118 /// Reseed the internal RNG if the number of bytes that have been
45119 /// generated exceed the threshold.
46- pub fn reseed_if_necessary ( & mut self ) {
47- if self . bytes_generated >= self . generation_threshold {
48- trace ! ( "Reseeding RNG after {} bytes" , self . bytes_generated) ;
49- R :: from_rng ( & mut self . reseeder ) . map ( |result| self . rng = result)
50- . unwrap_or_else ( |err| panic ! ( "reseeding failed: {}" , err) ) ;
51- self . bytes_generated = 0 ;
120+ ///
121+ /// If reseeding fails, return an error with the original cause. Note that
122+ /// if the cause has a permanent failure, we report a transient error and
123+ /// skip reseeding; this means that only two error kinds can be reported
124+ /// from this method: `ErrorKind::Transient` and `ErrorKind::NotReady`.
125+ #[ inline( never) ]
126+ pub fn try_reseed ( & mut self ) -> Result < ( ) , Error > {
127+ trace ! ( "Reseeding RNG after {} generated bytes" ,
128+ self . threshold - self . bytes_until_reseed) ;
129+ if let Err ( err) = R :: from_rng ( & mut self . reseeder )
130+ . map ( |result| self . rng = result) {
131+ let newkind = match err. kind ( ) {
132+ a @ ErrorKind :: NotReady => a,
133+ b @ ErrorKind :: Transient => b,
134+ _ => {
135+ self . bytes_until_reseed = self . threshold ; // skip reseeding
136+ ErrorKind :: Transient
137+ }
138+ } ;
139+ return Err ( Error :: with_cause ( newkind, "reseeding failed" , err) ) ;
52140 }
141+ self . bytes_until_reseed = self . threshold ;
142+ Ok ( ( ) )
53143 }
54144}
55145
56-
57146impl < R : Rng +SeedableRng , Rsdr : Rng > Rng for ReseedingRng < R , Rsdr > {
58147 fn next_u32 ( & mut self ) -> u32 {
59- self . reseed_if_necessary ( ) ;
60- self . bytes_generated += 4 ;
61- self . rng . next_u32 ( )
148+ let value = self . rng . next_u32 ( ) ;
149+ self . bytes_until_reseed -= 4 ;
150+ if self . bytes_until_reseed <= 0 {
151+ self . reseed ( ) ;
152+ }
153+ value
62154 }
63155
64156 fn next_u64 ( & mut self ) -> u64 {
65- self . reseed_if_necessary ( ) ;
66- self . bytes_generated += 8 ;
67- self . rng . next_u64 ( )
157+ let value = self . rng . next_u64 ( ) ;
158+ self . bytes_until_reseed -= 8 ;
159+ if self . bytes_until_reseed <= 0 {
160+ self . reseed ( ) ;
161+ }
162+ value
68163 }
69164
70165 fn fill_bytes ( & mut self , dest : & mut [ u8 ] ) {
71- self . reseed_if_necessary ( ) ;
72- self . bytes_generated += dest. len ( ) as u64 ;
73- self . rng . fill_bytes ( dest)
166+ self . rng . fill_bytes ( dest) ;
167+ self . bytes_until_reseed -= dest. len ( ) as i64 ;
168+ if self . bytes_until_reseed <= 0 {
169+ self . reseed ( ) ;
170+ }
74171 }
75-
172+
76173 fn try_fill_bytes ( & mut self , dest : & mut [ u8 ] ) -> Result < ( ) , Error > {
77- self . reseed_if_necessary ( ) ;
78- self . bytes_generated += dest. len ( ) as u64 ;
79- self . rng . try_fill_bytes ( dest)
174+ self . rng . try_fill_bytes ( dest) ?;
175+ self . bytes_until_reseed -= dest. len ( ) as i64 ;
176+ if self . bytes_until_reseed <= 0 {
177+ self . try_reseed ( ) ?;
178+ }
179+ Ok ( ( ) )
80180 }
81181}
82182
0 commit comments