@cardanowall/poe-standard 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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 -31
- 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 -32
- package/dist/error-codes.js.map +1 -1
- package/dist/index.cjs +1127 -766
- 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 +1116 -761
- 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 +973 -512
- 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 +973 -513
- 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,35 +2192,257 @@ function decodeCoseSign1(bytes) {
|
|
|
2184
2192
|
signature: signatureRaw
|
|
2185
2193
|
};
|
|
2186
2194
|
}
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2195
|
+
var CARDANO_POE_ITEM_HASHES_PREFIX = new TextEncoder().encode(
|
|
2196
|
+
"cardano-poe-item-hashes-v1"
|
|
2197
|
+
);
|
|
2198
|
+
var CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX = new TextEncoder().encode(
|
|
2199
|
+
"cardano-poe-slots-transcript-v1"
|
|
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
|
+
);
|
|
2210
|
+
var CARDANO_POE_HKDF_INFO_PAYLOAD = new TextEncoder().encode(
|
|
2211
|
+
"cardano-poe-payload-v1"
|
|
2212
|
+
);
|
|
2213
|
+
var CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE = new TextEncoder().encode(
|
|
2214
|
+
"cardano-poe-payload-passphrase-v1"
|
|
2215
|
+
);
|
|
2216
|
+
var CARDANO_POE_X25519_KEK_SALT_PREFIX = new TextEncoder().encode(
|
|
2217
|
+
"cardano-poe-x25519-kek-salt-v1"
|
|
2218
|
+
);
|
|
2219
|
+
var CARDANO_POE_XWING_KEK_SALT_PREFIX = new TextEncoder().encode(
|
|
2220
|
+
"cardano-poe-xwing-kek-salt-v1"
|
|
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)");
|
|
2200
2224
|
}
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2225
|
+
if (CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length !== 31) {
|
|
2226
|
+
throw new Error(
|
|
2227
|
+
"CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX byte-length invariant violated (expected 31)"
|
|
2228
|
+
);
|
|
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
|
+
}
|
|
2243
|
+
if (CARDANO_POE_HKDF_INFO_PAYLOAD.length !== 22) {
|
|
2244
|
+
throw new Error("CARDANO_POE_HKDF_INFO_PAYLOAD byte-length invariant violated (expected 22)");
|
|
2245
|
+
}
|
|
2246
|
+
if (CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE.length !== 33) {
|
|
2247
|
+
throw new Error(
|
|
2248
|
+
"CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE byte-length invariant violated (expected 33)"
|
|
2249
|
+
);
|
|
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
|
+
}
|
|
2256
|
+
if (CARDANO_POE_XWING_KEK_SALT_PREFIX.length !== 29) {
|
|
2257
|
+
throw new Error("CARDANO_POE_XWING_KEK_SALT_PREFIX byte-length invariant violated (expected 29)");
|
|
2258
|
+
}
|
|
2259
|
+
var MAX_SLOTS = 1024;
|
|
2260
|
+
var MAX_DECODED_ENVELOPE_BYTES = 65536;
|
|
2261
|
+
var CARDANO_POE_HKDF_INFO_KEK = new TextEncoder().encode("cardano-poe-kek-v1");
|
|
2262
|
+
var CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 = new TextEncoder().encode(
|
|
2263
|
+
"cardano-poe-kek-mlkem768x25519-v1"
|
|
2264
|
+
);
|
|
2265
|
+
var ZERO_NONCE_12 = new Uint8Array(12);
|
|
2266
|
+
if (CARDANO_POE_HKDF_INFO_KEK.length !== 18) {
|
|
2267
|
+
throw new Error("CARDANO_POE_HKDF_INFO_KEK byte-length invariant violated (expected 18)");
|
|
2268
|
+
}
|
|
2269
|
+
if (CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519.length !== 33) {
|
|
2270
|
+
throw new Error(
|
|
2271
|
+
"CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 byte-length invariant violated (expected 33)"
|
|
2272
|
+
);
|
|
2273
|
+
}
|
|
2274
|
+
if (ZERO_NONCE_12.length !== 12) {
|
|
2275
|
+
throw new Error("ZERO_NONCE_12 byte-length invariant violated (expected 12)");
|
|
2213
2276
|
}
|
|
2277
|
+
new TextEncoder();
|
|
2278
|
+
|
|
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
|
+
);
|
|
2214
2445
|
var SEVERITY = Object.freeze({
|
|
2215
|
-
// --- Part A ---
|
|
2216
2446
|
MALFORMED_CBOR: "error",
|
|
2217
2447
|
SCHEMA_TYPE_MISMATCH: "error",
|
|
2218
2448
|
SCHEMA_MISSING_REQUIRED: "error",
|
|
@@ -2222,12 +2452,14 @@ var SEVERITY = Object.freeze({
|
|
|
2222
2452
|
HASH_DIGEST_LENGTH_MISMATCH: "error",
|
|
2223
2453
|
UNSUPPORTED_HASH_ALG: "error",
|
|
2224
2454
|
UNSUPPORTED_MERKLE_COMMIT_ALG: "error",
|
|
2455
|
+
SCHEMA_MERKLE_LEAF_COUNT_INVALID: "error",
|
|
2225
2456
|
INVALID_URI: "error",
|
|
2226
2457
|
CHUNK_TOO_LARGE: "error",
|
|
2227
2458
|
UNAUTHENTICATED_CIPHER_FORBIDDEN: "error",
|
|
2228
2459
|
UNSUPPORTED_AEAD_ALG: "error",
|
|
2229
2460
|
NONCE_LENGTH_MISMATCH: "error",
|
|
2230
2461
|
UNSUPPORTED_ENVELOPE_SCHEME: "error",
|
|
2462
|
+
ENC_UNSUPPORTED: "info",
|
|
2231
2463
|
ENC_SLOTS_EMPTY: "error",
|
|
2232
2464
|
ENC_SLOT_INVALID_SHAPE: "error",
|
|
2233
2465
|
UNSUPPORTED_KEM_ALG: "error",
|
|
@@ -2238,6 +2470,9 @@ var SEVERITY = Object.freeze({
|
|
|
2238
2470
|
ENC_SLOTS_MAC_INVALID_LENGTH: "error",
|
|
2239
2471
|
ENC_SLOTS_MAC_REQUIRED: "error",
|
|
2240
2472
|
ENC_SLOTS_REQUIRED: "error",
|
|
2473
|
+
ENC_SLOTS_DUPLICATE_KEM_MATERIAL: "error",
|
|
2474
|
+
ENC_SLOTS_TOO_MANY: "error",
|
|
2475
|
+
ENC_ENVELOPE_TOO_LARGE: "error",
|
|
2241
2476
|
ENC_EXCLUSIVITY_VIOLATION: "error",
|
|
2242
2477
|
ENC_NO_KEY_PATH: "error",
|
|
2243
2478
|
ENC_REQUIRES_CONTENT_HASH: "error",
|
|
@@ -2254,7 +2489,9 @@ var SEVERITY = Object.freeze({
|
|
|
2254
2489
|
SUPERSEDES_TX_INVALID_LENGTH: "error",
|
|
2255
2490
|
EXTENSION_UNSUPPORTED_CRITICAL: "error",
|
|
2256
2491
|
CRIT_SHAPE_INVALID: "error",
|
|
2257
|
-
|
|
2492
|
+
TX_NOT_FOUND: "error",
|
|
2493
|
+
PROVIDER_UNAVAILABLE: "error",
|
|
2494
|
+
TX_INTEGRITY_MISMATCH: "error",
|
|
2258
2495
|
METADATA_NOT_FOUND: "error",
|
|
2259
2496
|
INSUFFICIENT_CONFIRMATIONS: "info",
|
|
2260
2497
|
SIGNATURE_INVALID: "error",
|
|
@@ -2262,119 +2499,135 @@ var SEVERITY = Object.freeze({
|
|
|
2262
2499
|
WALLET_ADDRESS_MISMATCH: "error",
|
|
2263
2500
|
URI_TARGET_FORBIDDEN: "error",
|
|
2264
2501
|
URI_INTEGRITY_MISMATCH: "error",
|
|
2502
|
+
URI_PROVIDER_INTEGRITY_MISMATCH: "warning",
|
|
2265
2503
|
URI_FETCH_FAILED: "warning",
|
|
2266
2504
|
CONTENT_UNAVAILABLE: "error",
|
|
2505
|
+
CONTENT_FETCH_LIMIT_EXCEEDED: "error",
|
|
2267
2506
|
CIPHERTEXT_UNAVAILABLE: "error",
|
|
2268
|
-
PROVIDER_UNAVAILABLE: "error",
|
|
2269
2507
|
SERVICE_INDEPENDENCE_VIOLATION: "error",
|
|
2270
2508
|
WRONG_DECRYPTION_INPUT_SHAPE: "error",
|
|
2271
2509
|
WRONG_RECIPIENT_KEY: "error",
|
|
2272
2510
|
TAMPERED_HEADER: "error",
|
|
2273
2511
|
TAMPERED_CIPHERTEXT: "error",
|
|
2274
2512
|
KDF_DERIVATION_FAILED: "error",
|
|
2513
|
+
ENC_PASSPHRASE_UNNORMALIZABLE: "error",
|
|
2514
|
+
ENC_PASSPHRASE_EMPTY: "error",
|
|
2275
2515
|
SCHEMA_MERKLE_LEAF_COUNT_MISMATCH: "error",
|
|
2276
2516
|
SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED: "error",
|
|
2277
2517
|
SCHEMA_MERKLE_LEAVES_MALFORMED: "error",
|
|
2278
2518
|
MERKLE_ROOT_MISMATCH: "error",
|
|
2279
2519
|
MERKLE_LEAVES_UNAVAILABLE: "warning",
|
|
2280
|
-
MERKLE_LEAVES_INFORMATIVE_FORM: "info",
|
|
2281
|
-
// Dual-severity — default reading is `info`; the verifier promotes to
|
|
2282
|
-
// `error` for merkle-only records (no `items[]` content claim was
|
|
2283
|
-
// validated in the same record).
|
|
2284
2520
|
MERKLE_UNSUPPORTED: "info",
|
|
2285
|
-
// Dual-severity — default reading is `info` (render mode); strict
|
|
2286
|
-
// end-to-end verifiers promote to `error`.
|
|
2287
2521
|
OUT_OF_PROFILE_SKIPPED: "info"
|
|
2288
2522
|
});
|
|
2289
|
-
var
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
zod.z.
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
},
|
|
2301
|
-
{ params: { code: "CHUNK_TOO_LARGE" } }
|
|
2302
|
-
)
|
|
2303
|
-
).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
|
+
}
|
|
2304
2534
|
var HashDigestSchema = zod.z.instanceof(Uint8Array);
|
|
2305
2535
|
var HashesMapSchema = zod.z.record(zod.z.string(), HashDigestSchema);
|
|
2306
|
-
var
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
zod.z.
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
}).
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
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
|
+
);
|
|
2366
2617
|
var SupersedesSchema = zod.z.instanceof(Uint8Array).refine((b5) => b5.length === 32, {
|
|
2367
2618
|
params: { code: "SUPERSEDES_TX_INVALID_LENGTH" }
|
|
2368
2619
|
});
|
|
2369
2620
|
var VersionLiteralSchema = zod.z.literal(1);
|
|
2370
|
-
var PoeRecordSchema =
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
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
|
+
);
|
|
2378
2631
|
var TOP_LEVEL_BASE_KEYS = /* @__PURE__ */ new Set([
|
|
2379
2632
|
"v",
|
|
2380
2633
|
"items",
|
|
@@ -2383,8 +2636,8 @@ var TOP_LEVEL_BASE_KEYS = /* @__PURE__ */ new Set([
|
|
|
2383
2636
|
"sigs",
|
|
2384
2637
|
"crit"
|
|
2385
2638
|
]);
|
|
2386
|
-
var EXTENSION_KEY_VENDOR_RE = /^x
|
|
2387
|
-
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]+$/;
|
|
2388
2641
|
function isExtensionKey(k4) {
|
|
2389
2642
|
return EXTENSION_KEY_VENDOR_RE.test(k4) || EXTENSION_KEY_COMPANION_RE.test(k4);
|
|
2390
2643
|
}
|
|
@@ -2398,9 +2651,9 @@ var MERKLE_COMMIT_ALG_LENGTHS = {
|
|
|
2398
2651
|
"rfc9162-sha256": 32
|
|
2399
2652
|
};
|
|
2400
2653
|
var AEAD_NONCE_LENGTHS = {
|
|
2401
|
-
"
|
|
2654
|
+
"chacha20-poly1305-stream64k": 24
|
|
2402
2655
|
};
|
|
2403
|
-
var UNAUTHENTICATED_CIPHER_RE = /(?:^|[-_])(?:cbc|ctr|ecb|cfb|ofb)(?:[-_]
|
|
2656
|
+
var UNAUTHENTICATED_CIPHER_RE = /(?:^|[-_])(?:cbc|ctr|ecb|cfb|ofb)(?:[-_]|$)|^(?:rc4|des|3des)(?:[-_]|$)/i;
|
|
2404
2657
|
var KEM_SLOT_DESCRIPTORS = {
|
|
2405
2658
|
x25519: { field: "epk", fieldLength: 32, wrapLength: 48 },
|
|
2406
2659
|
mlkem768x25519: { field: "kem_ct", fieldLength: 1120, wrapLength: 48 }
|
|
@@ -2411,13 +2664,31 @@ var KEM_FIELD_LENGTH_CODE = {
|
|
|
2411
2664
|
};
|
|
2412
2665
|
var PASSPHRASE_KDF_ALGS = /* @__PURE__ */ new Set(["argon2id"]);
|
|
2413
2666
|
var KNOWN_SIG_ALG_IDS = /* @__PURE__ */ new Set([-8, -19]);
|
|
2414
|
-
|
|
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);
|
|
2415
2686
|
let decoded;
|
|
2416
2687
|
try {
|
|
2417
2688
|
decoded = decodeCanonicalCbor(bytes);
|
|
2418
2689
|
} catch (cause) {
|
|
2419
2690
|
return {
|
|
2420
|
-
|
|
2691
|
+
valid: false,
|
|
2421
2692
|
issues: [
|
|
2422
2693
|
{
|
|
2423
2694
|
code: "MALFORMED_CBOR",
|
|
@@ -2428,132 +2699,181 @@ function validatePoeRecord(bytes) {
|
|
|
2428
2699
|
]
|
|
2429
2700
|
};
|
|
2430
2701
|
}
|
|
2702
|
+
const nonTextKeyIssues = collectNonTextKeyMapIssues(decoded);
|
|
2703
|
+
if (nonTextKeyIssues.length > 0) {
|
|
2704
|
+
return { valid: false, issues: sortIssues(nonTextKeyIssues) };
|
|
2705
|
+
}
|
|
2431
2706
|
const parse = PoeRecordSchema.safeParse(decoded);
|
|
2432
2707
|
if (!parse.success) {
|
|
2433
|
-
|
|
2434
|
-
return { ok: false, issues };
|
|
2708
|
+
return { valid: false, issues: sortIssues(mapZodIssues(parse.error.issues, decoded)) };
|
|
2435
2709
|
}
|
|
2436
2710
|
const record = parse.data;
|
|
2437
|
-
const
|
|
2438
|
-
|
|
2439
|
-
const info = [];
|
|
2440
|
-
const itemsLen = Array.isArray(record.items) ? record.items.length : 0;
|
|
2441
|
-
const merkleLen = Array.isArray(record.merkle) ? record.merkle.length : 0;
|
|
2442
|
-
if (itemsLen === 0 && merkleLen === 0) {
|
|
2443
|
-
errors.push(
|
|
2444
|
-
issue(
|
|
2445
|
-
"SCHEMA_EMPTY_RECORD",
|
|
2446
|
-
[],
|
|
2447
|
-
"record must carry at least one of items[] or merkle[] non-empty"
|
|
2448
|
-
)
|
|
2449
|
-
);
|
|
2450
|
-
}
|
|
2711
|
+
const issues = [];
|
|
2712
|
+
checkContentCommitmentPresence(record, issues);
|
|
2451
2713
|
const decodedTopKeys = topLevelKeysOf(decoded);
|
|
2452
|
-
|
|
2453
|
-
for (const
|
|
2454
|
-
if (TOP_LEVEL_BASE_KEYS.has(
|
|
2455
|
-
if (isExtensionKey(
|
|
2456
|
-
|
|
2457
|
-
}
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
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")
|
|
2468
2735
|
);
|
|
2469
2736
|
}
|
|
2737
|
+
for (let i = 0; i < record.sigs.length; i++) {
|
|
2738
|
+
checkSigEntry(record.sigs[i], i, issues);
|
|
2739
|
+
}
|
|
2470
2740
|
}
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
if (item.uris) checkItemUris(item.uris, ["items", i, "uris"], errors);
|
|
2475
|
-
if (item.enc !== void 0) checkItemEnc(item, i, errors);
|
|
2476
|
-
}
|
|
2477
|
-
for (let i = 0; i < (record.merkle ?? []).length; i++) {
|
|
2478
|
-
const commit = record.merkle[i];
|
|
2479
|
-
checkMerkleCommit(commit, i, errors);
|
|
2741
|
+
const sorted = sortIssues(issues);
|
|
2742
|
+
if (sorted.some((issue) => issue.severity === "error")) {
|
|
2743
|
+
return { valid: false, issues: sorted };
|
|
2480
2744
|
}
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
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;
|
|
2484
2762
|
}
|
|
2763
|
+
out.push(mapZodIssue(zissue, decodedRoot));
|
|
2485
2764
|
}
|
|
2486
|
-
|
|
2487
|
-
|
|
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";
|
|
2488
2770
|
}
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
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
|
+
);
|
|
2492
2783
|
};
|
|
2493
|
-
if (
|
|
2494
|
-
|
|
2495
|
-
|
|
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;
|
|
2496
2805
|
}
|
|
2497
|
-
function mapZodIssue(zissue,
|
|
2806
|
+
function mapZodIssue(zissue, decodedRoot) {
|
|
2498
2807
|
const path = zissue.path;
|
|
2499
2808
|
const explicit = zissue.params?.code;
|
|
2500
2809
|
if (explicit !== void 0) {
|
|
2501
|
-
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
|
+
);
|
|
2502
2819
|
}
|
|
2503
2820
|
const inSigsEntry = path.length >= 2 && path[0] === "sigs" && typeof path[1] === "number";
|
|
2504
|
-
const isInSlotEntry =
|
|
2505
|
-
if (path.length >= 5 && path[0] === "items" && typeof path[1] === "number" && path[2] === "enc" && path[3] === "slots" && typeof path[4] === "number") {
|
|
2506
|
-
return true;
|
|
2507
|
-
}
|
|
2508
|
-
if (path.length >= 2 && path[0] === "slots" && typeof path[1] === "number") {
|
|
2509
|
-
return true;
|
|
2510
|
-
}
|
|
2511
|
-
return false;
|
|
2512
|
-
})();
|
|
2513
|
-
const valueAtIssue = valueAtPath(decoded, path);
|
|
2821
|
+
const isInSlotEntry = path.length >= 2 && path[0] === "slots" && typeof path[1] === "number";
|
|
2514
2822
|
const isMissing = valueAtIssue === void 0;
|
|
2515
2823
|
switch (zissue.code) {
|
|
2516
2824
|
case "invalid_type":
|
|
2517
|
-
if (isInSlotEntry) return
|
|
2825
|
+
if (isInSlotEntry) return issueOf("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
|
|
2518
2826
|
if (isMissing) {
|
|
2519
|
-
if (inSigsEntry) return
|
|
2520
|
-
return
|
|
2827
|
+
if (inSigsEntry) return issueOf("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
2828
|
+
return issueOf("SCHEMA_MISSING_REQUIRED", path, zissue.message);
|
|
2521
2829
|
}
|
|
2522
|
-
if (inSigsEntry) return
|
|
2523
|
-
return
|
|
2830
|
+
if (inSigsEntry) return issueOf("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
2831
|
+
return issueOf("SCHEMA_TYPE_MISMATCH", path, zissue.message);
|
|
2524
2832
|
case "invalid_value":
|
|
2525
|
-
if (
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
path,
|
|
2529
|
-
zissue.message
|
|
2530
|
-
);
|
|
2531
|
-
}
|
|
2532
|
-
return issue("SCHEMA_INVALID_LITERAL", path, zissue.message);
|
|
2533
|
-
case "unrecognized_keys":
|
|
2534
|
-
if (isInSlotEntry) return issue("ENC_SLOT_INVALID_SHAPE", path, zissue.message);
|
|
2535
|
-
if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
2536
|
-
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":
|
|
2537
2836
|
case "invalid_format":
|
|
2538
2837
|
case "too_big":
|
|
2539
2838
|
case "too_small":
|
|
2540
|
-
if (inSigsEntry) return issue("SIG_ENTRY_INVALID_SHAPE", path, zissue.message);
|
|
2541
|
-
return issue("SCHEMA_TYPE_MISMATCH", path, zissue.message);
|
|
2542
|
-
case "invalid_union":
|
|
2543
2839
|
case "invalid_key":
|
|
2544
2840
|
case "invalid_element":
|
|
2545
2841
|
case "custom":
|
|
2546
2842
|
default:
|
|
2547
|
-
if (isInSlotEntry) return
|
|
2548
|
-
if (inSigsEntry) return
|
|
2549
|
-
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);
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
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
|
+
);
|
|
2550
2870
|
}
|
|
2551
2871
|
}
|
|
2552
|
-
function checkItemHashes(item, idx,
|
|
2872
|
+
function checkItemHashes(item, idx, issues) {
|
|
2553
2873
|
const entries = Object.entries(item.hashes);
|
|
2554
2874
|
if (entries.length === 0) {
|
|
2555
|
-
|
|
2556
|
-
|
|
2875
|
+
issues.push(
|
|
2876
|
+
issueOf(
|
|
2557
2877
|
"SCHEMA_TYPE_MISMATCH",
|
|
2558
2878
|
["items", idx, "hashes"],
|
|
2559
2879
|
"hashes must be a non-empty CBOR map of <alg-id> -> <digest>"
|
|
@@ -2563,15 +2883,15 @@ function checkItemHashes(item, idx, errors) {
|
|
|
2563
2883
|
}
|
|
2564
2884
|
for (const [alg, digest] of entries) {
|
|
2565
2885
|
if (!(alg in HASH_ALG_LENGTHS)) {
|
|
2566
|
-
|
|
2567
|
-
|
|
2886
|
+
issues.push(
|
|
2887
|
+
issueOf("UNSUPPORTED_HASH_ALG", ["items", idx, "hashes", alg], `unknown hash alg: ${alg}`)
|
|
2568
2888
|
);
|
|
2569
2889
|
continue;
|
|
2570
2890
|
}
|
|
2571
2891
|
const expected = HASH_ALG_LENGTHS[alg];
|
|
2572
2892
|
if (digest.length !== expected) {
|
|
2573
|
-
|
|
2574
|
-
|
|
2893
|
+
issues.push(
|
|
2894
|
+
issueOf(
|
|
2575
2895
|
"HASH_DIGEST_LENGTH_MISMATCH",
|
|
2576
2896
|
["items", idx, "hashes", alg],
|
|
2577
2897
|
`hashes['${alg}'] digest length ${digest.length} != ${expected}`
|
|
@@ -2580,35 +2900,33 @@ function checkItemHashes(item, idx, errors) {
|
|
|
2580
2900
|
}
|
|
2581
2901
|
}
|
|
2582
2902
|
}
|
|
2583
|
-
function
|
|
2584
|
-
uris.
|
|
2585
|
-
|
|
2586
|
-
function validateOneUri(chunks, path, errors) {
|
|
2587
|
-
const reconstructed = reconstructChunkedUri(chunks);
|
|
2588
|
-
if (!reconstructed.ok) {
|
|
2589
|
-
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"));
|
|
2590
2906
|
return;
|
|
2591
2907
|
}
|
|
2592
|
-
|
|
2908
|
+
uris.forEach((uri, ui) => checkOneUri(uri, [...basePath, ui], issues));
|
|
2909
|
+
}
|
|
2910
|
+
function checkOneUri(uri, path, issues) {
|
|
2593
2911
|
if (uri.includes("#")) {
|
|
2594
|
-
|
|
2595
|
-
|
|
2912
|
+
issues.push(
|
|
2913
|
+
issueOf("INVALID_URI", path, "URI contains a fragment identifier ('#'), which is forbidden")
|
|
2596
2914
|
);
|
|
2597
2915
|
return;
|
|
2598
2916
|
}
|
|
2599
2917
|
const sepIdx = uri.indexOf("://");
|
|
2600
2918
|
if (sepIdx <= 0 || !/^[a-z][a-z0-9+.-]*$/i.test(uri.slice(0, sepIdx))) {
|
|
2601
|
-
|
|
2602
|
-
|
|
2919
|
+
issues.push(
|
|
2920
|
+
issueOf("INVALID_URI", path, "URI is not absolute (missing scheme://hierarchical-part)")
|
|
2603
2921
|
);
|
|
2604
2922
|
return;
|
|
2605
2923
|
}
|
|
2606
2924
|
const scheme = uri.slice(0, sepIdx).toLowerCase();
|
|
2607
2925
|
const rest = uri.slice(sepIdx + "://".length);
|
|
2608
2926
|
if (scheme === "ar") {
|
|
2609
|
-
if (!/^
|
|
2610
|
-
|
|
2611
|
-
|
|
2927
|
+
if (!/^[A-Za-z0-9_-]{43}$/.test(rest)) {
|
|
2928
|
+
issues.push(
|
|
2929
|
+
issueOf(
|
|
2612
2930
|
"INVALID_URI",
|
|
2613
2931
|
path,
|
|
2614
2932
|
"ar:// URI does not match `^ar://[A-Za-z0-9_-]{43}$` (43-char base64url txid, no path/query/fragment)"
|
|
@@ -2621,277 +2939,286 @@ function validateOneUri(chunks, path, errors) {
|
|
|
2621
2939
|
const slashIdx = rest.indexOf("/");
|
|
2622
2940
|
const cid = slashIdx === -1 ? rest : rest.slice(0, slashIdx);
|
|
2623
2941
|
if (!validateCidProfile(cid)) {
|
|
2624
|
-
|
|
2625
|
-
|
|
2942
|
+
issues.push(
|
|
2943
|
+
issueOf("INVALID_URI", path, "ipfs:// URI is not a valid CID under the Label 309 profile")
|
|
2626
2944
|
);
|
|
2627
2945
|
}
|
|
2628
2946
|
return;
|
|
2629
2947
|
}
|
|
2630
|
-
|
|
2631
|
-
|
|
2948
|
+
issues.push(
|
|
2949
|
+
issueOf("INVALID_URI", path, "unsupported URI scheme; v1 PoE URI set is {ar://, ipfs://}")
|
|
2632
2950
|
);
|
|
2633
2951
|
}
|
|
2634
|
-
function checkItemEnc(item, idx,
|
|
2952
|
+
function checkItemEnc(item, idx, opts, issues) {
|
|
2953
|
+
const encPath = ["items", idx, "enc"];
|
|
2635
2954
|
const hasContentHash = Object.keys(item.hashes).some((alg) => alg in HASH_ALG_LENGTHS);
|
|
2636
2955
|
if (!hasContentHash) {
|
|
2637
|
-
|
|
2638
|
-
|
|
2956
|
+
issues.push(
|
|
2957
|
+
issueOf(
|
|
2639
2958
|
"ENC_REQUIRES_CONTENT_HASH",
|
|
2640
|
-
|
|
2641
|
-
"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)"
|
|
2642
2961
|
)
|
|
2643
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"));
|
|
2644
2967
|
return;
|
|
2645
2968
|
}
|
|
2646
|
-
const
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
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
|
+
);
|
|
2655
2985
|
return;
|
|
2656
2986
|
}
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
"
|
|
2663
|
-
[...basePath, "scheme"],
|
|
2664
|
-
`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"
|
|
2665
2993
|
)
|
|
2666
2994
|
);
|
|
2995
|
+
return;
|
|
2667
2996
|
}
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2997
|
+
const aead = enc["aead"];
|
|
2998
|
+
if (typeof aead === "string" && UNAUTHENTICATED_CIPHER_RE.test(aead)) {
|
|
2999
|
+
issues.push(
|
|
3000
|
+
issueOf(
|
|
2671
3001
|
"UNAUTHENTICATED_CIPHER_FORBIDDEN",
|
|
2672
|
-
[...
|
|
2673
|
-
`'${
|
|
3002
|
+
[...encPath, "aead"],
|
|
3003
|
+
`'${aead}' is an unauthenticated cipher; Label 309 mandates an authenticated (AEAD) cipher`
|
|
2674
3004
|
)
|
|
2675
3005
|
);
|
|
2676
3006
|
return;
|
|
2677
3007
|
}
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
);
|
|
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
|
+
}
|
|
2682
3044
|
return;
|
|
2683
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) {
|
|
2684
3075
|
const expectedNonceLen = AEAD_NONCE_LENGTHS[enc.aead];
|
|
2685
3076
|
if (enc.nonce.length !== expectedNonceLen) {
|
|
2686
|
-
|
|
2687
|
-
|
|
3077
|
+
issues.push(
|
|
3078
|
+
issueOf(
|
|
2688
3079
|
"NONCE_LENGTH_MISMATCH",
|
|
2689
|
-
[...
|
|
3080
|
+
[...encPath, "nonce"],
|
|
2690
3081
|
`nonce length ${enc.nonce.length} != ${expectedNonceLen} for ${enc.aead}`
|
|
2691
3082
|
)
|
|
2692
3083
|
);
|
|
2693
3084
|
}
|
|
2694
|
-
if (enc.kem !== void 0 && !(enc.kem in KEM_SLOT_DESCRIPTORS)) {
|
|
2695
|
-
errors.push(issue("UNSUPPORTED_KEM_ALG", [...basePath, "kem"], `unknown kem alg: ${enc.kem}`));
|
|
2696
|
-
}
|
|
2697
3085
|
const hasSlots = enc.slots !== void 0;
|
|
2698
3086
|
const hasSlotsMac = enc.slots_mac !== void 0;
|
|
2699
3087
|
const hasPassphrase = enc.passphrase !== void 0;
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
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
|
+
)
|
|
2703
3096
|
);
|
|
2704
3097
|
}
|
|
2705
3098
|
if (hasSlots && !hasSlotsMac) {
|
|
2706
|
-
|
|
2707
|
-
|
|
3099
|
+
issues.push(
|
|
3100
|
+
issueOf("ENC_SLOTS_MAC_REQUIRED", encPath, "enc.slots present but enc.slots_mac absent")
|
|
2708
3101
|
);
|
|
2709
3102
|
}
|
|
2710
3103
|
if (hasSlotsMac && !hasSlots) {
|
|
2711
|
-
|
|
2712
|
-
|
|
3104
|
+
issues.push(
|
|
3105
|
+
issueOf("ENC_SLOTS_REQUIRED", encPath, "enc.slots_mac present but enc.slots absent")
|
|
2713
3106
|
);
|
|
2714
3107
|
}
|
|
2715
|
-
if (hasSlots &&
|
|
2716
|
-
|
|
3108
|
+
if (hasSlots && !hasKem) {
|
|
3109
|
+
issues.push(issueOf("ENC_KEM_REQUIRED", encPath, "enc.slots present but enc.kem absent"));
|
|
2717
3110
|
}
|
|
2718
3111
|
if (!hasSlots && !hasPassphrase) {
|
|
2719
|
-
|
|
2720
|
-
|
|
3112
|
+
issues.push(
|
|
3113
|
+
issueOf(
|
|
2721
3114
|
"ENC_NO_KEY_PATH",
|
|
2722
|
-
|
|
3115
|
+
encPath,
|
|
2723
3116
|
"enc requires either slots or passphrase \u2014 no on-chain key path otherwise"
|
|
2724
3117
|
)
|
|
2725
3118
|
);
|
|
2726
3119
|
}
|
|
2727
3120
|
if (hasSlots) {
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
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")
|
|
2731
3125
|
);
|
|
2732
|
-
}
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
3126
|
+
} else if (slots.length > opts.maxSlots) {
|
|
3127
|
+
issues.push(
|
|
3128
|
+
issueOf(
|
|
3129
|
+
"ENC_SLOTS_TOO_MANY",
|
|
3130
|
+
[...encPath, "slots"],
|
|
3131
|
+
`slots length ${slots.length} exceeds the slot-count bound ${opts.maxSlots}`
|
|
3132
|
+
)
|
|
3133
|
+
);
|
|
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];
|
|
2737
3140
|
checkSlotShape(
|
|
2738
3141
|
slot,
|
|
2739
3142
|
rawSlotKeys[si] ?? /* @__PURE__ */ new Set(),
|
|
2740
3143
|
descriptor,
|
|
2741
3144
|
enc.kem,
|
|
2742
|
-
|
|
2743
|
-
|
|
3145
|
+
slotPath,
|
|
3146
|
+
issues
|
|
2744
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);
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
2745
3163
|
});
|
|
2746
3164
|
}
|
|
2747
3165
|
}
|
|
2748
3166
|
if (hasPassphrase) {
|
|
2749
|
-
|
|
2750
|
-
const ppPath = [...basePath, "passphrase"];
|
|
2751
|
-
if (!PASSPHRASE_KDF_ALGS.has(pp.alg)) {
|
|
2752
|
-
errors.push(
|
|
2753
|
-
issue(
|
|
2754
|
-
"ENC_PASSPHRASE_ALG_UNSUPPORTED",
|
|
2755
|
-
[...ppPath, "alg"],
|
|
2756
|
-
`unknown passphrase kdf alg: ${pp.alg}`
|
|
2757
|
-
)
|
|
2758
|
-
);
|
|
2759
|
-
return;
|
|
2760
|
-
}
|
|
2761
|
-
if (pp.alg === "argon2id") {
|
|
2762
|
-
const allowed = /* @__PURE__ */ new Set(["m", "t", "p"]);
|
|
2763
|
-
for (const k4 of Object.keys(pp.params)) {
|
|
2764
|
-
if (!allowed.has(k4)) {
|
|
2765
|
-
errors.push(
|
|
2766
|
-
issue(
|
|
2767
|
-
"SCHEMA_UNKNOWN_FIELD",
|
|
2768
|
-
[...ppPath, "params", k4],
|
|
2769
|
-
`unknown argon2id params field: ${k4}`
|
|
2770
|
-
)
|
|
2771
|
-
);
|
|
2772
|
-
}
|
|
2773
|
-
}
|
|
2774
|
-
const p5 = pp.params;
|
|
2775
|
-
const argonInt = (val, name) => {
|
|
2776
|
-
if (typeof val !== "number" || !Number.isInteger(val)) {
|
|
2777
|
-
errors.push(
|
|
2778
|
-
issue(
|
|
2779
|
-
"SCHEMA_TYPE_MISMATCH",
|
|
2780
|
-
[...ppPath, "params", name],
|
|
2781
|
-
`argon2id params.${name} must be a CBOR unsigned integer`
|
|
2782
|
-
)
|
|
2783
|
-
);
|
|
2784
|
-
return null;
|
|
2785
|
-
}
|
|
2786
|
-
return val;
|
|
2787
|
-
};
|
|
2788
|
-
const mVal = argonInt(p5.m, "m");
|
|
2789
|
-
const tVal = argonInt(p5.t, "t");
|
|
2790
|
-
const pVal = argonInt(p5.p, "p");
|
|
2791
|
-
if (mVal !== null && mVal < 65536) {
|
|
2792
|
-
errors.push(
|
|
2793
|
-
issue(
|
|
2794
|
-
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
2795
|
-
[...ppPath, "params", "m"],
|
|
2796
|
-
"argon2id requires m >= 65536 KiB"
|
|
2797
|
-
)
|
|
2798
|
-
);
|
|
2799
|
-
}
|
|
2800
|
-
if (tVal !== null && tVal < 3) {
|
|
2801
|
-
errors.push(
|
|
2802
|
-
issue(
|
|
2803
|
-
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
2804
|
-
[...ppPath, "params", "t"],
|
|
2805
|
-
"argon2id requires t >= 3"
|
|
2806
|
-
)
|
|
2807
|
-
);
|
|
2808
|
-
}
|
|
2809
|
-
if (pVal !== null && pVal < 1) {
|
|
2810
|
-
errors.push(
|
|
2811
|
-
issue(
|
|
2812
|
-
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
2813
|
-
[...ppPath, "params", "p"],
|
|
2814
|
-
"argon2id requires p >= 1"
|
|
2815
|
-
)
|
|
2816
|
-
);
|
|
2817
|
-
}
|
|
2818
|
-
}
|
|
3167
|
+
checkPassphraseBlock(enc.passphrase, [...encPath, "passphrase"], opts, issues);
|
|
2819
3168
|
}
|
|
2820
3169
|
}
|
|
2821
3170
|
var SLOT_KEY_UNIVERSE = /* @__PURE__ */ new Set(["epk", "kem_ct", "wrap"]);
|
|
2822
|
-
function checkSlotShape(slot, rawKeys, descriptor, kem, slotPath,
|
|
3171
|
+
function checkSlotShape(slot, rawKeys, descriptor, kem, slotPath, issues) {
|
|
2823
3172
|
const foreignField = descriptor.field === "epk" ? "kem_ct" : "epk";
|
|
2824
3173
|
if (rawKeys.has(foreignField)) {
|
|
2825
|
-
|
|
2826
|
-
|
|
3174
|
+
issues.push(
|
|
3175
|
+
issueOf(
|
|
2827
3176
|
"ENC_SLOT_INVALID_SHAPE",
|
|
2828
3177
|
[...slotPath, foreignField],
|
|
2829
3178
|
`slot carries '${foreignField}' but kem='${kem}' expects '${descriptor.field}'`
|
|
2830
3179
|
)
|
|
2831
3180
|
);
|
|
2832
3181
|
}
|
|
2833
|
-
for (const
|
|
2834
|
-
if (!SLOT_KEY_UNIVERSE.has(
|
|
2835
|
-
|
|
2836
|
-
|
|
3182
|
+
for (const key of rawKeys) {
|
|
3183
|
+
if (!SLOT_KEY_UNIVERSE.has(key)) {
|
|
3184
|
+
issues.push(
|
|
3185
|
+
issueOf(
|
|
2837
3186
|
"ENC_SLOT_INVALID_SHAPE",
|
|
2838
|
-
[...slotPath,
|
|
2839
|
-
`slot carries unexpected key '${
|
|
3187
|
+
[...slotPath, key],
|
|
3188
|
+
`slot carries unexpected key '${key}'; a slot is a 2-key map {${descriptor.field}, wrap}`
|
|
2840
3189
|
)
|
|
2841
3190
|
);
|
|
2842
3191
|
}
|
|
2843
3192
|
}
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
}
|
|
2862
|
-
} else {
|
|
2863
|
-
if (slot.kem_ct === void 0) {
|
|
2864
|
-
errors.push(
|
|
2865
|
-
issue(
|
|
2866
|
-
"ENC_SLOT_INVALID_SHAPE",
|
|
2867
|
-
[...slotPath, "kem_ct"],
|
|
2868
|
-
`slot for kem='${kem}' is missing required 'kem_ct'`
|
|
2869
|
-
)
|
|
2870
|
-
);
|
|
2871
|
-
} else {
|
|
2872
|
-
const reassembled = bytesChunkArrayConcat(slot.kem_ct).length;
|
|
2873
|
-
if (reassembled !== descriptor.fieldLength) {
|
|
2874
|
-
errors.push(
|
|
2875
|
-
issue(
|
|
2876
|
-
KEM_FIELD_LENGTH_CODE.kem_ct,
|
|
2877
|
-
[...slotPath, "kem_ct"],
|
|
2878
|
-
`slot.kem_ct reassembles to ${reassembled} bytes != ${descriptor.fieldLength} for ${kem}`
|
|
2879
|
-
)
|
|
2880
|
-
);
|
|
2881
|
-
}
|
|
2882
|
-
}
|
|
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
|
+
);
|
|
2883
3210
|
}
|
|
2884
3211
|
if (slot.wrap === void 0) {
|
|
2885
|
-
|
|
2886
|
-
|
|
3212
|
+
issues.push(
|
|
3213
|
+
issueOf(
|
|
2887
3214
|
"ENC_SLOT_INVALID_SHAPE",
|
|
2888
3215
|
[...slotPath, "wrap"],
|
|
2889
3216
|
`slot for kem='${kem}' is missing required 'wrap'`
|
|
2890
3217
|
)
|
|
2891
3218
|
);
|
|
2892
3219
|
} else if (slot.wrap.length !== descriptor.wrapLength) {
|
|
2893
|
-
|
|
2894
|
-
|
|
3220
|
+
issues.push(
|
|
3221
|
+
issueOf(
|
|
2895
3222
|
"WRAP_LENGTH_MISMATCH",
|
|
2896
3223
|
[...slotPath, "wrap"],
|
|
2897
3224
|
`slot.wrap length ${slot.wrap.length} != ${descriptor.wrapLength}`
|
|
@@ -2899,8 +3226,88 @@ function checkSlotShape(slot, rawKeys, descriptor, kem, slotPath, errors) {
|
|
|
2899
3226
|
);
|
|
2900
3227
|
}
|
|
2901
3228
|
}
|
|
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;
|
|
3239
|
+
}
|
|
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
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
2902
3309
|
function rawSlotKeySets(rawEnc) {
|
|
2903
|
-
const slots =
|
|
3310
|
+
const slots = rawEnc["slots"];
|
|
2904
3311
|
if (!Array.isArray(slots)) return [];
|
|
2905
3312
|
return slots.map((slot) => {
|
|
2906
3313
|
const keys = /* @__PURE__ */ new Set();
|
|
@@ -2912,54 +3319,64 @@ function rawSlotKeySets(rawEnc) {
|
|
|
2912
3319
|
return keys;
|
|
2913
3320
|
});
|
|
2914
3321
|
}
|
|
2915
|
-
function
|
|
2916
|
-
if (value instanceof Map) return value.get(key);
|
|
2917
|
-
if (typeof value === "object" && value !== null) {
|
|
2918
|
-
return value[key];
|
|
2919
|
-
}
|
|
2920
|
-
return void 0;
|
|
2921
|
-
}
|
|
2922
|
-
function checkMerkleCommit(commit, idx, errors) {
|
|
3322
|
+
function checkMerkleCommit(commit, idx, issues) {
|
|
2923
3323
|
const basePath = ["merkle", idx];
|
|
2924
3324
|
if (!(commit.alg in MERKLE_COMMIT_ALG_LENGTHS)) {
|
|
2925
|
-
|
|
2926
|
-
|
|
3325
|
+
issues.push(
|
|
3326
|
+
issueOf(
|
|
2927
3327
|
"UNSUPPORTED_MERKLE_COMMIT_ALG",
|
|
2928
3328
|
[...basePath, "alg"],
|
|
2929
3329
|
`unknown merkle commitment alg: ${commit.alg}`
|
|
2930
3330
|
)
|
|
2931
3331
|
);
|
|
2932
|
-
|
|
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
|
+
}
|
|
2933
3343
|
}
|
|
2934
|
-
const
|
|
2935
|
-
if (
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
"
|
|
2939
|
-
[...basePath, "
|
|
2940
|
-
|
|
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`
|
|
2941
3359
|
)
|
|
2942
3360
|
);
|
|
2943
3361
|
}
|
|
2944
|
-
if (commit.uris) {
|
|
2945
|
-
|
|
3362
|
+
if (commit.uris !== void 0) {
|
|
3363
|
+
checkUris(commit.uris, [...basePath, "uris"], issues);
|
|
2946
3364
|
}
|
|
2947
3365
|
}
|
|
2948
|
-
function checkSigEntry(entry, idx,
|
|
3366
|
+
function checkSigEntry(entry, idx, issues) {
|
|
2949
3367
|
if (entry.cose_key !== void 0) {
|
|
2950
3368
|
const keyIssue = inspectCoseKey(entry.cose_key, idx);
|
|
2951
3369
|
if (keyIssue !== null) {
|
|
2952
|
-
|
|
3370
|
+
issues.push(keyIssue);
|
|
2953
3371
|
return;
|
|
2954
3372
|
}
|
|
2955
3373
|
}
|
|
2956
|
-
const merged = bytesChunkArrayConcat(entry.cose_sign1);
|
|
2957
3374
|
let cose;
|
|
2958
3375
|
try {
|
|
2959
|
-
cose = decodeCoseSign1(
|
|
3376
|
+
cose = decodeCoseSign1(entry.cose_sign1);
|
|
2960
3377
|
} catch (cause) {
|
|
2961
|
-
|
|
2962
|
-
|
|
3378
|
+
issues.push(
|
|
3379
|
+
issueOf(
|
|
2963
3380
|
"MALFORMED_SIG_COSE_SIGN1",
|
|
2964
3381
|
["sigs", idx],
|
|
2965
3382
|
cause instanceof CoseVerifyError || cause instanceof Error ? cause.message : String(cause)
|
|
@@ -2968,8 +3385,8 @@ function checkSigEntry(entry, idx, errors, info) {
|
|
|
2968
3385
|
return;
|
|
2969
3386
|
}
|
|
2970
3387
|
if (cose.payload !== null) {
|
|
2971
|
-
|
|
2972
|
-
|
|
3388
|
+
issues.push(
|
|
3389
|
+
issueOf(
|
|
2973
3390
|
"MALFORMED_SIG_COSE_SIGN1",
|
|
2974
3391
|
["sigs", idx],
|
|
2975
3392
|
"COSE_Sign1 payload must be null (detached); attached form forbidden"
|
|
@@ -2979,8 +3396,8 @@ function checkSigEntry(entry, idx, errors, info) {
|
|
|
2979
3396
|
}
|
|
2980
3397
|
const alg = cose.protectedHeader.get(1);
|
|
2981
3398
|
if (typeof alg !== "number" || !KNOWN_SIG_ALG_IDS.has(alg)) {
|
|
2982
|
-
|
|
2983
|
-
|
|
3399
|
+
issues.push(
|
|
3400
|
+
issueOf(
|
|
2984
3401
|
"SIGNATURE_UNSUPPORTED",
|
|
2985
3402
|
["sigs", idx],
|
|
2986
3403
|
`COSE_Sign1 protected alg ${String(alg)} not in {-8, -19}`
|
|
@@ -2989,8 +3406,8 @@ function checkSigEntry(entry, idx, errors, info) {
|
|
|
2989
3406
|
}
|
|
2990
3407
|
const protectedKid = cose.protectedHeader.get(4);
|
|
2991
3408
|
if (protectedKid instanceof Uint8Array && protectedKid.length === 32 && entry.cose_key !== void 0) {
|
|
2992
|
-
|
|
2993
|
-
|
|
3409
|
+
issues.push(
|
|
3410
|
+
issueOf(
|
|
2994
3411
|
"SIG_ENTRY_KID_COSE_KEY_CONFLICT",
|
|
2995
3412
|
["sigs", idx],
|
|
2996
3413
|
"sigs[i] carries both a 32-byte protected `kid` (path 1) and an inline `cose_key` (path 2); paths are mutually exclusive"
|
|
@@ -2998,12 +3415,12 @@ function checkSigEntry(entry, idx, errors, info) {
|
|
|
2998
3415
|
);
|
|
2999
3416
|
}
|
|
3000
3417
|
}
|
|
3001
|
-
function inspectCoseKey(
|
|
3418
|
+
function inspectCoseKey(keyBytes, i) {
|
|
3002
3419
|
let decoded;
|
|
3003
3420
|
try {
|
|
3004
|
-
decoded = decodeCanonicalCbor(
|
|
3421
|
+
decoded = decodeCanonicalCbor(keyBytes);
|
|
3005
3422
|
} catch (cause) {
|
|
3006
|
-
return
|
|
3423
|
+
return issueOf(
|
|
3007
3424
|
"MALFORMED_SIG_COSE_SIGN1",
|
|
3008
3425
|
["sigs", i, "cose_key"],
|
|
3009
3426
|
`sigs[${i}].cose_key failed to decode as cbor<COSE_Key>: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
@@ -3011,20 +3428,14 @@ function inspectCoseKey(keyChunks, i) {
|
|
|
3011
3428
|
}
|
|
3012
3429
|
const getLabel = (label) => {
|
|
3013
3430
|
if (decoded instanceof Map) return decoded.get(label);
|
|
3014
|
-
if (typeof decoded === "object" && decoded !== null) {
|
|
3015
|
-
return decoded[String(label)];
|
|
3016
|
-
}
|
|
3017
3431
|
return void 0;
|
|
3018
3432
|
};
|
|
3019
3433
|
const hasLabel = (label) => {
|
|
3020
3434
|
if (decoded instanceof Map) return decoded.has(label);
|
|
3021
|
-
if (typeof decoded === "object" && decoded !== null) {
|
|
3022
|
-
return Object.prototype.hasOwnProperty.call(decoded, String(label));
|
|
3023
|
-
}
|
|
3024
3435
|
return false;
|
|
3025
3436
|
};
|
|
3026
3437
|
if (hasLabel(-4)) {
|
|
3027
|
-
return
|
|
3438
|
+
return issueOf(
|
|
3028
3439
|
"SIG_PRIVATE_KEY_LEAKED",
|
|
3029
3440
|
["sigs", i, "cose_key"],
|
|
3030
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"
|
|
@@ -3032,7 +3443,7 @@ function inspectCoseKey(keyChunks, i) {
|
|
|
3032
3443
|
}
|
|
3033
3444
|
const kty = getLabel(1);
|
|
3034
3445
|
if (kty !== 1) {
|
|
3035
|
-
return
|
|
3446
|
+
return issueOf(
|
|
3036
3447
|
"MALFORMED_SIG_COSE_SIGN1",
|
|
3037
3448
|
["sigs", i, "cose_key"],
|
|
3038
3449
|
`sigs[${i}].cose_key COSE_Key kty (label 1) must be 1 (OKP); got ${String(kty)}`
|
|
@@ -3040,23 +3451,16 @@ function inspectCoseKey(keyChunks, i) {
|
|
|
3040
3451
|
}
|
|
3041
3452
|
const crv = getLabel(-1);
|
|
3042
3453
|
if (crv !== 6) {
|
|
3043
|
-
return
|
|
3454
|
+
return issueOf(
|
|
3044
3455
|
"MALFORMED_SIG_COSE_SIGN1",
|
|
3045
3456
|
["sigs", i, "cose_key"],
|
|
3046
3457
|
`sigs[${i}].cose_key COSE_Key crv (label -1) must be 6 (Ed25519); got ${String(crv)}`
|
|
3047
3458
|
);
|
|
3048
3459
|
}
|
|
3049
|
-
if (!hasLabel(-2)) {
|
|
3050
|
-
return issue(
|
|
3051
|
-
"MALFORMED_SIG_COSE_SIGN1",
|
|
3052
|
-
["sigs", i, "cose_key"],
|
|
3053
|
-
`sigs[${i}].cose_key COSE_Key missing label -2 (Ed25519 public-key bytes)`
|
|
3054
|
-
);
|
|
3055
|
-
}
|
|
3056
3460
|
const x5 = getLabel(-2);
|
|
3057
3461
|
if (!(x5 instanceof Uint8Array) || x5.length !== 32) {
|
|
3058
3462
|
const got = x5 instanceof Uint8Array ? `${x5.length}-byte bstr` : typeof x5;
|
|
3059
|
-
return
|
|
3463
|
+
return issueOf(
|
|
3060
3464
|
"MALFORMED_SIG_COSE_SIGN1",
|
|
3061
3465
|
["sigs", i, "cose_key"],
|
|
3062
3466
|
`sigs[${i}].cose_key COSE_Key label -2 must be a 32-byte byte string (Ed25519 public key); got ${got}`
|
|
@@ -3064,6 +3468,58 @@ function inspectCoseKey(keyChunks, i) {
|
|
|
3064
3468
|
}
|
|
3065
3469
|
return null;
|
|
3066
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
|
+
}
|
|
3067
3523
|
var ACCEPTED_CIDV1_MULTIBASE = /* @__PURE__ */ new Set(["b", "B", "f", "F", "z"]);
|
|
3068
3524
|
var ACCEPTED_MULTICODECS = /* @__PURE__ */ new Set([85, 112, 113]);
|
|
3069
3525
|
var ACCEPTED_MULTIHASHES = /* @__PURE__ */ new Map([
|
|
@@ -3199,52 +3655,56 @@ function decodeBase58btc(s3) {
|
|
|
3199
3655
|
}
|
|
3200
3656
|
return out;
|
|
3201
3657
|
}
|
|
3202
|
-
function
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
errors.push(
|
|
3207
|
-
issue("SCHEMA_TYPE_MISMATCH", ["crit"], "crit[] must carry at least one entry when present")
|
|
3208
|
-
);
|
|
3209
|
-
return invalid;
|
|
3210
|
-
}
|
|
3211
|
-
const seen = /* @__PURE__ */ new Set();
|
|
3212
|
-
for (let i = 0; i < record.crit.length; i++) {
|
|
3213
|
-
const critName = record.crit[i];
|
|
3214
|
-
let reason = null;
|
|
3215
|
-
if (TOP_LEVEL_BASE_KEYS.has(critName)) {
|
|
3216
|
-
reason = `'${critName}' is a base key and MUST NOT appear in crit[]`;
|
|
3217
|
-
} else if (!isExtensionKey(critName)) {
|
|
3218
|
-
reason = `'${critName}' does not match the extension-key regex (^x-.+ or ^[a-z]+-.+)`;
|
|
3219
|
-
} else if (!decodedTopKeys.has(critName)) {
|
|
3220
|
-
reason = `'${critName}' is named in crit but absent from the record map`;
|
|
3221
|
-
} else if (seen.has(critName)) {
|
|
3222
|
-
reason = `'${critName}' appears more than once in crit[]`;
|
|
3223
|
-
}
|
|
3224
|
-
seen.add(critName);
|
|
3225
|
-
if (reason !== null) {
|
|
3226
|
-
invalid.add(i);
|
|
3227
|
-
errors.push(issue("CRIT_SHAPE_INVALID", ["crit", i], reason));
|
|
3228
|
-
}
|
|
3229
|
-
}
|
|
3230
|
-
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;
|
|
3231
3662
|
}
|
|
3232
|
-
function
|
|
3233
|
-
if (
|
|
3234
|
-
if (
|
|
3235
|
-
|
|
3236
|
-
for (const k4 of decoded.keys()) {
|
|
3237
|
-
if (typeof k4 === "string") out.add(k4);
|
|
3238
|
-
}
|
|
3239
|
-
return out;
|
|
3240
|
-
}
|
|
3241
|
-
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;
|
|
3242
3667
|
}
|
|
3243
|
-
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) {
|
|
3244
3673
|
return { code, path, message, severity: SEVERITY[code] };
|
|
3245
3674
|
}
|
|
3246
|
-
|
|
3247
|
-
|
|
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);
|
|
3248
3708
|
}
|
|
3249
3709
|
function valueAtPath(root, path) {
|
|
3250
3710
|
let cur = root;
|
|
@@ -3265,6 +3725,7 @@ function valueAtPath(root, path) {
|
|
|
3265
3725
|
(*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) *)
|
|
3266
3726
|
*/
|
|
3267
3727
|
|
|
3728
|
+
exports.DEFAULT_PASSPHRASE_PARAMS_CEILING = DEFAULT_PASSPHRASE_PARAMS_CEILING;
|
|
3268
3729
|
exports.validateCidProfile = validateCidProfile;
|
|
3269
3730
|
exports.validatePoeRecord = validatePoeRecord;
|
|
3270
3731
|
//# sourceMappingURL=validator.cjs.map
|