@attested-intelligence/aga-verify 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # AGA Independent Verifier (`@attested-intelligence/aga-verify`)
2
+
3
+ Standalone verification of AGA **Evidence Bundles** using only standard
4
+ cryptographic primitives. **This verifier imports zero modules from the
5
+ AGA codebase** — it depends only on `@noble/ed25519` and `@noble/hashes`.
6
+
7
+ ## Why this exists
8
+
9
+ AGA claims that Evidence Bundles provide tamper-evident, offline-verifiable
10
+ proof of governance enforcement. This tool proves that claim is checkable by
11
+ **anyone**, with no trust in AGA's own code: it re-implements the complete
12
+ verification from scratch and runs against a bundle you provide.
13
+
14
+ ## Quickstart
15
+
16
+ ```bash
17
+ # verify any AGA evidence bundle
18
+ npx @attested-intelligence/aga-verify <bundle.json>
19
+
20
+ # or smoke-test against the bundled canonical example
21
+ npx @attested-intelligence/aga-verify example-bundle.json
22
+ ```
23
+
24
+ Exit code is `0` on `VERIFIED`, `1` on `FAILED` — usable directly in CI.
25
+
26
+ `example-bundle.json` is a real, signed evidence bundle produced by the
27
+ reference implementation (the AI-agent governance scenario); it is shipped so
28
+ a third party can confirm the verifier end-to-end in one command.
29
+
30
+ ## What it verifies
31
+
32
+ 1. **Artifact signature** — Ed25519 over RFC 8785 canonical JSON.
33
+ 2. **Receipt signatures** — Ed25519 for each enforcement receipt.
34
+ 3. **Merkle inclusion proofs** — structural-metadata leaf hashes vs. the checkpoint root.
35
+ 4. **Checkpoint anchor** — optional; requires network access (skipped offline).
36
+
37
+ Steps 1–3 are fully offline. Step 4 is optional.
38
+
39
+ ## Independence guarantee
40
+
41
+ The published package's only runtime dependencies are `@noble/ed25519` and
42
+ `@noble/hashes`. It contains no AGA code and makes no network calls for steps
43
+ 1–3. You can confirm this from the dependency tree (`npm ls`) and by reading
44
+ the single source file, `verify.ts`.
45
+
46
+ ## From source
47
+
48
+ ```bash
49
+ npm install
50
+ npm test # vitest, including the zero-AGA-imports assertion
51
+ npm run build # bundles verify.ts -> dist/aga-verify.mjs (esbuild)
52
+ node dist/aga-verify.mjs example-bundle.json
53
+ ```
54
+
55
+ ---
56
+ Attested Intelligence Holdings LLC. Implements the AGA four-step offline verification process.
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env node
2
+
3
+ // verify.ts
4
+ import * as ed from "@noble/ed25519";
5
+ import { sha512 } from "@noble/hashes/sha512";
6
+ import { sha256 } from "@noble/hashes/sha256";
7
+ import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
8
+ ed.etc.sha512Sync = (...m) => {
9
+ const total = m.reduce((n, a) => n + a.length, 0);
10
+ const buf = new Uint8Array(total);
11
+ let off = 0;
12
+ for (const a of m) {
13
+ buf.set(a, off);
14
+ off += a.length;
15
+ }
16
+ return sha512(buf);
17
+ };
18
+ var enc = new TextEncoder();
19
+ function deepSortKeys(obj) {
20
+ if (obj === null || obj === void 0 || typeof obj !== "object") return obj;
21
+ if (Array.isArray(obj)) return obj.map(deepSortKeys);
22
+ if (obj instanceof Uint8Array) return obj;
23
+ const sorted = {};
24
+ for (const key of Object.keys(obj).sort()) {
25
+ sorted[key] = deepSortKeys(obj[key]);
26
+ }
27
+ return sorted;
28
+ }
29
+ function canonicalize(obj) {
30
+ return JSON.stringify(deepSortKeys(obj));
31
+ }
32
+ function sha256Hex(data) {
33
+ return bytesToHex(sha256(enc.encode(data)));
34
+ }
35
+ function verifyEd25519(sigBase64, message, publicKeyHex) {
36
+ try {
37
+ const sig = new Uint8Array(Buffer.from(sigBase64, "base64"));
38
+ const pk = hexToBytes(publicKeyHex);
39
+ return ed.verify(sig, enc.encode(message), pk);
40
+ } catch {
41
+ return false;
42
+ }
43
+ }
44
+ function merkleParentHash(left, right) {
45
+ return sha256Hex(left + right);
46
+ }
47
+ function verifyArtifactSignature(artifact) {
48
+ const { signature, ...unsigned } = artifact;
49
+ const canonical = canonicalize(unsigned);
50
+ return verifyEd25519(signature, canonical, artifact.issuer_identifier);
51
+ }
52
+ function verifyReceiptSignatures(receipts, portalPublicKey) {
53
+ return receipts.map((receipt) => {
54
+ const { portal_signature, ...unsigned } = receipt;
55
+ const canonical = canonicalize(unsigned);
56
+ return verifyEd25519(portal_signature, canonical, portalPublicKey);
57
+ });
58
+ }
59
+ function verifyMerkleProofs(proofs, checkpointRoot) {
60
+ return proofs.map((proof) => {
61
+ let hash = proof.leafHash;
62
+ for (const sibling of proof.siblings) {
63
+ hash = sibling.position === "left" ? merkleParentHash(sibling.hash, hash) : merkleParentHash(hash, sibling.hash);
64
+ }
65
+ return hash === checkpointRoot;
66
+ });
67
+ }
68
+ function verifyCheckpointAnchor(_checkpoint) {
69
+ return "SKIPPED";
70
+ }
71
+ function validateBundleShape(b) {
72
+ if (b === null || typeof b !== "object" || Array.isArray(b)) return "not a JSON object";
73
+ if (typeof b.artifact !== "object" || b.artifact === null) return 'missing "artifact" object';
74
+ if (typeof b.artifact.signature !== "string") return 'missing "artifact.signature"';
75
+ if (typeof b.artifact.issuer_identifier !== "string") return 'missing "artifact.issuer_identifier"';
76
+ if (!Array.isArray(b.receipts)) return 'missing "receipts" array';
77
+ if (typeof b.public_key !== "string") return 'missing "public_key"';
78
+ if (!Array.isArray(b.merkle_proofs)) return 'missing "merkle_proofs" array';
79
+ if (typeof b.checkpoint_reference !== "object" || b.checkpoint_reference === null || typeof b.checkpoint_reference.merkle_root !== "string") {
80
+ return 'missing "checkpoint_reference.merkle_root"';
81
+ }
82
+ return null;
83
+ }
84
+ function verifyEvidenceBundle(bundleJson) {
85
+ const errors = [];
86
+ let bundle;
87
+ try {
88
+ bundle = JSON.parse(bundleJson);
89
+ } catch {
90
+ return {
91
+ step1_artifact_sig: false,
92
+ step2_receipt_sigs: false,
93
+ step3_merkle_proofs: false,
94
+ step4_anchor: "SKIPPED",
95
+ overall: false,
96
+ errors: ["Failed to parse bundle JSON"],
97
+ details: { receipt_results: [], proof_results: [] }
98
+ };
99
+ }
100
+ const shapeError = validateBundleShape(bundle);
101
+ if (shapeError) {
102
+ return {
103
+ step1_artifact_sig: false,
104
+ step2_receipt_sigs: false,
105
+ step3_merkle_proofs: false,
106
+ step4_anchor: "SKIPPED",
107
+ overall: false,
108
+ errors: [`unrecognized evidence-bundle format: ${shapeError}`],
109
+ details: { receipt_results: [], proof_results: [] }
110
+ };
111
+ }
112
+ const step1 = verifyArtifactSignature(bundle.artifact);
113
+ if (!step1) errors.push("Artifact signature verification failed");
114
+ const receiptResults = verifyReceiptSignatures(bundle.receipts, bundle.public_key);
115
+ const step2 = receiptResults.every((r) => r);
116
+ receiptResults.forEach((r, i) => {
117
+ if (!r) errors.push(`Receipt ${bundle.receipts[i].receipt_id} signature failed`);
118
+ });
119
+ const proofResults = verifyMerkleProofs(bundle.merkle_proofs, bundle.checkpoint_reference.merkle_root);
120
+ const step3 = proofResults.length === 0 ? true : proofResults.every((r) => r);
121
+ proofResults.forEach((r, i) => {
122
+ if (!r) errors.push(`Merkle proof ${i} failed`);
123
+ });
124
+ const step4 = verifyCheckpointAnchor(bundle.checkpoint_reference);
125
+ return {
126
+ step1_artifact_sig: step1,
127
+ step2_receipt_sigs: step2,
128
+ step3_merkle_proofs: step3,
129
+ step4_anchor: step4,
130
+ overall: step1 && step2 && step3,
131
+ errors,
132
+ details: { receipt_results: receiptResults, proof_results: proofResults }
133
+ };
134
+ }
135
+ if (typeof process !== "undefined" && process.argv[1]?.includes("verify")) {
136
+ const { readFileSync } = await import("node:fs");
137
+ const bundlePath = process.argv[2];
138
+ if (!bundlePath) {
139
+ console.error("Usage: npx tsx verify.ts <bundle.json>");
140
+ process.exit(1);
141
+ }
142
+ const bundleJson = readFileSync(bundlePath, "utf-8");
143
+ const result = verifyEvidenceBundle(bundleJson);
144
+ console.log("\nAGA Independent Verifier\n");
145
+ console.log(`Step 1 - Artifact signature: ${result.step1_artifact_sig ? "PASS" : "FAIL"}`);
146
+ console.log(`Step 2 - Receipt signatures: ${result.step2_receipt_sigs ? "PASS" : "FAIL"} (${result.details.receipt_results.filter((r) => r).length}/${result.details.receipt_results.length})`);
147
+ console.log(`Step 3 - Merkle inclusion proofs: ${result.step3_merkle_proofs ? "PASS" : "FAIL"} (${result.details.proof_results.filter((r) => r).length}/${result.details.proof_results.length})`);
148
+ console.log(`Step 4 - Checkpoint anchor: ${result.step4_anchor}`);
149
+ console.log(`
150
+ OVERALL: ${result.overall ? "VERIFIED" : "FAILED"}`);
151
+ if (result.errors.length) {
152
+ console.log("\nErrors:");
153
+ result.errors.forEach((e) => console.log(` - ${e}`));
154
+ }
155
+ process.exit(result.overall ? 0 : 1);
156
+ }
157
+ export {
158
+ verifyArtifactSignature,
159
+ verifyCheckpointAnchor,
160
+ verifyEvidenceBundle,
161
+ verifyMerkleProofs,
162
+ verifyReceiptSignatures
163
+ };
@@ -0,0 +1,284 @@
1
+ {
2
+ "artifact": {
3
+ "schema_version": "1.0.0",
4
+ "protocol_version": "1.0.0",
5
+ "subject_identifier": {
6
+ "bytes_hash": "d75f39ce7c674f6e63ed0d36113262a5b3411c7974c4962f8a4b545996edd7d3",
7
+ "metadata_hash": "88fa13c6d55d6642c88dc4cc49ed53be26bafd26a3d67fdfd0b70c8ee39dbd63"
8
+ },
9
+ "policy_reference": "c5090e8677afc35133da645aa2aecdcd36cd67184dfcfe031f1c55d5d1294020",
10
+ "policy_version": 4,
11
+ "sealed_hash": "1dd8ed34d6cd1c1c5cec647d26cc4c3676d7796a729376bb3fcf26b2a6edba33",
12
+ "seal_salt": "910887666ac8dc2b7db5695fd0186b4e",
13
+ "issued_timestamp": "2026-06-02T08:30:36.164Z",
14
+ "effective_timestamp": "2026-06-02T08:30:36.164Z",
15
+ "expiration_timestamp": null,
16
+ "issuer_identifier": "ac92dd48402bf3255177df55198c449dc61e36c97a3ed62e81462e72f741b311",
17
+ "enforcement_parameters": {
18
+ "measurement_cadence_ms": 200,
19
+ "ttl_seconds": 3600,
20
+ "enforcement_triggers": [
21
+ "QUARANTINE",
22
+ "TERMINATE",
23
+ "SAFE_STATE"
24
+ ],
25
+ "re_attestation_required": true,
26
+ "measurement_types": [
27
+ "EXECUTABLE_IMAGE",
28
+ "LOADED_MODULES",
29
+ "CONFIG_MANIFEST"
30
+ ],
31
+ "behavioral_baseline": {
32
+ "permitted_tools": [
33
+ "web_search",
34
+ "code_execute",
35
+ "file_read",
36
+ "summarize"
37
+ ],
38
+ "forbidden_sequences": [
39
+ [
40
+ "file_read",
41
+ "web_search",
42
+ "code_execute"
43
+ ]
44
+ ],
45
+ "rate_limits": {
46
+ "web_search": 20,
47
+ "code_execute": 10,
48
+ "file_read": 30,
49
+ "summarize": 50
50
+ }
51
+ }
52
+ },
53
+ "disclosure_policy": {
54
+ "claims_taxonomy": [
55
+ {
56
+ "claim_id": "agent.model_weights_hash",
57
+ "sensitivity": "S4_CRITICAL",
58
+ "substitutes": [
59
+ "agent.model_family",
60
+ "agent.model_generation"
61
+ ],
62
+ "inference_risks": [],
63
+ "permitted_modes": []
64
+ },
65
+ {
66
+ "claim_id": "agent.model_family",
67
+ "sensitivity": "S2_MODERATE",
68
+ "substitutes": [
69
+ "agent.model_generation"
70
+ ],
71
+ "inference_risks": [],
72
+ "permitted_modes": [
73
+ "REVEAL_MIN",
74
+ "REVEAL_FULL"
75
+ ]
76
+ },
77
+ {
78
+ "claim_id": "agent.model_generation",
79
+ "sensitivity": "S1_LOW",
80
+ "substitutes": [],
81
+ "inference_risks": [],
82
+ "permitted_modes": [
83
+ "PROOF_ONLY",
84
+ "REVEAL_MIN",
85
+ "REVEAL_FULL"
86
+ ]
87
+ }
88
+ ],
89
+ "substitution_rules": []
90
+ },
91
+ "evidence_commitments": [
92
+ {
93
+ "commitment": "3a7cafa939fa3732b4d36875b8909b467a2d063ce01c617b1f491983f9b8de71",
94
+ "salt": "eb5050aaff095529b52e03f920f32736",
95
+ "label": "model_card"
96
+ },
97
+ {
98
+ "commitment": "8f6a61abea33aca30d67af6fee22780ac2b689058882f70f8723c9f37d8da8bd",
99
+ "salt": "edba40897cd4d45923f00891c11d7746",
100
+ "label": "scope_approval"
101
+ }
102
+ ],
103
+ "signature": "X9IEtvUlU8AUixd/ZTbWqUGFhfqEbeQKYZy3/nzKhbj43uYS678X8BV+7hrd26Ts1m2hbUa+JHPAqzgj+z5YDg=="
104
+ },
105
+ "receipts": [
106
+ {
107
+ "receipt_id": "c77294b7-567f-4019-bc82-1db8f8b41a36",
108
+ "subject_identifier": {
109
+ "bytes_hash": "d75f39ce7c674f6e63ed0d36113262a5b3411c7974c4962f8a4b545996edd7d3",
110
+ "metadata_hash": "88fa13c6d55d6642c88dc4cc49ed53be26bafd26a3d67fdfd0b70c8ee39dbd63"
111
+ },
112
+ "artifact_reference": "c801497e4020ea3913e94541c2c869fac9422735686a6d3c4b75c4298145b237",
113
+ "current_hash": "d75f39ce7c674f6e63ed0d36113262a5b3411c7974c4962f8a4b545996edd7d3||88fa13c6d55d6642c88dc4cc49ed53be26bafd26a3d67fdfd0b70c8ee39dbd63",
114
+ "sealed_hash": "d75f39ce7c674f6e63ed0d36113262a5b3411c7974c4962f8a4b545996edd7d3||88fa13c6d55d6642c88dc4cc49ed53be26bafd26a3d67fdfd0b70c8ee39dbd63",
115
+ "drift_detected": false,
116
+ "drift_description": null,
117
+ "enforcement_action": null,
118
+ "measurement_type": "EXECUTABLE_IMAGE",
119
+ "timestamp": "2026-06-02T08:30:36.178Z",
120
+ "sequence_number": 0,
121
+ "previous_leaf_hash": "dea5872156dd67a5cf43b9124ce0e8d0d33090a4186a98f573c55666ea172739",
122
+ "portal_signature": "TaIz2Tb0TCE34lKD0UKdTqpQzThourYzGZHhpVPajy6C2bBA3oJsbE9BpZhx3utm62O1erKYOlT/WWUAVCGjDg=="
123
+ },
124
+ {
125
+ "receipt_id": "a5900acb-2d66-4235-9347-d7da49d8dc0b",
126
+ "subject_identifier": {
127
+ "bytes_hash": "d75f39ce7c674f6e63ed0d36113262a5b3411c7974c4962f8a4b545996edd7d3",
128
+ "metadata_hash": "88fa13c6d55d6642c88dc4cc49ed53be26bafd26a3d67fdfd0b70c8ee39dbd63"
129
+ },
130
+ "artifact_reference": "c801497e4020ea3913e94541c2c869fac9422735686a6d3c4b75c4298145b237",
131
+ "current_hash": "d75f39ce7c674f6e63ed0d36113262a5b3411c7974c4962f8a4b545996edd7d3||88fa13c6d55d6642c88dc4cc49ed53be26bafd26a3d67fdfd0b70c8ee39dbd63",
132
+ "sealed_hash": "d75f39ce7c674f6e63ed0d36113262a5b3411c7974c4962f8a4b545996edd7d3||88fa13c6d55d6642c88dc4cc49ed53be26bafd26a3d67fdfd0b70c8ee39dbd63",
133
+ "drift_detected": false,
134
+ "drift_description": null,
135
+ "enforcement_action": null,
136
+ "measurement_type": "EXECUTABLE_IMAGE",
137
+ "timestamp": "2026-06-02T08:30:36.180Z",
138
+ "sequence_number": 1,
139
+ "previous_leaf_hash": "5ed1a416eb4c97b358cfceb1b3a0a662a698f1be2425b0063f546e4a2402f398",
140
+ "portal_signature": "lAhEHV/upEaWALRHCaqVc+thPmKdHHlGDGiFYvdv1TTtGzZLQ8S5Zjgm3jqGVactI+ntlwIbdNp6qhOY+CFkBA=="
141
+ },
142
+ {
143
+ "receipt_id": "e06a3914-392b-4b6d-870b-48d9058cde2c",
144
+ "subject_identifier": {
145
+ "bytes_hash": "d75f39ce7c674f6e63ed0d36113262a5b3411c7974c4962f8a4b545996edd7d3",
146
+ "metadata_hash": "88fa13c6d55d6642c88dc4cc49ed53be26bafd26a3d67fdfd0b70c8ee39dbd63"
147
+ },
148
+ "artifact_reference": "c801497e4020ea3913e94541c2c869fac9422735686a6d3c4b75c4298145b237",
149
+ "current_hash": "d75f39ce7c674f6e63ed0d36113262a5b3411c7974c4962f8a4b545996edd7d3||88fa13c6d55d6642c88dc4cc49ed53be26bafd26a3d67fdfd0b70c8ee39dbd63",
150
+ "sealed_hash": "d75f39ce7c674f6e63ed0d36113262a5b3411c7974c4962f8a4b545996edd7d3||88fa13c6d55d6642c88dc4cc49ed53be26bafd26a3d67fdfd0b70c8ee39dbd63",
151
+ "drift_detected": false,
152
+ "drift_description": null,
153
+ "enforcement_action": null,
154
+ "measurement_type": "EXECUTABLE_IMAGE",
155
+ "timestamp": "2026-06-02T08:30:36.182Z",
156
+ "sequence_number": 2,
157
+ "previous_leaf_hash": "893cefa6bddc9c8a40c6655e938f912cec5daef0f6a9074bd4d27ca09ad4af9c",
158
+ "portal_signature": "HGpzkE4CK1QNXngHafIe4sr7ydqs7ZY0ZD7+me8vPw4RxtqBMAWfi200A9dC5OvDcN4zwZcwWF51oqBNk1l2Cw=="
159
+ },
160
+ {
161
+ "receipt_id": "0040e0cc-e560-4014-86d0-1b294f3d4b4c",
162
+ "subject_identifier": {
163
+ "bytes_hash": "d75f39ce7c674f6e63ed0d36113262a5b3411c7974c4962f8a4b545996edd7d3",
164
+ "metadata_hash": "88fa13c6d55d6642c88dc4cc49ed53be26bafd26a3d67fdfd0b70c8ee39dbd63"
165
+ },
166
+ "artifact_reference": "c801497e4020ea3913e94541c2c869fac9422735686a6d3c4b75c4298145b237",
167
+ "current_hash": "950e5c3896a95e42d179500a6b61543f707285b8ee889327eb1be92daeec7f60",
168
+ "sealed_hash": "d75f39ce7c674f6e63ed0d36113262a5b3411c7974c4962f8a4b545996edd7d3||88fa13c6d55d6642c88dc4cc49ed53be26bafd26a3d67fdfd0b70c8ee39dbd63",
169
+ "drift_detected": true,
170
+ "drift_description": "Behavioral drift: forbidden tool sequence (data exfiltration pattern)",
171
+ "enforcement_action": "TERMINATE",
172
+ "measurement_type": "EXECUTABLE_IMAGE",
173
+ "timestamp": "2026-06-02T08:30:36.192Z",
174
+ "sequence_number": 3,
175
+ "previous_leaf_hash": "838f59b7c940b3061018f6c70c13c2e2ef17554659fb48dafd33af267d5d2026",
176
+ "portal_signature": "vMEgOydXm63Hgwm4+A75VKYsJ0BceNfR/juEEXmQh9/KmXpVDlTuXoHi3EHOUOv/t8Qey7rxfuldAq8hp+FkCA=="
177
+ }
178
+ ],
179
+ "merkle_proofs": [
180
+ {
181
+ "leafHash": "5ed1a416eb4c97b358cfceb1b3a0a662a698f1be2425b0063f546e4a2402f398",
182
+ "leafIndex": 2,
183
+ "siblings": [
184
+ {
185
+ "hash": "893cefa6bddc9c8a40c6655e938f912cec5daef0f6a9074bd4d27ca09ad4af9c",
186
+ "position": "right"
187
+ },
188
+ {
189
+ "hash": "db44e24f99b8086c7194eabcd240ef2b38f9d1214d49cc31337c45462a494082",
190
+ "position": "left"
191
+ },
192
+ {
193
+ "hash": "0463d91f9baebddee57414e78c8d22507a9d33a0ce5a7bfa882e2ccb4c98a13c",
194
+ "position": "right"
195
+ },
196
+ {
197
+ "hash": "71b83667104222412e522e83ce89978fbde04d0c02eabf0e1e759aadbde0dac2",
198
+ "position": "right"
199
+ }
200
+ ],
201
+ "root": "e1cc28411c41f749ab3ff8160c465bca538d3c1c0883eac56c3821ff1c7574c9"
202
+ },
203
+ {
204
+ "leafHash": "893cefa6bddc9c8a40c6655e938f912cec5daef0f6a9074bd4d27ca09ad4af9c",
205
+ "leafIndex": 3,
206
+ "siblings": [
207
+ {
208
+ "hash": "5ed1a416eb4c97b358cfceb1b3a0a662a698f1be2425b0063f546e4a2402f398",
209
+ "position": "left"
210
+ },
211
+ {
212
+ "hash": "db44e24f99b8086c7194eabcd240ef2b38f9d1214d49cc31337c45462a494082",
213
+ "position": "left"
214
+ },
215
+ {
216
+ "hash": "0463d91f9baebddee57414e78c8d22507a9d33a0ce5a7bfa882e2ccb4c98a13c",
217
+ "position": "right"
218
+ },
219
+ {
220
+ "hash": "71b83667104222412e522e83ce89978fbde04d0c02eabf0e1e759aadbde0dac2",
221
+ "position": "right"
222
+ }
223
+ ],
224
+ "root": "e1cc28411c41f749ab3ff8160c465bca538d3c1c0883eac56c3821ff1c7574c9"
225
+ },
226
+ {
227
+ "leafHash": "56bda530fe8e6813c6fef0577596c9f35aa21f8f90d6f8593d3831b39e565fc3",
228
+ "leafIndex": 4,
229
+ "siblings": [
230
+ {
231
+ "hash": "6f98d48d7117d51064d72ff13c2c84d379c906f540e370d01e1759e957653788",
232
+ "position": "right"
233
+ },
234
+ {
235
+ "hash": "74d7296252184052fcf08c23c6c60ef6efd34c86e743c441886c3f44fbd3ef28",
236
+ "position": "right"
237
+ },
238
+ {
239
+ "hash": "36a5c22125121fb0f9c7645f06454c48151970c297942e9025d2e8a0d0bc2a2b",
240
+ "position": "left"
241
+ },
242
+ {
243
+ "hash": "71b83667104222412e522e83ce89978fbde04d0c02eabf0e1e759aadbde0dac2",
244
+ "position": "right"
245
+ }
246
+ ],
247
+ "root": "e1cc28411c41f749ab3ff8160c465bca538d3c1c0883eac56c3821ff1c7574c9"
248
+ },
249
+ {
250
+ "leafHash": "13d4765f15b72d5e4192fc6f3c2efcc2d46b00df515d86a23b4c38c896a415b3",
251
+ "leafIndex": 9,
252
+ "siblings": [
253
+ {
254
+ "hash": "838f59b7c940b3061018f6c70c13c2e2ef17554659fb48dafd33af267d5d2026",
255
+ "position": "left"
256
+ },
257
+ {
258
+ "hash": "69479007abe86c6e09f6453dfa2762996a3cdc10b9df03e31b477958898877c8",
259
+ "position": "right"
260
+ },
261
+ {
262
+ "hash": "1b19cbeb9a5ecdb1c209447c9292a00d8699a18211fa3a97d0dc9776f9c64575",
263
+ "position": "right"
264
+ },
265
+ {
266
+ "hash": "e9599515a3379cc1ae106b51c80424db87db3c8ee57855f282843afdc12eda40",
267
+ "position": "left"
268
+ }
269
+ ],
270
+ "root": "e1cc28411c41f749ab3ff8160c465bca538d3c1c0883eac56c3821ff1c7574c9"
271
+ }
272
+ ],
273
+ "checkpoint_reference": {
274
+ "merkle_root": "e1cc28411c41f749ab3ff8160c465bca538d3c1c0883eac56c3821ff1c7574c9",
275
+ "batch_start_sequence": 0,
276
+ "batch_end_sequence": 9,
277
+ "anchor_network": "local",
278
+ "transaction_id": "local:04bb72ce-9af3-4406-95c9-0ad269d87696",
279
+ "timestamp": "2026-06-02T08:30:36.195Z"
280
+ },
281
+ "public_key": "6a65398e54dcecb4a8a3a38d351ce4ba947bae215a78d38a259959d609abd4fe",
282
+ "verification_tier": "GOLD",
283
+ "bundle_signature": "O6erEOh0UwH9vIEpkrdRKJl3wYb6d8RlS30I4jXeV+aKQOFSaOHuc4JF1KM73pLcGpqEv5c3slDxEs4loinMCg=="
284
+ }
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@attested-intelligence/aga-verify",
3
+ "version": "1.0.0",
4
+ "description": "Standalone, AGA-independent verifier for AGA Evidence Bundles (Ed25519 + SHA-256 via @noble; zero AGA imports).",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Attested Intelligence Holdings LLC",
8
+ "homepage": "https://attestedintelligence.com/technology",
9
+ "keywords": [
10
+ "aga",
11
+ "evidence-bundle",
12
+ "verifier",
13
+ "ed25519",
14
+ "merkle",
15
+ "offline-verification",
16
+ "attested-intelligence",
17
+ "ai-governance"
18
+ ],
19
+ "bin": {
20
+ "aga-verify": "dist/aga-verify.mjs"
21
+ },
22
+ "files": [
23
+ "dist/",
24
+ "verify.ts",
25
+ "example-bundle.json",
26
+ "README.md"
27
+ ],
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "scripts": {
32
+ "build": "node build.mjs",
33
+ "test": "vitest run",
34
+ "prepublishOnly": "node build.mjs && vitest run"
35
+ },
36
+ "dependencies": {
37
+ "@noble/ed25519": "^2.1.0",
38
+ "@noble/hashes": "^1.7.0"
39
+ },
40
+ "devDependencies": {
41
+ "esbuild": "^0.27.4",
42
+ "tsx": "^4.19.0",
43
+ "vitest": "^2.1.0"
44
+ }
45
+ }
package/verify.ts ADDED
@@ -0,0 +1,240 @@
1
+ /**
2
+ * AGA Independent Verifier
3
+ *
4
+ * Standalone verification of AGA Evidence Bundles using ONLY standard
5
+ * cryptographic libraries. This verifier imports ZERO modules from the
6
+ * AGA codebase (../src/).
7
+ *
8
+ * Implements the full 4-step verification process:
9
+ * 1. Verify artifact signature (Ed25519 over RFC 8785 canonical JSON)
10
+ * 2. Verify each receipt signature (Ed25519)
11
+ * 3. Verify Merkle inclusion proofs (structural metadata leaf hashes vs checkpoint root)
12
+ * 4. (Optional) Verify checkpoint anchor
13
+ *
14
+ * Steps 1-3 work fully offline. Step 4 is optional.
15
+ *
16
+ * Attested Intelligence Holdings LLC
17
+ */
18
+ import * as ed from '@noble/ed25519';
19
+ import { sha512 } from '@noble/hashes/sha512';
20
+ import { sha256 } from '@noble/hashes/sha256';
21
+ import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
22
+
23
+ // ── Ed25519 setup ────────────────────────────────────────────
24
+ ed.etc.sha512Sync = (...m: Uint8Array[]) => {
25
+ const total = m.reduce((n, a) => n + a.length, 0);
26
+ const buf = new Uint8Array(total);
27
+ let off = 0;
28
+ for (const a of m) { buf.set(a, off); off += a.length; }
29
+ return sha512(buf);
30
+ };
31
+
32
+ const enc = new TextEncoder();
33
+
34
+ // ── Types (reimplemented, no AGA imports) ────────────────────
35
+
36
+ export interface VerificationResult {
37
+ step1_artifact_sig: boolean;
38
+ step2_receipt_sigs: boolean;
39
+ step3_merkle_proofs: boolean;
40
+ step4_anchor: 'VERIFIED' | 'SKIPPED';
41
+ overall: boolean;
42
+ errors: string[];
43
+ details: {
44
+ receipt_results: boolean[];
45
+ proof_results: boolean[];
46
+ };
47
+ }
48
+
49
+ interface MerkleProof {
50
+ leafHash: string;
51
+ leafIndex: number;
52
+ siblings: Array<{ hash: string; position: 'left' | 'right' }>;
53
+ root: string;
54
+ }
55
+
56
+ interface EvidenceBundle {
57
+ artifact: Record<string, unknown> & { signature: string; issuer_identifier: string };
58
+ receipts: Array<Record<string, unknown> & { portal_signature: string; receipt_id: string }>;
59
+ merkle_proofs: MerkleProof[];
60
+ checkpoint_reference: { merkle_root: string; [key: string]: unknown };
61
+ public_key: string;
62
+ bundle_signature: string;
63
+ verification_tier?: string;
64
+ }
65
+
66
+ // ── Crypto helpers (reimplemented from scratch) ──────────────
67
+
68
+ function deepSortKeys(obj: unknown): unknown {
69
+ if (obj === null || obj === undefined || typeof obj !== 'object') return obj;
70
+ if (Array.isArray(obj)) return obj.map(deepSortKeys);
71
+ if (obj instanceof Uint8Array) return obj;
72
+ const sorted: Record<string, unknown> = {};
73
+ for (const key of Object.keys(obj as Record<string, unknown>).sort()) {
74
+ sorted[key] = deepSortKeys((obj as Record<string, unknown>)[key]);
75
+ }
76
+ return sorted;
77
+ }
78
+
79
+ function canonicalize(obj: unknown): string {
80
+ return JSON.stringify(deepSortKeys(obj));
81
+ }
82
+
83
+ function sha256Hex(data: string): string {
84
+ return bytesToHex(sha256(enc.encode(data)));
85
+ }
86
+
87
+ function verifyEd25519(sigBase64: string, message: string, publicKeyHex: string): boolean {
88
+ try {
89
+ const sig = new Uint8Array(Buffer.from(sigBase64, 'base64'));
90
+ const pk = hexToBytes(publicKeyHex);
91
+ return ed.verify(sig, enc.encode(message), pk);
92
+ } catch { return false; }
93
+ }
94
+
95
+ function merkleParentHash(left: string, right: string): string {
96
+ return sha256Hex(left + right);
97
+ }
98
+
99
+ // ── Step 1: Verify artifact signature (Ed25519) ─────────────
100
+
101
+ export function verifyArtifactSignature(artifact: EvidenceBundle['artifact']): boolean {
102
+ const { signature, ...unsigned } = artifact;
103
+ const canonical = canonicalize(unsigned);
104
+ return verifyEd25519(signature, canonical, artifact.issuer_identifier);
105
+ }
106
+
107
+ // ── Step 2: Verify each receipt signature (Ed25519) ──────────
108
+
109
+ export function verifyReceiptSignatures(receipts: EvidenceBundle['receipts'], portalPublicKey: string): boolean[] {
110
+ return receipts.map(receipt => {
111
+ const { portal_signature, ...unsigned } = receipt;
112
+ const canonical = canonicalize(unsigned);
113
+ return verifyEd25519(portal_signature, canonical, portalPublicKey);
114
+ });
115
+ }
116
+
117
+ // ── Step 3: Verify Merkle inclusion proofs ───────────────────
118
+
119
+ export function verifyMerkleProofs(proofs: MerkleProof[], checkpointRoot: string): boolean[] {
120
+ return proofs.map(proof => {
121
+ let hash = proof.leafHash;
122
+ for (const sibling of proof.siblings) {
123
+ hash = sibling.position === 'left'
124
+ ? merkleParentHash(sibling.hash, hash)
125
+ : merkleParentHash(hash, sibling.hash);
126
+ }
127
+ return hash === checkpointRoot;
128
+ });
129
+ }
130
+
131
+ // ── Step 4 (optional): Verify checkpoint anchor ─────────────
132
+
133
+ export function verifyCheckpointAnchor(_checkpoint: Record<string, unknown>): 'VERIFIED' | 'SKIPPED' {
134
+ // Offline mode - no network access to verify on-chain anchor
135
+ return 'SKIPPED';
136
+ }
137
+
138
+ // ── Main entry point ─────────────────────────────────────────
139
+
140
+ // validateBundleShape returns a human-readable reason if the parsed value is
141
+ // not a recognizable AGA evidence bundle, or null if the required fields are
142
+ // present. This lets the verifier fail cleanly on a wrong-format file instead
143
+ // of throwing on a missing field.
144
+ function validateBundleShape(b: any): string | null {
145
+ if (b === null || typeof b !== 'object' || Array.isArray(b)) return 'not a JSON object';
146
+ if (typeof b.artifact !== 'object' || b.artifact === null) return 'missing "artifact" object';
147
+ if (typeof b.artifact.signature !== 'string') return 'missing "artifact.signature"';
148
+ if (typeof b.artifact.issuer_identifier !== 'string') return 'missing "artifact.issuer_identifier"';
149
+ if (!Array.isArray(b.receipts)) return 'missing "receipts" array';
150
+ if (typeof b.public_key !== 'string') return 'missing "public_key"';
151
+ if (!Array.isArray(b.merkle_proofs)) return 'missing "merkle_proofs" array';
152
+ if (typeof b.checkpoint_reference !== 'object' || b.checkpoint_reference === null ||
153
+ typeof b.checkpoint_reference.merkle_root !== 'string') {
154
+ return 'missing "checkpoint_reference.merkle_root"';
155
+ }
156
+ return null;
157
+ }
158
+
159
+ export function verifyEvidenceBundle(bundleJson: string): VerificationResult {
160
+ const errors: string[] = [];
161
+ let bundle: EvidenceBundle;
162
+
163
+ try {
164
+ bundle = JSON.parse(bundleJson);
165
+ } catch {
166
+ return {
167
+ step1_artifact_sig: false, step2_receipt_sigs: false,
168
+ step3_merkle_proofs: false, step4_anchor: 'SKIPPED',
169
+ overall: false, errors: ['Failed to parse bundle JSON'],
170
+ details: { receipt_results: [], proof_results: [] },
171
+ };
172
+ }
173
+
174
+ const shapeError = validateBundleShape(bundle);
175
+ if (shapeError) {
176
+ return {
177
+ step1_artifact_sig: false, step2_receipt_sigs: false,
178
+ step3_merkle_proofs: false, step4_anchor: 'SKIPPED',
179
+ overall: false, errors: [`unrecognized evidence-bundle format: ${shapeError}`],
180
+ details: { receipt_results: [], proof_results: [] },
181
+ };
182
+ }
183
+
184
+ // Step 1: Artifact signature
185
+ const step1 = verifyArtifactSignature(bundle.artifact);
186
+ if (!step1) errors.push('Artifact signature verification failed');
187
+
188
+ // Step 2: Receipt signatures
189
+ const receiptResults = verifyReceiptSignatures(bundle.receipts, bundle.public_key);
190
+ const step2 = receiptResults.every(r => r);
191
+ receiptResults.forEach((r, i) => {
192
+ if (!r) errors.push(`Receipt ${bundle.receipts[i].receipt_id} signature failed`);
193
+ });
194
+
195
+ // Step 3: Merkle inclusion proofs
196
+ const proofResults = verifyMerkleProofs(bundle.merkle_proofs, bundle.checkpoint_reference.merkle_root);
197
+ const step3 = proofResults.length === 0 ? true : proofResults.every(r => r);
198
+ proofResults.forEach((r, i) => {
199
+ if (!r) errors.push(`Merkle proof ${i} failed`);
200
+ });
201
+
202
+ // Step 4: Checkpoint anchor
203
+ const step4 = verifyCheckpointAnchor(bundle.checkpoint_reference as Record<string, unknown>);
204
+
205
+ return {
206
+ step1_artifact_sig: step1,
207
+ step2_receipt_sigs: step2,
208
+ step3_merkle_proofs: step3,
209
+ step4_anchor: step4,
210
+ overall: step1 && step2 && step3,
211
+ errors,
212
+ details: { receipt_results: receiptResults, proof_results: proofResults },
213
+ };
214
+ }
215
+
216
+ // ── CLI mode ─────────────────────────────────────────────────
217
+
218
+ if (typeof process !== 'undefined' && process.argv[1]?.includes('verify')) {
219
+ const { readFileSync } = await import('node:fs');
220
+ const bundlePath = process.argv[2];
221
+ if (!bundlePath) {
222
+ console.error('Usage: npx tsx verify.ts <bundle.json>');
223
+ process.exit(1);
224
+ }
225
+ const bundleJson = readFileSync(bundlePath, 'utf-8');
226
+ const result = verifyEvidenceBundle(bundleJson);
227
+
228
+ console.log('\nAGA Independent Verifier\n');
229
+ console.log(`Step 1 - Artifact signature: ${result.step1_artifact_sig ? 'PASS' : 'FAIL'}`);
230
+ console.log(`Step 2 - Receipt signatures: ${result.step2_receipt_sigs ? 'PASS' : 'FAIL'} (${result.details.receipt_results.filter(r => r).length}/${result.details.receipt_results.length})`);
231
+ console.log(`Step 3 - Merkle inclusion proofs: ${result.step3_merkle_proofs ? 'PASS' : 'FAIL'} (${result.details.proof_results.filter(r => r).length}/${result.details.proof_results.length})`);
232
+ console.log(`Step 4 - Checkpoint anchor: ${result.step4_anchor}`);
233
+ console.log(`\nOVERALL: ${result.overall ? 'VERIFIED' : 'FAILED'}`);
234
+ if (result.errors.length) {
235
+ console.log('\nErrors:');
236
+ result.errors.forEach(e => console.log(` - ${e}`));
237
+ }
238
+
239
+ process.exit(result.overall ? 0 : 1);
240
+ }