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/// Although unlikely, reseeding the wrapped PRNG can fail. `ReseedingRng` will
45/// never panic but try to handle the error intelligently through some
46/// combination of retrying and delaying reseeding until later.
47/// If handling the source error fails `ReseedingRng` will continue generating
48/// data from the wrapped PRNG without reseeding.
49///
50/// Manually calling [`reseed()`] will not have this retry or delay logic, but
51/// reports the error.
52///
53/// # Example
54///
55/// ```
56/// use rand::prelude::*;
57/// use rand_chacha::ChaCha20Core; // Internal part of ChaChaRng that
58///                              // implements BlockRngCore
59/// use rand::rngs::OsRng;
60/// use rand::rngs::ReseedingRng;
61///
62/// let mut reseeding_rng = ReseedingRng::<ChaCha20Core, _>::new(0, OsRng).unwrap();
63///
64/// println!("{}", reseeding_rng.random::<u64>());
65///
66/// let mut cloned_rng = reseeding_rng.clone();
67/// assert!(reseeding_rng.random::<u64>() != cloned_rng.random::<u64>());
68/// ```
69///
70/// [`BlockRngCore`]: rand_core::block::BlockRngCore
71/// [`ReseedingRng::new`]: ReseedingRng::new
72/// [`reseed()`]: ReseedingRng::reseed
73#[derive(Debug)]
74pub struct ReseedingRng<R, Rsdr>(BlockRng<ReseedingCore<R, Rsdr>>)
75where
76    R: BlockRngCore + SeedableRng,
77    Rsdr: TryRngCore;
78
79impl<R, Rsdr> ReseedingRng<R, Rsdr>
80where
81    R: BlockRngCore + SeedableRng,
82    Rsdr: TryRngCore,
83{
84    /// Create a new `ReseedingRng` from an existing PRNG, combined with a RNG
85    /// to use as reseeder.
86    ///
87    /// `threshold` sets the number of generated bytes after which to reseed the
88    /// PRNG. Set it to zero to never reseed based on the number of generated
89    /// values.
90    pub fn new(threshold: u64, reseeder: Rsdr) -> Result<Self, Rsdr::Error> {
91        Ok(ReseedingRng(BlockRng::new(ReseedingCore::new(
92            threshold, reseeder,
93        )?)))
94    }
95
96    /// Immediately reseed the generator
97    ///
98    /// This discards any remaining random data in the cache.
99    pub fn reseed(&mut self) -> Result<(), Rsdr::Error> {
100        self.0.reset();
101        self.0.core.reseed()
102    }
103}
104
105// TODO: this should be implemented for any type where the inner type
106// implements RngCore, but we can't specify that because ReseedingCore is private
107impl<R, Rsdr> RngCore for ReseedingRng<R, Rsdr>
108where
109    R: BlockRngCore<Item = u32> + SeedableRng,
110    Rsdr: TryRngCore,
111{
112    #[inline(always)]
113    fn next_u32(&mut self) -> u32 {
114        self.0.next_u32()
115    }
116
117    #[inline(always)]
118    fn next_u64(&mut self) -> u64 {
119        self.0.next_u64()
120    }
121
122    fn fill_bytes(&mut self, dest: &mut [u8]) {
123        self.0.fill_bytes(dest)
124    }
125}
126
127impl<R, Rsdr> Clone for ReseedingRng<R, Rsdr>
128where
129    R: BlockRngCore + SeedableRng + Clone,
130    Rsdr: TryRngCore + Clone,
131{
132    fn clone(&self) -> ReseedingRng<R, Rsdr> {
133        // Recreating `BlockRng` seems easier than cloning it and resetting
134        // the index.
135        ReseedingRng(BlockRng::new(self.0.core.clone()))
136    }
137}
138
139impl<R, Rsdr> CryptoRng for ReseedingRng<R, Rsdr>
140where
141    R: BlockRngCore<Item = u32> + SeedableRng + CryptoBlockRng,
142    Rsdr: TryCryptoRng,
143{
144}
145
146#[derive(Debug)]
147struct ReseedingCore<R, Rsdr> {
148    inner: R,
149    reseeder: Rsdr,
150    threshold: i64,
151    bytes_until_reseed: i64,
152}
153
154impl<R, Rsdr> BlockRngCore for ReseedingCore<R, Rsdr>
155where
156    R: BlockRngCore + SeedableRng,
157    Rsdr: TryRngCore,
158{
159    type Item = <R as BlockRngCore>::Item;
160    type Results = <R as BlockRngCore>::Results;
161
162    fn generate(&mut self, results: &mut Self::Results) {
163        if self.bytes_until_reseed <= 0 {
164            // We get better performance by not calling only `reseed` here
165            // and continuing with the rest of the function, but by directly
166            // returning from a non-inlined function.
167            return self.reseed_and_generate(results);
168        }
169        let num_bytes = size_of_val(results.as_ref());
170        self.bytes_until_reseed -= num_bytes as i64;
171        self.inner.generate(results);
172    }
173}
174
175impl<R, Rsdr> ReseedingCore<R, Rsdr>
176where
177    R: BlockRngCore + SeedableRng,
178    Rsdr: TryRngCore,
179{
180    /// Create a new `ReseedingCore`.
181    ///
182    /// `threshold` is the maximum number of bytes produced by
183    /// [`BlockRngCore::generate`] before attempting reseeding.
184    fn new(threshold: u64, mut reseeder: Rsdr) -> Result<Self, Rsdr::Error> {
185        // Because generating more values than `i64::MAX` takes centuries on
186        // current hardware, we just clamp to that value.
187        // Also we set a threshold of 0, which indicates no limit, to that
188        // value.
189        let threshold = if threshold == 0 {
190            i64::MAX
191        } else if threshold <= i64::MAX as u64 {
192            threshold as i64
193        } else {
194            i64::MAX
195        };
196
197        let inner = R::try_from_rng(&mut reseeder)?;
198
199        Ok(ReseedingCore {
200            inner,
201            reseeder,
202            threshold,
203            bytes_until_reseed: threshold,
204        })
205    }
206
207    /// Reseed the internal PRNG.
208    fn reseed(&mut self) -> Result<(), Rsdr::Error> {
209        R::try_from_rng(&mut self.reseeder).map(|result| {
210            self.bytes_until_reseed = self.threshold;
211            self.inner = result
212        })
213    }
214
215    #[inline(never)]
216    fn reseed_and_generate(&mut self, results: &mut <Self as BlockRngCore>::Results) {
217        trace!("Reseeding RNG (periodic reseed)");
218
219        let num_bytes = size_of_val(results.as_ref());
220
221        if let Err(e) = self.reseed() {
222            warn!("Reseeding RNG failed: {}", e);
223            let _ = e;
224        }
225
226        self.bytes_until_reseed = self.threshold - num_bytes as i64;
227        self.inner.generate(results);
228    }
229}
230
231impl<R, Rsdr> Clone for ReseedingCore<R, Rsdr>
232where
233    R: BlockRngCore + SeedableRng + Clone,
234    Rsdr: TryRngCore + Clone,
235{
236    fn clone(&self) -> ReseedingCore<R, Rsdr> {
237        ReseedingCore {
238            inner: self.inner.clone(),
239            reseeder: self.reseeder.clone(),
240            threshold: self.threshold,
241            bytes_until_reseed: 0, // reseed clone on first use
242        }
243    }
244}
245
246impl<R, Rsdr> CryptoBlockRng for ReseedingCore<R, Rsdr>
247where
248    R: BlockRngCore<Item = u32> + SeedableRng + CryptoBlockRng,
249    Rsdr: TryCryptoRng,
250{
251}
252
253#[cfg(feature = "std_rng")]
254#[cfg(test)]
255mod test {
256    use crate::rngs::mock::StepRng;
257    use crate::rngs::std::Core;
258    use crate::Rng;
259
260    use super::ReseedingRng;
261
262    #[test]
263    fn test_reseeding() {
264        let zero = StepRng::new(0, 0);
265        let thresh = 1; // reseed every time the buffer is exhausted
266        let mut reseeding = ReseedingRng::<Core, _>::new(thresh, zero).unwrap();
267
268        // RNG buffer size is [u32; 64]
269        // Debug is only implemented up to length 32 so use two arrays
270        let mut buf = ([0u32; 32], [0u32; 32]);
271        reseeding.fill(&mut buf.0);
272        reseeding.fill(&mut buf.1);
273        let seq = buf;
274        for _ in 0..10 {
275            reseeding.fill(&mut buf.0);
276            reseeding.fill(&mut buf.1);
277            assert_eq!(buf, seq);
278        }
279    }
280
281    #[test]
282    #[allow(clippy::redundant_clone)]
283    fn test_clone_reseeding() {
284        let zero = StepRng::new(0, 0);
285        let mut rng1 = ReseedingRng::<Core, _>::new(32 * 4, zero).unwrap();
286
287        let first: u32 = rng1.random();
288        for _ in 0..10 {
289            let _ = rng1.random::<u32>();
290        }
291
292        let mut rng2 = rng1.clone();
293        assert_eq!(first, rng2.random::<u32>());
294    }
295}