@blamejs/pki 0.8.10 → 0.8.12
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/CHANGELOG.md +33 -0
- package/README.md +6 -6
- package/lib/attrcert-sign.js +14 -15
- package/lib/constants.js +5 -0
- package/lib/guard-parsed.js +1 -1
- package/lib/inspect.js +59 -0
- package/lib/lint.js +3 -3
- package/lib/path-validate.js +227 -97
- package/lib/schema-all.js +10 -5
- package/lib/schema-attrcert.js +108 -13
- package/lib/schema-crl.js +136 -38
- package/lib/schema-crmf.js +91 -6
- package/lib/schema-csr.js +116 -5
- package/lib/schema-pkix.js +494 -4
- package/lib/schema-x509.js +97 -3
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/schema-pkix.js
CHANGED
|
@@ -28,6 +28,8 @@ var _charAt = intrinsic.uncurry(String.prototype.charAt);
|
|
|
28
28
|
var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
29
29
|
var _split = intrinsic.uncurry(String.prototype.split);
|
|
30
30
|
var _create = intrinsic.create;
|
|
31
|
+
var _Number = intrinsic.Number;
|
|
32
|
+
var _append = guard.list.append;
|
|
31
33
|
var _isUint8Array = intrinsic.types.isUint8Array;
|
|
32
34
|
var _bigIntToString = intrinsic.bigIntToString;
|
|
33
35
|
var _weakGet = intrinsic.weakGet;
|
|
@@ -911,16 +913,39 @@ var CERT_FIXED_CRITICALITY_ROWS = [
|
|
|
911
913
|
["freshestCRL", false, "RFC 5280 sec. 4.2.1.15"], ["authorityInfoAccess", false, "RFC 5280 sec. 4.2.2.1"],
|
|
912
914
|
["subjectInfoAccess", false, "RFC 5280 sec. 4.2.2.2"],
|
|
913
915
|
["precertificatePoison", true, "RFC 6962 sec. 3.1"],
|
|
916
|
+
["ipAddrBlocks", true, "RFC 3779 sec. 2.2.2"], ["autonomousSysIds", true, "RFC 3779 sec. 3.2.2"],
|
|
917
|
+
["ipAddrBlocksV2", true, "RFC 8360 sec. 4.2.2, RFC 3779 sec. 2.2.2"],
|
|
918
|
+
["autonomousSysIdsV2", true, "RFC 8360 sec. 4.2.2, RFC 3779 sec. 3.2.2"],
|
|
914
919
|
];
|
|
915
|
-
/** @internal The
|
|
916
|
-
*
|
|
917
|
-
|
|
920
|
+
/** @internal The criticality RFC 5280 sec. 5.2 fixes for a CRL extension. issuerAltName is absent:
|
|
921
|
+
* sec. 5.2.2 states its criticality as a SHOULD, and carrying it in a table of MUSTs would promote
|
|
922
|
+
* the requirement level the clause states. */
|
|
923
|
+
var CRL_FIXED_CRITICALITY_ROWS = [
|
|
924
|
+
["authorityKeyIdentifier", false, "RFC 5280 sec. 5.2.1"], ["cRLNumber", false, "RFC 5280 sec. 5.2.3"],
|
|
925
|
+
["deltaCRLIndicator", true, "RFC 5280 sec. 5.2.4"], ["issuingDistributionPoint", true, "RFC 5280 sec. 5.2.5"],
|
|
926
|
+
["freshestCRL", false, "RFC 5280 sec. 5.2.6"], ["authorityInfoAccess", false, "RFC 5280 sec. 5.2.7"],
|
|
927
|
+
];
|
|
928
|
+
|
|
929
|
+
/** @internal The criticality RFC 5280 sec. 5.3 fixes for a CRL ENTRY extension. Three consumers read
|
|
930
|
+
* this one table: the CRL signer, the linter, and the OCSP signer, since RFC 6960 sec. 4.4.5
|
|
931
|
+
* carries the same extensions into a SingleResponse. */
|
|
932
|
+
var CRL_ENTRY_FIXED_CRITICALITY_ROWS = [
|
|
933
|
+
["reasonCode", false, "RFC 5280 sec. 5.3.1"], ["invalidityDate", false, "RFC 5280 sec. 5.3.2"],
|
|
934
|
+
["certificateIssuer", true, "RFC 5280 sec. 5.3.3"],
|
|
935
|
+
];
|
|
936
|
+
|
|
937
|
+
/** @internal A criticality table keyed by dotted OID through the registry the caller passes, since
|
|
938
|
+
* this module takes its registry as a parameter rather than requiring it. */
|
|
939
|
+
function _criticalityTable(rows, oidRegistry) {
|
|
918
940
|
var table = _create(null);
|
|
919
|
-
_forEach(
|
|
941
|
+
_forEach(rows, function (r) {
|
|
920
942
|
table[oidRegistry.byName(r[0])] = { critical: r[1], citation: r[2] };
|
|
921
943
|
});
|
|
922
944
|
return table;
|
|
923
945
|
}
|
|
946
|
+
function certFixedCriticality(oidRegistry) { return _criticalityTable(CERT_FIXED_CRITICALITY_ROWS, oidRegistry); }
|
|
947
|
+
function crlFixedCriticality(oidRegistry) { return _criticalityTable(CRL_FIXED_CRITICALITY_ROWS, oidRegistry); }
|
|
948
|
+
function crlEntryFixedCriticality(oidRegistry) { return _criticalityTable(CRL_ENTRY_FIXED_CRITICALITY_ROWS, oidRegistry); }
|
|
924
949
|
|
|
925
950
|
var GN_CONSTRUCTED = _assign(_create(null), { 0: 1, 3: 1, 4: 1, 5: 1 });
|
|
926
951
|
var GN_IA5 = _assign(_create(null), { 1: 1, 2: 1, 6: 1 });
|
|
@@ -1259,6 +1284,358 @@ function _decodeHelpers(ns) {
|
|
|
1259
1284
|
return { decodeTop: decodeTop, seqChildren: seqChildren, readInt: readInt, O: O };
|
|
1260
1285
|
}
|
|
1261
1286
|
|
|
1287
|
+
/** @internal The extensions a CRL profiles, keyed by the scope that profiles them. RFC 5280
|
|
1288
|
+
* sec. 5.2 defines the extensions of the list and sec. 5.3 those of an entry, and the two sets are
|
|
1289
|
+
* disjoint: a `cRLNumber` sitting on an entry and a `reasonCode` sitting on the list are each an
|
|
1290
|
+
* OID nothing profiles THERE, so neither is decoded and neither is a fault. Reading one table at
|
|
1291
|
+
* both scopes is what made `lib/lint.js` apply the CERTIFICATE table at CRL scope.
|
|
1292
|
+
*
|
|
1293
|
+
* A caller that names no scope gets both tables, which is what an OCSP singleExtension needs:
|
|
1294
|
+
* RFC 6960 sec. 4.4.5 carries a CRL-entry extension in a response, where neither scope is the
|
|
1295
|
+
* whole answer. That agnostic reading is the shipped default and is why the tables are returned
|
|
1296
|
+
* separately rather than merged.
|
|
1297
|
+
*
|
|
1298
|
+
* Each decoder takes the extnValue and the parse context and returns the decoded value, throwing a
|
|
1299
|
+
* plain Error whose message the caller wraps in its own typed code, so a module keeps its own
|
|
1300
|
+
* `<prefix>/bad-extension-value` rather than inheriting one from here. */
|
|
1301
|
+
function crlExtensionDecoders(ns) {
|
|
1302
|
+
var _h = _decodeHelpers(ns), O = _h.O;
|
|
1303
|
+
var byScope = _create(null);
|
|
1304
|
+
byScope.crl = _create(null);
|
|
1305
|
+
byScope.entry = _create(null);
|
|
1306
|
+
|
|
1307
|
+
/** @internal RFC 5280 sec. 5.2.4 defines BaseCRLNumber AS a CRLNumber, so the two read alike and
|
|
1308
|
+
* the sec. 5.2.3 type's `INTEGER (0..MAX)` binds both. The width ceiling that section also
|
|
1309
|
+
* states is a PROFILE rule rather than a shape rule and stays with the linter, which could not
|
|
1310
|
+
* report on a CRL a parse had already refused. */
|
|
1311
|
+
function crlNumberValue(what) {
|
|
1312
|
+
return function (buf, ctx) {
|
|
1313
|
+
var v = asn1.read.integer(decodeNested(buf, ctx, "a " + what + " extension value"));
|
|
1314
|
+
if (v < 0n) throw new Error(what + " must be non-negative (INTEGER 0..MAX)");
|
|
1315
|
+
return v;
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
byScope.crl[O("cRLNumber")] = crlNumberValue("cRLNumber");
|
|
1319
|
+
byScope.entry[O("reasonCode")] = function (buf, ctx) {
|
|
1320
|
+
var reason = asn1.read.enumerated(decodeNested(buf, ctx, "a reasonCode extension value"));
|
|
1321
|
+
/** @internal The decoded value is a BigInt and the table is keyed by its decimal text, so the
|
|
1322
|
+
* conversion is the captured one: a replaced `BigInt.prototype.toString` would otherwise
|
|
1323
|
+
* decide which reason codes this reader calls defined. */
|
|
1324
|
+
var key = _bigIntToString(reason);
|
|
1325
|
+
if (!_hasOwn(CRL_REASON_NAMES, key)) throw new Error("undefined CRLReason " + key);
|
|
1326
|
+
return _Number(reason);
|
|
1327
|
+
};
|
|
1328
|
+
byScope.entry[O("invalidityDate")] = function (buf, ctx) {
|
|
1329
|
+
var n = decodeNested(buf, ctx, "an invalidityDate extension value");
|
|
1330
|
+
if (n.tagClass !== "universal" || n.tagNumber !== asn1.TAGS.GENERALIZED_TIME) {
|
|
1331
|
+
throw new Error("invalidityDate must be a GeneralizedTime");
|
|
1332
|
+
}
|
|
1333
|
+
return asn1.read.time(n);
|
|
1334
|
+
};
|
|
1335
|
+
|
|
1336
|
+
/** @internal The decoder for one OID at one scope, or null. `scope` of `undefined` means both,
|
|
1337
|
+
* in the order the list is read before its entries, which is the order the shipped agnostic
|
|
1338
|
+
* reading already resolved them in. */
|
|
1339
|
+
function at(extOid, scope) {
|
|
1340
|
+
if (scope !== "entry" && _hasOwn(byScope.crl, extOid)) return byScope.crl[extOid];
|
|
1341
|
+
if (scope !== "crl" && _hasOwn(byScope.entry, extOid)) return byScope.entry[extOid];
|
|
1342
|
+
return null;
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
/** @internal The wider tables the extension door reads: every extension RFC 5280 sec. 5.2 and
|
|
1346
|
+
* sec. 5.3 profile, including the ones whose syntax is a certificate extension's. They are NOT
|
|
1347
|
+
* `byScope`, because `byScope` is what the parse decodes in place and the linter can only report
|
|
1348
|
+
* a malformed extension the parse left opaque. */
|
|
1349
|
+
var doorScope = _create(null);
|
|
1350
|
+
doorScope.crl = _assign(_create(null), byScope.crl);
|
|
1351
|
+
doorScope.entry = _assign(_create(null), byScope.entry);
|
|
1352
|
+
_forEach(["authorityKeyIdentifier", "issuerAltName", "authorityInfoAccess", "freshestCRL"], function (n) {
|
|
1353
|
+
doorScope.crl[O(n)] = scopedCertDecoder(ns, n);
|
|
1354
|
+
});
|
|
1355
|
+
doorScope.entry[O("certificateIssuer")] = _generalNamesDecoder(ns);
|
|
1356
|
+
/** @internal RFC 5280 sec. 5.2.4 defines BaseCRLNumber AS a CRLNumber, so the sec. 5.2.3 type
|
|
1357
|
+
* binds it and the non-negative rule is the same one. It is registered for the door and NOT in
|
|
1358
|
+
* `byScope`, because a parse that decoded it could no longer open a CRL whose delta indicator is
|
|
1359
|
+
* malformed, which is the object the linter's value-syntax rule exists to report on. */
|
|
1360
|
+
doorScope.crl[O("deltaCRLIndicator")] = crlNumberValue("baseCRLNumber");
|
|
1361
|
+
doorScope.crl[O("issuingDistributionPoint")] = _issuingDistributionPointDecoder(ns);
|
|
1362
|
+
|
|
1363
|
+
return { byScope: byScope, at: at, doorScope: doorScope };
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
/** @internal The door's reading of an IssuingDistributionPoint. The shared structural read reports a
|
|
1367
|
+
* fault rather than throwing, because path validation and linting both survey hostile input; the
|
|
1368
|
+
* door needs a decoder, so this is the throwing form over the same read. */
|
|
1369
|
+
function _issuingDistributionPointDecoder(ns) {
|
|
1370
|
+
var code = ns.prefix + "/bad-extension-value";
|
|
1371
|
+
return function (buf, ctx) {
|
|
1372
|
+
var read = decodeIssuingDistributionPointValue(ctx || ns, buf, code);
|
|
1373
|
+
if (read.malformed) {
|
|
1374
|
+
throw new Error("value is not a well-formed IssuingDistributionPoint (RFC 5280 sec. 5.2.5)");
|
|
1375
|
+
}
|
|
1376
|
+
return {
|
|
1377
|
+
distributionPoint: read.distributionPoint,
|
|
1378
|
+
onlyContainsUserCerts: read.onlyUser,
|
|
1379
|
+
onlyContainsCACerts: read.onlyCa,
|
|
1380
|
+
onlySomeReasons: read.onlySomeReasonsPresent ? read.onlySomeReasonsValue : null,
|
|
1381
|
+
indirectCRL: read.indirect,
|
|
1382
|
+
onlyContainsAttributeCerts: read.onlyAttr,
|
|
1383
|
+
};
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
/** @internal A certificate extension's decoder, reached from another format's table. The
|
|
1388
|
+
* certificate decoders close over the namespace they were built with, so a call that named caps
|
|
1389
|
+
* gets its own table and every decode below reads the caller's figure. A caller outside a parse
|
|
1390
|
+
* passes no context and reuses the module's table. */
|
|
1391
|
+
function scopedCertDecoder(ns, name) {
|
|
1392
|
+
var key = ns.oid.byName(name);
|
|
1393
|
+
var fixed = null;
|
|
1394
|
+
return function (buf, ctx) {
|
|
1395
|
+
if (ctx && (ctx.caps != null || ctx.budget != null)) {
|
|
1396
|
+
return certExtensionDecoders(schema.withResources(ns, ctx)).byOid[key](buf);
|
|
1397
|
+
}
|
|
1398
|
+
if (fixed === null) fixed = certExtensionDecoders(ns).byOid;
|
|
1399
|
+
return fixed[key](buf);
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
/** @internal certificateIssuer is a GeneralNames, which the CRL profile leaves raw at parse time
|
|
1404
|
+
* and the door reads. */
|
|
1405
|
+
function _generalNamesDecoder(ns) {
|
|
1406
|
+
var NAMES = generalNames(ns, { code: ns.prefix + "/bad-extension-value" });
|
|
1407
|
+
return function (buf, ctx) {
|
|
1408
|
+
return schema.walk(NAMES, decodeNested(buf, ctx || ns, "a certificateIssuer extension value"), ctx || ns).result;
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
/** @internal One decoded extension, as the public door hands it back. Eleven own properties, every
|
|
1413
|
+
* one always present, so a caller reads the same shape whatever happened to the value.
|
|
1414
|
+
*
|
|
1415
|
+
* `state` is the discriminator and is read first: `"decoded"` when the decoder produced a value,
|
|
1416
|
+
* `"unrecognized"` when no decoder is registered for that OID at that scope, `"undecodable"` when
|
|
1417
|
+
* one is and the value did not read as the structure its identifier names. `decoded` carries the
|
|
1418
|
+
* value only in the first arm and is `null` in the other two, and nothing may test `decoded` for
|
|
1419
|
+
* truthiness to tell them apart: `ocspNoCheck` decodes VALIDLY to null.
|
|
1420
|
+
*
|
|
1421
|
+
* `value` is the raw extnValue octets in every arm, byte-identical to the certificate's, because a
|
|
1422
|
+
* caller that has to recompute a hash or compare an encoding needs the bytes and not a rendering
|
|
1423
|
+
* of them. `code` is the typed error the decoder threw in the undecodable arm and `null`
|
|
1424
|
+
* otherwise. `profile` carries the criticality the scope's RFC fixes for that OID, with the clause
|
|
1425
|
+
* that fixes it and whether the encoded flag deviates, and is `null` for an extension whose
|
|
1426
|
+
* criticality the issuer chooses.
|
|
1427
|
+
*
|
|
1428
|
+
* `index` is the position in the SEQUENCE the row came from and `containerIndex` the position of
|
|
1429
|
+
* that SEQUENCE's container within the object: the revoked-certificate entry at `crl-entry` scope,
|
|
1430
|
+
* the `extensionRequest` attribute at `csr-requested` scope, and `null` for a list the object
|
|
1431
|
+
* carries directly. `scope` names which of the two it is, so one field serves both. */
|
|
1432
|
+
function _extensionRow(ext, opts, index, state, decoded, code, profile) {
|
|
1433
|
+
var row = _create(null);
|
|
1434
|
+
row.oid = ext.oid;
|
|
1435
|
+
row.name = ext.name === undefined ? null : ext.name;
|
|
1436
|
+
row.critical = ext.critical === true;
|
|
1437
|
+
row.value = ext.value;
|
|
1438
|
+
row.scope = opts.scope;
|
|
1439
|
+
row.index = index;
|
|
1440
|
+
row.containerIndex = opts.containerIndex === undefined ? null : opts.containerIndex;
|
|
1441
|
+
row.state = state;
|
|
1442
|
+
row.decoded = decoded;
|
|
1443
|
+
row.code = code;
|
|
1444
|
+
row.profile = profile;
|
|
1445
|
+
return row;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
/** @internal The door itself, parameterized by the scope it reads at and the table that scope
|
|
1449
|
+
* profiles, so each format module wires its own rather than this file reaching into theirs. The
|
|
1450
|
+
* attribute-certificate table lives in `schema-attrcert.js`, and a registry here that reached for
|
|
1451
|
+
* it would invert the dependency.
|
|
1452
|
+
*
|
|
1453
|
+
* `opts.decoders` is the OID-keyed table, `opts.scope` the key each row carries, `opts.criticality`
|
|
1454
|
+
* the fixed-criticality table or null, and `opts.strict` turns the undecodable arm into a throw.
|
|
1455
|
+
* An unrecognized OID is never a throw, in any mode, at any scope: refusing one would make it
|
|
1456
|
+
* impossible to READ a certificate carrying a private critical extension, which is what
|
|
1457
|
+
* `pki.inspect` and `pki.lint` are for. RFC 5280 sec. 4.2's reject-on-critical rule is the
|
|
1458
|
+
* caller's to apply, and the two facts it needs, `critical` and `state`, are on the row. */
|
|
1459
|
+
function decodeExtensionList(list, opts) {
|
|
1460
|
+
var out = [];
|
|
1461
|
+
for (var i = 0; i < list.length; i++) _append(out, decodeOneExtension(list[i], i, opts));
|
|
1462
|
+
return out;
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
/** @internal One row, so the single-extension verb and the table verb answer from one reader. */
|
|
1466
|
+
function decodeOneExtension(ext, index, opts) {
|
|
1467
|
+
var decoders = opts.decoders || _create(null);
|
|
1468
|
+
var criticality = opts.criticality || null;
|
|
1469
|
+
var profile = null;
|
|
1470
|
+
if (criticality !== null && _hasOwn(criticality, ext.oid)) {
|
|
1471
|
+
var fixed = criticality[ext.oid];
|
|
1472
|
+
profile = _create(null);
|
|
1473
|
+
profile.criticalExpected = fixed.critical;
|
|
1474
|
+
profile.citation = fixed.citation;
|
|
1475
|
+
profile.deviates = (ext.critical === true) !== fixed.critical;
|
|
1476
|
+
}
|
|
1477
|
+
if (!_hasOwn(decoders, ext.oid)) {
|
|
1478
|
+
return _extensionRow(ext, opts, index, "unrecognized", null, null, profile);
|
|
1479
|
+
}
|
|
1480
|
+
/** @internal `failed` rather than a test on the caught value: a decoder that threw something
|
|
1481
|
+
* falsy would otherwise read as a decode that produced `undefined`, and the row would say
|
|
1482
|
+
* `decoded` for a value nothing decoded. */
|
|
1483
|
+
var decoded, thrown = null, failed = false;
|
|
1484
|
+
try { decoded = decoders[ext.oid](ext.value, opts.ctx); }
|
|
1485
|
+
catch (e) { thrown = e; failed = true; }
|
|
1486
|
+
if (failed) {
|
|
1487
|
+
/** @internal A table's decoders do not all throw typed errors: the ones a parse composes throw
|
|
1488
|
+
* a plain Error so the parse can wrap them in its own code. The row carries a code in every
|
|
1489
|
+
* arm, so an untyped throw becomes the format's own `bad-extension-value` here. */
|
|
1490
|
+
var carriesCode = thrown !== null && thrown !== undefined && typeof thrown.code === "string";
|
|
1491
|
+
var typed = carriesCode ? thrown : opts.wrap(ext, thrown);
|
|
1492
|
+
if (opts.strict === true) throw typed;
|
|
1493
|
+
return _extensionRow(ext, opts, index, "undecodable", null, typed.code, profile);
|
|
1494
|
+
}
|
|
1495
|
+
return _extensionRow(ext, opts, index, "decoded", decoded, null, profile);
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
/** @internal The caps a caller may tighten on an extension door, and the two flags it takes
|
|
1499
|
+
* besides. `groups` is offered only by the door that has more than one list to group. */
|
|
1500
|
+
var EXTENSION_DOOR_OPTS = _assign(_create(null), { maxBytes: 1, maxDepth: 1, maxItems: 1, strict: 1 });
|
|
1501
|
+
var EXTENSION_DOOR_GROUP_OPTS = _assign(_create(null), _assign(_create(null), EXTENSION_DOOR_OPTS), { groups: 1 });
|
|
1502
|
+
var EXTENSION_CAP_KEYS = ["maxBytes", "maxDepth", "maxItems"];
|
|
1503
|
+
|
|
1504
|
+
/** @internal The public door onto one object's decoded extensions, built once here and wired by each
|
|
1505
|
+
* format module with its own scopes, so the option handling, the caps, the budget latch and the row
|
|
1506
|
+
* shape are stated in one place and no format can answer differently from another.
|
|
1507
|
+
*
|
|
1508
|
+
* `spec.scopes` maps each scope key this format reads to `{ decoders, criticality }`.
|
|
1509
|
+
* The attribute-certificate table lives in `schema-attrcert.js` and is passed in rather than
|
|
1510
|
+
* reached for, or the dependency inverts. `spec.segments(parsed, read)` names the lists to read and
|
|
1511
|
+
* the scope and container index of each; `spec.defaultScope` is the scope the single-extension verb
|
|
1512
|
+
* reads at, and a format with more than one scope leaves it out and requires `opts.scope`.
|
|
1513
|
+
*
|
|
1514
|
+
* Caps bound the decoding THIS call performs. Handed bytes the door parses under them; handed a
|
|
1515
|
+
* parse result it reads that result, whose own caps the caller chose when they parsed. */
|
|
1516
|
+
function extensionDoor(spec) {
|
|
1517
|
+
var ns = spec.ns;
|
|
1518
|
+
var prefix = ns.prefix;
|
|
1519
|
+
var badInput = prefix + "/bad-input";
|
|
1520
|
+
var knownOpts = spec.groups === true ? EXTENSION_DOOR_GROUP_OPTS : EXTENSION_DOOR_OPTS;
|
|
1521
|
+
var capOpts = parseOpts({ ErrorClass: spec.ErrorClass, prefix: prefix, what: spec.what });
|
|
1522
|
+
var scopeNames = intrinsic.keys(spec.scopes);
|
|
1523
|
+
function E(code, message, cause) { return new spec.ErrorClass(code, message, cause); }
|
|
1524
|
+
|
|
1525
|
+
function readOpts(callerOpts, verb, wantsScope) {
|
|
1526
|
+
var checked = guard.identifier.optionsObject(callerOpts, E, badInput, verb + ": opts");
|
|
1527
|
+
var known = wantsScope ? _assign(_create(null), _assign(_create(null), knownOpts), { scope: 1 }) : knownOpts;
|
|
1528
|
+
guard.identifier.assertKnownKeys(checked, known, E, badInput,
|
|
1529
|
+
"pki.schema." + spec.key + "." + verb + " does not take the option ");
|
|
1530
|
+
var asked = _create(null);
|
|
1531
|
+
for (var i = 0; i < EXTENSION_CAP_KEYS.length; i++) {
|
|
1532
|
+
var k = EXTENSION_CAP_KEYS[i];
|
|
1533
|
+
if (checked[k] !== undefined) asked[k] = checked[k];
|
|
1534
|
+
}
|
|
1535
|
+
var read = _create(null);
|
|
1536
|
+
read.caps = parseCaps(asked, capOpts);
|
|
1537
|
+
read.strict = checked.strict === true;
|
|
1538
|
+
read.groups = checked.groups === true;
|
|
1539
|
+
read.scope = checked.scope;
|
|
1540
|
+
read.tables = _create(null);
|
|
1541
|
+
return read;
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
/** @internal The scope's tables plus this call's walk context, as one options object the row
|
|
1545
|
+
* reader takes. A fresh context per call, so one call's budget cannot latch another's.
|
|
1546
|
+
*
|
|
1547
|
+
* `table.decoders` is a FUNCTION of the call's namespace rather than a table, because a decoder
|
|
1548
|
+
* built against the module's namespace carries the module's caps: the certificate decoders take
|
|
1549
|
+
* the value alone and reach their bounds through the namespace they were built with, so a table
|
|
1550
|
+
* held at module load would decode a caller's value under the built-in ceilings whatever caps
|
|
1551
|
+
* that caller named. Built once per call and reused across every segment, so a CRL's entries do
|
|
1552
|
+
* not rebuild it per entry. */
|
|
1553
|
+
function scopeOpts(key, read, containerIndex) {
|
|
1554
|
+
var table = spec.scopes[key];
|
|
1555
|
+
var out = _create(null);
|
|
1556
|
+
if (!_hasOwn(read.tables, key)) read.tables[key] = table.decoders(read.ctx.ns);
|
|
1557
|
+
out.decoders = read.tables[key];
|
|
1558
|
+
out.criticality = table.criticality === undefined ? null : table.criticality;
|
|
1559
|
+
out.scope = key;
|
|
1560
|
+
out.containerIndex = containerIndex;
|
|
1561
|
+
out.strict = read.strict;
|
|
1562
|
+
out.ctx = read.ctx.ns;
|
|
1563
|
+
out.wrap = wrapValueFault;
|
|
1564
|
+
return out;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
var badValueCode = prefix + "/bad-extension-value";
|
|
1568
|
+
function wrapValueFault(ext, thrown) {
|
|
1569
|
+
return E(badValueCode, "malformed " + (ext.name || ext.oid) + " extension value: " +
|
|
1570
|
+
guard.text.describeThrown(thrown), thrown);
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
function decodeExtensions(input, callerOpts) {
|
|
1574
|
+
var read = readOpts(callerOpts, "decodeExtensions", false);
|
|
1575
|
+
/** @internal A parse result is re-derived from the bytes it recorded, to prove it is the
|
|
1576
|
+
* unmodified output of this format's parser. That re-derivation is not work this call asked
|
|
1577
|
+
* for, so the caps do not bound it: they bound the decoding, and a caller naming a cap smaller
|
|
1578
|
+
* than the whole object would otherwise be refused an extension value that fits inside it.
|
|
1579
|
+
* Caps DO bound the parse of bytes the caller handed in, which is work this call performs. */
|
|
1580
|
+
var fromBytes = typeof input === "string" || guard.bytes.isByteSource(input);
|
|
1581
|
+
var parsed = guard.parsed.acceptDerived(input, spec.kind, function (bytes) {
|
|
1582
|
+
return fromBytes ? spec.parse(bytes, read.caps) : spec.parse(bytes);
|
|
1583
|
+
}, E, badInput, spec.what);
|
|
1584
|
+
read.ctx = nestedContext(ns, read.caps);
|
|
1585
|
+
var segments = spec.segments(parsed, read);
|
|
1586
|
+
var out = [];
|
|
1587
|
+
for (var s = 0; s < segments.length; s++) {
|
|
1588
|
+
var seg = segments[s];
|
|
1589
|
+
var rows = decodeExtensionList(seg.list, scopeOpts(seg.scope, read, seg.containerIndex));
|
|
1590
|
+
for (var r = 0; r < rows.length; r++) _append(out, rows[r]);
|
|
1591
|
+
}
|
|
1592
|
+
if (read.ctx.budget.exhausted()) {
|
|
1593
|
+
throw E(prefix + "/too-large", spec.what + " exceeds the decode budget for this call");
|
|
1594
|
+
}
|
|
1595
|
+
return read.groups ? spec.group(out, segments) : out;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
function decodeExtension(ext, callerOpts) {
|
|
1599
|
+
var read = readOpts(callerOpts, "decodeExtension", spec.defaultScope === undefined);
|
|
1600
|
+
var key = read.scope === undefined ? spec.defaultScope : read.scope;
|
|
1601
|
+
if (typeof key !== "string" || !_hasOwn(spec.scopes, key)) {
|
|
1602
|
+
throw E(badInput, "pki.schema." + spec.key + ".decodeExtension needs opts.scope naming which " +
|
|
1603
|
+
"scope to read at, one of " + _join(scopeNames, ", ") + "; got " + _String(key));
|
|
1604
|
+
}
|
|
1605
|
+
var record = _acceptExtensionRecord(ext, E, badInput, spec.key);
|
|
1606
|
+
read.ctx = nestedContext(ns, read.caps);
|
|
1607
|
+
var row = decodeOneExtension(record, 0, scopeOpts(key, read, undefined));
|
|
1608
|
+
if (read.ctx.budget.exhausted()) {
|
|
1609
|
+
throw E(prefix + "/too-large", "the extension value exceeds the decode budget for this call");
|
|
1610
|
+
}
|
|
1611
|
+
return row;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
return { decodeExtensions: decodeExtensions, decodeExtension: decodeExtension };
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
/** @internal The single-extension verbs take the record `parse` puts in an object's extension list.
|
|
1618
|
+
* The three fields the row is built from are read off a snapshot, so a caller object cannot answer
|
|
1619
|
+
* one value to the check and another to the decode. */
|
|
1620
|
+
function _acceptExtensionRecord(ext, E, code, key) {
|
|
1621
|
+
var checked = guard.identifier.optionsObject(ext, E, code,
|
|
1622
|
+
"pki.schema." + key + ".decodeExtension: the extension");
|
|
1623
|
+
if (typeof checked.oid !== "string" || checked.oid.length === 0) {
|
|
1624
|
+
throw E(code, "pki.schema." + key + ".decodeExtension takes one extension record from a parse " +
|
|
1625
|
+
"result, as { oid, name, critical, value }; its oid must be a dotted string");
|
|
1626
|
+
}
|
|
1627
|
+
if (!guard.bytes.isByteSource(checked.value)) {
|
|
1628
|
+
throw E(code, "pki.schema." + key + ".decodeExtension: the extension's value must be the raw " +
|
|
1629
|
+
"extnValue octets");
|
|
1630
|
+
}
|
|
1631
|
+
var record = _create(null);
|
|
1632
|
+
record.oid = checked.oid;
|
|
1633
|
+
record.name = typeof checked.name === "string" ? checked.name : null;
|
|
1634
|
+
record.critical = checked.critical === true;
|
|
1635
|
+
record.value = guard.bytes.snapshot(checked.value, E, code, "the extension value");
|
|
1636
|
+
return record;
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1262
1639
|
function certExtensionDecoders(ns) {
|
|
1263
1640
|
var GN_SUBTREE = generalName(ns, { decodeValue: true, subtreeBase: true, code: ns.prefix + "/bad-name-constraints" });
|
|
1264
1641
|
var _h = _decodeHelpers(ns), decodeTop = _h.decodeTop, seqChildren = _h.seqChildren, readInt = _h.readInt, O = _h.O;
|
|
@@ -1686,7 +2063,114 @@ function certExtensionDecoders(ns) {
|
|
|
1686
2063
|
return hash;
|
|
1687
2064
|
}
|
|
1688
2065
|
|
|
2066
|
+
/** @internal RFC 7633 sec. 4: Features ::= SEQUENCE OF INTEGER, each an entry of the TLS
|
|
2067
|
+
* ExtensionType registry. The type carries no range, so a feature is read as the INTEGER it is
|
|
2068
|
+
* and a number outside the registry's 16-bit width is the caller's to judge. */
|
|
2069
|
+
function tlsFeature(buf) {
|
|
2070
|
+
var C = ns.prefix + "/bad-tls-feature";
|
|
2071
|
+
var kids = seqChildren(buf, C, "Features");
|
|
2072
|
+
return { features: _map(kids, function (n) {
|
|
2073
|
+
if (n.tagClass !== "universal" || n.tagNumber !== _T.INTEGER) throw ns.E(C, "a Features element must be an INTEGER (RFC 7633 sec. 4)");
|
|
2074
|
+
return readInt(n, C, "a feature");
|
|
2075
|
+
}) };
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
/** @internal RFC 3779 sec. 2.2.3. An IPAddress is a BIT STRING whose bits are the leading bits of
|
|
2079
|
+
* the address, so a prefix is its length in bits and a range is a pair of them. The address
|
|
2080
|
+
* family is the two-octet IANA AFI, with an optional third octet naming the SAFI. */
|
|
2081
|
+
function _ipAddress(n, C, what) {
|
|
2082
|
+
if (n.tagClass !== "universal" || n.tagNumber !== _T.BIT_STRING) throw ns.E(C, what + " must be a BIT STRING (RFC 3779 sec. 2.1.1)");
|
|
2083
|
+
var bs;
|
|
2084
|
+
try { bs = asn1.read.bitString(n); }
|
|
2085
|
+
catch (e) { throw ns.E(C, what + " must be a well-formed BIT STRING", e); }
|
|
2086
|
+
return { unusedBits: bs.unusedBits, bytes: bs.bytes, prefixLength: bs.bytes.length * 8 - bs.unusedBits };
|
|
2087
|
+
}
|
|
2088
|
+
function ipAddrBlocks(buf) {
|
|
2089
|
+
var C = ns.prefix + "/bad-ip-addr-blocks";
|
|
2090
|
+
var families = seqChildren(buf, C, "IPAddrBlocks");
|
|
2091
|
+
return { families: _map(families, function (f) {
|
|
2092
|
+
if (f.tagClass !== "universal" || f.tagNumber !== _T.SEQUENCE || !f.children || f.children.length !== 2) {
|
|
2093
|
+
throw ns.E(C, "an IPAddressFamily must be a SEQUENCE { addressFamily, ipAddressChoice } (RFC 3779 sec. 2.2.3)");
|
|
2094
|
+
}
|
|
2095
|
+
var afi;
|
|
2096
|
+
try { afi = _bufferConcat([asn1.read.octetString(f.children[0])]); }
|
|
2097
|
+
catch (e) { throw ns.E(C, "an IPAddressFamily addressFamily must be an OCTET STRING", e); }
|
|
2098
|
+
if (afi.length < 2 || afi.length > 3) throw ns.E(C, "an IPAddressFamily addressFamily must be 2 or 3 octets (RFC 3779 sec. 2.2.3.3)");
|
|
2099
|
+
var choice = f.children[1];
|
|
2100
|
+
var inherit = choice.tagClass === "universal" && choice.tagNumber === _T.NULL;
|
|
2101
|
+
if (inherit) {
|
|
2102
|
+
try { asn1.read.nullValue(choice); }
|
|
2103
|
+
catch (e) { throw ns.E(C, "an inherit IPAddressChoice must be a NULL carrying no content", e); }
|
|
2104
|
+
} else if (choice.tagClass !== "universal" || choice.tagNumber !== _T.SEQUENCE || !choice.children) {
|
|
2105
|
+
throw ns.E(C, "an IPAddressChoice must be an inherit NULL or a SEQUENCE OF IPAddressOrRange (RFC 3779 sec. 2.2.3.7)");
|
|
2106
|
+
}
|
|
2107
|
+
return {
|
|
2108
|
+
addressFamily: afi, afi: (afi[0] << 8) | afi[1], safi: afi.length === 3 ? afi[2] : null,
|
|
2109
|
+
inherit: inherit,
|
|
2110
|
+
addressesOrRanges: inherit ? null : _map(choice.children, function (r) {
|
|
2111
|
+
if (r.tagClass === "universal" && r.tagNumber === _T.BIT_STRING) {
|
|
2112
|
+
return { kind: "addressPrefix", addressPrefix: _ipAddress(r, C, "an addressPrefix") };
|
|
2113
|
+
}
|
|
2114
|
+
if (r.tagClass !== "universal" || r.tagNumber !== _T.SEQUENCE || !r.children || r.children.length !== 2) {
|
|
2115
|
+
throw ns.E(C, "an IPAddressOrRange must be an addressPrefix BIT STRING or an addressRange SEQUENCE { min, max } (RFC 3779 sec. 2.2.3.7)");
|
|
2116
|
+
}
|
|
2117
|
+
return { kind: "addressRange", min: _ipAddress(r.children[0], C, "an addressRange min"),
|
|
2118
|
+
max: _ipAddress(r.children[1], C, "an addressRange max") };
|
|
2119
|
+
}),
|
|
2120
|
+
};
|
|
2121
|
+
}) };
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
/** @internal RFC 3779 sec. 3.2.3. `ASId ::= INTEGER` carries no range, so an AS number is read as
|
|
2125
|
+
* the INTEGER it is and one outside the 32 bits RFC 6793 sec. 2 gives the registry is the
|
|
2126
|
+
* caller's to judge. Both arms of the SEQUENCE are EXPLICIT because ASIdentifierChoice is a
|
|
2127
|
+
* CHOICE. */
|
|
2128
|
+
function _asIdChoice(wrap, C, what) {
|
|
2129
|
+
if (!wrap.children || wrap.children.length !== 1) throw ns.E(C, "an " + what + " [n] must wrap exactly one ASIdentifierChoice (RFC 3779 sec. 3.2.3)");
|
|
2130
|
+
var n = wrap.children[0];
|
|
2131
|
+
if (n.tagClass === "universal" && n.tagNumber === _T.NULL) {
|
|
2132
|
+
try { asn1.read.nullValue(n); }
|
|
2133
|
+
catch (e) { throw ns.E(C, "an inherit ASIdentifierChoice must be a NULL carrying no content", e); }
|
|
2134
|
+
return { inherit: true, asIdsOrRanges: null };
|
|
2135
|
+
}
|
|
2136
|
+
if (n.tagClass !== "universal" || n.tagNumber !== _T.SEQUENCE || !n.children) {
|
|
2137
|
+
throw ns.E(C, "an ASIdentifierChoice must be an inherit NULL or a SEQUENCE OF ASIdOrRange (RFC 3779 sec. 3.2.3)");
|
|
2138
|
+
}
|
|
2139
|
+
function asId(node, label) {
|
|
2140
|
+
if (node.tagClass !== "universal" || node.tagNumber !== _T.INTEGER) throw ns.E(C, label + " must be an INTEGER (RFC 3779 sec. 3.2.3)");
|
|
2141
|
+
return readInt(node, C, label);
|
|
2142
|
+
}
|
|
2143
|
+
return { inherit: false, asIdsOrRanges: _map(n.children, function (r) {
|
|
2144
|
+
if (r.tagClass === "universal" && r.tagNumber === _T.INTEGER) return { kind: "id", id: asId(r, "an ASId") };
|
|
2145
|
+
if (r.tagClass !== "universal" || r.tagNumber !== _T.SEQUENCE || !r.children || r.children.length !== 2) {
|
|
2146
|
+
throw ns.E(C, "an ASIdOrRange must be an ASId INTEGER or an ASRange SEQUENCE { min, max } (RFC 3779 sec. 3.2.3)");
|
|
2147
|
+
}
|
|
2148
|
+
return { kind: "range", min: asId(r.children[0], "an ASRange min"), max: asId(r.children[1], "an ASRange max") };
|
|
2149
|
+
}) };
|
|
2150
|
+
}
|
|
2151
|
+
function autonomousSysIds(buf) {
|
|
2152
|
+
var C = ns.prefix + "/bad-autonomous-sys-ids";
|
|
2153
|
+
var kids = seqChildren(buf, C, "ASIdentifiers");
|
|
2154
|
+
var asnum = null, rdi = null, seen = -1;
|
|
2155
|
+
for (var i = 0; i < kids.length; i++) {
|
|
2156
|
+
var k = kids[i];
|
|
2157
|
+
if (k.tagClass !== "context" || (k.tagNumber !== 0 && k.tagNumber !== 1)) {
|
|
2158
|
+
throw ns.E(C, "ASIdentifiers carries only asnum [0] and rdi [1] (RFC 3779 sec. 3.2.3)");
|
|
2159
|
+
}
|
|
2160
|
+
if (k.tagNumber <= seen) throw ns.E(C, "ASIdentifiers components must appear in tag order and at most once (RFC 3779 sec. 3.2.3)");
|
|
2161
|
+
seen = k.tagNumber;
|
|
2162
|
+
if (k.tagNumber === 0) asnum = _asIdChoice(k, C, "asnum");
|
|
2163
|
+
else rdi = _asIdChoice(k, C, "rdi");
|
|
2164
|
+
}
|
|
2165
|
+
return { asnum: asnum, rdi: rdi };
|
|
2166
|
+
}
|
|
2167
|
+
|
|
1689
2168
|
var byOid = _create(null);
|
|
2169
|
+
byOid[O("tlsFeature")] = tlsFeature;
|
|
2170
|
+
byOid[O("ipAddrBlocks")] = ipAddrBlocks;
|
|
2171
|
+
byOid[O("ipAddrBlocksV2")] = ipAddrBlocks;
|
|
2172
|
+
byOid[O("autonomousSysIds")] = autonomousSysIds;
|
|
2173
|
+
byOid[O("autonomousSysIdsV2")] = autonomousSysIds;
|
|
1690
2174
|
byOid[O("qcStatements")] = qcStatements;
|
|
1691
2175
|
byOid[O("basicConstraints")] = basicConstraints;
|
|
1692
2176
|
byOid[O("keyUsage")] = keyUsage;
|
|
@@ -1919,7 +2403,13 @@ module.exports = {
|
|
|
1919
2403
|
rawNonEmptySequence: rawNonEmptySequence,
|
|
1920
2404
|
CRL_REASON_NAMES: CRL_REASON_NAMES,
|
|
1921
2405
|
certFixedCriticality: certFixedCriticality,
|
|
2406
|
+
crlFixedCriticality: crlFixedCriticality,
|
|
2407
|
+
crlEntryFixedCriticality: crlEntryFixedCriticality,
|
|
1922
2408
|
certExtensionDecoders: certExtensionDecoders,
|
|
2409
|
+
scopedCertDecoder: scopedCertDecoder,
|
|
2410
|
+
crlExtensionDecoders: crlExtensionDecoders,
|
|
2411
|
+
decodeExtensionList: decodeExtensionList,
|
|
2412
|
+
extensionDoor: extensionDoor,
|
|
1923
2413
|
_decodeHelpers: _decodeHelpers,
|
|
1924
2414
|
attribute: attribute,
|
|
1925
2415
|
extension: extension,
|