# @suaveplan/srp - SRP-6a zero-knowledge password proof (RFC 5054 / RFC 2945) — universal client+server roles, bundled 2048-bit safe-prime group, optional 1024/1536/3072/4096/6144/8192 groups under subpaths, augmented PAKE bridge for future OPAQUE drop-in. ## Summary SRP-6a zero-knowledge password proof per [RFC 5054](https://www.rfc-editor.org/rfc/rfc5054) / [RFC 2945](https://www.rfc-editor.org/rfc/rfc2945). Universal — client and server roles in one package, bundled 2048-bit safe-prime group, optional 1024 / 1536 / 3072 / 4096 / 6144 / 8192-bit groups under tree-shakeable subpaths, augmented-PAKE bridge (`PakeProvider`) for future OPAQUE drop-in, dual sync / async hash backends. ## Documentation tree - [client-finish](./src/client/client-finish.md) — Complete the SRP-6a handshake on the client side after the server has emitted its public value `B`. Derives the shared… - [client-start](./src/client/client-start.md) — Kick off the SRP-6a handshake on the client side. Draws a private exponent `a ≥ 256` bits from an injectable `Rng` and… - [client-verify](./src/client/client-verify.md) — Verify the server's proof `M2 = H(A | M1 | K)` per RFC 2945 §3 on the client side. After the client emits `M1` and the… - [errors](./src/errors/errors.md) — _(no submodules)_ - [errors](./src/errors/errors.md) — _(no submodules)_ - [groups](./src/groups/groups-2048.md) — _(no submodules)_ - groups-1024 — _(no submodul… - [1024](./src/groups/1024/groups-1024.md) — _(no submodules)_ - [groups-1024](./src/groups/1024/groups-1024.md) — _(no submodules)_ - [1536](./src/groups/1536/groups-1536.md) — _(no submodules)_ - [groups-1536](./src/groups/1536/groups-1536.md) — _(no submodules)_ - [3072](./src/groups/3072/groups-3072.md) — _(no submodules)_ - [groups-3072](./src/groups/3072/groups-3072.md) — _(no submodules)_ - [4096](./src/groups/4096/groups-4096.md) — _(no submodules)_ - [groups-4096](./src/groups/4096/groups-4096.md) — _(no submodules)_ - [6144](./src/groups/6144/groups-6144.md) — _(no submodules)_ - [groups-6144](./src/groups/6144/groups-6144.md) — _(no submodules)_ - [8192](./src/groups/8192/groups-8192.md) — _(no submodules)_ - [groups-8192](./src/groups/8192/groups-8192.md) — _(no submodules)_ - [groups-2048](./src/groups/groups-2048.md) — _(no submodules)_ - groups-1024 — _(no submodul… - [hash](./src/hash/hash-bridge.md) — _(no submodules)_ - [hash-bridge](./src/hash/hash-bridge.md) — _(no submodules)_ - [k](./src/math/k.md) — Compute the SRP-6a multiplier parameter `k = H(N | PAD(g))` per RFC 5054 §2.5.3. The multiplier is the structural chang… - [modpow](./src/math/modpow.md) — Modular exponentiation primitive for `@suaveplan/srp`. This is a thin wrapper over `@noble/curves`'s hardened `pow(num,… - [pad](./src/math/pad.md) — The `PAD(x, N)` helper from RFC 5054 §2.5.3. Returns a left-zero-extended copy of `x` whose length equals `N.byteLength… - [proof](./src/math/proof.md) — Internal helpers for computing the SRP-6a / RFC 2945 §3 mutual-authentication proofs `M1` and `M2`. Both client and ser… - [rng-bytes](./src/math/rng-bytes.md) — Adapter helper that draws `n` bytes from any `Rng` implementing the `@suaveplan/types` contract. The `Rng` interface ma… - [u](./src/math/u.md) — Compute the SRP-6a scrambling value `u = H(PAD(A) | PAD(B))` per RFC 5054 §2.5.4. `u` mixes the two ephemeral public va… - [x](./src/math/x.md) — Compute the SRP-6a private exponent `x = H(salt | H(I | ":" | P))` per RFC 5054 §2.6. `x` ties username + password + sa… - [observability](./src/observability/metrics.md) — _(no submodules)_ - [metrics](./src/observability/metrics.md) — _(no submodules)_ - [pake](./src/pake/pake-provider.md) — _(no submodules)_ - [pake-provider](./src/pake/pake-provider.md) — _(no submodules)_ - [params](./src/params/params.md) — _(no submodules)_ - [params](./src/params/params.md) — _(no submodules)_ - [schemas](./src/schemas/schemas.md) — _(no submodules)_ - [schemas](./src/schemas/schemas.md) — _(no submodules)_ - [server-finish](./src/server/server-finish.md) — Complete the SRP-6a handshake on the server side after receiving the client's public value `A` and proof `M1`. Validate… - [server-m2](./src/server/server-m2.md) — Split-roundtrip helper that computes the server proof `M2 = H(A | M1 | K)` independently of `serverFinishHandshake`. Us… - [server-start](./src/server/server-start.md) — Begin the SRP-6a handshake on the server side. Loads the stored verifier `v` (alongside the user's `(I, salt)`), draws… - [verifier](./src/verifier/verifier.md) — _(no submodules)_ - [verifier](./src/verifier/verifier.md) — _(no submodules)_ - [errors](./src/errors/errors.md) — _(no submodules)_- [groups](./src/groups/groups-2048.md) — _(no submodules)_- [1024](./src/groups/1024/groups-1024.md) — _(no submodules)_- [groups-1024](./src/groups/1024/groups-1024.md) — _(no submodules)_- [1536](./src/groups/1536/groups-1536.md) — _(no submodules)_- [groups-1536](./src/groups/1536/groups-1536.md) — _(no submodules)_- [3072](./src/groups/3072/groups-3072.md) — _(no submodules)_- [groups-3072](./src/groups/3072/groups-3072.md) — _(no submodules)_- [4096](./src/groups/4096/groups-4096.md) — _(no submodules)_- [groups-4096](./src/groups/4096/groups-4096.md) — _(no submodules)_- [6144](./src/groups/6144/groups-6144.md) — _(no submodules)_- [groups-6144](./src/groups/6144/groups-6144.md) — _(no submodules)_- [8192](./src/groups/8192/groups-8192.md) — _(no submodules)_- [groups-8192](./src/groups/8192/groups-8192.md) — _(no submodules)_- [groups-2048](./src/groups/groups-2048.md) — _(no submodules)_- [hash](./src/hash/hash-bridge.md) — _(no submodules)_- [hash-bridge](./src/hash/hash-bridge.md) — _(no submodules)_- [k](./src/math/k.md) — Compute the SRP-6a multiplier parameter `k = H(N | PAD(g))` per RFC 5054 §2.5.3. The multiplier is the structural chang… - [modpow](./src/math/modpow.md) — Modular exponentiation primitive for `@suaveplan/srp`. This is a thin wrapper over `@noble/curves`'s hardened `pow(num,… - [pad](./src/math/pad.md) — The `PAD(x, N)` helper from RFC 5054 §2.5.3. Returns a left-zero-extended copy of `x` whose length equals `N.byteLength… - [proof](./src/math/proof.md) — Internal helpers for computing the SRP-6a / RFC 2945 §3 mutual-authentication proofs `M1` and `M2`. Both client and ser… - [rng-bytes](./src/math/rng-bytes.md) — Adapter helper that draws `n` bytes from any `Rng` implementing the `@suaveplan/types` contract. The `Rng` interface ma… - [u](./src/math/u.md) — Compute the SRP-6a scrambling value `u = H(PAD(A) | PAD(B))` per RFC 5054 §2.5.4. `u` mixes the two ephemeral public va… - [x](./src/math/x.md) — Compute the SRP-6a private exponent `x = H(salt | H(I | ":" | P))` per RFC 5054 §2.6. `x` ties username + password + sa… - [observability](./src/observability/metrics.md) — _(no submodules)_- [metrics](./src/observability/metrics.md) — _(no submodules)_- [pake](./src/pake/pake-provider.md) — _(no submodules)_- [pake-provider](./src/pake/pake-provider.md) — _(no submodules)_- [params](./src/params/params.md) — _(no submodules)_- [params](./src/params/params.md) — _(no submodules)_- [schemas](./src/schemas/schemas.md) — _(no submodules)_- [schemas](./src/schemas/schemas.md) — _(no submodules)_- [server-finish](./src/server/server-finish.md) — Complete the SRP-6a handshake on the server side after receiving the client's public value `A` and proof `M1`. Validate… - [server-m2](./src/server/server-m2.md) — Split-roundtrip helper that computes the server proof `M2 = H(A | M1 | K)` independently of `serverFinishHandshake`. Us… - [server-start](./src/server/server-start.md) — Begin the SRP-6a handshake on the server side. Loads the stored verifier `v` (alongside the user's `(I, salt)`), draws… - [verifier](./src/verifier/verifier.md) — _(no submodules)_- [verifier](./src/verifier/verifier.md) — _(no submodules)_- [errors](./src/errors/errors.md) — _(no submodules)_- [groups](./src/groups/groups-2048.md) — _(no submodules)_- groups-1024 — _(no submodules)_- [groups-1024](./src/groups/1024/groups-1024.md) — _(no submodules)_- [1536](./src/groups/1536/groups-1536.md) — _(no submodules)_- [groups-1536](./src/groups/1536/groups-1536.md) — _(no submodules)_- [3072](./src/groups/3072/groups-3072.md) — _(no submodules)_- [groups-3072](./src/groups/3072/groups-3072.md) — _(no submodules)_- [4096](./src/groups/4096/groups-4096.md) — _(no submodules)_- [groups-4096](./src/groups/4096/groups-4096.md) — _(no submodules)_- [6144](./src/groups/6144/groups-6144.md) — _(no submodules)_- [groups-6144](./src/groups/6144/groups-6144.md) — _(no submodules)_- [8192](./src/groups/8192/groups-8192.md) — _(no submodules)_- [groups-8192](./src/groups/8192/groups-8192.md) — _(no submodules)_- [groups-2048](./src/groups/groups-2048.md) — _(no submodules)_- groups-1024 — _(no submodules)_- [hash-bridge](./src/hash/hash-bridge.md) — _(no submodules)_- [k](./src/math/k.md) — Compute the SRP-6a multiplier parameter `k = H(N | PAD(g))` per RFC 5054 §2.5.3. The multiplier is the structural chang… - [modpow](./src/math/modpow.md) — Modular exponentiation primitive for `@suaveplan/srp`. This is a thin wrapper over `@noble/curves`'s hardened `pow(num,… - [pad](./src/math/pad.md) — The `PAD(x, N)` helper from RFC 5054 §2.5.3. Returns a left-zero-extended copy of `x` whose length equals `N.byteLength… - [proof](./src/math/proof.md) — Internal helpers for computing the SRP-6a / RFC 2945 §3 mutual-authentication proofs `M1` and `M2`. Both client and ser… - [rng-bytes](./src/math/rng-bytes.md) — Adapter helper that draws `n` bytes from any `Rng` implementing the `@suaveplan/types` contract. The `Rng` interface ma… - [u](./src/math/u.md) — Compute the SRP-6a scrambling value `u = H(PAD(A) | PAD(B))` per RFC 5054 §2.5.4. `u` mixes the two ephemeral public va… - [x](./src/math/x.md) — Compute the SRP-6a private exponent `x = H(salt | H(I | ":" | P))` per RFC 5054 §2.6. `x` ties username + password + sa… - [observability](./src/observability/metrics.md) — _(no submodules)_- [metrics](./src/observability/metrics.md) — _(no submodules)_- [pake](./src/pake/pake-provider.md) — _(no submodules)_- [pake-provider](./src/pake/pake-provider.md) — _(no submodules)_- [params](./src/params/params.md) — _(no submodules)_- [params](./src/params/params.md) — _(no submodules)_- [schemas](./src/schemas/schemas.md) — _(no submodules)_- [schemas](./src/schemas/schemas.md) — _(no submodules)_- [server-finish](./src/server/server-finish.md) — Complete the SRP-6a handshake on the server side after receiving the client's public value `A` and proof `M1`. Validate… - [server-m2](./src/server/server-m2.md) — Split-roundtrip helper that computes the server proof `M2 = H(A | M1 | K)` independently of `serverFinishHandshake`. Us… - [server-start](./src/server/server-start.md) — Begin the SRP-6a handshake on the server side. Loads the stored verifier `v` (alongside the user's `(I, salt)`), draws… - [verifier](./src/verifier/verifier.md) — _(no submodules)_- [verifier](./src/verifier/verifier.md) — _(no submodules)_- [errors](./src/errors/errors.md) — _(no submodules)_- [groups](./src/groups/groups-2048.md) — _(no submodules)_- groups-1024 — _(no submodu… - [1024](./src/groups/1024/groups-1024.md) — _(no submodules)_- [groups-1024](./src/groups/1024/groups-1024.md) — _(no submodules)_- [1536](./src/groups/1536/groups-1536.md) — _(no submodules)_- [groups-1536](./src/groups/1536/groups-1536.md) — _(no submodules)_- [3072](./src/groups/3072/groups-3072.md) — _(no submodules)_- [groups-3072](./src/groups/3072/groups-3072.md) — _(no submodules)_- [4096](./src/groups/4096/groups-4096.md) — _(no submodules)_- [groups-4096](./src/groups/4096/groups-4096.md) — _(no submodules)_- [6144](./src/groups/6144/groups-6144.md) — _(no submodules)_- [groups-6144](./src/groups/6144/groups-6144.md) — _(no submodules)_- [8192](./src/groups/8192/groups-8192.md) — _(no submodules)_- [groups-8192](./src/groups/8192/groups-8192.md) — _(no submodules)_- [groups-2048](./src/groups/groups-2048.md) — _(no submodules)_- groups-1024 — _(no submodu… - [hash](./src/hash/hash-bridge.md) — _(no submodules)_- [hash-bridge](./src/hash/hash-bridge.md) — _(no submodules)_- [k](./src/math/k.md) — Compute the SRP-6a multiplier parameter `k = H(N | PAD(g))` per RFC 5054 §2.5.3. The multiplier is the structural chang… - [modpow](./src/math/modpow.md) — Modular exponentiation primitive for `@suaveplan/srp`. This is a thin wrapper over `@noble/curves`'s hardened `pow(num,… - [pad](./src/math/pad.md) — The `PAD(x, N)` helper from RFC 5054 §2.5.3. Returns a left-zero-extended copy of `x` whose length equals `N.byteLength… - [proof](./src/math/proof.md) — Internal helpers for computing the SRP-6a / RFC 2945 §3 mutual-authentication proofs `M1` and `M2`. Both client and ser… - [rng-bytes](./src/math/rng-bytes.md) — Adapter helper that draws `n` bytes from any `Rng` implementing the `@suaveplan/types` contract. The `Rng` interface ma… - [u](./src/math/u.md) — Compute the SRP-6a scrambling value `u = H(PAD(A) | PAD(B))` per RFC 5054 §2.5.4. `u` mixes the two ephemeral public va… - [x](./src/math/x.md) — Compute the SRP-6a private exponent `x = H(salt | H(I | ":" | P))` per RFC 5054 §2.6. `x` ties username + password + sa… - [observability](./src/observability/metrics.md) — _(no submodules)_- [metrics](./src/observability/metrics.md) — _(no submodules)_- [pake](./src/pake/pake-provider.md) — _(no submodules)_- [pake-provider](./src/pake/pake-provider.md) — _(no submodules)_- [params](./src/params/params.md) — _(no submodules)_- [params](./src/params/params.md) — _(no submodules)_- [schemas](./src/schemas/schemas.md) — _(no submodules)_- [schemas](./src/schemas/schemas.md) — _(no submodules)_- [server-finish](./src/server/server-finish.md) — Complete the SRP-6a handshake on the server side after receiving the client's public value `A` and proof `M1`. Validate… - [server-m2](./src/server/server-m2.md) — Split-roundtrip helper that computes the server proof `M2 = H(A | M1 | K)` independently of `serverFinishHandshake`. Us… - [server-start](./src/server/server-start.md) — Begin the SRP-6a handshake on the server side. Loads the stored verifier `v` (alongside the user's `(I, salt)`), draws… - [verifier](./src/verifier/verifier.md) — _(no submodules)_- [verifier](./src/verifier/verifier.md) — _(no submodules)_- [errors](./src/errors/errors.md) — _(no submodules)_- [groups](./src/groups/groups-2048.md) — _(no submodules)_ - [1024](./src/groups/1024/groups-1024.md) — _(no submodules)_ - [groups-1024](./src/groups/1024/groups-1024.md) — _(no submodules)_ - [1536](./src/groups/1536/groups-1536.md) — _(no submodules)_ - [groups-1536](./src/groups/1536/groups-1536.md) — _(no submodules)_ - [3072](./src/groups/3072/groups-3072.md) — _(no submodules)_ - [groups-3072](./src/groups/3072/groups-3072.md) — _(no submodules)_ - [4096](./src/groups/4096/groups-4096.md) — _(no submodules)_ - [groups-4096](./src/groups/4096/groups-4096.md) — _(no submodules)_ - [6144](./src/groups/6144/groups-6144.md) — _(no submodules)_ - [groups-6144](./src/groups/6144/groups-6144.md) — _(no submodules)_ - [8192](./src/groups/8192/groups-8192.md) — _(no submodules)_ - [groups-8192](./src/groups/8192/groups-8192.md) — _(no submodules)_ - [groups-2048](./src/groups/groups-2048.md) — _(no submodules)_- [hash](./src/hash/hash-bridge.md) — _(no submodules)_ - [hash-bridge](./src/hash/hash-bridge.md) — _(no submodules)_- [k](./src/math/k.md) — Compute the SRP-6a multiplier parameter `k = H(N | PAD(g))` per RFC 5054 §2.5.3. The multiplier is the structural chang… - [modpow](./src/math/modpow.md) — Modular exponentiation primitive for `@suaveplan/srp`. This is a thin wrapper over `@noble/curves`'s hardened `pow(num,… - [pad](./src/math/pad.md) — The `PAD(x, N)` helper from RFC 5054 §2.5.3. Returns a left-zero-extended copy of `x` whose length equals `N.byteLength… - [proof](./src/math/proof.md) — Internal helpers for computing the SRP-6a / RFC 2945 §3 mutual-authentication proofs `M1` and `M2`. Both client and ser… - [rng-bytes](./src/math/rng-bytes.md) — Adapter helper that draws `n` bytes from any `Rng` implementing the `@suaveplan/types` contract. The `Rng` interface ma… - [u](./src/math/u.md) — Compute the SRP-6a scrambling value `u = H(PAD(A) | PAD(B))` per RFC 5054 §2.5.4. `u` mixes the two ephemeral public va… - [x](./src/math/x.md) — Compute the SRP-6a private exponent `x = H(salt | H(I | ":" | P))` per RFC 5054 §2.6. `x` ties username + password + sa… - [observability](./src/observability/metrics.md) — _(no submodules)_ - [metrics](./src/observability/metrics.md) — _(no submodules)_- [pake](./src/pake/pake-provider.md) — _(no submodules)_ - [pake-provider](./src/pake/pake-provider.md) — _(no submodules)_- [params](./src/params/params.md) — _(no submodules)_ - [params](./src/params/params.md) — _(no submodules)_- [schemas](./src/schemas/schemas.md) — _(no submodules)_ - [schemas](./src/schemas/schemas.md) — _(no submodules)_- [server-finish](./src/server/server-finish.md) — Complete the SRP-6a handshake on the server side after receiving the client's public value `A` and proof `M1`. Validate… - [server-m2](./src/server/server-m2.md) — Split-roundtrip helper that computes the server proof `M2 = H(A | M1 | K)` independently of `serverFinishHandshake`. Us… - [server-start](./src/server/server-start.md) — Begin the SRP-6a handshake on the server side. Loads the stored verifier `v` (alongside the user's `(I, salt)`), draws… - [verifier](./src/verifier/verifier.md) — _(no submodules)_ - [verifier](./src/verifier/verifier.md) — _(no submodules)_ ## Install ```bash bun add @suaveplan/srp ``` ## Quick start ```ts import { computeVerifier } from "@suaveplan/srp/verifier"; import { clientStartHandshake, clientFinishHandshake } from "@suaveplan/srp/client"; import { serverStartHandshake, serverFinishHandshake } from "@suaveplan/srp/server"; import { params2048Sha256 } from "@suaveplan/srp/params"; import { nobleHashBridge } from "@suaveplan/srp/hash"; const params = params2048Sha256(nobleHashBridge("SHA256")); const salt = crypto.getRandomValues(new Uint8Array(16)); const rng = { nextUint32: () => { const buf = new Uint32Array(1); crypto.getRandomValues(buf); return buf[0]!; }, nextFloat: () => 0.5, nextBytes: (n: number) => crypto.getRandomValues(new Uint8Array(n)), }; // Registration — client computes the verifier and ships { I, salt, v }. const { v } = computeVerifier("alice", "password123", salt, params); const serverRecord = { I: "alice", salt, v }; // Handshake — client and server exchange (A, B) then (M1, M2). const { a, A } = clientStartHandshake(rng, params); const { b, B } = serverStartHandshake(serverRecord.v, rng, params); const client = clientFinishHandshake("alice", "password123", salt, A, B, a, params); const server = serverFinishHandshake( "alice", salt, A, B, b, serverRecord.v, client.M1, params ); // client.K === server.K; both sides have authenticated each other. ``` ## Modules | Module | Subpath | Description | | ------------------------------------------------------- | ---------------------------------- | -------------------------------------------------------- | | [groups/groups-2048](src/groups/groups-2048.md) | `@suaveplan/srp/groups` | Default 2048-bit safe-prime group constants | | [groups/1024/groups-1024](src/groups/1024/groups-1024.md) | `@suaveplan/srp/groups/1024` | RFC 5054 Appendix A 1024-bit group (legacy / vectors) | | [groups/1536/groups-1536](src/groups/1536/groups-1536.md) | `@suaveplan/srp/groups/1536` | RFC 5054 Appendix A 1536-bit group | | [groups/3072/groups-3072](src/groups/3072/groups-3072.md) | `@suaveplan/srp/groups/3072` | RFC 5054 Appendix A 3072-bit group | | [groups/4096/groups-4096](src/groups/4096/groups-4096.md) | `@suaveplan/srp/groups/4096` | RFC 5054 Appendix A 4096-bit group | | [groups/6144/groups-6144](src/groups/6144/groups-6144.md) | `@suaveplan/srp/groups/6144` | RFC 5054 Appendix A 6144-bit group | | [groups/8192/groups-8192](src/groups/8192/groups-8192.md) | `@suaveplan/srp/groups/8192` | RFC 5054 Appendix A 8192-bit group | | [params/params](src/params/params.md) | `@suaveplan/srp/params` | `SrpParams` + factory + validator | | [math/modpow](src/math/modpow.md) | (root) | `modPow` + `bigIntToBytes` / `bytesToBigInt` | | [math/pad](src/math/pad.md) | (root) | RFC 5054 §2.5.3 `PAD` helper | | [math/k](src/math/k.md) | (root) | `computeK` — multiplier parameter | | [math/u](src/math/u.md) | (root) | `computeU` — scrambling value | | [math/x](src/math/x.md) | (root) | `computeX` — private exponent derivation | | [math/proof](src/math/proof.md) | (root) | `computeM1` / `computeM2` proof formulae | | [math/rng-bytes](src/math/rng-bytes.md) | (root) | `Rng` → `Uint8Array` adapter | | [verifier/verifier](src/verifier/verifier.md) | `@suaveplan/srp/verifier` | `computeVerifier` registration helper | | [client/client-start](src/client/client-start.md) | `@suaveplan/srp/client` | `clientStartHandshake` | | [client/client-finish](src/client/client-finish.md) | `@suaveplan/srp/client` | `clientFinishHandshake` | | [client/client-verify](src/client/client-verify.md) | `@suaveplan/srp/client` | `clientVerifyServerProof` | | [server/server-start](src/server/server-start.md) | `@suaveplan/srp/server` | `serverStartHandshake` | | [server/server-finish](src/server/server-finish.md) | `@suaveplan/srp/server` | `serverFinishHandshake` | | [server/server-m2](src/server/server-m2.md) | `@suaveplan/srp/server` | `serverComputeM2` split-roundtrip helper | | [pake/pake-provider](src/pake/pake-provider.md) | `@suaveplan/srp/pake` | Augmented-PAKE seam + SRP-6a impl | | [hash/hash-bridge](src/hash/hash-bridge.md) | `@suaveplan/srp/hash` | `nobleHashBridge` / `subtleHashBridge` / default | | [errors/errors](src/errors/errors.md) | `@suaveplan/srp/errors` | `SRP_CODES` + `SrpError` | | [observability/metrics](src/observability/metrics.md) | (root) | OTel `srp.handshake_duration_ms` + counters | | [schemas/schemas](src/schemas/schemas.md) | (root) | Zod boundary schemas | ## Conformance - RFC 5054 Appendix B 1024/SHA-1 vectors — every intermediate (`k`, `x`, `v`, `u`, `S`) byte-exact. - 2048/SHA-256 locally-derived fixture (`src/__fixtures__/vectors-2048-sha256.json`) — generated by `bun scripts/generate-srp-vectors.ts`, byte-stable across runs. - Property test — 1000 random `(I, P, salt)` triples through the full handshake; `client.K === server.K` on every run. - All MUST-abort gates from RFC 5054 §2.5.4 (`A ≡ 0`, `B ≡ 0`, `u ≡ 0`) wired and tested. - Constant-time compare on `M1`, `M2`, and `S` via `@suaveplan/crypto-utils.constantTimeEqual`. ## Test vector generator ```bash bun scripts/generate-srp-vectors.ts [--seed=42] ``` Regenerates `src/__fixtures__/vectors-2048-sha256.json` deterministically.