Random Numbers

~elly / 2026-02-04

~elly/random-numbers

What Is This?

  • what is a "random number"
  • when you use them
  • how to make them
  • how to screw up

Opening Caveats

  • my opinions
  • math is elided
  • theory is glossed over
  • probably wrong stuff

"Random Number"?

  • "uniform random variable"
  • one of { min, ..., max } ("range")
  • equal probability of each
  • non-uniform random variable?

Terms

  • uniform: coin, die roll, ...
  • non-uniform: letters, heights, ...
  • entropy:
    • log2(# options)
    • entropy(random_u64()) = 64 bits
    • entropy(random_u64() % 8) = 3 bits

Computer Facts

  • computers are deterministic
  • meaning: state S0 → state S1
  • meaning: rand() in state S0 → state S1
  • meaning: rand() in state S0 → V1
  • aside: does a computer have 0 entropy?

"Pseudorandom"

  • random: impossible to predict (*)
  • pseudorandom: hard to predict?
    • ↪ how hard?
  • RNG "strength" / "goodness"
(*): even with all info

True Randomness

  • thermal noise
  • various electrical effects
  • chaotic systems
  • Quantum ™
  • open q: how random are these?
  • → hardware RNG

Hardware RNGs

  • hardware to sample one of those things
  • very slow
  • "bad" randomness
  • hard to assess
  • sometimes not available
  • ... or even present

Software RNGs (PRNGs)

  • stateful object with a next() method

Software RNGs (PRNGs)

  • stateful object with a next() method
  • function : S → (V, S)

Software RNGs (PRNGs)

  • stateful object with a next() method
  • function : S → (V, S)
  • S: "entropy pool"
    • initial S: "seed"
  • V: value
    • "takes some entropy" from S (?)

PRNG Properties

  • Ni uncorrelated to Ni+1
  • N0 ... Ni uncorrelated to Ni+1
  • retain these properties for "a while"
    • note: all PRNGs are eventually periodic
    • → reseeding
  • fast?

Terrible RNGs

  • const(S) → (N, S): N, N, N, ... (*)
  • add1(S) → (S, S + 1): 0, 1, 2, ...
  • direct(S) → (S0, S1...): S0, S1, ...
(*): prior art

Less Terrible RNGs

  • linear congruential generator (LCG)
  • linear feedback shift register (LFSR)
    • shift-register generators
  • counter-based generators
  • more complex things

LCGs

  • lcg(S) → (S, (aS + c) % m)
  • (a, c, m) tuple defines an LCG
  • with good a, c, period is m - 1
  • quality critically depends on (a, c, m)!
  • see: RANDU (Knuth Vol 2 § 3.3.4)

RANDU

  • a = 65539, c = 0, m = 231
  • RANDU(S) → (S, 65539 * S mod 231)
  • 9Sn - 6Sn + 1 + Sn + 2 ≡ 0 mod 231 (!)
  • these form neat "planes" in 3d space
  • oops... for science!

LFSR

  • S : a bit vector { S0, S1, ..., Sn }
  • bit(S) → Sa ⊕ Sb ⊕ ... ("taps")
  • update(S, v) → { v, S0, ..., Sn - 1 }
  • lfsr(S) → (bit(S), update(S, bit(S)))
  • { a, b, ... } defines the LFSR

Counter-based generator

  • S : (seed, counter)
  • counter((s, c)) → (f(s, c), (s, c + 1))
  • what's f?
  • example: "Squares"
  • x = y = s * c, z = y + s
  • x = x2 + y; x = x2 + z; x = x2 + y

More complex stuff

  • [decades of research elided]
  • Mersenne Twister
    • kind of a feedback shift register
    • very fast
    • very long period
    • quite high quality

"Quality"?

  • warning: math
  • spectral test (for LCGs)
  • statistics to the rescue
  • Knuth Vol 2 § 3.3
  • → diehard & dieharder
  • "most runs mostly pass"

RNG security

  • recover MT state with a few hundred samples
  • → reproduce its output
  • same for many PRNGs
  • dangerous property!
  • idea: "cryptographically secure" PRNG

CSPRNG

  • csprng(Si) → (Vi, Si+1)
  • given V0, V1, ..., Vn, can't predict Vn + 1
  • → Vi doesn't "leak" state
  • given Si, can't recover Vi-k
  • → state update is one-way

General CSPRNGs

  • S: (key K, counter C)
  • mix(S, T): Si → S'0
  • update(S): Si → Si+1
  • extract(S): Si → Vi
  • update() and extract() are one-way

Aside: NIST SP 800-90A

  • us govt standard for "DRBGs"
  • a few subtle security issues...
    • eg CTR block recurrence
  • Dual_EC_DRBG scandal with NSA (!)
  • others still in wide use

The real world

Actual RNGs

  • the time
  • timings: keyboard, disk, network
  • how random is this stuff?
  • sidechannels?
  • magical amulets?? (special hardware)
  • state of the art for decades

RDRAND / RDSEED

  • RNG in the CPU hardware
  • CTR-DRBG seeded off "hardware"
    • thermal / RF noise
  • can you trust this?
  • CrossTalk, Zen5 zero bug, NSA?

Actual CSPRNGs

  • arc4random(): RC4 key stream
  • Fortuna: CTR-DRBG (ish) with AES
  • Windows BCryptGenRandom(): CTR-DRBG with AES
  • bssl RAND_bytes(): CTR-DRBG with AES
  • linux kernel: CTR-DRBG with ChaCha20

Best practices (kernel)

  • "entropy pool" (CSPRNG state)
  • mix "everything" into this
    • attacker can't control all of it
    • any unknown input adds entropy
  • generate out of it as needed

Aside: /dev/{,u}random

  • /dev/random: "actual entropy"
  • /dev/urandom: PRNG output
  • "move the mouse around" / "type"
  • random blocks, urandom doesn't
  • q: when to use random?

Aside: /dev/{,u}random

  • /dev/random: "actual entropy"
  • /dev/urandom: PRNG output
  • "move the mouse around" / "type"
  • random blocks, urandom doesn't
  • q: when to use random?
  • a: never

Best practices: user

  • seed PRNG off kernel, use that
  • CSPRNG ~always
    • unless you know perf is critical
  • just use base::RandBytes() :)

Aside: state cloning

  • problem: fork() copies PRNG state
    • detect fork() and reseed

Aside: state cloning

  • problem: fork() copies PRNG state
    • detect fork() and reseed
  • problem: VM snapshot copies PRNG state
    • reseed on every call?
    • ... if we can (RDRAND / etc)
    • ... or just give up

Oops #1: Debian OSSL

  • CVE-2008-0166
  • OpenSSL CSPRNG on Debian seeded with PID only
  • → 32,767 possible output streams
  • unnoticed for years
  • ~all crypto broken on such systems
  • CSPRNG only as good as seed

Oops #2: PS3 DSA

  • DSA uses a random nonce
  • non-random nonce → leak private key
  • Sony did this
  • → leaked root PS3 signing key!
  • random quantities must be random

Oops #3: RSA moduli

  • N = pq (p, q random primes)
  • N' = pq' or N' = p'q
  • gcd(N, N') = p or q
  • how??
    • bad or unseeded RNGs!
  • routinely breaks 100ks of keys

Oops #4: DUHK

  • ANSI X9.31 CSPRNG
  • X9.31 discloses full state if you have key...
  • various Fortinet devices hardcoded key
  • broke 100ks of keys

Oops #5: Android CSPRNG

  • Java SecureRandom default seed: 64 bits
  • disclosed in 2013
  • latent for... years?
  • BitCoin keys from Android broken

Oops #6: RC4 key

  • key schedule: key → longer key
  • ... basically a CSPRNG
  • output is biased
  • → Fluhrer-Mantin-Shamir
  • → break WEP for wifi
  • arc4random() drops stream prefix

Oops #7: Dual_EC_DRBG

  • from NIST SP 800-90A
  • slow, weird CSPRNG
  • NSA paid RSA Security to use it
  • ... then used that use to standardize it
  • widely considered likely backdoored
  • rejected by everyone, later de-standardized

Lessons

  • rng failure cannot be tested for
  • rng failure generally has no visible result
  • these bugs can have unfixable consequences
    • leaked user data
    • decrypt-later

Lessons 2

  • use a good rng
  • use big-enough random values
    • 128 or 256 bits
  • use constructs that don't need one
  • use bssl and //crypto
  • talk to security

Further reading

  • Knuth Volume 2 3.3 for Math
  • Cryptography Engineering Chapter 9 for Crypto
  • [add yours here]

Q&A