4.8 KiB
pake-provider
(no submodules)
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.clientHandshakeaccepts the client's pre-drawn private exponent so callers can replay deterministic handshakes from a seeded RNG.serverHandshakeaccepts the server's pre-drawn(b, B)so callers can replay deterministic handshakes symmetrically.- Outcome shape is consistent with
clientFinishHandshake/serverFinishHandshake—K,S,M1/M2are all surfaced.
Usage: Basic Example
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
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.