first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
# `entropy`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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
```ts
import { estimateEntropy } from "@suaveplan/password-generator";
const { bits, charsetSize } = estimateEntropy("Abc123Xy");
// charsetSize === 62, bits === log2(62) * 8 ≈ 47.6
```
## Usage: Advanced Example
```ts
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.