Skip to main content

reseeding_rng/
lib.rs

1//! [`ReseedingRng`] that periodically reseeds the underlying pseudorandom number
2//! generator.
3//!
4//! ```rust
5//! use rand::{RngExt as _, rngs::StdRng, rngs::SysRng};
6//! use reseeding_rng::ReseedingRng;
7//!
8//! let mut rng = ReseedingRng::<StdRng, _>::try_new(1024 * 64, SysRng)
9//!     .expect("couldn't initialize ReseedingRng due to SysRng failure");
10//! println!("{:?}", rng.random::<[char; 4]>());
11//! ```
12//!
13//! This crate provides a simplified reimplementation of `ReseedingRng` for use with
14//! the random number generators from the `rand` crate v0.10, which no longer
15//! includes [the `ReseedingRng` from v0.9] and earlier.
16//!
17//! This crate is `no_std`-compatible unless the `std_rng` feature is enabled.
18//!
19//! # Cargo features
20//!
21//! - `std_rng` (optional) enables [`StdReseedingRng`], a newtype wrapping
22//!   `ReseedingRng<StdRng, SysRng>` with a default reseeding threshold of 64KiB.
23//!
24//! [the `ReseedingRng` from v0.9]: https://docs.rs/rand/0.9.4/rand/rngs/struct.ReseedingRng.html
25
26#![cfg_attr(not(test), no_std)]
27#![cfg_attr(docsrs, feature(doc_cfg))]
28
29use core::fmt;
30use rand_core::{Rng, SeedableRng, TryCryptoRng, TryRng};
31
32/// A wrapper that periodically reseeds the underlying pseudorandom number generator.
33///
34/// This type reseeds the underlying generator every time a specified number of random bytes have
35/// been produced. If the periodic reseeding attempt fails, `ReseedingRng` silently skips it and
36/// retries after the next threshold is reached.
37///
38/// Unlike [`rand` v0.9's equivalent], this variant is built on top of [`TryRng`] instead of the
39/// block [`Generator`], allowing a wider choice of underlying generators, including [`StdRng`].
40///
41/// # Examples
42///
43/// `ReseedingRng` is useful to replicate the reseeding behavior of [`ThreadRng`]. As of `rand`
44/// v0.10.1, `ThreadRng` uses the same algorithm as [`StdRng`] and reseeds it via [`SysRng`] every
45/// 64KiB of output. You can emulate this behavior by configuring `ReseedingRng` as follows:
46///
47/// ```rust
48/// use rand::{RngExt as _, rngs::StdRng, rngs::SysRng};
49/// use reseeding_rng::ReseedingRng;
50///
51/// let mut rng = ReseedingRng::<StdRng, _>::try_new(1024 * 64, SysRng)
52///     .expect("couldn't initialize ReseedingRng due to SysRng failure");
53/// println!("{:?}", rng.random::<[char; 4]>());
54/// ```
55///
56/// See also [`StdReseedingRng`] for a convenient newtype with this configuration.
57///
58/// # Fork safety
59///
60/// The underlying generator is not automatically reseeded on process fork (contrast with
61/// `ReseedingRng` from `rand` v0.8 and earlier). Some applications need reseeding on fork to avoid
62/// the parent and child processes generating the same sequence of random numbers. The example
63/// below shows a wrapper that handles this using [the `forkguard` crate].
64///
65/// ```rust
66/// use rand::{Rng as _, rngs::StdRng, rngs::SysRng};
67///
68/// struct ForkSafeReseedingRng {
69///     inner: reseeding_rng::ReseedingRng<StdRng, SysRng>,
70///     guard: forkguard::Guard,
71/// }
72///
73/// impl ForkSafeReseedingRng {
74///     fn next_u32(&mut self) -> u32 {
75///         if self.guard.detected_fork() {
76///             // reseed ReseedingRng in child process
77///             let _ = self.inner.try_reseed();
78///         }
79///         self.inner.next_u32()
80///     }
81/// }
82/// ```
83///
84/// [`rand` v0.9's equivalent]: https://docs.rs/rand/0.9.4/rand/rngs/struct.ReseedingRng.html
85/// [`Generator`]: rand_core::block::Generator
86/// [`StdRng`]: https://docs.rs/rand/0.10/rand/rngs/struct.StdRng.html
87/// [`SysRng`]: https://docs.rs/rand/0.10/rand/rngs/struct.SysRng.html
88/// [`ThreadRng`]: https://docs.rs/rand/0.10/rand/rngs/struct.ThreadRng.html
89/// [the `forkguard` crate]: https://crates.io/crates/forkguard
90pub struct ReseedingRng<R, Rsdr> {
91    inner: R,
92    reseeder: Rsdr,
93    threshold: usize,
94    bytes_consumed: usize,
95}
96
97impl<R, Rsdr> ReseedingRng<R, Rsdr>
98where
99    R: SeedableRng,
100    Rsdr: TryRng,
101{
102    /// Creates a new instance with a reseeding threshold in bytes and a seed generator for
103    /// initialization and reseeding.
104    ///
105    /// # Panics
106    ///
107    /// Panics if `threshold` is zero.
108    ///
109    /// # Errors
110    ///
111    /// Returns `Err` if `reseeder` fails to seed the underlying generator.
112    #[track_caller]
113    pub fn try_new(threshold: usize, mut reseeder: Rsdr) -> Result<Self, Rsdr::Error> {
114        assert!(threshold > 0, "`threshold` must be greater than zero");
115        R::try_from_rng(&mut reseeder).map(|inner| Self {
116            inner,
117            reseeder,
118            threshold,
119            bytes_consumed: 0,
120        })
121    }
122
123    /// Reseeds the underlying generator immediately.
124    ///
125    /// # Errors
126    ///
127    /// Returns `Err` if `reseeder` fails to seed the underlying generator.
128    pub fn try_reseed(&mut self) -> Result<(), Rsdr::Error> {
129        R::try_from_rng(&mut self.reseeder).map(|inner| {
130            self.inner = inner;
131            self.bytes_consumed = 0;
132        })
133    }
134
135    #[cold]
136    fn reset_after_reseed_attempt_at(&mut self, pos: usize) {
137        // Unlike `ThreadRng` as of `rand` v0.10.1, this implementation does not panic if reseeding
138        // fails. Instead, it silently skips it and retries once the next threshold is reached. The
139        // reasoning is that users can easily get the panicking behavior by using `UnwrapErr`.
140        let _ = self.try_reseed();
141        self.bytes_consumed = pos;
142    }
143}
144
145impl<R, Rsdr> TryRng for ReseedingRng<R, Rsdr>
146where
147    R: TryRng + SeedableRng,
148    Rsdr: TryRng,
149{
150    type Error = R::Error;
151
152    fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
153        self.bytes_consumed += 32 / 8;
154        if self.bytes_consumed > self.threshold {
155            self.reset_after_reseed_attempt_at(32 / 8);
156        }
157        self.inner.try_next_u32()
158    }
159
160    fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
161        self.bytes_consumed += 64 / 8;
162        if self.bytes_consumed > self.threshold {
163            self.reset_after_reseed_attempt_at(64 / 8);
164        }
165        self.inner.try_next_u64()
166    }
167
168    fn try_fill_bytes(&mut self, mut dst: &mut [u8]) -> Result<(), Self::Error> {
169        loop {
170            if self.bytes_consumed + dst.len() <= self.threshold {
171                self.bytes_consumed += dst.len();
172                break self.inner.try_fill_bytes(dst);
173            }
174            if self.bytes_consumed < self.threshold {
175                let mid = self.threshold - self.bytes_consumed;
176                self.bytes_consumed += mid;
177                self.inner.try_fill_bytes(&mut dst[..mid])?;
178                dst = &mut dst[mid..];
179            }
180            self.reset_after_reseed_attempt_at(0);
181        }
182    }
183}
184
185impl<R, Rsdr> TryCryptoRng for ReseedingRng<R, Rsdr>
186where
187    R: TryCryptoRng + SeedableRng,
188    Rsdr: TryCryptoRng,
189{
190}
191
192/// This implementation reseeds the underlying generator upon `clone()`.
193impl<R, Rsdr> Clone for ReseedingRng<R, Rsdr>
194where
195    R: SeedableRng,
196    Rsdr: Clone + Rng,
197{
198    fn clone(&self) -> Self {
199        let mut reseeder = self.reseeder.clone();
200        Self {
201            inner: R::from_rng(&mut reseeder),
202            reseeder,
203            threshold: self.threshold,
204            bytes_consumed: 0,
205        }
206    }
207}
208
209impl<R, Rsdr> fmt::Debug for ReseedingRng<R, Rsdr>
210where
211    R: fmt::Debug,
212    Rsdr: fmt::Debug,
213{
214    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
215        f.debug_struct("ReseedingRng")
216            .field("inner", &self.inner)
217            .field("reseeder", &self.reseeder)
218            .field("threshold", &self.threshold)
219            .finish_non_exhaustive()
220    }
221}
222
223#[cfg(feature = "std_rng")]
224mod std_rng;
225
226#[cfg(feature = "std_rng")]
227pub use std_rng::StdReseedingRng;
228
229#[cfg(feature = "std_rng")]
230#[doc(no_inline)]
231pub use rand::RngExt; // re-exported for convenience
232
233#[cfg(test)]
234mod mock;
235
236#[cfg(test)]
237mod tests {
238    use super::*;
239    use rand::rngs::{StdRng, SysRng};
240
241    #[test]
242    fn mirror_rand09_reseeding_rng() {
243        use rand_chacha09::{ChaCha12Core, ChaCha12Rng};
244        use rand09::{RngCore as _, SeedableRng as _};
245
246        use mock::Rand09Adapter as Adapter;
247
248        type OurImpl = ReseedingRng<Adapter, Adapter>;
249        type TheirImpl = rand09::rngs::ReseedingRng<ChaCha12Core, ChaCha12Rng>;
250
251        const N: usize = 1024 * 16 * 5 + 997;
252
253        let seed = rand09::random();
254        let mut o = OurImpl::try_new(1024 * 16, Adapter::from_seed(seed)).unwrap();
255        let mut t = TheirImpl::new(1024 * 16, ChaCha12Rng::from_seed(seed)).unwrap();
256
257        for _ in 0..(N / 4) {
258            assert_eq!(o.next_u32(), t.next_u32());
259        }
260
261        o.try_reseed().unwrap();
262        t.reseed().unwrap();
263
264        for _ in 0..(N / 8) {
265            assert_eq!(o.next_u64(), t.next_u64());
266        }
267
268        o.try_reseed().unwrap();
269        t.reseed().unwrap();
270
271        let mut buf_o = vec![0u8; 17 * 4];
272        let mut buf_t = vec![0u8; buf_o.len()];
273        for _ in 0..(N / buf_o.len()) {
274            o.fill_bytes(&mut buf_o[..]);
275            t.fill_bytes(&mut buf_t[..]);
276            assert_eq!(buf_o, buf_t);
277        }
278
279        o.try_reseed().unwrap();
280        t.reseed().unwrap();
281
282        buf_o.resize(1024 * 16 * 2 + 7 * 4, 0);
283        buf_t.resize(buf_o.len(), 0);
284        for _ in 0..(N / buf_o.len()) {
285            o.fill_bytes(&mut buf_o[..]);
286            t.fill_bytes(&mut buf_t[..]);
287            assert_eq!(buf_o, buf_t);
288        }
289    }
290
291    #[test]
292    fn reseed_after_threshold() {
293        let seed = rand::random();
294        let mut g1 = StdRng::from_rng(&mut StdRng::from_seed(seed));
295        let mut g2 =
296            ReseedingRng::<StdRng, _>::try_new(1024 * 64, StdRng::from_seed(seed)).unwrap();
297
298        for _ in 0..(64 * 1024 / (32 / 8 + 32 / 8 + 64 / 8)) {
299            assert_eq!(g1.next_u32(), g2.next_u32());
300            assert_eq!(g1.next_u32(), g2.next_u32());
301            assert_eq!(g1.next_u64(), g2.next_u64());
302        }
303
304        assert_ne!(g1.next_u32(), g2.next_u32());
305        assert_ne!(g1.next_u64(), g2.next_u64());
306    }
307
308    #[test]
309    fn count_periodic_reseeds() {
310        use std::cell::Cell;
311
312        struct MockReseeder<'a> {
313            counter: &'a Cell<usize>,
314        }
315
316        impl TryRng for MockReseeder<'_> {
317            type Error = <SysRng as TryRng>::Error;
318
319            fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
320                self.counter.set(self.counter.get() + 1);
321                SysRng.try_next_u32()
322            }
323
324            fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
325                self.counter.set(self.counter.get() + 1);
326                SysRng.try_next_u64()
327            }
328
329            fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
330                self.counter.set(self.counter.get() + 1);
331                SysRng.try_fill_bytes(dst)
332            }
333        }
334
335        let counter = Cell::new(0);
336        let reseeder = MockReseeder { counter: &counter };
337        let mut rng = ReseedingRng::<StdRng, _>::try_new(10, reseeder).unwrap();
338        assert_eq!(counter.get(), 1);
339
340        rng.fill_bytes(&mut [0u8; 10]);
341        assert_eq!(counter.get(), 1);
342        assert_eq!(rng.bytes_consumed, 10);
343        rng.fill_bytes(&mut [0u8; 1]);
344        assert_eq!(counter.get(), 2);
345        assert_eq!(rng.bytes_consumed, 1);
346        rng.fill_bytes(&mut [0u8; 9]);
347        assert_eq!(counter.get(), 2);
348        assert_eq!(rng.bytes_consumed, 10);
349        rng.fill_bytes(&mut [0u8; 1]);
350        assert_eq!(counter.get(), 3);
351        assert_eq!(rng.bytes_consumed, 1);
352        rng.fill_bytes(&mut [0u8; 25]);
353        assert_eq!(counter.get(), 5);
354        assert_eq!(rng.bytes_consumed, 6);
355
356        rng.next_u32();
357        assert_eq!(counter.get(), 5);
358        assert_eq!(rng.bytes_consumed, 10);
359        rng.next_u32();
360        assert_eq!(counter.get(), 6);
361        assert_eq!(rng.bytes_consumed, 4);
362        rng.next_u32();
363        assert_eq!(counter.get(), 6);
364        assert_eq!(rng.bytes_consumed, 8);
365        rng.next_u32(); // discarding 2 bytes
366        assert_eq!(counter.get(), 7);
367        assert_eq!(rng.bytes_consumed, 4);
368
369        rng.fill_bytes(&mut [0u8; 7]);
370        assert_eq!(counter.get(), 8);
371        assert_eq!(rng.bytes_consumed, 1);
372        rng.next_u64();
373        assert_eq!(counter.get(), 8);
374        assert_eq!(rng.bytes_consumed, 9);
375        rng.next_u64(); // discarding 1 byte
376        assert_eq!(counter.get(), 9);
377        assert_eq!(rng.bytes_consumed, 8);
378    }
379
380    #[test]
381    #[should_panic]
382    fn panic_if_threshold_is_zero() {
383        let _ = ReseedingRng::<StdRng, _>::try_new(0, SysRng);
384    }
385
386    /// Tests in this module may occasionally fail.
387    mod fallible {
388        use super::*;
389
390        const N: usize = 20 * 256;
391
392        #[test]
393        fn generate_random_numbers() {
394            let mut rng = ReseedingRng::<StdRng, _>::try_new(1024, SysRng).unwrap();
395
396            let arrays = (0..N)
397                .map(|_| rng.next_u32().to_le_bytes())
398                .collect::<Vec<_>>();
399            assert!(check_each_byte_for_randomness(&arrays));
400
401            let arrays = (0..N)
402                .map(|_| rng.next_u64().to_le_bytes())
403                .collect::<Vec<_>>();
404            assert!(check_each_byte_for_randomness(&arrays));
405
406            let mut buf = [0u8; 17];
407            let arrays = (0..N)
408                .map(|_| {
409                    rng.fill_bytes(buf.as_mut());
410                    buf
411                })
412                .collect::<Vec<_>>();
413            assert!(check_each_byte_for_randomness(&arrays));
414        }
415
416        #[test]
417        fn handle_corner_cases() {
418            let mut rng = ReseedingRng::<StdRng, _>::try_new(1, SysRng).unwrap();
419
420            let arrays = (0..N)
421                .map(|_| rng.next_u32().to_le_bytes())
422                .collect::<Vec<_>>();
423            assert!(check_each_byte_for_randomness(&arrays));
424
425            let arrays = (0..N)
426                .map(|_| rng.next_u64().to_le_bytes())
427                .collect::<Vec<_>>();
428            assert!(check_each_byte_for_randomness(&arrays));
429
430            let mut buf = [0u8; 5];
431            let arrays = (0..N)
432                .map(|_| {
433                    rng.fill_bytes(buf.as_mut());
434                    buf
435                })
436                .collect::<Vec<_>>();
437            assert!(check_each_byte_for_randomness(&arrays));
438
439            let mut buf = [0u8; 0];
440            for _ in 0..N {
441                rng.fill_bytes(buf.as_mut());
442            }
443        }
444    }
445
446    pub(crate) fn check_each_byte_for_randomness<const N: usize>(arrays: &[[u8; N]]) -> bool {
447        (0..N).all(|i| {
448            let mut freq = [0usize; 256];
449            for array in arrays {
450                freq[array[i] as usize] += 1; // by column
451            }
452
453            let expected = arrays.len() as f64 / 256.0;
454            let chi_squared = freq.iter().fold(0.0, |acc, &observed| {
455                let dev = observed as f64 - expected;
456                acc + dev * dev / expected
457            });
458
459            chi_squared < 330.52 // df = 255, p = 0.001
460        })
461    }
462}