Entropy in information theory is the average uncertainty of a random outcome, measured in bits: Claude Shannon defined it in 1948 as H = -sum of p log2 p, so a fair coin carries 1 bit and a fair die about 2.585 bits. Cryptography uses a stricter measure, min-entropy, which counts only the probability of the single most likely value, because an attacker always tries the best guess first. Random number generators turn a little min-entropy from noisy hardware into large amounts of unpredictable output, and when that seeding fails, keys become guessable.
This post goes one level deeper than our famous equations overview and our laws of thermodynamics explainer. It works the numbers for coins, dice and English, explains NIST's min-entropy based standards, shows how Linux and Windows gather entropy, and walks through three documented failures. Safeguard builds software supply chain security tools, not mathematics or physics tools, and nothing here suggests otherwise.
Key takeaways
- Shannon entropy is an average. A fair coin is 1 bit, a fair die about 2.585 bits, and English text drops from about 4.7 bits per letter to roughly 1 bit once long range structure is counted, by Shannon's 1951 estimates.
- Min-entropy is a worst case. It is -log2 of the largest single probability, which is why NIST SP 800-90B uses it. A source can have healthy Shannon entropy and poor min-entropy at once.
- Thermodynamic and Shannon entropy share a formula, not a meaning. One has units of joules per kelvin and describes matter; the other is unitless and describes a probability model someone chose.
- NIST splits the problem in three. SP 800-90A (Rev. 1, June 2015) defines deterministic random bit generators, SP 800-90B (January 2018) defines entropy sources and their health tests, and SP 800-90C (final in September 2025) defines how to combine them into complete generators.
- Use the operating system's generator. getrandom() on Linux, BCryptGenRandom on Windows, or wrappers such as Python's secrets, Node's crypto.randomBytes and Go's crypto/rand.
- Failures come from seeding, design and process. The 2008 Debian OpenSSL bug left about 32,767 possible streams per architecture, Dual_EC_DRBG left NIST guidance in 2014, and 2012 research recovered RSA private keys for 0.50% of TLS hosts scanned.
- Size secrets in bits, not characters. OWASP asks for at least 64 bits of entropy in session IDs and recommends 128 bit CSPRNG values; NIST SP 800-63B asks for at least 64 bits for session secrets and recovery codes.
- Statistical tests cannot prove unpredictability. They catch broken noise sources; only design review and correct seeding catch predictable generators.
What is Shannon entropy?
In "A Mathematical Theory of Communication," published in two parts in the Bell System Technical Journal in July and October 1948, Claude Shannon asked how to measure the information produced by a source of messages. He showed that a measure satisfying a few reasonable conditions must have the form H = -K times the sum of p_i log p_i, where the constant K only sets the unit. With logarithms in base 2, the unit is the bit, and Shannon credits the word "bit" to J. W. Tukey.
Written out for a discrete random variable X with outcomes x_1 to x_n and probabilities p_1 to p_n:
H(X) = - sum over i of p_i * log2(p_i) (bits)
It is zero when the outcome is certain, largest (log2 n) when all n outcomes are equally likely, and it adds up for independent sources: a fair coin plus a fair die carry 1 + 2.585 = 3.585 bits.
The operational meaning is compression. Shannon's source coding results show that H is the minimum average number of binary digits per symbol that any lossless code can achieve for that source. That is why entropy is an average: it describes what happens over many symbols, not what happens on any single draw.
Worked example 1: a fair coin and a biased coin
A fair coin has two outcomes at probability 0.5 each.
H = -(0.5 * log2 0.5 + 0.5 * log2 0.5)
= -(0.5 * -1 + 0.5 * -1)
= 1 bit
Now bend the coin so heads comes up 90% of the time.
log2(0.9) = -0.152
log2(0.1) = -3.322
H = -(0.9 * -0.152 + 0.1 * -3.322)
= 0.137 + 0.332
= 0.469 bits
The biased coin produces less than half a bit per toss on average. Over 1,000 tosses you could, in principle, compress the record into about 469 bits instead of 1,000.
Worked example 2: a fair die and a loaded die
A fair six sided die has six outcomes at probability 1/6.
H = log2(6) = 2.585 bits
Load the die so a six appears half the time and each other face appears 10% of the time.
H = -(0.5 * log2 0.5 + 5 * 0.1 * log2 0.1)
= 0.5 * 1 + 5 * 0.1 * 3.322
= 0.5 + 1.661
= 2.161 bits
Loading the die cost about 0.42 bits per roll. Keep this die in mind; it comes back in the min-entropy section, where it looks much worse.
Worked example 3: English text
English is the example Shannon himself used. If you treat the 26 letters as equally likely, each letter carries log2(26), about 4.7 bits. Real English is not uniform: E is common, Q is rare, and Q is almost always followed by U. Shannon's 1951 paper "Prediction and Entropy of Printed English" walks down the ladder:
| Model of English | Entropy per letter | What it counts |
|---|---|---|
| 26 letters, all equally likely | about 4.7 bits | Alphabet size only |
| Single letter frequencies | 4.14 bits | E more common than Z |
| Letter pairs (digrams) | 3.56 bits | TH, HE, QU and so on |
| Structure up to about 8 letters | roughly 2.3 bits | Shannon's 1948 estimate; redundancy about 50% |
| Long range structure up to about 100 letters | on the order of 1 bit | Shannon's 1951 prediction experiments; redundancy roughly 75% |
In 1948 Shannon wrote that the redundancy of ordinary English, counting structure up to about eight letters, is roughly 50%, meaning about half of what we write is fixed by the structure of the language. The 1951 prediction experiments, where people guessed the next letter of a text, pushed the estimate to about one bit per letter for long range structure.
This matters for passwords: 12 letters of ordinary English prose carry nearer 12 bits than 12 x 4.7 = 56.
How does Shannon entropy relate to Boltzmann's thermodynamic entropy?
Shannon noticed the resemblance himself. In the 1948 paper he wrote that the form of H "will be recognized as that of entropy" in statistical mechanics, where p_i is the probability of a system being in cell i of its phase space, and pointed to Boltzmann's H theorem.
The physics version, Boltzmann's entropy formula, is:
S = k_B * ln(W)
where W is the number of microstates consistent with the observed macrostate and k_B is the Boltzmann constant. In the current SI, k_B is exact: NIST's 2022 CODATA listing gives 1.380 649 x 10^-23 joules per kelvin, with no uncertainty. When the W microstates are equally likely, each has probability 1/W, and the Gibbs form S = -k_B times the sum of p_i ln p_i reduces to Boltzmann's. Set k_B = 1 and switch to base 2 logarithms, and you have Shannon's formula.
The conversion factor is simple: one bit of Shannon entropy corresponds to k_B ln 2 of thermodynamic entropy.
k_B * ln 2 = 1.380649e-23 J/K * 0.693147
= 9.57e-24 J/K per bit
Where the analogy breaks
The shared formula hides real differences, and treating them as the same thing causes muddled security arguments.
| Question | Boltzmann and Gibbs entropy | Shannon entropy |
|---|---|---|
| Units | Joules per kelvin | Bits (or nats), unitless |
| What the probabilities describe | Physical microstates of matter | A model of a message source that you choose |
| Who decides the distribution | Physics, given the macrostate | The observer; different observers can assign different values |
| Does it tend to increase? | Yes, in an isolated system (second law) | No such law; a source has whatever distribution it has |
| Is it a property of one object? | Of a physical system in a macrostate | Of a probability distribution, never of a single string |
The last row trips up engineers. The string "4" has no entropy on its own; "4 from a fair die roll" has 2.585 bits. Cryptographic entropy describes the process that produced a value and what the attacker knows, which is why SP 800-90B defines entropy "relative to one's knowledge" of an output before it is observed.
A second break: the thermodynamic entropy in a noisy resistor is enormous, but a hardware noise source may still hand software a fraction of a bit per sample, because digitizing, sampling and correlations discard most of it. Entropy sources are validated on what leaves the digitizer, not on the physics underneath.
Why does cryptography use min-entropy rather than Shannon entropy?
Because attackers do not guess on average. They guess the most likely value first, then the second most likely, and so on. Shannon entropy averages over all outcomes, so a distribution with one very likely value and a long tail of rare values can show a comfortable Shannon number while the first guess succeeds alarmingly often.
SP 800-90B defines min-entropy for a discrete variable with probabilities p_1 to p_k as:
H_min = min over i of ( -log2 p_i ) = -log2( max p_i )
If X has min-entropy H, no single value occurs with probability greater than 2^-H. The maximum possible min-entropy for k values is log2 k, reached only by the uniform distribution. For uniform distributions, Shannon entropy and min-entropy are equal. For everything else, min-entropy is lower, and it is never higher.
Worked example 4: the same sources, measured by min-entropy
| Source | Shannon entropy | Most likely value | Min-entropy |
|---|---|---|---|
| Fair coin | 1 bit | 0.5 | 1 bit |
| Coin with heads at 0.9 | 0.469 bits | 0.9 | -log2 0.9 = 0.152 bits |
| Fair die | 2.585 bits | 1/6 | 2.585 bits |
| Die with six at 0.5 | 2.161 bits | 0.5 | -log2 0.5 = 1 bit |
The loaded die is the instructive case. By Shannon's measure it lost only 16% of its entropy. By min-entropy it lost 61%, because half the time a single guess of "six" wins.
Worked example 5: a key generator with a weak spot
Imagine a buggy key generator that, because of a race at boot, outputs one fixed 128 bit key 1% of the time and a uniformly random 128 bit key the other 99% of the time.
Shannon entropy = -(0.01 * log2 0.01) - sum over 2^128 keys of (0.99/2^128) * log2(0.99/2^128)
~= 0.066 + 0.99 * 128.01
~= 126.8 bits
Min-entropy = -log2(0.01)
~= 6.6 bits
A Shannon based review would call this a 126.8 bit generator and move on. An attacker who knows about the bug tries the fixed key first and is right one time in a hundred, across every device that shipped. Min-entropy reports 6.6 bits, which is the honest answer. This is not a contrived scenario: the 2012 weak key research described below found exactly this kind of shape, where many devices produced the same few keys.
Two related terms appear in standards. Collision entropy, -log2 of the sum of p_i squared, governs how often two draws collide, and SP 800-90B uses a collision estimator. Full entropy is NIST's term for a bitstring whose entropy essentially equals its length. Shannon entropy is always at least collision entropy, which is at least min-entropy, so when someone quotes an entropy figure, ask which one.
A timeline of entropy and randomness in security
Every date below is checked against the primary source listed at the end.
| Date | Event | Why it matters |
|---|---|---|
| July and October 1948 | Shannon publishes "A Mathematical Theory of Communication" in the Bell System Technical Journal | Defines H in bits and links it to statistical mechanics |
| 1951 | Shannon publishes "Prediction and Entropy of Printed English" | Estimates long range English at about 1 bit per letter |
| 13 May 2008 | Debian publishes DSA-1571 (CVE-2008-0166) | Predictable RNG; keys must be regenerated |
| April 2010 | NIST SP 800-22 Rev. 1a, statistical test suite for RNGs | Common statistical battery; NIST decided in April 2022 to revise it |
| August 2012 | "Mining Your Ps and Qs" at USENIX Security 2012 | Weak keys on embedded devices at Internet scale |
| September 2013 | News reports about Dual_EC_DRBG; NIST recommends against using it | Trust in a standardized DRBG collapses |
| 21 April 2014 | NIST removes Dual_EC_DRBG from the draft SP 800-90A Rev. 1 | Users told to move to the remaining three DRBGs |
| 24 June 2015 | SP 800-90A Rev. 1 final | Hash_DRBG, HMAC_DRBG and CTR_DRBG remain |
| January 2018 | SP 800-90B final | Entropy source design, min-entropy estimation and health tests |
| September 2025 | SP 800-90C final | RBG1, RBG2, RBG3 and RBGC constructions |
How do NIST SP 800-90A, 90B and 90C define random bit generators?
NIST splits a random bit generator (RBG) into two parts and a recipe. The entropy source supplies a modest amount of true unpredictability. The deterministic random bit generator (DRBG) stretches that seed into as many output bits as you need. The construction document says how to wire them together and when to reseed.
| Document | Status (checked October 2026) | What it specifies | The question it answers |
|---|---|---|---|
| SP 800-90A Rev. 1 | Final, June 2015; a Rev. 2 draft exists | DRBG mechanisms based on hash functions or block ciphers: Hash_DRBG, HMAC_DRBG, CTR_DRBG | Given a good seed, how do I generate lots of output safely? |
| SP 800-90B | Final, January 2018; NIST noted two errata in May 2025 | Entropy source design, min-entropy estimation, health tests, validation data | How much entropy does my noise source really deliver, and how do I notice when it breaks? |
| SP 800-90C | Final, September 2025 | Complete RBG constructions: RBG1, RBG2(P), RBG2(NP), RBG3(XOR), RBG3(RS), RBGC | How do I combine a DRBG and entropy sources into a generator with a stated security strength? |
SP 800-90A: deterministic random bit generators
A DRBG is an algorithm with internal state: you instantiate it with seed material, call generate, and periodically reseed. Its output is only as unpredictable as its seed. Backtracking resistance protects past output if the state later leaks, and prediction resistance, when requested, uses fresh entropy to restore security after a compromise.
Rev. 1 keeps three mechanism families. Hash_DRBG builds on an approved hash function, HMAC_DRBG on HMAC, and CTR_DRBG on a block cipher, in practice AES, running in counter mode. Microsoft documents that the Windows default provider implements the CTR_DRBG portion of SP 800-90, and Go documents that in FIPS 140-3 mode its crypto/rand output passes through an SP 800-90A Rev. 1 DRBG.
SP 800-90B: entropy sources, min-entropy and health tests
SP 800-90B is where min-entropy becomes operational. An entropy source has three parts: a noise source (the physical or non-physical process that produces unpredictable samples), an optional conditioning component that compresses and whitens samples, and health tests that run continuously.
Validation. A lab collects at least 1,000,000 sequential raw samples plus a dataset from 1,000 restarts, so the estimate covers power on as well as steady state. If samples are not independent and identically distributed (IID), the lab runs a battery of estimators and takes the minimum. SP 800-90B lists ten non-IID estimators: Most Common Value, Collision, Markov, Compression, t-Tuple, Longest Repeated Substring, Multi Most Common in Window prediction, Lag prediction, MultiMMC prediction and LZ78Y prediction. The Collision, Markov and Compression estimates apply only to binary data.
Health tests. SP 800-90B approves two continuous tests.
- The Repetition Count Test catches catastrophic failures, such as a noise source stuck on one value. Its cutoff is C = 1 + ceil(-log2(alpha) / H), where alpha is the acceptable false positive probability and H is the assessed min-entropy per sample. The standard's own worked number: with alpha = 2^-20 and H = 2.0 bits per sample, C = 1 + 20/2 = 11 identical samples in a row triggers a failure.
- The Adaptive Proportion Test catches subtler loss of entropy. It takes a sample and counts how often the same value appears in a window: W = 1024 samples for binary sources and 512 for non-binary sources. Too many repeats triggers a failure.
The standard is candid about limits. It notes that the Repetition Count Test "is not very powerful": a source assessed at 8 bits per sample that degraded to 4 bits per sample would take about a million samples before the test noticed. That candor is the right model for every RNG test discussed below.
SP 800-90C adds a distinction between physical noise sources, such as thermal noise, and non-physical ones, which it says "are typically influenced by human actions or network events" and treats as generally less reliable.
SP 800-90C: putting it together
SP 800-90C, finalized in September 2025, specifies four construction classes.
| Construction | Entropy source access | Reseeding | Full entropy output | Typical use |
|---|---|---|---|---|
| RBG1 | None after instantiation; seeded once over a physically secure channel from an RBG2(P), RBG3 or root RBGC | Not supported | No | Devices seeded at manufacture |
| RBG2(P) and RBG2(NP) | Physical (P) or non-physical (NP) entropy source | Optional; prediction resistance possible when reseeding | No | General purpose systems |
| RBG3(XOR) and RBG3(RS) | One or more physical entropy sources; XOR combines source output with DRBG output, RS reseeds continuously | Always | Yes | When output must have full entropy |
| RBGC | A tree of RBGs on one platform; the root draws from a randomness source, children seed from parents | From parent or approved siblings | No | Operating system style generator trees |
A compliant RBG supports 128, 192 or 256 bit security strengths. The RBGC tree closely matches how Windows already works, as shown below.
How do Linux and Windows gather and use entropy?
Linux: /dev/urandom, /dev/random and getrandom()
The random(7) man page summarizes the design: the kernel generator "relies on entropy gathered from device drivers and other sources of environmental noise" to seed a cryptographically secure pseudorandom number generator, and it is "designed for security, rather than speed."
There are three ways in.
- /dev/urandom never blocks. Early in boot, before the pool is initialized, it may return output from an uninitialized generator, which random(4) warns may be unsuitable for cryptography.
- /dev/random historically blocked whenever the kernel's entropy estimate ran low. Since Linux 5.6, random(4) says it "will no longer block except during early boot process."
- getrandom(), a Linux system call since 3.17 with a glibc wrapper since 2.25, reads from the urandom source by default but blocks until the pool is initialized. That makes it the right default: it never returns weak early boot output and never blocks once the system is up. The GRND_NONBLOCK flag returns EAGAIN instead of blocking, and getentropy(3) is a slightly more portable wrapper.
random(7) advises against /dev/random and the GRND_RANDOM flag for almost everything, including most long term key generation. It also says a key generator needs seed material equal to the key's effective strength: a 3072 bit RSA key, at about 128 bits of strength, needs only 16 bytes.
What changed inside the kernel. Jason A. Donenfeld, who maintains the Linux RNG, documented the 5.17 and 5.18 changes in March 2022. In 5.17, BLAKE2s replaced SHA-1 for extracting from the input pool, which he writes raised its forward security from 80 to 128 bits; extraction seeds a ChaCha based generator. Since 5.4, the kernel can seed itself from CPU cycle counter jitter within about a second. In 5.18, per the same write up, /dev/random and /dev/urandom became "exactly the same thing," with insecure output only through an explicit getrandom(GRND_INSECURE).
The /proc/sys/kernel/random/entropy_avail file reports the kernel's entropy estimate (random(4) documents a range up to 4096 bits). Treat it as a diagnostic, not a security gauge: once the pool is initialized, output is meant to be safe for all purposes.
Windows: BCryptGenRandom, ProcessPrng and the RNG tree
Microsoft Learn documents BCryptGenRandom as the CNG call that "generates a random number." Passing NULL for the algorithm handle with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag selects the system preferred generator, and the remarks state that the default provider complies with the CTR_DRBG portion of NIST SP 800-90. Before Windows Vista SP1, the default followed FIPS 186-2 instead. The BCRYPT_RNG_USE_ENTROPY_IN_BUFFER flag, which once mixed caller data in as extra entropy, is ignored in Windows 8 and later. A lower level call, ProcessPrng, retrieves bytes from the user mode per processor generator and is documented to always return TRUE; Go's crypto/rand uses it on Windows.
Niels Ferguson's October 2019 paper, "The Windows 10 random number generation infrastructure," describes the internals:
- One algorithm and a tree. Every PRNG is an SP 800-90 AES_CTR_DRBG with 256 bit security strength. A root PRNG in the kernel driver CNG.SYS seeds per processor kernel PRNGs, which seed per process, per processor PRNGs: the shape SP 800-90C later standardized as RBGC.
- A reseed schedule that slows down. The root reseeds 1 second after boot seeding, then 3, 9, 27 seconds and so on, tripling until the interval reaches 3,600 seconds.
- Multiple entropy pools. Each pool is a running SHA-512 computation; pool 0 feeds every reseed, pool 1 every third, pool 2 every ninth, so a slow trickle of entropy eventually adds up to an unpredictable reseed.
- Sources. Interrupt timings read through the CPU time stamp counter are the primary source. The loader takes 40 bytes from the TPM if present, RDRAND and RDSEED are used where available, and a 64 byte registry seed file is the main boot entropy source except on the first boot after installation.
Linux and Windows compared
| Aspect | Linux (5.18 and later) | Windows 10 and later |
|---|---|---|
| Recommended API | getrandom(), or /dev/urandom after boot | BCryptGenRandom with BCRYPT_USE_SYSTEM_PREFERRED_RNG; ProcessPrng |
| Output generator | ChaCha based crng | SP 800-90 AES_CTR_DRBG, 256 bit strength |
| Pool hashing | BLAKE2s (since 5.17) | SHA-512 per pool, multiple pools |
| Early boot behavior | getrandom() blocks until initialized; jitter seeding since 5.4 | Seed file, TPM bytes and CPU sources at boot |
| Blocking after boot | None (/dev/random no longer blocks since 5.6) | None |
| Primary documented noise | Interrupts, devices, jitter, CPU RNG instructions, seed file | Interrupt timings via TSC, TPM, RDRAND and RDSEED, seed file |
What your language calls
Most application code should never touch these system calls directly. Use the cryptographic module your language provides:
| Language | Use for secrets | Do not use for secrets |
|---|---|---|
| Python | secrets.token_bytes, token_hex, token_urlsafe; os.urandom | random module (Mersenne Twister) |
| Node.js | crypto.randomBytes, crypto.randomInt, crypto.randomUUID | Math.random |
| Go | crypto/rand (Read, Text, Int) | math/rand |
Python warns that the random module "should not be used for security purposes." Go documents that crypto/rand uses getrandom(2) on Linux, arc4random_buf(3) on macOS and iOS, and ProcessPrng on Windows.
What went wrong in documented RNG failures?
Three cases are worth knowing in detail because each teaches a different lesson. All are described from the defender's side: what failed, how it was fixed and what to change in your own process.
Debian OpenSSL, 2006 to 2008: a one line change that removed the entropy
Debian Security Advisory DSA-1571, published on 13 May 2008 and tracked as CVE-2008-0166, reported that Luciano Bello had discovered that "the random number generator in Debian's openssl package is predictable." The cause was an incorrect Debian specific change. The first vulnerable version, 0.9.8c-1, was uploaded to unstable on 17 September 2006 and reached the stable release, etch. The older sarge release was not affected.
Debian's wiki explains the mechanism: code was removed to silence a memory debugging tool's warnings about uninitialized memory, and one removed call was the one that fed entropy into the pool. What remained was the process ID. The wiki counts 32,767 possible random number streams per architecture, (2^15 - 1) times 3, or 98,301 in total.
In min-entropy terms, every key from an affected system had at most about 15 bits of unpredictability per architecture, whether it was a 1024 bit DSA key or a 4096 bit RSA key, yet the keys looked perfectly random.
The advisory's instructions were broad: upgrade the package, regenerate all key material created by OpenSSL on Debian since 0.9.8c-1 (SSH keys, OpenVPN keys, DNSSEC keys, X.509 certificate keys, and SSL/TLS session keys), and treat any DSA key ever used for signing or authentication on an affected system as compromised, because DSA leaks the private key when its per signature random value is predictable. Keys from GnuPG or GnuTLS were not affected, and Debian shipped weak key blacklists and checking tools such as ssh-vulnkey.
Lesson for defenders: downstream patches to cryptographic code need expert review and ideally upstream discussion, and knowing where and when each key was generated is what lets you rotate the right ones.
Dual_EC_DRBG, 2013 to 2015: a standardized generator nobody could trust
Dual_EC_DRBG was one of four DRBGs in the original SP 800-90A. Unlike the other three, it was built from elliptic curve operations using two fixed curve points. The concern discussed publicly by cryptographers is structural: whoever knows a secret relationship between those two points can, after seeing a modest amount of output, recover the generator's internal state and predict what comes next. Nobody outside can tell from the output whether such a relationship is known to anyone.
NIST's account of the outcome is short. After news reports in September 2013, NIST recommended against using the algorithm and reopened SP 800-90A for comment. On 21 April 2014, NIST announced that it had removed Dual_EC_DRBG from the revised draft, keeping the other three algorithms, and asked for final comments by 23 May 2014. It told current users to "transition to one of the three remaining approved algorithms as quickly as possible," told vendors not to wait for the final revision, and advised agencies to ask vendors whether their cryptographic modules relied on the algorithm. SP 800-90A Rev. 1 was finalized on 24 June 2015 without it.
Lesson for defenders: a generator can pass every statistical test and still be predictable to someone holding a secret, so prefer designs with publicly verifiable constants, and know which cryptographic modules you run. NIST's advice to "ask vendors" is an inventory question.
Weak keys at Internet scale, 2012: devices that booted without entropy
Nadia Heninger, Zakir Durumeric, Eric Wustrow and J. Alex Halderman presented "Mining Your Ps and Qs: Detection of Widespread Weak Keys in Network Devices" at the 21st USENIX Security Symposium in August 2012, where it won Best Paper. They collected TLS and SSH host keys from across the Internet and looked for keys that shared factors or values.
Their project site reports the headline numbers:
- 5.57% of TLS hosts and 9.60% of SSH hosts shared public keys "in an apparently vulnerable manner," due to insufficient randomness or device default keys.
- They obtained RSA private keys for 0.50% of TLS hosts and 0.03% of SSH hosts because those keys shared nontrivial common factors.
- They obtained DSA private keys for 1.03% of SSH hosts because of repeated signature randomness.
- Nearly all vulnerable hosts were headless and embedded network devices, such as routers, firewalls and server management cards, from 54 manufacturers they notified.
The mechanism, conceptually: an RSA modulus is the product of two primes. Two devices that boot with the same tiny entropy can generate the same first prime and different second primes, so their moduli share a factor, and a fast greatest common divisor computation exposes both private keys. The team also showed the Linux generator could be predictable at boot under certain conditions, and saw affected keys on BSD and Windows based systems too.
Lesson for defenders: never generate long lived keys before the generator is initialized; getrandom() blocks until it is. Shared default keys are not keys. And old firmware still carries old kernels, which is an inventory and patching problem.
The three failures compared
| Failure | Year disclosed | Root cause | Would statistical tests catch it? | Fix |
|---|---|---|---|---|
| Debian OpenSSL | 2008 | Downstream code change removed entropy input; only process ID left | No | Patch package, regenerate keys, blacklist weak keys |
| Dual_EC_DRBG | 2013 to 2014 | Design that allows state recovery by a party knowing a secret relation | No | Remove from standard; switch to Hash, HMAC or CTR DRBG |
| Weak device keys | 2012 | Keys generated at first boot before entropy was available | No | Seed before key generation; block until initialized; avoid shared defaults |
How much entropy do passwords, API tokens and session IDs need?
The answer depends on who generates the secret and how fast an attacker can test guesses.
Machine generated secrets
When software generates a secret with a CSPRNG, entropy equals the number of random bits drawn, as long as the encoding does not throw any away.
| Secret | Guidance | Bits | Example encoding |
|---|---|---|---|
| Web session ID | OWASP: at least 64 bits of entropy; use a CSPRNG value of at least 128 bits if you generate your own | 128 recommended | 32 hex characters or 22 base64url characters |
| Session secret (NIST) | SP 800-63B: input from an approved RBG, at least 64 bits | 64 minimum; 128 common | Opaque random value |
| Recovery code (NIST) | SP 800-63B: at least 64 bits from an approved RBG | 64 minimum | Base64 or numeric |
| Authentication nonce (NIST) | SP 800-63B: challenge nonce at least 64 bits, unique or statistically unique | 64 minimum | Random bytes |
| Short OTP (NIST) | SP 800-63B: below 64 bits, the verifier must rate limit attempts | 20 for 6 digits | 6 decimal digits |
| API token or password reset token | Python's secrets docs: as of 2015, 32 bytes (256 bits) believed sufficient for typical use | 256 | 43 base64url characters |
| Symmetric key | Equals the security strength; SP 800-63B refers to SP 800-131A's 112 bit minimum | 128 or 256 | Raw bytes |
OWASP's arithmetic is a good model: 64 bits as hex needs at least 16 characters, because each hex character carries 4 bits. Base64 carries 6 bits per character, so 128 bits needs 22 characters without padding.
Worked example 6: is a 64 bit session ID enough?
Suppose an attacker can test 10,000 session IDs per second against your site and you have 1,000,000 active sessions. Each guess hits some valid session with probability 10^6 / 2^64.
2^64 = 1.84e19 possible IDs
Valid fraction = 1e6 / 1.84e19 = 5.4e-14
Guesses per year = 1e4 * 3.15e7 = 3.15e11
Expected hits per year = 3.15e11 * 5.4e-14 = 0.017
Expected years to first hit ~= 58 years
At 128 bits the same calculation gives roughly 10^21 years. That margin is why OWASP's recommendation for self generated IDs is 128 bits even though its minimum is 64. Our post on how long a session ID should be works through more of these trade offs.
Worked example 7: collisions, the birthday bound
Guessing is one risk; accidental collisions are another. With n random IDs drawn from N = 2^b values, the probability of at least one collision is roughly 1 - e^(-n^2 / 2N).
b = 64, n = 2^32 (4.3 billion IDs): n^2 / 2N = 2^64 / 2^65 = 0.5 -> P ~= 1 - e^-0.5 ~= 39%
b = 128, n = 2^32: n^2 / 2N = 2^64 / 2^129 -> P ~= 2.7e-20
This is why random identifiers that must stay unique across a large system, such as request IDs or object keys, are usually 122 to 128 random bits, not 64.
Human chosen passwords
Human choices are not uniform, so there is no reliable way to compute their entropy from length and character set. SP 800-63B, in the 800-63-4 revision, stopped trying. It requires passwords used as a single factor to be at least 15 characters, allows a minimum of 8 when the password is only part of multi factor authentication, forbids composition rules such as mixing character types, forbids forced periodic changes absent evidence of compromise, and requires checking new passwords against a blocklist of commonly used, expected or compromised values. Length and blocklists handle the low min-entropy end, which is the part attackers try first.
For machine generated passwords the arithmetic is straightforward:
| Generation method | Calculation | Entropy |
|---|---|---|
| 4 words from a 7,776 word list | 4 x log2(7776) = 4 x 12.925 | 51.7 bits |
| 6 words from a 7,776 word list | 6 x 12.925 | 77.5 bits |
| 16 characters from 94 printable ASCII | 16 x log2(94) = 16 x 6.555 | 104.9 bits |
These figures hold only if each word or character is chosen by a CSPRNG. A person picking "random" words from the list does not get 12.925 bits per word.
How do you test an RNG, and what can tests not prove?
There are three layers of testing, and each answers a different question.
1. Statistical test batteries. NIST SP 800-22 Rev. 1a (April 2010) is a suite of statistical tests that look for patterns a uniform, independent bitstream would not show; NIST's April 2022 planning note says it will be revised. Batteries catch broken implementations, such as short cycles or biased conversions.
2. Entropy source validation. SP 800-90B's estimators and restart tests measure how much min-entropy a noise source actually delivers, and its health tests keep watching in the field. This is the layer that catches a dead ring oscillator or a thermal noise source that drifted when the device got cold.
3. Design and implementation review. Is the DRBG approved, seeded from a validated source before first use, reseeded, and safe across fork() and VM snapshots? Do callers use the cryptographic API?
What tests cannot prove:
- Unpredictability. AES in counter mode with an all zero key passes statistical batteries, as did every key from the 2008 Debian bug. Tests measure the output's distribution, not whether someone else can compute it.
- Absence of a back door. Dual_EC_DRBG's output is statistically fine. The question was who could predict it.
- Seeding at the moment that matters. A key made in a device's first second stays weak forever.
- That the caller used the generator correctly. Modulo bias (taking random bytes mod 10 and getting uneven digits), truncation, or reusing one value as both a token and a nonce all happen above the generator.
The defensible position is: use the platform generator, verify that your code calls it, and treat statistical tests as a smoke test for hardware and custom implementations, not as evidence of security.
Step by step: generate, inspect and audit randomness in your own code
These steps are safe to run on any development machine. None of them changes system configuration.
Step 1: check that the kernel generator is initialized (Linux)
# Kernel version: getrandom() needs 3.17+, the unified design arrived in 5.18
uname -r
# The kernel's entropy estimate (diagnostic only on modern kernels)
cat /proc/sys/kernel/random/entropy_avail
# Look for the "crng init done" message from boot
sudo dmesg | grep -i "crng init"
If the boot log shows that the generator initialized late on a device that generates keys at first boot, that is the 2012 weak key pattern, and keys should be generated after initialization.
Step 2: generate secrets the right way
# 32 random bytes as hex from OpenSSL's generator
openssl rand -hex 32
# Python: token for a URL, 32 bytes of randomness
python3 -c "import secrets; print(secrets.token_urlsafe(32))"
# Python: 128 bit session ID as 32 hex characters
python3 -c "import secrets; print(secrets.token_hex(16))"
# Node.js: 16 random bytes as base64url (22 characters)
node -e "console.log(require('crypto').randomBytes(16).toString('base64url'))"
# Raw bytes from the kernel, shown as hex
head -c 32 /dev/urandom | xxd -p
For Go, crypto/rand.Text returns a random string suitable for secrets, and rand.Read fills a buffer and, per the documentation, never returns an error.
package main
import (
"crypto/rand"
"encoding/hex"
"fmt"
)
func main() {
b := make([]byte, 16) // 128 bits
rand.Read(b) // documented to always fill b entirely
fmt.Println(hex.EncodeToString(b))
fmt.Println(rand.Text())
}
Step 3: estimate Shannon entropy and min-entropy of sample data
This short script computes the plug in Shannon and most common value min-entropy estimates per byte. It is a learning tool, not an SP 800-90B validation; real validation uses all the estimators, a million samples and restart data.
#!/usr/bin/env python3
"""Rough per-byte entropy estimates for a file. Educational only."""
import math
import sys
from collections import Counter
data = open(sys.argv[1], "rb").read()
n = len(data)
counts = Counter(data)
shannon = -sum((c / n) * math.log2(c / n) for c in counts.values())
p_max = max(counts.values()) / n
min_entropy = -math.log2(p_max)
print(f"samples: {n}")
print(f"distinct values: {len(counts)}")
print(f"Shannon (per byte): {shannon:.3f} bits")
print(f"Min-entropy (MCV): {min_entropy:.3f} bits")
Run it on three inputs and compare:
head -c 1000000 /dev/urandom > random.bin
python3 -I entropy_estimate.py random.bin # Shannon about 8.0, min-entropy about 7.9 bits per byte
python3 -c "print('the quick brown fox jumps over the lazy dog ' * 20000)" > text.txt
python3 -I entropy_estimate.py text.txt # Shannon about 4.3 bits, min-entropy about 2.3 (the space dominates)
python3 -c "import sys; sys.stdout.buffer.write(bytes(range(256)) * 4000)" > counter.bin
python3 -I entropy_estimate.py counter.bin # reports 8.0 for both, yet every byte is predictable
The third run is the whole lesson of the testing section: a counter has a perfectly flat byte histogram, so both estimates report the maximum, and every byte is predictable. Single symbol statistics cannot see structure across symbols, which is why SP 800-90B adds Markov, compression and prediction estimators and still takes the minimum.
Step 4: find general purpose generators used for secrets
Search your own repositories for general purpose generators near words like token, session, secret, password, nonce or key.
# JavaScript and TypeScript
grep -rnE "Math\.random\(" --include='*.js' --include='*.ts' . | grep -iE "token|session|secret|pass|nonce|key|otp"
# Python
grep -rnE "random\.(random|randint|choice|choices|getrandbits)\(" --include='*.py' . | grep -iE "token|session|secret|pass|nonce|key|otp"
# Go: math/rand imported in files that also mention tokens or keys
grep -rlE '"math/rand"' --include='*.go' . | xargs grep -liE "token|session|secret|nonce|key"
Each hit is a candidate, not a verdict. Replace confirmed cases with secrets, crypto.randomBytes or crypto/rand.
Step 5: check which cryptographic libraries you ship
RNG fixes arrive as library and distribution updates, as the Debian case showed. List the versions you actually ship.
openssl version -a
dpkg -l | grep -E "openssl|libssl|libgcrypt|gnutls" # Debian and Ubuntu
rpm -qa | grep -E "openssl|libgcrypt|gnutls" # RHEL family
pip show cryptography 2>/dev/null | head -3
npm ls --all 2>/dev/null | grep -iE "crypto|uuid|nanoid" | head
Container images need the same check, since a base image built last year carries last year's libraries.
Common mistakes
- Using Math.random, Python's random module or math/rand for tokens. These are fast, statistically good generators designed for simulation. Their state can often be reconstructed from outputs. Use the cryptographic API.
- Counting characters instead of bits. A 32 character token made of a timestamp, a username and four random hex digits has 16 bits of entropy, not 128.
- Quoting Shannon entropy for a security claim. A distribution with a heavy most likely value can report a high average. Ask for min-entropy, especially for hardware sources.
- Generating keys at first boot. Headless devices and freshly cloned VMs can start with little entropy. Wait for the generator to initialize, or provision keys from a strong source.
- Modulo bias. Taking a random byte mod 10 makes digits 0 to 5 slightly more likely than 6 to 9. Use library functions such as secrets.randbelow or crypto.randomInt that handle this.
- Forgetting nonce randomness in signatures. DSA and ECDSA expose the private key when per signature randomness repeats or is predictable.
- Patching cryptographic code downstream without upstream review. The Debian incident began with a well meant cleanup.
FAQ
What is the difference between Shannon entropy and min-entropy?
Shannon entropy averages the surprise across all outcomes. Min-entropy is -log2 of the probability of the single most likely outcome, so it measures how hard the best first guess is. Min-entropy is never higher than Shannon entropy and is the measure NIST uses for entropy sources.
Is /dev/urandom secure for generating keys?
Yes, once the kernel generator is initialized. The random(7) man page recommends /dev/urandom or getrandom() for nearly all purposes. The only caveat is very early boot, when /dev/urandom can return output before initialization; getrandom() avoids that by blocking until the pool is ready.
What is the difference between /dev/random and /dev/urandom?
/dev/random used to block when the kernel's entropy estimate ran low. Since Linux 5.6 it blocks only during early boot, and per the RNG maintainer, from 5.18 the two devices are the same generator.
How many bits of entropy should a session ID have?
OWASP says at least 64 bits, and recommends a CSPRNG value of at least 128 bits if you create your own. NIST SP 800-63B requires session secrets of at least 64 bits from an approved random bit generator. 128 bits, as 32 hex or 22 base64url characters, is a common choice.
How much entropy does a password need?
It depends on whether a human or a machine chooses it. For machine generated passwords, 70 to 80 bits or more resists offline guessing well; six random words from a 7,776 word list give 77.5 bits. For human chosen passwords, NIST SP 800-63B stops estimating entropy and instead requires minimum lengths, blocklist checks and no composition rules.
What are NIST SP 800-90A, 800-90B and 800-90C?
800-90A specifies deterministic random bit generators (Hash_DRBG, HMAC_DRBG, CTR_DRBG), 800-90B specifies entropy sources and health tests, and 800-90C, final since September 2025, combines them into complete generators.
Why was Dual_EC_DRBG removed?
After news reports in September 2013, NIST recommended against using Dual_EC_DRBG, and on 21 April 2014 it removed the generator from the draft revision of SP 800-90A, citing its own evaluation and the lack of public confidence. Its design allows anyone who knows a secret relationship between its two curve points to predict output after seeing some of it.
Can a statistical test prove a random number generator is secure?
No. A counter run through a cipher, or a weakly seeded generator, passes them. Security comes from approved designs, validated entropy sources, correct seeding and correct use.
Where entropy meets software supply chain security
Safeguard's models are single purpose security models, and Safeguard's own benchmarks page says plainly that a frontier general purpose LLM will out-write any Safeguard model on math word problems. Nothing in this post suggests Safeguard computes entropy, validates random number generators, assesses entropy sources or reviews cryptographic designs. Those jobs belong to cryptographers, accredited labs and the operating system and library maintainers named above.
The honest overlap is the code that ships the randomness. Each failure above reached users through software components (a distribution's OpenSSL package, vendor cryptographic modules, firmware carrying old kernels), and the first practical question each time was "where do we run the affected version?" That inventory and patching question is where Safeguard applies. Safeguard scans repositories, container images and SBOM files, matches components such as OpenSSL, libgcrypt, language cryptography packages and their transitive dependencies against vulnerability databases, shows which projects carry a given component and version, and uses reachability analysis to rank the vulnerable code paths your application actually calls. It does not judge how your code calls a generator; step 4 and code review do that.
Randomness bugs are rare, but when one lands it lands in a widely shared library, and the fix is a version change you have to roll out everywhere that library hides. The section below is our standard guidance for closing that gap in the open source components and container images you build and ship; it applies to those libraries, not to the mathematics of entropy itself.
Fix it without waiting for upstream
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.
3. Gold Registry: start from components that are already fixed
registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | The Gold scan GitHub Action in CI, plus Safeguard policies and gates |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch (early access) |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
None of these demos measure entropy or test a random number generator. They show how Safeguard handles the libraries and images your cryptographic code depends on.
The first demo is a real Safeguard CLI session: asked to fix a lodash CVE, it reads the finding and advisory, asks before each command, edits package.json to lodash 4.17.21, runs npm install and npm test with approval, and pushes nothing. The same flow applies when a pinned cryptography package needs an upgrade.
Click through it at your own pace, or open it full screen.
The second demo sets up Safeguard's local runner on your own machine, approves it with its device code, and runs a scan from the terminal. The scan shows which version of a cryptographic library a project actually ships.
Click through it at your own pace, or open it full screen.
The third demo connects Claude to the Safeguard MCP server with OAuth and lets you choose which tools Claude can use. When a library advisory lands, you can ask which projects carry the affected version.
Click through it at your own pace, or open it full screen.
To try it on your own projects, download Safeguard or read the CLI documentation.
Quick check
A source outputs A with probability 0.5, and B, C, D, E with probability 0.125 each. What are its Shannon entropy and min-entropy?
Shannon entropy is 0.5 x 1 + 4 x 0.125 x 3 = 2 bits. Min-entropy is -log2(0.5) = 1 bit. An attacker who always guesses A is right half the time, which is what min-entropy captures.
How many hex characters does a 128 bit session ID need, and how many base64url characters?
32 hex characters, because each carries 4 bits, and 22 base64url characters, because each carries 6 bits (21 characters give only 126 bits).
Sources
- C. E. Shannon, "A Mathematical Theory of Communication," Bell System Technical Journal, 1948 (reprint with corrections)
- C. E. Shannon, "Prediction and Entropy of Printed English," Bell System Technical Journal, 1951
- NIST CODATA: Boltzmann constant
- NIST SP 800-90A Rev. 1: Recommendation for Random Number Generation Using Deterministic Random Bit Generators
- NIST SP 800-90B: Recommendation for the Entropy Sources Used for Random Bit Generation
- NIST SP 800-90B full text (PDF)
- NIST SP 800-90C: Recommendation for Random Bit Generator (RBG) Constructions
- NIST SP 800-90C full text (PDF)
- NIST SP 800-22 Rev. 1a: A Statistical Test Suite for Random and Pseudorandom Number Generators
- NIST: NIST Removes Cryptography Algorithm from Random Number Generator Recommendations (21 April 2014)
- NIST SP 800-63B, Authentication and Authenticator Management (800-63-4)
- random(7) Linux manual page
- random(4) Linux manual page
- getrandom(2) Linux manual page
- Jason A. Donenfeld: Random number generator enhancements for Linux 5.17 and 5.18
- Microsoft Learn: BCryptGenRandom function
- Microsoft Learn: ProcessPrng function
- Niels Ferguson, "The Windows 10 random number generation infrastructure," Microsoft, October 2019
- Debian Security Advisory DSA-1571-1: openssl, predictable random number generator
- Debian Wiki: SSLkeys
- factorable.net: Widespread Weak Keys in Network Devices
- USENIX Security 2012: Mining Your Ps and Qs, Heninger, Durumeric, Wustrow, Halderman
- RFC 4086: Randomness Requirements for Security
- OWASP Session Management Cheat Sheet
- Python documentation: secrets module
- Python documentation: random module
- Go documentation: crypto/rand
- Node.js documentation: crypto
- Safeguard: Model benchmarks
Last reviewed: 24 October 2026.