Files
suaveplan/packages/security-stack/security/srp/src/pake/pake-provider.md
2026-05-27 23:47:53 +01:00

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.
  • 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

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.