11
11
//! A wrapper around another PRNG that reseeds it after it
12
12
//! generates a certain number of random bytes.
13
13
14
- use { RngCore , SeedableRng , Error , ErrorKind } ;
14
+ use { RngCore , BlockRngCore , SeedableRng , Error , ErrorKind } ;
15
+ use impls:: BlockRng ;
15
16
16
17
/// A wrapper around any PRNG which reseeds the underlying PRNG after it has
17
18
/// generated a certain number of random bytes.
@@ -39,73 +40,125 @@ use {RngCore, SeedableRng, Error, ErrorKind};
39
40
/// `ReseedingRng` with the ISAAC RNG. That algorithm, although apparently
40
41
/// strong and with no known attack, does not come with any proof of security
41
42
/// 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
+ /// PRNG. By reseeding it frequently (every 32 kiB ) it seems safe to assume
43
44
/// there is no attack that can operate on the tiny window between reseeds.
44
45
///
45
46
/// # Error handling
46
47
///
47
48
/// If reseeding fails, `try_fill_bytes` is the only `Rng` method to report it.
48
49
/// 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
+ /// handle the error intelligently through some combination of retrying and
51
+ /// delaying reseeding until later. If handling the source error fails these
50
52
/// methods will continue generating data from the wrapped PRNG without
51
53
/// 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.
57
54
#[ derive( Debug ) ]
58
- pub struct ReseedingRng < R , Rsdr > {
59
- rng : R ,
60
- reseeder : Rsdr ,
61
- threshold : i64 ,
62
- bytes_until_reseed : i64 ,
63
- }
55
+ pub struct ReseedingRng < R , Rsdr > ( BlockRng < Reseeder < R , Rsdr > > )
56
+ where R : BlockRngCore < u32 > + SeedableRng ,
57
+ Rsdr : RngCore ;
64
58
65
- impl < R : RngCore + SeedableRng , Rsdr : RngCore > ReseedingRng < R , Rsdr > {
59
+ impl < R , Rsdr > ReseedingRng < R , Rsdr >
60
+ where R : BlockRngCore < u32 > + SeedableRng ,
61
+ Rsdr : RngCore
62
+ {
66
63
/// Create a new `ReseedingRng` with the given parameters.
67
64
///
68
65
/// # Arguments
69
66
///
70
67
/// * `rng`: the random number generator to use.
71
68
/// * `threshold`: the number of generated bytes after which to reseed the RNG.
72
69
/// * `reseeder`: the RNG to use for reseeding.
73
- pub fn new ( rng : R , threshold : u64 , reseeder : Rsdr ) -> ReseedingRng < R , Rsdr > {
70
+ pub fn new ( rng : R , threshold : u64 , reseeder : Rsdr )
71
+ -> ReseedingRng < R , Rsdr >
72
+ {
74
73
assert ! ( threshold <= :: core:: i64 :: MAX as u64 ) ;
75
- ReseedingRng {
76
- rng : rng,
77
- reseeder : reseeder,
78
- threshold : threshold as i64 ,
79
- bytes_until_reseed : threshold as i64 ,
74
+ let results_empty = R :: Results :: default ( ) ;
75
+ ReseedingRng (
76
+ BlockRng {
77
+ core : Reseeder {
78
+ core : rng,
79
+ reseeder : reseeder,
80
+ threshold : threshold as i64 ,
81
+ bytes_until_reseed : threshold as i64 ,
82
+ } ,
83
+ index : results_empty. as_ref ( ) . len ( ) , // generate on first use
84
+ results : results_empty,
85
+ }
86
+ )
87
+ }
88
+
89
+ /// Reseed the internal PRNG.
90
+ pub fn reseed ( & mut self ) -> Result < ( ) , Error > {
91
+ self . 0 . core . reseed ( )
92
+ }
93
+ }
94
+
95
+ impl < R : BlockRngCore < u32 > + SeedableRng , Rsdr : RngCore > RngCore for ReseedingRng < R , Rsdr > {
96
+ #[ inline]
97
+ fn next_u32 ( & mut self ) -> u32 {
98
+ self . 0 . next_u32 ( )
99
+ }
100
+
101
+ #[ inline]
102
+ fn next_u64 ( & mut self ) -> u64 {
103
+ self . 0 . next_u64 ( )
104
+ }
105
+
106
+ #[ inline]
107
+ fn fill_bytes ( & mut self , dest : & mut [ u8 ] ) {
108
+ self . 0 . fill_bytes ( dest)
109
+ }
110
+
111
+ #[ inline]
112
+ fn try_fill_bytes ( & mut self , dest : & mut [ u8 ] ) -> Result < ( ) , Error > {
113
+ self . 0 . try_fill_bytes ( dest)
114
+ }
115
+ }
116
+
117
+ #[ derive( Debug ) ]
118
+ struct Reseeder < R , Rsdr > {
119
+ core : R ,
120
+ reseeder : Rsdr ,
121
+ threshold : i64 ,
122
+ bytes_until_reseed : i64 ,
123
+ }
124
+
125
+ impl < R , Rsdr > BlockRngCore < u32 > for Reseeder < R , Rsdr >
126
+ where R : BlockRngCore < u32 > + SeedableRng ,
127
+ Rsdr : RngCore
128
+ {
129
+ type Results = <R as BlockRngCore < u32 > >:: Results ;
130
+
131
+ fn generate ( & mut self , results : & mut Self :: Results ) -> Result < ( ) , Error > {
132
+ if self . bytes_until_reseed <= 0 {
133
+ // In the unlikely event the internal PRNG fails, we don't know
134
+ // whether this is resolvable; schedule to reseed on next use and
135
+ // return original error kind.
136
+ return self . reseed_and_generate ( results) ;
80
137
}
138
+ self . bytes_until_reseed -= results. as_ref ( ) . len ( ) as i64 * 4 ;
139
+ self . core . generate ( results)
81
140
}
141
+ }
82
142
143
+ impl < R , Rsdr > Reseeder < R , Rsdr >
144
+ where R : BlockRngCore < u32 > + SeedableRng ,
145
+ Rsdr : RngCore
146
+ {
83
147
/// Reseed the internal PRNG.
84
- ///
85
- /// This will try to work around errors in the RNG used for reseeding
86
- /// intelligently through some combination of retrying and delaying
87
- /// reseeding until later. So long as the internal PRNG doesn't fail, this
88
- /// method will not fail, i.e. failures from the reseeding source are not
89
- /// fatal.
90
- pub fn reseed ( & mut self ) {
91
- // Behaviour is identical to `try_reseed`; we just squelch the error.
92
- let _res = self . try_reseed ( ) ;
148
+ fn reseed ( & mut self ) -> Result < ( ) , Error > {
149
+ R :: from_rng ( & mut self . reseeder ) . map ( |result| self . core = result)
93
150
}
94
151
95
- /// Reseed the internal RNG if the number of bytes that have been
96
- /// generated exceed the threshold.
152
+ /// Reseed the internal PRNG.
97
153
///
98
- /// If reseeding fails, return an error with the original cause. Note that
99
- /// in case of error we simply delay reseeding, allowing the generator to
100
- /// continue its output of random data and try reseeding again later;
101
- /// because of this we always return kind `ErrorKind::Transient`.
102
- #[ inline( never) ]
103
- pub fn try_reseed ( & mut self ) -> Result < ( ) , Error > {
154
+ /// If reseeding fails, this will try to work around errors intelligently
155
+ /// through some combination of retrying and delaying reseeding until later.
156
+ /// It will also report the error with `ErrorKind::Transient` with the
157
+ /// original error as cause.
158
+ fn auto_reseed ( & mut self ) -> Result < ( ) , Error > {
104
159
trace ! ( "Reseeding RNG after {} generated bytes" ,
105
160
self . threshold - self . bytes_until_reseed) ;
106
- if let Err ( mut e) = R :: from_rng ( & mut self . reseeder )
107
- . map ( |result| self . rng = result)
108
- {
161
+ if let Err ( mut e) = self . reseed ( ) {
109
162
let delay = match e. kind {
110
163
ErrorKind :: Transient => 0 ,
111
164
kind @ _ if kind. should_retry ( ) => self . threshold >> 8 ,
@@ -121,69 +174,36 @@ impl<R: RngCore + SeedableRng, Rsdr: RngCore> ReseedingRng<R, Rsdr> {
121
174
Ok ( ( ) )
122
175
}
123
176
}
124
- }
125
177
126
- impl < R : RngCore + SeedableRng , Rsdr : RngCore > RngCore for ReseedingRng < R , Rsdr > {
127
- fn next_u32 ( & mut self ) -> u32 {
128
- let value = self . rng . next_u32 ( ) ;
129
- self . bytes_until_reseed -= 4 ;
130
- if self . bytes_until_reseed <= 0 {
131
- self . reseed ( ) ;
132
- }
133
- value
134
- }
135
-
136
- fn next_u64 ( & mut self ) -> u64 {
137
- let value = self . rng . next_u64 ( ) ;
138
- self . bytes_until_reseed -= 8 ;
139
- if self . bytes_until_reseed <= 0 {
140
- self . reseed ( ) ;
141
- }
142
- value
143
- }
144
-
145
- fn fill_bytes ( & mut self , dest : & mut [ u8 ] ) {
146
- self . rng . fill_bytes ( dest) ;
147
- self . bytes_until_reseed -= dest. len ( ) as i64 ;
148
- if self . bytes_until_reseed <= 0 {
149
- self . reseed ( ) ;
150
- }
151
- }
152
-
153
- fn try_fill_bytes ( & mut self , dest : & mut [ u8 ] ) -> Result < ( ) , Error > {
154
- let res1 = self . rng . try_fill_bytes ( dest) ;
155
- self . bytes_until_reseed -= dest. len ( ) as i64 ;
156
- let res2 = if self . bytes_until_reseed <= 0 {
157
- self . try_reseed ( )
158
- } else { Ok ( ( ) ) } ;
159
-
160
- if let Err ( e) = res1 {
161
- // In the unlikely event the internal PRNG fails, we don't know
162
- // whether this is resolvable; reseed immediately and return
163
- // original error kind.
164
- self . bytes_until_reseed = 0 ;
165
- Err ( e)
166
- } else {
167
- res2
168
- }
178
+ #[ inline( never) ]
179
+ fn reseed_and_generate ( & mut self ,
180
+ results : & mut <Self as BlockRngCore < u32 > >:: Results )
181
+ -> Result < ( ) , Error >
182
+ {
183
+ let res1 = self . auto_reseed ( ) ;
184
+ self . bytes_until_reseed -= results. as_ref ( ) . len ( ) as i64 * 4 ;
185
+ let res2 = self . core . generate ( results) ;
186
+ if res2. is_err ( ) { res2 } else { res1 }
169
187
}
170
188
}
171
189
172
190
#[ cfg( test) ]
173
191
mod test {
174
- use { Rng , SeedableRng , StdRng } ;
192
+ use { Rng , SeedableRng } ;
193
+ use prng:: chacha:: ChaChaCore ;
175
194
use mock:: StepRng ;
176
195
use super :: ReseedingRng ;
177
196
178
197
#[ test]
179
198
fn test_reseeding ( ) {
180
199
let mut zero = StepRng :: new ( 0 , 0 ) ;
181
- let rng = StdRng :: from_rng ( & mut zero) . unwrap ( ) ;
182
- let mut reseeding = ReseedingRng :: new ( rng, 32 , zero) ;
200
+ let rng = ChaChaCore :: from_rng ( & mut zero) . unwrap ( ) ;
201
+ let mut reseeding = ReseedingRng :: new ( rng, 32 * 4 , zero) ;
183
202
184
203
// Currently we only support for arrays up to length 32.
185
204
// TODO: cannot generate seq via Rng::gen because it uses different alg
186
- let mut buf = [ 0u8 ; 32 ] ;
205
+ let mut buf = [ 0u32 ; 32 ] ; // Needs to be a multiple of the RNGs result
206
+ // size to test exactly.
187
207
reseeding. fill ( & mut buf) ;
188
208
let seq = buf;
189
209
for _ in 0 ..10 {
0 commit comments