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,74 @@
|
||||
# `pake-provider`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`PakeProvider` is the augmented-password-authenticated-key-exchange seam for `@suaveplan/srp`. SRP-6a is the first implementation (`srpPakeProvider`); the interface is intentionally minimal so a future OPAQUE provider (`@suaveplan/opaque`, not yet shipped) can drop in without changing the `@suaveplan/vault` consumer surface. The seam covers three lifecycle methods: `registerCredential` (turn `(I, P, salt)` into a server-storable record), `clientHandshake` (drive the client side once the server's public value `B` is known), and `serverHandshake` (drive the server side once the client's `A` and `M1` are known).
|
||||
|
||||
## Features
|
||||
|
||||
- Stable interface shape independent of the underlying PAKE algorithm.
|
||||
- `srpPakeProvider(params)` returns a fully-wired SRP-6a implementation.
|
||||
- `clientHandshake` accepts the client's pre-drawn private exponent so callers can replay deterministic handshakes from a seeded RNG.
|
||||
- `serverHandshake` accepts the server's pre-drawn `(b, B)` so callers can replay deterministic handshakes symmetrically.
|
||||
- Outcome shape is consistent with `clientFinishHandshake` / `serverFinishHandshake` — `K`, `S`, `M1`/`M2` are all surfaced.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { srpPakeProvider } from "@suaveplan/srp/pake";
|
||||
import { params2048Sha256 } from "@suaveplan/srp/params";
|
||||
import { nobleHashBridge } from "@suaveplan/srp/hash";
|
||||
|
||||
const provider = srpPakeProvider(params2048Sha256(nobleHashBridge("SHA256")));
|
||||
const salt = new Uint8Array(16);
|
||||
const registration = provider.registerCredential("alice", "password123", salt);
|
||||
console.log(registration.v.byteLength); // 256
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { srpPakeProvider } from "@suaveplan/srp/pake";
|
||||
import { params2048Sha256 } from "@suaveplan/srp/params";
|
||||
import { nobleHashBridge } from "@suaveplan/srp/hash";
|
||||
import { clientStartHandshake } from "@suaveplan/srp/client";
|
||||
import { serverStartHandshake } from "@suaveplan/srp/server";
|
||||
|
||||
const params = params2048Sha256(nobleHashBridge("SHA256"));
|
||||
const provider = srpPakeProvider(params);
|
||||
const rng = {
|
||||
nextUint32: () => 0,
|
||||
nextFloat: () => 0,
|
||||
nextBytes: (n: number) => crypto.getRandomValues(new Uint8Array(n)),
|
||||
};
|
||||
const salt = new Uint8Array(16);
|
||||
const reg = provider.registerCredential("alice", "password123", salt);
|
||||
const { a } = clientStartHandshake(rng, params);
|
||||
const { b, B } = serverStartHandshake(reg.v, rng, params);
|
||||
const client = provider.clientHandshake("alice", "password123", salt, B, a);
|
||||
const server = provider.serverHandshake(reg, client.A, client.M1, B, b);
|
||||
console.log(Buffer.from(client.K).equals(Buffer.from(server.K))); // true
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `srpPakeProvider(params: SrpParams): PakeProvider`
|
||||
|
||||
Returns a SRP-6a implementation of the `PakeProvider` interface bound to the supplied `params`.
|
||||
|
||||
### `interface PakeProvider`
|
||||
|
||||
| Method | Signature | Description |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------- |
|
||||
| `registerCredential` | `(I, P, salt) → PakeRegistration` | Compute `{ I, salt, v }` server-storable record. |
|
||||
| `clientHandshake` | `(I, P, salt, serverPublic, clientPrivate) → PakeClientHandshake` | Drive the client; `clientPrivate` is the `a` from `clientStartHandshake`. |
|
||||
| `serverHandshake` | `(stored, clientPublic, clientProof, B, b) → PakeServerHandshake` | Drive the server; `(B, b)` come from `serverStartHandshake`. |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`clientHandshake` accepts the client's private exponent and recomputes `A = g^a mod N` rather than accepting `A` directly. This costs one extra `modPow` per call but lets the seam absorb any future PAKE-algorithm change that derives `A`-equivalent values differently from `clientPrivate`. The cost is amortised against the dominant `modPow` chain inside `clientFinishHandshake`, so the wall-time impact is roughly +20%.
|
||||
|
||||
The `PakeProvider` interface deliberately does NOT include `clientStartHandshake` / `serverStartHandshake` because the start step is a pure RNG draw plus a `modPow` — the values produced are not augmented-PAKE-specific, so the seam exposes only the augmented-step entry points. Consumers driving the seam externally call `clientStartHandshake` and `serverStartHandshake` from `@suaveplan/srp/client` and `@suaveplan/srp/server` respectively, then thread the resulting public values into `PakeProvider`.
|
||||
Reference in New Issue
Block a user