@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.
@@ -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 = Object.assign(Object.create(null), p);
283
- if (_copyableRecord(p.auth)) out.auth = Object.assign(Object.create(null), p.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 = Object.assign(Object.create(null), p.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 = Object.assign(Object.create(null), request.headers || {});
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 = Object.create(null);
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,
@@ -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), k;
203
- for (k in record) {
204
- if (intrinsic.hasOwn(record, k) && record[k] !== undefined) out[k] = record[k];
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
- return Object.assign({}, key, { mldsa: _snapshotSignerKey(key.mldsa, owned), trad: _snapshotSignerKey(key.trad, owned) });
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 = Object.create(null), k;
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
 
@@ -804,16 +804,69 @@ function _anchorSubtreeSeeds(anchor) {
804
804
  };
805
805
  }
806
806
 
807
- function initialize(certs, params, seeds, anchor) {
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
- maxPathLength: n,
833
- userInitialPolicySet: params.userInitialPolicySet || [OID.anyPolicy],
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
- return { name: cert.subject, publicKey: spki.bytes, algorithm: spki.algorithm.oid, parameters: spki.algorithm.parameters, subjectDer: cert.subject.bytes };
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 = intrinsic.create(null);
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 = Object.keys(rdnSpec);
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
  }
@@ -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 = Object.keys(h);
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) { var o = Object.create(null); for (var k in table) if (Object.prototype.hasOwnProperty.call(table, k)) o[table[k]] = k; return o; }
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, k;
625
- for (k in headers) {
626
- if (!Object.prototype.hasOwnProperty.call(headers, k)) continue;
627
- if (_foldLower(String(k)) === name) { found = headers[k]; seen += 1; }
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, Object.assign({}, headers), opts, transport, budgets);
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 ? Object.assign({}, opts.tls) : opts.tls,
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 ? Object.assign({}, opts.tls) : opts.tls, maxRedirects: opts.maxRedirects, allowCrossOriginRedirect: opts.allowCrossOriginRedirect, timeout: opts.timeout, maxResponseBytes: opts.maxResponseBytes, proxy: _snapshotProxy(opts.proxy) };
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) { var o = {}, k; for (k in _TRANSPORT_KEYS) o[k] = 1; for (k in extra) o[k] = 1; return o; }
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 },
@@ -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
- // allow:registry-table-inherits-object-prototype -- the record a schema's own check and build callbacks read as m.fields, whose keys are the field names that schema declared rather than anything the input supplies
343
- var fields = {};
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];
@@ -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,