Skip to content

Commit 31d28f7

Browse files
committed
ReseedingRng: use trait Generator
1 parent d53a946 commit 31d28f7

File tree

4 files changed

+148
-69
lines changed

4 files changed

+148
-69
lines changed

rand_chacha/src/chacha.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
1111
use crate::guts::ChaCha;
1212
use core::fmt;
13-
use rand_core::{le, CryptoRng, RngCore, SeedableRng, Generator, CryptoGenerator};
13+
use rand_core::{CryptoGenerator, CryptoRng, Generator, RngCore, SeedableRng, le};
1414

1515
#[cfg(feature = "serde")]
1616
use serde::{Deserialize, Deserializer, Serialize, Serializer};
@@ -185,8 +185,7 @@ macro_rules! chacha_impl {
185185
pub fn set_word_pos(&mut self, word_offset: u128) {
186186
let block = (word_offset / u128::from(BLOCK_WORDS)) as u64;
187187
self.core.state.set_block_pos(block);
188-
self
189-
.generate_and_set((word_offset % u128::from(BLOCK_WORDS)) as usize);
188+
self.generate_and_set((word_offset % u128::from(BLOCK_WORDS)) as usize);
190189
}
191190

192191
/// Set the stream number.

src/rngs/reseeding.rs

Lines changed: 140 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,15 @@
1010
//! A wrapper around another PRNG that reseeds it after it
1111
//! generates a certain number of random bytes.
1212
13+
use core::fmt;
1314
use 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)]
6772
pub struct ReseedingRng<R, Rsdr>
6873
where
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>
7996
where
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>
163240
where
164-
R: CryptoRng + SeedableRng,
241+
R: Generator + SeedableRng + CryptoGenerator,
165242
Rsdr: TryCryptoRng,
166243
{
167244
}
@@ -170,7 +247,7 @@ where
170247
#[cfg(test)]
171248
mod 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

src/rngs/std.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
1111
use rand_core::{CryptoRng, RngCore, SeedableRng};
1212

13+
#[cfg(any(test, feature = "os_rng"))]
14+
pub(crate) use chacha20::ChaCha12Core as Core;
15+
1316
use chacha20::ChaCha12Rng as Rng;
1417

1518
/// A strong, fast (amortized), non-portable RNG

src/rngs/thread.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use std::fmt;
1313
use std::rc::Rc;
1414
use std::thread_local;
1515

16-
use super::{OsError, OsRng, ReseedingRng, std::StdRng};
16+
use super::{OsError, OsRng, ReseedingRng, std::Core};
1717
use rand_core::{CryptoRng, RngCore};
1818

1919
// Rationale for using `UnsafeCell` in `ThreadRng`:
@@ -90,7 +90,7 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64;
9090
#[derive(Clone)]
9191
pub struct ThreadRng {
9292
// Rc is explicitly !Send and !Sync
93-
rng: Rc<UnsafeCell<ReseedingRng<StdRng, OsRng>>>,
93+
rng: Rc<UnsafeCell<ReseedingRng<Core, OsRng>>>,
9494
}
9595

9696
impl ThreadRng {
@@ -115,7 +115,7 @@ impl fmt::Debug for ThreadRng {
115115
thread_local!(
116116
// We require Rc<..> to avoid premature freeing when ThreadRng is used
117117
// within thread-local destructors. See #968.
118-
static THREAD_RNG_KEY: Rc<UnsafeCell<ReseedingRng<StdRng, OsRng>>> = {
118+
static THREAD_RNG_KEY: Rc<UnsafeCell<ReseedingRng<Core, OsRng>>> = {
119119
let rng = ReseedingRng::new(THREAD_RNG_RESEED_THRESHOLD,
120120
OsRng).unwrap_or_else(|err|
121121
panic!("could not initialize ThreadRng: {}", err));

0 commit comments

Comments
 (0)