import { generateKeyPairSync, sign } from "node:crypto"; import { describe, expect, test } from "vitest"; import { computeApprovalClosure, verifyApprovalProjectionAttestation, } from "../../src/security/approval-closure.ts"; import { type ApprovalPayload } from "../../src/security/approval-envelope.ts"; import { canonicalJsonDigest, canonicalValueBytes, } from "../../src/security/canonical-digest.ts"; const h = (c: string) => c.repeat(64); const keys = generateKeyPairSync("ed25519"); function envelope(payload: ApprovalPayload) { return { schema: "portfolio-approval-envelope/v1" as const, payload, signature: { algorithm: "Ed25519" as const, encoding: "base64url-nopad" as const, value: sign(null, canonicalValueBytes(payload), keys.privateKey).toString( "base64url", ), }, }; } function base( recordClass: "content" | "asset", id: string, ledgerRevision: number, previousLedgerHeadHash: string, ): ApprovalPayload { return { schema: "portfolio-approval-payload/v1", keyId: "key", issuerEpoch: 1, authorizedRole: "approver", evidenceFingerprints: [h("1")], recordClass, recordType: recordClass === "content" ? "profile" : "", publicRecordIdOrAssetId: id, approvedPublicRecordHashOrManifestRecordHash: h( recordClass === "content" ? "2" : "3", ), sanitizedOutputHashOrNull: recordClass === "asset" ? h("4") : null, decision: "approve", issuedAt: "2026-09-01T00:00:00Z", expiresAt: "2026-10-01T00:00:00Z", previousLedgerHeadHash, ledgerRevision, policyVersion: "p1", }; } function ledgerTrust(entries: ReturnType[]) { let head = h("0"); entries.forEach((entry, index) => { const envelopeDigest = canonicalJsonDigest( "portfolio-approval-envelope-digest/v1", entry, ); head = canonicalJsonDigest("portfolio-approval-ledger-entry/v1", { previousLedgerHeadHash: head, ledgerRevision: index + 1, envelopeDigest, }); }); return { now: "2026-09-02T00:00:00Z", latestLedgerHeadHash: head, latestLedgerRevision: entries.length, trustedKeys: [ { keyId: "key", issuerEpoch: 1, scope: "portfolio-approval-decision/v1" as const, authorizedRoles: ["approver"], publicKey: keys.publicKey, }, ], ledgerProof: { schema: "portfolio-approval-ledger-proof/v1" as const, genesisHeadHash: h("0"), entries, }, }; } describe("approval closure", () => { test("computes exact sorted record, asset, and proof roots", () => { const content = base("content", "profile-main", 1, h("0")); const contentEnvelope = envelope(content); const head1 = canonicalJsonDigest("portfolio-approval-ledger-entry/v1", { previousLedgerHeadHash: h("0"), ledgerRevision: 1, envelopeDigest: canonicalJsonDigest( "portfolio-approval-envelope-digest/v1", contentEnvelope, ), }); const asset = base("asset", "asset-main", 2, head1); const assetEnvelope = envelope(asset); const result = computeApprovalClosure({ contentRecords: [ { recordType: "profile", publicRecordId: "profile-main", approvedPublicRecordHash: h("2"), envelope: contentEnvelope, }, ], assetRecords: [ { assetId: "asset-main", approvedManifestRecordHash: h("3"), sanitizedOutputHash: h("4"), envelope: assetEnvelope, }, ], trust: ledgerTrust([contentEnvelope, assetEnvelope]), }); const contentEnvelopeDigest = canonicalJsonDigest( "portfolio-approval-envelope-digest/v1", envelope(content), ); const assetEnvelopeDigest = canonicalJsonDigest( "portfolio-approval-envelope-digest/v1", envelope(asset), ); expect(result.approvedRecordRoot).toBe( canonicalJsonDigest("portfolio-record-closure/v1", { records: [["profile", "profile-main", h("2"), contentEnvelopeDigest]], }), ); expect(result.approvedAssetRoot).toBe( canonicalJsonDigest("portfolio-asset-closure/v1", { assets: [["asset-main", h("3"), h("4"), assetEnvelopeDigest]], }), ); expect(result.approvalProofSetDigest).toBe( canonicalJsonDigest("portfolio-approval-proof-set/v1", { envelopes: [ ["asset", "", "asset-main", assetEnvelopeDigest], ["content", "profile", "profile-main", contentEnvelopeDigest], ], }), ); }); test("rejects duplicate IDs and stale protected ledger input", () => { const content = base("content", "profile-main", 1, h("0")); const record = { recordType: "profile", publicRecordId: "profile-main", approvedPublicRecordHash: h("2"), envelope: envelope(content), }; const trust = ledgerTrust([record.envelope]); expect(() => computeApprovalClosure({ contentRecords: [record, record], assetRecords: [], trust, }), ).toThrow(); expect(() => computeApprovalClosure({ contentRecords: [record], assetRecords: [], trust: { ...trust, latestLedgerRevision: 0 }, }), ).toThrow(); }); test("verifies a scoped projection attestation against protected state and trusted time", () => { const payload = { schema: "portfolio-approval-projection-attestation/v1" as const, keyId: "projection-key", issuerEpoch: 4, scope: "portfolio-approval-projection-attestation/v1" as const, approvedRecordRoot: h("1"), approvedAssetRoot: h("2"), approvalProofSetDigest: h("3"), inputDigests: { PortfolioContent: h("4"), PublicAssetManifest: h("5"), PreparedAssetSet: h("6"), SiteConfig: h("7"), }, productionInputRoot: "e19fd4083ba973074449e5ecc9364c2b339eadea32e61d8dd2f0d8e3d6e91c85", liveLedgerHeadHash: h("8"), liveLedgerRevision: 42, policyVersion: "projection-policy-1", issuedAt: "2026-09-02T00:00:00Z", expiresAt: "2026-09-03T00:00:00Z", earliestApprovalExpiry: "2026-09-04T00:00:00Z", }; const projectionKeys = generateKeyPairSync("ed25519"); const attestation = { schema: "portfolio-approval-projection-attestation-envelope/v1" as const, payload, signature: { algorithm: "Ed25519" as const, encoding: "base64url-nopad" as const, value: sign( null, canonicalValueBytes(payload), projectionKeys.privateKey, ).toString("base64url"), }, }; expect( verifyApprovalProjectionAttestation(attestation, { now: "2026-09-02T01:00:00Z", latestLedgerHeadHash: h("8"), latestLedgerRevision: 42, trustedKey: { keyId: "projection-key", issuerEpoch: 4, scope: "portfolio-approval-projection-attestation/v1", publicKey: projectionKeys.publicKey, }, }).liveLedgerRevision, ).toBe(42); expect(() => verifyApprovalProjectionAttestation(attestation, { now: "2026-09-02T01:00:00Z", latestLedgerHeadHash: h("9"), latestLedgerRevision: 42, trustedKey: { keyId: "projection-key", issuerEpoch: 4, scope: "portfolio-approval-projection-attestation/v1", publicKey: projectionKeys.publicKey, }, }), ).toThrow(); }); });