Skip to main content

rand/rngs/
reseeding.rs

1// Copyright 2018 Developers of the Rand project.
2// Copyright 2013 The Rust Project Developers.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! A wrapper around another PRNG that reseeds it after it
11//! generates a certain number of random bytes.
12
13use core::mem::size_of_val;
14
15use rand_core::block::{BlockRng, BlockRngCore, CryptoBlockRng};
16use rand_core::{CryptoRng, RngCore, SeedableRng, TryCryptoRng, TryRngCore};
17
18/// A wrapper around any PRNG that implements [`BlockRngCore`], that adds the
19/// ability to reseed it.
20///
21/// `ReseedingRng` reseeds the underlying PRNG in the following cases:
22///
23/// - On a manual call to [`reseed()`].
24/// - After `clone()`, the clone will be reseeded on first use.
25/// - After the PRNG has generated a configurable number of random bytes.
26///
27/// # When should reseeding after a fixed number of generated bytes be used?
28///
29/// Reseeding after a fixed number of generated bytes is never strictly
30/// *necessary*. Cryptographic PRNGs don't have a limited number of bytes they
31/// can output, or at least not a limit reachable in any practical way. There is
32/// no such thing as 'running out of entropy'.
33///
34/// Occasionally reseeding can be seen as some form of 'security in depth'. Even
35/// if in the future a cryptographic weakness is found in the CSPRNG being used,
36/// or a flaw in the implementation, occasionally reseeding should make
37/// exploiting it much more difficult or even impossible.
38///
39/// Use [`ReseedingRng::new`] with a `threshold` of `0` to disable reseeding
40/// after a fixed number of generated bytes.
41///
42/// # Error handling
43///
44/// Errors during reseeding are extremely unlikely, assuming the same random
45/// source successfully initialized the inner PRNG. A reseeding failure will be
46/// reported via panic (new behaviour since v0.9.3).
47///
48/// Manually calling [`reseed()`] will report errors.
49///
50/// # Example
51///
52/// ```
53/// use rand::prelude::*;
54/// use rand_chacha::ChaCha20Core; // Internal part of ChaChaRng that
55///                              // implements BlockRngCore
56/// use rand::rngs::OsRng;
57/// use rand::rngs::ReseedingRng;
58///
59/// let mut reseeding_rng = ReseedingRng::<ChaCha20Core, _>::new(0, OsRng).unwrap();
60///
61/// println!("{}", reseeding_rng.random::<u64>());
62///
63/// let mut cloned_rng = reseeding_rng.clone();
64/// assert!(reseeding_rng.random::<u64>() != cloned_rng.random::<u64>());
65/// ```
66///
67/// [`BlockRngCore`]: rand_core::block::BlockRngCore
68/// [`ReseedingRng::new`]: ReseedingRng::new
69/// [`reseed()`]: ReseedingRng::reseed
70#[derive(Debug)]
71pub struct ReseedingRng<R, Rsdr>(BlockRng<ReseedingCore<R, Rsdr>>)
72where
73    R: BlockRngCore + SeedableRng,
74    Rsdr: TryRngCore;
75
76impl<R, Rsdr> ReseedingRng<R, Rsdr>
77where
78    R: BlockRngCore + SeedableRng,
79    Rsdr: TryRngCore,
80{
81    /// Create a new `ReseedingRng` from an existing PRNG, combined with a RNG
82    /// to use as reseeder.
83    ///
84    /// `threshold` sets the number of generated bytes after which to reseed the
85    /// PRNG. Set it to zero to never reseed based on the number of generated
86    /// values.
87    pub fn new(threshold: u64, reseeder: Rsdr) -> Result<Self, Rsdr::Error> {
88        Ok(ReseedingRng(BlockRng::new(ReseedingCore::new(
89            threshold, reseeder,
90        )?)))
91    }
92
93    /// Immediately reseed the generator
94    ///
95    /// This discards any remaining random data in the cache.
96    pub fn reseed(&mut self) -> Result<(), Rsdr::Error> {
97        self.0.reset();
98        self.0.core.reseed()
99    }
100}
101
102// TODO: this should be implemented for any type where the inner type
103// implements RngCore, but we can't specify that because ReseedingCore is private
104impl<R, Rsdr> RngCore for ReseedingRng<R, Rsdr>
105where
106    R: BlockRngCore<Item = u32> + SeedableRng,
107    Rsdr: TryRngCore,
108{
109    #[inline(always)]
110    fn next_u32(&mut self) -> u32 {
111        self.0.next_u32()
112    }
113
114    #[inline(always)]
115    fn next_u64(&mut self) -> u64 {
116        self.0.next_u64()
117    }
118
119    fn fill_bytes(&mut self, dest: &mut [u8]) {
120        self.0.fill_bytes(dest)
121    }
122}
123
124impl<R, Rsdr> Clone for ReseedingRng<R, Rsdr>
125where
126    R: BlockRngCore + SeedableRng + Clone,
127    Rsdr: TryRngCore + Clone,
128{
129    fn clone(&self) -> ReseedingRng<R, Rsdr> {
130        // Recreating `BlockRng` seems easier than cloning it and resetting
131        // the index.
132        ReseedingRng(BlockRng::new(self.0.core.clone()))
133    }
134}
135
136impl<R, Rsdr> CryptoRng for ReseedingRng<R, Rsdr>
137where
138    R: BlockRngCore<Item = u32> + SeedableRng + CryptoBlockRng,
139    Rsdr: TryCryptoRng,
140{
141}
142
143#[derive(Debug)]
144struct ReseedingCore<R, Rsdr> {
145    inner: R,
146    reseeder: Rsdr,
147    threshold: i64,
148    bytes_until_reseed: i64,
149}
150
151impl<R, Rsdr> BlockRngCore for ReseedingCore<R, Rsdr>
152where
153    R: BlockRngCore + SeedableRng,
154    Rsdr: TryRngCore,
155{
156    type Item = <R as BlockRngCore>::Item;
157    type Results = <R as BlockRngCore>::Results;
158
159    fn generate(&mut self, results: &mut Self::Results) {
160        if self.bytes_until_reseed <= 0 {
161            // We get better performance by not calling only `reseed` here
162            // and continuing with the rest of the function, but by directly
163            // returning from a non-inlined function.
164            return self.reseed_and_generate(results);
165        }
166        let num_bytes = size_of_val(results.as_ref());
167        self.bytes_until_reseed -= num_bytes as i64;
168        self.inner.generate(results);
169    }
170}
171
172impl<R, Rsdr> ReseedingCore<R, Rsdr>
173where
174    R: BlockRngCore + SeedableRng,
175    Rsdr: TryRngCore,
176{
177    /// Create a new `ReseedingCore`.
178    ///
179    /// `threshold` is the maximum number of bytes produced by
180    /// [`BlockRngCore::generate`] before attempting reseeding.
181    fn new(threshold: u64, mut reseeder: Rsdr) -> Result<Self, Rsdr::Error> {
182        // Because generating more values than `i64::MAX` takes centuries on
183        // current hardware, we just clamp to that value.
184        // Also we set a threshold of 0, which indicates no limit, to that
185        // value.
186        let threshold = if threshold == 0 {
187            i64::MAX
188        } else if threshold <= i64::MAX as u64 {
189            threshold as i64
190        } else {
191            i64::MAX
192        };
193
194        let inner = R::try_from_rng(&mut reseeder)?;
195
196        Ok(ReseedingCore {
197            inner,
198            reseeder,
199            threshold,
200            bytes_until_reseed: threshold,
201        })
202    }
203
204    /// Reseed the internal PRNG.
205    fn reseed(&mut self) -> Result<(), Rsdr::Error> {
206        R::try_from_rng(&mut self.reseeder).map(|result| {
207            self.bytes_until_reseed = self.threshold;
208            self.inner = result
209        })
210    }
211
212    #[inline(never)]
213    fn reseed_and_generate(&mut self, results: &mut <Self as BlockRngCore>::Results) {
214        let num_bytes = size_of_val(results.as_ref());
215
216        if let Err(e) = self.reseed() {
217            panic!("Reseeding RNG failed: {e}");
218        }
219
220        self.bytes_until_reseed = self.threshold - num_bytes as i64;
221        self.inner.generate(results);
222    }
223}
224
225impl<R, Rsdr> Clone for ReseedingCore<R, Rsdr>
226where
227    R: BlockRngCore + SeedableRng + Clone,
228    Rsdr: TryRngCore + Clone,
229{
230    fn clone(&self) -> ReseedingCore<R, Rsdr> {
231        ReseedingCore {
232            inner: self.inner.clone(),
233            reseeder: self.reseeder.clone(),
234            threshold: self.threshold,
235            bytes_until_reseed: 0, // reseed clone on first use
236        }
237    }
238}
239
240impl<R, Rsdr> CryptoBlockRng for ReseedingCore<R, Rsdr>
241where
242    R: BlockRngCore<Item = u32> + SeedableRng + CryptoBlockRng,
243    Rsdr: TryCryptoRng,
244{
245}
246
247#[cfg(feature = "std_rng")]
248#[cfg(test)]
249mod test {
250    use crate::rngs::std::Core;
251    use crate::test::const_rng;
252    use crate::Rng;
253
254    use super::ReseedingRng;
255
256    #[test]
257    fn test_reseeding() {
258        let zero = const_rng(0);
259        let thresh = 1; // reseed every time the buffer is exhausted
260        let mut reseeding = ReseedingRng::<Core, _>::new(thresh, zero).unwrap();
261
262        // RNG buffer size is [u32; 64]
263        // Debug is only implemented up to length 32 so use two arrays
264        let mut buf = ([0u32; 32], [0u32; 32]);
265        reseeding.fill(&mut buf.0);
266        reseeding.fill(&mut buf.1);
267        let seq = buf;
268        for _ in 0..10 {
269            reseeding.fill(&mut buf.0);
270            reseeding.fill(&mut buf.1);
271            assert_eq!(buf, seq);
272        }
273    }
274
275    #[test]
276    #[allow(clippy::redundant_clone)]
277    fn test_clone_reseeding() {
278        let zero = const_rng(0);
279        let mut rng1 = ReseedingRng::<Core, _>::new(32 * 4, zero).unwrap();
280
281        let first: u32 = rng1.random();
282        for _ in 0..10 {
283            let _ = rng1.random::<u32>();
284        }
285
286        let mut rng2 = rng1.clone();
287        assert_eq!(first, rng2.random::<u32>());
288    }
289}