8
8
// option. This file may not be copied, modified, or distributed
9
9
// except according to those terms.
10
10
11
- //! A wrapper around another RNG that reseeds it after it
11
+ //! A wrapper around another PRNG that reseeds it after it
12
12
//! generates a certain number of random bytes.
13
13
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.
18
57
#[ derive( Debug ) ]
19
58
pub struct ReseedingRng < R , Rsdr > {
20
59
rng : R ,
21
- generation_threshold : u64 ,
22
- bytes_generated : u64 ,
23
- /// Controls the behaviour when reseeding the RNG.
24
60
reseeder : Rsdr ,
61
+ threshold : i64 ,
62
+ bytes_until_reseed : i64 ,
25
63
}
26
64
27
65
impl < R : Rng +SeedableRng , Rsdr : Rng > ReseedingRng < R , Rsdr > {
@@ -30,53 +68,115 @@ impl<R: Rng+SeedableRng, Rsdr: Rng> ReseedingRng<R, Rsdr> {
30
68
/// # Arguments
31
69
///
32
70
/// * `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 ) ;
36
75
ReseedingRng {
37
76
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.
41
115
}
42
116
}
43
117
44
118
/// Reseed the internal RNG if the number of bytes that have been
45
119
/// 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) ) ;
52
140
}
141
+ self . bytes_until_reseed = self . threshold ;
142
+ Ok ( ( ) )
53
143
}
54
144
}
55
145
56
-
57
146
impl < R : Rng +SeedableRng , Rsdr : Rng > Rng for ReseedingRng < R , Rsdr > {
58
147
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
62
154
}
63
155
64
156
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
68
163
}
69
164
70
165
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
+ }
74
171
}
75
-
172
+
76
173
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 ( ( ) )
80
180
}
81
181
}
82
182
0 commit comments