64 lines
2.4 KiB
TypeScript
64 lines
2.4 KiB
TypeScript
/**
|
|
* Signing seam — Ed25519 over sha256(manifest exact bytes).
|
|
* Pipeline produces signature.json; verification is Stage 7 (pure-JS verifier).
|
|
* This module provides a *test-only* signing interface using Node's Ed25519.
|
|
* Trace: ARCH 10.5, SPIKE-04 F-5, IMPLEMENTATION-CONTRACT.md §11 — Do NOT invent production key custody
|
|
*/
|
|
import { createHash, generateKeyPairSync, sign } from "node:crypto";
|
|
import { sha256Hex } from "./hash.js";
|
|
import type { PackageSignature } from "../src/data/festival-package/types.js";
|
|
|
|
export interface KeyPair {
|
|
readonly publicKeyDerBase64: string; // SPKI DER base64 for fingerprint derivation
|
|
readonly privateKeyPem: string; // PKCS8 PEM
|
|
readonly publicKeyPem: string;
|
|
readonly fingerprint: string; // "sha256:<hex of SPKI DER>"
|
|
}
|
|
|
|
/**
|
|
* Generate a fresh Ed25519 key pair for tests. Not production key custody.
|
|
*/
|
|
export function generateTestKeyPair(): KeyPair {
|
|
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
|
|
const pubDer = publicKey.export({ format: "der", type: "spki" });
|
|
const pubPem = publicKey.export({ format: "pem", type: "spki" }).toString();
|
|
const privPem = privateKey.export({ format: "pem", type: "pkcs8" }).toString();
|
|
const fpHex = sha256Hex(pubDer);
|
|
return {
|
|
publicKeyDerBase64: pubDer.toString("base64"),
|
|
privateKeyPem: privPem as unknown as string,
|
|
publicKeyPem: pubPem as unknown as string,
|
|
fingerprint: `sha256:${fpHex}`,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Import private key PEM and sign manifest bytes (exact canonical bytes).
|
|
*/
|
|
export function signManifest(
|
|
manifestBytes: Uint8Array,
|
|
privateKeyPem: string,
|
|
publicKeyFingerprint: string,
|
|
): PackageSignature {
|
|
const manifestDigest = createHash("sha256").update(manifestBytes).digest();
|
|
const sig = sign(null, manifestDigest, { key: privateKeyPem, format: "pem", type: "pkcs8" });
|
|
const sigB64 = sig.toString("base64");
|
|
const manifestSha256 = sha256Hex(manifestBytes);
|
|
return {
|
|
algorithm: "ed25519",
|
|
over: "sha256(manifest.json exact bytes)",
|
|
manifestSha256,
|
|
publicKeyFingerprint,
|
|
signature: sigB64,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Derive fingerprint from public key PEM (for verification side).
|
|
*/
|
|
export function fingerprintFromPublicPem(publicKeyPem: string): string {
|
|
const { createPublicKey } = require("node:crypto") as typeof import("node:crypto");
|
|
const key = createPublicKey(publicKeyPem);
|
|
const der = key.export({ format: "der", type: "spki" }) as Buffer;
|
|
return `sha256:${sha256Hex(der)}`;
|
|
}
|