@cardanowall/poe-standard 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,95 +1,108 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  // src/schema.ts
4
- var ChunkedBytesArraySchema = z.array(
5
- z.instanceof(Uint8Array).refine((b5) => b5.length >= 1 && b5.length <= 64, {
6
- params: { code: "CHUNK_TOO_LARGE" }
7
- })
8
- ).min(1);
9
- var UTF8_ENCODER = new TextEncoder();
10
- var UriChunkArraySchema = z.array(
11
- z.string().refine(
12
- (s3) => {
13
- const n2 = UTF8_ENCODER.encode(s3).length;
14
- return n2 >= 1 && n2 <= 64;
15
- },
16
- { params: { code: "CHUNK_TOO_LARGE" } }
17
- )
18
- ).min(1);
4
+ function textKeyedMap(inner) {
5
+ return z.custom((value) => !(value instanceof Uint8Array), {
6
+ message: "CBOR byte string present where a text-keyed map is required"
7
+ }).pipe(inner);
8
+ }
19
9
  var HashDigestSchema = z.instanceof(Uint8Array);
20
10
  var HashesMapSchema = z.record(z.string(), HashDigestSchema);
21
- var MerkleCommitSchema = z.object({
22
- alg: z.string(),
23
- root: z.instanceof(Uint8Array),
24
- leaf_count: z.number().int().min(1),
25
- uris: z.array(UriChunkArraySchema).min(1).optional()
26
- }).strict();
27
- var SlotSchema = z.object({
28
- epk: z.instanceof(Uint8Array).optional(),
29
- kem_ct: ChunkedBytesArraySchema.optional(),
30
- wrap: z.instanceof(Uint8Array).optional()
31
- });
32
- var Argon2idParamsSchema = z.object({
33
- m: z.number().int(),
34
- t: z.number().int(),
35
- p: z.number().int()
36
- }).strict();
37
- var PassphraseBlockSchema = z.object({
38
- alg: z.string(),
39
- salt: z.instanceof(Uint8Array).superRefine((bytes, ctx) => {
40
- if (bytes.length < 16) {
41
- ctx.addIssue({
42
- code: "custom",
43
- path: [],
44
- message: `passphrase.salt length ${bytes.length} < 16`,
45
- params: { code: "ENC_PASSPHRASE_SALT_TOO_SHORT" }
46
- });
47
- } else if (bytes.length > 64) {
48
- ctx.addIssue({
49
- code: "custom",
50
- path: [],
51
- message: `passphrase.salt length ${bytes.length} > 64`,
52
- params: { code: "ENC_PASSPHRASE_SALT_TOO_LONG" }
53
- });
54
- }
55
- }),
56
- params: z.record(z.string(), z.unknown())
57
- }).strict();
58
- var EncryptionEnvelopeSchema = z.object({
59
- scheme: z.unknown(),
60
- aead: z.string(),
61
- kem: z.string().optional(),
62
- nonce: z.instanceof(Uint8Array),
63
- slots: z.array(SlotSchema).optional(),
64
- slots_mac: z.instanceof(Uint8Array).refine((b5) => b5.length === 32, {
65
- params: { code: "ENC_SLOTS_MAC_INVALID_LENGTH" }
66
- }).optional(),
67
- passphrase: PassphraseBlockSchema.optional()
68
- }).strict();
69
- var ItemEntrySchema = z.object({
70
- hashes: HashesMapSchema,
71
- uris: z.array(UriChunkArraySchema).min(1).optional(),
72
- // Captured as `unknown` so the validator can run the
73
- // `ENC_REQUIRES_CONTENT_HASH` pre-check ahead of any inner-shape errors
74
- // and surface the most informative code first.
75
- enc: z.unknown().optional()
76
- }).strict();
77
- var SigEntrySchema = z.object({
78
- cose_key: ChunkedBytesArraySchema.optional(),
79
- cose_sign1: ChunkedBytesArraySchema
80
- }).strict();
11
+ var UriSchema = z.string();
12
+ var MerkleCommitSchema = textKeyedMap(
13
+ z.object({
14
+ alg: z.string(),
15
+ root: z.instanceof(Uint8Array),
16
+ leaf_count: z.union([z.number().int(), z.bigint()]),
17
+ uris: z.array(UriSchema).optional()
18
+ }).strict()
19
+ );
20
+ var SlotSchema = textKeyedMap(
21
+ z.object({
22
+ epk: z.instanceof(Uint8Array).optional(),
23
+ kem_ct: z.instanceof(Uint8Array).optional(),
24
+ wrap: z.instanceof(Uint8Array).optional()
25
+ })
26
+ );
27
+ var Argon2idParamsSchema = textKeyedMap(
28
+ z.object({
29
+ m: z.union([z.number().int(), z.bigint()]),
30
+ t: z.union([z.number().int(), z.bigint()]),
31
+ p: z.union([z.number().int(), z.bigint()])
32
+ }).strict()
33
+ );
34
+ var PassphraseBlockSchema = textKeyedMap(
35
+ z.object({
36
+ alg: z.string(),
37
+ salt: z.instanceof(Uint8Array).superRefine((bytes, ctx) => {
38
+ if (bytes.length < 16) {
39
+ ctx.addIssue({
40
+ code: "custom",
41
+ path: [],
42
+ message: `passphrase.salt length ${bytes.length} < 16`,
43
+ params: { code: "ENC_PASSPHRASE_SALT_TOO_SHORT" }
44
+ });
45
+ } else if (bytes.length > 64) {
46
+ ctx.addIssue({
47
+ code: "custom",
48
+ path: [],
49
+ message: `passphrase.salt length ${bytes.length} > 64`,
50
+ params: { code: "ENC_PASSPHRASE_SALT_TOO_LONG" }
51
+ });
52
+ }
53
+ }),
54
+ params: z.record(z.string(), z.unknown())
55
+ }).strict()
56
+ );
57
+ var EncScheme1Schema = textKeyedMap(
58
+ z.object({
59
+ scheme: z.literal(1),
60
+ aead: z.string(),
61
+ kem: z.string().optional(),
62
+ nonce: z.instanceof(Uint8Array),
63
+ slots: z.array(SlotSchema).optional(),
64
+ slots_mac: z.instanceof(Uint8Array).refine((b5) => b5.length === 32, {
65
+ params: { code: "ENC_SLOTS_MAC_INVALID_LENGTH" }
66
+ }).optional(),
67
+ passphrase: PassphraseBlockSchema.optional()
68
+ }).strict()
69
+ );
70
+ var EncOpaqueSchema = textKeyedMap(
71
+ z.looseObject({
72
+ scheme: z.union([z.number().int().nonnegative(), z.bigint().nonnegative()])
73
+ })
74
+ );
75
+ var EncryptionEnvelopeSchema = z.union([EncScheme1Schema, EncOpaqueSchema]);
76
+ var ItemEntrySchema = textKeyedMap(
77
+ z.object({
78
+ hashes: HashesMapSchema,
79
+ uris: z.array(UriSchema).optional(),
80
+ // Captured as `unknown`: the envelope is a union whose disposition
81
+ // (typed scheme-1 vs opaque) depends on identifier support, so the
82
+ // validator's domain pass — not this schema — narrows it.
83
+ enc: z.unknown().optional()
84
+ }).strict()
85
+ );
86
+ var SigEntrySchema = textKeyedMap(
87
+ z.object({
88
+ cose_key: z.instanceof(Uint8Array).optional(),
89
+ cose_sign1: z.instanceof(Uint8Array)
90
+ }).strict()
91
+ );
81
92
  var SupersedesSchema = z.instanceof(Uint8Array).refine((b5) => b5.length === 32, {
82
93
  params: { code: "SUPERSEDES_TX_INVALID_LENGTH" }
83
94
  });
84
95
  var VersionLiteralSchema = z.literal(1);
85
- var PoeRecordSchema = z.looseObject({
86
- v: VersionLiteralSchema,
87
- items: z.array(ItemEntrySchema).optional(),
88
- merkle: z.array(MerkleCommitSchema).optional(),
89
- supersedes: SupersedesSchema.optional(),
90
- sigs: z.array(SigEntrySchema).optional(),
91
- crit: z.array(z.string()).optional()
92
- });
96
+ var PoeRecordSchema = textKeyedMap(
97
+ z.looseObject({
98
+ v: VersionLiteralSchema,
99
+ items: z.array(ItemEntrySchema).optional(),
100
+ merkle: z.array(MerkleCommitSchema).optional(),
101
+ supersedes: SupersedesSchema.optional(),
102
+ sigs: z.array(SigEntrySchema).optional(),
103
+ crit: z.array(z.string()).optional()
104
+ })
105
+ );
93
106
  var TOP_LEVEL_BASE_KEYS = /* @__PURE__ */ new Set([
94
107
  "v",
95
108
  "items",
@@ -98,8 +111,8 @@ var TOP_LEVEL_BASE_KEYS = /* @__PURE__ */ new Set([
98
111
  "sigs",
99
112
  "crit"
100
113
  ]);
101
- var EXTENSION_KEY_VENDOR_RE = /^x-.+\n?$/;
102
- var EXTENSION_KEY_COMPANION_RE = /^[a-z]+-.+\n?$/;
114
+ var EXTENSION_KEY_VENDOR_RE = /^x-[^\u0000-\u001f\u007f-\u009f]+$/;
115
+ var EXTENSION_KEY_COMPANION_RE = /^[a-z]+-[^\u0000-\u001f\u007f-\u009f]+$/;
103
116
  function isExtensionKey(k4) {
104
117
  return EXTENSION_KEY_VENDOR_RE.test(k4) || EXTENSION_KEY_COMPANION_RE.test(k4);
105
118
  }
@@ -1765,97 +1778,366 @@ function mapDecodeError(cause) {
1765
1778
 
1766
1779
  // src/encoder.ts
1767
1780
  function encodePoeRecord(record) {
1768
- return encodeCanonicalCbor(recordToCbor(record));
1769
- }
1770
- function encodeRecordBodyForSigning(record) {
1771
- const body = recordToCborInternal(
1781
+ return encodeCanonicalCbor(toCborValue(
1772
1782
  record,
1773
1783
  /* includeSigs */
1774
- false
1775
- );
1776
- return encodeCanonicalCbor(body);
1784
+ true
1785
+ ));
1777
1786
  }
1778
- function recordToCbor(record) {
1779
- return recordToCborInternal(
1787
+ function encodeRecordBodyForSigning(record) {
1788
+ return encodeCanonicalCbor(toCborValue(
1780
1789
  record,
1781
1790
  /* includeSigs */
1782
- true
1783
- );
1791
+ false
1792
+ ));
1784
1793
  }
1785
- function recordToCborInternal(record, includeSigs) {
1786
- const out = { v: record.v };
1787
- if (record.items !== void 0) out["items"] = record.items.map(itemToCbor);
1788
- if (record.merkle !== void 0) out["merkle"] = record.merkle.map(merkleToCbor);
1789
- if (record.supersedes !== void 0) out["supersedes"] = record.supersedes;
1790
- if (includeSigs && record.sigs !== void 0) out["sigs"] = record.sigs.map(sigEntryToCbor);
1791
- if (record.crit !== void 0) out["crit"] = record.crit.slice();
1792
- for (const [k4, v3] of Object.entries(record)) {
1793
- if (k4 === "v" || k4 === "items" || k4 === "merkle" || k4 === "supersedes" || k4 === "sigs" || k4 === "crit") {
1794
- continue;
1795
- }
1796
- out[k4] = v3;
1794
+ function toCborValue(record, includeSigs) {
1795
+ const out = {};
1796
+ for (const [key, value] of Object.entries(record)) {
1797
+ if (value === void 0) continue;
1798
+ if (!includeSigs && key === "sigs") continue;
1799
+ out[key] = stripUndefined(value);
1797
1800
  }
1798
1801
  return out;
1799
1802
  }
1800
- function itemToCbor(item) {
1801
- const out = { hashes: hashesToCbor(item.hashes) };
1802
- if (item.uris !== void 0) {
1803
- out["uris"] = item.uris.map((chunks) => chunks.slice());
1803
+ function stripUndefined(value) {
1804
+ if (value === null || typeof value !== "object" || value instanceof Uint8Array) {
1805
+ return value;
1804
1806
  }
1805
- if (item.enc !== void 0) {
1806
- out["enc"] = envelopeToCbor(item.enc);
1807
+ if (Array.isArray(value)) {
1808
+ return value.map((element) => stripUndefined(element));
1809
+ }
1810
+ if (value instanceof Map) {
1811
+ const out2 = /* @__PURE__ */ new Map();
1812
+ for (const [k4, v3] of value) {
1813
+ if (v3 === void 0) continue;
1814
+ out2.set(k4, stripUndefined(v3));
1815
+ }
1816
+ return out2;
1807
1817
  }
1808
- return out;
1809
- }
1810
- function hashesToCbor(hashes2) {
1811
1818
  const out = {};
1812
- for (const [alg, digest] of Object.entries(hashes2)) {
1813
- out[alg] = digest;
1819
+ for (const [k4, v3] of Object.entries(value)) {
1820
+ if (v3 === void 0) continue;
1821
+ out[k4] = stripUndefined(v3);
1814
1822
  }
1815
1823
  return out;
1816
1824
  }
1817
- function merkleToCbor(commit) {
1818
- const out = {
1819
- alg: commit.alg,
1820
- root: commit.root,
1821
- leaf_count: commit.leaf_count
1822
- };
1823
- if (commit.uris !== void 0) {
1824
- out["uris"] = commit.uris.map((chunks) => chunks.slice());
1825
- }
1826
- return out;
1825
+
1826
+ // src/error-codes.ts
1827
+ var ERROR_CODES = [
1828
+ "MALFORMED_CBOR",
1829
+ "SCHEMA_TYPE_MISMATCH",
1830
+ "SCHEMA_MISSING_REQUIRED",
1831
+ "SCHEMA_UNKNOWN_FIELD",
1832
+ "SCHEMA_INVALID_LITERAL",
1833
+ "SCHEMA_EMPTY_RECORD",
1834
+ "HASH_DIGEST_LENGTH_MISMATCH",
1835
+ "UNSUPPORTED_HASH_ALG",
1836
+ "UNSUPPORTED_MERKLE_COMMIT_ALG",
1837
+ "SCHEMA_MERKLE_LEAF_COUNT_INVALID",
1838
+ "INVALID_URI",
1839
+ "CHUNK_TOO_LARGE",
1840
+ "UNAUTHENTICATED_CIPHER_FORBIDDEN",
1841
+ "UNSUPPORTED_AEAD_ALG",
1842
+ "NONCE_LENGTH_MISMATCH",
1843
+ "UNSUPPORTED_ENVELOPE_SCHEME",
1844
+ "ENC_UNSUPPORTED",
1845
+ "ENC_SLOTS_EMPTY",
1846
+ "ENC_SLOT_INVALID_SHAPE",
1847
+ "UNSUPPORTED_KEM_ALG",
1848
+ "ENC_KEM_REQUIRED",
1849
+ "KEM_EPK_LENGTH_MISMATCH",
1850
+ "KEM_CT_LENGTH_MISMATCH",
1851
+ "WRAP_LENGTH_MISMATCH",
1852
+ "ENC_SLOTS_MAC_INVALID_LENGTH",
1853
+ "ENC_SLOTS_MAC_REQUIRED",
1854
+ "ENC_SLOTS_REQUIRED",
1855
+ "ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
1856
+ "ENC_SLOTS_TOO_MANY",
1857
+ "ENC_ENVELOPE_TOO_LARGE",
1858
+ "ENC_EXCLUSIVITY_VIOLATION",
1859
+ "ENC_NO_KEY_PATH",
1860
+ "ENC_REQUIRES_CONTENT_HASH",
1861
+ "ENC_PASSPHRASE_ALG_UNSUPPORTED",
1862
+ "ENC_PASSPHRASE_SALT_TOO_SHORT",
1863
+ "ENC_PASSPHRASE_SALT_TOO_LONG",
1864
+ "ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
1865
+ "ENC_PASSPHRASE_PARAMS_EXCEED_POLICY",
1866
+ "MALFORMED_SIG_COSE_SIGN1",
1867
+ "SIGNATURE_UNSUPPORTED",
1868
+ "SIG_ENTRY_INVALID_SHAPE",
1869
+ "SIG_ENTRY_KID_COSE_KEY_CONFLICT",
1870
+ "SIG_PRIVATE_KEY_LEAKED",
1871
+ "SUPERSEDES_TX_INVALID_LENGTH",
1872
+ "EXTENSION_UNSUPPORTED_CRITICAL",
1873
+ "CRIT_SHAPE_INVALID",
1874
+ "TX_NOT_FOUND",
1875
+ "PROVIDER_UNAVAILABLE",
1876
+ "TX_INTEGRITY_MISMATCH",
1877
+ "METADATA_NOT_FOUND",
1878
+ "INSUFFICIENT_CONFIRMATIONS",
1879
+ "SIGNATURE_INVALID",
1880
+ "SIGNER_KEY_UNRESOLVED",
1881
+ "WALLET_ADDRESS_MISMATCH",
1882
+ "URI_TARGET_FORBIDDEN",
1883
+ "URI_INTEGRITY_MISMATCH",
1884
+ "URI_PROVIDER_INTEGRITY_MISMATCH",
1885
+ "URI_FETCH_FAILED",
1886
+ "CONTENT_UNAVAILABLE",
1887
+ "CONTENT_FETCH_LIMIT_EXCEEDED",
1888
+ "CIPHERTEXT_UNAVAILABLE",
1889
+ "SERVICE_INDEPENDENCE_VIOLATION",
1890
+ "WRONG_DECRYPTION_INPUT_SHAPE",
1891
+ "WRONG_RECIPIENT_KEY",
1892
+ "TAMPERED_HEADER",
1893
+ "TAMPERED_CIPHERTEXT",
1894
+ "KDF_DERIVATION_FAILED",
1895
+ "ENC_PASSPHRASE_UNNORMALIZABLE",
1896
+ "ENC_PASSPHRASE_EMPTY",
1897
+ "SCHEMA_MERKLE_LEAF_COUNT_MISMATCH",
1898
+ "SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED",
1899
+ "SCHEMA_MERKLE_LEAVES_MALFORMED",
1900
+ "MERKLE_ROOT_MISMATCH",
1901
+ "MERKLE_LEAVES_UNAVAILABLE",
1902
+ "MERKLE_UNSUPPORTED",
1903
+ "OUT_OF_PROFILE_SKIPPED"
1904
+ ];
1905
+ var ERROR_CODE_PART = Object.freeze({
1906
+ MALFORMED_CBOR: "A",
1907
+ SCHEMA_TYPE_MISMATCH: "A",
1908
+ SCHEMA_MISSING_REQUIRED: "A",
1909
+ SCHEMA_UNKNOWN_FIELD: "A",
1910
+ SCHEMA_INVALID_LITERAL: "A",
1911
+ SCHEMA_EMPTY_RECORD: "A",
1912
+ HASH_DIGEST_LENGTH_MISMATCH: "A",
1913
+ UNSUPPORTED_HASH_ALG: "A",
1914
+ UNSUPPORTED_MERKLE_COMMIT_ALG: "A",
1915
+ SCHEMA_MERKLE_LEAF_COUNT_INVALID: "A",
1916
+ INVALID_URI: "A",
1917
+ CHUNK_TOO_LARGE: "carriage",
1918
+ UNAUTHENTICATED_CIPHER_FORBIDDEN: "A",
1919
+ UNSUPPORTED_AEAD_ALG: "A",
1920
+ NONCE_LENGTH_MISMATCH: "A",
1921
+ UNSUPPORTED_ENVELOPE_SCHEME: "A",
1922
+ ENC_UNSUPPORTED: "A",
1923
+ ENC_SLOTS_EMPTY: "A",
1924
+ ENC_SLOT_INVALID_SHAPE: "A",
1925
+ UNSUPPORTED_KEM_ALG: "A",
1926
+ ENC_KEM_REQUIRED: "A",
1927
+ KEM_EPK_LENGTH_MISMATCH: "A",
1928
+ KEM_CT_LENGTH_MISMATCH: "A",
1929
+ WRAP_LENGTH_MISMATCH: "A",
1930
+ ENC_SLOTS_MAC_INVALID_LENGTH: "A",
1931
+ ENC_SLOTS_MAC_REQUIRED: "A",
1932
+ ENC_SLOTS_REQUIRED: "A",
1933
+ ENC_SLOTS_DUPLICATE_KEM_MATERIAL: "A",
1934
+ ENC_SLOTS_TOO_MANY: "A",
1935
+ ENC_ENVELOPE_TOO_LARGE: "A",
1936
+ ENC_EXCLUSIVITY_VIOLATION: "A",
1937
+ ENC_NO_KEY_PATH: "A",
1938
+ ENC_REQUIRES_CONTENT_HASH: "A",
1939
+ ENC_PASSPHRASE_ALG_UNSUPPORTED: "A",
1940
+ ENC_PASSPHRASE_SALT_TOO_SHORT: "A",
1941
+ ENC_PASSPHRASE_SALT_TOO_LONG: "A",
1942
+ ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW: "A",
1943
+ ENC_PASSPHRASE_PARAMS_EXCEED_POLICY: "A",
1944
+ MALFORMED_SIG_COSE_SIGN1: "A",
1945
+ SIGNATURE_UNSUPPORTED: "A",
1946
+ SIG_ENTRY_INVALID_SHAPE: "A",
1947
+ SIG_ENTRY_KID_COSE_KEY_CONFLICT: "A",
1948
+ SIG_PRIVATE_KEY_LEAKED: "A",
1949
+ SUPERSEDES_TX_INVALID_LENGTH: "A",
1950
+ EXTENSION_UNSUPPORTED_CRITICAL: "A",
1951
+ CRIT_SHAPE_INVALID: "A",
1952
+ TX_NOT_FOUND: "B",
1953
+ PROVIDER_UNAVAILABLE: "B",
1954
+ TX_INTEGRITY_MISMATCH: "B",
1955
+ METADATA_NOT_FOUND: "B",
1956
+ INSUFFICIENT_CONFIRMATIONS: "B",
1957
+ SIGNATURE_INVALID: "B",
1958
+ SIGNER_KEY_UNRESOLVED: "B",
1959
+ WALLET_ADDRESS_MISMATCH: "B",
1960
+ URI_TARGET_FORBIDDEN: "B",
1961
+ URI_INTEGRITY_MISMATCH: "B",
1962
+ URI_PROVIDER_INTEGRITY_MISMATCH: "B",
1963
+ URI_FETCH_FAILED: "B",
1964
+ CONTENT_UNAVAILABLE: "B",
1965
+ CONTENT_FETCH_LIMIT_EXCEEDED: "B",
1966
+ CIPHERTEXT_UNAVAILABLE: "B",
1967
+ SERVICE_INDEPENDENCE_VIOLATION: "B",
1968
+ WRONG_DECRYPTION_INPUT_SHAPE: "B",
1969
+ WRONG_RECIPIENT_KEY: "B",
1970
+ TAMPERED_HEADER: "B",
1971
+ TAMPERED_CIPHERTEXT: "B",
1972
+ KDF_DERIVATION_FAILED: "B",
1973
+ ENC_PASSPHRASE_UNNORMALIZABLE: "B",
1974
+ ENC_PASSPHRASE_EMPTY: "B",
1975
+ SCHEMA_MERKLE_LEAF_COUNT_MISMATCH: "B",
1976
+ SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED: "B",
1977
+ SCHEMA_MERKLE_LEAVES_MALFORMED: "B",
1978
+ MERKLE_ROOT_MISMATCH: "B",
1979
+ MERKLE_LEAVES_UNAVAILABLE: "B",
1980
+ MERKLE_UNSUPPORTED: "B",
1981
+ OUT_OF_PROFILE_SKIPPED: "B"
1982
+ });
1983
+ var STRUCTURAL_ERROR_CODES = Object.freeze(
1984
+ ERROR_CODES.filter((code) => ERROR_CODE_PART[code] === "A")
1985
+ );
1986
+ var CARRIAGE_ERROR_CODES = Object.freeze(
1987
+ ERROR_CODES.filter((code) => ERROR_CODE_PART[code] === "carriage")
1988
+ );
1989
+ var VERIFIER_ERROR_CODES = Object.freeze(
1990
+ ERROR_CODES.filter((code) => ERROR_CODE_PART[code] === "B")
1991
+ );
1992
+ var SEVERITY = Object.freeze({
1993
+ MALFORMED_CBOR: "error",
1994
+ SCHEMA_TYPE_MISMATCH: "error",
1995
+ SCHEMA_MISSING_REQUIRED: "error",
1996
+ SCHEMA_UNKNOWN_FIELD: "error",
1997
+ SCHEMA_INVALID_LITERAL: "error",
1998
+ SCHEMA_EMPTY_RECORD: "error",
1999
+ HASH_DIGEST_LENGTH_MISMATCH: "error",
2000
+ UNSUPPORTED_HASH_ALG: "error",
2001
+ UNSUPPORTED_MERKLE_COMMIT_ALG: "error",
2002
+ SCHEMA_MERKLE_LEAF_COUNT_INVALID: "error",
2003
+ INVALID_URI: "error",
2004
+ CHUNK_TOO_LARGE: "error",
2005
+ UNAUTHENTICATED_CIPHER_FORBIDDEN: "error",
2006
+ UNSUPPORTED_AEAD_ALG: "error",
2007
+ NONCE_LENGTH_MISMATCH: "error",
2008
+ UNSUPPORTED_ENVELOPE_SCHEME: "error",
2009
+ ENC_UNSUPPORTED: "info",
2010
+ ENC_SLOTS_EMPTY: "error",
2011
+ ENC_SLOT_INVALID_SHAPE: "error",
2012
+ UNSUPPORTED_KEM_ALG: "error",
2013
+ ENC_KEM_REQUIRED: "error",
2014
+ KEM_EPK_LENGTH_MISMATCH: "error",
2015
+ KEM_CT_LENGTH_MISMATCH: "error",
2016
+ WRAP_LENGTH_MISMATCH: "error",
2017
+ ENC_SLOTS_MAC_INVALID_LENGTH: "error",
2018
+ ENC_SLOTS_MAC_REQUIRED: "error",
2019
+ ENC_SLOTS_REQUIRED: "error",
2020
+ ENC_SLOTS_DUPLICATE_KEM_MATERIAL: "error",
2021
+ ENC_SLOTS_TOO_MANY: "error",
2022
+ ENC_ENVELOPE_TOO_LARGE: "error",
2023
+ ENC_EXCLUSIVITY_VIOLATION: "error",
2024
+ ENC_NO_KEY_PATH: "error",
2025
+ ENC_REQUIRES_CONTENT_HASH: "error",
2026
+ ENC_PASSPHRASE_ALG_UNSUPPORTED: "error",
2027
+ ENC_PASSPHRASE_SALT_TOO_SHORT: "error",
2028
+ ENC_PASSPHRASE_SALT_TOO_LONG: "error",
2029
+ ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW: "error",
2030
+ ENC_PASSPHRASE_PARAMS_EXCEED_POLICY: "error",
2031
+ MALFORMED_SIG_COSE_SIGN1: "error",
2032
+ SIGNATURE_UNSUPPORTED: "info",
2033
+ SIG_ENTRY_INVALID_SHAPE: "error",
2034
+ SIG_ENTRY_KID_COSE_KEY_CONFLICT: "error",
2035
+ SIG_PRIVATE_KEY_LEAKED: "error",
2036
+ SUPERSEDES_TX_INVALID_LENGTH: "error",
2037
+ EXTENSION_UNSUPPORTED_CRITICAL: "error",
2038
+ CRIT_SHAPE_INVALID: "error",
2039
+ TX_NOT_FOUND: "error",
2040
+ PROVIDER_UNAVAILABLE: "error",
2041
+ TX_INTEGRITY_MISMATCH: "error",
2042
+ METADATA_NOT_FOUND: "error",
2043
+ INSUFFICIENT_CONFIRMATIONS: "info",
2044
+ SIGNATURE_INVALID: "error",
2045
+ SIGNER_KEY_UNRESOLVED: "error",
2046
+ WALLET_ADDRESS_MISMATCH: "error",
2047
+ URI_TARGET_FORBIDDEN: "error",
2048
+ URI_INTEGRITY_MISMATCH: "error",
2049
+ URI_PROVIDER_INTEGRITY_MISMATCH: "warning",
2050
+ URI_FETCH_FAILED: "warning",
2051
+ CONTENT_UNAVAILABLE: "error",
2052
+ CONTENT_FETCH_LIMIT_EXCEEDED: "error",
2053
+ CIPHERTEXT_UNAVAILABLE: "error",
2054
+ SERVICE_INDEPENDENCE_VIOLATION: "error",
2055
+ WRONG_DECRYPTION_INPUT_SHAPE: "error",
2056
+ WRONG_RECIPIENT_KEY: "error",
2057
+ TAMPERED_HEADER: "error",
2058
+ TAMPERED_CIPHERTEXT: "error",
2059
+ KDF_DERIVATION_FAILED: "error",
2060
+ ENC_PASSPHRASE_UNNORMALIZABLE: "error",
2061
+ ENC_PASSPHRASE_EMPTY: "error",
2062
+ SCHEMA_MERKLE_LEAF_COUNT_MISMATCH: "error",
2063
+ SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED: "error",
2064
+ SCHEMA_MERKLE_LEAVES_MALFORMED: "error",
2065
+ MERKLE_ROOT_MISMATCH: "error",
2066
+ MERKLE_LEAVES_UNAVAILABLE: "warning",
2067
+ MERKLE_UNSUPPORTED: "info",
2068
+ OUT_OF_PROFILE_SKIPPED: "info"
2069
+ });
2070
+ var DUAL_SEVERITY_CODES = /* @__PURE__ */ new Set([
2071
+ "ENC_UNSUPPORTED",
2072
+ "MERKLE_LEAVES_UNAVAILABLE",
2073
+ "MERKLE_UNSUPPORTED",
2074
+ "OUT_OF_PROFILE_SKIPPED"
2075
+ ]);
2076
+ function severityOf(code) {
2077
+ return SEVERITY[code];
1827
2078
  }
1828
- function envelopeToCbor(enc) {
1829
- const out = {
1830
- scheme: enc.scheme,
1831
- aead: enc.aead,
1832
- nonce: enc.nonce
1833
- };
1834
- if (enc.kem !== void 0) out["kem"] = enc.kem;
1835
- if (enc.slots !== void 0) out["slots"] = enc.slots.map(slotToCbor);
1836
- if (enc.slots_mac !== void 0) out["slots_mac"] = enc.slots_mac;
1837
- if (enc.passphrase !== void 0) out["passphrase"] = passphraseToCbor(enc.passphrase);
1838
- return out;
2079
+ var REGISTRY_INDEX = new Map(
2080
+ ERROR_CODES.map((code, index) => [code, index])
2081
+ );
2082
+ function errorCodeRegistryIndex(code) {
2083
+ return REGISTRY_INDEX.get(code);
1839
2084
  }
1840
- function slotToCbor(slot) {
1841
- if (slot.kem_ct !== void 0) {
1842
- return { kem_ct: slot.kem_ct.map((c5) => c5), wrap: slot.wrap };
2085
+
2086
+ // src/carriage.ts
2087
+ var TRANSPORT_CHUNK_MAX_BYTES = 64;
2088
+ function chunkRecordBody(body) {
2089
+ if (body.length === 0) {
2090
+ throw new RangeError("record body must be non-empty; a CBOR value is at least one byte");
1843
2091
  }
1844
- return { epk: slot.epk, wrap: slot.wrap };
2092
+ const chunks = [];
2093
+ for (let offset = 0; offset < body.length; offset += TRANSPORT_CHUNK_MAX_BYTES) {
2094
+ chunks.push(body.slice(offset, Math.min(offset + TRANSPORT_CHUNK_MAX_BYTES, body.length)));
2095
+ }
2096
+ return chunks;
1845
2097
  }
1846
- function passphraseToCbor(pp) {
1847
- return {
1848
- alg: pp.alg,
1849
- salt: pp.salt,
1850
- params: pp.params
1851
- };
2098
+ function encodeLabel309Value(body) {
2099
+ return encodeCanonicalCbor(chunkRecordBody(body));
1852
2100
  }
1853
- function sigEntryToCbor(entry) {
1854
- const out = { cose_sign1: entry.cose_sign1.map((b5) => b5) };
1855
- if (entry.cose_key !== void 0) {
1856
- out["cose_key"] = entry.cose_key.map((b5) => b5);
2101
+ function reassembleLabel309Value(valueBytes) {
2102
+ let decoded;
2103
+ try {
2104
+ decoded = decodeCanonicalCbor(valueBytes);
2105
+ } catch (cause) {
2106
+ return failure(
2107
+ "MALFORMED_CBOR",
2108
+ `label-309 value failed to decode: ${cause instanceof Error ? cause.message : String(cause)}`
2109
+ );
1857
2110
  }
1858
- return out;
2111
+ if (!Array.isArray(decoded)) {
2112
+ return failure(
2113
+ "MALFORMED_CBOR",
2114
+ "label-309 value must be the whole-body chunk array (a CBOR array of byte strings), regardless of body length"
2115
+ );
2116
+ }
2117
+ let total = 0;
2118
+ for (let i = 0; i < decoded.length; i++) {
2119
+ const element = decoded[i];
2120
+ if (!(element instanceof Uint8Array)) {
2121
+ return failure("MALFORMED_CBOR", `chunk array element ${i} is not a byte string`);
2122
+ }
2123
+ if (element.length > TRANSPORT_CHUNK_MAX_BYTES) {
2124
+ return failure(
2125
+ "CHUNK_TOO_LARGE",
2126
+ `chunk array element ${i} is ${element.length} bytes; the ledger caps metadata byte strings at ${TRANSPORT_CHUNK_MAX_BYTES}`
2127
+ );
2128
+ }
2129
+ total += element.length;
2130
+ }
2131
+ const body = new Uint8Array(total);
2132
+ let offset = 0;
2133
+ for (const element of decoded) {
2134
+ body.set(element, offset);
2135
+ offset += element.length;
2136
+ }
2137
+ return { ok: true, body };
2138
+ }
2139
+ function failure(code, message) {
2140
+ return { ok: false, issue: { code, path: [], message, severity: SEVERITY[code] } };
1859
2141
  }
1860
2142
 
1861
2143
  // ../../node_modules/.pnpm/@noble+ed25519@3.1.0/node_modules/@noble/ed25519/index.js
@@ -2387,223 +2669,89 @@ function decodeCoseSign1(bytes) {
2387
2669
  signature: signatureRaw
2388
2670
  };
2389
2671
  }
2390
-
2391
- // src/chunked.ts
2392
- var CHUNK_MAX_BYTES = 64;
2393
- var UTF8_ENCODER2 = new TextEncoder();
2394
- function chunkBytes(value) {
2395
- if (value.length === 0) return [new Uint8Array(0)];
2396
- const chunks = [];
2397
- for (let i = 0; i < value.length; i += CHUNK_MAX_BYTES) {
2398
- chunks.push(value.subarray(i, Math.min(i + CHUNK_MAX_BYTES, value.length)));
2399
- }
2400
- return chunks;
2672
+ var CARDANO_POE_ITEM_HASHES_PREFIX = new TextEncoder().encode(
2673
+ "cardano-poe-item-hashes-v1"
2674
+ );
2675
+ var CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX = new TextEncoder().encode(
2676
+ "cardano-poe-slots-transcript-v1"
2677
+ );
2678
+ var CARDANO_POE_PASSPHRASE_TRANSCRIPT_PREFIX = new TextEncoder().encode(
2679
+ "cardano-poe-passphrase-transcript-v1"
2680
+ );
2681
+ var CARDANO_POE_HKDF_INFO_SLOTS_MAC = new TextEncoder().encode(
2682
+ "cardano-poe-slots-mac-v1"
2683
+ );
2684
+ var CARDANO_POE_HKDF_INFO_PASSPHRASE_MAC = new TextEncoder().encode(
2685
+ "cardano-poe-passphrase-mac-v1"
2686
+ );
2687
+ var CARDANO_POE_HKDF_INFO_PAYLOAD = new TextEncoder().encode(
2688
+ "cardano-poe-payload-v1"
2689
+ );
2690
+ var CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE = new TextEncoder().encode(
2691
+ "cardano-poe-payload-passphrase-v1"
2692
+ );
2693
+ var CARDANO_POE_X25519_KEK_SALT_PREFIX = new TextEncoder().encode(
2694
+ "cardano-poe-x25519-kek-salt-v1"
2695
+ );
2696
+ var CARDANO_POE_XWING_KEK_SALT_PREFIX = new TextEncoder().encode(
2697
+ "cardano-poe-xwing-kek-salt-v1"
2698
+ );
2699
+ if (CARDANO_POE_ITEM_HASHES_PREFIX.length !== 26) {
2700
+ throw new Error("CARDANO_POE_ITEM_HASHES_PREFIX byte-length invariant violated (expected 26)");
2401
2701
  }
2402
- function bytesChunkArrayConcat(chunks) {
2403
- let total = 0;
2404
- for (const c5 of chunks) total += c5.length;
2405
- const out = new Uint8Array(total);
2406
- let offset = 0;
2407
- for (const c5 of chunks) {
2408
- out.set(c5, offset);
2409
- offset += c5.length;
2410
- }
2411
- return out;
2702
+ if (CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length !== 31) {
2703
+ throw new Error(
2704
+ "CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX byte-length invariant violated (expected 31)"
2705
+ );
2412
2706
  }
2413
- function reconstructChunkedUri(chunks) {
2414
- const merged = bytesChunkArrayConcat(chunks.map((c5) => UTF8_ENCODER2.encode(c5)));
2415
- try {
2416
- const uri = new TextDecoder("utf-8", { fatal: true }).decode(merged);
2417
- return { ok: true, uri };
2418
- } catch (cause) {
2419
- return {
2420
- ok: false,
2421
- code: "INVALID_URI",
2422
- reason: cause instanceof Error ? cause.message : String(cause)
2423
- };
2424
- }
2707
+ if (CARDANO_POE_PASSPHRASE_TRANSCRIPT_PREFIX.length !== 36) {
2708
+ throw new Error(
2709
+ "CARDANO_POE_PASSPHRASE_TRANSCRIPT_PREFIX byte-length invariant violated (expected 36)"
2710
+ );
2425
2711
  }
2426
- function chunkUri(uri) {
2427
- const bytes = UTF8_ENCODER2.encode(uri);
2428
- if (bytes.length === 0) return [""];
2429
- if (bytes.length <= CHUNK_MAX_BYTES) return [uri];
2430
- const decoder = new TextDecoder("utf-8", { fatal: true });
2431
- const chunks = [];
2432
- let cursor = 0;
2433
- while (cursor < bytes.length) {
2434
- let end = Math.min(cursor + CHUNK_MAX_BYTES, bytes.length);
2435
- while (end < bytes.length && (bytes[end] & 192) === 128) end--;
2436
- chunks.push(decoder.decode(bytes.subarray(cursor, end)));
2437
- cursor = end;
2438
- }
2439
- return chunks;
2712
+ if (CARDANO_POE_HKDF_INFO_SLOTS_MAC.length !== 24) {
2713
+ throw new Error("CARDANO_POE_HKDF_INFO_SLOTS_MAC byte-length invariant violated (expected 24)");
2440
2714
  }
2441
-
2442
- // src/error-codes.ts
2443
- var STRUCTURAL_ERROR_CODES = [
2444
- // CBOR decode layer. A single code covers every canonical-decode failure —
2445
- // malformed/truncated bytes, indefinite-length encodings, non-canonical
2446
- // (unsorted) map-key ordering, duplicate map keys, non-minimal integers, and
2447
- // invalid UTF-8 by design (no separate duplicate-key code).
2448
- "MALFORMED_CBOR",
2449
- // Generic schema-layer
2450
- "SCHEMA_TYPE_MISMATCH",
2451
- "SCHEMA_MISSING_REQUIRED",
2452
- "SCHEMA_UNKNOWN_FIELD",
2453
- "SCHEMA_INVALID_LITERAL",
2454
- "SCHEMA_EMPTY_RECORD",
2455
- // Hash-map
2456
- "HASH_DIGEST_LENGTH_MISMATCH",
2457
- "UNSUPPORTED_HASH_ALG",
2458
- // Top-level `merkle[]`
2459
- "UNSUPPORTED_MERKLE_COMMIT_ALG",
2460
- // URI / chunking. A chunk whose bytes do not reconstruct to valid UTF-8
2461
- // surfaces as MALFORMED_CBOR at decode (cbor2 rejects invalid-UTF-8 tstr)
2462
- // or, in the residual reconstruct guard, as INVALID_URI — there is no
2463
- // separate codepoint-split code.
2464
- "INVALID_URI",
2465
- "CHUNK_TOO_LARGE",
2466
- // Encryption envelope
2467
- "UNAUTHENTICATED_CIPHER_FORBIDDEN",
2468
- "UNSUPPORTED_AEAD_ALG",
2469
- "NONCE_LENGTH_MISMATCH",
2470
- "UNSUPPORTED_ENVELOPE_SCHEME",
2471
- "ENC_SLOTS_EMPTY",
2472
- "ENC_SLOT_INVALID_SHAPE",
2473
- "UNSUPPORTED_KEM_ALG",
2474
- "ENC_KEM_REQUIRED",
2475
- "KEM_EPK_LENGTH_MISMATCH",
2476
- "KEM_CT_LENGTH_MISMATCH",
2477
- "WRAP_LENGTH_MISMATCH",
2478
- "ENC_SLOTS_MAC_INVALID_LENGTH",
2479
- "ENC_SLOTS_MAC_REQUIRED",
2480
- "ENC_SLOTS_REQUIRED",
2481
- "ENC_EXCLUSIVITY_VIOLATION",
2482
- "ENC_NO_KEY_PATH",
2483
- "ENC_REQUIRES_CONTENT_HASH",
2484
- "ENC_PASSPHRASE_ALG_UNSUPPORTED",
2485
- "ENC_PASSPHRASE_SALT_TOO_SHORT",
2486
- "ENC_PASSPHRASE_SALT_TOO_LONG",
2487
- "ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
2488
- "ENC_PASSPHRASE_PARAMS_EXCEED_POLICY",
2489
- // Signatures
2490
- "MALFORMED_SIG_COSE_SIGN1",
2491
- "SIGNATURE_UNSUPPORTED",
2492
- "SIG_ENTRY_INVALID_SHAPE",
2493
- "SIG_ENTRY_KID_COSE_KEY_CONFLICT",
2494
- "SIG_PRIVATE_KEY_LEAKED",
2495
- // Supersedence
2496
- "SUPERSEDES_TX_INVALID_LENGTH",
2497
- // Forward-compat critical extensions
2498
- "EXTENSION_UNSUPPORTED_CRITICAL",
2499
- "CRIT_SHAPE_INVALID"
2500
- ];
2501
- var VERIFIER_ERROR_CODES = [
2502
- "METADATA_NOT_FOUND",
2503
- "INSUFFICIENT_CONFIRMATIONS",
2504
- "SIGNATURE_INVALID",
2505
- "SIGNER_KEY_UNRESOLVED",
2506
- "WALLET_ADDRESS_MISMATCH",
2507
- "URI_TARGET_FORBIDDEN",
2508
- "URI_INTEGRITY_MISMATCH",
2509
- "URI_FETCH_FAILED",
2510
- "CONTENT_UNAVAILABLE",
2511
- "CIPHERTEXT_UNAVAILABLE",
2512
- "PROVIDER_UNAVAILABLE",
2513
- "SERVICE_INDEPENDENCE_VIOLATION",
2514
- "WRONG_DECRYPTION_INPUT_SHAPE",
2515
- "WRONG_RECIPIENT_KEY",
2516
- "TAMPERED_HEADER",
2517
- "TAMPERED_CIPHERTEXT",
2518
- "KDF_DERIVATION_FAILED",
2519
- "SCHEMA_MERKLE_LEAF_COUNT_MISMATCH",
2520
- "SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED",
2521
- "SCHEMA_MERKLE_LEAVES_MALFORMED",
2522
- "MERKLE_ROOT_MISMATCH",
2523
- "MERKLE_LEAVES_UNAVAILABLE",
2524
- "MERKLE_LEAVES_INFORMATIVE_FORM",
2525
- "MERKLE_UNSUPPORTED",
2526
- "OUT_OF_PROFILE_SKIPPED"
2527
- ];
2528
- var ERROR_CODES = [...STRUCTURAL_ERROR_CODES, ...VERIFIER_ERROR_CODES];
2529
- var SEVERITY = Object.freeze({
2530
- // --- Part A ---
2531
- MALFORMED_CBOR: "error",
2532
- SCHEMA_TYPE_MISMATCH: "error",
2533
- SCHEMA_MISSING_REQUIRED: "error",
2534
- SCHEMA_UNKNOWN_FIELD: "error",
2535
- SCHEMA_INVALID_LITERAL: "error",
2536
- SCHEMA_EMPTY_RECORD: "error",
2537
- HASH_DIGEST_LENGTH_MISMATCH: "error",
2538
- UNSUPPORTED_HASH_ALG: "error",
2539
- UNSUPPORTED_MERKLE_COMMIT_ALG: "error",
2540
- INVALID_URI: "error",
2541
- CHUNK_TOO_LARGE: "error",
2542
- UNAUTHENTICATED_CIPHER_FORBIDDEN: "error",
2543
- UNSUPPORTED_AEAD_ALG: "error",
2544
- NONCE_LENGTH_MISMATCH: "error",
2545
- UNSUPPORTED_ENVELOPE_SCHEME: "error",
2546
- ENC_SLOTS_EMPTY: "error",
2547
- ENC_SLOT_INVALID_SHAPE: "error",
2548
- UNSUPPORTED_KEM_ALG: "error",
2549
- ENC_KEM_REQUIRED: "error",
2550
- KEM_EPK_LENGTH_MISMATCH: "error",
2551
- KEM_CT_LENGTH_MISMATCH: "error",
2552
- WRAP_LENGTH_MISMATCH: "error",
2553
- ENC_SLOTS_MAC_INVALID_LENGTH: "error",
2554
- ENC_SLOTS_MAC_REQUIRED: "error",
2555
- ENC_SLOTS_REQUIRED: "error",
2556
- ENC_EXCLUSIVITY_VIOLATION: "error",
2557
- ENC_NO_KEY_PATH: "error",
2558
- ENC_REQUIRES_CONTENT_HASH: "error",
2559
- ENC_PASSPHRASE_ALG_UNSUPPORTED: "error",
2560
- ENC_PASSPHRASE_SALT_TOO_SHORT: "error",
2561
- ENC_PASSPHRASE_SALT_TOO_LONG: "error",
2562
- ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW: "error",
2563
- ENC_PASSPHRASE_PARAMS_EXCEED_POLICY: "error",
2564
- MALFORMED_SIG_COSE_SIGN1: "error",
2565
- SIGNATURE_UNSUPPORTED: "info",
2566
- SIG_ENTRY_INVALID_SHAPE: "error",
2567
- SIG_ENTRY_KID_COSE_KEY_CONFLICT: "error",
2568
- SIG_PRIVATE_KEY_LEAKED: "error",
2569
- SUPERSEDES_TX_INVALID_LENGTH: "error",
2570
- EXTENSION_UNSUPPORTED_CRITICAL: "error",
2571
- CRIT_SHAPE_INVALID: "error",
2572
- // --- Part B ---
2573
- METADATA_NOT_FOUND: "error",
2574
- INSUFFICIENT_CONFIRMATIONS: "info",
2575
- SIGNATURE_INVALID: "error",
2576
- SIGNER_KEY_UNRESOLVED: "error",
2577
- WALLET_ADDRESS_MISMATCH: "error",
2578
- URI_TARGET_FORBIDDEN: "error",
2579
- URI_INTEGRITY_MISMATCH: "error",
2580
- URI_FETCH_FAILED: "warning",
2581
- CONTENT_UNAVAILABLE: "error",
2582
- CIPHERTEXT_UNAVAILABLE: "error",
2583
- PROVIDER_UNAVAILABLE: "error",
2584
- SERVICE_INDEPENDENCE_VIOLATION: "error",
2585
- WRONG_DECRYPTION_INPUT_SHAPE: "error",
2586
- WRONG_RECIPIENT_KEY: "error",
2587
- TAMPERED_HEADER: "error",
2588
- TAMPERED_CIPHERTEXT: "error",
2589
- KDF_DERIVATION_FAILED: "error",
2590
- SCHEMA_MERKLE_LEAF_COUNT_MISMATCH: "error",
2591
- SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED: "error",
2592
- SCHEMA_MERKLE_LEAVES_MALFORMED: "error",
2593
- MERKLE_ROOT_MISMATCH: "error",
2594
- MERKLE_LEAVES_UNAVAILABLE: "warning",
2595
- MERKLE_LEAVES_INFORMATIVE_FORM: "info",
2596
- // Dual-severity — default reading is `info`; the verifier promotes to
2597
- // `error` for merkle-only records (no `items[]` content claim was
2598
- // validated in the same record).
2599
- MERKLE_UNSUPPORTED: "info",
2600
- // Dual-severity — default reading is `info` (render mode); strict
2601
- // end-to-end verifiers promote to `error`.
2602
- OUT_OF_PROFILE_SKIPPED: "info"
2603
- });
2604
- function severityOf(code) {
2605
- return SEVERITY[code];
2715
+ if (CARDANO_POE_HKDF_INFO_PASSPHRASE_MAC.length !== 29) {
2716
+ throw new Error(
2717
+ "CARDANO_POE_HKDF_INFO_PASSPHRASE_MAC byte-length invariant violated (expected 29)"
2718
+ );
2719
+ }
2720
+ if (CARDANO_POE_HKDF_INFO_PAYLOAD.length !== 22) {
2721
+ throw new Error("CARDANO_POE_HKDF_INFO_PAYLOAD byte-length invariant violated (expected 22)");
2722
+ }
2723
+ if (CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE.length !== 33) {
2724
+ throw new Error(
2725
+ "CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE byte-length invariant violated (expected 33)"
2726
+ );
2727
+ }
2728
+ if (CARDANO_POE_X25519_KEK_SALT_PREFIX.length !== 30) {
2729
+ throw new Error(
2730
+ "CARDANO_POE_X25519_KEK_SALT_PREFIX byte-length invariant violated (expected 30)"
2731
+ );
2732
+ }
2733
+ if (CARDANO_POE_XWING_KEK_SALT_PREFIX.length !== 29) {
2734
+ throw new Error("CARDANO_POE_XWING_KEK_SALT_PREFIX byte-length invariant violated (expected 29)");
2735
+ }
2736
+ var MAX_SLOTS = 1024;
2737
+ var MAX_DECODED_ENVELOPE_BYTES = 65536;
2738
+ var CARDANO_POE_HKDF_INFO_KEK = new TextEncoder().encode("cardano-poe-kek-v1");
2739
+ var CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 = new TextEncoder().encode(
2740
+ "cardano-poe-kek-mlkem768x25519-v1"
2741
+ );
2742
+ var ZERO_NONCE_12 = new Uint8Array(12);
2743
+ if (CARDANO_POE_HKDF_INFO_KEK.length !== 18) {
2744
+ throw new Error("CARDANO_POE_HKDF_INFO_KEK byte-length invariant violated (expected 18)");
2745
+ }
2746
+ if (CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519.length !== 33) {
2747
+ throw new Error(
2748
+ "CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 byte-length invariant violated (expected 33)"
2749
+ );
2750
+ }
2751
+ if (ZERO_NONCE_12.length !== 12) {
2752
+ throw new Error("ZERO_NONCE_12 byte-length invariant violated (expected 12)");
2606
2753
  }
2754
+ new TextEncoder();
2607
2755
 
2608
2756
  // src/validator.ts
2609
2757
  var HASH_ALG_LENGTHS = {
@@ -2614,9 +2762,9 @@ var MERKLE_COMMIT_ALG_LENGTHS = {
2614
2762
  "rfc9162-sha256": 32
2615
2763
  };
2616
2764
  var AEAD_NONCE_LENGTHS = {
2617
- "xchacha20-poly1305": 24
2765
+ "chacha20-poly1305-stream64k": 24
2618
2766
  };
2619
- var UNAUTHENTICATED_CIPHER_RE = /(?:^|[-_])(?:cbc|ctr|ecb|cfb|ofb)(?:[-_]|\n?$)|^(?:rc4|des|3des)(?:[-_]|\n?$)/i;
2767
+ var UNAUTHENTICATED_CIPHER_RE = /(?:^|[-_])(?:cbc|ctr|ecb|cfb|ofb)(?:[-_]|$)|^(?:rc4|des|3des)(?:[-_]|$)/i;
2620
2768
  var KEM_SLOT_DESCRIPTORS = {
2621
2769
  x25519: { field: "epk", fieldLength: 32, wrapLength: 48 },
2622
2770
  mlkem768x25519: { field: "kem_ct", fieldLength: 1120, wrapLength: 48 }
@@ -2627,13 +2775,31 @@ var KEM_FIELD_LENGTH_CODE = {
2627
2775
  };
2628
2776
  var PASSPHRASE_KDF_ALGS = /* @__PURE__ */ new Set(["argon2id"]);
2629
2777
  var KNOWN_SIG_ALG_IDS = /* @__PURE__ */ new Set([-8, -19]);
2630
- function validatePoeRecord(bytes) {
2778
+ var UINT32_MAX = 4294967295;
2779
+ var DEFAULT_PASSPHRASE_PARAMS_CEILING = Object.freeze({
2780
+ m: 2097152,
2781
+ // KiB = 2 GiB
2782
+ t: 16,
2783
+ p: 8
2784
+ });
2785
+ var EMPTY_EXTENSION_SET = /* @__PURE__ */ new Set();
2786
+ function resolveOptions(options) {
2787
+ return {
2788
+ supportedCriticalExtensions: options?.supportedCriticalExtensions ?? EMPTY_EXTENSION_SET,
2789
+ role: options?.role ?? "public",
2790
+ maxSlots: options?.maxSlots ?? MAX_SLOTS,
2791
+ maxEncEnvelopeBytes: options?.maxEncEnvelopeBytes ?? MAX_DECODED_ENVELOPE_BYTES,
2792
+ passphraseParamsCeiling: options?.passphraseParamsCeiling === void 0 ? DEFAULT_PASSPHRASE_PARAMS_CEILING : options.passphraseParamsCeiling
2793
+ };
2794
+ }
2795
+ function validatePoeRecord(bytes, options) {
2796
+ const opts = resolveOptions(options);
2631
2797
  let decoded;
2632
2798
  try {
2633
2799
  decoded = decodeCanonicalCbor(bytes);
2634
2800
  } catch (cause) {
2635
2801
  return {
2636
- ok: false,
2802
+ valid: false,
2637
2803
  issues: [
2638
2804
  {
2639
2805
  code: "MALFORMED_CBOR",
@@ -2644,132 +2810,181 @@ function validatePoeRecord(bytes) {
2644
2810
  ]
2645
2811
  };
2646
2812
  }
2813
+ const nonTextKeyIssues = collectNonTextKeyMapIssues(decoded);
2814
+ if (nonTextKeyIssues.length > 0) {
2815
+ return { valid: false, issues: sortIssues(nonTextKeyIssues) };
2816
+ }
2647
2817
  const parse = PoeRecordSchema.safeParse(decoded);
2648
2818
  if (!parse.success) {
2649
- const issues = parse.error.issues.map((issue2) => mapZodIssue(issue2, decoded)).sort(compareIssuePath);
2650
- return { ok: false, issues };
2819
+ return { valid: false, issues: sortIssues(mapZodIssues(parse.error.issues, decoded)) };
2651
2820
  }
2652
2821
  const record = parse.data;
2653
- const errors = [];
2654
- const warnings = [];
2655
- const info = [];
2656
- const itemsLen = Array.isArray(record.items) ? record.items.length : 0;
2657
- const merkleLen = Array.isArray(record.merkle) ? record.merkle.length : 0;
2658
- if (itemsLen === 0 && merkleLen === 0) {
2659
- errors.push(
2660
- issue(
2661
- "SCHEMA_EMPTY_RECORD",
2662
- [],
2663
- "record must carry at least one of items[] or merkle[] non-empty"
2664
- )
2665
- );
2666
- }
2822
+ const issues = [];
2823
+ checkContentCommitmentPresence(record, issues);
2667
2824
  const decodedTopKeys = topLevelKeysOf(decoded);
2668
- const critShapeInvalidIndices = checkCritShape(record, decodedTopKeys, errors);
2669
- for (const k4 of decodedTopKeys) {
2670
- if (TOP_LEVEL_BASE_KEYS.has(k4)) continue;
2671
- if (isExtensionKey(k4)) continue;
2672
- errors.push(issue("SCHEMA_UNKNOWN_FIELD", [k4], `unknown top-level field: ${k4}`));
2673
- }
2674
- if (Array.isArray(record.crit)) {
2675
- for (let i = 0; i < record.crit.length; i++) {
2676
- if (critShapeInvalidIndices.has(i)) continue;
2677
- const critName = record.crit[i];
2678
- errors.push(
2679
- issue(
2680
- "EXTENSION_UNSUPPORTED_CRITICAL",
2681
- ["crit", i],
2682
- `crit lists extension '${critName}' that this validator does not implement`
2683
- )
2825
+ checkCrit(record, decodedTopKeys, opts.supportedCriticalExtensions, issues);
2826
+ for (const key of decodedTopKeys) {
2827
+ if (TOP_LEVEL_BASE_KEYS.has(key)) continue;
2828
+ if (isExtensionKey(key)) continue;
2829
+ issues.push(issueOf("SCHEMA_UNKNOWN_FIELD", [key], `unknown top-level field: ${key}`));
2830
+ }
2831
+ const items = record.items ?? [];
2832
+ for (let i = 0; i < items.length; i++) {
2833
+ const item = items[i];
2834
+ checkItemHashes(item, i, issues);
2835
+ if (item.uris !== void 0) checkUris(item.uris, ["items", i, "uris"], issues);
2836
+ if (item.enc !== void 0) checkItemEnc(item, i, opts, issues);
2837
+ }
2838
+ const merkle = record.merkle ?? [];
2839
+ for (let i = 0; i < merkle.length; i++) {
2840
+ checkMerkleCommit(merkle[i], i, issues);
2841
+ }
2842
+ if (record.sigs !== void 0) {
2843
+ if (record.sigs.length === 0) {
2844
+ issues.push(
2845
+ issueOf("SCHEMA_TYPE_MISMATCH", ["sigs"], "sigs[] must be non-empty when present")
2684
2846
  );
2685
2847
  }
2848
+ for (let i = 0; i < record.sigs.length; i++) {
2849
+ checkSigEntry(record.sigs[i], i, issues);
2850
+ }
2686
2851
  }
2687
- for (let i = 0; i < (record.items ?? []).length; i++) {
2688
- const item = record.items[i];
2689
- checkItemHashes(item, i, errors);
2690
- if (item.uris) checkItemUris(item.uris, ["items", i, "uris"], errors);
2691
- if (item.enc !== void 0) checkItemEnc(item, i, errors);
2692
- }
2693
- for (let i = 0; i < (record.merkle ?? []).length; i++) {
2694
- const commit = record.merkle[i];
2695
- checkMerkleCommit(commit, i, errors);
2852
+ const sorted = sortIssues(issues);
2853
+ if (sorted.some((issue) => issue.severity === "error")) {
2854
+ return { valid: false, issues: sorted };
2696
2855
  }
2697
- if (record.sigs) {
2698
- for (let i = 0; i < record.sigs.length; i++) {
2699
- checkSigEntry(record.sigs[i], i, errors, info);
2856
+ const warnings = sorted.filter((issue) => issue.severity === "warning");
2857
+ const info = sorted.filter((issue) => issue.severity === "info");
2858
+ const result = { valid: true, record };
2859
+ if (warnings.length > 0) result.warnings = warnings;
2860
+ if (info.length > 0) result.info = info;
2861
+ return result;
2862
+ }
2863
+ function mapZodIssues(zissues, decodedRoot) {
2864
+ const out = [];
2865
+ for (const zissue of zissues) {
2866
+ if (zissue.code === "unrecognized_keys") {
2867
+ for (const key of zissue.keys) {
2868
+ const path = [...zissue.path, key];
2869
+ const code = unknownKeyCode(path);
2870
+ out.push(issueOf(code, path, `unrecognized key '${key}' in a closed map`));
2871
+ }
2872
+ continue;
2700
2873
  }
2874
+ out.push(mapZodIssue(zissue, decodedRoot));
2701
2875
  }
2702
- if (errors.length > 0) {
2703
- return { ok: false, issues: errors.sort(compareIssuePath) };
2876
+ return out;
2877
+ }
2878
+ function unknownKeyCode(path) {
2879
+ if (path.length >= 2 && path[0] === "sigs" && typeof path[1] === "number") {
2880
+ return "SIG_ENTRY_INVALID_SHAPE";
2704
2881
  }
2705
- const result = {
2706
- ok: true,
2707
- record
2882
+ return "SCHEMA_UNKNOWN_FIELD";
2883
+ }
2884
+ function collectNonTextKeyMapIssues(decoded) {
2885
+ const issues = [];
2886
+ const flag = (path) => {
2887
+ issues.push(
2888
+ issueOf(
2889
+ "SCHEMA_TYPE_MISMATCH",
2890
+ path,
2891
+ "CBOR map carries a non-text key where a text-keyed map is required"
2892
+ )
2893
+ );
2708
2894
  };
2709
- if (warnings.length > 0) result.warnings = warnings.sort(compareIssuePath);
2710
- if (info.length > 0) result.info = info.sort(compareIssuePath);
2711
- return result;
2895
+ if (decoded instanceof Map) {
2896
+ flag([]);
2897
+ return issues;
2898
+ }
2899
+ if (decoded === null || typeof decoded !== "object" || Array.isArray(decoded)) return issues;
2900
+ const record = decoded;
2901
+ for (const field of ["items", "merkle", "sigs"]) {
2902
+ const entries = record[field];
2903
+ if (!Array.isArray(entries)) continue;
2904
+ entries.forEach((entry, i) => {
2905
+ if (entry instanceof Map) {
2906
+ flag([field, i]);
2907
+ return;
2908
+ }
2909
+ if (field !== "items" || entry === null || typeof entry !== "object") return;
2910
+ const item = entry;
2911
+ if (item["hashes"] instanceof Map) flag([field, i, "hashes"]);
2912
+ if (item["enc"] instanceof Map) flag([field, i, "enc"]);
2913
+ });
2914
+ }
2915
+ return issues;
2712
2916
  }
2713
- function mapZodIssue(zissue, decoded) {
2917
+ function mapZodIssue(zissue, decodedRoot) {
2714
2918
  const path = zissue.path;
2715
2919
  const explicit = zissue.params?.code;
2716
2920
  if (explicit !== void 0) {
2717
- return issue(explicit, path, zissue.message);
2921
+ return issueOf(explicit, path, zissue.message);
2922
+ }
2923
+ const valueAtIssue = valueAtPath(decodedRoot, path);
2924
+ if (valueAtIssue instanceof Map) {
2925
+ return issueOf(
2926
+ "SCHEMA_TYPE_MISMATCH",
2927
+ path,
2928
+ "CBOR map carries a non-text key where a text-keyed map is required"
2929
+ );
2718
2930
  }
2719
2931
  const inSigsEntry = path.length >= 2 && path[0] === "sigs" && typeof path[1] === "number";
2720
- const isInSlotEntry = (() => {
2721
- if (path.length >= 5 && path[0] === "items" && typeof path[1] === "number" && path[2] === "enc" && path[3] === "slots" && typeof path[4] === "number") {
2722
- return true;
2723
- }
2724
- if (path.length >= 2 && path[0] === "slots" && typeof path[1] === "number") {
2725
- return true;
2726
- }
2727
- return false;
2728
- })();
2729
- const valueAtIssue = valueAtPath(decoded, path);
2932
+ const isInSlotEntry = path.length >= 2 && path[0] === "slots" && typeof path[1] === "number";
2730
2933
  const isMissing = valueAtIssue === void 0;
2731
2934
  switch (zissue.code) {
2732
2935
  case "invalid_type":
2733
- if (isInSlotEntry) return issue("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
2936
+ if (isInSlotEntry) return issueOf("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
2734
2937
  if (isMissing) {
2735
- if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
2736
- return issue("SCHEMA_MISSING_REQUIRED", path, zissue.message);
2938
+ if (inSigsEntry) return issueOf("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
2939
+ return issueOf("SCHEMA_MISSING_REQUIRED", path, zissue.message);
2737
2940
  }
2738
- if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
2739
- return issue("SCHEMA_TYPE_MISMATCH", path, zissue.message);
2941
+ if (inSigsEntry) return issueOf("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
2942
+ return issueOf("SCHEMA_TYPE_MISMATCH", path, zissue.message);
2740
2943
  case "invalid_value":
2741
- if (path.length === 1 && path[0] === "v") {
2742
- return issue(
2743
- isMissing ? "SCHEMA_MISSING_REQUIRED" : "SCHEMA_INVALID_LITERAL",
2744
- path,
2745
- zissue.message
2746
- );
2747
- }
2748
- return issue("SCHEMA_INVALID_LITERAL", path, zissue.message);
2749
- case "unrecognized_keys":
2750
- if (isInSlotEntry) return issue("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
2751
- if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
2752
- return issue("SCHEMA_UNKNOWN_FIELD", path, zissue.message);
2944
+ if (isMissing) return issueOf("SCHEMA_MISSING_REQUIRED", path, zissue.message);
2945
+ return issueOf("SCHEMA_INVALID_LITERAL", path, zissue.message);
2946
+ case "invalid_union":
2753
2947
  case "invalid_format":
2754
2948
  case "too_big":
2755
2949
  case "too_small":
2756
- if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
2757
- return issue("SCHEMA_TYPE_MISMATCH", path, zissue.message);
2758
- case "invalid_union":
2759
2950
  case "invalid_key":
2760
2951
  case "invalid_element":
2761
2952
  case "custom":
2762
2953
  default:
2763
- if (isInSlotEntry) return issue("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
2764
- if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
2765
- return issue("SCHEMA_TYPE_MISMATCH", path, zissue.message);
2954
+ if (isInSlotEntry) return issueOf("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
2955
+ if (inSigsEntry) return issueOf("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
2956
+ return issueOf("SCHEMA_TYPE_MISMATCH", path, zissue.message);
2957
+ }
2958
+ }
2959
+ function checkContentCommitmentPresence(record, issues) {
2960
+ const itemsLen = record.items?.length ?? 0;
2961
+ const merkleLen = record.merkle?.length ?? 0;
2962
+ if (itemsLen === 0 && merkleLen === 0) {
2963
+ issues.push(
2964
+ issueOf(
2965
+ "SCHEMA_EMPTY_RECORD",
2966
+ [],
2967
+ "record must carry at least one of items[] or merkle[] non-empty"
2968
+ )
2969
+ );
2970
+ return;
2971
+ }
2972
+ if (record.items !== void 0 && itemsLen === 0) {
2973
+ issues.push(
2974
+ issueOf("SCHEMA_TYPE_MISMATCH", ["items"], "items[] must be non-empty when present")
2975
+ );
2976
+ }
2977
+ if (record.merkle !== void 0 && merkleLen === 0) {
2978
+ issues.push(
2979
+ issueOf("SCHEMA_TYPE_MISMATCH", ["merkle"], "merkle[] must be non-empty when present")
2980
+ );
2766
2981
  }
2767
2982
  }
2768
- function checkItemHashes(item, idx, errors) {
2983
+ function checkItemHashes(item, idx, issues) {
2769
2984
  const entries = Object.entries(item.hashes);
2770
2985
  if (entries.length === 0) {
2771
- errors.push(
2772
- issue(
2986
+ issues.push(
2987
+ issueOf(
2773
2988
  "SCHEMA_TYPE_MISMATCH",
2774
2989
  ["items", idx, "hashes"],
2775
2990
  "hashes must be a non-empty CBOR map of <alg-id> -> <digest>"
@@ -2779,15 +2994,15 @@ function checkItemHashes(item, idx, errors) {
2779
2994
  }
2780
2995
  for (const [alg, digest] of entries) {
2781
2996
  if (!(alg in HASH_ALG_LENGTHS)) {
2782
- errors.push(
2783
- issue("UNSUPPORTED_HASH_ALG", ["items", idx, "hashes", alg], `unknown hash alg: ${alg}`)
2997
+ issues.push(
2998
+ issueOf("UNSUPPORTED_HASH_ALG", ["items", idx, "hashes", alg], `unknown hash alg: ${alg}`)
2784
2999
  );
2785
3000
  continue;
2786
3001
  }
2787
3002
  const expected = HASH_ALG_LENGTHS[alg];
2788
3003
  if (digest.length !== expected) {
2789
- errors.push(
2790
- issue(
3004
+ issues.push(
3005
+ issueOf(
2791
3006
  "HASH_DIGEST_LENGTH_MISMATCH",
2792
3007
  ["items", idx, "hashes", alg],
2793
3008
  `hashes['${alg}'] digest length ${digest.length} != ${expected}`
@@ -2796,35 +3011,33 @@ function checkItemHashes(item, idx, errors) {
2796
3011
  }
2797
3012
  }
2798
3013
  }
2799
- function checkItemUris(uris, basePath, errors) {
2800
- uris.forEach((chunks, ui) => validateOneUri(chunks, [...basePath, ui], errors));
2801
- }
2802
- function validateOneUri(chunks, path, errors) {
2803
- const reconstructed = reconstructChunkedUri(chunks);
2804
- if (!reconstructed.ok) {
2805
- errors.push(issue(reconstructed.code, path, reconstructed.reason));
3014
+ function checkUris(uris, basePath, issues) {
3015
+ if (uris.length === 0) {
3016
+ issues.push(issueOf("SCHEMA_TYPE_MISMATCH", basePath, "uris[] must be non-empty when present"));
2806
3017
  return;
2807
3018
  }
2808
- const uri = reconstructed.uri;
3019
+ uris.forEach((uri, ui) => checkOneUri(uri, [...basePath, ui], issues));
3020
+ }
3021
+ function checkOneUri(uri, path, issues) {
2809
3022
  if (uri.includes("#")) {
2810
- errors.push(
2811
- issue("INVALID_URI", path, "URI contains a fragment identifier ('#'), which is forbidden")
3023
+ issues.push(
3024
+ issueOf("INVALID_URI", path, "URI contains a fragment identifier ('#'), which is forbidden")
2812
3025
  );
2813
3026
  return;
2814
3027
  }
2815
3028
  const sepIdx = uri.indexOf("://");
2816
3029
  if (sepIdx <= 0 || !/^[a-z][a-z0-9+.-]*$/i.test(uri.slice(0, sepIdx))) {
2817
- errors.push(
2818
- issue("INVALID_URI", path, "URI is not absolute (missing scheme://hierarchical-part)")
3030
+ issues.push(
3031
+ issueOf("INVALID_URI", path, "URI is not absolute (missing scheme://hierarchical-part)")
2819
3032
  );
2820
3033
  return;
2821
3034
  }
2822
3035
  const scheme = uri.slice(0, sepIdx).toLowerCase();
2823
3036
  const rest = uri.slice(sepIdx + "://".length);
2824
3037
  if (scheme === "ar") {
2825
- if (!/^ar:\/\/[A-Za-z0-9_-]{43}$/.test("ar://" + rest)) {
2826
- errors.push(
2827
- issue(
3038
+ if (!/^[A-Za-z0-9_-]{43}$/.test(rest)) {
3039
+ issues.push(
3040
+ issueOf(
2828
3041
  "INVALID_URI",
2829
3042
  path,
2830
3043
  "ar:// URI does not match `^ar://[A-Za-z0-9_-]{43}$` (43-char base64url txid, no path/query/fragment)"
@@ -2837,277 +3050,286 @@ function validateOneUri(chunks, path, errors) {
2837
3050
  const slashIdx = rest.indexOf("/");
2838
3051
  const cid = slashIdx === -1 ? rest : rest.slice(0, slashIdx);
2839
3052
  if (!validateCidProfile(cid)) {
2840
- errors.push(
2841
- issue("INVALID_URI", path, "ipfs:// URI is not a valid CID under the Label 309 profile")
3053
+ issues.push(
3054
+ issueOf("INVALID_URI", path, "ipfs:// URI is not a valid CID under the Label 309 profile")
2842
3055
  );
2843
3056
  }
2844
3057
  return;
2845
3058
  }
2846
- errors.push(
2847
- issue("INVALID_URI", path, "unsupported URI scheme; v1 PoE URI set is {ar://, ipfs://}")
3059
+ issues.push(
3060
+ issueOf("INVALID_URI", path, "unsupported URI scheme; v1 PoE URI set is {ar://, ipfs://}")
2848
3061
  );
2849
3062
  }
2850
- function checkItemEnc(item, idx, errors) {
3063
+ function checkItemEnc(item, idx, opts, issues) {
3064
+ const encPath = ["items", idx, "enc"];
2851
3065
  const hasContentHash = Object.keys(item.hashes).some((alg) => alg in HASH_ALG_LENGTHS);
2852
3066
  if (!hasContentHash) {
2853
- errors.push(
2854
- issue(
3067
+ issues.push(
3068
+ issueOf(
2855
3069
  "ENC_REQUIRES_CONTENT_HASH",
2856
- ["items", idx, "enc"],
2857
- "item carries `enc` but `hashes` has no content-hash entry (sha2-256 or blake2b-256)"
3070
+ encPath,
3071
+ "item carries `enc` but `hashes` has no registered content-hash entry (sha2-256 or blake2b-256)"
2858
3072
  )
2859
3073
  );
3074
+ }
3075
+ const rawEnc = item.enc;
3076
+ if (rawEnc === null || typeof rawEnc !== "object" || Array.isArray(rawEnc) || rawEnc instanceof Uint8Array) {
3077
+ issues.push(issueOf("SCHEMA_TYPE_MISMATCH", encPath, "enc must be a CBOR map"));
2860
3078
  return;
2861
3079
  }
2862
- const encParse = EncryptionEnvelopeSchema.safeParse(item.enc);
2863
- if (!encParse.success) {
2864
- for (const zissue of encParse.error.issues) {
2865
- const mapped = mapZodIssue(zissue, item.enc);
2866
- errors.push({
2867
- ...mapped,
2868
- path: ["items", idx, "enc", ...mapped.path]
2869
- });
2870
- }
3080
+ const enc = rawEnc;
3081
+ const envelopeBytes = encodeCanonicalCbor(rawEnc).length;
3082
+ if (envelopeBytes > opts.maxEncEnvelopeBytes) {
3083
+ issues.push(
3084
+ issueOf(
3085
+ "ENC_ENVELOPE_TOO_LARGE",
3086
+ encPath,
3087
+ `decoded envelope is ${envelopeBytes} bytes; the resource bound is ${opts.maxEncEnvelopeBytes}`
3088
+ )
3089
+ );
3090
+ }
3091
+ const scheme = enc["scheme"];
3092
+ if (scheme === void 0) {
3093
+ issues.push(
3094
+ issueOf("SCHEMA_MISSING_REQUIRED", [...encPath, "scheme"], "enc.scheme is required")
3095
+ );
2871
3096
  return;
2872
3097
  }
2873
- const enc = encParse.data;
2874
- const basePath = ["items", idx, "enc"];
2875
- if (typeof enc.scheme !== "number" || !Number.isInteger(enc.scheme) || enc.scheme !== 1) {
2876
- errors.push(
2877
- issue(
2878
- "UNSUPPORTED_ENVELOPE_SCHEME",
2879
- [...basePath, "scheme"],
2880
- `enc.scheme must be the unsigned integer 1; got ${String(enc.scheme)}`
3098
+ if (!isUint(scheme)) {
3099
+ issues.push(
3100
+ issueOf(
3101
+ "SCHEMA_TYPE_MISMATCH",
3102
+ [...encPath, "scheme"],
3103
+ "enc.scheme must be a CBOR unsigned integer"
2881
3104
  )
2882
3105
  );
3106
+ return;
2883
3107
  }
2884
- if (UNAUTHENTICATED_CIPHER_RE.test(enc.aead)) {
2885
- errors.push(
2886
- issue(
3108
+ const aead = enc["aead"];
3109
+ if (typeof aead === "string" && UNAUTHENTICATED_CIPHER_RE.test(aead)) {
3110
+ issues.push(
3111
+ issueOf(
2887
3112
  "UNAUTHENTICATED_CIPHER_FORBIDDEN",
2888
- [...basePath, "aead"],
2889
- `'${enc.aead}' is an unauthenticated cipher; Label 309 mandates an authenticated (AEAD) cipher`
3113
+ [...encPath, "aead"],
3114
+ `'${aead}' is an unauthenticated cipher; Label 309 mandates an authenticated (AEAD) cipher`
2890
3115
  )
2891
3116
  );
2892
3117
  return;
2893
3118
  }
2894
- if (!(enc.aead in AEAD_NONCE_LENGTHS)) {
2895
- errors.push(
2896
- issue("UNSUPPORTED_AEAD_ALG", [...basePath, "aead"], `unknown aead alg: ${enc.aead}`)
2897
- );
3119
+ const kem = enc["kem"];
3120
+ const unsupported = [];
3121
+ if (!(typeof scheme === "number" && scheme === 1)) {
3122
+ unsupported.push({ field: "scheme", code: "UNSUPPORTED_ENVELOPE_SCHEME", id: String(scheme) });
3123
+ }
3124
+ if (typeof kem === "string" && !(kem in KEM_SLOT_DESCRIPTORS)) {
3125
+ unsupported.push({ field: "kem", code: "UNSUPPORTED_KEM_ALG", id: kem });
3126
+ }
3127
+ if (typeof aead === "string" && !(aead in AEAD_NONCE_LENGTHS)) {
3128
+ unsupported.push({ field: "aead", code: "UNSUPPORTED_AEAD_ALG", id: aead });
3129
+ }
3130
+ if (unsupported.length > 0) {
3131
+ const named = unsupported.map((u3) => `${u3.field}=${u3.id}`).join(", ");
3132
+ const message = `envelope uses identifiers this implementation does not support (${named}); the envelope is opaque and only the content-hash claim is validated`;
3133
+ if (opts.role === "recipient_or_strict") {
3134
+ issues.push({ code: "ENC_UNSUPPORTED", path: encPath, message, severity: "error" });
3135
+ for (const u3 of unsupported) {
3136
+ issues.push(
3137
+ issueOf(u3.code, [...encPath, u3.field], `enc.${u3.field} '${u3.id}' is not supported`)
3138
+ );
3139
+ }
3140
+ } else {
3141
+ issues.push({ code: "ENC_UNSUPPORTED", path: encPath, message, severity: "info" });
3142
+ }
3143
+ return;
3144
+ }
3145
+ const internalMapIssues = encInternalNonTextKeyIssues(enc, encPath);
3146
+ if (internalMapIssues.length > 0) {
3147
+ issues.push(...internalMapIssues);
3148
+ return;
3149
+ }
3150
+ const encParse = EncScheme1Schema.safeParse(rawEnc);
3151
+ if (!encParse.success) {
3152
+ for (const mapped of mapZodIssues(encParse.error.issues, rawEnc)) {
3153
+ issues.push({ ...mapped, path: [...encPath, ...mapped.path] });
3154
+ }
2898
3155
  return;
2899
3156
  }
3157
+ checkScheme1Envelope(encParse.data, rawEnc, encPath, opts, issues);
3158
+ }
3159
+ function encInternalNonTextKeyIssues(enc, encPath) {
3160
+ const issues = [];
3161
+ const flag = (path) => {
3162
+ issues.push(
3163
+ issueOf(
3164
+ "SCHEMA_TYPE_MISMATCH",
3165
+ path,
3166
+ "CBOR map carries a non-text key where a text-keyed map is required"
3167
+ )
3168
+ );
3169
+ };
3170
+ const slots = enc["slots"];
3171
+ if (Array.isArray(slots)) {
3172
+ slots.forEach((slot, i) => {
3173
+ if (slot instanceof Map) flag([...encPath, "slots", i]);
3174
+ });
3175
+ }
3176
+ const passphrase = enc["passphrase"];
3177
+ if (passphrase instanceof Map) {
3178
+ flag([...encPath, "passphrase"]);
3179
+ } else if (passphrase !== null && typeof passphrase === "object" && !Array.isArray(passphrase)) {
3180
+ const params = passphrase["params"];
3181
+ if (params instanceof Map) flag([...encPath, "passphrase", "params"]);
3182
+ }
3183
+ return issues;
3184
+ }
3185
+ function checkScheme1Envelope(enc, rawEnc, encPath, opts, issues) {
2900
3186
  const expectedNonceLen = AEAD_NONCE_LENGTHS[enc.aead];
2901
3187
  if (enc.nonce.length !== expectedNonceLen) {
2902
- errors.push(
2903
- issue(
3188
+ issues.push(
3189
+ issueOf(
2904
3190
  "NONCE_LENGTH_MISMATCH",
2905
- [...basePath, "nonce"],
3191
+ [...encPath, "nonce"],
2906
3192
  `nonce length ${enc.nonce.length} != ${expectedNonceLen} for ${enc.aead}`
2907
3193
  )
2908
3194
  );
2909
3195
  }
2910
- if (enc.kem !== void 0 && !(enc.kem in KEM_SLOT_DESCRIPTORS)) {
2911
- errors.push(issue("UNSUPPORTED_KEM_ALG", [...basePath, "kem"], `unknown kem alg: ${enc.kem}`));
2912
- }
2913
3196
  const hasSlots = enc.slots !== void 0;
2914
3197
  const hasSlotsMac = enc.slots_mac !== void 0;
2915
3198
  const hasPassphrase = enc.passphrase !== void 0;
2916
- if (hasSlots && hasPassphrase) {
2917
- errors.push(
2918
- issue("ENC_EXCLUSIVITY_VIOLATION", basePath, "enc combines slots with passphrase; pick one")
3199
+ const hasKem = enc.kem !== void 0;
3200
+ if (hasPassphrase && (hasSlots || hasSlotsMac || hasKem)) {
3201
+ issues.push(
3202
+ issueOf(
3203
+ "ENC_EXCLUSIVITY_VIOLATION",
3204
+ encPath,
3205
+ "enc.passphrase is mutually exclusive with kem / slots / slots_mac; exactly one key path is allowed"
3206
+ )
2919
3207
  );
2920
3208
  }
2921
3209
  if (hasSlots && !hasSlotsMac) {
2922
- errors.push(
2923
- issue("ENC_SLOTS_MAC_REQUIRED", basePath, "enc.slots present but enc.slots_mac absent")
3210
+ issues.push(
3211
+ issueOf("ENC_SLOTS_MAC_REQUIRED", encPath, "enc.slots present but enc.slots_mac absent")
2924
3212
  );
2925
3213
  }
2926
3214
  if (hasSlotsMac && !hasSlots) {
2927
- errors.push(
2928
- issue("ENC_SLOTS_REQUIRED", basePath, "enc.slots_mac present but enc.slots absent")
3215
+ issues.push(
3216
+ issueOf("ENC_SLOTS_REQUIRED", encPath, "enc.slots_mac present but enc.slots absent")
2929
3217
  );
2930
3218
  }
2931
- if (hasSlots && enc.kem === void 0) {
2932
- errors.push(issue("ENC_KEM_REQUIRED", basePath, "enc.slots present but enc.kem absent"));
3219
+ if (hasSlots && !hasKem) {
3220
+ issues.push(issueOf("ENC_KEM_REQUIRED", encPath, "enc.slots present but enc.kem absent"));
2933
3221
  }
2934
3222
  if (!hasSlots && !hasPassphrase) {
2935
- errors.push(
2936
- issue(
3223
+ issues.push(
3224
+ issueOf(
2937
3225
  "ENC_NO_KEY_PATH",
2938
- basePath,
3226
+ encPath,
2939
3227
  "enc requires either slots or passphrase \u2014 no on-chain key path otherwise"
2940
3228
  )
2941
3229
  );
2942
3230
  }
2943
3231
  if (hasSlots) {
2944
- if (enc.slots.length < 1) {
2945
- errors.push(
2946
- issue("ENC_SLOTS_EMPTY", [...basePath, "slots"], `slots length ${enc.slots.length} < 1`)
3232
+ const slots = enc.slots;
3233
+ if (slots.length < 1) {
3234
+ issues.push(
3235
+ issueOf("ENC_SLOTS_EMPTY", [...encPath, "slots"], "slots[] must carry at least one slot")
2947
3236
  );
2948
- }
2949
- const descriptor = enc.kem !== void 0 ? KEM_SLOT_DESCRIPTORS[enc.kem] : void 0;
2950
- if (descriptor !== void 0) {
2951
- const rawSlotKeys = rawSlotKeySets(item.enc);
2952
- enc.slots.forEach((slot, si) => {
3237
+ } else if (slots.length > opts.maxSlots) {
3238
+ issues.push(
3239
+ issueOf(
3240
+ "ENC_SLOTS_TOO_MANY",
3241
+ [...encPath, "slots"],
3242
+ `slots length ${slots.length} exceeds the slot-count bound ${opts.maxSlots}`
3243
+ )
3244
+ );
3245
+ } else if (hasKem) {
3246
+ const descriptor = KEM_SLOT_DESCRIPTORS[enc.kem];
3247
+ const rawSlotKeys = rawSlotKeySets(rawEnc);
3248
+ const seenKemMaterial = /* @__PURE__ */ new Set();
3249
+ slots.forEach((slot, si) => {
3250
+ const slotPath = [...encPath, "slots", si];
2953
3251
  checkSlotShape(
2954
3252
  slot,
2955
3253
  rawSlotKeys[si] ?? /* @__PURE__ */ new Set(),
2956
3254
  descriptor,
2957
3255
  enc.kem,
2958
- [...basePath, "slots", si],
2959
- errors
3256
+ slotPath,
3257
+ issues
2960
3258
  );
3259
+ const material = descriptor.field === "epk" ? slot.epk : slot.kem_ct;
3260
+ if (material !== void 0) {
3261
+ const key = bytesToHex2(material);
3262
+ if (seenKemMaterial.has(key)) {
3263
+ issues.push(
3264
+ issueOf(
3265
+ "ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
3266
+ [...slotPath, descriptor.field],
3267
+ `slot ${si} ${descriptor.field} duplicates an earlier slot \u2014 per-slot KEK uniqueness is violated`
3268
+ )
3269
+ );
3270
+ } else {
3271
+ seenKemMaterial.add(key);
3272
+ }
3273
+ }
2961
3274
  });
2962
3275
  }
2963
3276
  }
2964
3277
  if (hasPassphrase) {
2965
- const pp = enc.passphrase;
2966
- const ppPath = [...basePath, "passphrase"];
2967
- if (!PASSPHRASE_KDF_ALGS.has(pp.alg)) {
2968
- errors.push(
2969
- issue(
2970
- "ENC_PASSPHRASE_ALG_UNSUPPORTED",
2971
- [...ppPath, "alg"],
2972
- `unknown passphrase kdf alg: ${pp.alg}`
2973
- )
2974
- );
2975
- return;
2976
- }
2977
- if (pp.alg === "argon2id") {
2978
- const allowed = /* @__PURE__ */ new Set(["m", "t", "p"]);
2979
- for (const k4 of Object.keys(pp.params)) {
2980
- if (!allowed.has(k4)) {
2981
- errors.push(
2982
- issue(
2983
- "SCHEMA_UNKNOWN_FIELD",
2984
- [...ppPath, "params", k4],
2985
- `unknown argon2id params field: ${k4}`
2986
- )
2987
- );
2988
- }
2989
- }
2990
- const p5 = pp.params;
2991
- const argonInt = (val, name) => {
2992
- if (typeof val !== "number" || !Number.isInteger(val)) {
2993
- errors.push(
2994
- issue(
2995
- "SCHEMA_TYPE_MISMATCH",
2996
- [...ppPath, "params", name],
2997
- `argon2id params.${name} must be a CBOR unsigned integer`
2998
- )
2999
- );
3000
- return null;
3001
- }
3002
- return val;
3003
- };
3004
- const mVal = argonInt(p5.m, "m");
3005
- const tVal = argonInt(p5.t, "t");
3006
- const pVal = argonInt(p5.p, "p");
3007
- if (mVal !== null && mVal < 65536) {
3008
- errors.push(
3009
- issue(
3010
- "ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
3011
- [...ppPath, "params", "m"],
3012
- "argon2id requires m >= 65536 KiB"
3013
- )
3014
- );
3015
- }
3016
- if (tVal !== null && tVal < 3) {
3017
- errors.push(
3018
- issue(
3019
- "ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
3020
- [...ppPath, "params", "t"],
3021
- "argon2id requires t >= 3"
3022
- )
3023
- );
3024
- }
3025
- if (pVal !== null && pVal < 1) {
3026
- errors.push(
3027
- issue(
3028
- "ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
3029
- [...ppPath, "params", "p"],
3030
- "argon2id requires p >= 1"
3031
- )
3032
- );
3033
- }
3034
- }
3278
+ checkPassphraseBlock(enc.passphrase, [...encPath, "passphrase"], opts, issues);
3035
3279
  }
3036
3280
  }
3037
3281
  var SLOT_KEY_UNIVERSE = /* @__PURE__ */ new Set(["epk", "kem_ct", "wrap"]);
3038
- function checkSlotShape(slot, rawKeys, descriptor, kem, slotPath, errors) {
3282
+ function checkSlotShape(slot, rawKeys, descriptor, kem, slotPath, issues) {
3039
3283
  const foreignField = descriptor.field === "epk" ? "kem_ct" : "epk";
3040
3284
  if (rawKeys.has(foreignField)) {
3041
- errors.push(
3042
- issue(
3285
+ issues.push(
3286
+ issueOf(
3043
3287
  "ENC_SLOT_INVALID_SHAPE",
3044
3288
  [...slotPath, foreignField],
3045
3289
  `slot carries '${foreignField}' but kem='${kem}' expects '${descriptor.field}'`
3046
3290
  )
3047
3291
  );
3048
3292
  }
3049
- for (const k4 of rawKeys) {
3050
- if (!SLOT_KEY_UNIVERSE.has(k4)) {
3051
- errors.push(
3052
- issue(
3293
+ for (const key of rawKeys) {
3294
+ if (!SLOT_KEY_UNIVERSE.has(key)) {
3295
+ issues.push(
3296
+ issueOf(
3053
3297
  "ENC_SLOT_INVALID_SHAPE",
3054
- [...slotPath, k4],
3055
- `slot carries unexpected key '${k4}'; a slot is a 2-key map {${descriptor.field}, wrap}`
3298
+ [...slotPath, key],
3299
+ `slot carries unexpected key '${key}'; a slot is a 2-key map {${descriptor.field}, wrap}`
3056
3300
  )
3057
3301
  );
3058
3302
  }
3059
3303
  }
3060
- if (descriptor.field === "epk") {
3061
- if (slot.epk === void 0) {
3062
- errors.push(
3063
- issue(
3064
- "ENC_SLOT_INVALID_SHAPE",
3065
- [...slotPath, "epk"],
3066
- `slot for kem='${kem}' is missing required 'epk'`
3067
- )
3068
- );
3069
- } else if (slot.epk.length !== descriptor.fieldLength) {
3070
- errors.push(
3071
- issue(
3072
- KEM_FIELD_LENGTH_CODE.epk,
3073
- [...slotPath, "epk"],
3074
- `slot.epk length ${slot.epk.length} != ${descriptor.fieldLength} for ${kem}`
3075
- )
3076
- );
3077
- }
3078
- } else {
3079
- if (slot.kem_ct === void 0) {
3080
- errors.push(
3081
- issue(
3082
- "ENC_SLOT_INVALID_SHAPE",
3083
- [...slotPath, "kem_ct"],
3084
- `slot for kem='${kem}' is missing required 'kem_ct'`
3085
- )
3086
- );
3087
- } else {
3088
- const reassembled = bytesChunkArrayConcat(slot.kem_ct).length;
3089
- if (reassembled !== descriptor.fieldLength) {
3090
- errors.push(
3091
- issue(
3092
- KEM_FIELD_LENGTH_CODE.kem_ct,
3093
- [...slotPath, "kem_ct"],
3094
- `slot.kem_ct reassembles to ${reassembled} bytes != ${descriptor.fieldLength} for ${kem}`
3095
- )
3096
- );
3097
- }
3098
- }
3304
+ const ctField = descriptor.field === "epk" ? slot.epk : slot.kem_ct;
3305
+ if (ctField === void 0) {
3306
+ issues.push(
3307
+ issueOf(
3308
+ "ENC_SLOT_INVALID_SHAPE",
3309
+ [...slotPath, descriptor.field],
3310
+ `slot for kem='${kem}' is missing required '${descriptor.field}'`
3311
+ )
3312
+ );
3313
+ } else if (ctField.length !== descriptor.fieldLength) {
3314
+ issues.push(
3315
+ issueOf(
3316
+ KEM_FIELD_LENGTH_CODE[descriptor.field],
3317
+ [...slotPath, descriptor.field],
3318
+ `slot.${descriptor.field} length ${ctField.length} != ${descriptor.fieldLength} for ${kem}`
3319
+ )
3320
+ );
3099
3321
  }
3100
3322
  if (slot.wrap === void 0) {
3101
- errors.push(
3102
- issue(
3323
+ issues.push(
3324
+ issueOf(
3103
3325
  "ENC_SLOT_INVALID_SHAPE",
3104
3326
  [...slotPath, "wrap"],
3105
3327
  `slot for kem='${kem}' is missing required 'wrap'`
3106
3328
  )
3107
3329
  );
3108
3330
  } else if (slot.wrap.length !== descriptor.wrapLength) {
3109
- errors.push(
3110
- issue(
3331
+ issues.push(
3332
+ issueOf(
3111
3333
  "WRAP_LENGTH_MISMATCH",
3112
3334
  [...slotPath, "wrap"],
3113
3335
  `slot.wrap length ${slot.wrap.length} != ${descriptor.wrapLength}`
@@ -3115,8 +3337,88 @@ function checkSlotShape(slot, rawKeys, descriptor, kem, slotPath, errors) {
3115
3337
  );
3116
3338
  }
3117
3339
  }
3340
+ function checkPassphraseBlock(pp, ppPath, opts, issues) {
3341
+ if (!PASSPHRASE_KDF_ALGS.has(pp.alg)) {
3342
+ issues.push(
3343
+ issueOf(
3344
+ "ENC_PASSPHRASE_ALG_UNSUPPORTED",
3345
+ [...ppPath, "alg"],
3346
+ `unknown passphrase kdf alg: ${pp.alg}`
3347
+ )
3348
+ );
3349
+ return;
3350
+ }
3351
+ const paramsPath = [...ppPath, "params"];
3352
+ const params = pp.params;
3353
+ for (const key of Object.keys(params)) {
3354
+ if (key !== "m" && key !== "t" && key !== "p") {
3355
+ issues.push(
3356
+ issueOf(
3357
+ "SCHEMA_UNKNOWN_FIELD",
3358
+ [...paramsPath, key],
3359
+ `unknown argon2id params field: ${key}`
3360
+ )
3361
+ );
3362
+ }
3363
+ }
3364
+ const floors = { m: 65536, t: 3, p: 1 };
3365
+ const ceiling = opts.passphraseParamsCeiling;
3366
+ for (const name of ["m", "t", "p"]) {
3367
+ const value = params[name];
3368
+ if (value === void 0) {
3369
+ issues.push(
3370
+ issueOf(
3371
+ "SCHEMA_MISSING_REQUIRED",
3372
+ [...paramsPath, name],
3373
+ `argon2id params.${name} is required`
3374
+ )
3375
+ );
3376
+ continue;
3377
+ }
3378
+ if (!isUint(value)) {
3379
+ issues.push(
3380
+ issueOf(
3381
+ "SCHEMA_TYPE_MISMATCH",
3382
+ [...paramsPath, name],
3383
+ `argon2id params.${name} must be a CBOR unsigned integer`
3384
+ )
3385
+ );
3386
+ continue;
3387
+ }
3388
+ if (!uintWithin(value, 0, UINT32_MAX)) {
3389
+ issues.push(
3390
+ issueOf(
3391
+ "SCHEMA_TYPE_MISMATCH",
3392
+ [...paramsPath, name],
3393
+ `argon2id params.${name} exceeds the pinned wire range 0 .. 2^32 - 1`
3394
+ )
3395
+ );
3396
+ continue;
3397
+ }
3398
+ const num = Number(value);
3399
+ if (num < floors[name]) {
3400
+ issues.push(
3401
+ issueOf(
3402
+ "ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
3403
+ [...paramsPath, name],
3404
+ `argon2id requires ${name} >= ${floors[name]}`
3405
+ )
3406
+ );
3407
+ continue;
3408
+ }
3409
+ if (ceiling !== null && num > ceiling[name]) {
3410
+ issues.push(
3411
+ issueOf(
3412
+ "ENC_PASSPHRASE_PARAMS_EXCEED_POLICY",
3413
+ [...paramsPath, name],
3414
+ `argon2id params.${name} = ${num} exceeds the deployment ceiling ${ceiling[name]}`
3415
+ )
3416
+ );
3417
+ }
3418
+ }
3419
+ }
3118
3420
  function rawSlotKeySets(rawEnc) {
3119
- const slots = mapLikeGet(rawEnc, "slots");
3421
+ const slots = rawEnc["slots"];
3120
3422
  if (!Array.isArray(slots)) return [];
3121
3423
  return slots.map((slot) => {
3122
3424
  const keys = /* @__PURE__ */ new Set();
@@ -3128,54 +3430,64 @@ function rawSlotKeySets(rawEnc) {
3128
3430
  return keys;
3129
3431
  });
3130
3432
  }
3131
- function mapLikeGet(value, key) {
3132
- if (value instanceof Map) return value.get(key);
3133
- if (typeof value === "object" && value !== null) {
3134
- return value[key];
3135
- }
3136
- return void 0;
3137
- }
3138
- function checkMerkleCommit(commit, idx, errors) {
3433
+ function checkMerkleCommit(commit, idx, issues) {
3139
3434
  const basePath = ["merkle", idx];
3140
3435
  if (!(commit.alg in MERKLE_COMMIT_ALG_LENGTHS)) {
3141
- errors.push(
3142
- issue(
3436
+ issues.push(
3437
+ issueOf(
3143
3438
  "UNSUPPORTED_MERKLE_COMMIT_ALG",
3144
3439
  [...basePath, "alg"],
3145
3440
  `unknown merkle commitment alg: ${commit.alg}`
3146
3441
  )
3147
3442
  );
3148
- return;
3443
+ } else {
3444
+ const expected = MERKLE_COMMIT_ALG_LENGTHS[commit.alg];
3445
+ if (commit.root.length !== expected) {
3446
+ issues.push(
3447
+ issueOf(
3448
+ "HASH_DIGEST_LENGTH_MISMATCH",
3449
+ [...basePath, "root"],
3450
+ `merkle entry root length ${commit.root.length} != ${expected} for ${commit.alg}`
3451
+ )
3452
+ );
3453
+ }
3149
3454
  }
3150
- const expected = MERKLE_COMMIT_ALG_LENGTHS[commit.alg];
3151
- if (commit.root.length !== expected) {
3152
- errors.push(
3153
- issue(
3154
- "HASH_DIGEST_LENGTH_MISMATCH",
3155
- [...basePath, "root"],
3156
- `merkle entry root length ${commit.root.length} != ${expected} for ${commit.alg}`
3455
+ const leafCount = commit.leaf_count;
3456
+ if (!isUint(leafCount)) {
3457
+ issues.push(
3458
+ issueOf(
3459
+ "SCHEMA_TYPE_MISMATCH",
3460
+ [...basePath, "leaf_count"],
3461
+ "leaf_count must be a CBOR unsigned integer"
3462
+ )
3463
+ );
3464
+ } else if (!uintWithin(leafCount, 1, UINT32_MAX)) {
3465
+ issues.push(
3466
+ issueOf(
3467
+ "SCHEMA_MERKLE_LEAF_COUNT_INVALID",
3468
+ [...basePath, "leaf_count"],
3469
+ `leaf_count ${String(leafCount)} is outside the pinned range 1 .. 2^32 - 1`
3157
3470
  )
3158
3471
  );
3159
3472
  }
3160
- if (commit.uris) {
3161
- checkItemUris(commit.uris, [...basePath, "uris"], errors);
3473
+ if (commit.uris !== void 0) {
3474
+ checkUris(commit.uris, [...basePath, "uris"], issues);
3162
3475
  }
3163
3476
  }
3164
- function checkSigEntry(entry, idx, errors, info) {
3477
+ function checkSigEntry(entry, idx, issues) {
3165
3478
  if (entry.cose_key !== void 0) {
3166
3479
  const keyIssue = inspectCoseKey(entry.cose_key, idx);
3167
3480
  if (keyIssue !== null) {
3168
- errors.push(keyIssue);
3481
+ issues.push(keyIssue);
3169
3482
  return;
3170
3483
  }
3171
3484
  }
3172
- const merged = bytesChunkArrayConcat(entry.cose_sign1);
3173
3485
  let cose;
3174
3486
  try {
3175
- cose = decodeCoseSign1(merged);
3487
+ cose = decodeCoseSign1(entry.cose_sign1);
3176
3488
  } catch (cause) {
3177
- errors.push(
3178
- issue(
3489
+ issues.push(
3490
+ issueOf(
3179
3491
  "MALFORMED_SIG_COSE_SIGN1",
3180
3492
  ["sigs", idx],
3181
3493
  cause instanceof CoseVerifyError || cause instanceof Error ? cause.message : String(cause)
@@ -3184,8 +3496,8 @@ function checkSigEntry(entry, idx, errors, info) {
3184
3496
  return;
3185
3497
  }
3186
3498
  if (cose.payload !== null) {
3187
- errors.push(
3188
- issue(
3499
+ issues.push(
3500
+ issueOf(
3189
3501
  "MALFORMED_SIG_COSE_SIGN1",
3190
3502
  ["sigs", idx],
3191
3503
  "COSE_Sign1 payload must be null (detached); attached form forbidden"
@@ -3195,8 +3507,8 @@ function checkSigEntry(entry, idx, errors, info) {
3195
3507
  }
3196
3508
  const alg = cose.protectedHeader.get(1);
3197
3509
  if (typeof alg !== "number" || !KNOWN_SIG_ALG_IDS.has(alg)) {
3198
- info.push(
3199
- issue(
3510
+ issues.push(
3511
+ issueOf(
3200
3512
  "SIGNATURE_UNSUPPORTED",
3201
3513
  ["sigs", idx],
3202
3514
  `COSE_Sign1 protected alg ${String(alg)} not in {-8, -19}`
@@ -3205,8 +3517,8 @@ function checkSigEntry(entry, idx, errors, info) {
3205
3517
  }
3206
3518
  const protectedKid = cose.protectedHeader.get(4);
3207
3519
  if (protectedKid instanceof Uint8Array && protectedKid.length === 32 && entry.cose_key !== void 0) {
3208
- errors.push(
3209
- issue(
3520
+ issues.push(
3521
+ issueOf(
3210
3522
  "SIG_ENTRY_KID_COSE_KEY_CONFLICT",
3211
3523
  ["sigs", idx],
3212
3524
  "sigs[i] carries both a 32-byte protected `kid` (path 1) and an inline `cose_key` (path 2); paths are mutually exclusive"
@@ -3214,12 +3526,12 @@ function checkSigEntry(entry, idx, errors, info) {
3214
3526
  );
3215
3527
  }
3216
3528
  }
3217
- function inspectCoseKey(keyChunks, i) {
3529
+ function inspectCoseKey(keyBytes, i) {
3218
3530
  let decoded;
3219
3531
  try {
3220
- decoded = decodeCanonicalCbor(bytesChunkArrayConcat(keyChunks));
3532
+ decoded = decodeCanonicalCbor(keyBytes);
3221
3533
  } catch (cause) {
3222
- return issue(
3534
+ return issueOf(
3223
3535
  "MALFORMED_SIG_COSE_SIGN1",
3224
3536
  ["sigs", i, "cose_key"],
3225
3537
  `sigs[${i}].cose_key failed to decode as cbor<COSE_Key>: ${cause instanceof Error ? cause.message : String(cause)}`
@@ -3227,20 +3539,14 @@ function inspectCoseKey(keyChunks, i) {
3227
3539
  }
3228
3540
  const getLabel = (label) => {
3229
3541
  if (decoded instanceof Map) return decoded.get(label);
3230
- if (typeof decoded === "object" && decoded !== null) {
3231
- return decoded[String(label)];
3232
- }
3233
3542
  return void 0;
3234
3543
  };
3235
3544
  const hasLabel = (label) => {
3236
3545
  if (decoded instanceof Map) return decoded.has(label);
3237
- if (typeof decoded === "object" && decoded !== null) {
3238
- return Object.prototype.hasOwnProperty.call(decoded, String(label));
3239
- }
3240
3546
  return false;
3241
3547
  };
3242
3548
  if (hasLabel(-4)) {
3243
- return issue(
3549
+ return issueOf(
3244
3550
  "SIG_PRIVATE_KEY_LEAKED",
3245
3551
  ["sigs", i, "cose_key"],
3246
3552
  "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"
@@ -3248,7 +3554,7 @@ function inspectCoseKey(keyChunks, i) {
3248
3554
  }
3249
3555
  const kty = getLabel(1);
3250
3556
  if (kty !== 1) {
3251
- return issue(
3557
+ return issueOf(
3252
3558
  "MALFORMED_SIG_COSE_SIGN1",
3253
3559
  ["sigs", i, "cose_key"],
3254
3560
  `sigs[${i}].cose_key COSE_Key kty (label 1) must be 1 (OKP); got ${String(kty)}`
@@ -3256,23 +3562,16 @@ function inspectCoseKey(keyChunks, i) {
3256
3562
  }
3257
3563
  const crv = getLabel(-1);
3258
3564
  if (crv !== 6) {
3259
- return issue(
3565
+ return issueOf(
3260
3566
  "MALFORMED_SIG_COSE_SIGN1",
3261
3567
  ["sigs", i, "cose_key"],
3262
3568
  `sigs[${i}].cose_key COSE_Key crv (label -1) must be 6 (Ed25519); got ${String(crv)}`
3263
3569
  );
3264
3570
  }
3265
- if (!hasLabel(-2)) {
3266
- return issue(
3267
- "MALFORMED_SIG_COSE_SIGN1",
3268
- ["sigs", i, "cose_key"],
3269
- `sigs[${i}].cose_key COSE_Key missing label -2 (Ed25519 public-key bytes)`
3270
- );
3271
- }
3272
3571
  const x5 = getLabel(-2);
3273
3572
  if (!(x5 instanceof Uint8Array) || x5.length !== 32) {
3274
3573
  const got = x5 instanceof Uint8Array ? `${x5.length}-byte bstr` : typeof x5;
3275
- return issue(
3574
+ return issueOf(
3276
3575
  "MALFORMED_SIG_COSE_SIGN1",
3277
3576
  ["sigs", i, "cose_key"],
3278
3577
  `sigs[${i}].cose_key COSE_Key label -2 must be a 32-byte byte string (Ed25519 public key); got ${got}`
@@ -3280,6 +3579,58 @@ function inspectCoseKey(keyChunks, i) {
3280
3579
  }
3281
3580
  return null;
3282
3581
  }
3582
+ function checkCrit(record, decodedTopKeys, supportedCriticalExtensions, issues) {
3583
+ if (!Array.isArray(record.crit)) return;
3584
+ if (record.crit.length === 0) {
3585
+ issues.push(
3586
+ issueOf(
3587
+ "SCHEMA_TYPE_MISMATCH",
3588
+ ["crit"],
3589
+ "crit[] must carry at least one entry when present"
3590
+ )
3591
+ );
3592
+ return;
3593
+ }
3594
+ const seen = /* @__PURE__ */ new Set();
3595
+ for (let i = 0; i < record.crit.length; i++) {
3596
+ const critName = record.crit[i];
3597
+ let reason = null;
3598
+ if (TOP_LEVEL_BASE_KEYS.has(critName)) {
3599
+ reason = `'${critName}' is a base key and MUST NOT appear in crit[]`;
3600
+ } else if (!isExtensionKey(critName)) {
3601
+ reason = `'${critName}' does not match the extension-key form (^x-.+ or ^[a-z]+-.+, no control characters)`;
3602
+ } else if (!decodedTopKeys.has(critName)) {
3603
+ reason = `'${critName}' is named in crit but absent from the record map`;
3604
+ } else if (seen.has(critName)) {
3605
+ reason = `'${critName}' appears more than once in crit[]`;
3606
+ }
3607
+ seen.add(critName);
3608
+ if (reason !== null) {
3609
+ issues.push(issueOf("CRIT_SHAPE_INVALID", ["crit", i], reason));
3610
+ continue;
3611
+ }
3612
+ if (!supportedCriticalExtensions.has(critName)) {
3613
+ issues.push(
3614
+ issueOf(
3615
+ "EXTENSION_UNSUPPORTED_CRITICAL",
3616
+ ["crit", i],
3617
+ `crit lists extension '${critName}' that this validator does not implement`
3618
+ )
3619
+ );
3620
+ }
3621
+ }
3622
+ }
3623
+ function topLevelKeysOf(decoded) {
3624
+ if (decoded === null || typeof decoded !== "object") return /* @__PURE__ */ new Set();
3625
+ if (decoded instanceof Map) {
3626
+ const out = /* @__PURE__ */ new Set();
3627
+ for (const k4 of decoded.keys()) {
3628
+ if (typeof k4 === "string") out.add(k4);
3629
+ }
3630
+ return out;
3631
+ }
3632
+ return new Set(Object.keys(decoded));
3633
+ }
3283
3634
  var ACCEPTED_CIDV1_MULTIBASE = /* @__PURE__ */ new Set(["b", "B", "f", "F", "z"]);
3284
3635
  var ACCEPTED_MULTICODECS = /* @__PURE__ */ new Set([85, 112, 113]);
3285
3636
  var ACCEPTED_MULTIHASHES = /* @__PURE__ */ new Map([
@@ -3415,52 +3766,56 @@ function decodeBase58btc(s3) {
3415
3766
  }
3416
3767
  return out;
3417
3768
  }
3418
- function checkCritShape(record, decodedTopKeys, errors) {
3419
- const invalid = /* @__PURE__ */ new Set();
3420
- if (!Array.isArray(record.crit)) return invalid;
3421
- if (record.crit.length === 0) {
3422
- errors.push(
3423
- issue("SCHEMA_TYPE_MISMATCH", ["crit"], "crit[] must carry at least one entry when present")
3424
- );
3425
- return invalid;
3426
- }
3427
- const seen = /* @__PURE__ */ new Set();
3428
- for (let i = 0; i < record.crit.length; i++) {
3429
- const critName = record.crit[i];
3430
- let reason = null;
3431
- if (TOP_LEVEL_BASE_KEYS.has(critName)) {
3432
- reason = `'${critName}' is a base key and MUST NOT appear in crit[]`;
3433
- } else if (!isExtensionKey(critName)) {
3434
- reason = `'${critName}' does not match the extension-key regex (^x-.+ or ^[a-z]+-.+)`;
3435
- } else if (!decodedTopKeys.has(critName)) {
3436
- reason = `'${critName}' is named in crit but absent from the record map`;
3437
- } else if (seen.has(critName)) {
3438
- reason = `'${critName}' appears more than once in crit[]`;
3439
- }
3440
- seen.add(critName);
3441
- if (reason !== null) {
3442
- invalid.add(i);
3443
- errors.push(issue("CRIT_SHAPE_INVALID", ["crit", i], reason));
3444
- }
3445
- }
3446
- return invalid;
3769
+ function bytesToHex2(bytes) {
3770
+ let out = "";
3771
+ for (const b5 of bytes) out += b5.toString(16).padStart(2, "0");
3772
+ return out;
3447
3773
  }
3448
- function topLevelKeysOf(decoded) {
3449
- if (decoded === null || typeof decoded !== "object") return /* @__PURE__ */ new Set();
3450
- if (decoded instanceof Map) {
3451
- const out = /* @__PURE__ */ new Set();
3452
- for (const k4 of decoded.keys()) {
3453
- if (typeof k4 === "string") out.add(k4);
3454
- }
3455
- return out;
3456
- }
3457
- return new Set(Object.keys(decoded));
3774
+ function isUint(value) {
3775
+ if (typeof value === "number") return Number.isInteger(value) && value >= 0;
3776
+ if (typeof value === "bigint") return value >= 0n;
3777
+ return false;
3778
+ }
3779
+ function uintWithin(value, min, max) {
3780
+ if (typeof value === "bigint") return value >= BigInt(min) && value <= BigInt(max);
3781
+ return value >= min && value <= max;
3458
3782
  }
3459
- function issue(code, path, message) {
3783
+ function issueOf(code, path, message) {
3460
3784
  return { code, path, message, severity: SEVERITY[code] };
3461
3785
  }
3462
- function compareIssuePath(a4, b5) {
3463
- return a4.path.join(".").localeCompare(b5.path.join("."));
3786
+ var PATH_UTF8 = new TextEncoder();
3787
+ function compareTextSegments(a4, b5) {
3788
+ const ab = PATH_UTF8.encode(a4);
3789
+ const bb = PATH_UTF8.encode(b5);
3790
+ const n2 = Math.min(ab.length, bb.length);
3791
+ for (let i = 0; i < n2; i++) {
3792
+ const d6 = ab[i] - bb[i];
3793
+ if (d6 !== 0) return d6;
3794
+ }
3795
+ return ab.length - bb.length;
3796
+ }
3797
+ function compareIssues(a4, b5) {
3798
+ const ap = a4.path;
3799
+ const bp = b5.path;
3800
+ const n2 = Math.min(ap.length, bp.length);
3801
+ for (let i = 0; i < n2; i++) {
3802
+ const x5 = ap[i];
3803
+ const y7 = bp[i];
3804
+ const xIsNum = typeof x5 === "number";
3805
+ const yIsNum = typeof y7 === "number";
3806
+ if (xIsNum !== yIsNum) return xIsNum ? -1 : 1;
3807
+ if (xIsNum && yIsNum) {
3808
+ if (x5 !== y7) return x5 < y7 ? -1 : 1;
3809
+ } else {
3810
+ const d6 = compareTextSegments(x5, y7);
3811
+ if (d6 !== 0) return d6;
3812
+ }
3813
+ }
3814
+ if (ap.length !== bp.length) return ap.length - bp.length;
3815
+ return errorCodeRegistryIndex(a4.code) - errorCodeRegistryIndex(b5.code);
3816
+ }
3817
+ function sortIssues(issues) {
3818
+ return [...issues].sort(compareIssues);
3464
3819
  }
3465
3820
  function valueAtPath(root, path) {
3466
3821
  let cur = root;
@@ -3481,6 +3836,6 @@ function valueAtPath(root, path) {
3481
3836
  (*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) *)
3482
3837
  */
3483
3838
 
3484
- export { Argon2idParamsSchema, ChunkedBytesArraySchema, ERROR_CODES, EXTENSION_KEY_COMPANION_RE, EXTENSION_KEY_VENDOR_RE, EncryptionEnvelopeSchema, HashDigestSchema, HashesMapSchema, ItemEntrySchema, MerkleCommitSchema, PassphraseBlockSchema, PoeRecordSchema, SEVERITY, STRUCTURAL_ERROR_CODES, SigEntrySchema, SlotSchema, SupersedesSchema, TOP_LEVEL_BASE_KEYS, UriChunkArraySchema, VERIFIER_ERROR_CODES, VersionLiteralSchema, bytesChunkArrayConcat, chunkBytes, chunkUri, encodePoeRecord, encodeRecordBodyForSigning, isExtensionKey, reconstructChunkedUri, severityOf, validateCidProfile, validatePoeRecord };
3839
+ export { Argon2idParamsSchema, CARRIAGE_ERROR_CODES, DEFAULT_PASSPHRASE_PARAMS_CEILING, DUAL_SEVERITY_CODES, ERROR_CODES, ERROR_CODE_PART, EXTENSION_KEY_COMPANION_RE, EXTENSION_KEY_VENDOR_RE, EncOpaqueSchema, EncScheme1Schema, EncryptionEnvelopeSchema, HashDigestSchema, HashesMapSchema, ItemEntrySchema, MerkleCommitSchema, PassphraseBlockSchema, PoeRecordSchema, SEVERITY, STRUCTURAL_ERROR_CODES, SigEntrySchema, SlotSchema, SupersedesSchema, TOP_LEVEL_BASE_KEYS, TRANSPORT_CHUNK_MAX_BYTES, UriSchema, VERIFIER_ERROR_CODES, VersionLiteralSchema, chunkRecordBody, encodeLabel309Value, encodePoeRecord, encodeRecordBodyForSigning, errorCodeRegistryIndex, isExtensionKey, reassembleLabel309Value, severityOf, validateCidProfile, validatePoeRecord };
3485
3840
  //# sourceMappingURL=index.js.map
3486
3841
  //# sourceMappingURL=index.js.map