Coverage Report

Created: 2026-07-13 08:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rand-0.9.4/src/rngs/reseeding.rs
Line
Count
Source
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
13
use core::mem::size_of_val;
14
15
use rand_core::block::{BlockRng, BlockRngCore, CryptoBlockRng};
16
use 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)]
71
pub struct ReseedingRng<R, Rsdr>(BlockRng<ReseedingCore<R, Rsdr>>)
72
where
73
    R: BlockRngCore + SeedableRng,
74
    Rsdr: TryRngCore;
75
76
impl<R, Rsdr> ReseedingRng<R, Rsdr>
77
where
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
2
    pub fn new(threshold: u64, reseeder: Rsdr) -> Result<Self, Rsdr::Error> {
88
2
        Ok(ReseedingRng(BlockRng::new(ReseedingCore::new(
89
2
            threshold, reseeder,
90
0
        )?)))
91
2
    }
92
93
    /// Immediately reseed the generator
94
    ///
95
    /// This discards any remaining random data in the cache.
96
0
    pub fn reseed(&mut self) -> Result<(), Rsdr::Error> {
97
0
        self.0.reset();
98
0
        self.0.core.reseed()
99
0
    }
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
104
impl<R, Rsdr> RngCore for ReseedingRng<R, Rsdr>
105
where
106
    R: BlockRngCore<Item = u32> + SeedableRng,
107
    Rsdr: TryRngCore,
108
{
109
    #[inline(always)]
110
944k
    fn next_u32(&mut self) -> u32 {
111
944k
        self.0.next_u32()
112
944k
    }
<rand::rngs::reseeding::ReseedingRng<rand_chacha::chacha::ChaCha12Core, rand_core::os::OsRng> as rand_core::RngCore>::next_u32
Line
Count
Source
110
944k
    fn next_u32(&mut self) -> u32 {
111
944k
        self.0.next_u32()
112
944k
    }
Unexecuted instantiation: <rand::rngs::reseeding::ReseedingRng<rand_chacha::chacha::ChaCha12Core, rand_core::os::OsRng> as rand_core::RngCore>::next_u32
Unexecuted instantiation: <rand::rngs::reseeding::ReseedingRng<rand_chacha::chacha::ChaCha12Core, rand_core::os::OsRng> as rand_core::RngCore>::next_u32
Unexecuted instantiation: <rand::rngs::reseeding::ReseedingRng<_, _> as rand_core::RngCore>::next_u32
113
114
    #[inline(always)]
115
46
    fn next_u64(&mut self) -> u64 {
116
46
        self.0.next_u64()
117
46
    }
<rand::rngs::reseeding::ReseedingRng<rand_chacha::chacha::ChaCha12Core, rand_core::os::OsRng> as rand_core::RngCore>::next_u64
Line
Count
Source
115
46
    fn next_u64(&mut self) -> u64 {
116
46
        self.0.next_u64()
117
46
    }
Unexecuted instantiation: <rand::rngs::reseeding::ReseedingRng<rand_chacha::chacha::ChaCha12Core, rand_core::os::OsRng> as rand_core::RngCore>::next_u64
Unexecuted instantiation: <rand::rngs::reseeding::ReseedingRng<_, _> as rand_core::RngCore>::next_u64
118
119
0
    fn fill_bytes(&mut self, dest: &mut [u8]) {
120
0
        self.0.fill_bytes(dest)
121
0
    }
Unexecuted instantiation: <rand::rngs::reseeding::ReseedingRng<rand_chacha::chacha::ChaCha12Core, rand_core::os::OsRng> as rand_core::RngCore>::fill_bytes
Unexecuted instantiation: <rand::rngs::reseeding::ReseedingRng<_, _> as rand_core::RngCore>::fill_bytes
122
}
123
124
impl<R, Rsdr> Clone for ReseedingRng<R, Rsdr>
125
where
126
    R: BlockRngCore + SeedableRng + Clone,
127
    Rsdr: TryRngCore + Clone,
128
{
129
0
    fn clone(&self) -> ReseedingRng<R, Rsdr> {
130
        // Recreating `BlockRng` seems easier than cloning it and resetting
131
        // the index.
132
0
        ReseedingRng(BlockRng::new(self.0.core.clone()))
133
0
    }
134
}
135
136
impl<R, Rsdr> CryptoRng for ReseedingRng<R, Rsdr>
137
where
138
    R: BlockRngCore<Item = u32> + SeedableRng + CryptoBlockRng,
139
    Rsdr: TryCryptoRng,
140
{
141
}
142
143
#[derive(Debug)]
144
struct ReseedingCore<R, Rsdr> {
145
    inner: R,
146
    reseeder: Rsdr,
147
    threshold: i64,
148
    bytes_until_reseed: i64,
149
}
150
151
impl<R, Rsdr> BlockRngCore for ReseedingCore<R, Rsdr>
152
where
153
    R: BlockRngCore + SeedableRng,
154
    Rsdr: TryRngCore,
155
{
156
    type Item = <R as BlockRngCore>::Item;
157
    type Results = <R as BlockRngCore>::Results;
158
159
14.7k
    fn generate(&mut self, results: &mut Self::Results) {
160
14.7k
        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
57
            return self.reseed_and_generate(results);
165
14.7k
        }
166
14.7k
        let num_bytes = size_of_val(results.as_ref());
167
14.7k
        self.bytes_until_reseed -= num_bytes as i64;
168
14.7k
        self.inner.generate(results);
169
14.7k
    }
<rand::rngs::reseeding::ReseedingCore<rand_chacha::chacha::ChaCha12Core, rand_core::os::OsRng> as rand_core::block::BlockRngCore>::generate
Line
Count
Source
159
14.7k
    fn generate(&mut self, results: &mut Self::Results) {
160
14.7k
        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
57
            return self.reseed_and_generate(results);
165
14.7k
        }
166
14.7k
        let num_bytes = size_of_val(results.as_ref());
167
14.7k
        self.bytes_until_reseed -= num_bytes as i64;
168
14.7k
        self.inner.generate(results);
169
14.7k
    }
Unexecuted instantiation: <rand::rngs::reseeding::ReseedingCore<_, _> as rand_core::block::BlockRngCore>::generate
170
}
171
172
impl<R, Rsdr> ReseedingCore<R, Rsdr>
173
where
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
2
    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
2
        let threshold = if threshold == 0 {
187
0
            i64::MAX
188
2
        } else if threshold <= i64::MAX as u64 {
189
2
            threshold as i64
190
        } else {
191
0
            i64::MAX
192
        };
193
194
2
        let inner = R::try_from_rng(&mut reseeder)?;
195
196
2
        Ok(ReseedingCore {
197
2
            inner,
198
2
            reseeder,
199
2
            threshold,
200
2
            bytes_until_reseed: threshold,
201
2
        })
202
2
    }
203
204
    /// Reseed the internal PRNG.
205
57
    fn reseed(&mut self) -> Result<(), Rsdr::Error> {
206
57
        R::try_from_rng(&mut self.reseeder).map(|result| {
207
57
            self.bytes_until_reseed = self.threshold;
208
57
            self.inner = result
209
57
        })
210
57
    }
211
212
    #[inline(never)]
213
57
    fn reseed_and_generate(&mut self, results: &mut <Self as BlockRngCore>::Results) {
214
57
        let num_bytes = size_of_val(results.as_ref());
215
216
57
        if let Err(e) = self.reseed() {
217
0
            panic!("Reseeding RNG failed: {e}");
218
57
        }
219
220
57
        self.bytes_until_reseed = self.threshold - num_bytes as i64;
221
57
        self.inner.generate(results);
222
57
    }
<rand::rngs::reseeding::ReseedingCore<rand_chacha::chacha::ChaCha12Core, rand_core::os::OsRng>>::reseed_and_generate
Line
Count
Source
213
57
    fn reseed_and_generate(&mut self, results: &mut <Self as BlockRngCore>::Results) {
214
57
        let num_bytes = size_of_val(results.as_ref());
215
216
57
        if let Err(e) = self.reseed() {
217
0
            panic!("Reseeding RNG failed: {e}");
218
57
        }
219
220
57
        self.bytes_until_reseed = self.threshold - num_bytes as i64;
221
57
        self.inner.generate(results);
222
57
    }
Unexecuted instantiation: <rand::rngs::reseeding::ReseedingCore<_, _>>::reseed_and_generate
223
}
224
225
impl<R, Rsdr> Clone for ReseedingCore<R, Rsdr>
226
where
227
    R: BlockRngCore + SeedableRng + Clone,
228
    Rsdr: TryRngCore + Clone,
229
{
230
0
    fn clone(&self) -> ReseedingCore<R, Rsdr> {
231
0
        ReseedingCore {
232
0
            inner: self.inner.clone(),
233
0
            reseeder: self.reseeder.clone(),
234
0
            threshold: self.threshold,
235
0
            bytes_until_reseed: 0, // reseed clone on first use
236
0
        }
237
0
    }
238
}
239
240
impl<R, Rsdr> CryptoBlockRng for ReseedingCore<R, Rsdr>
241
where
242
    R: BlockRngCore<Item = u32> + SeedableRng + CryptoBlockRng,
243
    Rsdr: TryCryptoRng,
244
{
245
}
246
247
#[cfg(feature = "std_rng")]
248
#[cfg(test)]
249
mod 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
}