Seeding RNGs

As we have seen, the output of pseudo-random number generators (PRNGs) is determined by their initial state.

Some PRNG definitions specify how the initial state should be generated from a key, usually specified as a byte-sequence for cryptographic generators or, for small PRNGs, often just a word. We formalise this for all our generators with the SeedableRng trait.

The Seed type

We require all seedable RNGs to define a Seed type satisfying AsMut<[u8]> + Default + Sized (usually [u8; N] for a fixed N). We recommend using [u8; 12] or larger for non-cryptographic PRNGs and [u8; 32] for cryptographic PRNGs.

PRNGs may be seeded directly from such a value with SeedableRng::from_seed.

Seeding from ...

Fresh entropy

Using a fresh seed (direct from the OS) is easy using SeedableRng::from_entropy:


#![allow(unused)]
fn main() {
use rand::prelude::*;
use rand_chacha::ChaCha20Rng;

let rng = ChaCha20Rng::from_entropy();
}

Note that this requires rand_core has the feature getrandom enabled.

Another RNG

Quite obviously, another RNG may be used to fill a seed. We provide a convenience method for this:


#![allow(unused)]
fn main() {
use rand::prelude::*;
use rand_pcg::Pcg64;

let rng = Pcg64::from_rng(thread_rng());
}

But, say you want to save a key and use it later. For that you need to be a little bit more explicit:


#![allow(unused)]
fn main() {
use rand::prelude::*;
use rand_chacha::ChaCha8Rng;

let mut seed: <ChaCha8Rng as SeedableRng>::Seed = Default::default();
thread_rng().fill(&mut seed);
let rng = ChaCha8Rng::from_seed(seed);
}

Obligatory warning: a few simple PRNGs, notably XorShiftRng, behave badly when seeded from the same type of generator (in this case, Xorshift generates a clone). For cryptographic PRNGs this is not a problem; for others it is recommended to seed from a different type of generator. ChaCha8Rng is an excellent choice for a deterministic master generator (but for cryptographic uses, prefer the 12-round variant or higher).

A simple number

For some applications, especially simulations, all you want are a sequence of distinct, fixed random number seeds, e.g. 1, 2, 3, etc.

SeedableRng::seed_from_u64 is designed exactly for this use-case. Internally, it uses a simple PRNG to fill the bits of the seed from the input number while providing good bit-avalance (so that two similar numbers such as 0 and 1 translate to very different seeds and independent RNG sequences).


#![allow(unused)]
fn main() {
use rand::prelude::*;
use rand_pcg::Pcg64;

let rng = Pcg64::seed_from_u64(2);
}

Note that a number with 64-bits or less cannot be secure, so this should not be used for applications such as cryptography or gambling games.

A string, or any hashable data

Say you let users enter a string to seed the random number generator. Ideally, all parts of the string should influence the generator and making only a small change to the string should result in a fully independent generator sequence.

This can be achieved via use of a hash function to compress all input data down to a hash result, then using that result to seed a generator. The rand_seeder crate is designed for just this purpose.


#![allow(unused)]
fn main() {
use rand::prelude::*;
use rand_seeder::{Seeder, SipHasher};
use rand_pcg::Pcg64;

// In one line:
let rng: Pcg64 = Seeder::from("stripy zebra").make_rng();

// If we want to be more explicit, first we create a SipRng:
let hasher = SipHasher::from("a sailboat");
let mut hasher_rng = hasher.into_rng();
// (Note: hasher_rng is a full RNG and can be used directly.)

// Now, we use hasher_rng to create a seed:
let mut seed: <Pcg64 as SeedableRng>::Seed = Default::default();
hasher_rng.fill(&mut seed);

// And create our RNG from that seed:
let rng = Pcg64::from_seed(seed);
}

Note that rand_seeder is not suitable for cryptographic usage. It is not a password hasher, for such applications a key-derivation function such as Argon2 must be used.