@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.
- package/LICENSE +1 -1
- package/README.md +58 -35
- package/dist/constructor.js +1 -1
- package/dist/constructor.js.map +1 -1
- package/dist/fuse-cli.js +1 -1
- package/dist/fuse-cli.js.map +1 -1
- package/dist/fuse.js +1 -1
- package/dist/fuse.js.map +1 -1
- package/dist/host.js +1 -1
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/policy.js +1 -1
- package/dist/policy.js.map +1 -1
- package/package.json +23 -8
- package/src/constructor.ts +1 -1
- package/src/fuse-cli.ts +1 -1
- package/src/fuse.ts +1 -1
- package/src/host.ts +1 -1
- package/src/index.ts +3 -2
- package/src/policy.ts +1 -1
- package/dist/__tests__/audit-anchors.test.d.ts +0 -2
- package/dist/__tests__/audit-anchors.test.d.ts.map +0 -1
- package/dist/__tests__/audit-anchors.test.js +0 -152
- package/dist/__tests__/audit-anchors.test.js.map +0 -1
- package/dist/__tests__/audit-anomalies.test.d.ts +0 -2
- package/dist/__tests__/audit-anomalies.test.d.ts.map +0 -1
- package/dist/__tests__/audit-anomalies.test.js +0 -663
- package/dist/__tests__/audit-anomalies.test.js.map +0 -1
- package/dist/__tests__/audit-attestation.test.d.ts +0 -2
- package/dist/__tests__/audit-attestation.test.d.ts.map +0 -1
- package/dist/__tests__/audit-attestation.test.js +0 -403
- package/dist/__tests__/audit-attestation.test.js.map +0 -1
- package/dist/__tests__/audit-bench-smoke.test.d.ts +0 -2
- package/dist/__tests__/audit-bench-smoke.test.d.ts.map +0 -1
- package/dist/__tests__/audit-bench-smoke.test.js +0 -87
- package/dist/__tests__/audit-bench-smoke.test.js.map +0 -1
- package/dist/__tests__/audit-boundary.test.d.ts +0 -20
- package/dist/__tests__/audit-boundary.test.d.ts.map +0 -1
- package/dist/__tests__/audit-boundary.test.js +0 -84
- package/dist/__tests__/audit-boundary.test.js.map +0 -1
- package/dist/__tests__/audit-cli.test.d.ts +0 -2
- package/dist/__tests__/audit-cli.test.d.ts.map +0 -1
- package/dist/__tests__/audit-cli.test.js +0 -239
- package/dist/__tests__/audit-cli.test.js.map +0 -1
- package/dist/__tests__/audit-dependencies.test.d.ts +0 -2
- package/dist/__tests__/audit-dependencies.test.d.ts.map +0 -1
- package/dist/__tests__/audit-dependencies.test.js +0 -116
- package/dist/__tests__/audit-dependencies.test.js.map +0 -1
- package/dist/__tests__/audit-fixtures.d.ts +0 -265
- package/dist/__tests__/audit-fixtures.d.ts.map +0 -1
- package/dist/__tests__/audit-fixtures.js +0 -511
- package/dist/__tests__/audit-fixtures.js.map +0 -1
- package/dist/__tests__/audit-ingest.test.d.ts +0 -2
- package/dist/__tests__/audit-ingest.test.d.ts.map +0 -1
- package/dist/__tests__/audit-ingest.test.js +0 -473
- package/dist/__tests__/audit-ingest.test.js.map +0 -1
- package/dist/__tests__/audit-memory-ingest.test.d.ts +0 -12
- package/dist/__tests__/audit-memory-ingest.test.d.ts.map +0 -1
- package/dist/__tests__/audit-memory-ingest.test.js +0 -135
- package/dist/__tests__/audit-memory-ingest.test.js.map +0 -1
- package/dist/__tests__/audit-property.test.d.ts +0 -2
- package/dist/__tests__/audit-property.test.d.ts.map +0 -1
- package/dist/__tests__/audit-property.test.js +0 -771
- package/dist/__tests__/audit-property.test.js.map +0 -1
- package/dist/__tests__/audit-real-fixtures.test.d.ts +0 -2
- package/dist/__tests__/audit-real-fixtures.test.d.ts.map +0 -1
- package/dist/__tests__/audit-real-fixtures.test.js +0 -132
- package/dist/__tests__/audit-real-fixtures.test.js.map +0 -1
- package/dist/__tests__/audit-real-live.test.d.ts +0 -2
- package/dist/__tests__/audit-real-live.test.d.ts.map +0 -1
- package/dist/__tests__/audit-real-live.test.js +0 -107
- package/dist/__tests__/audit-real-live.test.js.map +0 -1
- package/dist/__tests__/audit-reconstruct.test.d.ts +0 -2
- package/dist/__tests__/audit-reconstruct.test.d.ts.map +0 -1
- package/dist/__tests__/audit-reconstruct.test.js +0 -287
- package/dist/__tests__/audit-reconstruct.test.js.map +0 -1
- package/dist/__tests__/audit-report.test.d.ts +0 -2
- package/dist/__tests__/audit-report.test.d.ts.map +0 -1
- package/dist/__tests__/audit-report.test.js +0 -186
- package/dist/__tests__/audit-report.test.js.map +0 -1
- package/dist/__tests__/audit-roundtrip.test.d.ts +0 -2
- package/dist/__tests__/audit-roundtrip.test.d.ts.map +0 -1
- package/dist/__tests__/audit-roundtrip.test.js +0 -368
- package/dist/__tests__/audit-roundtrip.test.js.map +0 -1
- package/dist/__tests__/audit-temporal.test.d.ts +0 -2
- package/dist/__tests__/audit-temporal.test.d.ts.map +0 -1
- package/dist/__tests__/audit-temporal.test.js +0 -364
- package/dist/__tests__/audit-temporal.test.js.map +0 -1
- package/dist/__tests__/audit-tiers.test.d.ts +0 -2
- package/dist/__tests__/audit-tiers.test.d.ts.map +0 -1
- package/dist/__tests__/audit-tiers.test.js +0 -282
- package/dist/__tests__/audit-tiers.test.js.map +0 -1
- package/dist/__tests__/audit-witness-real.test.d.ts +0 -19
- package/dist/__tests__/audit-witness-real.test.d.ts.map +0 -1
- package/dist/__tests__/audit-witness-real.test.js +0 -82
- package/dist/__tests__/audit-witness-real.test.js.map +0 -1
- package/dist/__tests__/audit-witness.test.d.ts +0 -2
- package/dist/__tests__/audit-witness.test.d.ts.map +0 -1
- package/dist/__tests__/audit-witness.test.js +0 -308
- package/dist/__tests__/audit-witness.test.js.map +0 -1
- package/dist/__tests__/canonical.test.d.ts +0 -2
- package/dist/__tests__/canonical.test.d.ts.map +0 -1
- package/dist/__tests__/canonical.test.js +0 -162
- package/dist/__tests__/canonical.test.js.map +0 -1
- package/dist/__tests__/chain-hash-anchor.test.d.ts +0 -22
- package/dist/__tests__/chain-hash-anchor.test.d.ts.map +0 -1
- package/dist/__tests__/chain-hash-anchor.test.js +0 -55
- package/dist/__tests__/chain-hash-anchor.test.js.map +0 -1
- package/dist/__tests__/commit-service.integration.test.d.ts +0 -2
- package/dist/__tests__/commit-service.integration.test.d.ts.map +0 -1
- package/dist/__tests__/commit-service.integration.test.js +0 -95
- package/dist/__tests__/commit-service.integration.test.js.map +0 -1
- package/dist/__tests__/constructor.test.d.ts +0 -2
- package/dist/__tests__/constructor.test.d.ts.map +0 -1
- package/dist/__tests__/constructor.test.js +0 -267
- package/dist/__tests__/constructor.test.js.map +0 -1
- package/dist/__tests__/declared-proof-fixture.d.ts +0 -66
- package/dist/__tests__/declared-proof-fixture.d.ts.map +0 -1
- package/dist/__tests__/declared-proof-fixture.js +0 -66
- package/dist/__tests__/declared-proof-fixture.js.map +0 -1
- package/dist/__tests__/fuse-placement.test.d.ts +0 -2
- package/dist/__tests__/fuse-placement.test.d.ts.map +0 -1
- package/dist/__tests__/fuse-placement.test.js +0 -31
- package/dist/__tests__/fuse-placement.test.js.map +0 -1
- package/dist/__tests__/fuse-roundtrip.test.d.ts +0 -2
- package/dist/__tests__/fuse-roundtrip.test.d.ts.map +0 -1
- package/dist/__tests__/fuse-roundtrip.test.js +0 -241
- package/dist/__tests__/fuse-roundtrip.test.js.map +0 -1
- package/dist/__tests__/fuse-sdk.test.d.ts +0 -2
- package/dist/__tests__/fuse-sdk.test.d.ts.map +0 -1
- package/dist/__tests__/fuse-sdk.test.js +0 -117
- package/dist/__tests__/fuse-sdk.test.js.map +0 -1
- package/dist/__tests__/fuse-set-sdk.test.d.ts +0 -2
- package/dist/__tests__/fuse-set-sdk.test.d.ts.map +0 -1
- package/dist/__tests__/fuse-set-sdk.test.js +0 -721
- package/dist/__tests__/fuse-set-sdk.test.js.map +0 -1
- package/dist/__tests__/fuse-set.test.d.ts +0 -2
- package/dist/__tests__/fuse-set.test.d.ts.map +0 -1
- package/dist/__tests__/fuse-set.test.js +0 -868
- package/dist/__tests__/fuse-set.test.js.map +0 -1
- package/dist/__tests__/fuse-set2.test.d.ts +0 -2
- package/dist/__tests__/fuse-set2.test.d.ts.map +0 -1
- package/dist/__tests__/fuse-set2.test.js +0 -294
- package/dist/__tests__/fuse-set2.test.js.map +0 -1
- package/dist/__tests__/fuse-vectors.test.d.ts +0 -2
- package/dist/__tests__/fuse-vectors.test.d.ts.map +0 -1
- package/dist/__tests__/fuse-vectors.test.js +0 -175
- package/dist/__tests__/fuse-vectors.test.js.map +0 -1
- package/dist/__tests__/fuse-verify.test.d.ts +0 -2
- package/dist/__tests__/fuse-verify.test.d.ts.map +0 -1
- package/dist/__tests__/fuse-verify.test.js +0 -250
- package/dist/__tests__/fuse-verify.test.js.map +0 -1
- package/dist/__tests__/hello-world.test.d.ts +0 -2
- package/dist/__tests__/hello-world.test.d.ts.map +0 -1
- package/dist/__tests__/hello-world.test.js +0 -9
- package/dist/__tests__/hello-world.test.js.map +0 -1
- package/dist/__tests__/mock-proof-fixture.d.ts +0 -15
- package/dist/__tests__/mock-proof-fixture.d.ts.map +0 -1
- package/dist/__tests__/mock-proof-fixture.js +0 -34
- package/dist/__tests__/mock-proof-fixture.js.map +0 -1
- package/dist/__tests__/policy-enforcement.test.d.ts +0 -2
- package/dist/__tests__/policy-enforcement.test.d.ts.map +0 -1
- package/dist/__tests__/policy-enforcement.test.js +0 -188
- package/dist/__tests__/policy-enforcement.test.js.map +0 -1
- package/dist/__tests__/proof-hash-regression.test.d.ts +0 -2
- package/dist/__tests__/proof-hash-regression.test.d.ts.map +0 -1
- package/dist/__tests__/proof-hash-regression.test.js +0 -95
- package/dist/__tests__/proof-hash-regression.test.js.map +0 -1
- package/dist/__tests__/proof-hash.test.d.ts +0 -2
- package/dist/__tests__/proof-hash.test.d.ts.map +0 -1
- package/dist/__tests__/proof-hash.test.js +0 -116
- package/dist/__tests__/proof-hash.test.js.map +0 -1
- package/dist/__tests__/proof-integrity.test.d.ts +0 -2
- package/dist/__tests__/proof-integrity.test.d.ts.map +0 -1
- package/dist/__tests__/proof-integrity.test.js +0 -426
- package/dist/__tests__/proof-integrity.test.js.map +0 -1
- package/dist/__tests__/realistic-proof-fixture.d.ts +0 -56
- package/dist/__tests__/realistic-proof-fixture.d.ts.map +0 -1
- package/dist/__tests__/realistic-proof-fixture.js +0 -57
- package/dist/__tests__/realistic-proof-fixture.js.map +0 -1
- package/dist/__tests__/verifier.test.d.ts +0 -2
- package/dist/__tests__/verifier.test.d.ts.map +0 -1
- package/dist/__tests__/verifier.test.js +0 -531
- package/dist/__tests__/verifier.test.js.map +0 -1
- package/dist/canonical.d.ts +0 -54
- package/dist/canonical.d.ts.map +0 -1
- package/dist/canonical.js +0 -119
- package/dist/canonical.js.map +0 -1
- package/dist/proof-hash.d.ts +0 -9
- package/dist/proof-hash.d.ts.map +0 -1
- package/dist/proof-hash.js +0 -75
- package/dist/proof-hash.js.map +0 -1
- package/dist/types.d.ts +0 -705
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -3
- package/dist/types.js.map +0 -1
- package/dist/verifier.d.ts +0 -27
- package/dist/verifier.d.ts.map +0 -1
- package/dist/verifier.js +0 -844
- package/dist/verifier.js.map +0 -1
- package/src/__tests__/audit-anchors.test.ts +0 -188
- package/src/__tests__/audit-anomalies.test.ts +0 -815
- package/src/__tests__/audit-attestation.test.ts +0 -529
- package/src/__tests__/audit-bench-smoke.test.ts +0 -108
- package/src/__tests__/audit-boundary.test.ts +0 -96
- package/src/__tests__/audit-cli.test.ts +0 -288
- package/src/__tests__/audit-dependencies.test.ts +0 -141
- package/src/__tests__/audit-fixtures.ts +0 -736
- package/src/__tests__/audit-ingest.test.ts +0 -575
- package/src/__tests__/audit-memory-ingest.test.ts +0 -156
- package/src/__tests__/audit-property.test.ts +0 -892
- package/src/__tests__/audit-real-fixtures.test.ts +0 -153
- package/src/__tests__/audit-real-live.test.ts +0 -128
- package/src/__tests__/audit-reconstruct.test.ts +0 -353
- package/src/__tests__/audit-report.test.ts +0 -222
- package/src/__tests__/audit-roundtrip.test.ts +0 -464
- package/src/__tests__/audit-temporal.test.ts +0 -446
- package/src/__tests__/audit-tiers.test.ts +0 -343
- package/src/__tests__/audit-witness-real.test.ts +0 -92
- package/src/__tests__/audit-witness.test.ts +0 -345
- package/src/__tests__/canonical.test.ts +0 -253
- package/src/__tests__/chain-hash-anchor.test.ts +0 -67
- package/src/__tests__/commit-service.integration.test.ts +0 -117
- package/src/__tests__/constructor.test.ts +0 -340
- package/src/__tests__/declared-proof-fixture.ts +0 -65
- package/src/__tests__/fuse-placement.test.ts +0 -32
- package/src/__tests__/fuse-roundtrip.test.ts +0 -243
- package/src/__tests__/fuse-sdk.test.ts +0 -142
- package/src/__tests__/fuse-set-sdk.test.ts +0 -852
- package/src/__tests__/fuse-set.test.ts +0 -1005
- package/src/__tests__/fuse-set2.test.ts +0 -343
- package/src/__tests__/fuse-vectors.test.ts +0 -218
- package/src/__tests__/fuse-verify.test.ts +0 -278
- package/src/__tests__/hello-world.test.ts +0 -10
- package/src/__tests__/mock-proof-fixture.ts +0 -36
- package/src/__tests__/policy-enforcement.test.ts +0 -226
- package/src/__tests__/proof-hash-regression.test.ts +0 -107
- package/src/__tests__/proof-hash.test.ts +0 -129
- package/src/__tests__/proof-integrity.test.ts +0 -541
- package/src/__tests__/real-fixtures/anchor-after-112762.json +0 -47
- package/src/__tests__/real-fixtures/anchor-with-ethereum-112758.json +0 -47
- package/src/__tests__/real-fixtures/live-proof-0.json +0 -42
- package/src/__tests__/real-fixtures/live-proof-1.json +0 -43
- package/src/__tests__/real-fixtures/live-proof-2.json +0 -43
- package/src/__tests__/real-fixtures/successor-112760.json +0 -38
- package/src/__tests__/real-fixtures/witness-25525146.json +0 -6
- package/src/__tests__/real-fixtures/witness-25525147.json +0 -6
- package/src/__tests__/realistic-proof-fixture.ts +0 -57
- package/src/__tests__/verifier.test.ts +0 -624
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { describe, it } from "node:test";
|
|
2
|
-
import * as assert from "node:assert/strict";
|
|
3
|
-
import { sha256 } from "@noble/hashes/sha256";
|
|
4
|
-
import { computeProofHash, canonicalize } from "../index.js";
|
|
5
|
-
import type { BitGraphProof } from "../index.js";
|
|
6
|
-
// Fixture moved verbatim to a shared non-test module so other suites can
|
|
7
|
-
// import it without re-registering this file's describes under node:test.
|
|
8
|
-
import { MOCK_PROOF } from "./mock-proof-fixture.js";
|
|
9
|
-
|
|
10
|
-
describe("computeProofHash", () => {
|
|
11
|
-
it("produces base64-encoded SHA-256", () => {
|
|
12
|
-
const hash = computeProofHash(MOCK_PROOF);
|
|
13
|
-
// Base64 of SHA-256 is always 44 chars (with padding)
|
|
14
|
-
assert.ok(hash.length > 0 && hash.length <= 44, `unexpected hash length: ${hash.length}`);
|
|
15
|
-
// Valid base64
|
|
16
|
-
const decoded = Buffer.from(hash, "base64");
|
|
17
|
-
assert.equal(decoded.length, 32, "SHA-256 output should be 32 bytes");
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("matches manual signed-body computation", () => {
|
|
21
|
-
const signedBody: Record<string, unknown> = {
|
|
22
|
-
version: MOCK_PROOF.version,
|
|
23
|
-
artifact: MOCK_PROOF.artifact,
|
|
24
|
-
commit: MOCK_PROOF.commit,
|
|
25
|
-
publicKeyB64: MOCK_PROOF.signer.publicKeyB64,
|
|
26
|
-
enforcement: MOCK_PROOF.environment.enforcement,
|
|
27
|
-
measurement: MOCK_PROOF.environment.measurement,
|
|
28
|
-
attribution: MOCK_PROOF.attribution,
|
|
29
|
-
attestationFormat: MOCK_PROOF.environment.attestation!.format,
|
|
30
|
-
};
|
|
31
|
-
const expected = Buffer.from(sha256(canonicalize(signedBody))).toString("base64");
|
|
32
|
-
const actual = computeProofHash(MOCK_PROOF);
|
|
33
|
-
assert.equal(actual, expected, "computeProofHash must match manual signed-body hash");
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("is deterministic — same input always produces same output", () => {
|
|
37
|
-
const a = computeProofHash(MOCK_PROOF);
|
|
38
|
-
const b = computeProofHash(MOCK_PROOF);
|
|
39
|
-
assert.equal(a, b);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("is not affected by key ordering in input", () => {
|
|
43
|
-
// Construct same proof with keys in different order
|
|
44
|
-
const reordered = {
|
|
45
|
-
environment: MOCK_PROOF.environment,
|
|
46
|
-
version: MOCK_PROOF.version,
|
|
47
|
-
signer: MOCK_PROOF.signer,
|
|
48
|
-
artifact: MOCK_PROOF.artifact,
|
|
49
|
-
commit: MOCK_PROOF.commit,
|
|
50
|
-
attribution: MOCK_PROOF.attribution,
|
|
51
|
-
} as unknown as BitGraphProof;
|
|
52
|
-
assert.equal(computeProofHash(reordered), computeProofHash(MOCK_PROOF));
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("is not affected by metadata (outside signed body)", () => {
|
|
56
|
-
const withMetadata = {
|
|
57
|
-
...MOCK_PROOF,
|
|
58
|
-
metadata: { extra: "stuff", nested: { deep: true } },
|
|
59
|
-
timestamps: { artifact: { time: 12345 } },
|
|
60
|
-
} as unknown as BitGraphProof;
|
|
61
|
-
assert.equal(computeProofHash(withMetadata), computeProofHash(MOCK_PROOF));
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it("is not affected by signatureB64 (not in signed body)", () => {
|
|
65
|
-
const differentSig = {
|
|
66
|
-
...MOCK_PROOF,
|
|
67
|
-
signer: { ...MOCK_PROOF.signer, signatureB64: "completely-different-sig==" },
|
|
68
|
-
} as BitGraphProof;
|
|
69
|
-
assert.equal(computeProofHash(differentSig), computeProofHash(MOCK_PROOF));
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("is not affected by attestation reportB64 (not in signed body)", () => {
|
|
73
|
-
const differentReport = {
|
|
74
|
-
...MOCK_PROOF,
|
|
75
|
-
environment: {
|
|
76
|
-
...MOCK_PROOF.environment,
|
|
77
|
-
attestation: { format: "aws-nitro", reportB64: "totally-different==" },
|
|
78
|
-
},
|
|
79
|
-
} as BitGraphProof;
|
|
80
|
-
assert.equal(computeProofHash(differentReport), computeProofHash(MOCK_PROOF));
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("changes when artifact changes", () => {
|
|
84
|
-
const different = {
|
|
85
|
-
...MOCK_PROOF,
|
|
86
|
-
artifact: { hashAlg: "sha256" as const, digestB64: "different==" },
|
|
87
|
-
};
|
|
88
|
-
assert.notEqual(computeProofHash(different), computeProofHash(MOCK_PROOF));
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("changes when counter changes", () => {
|
|
92
|
-
const different = {
|
|
93
|
-
...MOCK_PROOF,
|
|
94
|
-
commit: { ...MOCK_PROOF.commit, counter: "99" },
|
|
95
|
-
};
|
|
96
|
-
assert.notEqual(computeProofHash(different), computeProofHash(MOCK_PROOF));
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it("changes when publicKeyB64 changes", () => {
|
|
100
|
-
const different = {
|
|
101
|
-
...MOCK_PROOF,
|
|
102
|
-
signer: { ...MOCK_PROOF.signer, publicKeyB64: "different-key==" },
|
|
103
|
-
} as BitGraphProof;
|
|
104
|
-
assert.notEqual(computeProofHash(different), computeProofHash(MOCK_PROOF));
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("includes attribution when present", () => {
|
|
108
|
-
const without = { ...MOCK_PROOF } as Record<string, unknown>;
|
|
109
|
-
delete without.attribution;
|
|
110
|
-
assert.notEqual(computeProofHash(without), computeProofHash(MOCK_PROOF));
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it("includes attestationFormat when present", () => {
|
|
114
|
-
const withoutAttestation = {
|
|
115
|
-
...MOCK_PROOF,
|
|
116
|
-
environment: {
|
|
117
|
-
enforcement: MOCK_PROOF.environment.enforcement,
|
|
118
|
-
measurement: MOCK_PROOF.environment.measurement,
|
|
119
|
-
},
|
|
120
|
-
} as BitGraphProof;
|
|
121
|
-
assert.notEqual(computeProofHash(withoutAttestation), computeProofHash(MOCK_PROOF));
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it("works with Record<string, unknown> input (not just BitGraphProof)", () => {
|
|
125
|
-
const record: Record<string, unknown> = { ...MOCK_PROOF };
|
|
126
|
-
const hash = computeProofHash(record);
|
|
127
|
-
assert.equal(hash, computeProofHash(MOCK_PROOF));
|
|
128
|
-
});
|
|
129
|
-
});
|
|
@@ -1,541 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Mike Argento. All rights reserved. See LICENSE.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Tests for verifyProofIntegrity, the bytes-free proof integrity API in
|
|
5
|
-
* @mikeargento/bitgraph-verify.
|
|
6
|
-
*
|
|
7
|
-
* Also carries regression coverage asserting that verify() behavior is
|
|
8
|
-
* unchanged by the shared-pipeline refactor: same checks, same order,
|
|
9
|
-
* same failure strings.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { describe, test, before, beforeEach } from "node:test";
|
|
13
|
-
import * as assert from "node:assert/strict";
|
|
14
|
-
import { getPublicKeyAsync, signAsync } from "@noble/ed25519";
|
|
15
|
-
import { sha256 } from "@noble/hashes/sha256";
|
|
16
|
-
import {
|
|
17
|
-
verify,
|
|
18
|
-
verifyProofIntegrity,
|
|
19
|
-
resetEpochLinkState,
|
|
20
|
-
canonicalize,
|
|
21
|
-
} from "@mikeargento/bitgraph-verify";
|
|
22
|
-
import type { BitGraphProof, ProofIntegrityResult } from "@mikeargento/bitgraph-verify";
|
|
23
|
-
import { Constructor } from "../constructor.js";
|
|
24
|
-
import type { HostCapabilities } from "../host.js";
|
|
25
|
-
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
// Test-fixture helpers
|
|
28
|
-
// ---------------------------------------------------------------------------
|
|
29
|
-
|
|
30
|
-
interface Fixture {
|
|
31
|
-
proof: BitGraphProof;
|
|
32
|
-
bytes: Uint8Array;
|
|
33
|
-
publicKeyB64: string;
|
|
34
|
-
measurement: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Constructor-built fixture, mirroring makeFixture in verifier.test.ts. */
|
|
38
|
-
async function makeFixture(opts?: {
|
|
39
|
-
epochId?: string;
|
|
40
|
-
withCounter?: boolean;
|
|
41
|
-
}): Promise<Fixture> {
|
|
42
|
-
const privateKey = crypto.getRandomValues(new Uint8Array(32));
|
|
43
|
-
const publicKeyBytes = await getPublicKeyAsync(privateKey);
|
|
44
|
-
const publicKeyB64 = Buffer.from(publicKeyBytes).toString("base64");
|
|
45
|
-
const measurement = "test-measurement-integrity";
|
|
46
|
-
|
|
47
|
-
let counter = 0;
|
|
48
|
-
const host: HostCapabilities = opts?.withCounter
|
|
49
|
-
? {
|
|
50
|
-
enforcementTier: "stub" as const,
|
|
51
|
-
getMeasurement: async () => measurement,
|
|
52
|
-
getFreshNonce: async () => crypto.getRandomValues(new Uint8Array(16)),
|
|
53
|
-
sign: async (data: Uint8Array) => signAsync(data, privateKey),
|
|
54
|
-
getPublicKey: async () => publicKeyBytes,
|
|
55
|
-
nextCounter: async () => String(++counter),
|
|
56
|
-
}
|
|
57
|
-
: {
|
|
58
|
-
enforcementTier: "stub" as const,
|
|
59
|
-
getMeasurement: async () => measurement,
|
|
60
|
-
getFreshNonce: async () => crypto.getRandomValues(new Uint8Array(16)),
|
|
61
|
-
sign: async (data: Uint8Array) => signAsync(data, privateKey),
|
|
62
|
-
getPublicKey: async () => publicKeyBytes,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const ctor = await Constructor.initialize(
|
|
66
|
-
opts?.epochId !== undefined ? { host, epochId: opts.epochId } : { host },
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
const bytes = new TextEncoder().encode("bitgraph-integrity-test-payload");
|
|
70
|
-
const proof = await ctor.commit({ bytes });
|
|
71
|
-
|
|
72
|
-
return { proof, bytes, publicKeyB64, measurement };
|
|
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
|
-
function b64(bytes: Uint8Array): string {
|
|
81
|
-
return Buffer.from(bytes).toString("base64");
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
interface ManualKey {
|
|
85
|
-
privateKey: Uint8Array;
|
|
86
|
-
publicKeyB64: string;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
async function makeKey(): Promise<ManualKey> {
|
|
90
|
-
const privateKey = crypto.getRandomValues(new Uint8Array(32));
|
|
91
|
-
const publicKeyB64 = b64(await getPublicKeyAsync(privateKey));
|
|
92
|
-
return { privateKey, publicKeyB64 };
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Manually build and sign a proof over the exact canonical SignedBody
|
|
97
|
-
* layout the verifier reconstructs. Used for slot-carrying and
|
|
98
|
-
* epochLink-carrying proofs, which the root Constructor does not produce.
|
|
99
|
-
*/
|
|
100
|
-
async function signBody(
|
|
101
|
-
key: ManualKey,
|
|
102
|
-
artifact: BitGraphProof["artifact"],
|
|
103
|
-
commit: BitGraphProof["commit"],
|
|
104
|
-
measurement: string,
|
|
105
|
-
): Promise<BitGraphProof> {
|
|
106
|
-
const signedBody = {
|
|
107
|
-
version: "bitgraph/1" as const,
|
|
108
|
-
artifact,
|
|
109
|
-
commit,
|
|
110
|
-
publicKeyB64: key.publicKeyB64,
|
|
111
|
-
enforcement: "stub" as const,
|
|
112
|
-
measurement,
|
|
113
|
-
};
|
|
114
|
-
const signatureB64 = b64(await signAsync(canonicalize(signedBody), key.privateKey));
|
|
115
|
-
return {
|
|
116
|
-
version: "bitgraph/1",
|
|
117
|
-
artifact,
|
|
118
|
-
commit,
|
|
119
|
-
signer: { publicKeyB64: key.publicKeyB64, signatureB64 },
|
|
120
|
-
environment: { enforcement: "stub", measurement },
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
interface SlotFixture {
|
|
125
|
-
proof: BitGraphProof;
|
|
126
|
-
bytes: Uint8Array;
|
|
127
|
-
measurement: string;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Build a slot-carrying proof: slot allocated at counter 1, commit at
|
|
132
|
-
* counter 2, slotHashB64 binding the signed commit to the exact slot body.
|
|
133
|
-
*/
|
|
134
|
-
async function makeSlotFixture(opts?: { commitCounter?: string }): Promise<SlotFixture> {
|
|
135
|
-
const key = await makeKey();
|
|
136
|
-
const measurement = "test-measurement-slot";
|
|
137
|
-
const epochId = "epoch-slot-test";
|
|
138
|
-
const nonceB64 = b64(crypto.getRandomValues(new Uint8Array(16)));
|
|
139
|
-
|
|
140
|
-
const slotBody = {
|
|
141
|
-
version: "bitgraph/slot/1" as const,
|
|
142
|
-
nonceB64,
|
|
143
|
-
counter: "1",
|
|
144
|
-
time: 1_700_000_000_000,
|
|
145
|
-
epochId,
|
|
146
|
-
publicKeyB64: key.publicKeyB64,
|
|
147
|
-
};
|
|
148
|
-
const slotCanonical = canonicalize(slotBody);
|
|
149
|
-
const slotSignatureB64 = b64(await signAsync(slotCanonical, key.privateKey));
|
|
150
|
-
const slotHashB64 = b64(sha256(slotCanonical));
|
|
151
|
-
|
|
152
|
-
const bytes = new TextEncoder().encode("bitgraph-integrity-slot-payload");
|
|
153
|
-
const artifact: BitGraphProof["artifact"] = {
|
|
154
|
-
hashAlg: "sha256",
|
|
155
|
-
digestB64: b64(sha256(bytes)),
|
|
156
|
-
};
|
|
157
|
-
const commit: BitGraphProof["commit"] = {
|
|
158
|
-
nonceB64,
|
|
159
|
-
counter: opts?.commitCounter ?? "2",
|
|
160
|
-
epochId,
|
|
161
|
-
slotCounter: "1",
|
|
162
|
-
slotHashB64,
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
const proof = await signBody(key, artifact, commit, measurement);
|
|
166
|
-
proof.slotAllocation = { ...slotBody, signatureB64: slotSignatureB64 };
|
|
167
|
-
return { proof, bytes, measurement };
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
interface EpochLinkFixture {
|
|
171
|
-
proof: BitGraphProof;
|
|
172
|
-
bytes: Uint8Array;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Build an epoch-genesis proof carrying an epochLink. The link is inside
|
|
177
|
-
* the signed commit, so the Ed25519 signature covers it.
|
|
178
|
-
*/
|
|
179
|
-
async function makeEpochLinkFixture(opts?: {
|
|
180
|
-
toEpochId?: string;
|
|
181
|
-
samePrevKey?: boolean;
|
|
182
|
-
}): Promise<EpochLinkFixture> {
|
|
183
|
-
const key = await makeKey();
|
|
184
|
-
const prevKey = await makeKey();
|
|
185
|
-
const toEpochId = opts?.toEpochId ?? "epoch-successor-a";
|
|
186
|
-
|
|
187
|
-
const epochLink = {
|
|
188
|
-
prevEpochId: "epoch-predecessor-a",
|
|
189
|
-
prevPublicKeyB64: opts?.samePrevKey === true ? key.publicKeyB64 : prevKey.publicKeyB64,
|
|
190
|
-
prevCounter: "42",
|
|
191
|
-
prevProofHashB64: b64(sha256(new TextEncoder().encode("predecessor-terminal-proof"))),
|
|
192
|
-
toEpochId,
|
|
193
|
-
toPublicKeyB64: key.publicKeyB64,
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
const bytes = new TextEncoder().encode("bitgraph-integrity-epochlink-payload");
|
|
197
|
-
const artifact: BitGraphProof["artifact"] = {
|
|
198
|
-
hashAlg: "sha256",
|
|
199
|
-
digestB64: b64(sha256(bytes)),
|
|
200
|
-
};
|
|
201
|
-
const commit: BitGraphProof["commit"] = {
|
|
202
|
-
nonceB64: b64(crypto.getRandomValues(new Uint8Array(16))),
|
|
203
|
-
counter: "1",
|
|
204
|
-
epochId: toEpochId,
|
|
205
|
-
epochLink,
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
const proof = await signBody(key, artifact, commit, "test-measurement-epochlink");
|
|
209
|
-
return { proof, bytes };
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// ---------------------------------------------------------------------------
|
|
213
|
-
// Module-level fixtures
|
|
214
|
-
// ---------------------------------------------------------------------------
|
|
215
|
-
|
|
216
|
-
let fx!: Fixture;
|
|
217
|
-
|
|
218
|
-
before(async () => {
|
|
219
|
-
fx = await makeFixture();
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
// ---------------------------------------------------------------------------
|
|
223
|
-
// Happy path: integrity without bytes
|
|
224
|
-
// ---------------------------------------------------------------------------
|
|
225
|
-
|
|
226
|
-
describe("verifyProofIntegrity: valid proof, no bytes", () => {
|
|
227
|
-
test("passes for a freshly created proof and reports binding not checked", async () => {
|
|
228
|
-
const result = await verifyProofIntegrity({ proof: fx.proof });
|
|
229
|
-
assert.deepEqual(result, { valid: true, artifactBinding: "not-checked" });
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
test("passes for a proof with counter and epochId", async () => {
|
|
233
|
-
const chained = await makeFixture({ withCounter: true, epochId: "epoch-integrity-xyz" });
|
|
234
|
-
const result = await verifyProofIntegrity({ proof: chained.proof });
|
|
235
|
-
assert.deepEqual(result, { valid: true, artifactBinding: "not-checked" });
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
test("passes even when verify() with wrong bytes fails: binding is genuinely not checked", async () => {
|
|
239
|
-
const wrongBytes = new Uint8Array([1, 2, 3]);
|
|
240
|
-
const full = await verify({ proof: fx.proof, bytes: wrongBytes });
|
|
241
|
-
assert.equal(full.valid, false);
|
|
242
|
-
assert.match(full.reason ?? "", /digest mismatch/);
|
|
243
|
-
|
|
244
|
-
const integrity = await verifyProofIntegrity({ proof: fx.proof });
|
|
245
|
-
assert.deepEqual(integrity, { valid: true, artifactBinding: "not-checked" });
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
// ---------------------------------------------------------------------------
|
|
250
|
-
// Result shape
|
|
251
|
-
// ---------------------------------------------------------------------------
|
|
252
|
-
|
|
253
|
-
describe("verifyProofIntegrity: result shape", () => {
|
|
254
|
-
test("failure result also carries artifactBinding 'not-checked' plus a reason", async () => {
|
|
255
|
-
const tampered = clone(fx.proof);
|
|
256
|
-
tampered.signer.signatureB64 = Buffer.alloc(64).toString("base64");
|
|
257
|
-
const result: ProofIntegrityResult = await verifyProofIntegrity({ proof: tampered });
|
|
258
|
-
assert.equal(result.valid, false);
|
|
259
|
-
assert.equal(result.artifactBinding, "not-checked");
|
|
260
|
-
assert.equal(typeof result.reason, "string");
|
|
261
|
-
});
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
// ---------------------------------------------------------------------------
|
|
265
|
-
// Tampering
|
|
266
|
-
// ---------------------------------------------------------------------------
|
|
267
|
-
|
|
268
|
-
describe("verifyProofIntegrity: tampering", () => {
|
|
269
|
-
test("fails when signatureB64 is replaced with all-zeros", async () => {
|
|
270
|
-
const tampered = clone(fx.proof);
|
|
271
|
-
tampered.signer.signatureB64 = Buffer.alloc(64).toString("base64");
|
|
272
|
-
const result = await verifyProofIntegrity({ proof: tampered });
|
|
273
|
-
assert.equal(result.valid, false);
|
|
274
|
-
assert.match(result.reason ?? "", /signature/);
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
test("fails when version is tampered, with a version reason", async () => {
|
|
278
|
-
const tampered = clone(fx.proof);
|
|
279
|
-
(tampered as unknown as Record<string, unknown>)["version"] = "bitgraph/2";
|
|
280
|
-
const result = await verifyProofIntegrity({ proof: tampered });
|
|
281
|
-
assert.equal(result.valid, false);
|
|
282
|
-
assert.equal(result.reason, "unsupported proof version: bitgraph/2");
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
test("fails when a signed field (measurement) is tampered", async () => {
|
|
286
|
-
const tampered = clone(fx.proof);
|
|
287
|
-
tampered.environment.measurement = "evil-measurement";
|
|
288
|
-
const result = await verifyProofIntegrity({ proof: tampered });
|
|
289
|
-
assert.equal(result.valid, false);
|
|
290
|
-
assert.match(result.reason ?? "", /signature/);
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
test("fails when artifact.digestB64 is not valid base64 (well-formedness still checked)", async () => {
|
|
294
|
-
const tampered = clone(fx.proof);
|
|
295
|
-
tampered.artifact.digestB64 = "!!!";
|
|
296
|
-
const result = await verifyProofIntegrity({ proof: tampered });
|
|
297
|
-
assert.equal(result.valid, false);
|
|
298
|
-
assert.equal(result.reason, "artifact.digestB64 is not valid base64");
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
test("fails for non-object proof", async () => {
|
|
302
|
-
const result = await verifyProofIntegrity({
|
|
303
|
-
proof: "not-a-proof" as unknown as BitGraphProof,
|
|
304
|
-
});
|
|
305
|
-
assert.equal(result.valid, false);
|
|
306
|
-
assert.match(result.reason ?? "", /object/);
|
|
307
|
-
});
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
// ---------------------------------------------------------------------------
|
|
311
|
-
// Slot allocation (bytes-free)
|
|
312
|
-
// ---------------------------------------------------------------------------
|
|
313
|
-
|
|
314
|
-
describe("verifyProofIntegrity: slot allocation", () => {
|
|
315
|
-
test("passes for a valid slot-carrying proof", async () => {
|
|
316
|
-
const slotFx = await makeSlotFixture();
|
|
317
|
-
const result = await verifyProofIntegrity({ proof: slotFx.proof });
|
|
318
|
-
assert.deepEqual(result, { valid: true, artifactBinding: "not-checked" });
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
test("fails when commit.slotHashB64 is tampered (signed field)", async () => {
|
|
322
|
-
const slotFx = await makeSlotFixture();
|
|
323
|
-
const tampered = clone(slotFx.proof);
|
|
324
|
-
tampered.commit.slotHashB64 = Buffer.alloc(32, 0xee).toString("base64");
|
|
325
|
-
const result = await verifyProofIntegrity({ proof: tampered });
|
|
326
|
-
assert.equal(result.valid, false);
|
|
327
|
-
assert.match(result.reason ?? "", /signature/);
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
test("fails with a slot binding reason when the embedded slot record is swapped", async () => {
|
|
331
|
-
const slotA = await makeSlotFixture();
|
|
332
|
-
const slotB = await makeSlotFixture();
|
|
333
|
-
const tampered = clone(slotA.proof);
|
|
334
|
-
// slotB's record is validly signed by its own key, but it is not the
|
|
335
|
-
// record that slotA's signed commit.slotHashB64 binds to.
|
|
336
|
-
tampered.slotAllocation = clone(slotB.proof).slotAllocation!;
|
|
337
|
-
const result = await verifyProofIntegrity({ proof: tampered });
|
|
338
|
-
assert.equal(result.valid, false);
|
|
339
|
-
assert.match(result.reason ?? "", /slotHashB64 does not match SHA-256 of canonical slot body/);
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
test("fails when slotAllocation.counter is tampered (slot signature breaks)", async () => {
|
|
343
|
-
const slotFx = await makeSlotFixture();
|
|
344
|
-
const tampered = clone(slotFx.proof);
|
|
345
|
-
tampered.slotAllocation!.counter = "999";
|
|
346
|
-
const result = await verifyProofIntegrity({ proof: tampered });
|
|
347
|
-
assert.equal(result.valid, false);
|
|
348
|
-
assert.match(result.reason ?? "", /slotAllocation signature verification failed/);
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
test("fails when slot counter is not less than commit counter", async () => {
|
|
352
|
-
// Slot at counter 1, commit also at counter 1: ordering violated even
|
|
353
|
-
// though every signature is genuine.
|
|
354
|
-
const slotFx = await makeSlotFixture({ commitCounter: "1" });
|
|
355
|
-
const result = await verifyProofIntegrity({ proof: slotFx.proof });
|
|
356
|
-
assert.equal(result.valid, false);
|
|
357
|
-
assert.match(result.reason ?? "", /must be less than commit\.counter/);
|
|
358
|
-
});
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
// ---------------------------------------------------------------------------
|
|
362
|
-
// Epoch link (bytes-free)
|
|
363
|
-
// ---------------------------------------------------------------------------
|
|
364
|
-
|
|
365
|
-
describe("verifyProofIntegrity: epochLink", () => {
|
|
366
|
-
beforeEach(() => {
|
|
367
|
-
resetEpochLinkState();
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
test("passes for a valid epoch-genesis proof with epochLink", async () => {
|
|
371
|
-
const linkFx = await makeEpochLinkFixture();
|
|
372
|
-
const result = await verifyProofIntegrity({ proof: linkFx.proof });
|
|
373
|
-
assert.deepEqual(result, { valid: true, artifactBinding: "not-checked" });
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
test("is idempotent for the same successor epoch", async () => {
|
|
377
|
-
const linkFx = await makeEpochLinkFixture();
|
|
378
|
-
const first = await verifyProofIntegrity({ proof: linkFx.proof });
|
|
379
|
-
const second = await verifyProofIntegrity({ proof: linkFx.proof });
|
|
380
|
-
assert.equal(first.valid, true);
|
|
381
|
-
assert.equal(second.valid, true);
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
test("fails when predecessor and successor keys are identical", async () => {
|
|
385
|
-
const linkFx = await makeEpochLinkFixture({ samePrevKey: true });
|
|
386
|
-
const result = await verifyProofIntegrity({ proof: linkFx.proof });
|
|
387
|
-
assert.equal(result.valid, false);
|
|
388
|
-
assert.match(result.reason ?? "", /different keys/);
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
test("detects a fork: same predecessor consumed by two successor epochs", async () => {
|
|
392
|
-
const first = await makeEpochLinkFixture({ toEpochId: "epoch-successor-a" });
|
|
393
|
-
const second = await makeEpochLinkFixture({ toEpochId: "epoch-successor-b" });
|
|
394
|
-
|
|
395
|
-
const r1 = await verifyProofIntegrity({ proof: first.proof });
|
|
396
|
-
assert.equal(r1.valid, true);
|
|
397
|
-
|
|
398
|
-
const r2 = await verifyProofIntegrity({ proof: second.proof });
|
|
399
|
-
assert.equal(r2.valid, false);
|
|
400
|
-
assert.match(r2.reason ?? "", /FORK DETECTED/);
|
|
401
|
-
});
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
// ---------------------------------------------------------------------------
|
|
405
|
-
// Policy checks (trustAnchors)
|
|
406
|
-
// ---------------------------------------------------------------------------
|
|
407
|
-
|
|
408
|
-
describe("verifyProofIntegrity: policy (trustAnchors)", () => {
|
|
409
|
-
test("passes when measurement is in the allowlist", async () => {
|
|
410
|
-
const result = await verifyProofIntegrity({
|
|
411
|
-
proof: fx.proof,
|
|
412
|
-
trustAnchors: { allowedMeasurements: [fx.measurement] },
|
|
413
|
-
});
|
|
414
|
-
assert.deepEqual(result, { valid: true, artifactBinding: "not-checked" });
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
test("fails when measurement is not in the allowlist", async () => {
|
|
418
|
-
const result = await verifyProofIntegrity({
|
|
419
|
-
proof: fx.proof,
|
|
420
|
-
trustAnchors: { allowedMeasurements: ["expected-meas"] },
|
|
421
|
-
});
|
|
422
|
-
assert.equal(result.valid, false);
|
|
423
|
-
assert.match(result.reason ?? "", /measurement/);
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
test("fails when requireEnforcement does not match", async () => {
|
|
427
|
-
const result = await verifyProofIntegrity({
|
|
428
|
-
proof: fx.proof,
|
|
429
|
-
trustAnchors: { requireEnforcement: "measured-tee" },
|
|
430
|
-
});
|
|
431
|
-
assert.equal(result.valid, false);
|
|
432
|
-
assert.match(result.reason ?? "", /enforcement/);
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
test("requireSlot fails for a slotless proof and passes for a slot-carrying proof", async () => {
|
|
436
|
-
const slotless = await verifyProofIntegrity({
|
|
437
|
-
proof: fx.proof,
|
|
438
|
-
trustAnchors: { requireSlot: true },
|
|
439
|
-
});
|
|
440
|
-
assert.equal(slotless.valid, false);
|
|
441
|
-
assert.match(slotless.reason ?? "", /slotAllocation/);
|
|
442
|
-
|
|
443
|
-
const slotFx = await makeSlotFixture();
|
|
444
|
-
const slotted = await verifyProofIntegrity({
|
|
445
|
-
proof: slotFx.proof,
|
|
446
|
-
trustAnchors: { requireSlot: true },
|
|
447
|
-
});
|
|
448
|
-
assert.deepEqual(slotted, { valid: true, artifactBinding: "not-checked" });
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
test("counter policy applies without bytes", async () => {
|
|
452
|
-
const slotFx = await makeSlotFixture();
|
|
453
|
-
const pass = await verifyProofIntegrity({
|
|
454
|
-
proof: slotFx.proof,
|
|
455
|
-
trustAnchors: { minCounter: "1" },
|
|
456
|
-
});
|
|
457
|
-
assert.equal(pass.valid, true);
|
|
458
|
-
|
|
459
|
-
const failResult = await verifyProofIntegrity({
|
|
460
|
-
proof: slotFx.proof,
|
|
461
|
-
trustAnchors: { minCounter: "999" },
|
|
462
|
-
});
|
|
463
|
-
assert.equal(failResult.valid, false);
|
|
464
|
-
assert.match(failResult.reason ?? "", /counter/);
|
|
465
|
-
});
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
// ---------------------------------------------------------------------------
|
|
469
|
-
// Regression: verify() behavior unchanged by the shared-pipeline refactor
|
|
470
|
-
// ---------------------------------------------------------------------------
|
|
471
|
-
|
|
472
|
-
describe("regression: verify() unchanged", () => {
|
|
473
|
-
test("valid proof with bytes returns exactly { valid: true }", async () => {
|
|
474
|
-
const result = await verify({ proof: fx.proof, bytes: fx.bytes });
|
|
475
|
-
assert.deepEqual(result, { valid: true });
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
test("wrong bytes fail with the exact digest mismatch reason", async () => {
|
|
479
|
-
const result = await verify({ proof: fx.proof, bytes: new Uint8Array([9, 9, 9]) });
|
|
480
|
-
assert.equal(result.valid, false);
|
|
481
|
-
assert.equal(
|
|
482
|
-
result.reason,
|
|
483
|
-
"artifact digest mismatch: the provided bytes do not match the committed digest",
|
|
484
|
-
);
|
|
485
|
-
});
|
|
486
|
-
|
|
487
|
-
test("tampered signature fails with the exact signature reason", async () => {
|
|
488
|
-
const tampered = clone(fx.proof);
|
|
489
|
-
tampered.signer.signatureB64 = Buffer.alloc(64).toString("base64");
|
|
490
|
-
const result = await verify({ proof: tampered, bytes: fx.bytes });
|
|
491
|
-
assert.equal(result.valid, false);
|
|
492
|
-
assert.equal(result.reason, "signature verification failed: signature does not match");
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
test("tampered version fails with the exact version reason", async () => {
|
|
496
|
-
const tampered = clone(fx.proof);
|
|
497
|
-
(tampered as unknown as Record<string, unknown>)["version"] = "bitgraph/2";
|
|
498
|
-
const result = await verify({ proof: tampered, bytes: fx.bytes });
|
|
499
|
-
assert.equal(result.valid, false);
|
|
500
|
-
assert.equal(result.reason, "unsupported proof version: bitgraph/2");
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
test("verify() and verifyProofIntegrity() agree on every non-artifact failure", async () => {
|
|
504
|
-
const variants: Array<(p: BitGraphProof) => void> = [
|
|
505
|
-
(p) => {
|
|
506
|
-
(p as unknown as Record<string, unknown>)["version"] = "occ/1";
|
|
507
|
-
},
|
|
508
|
-
(p) => {
|
|
509
|
-
p.signer.signatureB64 = Buffer.alloc(64).toString("base64");
|
|
510
|
-
},
|
|
511
|
-
(p) => {
|
|
512
|
-
p.environment.measurement = "evil-measurement";
|
|
513
|
-
},
|
|
514
|
-
(p) => {
|
|
515
|
-
delete (p as unknown as Record<string, unknown>)["signer"];
|
|
516
|
-
},
|
|
517
|
-
];
|
|
518
|
-
|
|
519
|
-
for (const mutate of variants) {
|
|
520
|
-
const tampered = clone(fx.proof);
|
|
521
|
-
mutate(tampered);
|
|
522
|
-
const full = await verify({ proof: tampered, bytes: fx.bytes });
|
|
523
|
-
const integrity = await verifyProofIntegrity({ proof: tampered });
|
|
524
|
-
assert.equal(full.valid, integrity.valid);
|
|
525
|
-
assert.equal(full.reason, integrity.reason);
|
|
526
|
-
}
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
test("verify() accepts the slot-carrying fixture with its bytes", async () => {
|
|
530
|
-
const slotFx = await makeSlotFixture();
|
|
531
|
-
const result = await verify({ proof: slotFx.proof, bytes: slotFx.bytes });
|
|
532
|
-
assert.deepEqual(result, { valid: true });
|
|
533
|
-
});
|
|
534
|
-
|
|
535
|
-
test("verify() accepts the epochLink fixture with its bytes", async () => {
|
|
536
|
-
resetEpochLinkState();
|
|
537
|
-
const linkFx = await makeEpochLinkFixture();
|
|
538
|
-
const result = await verify({ proof: linkFx.proof, bytes: linkFx.bytes });
|
|
539
|
-
assert.deepEqual(result, { valid: true });
|
|
540
|
-
});
|
|
541
|
-
});
|