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