Initial Commit
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Spec-conformance tests for `openspec/specs/crypto-curve/spec.md`.
|
||||
*
|
||||
* Pins structural scenarios that the per-primitive unit suites do not
|
||||
* cover directly: every curve has both `noble.ts` and `native.ts` impl
|
||||
* files, every `*.select.ts` carries the GENERATED banner, every
|
||||
* `*.native.ts` is currently a `PrimitiveNotImplemented` stub (the
|
||||
* runtime reflection of the dangerous-tier "no flip without audit"
|
||||
* gate), and every `*.native.ts` source contains no banned data-
|
||||
* dependent branch patterns on secret material.
|
||||
*/
|
||||
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
|
||||
const ROOT = join(__dirname, "..");
|
||||
const SRC = join(ROOT, "src");
|
||||
const PRIMITIVES = join(SRC, "primitives");
|
||||
|
||||
const CURVE_IDS = ["secp256k1", "ed25519", "x25519", "p256"] as const;
|
||||
|
||||
describe("crypto-curve — universal curve primitive surface", () => {
|
||||
it("every curve id has its primitive directory", { timeout: 5000 }, () => {
|
||||
for (const id of CURVE_IDS) {
|
||||
expect(existsSync(join(PRIMITIVES, id))).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("crypto-curve — per-primitive replaceability layout", () => {
|
||||
for (const id of CURVE_IDS) {
|
||||
it(`${id} has both noble.ts and native.ts impl files`, {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const noble = join(
|
||||
PRIMITIVES,
|
||||
id,
|
||||
"impl",
|
||||
`${id}.noble`,
|
||||
`${id}.noble.ts`
|
||||
);
|
||||
const native = join(
|
||||
PRIMITIVES,
|
||||
id,
|
||||
"impl",
|
||||
`${id}.native`,
|
||||
`${id}.native.ts`
|
||||
);
|
||||
expect(existsSync(noble)).toBe(true);
|
||||
expect(existsSync(native)).toBe(true);
|
||||
});
|
||||
|
||||
it(`${id}.select.ts carries the GENERATED banner`, {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const sel = join(PRIMITIVES, id, `${id}.select`, `${id}.select.ts`);
|
||||
expect(existsSync(sel)).toBe(true);
|
||||
const content = readFileSync(sel, "utf8");
|
||||
expect(content).toMatch(/GENERATED/);
|
||||
expect(content).toMatch(/DO NOT EDIT/);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe("crypto-curve — vendored noble source (Phase 0.5)", () => {
|
||||
it("is a Phase-0.5 forward gate; current state is pre-0.5 (noble dep + no vendored/)", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const pkg = JSON.parse(
|
||||
readFileSync(join(ROOT, "package.json"), "utf8")
|
||||
);
|
||||
const noblePresent =
|
||||
"@noble/curves" in (pkg.dependencies ?? {}) ||
|
||||
"@noble/curves" in (pkg.peerDependencies ?? {});
|
||||
const vendoredPresent = existsSync(join(SRC, "vendored"));
|
||||
const upstreamFile = existsSync(join(ROOT, "NOBLE_UPSTREAM.md"));
|
||||
// Either we are pre-0.5 (noble dep present, no vendored, no
|
||||
// upstream record) or post-0.5 (no noble dep, vendored
|
||||
// directory exists with NOBLE_UPSTREAM.md present). Mixed
|
||||
// states are forbidden.
|
||||
const pre05 = noblePresent && !vendoredPresent && !upstreamFile;
|
||||
const post05 = !noblePresent && vendoredPresent && upstreamFile;
|
||||
expect(pre05 || post05).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("crypto-curve — constant-time curve math (native stubs)", () => {
|
||||
function srcOf(id: (typeof CURVE_IDS)[number]): string {
|
||||
return readFileSync(
|
||||
join(PRIMITIVES, id, "impl", `${id}.native`, `${id}.native.ts`),
|
||||
"utf8"
|
||||
);
|
||||
}
|
||||
|
||||
for (const id of CURVE_IDS) {
|
||||
it(`${id}.native.ts is currently a PrimitiveNotImplemented stub`, {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
// While native impls are stubs, "no data-dependent branch on
|
||||
// private key material" is trivially satisfied. The flip-PR
|
||||
// gate (audit + ct-lint pass) is what re-checks the
|
||||
// invariant when the stub is replaced.
|
||||
const src = srcOf(id);
|
||||
expect(src).toMatch(/PrimitiveNotImplemented/);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe("crypto-curve — dangerous-tier flip gate (runtime reflection)", () => {
|
||||
it("every native is a stub today, so no curve has been flipped without an audit", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const offenders: string[] = [];
|
||||
for (const id of CURVE_IDS) {
|
||||
const native = readFileSync(
|
||||
join(PRIMITIVES, id, "impl", `${id}.native`, `${id}.native.ts`),
|
||||
"utf8"
|
||||
);
|
||||
if (!/PrimitiveNotImplemented/.test(native)) {
|
||||
offenders.push(id);
|
||||
}
|
||||
}
|
||||
// If a curve graduates from stub, this test will fail and
|
||||
// force the change to attach an audit attestation per the
|
||||
// CI-policy gate before re-baselining.
|
||||
expect(offenders).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
void readdirSync;
|
||||
void statSync;
|
||||
Reference in New Issue
Block a user