@mikeargento/bitgraph 1.9.0 → 1.9.2

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.
Files changed (252) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +58 -35
  3. package/dist/constructor.js +1 -1
  4. package/dist/constructor.js.map +1 -1
  5. package/dist/fuse-cli.js +1 -1
  6. package/dist/fuse-cli.js.map +1 -1
  7. package/dist/fuse.js +1 -1
  8. package/dist/fuse.js.map +1 -1
  9. package/dist/host.js +1 -1
  10. package/dist/host.js.map +1 -1
  11. package/dist/index.d.ts +2 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +2 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/policy.js +1 -1
  16. package/dist/policy.js.map +1 -1
  17. package/package.json +23 -8
  18. package/src/constructor.ts +1 -1
  19. package/src/fuse-cli.ts +1 -1
  20. package/src/fuse.ts +1 -1
  21. package/src/host.ts +1 -1
  22. package/src/index.ts +3 -2
  23. package/src/policy.ts +1 -1
  24. package/dist/__tests__/audit-anchors.test.d.ts +0 -2
  25. package/dist/__tests__/audit-anchors.test.d.ts.map +0 -1
  26. package/dist/__tests__/audit-anchors.test.js +0 -152
  27. package/dist/__tests__/audit-anchors.test.js.map +0 -1
  28. package/dist/__tests__/audit-anomalies.test.d.ts +0 -2
  29. package/dist/__tests__/audit-anomalies.test.d.ts.map +0 -1
  30. package/dist/__tests__/audit-anomalies.test.js +0 -663
  31. package/dist/__tests__/audit-anomalies.test.js.map +0 -1
  32. package/dist/__tests__/audit-attestation.test.d.ts +0 -2
  33. package/dist/__tests__/audit-attestation.test.d.ts.map +0 -1
  34. package/dist/__tests__/audit-attestation.test.js +0 -403
  35. package/dist/__tests__/audit-attestation.test.js.map +0 -1
  36. package/dist/__tests__/audit-bench-smoke.test.d.ts +0 -2
  37. package/dist/__tests__/audit-bench-smoke.test.d.ts.map +0 -1
  38. package/dist/__tests__/audit-bench-smoke.test.js +0 -87
  39. package/dist/__tests__/audit-bench-smoke.test.js.map +0 -1
  40. package/dist/__tests__/audit-boundary.test.d.ts +0 -20
  41. package/dist/__tests__/audit-boundary.test.d.ts.map +0 -1
  42. package/dist/__tests__/audit-boundary.test.js +0 -84
  43. package/dist/__tests__/audit-boundary.test.js.map +0 -1
  44. package/dist/__tests__/audit-cli.test.d.ts +0 -2
  45. package/dist/__tests__/audit-cli.test.d.ts.map +0 -1
  46. package/dist/__tests__/audit-cli.test.js +0 -239
  47. package/dist/__tests__/audit-cli.test.js.map +0 -1
  48. package/dist/__tests__/audit-dependencies.test.d.ts +0 -2
  49. package/dist/__tests__/audit-dependencies.test.d.ts.map +0 -1
  50. package/dist/__tests__/audit-dependencies.test.js +0 -116
  51. package/dist/__tests__/audit-dependencies.test.js.map +0 -1
  52. package/dist/__tests__/audit-fixtures.d.ts +0 -265
  53. package/dist/__tests__/audit-fixtures.d.ts.map +0 -1
  54. package/dist/__tests__/audit-fixtures.js +0 -511
  55. package/dist/__tests__/audit-fixtures.js.map +0 -1
  56. package/dist/__tests__/audit-ingest.test.d.ts +0 -2
  57. package/dist/__tests__/audit-ingest.test.d.ts.map +0 -1
  58. package/dist/__tests__/audit-ingest.test.js +0 -473
  59. package/dist/__tests__/audit-ingest.test.js.map +0 -1
  60. package/dist/__tests__/audit-memory-ingest.test.d.ts +0 -12
  61. package/dist/__tests__/audit-memory-ingest.test.d.ts.map +0 -1
  62. package/dist/__tests__/audit-memory-ingest.test.js +0 -135
  63. package/dist/__tests__/audit-memory-ingest.test.js.map +0 -1
  64. package/dist/__tests__/audit-property.test.d.ts +0 -2
  65. package/dist/__tests__/audit-property.test.d.ts.map +0 -1
  66. package/dist/__tests__/audit-property.test.js +0 -771
  67. package/dist/__tests__/audit-property.test.js.map +0 -1
  68. package/dist/__tests__/audit-real-fixtures.test.d.ts +0 -2
  69. package/dist/__tests__/audit-real-fixtures.test.d.ts.map +0 -1
  70. package/dist/__tests__/audit-real-fixtures.test.js +0 -132
  71. package/dist/__tests__/audit-real-fixtures.test.js.map +0 -1
  72. package/dist/__tests__/audit-real-live.test.d.ts +0 -2
  73. package/dist/__tests__/audit-real-live.test.d.ts.map +0 -1
  74. package/dist/__tests__/audit-real-live.test.js +0 -107
  75. package/dist/__tests__/audit-real-live.test.js.map +0 -1
  76. package/dist/__tests__/audit-reconstruct.test.d.ts +0 -2
  77. package/dist/__tests__/audit-reconstruct.test.d.ts.map +0 -1
  78. package/dist/__tests__/audit-reconstruct.test.js +0 -287
  79. package/dist/__tests__/audit-reconstruct.test.js.map +0 -1
  80. package/dist/__tests__/audit-report.test.d.ts +0 -2
  81. package/dist/__tests__/audit-report.test.d.ts.map +0 -1
  82. package/dist/__tests__/audit-report.test.js +0 -186
  83. package/dist/__tests__/audit-report.test.js.map +0 -1
  84. package/dist/__tests__/audit-roundtrip.test.d.ts +0 -2
  85. package/dist/__tests__/audit-roundtrip.test.d.ts.map +0 -1
  86. package/dist/__tests__/audit-roundtrip.test.js +0 -368
  87. package/dist/__tests__/audit-roundtrip.test.js.map +0 -1
  88. package/dist/__tests__/audit-temporal.test.d.ts +0 -2
  89. package/dist/__tests__/audit-temporal.test.d.ts.map +0 -1
  90. package/dist/__tests__/audit-temporal.test.js +0 -364
  91. package/dist/__tests__/audit-temporal.test.js.map +0 -1
  92. package/dist/__tests__/audit-tiers.test.d.ts +0 -2
  93. package/dist/__tests__/audit-tiers.test.d.ts.map +0 -1
  94. package/dist/__tests__/audit-tiers.test.js +0 -282
  95. package/dist/__tests__/audit-tiers.test.js.map +0 -1
  96. package/dist/__tests__/audit-witness-real.test.d.ts +0 -19
  97. package/dist/__tests__/audit-witness-real.test.d.ts.map +0 -1
  98. package/dist/__tests__/audit-witness-real.test.js +0 -82
  99. package/dist/__tests__/audit-witness-real.test.js.map +0 -1
  100. package/dist/__tests__/audit-witness.test.d.ts +0 -2
  101. package/dist/__tests__/audit-witness.test.d.ts.map +0 -1
  102. package/dist/__tests__/audit-witness.test.js +0 -308
  103. package/dist/__tests__/audit-witness.test.js.map +0 -1
  104. package/dist/__tests__/canonical.test.d.ts +0 -2
  105. package/dist/__tests__/canonical.test.d.ts.map +0 -1
  106. package/dist/__tests__/canonical.test.js +0 -162
  107. package/dist/__tests__/canonical.test.js.map +0 -1
  108. package/dist/__tests__/chain-hash-anchor.test.d.ts +0 -22
  109. package/dist/__tests__/chain-hash-anchor.test.d.ts.map +0 -1
  110. package/dist/__tests__/chain-hash-anchor.test.js +0 -55
  111. package/dist/__tests__/chain-hash-anchor.test.js.map +0 -1
  112. package/dist/__tests__/commit-service.integration.test.d.ts +0 -2
  113. package/dist/__tests__/commit-service.integration.test.d.ts.map +0 -1
  114. package/dist/__tests__/commit-service.integration.test.js +0 -95
  115. package/dist/__tests__/commit-service.integration.test.js.map +0 -1
  116. package/dist/__tests__/constructor.test.d.ts +0 -2
  117. package/dist/__tests__/constructor.test.d.ts.map +0 -1
  118. package/dist/__tests__/constructor.test.js +0 -267
  119. package/dist/__tests__/constructor.test.js.map +0 -1
  120. package/dist/__tests__/declared-proof-fixture.d.ts +0 -66
  121. package/dist/__tests__/declared-proof-fixture.d.ts.map +0 -1
  122. package/dist/__tests__/declared-proof-fixture.js +0 -66
  123. package/dist/__tests__/declared-proof-fixture.js.map +0 -1
  124. package/dist/__tests__/fuse-placement.test.d.ts +0 -2
  125. package/dist/__tests__/fuse-placement.test.d.ts.map +0 -1
  126. package/dist/__tests__/fuse-placement.test.js +0 -31
  127. package/dist/__tests__/fuse-placement.test.js.map +0 -1
  128. package/dist/__tests__/fuse-roundtrip.test.d.ts +0 -2
  129. package/dist/__tests__/fuse-roundtrip.test.d.ts.map +0 -1
  130. package/dist/__tests__/fuse-roundtrip.test.js +0 -241
  131. package/dist/__tests__/fuse-roundtrip.test.js.map +0 -1
  132. package/dist/__tests__/fuse-sdk.test.d.ts +0 -2
  133. package/dist/__tests__/fuse-sdk.test.d.ts.map +0 -1
  134. package/dist/__tests__/fuse-sdk.test.js +0 -117
  135. package/dist/__tests__/fuse-sdk.test.js.map +0 -1
  136. package/dist/__tests__/fuse-set-sdk.test.d.ts +0 -2
  137. package/dist/__tests__/fuse-set-sdk.test.d.ts.map +0 -1
  138. package/dist/__tests__/fuse-set-sdk.test.js +0 -721
  139. package/dist/__tests__/fuse-set-sdk.test.js.map +0 -1
  140. package/dist/__tests__/fuse-set.test.d.ts +0 -2
  141. package/dist/__tests__/fuse-set.test.d.ts.map +0 -1
  142. package/dist/__tests__/fuse-set.test.js +0 -868
  143. package/dist/__tests__/fuse-set.test.js.map +0 -1
  144. package/dist/__tests__/fuse-set2.test.d.ts +0 -2
  145. package/dist/__tests__/fuse-set2.test.d.ts.map +0 -1
  146. package/dist/__tests__/fuse-set2.test.js +0 -294
  147. package/dist/__tests__/fuse-set2.test.js.map +0 -1
  148. package/dist/__tests__/fuse-vectors.test.d.ts +0 -2
  149. package/dist/__tests__/fuse-vectors.test.d.ts.map +0 -1
  150. package/dist/__tests__/fuse-vectors.test.js +0 -175
  151. package/dist/__tests__/fuse-vectors.test.js.map +0 -1
  152. package/dist/__tests__/fuse-verify.test.d.ts +0 -2
  153. package/dist/__tests__/fuse-verify.test.d.ts.map +0 -1
  154. package/dist/__tests__/fuse-verify.test.js +0 -250
  155. package/dist/__tests__/fuse-verify.test.js.map +0 -1
  156. package/dist/__tests__/hello-world.test.d.ts +0 -2
  157. package/dist/__tests__/hello-world.test.d.ts.map +0 -1
  158. package/dist/__tests__/hello-world.test.js +0 -9
  159. package/dist/__tests__/hello-world.test.js.map +0 -1
  160. package/dist/__tests__/mock-proof-fixture.d.ts +0 -15
  161. package/dist/__tests__/mock-proof-fixture.d.ts.map +0 -1
  162. package/dist/__tests__/mock-proof-fixture.js +0 -34
  163. package/dist/__tests__/mock-proof-fixture.js.map +0 -1
  164. package/dist/__tests__/policy-enforcement.test.d.ts +0 -2
  165. package/dist/__tests__/policy-enforcement.test.d.ts.map +0 -1
  166. package/dist/__tests__/policy-enforcement.test.js +0 -188
  167. package/dist/__tests__/policy-enforcement.test.js.map +0 -1
  168. package/dist/__tests__/proof-hash-regression.test.d.ts +0 -2
  169. package/dist/__tests__/proof-hash-regression.test.d.ts.map +0 -1
  170. package/dist/__tests__/proof-hash-regression.test.js +0 -95
  171. package/dist/__tests__/proof-hash-regression.test.js.map +0 -1
  172. package/dist/__tests__/proof-hash.test.d.ts +0 -2
  173. package/dist/__tests__/proof-hash.test.d.ts.map +0 -1
  174. package/dist/__tests__/proof-hash.test.js +0 -116
  175. package/dist/__tests__/proof-hash.test.js.map +0 -1
  176. package/dist/__tests__/proof-integrity.test.d.ts +0 -2
  177. package/dist/__tests__/proof-integrity.test.d.ts.map +0 -1
  178. package/dist/__tests__/proof-integrity.test.js +0 -426
  179. package/dist/__tests__/proof-integrity.test.js.map +0 -1
  180. package/dist/__tests__/realistic-proof-fixture.d.ts +0 -56
  181. package/dist/__tests__/realistic-proof-fixture.d.ts.map +0 -1
  182. package/dist/__tests__/realistic-proof-fixture.js +0 -57
  183. package/dist/__tests__/realistic-proof-fixture.js.map +0 -1
  184. package/dist/__tests__/verifier.test.d.ts +0 -2
  185. package/dist/__tests__/verifier.test.d.ts.map +0 -1
  186. package/dist/__tests__/verifier.test.js +0 -531
  187. package/dist/__tests__/verifier.test.js.map +0 -1
  188. package/dist/canonical.d.ts +0 -54
  189. package/dist/canonical.d.ts.map +0 -1
  190. package/dist/canonical.js +0 -119
  191. package/dist/canonical.js.map +0 -1
  192. package/dist/proof-hash.d.ts +0 -9
  193. package/dist/proof-hash.d.ts.map +0 -1
  194. package/dist/proof-hash.js +0 -75
  195. package/dist/proof-hash.js.map +0 -1
  196. package/dist/types.d.ts +0 -705
  197. package/dist/types.d.ts.map +0 -1
  198. package/dist/types.js +0 -3
  199. package/dist/types.js.map +0 -1
  200. package/dist/verifier.d.ts +0 -27
  201. package/dist/verifier.d.ts.map +0 -1
  202. package/dist/verifier.js +0 -844
  203. package/dist/verifier.js.map +0 -1
  204. package/src/__tests__/audit-anchors.test.ts +0 -188
  205. package/src/__tests__/audit-anomalies.test.ts +0 -815
  206. package/src/__tests__/audit-attestation.test.ts +0 -529
  207. package/src/__tests__/audit-bench-smoke.test.ts +0 -108
  208. package/src/__tests__/audit-boundary.test.ts +0 -96
  209. package/src/__tests__/audit-cli.test.ts +0 -288
  210. package/src/__tests__/audit-dependencies.test.ts +0 -141
  211. package/src/__tests__/audit-fixtures.ts +0 -736
  212. package/src/__tests__/audit-ingest.test.ts +0 -575
  213. package/src/__tests__/audit-memory-ingest.test.ts +0 -156
  214. package/src/__tests__/audit-property.test.ts +0 -892
  215. package/src/__tests__/audit-real-fixtures.test.ts +0 -153
  216. package/src/__tests__/audit-real-live.test.ts +0 -128
  217. package/src/__tests__/audit-reconstruct.test.ts +0 -353
  218. package/src/__tests__/audit-report.test.ts +0 -222
  219. package/src/__tests__/audit-roundtrip.test.ts +0 -464
  220. package/src/__tests__/audit-temporal.test.ts +0 -446
  221. package/src/__tests__/audit-tiers.test.ts +0 -343
  222. package/src/__tests__/audit-witness-real.test.ts +0 -92
  223. package/src/__tests__/audit-witness.test.ts +0 -345
  224. package/src/__tests__/canonical.test.ts +0 -253
  225. package/src/__tests__/chain-hash-anchor.test.ts +0 -67
  226. package/src/__tests__/commit-service.integration.test.ts +0 -117
  227. package/src/__tests__/constructor.test.ts +0 -340
  228. package/src/__tests__/declared-proof-fixture.ts +0 -65
  229. package/src/__tests__/fuse-placement.test.ts +0 -32
  230. package/src/__tests__/fuse-roundtrip.test.ts +0 -243
  231. package/src/__tests__/fuse-sdk.test.ts +0 -142
  232. package/src/__tests__/fuse-set-sdk.test.ts +0 -852
  233. package/src/__tests__/fuse-set.test.ts +0 -1005
  234. package/src/__tests__/fuse-set2.test.ts +0 -343
  235. package/src/__tests__/fuse-vectors.test.ts +0 -218
  236. package/src/__tests__/fuse-verify.test.ts +0 -278
  237. package/src/__tests__/hello-world.test.ts +0 -10
  238. package/src/__tests__/mock-proof-fixture.ts +0 -36
  239. package/src/__tests__/policy-enforcement.test.ts +0 -226
  240. package/src/__tests__/proof-hash-regression.test.ts +0 -107
  241. package/src/__tests__/proof-hash.test.ts +0 -129
  242. package/src/__tests__/proof-integrity.test.ts +0 -541
  243. package/src/__tests__/real-fixtures/anchor-after-112762.json +0 -47
  244. package/src/__tests__/real-fixtures/anchor-with-ethereum-112758.json +0 -47
  245. package/src/__tests__/real-fixtures/live-proof-0.json +0 -42
  246. package/src/__tests__/real-fixtures/live-proof-1.json +0 -43
  247. package/src/__tests__/real-fixtures/live-proof-2.json +0 -43
  248. package/src/__tests__/real-fixtures/successor-112760.json +0 -38
  249. package/src/__tests__/real-fixtures/witness-25525146.json +0 -6
  250. package/src/__tests__/real-fixtures/witness-25525147.json +0 -6
  251. package/src/__tests__/realistic-proof-fixture.ts +0 -57
  252. package/src/__tests__/verifier.test.ts +0 -624
@@ -1,624 +0,0 @@
1
- // Copyright (c) Mike Argento. All rights reserved. See LICENSE.
2
-
3
- import { describe, test, before } from "node:test";
4
- import * as assert from "node:assert/strict";
5
- import { getPublicKeyAsync, signAsync } from "@noble/ed25519";
6
- import { Constructor } from "../constructor.js";
7
- import { verify, resetEpochLinkState } from "../index.js";
8
- import { verifyProofIntegrity } from "@mikeargento/bitgraph-verify";
9
- import { DECLARED_12010 } from "./declared-proof-fixture.js";
10
- import type { HostCapabilities } from "../host.js";
11
- import type { BitGraphProof } from "../index.js";
12
-
13
- // ---------------------------------------------------------------------------
14
- // Test-fixture helpers
15
- // ---------------------------------------------------------------------------
16
-
17
- interface Fixture {
18
- proof: BitGraphProof;
19
- bytes: Uint8Array;
20
- publicKeyB64: string;
21
- measurement: string;
22
- privateKey: Uint8Array;
23
- }
24
-
25
- async function makeFixture(opts?: {
26
- epochId?: string;
27
- withCounter?: boolean;
28
- withTime?: number;
29
- measurement?: string;
30
- }): Promise<Fixture> {
31
- const privateKey = crypto.getRandomValues(new Uint8Array(32));
32
- const publicKeyBytes = await getPublicKeyAsync(privateKey);
33
- const publicKeyB64 = Buffer.from(publicKeyBytes).toString("base64");
34
- const measurement = opts?.measurement ?? "test-measurement-deadbeef";
35
-
36
- let counter = 0;
37
- const host: HostCapabilities = opts?.withCounter
38
- ? {
39
- enforcementTier: "stub" as const,
40
- getMeasurement: async () => measurement,
41
- getFreshNonce: async () => crypto.getRandomValues(new Uint8Array(16)),
42
- sign: async (data: Uint8Array) => signAsync(data, privateKey),
43
- getPublicKey: async () => publicKeyBytes,
44
- nextCounter: async () => String(++counter),
45
- }
46
- : opts?.withTime !== undefined
47
- ? {
48
- enforcementTier: "stub" as const,
49
- getMeasurement: async () => measurement,
50
- getFreshNonce: async () => crypto.getRandomValues(new Uint8Array(16)),
51
- sign: async (data: Uint8Array) => signAsync(data, privateKey),
52
- getPublicKey: async () => publicKeyBytes,
53
- secureTime: async () => opts.withTime as number,
54
- }
55
- : {
56
- enforcementTier: "stub" as const,
57
- getMeasurement: async () => measurement,
58
- getFreshNonce: async () => crypto.getRandomValues(new Uint8Array(16)),
59
- sign: async (data: Uint8Array) => signAsync(data, privateKey),
60
- getPublicKey: async () => publicKeyBytes,
61
- };
62
-
63
- const ctor = await Constructor.initialize(
64
- opts?.epochId !== undefined
65
- ? { host, epochId: opts.epochId }
66
- : { host },
67
- );
68
-
69
- const bytes = new TextEncoder().encode("bitgraph-test-payload-42");
70
- const proof = await ctor.commit({ bytes });
71
-
72
- return { proof, bytes, publicKeyB64, measurement, privateKey };
73
- }
74
-
75
- /** Deep-clone a proof so mutations don't affect the original. */
76
- function clone(proof: BitGraphProof): BitGraphProof {
77
- return JSON.parse(JSON.stringify(proof)) as BitGraphProof;
78
- }
79
-
80
- // ---------------------------------------------------------------------------
81
- // Module-level fixture (no optional capabilities)
82
- // ---------------------------------------------------------------------------
83
-
84
- let fx!: Fixture;
85
-
86
- before(async () => {
87
- fx = await makeFixture();
88
- });
89
-
90
- // ---------------------------------------------------------------------------
91
- // Happy path
92
- // ---------------------------------------------------------------------------
93
-
94
- describe("verify — valid proof", () => {
95
- test("returns { valid: true } for a freshly created proof", async () => {
96
- const result = await verify({ proof: fx.proof, bytes: fx.bytes });
97
- assert.deepEqual(result, { valid: true });
98
- });
99
-
100
- test("verify is idempotent (same result on second call)", async () => {
101
- const r1 = await verify({ proof: fx.proof, bytes: fx.bytes });
102
- const r2 = await verify({ proof: fx.proof, bytes: fx.bytes });
103
- assert.deepEqual(r1, { valid: true });
104
- assert.deepEqual(r2, { valid: true });
105
- });
106
- });
107
-
108
- // ---------------------------------------------------------------------------
109
- // Artifact check (step 2)
110
- // ---------------------------------------------------------------------------
111
-
112
- describe("verify — artifact mismatch", () => {
113
- test("fails when wrong bytes are provided", async () => {
114
- const result = await verify({
115
- proof: fx.proof,
116
- bytes: new Uint8Array([99, 98, 97]),
117
- });
118
- assert.equal(result.valid, false);
119
- assert.match(result.reason ?? "", /digest mismatch/);
120
- });
121
-
122
- test("fails with empty bytes when proof covers non-empty content", async () => {
123
- const result = await verify({
124
- proof: fx.proof,
125
- bytes: new Uint8Array(0),
126
- });
127
- assert.equal(result.valid, false);
128
- assert.match(result.reason ?? "", /digest mismatch/);
129
- });
130
-
131
- test("succeeds when proof covers empty bytes and empty bytes are provided", async () => {
132
- const pk = crypto.getRandomValues(new Uint8Array(32));
133
- const pkBytes = await getPublicKeyAsync(pk);
134
- const host: HostCapabilities = {
135
- enforcementTier: "stub",
136
- getMeasurement: async () => "meas",
137
- getFreshNonce: async () => crypto.getRandomValues(new Uint8Array(16)),
138
- sign: async (data: Uint8Array) => signAsync(data, pk),
139
- getPublicKey: async () => pkBytes,
140
- };
141
- const ctor = await Constructor.initialize({ host });
142
- const emptyBytes = new Uint8Array(0);
143
- const proof = await ctor.commit({ bytes: emptyBytes });
144
- const result = await verify({ proof, bytes: emptyBytes });
145
- assert.deepEqual(result, { valid: true });
146
- });
147
- });
148
-
149
- // ---------------------------------------------------------------------------
150
- // Signature verification (step 4)
151
- // ---------------------------------------------------------------------------
152
-
153
- describe("verify — signature tampering", () => {
154
- test("fails when signatureB64 is replaced with all-zeros", async () => {
155
- const tampered = clone(fx.proof);
156
- tampered.signer.signatureB64 = Buffer.alloc(64).toString("base64");
157
- const result = await verify({ proof: tampered, bytes: fx.bytes });
158
- assert.equal(result.valid, false);
159
- assert.match(result.reason ?? "", /signature/);
160
- });
161
-
162
- test("fails when publicKeyB64 is replaced with a different key", async () => {
163
- const otherKey = crypto.getRandomValues(new Uint8Array(32));
164
- const otherPublic = await getPublicKeyAsync(otherKey);
165
- const tampered = clone(fx.proof);
166
- tampered.signer.publicKeyB64 = Buffer.from(otherPublic).toString("base64");
167
- const result = await verify({ proof: tampered, bytes: fx.bytes });
168
- assert.equal(result.valid, false);
169
- assert.match(result.reason ?? "", /signature/);
170
- });
171
-
172
- test("fails when artifact.digestB64 is tampered", async () => {
173
- const tampered = clone(fx.proof);
174
- tampered.artifact.digestB64 = Buffer.alloc(32, 0xff).toString("base64");
175
- const result = await verify({ proof: tampered, bytes: fx.bytes });
176
- assert.equal(result.valid, false);
177
- // Either artifact mismatch OR signature failure (both are valid)
178
- assert.ok(
179
- (result.reason ?? "").includes("digest mismatch") ||
180
- (result.reason ?? "").includes("signature"),
181
- );
182
- });
183
-
184
- test("fails when measurement is tampered (signed field)", async () => {
185
- const tampered = clone(fx.proof);
186
- tampered.environment.measurement = "evil-measurement";
187
- const result = await verify({ proof: tampered, bytes: fx.bytes });
188
- assert.equal(result.valid, false);
189
- assert.match(result.reason ?? "", /signature/);
190
- });
191
-
192
- test("fails when nonce is tampered (signed field)", async () => {
193
- const tampered = clone(fx.proof);
194
- tampered.commit.nonceB64 = Buffer.alloc(16, 0xab).toString("base64");
195
- const result = await verify({ proof: tampered, bytes: fx.bytes });
196
- assert.equal(result.valid, false);
197
- assert.match(result.reason ?? "", /signature/);
198
- });
199
- });
200
-
201
- // ---------------------------------------------------------------------------
202
- // Structural validation (step 1)
203
- // ---------------------------------------------------------------------------
204
-
205
- describe("verify — structural validation", () => {
206
- test("fails for non-object proof", async () => {
207
- const result = await verify({
208
- proof: "not-a-proof" as unknown as BitGraphProof,
209
- bytes: fx.bytes,
210
- });
211
- assert.equal(result.valid, false);
212
- assert.match(result.reason ?? "", /object/);
213
- });
214
-
215
- test("fails for null proof", async () => {
216
- const result = await verify({
217
- proof: null as unknown as BitGraphProof,
218
- bytes: fx.bytes,
219
- });
220
- assert.equal(result.valid, false);
221
- });
222
-
223
- test("fails for wrong version", async () => {
224
- const tampered = clone(fx.proof);
225
- (tampered as unknown as Record<string, unknown>)["version"] = "bitgraph/2";
226
- const result = await verify({ proof: tampered, bytes: fx.bytes });
227
- assert.equal(result.valid, false);
228
- assert.match(result.reason ?? "", /version/);
229
- });
230
-
231
- test("fails when artifact is missing", async () => {
232
- const tampered = clone(fx.proof);
233
- delete (tampered as unknown as Record<string, unknown>)["artifact"];
234
- const result = await verify({ proof: tampered, bytes: fx.bytes });
235
- assert.equal(result.valid, false);
236
- assert.match(result.reason ?? "", /artifact/);
237
- });
238
-
239
- test("fails when commit is missing", async () => {
240
- const tampered = clone(fx.proof);
241
- delete (tampered as unknown as Record<string, unknown>)["commit"];
242
- const result = await verify({ proof: tampered, bytes: fx.bytes });
243
- assert.equal(result.valid, false);
244
- assert.match(result.reason ?? "", /commit/);
245
- });
246
-
247
- test("fails when signer is missing", async () => {
248
- const tampered = clone(fx.proof);
249
- delete (tampered as unknown as Record<string, unknown>)["signer"];
250
- const result = await verify({ proof: tampered, bytes: fx.bytes });
251
- assert.equal(result.valid, false);
252
- assert.match(result.reason ?? "", /signer/);
253
- });
254
-
255
- test("fails when environment is missing", async () => {
256
- const tampered = clone(fx.proof);
257
- delete (tampered as unknown as Record<string, unknown>)["environment"];
258
- const result = await verify({ proof: tampered, bytes: fx.bytes });
259
- assert.equal(result.valid, false);
260
- assert.match(result.reason ?? "", /environment/);
261
- });
262
-
263
- test("fails for invalid enforcement tier", async () => {
264
- const tampered = clone(fx.proof);
265
- tampered.environment.enforcement = "invalid-tier" as "stub";
266
- const result = await verify({ proof: tampered, bytes: fx.bytes });
267
- assert.equal(result.valid, false);
268
- assert.match(result.reason ?? "", /enforcement/);
269
- });
270
-
271
- test("fails when publicKeyB64 is wrong length (too short)", async () => {
272
- const tampered = clone(fx.proof);
273
- tampered.signer.publicKeyB64 = Buffer.alloc(16).toString("base64");
274
- const result = await verify({ proof: tampered, bytes: fx.bytes });
275
- assert.equal(result.valid, false);
276
- assert.match(result.reason ?? "", /32/);
277
- });
278
- });
279
-
280
- // ---------------------------------------------------------------------------
281
- // Policy checks (step 5)
282
- // ---------------------------------------------------------------------------
283
-
284
- describe("verify — policy: requireEnforcement", () => {
285
- test("passes when enforcement matches required tier", async () => {
286
- const result = await verify({
287
- proof: fx.proof,
288
- bytes: fx.bytes,
289
- trustAnchors: { requireEnforcement: "stub" },
290
- });
291
- assert.deepEqual(result, { valid: true });
292
- });
293
-
294
- test("fails when enforcement does not match required tier", async () => {
295
- const result = await verify({
296
- proof: fx.proof,
297
- bytes: fx.bytes,
298
- trustAnchors: { requireEnforcement: "measured-tee" },
299
- });
300
- assert.equal(result.valid, false);
301
- assert.match(result.reason ?? "", /enforcement/);
302
- });
303
- });
304
-
305
- describe("verify — policy: allowedMeasurements", () => {
306
- test("passes when measurement is in the allowlist", async () => {
307
- const result = await verify({
308
- proof: fx.proof,
309
- bytes: fx.bytes,
310
- trustAnchors: { allowedMeasurements: [fx.measurement] },
311
- });
312
- assert.deepEqual(result, { valid: true });
313
- });
314
-
315
- test("passes when measurement is one of several in the allowlist", async () => {
316
- const result = await verify({
317
- proof: fx.proof,
318
- bytes: fx.bytes,
319
- trustAnchors: {
320
- allowedMeasurements: ["other-meas-1", fx.measurement, "other-meas-2"],
321
- },
322
- });
323
- assert.deepEqual(result, { valid: true });
324
- });
325
-
326
- test("fails when measurement is not in the allowlist", async () => {
327
- const result = await verify({
328
- proof: fx.proof,
329
- bytes: fx.bytes,
330
- trustAnchors: { allowedMeasurements: ["expected-meas", "another-meas"] },
331
- });
332
- assert.equal(result.valid, false);
333
- assert.match(result.reason ?? "", /measurement/);
334
- });
335
-
336
- test("passes with empty allowedMeasurements (no constraint)", async () => {
337
- const result = await verify({
338
- proof: fx.proof,
339
- bytes: fx.bytes,
340
- trustAnchors: { allowedMeasurements: [] },
341
- });
342
- assert.deepEqual(result, { valid: true });
343
- });
344
- });
345
-
346
- describe("verify — policy: allowedPublicKeys", () => {
347
- test("passes when public key is in the allowlist", async () => {
348
- const result = await verify({
349
- proof: fx.proof,
350
- bytes: fx.bytes,
351
- trustAnchors: { allowedPublicKeys: [fx.publicKeyB64] },
352
- });
353
- assert.deepEqual(result, { valid: true });
354
- });
355
-
356
- test("fails when public key is not in the allowlist", async () => {
357
- const otherKey = crypto.getRandomValues(new Uint8Array(32));
358
- const otherPublic = Buffer.from(await getPublicKeyAsync(otherKey)).toString("base64");
359
- const result = await verify({
360
- proof: fx.proof,
361
- bytes: fx.bytes,
362
- trustAnchors: { allowedPublicKeys: [otherPublic] },
363
- });
364
- assert.equal(result.valid, false);
365
- assert.match(result.reason ?? "", /public key/i);
366
- });
367
- });
368
-
369
- describe("verify — policy: requireAttestation", () => {
370
- test("fails when attestation required but proof has none", async () => {
371
- const result = await verify({
372
- proof: fx.proof,
373
- bytes: fx.bytes,
374
- trustAnchors: { requireAttestation: true },
375
- });
376
- assert.equal(result.valid, false);
377
- assert.match(result.reason ?? "", /attestation/);
378
- });
379
- });
380
-
381
- describe("verify — policy: counter checks", () => {
382
- let counterFx!: Fixture;
383
-
384
- before(async () => {
385
- counterFx = await makeFixture({ withCounter: true });
386
- });
387
-
388
- test("valid proof with counter passes minCounter check", async () => {
389
- const result = await verify({
390
- proof: counterFx.proof,
391
- bytes: counterFx.bytes,
392
- trustAnchors: { minCounter: "1" },
393
- });
394
- assert.deepEqual(result, { valid: true });
395
- });
396
-
397
- test("fails when counter is below minCounter", async () => {
398
- const result = await verify({
399
- proof: counterFx.proof,
400
- bytes: counterFx.bytes,
401
- trustAnchors: { minCounter: "999" },
402
- });
403
- assert.equal(result.valid, false);
404
- assert.match(result.reason ?? "", /counter/);
405
- });
406
-
407
- test("passes when counter is at exactly maxCounter", async () => {
408
- // counter is set because hostWithCounter provides nextCounter
409
- const counter = counterFx.proof.commit.counter!;
410
- const result = await verify({
411
- proof: counterFx.proof,
412
- bytes: counterFx.bytes,
413
- trustAnchors: { maxCounter: counter },
414
- });
415
- assert.deepEqual(result, { valid: true });
416
- });
417
-
418
- test("fails when counter exceeds maxCounter", async () => {
419
- const result = await verify({
420
- proof: counterFx.proof,
421
- bytes: counterFx.bytes,
422
- trustAnchors: { maxCounter: "0" },
423
- });
424
- assert.equal(result.valid, false);
425
- assert.match(result.reason ?? "", /counter/);
426
- });
427
-
428
- test("fails when minCounter policy requires counter but proof has none", async () => {
429
- // fx.proof has no counter (base host has no nextCounter)
430
- const result = await verify({
431
- proof: fx.proof,
432
- bytes: fx.bytes,
433
- trustAnchors: { minCounter: "0" },
434
- });
435
- assert.equal(result.valid, false);
436
- assert.match(result.reason ?? "", /counter/);
437
- });
438
- });
439
-
440
- describe("verify — policy: time checks", () => {
441
- const fixedTime = 1_700_000_000_000;
442
- let timeFx!: Fixture;
443
-
444
- before(async () => {
445
- timeFx = await makeFixture({ withTime: fixedTime });
446
- });
447
-
448
- test("passes when commit time is within minTime/maxTime window", async () => {
449
- const result = await verify({
450
- proof: timeFx.proof,
451
- bytes: timeFx.bytes,
452
- trustAnchors: {
453
- minTime: fixedTime - 1000,
454
- maxTime: fixedTime + 1000,
455
- },
456
- });
457
- assert.deepEqual(result, { valid: true });
458
- });
459
-
460
- test("fails when commit time is before minTime", async () => {
461
- const result = await verify({
462
- proof: timeFx.proof,
463
- bytes: timeFx.bytes,
464
- trustAnchors: { minTime: fixedTime + 1 },
465
- });
466
- assert.equal(result.valid, false);
467
- assert.match(result.reason ?? "", /time/);
468
- });
469
-
470
- test("fails when commit time is after maxTime", async () => {
471
- const result = await verify({
472
- proof: timeFx.proof,
473
- bytes: timeFx.bytes,
474
- trustAnchors: { maxTime: fixedTime - 1 },
475
- });
476
- assert.equal(result.valid, false);
477
- assert.match(result.reason ?? "", /time/);
478
- });
479
-
480
- test("fails when time policy requires time but proof has none", async () => {
481
- // fx.proof has no time (base host has no secureTime)
482
- const result = await verify({
483
- proof: fx.proof,
484
- bytes: fx.bytes,
485
- trustAnchors: { minTime: 0 },
486
- });
487
- assert.equal(result.valid, false);
488
- assert.match(result.reason ?? "", /time/);
489
- });
490
- });
491
-
492
- describe("verify — policy: requireEpochId", () => {
493
- test("fails when epochId is required but proof has none", async () => {
494
- // fx.proof was created without epochId
495
- const result = await verify({
496
- proof: fx.proof,
497
- bytes: fx.bytes,
498
- trustAnchors: { requireEpochId: true },
499
- });
500
- assert.equal(result.valid, false);
501
- assert.match(result.reason ?? "", /epochId/i);
502
- });
503
-
504
- test("passes when epochId is required and proof has it", async () => {
505
- const epochFx = await makeFixture({ epochId: "epoch-test-xyz" });
506
- const result = await verify({
507
- proof: epochFx.proof,
508
- bytes: epochFx.bytes,
509
- trustAnchors: { requireEpochId: true },
510
- });
511
- assert.deepEqual(result, { valid: true });
512
- });
513
- });
514
-
515
- describe("verify — policy: requireActor", () => {
516
- test("fails when actor is required but proof has none", async () => {
517
- const result = await verify({
518
- proof: fx.proof,
519
- bytes: fx.bytes,
520
- trustAnchors: { requireActor: true },
521
- });
522
- assert.equal(result.valid, false);
523
- assert.match(result.reason ?? "", /actor|agency/i);
524
- });
525
- });
526
-
527
- // ---------------------------------------------------------------------------
528
- // Agency: WebAuthn origin and RP binding
529
- // ---------------------------------------------------------------------------
530
-
531
- /* The real first declaration (#12,010) as it is, then the same proof with its
532
- client data altered. The origin check runs BEFORE the signature check, so a
533
- changed origin is reported as the origin problem it is, rather than as the
534
- signature failure it also causes; and a changed origin whose host still
535
- agrees with the rpIdHash passes the origin check and is then caught by the
536
- signature, which is the order a reader would want the reasons in. */
537
- function withClientData(proof: typeof DECLARED_12010, patch: (cd: Record<string, unknown>) => void): BitGraphProof {
538
- const p = JSON.parse(JSON.stringify(proof)) as BitGraphProof;
539
- const auth = (p.agency as { authorization: { clientDataJSON: string } }).authorization;
540
- const cd = JSON.parse(auth.clientDataJSON) as Record<string, unknown>;
541
- patch(cd);
542
- auth.clientDataJSON = JSON.stringify(cd);
543
- return p;
544
- }
545
-
546
- describe("verify — agency: WebAuthn origin and rpIdHash", () => {
547
- test("the real declared proof verifies (origin https://bitgraph.ing, rpIdHash = SHA-256(bitgraph.ing))", async () => {
548
- resetEpochLinkState();
549
- const result = await verifyProofIntegrity({ proof: DECLARED_12010 as unknown as BitGraphProof });
550
- assert.equal(result.valid, true, result.valid ? "" : result.reason);
551
- });
552
-
553
- test("an origin whose host disagrees with the authenticator's rpIdHash is refused as such", async () => {
554
- resetEpochLinkState();
555
- const tampered = withClientData(DECLARED_12010, (cd) => { cd.origin = "https://evil.example"; });
556
- const result = await verifyProofIntegrity({ proof: tampered });
557
- assert.equal(result.valid, false);
558
- assert.match(result.reason ?? "", /rpIdHash does not match the origin/);
559
- });
560
-
561
- test("a missing origin is refused", async () => {
562
- resetEpochLinkState();
563
- const tampered = withClientData(DECLARED_12010, (cd) => { delete cd.origin; });
564
- const result = await verifyProofIntegrity({ proof: tampered });
565
- assert.equal(result.valid, false);
566
- assert.match(result.reason ?? "", /missing origin/);
567
- });
568
-
569
- test("a subdomain of the RP ID passes the origin check (and is then caught by the signature)", async () => {
570
- resetEpochLinkState();
571
- const tampered = withClientData(DECLARED_12010, (cd) => { cd.origin = "https://app.bitgraph.ing"; });
572
- const result = await verifyProofIntegrity({ proof: tampered });
573
- assert.equal(result.valid, false);
574
- assert.doesNotMatch(result.reason ?? "", /origin|rpIdHash/);
575
- assert.match(result.reason ?? "", /signature/i);
576
- });
577
-
578
- test("policy allowedOrigins accepts the production origin", async () => {
579
- resetEpochLinkState();
580
- const result = await verifyProofIntegrity({
581
- proof: DECLARED_12010 as unknown as BitGraphProof,
582
- trustAnchors: { allowedOrigins: ["https://bitgraph.ing"] },
583
- });
584
- assert.equal(result.valid, true, result.valid ? "" : result.reason);
585
- });
586
-
587
- test("policy allowedOrigins refuses any other origin", async () => {
588
- resetEpochLinkState();
589
- const result = await verifyProofIntegrity({
590
- proof: DECLARED_12010 as unknown as BitGraphProof,
591
- trustAnchors: { allowedOrigins: ["https://other.example"] },
592
- });
593
- assert.equal(result.valid, false);
594
- assert.match(result.reason ?? "", /origin .* is not in the allowed set/);
595
- });
596
-
597
- test("policy allowedOrigins refuses a proof with no agency", async () => {
598
- const result = await verify({
599
- proof: fx.proof,
600
- bytes: fx.bytes,
601
- trustAnchors: { allowedOrigins: ["https://bitgraph.ing"] },
602
- });
603
- assert.equal(result.valid, false);
604
- assert.match(result.reason ?? "", /no agency/);
605
- });
606
- });
607
-
608
- // ---------------------------------------------------------------------------
609
- // resetEpochLinkState
610
- // ---------------------------------------------------------------------------
611
-
612
- describe("resetEpochLinkState", () => {
613
- test("is a function that can be called without error", () => {
614
- assert.doesNotThrow(() => resetEpochLinkState());
615
- });
616
-
617
- test("can be called multiple times", () => {
618
- assert.doesNotThrow(() => {
619
- resetEpochLinkState();
620
- resetEpochLinkState();
621
- resetEpochLinkState();
622
- });
623
- });
624
- });