@mikeargento/bitgraph-verify 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +26 -0
- package/dist/canonical.d.ts +54 -0
- package/dist/canonical.d.ts.map +1 -0
- package/dist/canonical.js +119 -0
- package/dist/canonical.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/proof-hash.d.ts +9 -0
- package/dist/proof-hash.d.ts.map +1 -0
- package/dist/proof-hash.js +75 -0
- package/dist/proof-hash.js.map +1 -0
- package/dist/types.d.ts +705 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/verifier.d.ts +27 -0
- package/dist/verifier.d.ts.map +1 -0
- package/dist/verifier.js +844 -0
- package/dist/verifier.js.map +1 -0
- package/package.json +33 -0
- package/src/canonical.ts +134 -0
- package/src/index.ts +31 -0
- package/src/proof-hash.ts +83 -0
- package/src/types.ts +778 -0
- package/src/verifier.ts +964 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bitgraph-core types
|
|
3
|
+
*
|
|
4
|
+
* All public-facing data structures for the BitGraph proof system.
|
|
5
|
+
* This file intentionally contains no logic.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Declares the enforcement tier of the host adapter that produced a proof.
|
|
9
|
+
*
|
|
10
|
+
* This field is SIGNED and therefore tamper-evident in transit.
|
|
11
|
+
* However, it is self-reported by the adapter and MUST NOT be treated as
|
|
12
|
+
* self-authenticating by verifiers.
|
|
13
|
+
*
|
|
14
|
+
* Trust is established by verifier policy (allowedMeasurements +
|
|
15
|
+
* attestation chain validation), not by this field alone.
|
|
16
|
+
*
|
|
17
|
+
* Tier semantics:
|
|
18
|
+
*
|
|
19
|
+
* "stub"
|
|
20
|
+
* Software-only. Key lives in process memory. No hardware protection.
|
|
21
|
+
* Suitable for development, testing, and local demos only.
|
|
22
|
+
* measurement MAY be a synthetic sentinel value.
|
|
23
|
+
*
|
|
24
|
+
* "hw-key"
|
|
25
|
+
* Hardware-protected signing key (Secure Enclave / TPM class).
|
|
26
|
+
* The key is non-exportable from the hardware boundary.
|
|
27
|
+
* However, the commit gate runs outside the measured boundary —
|
|
28
|
+
* the host feeds digests to the hardware for signing.
|
|
29
|
+
* This provides hardware-bound identity but NOT causal enforcement.
|
|
30
|
+
* measurement SHOULD identify the key environment.
|
|
31
|
+
*
|
|
32
|
+
* "measured-tee"
|
|
33
|
+
* Full causal enforcement. The commit gate, key management, nonce
|
|
34
|
+
* generation, monotonic counter, and signing all execute inside the
|
|
35
|
+
* attested enclave boundary. The host is treated as untrusted.
|
|
36
|
+
* Satisfies BitGraph's atomic causality invariant when combined with a
|
|
37
|
+
* verifier that pins allowedMeasurements to a known-good enclave image.
|
|
38
|
+
* measurement MUST identify the attested enclave image.
|
|
39
|
+
* attestation SHOULD be present and verified by the relying party.
|
|
40
|
+
*
|
|
41
|
+
* IMPORTANT: Signing this field prevents downgrade tampering in transit
|
|
42
|
+
* but does NOT prevent a malicious adapter from lying about its tier.
|
|
43
|
+
* A verifier requiring "measured-tee" guarantees MUST independently
|
|
44
|
+
* validate measurement and attestation — not rely on this field alone.
|
|
45
|
+
*/
|
|
46
|
+
export type EnforcementTier = "stub" | "hw-key" | "measured-tee";
|
|
47
|
+
/**
|
|
48
|
+
* A fully self-contained, verifiable commit proof.
|
|
49
|
+
*
|
|
50
|
+
* Two orthogonal facts are encoded:
|
|
51
|
+
*
|
|
52
|
+
* (A) Cryptographic fact — proven by signer.publicKeyB64 + signer.signatureB64
|
|
53
|
+
* Answers: "Who signed?" — objective, machine-checkable.
|
|
54
|
+
*
|
|
55
|
+
* (B) Enforcement fact — proven by environment.measurement + environment.attestation
|
|
56
|
+
* + verifier allowlist policy.
|
|
57
|
+
* Answers: "Under what enforced conditions was signing allowed?"
|
|
58
|
+
* Atomic causality lives here, not in (A).
|
|
59
|
+
*
|
|
60
|
+
* Signed body covers:
|
|
61
|
+
* version, artifact, commit, signer.publicKeyB64,
|
|
62
|
+
* environment.enforcement, environment.measurement,
|
|
63
|
+
* environment.attestation.format (when present),
|
|
64
|
+
* agency.actor identity (when agency is present)
|
|
65
|
+
*
|
|
66
|
+
* Outside the signature (advisory / vendor-signed / independently verifiable):
|
|
67
|
+
* signer.signatureB64, environment.attestation.reportB64,
|
|
68
|
+
* agency.authorization (P-256 signed, independently verifiable), metadata
|
|
69
|
+
*/
|
|
70
|
+
export interface BitGraphProof {
|
|
71
|
+
/** Schema version. Hard-coded for forward-compatibility detection. */
|
|
72
|
+
version: "bitgraph/1";
|
|
73
|
+
/** Describes the committed artifact. */
|
|
74
|
+
artifact: {
|
|
75
|
+
/**
|
|
76
|
+
* Hash algorithm applied to the raw input bytes.
|
|
77
|
+
* Only "sha256" is defined for bitgraph/1.
|
|
78
|
+
*/
|
|
79
|
+
hashAlg: "sha256";
|
|
80
|
+
/** Base64-standard (RFC 4648 §4) encoded SHA-256 digest of the input bytes. */
|
|
81
|
+
digestB64: string;
|
|
82
|
+
};
|
|
83
|
+
/** Describes the commit context that ensures uniqueness and ordering. */
|
|
84
|
+
commit: {
|
|
85
|
+
/** Base64-encoded boundary-fresh nonce produced by the host. */
|
|
86
|
+
nonceB64: string;
|
|
87
|
+
/**
|
|
88
|
+
* Monotonic counter value at commit time.
|
|
89
|
+
* Decimal string (no leading zeros unless value is "0", no leading +,
|
|
90
|
+
* ASCII digits only) to avoid IEEE-754 precision loss.
|
|
91
|
+
* Compared as BigInt by the verifier.
|
|
92
|
+
* Present only when the host implements nextCounter().
|
|
93
|
+
*/
|
|
94
|
+
counter?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Unix epoch milliseconds at commit time from a TEE-trusted clock.
|
|
97
|
+
* Advisory — software clocks may be skewed. Use counter for ordering.
|
|
98
|
+
* Present only when the host implements secureTime().
|
|
99
|
+
*/
|
|
100
|
+
time?: number;
|
|
101
|
+
/**
|
|
102
|
+
* Base64-encoded hash of a previous BitGraphProof's canonical form,
|
|
103
|
+
* allowing callers to chain proofs into a verifiable sequence.
|
|
104
|
+
*/
|
|
105
|
+
prevB64?: string;
|
|
106
|
+
/**
|
|
107
|
+
* Opaque identifier for the enclave lifecycle (epoch).
|
|
108
|
+
* Generated at enclave boot as SHA-256(publicKeyB64 + ":" + bootNonceB64).
|
|
109
|
+
* Unique per enclave lifecycle. Changes on every restart.
|
|
110
|
+
*
|
|
111
|
+
* Verifiers use this to detect epoch boundaries: when epochId changes,
|
|
112
|
+
* a new enclave lifecycle has begun (new keypair, counter may reset).
|
|
113
|
+
*
|
|
114
|
+
* Included in the signed body (via commit) — tamper-evident.
|
|
115
|
+
*/
|
|
116
|
+
epochId?: string;
|
|
117
|
+
/**
|
|
118
|
+
* Counter value of the consumed slot allocation.
|
|
119
|
+
*
|
|
120
|
+
* When present, proves causal ordering: slotCounter < counter means
|
|
121
|
+
* the slot was allocated before the commit occurred. Combined with
|
|
122
|
+
* slotHashB64 and the embedded slotAllocation record, this provides
|
|
123
|
+
* structural evidence of nonce-first causality.
|
|
124
|
+
*
|
|
125
|
+
* Decimal string, compared as BigInt by the verifier.
|
|
126
|
+
*/
|
|
127
|
+
slotCounter?: string;
|
|
128
|
+
/**
|
|
129
|
+
* SHA-256 hash of the canonical slot allocation body (Base64-encoded).
|
|
130
|
+
*
|
|
131
|
+
* Cryptographically binds the commit to the exact slot allocation record.
|
|
132
|
+
* The slot body is canonicalized (sorted keys, compact JSON, UTF-8) and
|
|
133
|
+
* hashed. This field is SIGNED (included in the signed body), so the
|
|
134
|
+
* Ed25519 commit signature covers the binding.
|
|
135
|
+
*
|
|
136
|
+
* A verifier reconstructs the slot body from slotAllocation, canonicalizes,
|
|
137
|
+
* hashes, and checks: SHA-256(canonicalize(slotBody)) === slotHashB64.
|
|
138
|
+
* This prevents slot swapping — any change to the slot record would
|
|
139
|
+
* break the commit signature.
|
|
140
|
+
*/
|
|
141
|
+
slotHashB64?: string;
|
|
142
|
+
/**
|
|
143
|
+
* Epoch lineage link — present only on the FIRST proof of a new epoch.
|
|
144
|
+
*
|
|
145
|
+
* Establishes cryptographic succession: this epoch consumed the final
|
|
146
|
+
* proof of the previous epoch. The referenced proof's hash, signer,
|
|
147
|
+
* epochId, and counter are all included so a verifier can:
|
|
148
|
+
* 1. Confirm the predecessor proof is valid
|
|
149
|
+
* 2. Confirm prevProofHashB64 matches its canonical hash
|
|
150
|
+
* 3. Confirm prevPublicKeyB64 matches its signer
|
|
151
|
+
* 4. Detect forks (same predecessor consumed by multiple successors)
|
|
152
|
+
*
|
|
153
|
+
* New epochs continue lineage, not counters. Each epoch maintains its
|
|
154
|
+
* own local monotonic counter starting fresh.
|
|
155
|
+
*
|
|
156
|
+
* Single-successor invariant: a given (prevEpochId, prevCounter,
|
|
157
|
+
* prevProofHashB64) tuple MUST be consumed by AT MOST ONE successor
|
|
158
|
+
* epoch. Multiple epoch-genesis proofs referencing the same predecessor
|
|
159
|
+
* constitute a detectable fork.
|
|
160
|
+
*/
|
|
161
|
+
epochLink?: {
|
|
162
|
+
/** EpochId of the predecessor epoch. */
|
|
163
|
+
prevEpochId: string;
|
|
164
|
+
/** Ed25519 public key of the predecessor epoch's signer. */
|
|
165
|
+
prevPublicKeyB64: string;
|
|
166
|
+
/** Final counter value from the predecessor epoch. */
|
|
167
|
+
prevCounter: string;
|
|
168
|
+
/** SHA-256 hash of the full predecessor proof (canonicalized). */
|
|
169
|
+
prevProofHashB64: string;
|
|
170
|
+
/** EpochId of THIS (successor) epoch — must match proof's commit.epochId. */
|
|
171
|
+
toEpochId: string;
|
|
172
|
+
/** Ed25519 public key of THIS (successor) epoch — must match proof's signer.publicKeyB64. */
|
|
173
|
+
toPublicKeyB64: string;
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* Cryptographic identity of the signer.
|
|
178
|
+
* Contains only the signing keypair — who signed.
|
|
179
|
+
* Enforcement context lives in environment, not here.
|
|
180
|
+
*/
|
|
181
|
+
signer: {
|
|
182
|
+
/** Base64-encoded Ed25519 public key (32 bytes). */
|
|
183
|
+
publicKeyB64: string;
|
|
184
|
+
/**
|
|
185
|
+
* Base64-encoded Ed25519 signature (64 bytes) over the canonical
|
|
186
|
+
* serialization of the signed body.
|
|
187
|
+
* NOT included in the signed body itself.
|
|
188
|
+
*/
|
|
189
|
+
signatureB64: string;
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Enforcement context — under what conditions signing was allowed.
|
|
193
|
+
*
|
|
194
|
+
* enforcement and measurement are included in the signed body,
|
|
195
|
+
* making them tamper-evident. attestation.reportB64 is excluded
|
|
196
|
+
* from the signature because it is a vendor-signed document that
|
|
197
|
+
* self-authenticates.
|
|
198
|
+
*/
|
|
199
|
+
environment: {
|
|
200
|
+
/**
|
|
201
|
+
* Self-reported enforcement tier.
|
|
202
|
+
* SIGNED — tamper-evident in transit but not self-authenticating.
|
|
203
|
+
* Verifiers MUST NOT treat this as sufficient evidence of tier.
|
|
204
|
+
*/
|
|
205
|
+
enforcement: EnforcementTier;
|
|
206
|
+
/**
|
|
207
|
+
* Platform-specific measurement string.
|
|
208
|
+
* - stub: MAY be a synthetic sentinel
|
|
209
|
+
* - hw-key: SHOULD identify the key environment
|
|
210
|
+
* - measured-tee: MUST identify the attested enclave image
|
|
211
|
+
* SIGNED — pinned by verifier allowedMeasurements policy.
|
|
212
|
+
*/
|
|
213
|
+
measurement: string;
|
|
214
|
+
/**
|
|
215
|
+
* Optional platform attestation report.
|
|
216
|
+
* If present, format MUST be a non-empty string.
|
|
217
|
+
* reportB64 is excluded from the signature (vendor-signed).
|
|
218
|
+
* attestation.format IS included in the signed body.
|
|
219
|
+
*/
|
|
220
|
+
attestation?: {
|
|
221
|
+
/**
|
|
222
|
+
* Identifies the attestation document format.
|
|
223
|
+
* e.g. "aws-nitro", "sgx-dcap", "amd-sev-snp"
|
|
224
|
+
* SIGNED — prevents semantic ambiguity via format rewrite.
|
|
225
|
+
*/
|
|
226
|
+
format: string;
|
|
227
|
+
/**
|
|
228
|
+
* Base64-encoded raw attestation report bytes.
|
|
229
|
+
* NOT signed — vendor-signed and self-authenticating.
|
|
230
|
+
* Platform-specific verifiers must parse and validate this.
|
|
231
|
+
*/
|
|
232
|
+
reportB64: string;
|
|
233
|
+
};
|
|
234
|
+
};
|
|
235
|
+
/**
|
|
236
|
+
* Optional external timestamps (e.g. RFC 3161 TSA tokens).
|
|
237
|
+
* NOT included in the signed body. Independently verifiable.
|
|
238
|
+
*
|
|
239
|
+
* When present, each entry contains a TSA authority, ISO 8601 time,
|
|
240
|
+
* DER-encoded token (base64), digest algorithm, and the digest
|
|
241
|
+
* that was timestamped. The "artifact" timestamp covers the artifact
|
|
242
|
+
* digest; the "proof" timestamp covers the proof's canonical hash.
|
|
243
|
+
*/
|
|
244
|
+
timestamps?: {
|
|
245
|
+
artifact?: {
|
|
246
|
+
authority: string;
|
|
247
|
+
time: string;
|
|
248
|
+
tokenB64: string;
|
|
249
|
+
digestAlg: string;
|
|
250
|
+
digestB64: string;
|
|
251
|
+
};
|
|
252
|
+
proof?: {
|
|
253
|
+
authority: string;
|
|
254
|
+
time: string;
|
|
255
|
+
tokenB64: string;
|
|
256
|
+
digestAlg: string;
|
|
257
|
+
digestB64: string;
|
|
258
|
+
};
|
|
259
|
+
};
|
|
260
|
+
/**
|
|
261
|
+
* Optional agency envelope — binds actor identity to this proof.
|
|
262
|
+
*
|
|
263
|
+
* When present, proves WHO authorized the commitment:
|
|
264
|
+
* - actor: device-bound identity (Secure Enclave P-256 key)
|
|
265
|
+
* - authorization: the possession commitment the actor signed
|
|
266
|
+
* (artifact hash + enclave-issued challenge + purpose + timestamp)
|
|
267
|
+
*
|
|
268
|
+
* Two independent signatures in the proof:
|
|
269
|
+
* - P-256 ECDSA (device Secure Enclave) → proves WHO authorized it
|
|
270
|
+
* - Ed25519 (Nitro Enclave) → proves it was committed inside the TEE
|
|
271
|
+
*
|
|
272
|
+
* The actor identity summary (keyId, publicKeyB64, algorithm, provider)
|
|
273
|
+
* is also included in the signed body (via SignedBody.actor), making it
|
|
274
|
+
* tamper-evident under the enclave's Ed25519 signature.
|
|
275
|
+
*
|
|
276
|
+
* The full authorization envelope (including the device's P-256 signature)
|
|
277
|
+
* lives here, outside the Ed25519 signed body — like attestation.reportB64,
|
|
278
|
+
* it is independently verifiable.
|
|
279
|
+
*
|
|
280
|
+
* NOT included in the Ed25519 signed body (independently verifiable).
|
|
281
|
+
*/
|
|
282
|
+
agency?: AgencyEnvelope;
|
|
283
|
+
/**
|
|
284
|
+
* Embedded slot allocation record proving nonce-first causality.
|
|
285
|
+
*
|
|
286
|
+
* When present, this is a self-contained signed record that the enclave
|
|
287
|
+
* created BEFORE any artifact hash was known. The verifier checks:
|
|
288
|
+
* 1. Slot signature valid (enclave created it)
|
|
289
|
+
* 2. Slot body has no artifact hash (causal independence)
|
|
290
|
+
* 3. Slot nonce == commit nonce (binding)
|
|
291
|
+
* 4. Slot counter < commit counter (ordering)
|
|
292
|
+
* 5. Same key and epoch (same enclave)
|
|
293
|
+
*
|
|
294
|
+
* NOT included in the Ed25519 signed commit body — independently
|
|
295
|
+
* verifiable via its own Ed25519 signature (same pattern as
|
|
296
|
+
* attestation.reportB64 and agency.authorization).
|
|
297
|
+
*/
|
|
298
|
+
slotAllocation?: SlotAllocation;
|
|
299
|
+
/**
|
|
300
|
+
* Optional policy binding — cryptographic proof that a specific policy
|
|
301
|
+
* document governed this action.
|
|
302
|
+
*
|
|
303
|
+
* Contains the SHA-256 hash of the policy document, plus optional
|
|
304
|
+
* human-readable name and version. INCLUDED in the Ed25519 signed body
|
|
305
|
+
* so the policy binding is tamper-evident and cryptographically sealed.
|
|
306
|
+
*
|
|
307
|
+
* Verifiers can confirm the policy hash matches a known-good policy
|
|
308
|
+
* document, establishing what rules the agent was operating under.
|
|
309
|
+
*/
|
|
310
|
+
policy?: PolicyBinding;
|
|
311
|
+
/**
|
|
312
|
+
* Optional human-readable attribution claim.
|
|
313
|
+
*
|
|
314
|
+
* A free-form human claim associated with the artifact and commit event.
|
|
315
|
+
* INCLUDED in the Ed25519 signed body — cryptographically sealed and
|
|
316
|
+
* tamper-evident. Cannot be modified after the proof is created.
|
|
317
|
+
*
|
|
318
|
+
* This is a claim, not a guaranteed identity. It complements (does not
|
|
319
|
+
* replace) the cryptographic actor key in agency.
|
|
320
|
+
*
|
|
321
|
+
* All fields are optional. If no fields are provided, omit the object.
|
|
322
|
+
*/
|
|
323
|
+
attribution?: Attribution;
|
|
324
|
+
/**
|
|
325
|
+
* Caller-supplied metadata key/value pairs.
|
|
326
|
+
* NOT included in the signed body. Treat as advisory only.
|
|
327
|
+
*/
|
|
328
|
+
metadata?: Record<string, unknown>;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Policy applied during Constructor initialization.
|
|
332
|
+
*/
|
|
333
|
+
export interface BitGraphPolicy {
|
|
334
|
+
/**
|
|
335
|
+
* If true, commits are rejected when host.nextCounter() is unavailable.
|
|
336
|
+
* Defaults to false (counter is advisory).
|
|
337
|
+
*/
|
|
338
|
+
requireCounter?: boolean;
|
|
339
|
+
/**
|
|
340
|
+
* If true, commits are rejected when host.secureTime() is unavailable.
|
|
341
|
+
* Defaults to false.
|
|
342
|
+
*/
|
|
343
|
+
requireTime?: boolean;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Constraints checked by the verifier against a BitGraphProof.
|
|
347
|
+
* All fields are optional; omitting a field skips that check.
|
|
348
|
+
*
|
|
349
|
+
* Trust model:
|
|
350
|
+
* - Cryptographic identity is proven by signature verification.
|
|
351
|
+
* - Enforcement guarantees are proven by measurement + attestation policy.
|
|
352
|
+
* - requireEnforcement alone is NOT sufficient for security — it must
|
|
353
|
+
* be combined with allowedMeasurements (and requireAttestation for
|
|
354
|
+
* measured-tee) to establish actual trust.
|
|
355
|
+
*/
|
|
356
|
+
export interface VerificationPolicy {
|
|
357
|
+
/**
|
|
358
|
+
* Require a specific enforcement tier.
|
|
359
|
+
* MUST be combined with allowedMeasurements for security.
|
|
360
|
+
* This field is tamper-evident (signed) but not self-authenticating.
|
|
361
|
+
*/
|
|
362
|
+
requireEnforcement?: EnforcementTier;
|
|
363
|
+
/**
|
|
364
|
+
* Accepted measurement strings. The proof's environment.measurement
|
|
365
|
+
* must exactly match one of the listed values.
|
|
366
|
+
* This is the primary trust anchor for enforcement verification.
|
|
367
|
+
*/
|
|
368
|
+
allowedMeasurements?: string[];
|
|
369
|
+
/**
|
|
370
|
+
* Accepted public keys (Base64-encoded). The proof's
|
|
371
|
+
* signer.publicKeyB64 must exactly match one of the listed values.
|
|
372
|
+
*/
|
|
373
|
+
allowedPublicKeys?: string[];
|
|
374
|
+
/**
|
|
375
|
+
* If true, the proof must contain environment.attestation.
|
|
376
|
+
* Required for full measured-tee verification.
|
|
377
|
+
*/
|
|
378
|
+
requireAttestation?: boolean;
|
|
379
|
+
/**
|
|
380
|
+
* If provided, environment.attestation.format must match one of
|
|
381
|
+
* the listed format strings (e.g. ["aws-nitro", "sgx-dcap"]).
|
|
382
|
+
* Only checked when attestation is present.
|
|
383
|
+
*/
|
|
384
|
+
requireAttestationFormat?: string[];
|
|
385
|
+
/**
|
|
386
|
+
* If set, proof.commit.counter must be >= this value (as BigInt).
|
|
387
|
+
* Decimal string, compared as BigInt to avoid precision loss.
|
|
388
|
+
*/
|
|
389
|
+
minCounter?: string;
|
|
390
|
+
/**
|
|
391
|
+
* If set, proof.commit.counter must be <= this value (as BigInt).
|
|
392
|
+
* Use with minCounter to enforce a sliding replay-resistance window.
|
|
393
|
+
*/
|
|
394
|
+
maxCounter?: string;
|
|
395
|
+
/**
|
|
396
|
+
* If set, proof.commit.time must be >= this Unix ms value.
|
|
397
|
+
*/
|
|
398
|
+
minTime?: number;
|
|
399
|
+
/**
|
|
400
|
+
* If set, proof.commit.time must be <= this Unix ms value.
|
|
401
|
+
* Use with minTime to enforce a validity window.
|
|
402
|
+
*/
|
|
403
|
+
maxTime?: number;
|
|
404
|
+
/**
|
|
405
|
+
* If true, proof.commit.epochId must be present.
|
|
406
|
+
* Required for production deployments using the epoch model.
|
|
407
|
+
* Verifiers should track epochId to detect epoch boundaries
|
|
408
|
+
* (enclave restarts) and cross-reference with an external
|
|
409
|
+
* monotonic anchor (e.g., DynamoDB) for cross-epoch continuity.
|
|
410
|
+
*/
|
|
411
|
+
requireEpochId?: boolean;
|
|
412
|
+
/**
|
|
413
|
+
* If true, proof.agency must be present with a valid actor identity
|
|
414
|
+
* and authorization signature. The proof must answer WHO authorized
|
|
415
|
+
* the commitment, not just WHAT and WHERE.
|
|
416
|
+
*/
|
|
417
|
+
requireActor?: boolean;
|
|
418
|
+
/**
|
|
419
|
+
* Accepted actor key IDs. The proof's agency.actor.keyId must
|
|
420
|
+
* exactly match one of the listed values.
|
|
421
|
+
* Key ID is hex(SHA-256(SPKI DER public key bytes)) — stable,
|
|
422
|
+
* deterministic, derived from the raw key material.
|
|
423
|
+
*/
|
|
424
|
+
allowedActorKeyIds?: string[];
|
|
425
|
+
/**
|
|
426
|
+
* Accepted actor key providers. The proof's agency.actor.provider
|
|
427
|
+
* must exactly match one of the listed values.
|
|
428
|
+
* e.g. ["apple-secure-enclave", "android-strongbox", "webauthn"]
|
|
429
|
+
*/
|
|
430
|
+
allowedActorProviders?: string[];
|
|
431
|
+
/**
|
|
432
|
+
* If true, proof must contain a valid slotAllocation proving
|
|
433
|
+
* nonce-first atomic causality (BitGraph causal commit model).
|
|
434
|
+
*
|
|
435
|
+
* When enabled, the verifier checks:
|
|
436
|
+
* - slotAllocation is present with valid Ed25519 signature
|
|
437
|
+
* - slotAllocation contains no artifact data (causal independence)
|
|
438
|
+
* - slotAllocation.nonceB64 === commit.nonceB64 (binding)
|
|
439
|
+
* - slotAllocation.counter < commit.counter (ordering)
|
|
440
|
+
* - slotAllocation.publicKeyB64 === signer.publicKeyB64 (same enclave)
|
|
441
|
+
* - slotAllocation.epochId === commit.epochId (same lifecycle)
|
|
442
|
+
*
|
|
443
|
+
* Required for verifiers that need to confirm true BitGraph causality,
|
|
444
|
+
* not just TEE-enforced signing with freshness.
|
|
445
|
+
*/
|
|
446
|
+
requireSlot?: boolean;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Binds a BitGraphProof to the policy document that governed the action.
|
|
450
|
+
*
|
|
451
|
+
* The digestB64 is the SHA-256 hash of the raw policy document bytes
|
|
452
|
+
* (UTF-8 encoded). Any verifier can recompute this from the original
|
|
453
|
+
* policy document to confirm the binding.
|
|
454
|
+
*
|
|
455
|
+
* Included in the Ed25519 signed body — tamper-evident and
|
|
456
|
+
* cryptographically sealed by the signer.
|
|
457
|
+
*/
|
|
458
|
+
export interface PolicyBinding {
|
|
459
|
+
/** SHA-256 hash of the policy document (Base64-standard, RFC 4648 §4). */
|
|
460
|
+
digestB64: string;
|
|
461
|
+
/**
|
|
462
|
+
* Digest of the BitGraph proof that committed this policy document.
|
|
463
|
+
* When present, the policy was authored through BitGraph (typically with
|
|
464
|
+
* biometric/passkey signing), making the authorship tamper-evident.
|
|
465
|
+
* Verifiers can look up this proof to confirm WHO authored the rules
|
|
466
|
+
* and WHEN they were signed — not just what the rules say.
|
|
467
|
+
*/
|
|
468
|
+
authorProofDigestB64?: string;
|
|
469
|
+
/** Human-readable name of the policy (e.g. "Financial Agent"). */
|
|
470
|
+
name?: string;
|
|
471
|
+
/** Version identifier of the policy document. */
|
|
472
|
+
version?: string;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Human-readable attribution claim associated with the artifact.
|
|
476
|
+
*
|
|
477
|
+
* All fields are optional. Included in the Ed25519 signed body so that
|
|
478
|
+
* the text is cryptographically sealed and tamper-evident.
|
|
479
|
+
*
|
|
480
|
+
* This is a claim, not a guaranteed identity. It complements the
|
|
481
|
+
* cryptographic actor key in agency.
|
|
482
|
+
*/
|
|
483
|
+
export interface Attribution {
|
|
484
|
+
/** Human name of the person or entity making the claim. */
|
|
485
|
+
name?: string;
|
|
486
|
+
/** Short description of the claim (e.g. "Original capture"). */
|
|
487
|
+
title?: string;
|
|
488
|
+
/** Free-form message associated with the artifact (e.g. "Shot at sunset in Buffalo."). */
|
|
489
|
+
message?: string;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Actor identity — a device-bound key that identifies WHO authorized
|
|
493
|
+
* a commitment.
|
|
494
|
+
*
|
|
495
|
+
* This structure appears in two places:
|
|
496
|
+
* 1. SignedBody.actor — signed by the TEE's Ed25519 key (tamper-evident)
|
|
497
|
+
* 2. BitGraphProof.agency.actor — in the full agency envelope
|
|
498
|
+
*
|
|
499
|
+
* The keyId is deterministic: hex(SHA-256(SPKI DER public key bytes)).
|
|
500
|
+
* Any verifier can recompute it from the raw public key to confirm
|
|
501
|
+
* the binding.
|
|
502
|
+
*/
|
|
503
|
+
export interface ActorIdentity {
|
|
504
|
+
/** Stable device key ID: hex(SHA-256(SPKI DER public key bytes)). */
|
|
505
|
+
keyId: string;
|
|
506
|
+
/** Base64 SPKI DER P-256 public key (standard format, any platform can parse). */
|
|
507
|
+
publicKeyB64: string;
|
|
508
|
+
/** Signature algorithm the device used. */
|
|
509
|
+
algorithm: "ES256";
|
|
510
|
+
/** Key origin: "apple-secure-enclave", "android-strongbox", "webauthn". */
|
|
511
|
+
provider: string;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* The canonical payload that the actor's device signs.
|
|
515
|
+
*
|
|
516
|
+
* Serialized as canonical JSON (sorted keys, compact, UTF-8) for signing.
|
|
517
|
+
* signatureB64 is excluded from the signed bytes (it IS the signature).
|
|
518
|
+
*
|
|
519
|
+
* Fields:
|
|
520
|
+
* - purpose: domain separation — prevents cross-context signature reuse
|
|
521
|
+
* - actorKeyId: must match actor.keyId (binds signature to specific key)
|
|
522
|
+
* - artifactHash: must match proof.artifact.digestB64 (binds to specific artifact)
|
|
523
|
+
* - challenge: enclave-issued nonce (prevents replay)
|
|
524
|
+
* - timestamp: Unix epoch ms (enclave checks freshness)
|
|
525
|
+
*/
|
|
526
|
+
export interface AuthorizationPayload {
|
|
527
|
+
/** Domain separation: prevents cross-context signature reuse. */
|
|
528
|
+
purpose: "bitgraph/commit-authorize/v1";
|
|
529
|
+
/** Must match actor.keyId. */
|
|
530
|
+
actorKeyId: string;
|
|
531
|
+
/** Base64 SHA-256 of artifact — must match proof.artifact.digestB64. */
|
|
532
|
+
artifactHash: string;
|
|
533
|
+
/** Enclave-issued challenge (prevents replay). */
|
|
534
|
+
challenge: string;
|
|
535
|
+
/** Unix epoch ms — enclave checks freshness. */
|
|
536
|
+
timestamp: number;
|
|
537
|
+
/**
|
|
538
|
+
* Optional protocol version. When present, included in the canonical
|
|
539
|
+
* JSON payload that is P-256-signed. Allows versioning the authorization
|
|
540
|
+
* format while maintaining backward compatibility (old payloads without
|
|
541
|
+
* this field remain valid).
|
|
542
|
+
*/
|
|
543
|
+
protocolVersion?: string;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* WebAuthn authorization payload — sent when the device signs via
|
|
547
|
+
* navigator.credentials.get() (passkey / Face ID / Touch ID).
|
|
548
|
+
*
|
|
549
|
+
* WebAuthn signs `authenticatorData || SHA-256(clientDataJSON)`, not
|
|
550
|
+
* arbitrary data. The enclave-issued challenge is embedded in
|
|
551
|
+
* clientDataJSON.challenge. The enclave must verify the WebAuthn
|
|
552
|
+
* signature over the standard WebAuthn signed data format.
|
|
553
|
+
*/
|
|
554
|
+
export interface WebAuthnAuthorization {
|
|
555
|
+
/** Domain separation — same as direct. */
|
|
556
|
+
purpose: "bitgraph/commit-authorize/v1";
|
|
557
|
+
/** Discriminator for verification path. */
|
|
558
|
+
format: "webauthn";
|
|
559
|
+
/** Must match actor.keyId. */
|
|
560
|
+
actorKeyId: string;
|
|
561
|
+
/** Base64 SHA-256 of artifact — must match proof.artifact.digestB64. */
|
|
562
|
+
artifactHash: string;
|
|
563
|
+
/** Enclave-issued challenge (base64). Embedded in clientDataJSON. */
|
|
564
|
+
challenge: string;
|
|
565
|
+
/** Unix epoch ms — checked for freshness. */
|
|
566
|
+
timestamp: number;
|
|
567
|
+
/** Base64-encoded raw authenticator data bytes. */
|
|
568
|
+
authenticatorDataB64: string;
|
|
569
|
+
/** Full clientDataJSON string (UTF-8). Contains challenge, origin, type. */
|
|
570
|
+
clientDataJSON: string;
|
|
571
|
+
/** Base64 DER ECDSA P-256 signature from the authenticator. */
|
|
572
|
+
signatureB64: string;
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Full agency envelope — lives in BitGraphProof.agency.
|
|
576
|
+
*
|
|
577
|
+
* Contains the actor identity and the authorization payload
|
|
578
|
+
* (including the device's P-256 signature). Independently verifiable:
|
|
579
|
+
* any verifier can check the P-256 signature over the authorization
|
|
580
|
+
* payload without needing any server or API.
|
|
581
|
+
*
|
|
582
|
+
* Two authorization formats:
|
|
583
|
+
* - Direct: P-256 signature over canonical JSON (native apps, test scripts)
|
|
584
|
+
* - WebAuthn: Standard WebAuthn assertion (browser passkeys, Face ID / Touch ID)
|
|
585
|
+
*/
|
|
586
|
+
export interface AgencyEnvelope {
|
|
587
|
+
/** Actor identity (matches SignedBody.actor when present). */
|
|
588
|
+
actor: ActorIdentity;
|
|
589
|
+
/** The possession commitment the actor signed. */
|
|
590
|
+
authorization: (AuthorizationPayload & {
|
|
591
|
+
/**
|
|
592
|
+
* Base64 DER ECDSA signature over canonical JSON of the
|
|
593
|
+
* AuthorizationPayload (excluding signatureB64 itself).
|
|
594
|
+
* P-256 / ES256 — verifiable with actor.publicKeyB64.
|
|
595
|
+
*/
|
|
596
|
+
signatureB64: string;
|
|
597
|
+
}) | WebAuthnAuthorization;
|
|
598
|
+
/**
|
|
599
|
+
* Present on batch proofs. The P-256 signature binds to the first digest;
|
|
600
|
+
* batchContext maps every proof in the batch back to that authorization.
|
|
601
|
+
*/
|
|
602
|
+
batchContext?: {
|
|
603
|
+
batchSize: number;
|
|
604
|
+
batchIndex: number;
|
|
605
|
+
batchDigests: string[];
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Signed slot allocation record.
|
|
610
|
+
*
|
|
611
|
+
* Created by the enclave's allocateSlot endpoint BEFORE any artifact hash
|
|
612
|
+
* is known. The slot body deliberately contains NO artifact data — this is
|
|
613
|
+
* the structural proof of causal independence.
|
|
614
|
+
*
|
|
615
|
+
* The enclave signs this record at allocation time and stores the nonce as
|
|
616
|
+
* a single-use resource in pendingSlots. When a client later calls commit
|
|
617
|
+
* with the slotId, the enclave consumes the slot and embeds this record in
|
|
618
|
+
* the proof. A verifier can then confirm:
|
|
619
|
+
*
|
|
620
|
+
* 1. The slot signature is valid (enclave created it)
|
|
621
|
+
* 2. The slot body contains no artifact hash (causal independence)
|
|
622
|
+
* 3. SHA-256(canonicalize(slotBody)) === commit.slotHashB64 (signed binding)
|
|
623
|
+
* 4. slotAllocation.nonceB64 === commit.nonceB64 (nonce binding)
|
|
624
|
+
* 5. slotAllocation.counter < commit.counter (ordering)
|
|
625
|
+
* 6. Same publicKeyB64 and epochId (same enclave lifecycle)
|
|
626
|
+
*
|
|
627
|
+
* Together these checks prove the nonce existed before the artifact was
|
|
628
|
+
* bound to it — the BitGraph atomic causality invariant.
|
|
629
|
+
*/
|
|
630
|
+
export interface SlotAllocation {
|
|
631
|
+
/** Schema version for slot records. Domain separation from proof bodies. */
|
|
632
|
+
version: "bitgraph/slot/1";
|
|
633
|
+
/** NSM-generated nonce. Becomes commit.nonceB64 when consumed. */
|
|
634
|
+
nonceB64: string;
|
|
635
|
+
/** Monotonic counter at allocation time. Must be < commit counter. */
|
|
636
|
+
counter: string;
|
|
637
|
+
/** Advisory timestamp (Unix epoch ms) at allocation time. Optional — BitGraph is causal, not temporal. */
|
|
638
|
+
time?: number;
|
|
639
|
+
/** Enclave lifecycle identifier. Must match commit.epochId. */
|
|
640
|
+
epochId: string;
|
|
641
|
+
/** Enclave Ed25519 public key. Must match signer.publicKeyB64. */
|
|
642
|
+
publicKeyB64: string;
|
|
643
|
+
/** Chain isolation identifier. Omitted for the default "global" chain. */
|
|
644
|
+
chainId?: string;
|
|
645
|
+
/** Ed25519 signature over canonical slot body (all fields above). */
|
|
646
|
+
signatureB64: string;
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* The object that is serialized and signed.
|
|
650
|
+
* Constructed internally by Constructor.commit() and reconstructed by
|
|
651
|
+
* verify() for signature validation.
|
|
652
|
+
*
|
|
653
|
+
* Exported so external adapters can construct test proofs without
|
|
654
|
+
* re-implementing the signing body layout.
|
|
655
|
+
*
|
|
656
|
+
* Signed fields:
|
|
657
|
+
* version, artifact, commit, publicKeyB64,
|
|
658
|
+
* enforcement, measurement, attestationFormat (when present),
|
|
659
|
+
* actor (when agency is present),
|
|
660
|
+
* attribution (when provided)
|
|
661
|
+
*/
|
|
662
|
+
export interface SignedBody {
|
|
663
|
+
version: "bitgraph/1";
|
|
664
|
+
artifact: BitGraphProof["artifact"];
|
|
665
|
+
commit: BitGraphProof["commit"];
|
|
666
|
+
/** Public key included to bind cryptographic identity to the body. */
|
|
667
|
+
publicKeyB64: string;
|
|
668
|
+
/** Enforcement tier — signed to prevent downgrade attacks. */
|
|
669
|
+
enforcement: EnforcementTier;
|
|
670
|
+
/** Measurement — primary trust anchor for enforcement verification. */
|
|
671
|
+
measurement: string;
|
|
672
|
+
/**
|
|
673
|
+
* Attestation format string — signed to prevent semantic ambiguity.
|
|
674
|
+
* Present only when environment.attestation is present.
|
|
675
|
+
* reportB64 is intentionally excluded (vendor-signed).
|
|
676
|
+
*/
|
|
677
|
+
attestationFormat?: string;
|
|
678
|
+
/**
|
|
679
|
+
* Actor identity summary — included in the signed body when agency
|
|
680
|
+
* is present.
|
|
681
|
+
*
|
|
682
|
+
* Follows the same pattern as attestationFormat: identity summary is
|
|
683
|
+
* signed (tamper-evident under Ed25519), while the full signature
|
|
684
|
+
* envelope (agency.authorization.signatureB64) lives outside the
|
|
685
|
+
* signed body and is independently verifiable.
|
|
686
|
+
*
|
|
687
|
+
* The TEE verifies the actor's P-256 signature BEFORE including this
|
|
688
|
+
* in the signed body, so its presence means the TEE confirmed the
|
|
689
|
+
* actor authorized this specific commitment.
|
|
690
|
+
*/
|
|
691
|
+
actor?: ActorIdentity;
|
|
692
|
+
/**
|
|
693
|
+
* Policy binding — sealed into the signed body.
|
|
694
|
+
* Present only when a policy document was active at commit time.
|
|
695
|
+
* Canonical serialization includes this when present.
|
|
696
|
+
*/
|
|
697
|
+
policy?: PolicyBinding;
|
|
698
|
+
/**
|
|
699
|
+
* Human-readable attribution claim — sealed into the signed body.
|
|
700
|
+
* Present only when the user provided attribution fields at commit time.
|
|
701
|
+
* Canonical serialization includes this when present.
|
|
702
|
+
*/
|
|
703
|
+
attribution?: Attribution;
|
|
704
|
+
}
|
|
705
|
+
//# sourceMappingURL=types.d.ts.map
|