import { generateKeyPairSync, sign, verify } from "node:crypto"; import { mkdir, readFile, writeFile } from "node:fs/promises"; import { join } from "node:path"; import { canonicalJsonDigest, canonicalValueBytes, byteDigest, } from "../../src/security/canonical-digest.ts"; import { computeProductionInputRoot } from "../../src/security/production-input-root.ts"; const digest = (character: string) => character.repeat(64); export interface ReleaseFixture { distDir: string; signedManifestPath: string; sbomPath: string; trustPolicyPath: string; trustPolicy: Record; verifySignedReleaseEnvelope(input: { signedRelease: any; trustPolicy: any; now: string; protectedState: any; }): any; protectedState: { latestLedgerHeadHash: string; latestLedgerRevision: number; currentManifestDigest: string | null; publisherFence: number; releaseTrustPolicyDigest: string; }; manifestDigest: string; buildId: string; expiresAt: string; signFor(input: { buildId: string; distDir: string; predecessorManifestDigest: string | null; publisherFence: number; issuedAt?: string; expiresAt?: string; outputPath: string; }): Promise<{ manifestDigest: string; signedManifestPath: string }>; } export async function createReleaseFixture( root: string, ): Promise { const distDir = join(root, "dist"); await mkdir(join(distDir, "assets"), { recursive: true }); await writeFile( join(distDir, "index.html"), "Portfolio\n", ); await writeFile( join(distDir, "assets/app.js"), 'console.log("portfolio");\n', ); const sbomPath = join(root, "sbom.cdx.json"); await writeFile(sbomPath, '{"bomFormat":"CycloneDX","specVersion":"1.6"}\n'); const keys = generateKeyPairSync("ed25519"); const publicKeyPem = keys.publicKey .export({ type: "spki", format: "pem" }) .toString(); const trustPolicyPath = join(root, "release-trust.json"); const trustPolicy = { keyId: "release-test-key", issuerEpoch: 7, scope: "portfolio-release-signing/v1", publicKeyPem, }; await writeFile(trustPolicyPath, canonicalValueBytes(trustPolicy)); const protectedState = { latestLedgerHeadHash: digest("9"), latestLedgerRevision: 11, currentManifestDigest: null, publisherFence: 0, releaseTrustPolicyDigest: canonicalJsonDigest( "portfolio-trust-policy/v1", trustPolicy, ), }; const verifySignedReleaseEnvelope = ({ signedRelease, trustPolicy, now, protectedState: state, }: { signedRelease: any; trustPolicy: any; now: string; protectedState: any; }) => { if (signedRelease?.schema !== "portfolio-signed-release-manifest/v1") throw new Error("bad envelope schema"); if ( signedRelease.signature?.keyId !== trustPolicy.keyId || signedRelease.signature?.issuerEpoch !== trustPolicy.issuerEpoch || signedRelease.signature?.scope !== trustPolicy.scope ) throw new Error("untrusted signer"); const signature = Buffer.from(signedRelease.signature.value, "base64url"); if ( signature.length !== 64 || !verify( null, canonicalValueBytes(signedRelease.payload), trustPolicy.publicKeyPem, signature, ) ) throw new Error("invalid release signature"); const manifestDigest = canonicalJsonDigest( "portfolio-candidate-release-manifest/v1", signedRelease.payload, ); if (manifestDigest !== signedRelease.payloadDigest) throw new Error("candidate digest mismatch"); const candidate = signedRelease.payload; if ( candidate.latestLedgerHeadHash !== state.latestLedgerHeadHash || candidate.latestLedgerRevision !== state.latestLedgerRevision ) throw new Error("stale ledger"); if ( candidate.releaseManifest.currentManifestDigest !== state.currentManifestDigest ) throw new Error("stale predecessor"); if (Date.parse(now) >= Date.parse(candidate.releaseManifest.expiresAt)) throw new Error("expired release"); return { schema: "portfolio-verified-release-admission/v1", manifestDigest, manifestPayload: candidate.releaseManifest, expiresAt: candidate.releaseManifest.expiresAt, predecessorManifestDigest: candidate.releaseManifest.currentManifestDigest, latestLedgerHeadHash: candidate.latestLedgerHeadHash, latestLedgerRevision: candidate.latestLedgerRevision, expectedFence: state.publisherFence + 1, signatureVerified: true, }; }; const signFor = async (input: { buildId: string; distDir: string; predecessorManifestDigest: string | null; publisherFence: number; issuedAt?: string; expiresAt?: string; outputPath: string; }) => { const artifactPaths = ["assets/app.js", "index.html"]; const artifacts = await Promise.all( artifactPaths.map(async (path) => { const bytes = await readFile(join(input.distDir, path)); return { path, sha256: byteDigest(bytes), bytes: bytes.length, encoding: "identity" as const, }; }), ); const inputDigests = { PortfolioContent: digest("a"), PublicAssetManifest: digest("b"), PreparedAssetSet: digest("c"), SiteConfig: digest("d"), }; const issuedAt = input.issuedAt ?? "2026-09-02T00:00:00Z"; const expiresAt = input.expiresAt ?? "2026-09-03T00:00:00Z"; const releaseManifest = { schema: "portfolio-release-manifest/v1" as const, launchMode: "private-source-initial-launch" as const, buildId: input.buildId, inputDigests, productionInputRoot: computeProductionInputRoot({ portfolioContentDigest: inputDigests.PortfolioContent, publicAssetManifestDigest: inputDigests.PublicAssetManifest, preparedAssetSetDigest: inputDigests.PreparedAssetSet, siteConfigDigest: inputDigests.SiteConfig, }), artifacts, deployFiles: artifactPaths, sourceCommit: digest("e"), sourceTree: digest("f"), approvedRecordRoot: digest("1"), approvedAssetRoot: digest("2"), approvalProofSetDigest: digest("3"), approvalProjectionAttestationDigest: digest("4"), scanEvidenceDigest: digest("5"), policyVersions: { approval: "1", privacy: "1", release: "1" }, toolVersions: { node: "22.22.1" }, predecessorManifestDigest: input.predecessorManifestDigest, currentManifestDigest: input.predecessorManifestDigest, issuedAt, expiresAt, minimumBoundExpiry: expiresAt, }; const candidate = { schema: "portfolio-candidate-release-manifest/v1", releaseManifest, latestLedgerHeadHash: protectedState.latestLedgerHeadHash, latestLedgerRevision: protectedState.latestLedgerRevision, sourceClosureDigest: digest("6"), publicObjectClosureDigest: digest("b"), publicRefMap: {}, lfsOids: [], releaseArchives: [], buildProvenanceDigest: digest("7"), privacyPolicyDigest: digest("8"), toolImageDigests: [digest("0")], }; const payloadDigest = canonicalJsonDigest( "portfolio-candidate-release-manifest/v1", candidate, ); const signature = sign( null, canonicalValueBytes(candidate), keys.privateKey, ).toString("base64url"); const signedRelease = { schema: "portfolio-signed-release-manifest/v1", payload: candidate, payloadDigest, signature: { algorithm: "Ed25519", encoding: "base64url-nopad", keyId: "release-test-key", issuerEpoch: 7, scope: "portfolio-release-signing/v1", value: signature, }, }; await writeFile(input.outputPath, canonicalValueBytes(signedRelease)); return { manifestDigest: payloadDigest, signedManifestPath: input.outputPath, }; }; const signedManifestPath = join(root, "signed-release.json"); const initial = await signFor({ buildId: "release-a", distDir, predecessorManifestDigest: null, publisherFence: 0, outputPath: signedManifestPath, }); return { distDir, signedManifestPath, sbomPath, trustPolicyPath, trustPolicy, verifySignedReleaseEnvelope, protectedState, manifestDigest: initial.manifestDigest, buildId: "release-a", expiresAt: "2026-09-03T00:00:00Z", signFor, }; } export async function initializeReleaseRoot( root: string, state?: Partial, trustPolicy?: Record, ): Promise { await mkdir(join(root, "control"), { recursive: true }); await mkdir(join(root, ".control"), { recursive: true }); await mkdir(join(root, "inbox"), { recursive: true }); if (trustPolicy !== undefined) await writeFile( join(root, ".control", "release-trust.json"), canonicalValueBytes(trustPolicy), ); await writeFile( join(root, ".portfolio-release-root"), "portfolio-release-root/v1\n", ); await writeFile( join(root, "control/state.json"), canonicalValueBytes({ schema: "portfolio-release-control-state/v1", currentManifestDigest: state?.currentManifestDigest ?? null, currentReleaseId: null, publisherFence: state?.publisherFence ?? 0, latestLedgerHeadHash: state?.latestLedgerHeadHash ?? digest("9"), latestLedgerRevision: state?.latestLedgerRevision ?? 11, releaseTrustPolicyDigest: state?.releaseTrustPolicyDigest ?? digest("8"), activeTransaction: null, }), ); }