@gate-forge/core 0.1.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/LICENSE +202 -0
- package/dist/baselines/adoption.d.ts +76 -0
- package/dist/baselines/adoption.d.ts.map +1 -0
- package/dist/baselines/adoption.js +163 -0
- package/dist/baselines/adoption.js.map +1 -0
- package/dist/baselines/index.d.ts +102 -0
- package/dist/baselines/index.d.ts.map +1 -0
- package/dist/baselines/index.js +186 -0
- package/dist/baselines/index.js.map +1 -0
- package/dist/canonical-json.d.ts +51 -0
- package/dist/canonical-json.d.ts.map +1 -0
- package/dist/canonical-json.js +112 -0
- package/dist/canonical-json.js.map +1 -0
- package/dist/classifier/bind.d.ts +64 -0
- package/dist/classifier/bind.d.ts.map +1 -0
- package/dist/classifier/bind.js +188 -0
- package/dist/classifier/bind.js.map +1 -0
- package/dist/classifier/classify.d.ts +154 -0
- package/dist/classifier/classify.d.ts.map +1 -0
- package/dist/classifier/classify.js +1207 -0
- package/dist/classifier/classify.js.map +1 -0
- package/dist/classifier/glob.d.ts +48 -0
- package/dist/classifier/glob.d.ts.map +1 -0
- package/dist/classifier/glob.js +104 -0
- package/dist/classifier/glob.js.map +1 -0
- package/dist/classifier/index.d.ts +10 -0
- package/dist/classifier/index.d.ts.map +1 -0
- package/dist/classifier/index.js +21 -0
- package/dist/classifier/index.js.map +1 -0
- package/dist/classifier/schema.d.ts +150 -0
- package/dist/classifier/schema.d.ts.map +1 -0
- package/dist/classifier/schema.js +139 -0
- package/dist/classifier/schema.js.map +1 -0
- package/dist/config/index.d.ts +259 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +523 -0
- package/dist/config/index.js.map +1 -0
- package/dist/fingerprints.d.ts +56 -0
- package/dist/fingerprints.d.ts.map +1 -0
- package/dist/fingerprints.js +59 -0
- package/dist/fingerprints.js.map +1 -0
- package/dist/graph/build.d.ts +16 -0
- package/dist/graph/build.d.ts.map +1 -0
- package/dist/graph/build.js +423 -0
- package/dist/graph/build.js.map +1 -0
- package/dist/graph/index.d.ts +12 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +10 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/schema.d.ts +448 -0
- package/dist/graph/schema.d.ts.map +1 -0
- package/dist/graph/schema.js +237 -0
- package/dist/graph/schema.js.map +1 -0
- package/dist/graph/symbols.d.ts +129 -0
- package/dist/graph/symbols.d.ts.map +1 -0
- package/dist/graph/symbols.js +177 -0
- package/dist/graph/symbols.js.map +1 -0
- package/dist/graph/util.d.ts +40 -0
- package/dist/graph/util.d.ts.map +1 -0
- package/dist/graph/util.js +41 -0
- package/dist/graph/util.js.map +1 -0
- package/dist/index.d.ts +683 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +563 -0
- package/dist/index.js.map +1 -0
- package/dist/mapping/resolve.d.ts +177 -0
- package/dist/mapping/resolve.d.ts.map +1 -0
- package/dist/mapping/resolve.js +523 -0
- package/dist/mapping/resolve.js.map +1 -0
- package/dist/policy/coverage.d.ts +79 -0
- package/dist/policy/coverage.d.ts.map +1 -0
- package/dist/policy/coverage.js +119 -0
- package/dist/policy/coverage.js.map +1 -0
- package/dist/policy/evaluate.d.ts +275 -0
- package/dist/policy/evaluate.d.ts.map +1 -0
- package/dist/policy/evaluate.js +382 -0
- package/dist/policy/evaluate.js.map +1 -0
- package/dist/policy/index.d.ts +15 -0
- package/dist/policy/index.d.ts.map +1 -0
- package/dist/policy/index.js +14 -0
- package/dist/policy/index.js.map +1 -0
- package/dist/policy/trusted.d.ts +59 -0
- package/dist/policy/trusted.d.ts.map +1 -0
- package/dist/policy/trusted.js +96 -0
- package/dist/policy/trusted.js.map +1 -0
- package/dist/provenance.d.ts +170 -0
- package/dist/provenance.d.ts.map +1 -0
- package/dist/provenance.js +306 -0
- package/dist/provenance.js.map +1 -0
- package/dist/receipt/index.d.ts +74 -0
- package/dist/receipt/index.d.ts.map +1 -0
- package/dist/receipt/index.js +154 -0
- package/dist/receipt/index.js.map +1 -0
- package/dist/report/index.d.ts +100 -0
- package/dist/report/index.d.ts.map +1 -0
- package/dist/report/index.js +355 -0
- package/dist/report/index.js.map +1 -0
- package/dist/schemas/adoption.d.ts +40 -0
- package/dist/schemas/adoption.d.ts.map +1 -0
- package/dist/schemas/adoption.js +87 -0
- package/dist/schemas/adoption.js.map +1 -0
- package/dist/schemas/baseline.d.ts +19 -0
- package/dist/schemas/baseline.d.ts.map +1 -0
- package/dist/schemas/baseline.js +45 -0
- package/dist/schemas/baseline.js.map +1 -0
- package/dist/schemas/claim.d.ts +32 -0
- package/dist/schemas/claim.d.ts.map +1 -0
- package/dist/schemas/claim.js +34 -0
- package/dist/schemas/claim.js.map +1 -0
- package/dist/schemas/classification-policy.d.ts +84 -0
- package/dist/schemas/classification-policy.d.ts.map +1 -0
- package/dist/schemas/classification-policy.js +130 -0
- package/dist/schemas/classification-policy.js.map +1 -0
- package/dist/schemas/classification-signal.d.ts +125 -0
- package/dist/schemas/classification-signal.d.ts.map +1 -0
- package/dist/schemas/classification-signal.js +126 -0
- package/dist/schemas/classification-signal.js.map +1 -0
- package/dist/schemas/classification.d.ts +120 -0
- package/dist/schemas/classification.d.ts.map +1 -0
- package/dist/schemas/classification.js +154 -0
- package/dist/schemas/classification.js.map +1 -0
- package/dist/schemas/common.d.ts +73 -0
- package/dist/schemas/common.d.ts.map +1 -0
- package/dist/schemas/common.js +60 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/schemas/coverage-policy.d.ts +89 -0
- package/dist/schemas/coverage-policy.d.ts.map +1 -0
- package/dist/schemas/coverage-policy.js +81 -0
- package/dist/schemas/coverage-policy.js.map +1 -0
- package/dist/schemas/evidence.d.ts +65 -0
- package/dist/schemas/evidence.d.ts.map +1 -0
- package/dist/schemas/evidence.js +64 -0
- package/dist/schemas/evidence.js.map +1 -0
- package/dist/schemas/execution-result.d.ts +214 -0
- package/dist/schemas/execution-result.d.ts.map +1 -0
- package/dist/schemas/execution-result.js +240 -0
- package/dist/schemas/execution-result.js.map +1 -0
- package/dist/schemas/gate-receipt.d.ts +86 -0
- package/dist/schemas/gate-receipt.d.ts.map +1 -0
- package/dist/schemas/gate-receipt.js +193 -0
- package/dist/schemas/gate-receipt.js.map +1 -0
- package/dist/schemas/index.d.ts +22 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +22 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/obligation.d.ts +37 -0
- package/dist/schemas/obligation.d.ts.map +1 -0
- package/dist/schemas/obligation.js +46 -0
- package/dist/schemas/obligation.js.map +1 -0
- package/dist/schemas/plugin.d.ts +19 -0
- package/dist/schemas/plugin.d.ts.map +1 -0
- package/dist/schemas/plugin.js +20 -0
- package/dist/schemas/plugin.js.map +1 -0
- package/dist/schemas/policy.d.ts +73 -0
- package/dist/schemas/policy.d.ts.map +1 -0
- package/dist/schemas/policy.js +46 -0
- package/dist/schemas/policy.js.map +1 -0
- package/dist/schemas/resource.d.ts +26 -0
- package/dist/schemas/resource.d.ts.map +1 -0
- package/dist/schemas/resource.js +29 -0
- package/dist/schemas/resource.js.map +1 -0
- package/dist/schemas/run-manifest.d.ts +72 -0
- package/dist/schemas/run-manifest.d.ts.map +1 -0
- package/dist/schemas/run-manifest.js +125 -0
- package/dist/schemas/run-manifest.js.map +1 -0
- package/dist/schemas/runner-adapter.d.ts +169 -0
- package/dist/schemas/runner-adapter.d.ts.map +1 -0
- package/dist/schemas/runner-adapter.js +2 -0
- package/dist/schemas/runner-adapter.js.map +1 -0
- package/dist/schemas/test-catalog.d.ts +472 -0
- package/dist/schemas/test-catalog.d.ts.map +1 -0
- package/dist/schemas/test-catalog.js +311 -0
- package/dist/schemas/test-catalog.js.map +1 -0
- package/dist/schemas/test-map.d.ts +99 -0
- package/dist/schemas/test-map.d.ts.map +1 -0
- package/dist/schemas/test-map.js +103 -0
- package/dist/schemas/test-map.js.map +1 -0
- package/dist/schemas/verdict.d.ts +73 -0
- package/dist/schemas/verdict.d.ts.map +1 -0
- package/dist/schemas/verdict.js +92 -0
- package/dist/schemas/verdict.js.map +1 -0
- package/dist/schemas/waiver.d.ts +36 -0
- package/dist/schemas/waiver.d.ts.map +1 -0
- package/dist/schemas/waiver.js +42 -0
- package/dist/schemas/waiver.js.map +1 -0
- package/dist/supervision/index.d.ts +170 -0
- package/dist/supervision/index.d.ts.map +1 -0
- package/dist/supervision/index.js +354 -0
- package/dist/supervision/index.js.map +1 -0
- package/dist/testing/clock.d.ts +50 -0
- package/dist/testing/clock.d.ts.map +1 -0
- package/dist/testing/clock.js +75 -0
- package/dist/testing/clock.js.map +1 -0
- package/dist/testing/env.d.ts +66 -0
- package/dist/testing/env.d.ts.map +1 -0
- package/dist/testing/env.js +106 -0
- package/dist/testing/env.js.map +1 -0
- package/dist/testing/gate-runner.d.ts +141 -0
- package/dist/testing/gate-runner.d.ts.map +1 -0
- package/dist/testing/gate-runner.js +173 -0
- package/dist/testing/gate-runner.js.map +1 -0
- package/dist/testing/gf19.d.ts +106 -0
- package/dist/testing/gf19.d.ts.map +1 -0
- package/dist/testing/gf19.js +282 -0
- package/dist/testing/gf19.js.map +1 -0
- package/dist/testing/index.d.ts +20 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +14 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/red-probe.d.ts +138 -0
- package/dist/testing/red-probe.d.ts.map +1 -0
- package/dist/testing/red-probe.js +251 -0
- package/dist/testing/red-probe.js.map +1 -0
- package/dist/testing/temp-repo.d.ts +126 -0
- package/dist/testing/temp-repo.d.ts.map +1 -0
- package/dist/testing/temp-repo.js +214 -0
- package/dist/testing/temp-repo.js.map +1 -0
- package/dist/verdict/cause.d.ts +99 -0
- package/dist/verdict/cause.d.ts.map +1 -0
- package/dist/verdict/cause.js +206 -0
- package/dist/verdict/cause.js.map +1 -0
- package/dist/verdict/evaluate.d.ts +135 -0
- package/dist/verdict/evaluate.d.ts.map +1 -0
- package/dist/verdict/evaluate.js +1513 -0
- package/dist/verdict/evaluate.js.map +1 -0
- package/dist/verdict/index.d.ts +21 -0
- package/dist/verdict/index.d.ts.map +1 -0
- package/dist/verdict/index.js +20 -0
- package/dist/verdict/index.js.map +1 -0
- package/dist/verdict/pack-verifiers.d.ts +97 -0
- package/dist/verdict/pack-verifiers.d.ts.map +1 -0
- package/dist/verdict/pack-verifiers.js +763 -0
- package/dist/verdict/pack-verifiers.js.map +1 -0
- package/dist/verdict/registry.d.ts +180 -0
- package/dist/verdict/registry.d.ts.map +1 -0
- package/dist/verdict/registry.js +67 -0
- package/dist/verdict/registry.js.map +1 -0
- package/dist/waivers/index.d.ts +103 -0
- package/dist/waivers/index.d.ts.map +1 -0
- package/dist/waivers/index.js +192 -0
- package/dist/waivers/index.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import type { RecordOrigin } from './schemas/evidence.js';
|
|
2
|
+
/** The record identity a witness hashes into the service-issued id. */
|
|
3
|
+
export interface RecordIdentity {
|
|
4
|
+
/** Run manifest identity the record was issued under. */
|
|
5
|
+
runId: string;
|
|
6
|
+
/** Obligation the record evidences. */
|
|
7
|
+
obligationId: string;
|
|
8
|
+
/** Evidence kind (e.g. `ui.action`, `persistence.entity`). */
|
|
9
|
+
kind: string;
|
|
10
|
+
/** Witness-issued test attribution. */
|
|
11
|
+
testId: string;
|
|
12
|
+
/** Where the contents came from (drives the trust stamp at issuance). */
|
|
13
|
+
origin: RecordOrigin;
|
|
14
|
+
/** JSON-representable evidence payload. */
|
|
15
|
+
payload: unknown;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Derives the service-issued record id: sha256 over the GF-canonical
|
|
19
|
+
* JSON of exactly the identity fields (pin #1/#7). Deterministic and
|
|
20
|
+
* stable across runs.
|
|
21
|
+
*
|
|
22
|
+
* Args:
|
|
23
|
+
* identity: runId, obligationId, kind, testId, origin, and payload.
|
|
24
|
+
*
|
|
25
|
+
* Returns:
|
|
26
|
+
* string: 64-char lowercase hex digest.
|
|
27
|
+
*
|
|
28
|
+
* Throws:
|
|
29
|
+
* TypeError: when the payload is not JSON-representable (the witness
|
|
30
|
+
* refuses to issue such records; callers issuing must too).
|
|
31
|
+
*/
|
|
32
|
+
export declare function recordIdOf(identity: {
|
|
33
|
+
runId: string;
|
|
34
|
+
obligationId: string;
|
|
35
|
+
kind: string;
|
|
36
|
+
testId: string;
|
|
37
|
+
origin: RecordOrigin;
|
|
38
|
+
payload: unknown;
|
|
39
|
+
}): string;
|
|
40
|
+
/**
|
|
41
|
+
* Verifies a lenient record view against its own provenance (check 1):
|
|
42
|
+
* the recordId must be 64-hex, every identity field present, and the
|
|
43
|
+
* id must recompute from the contents. Adversary-controlled input can
|
|
44
|
+
* never throw — any structural problem is `false` (the record then
|
|
45
|
+
* demotes to claimed tier, GF-23).
|
|
46
|
+
*
|
|
47
|
+
* Args:
|
|
48
|
+
* record: an arbitrary record-shaped value (e.g. a `records.json`
|
|
49
|
+
* entry or a witness ledger row).
|
|
50
|
+
*
|
|
51
|
+
* Returns:
|
|
52
|
+
* boolean: true only when the record's own contents hash to its id.
|
|
53
|
+
*/
|
|
54
|
+
export declare function isProvenancedRecord(record: unknown): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* True when a record asserts the witnessed tier AND its provenance
|
|
57
|
+
* verifies ({@link isProvenancedRecord}). The single witnessed-tier
|
|
58
|
+
* predicate for the engine and the reporter's trust display, so a
|
|
59
|
+
* record can never display as witnessed while the engine would demote
|
|
60
|
+
* it (or vice versa).
|
|
61
|
+
*
|
|
62
|
+
* Args:
|
|
63
|
+
* record: an arbitrary record-shaped value.
|
|
64
|
+
*
|
|
65
|
+
* Returns:
|
|
66
|
+
* boolean: witnessed tier with verified provenance.
|
|
67
|
+
*/
|
|
68
|
+
export declare function isWitnessedRecord(record: unknown): boolean;
|
|
69
|
+
/** Domain tag binding v2 attestation MACs to the ledger envelope format. */
|
|
70
|
+
export declare const ATTESTATION_DOMAIN = "gateforge.ledger.v2";
|
|
71
|
+
/** The only attestation envelope version this code produces or honors. */
|
|
72
|
+
export declare const ATTESTATION_VERSION = 2;
|
|
73
|
+
/**
|
|
74
|
+
* The unsigned v2 attestation body: the witness run identity, the fresh
|
|
75
|
+
* trusted invocation identity, the tested input digest, and the issued
|
|
76
|
+
* record set. The MAC covers exactly these fields plus the domain tag —
|
|
77
|
+
* never `mac` itself.
|
|
78
|
+
*/
|
|
79
|
+
export interface AttestationBody {
|
|
80
|
+
/** Witness run UUID. */
|
|
81
|
+
runId: string;
|
|
82
|
+
/** Fresh UUID minted by the trusted test-gates caller per invocation. */
|
|
83
|
+
invocationId: string;
|
|
84
|
+
/** 64-char lowercase hex digest of the canonical input snapshot. */
|
|
85
|
+
inputDigest: string;
|
|
86
|
+
/** Issued record ids (normalized internally: sorted, unique). */
|
|
87
|
+
recordIds: readonly string[];
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Computes the witness-ledger MAC (pin #7): HMAC-SHA256 over the
|
|
91
|
+
* GF-canonical JSON of `{runId, recordIds}` keyed by the witness's
|
|
92
|
+
* VERIFIER KEY — a secret handed to the witness and the verifying CLI
|
|
93
|
+
* by the run orchestrator, never to the tested suite (the suite's run
|
|
94
|
+
* token authorizes submissions; it must not authorize attestation).
|
|
95
|
+
* The MAC makes the durable, suite-writable manifest append tamper-
|
|
96
|
+
* evident: changing one id, adding a forged id, or transplanting the
|
|
97
|
+
* set across runs invalidates it.
|
|
98
|
+
*
|
|
99
|
+
* Args:
|
|
100
|
+
* verifierKey: the witness verifier secret (non-empty).
|
|
101
|
+
* runId: the run manifest identity binding the set to THIS run.
|
|
102
|
+
* recordIds: the issued record ids (normalized internally).
|
|
103
|
+
*
|
|
104
|
+
* Returns:
|
|
105
|
+
* string: 64-char lowercase hex HMAC.
|
|
106
|
+
*
|
|
107
|
+
* Throws:
|
|
108
|
+
* TypeError: when the verifier key is empty.
|
|
109
|
+
*/
|
|
110
|
+
export declare function ledgerMac(verifierKey: string, runId: string, recordIds: readonly string[]): string;
|
|
111
|
+
/**
|
|
112
|
+
* Verifies a ledger MAC ({@link ledgerMac}) in constant time where the
|
|
113
|
+
* inputs allow it. Adversary-controlled input never throws.
|
|
114
|
+
*
|
|
115
|
+
* Args:
|
|
116
|
+
* verifierKey: the witness verifier secret (non-empty).
|
|
117
|
+
* runId: the run manifest identity the set claims.
|
|
118
|
+
* recordIds: the record-id set the MAC must cover (normalized
|
|
119
|
+
* internally, exactly like the witness computed it).
|
|
120
|
+
* mac: the claimed MAC (64-char lowercase hex).
|
|
121
|
+
*
|
|
122
|
+
* Returns:
|
|
123
|
+
* boolean: true only when the MAC verifies over the exact set.
|
|
124
|
+
*/
|
|
125
|
+
export declare function verifyLedgerMac(verifierKey: string, runId: string, recordIds: readonly string[], mac: unknown): boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Computes the v2 attestation MAC (plan §11.3): HMAC-SHA256 over the
|
|
128
|
+
* GF-canonical JSON of `{domain, attestationVersion: 2, runId,
|
|
129
|
+
* invocationId, inputDigest, recordIds}` keyed by the witness's VERIFIER
|
|
130
|
+
* KEY — a secret the tested suite never receives. The fixed domain tag
|
|
131
|
+
* prevents cross-format signature acceptance: a legacy `{runId,
|
|
132
|
+
* recordIds}` MAC can never verify as a v2 attestation, and a v2 MAC
|
|
133
|
+
* can never verify as anything else.
|
|
134
|
+
*
|
|
135
|
+
* Args:
|
|
136
|
+
* verifierKey: the witness verifier secret (non-empty).
|
|
137
|
+
* body: runId, invocationId, 64-hex inputDigest, and issued record ids
|
|
138
|
+
* (normalized internally: deduplicated, codepoint-sorted).
|
|
139
|
+
*
|
|
140
|
+
* Returns:
|
|
141
|
+
* string: 64-char lowercase hex HMAC.
|
|
142
|
+
*
|
|
143
|
+
* Throws:
|
|
144
|
+
* TypeError: when the verifier key is empty, an identity is empty, or
|
|
145
|
+
* the input digest is not 64-char lowercase hex.
|
|
146
|
+
*/
|
|
147
|
+
export declare function attestationMac(verifierKey: string, body: AttestationBody): string;
|
|
148
|
+
/**
|
|
149
|
+
* Verifies a v2 attestation MAC ({@link attestationMac}) in constant
|
|
150
|
+
* time where the inputs allow it. Adversary-controlled input never
|
|
151
|
+
* throws. A legacy `{runId, recordIds}` MAC is structurally incapable
|
|
152
|
+
* of verifying here — the domain tag and the extra fields change the
|
|
153
|
+
* signed bytes — so old evidence can never authorize through this path.
|
|
154
|
+
*
|
|
155
|
+
* Args:
|
|
156
|
+
* verifierKey: the witness verifier secret (non-empty).
|
|
157
|
+
* body: the claimed attestation body (runId, invocationId,
|
|
158
|
+
* inputDigest, recordIds).
|
|
159
|
+
* mac: the claimed MAC (64-char lowercase hex).
|
|
160
|
+
*
|
|
161
|
+
* Returns:
|
|
162
|
+
* boolean: true only when the MAC verifies over the exact v2 body.
|
|
163
|
+
*/
|
|
164
|
+
export declare function verifyAttestationMac(verifierKey: string, body: {
|
|
165
|
+
runId: unknown;
|
|
166
|
+
invocationId: unknown;
|
|
167
|
+
inputDigest: unknown;
|
|
168
|
+
recordIds: unknown;
|
|
169
|
+
}, mac: unknown): boolean;
|
|
170
|
+
//# sourceMappingURL=provenance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provenance.d.ts","sourceRoot":"","sources":["../src/provenance.ts"],"names":[],"mappings":"AAyCA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,uEAAuE;AACvE,MAAM,WAAW,cAAc;IAC7B,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,MAAM,EAAE,YAAY,CAAC;IACrB,2CAA2C;IAC3C,OAAO,EAAE,OAAO,CAAC;CAClB;AAKD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;CAClB,GAAG,MAAM,CAeT;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAyC5D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAQ1D;AAKD,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,wBAAwB,CAAC;AAExD,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAKrC;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9B;AAWD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,SAAS,CACvB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,SAAS,MAAM,EAAE,GAC3B,MAAM,CAUR;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,GAAG,EAAE,OAAO,GACX,OAAO,CAST;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,MAAM,CAyBjF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,EACzF,GAAG,EAAE,OAAO,GACX,OAAO,CAmBT"}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Witness provenance verification (pin #7, ADR 0001 D2c, GF-23).
|
|
3
|
+
*
|
|
4
|
+
* The witness derives every record id as `sha256(canonicalJson({
|
|
5
|
+
* runId, obligationId, kind, testId, payload }))` (pin #1) — a
|
|
6
|
+
* deterministic function of the record's identity alone. That makes
|
|
7
|
+
* provenance CHECKABLE by any consumer without contacting the witness:
|
|
8
|
+
* an entry that never passed through the service cannot carry a record
|
|
9
|
+
* id that both recomputes correctly and appears in an AUTHENTICATED
|
|
10
|
+
* copy of the witness-issued set.
|
|
11
|
+
*
|
|
12
|
+
* Three independent checks exist, and all must pass for witnessed tier:
|
|
13
|
+
* 1. {@link isProvenancedRecord} — the record id recomputes from the
|
|
14
|
+
* record's own contents (structural integrity; catches arbitrary or
|
|
15
|
+
* transplanted hex ids). Verified here, in the engine, so every
|
|
16
|
+
* consumer of `evaluateObligation` gets it.
|
|
17
|
+
* 2. Authenticated single-envelope membership (plan §11.5–§11.6) — the
|
|
18
|
+
* id appears in a v2 attestation envelope ({@link attestationMac})
|
|
19
|
+
* whose integrity is protected by a secret the tested suite never
|
|
20
|
+
* receives: the witness's verifier key. The witness serves the live
|
|
21
|
+
* envelope at `GET /ledger-attestation` (verifier-key header, bound
|
|
22
|
+
* context only) and stamps the durable manifest append with the same
|
|
23
|
+
* signed object. The envelope binds the run id, the fresh invocation
|
|
24
|
+
* id, the tested input digest, and the issued set — one envelope must
|
|
25
|
+
* match all four at once; contexts are never merged. Hash
|
|
26
|
+
* recomputation is public, and the run manifest lives in the
|
|
27
|
+
* suite-writable state directory, so PLAIN manifest membership is
|
|
28
|
+
* trustworthiness-neutral: a hostile suite can fabricate both the
|
|
29
|
+
* records and the id list, but it cannot produce a valid MAC for a
|
|
30
|
+
* context the witness never attested. The legacy {@link ledgerMac}
|
|
31
|
+
* over `{runId, recordIds}` stays readable for diagnostics but NEVER
|
|
32
|
+
* authorizes evidence (it binds no input snapshot).
|
|
33
|
+
* 3. Run identity — record.runId equals the authorizing envelope's
|
|
34
|
+
* runId, so ids cannot be transplanted across runs.
|
|
35
|
+
*
|
|
36
|
+
* Enforcement lives in the CLI's provenance gate (packages/cli), which
|
|
37
|
+
* owns the run manifest and the verifier key surface.
|
|
38
|
+
*/
|
|
39
|
+
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
40
|
+
import { canonicalJson, isJsonValue, sha256Canonical } from './canonical-json.js';
|
|
41
|
+
import { compareStrings } from './graph/util.js';
|
|
42
|
+
/** Shape a 64-char lowercase hex record id must have. */
|
|
43
|
+
const RECORD_ID_PATTERN = /^[0-9a-f]{64}$/;
|
|
44
|
+
/**
|
|
45
|
+
* Derives the service-issued record id: sha256 over the GF-canonical
|
|
46
|
+
* JSON of exactly the identity fields (pin #1/#7). Deterministic and
|
|
47
|
+
* stable across runs.
|
|
48
|
+
*
|
|
49
|
+
* Args:
|
|
50
|
+
* identity: runId, obligationId, kind, testId, origin, and payload.
|
|
51
|
+
*
|
|
52
|
+
* Returns:
|
|
53
|
+
* string: 64-char lowercase hex digest.
|
|
54
|
+
*
|
|
55
|
+
* Throws:
|
|
56
|
+
* TypeError: when the payload is not JSON-representable (the witness
|
|
57
|
+
* refuses to issue such records; callers issuing must too).
|
|
58
|
+
*/
|
|
59
|
+
export function recordIdOf(identity) {
|
|
60
|
+
if (!isJsonValue(identity.payload)) {
|
|
61
|
+
throw new TypeError('recordIdOf: payload is not GF-canonical-JSON-representable');
|
|
62
|
+
}
|
|
63
|
+
if (identity.origin !== 'suite-submitted' && identity.origin !== 'engine-observed') {
|
|
64
|
+
throw new TypeError('recordIdOf: origin must be suite-submitted or engine-observed');
|
|
65
|
+
}
|
|
66
|
+
return sha256Canonical({
|
|
67
|
+
runId: identity.runId,
|
|
68
|
+
obligationId: identity.obligationId,
|
|
69
|
+
kind: identity.kind,
|
|
70
|
+
testId: identity.testId,
|
|
71
|
+
origin: identity.origin,
|
|
72
|
+
payload: identity.payload,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Verifies a lenient record view against its own provenance (check 1):
|
|
77
|
+
* the recordId must be 64-hex, every identity field present, and the
|
|
78
|
+
* id must recompute from the contents. Adversary-controlled input can
|
|
79
|
+
* never throw — any structural problem is `false` (the record then
|
|
80
|
+
* demotes to claimed tier, GF-23).
|
|
81
|
+
*
|
|
82
|
+
* Args:
|
|
83
|
+
* record: an arbitrary record-shaped value (e.g. a `records.json`
|
|
84
|
+
* entry or a witness ledger row).
|
|
85
|
+
*
|
|
86
|
+
* Returns:
|
|
87
|
+
* boolean: true only when the record's own contents hash to its id.
|
|
88
|
+
*/
|
|
89
|
+
export function isProvenancedRecord(record) {
|
|
90
|
+
if (typeof record !== 'object' || record === null || Array.isArray(record)) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const candidate = record;
|
|
94
|
+
const recordId = candidate['recordId'];
|
|
95
|
+
if (typeof recordId !== 'string' || !RECORD_ID_PATTERN.test(recordId))
|
|
96
|
+
return false;
|
|
97
|
+
const runId = candidate['runId'];
|
|
98
|
+
const obligationId = candidate['obligationId'];
|
|
99
|
+
const kind = candidate['kind'];
|
|
100
|
+
const testId = candidate['testId'];
|
|
101
|
+
if (typeof runId !== 'string' ||
|
|
102
|
+
runId.length === 0 ||
|
|
103
|
+
typeof obligationId !== 'string' ||
|
|
104
|
+
obligationId.length === 0 ||
|
|
105
|
+
typeof kind !== 'string' ||
|
|
106
|
+
kind.length === 0 ||
|
|
107
|
+
typeof testId !== 'string' ||
|
|
108
|
+
testId.length === 0) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
const payload = candidate['payload'];
|
|
112
|
+
if (!isJsonValue(payload))
|
|
113
|
+
return false;
|
|
114
|
+
const origin = candidate['origin'];
|
|
115
|
+
if (origin !== 'suite-submitted' && origin !== 'engine-observed')
|
|
116
|
+
return false;
|
|
117
|
+
try {
|
|
118
|
+
return (recordIdOf({
|
|
119
|
+
runId,
|
|
120
|
+
obligationId,
|
|
121
|
+
kind,
|
|
122
|
+
testId,
|
|
123
|
+
origin,
|
|
124
|
+
payload,
|
|
125
|
+
}) === recordId);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* True when a record asserts the witnessed tier AND its provenance
|
|
133
|
+
* verifies ({@link isProvenancedRecord}). The single witnessed-tier
|
|
134
|
+
* predicate for the engine and the reporter's trust display, so a
|
|
135
|
+
* record can never display as witnessed while the engine would demote
|
|
136
|
+
* it (or vice versa).
|
|
137
|
+
*
|
|
138
|
+
* Args:
|
|
139
|
+
* record: an arbitrary record-shaped value.
|
|
140
|
+
*
|
|
141
|
+
* Returns:
|
|
142
|
+
* boolean: witnessed tier with verified provenance.
|
|
143
|
+
*/
|
|
144
|
+
export function isWitnessedRecord(record) {
|
|
145
|
+
if (typeof record !== 'object' || record === null || Array.isArray(record)) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return (record['trust'] === 'witnessed' &&
|
|
149
|
+
isProvenancedRecord(record));
|
|
150
|
+
}
|
|
151
|
+
/** Shape a 64-char lowercase hex MAC must have. */
|
|
152
|
+
const MAC_PATTERN = /^[0-9a-f]{64}$/;
|
|
153
|
+
/** Domain tag binding v2 attestation MACs to the ledger envelope format. */
|
|
154
|
+
export const ATTESTATION_DOMAIN = 'gateforge.ledger.v2';
|
|
155
|
+
/** The only attestation envelope version this code produces or honors. */
|
|
156
|
+
export const ATTESTATION_VERSION = 2;
|
|
157
|
+
/** Shape a 64-char lowercase hex input digest must have. */
|
|
158
|
+
const INPUT_DIGEST_PATTERN = /^[0-9a-f]{64}$/;
|
|
159
|
+
/**
|
|
160
|
+
* Normalizes a record-id set for MAC computation: deduplicated and
|
|
161
|
+
* sorted by codepoint, so the witness and every verifier hash the same
|
|
162
|
+
* canonical list regardless of insertion or serialization order.
|
|
163
|
+
*/
|
|
164
|
+
function canonicalIdSet(recordIds) {
|
|
165
|
+
return [...new Set(recordIds)].sort(compareStrings);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Computes the witness-ledger MAC (pin #7): HMAC-SHA256 over the
|
|
169
|
+
* GF-canonical JSON of `{runId, recordIds}` keyed by the witness's
|
|
170
|
+
* VERIFIER KEY — a secret handed to the witness and the verifying CLI
|
|
171
|
+
* by the run orchestrator, never to the tested suite (the suite's run
|
|
172
|
+
* token authorizes submissions; it must not authorize attestation).
|
|
173
|
+
* The MAC makes the durable, suite-writable manifest append tamper-
|
|
174
|
+
* evident: changing one id, adding a forged id, or transplanting the
|
|
175
|
+
* set across runs invalidates it.
|
|
176
|
+
*
|
|
177
|
+
* Args:
|
|
178
|
+
* verifierKey: the witness verifier secret (non-empty).
|
|
179
|
+
* runId: the run manifest identity binding the set to THIS run.
|
|
180
|
+
* recordIds: the issued record ids (normalized internally).
|
|
181
|
+
*
|
|
182
|
+
* Returns:
|
|
183
|
+
* string: 64-char lowercase hex HMAC.
|
|
184
|
+
*
|
|
185
|
+
* Throws:
|
|
186
|
+
* TypeError: when the verifier key is empty.
|
|
187
|
+
*/
|
|
188
|
+
export function ledgerMac(verifierKey, runId, recordIds) {
|
|
189
|
+
if (typeof verifierKey !== 'string' || verifierKey.length === 0) {
|
|
190
|
+
throw new TypeError('ledgerMac: verifier key must be a non-empty string');
|
|
191
|
+
}
|
|
192
|
+
if (typeof runId !== 'string' || runId.length === 0) {
|
|
193
|
+
throw new TypeError('ledgerMac: runId must be a non-empty string');
|
|
194
|
+
}
|
|
195
|
+
return createHmac('sha256', verifierKey)
|
|
196
|
+
.update(canonicalJson({ runId, recordIds: canonicalIdSet(recordIds) }))
|
|
197
|
+
.digest('hex');
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Verifies a ledger MAC ({@link ledgerMac}) in constant time where the
|
|
201
|
+
* inputs allow it. Adversary-controlled input never throws.
|
|
202
|
+
*
|
|
203
|
+
* Args:
|
|
204
|
+
* verifierKey: the witness verifier secret (non-empty).
|
|
205
|
+
* runId: the run manifest identity the set claims.
|
|
206
|
+
* recordIds: the record-id set the MAC must cover (normalized
|
|
207
|
+
* internally, exactly like the witness computed it).
|
|
208
|
+
* mac: the claimed MAC (64-char lowercase hex).
|
|
209
|
+
*
|
|
210
|
+
* Returns:
|
|
211
|
+
* boolean: true only when the MAC verifies over the exact set.
|
|
212
|
+
*/
|
|
213
|
+
export function verifyLedgerMac(verifierKey, runId, recordIds, mac) {
|
|
214
|
+
if (typeof mac !== 'string' || !MAC_PATTERN.test(mac))
|
|
215
|
+
return false;
|
|
216
|
+
try {
|
|
217
|
+
const expected = Buffer.from(ledgerMac(verifierKey, runId, recordIds), 'hex');
|
|
218
|
+
const claimed = Buffer.from(mac, 'hex');
|
|
219
|
+
return expected.length === claimed.length && timingSafeEqual(expected, claimed);
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Computes the v2 attestation MAC (plan §11.3): HMAC-SHA256 over the
|
|
227
|
+
* GF-canonical JSON of `{domain, attestationVersion: 2, runId,
|
|
228
|
+
* invocationId, inputDigest, recordIds}` keyed by the witness's VERIFIER
|
|
229
|
+
* KEY — a secret the tested suite never receives. The fixed domain tag
|
|
230
|
+
* prevents cross-format signature acceptance: a legacy `{runId,
|
|
231
|
+
* recordIds}` MAC can never verify as a v2 attestation, and a v2 MAC
|
|
232
|
+
* can never verify as anything else.
|
|
233
|
+
*
|
|
234
|
+
* Args:
|
|
235
|
+
* verifierKey: the witness verifier secret (non-empty).
|
|
236
|
+
* body: runId, invocationId, 64-hex inputDigest, and issued record ids
|
|
237
|
+
* (normalized internally: deduplicated, codepoint-sorted).
|
|
238
|
+
*
|
|
239
|
+
* Returns:
|
|
240
|
+
* string: 64-char lowercase hex HMAC.
|
|
241
|
+
*
|
|
242
|
+
* Throws:
|
|
243
|
+
* TypeError: when the verifier key is empty, an identity is empty, or
|
|
244
|
+
* the input digest is not 64-char lowercase hex.
|
|
245
|
+
*/
|
|
246
|
+
export function attestationMac(verifierKey, body) {
|
|
247
|
+
if (typeof verifierKey !== 'string' || verifierKey.length === 0) {
|
|
248
|
+
throw new TypeError('attestationMac: verifier key must be a non-empty string');
|
|
249
|
+
}
|
|
250
|
+
if (typeof body.runId !== 'string' || body.runId.length === 0) {
|
|
251
|
+
throw new TypeError('attestationMac: runId must be a non-empty string');
|
|
252
|
+
}
|
|
253
|
+
if (typeof body.invocationId !== 'string' || body.invocationId.length === 0) {
|
|
254
|
+
throw new TypeError('attestationMac: invocationId must be a non-empty string');
|
|
255
|
+
}
|
|
256
|
+
if (typeof body.inputDigest !== 'string' || !INPUT_DIGEST_PATTERN.test(body.inputDigest)) {
|
|
257
|
+
throw new TypeError('attestationMac: inputDigest must be 64-char lowercase hex');
|
|
258
|
+
}
|
|
259
|
+
return createHmac('sha256', verifierKey)
|
|
260
|
+
.update(canonicalJson({
|
|
261
|
+
domain: ATTESTATION_DOMAIN,
|
|
262
|
+
attestationVersion: ATTESTATION_VERSION,
|
|
263
|
+
runId: body.runId,
|
|
264
|
+
invocationId: body.invocationId,
|
|
265
|
+
inputDigest: body.inputDigest,
|
|
266
|
+
recordIds: canonicalIdSet(body.recordIds),
|
|
267
|
+
}))
|
|
268
|
+
.digest('hex');
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Verifies a v2 attestation MAC ({@link attestationMac}) in constant
|
|
272
|
+
* time where the inputs allow it. Adversary-controlled input never
|
|
273
|
+
* throws. A legacy `{runId, recordIds}` MAC is structurally incapable
|
|
274
|
+
* of verifying here — the domain tag and the extra fields change the
|
|
275
|
+
* signed bytes — so old evidence can never authorize through this path.
|
|
276
|
+
*
|
|
277
|
+
* Args:
|
|
278
|
+
* verifierKey: the witness verifier secret (non-empty).
|
|
279
|
+
* body: the claimed attestation body (runId, invocationId,
|
|
280
|
+
* inputDigest, recordIds).
|
|
281
|
+
* mac: the claimed MAC (64-char lowercase hex).
|
|
282
|
+
*
|
|
283
|
+
* Returns:
|
|
284
|
+
* boolean: true only when the MAC verifies over the exact v2 body.
|
|
285
|
+
*/
|
|
286
|
+
export function verifyAttestationMac(verifierKey, body, mac) {
|
|
287
|
+
if (typeof mac !== 'string' || !MAC_PATTERN.test(mac))
|
|
288
|
+
return false;
|
|
289
|
+
if (typeof body !== 'object' ||
|
|
290
|
+
body === null ||
|
|
291
|
+
typeof body.runId !== 'string' ||
|
|
292
|
+
typeof body.invocationId !== 'string' ||
|
|
293
|
+
typeof body.inputDigest !== 'string' ||
|
|
294
|
+
!Array.isArray(body.recordIds)) {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
const expected = Buffer.from(attestationMac(verifierKey, body), 'hex');
|
|
299
|
+
const claimed = Buffer.from(mac, 'hex');
|
|
300
|
+
return expected.length === claimed.length && timingSafeEqual(expected, claimed);
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
//# sourceMappingURL=provenance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provenance.js","sourceRoot":"","sources":["../src/provenance.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type GateReceipt } from '../schemas/gate-receipt.js';
|
|
2
|
+
/** Domain tag binding receipt MACs to the gate-receipt envelope format. */
|
|
3
|
+
export declare const RECEIPT_DOMAIN = "gateforge.receipt.v1";
|
|
4
|
+
/** The only receipt envelope version this code produces or honors. */
|
|
5
|
+
export declare const RECEIPT_VERSION = 1;
|
|
6
|
+
/** The unsigned receipt body (everything the MAC covers). */
|
|
7
|
+
export type GateReceiptBody = Omit<GateReceipt, 'mac'>;
|
|
8
|
+
/**
|
|
9
|
+
* Computes the receipt MAC (ADR 0005 D3): HMAC-SHA256 over the GF-canonical
|
|
10
|
+
* JSON of `{domain: 'gateforge.receipt.v1', receiptVersion: 1, ...body}`
|
|
11
|
+
* keyed by the witness verifier key — the same secret the tested suite
|
|
12
|
+
* never receives for ledger attestations. The fixed domain tag prevents
|
|
13
|
+
* cross-format signature acceptance in BOTH directions: a v2 ledger MAC
|
|
14
|
+
* can never verify as a receipt, and a receipt MAC can never verify as a
|
|
15
|
+
* ledger attestation.
|
|
16
|
+
*
|
|
17
|
+
* Args:
|
|
18
|
+
* verifierKey: the witness verifier secret (non-empty).
|
|
19
|
+
* body: the unsigned receipt body.
|
|
20
|
+
*
|
|
21
|
+
* Returns:
|
|
22
|
+
* string: 64-char lowercase hex HMAC.
|
|
23
|
+
*
|
|
24
|
+
* Throws:
|
|
25
|
+
* TypeError: when the verifier key is empty.
|
|
26
|
+
*/
|
|
27
|
+
export declare function gateReceiptMac(verifierKey: string, body: GateReceiptBody): string;
|
|
28
|
+
/** Typed reasons a receipt candidate failed verification (fail closed). */
|
|
29
|
+
export type ReceiptRejection = 'missing' | 'malformed' | 'mac-fail' | 'input-digest-mismatch' | 'policy-digest-mismatch' | 'selection-digest-mismatch' | 'catalog-digest-mismatch' | 'execution-digest-mismatch' | 'attestation-digest-mismatch' | 'not-clean';
|
|
30
|
+
/** The verification outcome: a validated receipt or a typed rejection. */
|
|
31
|
+
export type ReceiptVerification = {
|
|
32
|
+
ok: true;
|
|
33
|
+
receipt: GateReceipt;
|
|
34
|
+
} | {
|
|
35
|
+
ok: false;
|
|
36
|
+
rejection: ReceiptRejection;
|
|
37
|
+
detail: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Verifies a receipt candidate end-to-end (fail closed):
|
|
41
|
+
* 1. structural — the value must parse against the strict v1 schema;
|
|
42
|
+
* 2. authenticity — the HMAC must verify over the exact body (a forged
|
|
43
|
+
* or tampered receipt is a `mac-fail`, never a downgrade);
|
|
44
|
+
* 3. binding — when an expected context is given, every supplied digest
|
|
45
|
+
* must equal the receipt's (mismatches are typed, per-field);
|
|
46
|
+
* 4. content — the sealed verdict summary must be clean (blocking 0).
|
|
47
|
+
* Adversary-controlled input never throws.
|
|
48
|
+
*
|
|
49
|
+
* Args:
|
|
50
|
+
* verifierKey: the witness verifier secret (non-empty).
|
|
51
|
+
* candidate: an arbitrary value (e.g. a parsed receipt.json).
|
|
52
|
+
* expected: optional digests the receipt must match for THIS use.
|
|
53
|
+
*
|
|
54
|
+
* Returns:
|
|
55
|
+
* ReceiptVerification: the validated receipt or a typed rejection.
|
|
56
|
+
*/
|
|
57
|
+
export declare function verifyGateReceipt(verifierKey: string, candidate: unknown, expected?: {
|
|
58
|
+
inputDigest?: string;
|
|
59
|
+
trustedPolicyDigest?: string;
|
|
60
|
+
selectionDigest?: string;
|
|
61
|
+
catalogDigest?: string;
|
|
62
|
+
executionResultDigest?: string;
|
|
63
|
+
evidenceAttestationDigest?: string | null;
|
|
64
|
+
}): ReceiptVerification;
|
|
65
|
+
/** The expected-digest shape of {@link verifyGateReceipt} (named for docs). */
|
|
66
|
+
export interface ReceiptVerificationExpectations {
|
|
67
|
+
inputDigest?: string;
|
|
68
|
+
trustedPolicyDigest?: string;
|
|
69
|
+
selectionDigest?: string;
|
|
70
|
+
catalogDigest?: string;
|
|
71
|
+
executionResultDigest?: string;
|
|
72
|
+
evidenceAttestationDigest?: string | null;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/receipt/index.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEjF,2EAA2E;AAC3E,eAAO,MAAM,cAAc,yBAAyB,CAAC;AAErD,sEAAsE;AACtE,eAAO,MAAM,eAAe,IAAI,CAAC;AAKjC,6DAA6D;AAC7D,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;AAEvD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,MAAM,CAYjF;AAED,2EAA2E;AAC3E,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,WAAW,GACX,UAAU,GACV,uBAAuB,GACvB,wBAAwB,GACxB,2BAA2B,GAC3B,yBAAyB,GACzB,2BAA2B,GAC3B,6BAA6B,GAC7B,WAAW,CAAC;AAEhB,0EAA0E;AAC1E,MAAM,MAAM,mBAAmB,GAC3B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,GAClC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,OAAO,EAClB,QAAQ,GAAE;IACR,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,yBAAyB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC,GACL,mBAAmB,CAuFrB;AAED,+EAA+E;AAC/E,MAAM,WAAW,+BAA+B;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,yBAAyB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3C"}
|