@cardanowall/poe-standard 0.3.0 → 0.5.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/validator.cjs
CHANGED
|
@@ -1621,6 +1621,14 @@ var CanonicalCborError = class extends Error {
|
|
|
1621
1621
|
this.code = code;
|
|
1622
1622
|
}
|
|
1623
1623
|
};
|
|
1624
|
+
function encodeCanonicalCbor(value) {
|
|
1625
|
+
return v2(value, {
|
|
1626
|
+
cde: true,
|
|
1627
|
+
collapseBigInts: true,
|
|
1628
|
+
rejectDuplicateKeys: true,
|
|
1629
|
+
sortKeys: f4
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1624
1632
|
function decodeCanonicalCbor(bytes) {
|
|
1625
1633
|
try {
|
|
1626
1634
|
return l4(bytes, {
|
|
@@ -2184,25 +2192,54 @@ function decodeCoseSign1(bytes) {
|
|
|
2184
2192
|
signature: signatureRaw
|
|
2185
2193
|
};
|
|
2186
2194
|
}
|
|
2187
|
-
|
|
2188
|
-
|
|
2195
|
+
var CARDANO_POE_ITEM_HASHES_PREFIX = new TextEncoder().encode(
|
|
2196
|
+
"cardano-poe-item-hashes-v1"
|
|
2197
|
+
);
|
|
2189
2198
|
var CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX = new TextEncoder().encode(
|
|
2190
2199
|
"cardano-poe-slots-transcript-v1"
|
|
2191
2200
|
);
|
|
2201
|
+
var CARDANO_POE_PASSPHRASE_TRANSCRIPT_PREFIX = new TextEncoder().encode(
|
|
2202
|
+
"cardano-poe-passphrase-transcript-v1"
|
|
2203
|
+
);
|
|
2204
|
+
var CARDANO_POE_HKDF_INFO_SLOTS_MAC = new TextEncoder().encode(
|
|
2205
|
+
"cardano-poe-slots-mac-v1"
|
|
2206
|
+
);
|
|
2207
|
+
var CARDANO_POE_HKDF_INFO_PASSPHRASE_MAC = new TextEncoder().encode(
|
|
2208
|
+
"cardano-poe-passphrase-mac-v1"
|
|
2209
|
+
);
|
|
2192
2210
|
var CARDANO_POE_HKDF_INFO_PAYLOAD = new TextEncoder().encode(
|
|
2193
2211
|
"cardano-poe-payload-v1"
|
|
2194
2212
|
);
|
|
2195
2213
|
var CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE = new TextEncoder().encode(
|
|
2196
2214
|
"cardano-poe-payload-passphrase-v1"
|
|
2197
2215
|
);
|
|
2216
|
+
var CARDANO_POE_X25519_KEK_SALT_PREFIX = new TextEncoder().encode(
|
|
2217
|
+
"cardano-poe-x25519-kek-salt-v1"
|
|
2218
|
+
);
|
|
2198
2219
|
var CARDANO_POE_XWING_KEK_SALT_PREFIX = new TextEncoder().encode(
|
|
2199
2220
|
"cardano-poe-xwing-kek-salt-v1"
|
|
2200
2221
|
);
|
|
2222
|
+
if (CARDANO_POE_ITEM_HASHES_PREFIX.length !== 26) {
|
|
2223
|
+
throw new Error("CARDANO_POE_ITEM_HASHES_PREFIX byte-length invariant violated (expected 26)");
|
|
2224
|
+
}
|
|
2201
2225
|
if (CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length !== 31) {
|
|
2202
2226
|
throw new Error(
|
|
2203
2227
|
"CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX byte-length invariant violated (expected 31)"
|
|
2204
2228
|
);
|
|
2205
2229
|
}
|
|
2230
|
+
if (CARDANO_POE_PASSPHRASE_TRANSCRIPT_PREFIX.length !== 36) {
|
|
2231
|
+
throw new Error(
|
|
2232
|
+
"CARDANO_POE_PASSPHRASE_TRANSCRIPT_PREFIX byte-length invariant violated (expected 36)"
|
|
2233
|
+
);
|
|
2234
|
+
}
|
|
2235
|
+
if (CARDANO_POE_HKDF_INFO_SLOTS_MAC.length !== 24) {
|
|
2236
|
+
throw new Error("CARDANO_POE_HKDF_INFO_SLOTS_MAC byte-length invariant violated (expected 24)");
|
|
2237
|
+
}
|
|
2238
|
+
if (CARDANO_POE_HKDF_INFO_PASSPHRASE_MAC.length !== 29) {
|
|
2239
|
+
throw new Error(
|
|
2240
|
+
"CARDANO_POE_HKDF_INFO_PASSPHRASE_MAC byte-length invariant violated (expected 29)"
|
|
2241
|
+
);
|
|
2242
|
+
}
|
|
2206
2243
|
if (CARDANO_POE_HKDF_INFO_PAYLOAD.length !== 22) {
|
|
2207
2244
|
throw new Error("CARDANO_POE_HKDF_INFO_PAYLOAD byte-length invariant violated (expected 22)");
|
|
2208
2245
|
}
|
|
@@ -2211,6 +2248,11 @@ if (CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE.length !== 33) {
|
|
|
2211
2248
|
"CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE byte-length invariant violated (expected 33)"
|
|
2212
2249
|
);
|
|
2213
2250
|
}
|
|
2251
|
+
if (CARDANO_POE_X25519_KEK_SALT_PREFIX.length !== 30) {
|
|
2252
|
+
throw new Error(
|
|
2253
|
+
"CARDANO_POE_X25519_KEK_SALT_PREFIX byte-length invariant violated (expected 30)"
|
|
2254
|
+
);
|
|
2255
|
+
}
|
|
2214
2256
|
if (CARDANO_POE_XWING_KEK_SALT_PREFIX.length !== 29) {
|
|
2215
2257
|
throw new Error("CARDANO_POE_XWING_KEK_SALT_PREFIX byte-length invariant violated (expected 29)");
|
|
2216
2258
|
}
|
|
@@ -2220,9 +2262,6 @@ var CARDANO_POE_HKDF_INFO_KEK = new TextEncoder().encode("cardano-poe-kek-v1");
|
|
|
2220
2262
|
var CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 = new TextEncoder().encode(
|
|
2221
2263
|
"cardano-poe-kek-mlkem768x25519-v1"
|
|
2222
2264
|
);
|
|
2223
|
-
var CARDANO_POE_HKDF_INFO_SLOTS_MAC = new TextEncoder().encode(
|
|
2224
|
-
"cardano-poe-slots-mac-v1"
|
|
2225
|
-
);
|
|
2226
2265
|
var ZERO_NONCE_12 = new Uint8Array(12);
|
|
2227
2266
|
if (CARDANO_POE_HKDF_INFO_KEK.length !== 18) {
|
|
2228
2267
|
throw new Error("CARDANO_POE_HKDF_INFO_KEK byte-length invariant violated (expected 18)");
|
|
@@ -2232,41 +2271,178 @@ if (CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519.length !== 33) {
|
|
|
2232
2271
|
"CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 byte-length invariant violated (expected 33)"
|
|
2233
2272
|
);
|
|
2234
2273
|
}
|
|
2235
|
-
if (CARDANO_POE_HKDF_INFO_SLOTS_MAC.length !== 24) {
|
|
2236
|
-
throw new Error("CARDANO_POE_HKDF_INFO_SLOTS_MAC byte-length invariant violated (expected 24)");
|
|
2237
|
-
}
|
|
2238
2274
|
if (ZERO_NONCE_12.length !== 12) {
|
|
2239
2275
|
throw new Error("ZERO_NONCE_12 byte-length invariant violated (expected 12)");
|
|
2240
2276
|
}
|
|
2277
|
+
new TextEncoder();
|
|
2241
2278
|
|
|
2242
|
-
// src/
|
|
2243
|
-
var
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2279
|
+
// src/error-codes.ts
|
|
2280
|
+
var ERROR_CODES = [
|
|
2281
|
+
"MALFORMED_CBOR",
|
|
2282
|
+
"SCHEMA_TYPE_MISMATCH",
|
|
2283
|
+
"SCHEMA_MISSING_REQUIRED",
|
|
2284
|
+
"SCHEMA_UNKNOWN_FIELD",
|
|
2285
|
+
"SCHEMA_INVALID_LITERAL",
|
|
2286
|
+
"SCHEMA_EMPTY_RECORD",
|
|
2287
|
+
"HASH_DIGEST_LENGTH_MISMATCH",
|
|
2288
|
+
"UNSUPPORTED_HASH_ALG",
|
|
2289
|
+
"UNSUPPORTED_MERKLE_COMMIT_ALG",
|
|
2290
|
+
"SCHEMA_MERKLE_LEAF_COUNT_INVALID",
|
|
2291
|
+
"INVALID_URI",
|
|
2292
|
+
"CHUNK_TOO_LARGE",
|
|
2293
|
+
"UNAUTHENTICATED_CIPHER_FORBIDDEN",
|
|
2294
|
+
"UNSUPPORTED_AEAD_ALG",
|
|
2295
|
+
"NONCE_LENGTH_MISMATCH",
|
|
2296
|
+
"UNSUPPORTED_ENVELOPE_SCHEME",
|
|
2297
|
+
"ENC_UNSUPPORTED",
|
|
2298
|
+
"ENC_SLOTS_EMPTY",
|
|
2299
|
+
"ENC_SLOT_INVALID_SHAPE",
|
|
2300
|
+
"UNSUPPORTED_KEM_ALG",
|
|
2301
|
+
"ENC_KEM_REQUIRED",
|
|
2302
|
+
"KEM_EPK_LENGTH_MISMATCH",
|
|
2303
|
+
"KEM_CT_LENGTH_MISMATCH",
|
|
2304
|
+
"WRAP_LENGTH_MISMATCH",
|
|
2305
|
+
"ENC_SLOTS_MAC_INVALID_LENGTH",
|
|
2306
|
+
"ENC_SLOTS_MAC_REQUIRED",
|
|
2307
|
+
"ENC_SLOTS_REQUIRED",
|
|
2308
|
+
"ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
|
|
2309
|
+
"ENC_SLOTS_TOO_MANY",
|
|
2310
|
+
"ENC_ENVELOPE_TOO_LARGE",
|
|
2311
|
+
"ENC_EXCLUSIVITY_VIOLATION",
|
|
2312
|
+
"ENC_NO_KEY_PATH",
|
|
2313
|
+
"ENC_REQUIRES_CONTENT_HASH",
|
|
2314
|
+
"ENC_PASSPHRASE_ALG_UNSUPPORTED",
|
|
2315
|
+
"ENC_PASSPHRASE_SALT_TOO_SHORT",
|
|
2316
|
+
"ENC_PASSPHRASE_SALT_TOO_LONG",
|
|
2317
|
+
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
2318
|
+
"ENC_PASSPHRASE_PARAMS_EXCEED_POLICY",
|
|
2319
|
+
"MALFORMED_SIG_COSE_SIGN1",
|
|
2320
|
+
"SIGNATURE_UNSUPPORTED",
|
|
2321
|
+
"SIG_ENTRY_INVALID_SHAPE",
|
|
2322
|
+
"SIG_ENTRY_KID_COSE_KEY_CONFLICT",
|
|
2323
|
+
"SIG_PRIVATE_KEY_LEAKED",
|
|
2324
|
+
"SUPERSEDES_TX_INVALID_LENGTH",
|
|
2325
|
+
"EXTENSION_UNSUPPORTED_CRITICAL",
|
|
2326
|
+
"CRIT_SHAPE_INVALID",
|
|
2327
|
+
"TX_NOT_FOUND",
|
|
2328
|
+
"PROVIDER_UNAVAILABLE",
|
|
2329
|
+
"TX_INTEGRITY_MISMATCH",
|
|
2330
|
+
"METADATA_NOT_FOUND",
|
|
2331
|
+
"INSUFFICIENT_CONFIRMATIONS",
|
|
2332
|
+
"SIGNATURE_INVALID",
|
|
2333
|
+
"SIGNER_KEY_UNRESOLVED",
|
|
2334
|
+
"WALLET_ADDRESS_MISMATCH",
|
|
2335
|
+
"URI_TARGET_FORBIDDEN",
|
|
2336
|
+
"URI_INTEGRITY_MISMATCH",
|
|
2337
|
+
"URI_PROVIDER_INTEGRITY_MISMATCH",
|
|
2338
|
+
"URI_FETCH_FAILED",
|
|
2339
|
+
"CONTENT_UNAVAILABLE",
|
|
2340
|
+
"CONTENT_FETCH_LIMIT_EXCEEDED",
|
|
2341
|
+
"CIPHERTEXT_UNAVAILABLE",
|
|
2342
|
+
"SERVICE_INDEPENDENCE_VIOLATION",
|
|
2343
|
+
"WRONG_DECRYPTION_INPUT_SHAPE",
|
|
2344
|
+
"WRONG_RECIPIENT_KEY",
|
|
2345
|
+
"TAMPERED_HEADER",
|
|
2346
|
+
"TAMPERED_CIPHERTEXT",
|
|
2347
|
+
"KDF_DERIVATION_FAILED",
|
|
2348
|
+
"ENC_PASSPHRASE_UNNORMALIZABLE",
|
|
2349
|
+
"ENC_PASSPHRASE_EMPTY",
|
|
2350
|
+
"SCHEMA_MERKLE_LEAF_COUNT_MISMATCH",
|
|
2351
|
+
"SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED",
|
|
2352
|
+
"SCHEMA_MERKLE_LEAVES_MALFORMED",
|
|
2353
|
+
"MERKLE_ROOT_MISMATCH",
|
|
2354
|
+
"MERKLE_LEAVES_UNAVAILABLE",
|
|
2355
|
+
"MERKLE_UNSUPPORTED",
|
|
2356
|
+
"OUT_OF_PROFILE_SKIPPED"
|
|
2357
|
+
];
|
|
2358
|
+
var ERROR_CODE_PART = Object.freeze({
|
|
2359
|
+
MALFORMED_CBOR: "A",
|
|
2360
|
+
SCHEMA_TYPE_MISMATCH: "A",
|
|
2361
|
+
SCHEMA_MISSING_REQUIRED: "A",
|
|
2362
|
+
SCHEMA_UNKNOWN_FIELD: "A",
|
|
2363
|
+
SCHEMA_INVALID_LITERAL: "A",
|
|
2364
|
+
SCHEMA_EMPTY_RECORD: "A",
|
|
2365
|
+
HASH_DIGEST_LENGTH_MISMATCH: "A",
|
|
2366
|
+
UNSUPPORTED_HASH_ALG: "A",
|
|
2367
|
+
UNSUPPORTED_MERKLE_COMMIT_ALG: "A",
|
|
2368
|
+
SCHEMA_MERKLE_LEAF_COUNT_INVALID: "A",
|
|
2369
|
+
INVALID_URI: "A",
|
|
2370
|
+
CHUNK_TOO_LARGE: "carriage",
|
|
2371
|
+
UNAUTHENTICATED_CIPHER_FORBIDDEN: "A",
|
|
2372
|
+
UNSUPPORTED_AEAD_ALG: "A",
|
|
2373
|
+
NONCE_LENGTH_MISMATCH: "A",
|
|
2374
|
+
UNSUPPORTED_ENVELOPE_SCHEME: "A",
|
|
2375
|
+
ENC_UNSUPPORTED: "A",
|
|
2376
|
+
ENC_SLOTS_EMPTY: "A",
|
|
2377
|
+
ENC_SLOT_INVALID_SHAPE: "A",
|
|
2378
|
+
UNSUPPORTED_KEM_ALG: "A",
|
|
2379
|
+
ENC_KEM_REQUIRED: "A",
|
|
2380
|
+
KEM_EPK_LENGTH_MISMATCH: "A",
|
|
2381
|
+
KEM_CT_LENGTH_MISMATCH: "A",
|
|
2382
|
+
WRAP_LENGTH_MISMATCH: "A",
|
|
2383
|
+
ENC_SLOTS_MAC_INVALID_LENGTH: "A",
|
|
2384
|
+
ENC_SLOTS_MAC_REQUIRED: "A",
|
|
2385
|
+
ENC_SLOTS_REQUIRED: "A",
|
|
2386
|
+
ENC_SLOTS_DUPLICATE_KEM_MATERIAL: "A",
|
|
2387
|
+
ENC_SLOTS_TOO_MANY: "A",
|
|
2388
|
+
ENC_ENVELOPE_TOO_LARGE: "A",
|
|
2389
|
+
ENC_EXCLUSIVITY_VIOLATION: "A",
|
|
2390
|
+
ENC_NO_KEY_PATH: "A",
|
|
2391
|
+
ENC_REQUIRES_CONTENT_HASH: "A",
|
|
2392
|
+
ENC_PASSPHRASE_ALG_UNSUPPORTED: "A",
|
|
2393
|
+
ENC_PASSPHRASE_SALT_TOO_SHORT: "A",
|
|
2394
|
+
ENC_PASSPHRASE_SALT_TOO_LONG: "A",
|
|
2395
|
+
ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW: "A",
|
|
2396
|
+
ENC_PASSPHRASE_PARAMS_EXCEED_POLICY: "A",
|
|
2397
|
+
MALFORMED_SIG_COSE_SIGN1: "A",
|
|
2398
|
+
SIGNATURE_UNSUPPORTED: "A",
|
|
2399
|
+
SIG_ENTRY_INVALID_SHAPE: "A",
|
|
2400
|
+
SIG_ENTRY_KID_COSE_KEY_CONFLICT: "A",
|
|
2401
|
+
SIG_PRIVATE_KEY_LEAKED: "A",
|
|
2402
|
+
SUPERSEDES_TX_INVALID_LENGTH: "A",
|
|
2403
|
+
EXTENSION_UNSUPPORTED_CRITICAL: "A",
|
|
2404
|
+
CRIT_SHAPE_INVALID: "A",
|
|
2405
|
+
TX_NOT_FOUND: "B",
|
|
2406
|
+
PROVIDER_UNAVAILABLE: "B",
|
|
2407
|
+
TX_INTEGRITY_MISMATCH: "B",
|
|
2408
|
+
METADATA_NOT_FOUND: "B",
|
|
2409
|
+
INSUFFICIENT_CONFIRMATIONS: "B",
|
|
2410
|
+
SIGNATURE_INVALID: "B",
|
|
2411
|
+
SIGNER_KEY_UNRESOLVED: "B",
|
|
2412
|
+
WALLET_ADDRESS_MISMATCH: "B",
|
|
2413
|
+
URI_TARGET_FORBIDDEN: "B",
|
|
2414
|
+
URI_INTEGRITY_MISMATCH: "B",
|
|
2415
|
+
URI_PROVIDER_INTEGRITY_MISMATCH: "B",
|
|
2416
|
+
URI_FETCH_FAILED: "B",
|
|
2417
|
+
CONTENT_UNAVAILABLE: "B",
|
|
2418
|
+
CONTENT_FETCH_LIMIT_EXCEEDED: "B",
|
|
2419
|
+
CIPHERTEXT_UNAVAILABLE: "B",
|
|
2420
|
+
SERVICE_INDEPENDENCE_VIOLATION: "B",
|
|
2421
|
+
WRONG_DECRYPTION_INPUT_SHAPE: "B",
|
|
2422
|
+
WRONG_RECIPIENT_KEY: "B",
|
|
2423
|
+
TAMPERED_HEADER: "B",
|
|
2424
|
+
TAMPERED_CIPHERTEXT: "B",
|
|
2425
|
+
KDF_DERIVATION_FAILED: "B",
|
|
2426
|
+
ENC_PASSPHRASE_UNNORMALIZABLE: "B",
|
|
2427
|
+
ENC_PASSPHRASE_EMPTY: "B",
|
|
2428
|
+
SCHEMA_MERKLE_LEAF_COUNT_MISMATCH: "B",
|
|
2429
|
+
SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED: "B",
|
|
2430
|
+
SCHEMA_MERKLE_LEAVES_MALFORMED: "B",
|
|
2431
|
+
MERKLE_ROOT_MISMATCH: "B",
|
|
2432
|
+
MERKLE_LEAVES_UNAVAILABLE: "B",
|
|
2433
|
+
MERKLE_UNSUPPORTED: "B",
|
|
2434
|
+
OUT_OF_PROFILE_SKIPPED: "B"
|
|
2435
|
+
});
|
|
2436
|
+
Object.freeze(
|
|
2437
|
+
ERROR_CODES.filter((code) => ERROR_CODE_PART[code] === "A")
|
|
2438
|
+
);
|
|
2439
|
+
Object.freeze(
|
|
2440
|
+
ERROR_CODES.filter((code) => ERROR_CODE_PART[code] === "carriage")
|
|
2441
|
+
);
|
|
2442
|
+
Object.freeze(
|
|
2443
|
+
ERROR_CODES.filter((code) => ERROR_CODE_PART[code] === "B")
|
|
2444
|
+
);
|
|
2268
2445
|
var SEVERITY = Object.freeze({
|
|
2269
|
-
// --- Part A ---
|
|
2270
2446
|
MALFORMED_CBOR: "error",
|
|
2271
2447
|
SCHEMA_TYPE_MISMATCH: "error",
|
|
2272
2448
|
SCHEMA_MISSING_REQUIRED: "error",
|
|
@@ -2276,17 +2452,16 @@ var SEVERITY = Object.freeze({
|
|
|
2276
2452
|
HASH_DIGEST_LENGTH_MISMATCH: "error",
|
|
2277
2453
|
UNSUPPORTED_HASH_ALG: "error",
|
|
2278
2454
|
UNSUPPORTED_MERKLE_COMMIT_ALG: "error",
|
|
2455
|
+
SCHEMA_MERKLE_LEAF_COUNT_INVALID: "error",
|
|
2279
2456
|
INVALID_URI: "error",
|
|
2280
2457
|
CHUNK_TOO_LARGE: "error",
|
|
2281
2458
|
UNAUTHENTICATED_CIPHER_FORBIDDEN: "error",
|
|
2282
2459
|
UNSUPPORTED_AEAD_ALG: "error",
|
|
2283
2460
|
NONCE_LENGTH_MISMATCH: "error",
|
|
2284
2461
|
UNSUPPORTED_ENVELOPE_SCHEME: "error",
|
|
2462
|
+
ENC_UNSUPPORTED: "info",
|
|
2285
2463
|
ENC_SLOTS_EMPTY: "error",
|
|
2286
2464
|
ENC_SLOT_INVALID_SHAPE: "error",
|
|
2287
|
-
ENC_SLOTS_DUPLICATE_KEM_MATERIAL: "error",
|
|
2288
|
-
ENC_SLOTS_TOO_MANY: "error",
|
|
2289
|
-
ENC_ENVELOPE_TOO_LARGE: "error",
|
|
2290
2465
|
UNSUPPORTED_KEM_ALG: "error",
|
|
2291
2466
|
ENC_KEM_REQUIRED: "error",
|
|
2292
2467
|
KEM_EPK_LENGTH_MISMATCH: "error",
|
|
@@ -2295,6 +2470,9 @@ var SEVERITY = Object.freeze({
|
|
|
2295
2470
|
ENC_SLOTS_MAC_INVALID_LENGTH: "error",
|
|
2296
2471
|
ENC_SLOTS_MAC_REQUIRED: "error",
|
|
2297
2472
|
ENC_SLOTS_REQUIRED: "error",
|
|
2473
|
+
ENC_SLOTS_DUPLICATE_KEM_MATERIAL: "error",
|
|
2474
|
+
ENC_SLOTS_TOO_MANY: "error",
|
|
2475
|
+
ENC_ENVELOPE_TOO_LARGE: "error",
|
|
2298
2476
|
ENC_EXCLUSIVITY_VIOLATION: "error",
|
|
2299
2477
|
ENC_NO_KEY_PATH: "error",
|
|
2300
2478
|
ENC_REQUIRES_CONTENT_HASH: "error",
|
|
@@ -2311,7 +2489,9 @@ var SEVERITY = Object.freeze({
|
|
|
2311
2489
|
SUPERSEDES_TX_INVALID_LENGTH: "error",
|
|
2312
2490
|
EXTENSION_UNSUPPORTED_CRITICAL: "error",
|
|
2313
2491
|
CRIT_SHAPE_INVALID: "error",
|
|
2314
|
-
|
|
2492
|
+
TX_NOT_FOUND: "error",
|
|
2493
|
+
PROVIDER_UNAVAILABLE: "error",
|
|
2494
|
+
TX_INTEGRITY_MISMATCH: "error",
|
|
2315
2495
|
METADATA_NOT_FOUND: "error",
|
|
2316
2496
|
INSUFFICIENT_CONFIRMATIONS: "info",
|
|
2317
2497
|
SIGNATURE_INVALID: "error",
|
|
@@ -2319,119 +2499,135 @@ var SEVERITY = Object.freeze({
|
|
|
2319
2499
|
WALLET_ADDRESS_MISMATCH: "error",
|
|
2320
2500
|
URI_TARGET_FORBIDDEN: "error",
|
|
2321
2501
|
URI_INTEGRITY_MISMATCH: "error",
|
|
2502
|
+
URI_PROVIDER_INTEGRITY_MISMATCH: "warning",
|
|
2322
2503
|
URI_FETCH_FAILED: "warning",
|
|
2323
2504
|
CONTENT_UNAVAILABLE: "error",
|
|
2505
|
+
CONTENT_FETCH_LIMIT_EXCEEDED: "error",
|
|
2324
2506
|
CIPHERTEXT_UNAVAILABLE: "error",
|
|
2325
|
-
PROVIDER_UNAVAILABLE: "error",
|
|
2326
2507
|
SERVICE_INDEPENDENCE_VIOLATION: "error",
|
|
2327
2508
|
WRONG_DECRYPTION_INPUT_SHAPE: "error",
|
|
2328
2509
|
WRONG_RECIPIENT_KEY: "error",
|
|
2329
2510
|
TAMPERED_HEADER: "error",
|
|
2330
2511
|
TAMPERED_CIPHERTEXT: "error",
|
|
2331
2512
|
KDF_DERIVATION_FAILED: "error",
|
|
2513
|
+
ENC_PASSPHRASE_UNNORMALIZABLE: "error",
|
|
2514
|
+
ENC_PASSPHRASE_EMPTY: "error",
|
|
2332
2515
|
SCHEMA_MERKLE_LEAF_COUNT_MISMATCH: "error",
|
|
2333
2516
|
SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED: "error",
|
|
2334
2517
|
SCHEMA_MERKLE_LEAVES_MALFORMED: "error",
|
|
2335
2518
|
MERKLE_ROOT_MISMATCH: "error",
|
|
2336
2519
|
MERKLE_LEAVES_UNAVAILABLE: "warning",
|
|
2337
|
-
MERKLE_LEAVES_INFORMATIVE_FORM: "info",
|
|
2338
|
-
// Dual-severity — default reading is `info`; the verifier promotes to
|
|
2339
|
-
// `error` for merkle-only records (no `items[]` content claim was
|
|
2340
|
-
// validated in the same record).
|
|
2341
2520
|
MERKLE_UNSUPPORTED: "info",
|
|
2342
|
-
// Dual-severity — default reading is `info` (render mode); strict
|
|
2343
|
-
// end-to-end verifiers promote to `error`.
|
|
2344
2521
|
OUT_OF_PROFILE_SKIPPED: "info"
|
|
2345
2522
|
});
|
|
2346
|
-
var
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
zod.z.
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
},
|
|
2358
|
-
{ params: { code: "CHUNK_TOO_LARGE" } }
|
|
2359
|
-
)
|
|
2360
|
-
).min(1);
|
|
2523
|
+
var REGISTRY_INDEX = new Map(
|
|
2524
|
+
ERROR_CODES.map((code, index) => [code, index])
|
|
2525
|
+
);
|
|
2526
|
+
function errorCodeRegistryIndex(code) {
|
|
2527
|
+
return REGISTRY_INDEX.get(code);
|
|
2528
|
+
}
|
|
2529
|
+
function textKeyedMap(inner) {
|
|
2530
|
+
return zod.z.custom((value) => !(value instanceof Uint8Array), {
|
|
2531
|
+
message: "CBOR byte string present where a text-keyed map is required"
|
|
2532
|
+
}).pipe(inner);
|
|
2533
|
+
}
|
|
2361
2534
|
var HashDigestSchema = zod.z.instanceof(Uint8Array);
|
|
2362
2535
|
var HashesMapSchema = zod.z.record(zod.z.string(), HashDigestSchema);
|
|
2363
|
-
var
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
zod.z.
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
}).
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2536
|
+
var UriSchema = zod.z.string();
|
|
2537
|
+
var MerkleCommitSchema = textKeyedMap(
|
|
2538
|
+
zod.z.object({
|
|
2539
|
+
alg: zod.z.string(),
|
|
2540
|
+
root: zod.z.instanceof(Uint8Array),
|
|
2541
|
+
leaf_count: zod.z.union([zod.z.number().int(), zod.z.bigint()]),
|
|
2542
|
+
uris: zod.z.array(UriSchema).optional()
|
|
2543
|
+
}).strict()
|
|
2544
|
+
);
|
|
2545
|
+
var SlotSchema = textKeyedMap(
|
|
2546
|
+
zod.z.object({
|
|
2547
|
+
epk: zod.z.instanceof(Uint8Array).optional(),
|
|
2548
|
+
kem_ct: zod.z.instanceof(Uint8Array).optional(),
|
|
2549
|
+
wrap: zod.z.instanceof(Uint8Array).optional()
|
|
2550
|
+
})
|
|
2551
|
+
);
|
|
2552
|
+
textKeyedMap(
|
|
2553
|
+
zod.z.object({
|
|
2554
|
+
m: zod.z.union([zod.z.number().int(), zod.z.bigint()]),
|
|
2555
|
+
t: zod.z.union([zod.z.number().int(), zod.z.bigint()]),
|
|
2556
|
+
p: zod.z.union([zod.z.number().int(), zod.z.bigint()])
|
|
2557
|
+
}).strict()
|
|
2558
|
+
);
|
|
2559
|
+
var PassphraseBlockSchema = textKeyedMap(
|
|
2560
|
+
zod.z.object({
|
|
2561
|
+
alg: zod.z.string(),
|
|
2562
|
+
salt: zod.z.instanceof(Uint8Array).superRefine((bytes, ctx) => {
|
|
2563
|
+
if (bytes.length < 16) {
|
|
2564
|
+
ctx.addIssue({
|
|
2565
|
+
code: "custom",
|
|
2566
|
+
path: [],
|
|
2567
|
+
message: `passphrase.salt length ${bytes.length} < 16`,
|
|
2568
|
+
params: { code: "ENC_PASSPHRASE_SALT_TOO_SHORT" }
|
|
2569
|
+
});
|
|
2570
|
+
} else if (bytes.length > 64) {
|
|
2571
|
+
ctx.addIssue({
|
|
2572
|
+
code: "custom",
|
|
2573
|
+
path: [],
|
|
2574
|
+
message: `passphrase.salt length ${bytes.length} > 64`,
|
|
2575
|
+
params: { code: "ENC_PASSPHRASE_SALT_TOO_LONG" }
|
|
2576
|
+
});
|
|
2577
|
+
}
|
|
2578
|
+
}),
|
|
2579
|
+
params: zod.z.record(zod.z.string(), zod.z.unknown())
|
|
2580
|
+
}).strict()
|
|
2581
|
+
);
|
|
2582
|
+
var EncScheme1Schema = textKeyedMap(
|
|
2583
|
+
zod.z.object({
|
|
2584
|
+
scheme: zod.z.literal(1),
|
|
2585
|
+
aead: zod.z.string(),
|
|
2586
|
+
kem: zod.z.string().optional(),
|
|
2587
|
+
nonce: zod.z.instanceof(Uint8Array),
|
|
2588
|
+
slots: zod.z.array(SlotSchema).optional(),
|
|
2589
|
+
slots_mac: zod.z.instanceof(Uint8Array).refine((b5) => b5.length === 32, {
|
|
2590
|
+
params: { code: "ENC_SLOTS_MAC_INVALID_LENGTH" }
|
|
2591
|
+
}).optional(),
|
|
2592
|
+
passphrase: PassphraseBlockSchema.optional()
|
|
2593
|
+
}).strict()
|
|
2594
|
+
);
|
|
2595
|
+
var EncOpaqueSchema = textKeyedMap(
|
|
2596
|
+
zod.z.looseObject({
|
|
2597
|
+
scheme: zod.z.union([zod.z.number().int().nonnegative(), zod.z.bigint().nonnegative()])
|
|
2598
|
+
})
|
|
2599
|
+
);
|
|
2600
|
+
zod.z.union([EncScheme1Schema, EncOpaqueSchema]);
|
|
2601
|
+
var ItemEntrySchema = textKeyedMap(
|
|
2602
|
+
zod.z.object({
|
|
2603
|
+
hashes: HashesMapSchema,
|
|
2604
|
+
uris: zod.z.array(UriSchema).optional(),
|
|
2605
|
+
// Captured as `unknown`: the envelope is a union whose disposition
|
|
2606
|
+
// (typed scheme-1 vs opaque) depends on identifier support, so the
|
|
2607
|
+
// validator's domain pass — not this schema — narrows it.
|
|
2608
|
+
enc: zod.z.unknown().optional()
|
|
2609
|
+
}).strict()
|
|
2610
|
+
);
|
|
2611
|
+
var SigEntrySchema = textKeyedMap(
|
|
2612
|
+
zod.z.object({
|
|
2613
|
+
cose_key: zod.z.instanceof(Uint8Array).optional(),
|
|
2614
|
+
cose_sign1: zod.z.instanceof(Uint8Array)
|
|
2615
|
+
}).strict()
|
|
2616
|
+
);
|
|
2423
2617
|
var SupersedesSchema = zod.z.instanceof(Uint8Array).refine((b5) => b5.length === 32, {
|
|
2424
2618
|
params: { code: "SUPERSEDES_TX_INVALID_LENGTH" }
|
|
2425
2619
|
});
|
|
2426
2620
|
var VersionLiteralSchema = zod.z.literal(1);
|
|
2427
|
-
var PoeRecordSchema =
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2621
|
+
var PoeRecordSchema = textKeyedMap(
|
|
2622
|
+
zod.z.looseObject({
|
|
2623
|
+
v: VersionLiteralSchema,
|
|
2624
|
+
items: zod.z.array(ItemEntrySchema).optional(),
|
|
2625
|
+
merkle: zod.z.array(MerkleCommitSchema).optional(),
|
|
2626
|
+
supersedes: SupersedesSchema.optional(),
|
|
2627
|
+
sigs: zod.z.array(SigEntrySchema).optional(),
|
|
2628
|
+
crit: zod.z.array(zod.z.string()).optional()
|
|
2629
|
+
})
|
|
2630
|
+
);
|
|
2435
2631
|
var TOP_LEVEL_BASE_KEYS = /* @__PURE__ */ new Set([
|
|
2436
2632
|
"v",
|
|
2437
2633
|
"items",
|
|
@@ -2440,8 +2636,8 @@ var TOP_LEVEL_BASE_KEYS = /* @__PURE__ */ new Set([
|
|
|
2440
2636
|
"sigs",
|
|
2441
2637
|
"crit"
|
|
2442
2638
|
]);
|
|
2443
|
-
var EXTENSION_KEY_VENDOR_RE = /^x
|
|
2444
|
-
var EXTENSION_KEY_COMPANION_RE = /^[a-z]
|
|
2639
|
+
var EXTENSION_KEY_VENDOR_RE = /^x-[^\u0000-\u001f\u007f-\u009f]+$/;
|
|
2640
|
+
var EXTENSION_KEY_COMPANION_RE = /^[a-z]+-[^\u0000-\u001f\u007f-\u009f]+$/;
|
|
2445
2641
|
function isExtensionKey(k4) {
|
|
2446
2642
|
return EXTENSION_KEY_VENDOR_RE.test(k4) || EXTENSION_KEY_COMPANION_RE.test(k4);
|
|
2447
2643
|
}
|
|
@@ -2455,9 +2651,9 @@ var MERKLE_COMMIT_ALG_LENGTHS = {
|
|
|
2455
2651
|
"rfc9162-sha256": 32
|
|
2456
2652
|
};
|
|
2457
2653
|
var AEAD_NONCE_LENGTHS = {
|
|
2458
|
-
"
|
|
2654
|
+
"chacha20-poly1305-stream64k": 24
|
|
2459
2655
|
};
|
|
2460
|
-
var UNAUTHENTICATED_CIPHER_RE = /(?:^|[-_])(?:cbc|ctr|ecb|cfb|ofb)(?:[-_]
|
|
2656
|
+
var UNAUTHENTICATED_CIPHER_RE = /(?:^|[-_])(?:cbc|ctr|ecb|cfb|ofb)(?:[-_]|$)|^(?:rc4|des|3des)(?:[-_]|$)/i;
|
|
2461
2657
|
var KEM_SLOT_DESCRIPTORS = {
|
|
2462
2658
|
x25519: { field: "epk", fieldLength: 32, wrapLength: 48 },
|
|
2463
2659
|
mlkem768x25519: { field: "kem_ct", fieldLength: 1120, wrapLength: 48 }
|
|
@@ -2466,17 +2662,33 @@ var KEM_FIELD_LENGTH_CODE = {
|
|
|
2466
2662
|
epk: "KEM_EPK_LENGTH_MISMATCH",
|
|
2467
2663
|
kem_ct: "KEM_CT_LENGTH_MISMATCH"
|
|
2468
2664
|
};
|
|
2469
|
-
var NONCE_LENGTH = 24;
|
|
2470
|
-
var SLOTS_MAC_LENGTH = 32;
|
|
2471
2665
|
var PASSPHRASE_KDF_ALGS = /* @__PURE__ */ new Set(["argon2id"]);
|
|
2472
2666
|
var KNOWN_SIG_ALG_IDS = /* @__PURE__ */ new Set([-8, -19]);
|
|
2473
|
-
|
|
2667
|
+
var UINT32_MAX = 4294967295;
|
|
2668
|
+
var DEFAULT_PASSPHRASE_PARAMS_CEILING = Object.freeze({
|
|
2669
|
+
m: 2097152,
|
|
2670
|
+
// KiB = 2 GiB
|
|
2671
|
+
t: 16,
|
|
2672
|
+
p: 8
|
|
2673
|
+
});
|
|
2674
|
+
var EMPTY_EXTENSION_SET = /* @__PURE__ */ new Set();
|
|
2675
|
+
function resolveOptions(options) {
|
|
2676
|
+
return {
|
|
2677
|
+
supportedCriticalExtensions: options?.supportedCriticalExtensions ?? EMPTY_EXTENSION_SET,
|
|
2678
|
+
role: options?.role ?? "public",
|
|
2679
|
+
maxSlots: options?.maxSlots ?? MAX_SLOTS,
|
|
2680
|
+
maxEncEnvelopeBytes: options?.maxEncEnvelopeBytes ?? MAX_DECODED_ENVELOPE_BYTES,
|
|
2681
|
+
passphraseParamsCeiling: options?.passphraseParamsCeiling === void 0 ? DEFAULT_PASSPHRASE_PARAMS_CEILING : options.passphraseParamsCeiling
|
|
2682
|
+
};
|
|
2683
|
+
}
|
|
2684
|
+
function validatePoeRecord(bytes, options) {
|
|
2685
|
+
const opts = resolveOptions(options);
|
|
2474
2686
|
let decoded;
|
|
2475
2687
|
try {
|
|
2476
2688
|
decoded = decodeCanonicalCbor(bytes);
|
|
2477
2689
|
} catch (cause) {
|
|
2478
2690
|
return {
|
|
2479
|
-
|
|
2691
|
+
valid: false,
|
|
2480
2692
|
issues: [
|
|
2481
2693
|
{
|
|
2482
2694
|
code: "MALFORMED_CBOR",
|
|
@@ -2487,132 +2699,181 @@ function validatePoeRecord(bytes) {
|
|
|
2487
2699
|
]
|
|
2488
2700
|
};
|
|
2489
2701
|
}
|
|
2702
|
+
const nonTextKeyIssues = collectNonTextKeyMapIssues(decoded);
|
|
2703
|
+
if (nonTextKeyIssues.length > 0) {
|
|
2704
|
+
return { valid: false, issues: sortIssues(nonTextKeyIssues) };
|
|
2705
|
+
}
|
|
2490
2706
|
const parse = PoeRecordSchema.safeParse(decoded);
|
|
2491
2707
|
if (!parse.success) {
|
|
2492
|
-
|
|
2493
|
-
return { ok: false, issues };
|
|
2708
|
+
return { valid: false, issues: sortIssues(mapZodIssues(parse.error.issues, decoded)) };
|
|
2494
2709
|
}
|
|
2495
2710
|
const record = parse.data;
|
|
2496
|
-
const
|
|
2497
|
-
|
|
2498
|
-
const info = [];
|
|
2499
|
-
const itemsLen = Array.isArray(record.items) ? record.items.length : 0;
|
|
2500
|
-
const merkleLen = Array.isArray(record.merkle) ? record.merkle.length : 0;
|
|
2501
|
-
if (itemsLen === 0 && merkleLen === 0) {
|
|
2502
|
-
errors.push(
|
|
2503
|
-
issue(
|
|
2504
|
-
"SCHEMA_EMPTY_RECORD",
|
|
2505
|
-
[],
|
|
2506
|
-
"record must carry at least one of items[] or merkle[] non-empty"
|
|
2507
|
-
)
|
|
2508
|
-
);
|
|
2509
|
-
}
|
|
2711
|
+
const issues = [];
|
|
2712
|
+
checkContentCommitmentPresence(record, issues);
|
|
2510
2713
|
const decodedTopKeys = topLevelKeysOf(decoded);
|
|
2511
|
-
|
|
2512
|
-
for (const
|
|
2513
|
-
if (TOP_LEVEL_BASE_KEYS.has(
|
|
2514
|
-
if (isExtensionKey(
|
|
2515
|
-
|
|
2516
|
-
}
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2714
|
+
checkCrit(record, decodedTopKeys, opts.supportedCriticalExtensions, issues);
|
|
2715
|
+
for (const key of decodedTopKeys) {
|
|
2716
|
+
if (TOP_LEVEL_BASE_KEYS.has(key)) continue;
|
|
2717
|
+
if (isExtensionKey(key)) continue;
|
|
2718
|
+
issues.push(issueOf("SCHEMA_UNKNOWN_FIELD", [key], `unknown top-level field: ${key}`));
|
|
2719
|
+
}
|
|
2720
|
+
const items = record.items ?? [];
|
|
2721
|
+
for (let i = 0; i < items.length; i++) {
|
|
2722
|
+
const item = items[i];
|
|
2723
|
+
checkItemHashes(item, i, issues);
|
|
2724
|
+
if (item.uris !== void 0) checkUris(item.uris, ["items", i, "uris"], issues);
|
|
2725
|
+
if (item.enc !== void 0) checkItemEnc(item, i, opts, issues);
|
|
2726
|
+
}
|
|
2727
|
+
const merkle = record.merkle ?? [];
|
|
2728
|
+
for (let i = 0; i < merkle.length; i++) {
|
|
2729
|
+
checkMerkleCommit(merkle[i], i, issues);
|
|
2730
|
+
}
|
|
2731
|
+
if (record.sigs !== void 0) {
|
|
2732
|
+
if (record.sigs.length === 0) {
|
|
2733
|
+
issues.push(
|
|
2734
|
+
issueOf("SCHEMA_TYPE_MISMATCH", ["sigs"], "sigs[] must be non-empty when present")
|
|
2527
2735
|
);
|
|
2528
2736
|
}
|
|
2737
|
+
for (let i = 0; i < record.sigs.length; i++) {
|
|
2738
|
+
checkSigEntry(record.sigs[i], i, issues);
|
|
2739
|
+
}
|
|
2529
2740
|
}
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
if (item.uris) checkItemUris(item.uris, ["items", i, "uris"], errors);
|
|
2534
|
-
if (item.enc !== void 0) checkItemEnc(item, i, errors);
|
|
2535
|
-
}
|
|
2536
|
-
for (let i = 0; i < (record.merkle ?? []).length; i++) {
|
|
2537
|
-
const commit = record.merkle[i];
|
|
2538
|
-
checkMerkleCommit(commit, i, errors);
|
|
2741
|
+
const sorted = sortIssues(issues);
|
|
2742
|
+
if (sorted.some((issue) => issue.severity === "error")) {
|
|
2743
|
+
return { valid: false, issues: sorted };
|
|
2539
2744
|
}
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2745
|
+
const warnings = sorted.filter((issue) => issue.severity === "warning");
|
|
2746
|
+
const info = sorted.filter((issue) => issue.severity === "info");
|
|
2747
|
+
const result = { valid: true, record };
|
|
2748
|
+
if (warnings.length > 0) result.warnings = warnings;
|
|
2749
|
+
if (info.length > 0) result.info = info;
|
|
2750
|
+
return result;
|
|
2751
|
+
}
|
|
2752
|
+
function mapZodIssues(zissues, decodedRoot) {
|
|
2753
|
+
const out = [];
|
|
2754
|
+
for (const zissue of zissues) {
|
|
2755
|
+
if (zissue.code === "unrecognized_keys") {
|
|
2756
|
+
for (const key of zissue.keys) {
|
|
2757
|
+
const path = [...zissue.path, key];
|
|
2758
|
+
const code = unknownKeyCode(path);
|
|
2759
|
+
out.push(issueOf(code, path, `unrecognized key '${key}' in a closed map`));
|
|
2760
|
+
}
|
|
2761
|
+
continue;
|
|
2543
2762
|
}
|
|
2763
|
+
out.push(mapZodIssue(zissue, decodedRoot));
|
|
2544
2764
|
}
|
|
2545
|
-
|
|
2546
|
-
|
|
2765
|
+
return out;
|
|
2766
|
+
}
|
|
2767
|
+
function unknownKeyCode(path) {
|
|
2768
|
+
if (path.length >= 2 && path[0] === "sigs" && typeof path[1] === "number") {
|
|
2769
|
+
return "SIG_ENTRY_INVALID_SHAPE";
|
|
2547
2770
|
}
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2771
|
+
return "SCHEMA_UNKNOWN_FIELD";
|
|
2772
|
+
}
|
|
2773
|
+
function collectNonTextKeyMapIssues(decoded) {
|
|
2774
|
+
const issues = [];
|
|
2775
|
+
const flag = (path) => {
|
|
2776
|
+
issues.push(
|
|
2777
|
+
issueOf(
|
|
2778
|
+
"SCHEMA_TYPE_MISMATCH",
|
|
2779
|
+
path,
|
|
2780
|
+
"CBOR map carries a non-text key where a text-keyed map is required"
|
|
2781
|
+
)
|
|
2782
|
+
);
|
|
2551
2783
|
};
|
|
2552
|
-
if (
|
|
2553
|
-
|
|
2554
|
-
|
|
2784
|
+
if (decoded instanceof Map) {
|
|
2785
|
+
flag([]);
|
|
2786
|
+
return issues;
|
|
2787
|
+
}
|
|
2788
|
+
if (decoded === null || typeof decoded !== "object" || Array.isArray(decoded)) return issues;
|
|
2789
|
+
const record = decoded;
|
|
2790
|
+
for (const field of ["items", "merkle", "sigs"]) {
|
|
2791
|
+
const entries = record[field];
|
|
2792
|
+
if (!Array.isArray(entries)) continue;
|
|
2793
|
+
entries.forEach((entry, i) => {
|
|
2794
|
+
if (entry instanceof Map) {
|
|
2795
|
+
flag([field, i]);
|
|
2796
|
+
return;
|
|
2797
|
+
}
|
|
2798
|
+
if (field !== "items" || entry === null || typeof entry !== "object") return;
|
|
2799
|
+
const item = entry;
|
|
2800
|
+
if (item["hashes"] instanceof Map) flag([field, i, "hashes"]);
|
|
2801
|
+
if (item["enc"] instanceof Map) flag([field, i, "enc"]);
|
|
2802
|
+
});
|
|
2803
|
+
}
|
|
2804
|
+
return issues;
|
|
2555
2805
|
}
|
|
2556
|
-
function mapZodIssue(zissue,
|
|
2806
|
+
function mapZodIssue(zissue, decodedRoot) {
|
|
2557
2807
|
const path = zissue.path;
|
|
2558
2808
|
const explicit = zissue.params?.code;
|
|
2559
2809
|
if (explicit !== void 0) {
|
|
2560
|
-
return
|
|
2810
|
+
return issueOf(explicit, path, zissue.message);
|
|
2811
|
+
}
|
|
2812
|
+
const valueAtIssue = valueAtPath(decodedRoot, path);
|
|
2813
|
+
if (valueAtIssue instanceof Map) {
|
|
2814
|
+
return issueOf(
|
|
2815
|
+
"SCHEMA_TYPE_MISMATCH",
|
|
2816
|
+
path,
|
|
2817
|
+
"CBOR map carries a non-text key where a text-keyed map is required"
|
|
2818
|
+
);
|
|
2561
2819
|
}
|
|
2562
2820
|
const inSigsEntry = path.length >= 2 && path[0] === "sigs" && typeof path[1] === "number";
|
|
2563
|
-
const isInSlotEntry =
|
|
2564
|
-
if (path.length >= 5 && path[0] === "items" && typeof path[1] === "number" && path[2] === "enc" && path[3] === "slots" && typeof path[4] === "number") {
|
|
2565
|
-
return true;
|
|
2566
|
-
}
|
|
2567
|
-
if (path.length >= 2 && path[0] === "slots" && typeof path[1] === "number") {
|
|
2568
|
-
return true;
|
|
2569
|
-
}
|
|
2570
|
-
return false;
|
|
2571
|
-
})();
|
|
2572
|
-
const valueAtIssue = valueAtPath(decoded, path);
|
|
2821
|
+
const isInSlotEntry = path.length >= 2 && path[0] === "slots" && typeof path[1] === "number";
|
|
2573
2822
|
const isMissing = valueAtIssue === void 0;
|
|
2574
2823
|
switch (zissue.code) {
|
|
2575
2824
|
case "invalid_type":
|
|
2576
|
-
if (isInSlotEntry) return
|
|
2825
|
+
if (isInSlotEntry) return issueOf("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
|
|
2577
2826
|
if (isMissing) {
|
|
2578
|
-
if (inSigsEntry) return
|
|
2579
|
-
return
|
|
2827
|
+
if (inSigsEntry) return issueOf("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
2828
|
+
return issueOf("SCHEMA_MISSING_REQUIRED", path, zissue.message);
|
|
2580
2829
|
}
|
|
2581
|
-
if (inSigsEntry) return
|
|
2582
|
-
return
|
|
2830
|
+
if (inSigsEntry) return issueOf("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
2831
|
+
return issueOf("SCHEMA_TYPE_MISMATCH", path, zissue.message);
|
|
2583
2832
|
case "invalid_value":
|
|
2584
|
-
if (
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
path,
|
|
2588
|
-
zissue.message
|
|
2589
|
-
);
|
|
2590
|
-
}
|
|
2591
|
-
return issue("SCHEMA_INVALID_LITERAL", path, zissue.message);
|
|
2592
|
-
case "unrecognized_keys":
|
|
2593
|
-
if (isInSlotEntry) return issue("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
|
|
2594
|
-
if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
2595
|
-
return issue("SCHEMA_UNKNOWN_FIELD", path, zissue.message);
|
|
2833
|
+
if (isMissing) return issueOf("SCHEMA_MISSING_REQUIRED", path, zissue.message);
|
|
2834
|
+
return issueOf("SCHEMA_INVALID_LITERAL", path, zissue.message);
|
|
2835
|
+
case "invalid_union":
|
|
2596
2836
|
case "invalid_format":
|
|
2597
2837
|
case "too_big":
|
|
2598
2838
|
case "too_small":
|
|
2599
|
-
if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
2600
|
-
return issue("SCHEMA_TYPE_MISMATCH", path, zissue.message);
|
|
2601
|
-
case "invalid_union":
|
|
2602
2839
|
case "invalid_key":
|
|
2603
2840
|
case "invalid_element":
|
|
2604
2841
|
case "custom":
|
|
2605
2842
|
default:
|
|
2606
|
-
if (isInSlotEntry) return
|
|
2607
|
-
if (inSigsEntry) return
|
|
2608
|
-
return
|
|
2843
|
+
if (isInSlotEntry) return issueOf("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
|
|
2844
|
+
if (inSigsEntry) return issueOf("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
2845
|
+
return issueOf("SCHEMA_TYPE_MISMATCH", path, zissue.message);
|
|
2609
2846
|
}
|
|
2610
2847
|
}
|
|
2611
|
-
function
|
|
2848
|
+
function checkContentCommitmentPresence(record, issues) {
|
|
2849
|
+
const itemsLen = record.items?.length ?? 0;
|
|
2850
|
+
const merkleLen = record.merkle?.length ?? 0;
|
|
2851
|
+
if (itemsLen === 0 && merkleLen === 0) {
|
|
2852
|
+
issues.push(
|
|
2853
|
+
issueOf(
|
|
2854
|
+
"SCHEMA_EMPTY_RECORD",
|
|
2855
|
+
[],
|
|
2856
|
+
"record must carry at least one of items[] or merkle[] non-empty"
|
|
2857
|
+
)
|
|
2858
|
+
);
|
|
2859
|
+
return;
|
|
2860
|
+
}
|
|
2861
|
+
if (record.items !== void 0 && itemsLen === 0) {
|
|
2862
|
+
issues.push(
|
|
2863
|
+
issueOf("SCHEMA_TYPE_MISMATCH", ["items"], "items[] must be non-empty when present")
|
|
2864
|
+
);
|
|
2865
|
+
}
|
|
2866
|
+
if (record.merkle !== void 0 && merkleLen === 0) {
|
|
2867
|
+
issues.push(
|
|
2868
|
+
issueOf("SCHEMA_TYPE_MISMATCH", ["merkle"], "merkle[] must be non-empty when present")
|
|
2869
|
+
);
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
function checkItemHashes(item, idx, issues) {
|
|
2612
2873
|
const entries = Object.entries(item.hashes);
|
|
2613
2874
|
if (entries.length === 0) {
|
|
2614
|
-
|
|
2615
|
-
|
|
2875
|
+
issues.push(
|
|
2876
|
+
issueOf(
|
|
2616
2877
|
"SCHEMA_TYPE_MISMATCH",
|
|
2617
2878
|
["items", idx, "hashes"],
|
|
2618
2879
|
"hashes must be a non-empty CBOR map of <alg-id> -> <digest>"
|
|
@@ -2622,15 +2883,15 @@ function checkItemHashes(item, idx, errors) {
|
|
|
2622
2883
|
}
|
|
2623
2884
|
for (const [alg, digest] of entries) {
|
|
2624
2885
|
if (!(alg in HASH_ALG_LENGTHS)) {
|
|
2625
|
-
|
|
2626
|
-
|
|
2886
|
+
issues.push(
|
|
2887
|
+
issueOf("UNSUPPORTED_HASH_ALG", ["items", idx, "hashes", alg], `unknown hash alg: ${alg}`)
|
|
2627
2888
|
);
|
|
2628
2889
|
continue;
|
|
2629
2890
|
}
|
|
2630
2891
|
const expected = HASH_ALG_LENGTHS[alg];
|
|
2631
2892
|
if (digest.length !== expected) {
|
|
2632
|
-
|
|
2633
|
-
|
|
2893
|
+
issues.push(
|
|
2894
|
+
issueOf(
|
|
2634
2895
|
"HASH_DIGEST_LENGTH_MISMATCH",
|
|
2635
2896
|
["items", idx, "hashes", alg],
|
|
2636
2897
|
`hashes['${alg}'] digest length ${digest.length} != ${expected}`
|
|
@@ -2639,35 +2900,33 @@ function checkItemHashes(item, idx, errors) {
|
|
|
2639
2900
|
}
|
|
2640
2901
|
}
|
|
2641
2902
|
}
|
|
2642
|
-
function
|
|
2643
|
-
uris.
|
|
2644
|
-
|
|
2645
|
-
function validateOneUri(chunks, path, errors) {
|
|
2646
|
-
const reconstructed = reconstructChunkedUri(chunks);
|
|
2647
|
-
if (!reconstructed.ok) {
|
|
2648
|
-
errors.push(issue(reconstructed.code, path, reconstructed.reason));
|
|
2903
|
+
function checkUris(uris, basePath, issues) {
|
|
2904
|
+
if (uris.length === 0) {
|
|
2905
|
+
issues.push(issueOf("SCHEMA_TYPE_MISMATCH", basePath, "uris[] must be non-empty when present"));
|
|
2649
2906
|
return;
|
|
2650
2907
|
}
|
|
2651
|
-
|
|
2908
|
+
uris.forEach((uri, ui) => checkOneUri(uri, [...basePath, ui], issues));
|
|
2909
|
+
}
|
|
2910
|
+
function checkOneUri(uri, path, issues) {
|
|
2652
2911
|
if (uri.includes("#")) {
|
|
2653
|
-
|
|
2654
|
-
|
|
2912
|
+
issues.push(
|
|
2913
|
+
issueOf("INVALID_URI", path, "URI contains a fragment identifier ('#'), which is forbidden")
|
|
2655
2914
|
);
|
|
2656
2915
|
return;
|
|
2657
2916
|
}
|
|
2658
2917
|
const sepIdx = uri.indexOf("://");
|
|
2659
2918
|
if (sepIdx <= 0 || !/^[a-z][a-z0-9+.-]*$/i.test(uri.slice(0, sepIdx))) {
|
|
2660
|
-
|
|
2661
|
-
|
|
2919
|
+
issues.push(
|
|
2920
|
+
issueOf("INVALID_URI", path, "URI is not absolute (missing scheme://hierarchical-part)")
|
|
2662
2921
|
);
|
|
2663
2922
|
return;
|
|
2664
2923
|
}
|
|
2665
2924
|
const scheme = uri.slice(0, sepIdx).toLowerCase();
|
|
2666
2925
|
const rest = uri.slice(sepIdx + "://".length);
|
|
2667
2926
|
if (scheme === "ar") {
|
|
2668
|
-
if (!/^
|
|
2669
|
-
|
|
2670
|
-
|
|
2927
|
+
if (!/^[A-Za-z0-9_-]{43}$/.test(rest)) {
|
|
2928
|
+
issues.push(
|
|
2929
|
+
issueOf(
|
|
2671
2930
|
"INVALID_URI",
|
|
2672
2931
|
path,
|
|
2673
2932
|
"ar:// URI does not match `^ar://[A-Za-z0-9_-]{43}$` (43-char base64url txid, no path/query/fragment)"
|
|
@@ -2680,315 +2939,286 @@ function validateOneUri(chunks, path, errors) {
|
|
|
2680
2939
|
const slashIdx = rest.indexOf("/");
|
|
2681
2940
|
const cid = slashIdx === -1 ? rest : rest.slice(0, slashIdx);
|
|
2682
2941
|
if (!validateCidProfile(cid)) {
|
|
2683
|
-
|
|
2684
|
-
|
|
2942
|
+
issues.push(
|
|
2943
|
+
issueOf("INVALID_URI", path, "ipfs:// URI is not a valid CID under the Label 309 profile")
|
|
2685
2944
|
);
|
|
2686
2945
|
}
|
|
2687
2946
|
return;
|
|
2688
2947
|
}
|
|
2689
|
-
|
|
2690
|
-
|
|
2948
|
+
issues.push(
|
|
2949
|
+
issueOf("INVALID_URI", path, "unsupported URI scheme; v1 PoE URI set is {ar://, ipfs://}")
|
|
2691
2950
|
);
|
|
2692
2951
|
}
|
|
2693
|
-
function checkItemEnc(item, idx,
|
|
2952
|
+
function checkItemEnc(item, idx, opts, issues) {
|
|
2953
|
+
const encPath = ["items", idx, "enc"];
|
|
2694
2954
|
const hasContentHash = Object.keys(item.hashes).some((alg) => alg in HASH_ALG_LENGTHS);
|
|
2695
2955
|
if (!hasContentHash) {
|
|
2696
|
-
|
|
2697
|
-
|
|
2956
|
+
issues.push(
|
|
2957
|
+
issueOf(
|
|
2698
2958
|
"ENC_REQUIRES_CONTENT_HASH",
|
|
2699
|
-
|
|
2700
|
-
"item carries `enc` but `hashes` has no content-hash entry (sha2-256 or blake2b-256)"
|
|
2959
|
+
encPath,
|
|
2960
|
+
"item carries `enc` but `hashes` has no registered content-hash entry (sha2-256 or blake2b-256)"
|
|
2701
2961
|
)
|
|
2702
2962
|
);
|
|
2963
|
+
}
|
|
2964
|
+
const rawEnc = item.enc;
|
|
2965
|
+
if (rawEnc === null || typeof rawEnc !== "object" || Array.isArray(rawEnc) || rawEnc instanceof Uint8Array) {
|
|
2966
|
+
issues.push(issueOf("SCHEMA_TYPE_MISMATCH", encPath, "enc must be a CBOR map"));
|
|
2703
2967
|
return;
|
|
2704
2968
|
}
|
|
2705
|
-
const
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2969
|
+
const enc = rawEnc;
|
|
2970
|
+
const envelopeBytes = encodeCanonicalCbor(rawEnc).length;
|
|
2971
|
+
if (envelopeBytes > opts.maxEncEnvelopeBytes) {
|
|
2972
|
+
issues.push(
|
|
2973
|
+
issueOf(
|
|
2974
|
+
"ENC_ENVELOPE_TOO_LARGE",
|
|
2975
|
+
encPath,
|
|
2976
|
+
`decoded envelope is ${envelopeBytes} bytes; the resource bound is ${opts.maxEncEnvelopeBytes}`
|
|
2977
|
+
)
|
|
2978
|
+
);
|
|
2979
|
+
}
|
|
2980
|
+
const scheme = enc["scheme"];
|
|
2981
|
+
if (scheme === void 0) {
|
|
2982
|
+
issues.push(
|
|
2983
|
+
issueOf("SCHEMA_MISSING_REQUIRED", [...encPath, "scheme"], "enc.scheme is required")
|
|
2984
|
+
);
|
|
2714
2985
|
return;
|
|
2715
2986
|
}
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
"
|
|
2722
|
-
[...basePath, "scheme"],
|
|
2723
|
-
`enc.scheme must be the unsigned integer 1; got ${String(enc.scheme)}`
|
|
2987
|
+
if (!isUint(scheme)) {
|
|
2988
|
+
issues.push(
|
|
2989
|
+
issueOf(
|
|
2990
|
+
"SCHEMA_TYPE_MISMATCH",
|
|
2991
|
+
[...encPath, "scheme"],
|
|
2992
|
+
"enc.scheme must be a CBOR unsigned integer"
|
|
2724
2993
|
)
|
|
2725
2994
|
);
|
|
2995
|
+
return;
|
|
2726
2996
|
}
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2997
|
+
const aead = enc["aead"];
|
|
2998
|
+
if (typeof aead === "string" && UNAUTHENTICATED_CIPHER_RE.test(aead)) {
|
|
2999
|
+
issues.push(
|
|
3000
|
+
issueOf(
|
|
2730
3001
|
"UNAUTHENTICATED_CIPHER_FORBIDDEN",
|
|
2731
|
-
[...
|
|
2732
|
-
`'${
|
|
3002
|
+
[...encPath, "aead"],
|
|
3003
|
+
`'${aead}' is an unauthenticated cipher; Label 309 mandates an authenticated (AEAD) cipher`
|
|
2733
3004
|
)
|
|
2734
3005
|
);
|
|
2735
3006
|
return;
|
|
2736
3007
|
}
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
);
|
|
3008
|
+
const kem = enc["kem"];
|
|
3009
|
+
const unsupported = [];
|
|
3010
|
+
if (!(typeof scheme === "number" && scheme === 1)) {
|
|
3011
|
+
unsupported.push({ field: "scheme", code: "UNSUPPORTED_ENVELOPE_SCHEME", id: String(scheme) });
|
|
3012
|
+
}
|
|
3013
|
+
if (typeof kem === "string" && !(kem in KEM_SLOT_DESCRIPTORS)) {
|
|
3014
|
+
unsupported.push({ field: "kem", code: "UNSUPPORTED_KEM_ALG", id: kem });
|
|
3015
|
+
}
|
|
3016
|
+
if (typeof aead === "string" && !(aead in AEAD_NONCE_LENGTHS)) {
|
|
3017
|
+
unsupported.push({ field: "aead", code: "UNSUPPORTED_AEAD_ALG", id: aead });
|
|
3018
|
+
}
|
|
3019
|
+
if (unsupported.length > 0) {
|
|
3020
|
+
const named = unsupported.map((u3) => `${u3.field}=${u3.id}`).join(", ");
|
|
3021
|
+
const message = `envelope uses identifiers this implementation does not support (${named}); the envelope is opaque and only the content-hash claim is validated`;
|
|
3022
|
+
if (opts.role === "recipient_or_strict") {
|
|
3023
|
+
issues.push({ code: "ENC_UNSUPPORTED", path: encPath, message, severity: "error" });
|
|
3024
|
+
for (const u3 of unsupported) {
|
|
3025
|
+
issues.push(
|
|
3026
|
+
issueOf(u3.code, [...encPath, u3.field], `enc.${u3.field} '${u3.id}' is not supported`)
|
|
3027
|
+
);
|
|
3028
|
+
}
|
|
3029
|
+
} else {
|
|
3030
|
+
issues.push({ code: "ENC_UNSUPPORTED", path: encPath, message, severity: "info" });
|
|
3031
|
+
}
|
|
3032
|
+
return;
|
|
3033
|
+
}
|
|
3034
|
+
const internalMapIssues = encInternalNonTextKeyIssues(enc, encPath);
|
|
3035
|
+
if (internalMapIssues.length > 0) {
|
|
3036
|
+
issues.push(...internalMapIssues);
|
|
3037
|
+
return;
|
|
3038
|
+
}
|
|
3039
|
+
const encParse = EncScheme1Schema.safeParse(rawEnc);
|
|
3040
|
+
if (!encParse.success) {
|
|
3041
|
+
for (const mapped of mapZodIssues(encParse.error.issues, rawEnc)) {
|
|
3042
|
+
issues.push({ ...mapped, path: [...encPath, ...mapped.path] });
|
|
3043
|
+
}
|
|
2741
3044
|
return;
|
|
2742
3045
|
}
|
|
3046
|
+
checkScheme1Envelope(encParse.data, rawEnc, encPath, opts, issues);
|
|
3047
|
+
}
|
|
3048
|
+
function encInternalNonTextKeyIssues(enc, encPath) {
|
|
3049
|
+
const issues = [];
|
|
3050
|
+
const flag = (path) => {
|
|
3051
|
+
issues.push(
|
|
3052
|
+
issueOf(
|
|
3053
|
+
"SCHEMA_TYPE_MISMATCH",
|
|
3054
|
+
path,
|
|
3055
|
+
"CBOR map carries a non-text key where a text-keyed map is required"
|
|
3056
|
+
)
|
|
3057
|
+
);
|
|
3058
|
+
};
|
|
3059
|
+
const slots = enc["slots"];
|
|
3060
|
+
if (Array.isArray(slots)) {
|
|
3061
|
+
slots.forEach((slot, i) => {
|
|
3062
|
+
if (slot instanceof Map) flag([...encPath, "slots", i]);
|
|
3063
|
+
});
|
|
3064
|
+
}
|
|
3065
|
+
const passphrase = enc["passphrase"];
|
|
3066
|
+
if (passphrase instanceof Map) {
|
|
3067
|
+
flag([...encPath, "passphrase"]);
|
|
3068
|
+
} else if (passphrase !== null && typeof passphrase === "object" && !Array.isArray(passphrase)) {
|
|
3069
|
+
const params = passphrase["params"];
|
|
3070
|
+
if (params instanceof Map) flag([...encPath, "passphrase", "params"]);
|
|
3071
|
+
}
|
|
3072
|
+
return issues;
|
|
3073
|
+
}
|
|
3074
|
+
function checkScheme1Envelope(enc, rawEnc, encPath, opts, issues) {
|
|
2743
3075
|
const expectedNonceLen = AEAD_NONCE_LENGTHS[enc.aead];
|
|
2744
3076
|
if (enc.nonce.length !== expectedNonceLen) {
|
|
2745
|
-
|
|
2746
|
-
|
|
3077
|
+
issues.push(
|
|
3078
|
+
issueOf(
|
|
2747
3079
|
"NONCE_LENGTH_MISMATCH",
|
|
2748
|
-
[...
|
|
3080
|
+
[...encPath, "nonce"],
|
|
2749
3081
|
`nonce length ${enc.nonce.length} != ${expectedNonceLen} for ${enc.aead}`
|
|
2750
3082
|
)
|
|
2751
3083
|
);
|
|
2752
3084
|
}
|
|
2753
|
-
if (enc.kem !== void 0 && !(enc.kem in KEM_SLOT_DESCRIPTORS)) {
|
|
2754
|
-
errors.push(issue("UNSUPPORTED_KEM_ALG", [...basePath, "kem"], `unknown kem alg: ${enc.kem}`));
|
|
2755
|
-
}
|
|
2756
3085
|
const hasSlots = enc.slots !== void 0;
|
|
2757
3086
|
const hasSlotsMac = enc.slots_mac !== void 0;
|
|
2758
3087
|
const hasPassphrase = enc.passphrase !== void 0;
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
3088
|
+
const hasKem = enc.kem !== void 0;
|
|
3089
|
+
if (hasPassphrase && (hasSlots || hasSlotsMac || hasKem)) {
|
|
3090
|
+
issues.push(
|
|
3091
|
+
issueOf(
|
|
3092
|
+
"ENC_EXCLUSIVITY_VIOLATION",
|
|
3093
|
+
encPath,
|
|
3094
|
+
"enc.passphrase is mutually exclusive with kem / slots / slots_mac; exactly one key path is allowed"
|
|
3095
|
+
)
|
|
2762
3096
|
);
|
|
2763
3097
|
}
|
|
2764
3098
|
if (hasSlots && !hasSlotsMac) {
|
|
2765
|
-
|
|
2766
|
-
|
|
3099
|
+
issues.push(
|
|
3100
|
+
issueOf("ENC_SLOTS_MAC_REQUIRED", encPath, "enc.slots present but enc.slots_mac absent")
|
|
2767
3101
|
);
|
|
2768
3102
|
}
|
|
2769
3103
|
if (hasSlotsMac && !hasSlots) {
|
|
2770
|
-
|
|
2771
|
-
|
|
3104
|
+
issues.push(
|
|
3105
|
+
issueOf("ENC_SLOTS_REQUIRED", encPath, "enc.slots_mac present but enc.slots absent")
|
|
2772
3106
|
);
|
|
2773
3107
|
}
|
|
2774
|
-
if (hasSlots &&
|
|
2775
|
-
|
|
3108
|
+
if (hasSlots && !hasKem) {
|
|
3109
|
+
issues.push(issueOf("ENC_KEM_REQUIRED", encPath, "enc.slots present but enc.kem absent"));
|
|
2776
3110
|
}
|
|
2777
3111
|
if (!hasSlots && !hasPassphrase) {
|
|
2778
|
-
|
|
2779
|
-
|
|
3112
|
+
issues.push(
|
|
3113
|
+
issueOf(
|
|
2780
3114
|
"ENC_NO_KEY_PATH",
|
|
2781
|
-
|
|
3115
|
+
encPath,
|
|
2782
3116
|
"enc requires either slots or passphrase \u2014 no on-chain key path otherwise"
|
|
2783
3117
|
)
|
|
2784
3118
|
);
|
|
2785
3119
|
}
|
|
2786
3120
|
if (hasSlots) {
|
|
2787
|
-
const
|
|
2788
|
-
if (
|
|
2789
|
-
|
|
2790
|
-
|
|
3121
|
+
const slots = enc.slots;
|
|
3122
|
+
if (slots.length < 1) {
|
|
3123
|
+
issues.push(
|
|
3124
|
+
issueOf("ENC_SLOTS_EMPTY", [...encPath, "slots"], "slots[] must carry at least one slot")
|
|
2791
3125
|
);
|
|
2792
|
-
} else if (
|
|
2793
|
-
|
|
2794
|
-
|
|
3126
|
+
} else if (slots.length > opts.maxSlots) {
|
|
3127
|
+
issues.push(
|
|
3128
|
+
issueOf(
|
|
2795
3129
|
"ENC_SLOTS_TOO_MANY",
|
|
2796
|
-
[...
|
|
2797
|
-
`slots length ${
|
|
3130
|
+
[...encPath, "slots"],
|
|
3131
|
+
`slots length ${slots.length} exceeds the slot-count bound ${opts.maxSlots}`
|
|
2798
3132
|
)
|
|
2799
3133
|
);
|
|
2800
|
-
} else {
|
|
2801
|
-
const descriptor =
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
seenKemMaterial.add(key);
|
|
2828
|
-
}
|
|
3134
|
+
} else if (hasKem) {
|
|
3135
|
+
const descriptor = KEM_SLOT_DESCRIPTORS[enc.kem];
|
|
3136
|
+
const rawSlotKeys = rawSlotKeySets(rawEnc);
|
|
3137
|
+
const seenKemMaterial = /* @__PURE__ */ new Set();
|
|
3138
|
+
slots.forEach((slot, si) => {
|
|
3139
|
+
const slotPath = [...encPath, "slots", si];
|
|
3140
|
+
checkSlotShape(
|
|
3141
|
+
slot,
|
|
3142
|
+
rawSlotKeys[si] ?? /* @__PURE__ */ new Set(),
|
|
3143
|
+
descriptor,
|
|
3144
|
+
enc.kem,
|
|
3145
|
+
slotPath,
|
|
3146
|
+
issues
|
|
3147
|
+
);
|
|
3148
|
+
const material = descriptor.field === "epk" ? slot.epk : slot.kem_ct;
|
|
3149
|
+
if (material !== void 0) {
|
|
3150
|
+
const key = bytesToHex2(material);
|
|
3151
|
+
if (seenKemMaterial.has(key)) {
|
|
3152
|
+
issues.push(
|
|
3153
|
+
issueOf(
|
|
3154
|
+
"ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
|
|
3155
|
+
[...slotPath, descriptor.field],
|
|
3156
|
+
`slot ${si} ${descriptor.field} duplicates an earlier slot \u2014 per-slot KEK uniqueness is violated`
|
|
3157
|
+
)
|
|
3158
|
+
);
|
|
3159
|
+
} else {
|
|
3160
|
+
seenKemMaterial.add(key);
|
|
2829
3161
|
}
|
|
2830
|
-
});
|
|
2831
|
-
const perSlotBytes = descriptor.fieldLength + descriptor.wrapLength;
|
|
2832
|
-
const decodedEnvelopeBytes = NONCE_LENGTH + SLOTS_MAC_LENGTH + slotCount * perSlotBytes;
|
|
2833
|
-
if (decodedEnvelopeBytes > MAX_DECODED_ENVELOPE_BYTES) {
|
|
2834
|
-
errors.push(
|
|
2835
|
-
issue(
|
|
2836
|
-
"ENC_ENVELOPE_TOO_LARGE",
|
|
2837
|
-
[...basePath, "slots"],
|
|
2838
|
-
`decoded envelope size ${decodedEnvelopeBytes} exceeds MAX_DECODED_ENVELOPE_BYTES=${MAX_DECODED_ENVELOPE_BYTES}`
|
|
2839
|
-
)
|
|
2840
|
-
);
|
|
2841
3162
|
}
|
|
2842
|
-
}
|
|
3163
|
+
});
|
|
2843
3164
|
}
|
|
2844
3165
|
}
|
|
2845
3166
|
if (hasPassphrase) {
|
|
2846
|
-
|
|
2847
|
-
const ppPath = [...basePath, "passphrase"];
|
|
2848
|
-
if (!PASSPHRASE_KDF_ALGS.has(pp.alg)) {
|
|
2849
|
-
errors.push(
|
|
2850
|
-
issue(
|
|
2851
|
-
"ENC_PASSPHRASE_ALG_UNSUPPORTED",
|
|
2852
|
-
[...ppPath, "alg"],
|
|
2853
|
-
`unknown passphrase kdf alg: ${pp.alg}`
|
|
2854
|
-
)
|
|
2855
|
-
);
|
|
2856
|
-
return;
|
|
2857
|
-
}
|
|
2858
|
-
if (pp.alg === "argon2id") {
|
|
2859
|
-
const allowed = /* @__PURE__ */ new Set(["m", "t", "p"]);
|
|
2860
|
-
for (const k4 of Object.keys(pp.params)) {
|
|
2861
|
-
if (!allowed.has(k4)) {
|
|
2862
|
-
errors.push(
|
|
2863
|
-
issue(
|
|
2864
|
-
"SCHEMA_UNKNOWN_FIELD",
|
|
2865
|
-
[...ppPath, "params", k4],
|
|
2866
|
-
`unknown argon2id params field: ${k4}`
|
|
2867
|
-
)
|
|
2868
|
-
);
|
|
2869
|
-
}
|
|
2870
|
-
}
|
|
2871
|
-
const p5 = pp.params;
|
|
2872
|
-
const argonInt = (val, name) => {
|
|
2873
|
-
if (typeof val !== "number" || !Number.isInteger(val)) {
|
|
2874
|
-
errors.push(
|
|
2875
|
-
issue(
|
|
2876
|
-
"SCHEMA_TYPE_MISMATCH",
|
|
2877
|
-
[...ppPath, "params", name],
|
|
2878
|
-
`argon2id params.${name} must be a CBOR unsigned integer`
|
|
2879
|
-
)
|
|
2880
|
-
);
|
|
2881
|
-
return null;
|
|
2882
|
-
}
|
|
2883
|
-
return val;
|
|
2884
|
-
};
|
|
2885
|
-
const mVal = argonInt(p5.m, "m");
|
|
2886
|
-
const tVal = argonInt(p5.t, "t");
|
|
2887
|
-
const pVal = argonInt(p5.p, "p");
|
|
2888
|
-
if (mVal !== null && mVal < 65536) {
|
|
2889
|
-
errors.push(
|
|
2890
|
-
issue(
|
|
2891
|
-
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
2892
|
-
[...ppPath, "params", "m"],
|
|
2893
|
-
"argon2id requires m >= 65536 KiB"
|
|
2894
|
-
)
|
|
2895
|
-
);
|
|
2896
|
-
}
|
|
2897
|
-
if (tVal !== null && tVal < 3) {
|
|
2898
|
-
errors.push(
|
|
2899
|
-
issue(
|
|
2900
|
-
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
2901
|
-
[...ppPath, "params", "t"],
|
|
2902
|
-
"argon2id requires t >= 3"
|
|
2903
|
-
)
|
|
2904
|
-
);
|
|
2905
|
-
}
|
|
2906
|
-
if (pVal !== null && pVal < 1) {
|
|
2907
|
-
errors.push(
|
|
2908
|
-
issue(
|
|
2909
|
-
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
2910
|
-
[...ppPath, "params", "p"],
|
|
2911
|
-
"argon2id requires p >= 1"
|
|
2912
|
-
)
|
|
2913
|
-
);
|
|
2914
|
-
}
|
|
2915
|
-
}
|
|
3167
|
+
checkPassphraseBlock(enc.passphrase, [...encPath, "passphrase"], opts, issues);
|
|
2916
3168
|
}
|
|
2917
3169
|
}
|
|
2918
3170
|
var SLOT_KEY_UNIVERSE = /* @__PURE__ */ new Set(["epk", "kem_ct", "wrap"]);
|
|
2919
|
-
function checkSlotShape(slot, rawKeys, descriptor, kem, slotPath,
|
|
3171
|
+
function checkSlotShape(slot, rawKeys, descriptor, kem, slotPath, issues) {
|
|
2920
3172
|
const foreignField = descriptor.field === "epk" ? "kem_ct" : "epk";
|
|
2921
3173
|
if (rawKeys.has(foreignField)) {
|
|
2922
|
-
|
|
2923
|
-
|
|
3174
|
+
issues.push(
|
|
3175
|
+
issueOf(
|
|
2924
3176
|
"ENC_SLOT_INVALID_SHAPE",
|
|
2925
3177
|
[...slotPath, foreignField],
|
|
2926
3178
|
`slot carries '${foreignField}' but kem='${kem}' expects '${descriptor.field}'`
|
|
2927
3179
|
)
|
|
2928
3180
|
);
|
|
2929
3181
|
}
|
|
2930
|
-
for (const
|
|
2931
|
-
if (!SLOT_KEY_UNIVERSE.has(
|
|
2932
|
-
|
|
2933
|
-
|
|
3182
|
+
for (const key of rawKeys) {
|
|
3183
|
+
if (!SLOT_KEY_UNIVERSE.has(key)) {
|
|
3184
|
+
issues.push(
|
|
3185
|
+
issueOf(
|
|
2934
3186
|
"ENC_SLOT_INVALID_SHAPE",
|
|
2935
|
-
[...slotPath,
|
|
2936
|
-
`slot carries unexpected key '${
|
|
3187
|
+
[...slotPath, key],
|
|
3188
|
+
`slot carries unexpected key '${key}'; a slot is a 2-key map {${descriptor.field}, wrap}`
|
|
2937
3189
|
)
|
|
2938
3190
|
);
|
|
2939
3191
|
}
|
|
2940
3192
|
}
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
}
|
|
2959
|
-
} else {
|
|
2960
|
-
if (slot.kem_ct === void 0) {
|
|
2961
|
-
errors.push(
|
|
2962
|
-
issue(
|
|
2963
|
-
"ENC_SLOT_INVALID_SHAPE",
|
|
2964
|
-
[...slotPath, "kem_ct"],
|
|
2965
|
-
`slot for kem='${kem}' is missing required 'kem_ct'`
|
|
2966
|
-
)
|
|
2967
|
-
);
|
|
2968
|
-
} else {
|
|
2969
|
-
const reassembled = bytesChunkArrayConcat(slot.kem_ct).length;
|
|
2970
|
-
if (reassembled !== descriptor.fieldLength) {
|
|
2971
|
-
errors.push(
|
|
2972
|
-
issue(
|
|
2973
|
-
KEM_FIELD_LENGTH_CODE.kem_ct,
|
|
2974
|
-
[...slotPath, "kem_ct"],
|
|
2975
|
-
`slot.kem_ct reassembles to ${reassembled} bytes != ${descriptor.fieldLength} for ${kem}`
|
|
2976
|
-
)
|
|
2977
|
-
);
|
|
2978
|
-
}
|
|
2979
|
-
}
|
|
3193
|
+
const ctField = descriptor.field === "epk" ? slot.epk : slot.kem_ct;
|
|
3194
|
+
if (ctField === void 0) {
|
|
3195
|
+
issues.push(
|
|
3196
|
+
issueOf(
|
|
3197
|
+
"ENC_SLOT_INVALID_SHAPE",
|
|
3198
|
+
[...slotPath, descriptor.field],
|
|
3199
|
+
`slot for kem='${kem}' is missing required '${descriptor.field}'`
|
|
3200
|
+
)
|
|
3201
|
+
);
|
|
3202
|
+
} else if (ctField.length !== descriptor.fieldLength) {
|
|
3203
|
+
issues.push(
|
|
3204
|
+
issueOf(
|
|
3205
|
+
KEM_FIELD_LENGTH_CODE[descriptor.field],
|
|
3206
|
+
[...slotPath, descriptor.field],
|
|
3207
|
+
`slot.${descriptor.field} length ${ctField.length} != ${descriptor.fieldLength} for ${kem}`
|
|
3208
|
+
)
|
|
3209
|
+
);
|
|
2980
3210
|
}
|
|
2981
3211
|
if (slot.wrap === void 0) {
|
|
2982
|
-
|
|
2983
|
-
|
|
3212
|
+
issues.push(
|
|
3213
|
+
issueOf(
|
|
2984
3214
|
"ENC_SLOT_INVALID_SHAPE",
|
|
2985
3215
|
[...slotPath, "wrap"],
|
|
2986
3216
|
`slot for kem='${kem}' is missing required 'wrap'`
|
|
2987
3217
|
)
|
|
2988
3218
|
);
|
|
2989
3219
|
} else if (slot.wrap.length !== descriptor.wrapLength) {
|
|
2990
|
-
|
|
2991
|
-
|
|
3220
|
+
issues.push(
|
|
3221
|
+
issueOf(
|
|
2992
3222
|
"WRAP_LENGTH_MISMATCH",
|
|
2993
3223
|
[...slotPath, "wrap"],
|
|
2994
3224
|
`slot.wrap length ${slot.wrap.length} != ${descriptor.wrapLength}`
|
|
@@ -2996,22 +3226,88 @@ function checkSlotShape(slot, rawKeys, descriptor, kem, slotPath, errors) {
|
|
|
2996
3226
|
);
|
|
2997
3227
|
}
|
|
2998
3228
|
}
|
|
2999
|
-
function
|
|
3000
|
-
if (
|
|
3001
|
-
|
|
3229
|
+
function checkPassphraseBlock(pp, ppPath, opts, issues) {
|
|
3230
|
+
if (!PASSPHRASE_KDF_ALGS.has(pp.alg)) {
|
|
3231
|
+
issues.push(
|
|
3232
|
+
issueOf(
|
|
3233
|
+
"ENC_PASSPHRASE_ALG_UNSUPPORTED",
|
|
3234
|
+
[...ppPath, "alg"],
|
|
3235
|
+
`unknown passphrase kdf alg: ${pp.alg}`
|
|
3236
|
+
)
|
|
3237
|
+
);
|
|
3238
|
+
return;
|
|
3002
3239
|
}
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3240
|
+
const paramsPath = [...ppPath, "params"];
|
|
3241
|
+
const params = pp.params;
|
|
3242
|
+
for (const key of Object.keys(params)) {
|
|
3243
|
+
if (key !== "m" && key !== "t" && key !== "p") {
|
|
3244
|
+
issues.push(
|
|
3245
|
+
issueOf(
|
|
3246
|
+
"SCHEMA_UNKNOWN_FIELD",
|
|
3247
|
+
[...paramsPath, key],
|
|
3248
|
+
`unknown argon2id params field: ${key}`
|
|
3249
|
+
)
|
|
3250
|
+
);
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
const floors = { m: 65536, t: 3, p: 1 };
|
|
3254
|
+
const ceiling = opts.passphraseParamsCeiling;
|
|
3255
|
+
for (const name of ["m", "t", "p"]) {
|
|
3256
|
+
const value = params[name];
|
|
3257
|
+
if (value === void 0) {
|
|
3258
|
+
issues.push(
|
|
3259
|
+
issueOf(
|
|
3260
|
+
"SCHEMA_MISSING_REQUIRED",
|
|
3261
|
+
[...paramsPath, name],
|
|
3262
|
+
`argon2id params.${name} is required`
|
|
3263
|
+
)
|
|
3264
|
+
);
|
|
3265
|
+
continue;
|
|
3266
|
+
}
|
|
3267
|
+
if (!isUint(value)) {
|
|
3268
|
+
issues.push(
|
|
3269
|
+
issueOf(
|
|
3270
|
+
"SCHEMA_TYPE_MISMATCH",
|
|
3271
|
+
[...paramsPath, name],
|
|
3272
|
+
`argon2id params.${name} must be a CBOR unsigned integer`
|
|
3273
|
+
)
|
|
3274
|
+
);
|
|
3275
|
+
continue;
|
|
3276
|
+
}
|
|
3277
|
+
if (!uintWithin(value, 0, UINT32_MAX)) {
|
|
3278
|
+
issues.push(
|
|
3279
|
+
issueOf(
|
|
3280
|
+
"SCHEMA_TYPE_MISMATCH",
|
|
3281
|
+
[...paramsPath, name],
|
|
3282
|
+
`argon2id params.${name} exceeds the pinned wire range 0 .. 2^32 - 1`
|
|
3283
|
+
)
|
|
3284
|
+
);
|
|
3285
|
+
continue;
|
|
3286
|
+
}
|
|
3287
|
+
const num = Number(value);
|
|
3288
|
+
if (num < floors[name]) {
|
|
3289
|
+
issues.push(
|
|
3290
|
+
issueOf(
|
|
3291
|
+
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
3292
|
+
[...paramsPath, name],
|
|
3293
|
+
`argon2id requires ${name} >= ${floors[name]}`
|
|
3294
|
+
)
|
|
3295
|
+
);
|
|
3296
|
+
continue;
|
|
3297
|
+
}
|
|
3298
|
+
if (ceiling !== null && num > ceiling[name]) {
|
|
3299
|
+
issues.push(
|
|
3300
|
+
issueOf(
|
|
3301
|
+
"ENC_PASSPHRASE_PARAMS_EXCEED_POLICY",
|
|
3302
|
+
[...paramsPath, name],
|
|
3303
|
+
`argon2id params.${name} = ${num} exceeds the deployment ceiling ${ceiling[name]}`
|
|
3304
|
+
)
|
|
3305
|
+
);
|
|
3306
|
+
}
|
|
3010
3307
|
}
|
|
3011
|
-
return hex;
|
|
3012
3308
|
}
|
|
3013
3309
|
function rawSlotKeySets(rawEnc) {
|
|
3014
|
-
const slots =
|
|
3310
|
+
const slots = rawEnc["slots"];
|
|
3015
3311
|
if (!Array.isArray(slots)) return [];
|
|
3016
3312
|
return slots.map((slot) => {
|
|
3017
3313
|
const keys = /* @__PURE__ */ new Set();
|
|
@@ -3023,54 +3319,64 @@ function rawSlotKeySets(rawEnc) {
|
|
|
3023
3319
|
return keys;
|
|
3024
3320
|
});
|
|
3025
3321
|
}
|
|
3026
|
-
function
|
|
3027
|
-
if (value instanceof Map) return value.get(key);
|
|
3028
|
-
if (typeof value === "object" && value !== null) {
|
|
3029
|
-
return value[key];
|
|
3030
|
-
}
|
|
3031
|
-
return void 0;
|
|
3032
|
-
}
|
|
3033
|
-
function checkMerkleCommit(commit, idx, errors) {
|
|
3322
|
+
function checkMerkleCommit(commit, idx, issues) {
|
|
3034
3323
|
const basePath = ["merkle", idx];
|
|
3035
3324
|
if (!(commit.alg in MERKLE_COMMIT_ALG_LENGTHS)) {
|
|
3036
|
-
|
|
3037
|
-
|
|
3325
|
+
issues.push(
|
|
3326
|
+
issueOf(
|
|
3038
3327
|
"UNSUPPORTED_MERKLE_COMMIT_ALG",
|
|
3039
3328
|
[...basePath, "alg"],
|
|
3040
3329
|
`unknown merkle commitment alg: ${commit.alg}`
|
|
3041
3330
|
)
|
|
3042
3331
|
);
|
|
3043
|
-
|
|
3332
|
+
} else {
|
|
3333
|
+
const expected = MERKLE_COMMIT_ALG_LENGTHS[commit.alg];
|
|
3334
|
+
if (commit.root.length !== expected) {
|
|
3335
|
+
issues.push(
|
|
3336
|
+
issueOf(
|
|
3337
|
+
"HASH_DIGEST_LENGTH_MISMATCH",
|
|
3338
|
+
[...basePath, "root"],
|
|
3339
|
+
`merkle entry root length ${commit.root.length} != ${expected} for ${commit.alg}`
|
|
3340
|
+
)
|
|
3341
|
+
);
|
|
3342
|
+
}
|
|
3044
3343
|
}
|
|
3045
|
-
const
|
|
3046
|
-
if (
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
"
|
|
3050
|
-
[...basePath, "
|
|
3051
|
-
|
|
3344
|
+
const leafCount = commit.leaf_count;
|
|
3345
|
+
if (!isUint(leafCount)) {
|
|
3346
|
+
issues.push(
|
|
3347
|
+
issueOf(
|
|
3348
|
+
"SCHEMA_TYPE_MISMATCH",
|
|
3349
|
+
[...basePath, "leaf_count"],
|
|
3350
|
+
"leaf_count must be a CBOR unsigned integer"
|
|
3351
|
+
)
|
|
3352
|
+
);
|
|
3353
|
+
} else if (!uintWithin(leafCount, 1, UINT32_MAX)) {
|
|
3354
|
+
issues.push(
|
|
3355
|
+
issueOf(
|
|
3356
|
+
"SCHEMA_MERKLE_LEAF_COUNT_INVALID",
|
|
3357
|
+
[...basePath, "leaf_count"],
|
|
3358
|
+
`leaf_count ${String(leafCount)} is outside the pinned range 1 .. 2^32 - 1`
|
|
3052
3359
|
)
|
|
3053
3360
|
);
|
|
3054
3361
|
}
|
|
3055
|
-
if (commit.uris) {
|
|
3056
|
-
|
|
3362
|
+
if (commit.uris !== void 0) {
|
|
3363
|
+
checkUris(commit.uris, [...basePath, "uris"], issues);
|
|
3057
3364
|
}
|
|
3058
3365
|
}
|
|
3059
|
-
function checkSigEntry(entry, idx,
|
|
3366
|
+
function checkSigEntry(entry, idx, issues) {
|
|
3060
3367
|
if (entry.cose_key !== void 0) {
|
|
3061
3368
|
const keyIssue = inspectCoseKey(entry.cose_key, idx);
|
|
3062
3369
|
if (keyIssue !== null) {
|
|
3063
|
-
|
|
3370
|
+
issues.push(keyIssue);
|
|
3064
3371
|
return;
|
|
3065
3372
|
}
|
|
3066
3373
|
}
|
|
3067
|
-
const merged = bytesChunkArrayConcat(entry.cose_sign1);
|
|
3068
3374
|
let cose;
|
|
3069
3375
|
try {
|
|
3070
|
-
cose = decodeCoseSign1(
|
|
3376
|
+
cose = decodeCoseSign1(entry.cose_sign1);
|
|
3071
3377
|
} catch (cause) {
|
|
3072
|
-
|
|
3073
|
-
|
|
3378
|
+
issues.push(
|
|
3379
|
+
issueOf(
|
|
3074
3380
|
"MALFORMED_SIG_COSE_SIGN1",
|
|
3075
3381
|
["sigs", idx],
|
|
3076
3382
|
cause instanceof CoseVerifyError || cause instanceof Error ? cause.message : String(cause)
|
|
@@ -3079,8 +3385,8 @@ function checkSigEntry(entry, idx, errors, info) {
|
|
|
3079
3385
|
return;
|
|
3080
3386
|
}
|
|
3081
3387
|
if (cose.payload !== null) {
|
|
3082
|
-
|
|
3083
|
-
|
|
3388
|
+
issues.push(
|
|
3389
|
+
issueOf(
|
|
3084
3390
|
"MALFORMED_SIG_COSE_SIGN1",
|
|
3085
3391
|
["sigs", idx],
|
|
3086
3392
|
"COSE_Sign1 payload must be null (detached); attached form forbidden"
|
|
@@ -3090,8 +3396,8 @@ function checkSigEntry(entry, idx, errors, info) {
|
|
|
3090
3396
|
}
|
|
3091
3397
|
const alg = cose.protectedHeader.get(1);
|
|
3092
3398
|
if (typeof alg !== "number" || !KNOWN_SIG_ALG_IDS.has(alg)) {
|
|
3093
|
-
|
|
3094
|
-
|
|
3399
|
+
issues.push(
|
|
3400
|
+
issueOf(
|
|
3095
3401
|
"SIGNATURE_UNSUPPORTED",
|
|
3096
3402
|
["sigs", idx],
|
|
3097
3403
|
`COSE_Sign1 protected alg ${String(alg)} not in {-8, -19}`
|
|
@@ -3100,8 +3406,8 @@ function checkSigEntry(entry, idx, errors, info) {
|
|
|
3100
3406
|
}
|
|
3101
3407
|
const protectedKid = cose.protectedHeader.get(4);
|
|
3102
3408
|
if (protectedKid instanceof Uint8Array && protectedKid.length === 32 && entry.cose_key !== void 0) {
|
|
3103
|
-
|
|
3104
|
-
|
|
3409
|
+
issues.push(
|
|
3410
|
+
issueOf(
|
|
3105
3411
|
"SIG_ENTRY_KID_COSE_KEY_CONFLICT",
|
|
3106
3412
|
["sigs", idx],
|
|
3107
3413
|
"sigs[i] carries both a 32-byte protected `kid` (path 1) and an inline `cose_key` (path 2); paths are mutually exclusive"
|
|
@@ -3109,12 +3415,12 @@ function checkSigEntry(entry, idx, errors, info) {
|
|
|
3109
3415
|
);
|
|
3110
3416
|
}
|
|
3111
3417
|
}
|
|
3112
|
-
function inspectCoseKey(
|
|
3418
|
+
function inspectCoseKey(keyBytes, i) {
|
|
3113
3419
|
let decoded;
|
|
3114
3420
|
try {
|
|
3115
|
-
decoded = decodeCanonicalCbor(
|
|
3421
|
+
decoded = decodeCanonicalCbor(keyBytes);
|
|
3116
3422
|
} catch (cause) {
|
|
3117
|
-
return
|
|
3423
|
+
return issueOf(
|
|
3118
3424
|
"MALFORMED_SIG_COSE_SIGN1",
|
|
3119
3425
|
["sigs", i, "cose_key"],
|
|
3120
3426
|
`sigs[${i}].cose_key failed to decode as cbor<COSE_Key>: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
@@ -3122,20 +3428,14 @@ function inspectCoseKey(keyChunks, i) {
|
|
|
3122
3428
|
}
|
|
3123
3429
|
const getLabel = (label) => {
|
|
3124
3430
|
if (decoded instanceof Map) return decoded.get(label);
|
|
3125
|
-
if (typeof decoded === "object" && decoded !== null) {
|
|
3126
|
-
return decoded[String(label)];
|
|
3127
|
-
}
|
|
3128
3431
|
return void 0;
|
|
3129
3432
|
};
|
|
3130
3433
|
const hasLabel = (label) => {
|
|
3131
3434
|
if (decoded instanceof Map) return decoded.has(label);
|
|
3132
|
-
if (typeof decoded === "object" && decoded !== null) {
|
|
3133
|
-
return Object.prototype.hasOwnProperty.call(decoded, String(label));
|
|
3134
|
-
}
|
|
3135
3435
|
return false;
|
|
3136
3436
|
};
|
|
3137
3437
|
if (hasLabel(-4)) {
|
|
3138
|
-
return
|
|
3438
|
+
return issueOf(
|
|
3139
3439
|
"SIG_PRIVATE_KEY_LEAKED",
|
|
3140
3440
|
["sigs", i, "cose_key"],
|
|
3141
3441
|
"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"
|
|
@@ -3143,7 +3443,7 @@ function inspectCoseKey(keyChunks, i) {
|
|
|
3143
3443
|
}
|
|
3144
3444
|
const kty = getLabel(1);
|
|
3145
3445
|
if (kty !== 1) {
|
|
3146
|
-
return
|
|
3446
|
+
return issueOf(
|
|
3147
3447
|
"MALFORMED_SIG_COSE_SIGN1",
|
|
3148
3448
|
["sigs", i, "cose_key"],
|
|
3149
3449
|
`sigs[${i}].cose_key COSE_Key kty (label 1) must be 1 (OKP); got ${String(kty)}`
|
|
@@ -3151,23 +3451,16 @@ function inspectCoseKey(keyChunks, i) {
|
|
|
3151
3451
|
}
|
|
3152
3452
|
const crv = getLabel(-1);
|
|
3153
3453
|
if (crv !== 6) {
|
|
3154
|
-
return
|
|
3454
|
+
return issueOf(
|
|
3155
3455
|
"MALFORMED_SIG_COSE_SIGN1",
|
|
3156
3456
|
["sigs", i, "cose_key"],
|
|
3157
3457
|
`sigs[${i}].cose_key COSE_Key crv (label -1) must be 6 (Ed25519); got ${String(crv)}`
|
|
3158
3458
|
);
|
|
3159
3459
|
}
|
|
3160
|
-
if (!hasLabel(-2)) {
|
|
3161
|
-
return issue(
|
|
3162
|
-
"MALFORMED_SIG_COSE_SIGN1",
|
|
3163
|
-
["sigs", i, "cose_key"],
|
|
3164
|
-
`sigs[${i}].cose_key COSE_Key missing label -2 (Ed25519 public-key bytes)`
|
|
3165
|
-
);
|
|
3166
|
-
}
|
|
3167
3460
|
const x5 = getLabel(-2);
|
|
3168
3461
|
if (!(x5 instanceof Uint8Array) || x5.length !== 32) {
|
|
3169
3462
|
const got = x5 instanceof Uint8Array ? `${x5.length}-byte bstr` : typeof x5;
|
|
3170
|
-
return
|
|
3463
|
+
return issueOf(
|
|
3171
3464
|
"MALFORMED_SIG_COSE_SIGN1",
|
|
3172
3465
|
["sigs", i, "cose_key"],
|
|
3173
3466
|
`sigs[${i}].cose_key COSE_Key label -2 must be a 32-byte byte string (Ed25519 public key); got ${got}`
|
|
@@ -3175,6 +3468,58 @@ function inspectCoseKey(keyChunks, i) {
|
|
|
3175
3468
|
}
|
|
3176
3469
|
return null;
|
|
3177
3470
|
}
|
|
3471
|
+
function checkCrit(record, decodedTopKeys, supportedCriticalExtensions, issues) {
|
|
3472
|
+
if (!Array.isArray(record.crit)) return;
|
|
3473
|
+
if (record.crit.length === 0) {
|
|
3474
|
+
issues.push(
|
|
3475
|
+
issueOf(
|
|
3476
|
+
"SCHEMA_TYPE_MISMATCH",
|
|
3477
|
+
["crit"],
|
|
3478
|
+
"crit[] must carry at least one entry when present"
|
|
3479
|
+
)
|
|
3480
|
+
);
|
|
3481
|
+
return;
|
|
3482
|
+
}
|
|
3483
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3484
|
+
for (let i = 0; i < record.crit.length; i++) {
|
|
3485
|
+
const critName = record.crit[i];
|
|
3486
|
+
let reason = null;
|
|
3487
|
+
if (TOP_LEVEL_BASE_KEYS.has(critName)) {
|
|
3488
|
+
reason = `'${critName}' is a base key and MUST NOT appear in crit[]`;
|
|
3489
|
+
} else if (!isExtensionKey(critName)) {
|
|
3490
|
+
reason = `'${critName}' does not match the extension-key form (^x-.+ or ^[a-z]+-.+, no control characters)`;
|
|
3491
|
+
} else if (!decodedTopKeys.has(critName)) {
|
|
3492
|
+
reason = `'${critName}' is named in crit but absent from the record map`;
|
|
3493
|
+
} else if (seen.has(critName)) {
|
|
3494
|
+
reason = `'${critName}' appears more than once in crit[]`;
|
|
3495
|
+
}
|
|
3496
|
+
seen.add(critName);
|
|
3497
|
+
if (reason !== null) {
|
|
3498
|
+
issues.push(issueOf("CRIT_SHAPE_INVALID", ["crit", i], reason));
|
|
3499
|
+
continue;
|
|
3500
|
+
}
|
|
3501
|
+
if (!supportedCriticalExtensions.has(critName)) {
|
|
3502
|
+
issues.push(
|
|
3503
|
+
issueOf(
|
|
3504
|
+
"EXTENSION_UNSUPPORTED_CRITICAL",
|
|
3505
|
+
["crit", i],
|
|
3506
|
+
`crit lists extension '${critName}' that this validator does not implement`
|
|
3507
|
+
)
|
|
3508
|
+
);
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
}
|
|
3512
|
+
function topLevelKeysOf(decoded) {
|
|
3513
|
+
if (decoded === null || typeof decoded !== "object") return /* @__PURE__ */ new Set();
|
|
3514
|
+
if (decoded instanceof Map) {
|
|
3515
|
+
const out = /* @__PURE__ */ new Set();
|
|
3516
|
+
for (const k4 of decoded.keys()) {
|
|
3517
|
+
if (typeof k4 === "string") out.add(k4);
|
|
3518
|
+
}
|
|
3519
|
+
return out;
|
|
3520
|
+
}
|
|
3521
|
+
return new Set(Object.keys(decoded));
|
|
3522
|
+
}
|
|
3178
3523
|
var ACCEPTED_CIDV1_MULTIBASE = /* @__PURE__ */ new Set(["b", "B", "f", "F", "z"]);
|
|
3179
3524
|
var ACCEPTED_MULTICODECS = /* @__PURE__ */ new Set([85, 112, 113]);
|
|
3180
3525
|
var ACCEPTED_MULTIHASHES = /* @__PURE__ */ new Map([
|
|
@@ -3310,52 +3655,56 @@ function decodeBase58btc(s3) {
|
|
|
3310
3655
|
}
|
|
3311
3656
|
return out;
|
|
3312
3657
|
}
|
|
3313
|
-
function
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
errors.push(
|
|
3318
|
-
issue("SCHEMA_TYPE_MISMATCH", ["crit"], "crit[] must carry at least one entry when present")
|
|
3319
|
-
);
|
|
3320
|
-
return invalid;
|
|
3321
|
-
}
|
|
3322
|
-
const seen = /* @__PURE__ */ new Set();
|
|
3323
|
-
for (let i = 0; i < record.crit.length; i++) {
|
|
3324
|
-
const critName = record.crit[i];
|
|
3325
|
-
let reason = null;
|
|
3326
|
-
if (TOP_LEVEL_BASE_KEYS.has(critName)) {
|
|
3327
|
-
reason = `'${critName}' is a base key and MUST NOT appear in crit[]`;
|
|
3328
|
-
} else if (!isExtensionKey(critName)) {
|
|
3329
|
-
reason = `'${critName}' does not match the extension-key regex (^x-.+ or ^[a-z]+-.+)`;
|
|
3330
|
-
} else if (!decodedTopKeys.has(critName)) {
|
|
3331
|
-
reason = `'${critName}' is named in crit but absent from the record map`;
|
|
3332
|
-
} else if (seen.has(critName)) {
|
|
3333
|
-
reason = `'${critName}' appears more than once in crit[]`;
|
|
3334
|
-
}
|
|
3335
|
-
seen.add(critName);
|
|
3336
|
-
if (reason !== null) {
|
|
3337
|
-
invalid.add(i);
|
|
3338
|
-
errors.push(issue("CRIT_SHAPE_INVALID", ["crit", i], reason));
|
|
3339
|
-
}
|
|
3340
|
-
}
|
|
3341
|
-
return invalid;
|
|
3658
|
+
function bytesToHex2(bytes) {
|
|
3659
|
+
let out = "";
|
|
3660
|
+
for (const b5 of bytes) out += b5.toString(16).padStart(2, "0");
|
|
3661
|
+
return out;
|
|
3342
3662
|
}
|
|
3343
|
-
function
|
|
3344
|
-
if (
|
|
3345
|
-
if (
|
|
3346
|
-
|
|
3347
|
-
for (const k4 of decoded.keys()) {
|
|
3348
|
-
if (typeof k4 === "string") out.add(k4);
|
|
3349
|
-
}
|
|
3350
|
-
return out;
|
|
3351
|
-
}
|
|
3352
|
-
return new Set(Object.keys(decoded));
|
|
3663
|
+
function isUint(value) {
|
|
3664
|
+
if (typeof value === "number") return Number.isInteger(value) && value >= 0;
|
|
3665
|
+
if (typeof value === "bigint") return value >= 0n;
|
|
3666
|
+
return false;
|
|
3353
3667
|
}
|
|
3354
|
-
function
|
|
3668
|
+
function uintWithin(value, min, max) {
|
|
3669
|
+
if (typeof value === "bigint") return value >= BigInt(min) && value <= BigInt(max);
|
|
3670
|
+
return value >= min && value <= max;
|
|
3671
|
+
}
|
|
3672
|
+
function issueOf(code, path, message) {
|
|
3355
3673
|
return { code, path, message, severity: SEVERITY[code] };
|
|
3356
3674
|
}
|
|
3357
|
-
|
|
3358
|
-
|
|
3675
|
+
var PATH_UTF8 = new TextEncoder();
|
|
3676
|
+
function compareTextSegments(a4, b5) {
|
|
3677
|
+
const ab = PATH_UTF8.encode(a4);
|
|
3678
|
+
const bb = PATH_UTF8.encode(b5);
|
|
3679
|
+
const n2 = Math.min(ab.length, bb.length);
|
|
3680
|
+
for (let i = 0; i < n2; i++) {
|
|
3681
|
+
const d6 = ab[i] - bb[i];
|
|
3682
|
+
if (d6 !== 0) return d6;
|
|
3683
|
+
}
|
|
3684
|
+
return ab.length - bb.length;
|
|
3685
|
+
}
|
|
3686
|
+
function compareIssues(a4, b5) {
|
|
3687
|
+
const ap = a4.path;
|
|
3688
|
+
const bp = b5.path;
|
|
3689
|
+
const n2 = Math.min(ap.length, bp.length);
|
|
3690
|
+
for (let i = 0; i < n2; i++) {
|
|
3691
|
+
const x5 = ap[i];
|
|
3692
|
+
const y7 = bp[i];
|
|
3693
|
+
const xIsNum = typeof x5 === "number";
|
|
3694
|
+
const yIsNum = typeof y7 === "number";
|
|
3695
|
+
if (xIsNum !== yIsNum) return xIsNum ? -1 : 1;
|
|
3696
|
+
if (xIsNum && yIsNum) {
|
|
3697
|
+
if (x5 !== y7) return x5 < y7 ? -1 : 1;
|
|
3698
|
+
} else {
|
|
3699
|
+
const d6 = compareTextSegments(x5, y7);
|
|
3700
|
+
if (d6 !== 0) return d6;
|
|
3701
|
+
}
|
|
3702
|
+
}
|
|
3703
|
+
if (ap.length !== bp.length) return ap.length - bp.length;
|
|
3704
|
+
return errorCodeRegistryIndex(a4.code) - errorCodeRegistryIndex(b5.code);
|
|
3705
|
+
}
|
|
3706
|
+
function sortIssues(issues) {
|
|
3707
|
+
return [...issues].sort(compareIssues);
|
|
3359
3708
|
}
|
|
3360
3709
|
function valueAtPath(root, path) {
|
|
3361
3710
|
let cur = root;
|
|
@@ -3376,6 +3725,7 @@ function valueAtPath(root, path) {
|
|
|
3376
3725
|
(*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) *)
|
|
3377
3726
|
*/
|
|
3378
3727
|
|
|
3728
|
+
exports.DEFAULT_PASSPHRASE_PARAMS_CEILING = DEFAULT_PASSPHRASE_PARAMS_CEILING;
|
|
3379
3729
|
exports.validateCidProfile = validateCidProfile;
|
|
3380
3730
|
exports.validatePoeRecord = validatePoeRecord;
|
|
3381
3731
|
//# sourceMappingURL=validator.cjs.map
|