Move docs/ package mirror into document-navigator plugin
Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
# `verifier`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Compute the SRP-6a verifier `v = g^x mod N` where `x = H(salt | H(I | ":" | P))`, per RFC 5054 §2.6. The verifier is what the server stores on registration; the password never leaves the client. The deterministic mapping from `(I, P, salt)` to `(x, v)` means an attacker who steals the verifier database cannot recover passwords without solving a discrete-log problem in the chosen safe-prime group — for the bundled 2048-bit group, that puts the attacker at the same security level as a 2048-bit RSA key.
|
||||
|
||||
## Features
|
||||
|
||||
- Pure deterministic function of `(I, P, salt, params)`.
|
||||
- Returns both `x` (private; consumer should zeroize immediately after use) and `v` (public; server-side storage).
|
||||
- `v` is zero-padded to `byteLength(N)` so on-wire / on-disk shape is fixed.
|
||||
- `modPow` routes through `@noble/curves`'s hardened path.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { computeVerifier } from "@suaveplan/srp/verifier";
|
||||
import { params2048Sha256 } from "@suaveplan/srp/params";
|
||||
import { nobleHashBridge } from "@suaveplan/srp/hash";
|
||||
|
||||
const params = params2048Sha256(nobleHashBridge("SHA256"));
|
||||
const salt = new Uint8Array(16);
|
||||
const { x, v } = computeVerifier("alice", "password123", salt, params);
|
||||
console.log(x.byteLength, v.byteLength); // 32 256
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { computeVerifier } from "@suaveplan/srp/verifier";
|
||||
import { params2048Sha256 } from "@suaveplan/srp/params";
|
||||
import { nobleHashBridge } from "@suaveplan/srp/hash";
|
||||
import { zeroize } from "@suaveplan/crypto-utils";
|
||||
|
||||
const params = params2048Sha256(nobleHashBridge("SHA256"));
|
||||
const salt = crypto.getRandomValues(new Uint8Array(16));
|
||||
const { x, v } = computeVerifier("alice", "correct horse battery staple", salt, params);
|
||||
|
||||
// Persist v + salt server-side; zeroize x locally.
|
||||
const serverRecord = { I: "alice", salt, v };
|
||||
zeroize(x);
|
||||
console.log(serverRecord.v.byteLength);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `computeVerifier(I, P, salt, params): { x, v }`
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ------------ | ---------------------------------------- |
|
||||
| `I` | `string` | Username / identity, UTF-8 encoded. |
|
||||
| `P` | `string` | Password, UTF-8 encoded. |
|
||||
| `salt` | `Uint8Array` | Random per-user salt. |
|
||||
| `params` | `SrpParams` | Group + hash bundle. |
|
||||
|
||||
Returns `{ x, v }`. `x` is `params.hash.digestSize` bytes; `v` is `params.N.byteLength` bytes.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The function returns BOTH `x` and `v` even though only `v` is sent to the server. The reason: most consumers compute the verifier as part of a multi-step registration flow that also needs `x` for an immediate `clientFinishHandshake` (e.g. "register, then log in" UX patterns). Returning `x` saves one redundant hash chain; consumers that never use `x` should `zeroize(x)` immediately after the call.
|
||||
|
||||
The `bigIntToBytes(vInt, params.N.byteLength)` call zero-pads `v` on the left to the modulus width. This matters because `v` is later mixed into hashes via `PAD` on the server side; storing the un-padded representation would force every server-side handshake step to re-pad on read, and would produce different on-disk bytes for the same logical verifier depending on its leading-zero pattern.
|
||||
Reference in New Issue
Block a user