How does rand::rngs::StdRng::from_entropy seed a reproducible RNG from system entropy sources?
StdRng::from_entropy creates a random number generator seeded from operating system entropy sources through the SeedableRng traitβit gathers cryptographically secure randomness from the OS (via getrandom on Unix-like systems, RtlGenRandom on Windows) to initialize the RNG's internal state, providing unpredictable starting conditions for applications requiring non-deterministic randomness, while reproducibility is achieved through from_seed with explicit seed values, not from_entropy. The method bridges the gap between deterministic PRNG algorithms (which need initial state) and unpredictable real-world randomness (from hardware and OS entropy pools).
The SeedableRng Trait and from_entropy
use rand::prelude::*;
use rand::rngs::StdRng;
use rand::SeedableRng;
// The SeedableRng trait defines how RNGs are initialized:
pub trait SeedableRng: Sized {
type Seed: AsMut<[u8]> + Default + Sized;
/// Create a new RNG from a fixed seed.
/// DETERMINISTIC: same seed always produces same sequence.
fn from_seed(seed: Self::Seed) -> Self;
/// Create a new RNG from an unpredictable entropy source.
/// NON-DETERMINISTIC: each call produces different starting state.
fn from_entropy() -> Self {
let mut seed = Self::Seed::default();
rand::fill(&mut seed).expect("failed to fill seed");
Self::from_seed(seed)
}
/// Create a new RNG from a u64 seed.
fn seed_from_u64(state: u64) -> Self;
}
// StdRng uses ChaCha12 (or ChaCha20 depending on version) as its core:
fn from_entropy_implementation() {
// StdRng::from_entropy() does the following:
// 1. Create default seed (all zeros, typically 32 bytes)
let mut seed = <StdRng as SeedableRng>::Seed::default();
// seed = [0, 0, 0, ..., 0] (32 bytes for ChaCha)
// 2. Fill seed with entropy from OS
rand::fill(&mut seed).expect("failed to gather entropy");
// seed now contains unpredictable random bytes
// 3. Create RNG from seed
let rng = StdRng::from_seed(seed);
// rng is now initialized with unpredictable state
// Equivalent to:
let rng = StdRng::from_entropy();
}
// The seed size depends on the RNG algorithm:
fn seed_sizes() {
// StdRng (ChaCha12/20): 32-byte seed
type StdSeed = <StdRng as SeedableRng>::Seed;
assert_eq!(std::mem::size_of::<StdSeed>(), 32);
// SmallRng (PCG): smaller seed
use rand::rngs::SmallRng;
type SmallSeed = <SmallRng as SeedableRng>::Seed;
// Typically 16 bytes
// ChaCha20 core needs 256 bits (32 bytes) of key material
// plus additional bytes for nonce/counter
}from_entropy is a default trait method that fills a default seed with OS entropy and passes it to from_seed.
System Entropy Sources
use rand::rngs::StdRng;
use rand::SeedableRng;
// The rand crate uses the getrandom crate for entropy:
fn entropy_sources() {
// On Unix-like systems (Linux, macOS, BSD):
// - /dev/urandom (preferred)
// - /dev/random (blocking, rarely needed)
// - getrandom() syscall (Linux 3.17+)
// - sysctl KERN_ARND (older systems)
// On Windows:
// - RtlGenRandom (BCryptGenRandom on newer systems)
// - CryptoAPI's CryptGenRandom (legacy)
// On WebAssembly:
// - Web Crypto API (window.crypto.getRandomValues)
// - Node.js crypto module (require('crypto').randomBytes)
// On embedded (requires feature flags):
// - Hardware RNG peripheral
// - Custom entropy source implementation
// The getrandom crate handles platform differences:
// Internally, rand::fill calls getrandom::getrandom:
// let mut seed = [0u8; 32];
// getrandom::getrandom(&mut seed).unwrap();
}
// The entropy gathering process:
fn entropy_gathering_process() {
// Step 1: OS maintains entropy pool
// - Hardware interrupts (keyboard, mouse, network)
// - Disk I/O timing
// - CPU timing jitter
// - Hardware RNG (RDRAND, /dev/hwrng)
// - Boot time measurements
// Step 2: getrandom() syscall reads from entropy pool
// - Cryptographically secure
// - Non-blocking (for urandom)
// - Returns requested bytes
// Step 3: rand::fill wraps getrandom
// - Handles errors appropriately
// - Respects platform-specific behavior
// Step 4: Seed initializes RNG state
// - ChaCha takes 256-bit key
// - State includes counter and nonce
// - Ready to generate random values
}System entropy comes from OS-provided CSPRNGs that collect hardware and software randomness sources.
Reproducibility vs Unpredictability
use rand::prelude::*;
use rand::rngs::StdRng;
use rand::SeedableRng;
fn reproducibility_comparison() {
// === from_entropy: UNPREDICTABLE ===
// Each call produces different results:
let rng1 = StdRng::from_entropy();
let rng2 = StdRng::from_entropy();
// rng1 and rng2 have completely different internal states
// because entropy is different each time
// Use when:
// - Security requirements (keys, tokens, nonces)
// - Unique identifiers
// - Shuffling with unpredictable results
// - Testing with varied randomness
// === from_seed: REPRODUCIBLE ===
// Same seed always produces same sequence:
let seed = [42u8; 32]; // Fixed seed
let rng1 = StdRng::from_seed(seed);
let rng2 = StdRng::from_seed(seed);
// rng1 and rng2 will produce IDENTICAL sequences
// because they start from same state
// Use when:
// - Reproducible tests
// - Deterministic simulations
// - Debugging with controlled randomness
// - Procedural generation with seeds
// Demonstration:
let seed = [1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32];
let mut rng_a = StdRng::from_seed(seed);
let mut rng_b = StdRng::from_seed(seed);
// Same sequence guaranteed:
assert_eq!(rng_a.gen::<u64>(), rng_b.gen::<u64>());
assert_eq!(rng_a.gen::<u64>(), rng_b.gen::<u64>());
assert_eq!(rng_a.gen::<u64>(), rng_b.gen::<u64>());
// With from_entropy, no such guarantee:
let mut rng_c = StdRng::from_entropy();
let mut rng_d = StdRng::from_entropy();
// These may produce same values, but extremely unlikely
// (would require identical entropy bytes)
}from_entropy provides unpredictability; from_seed provides reproducibilityβeach serves different use cases.
The Entropy Seed Generation Process
use rand::rngs::StdRng;
use rand::SeedableRng;
// Detailed breakdown of what happens:
fn entropy_seed_process_detailed() {
// 1. Default seed creation
let mut seed = <StdRng as SeedableRng>::Seed::default();
// For StdRng (ChaCha):
// seed: [u8; 32] = [0, 0, 0, ..., 0]
// 2. Entropy fill (simplified)
// This is what rand::fill does internally:
// On Linux:
// syscall(SYS_getrandom, seed.as_mut_ptr(), seed.len(), 0)
// Reads from kernel's CSPRNG (based on ChaCha20)
// On macOS:
// Reads from /dev/urandom
// Uses SecRandomCopyBytes internally
// On Windows:
// Calls RtlGenRandom (BCryptGenRandom)
// Uses Windows CSPRNG
// 3. Error handling
// Very rare for entropy fill to fail:
// - Would indicate serious OS issue
// - No entropy available (embedded, VM issues)
// - Permission denied (shouldn't happen)
// 4. State initialization
let rng = StdRng::from_seed(seed);
// ChaCha state:
// - Key: 256 bits from seed
// - Counter: 0 (incremented each block)
// - Nonce: derived from seed or fixed
}
// The seed bytes become ChaCha's internal key:
fn seed_to_chacha_key() {
// StdRng uses ChaCha12 (12 rounds) as its core:
// ChaCha state initialization:
// - "expand 32-byte k" constant
// - 256-bit key (32 bytes from seed)
// - 64-bit counter (starts at 0)
// - 64-bit nonce (from seed bytes or fixed)
// The 32-byte seed IS the key:
// seed[0..32] -> ChaCha key
// ChaCha generates output blocks:
// block[0..63] = ChaChaBlock(key, counter=0)
// block[64..127] = ChaChaBlock(key, counter=1)
// ...
// Each gen::<T>() call advances through blocks
}The seed bytes directly initialize ChaCha's 256-bit key, making the seed the foundation of the RNG's entire state.
Seeding Strategies and Their Trade-offs
use rand::prelude::*;
use rand::rngs::StdRng;
use rand::SeedableRng;
fn seeding_strategies() {
// === Strategy 1: from_entropy (unpredictable) ===
let rng = StdRng::from_entropy();
// Pros:
// - Cryptographically unpredictable starting state
// - No seed management needed
// - Different results each run
// - Simple API
// Cons:
// - Not reproducible
// - Requires OS entropy source
// - May fail on constrained systems
// - Not suitable for testing
// Use when:
// - Generating security-sensitive values
// - Unique identifiers/tokens
// - Cryptographic keys
// - Shuffling user-visible data
// === Strategy 2: from_seed (reproducible) ===
let seed = [0u8; 32]; // Fixed seed
let rng = StdRng::from_seed(seed);
// Pros:
// - Fully reproducible
// - Works without OS entropy
// - Deterministic for testing
// - Seed can be saved/shared
// Cons:
// - Predictable if seed known
// - Requires seed management
// - Same seed = same sequence
// - Not suitable for security
// Use when:
// - Reproducible tests
// - Deterministic simulations
// - Procedural generation
// - Debugging randomness issues
// === Strategy 3: seed_from_u64 (compact seed) ===
let rng = StdRng::seed_from_u64(12345);
// Pros:
// - Easy to specify (single number)
// - Reproducible
// - Good for simple seeding
// Cons:
// - Less entropy than 32-byte seed
// - Hash-based expansion needed
// - Not cryptographically seeded
// Use when:
// - CLI tools with numeric seed argument
// - Quick reproducibility
// - Configuration files with seeds
// === Strategy 4: Hybrid (entropy + seed) ===
// Combine entropy with application seed:
let mut seed = <StdRng as SeedableRng>::Seed::default();
rand::fill(&mut seed).unwrap();
// XOR with application identifier for domain separation:
let app_id = [1u8; 32]; // Application-specific
for (s, a) in seed.iter_mut().zip(app_id.iter()) {
*s ^= a;
}
let rng = StdRng::from_seed(seed);
// Use when:
// - Different RNG streams in same app
// - Domain separation between components
// - Preventing seed reuse across contexts
}Choosing between seeding strategies depends on whether unpredictability or reproducibility is required.
Practical Patterns
use rand::prelude::*;
use rand::rngs::StdRng;
use rand::SeedableRng;
// Pattern 1: Test with reproducible seed
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_random_sorting() {
// Fixed seed for reproducible test
let mut rng = StdRng::seed_from_u64(42);
let mut values = vec![1, 2, 3, 4, 5];
values.shuffle(&mut rng);
// Deterministic result
assert_eq!(values, vec![3, 1, 5, 2, 4]); // Always same
}
}
// Pattern 2: Production with entropy
fn generate_session_token() -> String {
let mut rng = StdRng::from_entropy();
// Unpredictable token for security
let mut bytes = [0u8; 32];
rng.fill_bytes(&mut bytes);
hex::encode(bytes)
}
// Pattern 3: Configurable seed for simulation
struct SimulationConfig {
// None = from_entropy (different each run)
// Some(seed) = reproducible runs
seed: Option<[u8; 32]>,
}
fn run_simulation(config: SimulationConfig) {
let mut rng = match config.seed {
Some(seed) => StdRng::from_seed(seed),
None => StdRng::from_entropy(),
};
// Simulation uses rng for all randomness
// Deterministic if seed provided, unpredictable otherwise
}
// Pattern 4: Seed logging for debugging
struct DebugRng {
rng: StdRng,
seed: [u8; 32],
}
impl DebugRng {
fn from_entropy() -> Self {
let mut seed = [0u8; 32];
rand::fill(&mut seed).expect("entropy failed");
let rng = StdRng::from_seed(seed);
Self { rng, seed }
}
fn from_seed(seed: [u8; 32]) -> Self {
let rng = StdRng::from_seed(seed);
Self { rng, seed }
}
fn gen<T: rand::distributions::Distribution<StdRng>>(&mut self) -> T
where StdRng: rand::Rng
{
self.rng.gen()
}
fn log_seed(&self) {
eprintln!("RNG seed: {}", hex::encode(self.seed));
}
fn get_seed(&self) -> [u8; 32] {
self.seed
}
}
// Pattern 5: Deterministic parallel RNGs
fn parallel_simulations(base_seed: [u8; 32], count: usize) -> Vec<StdRng> {
(0..count)
.map(|i| {
// Derive unique seed for each worker
let mut seed = base_seed;
// XOR worker index into seed
let idx_bytes = i.to_le_bytes();
for (j, b) in idx_bytes.iter().enumerate() {
seed[j % 32] ^= b;
}
StdRng::from_seed(seed)
})
.collect()
}Common patterns distinguish between entropy-based initialization for production and seeded initialization for testing/debugging.
Entropy Sources by Platform
use rand::rngs::StdRng;
use rand::SeedableRng;
fn platform_entropy_sources() {
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// β PLATFORM ENTROPY SOURCES β
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
// β Platform β Source β Characteristics β
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
// β Linux β getrandom() syscall β Non-blocking, crypto-secure β
// β β /dev/urandom fallback β Pulls from kernel CSPRNG β
// β β β ChaCha20 based (modern) β
// β β β β
// β macOS β getentropy() syscall β Non-blocking, crypto-secure β
// β β SecRandomCopyBytes β Uses Yarrow/Fortuna CSPRNG β
// β β /dev/urandom fallback β β
// β β β β
// β Windows β BCryptGenRandom β CryptoAPI, non-blocking β
// β β RtlGenRandom (legacy) β FIPS compliant β
// β β β β
// β FreeBSD β getrandom() syscall β Non-blocking, crypto-secure β
// β β /dev/urandom β Uses Yarrow CSPRNG β
// β β β β
// β OpenBSD β getentropy() β Uses arc4random β
// β β β ChaCha20 based β
// β β β β
// β WebAssembly β Web Crypto API β window.crypto.getRandomValuesβ
// β (browser) β β Crypto-secure β
// β β β β
// β WebAssembly β crypto.randomBytes β Node.js crypto module β
// β (Node.js) β β β
// β β β β
// β Embedded β Hardware RNG β Requires feature flag β
// β (with features) β Custom implementation β Platform-dependent β
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// The getrandom crate (used by rand) handles all platform differences:
// Cross-platform behavior:
// 1. Try syscall first (fastest, most direct)
// 2. Fall back to file-based sources (/dev/urandom)
// 3. Fail if no entropy source available
// Error conditions:
// - Embedded without configured source
// - OS entropy pool exhausted (extremely rare)
// - Syscall blocked by seccomp/sandbox
// - Virtual machine without entropy source
}
// Feature flags for custom entropy:
#[cfg(feature = "custom_entropy")]
fn custom_entropy_setup() {
// rand allows custom entropy sources via features:
// [dependencies]
// rand = { version = "0.8", features = ["custom"] }
// Then implement getrandom::register_custom_getrandom!:
// getrandom::register_custom_getrandom!(my_entropy_source);
//
// fn my_entropy_source(dest: &mut [u8]) -> Result<(), getrandom::Error> {
// // Read from hardware RNG
// // Or custom entropy pool
// Ok(())
// }
}The getrandom crate abstracts platform differences, providing a consistent entropy API across operating systems.
Security Considerations
use rand::rngs::StdRng;
use rand::SeedableRng;
use rand::prelude::*;
fn security_considerations() {
// === When from_entropy IS appropriate for security ===
// Cryptographic key generation:
let mut rng = StdRng::from_entropy();
let mut key = [0u8; 32];
rng.fill_bytes(&mut key);
// key is unpredictable, suitable for encryption
// Session tokens:
let token: [u8; 16] = rng.gen();
// Unpredictable, can't be guessed
// Nonces for encryption:
let nonce: [u8; 12] = rng.gen();
// ChaCha20 requires unique, unpredictable nonces
// === When from_entropy is NOT enough ===
// Long-term secrets need more consideration:
// - Consider OS key derivation (KDF)
// - Use dedicated crypto libraries
// - Key management beyond seeding
// Never use from_seed for security:
let seed = [0u8; 32]; // NEVER DO THIS FOR SECURITY
let rng = StdRng::from_seed(seed);
// This is completely predictable!
// Weak seeds:
let rng = StdRng::seed_from_u64(42); // WEAK for security
// Only 64 bits of entropy, easily brute-forced
// Time-based seeds:
let time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
let rng = StdRng::seed_from_u64(time); // WEAK for security
// Predictable if attacker knows approximate time
// === Entropy quality ===
// from_entropy provides:
// - Full 256 bits of entropy (for ChaCha seed)
// - Cryptographically secure source
// - Unpredictable output
// StdRng itself provides:
// - ChaCha12 PRNG (cryptographically secure)
// - Passes statistical tests
// - Suitable for security-sensitive applications
// But for critical security:
// - Use ring or RustCrypto libraries
// - They provide audited implementations
// - Follow best practices for key management
}
fn key_generation_best_practices() {
// Correct: Use entropy directly
let mut key = [0u8; 32];
rand::fill(&mut key).expect("entropy");
// Or via StdRng:
let mut rng = StdRng::from_entropy();
rng.fill_bytes(&mut key);
// Correct: Use dedicated crypto library
// use ring::rand;
// let rng = rand::SystemRandom::new();
// let mut key = [0u8; 32];
// rng.fill(&mut key).unwrap();
// Incorrect: Use fixed seed
// let rng = StdRng::from_seed([0u8; 32]); // NEVER for security!
// Incorrect: Use predictable seed
// let rng = StdRng::seed_from_u64(42); // NEVER for security!
}For cryptographic use, from_entropy provides unpredictability, but critical applications should use audited cryptographic libraries.
Reproducibility Patterns
use rand::rngs::StdRng;
use rand::SeedableRng;
use rand::prelude::*;
fn reproducibility_patterns() {
// === Saving seed for later reproduction ===
struct ReproducibleRng {
rng: StdRng,
initial_seed: [u8; 32],
}
impl ReproducibleRng {
fn new_from_entropy() -> Self {
let mut seed = [0u8; 32];
rand::fill(&mut seed).expect("entropy");
Self {
rng: StdRng::from_seed(seed),
initial_seed: seed,
}
}
fn new_from_seed(seed: [u8; 32]) -> Self {
Self {
rng: StdRng::from_seed(seed),
initial_seed: seed,
}
}
fn gen_u64(&mut self) -> u64 {
self.rng.gen()
}
fn get_seed(&self) -> [u8; 32] {
self.initial_seed
}
fn reset(&mut self) {
self.rng = StdRng::from_seed(self.initial_seed);
}
fn replay_from_start(&self) -> StdRng {
StdRng::from_seed(self.initial_seed)
}
}
// === Deterministic simulation with logging ===
struct Simulation {
rng: StdRng,
seed: [u8; 32],
steps: Vec<String>,
}
impl Simulation {
fn new(seed: Option<[u8; 32]>) -> Self {
let seed = seed.unwrap_or_else(|| {
let mut s = [0u8; 32];
rand::fill(&mut s).expect("entropy");
s
});
Self {
rng: StdRng::from_seed(seed),
seed,
steps: Vec::new(),
}
}
fn step(&mut self) -> u64 {
let value = self.rng.gen();
self.steps.push(format!("Generated: {}", value));
value
}
fn get_seed(&self) -> [u8; 32] {
self.seed
}
fn replay(&self) -> Vec<u64> {
// Replay from saved seed
let mut rng = StdRng::from_seed(self.seed);
self.steps.iter().map(|_| rng.gen()).collect()
}
}
// === Seed for debugging ===
fn production_mode() -> StdRng {
StdRng::from_entropy()
}
fn debug_mode(seed: [u8; 32]) -> StdRng {
eprintln!("Debug mode with seed: {}", hex::encode(seed));
StdRng::from_seed(seed)
}
fn run_with_config(debug: bool) {
let rng = if debug {
debug_mode([1u8; 32]) // Known seed for debugging
} else {
production_mode() // Unpredictable for production
};
// Use rng...
}
}For reproducibility, save the seed alongside results; for debugging, use fixed seeds to reproduce issues.
Thread Rng vs Std Rng
use rand::prelude::*;
use rand::rngs::{StdRng, ThreadRng};
use rand::SeedableRng;
fn thread_rng_vs_std_rng() {
// === ThreadRng (thread-local, entropy-based) ===
// Automatic, thread-local RNG:
let mut rng = thread_rng();
// ThreadRng characteristics:
// - Seeded from entropy automatically
// - Thread-local (each thread has its own)
// - Resizes/rescued from entropy periodically
// - Cannot be seeded (use StdRng for reproducibility)
// - Convenience for one-off random values
// Use for:
// - Simple random value generation
// - No reproducibility requirement
// - Quick prototyping
// - One-time operations
// === StdRng (explicit, seedable) ===
// Explicit creation and control:
let mut rng = StdRng::from_entropy(); // Or from_seed(...)
// StdRng characteristics:
// - Explicit seeding
// - Can be seeded for reproducibility
// - Instance can be passed around
// - Full control over lifecycle
// Use for:
// - Reproducible tests
// - Security-sensitive operations
// - Multiple independent streams
// - Controlled randomness
// === Choosing between them ===
// Use ThreadRng when:
// - You need random values
// - Reproducibility doesn't matter
// - Simplicity preferred
// Use StdRng when:
// - You need reproducibility
// - You need to control seeding
// - You need multiple independent RNGs
// - You're doing cryptographic operations
// Converting between them:
let mut std_rng = StdRng::from_entropy();
let thread_rng = thread_rng();
// Can't extract seed from ThreadRng
// (it's intentionally hidden for security)
// Can use StdRng as RngCore:
fn use_rng<R: RngCore>(rng: &mut R) -> u64 {
rng.gen()
}
use_rng(&mut std_rng);
use_rng(&mut thread_rng());
}
// Multiple independent RNG streams:
fn independent_streams() {
// Different seeds for different purposes:
let mut key_rng = StdRng::from_entropy();
let mut shuffle_rng = StdRng::from_entropy();
let mut simulation_rng = StdRng::from_entropy();
// Or derive from single entropy:
let mut master_seed = [0u8; 32];
rand::fill(&mut master_seed).expect("entropy");
// Derive streams:
let mut derive = |purpose: u8| {
let mut seed = master_seed;
seed[0] ^= purpose; // Domain separation
StdRng::from_seed(seed)
};
let key_rng = derive(1);
let shuffle_rng = derive(2);
let simulation_rng = derive(3);
// Now each RNG produces independent sequences
}ThreadRng provides automatic entropy seeding and thread-local storage, while StdRng offers explicit control and seedability.
StdRng Internal Algorithm
use rand::rngs::StdRng;
use rand::SeedableRng;
fn stdrng_algorithm() {
// StdRng uses ChaCha12 (12 rounds of ChaCha)
// Some versions use ChaCha20 (20 rounds)
// ChaCha is a stream cipher / PRNG:
// - Takes 256-bit key (our seed)
// - Uses 64-bit counter and 64-bit nonce
// - Generates 512-bit blocks per iteration
// - Each block becomes random output
// Internal state (512 bits total):
// βββββββββββββββββββββββββββββββββββββββββββββββ
// β ChaCha State (512 bits = 16 x 32-bit words)β
// βββββββββββββββββββββββββββββββββββββββββββββββ€
// β Constants: "expand 32-byte k" (4 words) β
// β Key: 256 bits from seed (8 words) β
// β Counter: 64 bits (2 words) β
// β Nonce: 64 bits (2 words) β
// βββββββββββββββββββββββββββββββββββββββββββββββ
// Output generation:
// 1. Initialize state with seed as key
// 2. Set counter = 0
// 3. Run ChaCha rounds (12 or 20)
// 4. Output state as 64 bytes
// 5. Increment counter
// 6. Repeat from step 3
// When you call gen::<T>():
// - Takes bytes from current block
// - If block exhausted, generates new block
// - Counter increments automatically
// Security properties:
// - ChaCha is cryptographically secure PRNG
// - Resistant to state recovery attacks
// - Long period (2^64 blocks before repeat)
// - Passes all statistical randomness tests
// ChaCha12 vs ChaCha20:
// - ChaCha12: 12 rounds, faster, still secure
// - ChaCha20: 20 rounds, more conservative
// - Both are cryptographically secure
// Seed handling:
// - Full 256 bits used as key
// - No entropy wasted
// - Seed directly becomes internal state
}StdRng uses ChaCha as its core algorithm, providing cryptographically secure pseudorandom generation from the seed.
Summary
fn summary() {
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// β FROM_ENTROPY AND SEEDING SUMMARY β
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
//
// === FROM_ENTROPY MECHANISM ===
//
// StdRng::from_entropy() does:
// 1. Create default zeroed seed (32 bytes for ChaCha)
// 2. Call rand::fill() to get OS entropy
// 3. Pass seed to from_seed()
// 4. Return initialized StdRng
//
// The entropy comes from:
// - Linux: getrandom() syscall or /dev/urandom
// - macOS: getentropy() or SecRandomCopyBytes
// - Windows: BCryptGenRandom or RtlGenRandom
// - Others: Platform-specific CSPRNGs
//
// === REPRODUCIBILITY VS UNPREDICTABILITY ===
//
// from_entropy():
// - Unpredictable starting state
// - Different every run
// - Suitable for security
// - Not reproducible
//
// from_seed(seed):
// - Deterministic starting state
// - Same sequence every time
// - Reproducible
// - NOT suitable for security
//
// seed_from_u64(n):
// - Compact seed (single u64)
// - Deterministic but limited entropy
// - NOT suitable for security
// - Good for CLI tools
//
// === USE CASES ===
//
// Use from_entropy when:
// - Generating security-sensitive values
// - Creating session tokens, nonces
// - Cryptographic key material
// - Production random values
// - No reproducibility needed
//
// Use from_seed when:
// - Reproducible tests
// - Deterministic simulations
// - Procedural generation with seeds
// - Debugging random behavior
// - Saving/replaying random sequences
//
// Use seed_from_u64 when:
// - Simple numeric seed needed
// - CLI with --seed argument
// - Quick prototyping
//
// === SECURITY NOTES ===
//
// StdRng (ChaCha) is cryptographically secure
// from_entropy provides unpredictable seeds
// Never use fixed seeds for security
// Never use seed_from_u64 for security
// For critical crypto, use dedicated libraries
//
// === PLATFORM BEHAVIOR ===
//
// All platforms use OS CSPRNG
// getrandom crate handles differences
// Fallback mechanisms available
// Custom entropy sources possible
//
// === KEY INSIGHT ===
//
// from_entropy is NOT about reproducibilityβ
// it's about UNPREDICTABILITY from OS entropy.
//
// Reproducibility comes from from_seed with
// explicit, known seed values.
//
// The seed IS reproducibility:
// - Save seed = save reproducibility
// - Share seed = share sequence
// - Log seed = enable debugging
// - from_entropy = no seed = no reproducibility
}Key insight: StdRng::from_entropy provides unpredictability by seeding from OS entropy, while reproducibility requires from_seed with explicit seed valuesβthe method bridges deterministic PRNG algorithms (ChaCha) and non-deterministic real-world entropy, with the seed being the sole determinant of whether results can be reproduced (explicit seed = yes, entropy = no). The entropy gathering is delegated to getrandom, which abstracts platform differences (syscalls on Unix, CryptoAPI on Windows) to provide consistent seeding from cryptographically secure OS sources.
