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