burnledger 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +182 -0
- package/dist/cjs/cli.d.ts +16 -0
- package/dist/cjs/cli.d.ts.map +1 -0
- package/dist/cjs/cli.js +168 -0
- package/dist/cjs/cli.js.map +1 -0
- package/dist/cjs/client.d.ts +116 -0
- package/dist/cjs/client.d.ts.map +1 -0
- package/dist/cjs/client.js +353 -0
- package/dist/cjs/client.js.map +1 -0
- package/dist/cjs/crypto-browser.d.ts +7 -0
- package/dist/cjs/crypto-browser.d.ts.map +1 -0
- package/dist/cjs/crypto-browser.js +37 -0
- package/dist/cjs/crypto-browser.js.map +1 -0
- package/dist/cjs/crypto-node.d.ts +4 -0
- package/dist/cjs/crypto-node.d.ts.map +1 -0
- package/dist/cjs/crypto-node.js +30 -0
- package/dist/cjs/crypto-node.js.map +1 -0
- package/dist/cjs/crypto.d.ts +6 -0
- package/dist/cjs/crypto.d.ts.map +1 -0
- package/dist/cjs/crypto.js +4 -0
- package/dist/cjs/crypto.js.map +1 -0
- package/dist/cjs/errors.d.ts +76 -0
- package/dist/cjs/errors.d.ts.map +1 -0
- package/dist/cjs/errors.js +119 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/http.d.ts +35 -0
- package/dist/cjs/http.d.ts.map +1 -0
- package/dist/cjs/http.js +160 -0
- package/dist/cjs/http.js.map +1 -0
- package/dist/cjs/index.browser.d.ts +31 -0
- package/dist/cjs/index.browser.d.ts.map +1 -0
- package/dist/cjs/index.browser.js +54 -0
- package/dist/cjs/index.browser.js.map +1 -0
- package/dist/cjs/index.d.ts +26 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +50 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/keys.d.ts +12 -0
- package/dist/cjs/keys.d.ts.map +1 -0
- package/dist/cjs/keys.js +17 -0
- package/dist/cjs/keys.js.map +1 -0
- package/dist/cjs/models.d.ts +197 -0
- package/dist/cjs/models.d.ts.map +1 -0
- package/dist/cjs/models.js +246 -0
- package/dist/cjs/models.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/pagination.d.ts +21 -0
- package/dist/cjs/pagination.d.ts.map +1 -0
- package/dist/cjs/pagination.js +50 -0
- package/dist/cjs/pagination.js.map +1 -0
- package/dist/cjs/verify.d.ts +50 -0
- package/dist/cjs/verify.d.ts.map +1 -0
- package/dist/cjs/verify.js +500 -0
- package/dist/cjs/verify.js.map +1 -0
- package/dist/cjs/webhooks.d.ts +36 -0
- package/dist/cjs/webhooks.d.ts.map +1 -0
- package/dist/cjs/webhooks.js +71 -0
- package/dist/cjs/webhooks.js.map +1 -0
- package/dist/esm/cli.d.ts +16 -0
- package/dist/esm/cli.d.ts.map +1 -0
- package/dist/esm/cli.js +164 -0
- package/dist/esm/cli.js.map +1 -0
- package/dist/esm/client.d.ts +116 -0
- package/dist/esm/client.d.ts.map +1 -0
- package/dist/esm/client.js +316 -0
- package/dist/esm/client.js.map +1 -0
- package/dist/esm/crypto-browser.d.ts +7 -0
- package/dist/esm/crypto-browser.d.ts.map +1 -0
- package/dist/esm/crypto-browser.js +34 -0
- package/dist/esm/crypto-browser.js.map +1 -0
- package/dist/esm/crypto-node.d.ts +4 -0
- package/dist/esm/crypto-node.d.ts.map +1 -0
- package/dist/esm/crypto-node.js +27 -0
- package/dist/esm/crypto-node.js.map +1 -0
- package/dist/esm/crypto.d.ts +6 -0
- package/dist/esm/crypto.d.ts.map +1 -0
- package/dist/esm/crypto.js +3 -0
- package/dist/esm/crypto.js.map +1 -0
- package/dist/esm/errors.d.ts +76 -0
- package/dist/esm/errors.d.ts.map +1 -0
- package/dist/esm/errors.js +105 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/http.d.ts +35 -0
- package/dist/esm/http.d.ts.map +1 -0
- package/dist/esm/http.js +156 -0
- package/dist/esm/http.js.map +1 -0
- package/dist/esm/index.browser.d.ts +31 -0
- package/dist/esm/index.browser.d.ts.map +1 -0
- package/dist/esm/index.browser.js +31 -0
- package/dist/esm/index.browser.js.map +1 -0
- package/dist/esm/index.d.ts +26 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +26 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/keys.d.ts +12 -0
- package/dist/esm/keys.d.ts.map +1 -0
- package/dist/esm/keys.js +14 -0
- package/dist/esm/keys.js.map +1 -0
- package/dist/esm/models.d.ts +197 -0
- package/dist/esm/models.d.ts.map +1 -0
- package/dist/esm/models.js +226 -0
- package/dist/esm/models.js.map +1 -0
- package/dist/esm/pagination.d.ts +21 -0
- package/dist/esm/pagination.d.ts.map +1 -0
- package/dist/esm/pagination.js +46 -0
- package/dist/esm/pagination.js.map +1 -0
- package/dist/esm/verify.d.ts +50 -0
- package/dist/esm/verify.d.ts.map +1 -0
- package/dist/esm/verify.js +491 -0
- package/dist/esm/verify.js.map +1 -0
- package/dist/esm/webhooks.d.ts +36 -0
- package/dist/esm/webhooks.d.ts.map +1 -0
- package/dist/esm/webhooks.js +67 -0
- package/dist/esm/webhooks.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
/** Offline certificate and transparency verification.
|
|
2
|
+
*
|
|
3
|
+
* Ports the Go signing payload builders (core/payload.go) and Merkle tree
|
|
4
|
+
* verification (core/merkle.go) to TypeScript. All crypto is delegated to
|
|
5
|
+
* a CryptoOps provider (Node or browser).
|
|
6
|
+
*
|
|
7
|
+
* Critical encoding details:
|
|
8
|
+
* - Go's json.Marshal encodes []byte as base64, [N]byte as number arrays.
|
|
9
|
+
* - The payload builders normalize both formats to lowercase hex strings.
|
|
10
|
+
* - Timestamps in payloads use second-precision UTC: "YYYY-MM-DDTHH:MM:SSZ".
|
|
11
|
+
* - RFC 8785 canonical JSON: sorted keys recursively, no whitespace.
|
|
12
|
+
* - Transparency leaf = SHA-256(0x00 || issuanceBytes(cert)), NOT the
|
|
13
|
+
* certificate signing payload. issuanceBytes reconstructs json.Marshal
|
|
14
|
+
* output at issuance time (transparency_status=PENDING, no transparency).
|
|
15
|
+
*/
|
|
16
|
+
import { VerificationError } from "./errors.js";
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Pure byte helpers (no Buffer, no node:crypto)
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
const HEX_CHARS = "0123456789abcdef";
|
|
21
|
+
// Signing-format v2 domain-separation tags (CR-H01). Must match the
|
|
22
|
+
// source-of-truth const block in Go's core/payload.go byte-for-byte.
|
|
23
|
+
const PAYLOAD_TYPE_ATTESTATION = "burnledger.attestation.v2";
|
|
24
|
+
const PAYLOAD_TYPE_VERIFICATION = "burnledger.verification.v2";
|
|
25
|
+
const PAYLOAD_TYPE_CERTIFICATE = "burnledger.certificate.v2";
|
|
26
|
+
const PAYLOAD_TYPE_TREE_HEAD = "burnledger.sth.v2";
|
|
27
|
+
export function hexToBytes(hex) {
|
|
28
|
+
const len = hex.length >>> 1;
|
|
29
|
+
const out = new Uint8Array(len);
|
|
30
|
+
for (let i = 0; i < len; i++) {
|
|
31
|
+
const hi = hex.charCodeAt(i * 2);
|
|
32
|
+
const lo = hex.charCodeAt(i * 2 + 1);
|
|
33
|
+
out[i] = (unhex(hi) << 4) | unhex(lo);
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
function unhex(c) {
|
|
38
|
+
// 0-9
|
|
39
|
+
if (c >= 48 && c <= 57)
|
|
40
|
+
return c - 48;
|
|
41
|
+
// a-f
|
|
42
|
+
if (c >= 97 && c <= 102)
|
|
43
|
+
return c - 87;
|
|
44
|
+
// A-F
|
|
45
|
+
if (c >= 65 && c <= 70)
|
|
46
|
+
return c - 55;
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
49
|
+
export function bytesToHex(bytes) {
|
|
50
|
+
let out = "";
|
|
51
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
52
|
+
const b = bytes[i];
|
|
53
|
+
out += HEX_CHARS[b >>> 4];
|
|
54
|
+
out += HEX_CHARS[b & 0x0f];
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
function concatBytes(a, b) {
|
|
59
|
+
const out = new Uint8Array(a.length + b.length);
|
|
60
|
+
out.set(a, 0);
|
|
61
|
+
out.set(b, a.length);
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
function bytesEqual(a, b) {
|
|
65
|
+
if (a.length !== b.length)
|
|
66
|
+
return false;
|
|
67
|
+
for (let i = 0; i < a.length; i++) {
|
|
68
|
+
if (a[i] !== b[i])
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
function base64ToBytes(b64) {
|
|
74
|
+
// Use atob for universal browser+node compatibility.
|
|
75
|
+
// Node 18+ has atob globally; browsers always have it.
|
|
76
|
+
const bin = atob(b64);
|
|
77
|
+
const out = new Uint8Array(bin.length);
|
|
78
|
+
for (let i = 0; i < bin.length; i++) {
|
|
79
|
+
out[i] = bin.charCodeAt(i);
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
function textToBytes(s) {
|
|
84
|
+
return new TextEncoder().encode(s);
|
|
85
|
+
}
|
|
86
|
+
export async function publicKeyFromHex(crypto, hexKey, opts) {
|
|
87
|
+
const raw = hexToBytes(hexKey);
|
|
88
|
+
if (raw.length !== 32) {
|
|
89
|
+
throw new VerificationError(`public key must be 32 bytes, got ${raw.length}`);
|
|
90
|
+
}
|
|
91
|
+
const hash = await crypto.sha256(raw);
|
|
92
|
+
const keyId = "dp_k_" + bytesToHex(hash);
|
|
93
|
+
return { keyBytes: raw, keyId, revoked: opts?.revoked ?? false };
|
|
94
|
+
}
|
|
95
|
+
/** Verify all signatures on a deletion certificate offline. */
|
|
96
|
+
export async function verifyCertificate(crypto, certificate, publicKeys) {
|
|
97
|
+
const issuer = certificate.issuer;
|
|
98
|
+
const keyId = issuer.key_id;
|
|
99
|
+
const pki = publicKeys.get(keyId);
|
|
100
|
+
if (pki === undefined)
|
|
101
|
+
throw new VerificationError(`unknown issuer key: ${keyId}`);
|
|
102
|
+
if (pki.revoked)
|
|
103
|
+
throw new VerificationError(`issuer key is revoked: ${keyId}`);
|
|
104
|
+
// 1. Attestation signature
|
|
105
|
+
const att = certificate.attestation;
|
|
106
|
+
const attPayload = buildAttestationPayload(certificate.subject, att);
|
|
107
|
+
const attSig = decodeSignature(att.attestation_signature);
|
|
108
|
+
if (!(await crypto.ed25519Verify(pki.keyBytes, attPayload, attSig))) {
|
|
109
|
+
throw new VerificationError("attestation signature is invalid");
|
|
110
|
+
}
|
|
111
|
+
// 2. Verification signature
|
|
112
|
+
const ver = certificate.verification;
|
|
113
|
+
const verPayload = buildVerificationPayload(certificate.attestation_id, ver);
|
|
114
|
+
const verSig = decodeSignature(ver.verification_signature);
|
|
115
|
+
if (!(await crypto.ed25519Verify(pki.keyBytes, verPayload, verSig))) {
|
|
116
|
+
throw new VerificationError("verification signature is invalid");
|
|
117
|
+
}
|
|
118
|
+
// 3. Certificate signature
|
|
119
|
+
const certPayload = buildCertificatePayload(certificate);
|
|
120
|
+
const certSig = decodeSignature(certificate.certificate_signature);
|
|
121
|
+
if (!(await crypto.ed25519Verify(pki.keyBytes, certPayload, certSig))) {
|
|
122
|
+
throw new VerificationError("certificate signature is invalid");
|
|
123
|
+
}
|
|
124
|
+
// 4. Now that every signature is verified (so these fields are trustworthy),
|
|
125
|
+
// reject certificates that are validly signed but are NOT proof of deletion.
|
|
126
|
+
// Mirrors core/verify.go steps 3 and 6 — without these the SDK would accept
|
|
127
|
+
// a revoked certificate, or one attesting data that is still present, as VALID.
|
|
128
|
+
if (certificate.status === "REVOKED") {
|
|
129
|
+
throw new VerificationError("certificate has been revoked");
|
|
130
|
+
}
|
|
131
|
+
for (const s of ver.systems ?? []) {
|
|
132
|
+
const recordCount = s.record_count;
|
|
133
|
+
if (recordCount !== 0) {
|
|
134
|
+
throw new VerificationError(`data still present: system "${s.system_name}" reports ${recordCount} record(s)`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return "VALID";
|
|
138
|
+
}
|
|
139
|
+
/** Verify the transparency proof embedded in a certificate. */
|
|
140
|
+
export async function verifyTransparency(crypto, certificate, publicKeys) {
|
|
141
|
+
const transparency = certificate.transparency;
|
|
142
|
+
if (transparency == null) {
|
|
143
|
+
return "NOT_AVAILABLE";
|
|
144
|
+
}
|
|
145
|
+
const sth = transparency.signed_tree_head;
|
|
146
|
+
const issuer = certificate.issuer;
|
|
147
|
+
const keyId = issuer.key_id;
|
|
148
|
+
const pki = publicKeys.get(keyId);
|
|
149
|
+
if (pki === undefined)
|
|
150
|
+
throw new VerificationError(`unknown issuer key: ${keyId}`);
|
|
151
|
+
if (pki.revoked)
|
|
152
|
+
throw new VerificationError(`issuer key is revoked: ${keyId}`);
|
|
153
|
+
// 1. Tree head signature
|
|
154
|
+
const headPayload = buildTreeHeadPayload(sth);
|
|
155
|
+
const headSig = decodeSignature(sth.signature);
|
|
156
|
+
if (!(await crypto.ed25519Verify(pki.keyBytes, headPayload, headSig))) {
|
|
157
|
+
throw new VerificationError("tree head signature is invalid");
|
|
158
|
+
}
|
|
159
|
+
// 2. Merkle inclusion proof — leaf is hash of issuance-time certificate JSON,
|
|
160
|
+
// NOT the canonical signing payload. Matches Go's core.IssuanceBytes(cert).
|
|
161
|
+
const issuanceData = issuanceBytes(certificate);
|
|
162
|
+
const leaf = await hashLeaf(crypto, issuanceData);
|
|
163
|
+
const proofHashes = (transparency.inclusion_proof ?? []).map((h) => decodeBytes(h));
|
|
164
|
+
const root = decodeBytes(sth.root_hash);
|
|
165
|
+
const index = transparency.entry_index;
|
|
166
|
+
const treeSize = sth.tree_size;
|
|
167
|
+
if (!(await verifyInclusion(crypto, leaf, index, treeSize, proofHashes, root))) {
|
|
168
|
+
throw new VerificationError("merkle inclusion proof is invalid");
|
|
169
|
+
}
|
|
170
|
+
return "INCLUDED";
|
|
171
|
+
}
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
// Issuance bytes — matches Go's core.IssuanceBytes(cert)
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
/** Reconstruct the certificate JSON as it existed at issuance time.
|
|
176
|
+
*
|
|
177
|
+
* At issuance: transparency_status = "PENDING", transparency = nil (omitted).
|
|
178
|
+
* The leaf preimage is the RFC 8785 canonical JSON of that form — exactly
|
|
179
|
+
* Go's `CanonicalJSON(json.Marshal(cert))` in core.IssuanceBytes. It MUST be
|
|
180
|
+
* canonicalized (sorted keys, recursively), not serialized in insertion order:
|
|
181
|
+
* Go's struct field order is not alphabetical, so insertion-order stringify
|
|
182
|
+
* produces a different leaf hash and every genuine certificate fails inclusion
|
|
183
|
+
* verification (#C-01).
|
|
184
|
+
*/
|
|
185
|
+
export function issuanceBytes(certificate) {
|
|
186
|
+
const clone = JSON.parse(JSON.stringify(certificate));
|
|
187
|
+
clone.transparency_status = "PENDING";
|
|
188
|
+
delete clone.transparency;
|
|
189
|
+
return canonicalJson(clone);
|
|
190
|
+
}
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
// RFC 8785 Canonical JSON
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
function canonicalJson(obj) {
|
|
195
|
+
return textToBytes(canonicalStringify(obj));
|
|
196
|
+
}
|
|
197
|
+
function canonicalStringify(val) {
|
|
198
|
+
if (val === null || val === undefined)
|
|
199
|
+
return "null";
|
|
200
|
+
if (typeof val === "boolean")
|
|
201
|
+
return val ? "true" : "false";
|
|
202
|
+
if (typeof val === "number")
|
|
203
|
+
return JSON.stringify(val);
|
|
204
|
+
if (typeof val === "string")
|
|
205
|
+
return JSON.stringify(val);
|
|
206
|
+
if (Array.isArray(val)) {
|
|
207
|
+
return "[" + val.map(canonicalStringify).join(",") + "]";
|
|
208
|
+
}
|
|
209
|
+
if (typeof val === "object") {
|
|
210
|
+
const obj = val;
|
|
211
|
+
const keys = Object.keys(obj).sort();
|
|
212
|
+
const pairs = keys.map((k) => JSON.stringify(k) + ":" + canonicalStringify(obj[k]));
|
|
213
|
+
return "{" + pairs.join(",") + "}";
|
|
214
|
+
}
|
|
215
|
+
return JSON.stringify(val);
|
|
216
|
+
}
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
// Byte encoding helpers
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
/** Convert a byte field to lowercase hex. Handles:
|
|
221
|
+
* - hex string (from hand-crafted test data)
|
|
222
|
+
* - base64 string (from Go's json.Marshal of []byte slices)
|
|
223
|
+
* - number[] (from Go's json.Marshal of [N]byte fixed arrays)
|
|
224
|
+
*/
|
|
225
|
+
function toHex(value) {
|
|
226
|
+
if (Array.isArray(value)) {
|
|
227
|
+
return bytesToHex(new Uint8Array(value));
|
|
228
|
+
}
|
|
229
|
+
const str = value;
|
|
230
|
+
if (/^[0-9a-fA-F]+$/.test(str) && str.length % 2 === 0) {
|
|
231
|
+
return str.toLowerCase();
|
|
232
|
+
}
|
|
233
|
+
return bytesToHex(base64ToBytes(str));
|
|
234
|
+
}
|
|
235
|
+
/** Decode a signature field to raw bytes. Same format handling as toHex. */
|
|
236
|
+
function decodeSignature(value) {
|
|
237
|
+
if (Array.isArray(value)) {
|
|
238
|
+
return new Uint8Array(value);
|
|
239
|
+
}
|
|
240
|
+
const str = value;
|
|
241
|
+
if (/^[0-9a-fA-F]+$/.test(str)) {
|
|
242
|
+
const raw = hexToBytes(str);
|
|
243
|
+
if (raw.length === 64)
|
|
244
|
+
return raw;
|
|
245
|
+
}
|
|
246
|
+
return base64ToBytes(str);
|
|
247
|
+
}
|
|
248
|
+
/** Decode a hash/bytes field to raw bytes. Same format handling as toHex. */
|
|
249
|
+
function decodeBytes(value) {
|
|
250
|
+
if (Array.isArray(value)) {
|
|
251
|
+
return new Uint8Array(value);
|
|
252
|
+
}
|
|
253
|
+
const str = value;
|
|
254
|
+
if (/^[0-9a-fA-F]+$/.test(str) && str.length % 2 === 0) {
|
|
255
|
+
return hexToBytes(str);
|
|
256
|
+
}
|
|
257
|
+
return base64ToBytes(str);
|
|
258
|
+
}
|
|
259
|
+
function formatTimestamp(ts) {
|
|
260
|
+
// Strip fractional seconds, normalize to YYYY-MM-DDTHH:MM:SSZ
|
|
261
|
+
let s = ts;
|
|
262
|
+
const dotIdx = s.indexOf(".");
|
|
263
|
+
if (dotIdx !== -1) {
|
|
264
|
+
// Find the end of fractional seconds (Z, +, or end)
|
|
265
|
+
let endIdx = s.length;
|
|
266
|
+
for (let i = dotIdx + 1; i < s.length; i++) {
|
|
267
|
+
if (s[i] === "Z" || s[i] === "+" || s[i] === "-") {
|
|
268
|
+
endIdx = i;
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
s = s.slice(0, dotIdx) + s.slice(endIdx);
|
|
273
|
+
}
|
|
274
|
+
if (s.endsWith("Z"))
|
|
275
|
+
return s;
|
|
276
|
+
// Strip timezone offset
|
|
277
|
+
const plusIdx = s.indexOf("+");
|
|
278
|
+
if (plusIdx !== -1) {
|
|
279
|
+
s = s.slice(0, plusIdx);
|
|
280
|
+
}
|
|
281
|
+
return s + "Z";
|
|
282
|
+
}
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
// Payload builders — exact ports of core/payload.go
|
|
285
|
+
// ---------------------------------------------------------------------------
|
|
286
|
+
function buildAttestationPayload(subject, att) {
|
|
287
|
+
const attestedAt = formatTimestamp(att.attested_at);
|
|
288
|
+
const systems = att.systems.map((s) => ({
|
|
289
|
+
canonical_version: s.canonical_version ?? null,
|
|
290
|
+
connector_type: s.connector_type,
|
|
291
|
+
hash_scope: s.hash_scope,
|
|
292
|
+
merkle_root: s.merkle_root
|
|
293
|
+
? toHex(s.merkle_root)
|
|
294
|
+
: null,
|
|
295
|
+
observed_at: attestedAt,
|
|
296
|
+
record_count: s.record_count,
|
|
297
|
+
system_name: s.system_name,
|
|
298
|
+
}));
|
|
299
|
+
const payload = {
|
|
300
|
+
attested_at: attestedAt,
|
|
301
|
+
payload_type: PAYLOAD_TYPE_ATTESTATION,
|
|
302
|
+
proof_mode: att.proof_mode,
|
|
303
|
+
subject_hash: toHex(subject.identifier_hash),
|
|
304
|
+
systems,
|
|
305
|
+
};
|
|
306
|
+
return canonicalJson(payload);
|
|
307
|
+
}
|
|
308
|
+
function buildVerificationPayload(attestationId, ver) {
|
|
309
|
+
const verifiedAt = formatTimestamp(ver.verified_at);
|
|
310
|
+
const systems = ver.systems.map((s) => ({
|
|
311
|
+
observed_at: verifiedAt,
|
|
312
|
+
record_count: s.record_count,
|
|
313
|
+
system_name: s.system_name,
|
|
314
|
+
}));
|
|
315
|
+
const payload = {
|
|
316
|
+
attestation_id: attestationId,
|
|
317
|
+
payload_type: PAYLOAD_TYPE_VERIFICATION,
|
|
318
|
+
systems,
|
|
319
|
+
verified_at: verifiedAt,
|
|
320
|
+
};
|
|
321
|
+
return canonicalJson(payload);
|
|
322
|
+
}
|
|
323
|
+
function buildCertificatePayload(cert) {
|
|
324
|
+
const att = cert.attestation;
|
|
325
|
+
const ver = cert.verification;
|
|
326
|
+
const issuer = cert.issuer;
|
|
327
|
+
const subject = cert.subject;
|
|
328
|
+
// Attestation systems — uses "system_name" field name
|
|
329
|
+
const attSystems = att.systems.map((s) => ({
|
|
330
|
+
canonical_version: s.canonical_version ?? null,
|
|
331
|
+
connector_type: s.connector_type,
|
|
332
|
+
hash_scope: s.hash_scope,
|
|
333
|
+
merkle_root: s.merkle_root
|
|
334
|
+
? toHex(s.merkle_root)
|
|
335
|
+
: null,
|
|
336
|
+
record_count: s.record_count,
|
|
337
|
+
system_name: s.system_name,
|
|
338
|
+
}));
|
|
339
|
+
const attObj = {
|
|
340
|
+
attestation_signature: toHex(att.attestation_signature),
|
|
341
|
+
attested_at: formatTimestamp(att.attested_at),
|
|
342
|
+
proof_mode: att.proof_mode,
|
|
343
|
+
systems: attSystems,
|
|
344
|
+
};
|
|
345
|
+
// Verification systems
|
|
346
|
+
const verSystems = ver.systems.map((s) => ({
|
|
347
|
+
connector_type: s.connector_type,
|
|
348
|
+
record_count: s.record_count,
|
|
349
|
+
system_name: s.system_name,
|
|
350
|
+
}));
|
|
351
|
+
const verObj = {
|
|
352
|
+
systems: verSystems,
|
|
353
|
+
verification_signature: toHex(ver.verification_signature),
|
|
354
|
+
verified_at: formatTimestamp(ver.verified_at),
|
|
355
|
+
};
|
|
356
|
+
const issuerObj = {
|
|
357
|
+
key_id: issuer.key_id,
|
|
358
|
+
name: issuer.name,
|
|
359
|
+
public_key: toHex(issuer.public_key),
|
|
360
|
+
};
|
|
361
|
+
const subjectObj = {
|
|
362
|
+
identifier_hash: toHex(subject.identifier_hash),
|
|
363
|
+
identifier_type_hint: subject.identifier_type_hint,
|
|
364
|
+
};
|
|
365
|
+
// Revocation
|
|
366
|
+
let revocation = null;
|
|
367
|
+
const rev = cert.revocation;
|
|
368
|
+
if (rev != null) {
|
|
369
|
+
revocation = {
|
|
370
|
+
reason: rev.reason,
|
|
371
|
+
replacement_certificate_id: rev.replacement_certificate_id ?? null,
|
|
372
|
+
revoked_at: formatTimestamp(rev.revoked_at),
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
const payload = {
|
|
376
|
+
attestation: attObj,
|
|
377
|
+
attestation_id: cert.attestation_id,
|
|
378
|
+
certificate_format_version: cert.certificate_format_version,
|
|
379
|
+
certificate_id: cert.certificate_id,
|
|
380
|
+
issued_at: formatTimestamp(cert.issued_at),
|
|
381
|
+
issuer: issuerObj,
|
|
382
|
+
payload_type: PAYLOAD_TYPE_CERTIFICATE,
|
|
383
|
+
revocation,
|
|
384
|
+
status: cert.status,
|
|
385
|
+
subject: subjectObj,
|
|
386
|
+
verification: verObj,
|
|
387
|
+
};
|
|
388
|
+
return canonicalJson(payload);
|
|
389
|
+
}
|
|
390
|
+
function buildTreeHeadPayload(head) {
|
|
391
|
+
const payload = {
|
|
392
|
+
payload_type: PAYLOAD_TYPE_TREE_HEAD,
|
|
393
|
+
root_hash: toHex(head.root_hash),
|
|
394
|
+
timestamp: formatTimestamp(head.timestamp),
|
|
395
|
+
tree_size: head.tree_size,
|
|
396
|
+
};
|
|
397
|
+
return canonicalJson(payload);
|
|
398
|
+
}
|
|
399
|
+
// ---------------------------------------------------------------------------
|
|
400
|
+
// Merkle tree (RFC 6962) — ports of core/merkle.go
|
|
401
|
+
// ---------------------------------------------------------------------------
|
|
402
|
+
async function hashLeaf(crypto, data) {
|
|
403
|
+
return crypto.sha256(concatBytes(new Uint8Array([0x00]), data));
|
|
404
|
+
}
|
|
405
|
+
async function hashNode(crypto, left, right) {
|
|
406
|
+
const prefix = new Uint8Array([0x01]);
|
|
407
|
+
return crypto.sha256(concatBytes(prefix, concatBytes(left, right)));
|
|
408
|
+
}
|
|
409
|
+
function splitPoint(n) {
|
|
410
|
+
let k = 1;
|
|
411
|
+
while (k * 2 < n) {
|
|
412
|
+
k *= 2;
|
|
413
|
+
}
|
|
414
|
+
return k;
|
|
415
|
+
}
|
|
416
|
+
async function verifyInclusion(crypto, leaf, index, size, proof, root) {
|
|
417
|
+
if (size === 0 || index >= size)
|
|
418
|
+
return false;
|
|
419
|
+
if (size === 1)
|
|
420
|
+
return proof.length === 0 && bytesEqual(leaf, root);
|
|
421
|
+
const [computed, consumed] = await chainInclusion(crypto, leaf, index, size, proof);
|
|
422
|
+
return consumed === proof.length && bytesEqual(computed, root);
|
|
423
|
+
}
|
|
424
|
+
async function chainInclusion(crypto, leaf, index, n, proof) {
|
|
425
|
+
if (n === 1)
|
|
426
|
+
return [leaf, 0];
|
|
427
|
+
const k = splitPoint(n);
|
|
428
|
+
if (index < k) {
|
|
429
|
+
const [inner, used] = await chainInclusion(crypto, leaf, index, k, proof);
|
|
430
|
+
if (used >= proof.length)
|
|
431
|
+
return [inner, used + 1];
|
|
432
|
+
return [await hashNode(crypto, inner, proof[used]), used + 1];
|
|
433
|
+
}
|
|
434
|
+
const [inner, used] = await chainInclusion(crypto, leaf, index - k, n - k, proof);
|
|
435
|
+
if (used >= proof.length)
|
|
436
|
+
return [inner, used + 1];
|
|
437
|
+
return [await hashNode(crypto, proof[used], inner), used + 1];
|
|
438
|
+
}
|
|
439
|
+
function isPow2(n) {
|
|
440
|
+
return n > 0 && (n & (n - 1)) === 0;
|
|
441
|
+
}
|
|
442
|
+
/** Verify a Merkle consistency proof (RFC 6962 Section 2.1.4).
|
|
443
|
+
*
|
|
444
|
+
* Port of Go's VerifyConsistency / Python's _verify_consistency.
|
|
445
|
+
*/
|
|
446
|
+
export async function verifyConsistency(crypto, oldSize, newSize, oldRoot, newRoot, proof) {
|
|
447
|
+
if (oldSize === 0 || newSize === 0 || oldSize > newSize)
|
|
448
|
+
return false;
|
|
449
|
+
if (oldSize === newSize) {
|
|
450
|
+
return proof.length === 0 && bytesEqual(oldRoot, newRoot);
|
|
451
|
+
}
|
|
452
|
+
let pIdx = 0;
|
|
453
|
+
let fr;
|
|
454
|
+
let sr;
|
|
455
|
+
if (isPow2(oldSize)) {
|
|
456
|
+
fr = oldRoot;
|
|
457
|
+
sr = oldRoot;
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
if (pIdx >= proof.length)
|
|
461
|
+
return false;
|
|
462
|
+
fr = proof[pIdx];
|
|
463
|
+
sr = proof[pIdx];
|
|
464
|
+
pIdx++;
|
|
465
|
+
}
|
|
466
|
+
let fn = oldSize - 1;
|
|
467
|
+
let sn = newSize - 1;
|
|
468
|
+
while ((fn & 1) === 1) {
|
|
469
|
+
fn >>= 1;
|
|
470
|
+
sn >>= 1;
|
|
471
|
+
}
|
|
472
|
+
while (pIdx < proof.length) {
|
|
473
|
+
const c = proof[pIdx];
|
|
474
|
+
pIdx++;
|
|
475
|
+
if ((fn & 1) === 1 || fn === sn) {
|
|
476
|
+
fr = await hashNode(crypto, c, fr);
|
|
477
|
+
sr = await hashNode(crypto, c, sr);
|
|
478
|
+
while (fn !== 0 && (fn & 1) === 0) {
|
|
479
|
+
fn >>= 1;
|
|
480
|
+
sn >>= 1;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
sr = await hashNode(crypto, sr, c);
|
|
485
|
+
}
|
|
486
|
+
fn >>= 1;
|
|
487
|
+
sn >>= 1;
|
|
488
|
+
}
|
|
489
|
+
return sn === 0 && bytesEqual(fr, oldRoot) && bytesEqual(sr, newRoot);
|
|
490
|
+
}
|
|
491
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Webhook signature verification for incoming BurnLedger events.
|
|
2
|
+
*
|
|
3
|
+
* The server signs webhook payloads with HMAC-SHA256 using the webhook secret.
|
|
4
|
+
* The signature is sent in the X-BurnLedger-Signature header as a hex string,
|
|
5
|
+
* and the timestamp in the X-BurnLedger-Timestamp header as an RFC3339 string.
|
|
6
|
+
*
|
|
7
|
+
* CR-M01: the signed preimage is `timestamp + "." + body`, where `timestamp` is
|
|
8
|
+
* the exact RFC3339 string from the X-BurnLedger-Timestamp header. Binding the
|
|
9
|
+
* timestamp into the MAC prevents a captured delivery from being replayed under
|
|
10
|
+
* a different timestamp. Receivers MUST also verify the timestamp is fresh (see
|
|
11
|
+
* isTimestampFresh) — the signature proves authenticity, freshness prevents
|
|
12
|
+
* replay of an old-but-validly-signed delivery.
|
|
13
|
+
*
|
|
14
|
+
* Node-only — webhooks are received server-side.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Verify that a webhook payload was signed by the expected secret.
|
|
18
|
+
*
|
|
19
|
+
* @param secret The webhook secret (raw UTF-8, as returned by the API)
|
|
20
|
+
* @param timestamp The RFC3339 timestamp from the X-BurnLedger-Timestamp header
|
|
21
|
+
* @param body The raw request body bytes
|
|
22
|
+
* @param signature The hex-encoded HMAC-SHA256 signature from the X-BurnLedger-Signature header
|
|
23
|
+
* @returns true if the signature is valid
|
|
24
|
+
*/
|
|
25
|
+
export declare function verifyWebhookSignature(secret: string, timestamp: string, body: Uint8Array | string, signature: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Verify that the webhook timestamp is within an acceptable freshness window.
|
|
28
|
+
* Receivers MUST call this (in addition to verifyWebhookSignature) to prevent
|
|
29
|
+
* replay of an old-but-validly-signed delivery.
|
|
30
|
+
*
|
|
31
|
+
* @param timestamp The RFC3339 timestamp from the X-BurnLedger-Timestamp header
|
|
32
|
+
* @param toleranceSeconds Maximum accepted age/skew in seconds (default 300)
|
|
33
|
+
* @returns true if the timestamp parses and is within +/- tolerance of now
|
|
34
|
+
*/
|
|
35
|
+
export declare function isTimestampFresh(timestamp: string, toleranceSeconds?: number): boolean;
|
|
36
|
+
//# sourceMappingURL=webhooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/webhooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,UAAU,GAAG,MAAM,EACzB,SAAS,EAAE,MAAM,GAChB,OAAO,CAwBT;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,gBAAgB,GAAE,MAAY,GAC7B,OAAO,CAKT"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** Webhook signature verification for incoming BurnLedger events.
|
|
2
|
+
*
|
|
3
|
+
* The server signs webhook payloads with HMAC-SHA256 using the webhook secret.
|
|
4
|
+
* The signature is sent in the X-BurnLedger-Signature header as a hex string,
|
|
5
|
+
* and the timestamp in the X-BurnLedger-Timestamp header as an RFC3339 string.
|
|
6
|
+
*
|
|
7
|
+
* CR-M01: the signed preimage is `timestamp + "." + body`, where `timestamp` is
|
|
8
|
+
* the exact RFC3339 string from the X-BurnLedger-Timestamp header. Binding the
|
|
9
|
+
* timestamp into the MAC prevents a captured delivery from being replayed under
|
|
10
|
+
* a different timestamp. Receivers MUST also verify the timestamp is fresh (see
|
|
11
|
+
* isTimestampFresh) — the signature proves authenticity, freshness prevents
|
|
12
|
+
* replay of an old-but-validly-signed delivery.
|
|
13
|
+
*
|
|
14
|
+
* Node-only — webhooks are received server-side.
|
|
15
|
+
*/
|
|
16
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
17
|
+
/**
|
|
18
|
+
* Verify that a webhook payload was signed by the expected secret.
|
|
19
|
+
*
|
|
20
|
+
* @param secret The webhook secret (raw UTF-8, as returned by the API)
|
|
21
|
+
* @param timestamp The RFC3339 timestamp from the X-BurnLedger-Timestamp header
|
|
22
|
+
* @param body The raw request body bytes
|
|
23
|
+
* @param signature The hex-encoded HMAC-SHA256 signature from the X-BurnLedger-Signature header
|
|
24
|
+
* @returns true if the signature is valid
|
|
25
|
+
*/
|
|
26
|
+
export function verifyWebhookSignature(secret, timestamp, body, signature) {
|
|
27
|
+
if (secret.length === 0)
|
|
28
|
+
return false;
|
|
29
|
+
if (timestamp.length === 0)
|
|
30
|
+
return false;
|
|
31
|
+
if (signature.length === 0)
|
|
32
|
+
return false;
|
|
33
|
+
// Preimage: timestamp + "." + body. An RFC3339 timestamp never contains a
|
|
34
|
+
// ".", so the field boundary is unambiguous.
|
|
35
|
+
const bodyBytes = typeof body === "string" ? Buffer.from(body, "utf-8") : Buffer.from(body);
|
|
36
|
+
const expected = createHmac("sha256", secret)
|
|
37
|
+
.update(Buffer.from(`${timestamp}.`, "utf-8"))
|
|
38
|
+
.update(bodyBytes)
|
|
39
|
+
.digest();
|
|
40
|
+
let received;
|
|
41
|
+
try {
|
|
42
|
+
received = Buffer.from(signature, "hex");
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (received.length !== expected.length)
|
|
48
|
+
return false;
|
|
49
|
+
return timingSafeEqual(expected, received);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Verify that the webhook timestamp is within an acceptable freshness window.
|
|
53
|
+
* Receivers MUST call this (in addition to verifyWebhookSignature) to prevent
|
|
54
|
+
* replay of an old-but-validly-signed delivery.
|
|
55
|
+
*
|
|
56
|
+
* @param timestamp The RFC3339 timestamp from the X-BurnLedger-Timestamp header
|
|
57
|
+
* @param toleranceSeconds Maximum accepted age/skew in seconds (default 300)
|
|
58
|
+
* @returns true if the timestamp parses and is within +/- tolerance of now
|
|
59
|
+
*/
|
|
60
|
+
export function isTimestampFresh(timestamp, toleranceSeconds = 300) {
|
|
61
|
+
const parsed = Date.parse(timestamp);
|
|
62
|
+
if (Number.isNaN(parsed))
|
|
63
|
+
return false;
|
|
64
|
+
const ageSeconds = Math.abs(Date.now() - parsed) / 1000;
|
|
65
|
+
return ageSeconds <= toleranceSeconds;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=webhooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/webhooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE1D;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAc,EACd,SAAiB,EACjB,IAAyB,EACzB,SAAiB;IAEjB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACtC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACzC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAEzC,0EAA0E;IAC1E,6CAA6C;IAC7C,MAAM,SAAS,GACb,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5E,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC;SAC1C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,SAAS,GAAG,EAAE,OAAO,CAAC,CAAC;SAC7C,MAAM,CAAC,SAAS,CAAC;SACjB,MAAM,EAAE,CAAC;IAEZ,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAEtD,OAAO,eAAe,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAC9B,SAAiB,EACjB,mBAA2B,GAAG;IAE9B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACrC,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC;IACxD,OAAO,UAAU,IAAI,gBAAgB,CAAC;AACxC,CAAC"}
|