1010//! A wrapper around another PRNG that reseeds it after it
1111//! generates a certain number of random bytes.
1212
13+ use core:: fmt;
1314use core:: mem:: size_of_val;
14- use rand_core:: { CryptoRng , RngCore , SeedableRng , TryCryptoRng , TryRngCore } ;
1515
16- /// A wrapper around any PRNG that implements [`RngCore`], that adds the
16+ use rand_core:: le:: { self , Word } ;
17+ use rand_core:: {
18+ CryptoGenerator , CryptoRng , Generator , RngCore , SeedableRng , TryCryptoRng , TryRngCore ,
19+ } ;
20+
21+ /// A wrapper around any PRNG that implements [`Generator`], that adds the
1722/// ability to reseed it.
1823///
1924/// `ReseedingRng` reseeds the underlying PRNG in the following cases:
@@ -50,34 +55,46 @@ use rand_core::{CryptoRng, RngCore, SeedableRng, TryCryptoRng, TryRngCore};
5055/// # Example
5156///
5257/// ```
53- /// use chacha20::ChaCha20Rng ; // Internal part of ChaChaRng that
54- /// // implements BlockRngCore
58+ /// use chacha20::ChaCha20Core ; // Internal part of ChaChaRng that
59+ /// // implements Generator
5560/// use rand::prelude::*;
5661/// use rand::rngs::OsRng;
5762/// use rand::rngs::ReseedingRng;
5863///
59- /// let mut reseeding_rng = ReseedingRng::<ChaCha20Rng , _>::new(0, OsRng).unwrap();
64+ /// let mut reseeding_rng = ReseedingRng::<ChaCha20Core , _>::new(0, OsRng).unwrap();
6065///
6166/// println!("{}", reseeding_rng.random::<u64>());
6267/// ```
6368///
69+ /// [`Generator`]: rand_core::Generator
6470/// [`ReseedingRng::new`]: ReseedingRng::new
6571/// [`reseed()`]: ReseedingRng::reseed
66- #[ derive( Debug ) ]
6772pub struct ReseedingRng < R , Rsdr >
6873where
69- R : RngCore + SeedableRng ,
74+ R : Generator + SeedableRng ,
7075 Rsdr : TryRngCore ,
7176{
72- prng : R ,
73- reseed_rng : Rsdr ,
74- threshold : i64 ,
75- bytes_until_reseed : i64 ,
77+ core : ReseedingCore < R , Rsdr > ,
78+ buffer : <R as Generator >:: Result ,
79+ }
80+
81+ impl < R : fmt:: Debug , Rsdr : fmt:: Debug > fmt:: Debug for ReseedingRng < R , Rsdr >
82+ where
83+ R : Generator + SeedableRng ,
84+ R :: Result : fmt:: Debug ,
85+ Rsdr : TryRngCore ,
86+ {
87+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
88+ f. debug_struct ( "ReseedingRng" )
89+ . field ( "core" , & self . core )
90+ . field ( "buffer" , & self . buffer )
91+ . finish ( )
92+ }
7693}
7794
78- impl < R , Rsdr > ReseedingRng < R , Rsdr >
95+ impl < W : Word , const N : usize , R , Rsdr > ReseedingRng < R , Rsdr >
7996where
80- R : RngCore + SeedableRng ,
97+ R : Generator < Result = [ W ; N ] > + SeedableRng ,
8198 Rsdr : TryRngCore ,
8299{
83100 /// Create a new `ReseedingRng` from an existing PRNG, combined with a RNG
@@ -86,8 +103,93 @@ where
86103 /// `threshold` sets the number of generated bytes after which to reseed the
87104 /// PRNG. Set it to zero to never reseed based on the number of generated
88105 /// values.
106+ pub fn new ( threshold : u64 , reseeder : Rsdr ) -> Result < Self , Rsdr :: Error > {
107+ Ok ( ReseedingRng {
108+ core : ReseedingCore :: new ( threshold, reseeder) ?,
109+ buffer : le:: new_buffer ( ) ,
110+ } )
111+ }
112+
113+ /// Immediately reseed the generator
114+ ///
115+ /// This discards any remaining random data in the cache.
116+ pub fn reseed ( & mut self ) -> Result < ( ) , Rsdr :: Error > {
117+ self . buffer = le:: new_buffer ( ) ;
118+ self . core . reseed ( )
119+ }
120+ }
121+
122+ impl < const N : usize , R , Rsdr > RngCore for ReseedingRng < R , Rsdr >
123+ where
124+ R : Generator < Result = [ u32 ; N ] > + SeedableRng ,
125+ Rsdr : TryRngCore ,
126+ {
127+ #[ inline]
128+ fn next_u32 ( & mut self ) -> u32 {
129+ let Self { core, buffer } = self ;
130+ // NOTE: this fn always returns one word, thus cannot return u32 for any W: Word
131+ le:: next_word_via_gen_block ( buffer, |block| core. generate ( block) )
132+ }
133+
134+ #[ inline]
135+ fn next_u64 ( & mut self ) -> u64 {
136+ let Self { core, buffer } = self ;
137+ // NOTE: this fn is specific to u32 buffers
138+ le:: next_u64_via_gen_block ( buffer, |block| core. generate ( block) )
139+ }
140+
89141 #[ inline]
90- pub fn new ( threshold : u64 , mut reseed_rng : Rsdr ) -> Result < Self , Rsdr :: Error > {
142+ fn fill_bytes ( & mut self , dst : & mut [ u8 ] ) {
143+ let Self { core, buffer } = self ;
144+ le:: fill_bytes_via_gen_block ( dst, buffer, |block| core. generate ( block) ) ;
145+ }
146+ }
147+
148+ impl < const N : usize , R , Rsdr > CryptoRng for ReseedingRng < R , Rsdr >
149+ where
150+ R : Generator < Result = [ u32 ; N ] > + SeedableRng + CryptoGenerator ,
151+ Rsdr : TryCryptoRng ,
152+ {
153+ }
154+
155+ #[ derive( Debug ) ]
156+ struct ReseedingCore < R , Rsdr > {
157+ inner : R ,
158+ reseeder : Rsdr ,
159+ threshold : i64 ,
160+ bytes_until_reseed : i64 ,
161+ }
162+
163+ impl < R , Rsdr > Generator for ReseedingCore < R , Rsdr >
164+ where
165+ R : Generator + SeedableRng ,
166+ Rsdr : TryRngCore ,
167+ {
168+ type Result = <R as Generator >:: Result ;
169+
170+ fn generate ( & mut self , results : & mut Self :: Result ) {
171+ if self . bytes_until_reseed <= 0 {
172+ // We get better performance by not calling only `reseed` here
173+ // and continuing with the rest of the function, but by directly
174+ // returning from a non-inlined function.
175+ return self . reseed_and_generate ( results) ;
176+ }
177+ let num_bytes = size_of_val ( results) ;
178+ self . bytes_until_reseed -= num_bytes as i64 ;
179+ self . inner . generate ( results) ;
180+ }
181+ }
182+
183+ impl < R , Rsdr > ReseedingCore < R , Rsdr >
184+ where
185+ R : Generator + SeedableRng ,
186+ Rsdr : TryRngCore ,
187+ {
188+ /// Create a new `ReseedingCore`.
189+ ///
190+ /// `threshold` is the maximum number of bytes produced by
191+ /// [`Generator::generate`] before attempting reseeding.
192+ fn new ( threshold : u64 , mut reseeder : Rsdr ) -> Result < Self , Rsdr :: Error > {
91193 // Because generating more values than `i64::MAX` takes centuries on
92194 // current hardware, we just clamp to that value.
93195 // Also we set a threshold of 0, which indicates no limit, to that
@@ -100,68 +202,43 @@ where
100202 i64:: MAX
101203 } ;
102204
103- R :: try_from_rng ( & mut reseed_rng) . map ( |prng| Self {
104- prng,
105- reseed_rng,
205+ let inner = R :: try_from_rng ( & mut reseeder) ?;
206+
207+ Ok ( ReseedingCore {
208+ inner,
209+ reseeder,
106210 threshold,
107211 bytes_until_reseed : threshold,
108212 } )
109213 }
110214
111- /// Immediately reseed the generator.
112- ///
113- /// This discards any remaining random data in the cache.
114- #[ inline]
115- pub fn reseed ( & mut self ) -> Result < ( ) , Rsdr :: Error > {
116- self . prng = R :: try_from_rng ( & mut self . reseed_rng ) ?;
117- Ok ( ( ) )
118- }
119-
120- #[ inline]
121- fn reseed_check ( & mut self , bytes : usize ) {
122- if self . bytes_until_reseed <= 0 {
123- let res = self . reseed ( ) ;
124- if let Err ( _e) = res {
125- warn ! ( "Reseeding RNG failed: {_e}" ) ;
126- }
215+ /// Reseed the internal PRNG.
216+ fn reseed ( & mut self ) -> Result < ( ) , Rsdr :: Error > {
217+ R :: try_from_rng ( & mut self . reseeder ) . map ( |result| {
127218 self . bytes_until_reseed = self . threshold ;
128- }
129- self . bytes_until_reseed -= bytes as i64 ;
219+ self . inner = result
220+ } )
130221 }
131- }
132222
133- impl < R , Rsdr > RngCore for ReseedingRng < R , Rsdr >
134- where
135- R : RngCore + SeedableRng ,
136- Rsdr : TryRngCore ,
137- {
138- #[ inline( always) ]
139- fn next_u32 ( & mut self ) -> u32 {
140- self . reseed_check ( size_of :: < u32 > ( ) ) ;
141- self . prng . next_u32 ( )
142- }
223+ #[ inline( never) ]
224+ fn reseed_and_generate ( & mut self , results : & mut <Self as Generator >:: Result ) {
225+ trace ! ( "Reseeding RNG (periodic reseed)" ) ;
143226
144- #[ inline( always) ]
145- fn next_u64 ( & mut self ) -> u64 {
146- self . reseed_check ( size_of :: < u64 > ( ) ) ;
147- self . prng . next_u64 ( )
148- }
227+ let num_bytes = size_of_val ( results) ;
149228
150- #[ inline( always) ]
151- fn fill_bytes ( & mut self , dst : & mut [ u8 ] ) {
152- // Do not perform `reseed_check` for an empty `dst` to allow the compiler
153- // to infer that we always generate PRNG data after reseeding.
154- if dst. is_empty ( ) {
155- return ;
229+ if let Err ( e) = self . reseed ( ) {
230+ warn ! ( "Reseeding RNG failed: {}" , e) ;
231+ let _ = e;
156232 }
157- self . reseed_check ( size_of_val ( dst) ) ;
158- self . prng . fill_bytes ( dst)
233+
234+ self . bytes_until_reseed = self . threshold - num_bytes as i64 ;
235+ self . inner . generate ( results) ;
159236 }
160237}
161238
162- impl < R , Rsdr > CryptoRng for ReseedingRng < R , Rsdr >
239+ impl < R , Rsdr > CryptoGenerator for ReseedingCore < R , Rsdr >
163240where
164- R : CryptoRng + SeedableRng ,
241+ R : Generator + SeedableRng + CryptoGenerator ,
165242 Rsdr : TryCryptoRng ,
166243{
167244}
@@ -170,7 +247,7 @@ where
170247#[ cfg( test) ]
171248mod test {
172249 use crate :: Rng ;
173- use crate :: rngs:: std:: StdRng ;
250+ use crate :: rngs:: std:: Core ;
174251 use crate :: test:: const_rng;
175252
176253 use super :: ReseedingRng ;
@@ -179,7 +256,7 @@ mod test {
179256 fn test_reseeding ( ) {
180257 let zero = const_rng ( 0 ) ;
181258 let thresh = 1 ; // reseed every time the buffer is exhausted
182- let mut reseeding = ReseedingRng :: < StdRng , _ > :: new ( thresh, zero) . unwrap ( ) ;
259+ let mut reseeding = ReseedingRng :: < Core , _ > :: new ( thresh, zero) . unwrap ( ) ;
183260
184261 // RNG buffer size is [u32; 64]
185262 // Debug is only implemented up to length 32 so use two arrays
0 commit comments