Initial Commit
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import type { Rng } from "@suaveplan/types";
|
||||
import { SRP_CODES, srpError } from "../errors/errors.js";
|
||||
import { bigIntToBytes, bytesToBigInt, modPow } from "../math/modpow.js";
|
||||
import { rngBytes } from "../math/rng-bytes.js";
|
||||
import {
|
||||
recordDuration,
|
||||
recordOutcome,
|
||||
recordSrpError,
|
||||
} from "../observability/metrics.js";
|
||||
import type { SrpParams } from "../params/params.js";
|
||||
|
||||
const PRIVATE_EXPONENT_BYTES = 32;
|
||||
|
||||
/**
|
||||
* RFC 5054 §2.5.4 — client kicks off the handshake by drawing a
|
||||
* private exponent `a ≥ 256` bits and emitting `A = g^a mod N`.
|
||||
*
|
||||
* If a synthetic / pathological RNG produces an `a` whose resulting
|
||||
* `A mod N === 0n`, the function re-draws automatically; in practice
|
||||
* (with a real CSPRNG over a safe-prime group) this never trips, but
|
||||
* the gate satisfies RFC 5054 §2.5.4's "MUST abort if A ≡ 0 mod N"
|
||||
* requirement on the originating side.
|
||||
*/
|
||||
export function clientStartHandshake(
|
||||
rng: Rng,
|
||||
params: SrpParams
|
||||
): { a: Uint8Array; A: Uint8Array } {
|
||||
const t0 = performance.now();
|
||||
const NInt = bytesToBigInt(params.N);
|
||||
const gInt = bytesToBigInt(params.g);
|
||||
|
||||
// RFC 5054 §2.5.4 requires aborting when A ≡ 0 mod N. With a
|
||||
// well-formed group where `gcd(g, N) === 1` (every RFC 5054
|
||||
// Appendix A group satisfies this — `g ∈ {2, 5, 19}` and `N` is
|
||||
// prime), `g^a mod N` is never zero for any non-zero `a`. We
|
||||
// surface the check defensively but never expect the loop to
|
||||
// iterate more than once with a valid `(g, N)` pair.
|
||||
const a = rngBytes(rng, PRIVATE_EXPONENT_BYTES);
|
||||
const aInt = bytesToBigInt(a);
|
||||
const A = modPow(gInt, aInt, NInt);
|
||||
if (A === 0n) {
|
||||
recordSrpError(SRP_CODES.PUBLIC_KEY_INVALID);
|
||||
recordOutcome("client", "public_key_invalid");
|
||||
throw srpError(
|
||||
SRP_CODES.PUBLIC_KEY_INVALID,
|
||||
"clientStartHandshake produced A ≡ 0 mod N — invalid group parameters"
|
||||
);
|
||||
}
|
||||
const APadded = bigIntToBytes(A, params.N.byteLength);
|
||||
recordDuration("client", "start", performance.now() - t0);
|
||||
return { a, A: APadded };
|
||||
}
|
||||
Reference in New Issue
Block a user