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

87
scripts/crypto-timing.ts Normal file
View File

@@ -0,0 +1,87 @@
/**
* Nightly dudect-style timing driver. For every native impl that is not a
* `PrimitiveNotImplemented` stub, sample its runtime across fixed vs. random
* inputs and flag t-statistics above the threshold as potential leaks.
*
* In Phase 0 every native is a stub — the driver probes each, catches
* `PrimitiveNotImplemented`, skips, and exits clean. The wiring fires the
* moment a real native lands.
*/
import { dudect } from "../universal/crypto/crypto-utils/src/testing/dudect/dudect.ts";
/** Stable code emitted by `PrimitiveNotImplemented`. */
const NOT_IMPLEMENTED_CODE = "CRYPTO.UTILS.PRIMITIVE_NOT_IMPLEMENTED";
const MEASUREMENTS = Number.parseInt(
process.env.CRYPTO_TIMING_MEASUREMENTS ?? "20000",
10
);
const T_THRESHOLD = Number.parseFloat(
process.env.CRYPTO_TIMING_T_THRESHOLD ?? "4.5"
);
function randomBytes(len: number): Uint8Array {
const buf = new Uint8Array(len);
crypto.getRandomValues(buf);
return buf;
}
async function probeAndRun(
id: string,
category: "hash",
sampleFixed: () => unknown,
sampleRandom: () => unknown,
pickFn: (mod: Record<string, unknown>) => (input: unknown) => unknown
): Promise<void> {
const nativePath = `../universal/crypto/crypto-${category}/src/primitives/${id}/impl/${id}.native.ts`;
const mod = (await import(nativePath)) as Record<string, unknown>;
const fn = pickFn(mod);
try {
fn(sampleFixed());
} catch (err) {
if (
typeof err === "object" &&
err !== null &&
(err as { code?: unknown }).code === NOT_IMPLEMENTED_CODE
) {
console.log(
`[timing] ${id}: native is PrimitiveNotImplemented, skipping`
);
return;
}
throw err;
}
const result = dudect({
fn,
sampleFixed,
sampleRandom,
measurements: MEASUREMENTS,
tThreshold: T_THRESHOLD,
});
console.log(
`[timing] ${id}: t=${result.tStatistic.toFixed(3)} (threshold ${result.tThreshold}) → ${
result.constantTime ? "OK" : "LEAK"
}`
);
if (!result.constantTime) process.exit(1);
}
const HASH_ONE_SHOT_IDS = ["sha256", "sha384", "sha512"] as const;
async function main(): Promise<void> {
for (const id of HASH_ONE_SHOT_IDS) {
await probeAndRun(
id,
"hash",
() => new Uint8Array(64), // class A: all zeros, fixed length
() => randomBytes(64), // class B: random bytes, same length
(mod) => mod[id] as (input: unknown) => unknown
);
}
console.log("[timing] done");
}
if (import.meta.main) {
await main();
}