Files
skills/docs/security-stack/security/password-generator/src/entropy/entropy.md
2026-05-29 08:20:56 +01:00

3.8 KiB

entropy

(no submodules)

Purpose

estimateEntropy(password, opts?) returns the upper-bound entropy of a candidate password as log2(charsetSize) * password.length, where charsetSize is the cardinality of the union of every detected built-in class (lowercase / uppercase / digits / symbols) plus any caller-supplied opts.extraClasses whose characters appear in the password. estimatePassphraseEntropy(words, wordlistSize) returns log2(wordlistSize) * words.length for Diceware-style passphrases. assertStrength(password, minBits, opts?) consumes estimateEntropy and throws PWGEN.STRENGTH_INSUFFICIENT when the computed bits are below the floor.

The entropy formulas assume uniform sampling and ignore patterns — "AAAAAAAA" is reported as the same entropy as "hG83mP9k" if both contain the same charset classes. Pattern-aware scoring (zxcvbn-style) is intentionally out of scope and lives in the separate @suaveplan/password-strength package.

Features

  • Detects class membership in a single pass over the password.
  • extraClasses opt-in lets callers report custom-charset passwords (emoji unions, hex-only PINs).
  • assertStrength throws on insufficient entropy with a structured error code.
  • Zod-validated EntropyOptions; mismatch → PWGEN.INVALID_OPTIONS.

Usage: Basic Example

import { estimateEntropy } from "@suaveplan/password-generator";

const { bits, charsetSize } = estimateEntropy("Abc123Xy");
// charsetSize === 62, bits === log2(62) * 8 ≈ 47.6

Usage: Advanced Example

import {
 assertStrength,
 estimateEntropy,
 estimatePassphraseEntropy,
} from "@suaveplan/password-generator";

// Server-side guard before persisting a vault item
function persistVaultItem(pw: string): void {
 assertStrength(pw, 80); // throws PWGEN.STRENGTH_INSUFFICIENT if < 80 bits
 // ... persist
}

// Custom emoji-augmented class
const e = estimateEntropy("emoji👍2025", { extraClasses: ["👍🎉🚀"] });

// Diceware reporting
const words = ["correct", "horse", "battery", "staple", "alpha", "beta"];
const passEntropy = estimatePassphraseEntropy(words, 7776); // ≈ 77.55 bits

API Reference

estimateEntropy(password: string, opts?: EntropyOptions): EntropyEstimate

Returns { bits, charsetSize }. bits is 0 when the password is empty.

estimatePassphraseEntropy(words: readonly string[], wordlistSize: number): number

Returns log2(wordlistSize) * words.length. Throws PWGEN.INVALID_OPTIONS for non-positive integer wordlistSize.

assertStrength(password: string, minBits: number, opts?: EntropyOptions): void

Returns void on success. Throws PwgenError(PWGEN.STRENGTH_INSUFFICIENT) when the computed bits are strictly less than minBits.

EntropyOptions

  • extraClasses?: readonly string[] — additional character classes the password may draw from.

Implementation Notes

Class detection is a single forward pass over the password, tracking four boolean flags for the built-in classes plus a Set<number> of seen extraClasses indices. The charsetSize accumulator only adds a class's cardinality when the first matching character is seen, which keeps the cost O(password.length * extraClasses.length) worst-case.

The formula deliberately reports an upper bound. A consumer comparing this number to a cracking-time chart should treat it as a ceiling: real-world passwords contain dictionary patterns, keyboard walks, and date stamps that reduce the effective entropy substantially. Use @suaveplan/password-strength for adversarial-aware estimates. assertStrength is a deliberately blunt instrument — it answers "does this password meet the floor on the most generous estimator?" — and is intended as a server-side guardrail against drastically weak inputs, not a substitute for proper strength scoring.