3.4 KiB
server-m2
Purpose
Split-roundtrip helper that computes the server proof M2 = H(A | M1 | K) independently of serverFinishHandshake. Useful for transports that ack M1 over one HTTP request (server validates client proof, persists the session) and emit M2 from a different worker or route (perhaps a follow-up acknowledgement endpoint, or a fan-out broadcast confirming successful authentication). The second-half route does not need to redo the modPow + KDF chain; it just needs (A, M1, K) (which it can fetch from a session store) and this helper.
Features
- Single function call returning
H(A | M1 | K). - Byte-identical to the
M2field returned byserverFinishHandshakefor the same(A, M1, K)triple. - Deterministic — same inputs always produce the same bytes.
- Wraps the shared
computeM2helper inmath/proof.tsso the proof formula lives in exactly one place.
Usage: Basic Example
import { serverComputeM2 } from "@suaveplan/srp/server";
import { params2048Sha256 } from "@suaveplan/srp/params";
import { nobleHashBridge } from "@suaveplan/srp/hash";
const params = params2048Sha256(nobleHashBridge("SHA256"));
const A = new Uint8Array(256);
const M1 = new Uint8Array(32);
const K = new Uint8Array(32);
const M2 = serverComputeM2(A, M1, K, params);
console.log(M2.byteLength); // 32
Usage: Advanced Example
import { serverComputeM2, serverFinishHandshake } from "@suaveplan/srp/server";
import { clientFinishHandshake, clientStartHandshake } from "@suaveplan/srp/client";
import { serverStartHandshake } from "@suaveplan/srp/server";
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 rng = {
nextUint32: () => 0,
nextFloat: () => 0,
nextBytes: (n: number) => crypto.getRandomValues(new Uint8Array(n)),
};
const { v } = computeVerifier("alice", "password123", salt, params);
const { a, A } = clientStartHandshake(rng, params);
const { b, B } = serverStartHandshake(v, rng, params);
const client = clientFinishHandshake("alice", "password123", salt, A, B, a, params);
const inline = serverFinishHandshake("alice", salt, A, B, b, v, client.M1, params);
const split = serverComputeM2(A, client.M1, inline.K, params);
console.log(Buffer.from(inline.M2).equals(Buffer.from(split)));
API Reference
serverComputeM2(A, M1, K, params): Uint8Array
Returns H(A | M1 | K) of length params.hash.digestSize.
Implementation Notes
The split-roundtrip pattern matters operationally: many real deployments separate the "validate client proof" step (which needs the full SRP state machine — v, b, B) from the "ack mutual authentication" step (which only needs A, M1, and the session key K). The first step can run on a stateful auth node with the verifier DB; the second can run on any edge that has been handed the session-key blob. serverComputeM2 is the API surface that supports that split without forcing the second step to re-derive S and K.
The function is intentionally trivial — a single forward to computeM2. The wrapper exists for API stability: if RFC 2945 ever extends M2 (e.g. adds a session-binding field), this entry point is where the change lands, and serverFinishHandshake is updated to call into the same helper.