@cardanowall/sdk-ts 0.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.
Files changed (67) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +207 -0
  3. package/dist/client/index.cjs +2695 -0
  4. package/dist/client/index.cjs.map +1 -0
  5. package/dist/client/index.d.cts +397 -0
  6. package/dist/client/index.d.ts +397 -0
  7. package/dist/client/index.js +2641 -0
  8. package/dist/client/index.js.map +1 -0
  9. package/dist/conformance/cli.cjs +4901 -0
  10. package/dist/conformance/cli.cjs.map +1 -0
  11. package/dist/conformance/cli.d.cts +18 -0
  12. package/dist/conformance/cli.d.ts +18 -0
  13. package/dist/conformance/cli.js +4878 -0
  14. package/dist/conformance/cli.js.map +1 -0
  15. package/dist/fetch/index.cjs +335 -0
  16. package/dist/fetch/index.cjs.map +1 -0
  17. package/dist/fetch/index.d.cts +13 -0
  18. package/dist/fetch/index.d.ts +13 -0
  19. package/dist/fetch/index.js +323 -0
  20. package/dist/fetch/index.js.map +1 -0
  21. package/dist/fetch-outbound-BT5-NiYN.d.cts +76 -0
  22. package/dist/fetch-outbound-BT5-NiYN.d.ts +76 -0
  23. package/dist/hash/index.cjs +25 -0
  24. package/dist/hash/index.cjs.map +1 -0
  25. package/dist/hash/index.d.cts +1 -0
  26. package/dist/hash/index.d.ts +1 -0
  27. package/dist/hash/index.js +21 -0
  28. package/dist/hash/index.js.map +1 -0
  29. package/dist/identity/index.cjs +1388 -0
  30. package/dist/identity/index.cjs.map +1 -0
  31. package/dist/identity/index.d.cts +27 -0
  32. package/dist/identity/index.d.ts +27 -0
  33. package/dist/identity/index.js +1362 -0
  34. package/dist/identity/index.js.map +1 -0
  35. package/dist/ids/index.cjs +146 -0
  36. package/dist/ids/index.cjs.map +1 -0
  37. package/dist/ids/index.d.cts +55 -0
  38. package/dist/ids/index.d.ts +55 -0
  39. package/dist/ids/index.js +135 -0
  40. package/dist/ids/index.js.map +1 -0
  41. package/dist/index-BhnlWJAY.d.cts +10 -0
  42. package/dist/index-BhnlWJAY.d.ts +10 -0
  43. package/dist/index-Cg1QqVmA.d.cts +19 -0
  44. package/dist/index-Cg1QqVmA.d.ts +19 -0
  45. package/dist/index.cjs +7127 -0
  46. package/dist/index.cjs.map +1 -0
  47. package/dist/index.d.cts +21 -0
  48. package/dist/index.d.ts +21 -0
  49. package/dist/index.js +7004 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/merkle/index.cjs +396 -0
  52. package/dist/merkle/index.cjs.map +1 -0
  53. package/dist/merkle/index.d.cts +2 -0
  54. package/dist/merkle/index.d.ts +2 -0
  55. package/dist/merkle/index.js +387 -0
  56. package/dist/merkle/index.js.map +1 -0
  57. package/dist/types-B8Q3gW54.d.ts +123 -0
  58. package/dist/types-BQMtbRCb.d.cts +321 -0
  59. package/dist/types-BQMtbRCb.d.ts +321 -0
  60. package/dist/types-CLXdbjqr.d.cts +123 -0
  61. package/dist/verifier/index.cjs +4901 -0
  62. package/dist/verifier/index.cjs.map +1 -0
  63. package/dist/verifier/index.d.cts +176 -0
  64. package/dist/verifier/index.d.ts +176 -0
  65. package/dist/verifier/index.js +4848 -0
  66. package/dist/verifier/index.js.map +1 -0
  67. package/package.json +108 -0
@@ -0,0 +1,4848 @@
1
+ import { z } from 'zod';
2
+ import { decode, cdeDecodeOptions, encode } from 'cbor2';
3
+ import { sortCoreDeterministic } from 'cbor2/sorts';
4
+ import { blake2b } from '@noble/hashes/blake2.js';
5
+ import * as ed from '@noble/ed25519';
6
+ import { sha512, sha256 } from '@noble/hashes/sha2.js';
7
+ import { hkdf } from '@noble/hashes/hkdf.js';
8
+ import { argon2id } from 'hash-wasm';
9
+ import { xchacha20poly1305, chacha20poly1305 } from '@noble/ciphers/chacha.js';
10
+ import '@noble/ciphers/utils.js';
11
+ import { hmac } from '@noble/hashes/hmac.js';
12
+ import '@noble/curves/abstract/edwards.js';
13
+ import '@noble/curves/abstract/montgomery.js';
14
+ import '@noble/curves/abstract/weierstrass.js';
15
+ import { x25519 } from '@noble/curves/ed25519.js';
16
+ import '@noble/curves/nist.js';
17
+ import { concatBytes as concatBytes$1, asciiToBytes, bytesToNumberLE, bytesToNumberBE } from '@noble/curves/utils.js';
18
+ import { sha3_256, shake256, sha3_512, shake128 } from '@noble/hashes/sha3.js';
19
+ import { anumber, abytes, randomBytes as randomBytes$1, u32, swap32IfBE } from '@noble/hashes/utils.js';
20
+ import { FFTCore, reverseBits } from '@noble/curves/abstract/fft.js';
21
+
22
+ // src/verifier/types.ts
23
+ var PROFILE_RANK = Object.freeze({
24
+ core: 0,
25
+ signed: 1,
26
+ sealed: 2,
27
+ "recipient-sealed": 3
28
+ });
29
+ var ChunkedBytesArraySchema = z.array(
30
+ z.instanceof(Uint8Array).refine((b) => b.length >= 1 && b.length <= 64, {
31
+ params: { code: "CHUNK_TOO_LARGE" }
32
+ })
33
+ ).min(1);
34
+ var UTF8_ENCODER = new TextEncoder();
35
+ var UriChunkArraySchema = z.array(
36
+ z.string().refine(
37
+ (s) => {
38
+ const n = UTF8_ENCODER.encode(s).length;
39
+ return n >= 1 && n <= 64;
40
+ },
41
+ { params: { code: "CHUNK_TOO_LARGE" } }
42
+ )
43
+ ).min(1);
44
+ var HashDigestSchema = z.instanceof(Uint8Array);
45
+ var HashesMapSchema = z.record(z.string(), HashDigestSchema);
46
+ var MerkleCommitSchema = z.object({
47
+ alg: z.string(),
48
+ root: z.instanceof(Uint8Array),
49
+ leaf_count: z.number().int().min(1),
50
+ uris: z.array(UriChunkArraySchema).min(1).optional()
51
+ }).strict();
52
+ var SlotSchema = z.object({
53
+ epk: z.instanceof(Uint8Array).optional(),
54
+ kem_ct: ChunkedBytesArraySchema.optional(),
55
+ wrap: z.instanceof(Uint8Array).optional()
56
+ });
57
+ z.object({
58
+ m: z.number().int(),
59
+ t: z.number().int(),
60
+ p: z.number().int()
61
+ }).strict();
62
+ var PassphraseBlockSchema = z.object({
63
+ alg: z.string(),
64
+ salt: z.instanceof(Uint8Array).superRefine((bytes, ctx) => {
65
+ if (bytes.length < 16) {
66
+ ctx.addIssue({
67
+ code: "custom",
68
+ path: [],
69
+ message: `passphrase.salt length ${bytes.length} < 16`,
70
+ params: { code: "ENC_PASSPHRASE_SALT_TOO_SHORT" }
71
+ });
72
+ } else if (bytes.length > 64) {
73
+ ctx.addIssue({
74
+ code: "custom",
75
+ path: [],
76
+ message: `passphrase.salt length ${bytes.length} > 64`,
77
+ params: { code: "ENC_PASSPHRASE_SALT_TOO_LONG" }
78
+ });
79
+ }
80
+ }),
81
+ params: z.record(z.string(), z.unknown())
82
+ }).strict();
83
+ var EncryptionEnvelopeSchema = z.object({
84
+ scheme: z.unknown(),
85
+ aead: z.string(),
86
+ kem: z.string().optional(),
87
+ nonce: z.instanceof(Uint8Array),
88
+ slots: z.array(SlotSchema).optional(),
89
+ slots_mac: z.instanceof(Uint8Array).refine((b) => b.length === 32, {
90
+ params: { code: "ENC_SLOTS_MAC_INVALID_LENGTH" }
91
+ }).optional(),
92
+ passphrase: PassphraseBlockSchema.optional()
93
+ }).strict();
94
+ var ItemEntrySchema = z.object({
95
+ hashes: HashesMapSchema,
96
+ uris: z.array(UriChunkArraySchema).min(1).optional(),
97
+ // Captured as `unknown` so the validator can run the
98
+ // `ENC_REQUIRES_CONTENT_HASH` pre-check ahead of any inner-shape errors
99
+ // and surface the most informative code first.
100
+ enc: z.unknown().optional()
101
+ }).strict();
102
+ var SigEntrySchema = z.object({
103
+ cose_key: ChunkedBytesArraySchema.optional(),
104
+ cose_sign1: ChunkedBytesArraySchema
105
+ }).strict();
106
+ var SupersedesSchema = z.instanceof(Uint8Array).refine((b) => b.length === 32, {
107
+ params: { code: "SUPERSEDES_TX_INVALID_LENGTH" }
108
+ });
109
+ var VersionLiteralSchema = z.literal(1);
110
+ var PoeRecordSchema = z.looseObject({
111
+ v: VersionLiteralSchema,
112
+ items: z.array(ItemEntrySchema).optional(),
113
+ merkle: z.array(MerkleCommitSchema).optional(),
114
+ supersedes: SupersedesSchema.optional(),
115
+ sigs: z.array(SigEntrySchema).optional(),
116
+ crit: z.array(z.string()).optional()
117
+ });
118
+ var TOP_LEVEL_BASE_KEYS = /* @__PURE__ */ new Set([
119
+ "v",
120
+ "items",
121
+ "merkle",
122
+ "supersedes",
123
+ "sigs",
124
+ "crit"
125
+ ]);
126
+ var EXTENSION_KEY_VENDOR_RE = /^x-.+\n?$/;
127
+ var EXTENSION_KEY_COMPANION_RE = /^[a-z]+-.+\n?$/;
128
+ function isExtensionKey(k) {
129
+ return EXTENSION_KEY_VENDOR_RE.test(k) || EXTENSION_KEY_COMPANION_RE.test(k);
130
+ }
131
+ var CanonicalCborError = class extends Error {
132
+ code;
133
+ constructor(code, message, options) {
134
+ super(message, options);
135
+ this.name = "CanonicalCborError";
136
+ this.code = code;
137
+ }
138
+ };
139
+ function encodeCanonicalCbor(value) {
140
+ return encode(value, {
141
+ cde: true,
142
+ collapseBigInts: true,
143
+ rejectDuplicateKeys: true,
144
+ sortKeys: sortCoreDeterministic
145
+ });
146
+ }
147
+ function decodeCanonicalCbor(bytes) {
148
+ try {
149
+ return decode(bytes, {
150
+ ...cdeDecodeOptions,
151
+ rejectStreaming: true,
152
+ rejectDuplicateKeys: true,
153
+ // A CIP-309 record carries integers, byte/text strings, arrays, maps and
154
+ // `null` — and nothing else. Without these rejections the major-type-7
155
+ // surface leaks into the decoder: a float16/32/64 that happens to hold an
156
+ // integral value (e.g. 1.0) silently decodes to the integer 1 and passes
157
+ // a `z.literal(1)` / Number.isInteger schema check, so two byte strings
158
+ // that are NOT byte-identical canonicalise to the same record. That
159
+ // breaks the cross-implementation parity invariant (the Python twin
160
+ // already rejects non-integer `v` / `enc.scheme` outright). Reject the
161
+ // whole non-record surface — floats, negative zero, undefined, and
162
+ // non-{true,false,null} simple values — so any such input surfaces as
163
+ // MALFORMED_CBOR via mapDecodeError rather than decoding to a look-alike.
164
+ rejectFloats: true,
165
+ rejectNegativeZero: true,
166
+ rejectUndefined: true,
167
+ rejectSimple: true
168
+ });
169
+ } catch (cause) {
170
+ throw mapDecodeError(cause);
171
+ }
172
+ }
173
+ function mapDecodeError(cause) {
174
+ const message = cause instanceof Error ? cause.message : String(cause);
175
+ const lower = message.toLowerCase();
176
+ const isIndefinite = lower.includes("streaming") || lower.includes("indefinite");
177
+ const detail = isIndefinite ? `indefinite-length items are not permitted in canonical CBOR: ${message}` : message;
178
+ return new CanonicalCborError("MALFORMED_CBOR", `cbor decode failed: ${detail}`, { cause });
179
+ }
180
+ function decodeCbor(bytes) {
181
+ return decode(bytes);
182
+ }
183
+
184
+ // ../poe-standard/src/encoder.ts
185
+ function encodeRecordBodyForSigning(record) {
186
+ const body = recordToCborInternal(
187
+ record);
188
+ return encodeCanonicalCbor(body);
189
+ }
190
+ function recordToCborInternal(record, includeSigs) {
191
+ const out = { v: record.v };
192
+ if (record.items !== void 0) out["items"] = record.items.map(itemToCbor);
193
+ if (record.merkle !== void 0) out["merkle"] = record.merkle.map(merkleToCbor);
194
+ if (record.supersedes !== void 0) out["supersedes"] = record.supersedes;
195
+ if (record.crit !== void 0) out["crit"] = record.crit.slice();
196
+ for (const [k, v] of Object.entries(record)) {
197
+ if (k === "v" || k === "items" || k === "merkle" || k === "supersedes" || k === "sigs" || k === "crit") {
198
+ continue;
199
+ }
200
+ out[k] = v;
201
+ }
202
+ return out;
203
+ }
204
+ function itemToCbor(item) {
205
+ const out = { hashes: hashesToCbor(item.hashes) };
206
+ if (item.uris !== void 0) {
207
+ out["uris"] = item.uris.map((chunks) => chunks.slice());
208
+ }
209
+ if (item.enc !== void 0) {
210
+ out["enc"] = envelopeToCbor(item.enc);
211
+ }
212
+ return out;
213
+ }
214
+ function hashesToCbor(hashes3) {
215
+ const out = {};
216
+ for (const [alg, digest] of Object.entries(hashes3)) {
217
+ out[alg] = digest;
218
+ }
219
+ return out;
220
+ }
221
+ function merkleToCbor(commit) {
222
+ const out = {
223
+ alg: commit.alg,
224
+ root: commit.root,
225
+ leaf_count: commit.leaf_count
226
+ };
227
+ if (commit.uris !== void 0) {
228
+ out["uris"] = commit.uris.map((chunks) => chunks.slice());
229
+ }
230
+ return out;
231
+ }
232
+ function envelopeToCbor(enc) {
233
+ const out = {
234
+ scheme: enc.scheme,
235
+ aead: enc.aead,
236
+ nonce: enc.nonce
237
+ };
238
+ if (enc.kem !== void 0) out["kem"] = enc.kem;
239
+ if (enc.slots !== void 0) out["slots"] = enc.slots.map(slotToCbor);
240
+ if (enc.slots_mac !== void 0) out["slots_mac"] = enc.slots_mac;
241
+ if (enc.passphrase !== void 0) out["passphrase"] = passphraseToCbor(enc.passphrase);
242
+ return out;
243
+ }
244
+ function slotToCbor(slot) {
245
+ if (slot.kem_ct !== void 0) {
246
+ return { kem_ct: slot.kem_ct.map((c) => c), wrap: slot.wrap };
247
+ }
248
+ return { epk: slot.epk, wrap: slot.wrap };
249
+ }
250
+ function passphraseToCbor(pp) {
251
+ return {
252
+ alg: pp.alg,
253
+ salt: pp.salt,
254
+ params: pp.params
255
+ };
256
+ }
257
+ var CanonicalCborError2 = class extends Error {
258
+ code;
259
+ constructor(code, message, options) {
260
+ super(message, options);
261
+ this.name = "CanonicalCborError";
262
+ this.code = code;
263
+ }
264
+ };
265
+ function encodeCanonicalCbor2(value) {
266
+ return encode(value, {
267
+ cde: true,
268
+ collapseBigInts: true,
269
+ rejectDuplicateKeys: true,
270
+ sortKeys: sortCoreDeterministic
271
+ });
272
+ }
273
+ function decodeCanonicalCbor2(bytes) {
274
+ try {
275
+ return decode(bytes, {
276
+ ...cdeDecodeOptions,
277
+ rejectStreaming: true,
278
+ rejectDuplicateKeys: true,
279
+ // A CIP-309 record carries integers, byte/text strings, arrays, maps and
280
+ // `null` — and nothing else. Without these rejections the major-type-7
281
+ // surface leaks into the decoder: a float16/32/64 that happens to hold an
282
+ // integral value (e.g. 1.0) silently decodes to the integer 1 and passes
283
+ // a `z.literal(1)` / Number.isInteger schema check, so two byte strings
284
+ // that are NOT byte-identical canonicalise to the same record. That
285
+ // breaks the cross-implementation parity invariant (the Python twin
286
+ // already rejects non-integer `v` / `enc.scheme` outright). Reject the
287
+ // whole non-record surface — floats, negative zero, undefined, and
288
+ // non-{true,false,null} simple values — so any such input surfaces as
289
+ // MALFORMED_CBOR via mapDecodeError rather than decoding to a look-alike.
290
+ rejectFloats: true,
291
+ rejectNegativeZero: true,
292
+ rejectUndefined: true,
293
+ rejectSimple: true
294
+ });
295
+ } catch (cause) {
296
+ throw mapDecodeError2(cause);
297
+ }
298
+ }
299
+ function mapDecodeError2(cause) {
300
+ const message = cause instanceof Error ? cause.message : String(cause);
301
+ const lower = message.toLowerCase();
302
+ const isIndefinite = lower.includes("streaming") || lower.includes("indefinite");
303
+ const detail = isIndefinite ? `indefinite-length items are not permitted in canonical CBOR: ${message}` : message;
304
+ return new CanonicalCborError2("MALFORMED_CBOR", `cbor decode failed: ${detail}`, { cause });
305
+ }
306
+ function blake2b224(input) {
307
+ return blake2b(input, { dkLen: 28 });
308
+ }
309
+ ed.hashes.sha512 = sha512;
310
+ var L = ed.Point.CURVE().n;
311
+ function leBytesToBigInt(bytes) {
312
+ let value = 0n;
313
+ for (let i = bytes.length - 1; i >= 0; i--) {
314
+ value = value << 8n | BigInt(bytes[i]);
315
+ }
316
+ return value;
317
+ }
318
+ function verifyEd25519(opts2) {
319
+ const { signature, message, publicKey } = opts2;
320
+ if (signature.length !== 64 || publicKey.length !== 32) return false;
321
+ const S = leBytesToBigInt(signature.subarray(32, 64));
322
+ if (S >= L) return false;
323
+ let A;
324
+ let R;
325
+ try {
326
+ A = ed.Point.fromBytes(publicKey);
327
+ R = ed.Point.fromBytes(signature.subarray(0, 32));
328
+ } catch {
329
+ return false;
330
+ }
331
+ if (A.isSmallOrder() || R.isSmallOrder()) return false;
332
+ const k = leBytesToBigInt(ed.hash(concatBytes(signature.subarray(0, 32), publicKey, message))) % L;
333
+ const sB = S === 0n ? ed.Point.ZERO : ed.Point.BASE.multiplyUnsafe(S);
334
+ const kA = k === 0n ? ed.Point.ZERO : A.multiplyUnsafe(k);
335
+ return sB.subtract(kA).subtract(R).is0();
336
+ }
337
+ function concatBytes(...parts) {
338
+ let total = 0;
339
+ for (const p of parts) total += p.length;
340
+ const out = new Uint8Array(total);
341
+ let offset = 0;
342
+ for (const p of parts) {
343
+ out.set(p, offset);
344
+ offset += p.length;
345
+ }
346
+ return out;
347
+ }
348
+ function compareCt(a, b) {
349
+ if (a.length !== b.length) return false;
350
+ let diff = 0;
351
+ for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
352
+ return diff === 0;
353
+ }
354
+ var CoseVerifyError = class extends Error {
355
+ code;
356
+ constructor(code, message, options) {
357
+ super(message, options);
358
+ this.name = "CoseVerifyError";
359
+ this.code = code;
360
+ }
361
+ };
362
+ var CARDANO_POE_SIG_DOMAIN_PREFIX = "cardano-poe-record-sig-v1";
363
+ var CARDANO_POE_SIG_DOMAIN_PREFIX_BYTES = new TextEncoder().encode(
364
+ CARDANO_POE_SIG_DOMAIN_PREFIX
365
+ );
366
+ if (CARDANO_POE_SIG_DOMAIN_PREFIX_BYTES.length !== 25) {
367
+ throw new Error(
368
+ `cardano-poe-record-sig-v1 prefix must encode to exactly 25 UTF-8 bytes, got ${CARDANO_POE_SIG_DOMAIN_PREFIX_BYTES.length}`
369
+ );
370
+ }
371
+ var EMPTY_BYTES = new Uint8Array(0);
372
+ function buildSigStructure(args) {
373
+ return encodeCanonicalCbor2([
374
+ args.context,
375
+ args.bodyProtectedBytes,
376
+ args.externalAad,
377
+ args.payload
378
+ ]);
379
+ }
380
+ function buildCip309SigStructure(args) {
381
+ const toSign = new Uint8Array(
382
+ CARDANO_POE_SIG_DOMAIN_PREFIX_BYTES.length + args.recordBodyCbor.length
383
+ );
384
+ toSign.set(CARDANO_POE_SIG_DOMAIN_PREFIX_BYTES, 0);
385
+ toSign.set(args.recordBodyCbor, CARDANO_POE_SIG_DOMAIN_PREFIX_BYTES.length);
386
+ return buildSigStructure({
387
+ context: "Signature1",
388
+ bodyProtectedBytes: args.bodyProtectedBytes,
389
+ externalAad: EMPTY_BYTES,
390
+ payload: toSign
391
+ });
392
+ }
393
+ function asCoseHeader(value) {
394
+ if (value instanceof Map) return value;
395
+ if (value !== null && typeof value === "object" && value.constructor === Object) {
396
+ return new Map(Object.entries(value));
397
+ }
398
+ return null;
399
+ }
400
+ function decodeCoseSign1(bytes) {
401
+ let arr;
402
+ try {
403
+ arr = decodeCanonicalCbor2(bytes);
404
+ } catch (cause) {
405
+ throw new CoseVerifyError("MALFORMED_SIG_COSE", "cose decode failed", { cause });
406
+ }
407
+ if (!Array.isArray(arr) || arr.length !== 4) {
408
+ throw new CoseVerifyError("MALFORMED_SIG_COSE", "expected 4-element array");
409
+ }
410
+ const [protectedBytesRaw, unprotectedRaw, payloadRaw, signatureRaw] = arr;
411
+ if (!(protectedBytesRaw instanceof Uint8Array)) {
412
+ throw new CoseVerifyError("MALFORMED_SIG_COSE", "protected_bytes must be bytes");
413
+ }
414
+ const unprotectedHeader = asCoseHeader(unprotectedRaw);
415
+ if (unprotectedHeader === null) {
416
+ throw new CoseVerifyError("MALFORMED_SIG_COSE", "unprotected header must be map");
417
+ }
418
+ if (payloadRaw !== null && !(payloadRaw instanceof Uint8Array)) {
419
+ throw new CoseVerifyError("MALFORMED_SIG_COSE", "payload must be bytes or null");
420
+ }
421
+ if (!(signatureRaw instanceof Uint8Array) || signatureRaw.length !== 64) {
422
+ throw new CoseVerifyError("MALFORMED_SIG_COSE", "signature must be 64 bytes");
423
+ }
424
+ let protectedHeader;
425
+ if (protectedBytesRaw.length === 0) {
426
+ protectedHeader = /* @__PURE__ */ new Map();
427
+ } else {
428
+ let decodedProtected;
429
+ try {
430
+ decodedProtected = decodeCanonicalCbor2(protectedBytesRaw);
431
+ } catch (cause) {
432
+ throw new CoseVerifyError("MALFORMED_SIG_COSE", "protected header decode failed", { cause });
433
+ }
434
+ const ph = asCoseHeader(decodedProtected);
435
+ if (ph === null) {
436
+ throw new CoseVerifyError("MALFORMED_SIG_COSE", "protected header must decode to map");
437
+ }
438
+ if (ph.size === 0) {
439
+ throw new CoseVerifyError(
440
+ "MALFORMED_SIG_COSE",
441
+ "empty protected header must encode as 0x40 (zero-length bstr), not as an empty map"
442
+ );
443
+ }
444
+ protectedHeader = ph;
445
+ }
446
+ return {
447
+ protectedHeader,
448
+ protectedBytes: protectedBytesRaw,
449
+ unprotectedHeader,
450
+ payload: payloadRaw,
451
+ signature: signatureRaw
452
+ };
453
+ }
454
+ function coseSign1Cip309Verify(args) {
455
+ let decoded;
456
+ try {
457
+ decoded = decodeCoseSign1(args.message);
458
+ } catch (e) {
459
+ if (e instanceof CoseVerifyError) {
460
+ return { ok: false, error: { code: e.code, message: "errors.cose.malformed" } };
461
+ }
462
+ if (e instanceof CanonicalCborError2) {
463
+ return {
464
+ ok: false,
465
+ error: { code: "MALFORMED_SIG_COSE", message: "errors.cose.malformed_cbor" }
466
+ };
467
+ }
468
+ throw e;
469
+ }
470
+ if (decoded.payload !== null) {
471
+ return {
472
+ ok: false,
473
+ error: {
474
+ code: "MALFORMED_SIG_COSE_SIGN1",
475
+ message: "errors.cose.attached_payload_forbidden"
476
+ }
477
+ };
478
+ }
479
+ const alg = decoded.protectedHeader.get(1);
480
+ if (typeof alg !== "number" || alg !== -8) {
481
+ return {
482
+ ok: false,
483
+ error: { code: "UNSUPPORTED_SIG_ALG", message: "errors.cose.unsupported_alg" }
484
+ };
485
+ }
486
+ const kidRaw = decoded.protectedHeader.get(4);
487
+ let signerKey;
488
+ if (kidRaw instanceof Uint8Array && kidRaw.length === 32) {
489
+ signerKey = kidRaw;
490
+ } else if (args.expectedSignerKey instanceof Uint8Array && args.expectedSignerKey.length === 32) {
491
+ signerKey = args.expectedSignerKey;
492
+ }
493
+ if (signerKey === void 0) {
494
+ return {
495
+ ok: false,
496
+ error: { code: "KID_UNRESOLVED", message: "errors.cose.kid_unresolved" }
497
+ };
498
+ }
499
+ if (kidRaw instanceof Uint8Array && kidRaw.length === 32 && args.expectedSignerKey instanceof Uint8Array && args.expectedSignerKey.length === 32 && !compareCt(kidRaw, args.expectedSignerKey)) {
500
+ return {
501
+ ok: false,
502
+ error: { code: "KID_UNRESOLVED", message: "errors.cose.kid_mismatch" }
503
+ };
504
+ }
505
+ const hashedFlag = decoded.unprotectedHeader.get("hashed");
506
+ let sigStructureBytes;
507
+ if (hashedFlag === true) {
508
+ const toSign = new Uint8Array(
509
+ CARDANO_POE_SIG_DOMAIN_PREFIX_BYTES.length + args.detachedRecordBodyCbor.length
510
+ );
511
+ toSign.set(CARDANO_POE_SIG_DOMAIN_PREFIX_BYTES, 0);
512
+ toSign.set(args.detachedRecordBodyCbor, CARDANO_POE_SIG_DOMAIN_PREFIX_BYTES.length);
513
+ const hashedPayload = blake2b224(toSign);
514
+ sigStructureBytes = buildSigStructure({
515
+ context: "Signature1",
516
+ bodyProtectedBytes: decoded.protectedBytes,
517
+ externalAad: EMPTY_BYTES,
518
+ payload: hashedPayload
519
+ });
520
+ } else {
521
+ sigStructureBytes = buildCip309SigStructure({
522
+ bodyProtectedBytes: decoded.protectedBytes,
523
+ recordBodyCbor: args.detachedRecordBodyCbor
524
+ });
525
+ }
526
+ const valid = verifyEd25519({
527
+ publicKey: signerKey,
528
+ message: sigStructureBytes,
529
+ signature: decoded.signature
530
+ });
531
+ if (!valid) {
532
+ return {
533
+ ok: false,
534
+ error: { code: "SIGNATURE_INVALID", message: "errors.cose.signature_invalid" }
535
+ };
536
+ }
537
+ return { ok: true, signerKey, alg };
538
+ }
539
+ var COSE_KEY_LABEL_KTY = 1;
540
+ var COSE_KEY_LABEL_ALG = 3;
541
+ var COSE_KEY_LABEL_CRV = -1;
542
+ var COSE_KEY_LABEL_X = -2;
543
+ var KTY_OKP = 1;
544
+ var ALG_EDDSA = -8;
545
+ var CRV_ED25519 = 6;
546
+ var ED25519_PUBLIC_KEY_LENGTH = 32;
547
+ function asMap(value) {
548
+ if (value instanceof Map) return value;
549
+ if (value !== null && typeof value === "object" && value.constructor === Object) {
550
+ return new Map(Object.entries(value));
551
+ }
552
+ return null;
553
+ }
554
+ function parseCoseKeyEd25519(blob) {
555
+ let decoded;
556
+ try {
557
+ decoded = decodeCanonicalCbor2(blob);
558
+ } catch {
559
+ return null;
560
+ }
561
+ const map = asMap(decoded);
562
+ if (map === null) return null;
563
+ const kty = map.get(COSE_KEY_LABEL_KTY);
564
+ if (typeof kty !== "number" || kty !== KTY_OKP) return null;
565
+ const crv = map.get(COSE_KEY_LABEL_CRV);
566
+ if (typeof crv !== "number" || crv !== CRV_ED25519) return null;
567
+ if (map.has(COSE_KEY_LABEL_ALG)) {
568
+ const alg = map.get(COSE_KEY_LABEL_ALG);
569
+ if (typeof alg !== "number" || alg !== ALG_EDDSA) return null;
570
+ }
571
+ const x = map.get(COSE_KEY_LABEL_X);
572
+ if (!(x instanceof Uint8Array) || x.length !== ED25519_PUBLIC_KEY_LENGTH) return null;
573
+ return x;
574
+ }
575
+
576
+ // ../poe-standard/src/chunked.ts
577
+ var UTF8_ENCODER2 = new TextEncoder();
578
+ function bytesChunkArrayConcat(chunks) {
579
+ let total = 0;
580
+ for (const c of chunks) total += c.length;
581
+ const out = new Uint8Array(total);
582
+ let offset = 0;
583
+ for (const c of chunks) {
584
+ out.set(c, offset);
585
+ offset += c.length;
586
+ }
587
+ return out;
588
+ }
589
+ function reconstructChunkedUri(chunks) {
590
+ const merged = bytesChunkArrayConcat(chunks.map((c) => UTF8_ENCODER2.encode(c)));
591
+ try {
592
+ const uri = new TextDecoder("utf-8", { fatal: true }).decode(merged);
593
+ return { ok: true, uri };
594
+ } catch (cause) {
595
+ return {
596
+ ok: false,
597
+ code: "INVALID_URI",
598
+ reason: cause instanceof Error ? cause.message : String(cause)
599
+ };
600
+ }
601
+ }
602
+ var SEVERITY = Object.freeze({
603
+ // --- Part A ---
604
+ MALFORMED_CBOR: "error",
605
+ SCHEMA_TYPE_MISMATCH: "error",
606
+ SCHEMA_MISSING_REQUIRED: "error",
607
+ SCHEMA_UNKNOWN_FIELD: "error",
608
+ SCHEMA_INVALID_LITERAL: "error",
609
+ SCHEMA_EMPTY_RECORD: "error",
610
+ HASH_DIGEST_LENGTH_MISMATCH: "error",
611
+ UNSUPPORTED_HASH_ALG: "error",
612
+ UNSUPPORTED_MERKLE_COMMIT_ALG: "error",
613
+ INVALID_URI: "error",
614
+ CHUNK_TOO_LARGE: "error",
615
+ UNAUTHENTICATED_CIPHER_FORBIDDEN: "error",
616
+ UNSUPPORTED_AEAD_ALG: "error",
617
+ NONCE_LENGTH_MISMATCH: "error",
618
+ UNSUPPORTED_ENVELOPE_SCHEME: "error",
619
+ ENC_SLOTS_EMPTY: "error",
620
+ ENC_SLOT_INVALID_SHAPE: "error",
621
+ UNSUPPORTED_KEM_ALG: "error",
622
+ ENC_KEM_REQUIRED: "error",
623
+ KEM_EPK_LENGTH_MISMATCH: "error",
624
+ KEM_CT_LENGTH_MISMATCH: "error",
625
+ WRAP_LENGTH_MISMATCH: "error",
626
+ ENC_SLOTS_MAC_INVALID_LENGTH: "error",
627
+ ENC_SLOTS_MAC_REQUIRED: "error",
628
+ ENC_SLOTS_REQUIRED: "error",
629
+ ENC_EXCLUSIVITY_VIOLATION: "error",
630
+ ENC_NO_KEY_PATH: "error",
631
+ ENC_REQUIRES_CONTENT_HASH: "error",
632
+ ENC_PASSPHRASE_ALG_UNSUPPORTED: "error",
633
+ ENC_PASSPHRASE_SALT_TOO_SHORT: "error",
634
+ ENC_PASSPHRASE_SALT_TOO_LONG: "error",
635
+ ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW: "error",
636
+ ENC_PASSPHRASE_PARAMS_EXCEED_POLICY: "error",
637
+ MALFORMED_SIG_COSE_SIGN1: "error",
638
+ SIGNATURE_UNSUPPORTED: "info",
639
+ SIG_ENTRY_INVALID_SHAPE: "error",
640
+ SIG_ENTRY_KID_COSE_KEY_CONFLICT: "error",
641
+ SIG_PRIVATE_KEY_LEAKED: "error",
642
+ SUPERSEDES_TX_INVALID_LENGTH: "error",
643
+ EXTENSION_UNSUPPORTED_CRITICAL: "error",
644
+ CRIT_SHAPE_INVALID: "error",
645
+ // --- Part B ---
646
+ METADATA_NOT_FOUND: "error",
647
+ INSUFFICIENT_CONFIRMATIONS: "info",
648
+ SIGNATURE_INVALID: "error",
649
+ SIGNER_KEY_UNRESOLVED: "error",
650
+ WALLET_ADDRESS_MISMATCH: "error",
651
+ URI_TARGET_FORBIDDEN: "error",
652
+ URI_INTEGRITY_MISMATCH: "error",
653
+ URI_FETCH_FAILED: "warning",
654
+ CONTENT_UNAVAILABLE: "error",
655
+ CIPHERTEXT_UNAVAILABLE: "error",
656
+ PROVIDER_UNAVAILABLE: "error",
657
+ SERVICE_INDEPENDENCE_VIOLATION: "error",
658
+ WRONG_DECRYPTION_INPUT_SHAPE: "error",
659
+ WRONG_RECIPIENT_KEY: "error",
660
+ TAMPERED_HEADER: "error",
661
+ TAMPERED_CIPHERTEXT: "error",
662
+ KDF_DERIVATION_FAILED: "error",
663
+ SCHEMA_MERKLE_LEAF_COUNT_MISMATCH: "error",
664
+ SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED: "error",
665
+ SCHEMA_MERKLE_LEAVES_MALFORMED: "error",
666
+ MERKLE_ROOT_MISMATCH: "error",
667
+ MERKLE_LEAVES_UNAVAILABLE: "warning",
668
+ MERKLE_LEAVES_INFORMATIVE_FORM: "info",
669
+ // Dual-severity — default reading is `info`; the verifier promotes to
670
+ // `error` for merkle-only records (no `items[]` content claim was
671
+ // validated in the same record).
672
+ MERKLE_UNSUPPORTED: "info",
673
+ // Dual-severity — default reading is `info` (render mode); strict
674
+ // end-to-end verifiers promote to `error`.
675
+ OUT_OF_PROFILE_SKIPPED: "info"
676
+ });
677
+
678
+ // ../poe-standard/src/validator.ts
679
+ var HASH_ALG_LENGTHS = {
680
+ "sha2-256": 32,
681
+ "blake2b-256": 32
682
+ };
683
+ var MERKLE_COMMIT_ALG_LENGTHS = {
684
+ "rfc9162-sha256": 32
685
+ };
686
+ var AEAD_NONCE_LENGTHS = {
687
+ "xchacha20-poly1305": 24
688
+ };
689
+ var UNAUTHENTICATED_CIPHER_RE = /(?:^|[-_])(?:cbc|ctr|ecb|cfb|ofb)(?:[-_]|\n?$)|^(?:rc4|des|3des)(?:[-_]|\n?$)/i;
690
+ var KEM_SLOT_DESCRIPTORS = {
691
+ x25519: { field: "epk", fieldLength: 32, wrapLength: 48 },
692
+ mlkem768x25519: { field: "kem_ct", fieldLength: 1120, wrapLength: 48 }
693
+ };
694
+ var KEM_FIELD_LENGTH_CODE = {
695
+ epk: "KEM_EPK_LENGTH_MISMATCH",
696
+ kem_ct: "KEM_CT_LENGTH_MISMATCH"
697
+ };
698
+ var PASSPHRASE_KDF_ALGS = /* @__PURE__ */ new Set(["argon2id"]);
699
+ var KNOWN_SIG_ALG_IDS = /* @__PURE__ */ new Set([-8, -19]);
700
+ function validatePoeRecord(bytes) {
701
+ let decoded;
702
+ try {
703
+ decoded = decodeCanonicalCbor(bytes);
704
+ } catch (cause) {
705
+ return {
706
+ ok: false,
707
+ issues: [
708
+ {
709
+ code: "MALFORMED_CBOR",
710
+ path: [],
711
+ message: cause instanceof Error ? cause.message : String(cause),
712
+ severity: "error"
713
+ }
714
+ ]
715
+ };
716
+ }
717
+ const parse = PoeRecordSchema.safeParse(decoded);
718
+ if (!parse.success) {
719
+ const issues = parse.error.issues.map((issue2) => mapZodIssue(issue2, decoded)).sort(compareIssuePath);
720
+ return { ok: false, issues };
721
+ }
722
+ const record = parse.data;
723
+ const errors = [];
724
+ const warnings = [];
725
+ const info = [];
726
+ const itemsLen = Array.isArray(record.items) ? record.items.length : 0;
727
+ const merkleLen = Array.isArray(record.merkle) ? record.merkle.length : 0;
728
+ if (itemsLen === 0 && merkleLen === 0) {
729
+ errors.push(
730
+ issue(
731
+ "SCHEMA_EMPTY_RECORD",
732
+ [],
733
+ "record must carry at least one of items[] or merkle[] non-empty"
734
+ )
735
+ );
736
+ }
737
+ const decodedTopKeys = topLevelKeysOf(decoded);
738
+ const critShapeInvalidIndices = checkCritShape(record, decodedTopKeys, errors);
739
+ for (const k of decodedTopKeys) {
740
+ if (TOP_LEVEL_BASE_KEYS.has(k)) continue;
741
+ if (isExtensionKey(k)) continue;
742
+ errors.push(issue("SCHEMA_UNKNOWN_FIELD", [k], `unknown top-level field: ${k}`));
743
+ }
744
+ if (Array.isArray(record.crit)) {
745
+ for (let i = 0; i < record.crit.length; i++) {
746
+ if (critShapeInvalidIndices.has(i)) continue;
747
+ const critName = record.crit[i];
748
+ errors.push(
749
+ issue(
750
+ "EXTENSION_UNSUPPORTED_CRITICAL",
751
+ ["crit", i],
752
+ `crit lists extension '${critName}' that this validator does not implement`
753
+ )
754
+ );
755
+ }
756
+ }
757
+ for (let i = 0; i < (record.items ?? []).length; i++) {
758
+ const item = record.items[i];
759
+ checkItemHashes(item, i, errors);
760
+ if (item.uris) checkItemUris(item.uris, ["items", i, "uris"], errors);
761
+ if (item.enc !== void 0) checkItemEnc(item, i, errors);
762
+ }
763
+ for (let i = 0; i < (record.merkle ?? []).length; i++) {
764
+ const commit = record.merkle[i];
765
+ checkMerkleCommit(commit, i, errors);
766
+ }
767
+ if (record.sigs) {
768
+ for (let i = 0; i < record.sigs.length; i++) {
769
+ checkSigEntry(record.sigs[i], i, errors, info);
770
+ }
771
+ }
772
+ if (errors.length > 0) {
773
+ return { ok: false, issues: errors.sort(compareIssuePath) };
774
+ }
775
+ const result = {
776
+ ok: true,
777
+ record
778
+ };
779
+ if (warnings.length > 0) result.warnings = warnings.sort(compareIssuePath);
780
+ if (info.length > 0) result.info = info.sort(compareIssuePath);
781
+ return result;
782
+ }
783
+ function mapZodIssue(zissue, decoded) {
784
+ const path = zissue.path;
785
+ const explicit = zissue.params?.code;
786
+ if (explicit !== void 0) {
787
+ return issue(explicit, path, zissue.message);
788
+ }
789
+ const inSigsEntry = path.length >= 2 && path[0] === "sigs" && typeof path[1] === "number";
790
+ const isInSlotEntry = (() => {
791
+ if (path.length >= 5 && path[0] === "items" && typeof path[1] === "number" && path[2] === "enc" && path[3] === "slots" && typeof path[4] === "number") {
792
+ return true;
793
+ }
794
+ if (path.length >= 2 && path[0] === "slots" && typeof path[1] === "number") {
795
+ return true;
796
+ }
797
+ return false;
798
+ })();
799
+ const valueAtIssue = valueAtPath(decoded, path);
800
+ const isMissing = valueAtIssue === void 0;
801
+ switch (zissue.code) {
802
+ case "invalid_type":
803
+ if (isInSlotEntry) return issue("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
804
+ if (isMissing) {
805
+ if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
806
+ return issue("SCHEMA_MISSING_REQUIRED", path, zissue.message);
807
+ }
808
+ if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
809
+ return issue("SCHEMA_TYPE_MISMATCH", path, zissue.message);
810
+ case "invalid_value":
811
+ if (path.length === 1 && path[0] === "v") {
812
+ return issue(
813
+ isMissing ? "SCHEMA_MISSING_REQUIRED" : "SCHEMA_INVALID_LITERAL",
814
+ path,
815
+ zissue.message
816
+ );
817
+ }
818
+ return issue("SCHEMA_INVALID_LITERAL", path, zissue.message);
819
+ case "unrecognized_keys":
820
+ if (isInSlotEntry) return issue("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
821
+ if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
822
+ return issue("SCHEMA_UNKNOWN_FIELD", path, zissue.message);
823
+ case "invalid_format":
824
+ case "too_big":
825
+ case "too_small":
826
+ if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
827
+ return issue("SCHEMA_TYPE_MISMATCH", path, zissue.message);
828
+ case "invalid_union":
829
+ case "invalid_key":
830
+ case "invalid_element":
831
+ case "custom":
832
+ default:
833
+ if (isInSlotEntry) return issue("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
834
+ if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
835
+ return issue("SCHEMA_TYPE_MISMATCH", path, zissue.message);
836
+ }
837
+ }
838
+ function checkItemHashes(item, idx, errors) {
839
+ const entries = Object.entries(item.hashes);
840
+ if (entries.length === 0) {
841
+ errors.push(
842
+ issue(
843
+ "SCHEMA_TYPE_MISMATCH",
844
+ ["items", idx, "hashes"],
845
+ "hashes must be a non-empty CBOR map of <alg-id> -> <digest>"
846
+ )
847
+ );
848
+ return;
849
+ }
850
+ for (const [alg, digest] of entries) {
851
+ if (!(alg in HASH_ALG_LENGTHS)) {
852
+ errors.push(
853
+ issue("UNSUPPORTED_HASH_ALG", ["items", idx, "hashes", alg], `unknown hash alg: ${alg}`)
854
+ );
855
+ continue;
856
+ }
857
+ const expected = HASH_ALG_LENGTHS[alg];
858
+ if (digest.length !== expected) {
859
+ errors.push(
860
+ issue(
861
+ "HASH_DIGEST_LENGTH_MISMATCH",
862
+ ["items", idx, "hashes", alg],
863
+ `hashes['${alg}'] digest length ${digest.length} != ${expected}`
864
+ )
865
+ );
866
+ }
867
+ }
868
+ }
869
+ function checkItemUris(uris, basePath, errors) {
870
+ uris.forEach((chunks, ui) => validateOneUri(chunks, [...basePath, ui], errors));
871
+ }
872
+ function validateOneUri(chunks, path, errors) {
873
+ const reconstructed = reconstructChunkedUri(chunks);
874
+ if (!reconstructed.ok) {
875
+ errors.push(issue(reconstructed.code, path, reconstructed.reason));
876
+ return;
877
+ }
878
+ const uri = reconstructed.uri;
879
+ if (uri.includes("#")) {
880
+ errors.push(
881
+ issue(
882
+ "INVALID_URI",
883
+ path,
884
+ "URI contains a fragment identifier ('#'), which is forbidden"
885
+ )
886
+ );
887
+ return;
888
+ }
889
+ const sepIdx = uri.indexOf("://");
890
+ if (sepIdx <= 0 || !/^[a-z][a-z0-9+.-]*$/i.test(uri.slice(0, sepIdx))) {
891
+ errors.push(
892
+ issue("INVALID_URI", path, "URI is not absolute (missing scheme://hierarchical-part)")
893
+ );
894
+ return;
895
+ }
896
+ const scheme = uri.slice(0, sepIdx).toLowerCase();
897
+ const rest = uri.slice(sepIdx + "://".length);
898
+ if (scheme === "ar") {
899
+ if (!/^ar:\/\/[A-Za-z0-9_-]{43}$/.test("ar://" + rest)) {
900
+ errors.push(
901
+ issue(
902
+ "INVALID_URI",
903
+ path,
904
+ "ar:// URI does not match `^ar://[A-Za-z0-9_-]{43}$` (43-char base64url txid, no path/query/fragment)"
905
+ )
906
+ );
907
+ }
908
+ return;
909
+ }
910
+ if (scheme === "ipfs") {
911
+ const slashIdx = rest.indexOf("/");
912
+ const cid = slashIdx === -1 ? rest : rest.slice(0, slashIdx);
913
+ if (!validateCidProfile(cid)) {
914
+ errors.push(
915
+ issue(
916
+ "INVALID_URI",
917
+ path,
918
+ "ipfs:// URI is not a valid CID under the CIP-309 profile"
919
+ )
920
+ );
921
+ }
922
+ return;
923
+ }
924
+ errors.push(
925
+ issue(
926
+ "INVALID_URI",
927
+ path,
928
+ "unsupported URI scheme; v1 PoE URI set is {ar://, ipfs://}"
929
+ )
930
+ );
931
+ }
932
+ function checkItemEnc(item, idx, errors) {
933
+ const hasContentHash = Object.keys(item.hashes).some((alg) => alg in HASH_ALG_LENGTHS);
934
+ if (!hasContentHash) {
935
+ errors.push(
936
+ issue(
937
+ "ENC_REQUIRES_CONTENT_HASH",
938
+ ["items", idx, "enc"],
939
+ "item carries `enc` but `hashes` has no content-hash entry (sha2-256 or blake2b-256)"
940
+ )
941
+ );
942
+ return;
943
+ }
944
+ const encParse = EncryptionEnvelopeSchema.safeParse(item.enc);
945
+ if (!encParse.success) {
946
+ for (const zissue of encParse.error.issues) {
947
+ const mapped = mapZodIssue(zissue, item.enc);
948
+ errors.push({
949
+ ...mapped,
950
+ path: ["items", idx, "enc", ...mapped.path]
951
+ });
952
+ }
953
+ return;
954
+ }
955
+ const enc = encParse.data;
956
+ const basePath = ["items", idx, "enc"];
957
+ if (typeof enc.scheme !== "number" || !Number.isInteger(enc.scheme) || enc.scheme !== 1) {
958
+ errors.push(
959
+ issue(
960
+ "UNSUPPORTED_ENVELOPE_SCHEME",
961
+ [...basePath, "scheme"],
962
+ `enc.scheme must be the unsigned integer 1; got ${String(enc.scheme)}`
963
+ )
964
+ );
965
+ }
966
+ if (UNAUTHENTICATED_CIPHER_RE.test(enc.aead)) {
967
+ errors.push(
968
+ issue(
969
+ "UNAUTHENTICATED_CIPHER_FORBIDDEN",
970
+ [...basePath, "aead"],
971
+ `'${enc.aead}' is an unauthenticated cipher; CIP-309 mandates an authenticated (AEAD) cipher`
972
+ )
973
+ );
974
+ return;
975
+ }
976
+ if (!(enc.aead in AEAD_NONCE_LENGTHS)) {
977
+ errors.push(
978
+ issue("UNSUPPORTED_AEAD_ALG", [...basePath, "aead"], `unknown aead alg: ${enc.aead}`)
979
+ );
980
+ return;
981
+ }
982
+ const expectedNonceLen = AEAD_NONCE_LENGTHS[enc.aead];
983
+ if (enc.nonce.length !== expectedNonceLen) {
984
+ errors.push(
985
+ issue(
986
+ "NONCE_LENGTH_MISMATCH",
987
+ [...basePath, "nonce"],
988
+ `nonce length ${enc.nonce.length} != ${expectedNonceLen} for ${enc.aead}`
989
+ )
990
+ );
991
+ }
992
+ if (enc.kem !== void 0 && !(enc.kem in KEM_SLOT_DESCRIPTORS)) {
993
+ errors.push(issue("UNSUPPORTED_KEM_ALG", [...basePath, "kem"], `unknown kem alg: ${enc.kem}`));
994
+ }
995
+ const hasSlots = enc.slots !== void 0;
996
+ const hasSlotsMac = enc.slots_mac !== void 0;
997
+ const hasPassphrase = enc.passphrase !== void 0;
998
+ if (hasSlots && hasPassphrase) {
999
+ errors.push(
1000
+ issue("ENC_EXCLUSIVITY_VIOLATION", basePath, "enc combines slots with passphrase; pick one")
1001
+ );
1002
+ }
1003
+ if (hasSlots && !hasSlotsMac) {
1004
+ errors.push(
1005
+ issue("ENC_SLOTS_MAC_REQUIRED", basePath, "enc.slots present but enc.slots_mac absent")
1006
+ );
1007
+ }
1008
+ if (hasSlotsMac && !hasSlots) {
1009
+ errors.push(
1010
+ issue("ENC_SLOTS_REQUIRED", basePath, "enc.slots_mac present but enc.slots absent")
1011
+ );
1012
+ }
1013
+ if (hasSlots && enc.kem === void 0) {
1014
+ errors.push(issue("ENC_KEM_REQUIRED", basePath, "enc.slots present but enc.kem absent"));
1015
+ }
1016
+ if (!hasSlots && !hasPassphrase) {
1017
+ errors.push(
1018
+ issue(
1019
+ "ENC_NO_KEY_PATH",
1020
+ basePath,
1021
+ "enc requires either slots or passphrase \u2014 no on-chain key path otherwise"
1022
+ )
1023
+ );
1024
+ }
1025
+ if (hasSlots) {
1026
+ if (enc.slots.length < 1) {
1027
+ errors.push(
1028
+ issue("ENC_SLOTS_EMPTY", [...basePath, "slots"], `slots length ${enc.slots.length} < 1`)
1029
+ );
1030
+ }
1031
+ const descriptor = enc.kem !== void 0 ? KEM_SLOT_DESCRIPTORS[enc.kem] : void 0;
1032
+ if (descriptor !== void 0) {
1033
+ const rawSlotKeys = rawSlotKeySets(item.enc);
1034
+ enc.slots.forEach((slot, si) => {
1035
+ checkSlotShape(
1036
+ slot,
1037
+ rawSlotKeys[si] ?? /* @__PURE__ */ new Set(),
1038
+ descriptor,
1039
+ enc.kem,
1040
+ [...basePath, "slots", si],
1041
+ errors
1042
+ );
1043
+ });
1044
+ }
1045
+ }
1046
+ if (hasPassphrase) {
1047
+ const pp = enc.passphrase;
1048
+ const ppPath = [...basePath, "passphrase"];
1049
+ if (!PASSPHRASE_KDF_ALGS.has(pp.alg)) {
1050
+ errors.push(
1051
+ issue(
1052
+ "ENC_PASSPHRASE_ALG_UNSUPPORTED",
1053
+ [...ppPath, "alg"],
1054
+ `unknown passphrase kdf alg: ${pp.alg}`
1055
+ )
1056
+ );
1057
+ return;
1058
+ }
1059
+ if (pp.alg === "argon2id") {
1060
+ const allowed = /* @__PURE__ */ new Set(["m", "t", "p"]);
1061
+ for (const k of Object.keys(pp.params)) {
1062
+ if (!allowed.has(k)) {
1063
+ errors.push(
1064
+ issue(
1065
+ "SCHEMA_UNKNOWN_FIELD",
1066
+ [...ppPath, "params", k],
1067
+ `unknown argon2id params field: ${k}`
1068
+ )
1069
+ );
1070
+ }
1071
+ }
1072
+ const p = pp.params;
1073
+ const argonInt = (val, name) => {
1074
+ if (typeof val !== "number" || !Number.isInteger(val)) {
1075
+ errors.push(
1076
+ issue(
1077
+ "SCHEMA_TYPE_MISMATCH",
1078
+ [...ppPath, "params", name],
1079
+ `argon2id params.${name} must be a CBOR unsigned integer`
1080
+ )
1081
+ );
1082
+ return null;
1083
+ }
1084
+ return val;
1085
+ };
1086
+ const mVal = argonInt(p.m, "m");
1087
+ const tVal = argonInt(p.t, "t");
1088
+ const pVal = argonInt(p.p, "p");
1089
+ if (mVal !== null && mVal < 65536) {
1090
+ errors.push(
1091
+ issue(
1092
+ "ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
1093
+ [...ppPath, "params", "m"],
1094
+ "argon2id requires m >= 65536 KiB"
1095
+ )
1096
+ );
1097
+ }
1098
+ if (tVal !== null && tVal < 3) {
1099
+ errors.push(
1100
+ issue(
1101
+ "ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
1102
+ [...ppPath, "params", "t"],
1103
+ "argon2id requires t >= 3"
1104
+ )
1105
+ );
1106
+ }
1107
+ if (pVal !== null && pVal < 1) {
1108
+ errors.push(
1109
+ issue(
1110
+ "ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
1111
+ [...ppPath, "params", "p"],
1112
+ "argon2id requires p >= 1"
1113
+ )
1114
+ );
1115
+ }
1116
+ }
1117
+ }
1118
+ }
1119
+ var SLOT_KEY_UNIVERSE = /* @__PURE__ */ new Set(["epk", "kem_ct", "wrap"]);
1120
+ function checkSlotShape(slot, rawKeys, descriptor, kem, slotPath, errors) {
1121
+ const foreignField = descriptor.field === "epk" ? "kem_ct" : "epk";
1122
+ if (rawKeys.has(foreignField)) {
1123
+ errors.push(
1124
+ issue(
1125
+ "ENC_SLOT_INVALID_SHAPE",
1126
+ [...slotPath, foreignField],
1127
+ `slot carries '${foreignField}' but kem='${kem}' expects '${descriptor.field}'`
1128
+ )
1129
+ );
1130
+ }
1131
+ for (const k of rawKeys) {
1132
+ if (!SLOT_KEY_UNIVERSE.has(k)) {
1133
+ errors.push(
1134
+ issue(
1135
+ "ENC_SLOT_INVALID_SHAPE",
1136
+ [...slotPath, k],
1137
+ `slot carries unexpected key '${k}'; a slot is a 2-key map {${descriptor.field}, wrap}`
1138
+ )
1139
+ );
1140
+ }
1141
+ }
1142
+ if (descriptor.field === "epk") {
1143
+ if (slot.epk === void 0) {
1144
+ errors.push(
1145
+ issue(
1146
+ "ENC_SLOT_INVALID_SHAPE",
1147
+ [...slotPath, "epk"],
1148
+ `slot for kem='${kem}' is missing required 'epk'`
1149
+ )
1150
+ );
1151
+ } else if (slot.epk.length !== descriptor.fieldLength) {
1152
+ errors.push(
1153
+ issue(
1154
+ KEM_FIELD_LENGTH_CODE.epk,
1155
+ [...slotPath, "epk"],
1156
+ `slot.epk length ${slot.epk.length} != ${descriptor.fieldLength} for ${kem}`
1157
+ )
1158
+ );
1159
+ }
1160
+ } else {
1161
+ if (slot.kem_ct === void 0) {
1162
+ errors.push(
1163
+ issue(
1164
+ "ENC_SLOT_INVALID_SHAPE",
1165
+ [...slotPath, "kem_ct"],
1166
+ `slot for kem='${kem}' is missing required 'kem_ct'`
1167
+ )
1168
+ );
1169
+ } else {
1170
+ const reassembled = bytesChunkArrayConcat(slot.kem_ct).length;
1171
+ if (reassembled !== descriptor.fieldLength) {
1172
+ errors.push(
1173
+ issue(
1174
+ KEM_FIELD_LENGTH_CODE.kem_ct,
1175
+ [...slotPath, "kem_ct"],
1176
+ `slot.kem_ct reassembles to ${reassembled} bytes != ${descriptor.fieldLength} for ${kem}`
1177
+ )
1178
+ );
1179
+ }
1180
+ }
1181
+ }
1182
+ if (slot.wrap === void 0) {
1183
+ errors.push(
1184
+ issue(
1185
+ "ENC_SLOT_INVALID_SHAPE",
1186
+ [...slotPath, "wrap"],
1187
+ `slot for kem='${kem}' is missing required 'wrap'`
1188
+ )
1189
+ );
1190
+ } else if (slot.wrap.length !== descriptor.wrapLength) {
1191
+ errors.push(
1192
+ issue(
1193
+ "WRAP_LENGTH_MISMATCH",
1194
+ [...slotPath, "wrap"],
1195
+ `slot.wrap length ${slot.wrap.length} != ${descriptor.wrapLength}`
1196
+ )
1197
+ );
1198
+ }
1199
+ }
1200
+ function rawSlotKeySets(rawEnc) {
1201
+ const slots = mapLikeGet(rawEnc, "slots");
1202
+ if (!Array.isArray(slots)) return [];
1203
+ return slots.map((slot) => {
1204
+ const keys = /* @__PURE__ */ new Set();
1205
+ if (slot instanceof Map) {
1206
+ for (const k of slot.keys()) if (typeof k === "string") keys.add(k);
1207
+ } else if (typeof slot === "object" && slot !== null) {
1208
+ for (const k of Object.keys(slot)) keys.add(k);
1209
+ }
1210
+ return keys;
1211
+ });
1212
+ }
1213
+ function mapLikeGet(value, key) {
1214
+ if (value instanceof Map) return value.get(key);
1215
+ if (typeof value === "object" && value !== null) {
1216
+ return value[key];
1217
+ }
1218
+ return void 0;
1219
+ }
1220
+ function checkMerkleCommit(commit, idx, errors) {
1221
+ const basePath = ["merkle", idx];
1222
+ if (!(commit.alg in MERKLE_COMMIT_ALG_LENGTHS)) {
1223
+ errors.push(
1224
+ issue(
1225
+ "UNSUPPORTED_MERKLE_COMMIT_ALG",
1226
+ [...basePath, "alg"],
1227
+ `unknown merkle commitment alg: ${commit.alg}`
1228
+ )
1229
+ );
1230
+ return;
1231
+ }
1232
+ const expected = MERKLE_COMMIT_ALG_LENGTHS[commit.alg];
1233
+ if (commit.root.length !== expected) {
1234
+ errors.push(
1235
+ issue(
1236
+ "HASH_DIGEST_LENGTH_MISMATCH",
1237
+ [...basePath, "root"],
1238
+ `merkle entry root length ${commit.root.length} != ${expected} for ${commit.alg}`
1239
+ )
1240
+ );
1241
+ }
1242
+ if (commit.uris) {
1243
+ checkItemUris(commit.uris, [...basePath, "uris"], errors);
1244
+ }
1245
+ }
1246
+ function checkSigEntry(entry, idx, errors, info) {
1247
+ if (entry.cose_key !== void 0) {
1248
+ const keyIssue = inspectCoseKey(entry.cose_key, idx);
1249
+ if (keyIssue !== null) {
1250
+ errors.push(keyIssue);
1251
+ return;
1252
+ }
1253
+ }
1254
+ const merged = bytesChunkArrayConcat(entry.cose_sign1);
1255
+ let cose;
1256
+ try {
1257
+ cose = decodeCoseSign1(merged);
1258
+ } catch (cause) {
1259
+ errors.push(
1260
+ issue(
1261
+ "MALFORMED_SIG_COSE_SIGN1",
1262
+ ["sigs", idx],
1263
+ cause instanceof CoseVerifyError || cause instanceof Error ? cause.message : String(cause)
1264
+ )
1265
+ );
1266
+ return;
1267
+ }
1268
+ if (cose.payload !== null) {
1269
+ errors.push(
1270
+ issue(
1271
+ "MALFORMED_SIG_COSE_SIGN1",
1272
+ ["sigs", idx],
1273
+ "COSE_Sign1 payload must be null (detached); attached form forbidden"
1274
+ )
1275
+ );
1276
+ return;
1277
+ }
1278
+ const alg = cose.protectedHeader.get(1);
1279
+ if (typeof alg !== "number" || !KNOWN_SIG_ALG_IDS.has(alg)) {
1280
+ info.push(
1281
+ issue(
1282
+ "SIGNATURE_UNSUPPORTED",
1283
+ ["sigs", idx],
1284
+ `COSE_Sign1 protected alg ${String(alg)} not in {-8, -19}`
1285
+ )
1286
+ );
1287
+ }
1288
+ const protectedKid = cose.protectedHeader.get(4);
1289
+ if (protectedKid instanceof Uint8Array && protectedKid.length === 32 && entry.cose_key !== void 0) {
1290
+ errors.push(
1291
+ issue(
1292
+ "SIG_ENTRY_KID_COSE_KEY_CONFLICT",
1293
+ ["sigs", idx],
1294
+ "sigs[i] carries both a 32-byte protected `kid` (path 1) and an inline `cose_key` (path 2); paths are mutually exclusive"
1295
+ )
1296
+ );
1297
+ }
1298
+ }
1299
+ function inspectCoseKey(keyChunks, i) {
1300
+ let decoded;
1301
+ try {
1302
+ decoded = decodeCanonicalCbor(bytesChunkArrayConcat(keyChunks));
1303
+ } catch (cause) {
1304
+ return issue(
1305
+ "MALFORMED_SIG_COSE_SIGN1",
1306
+ ["sigs", i, "cose_key"],
1307
+ `sigs[${i}].cose_key failed to decode as cbor<COSE_Key>: ${cause instanceof Error ? cause.message : String(cause)}`
1308
+ );
1309
+ }
1310
+ const getLabel = (label) => {
1311
+ if (decoded instanceof Map) return decoded.get(label);
1312
+ if (typeof decoded === "object" && decoded !== null) {
1313
+ return decoded[String(label)];
1314
+ }
1315
+ return void 0;
1316
+ };
1317
+ const hasLabel = (label) => {
1318
+ if (decoded instanceof Map) return decoded.has(label);
1319
+ if (typeof decoded === "object" && decoded !== null) {
1320
+ return Object.prototype.hasOwnProperty.call(decoded, String(label));
1321
+ }
1322
+ return false;
1323
+ };
1324
+ if (hasLabel(-4)) {
1325
+ return issue(
1326
+ "SIG_PRIVATE_KEY_LEAKED",
1327
+ ["sigs", i, "cose_key"],
1328
+ "cose_key carries COSE_Key private-key material (label -4, the OKP/EC2 private scalar d); publishing a private key on the permanent ledger is forbidden"
1329
+ );
1330
+ }
1331
+ const kty = getLabel(1);
1332
+ if (kty !== 1) {
1333
+ return issue(
1334
+ "MALFORMED_SIG_COSE_SIGN1",
1335
+ ["sigs", i, "cose_key"],
1336
+ `sigs[${i}].cose_key COSE_Key kty (label 1) must be 1 (OKP); got ${String(kty)}`
1337
+ );
1338
+ }
1339
+ const crv = getLabel(-1);
1340
+ if (crv !== 6) {
1341
+ return issue(
1342
+ "MALFORMED_SIG_COSE_SIGN1",
1343
+ ["sigs", i, "cose_key"],
1344
+ `sigs[${i}].cose_key COSE_Key crv (label -1) must be 6 (Ed25519); got ${String(crv)}`
1345
+ );
1346
+ }
1347
+ if (!hasLabel(-2)) {
1348
+ return issue(
1349
+ "MALFORMED_SIG_COSE_SIGN1",
1350
+ ["sigs", i, "cose_key"],
1351
+ `sigs[${i}].cose_key COSE_Key missing label -2 (Ed25519 public-key bytes)`
1352
+ );
1353
+ }
1354
+ const x = getLabel(-2);
1355
+ if (!(x instanceof Uint8Array) || x.length !== 32) {
1356
+ const got = x instanceof Uint8Array ? `${x.length}-byte bstr` : typeof x;
1357
+ return issue(
1358
+ "MALFORMED_SIG_COSE_SIGN1",
1359
+ ["sigs", i, "cose_key"],
1360
+ `sigs[${i}].cose_key COSE_Key label -2 must be a 32-byte byte string (Ed25519 public key); got ${got}`
1361
+ );
1362
+ }
1363
+ return null;
1364
+ }
1365
+ var ACCEPTED_CIDV1_MULTIBASE = /* @__PURE__ */ new Set(["b", "B", "f", "F", "z"]);
1366
+ var ACCEPTED_MULTICODECS = /* @__PURE__ */ new Set([85, 112, 113]);
1367
+ var ACCEPTED_MULTIHASHES = /* @__PURE__ */ new Map([
1368
+ [18, 32],
1369
+ [45600, 32]
1370
+ ]);
1371
+ function validateCidProfile(cid) {
1372
+ if (cid.length === 0) return false;
1373
+ if (cid.startsWith("Qm")) {
1374
+ let decoded;
1375
+ try {
1376
+ decoded = decodeBase58btc(cid);
1377
+ } catch {
1378
+ return false;
1379
+ }
1380
+ return decoded.length === 34 && decoded[0] === 18 && decoded[1] === 32;
1381
+ }
1382
+ const mbPrefix = cid[0];
1383
+ if (!ACCEPTED_CIDV1_MULTIBASE.has(mbPrefix)) return false;
1384
+ let bytes;
1385
+ try {
1386
+ bytes = decodeMultibase(mbPrefix, cid.slice(1));
1387
+ } catch {
1388
+ return false;
1389
+ }
1390
+ if (bytes.length < 4) return false;
1391
+ const versionParse = readVarint(bytes, 0);
1392
+ if (versionParse === null || versionParse.value !== 1) return false;
1393
+ const codecParse = readVarint(bytes, versionParse.next);
1394
+ if (codecParse === null) return false;
1395
+ if (!ACCEPTED_MULTICODECS.has(codecParse.value)) return false;
1396
+ const mhParse = readVarint(bytes, codecParse.next);
1397
+ if (mhParse === null) return false;
1398
+ const lenParse = readVarint(bytes, mhParse.next);
1399
+ if (lenParse === null) return false;
1400
+ const digestLen = lenParse.value;
1401
+ const expectedLen = ACCEPTED_MULTIHASHES.get(mhParse.value);
1402
+ if (expectedLen === void 0 || digestLen !== expectedLen) return false;
1403
+ if (lenParse.next + digestLen !== bytes.length) return false;
1404
+ return true;
1405
+ }
1406
+ function readVarint(bytes, start) {
1407
+ let value = 0;
1408
+ let shift = 0;
1409
+ let i = start;
1410
+ while (i < bytes.length) {
1411
+ const b = bytes[i];
1412
+ value |= (b & 127) << shift;
1413
+ i++;
1414
+ if ((b & 128) === 0) return { value, next: i };
1415
+ shift += 7;
1416
+ if (shift > 28) return null;
1417
+ }
1418
+ return null;
1419
+ }
1420
+ function decodeMultibase(prefix, body) {
1421
+ switch (prefix) {
1422
+ case "b":
1423
+ return decodeBase32(body.toLowerCase(), "rfc4648-lower");
1424
+ case "B":
1425
+ return decodeBase32(body.toUpperCase(), "rfc4648-upper");
1426
+ case "f":
1427
+ return decodeBase16(body.toLowerCase());
1428
+ case "F":
1429
+ return decodeBase16(body.toUpperCase());
1430
+ case "z":
1431
+ return decodeBase58btc(body);
1432
+ default:
1433
+ throw new Error(`unsupported multibase prefix ${prefix}`);
1434
+ }
1435
+ }
1436
+ var BASE16_LOWER = "0123456789abcdef";
1437
+ var BASE16_UPPER = "0123456789ABCDEF";
1438
+ function decodeBase16(s) {
1439
+ if (s.length % 2 !== 0) throw new Error("base16: odd-length");
1440
+ const out = new Uint8Array(s.length / 2);
1441
+ const alphabet = s === s.toLowerCase() ? BASE16_LOWER : BASE16_UPPER;
1442
+ for (let i = 0; i < out.length; i++) {
1443
+ const hi = alphabet.indexOf(s[i * 2]);
1444
+ const lo = alphabet.indexOf(s[i * 2 + 1]);
1445
+ if (hi < 0 || lo < 0) throw new Error(`base16: non-hex char at ${i * 2}`);
1446
+ out[i] = hi << 4 | lo;
1447
+ }
1448
+ return out;
1449
+ }
1450
+ var BASE32_RFC4648_LOWER = "abcdefghijklmnopqrstuvwxyz234567";
1451
+ var BASE32_RFC4648_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
1452
+ function decodeBase32(s, variant) {
1453
+ const alphabet = variant === "rfc4648-lower" ? BASE32_RFC4648_LOWER : BASE32_RFC4648_UPPER;
1454
+ const trimmed = s.replace(/=+$/, "");
1455
+ const out = [];
1456
+ let buf = 0;
1457
+ let bits = 0;
1458
+ for (const ch of trimmed) {
1459
+ const idx = alphabet.indexOf(ch);
1460
+ if (idx < 0) throw new Error(`base32: invalid char '${ch}'`);
1461
+ buf = buf << 5 | idx;
1462
+ bits += 5;
1463
+ if (bits >= 8) {
1464
+ bits -= 8;
1465
+ out.push(buf >> bits & 255);
1466
+ }
1467
+ }
1468
+ return Uint8Array.from(out);
1469
+ }
1470
+ var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
1471
+ function decodeBase58btc(s) {
1472
+ if (s.length === 0) return new Uint8Array(0);
1473
+ let zeros = 0;
1474
+ while (zeros < s.length && s[zeros] === "1") zeros++;
1475
+ const size = Math.floor((s.length - zeros) * 733 / 1e3) + 1;
1476
+ const b256 = new Uint8Array(size);
1477
+ let length = 0;
1478
+ for (let i = zeros; i < s.length; i++) {
1479
+ const ch = s[i];
1480
+ const carryIdx = BASE58_ALPHABET.indexOf(ch);
1481
+ if (carryIdx < 0) throw new Error(`base58: invalid char '${ch}'`);
1482
+ let carry = carryIdx;
1483
+ let k = 0;
1484
+ for (let j2 = size - 1; (carry !== 0 || k < length) && j2 >= 0; j2--, k++) {
1485
+ carry += 58 * b256[j2];
1486
+ b256[j2] = carry % 256;
1487
+ carry = Math.floor(carry / 256);
1488
+ }
1489
+ length = k;
1490
+ }
1491
+ let it = size - length;
1492
+ while (it < size && b256[it] === 0) it++;
1493
+ const out = new Uint8Array(zeros + (size - it));
1494
+ let j = zeros;
1495
+ while (it < size) {
1496
+ out[j++] = b256[it++];
1497
+ }
1498
+ return out;
1499
+ }
1500
+ function checkCritShape(record, decodedTopKeys, errors) {
1501
+ const invalid = /* @__PURE__ */ new Set();
1502
+ if (!Array.isArray(record.crit)) return invalid;
1503
+ if (record.crit.length === 0) {
1504
+ errors.push(
1505
+ issue("SCHEMA_TYPE_MISMATCH", ["crit"], "crit[] must carry at least one entry when present")
1506
+ );
1507
+ return invalid;
1508
+ }
1509
+ const seen = /* @__PURE__ */ new Set();
1510
+ for (let i = 0; i < record.crit.length; i++) {
1511
+ const critName = record.crit[i];
1512
+ let reason = null;
1513
+ if (TOP_LEVEL_BASE_KEYS.has(critName)) {
1514
+ reason = `'${critName}' is a base key and MUST NOT appear in crit[]`;
1515
+ } else if (!isExtensionKey(critName)) {
1516
+ reason = `'${critName}' does not match the extension-key regex (^x-.+ or ^[a-z]+-.+)`;
1517
+ } else if (!decodedTopKeys.has(critName)) {
1518
+ reason = `'${critName}' is named in crit but absent from the record map`;
1519
+ } else if (seen.has(critName)) {
1520
+ reason = `'${critName}' appears more than once in crit[]`;
1521
+ }
1522
+ seen.add(critName);
1523
+ if (reason !== null) {
1524
+ invalid.add(i);
1525
+ errors.push(issue("CRIT_SHAPE_INVALID", ["crit", i], reason));
1526
+ }
1527
+ }
1528
+ return invalid;
1529
+ }
1530
+ function topLevelKeysOf(decoded) {
1531
+ if (decoded === null || typeof decoded !== "object") return /* @__PURE__ */ new Set();
1532
+ if (decoded instanceof Map) {
1533
+ const out = /* @__PURE__ */ new Set();
1534
+ for (const k of decoded.keys()) {
1535
+ if (typeof k === "string") out.add(k);
1536
+ }
1537
+ return out;
1538
+ }
1539
+ return new Set(Object.keys(decoded));
1540
+ }
1541
+ function issue(code, path, message) {
1542
+ return { code, path, message, severity: SEVERITY[code] };
1543
+ }
1544
+ function compareIssuePath(a, b) {
1545
+ return a.path.join(".").localeCompare(b.path.join("."));
1546
+ }
1547
+ function valueAtPath(root, path) {
1548
+ let cur = root;
1549
+ for (const seg of path) {
1550
+ if (cur === null || cur === void 0) return void 0;
1551
+ if (cur instanceof Map) {
1552
+ cur = cur.get(seg);
1553
+ continue;
1554
+ }
1555
+ if (typeof cur !== "object") return void 0;
1556
+ cur = cur[seg];
1557
+ }
1558
+ return cur;
1559
+ }
1560
+ async function argon2idV13(opts2) {
1561
+ return await argon2id({
1562
+ password: opts2.password,
1563
+ salt: opts2.salt,
1564
+ parallelism: opts2.parallelism,
1565
+ iterations: opts2.iterations,
1566
+ memorySize: opts2.memSizeKB,
1567
+ hashLength: opts2.outBytes,
1568
+ outputType: "binary"
1569
+ });
1570
+ }
1571
+ var AeadVerificationError = class extends Error {
1572
+ code = "aead_verification_failed";
1573
+ constructor(message, options) {
1574
+ super(message, options);
1575
+ this.name = "AeadVerificationError";
1576
+ }
1577
+ };
1578
+ function xchacha20Poly1305Decrypt(opts2) {
1579
+ try {
1580
+ return xchacha20poly1305(opts2.key, opts2.nonce, opts2.aad).decrypt(opts2.ciphertext);
1581
+ } catch (cause) {
1582
+ throw new AeadVerificationError("xchacha20-poly1305 decrypt failed", { cause });
1583
+ }
1584
+ }
1585
+ function sha2562(input) {
1586
+ return sha256(input);
1587
+ }
1588
+ function blake2b256(input) {
1589
+ return blake2b(input, { dkLen: 32 });
1590
+ }
1591
+ function blake2b2242(input) {
1592
+ return blake2b(input, { dkLen: 28 });
1593
+ }
1594
+ var LEAF_PREFIX = 0;
1595
+ var NODE_PREFIX = 1;
1596
+ var DIGEST_LENGTH = 32;
1597
+ function validateLeaves(leaves, fnName) {
1598
+ if (leaves.length === 0) {
1599
+ throw new Error(`${fnName}: empty leaf list (n == 0 is forbidden by RFC 9162 \xA72.1.1)`);
1600
+ }
1601
+ for (let i = 0; i < leaves.length; i++) {
1602
+ const leaf = leaves[i];
1603
+ if (!(leaf instanceof Uint8Array) || leaf.length !== DIGEST_LENGTH) {
1604
+ throw new Error(
1605
+ `${fnName}: leaf[${i}] must be a Uint8Array(${DIGEST_LENGTH}); got length ${leaf instanceof Uint8Array ? leaf.length : "non-Uint8Array"}`
1606
+ );
1607
+ }
1608
+ }
1609
+ }
1610
+ function merkleSha2256Root(leaves) {
1611
+ validateLeaves(leaves, "merkleSha2256Root");
1612
+ return mthRecursive(leaves, 0, leaves.length);
1613
+ }
1614
+ function largestPow2Lt(n) {
1615
+ let k = 1;
1616
+ while (k * 2 < n) k *= 2;
1617
+ return k;
1618
+ }
1619
+ function hashLeaf(d) {
1620
+ const buf = new Uint8Array(1 + d.length);
1621
+ buf[0] = LEAF_PREFIX;
1622
+ buf.set(d, 1);
1623
+ return sha256(buf);
1624
+ }
1625
+ function hashNode(left, right) {
1626
+ const buf = new Uint8Array(1 + left.length + right.length);
1627
+ buf[0] = NODE_PREFIX;
1628
+ buf.set(left, 1);
1629
+ buf.set(right, 1 + left.length);
1630
+ return sha256(buf);
1631
+ }
1632
+ function mthRecursive(leaves, start, end) {
1633
+ const n = end - start;
1634
+ if (n === 1) {
1635
+ return hashLeaf(leaves[start]);
1636
+ }
1637
+ const k = largestPow2Lt(n);
1638
+ const left = mthRecursive(leaves, start, start + k);
1639
+ const right = mthRecursive(leaves, start + k, end);
1640
+ return hashNode(left, right);
1641
+ }
1642
+ var abytesDoc = abytes;
1643
+ var randomBytes = randomBytes$1;
1644
+ function equalBytes(a, b) {
1645
+ if (a.length !== b.length)
1646
+ return false;
1647
+ let diff = 0;
1648
+ for (let i = 0; i < a.length; i++)
1649
+ diff |= a[i] ^ b[i];
1650
+ return diff === 0;
1651
+ }
1652
+ function copyBytes(bytes) {
1653
+ return Uint8Array.from(abytes(bytes));
1654
+ }
1655
+ function splitCoder(label, ...lengths) {
1656
+ const getLength = (c) => typeof c === "number" ? c : c.bytesLen;
1657
+ const bytesLen = lengths.reduce((sum, a) => sum + getLength(a), 0);
1658
+ return {
1659
+ bytesLen,
1660
+ encode: (bufs) => {
1661
+ const res = new Uint8Array(bytesLen);
1662
+ for (let i = 0, pos = 0; i < lengths.length; i++) {
1663
+ const c = lengths[i];
1664
+ const l = getLength(c);
1665
+ const b = typeof c === "number" ? bufs[i] : c.encode(bufs[i]);
1666
+ abytes(b, l, label);
1667
+ res.set(b, pos);
1668
+ if (typeof c !== "number")
1669
+ b.fill(0);
1670
+ pos += l;
1671
+ }
1672
+ return res;
1673
+ },
1674
+ decode: (buf) => {
1675
+ abytes(buf, bytesLen, label);
1676
+ const res = [];
1677
+ for (const c of lengths) {
1678
+ const l = getLength(c);
1679
+ const b = buf.subarray(0, l);
1680
+ res.push(typeof c === "number" ? b : c.decode(b));
1681
+ buf = buf.subarray(l);
1682
+ }
1683
+ return res;
1684
+ }
1685
+ };
1686
+ }
1687
+ function vecCoder(c, vecLen) {
1688
+ const coder = c;
1689
+ const bytesLen = vecLen * coder.bytesLen;
1690
+ return {
1691
+ bytesLen,
1692
+ encode: (u) => {
1693
+ if (u.length !== vecLen)
1694
+ throw new RangeError(`vecCoder.encode: wrong length=${u.length}. Expected: ${vecLen}`);
1695
+ const res = new Uint8Array(bytesLen);
1696
+ for (let i = 0, pos = 0; i < u.length; i++) {
1697
+ const b = coder.encode(u[i]);
1698
+ res.set(b, pos);
1699
+ b.fill(0);
1700
+ pos += b.length;
1701
+ }
1702
+ return res;
1703
+ },
1704
+ decode: (a) => {
1705
+ abytes(a, bytesLen);
1706
+ const r = [];
1707
+ for (let i = 0; i < a.length; i += coder.bytesLen)
1708
+ r.push(coder.decode(a.subarray(i, i + coder.bytesLen)));
1709
+ return r;
1710
+ }
1711
+ };
1712
+ }
1713
+ function cleanBytes(...list) {
1714
+ for (const t of list) {
1715
+ if (Array.isArray(t))
1716
+ for (const b of t)
1717
+ b.fill(0);
1718
+ else
1719
+ t.fill(0);
1720
+ }
1721
+ }
1722
+ function getMask(bits) {
1723
+ if (!Number.isSafeInteger(bits) || bits < 0 || bits > 32)
1724
+ throw new RangeError(`expected bits in [0..32], got ${bits}`);
1725
+ return bits === 32 ? 4294967295 : ~(-1 << bits) >>> 0;
1726
+ }
1727
+
1728
+ // ../../node_modules/.pnpm/@noble+post-quantum@0.6.1/node_modules/@noble/post-quantum/_crystals.js
1729
+ var genCrystals = (opts2) => {
1730
+ const { newPoly, N: N2, Q: Q2, F: F2, ROOT_OF_UNITY: ROOT_OF_UNITY2, brvBits} = opts2;
1731
+ const mod = (a, modulo = Q2) => {
1732
+ const result = a % modulo | 0;
1733
+ return (result >= 0 ? result | 0 : modulo + result | 0) | 0;
1734
+ };
1735
+ const smod = (a, modulo = Q2) => {
1736
+ const r = mod(a, modulo) | 0;
1737
+ return (r > modulo >> 1 ? r - modulo | 0 : r) | 0;
1738
+ };
1739
+ function getZettas() {
1740
+ const out = newPoly(N2);
1741
+ for (let i = 0; i < N2; i++) {
1742
+ const b = reverseBits(i, brvBits);
1743
+ const p = BigInt(ROOT_OF_UNITY2) ** BigInt(b) % BigInt(Q2);
1744
+ out[i] = Number(p) | 0;
1745
+ }
1746
+ return out;
1747
+ }
1748
+ const nttZetas = getZettas();
1749
+ const field = {
1750
+ add: (a, b) => mod((a | 0) + (b | 0)) | 0,
1751
+ sub: (a, b) => mod((a | 0) - (b | 0)) | 0,
1752
+ mul: (a, b) => mod((a | 0) * (b | 0)) | 0,
1753
+ inv: (_a) => {
1754
+ throw new Error("not implemented");
1755
+ }
1756
+ };
1757
+ const nttOpts = {
1758
+ N: N2,
1759
+ roots: nttZetas,
1760
+ invertButterflies: true,
1761
+ skipStages: 1 ,
1762
+ brp: false
1763
+ };
1764
+ const dif = FFTCore(field, { dit: false, ...nttOpts });
1765
+ const dit = FFTCore(field, { dit: true, ...nttOpts });
1766
+ const NTT = {
1767
+ encode: (r) => {
1768
+ return dif(r);
1769
+ },
1770
+ decode: (r) => {
1771
+ dit(r);
1772
+ for (let i = 0; i < r.length; i++)
1773
+ r[i] = mod(F2 * r[i]);
1774
+ return r;
1775
+ }
1776
+ };
1777
+ const bitsCoder = (d, c) => {
1778
+ const mask = getMask(d);
1779
+ const bytesLen = d * (N2 / 8);
1780
+ return {
1781
+ bytesLen,
1782
+ encode: (poly_) => {
1783
+ const poly = poly_;
1784
+ const r = new Uint8Array(bytesLen);
1785
+ for (let i = 0, buf = 0, bufLen = 0, pos = 0; i < poly.length; i++) {
1786
+ buf |= (c.encode(poly[i]) & mask) << bufLen;
1787
+ bufLen += d;
1788
+ for (; bufLen >= 8; bufLen -= 8, buf >>= 8)
1789
+ r[pos++] = buf & getMask(bufLen);
1790
+ }
1791
+ return r;
1792
+ },
1793
+ decode: (bytes) => {
1794
+ const r = newPoly(N2);
1795
+ for (let i = 0, buf = 0, bufLen = 0, pos = 0; i < bytes.length; i++) {
1796
+ buf |= bytes[i] << bufLen;
1797
+ bufLen += 8;
1798
+ for (; bufLen >= d; bufLen -= d, buf >>= d)
1799
+ r[pos++] = c.decode(buf & mask);
1800
+ }
1801
+ return r;
1802
+ }
1803
+ };
1804
+ };
1805
+ return {
1806
+ mod,
1807
+ smod,
1808
+ nttZetas,
1809
+ NTT: {
1810
+ encode: (r) => NTT.encode(r),
1811
+ decode: (r) => NTT.decode(r)
1812
+ },
1813
+ bitsCoder
1814
+ };
1815
+ };
1816
+ var createXofShake = (shake) => (seed, blockLen) => {
1817
+ if (!blockLen)
1818
+ blockLen = shake.blockLen;
1819
+ const _seed = new Uint8Array(seed.length + 2);
1820
+ _seed.set(seed);
1821
+ const seedLen = seed.length;
1822
+ const buf = new Uint8Array(blockLen);
1823
+ let h = shake.create({});
1824
+ let calls = 0;
1825
+ let xofs = 0;
1826
+ return {
1827
+ stats: () => ({ calls, xofs }),
1828
+ get: (x, y) => {
1829
+ _seed[seedLen + 0] = x;
1830
+ _seed[seedLen + 1] = y;
1831
+ h.destroy();
1832
+ h = shake.create({}).update(_seed);
1833
+ calls++;
1834
+ return () => {
1835
+ xofs++;
1836
+ return h.xofInto(buf);
1837
+ };
1838
+ },
1839
+ clean: () => {
1840
+ h.destroy();
1841
+ cleanBytes(buf, _seed);
1842
+ }
1843
+ };
1844
+ };
1845
+ var XOF128 = /* @__PURE__ */ createXofShake(shake128);
1846
+
1847
+ // ../../node_modules/.pnpm/@noble+post-quantum@0.6.1/node_modules/@noble/post-quantum/ml-kem.js
1848
+ var N = 256;
1849
+ var Q = 3329;
1850
+ var F = 3303;
1851
+ var ROOT_OF_UNITY = 17;
1852
+ var crystals = /* @__PURE__ */ genCrystals({
1853
+ N,
1854
+ Q,
1855
+ F,
1856
+ ROOT_OF_UNITY,
1857
+ newPoly: (n) => new Uint16Array(n),
1858
+ brvBits: 7});
1859
+ var PARAMS = /* @__PURE__ */ (() => Object.freeze({
1860
+ 512: Object.freeze({ N, Q, K: 2, ETA1: 3, ETA2: 2, du: 10, dv: 4, RBGstrength: 128 }),
1861
+ 768: Object.freeze({ N, Q, K: 3, ETA1: 2, ETA2: 2, du: 10, dv: 4, RBGstrength: 192 }),
1862
+ 1024: Object.freeze({ N, Q, K: 4, ETA1: 2, ETA2: 2, du: 11, dv: 5, RBGstrength: 256 })
1863
+ }))();
1864
+ var compress = (d) => {
1865
+ if (d >= 12)
1866
+ return { encode: (i) => i, decode: (i) => i >= Q ? i - Q : i };
1867
+ const a = 2 ** (d - 1);
1868
+ return {
1869
+ // This only matches standalone Compress_d after bitsCoder masks the result into Z_(2^d).
1870
+ encode: (i) => ((i << d) + Q / 2) / Q,
1871
+ // const decompress = (i: number) => round((Q / 2 ** d) * i);
1872
+ decode: (i) => i * Q + a >>> d
1873
+ };
1874
+ };
1875
+ var byteCoder = (d) => crystals.bitsCoder(d, { encode: (i) => i, decode: (i) => i >= Q ? i - Q : i } );
1876
+ var polyCoder = (d) => d === 12 ? byteCoder(12) : crystals.bitsCoder(d, compress(d));
1877
+ function polyAdd(a_, b_) {
1878
+ const a = a_;
1879
+ const b = b_;
1880
+ for (let i = 0; i < N; i++)
1881
+ a[i] = crystals.mod(a[i] + b[i]);
1882
+ }
1883
+ function polySub(a_, b_) {
1884
+ const a = a_;
1885
+ const b = b_;
1886
+ for (let i = 0; i < N; i++)
1887
+ a[i] = crystals.mod(a[i] - b[i]);
1888
+ }
1889
+ function BaseCaseMultiply(a0, a1, b0, b1, zeta) {
1890
+ const c0 = crystals.mod(a1 * b1 * zeta + a0 * b0);
1891
+ const c1 = crystals.mod(a0 * b1 + a1 * b0);
1892
+ return { c0, c1 };
1893
+ }
1894
+ function MultiplyNTTs(f_, g_) {
1895
+ const f = f_;
1896
+ const g = g_;
1897
+ for (let i = 0; i < N / 2; i++) {
1898
+ let z3 = crystals.nttZetas[64 + (i >> 1)];
1899
+ if (i & 1)
1900
+ z3 = -z3;
1901
+ const { c0, c1 } = BaseCaseMultiply(f[2 * i + 0], f[2 * i + 1], g[2 * i + 0], g[2 * i + 1], z3);
1902
+ f[2 * i + 0] = c0;
1903
+ f[2 * i + 1] = c1;
1904
+ }
1905
+ return f;
1906
+ }
1907
+ function SampleNTT(xof_) {
1908
+ const xof = xof_;
1909
+ const r = new Uint16Array(N);
1910
+ for (let j = 0; j < N; ) {
1911
+ const b = xof();
1912
+ if (b.length % 3)
1913
+ throw new Error("SampleNTT: unaligned block");
1914
+ for (let i = 0; j < N && i + 3 <= b.length; i += 3) {
1915
+ const d1 = (b[i + 0] >> 0 | b[i + 1] << 8) & 4095;
1916
+ const d2 = (b[i + 1] >> 4 | b[i + 2] << 4) & 4095;
1917
+ if (d1 < Q)
1918
+ r[j++] = d1;
1919
+ if (j < N && d2 < Q)
1920
+ r[j++] = d2;
1921
+ }
1922
+ }
1923
+ return r;
1924
+ }
1925
+ var sampleCBDBytes = (buf, eta) => {
1926
+ const r = new Uint16Array(N);
1927
+ const b32 = u32(buf);
1928
+ swap32IfBE(b32);
1929
+ let len = 0;
1930
+ for (let i = 0, p = 0, bb = 0, t0 = 0; i < b32.length; i++) {
1931
+ let b = b32[i];
1932
+ for (let j = 0; j < 32; j++) {
1933
+ bb += b & 1;
1934
+ b >>= 1;
1935
+ len += 1;
1936
+ if (len === eta) {
1937
+ t0 = bb;
1938
+ bb = 0;
1939
+ } else if (len === 2 * eta) {
1940
+ r[p++] = crystals.mod(t0 - bb);
1941
+ bb = 0;
1942
+ len = 0;
1943
+ }
1944
+ }
1945
+ }
1946
+ swap32IfBE(b32);
1947
+ if (len)
1948
+ throw new Error(`sampleCBD: leftover bits: ${len}`);
1949
+ return r;
1950
+ };
1951
+ function sampleCBD(PRF_, seed, nonce, eta) {
1952
+ const PRF = PRF_;
1953
+ return sampleCBDBytes(PRF(eta * N / 4, seed, nonce), eta);
1954
+ }
1955
+ var genKPKE = (opts_) => {
1956
+ const opts2 = opts_;
1957
+ const { K, PRF, XOF, HASH512, ETA1, ETA2, du, dv } = opts2;
1958
+ const poly1 = polyCoder(1);
1959
+ const polyV = polyCoder(dv);
1960
+ const polyU = polyCoder(du);
1961
+ const publicCoder = splitCoder("publicKey", vecCoder(polyCoder(12), K), 32);
1962
+ const secretCoder = vecCoder(polyCoder(12), K);
1963
+ const cipherCoder = splitCoder("ciphertext", vecCoder(polyU, K), polyV);
1964
+ const seedCoder = splitCoder("seed", 32, 32);
1965
+ return {
1966
+ secretCoder,
1967
+ lengths: {
1968
+ secretKey: secretCoder.bytesLen,
1969
+ publicKey: publicCoder.bytesLen,
1970
+ cipherText: cipherCoder.bytesLen
1971
+ },
1972
+ keygen: (seed) => {
1973
+ abytesDoc(seed, 32, "seed");
1974
+ const seedDst = new Uint8Array(33);
1975
+ seedDst.set(seed);
1976
+ seedDst[32] = K;
1977
+ const seedHash = HASH512(seedDst);
1978
+ const [rho, sigma] = seedCoder.decode(seedHash);
1979
+ const sHat = [];
1980
+ const tHat = [];
1981
+ for (let i = 0; i < K; i++)
1982
+ sHat.push(crystals.NTT.encode(sampleCBD(PRF, sigma, i, ETA1)));
1983
+ const x = XOF(rho);
1984
+ for (let i = 0; i < K; i++) {
1985
+ const e = crystals.NTT.encode(sampleCBD(PRF, sigma, K + i, ETA1));
1986
+ for (let j = 0; j < K; j++) {
1987
+ const aji = SampleNTT(x.get(j, i));
1988
+ polyAdd(e, MultiplyNTTs(aji, sHat[j]));
1989
+ }
1990
+ tHat.push(e);
1991
+ }
1992
+ x.clean();
1993
+ const res = {
1994
+ publicKey: publicCoder.encode([tHat, rho]),
1995
+ secretKey: secretCoder.encode(sHat)
1996
+ };
1997
+ cleanBytes(rho, sigma, sHat, tHat, seedDst, seedHash);
1998
+ return res;
1999
+ },
2000
+ encrypt: (publicKey, msg, seed) => {
2001
+ const [tHat, rho] = publicCoder.decode(publicKey);
2002
+ const rHat = [];
2003
+ for (let i = 0; i < K; i++)
2004
+ rHat.push(crystals.NTT.encode(sampleCBD(PRF, seed, i, ETA1)));
2005
+ const x = XOF(rho);
2006
+ const tmp2 = new Uint16Array(N);
2007
+ const u = [];
2008
+ for (let i = 0; i < K; i++) {
2009
+ const e1 = sampleCBD(PRF, seed, K + i, ETA2);
2010
+ const tmp = new Uint16Array(N);
2011
+ for (let j = 0; j < K; j++) {
2012
+ const aij = SampleNTT(x.get(i, j));
2013
+ polyAdd(tmp, MultiplyNTTs(aij, rHat[j]));
2014
+ }
2015
+ polyAdd(e1, crystals.NTT.decode(tmp));
2016
+ u.push(e1);
2017
+ polyAdd(tmp2, MultiplyNTTs(tHat[i], rHat[i]));
2018
+ cleanBytes(tmp);
2019
+ }
2020
+ x.clean();
2021
+ const e2 = sampleCBD(PRF, seed, 2 * K, ETA2);
2022
+ polyAdd(e2, crystals.NTT.decode(tmp2));
2023
+ const v = poly1.decode(msg);
2024
+ polyAdd(v, e2);
2025
+ cleanBytes(tHat, rHat, tmp2, e2);
2026
+ return cipherCoder.encode([u, v]);
2027
+ },
2028
+ decrypt: (cipherText, privateKey) => {
2029
+ const [u, v] = cipherCoder.decode(cipherText);
2030
+ const sk = secretCoder.decode(privateKey);
2031
+ const tmp = new Uint16Array(N);
2032
+ for (let i = 0; i < K; i++)
2033
+ polyAdd(tmp, MultiplyNTTs(sk[i], crystals.NTT.encode(u[i])));
2034
+ polySub(v, crystals.NTT.decode(tmp));
2035
+ cleanBytes(tmp, sk, u);
2036
+ return poly1.encode(v);
2037
+ }
2038
+ };
2039
+ };
2040
+ function createKyber(opts2) {
2041
+ const rawOpts = opts2;
2042
+ const KPKE = genKPKE(rawOpts);
2043
+ const { HASH256, HASH512, KDF } = rawOpts;
2044
+ const { secretCoder: KPKESecretCoder, lengths } = KPKE;
2045
+ const secretCoder = splitCoder("secretKey", lengths.secretKey, lengths.publicKey, 32, 32);
2046
+ const msgLen = 32;
2047
+ const seedLen = 64;
2048
+ const kemLengths = Object.freeze({
2049
+ ...lengths,
2050
+ seed: 64,
2051
+ msg: msgLen,
2052
+ msgRand: msgLen,
2053
+ secretKey: secretCoder.bytesLen
2054
+ });
2055
+ return Object.freeze({
2056
+ info: Object.freeze({ type: "ml-kem" }),
2057
+ lengths: kemLengths,
2058
+ keygen: (seed = randomBytes(seedLen)) => {
2059
+ abytesDoc(seed, seedLen, "seed");
2060
+ const { publicKey, secretKey: sk } = KPKE.keygen(seed.subarray(0, 32));
2061
+ const publicKeyHash = HASH256(publicKey);
2062
+ const secretKey = secretCoder.encode([sk, publicKey, publicKeyHash, seed.subarray(32)]);
2063
+ cleanBytes(sk, publicKeyHash);
2064
+ return {
2065
+ publicKey,
2066
+ secretKey
2067
+ };
2068
+ },
2069
+ getPublicKey: (secretKey) => {
2070
+ const [_sk, publicKey, _publicKeyHash, _z] = secretCoder.decode(secretKey);
2071
+ return Uint8Array.from(publicKey);
2072
+ },
2073
+ encapsulate: (publicKey, msg = randomBytes(msgLen)) => {
2074
+ abytesDoc(publicKey, lengths.publicKey, "publicKey");
2075
+ abytesDoc(msg, msgLen, "message");
2076
+ const eke = publicKey.subarray(0, 384 * opts2.K);
2077
+ const ek = KPKESecretCoder.encode(KPKESecretCoder.decode(copyBytes(eke)));
2078
+ if (!equalBytes(ek, eke)) {
2079
+ cleanBytes(ek);
2080
+ throw new Error("ML-KEM.encapsulate: wrong publicKey modulus");
2081
+ }
2082
+ cleanBytes(ek);
2083
+ const kr = HASH512.create().update(msg).update(HASH256(publicKey)).digest();
2084
+ const cipherText = KPKE.encrypt(publicKey, msg, kr.subarray(32, 64));
2085
+ cleanBytes(kr.subarray(32));
2086
+ return {
2087
+ cipherText,
2088
+ sharedSecret: kr.subarray(0, 32)
2089
+ };
2090
+ },
2091
+ decapsulate: (cipherText, secretKey) => {
2092
+ abytesDoc(secretKey, secretCoder.bytesLen, "secretKey");
2093
+ abytesDoc(cipherText, lengths.cipherText, "cipherText");
2094
+ const k768 = secretCoder.bytesLen - 96;
2095
+ const start = k768 + 32;
2096
+ const test = HASH256(secretKey.subarray(k768 / 2, start));
2097
+ if (!equalBytes(test, secretKey.subarray(start, start + 32)))
2098
+ throw new Error("invalid secretKey: hash check failed");
2099
+ const [sk, publicKey, publicKeyHash, z3] = secretCoder.decode(secretKey);
2100
+ const msg = KPKE.decrypt(cipherText, sk);
2101
+ const kr = HASH512.create().update(msg).update(publicKeyHash).digest();
2102
+ const Khat = kr.subarray(0, 32);
2103
+ const cipherText2 = KPKE.encrypt(publicKey, msg, kr.subarray(32, 64));
2104
+ const isValid = equalBytes(cipherText, cipherText2);
2105
+ const Kbar = KDF.create({ dkLen: 32 }).update(z3).update(cipherText).digest();
2106
+ cleanBytes(msg, cipherText2, !isValid ? Khat : Kbar);
2107
+ return isValid ? Khat : Kbar;
2108
+ }
2109
+ });
2110
+ }
2111
+ function shakePRF(dkLen, key, nonce) {
2112
+ return shake256.create({ dkLen }).update(key).update(new Uint8Array([nonce])).digest();
2113
+ }
2114
+ var opts = /* @__PURE__ */ (() => ({
2115
+ HASH256: sha3_256,
2116
+ HASH512: sha3_512,
2117
+ KDF: shake256,
2118
+ XOF: XOF128,
2119
+ PRF: shakePRF
2120
+ }))();
2121
+ var mk = (params) => createKyber({
2122
+ ...opts,
2123
+ ...params
2124
+ });
2125
+ var ml_kem768 = /* @__PURE__ */ (() => mk(PARAMS[768]))();
2126
+
2127
+ // ../../node_modules/.pnpm/@noble+post-quantum@0.6.1/node_modules/@noble/post-quantum/hybrid.js
2128
+ function ecKeygen(curve, allowZeroKey = false) {
2129
+ const lengths = curve.lengths;
2130
+ let keygen = curve.keygen;
2131
+ if (allowZeroKey) {
2132
+ if (!("getSharedSecret" in curve && "sign" in curve && "verify" in curve))
2133
+ throw new Error("allowZeroKey requires a Weierstrass curve");
2134
+ const wCurve = curve;
2135
+ const Fn = wCurve.Point.Fn;
2136
+ keygen = (seed = randomBytes(lengths.seed)) => {
2137
+ abytes(seed, lengths.seed, "seed");
2138
+ const seedScalar = Fn.isLE ? bytesToNumberLE(seed) : bytesToNumberBE(seed);
2139
+ const secretKey = Fn.toBytes(Fn.create(seedScalar));
2140
+ return {
2141
+ secretKey,
2142
+ publicKey: curve.getPublicKey(secretKey)
2143
+ };
2144
+ };
2145
+ }
2146
+ return {
2147
+ lengths: { secretKey: lengths.secretKey, publicKey: lengths.publicKey, seed: lengths.seed },
2148
+ keygen: (seed) => keygen(seed),
2149
+ getPublicKey: (secretKey) => curve.getPublicKey(secretKey)
2150
+ };
2151
+ }
2152
+ function ecdhKem(curve, allowZeroKey = false) {
2153
+ const kg = ecKeygen(curve, allowZeroKey);
2154
+ if (!curve.getSharedSecret)
2155
+ throw new Error("wrong curve");
2156
+ return {
2157
+ lengths: { ...kg.lengths, msg: kg.lengths.seed, cipherText: kg.lengths.publicKey },
2158
+ keygen: kg.keygen,
2159
+ getPublicKey: kg.getPublicKey,
2160
+ encapsulate(publicKey, rand = randomBytes(curve.lengths.seed)) {
2161
+ const seed = copyBytes(rand);
2162
+ let ek = void 0;
2163
+ try {
2164
+ ek = this.keygen(seed).secretKey;
2165
+ const sharedSecret = this.decapsulate(publicKey, ek);
2166
+ const cipherText = curve.getPublicKey(ek);
2167
+ return { sharedSecret, cipherText };
2168
+ } finally {
2169
+ cleanBytes(seed);
2170
+ if (ek)
2171
+ cleanBytes(ek);
2172
+ }
2173
+ },
2174
+ decapsulate(cipherText, secretKey) {
2175
+ const res = curve.getSharedSecret(secretKey, cipherText);
2176
+ return curve.lengths.publicKeyHasPrefix ? res.subarray(1) : res;
2177
+ }
2178
+ };
2179
+ }
2180
+ function splitLengths(lst, name) {
2181
+ return splitCoder(name, ...lst.map((i) => {
2182
+ if (typeof i.lengths[name] !== "number")
2183
+ throw new Error("wrong length: " + name);
2184
+ return i.lengths[name];
2185
+ }));
2186
+ }
2187
+ function expandSeedXof(xof) {
2188
+ return ((seed, seedLen) => xof(seed, { dkLen: seedLen }));
2189
+ }
2190
+ function combineKeys(realSeedLen, expandSeed_, ...ck_) {
2191
+ const expandSeed = expandSeed_;
2192
+ const ck = ck_;
2193
+ const seedCoder = splitLengths(ck, "seed");
2194
+ const pkCoder = splitLengths(ck, "publicKey");
2195
+ anumber(realSeedLen);
2196
+ function expandDecapsulationKey(seed) {
2197
+ abytes(seed, realSeedLen);
2198
+ const expandedRaw = expandSeed(seed, seedCoder.bytesLen);
2199
+ const expandedSeed = expandedRaw.buffer === seed.buffer ? copyBytes(expandedRaw) : expandedRaw;
2200
+ const expanded = [];
2201
+ const keySecret = [];
2202
+ const secretKey = [];
2203
+ const publicKey = [];
2204
+ let ok = false;
2205
+ try {
2206
+ for (const part of seedCoder.decode(expandedSeed))
2207
+ expanded.push(copyBytes(part));
2208
+ for (let i = 0; i < ck.length; i++) {
2209
+ const keys = ck[i].keygen(expanded[i]);
2210
+ keySecret.push(keys.secretKey);
2211
+ secretKey.push(copyBytes(keys.secretKey));
2212
+ publicKey.push(keys.publicKey);
2213
+ }
2214
+ ok = true;
2215
+ return { secretKey, publicKey };
2216
+ } finally {
2217
+ cleanBytes(expandedSeed, expanded, keySecret);
2218
+ if (!ok)
2219
+ cleanBytes(secretKey);
2220
+ }
2221
+ }
2222
+ return {
2223
+ info: { lengths: { seed: realSeedLen, publicKey: pkCoder.bytesLen, secretKey: realSeedLen } },
2224
+ getPublicKey(secretKey) {
2225
+ return this.keygen(secretKey).publicKey;
2226
+ },
2227
+ keygen(seed = randomBytes(realSeedLen)) {
2228
+ const { publicKey: pk, secretKey } = expandDecapsulationKey(seed);
2229
+ try {
2230
+ const publicKey = pkCoder.encode(pk);
2231
+ return { secretKey: seed, publicKey };
2232
+ } finally {
2233
+ cleanBytes(pk);
2234
+ cleanBytes(secretKey);
2235
+ }
2236
+ },
2237
+ expandDecapsulationKey,
2238
+ realSeedLen
2239
+ };
2240
+ }
2241
+ function combineKEMS(realSeedLen, realMsgLen, expandSeed, combiner, ...kems) {
2242
+ const rawCombiner = combiner;
2243
+ const rawKems = kems;
2244
+ const keys = combineKeys(realSeedLen, expandSeed, ...rawKems);
2245
+ const ctCoder = splitLengths(rawKems, "cipherText");
2246
+ const pkCoder = splitLengths(rawKems, "publicKey");
2247
+ const msgCoder = splitLengths(rawKems, "msg");
2248
+ anumber(realMsgLen);
2249
+ const lengths = Object.freeze({
2250
+ ...keys.info.lengths,
2251
+ msg: realMsgLen,
2252
+ msgRand: msgCoder.bytesLen,
2253
+ cipherText: ctCoder.bytesLen
2254
+ });
2255
+ return Object.freeze({
2256
+ lengths,
2257
+ getPublicKey: keys.getPublicKey,
2258
+ keygen: keys.keygen,
2259
+ encapsulate(pk, randomness = randomBytes(msgCoder.bytesLen)) {
2260
+ const pks = pkCoder.decode(pk);
2261
+ const rand = msgCoder.decode(randomness);
2262
+ const sharedSecret = [];
2263
+ const cipherText = [];
2264
+ try {
2265
+ for (let i = 0; i < rawKems.length; i++) {
2266
+ const enc = rawKems[i].encapsulate(pks[i], rand[i]);
2267
+ sharedSecret.push(enc.sharedSecret);
2268
+ cipherText.push(enc.cipherText);
2269
+ }
2270
+ return {
2271
+ // Detach the combiner result before cleanup: a caller-provided combiner may alias one of
2272
+ // the child sharedSecret buffers, and those child buffers are zeroized immediately below.
2273
+ sharedSecret: copyBytes(rawCombiner(pks, cipherText, sharedSecret)),
2274
+ cipherText: ctCoder.encode(cipherText)
2275
+ };
2276
+ } finally {
2277
+ cleanBytes(sharedSecret, cipherText);
2278
+ }
2279
+ },
2280
+ decapsulate(ct, seed) {
2281
+ const cts = ctCoder.decode(ct);
2282
+ const { publicKey, secretKey } = keys.expandDecapsulationKey(seed);
2283
+ const sharedSecret = rawKems.map((i, j) => i.decapsulate(cts[j], secretKey[j]));
2284
+ try {
2285
+ return copyBytes(rawCombiner(publicKey, cts, sharedSecret));
2286
+ } finally {
2287
+ cleanBytes(secretKey, sharedSecret);
2288
+ }
2289
+ }
2290
+ });
2291
+ }
2292
+ var x25519kem = /* @__PURE__ */ ecdhKem(x25519);
2293
+ var ml_kem768_x25519 = /* @__PURE__ */ (() => combineKEMS(
2294
+ 32,
2295
+ 32,
2296
+ expandSeedXof(shake256),
2297
+ // Awesome label, so much escaping hell in a single line.
2298
+ (pk, ct, ss) => sha3_256(concatBytes$1(ss[0], ss[1], ct[1], pk[1], asciiToBytes("\\.//^\\"))),
2299
+ ml_kem768,
2300
+ x25519kem
2301
+ ))();
2302
+ var XWing = /* @__PURE__ */ (() => ml_kem768_x25519)();
2303
+ var AeadVerificationError2 = class extends Error {
2304
+ code = "aead_verification_failed";
2305
+ constructor(message, options) {
2306
+ super(message, options);
2307
+ this.name = "AeadVerificationError";
2308
+ }
2309
+ };
2310
+ function chacha20Poly1305Decrypt(opts2) {
2311
+ try {
2312
+ return chacha20poly1305(opts2.key, opts2.nonce, opts2.aad).decrypt(opts2.ciphertext);
2313
+ } catch (cause) {
2314
+ throw new AeadVerificationError2("chacha20-poly1305 decrypt failed", { cause });
2315
+ }
2316
+ }
2317
+ function xchacha20Poly1305Decrypt2(opts2) {
2318
+ try {
2319
+ return xchacha20poly1305(opts2.key, opts2.nonce, opts2.aad).decrypt(opts2.ciphertext);
2320
+ } catch (cause) {
2321
+ throw new AeadVerificationError2("xchacha20-poly1305 decrypt failed", { cause });
2322
+ }
2323
+ }
2324
+ function hkdfSha256(opts2) {
2325
+ return hkdf(sha256, opts2.ikm, opts2.salt, opts2.info, opts2.length);
2326
+ }
2327
+ var MLKEM768X25519_ENC_LENGTH = 1120;
2328
+ var MLKEM768X25519_SEED_LENGTH = 32;
2329
+ function mlkem768x25519Decapsulate(opts2) {
2330
+ if (opts2.secretSeed.length !== MLKEM768X25519_SEED_LENGTH) {
2331
+ throw new Error(
2332
+ `mlkem768x25519 secret seed must be ${MLKEM768X25519_SEED_LENGTH} bytes, got ${opts2.secretSeed.length}`
2333
+ );
2334
+ }
2335
+ if (opts2.enc.length !== MLKEM768X25519_ENC_LENGTH) {
2336
+ throw new Error(
2337
+ `mlkem768x25519 enc must be ${MLKEM768X25519_ENC_LENGTH} bytes, got ${opts2.enc.length}`
2338
+ );
2339
+ }
2340
+ return XWing.decapsulate(opts2.enc, opts2.secretSeed);
2341
+ }
2342
+ var X25519LowOrderPointError = class extends Error {
2343
+ code = "X25519_LOW_ORDER_POINT";
2344
+ constructor(options) {
2345
+ super("x25519 ECDH rejected: peer public key is a small-order point", options);
2346
+ this.name = "X25519LowOrderPointError";
2347
+ }
2348
+ };
2349
+ var NOBLE_LOW_ORDER_MESSAGE = "invalid private or public key received";
2350
+ function x25519PublicKey(opts2) {
2351
+ return x25519.getPublicKey(opts2.secretKey);
2352
+ }
2353
+ function x25519Ecdh(opts2) {
2354
+ try {
2355
+ return x25519.getSharedSecret(opts2.secretKey, opts2.theirPublicKey);
2356
+ } catch (e) {
2357
+ if (e instanceof Error && e.message === NOBLE_LOW_ORDER_MESSAGE) {
2358
+ throw new X25519LowOrderPointError({ cause: e });
2359
+ }
2360
+ throw e;
2361
+ }
2362
+ }
2363
+ var EciesSealedPoeError = class extends Error {
2364
+ code;
2365
+ constructor(code, message, options) {
2366
+ super(message, options);
2367
+ this.name = "EciesSealedPoeError";
2368
+ this.code = code;
2369
+ }
2370
+ };
2371
+ function encodeCanonicalCbor3(value) {
2372
+ return encode(value, {
2373
+ cde: true,
2374
+ collapseBigInts: true,
2375
+ rejectDuplicateKeys: true,
2376
+ sortKeys: sortCoreDeterministic
2377
+ });
2378
+ }
2379
+ var CHUNK_MAX_BYTES = 64;
2380
+ function chunkKemCt(value) {
2381
+ if (value.length === 0) {
2382
+ throw new Error("chunkKemCt: refusing to chunk an empty byte string");
2383
+ }
2384
+ const chunks = [];
2385
+ for (let i = 0; i < value.length; i += CHUNK_MAX_BYTES) {
2386
+ chunks.push(value.subarray(i, Math.min(i + CHUNK_MAX_BYTES, value.length)));
2387
+ }
2388
+ return chunks;
2389
+ }
2390
+ function joinKemCt(chunks) {
2391
+ let total = 0;
2392
+ for (const c of chunks) total += c.length;
2393
+ const out = new Uint8Array(total);
2394
+ let offset = 0;
2395
+ for (const c of chunks) {
2396
+ out.set(c, offset);
2397
+ offset += c.length;
2398
+ }
2399
+ return out;
2400
+ }
2401
+ function slotsToMacCbor(slots, kem) {
2402
+ let value;
2403
+ if (kem === "x25519") {
2404
+ value = slots.map((s) => ({ epk: s.epk, wrap: s.wrap }));
2405
+ } else {
2406
+ value = slots.map((s) => ({
2407
+ // Canonicalize the chunk boundaries before the MAC commits to them:
2408
+ // reassemble the logical ciphertext and re-split into canonical ≤ 64-byte
2409
+ // chunks. The on-wire `kem_ct` array is a transport detail (the Cardano
2410
+ // ledger's 64-byte metadatum cap), and a hostile or non-canonical chunking
2411
+ // ([1, 63, …] instead of [64, …]) reassembles to the SAME bytes — so the
2412
+ // MAC must be invariant to it. Committing to the verbatim wire chunks would
2413
+ // let an attacker re-chunk an honest envelope and break the slots_mac match
2414
+ // for an honest recipient. Honest (already-64B-chunked) records are
2415
+ // unchanged; a real byte flip still changes the reassembled bytes and is
2416
+ // still rejected.
2417
+ kem_ct: chunkKemCt(joinKemCt(s.kem_ct)),
2418
+ wrap: s.wrap
2419
+ }));
2420
+ }
2421
+ return encodeCanonicalCbor3(value);
2422
+ }
2423
+ var CARDANO_POE_HKDF_INFO_KEK = new TextEncoder().encode("cardano-poe-kek-v1");
2424
+ var CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 = new TextEncoder().encode(
2425
+ "cardano-poe-kek-mlkem768x25519-v1"
2426
+ );
2427
+ var CARDANO_POE_HKDF_INFO_SLOTS_MAC = new TextEncoder().encode(
2428
+ "cardano-poe-slots-mac-v1"
2429
+ );
2430
+ var ZERO_NONCE_12 = new Uint8Array(12);
2431
+ if (CARDANO_POE_HKDF_INFO_KEK.length !== 18) {
2432
+ throw new Error("CARDANO_POE_HKDF_INFO_KEK byte-length invariant violated (expected 18)");
2433
+ }
2434
+ if (CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519.length !== 33) {
2435
+ throw new Error(
2436
+ "CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 byte-length invariant violated (expected 33)"
2437
+ );
2438
+ }
2439
+ if (CARDANO_POE_HKDF_INFO_SLOTS_MAC.length !== 24) {
2440
+ throw new Error("CARDANO_POE_HKDF_INFO_SLOTS_MAC byte-length invariant violated (expected 24)");
2441
+ }
2442
+ if (ZERO_NONCE_12.length !== 12) {
2443
+ throw new Error("ZERO_NONCE_12 byte-length invariant violated (expected 12)");
2444
+ }
2445
+ function compareCt2(a, b) {
2446
+ if (a.length !== b.length) return false;
2447
+ let diff = 0;
2448
+ for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
2449
+ return diff === 0;
2450
+ }
2451
+ function selectBundleSecrets(envelope, bundle) {
2452
+ return envelope.kem === "x25519" ? bundle.x25519PrivateKeys : bundle.mlkem768x25519SecretSeeds;
2453
+ }
2454
+ var ZERO_NONCE_122 = new Uint8Array(12);
2455
+ var EMPTY_SALT2 = new Uint8Array(0);
2456
+ var X25519_SECRET_KEY_LENGTH2 = 32;
2457
+ var X25519_PUBLIC_KEY_LENGTH2 = 32;
2458
+ var NONCE_LENGTH2 = 24;
2459
+ var WRAP_LENGTH2 = 48;
2460
+ var SLOTS_MAC_LENGTH2 = 32;
2461
+ function concat2(a, b) {
2462
+ const out = new Uint8Array(a.length + b.length);
2463
+ out.set(a, 0);
2464
+ out.set(b, a.length);
2465
+ return out;
2466
+ }
2467
+ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
2468
+ if (envelope.scheme !== 1) {
2469
+ throw new EciesSealedPoeError(
2470
+ "UNSUPPORTED_ENC_VERSION",
2471
+ `envelope.scheme=${String(envelope.scheme)} unsupported (expected 1)`
2472
+ );
2473
+ }
2474
+ if (envelope.aead !== "xchacha20-poly1305") {
2475
+ throw new EciesSealedPoeError(
2476
+ "UNSUPPORTED_AEAD_ALG",
2477
+ `envelope.aead=${String(envelope.aead)} unsupported (expected 'xchacha20-poly1305')`
2478
+ );
2479
+ }
2480
+ if (envelope.kem !== "x25519" && envelope.kem !== "mlkem768x25519") {
2481
+ throw new EciesSealedPoeError(
2482
+ "UNSUPPORTED_KEM_ALG",
2483
+ `envelope.kem=${String(envelope.kem)} unsupported (expected 'x25519' or 'mlkem768x25519')`
2484
+ );
2485
+ }
2486
+ const n = envelope.slots.length;
2487
+ if (n < 1) {
2488
+ throw new EciesSealedPoeError("ENC_SLOTS_EMPTY", `envelope.slots.length=${n} must be >= 1`);
2489
+ }
2490
+ if (envelope.nonce.length !== NONCE_LENGTH2) {
2491
+ throw new EciesSealedPoeError(
2492
+ "NONCE_LENGTH_MISMATCH",
2493
+ `envelope.nonce MUST be exactly ${NONCE_LENGTH2} bytes, got ${envelope.nonce.length}`
2494
+ );
2495
+ }
2496
+ if (envelope.slots_mac.length !== SLOTS_MAC_LENGTH2) {
2497
+ throw new EciesSealedPoeError(
2498
+ "ENC_SLOTS_MAC_INVALID_LENGTH",
2499
+ `envelope.slots_mac MUST be exactly ${SLOTS_MAC_LENGTH2} bytes, got ${envelope.slots_mac.length}`
2500
+ );
2501
+ }
2502
+ if (envelope.kem === "x25519") {
2503
+ for (let i = 0; i < n; i++) {
2504
+ const slot = envelope.slots[i];
2505
+ if (slot.epk.length !== X25519_PUBLIC_KEY_LENGTH2) {
2506
+ throw new EciesSealedPoeError(
2507
+ "KEM_EPK_LENGTH_MISMATCH",
2508
+ `envelope.slots[${i}].epk MUST be exactly ${X25519_PUBLIC_KEY_LENGTH2} bytes, got ${slot.epk.length}`
2509
+ );
2510
+ }
2511
+ if (slot.wrap.length !== WRAP_LENGTH2) {
2512
+ throw new EciesSealedPoeError(
2513
+ "WRAP_LENGTH_MISMATCH",
2514
+ `envelope.slots[${i}].wrap MUST be exactly ${WRAP_LENGTH2} bytes, got ${slot.wrap.length}`
2515
+ );
2516
+ }
2517
+ }
2518
+ } else {
2519
+ for (let i = 0; i < n; i++) {
2520
+ const slot = envelope.slots[i];
2521
+ const enc = joinKemCt(slot.kem_ct);
2522
+ if (enc.length !== MLKEM768X25519_ENC_LENGTH) {
2523
+ throw new EciesSealedPoeError(
2524
+ "KEM_CT_LENGTH_MISMATCH",
2525
+ `envelope.slots[${i}].kem_ct MUST reassemble to exactly ${MLKEM768X25519_ENC_LENGTH} bytes, got ${enc.length}`
2526
+ );
2527
+ }
2528
+ if (slot.wrap.length !== WRAP_LENGTH2) {
2529
+ throw new EciesSealedPoeError(
2530
+ "WRAP_LENGTH_MISMATCH",
2531
+ `envelope.slots[${i}].wrap MUST be exactly ${WRAP_LENGTH2} bytes, got ${slot.wrap.length}`
2532
+ );
2533
+ }
2534
+ }
2535
+ }
2536
+ if (multiPrivKeys !== void 0) {
2537
+ for (let i = 0; i < multiPrivKeys.length; i++) {
2538
+ if (multiPrivKeys[i].length !== X25519_SECRET_KEY_LENGTH2) {
2539
+ throw new EciesSealedPoeError(
2540
+ "INVALID_RECIPIENT_KEY",
2541
+ `recipientSecretKeys[${i}] MUST be exactly ${X25519_SECRET_KEY_LENGTH2} bytes, got ${multiPrivKeys[i].length}`
2542
+ );
2543
+ }
2544
+ }
2545
+ } else if (singlePrivKey !== void 0) {
2546
+ if (singlePrivKey.length !== X25519_SECRET_KEY_LENGTH2) {
2547
+ throw new EciesSealedPoeError(
2548
+ "INVALID_RECIPIENT_KEY",
2549
+ `recipientSecretKey MUST be exactly ${X25519_SECRET_KEY_LENGTH2} bytes, got ${singlePrivKey.length}`
2550
+ );
2551
+ }
2552
+ }
2553
+ }
2554
+ function tryX25519Slot(args) {
2555
+ if (args.liveSlot) {
2556
+ try {
2557
+ const shared = x25519Ecdh({
2558
+ secretKey: args.recipientSecretKey,
2559
+ theirPublicKey: args.slot.epk
2560
+ });
2561
+ const kek = hkdfSha256({
2562
+ ikm: shared,
2563
+ salt: concat2(args.slot.epk, args.pubRLocal),
2564
+ info: CARDANO_POE_HKDF_INFO_KEK,
2565
+ length: 32
2566
+ });
2567
+ return chacha20Poly1305Decrypt({
2568
+ key: kek,
2569
+ nonce: ZERO_NONCE_122,
2570
+ aad: CARDANO_POE_HKDF_INFO_KEK,
2571
+ ciphertext: args.slot.wrap
2572
+ });
2573
+ } catch (e) {
2574
+ if (!(e instanceof AeadVerificationError2) && !(e instanceof X25519LowOrderPointError)) {
2575
+ throw e;
2576
+ }
2577
+ return null;
2578
+ }
2579
+ }
2580
+ try {
2581
+ const shared = x25519Ecdh({
2582
+ secretKey: args.recipientSecretKey,
2583
+ theirPublicKey: args.slot.epk
2584
+ });
2585
+ hkdfSha256({
2586
+ ikm: shared,
2587
+ salt: concat2(args.slot.epk, args.pubRLocal),
2588
+ info: CARDANO_POE_HKDF_INFO_KEK,
2589
+ length: 32
2590
+ });
2591
+ } catch (e) {
2592
+ if (!(e instanceof X25519LowOrderPointError)) throw e;
2593
+ }
2594
+ return null;
2595
+ }
2596
+ function tryMlkem768X25519Slot(args) {
2597
+ const enc = joinKemCt(args.slot.kem_ct);
2598
+ const ss = mlkem768x25519Decapsulate({ secretSeed: args.recipientSecretKey, enc });
2599
+ const kek = hkdfSha256({
2600
+ ikm: ss,
2601
+ salt: EMPTY_SALT2,
2602
+ info: CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519,
2603
+ length: 32
2604
+ });
2605
+ if (!args.liveSlot) {
2606
+ return null;
2607
+ }
2608
+ try {
2609
+ return chacha20Poly1305Decrypt({
2610
+ key: kek,
2611
+ nonce: ZERO_NONCE_122,
2612
+ aad: CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519,
2613
+ ciphertext: args.slot.wrap
2614
+ });
2615
+ } catch (e) {
2616
+ if (!(e instanceof AeadVerificationError2)) throw e;
2617
+ return null;
2618
+ }
2619
+ }
2620
+ function tryRecipientUnwrapWithIdx(envelope, recipientSecretKey, constantTimeN, slotsAttemptedOut) {
2621
+ const n = envelope.slots.length;
2622
+ let cek = null;
2623
+ let matchedSlotIdx = -1;
2624
+ if (envelope.kem === "x25519") {
2625
+ const pubRLocal = x25519PublicKey({ secretKey: recipientSecretKey });
2626
+ for (let i = 0; i < n; i++) {
2627
+ if (slotsAttemptedOut !== void 0) {
2628
+ slotsAttemptedOut.count = i + 1;
2629
+ }
2630
+ const candidate = tryX25519Slot({
2631
+ slot: envelope.slots[i],
2632
+ recipientSecretKey,
2633
+ pubRLocal,
2634
+ liveSlot: cek === null
2635
+ });
2636
+ if (cek === null && candidate !== null) {
2637
+ cek = candidate;
2638
+ matchedSlotIdx = i;
2639
+ }
2640
+ if (cek !== null && !constantTimeN) break;
2641
+ }
2642
+ } else {
2643
+ for (let i = 0; i < n; i++) {
2644
+ if (slotsAttemptedOut !== void 0) {
2645
+ slotsAttemptedOut.count = i + 1;
2646
+ }
2647
+ const candidate = tryMlkem768X25519Slot({
2648
+ slot: envelope.slots[i],
2649
+ recipientSecretKey,
2650
+ liveSlot: cek === null
2651
+ });
2652
+ if (cek === null && candidate !== null) {
2653
+ cek = candidate;
2654
+ matchedSlotIdx = i;
2655
+ }
2656
+ if (cek !== null && !constantTimeN) break;
2657
+ }
2658
+ }
2659
+ return cek === null ? null : { cek, slotIdx: matchedSlotIdx };
2660
+ }
2661
+ function tryRecipientUnwrap(envelope, recipientSecretKey, constantTimeN, slotsAttemptedOut) {
2662
+ return tryRecipientUnwrapWithIdx(envelope, recipientSecretKey, constantTimeN, slotsAttemptedOut)?.cek ?? null;
2663
+ }
2664
+ function slotsMacCborBytes(envelope) {
2665
+ return slotsToMacCbor(
2666
+ envelope.slots,
2667
+ envelope.kem
2668
+ );
2669
+ }
2670
+ function eciesSealedPoeUnwrap(args) {
2671
+ const { envelope, ciphertext } = args;
2672
+ const constantTimeN = args.constantTimeN ?? true;
2673
+ const hasSingle = "recipientSecretKey" in args;
2674
+ const hasBundle = "recipientKeyBundle" in args;
2675
+ const multiPrivKeys = hasBundle ? selectBundleSecrets(envelope, args.recipientKeyBundle) : "recipientSecretKeys" in args ? args.recipientSecretKeys : void 0;
2676
+ const hasMulti = multiPrivKeys !== void 0;
2677
+ if (hasSingle === hasMulti) {
2678
+ throw new EciesSealedPoeError(
2679
+ "INVALID_RECIPIENT_KEY",
2680
+ "exactly one of recipientSecretKey / recipientSecretKeys / recipientKeyBundle MUST be supplied"
2681
+ );
2682
+ }
2683
+ if (hasMulti && multiPrivKeys.length === 0) {
2684
+ if (hasBundle) {
2685
+ return { matched: false, reason: "WRONG_RECIPIENT_KEY" };
2686
+ }
2687
+ throw new EciesSealedPoeError(
2688
+ "INVALID_RECIPIENT_KEY",
2689
+ "recipientSecretKeys MUST be a non-empty array, got length=0"
2690
+ );
2691
+ }
2692
+ if (hasMulti) {
2693
+ assertEnvelopeStructure(envelope, multiPrivKeys, void 0);
2694
+ } else {
2695
+ assertEnvelopeStructure(envelope, void 0, args.recipientSecretKey);
2696
+ }
2697
+ let matchedCek = null;
2698
+ let anyCandidateRecovered = false;
2699
+ if (hasSingle) {
2700
+ const recipientSecretKey = args.recipientSecretKey;
2701
+ const cek = tryRecipientUnwrap(
2702
+ envelope,
2703
+ recipientSecretKey,
2704
+ constantTimeN,
2705
+ args._slotsAttemptedOut
2706
+ );
2707
+ if (cek === null) {
2708
+ return { matched: false, reason: "WRONG_RECIPIENT_KEY" };
2709
+ }
2710
+ const slotsCbor = slotsMacCborBytes(envelope);
2711
+ const hmacKey = hkdfSha256({
2712
+ ikm: cek,
2713
+ salt: EMPTY_SALT2,
2714
+ info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
2715
+ length: 32
2716
+ });
2717
+ const slotsMacCalc = hmac(sha256, hmacKey, slotsCbor);
2718
+ if (!compareCt2(slotsMacCalc, envelope.slots_mac)) {
2719
+ return { matched: false, reason: "TAMPERED_HEADER" };
2720
+ }
2721
+ matchedCek = cek;
2722
+ } else {
2723
+ const slotsCbor = slotsMacCborBytes(envelope);
2724
+ const recipientSecretKeys = multiPrivKeys;
2725
+ for (let k = 0; k < recipientSecretKeys.length; k++) {
2726
+ if (args._privsAttemptedOut !== void 0) {
2727
+ args._privsAttemptedOut.count = k + 1;
2728
+ }
2729
+ if (args._slotsAttemptedOut !== void 0) {
2730
+ args._slotsAttemptedOut.count = 0;
2731
+ }
2732
+ const cek = tryRecipientUnwrap(
2733
+ envelope,
2734
+ recipientSecretKeys[k],
2735
+ constantTimeN,
2736
+ args._slotsAttemptedOut
2737
+ );
2738
+ if (args._slotsAttemptedOut?.perPrivCounts !== void 0) {
2739
+ args._slotsAttemptedOut.perPrivCounts.push(args._slotsAttemptedOut.count);
2740
+ }
2741
+ if (cek === null) continue;
2742
+ const hmacKey = hkdfSha256({
2743
+ ikm: cek,
2744
+ salt: EMPTY_SALT2,
2745
+ info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
2746
+ length: 32
2747
+ });
2748
+ const slotsMacCalc = hmac(sha256, hmacKey, slotsCbor);
2749
+ if (compareCt2(slotsMacCalc, envelope.slots_mac)) {
2750
+ matchedCek = cek;
2751
+ break;
2752
+ }
2753
+ anyCandidateRecovered = true;
2754
+ }
2755
+ if (matchedCek === null) {
2756
+ return {
2757
+ matched: false,
2758
+ reason: anyCandidateRecovered ? "TAMPERED_HEADER" : "WRONG_RECIPIENT_KEY"
2759
+ };
2760
+ }
2761
+ }
2762
+ const adContent = concat2(envelope.nonce, envelope.slots_mac);
2763
+ try {
2764
+ const plaintext = xchacha20Poly1305Decrypt2({
2765
+ key: matchedCek,
2766
+ nonce: envelope.nonce,
2767
+ aad: adContent,
2768
+ ciphertext
2769
+ });
2770
+ return { matched: true, plaintext };
2771
+ } catch (e) {
2772
+ if (!(e instanceof AeadVerificationError2)) throw e;
2773
+ return { matched: false, reason: "TAMPERED_CIPHERTEXT" };
2774
+ }
2775
+ }
2776
+ function sealedEnvelopeFromParsed(enc) {
2777
+ if (enc.scheme !== 1 || enc.aead !== "xchacha20-poly1305") return null;
2778
+ if (enc.nonce === void 0 || enc.slots_mac === void 0) return null;
2779
+ const slots = enc.slots;
2780
+ if (slots === void 0 || slots.length < 1) return null;
2781
+ if (enc.kem === "x25519") {
2782
+ const x25519Slots = [];
2783
+ for (const s of slots) {
2784
+ if (s.epk === void 0 || s.wrap === void 0) return null;
2785
+ x25519Slots.push({ epk: s.epk, wrap: s.wrap });
2786
+ }
2787
+ return {
2788
+ scheme: 1,
2789
+ aead: "xchacha20-poly1305",
2790
+ kem: "x25519",
2791
+ nonce: enc.nonce,
2792
+ slots: x25519Slots,
2793
+ slots_mac: enc.slots_mac
2794
+ };
2795
+ }
2796
+ if (enc.kem === "mlkem768x25519") {
2797
+ const hybridSlots = [];
2798
+ for (const s of slots) {
2799
+ if (s.kem_ct === void 0 || s.wrap === void 0) return null;
2800
+ hybridSlots.push({ kem_ct: s.kem_ct, wrap: s.wrap });
2801
+ }
2802
+ return {
2803
+ scheme: 1,
2804
+ aead: "xchacha20-poly1305",
2805
+ kem: "mlkem768x25519",
2806
+ nonce: enc.nonce,
2807
+ slots: hybridSlots,
2808
+ slots_mac: enc.slots_mac
2809
+ };
2810
+ }
2811
+ return null;
2812
+ }
2813
+
2814
+ // ../crypto-core/dist/util.js
2815
+ function compareCt3(a, b) {
2816
+ if (a.length !== b.length) return false;
2817
+ let diff = 0;
2818
+ for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
2819
+ return diff === 0;
2820
+ }
2821
+
2822
+ // src/fetch/fetch-outbound.ts
2823
+ var DENY_HOSTS_DEFAULT = ["localhost", "127.0.0.1"];
2824
+ var DenyHostError = class extends Error {
2825
+ code = "SERVICE_INDEPENDENCE_VIOLATION";
2826
+ host;
2827
+ url;
2828
+ constructor(host, url) {
2829
+ super(`SERVICE_INDEPENDENCE_VIOLATION: host "${host}" is in denyHosts (url=${url})`);
2830
+ this.name = "DenyHostError";
2831
+ this.host = host;
2832
+ this.url = url;
2833
+ }
2834
+ };
2835
+ var UnsupportedProtocolError = class extends Error {
2836
+ code = "UNSUPPORTED_PROTOCOL";
2837
+ protocol;
2838
+ url;
2839
+ constructor(protocol, url) {
2840
+ super(`UNSUPPORTED_PROTOCOL: "${protocol}" not in {http:, https:} (url=${url})`);
2841
+ this.name = "UnsupportedProtocolError";
2842
+ this.protocol = protocol;
2843
+ this.url = url;
2844
+ }
2845
+ };
2846
+ var UnsupportedMethodError = class extends Error {
2847
+ code = "UNSUPPORTED_METHOD";
2848
+ method;
2849
+ url;
2850
+ constructor(method, url) {
2851
+ super(`UNSUPPORTED_METHOD: "${method}" not in {GET, POST} (url=${url})`);
2852
+ this.name = "UnsupportedMethodError";
2853
+ this.method = method;
2854
+ this.url = url;
2855
+ }
2856
+ };
2857
+ var BodyTooLargeError = class extends Error {
2858
+ code = "OUTBOUND_BODY_TOO_LARGE";
2859
+ url;
2860
+ limitBytes;
2861
+ constructor(url, limitBytes) {
2862
+ super(`OUTBOUND_BODY_TOO_LARGE: response exceeded ${limitBytes} bytes (url=${url})`);
2863
+ this.name = "BodyTooLargeError";
2864
+ this.url = url;
2865
+ this.limitBytes = limitBytes;
2866
+ }
2867
+ };
2868
+ var OutboundExhaustedError = class extends Error {
2869
+ code = "OUTBOUND_EXHAUSTED";
2870
+ url;
2871
+ attempts;
2872
+ lastStatus;
2873
+ lastError;
2874
+ constructor(args) {
2875
+ super(
2876
+ `OUTBOUND_EXHAUSTED: ${args.attempts} attempts exhausted (url=${args.url}, lastStatus=${args.lastStatus ?? "-"})`
2877
+ );
2878
+ this.name = "OutboundExhaustedError";
2879
+ this.url = args.url;
2880
+ this.attempts = args.attempts;
2881
+ this.lastStatus = args.lastStatus;
2882
+ this.lastError = args.lastError;
2883
+ }
2884
+ };
2885
+ var DEFAULT_TIMEOUT_MS = 1e4;
2886
+ var DEFAULT_OUTBOUND_MAX_BYTES = 64 * 1024 * 1024;
2887
+ var DEFAULT_RETRYABLE_STATUSES = [502, 503, 504];
2888
+ var BACKOFF_BASE_MS = [1e3, 2e3, 4e3];
2889
+ var JITTER_RATIO = 0.25;
2890
+ function canonicaliseHost(host) {
2891
+ return host.replace(/^\[/, "").replace(/\]$/, "").replace(/\.$/, "").toLowerCase();
2892
+ }
2893
+ function matchesDenyList(host, denyHosts) {
2894
+ const h = canonicaliseHost(host);
2895
+ for (const raw of denyHosts) {
2896
+ const pattern = raw.replace(/\.$/, "").toLowerCase();
2897
+ if (pattern.startsWith("*.")) {
2898
+ const suffix = pattern.slice(2);
2899
+ if (h.endsWith("." + suffix)) return true;
2900
+ continue;
2901
+ }
2902
+ if (h === pattern) return true;
2903
+ if (pattern === "localhost") {
2904
+ if (h === "::1" || h === "0.0.0.0" || h === "169.254.169.254") return true;
2905
+ }
2906
+ if (pattern === "127.0.0.1") {
2907
+ if (/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(h)) return true;
2908
+ }
2909
+ }
2910
+ return false;
2911
+ }
2912
+ function parseProtocol(url) {
2913
+ try {
2914
+ return new URL(url).protocol;
2915
+ } catch {
2916
+ return null;
2917
+ }
2918
+ }
2919
+ function isAllowedMethod(method) {
2920
+ return method === "GET" || method === "POST";
2921
+ }
2922
+ function backoffJitteredMs(attemptIndex) {
2923
+ const idx = Math.min(attemptIndex, BACKOFF_BASE_MS.length - 1);
2924
+ const base = BACKOFF_BASE_MS[idx] ?? BACKOFF_BASE_MS[BACKOFF_BASE_MS.length - 1];
2925
+ const jitter = 1 + (Math.random() - 0.5) * 2 * JITTER_RATIO;
2926
+ return base * jitter;
2927
+ }
2928
+ function sleep(ms) {
2929
+ return new Promise((resolve) => {
2930
+ setTimeout(resolve, ms);
2931
+ });
2932
+ }
2933
+ var defaultFetchOutbound = async (url, opts2) => {
2934
+ const t0 = Date.now();
2935
+ const maxBytes = opts2.maxBytes ?? DEFAULT_OUTBOUND_MAX_BYTES;
2936
+ const controller = new AbortController();
2937
+ const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);
2938
+ const init = {
2939
+ method: opts2.method,
2940
+ signal: controller.signal
2941
+ };
2942
+ if (opts2.headers) init.headers = { ...opts2.headers };
2943
+ if (opts2.body !== void 0) init.body = opts2.body;
2944
+ try {
2945
+ const res = await fetch(url, init);
2946
+ const declared = res.headers.get("content-length");
2947
+ if (declared !== null) {
2948
+ const declaredLen = Number(declared);
2949
+ if (Number.isFinite(declaredLen) && declaredLen > maxBytes) {
2950
+ controller.abort();
2951
+ throw new BodyTooLargeError(url, maxBytes);
2952
+ }
2953
+ }
2954
+ const bytes = await readBodyCapped(res, url, maxBytes, controller);
2955
+ return { status: res.status, bytes, durationMs: Date.now() - t0 };
2956
+ } finally {
2957
+ clearTimeout(timeout);
2958
+ }
2959
+ };
2960
+ async function readBodyCapped(res, url, maxBytes, controller) {
2961
+ const body = res.body;
2962
+ if (body === null) {
2963
+ const buf = await res.arrayBuffer();
2964
+ if (buf.byteLength > maxBytes) {
2965
+ throw new BodyTooLargeError(url, maxBytes);
2966
+ }
2967
+ return new Uint8Array(buf);
2968
+ }
2969
+ const reader = body.getReader();
2970
+ const chunks = [];
2971
+ let total = 0;
2972
+ try {
2973
+ for (; ; ) {
2974
+ const { done, value } = await reader.read();
2975
+ if (done) break;
2976
+ if (value === void 0) continue;
2977
+ total += value.byteLength;
2978
+ if (total > maxBytes) {
2979
+ controller.abort();
2980
+ throw new BodyTooLargeError(url, maxBytes);
2981
+ }
2982
+ chunks.push(value);
2983
+ }
2984
+ } finally {
2985
+ reader.releaseLock();
2986
+ }
2987
+ const out = new Uint8Array(total);
2988
+ let offset = 0;
2989
+ for (const chunk of chunks) {
2990
+ out.set(chunk, offset);
2991
+ offset += chunk.byteLength;
2992
+ }
2993
+ return out;
2994
+ }
2995
+ function wrapFetchOutbound(inner, audit, config = void 0) {
2996
+ const normConfig = config === void 0 ? {} : Array.isArray(config) ? { denyHosts: config } : config;
2997
+ const denyHosts = normConfig.denyHosts ?? [];
2998
+ const retries = normConfig.retries ?? 0;
2999
+ const retryableStatuses = normConfig.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES;
3000
+ return async (url, opts2) => {
3001
+ if (opts2.purpose === "webhook") {
3002
+ audit.push({
3003
+ url,
3004
+ method: "GET",
3005
+ status: 0,
3006
+ bytes: 0,
3007
+ duration_ms: 0,
3008
+ purpose: opts2.purpose
3009
+ });
3010
+ throw new Error(
3011
+ `webhook purpose must be sent via fetchWebhook, not fetchOutbound (url=${url})`
3012
+ );
3013
+ }
3014
+ const protocol = parseProtocol(url);
3015
+ if (protocol !== "http:" && protocol !== "https:") {
3016
+ audit.push({
3017
+ url,
3018
+ method: "GET",
3019
+ status: 0,
3020
+ bytes: 0,
3021
+ duration_ms: 0,
3022
+ purpose: opts2.purpose
3023
+ });
3024
+ throw new UnsupportedProtocolError(protocol ?? "", url);
3025
+ }
3026
+ if (!isAllowedMethod(opts2.method)) {
3027
+ audit.push({
3028
+ url,
3029
+ method: "GET",
3030
+ status: 0,
3031
+ bytes: 0,
3032
+ duration_ms: 0,
3033
+ purpose: opts2.purpose
3034
+ });
3035
+ throw new UnsupportedMethodError(opts2.method, url);
3036
+ }
3037
+ if (denyHosts.length > 0) {
3038
+ const host = new URL(url).hostname;
3039
+ if (matchesDenyList(host, denyHosts)) {
3040
+ audit.push({
3041
+ url,
3042
+ method: opts2.method,
3043
+ status: 0,
3044
+ bytes: 0,
3045
+ duration_ms: 0,
3046
+ purpose: opts2.purpose
3047
+ });
3048
+ throw new DenyHostError(canonicaliseHost(host), url);
3049
+ }
3050
+ }
3051
+ let lastStatus;
3052
+ let lastError;
3053
+ const totalAttempts = retries + 1;
3054
+ for (let attempt = 1; attempt <= totalAttempts; attempt++) {
3055
+ const t0 = Date.now();
3056
+ try {
3057
+ const result = await inner(url, opts2);
3058
+ audit.push({
3059
+ url,
3060
+ method: opts2.method,
3061
+ status: result.status,
3062
+ bytes: result.bytes.byteLength,
3063
+ duration_ms: result.durationMs,
3064
+ purpose: opts2.purpose
3065
+ });
3066
+ if (retryableStatuses.includes(result.status) && retries > 0) {
3067
+ lastStatus = result.status;
3068
+ if (attempt < totalAttempts) {
3069
+ await sleep(backoffJitteredMs(attempt - 1));
3070
+ continue;
3071
+ }
3072
+ break;
3073
+ }
3074
+ return result;
3075
+ } catch (e) {
3076
+ const durationMs = Date.now() - t0;
3077
+ if (e instanceof DenyHostError || e instanceof UnsupportedProtocolError || e instanceof UnsupportedMethodError) {
3078
+ audit.push({
3079
+ url,
3080
+ method: opts2.method,
3081
+ status: 0,
3082
+ bytes: 0,
3083
+ duration_ms: durationMs,
3084
+ purpose: opts2.purpose
3085
+ });
3086
+ throw e;
3087
+ }
3088
+ audit.push({
3089
+ url,
3090
+ method: opts2.method,
3091
+ status: 0,
3092
+ bytes: 0,
3093
+ duration_ms: durationMs,
3094
+ purpose: opts2.purpose
3095
+ });
3096
+ lastError = e;
3097
+ if (attempt < totalAttempts) {
3098
+ await sleep(backoffJitteredMs(attempt - 1));
3099
+ continue;
3100
+ }
3101
+ break;
3102
+ }
3103
+ }
3104
+ if (retries === 0 && lastError !== void 0) {
3105
+ throw lastError;
3106
+ }
3107
+ throw new OutboundExhaustedError({ url, attempts: totalAttempts, lastStatus, lastError });
3108
+ };
3109
+ }
3110
+ async function fetchOutbound(url, opts2, audit, config = {}) {
3111
+ const wrapped = wrapFetchOutbound(defaultFetchOutbound, audit, config);
3112
+ return wrapped(url, opts2);
3113
+ }
3114
+
3115
+ // src/verifier/fetch.ts
3116
+ var ARWEAVE_DEFAULTS = [
3117
+ "https://arweave.net",
3118
+ "https://ar-io.net",
3119
+ "https://g8way.io"
3120
+ ];
3121
+ var ARWEAVE_TXID_RE = /^[A-Za-z0-9_-]{43}$/;
3122
+ async function fetchItemCiphertext(args) {
3123
+ const reconstructed = args.uris.map((chunks) => chunks.join(""));
3124
+ const candidate = reconstructed.find((u) => /^(ar|ipfs):\/\//.test(u));
3125
+ if (candidate === void 0) {
3126
+ for (const u of reconstructed) {
3127
+ args.uriChecksOut.push({
3128
+ item_index: args.itemIndex,
3129
+ uri: u,
3130
+ ok: false,
3131
+ reason: "URI_TARGET_FORBIDDEN"
3132
+ });
3133
+ }
3134
+ throw new Error("URI_TARGET_FORBIDDEN");
3135
+ }
3136
+ if (candidate.startsWith("ar://")) {
3137
+ const txid = candidate.slice(5);
3138
+ if (!ARWEAVE_TXID_RE.test(txid)) {
3139
+ args.uriChecksOut.push({
3140
+ item_index: args.itemIndex,
3141
+ uri: candidate,
3142
+ ok: false,
3143
+ reason: "INVALID_URI"
3144
+ });
3145
+ throw new Error("CONTENT_UNAVAILABLE");
3146
+ }
3147
+ const gateways = args.arweaveGateways && args.arweaveGateways.length > 0 ? args.arweaveGateways : ARWEAVE_DEFAULTS;
3148
+ for (const gw of gateways) {
3149
+ try {
3150
+ const res = await args.fetchFn(`${gw}/${txid}`, { method: "GET", purpose: "arweave" });
3151
+ if (res.status === 200) {
3152
+ args.uriChecksOut.push({ item_index: args.itemIndex, uri: candidate, ok: true });
3153
+ return res.bytes;
3154
+ }
3155
+ args.uriChecksOut.push({
3156
+ item_index: args.itemIndex,
3157
+ uri: candidate,
3158
+ ok: false,
3159
+ reason: `URI_FETCH_FAILED:${gw}:${res.status}`
3160
+ });
3161
+ } catch (e) {
3162
+ args.uriChecksOut.push({
3163
+ item_index: args.itemIndex,
3164
+ uri: candidate,
3165
+ ok: false,
3166
+ reason: `URI_FETCH_FAILED:${gw}:${e instanceof Error ? e.message : String(e)}`
3167
+ });
3168
+ }
3169
+ }
3170
+ throw new Error("CONTENT_UNAVAILABLE");
3171
+ }
3172
+ const cidPart = candidate.slice("ipfs://".length);
3173
+ const ipfsCid = cidPart.split("/")[0] ?? cidPart;
3174
+ const ipfsGateways = args.ipfsGateways;
3175
+ if (ipfsGateways === void 0 || ipfsGateways.length === 0) {
3176
+ args.uriChecksOut.push({
3177
+ item_index: args.itemIndex,
3178
+ uri: candidate,
3179
+ ok: false,
3180
+ reason: "CONTENT_UNAVAILABLE:no_ipfs_gateway"
3181
+ });
3182
+ throw new Error("CONTENT_UNAVAILABLE");
3183
+ }
3184
+ for (const gw of ipfsGateways) {
3185
+ try {
3186
+ const sep = gw.endsWith("/") ? "" : "/";
3187
+ const url = `${gw}${sep}ipfs/${ipfsCid}`;
3188
+ const res = await args.fetchFn(url, { method: "GET", purpose: "ipfs" });
3189
+ if (res.status === 200) {
3190
+ args.uriChecksOut.push({ item_index: args.itemIndex, uri: candidate, ok: true });
3191
+ return res.bytes;
3192
+ }
3193
+ args.uriChecksOut.push({
3194
+ item_index: args.itemIndex,
3195
+ uri: candidate,
3196
+ ok: false,
3197
+ reason: `URI_FETCH_FAILED:${gw}:${res.status}`
3198
+ });
3199
+ } catch (e) {
3200
+ args.uriChecksOut.push({
3201
+ item_index: args.itemIndex,
3202
+ uri: candidate,
3203
+ ok: false,
3204
+ reason: `URI_FETCH_FAILED:${gw}:${e instanceof Error ? e.message : String(e)}`
3205
+ });
3206
+ }
3207
+ }
3208
+ throw new Error("CONTENT_UNAVAILABLE");
3209
+ }
3210
+
3211
+ // src/verifier/decrypt.ts
3212
+ var PASSPHRASE_KDF_ARGON2ID = "argon2id";
3213
+ var EMPTY_AAD = new Uint8Array(0);
3214
+ async function tryDecryptions(args) {
3215
+ const { record, input } = args;
3216
+ const items = record.items ?? [];
3217
+ const out = [];
3218
+ const reqs = input.decryption ?? [];
3219
+ for (const req of reqs) {
3220
+ const idx = req.itemIndex;
3221
+ if (!Number.isInteger(idx) || idx < 0 || idx >= items.length) {
3222
+ out.push({
3223
+ item_index: idx,
3224
+ verdict: "no-enc-envelope",
3225
+ reason: "itemIndex out of range"
3226
+ });
3227
+ continue;
3228
+ }
3229
+ const item = items[idx];
3230
+ const enc = item.enc;
3231
+ if (enc === void 0 || enc === null || typeof enc !== "object") {
3232
+ out.push({ item_index: idx, verdict: "no-enc-envelope" });
3233
+ continue;
3234
+ }
3235
+ const encShape = enc;
3236
+ const hasSlots = Array.isArray(encShape.slots);
3237
+ const hasPassphrase = encShape.passphrase !== void 0 && encShape.passphrase !== null;
3238
+ const reqHasSecret = "recipientSecretKey" in req;
3239
+ const reqHasPassphrase = "passphrase" in req;
3240
+ if (hasSlots && !reqHasSecret) {
3241
+ out.push({
3242
+ item_index: idx,
3243
+ verdict: "wrong-input-shape",
3244
+ reason: "WRONG_DECRYPTION_INPUT_SHAPE"
3245
+ });
3246
+ continue;
3247
+ }
3248
+ if (hasPassphrase && !reqHasPassphrase) {
3249
+ out.push({
3250
+ item_index: idx,
3251
+ verdict: "wrong-input-shape",
3252
+ reason: "WRONG_DECRYPTION_INPUT_SHAPE"
3253
+ });
3254
+ continue;
3255
+ }
3256
+ const oobBytes = input.ciphertextBytes?.[idx];
3257
+ let ciphertext;
3258
+ if (oobBytes !== void 0) {
3259
+ ciphertext = oobBytes;
3260
+ } else if (args.allowUriFetch && Array.isArray(item.uris) && item.uris.length > 0) {
3261
+ try {
3262
+ ciphertext = await fetchItemCiphertext({
3263
+ uris: item.uris,
3264
+ arweaveGateways: input.arweaveGatewayChain,
3265
+ ipfsGateways: input.ipfsGatewayChain,
3266
+ fetchFn: args.fetchFn,
3267
+ uriChecksOut: args.uriChecksOut,
3268
+ itemIndex: idx
3269
+ });
3270
+ } catch (e) {
3271
+ const code = e instanceof Error ? e.message : "CONTENT_UNAVAILABLE";
3272
+ const verdict = code === "URI_TARGET_FORBIDDEN" ? "ciphertext-unavailable" : "content-unavailable";
3273
+ out.push({ item_index: idx, verdict, reason: code });
3274
+ continue;
3275
+ }
3276
+ } else {
3277
+ out.push({
3278
+ item_index: idx,
3279
+ verdict: "ciphertext-unavailable",
3280
+ reason: "CIPHERTEXT_UNAVAILABLE"
3281
+ });
3282
+ continue;
3283
+ }
3284
+ if (ciphertext === null) {
3285
+ out.push({
3286
+ item_index: idx,
3287
+ verdict: "ciphertext-unavailable",
3288
+ reason: "CIPHERTEXT_UNAVAILABLE"
3289
+ });
3290
+ continue;
3291
+ }
3292
+ let plaintext = null;
3293
+ let failure = null;
3294
+ if (reqHasSecret) {
3295
+ const envelope = sealedEnvelopeFromParsed(enc);
3296
+ if (envelope === null) {
3297
+ out.push({
3298
+ item_index: idx,
3299
+ verdict: "wrong-input-shape",
3300
+ reason: "WRONG_DECRYPTION_INPUT_SHAPE"
3301
+ });
3302
+ continue;
3303
+ }
3304
+ const unwrap = eciesSealedPoeUnwrap({
3305
+ envelope,
3306
+ ciphertext,
3307
+ recipientSecretKey: req.recipientSecretKey
3308
+ });
3309
+ if (unwrap.matched) {
3310
+ plaintext = unwrap.plaintext;
3311
+ } else {
3312
+ const map = {
3313
+ WRONG_RECIPIENT_KEY: { verdict: "wrong-key", reason: "WRONG_RECIPIENT_KEY" },
3314
+ TAMPERED_HEADER: { verdict: "tampered-header", reason: "TAMPERED_HEADER" },
3315
+ TAMPERED_CIPHERTEXT: { verdict: "tampered-ciphertext", reason: "TAMPERED_CIPHERTEXT" }
3316
+ };
3317
+ failure = map[unwrap.reason] ?? {
3318
+ verdict: "tampered-ciphertext",
3319
+ reason: "TAMPERED_CIPHERTEXT"
3320
+ };
3321
+ }
3322
+ } else {
3323
+ try {
3324
+ plaintext = await decryptPassphrase({
3325
+ enc,
3326
+ ciphertext,
3327
+ passphrase: req.passphrase
3328
+ });
3329
+ } catch (e) {
3330
+ if (e instanceof AeadVerificationError) {
3331
+ failure = { verdict: "tampered-ciphertext", reason: "TAMPERED_CIPHERTEXT" };
3332
+ } else if (e instanceof Error && e.message.startsWith("KDF_")) {
3333
+ failure = { verdict: "kdf-failed", reason: e.message };
3334
+ } else {
3335
+ failure = {
3336
+ verdict: "tampered-ciphertext",
3337
+ reason: e instanceof Error ? e.message : "TAMPERED_CIPHERTEXT"
3338
+ };
3339
+ }
3340
+ }
3341
+ }
3342
+ if (failure !== null) {
3343
+ out.push({ item_index: idx, verdict: failure.verdict, reason: failure.reason });
3344
+ continue;
3345
+ }
3346
+ if (plaintext === null) {
3347
+ out.push({ item_index: idx, verdict: "tampered-ciphertext", reason: "TAMPERED_CIPHERTEXT" });
3348
+ continue;
3349
+ }
3350
+ const plaintextHashOk = recomputeHashes(item, plaintext);
3351
+ out.push({ item_index: idx, verdict: "decrypted", plaintext_hash_ok: plaintextHashOk });
3352
+ }
3353
+ return { results: out };
3354
+ }
3355
+ async function decryptPassphrase(args) {
3356
+ const { enc, ciphertext, passphrase } = args;
3357
+ if (enc.passphrase.alg !== PASSPHRASE_KDF_ARGON2ID) {
3358
+ throw new Error(`KDF_DERIVATION_FAILED: unsupported passphrase alg ${enc.passphrase.alg}`);
3359
+ }
3360
+ const normalised = passphrase.normalize("NFKC").replace(/\s+/g, " ").trim();
3361
+ const password = new TextEncoder().encode(normalised);
3362
+ let cek;
3363
+ try {
3364
+ cek = await argon2idV13({
3365
+ password,
3366
+ salt: enc.passphrase.salt,
3367
+ memSizeKB: enc.passphrase.params.m,
3368
+ iterations: enc.passphrase.params.t,
3369
+ parallelism: enc.passphrase.params.p,
3370
+ outBytes: 32
3371
+ });
3372
+ } catch (cause) {
3373
+ const reason = cause instanceof Error ? cause.message : String(cause);
3374
+ throw new Error(`KDF_DERIVATION_FAILED: ${reason}`, { cause });
3375
+ }
3376
+ if (enc.aead !== "xchacha20-poly1305") {
3377
+ throw new Error(`KDF_DERIVATION_FAILED: unsupported aead ${enc.aead}`);
3378
+ }
3379
+ return xchacha20Poly1305Decrypt({
3380
+ key: cek,
3381
+ nonce: enc.nonce,
3382
+ aad: EMPTY_AAD,
3383
+ ciphertext
3384
+ });
3385
+ }
3386
+ function recomputeHashes(item, plaintext) {
3387
+ const entries = Object.entries(item.hashes);
3388
+ if (entries.length === 0) return false;
3389
+ for (const [alg, digest] of entries) {
3390
+ if (alg === "sha2-256") {
3391
+ if (!compareCt3(sha2562(plaintext), digest)) return false;
3392
+ } else if (alg === "blake2b-256") {
3393
+ if (!compareCt3(blake2b256(plaintext), digest)) return false;
3394
+ } else {
3395
+ return false;
3396
+ }
3397
+ }
3398
+ return true;
3399
+ }
3400
+ var CanonicalCborError3 = class extends Error {
3401
+ code;
3402
+ constructor(code, message, options) {
3403
+ super(message, options);
3404
+ this.name = "CanonicalCborError";
3405
+ this.code = code;
3406
+ }
3407
+ };
3408
+ function decodeCanonicalCbor3(bytes) {
3409
+ try {
3410
+ return decode(bytes, {
3411
+ ...cdeDecodeOptions,
3412
+ rejectStreaming: true,
3413
+ rejectDuplicateKeys: true,
3414
+ // A CIP-309 record carries integers, byte/text strings, arrays, maps and
3415
+ // `null` — and nothing else. Without these rejections the major-type-7
3416
+ // surface leaks into the decoder: a float16/32/64 that happens to hold an
3417
+ // integral value (e.g. 1.0) silently decodes to the integer 1 and passes
3418
+ // a `z.literal(1)` / Number.isInteger schema check, so two byte strings
3419
+ // that are NOT byte-identical canonicalise to the same record. That
3420
+ // breaks the cross-implementation parity invariant (the Python twin
3421
+ // already rejects non-integer `v` / `enc.scheme` outright). Reject the
3422
+ // whole non-record surface — floats, negative zero, undefined, and
3423
+ // non-{true,false,null} simple values — so any such input surfaces as
3424
+ // MALFORMED_CBOR via mapDecodeError rather than decoding to a look-alike.
3425
+ rejectFloats: true,
3426
+ rejectNegativeZero: true,
3427
+ rejectUndefined: true,
3428
+ rejectSimple: true
3429
+ });
3430
+ } catch (cause) {
3431
+ throw mapDecodeError3(cause);
3432
+ }
3433
+ }
3434
+ function mapDecodeError3(cause) {
3435
+ const message = cause instanceof Error ? cause.message : String(cause);
3436
+ const lower = message.toLowerCase();
3437
+ const isIndefinite = lower.includes("streaming") || lower.includes("indefinite");
3438
+ const detail = isIndefinite ? `indefinite-length items are not permitted in canonical CBOR: ${message}` : message;
3439
+ return new CanonicalCborError3("MALFORMED_CBOR", `cbor decode failed: ${detail}`, { cause });
3440
+ }
3441
+ function compareCt4(a, b) {
3442
+ if (a.length !== b.length) return false;
3443
+ let diff = 0;
3444
+ for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
3445
+ return diff === 0;
3446
+ }
3447
+ var LEAF_PREFIX2 = 0;
3448
+ var NODE_PREFIX2 = 1;
3449
+ var DIGEST_LENGTH2 = 32;
3450
+ function validateLeaves2(leaves, fnName) {
3451
+ if (leaves.length === 0) {
3452
+ throw new Error(`${fnName}: empty leaf list (n == 0 is forbidden by RFC 9162 \xA72.1.1)`);
3453
+ }
3454
+ for (let i = 0; i < leaves.length; i++) {
3455
+ const leaf = leaves[i];
3456
+ if (!(leaf instanceof Uint8Array) || leaf.length !== DIGEST_LENGTH2) {
3457
+ throw new Error(
3458
+ `${fnName}: leaf[${i}] must be a Uint8Array(${DIGEST_LENGTH2}); got length ${leaf instanceof Uint8Array ? leaf.length : "non-Uint8Array"}`
3459
+ );
3460
+ }
3461
+ }
3462
+ }
3463
+ function merkleSha2256Root2(leaves) {
3464
+ validateLeaves2(leaves, "merkleSha2256Root");
3465
+ return mthRecursive2(leaves, 0, leaves.length);
3466
+ }
3467
+ function largestPow2Lt2(n) {
3468
+ let k = 1;
3469
+ while (k * 2 < n) k *= 2;
3470
+ return k;
3471
+ }
3472
+ function hashLeaf2(d) {
3473
+ const buf = new Uint8Array(1 + d.length);
3474
+ buf[0] = LEAF_PREFIX2;
3475
+ buf.set(d, 1);
3476
+ return sha256(buf);
3477
+ }
3478
+ function hashNode2(left, right) {
3479
+ const buf = new Uint8Array(1 + left.length + right.length);
3480
+ buf[0] = NODE_PREFIX2;
3481
+ buf.set(left, 1);
3482
+ buf.set(right, 1 + left.length);
3483
+ return sha256(buf);
3484
+ }
3485
+ function mthRecursive2(leaves, start, end) {
3486
+ const n = end - start;
3487
+ if (n === 1) {
3488
+ return hashLeaf2(leaves[start]);
3489
+ }
3490
+ const k = largestPow2Lt2(n);
3491
+ const left = mthRecursive2(leaves, start, start + k);
3492
+ const right = mthRecursive2(leaves, start + k, end);
3493
+ return hashNode2(left, right);
3494
+ }
3495
+ var LEAVES_LIST_FORMAT_V1 = "cardano-poe-merkle-leaves-v1";
3496
+ var TREE_ALG_RFC9162 = "rfc9162-sha256";
3497
+ var DIGEST_LENGTH22 = 32;
3498
+ var REGISTERED_FORMATS = /* @__PURE__ */ new Set([LEAVES_LIST_FORMAT_V1]);
3499
+ var MerkleLeavesListError = class extends Error {
3500
+ code;
3501
+ constructor(code, message) {
3502
+ super(message ? `${code}: ${message}` : code);
3503
+ this.code = code;
3504
+ this.name = "MerkleLeavesListError";
3505
+ }
3506
+ };
3507
+ function decodeLeavesList(bytes) {
3508
+ const decoded = decodeCanonicalCbor3(bytes);
3509
+ if (typeof decoded !== "object" || decoded === null || Array.isArray(decoded)) {
3510
+ throw new MerkleLeavesListError(
3511
+ "SCHEMA_MERKLE_LEAVES_MALFORMED",
3512
+ "leaves-list MUST be a CBOR map"
3513
+ );
3514
+ }
3515
+ const m = decoded;
3516
+ const format = m["format"];
3517
+ if (typeof format !== "string") {
3518
+ throw new MerkleLeavesListError(
3519
+ "SCHEMA_MERKLE_LEAVES_MALFORMED",
3520
+ "format must be a text string"
3521
+ );
3522
+ }
3523
+ if (!REGISTERED_FORMATS.has(format)) {
3524
+ throw new MerkleLeavesListError(
3525
+ "SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED",
3526
+ `format '${format}' is not in the registered set`
3527
+ );
3528
+ }
3529
+ const treeAlg = m["tree_alg"];
3530
+ if (treeAlg !== TREE_ALG_RFC9162) {
3531
+ throw new MerkleLeavesListError(
3532
+ "SCHEMA_MERKLE_LEAVES_MALFORMED",
3533
+ `tree_alg '${String(treeAlg)}' is not '${TREE_ALG_RFC9162}'`
3534
+ );
3535
+ }
3536
+ const root = m["root"];
3537
+ if (!(root instanceof Uint8Array) || root.length !== DIGEST_LENGTH22) {
3538
+ throw new MerkleLeavesListError(
3539
+ "SCHEMA_MERKLE_LEAVES_MALFORMED",
3540
+ `root must be a ${DIGEST_LENGTH22}-byte byte string`
3541
+ );
3542
+ }
3543
+ const leavesRaw = m["leaves"];
3544
+ if (!Array.isArray(leavesRaw) || leavesRaw.length < 1) {
3545
+ throw new MerkleLeavesListError(
3546
+ "SCHEMA_MERKLE_LEAVES_MALFORMED",
3547
+ "leaves must be a non-empty array"
3548
+ );
3549
+ }
3550
+ const leaves = [];
3551
+ for (let i = 0; i < leavesRaw.length; i++) {
3552
+ const leaf = leavesRaw[i];
3553
+ if (!(leaf instanceof Uint8Array) || leaf.length !== DIGEST_LENGTH22) {
3554
+ throw new MerkleLeavesListError(
3555
+ "SCHEMA_MERKLE_LEAVES_MALFORMED",
3556
+ `leaves[${i}] must be a ${DIGEST_LENGTH22}-byte byte string`
3557
+ );
3558
+ }
3559
+ leaves.push(leaf);
3560
+ }
3561
+ const leafCountRaw = m["leaf_count"];
3562
+ let leafCount;
3563
+ if (typeof leafCountRaw === "number" && Number.isInteger(leafCountRaw) && leafCountRaw >= 0) {
3564
+ leafCount = leafCountRaw;
3565
+ } else if (typeof leafCountRaw === "bigint" && leafCountRaw >= 0n) {
3566
+ if (leafCountRaw > BigInt(Number.MAX_SAFE_INTEGER)) {
3567
+ throw new MerkleLeavesListError(
3568
+ "SCHEMA_MERKLE_LEAVES_MALFORMED",
3569
+ "leaf_count exceeds Number.MAX_SAFE_INTEGER"
3570
+ );
3571
+ }
3572
+ leafCount = Number(leafCountRaw);
3573
+ } else {
3574
+ throw new MerkleLeavesListError(
3575
+ "SCHEMA_MERKLE_LEAVES_MALFORMED",
3576
+ "leaf_count must be a non-negative CBOR uint"
3577
+ );
3578
+ }
3579
+ if (leaves.length !== leafCount) {
3580
+ throw new MerkleLeavesListError(
3581
+ "SCHEMA_MERKLE_LEAF_COUNT_MISMATCH",
3582
+ `leaves.length (${leaves.length}) != leaf_count (${leafCount})`
3583
+ );
3584
+ }
3585
+ let leafAlg;
3586
+ if (m["leaf_alg"] !== void 0) {
3587
+ if (typeof m["leaf_alg"] !== "string") {
3588
+ throw new MerkleLeavesListError(
3589
+ "SCHEMA_MERKLE_LEAVES_MALFORMED",
3590
+ "leaf_alg must be a text string when present"
3591
+ );
3592
+ }
3593
+ leafAlg = m["leaf_alg"];
3594
+ }
3595
+ const recomputed = merkleSha2256Root2(leaves);
3596
+ if (!compareCt4(recomputed, root)) {
3597
+ throw new MerkleLeavesListError(
3598
+ "MERKLE_ROOT_MISMATCH",
3599
+ "leaves recompute does not match declared root"
3600
+ );
3601
+ }
3602
+ const out = {
3603
+ format: LEAVES_LIST_FORMAT_V1,
3604
+ treeAlg: TREE_ALG_RFC9162,
3605
+ root,
3606
+ leaves,
3607
+ leafCount,
3608
+ ...leafAlg !== void 0 ? { leafAlg } : {}
3609
+ };
3610
+ return out;
3611
+ }
3612
+
3613
+ // src/verifier/merkle.ts
3614
+ async function verifyMerkleCommitments(args) {
3615
+ const merkleArr = args.record.merkle ?? [];
3616
+ const out = [];
3617
+ for (let i = 0; i < merkleArr.length; i++) {
3618
+ out.push(await verifyOneCommit(i, merkleArr[i], args));
3619
+ }
3620
+ return { checks: out };
3621
+ }
3622
+ async function verifyOneCommit(index, commit, args) {
3623
+ if (commit.alg !== "rfc9162-sha256") {
3624
+ return {
3625
+ merkle_index: index,
3626
+ alg: commit.alg,
3627
+ verdict: "unsupported",
3628
+ reason: "UNSUPPORTED_MERKLE_COMMIT_ALG"
3629
+ };
3630
+ }
3631
+ let leavesBytes = args.input.merkleLeaves?.[index] ?? null;
3632
+ if (leavesBytes === null) {
3633
+ const uris = commit.uris;
3634
+ if (uris === void 0 || uris.length === 0) {
3635
+ return {
3636
+ merkle_index: index,
3637
+ alg: commit.alg,
3638
+ verdict: "unavailable",
3639
+ reason: "MERKLE_LEAVES_UNAVAILABLE"
3640
+ };
3641
+ }
3642
+ try {
3643
+ leavesBytes = await fetchItemCiphertext({
3644
+ uris,
3645
+ arweaveGateways: args.input.arweaveGatewayChain,
3646
+ ipfsGateways: args.input.ipfsGatewayChain,
3647
+ fetchFn: args.fetchFn,
3648
+ uriChecksOut: args.uriChecksOut,
3649
+ // Merkle commits are not item-indexed; reuse a sentinel index so
3650
+ // downstream UIs can distinguish them from item URIs.
3651
+ itemIndex: -1 - index
3652
+ });
3653
+ } catch {
3654
+ return {
3655
+ merkle_index: index,
3656
+ alg: commit.alg,
3657
+ verdict: "unavailable",
3658
+ reason: "MERKLE_LEAVES_UNAVAILABLE"
3659
+ };
3660
+ }
3661
+ }
3662
+ try {
3663
+ const decoded = decodeLeavesList(leavesBytes);
3664
+ const recomputed = merkleSha2256Root(decoded.leaves);
3665
+ if (!compareCt3(recomputed, commit.root)) {
3666
+ return {
3667
+ merkle_index: index,
3668
+ alg: commit.alg,
3669
+ verdict: "mismatch",
3670
+ reason: "MERKLE_ROOT_MISMATCH",
3671
+ root_recomputed: recomputed
3672
+ };
3673
+ }
3674
+ if (decoded.leafCount !== commit.leaf_count) {
3675
+ return {
3676
+ merkle_index: index,
3677
+ alg: commit.alg,
3678
+ verdict: "mismatch",
3679
+ reason: "SCHEMA_MERKLE_LEAF_COUNT_MISMATCH"
3680
+ };
3681
+ }
3682
+ return {
3683
+ merkle_index: index,
3684
+ alg: commit.alg,
3685
+ verdict: "valid",
3686
+ root_recomputed: recomputed
3687
+ };
3688
+ } catch (e) {
3689
+ if (e instanceof MerkleLeavesListError) {
3690
+ if (e.code === "SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED") {
3691
+ return {
3692
+ merkle_index: index,
3693
+ alg: commit.alg,
3694
+ verdict: "format-unsupported",
3695
+ reason: "SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED"
3696
+ };
3697
+ }
3698
+ if (e.code === "SCHEMA_MERKLE_LEAF_COUNT_MISMATCH") {
3699
+ return {
3700
+ merkle_index: index,
3701
+ alg: commit.alg,
3702
+ verdict: "mismatch",
3703
+ reason: "SCHEMA_MERKLE_LEAF_COUNT_MISMATCH"
3704
+ };
3705
+ }
3706
+ if (e.code === "MERKLE_ROOT_MISMATCH") {
3707
+ return {
3708
+ merkle_index: index,
3709
+ alg: commit.alg,
3710
+ verdict: "mismatch",
3711
+ reason: "MERKLE_ROOT_MISMATCH"
3712
+ };
3713
+ }
3714
+ return {
3715
+ merkle_index: index,
3716
+ alg: commit.alg,
3717
+ verdict: "unavailable",
3718
+ reason: e.code
3719
+ };
3720
+ }
3721
+ return {
3722
+ merkle_index: index,
3723
+ alg: commit.alg,
3724
+ verdict: "unavailable",
3725
+ reason: e instanceof Error ? e.message : String(e)
3726
+ };
3727
+ }
3728
+ }
3729
+
3730
+ // src/verifier/profile.ts
3731
+ var DEFAULT_PROFILE = "recipient-sealed";
3732
+ function profileImplements(actual, required) {
3733
+ return PROFILE_RANK[actual] >= PROFILE_RANK[required];
3734
+ }
3735
+ function planProfileSkips(profile, record) {
3736
+ const skips = [];
3737
+ const has = (k) => Object.prototype.hasOwnProperty.call(record, k);
3738
+ const verifySignatures = PROFILE_RANK[profile] >= PROFILE_RANK["signed"];
3739
+ const readsEnc = PROFILE_RANK[profile] >= PROFILE_RANK["sealed"];
3740
+ const verifyDecrypt = PROFILE_RANK[profile] >= PROFILE_RANK["recipient-sealed"];
3741
+ if (!verifySignatures && has("sigs")) {
3742
+ skips.push({
3743
+ code: "OUT_OF_PROFILE_SKIPPED",
3744
+ path: ["sigs"],
3745
+ message: `sigs[] requires profile >= 'signed'; active profile is '${profile}'`,
3746
+ severity: "info"
3747
+ });
3748
+ }
3749
+ if (!readsEnc && Array.isArray(record.items) && record.items.some((it) => it.enc !== void 0)) {
3750
+ skips.push({
3751
+ code: "OUT_OF_PROFILE_SKIPPED",
3752
+ path: ["items", "enc"],
3753
+ message: `items[].enc requires profile >= 'sealed'; active profile is '${profile}'`,
3754
+ severity: "info"
3755
+ });
3756
+ }
3757
+ return { skips, verifySignatures, verifyDecrypt };
3758
+ }
3759
+
3760
+ // src/verifier/cbor-walker.ts
3761
+ function readHead(bytes, pos) {
3762
+ if (pos >= bytes.length) {
3763
+ throw new RangeError("MALFORMED_CBOR: truncated input (no head byte)");
3764
+ }
3765
+ const head = bytes[pos];
3766
+ const mt = head >> 5;
3767
+ const ai = head & 31;
3768
+ let p = pos + 1;
3769
+ let valueU64;
3770
+ if (ai < 24) {
3771
+ valueU64 = ai;
3772
+ } else if (ai === 24) {
3773
+ if (p + 1 > bytes.length) {
3774
+ throw new RangeError("MALFORMED_CBOR: truncated 1-byte argument");
3775
+ }
3776
+ valueU64 = bytes[p];
3777
+ p += 1;
3778
+ } else if (ai === 25) {
3779
+ if (p + 2 > bytes.length) {
3780
+ throw new RangeError("MALFORMED_CBOR: truncated 2-byte argument");
3781
+ }
3782
+ valueU64 = bytes[p] << 8 | bytes[p + 1];
3783
+ p += 2;
3784
+ } else if (ai === 26) {
3785
+ if (p + 4 > bytes.length) {
3786
+ throw new RangeError("MALFORMED_CBOR: truncated 4-byte argument");
3787
+ }
3788
+ valueU64 = bytes[p] * 16777216 + (bytes[p + 1] << 16 | bytes[p + 2] << 8 | bytes[p + 3]);
3789
+ p += 4;
3790
+ } else if (ai === 27) {
3791
+ if (p + 8 > bytes.length) {
3792
+ throw new RangeError("MALFORMED_CBOR: truncated 8-byte argument");
3793
+ }
3794
+ let n = 0;
3795
+ for (let k = 0; k < 8; k++) n = n * 256 + bytes[p + k];
3796
+ if (n > Number.MAX_SAFE_INTEGER) {
3797
+ throw new RangeError("MALFORMED_CBOR: 8-byte argument exceeds JavaScript safe integer range");
3798
+ }
3799
+ valueU64 = n;
3800
+ p += 8;
3801
+ } else if (ai === 31) {
3802
+ throw new RangeError(
3803
+ "MALFORMED_CBOR: indefinite-length encoding (ai=31) not allowed under canonical CBOR"
3804
+ );
3805
+ } else {
3806
+ throw new RangeError(`MALFORMED_CBOR: reserved additional info ai=${ai}`);
3807
+ }
3808
+ return { mt, ai, payloadStart: p, valueU64 };
3809
+ }
3810
+ function skipCborItem(bytes, pos) {
3811
+ const h = readHead(bytes, pos);
3812
+ let p = h.payloadStart;
3813
+ switch (h.mt) {
3814
+ case 0:
3815
+ case 1:
3816
+ return p;
3817
+ case 2:
3818
+ case 3:
3819
+ if (p + h.valueU64 > bytes.length) {
3820
+ throw new RangeError(
3821
+ `MALFORMED_CBOR: truncated ${h.mt === 2 ? "byte" : "text"} string payload`
3822
+ );
3823
+ }
3824
+ return p + h.valueU64;
3825
+ case 4:
3826
+ for (let i = 0; i < h.valueU64; i++) p = skipCborItem(bytes, p);
3827
+ return p;
3828
+ case 5:
3829
+ for (let i = 0; i < h.valueU64 * 2; i++) p = skipCborItem(bytes, p);
3830
+ return p;
3831
+ case 6:
3832
+ return skipCborItem(bytes, p);
3833
+ case 7: {
3834
+ if (h.ai < 24) return p;
3835
+ if (h.ai === 24) {
3836
+ if (p + 1 > bytes.length) {
3837
+ throw new RangeError("MALFORMED_CBOR: truncated simple value");
3838
+ }
3839
+ return p + 1;
3840
+ }
3841
+ if (h.ai === 25 || h.ai === 26 || h.ai === 27) return p;
3842
+ throw new RangeError(`MALFORMED_CBOR: unsupported major-7 ai=${h.ai}`);
3843
+ }
3844
+ default:
3845
+ throw new RangeError(`MALFORMED_CBOR: unknown major type ${h.mt}`);
3846
+ }
3847
+ }
3848
+ var CARDANO_AUX_DATA_TAG = 259;
3849
+ var POE_LABEL = 309;
3850
+ function sliceTxComponents(txCbor) {
3851
+ const txHead = readHead(txCbor, 0);
3852
+ if (txHead.mt !== 4) {
3853
+ throw new RangeError(`MALFORMED_CBOR: tx CBOR is not a CBOR array (major type ${txHead.mt})`);
3854
+ }
3855
+ if (txHead.valueU64 < 4) {
3856
+ throw new RangeError(
3857
+ `MALFORMED_CBOR: tx CBOR array has ${txHead.valueU64} elements; expected >= 4 (post-Conway: [body, witness_set, is_valid, auxiliary_data])`
3858
+ );
3859
+ }
3860
+ const bodyStart = txHead.payloadStart;
3861
+ const bodyEnd = skipCborItem(txCbor, bodyStart);
3862
+ const witnessSetStart = bodyEnd;
3863
+ const witnessSetEnd = skipCborItem(txCbor, witnessSetStart);
3864
+ const pos = skipCborItem(txCbor, witnessSetEnd);
3865
+ const txBody = txCbor.slice(bodyStart, bodyEnd);
3866
+ const witnessSet = txCbor.slice(witnessSetStart, witnessSetEnd);
3867
+ if (pos >= txCbor.length) {
3868
+ throw new RangeError("MALFORMED_CBOR: truncated tx (auxiliary_data missing)");
3869
+ }
3870
+ const auxFirstByte = txCbor[pos];
3871
+ if (auxFirstByte === 246 || auxFirstByte === 247) {
3872
+ return { label309: null, txBody, witnessSet, auxMetadataLabels: [] };
3873
+ }
3874
+ let auxMapPos = pos;
3875
+ const auxHead = readHead(txCbor, pos);
3876
+ if (auxHead.mt === 6) {
3877
+ if (auxHead.valueU64 !== CARDANO_AUX_DATA_TAG) {
3878
+ throw new RangeError(
3879
+ `MALFORMED_CBOR: auxiliary_data carries unexpected CBOR tag ${auxHead.valueU64}; expected ${CARDANO_AUX_DATA_TAG} or bare map`
3880
+ );
3881
+ }
3882
+ auxMapPos = auxHead.payloadStart;
3883
+ }
3884
+ const mapHead = readHead(txCbor, auxMapPos);
3885
+ if (mapHead.mt !== 5) {
3886
+ throw new RangeError(
3887
+ `MALFORMED_CBOR: auxiliary_data is not a CBOR map (major type ${mapHead.mt})`
3888
+ );
3889
+ }
3890
+ let metadataMapPos;
3891
+ {
3892
+ let entryPos = mapHead.payloadStart;
3893
+ let sawAuxKey = false;
3894
+ let foundMetadataAt = null;
3895
+ for (let i = 0; i < mapHead.valueU64; i++) {
3896
+ const keyHead = readHead(txCbor, entryPos);
3897
+ if (keyHead.mt === 0 && keyHead.valueU64 <= 3) {
3898
+ sawAuxKey = true;
3899
+ if (keyHead.valueU64 === 0) {
3900
+ foundMetadataAt = keyHead.payloadStart;
3901
+ }
3902
+ }
3903
+ entryPos = skipCborItem(txCbor, entryPos);
3904
+ entryPos = skipCborItem(txCbor, entryPos);
3905
+ }
3906
+ if (sawAuxKey || auxHead.mt === 6) {
3907
+ metadataMapPos = foundMetadataAt;
3908
+ } else {
3909
+ metadataMapPos = auxMapPos;
3910
+ }
3911
+ }
3912
+ if (metadataMapPos === null) {
3913
+ return { label309: null, txBody, witnessSet, auxMetadataLabels: [] };
3914
+ }
3915
+ const metaHead = readHead(txCbor, metadataMapPos);
3916
+ if (metaHead.mt !== 5) {
3917
+ throw new RangeError(`MALFORMED_CBOR: metadata is not a CBOR map (major type ${metaHead.mt})`);
3918
+ }
3919
+ const labels = [];
3920
+ let label309 = null;
3921
+ let pairPos = metaHead.payloadStart;
3922
+ for (let i = 0; i < metaHead.valueU64; i++) {
3923
+ const keyHead = readHead(txCbor, pairPos);
3924
+ const keyVal = decodeIntKey(keyHead);
3925
+ labels.push(keyVal);
3926
+ const valueStart = skipCborItem(txCbor, pairPos);
3927
+ const valueEnd = skipCborItem(txCbor, valueStart);
3928
+ if (keyVal === POE_LABEL) {
3929
+ label309 = reassembleLabel309Value(txCbor, valueStart, valueEnd);
3930
+ }
3931
+ pairPos = valueEnd;
3932
+ }
3933
+ labels.sort((a, b) => a - b);
3934
+ return { label309, txBody, witnessSet, auxMetadataLabels: labels };
3935
+ }
3936
+ function sliceLabel309Value(txCbor) {
3937
+ return sliceTxComponents(txCbor).label309;
3938
+ }
3939
+ function reassembleLabel309Value(txCbor, valueStart, valueEnd) {
3940
+ const head = readHead(txCbor, valueStart);
3941
+ if (head.mt === 4) {
3942
+ const out = [];
3943
+ let totalLen = 0;
3944
+ let chunkPos = head.payloadStart;
3945
+ for (let i = 0; i < head.valueU64; i++) {
3946
+ const chunkHead = readHead(txCbor, chunkPos);
3947
+ if (chunkHead.mt !== 2) {
3948
+ throw new RangeError(
3949
+ `MALFORMED_CBOR: label-309 value is a CBOR array but element ${i} has major type ${chunkHead.mt}; expected byte string (chunked-bytes shape)`
3950
+ );
3951
+ }
3952
+ const chunkValueStart = chunkHead.payloadStart;
3953
+ const chunkValueEnd = chunkValueStart + chunkHead.valueU64;
3954
+ out.push(txCbor.slice(chunkValueStart, chunkValueEnd));
3955
+ totalLen += chunkHead.valueU64;
3956
+ chunkPos = chunkValueEnd;
3957
+ }
3958
+ const concat = new Uint8Array(totalLen);
3959
+ let offset = 0;
3960
+ for (const c of out) {
3961
+ concat.set(c, offset);
3962
+ offset += c.length;
3963
+ }
3964
+ return concat;
3965
+ }
3966
+ if (head.mt === 2) {
3967
+ return txCbor.slice(head.payloadStart, head.payloadStart + head.valueU64);
3968
+ }
3969
+ if (head.mt === 5) {
3970
+ return txCbor.slice(valueStart, valueEnd);
3971
+ }
3972
+ throw new RangeError(
3973
+ `MALFORMED_CBOR: label-309 value has major type ${head.mt}; expected array (chunked), byte string, or map`
3974
+ );
3975
+ }
3976
+ function decodeIntKey(h) {
3977
+ if (h.mt === 0) return h.valueU64;
3978
+ if (h.mt === 1) return -1 - h.valueU64;
3979
+ throw new RangeError(
3980
+ `MALFORMED_CBOR: metadata map key has major type ${h.mt}; expected unsigned integer`
3981
+ );
3982
+ }
3983
+
3984
+ // src/verifier/resolve.ts
3985
+ var KOIOS_MAINNET_URL = "https://api.koios.rest/api/v1";
3986
+ var BLOCKFROST_MAINNET_HOST = "https://cardano-mainnet.blockfrost.io/api/v0";
3987
+ var NotACip309RecordError = class extends Error {
3988
+ code = "METADATA_NOT_FOUND";
3989
+ constructor(message) {
3990
+ super(message);
3991
+ this.name = "NotACip309RecordError";
3992
+ }
3993
+ };
3994
+ async function resolveCardanoTx(args) {
3995
+ const { input, fetchFn } = args;
3996
+ const koiosChain = input.cardanoGatewayChain ?? [KOIOS_MAINNET_URL];
3997
+ let lastErr;
3998
+ for (const koiosUrl of koiosChain) {
3999
+ try {
4000
+ return await resolveViaKoios(input.txHash, koiosUrl, fetchFn);
4001
+ } catch (e) {
4002
+ if (e instanceof NotACip309RecordError) throw e;
4003
+ lastErr = e;
4004
+ }
4005
+ }
4006
+ if (input.blockfrostProjectId !== void 0) {
4007
+ try {
4008
+ return await resolveViaBlockfrost(input.txHash, input.blockfrostProjectId, fetchFn);
4009
+ } catch (e) {
4010
+ if (e instanceof NotACip309RecordError) throw e;
4011
+ lastErr = e;
4012
+ }
4013
+ }
4014
+ throw new Error(`all_providers_failed: ${lastErr?.message ?? "unknown"}`);
4015
+ }
4016
+ async function resolveViaKoios(txHash, koiosUrl, fetchFn) {
4017
+ const cborRes = await fetchFn(`${koiosUrl}/tx_cbor`, {
4018
+ method: "POST",
4019
+ headers: { "content-type": "application/json", accept: "application/json" },
4020
+ body: JSON.stringify({ _tx_hashes: [txHash] }),
4021
+ purpose: "cardano"
4022
+ });
4023
+ if (cborRes.status !== 200) {
4024
+ throw new Error(`koios_tx_cbor_${cborRes.status}`);
4025
+ }
4026
+ const cborJson = parseJson(cborRes.bytes);
4027
+ if (!Array.isArray(cborJson) || cborJson.length === 0) {
4028
+ throw new NotACip309RecordError("koios returned empty array for tx_cbor; tx may not exist");
4029
+ }
4030
+ const cborEntry = cborJson[0];
4031
+ if (typeof cborEntry.cbor !== "string") {
4032
+ throw new Error("koios_tx_cbor_missing_cbor_field");
4033
+ }
4034
+ if (typeof cborEntry.tx_hash === "string" && cborEntry.tx_hash.toLowerCase() !== txHash.toLowerCase()) {
4035
+ throw new Error(`koios_tx_cbor_hash_mismatch: requested ${txHash} got ${cborEntry.tx_hash}`);
4036
+ }
4037
+ const txCbor = hexToBytes(cborEntry.cbor);
4038
+ const infoRes = await fetchFn(`${koiosUrl}/tx_info`, {
4039
+ method: "POST",
4040
+ headers: { "content-type": "application/json", accept: "application/json" },
4041
+ body: JSON.stringify({ _tx_hashes: [txHash] }),
4042
+ purpose: "cardano"
4043
+ });
4044
+ if (infoRes.status !== 200) {
4045
+ throw new Error(`koios_tx_info_${infoRes.status}`);
4046
+ }
4047
+ const infoJson = parseJson(infoRes.bytes);
4048
+ if (!Array.isArray(infoJson) || infoJson.length === 0) {
4049
+ throw new NotACip309RecordError("koios returned empty array for tx_info");
4050
+ }
4051
+ const infoEntry = infoJson[0];
4052
+ if (typeof infoEntry.tx_hash === "string" && infoEntry.tx_hash.toLowerCase() !== txHash.toLowerCase()) {
4053
+ throw new Error(`koios_tx_info_hash_mismatch: requested ${txHash} got ${infoEntry.tx_hash}`);
4054
+ }
4055
+ let numConfirmations;
4056
+ if (typeof infoEntry.num_confirmations === "number") {
4057
+ numConfirmations = requireNonNegativeInt(infoEntry.num_confirmations, "num_confirmations");
4058
+ } else {
4059
+ const txBlockHeight = requireNonNegativeInt(infoEntry.block_height, "block_height");
4060
+ const tipRes = await fetchFn(`${koiosUrl}/tip`, {
4061
+ method: "GET",
4062
+ headers: { accept: "application/json" },
4063
+ purpose: "cardano"
4064
+ });
4065
+ if (tipRes.status !== 200) {
4066
+ throw new Error(`koios_tip_${tipRes.status}`);
4067
+ }
4068
+ const tipJson = parseJson(tipRes.bytes);
4069
+ if (!Array.isArray(tipJson) || tipJson.length === 0) {
4070
+ throw new Error("koios_tip_empty");
4071
+ }
4072
+ const tipEntry = tipJson[0];
4073
+ const tipHeight = requireNonNegativeInt(tipEntry.block_height, "tip.block_height");
4074
+ numConfirmations = Math.max(0, tipHeight - txBlockHeight + 1);
4075
+ }
4076
+ return {
4077
+ txCbor,
4078
+ numConfirmations,
4079
+ blockTime: requireNonNegativeInt(infoEntry.tx_timestamp, "tx_timestamp"),
4080
+ blockSlot: requireNonNegativeInt(infoEntry.absolute_slot, "absolute_slot"),
4081
+ provider: "koios",
4082
+ providerUrl: koiosUrl
4083
+ };
4084
+ }
4085
+ async function resolveViaBlockfrost(txHash, projectId, fetchFn) {
4086
+ const base = BLOCKFROST_MAINNET_HOST;
4087
+ const headers = { project_id: projectId, accept: "application/json" };
4088
+ const cborRes = await fetchFn(`${base}/txs/${txHash}/cbor`, {
4089
+ method: "GET",
4090
+ headers,
4091
+ purpose: "cardano"
4092
+ });
4093
+ if (cborRes.status !== 200) {
4094
+ throw new Error(`blockfrost_tx_cbor_${cborRes.status}`);
4095
+ }
4096
+ const cborJson = parseJson(cborRes.bytes);
4097
+ if (typeof cborJson.cbor !== "string") {
4098
+ throw new Error("blockfrost_tx_cbor_missing_cbor_field");
4099
+ }
4100
+ const txCbor = hexToBytes(cborJson.cbor);
4101
+ const txRes = await fetchFn(`${base}/txs/${txHash}`, {
4102
+ method: "GET",
4103
+ headers,
4104
+ purpose: "cardano"
4105
+ });
4106
+ if (txRes.status !== 200) {
4107
+ throw new Error(`blockfrost_tx_${txRes.status}`);
4108
+ }
4109
+ const txJson = parseJson(txRes.bytes);
4110
+ const blockTime = requireNonNegativeInt(txJson.block_time, "block_time");
4111
+ const txSlot = requireNonNegativeInt(txJson.slot, "slot");
4112
+ const txBlockHeight = requireNonNegativeInt(txJson.block_height, "block_height");
4113
+ const tipRes = await fetchFn(`${base}/blocks/latest`, {
4114
+ method: "GET",
4115
+ headers,
4116
+ purpose: "cardano"
4117
+ });
4118
+ if (tipRes.status !== 200) {
4119
+ throw new Error(`blockfrost_blocks_latest_${tipRes.status}`);
4120
+ }
4121
+ const tipJson = parseJson(tipRes.bytes);
4122
+ const tipHeight = requireNonNegativeInt(tipJson.height, "tip_height");
4123
+ const numConfirmations = Math.max(0, tipHeight - txBlockHeight + 1);
4124
+ return {
4125
+ txCbor,
4126
+ numConfirmations,
4127
+ blockTime,
4128
+ blockSlot: txSlot,
4129
+ provider: "blockfrost",
4130
+ providerUrl: base
4131
+ };
4132
+ }
4133
+ function extractLabel309Metadata(txCbor) {
4134
+ return sliceLabel309Value(txCbor);
4135
+ }
4136
+ function parseJson(bytes) {
4137
+ return JSON.parse(new TextDecoder().decode(bytes));
4138
+ }
4139
+ function requireNonNegativeInt(value, field) {
4140
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
4141
+ throw new Error(`gateway_field_invalid: ${field} (got ${typeof value}=${String(value)})`);
4142
+ }
4143
+ return value;
4144
+ }
4145
+ function hexToBytes(hex) {
4146
+ const clean = hex.startsWith("0x") || hex.startsWith("0X") ? hex.slice(2) : hex;
4147
+ if (clean.length % 2 !== 0) {
4148
+ throw new Error(`hex string has odd length (${clean.length})`);
4149
+ }
4150
+ if (!/^[0-9a-fA-F]*$/.test(clean)) {
4151
+ throw new Error("hex string contains non-hex characters");
4152
+ }
4153
+ const out = new Uint8Array(clean.length / 2);
4154
+ for (let i = 0; i < out.length; i++) {
4155
+ out[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);
4156
+ }
4157
+ return out;
4158
+ }
4159
+
4160
+ // src/hex.ts
4161
+ function bytesToHex(bytes) {
4162
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
4163
+ }
4164
+
4165
+ // src/verifier/signatures.ts
4166
+ var CARDANO_MAINNET_STAKE_NETWORK_BYTE = 225;
4167
+ var CARDANO_PREPROD_STAKE_NETWORK_BYTE = 224;
4168
+ var CARDANO_STAKE_ADDRESS_LENGTH = 29;
4169
+ var ED25519_PUBLIC_KEY_LENGTH2 = 32;
4170
+ var BLAKE2B_224_LENGTH = 28;
4171
+ async function verifyRecordSignatures(args) {
4172
+ const { record, input } = args;
4173
+ const recordBodyCbor = encodeRecordBodyForSigning(record);
4174
+ const list = record.sigs ?? [];
4175
+ const out = [];
4176
+ for (let i = 0; i < list.length; i++) {
4177
+ out.push(await verifyOneSig(i, list[i], recordBodyCbor, input));
4178
+ }
4179
+ return out;
4180
+ }
4181
+ async function verifyOneSig(index, entry, recordBodyCbor, input) {
4182
+ const coseBytes = bytesChunkArrayConcat(entry.cose_sign1);
4183
+ let cose;
4184
+ try {
4185
+ cose = decodeCoseSign1(coseBytes);
4186
+ } catch {
4187
+ return { index, verdict: "invalid", reason: "MALFORMED_SIG_COSE_SIGN1" };
4188
+ }
4189
+ const resolved = resolveSignerKey(cose, entry);
4190
+ if (resolved.kind === "unresolved") {
4191
+ return { index, verdict: "unresolved", reason: "SIGNER_KEY_UNRESOLVED" };
4192
+ }
4193
+ const { pub, signerType } = resolved;
4194
+ const verifyResult = coseSign1Cip309Verify({
4195
+ message: coseBytes,
4196
+ detachedRecordBodyCbor: recordBodyCbor,
4197
+ expectedSignerKey: pub
4198
+ });
4199
+ if (!verifyResult.ok) {
4200
+ const reason = mapVerifyError(verifyResult.error.code);
4201
+ if (reason === "SIGNATURE_UNSUPPORTED") {
4202
+ return {
4203
+ index,
4204
+ verdict: "unsupported",
4205
+ signer_type: signerType,
4206
+ signer_pub: bytesToHex(pub),
4207
+ reason
4208
+ };
4209
+ }
4210
+ return {
4211
+ index,
4212
+ verdict: "invalid",
4213
+ signer_type: signerType,
4214
+ signer_pub: bytesToHex(pub),
4215
+ reason
4216
+ };
4217
+ }
4218
+ if (signerType === "wallet-inline-key") {
4219
+ const addressOk = checkWalletAddressBinding(cose, pub, input);
4220
+ if (!addressOk) {
4221
+ return {
4222
+ index,
4223
+ verdict: "invalid",
4224
+ signer_type: signerType,
4225
+ signer_pub: bytesToHex(pub),
4226
+ reason: "WALLET_ADDRESS_MISMATCH"
4227
+ };
4228
+ }
4229
+ }
4230
+ return {
4231
+ index,
4232
+ verdict: "valid",
4233
+ signer_type: signerType,
4234
+ signer_pub: bytesToHex(pub)
4235
+ };
4236
+ }
4237
+ function resolveSignerKey(cose, entry) {
4238
+ const protectedKid = cose.protectedHeader.get(4);
4239
+ if (protectedKid instanceof Uint8Array && protectedKid.length === ED25519_PUBLIC_KEY_LENGTH2 && entry.cose_key === void 0) {
4240
+ return {
4241
+ kind: "in-signature-kid",
4242
+ pub: protectedKid,
4243
+ signerType: "in-signature-kid"
4244
+ };
4245
+ }
4246
+ if (entry.cose_key !== void 0) {
4247
+ const blob = bytesChunkArrayConcat(entry.cose_key);
4248
+ const pub = parseCoseKeyEd25519(blob);
4249
+ if (pub !== null && pub.length === ED25519_PUBLIC_KEY_LENGTH2) {
4250
+ return { kind: "wallet-inline-key", pub, signerType: "wallet-inline-key" };
4251
+ }
4252
+ }
4253
+ return { kind: "unresolved" };
4254
+ }
4255
+ function mapVerifyError(code) {
4256
+ switch (code) {
4257
+ case "MALFORMED_SIG_COSE":
4258
+ case "MALFORMED_SIG_COSE_SIGN1":
4259
+ return "MALFORMED_SIG_COSE_SIGN1";
4260
+ case "UNSUPPORTED_SIG_ALG":
4261
+ return "SIGNATURE_UNSUPPORTED";
4262
+ case "KID_UNRESOLVED":
4263
+ return "SIGNER_KEY_UNRESOLVED";
4264
+ case "SIGNATURE_INVALID":
4265
+ return "SIGNATURE_INVALID";
4266
+ default:
4267
+ return "SIGNATURE_INVALID";
4268
+ }
4269
+ }
4270
+ function checkWalletAddressBinding(cose, pub, input) {
4271
+ const networkByte = (input.cardanoNetwork ?? "mainnet") === "preprod" ? CARDANO_PREPROD_STAKE_NETWORK_BYTE : CARDANO_MAINNET_STAKE_NETWORK_BYTE;
4272
+ const rawAddress = cose.protectedHeader.get("address");
4273
+ if (!(rawAddress instanceof Uint8Array)) {
4274
+ return false;
4275
+ }
4276
+ if (rawAddress.length !== CARDANO_STAKE_ADDRESS_LENGTH) return false;
4277
+ if (rawAddress[0] !== networkByte) return false;
4278
+ const stakeKeyHash = blake2b2242(pub);
4279
+ if (stakeKeyHash.length !== BLAKE2B_224_LENGTH) {
4280
+ return false;
4281
+ }
4282
+ const derived = new Uint8Array(CARDANO_STAKE_ADDRESS_LENGTH);
4283
+ derived[0] = networkByte;
4284
+ derived.set(stakeKeyHash, 1);
4285
+ return compareCt3(derived, rawAddress);
4286
+ }
4287
+ ed.hashes.sha512 = sha512;
4288
+ var L2 = ed.Point.CURVE().n;
4289
+ function leBytesToBigInt2(bytes) {
4290
+ let value = 0n;
4291
+ for (let i = bytes.length - 1; i >= 0; i--) {
4292
+ value = value << 8n | BigInt(bytes[i]);
4293
+ }
4294
+ return value;
4295
+ }
4296
+ function verifyEd255192(opts2) {
4297
+ const { signature, message, publicKey } = opts2;
4298
+ if (signature.length !== 64 || publicKey.length !== 32) return false;
4299
+ const S = leBytesToBigInt2(signature.subarray(32, 64));
4300
+ if (S >= L2) return false;
4301
+ let A;
4302
+ let R;
4303
+ try {
4304
+ A = ed.Point.fromBytes(publicKey);
4305
+ R = ed.Point.fromBytes(signature.subarray(0, 32));
4306
+ } catch {
4307
+ return false;
4308
+ }
4309
+ if (A.isSmallOrder() || R.isSmallOrder()) return false;
4310
+ const k = leBytesToBigInt2(ed.hash(concatBytes4(signature.subarray(0, 32), publicKey, message))) % L2;
4311
+ const sB = S === 0n ? ed.Point.ZERO : ed.Point.BASE.multiplyUnsafe(S);
4312
+ const kA = k === 0n ? ed.Point.ZERO : A.multiplyUnsafe(k);
4313
+ return sB.subtract(kA).subtract(R).is0();
4314
+ }
4315
+ function concatBytes4(...parts) {
4316
+ let total = 0;
4317
+ for (const p of parts) total += p.length;
4318
+ const out = new Uint8Array(total);
4319
+ let offset = 0;
4320
+ for (const p of parts) {
4321
+ out.set(p, offset);
4322
+ offset += p.length;
4323
+ }
4324
+ return out;
4325
+ }
4326
+
4327
+ // src/verifier/tx-witnesses.ts
4328
+ var ED25519_PUBLIC_KEY_LENGTH3 = 32;
4329
+ var ED25519_SIGNATURE_LENGTH = 64;
4330
+ var BODY_KEY_INPUTS = 0;
4331
+ var BODY_KEY_OUTPUTS = 1;
4332
+ var BODY_KEY_FEE = 2;
4333
+ var BODY_KEY_INVALID_HEREAFTER = 3;
4334
+ var BODY_KEY_INVALID_BEFORE = 8;
4335
+ var BODY_KEY_REQUIRED_SIGNERS = 14;
4336
+ var BODY_KEY_NETWORK_ID = 15;
4337
+ var WITNESS_KEY_VKEY = 0;
4338
+ function asArray(v) {
4339
+ if (v instanceof Set) return [...v];
4340
+ if (Array.isArray(v)) return v;
4341
+ return [];
4342
+ }
4343
+ function asMap2(v) {
4344
+ return v instanceof Map ? v : null;
4345
+ }
4346
+ function decodeTxWitnesses(witnessSetBytes, txBodyBytes) {
4347
+ const witnessSet = asMap2(decodeCbor(witnessSetBytes));
4348
+ if (witnessSet === null) return [];
4349
+ const vkeyWitnesses = asArray(witnessSet.get(WITNESS_KEY_VKEY));
4350
+ const txHash = blake2b256(txBodyBytes);
4351
+ const out = [];
4352
+ for (const entry of vkeyWitnesses) {
4353
+ const pair = asArray(entry);
4354
+ const vkey = pair[0];
4355
+ const signature = pair[1];
4356
+ if (!(vkey instanceof Uint8Array) || vkey.length !== ED25519_PUBLIC_KEY_LENGTH3 || !(signature instanceof Uint8Array) || signature.length !== ED25519_SIGNATURE_LENGTH) {
4357
+ if (vkey instanceof Uint8Array && vkey.length === ED25519_PUBLIC_KEY_LENGTH3) {
4358
+ out.push({
4359
+ type: "vkey",
4360
+ vkey: bytesToHex(vkey),
4361
+ key_hash: bytesToHex(blake2b2242(vkey)),
4362
+ signature_valid: false
4363
+ });
4364
+ }
4365
+ continue;
4366
+ }
4367
+ let signatureValid;
4368
+ try {
4369
+ signatureValid = verifyEd255192({ publicKey: vkey, message: txHash, signature });
4370
+ } catch {
4371
+ signatureValid = false;
4372
+ }
4373
+ out.push({
4374
+ type: "vkey",
4375
+ vkey: bytesToHex(vkey),
4376
+ key_hash: bytesToHex(blake2b2242(vkey)),
4377
+ signature_valid: signatureValid
4378
+ });
4379
+ }
4380
+ return out;
4381
+ }
4382
+ function countScriptWitnesses(witnessSetBytes) {
4383
+ const witnessSet = asMap2(decodeCbor(witnessSetBytes));
4384
+ if (witnessSet === null) return 0;
4385
+ let count = 0;
4386
+ for (const [key, value] of witnessSet) {
4387
+ if (key === WITNESS_KEY_VKEY) continue;
4388
+ count += asArray(value).length;
4389
+ }
4390
+ return count;
4391
+ }
4392
+ function decodeTxSummary(txBodyBytes, witnessSetBytes, network) {
4393
+ const body = asMap2(decodeCbor(txBodyBytes));
4394
+ if (body === null) {
4395
+ throw new RangeError("MALFORMED_CBOR: tx body is not a CBOR map");
4396
+ }
4397
+ const inputs = asArray(body.get(BODY_KEY_INPUTS));
4398
+ const outputsRaw = asArray(body.get(BODY_KEY_OUTPUTS));
4399
+ const outputs = [];
4400
+ let totalOutput = 0n;
4401
+ for (const o of outputsRaw) {
4402
+ const { addressBytes, lovelace } = readOutput(o);
4403
+ totalOutput += lovelace;
4404
+ outputs.push({
4405
+ address: encodeCardanoAddress(addressBytes, network),
4406
+ lovelace: lovelace.toString()
4407
+ });
4408
+ }
4409
+ const requiredSigners = asArray(body.get(BODY_KEY_REQUIRED_SIGNERS)).filter((s) => s instanceof Uint8Array).map((s) => bytesToHex(s));
4410
+ const summary = {
4411
+ fee_lovelace: coinToString(body.get(BODY_KEY_FEE)),
4412
+ input_count: inputs.length,
4413
+ output_count: outputs.length,
4414
+ outputs,
4415
+ total_output_lovelace: totalOutput.toString(),
4416
+ script_witness_count: countScriptWitnesses(witnessSetBytes)
4417
+ };
4418
+ const invalidBefore = body.get(BODY_KEY_INVALID_BEFORE);
4419
+ if (typeof invalidBefore === "number") summary.invalid_before = invalidBefore;
4420
+ else if (typeof invalidBefore === "bigint") summary.invalid_before = Number(invalidBefore);
4421
+ const invalidHereafter = body.get(BODY_KEY_INVALID_HEREAFTER);
4422
+ if (typeof invalidHereafter === "number") summary.invalid_hereafter = invalidHereafter;
4423
+ else if (typeof invalidHereafter === "bigint") summary.invalid_hereafter = Number(invalidHereafter);
4424
+ if (requiredSigners.length > 0) summary.required_signer_key_hashes = requiredSigners;
4425
+ const networkId = body.get(BODY_KEY_NETWORK_ID);
4426
+ if (typeof networkId === "number") summary.network_id = networkId;
4427
+ else if (typeof networkId === "bigint") summary.network_id = Number(networkId);
4428
+ return summary;
4429
+ }
4430
+ function readOutput(output) {
4431
+ let address;
4432
+ let amount;
4433
+ if (Array.isArray(output)) {
4434
+ address = output[0];
4435
+ amount = output[1];
4436
+ } else if (output instanceof Map) {
4437
+ address = output.get(0);
4438
+ amount = output.get(1);
4439
+ } else {
4440
+ throw new RangeError("MALFORMED_CBOR: tx output is neither a CBOR array nor a CBOR map");
4441
+ }
4442
+ if (!(address instanceof Uint8Array)) {
4443
+ throw new RangeError("MALFORMED_CBOR: tx output address is not a byte string");
4444
+ }
4445
+ const lovelace = Array.isArray(amount) ? toBigInt(amount[0]) : toBigInt(amount);
4446
+ return { addressBytes: address, lovelace };
4447
+ }
4448
+ function coinToString(v) {
4449
+ return toBigInt(v).toString();
4450
+ }
4451
+ function toBigInt(v) {
4452
+ if (typeof v === "bigint") return v;
4453
+ if (typeof v === "number" && Number.isInteger(v)) return BigInt(v);
4454
+ throw new RangeError(`MALFORMED_CBOR: expected an integer coin value, got ${typeof v}`);
4455
+ }
4456
+ var BECH32_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
4457
+ function encodeCardanoAddress(addressBytes, network) {
4458
+ if (addressBytes.length === 0) {
4459
+ throw new RangeError("MALFORMED_CBOR: empty address byte string");
4460
+ }
4461
+ const header = addressBytes[0];
4462
+ const addressType = header >> 4;
4463
+ const networkNibble = header & 15;
4464
+ const isStake = addressType === 14 || addressType === 15;
4465
+ const isTestnet = networkNibble === 0 ? true : networkNibble === 1 ? false : network === "preprod";
4466
+ const base = isStake ? "stake" : "addr";
4467
+ const hrp = isTestnet ? `${base}_test` : base;
4468
+ return bech32Encode(hrp, addressBytes);
4469
+ }
4470
+ function bech32Polymod(values) {
4471
+ const generators = [996825010, 642813549, 513874426, 1027748829, 705979059];
4472
+ let chk = 1;
4473
+ for (const value of values) {
4474
+ const top = chk >> 25;
4475
+ chk = (chk & 33554431) << 5 ^ value;
4476
+ for (let i = 0; i < 5; i++) {
4477
+ if (top >> i & 1) chk ^= generators[i];
4478
+ }
4479
+ }
4480
+ return chk;
4481
+ }
4482
+ function bech32HrpExpand(hrp) {
4483
+ const out = [];
4484
+ for (let i = 0; i < hrp.length; i++) out.push(hrp.charCodeAt(i) >> 5);
4485
+ out.push(0);
4486
+ for (let i = 0; i < hrp.length; i++) out.push(hrp.charCodeAt(i) & 31);
4487
+ return out;
4488
+ }
4489
+ function bech32ToWords(data) {
4490
+ let acc = 0;
4491
+ let bits = 0;
4492
+ const out = [];
4493
+ const maxv = (1 << 5) - 1;
4494
+ for (const value of data) {
4495
+ acc = acc << 8 | value;
4496
+ bits += 8;
4497
+ while (bits >= 5) {
4498
+ bits -= 5;
4499
+ out.push(acc >> bits & maxv);
4500
+ }
4501
+ }
4502
+ if (bits > 0) out.push(acc << 5 - bits & maxv);
4503
+ return out;
4504
+ }
4505
+ function bech32Encode(hrp, data) {
4506
+ const words = bech32ToWords(data);
4507
+ const polymodInput = bech32HrpExpand(hrp).concat(words, [0, 0, 0, 0, 0, 0]);
4508
+ const polymod = bech32Polymod(polymodInput) ^ 1;
4509
+ const checksum = [];
4510
+ for (let i = 0; i < 6; i++) checksum.push(polymod >> 5 * (5 - i) & 31);
4511
+ let result = `${hrp}1`;
4512
+ for (const w of words.concat(checksum)) result += BECH32_CHARSET.charAt(w);
4513
+ return result;
4514
+ }
4515
+
4516
+ // src/verifier/verify.ts
4517
+ var CONFIRMATION_DEPTH_THRESHOLD_DEFAULT = 15;
4518
+ async function verifyTx(input) {
4519
+ const profile = input.profile ?? DEFAULT_PROFILE;
4520
+ const threshold = input.confirmationDepthThreshold ?? CONFIRMATION_DEPTH_THRESHOLD_DEFAULT;
4521
+ const httpCalls = [];
4522
+ const fetchFn = wrapFetchOutbound(
4523
+ input.fetchOutbound ?? defaultFetchOutbound,
4524
+ httpCalls,
4525
+ input.denyHosts
4526
+ );
4527
+ const base = (over) => ({
4528
+ tx_hash: input.txHash,
4529
+ network: "cardano:mainnet",
4530
+ profile,
4531
+ num_confirmations: 0,
4532
+ confirmation_depth_threshold: threshold,
4533
+ metadata_present: false,
4534
+ validation: { valid: false },
4535
+ http_calls: httpCalls,
4536
+ ...over
4537
+ });
4538
+ let resolved;
4539
+ try {
4540
+ resolved = await resolveCardanoTx({ input, fetchFn });
4541
+ } catch (e) {
4542
+ if (e instanceof NotACip309RecordError) {
4543
+ return base({
4544
+ verdict: "failed",
4545
+ exit_code: 1,
4546
+ validation: {
4547
+ valid: false,
4548
+ issues: [issueOf("METADATA_NOT_FOUND", [], e.message)]
4549
+ }
4550
+ });
4551
+ }
4552
+ return base({
4553
+ verdict: "failed",
4554
+ exit_code: 2,
4555
+ validation: {
4556
+ valid: false,
4557
+ issues: [issueOf("PROVIDER_UNAVAILABLE", [], e.message)]
4558
+ }
4559
+ });
4560
+ }
4561
+ let metadataBytes;
4562
+ try {
4563
+ metadataBytes = extractLabel309Metadata(resolved.txCbor);
4564
+ } catch (e) {
4565
+ return base({
4566
+ verdict: "failed",
4567
+ exit_code: 1,
4568
+ num_confirmations: resolved.numConfirmations,
4569
+ block_time: resolved.blockTime,
4570
+ block_slot: resolved.blockSlot,
4571
+ validation: {
4572
+ valid: false,
4573
+ issues: [issueOf("MALFORMED_CBOR", [], e.message)]
4574
+ }
4575
+ });
4576
+ }
4577
+ if (metadataBytes === null) {
4578
+ return base({
4579
+ verdict: "failed",
4580
+ exit_code: 1,
4581
+ num_confirmations: resolved.numConfirmations,
4582
+ block_time: resolved.blockTime,
4583
+ block_slot: resolved.blockSlot,
4584
+ metadata_present: false,
4585
+ validation: {
4586
+ valid: false,
4587
+ issues: [issueOf("METADATA_NOT_FOUND", [], "no label-309 metadata on this tx")]
4588
+ }
4589
+ });
4590
+ }
4591
+ return verifyResolvedRecord({
4592
+ input,
4593
+ metadataBytes,
4594
+ txCbor: resolved.txCbor,
4595
+ numConfirmations: resolved.numConfirmations,
4596
+ blockTime: resolved.blockTime,
4597
+ blockSlot: resolved.blockSlot,
4598
+ httpCalls,
4599
+ fetchFn
4600
+ });
4601
+ }
4602
+ async function verifyResolved(input) {
4603
+ const httpCalls = [];
4604
+ const fetchFn = wrapFetchOutbound(
4605
+ input.fetchOutbound ?? defaultFetchOutbound,
4606
+ httpCalls,
4607
+ input.denyHosts
4608
+ );
4609
+ const verifyTxInput = {
4610
+ txHash: input.txHash,
4611
+ ...input.profile !== void 0 ? { profile: input.profile } : {},
4612
+ ...input.cardanoNetwork !== void 0 ? { cardanoNetwork: input.cardanoNetwork } : {},
4613
+ ...input.confirmationDepthThreshold !== void 0 ? { confirmationDepthThreshold: input.confirmationDepthThreshold } : {},
4614
+ ...input.fetchOutbound !== void 0 ? { fetchOutbound: input.fetchOutbound } : {},
4615
+ ...input.denyHosts !== void 0 ? { denyHosts: input.denyHosts } : {},
4616
+ ...input.decryption !== void 0 ? { decryption: input.decryption } : {},
4617
+ ...input.verifyMerkle !== void 0 ? { verifyMerkle: input.verifyMerkle } : {}
4618
+ };
4619
+ const report = await verifyResolvedRecord({
4620
+ input: verifyTxInput,
4621
+ metadataBytes: input.metadataCbor,
4622
+ ...input.txCbor !== void 0 ? { txCbor: input.txCbor } : {},
4623
+ numConfirmations: input.numConfirmations,
4624
+ ...input.blockTime !== void 0 ? { blockTime: input.blockTime } : {},
4625
+ ...input.blockSlot !== void 0 ? { blockSlot: input.blockSlot } : {},
4626
+ httpCalls,
4627
+ fetchFn
4628
+ });
4629
+ if (input.network !== void 0) {
4630
+ return { ...report, network: input.network };
4631
+ }
4632
+ return report;
4633
+ }
4634
+ async function verifyResolvedRecord(args) {
4635
+ const { input, metadataBytes, txCbor, numConfirmations, blockTime, blockSlot, httpCalls, fetchFn } = args;
4636
+ const profile = input.profile ?? DEFAULT_PROFILE;
4637
+ const threshold = input.confirmationDepthThreshold ?? CONFIRMATION_DEPTH_THRESHOLD_DEFAULT;
4638
+ const txDescription = txCbor !== void 0 ? decodeTxDescription(txCbor, input) : {};
4639
+ const base = (over) => ({
4640
+ tx_hash: input.txHash,
4641
+ network: "cardano:mainnet",
4642
+ profile,
4643
+ num_confirmations: 0,
4644
+ confirmation_depth_threshold: threshold,
4645
+ metadata_present: false,
4646
+ validation: { valid: false },
4647
+ http_calls: httpCalls,
4648
+ ...txDescription,
4649
+ ...over
4650
+ });
4651
+ const validation = validatePoeRecord(metadataBytes);
4652
+ if (!validation.ok) {
4653
+ return base({
4654
+ verdict: "failed",
4655
+ exit_code: 1,
4656
+ num_confirmations: numConfirmations,
4657
+ ...blockTime !== void 0 ? { block_time: blockTime } : {},
4658
+ ...blockSlot !== void 0 ? { block_slot: blockSlot } : {},
4659
+ metadata_present: true,
4660
+ validation: { valid: false, issues: validation.issues }
4661
+ });
4662
+ }
4663
+ const record = validation.record;
4664
+ if (numConfirmations < threshold) {
4665
+ return base({
4666
+ verdict: "pending",
4667
+ exit_code: 3,
4668
+ num_confirmations: numConfirmations,
4669
+ ...blockTime !== void 0 ? { block_time: blockTime } : {},
4670
+ ...blockSlot !== void 0 ? { block_slot: blockSlot } : {},
4671
+ metadata_present: true,
4672
+ record,
4673
+ validation: {
4674
+ valid: false,
4675
+ issues: [
4676
+ issueOf("INSUFFICIENT_CONFIRMATIONS", [], `${numConfirmations} < threshold ${threshold}`)
4677
+ ]
4678
+ }
4679
+ });
4680
+ }
4681
+ const initialWarnings = (validation.warnings ?? []).slice();
4682
+ const initialInfo = (validation.info ?? []).slice();
4683
+ const plan = planProfileSkips(profile, record);
4684
+ initialInfo.push(...plan.skips);
4685
+ const reportShape = {
4686
+ tx_hash: input.txHash,
4687
+ network: "cardano:mainnet",
4688
+ profile,
4689
+ num_confirmations: numConfirmations,
4690
+ confirmation_depth_threshold: threshold,
4691
+ ...blockTime !== void 0 ? { block_time: blockTime } : {},
4692
+ ...blockSlot !== void 0 ? { block_slot: blockSlot } : {},
4693
+ metadata_present: true,
4694
+ validation: composeValidation(true, void 0, initialWarnings, initialInfo),
4695
+ record,
4696
+ ...txDescription,
4697
+ http_calls: httpCalls,
4698
+ verdict: "valid",
4699
+ exit_code: 0
4700
+ };
4701
+ const report = { ...reportShape };
4702
+ const uriChecks = [];
4703
+ const allowUriFetch = input.verifyMerkle ?? true;
4704
+ if (plan.verifySignatures && record.sigs && record.sigs.length > 0) {
4705
+ const sigOut = await verifyRecordSignatures({ record, input });
4706
+ report.record_signatures = sigOut;
4707
+ if (recordSignaturesShouldFail(sigOut)) {
4708
+ report.verdict = "failed";
4709
+ report.exit_code = 1;
4710
+ }
4711
+ }
4712
+ if (plan.verifyDecrypt && input.decryption && input.decryption.length > 0) {
4713
+ const dec = await tryDecryptions({
4714
+ record,
4715
+ input,
4716
+ fetchFn,
4717
+ httpCalls,
4718
+ uriChecksOut: uriChecks,
4719
+ allowUriFetch
4720
+ });
4721
+ report.item_decryptions = dec.results;
4722
+ const decFailure = decryptionsShouldFail(dec.results);
4723
+ if (decFailure !== null) {
4724
+ report.verdict = "failed";
4725
+ report.exit_code = decFailure === "network" ? 2 : 1;
4726
+ }
4727
+ }
4728
+ if (allowUriFetch && Array.isArray(record.merkle) && record.merkle.length > 0) {
4729
+ const merkle = await verifyMerkleCommitments({
4730
+ record,
4731
+ input,
4732
+ fetchFn,
4733
+ uriChecksOut: uriChecks
4734
+ });
4735
+ report.merkle_checks = merkle.checks;
4736
+ const merkleFailure = merkleChecksShouldFail(merkle.checks);
4737
+ if (merkleFailure && report.verdict === "valid") {
4738
+ report.verdict = "failed";
4739
+ report.exit_code = 1;
4740
+ }
4741
+ }
4742
+ if (uriChecks.length > 0) {
4743
+ report.uri_checks = uriChecks;
4744
+ }
4745
+ return report;
4746
+ }
4747
+ function decodeTxDescription(txCbor, input) {
4748
+ const network = input.cardanoNetwork ?? "mainnet";
4749
+ const out = {};
4750
+ let components;
4751
+ try {
4752
+ components = sliceTxComponents(txCbor);
4753
+ } catch {
4754
+ return out;
4755
+ }
4756
+ out.metadata_labels = components.auxMetadataLabels;
4757
+ try {
4758
+ out.tx_witnesses = decodeTxWitnesses(components.witnessSet, components.txBody);
4759
+ } catch {
4760
+ }
4761
+ try {
4762
+ out.tx_summary = decodeTxSummary(components.txBody, components.witnessSet, network);
4763
+ } catch {
4764
+ }
4765
+ return out;
4766
+ }
4767
+ function recordSignaturesShouldFail(sigs) {
4768
+ return sigs.some((s) => s.verdict === "invalid" || s.verdict === "unresolved");
4769
+ }
4770
+ function decryptionsShouldFail(results) {
4771
+ let saw = null;
4772
+ for (const d of results) {
4773
+ if (d.verdict === "decrypted" && d.plaintext_hash_ok !== false) continue;
4774
+ if (d.verdict === "content-unavailable" || d.verdict === "ciphertext-unavailable") {
4775
+ saw = saw === "integrity" ? "integrity" : "network";
4776
+ continue;
4777
+ }
4778
+ saw = "integrity";
4779
+ }
4780
+ return saw;
4781
+ }
4782
+ function merkleChecksShouldFail(checks) {
4783
+ for (const c of checks) {
4784
+ if (c.verdict === "mismatch") return true;
4785
+ }
4786
+ return false;
4787
+ }
4788
+ function issueOf(code, path, message) {
4789
+ return { code, path, message, severity: SEVERITY[code] };
4790
+ }
4791
+ function composeValidation(valid, issues, warnings, info) {
4792
+ const out = { valid };
4793
+ if (warnings.length > 0) out.warnings = warnings;
4794
+ if (info.length > 0) out.info = info;
4795
+ return out;
4796
+ }
4797
+ function exitCodeForVerdict(report) {
4798
+ return report.exit_code;
4799
+ }
4800
+
4801
+ // src/verifier/serialize.ts
4802
+ function isPlainObject(v) {
4803
+ if (v === null || typeof v !== "object") return false;
4804
+ if (Array.isArray(v) || v instanceof Uint8Array || v instanceof Map || v instanceof Set) {
4805
+ return false;
4806
+ }
4807
+ return true;
4808
+ }
4809
+ function walk(value) {
4810
+ if (value === void 0 || value === null) return void 0;
4811
+ if (value instanceof Uint8Array) return bytesToHex(value);
4812
+ if (value instanceof Map) {
4813
+ throw new Error("unsupported Map in VerifyReport tree");
4814
+ }
4815
+ if (Array.isArray(value)) {
4816
+ return value.map((v) => walk(v));
4817
+ }
4818
+ if (isPlainObject(value)) {
4819
+ const out = {};
4820
+ for (const [k, v] of Object.entries(value)) {
4821
+ if (v === void 0 || v === null) continue;
4822
+ const walked = walk(v);
4823
+ if (walked === void 0) continue;
4824
+ out[k] = walked;
4825
+ }
4826
+ return out;
4827
+ }
4828
+ return value;
4829
+ }
4830
+ function verifyReportToDict(report) {
4831
+ const out = walk(report);
4832
+ if (!isPlainObject(out)) {
4833
+ throw new Error("verifyReportToDict: walk produced non-object root");
4834
+ }
4835
+ return out;
4836
+ }
4837
+ /*! Bundled license information:
4838
+
4839
+ @noble/post-quantum/utils.js:
4840
+ @noble/post-quantum/_crystals.js:
4841
+ @noble/post-quantum/ml-kem.js:
4842
+ @noble/post-quantum/hybrid.js:
4843
+ (*! noble-post-quantum - MIT License (c) 2024 Paul Miller (paulmillr.com) *)
4844
+ */
4845
+
4846
+ export { BLOCKFROST_MAINNET_HOST, BodyTooLargeError, CONFIRMATION_DEPTH_THRESHOLD_DEFAULT, DEFAULT_OUTBOUND_MAX_BYTES, DEFAULT_PROFILE, DENY_HOSTS_DEFAULT, DenyHostError, KOIOS_MAINNET_URL, NotACip309RecordError, OutboundExhaustedError, PROFILE_RANK, UnsupportedMethodError, UnsupportedProtocolError, decodeTxSummary, decodeTxWitnesses, defaultFetchOutbound, exitCodeForVerdict, extractLabel309Metadata, fetchItemCiphertext, fetchOutbound, planProfileSkips, profileImplements, resolveCardanoTx, sliceLabel309Value, sliceTxComponents, tryDecryptions, verifyMerkleCommitments, verifyRecordSignatures, verifyReportToDict, verifyResolved, verifyTx, wrapFetchOutbound };
4847
+ //# sourceMappingURL=index.js.map
4848
+ //# sourceMappingURL=index.js.map