Skip to main content

entropy/rng/
mod.rs

1//! [`Rng`] trait and all generator implementations used by the test suite.
2
3pub mod aes_ctr;
4pub mod bad;
5pub mod block_ctr;
6pub mod c_stdlib;
7pub mod chacha20_rng;
8pub mod crypto_cprng;
9pub mod dual_ec;
10pub mod hash_drbg;
11pub mod hmac_drbg;
12pub mod lcg;
13pub mod mt19937;
14pub mod os;
15pub mod pcg;
16pub mod sfc;
17pub mod spongebob;
18pub mod squidward;
19pub mod stream_rng;
20pub mod wyrand;
21pub mod xorshift;
22pub mod xoshiro;
23
24pub use aes_ctr::AesCtr;
25pub use bad::{ConstantRng, CounterRng};
26pub use block_ctr::BlockCtrRng;
27pub use c_stdlib::{
28    BsdRandCompat, BsdRandom, CRand, LinuxLibcRandom, Rand48, SystemVRand, WindowsDotNetRandom,
29    WindowsMsvcRand, WindowsVb6Rnd,
30};
31pub use chacha20_rng::ChaCha20Rng;
32pub use crypto_cprng::CryptoCtrDrbg;
33pub use dual_ec::DualEcDrbg;
34pub use hash_drbg::HashDrbg;
35pub use hmac_drbg::HmacDrbg;
36pub use lcg::{Lcg32, LcgVariant};
37pub use mt19937::Mt19937;
38pub use os::OsRng;
39pub use pcg::{Pcg32, Pcg64};
40pub use sfc::{Jsf64, Sfc64};
41pub use spongebob::SpongeBob;
42pub use squidward::Squidward;
43pub use stream_rng::StreamRng;
44pub use wyrand::WyRand;
45pub use xorshift::{Xorshift32, Xorshift64};
46pub use xoshiro::{Xoroshiro128, Xoshiro256};
47
48// ── Rng trait ─────────────────────────────────────────────────────────────────
49
50/// Minimal interface required by every test.
51///
52/// All tests consume bits or 32-bit words; the trait methods below are the
53/// only ones needed.  Blanket impls fill in the derived methods.
54///
55/// ## Design note — no CSPRNG marker type
56///
57/// This trait is intentionally flat: every generator from `ConstantRng` to
58/// `ChaCha20Rng` implements the same `Rng`.  This is correct for a test
59/// harness whose job is to compare generators uniformly, but it means the
60/// **type system provides no barrier** against substituting a weak generator
61/// where a strong one is required.  Any function that accepts `impl Rng` will
62/// silently compile with `ConstantRng` or `SystemVRand`.
63///
64/// **Do not copy this design into production code.**  In an application,
65/// define a separate `CsprngRng: Rng` marker subtrait (or a newtype) and
66/// restrict security-sensitive functions to `impl CsprngRng` so that weak
67/// generators are rejected at compile time.
68///
69/// ## Byte and word ordering contract
70///
71/// * **`next_u64` default** — assembles two `next_u32` calls with the *first*
72///   call becoming the **high** 32 bits: `(hi << 32) | lo`.  Generators that
73///   override this (e.g. byte-backed DRBG adapters) may emit a different
74///   interleaving; those overrides are documented on the concrete type.
75///
76/// * **`collect_bits`** — extracts bits **LSB-first** from each 32-bit word:
77///   bit 0 of word 0 is the first element of the returned slice.
78///
79/// * **Byte-backed generators** (HMAC_DRBG, Hash_DRBG, ChaCha20, Squidward)
80///   expose a little-endian byte stream: `next_u32` reads 4 bytes in LE order,
81///   `next_u64` reads 8 bytes in LE order, independently of the default
82///   `next_u64` above.  Mixing `next_u32` and `next_u64` at a buffer refill
83///   boundary silently discards up to 7 trailing bytes; see the individual
84///   adapter doc comments for the full caveat.
85pub trait Rng {
86    /// Return the next 32-bit pseudo-random word.
87    fn next_u32(&mut self) -> u32;
88
89    /// Return the next 64-bit pseudo-random word.
90    ///
91    /// Default: two `next_u32` calls, first call → high 32 bits.
92    /// Byte-backed generators override this to read 8 LE bytes directly.
93    fn next_u64(&mut self) -> u64 {
94        ((self.next_u32() as u64) << 32) | (self.next_u32() as u64)
95    }
96
97    /// Uniform float in \[0, 1) built from **32 bits** of the generator's output.
98    ///
99    /// This default always calls `next_u32`, even for generators that natively
100    /// produce 64-bit output (PCG64, Xoshiro256, ChaCha20Rng, etc.).  Those
101    /// generators' upper 32 bits are discarded, so `next_f64` never delivers
102    /// more than 2³² distinct values regardless of the underlying generator.
103    /// For the statistical tests in this crate (which use floats only for
104    /// p-value lookup) this is fine.  Do not use `next_f64` to sample
105    /// high-precision distributions from a 64-bit generator.
106    fn next_f64(&mut self) -> f64 {
107        self.next_u32() as f64 * (1.0 / 4_294_967_296.0)
108    }
109
110    /// Collect `n` bits as `Vec<u8>` values 0 or 1, LSB-first from each word.
111    fn collect_bits(&mut self, n: usize) -> Vec<u8> {
112        let mut bits = Vec::with_capacity(n);
113        let mut remaining = n;
114        while remaining > 0 {
115            let word = self.next_u32();
116            let take = remaining.min(32);
117            for i in 0..take {
118                bits.push(((word >> i) & 1) as u8);
119            }
120            remaining -= take;
121        }
122        bits
123    }
124
125    /// Collect `n` 32-bit words.
126    fn collect_u32s(&mut self, n: usize) -> Vec<u32> {
127        (0..n).map(|_| self.next_u32()).collect()
128    }
129
130    /// Collect `n` floats in \[0, 1).
131    fn collect_f64s(&mut self, n: usize) -> Vec<f64> {
132        (0..n).map(|_| self.next_f64()).collect()
133    }
134}