ProtemKey

Under the hood · 5 min read

How password generators work

Inside a password generator: why it must use the browser's CSPRNG and not Math.random, how it picks characters fairly, and how settings turn into bits.

A password generator has one job: pick a string so that every possible string allowed by your settings is equally likely, using randomness nobody else can predict. When both conditions hold, the strength of the result can be calculated exactly. When either fails, the number shown next to the password stops meaning anything.

This guide walks through the three parts that matter: the random source, the way each character is chosen, and how the strength figure is computed. The password generator on this site works as described here, and its test results are on the methodology page.

Part 1: the random source

Computers are deterministic, so “random” numbers in software come from a pseudo-random number generator (PRNG): an algorithm that expands a starting value, the seed, into a long sequence that looks random. Whether that is good enough depends on what the numbers are for.

Math.random is not designed for secrets

JavaScript’s Math.random() is the obvious tool, and the wrong one. The language specification promises only that it returns a number from 0 up to 1 “chosen randomly or pseudo randomly with approximately uniform distribution over that range, using an implementation-defined algorithm or strategy.”

That definition says nothing about unpredictability. A browser may use a fast generator whose internal state is small, and for that kind of generator, someone who sees enough outputs can often reconstruct the state and predict every value that follows. That is fine for shuffling a playlist or placing particles in an animation. It is not fine for a password, where the entire point is that nobody can work out what was chosen.

crypto.getRandomValues is

Browsers provide a separate source for security work. The W3C Web Cryptography API defines crypto.getRandomValues(), which fills an array with random bytes, and says: “Implementations should generate cryptographically strong random values using well-established cryptographic pseudo-random number generators seeded with high-quality entropy, such as from an operating-system entropy source (e.g., “/dev/urandom”).”

A cryptographically secure PRNG (CSPRNG) is one where seeing any amount of output does not help predict the next bit better than a coin toss, as long as the seed stays secret. The operating system gathers that seed from unpredictable physical events. RFC 4086, the IETF’s guide to randomness for security, covers why this distinction matters and how such sources are built.

Two practical details: one call can return at most 65,536 bytes (the specification throws an error above that), and it works without any network access. This site’s generators call crypto.getRandomValues and nothing else.

NIST SP 800-63B-4 asks the same of anyone generating secrets for authentication: random values “SHALL be generated by an approved random bit generator.”

Part 2: choosing each character fairly

A CSPRNG gives bytes or 32-bit integers. A password needs characters from a set, for example the 94 printable ASCII characters. Turning one into the other is where many generators quietly go wrong.

The tempting shortcut is randomByte % 94. A byte has 256 values, and 256 is not a multiple of 94, so some characters come up more often than others. The modulo bias guide works through the exact numbers: with one byte, 68 of the 94 characters get 3 chances in 256 and the other 26 get only 2.

The fix is rejection sampling: discard random values from the uneven top end of the range and draw again. This site draws a 32-bit value x, rejects it if x ≥ 2^32 − (2^32 mod n), and otherwise returns x mod n. For n = 94, a redraw is needed with probability about 1 in 100 million, so the cost is nil and the result is exactly uniform.

Repeat for each position, and every password of that length over that set is equally likely.

Part 3: strength from the settings, not the output

If every one of N possible passwords is equally likely, the strength is log2(N) bits. That figure describes the process, not the string it happened to produce. A good generator computes it from your settings:

bits = length × log2(size of character set)

Character set Size Bits per character
Digits 10 3.322
Lowercase letters 26 4.700
Letters and digits 62 5.954
All printable ASCII 94 6.555
Printable ASCII without the 7 look-alikes 87 6.443

The look-alikes are I, l, 1, |, O, 0 and o (see character sets and look-alikes). So 20 characters from all 94 give 20 × 6.555 = 131.09 bits; 12 from letters and digits give 71.45 bits.

Bits of entropy for random passwords: length × log2(characters in the set).
LengthDigits (10)Lowercase (26)Letters (52)Letters + digits (62)All printable ASCII (94)
826.637.645.647.652.4
1033.247.057.059.565.5
1239.956.468.471.578.7
1446.565.879.883.491.8
1653.275.291.295.3104.9
2066.494.0114.0119.1131.1
2479.7112.8136.8142.9157.3

Strength estimates that look at the output instead, as a strength meter must for a password someone typed, answer a different question: how quickly would an attacker who guesses common patterns first reach this string? For a generated password, the settings already tell you the answer.

Worked example: “at least one of each”

Many sites demand at least one lowercase, uppercase, digit and symbol. A generator can satisfy that by rejecting any password that lacks a class and drawing again, which keeps the remaining passwords equally likely. The cost is that the rejected ones no longer count.

Inclusion-exclusion counts what remains. For length L over classes of size 26, 26, 10 and 32: start with 94^L, subtract every password missing one class (for example 68^L missing lowercase, 84^L missing digits), add back those missing two, and so on.

Length All strings: bits With “one of each”: bits Share of strings that qualify
8 52.44 51.32 46.1%
12 78.66 78.14 70.0%
16 104.87 104.59 82.3%
20 131.09 130.93 89.1%

At 8 characters, more than half of all possible strings lack some class, so the rule costs about 1.1 bits. At 20 characters it costs under 0.2 bits. The rule makes passwords slightly weaker, never stronger; it exists because some sites require it. This site’s generator computes the exact figure this way when the option is on.

What a generator cannot do

A generator controls how the password is made. It cannot control what happens next: reuse on several sites, a site storing it badly, or typing it into a phishing page. Those are covered in credential stuffing and password reuse and password managers explained. For why randomness beats human choice at all, see random versus human-chosen passwords.

Sources

  1. W3C Web Cryptography API (getRandomValues)
  2. ECMAScript Language Specification, Math.random
  3. RFC 4086: Randomness Requirements for Security (IETF, 2005)
  4. NIST SP 800-63B-4, Digital Identity Guidelines: Authentication and Authenticator Management (2025)

General explanation, not advice for a particular situation. Updated 2026-10-31.