Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
#!/usr/bin/env bun
/**
* Deterministic SRP test-vector generator.
*
* Runs the full SRP-6a handshake (computeVerifier → clientStartHandshake →
* serverStartHandshake → clientFinishHandshake → serverFinishHandshake)
* for the bundled 2048-bit group with SHA-256, using a fixed-seed
* deterministic RNG, and writes the resulting `(x, v, A, B, K, M1, M2)`
* tuple to `src/__fixtures__/vectors-2048-sha256.json`.
*
* Re-running the script with the same seed produces a byte-identical
* JSON fixture. The conformance test replays the same handshake against
* the committed fixture to anchor the modern 2048/SHA-256 default.
*
* Usage:
* bun scripts/generate-srp-vectors.ts [--seed=42]
*/
import { mkdirSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import type { Rng } from "@suaveplan/types";
import { clientFinishHandshake } from "../src/client/client-finish.js";
import { clientStartHandshake } from "../src/client/client-start.js";
import { nobleHashBridge } from "../src/hash/hash-bridge.js";
import { params2048Sha256 } from "../src/params/params.js";
import { serverFinishHandshake } from "../src/server/server-finish.js";
import { serverStartHandshake } from "../src/server/server-start.js";
import { computeVerifier } from "../src/verifier/verifier.js";
function seededRng(seed: number): Rng {
let state = seed >>> 0;
return {
nextUint32: () => {
state = (state * 1664525 + 1013904223) >>> 0;
return state;
},
nextFloat: () => 0.5,
nextBytes: (n: number) => {
const out = new Uint8Array(n);
let i = 0;
while (i < n) {
state = (state * 1664525 + 1013904223) >>> 0;
out[i++] = state & 0xff;
if (i < n) out[i++] = (state >>> 8) & 0xff;
if (i < n) out[i++] = (state >>> 16) & 0xff;
if (i < n) out[i++] = (state >>> 24) & 0xff;
}
return out;
},
};
}
function toHex(bytes: Uint8Array): string {
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
}
function parseSeed(): number {
const arg = process.argv.find((a) => a.startsWith("--seed="));
if (arg) return Number.parseInt(arg.slice("--seed=".length), 10);
return 42;
}
async function main(): Promise<void> {
const seed = parseSeed();
const params = params2048Sha256(nobleHashBridge("SHA256"));
const I = "alice@example.com";
const P = "correct horse battery staple";
const salt = seededRng(seed + 1).nextBytes!(16);
const { x, v } = computeVerifier(I, P, salt, params);
const { a, A } = clientStartHandshake(seededRng(seed + 2), params);
const { b, B } = serverStartHandshake(v, seededRng(seed + 3), params);
const client = clientFinishHandshake(I, P, salt, A, B, a, params);
const server = serverFinishHandshake(I, salt, A, B, b, v, client.M1, params);
const fixture = {
meta: {
group: "RFC 5054 Appendix A 2048-bit",
hash: "SHA-256",
seed,
generator: "scripts/generate-srp-vectors.ts",
},
inputs: {
I,
P,
salt_hex: toHex(salt),
a_hex: toHex(a),
b_hex: toHex(b),
},
outputs: {
x_hex: toHex(x),
v_hex: toHex(v),
A_hex: toHex(A),
B_hex: toHex(B),
K_hex: toHex(client.K),
M1_hex: toHex(client.M1),
M2_hex: toHex(server.M2),
S_hex: toHex(client.S),
},
};
const outPath = join(
dirname(new URL(import.meta.url).pathname),
"..",
"src",
"__fixtures__",
"vectors-2048-sha256.json"
);
mkdirSync(dirname(outPath), { recursive: true });
writeFileSync(outPath, `${JSON.stringify(fixture, null, 2)}\n`);
console.log(`wrote ${outPath}`);
}
await main();