@blamejs/pki 0.8.12 → 0.8.15
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 +47 -0
- package/README.md +2 -1
- package/index.js +6 -0
- package/lib/acme.js +13 -5
- package/lib/attrcert-sign.js +3 -3
- package/lib/cmc-build.js +9 -9
- package/lib/cmc-verify.js +5 -5
- package/lib/cmp-build.js +3 -4
- package/lib/cmp-session.js +6 -6
- package/lib/cms-verify.js +4 -4
- package/lib/crl-sign.js +1 -1
- package/lib/crmf-sign.js +1 -1
- package/lib/ct.js +2 -2
- package/lib/est.js +12 -13
- package/lib/framework-error.js +3 -0
- package/lib/guard-header.js +15 -1
- package/lib/guard-identifier.js +34 -0
- package/lib/hpke.js +7 -2
- package/lib/http-transport.js +5 -6
- package/lib/identity-match.js +4 -3
- package/lib/ocsp.js +5 -3
- package/lib/oid.js +1 -1
- package/lib/path-validate.js +155 -16
- package/lib/pki-build.js +1 -1
- package/lib/revocation-fetch.js +1 -1
- package/lib/scep.js +21 -9
- package/lib/schema-all.js +10 -1
- package/lib/schema-engine.js +7 -2
- package/lib/schema-pkix.js +40 -0
- package/lib/schema-trustanchor.js +469 -0
- package/lib/schema-x509.js +66 -33
- package/lib/smime.js +5 -1
- package/lib/trust.js +502 -11
- package/lib/trustanchor-build.js +278 -0
- package/lib/validator-tpm.js +1 -1
- package/lib/webauthn-mds.js +1 -1
- package/lib/webauthn.js +9 -10
- package/lib/webcrypto.js +3 -3
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/http-transport.js
CHANGED
|
@@ -279,10 +279,10 @@ function _copyableArray(a) {
|
|
|
279
279
|
function snapshotProxy(p) {
|
|
280
280
|
try {
|
|
281
281
|
if (!_copyableRecord(p)) return p;
|
|
282
|
-
var out =
|
|
283
|
-
if (_copyableRecord(p.auth)) out.auth =
|
|
282
|
+
var out = guard.identifier.ownOptions(p);
|
|
283
|
+
if (_copyableRecord(p.auth)) out.auth = guard.identifier.ownOptions(p.auth);
|
|
284
284
|
if (_copyableRecord(p.tls)) {
|
|
285
|
-
out.tls =
|
|
285
|
+
out.tls = guard.identifier.ownOptions(p.tls);
|
|
286
286
|
var a = p.tls.anchors;
|
|
287
287
|
if (_copyableArray(a)) {
|
|
288
288
|
var copied = [];
|
|
@@ -492,7 +492,7 @@ function httpsTransport(defaults) {
|
|
|
492
492
|
var bodyIsFn = typeof body === "function";
|
|
493
493
|
if (!bodyIsFn && guard.bytes.isByteSource(body)) body = guard.bytes.source(body, E, C("bad-input"), "the request body");
|
|
494
494
|
|
|
495
|
-
var reqHeaders =
|
|
495
|
+
var reqHeaders = guard.identifier.ownOptions(request.headers);
|
|
496
496
|
Object.keys(reqHeaders).forEach(function (k) { var lk = k.toLowerCase(); if (lk === "proxy-authorization" || lk === "proxy-connection") delete reqHeaders[k]; });
|
|
497
497
|
if (bodyIsFn) {
|
|
498
498
|
Object.keys(reqHeaders).forEach(function (k) { var lk = k.toLowerCase(); if (lk === "content-length" || lk === "transfer-encoding" || lk === "expect") delete reqHeaders[k]; });
|
|
@@ -609,8 +609,7 @@ function httpsTransport(defaults) {
|
|
|
609
609
|
if (settled) return;
|
|
610
610
|
settled = true;
|
|
611
611
|
clearTimer();
|
|
612
|
-
var lower =
|
|
613
|
-
Object.keys(res.headers || {}).forEach(function (k) { lower[k.toLowerCase()] = res.headers[k]; });
|
|
612
|
+
var lower = guard.header.lowerCased(res.headers);
|
|
614
613
|
resolve({
|
|
615
614
|
status: res.statusCode,
|
|
616
615
|
headers: lower,
|
package/lib/identity-match.js
CHANGED
|
@@ -199,9 +199,10 @@ function _reference(r, index) {
|
|
|
199
199
|
* afterwards would otherwise name an identity nothing compared. A string is already a value; bytes
|
|
200
200
|
* are the copy taken at the door. */
|
|
201
201
|
function _reported(record) {
|
|
202
|
-
var out = intrinsic.create(null)
|
|
203
|
-
|
|
204
|
-
|
|
202
|
+
var out = intrinsic.create(null);
|
|
203
|
+
var names = guard.identifier.optionNames(record);
|
|
204
|
+
for (var i = 0; i < names.length; i++) {
|
|
205
|
+
if (record[names[i]] !== undefined) out[names[i]] = record[names[i]];
|
|
205
206
|
}
|
|
206
207
|
return intrinsic.freeze(out);
|
|
207
208
|
}
|
package/lib/ocsp.js
CHANGED
|
@@ -390,7 +390,10 @@ function _snapshotSignerKey(key, owned) {
|
|
|
390
390
|
return copy;
|
|
391
391
|
}
|
|
392
392
|
if (key && typeof key === "object" && (key.mldsa != null || key.trad != null)) {
|
|
393
|
-
|
|
393
|
+
var ks = guard.identifier.ownOptions(key);
|
|
394
|
+
ks.mldsa = _snapshotSignerKey(key.mldsa, owned);
|
|
395
|
+
ks.trad = _snapshotSignerKey(key.trad, owned);
|
|
396
|
+
return ks;
|
|
394
397
|
}
|
|
395
398
|
return key;
|
|
396
399
|
}
|
|
@@ -925,8 +928,7 @@ function httpRequest(der, responderUrl, opts) {
|
|
|
925
928
|
var HTTP_REQUEST_OPTS = Object.assign(Object.create(null), { method: 1 });
|
|
926
929
|
|
|
927
930
|
function _frozenHeaders(h) {
|
|
928
|
-
var out =
|
|
929
|
-
for (k in h) { if (Object.prototype.hasOwnProperty.call(h, k)) out[k] = h[k]; }
|
|
931
|
+
var out = guard.identifier.ownOptions(h);
|
|
930
932
|
return Object.freeze(out);
|
|
931
933
|
}
|
|
932
934
|
|
package/lib/oid.js
CHANGED
|
@@ -217,7 +217,7 @@ var FAMILIES = Object.assign(Object.create(null), {
|
|
|
217
217
|
hmacWithSHA1: 7, hmacWithSHA224: 8, hmacWithSHA256: 9,
|
|
218
218
|
hmacWithSHA384: 10, hmacWithSHA512: 11 } },
|
|
219
219
|
|
|
220
|
-
smimeCt: { base: [1, 2, 840, 113549, 1, 9, 16, 1], of: { authData: 2, tSTInfo: 4, compressedData: 9, encKeyWithID: 21, authEnvelopedData: 23 } },
|
|
220
|
+
smimeCt: { base: [1, 2, 840, 113549, 1, 9, 16, 1], of: { authData: 2, tSTInfo: 4, compressedData: 9, encKeyWithID: 21, authEnvelopedData: 23, trustAnchorList: 34 } },
|
|
221
221
|
|
|
222
222
|
smimeOri: { base: [1, 2, 840, 113549, 1, 9, 16, 13], of: { kem: 3 } },
|
|
223
223
|
|
package/lib/path-validate.js
CHANGED
|
@@ -804,16 +804,69 @@ function _anchorSubtreeSeeds(anchor) {
|
|
|
804
804
|
};
|
|
805
805
|
}
|
|
806
806
|
|
|
807
|
-
|
|
807
|
+
/**
|
|
808
|
+
* @internal An RFC 5914 trust anchor's `certPath` constraints, read off the anchor `pki.trust`
|
|
809
|
+
* produced. Absent on an anchor from any other source, so every reader below sees empty lists and
|
|
810
|
+
* nothing changes for a caller who does not use the format.
|
|
811
|
+
*/
|
|
812
|
+
function _anchorRfc5914Constraints(anchor) {
|
|
813
|
+
var empty = { permitted: [], excluded: [], policySet: null, policyFlags: null, pathLenConstraint: null };
|
|
814
|
+
var c = anchor === null || anchor === undefined ? undefined : anchor.constraints;
|
|
815
|
+
if (!c || typeof c !== "object") return empty;
|
|
816
|
+
var nc = c.nameConstraints;
|
|
817
|
+
return {
|
|
818
|
+
permitted: nc && intrinsic.isArray(nc.permitted) ? checkedSubtreeSeeds(nc.permitted, "trustAnchors[].constraints.nameConstraints.permitted") : [],
|
|
819
|
+
excluded: nc && intrinsic.isArray(nc.excluded) ? checkedSubtreeSeeds(nc.excluded, "trustAnchors[].constraints.nameConstraints.excluded") : [],
|
|
820
|
+
policySet: intrinsic.isArray(c.policySet) ? c.policySet : null,
|
|
821
|
+
policyFlags: c.policyFlags && typeof c.policyFlags === "object" ? c.policyFlags : null,
|
|
822
|
+
pathLenConstraint: intrinsic.isInteger(c.pathLenConstraint) ? c.pathLenConstraint : null,
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* @internal RFC 5937 sec. 3.2, the policy set. anyPolicy on either side means "any policy satisfies
|
|
828
|
+
* me", so intersecting with it leaves the other side's set untouched: filtering it literally would
|
|
829
|
+
* compare a concrete identifier against the wildcard identifier, find no match, and refuse a path
|
|
830
|
+
* both sides accept. Only where BOTH name concrete policies is the intersection a comparison, and
|
|
831
|
+
* an empty result there is a real answer rather than "unconstrained": the two name disjoint policy
|
|
832
|
+
* spaces, so no policy satisfies both.
|
|
833
|
+
*/
|
|
834
|
+
function _mergedPolicySet(callerSet, anchorSet) {
|
|
835
|
+
if (anchorSet === null) return callerSet;
|
|
836
|
+
var callerAny = callerSet === undefined || callerSet === null || contains(callerSet, OID.anyPolicy);
|
|
837
|
+
var anchorAny = contains(anchorSet, OID.anyPolicy);
|
|
838
|
+
if (anchorAny && callerAny) return [OID.anyPolicy];
|
|
839
|
+
if (anchorAny) return intrinsic.map(callerSet, function (p) { return p; });
|
|
840
|
+
if (callerAny) return intrinsic.map(anchorSet, function (p) { return p; });
|
|
841
|
+
return intrinsic.filter(anchorSet, function (p) { return contains(callerSet, p); });
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* @internal RFC 5937 sec. 3.2, the flags. Each is one-directional: a flag the anchor asserts turns
|
|
846
|
+
* the matching initial value on where it was off and never off where it was on, so an anchor can
|
|
847
|
+
* only narrow what a caller asked for. That is what makes RFC 5914's "always enforced" safe.
|
|
848
|
+
*/
|
|
849
|
+
function _flagSet(callerValue, anchorFlags, name) {
|
|
850
|
+
if (callerValue) return true;
|
|
851
|
+
return anchorFlags !== null && anchorFlags[name] === true;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
function initialize(certs, params, seeds, anchor, ta) {
|
|
808
855
|
var n = certs.length;
|
|
809
|
-
var state = _initialState(certs, params, seeds, anchor, n);
|
|
856
|
+
var state = _initialState(certs, params, seeds, anchor, n, ta);
|
|
810
857
|
/** @internal The root is the depth-zero anyPolicy node, and it is indexed like any other, so the
|
|
811
858
|
* "is there a node at this depth for this policy" lookup answers for depth zero too. */
|
|
812
859
|
policyLayer(state, 0)[OID.anyPolicy] = state.validPolicyGraph;
|
|
813
860
|
return state;
|
|
814
861
|
}
|
|
815
862
|
|
|
816
|
-
function _initialState(certs, params, seeds, anchor, n) {
|
|
863
|
+
function _initialState(certs, params, seeds, anchor, n, ta) {
|
|
864
|
+
/** @internal The anchor's constraints are read HERE rather than merged into a copy of `params`,
|
|
865
|
+
* because a copy answers only for the own properties the copy loop enumerated. A caller option
|
|
866
|
+
* defined non-enumerably would be dropped by the copy and the anchor would then WIDEN the
|
|
867
|
+
* validation, which is the one thing sec. 3.2 does not allow. Reading the caller's object
|
|
868
|
+
* directly keeps every option live whatever its descriptor. */
|
|
869
|
+
var anchorPathLen = ta.pathLenConstraint;
|
|
817
870
|
return {
|
|
818
871
|
validPolicyGraph: graphRoot(),
|
|
819
872
|
policyIndex: [],
|
|
@@ -822,15 +875,19 @@ function _initialState(certs, params, seeds, anchor, n) {
|
|
|
822
875
|
minRsaModulusBits: params.minRsaModulusBits !== undefined ? params.minRsaModulusBits : constants.LIMITS.RSA_MIN_MODULUS_BITS,
|
|
823
876
|
permittedGenerations: intrinsic.filter(seeds.permittedGenerations, function (g) { return g.length > 0; }),
|
|
824
877
|
excludedSubtrees: seeds.excluded,
|
|
825
|
-
explicitPolicy: params.initialExplicitPolicy ? 0 : n + 1,
|
|
826
|
-
inhibitAnyPolicy: params.initialAnyPolicyInhibit ? 0 : n + 1,
|
|
827
|
-
policyMapping: params.initialPolicyMappingInhibit ? 0 : n + 1,
|
|
878
|
+
explicitPolicy: _flagSet(params.initialExplicitPolicy, ta.policyFlags, "requireExplicitPolicy") ? 0 : n + 1,
|
|
879
|
+
inhibitAnyPolicy: _flagSet(params.initialAnyPolicyInhibit, ta.policyFlags, "inhibitAnyPolicy") ? 0 : n + 1,
|
|
880
|
+
policyMapping: _flagSet(params.initialPolicyMappingInhibit, ta.policyFlags, "inhibitPolicyMapping") ? 0 : n + 1,
|
|
828
881
|
workingPublicKeyAlgorithm: anchor.algorithm,
|
|
829
882
|
workingPublicKey: anchor.publicKey,
|
|
830
883
|
workingPublicKeyParameters: anchor.parameters || null,
|
|
831
884
|
workingIssuerName: anchor.name,
|
|
832
|
-
|
|
833
|
-
|
|
885
|
+
/** @internal RFC 5937 sec. 3.2: "If a basic constraints extension is associated with the trust
|
|
886
|
+
* anchor and contains a pathLenConstraint value, set the max_path_length state variable equal
|
|
887
|
+
* to the pathLenConstraint value". Taken as the lower of the two, since sec. 6.1.2's own
|
|
888
|
+
* initial value is the path length and an anchor may only narrow. */
|
|
889
|
+
maxPathLength: intrinsic.isInteger(anchorPathLen) && anchorPathLen < n ? anchorPathLen : n,
|
|
890
|
+
userInitialPolicySet: _mergedPolicySet(params.userInitialPolicySet, ta.policySet) || [OID.anyPolicy],
|
|
834
891
|
results: [],
|
|
835
892
|
};
|
|
836
893
|
}
|
|
@@ -1356,16 +1413,22 @@ async function _validate(path, opts, run) {
|
|
|
1356
1413
|
guard.limits.cap(opts.maxPolicyNodes, "validate: opts.maxPolicyNodes", undefined, { E: E, code: "path/bad-input", min: 1 });
|
|
1357
1414
|
_assertUserInitialPolicySet(opts.userInitialPolicySet, "validate");
|
|
1358
1415
|
var anchorNc = _anchorSubtreeSeeds(anchor);
|
|
1416
|
+
/** @internal RFC 5937 sec. 3.2 says how an RFC 5914 anchor's own constraints reach this
|
|
1417
|
+
* algorithm, and the rule is uniform: the stricter of the anchor's value and the caller's wins.
|
|
1418
|
+
* Its name constraints are a SEPARATE generation from the caller's and from the `nameConstraints`
|
|
1419
|
+
* overlay, because generations intersect and a replacement would let one loosen the other. */
|
|
1420
|
+
var taConstraints = _anchorRfc5914Constraints(anchor);
|
|
1359
1421
|
var seeds = {
|
|
1360
|
-
permittedGenerations: [callerPermitted, anchorNc.permitted],
|
|
1361
|
-
excluded: intrinsic.concat(callerExcluded, anchorNc.excluded),
|
|
1422
|
+
permittedGenerations: [callerPermitted, anchorNc.permitted, taConstraints.permitted],
|
|
1423
|
+
excluded: intrinsic.concat(intrinsic.concat(callerExcluded, anchorNc.excluded), taConstraints.excluded),
|
|
1362
1424
|
};
|
|
1363
|
-
var anchorNameConstraintsApplied = anchorNc.permitted.length > 0 || anchorNc.excluded.length > 0
|
|
1425
|
+
var anchorNameConstraintsApplied = anchorNc.permitted.length > 0 || anchorNc.excluded.length > 0 ||
|
|
1426
|
+
taConstraints.permitted.length > 0 || taConstraints.excluded.length > 0;
|
|
1364
1427
|
var purposeOpts = resolvePurposeOpts(opts);
|
|
1365
1428
|
var requiredEku = purposeOpts.requiredEku;
|
|
1366
1429
|
var checkPurpose = purposeOpts.checkPurpose;
|
|
1367
1430
|
|
|
1368
|
-
var state = initialize(certs, opts, seeds, anchor);
|
|
1431
|
+
var state = initialize(certs, opts, seeds, anchor, taConstraints);
|
|
1369
1432
|
state._n = n;
|
|
1370
1433
|
state.requiredEku = requiredEku;
|
|
1371
1434
|
var verifier = opts.verifier || null;
|
|
@@ -2497,6 +2560,70 @@ function _precaptureAnchorNameConstraints(list, who) {
|
|
|
2497
2560
|
}
|
|
2498
2561
|
return out;
|
|
2499
2562
|
}
|
|
2563
|
+
var _TA_CONSTRAINT_KEYS = intrinsic.assign(intrinsic.create(null), {
|
|
2564
|
+
nameConstraints: 1, policySet: 1, policyFlags: 1, pathLenConstraint: 1 });
|
|
2565
|
+
var _TA_FLAG_KEYS = intrinsic.assign(intrinsic.create(null), {
|
|
2566
|
+
inhibitPolicyMapping: 1, requireExplicitPolicy: 1, inhibitAnyPolicy: 1 });
|
|
2567
|
+
/**
|
|
2568
|
+
* @internal An RFC 5914 anchor's own constraints, snapshotted the way every other anchor field is:
|
|
2569
|
+
* read once from its own data property, copied, and never read again. A caller object that answered
|
|
2570
|
+
* one value to the check and another to the enforcement would otherwise decide the namespace and
|
|
2571
|
+
* the policy set a path is held to.
|
|
2572
|
+
*/
|
|
2573
|
+
function _copyAnchorRfc5914Constraints(c, who) {
|
|
2574
|
+
var label = who + ": a trustAnchor constraints";
|
|
2575
|
+
if (c === null || typeof c !== "object" || intrinsic.isArray(c)) {
|
|
2576
|
+
throw E("path/bad-input", label + " must be a { nameConstraints, policySet, policyFlags, pathLenConstraint } object");
|
|
2577
|
+
}
|
|
2578
|
+
guard.identifier.assertPlainRecord(c, E, "path/bad-input", label);
|
|
2579
|
+
guard.identifier.assertKnownKeys(c, _TA_CONSTRAINT_KEYS, E, "path/bad-input",
|
|
2580
|
+
label + " names nameConstraints, policySet, policyFlags and pathLenConstraint. The unknown key was: ");
|
|
2581
|
+
var out = {};
|
|
2582
|
+
var ncD = intrinsic.getOwnPropertyDescriptor(c, "nameConstraints");
|
|
2583
|
+
if (ncD !== undefined) {
|
|
2584
|
+
if (!intrinsic.hasOwn(ncD, "value")) throw E("path/bad-input", label + ".nameConstraints must be a data property, not an accessor");
|
|
2585
|
+
intrinsic.defineProperty(out, "nameConstraints", { value: _copyAnchorNameConstraints(ncD.value, who + ": a trustAnchor constraints"), enumerable: true, configurable: true, writable: true });
|
|
2586
|
+
}
|
|
2587
|
+
var psD = intrinsic.getOwnPropertyDescriptor(c, "policySet");
|
|
2588
|
+
if (psD !== undefined) {
|
|
2589
|
+
if (!intrinsic.hasOwn(psD, "value")) throw E("path/bad-input", label + ".policySet must be a data property, not an accessor");
|
|
2590
|
+
var ps = psD.value;
|
|
2591
|
+
if (!intrinsic.isArray(ps) || intrinsic.types.isProxy(ps)) throw E("path/bad-input", label + ".policySet must be a plain array of policy identifiers");
|
|
2592
|
+
var psOut = [];
|
|
2593
|
+
for (var i = 0; i < ps.length; i++) {
|
|
2594
|
+
var pd = intrinsic.getOwnPropertyDescriptor(ps, i);
|
|
2595
|
+
if (pd === undefined || !intrinsic.hasOwn(pd, "value")) throw E("path/bad-input", label + ".policySet entry " + i + " must be a data property, not a hole or an accessor");
|
|
2596
|
+
if (typeof pd.value !== "string" || pd.value.length === 0) throw E("path/bad-input", label + ".policySet entries must be dotted-decimal policy identifiers");
|
|
2597
|
+
guard.list.append(psOut, guard.identifier.assertCanonicalOid(pd.value, E, "path/bad-input", label + ".policySet entry " + pd.value));
|
|
2598
|
+
}
|
|
2599
|
+
intrinsic.defineProperty(out, "policySet", { value: psOut, enumerable: true, configurable: true, writable: true });
|
|
2600
|
+
}
|
|
2601
|
+
var pfD = intrinsic.getOwnPropertyDescriptor(c, "policyFlags");
|
|
2602
|
+
if (pfD !== undefined) {
|
|
2603
|
+
if (!intrinsic.hasOwn(pfD, "value")) throw E("path/bad-input", label + ".policyFlags must be a data property, not an accessor");
|
|
2604
|
+
var pf = pfD.value;
|
|
2605
|
+
if (pf === null || typeof pf !== "object" || intrinsic.isArray(pf)) throw E("path/bad-input", label + ".policyFlags must be an object of boolean flags");
|
|
2606
|
+
guard.identifier.assertPlainRecord(pf, E, "path/bad-input", label + ".policyFlags");
|
|
2607
|
+
guard.identifier.assertKnownKeys(pf, _TA_FLAG_KEYS, E, "path/bad-input", label + ".policyFlags names the three RFC 5914 flags. The unknown key was: ");
|
|
2608
|
+
var fOut = {};
|
|
2609
|
+
intrinsic.forEach(intrinsic.keys(_TA_FLAG_KEYS), function (k) {
|
|
2610
|
+
var fd = intrinsic.getOwnPropertyDescriptor(pf, k);
|
|
2611
|
+
if (fd === undefined) return;
|
|
2612
|
+
if (!intrinsic.hasOwn(fd, "value")) throw E("path/bad-input", label + ".policyFlags." + k + " must be a data property, not an accessor");
|
|
2613
|
+
if (typeof fd.value !== "boolean") throw E("path/bad-input", label + ".policyFlags." + k + " must be a boolean");
|
|
2614
|
+
intrinsic.defineProperty(fOut, k, { value: fd.value, enumerable: true, configurable: true, writable: true });
|
|
2615
|
+
});
|
|
2616
|
+
intrinsic.defineProperty(out, "policyFlags", { value: fOut, enumerable: true, configurable: true, writable: true });
|
|
2617
|
+
}
|
|
2618
|
+
var plD = intrinsic.getOwnPropertyDescriptor(c, "pathLenConstraint");
|
|
2619
|
+
if (plD !== undefined) {
|
|
2620
|
+
if (!intrinsic.hasOwn(plD, "value")) throw E("path/bad-input", label + ".pathLenConstraint must be a data property, not an accessor");
|
|
2621
|
+
if (!intrinsic.isInteger(plD.value) || plD.value < 0) throw E("path/bad-input", label + ".pathLenConstraint must be a non-negative integer");
|
|
2622
|
+
intrinsic.defineProperty(out, "pathLenConstraint", { value: plD.value, enumerable: true, configurable: true, writable: true });
|
|
2623
|
+
}
|
|
2624
|
+
return out;
|
|
2625
|
+
}
|
|
2626
|
+
|
|
2500
2627
|
function _copyAnchorNameConstraints(nc, who) {
|
|
2501
2628
|
if (nc === null || typeof nc !== "object" || intrinsic.isArray(nc)) {
|
|
2502
2629
|
throw E("path/bad-input", who + ": a trustAnchor nameConstraints must be a { permitted, excluded } object of { tag, base } subtree entries");
|
|
@@ -2565,7 +2692,7 @@ function _copyRdnsDeep(rdns) {
|
|
|
2565
2692
|
function toAnchor(entry, verb) {
|
|
2566
2693
|
var who = verb || "build";
|
|
2567
2694
|
var isTuple = false, alg = null, pubRaw, nameRaw = null, pk = null, purposesSnap, distrustSnap;
|
|
2568
|
-
var subjectDerRaw, labelRaw, mozRaw, ncSnap;
|
|
2695
|
+
var subjectDerRaw, labelRaw, mozRaw, ncSnap, taSnap;
|
|
2569
2696
|
if (intrinsic.types.isProxy(entry)) {
|
|
2570
2697
|
throw E("path/bad-input", who + ": a trustAnchor must not be a Proxy");
|
|
2571
2698
|
}
|
|
@@ -2584,6 +2711,8 @@ function toAnchor(entry, verb) {
|
|
|
2584
2711
|
distrustSnap = _normalizeConstraintField(dRaw, E, "path/bad-input", who);
|
|
2585
2712
|
var ncRaw = _captureConstraintMap(entry, "nameConstraints", E, "path/bad-input", who);
|
|
2586
2713
|
ncSnap = ncRaw == null ? undefined : _copyAnchorNameConstraints(ncRaw, who);
|
|
2714
|
+
var taRaw = _captureConstraintMap(entry, "constraints", E, "path/bad-input", who);
|
|
2715
|
+
taSnap = taRaw == null ? undefined : _copyAnchorRfc5914Constraints(taRaw, who);
|
|
2587
2716
|
subjectDerRaw = _captureOwnData(entry, "subjectDer");
|
|
2588
2717
|
labelRaw = _captureOwnData(entry, "label");
|
|
2589
2718
|
mozRaw = _captureOwnData(entry, "mozillaCaPolicy");
|
|
@@ -2652,6 +2781,7 @@ function toAnchor(entry, verb) {
|
|
|
2652
2781
|
if (purposesSnap !== undefined) intrinsic.defineProperty(flat, "purposes", { value: purposesSnap, enumerable: true, configurable: true, writable: true });
|
|
2653
2782
|
if (distrustSnap !== undefined) intrinsic.defineProperty(flat, "distrustAfter", { value: distrustSnap, enumerable: true, configurable: true, writable: true });
|
|
2654
2783
|
if (ncSnap !== undefined) intrinsic.defineProperty(flat, "nameConstraints", { value: ncSnap, enumerable: true, configurable: true, writable: true });
|
|
2784
|
+
if (taSnap !== undefined) intrinsic.defineProperty(flat, "constraints", { value: taSnap, enumerable: true, configurable: true, writable: true });
|
|
2655
2785
|
if (subjectDerRaw !== undefined) intrinsic.defineProperty(flat, "subjectDer", { value: subjectDerRaw, enumerable: true, configurable: true, writable: true });
|
|
2656
2786
|
if (labelRaw !== undefined) intrinsic.defineProperty(flat, "label", { value: labelRaw, enumerable: true, configurable: true, writable: true });
|
|
2657
2787
|
if (mozRaw !== undefined) intrinsic.defineProperty(flat, "mozillaCaPolicy", { value: mozRaw, enumerable: true, configurable: true, writable: true });
|
|
@@ -2667,7 +2797,17 @@ function toAnchor(entry, verb) {
|
|
|
2667
2797
|
try { cert = coerceCert(entry); }
|
|
2668
2798
|
catch (e) { throw E("path/bad-input", who + ": a trustAnchor entry must be a { name, publicKey, algorithm } tuple or a certificate", e); }
|
|
2669
2799
|
var spki = cert.subjectPublicKeyInfo;
|
|
2670
|
-
|
|
2800
|
+
var out = { name: cert.subject, publicKey: spki.bytes, algorithm: spki.algorithm.oid, parameters: spki.algorithm.parameters, subjectDer: cert.subject.bytes };
|
|
2801
|
+
/** @internal The certificate's own basic constraints are associated with the anchor, so its
|
|
2802
|
+
* pathLenConstraint bounds the path below it (RFC 5937 sec. 3.2). Defined rather than assigned,
|
|
2803
|
+
* for the reason every other anchor field is. */
|
|
2804
|
+
var certPathLen = pkix.anchorPathLenFromExtensions(cert.extensions, NS, who + ": the trustAnchor certificate");
|
|
2805
|
+
if (certPathLen !== null) {
|
|
2806
|
+
var c = intrinsic.create(null);
|
|
2807
|
+
c.pathLenConstraint = certPathLen;
|
|
2808
|
+
guard.verdict.set(out, "constraints", c);
|
|
2809
|
+
}
|
|
2810
|
+
return out;
|
|
2671
2811
|
}
|
|
2672
2812
|
|
|
2673
2813
|
/**
|
|
@@ -2903,8 +3043,7 @@ async function _aiaFetchOne(uri, aia) {
|
|
|
2903
3043
|
: Buffer.from(res.body == null ? "" : String(res.body), "latin1");
|
|
2904
3044
|
if (body.length === 0) throw E("path/aia-empty", "an AIA fetch returned an empty body");
|
|
2905
3045
|
if (body.length > aia.maxResponseBytes) throw E("path/aia-too-large", "an AIA response exceeds the " + aia.maxResponseBytes + "-byte cap");
|
|
2906
|
-
var headers =
|
|
2907
|
-
Object.keys(res.headers || {}).forEach(function (k) { headers[k.toLowerCase()] = res.headers[k]; });
|
|
3046
|
+
var headers = guard.header.lowerCased(res.headers);
|
|
2908
3047
|
return _aiaParseBody(body, headers["content-type"], aia.maxCertsPerResponse);
|
|
2909
3048
|
}
|
|
2910
3049
|
|
package/lib/pki-build.js
CHANGED
|
@@ -436,7 +436,7 @@ function makeBuilder(ctx) {
|
|
|
436
436
|
}
|
|
437
437
|
function encodeRdn(rdnSpec) {
|
|
438
438
|
if (!rdnSpec || typeof rdnSpec !== "object" || Buffer.isBuffer(rdnSpec)) throw E("bad-name", "each RDN must be an object of { attributeName: value }");
|
|
439
|
-
var keys =
|
|
439
|
+
var keys = guard.identifier.optionNames(rdnSpec);
|
|
440
440
|
if (!keys.length) throw E("bad-name", "an RDN must carry at least one attribute");
|
|
441
441
|
return b.set(keys.map(function (k) { return encodeAtv(k, rdnSpec[k]); }));
|
|
442
442
|
}
|
package/lib/revocation-fetch.js
CHANGED
|
@@ -294,7 +294,7 @@ async function _fetchOne(state, req, what) {
|
|
|
294
294
|
|
|
295
295
|
function _contentType(headers) {
|
|
296
296
|
var h = headers || {};
|
|
297
|
-
var keys =
|
|
297
|
+
var keys = guard.identifier.optionNames(h);
|
|
298
298
|
for (var i = 0; i < keys.length; i++) {
|
|
299
299
|
if (keys[i].toLowerCase() !== "content-type") continue;
|
|
300
300
|
try { return mime.parse(String(h[keys[i]])).type; }
|
package/lib/scep.js
CHANGED
|
@@ -68,7 +68,12 @@ var MESSAGE_TYPE = Object.assign(Object.create(null), { CertRep: "3", RenewalReq
|
|
|
68
68
|
var PKI_STATUS = Object.assign(Object.create(null), { SUCCESS: "0", FAILURE: "2", PENDING: "3" });
|
|
69
69
|
var FAIL_INFO = Object.assign(Object.create(null), { badAlg: "0", badMessageCheck: "1", badRequest: "2", badTime: "3", badCertId: "4" });
|
|
70
70
|
|
|
71
|
-
function _invert(table) {
|
|
71
|
+
function _invert(table) {
|
|
72
|
+
var o = Object.create(null);
|
|
73
|
+
var names = guard.identifier.optionNames(table);
|
|
74
|
+
for (var i = 0; i < names.length; i++) o[table[names[i]]] = names[i];
|
|
75
|
+
return o;
|
|
76
|
+
}
|
|
72
77
|
var MESSAGE_TYPE_BY_CODE = _invert(MESSAGE_TYPE), PKI_STATUS_BY_CODE = _invert(PKI_STATUS), FAIL_INFO_BY_CODE = _invert(FAIL_INFO);
|
|
73
78
|
|
|
74
79
|
var _PARSE_SUPPORTED = Object.assign(Object.create(null), { PKCSReq: 1, RenewalReq: 1, CertRep: 1, CertPoll: 1, GetCert: 1, GetCRL: 1 });
|
|
@@ -621,10 +626,10 @@ function _trim(s) { var a = 0, e = s.length; while (a < e && _isWs(s.charCodeAt(
|
|
|
621
626
|
|
|
622
627
|
function _header(headers, name) {
|
|
623
628
|
if (!headers || typeof headers !== "object") return null;
|
|
624
|
-
var found = null, seen = 0
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
if (_foldLower(String(
|
|
629
|
+
var found = null, seen = 0;
|
|
630
|
+
var hNames = guard.identifier.optionNames(headers);
|
|
631
|
+
for (var hi = 0; hi < hNames.length; hi++) {
|
|
632
|
+
if (_foldLower(String(hNames[hi])) === name) { found = headers[hNames[hi]]; seen += 1; }
|
|
628
633
|
}
|
|
629
634
|
if (seen > 1) throw _err("scep/bad-response", "the response carried more than one " + name + " header");
|
|
630
635
|
return found == null ? null : String(found);
|
|
@@ -739,7 +744,7 @@ function _client(method, url, body, headers, opts, checked) {
|
|
|
739
744
|
maxResponseBytes: guard.limits.cap(opts.maxResponseBytes, "maxResponseBytes", C.LIMITS.HTTP_MAX_RESPONSE_BYTES, { E: _err, code: "scep/bad-input", min: 1, max: C.LIMITS.HTTP_MAX_RESPONSE_BYTES }),
|
|
740
745
|
maxRedirects: guard.limits.cap(opts.maxRedirects, "maxRedirects", 5, { E: _err, code: "scep/bad-input", min: 0, max: 32 }),
|
|
741
746
|
};
|
|
742
|
-
return _drive(method, url, body,
|
|
747
|
+
return _drive(method, url, body, guard.identifier.ownOptions(headers), opts, transport, budgets);
|
|
743
748
|
}
|
|
744
749
|
function _requireOk(res, expected, what) {
|
|
745
750
|
var status = res.status;
|
|
@@ -1056,7 +1061,7 @@ async function _pkiOperation(messageType, baseUrl, opts, verbName) {
|
|
|
1056
1061
|
var recipientKey = _captureRecipientKey(opts.recipientKey);
|
|
1057
1062
|
var signer = { cert: guard.bytes.snapshotSource(opts.signer.cert, ScepError, "scep/bad-input", "signer.cert"), key: opts.signer.key };
|
|
1058
1063
|
var clientOpts = {
|
|
1059
|
-
tls: opts.tls != null ?
|
|
1064
|
+
tls: opts.tls != null ? guard.identifier.ownOptions(opts.tls) : opts.tls,
|
|
1060
1065
|
maxRedirects: opts.maxRedirects,
|
|
1061
1066
|
allowCrossOriginRedirect: opts.allowCrossOriginRedirect,
|
|
1062
1067
|
timeout: opts.timeout,
|
|
@@ -1207,7 +1212,7 @@ async function _queryOperation(messageType, baseUrl, opts, verbName) {
|
|
|
1207
1212
|
var recipientKey = _captureRecipientKey(opts.recipientKey);
|
|
1208
1213
|
var signer = { cert: guard.bytes.snapshotSource(opts.signer.cert, ScepError, "scep/bad-input", "signer.cert"), key: opts.signer.key };
|
|
1209
1214
|
var want = _resolveQueryTarget(opts);
|
|
1210
|
-
var clientOpts = { tls: opts.tls != null ?
|
|
1215
|
+
var clientOpts = { tls: opts.tls != null ? guard.identifier.ownOptions(opts.tls) : opts.tls, maxRedirects: opts.maxRedirects, allowCrossOriginRedirect: opts.allowCrossOriginRedirect, timeout: opts.timeout, maxResponseBytes: opts.maxResponseBytes, proxy: _snapshotProxy(opts.proxy) };
|
|
1211
1216
|
if (checkedTransport !== undefined) clientOpts.transport = checkedTransport;
|
|
1212
1217
|
var senderNonce = nodeCrypto.randomBytes(16);
|
|
1213
1218
|
var transactionId = opts.transactionId != null ? opts.transactionId : _defaultTransactionId();
|
|
@@ -1320,7 +1325,14 @@ async function getCrl(baseUrl, opts) {
|
|
|
1320
1325
|
}
|
|
1321
1326
|
|
|
1322
1327
|
var _TRANSPORT_KEYS = Object.assign(Object.create(null), { transport: 1, timeout: 1, maxResponseBytes: 1, maxRedirects: 1, tls: 1, allowCrossOriginRedirect: 1, proxy: 1 });
|
|
1323
|
-
function _withKeys(extra) {
|
|
1328
|
+
function _withKeys(extra) {
|
|
1329
|
+
var o = Object.create(null);
|
|
1330
|
+
var base = guard.identifier.optionNames(_TRANSPORT_KEYS);
|
|
1331
|
+
for (var bi = 0; bi < base.length; bi++) o[base[bi]] = 1;
|
|
1332
|
+
var more = guard.identifier.optionNames(extra);
|
|
1333
|
+
for (var mi = 0; mi < more.length; mi++) o[more[mi]] = 1;
|
|
1334
|
+
return o;
|
|
1335
|
+
}
|
|
1324
1336
|
var _CACAPS_KEYS = _withKeys({ expectSCEPStandard: 1, requireStrongProfile: 1 });
|
|
1325
1337
|
var _CACERT_KEYS = _withKeys({ expectedFingerprint: 1, fingerprintAlgorithm: 1 });
|
|
1326
1338
|
var _NEXTCACERT_KEYS = _withKeys({ caCertificate: 1 });
|
package/lib/schema-all.js
CHANGED
|
@@ -47,6 +47,7 @@ var tsp = require("./schema-tsp");
|
|
|
47
47
|
var crmf = require("./schema-crmf");
|
|
48
48
|
var cmp = require("./schema-cmp");
|
|
49
49
|
var csrattrs = require("./schema-csrattrs");
|
|
50
|
+
var trustanchor = require("./schema-trustanchor");
|
|
50
51
|
var attrcert = require("./schema-attrcert");
|
|
51
52
|
var smime = require("./schema-smime");
|
|
52
53
|
var cmc = require("./schema-cmc");
|
|
@@ -88,6 +89,12 @@ var FORMATS = [
|
|
|
88
89
|
detect: csrattrs.matches,
|
|
89
90
|
parse: function (input, caps) { return csrattrs.parse(input, caps); },
|
|
90
91
|
},
|
|
92
|
+
{
|
|
93
|
+
name: "trustanchor",
|
|
94
|
+
module: trustanchor,
|
|
95
|
+
detect: trustanchor.matches,
|
|
96
|
+
parse: function (input, caps) { return trustanchor.parse(input, caps); },
|
|
97
|
+
},
|
|
91
98
|
{
|
|
92
99
|
name: "ocsp-request",
|
|
93
100
|
module: ocsp,
|
|
@@ -155,7 +162,7 @@ var FORMATS = [
|
|
|
155
162
|
* The names of every registered format, in detection order.
|
|
156
163
|
*
|
|
157
164
|
* @example
|
|
158
|
-
* pki.schema.all(); // -> ["cms", "tsp", "crmf", "cmp", "csrattrs", "ocsp-request", "ocsp-response", "pkcs12", "pkcs8", "csr", "attrcert", "attrcert-v1", "crl", "x509"]
|
|
165
|
+
* pki.schema.all(); // -> ["cms", "tsp", "crmf", "cmp", "csrattrs", "trustanchor", "ocsp-request", "ocsp-response", "pkcs12", "pkcs8", "csr", "attrcert", "attrcert-v1", "crl", "x509"]
|
|
159
166
|
*/
|
|
160
167
|
function all() { return FORMATS.map(function (f) { return f.name; }); }
|
|
161
168
|
|
|
@@ -368,6 +375,8 @@ module.exports = {
|
|
|
368
375
|
decodeExtensions: crmf.decodeExtensions, decodeExtension: crmf.decodeExtension },
|
|
369
376
|
cmp: { parse: cmp.parse, pemDecode: cmp.pemDecode, pemEncode: cmp.pemEncode },
|
|
370
377
|
csrattrs: { parse: csrattrs.parse },
|
|
378
|
+
trustanchor: { parse: trustanchor.parse, parseInfo: trustanchor.parseInfo,
|
|
379
|
+
pemDecode: trustanchor.pemDecode, pemEncode: trustanchor.pemEncode },
|
|
371
380
|
attrcert: { parse: attrcert.parse, pemDecode: attrcert.pemDecode, pemEncode: attrcert.pemEncode,
|
|
372
381
|
decodeExtensions: attrcert.decodeExtensions, decodeExtension: attrcert.decodeExtension },
|
|
373
382
|
smime: { parseSigningCertificate: smime.parseSigningCertificate, parseSigningCertificateV2: smime.parseSigningCertificateV2, parseSmimeCapabilities: smime.parseSmimeCapabilities, decodeAttribute: smime.decodeAttribute },
|
package/lib/schema-engine.js
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
var asn1 = require("./asn1-der.js");
|
|
30
|
+
var intrinsic = require("./guard-intrinsic");
|
|
30
31
|
|
|
31
32
|
var TAGS = asn1.TAGS;
|
|
32
33
|
|
|
@@ -339,8 +340,12 @@ function _walkSeq(schema, node, ctx) {
|
|
|
339
340
|
var kids = _assertShape(schema, node, ctx);
|
|
340
341
|
_assertArity(schema, kids, ctx);
|
|
341
342
|
|
|
342
|
-
|
|
343
|
-
|
|
343
|
+
/** @internal Prototype-less, so writing a field creates an own property instead of dispatching
|
|
344
|
+
* through an accessor of the same name on Object.prototype. Every format parser's record is
|
|
345
|
+
* built here, so a getter-only slot planted under any field name a schema declares would
|
|
346
|
+
* otherwise throw out of an ordinary parse, and a setter would swallow the decoded value. The
|
|
347
|
+
* absent-member test below reads it the same way. */
|
|
348
|
+
var fields = intrinsic.create(null);
|
|
344
349
|
var idx = 0;
|
|
345
350
|
for (var f = 0; f < schema.fields.length; f++) {
|
|
346
351
|
var fld = schema.fields[f];
|
package/lib/schema-pkix.js
CHANGED
|
@@ -1456,6 +1456,44 @@ function _extensionRow(ext, opts, index, state, decoded, code, profile) {
|
|
|
1456
1456
|
* impossible to READ a certificate carrying a private critical extension, which is what
|
|
1457
1457
|
* `pki.inspect` and `pki.lint` are for. RFC 5280 sec. 4.2's reject-on-critical rule is the
|
|
1458
1458
|
* caller's to apply, and the two facts it needs, `critical` and `state`, are on the row. */
|
|
1459
|
+
/** @internal RFC 5937 sec. 3.2: "If a basic constraints extension is associated with the trust
|
|
1460
|
+
* anchor and contains a pathLenConstraint value, set the max_path_length state variable equal to
|
|
1461
|
+
* the pathLenConstraint value from the basic constraints extension." A trust anchor associates
|
|
1462
|
+
* one in three ways: the certificate it is supplied as, the `exts` of an RFC 5914
|
|
1463
|
+
* `TrustAnchorInfo`, and that anchor's `certPath.pathLenConstraint`. The first two are an
|
|
1464
|
+
* extension list, so both read it here and neither restates the decode. Returns null only where
|
|
1465
|
+
* the list states no path length. An extension that does not decode is a refusal, not an absent
|
|
1466
|
+
* limit: nothing validates the anchor's own certificate, so reading an unreadable constraint as
|
|
1467
|
+
* "no constraint" would leave the anchor less restricted than the store that carries it says. */
|
|
1468
|
+
function anchorPathLenFromExtensions(list, ns, label) {
|
|
1469
|
+
if (!intrinsic.isArray(list)) return null;
|
|
1470
|
+
var bcOid = ns.oid.byName("basicConstraints");
|
|
1471
|
+
var decode = certExtensionDecoders(ns).byOid[bcOid];
|
|
1472
|
+
if (typeof decode !== "function") return null;
|
|
1473
|
+
var found = null;
|
|
1474
|
+
_forEach(list, function (ext) {
|
|
1475
|
+
if (!ext || ext.oid !== bcOid || found !== null) return;
|
|
1476
|
+
var value;
|
|
1477
|
+
try { value = decode(ext.value); }
|
|
1478
|
+
catch (e) {
|
|
1479
|
+
throw ns.E(ns.prefix + "/bad-basic-constraints", (label || "the trust anchor") +
|
|
1480
|
+
" carries a basicConstraints extension that does not decode, so the path length it states " +
|
|
1481
|
+
"cannot be read; a trust anchor's constraints are never checked against a path above it, " +
|
|
1482
|
+
"so this is refused rather than read as no constraint", e);
|
|
1483
|
+
}
|
|
1484
|
+
if (value && intrinsic.isInteger(value.pathLenConstraint)) found = value.pathLenConstraint;
|
|
1485
|
+
});
|
|
1486
|
+
return found;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
/** @internal The stricter of two path lengths an anchor states, which is the only direction
|
|
1490
|
+
* RFC 5937 sec. 3.2 allows an anchor to move a validation. */
|
|
1491
|
+
function lowerPathLen(a, b) {
|
|
1492
|
+
if (!intrinsic.isInteger(a)) return intrinsic.isInteger(b) ? b : null;
|
|
1493
|
+
if (!intrinsic.isInteger(b)) return a;
|
|
1494
|
+
return a < b ? a : b;
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1459
1497
|
function decodeExtensionList(list, opts) {
|
|
1460
1498
|
var out = [];
|
|
1461
1499
|
for (var i = 0; i < list.length; i++) _append(out, decodeOneExtension(list[i], i, opts));
|
|
@@ -2409,6 +2447,8 @@ module.exports = {
|
|
|
2409
2447
|
scopedCertDecoder: scopedCertDecoder,
|
|
2410
2448
|
crlExtensionDecoders: crlExtensionDecoders,
|
|
2411
2449
|
decodeExtensionList: decodeExtensionList,
|
|
2450
|
+
anchorPathLenFromExtensions: anchorPathLenFromExtensions,
|
|
2451
|
+
lowerPathLen: lowerPathLen,
|
|
2412
2452
|
extensionDoor: extensionDoor,
|
|
2413
2453
|
_decodeHelpers: _decodeHelpers,
|
|
2414
2454
|
attribute: attribute,
|