Definition
A CSPRNG (Cryptographically Secure Pseudorandom Number Generator) is an algorithm that produces sequences of numbers that are:
- Statistically random — no pattern is detectable in the output
- Unpredictable — given any subsequence of outputs, the next value cannot be predicted
- Backward-secure — compromising the current state does not allow reconstructing previous outputs
These properties make CSPRNGs suitable for generating cryptographic keys, tokens, and UUID v4/v7 values. A regular PRNG (like Mersenne Twister used by Math.random()) is fast and statistically uniform but is predictable — an adversary who observes output can compute the internal state and predict future values.
The formal definition and security requirements are covered in Wikipedia’s CSPRNG article and NIST SP 800-90A.
How UUIDs use a CSPRNG
RFC 9562 §6.9 requires that UUID v4 implementations use a CSPRNG:
“Implementations SHOULD use a cryptographically secure pseudorandom number generator (CSPRNG) to provide values that are both unique and hard to guess.”
- UUID v4: all 122 non-structural bits (128 bits minus 4 version bits and 2 variant bits) come from a CSPRNG
- UUID v7: 62 bits in the
rand_bfield come from a CSPRNG; the remaining bits are the timestamp and optional monotonic counter
This is why UUID collisions are effectively impossible: the probability of two independently generated UUID v4 values colliding is approximately 1 in 5.3 × 10^36. See Collision for the math.
Platform CSPRNG sources
| Platform | API | Underlying source |
|---|---|---|
| Browser | crypto.getRandomValues() | OS CSPRNG (/dev/urandom, BCryptGenRandom) |
| Node.js | crypto.randomBytes() | OpenSSL → OS CSPRNG |
| Python | os.urandom() | OS CSPRNG |
| Go | crypto/rand.Read() | OS CSPRNG |
| Rust | rand::rngs::OsRng | OS CSPRNG |
| Java | SecureRandom | OS CSPRNG or hardware RNG |
| .NET | System.Security.Cryptography.RandomNumberGenerator | OS CSPRNG |
All of these ultimately draw from the operating system’s CSPRNG, which is seeded from hardware entropy sources: disk timing jitter, CPU performance counters, thermal noise, or a dedicated hardware random number generator (HRNG) chip if available.
CSPRNG vs PRNG — the UUID implication
// ❌ Predictable — never use for UUIDs
const bad = Math.random().toString(16);
// ✅ Cryptographically secure — use this
const good = crypto.randomUUID(); // UUID v4, uses getRandomValues() internally
Using a non-cryptographic PRNG for UUID generation creates a security vulnerability: an attacker who observes a few UUIDs can reconstruct the PRNG state and enumerate or predict future UUIDs. This enables IDOR (Insecure Direct Object Reference) attacks on systems that use UUIDs as authorization tokens. The OWASP Insecure Randomness guide documents this attack class.
Seeding and initialization
A CSPRNG must be seeded with sufficient entropy before it can produce secure output. Operating systems accumulate entropy during boot from hardware events. If a system generates UUIDs before the CSPRNG is properly seeded (a risk on embedded systems or fresh VMs without hardware entropy), the output may be predictable.
Linux’s /dev/random blocks until sufficient entropy is available; /dev/urandom does not block but uses a CSPRNG internally that may produce lower-quality output at boot before the entropy pool is full. Modern Linux kernels (5.6+) treat /dev/urandom as equivalent to /dev/random after the CSPRNG is initialized.
Cloud VM instances benefit from virtio-rng or similar hypervisor-provided entropy injection to ensure the CSPRNG is seeded at VM startup.
UUID v7 and CSPRNG bits
UUID v7 uses fewer CSPRNG bits than v4 because 48 bits are consumed by the timestamp and up to 12 bits by the optional monotonic counter:
UUID v4: [122 random bits]
UUID v7: [48 timestamp bits] [12 counter/random bits] [62 random bits]
74 bits of randomness (12 + 62) is more than sufficient to prevent collisions — the birthday bound for 74 bits of randomness exceeds 10^11 UUIDs before the probability of any collision reaches 50%. For most applications, UUID v7’s reduced randomness is irrelevant.
Related terms
- Entropy — the randomness that feeds the CSPRNG
- Collision — why UUID collisions are effectively impossible
- UUID v4 — the version that uses 122 CSPRNG bits
- UUID v7 — uses 62–74 CSPRNG bits with a timestamp prefix
External references
- Wikipedia — CSPRNG — formal definition, security properties, and common constructions including CTR_DRBG and ChaCha20
- NIST SP 800-90A Rev 1 — the NIST standard specifying approved DRBG (deterministic random bit generator) algorithms
- MDN — Crypto.getRandomValues() — browser CSPRNG API used by crypto.randomUUID() and UUID libraries
- OWASP — Insecure Randomness — attack scenarios when a non-cryptographic PRNG is used for token or UUID generation
- RFC 9562 §6.9 — CSPRNG requirement for UUID generation — normative requirement that UUID implementations use a CSPRNG
Frequently asked questions
What is a CSPRNG and why does UUID need one?
A CSPRNG (Cryptographically Secure Pseudorandom Number Generator) produces bit sequences that are statistically indistinguishable from true randomness and computationally infeasible to predict or reverse. UUID v4 uses 122 bits from a CSPRNG; UUID v7 uses 62–74 bits. Without a CSPRNG, UUID values could be guessed or forged. See Wikipedia's CSPRNG article for the formal definition.
What CSPRNG does each platform use for UUID generation?
Browsers: crypto.getRandomValues() backed by the OS CSPRNG (usually /dev/urandom on Linux or BCryptGenRandom on Windows). Node.js: crypto.randomBytes() from the same OS source. Python: os.urandom(). Go: crypto/rand.Read(). All are backed by platform-level CSPRNGs seeded from hardware noise, interrupt timing, or a hardware random number generator (HRNG).
Is Math.random() a CSPRNG?
No. Math.random() in JavaScript is a pseudorandom number generator (PRNG) — not cryptographically secure. Its output can be predicted if an attacker observes enough values. Never use Math.random() to generate UUIDs, tokens, or any security-sensitive identifier. Use crypto.randomUUID() or a UUID library that uses crypto.getRandomValues().