@cardanowall/sdk-ts 0.1.0 → 0.3.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 (38) hide show
  1. package/README.md +14 -14
  2. package/dist/client/index.cjs +1621 -1538
  3. package/dist/client/index.cjs.map +1 -1
  4. package/dist/client/index.d.cts +52 -52
  5. package/dist/client/index.d.ts +52 -52
  6. package/dist/client/index.js +1620 -1537
  7. package/dist/client/index.js.map +1 -1
  8. package/dist/conformance/cli.cjs +2367 -2106
  9. package/dist/conformance/cli.cjs.map +1 -1
  10. package/dist/conformance/cli.js +2367 -2106
  11. package/dist/conformance/cli.js.map +1 -1
  12. package/dist/identity/index.cjs +219 -104
  13. package/dist/identity/index.cjs.map +1 -1
  14. package/dist/identity/index.d.cts +1 -1
  15. package/dist/identity/index.d.ts +1 -1
  16. package/dist/identity/index.js +219 -104
  17. package/dist/identity/index.js.map +1 -1
  18. package/dist/ids/index.cjs.map +1 -1
  19. package/dist/ids/index.js.map +1 -1
  20. package/dist/index.cjs +2808 -2530
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +3 -3
  23. package/dist/index.d.ts +3 -3
  24. package/dist/index.js +2805 -2527
  25. package/dist/index.js.map +1 -1
  26. package/dist/merkle/index.cjs +1 -1
  27. package/dist/merkle/index.cjs.map +1 -1
  28. package/dist/merkle/index.js +1 -1
  29. package/dist/merkle/index.js.map +1 -1
  30. package/dist/{types-BQMtbRCb.d.cts → types-DGsZTMuZ.d.cts} +6 -6
  31. package/dist/{types-BQMtbRCb.d.ts → types-DGsZTMuZ.d.ts} +6 -6
  32. package/dist/verifier/index.cjs +2368 -2107
  33. package/dist/verifier/index.cjs.map +1 -1
  34. package/dist/verifier/index.d.cts +3 -3
  35. package/dist/verifier/index.d.ts +3 -3
  36. package/dist/verifier/index.js +2369 -2108
  37. package/dist/verifier/index.js.map +1 -1
  38. package/package.json +8 -8
@@ -1,14 +1,13 @@
1
1
  import { z } from 'zod';
2
- import { decode, cdeDecodeOptions, encode } from 'cbor2';
2
+ import { decode, encode, cdeDecodeOptions } from 'cbor2';
3
3
  import { sortCoreDeterministic } from 'cbor2/sorts';
4
4
  import { blake2b } from '@noble/hashes/blake2.js';
5
5
  import * as ed from '@noble/ed25519';
6
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
7
  import '@noble/ciphers/utils.js';
11
8
  import { hmac } from '@noble/hashes/hmac.js';
9
+ import { xchacha20poly1305, chacha20poly1305 } from '@noble/ciphers/chacha.js';
10
+ import { hkdf } from '@noble/hashes/hkdf.js';
12
11
  import '@noble/curves/abstract/edwards.js';
13
12
  import '@noble/curves/abstract/montgomery.js';
14
13
  import '@noble/curves/abstract/weierstrass.js';
@@ -18,6 +17,7 @@ import { concatBytes as concatBytes$1, asciiToBytes, bytesToNumberLE, bytesToNum
18
17
  import { sha3_256, shake256, sha3_512, shake128 } from '@noble/hashes/sha3.js';
19
18
  import { anumber, abytes, randomBytes as randomBytes$1, u32, swap32IfBE } from '@noble/hashes/utils.js';
20
19
  import { FFTCore, reverseBits } from '@noble/curves/abstract/fft.js';
20
+ import { argon2id } from 'hash-wasm';
21
21
 
22
22
  // src/verifier/types.ts
23
23
  var PROFILE_RANK = Object.freeze({
@@ -150,7 +150,7 @@ function decodeCanonicalCbor(bytes) {
150
150
  ...cdeDecodeOptions,
151
151
  rejectStreaming: true,
152
152
  rejectDuplicateKeys: true,
153
- // A CIP-309 record carries integers, byte/text strings, arrays, maps and
153
+ // A Label 309 record carries integers, byte/text strings, arrays, maps and
154
154
  // `null` — and nothing else. Without these rejections the major-type-7
155
155
  // surface leaks into the decoder: a float16/32/64 that happens to hold an
156
156
  // integral value (e.g. 1.0) silently decodes to the integer 1 and passes
@@ -276,7 +276,7 @@ function decodeCanonicalCbor2(bytes) {
276
276
  ...cdeDecodeOptions,
277
277
  rejectStreaming: true,
278
278
  rejectDuplicateKeys: true,
279
- // A CIP-309 record carries integers, byte/text strings, arrays, maps and
279
+ // A Label 309 record carries integers, byte/text strings, arrays, maps and
280
280
  // `null` — and nothing else. Without these rejections the major-type-7
281
281
  // surface leaks into the decoder: a float16/32/64 that happens to hold an
282
282
  // integral value (e.g. 1.0) silently decodes to the integer 1 and passes
@@ -377,7 +377,7 @@ function buildSigStructure(args) {
377
377
  args.payload
378
378
  ]);
379
379
  }
380
- function buildCip309SigStructure(args) {
380
+ function buildLabel309SigStructure(args) {
381
381
  const toSign = new Uint8Array(
382
382
  CARDANO_POE_SIG_DOMAIN_PREFIX_BYTES.length + args.recordBodyCbor.length
383
383
  );
@@ -451,7 +451,7 @@ function decodeCoseSign1(bytes) {
451
451
  signature: signatureRaw
452
452
  };
453
453
  }
454
- function coseSign1Cip309Verify(args) {
454
+ function coseSign1Label309Verify(args) {
455
455
  let decoded;
456
456
  try {
457
457
  decoded = decodeCoseSign1(args.message);
@@ -518,7 +518,7 @@ function coseSign1Cip309Verify(args) {
518
518
  payload: hashedPayload
519
519
  });
520
520
  } else {
521
- sigStructureBytes = buildCip309SigStructure({
521
+ sigStructureBytes = buildLabel309SigStructure({
522
522
  bodyProtectedBytes: decoded.protectedBytes,
523
523
  recordBodyCbor: args.detachedRecordBodyCbor
524
524
  });
@@ -572,2231 +572,2431 @@ function parseCoseKeyEd25519(blob) {
572
572
  if (!(x instanceof Uint8Array) || x.length !== ED25519_PUBLIC_KEY_LENGTH) return null;
573
573
  return x;
574
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;
575
+ var abytesDoc = abytes;
576
+ var randomBytes = randomBytes$1;
577
+ function equalBytes(a, b) {
578
+ if (a.length !== b.length)
579
+ return false;
580
+ let diff = 0;
581
+ for (let i = 0; i < a.length; i++)
582
+ diff |= a[i] ^ b[i];
583
+ return diff === 0;
588
584
  }
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
- }
585
+ function copyBytes(bytes) {
586
+ return Uint8Array.from(abytes(bytes));
601
587
  }
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);
588
+ function splitCoder(label, ...lengths) {
589
+ const getLength = (c) => typeof c === "number" ? c : c.bytesLen;
590
+ const bytesLen = lengths.reduce((sum, a) => sum + getLength(a), 0);
591
+ return {
592
+ bytesLen,
593
+ encode: (bufs) => {
594
+ const res = new Uint8Array(bytesLen);
595
+ for (let i = 0, pos = 0; i < lengths.length; i++) {
596
+ const c = lengths[i];
597
+ const l = getLength(c);
598
+ const b = typeof c === "number" ? bufs[i] : c.encode(bufs[i]);
599
+ abytes(b, l, label);
600
+ res.set(b, pos);
601
+ if (typeof c !== "number")
602
+ b.fill(0);
603
+ pos += l;
604
+ }
605
+ return res;
606
+ },
607
+ decode: (buf) => {
608
+ abytes(buf, bytesLen, label);
609
+ const res = [];
610
+ for (const c of lengths) {
611
+ const l = getLength(c);
612
+ const b = buf.subarray(0, l);
613
+ res.push(typeof c === "number" ? b : c.decode(b));
614
+ buf = buf.subarray(l);
615
+ }
616
+ return res;
770
617
  }
771
- }
772
- if (errors.length > 0) {
773
- return { ok: false, issues: errors.sort(compareIssuePath) };
774
- }
775
- const result = {
776
- ok: true,
777
- record
778
618
  };
779
- if (warnings.length > 0) result.warnings = warnings.sort(compareIssuePath);
780
- if (info.length > 0) result.info = info.sort(compareIssuePath);
781
- return result;
782
619
  }
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
- );
620
+ function vecCoder(c, vecLen) {
621
+ const coder = c;
622
+ const bytesLen = vecLen * coder.bytesLen;
623
+ return {
624
+ bytesLen,
625
+ encode: (u) => {
626
+ if (u.length !== vecLen)
627
+ throw new RangeError(`vecCoder.encode: wrong length=${u.length}. Expected: ${vecLen}`);
628
+ const res = new Uint8Array(bytesLen);
629
+ for (let i = 0, pos = 0; i < u.length; i++) {
630
+ const b = coder.encode(u[i]);
631
+ res.set(b, pos);
632
+ b.fill(0);
633
+ pos += b.length;
817
634
  }
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
- );
635
+ return res;
636
+ },
637
+ decode: (a) => {
638
+ abytes(a, bytesLen);
639
+ const r = [];
640
+ for (let i = 0; i < a.length; i += coder.bytesLen)
641
+ r.push(coder.decode(a.subarray(i, i + coder.bytesLen)));
642
+ return r;
866
643
  }
644
+ };
645
+ }
646
+ function cleanBytes(...list) {
647
+ for (const t of list) {
648
+ if (Array.isArray(t))
649
+ for (const b of t)
650
+ b.fill(0);
651
+ else
652
+ t.fill(0);
867
653
  }
868
654
  }
869
- function checkItemUris(uris, basePath, errors) {
870
- uris.forEach((chunks, ui) => validateOneUri(chunks, [...basePath, ui], errors));
655
+ function getMask(bits) {
656
+ if (!Number.isSafeInteger(bits) || bits < 0 || bits > 32)
657
+ throw new RangeError(`expected bits in [0..32], got ${bits}`);
658
+ return bits === 32 ? 4294967295 : ~(-1 << bits) >>> 0;
871
659
  }
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("INVALID_URI", path, "URI contains a fragment identifier ('#'), which is forbidden")
882
- );
883
- return;
884
- }
885
- const sepIdx = uri.indexOf("://");
886
- if (sepIdx <= 0 || !/^[a-z][a-z0-9+.-]*$/i.test(uri.slice(0, sepIdx))) {
887
- errors.push(
888
- issue("INVALID_URI", path, "URI is not absolute (missing scheme://hierarchical-part)")
889
- );
890
- return;
891
- }
892
- const scheme = uri.slice(0, sepIdx).toLowerCase();
893
- const rest = uri.slice(sepIdx + "://".length);
894
- if (scheme === "ar") {
895
- if (!/^ar:\/\/[A-Za-z0-9_-]{43}$/.test("ar://" + rest)) {
896
- errors.push(
897
- issue(
898
- "INVALID_URI",
899
- path,
900
- "ar:// URI does not match `^ar://[A-Za-z0-9_-]{43}$` (43-char base64url txid, no path/query/fragment)"
901
- )
902
- );
660
+
661
+ // ../../node_modules/.pnpm/@noble+post-quantum@0.6.1/node_modules/@noble/post-quantum/_crystals.js
662
+ var genCrystals = (opts2) => {
663
+ const { newPoly, N: N2, Q: Q2, F: F2, ROOT_OF_UNITY: ROOT_OF_UNITY2, brvBits} = opts2;
664
+ const mod = (a, modulo = Q2) => {
665
+ const result = a % modulo | 0;
666
+ return (result >= 0 ? result | 0 : modulo + result | 0) | 0;
667
+ };
668
+ const smod = (a, modulo = Q2) => {
669
+ const r = mod(a, modulo) | 0;
670
+ return (r > modulo >> 1 ? r - modulo | 0 : r) | 0;
671
+ };
672
+ function getZettas() {
673
+ const out = newPoly(N2);
674
+ for (let i = 0; i < N2; i++) {
675
+ const b = reverseBits(i, brvBits);
676
+ const p = BigInt(ROOT_OF_UNITY2) ** BigInt(b) % BigInt(Q2);
677
+ out[i] = Number(p) | 0;
903
678
  }
904
- return;
679
+ return out;
905
680
  }
906
- if (scheme === "ipfs") {
907
- const slashIdx = rest.indexOf("/");
908
- const cid = slashIdx === -1 ? rest : rest.slice(0, slashIdx);
909
- if (!validateCidProfile(cid)) {
910
- errors.push(
911
- issue("INVALID_URI", path, "ipfs:// URI is not a valid CID under the CIP-309 profile")
912
- );
681
+ const nttZetas = getZettas();
682
+ const field = {
683
+ add: (a, b) => mod((a | 0) + (b | 0)) | 0,
684
+ sub: (a, b) => mod((a | 0) - (b | 0)) | 0,
685
+ mul: (a, b) => mod((a | 0) * (b | 0)) | 0,
686
+ inv: (_a) => {
687
+ throw new Error("not implemented");
913
688
  }
914
- return;
915
- }
916
- errors.push(
917
- issue("INVALID_URI", path, "unsupported URI scheme; v1 PoE URI set is {ar://, ipfs://}")
918
- );
919
- }
920
- function checkItemEnc(item, idx, errors) {
921
- const hasContentHash = Object.keys(item.hashes).some((alg) => alg in HASH_ALG_LENGTHS);
922
- if (!hasContentHash) {
923
- errors.push(
924
- issue(
925
- "ENC_REQUIRES_CONTENT_HASH",
926
- ["items", idx, "enc"],
927
- "item carries `enc` but `hashes` has no content-hash entry (sha2-256 or blake2b-256)"
928
- )
929
- );
930
- return;
931
- }
932
- const encParse = EncryptionEnvelopeSchema.safeParse(item.enc);
933
- if (!encParse.success) {
934
- for (const zissue of encParse.error.issues) {
935
- const mapped = mapZodIssue(zissue, item.enc);
936
- errors.push({
937
- ...mapped,
938
- path: ["items", idx, "enc", ...mapped.path]
939
- });
689
+ };
690
+ const nttOpts = {
691
+ N: N2,
692
+ roots: nttZetas,
693
+ invertButterflies: true,
694
+ skipStages: 1 ,
695
+ brp: false
696
+ };
697
+ const dif = FFTCore(field, { dit: false, ...nttOpts });
698
+ const dit = FFTCore(field, { dit: true, ...nttOpts });
699
+ const NTT = {
700
+ encode: (r) => {
701
+ return dif(r);
702
+ },
703
+ decode: (r) => {
704
+ dit(r);
705
+ for (let i = 0; i < r.length; i++)
706
+ r[i] = mod(F2 * r[i]);
707
+ return r;
940
708
  }
941
- return;
942
- }
943
- const enc = encParse.data;
944
- const basePath = ["items", idx, "enc"];
945
- if (typeof enc.scheme !== "number" || !Number.isInteger(enc.scheme) || enc.scheme !== 1) {
946
- errors.push(
947
- issue(
948
- "UNSUPPORTED_ENVELOPE_SCHEME",
949
- [...basePath, "scheme"],
950
- `enc.scheme must be the unsigned integer 1; got ${String(enc.scheme)}`
951
- )
952
- );
953
- }
954
- if (UNAUTHENTICATED_CIPHER_RE.test(enc.aead)) {
955
- errors.push(
956
- issue(
957
- "UNAUTHENTICATED_CIPHER_FORBIDDEN",
958
- [...basePath, "aead"],
959
- `'${enc.aead}' is an unauthenticated cipher; CIP-309 mandates an authenticated (AEAD) cipher`
960
- )
961
- );
962
- return;
963
- }
964
- if (!(enc.aead in AEAD_NONCE_LENGTHS)) {
965
- errors.push(
966
- issue("UNSUPPORTED_AEAD_ALG", [...basePath, "aead"], `unknown aead alg: ${enc.aead}`)
967
- );
968
- return;
969
- }
970
- const expectedNonceLen = AEAD_NONCE_LENGTHS[enc.aead];
971
- if (enc.nonce.length !== expectedNonceLen) {
972
- errors.push(
973
- issue(
974
- "NONCE_LENGTH_MISMATCH",
975
- [...basePath, "nonce"],
976
- `nonce length ${enc.nonce.length} != ${expectedNonceLen} for ${enc.aead}`
977
- )
978
- );
979
- }
980
- if (enc.kem !== void 0 && !(enc.kem in KEM_SLOT_DESCRIPTORS)) {
981
- errors.push(issue("UNSUPPORTED_KEM_ALG", [...basePath, "kem"], `unknown kem alg: ${enc.kem}`));
982
- }
983
- const hasSlots = enc.slots !== void 0;
984
- const hasSlotsMac = enc.slots_mac !== void 0;
985
- const hasPassphrase = enc.passphrase !== void 0;
986
- if (hasSlots && hasPassphrase) {
987
- errors.push(
988
- issue("ENC_EXCLUSIVITY_VIOLATION", basePath, "enc combines slots with passphrase; pick one")
989
- );
990
- }
991
- if (hasSlots && !hasSlotsMac) {
992
- errors.push(
993
- issue("ENC_SLOTS_MAC_REQUIRED", basePath, "enc.slots present but enc.slots_mac absent")
994
- );
995
- }
996
- if (hasSlotsMac && !hasSlots) {
997
- errors.push(
998
- issue("ENC_SLOTS_REQUIRED", basePath, "enc.slots_mac present but enc.slots absent")
999
- );
1000
- }
1001
- if (hasSlots && enc.kem === void 0) {
1002
- errors.push(issue("ENC_KEM_REQUIRED", basePath, "enc.slots present but enc.kem absent"));
1003
- }
1004
- if (!hasSlots && !hasPassphrase) {
1005
- errors.push(
1006
- issue(
1007
- "ENC_NO_KEY_PATH",
1008
- basePath,
1009
- "enc requires either slots or passphrase \u2014 no on-chain key path otherwise"
1010
- )
1011
- );
1012
- }
1013
- if (hasSlots) {
1014
- if (enc.slots.length < 1) {
1015
- errors.push(
1016
- issue("ENC_SLOTS_EMPTY", [...basePath, "slots"], `slots length ${enc.slots.length} < 1`)
1017
- );
1018
- }
1019
- const descriptor = enc.kem !== void 0 ? KEM_SLOT_DESCRIPTORS[enc.kem] : void 0;
1020
- if (descriptor !== void 0) {
1021
- const rawSlotKeys = rawSlotKeySets(item.enc);
1022
- enc.slots.forEach((slot, si) => {
1023
- checkSlotShape(
1024
- slot,
1025
- rawSlotKeys[si] ?? /* @__PURE__ */ new Set(),
1026
- descriptor,
1027
- enc.kem,
1028
- [...basePath, "slots", si],
1029
- errors
1030
- );
1031
- });
1032
- }
1033
- }
1034
- if (hasPassphrase) {
1035
- const pp = enc.passphrase;
1036
- const ppPath = [...basePath, "passphrase"];
1037
- if (!PASSPHRASE_KDF_ALGS.has(pp.alg)) {
1038
- errors.push(
1039
- issue(
1040
- "ENC_PASSPHRASE_ALG_UNSUPPORTED",
1041
- [...ppPath, "alg"],
1042
- `unknown passphrase kdf alg: ${pp.alg}`
1043
- )
1044
- );
1045
- return;
1046
- }
1047
- if (pp.alg === "argon2id") {
1048
- const allowed = /* @__PURE__ */ new Set(["m", "t", "p"]);
1049
- for (const k of Object.keys(pp.params)) {
1050
- if (!allowed.has(k)) {
1051
- errors.push(
1052
- issue(
1053
- "SCHEMA_UNKNOWN_FIELD",
1054
- [...ppPath, "params", k],
1055
- `unknown argon2id params field: ${k}`
1056
- )
1057
- );
709
+ };
710
+ const bitsCoder = (d, c) => {
711
+ const mask = getMask(d);
712
+ const bytesLen = d * (N2 / 8);
713
+ return {
714
+ bytesLen,
715
+ encode: (poly_) => {
716
+ const poly = poly_;
717
+ const r = new Uint8Array(bytesLen);
718
+ for (let i = 0, buf = 0, bufLen = 0, pos = 0; i < poly.length; i++) {
719
+ buf |= (c.encode(poly[i]) & mask) << bufLen;
720
+ bufLen += d;
721
+ for (; bufLen >= 8; bufLen -= 8, buf >>= 8)
722
+ r[pos++] = buf & getMask(bufLen);
1058
723
  }
1059
- }
1060
- const p = pp.params;
1061
- const argonInt = (val, name) => {
1062
- if (typeof val !== "number" || !Number.isInteger(val)) {
1063
- errors.push(
1064
- issue(
1065
- "SCHEMA_TYPE_MISMATCH",
1066
- [...ppPath, "params", name],
1067
- `argon2id params.${name} must be a CBOR unsigned integer`
1068
- )
1069
- );
1070
- return null;
724
+ return r;
725
+ },
726
+ decode: (bytes) => {
727
+ const r = newPoly(N2);
728
+ for (let i = 0, buf = 0, bufLen = 0, pos = 0; i < bytes.length; i++) {
729
+ buf |= bytes[i] << bufLen;
730
+ bufLen += 8;
731
+ for (; bufLen >= d; bufLen -= d, buf >>= d)
732
+ r[pos++] = c.decode(buf & mask);
1071
733
  }
1072
- return val;
1073
- };
1074
- const mVal = argonInt(p.m, "m");
1075
- const tVal = argonInt(p.t, "t");
1076
- const pVal = argonInt(p.p, "p");
1077
- if (mVal !== null && mVal < 65536) {
1078
- errors.push(
1079
- issue(
1080
- "ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
1081
- [...ppPath, "params", "m"],
1082
- "argon2id requires m >= 65536 KiB"
1083
- )
1084
- );
1085
- }
1086
- if (tVal !== null && tVal < 3) {
1087
- errors.push(
1088
- issue(
1089
- "ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
1090
- [...ppPath, "params", "t"],
1091
- "argon2id requires t >= 3"
1092
- )
1093
- );
1094
- }
1095
- if (pVal !== null && pVal < 1) {
1096
- errors.push(
1097
- issue(
1098
- "ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
1099
- [...ppPath, "params", "p"],
1100
- "argon2id requires p >= 1"
1101
- )
1102
- );
734
+ return r;
1103
735
  }
736
+ };
737
+ };
738
+ return {
739
+ mod,
740
+ smod,
741
+ nttZetas,
742
+ NTT: {
743
+ encode: (r) => NTT.encode(r),
744
+ decode: (r) => NTT.decode(r)
745
+ },
746
+ bitsCoder
747
+ };
748
+ };
749
+ var createXofShake = (shake) => (seed, blockLen) => {
750
+ if (!blockLen)
751
+ blockLen = shake.blockLen;
752
+ const _seed = new Uint8Array(seed.length + 2);
753
+ _seed.set(seed);
754
+ const seedLen = seed.length;
755
+ const buf = new Uint8Array(blockLen);
756
+ let h = shake.create({});
757
+ let calls = 0;
758
+ let xofs = 0;
759
+ return {
760
+ stats: () => ({ calls, xofs }),
761
+ get: (x, y) => {
762
+ _seed[seedLen + 0] = x;
763
+ _seed[seedLen + 1] = y;
764
+ h.destroy();
765
+ h = shake.create({}).update(_seed);
766
+ calls++;
767
+ return () => {
768
+ xofs++;
769
+ return h.xofInto(buf);
770
+ };
771
+ },
772
+ clean: () => {
773
+ h.destroy();
774
+ cleanBytes(buf, _seed);
1104
775
  }
1105
- }
776
+ };
777
+ };
778
+ var XOF128 = /* @__PURE__ */ createXofShake(shake128);
779
+
780
+ // ../../node_modules/.pnpm/@noble+post-quantum@0.6.1/node_modules/@noble/post-quantum/ml-kem.js
781
+ var N = 256;
782
+ var Q = 3329;
783
+ var F = 3303;
784
+ var ROOT_OF_UNITY = 17;
785
+ var crystals = /* @__PURE__ */ genCrystals({
786
+ N,
787
+ Q,
788
+ F,
789
+ ROOT_OF_UNITY,
790
+ newPoly: (n) => new Uint16Array(n),
791
+ brvBits: 7});
792
+ var PARAMS = /* @__PURE__ */ (() => Object.freeze({
793
+ 512: Object.freeze({ N, Q, K: 2, ETA1: 3, ETA2: 2, du: 10, dv: 4, RBGstrength: 128 }),
794
+ 768: Object.freeze({ N, Q, K: 3, ETA1: 2, ETA2: 2, du: 10, dv: 4, RBGstrength: 192 }),
795
+ 1024: Object.freeze({ N, Q, K: 4, ETA1: 2, ETA2: 2, du: 11, dv: 5, RBGstrength: 256 })
796
+ }))();
797
+ var compress = (d) => {
798
+ if (d >= 12)
799
+ return { encode: (i) => i, decode: (i) => i >= Q ? i - Q : i };
800
+ const a = 2 ** (d - 1);
801
+ return {
802
+ // This only matches standalone Compress_d after bitsCoder masks the result into Z_(2^d).
803
+ encode: (i) => ((i << d) + Q / 2) / Q,
804
+ // const decompress = (i: number) => round((Q / 2 ** d) * i);
805
+ decode: (i) => i * Q + a >>> d
806
+ };
807
+ };
808
+ var byteCoder = (d) => crystals.bitsCoder(d, { encode: (i) => i, decode: (i) => i >= Q ? i - Q : i } );
809
+ var polyCoder = (d) => d === 12 ? byteCoder(12) : crystals.bitsCoder(d, compress(d));
810
+ function polyAdd(a_, b_) {
811
+ const a = a_;
812
+ const b = b_;
813
+ for (let i = 0; i < N; i++)
814
+ a[i] = crystals.mod(a[i] + b[i]);
1106
815
  }
1107
- var SLOT_KEY_UNIVERSE = /* @__PURE__ */ new Set(["epk", "kem_ct", "wrap"]);
1108
- function checkSlotShape(slot, rawKeys, descriptor, kem, slotPath, errors) {
1109
- const foreignField = descriptor.field === "epk" ? "kem_ct" : "epk";
1110
- if (rawKeys.has(foreignField)) {
1111
- errors.push(
1112
- issue(
1113
- "ENC_SLOT_INVALID_SHAPE",
1114
- [...slotPath, foreignField],
1115
- `slot carries '${foreignField}' but kem='${kem}' expects '${descriptor.field}'`
1116
- )
1117
- );
1118
- }
1119
- for (const k of rawKeys) {
1120
- if (!SLOT_KEY_UNIVERSE.has(k)) {
1121
- errors.push(
1122
- issue(
1123
- "ENC_SLOT_INVALID_SHAPE",
1124
- [...slotPath, k],
1125
- `slot carries unexpected key '${k}'; a slot is a 2-key map {${descriptor.field}, wrap}`
1126
- )
1127
- );
1128
- }
1129
- }
1130
- if (descriptor.field === "epk") {
1131
- if (slot.epk === void 0) {
1132
- errors.push(
1133
- issue(
1134
- "ENC_SLOT_INVALID_SHAPE",
1135
- [...slotPath, "epk"],
1136
- `slot for kem='${kem}' is missing required 'epk'`
1137
- )
1138
- );
1139
- } else if (slot.epk.length !== descriptor.fieldLength) {
1140
- errors.push(
1141
- issue(
1142
- KEM_FIELD_LENGTH_CODE.epk,
1143
- [...slotPath, "epk"],
1144
- `slot.epk length ${slot.epk.length} != ${descriptor.fieldLength} for ${kem}`
1145
- )
1146
- );
1147
- }
1148
- } else {
1149
- if (slot.kem_ct === void 0) {
1150
- errors.push(
1151
- issue(
1152
- "ENC_SLOT_INVALID_SHAPE",
1153
- [...slotPath, "kem_ct"],
1154
- `slot for kem='${kem}' is missing required 'kem_ct'`
1155
- )
1156
- );
1157
- } else {
1158
- const reassembled = bytesChunkArrayConcat(slot.kem_ct).length;
1159
- if (reassembled !== descriptor.fieldLength) {
1160
- errors.push(
1161
- issue(
1162
- KEM_FIELD_LENGTH_CODE.kem_ct,
1163
- [...slotPath, "kem_ct"],
1164
- `slot.kem_ct reassembles to ${reassembled} bytes != ${descriptor.fieldLength} for ${kem}`
1165
- )
1166
- );
1167
- }
1168
- }
1169
- }
1170
- if (slot.wrap === void 0) {
1171
- errors.push(
1172
- issue(
1173
- "ENC_SLOT_INVALID_SHAPE",
1174
- [...slotPath, "wrap"],
1175
- `slot for kem='${kem}' is missing required 'wrap'`
1176
- )
1177
- );
1178
- } else if (slot.wrap.length !== descriptor.wrapLength) {
1179
- errors.push(
1180
- issue(
1181
- "WRAP_LENGTH_MISMATCH",
1182
- [...slotPath, "wrap"],
1183
- `slot.wrap length ${slot.wrap.length} != ${descriptor.wrapLength}`
1184
- )
1185
- );
816
+ function polySub(a_, b_) {
817
+ const a = a_;
818
+ const b = b_;
819
+ for (let i = 0; i < N; i++)
820
+ a[i] = crystals.mod(a[i] - b[i]);
821
+ }
822
+ function BaseCaseMultiply(a0, a1, b0, b1, zeta) {
823
+ const c0 = crystals.mod(a1 * b1 * zeta + a0 * b0);
824
+ const c1 = crystals.mod(a0 * b1 + a1 * b0);
825
+ return { c0, c1 };
826
+ }
827
+ function MultiplyNTTs(f_, g_) {
828
+ const f = f_;
829
+ const g = g_;
830
+ for (let i = 0; i < N / 2; i++) {
831
+ let z3 = crystals.nttZetas[64 + (i >> 1)];
832
+ if (i & 1)
833
+ z3 = -z3;
834
+ const { c0, c1 } = BaseCaseMultiply(f[2 * i + 0], f[2 * i + 1], g[2 * i + 0], g[2 * i + 1], z3);
835
+ f[2 * i + 0] = c0;
836
+ f[2 * i + 1] = c1;
1186
837
  }
838
+ return f;
1187
839
  }
1188
- function rawSlotKeySets(rawEnc) {
1189
- const slots = mapLikeGet(rawEnc, "slots");
1190
- if (!Array.isArray(slots)) return [];
1191
- return slots.map((slot) => {
1192
- const keys = /* @__PURE__ */ new Set();
1193
- if (slot instanceof Map) {
1194
- for (const k of slot.keys()) if (typeof k === "string") keys.add(k);
1195
- } else if (typeof slot === "object" && slot !== null) {
1196
- for (const k of Object.keys(slot)) keys.add(k);
840
+ function SampleNTT(xof_) {
841
+ const xof = xof_;
842
+ const r = new Uint16Array(N);
843
+ for (let j = 0; j < N; ) {
844
+ const b = xof();
845
+ if (b.length % 3)
846
+ throw new Error("SampleNTT: unaligned block");
847
+ for (let i = 0; j < N && i + 3 <= b.length; i += 3) {
848
+ const d1 = (b[i + 0] >> 0 | b[i + 1] << 8) & 4095;
849
+ const d2 = (b[i + 1] >> 4 | b[i + 2] << 4) & 4095;
850
+ if (d1 < Q)
851
+ r[j++] = d1;
852
+ if (j < N && d2 < Q)
853
+ r[j++] = d2;
1197
854
  }
1198
- return keys;
1199
- });
1200
- }
1201
- function mapLikeGet(value, key) {
1202
- if (value instanceof Map) return value.get(key);
1203
- if (typeof value === "object" && value !== null) {
1204
- return value[key];
1205
- }
1206
- return void 0;
1207
- }
1208
- function checkMerkleCommit(commit, idx, errors) {
1209
- const basePath = ["merkle", idx];
1210
- if (!(commit.alg in MERKLE_COMMIT_ALG_LENGTHS)) {
1211
- errors.push(
1212
- issue(
1213
- "UNSUPPORTED_MERKLE_COMMIT_ALG",
1214
- [...basePath, "alg"],
1215
- `unknown merkle commitment alg: ${commit.alg}`
1216
- )
1217
- );
1218
- return;
1219
- }
1220
- const expected = MERKLE_COMMIT_ALG_LENGTHS[commit.alg];
1221
- if (commit.root.length !== expected) {
1222
- errors.push(
1223
- issue(
1224
- "HASH_DIGEST_LENGTH_MISMATCH",
1225
- [...basePath, "root"],
1226
- `merkle entry root length ${commit.root.length} != ${expected} for ${commit.alg}`
1227
- )
1228
- );
1229
- }
1230
- if (commit.uris) {
1231
- checkItemUris(commit.uris, [...basePath, "uris"], errors);
1232
855
  }
856
+ return r;
1233
857
  }
1234
- function checkSigEntry(entry, idx, errors, info) {
1235
- if (entry.cose_key !== void 0) {
1236
- const keyIssue = inspectCoseKey(entry.cose_key, idx);
1237
- if (keyIssue !== null) {
1238
- errors.push(keyIssue);
1239
- return;
858
+ var sampleCBDBytes = (buf, eta) => {
859
+ const r = new Uint16Array(N);
860
+ const b32 = u32(buf);
861
+ swap32IfBE(b32);
862
+ let len = 0;
863
+ for (let i = 0, p = 0, bb = 0, t0 = 0; i < b32.length; i++) {
864
+ let b = b32[i];
865
+ for (let j = 0; j < 32; j++) {
866
+ bb += b & 1;
867
+ b >>= 1;
868
+ len += 1;
869
+ if (len === eta) {
870
+ t0 = bb;
871
+ bb = 0;
872
+ } else if (len === 2 * eta) {
873
+ r[p++] = crystals.mod(t0 - bb);
874
+ bb = 0;
875
+ len = 0;
876
+ }
1240
877
  }
1241
878
  }
1242
- const merged = bytesChunkArrayConcat(entry.cose_sign1);
1243
- let cose;
1244
- try {
1245
- cose = decodeCoseSign1(merged);
1246
- } catch (cause) {
1247
- errors.push(
1248
- issue(
1249
- "MALFORMED_SIG_COSE_SIGN1",
1250
- ["sigs", idx],
1251
- cause instanceof CoseVerifyError || cause instanceof Error ? cause.message : String(cause)
1252
- )
1253
- );
1254
- return;
1255
- }
1256
- if (cose.payload !== null) {
1257
- errors.push(
1258
- issue(
1259
- "MALFORMED_SIG_COSE_SIGN1",
1260
- ["sigs", idx],
1261
- "COSE_Sign1 payload must be null (detached); attached form forbidden"
1262
- )
1263
- );
1264
- return;
1265
- }
1266
- const alg = cose.protectedHeader.get(1);
1267
- if (typeof alg !== "number" || !KNOWN_SIG_ALG_IDS.has(alg)) {
1268
- info.push(
1269
- issue(
1270
- "SIGNATURE_UNSUPPORTED",
1271
- ["sigs", idx],
1272
- `COSE_Sign1 protected alg ${String(alg)} not in {-8, -19}`
1273
- )
1274
- );
1275
- }
1276
- const protectedKid = cose.protectedHeader.get(4);
1277
- if (protectedKid instanceof Uint8Array && protectedKid.length === 32 && entry.cose_key !== void 0) {
1278
- errors.push(
1279
- issue(
1280
- "SIG_ENTRY_KID_COSE_KEY_CONFLICT",
1281
- ["sigs", idx],
1282
- "sigs[i] carries both a 32-byte protected `kid` (path 1) and an inline `cose_key` (path 2); paths are mutually exclusive"
1283
- )
1284
- );
1285
- }
879
+ swap32IfBE(b32);
880
+ if (len)
881
+ throw new Error(`sampleCBD: leftover bits: ${len}`);
882
+ return r;
883
+ };
884
+ function sampleCBD(PRF_, seed, nonce, eta) {
885
+ const PRF = PRF_;
886
+ return sampleCBDBytes(PRF(eta * N / 4, seed, nonce), eta);
1286
887
  }
1287
- function inspectCoseKey(keyChunks, i) {
1288
- let decoded;
1289
- try {
1290
- decoded = decodeCanonicalCbor(bytesChunkArrayConcat(keyChunks));
1291
- } catch (cause) {
1292
- return issue(
1293
- "MALFORMED_SIG_COSE_SIGN1",
1294
- ["sigs", i, "cose_key"],
1295
- `sigs[${i}].cose_key failed to decode as cbor<COSE_Key>: ${cause instanceof Error ? cause.message : String(cause)}`
1296
- );
1297
- }
1298
- const getLabel = (label) => {
1299
- if (decoded instanceof Map) return decoded.get(label);
1300
- if (typeof decoded === "object" && decoded !== null) {
1301
- return decoded[String(label)];
1302
- }
1303
- return void 0;
1304
- };
1305
- const hasLabel = (label) => {
1306
- if (decoded instanceof Map) return decoded.has(label);
1307
- if (typeof decoded === "object" && decoded !== null) {
1308
- return Object.prototype.hasOwnProperty.call(decoded, String(label));
1309
- }
1310
- return false;
1311
- };
1312
- if (hasLabel(-4)) {
1313
- return issue(
1314
- "SIG_PRIVATE_KEY_LEAKED",
1315
- ["sigs", i, "cose_key"],
1316
- "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"
1317
- );
1318
- }
1319
- const kty = getLabel(1);
1320
- if (kty !== 1) {
1321
- return issue(
1322
- "MALFORMED_SIG_COSE_SIGN1",
1323
- ["sigs", i, "cose_key"],
1324
- `sigs[${i}].cose_key COSE_Key kty (label 1) must be 1 (OKP); got ${String(kty)}`
1325
- );
1326
- }
1327
- const crv = getLabel(-1);
1328
- if (crv !== 6) {
1329
- return issue(
1330
- "MALFORMED_SIG_COSE_SIGN1",
1331
- ["sigs", i, "cose_key"],
1332
- `sigs[${i}].cose_key COSE_Key crv (label -1) must be 6 (Ed25519); got ${String(crv)}`
1333
- );
1334
- }
1335
- if (!hasLabel(-2)) {
1336
- return issue(
1337
- "MALFORMED_SIG_COSE_SIGN1",
1338
- ["sigs", i, "cose_key"],
1339
- `sigs[${i}].cose_key COSE_Key missing label -2 (Ed25519 public-key bytes)`
1340
- );
1341
- }
1342
- const x = getLabel(-2);
1343
- if (!(x instanceof Uint8Array) || x.length !== 32) {
1344
- const got = x instanceof Uint8Array ? `${x.length}-byte bstr` : typeof x;
1345
- return issue(
1346
- "MALFORMED_SIG_COSE_SIGN1",
1347
- ["sigs", i, "cose_key"],
1348
- `sigs[${i}].cose_key COSE_Key label -2 must be a 32-byte byte string (Ed25519 public key); got ${got}`
1349
- );
888
+ var genKPKE = (opts_) => {
889
+ const opts2 = opts_;
890
+ const { K, PRF, XOF, HASH512, ETA1, ETA2, du, dv } = opts2;
891
+ const poly1 = polyCoder(1);
892
+ const polyV = polyCoder(dv);
893
+ const polyU = polyCoder(du);
894
+ const publicCoder = splitCoder("publicKey", vecCoder(polyCoder(12), K), 32);
895
+ const secretCoder = vecCoder(polyCoder(12), K);
896
+ const cipherCoder = splitCoder("ciphertext", vecCoder(polyU, K), polyV);
897
+ const seedCoder = splitCoder("seed", 32, 32);
898
+ return {
899
+ secretCoder,
900
+ lengths: {
901
+ secretKey: secretCoder.bytesLen,
902
+ publicKey: publicCoder.bytesLen,
903
+ cipherText: cipherCoder.bytesLen
904
+ },
905
+ keygen: (seed) => {
906
+ abytesDoc(seed, 32, "seed");
907
+ const seedDst = new Uint8Array(33);
908
+ seedDst.set(seed);
909
+ seedDst[32] = K;
910
+ const seedHash = HASH512(seedDst);
911
+ const [rho, sigma] = seedCoder.decode(seedHash);
912
+ const sHat = [];
913
+ const tHat = [];
914
+ for (let i = 0; i < K; i++)
915
+ sHat.push(crystals.NTT.encode(sampleCBD(PRF, sigma, i, ETA1)));
916
+ const x = XOF(rho);
917
+ for (let i = 0; i < K; i++) {
918
+ const e = crystals.NTT.encode(sampleCBD(PRF, sigma, K + i, ETA1));
919
+ for (let j = 0; j < K; j++) {
920
+ const aji = SampleNTT(x.get(j, i));
921
+ polyAdd(e, MultiplyNTTs(aji, sHat[j]));
922
+ }
923
+ tHat.push(e);
924
+ }
925
+ x.clean();
926
+ const res = {
927
+ publicKey: publicCoder.encode([tHat, rho]),
928
+ secretKey: secretCoder.encode(sHat)
929
+ };
930
+ cleanBytes(rho, sigma, sHat, tHat, seedDst, seedHash);
931
+ return res;
932
+ },
933
+ encrypt: (publicKey, msg, seed) => {
934
+ const [tHat, rho] = publicCoder.decode(publicKey);
935
+ const rHat = [];
936
+ for (let i = 0; i < K; i++)
937
+ rHat.push(crystals.NTT.encode(sampleCBD(PRF, seed, i, ETA1)));
938
+ const x = XOF(rho);
939
+ const tmp2 = new Uint16Array(N);
940
+ const u = [];
941
+ for (let i = 0; i < K; i++) {
942
+ const e1 = sampleCBD(PRF, seed, K + i, ETA2);
943
+ const tmp = new Uint16Array(N);
944
+ for (let j = 0; j < K; j++) {
945
+ const aij = SampleNTT(x.get(i, j));
946
+ polyAdd(tmp, MultiplyNTTs(aij, rHat[j]));
947
+ }
948
+ polyAdd(e1, crystals.NTT.decode(tmp));
949
+ u.push(e1);
950
+ polyAdd(tmp2, MultiplyNTTs(tHat[i], rHat[i]));
951
+ cleanBytes(tmp);
952
+ }
953
+ x.clean();
954
+ const e2 = sampleCBD(PRF, seed, 2 * K, ETA2);
955
+ polyAdd(e2, crystals.NTT.decode(tmp2));
956
+ const v = poly1.decode(msg);
957
+ polyAdd(v, e2);
958
+ cleanBytes(tHat, rHat, tmp2, e2);
959
+ return cipherCoder.encode([u, v]);
960
+ },
961
+ decrypt: (cipherText, privateKey) => {
962
+ const [u, v] = cipherCoder.decode(cipherText);
963
+ const sk = secretCoder.decode(privateKey);
964
+ const tmp = new Uint16Array(N);
965
+ for (let i = 0; i < K; i++)
966
+ polyAdd(tmp, MultiplyNTTs(sk[i], crystals.NTT.encode(u[i])));
967
+ polySub(v, crystals.NTT.decode(tmp));
968
+ cleanBytes(tmp, sk, u);
969
+ return poly1.encode(v);
970
+ }
971
+ };
972
+ };
973
+ function createKyber(opts2) {
974
+ const rawOpts = opts2;
975
+ const KPKE = genKPKE(rawOpts);
976
+ const { HASH256, HASH512, KDF } = rawOpts;
977
+ const { secretCoder: KPKESecretCoder, lengths } = KPKE;
978
+ const secretCoder = splitCoder("secretKey", lengths.secretKey, lengths.publicKey, 32, 32);
979
+ const msgLen = 32;
980
+ const seedLen = 64;
981
+ const kemLengths = Object.freeze({
982
+ ...lengths,
983
+ seed: 64,
984
+ msg: msgLen,
985
+ msgRand: msgLen,
986
+ secretKey: secretCoder.bytesLen
987
+ });
988
+ return Object.freeze({
989
+ info: Object.freeze({ type: "ml-kem" }),
990
+ lengths: kemLengths,
991
+ keygen: (seed = randomBytes(seedLen)) => {
992
+ abytesDoc(seed, seedLen, "seed");
993
+ const { publicKey, secretKey: sk } = KPKE.keygen(seed.subarray(0, 32));
994
+ const publicKeyHash = HASH256(publicKey);
995
+ const secretKey = secretCoder.encode([sk, publicKey, publicKeyHash, seed.subarray(32)]);
996
+ cleanBytes(sk, publicKeyHash);
997
+ return {
998
+ publicKey,
999
+ secretKey
1000
+ };
1001
+ },
1002
+ getPublicKey: (secretKey) => {
1003
+ const [_sk, publicKey, _publicKeyHash, _z] = secretCoder.decode(secretKey);
1004
+ return Uint8Array.from(publicKey);
1005
+ },
1006
+ encapsulate: (publicKey, msg = randomBytes(msgLen)) => {
1007
+ abytesDoc(publicKey, lengths.publicKey, "publicKey");
1008
+ abytesDoc(msg, msgLen, "message");
1009
+ const eke = publicKey.subarray(0, 384 * opts2.K);
1010
+ const ek = KPKESecretCoder.encode(KPKESecretCoder.decode(copyBytes(eke)));
1011
+ if (!equalBytes(ek, eke)) {
1012
+ cleanBytes(ek);
1013
+ throw new Error("ML-KEM.encapsulate: wrong publicKey modulus");
1014
+ }
1015
+ cleanBytes(ek);
1016
+ const kr = HASH512.create().update(msg).update(HASH256(publicKey)).digest();
1017
+ const cipherText = KPKE.encrypt(publicKey, msg, kr.subarray(32, 64));
1018
+ cleanBytes(kr.subarray(32));
1019
+ return {
1020
+ cipherText,
1021
+ sharedSecret: kr.subarray(0, 32)
1022
+ };
1023
+ },
1024
+ decapsulate: (cipherText, secretKey) => {
1025
+ abytesDoc(secretKey, secretCoder.bytesLen, "secretKey");
1026
+ abytesDoc(cipherText, lengths.cipherText, "cipherText");
1027
+ const k768 = secretCoder.bytesLen - 96;
1028
+ const start = k768 + 32;
1029
+ const test = HASH256(secretKey.subarray(k768 / 2, start));
1030
+ if (!equalBytes(test, secretKey.subarray(start, start + 32)))
1031
+ throw new Error("invalid secretKey: hash check failed");
1032
+ const [sk, publicKey, publicKeyHash, z3] = secretCoder.decode(secretKey);
1033
+ const msg = KPKE.decrypt(cipherText, sk);
1034
+ const kr = HASH512.create().update(msg).update(publicKeyHash).digest();
1035
+ const Khat = kr.subarray(0, 32);
1036
+ const cipherText2 = KPKE.encrypt(publicKey, msg, kr.subarray(32, 64));
1037
+ const isValid = equalBytes(cipherText, cipherText2);
1038
+ const Kbar = KDF.create({ dkLen: 32 }).update(z3).update(cipherText).digest();
1039
+ cleanBytes(msg, cipherText2, !isValid ? Khat : Kbar);
1040
+ return isValid ? Khat : Kbar;
1041
+ }
1042
+ });
1043
+ }
1044
+ function shakePRF(dkLen, key, nonce) {
1045
+ return shake256.create({ dkLen }).update(key).update(new Uint8Array([nonce])).digest();
1046
+ }
1047
+ var opts = /* @__PURE__ */ (() => ({
1048
+ HASH256: sha3_256,
1049
+ HASH512: sha3_512,
1050
+ KDF: shake256,
1051
+ XOF: XOF128,
1052
+ PRF: shakePRF
1053
+ }))();
1054
+ var mk = (params) => createKyber({
1055
+ ...opts,
1056
+ ...params
1057
+ });
1058
+ var ml_kem768 = /* @__PURE__ */ (() => mk(PARAMS[768]))();
1059
+
1060
+ // ../../node_modules/.pnpm/@noble+post-quantum@0.6.1/node_modules/@noble/post-quantum/hybrid.js
1061
+ function ecKeygen(curve, allowZeroKey = false) {
1062
+ const lengths = curve.lengths;
1063
+ let keygen = curve.keygen;
1064
+ if (allowZeroKey) {
1065
+ if (!("getSharedSecret" in curve && "sign" in curve && "verify" in curve))
1066
+ throw new Error("allowZeroKey requires a Weierstrass curve");
1067
+ const wCurve = curve;
1068
+ const Fn = wCurve.Point.Fn;
1069
+ keygen = (seed = randomBytes(lengths.seed)) => {
1070
+ abytes(seed, lengths.seed, "seed");
1071
+ const seedScalar = Fn.isLE ? bytesToNumberLE(seed) : bytesToNumberBE(seed);
1072
+ const secretKey = Fn.toBytes(Fn.create(seedScalar));
1073
+ return {
1074
+ secretKey,
1075
+ publicKey: curve.getPublicKey(secretKey)
1076
+ };
1077
+ };
1350
1078
  }
1351
- return null;
1079
+ return {
1080
+ lengths: { secretKey: lengths.secretKey, publicKey: lengths.publicKey, seed: lengths.seed },
1081
+ keygen: (seed) => keygen(seed),
1082
+ getPublicKey: (secretKey) => curve.getPublicKey(secretKey)
1083
+ };
1352
1084
  }
1353
- var ACCEPTED_CIDV1_MULTIBASE = /* @__PURE__ */ new Set(["b", "B", "f", "F", "z"]);
1354
- var ACCEPTED_MULTICODECS = /* @__PURE__ */ new Set([85, 112, 113]);
1355
- var ACCEPTED_MULTIHASHES = /* @__PURE__ */ new Map([
1356
- [18, 32],
1357
- [45600, 32]
1358
- ]);
1359
- function validateCidProfile(cid) {
1360
- if (cid.length === 0) return false;
1361
- if (cid.startsWith("Qm")) {
1362
- let decoded;
1085
+ function ecdhKem(curve, allowZeroKey = false) {
1086
+ const kg = ecKeygen(curve, allowZeroKey);
1087
+ if (!curve.getSharedSecret)
1088
+ throw new Error("wrong curve");
1089
+ return {
1090
+ lengths: { ...kg.lengths, msg: kg.lengths.seed, cipherText: kg.lengths.publicKey },
1091
+ keygen: kg.keygen,
1092
+ getPublicKey: kg.getPublicKey,
1093
+ encapsulate(publicKey, rand = randomBytes(curve.lengths.seed)) {
1094
+ const seed = copyBytes(rand);
1095
+ let ek = void 0;
1096
+ try {
1097
+ ek = this.keygen(seed).secretKey;
1098
+ const sharedSecret = this.decapsulate(publicKey, ek);
1099
+ const cipherText = curve.getPublicKey(ek);
1100
+ return { sharedSecret, cipherText };
1101
+ } finally {
1102
+ cleanBytes(seed);
1103
+ if (ek)
1104
+ cleanBytes(ek);
1105
+ }
1106
+ },
1107
+ decapsulate(cipherText, secretKey) {
1108
+ const res = curve.getSharedSecret(secretKey, cipherText);
1109
+ return curve.lengths.publicKeyHasPrefix ? res.subarray(1) : res;
1110
+ }
1111
+ };
1112
+ }
1113
+ function splitLengths(lst, name) {
1114
+ return splitCoder(name, ...lst.map((i) => {
1115
+ if (typeof i.lengths[name] !== "number")
1116
+ throw new Error("wrong length: " + name);
1117
+ return i.lengths[name];
1118
+ }));
1119
+ }
1120
+ function expandSeedXof(xof) {
1121
+ return ((seed, seedLen) => xof(seed, { dkLen: seedLen }));
1122
+ }
1123
+ function combineKeys(realSeedLen, expandSeed_, ...ck_) {
1124
+ const expandSeed = expandSeed_;
1125
+ const ck = ck_;
1126
+ const seedCoder = splitLengths(ck, "seed");
1127
+ const pkCoder = splitLengths(ck, "publicKey");
1128
+ anumber(realSeedLen);
1129
+ function expandDecapsulationKey(seed) {
1130
+ abytes(seed, realSeedLen);
1131
+ const expandedRaw = expandSeed(seed, seedCoder.bytesLen);
1132
+ const expandedSeed = expandedRaw.buffer === seed.buffer ? copyBytes(expandedRaw) : expandedRaw;
1133
+ const expanded = [];
1134
+ const keySecret = [];
1135
+ const secretKey = [];
1136
+ const publicKey = [];
1137
+ let ok = false;
1363
1138
  try {
1364
- decoded = decodeBase58btc(cid);
1365
- } catch {
1366
- return false;
1139
+ for (const part of seedCoder.decode(expandedSeed))
1140
+ expanded.push(copyBytes(part));
1141
+ for (let i = 0; i < ck.length; i++) {
1142
+ const keys = ck[i].keygen(expanded[i]);
1143
+ keySecret.push(keys.secretKey);
1144
+ secretKey.push(copyBytes(keys.secretKey));
1145
+ publicKey.push(keys.publicKey);
1146
+ }
1147
+ ok = true;
1148
+ return { secretKey, publicKey };
1149
+ } finally {
1150
+ cleanBytes(expandedSeed, expanded, keySecret);
1151
+ if (!ok)
1152
+ cleanBytes(secretKey);
1367
1153
  }
1368
- return decoded.length === 34 && decoded[0] === 18 && decoded[1] === 32;
1369
1154
  }
1370
- const mbPrefix = cid[0];
1371
- if (!ACCEPTED_CIDV1_MULTIBASE.has(mbPrefix)) return false;
1372
- let bytes;
1155
+ return {
1156
+ info: { lengths: { seed: realSeedLen, publicKey: pkCoder.bytesLen, secretKey: realSeedLen } },
1157
+ getPublicKey(secretKey) {
1158
+ return this.keygen(secretKey).publicKey;
1159
+ },
1160
+ keygen(seed = randomBytes(realSeedLen)) {
1161
+ const { publicKey: pk, secretKey } = expandDecapsulationKey(seed);
1162
+ try {
1163
+ const publicKey = pkCoder.encode(pk);
1164
+ return { secretKey: seed, publicKey };
1165
+ } finally {
1166
+ cleanBytes(pk);
1167
+ cleanBytes(secretKey);
1168
+ }
1169
+ },
1170
+ expandDecapsulationKey,
1171
+ realSeedLen
1172
+ };
1173
+ }
1174
+ function combineKEMS(realSeedLen, realMsgLen, expandSeed, combiner, ...kems) {
1175
+ const rawCombiner = combiner;
1176
+ const rawKems = kems;
1177
+ const keys = combineKeys(realSeedLen, expandSeed, ...rawKems);
1178
+ const ctCoder = splitLengths(rawKems, "cipherText");
1179
+ const pkCoder = splitLengths(rawKems, "publicKey");
1180
+ const msgCoder = splitLengths(rawKems, "msg");
1181
+ anumber(realMsgLen);
1182
+ const lengths = Object.freeze({
1183
+ ...keys.info.lengths,
1184
+ msg: realMsgLen,
1185
+ msgRand: msgCoder.bytesLen,
1186
+ cipherText: ctCoder.bytesLen
1187
+ });
1188
+ return Object.freeze({
1189
+ lengths,
1190
+ getPublicKey: keys.getPublicKey,
1191
+ keygen: keys.keygen,
1192
+ encapsulate(pk, randomness = randomBytes(msgCoder.bytesLen)) {
1193
+ const pks = pkCoder.decode(pk);
1194
+ const rand = msgCoder.decode(randomness);
1195
+ const sharedSecret = [];
1196
+ const cipherText = [];
1197
+ try {
1198
+ for (let i = 0; i < rawKems.length; i++) {
1199
+ const enc = rawKems[i].encapsulate(pks[i], rand[i]);
1200
+ sharedSecret.push(enc.sharedSecret);
1201
+ cipherText.push(enc.cipherText);
1202
+ }
1203
+ return {
1204
+ // Detach the combiner result before cleanup: a caller-provided combiner may alias one of
1205
+ // the child sharedSecret buffers, and those child buffers are zeroized immediately below.
1206
+ sharedSecret: copyBytes(rawCombiner(pks, cipherText, sharedSecret)),
1207
+ cipherText: ctCoder.encode(cipherText)
1208
+ };
1209
+ } finally {
1210
+ cleanBytes(sharedSecret, cipherText);
1211
+ }
1212
+ },
1213
+ decapsulate(ct, seed) {
1214
+ const cts = ctCoder.decode(ct);
1215
+ const { publicKey, secretKey } = keys.expandDecapsulationKey(seed);
1216
+ const sharedSecret = rawKems.map((i, j) => i.decapsulate(cts[j], secretKey[j]));
1217
+ try {
1218
+ return copyBytes(rawCombiner(publicKey, cts, sharedSecret));
1219
+ } finally {
1220
+ cleanBytes(secretKey, sharedSecret);
1221
+ }
1222
+ }
1223
+ });
1224
+ }
1225
+ var x25519kem = /* @__PURE__ */ ecdhKem(x25519);
1226
+ var ml_kem768_x25519 = /* @__PURE__ */ (() => combineKEMS(
1227
+ 32,
1228
+ 32,
1229
+ expandSeedXof(shake256),
1230
+ // Awesome label, so much escaping hell in a single line.
1231
+ (pk, ct, ss) => sha3_256(concatBytes$1(ss[0], ss[1], ct[1], pk[1], asciiToBytes("\\.//^\\"))),
1232
+ ml_kem768,
1233
+ x25519kem
1234
+ ))();
1235
+ var XWing = /* @__PURE__ */ (() => ml_kem768_x25519)();
1236
+ var AeadVerificationError = class extends Error {
1237
+ code = "aead_verification_failed";
1238
+ constructor(message, options) {
1239
+ super(message, options);
1240
+ this.name = "AeadVerificationError";
1241
+ }
1242
+ };
1243
+ function chacha20Poly1305Decrypt(opts2) {
1373
1244
  try {
1374
- bytes = decodeMultibase(mbPrefix, cid.slice(1));
1375
- } catch {
1376
- return false;
1245
+ return chacha20poly1305(opts2.key, opts2.nonce, opts2.aad).decrypt(opts2.ciphertext);
1246
+ } catch (cause) {
1247
+ throw new AeadVerificationError("chacha20-poly1305 decrypt failed", { cause });
1248
+ }
1249
+ }
1250
+ function xchacha20Poly1305Decrypt(opts2) {
1251
+ try {
1252
+ return xchacha20poly1305(opts2.key, opts2.nonce, opts2.aad).decrypt(opts2.ciphertext);
1253
+ } catch (cause) {
1254
+ throw new AeadVerificationError("xchacha20-poly1305 decrypt failed", { cause });
1255
+ }
1256
+ }
1257
+ function hkdfSha256(opts2) {
1258
+ return hkdf(sha256, opts2.ikm, opts2.salt, opts2.info, opts2.length);
1259
+ }
1260
+ var MLKEM768X25519_ENC_LENGTH = 1120;
1261
+ var MLKEM768X25519_SEED_LENGTH = 32;
1262
+ function mlkem768x25519Keygen(seed) {
1263
+ if (seed.length !== MLKEM768X25519_SEED_LENGTH) {
1264
+ throw new Error(
1265
+ `mlkem768x25519 seed must be ${MLKEM768X25519_SEED_LENGTH} bytes, got ${seed.length}`
1266
+ );
1267
+ }
1268
+ const { secretKey, publicKey } = XWing.keygen(seed);
1269
+ return { secretSeed: secretKey, publicKey };
1270
+ }
1271
+ function mlkem768x25519Decapsulate(opts2) {
1272
+ if (opts2.secretSeed.length !== MLKEM768X25519_SEED_LENGTH) {
1273
+ throw new Error(
1274
+ `mlkem768x25519 secret seed must be ${MLKEM768X25519_SEED_LENGTH} bytes, got ${opts2.secretSeed.length}`
1275
+ );
1276
+ }
1277
+ if (opts2.enc.length !== MLKEM768X25519_ENC_LENGTH) {
1278
+ throw new Error(
1279
+ `mlkem768x25519 enc must be ${MLKEM768X25519_ENC_LENGTH} bytes, got ${opts2.enc.length}`
1280
+ );
1281
+ }
1282
+ return XWing.decapsulate(opts2.enc, opts2.secretSeed);
1283
+ }
1284
+ var X25519LowOrderPointError = class extends Error {
1285
+ code = "X25519_LOW_ORDER_POINT";
1286
+ constructor(options) {
1287
+ super("x25519 ECDH rejected: peer public key is a small-order point", options);
1288
+ this.name = "X25519LowOrderPointError";
1289
+ }
1290
+ };
1291
+ var NOBLE_LOW_ORDER_MESSAGE = "invalid private or public key received";
1292
+ function x25519PublicKey(opts2) {
1293
+ return x25519.getPublicKey(opts2.secretKey);
1294
+ }
1295
+ function x25519Ecdh(opts2) {
1296
+ try {
1297
+ return x25519.getSharedSecret(opts2.secretKey, opts2.theirPublicKey);
1298
+ } catch (e) {
1299
+ if (e instanceof Error && e.message === NOBLE_LOW_ORDER_MESSAGE) {
1300
+ throw new X25519LowOrderPointError({ cause: e });
1301
+ }
1302
+ throw e;
1303
+ }
1304
+ }
1305
+ var EciesSealedPoeError = class extends Error {
1306
+ code;
1307
+ constructor(code, message, options) {
1308
+ super(message, options);
1309
+ this.name = "EciesSealedPoeError";
1310
+ this.code = code;
1311
+ }
1312
+ };
1313
+ var CHUNK_MAX_BYTES = 64;
1314
+ function chunkKemCt(value) {
1315
+ if (value.length === 0) {
1316
+ throw new Error("chunkKemCt: refusing to chunk an empty byte string");
1317
+ }
1318
+ const chunks = [];
1319
+ for (let i = 0; i < value.length; i += CHUNK_MAX_BYTES) {
1320
+ chunks.push(value.subarray(i, Math.min(i + CHUNK_MAX_BYTES, value.length)));
1321
+ }
1322
+ return chunks;
1323
+ }
1324
+ function joinKemCt(chunks) {
1325
+ let total = 0;
1326
+ for (const c of chunks) total += c.length;
1327
+ const out = new Uint8Array(total);
1328
+ let offset = 0;
1329
+ for (const c of chunks) {
1330
+ out.set(c, offset);
1331
+ offset += c.length;
1377
1332
  }
1378
- if (bytes.length < 4) return false;
1379
- const versionParse = readVarint(bytes, 0);
1380
- if (versionParse === null || versionParse.value !== 1) return false;
1381
- const codecParse = readVarint(bytes, versionParse.next);
1382
- if (codecParse === null) return false;
1383
- if (!ACCEPTED_MULTICODECS.has(codecParse.value)) return false;
1384
- const mhParse = readVarint(bytes, codecParse.next);
1385
- if (mhParse === null) return false;
1386
- const lenParse = readVarint(bytes, mhParse.next);
1387
- if (lenParse === null) return false;
1388
- const digestLen = lenParse.value;
1389
- const expectedLen = ACCEPTED_MULTIHASHES.get(mhParse.value);
1390
- if (expectedLen === void 0 || digestLen !== expectedLen) return false;
1391
- if (lenParse.next + digestLen !== bytes.length) return false;
1392
- return true;
1333
+ return out;
1393
1334
  }
1394
- function readVarint(bytes, start) {
1395
- let value = 0;
1396
- let shift = 0;
1397
- let i = start;
1398
- while (i < bytes.length) {
1399
- const b = bytes[i];
1400
- value |= (b & 127) << shift;
1401
- i++;
1402
- if ((b & 128) === 0) return { value, next: i };
1403
- shift += 7;
1404
- if (shift > 28) return null;
1335
+ function canonicalizeSlots(slots, kem) {
1336
+ if (kem === "x25519") {
1337
+ return slots.map((s) => ({ epk: s.epk, wrap: s.wrap }));
1405
1338
  }
1406
- return null;
1339
+ return slots.map((s) => ({
1340
+ kem_ct: chunkKemCt(joinKemCt(s.kem_ct)),
1341
+ wrap: s.wrap
1342
+ }));
1407
1343
  }
1408
- function decodeMultibase(prefix, body) {
1409
- switch (prefix) {
1410
- case "b":
1411
- return decodeBase32(body.toLowerCase(), "rfc4648-lower");
1412
- case "B":
1413
- return decodeBase32(body.toUpperCase(), "rfc4648-upper");
1414
- case "f":
1415
- return decodeBase16(body.toLowerCase());
1416
- case "F":
1417
- return decodeBase16(body.toUpperCase());
1418
- case "z":
1419
- return decodeBase58btc(body);
1420
- default:
1421
- throw new Error(`unsupported multibase prefix ${prefix}`);
1344
+ function encodeCanonicalCbor3(value) {
1345
+ return encode(value, {
1346
+ cde: true,
1347
+ collapseBigInts: true,
1348
+ rejectDuplicateKeys: true,
1349
+ sortKeys: sortCoreDeterministic
1350
+ });
1351
+ }
1352
+ var CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX = new TextEncoder().encode(
1353
+ "cardano-poe-slots-transcript-v1"
1354
+ );
1355
+ var CARDANO_POE_HKDF_INFO_PAYLOAD = new TextEncoder().encode(
1356
+ "cardano-poe-payload-v1"
1357
+ );
1358
+ var CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE = new TextEncoder().encode(
1359
+ "cardano-poe-payload-passphrase-v1"
1360
+ );
1361
+ var CARDANO_POE_XWING_KEK_SALT_PREFIX = new TextEncoder().encode(
1362
+ "cardano-poe-xwing-kek-salt-v1"
1363
+ );
1364
+ if (CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length !== 31) {
1365
+ throw new Error(
1366
+ "CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX byte-length invariant violated (expected 31)"
1367
+ );
1368
+ }
1369
+ if (CARDANO_POE_HKDF_INFO_PAYLOAD.length !== 22) {
1370
+ throw new Error("CARDANO_POE_HKDF_INFO_PAYLOAD byte-length invariant violated (expected 22)");
1371
+ }
1372
+ if (CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE.length !== 33) {
1373
+ throw new Error(
1374
+ "CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE byte-length invariant violated (expected 33)"
1375
+ );
1376
+ }
1377
+ if (CARDANO_POE_XWING_KEK_SALT_PREFIX.length !== 29) {
1378
+ throw new Error("CARDANO_POE_XWING_KEK_SALT_PREFIX byte-length invariant violated (expected 29)");
1379
+ }
1380
+ var CARDANO_POE_PW_NORM_PROFILE = "cardano-poe-pw-norm-v1";
1381
+ var MAX_SLOTS = 1024;
1382
+ var MAX_DECODED_ENVELOPE_BYTES = 65536;
1383
+ var MAX_SEALED_PLAINTEXT = 274877906880;
1384
+ var MAX_SEALED_CIPHERTEXT = MAX_SEALED_PLAINTEXT + 16;
1385
+ function assertCiphertextWithinBound(ciphertextLength) {
1386
+ if (ciphertextLength >= MAX_SEALED_CIPHERTEXT) {
1387
+ throw new SealedPayloadTooLargeError(
1388
+ `ciphertext length ${ciphertextLength} is at or above the maximum sealed ciphertext size ${MAX_SEALED_CIPHERTEXT}`
1389
+ );
1422
1390
  }
1423
1391
  }
1424
- var BASE16_LOWER = "0123456789abcdef";
1425
- var BASE16_UPPER = "0123456789ABCDEF";
1426
- function decodeBase16(s) {
1427
- if (s.length % 2 !== 0) throw new Error("base16: odd-length");
1428
- const out = new Uint8Array(s.length / 2);
1429
- const alphabet = s === s.toLowerCase() ? BASE16_LOWER : BASE16_UPPER;
1430
- for (let i = 0; i < out.length; i++) {
1431
- const hi = alphabet.indexOf(s[i * 2]);
1432
- const lo = alphabet.indexOf(s[i * 2 + 1]);
1433
- if (hi < 0 || lo < 0) throw new Error(`base16: non-hex char at ${i * 2}`);
1434
- out[i] = hi << 4 | lo;
1392
+ var SealedPayloadTooLargeError = class extends Error {
1393
+ constructor(message) {
1394
+ super(message);
1395
+ this.name = "SealedPayloadTooLargeError";
1435
1396
  }
1397
+ };
1398
+ function computeSlotsHash(args) {
1399
+ const transcript = {
1400
+ scheme: 1,
1401
+ path: "slots",
1402
+ aead: "xchacha20-poly1305",
1403
+ kem: args.kem,
1404
+ nonce: args.nonce,
1405
+ slots: canonicalizeSlots(args.slots, args.kem)
1406
+ };
1407
+ const encoded = encodeCanonicalCbor3(transcript);
1408
+ const message = new Uint8Array(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length + encoded.length);
1409
+ message.set(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX, 0);
1410
+ message.set(encoded, CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length);
1411
+ return sha256(message);
1412
+ }
1413
+ function adContentSlots(args) {
1414
+ const ad = {
1415
+ scheme: 1,
1416
+ path: "slots",
1417
+ aead: "xchacha20-poly1305",
1418
+ kem: args.kem,
1419
+ nonce: args.nonce,
1420
+ slots_hash: args.slotsHash,
1421
+ slots_mac: args.slotsMac
1422
+ };
1423
+ return encodeCanonicalCbor3(ad);
1424
+ }
1425
+ function adContentPassphrase(args) {
1426
+ const ad = {
1427
+ scheme: 1,
1428
+ path: "passphrase",
1429
+ aead: "xchacha20-poly1305",
1430
+ nonce: args.nonce,
1431
+ passphrase: {
1432
+ alg: args.passphrase.alg,
1433
+ salt: args.passphrase.salt,
1434
+ params: {
1435
+ m: args.passphrase.params.m,
1436
+ t: args.passphrase.params.t,
1437
+ p: args.passphrase.params.p
1438
+ },
1439
+ normalization: CARDANO_POE_PW_NORM_PROFILE
1440
+ }
1441
+ };
1442
+ return encodeCanonicalCbor3(ad);
1443
+ }
1444
+ function slotsPayloadKey(args) {
1445
+ return hkdfSha256({
1446
+ ikm: args.cek,
1447
+ salt: args.nonce,
1448
+ info: CARDANO_POE_HKDF_INFO_PAYLOAD,
1449
+ length: 32
1450
+ });
1451
+ }
1452
+ function passphrasePayloadKey(args) {
1453
+ return hkdfSha256({
1454
+ ikm: args.cek,
1455
+ salt: args.nonce,
1456
+ info: CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE,
1457
+ length: 32
1458
+ });
1459
+ }
1460
+ function xwingKekSalt(args) {
1461
+ const message = new Uint8Array(
1462
+ CARDANO_POE_XWING_KEK_SALT_PREFIX.length + args.kemCt.length + args.pubR.length
1463
+ );
1464
+ let offset = 0;
1465
+ message.set(CARDANO_POE_XWING_KEK_SALT_PREFIX, offset);
1466
+ offset += CARDANO_POE_XWING_KEK_SALT_PREFIX.length;
1467
+ message.set(args.kemCt, offset);
1468
+ offset += args.kemCt.length;
1469
+ message.set(args.pubR, offset);
1470
+ return sha256(message);
1471
+ }
1472
+ var CARDANO_POE_HKDF_INFO_KEK = new TextEncoder().encode("cardano-poe-kek-v1");
1473
+ var CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 = new TextEncoder().encode(
1474
+ "cardano-poe-kek-mlkem768x25519-v1"
1475
+ );
1476
+ var CARDANO_POE_HKDF_INFO_SLOTS_MAC = new TextEncoder().encode(
1477
+ "cardano-poe-slots-mac-v1"
1478
+ );
1479
+ var ZERO_NONCE_12 = new Uint8Array(12);
1480
+ if (CARDANO_POE_HKDF_INFO_KEK.length !== 18) {
1481
+ throw new Error("CARDANO_POE_HKDF_INFO_KEK byte-length invariant violated (expected 18)");
1482
+ }
1483
+ if (CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519.length !== 33) {
1484
+ throw new Error(
1485
+ "CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 byte-length invariant violated (expected 33)"
1486
+ );
1487
+ }
1488
+ if (CARDANO_POE_HKDF_INFO_SLOTS_MAC.length !== 24) {
1489
+ throw new Error("CARDANO_POE_HKDF_INFO_SLOTS_MAC byte-length invariant violated (expected 24)");
1490
+ }
1491
+ if (ZERO_NONCE_12.length !== 12) {
1492
+ throw new Error("ZERO_NONCE_12 byte-length invariant violated (expected 12)");
1493
+ }
1494
+ function compareCt2(a, b) {
1495
+ if (a.length !== b.length) return false;
1496
+ let diff = 0;
1497
+ for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
1498
+ return diff === 0;
1499
+ }
1500
+ function selectBundleSecrets(envelope, bundle) {
1501
+ return envelope.kem === "x25519" ? bundle.x25519PrivateKeys : bundle.mlkem768x25519SecretSeeds;
1502
+ }
1503
+ var ZERO_NONCE_122 = new Uint8Array(12);
1504
+ var EMPTY_SALT2 = new Uint8Array(0);
1505
+ var X25519_SECRET_KEY_LENGTH2 = 32;
1506
+ var X25519_PUBLIC_KEY_LENGTH2 = 32;
1507
+ var NONCE_LENGTH2 = 24;
1508
+ var WRAP_LENGTH2 = 48;
1509
+ var SLOTS_MAC_LENGTH2 = 32;
1510
+ function concat2(a, b) {
1511
+ const out = new Uint8Array(a.length + b.length);
1512
+ out.set(a, 0);
1513
+ out.set(b, a.length);
1436
1514
  return out;
1437
1515
  }
1438
- var BASE32_RFC4648_LOWER = "abcdefghijklmnopqrstuvwxyz234567";
1439
- var BASE32_RFC4648_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
1440
- function decodeBase32(s, variant) {
1441
- const alphabet = variant === "rfc4648-lower" ? BASE32_RFC4648_LOWER : BASE32_RFC4648_UPPER;
1442
- const trimmed = s.replace(/=+$/, "");
1443
- const out = [];
1444
- let buf = 0;
1445
- let bits = 0;
1446
- for (const ch of trimmed) {
1447
- const idx = alphabet.indexOf(ch);
1448
- if (idx < 0) throw new Error(`base32: invalid char '${ch}'`);
1449
- buf = buf << 5 | idx;
1450
- bits += 5;
1451
- if (bits >= 8) {
1452
- bits -= 8;
1453
- out.push(buf >> bits & 255);
1454
- }
1516
+ function bytesKey(bytes) {
1517
+ let s = "";
1518
+ for (let i = 0; i < bytes.length; i++) {
1519
+ s += String.fromCharCode(bytes[i]);
1455
1520
  }
1456
- return Uint8Array.from(out);
1521
+ return s;
1457
1522
  }
1458
- var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
1459
- function decodeBase58btc(s) {
1460
- if (s.length === 0) return new Uint8Array(0);
1461
- let zeros = 0;
1462
- while (zeros < s.length && s[zeros] === "1") zeros++;
1463
- const size = Math.floor((s.length - zeros) * 733 / 1e3) + 1;
1464
- const b256 = new Uint8Array(size);
1465
- let length = 0;
1466
- for (let i = zeros; i < s.length; i++) {
1467
- const ch = s[i];
1468
- const carryIdx = BASE58_ALPHABET.indexOf(ch);
1469
- if (carryIdx < 0) throw new Error(`base58: invalid char '${ch}'`);
1470
- let carry = carryIdx;
1471
- let k = 0;
1472
- for (let j2 = size - 1; (carry !== 0 || k < length) && j2 >= 0; j2--, k++) {
1473
- carry += 58 * b256[j2];
1474
- b256[j2] = carry % 256;
1475
- carry = Math.floor(carry / 256);
1476
- }
1477
- length = k;
1523
+ function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
1524
+ if (envelope.scheme !== 1) {
1525
+ throw new EciesSealedPoeError(
1526
+ "UNSUPPORTED_ENC_VERSION",
1527
+ `envelope.scheme=${String(envelope.scheme)} unsupported (expected 1)`
1528
+ );
1478
1529
  }
1479
- let it = size - length;
1480
- while (it < size && b256[it] === 0) it++;
1481
- const out = new Uint8Array(zeros + (size - it));
1482
- let j = zeros;
1483
- while (it < size) {
1484
- out[j++] = b256[it++];
1530
+ if (envelope.aead !== "xchacha20-poly1305") {
1531
+ throw new EciesSealedPoeError(
1532
+ "UNSUPPORTED_AEAD_ALG",
1533
+ `envelope.aead=${String(envelope.aead)} unsupported (expected 'xchacha20-poly1305')`
1534
+ );
1485
1535
  }
1486
- return out;
1487
- }
1488
- function checkCritShape(record, decodedTopKeys, errors) {
1489
- const invalid = /* @__PURE__ */ new Set();
1490
- if (!Array.isArray(record.crit)) return invalid;
1491
- if (record.crit.length === 0) {
1492
- errors.push(
1493
- issue("SCHEMA_TYPE_MISMATCH", ["crit"], "crit[] must carry at least one entry when present")
1536
+ if (envelope.kem !== "x25519" && envelope.kem !== "mlkem768x25519") {
1537
+ throw new EciesSealedPoeError(
1538
+ "UNSUPPORTED_KEM_ALG",
1539
+ `envelope.kem=${String(envelope.kem)} unsupported (expected 'x25519' or 'mlkem768x25519')`
1540
+ );
1541
+ }
1542
+ const n = envelope.slots.length;
1543
+ if (n < 1) {
1544
+ throw new EciesSealedPoeError("ENC_SLOTS_EMPTY", `envelope.slots.length=${n} must be >= 1`);
1545
+ }
1546
+ if (n > MAX_SLOTS) {
1547
+ throw new EciesSealedPoeError(
1548
+ "ENC_SLOTS_TOO_MANY",
1549
+ `envelope.slots.length=${n} exceeds MAX_SLOTS=${MAX_SLOTS}`
1550
+ );
1551
+ }
1552
+ if (envelope.nonce.length !== NONCE_LENGTH2) {
1553
+ throw new EciesSealedPoeError(
1554
+ "NONCE_LENGTH_MISMATCH",
1555
+ `envelope.nonce MUST be exactly ${NONCE_LENGTH2} bytes, got ${envelope.nonce.length}`
1556
+ );
1557
+ }
1558
+ if (envelope.slots_mac.length !== SLOTS_MAC_LENGTH2) {
1559
+ throw new EciesSealedPoeError(
1560
+ "ENC_SLOTS_MAC_INVALID_LENGTH",
1561
+ `envelope.slots_mac MUST be exactly ${SLOTS_MAC_LENGTH2} bytes, got ${envelope.slots_mac.length}`
1562
+ );
1563
+ }
1564
+ const seenKemMaterial = /* @__PURE__ */ new Set();
1565
+ if (envelope.kem === "x25519") {
1566
+ for (let i = 0; i < n; i++) {
1567
+ const slot = envelope.slots[i];
1568
+ if (slot.epk.length !== X25519_PUBLIC_KEY_LENGTH2) {
1569
+ throw new EciesSealedPoeError(
1570
+ "KEM_EPK_LENGTH_MISMATCH",
1571
+ `envelope.slots[${i}].epk MUST be exactly ${X25519_PUBLIC_KEY_LENGTH2} bytes, got ${slot.epk.length}`
1572
+ );
1573
+ }
1574
+ if (slot.wrap.length !== WRAP_LENGTH2) {
1575
+ throw new EciesSealedPoeError(
1576
+ "WRAP_LENGTH_MISMATCH",
1577
+ `envelope.slots[${i}].wrap MUST be exactly ${WRAP_LENGTH2} bytes, got ${slot.wrap.length}`
1578
+ );
1579
+ }
1580
+ const key = bytesKey(slot.epk);
1581
+ if (seenKemMaterial.has(key)) {
1582
+ throw new EciesSealedPoeError(
1583
+ "ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
1584
+ `envelope.slots[${i}].epk duplicates an earlier slot \u2014 per-slot KEK uniqueness is violated`
1585
+ );
1586
+ }
1587
+ seenKemMaterial.add(key);
1588
+ }
1589
+ } else {
1590
+ for (let i = 0; i < n; i++) {
1591
+ const slot = envelope.slots[i];
1592
+ const enc = joinKemCt(slot.kem_ct);
1593
+ if (enc.length !== MLKEM768X25519_ENC_LENGTH) {
1594
+ throw new EciesSealedPoeError(
1595
+ "KEM_CT_LENGTH_MISMATCH",
1596
+ `envelope.slots[${i}].kem_ct MUST reassemble to exactly ${MLKEM768X25519_ENC_LENGTH} bytes, got ${enc.length}`
1597
+ );
1598
+ }
1599
+ if (slot.wrap.length !== WRAP_LENGTH2) {
1600
+ throw new EciesSealedPoeError(
1601
+ "WRAP_LENGTH_MISMATCH",
1602
+ `envelope.slots[${i}].wrap MUST be exactly ${WRAP_LENGTH2} bytes, got ${slot.wrap.length}`
1603
+ );
1604
+ }
1605
+ const key = bytesKey(enc);
1606
+ if (seenKemMaterial.has(key)) {
1607
+ throw new EciesSealedPoeError(
1608
+ "ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
1609
+ `envelope.slots[${i}].kem_ct duplicates an earlier slot \u2014 per-slot KEK uniqueness is violated`
1610
+ );
1611
+ }
1612
+ seenKemMaterial.add(key);
1613
+ }
1614
+ }
1615
+ const perSlotBytes = envelope.kem === "x25519" ? X25519_PUBLIC_KEY_LENGTH2 + WRAP_LENGTH2 : MLKEM768X25519_ENC_LENGTH + WRAP_LENGTH2;
1616
+ const decodedEnvelopeBytes = NONCE_LENGTH2 + SLOTS_MAC_LENGTH2 + n * perSlotBytes;
1617
+ if (decodedEnvelopeBytes > MAX_DECODED_ENVELOPE_BYTES) {
1618
+ throw new EciesSealedPoeError(
1619
+ "ENC_ENVELOPE_TOO_LARGE",
1620
+ `decoded envelope size ${decodedEnvelopeBytes} exceeds MAX_DECODED_ENVELOPE_BYTES=${MAX_DECODED_ENVELOPE_BYTES}`
1494
1621
  );
1495
- return invalid;
1496
1622
  }
1497
- const seen = /* @__PURE__ */ new Set();
1498
- for (let i = 0; i < record.crit.length; i++) {
1499
- const critName = record.crit[i];
1500
- let reason = null;
1501
- if (TOP_LEVEL_BASE_KEYS.has(critName)) {
1502
- reason = `'${critName}' is a base key and MUST NOT appear in crit[]`;
1503
- } else if (!isExtensionKey(critName)) {
1504
- reason = `'${critName}' does not match the extension-key regex (^x-.+ or ^[a-z]+-.+)`;
1505
- } else if (!decodedTopKeys.has(critName)) {
1506
- reason = `'${critName}' is named in crit but absent from the record map`;
1507
- } else if (seen.has(critName)) {
1508
- reason = `'${critName}' appears more than once in crit[]`;
1623
+ if (multiPrivKeys !== void 0) {
1624
+ for (let i = 0; i < multiPrivKeys.length; i++) {
1625
+ if (multiPrivKeys[i].length !== X25519_SECRET_KEY_LENGTH2) {
1626
+ throw new EciesSealedPoeError(
1627
+ "INVALID_RECIPIENT_KEY",
1628
+ `recipientSecretKeys[${i}] MUST be exactly ${X25519_SECRET_KEY_LENGTH2} bytes, got ${multiPrivKeys[i].length}`
1629
+ );
1630
+ }
1509
1631
  }
1510
- seen.add(critName);
1511
- if (reason !== null) {
1512
- invalid.add(i);
1513
- errors.push(issue("CRIT_SHAPE_INVALID", ["crit", i], reason));
1632
+ } else if (singlePrivKey !== void 0) {
1633
+ if (singlePrivKey.length !== X25519_SECRET_KEY_LENGTH2) {
1634
+ throw new EciesSealedPoeError(
1635
+ "INVALID_RECIPIENT_KEY",
1636
+ `recipientSecretKey MUST be exactly ${X25519_SECRET_KEY_LENGTH2} bytes, got ${singlePrivKey.length}`
1637
+ );
1514
1638
  }
1515
1639
  }
1516
- return invalid;
1517
1640
  }
1518
- function topLevelKeysOf(decoded) {
1519
- if (decoded === null || typeof decoded !== "object") return /* @__PURE__ */ new Set();
1520
- if (decoded instanceof Map) {
1521
- const out = /* @__PURE__ */ new Set();
1522
- for (const k of decoded.keys()) {
1523
- if (typeof k === "string") out.add(k);
1524
- }
1525
- return out;
1641
+ var ZERO_IKM_32 = new Uint8Array(32);
1642
+ function tryX25519Slot(args) {
1643
+ const salt = concat2(args.slot.epk, args.pubRLocal);
1644
+ let shared;
1645
+ try {
1646
+ shared = x25519Ecdh({
1647
+ secretKey: args.recipientSecretKey,
1648
+ theirPublicKey: args.slot.epk
1649
+ });
1650
+ } catch (e) {
1651
+ if (!(e instanceof X25519LowOrderPointError)) throw e;
1652
+ hkdfSha256({ ikm: ZERO_IKM_32, salt, info: CARDANO_POE_HKDF_INFO_KEK, length: 32 });
1653
+ return null;
1526
1654
  }
1527
- return new Set(Object.keys(decoded));
1528
- }
1529
- function issue(code, path, message) {
1530
- return { code, path, message, severity: SEVERITY[code] };
1531
- }
1532
- function compareIssuePath(a, b) {
1533
- return a.path.join(".").localeCompare(b.path.join("."));
1534
- }
1535
- function valueAtPath(root, path) {
1536
- let cur = root;
1537
- for (const seg of path) {
1538
- if (cur === null || cur === void 0) return void 0;
1539
- if (cur instanceof Map) {
1540
- cur = cur.get(seg);
1541
- continue;
1542
- }
1543
- if (typeof cur !== "object") return void 0;
1544
- cur = cur[seg];
1655
+ const kek = hkdfSha256({ ikm: shared, salt, info: CARDANO_POE_HKDF_INFO_KEK, length: 32 });
1656
+ try {
1657
+ return chacha20Poly1305Decrypt({
1658
+ key: kek,
1659
+ nonce: ZERO_NONCE_122,
1660
+ aad: CARDANO_POE_HKDF_INFO_KEK,
1661
+ ciphertext: args.slot.wrap
1662
+ });
1663
+ } catch (e) {
1664
+ if (!(e instanceof AeadVerificationError)) throw e;
1665
+ return null;
1545
1666
  }
1546
- return cur;
1547
1667
  }
1548
- async function argon2idV13(opts2) {
1549
- return await argon2id({
1550
- password: opts2.password,
1551
- salt: opts2.salt,
1552
- parallelism: opts2.parallelism,
1553
- iterations: opts2.iterations,
1554
- memorySize: opts2.memSizeKB,
1555
- hashLength: opts2.outBytes,
1556
- outputType: "binary"
1668
+ function tryMlkem768X25519Slot(args) {
1669
+ const enc = joinKemCt(args.slot.kem_ct);
1670
+ const ss = mlkem768x25519Decapsulate({ secretSeed: args.recipientSecretKey, enc });
1671
+ const kek = hkdfSha256({
1672
+ ikm: ss,
1673
+ salt: xwingKekSalt({ kemCt: enc, pubR: args.pubR }),
1674
+ info: CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519,
1675
+ length: 32
1557
1676
  });
1558
- }
1559
- var AeadVerificationError = class extends Error {
1560
- code = "aead_verification_failed";
1561
- constructor(message, options) {
1562
- super(message, options);
1563
- this.name = "AeadVerificationError";
1564
- }
1565
- };
1566
- function xchacha20Poly1305Decrypt(opts2) {
1567
1677
  try {
1568
- return xchacha20poly1305(opts2.key, opts2.nonce, opts2.aad).decrypt(opts2.ciphertext);
1569
- } catch (cause) {
1570
- throw new AeadVerificationError("xchacha20-poly1305 decrypt failed", { cause });
1678
+ return chacha20Poly1305Decrypt({
1679
+ key: kek,
1680
+ nonce: ZERO_NONCE_122,
1681
+ aad: CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519,
1682
+ ciphertext: args.slot.wrap
1683
+ });
1684
+ } catch (e) {
1685
+ if (!(e instanceof AeadVerificationError)) throw e;
1686
+ return null;
1571
1687
  }
1572
1688
  }
1573
- function sha2562(input) {
1574
- return sha256(input);
1575
- }
1576
- function blake2b256(input) {
1577
- return blake2b(input, { dkLen: 32 });
1689
+ function tryRecipientUnwrapWithIdx(envelope, recipientSecretKey, constantTimeN, slotsAttemptedOut) {
1690
+ const n = envelope.slots.length;
1691
+ let cek = null;
1692
+ let matchedSlotIdx = -1;
1693
+ let cekConflict = false;
1694
+ const recordMatch = (candidate, i) => {
1695
+ if (candidate === null) return;
1696
+ if (cek === null) {
1697
+ cek = candidate;
1698
+ matchedSlotIdx = i;
1699
+ } else if (!compareCt2(candidate, cek)) {
1700
+ cekConflict = true;
1701
+ }
1702
+ };
1703
+ if (envelope.kem === "x25519") {
1704
+ const pubRLocal = x25519PublicKey({ secretKey: recipientSecretKey });
1705
+ for (let i = 0; i < n; i++) {
1706
+ if (slotsAttemptedOut !== void 0) {
1707
+ slotsAttemptedOut.count = i + 1;
1708
+ }
1709
+ recordMatch(tryX25519Slot({ slot: envelope.slots[i], recipientSecretKey, pubRLocal }), i);
1710
+ if (cek !== null && !constantTimeN) break;
1711
+ }
1712
+ } else {
1713
+ const pubR = mlkem768x25519Keygen(recipientSecretKey).publicKey;
1714
+ for (let i = 0; i < n; i++) {
1715
+ if (slotsAttemptedOut !== void 0) {
1716
+ slotsAttemptedOut.count = i + 1;
1717
+ }
1718
+ recordMatch(tryMlkem768X25519Slot({ slot: envelope.slots[i], recipientSecretKey, pubR }), i);
1719
+ if (cek !== null && !constantTimeN) break;
1720
+ }
1721
+ }
1722
+ return cek === null ? null : { cek, slotIdx: matchedSlotIdx, cekConflict };
1578
1723
  }
1579
- function blake2b2242(input) {
1580
- return blake2b(input, { dkLen: 28 });
1724
+ function slotsHashBytes(envelope) {
1725
+ return computeSlotsHash({
1726
+ kem: envelope.kem,
1727
+ nonce: envelope.nonce,
1728
+ slots: envelope.slots
1729
+ });
1581
1730
  }
1582
- var LEAF_PREFIX = 0;
1583
- var NODE_PREFIX = 1;
1584
- var DIGEST_LENGTH = 32;
1585
- function validateLeaves(leaves, fnName) {
1586
- if (leaves.length === 0) {
1587
- throw new Error(`${fnName}: empty leaf list (n == 0 is forbidden by RFC 9162 \xA72.1.1)`);
1731
+ function eciesSealedPoeUnwrap(args) {
1732
+ const { envelope, ciphertext } = args;
1733
+ const constantTimeN = args.constantTimeN ?? true;
1734
+ const hasSingle = "recipientSecretKey" in args;
1735
+ const hasBundle = "recipientKeyBundle" in args;
1736
+ const multiPrivKeys = hasBundle ? selectBundleSecrets(envelope, args.recipientKeyBundle) : "recipientSecretKeys" in args ? args.recipientSecretKeys : void 0;
1737
+ const hasMulti = multiPrivKeys !== void 0;
1738
+ if (hasSingle === hasMulti) {
1739
+ throw new EciesSealedPoeError(
1740
+ "INVALID_RECIPIENT_KEY",
1741
+ "exactly one of recipientSecretKey / recipientSecretKeys / recipientKeyBundle MUST be supplied"
1742
+ );
1588
1743
  }
1589
- for (let i = 0; i < leaves.length; i++) {
1590
- const leaf = leaves[i];
1591
- if (!(leaf instanceof Uint8Array) || leaf.length !== DIGEST_LENGTH) {
1592
- throw new Error(
1593
- `${fnName}: leaf[${i}] must be a Uint8Array(${DIGEST_LENGTH}); got length ${leaf instanceof Uint8Array ? leaf.length : "non-Uint8Array"}`
1594
- );
1744
+ if (hasMulti && multiPrivKeys.length === 0) {
1745
+ if (hasBundle) {
1746
+ return { matched: false, reason: "WRONG_RECIPIENT_KEY" };
1595
1747
  }
1748
+ throw new EciesSealedPoeError(
1749
+ "INVALID_RECIPIENT_KEY",
1750
+ "recipientSecretKeys MUST be a non-empty array, got length=0"
1751
+ );
1596
1752
  }
1597
- }
1598
- function merkleSha2256Root(leaves) {
1599
- validateLeaves(leaves, "merkleSha2256Root");
1600
- return mthRecursive(leaves, 0, leaves.length);
1601
- }
1602
- function largestPow2Lt(n) {
1603
- let k = 1;
1604
- while (k * 2 < n) k *= 2;
1605
- return k;
1606
- }
1607
- function hashLeaf(d) {
1608
- const buf = new Uint8Array(1 + d.length);
1609
- buf[0] = LEAF_PREFIX;
1610
- buf.set(d, 1);
1611
- return sha256(buf);
1612
- }
1613
- function hashNode(left, right) {
1614
- const buf = new Uint8Array(1 + left.length + right.length);
1615
- buf[0] = NODE_PREFIX;
1616
- buf.set(left, 1);
1617
- buf.set(right, 1 + left.length);
1618
- return sha256(buf);
1619
- }
1620
- function mthRecursive(leaves, start, end) {
1621
- const n = end - start;
1622
- if (n === 1) {
1623
- return hashLeaf(leaves[start]);
1753
+ if (hasMulti) {
1754
+ assertEnvelopeStructure(envelope, multiPrivKeys, void 0);
1755
+ } else {
1756
+ assertEnvelopeStructure(envelope, void 0, args.recipientSecretKey);
1624
1757
  }
1625
- const k = largestPow2Lt(n);
1626
- const left = mthRecursive(leaves, start, start + k);
1627
- const right = mthRecursive(leaves, start + k, end);
1628
- return hashNode(left, right);
1629
- }
1630
- var abytesDoc = abytes;
1631
- var randomBytes = randomBytes$1;
1632
- function equalBytes(a, b) {
1633
- if (a.length !== b.length)
1634
- return false;
1635
- let diff = 0;
1636
- for (let i = 0; i < a.length; i++)
1637
- diff |= a[i] ^ b[i];
1638
- return diff === 0;
1639
- }
1640
- function copyBytes(bytes) {
1641
- return Uint8Array.from(abytes(bytes));
1642
- }
1643
- function splitCoder(label, ...lengths) {
1644
- const getLength = (c) => typeof c === "number" ? c : c.bytesLen;
1645
- const bytesLen = lengths.reduce((sum, a) => sum + getLength(a), 0);
1646
- return {
1647
- bytesLen,
1648
- encode: (bufs) => {
1649
- const res = new Uint8Array(bytesLen);
1650
- for (let i = 0, pos = 0; i < lengths.length; i++) {
1651
- const c = lengths[i];
1652
- const l = getLength(c);
1653
- const b = typeof c === "number" ? bufs[i] : c.encode(bufs[i]);
1654
- abytes(b, l, label);
1655
- res.set(b, pos);
1656
- if (typeof c !== "number")
1657
- b.fill(0);
1658
- pos += l;
1758
+ assertCiphertextWithinBound(ciphertext.length);
1759
+ const slotsHash = slotsHashBytes(envelope);
1760
+ let matchedCek = null;
1761
+ let anyCandidateRecovered = false;
1762
+ if (hasSingle) {
1763
+ const recipientSecretKey = args.recipientSecretKey;
1764
+ const candidate = tryRecipientUnwrapWithIdx(
1765
+ envelope,
1766
+ recipientSecretKey,
1767
+ constantTimeN,
1768
+ args._slotsAttemptedOut
1769
+ );
1770
+ if (candidate === null) {
1771
+ return { matched: false, reason: "WRONG_RECIPIENT_KEY" };
1772
+ }
1773
+ if (candidate.cekConflict) {
1774
+ return { matched: false, reason: "TAMPERED_HEADER" };
1775
+ }
1776
+ const hmacKey = hkdfSha256({
1777
+ ikm: candidate.cek,
1778
+ salt: EMPTY_SALT2,
1779
+ info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
1780
+ length: 32
1781
+ });
1782
+ const slotsMacCalc = hmac(sha256, hmacKey, slotsHash);
1783
+ if (!compareCt2(slotsMacCalc, envelope.slots_mac)) {
1784
+ return { matched: false, reason: "TAMPERED_HEADER" };
1785
+ }
1786
+ matchedCek = candidate.cek;
1787
+ } else {
1788
+ const recipientSecretKeys = multiPrivKeys;
1789
+ let cekConflict = false;
1790
+ for (let k = 0; k < recipientSecretKeys.length; k++) {
1791
+ if (args._privsAttemptedOut !== void 0) {
1792
+ args._privsAttemptedOut.count = k + 1;
1659
1793
  }
1660
- return res;
1661
- },
1662
- decode: (buf) => {
1663
- abytes(buf, bytesLen, label);
1664
- const res = [];
1665
- for (const c of lengths) {
1666
- const l = getLength(c);
1667
- const b = buf.subarray(0, l);
1668
- res.push(typeof c === "number" ? b : c.decode(b));
1669
- buf = buf.subarray(l);
1794
+ if (args._slotsAttemptedOut !== void 0) {
1795
+ args._slotsAttemptedOut.count = 0;
1670
1796
  }
1671
- return res;
1672
- }
1673
- };
1674
- }
1675
- function vecCoder(c, vecLen) {
1676
- const coder = c;
1677
- const bytesLen = vecLen * coder.bytesLen;
1678
- return {
1679
- bytesLen,
1680
- encode: (u) => {
1681
- if (u.length !== vecLen)
1682
- throw new RangeError(`vecCoder.encode: wrong length=${u.length}. Expected: ${vecLen}`);
1683
- const res = new Uint8Array(bytesLen);
1684
- for (let i = 0, pos = 0; i < u.length; i++) {
1685
- const b = coder.encode(u[i]);
1686
- res.set(b, pos);
1687
- b.fill(0);
1688
- pos += b.length;
1797
+ const candidate = tryRecipientUnwrapWithIdx(
1798
+ envelope,
1799
+ recipientSecretKeys[k],
1800
+ constantTimeN,
1801
+ args._slotsAttemptedOut
1802
+ );
1803
+ if (args._slotsAttemptedOut?.perPrivCounts !== void 0) {
1804
+ args._slotsAttemptedOut.perPrivCounts.push(args._slotsAttemptedOut.count);
1689
1805
  }
1690
- return res;
1691
- },
1692
- decode: (a) => {
1693
- abytes(a, bytesLen);
1694
- const r = [];
1695
- for (let i = 0; i < a.length; i += coder.bytesLen)
1696
- r.push(coder.decode(a.subarray(i, i + coder.bytesLen)));
1697
- return r;
1806
+ if (candidate === null) continue;
1807
+ if (candidate.cekConflict) cekConflict = true;
1808
+ const cek = candidate.cek;
1809
+ const hmacKey = hkdfSha256({
1810
+ ikm: cek,
1811
+ salt: EMPTY_SALT2,
1812
+ info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
1813
+ length: 32
1814
+ });
1815
+ const slotsMacCalc = hmac(sha256, hmacKey, slotsHash);
1816
+ if (compareCt2(slotsMacCalc, envelope.slots_mac)) {
1817
+ matchedCek = cek;
1818
+ break;
1819
+ }
1820
+ anyCandidateRecovered = true;
1821
+ }
1822
+ if (matchedCek !== null && cekConflict) {
1823
+ return { matched: false, reason: "TAMPERED_HEADER" };
1824
+ }
1825
+ if (matchedCek === null) {
1826
+ return {
1827
+ matched: false,
1828
+ reason: anyCandidateRecovered ? "TAMPERED_HEADER" : "WRONG_RECIPIENT_KEY"
1829
+ };
1698
1830
  }
1699
- };
1700
- }
1701
- function cleanBytes(...list) {
1702
- for (const t of list) {
1703
- if (Array.isArray(t))
1704
- for (const b of t)
1705
- b.fill(0);
1706
- else
1707
- t.fill(0);
1831
+ }
1832
+ const payloadKey = slotsPayloadKey({ cek: matchedCek, nonce: envelope.nonce });
1833
+ const adContent = adContentSlots({
1834
+ kem: envelope.kem,
1835
+ nonce: envelope.nonce,
1836
+ slotsHash,
1837
+ slotsMac: envelope.slots_mac
1838
+ });
1839
+ try {
1840
+ const plaintext = xchacha20Poly1305Decrypt({
1841
+ key: payloadKey,
1842
+ nonce: envelope.nonce,
1843
+ aad: adContent,
1844
+ ciphertext
1845
+ });
1846
+ return { matched: true, plaintext };
1847
+ } catch (e) {
1848
+ if (!(e instanceof AeadVerificationError)) throw e;
1849
+ return { matched: false, reason: "TAMPERED_CIPHERTEXT" };
1708
1850
  }
1709
1851
  }
1710
- function getMask(bits) {
1711
- if (!Number.isSafeInteger(bits) || bits < 0 || bits > 32)
1712
- throw new RangeError(`expected bits in [0..32], got ${bits}`);
1713
- return bits === 32 ? 4294967295 : ~(-1 << bits) >>> 0;
1714
- }
1715
-
1716
- // ../../node_modules/.pnpm/@noble+post-quantum@0.6.1/node_modules/@noble/post-quantum/_crystals.js
1717
- var genCrystals = (opts2) => {
1718
- const { newPoly, N: N2, Q: Q2, F: F2, ROOT_OF_UNITY: ROOT_OF_UNITY2, brvBits} = opts2;
1719
- const mod = (a, modulo = Q2) => {
1720
- const result = a % modulo | 0;
1721
- return (result >= 0 ? result | 0 : modulo + result | 0) | 0;
1722
- };
1723
- const smod = (a, modulo = Q2) => {
1724
- const r = mod(a, modulo) | 0;
1725
- return (r > modulo >> 1 ? r - modulo | 0 : r) | 0;
1726
- };
1727
- function getZettas() {
1728
- const out = newPoly(N2);
1729
- for (let i = 0; i < N2; i++) {
1730
- const b = reverseBits(i, brvBits);
1731
- const p = BigInt(ROOT_OF_UNITY2) ** BigInt(b) % BigInt(Q2);
1732
- out[i] = Number(p) | 0;
1852
+ function sealedEnvelopeFromParsed(enc) {
1853
+ if (enc.scheme !== 1 || enc.aead !== "xchacha20-poly1305") return null;
1854
+ if (enc.nonce === void 0 || enc.slots_mac === void 0) return null;
1855
+ const slots = enc.slots;
1856
+ if (slots === void 0 || slots.length < 1) return null;
1857
+ if (enc.kem === "x25519") {
1858
+ const x25519Slots = [];
1859
+ for (const s of slots) {
1860
+ if (s.epk === void 0 || s.wrap === void 0) return null;
1861
+ x25519Slots.push({ epk: s.epk, wrap: s.wrap });
1733
1862
  }
1734
- return out;
1863
+ return {
1864
+ scheme: 1,
1865
+ aead: "xchacha20-poly1305",
1866
+ kem: "x25519",
1867
+ nonce: enc.nonce,
1868
+ slots: x25519Slots,
1869
+ slots_mac: enc.slots_mac
1870
+ };
1735
1871
  }
1736
- const nttZetas = getZettas();
1737
- const field = {
1738
- add: (a, b) => mod((a | 0) + (b | 0)) | 0,
1739
- sub: (a, b) => mod((a | 0) - (b | 0)) | 0,
1740
- mul: (a, b) => mod((a | 0) * (b | 0)) | 0,
1741
- inv: (_a) => {
1742
- throw new Error("not implemented");
1743
- }
1744
- };
1745
- const nttOpts = {
1746
- N: N2,
1747
- roots: nttZetas,
1748
- invertButterflies: true,
1749
- skipStages: 1 ,
1750
- brp: false
1751
- };
1752
- const dif = FFTCore(field, { dit: false, ...nttOpts });
1753
- const dit = FFTCore(field, { dit: true, ...nttOpts });
1754
- const NTT = {
1755
- encode: (r) => {
1756
- return dif(r);
1757
- },
1758
- decode: (r) => {
1759
- dit(r);
1760
- for (let i = 0; i < r.length; i++)
1761
- r[i] = mod(F2 * r[i]);
1762
- return r;
1872
+ if (enc.kem === "mlkem768x25519") {
1873
+ const hybridSlots = [];
1874
+ for (const s of slots) {
1875
+ if (s.kem_ct === void 0 || s.wrap === void 0) return null;
1876
+ hybridSlots.push({ kem_ct: s.kem_ct, wrap: s.wrap });
1763
1877
  }
1764
- };
1765
- const bitsCoder = (d, c) => {
1766
- const mask = getMask(d);
1767
- const bytesLen = d * (N2 / 8);
1768
1878
  return {
1769
- bytesLen,
1770
- encode: (poly_) => {
1771
- const poly = poly_;
1772
- const r = new Uint8Array(bytesLen);
1773
- for (let i = 0, buf = 0, bufLen = 0, pos = 0; i < poly.length; i++) {
1774
- buf |= (c.encode(poly[i]) & mask) << bufLen;
1775
- bufLen += d;
1776
- for (; bufLen >= 8; bufLen -= 8, buf >>= 8)
1777
- r[pos++] = buf & getMask(bufLen);
1778
- }
1779
- return r;
1780
- },
1781
- decode: (bytes) => {
1782
- const r = newPoly(N2);
1783
- for (let i = 0, buf = 0, bufLen = 0, pos = 0; i < bytes.length; i++) {
1784
- buf |= bytes[i] << bufLen;
1785
- bufLen += 8;
1786
- for (; bufLen >= d; bufLen -= d, buf >>= d)
1787
- r[pos++] = c.decode(buf & mask);
1879
+ scheme: 1,
1880
+ aead: "xchacha20-poly1305",
1881
+ kem: "mlkem768x25519",
1882
+ nonce: enc.nonce,
1883
+ slots: hybridSlots,
1884
+ slots_mac: enc.slots_mac
1885
+ };
1886
+ }
1887
+ return null;
1888
+ }
1889
+
1890
+ // ../poe-standard/src/chunked.ts
1891
+ var UTF8_ENCODER2 = new TextEncoder();
1892
+ function bytesChunkArrayConcat(chunks) {
1893
+ let total = 0;
1894
+ for (const c of chunks) total += c.length;
1895
+ const out = new Uint8Array(total);
1896
+ let offset = 0;
1897
+ for (const c of chunks) {
1898
+ out.set(c, offset);
1899
+ offset += c.length;
1900
+ }
1901
+ return out;
1902
+ }
1903
+ function reconstructChunkedUri(chunks) {
1904
+ const merged = bytesChunkArrayConcat(chunks.map((c) => UTF8_ENCODER2.encode(c)));
1905
+ try {
1906
+ const uri = new TextDecoder("utf-8", { fatal: true }).decode(merged);
1907
+ return { ok: true, uri };
1908
+ } catch (cause) {
1909
+ return {
1910
+ ok: false,
1911
+ code: "INVALID_URI",
1912
+ reason: cause instanceof Error ? cause.message : String(cause)
1913
+ };
1914
+ }
1915
+ }
1916
+ var SEVERITY = Object.freeze({
1917
+ // --- Part A ---
1918
+ MALFORMED_CBOR: "error",
1919
+ SCHEMA_TYPE_MISMATCH: "error",
1920
+ SCHEMA_MISSING_REQUIRED: "error",
1921
+ SCHEMA_UNKNOWN_FIELD: "error",
1922
+ SCHEMA_INVALID_LITERAL: "error",
1923
+ SCHEMA_EMPTY_RECORD: "error",
1924
+ HASH_DIGEST_LENGTH_MISMATCH: "error",
1925
+ UNSUPPORTED_HASH_ALG: "error",
1926
+ UNSUPPORTED_MERKLE_COMMIT_ALG: "error",
1927
+ INVALID_URI: "error",
1928
+ CHUNK_TOO_LARGE: "error",
1929
+ UNAUTHENTICATED_CIPHER_FORBIDDEN: "error",
1930
+ UNSUPPORTED_AEAD_ALG: "error",
1931
+ NONCE_LENGTH_MISMATCH: "error",
1932
+ UNSUPPORTED_ENVELOPE_SCHEME: "error",
1933
+ ENC_SLOTS_EMPTY: "error",
1934
+ ENC_SLOT_INVALID_SHAPE: "error",
1935
+ ENC_SLOTS_DUPLICATE_KEM_MATERIAL: "error",
1936
+ ENC_SLOTS_TOO_MANY: "error",
1937
+ ENC_ENVELOPE_TOO_LARGE: "error",
1938
+ UNSUPPORTED_KEM_ALG: "error",
1939
+ ENC_KEM_REQUIRED: "error",
1940
+ KEM_EPK_LENGTH_MISMATCH: "error",
1941
+ KEM_CT_LENGTH_MISMATCH: "error",
1942
+ WRAP_LENGTH_MISMATCH: "error",
1943
+ ENC_SLOTS_MAC_INVALID_LENGTH: "error",
1944
+ ENC_SLOTS_MAC_REQUIRED: "error",
1945
+ ENC_SLOTS_REQUIRED: "error",
1946
+ ENC_EXCLUSIVITY_VIOLATION: "error",
1947
+ ENC_NO_KEY_PATH: "error",
1948
+ ENC_REQUIRES_CONTENT_HASH: "error",
1949
+ ENC_PASSPHRASE_ALG_UNSUPPORTED: "error",
1950
+ ENC_PASSPHRASE_SALT_TOO_SHORT: "error",
1951
+ ENC_PASSPHRASE_SALT_TOO_LONG: "error",
1952
+ ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW: "error",
1953
+ ENC_PASSPHRASE_PARAMS_EXCEED_POLICY: "error",
1954
+ MALFORMED_SIG_COSE_SIGN1: "error",
1955
+ SIGNATURE_UNSUPPORTED: "info",
1956
+ SIG_ENTRY_INVALID_SHAPE: "error",
1957
+ SIG_ENTRY_KID_COSE_KEY_CONFLICT: "error",
1958
+ SIG_PRIVATE_KEY_LEAKED: "error",
1959
+ SUPERSEDES_TX_INVALID_LENGTH: "error",
1960
+ EXTENSION_UNSUPPORTED_CRITICAL: "error",
1961
+ CRIT_SHAPE_INVALID: "error",
1962
+ // --- Part B ---
1963
+ METADATA_NOT_FOUND: "error",
1964
+ INSUFFICIENT_CONFIRMATIONS: "info",
1965
+ SIGNATURE_INVALID: "error",
1966
+ SIGNER_KEY_UNRESOLVED: "error",
1967
+ WALLET_ADDRESS_MISMATCH: "error",
1968
+ URI_TARGET_FORBIDDEN: "error",
1969
+ URI_INTEGRITY_MISMATCH: "error",
1970
+ URI_FETCH_FAILED: "warning",
1971
+ CONTENT_UNAVAILABLE: "error",
1972
+ CIPHERTEXT_UNAVAILABLE: "error",
1973
+ PROVIDER_UNAVAILABLE: "error",
1974
+ SERVICE_INDEPENDENCE_VIOLATION: "error",
1975
+ WRONG_DECRYPTION_INPUT_SHAPE: "error",
1976
+ WRONG_RECIPIENT_KEY: "error",
1977
+ TAMPERED_HEADER: "error",
1978
+ TAMPERED_CIPHERTEXT: "error",
1979
+ KDF_DERIVATION_FAILED: "error",
1980
+ SCHEMA_MERKLE_LEAF_COUNT_MISMATCH: "error",
1981
+ SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED: "error",
1982
+ SCHEMA_MERKLE_LEAVES_MALFORMED: "error",
1983
+ MERKLE_ROOT_MISMATCH: "error",
1984
+ MERKLE_LEAVES_UNAVAILABLE: "warning",
1985
+ MERKLE_LEAVES_INFORMATIVE_FORM: "info",
1986
+ // Dual-severity — default reading is `info`; the verifier promotes to
1987
+ // `error` for merkle-only records (no `items[]` content claim was
1988
+ // validated in the same record).
1989
+ MERKLE_UNSUPPORTED: "info",
1990
+ // Dual-severity — default reading is `info` (render mode); strict
1991
+ // end-to-end verifiers promote to `error`.
1992
+ OUT_OF_PROFILE_SKIPPED: "info"
1993
+ });
1994
+
1995
+ // ../poe-standard/src/validator.ts
1996
+ var HASH_ALG_LENGTHS = {
1997
+ "sha2-256": 32,
1998
+ "blake2b-256": 32
1999
+ };
2000
+ var MERKLE_COMMIT_ALG_LENGTHS = {
2001
+ "rfc9162-sha256": 32
2002
+ };
2003
+ var AEAD_NONCE_LENGTHS = {
2004
+ "xchacha20-poly1305": 24
2005
+ };
2006
+ var UNAUTHENTICATED_CIPHER_RE = /(?:^|[-_])(?:cbc|ctr|ecb|cfb|ofb)(?:[-_]|\n?$)|^(?:rc4|des|3des)(?:[-_]|\n?$)/i;
2007
+ var KEM_SLOT_DESCRIPTORS = {
2008
+ x25519: { field: "epk", fieldLength: 32, wrapLength: 48 },
2009
+ mlkem768x25519: { field: "kem_ct", fieldLength: 1120, wrapLength: 48 }
2010
+ };
2011
+ var KEM_FIELD_LENGTH_CODE = {
2012
+ epk: "KEM_EPK_LENGTH_MISMATCH",
2013
+ kem_ct: "KEM_CT_LENGTH_MISMATCH"
2014
+ };
2015
+ var NONCE_LENGTH = 24;
2016
+ var SLOTS_MAC_LENGTH = 32;
2017
+ var PASSPHRASE_KDF_ALGS = /* @__PURE__ */ new Set(["argon2id"]);
2018
+ var KNOWN_SIG_ALG_IDS = /* @__PURE__ */ new Set([-8, -19]);
2019
+ function validatePoeRecord(bytes) {
2020
+ let decoded;
2021
+ try {
2022
+ decoded = decodeCanonicalCbor(bytes);
2023
+ } catch (cause) {
2024
+ return {
2025
+ ok: false,
2026
+ issues: [
2027
+ {
2028
+ code: "MALFORMED_CBOR",
2029
+ path: [],
2030
+ message: cause instanceof Error ? cause.message : String(cause),
2031
+ severity: "error"
1788
2032
  }
1789
- return r;
1790
- }
2033
+ ]
1791
2034
  };
1792
- };
1793
- return {
1794
- mod,
1795
- smod,
1796
- nttZetas,
1797
- NTT: {
1798
- encode: (r) => NTT.encode(r),
1799
- decode: (r) => NTT.decode(r)
1800
- },
1801
- bitsCoder
1802
- };
1803
- };
1804
- var createXofShake = (shake) => (seed, blockLen) => {
1805
- if (!blockLen)
1806
- blockLen = shake.blockLen;
1807
- const _seed = new Uint8Array(seed.length + 2);
1808
- _seed.set(seed);
1809
- const seedLen = seed.length;
1810
- const buf = new Uint8Array(blockLen);
1811
- let h = shake.create({});
1812
- let calls = 0;
1813
- let xofs = 0;
1814
- return {
1815
- stats: () => ({ calls, xofs }),
1816
- get: (x, y) => {
1817
- _seed[seedLen + 0] = x;
1818
- _seed[seedLen + 1] = y;
1819
- h.destroy();
1820
- h = shake.create({}).update(_seed);
1821
- calls++;
1822
- return () => {
1823
- xofs++;
1824
- return h.xofInto(buf);
1825
- };
1826
- },
1827
- clean: () => {
1828
- h.destroy();
1829
- cleanBytes(buf, _seed);
2035
+ }
2036
+ const parse = PoeRecordSchema.safeParse(decoded);
2037
+ if (!parse.success) {
2038
+ const issues = parse.error.issues.map((issue2) => mapZodIssue(issue2, decoded)).sort(compareIssuePath);
2039
+ return { ok: false, issues };
2040
+ }
2041
+ const record = parse.data;
2042
+ const errors = [];
2043
+ const warnings = [];
2044
+ const info = [];
2045
+ const itemsLen = Array.isArray(record.items) ? record.items.length : 0;
2046
+ const merkleLen = Array.isArray(record.merkle) ? record.merkle.length : 0;
2047
+ if (itemsLen === 0 && merkleLen === 0) {
2048
+ errors.push(
2049
+ issue(
2050
+ "SCHEMA_EMPTY_RECORD",
2051
+ [],
2052
+ "record must carry at least one of items[] or merkle[] non-empty"
2053
+ )
2054
+ );
2055
+ }
2056
+ const decodedTopKeys = topLevelKeysOf(decoded);
2057
+ const critShapeInvalidIndices = checkCritShape(record, decodedTopKeys, errors);
2058
+ for (const k of decodedTopKeys) {
2059
+ if (TOP_LEVEL_BASE_KEYS.has(k)) continue;
2060
+ if (isExtensionKey(k)) continue;
2061
+ errors.push(issue("SCHEMA_UNKNOWN_FIELD", [k], `unknown top-level field: ${k}`));
2062
+ }
2063
+ if (Array.isArray(record.crit)) {
2064
+ for (let i = 0; i < record.crit.length; i++) {
2065
+ if (critShapeInvalidIndices.has(i)) continue;
2066
+ const critName = record.crit[i];
2067
+ errors.push(
2068
+ issue(
2069
+ "EXTENSION_UNSUPPORTED_CRITICAL",
2070
+ ["crit", i],
2071
+ `crit lists extension '${critName}' that this validator does not implement`
2072
+ )
2073
+ );
1830
2074
  }
1831
- };
1832
- };
1833
- var XOF128 = /* @__PURE__ */ createXofShake(shake128);
1834
-
1835
- // ../../node_modules/.pnpm/@noble+post-quantum@0.6.1/node_modules/@noble/post-quantum/ml-kem.js
1836
- var N = 256;
1837
- var Q = 3329;
1838
- var F = 3303;
1839
- var ROOT_OF_UNITY = 17;
1840
- var crystals = /* @__PURE__ */ genCrystals({
1841
- N,
1842
- Q,
1843
- F,
1844
- ROOT_OF_UNITY,
1845
- newPoly: (n) => new Uint16Array(n),
1846
- brvBits: 7});
1847
- var PARAMS = /* @__PURE__ */ (() => Object.freeze({
1848
- 512: Object.freeze({ N, Q, K: 2, ETA1: 3, ETA2: 2, du: 10, dv: 4, RBGstrength: 128 }),
1849
- 768: Object.freeze({ N, Q, K: 3, ETA1: 2, ETA2: 2, du: 10, dv: 4, RBGstrength: 192 }),
1850
- 1024: Object.freeze({ N, Q, K: 4, ETA1: 2, ETA2: 2, du: 11, dv: 5, RBGstrength: 256 })
1851
- }))();
1852
- var compress = (d) => {
1853
- if (d >= 12)
1854
- return { encode: (i) => i, decode: (i) => i >= Q ? i - Q : i };
1855
- const a = 2 ** (d - 1);
1856
- return {
1857
- // This only matches standalone Compress_d after bitsCoder masks the result into Z_(2^d).
1858
- encode: (i) => ((i << d) + Q / 2) / Q,
1859
- // const decompress = (i: number) => round((Q / 2 ** d) * i);
1860
- decode: (i) => i * Q + a >>> d
1861
- };
1862
- };
1863
- var byteCoder = (d) => crystals.bitsCoder(d, { encode: (i) => i, decode: (i) => i >= Q ? i - Q : i } );
1864
- var polyCoder = (d) => d === 12 ? byteCoder(12) : crystals.bitsCoder(d, compress(d));
1865
- function polyAdd(a_, b_) {
1866
- const a = a_;
1867
- const b = b_;
1868
- for (let i = 0; i < N; i++)
1869
- a[i] = crystals.mod(a[i] + b[i]);
1870
- }
1871
- function polySub(a_, b_) {
1872
- const a = a_;
1873
- const b = b_;
1874
- for (let i = 0; i < N; i++)
1875
- a[i] = crystals.mod(a[i] - b[i]);
1876
- }
1877
- function BaseCaseMultiply(a0, a1, b0, b1, zeta) {
1878
- const c0 = crystals.mod(a1 * b1 * zeta + a0 * b0);
1879
- const c1 = crystals.mod(a0 * b1 + a1 * b0);
1880
- return { c0, c1 };
1881
- }
1882
- function MultiplyNTTs(f_, g_) {
1883
- const f = f_;
1884
- const g = g_;
1885
- for (let i = 0; i < N / 2; i++) {
1886
- let z3 = crystals.nttZetas[64 + (i >> 1)];
1887
- if (i & 1)
1888
- z3 = -z3;
1889
- const { c0, c1 } = BaseCaseMultiply(f[2 * i + 0], f[2 * i + 1], g[2 * i + 0], g[2 * i + 1], z3);
1890
- f[2 * i + 0] = c0;
1891
- f[2 * i + 1] = c1;
1892
2075
  }
1893
- return f;
1894
- }
1895
- function SampleNTT(xof_) {
1896
- const xof = xof_;
1897
- const r = new Uint16Array(N);
1898
- for (let j = 0; j < N; ) {
1899
- const b = xof();
1900
- if (b.length % 3)
1901
- throw new Error("SampleNTT: unaligned block");
1902
- for (let i = 0; j < N && i + 3 <= b.length; i += 3) {
1903
- const d1 = (b[i + 0] >> 0 | b[i + 1] << 8) & 4095;
1904
- const d2 = (b[i + 1] >> 4 | b[i + 2] << 4) & 4095;
1905
- if (d1 < Q)
1906
- r[j++] = d1;
1907
- if (j < N && d2 < Q)
1908
- r[j++] = d2;
2076
+ for (let i = 0; i < (record.items ?? []).length; i++) {
2077
+ const item = record.items[i];
2078
+ checkItemHashes(item, i, errors);
2079
+ if (item.uris) checkItemUris(item.uris, ["items", i, "uris"], errors);
2080
+ if (item.enc !== void 0) checkItemEnc(item, i, errors);
2081
+ }
2082
+ for (let i = 0; i < (record.merkle ?? []).length; i++) {
2083
+ const commit = record.merkle[i];
2084
+ checkMerkleCommit(commit, i, errors);
2085
+ }
2086
+ if (record.sigs) {
2087
+ for (let i = 0; i < record.sigs.length; i++) {
2088
+ checkSigEntry(record.sigs[i], i, errors, info);
1909
2089
  }
1910
2090
  }
1911
- return r;
1912
- }
1913
- var sampleCBDBytes = (buf, eta) => {
1914
- const r = new Uint16Array(N);
1915
- const b32 = u32(buf);
1916
- swap32IfBE(b32);
1917
- let len = 0;
1918
- for (let i = 0, p = 0, bb = 0, t0 = 0; i < b32.length; i++) {
1919
- let b = b32[i];
1920
- for (let j = 0; j < 32; j++) {
1921
- bb += b & 1;
1922
- b >>= 1;
1923
- len += 1;
1924
- if (len === eta) {
1925
- t0 = bb;
1926
- bb = 0;
1927
- } else if (len === 2 * eta) {
1928
- r[p++] = crystals.mod(t0 - bb);
1929
- bb = 0;
1930
- len = 0;
1931
- }
1932
- }
2091
+ if (errors.length > 0) {
2092
+ return { ok: false, issues: errors.sort(compareIssuePath) };
1933
2093
  }
1934
- swap32IfBE(b32);
1935
- if (len)
1936
- throw new Error(`sampleCBD: leftover bits: ${len}`);
1937
- return r;
1938
- };
1939
- function sampleCBD(PRF_, seed, nonce, eta) {
1940
- const PRF = PRF_;
1941
- return sampleCBDBytes(PRF(eta * N / 4, seed, nonce), eta);
2094
+ const result = {
2095
+ ok: true,
2096
+ record
2097
+ };
2098
+ if (warnings.length > 0) result.warnings = warnings.sort(compareIssuePath);
2099
+ if (info.length > 0) result.info = info.sort(compareIssuePath);
2100
+ return result;
1942
2101
  }
1943
- var genKPKE = (opts_) => {
1944
- const opts2 = opts_;
1945
- const { K, PRF, XOF, HASH512, ETA1, ETA2, du, dv } = opts2;
1946
- const poly1 = polyCoder(1);
1947
- const polyV = polyCoder(dv);
1948
- const polyU = polyCoder(du);
1949
- const publicCoder = splitCoder("publicKey", vecCoder(polyCoder(12), K), 32);
1950
- const secretCoder = vecCoder(polyCoder(12), K);
1951
- const cipherCoder = splitCoder("ciphertext", vecCoder(polyU, K), polyV);
1952
- const seedCoder = splitCoder("seed", 32, 32);
1953
- return {
1954
- secretCoder,
1955
- lengths: {
1956
- secretKey: secretCoder.bytesLen,
1957
- publicKey: publicCoder.bytesLen,
1958
- cipherText: cipherCoder.bytesLen
1959
- },
1960
- keygen: (seed) => {
1961
- abytesDoc(seed, 32, "seed");
1962
- const seedDst = new Uint8Array(33);
1963
- seedDst.set(seed);
1964
- seedDst[32] = K;
1965
- const seedHash = HASH512(seedDst);
1966
- const [rho, sigma] = seedCoder.decode(seedHash);
1967
- const sHat = [];
1968
- const tHat = [];
1969
- for (let i = 0; i < K; i++)
1970
- sHat.push(crystals.NTT.encode(sampleCBD(PRF, sigma, i, ETA1)));
1971
- const x = XOF(rho);
1972
- for (let i = 0; i < K; i++) {
1973
- const e = crystals.NTT.encode(sampleCBD(PRF, sigma, K + i, ETA1));
1974
- for (let j = 0; j < K; j++) {
1975
- const aji = SampleNTT(x.get(j, i));
1976
- polyAdd(e, MultiplyNTTs(aji, sHat[j]));
1977
- }
1978
- tHat.push(e);
1979
- }
1980
- x.clean();
1981
- const res = {
1982
- publicKey: publicCoder.encode([tHat, rho]),
1983
- secretKey: secretCoder.encode(sHat)
1984
- };
1985
- cleanBytes(rho, sigma, sHat, tHat, seedDst, seedHash);
1986
- return res;
1987
- },
1988
- encrypt: (publicKey, msg, seed) => {
1989
- const [tHat, rho] = publicCoder.decode(publicKey);
1990
- const rHat = [];
1991
- for (let i = 0; i < K; i++)
1992
- rHat.push(crystals.NTT.encode(sampleCBD(PRF, seed, i, ETA1)));
1993
- const x = XOF(rho);
1994
- const tmp2 = new Uint16Array(N);
1995
- const u = [];
1996
- for (let i = 0; i < K; i++) {
1997
- const e1 = sampleCBD(PRF, seed, K + i, ETA2);
1998
- const tmp = new Uint16Array(N);
1999
- for (let j = 0; j < K; j++) {
2000
- const aij = SampleNTT(x.get(i, j));
2001
- polyAdd(tmp, MultiplyNTTs(aij, rHat[j]));
2002
- }
2003
- polyAdd(e1, crystals.NTT.decode(tmp));
2004
- u.push(e1);
2005
- polyAdd(tmp2, MultiplyNTTs(tHat[i], rHat[i]));
2006
- cleanBytes(tmp);
2007
- }
2008
- x.clean();
2009
- const e2 = sampleCBD(PRF, seed, 2 * K, ETA2);
2010
- polyAdd(e2, crystals.NTT.decode(tmp2));
2011
- const v = poly1.decode(msg);
2012
- polyAdd(v, e2);
2013
- cleanBytes(tHat, rHat, tmp2, e2);
2014
- return cipherCoder.encode([u, v]);
2015
- },
2016
- decrypt: (cipherText, privateKey) => {
2017
- const [u, v] = cipherCoder.decode(cipherText);
2018
- const sk = secretCoder.decode(privateKey);
2019
- const tmp = new Uint16Array(N);
2020
- for (let i = 0; i < K; i++)
2021
- polyAdd(tmp, MultiplyNTTs(sk[i], crystals.NTT.encode(u[i])));
2022
- polySub(v, crystals.NTT.decode(tmp));
2023
- cleanBytes(tmp, sk, u);
2024
- return poly1.encode(v);
2102
+ function mapZodIssue(zissue, decoded) {
2103
+ const path = zissue.path;
2104
+ const explicit = zissue.params?.code;
2105
+ if (explicit !== void 0) {
2106
+ return issue(explicit, path, zissue.message);
2107
+ }
2108
+ const inSigsEntry = path.length >= 2 && path[0] === "sigs" && typeof path[1] === "number";
2109
+ const isInSlotEntry = (() => {
2110
+ if (path.length >= 5 && path[0] === "items" && typeof path[1] === "number" && path[2] === "enc" && path[3] === "slots" && typeof path[4] === "number") {
2111
+ return true;
2025
2112
  }
2026
- };
2027
- };
2028
- function createKyber(opts2) {
2029
- const rawOpts = opts2;
2030
- const KPKE = genKPKE(rawOpts);
2031
- const { HASH256, HASH512, KDF } = rawOpts;
2032
- const { secretCoder: KPKESecretCoder, lengths } = KPKE;
2033
- const secretCoder = splitCoder("secretKey", lengths.secretKey, lengths.publicKey, 32, 32);
2034
- const msgLen = 32;
2035
- const seedLen = 64;
2036
- const kemLengths = Object.freeze({
2037
- ...lengths,
2038
- seed: 64,
2039
- msg: msgLen,
2040
- msgRand: msgLen,
2041
- secretKey: secretCoder.bytesLen
2042
- });
2043
- return Object.freeze({
2044
- info: Object.freeze({ type: "ml-kem" }),
2045
- lengths: kemLengths,
2046
- keygen: (seed = randomBytes(seedLen)) => {
2047
- abytesDoc(seed, seedLen, "seed");
2048
- const { publicKey, secretKey: sk } = KPKE.keygen(seed.subarray(0, 32));
2049
- const publicKeyHash = HASH256(publicKey);
2050
- const secretKey = secretCoder.encode([sk, publicKey, publicKeyHash, seed.subarray(32)]);
2051
- cleanBytes(sk, publicKeyHash);
2052
- return {
2053
- publicKey,
2054
- secretKey
2055
- };
2056
- },
2057
- getPublicKey: (secretKey) => {
2058
- const [_sk, publicKey, _publicKeyHash, _z] = secretCoder.decode(secretKey);
2059
- return Uint8Array.from(publicKey);
2060
- },
2061
- encapsulate: (publicKey, msg = randomBytes(msgLen)) => {
2062
- abytesDoc(publicKey, lengths.publicKey, "publicKey");
2063
- abytesDoc(msg, msgLen, "message");
2064
- const eke = publicKey.subarray(0, 384 * opts2.K);
2065
- const ek = KPKESecretCoder.encode(KPKESecretCoder.decode(copyBytes(eke)));
2066
- if (!equalBytes(ek, eke)) {
2067
- cleanBytes(ek);
2068
- throw new Error("ML-KEM.encapsulate: wrong publicKey modulus");
2069
- }
2070
- cleanBytes(ek);
2071
- const kr = HASH512.create().update(msg).update(HASH256(publicKey)).digest();
2072
- const cipherText = KPKE.encrypt(publicKey, msg, kr.subarray(32, 64));
2073
- cleanBytes(kr.subarray(32));
2074
- return {
2075
- cipherText,
2076
- sharedSecret: kr.subarray(0, 32)
2077
- };
2078
- },
2079
- decapsulate: (cipherText, secretKey) => {
2080
- abytesDoc(secretKey, secretCoder.bytesLen, "secretKey");
2081
- abytesDoc(cipherText, lengths.cipherText, "cipherText");
2082
- const k768 = secretCoder.bytesLen - 96;
2083
- const start = k768 + 32;
2084
- const test = HASH256(secretKey.subarray(k768 / 2, start));
2085
- if (!equalBytes(test, secretKey.subarray(start, start + 32)))
2086
- throw new Error("invalid secretKey: hash check failed");
2087
- const [sk, publicKey, publicKeyHash, z3] = secretCoder.decode(secretKey);
2088
- const msg = KPKE.decrypt(cipherText, sk);
2089
- const kr = HASH512.create().update(msg).update(publicKeyHash).digest();
2090
- const Khat = kr.subarray(0, 32);
2091
- const cipherText2 = KPKE.encrypt(publicKey, msg, kr.subarray(32, 64));
2092
- const isValid = equalBytes(cipherText, cipherText2);
2093
- const Kbar = KDF.create({ dkLen: 32 }).update(z3).update(cipherText).digest();
2094
- cleanBytes(msg, cipherText2, !isValid ? Khat : Kbar);
2095
- return isValid ? Khat : Kbar;
2113
+ if (path.length >= 2 && path[0] === "slots" && typeof path[1] === "number") {
2114
+ return true;
2096
2115
  }
2097
- });
2098
- }
2099
- function shakePRF(dkLen, key, nonce) {
2100
- return shake256.create({ dkLen }).update(key).update(new Uint8Array([nonce])).digest();
2101
- }
2102
- var opts = /* @__PURE__ */ (() => ({
2103
- HASH256: sha3_256,
2104
- HASH512: sha3_512,
2105
- KDF: shake256,
2106
- XOF: XOF128,
2107
- PRF: shakePRF
2108
- }))();
2109
- var mk = (params) => createKyber({
2110
- ...opts,
2111
- ...params
2112
- });
2113
- var ml_kem768 = /* @__PURE__ */ (() => mk(PARAMS[768]))();
2114
-
2115
- // ../../node_modules/.pnpm/@noble+post-quantum@0.6.1/node_modules/@noble/post-quantum/hybrid.js
2116
- function ecKeygen(curve, allowZeroKey = false) {
2117
- const lengths = curve.lengths;
2118
- let keygen = curve.keygen;
2119
- if (allowZeroKey) {
2120
- if (!("getSharedSecret" in curve && "sign" in curve && "verify" in curve))
2121
- throw new Error("allowZeroKey requires a Weierstrass curve");
2122
- const wCurve = curve;
2123
- const Fn = wCurve.Point.Fn;
2124
- keygen = (seed = randomBytes(lengths.seed)) => {
2125
- abytes(seed, lengths.seed, "seed");
2126
- const seedScalar = Fn.isLE ? bytesToNumberLE(seed) : bytesToNumberBE(seed);
2127
- const secretKey = Fn.toBytes(Fn.create(seedScalar));
2128
- return {
2129
- secretKey,
2130
- publicKey: curve.getPublicKey(secretKey)
2131
- };
2132
- };
2116
+ return false;
2117
+ })();
2118
+ const valueAtIssue = valueAtPath(decoded, path);
2119
+ const isMissing = valueAtIssue === void 0;
2120
+ switch (zissue.code) {
2121
+ case "invalid_type":
2122
+ if (isInSlotEntry) return issue("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
2123
+ if (isMissing) {
2124
+ if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
2125
+ return issue("SCHEMA_MISSING_REQUIRED", path, zissue.message);
2126
+ }
2127
+ if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
2128
+ return issue("SCHEMA_TYPE_MISMATCH", path, zissue.message);
2129
+ case "invalid_value":
2130
+ if (path.length === 1 && path[0] === "v") {
2131
+ return issue(
2132
+ isMissing ? "SCHEMA_MISSING_REQUIRED" : "SCHEMA_INVALID_LITERAL",
2133
+ path,
2134
+ zissue.message
2135
+ );
2136
+ }
2137
+ return issue("SCHEMA_INVALID_LITERAL", path, zissue.message);
2138
+ case "unrecognized_keys":
2139
+ if (isInSlotEntry) return issue("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
2140
+ if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
2141
+ return issue("SCHEMA_UNKNOWN_FIELD", path, zissue.message);
2142
+ case "invalid_format":
2143
+ case "too_big":
2144
+ case "too_small":
2145
+ if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
2146
+ return issue("SCHEMA_TYPE_MISMATCH", path, zissue.message);
2147
+ case "invalid_union":
2148
+ case "invalid_key":
2149
+ case "invalid_element":
2150
+ case "custom":
2151
+ default:
2152
+ if (isInSlotEntry) return issue("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
2153
+ if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
2154
+ return issue("SCHEMA_TYPE_MISMATCH", path, zissue.message);
2133
2155
  }
2134
- return {
2135
- lengths: { secretKey: lengths.secretKey, publicKey: lengths.publicKey, seed: lengths.seed },
2136
- keygen: (seed) => keygen(seed),
2137
- getPublicKey: (secretKey) => curve.getPublicKey(secretKey)
2138
- };
2139
2156
  }
2140
- function ecdhKem(curve, allowZeroKey = false) {
2141
- const kg = ecKeygen(curve, allowZeroKey);
2142
- if (!curve.getSharedSecret)
2143
- throw new Error("wrong curve");
2144
- return {
2145
- lengths: { ...kg.lengths, msg: kg.lengths.seed, cipherText: kg.lengths.publicKey },
2146
- keygen: kg.keygen,
2147
- getPublicKey: kg.getPublicKey,
2148
- encapsulate(publicKey, rand = randomBytes(curve.lengths.seed)) {
2149
- const seed = copyBytes(rand);
2150
- let ek = void 0;
2151
- try {
2152
- ek = this.keygen(seed).secretKey;
2153
- const sharedSecret = this.decapsulate(publicKey, ek);
2154
- const cipherText = curve.getPublicKey(ek);
2155
- return { sharedSecret, cipherText };
2156
- } finally {
2157
- cleanBytes(seed);
2158
- if (ek)
2159
- cleanBytes(ek);
2160
- }
2161
- },
2162
- decapsulate(cipherText, secretKey) {
2163
- const res = curve.getSharedSecret(secretKey, cipherText);
2164
- return curve.lengths.publicKeyHasPrefix ? res.subarray(1) : res;
2157
+ function checkItemHashes(item, idx, errors) {
2158
+ const entries = Object.entries(item.hashes);
2159
+ if (entries.length === 0) {
2160
+ errors.push(
2161
+ issue(
2162
+ "SCHEMA_TYPE_MISMATCH",
2163
+ ["items", idx, "hashes"],
2164
+ "hashes must be a non-empty CBOR map of <alg-id> -> <digest>"
2165
+ )
2166
+ );
2167
+ return;
2168
+ }
2169
+ for (const [alg, digest] of entries) {
2170
+ if (!(alg in HASH_ALG_LENGTHS)) {
2171
+ errors.push(
2172
+ issue("UNSUPPORTED_HASH_ALG", ["items", idx, "hashes", alg], `unknown hash alg: ${alg}`)
2173
+ );
2174
+ continue;
2165
2175
  }
2166
- };
2176
+ const expected = HASH_ALG_LENGTHS[alg];
2177
+ if (digest.length !== expected) {
2178
+ errors.push(
2179
+ issue(
2180
+ "HASH_DIGEST_LENGTH_MISMATCH",
2181
+ ["items", idx, "hashes", alg],
2182
+ `hashes['${alg}'] digest length ${digest.length} != ${expected}`
2183
+ )
2184
+ );
2185
+ }
2186
+ }
2167
2187
  }
2168
- function splitLengths(lst, name) {
2169
- return splitCoder(name, ...lst.map((i) => {
2170
- if (typeof i.lengths[name] !== "number")
2171
- throw new Error("wrong length: " + name);
2172
- return i.lengths[name];
2173
- }));
2188
+ function checkItemUris(uris, basePath, errors) {
2189
+ uris.forEach((chunks, ui) => validateOneUri(chunks, [...basePath, ui], errors));
2174
2190
  }
2175
- function expandSeedXof(xof) {
2176
- return ((seed, seedLen) => xof(seed, { dkLen: seedLen }));
2191
+ function validateOneUri(chunks, path, errors) {
2192
+ const reconstructed = reconstructChunkedUri(chunks);
2193
+ if (!reconstructed.ok) {
2194
+ errors.push(issue(reconstructed.code, path, reconstructed.reason));
2195
+ return;
2196
+ }
2197
+ const uri = reconstructed.uri;
2198
+ if (uri.includes("#")) {
2199
+ errors.push(
2200
+ issue("INVALID_URI", path, "URI contains a fragment identifier ('#'), which is forbidden")
2201
+ );
2202
+ return;
2203
+ }
2204
+ const sepIdx = uri.indexOf("://");
2205
+ if (sepIdx <= 0 || !/^[a-z][a-z0-9+.-]*$/i.test(uri.slice(0, sepIdx))) {
2206
+ errors.push(
2207
+ issue("INVALID_URI", path, "URI is not absolute (missing scheme://hierarchical-part)")
2208
+ );
2209
+ return;
2210
+ }
2211
+ const scheme = uri.slice(0, sepIdx).toLowerCase();
2212
+ const rest = uri.slice(sepIdx + "://".length);
2213
+ if (scheme === "ar") {
2214
+ if (!/^ar:\/\/[A-Za-z0-9_-]{43}$/.test("ar://" + rest)) {
2215
+ errors.push(
2216
+ issue(
2217
+ "INVALID_URI",
2218
+ path,
2219
+ "ar:// URI does not match `^ar://[A-Za-z0-9_-]{43}$` (43-char base64url txid, no path/query/fragment)"
2220
+ )
2221
+ );
2222
+ }
2223
+ return;
2224
+ }
2225
+ if (scheme === "ipfs") {
2226
+ const slashIdx = rest.indexOf("/");
2227
+ const cid = slashIdx === -1 ? rest : rest.slice(0, slashIdx);
2228
+ if (!validateCidProfile(cid)) {
2229
+ errors.push(
2230
+ issue("INVALID_URI", path, "ipfs:// URI is not a valid CID under the Label 309 profile")
2231
+ );
2232
+ }
2233
+ return;
2234
+ }
2235
+ errors.push(
2236
+ issue("INVALID_URI", path, "unsupported URI scheme; v1 PoE URI set is {ar://, ipfs://}")
2237
+ );
2177
2238
  }
2178
- function combineKeys(realSeedLen, expandSeed_, ...ck_) {
2179
- const expandSeed = expandSeed_;
2180
- const ck = ck_;
2181
- const seedCoder = splitLengths(ck, "seed");
2182
- const pkCoder = splitLengths(ck, "publicKey");
2183
- anumber(realSeedLen);
2184
- function expandDecapsulationKey(seed) {
2185
- abytes(seed, realSeedLen);
2186
- const expandedRaw = expandSeed(seed, seedCoder.bytesLen);
2187
- const expandedSeed = expandedRaw.buffer === seed.buffer ? copyBytes(expandedRaw) : expandedRaw;
2188
- const expanded = [];
2189
- const keySecret = [];
2190
- const secretKey = [];
2191
- const publicKey = [];
2192
- let ok = false;
2193
- try {
2194
- for (const part of seedCoder.decode(expandedSeed))
2195
- expanded.push(copyBytes(part));
2196
- for (let i = 0; i < ck.length; i++) {
2197
- const keys = ck[i].keygen(expanded[i]);
2198
- keySecret.push(keys.secretKey);
2199
- secretKey.push(copyBytes(keys.secretKey));
2200
- publicKey.push(keys.publicKey);
2239
+ function checkItemEnc(item, idx, errors) {
2240
+ const hasContentHash = Object.keys(item.hashes).some((alg) => alg in HASH_ALG_LENGTHS);
2241
+ if (!hasContentHash) {
2242
+ errors.push(
2243
+ issue(
2244
+ "ENC_REQUIRES_CONTENT_HASH",
2245
+ ["items", idx, "enc"],
2246
+ "item carries `enc` but `hashes` has no content-hash entry (sha2-256 or blake2b-256)"
2247
+ )
2248
+ );
2249
+ return;
2250
+ }
2251
+ const encParse = EncryptionEnvelopeSchema.safeParse(item.enc);
2252
+ if (!encParse.success) {
2253
+ for (const zissue of encParse.error.issues) {
2254
+ const mapped = mapZodIssue(zissue, item.enc);
2255
+ errors.push({
2256
+ ...mapped,
2257
+ path: ["items", idx, "enc", ...mapped.path]
2258
+ });
2259
+ }
2260
+ return;
2261
+ }
2262
+ const enc = encParse.data;
2263
+ const basePath = ["items", idx, "enc"];
2264
+ if (typeof enc.scheme !== "number" || !Number.isInteger(enc.scheme) || enc.scheme !== 1) {
2265
+ errors.push(
2266
+ issue(
2267
+ "UNSUPPORTED_ENVELOPE_SCHEME",
2268
+ [...basePath, "scheme"],
2269
+ `enc.scheme must be the unsigned integer 1; got ${String(enc.scheme)}`
2270
+ )
2271
+ );
2272
+ }
2273
+ if (UNAUTHENTICATED_CIPHER_RE.test(enc.aead)) {
2274
+ errors.push(
2275
+ issue(
2276
+ "UNAUTHENTICATED_CIPHER_FORBIDDEN",
2277
+ [...basePath, "aead"],
2278
+ `'${enc.aead}' is an unauthenticated cipher; Label 309 mandates an authenticated (AEAD) cipher`
2279
+ )
2280
+ );
2281
+ return;
2282
+ }
2283
+ if (!(enc.aead in AEAD_NONCE_LENGTHS)) {
2284
+ errors.push(
2285
+ issue("UNSUPPORTED_AEAD_ALG", [...basePath, "aead"], `unknown aead alg: ${enc.aead}`)
2286
+ );
2287
+ return;
2288
+ }
2289
+ const expectedNonceLen = AEAD_NONCE_LENGTHS[enc.aead];
2290
+ if (enc.nonce.length !== expectedNonceLen) {
2291
+ errors.push(
2292
+ issue(
2293
+ "NONCE_LENGTH_MISMATCH",
2294
+ [...basePath, "nonce"],
2295
+ `nonce length ${enc.nonce.length} != ${expectedNonceLen} for ${enc.aead}`
2296
+ )
2297
+ );
2298
+ }
2299
+ if (enc.kem !== void 0 && !(enc.kem in KEM_SLOT_DESCRIPTORS)) {
2300
+ errors.push(issue("UNSUPPORTED_KEM_ALG", [...basePath, "kem"], `unknown kem alg: ${enc.kem}`));
2301
+ }
2302
+ const hasSlots = enc.slots !== void 0;
2303
+ const hasSlotsMac = enc.slots_mac !== void 0;
2304
+ const hasPassphrase = enc.passphrase !== void 0;
2305
+ if (hasSlots && hasPassphrase) {
2306
+ errors.push(
2307
+ issue("ENC_EXCLUSIVITY_VIOLATION", basePath, "enc combines slots with passphrase; pick one")
2308
+ );
2309
+ }
2310
+ if (hasSlots && !hasSlotsMac) {
2311
+ errors.push(
2312
+ issue("ENC_SLOTS_MAC_REQUIRED", basePath, "enc.slots present but enc.slots_mac absent")
2313
+ );
2314
+ }
2315
+ if (hasSlotsMac && !hasSlots) {
2316
+ errors.push(
2317
+ issue("ENC_SLOTS_REQUIRED", basePath, "enc.slots_mac present but enc.slots absent")
2318
+ );
2319
+ }
2320
+ if (hasSlots && enc.kem === void 0) {
2321
+ errors.push(issue("ENC_KEM_REQUIRED", basePath, "enc.slots present but enc.kem absent"));
2322
+ }
2323
+ if (!hasSlots && !hasPassphrase) {
2324
+ errors.push(
2325
+ issue(
2326
+ "ENC_NO_KEY_PATH",
2327
+ basePath,
2328
+ "enc requires either slots or passphrase \u2014 no on-chain key path otherwise"
2329
+ )
2330
+ );
2331
+ }
2332
+ if (hasSlots) {
2333
+ const slotCount = enc.slots.length;
2334
+ if (slotCount < 1) {
2335
+ errors.push(
2336
+ issue("ENC_SLOTS_EMPTY", [...basePath, "slots"], `slots length ${slotCount} < 1`)
2337
+ );
2338
+ } else if (slotCount > MAX_SLOTS) {
2339
+ errors.push(
2340
+ issue(
2341
+ "ENC_SLOTS_TOO_MANY",
2342
+ [...basePath, "slots"],
2343
+ `slots length ${slotCount} exceeds MAX_SLOTS=${MAX_SLOTS}`
2344
+ )
2345
+ );
2346
+ } else {
2347
+ const descriptor = enc.kem !== void 0 ? KEM_SLOT_DESCRIPTORS[enc.kem] : void 0;
2348
+ if (descriptor !== void 0) {
2349
+ const rawSlotKeys = rawSlotKeySets(item.enc);
2350
+ const seenKemMaterial = /* @__PURE__ */ new Set();
2351
+ enc.slots.forEach((slot, si) => {
2352
+ const slotPath = [...basePath, "slots", si];
2353
+ checkSlotShape(
2354
+ slot,
2355
+ rawSlotKeys[si] ?? /* @__PURE__ */ new Set(),
2356
+ descriptor,
2357
+ enc.kem,
2358
+ slotPath,
2359
+ errors
2360
+ );
2361
+ const material = slotKemMaterial(slot, descriptor);
2362
+ if (material !== void 0) {
2363
+ const key = bytesToHex(material);
2364
+ if (seenKemMaterial.has(key)) {
2365
+ errors.push(
2366
+ issue(
2367
+ "ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
2368
+ [...slotPath, descriptor.field],
2369
+ `slot ${si} ${descriptor.field} duplicates an earlier slot \u2014 per-slot KEK uniqueness is violated`
2370
+ )
2371
+ );
2372
+ } else {
2373
+ seenKemMaterial.add(key);
2374
+ }
2375
+ }
2376
+ });
2377
+ const perSlotBytes = descriptor.fieldLength + descriptor.wrapLength;
2378
+ const decodedEnvelopeBytes = NONCE_LENGTH + SLOTS_MAC_LENGTH + slotCount * perSlotBytes;
2379
+ if (decodedEnvelopeBytes > MAX_DECODED_ENVELOPE_BYTES) {
2380
+ errors.push(
2381
+ issue(
2382
+ "ENC_ENVELOPE_TOO_LARGE",
2383
+ [...basePath, "slots"],
2384
+ `decoded envelope size ${decodedEnvelopeBytes} exceeds MAX_DECODED_ENVELOPE_BYTES=${MAX_DECODED_ENVELOPE_BYTES}`
2385
+ )
2386
+ );
2387
+ }
2201
2388
  }
2202
- ok = true;
2203
- return { secretKey, publicKey };
2204
- } finally {
2205
- cleanBytes(expandedSeed, expanded, keySecret);
2206
- if (!ok)
2207
- cleanBytes(secretKey);
2208
2389
  }
2209
2390
  }
2210
- return {
2211
- info: { lengths: { seed: realSeedLen, publicKey: pkCoder.bytesLen, secretKey: realSeedLen } },
2212
- getPublicKey(secretKey) {
2213
- return this.keygen(secretKey).publicKey;
2214
- },
2215
- keygen(seed = randomBytes(realSeedLen)) {
2216
- const { publicKey: pk, secretKey } = expandDecapsulationKey(seed);
2217
- try {
2218
- const publicKey = pkCoder.encode(pk);
2219
- return { secretKey: seed, publicKey };
2220
- } finally {
2221
- cleanBytes(pk);
2222
- cleanBytes(secretKey);
2391
+ if (hasPassphrase) {
2392
+ const pp = enc.passphrase;
2393
+ const ppPath = [...basePath, "passphrase"];
2394
+ if (!PASSPHRASE_KDF_ALGS.has(pp.alg)) {
2395
+ errors.push(
2396
+ issue(
2397
+ "ENC_PASSPHRASE_ALG_UNSUPPORTED",
2398
+ [...ppPath, "alg"],
2399
+ `unknown passphrase kdf alg: ${pp.alg}`
2400
+ )
2401
+ );
2402
+ return;
2403
+ }
2404
+ if (pp.alg === "argon2id") {
2405
+ const allowed = /* @__PURE__ */ new Set(["m", "t", "p"]);
2406
+ for (const k of Object.keys(pp.params)) {
2407
+ if (!allowed.has(k)) {
2408
+ errors.push(
2409
+ issue(
2410
+ "SCHEMA_UNKNOWN_FIELD",
2411
+ [...ppPath, "params", k],
2412
+ `unknown argon2id params field: ${k}`
2413
+ )
2414
+ );
2415
+ }
2223
2416
  }
2224
- },
2225
- expandDecapsulationKey,
2226
- realSeedLen
2227
- };
2228
- }
2229
- function combineKEMS(realSeedLen, realMsgLen, expandSeed, combiner, ...kems) {
2230
- const rawCombiner = combiner;
2231
- const rawKems = kems;
2232
- const keys = combineKeys(realSeedLen, expandSeed, ...rawKems);
2233
- const ctCoder = splitLengths(rawKems, "cipherText");
2234
- const pkCoder = splitLengths(rawKems, "publicKey");
2235
- const msgCoder = splitLengths(rawKems, "msg");
2236
- anumber(realMsgLen);
2237
- const lengths = Object.freeze({
2238
- ...keys.info.lengths,
2239
- msg: realMsgLen,
2240
- msgRand: msgCoder.bytesLen,
2241
- cipherText: ctCoder.bytesLen
2242
- });
2243
- return Object.freeze({
2244
- lengths,
2245
- getPublicKey: keys.getPublicKey,
2246
- keygen: keys.keygen,
2247
- encapsulate(pk, randomness = randomBytes(msgCoder.bytesLen)) {
2248
- const pks = pkCoder.decode(pk);
2249
- const rand = msgCoder.decode(randomness);
2250
- const sharedSecret = [];
2251
- const cipherText = [];
2252
- try {
2253
- for (let i = 0; i < rawKems.length; i++) {
2254
- const enc = rawKems[i].encapsulate(pks[i], rand[i]);
2255
- sharedSecret.push(enc.sharedSecret);
2256
- cipherText.push(enc.cipherText);
2417
+ const p = pp.params;
2418
+ const argonInt = (val, name) => {
2419
+ if (typeof val !== "number" || !Number.isInteger(val)) {
2420
+ errors.push(
2421
+ issue(
2422
+ "SCHEMA_TYPE_MISMATCH",
2423
+ [...ppPath, "params", name],
2424
+ `argon2id params.${name} must be a CBOR unsigned integer`
2425
+ )
2426
+ );
2427
+ return null;
2257
2428
  }
2258
- return {
2259
- // Detach the combiner result before cleanup: a caller-provided combiner may alias one of
2260
- // the child sharedSecret buffers, and those child buffers are zeroized immediately below.
2261
- sharedSecret: copyBytes(rawCombiner(pks, cipherText, sharedSecret)),
2262
- cipherText: ctCoder.encode(cipherText)
2263
- };
2264
- } finally {
2265
- cleanBytes(sharedSecret, cipherText);
2429
+ return val;
2430
+ };
2431
+ const mVal = argonInt(p.m, "m");
2432
+ const tVal = argonInt(p.t, "t");
2433
+ const pVal = argonInt(p.p, "p");
2434
+ if (mVal !== null && mVal < 65536) {
2435
+ errors.push(
2436
+ issue(
2437
+ "ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
2438
+ [...ppPath, "params", "m"],
2439
+ "argon2id requires m >= 65536 KiB"
2440
+ )
2441
+ );
2266
2442
  }
2267
- },
2268
- decapsulate(ct, seed) {
2269
- const cts = ctCoder.decode(ct);
2270
- const { publicKey, secretKey } = keys.expandDecapsulationKey(seed);
2271
- const sharedSecret = rawKems.map((i, j) => i.decapsulate(cts[j], secretKey[j]));
2272
- try {
2273
- return copyBytes(rawCombiner(publicKey, cts, sharedSecret));
2274
- } finally {
2275
- cleanBytes(secretKey, sharedSecret);
2443
+ if (tVal !== null && tVal < 3) {
2444
+ errors.push(
2445
+ issue(
2446
+ "ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
2447
+ [...ppPath, "params", "t"],
2448
+ "argon2id requires t >= 3"
2449
+ )
2450
+ );
2451
+ }
2452
+ if (pVal !== null && pVal < 1) {
2453
+ errors.push(
2454
+ issue(
2455
+ "ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
2456
+ [...ppPath, "params", "p"],
2457
+ "argon2id requires p >= 1"
2458
+ )
2459
+ );
2276
2460
  }
2277
2461
  }
2278
- });
2462
+ }
2279
2463
  }
2280
- var x25519kem = /* @__PURE__ */ ecdhKem(x25519);
2281
- var ml_kem768_x25519 = /* @__PURE__ */ (() => combineKEMS(
2282
- 32,
2283
- 32,
2284
- expandSeedXof(shake256),
2285
- // Awesome label, so much escaping hell in a single line.
2286
- (pk, ct, ss) => sha3_256(concatBytes$1(ss[0], ss[1], ct[1], pk[1], asciiToBytes("\\.//^\\"))),
2287
- ml_kem768,
2288
- x25519kem
2289
- ))();
2290
- var XWing = /* @__PURE__ */ (() => ml_kem768_x25519)();
2291
- var AeadVerificationError2 = class extends Error {
2292
- code = "aead_verification_failed";
2293
- constructor(message, options) {
2294
- super(message, options);
2295
- this.name = "AeadVerificationError";
2464
+ var SLOT_KEY_UNIVERSE = /* @__PURE__ */ new Set(["epk", "kem_ct", "wrap"]);
2465
+ function checkSlotShape(slot, rawKeys, descriptor, kem, slotPath, errors) {
2466
+ const foreignField = descriptor.field === "epk" ? "kem_ct" : "epk";
2467
+ if (rawKeys.has(foreignField)) {
2468
+ errors.push(
2469
+ issue(
2470
+ "ENC_SLOT_INVALID_SHAPE",
2471
+ [...slotPath, foreignField],
2472
+ `slot carries '${foreignField}' but kem='${kem}' expects '${descriptor.field}'`
2473
+ )
2474
+ );
2296
2475
  }
2297
- };
2298
- function chacha20Poly1305Decrypt(opts2) {
2299
- try {
2300
- return chacha20poly1305(opts2.key, opts2.nonce, opts2.aad).decrypt(opts2.ciphertext);
2301
- } catch (cause) {
2302
- throw new AeadVerificationError2("chacha20-poly1305 decrypt failed", { cause });
2476
+ for (const k of rawKeys) {
2477
+ if (!SLOT_KEY_UNIVERSE.has(k)) {
2478
+ errors.push(
2479
+ issue(
2480
+ "ENC_SLOT_INVALID_SHAPE",
2481
+ [...slotPath, k],
2482
+ `slot carries unexpected key '${k}'; a slot is a 2-key map {${descriptor.field}, wrap}`
2483
+ )
2484
+ );
2485
+ }
2486
+ }
2487
+ if (descriptor.field === "epk") {
2488
+ if (slot.epk === void 0) {
2489
+ errors.push(
2490
+ issue(
2491
+ "ENC_SLOT_INVALID_SHAPE",
2492
+ [...slotPath, "epk"],
2493
+ `slot for kem='${kem}' is missing required 'epk'`
2494
+ )
2495
+ );
2496
+ } else if (slot.epk.length !== descriptor.fieldLength) {
2497
+ errors.push(
2498
+ issue(
2499
+ KEM_FIELD_LENGTH_CODE.epk,
2500
+ [...slotPath, "epk"],
2501
+ `slot.epk length ${slot.epk.length} != ${descriptor.fieldLength} for ${kem}`
2502
+ )
2503
+ );
2504
+ }
2505
+ } else {
2506
+ if (slot.kem_ct === void 0) {
2507
+ errors.push(
2508
+ issue(
2509
+ "ENC_SLOT_INVALID_SHAPE",
2510
+ [...slotPath, "kem_ct"],
2511
+ `slot for kem='${kem}' is missing required 'kem_ct'`
2512
+ )
2513
+ );
2514
+ } else {
2515
+ const reassembled = bytesChunkArrayConcat(slot.kem_ct).length;
2516
+ if (reassembled !== descriptor.fieldLength) {
2517
+ errors.push(
2518
+ issue(
2519
+ KEM_FIELD_LENGTH_CODE.kem_ct,
2520
+ [...slotPath, "kem_ct"],
2521
+ `slot.kem_ct reassembles to ${reassembled} bytes != ${descriptor.fieldLength} for ${kem}`
2522
+ )
2523
+ );
2524
+ }
2525
+ }
2526
+ }
2527
+ if (slot.wrap === void 0) {
2528
+ errors.push(
2529
+ issue(
2530
+ "ENC_SLOT_INVALID_SHAPE",
2531
+ [...slotPath, "wrap"],
2532
+ `slot for kem='${kem}' is missing required 'wrap'`
2533
+ )
2534
+ );
2535
+ } else if (slot.wrap.length !== descriptor.wrapLength) {
2536
+ errors.push(
2537
+ issue(
2538
+ "WRAP_LENGTH_MISMATCH",
2539
+ [...slotPath, "wrap"],
2540
+ `slot.wrap length ${slot.wrap.length} != ${descriptor.wrapLength}`
2541
+ )
2542
+ );
2543
+ }
2544
+ }
2545
+ function slotKemMaterial(slot, descriptor) {
2546
+ if (descriptor.field === "epk") {
2547
+ return slot.epk;
2548
+ }
2549
+ if (slot.kem_ct === void 0) return void 0;
2550
+ return bytesChunkArrayConcat(slot.kem_ct);
2551
+ }
2552
+ function bytesToHex(bytes) {
2553
+ let hex = "";
2554
+ for (let i = 0; i < bytes.length; i++) {
2555
+ hex += bytes[i].toString(16).padStart(2, "0");
2303
2556
  }
2557
+ return hex;
2304
2558
  }
2305
- function xchacha20Poly1305Decrypt2(opts2) {
2306
- try {
2307
- return xchacha20poly1305(opts2.key, opts2.nonce, opts2.aad).decrypt(opts2.ciphertext);
2308
- } catch (cause) {
2309
- throw new AeadVerificationError2("xchacha20-poly1305 decrypt failed", { cause });
2310
- }
2559
+ function rawSlotKeySets(rawEnc) {
2560
+ const slots = mapLikeGet(rawEnc, "slots");
2561
+ if (!Array.isArray(slots)) return [];
2562
+ return slots.map((slot) => {
2563
+ const keys = /* @__PURE__ */ new Set();
2564
+ if (slot instanceof Map) {
2565
+ for (const k of slot.keys()) if (typeof k === "string") keys.add(k);
2566
+ } else if (typeof slot === "object" && slot !== null) {
2567
+ for (const k of Object.keys(slot)) keys.add(k);
2568
+ }
2569
+ return keys;
2570
+ });
2311
2571
  }
2312
- function hkdfSha256(opts2) {
2313
- return hkdf(sha256, opts2.ikm, opts2.salt, opts2.info, opts2.length);
2572
+ function mapLikeGet(value, key) {
2573
+ if (value instanceof Map) return value.get(key);
2574
+ if (typeof value === "object" && value !== null) {
2575
+ return value[key];
2576
+ }
2577
+ return void 0;
2314
2578
  }
2315
- var MLKEM768X25519_ENC_LENGTH = 1120;
2316
- var MLKEM768X25519_SEED_LENGTH = 32;
2317
- function mlkem768x25519Decapsulate(opts2) {
2318
- if (opts2.secretSeed.length !== MLKEM768X25519_SEED_LENGTH) {
2319
- throw new Error(
2320
- `mlkem768x25519 secret seed must be ${MLKEM768X25519_SEED_LENGTH} bytes, got ${opts2.secretSeed.length}`
2579
+ function checkMerkleCommit(commit, idx, errors) {
2580
+ const basePath = ["merkle", idx];
2581
+ if (!(commit.alg in MERKLE_COMMIT_ALG_LENGTHS)) {
2582
+ errors.push(
2583
+ issue(
2584
+ "UNSUPPORTED_MERKLE_COMMIT_ALG",
2585
+ [...basePath, "alg"],
2586
+ `unknown merkle commitment alg: ${commit.alg}`
2587
+ )
2321
2588
  );
2589
+ return;
2322
2590
  }
2323
- if (opts2.enc.length !== MLKEM768X25519_ENC_LENGTH) {
2324
- throw new Error(
2325
- `mlkem768x25519 enc must be ${MLKEM768X25519_ENC_LENGTH} bytes, got ${opts2.enc.length}`
2591
+ const expected = MERKLE_COMMIT_ALG_LENGTHS[commit.alg];
2592
+ if (commit.root.length !== expected) {
2593
+ errors.push(
2594
+ issue(
2595
+ "HASH_DIGEST_LENGTH_MISMATCH",
2596
+ [...basePath, "root"],
2597
+ `merkle entry root length ${commit.root.length} != ${expected} for ${commit.alg}`
2598
+ )
2326
2599
  );
2327
2600
  }
2328
- return XWing.decapsulate(opts2.enc, opts2.secretSeed);
2329
- }
2330
- var X25519LowOrderPointError = class extends Error {
2331
- code = "X25519_LOW_ORDER_POINT";
2332
- constructor(options) {
2333
- super("x25519 ECDH rejected: peer public key is a small-order point", options);
2334
- this.name = "X25519LowOrderPointError";
2601
+ if (commit.uris) {
2602
+ checkItemUris(commit.uris, [...basePath, "uris"], errors);
2335
2603
  }
2336
- };
2337
- var NOBLE_LOW_ORDER_MESSAGE = "invalid private or public key received";
2338
- function x25519PublicKey(opts2) {
2339
- return x25519.getPublicKey(opts2.secretKey);
2340
2604
  }
2341
- function x25519Ecdh(opts2) {
2342
- try {
2343
- return x25519.getSharedSecret(opts2.secretKey, opts2.theirPublicKey);
2344
- } catch (e) {
2345
- if (e instanceof Error && e.message === NOBLE_LOW_ORDER_MESSAGE) {
2346
- throw new X25519LowOrderPointError({ cause: e });
2605
+ function checkSigEntry(entry, idx, errors, info) {
2606
+ if (entry.cose_key !== void 0) {
2607
+ const keyIssue = inspectCoseKey(entry.cose_key, idx);
2608
+ if (keyIssue !== null) {
2609
+ errors.push(keyIssue);
2610
+ return;
2347
2611
  }
2348
- throw e;
2349
2612
  }
2350
- }
2351
- var EciesSealedPoeError = class extends Error {
2352
- code;
2353
- constructor(code, message, options) {
2354
- super(message, options);
2355
- this.name = "EciesSealedPoeError";
2356
- this.code = code;
2613
+ const merged = bytesChunkArrayConcat(entry.cose_sign1);
2614
+ let cose;
2615
+ try {
2616
+ cose = decodeCoseSign1(merged);
2617
+ } catch (cause) {
2618
+ errors.push(
2619
+ issue(
2620
+ "MALFORMED_SIG_COSE_SIGN1",
2621
+ ["sigs", idx],
2622
+ cause instanceof CoseVerifyError || cause instanceof Error ? cause.message : String(cause)
2623
+ )
2624
+ );
2625
+ return;
2357
2626
  }
2358
- };
2359
- function encodeCanonicalCbor3(value) {
2360
- return encode(value, {
2361
- cde: true,
2362
- collapseBigInts: true,
2363
- rejectDuplicateKeys: true,
2364
- sortKeys: sortCoreDeterministic
2365
- });
2366
- }
2367
- var CHUNK_MAX_BYTES = 64;
2368
- function chunkKemCt(value) {
2369
- if (value.length === 0) {
2370
- throw new Error("chunkKemCt: refusing to chunk an empty byte string");
2627
+ if (cose.payload !== null) {
2628
+ errors.push(
2629
+ issue(
2630
+ "MALFORMED_SIG_COSE_SIGN1",
2631
+ ["sigs", idx],
2632
+ "COSE_Sign1 payload must be null (detached); attached form forbidden"
2633
+ )
2634
+ );
2635
+ return;
2371
2636
  }
2372
- const chunks = [];
2373
- for (let i = 0; i < value.length; i += CHUNK_MAX_BYTES) {
2374
- chunks.push(value.subarray(i, Math.min(i + CHUNK_MAX_BYTES, value.length)));
2637
+ const alg = cose.protectedHeader.get(1);
2638
+ if (typeof alg !== "number" || !KNOWN_SIG_ALG_IDS.has(alg)) {
2639
+ info.push(
2640
+ issue(
2641
+ "SIGNATURE_UNSUPPORTED",
2642
+ ["sigs", idx],
2643
+ `COSE_Sign1 protected alg ${String(alg)} not in {-8, -19}`
2644
+ )
2645
+ );
2375
2646
  }
2376
- return chunks;
2377
- }
2378
- function joinKemCt(chunks) {
2379
- let total = 0;
2380
- for (const c of chunks) total += c.length;
2381
- const out = new Uint8Array(total);
2382
- let offset = 0;
2383
- for (const c of chunks) {
2384
- out.set(c, offset);
2385
- offset += c.length;
2647
+ const protectedKid = cose.protectedHeader.get(4);
2648
+ if (protectedKid instanceof Uint8Array && protectedKid.length === 32 && entry.cose_key !== void 0) {
2649
+ errors.push(
2650
+ issue(
2651
+ "SIG_ENTRY_KID_COSE_KEY_CONFLICT",
2652
+ ["sigs", idx],
2653
+ "sigs[i] carries both a 32-byte protected `kid` (path 1) and an inline `cose_key` (path 2); paths are mutually exclusive"
2654
+ )
2655
+ );
2386
2656
  }
2387
- return out;
2388
- }
2389
- function slotsToMacCbor(slots, kem) {
2390
- let value;
2391
- if (kem === "x25519") {
2392
- value = slots.map((s) => ({ epk: s.epk, wrap: s.wrap }));
2393
- } else {
2394
- value = slots.map((s) => ({
2395
- // Canonicalize the chunk boundaries before the MAC commits to them:
2396
- // reassemble the logical ciphertext and re-split into canonical ≤ 64-byte
2397
- // chunks. The on-wire `kem_ct` array is a transport detail (the Cardano
2398
- // ledger's 64-byte metadatum cap), and a hostile or non-canonical chunking
2399
- // ([1, 63, …] instead of [64, …]) reassembles to the SAME bytes — so the
2400
- // MAC must be invariant to it. Committing to the verbatim wire chunks would
2401
- // let an attacker re-chunk an honest envelope and break the slots_mac match
2402
- // for an honest recipient. Honest (already-64B-chunked) records are
2403
- // unchanged; a real byte flip still changes the reassembled bytes and is
2404
- // still rejected.
2405
- kem_ct: chunkKemCt(joinKemCt(s.kem_ct)),
2406
- wrap: s.wrap
2407
- }));
2408
- }
2409
- return encodeCanonicalCbor3(value);
2410
- }
2411
- var CARDANO_POE_HKDF_INFO_KEK = new TextEncoder().encode("cardano-poe-kek-v1");
2412
- var CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 = new TextEncoder().encode(
2413
- "cardano-poe-kek-mlkem768x25519-v1"
2414
- );
2415
- var CARDANO_POE_HKDF_INFO_SLOTS_MAC = new TextEncoder().encode(
2416
- "cardano-poe-slots-mac-v1"
2417
- );
2418
- var ZERO_NONCE_12 = new Uint8Array(12);
2419
- if (CARDANO_POE_HKDF_INFO_KEK.length !== 18) {
2420
- throw new Error("CARDANO_POE_HKDF_INFO_KEK byte-length invariant violated (expected 18)");
2421
- }
2422
- if (CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519.length !== 33) {
2423
- throw new Error(
2424
- "CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 byte-length invariant violated (expected 33)"
2425
- );
2426
- }
2427
- if (CARDANO_POE_HKDF_INFO_SLOTS_MAC.length !== 24) {
2428
- throw new Error("CARDANO_POE_HKDF_INFO_SLOTS_MAC byte-length invariant violated (expected 24)");
2429
- }
2430
- if (ZERO_NONCE_12.length !== 12) {
2431
- throw new Error("ZERO_NONCE_12 byte-length invariant violated (expected 12)");
2432
- }
2433
- function compareCt2(a, b) {
2434
- if (a.length !== b.length) return false;
2435
- let diff = 0;
2436
- for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
2437
- return diff === 0;
2438
- }
2439
- function selectBundleSecrets(envelope, bundle) {
2440
- return envelope.kem === "x25519" ? bundle.x25519PrivateKeys : bundle.mlkem768x25519SecretSeeds;
2441
- }
2442
- var ZERO_NONCE_122 = new Uint8Array(12);
2443
- var EMPTY_SALT2 = new Uint8Array(0);
2444
- var X25519_SECRET_KEY_LENGTH2 = 32;
2445
- var X25519_PUBLIC_KEY_LENGTH2 = 32;
2446
- var NONCE_LENGTH2 = 24;
2447
- var WRAP_LENGTH2 = 48;
2448
- var SLOTS_MAC_LENGTH2 = 32;
2449
- function concat2(a, b) {
2450
- const out = new Uint8Array(a.length + b.length);
2451
- out.set(a, 0);
2452
- out.set(b, a.length);
2453
- return out;
2454
2657
  }
2455
- function assertEnvelopeStructure(envelope, multiPrivKeys, singlePrivKey) {
2456
- if (envelope.scheme !== 1) {
2457
- throw new EciesSealedPoeError(
2458
- "UNSUPPORTED_ENC_VERSION",
2459
- `envelope.scheme=${String(envelope.scheme)} unsupported (expected 1)`
2658
+ function inspectCoseKey(keyChunks, i) {
2659
+ let decoded;
2660
+ try {
2661
+ decoded = decodeCanonicalCbor(bytesChunkArrayConcat(keyChunks));
2662
+ } catch (cause) {
2663
+ return issue(
2664
+ "MALFORMED_SIG_COSE_SIGN1",
2665
+ ["sigs", i, "cose_key"],
2666
+ `sigs[${i}].cose_key failed to decode as cbor<COSE_Key>: ${cause instanceof Error ? cause.message : String(cause)}`
2460
2667
  );
2461
2668
  }
2462
- if (envelope.aead !== "xchacha20-poly1305") {
2463
- throw new EciesSealedPoeError(
2464
- "UNSUPPORTED_AEAD_ALG",
2465
- `envelope.aead=${String(envelope.aead)} unsupported (expected 'xchacha20-poly1305')`
2669
+ const getLabel = (label) => {
2670
+ if (decoded instanceof Map) return decoded.get(label);
2671
+ if (typeof decoded === "object" && decoded !== null) {
2672
+ return decoded[String(label)];
2673
+ }
2674
+ return void 0;
2675
+ };
2676
+ const hasLabel = (label) => {
2677
+ if (decoded instanceof Map) return decoded.has(label);
2678
+ if (typeof decoded === "object" && decoded !== null) {
2679
+ return Object.prototype.hasOwnProperty.call(decoded, String(label));
2680
+ }
2681
+ return false;
2682
+ };
2683
+ if (hasLabel(-4)) {
2684
+ return issue(
2685
+ "SIG_PRIVATE_KEY_LEAKED",
2686
+ ["sigs", i, "cose_key"],
2687
+ "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"
2466
2688
  );
2467
2689
  }
2468
- if (envelope.kem !== "x25519" && envelope.kem !== "mlkem768x25519") {
2469
- throw new EciesSealedPoeError(
2470
- "UNSUPPORTED_KEM_ALG",
2471
- `envelope.kem=${String(envelope.kem)} unsupported (expected 'x25519' or 'mlkem768x25519')`
2690
+ const kty = getLabel(1);
2691
+ if (kty !== 1) {
2692
+ return issue(
2693
+ "MALFORMED_SIG_COSE_SIGN1",
2694
+ ["sigs", i, "cose_key"],
2695
+ `sigs[${i}].cose_key COSE_Key kty (label 1) must be 1 (OKP); got ${String(kty)}`
2472
2696
  );
2473
2697
  }
2474
- const n = envelope.slots.length;
2475
- if (n < 1) {
2476
- throw new EciesSealedPoeError("ENC_SLOTS_EMPTY", `envelope.slots.length=${n} must be >= 1`);
2698
+ const crv = getLabel(-1);
2699
+ if (crv !== 6) {
2700
+ return issue(
2701
+ "MALFORMED_SIG_COSE_SIGN1",
2702
+ ["sigs", i, "cose_key"],
2703
+ `sigs[${i}].cose_key COSE_Key crv (label -1) must be 6 (Ed25519); got ${String(crv)}`
2704
+ );
2477
2705
  }
2478
- if (envelope.nonce.length !== NONCE_LENGTH2) {
2479
- throw new EciesSealedPoeError(
2480
- "NONCE_LENGTH_MISMATCH",
2481
- `envelope.nonce MUST be exactly ${NONCE_LENGTH2} bytes, got ${envelope.nonce.length}`
2706
+ if (!hasLabel(-2)) {
2707
+ return issue(
2708
+ "MALFORMED_SIG_COSE_SIGN1",
2709
+ ["sigs", i, "cose_key"],
2710
+ `sigs[${i}].cose_key COSE_Key missing label -2 (Ed25519 public-key bytes)`
2482
2711
  );
2483
2712
  }
2484
- if (envelope.slots_mac.length !== SLOTS_MAC_LENGTH2) {
2485
- throw new EciesSealedPoeError(
2486
- "ENC_SLOTS_MAC_INVALID_LENGTH",
2487
- `envelope.slots_mac MUST be exactly ${SLOTS_MAC_LENGTH2} bytes, got ${envelope.slots_mac.length}`
2713
+ const x = getLabel(-2);
2714
+ if (!(x instanceof Uint8Array) || x.length !== 32) {
2715
+ const got = x instanceof Uint8Array ? `${x.length}-byte bstr` : typeof x;
2716
+ return issue(
2717
+ "MALFORMED_SIG_COSE_SIGN1",
2718
+ ["sigs", i, "cose_key"],
2719
+ `sigs[${i}].cose_key COSE_Key label -2 must be a 32-byte byte string (Ed25519 public key); got ${got}`
2488
2720
  );
2489
2721
  }
2490
- if (envelope.kem === "x25519") {
2491
- for (let i = 0; i < n; i++) {
2492
- const slot = envelope.slots[i];
2493
- if (slot.epk.length !== X25519_PUBLIC_KEY_LENGTH2) {
2494
- throw new EciesSealedPoeError(
2495
- "KEM_EPK_LENGTH_MISMATCH",
2496
- `envelope.slots[${i}].epk MUST be exactly ${X25519_PUBLIC_KEY_LENGTH2} bytes, got ${slot.epk.length}`
2497
- );
2498
- }
2499
- if (slot.wrap.length !== WRAP_LENGTH2) {
2500
- throw new EciesSealedPoeError(
2501
- "WRAP_LENGTH_MISMATCH",
2502
- `envelope.slots[${i}].wrap MUST be exactly ${WRAP_LENGTH2} bytes, got ${slot.wrap.length}`
2503
- );
2504
- }
2505
- }
2506
- } else {
2507
- for (let i = 0; i < n; i++) {
2508
- const slot = envelope.slots[i];
2509
- const enc = joinKemCt(slot.kem_ct);
2510
- if (enc.length !== MLKEM768X25519_ENC_LENGTH) {
2511
- throw new EciesSealedPoeError(
2512
- "KEM_CT_LENGTH_MISMATCH",
2513
- `envelope.slots[${i}].kem_ct MUST reassemble to exactly ${MLKEM768X25519_ENC_LENGTH} bytes, got ${enc.length}`
2514
- );
2515
- }
2516
- if (slot.wrap.length !== WRAP_LENGTH2) {
2517
- throw new EciesSealedPoeError(
2518
- "WRAP_LENGTH_MISMATCH",
2519
- `envelope.slots[${i}].wrap MUST be exactly ${WRAP_LENGTH2} bytes, got ${slot.wrap.length}`
2520
- );
2521
- }
2722
+ return null;
2723
+ }
2724
+ var ACCEPTED_CIDV1_MULTIBASE = /* @__PURE__ */ new Set(["b", "B", "f", "F", "z"]);
2725
+ var ACCEPTED_MULTICODECS = /* @__PURE__ */ new Set([85, 112, 113]);
2726
+ var ACCEPTED_MULTIHASHES = /* @__PURE__ */ new Map([
2727
+ [18, 32],
2728
+ [45600, 32]
2729
+ ]);
2730
+ function validateCidProfile(cid) {
2731
+ if (cid.length === 0) return false;
2732
+ if (cid.startsWith("Qm")) {
2733
+ let decoded;
2734
+ try {
2735
+ decoded = decodeBase58btc(cid);
2736
+ } catch {
2737
+ return false;
2522
2738
  }
2739
+ return decoded.length === 34 && decoded[0] === 18 && decoded[1] === 32;
2523
2740
  }
2524
- if (multiPrivKeys !== void 0) {
2525
- for (let i = 0; i < multiPrivKeys.length; i++) {
2526
- if (multiPrivKeys[i].length !== X25519_SECRET_KEY_LENGTH2) {
2527
- throw new EciesSealedPoeError(
2528
- "INVALID_RECIPIENT_KEY",
2529
- `recipientSecretKeys[${i}] MUST be exactly ${X25519_SECRET_KEY_LENGTH2} bytes, got ${multiPrivKeys[i].length}`
2530
- );
2531
- }
2532
- }
2533
- } else if (singlePrivKey !== void 0) {
2534
- if (singlePrivKey.length !== X25519_SECRET_KEY_LENGTH2) {
2535
- throw new EciesSealedPoeError(
2536
- "INVALID_RECIPIENT_KEY",
2537
- `recipientSecretKey MUST be exactly ${X25519_SECRET_KEY_LENGTH2} bytes, got ${singlePrivKey.length}`
2538
- );
2741
+ const mbPrefix = cid[0];
2742
+ if (!ACCEPTED_CIDV1_MULTIBASE.has(mbPrefix)) return false;
2743
+ let bytes;
2744
+ try {
2745
+ bytes = decodeMultibase(mbPrefix, cid.slice(1));
2746
+ } catch {
2747
+ return false;
2748
+ }
2749
+ if (bytes.length < 4) return false;
2750
+ const versionParse = readVarint(bytes, 0);
2751
+ if (versionParse === null || versionParse.value !== 1) return false;
2752
+ const codecParse = readVarint(bytes, versionParse.next);
2753
+ if (codecParse === null) return false;
2754
+ if (!ACCEPTED_MULTICODECS.has(codecParse.value)) return false;
2755
+ const mhParse = readVarint(bytes, codecParse.next);
2756
+ if (mhParse === null) return false;
2757
+ const lenParse = readVarint(bytes, mhParse.next);
2758
+ if (lenParse === null) return false;
2759
+ const digestLen = lenParse.value;
2760
+ const expectedLen = ACCEPTED_MULTIHASHES.get(mhParse.value);
2761
+ if (expectedLen === void 0 || digestLen !== expectedLen) return false;
2762
+ if (lenParse.next + digestLen !== bytes.length) return false;
2763
+ return true;
2764
+ }
2765
+ function readVarint(bytes, start) {
2766
+ let value = 0;
2767
+ let shift = 0;
2768
+ let i = start;
2769
+ while (i < bytes.length) {
2770
+ const b = bytes[i];
2771
+ value |= (b & 127) << shift;
2772
+ i++;
2773
+ if ((b & 128) === 0) return { value, next: i };
2774
+ shift += 7;
2775
+ if (shift > 28) return null;
2776
+ }
2777
+ return null;
2778
+ }
2779
+ function decodeMultibase(prefix, body) {
2780
+ switch (prefix) {
2781
+ case "b":
2782
+ return decodeBase32(body.toLowerCase(), "rfc4648-lower");
2783
+ case "B":
2784
+ return decodeBase32(body.toUpperCase(), "rfc4648-upper");
2785
+ case "f":
2786
+ return decodeBase16(body.toLowerCase());
2787
+ case "F":
2788
+ return decodeBase16(body.toUpperCase());
2789
+ case "z":
2790
+ return decodeBase58btc(body);
2791
+ default:
2792
+ throw new Error(`unsupported multibase prefix ${prefix}`);
2793
+ }
2794
+ }
2795
+ var BASE16_LOWER = "0123456789abcdef";
2796
+ var BASE16_UPPER = "0123456789ABCDEF";
2797
+ function decodeBase16(s) {
2798
+ if (s.length % 2 !== 0) throw new Error("base16: odd-length");
2799
+ const out = new Uint8Array(s.length / 2);
2800
+ const alphabet = s === s.toLowerCase() ? BASE16_LOWER : BASE16_UPPER;
2801
+ for (let i = 0; i < out.length; i++) {
2802
+ const hi = alphabet.indexOf(s[i * 2]);
2803
+ const lo = alphabet.indexOf(s[i * 2 + 1]);
2804
+ if (hi < 0 || lo < 0) throw new Error(`base16: non-hex char at ${i * 2}`);
2805
+ out[i] = hi << 4 | lo;
2806
+ }
2807
+ return out;
2808
+ }
2809
+ var BASE32_RFC4648_LOWER = "abcdefghijklmnopqrstuvwxyz234567";
2810
+ var BASE32_RFC4648_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
2811
+ function decodeBase32(s, variant) {
2812
+ const alphabet = variant === "rfc4648-lower" ? BASE32_RFC4648_LOWER : BASE32_RFC4648_UPPER;
2813
+ const trimmed = s.replace(/=+$/, "");
2814
+ const out = [];
2815
+ let buf = 0;
2816
+ let bits = 0;
2817
+ for (const ch of trimmed) {
2818
+ const idx = alphabet.indexOf(ch);
2819
+ if (idx < 0) throw new Error(`base32: invalid char '${ch}'`);
2820
+ buf = buf << 5 | idx;
2821
+ bits += 5;
2822
+ if (bits >= 8) {
2823
+ bits -= 8;
2824
+ out.push(buf >> bits & 255);
2539
2825
  }
2540
2826
  }
2827
+ return Uint8Array.from(out);
2541
2828
  }
2542
- function tryX25519Slot(args) {
2543
- if (args.liveSlot) {
2544
- try {
2545
- const shared = x25519Ecdh({
2546
- secretKey: args.recipientSecretKey,
2547
- theirPublicKey: args.slot.epk
2548
- });
2549
- const kek = hkdfSha256({
2550
- ikm: shared,
2551
- salt: concat2(args.slot.epk, args.pubRLocal),
2552
- info: CARDANO_POE_HKDF_INFO_KEK,
2553
- length: 32
2554
- });
2555
- return chacha20Poly1305Decrypt({
2556
- key: kek,
2557
- nonce: ZERO_NONCE_122,
2558
- aad: CARDANO_POE_HKDF_INFO_KEK,
2559
- ciphertext: args.slot.wrap
2560
- });
2561
- } catch (e) {
2562
- if (!(e instanceof AeadVerificationError2) && !(e instanceof X25519LowOrderPointError)) {
2563
- throw e;
2564
- }
2565
- return null;
2829
+ var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
2830
+ function decodeBase58btc(s) {
2831
+ if (s.length === 0) return new Uint8Array(0);
2832
+ let zeros = 0;
2833
+ while (zeros < s.length && s[zeros] === "1") zeros++;
2834
+ const size = Math.floor((s.length - zeros) * 733 / 1e3) + 1;
2835
+ const b256 = new Uint8Array(size);
2836
+ let length = 0;
2837
+ for (let i = zeros; i < s.length; i++) {
2838
+ const ch = s[i];
2839
+ const carryIdx = BASE58_ALPHABET.indexOf(ch);
2840
+ if (carryIdx < 0) throw new Error(`base58: invalid char '${ch}'`);
2841
+ let carry = carryIdx;
2842
+ let k = 0;
2843
+ for (let j2 = size - 1; (carry !== 0 || k < length) && j2 >= 0; j2--, k++) {
2844
+ carry += 58 * b256[j2];
2845
+ b256[j2] = carry % 256;
2846
+ carry = Math.floor(carry / 256);
2566
2847
  }
2848
+ length = k;
2567
2849
  }
2568
- try {
2569
- const shared = x25519Ecdh({
2570
- secretKey: args.recipientSecretKey,
2571
- theirPublicKey: args.slot.epk
2572
- });
2573
- hkdfSha256({
2574
- ikm: shared,
2575
- salt: concat2(args.slot.epk, args.pubRLocal),
2576
- info: CARDANO_POE_HKDF_INFO_KEK,
2577
- length: 32
2578
- });
2579
- } catch (e) {
2580
- if (!(e instanceof X25519LowOrderPointError)) throw e;
2850
+ let it = size - length;
2851
+ while (it < size && b256[it] === 0) it++;
2852
+ const out = new Uint8Array(zeros + (size - it));
2853
+ let j = zeros;
2854
+ while (it < size) {
2855
+ out[j++] = b256[it++];
2581
2856
  }
2582
- return null;
2857
+ return out;
2583
2858
  }
2584
- function tryMlkem768X25519Slot(args) {
2585
- const enc = joinKemCt(args.slot.kem_ct);
2586
- const ss = mlkem768x25519Decapsulate({ secretSeed: args.recipientSecretKey, enc });
2587
- const kek = hkdfSha256({
2588
- ikm: ss,
2589
- salt: EMPTY_SALT2,
2590
- info: CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519,
2591
- length: 32
2592
- });
2593
- if (!args.liveSlot) {
2594
- return null;
2859
+ function checkCritShape(record, decodedTopKeys, errors) {
2860
+ const invalid = /* @__PURE__ */ new Set();
2861
+ if (!Array.isArray(record.crit)) return invalid;
2862
+ if (record.crit.length === 0) {
2863
+ errors.push(
2864
+ issue("SCHEMA_TYPE_MISMATCH", ["crit"], "crit[] must carry at least one entry when present")
2865
+ );
2866
+ return invalid;
2595
2867
  }
2596
- try {
2597
- return chacha20Poly1305Decrypt({
2598
- key: kek,
2599
- nonce: ZERO_NONCE_122,
2600
- aad: CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519,
2601
- ciphertext: args.slot.wrap
2602
- });
2603
- } catch (e) {
2604
- if (!(e instanceof AeadVerificationError2)) throw e;
2605
- return null;
2868
+ const seen = /* @__PURE__ */ new Set();
2869
+ for (let i = 0; i < record.crit.length; i++) {
2870
+ const critName = record.crit[i];
2871
+ let reason = null;
2872
+ if (TOP_LEVEL_BASE_KEYS.has(critName)) {
2873
+ reason = `'${critName}' is a base key and MUST NOT appear in crit[]`;
2874
+ } else if (!isExtensionKey(critName)) {
2875
+ reason = `'${critName}' does not match the extension-key regex (^x-.+ or ^[a-z]+-.+)`;
2876
+ } else if (!decodedTopKeys.has(critName)) {
2877
+ reason = `'${critName}' is named in crit but absent from the record map`;
2878
+ } else if (seen.has(critName)) {
2879
+ reason = `'${critName}' appears more than once in crit[]`;
2880
+ }
2881
+ seen.add(critName);
2882
+ if (reason !== null) {
2883
+ invalid.add(i);
2884
+ errors.push(issue("CRIT_SHAPE_INVALID", ["crit", i], reason));
2885
+ }
2606
2886
  }
2887
+ return invalid;
2607
2888
  }
2608
- function tryRecipientUnwrapWithIdx(envelope, recipientSecretKey, constantTimeN, slotsAttemptedOut) {
2609
- const n = envelope.slots.length;
2610
- let cek = null;
2611
- let matchedSlotIdx = -1;
2612
- if (envelope.kem === "x25519") {
2613
- const pubRLocal = x25519PublicKey({ secretKey: recipientSecretKey });
2614
- for (let i = 0; i < n; i++) {
2615
- if (slotsAttemptedOut !== void 0) {
2616
- slotsAttemptedOut.count = i + 1;
2617
- }
2618
- const candidate = tryX25519Slot({
2619
- slot: envelope.slots[i],
2620
- recipientSecretKey,
2621
- pubRLocal,
2622
- liveSlot: cek === null
2623
- });
2624
- if (cek === null && candidate !== null) {
2625
- cek = candidate;
2626
- matchedSlotIdx = i;
2627
- }
2628
- if (cek !== null && !constantTimeN) break;
2629
- }
2630
- } else {
2631
- for (let i = 0; i < n; i++) {
2632
- if (slotsAttemptedOut !== void 0) {
2633
- slotsAttemptedOut.count = i + 1;
2634
- }
2635
- const candidate = tryMlkem768X25519Slot({
2636
- slot: envelope.slots[i],
2637
- recipientSecretKey,
2638
- liveSlot: cek === null
2639
- });
2640
- if (cek === null && candidate !== null) {
2641
- cek = candidate;
2642
- matchedSlotIdx = i;
2643
- }
2644
- if (cek !== null && !constantTimeN) break;
2889
+ function topLevelKeysOf(decoded) {
2890
+ if (decoded === null || typeof decoded !== "object") return /* @__PURE__ */ new Set();
2891
+ if (decoded instanceof Map) {
2892
+ const out = /* @__PURE__ */ new Set();
2893
+ for (const k of decoded.keys()) {
2894
+ if (typeof k === "string") out.add(k);
2645
2895
  }
2896
+ return out;
2646
2897
  }
2647
- return cek === null ? null : { cek, slotIdx: matchedSlotIdx };
2898
+ return new Set(Object.keys(decoded));
2648
2899
  }
2649
- function tryRecipientUnwrap(envelope, recipientSecretKey, constantTimeN, slotsAttemptedOut) {
2650
- return tryRecipientUnwrapWithIdx(envelope, recipientSecretKey, constantTimeN, slotsAttemptedOut)?.cek ?? null;
2900
+ function issue(code, path, message) {
2901
+ return { code, path, message, severity: SEVERITY[code] };
2651
2902
  }
2652
- function slotsMacCborBytes(envelope) {
2653
- return slotsToMacCbor(
2654
- envelope.slots,
2655
- envelope.kem
2656
- );
2903
+ function compareIssuePath(a, b) {
2904
+ return a.path.join(".").localeCompare(b.path.join("."));
2657
2905
  }
2658
- function eciesSealedPoeUnwrap(args) {
2659
- const { envelope, ciphertext } = args;
2660
- const constantTimeN = args.constantTimeN ?? true;
2661
- const hasSingle = "recipientSecretKey" in args;
2662
- const hasBundle = "recipientKeyBundle" in args;
2663
- const multiPrivKeys = hasBundle ? selectBundleSecrets(envelope, args.recipientKeyBundle) : "recipientSecretKeys" in args ? args.recipientSecretKeys : void 0;
2664
- const hasMulti = multiPrivKeys !== void 0;
2665
- if (hasSingle === hasMulti) {
2666
- throw new EciesSealedPoeError(
2667
- "INVALID_RECIPIENT_KEY",
2668
- "exactly one of recipientSecretKey / recipientSecretKeys / recipientKeyBundle MUST be supplied"
2669
- );
2670
- }
2671
- if (hasMulti && multiPrivKeys.length === 0) {
2672
- if (hasBundle) {
2673
- return { matched: false, reason: "WRONG_RECIPIENT_KEY" };
2906
+ function valueAtPath(root, path) {
2907
+ let cur = root;
2908
+ for (const seg of path) {
2909
+ if (cur === null || cur === void 0) return void 0;
2910
+ if (cur instanceof Map) {
2911
+ cur = cur.get(seg);
2912
+ continue;
2674
2913
  }
2675
- throw new EciesSealedPoeError(
2676
- "INVALID_RECIPIENT_KEY",
2677
- "recipientSecretKeys MUST be a non-empty array, got length=0"
2678
- );
2679
- }
2680
- if (hasMulti) {
2681
- assertEnvelopeStructure(envelope, multiPrivKeys, void 0);
2682
- } else {
2683
- assertEnvelopeStructure(envelope, void 0, args.recipientSecretKey);
2914
+ if (typeof cur !== "object") return void 0;
2915
+ cur = cur[seg];
2684
2916
  }
2685
- let matchedCek = null;
2686
- let anyCandidateRecovered = false;
2687
- if (hasSingle) {
2688
- const recipientSecretKey = args.recipientSecretKey;
2689
- const cek = tryRecipientUnwrap(
2690
- envelope,
2691
- recipientSecretKey,
2692
- constantTimeN,
2693
- args._slotsAttemptedOut
2694
- );
2695
- if (cek === null) {
2696
- return { matched: false, reason: "WRONG_RECIPIENT_KEY" };
2697
- }
2698
- const slotsCbor = slotsMacCborBytes(envelope);
2699
- const hmacKey = hkdfSha256({
2700
- ikm: cek,
2701
- salt: EMPTY_SALT2,
2702
- info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
2703
- length: 32
2704
- });
2705
- const slotsMacCalc = hmac(sha256, hmacKey, slotsCbor);
2706
- if (!compareCt2(slotsMacCalc, envelope.slots_mac)) {
2707
- return { matched: false, reason: "TAMPERED_HEADER" };
2708
- }
2709
- matchedCek = cek;
2710
- } else {
2711
- const slotsCbor = slotsMacCborBytes(envelope);
2712
- const recipientSecretKeys = multiPrivKeys;
2713
- for (let k = 0; k < recipientSecretKeys.length; k++) {
2714
- if (args._privsAttemptedOut !== void 0) {
2715
- args._privsAttemptedOut.count = k + 1;
2716
- }
2717
- if (args._slotsAttemptedOut !== void 0) {
2718
- args._slotsAttemptedOut.count = 0;
2719
- }
2720
- const cek = tryRecipientUnwrap(
2721
- envelope,
2722
- recipientSecretKeys[k],
2723
- constantTimeN,
2724
- args._slotsAttemptedOut
2725
- );
2726
- if (args._slotsAttemptedOut?.perPrivCounts !== void 0) {
2727
- args._slotsAttemptedOut.perPrivCounts.push(args._slotsAttemptedOut.count);
2728
- }
2729
- if (cek === null) continue;
2730
- const hmacKey = hkdfSha256({
2731
- ikm: cek,
2732
- salt: EMPTY_SALT2,
2733
- info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
2734
- length: 32
2735
- });
2736
- const slotsMacCalc = hmac(sha256, hmacKey, slotsCbor);
2737
- if (compareCt2(slotsMacCalc, envelope.slots_mac)) {
2738
- matchedCek = cek;
2739
- break;
2740
- }
2741
- anyCandidateRecovered = true;
2742
- }
2743
- if (matchedCek === null) {
2744
- return {
2745
- matched: false,
2746
- reason: anyCandidateRecovered ? "TAMPERED_HEADER" : "WRONG_RECIPIENT_KEY"
2747
- };
2748
- }
2917
+ return cur;
2918
+ }
2919
+ async function argon2idV13(opts2) {
2920
+ return await argon2id({
2921
+ password: opts2.password,
2922
+ salt: opts2.salt,
2923
+ parallelism: opts2.parallelism,
2924
+ iterations: opts2.iterations,
2925
+ memorySize: opts2.memSizeKB,
2926
+ hashLength: opts2.outBytes,
2927
+ outputType: "binary"
2928
+ });
2929
+ }
2930
+ var AeadVerificationError2 = class extends Error {
2931
+ code = "aead_verification_failed";
2932
+ constructor(message, options) {
2933
+ super(message, options);
2934
+ this.name = "AeadVerificationError";
2749
2935
  }
2750
- const adContent = concat2(envelope.nonce, envelope.slots_mac);
2936
+ };
2937
+ function xchacha20Poly1305Decrypt2(opts2) {
2751
2938
  try {
2752
- const plaintext = xchacha20Poly1305Decrypt2({
2753
- key: matchedCek,
2754
- nonce: envelope.nonce,
2755
- aad: adContent,
2756
- ciphertext
2757
- });
2758
- return { matched: true, plaintext };
2759
- } catch (e) {
2760
- if (!(e instanceof AeadVerificationError2)) throw e;
2761
- return { matched: false, reason: "TAMPERED_CIPHERTEXT" };
2939
+ return xchacha20poly1305(opts2.key, opts2.nonce, opts2.aad).decrypt(opts2.ciphertext);
2940
+ } catch (cause) {
2941
+ throw new AeadVerificationError2("xchacha20-poly1305 decrypt failed", { cause });
2762
2942
  }
2763
2943
  }
2764
- function sealedEnvelopeFromParsed(enc) {
2765
- if (enc.scheme !== 1 || enc.aead !== "xchacha20-poly1305") return null;
2766
- if (enc.nonce === void 0 || enc.slots_mac === void 0) return null;
2767
- const slots = enc.slots;
2768
- if (slots === void 0 || slots.length < 1) return null;
2769
- if (enc.kem === "x25519") {
2770
- const x25519Slots = [];
2771
- for (const s of slots) {
2772
- if (s.epk === void 0 || s.wrap === void 0) return null;
2773
- x25519Slots.push({ epk: s.epk, wrap: s.wrap });
2774
- }
2775
- return {
2776
- scheme: 1,
2777
- aead: "xchacha20-poly1305",
2778
- kem: "x25519",
2779
- nonce: enc.nonce,
2780
- slots: x25519Slots,
2781
- slots_mac: enc.slots_mac
2782
- };
2944
+ function sha2564(input) {
2945
+ return sha256(input);
2946
+ }
2947
+ function blake2b256(input) {
2948
+ return blake2b(input, { dkLen: 32 });
2949
+ }
2950
+ function blake2b2242(input) {
2951
+ return blake2b(input, { dkLen: 28 });
2952
+ }
2953
+ var LEAF_PREFIX = 0;
2954
+ var NODE_PREFIX = 1;
2955
+ var DIGEST_LENGTH = 32;
2956
+ function validateLeaves(leaves, fnName) {
2957
+ if (leaves.length === 0) {
2958
+ throw new Error(`${fnName}: empty leaf list (n == 0 is forbidden by RFC 9162 \xA72.1.1)`);
2783
2959
  }
2784
- if (enc.kem === "mlkem768x25519") {
2785
- const hybridSlots = [];
2786
- for (const s of slots) {
2787
- if (s.kem_ct === void 0 || s.wrap === void 0) return null;
2788
- hybridSlots.push({ kem_ct: s.kem_ct, wrap: s.wrap });
2960
+ for (let i = 0; i < leaves.length; i++) {
2961
+ const leaf = leaves[i];
2962
+ if (!(leaf instanceof Uint8Array) || leaf.length !== DIGEST_LENGTH) {
2963
+ throw new Error(
2964
+ `${fnName}: leaf[${i}] must be a Uint8Array(${DIGEST_LENGTH}); got length ${leaf instanceof Uint8Array ? leaf.length : "non-Uint8Array"}`
2965
+ );
2789
2966
  }
2790
- return {
2791
- scheme: 1,
2792
- aead: "xchacha20-poly1305",
2793
- kem: "mlkem768x25519",
2794
- nonce: enc.nonce,
2795
- slots: hybridSlots,
2796
- slots_mac: enc.slots_mac
2797
- };
2798
2967
  }
2799
- return null;
2968
+ }
2969
+ function merkleSha2256Root(leaves) {
2970
+ validateLeaves(leaves, "merkleSha2256Root");
2971
+ return mthRecursive(leaves, 0, leaves.length);
2972
+ }
2973
+ function largestPow2Lt(n) {
2974
+ let k = 1;
2975
+ while (k * 2 < n) k *= 2;
2976
+ return k;
2977
+ }
2978
+ function hashLeaf(d) {
2979
+ const buf = new Uint8Array(1 + d.length);
2980
+ buf[0] = LEAF_PREFIX;
2981
+ buf.set(d, 1);
2982
+ return sha256(buf);
2983
+ }
2984
+ function hashNode(left, right) {
2985
+ const buf = new Uint8Array(1 + left.length + right.length);
2986
+ buf[0] = NODE_PREFIX;
2987
+ buf.set(left, 1);
2988
+ buf.set(right, 1 + left.length);
2989
+ return sha256(buf);
2990
+ }
2991
+ function mthRecursive(leaves, start, end) {
2992
+ const n = end - start;
2993
+ if (n === 1) {
2994
+ return hashLeaf(leaves[start]);
2995
+ }
2996
+ const k = largestPow2Lt(n);
2997
+ const left = mthRecursive(leaves, start, start + k);
2998
+ const right = mthRecursive(leaves, start + k, end);
2999
+ return hashNode(left, right);
2800
3000
  }
2801
3001
 
2802
3002
  // ../crypto-core/dist/util.js
@@ -3198,7 +3398,53 @@ async function fetchItemCiphertext(args) {
3198
3398
 
3199
3399
  // src/verifier/decrypt.ts
3200
3400
  var PASSPHRASE_KDF_ARGON2ID = "argon2id";
3201
- var EMPTY_AAD = new Uint8Array(0);
3401
+ var MAX_PASSPHRASE_INPUT_BYTES = 4096;
3402
+ var UNICODE_WHITE_SPACE = /* @__PURE__ */ new Set([
3403
+ 9,
3404
+ 10,
3405
+ 11,
3406
+ 12,
3407
+ 13,
3408
+ 32,
3409
+ 133,
3410
+ 160,
3411
+ 5760,
3412
+ 8192,
3413
+ 8193,
3414
+ 8194,
3415
+ 8195,
3416
+ 8196,
3417
+ 8197,
3418
+ 8198,
3419
+ 8199,
3420
+ 8200,
3421
+ 8201,
3422
+ 8202,
3423
+ 8232,
3424
+ 8233,
3425
+ 8239,
3426
+ 8287,
3427
+ 12288
3428
+ ]);
3429
+ function normalizePassphrase(passphrase) {
3430
+ const nfkc = passphrase.normalize("NFKC");
3431
+ let collapsed = "";
3432
+ let inRun = false;
3433
+ for (const ch of nfkc) {
3434
+ if (UNICODE_WHITE_SPACE.has(ch.codePointAt(0))) {
3435
+ if (!inRun) {
3436
+ collapsed += " ";
3437
+ inRun = true;
3438
+ }
3439
+ } else {
3440
+ collapsed += ch;
3441
+ inRun = false;
3442
+ }
3443
+ }
3444
+ if (collapsed.startsWith(" ")) collapsed = collapsed.slice(1);
3445
+ if (collapsed.endsWith(" ")) collapsed = collapsed.slice(0, -1);
3446
+ return new TextEncoder().encode(collapsed);
3447
+ }
3202
3448
  async function tryDecryptions(args) {
3203
3449
  const { record, input } = args;
3204
3450
  const items = record.items ?? [];
@@ -3317,7 +3563,7 @@ async function tryDecryptions(args) {
3317
3563
  passphrase: req.passphrase
3318
3564
  });
3319
3565
  } catch (e) {
3320
- if (e instanceof AeadVerificationError) {
3566
+ if (e instanceof AeadVerificationError2) {
3321
3567
  failure = { verdict: "tampered-ciphertext", reason: "TAMPERED_CIPHERTEXT" };
3322
3568
  } else if (e instanceof Error && e.message.startsWith("KDF_")) {
3323
3569
  failure = { verdict: "kdf-failed", reason: e.message };
@@ -3347,8 +3593,13 @@ async function decryptPassphrase(args) {
3347
3593
  if (enc.passphrase.alg !== PASSPHRASE_KDF_ARGON2ID) {
3348
3594
  throw new Error(`KDF_DERIVATION_FAILED: unsupported passphrase alg ${enc.passphrase.alg}`);
3349
3595
  }
3350
- const normalised = passphrase.normalize("NFKC").replace(/\s+/g, " ").trim();
3351
- const password = new TextEncoder().encode(normalised);
3596
+ const rawPassphraseBytes = new TextEncoder().encode(passphrase).length;
3597
+ if (rawPassphraseBytes > MAX_PASSPHRASE_INPUT_BYTES) {
3598
+ throw new Error(
3599
+ `KDF_DERIVATION_FAILED: passphrase length ${rawPassphraseBytes} bytes exceeds the maximum ${MAX_PASSPHRASE_INPUT_BYTES} bytes`
3600
+ );
3601
+ }
3602
+ const password = normalizePassphrase(passphrase);
3352
3603
  let cek;
3353
3604
  try {
3354
3605
  cek = await argon2idV13({
@@ -3366,10 +3617,20 @@ async function decryptPassphrase(args) {
3366
3617
  if (enc.aead !== "xchacha20-poly1305") {
3367
3618
  throw new Error(`KDF_DERIVATION_FAILED: unsupported aead ${enc.aead}`);
3368
3619
  }
3369
- return xchacha20Poly1305Decrypt({
3370
- key: cek,
3620
+ assertCiphertextWithinBound(ciphertext.length);
3621
+ const payloadKey = passphrasePayloadKey({ cek, nonce: enc.nonce });
3622
+ const aad = adContentPassphrase({
3623
+ nonce: enc.nonce,
3624
+ passphrase: {
3625
+ alg: enc.passphrase.alg,
3626
+ salt: enc.passphrase.salt,
3627
+ params: enc.passphrase.params
3628
+ }
3629
+ });
3630
+ return xchacha20Poly1305Decrypt2({
3631
+ key: payloadKey,
3371
3632
  nonce: enc.nonce,
3372
- aad: EMPTY_AAD,
3633
+ aad,
3373
3634
  ciphertext
3374
3635
  });
3375
3636
  }
@@ -3378,7 +3639,7 @@ function recomputeHashes(item, plaintext) {
3378
3639
  if (entries.length === 0) return false;
3379
3640
  for (const [alg, digest] of entries) {
3380
3641
  if (alg === "sha2-256") {
3381
- if (!compareCt3(sha2562(plaintext), digest)) return false;
3642
+ if (!compareCt3(sha2564(plaintext), digest)) return false;
3382
3643
  } else if (alg === "blake2b-256") {
3383
3644
  if (!compareCt3(blake2b256(plaintext), digest)) return false;
3384
3645
  } else {
@@ -3401,7 +3662,7 @@ function decodeCanonicalCbor3(bytes) {
3401
3662
  ...cdeDecodeOptions,
3402
3663
  rejectStreaming: true,
3403
3664
  rejectDuplicateKeys: true,
3404
- // A CIP-309 record carries integers, byte/text strings, arrays, maps and
3665
+ // A Label 309 record carries integers, byte/text strings, arrays, maps and
3405
3666
  // `null` — and nothing else. Without these rejections the major-type-7
3406
3667
  // surface leaks into the decoder: a float16/32/64 that happens to hold an
3407
3668
  // integral value (e.g. 1.0) silently decodes to the integer 1 and passes
@@ -3974,11 +4235,11 @@ function decodeIntKey(h) {
3974
4235
  // src/verifier/resolve.ts
3975
4236
  var KOIOS_MAINNET_URL = "https://api.koios.rest/api/v1";
3976
4237
  var BLOCKFROST_MAINNET_HOST = "https://cardano-mainnet.blockfrost.io/api/v0";
3977
- var NotACip309RecordError = class extends Error {
4238
+ var NotALabel309RecordError = class extends Error {
3978
4239
  code = "METADATA_NOT_FOUND";
3979
4240
  constructor(message) {
3980
4241
  super(message);
3981
- this.name = "NotACip309RecordError";
4242
+ this.name = "NotALabel309RecordError";
3982
4243
  }
3983
4244
  };
3984
4245
  async function resolveCardanoTx(args) {
@@ -3989,7 +4250,7 @@ async function resolveCardanoTx(args) {
3989
4250
  try {
3990
4251
  return await resolveViaKoios(input.txHash, koiosUrl, fetchFn);
3991
4252
  } catch (e) {
3992
- if (e instanceof NotACip309RecordError) throw e;
4253
+ if (e instanceof NotALabel309RecordError) throw e;
3993
4254
  lastErr = e;
3994
4255
  }
3995
4256
  }
@@ -3997,7 +4258,7 @@ async function resolveCardanoTx(args) {
3997
4258
  try {
3998
4259
  return await resolveViaBlockfrost(input.txHash, input.blockfrostProjectId, fetchFn);
3999
4260
  } catch (e) {
4000
- if (e instanceof NotACip309RecordError) throw e;
4261
+ if (e instanceof NotALabel309RecordError) throw e;
4001
4262
  lastErr = e;
4002
4263
  }
4003
4264
  }
@@ -4015,7 +4276,7 @@ async function resolveViaKoios(txHash, koiosUrl, fetchFn) {
4015
4276
  }
4016
4277
  const cborJson = parseJson(cborRes.bytes);
4017
4278
  if (!Array.isArray(cborJson) || cborJson.length === 0) {
4018
- throw new NotACip309RecordError("koios returned empty array for tx_cbor; tx may not exist");
4279
+ throw new NotALabel309RecordError("koios returned empty array for tx_cbor; tx may not exist");
4019
4280
  }
4020
4281
  const cborEntry = cborJson[0];
4021
4282
  if (typeof cborEntry.cbor !== "string") {
@@ -4036,7 +4297,7 @@ async function resolveViaKoios(txHash, koiosUrl, fetchFn) {
4036
4297
  }
4037
4298
  const infoJson = parseJson(infoRes.bytes);
4038
4299
  if (!Array.isArray(infoJson) || infoJson.length === 0) {
4039
- throw new NotACip309RecordError("koios returned empty array for tx_info");
4300
+ throw new NotALabel309RecordError("koios returned empty array for tx_info");
4040
4301
  }
4041
4302
  const infoEntry = infoJson[0];
4042
4303
  if (typeof infoEntry.tx_hash === "string" && infoEntry.tx_hash.toLowerCase() !== txHash.toLowerCase()) {
@@ -4148,7 +4409,7 @@ function hexToBytes(hex) {
4148
4409
  }
4149
4410
 
4150
4411
  // src/hex.ts
4151
- function bytesToHex(bytes) {
4412
+ function bytesToHex2(bytes) {
4152
4413
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
4153
4414
  }
4154
4415
 
@@ -4181,7 +4442,7 @@ async function verifyOneSig(index, entry, recordBodyCbor, input) {
4181
4442
  return { index, verdict: "unresolved", reason: "SIGNER_KEY_UNRESOLVED" };
4182
4443
  }
4183
4444
  const { pub, signerType } = resolved;
4184
- const verifyResult = coseSign1Cip309Verify({
4445
+ const verifyResult = coseSign1Label309Verify({
4185
4446
  message: coseBytes,
4186
4447
  detachedRecordBodyCbor: recordBodyCbor,
4187
4448
  expectedSignerKey: pub
@@ -4193,7 +4454,7 @@ async function verifyOneSig(index, entry, recordBodyCbor, input) {
4193
4454
  index,
4194
4455
  verdict: "unsupported",
4195
4456
  signer_type: signerType,
4196
- signer_pub: bytesToHex(pub),
4457
+ signer_pub: bytesToHex2(pub),
4197
4458
  reason
4198
4459
  };
4199
4460
  }
@@ -4201,7 +4462,7 @@ async function verifyOneSig(index, entry, recordBodyCbor, input) {
4201
4462
  index,
4202
4463
  verdict: "invalid",
4203
4464
  signer_type: signerType,
4204
- signer_pub: bytesToHex(pub),
4465
+ signer_pub: bytesToHex2(pub),
4205
4466
  reason
4206
4467
  };
4207
4468
  }
@@ -4212,7 +4473,7 @@ async function verifyOneSig(index, entry, recordBodyCbor, input) {
4212
4473
  index,
4213
4474
  verdict: "invalid",
4214
4475
  signer_type: signerType,
4215
- signer_pub: bytesToHex(pub),
4476
+ signer_pub: bytesToHex2(pub),
4216
4477
  reason: "WALLET_ADDRESS_MISMATCH"
4217
4478
  };
4218
4479
  }
@@ -4221,7 +4482,7 @@ async function verifyOneSig(index, entry, recordBodyCbor, input) {
4221
4482
  index,
4222
4483
  verdict: "valid",
4223
4484
  signer_type: signerType,
4224
- signer_pub: bytesToHex(pub)
4485
+ signer_pub: bytesToHex2(pub)
4225
4486
  };
4226
4487
  }
4227
4488
  function resolveSignerKey(cose, entry) {
@@ -4347,8 +4608,8 @@ function decodeTxWitnesses(witnessSetBytes, txBodyBytes) {
4347
4608
  if (vkey instanceof Uint8Array && vkey.length === ED25519_PUBLIC_KEY_LENGTH3) {
4348
4609
  out.push({
4349
4610
  type: "vkey",
4350
- vkey: bytesToHex(vkey),
4351
- key_hash: bytesToHex(blake2b2242(vkey)),
4611
+ vkey: bytesToHex2(vkey),
4612
+ key_hash: bytesToHex2(blake2b2242(vkey)),
4352
4613
  signature_valid: false
4353
4614
  });
4354
4615
  }
@@ -4362,8 +4623,8 @@ function decodeTxWitnesses(witnessSetBytes, txBodyBytes) {
4362
4623
  }
4363
4624
  out.push({
4364
4625
  type: "vkey",
4365
- vkey: bytesToHex(vkey),
4366
- key_hash: bytesToHex(blake2b2242(vkey)),
4626
+ vkey: bytesToHex2(vkey),
4627
+ key_hash: bytesToHex2(blake2b2242(vkey)),
4367
4628
  signature_valid: signatureValid
4368
4629
  });
4369
4630
  }
@@ -4396,7 +4657,7 @@ function decodeTxSummary(txBodyBytes, witnessSetBytes, network) {
4396
4657
  lovelace: lovelace.toString()
4397
4658
  });
4398
4659
  }
4399
- const requiredSigners = asArray(body.get(BODY_KEY_REQUIRED_SIGNERS)).filter((s) => s instanceof Uint8Array).map((s) => bytesToHex(s));
4660
+ const requiredSigners = asArray(body.get(BODY_KEY_REQUIRED_SIGNERS)).filter((s) => s instanceof Uint8Array).map((s) => bytesToHex2(s));
4400
4661
  const summary = {
4401
4662
  fee_lovelace: coinToString(body.get(BODY_KEY_FEE)),
4402
4663
  input_count: inputs.length,
@@ -4530,7 +4791,7 @@ async function verifyTx(input) {
4530
4791
  try {
4531
4792
  resolved = await resolveCardanoTx({ input, fetchFn });
4532
4793
  } catch (e) {
4533
- if (e instanceof NotACip309RecordError) {
4794
+ if (e instanceof NotALabel309RecordError) {
4534
4795
  return base({
4535
4796
  verdict: "failed",
4536
4797
  exit_code: 1,
@@ -4808,7 +5069,7 @@ function isPlainObject(v) {
4808
5069
  }
4809
5070
  function walk(value) {
4810
5071
  if (value === void 0 || value === null) return void 0;
4811
- if (value instanceof Uint8Array) return bytesToHex(value);
5072
+ if (value instanceof Uint8Array) return bytesToHex2(value);
4812
5073
  if (value instanceof Map) {
4813
5074
  throw new Error("unsupported Map in VerifyReport tree");
4814
5075
  }
@@ -4843,6 +5104,6 @@ function verifyReportToDict(report) {
4843
5104
  (*! noble-post-quantum - MIT License (c) 2024 Paul Miller (paulmillr.com) *)
4844
5105
  */
4845
5106
 
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 };
5107
+ export { BLOCKFROST_MAINNET_HOST, BodyTooLargeError, CONFIRMATION_DEPTH_THRESHOLD_DEFAULT, DEFAULT_OUTBOUND_MAX_BYTES, DEFAULT_PROFILE, DENY_HOSTS_DEFAULT, DenyHostError, KOIOS_MAINNET_URL, NotALabel309RecordError, 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
5108
  //# sourceMappingURL=index.js.map
4848
5109
  //# sourceMappingURL=index.js.map