Skip to content
pwnsy
cryptographyintermediate#csprng#entropy#randomness#cryptography#key-generation

Secure Random Number Generation Explained

Why cryptography needs CSPRNGs, how they differ from ordinary random functions, what entropy is, and how weak randomness breaks otherwise strong crypto.

You can use a flawless cipher, a long key, and a well-reviewed library, and still lose everything, because the one number you needed to be secret was guessable. Randomness is the foundation almost every other piece of cryptography stands on, and it is also the piece developers reach for most casually. The gap between the random function that shuffles a playlist and the one that generates an encryption key is enormous, and confusing the two has broken real systems.

This guide explains what makes a random number generator safe for cryptography, what entropy actually is, and the specific ways weak randomness quietly destroys otherwise strong protection. The short version is that the generator you use for anything an attacker would want to guess has to meet a much higher bar than the one you use for a dice roll, and the two look similar enough in code that they get swapped by accident all the time.

Where cryptography spends its randomness

Cryptography consumes unpredictable values constantly. Key generation needs them, since a key an attacker can guess is no key at all. Nonces and initialization vectors need them, so the same plaintext does not produce the same ciphertext twice. Session tokens, password reset codes, and salts need them. Digital signature schemes need a fresh secret value per signature. In every one of these, the security argument assumes the value could not be predicted. Remove that assumption and the argument collapses.

Two very different kinds of random

The word random hides two goals that pull in opposite directions.

An ordinary pseudorandom number generator, the kind behind most standard library random calls, is built to be fast and to spread values evenly across a range. That is perfect for simulations, games, and sampling. It is designed with no thought for an adversary. Its internal state is usually small, and from a handful of outputs a knowledgeable attacker can often reconstruct that state and then predict every value it will ever produce. It was never trying to stop that.

A cryptographically secure pseudorandom number generator, a CSPRNG, adds a stronger promise. Given all the output so far, no efficient attacker can predict the next bit better than a coin flip, and given the current output, no efficient attacker can work backward to earlier output. That unpredictability is the entire reason it exists, and it is what ordinary generators lack.

PropertyOrdinary PRNGCSPRNG
Design goalSpeed and even distributionUnpredictability against an attacker
Internal state recoveryOften easy from a few outputsComputationally infeasible
Predict next outputYes, once state is knownNo
Safe for keys and noncesNoYes
Typical sourceLanguage default random functionOS generator or vetted crypto library
The default random function is not for keys

Functions named like a plain random, rand, or Math.random almost always use a fast, predictable algorithm such as a linear congruential generator or Mersenne Twister. They are fine for non-security uses and unsafe for anything an attacker would want to guess: keys, tokens, nonces, salts, reset codes. Reach for the operating system generator or a library explicitly documented as cryptographically secure instead.

Entropy is the raw material

A CSPRNG is deterministic. Feed it the same starting state and it produces the same stream. So the unpredictability has to come from somewhere real, and that somewhere is entropy: genuine physical disorder that an attacker cannot observe or reproduce.

Operating systems collect entropy from sources that are hard to predict: precise timing of hardware interrupts, mouse and keyboard timing, disk and network jitter, and dedicated hardware random generators built into modern CPUs. This raw entropy is gathered into a pool, and the CSPRNG uses it to seed and periodically reseed its internal state. Given a strong seed, the generator can stretch a modest amount of true entropy into a long stream of unpredictable output, because reconstructing the state without the seed is computationally out of reach.

The catch is the seed. A CSPRNG with a weak or guessable seed produces a weak, guessable stream no matter how good its algorithm is. Entropy quality at seed time is everything.

A useful way to picture it: the CSPRNG is a lock, and the seed is the combination. A strong lock with a combination the attacker can guess is no protection at all. This is why the question is rarely which algorithm to use, since the standard ones are all sound, and almost always whether the seed carried enough real, unobservable entropy. NIST SP 800-90A specifies the approved deterministic generators, but every one of them assumes it was seeded from a genuine entropy source, and the standard is explicit that the entropy input is the security-critical part.

How weak randomness breaks real crypto

The failures almost never come from the cipher. They come from the numbers around it.

The classic case is a system generating keys before it has collected enough entropy, typically right after boot on an embedded device or a freshly cloned virtual machine. With a thin entropy pool, different devices can end up seeding from nearly the same state and generating nearly the same keys. Studies of internet-wide key collections have found large numbers of devices sharing keys or prime factors for exactly this reason. The cipher was fine; the seed was empty.

A second, sharper failure is reuse of a per-use random value. Some signature schemes require a unique secret random value for every signature. Use the same value for two different signatures, and the private key can be recovered with straightforward algebra. This is not theoretical; it has led to real key recovery on production systems. The same danger applies to nonce reuse in encryption, which can expose plaintext relationships and, in some modes, the authentication key.

A third is predictable session tokens or reset codes. Generate them with an ordinary PRNG and an attacker who collects a few can predict the rest, hijacking sessions or seizing accounts. A password reset link built on a fast, seedable generator is a particularly common example: if the token that authorizes the reset can be predicted, the account is taken over without ever knowing the password.

What ties these cases together is that the visible symptom is always downstream of the randomness. The report says a key was recovered, a session was hijacked, or an account was seized, and only the root-cause analysis points back to a generator that was never fit for the job. That distance between cause and symptom is exactly why insecure randomness is easy to ship and hard to notice, and why OWASP treats it as its own vulnerability class rather than a footnote to cryptography.

Unique, every single time

Many randomness failures reduce to one rule: values that must be unique per use, such as nonces, initialization vectors, and per-signature secrets, must actually be unique. Reuse is often as damaging as using no randomness at all. When a scheme demands a fresh value each time, treat repeating it as an immediate key-loss event.

A worked example: from entropy to a key

Following a single key generation from raw disorder to finished output makes the moving parts concrete. Consider what happens when a well-built system generates a symmetric key.

  1. Entropy collection. Long before the key request arrives, the operating system has been gathering unpredictable events into a pool: the exact cycle counts of hardware interrupts, timing jitter between disk operations, samples from a hardware random source on the CPU. None of these is perfectly random on its own, so the system estimates how much genuine unpredictability each contributes and mixes them together.
  2. Seeding. When enough estimated entropy has accumulated, the pool is used to seed the deterministic generator. This is the security-critical moment. If the seed carries, say, only a handful of bits of real unpredictability, then no matter how large the key that comes out looks, an attacker only has to search that handful of bits to reproduce it.
  3. Generation. The seeded generator now produces output deterministically. From the seed it stretches out a long stream of bits that pass statistical randomness tests and, crucially, cannot be extended or rewound by anyone who does not hold the internal state. The key is drawn from this stream.
  4. Reseeding. A good generator periodically folds fresh entropy back into its state, so that even if an attacker somehow learned the state at one moment, later output moves out of their reach again.

The important lesson from the walkthrough is that the strength of the finished key is capped by step two, not by the algorithm in step three. A 256-bit key produced from a seed with 20 bits of real entropy has roughly 20 bits of security. The output looks 256 bits strong under every statistical test, and it is not. This is the single most common way strong-looking randomness turns out to be weak, and it is invisible to anyone inspecting only the output.

Detection signals

Weak randomness is hard to spot from the outside, which is part of why it ships. A few concrete signals can surface it before an attacker does.

  • Repeated values that must be unique. Scanning logs, tokens, or generated keys for exact duplicates is the bluntest and most effective check. Any collision among values that were supposed to be unique per use, such as session tokens or per-signature secrets, is a red flag that points straight at the generator.
  • Keys generated at first boot. Devices that create long-lived keys immediately on first startup, before the entropy pool is warm, are the classic weak-seed scenario. An inventory of when and where key material was generated helps flag the risky cases.
  • Cloned or imaged systems sharing keys. Virtual machines cloned from a snapshot, or embedded devices flashed from one image, can carry identical generator state. Comparing keys across a fleet for shared values or shared factors catches this.
  • Tokens with visible structure. Tokens that increment, share long common prefixes, or correlate with time often come from an ordinary predictable generator rather than a CSPRNG. Sampling a batch and looking for patterns is a quick smell test.
  • Use of a language default random in security code. At the source level, the strongest signal is a call to a plain random or Math.random anywhere near key, token, nonce, salt, or reset-code generation. Static analysis and code review catch this before it ships, which is the cheapest place to catch it.

The common thread is that the symptom shows up downstream, in the keys and tokens, while the cause sits in the generator. Detection works best when it looks at both ends: the source code that chooses a generator, and the output the generator produces.

Common misconceptions

Randomness carries a set of comfortable but wrong beliefs that lead directly to weak systems.

  • "The numbers look random, so they are secure." Statistical randomness and cryptographic unpredictability are different properties. An ordinary generator passes most statistical tests easily while remaining fully predictable to anyone who recovers its state. Looking random is necessary and nowhere near sufficient.
  • "A longer key fixes weak randomness." Key length sets an upper bound on security, and the seed sets the real one. A long key from a weak seed is only as strong as the seed. Adding bits to the output while the input stays thin buys nothing.
  • "I should build my own generator for extra safety." Hand-rolled generators are a reliable source of failure. The standard operating-system generators are vetted, maintained, and correct. Rolling your own almost always means reintroducing a bug that professionals already removed.
  • "/dev/urandom is insecure because it does not block." On modern systems, once the pool has been seeded at boot, the non-blocking interface produces cryptographically strong output indefinitely. The real concern is drawing from it before the pool is first seeded, which the newer getrandom interface handles by blocking only until that initial seeding is done.
  • "Reseeding is optional." For long-lived generators, periodic reseeding is what limits the damage if internal state ever leaks. Treating the initial seed as a one-time event leaves a generator whose entire future is exposed by a single state disclosure.

How it evolved

The understanding of what randomness cryptography needs sharpened over decades of failures. Early systems routinely seeded generators from the process ID, the wall-clock time, or similar low-entropy values that an attacker could narrow down to a small range, which made the resulting keys searchable. Each such failure pushed the field toward the same conclusion: the seed is the security boundary, and it has to come from genuine, unobservable entropy.

Operating systems responded by building dedicated entropy pools that continuously harvest hardware and system events, exposing them through interfaces such as the special random devices on Unix-like systems and the cryptographic providers on Windows. Modern CPUs added on-chip hardware random generators to feed those pools directly. The standards followed, with NIST specifying approved deterministic generators and being explicit that each one assumes a real entropy source behind it.

The most instructive lesson came from the early-boot problem. As embedded devices and virtual machines multiplied, researchers scanned large collections of internet-facing keys and found substantial numbers of devices sharing keys or key factors, traced back to generating keys before the entropy pool had filled. That work is why current interfaces default to waiting for the pool to be seeded rather than handing out weak early output, and why generating long-lived keys at first boot is now treated as a design smell.

Where the secure generator lives on each platform

Part of using randomness correctly is knowing which interface to call, because every mainstream platform ships a vetted generator and the safe move is to use it rather than anything in a general-purpose library.

PlatformPreferred interfaceNotes
Linuxgetrandom system call, or /dev/urandomgetrandom blocks only until the pool is first seeded, then returns strong output
WindowsBCryptGenRandomThe newer CNG interface; CryptGenRandom is the older equivalent
macOS and BSDgetentropy, or /dev/urandomDraws from the kernel entropy pool
Most languagesa secrets or crypto moduleLook for wording like cryptographically secure in the documentation, not a plain random

The pattern across all of them is the same. There is one interface documented as cryptographically secure, and the language default random function is never it. When you are unsure which call a library exposes, the documentation phrase to search for is cryptographically secure. If the docs do not say that, assume the function is the fast, predictable kind and unsafe for anything an attacker would want to guess.

Using randomness correctly

The safe path is short, and most of it is about not being clever.

  1. Call the operating system generator. Use getrandom or /dev/urandom on Linux, BCryptGenRandom or CryptGenRandom on Windows, or a language API explicitly documented as cryptographically secure, such as a secrets module or a crypto-grade byte source. Do not hand-roll a generator.
  2. Never use the default random function for security. If the value protects anything, the plain random function is the wrong tool.
  3. Seed from real entropy, and wait for it if needed. On systems that can start before the entropy pool is ready, use the blocking interface that waits until the pool is initialized rather than accepting weak early output.
  4. Guarantee uniqueness where the scheme demands it. For nonces, initialization vectors, and per-signature values, make certain each is fresh. Prefer schemes and libraries that manage this for you, since these values feed directly into ciphers like the ones in How AES Works and into Block vs Stream Ciphers.
  5. Separate randomness from key stretching. Random values seed the process; deriving keys from passwords or shared secrets is a different job handled by the functions in Key Derivation Functions Explained.

Weak randomness rarely announces itself. It looks like working code right up until someone predicts a token or recovers a key. When a randomness or key-generation flaw does surface in a library, knowing whether it is being exploited changes how fast you move. Our Exploit Intelligence dashboard tracks which cryptographic and library vulnerabilities are under active exploitation, so a randomness bug in your stack gets triaged on evidence.

Good randomness is invisible when it works and catastrophic when it does not. Use the operating system's generator, respect the uniqueness rules, and give the pool enough entropy before you draw from it, and the foundation under the rest of your cryptography stays solid.

Frequently asked questions

What is the actual difference between a PRNG and a CSPRNG? Both are deterministic algorithms that stretch a seed into a longer stream. A PRNG optimizes for speed and even statistical distribution and makes no attempt to resist an adversary, so its internal state can often be reconstructed from a few outputs. A CSPRNG adds the guarantee that no efficient attacker who has seen past output can predict the next bit better than chance, or work backward to earlier output. The difference is the adversary the design accounts for.

Is /dev/urandom safe to use for keys? On a modern system whose entropy pool has been seeded at boot, yes. The old advice to prefer the blocking device came from concern about drawing output before the pool was ready. Current interfaces such as getrandom solve that by blocking only until the initial seeding is complete, after which the non-blocking source is cryptographically strong for as much output as you need.

How much entropy do I actually need? You need enough real, unobservable entropy in the seed to exceed the strength you want from the output. A generator seeded with a few hundred bits of genuine entropy can produce effectively unlimited strong output, because reconstructing its state without the seed is computationally infeasible. The mistake is not asking for too little total output, it is seeding from too little real entropy.

Why is reusing a nonce or per-signature value so dangerous? Because the security proofs behind those schemes assume the value is fresh every time. Reusing a per-signature secret in some signature schemes lets an attacker solve for the private key with basic algebra. Reusing a nonce in some encryption modes can expose relationships between plaintexts and, in certain modes, leak the authentication key. In these cases repetition is often as damaging as using no randomness at all.

Can I seed a generator from the current time? No, not for anything security-sensitive. Wall-clock time has very little unpredictability, and an attacker can narrow it to a small window, then search it. Time, process IDs, and similar values were the source of many early key-generation failures. Seed from the operating system entropy pool instead.

Does hardware random on the CPU replace the operating system generator? It is best treated as one entropy source feeding the operating system pool, not as a direct replacement. Mixing a hardware source with other collected entropy means a flaw or backdoor in any single source does not compromise the whole pool. The recommended practice is to draw from the operating system generator, which blends the hardware source with everything else it gathers.

How do I test whether my randomness is secure? Statistical test suites can tell you output looks random, and they cannot tell you it is unpredictable, since a predictable generator passes them. Meaningful assurance comes from confirming you use a vetted CSPRNG, that it is seeded from a real entropy source after the pool is ready, and that values requiring uniqueness are genuinely unique. Auditing the source of randomness beats testing its output.

Sources & further reading

Sharetwitterlinkedin

Related guides