@cardanowall/poe-standard 0.3.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/carriage.cjs +1972 -0
- package/dist/carriage.cjs.map +1 -0
- package/dist/carriage.d.cts +55 -0
- package/dist/carriage.d.ts +55 -0
- package/dist/carriage.js +1967 -0
- package/dist/carriage.js.map +1 -0
- package/dist/encoder.cjs +29 -77
- package/dist/encoder.cjs.map +1 -1
- package/dist/encoder.d.cts +10 -0
- package/dist/encoder.d.ts +10 -0
- package/dist/encoder.js +29 -77
- package/dist/encoder.js.map +1 -1
- package/dist/error-codes.cjs +129 -37
- package/dist/error-codes.cjs.map +1 -1
- package/dist/error-codes.d.cts +11 -6
- package/dist/error-codes.d.ts +11 -6
- package/dist/error-codes.js +126 -38
- package/dist/error-codes.js.map +1 -1
- package/dist/index.cjs +1085 -838
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -58
- package/dist/index.d.ts +4 -58
- package/dist/index.js +1074 -833
- package/dist/index.js.map +1 -1
- package/dist/schema.cjs +101 -87
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.d.cts +69 -47
- package/dist/schema.d.ts +69 -47
- package/dist/schema.js +99 -86
- package/dist/schema.js.map +1 -1
- package/dist/validator.cjs +927 -577
- package/dist/validator.cjs.map +1 -1
- package/dist/validator.d.cts +48 -7
- package/dist/validator.d.ts +48 -7
- package/dist/validator.js +927 -578
- package/dist/validator.js.map +1 -1
- package/package.json +8 -2
package/dist/index.js
CHANGED
|
@@ -1,95 +1,108 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
3
|
// src/schema.ts
|
|
4
|
-
|
|
5
|
-
z.
|
|
6
|
-
|
|
7
|
-
})
|
|
8
|
-
|
|
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
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
var
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}).
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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 =
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
|
102
|
-
var EXTENSION_KEY_COMPANION_RE = /^[a-z]
|
|
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(
|
|
1769
|
-
}
|
|
1770
|
-
function encodeRecordBodyForSigning(record) {
|
|
1771
|
-
const body = recordToCborInternal(
|
|
1781
|
+
return encodeCanonicalCbor(toCborValue(
|
|
1772
1782
|
record,
|
|
1773
1783
|
/* includeSigs */
|
|
1774
|
-
|
|
1775
|
-
);
|
|
1776
|
-
return encodeCanonicalCbor(body);
|
|
1784
|
+
true
|
|
1785
|
+
));
|
|
1777
1786
|
}
|
|
1778
|
-
function
|
|
1779
|
-
return
|
|
1787
|
+
function encodeRecordBodyForSigning(record) {
|
|
1788
|
+
return encodeCanonicalCbor(toCborValue(
|
|
1780
1789
|
record,
|
|
1781
1790
|
/* includeSigs */
|
|
1782
|
-
|
|
1783
|
-
);
|
|
1791
|
+
false
|
|
1792
|
+
));
|
|
1784
1793
|
}
|
|
1785
|
-
function
|
|
1786
|
-
const out = {
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
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
|
|
1801
|
-
|
|
1802
|
-
|
|
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 (
|
|
1806
|
-
|
|
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 [
|
|
1813
|
-
|
|
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
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
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
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
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
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
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");
|
|
2091
|
+
}
|
|
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)));
|
|
1843
2095
|
}
|
|
1844
|
-
return
|
|
2096
|
+
return chunks;
|
|
1845
2097
|
}
|
|
1846
|
-
function
|
|
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
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
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
|
-
|
|
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,25 +2669,54 @@ function decodeCoseSign1(bytes) {
|
|
|
2387
2669
|
signature: signatureRaw
|
|
2388
2670
|
};
|
|
2389
2671
|
}
|
|
2390
|
-
|
|
2391
|
-
|
|
2672
|
+
var CARDANO_POE_ITEM_HASHES_PREFIX = new TextEncoder().encode(
|
|
2673
|
+
"cardano-poe-item-hashes-v1"
|
|
2674
|
+
);
|
|
2392
2675
|
var CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX = new TextEncoder().encode(
|
|
2393
2676
|
"cardano-poe-slots-transcript-v1"
|
|
2394
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
|
+
);
|
|
2395
2687
|
var CARDANO_POE_HKDF_INFO_PAYLOAD = new TextEncoder().encode(
|
|
2396
2688
|
"cardano-poe-payload-v1"
|
|
2397
2689
|
);
|
|
2398
2690
|
var CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE = new TextEncoder().encode(
|
|
2399
2691
|
"cardano-poe-payload-passphrase-v1"
|
|
2400
2692
|
);
|
|
2693
|
+
var CARDANO_POE_X25519_KEK_SALT_PREFIX = new TextEncoder().encode(
|
|
2694
|
+
"cardano-poe-x25519-kek-salt-v1"
|
|
2695
|
+
);
|
|
2401
2696
|
var CARDANO_POE_XWING_KEK_SALT_PREFIX = new TextEncoder().encode(
|
|
2402
2697
|
"cardano-poe-xwing-kek-salt-v1"
|
|
2403
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)");
|
|
2701
|
+
}
|
|
2404
2702
|
if (CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length !== 31) {
|
|
2405
2703
|
throw new Error(
|
|
2406
2704
|
"CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX byte-length invariant violated (expected 31)"
|
|
2407
2705
|
);
|
|
2408
2706
|
}
|
|
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
|
+
);
|
|
2711
|
+
}
|
|
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)");
|
|
2714
|
+
}
|
|
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
|
+
}
|
|
2409
2720
|
if (CARDANO_POE_HKDF_INFO_PAYLOAD.length !== 22) {
|
|
2410
2721
|
throw new Error("CARDANO_POE_HKDF_INFO_PAYLOAD byte-length invariant violated (expected 22)");
|
|
2411
2722
|
}
|
|
@@ -2414,6 +2725,11 @@ if (CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE.length !== 33) {
|
|
|
2414
2725
|
"CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE byte-length invariant violated (expected 33)"
|
|
2415
2726
|
);
|
|
2416
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
|
+
}
|
|
2417
2733
|
if (CARDANO_POE_XWING_KEK_SALT_PREFIX.length !== 29) {
|
|
2418
2734
|
throw new Error("CARDANO_POE_XWING_KEK_SALT_PREFIX byte-length invariant violated (expected 29)");
|
|
2419
2735
|
}
|
|
@@ -2423,9 +2739,6 @@ var CARDANO_POE_HKDF_INFO_KEK = new TextEncoder().encode("cardano-poe-kek-v1");
|
|
|
2423
2739
|
var CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 = new TextEncoder().encode(
|
|
2424
2740
|
"cardano-poe-kek-mlkem768x25519-v1"
|
|
2425
2741
|
);
|
|
2426
|
-
var CARDANO_POE_HKDF_INFO_SLOTS_MAC = new TextEncoder().encode(
|
|
2427
|
-
"cardano-poe-slots-mac-v1"
|
|
2428
|
-
);
|
|
2429
2742
|
var ZERO_NONCE_12 = new Uint8Array(12);
|
|
2430
2743
|
if (CARDANO_POE_HKDF_INFO_KEK.length !== 18) {
|
|
2431
2744
|
throw new Error("CARDANO_POE_HKDF_INFO_KEK byte-length invariant violated (expected 18)");
|
|
@@ -2435,235 +2748,10 @@ if (CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519.length !== 33) {
|
|
|
2435
2748
|
"CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 byte-length invariant violated (expected 33)"
|
|
2436
2749
|
);
|
|
2437
2750
|
}
|
|
2438
|
-
if (CARDANO_POE_HKDF_INFO_SLOTS_MAC.length !== 24) {
|
|
2439
|
-
throw new Error("CARDANO_POE_HKDF_INFO_SLOTS_MAC byte-length invariant violated (expected 24)");
|
|
2440
|
-
}
|
|
2441
2751
|
if (ZERO_NONCE_12.length !== 12) {
|
|
2442
2752
|
throw new Error("ZERO_NONCE_12 byte-length invariant violated (expected 12)");
|
|
2443
2753
|
}
|
|
2444
|
-
|
|
2445
|
-
// src/chunked.ts
|
|
2446
|
-
var CHUNK_MAX_BYTES = 64;
|
|
2447
|
-
var UTF8_ENCODER2 = new TextEncoder();
|
|
2448
|
-
function chunkBytes(value) {
|
|
2449
|
-
if (value.length === 0) return [new Uint8Array(0)];
|
|
2450
|
-
const chunks = [];
|
|
2451
|
-
for (let i = 0; i < value.length; i += CHUNK_MAX_BYTES) {
|
|
2452
|
-
chunks.push(value.subarray(i, Math.min(i + CHUNK_MAX_BYTES, value.length)));
|
|
2453
|
-
}
|
|
2454
|
-
return chunks;
|
|
2455
|
-
}
|
|
2456
|
-
function bytesChunkArrayConcat(chunks) {
|
|
2457
|
-
let total = 0;
|
|
2458
|
-
for (const c5 of chunks) total += c5.length;
|
|
2459
|
-
const out = new Uint8Array(total);
|
|
2460
|
-
let offset = 0;
|
|
2461
|
-
for (const c5 of chunks) {
|
|
2462
|
-
out.set(c5, offset);
|
|
2463
|
-
offset += c5.length;
|
|
2464
|
-
}
|
|
2465
|
-
return out;
|
|
2466
|
-
}
|
|
2467
|
-
function reconstructChunkedUri(chunks) {
|
|
2468
|
-
const merged = bytesChunkArrayConcat(chunks.map((c5) => UTF8_ENCODER2.encode(c5)));
|
|
2469
|
-
try {
|
|
2470
|
-
const uri = new TextDecoder("utf-8", { fatal: true }).decode(merged);
|
|
2471
|
-
return { ok: true, uri };
|
|
2472
|
-
} catch (cause) {
|
|
2473
|
-
return {
|
|
2474
|
-
ok: false,
|
|
2475
|
-
code: "INVALID_URI",
|
|
2476
|
-
reason: cause instanceof Error ? cause.message : String(cause)
|
|
2477
|
-
};
|
|
2478
|
-
}
|
|
2479
|
-
}
|
|
2480
|
-
function chunkUri(uri) {
|
|
2481
|
-
const bytes = UTF8_ENCODER2.encode(uri);
|
|
2482
|
-
if (bytes.length === 0) return [""];
|
|
2483
|
-
if (bytes.length <= CHUNK_MAX_BYTES) return [uri];
|
|
2484
|
-
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
2485
|
-
const chunks = [];
|
|
2486
|
-
let cursor = 0;
|
|
2487
|
-
while (cursor < bytes.length) {
|
|
2488
|
-
let end = Math.min(cursor + CHUNK_MAX_BYTES, bytes.length);
|
|
2489
|
-
while (end < bytes.length && (bytes[end] & 192) === 128) end--;
|
|
2490
|
-
chunks.push(decoder.decode(bytes.subarray(cursor, end)));
|
|
2491
|
-
cursor = end;
|
|
2492
|
-
}
|
|
2493
|
-
return chunks;
|
|
2494
|
-
}
|
|
2495
|
-
|
|
2496
|
-
// src/error-codes.ts
|
|
2497
|
-
var STRUCTURAL_ERROR_CODES = [
|
|
2498
|
-
// CBOR decode layer. A single code covers every canonical-decode failure —
|
|
2499
|
-
// malformed/truncated bytes, indefinite-length encodings, non-canonical
|
|
2500
|
-
// (unsorted) map-key ordering, duplicate map keys, non-minimal integers, and
|
|
2501
|
-
// invalid UTF-8 — by design (no separate duplicate-key code).
|
|
2502
|
-
"MALFORMED_CBOR",
|
|
2503
|
-
// Generic schema-layer
|
|
2504
|
-
"SCHEMA_TYPE_MISMATCH",
|
|
2505
|
-
"SCHEMA_MISSING_REQUIRED",
|
|
2506
|
-
"SCHEMA_UNKNOWN_FIELD",
|
|
2507
|
-
"SCHEMA_INVALID_LITERAL",
|
|
2508
|
-
"SCHEMA_EMPTY_RECORD",
|
|
2509
|
-
// Hash-map
|
|
2510
|
-
"HASH_DIGEST_LENGTH_MISMATCH",
|
|
2511
|
-
"UNSUPPORTED_HASH_ALG",
|
|
2512
|
-
// Top-level `merkle[]`
|
|
2513
|
-
"UNSUPPORTED_MERKLE_COMMIT_ALG",
|
|
2514
|
-
// URI / chunking. A chunk whose bytes do not reconstruct to valid UTF-8
|
|
2515
|
-
// surfaces as MALFORMED_CBOR at decode (cbor2 rejects invalid-UTF-8 tstr)
|
|
2516
|
-
// or, in the residual reconstruct guard, as INVALID_URI — there is no
|
|
2517
|
-
// separate codepoint-split code.
|
|
2518
|
-
"INVALID_URI",
|
|
2519
|
-
"CHUNK_TOO_LARGE",
|
|
2520
|
-
// Encryption envelope
|
|
2521
|
-
"UNAUTHENTICATED_CIPHER_FORBIDDEN",
|
|
2522
|
-
"UNSUPPORTED_AEAD_ALG",
|
|
2523
|
-
"NONCE_LENGTH_MISMATCH",
|
|
2524
|
-
"UNSUPPORTED_ENVELOPE_SCHEME",
|
|
2525
|
-
"ENC_SLOTS_EMPTY",
|
|
2526
|
-
"ENC_SLOT_INVALID_SHAPE",
|
|
2527
|
-
"ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
|
|
2528
|
-
"ENC_SLOTS_TOO_MANY",
|
|
2529
|
-
"ENC_ENVELOPE_TOO_LARGE",
|
|
2530
|
-
"UNSUPPORTED_KEM_ALG",
|
|
2531
|
-
"ENC_KEM_REQUIRED",
|
|
2532
|
-
"KEM_EPK_LENGTH_MISMATCH",
|
|
2533
|
-
"KEM_CT_LENGTH_MISMATCH",
|
|
2534
|
-
"WRAP_LENGTH_MISMATCH",
|
|
2535
|
-
"ENC_SLOTS_MAC_INVALID_LENGTH",
|
|
2536
|
-
"ENC_SLOTS_MAC_REQUIRED",
|
|
2537
|
-
"ENC_SLOTS_REQUIRED",
|
|
2538
|
-
"ENC_EXCLUSIVITY_VIOLATION",
|
|
2539
|
-
"ENC_NO_KEY_PATH",
|
|
2540
|
-
"ENC_REQUIRES_CONTENT_HASH",
|
|
2541
|
-
"ENC_PASSPHRASE_ALG_UNSUPPORTED",
|
|
2542
|
-
"ENC_PASSPHRASE_SALT_TOO_SHORT",
|
|
2543
|
-
"ENC_PASSPHRASE_SALT_TOO_LONG",
|
|
2544
|
-
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
2545
|
-
"ENC_PASSPHRASE_PARAMS_EXCEED_POLICY",
|
|
2546
|
-
// Signatures
|
|
2547
|
-
"MALFORMED_SIG_COSE_SIGN1",
|
|
2548
|
-
"SIGNATURE_UNSUPPORTED",
|
|
2549
|
-
"SIG_ENTRY_INVALID_SHAPE",
|
|
2550
|
-
"SIG_ENTRY_KID_COSE_KEY_CONFLICT",
|
|
2551
|
-
"SIG_PRIVATE_KEY_LEAKED",
|
|
2552
|
-
// Supersedence
|
|
2553
|
-
"SUPERSEDES_TX_INVALID_LENGTH",
|
|
2554
|
-
// Forward-compat critical extensions
|
|
2555
|
-
"EXTENSION_UNSUPPORTED_CRITICAL",
|
|
2556
|
-
"CRIT_SHAPE_INVALID"
|
|
2557
|
-
];
|
|
2558
|
-
var VERIFIER_ERROR_CODES = [
|
|
2559
|
-
"METADATA_NOT_FOUND",
|
|
2560
|
-
"INSUFFICIENT_CONFIRMATIONS",
|
|
2561
|
-
"SIGNATURE_INVALID",
|
|
2562
|
-
"SIGNER_KEY_UNRESOLVED",
|
|
2563
|
-
"WALLET_ADDRESS_MISMATCH",
|
|
2564
|
-
"URI_TARGET_FORBIDDEN",
|
|
2565
|
-
"URI_INTEGRITY_MISMATCH",
|
|
2566
|
-
"URI_FETCH_FAILED",
|
|
2567
|
-
"CONTENT_UNAVAILABLE",
|
|
2568
|
-
"CIPHERTEXT_UNAVAILABLE",
|
|
2569
|
-
"PROVIDER_UNAVAILABLE",
|
|
2570
|
-
"SERVICE_INDEPENDENCE_VIOLATION",
|
|
2571
|
-
"WRONG_DECRYPTION_INPUT_SHAPE",
|
|
2572
|
-
"WRONG_RECIPIENT_KEY",
|
|
2573
|
-
"TAMPERED_HEADER",
|
|
2574
|
-
"TAMPERED_CIPHERTEXT",
|
|
2575
|
-
"KDF_DERIVATION_FAILED",
|
|
2576
|
-
"SCHEMA_MERKLE_LEAF_COUNT_MISMATCH",
|
|
2577
|
-
"SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED",
|
|
2578
|
-
"SCHEMA_MERKLE_LEAVES_MALFORMED",
|
|
2579
|
-
"MERKLE_ROOT_MISMATCH",
|
|
2580
|
-
"MERKLE_LEAVES_UNAVAILABLE",
|
|
2581
|
-
"MERKLE_LEAVES_INFORMATIVE_FORM",
|
|
2582
|
-
"MERKLE_UNSUPPORTED",
|
|
2583
|
-
"OUT_OF_PROFILE_SKIPPED"
|
|
2584
|
-
];
|
|
2585
|
-
var ERROR_CODES = [...STRUCTURAL_ERROR_CODES, ...VERIFIER_ERROR_CODES];
|
|
2586
|
-
var SEVERITY = Object.freeze({
|
|
2587
|
-
// --- Part A ---
|
|
2588
|
-
MALFORMED_CBOR: "error",
|
|
2589
|
-
SCHEMA_TYPE_MISMATCH: "error",
|
|
2590
|
-
SCHEMA_MISSING_REQUIRED: "error",
|
|
2591
|
-
SCHEMA_UNKNOWN_FIELD: "error",
|
|
2592
|
-
SCHEMA_INVALID_LITERAL: "error",
|
|
2593
|
-
SCHEMA_EMPTY_RECORD: "error",
|
|
2594
|
-
HASH_DIGEST_LENGTH_MISMATCH: "error",
|
|
2595
|
-
UNSUPPORTED_HASH_ALG: "error",
|
|
2596
|
-
UNSUPPORTED_MERKLE_COMMIT_ALG: "error",
|
|
2597
|
-
INVALID_URI: "error",
|
|
2598
|
-
CHUNK_TOO_LARGE: "error",
|
|
2599
|
-
UNAUTHENTICATED_CIPHER_FORBIDDEN: "error",
|
|
2600
|
-
UNSUPPORTED_AEAD_ALG: "error",
|
|
2601
|
-
NONCE_LENGTH_MISMATCH: "error",
|
|
2602
|
-
UNSUPPORTED_ENVELOPE_SCHEME: "error",
|
|
2603
|
-
ENC_SLOTS_EMPTY: "error",
|
|
2604
|
-
ENC_SLOT_INVALID_SHAPE: "error",
|
|
2605
|
-
ENC_SLOTS_DUPLICATE_KEM_MATERIAL: "error",
|
|
2606
|
-
ENC_SLOTS_TOO_MANY: "error",
|
|
2607
|
-
ENC_ENVELOPE_TOO_LARGE: "error",
|
|
2608
|
-
UNSUPPORTED_KEM_ALG: "error",
|
|
2609
|
-
ENC_KEM_REQUIRED: "error",
|
|
2610
|
-
KEM_EPK_LENGTH_MISMATCH: "error",
|
|
2611
|
-
KEM_CT_LENGTH_MISMATCH: "error",
|
|
2612
|
-
WRAP_LENGTH_MISMATCH: "error",
|
|
2613
|
-
ENC_SLOTS_MAC_INVALID_LENGTH: "error",
|
|
2614
|
-
ENC_SLOTS_MAC_REQUIRED: "error",
|
|
2615
|
-
ENC_SLOTS_REQUIRED: "error",
|
|
2616
|
-
ENC_EXCLUSIVITY_VIOLATION: "error",
|
|
2617
|
-
ENC_NO_KEY_PATH: "error",
|
|
2618
|
-
ENC_REQUIRES_CONTENT_HASH: "error",
|
|
2619
|
-
ENC_PASSPHRASE_ALG_UNSUPPORTED: "error",
|
|
2620
|
-
ENC_PASSPHRASE_SALT_TOO_SHORT: "error",
|
|
2621
|
-
ENC_PASSPHRASE_SALT_TOO_LONG: "error",
|
|
2622
|
-
ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW: "error",
|
|
2623
|
-
ENC_PASSPHRASE_PARAMS_EXCEED_POLICY: "error",
|
|
2624
|
-
MALFORMED_SIG_COSE_SIGN1: "error",
|
|
2625
|
-
SIGNATURE_UNSUPPORTED: "info",
|
|
2626
|
-
SIG_ENTRY_INVALID_SHAPE: "error",
|
|
2627
|
-
SIG_ENTRY_KID_COSE_KEY_CONFLICT: "error",
|
|
2628
|
-
SIG_PRIVATE_KEY_LEAKED: "error",
|
|
2629
|
-
SUPERSEDES_TX_INVALID_LENGTH: "error",
|
|
2630
|
-
EXTENSION_UNSUPPORTED_CRITICAL: "error",
|
|
2631
|
-
CRIT_SHAPE_INVALID: "error",
|
|
2632
|
-
// --- Part B ---
|
|
2633
|
-
METADATA_NOT_FOUND: "error",
|
|
2634
|
-
INSUFFICIENT_CONFIRMATIONS: "info",
|
|
2635
|
-
SIGNATURE_INVALID: "error",
|
|
2636
|
-
SIGNER_KEY_UNRESOLVED: "error",
|
|
2637
|
-
WALLET_ADDRESS_MISMATCH: "error",
|
|
2638
|
-
URI_TARGET_FORBIDDEN: "error",
|
|
2639
|
-
URI_INTEGRITY_MISMATCH: "error",
|
|
2640
|
-
URI_FETCH_FAILED: "warning",
|
|
2641
|
-
CONTENT_UNAVAILABLE: "error",
|
|
2642
|
-
CIPHERTEXT_UNAVAILABLE: "error",
|
|
2643
|
-
PROVIDER_UNAVAILABLE: "error",
|
|
2644
|
-
SERVICE_INDEPENDENCE_VIOLATION: "error",
|
|
2645
|
-
WRONG_DECRYPTION_INPUT_SHAPE: "error",
|
|
2646
|
-
WRONG_RECIPIENT_KEY: "error",
|
|
2647
|
-
TAMPERED_HEADER: "error",
|
|
2648
|
-
TAMPERED_CIPHERTEXT: "error",
|
|
2649
|
-
KDF_DERIVATION_FAILED: "error",
|
|
2650
|
-
SCHEMA_MERKLE_LEAF_COUNT_MISMATCH: "error",
|
|
2651
|
-
SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED: "error",
|
|
2652
|
-
SCHEMA_MERKLE_LEAVES_MALFORMED: "error",
|
|
2653
|
-
MERKLE_ROOT_MISMATCH: "error",
|
|
2654
|
-
MERKLE_LEAVES_UNAVAILABLE: "warning",
|
|
2655
|
-
MERKLE_LEAVES_INFORMATIVE_FORM: "info",
|
|
2656
|
-
// Dual-severity — default reading is `info`; the verifier promotes to
|
|
2657
|
-
// `error` for merkle-only records (no `items[]` content claim was
|
|
2658
|
-
// validated in the same record).
|
|
2659
|
-
MERKLE_UNSUPPORTED: "info",
|
|
2660
|
-
// Dual-severity — default reading is `info` (render mode); strict
|
|
2661
|
-
// end-to-end verifiers promote to `error`.
|
|
2662
|
-
OUT_OF_PROFILE_SKIPPED: "info"
|
|
2663
|
-
});
|
|
2664
|
-
function severityOf(code) {
|
|
2665
|
-
return SEVERITY[code];
|
|
2666
|
-
}
|
|
2754
|
+
new TextEncoder();
|
|
2667
2755
|
|
|
2668
2756
|
// src/validator.ts
|
|
2669
2757
|
var HASH_ALG_LENGTHS = {
|
|
@@ -2674,9 +2762,9 @@ var MERKLE_COMMIT_ALG_LENGTHS = {
|
|
|
2674
2762
|
"rfc9162-sha256": 32
|
|
2675
2763
|
};
|
|
2676
2764
|
var AEAD_NONCE_LENGTHS = {
|
|
2677
|
-
"
|
|
2765
|
+
"chacha20-poly1305-stream64k": 24
|
|
2678
2766
|
};
|
|
2679
|
-
var UNAUTHENTICATED_CIPHER_RE = /(?:^|[-_])(?:cbc|ctr|ecb|cfb|ofb)(?:[-_]
|
|
2767
|
+
var UNAUTHENTICATED_CIPHER_RE = /(?:^|[-_])(?:cbc|ctr|ecb|cfb|ofb)(?:[-_]|$)|^(?:rc4|des|3des)(?:[-_]|$)/i;
|
|
2680
2768
|
var KEM_SLOT_DESCRIPTORS = {
|
|
2681
2769
|
x25519: { field: "epk", fieldLength: 32, wrapLength: 48 },
|
|
2682
2770
|
mlkem768x25519: { field: "kem_ct", fieldLength: 1120, wrapLength: 48 }
|
|
@@ -2685,17 +2773,33 @@ var KEM_FIELD_LENGTH_CODE = {
|
|
|
2685
2773
|
epk: "KEM_EPK_LENGTH_MISMATCH",
|
|
2686
2774
|
kem_ct: "KEM_CT_LENGTH_MISMATCH"
|
|
2687
2775
|
};
|
|
2688
|
-
var NONCE_LENGTH = 24;
|
|
2689
|
-
var SLOTS_MAC_LENGTH = 32;
|
|
2690
2776
|
var PASSPHRASE_KDF_ALGS = /* @__PURE__ */ new Set(["argon2id"]);
|
|
2691
2777
|
var KNOWN_SIG_ALG_IDS = /* @__PURE__ */ new Set([-8, -19]);
|
|
2692
|
-
|
|
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);
|
|
2693
2797
|
let decoded;
|
|
2694
2798
|
try {
|
|
2695
2799
|
decoded = decodeCanonicalCbor(bytes);
|
|
2696
2800
|
} catch (cause) {
|
|
2697
2801
|
return {
|
|
2698
|
-
|
|
2802
|
+
valid: false,
|
|
2699
2803
|
issues: [
|
|
2700
2804
|
{
|
|
2701
2805
|
code: "MALFORMED_CBOR",
|
|
@@ -2706,132 +2810,181 @@ function validatePoeRecord(bytes) {
|
|
|
2706
2810
|
]
|
|
2707
2811
|
};
|
|
2708
2812
|
}
|
|
2813
|
+
const nonTextKeyIssues = collectNonTextKeyMapIssues(decoded);
|
|
2814
|
+
if (nonTextKeyIssues.length > 0) {
|
|
2815
|
+
return { valid: false, issues: sortIssues(nonTextKeyIssues) };
|
|
2816
|
+
}
|
|
2709
2817
|
const parse = PoeRecordSchema.safeParse(decoded);
|
|
2710
2818
|
if (!parse.success) {
|
|
2711
|
-
|
|
2712
|
-
return { ok: false, issues };
|
|
2819
|
+
return { valid: false, issues: sortIssues(mapZodIssues(parse.error.issues, decoded)) };
|
|
2713
2820
|
}
|
|
2714
2821
|
const record = parse.data;
|
|
2715
|
-
const
|
|
2716
|
-
|
|
2717
|
-
const info = [];
|
|
2718
|
-
const itemsLen = Array.isArray(record.items) ? record.items.length : 0;
|
|
2719
|
-
const merkleLen = Array.isArray(record.merkle) ? record.merkle.length : 0;
|
|
2720
|
-
if (itemsLen === 0 && merkleLen === 0) {
|
|
2721
|
-
errors.push(
|
|
2722
|
-
issue(
|
|
2723
|
-
"SCHEMA_EMPTY_RECORD",
|
|
2724
|
-
[],
|
|
2725
|
-
"record must carry at least one of items[] or merkle[] non-empty"
|
|
2726
|
-
)
|
|
2727
|
-
);
|
|
2728
|
-
}
|
|
2822
|
+
const issues = [];
|
|
2823
|
+
checkContentCommitmentPresence(record, issues);
|
|
2729
2824
|
const decodedTopKeys = topLevelKeysOf(decoded);
|
|
2730
|
-
|
|
2731
|
-
for (const
|
|
2732
|
-
if (TOP_LEVEL_BASE_KEYS.has(
|
|
2733
|
-
if (isExtensionKey(
|
|
2734
|
-
|
|
2735
|
-
}
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
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")
|
|
2746
2846
|
);
|
|
2747
2847
|
}
|
|
2848
|
+
for (let i = 0; i < record.sigs.length; i++) {
|
|
2849
|
+
checkSigEntry(record.sigs[i], i, issues);
|
|
2850
|
+
}
|
|
2748
2851
|
}
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
if (item.uris) checkItemUris(item.uris, ["items", i, "uris"], errors);
|
|
2753
|
-
if (item.enc !== void 0) checkItemEnc(item, i, errors);
|
|
2754
|
-
}
|
|
2755
|
-
for (let i = 0; i < (record.merkle ?? []).length; i++) {
|
|
2756
|
-
const commit = record.merkle[i];
|
|
2757
|
-
checkMerkleCommit(commit, i, errors);
|
|
2852
|
+
const sorted = sortIssues(issues);
|
|
2853
|
+
if (sorted.some((issue) => issue.severity === "error")) {
|
|
2854
|
+
return { valid: false, issues: sorted };
|
|
2758
2855
|
}
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
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;
|
|
2762
2873
|
}
|
|
2874
|
+
out.push(mapZodIssue(zissue, decodedRoot));
|
|
2763
2875
|
}
|
|
2764
|
-
|
|
2765
|
-
|
|
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";
|
|
2766
2881
|
}
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
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
|
+
);
|
|
2770
2894
|
};
|
|
2771
|
-
if (
|
|
2772
|
-
|
|
2773
|
-
|
|
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;
|
|
2774
2916
|
}
|
|
2775
|
-
function mapZodIssue(zissue,
|
|
2917
|
+
function mapZodIssue(zissue, decodedRoot) {
|
|
2776
2918
|
const path = zissue.path;
|
|
2777
2919
|
const explicit = zissue.params?.code;
|
|
2778
2920
|
if (explicit !== void 0) {
|
|
2779
|
-
return
|
|
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
|
+
);
|
|
2780
2930
|
}
|
|
2781
2931
|
const inSigsEntry = path.length >= 2 && path[0] === "sigs" && typeof path[1] === "number";
|
|
2782
|
-
const isInSlotEntry =
|
|
2783
|
-
if (path.length >= 5 && path[0] === "items" && typeof path[1] === "number" && path[2] === "enc" && path[3] === "slots" && typeof path[4] === "number") {
|
|
2784
|
-
return true;
|
|
2785
|
-
}
|
|
2786
|
-
if (path.length >= 2 && path[0] === "slots" && typeof path[1] === "number") {
|
|
2787
|
-
return true;
|
|
2788
|
-
}
|
|
2789
|
-
return false;
|
|
2790
|
-
})();
|
|
2791
|
-
const valueAtIssue = valueAtPath(decoded, path);
|
|
2932
|
+
const isInSlotEntry = path.length >= 2 && path[0] === "slots" && typeof path[1] === "number";
|
|
2792
2933
|
const isMissing = valueAtIssue === void 0;
|
|
2793
2934
|
switch (zissue.code) {
|
|
2794
2935
|
case "invalid_type":
|
|
2795
|
-
if (isInSlotEntry) return
|
|
2936
|
+
if (isInSlotEntry) return issueOf("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
|
|
2796
2937
|
if (isMissing) {
|
|
2797
|
-
if (inSigsEntry) return
|
|
2798
|
-
return
|
|
2938
|
+
if (inSigsEntry) return issueOf("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
2939
|
+
return issueOf("SCHEMA_MISSING_REQUIRED", path, zissue.message);
|
|
2799
2940
|
}
|
|
2800
|
-
if (inSigsEntry) return
|
|
2801
|
-
return
|
|
2941
|
+
if (inSigsEntry) return issueOf("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
2942
|
+
return issueOf("SCHEMA_TYPE_MISMATCH", path, zissue.message);
|
|
2802
2943
|
case "invalid_value":
|
|
2803
|
-
if (
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
path,
|
|
2807
|
-
zissue.message
|
|
2808
|
-
);
|
|
2809
|
-
}
|
|
2810
|
-
return issue("SCHEMA_INVALID_LITERAL", path, zissue.message);
|
|
2811
|
-
case "unrecognized_keys":
|
|
2812
|
-
if (isInSlotEntry) return issue("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
|
|
2813
|
-
if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
2814
|
-
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":
|
|
2815
2947
|
case "invalid_format":
|
|
2816
2948
|
case "too_big":
|
|
2817
2949
|
case "too_small":
|
|
2818
|
-
if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
2819
|
-
return issue("SCHEMA_TYPE_MISMATCH", path, zissue.message);
|
|
2820
|
-
case "invalid_union":
|
|
2821
2950
|
case "invalid_key":
|
|
2822
2951
|
case "invalid_element":
|
|
2823
2952
|
case "custom":
|
|
2824
2953
|
default:
|
|
2825
|
-
if (isInSlotEntry) return
|
|
2826
|
-
if (inSigsEntry) return
|
|
2827
|
-
return
|
|
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);
|
|
2828
2957
|
}
|
|
2829
2958
|
}
|
|
2830
|
-
function
|
|
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
|
+
);
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
function checkItemHashes(item, idx, issues) {
|
|
2831
2984
|
const entries = Object.entries(item.hashes);
|
|
2832
2985
|
if (entries.length === 0) {
|
|
2833
|
-
|
|
2834
|
-
|
|
2986
|
+
issues.push(
|
|
2987
|
+
issueOf(
|
|
2835
2988
|
"SCHEMA_TYPE_MISMATCH",
|
|
2836
2989
|
["items", idx, "hashes"],
|
|
2837
2990
|
"hashes must be a non-empty CBOR map of <alg-id> -> <digest>"
|
|
@@ -2841,15 +2994,15 @@ function checkItemHashes(item, idx, errors) {
|
|
|
2841
2994
|
}
|
|
2842
2995
|
for (const [alg, digest] of entries) {
|
|
2843
2996
|
if (!(alg in HASH_ALG_LENGTHS)) {
|
|
2844
|
-
|
|
2845
|
-
|
|
2997
|
+
issues.push(
|
|
2998
|
+
issueOf("UNSUPPORTED_HASH_ALG", ["items", idx, "hashes", alg], `unknown hash alg: ${alg}`)
|
|
2846
2999
|
);
|
|
2847
3000
|
continue;
|
|
2848
3001
|
}
|
|
2849
3002
|
const expected = HASH_ALG_LENGTHS[alg];
|
|
2850
3003
|
if (digest.length !== expected) {
|
|
2851
|
-
|
|
2852
|
-
|
|
3004
|
+
issues.push(
|
|
3005
|
+
issueOf(
|
|
2853
3006
|
"HASH_DIGEST_LENGTH_MISMATCH",
|
|
2854
3007
|
["items", idx, "hashes", alg],
|
|
2855
3008
|
`hashes['${alg}'] digest length ${digest.length} != ${expected}`
|
|
@@ -2858,35 +3011,33 @@ function checkItemHashes(item, idx, errors) {
|
|
|
2858
3011
|
}
|
|
2859
3012
|
}
|
|
2860
3013
|
}
|
|
2861
|
-
function
|
|
2862
|
-
uris.
|
|
2863
|
-
|
|
2864
|
-
function validateOneUri(chunks, path, errors) {
|
|
2865
|
-
const reconstructed = reconstructChunkedUri(chunks);
|
|
2866
|
-
if (!reconstructed.ok) {
|
|
2867
|
-
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"));
|
|
2868
3017
|
return;
|
|
2869
3018
|
}
|
|
2870
|
-
|
|
3019
|
+
uris.forEach((uri, ui) => checkOneUri(uri, [...basePath, ui], issues));
|
|
3020
|
+
}
|
|
3021
|
+
function checkOneUri(uri, path, issues) {
|
|
2871
3022
|
if (uri.includes("#")) {
|
|
2872
|
-
|
|
2873
|
-
|
|
3023
|
+
issues.push(
|
|
3024
|
+
issueOf("INVALID_URI", path, "URI contains a fragment identifier ('#'), which is forbidden")
|
|
2874
3025
|
);
|
|
2875
3026
|
return;
|
|
2876
3027
|
}
|
|
2877
3028
|
const sepIdx = uri.indexOf("://");
|
|
2878
3029
|
if (sepIdx <= 0 || !/^[a-z][a-z0-9+.-]*$/i.test(uri.slice(0, sepIdx))) {
|
|
2879
|
-
|
|
2880
|
-
|
|
3030
|
+
issues.push(
|
|
3031
|
+
issueOf("INVALID_URI", path, "URI is not absolute (missing scheme://hierarchical-part)")
|
|
2881
3032
|
);
|
|
2882
3033
|
return;
|
|
2883
3034
|
}
|
|
2884
3035
|
const scheme = uri.slice(0, sepIdx).toLowerCase();
|
|
2885
3036
|
const rest = uri.slice(sepIdx + "://".length);
|
|
2886
3037
|
if (scheme === "ar") {
|
|
2887
|
-
if (!/^
|
|
2888
|
-
|
|
2889
|
-
|
|
3038
|
+
if (!/^[A-Za-z0-9_-]{43}$/.test(rest)) {
|
|
3039
|
+
issues.push(
|
|
3040
|
+
issueOf(
|
|
2890
3041
|
"INVALID_URI",
|
|
2891
3042
|
path,
|
|
2892
3043
|
"ar:// URI does not match `^ar://[A-Za-z0-9_-]{43}$` (43-char base64url txid, no path/query/fragment)"
|
|
@@ -2899,315 +3050,286 @@ function validateOneUri(chunks, path, errors) {
|
|
|
2899
3050
|
const slashIdx = rest.indexOf("/");
|
|
2900
3051
|
const cid = slashIdx === -1 ? rest : rest.slice(0, slashIdx);
|
|
2901
3052
|
if (!validateCidProfile(cid)) {
|
|
2902
|
-
|
|
2903
|
-
|
|
3053
|
+
issues.push(
|
|
3054
|
+
issueOf("INVALID_URI", path, "ipfs:// URI is not a valid CID under the Label 309 profile")
|
|
2904
3055
|
);
|
|
2905
3056
|
}
|
|
2906
3057
|
return;
|
|
2907
3058
|
}
|
|
2908
|
-
|
|
2909
|
-
|
|
3059
|
+
issues.push(
|
|
3060
|
+
issueOf("INVALID_URI", path, "unsupported URI scheme; v1 PoE URI set is {ar://, ipfs://}")
|
|
2910
3061
|
);
|
|
2911
3062
|
}
|
|
2912
|
-
function checkItemEnc(item, idx,
|
|
3063
|
+
function checkItemEnc(item, idx, opts, issues) {
|
|
3064
|
+
const encPath = ["items", idx, "enc"];
|
|
2913
3065
|
const hasContentHash = Object.keys(item.hashes).some((alg) => alg in HASH_ALG_LENGTHS);
|
|
2914
3066
|
if (!hasContentHash) {
|
|
2915
|
-
|
|
2916
|
-
|
|
3067
|
+
issues.push(
|
|
3068
|
+
issueOf(
|
|
2917
3069
|
"ENC_REQUIRES_CONTENT_HASH",
|
|
2918
|
-
|
|
2919
|
-
"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)"
|
|
2920
3072
|
)
|
|
2921
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"));
|
|
2922
3078
|
return;
|
|
2923
3079
|
}
|
|
2924
|
-
const
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
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
|
+
);
|
|
2933
3096
|
return;
|
|
2934
3097
|
}
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
"
|
|
2941
|
-
[...basePath, "scheme"],
|
|
2942
|
-
`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"
|
|
2943
3104
|
)
|
|
2944
3105
|
);
|
|
3106
|
+
return;
|
|
2945
3107
|
}
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
3108
|
+
const aead = enc["aead"];
|
|
3109
|
+
if (typeof aead === "string" && UNAUTHENTICATED_CIPHER_RE.test(aead)) {
|
|
3110
|
+
issues.push(
|
|
3111
|
+
issueOf(
|
|
2949
3112
|
"UNAUTHENTICATED_CIPHER_FORBIDDEN",
|
|
2950
|
-
[...
|
|
2951
|
-
`'${
|
|
3113
|
+
[...encPath, "aead"],
|
|
3114
|
+
`'${aead}' is an unauthenticated cipher; Label 309 mandates an authenticated (AEAD) cipher`
|
|
2952
3115
|
)
|
|
2953
3116
|
);
|
|
2954
3117
|
return;
|
|
2955
3118
|
}
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
);
|
|
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);
|
|
2960
3148
|
return;
|
|
2961
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
|
+
}
|
|
3155
|
+
return;
|
|
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) {
|
|
2962
3186
|
const expectedNonceLen = AEAD_NONCE_LENGTHS[enc.aead];
|
|
2963
3187
|
if (enc.nonce.length !== expectedNonceLen) {
|
|
2964
|
-
|
|
2965
|
-
|
|
3188
|
+
issues.push(
|
|
3189
|
+
issueOf(
|
|
2966
3190
|
"NONCE_LENGTH_MISMATCH",
|
|
2967
|
-
[...
|
|
3191
|
+
[...encPath, "nonce"],
|
|
2968
3192
|
`nonce length ${enc.nonce.length} != ${expectedNonceLen} for ${enc.aead}`
|
|
2969
3193
|
)
|
|
2970
3194
|
);
|
|
2971
3195
|
}
|
|
2972
|
-
if (enc.kem !== void 0 && !(enc.kem in KEM_SLOT_DESCRIPTORS)) {
|
|
2973
|
-
errors.push(issue("UNSUPPORTED_KEM_ALG", [...basePath, "kem"], `unknown kem alg: ${enc.kem}`));
|
|
2974
|
-
}
|
|
2975
3196
|
const hasSlots = enc.slots !== void 0;
|
|
2976
3197
|
const hasSlotsMac = enc.slots_mac !== void 0;
|
|
2977
3198
|
const hasPassphrase = enc.passphrase !== void 0;
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
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
|
+
)
|
|
2981
3207
|
);
|
|
2982
3208
|
}
|
|
2983
3209
|
if (hasSlots && !hasSlotsMac) {
|
|
2984
|
-
|
|
2985
|
-
|
|
3210
|
+
issues.push(
|
|
3211
|
+
issueOf("ENC_SLOTS_MAC_REQUIRED", encPath, "enc.slots present but enc.slots_mac absent")
|
|
2986
3212
|
);
|
|
2987
3213
|
}
|
|
2988
3214
|
if (hasSlotsMac && !hasSlots) {
|
|
2989
|
-
|
|
2990
|
-
|
|
3215
|
+
issues.push(
|
|
3216
|
+
issueOf("ENC_SLOTS_REQUIRED", encPath, "enc.slots_mac present but enc.slots absent")
|
|
2991
3217
|
);
|
|
2992
3218
|
}
|
|
2993
|
-
if (hasSlots &&
|
|
2994
|
-
|
|
3219
|
+
if (hasSlots && !hasKem) {
|
|
3220
|
+
issues.push(issueOf("ENC_KEM_REQUIRED", encPath, "enc.slots present but enc.kem absent"));
|
|
2995
3221
|
}
|
|
2996
3222
|
if (!hasSlots && !hasPassphrase) {
|
|
2997
|
-
|
|
2998
|
-
|
|
3223
|
+
issues.push(
|
|
3224
|
+
issueOf(
|
|
2999
3225
|
"ENC_NO_KEY_PATH",
|
|
3000
|
-
|
|
3226
|
+
encPath,
|
|
3001
3227
|
"enc requires either slots or passphrase \u2014 no on-chain key path otherwise"
|
|
3002
3228
|
)
|
|
3003
3229
|
);
|
|
3004
3230
|
}
|
|
3005
3231
|
if (hasSlots) {
|
|
3006
|
-
const
|
|
3007
|
-
if (
|
|
3008
|
-
|
|
3009
|
-
|
|
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")
|
|
3010
3236
|
);
|
|
3011
|
-
} else if (
|
|
3012
|
-
|
|
3013
|
-
|
|
3237
|
+
} else if (slots.length > opts.maxSlots) {
|
|
3238
|
+
issues.push(
|
|
3239
|
+
issueOf(
|
|
3014
3240
|
"ENC_SLOTS_TOO_MANY",
|
|
3015
|
-
[...
|
|
3016
|
-
`slots length ${
|
|
3241
|
+
[...encPath, "slots"],
|
|
3242
|
+
`slots length ${slots.length} exceeds the slot-count bound ${opts.maxSlots}`
|
|
3017
3243
|
)
|
|
3018
3244
|
);
|
|
3019
|
-
} else {
|
|
3020
|
-
const descriptor =
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
seenKemMaterial.add(key);
|
|
3047
|
-
}
|
|
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];
|
|
3251
|
+
checkSlotShape(
|
|
3252
|
+
slot,
|
|
3253
|
+
rawSlotKeys[si] ?? /* @__PURE__ */ new Set(),
|
|
3254
|
+
descriptor,
|
|
3255
|
+
enc.kem,
|
|
3256
|
+
slotPath,
|
|
3257
|
+
issues
|
|
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);
|
|
3048
3272
|
}
|
|
3049
|
-
});
|
|
3050
|
-
const perSlotBytes = descriptor.fieldLength + descriptor.wrapLength;
|
|
3051
|
-
const decodedEnvelopeBytes = NONCE_LENGTH + SLOTS_MAC_LENGTH + slotCount * perSlotBytes;
|
|
3052
|
-
if (decodedEnvelopeBytes > MAX_DECODED_ENVELOPE_BYTES) {
|
|
3053
|
-
errors.push(
|
|
3054
|
-
issue(
|
|
3055
|
-
"ENC_ENVELOPE_TOO_LARGE",
|
|
3056
|
-
[...basePath, "slots"],
|
|
3057
|
-
`decoded envelope size ${decodedEnvelopeBytes} exceeds MAX_DECODED_ENVELOPE_BYTES=${MAX_DECODED_ENVELOPE_BYTES}`
|
|
3058
|
-
)
|
|
3059
|
-
);
|
|
3060
3273
|
}
|
|
3061
|
-
}
|
|
3274
|
+
});
|
|
3062
3275
|
}
|
|
3063
3276
|
}
|
|
3064
3277
|
if (hasPassphrase) {
|
|
3065
|
-
|
|
3066
|
-
const ppPath = [...basePath, "passphrase"];
|
|
3067
|
-
if (!PASSPHRASE_KDF_ALGS.has(pp.alg)) {
|
|
3068
|
-
errors.push(
|
|
3069
|
-
issue(
|
|
3070
|
-
"ENC_PASSPHRASE_ALG_UNSUPPORTED",
|
|
3071
|
-
[...ppPath, "alg"],
|
|
3072
|
-
`unknown passphrase kdf alg: ${pp.alg}`
|
|
3073
|
-
)
|
|
3074
|
-
);
|
|
3075
|
-
return;
|
|
3076
|
-
}
|
|
3077
|
-
if (pp.alg === "argon2id") {
|
|
3078
|
-
const allowed = /* @__PURE__ */ new Set(["m", "t", "p"]);
|
|
3079
|
-
for (const k4 of Object.keys(pp.params)) {
|
|
3080
|
-
if (!allowed.has(k4)) {
|
|
3081
|
-
errors.push(
|
|
3082
|
-
issue(
|
|
3083
|
-
"SCHEMA_UNKNOWN_FIELD",
|
|
3084
|
-
[...ppPath, "params", k4],
|
|
3085
|
-
`unknown argon2id params field: ${k4}`
|
|
3086
|
-
)
|
|
3087
|
-
);
|
|
3088
|
-
}
|
|
3089
|
-
}
|
|
3090
|
-
const p5 = pp.params;
|
|
3091
|
-
const argonInt = (val, name) => {
|
|
3092
|
-
if (typeof val !== "number" || !Number.isInteger(val)) {
|
|
3093
|
-
errors.push(
|
|
3094
|
-
issue(
|
|
3095
|
-
"SCHEMA_TYPE_MISMATCH",
|
|
3096
|
-
[...ppPath, "params", name],
|
|
3097
|
-
`argon2id params.${name} must be a CBOR unsigned integer`
|
|
3098
|
-
)
|
|
3099
|
-
);
|
|
3100
|
-
return null;
|
|
3101
|
-
}
|
|
3102
|
-
return val;
|
|
3103
|
-
};
|
|
3104
|
-
const mVal = argonInt(p5.m, "m");
|
|
3105
|
-
const tVal = argonInt(p5.t, "t");
|
|
3106
|
-
const pVal = argonInt(p5.p, "p");
|
|
3107
|
-
if (mVal !== null && mVal < 65536) {
|
|
3108
|
-
errors.push(
|
|
3109
|
-
issue(
|
|
3110
|
-
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
3111
|
-
[...ppPath, "params", "m"],
|
|
3112
|
-
"argon2id requires m >= 65536 KiB"
|
|
3113
|
-
)
|
|
3114
|
-
);
|
|
3115
|
-
}
|
|
3116
|
-
if (tVal !== null && tVal < 3) {
|
|
3117
|
-
errors.push(
|
|
3118
|
-
issue(
|
|
3119
|
-
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
3120
|
-
[...ppPath, "params", "t"],
|
|
3121
|
-
"argon2id requires t >= 3"
|
|
3122
|
-
)
|
|
3123
|
-
);
|
|
3124
|
-
}
|
|
3125
|
-
if (pVal !== null && pVal < 1) {
|
|
3126
|
-
errors.push(
|
|
3127
|
-
issue(
|
|
3128
|
-
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
3129
|
-
[...ppPath, "params", "p"],
|
|
3130
|
-
"argon2id requires p >= 1"
|
|
3131
|
-
)
|
|
3132
|
-
);
|
|
3133
|
-
}
|
|
3134
|
-
}
|
|
3278
|
+
checkPassphraseBlock(enc.passphrase, [...encPath, "passphrase"], opts, issues);
|
|
3135
3279
|
}
|
|
3136
3280
|
}
|
|
3137
3281
|
var SLOT_KEY_UNIVERSE = /* @__PURE__ */ new Set(["epk", "kem_ct", "wrap"]);
|
|
3138
|
-
function checkSlotShape(slot, rawKeys, descriptor, kem, slotPath,
|
|
3282
|
+
function checkSlotShape(slot, rawKeys, descriptor, kem, slotPath, issues) {
|
|
3139
3283
|
const foreignField = descriptor.field === "epk" ? "kem_ct" : "epk";
|
|
3140
3284
|
if (rawKeys.has(foreignField)) {
|
|
3141
|
-
|
|
3142
|
-
|
|
3285
|
+
issues.push(
|
|
3286
|
+
issueOf(
|
|
3143
3287
|
"ENC_SLOT_INVALID_SHAPE",
|
|
3144
3288
|
[...slotPath, foreignField],
|
|
3145
3289
|
`slot carries '${foreignField}' but kem='${kem}' expects '${descriptor.field}'`
|
|
3146
3290
|
)
|
|
3147
3291
|
);
|
|
3148
3292
|
}
|
|
3149
|
-
for (const
|
|
3150
|
-
if (!SLOT_KEY_UNIVERSE.has(
|
|
3151
|
-
|
|
3152
|
-
|
|
3293
|
+
for (const key of rawKeys) {
|
|
3294
|
+
if (!SLOT_KEY_UNIVERSE.has(key)) {
|
|
3295
|
+
issues.push(
|
|
3296
|
+
issueOf(
|
|
3153
3297
|
"ENC_SLOT_INVALID_SHAPE",
|
|
3154
|
-
[...slotPath,
|
|
3155
|
-
`slot carries unexpected key '${
|
|
3298
|
+
[...slotPath, key],
|
|
3299
|
+
`slot carries unexpected key '${key}'; a slot is a 2-key map {${descriptor.field}, wrap}`
|
|
3156
3300
|
)
|
|
3157
3301
|
);
|
|
3158
3302
|
}
|
|
3159
3303
|
}
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
}
|
|
3178
|
-
} else {
|
|
3179
|
-
if (slot.kem_ct === void 0) {
|
|
3180
|
-
errors.push(
|
|
3181
|
-
issue(
|
|
3182
|
-
"ENC_SLOT_INVALID_SHAPE",
|
|
3183
|
-
[...slotPath, "kem_ct"],
|
|
3184
|
-
`slot for kem='${kem}' is missing required 'kem_ct'`
|
|
3185
|
-
)
|
|
3186
|
-
);
|
|
3187
|
-
} else {
|
|
3188
|
-
const reassembled = bytesChunkArrayConcat(slot.kem_ct).length;
|
|
3189
|
-
if (reassembled !== descriptor.fieldLength) {
|
|
3190
|
-
errors.push(
|
|
3191
|
-
issue(
|
|
3192
|
-
KEM_FIELD_LENGTH_CODE.kem_ct,
|
|
3193
|
-
[...slotPath, "kem_ct"],
|
|
3194
|
-
`slot.kem_ct reassembles to ${reassembled} bytes != ${descriptor.fieldLength} for ${kem}`
|
|
3195
|
-
)
|
|
3196
|
-
);
|
|
3197
|
-
}
|
|
3198
|
-
}
|
|
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
|
+
);
|
|
3199
3321
|
}
|
|
3200
3322
|
if (slot.wrap === void 0) {
|
|
3201
|
-
|
|
3202
|
-
|
|
3323
|
+
issues.push(
|
|
3324
|
+
issueOf(
|
|
3203
3325
|
"ENC_SLOT_INVALID_SHAPE",
|
|
3204
3326
|
[...slotPath, "wrap"],
|
|
3205
3327
|
`slot for kem='${kem}' is missing required 'wrap'`
|
|
3206
3328
|
)
|
|
3207
3329
|
);
|
|
3208
3330
|
} else if (slot.wrap.length !== descriptor.wrapLength) {
|
|
3209
|
-
|
|
3210
|
-
|
|
3331
|
+
issues.push(
|
|
3332
|
+
issueOf(
|
|
3211
3333
|
"WRAP_LENGTH_MISMATCH",
|
|
3212
3334
|
[...slotPath, "wrap"],
|
|
3213
3335
|
`slot.wrap length ${slot.wrap.length} != ${descriptor.wrapLength}`
|
|
@@ -3215,22 +3337,88 @@ function checkSlotShape(slot, rawKeys, descriptor, kem, slotPath, errors) {
|
|
|
3215
3337
|
);
|
|
3216
3338
|
}
|
|
3217
3339
|
}
|
|
3218
|
-
function
|
|
3219
|
-
if (
|
|
3220
|
-
|
|
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;
|
|
3221
3350
|
}
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
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
|
+
}
|
|
3229
3418
|
}
|
|
3230
|
-
return hex;
|
|
3231
3419
|
}
|
|
3232
3420
|
function rawSlotKeySets(rawEnc) {
|
|
3233
|
-
const slots =
|
|
3421
|
+
const slots = rawEnc["slots"];
|
|
3234
3422
|
if (!Array.isArray(slots)) return [];
|
|
3235
3423
|
return slots.map((slot) => {
|
|
3236
3424
|
const keys = /* @__PURE__ */ new Set();
|
|
@@ -3242,54 +3430,64 @@ function rawSlotKeySets(rawEnc) {
|
|
|
3242
3430
|
return keys;
|
|
3243
3431
|
});
|
|
3244
3432
|
}
|
|
3245
|
-
function
|
|
3246
|
-
if (value instanceof Map) return value.get(key);
|
|
3247
|
-
if (typeof value === "object" && value !== null) {
|
|
3248
|
-
return value[key];
|
|
3249
|
-
}
|
|
3250
|
-
return void 0;
|
|
3251
|
-
}
|
|
3252
|
-
function checkMerkleCommit(commit, idx, errors) {
|
|
3433
|
+
function checkMerkleCommit(commit, idx, issues) {
|
|
3253
3434
|
const basePath = ["merkle", idx];
|
|
3254
3435
|
if (!(commit.alg in MERKLE_COMMIT_ALG_LENGTHS)) {
|
|
3255
|
-
|
|
3256
|
-
|
|
3436
|
+
issues.push(
|
|
3437
|
+
issueOf(
|
|
3257
3438
|
"UNSUPPORTED_MERKLE_COMMIT_ALG",
|
|
3258
3439
|
[...basePath, "alg"],
|
|
3259
3440
|
`unknown merkle commitment alg: ${commit.alg}`
|
|
3260
3441
|
)
|
|
3261
3442
|
);
|
|
3262
|
-
|
|
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
|
+
}
|
|
3263
3454
|
}
|
|
3264
|
-
const
|
|
3265
|
-
if (
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
"
|
|
3269
|
-
[...basePath, "
|
|
3270
|
-
|
|
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`
|
|
3271
3470
|
)
|
|
3272
3471
|
);
|
|
3273
3472
|
}
|
|
3274
|
-
if (commit.uris) {
|
|
3275
|
-
|
|
3473
|
+
if (commit.uris !== void 0) {
|
|
3474
|
+
checkUris(commit.uris, [...basePath, "uris"], issues);
|
|
3276
3475
|
}
|
|
3277
3476
|
}
|
|
3278
|
-
function checkSigEntry(entry, idx,
|
|
3477
|
+
function checkSigEntry(entry, idx, issues) {
|
|
3279
3478
|
if (entry.cose_key !== void 0) {
|
|
3280
3479
|
const keyIssue = inspectCoseKey(entry.cose_key, idx);
|
|
3281
3480
|
if (keyIssue !== null) {
|
|
3282
|
-
|
|
3481
|
+
issues.push(keyIssue);
|
|
3283
3482
|
return;
|
|
3284
3483
|
}
|
|
3285
3484
|
}
|
|
3286
|
-
const merged = bytesChunkArrayConcat(entry.cose_sign1);
|
|
3287
3485
|
let cose;
|
|
3288
3486
|
try {
|
|
3289
|
-
cose = decodeCoseSign1(
|
|
3487
|
+
cose = decodeCoseSign1(entry.cose_sign1);
|
|
3290
3488
|
} catch (cause) {
|
|
3291
|
-
|
|
3292
|
-
|
|
3489
|
+
issues.push(
|
|
3490
|
+
issueOf(
|
|
3293
3491
|
"MALFORMED_SIG_COSE_SIGN1",
|
|
3294
3492
|
["sigs", idx],
|
|
3295
3493
|
cause instanceof CoseVerifyError || cause instanceof Error ? cause.message : String(cause)
|
|
@@ -3298,8 +3496,8 @@ function checkSigEntry(entry, idx, errors, info) {
|
|
|
3298
3496
|
return;
|
|
3299
3497
|
}
|
|
3300
3498
|
if (cose.payload !== null) {
|
|
3301
|
-
|
|
3302
|
-
|
|
3499
|
+
issues.push(
|
|
3500
|
+
issueOf(
|
|
3303
3501
|
"MALFORMED_SIG_COSE_SIGN1",
|
|
3304
3502
|
["sigs", idx],
|
|
3305
3503
|
"COSE_Sign1 payload must be null (detached); attached form forbidden"
|
|
@@ -3309,8 +3507,8 @@ function checkSigEntry(entry, idx, errors, info) {
|
|
|
3309
3507
|
}
|
|
3310
3508
|
const alg = cose.protectedHeader.get(1);
|
|
3311
3509
|
if (typeof alg !== "number" || !KNOWN_SIG_ALG_IDS.has(alg)) {
|
|
3312
|
-
|
|
3313
|
-
|
|
3510
|
+
issues.push(
|
|
3511
|
+
issueOf(
|
|
3314
3512
|
"SIGNATURE_UNSUPPORTED",
|
|
3315
3513
|
["sigs", idx],
|
|
3316
3514
|
`COSE_Sign1 protected alg ${String(alg)} not in {-8, -19}`
|
|
@@ -3319,8 +3517,8 @@ function checkSigEntry(entry, idx, errors, info) {
|
|
|
3319
3517
|
}
|
|
3320
3518
|
const protectedKid = cose.protectedHeader.get(4);
|
|
3321
3519
|
if (protectedKid instanceof Uint8Array && protectedKid.length === 32 && entry.cose_key !== void 0) {
|
|
3322
|
-
|
|
3323
|
-
|
|
3520
|
+
issues.push(
|
|
3521
|
+
issueOf(
|
|
3324
3522
|
"SIG_ENTRY_KID_COSE_KEY_CONFLICT",
|
|
3325
3523
|
["sigs", idx],
|
|
3326
3524
|
"sigs[i] carries both a 32-byte protected `kid` (path 1) and an inline `cose_key` (path 2); paths are mutually exclusive"
|
|
@@ -3328,12 +3526,12 @@ function checkSigEntry(entry, idx, errors, info) {
|
|
|
3328
3526
|
);
|
|
3329
3527
|
}
|
|
3330
3528
|
}
|
|
3331
|
-
function inspectCoseKey(
|
|
3529
|
+
function inspectCoseKey(keyBytes, i) {
|
|
3332
3530
|
let decoded;
|
|
3333
3531
|
try {
|
|
3334
|
-
decoded = decodeCanonicalCbor(
|
|
3532
|
+
decoded = decodeCanonicalCbor(keyBytes);
|
|
3335
3533
|
} catch (cause) {
|
|
3336
|
-
return
|
|
3534
|
+
return issueOf(
|
|
3337
3535
|
"MALFORMED_SIG_COSE_SIGN1",
|
|
3338
3536
|
["sigs", i, "cose_key"],
|
|
3339
3537
|
`sigs[${i}].cose_key failed to decode as cbor<COSE_Key>: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
@@ -3341,20 +3539,14 @@ function inspectCoseKey(keyChunks, i) {
|
|
|
3341
3539
|
}
|
|
3342
3540
|
const getLabel = (label) => {
|
|
3343
3541
|
if (decoded instanceof Map) return decoded.get(label);
|
|
3344
|
-
if (typeof decoded === "object" && decoded !== null) {
|
|
3345
|
-
return decoded[String(label)];
|
|
3346
|
-
}
|
|
3347
3542
|
return void 0;
|
|
3348
3543
|
};
|
|
3349
3544
|
const hasLabel = (label) => {
|
|
3350
3545
|
if (decoded instanceof Map) return decoded.has(label);
|
|
3351
|
-
if (typeof decoded === "object" && decoded !== null) {
|
|
3352
|
-
return Object.prototype.hasOwnProperty.call(decoded, String(label));
|
|
3353
|
-
}
|
|
3354
3546
|
return false;
|
|
3355
3547
|
};
|
|
3356
3548
|
if (hasLabel(-4)) {
|
|
3357
|
-
return
|
|
3549
|
+
return issueOf(
|
|
3358
3550
|
"SIG_PRIVATE_KEY_LEAKED",
|
|
3359
3551
|
["sigs", i, "cose_key"],
|
|
3360
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"
|
|
@@ -3362,7 +3554,7 @@ function inspectCoseKey(keyChunks, i) {
|
|
|
3362
3554
|
}
|
|
3363
3555
|
const kty = getLabel(1);
|
|
3364
3556
|
if (kty !== 1) {
|
|
3365
|
-
return
|
|
3557
|
+
return issueOf(
|
|
3366
3558
|
"MALFORMED_SIG_COSE_SIGN1",
|
|
3367
3559
|
["sigs", i, "cose_key"],
|
|
3368
3560
|
`sigs[${i}].cose_key COSE_Key kty (label 1) must be 1 (OKP); got ${String(kty)}`
|
|
@@ -3370,23 +3562,16 @@ function inspectCoseKey(keyChunks, i) {
|
|
|
3370
3562
|
}
|
|
3371
3563
|
const crv = getLabel(-1);
|
|
3372
3564
|
if (crv !== 6) {
|
|
3373
|
-
return
|
|
3565
|
+
return issueOf(
|
|
3374
3566
|
"MALFORMED_SIG_COSE_SIGN1",
|
|
3375
3567
|
["sigs", i, "cose_key"],
|
|
3376
3568
|
`sigs[${i}].cose_key COSE_Key crv (label -1) must be 6 (Ed25519); got ${String(crv)}`
|
|
3377
3569
|
);
|
|
3378
3570
|
}
|
|
3379
|
-
if (!hasLabel(-2)) {
|
|
3380
|
-
return issue(
|
|
3381
|
-
"MALFORMED_SIG_COSE_SIGN1",
|
|
3382
|
-
["sigs", i, "cose_key"],
|
|
3383
|
-
`sigs[${i}].cose_key COSE_Key missing label -2 (Ed25519 public-key bytes)`
|
|
3384
|
-
);
|
|
3385
|
-
}
|
|
3386
3571
|
const x5 = getLabel(-2);
|
|
3387
3572
|
if (!(x5 instanceof Uint8Array) || x5.length !== 32) {
|
|
3388
3573
|
const got = x5 instanceof Uint8Array ? `${x5.length}-byte bstr` : typeof x5;
|
|
3389
|
-
return
|
|
3574
|
+
return issueOf(
|
|
3390
3575
|
"MALFORMED_SIG_COSE_SIGN1",
|
|
3391
3576
|
["sigs", i, "cose_key"],
|
|
3392
3577
|
`sigs[${i}].cose_key COSE_Key label -2 must be a 32-byte byte string (Ed25519 public key); got ${got}`
|
|
@@ -3394,6 +3579,58 @@ function inspectCoseKey(keyChunks, i) {
|
|
|
3394
3579
|
}
|
|
3395
3580
|
return null;
|
|
3396
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
|
+
}
|
|
3397
3634
|
var ACCEPTED_CIDV1_MULTIBASE = /* @__PURE__ */ new Set(["b", "B", "f", "F", "z"]);
|
|
3398
3635
|
var ACCEPTED_MULTICODECS = /* @__PURE__ */ new Set([85, 112, 113]);
|
|
3399
3636
|
var ACCEPTED_MULTIHASHES = /* @__PURE__ */ new Map([
|
|
@@ -3529,52 +3766,56 @@ function decodeBase58btc(s3) {
|
|
|
3529
3766
|
}
|
|
3530
3767
|
return out;
|
|
3531
3768
|
}
|
|
3532
|
-
function
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
errors.push(
|
|
3537
|
-
issue("SCHEMA_TYPE_MISMATCH", ["crit"], "crit[] must carry at least one entry when present")
|
|
3538
|
-
);
|
|
3539
|
-
return invalid;
|
|
3540
|
-
}
|
|
3541
|
-
const seen = /* @__PURE__ */ new Set();
|
|
3542
|
-
for (let i = 0; i < record.crit.length; i++) {
|
|
3543
|
-
const critName = record.crit[i];
|
|
3544
|
-
let reason = null;
|
|
3545
|
-
if (TOP_LEVEL_BASE_KEYS.has(critName)) {
|
|
3546
|
-
reason = `'${critName}' is a base key and MUST NOT appear in crit[]`;
|
|
3547
|
-
} else if (!isExtensionKey(critName)) {
|
|
3548
|
-
reason = `'${critName}' does not match the extension-key regex (^x-.+ or ^[a-z]+-.+)`;
|
|
3549
|
-
} else if (!decodedTopKeys.has(critName)) {
|
|
3550
|
-
reason = `'${critName}' is named in crit but absent from the record map`;
|
|
3551
|
-
} else if (seen.has(critName)) {
|
|
3552
|
-
reason = `'${critName}' appears more than once in crit[]`;
|
|
3553
|
-
}
|
|
3554
|
-
seen.add(critName);
|
|
3555
|
-
if (reason !== null) {
|
|
3556
|
-
invalid.add(i);
|
|
3557
|
-
errors.push(issue("CRIT_SHAPE_INVALID", ["crit", i], reason));
|
|
3558
|
-
}
|
|
3559
|
-
}
|
|
3560
|
-
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;
|
|
3561
3773
|
}
|
|
3562
|
-
function
|
|
3563
|
-
if (
|
|
3564
|
-
if (
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
}
|
|
3571
|
-
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;
|
|
3572
3782
|
}
|
|
3573
|
-
function
|
|
3783
|
+
function issueOf(code, path, message) {
|
|
3574
3784
|
return { code, path, message, severity: SEVERITY[code] };
|
|
3575
3785
|
}
|
|
3576
|
-
|
|
3577
|
-
|
|
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);
|
|
3578
3819
|
}
|
|
3579
3820
|
function valueAtPath(root, path) {
|
|
3580
3821
|
let cur = root;
|
|
@@ -3595,6 +3836,6 @@ function valueAtPath(root, path) {
|
|
|
3595
3836
|
(*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) *)
|
|
3596
3837
|
*/
|
|
3597
3838
|
|
|
3598
|
-
export { Argon2idParamsSchema,
|
|
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 };
|
|
3599
3840
|
//# sourceMappingURL=index.js.map
|
|
3600
3841
|
//# sourceMappingURL=index.js.map
|