import { generateKeyPairSync, sign } from "node:crypto"; import { describe, expect, test } from "vitest"; import { type ApprovalPayload, parseApprovalEnvelopeJson, verifyApprovalEnvelope, } from "../../src/security/approval-envelope.ts"; import { canonicalJsonDigest, canonicalValueBytes, } from "../../src/security/canonical-digest.ts"; const hash = (character: string) => character.repeat(64); const payload = (): ApprovalPayload => ({ schema: "portfolio-approval-payload/v1", keyId: "approval-2026", issuerEpoch: 3, authorizedRole: "portfolio-approver", evidenceFingerprints: [hash("a")], recordClass: "content", recordType: "profile", publicRecordIdOrAssetId: "profile-main", approvedPublicRecordHashOrManifestRecordHash: hash("b"), sanitizedOutputHashOrNull: null, decision: "approve", issuedAt: "2026-09-01T08:00:00Z", expiresAt: "2026-10-01T08:00:00Z", previousLedgerHeadHash: hash("0"), ledgerRevision: 1, policyVersion: "approval-policy-1", }); function signedEnvelope(overrides: Partial = {}) { const keys = generateKeyPairSync("ed25519"); const signedPayload = { ...payload(), ...overrides }; const signature = sign( null, canonicalValueBytes(signedPayload), keys.privateKey, ).toString("base64url"); return { envelope: { schema: "portfolio-approval-envelope/v1" as const, payload: signedPayload, signature: { algorithm: "Ed25519" as const, encoding: "base64url-nopad" as const, value: signature, }, }, publicKey: keys.publicKey, }; } function trustFor( envelope: ReturnType["envelope"], publicKey: ReturnType["publicKey"], ) { const envelopeDigest = canonicalJsonDigest( "portfolio-approval-envelope-digest/v1", envelope, ); const latestLedgerHeadHash = canonicalJsonDigest( "portfolio-approval-ledger-entry/v1", { previousLedgerHeadHash: hash("0"), ledgerRevision: 1, envelopeDigest, }, ); return { now: "2026-09-02T00:00:00Z", latestLedgerHeadHash, latestLedgerRevision: 1, trustedKeys: [ { keyId: "approval-2026", issuerEpoch: 3, scope: "portfolio-approval-decision/v1" as const, authorizedRoles: ["portfolio-approver"], publicKey, }, ], ledgerProof: { schema: "portfolio-approval-ledger-proof/v1" as const, genesisHeadHash: hash("0"), entries: [envelope], }, }; } describe("approval envelopes", () => { test("verifies an exact, current, scoped Ed25519 approval", () => { const { envelope, publicKey } = signedEnvelope(); const verified = verifyApprovalEnvelope( envelope, trustFor(envelope, publicKey), ); expect(verified.publicRecordIdOrAssetId).toBe("profile-main"); }); test.each([ ["expired", { expiresAt: "2026-09-01T09:00:00Z" }], ["non-UTC timestamp", { issuedAt: "2026-09-01T10:00:00+02:00" }], ["wrong epoch", { issuerEpoch: 2 }], ["rejection", { decision: "reject" }], ] as const)("rejects a %s envelope", (_label, override) => { const { envelope, publicKey } = signedEnvelope(override); expect(() => verifyApprovalEnvelope(envelope, trustFor(envelope, publicKey)), ).toThrow(); }); test("rejects unknown fields, duplicate keys, and alternate signature encodings at ingest", () => { const { envelope } = signedEnvelope(); expect(() => parseApprovalEnvelopeJson(JSON.stringify({ ...envelope, extra: true })), ).toThrow(); expect(() => parseApprovalEnvelopeJson( '{"schema":"portfolio-approval-envelope/v1","schema":"portfolio-approval-envelope/v1"}', ), ).toThrow(); expect(() => parseApprovalEnvelopeJson( JSON.stringify({ ...envelope, signature: { ...envelope.signature, value: `${envelope.signature.value}=`, }, }), ), ).toThrow(); }); });