Password entropy, honestly computed, and what NIST 800-63B says
A calculator for the entropy of a password generator, the guess-time arithmetic at four rates, and why a chosen password cannot be scored.
Most "password strength meters" answer a question that cannot be answered: how random is this string a person just typed. This article does the arithmetic that can be done, entropy of a generation method and the time it buys at a stated guess rate, ships the calculator, and then reads NIST SP 800-63B, which reached the same conclusion and rewrote the rules around it.
Entropy belongs to the process, not the string
Entropy in bits is log2 of the number of equally likely outcomes. A password picked uniformly at random from a set of N possibilities has log2(N) bits, and an attacker who knows the set but not the pick needs, on average, half the set to find it.
For a string of L characters each drawn uniformly from an alphabet of size A, that is L * log2(A). For a passphrase of W words each drawn uniformly from a list of D words, it is W * log2(D). Those are the only two formulas in the calculator, because they are the only two that are honest.
The string Tr0ub4dor&3 has no entropy on its own. If it was produced by a person following the habit "take a word, swap some letters for lookalikes, add a symbol and a digit", its entropy is log2 of how many strings that habit produces, which is far smaller than the 95-character arithmetic suggests. A meter that sees only the string cannot know which process produced it. This is why NIST's guidance, in Section 3.1.1.2 of SP 800-63B, "SHALL NOT impose other composition rules (e.g., requiring mixtures of different character types)". Composition rules shape human habits into a smaller, more predictable set; they do not add randomness.
The calculator and its table
password-entropy-calculator-nist-800-63b.py prints expected guess time for eight generation methods at four rates. The rates are chosen to bracket the two situations that matter: guessing through your login form, where you control the rate, and guessing against a stolen hash table, where the attacker does.
python code/password-entropy-calculator-nist-800-63b.py
python code/password-entropy-calculator-nist-800-63b.py --length 20 --alphabet lower
python code/password-entropy-calculator-nist-800-63b.py --words 5 --wordlist-size 7776
The table below is the script's output, rounded. "Bits" is the entropy of the method; each time column is the expected time to reach half the keyspace at that rate.
| Generation method | Bits | 10/s (throttled online) | 1,000/s (unthrottled online) | 10^10/s (offline, fast hash) | 10^12/s (offline, cluster) |
|---|---|---|---|---|---|
| 6 random digits (a PIN) | 19.9 | 13.9 h | 8.3 min | under 1 s | under 1 s |
| 8 random alphanumeric | 47.6 | 346,000 y | 3,459 y | 3.0 h | 1.8 min |
| 4 random words (7,776 list) | 51.7 | 5.8 million y | 57,900 y | 2.1 d | 30.5 min |
| 8 random printable ASCII | 52.6 | 10.5 million y | 105,000 y | 3.8 d | 55 min |
| 15 random lowercase letters | 70.5 | 2.7 x 10^12 y | 2.7 x 10^10 y | 2,657 y | 26.6 y |
| 6 random words (7,776 list) | 77.5 | 3.5 x 10^14 y | 3.5 x 10^12 y | 350,000 y | 3,503 y |
| 12 random printable ASCII | 78.8 | 8.6 x 10^14 y | 8.6 x 10^12 y | 856,000 y | 8,561 y |
| 16 random printable ASCII | 105.1 | 7 x 10^22 y | 7 x 10^20 y | 7 x 10^13 y | 7 x 10^11 y |
Two things fall out of the table. First, the offline columns are where passwords die. Eight random characters, which many people would call strong, last hours against a fast unsalted hash on ordinary hardware. That column is why the previous article on this site exists: a memory-hard hash at OWASP's Argon2id parameters drops the attacker's rate by orders of magnitude, and the same eight characters move rightward in the table. Second, the online columns are enormous even for weak methods, provided the rate really is throttled. A six-digit PIN survives half a day at ten guesses per second, and NIST's requirement in Section 3.2.2 is tighter than that: the verifier "SHALL implement a rate-limiting mechanism that effectively limits the number of failed authentication attempts", with a general limit of "no more than 100" consecutive failures. At a hundred guesses total, the script's second table shows the fraction of each keyspace an attacker covers: 1.5 x 10^-14 of the 8-character printable space, and 4.6 x 10^-13 of the alphanumeric one.
Where the arithmetic stops being useful
The table describes uniformly random secrets. Real attacks on real password tables do not enumerate the keyspace; they try the passwords people actually use, in order of popularity, which is why "password" appears 52,372,427 times in the Pwned Passwords corpus (checked 2026-09-05 through the range API; the audit script elsewhere on this site does the same lookup). Against that ordering, entropy is the wrong measure, and popularity is the right one.
This is the reasoning behind the other half of NIST's Section 3.1.1.2. Verifiers "SHALL compare the prospective secret against a blocklist that contains known commonly used, expected, or compromised passwords", explicitly including "passwords obtained from previous breach corpuses". Have I Been Pwned's Pwned Passwords service exists for this check; its own page notes that "NIST guidelines specifically recommend checking user passwords against previously breached datasets" and describes the k-anonymity design where the browser sends only the first five characters of a SHA-1 hash.
The rules that follow from the arithmetic
Read together, SP 800-63B's password requirements are the arithmetic above turned into policy.
| Rule (SP 800-63B, 2025 revision) | Why |
|---|---|
| Minimum 15 characters when the password is the only factor; 8 with MFA (3.1.1.2) | length is the one lever a policy can pull that reliably adds keyspace |
| Allow at least 64 characters (3.1.1.2) | passphrases from a wordlist need room; 6 words of 7 letters is over 40 characters with spaces |
| No composition rules (3.1.1.2) | they shrink the human-chosen set and do not add randomness |
| No periodic change unless there is evidence of compromise (3.1.1.2) | forced rotation produces predictable increments, not new entropy |
| Blocklist check against breach corpora (3.1.1.2) | real guessing is by popularity, not by keyspace |
| Rate limit failed attempts, generally no more than 100 (3.2.2) | it is the only thing that makes the online columns real |
| Salt of at least 32 bits, memory-hard hashing at the highest practical cost (3.1.1.2) | it is the only thing that makes the offline columns survivable |
One caveat on the bcrypt row in the OWASP cheat sheet: it caps input at 72 bytes. If your policy allows 64-character passwords and your hash is bcrypt, characters beyond 72 bytes are silently ignored, and a long passphrase in a non-Latin script can hit that limit well before 64 characters. Either use Argon2id or scrypt, or enforce the byte cap at the form.
What to do
For the systems you run:
- Set a 15-character minimum, a 64-character or higher maximum, and no character-class rules. Reject anything on a breach blocklist with a message that says why.
- Rate limit by account and by source, count failures, and lock or slow down long before 100 consecutive failures. Log the failures so the guessing shows in your logs.
- Hash with Argon2id at or above OWASP's minimum and measure your login latency.
- Run the calculator with your own alphabet and length to see which column your policy lives in, and remember the column is only as good as the rate limit or the hash that enforces it.
For your own accounts, the honest advice is the one the table gives: stop choosing passwords. Let a generator pick them, use at least twelve printable characters or five to six dictionary words where a site allows length, and let the password manager remember them. The entropy then belongs to the generator, which is the only place it can be measured.
The limitation, stated plainly: this calculator will not tell you whether your current password is good. Nothing can, from the string alone. It tells you what a generator buys and what the verifier on the other end has to do to keep that purchase intact.