Skip to content

Commit 7b14992

Browse files
authored
Merge pull request #383 from pitdicker/impl_default_doc
Improve doc on implementing `Default` and `Clone`
2 parents 5641c0d + 6060f1a commit 7b14992

File tree

4 files changed

+81
-10
lines changed

4 files changed

+81
-10
lines changed

rand_core/src/lib.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,16 @@ pub mod le;
9191
/// It is recommended that implementations also implement:
9292
///
9393
/// - `Debug` with a custom implementation which *does not* print any internal
94-
/// state (at least, [`CryptoRng`]s should not risk leaking state through Debug)
94+
/// state (at least, [`CryptoRng`]s should not risk leaking state through
95+
/// `Debug`).
9596
/// - `Serialize` and `Deserialize` (from Serde), preferably making Serde
96-
/// support optional at the crate level in PRNG libs
97-
/// - `Clone` if, and only if, the clone will have identical output to the
98-
/// original (i.e. all deterministic PRNGs but not external generators)
99-
/// - *never* implement `Copy` (accidental copies may cause repeated values)
100-
/// - also *do not* implement `Default`, but instead implement `SeedableRng`
101-
/// thus allowing use of `rand::FromEntropy` (which is automatically implemented)
102-
/// - `Eq` and `PartialEq` could be implemented, but are probably not useful
97+
/// support optional at the crate level in PRNG libs.
98+
/// - `Clone`, if possible.
99+
/// - *never* implement `Copy` (accidental copies may cause repeated values).
100+
/// - *do not* implement `Default` for pseudorandom generators, but instead
101+
/// implement [`SeedableRng`], to guide users towards proper seeding.
102+
/// External / hardware RNGs can choose to implement `Default`.
103+
/// - `Eq` and `PartialEq` could be implemented, but are probably not useful.
103104
///
104105
/// # Example
105106
///

src/jitter.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,22 @@ pub struct JitterRng {
6363
data_half_used: bool,
6464
}
6565

66+
// Note: `JitterRng` maintains a small 64-bit entropy pool. With every
67+
// `generate` 64 new bits should be integrated in the pool. If a round of
68+
// `generate` were to collect less than the expected 64 bit, then the returned
69+
// value, and the new state of the entropy pool, would be in some way related to
70+
// the initial state. It is therefore better if the initial state of the entropy
71+
// pool is different on each call to `generate`. This has a few implications:
72+
// - `generate` should be called once before using `JitterRng` to produce the
73+
// first usable value (this is done by default in `new`);
74+
// - We do not zero the entropy pool after generating a result. The reference
75+
// implementation also does not support zeroing, but recommends generating a
76+
// new value without using it if you want to protect a previously generated
77+
// 'secret' value from someone inspecting the memory;
78+
// - Implementing `Clone` seems acceptable, as it would not cause the systematic
79+
// bias a constant might cause. Only instead of one value that could be
80+
// potentially related to the same initial state, there are now two.
81+
6682
// Entropy collector state.
6783
// These values are not necessary to preserve across runs.
6884
struct EcState {
@@ -103,6 +119,20 @@ impl fmt::Debug for JitterRng {
103119
}
104120
}
105121

122+
impl Clone for JitterRng {
123+
fn clone(&self) -> JitterRng {
124+
JitterRng {
125+
data: self.data,
126+
rounds: self.rounds,
127+
timer: self.timer,
128+
mem_prev_index: self.mem_prev_index,
129+
// The 32 bits that may still be unused from the previous round are
130+
// for the original to use, not for the clone.
131+
data_half_used: false,
132+
}
133+
}
134+
}
135+
106136
/// An error that can occur when [`JitterRng::test_timer`] fails.
107137
///
108138
/// [`JitterRng::test_timer`]: struct.JitterRng.html#method.test_timer

src/os.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ use rand_core::{RngCore, Error, impls};
2929
/// machines, it may block very early in the init process, when the OS CSPRNG
3030
/// has not yet been seeded.
3131
///
32-
/// `OsRng::new()` is guaranteed to be very cheap (after first call), and will
33-
/// never consume more than one file handle per process.
32+
/// `OsRng::new()` is guaranteed to be very cheap (after the first successful
33+
/// call), and will never consume more than one file handle per process.
3434
///
3535
/// ## Platform sources:
3636
///

src/reseeding.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ use rand_core::impls::BlockRng;
1919
/// A wrapper around any PRNG which reseeds the underlying PRNG after it has
2020
/// generated a certain number of random bytes.
2121
///
22+
/// When the RNG gets cloned, the clone is reseeded on first use.
23+
///
2224
/// Reseeding is never strictly *necessary*. Cryptographic PRNGs don't have a
2325
/// limited number of bytes they can output, or at least not a limit reachable
2426
/// in any practical way. There is no such thing as 'running out of entropy'.
@@ -103,6 +105,17 @@ where R: BlockRngCore<Item = u32> + SeedableRng,
103105
}
104106
}
105107

108+
impl<R, Rsdr> Clone for ReseedingRng<R, Rsdr>
109+
where R: BlockRngCore + SeedableRng + Clone,
110+
Rsdr: RngCore + Clone
111+
{
112+
fn clone(&self) -> ReseedingRng<R, Rsdr> {
113+
// Recreating `BlockRng` seems easier than cloning it and resetting
114+
// the index.
115+
ReseedingRng(BlockRng::new(self.0.inner().clone()))
116+
}
117+
}
118+
106119
impl<R, Rsdr> CryptoRng for ReseedingRng<R, Rsdr>
107120
where R: BlockRngCore + SeedableRng + CryptoRng,
108121
Rsdr: RngCore + CryptoRng {}
@@ -189,6 +202,20 @@ where R: BlockRngCore + SeedableRng,
189202
}
190203
}
191204

205+
impl<R, Rsdr> Clone for ReseedingCore<R, Rsdr>
206+
where R: BlockRngCore + SeedableRng + Clone,
207+
Rsdr: RngCore + Clone
208+
{
209+
fn clone(&self) -> ReseedingCore<R, Rsdr> {
210+
ReseedingCore {
211+
inner: self.inner.clone(),
212+
reseeder: self.reseeder.clone(),
213+
threshold: self.threshold,
214+
bytes_until_reseed: 0, // reseed clone on first use
215+
}
216+
}
217+
}
218+
192219
impl<R, Rsdr> CryptoRng for ReseedingCore<R, Rsdr>
193220
where R: BlockRngCore + SeedableRng + CryptoRng,
194221
Rsdr: RngCore + CryptoRng {}
@@ -217,4 +244,17 @@ mod test {
217244
assert_eq!(buf, seq);
218245
}
219246
}
247+
248+
#[test]
249+
fn test_clone_reseeding() {
250+
let mut zero = StepRng::new(0, 0);
251+
let rng = ChaChaCore::from_rng(&mut zero).unwrap();
252+
let mut rng1 = ReseedingRng::new(rng, 32*4, zero);
253+
254+
let first: u32 = rng1.gen();
255+
for _ in 0..10 { let _ = rng1.gen::<u32>(); }
256+
257+
let mut rng2 = rng1.clone();
258+
assert_eq!(first, rng2.gen::<u32>());
259+
}
220260
}

0 commit comments

Comments
 (0)