# `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 `M2` field returned by `serverFinishHandshake` for the same `(A, M1, K)` triple. - Deterministic — same inputs always produce the same bytes. - Wraps the shared `computeM2` helper in `math/proof.ts` so the proof formula lives in exactly one place. ## Usage: Basic Example ```ts 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 ```ts 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.