@blamejs/pki 0.8.50 → 0.8.52
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 +37 -0
- package/README.md +4 -4
- package/index.js +2 -1
- package/lib/acme.js +33 -13
- package/lib/cmp-build.js +9 -3
- package/lib/cms-sign.js +254 -0
- package/lib/cms-verify.js +456 -8
- package/lib/constants.js +18 -2
- package/lib/est.js +31 -14
- package/lib/guard-intrinsic.js +4 -0
- package/lib/guard-time.js +98 -0
- package/lib/http-digest.js +7 -2
- package/lib/inspect.js +3 -2
- package/lib/lint.js +105 -2
- package/lib/path-validate.js +89 -8
- package/lib/pki-build.js +40 -0
- package/lib/revocation-fetch.js +11 -5
- package/lib/scep.js +26 -12
- package/lib/schema-all.js +1 -1
- package/lib/schema-smime.js +215 -5
- package/lib/tsp-sign.js +58 -95
- package/lib/x509-sign.js +22 -2
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/est.js
CHANGED
|
@@ -85,6 +85,15 @@ var _Promise = intrinsic.Promise;
|
|
|
85
85
|
* is handed is taken with it, since a replacement answers with a host of its own. */
|
|
86
86
|
var _URL = intrinsic.URL;
|
|
87
87
|
var _stringOf = intrinsic.String;
|
|
88
|
+
/** @internal And the accessors its result is read through: capturing the constructor does not capture
|
|
89
|
+
* the prototype the value reads its parts off, and each part here decides something, the scheme
|
|
90
|
+
* whether the request is allowed, the origin whether credentials may still be sent after a redirect. */
|
|
91
|
+
var _urlHref = intrinsic.urlHref;
|
|
92
|
+
var _urlOrigin = intrinsic.urlOrigin;
|
|
93
|
+
var _urlProtocol = intrinsic.urlProtocol;
|
|
94
|
+
var _urlPathname = intrinsic.urlPathname;
|
|
95
|
+
var _urlSearch = intrinsic.urlSearch;
|
|
96
|
+
var _urlHash = intrinsic.urlHash;
|
|
88
97
|
var _promiseResolve = intrinsic.uncurry(intrinsic.promiseResolve);
|
|
89
98
|
var _promiseReject = intrinsic.uncurry(intrinsic.promiseReject);
|
|
90
99
|
var pkix = require("./schema-pkix");
|
|
@@ -810,7 +819,8 @@ function _parseUrl(urlStr) {
|
|
|
810
819
|
var url;
|
|
811
820
|
try { url = new _URL(_stringOf(urlStr)); }
|
|
812
821
|
catch (e) { throw E("est/bad-url", "the EST server URL did not parse: " + String(urlStr), e); }
|
|
813
|
-
|
|
822
|
+
var scheme = _urlProtocol(url);
|
|
823
|
+
if (scheme !== "https:") throw E("est/insecure-url", "EST requires https (RFC 7030 sec. 3.3), got " + scheme + " for " + urlStr);
|
|
814
824
|
return url;
|
|
815
825
|
}
|
|
816
826
|
|
|
@@ -823,11 +833,12 @@ function _tlsForRequest(opts) {
|
|
|
823
833
|
function _redirectTarget(current, location, method, opts) {
|
|
824
834
|
if (location == null || String(location).trim() === "") throw E("est/http-error", "a redirect response carried no Location header");
|
|
825
835
|
var resolved;
|
|
826
|
-
try { resolved = new _URL(_stringOf(location), current
|
|
836
|
+
try { resolved = new _URL(_stringOf(location), _urlHref(current)); }
|
|
827
837
|
catch (e) { throw E("est/bad-url", "a redirect Location did not parse: " + location, e); }
|
|
828
|
-
|
|
838
|
+
var resolvedScheme = _urlProtocol(resolved);
|
|
839
|
+
if (resolvedScheme !== "https:") throw E("est/insecure-redirect", "a redirect to a non-https URL is refused (RFC 7030 sec. 3.2.1): " + resolvedScheme);
|
|
829
840
|
var safeMethod = method === "GET" || method === "HEAD";
|
|
830
|
-
if (resolved
|
|
841
|
+
if (_urlOrigin(resolved) !== _urlOrigin(current) && !safeMethod && !opts.allowCrossOriginRedirect) {
|
|
831
842
|
throw E("est/cross-origin-redirect", "a cross-origin redirect on a " + method + " needs opts.allowCrossOriginRedirect (RFC 7030 sec. 3.2.1)");
|
|
832
843
|
}
|
|
833
844
|
return resolved;
|
|
@@ -890,10 +901,10 @@ function _drive(method, url, body, headers, opts, transport, budgets) {
|
|
|
890
901
|
var staleRetries = 0;
|
|
891
902
|
var authSpaces = 0;
|
|
892
903
|
var lastDigestNonce = null;
|
|
893
|
-
var initialOrigin = url
|
|
904
|
+
var initialOrigin = _urlOrigin(url);
|
|
894
905
|
function _tlsFor(u) {
|
|
895
906
|
var t = budgets.tls;
|
|
896
|
-
if (t && u
|
|
907
|
+
if (t && _urlOrigin(u) !== initialOrigin) {
|
|
897
908
|
t = Object.assign({}, t);
|
|
898
909
|
t.cert = null; t.key = null; t.servername = null;
|
|
899
910
|
}
|
|
@@ -905,20 +916,26 @@ function _drive(method, url, body, headers, opts, transport, budgets) {
|
|
|
905
916
|
var digestSent = false;
|
|
906
917
|
function _headersFor(u) {
|
|
907
918
|
digestSent = false;
|
|
908
|
-
|
|
919
|
+
/** @internal The origin and the request target are each read ONCE and both the protection-space
|
|
920
|
+
* test and the answer are built from those locals: the credential is bound to the target it is
|
|
921
|
+
* computed over, so a part answering two reads differently would answer for one target and be
|
|
922
|
+
* sent to another. */
|
|
923
|
+
var uOrigin = _urlOrigin(u);
|
|
924
|
+
var uTarget = _urlPathname(u) + _urlSearch(u);
|
|
925
|
+
if (uOrigin !== initialOrigin || (!authValue && !digestChallenge)) return headers;
|
|
909
926
|
var hh = guard.identifier.ownOptions(headers);
|
|
910
927
|
if (digestChallenge) {
|
|
911
|
-
if (!httpDigest.inProtectionSpace(digestChallenge,
|
|
928
|
+
if (!httpDigest.inProtectionSpace(digestChallenge, uOrigin, uTarget)) return headers;
|
|
912
929
|
var nkey = digestChallenge.nonce;
|
|
913
930
|
digestNcByNonce[nkey] = (digestNcByNonce[nkey] || 0) + 1;
|
|
914
|
-
hh.authorization = httpDigest.answer(digestChallenge, { method: method, uri:
|
|
931
|
+
hh.authorization = httpDigest.answer(digestChallenge, { method: method, uri: uTarget, username: _authUser(opts), password: _authPass(opts), body: body, nc: digestNcByNonce[nkey], policy: _digestPolicy(opts) }, E);
|
|
915
932
|
digestSent = true;
|
|
916
933
|
} else { hh.authorization = authValue; }
|
|
917
934
|
return hh;
|
|
918
935
|
}
|
|
919
936
|
function step() {
|
|
920
937
|
var hopBody = (typeof body === "function" && budgets.onAttempt) ? budgets.onAttempt() : body;
|
|
921
|
-
return guard.async.invoked(transport, [{ method: method, url: url
|
|
938
|
+
return guard.async.invoked(transport, [{ method: method, url: _urlHref(url), headers: _headersFor(url), body: hopBody, tls: _tlsFor(url), proxy: budgets.proxy, timeout: budgets.timeout, maxResponseBytes: budgets.maxResponseBytes }], E, "est/bad-input", "opts.transport").then(function (res) {
|
|
922
939
|
res = res || {};
|
|
923
940
|
var blen = _bodyLen(res);
|
|
924
941
|
if (blen > budgets.maxResponseBytes) throw E("est/response-too-large", "the response body (" + blen + " bytes) exceeds the " + budgets.maxResponseBytes + "-byte cap (RFC 7030 sec. 6)");
|
|
@@ -939,7 +956,7 @@ function _drive(method, url, body, headers, opts, transport, budgets) {
|
|
|
939
956
|
return step();
|
|
940
957
|
}
|
|
941
958
|
if (status === 401) {
|
|
942
|
-
if (url
|
|
959
|
+
if (_urlOrigin(url) !== initialOrigin) throw E("est/auth-required", "refusing to send HTTP credentials to a redirected origin (RFC 7030 sec. 3.6)");
|
|
943
960
|
var www = String(h["www-authenticate"] || "");
|
|
944
961
|
if (www.length > constants.LIMITS.HTTP_AUTH_HEADER_MAX_BYTES) throw E("est/auth-required", "the WWW-Authenticate header exceeds the " + constants.LIMITS.HTTP_AUTH_HEADER_MAX_BYTES + "-byte cap (RFC 7030 sec. 3.2.3)");
|
|
945
962
|
var scheme = _authScheme(opts);
|
|
@@ -951,8 +968,8 @@ function _drive(method, url, body, headers, opts, transport, budgets) {
|
|
|
951
968
|
selPol.preferStale = digestSent;
|
|
952
969
|
selPol.priorNonce = lastDigestNonce;
|
|
953
970
|
selPol.priorRealm = digestChallenge && digestChallenge.realm;
|
|
954
|
-
selPol.requestOrigin = url
|
|
955
|
-
selPol.requestTarget = url
|
|
971
|
+
selPol.requestOrigin = _urlOrigin(url);
|
|
972
|
+
selPol.requestTarget = _urlPathname(url) + _urlSearch(url);
|
|
956
973
|
var ch = httpDigest.parseChallenge(www, E, "est/digest-bad-challenge", selPol);
|
|
957
974
|
if (!ch) throw E("est/auth-required", "the server offered no usable Digest challenge: " + www);
|
|
958
975
|
if (digestSent && digestChallenge && ch.realm === digestChallenge.realm) {
|
|
@@ -988,7 +1005,7 @@ function _client(op, method, baseUrl, body, headers, opts, proxySnap, onAttempt,
|
|
|
988
1005
|
var base;
|
|
989
1006
|
try { base = new _URL(_stringOf(baseUrl)); }
|
|
990
1007
|
catch (e) { throw E("est/bad-url", "the EST base URL did not parse: " + guard.text.showValue(baseUrl), e); }
|
|
991
|
-
if (base
|
|
1008
|
+
if (_urlSearch(base) || _urlHash(base)) throw E("est/bad-url", "the EST base URL must not carry a query or fragment component (RFC 7030 sec. 3.2.2)");
|
|
992
1009
|
var url = _parseUrl(paths(baseUrl, { label: opts.label })[op]);
|
|
993
1010
|
var transport = checked !== undefined ? checked : _assertTransport(opts);
|
|
994
1011
|
if (!transport) {
|
package/lib/guard-intrinsic.js
CHANGED
|
@@ -85,6 +85,7 @@ var _urlOrigin = getter(URL.prototype, "origin", "URL.prototype");
|
|
|
85
85
|
var _urlProtocol = getter(URL.prototype, "protocol", "URL.prototype");
|
|
86
86
|
var _urlPathname = getter(URL.prototype, "pathname", "URL.prototype");
|
|
87
87
|
var _urlSearch = getter(URL.prototype, "search", "URL.prototype");
|
|
88
|
+
var _urlHash = getter(URL.prototype, "hash", "URL.prototype");
|
|
88
89
|
var _urlHostname = getter(URL.prototype, "hostname", "URL.prototype");
|
|
89
90
|
var _urlPort = getter(URL.prototype, "port", "URL.prototype");
|
|
90
91
|
var _urlUsername = getter(URL.prototype, "username", "URL.prototype");
|
|
@@ -92,6 +93,7 @@ var _urlPassword = getter(URL.prototype, "password", "URL.prototype");
|
|
|
92
93
|
var _urlSearchParams = getter(URL.prototype, "searchParams", "URL.prototype");
|
|
93
94
|
var _urlSetPathname = setter(URL.prototype, "pathname", "URL.prototype");
|
|
94
95
|
var _urlSetSearch = setter(URL.prototype, "search", "URL.prototype");
|
|
96
|
+
var _urlSetHash = setter(URL.prototype, "hash", "URL.prototype");
|
|
95
97
|
var _asyncIterator = Symbol.asyncIterator;
|
|
96
98
|
|
|
97
99
|
var _arrForEach = uncurry(Array.prototype.forEach);
|
|
@@ -275,6 +277,7 @@ module.exports = Object.freeze({
|
|
|
275
277
|
urlProtocol: _urlProtocol,
|
|
276
278
|
urlPathname: _urlPathname,
|
|
277
279
|
urlSearch: _urlSearch,
|
|
280
|
+
urlHash: _urlHash,
|
|
278
281
|
urlHostname: _urlHostname,
|
|
279
282
|
urlPort: _urlPort,
|
|
280
283
|
urlUsername: _urlUsername,
|
|
@@ -282,6 +285,7 @@ module.exports = Object.freeze({
|
|
|
282
285
|
urlSearchParams: _urlSearchParams,
|
|
283
286
|
urlSetPathname: _urlSetPathname,
|
|
284
287
|
urlSetSearch: _urlSetSearch,
|
|
288
|
+
urlSetHash: _urlSetHash,
|
|
285
289
|
asyncIterator: _asyncIterator,
|
|
286
290
|
forEach: _arrForEach,
|
|
287
291
|
map: _arrMap,
|
package/lib/guard-time.js
CHANGED
|
@@ -19,17 +19,112 @@ var _dateGetUTCMinutes = require("./guard-intrinsic").uncurry(Date.prototype.get
|
|
|
19
19
|
var _dateGetUTCSeconds = require("./guard-intrinsic").uncurry(Date.prototype.getUTCSeconds);
|
|
20
20
|
var _dateGetUTCMilliseconds = require("./guard-intrinsic").uncurry(Date.prototype.getUTCMilliseconds);
|
|
21
21
|
var _dateSetUTCFullYear = require("./guard-intrinsic").uncurry(Date.prototype.setUTCFullYear);
|
|
22
|
+
var _dateToISOString = require("./guard-intrinsic").uncurry(Date.prototype.toISOString);
|
|
23
|
+
var _charCodeAt = require("./guard-intrinsic").uncurry(String.prototype.charCodeAt);
|
|
24
|
+
var _Number = require("./guard-intrinsic").Number;
|
|
25
|
+
var constants = require("./constants");
|
|
26
|
+
/** @internal RFC 3161 clause 2.4.2 states an Accuracy's third field in microseconds, which no
|
|
27
|
+
* `C.TIME` scale names because no PKI structure carries a microsecond duration anywhere else. */
|
|
28
|
+
var MICROS_PER_MILLISECOND = 1000;
|
|
22
29
|
var _dateUTC = Date.UTC;
|
|
23
30
|
var _floor = Math.floor;
|
|
24
31
|
var _ceil = Math.ceil;
|
|
25
32
|
/** @internal A DER GeneralizedTime carries a four-digit year and a UTCTime a narrower one, so a Date
|
|
26
33
|
* outside 0000..9999 has no PKI encoding. */
|
|
27
34
|
var DER_YEAR_MIN = 0, DER_YEAR_MAX = 9999;
|
|
35
|
+
/** @internal The widest accuracy a date comparison can use, the DER year range in seconds, and the
|
|
36
|
+
* instant a wider one saturates to: the last instant a DER time can carry, which is past every
|
|
37
|
+
* certificate date and, unlike an arbitrary ceiling, is a `Date` a verdict can render. Declared
|
|
38
|
+
* after the range it derives from, since a `var` read above its own initializer is `undefined`. */
|
|
39
|
+
var MAX_ACCURACY_SECONDS = constants.TIME.days((DER_YEAR_MAX + 1) * 366) / constants.TIME.seconds(1);
|
|
40
|
+
var MAX_INSTANT = _dateUTC(DER_YEAR_MAX, 11, 31, 23, 59, 59, 999);
|
|
28
41
|
|
|
29
42
|
// @enforced-by guard-shape-reinlined
|
|
30
43
|
// @guard-shape \.getTime\s*\(
|
|
31
44
|
function instantOf(value) { return _dateGetTime(value); }
|
|
32
45
|
|
|
46
|
+
/** The instant an error message or a verdict names, rendered through the operation captured at load.
|
|
47
|
+
* A replaced `toISOString` chooses the time a refusal reports, which is what an operator reads when
|
|
48
|
+
* deciding whether the refusal was right, so the rendering is taken from the same place every other
|
|
49
|
+
* read of a Date is.
|
|
50
|
+
*
|
|
51
|
+
* @enforced-by behavioral -- the live-read budget each module declares is what holds a call-time
|
|
52
|
+
* `toISOString` out of a converted file; a `@guard-shape` here would name the read in every module
|
|
53
|
+
* that renders a date and the budget already answers for those.
|
|
54
|
+
*/
|
|
55
|
+
function isoOf(value) { return _dateToISOString(value); }
|
|
56
|
+
|
|
57
|
+
/** The instant a DER GeneralizedTime names, rounded UP to the next whole millisecond where its
|
|
58
|
+
* fractional seconds carry a digit a `Date` cannot hold. `fraction` is the fraction's digit string;
|
|
59
|
+
* the first three digits ARE the milliseconds, so a nonzero digit after them puts the real instant
|
|
60
|
+
* strictly between `value` and `value + 1ms`.
|
|
61
|
+
*
|
|
62
|
+
* An upper bound is decided against this rather than against the Date, because the Date truncates:
|
|
63
|
+
* a time stamped at `...000000.000001Z` reads as `...000000.000Z`, which compares equal to a bound
|
|
64
|
+
* of `...000000Z` and so passes an "after the bound" test it is on the wrong side of. The two
|
|
65
|
+
* consumers are the timestamp verifier's own check of the authority's certificate window and the
|
|
66
|
+
* signature-timestamp row's check of the signing certificate's expiry.
|
|
67
|
+
*
|
|
68
|
+
* @enforced-by behavioral -- a truncating comparison has no rename-proof shape; the RED vector is a
|
|
69
|
+
* token minted with a sub-millisecond fraction one tick past a certificate's notAfter.
|
|
70
|
+
*/
|
|
71
|
+
function ceilInstantOf(value, fraction) { return latestInstantOf(value, fraction, null); }
|
|
72
|
+
|
|
73
|
+
/** @internal The part of a DER GeneralizedTime's fractional seconds a `Date` cannot hold, in whole
|
|
74
|
+
* microseconds. The first three digits ARE the milliseconds; the next three are microseconds, and
|
|
75
|
+
* anything finer than a microsecond counts as one, since a bound is only a bound if it is not
|
|
76
|
+
* exceeded. Read digit by digit through the captured accessor: a fraction is caller bytes. */
|
|
77
|
+
function _fractionMicros(fraction) {
|
|
78
|
+
if (typeof fraction !== "string" || fraction.length <= 3) return 0;
|
|
79
|
+
var micros = 0;
|
|
80
|
+
var finer = false;
|
|
81
|
+
for (var i = 3; i < fraction.length; i++) {
|
|
82
|
+
var c = _charCodeAt(fraction, i);
|
|
83
|
+
if (c < 48 || c > 57) break;
|
|
84
|
+
var digit = c - 48;
|
|
85
|
+
if (i === 3) micros += digit * 100;
|
|
86
|
+
else if (i === 4) micros += digit * 10;
|
|
87
|
+
else if (i === 5) micros += digit;
|
|
88
|
+
else if (digit > 0) finer = true;
|
|
89
|
+
}
|
|
90
|
+
return finer ? micros + 1 : micros;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** The LATEST instant a timestamp could have been created at: its `genTime` ceiling plus the
|
|
94
|
+
* accuracy the token states. RFC 3161 clause 2.4.2 defines accuracy as the bound on the difference
|
|
95
|
+
* between the stated time and the real one, so a token saying it is accurate to two seconds places
|
|
96
|
+
* its creation anywhere in a four-second window, and an upper bound that reads only `genTime`
|
|
97
|
+
* concludes something the token does not establish. `accuracy` is the parsed `{ seconds, millis,
|
|
98
|
+
* micros }` or null; `seconds` arrives as a BigInt, the other two as numbers, and a micros
|
|
99
|
+
* remainder rounds up because a bound is only a bound if it is not exceeded.
|
|
100
|
+
*
|
|
101
|
+
* @enforced-by behavioral -- a bound that ignores a field has no rename-proof shape; the RED vector
|
|
102
|
+
* is a token whose genTime precedes a certificate's notAfter while its accuracy window does not.
|
|
103
|
+
*/
|
|
104
|
+
function latestInstantOf(value, fraction, accuracy) {
|
|
105
|
+
var at = instantOf(value);
|
|
106
|
+
var ms = 0;
|
|
107
|
+
/** @internal The sub-millisecond parts are summed and rounded ONCE. Rounding the genTime fraction
|
|
108
|
+
* and the accuracy up separately overstates the bound by a millisecond when both carry one, which
|
|
109
|
+
* refuses a token whose real window ends before the instant it is compared against. */
|
|
110
|
+
var micros = _fractionMicros(fraction);
|
|
111
|
+
if (accuracy !== null && typeof accuracy === "object") {
|
|
112
|
+
/** @internal An Accuracy's seconds field is an arbitrary-size INTEGER, so it is bounded BEFORE
|
|
113
|
+
* it is scaled: a token claiming more than the DER time range can hold is saturated to the
|
|
114
|
+
* widest instant rather than scaled into a value the scale helper refuses. The refusal would
|
|
115
|
+
* escape the row this builds and take the whole verify call down, where what the token has
|
|
116
|
+
* established is nothing about which side of any certificate date it falls on, which is the
|
|
117
|
+
* answer a saturated bound gives. */
|
|
118
|
+
if (accuracy.seconds > MAX_ACCURACY_SECONDS) return MAX_INSTANT;
|
|
119
|
+
if (typeof accuracy.seconds === "bigint") ms += constants.TIME.seconds(_Number(accuracy.seconds));
|
|
120
|
+
else if (typeof accuracy.seconds === "number") ms += constants.TIME.seconds(accuracy.seconds);
|
|
121
|
+
if (typeof accuracy.millis === "number") ms += constants.TIME.milliseconds(accuracy.millis);
|
|
122
|
+
if (typeof accuracy.micros === "number") micros += accuracy.micros;
|
|
123
|
+
}
|
|
124
|
+
if (micros > 0) ms += _ceil(micros / MICROS_PER_MILLISECOND);
|
|
125
|
+
return ms > 0 ? at + ms : at;
|
|
126
|
+
}
|
|
127
|
+
|
|
33
128
|
// @enforced-by nan-date-comparison-unguarded
|
|
34
129
|
function assertValid(value, E, code, label) {
|
|
35
130
|
if (!util.types.isDate(value) || _isNaN(instantOf(value))) {
|
|
@@ -120,6 +215,9 @@ module.exports = _freeze({
|
|
|
120
215
|
assertEncodable: assertEncodable,
|
|
121
216
|
addMonths: addMonths,
|
|
122
217
|
instantOf: instantOf,
|
|
218
|
+
isoOf: isoOf,
|
|
219
|
+
ceilInstantOf: ceilInstantOf,
|
|
220
|
+
latestInstantOf: latestInstantOf,
|
|
123
221
|
isDate: isDate,
|
|
124
222
|
toDate: toDate,
|
|
125
223
|
within: within,
|
package/lib/http-digest.js
CHANGED
|
@@ -7,8 +7,13 @@ var crypto = require("crypto");
|
|
|
7
7
|
var constants = require("./constants");
|
|
8
8
|
var intrinsic = require("./guard-intrinsic");
|
|
9
9
|
var _hasOwn = intrinsic.hasOwn;
|
|
10
|
-
/** @internal The URL parser, taken at load: the origin it reports is what a challenge is bound to.
|
|
10
|
+
/** @internal The URL parser, taken at load: the origin it reports is what a challenge is bound to.
|
|
11
|
+
* Its accessors are taken with it, since the constructor's capture does not cover the prototype the
|
|
12
|
+
* value reads its parts off and this origin is what a credential is scoped by. */
|
|
11
13
|
var _URL = intrinsic.URL;
|
|
14
|
+
var _urlOrigin = intrinsic.urlOrigin;
|
|
15
|
+
var _urlPathname = intrinsic.urlPathname;
|
|
16
|
+
var _urlSearch = intrinsic.urlSearch;
|
|
12
17
|
var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
13
18
|
var _strSlice = intrinsic.uncurry(String.prototype.slice);
|
|
14
19
|
var _fromCharCode = intrinsic.fromCharCode;
|
|
@@ -295,7 +300,7 @@ function answer(challenge, params, E) {
|
|
|
295
300
|
function _domainEntry(d) {
|
|
296
301
|
d = String(d == null ? "" : d);
|
|
297
302
|
if (d.charAt(0) === "/") return { origin: null, full: d };
|
|
298
|
-
try { var u = new _URL(d); return { origin: u.
|
|
303
|
+
try { var u = new _URL(d); return { origin: _urlOrigin(u).toLowerCase(), full: (_urlPathname(u) || "/") + (_urlSearch(u) || "") }; }
|
|
299
304
|
catch (_e) { return null; }
|
|
300
305
|
}
|
|
301
306
|
|
package/lib/inspect.js
CHANGED
|
@@ -449,8 +449,9 @@ var EXT_RENDERERS = Object.assign(Object.create(null), {
|
|
|
449
449
|
}
|
|
450
450
|
return akiLines.length ? akiLines.join("\n") : inner + "keyid:(none)";
|
|
451
451
|
},
|
|
452
|
-
/** @internal RFC 7633 sec.
|
|
453
|
-
* where the toolkit knows it and printed as its number where it does not.
|
|
452
|
+
/** @internal RFC 7633 sec. 4.1 binds these values to the IANA TLS Extensions registry, so each is
|
|
453
|
+
* named from the registry where the toolkit knows it and printed as its number where it does not.
|
|
454
|
+
* sec. 6 is IANA Considerations and registers the extension's own OID; it carries no value. */
|
|
454
455
|
tlsFeature: function (decoded, inner) {
|
|
455
456
|
return (decoded.features || []).map(function (f) {
|
|
456
457
|
var text = String(f);
|
package/lib/lint.js
CHANGED
|
@@ -303,9 +303,16 @@ var OID_CPS = oid.byName("cps");
|
|
|
303
303
|
/** @internal The extensions RFC 5280 says a conforming CA SHOULD mark non-critical, keyed by OID
|
|
304
304
|
* with the clause; the MUSTs live in the shared CERT_FIXED_CRITICALITY table. The subjectAltName
|
|
305
305
|
* and extKeyUsage recommendations are conditional and have their own rules. */
|
|
306
|
+
/** @internal The per-extension citation travels with the finding, so a row here may come from a
|
|
307
|
+
* document other than RFC 5280: RFC 7633 sec. 4 states the same shape of recommendation for the TLS
|
|
308
|
+
* feature extension, and its clause closes by naming the case where critical is what the caller
|
|
309
|
+
* wants, so this grades it rather than refusing it. Adding a row here grades the extension in the
|
|
310
|
+
* DEFAULT set, which a named profile would not; it does not feed `_isUnknownExtension`, so the
|
|
311
|
+
* unknown-critical verdict a critical one draws is unaffected. */
|
|
306
312
|
var CERT_RECOMMENDED_NON_CRITICAL = Object.create(null);
|
|
307
313
|
CERT_RECOMMENDED_NON_CRITICAL[oid.byName("issuerAltName")] = "RFC 5280 sec. 4.2.1.7";
|
|
308
314
|
CERT_RECOMMENDED_NON_CRITICAL[oid.byName("cRLDistributionPoints")] = "RFC 5280 sec. 4.2.1.13";
|
|
315
|
+
CERT_RECOMMENDED_NON_CRITICAL[oid.byName("tlsFeature")] = "RFC 7633 sec. 4";
|
|
309
316
|
var _T_BMP = asn1.TAGS.BMP_STRING, _T_VISIBLE = asn1.TAGS.VISIBLE_STRING, _T_UTF8 = asn1.TAGS.UTF8_STRING;
|
|
310
317
|
function _hasControlChar(str) {
|
|
311
318
|
for (var i = 0; i < str.length; i++) {
|
|
@@ -488,7 +495,7 @@ var RFC5280_RULES = [
|
|
|
488
495
|
_criticalityRule("keyUsage", "lint/rfc5280/key-usage-not-critical", "warn",
|
|
489
496
|
"RFC 5280 4.2.1.3", "the keyUsage extension should be marked critical"),
|
|
490
497
|
{
|
|
491
|
-
id: "lint/rfc5280/recommended-criticality", severity: "warn", source: "rfc5280", citation: "RFC 5280 4.2.1.7 / 4.2.1.13",
|
|
498
|
+
id: "lint/rfc5280/recommended-criticality", severity: "warn", source: "rfc5280", citation: "RFC 5280 4.2.1.7 / 4.2.1.13, RFC 7633 sec. 4",
|
|
492
499
|
message: "an extension a conforming CA should mark non-critical is marked critical",
|
|
493
500
|
check: function (cert) {
|
|
494
501
|
var rows = [];
|
|
@@ -4056,6 +4063,96 @@ var RFC9763_RULES = [
|
|
|
4056
4063
|
},
|
|
4057
4064
|
];
|
|
4058
4065
|
|
|
4066
|
+
/** @internal RFC 7633. The criticality recommendation lives in the always-on table above, because
|
|
4067
|
+
* sec. 4 states it for every certificate carrying the extension. These rows read the VALUE, which
|
|
4068
|
+
* RFC 7633 leaves open in three ways and another document speaks to in a fourth:
|
|
4069
|
+
*
|
|
4070
|
+
* sec. 4 writes `Features ::= SEQUENCE OF INTEGER` with no range, no ordering rule and no prohibition
|
|
4071
|
+
* on repeats, so none of those is a conformance failure and none is refused by the encoder. What
|
|
4072
|
+
* sec. 4.1 does say is that the values are "TLS extension identifiers ... as specified in the IANA
|
|
4073
|
+
* Transport Layer Security (TLS) Extensions registry", and that registry is 16 bits wide, so a value
|
|
4074
|
+
* outside 0..65535 names nothing in it. That is a `warn`, not an `error`: the finding rests on a
|
|
4075
|
+
* reference to a registry rather than on a syntax rule, and at `error` in a named profile it would
|
|
4076
|
+
* make `pki.x509.sign` refuse to emit the certificate.
|
|
4077
|
+
*
|
|
4078
|
+
* The transparency_info row is RFC 9162 sec. 7.2, read directly: "A certification authority SHOULD
|
|
4079
|
+
* NOT issue any certificate that identifies the transparency_info TLS extension in a TLS feature
|
|
4080
|
+
* extension [RFC7633], because TLS servers are not required to support the transparency_info TLS
|
|
4081
|
+
* extension in order to participate in CT". That is the only clause outside RFC 7633 that constrains
|
|
4082
|
+
* this field.
|
|
4083
|
+
*
|
|
4084
|
+
* There is NO deprecation row for status_request_v2, and the absence is deliberate. The IANA registry
|
|
4085
|
+
* still lists `17 status_request_v2 [RFC 6961]` as Recommended and marks it neither obsolete nor
|
|
4086
|
+
* deprecated; RFC 9846 contains no occurrence of "status_request_v2" and none of "7633", so no
|
|
4087
|
+
* document states a deprecation to cite. What is true is narrower: RFC 9846 obsoletes RFC 6961, which
|
|
4088
|
+
* defined the extension, and the ExtensionType enum at its sec. 4.3 does not list 17 at all, so a
|
|
4089
|
+
* certificate naming feature 17 asks a TLS 1.3 server for something that version does not define
|
|
4090
|
+
* while a TLS 1.2 deployment can still satisfy it. That is reported at `notice`, with the clause that
|
|
4091
|
+
* supports it and nothing more. */
|
|
4092
|
+
var RFC7633_RULES = [
|
|
4093
|
+
{
|
|
4094
|
+
id: "lint/rfc7633/feature-outside-registry-width", severity: "warn", source: "rfc7633", citation: "RFC 7633 4.1",
|
|
4095
|
+
message: "a TLS feature value lies outside the 16-bit width of the IANA TLS ExtensionType registry sec. 4.1 points at, so it names no extension in it",
|
|
4096
|
+
appliesTo: function (cert, ctx) { return !!ctx.raw("tlsFeature"); },
|
|
4097
|
+
check: function (cert, ctx) {
|
|
4098
|
+
var d = ctx.decode("tlsFeature");
|
|
4099
|
+
var features = (d && d.value && d.value.features) || [];
|
|
4100
|
+
var rows = [];
|
|
4101
|
+
for (var i = 0; i < features.length; i++) {
|
|
4102
|
+
var f = features[i];
|
|
4103
|
+
if (f < 0n || f > 65535n) rows.push({ context: { feature: String(f), width: "0..65535" } });
|
|
4104
|
+
}
|
|
4105
|
+
return rows.length ? rows : null;
|
|
4106
|
+
},
|
|
4107
|
+
},
|
|
4108
|
+
{
|
|
4109
|
+
id: "lint/rfc7633/feature-repeated", severity: "notice", source: "rfc7633", citation: "RFC 7633 4",
|
|
4110
|
+
message: "a TLS feature value appears more than once; the syntax permits it and the set it names is the same, so the repeat carries no meaning",
|
|
4111
|
+
appliesTo: function (cert, ctx) { return !!ctx.raw("tlsFeature"); },
|
|
4112
|
+
check: function (cert, ctx) {
|
|
4113
|
+
var d = ctx.decode("tlsFeature");
|
|
4114
|
+
var features = (d && d.value && d.value.features) || [];
|
|
4115
|
+
/** @internal One pass with a set of what has been seen, not a rescan of every preceding value.
|
|
4116
|
+
* This rule is in the default set and a certificate reaching it is unauthenticated, so the
|
|
4117
|
+
* pairwise form is work the input chooses: MEASURED, 20000 distinct features in an 80 KiB
|
|
4118
|
+
* certificate took 355 ms against 5 ms for 50 of them. A set compares the bigint values. */
|
|
4119
|
+
var seen = new Set();
|
|
4120
|
+
var rows = [];
|
|
4121
|
+
for (var i = 0; i < features.length; i++) {
|
|
4122
|
+
if (seen.has(features[i])) rows.push({ context: { feature: String(features[i]) } });
|
|
4123
|
+
else seen.add(features[i]);
|
|
4124
|
+
}
|
|
4125
|
+
return rows.length ? rows : null;
|
|
4126
|
+
},
|
|
4127
|
+
},
|
|
4128
|
+
{
|
|
4129
|
+
id: "lint/rfc7633/transparency-info-feature", severity: "warn", source: "rfc7633", citation: "RFC 9162 7.2",
|
|
4130
|
+
message: "a certification authority should not issue a certificate naming the transparency_info TLS extension (52) in a TLS feature extension, because a TLS server need not support it to participate in certificate transparency",
|
|
4131
|
+
appliesTo: function (cert, ctx) { return !!ctx.raw("tlsFeature"); },
|
|
4132
|
+
check: function (cert, ctx) {
|
|
4133
|
+
var d = ctx.decode("tlsFeature");
|
|
4134
|
+
var features = (d && d.value && d.value.features) || [];
|
|
4135
|
+
for (var i = 0; i < features.length; i++) {
|
|
4136
|
+
if (features[i] === 52n) return { context: { feature: "52", name: "transparency_info" } };
|
|
4137
|
+
}
|
|
4138
|
+
return null;
|
|
4139
|
+
},
|
|
4140
|
+
},
|
|
4141
|
+
{
|
|
4142
|
+
id: "lint/rfc7633/feature-undefined-in-tls13", severity: "notice", source: "rfc7633", citation: "RFC 9846 4.3",
|
|
4143
|
+
message: "a TLS feature value names status_request_v2 (17), which RFC 9846 does not list in its ExtensionType enum and whose defining document RFC 6961 it obsoletes, so a TLS 1.3 server cannot satisfy it",
|
|
4144
|
+
appliesTo: function (cert, ctx) { return !!ctx.raw("tlsFeature"); },
|
|
4145
|
+
check: function (cert, ctx) {
|
|
4146
|
+
var d = ctx.decode("tlsFeature");
|
|
4147
|
+
var features = (d && d.value && d.value.features) || [];
|
|
4148
|
+
for (var i = 0; i < features.length; i++) {
|
|
4149
|
+
if (features[i] === 17n) return { context: { feature: "17", name: "status_request_v2", obsoletedBy: "RFC 9846" } };
|
|
4150
|
+
}
|
|
4151
|
+
return null;
|
|
4152
|
+
},
|
|
4153
|
+
},
|
|
4154
|
+
];
|
|
4155
|
+
|
|
4059
4156
|
/** @internal RFC 5280 sec. 5 states the criticality of each CRL extension it profiles, and sec. 5.3
|
|
4060
4157
|
* does the same for the entry extensions. certificateIssuer is critical (sec. 5.3.3) because a
|
|
4061
4158
|
* consumer that ignores it attributes an entry to the wrong issuer. */
|
|
@@ -6413,6 +6510,7 @@ var PROFILES = Object.assign(Object.create(null), {
|
|
|
6413
6510
|
"rfc9909": RFC9909_RULES.concat(RFC5280_EXTENSION_SYNTAX_RULES),
|
|
6414
6511
|
"rfc9935": RFC9935_RULES.concat(RFC5280_EXTENSION_SYNTAX_RULES),
|
|
6415
6512
|
"rfc9763": RFC9763_RULES.concat(RFC5280_EXTENSION_SYNTAX_RULES),
|
|
6513
|
+
"rfc7633": RFC7633_RULES.concat(RFC5280_EXTENSION_SYNTAX_RULES),
|
|
6416
6514
|
"x509-altsig": X509_ALTSIG_RULES.concat(RFC5280_EXTENSION_SYNTAX_RULES),
|
|
6417
6515
|
"cabf-tls": CABF_TLS_RULES.concat(RFC5280_EXTENSION_SYNTAX_RULES),
|
|
6418
6516
|
"cabf-smime": CABF_SMIME_RULES.concat(RFC5280_EXTENSION_SYNTAX_RULES),
|
|
@@ -6436,7 +6534,7 @@ var PROFILES = Object.assign(Object.create(null), {
|
|
|
6436
6534
|
.concat(RFC5280_EXTENSION_SYNTAX_RULES),
|
|
6437
6535
|
});
|
|
6438
6536
|
var ALL_RULES = RFC5280_RULES.concat(RFC9881_RULES).concat(RFC9909_RULES).concat(RFC9935_RULES)
|
|
6439
|
-
.concat(RFC9763_RULES).concat(X509_ALTSIG_RULES)
|
|
6537
|
+
.concat(RFC9763_RULES).concat(RFC7633_RULES).concat(X509_ALTSIG_RULES)
|
|
6440
6538
|
.concat(CABF_TLS_RULES).concat(CABF_SMIME_RULES).concat(CABF_CS_RULES).concat(ETSI_QC_RULES)
|
|
6441
6539
|
/** @internal The root-program rows are in the registry so `rules()` enumerates them, and stay
|
|
6442
6540
|
* inert under the default set because each one asks whether the caller named the profile. */
|
|
@@ -6725,6 +6823,11 @@ function _applyThreshold(report, severity) {
|
|
|
6725
6823
|
* id-kp-serverAuth and is not a CA. `"nist-pqc-transition"` and `"cnsa-2.0"` are
|
|
6726
6824
|
* named the same way: which algorithm policy a certificate is held to is the
|
|
6727
6825
|
* caller's assertion, so neither is detected and both are inert until named.
|
|
6826
|
+
* `"rfc7633"` adds the rows that read a TLS Feature extension's VALUES: a feature
|
|
6827
|
+
* outside the 16-bit width of the registry sec. 4.1 points at, a repeated feature,
|
|
6828
|
+
* the transparency_info feature RFC 9162 sec. 7.2 asks a CA not to issue, and the
|
|
6829
|
+
* status_request_v2 feature RFC 9846 leaves out of its ExtensionType enum. The
|
|
6830
|
+
* criticality of that extension is graded by default and needs no profile.
|
|
6728
6831
|
* @opts severity Suppress findings below this floor (default `"notice"`). `counts` and
|
|
6729
6832
|
* `worst` always reflect the complete, unfiltered result.
|
|
6730
6833
|
* @example
|
package/lib/path-validate.js
CHANGED
|
@@ -68,8 +68,14 @@ var _reject = intrinsic.uncurry(intrinsic.promiseReject);
|
|
|
68
68
|
* not a refusal, and this module's verdicts are what a caller acts on. */
|
|
69
69
|
var _Promise = intrinsic.Promise;
|
|
70
70
|
/** @internal The URL parser, taken at load: the host it reports from an access descriptor is the host a
|
|
71
|
-
* revocation fetch would be directed to.
|
|
71
|
+
* revocation fetch would be directed to. Its accessors are taken with it, because the constructor's
|
|
72
|
+
* capture does not cover the prototype the value reads its parts off, and the host read here is the
|
|
73
|
+
* one the address filter is applied to. */
|
|
72
74
|
var _URL = intrinsic.URL;
|
|
75
|
+
var _urlHref = intrinsic.urlHref;
|
|
76
|
+
var _urlProtocol = intrinsic.urlProtocol;
|
|
77
|
+
var _urlHostname = intrinsic.urlHostname;
|
|
78
|
+
var _urlSetHash = intrinsic.urlSetHash;
|
|
73
79
|
var constants = require("./constants");
|
|
74
80
|
var validator = require("./validator-all");
|
|
75
81
|
var cms = require("./schema-cms");
|
|
@@ -125,6 +131,7 @@ var OID = intrinsic.assign(intrinsic.create(null), {
|
|
|
125
131
|
anyExtendedKeyUsage: oid.byName("anyExtendedKeyUsage"),
|
|
126
132
|
cRLDistributionPoints: oid.byName("cRLDistributionPoints"),
|
|
127
133
|
subjectKeyIdentifier: oid.byName("subjectKeyIdentifier"),
|
|
134
|
+
tlsFeature: oid.byName("tlsFeature"),
|
|
128
135
|
authorityKeyIdentifier: oid.byName("authorityKeyIdentifier"),
|
|
129
136
|
authorityInfoAccess: oid.byName("authorityInfoAccess"),
|
|
130
137
|
caIssuers: oid.byName("caIssuers"),
|
|
@@ -300,6 +307,66 @@ function _isCaCert(cert) {
|
|
|
300
307
|
return !!(bc && bc.value && bc.value.cA === true);
|
|
301
308
|
}
|
|
302
309
|
|
|
310
|
+
/** @internal RFC 7633 sec. 4.2.2, the one requirement in that document decidable from certificate
|
|
311
|
+
* bytes: "a certificate that is signed by a Certificate Signing Certificate that contains a TLS
|
|
312
|
+
* feature extension MUST contain a TLS feature extension that offers the same set or a superset of
|
|
313
|
+
* the features advertised in the signing certificate." The clause's own gate is the issuer's key
|
|
314
|
+
* usage, "Certification Authority certificate with the key usage extension value set to
|
|
315
|
+
* keyCertSign", which is a different question from its basicConstraints cA bit, so this asks
|
|
316
|
+
* keyUsage. A subject carrying NO extension under an issuer that advertises fails, because the
|
|
317
|
+
* clause says the subject MUST contain one; an issuer carrying none constrains nothing, so the
|
|
318
|
+
* constraint is one-directional.
|
|
319
|
+
*
|
|
320
|
+
* Set semantics. SEQUENCE OF fixes no order and forbids no repeat, so neither side's order nor its
|
|
321
|
+
* multiplicity may be read as meaning, and the comparison cannot stop at a first match.
|
|
322
|
+
*
|
|
323
|
+
* Returns null when there is nothing to check, so a boundary that was not checked reports no row
|
|
324
|
+
* rather than a passing one.
|
|
325
|
+
*
|
|
326
|
+
* The anchor is not read. `anchorFromCert` keeps the name, key and algorithm and drops the
|
|
327
|
+
* certificate's extensions, so a trust anchor's own advertised features cannot reach this and the
|
|
328
|
+
* rule is applied from the first certificate in the path downward. An anchor that advertises
|
|
329
|
+
* features constrains nothing here, which is stated rather than implied. */
|
|
330
|
+
function tlsFeatureChainCheck(issuerCert, cert) {
|
|
331
|
+
if (!issuerCert) return null;
|
|
332
|
+
var issuerKu;
|
|
333
|
+
try { issuerKu = decodeExt(issuerCert, OID.keyUsage); }
|
|
334
|
+
// allow:swallow-unverified a keyUsage that cannot be read is refused by its own check on that certificate
|
|
335
|
+
catch (_e) { return null; }
|
|
336
|
+
if (!(issuerKu && issuerKu.value && issuerKu.value.keyCertSign === true)) return null;
|
|
337
|
+
var issuerFeatures;
|
|
338
|
+
try { issuerFeatures = decodeExt(issuerCert, OID.tlsFeature); }
|
|
339
|
+
catch (e) { return guard.verdict.of({ ok: false, code: pathCode(e, "path/bad-tls-feature") }); }
|
|
340
|
+
if (!issuerFeatures) return null;
|
|
341
|
+
/** @internal The subject's extension is read BEFORE the issuer's set is looked at, because the
|
|
342
|
+
* clause is two requirements and the first one does not mention the set: a certificate signed by a
|
|
343
|
+
* signing certificate that CONTAINS the extension MUST CONTAIN one, and what it contains must then
|
|
344
|
+
* cover the issuer's features. Testing the set first read an issuer whose Features list is EMPTY as
|
|
345
|
+
* nothing to require and returned success for a subject that carried no extension at all, which the
|
|
346
|
+
* MUST CONTAIN half forbids; it also meant a malformed subject value under such an issuer was never
|
|
347
|
+
* decoded. An empty SEQUENCE is a conforming Features, the syntax carrying no SIZE constraint. */
|
|
348
|
+
var offeredExt;
|
|
349
|
+
try { offeredExt = decodeExt(cert, OID.tlsFeature); }
|
|
350
|
+
catch (e) { return guard.verdict.of({ ok: false, code: pathCode(e, "path/bad-tls-feature") }); }
|
|
351
|
+
if (!offeredExt) return guard.verdict.of({ ok: false, code: "path/tls-feature-not-offered" });
|
|
352
|
+
var required = (issuerFeatures.value && issuerFeatures.value.features) || [];
|
|
353
|
+
if (!required.length) return guard.verdict.of({ ok: true });
|
|
354
|
+
var offered = (offeredExt.value && offeredExt.value.features) || [];
|
|
355
|
+
/** @internal Membership through a set rather than a scan of the offered list per required value.
|
|
356
|
+
* Both lists come off untrusted certificates and the syntax bounds neither, so the pairwise form is
|
|
357
|
+
* work an issuer and a subject choose together: MEASURED, 20000 values on each side is 4 x 10^8
|
|
358
|
+
* comparisons, synchronous, on a path whose signatures may already have failed. A set makes it
|
|
359
|
+
* linear in the two lengths, and the values are bigints, which a set compares by value. */
|
|
360
|
+
var offeredSet = new intrinsic.Set();
|
|
361
|
+
for (var j = 0; j < offered.length; j++) intrinsic.setAdd(offeredSet, offered[j]);
|
|
362
|
+
for (var i = 0; i < required.length; i++) {
|
|
363
|
+
if (!intrinsic.setHas(offeredSet, required[i])) {
|
|
364
|
+
return guard.verdict.of({ ok: false, code: "path/tls-feature-not-offered" });
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return guard.verdict.of({ ok: true });
|
|
368
|
+
}
|
|
369
|
+
|
|
303
370
|
function statefulHbsKeyUsageCheck(cert, isCa) {
|
|
304
371
|
var ku;
|
|
305
372
|
try { ku = decodeExt(cert, OID.keyUsage); }
|
|
@@ -1552,6 +1619,16 @@ async function _validate(path, opts, run, scope) {
|
|
|
1552
1619
|
guard.list.append(checks,{ name: "nameChaining", ok: chainOk === true, code: chainOk === true ? undefined : "path/name-chaining" });
|
|
1553
1620
|
if (chainOk !== true) failed = true;
|
|
1554
1621
|
|
|
1622
|
+
/** @internal In the loop body, so it runs at EVERY issuer-to-subject boundary rather than only at
|
|
1623
|
+
* the target: RFC 7633 sec. 4.2.2 constrains each certificate against the one that signed it. The
|
|
1624
|
+
* issuer is the previous entry, which is `null` for the first certificate because the anchor's
|
|
1625
|
+
* extensions are not part of the path. */
|
|
1626
|
+
var tfRes = tlsFeatureChainCheck(idx > 0 ? certs[idx - 1] : null, cert);
|
|
1627
|
+
if (tfRes) {
|
|
1628
|
+
guard.list.append(checks, { name: "tlsFeatureChain", ok: tfRes.ok, code: tfRes.ok ? undefined : tfRes.code });
|
|
1629
|
+
if (!tfRes.ok) failed = true;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1555
1632
|
if (!(selfIssued(cert) && i !== n)) {
|
|
1556
1633
|
var ncRes;
|
|
1557
1634
|
try { ncRes = checkNameConstraints(state, cert); }
|
|
@@ -3151,14 +3228,18 @@ async function _fetchAiaIssuers(current, aia) {
|
|
|
3151
3228
|
var u;
|
|
3152
3229
|
try { u = new _URL(ad.accessLocation.value); }
|
|
3153
3230
|
catch (_e) { continue; }
|
|
3154
|
-
if (u
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
if (
|
|
3231
|
+
if (_urlProtocol(u) !== "https:") continue;
|
|
3232
|
+
/** @internal The host read ONCE and both address tests applied to that one string: the blocklist
|
|
3233
|
+
* and the literal-versus-name question have to be answered about the same host. */
|
|
3234
|
+
var uHost = _urlHostname(u);
|
|
3235
|
+
if (_isBlockedAiaHost(uHost)) continue;
|
|
3236
|
+
if (net.isIP(_stripBrackets(uHost)) === 0 && !aia.transportGuardsAddresses) continue;
|
|
3237
|
+
_urlSetHash(u, "");
|
|
3238
|
+
var uHref = _urlHref(u);
|
|
3239
|
+
if (intrinsic.setHas(aia.fetchedUrls, uHref) || intrinsic.setHas(seenThisCert, uHref)) continue;
|
|
3159
3240
|
if (seenThisCert.size >= aia.maxPerCert) break;
|
|
3160
|
-
intrinsic.setAdd(seenThisCert,
|
|
3161
|
-
guard.list.append(uris,
|
|
3241
|
+
intrinsic.setAdd(seenThisCert, uHref);
|
|
3242
|
+
guard.list.append(uris, uHref);
|
|
3162
3243
|
}
|
|
3163
3244
|
var out = [];
|
|
3164
3245
|
for (var k = 0; k < uris.length; k++) {
|