first commit
This commit is contained in:
74
docs/security-stack/security/srp/src/server/server-start.md
Normal file
74
docs/security-stack/security/srp/src/server/server-start.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# `server-start`
|
||||
|
||||
## Purpose
|
||||
|
||||
Begin the SRP-6a handshake on the server side. Loads the stored verifier `v` (alongside the user's `(I, salt)`), draws a `≥ 256`-bit private exponent `b`, and emits the server public value `B = (k * v + g^b) mod N` per RFC 5054 §2.5.4. The augmented form (multiplying `k` into the verifier) is what distinguishes SRP-6a from SRP-6. The function re-draws `b` if the resulting `B mod N == 0`, up to 16 attempts.
|
||||
|
||||
## Features
|
||||
|
||||
- 256-bit private exponent — matches RFC 5054 §2.5.4 minimum.
|
||||
- `k = H(N | PAD(g))` precomputed once per call (could be memoized externally for further savings).
|
||||
- `B ≡ 0 mod N` re-draw with 16-attempt safety cap.
|
||||
- Returns `B` zero-padded to `byteLength(N)`.
|
||||
- Emits `srp.handshake_duration_ms{role=server,phase=start}` and on abort `srp.outcome_total{role=server,outcome=public_key_invalid}` + `srp.errors_total{code=SRP.PUBLIC_KEY_INVALID}`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { serverStartHandshake } from "@suaveplan/srp/server";
|
||||
import { params2048Sha256 } from "@suaveplan/srp/params";
|
||||
import { nobleHashBridge } from "@suaveplan/srp/hash";
|
||||
import { computeVerifier } from "@suaveplan/srp/verifier";
|
||||
|
||||
const params = params2048Sha256(nobleHashBridge("SHA256"));
|
||||
const salt = new Uint8Array(16);
|
||||
const { v } = computeVerifier("alice", "password123", salt, params);
|
||||
const rng = {
|
||||
nextUint32: () => 0,
|
||||
nextFloat: () => 0,
|
||||
nextBytes: (n: number) => crypto.getRandomValues(new Uint8Array(n)),
|
||||
};
|
||||
const { b, B } = serverStartHandshake(v, rng, params);
|
||||
console.log(b.byteLength, B.byteLength); // 32 256
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { serverStartHandshake } from "@suaveplan/srp/server";
|
||||
import { params2048Sha256 } from "@suaveplan/srp/params";
|
||||
import { nobleHashBridge } from "@suaveplan/srp/hash";
|
||||
import { computeVerifier } from "@suaveplan/srp/verifier";
|
||||
import type { Rng } from "@suaveplan/types";
|
||||
|
||||
const params = params2048Sha256(nobleHashBridge("SHA256"));
|
||||
const salt = new Uint8Array(16);
|
||||
const { v } = computeVerifier("alice", "password123", salt, params);
|
||||
|
||||
// Synthetic deterministic RNG to demonstrate reproducibility.
|
||||
function seededRng(seed: number): Rng {
|
||||
let state = seed >>> 0;
|
||||
return {
|
||||
nextUint32: () => {
|
||||
state = (state * 1664525 + 1013904223) >>> 0;
|
||||
return state;
|
||||
},
|
||||
nextFloat: () => 0.5,
|
||||
};
|
||||
}
|
||||
const r1 = serverStartHandshake(v, seededRng(42), params);
|
||||
const r2 = serverStartHandshake(v, seededRng(42), params);
|
||||
console.log(Buffer.from(r1.B).equals(Buffer.from(r2.B))); // true
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `serverStartHandshake(v, rng, params): { b, B }`
|
||||
|
||||
Returns `{ b, B }`. `b` is 32 bytes of fresh entropy from `rng`; `B` is `params.N.byteLength` bytes, zero-padded.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`kvModN = (k * v) mod N` is computed once at the top of the function and reused inside the re-draw loop because it does not depend on `b`. The only loop-variant work is `g^b mod N` (the `modPow` call) and the final `((kvModN + gb) mod N)`. This matters because the dominant cost is `modPow`; pulling `k*v` out of the loop saves wall time on the (vanishingly unlikely) re-draw path.
|
||||
|
||||
The re-draw cap is the same `16` as the client side. Per RFC 5054 §2.5.4 the requirement is "MUST abort if `B ≡ 0 mod N`"; the cap converts that into a finite-bound failure rather than an unbounded loop on a pathological RNG.
|
||||
Reference in New Issue
Block a user