Initial Commit
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# `passphrase`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`generatePassphrase(opts, rng?)` produces an EFF-style Diceware passphrase by drawing `length` words uniformly (via the bias-free `randomInt`) from the wordlist resolved via `opts.wordlist`. The default — 6 words from the EFF Long Wordlist joined with `-` — gives ≈ 77.55 bits of entropy, comfortably above the 75-bit floor recommended for symmetric-key-equivalent secrets. Diceware-style passphrases are dramatically more memorable than random char-class passwords at equivalent entropy, which is why the vault item editor in `@suaveplan/vault` defaults to this mode for the "Generate password" surface.
|
||||
|
||||
The function honours configurable separator, three capitalisation modes (`none` / `firstChar` / `randomWord`), and optional `addDigits` / `addSymbols` injections at random positions in the joined output. The decoration knobs exist so the same generator can produce strings that satisfy downstream "must contain a digit" composition rules without abandoning the Diceware backbone.
|
||||
|
||||
## Features
|
||||
|
||||
- Bundled EFF Long (7776) and EFF Short 1 (1296) wordlists via tree-shakeable subpath imports.
|
||||
- Caller-supplied `string[]` wordlists allowed past the 256-entry min-size guard.
|
||||
- Configurable separator (`default "-"`).
|
||||
- `capitalize: "none" | "firstChar" | "randomWord"`.
|
||||
- `addDigits` / `addSymbols` injection at randomised positions.
|
||||
- Deterministic under seeded `Rng`.
|
||||
- Zod-validated; mismatch → `PWGEN.INVALID_OPTIONS`.
|
||||
- Emits `pwgen.generate_duration_ms{mode=passphrase}` and `pwgen.entropy_bits{mode=passphrase}` histograms.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { generatePassphrase } from "@suaveplan/password-generator";
|
||||
|
||||
const pp = generatePassphrase({}); // 6 words, EFF Long, joined with "-"
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { generatePassphrase } from "@suaveplan/password-generator";
|
||||
|
||||
// 5 short words capitalised at random, two trailing digits, one symbol
|
||||
const pp = generatePassphrase({
|
||||
length: 5,
|
||||
wordlist: "short",
|
||||
capitalize: "randomWord",
|
||||
addDigits: 2,
|
||||
addSymbols: 1,
|
||||
separator: " ",
|
||||
});
|
||||
|
||||
// Caller-supplied themed wordlist (must be ≥ 256 entries)
|
||||
const dnd = [/* ... 300 fantasy nouns ... */];
|
||||
const themed = generatePassphrase({ wordlist: dnd, length: 4 });
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `generatePassphrase(opts?: GeneratePassphraseOptions, rng?: Rng): string`
|
||||
|
||||
- `opts.length` (default `6`): number of words to draw.
|
||||
- `opts.wordlist` (default `"long"`): `"long"` / `"short"` / registry name / inline `string[]`.
|
||||
- `opts.separator` (default `"-"`): string inserted between words before decoration.
|
||||
- `opts.capitalize` (default `"none"`): `"none"` / `"firstChar"` / `"randomWord"`.
|
||||
- `opts.addDigits` / `opts.addSymbols` (default `0`): count of characters injected at randomised positions.
|
||||
- `rng`: defaults to `defaultRng()`.
|
||||
|
||||
Throws `PwgenError(PWGEN.INVALID_OPTIONS)` on Zod mismatch, `PwgenError(PWGEN.WORDLIST_TOO_SHORT)` for inline arrays below 256 entries, and `PwgenError(PWGEN.WORDLIST_NOT_REGISTERED)` for unknown registry names.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Wordlist resolution lazily routes through `getWordlist(name)` so the registry's min-size guard applies uniformly — there's no special-case for the bundled EFF lists. Inline `string[]` wordlists bypass the registry but go through the same 256-entry floor inline, so a caller passing a tiny array sees the same `PWGEN.WORDLIST_TOO_SHORT` they would see from `registerWordlist`.
|
||||
|
||||
Decoration is applied _after_ joining: `addDigits` / `addSymbols` inserts characters at uniformly-random positions in the already-joined string. This means a digit can land mid-word (e.g. `hor5se-rifle`) — that's intentional because inserting only at word boundaries would create a small-cardinality position distribution that's easier to fingerprint. Per-class injection uses bias-free `randomInt` against the `DIGITS` / `SYMBOLS` sets defined in the `charsets` module.
|
||||
|
||||
Entropy reporting uses `estimatePassphraseEntropy(words, wordlistSize)` against the _picked_ words (not the post-decoration string), so the reported figure reflects the Diceware contribution. Decoration adds a small amount of entropy on top of that, but the package does not currently roll that into the reported figure to keep the formula stable across runs.
|
||||
Reference in New Issue
Block a user