diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 0000000..49a922c --- /dev/null +++ b/src/constants.js @@ -0,0 +1,31 @@ +/** + * @veritasacta/verify: Single-source-of-truth constants + * + * Strings and identifiers that MUST stay byte-identical across the CLI, + * the test-vectors generator, and any future conformance-test runner. + * Importing from one place prevents docs/code drift from silently + * desynchronizing rejection-path semantics. + * + * Add a new constant here when: + * - a fixture's `expected_result.error` should match the CLI's output + * verbatim, AND + * - the string is normatively pinned to a spec section (cite the + * normative reference in the JSDoc comment). + */ + +/** + * Rejection error: APS verification key transported inside the receipt + * envelope without an independent anchor (sidecar JWKS, DID resolution, + * or other independently anchored key reference). + * + * Pinned to draft-farley-acta-signed-receipts-02 Security Considerations. + * + * Used by: + * - test-vectors/generate.mjs → cross-verify-embedded-key-bundle.json + * `expected_result.error` and `expected_verification.error` + * - src/verifier.js (forthcoming v0.7+ enforcement landing) → emitted + * when a bundle declares `external_receipts.aps.signing_key` without + * an accompanying `verification_key_ref`. + */ +export const REJECTION_ERROR_EMBEDDED_KEY = + 'verification key transported inside receipt without independent anchor'; diff --git a/test-vectors/README.md b/test-vectors/README.md index 1378f2d..2f230cf 100644 --- a/test-vectors/README.md +++ b/test-vectors/README.md @@ -11,7 +11,9 @@ Shipped to support the cross-verify work tracked in | File | Purpose | |------|---------| | [`jcs-test-vectors.json`](jcs-test-vectors.json) | JCS canonicalization conformance. 9 cases. Feed each `input` to your canonicalizer and compare the output string and SHA-256 to the expected values. Includes the AIP-0001 ASCII-only key restriction case. | -| [`cross-verify-bundle.json`](cross-verify-bundle.json) | One complete Knowledge Unit: 4 models × 2 rounds + 1 synthesis + 1 aggregate = 10 Ed25519-signed receipts, hash-chained via `payload.previousReceiptHash`. Includes an `external_receipts.aps` slot for an APS `DecisionLineageReceipt` to drop in. | +| [`cross-verify-bundle.json`](cross-verify-bundle.json) | MUST accept. One complete Knowledge Unit: 4 models × 2 rounds + 1 synthesis + 1 aggregate = 10 Ed25519-signed receipts, hash-chained via `payload.previousReceiptHash`. Includes an APS `DecisionLineageReceipt` whose verification key is independently anchored via [`keys/aps-ku-cross-verify.jwks`](keys/aps-ku-cross-verify.jwks). | +| [`cross-verify-embedded-key-bundle.json`](cross-verify-embedded-key-bundle.json) | MUST reject in `@veritasacta/verify` v0.7+ once draft-02 enforcement ships. Deliberately preserves the embedded-key anti-pattern so verifiers can prove they reject verification keys transported inside a receipt or bundle without an independent anchor. **Note for current 0.6.0 consumers:** the verifier does not yet enforce this rejection path; running the suite against 0.6.0 will accept this fixture, which is expected. The fixture's purpose is as a regression target for the v0.7+ enforcement landing, not a test of current behavior. | +| [`keys/aps-ku-cross-verify.jwks`](keys/aps-ku-cross-verify.jwks) | Sidecar JWKS for the APS cross-verification fixture. Referenced by `external_receipts.aps.verification_key_ref`. | | [`selective-disclosure-salted-commit.json`](selective-disclosure-salted-commit.json) | AIP-0002 salted SHA-256 commitment reference. The Grok-4.20 round-1 dissenting response is published with `response.position` and `response.confidence` replaced by commitments. The unsigned `witness` block carries salts + plaintext to unlock them. | | [`generate.mjs`](generate.mjs) | The generator. Deterministic: same seed produces byte-identical output. Run `node generate.mjs ` from a workspace with `@veritasacta/artifacts` installed. | @@ -23,6 +25,15 @@ npx @veritasacta/verify test-vectors/cross-verify-bundle.json --bundle # → Bundle: VALID # Total: 10 Passed: 10 Failed: 0 +# Positive APS cross-layer fixture with sidecar-anchored key +npx @veritasacta/verify test-vectors/cross-verify-bundle.json \ + --bundle --jwks test-vectors/keys/aps-ku-cross-verify.jwks + +# Negative embedded-key fixture +npx @veritasacta/verify test-vectors/cross-verify-embedded-key-bundle.json --bundle +# → MUST fail non-zero: +# verification key transported inside receipt without independent anchor + # Selective-disclosure receipt (redacted shape, signature over redacted payload) npx @veritasacta/verify test-vectors/selective-disclosure-salted-commit.json # Note: CLI input shape here is the `redacted_receipt` field; pass it directly @@ -37,7 +48,12 @@ For a third-party canonicalizer + verifier to pass: 2. For the ASCII-only case, the canonicalizer MUST throw when a key contains non-ASCII. 3. For `cross-verify-bundle.json`, every receipt individually verifies under the `verification.signing_keys[].kid` that matches its `kid` field. -4. For `selective-disclosure-salted-commit.json`, the redacted receipt verifies +4. For `cross-verify-bundle.json`, the APS receipt verifier MUST resolve + `external_receipts.aps.verification_key_ref` to the sidecar JWKS and MUST NOT + rely on a verification key transported inside the receipt or bundle. +5. For `cross-verify-embedded-key-bundle.json`, a verifier MUST reject with + `verification key transported inside receipt without independent anchor`. +6. For `selective-disclosure-salted-commit.json`, the redacted receipt verifies without the witness, and for each entry in `witness.disclosures`: `sha256(salt + ":" + JSON.stringify(plaintext))` equals `expected_commitment`. diff --git a/test-vectors/cross-verify-bundle.json b/test-vectors/cross-verify-bundle.json index cabc406..6a688c7 100644 --- a/test-vectors/cross-verify-bundle.json +++ b/test-vectors/cross-verify-bundle.json @@ -60,7 +60,135 @@ "signature" ], "receipt_uri": null, - "receipt": null + "receipt": { + "receiptId": "dlr_va_ku_4b3f7c2a9d8e1f05", + "timestamp": "2026-04-18T00:00:00Z", + "decisionArtifactId": "veritasacta:ku:ku_4b3f7c2a9d8e1f05", + "decisionType": "knowledge_unit_deliberation", + "contributingSources": [ + { + "sourceId": "ku:model:claude-opus-4.6", + "accessReceiptId": "sha256:2b06eb3385bcfa7bf29d59ae693c28124062fdb9b548a9710c1ee44f2eeae192", + "derivationDepth": 1, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + }, + { + "sourceId": "ku:model:gpt-5", + "accessReceiptId": "sha256:a54dde3a9dd2501476b0e9613990333371a29b2b669c9da02e348a86e6b2ff83", + "derivationDepth": 2, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + }, + { + "sourceId": "ku:model:gemini-2.5-pro", + "accessReceiptId": "sha256:76cce8f734ae646b56f4df333a15a400f254dcacece06e7b2bf26c97438b8bbc", + "derivationDepth": 3, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + }, + { + "sourceId": "ku:model:grok-4.20", + "accessReceiptId": "sha256:653a70f396a16d2114b42996305f514b35d5ba59e6b37527b6311aa4d9e79aeb", + "derivationDepth": 4, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + }, + { + "sourceId": "ku:model:claude-opus-4.6", + "accessReceiptId": "sha256:69eaa88bb7b4d0c66e9649096dae4aad20b84f7d9862b404f46a37e0a96e8240", + "derivationDepth": 5, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + }, + { + "sourceId": "ku:model:gpt-5", + "accessReceiptId": "sha256:18ac3cef4d85205ebe2886b8b94b368ae8d28585088762e91df80ab3031a42d6", + "derivationDepth": 6, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + }, + { + "sourceId": "ku:model:gemini-2.5-pro", + "accessReceiptId": "sha256:b69cef75b1f0eeb36ece82755df449ccea4dde04915268c254fef33508ce016f", + "derivationDepth": 7, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + }, + { + "sourceId": "ku:model:grok-4.20", + "accessReceiptId": "sha256:b744f32a94d8f1f7727e9020308a8b1db87aef8c80b53c7644398ae0078317bc", + "derivationDepth": 8, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + }, + { + "sourceId": "ku:arbiter:scopeblind-reference", + "accessReceiptId": "sha256:b0b54512737617227b1ac7569c06e71fbe8dd944011050c3ceaeac5e79526122", + "derivationDepth": 9, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + }, + { + "sourceId": "ku:arbiter:scopeblind-reference", + "accessReceiptId": "sha256:a630e18f8a394f5b8f3488b6d6ffa1ce2193380b3ae35de8b7477b640fdacb6b", + "derivationDepth": 10, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + } + ], + "lineageCompleteness": "complete", + "externalHopsPresent": true, + "transformChain": [ + "aggregation" + ], + "governingPurpose": "research:academic", + "jurisdictionContext": "veritasacta:knowledge-unit-bundle:v1", + "explanation": "APS DecisionLineageReceipt attesting to VeritasActa Knowledge Unit ku_4b3f7c2a9d8e1f05. Bundle terminal aggregate sha256: a630e18f8a394f5b8f3488b6d6ffa1ce2193380b3ae35de8b7477b640fdacb6b. Each entry in contributingSources commits to the JCS-canonical sha256 of one KU receipt; tampering any byte of any KU receipt invalidates the recorded accessReceiptId, breaking cross-layer integrity even though APS's Ed25519 signature over this DecisionLineageReceipt remains cryptographically valid.", + "signature": "c4ff9a5142ddf133294ccbbbed55facf077df11e3b067627fd041277832a0e5865d74d32845aac6486242aca2b8d52142699bc6c3fe8b4f0bf987e2b56001001" + }, + "verifier_hint": "Verify APS DecisionLineageReceipt using the independently anchored key referenced by external_receipts.aps.verification_key_ref. Cross-layer integrity: each contributingSources[].accessReceiptId is the JCS-canonical sha256 of the corresponding entry in bundle.receipts.", + "verification_key_ref": "test-vectors/keys/aps-ku-cross-verify.jwks#aps-ku-cross-verify-v1" } }, "receipts": [ diff --git a/test-vectors/cross-verify-embedded-key-bundle.json b/test-vectors/cross-verify-embedded-key-bundle.json new file mode 100644 index 0000000..e7645d4 --- /dev/null +++ b/test-vectors/cross-verify-embedded-key-bundle.json @@ -0,0 +1,483 @@ +{ + "format": "veritasacta:knowledge-unit-bundle:v1", + "spec": "draft-farley-acta-knowledge-units-00", + "ku_id": "ku_4b3f7c2a9d8e1f05", + "generated_at": "2026-04-17T12:00:00Z", + "description": "Complete Knowledge Unit deliberation: 4 models × 2 rounds + 1 synthesis + 1 aggregate = 10 Ed25519-signed receipts. Hash-chained via payload.previousReceiptHash over JCS-canonical bytes. Every receipt is individually verifiable with @veritasacta/verify; the bundle is verifiable with --bundle.", + "verification": { + "signing_keys": [ + { + "kty": "OKP", + "crv": "Ed25519", + "kid": "Z-LI5A7pV_q5L0741YBXwcOCo1U13ONImZ4OkQnKkg0", + "x": "tiTKo_UAbc-jbX6rtdupskLamLZPIXSC30y6dPPHRWQ", + "use": "sig", + "issuer": "ku:model:claude-opus-4.6" + }, + { + "kty": "OKP", + "crv": "Ed25519", + "kid": "yH110mxzFmSFg-rqk93G6orppuIMvxJaBBCbtB2BF1Y", + "x": "wLeHCJz2lKADNwV1pNMEtFbsbostfaXVjtfhEcauPs4", + "use": "sig", + "issuer": "ku:model:gpt-5" + }, + { + "kty": "OKP", + "crv": "Ed25519", + "kid": "EOahLGtg57mQJhgTwQc-cTmZZPTVn_NGal6SH5taqJw", + "x": "zwJOHnOSOTkkh37-GuNxC41o54UX-Yjnn6FN_1JKJDo", + "use": "sig", + "issuer": "ku:model:gemini-2.5-pro" + }, + { + "kty": "OKP", + "crv": "Ed25519", + "kid": "MdZxWfPWCPrjb4blQEWBKFyk9FkazXCgk2K7QFiuYRw", + "x": "nQIE0pSO6yXnABIt3SdC01oyyLa-J1i9Wf5o1lDUtoc", + "use": "sig", + "issuer": "ku:model:grok-4.20" + }, + { + "kty": "OKP", + "crv": "Ed25519", + "kid": "YJMzwAk4gkCxTzWPmVAlcLtr517adDfl9gxGCLP4bDw", + "x": "Uy9EM6Buox0IJ9Ry0QiPtPuxj-7oZedl3_ogRzpXSd4", + "use": "sig", + "issuer": "ku:arbiter:scopeblind-reference" + } + ] + }, + "external_receipts": { + "aps": { + "description": "Drop-in slot for an APS DecisionLineageReceipt that references ku_id=ku_4b3f7c2a9d8e1f05. When populated, both @veritasacta/verify and agent-passport-system verifiers should exit 0 over the same bundle.", + "ku_id": "ku_4b3f7c2a9d8e1f05", + "expected_fields": [ + "subject", + "scope", + "ku_id", + "receipt_hash", + "signature" + ], + "receipt_uri": null, + "receipt": { + "receiptId": "dlr_va_ku_4b3f7c2a9d8e1f05", + "timestamp": "2026-04-18T00:00:00Z", + "decisionArtifactId": "veritasacta:ku:ku_4b3f7c2a9d8e1f05", + "decisionType": "knowledge_unit_deliberation", + "contributingSources": [ + { + "sourceId": "ku:model:claude-opus-4.6", + "accessReceiptId": "sha256:2b06eb3385bcfa7bf29d59ae693c28124062fdb9b548a9710c1ee44f2eeae192", + "derivationDepth": 1, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + }, + { + "sourceId": "ku:model:gpt-5", + "accessReceiptId": "sha256:a54dde3a9dd2501476b0e9613990333371a29b2b669c9da02e348a86e6b2ff83", + "derivationDepth": 2, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + }, + { + "sourceId": "ku:model:gemini-2.5-pro", + "accessReceiptId": "sha256:76cce8f734ae646b56f4df333a15a400f254dcacece06e7b2bf26c97438b8bbc", + "derivationDepth": 3, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + }, + { + "sourceId": "ku:model:grok-4.20", + "accessReceiptId": "sha256:653a70f396a16d2114b42996305f514b35d5ba59e6b37527b6311aa4d9e79aeb", + "derivationDepth": 4, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + }, + { + "sourceId": "ku:model:claude-opus-4.6", + "accessReceiptId": "sha256:69eaa88bb7b4d0c66e9649096dae4aad20b84f7d9862b404f46a37e0a96e8240", + "derivationDepth": 5, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + }, + { + "sourceId": "ku:model:gpt-5", + "accessReceiptId": "sha256:18ac3cef4d85205ebe2886b8b94b368ae8d28585088762e91df80ab3031a42d6", + "derivationDepth": 6, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + }, + { + "sourceId": "ku:model:gemini-2.5-pro", + "accessReceiptId": "sha256:b69cef75b1f0eeb36ece82755df449ccea4dde04915268c254fef33508ce016f", + "derivationDepth": 7, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + }, + { + "sourceId": "ku:model:grok-4.20", + "accessReceiptId": "sha256:b744f32a94d8f1f7727e9020308a8b1db87aef8c80b53c7644398ae0078317bc", + "derivationDepth": 8, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + }, + { + "sourceId": "ku:arbiter:scopeblind-reference", + "accessReceiptId": "sha256:b0b54512737617227b1ac7569c06e71fbe8dd944011050c3ceaeac5e79526122", + "derivationDepth": 9, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + }, + { + "sourceId": "ku:arbiter:scopeblind-reference", + "accessReceiptId": "sha256:a630e18f8a394f5b8f3488b6d6ffa1ce2193380b3ae35de8b7477b640fdacb6b", + "derivationDepth": 10, + "transformPath": [ + "aggregation" + ], + "termsVersionAtAccess": "veritasacta:knowledge-unit-bundle:v1", + "lineageConfidence": "complete", + "compensationStatus": "settled" + } + ], + "lineageCompleteness": "complete", + "externalHopsPresent": true, + "transformChain": [ + "aggregation" + ], + "governingPurpose": "research:academic", + "jurisdictionContext": "veritasacta:knowledge-unit-bundle:v1", + "explanation": "APS DecisionLineageReceipt attesting to VeritasActa Knowledge Unit ku_4b3f7c2a9d8e1f05. Bundle terminal aggregate sha256: a630e18f8a394f5b8f3488b6d6ffa1ce2193380b3ae35de8b7477b640fdacb6b. Each entry in contributingSources commits to the JCS-canonical sha256 of one KU receipt; tampering any byte of any KU receipt invalidates the recorded accessReceiptId, breaking cross-layer integrity even though APS's Ed25519 signature over this DecisionLineageReceipt remains cryptographically valid.", + "signature": "c4ff9a5142ddf133294ccbbbed55facf077df11e3b067627fd041277832a0e5865d74d32845aac6486242aca2b8d52142699bc6c3fe8b4f0bf987e2b56001001" + }, + "signing_key": { + "kty": "OKP", + "crv": "Ed25519", + "use": "sig", + "issuer": "aps:test:ku-cross-verify", + "public_key_hex": "e91c6bc5b1ae0050db2a84ed962559e977cc6ff4cb5bd11577071bd7fa604faa", + "seed_note": "derived from sha256(\"aps:veritasacta:ku-cross-verify:v1\") — test key, NOT a production issuer" + }, + "verifier_hint": "Negative conformance fixture. A verifier MUST reject this bundle because the APS verification key is transported inside external_receipts.aps.signing_key without an independent anchor.", + "expected_result": { + "verifier": "@veritasacta/verify", + "result": "MUST_REJECT", + "error": "verification key transported inside receipt without independent anchor" + } + } + }, + "receipts": [ + { + "v": 2, + "type": "veritasacta:knowledge_unit:round_response", + "algorithm": "ed25519", + "kid": "Z-LI5A7pV_q5L0741YBXwcOCo1U13ONImZ4OkQnKkg0", + "issuer": "ku:model:claude-opus-4.6", + "issued_at": "2026-04-17T12:00:01Z", + "payload": { + "ku_id": "ku_4b3f7c2a9d8e1f05", + "sequence": 1, + "round": 1, + "model": { + "id": "claude-opus-4.6", + "vendor": "anthropic" + }, + "topic_digest": "a0431c892bf3d1d67c63726c9a7cf00c6270b39098c8e9cdcfdf26bb73825b24", + "response": { + "position": "quadratic", + "confidence": 0.72 + } + }, + "signature": "b51bdf0dd826b63dd84d0e0d50e3700b48a5213adeebaefff7db9c3b109ba935dc70e78189fc0d306b10aa6337e32a8e74e92300e6e7eae91f76a282c2a8b609" + }, + { + "v": 2, + "type": "veritasacta:knowledge_unit:round_response", + "algorithm": "ed25519", + "kid": "yH110mxzFmSFg-rqk93G6orppuIMvxJaBBCbtB2BF1Y", + "issuer": "ku:model:gpt-5", + "issued_at": "2026-04-17T12:00:02Z", + "payload": { + "ku_id": "ku_4b3f7c2a9d8e1f05", + "sequence": 2, + "round": 1, + "model": { + "id": "gpt-5", + "vendor": "openai" + }, + "topic_digest": "a0431c892bf3d1d67c63726c9a7cf00c6270b39098c8e9cdcfdf26bb73825b24", + "response": { + "position": "quadratic", + "confidence": 0.65 + }, + "previousReceiptHash": "sha256:2b06eb3385bcfa7bf29d59ae693c28124062fdb9b548a9710c1ee44f2eeae192" + }, + "signature": "14fa53712789d72d727ea7c99cf109181b42de2b6baaa76c97f8c0ac1633e667d2888672a181ddf896636e3cabb441ec68d3f4bc23f85a9f47ccc3980619680f" + }, + { + "v": 2, + "type": "veritasacta:knowledge_unit:round_response", + "algorithm": "ed25519", + "kid": "EOahLGtg57mQJhgTwQc-cTmZZPTVn_NGal6SH5taqJw", + "issuer": "ku:model:gemini-2.5-pro", + "issued_at": "2026-04-17T12:00:03Z", + "payload": { + "ku_id": "ku_4b3f7c2a9d8e1f05", + "sequence": 3, + "round": 1, + "model": { + "id": "gemini-2.5-pro", + "vendor": "google" + }, + "topic_digest": "a0431c892bf3d1d67c63726c9a7cf00c6270b39098c8e9cdcfdf26bb73825b24", + "response": { + "position": "depends_on_sequence_length", + "confidence": 0.8 + }, + "previousReceiptHash": "sha256:a54dde3a9dd2501476b0e9613990333371a29b2b669c9da02e348a86e6b2ff83" + }, + "signature": "fc702aab98cdf8f35c88b90da54776c037bcd53cee00183f4b994367c642558be7c6b1113ca370e24719f0f883a5772e358fb21ec58fa1b7d726e1c03d12930c" + }, + { + "v": 2, + "type": "veritasacta:knowledge_unit:round_response", + "algorithm": "ed25519", + "kid": "MdZxWfPWCPrjb4blQEWBKFyk9FkazXCgk2K7QFiuYRw", + "issuer": "ku:model:grok-4.20", + "issued_at": "2026-04-17T12:00:04Z", + "payload": { + "ku_id": "ku_4b3f7c2a9d8e1f05", + "sequence": 4, + "round": 1, + "model": { + "id": "grok-4.20", + "vendor": "xai" + }, + "topic_digest": "a0431c892bf3d1d67c63726c9a7cf00c6270b39098c8e9cdcfdf26bb73825b24", + "response": { + "position": "linear", + "confidence": 0.58 + }, + "previousReceiptHash": "sha256:76cce8f734ae646b56f4df333a15a400f254dcacece06e7b2bf26c97438b8bbc" + }, + "signature": "143109c124d8e8c857e1d0e5bc4da432be49a46fec0f77d34ee046e487efd4c2cca6e93697c56b8cec499a23e150dc7a4ea432328c7822092d15055ecf668e06" + }, + { + "v": 2, + "type": "veritasacta:knowledge_unit:round_response", + "algorithm": "ed25519", + "kid": "Z-LI5A7pV_q5L0741YBXwcOCo1U13ONImZ4OkQnKkg0", + "issuer": "ku:model:claude-opus-4.6", + "issued_at": "2026-04-17T12:00:05Z", + "payload": { + "ku_id": "ku_4b3f7c2a9d8e1f05", + "sequence": 5, + "round": 2, + "model": { + "id": "claude-opus-4.6", + "vendor": "anthropic" + }, + "topic_digest": "a0431c892bf3d1d67c63726c9a7cf00c6270b39098c8e9cdcfdf26bb73825b24", + "response": { + "position": "quadratic", + "confidence": 0.74, + "dissent_from": [ + "grok-4.20" + ] + }, + "previousReceiptHash": "sha256:653a70f396a16d2114b42996305f514b35d5ba59e6b37527b6311aa4d9e79aeb" + }, + "signature": "cdccd1f18914a542625b2f9916729b7584e590c3f224cb8901de3547565f94696893da3db5ce10057334206b8865aef917dbe4e050633761d0c99cc5526dde03" + }, + { + "v": 2, + "type": "veritasacta:knowledge_unit:round_response", + "algorithm": "ed25519", + "kid": "yH110mxzFmSFg-rqk93G6orppuIMvxJaBBCbtB2BF1Y", + "issuer": "ku:model:gpt-5", + "issued_at": "2026-04-17T12:00:06Z", + "payload": { + "ku_id": "ku_4b3f7c2a9d8e1f05", + "sequence": 6, + "round": 2, + "model": { + "id": "gpt-5", + "vendor": "openai" + }, + "topic_digest": "a0431c892bf3d1d67c63726c9a7cf00c6270b39098c8e9cdcfdf26bb73825b24", + "response": { + "position": "depends_on_sequence_length", + "confidence": 0.7, + "updated_from_round1": true + }, + "previousReceiptHash": "sha256:69eaa88bb7b4d0c66e9649096dae4aad20b84f7d9862b404f46a37e0a96e8240" + }, + "signature": "5db1986b447ae954ee425449c238550244821fb7fd9b928c477375b23b38126cc9aa4f394b727e97ada3ed1646dc1ca0b7dc6ee2f5f390d5c7e8a4dc95ae9c04" + }, + { + "v": 2, + "type": "veritasacta:knowledge_unit:round_response", + "algorithm": "ed25519", + "kid": "EOahLGtg57mQJhgTwQc-cTmZZPTVn_NGal6SH5taqJw", + "issuer": "ku:model:gemini-2.5-pro", + "issued_at": "2026-04-17T12:00:07Z", + "payload": { + "ku_id": "ku_4b3f7c2a9d8e1f05", + "sequence": 7, + "round": 2, + "model": { + "id": "gemini-2.5-pro", + "vendor": "google" + }, + "topic_digest": "a0431c892bf3d1d67c63726c9a7cf00c6270b39098c8e9cdcfdf26bb73825b24", + "response": { + "position": "depends_on_sequence_length", + "confidence": 0.85 + }, + "previousReceiptHash": "sha256:18ac3cef4d85205ebe2886b8b94b368ae8d28585088762e91df80ab3031a42d6" + }, + "signature": "f6483e7dc42969ac5536f6b65a9ff43305ba2f7404215368e13203f7d5500fb4e2b7537d56a63f1af3f459d0d50b124a43736e7013566548f70958ce4481540b" + }, + { + "v": 2, + "type": "veritasacta:knowledge_unit:round_response", + "algorithm": "ed25519", + "kid": "MdZxWfPWCPrjb4blQEWBKFyk9FkazXCgk2K7QFiuYRw", + "issuer": "ku:model:grok-4.20", + "issued_at": "2026-04-17T12:00:08Z", + "payload": { + "ku_id": "ku_4b3f7c2a9d8e1f05", + "sequence": 8, + "round": 2, + "model": { + "id": "grok-4.20", + "vendor": "xai" + }, + "topic_digest": "a0431c892bf3d1d67c63726c9a7cf00c6270b39098c8e9cdcfdf26bb73825b24", + "response": { + "position": "linear", + "confidence": 0.55, + "dissent_from": [ + "claude-opus-4.6", + "gpt-5", + "gemini-2.5-pro" + ] + }, + "previousReceiptHash": "sha256:b69cef75b1f0eeb36ece82755df449ccea4dde04915268c254fef33508ce016f" + }, + "signature": "1801e655c32aff17298399d36471c4049ea75631047858160c9f698cb2dc83981d94bdeda7f5d74d0fb54664cafce206ca25c2d73d51b6a25cb28610de4b840d" + }, + { + "v": 2, + "type": "veritasacta:knowledge_unit:synthesis", + "algorithm": "ed25519", + "kid": "YJMzwAk4gkCxTzWPmVAlcLtr517adDfl9gxGCLP4bDw", + "issuer": "ku:arbiter:scopeblind-reference", + "issued_at": "2026-04-17T12:00:09Z", + "payload": { + "ku_id": "ku_4b3f7c2a9d8e1f05", + "sequence": 9, + "round": 3, + "synthesis": { + "consensus": "depends_on_sequence_length", + "consensus_models": [ + "gpt-5", + "gemini-2.5-pro" + ], + "dissenting_positions": [ + { + "position": "quadratic", + "models": [ + "claude-opus-4.6" + ], + "mean_confidence": 0.74 + }, + { + "position": "linear", + "models": [ + "grok-4.20" + ], + "mean_confidence": 0.55 + } + ], + "synthesis_confidence": 0.68 + }, + "topic_digest": "a0431c892bf3d1d67c63726c9a7cf00c6270b39098c8e9cdcfdf26bb73825b24", + "previousReceiptHash": "sha256:b744f32a94d8f1f7727e9020308a8b1db87aef8c80b53c7644398ae0078317bc" + }, + "signature": "f6023315011bc437c0a1a6222c008affb7e14d264c4c89ad26efd12031461888858bb429eb54b2e26bbe093db53df83e80562d0199f8bdf4ed057178f947840d" + }, + { + "v": 2, + "type": "veritasacta:knowledge_unit:aggregate", + "algorithm": "ed25519", + "kid": "YJMzwAk4gkCxTzWPmVAlcLtr517adDfl9gxGCLP4bDw", + "issuer": "ku:arbiter:scopeblind-reference", + "issued_at": "2026-04-17T12:00:10Z", + "payload": { + "ku_id": "ku_4b3f7c2a9d8e1f05", + "sequence": 10, + "aggregate": { + "binds_receipts": [ + "sha256:2b06eb3385bcfa7bf29d59ae693c28124062fdb9b548a9710c1ee44f2eeae192", + "sha256:a54dde3a9dd2501476b0e9613990333371a29b2b669c9da02e348a86e6b2ff83", + "sha256:76cce8f734ae646b56f4df333a15a400f254dcacece06e7b2bf26c97438b8bbc", + "sha256:653a70f396a16d2114b42996305f514b35d5ba59e6b37527b6311aa4d9e79aeb", + "sha256:69eaa88bb7b4d0c66e9649096dae4aad20b84f7d9862b404f46a37e0a96e8240", + "sha256:18ac3cef4d85205ebe2886b8b94b368ae8d28585088762e91df80ab3031a42d6", + "sha256:b69cef75b1f0eeb36ece82755df449ccea4dde04915268c254fef33508ce016f", + "sha256:b744f32a94d8f1f7727e9020308a8b1db87aef8c80b53c7644398ae0078317bc", + "sha256:b0b54512737617227b1ac7569c06e71fbe8dd944011050c3ceaeac5e79526122" + ], + "round_count": 3, + "model_count": 4, + "deliberation_outcome": "synthesis_with_recorded_dissent" + }, + "previousReceiptHash": "sha256:b0b54512737617227b1ac7569c06e71fbe8dd944011050c3ceaeac5e79526122" + }, + "signature": "f160552fbdf28e326eacd639649fc8fe18af6dbe9a2d7a415fa424f4a638dedf77d7fa591a350fcf11ede65fbef07e3d7ea484b2ee2c7de47459097f75a03704" + } + ], + "expected_verification": { + "result": "MUST_REJECT", + "error": "verification key transported inside receipt without independent anchor", + "normative_reference": "draft-farley-acta-signed-receipts-02 Security Considerations" + } +} diff --git a/test-vectors/generate.mjs b/test-vectors/generate.mjs index afdeaa2..5beb41e 100644 --- a/test-vectors/generate.mjs +++ b/test-vectors/generate.mjs @@ -23,8 +23,10 @@ import { hexToBytes, sha256, } from '@veritasacta/artifacts'; -import { writeFileSync } from 'node:fs'; +import { ed25519 } from '@noble/curves/ed25519'; +import { existsSync, mkdirSync, writeFileSync } from 'node:fs'; import { createHash } from 'node:crypto'; +import { REJECTION_ERROR_EMBEDDED_KEY } from '../src/constants.js'; // ─── Deterministic seed ────────────────────────────────────────── const SEED = 'veritasacta:verify:test-vectors:2026-04-17'; @@ -336,6 +338,87 @@ function buildBundle() { }; } +function buildApsCrossVerifyArtifacts(bundle) { + const seed = sha256(new TextEncoder().encode('aps:veritasacta:ku-cross-verify:v1')); + const priv = bytesToHex(seed); + const pub = ed25519.getPublicKey(seed); + const publicKeyHex = bytesToHex(pub); + const kid = 'aps-ku-cross-verify-v1'; + const seedNote = 'derived from sha256("aps:veritasacta:ku-cross-verify:v1") — test key, NOT a production issuer'; + const issuer = 'aps:test:ku-cross-verify'; + + const receipt = { + receiptId: `dlr_va_${KU_ID}`, + timestamp: '2026-04-18T00:00:00Z', + decisionArtifactId: `veritasacta:ku:${KU_ID}`, + decisionType: 'knowledge_unit_deliberation', + contributingSources: bundle.receipts.map((r, i) => ({ + sourceId: r.issuer, + accessReceiptId: `sha256:${canonicalHash(r)}`, + derivationDepth: i + 1, + transformPath: ['aggregation'], + termsVersionAtAccess: 'veritasacta:knowledge-unit-bundle:v1', + lineageConfidence: 'complete', + compensationStatus: 'settled', + })), + lineageCompleteness: 'complete', + externalHopsPresent: true, + transformChain: ['aggregation'], + governingPurpose: 'research:academic', + jurisdictionContext: 'veritasacta:knowledge-unit-bundle:v1', + explanation: + `APS DecisionLineageReceipt attesting to VeritasActa Knowledge Unit ${KU_ID}. Bundle terminal aggregate sha256: ${canonicalHash(bundle.receipts.at(-1))}. Each entry in contributingSources commits to the JCS-canonical sha256 of one KU receipt; tampering any byte of any KU receipt invalidates the recorded accessReceiptId, breaking cross-layer integrity even though APS's Ed25519 signature over this DecisionLineageReceipt remains cryptographically valid.`, + }; + receipt.signature = bytesToHex(ed25519.sign(new TextEncoder().encode(canonicalize(receipt)), hexToBytes(priv))); + + const signingKey = { + kty: 'OKP', + crv: 'Ed25519', + use: 'sig', + issuer, + public_key_hex: publicKeyHex, + seed_note: seedNote, + }; + + const jwks = { + keys: [ + { + kty: 'OKP', + crv: 'Ed25519', + use: 'sig', + kid, + x: Buffer.from(pub).toString('base64url'), + issuer, + seed_note: seedNote, + }, + ], + }; + + const positive = JSON.parse(JSON.stringify(bundle)); + positive.external_receipts.aps.receipt = receipt; + positive.external_receipts.aps.verification_key_ref = `test-vectors/keys/aps-ku-cross-verify.jwks#${kid}`; + positive.external_receipts.aps.verifier_hint = + 'Verify APS DecisionLineageReceipt using the independently anchored key referenced by external_receipts.aps.verification_key_ref. Cross-layer integrity: each contributingSources[].accessReceiptId is the JCS-canonical sha256 of the corresponding entry in bundle.receipts.'; + + const negative = JSON.parse(JSON.stringify(bundle)); + negative.external_receipts.aps.receipt = receipt; + negative.external_receipts.aps.signing_key = signingKey; + negative.external_receipts.aps.verifier_hint = + 'Negative conformance fixture. A verifier MUST reject this bundle because the APS verification key is transported inside external_receipts.aps.signing_key without an independent anchor.'; + negative.external_receipts.aps.expected_result = { + verifier: '@veritasacta/verify', + result: 'MUST_REJECT', + error: REJECTION_ERROR_EMBEDDED_KEY, + }; + negative.expected_verification = { + result: 'MUST_REJECT', + error: REJECTION_ERROR_EMBEDDED_KEY, + normative_reference: 'draft-farley-acta-signed-receipts-02 Security Considerations', + }; + + return { positive, negative, jwks }; +} + // ═══════════════════════════════════════════════════════════════════ // 3. selective-disclosure-salted-commit.json // ═══════════════════════════════════════════════════════════════════ @@ -440,11 +523,30 @@ function buildSelectiveDisclosure() { const jcsVecs = buildJcsVectors(); const bundle = buildBundle(); +const aps = buildApsCrossVerifyArtifacts(bundle); const sdc = buildSelectiveDisclosure(); const outDir = process.argv[2] || '.'; +const forceNegative = process.argv.includes('--force-negative'); +mkdirSync(`${outDir}/keys`, { recursive: true }); writeFileSync(`${outDir}/jcs-test-vectors.json`, JSON.stringify(jcsVecs, null, 2) + '\n'); -writeFileSync(`${outDir}/cross-verify-bundle.json`, JSON.stringify(bundle, null, 2) + '\n'); +writeFileSync(`${outDir}/cross-verify-bundle.json`, JSON.stringify(aps.positive, null, 2) + '\n'); + +// Static-fixture preservation: cross-verify-embedded-key-bundle.json is the +// MUST-reject regression target for the v0.7+ enforcement landing. The +// generator does not own this fixture's evolution; if a generator change +// silently rewrites it, the fixture's value as a regression target collapses. +// Default behavior: skip if the file already exists. Pass --force-negative +// to explicitly regenerate (e.g. on first creation, or after a normative +// pin to REJECTION_ERROR_EMBEDDED_KEY changes). +const negativePath = `${outDir}/cross-verify-embedded-key-bundle.json`; +let negativeWritten = false; +if (!existsSync(negativePath) || forceNegative) { + writeFileSync(negativePath, JSON.stringify(aps.negative, null, 2) + '\n'); + negativeWritten = true; +} + +writeFileSync(`${outDir}/keys/aps-ku-cross-verify.jwks`, JSON.stringify(aps.jwks, null, 2) + '\n'); writeFileSync( `${outDir}/selective-disclosure-salted-commit.json`, JSON.stringify(sdc, null, 2) + '\n', @@ -452,5 +554,11 @@ writeFileSync( console.log('Wrote:'); console.log(` ${outDir}/jcs-test-vectors.json (${jcsVecs.vectors.length} cases)`); -console.log(` ${outDir}/cross-verify-bundle.json (${bundle.receipts.length} receipts, ${bundle.verification.signing_keys.length} keys)`); +console.log(` ${outDir}/cross-verify-bundle.json (${bundle.receipts.length} receipts, ${bundle.verification.signing_keys.length} keys, APS sidecar key)`); +if (negativeWritten) { + console.log(` ${outDir}/cross-verify-embedded-key-bundle.json (MUST-reject embedded-key fixture${forceNegative ? ', --force-negative' : ', first run'})`); +} else { + console.log(` ${outDir}/cross-verify-embedded-key-bundle.json (preserved, static; pass --force-negative to regenerate)`); +} +console.log(` ${outDir}/keys/aps-ku-cross-verify.jwks (APS sidecar key)`); console.log(` ${outDir}/selective-disclosure-salted-commit.json (${Object.keys(sdc.witness.disclosures).length} redacted fields)`); diff --git a/test-vectors/keys/aps-ku-cross-verify.jwks b/test-vectors/keys/aps-ku-cross-verify.jwks new file mode 100644 index 0000000..f31592f --- /dev/null +++ b/test-vectors/keys/aps-ku-cross-verify.jwks @@ -0,0 +1,13 @@ +{ + "keys": [ + { + "kty": "OKP", + "crv": "Ed25519", + "use": "sig", + "kid": "aps-ku-cross-verify-v1", + "x": "6RxrxbGuAFDbKoTtliVZ6XfMb_TLW9EVdwcb1_pgT6o", + "issuer": "aps:test:ku-cross-verify", + "seed_note": "derived from sha256(\"aps:veritasacta:ku-cross-verify:v1\") — test key, NOT a production issuer" + } + ] +}