@blamejs/pki 0.5.31 → 0.5.33
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 +25 -1
- package/README.md +3 -3
- package/lib/acme.js +347 -90
- package/lib/asn1-der.js +93 -33
- package/lib/cmc-build.js +19 -8
- package/lib/cmc-verify.js +4 -4
- package/lib/cmp-build.js +36 -6
- package/lib/cmp-session.js +5 -5
- package/lib/cmp-verify.js +86 -16
- package/lib/cms-compress.js +2 -2
- package/lib/cms-decrypt.js +3 -3
- package/lib/cms-encrypt.js +1 -1
- package/lib/cms-sign.js +214 -93
- package/lib/cms-verify.js +363 -237
- package/lib/crl-sign.js +4 -4
- package/lib/crmf-sign.js +2 -1
- package/lib/csr-sign.js +1 -4
- package/lib/ct.js +8 -4
- package/lib/est.js +234 -34
- package/lib/framework-error.js +20 -6
- package/lib/guard-bytes.js +56 -4
- package/lib/guard-intrinsic.js +13 -0
- package/lib/http-digest.js +69 -16
- package/lib/http-retry-after.js +68 -24
- package/lib/http-transport.js +60 -6
- package/lib/inspect.js +13 -2
- package/lib/key.js +2 -2
- package/lib/lint.js +1 -1
- package/lib/mime.js +27 -4
- package/lib/ocsp.js +7 -7
- package/lib/oid.js +16 -0
- package/lib/path-validate.js +76 -18
- package/lib/pbes2.js +2 -2
- package/lib/pkcs12-build.js +2 -2
- package/lib/pki-build.js +6 -6
- package/lib/rfc3339.js +75 -17
- package/lib/schema-c509.js +36 -9
- package/lib/schema-cmc.js +3 -3
- package/lib/schema-pkix.js +174 -25
- package/lib/schema-tsp.js +10 -1
- package/lib/sign-scheme.js +1 -1
- package/lib/sigstore.js +52 -9
- package/lib/smime.js +86 -6
- package/lib/trust.js +114 -14
- package/lib/tsp-sign.js +39 -27
- package/lib/validator-tpm.js +2 -2
- package/lib/webauthn-mds.js +24 -6
- package/lib/webauthn.js +3 -3
- package/lib/webcrypto.js +80 -1
- package/lib/x509-sign.js +1 -1
- package/package.json +2 -2
- package/sbom.cdx.json +6 -6
package/lib/guard-bytes.js
CHANGED
|
@@ -27,6 +27,8 @@ var _bufferFrom = intrinsic.bufferFrom;
|
|
|
27
27
|
// question this guard asked on the caller's behalf. See guard-intrinsic for the whole set.
|
|
28
28
|
var _isView = intrinsic.isView;
|
|
29
29
|
var _isBuffer = intrinsic.isBuffer;
|
|
30
|
+
var _asyncIterator = intrinsic.asyncIterator;
|
|
31
|
+
var _NO_ARGS = intrinsic.freeze([]);
|
|
30
32
|
var _create = intrinsic.create;
|
|
31
33
|
var _getPrototypeOf = intrinsic.getPrototypeOf;
|
|
32
34
|
var _getOwnPropertyDescriptor = intrinsic.getOwnPropertyDescriptor;
|
|
@@ -41,7 +43,7 @@ var _stringify = intrinsic.stringify;
|
|
|
41
43
|
var _forEach = intrinsic.forEach;
|
|
42
44
|
var _map = intrinsic.map;
|
|
43
45
|
var _filter = intrinsic.filter;
|
|
44
|
-
var
|
|
46
|
+
var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
45
47
|
var _setForEach = intrinsic.uncurry(Set.prototype.forEach);
|
|
46
48
|
var _mapForEach = intrinsic.uncurry(Map.prototype.forEach);
|
|
47
49
|
var _String = intrinsic.String;
|
|
@@ -80,6 +82,35 @@ function isByteSource(x) {
|
|
|
80
82
|
return _isBuffer(x) || _isView(x) || _isArrayBuffer(x);
|
|
81
83
|
}
|
|
82
84
|
|
|
85
|
+
// asyncStreamOf(x) -> a LAZY single-use async iterable of x's byte chunks, or null when x is not a
|
|
86
|
+
// byte stream.
|
|
87
|
+
//
|
|
88
|
+
// A door offering both a byte source and a streamed content (an async iterable of byte chunks) asks
|
|
89
|
+
// this ONE module which it has, instead of re-inlining the `for await` protocol test and drifting on
|
|
90
|
+
// what counts. It reads x's Symbol.asyncIterator EXACTLY ONCE, captures that method, and returns a
|
|
91
|
+
// thin iterable that CALLS it only when the consumer drives the stream. Deferring the call matters
|
|
92
|
+
// twice: the underlying iterator is acquired once, in the digest engine, so reading the protocol
|
|
93
|
+
// property a second time downstream cannot fire a one-shot or stateful accessor and refuse a stream a
|
|
94
|
+
// direct `for await` accepts; AND a caller whose iterator factory opens a file or socket has that
|
|
95
|
+
// resource opened only on a path that will actually consume it, never on a pre-hash rejection (a
|
|
96
|
+
// missing detached option, an empty signer list, a message needing no streamed digest) that would
|
|
97
|
+
// otherwise acquire and leak it. A byte source is bytes, never a stream, EVEN when a caller attached a
|
|
98
|
+
// Symbol.asyncIterator to it: the byte-source classification wins and is checked FIRST, so a Buffer /
|
|
99
|
+
// typed array / DataView goes down the byte path every time and its own accessors are never invoked
|
|
100
|
+
// here. A callable carrying the method is a valid async iterable that `for await` consumes, so it too
|
|
101
|
+
// yields a stream.
|
|
102
|
+
// @enforced-by behavioral -- a lazy stream wrapper has no rename-proof code shape; the RED vectors (a
|
|
103
|
+
// stream produced where a byte source is not, a one-shot-accessor stream still consumed, and the
|
|
104
|
+
// factory NOT invoked until the stream is driven) are the guard.
|
|
105
|
+
function asyncStreamOf(x) {
|
|
106
|
+
if (isByteSource(x) || x == null || (typeof x !== "object" && typeof x !== "function")) return null;
|
|
107
|
+
var acquire = x[_asyncIterator];
|
|
108
|
+
if (typeof acquire !== "function") return null;
|
|
109
|
+
var stream = {};
|
|
110
|
+
stream[_asyncIterator] = function () { return _apply(acquire, x, _NO_ARGS); };
|
|
111
|
+
return stream;
|
|
112
|
+
}
|
|
113
|
+
|
|
83
114
|
// Is this value a byte store, asked of the internal slot rather than the prototype?
|
|
84
115
|
//
|
|
85
116
|
// `v instanceof ArrayBuffer` answers what a value inherits from, which is a different question
|
|
@@ -151,10 +182,26 @@ function _reView(v) {
|
|
|
151
182
|
// The two string operations in this table run AT LOAD, in the same statement that reads
|
|
152
183
|
// `util.types` once and for the same reason. Nothing a caller can replace has run yet, so there is
|
|
153
184
|
// no live read to remove; capturing them would only move the question one line earlier.
|
|
185
|
+
// A util.types predicate that names a concrete typed-array kind: "is" + one-or-more alphanumerics +
|
|
186
|
+
// "Array" (e.g. "isUint8Array"), decided character by character so no regex match protocol -- which
|
|
187
|
+
// re-dispatches through a replaceable `exec` -- stands between this table and the runtime it reads.
|
|
188
|
+
function _isTypedArrayPredName(name) {
|
|
189
|
+
var n = name.length;
|
|
190
|
+
if (n < 8) return false; // "is" + >=1 middle char + "Array"
|
|
191
|
+
if (_charCodeAt(name, 0) !== 105 || _charCodeAt(name, 1) !== 115) return false; // "is"
|
|
192
|
+
var tail = "Array";
|
|
193
|
+
for (var k = 0; k < 5; k++) { if (_charCodeAt(name, n - 5 + k) !== _charCodeAt(tail, k)) return false; }
|
|
194
|
+
for (var i = 2; i < n - 5; i++) {
|
|
195
|
+
var c = _charCodeAt(name, i);
|
|
196
|
+
if (!((c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) return false;
|
|
197
|
+
}
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
|
|
154
201
|
var _CONCRETE_KINDS = _filter(
|
|
155
202
|
_map(
|
|
156
203
|
// allow:guard-reads-runtime-live -- module-init, see above
|
|
157
|
-
_filter(_objectKeys(util.types), function (name) { return
|
|
204
|
+
_filter(_objectKeys(util.types), function (name) { return _isTypedArrayPredName(name); }),
|
|
158
205
|
// allow:guard-reads-runtime-live -- module-init, see above
|
|
159
206
|
function (name) { return [util.types[name], globalThis[name.slice(2)]]; }
|
|
160
207
|
),
|
|
@@ -188,7 +235,12 @@ function _kindName(v) {
|
|
|
188
235
|
return typeof v === "function" ? "function" : "value";
|
|
189
236
|
}
|
|
190
237
|
|
|
191
|
-
function _article(name) {
|
|
238
|
+
function _article(name) {
|
|
239
|
+
var c = name.length > 0 ? _charCodeAt(name, 0) : 0;
|
|
240
|
+
var vowel = c === 65 || c === 69 || c === 73 || c === 79 || c === 85 ||
|
|
241
|
+
c === 97 || c === 101 || c === 105 || c === 111 || c === 117; // A E I O U, either case
|
|
242
|
+
return vowel ? "an" : "a";
|
|
243
|
+
}
|
|
192
244
|
|
|
193
245
|
// outputView(input, ErrorClass, code, label) -> a Buffer aliasing a caller's view, for WRITING.
|
|
194
246
|
//
|
|
@@ -1036,6 +1088,6 @@ function fixedCall(ErrorClass, code, args, body) {
|
|
|
1036
1088
|
// call, so a writable export would let a caller replace the check rather than pass it.
|
|
1037
1089
|
module.exports = intrinsic.freeze({
|
|
1038
1090
|
view: view, source: source, snapshot: snapshot, snapshotSource: snapshotSource,
|
|
1039
|
-
isByteSource: isByteSource, lengthOf: lengthOf, outputView: outputView,
|
|
1091
|
+
isByteSource: isByteSource, asyncStreamOf: asyncStreamOf, lengthOf: lengthOf, outputView: outputView,
|
|
1040
1092
|
snapshotDeep: snapshotDeep, fixArguments: fixArguments, fixedCall: fixedCall,
|
|
1041
1093
|
});
|
package/lib/guard-intrinsic.js
CHANGED
|
@@ -59,6 +59,10 @@ var _bufferCompare = Buffer.compare;
|
|
|
59
59
|
// response is about this issuer, a responder ID says this key signed it. Dispatched live, one
|
|
60
60
|
// answering true makes a response about a DIFFERENT certificate match the one being asked about.
|
|
61
61
|
var _bufferEquals = uncurry(Buffer.prototype.equals);
|
|
62
|
+
// Buffer#toString, uncurried at load. Used to build dedup keys (a hex string) from bytes the toolkit
|
|
63
|
+
// owns; dispatched live, a replaced toString could make two distinct values key alike or one value key
|
|
64
|
+
// differently on two reads, so a dedup over untrusted-adjacent bytes would merge or split wrongly.
|
|
65
|
+
var _bufferToString = uncurry(Buffer.prototype.toString);
|
|
62
66
|
|
|
63
67
|
// The rest of what a guard reads from the runtime while it is deciding something. These are
|
|
64
68
|
// grouped here rather than captured file by file so the set is enumerable in one place and a guard
|
|
@@ -94,6 +98,13 @@ var _mathMax = Math.max;
|
|
|
94
98
|
var _parseInt = parseInt;
|
|
95
99
|
var _promiseResolve = Promise.resolve;
|
|
96
100
|
var _promiseReject = Promise.reject;
|
|
101
|
+
// The well-known async-iteration symbol, captured at load. `for await` uses this symbol at the
|
|
102
|
+
// language level regardless of the global `Symbol` object, so a classifier that reads
|
|
103
|
+
// `Symbol.asyncIterator` live would disagree with the loop the moment a co-resident replaced the
|
|
104
|
+
// writable `Symbol` global: a real async iterable would be looked up under the wrong key, judged not
|
|
105
|
+
// iterable, and refused where the engine would have consumed it. Reading the captured symbol keeps
|
|
106
|
+
// the door's answer and the loop's behavior the same.
|
|
107
|
+
var _asyncIterator = Symbol.asyncIterator;
|
|
97
108
|
|
|
98
109
|
// The array operations, uncurried at load. These run on arrays the TOOLKIT owns -- an `ownKeys`
|
|
99
110
|
// result, a local accumulator -- so the receiver is never a caller's object; what is caller-
|
|
@@ -274,6 +285,7 @@ module.exports = Object.freeze({
|
|
|
274
285
|
bufferFrom: _bufferFrom,
|
|
275
286
|
bufferAlloc: _bufferAlloc,
|
|
276
287
|
bufferConcat: _bufferConcat,
|
|
288
|
+
bufToString: _bufferToString,
|
|
277
289
|
isBuffer: _bufferIsBuffer,
|
|
278
290
|
byteLength: _bufferByteLength,
|
|
279
291
|
compare: _bufferCompare,
|
|
@@ -309,6 +321,7 @@ module.exports = Object.freeze({
|
|
|
309
321
|
bigIntToString: _bigIntToString,
|
|
310
322
|
promiseResolve: _promiseResolve,
|
|
311
323
|
promiseReject: _promiseReject,
|
|
324
|
+
asyncIterator: _asyncIterator,
|
|
312
325
|
forEach: _arrForEach,
|
|
313
326
|
map: _arrMap,
|
|
314
327
|
filter: _arrFilter,
|
package/lib/http-digest.js
CHANGED
|
@@ -19,6 +19,10 @@ var intrinsic = require("./guard-intrinsic");
|
|
|
19
19
|
// replaceable properties to answer one thing, and here it decides whether an auth-param repeats --
|
|
20
20
|
// answering false lets the second copy of a nonce overwrite the first.
|
|
21
21
|
var _hasOwn = intrinsic.hasOwn;
|
|
22
|
+
var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
23
|
+
var _strSlice = intrinsic.uncurry(String.prototype.slice);
|
|
24
|
+
var _fromCharCode = intrinsic.fromCharCode;
|
|
25
|
+
var pkix = require("./schema-pkix"); // the shared JS-\s code-point predicate (pkix.isJsWhitespace)
|
|
22
26
|
|
|
23
27
|
// Algorithm registry, a data row and not a switch: rank orders the RFC 7616 sec. 3.7
|
|
24
28
|
// "most secure the client can use" selection (SHA-512-256 > SHA-256 > MD5); an unknown algorithm ranks 0.
|
|
@@ -41,7 +45,12 @@ var DEFAULT_CODES = {
|
|
|
41
45
|
// is hashed over its exact bytes. KD(secret, data) = H(secret ":" data).
|
|
42
46
|
function H(hash, s) { return crypto.createHash(hash).update(s, "latin1").digest("hex"); }
|
|
43
47
|
function KD(hash, secret, data) { return H(hash, secret + ":" + data); }
|
|
44
|
-
|
|
48
|
+
// Quote a value: wrap in double quotes, backslash-escaping any " or \ (matching /(["\\])/g -> "\\$1").
|
|
49
|
+
function _qstr(v) {
|
|
50
|
+
var s = String(v), out = "";
|
|
51
|
+
for (var i = 0; i < s.length; i++) { var c = _charCodeAt(s, i); if (c === 34 || c === 92) out += "\\"; out += _fromCharCode(c); }
|
|
52
|
+
return "\"" + out + "\"";
|
|
53
|
+
}
|
|
45
54
|
|
|
46
55
|
// Split a header at commas outside a quoted-string (a quoted comma is a literal, not a delimiter).
|
|
47
56
|
function _commaSplitOutsideQuotes(s) {
|
|
@@ -69,7 +78,13 @@ function _firstEqOutsideQuotes(s) {
|
|
|
69
78
|
}
|
|
70
79
|
return -1;
|
|
71
80
|
}
|
|
72
|
-
|
|
81
|
+
// Unquote a well-formed quoted-string (only ever called after _closedQuote): drop the surrounding
|
|
82
|
+
// quotes and each escaping backslash (matching s.slice(1,-1).replace(/\\(.)/g, "$1")).
|
|
83
|
+
function _unq(s) {
|
|
84
|
+
var out = "", last = 1, n = s.length; // skip the opening quote at 0
|
|
85
|
+
for (var i = 1; i < n - 1; i++) { if (_charCodeAt(s, i) === 92) { out += _strSlice(s, last, i); last = i + 1; i += 1; } }
|
|
86
|
+
return out + _strSlice(s, last, n - 1); // trailing content, excluding the closing quote
|
|
87
|
+
}
|
|
73
88
|
// A well-formed quoted-string: opens with ", closes with an unescaped " as the last character (nothing trails
|
|
74
89
|
// it). An unterminated / trailing-garbage quoted-string is malformed (RFC 7230 sec. 3.2.6) and rejected.
|
|
75
90
|
function _closedQuote(s) {
|
|
@@ -86,24 +101,62 @@ function _closedQuote(s) {
|
|
|
86
101
|
// A control octet (RFC 7230 sec. 3.2.6 quoted-string / qdtext forbids CTL except HTAB). Scanned with charCodeAt
|
|
87
102
|
// instead of a control-char regex literal (which eslint no-control-regex correctly refuses).
|
|
88
103
|
function _hasCtl(s) {
|
|
89
|
-
for (var i = 0; i < s.length; i++) { var c = s
|
|
104
|
+
for (var i = 0; i < s.length; i++) { var c = _charCodeAt(s, i); if ((c < 0x20 && c !== 0x09) || c === 0x7f) return true; }
|
|
90
105
|
return false;
|
|
91
106
|
}
|
|
92
107
|
|
|
93
108
|
// Split a WWW-Authenticate header into [{ scheme, paramText }]. A comma-separated segment whose pre-'='
|
|
94
109
|
// text is "token WS token" (a scheme name before the first auth-param key), or a bare token with no '=',
|
|
95
110
|
// STARTS a new challenge; a "key=value" segment is a param continuation of the current challenge.
|
|
111
|
+
// The leading token when `pre` is "token WS more" (/^(\S+)\s+(\S[\s\S]*)$/) -- a scheme name before the
|
|
112
|
+
// first auth-param -- else null. \s is the shared JS-\s predicate (RFC 7230 OWS is a subset).
|
|
113
|
+
function _matchTokenRest(pre) {
|
|
114
|
+
var n = pre.length, p = 0;
|
|
115
|
+
while (p < n && !pkix.isJsWhitespace(_charCodeAt(pre, p))) p += 1; // ^(\S+)
|
|
116
|
+
if (p === 0) return null;
|
|
117
|
+
var tokEnd = p;
|
|
118
|
+
if (p >= n || !pkix.isJsWhitespace(_charCodeAt(pre, p))) return null; // \s+
|
|
119
|
+
while (p < n && pkix.isJsWhitespace(_charCodeAt(pre, p))) p += 1;
|
|
120
|
+
if (p >= n || pkix.isJsWhitespace(_charCodeAt(pre, p))) return null; // (\S[\s\S]*)$ -- at least one non-\s
|
|
121
|
+
return _strSlice(pre, 0, tokEnd);
|
|
122
|
+
}
|
|
123
|
+
// The index past a leading "OWS token OWS" (/^\s*\S+\s+/), or 0 when the string does not begin that way.
|
|
124
|
+
// The caller slices from this index (the slice that replaces the removed .replace).
|
|
125
|
+
function _stripTokenIndex(seg) {
|
|
126
|
+
var n = seg.length, p = 0;
|
|
127
|
+
while (p < n && pkix.isJsWhitespace(_charCodeAt(seg, p))) p += 1; // ^\s*
|
|
128
|
+
var ts = p;
|
|
129
|
+
while (p < n && !pkix.isJsWhitespace(_charCodeAt(seg, p))) p += 1; // \S+
|
|
130
|
+
if (p === ts) return 0;
|
|
131
|
+
var ws = p;
|
|
132
|
+
while (p < n && pkix.isJsWhitespace(_charCodeAt(seg, p))) p += 1; // \s+ (required)
|
|
133
|
+
return p === ws ? 0 : p;
|
|
134
|
+
}
|
|
135
|
+
// String.prototype.split(/\s+/): the pieces between \s runs (a leading/trailing run yields a "" element).
|
|
136
|
+
function _splitWhitespace(s) {
|
|
137
|
+
var out = [], i = 0, n = s.length, ps = 0;
|
|
138
|
+
while (i < n) {
|
|
139
|
+
if (pkix.isJsWhitespace(_charCodeAt(s, i))) {
|
|
140
|
+
out[out.length] = _strSlice(s, ps, i);
|
|
141
|
+
while (i < n && pkix.isJsWhitespace(_charCodeAt(s, i))) i += 1;
|
|
142
|
+
ps = i;
|
|
143
|
+
} else i += 1;
|
|
144
|
+
}
|
|
145
|
+
out[out.length] = _strSlice(s, ps);
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
|
|
96
149
|
function _splitChallenges(s) {
|
|
97
150
|
var segs = _commaSplitOutsideQuotes(s);
|
|
98
151
|
var out = [], cur = null;
|
|
99
152
|
for (var i = 0; i < segs.length; i++) {
|
|
100
|
-
var seg = segs[i].
|
|
153
|
+
var seg = segs[i].trim();
|
|
101
154
|
if (seg === "") continue;
|
|
102
155
|
var eq = _firstEqOutsideQuotes(seg);
|
|
103
|
-
var pre = (eq < 0 ? seg : seg
|
|
104
|
-
var
|
|
156
|
+
var pre = (eq < 0 ? seg : _strSlice(seg, 0, eq)).trim();
|
|
157
|
+
var scheme = _matchTokenRest(pre);
|
|
105
158
|
if (eq < 0) { cur = { scheme: seg, paramText: "" }; out.push(cur); } // a bare scheme token (e.g. "Basic")
|
|
106
|
-
else if (
|
|
159
|
+
else if (scheme !== null) { cur = { scheme: scheme, paramText: _strSlice(seg, _stripTokenIndex(seg)) }; out.push(cur); } // "scheme firstKey=..."
|
|
107
160
|
else if (cur) { cur.paramText = cur.paramText ? cur.paramText + "," + seg : seg; } // "key=value" continuation
|
|
108
161
|
}
|
|
109
162
|
return out;
|
|
@@ -112,12 +165,12 @@ function _splitChallenges(s) {
|
|
|
112
165
|
function _parseParams(paramText, E, code) {
|
|
113
166
|
var segs = _commaSplitOutsideQuotes(paramText), map = Object.create(null);
|
|
114
167
|
for (var i = 0; i < segs.length; i++) {
|
|
115
|
-
var seg = segs[i].
|
|
168
|
+
var seg = segs[i].trim();
|
|
116
169
|
if (seg === "") continue;
|
|
117
170
|
var eq = _firstEqOutsideQuotes(seg);
|
|
118
171
|
if (eq < 0) throw E(code, "malformed Digest auth-param (no '='): " + JSON.stringify(seg));
|
|
119
|
-
var key = seg
|
|
120
|
-
var rawVal = seg
|
|
172
|
+
var key = _strSlice(seg, 0, eq).trim().toLowerCase();
|
|
173
|
+
var rawVal = _strSlice(seg, eq + 1).trim();
|
|
121
174
|
var quoted = rawVal.charAt(0) === "\"";
|
|
122
175
|
if (quoted && !_closedQuote(rawVal)) throw E(code, "an unterminated or trailing-garbage Digest quoted-string (RFC 7230 sec. 3.2.6)");
|
|
123
176
|
if (_hasOwn(map, key)) throw E(code, "repeated Digest auth-param " + JSON.stringify(key));
|
|
@@ -138,7 +191,7 @@ function _validateDigest(paramText, E, code) {
|
|
|
138
191
|
var qop = [];
|
|
139
192
|
if (p.qop) {
|
|
140
193
|
if (!p.qop.quoted) throw E(code, "the Digest qop must be a quoted list (RFC 7616 sec. 3.3)");
|
|
141
|
-
qop = p.qop.value.split(",").map(function (x) { return x.
|
|
194
|
+
qop = p.qop.value.split(",").map(function (x) { return x.trim().toLowerCase(); }).filter(Boolean);
|
|
142
195
|
// A qop directive that is present but lists no value (qop="" / qop=", ,") is malformed, and is not the
|
|
143
196
|
// absent-qop (RFC 2069) case. Reject it instead of silently collapsing to a no-qop response the server
|
|
144
197
|
// cannot accept (RFC 7616 sec. 3.3). Only an omitted qop directive selects the legacy no-qop path.
|
|
@@ -150,8 +203,8 @@ function _validateDigest(paramText, E, code) {
|
|
|
150
203
|
// server intended. Surfaced as an array of the listed URIs, or null when omitted or quoted-empty, which per
|
|
151
204
|
// the RFC means "all URIs on the responding server" (the answer may be reused for any same-origin URI, sec. 3.5).
|
|
152
205
|
if (p.domain && !p.domain.quoted) throw E(code, "the Digest domain must be a quoted-string (RFC 7616 sec. 3.3)");
|
|
153
|
-
var domain = (p.domain && p.domain.value.
|
|
154
|
-
? p.domain.value.
|
|
206
|
+
var domain = (p.domain && p.domain.value.trim() !== "")
|
|
207
|
+
? _splitWhitespace(p.domain.value.trim()) : null;
|
|
155
208
|
// charset (RFC 7616 sec. 3.3): the one permitted value is the unquoted token "UTF-8". A quoted charset, or any
|
|
156
209
|
// other value, is malformed, since answering it would hash the credentials in the wrong encoding, so it is
|
|
157
210
|
// rejected (and thus skipped during multi-offer selection in favor of a conforming offer).
|
|
@@ -250,7 +303,7 @@ function _octets(s, charset) {
|
|
|
250
303
|
// Does the string carry any non-ASCII code unit (> U+007F)? Such a UTF-8 username is sent via `username*`.
|
|
251
304
|
function _hasNonAscii(s) {
|
|
252
305
|
s = String(s == null ? "" : s);
|
|
253
|
-
for (var i = 0; i < s.length; i++) { if (s
|
|
306
|
+
for (var i = 0; i < s.length; i++) { if (_charCodeAt(s, i) > 0x7F) return true; }
|
|
254
307
|
return false;
|
|
255
308
|
}
|
|
256
309
|
|
|
@@ -264,7 +317,7 @@ function _pctEncodeUtf8(s) {
|
|
|
264
317
|
if ((b >= 0x30 && b <= 0x39) || (b >= 0x41 && b <= 0x5A) || (b >= 0x61 && b <= 0x7A) ||
|
|
265
318
|
b === 0x21 || b === 0x23 || b === 0x24 || b === 0x26 || b === 0x2B || b === 0x2D ||
|
|
266
319
|
b === 0x2E || b === 0x5E || b === 0x5F || b === 0x60 || b === 0x7C || b === 0x7E) {
|
|
267
|
-
out +=
|
|
320
|
+
out += _fromCharCode(b);
|
|
268
321
|
} else {
|
|
269
322
|
out += "%" + (b < 0x10 ? "0" : "") + b.toString(16).toUpperCase();
|
|
270
323
|
}
|
|
@@ -323,7 +376,7 @@ function answer(challenge, params, E) {
|
|
|
323
376
|
// (a same-origin redirect, an auth-int retry) increments nc; a fresh nonce restarts at 1. The caller supplies
|
|
324
377
|
// the count for this nonce (default 1); formatted as 8 lowercase hex.
|
|
325
378
|
var ncNum = (typeof params.nc === "number" && params.nc >= 1) ? Math.floor(params.nc) : 1;
|
|
326
|
-
var nc = ("0000000" + ncNum.toString(16)
|
|
379
|
+
var nc = _strSlice("0000000" + ncNum.toString(16), -8);
|
|
327
380
|
var response = useQop
|
|
328
381
|
? KD(alg.hash, HA1, nonce + ":" + nc + ":" + cnonce + ":" + useQop + ":" + HA2)
|
|
329
382
|
: KD(alg.hash, HA1, nonce + ":" + HA2);
|
package/lib/http-retry-after.js
CHANGED
|
@@ -19,12 +19,32 @@ var MAX_RETRY_AFTER_SECONDS = constants.TIME.days(365) / constants.TIME.seconds(
|
|
|
19
19
|
// rfc850-date / asctime-date a recipient must still accept. Gating Date.parse on this grammar keeps its
|
|
20
20
|
// permissiveness (an ISO string like "2026-07-10") from passing as a valid Retry-After header.
|
|
21
21
|
var MONTHS = "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" ");
|
|
22
|
-
var
|
|
23
|
-
var
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
var DOW_SHORT = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
|
23
|
+
var DOW_LONG = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
|
|
24
|
+
|
|
25
|
+
// The grammar is scanned character by character (no regex): each form is a fixed layout of a
|
|
26
|
+
// day-of-week name, delimiters, an all-digit field, a three-letter month name, and an HH:MM:SS time.
|
|
27
|
+
function _isDigit(ch) { return ch >= "0" && ch <= "9"; }
|
|
28
|
+
// The two-digit / four-digit integer at `i`, or -1 when those positions are not all digits.
|
|
29
|
+
function _digits2(s, i) { return _isDigit(s.charAt(i)) && _isDigit(s.charAt(i + 1)) ? (s.charCodeAt(i) - 48) * 10 + (s.charCodeAt(i + 1) - 48) : -1; }
|
|
30
|
+
function _digits4(s, i) { for (var k = 0; k < 4; k++) if (!_isDigit(s.charAt(i + k))) return -1; return parseInt(s.slice(i, i + 4), 10); }
|
|
31
|
+
// The length of a name from `list` matching `s` at `i` (the day-of-week alternation), or -1.
|
|
32
|
+
function _matchName(s, i, list) { for (var j = 0; j < list.length; j++) { if (s.slice(i, i + list[j].length) === list[j]) return list[j].length; } return -1; }
|
|
33
|
+
// The three-letter month at `i` when it is a real month name, else null.
|
|
34
|
+
function _monthAt(s, i) { return _matchName(s, i, MONTHS) === -1 ? null : s.slice(i, i + 3); }
|
|
35
|
+
// An "HH:MM:SS" time at `i` -> { hh, mi, ss, next } (next = i + 8), or null.
|
|
36
|
+
function _timeAt(s, i) {
|
|
37
|
+
var hh = _digits2(s, i); if (hh < 0 || s.charAt(i + 2) !== ":") return null;
|
|
38
|
+
var mi = _digits2(s, i + 3); if (mi < 0 || s.charAt(i + 5) !== ":") return null;
|
|
39
|
+
var ss = _digits2(s, i + 6); if (ss < 0) return null;
|
|
40
|
+
return { hh: hh, mi: mi, ss: ss, next: i + 8 };
|
|
41
|
+
}
|
|
42
|
+
// A non-empty run of ASCII digits (matching /^\d+$/, the delay-seconds form).
|
|
43
|
+
function _isAllDigits(s) {
|
|
44
|
+
if (s.length === 0) return false;
|
|
45
|
+
for (var i = 0; i < s.length; i++) if (!_isDigit(s.charAt(i))) return false;
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
28
48
|
|
|
29
49
|
// Expand an obsolete RFC 850 two-digit year: HTTP interprets it relative to the receipt year, as the full
|
|
30
50
|
// year with these two digits within [refYear-50, refYear+49] (moved to the past only when it would
|
|
@@ -40,29 +60,53 @@ function _rfc850Year(yy, refMs) {
|
|
|
40
60
|
return full;
|
|
41
61
|
}
|
|
42
62
|
|
|
63
|
+
// IMF-fixdate (the required form): "Sun, 06 Nov 1994 08:49:37 GMT".
|
|
64
|
+
function _scanImf(s) {
|
|
65
|
+
var p = _matchName(s, 0, DOW_SHORT);
|
|
66
|
+
if (p === -1 || s.slice(p, p + 2) !== ", ") return null; p += 2;
|
|
67
|
+
var dd = _digits2(s, p); if (dd < 0 || s.charAt(p + 2) !== " ") return null; p += 3;
|
|
68
|
+
var mo = _monthAt(s, p); if (!mo || s.charAt(p + 3) !== " ") return null; p += 4;
|
|
69
|
+
var yy = _digits4(s, p); if (yy < 0 || s.charAt(p + 4) !== " ") return null; p += 5;
|
|
70
|
+
var t = _timeAt(s, p); if (!t || s.slice(t.next, t.next + 4) !== " GMT" || t.next + 4 !== s.length) return null;
|
|
71
|
+
return { day: dd, monName: mo, year: yy, hh: t.hh, mi: t.mi, ss: t.ss };
|
|
72
|
+
}
|
|
73
|
+
// rfc850-date (obsolete, still accepted): "Sunday, 06-Nov-94 08:49:37 GMT" -- a two-digit year expanded
|
|
74
|
+
// relative to the receipt year.
|
|
75
|
+
function _scanRfc850(s, refMs) {
|
|
76
|
+
var p = _matchName(s, 0, DOW_LONG);
|
|
77
|
+
if (p === -1 || s.slice(p, p + 2) !== ", ") return null; p += 2;
|
|
78
|
+
var dd = _digits2(s, p); if (dd < 0 || s.charAt(p + 2) !== "-") return null; p += 3;
|
|
79
|
+
var mo = _monthAt(s, p); if (!mo || s.charAt(p + 3) !== "-") return null; p += 4;
|
|
80
|
+
var yy = _digits2(s, p); if (yy < 0 || s.charAt(p + 2) !== " ") return null; p += 3;
|
|
81
|
+
var t = _timeAt(s, p); if (!t || s.slice(t.next, t.next + 4) !== " GMT" || t.next + 4 !== s.length) return null;
|
|
82
|
+
return { day: dd, monName: mo, year: _rfc850Year(yy, refMs), hh: t.hh, mi: t.mi, ss: t.ss };
|
|
83
|
+
}
|
|
84
|
+
// asctime-date (obsolete, still accepted): "Sun Nov 6 08:49:37 1994" -- a space-padded one/two-digit day.
|
|
85
|
+
function _scanAsctime(s) {
|
|
86
|
+
var p = _matchName(s, 0, DOW_SHORT);
|
|
87
|
+
if (p === -1 || s.charAt(p) !== " ") return null; p += 1;
|
|
88
|
+
var mo = _monthAt(s, p); if (!mo || s.charAt(p + 3) !== " ") return null; p += 4;
|
|
89
|
+
var c0 = s.charAt(p);
|
|
90
|
+
if (!(c0 === " " || _isDigit(c0)) || !_isDigit(s.charAt(p + 1))) return null;
|
|
91
|
+
var dd = parseInt(s.slice(p, p + 2), 10); if (s.charAt(p + 2) !== " ") return null; p += 3;
|
|
92
|
+
var t = _timeAt(s, p); if (!t || s.charAt(t.next) !== " ") return null;
|
|
93
|
+
var yy = _digits4(s, t.next + 1); if (yy < 0 || t.next + 1 + 4 !== s.length) return null;
|
|
94
|
+
return { day: dd, monName: mo, year: yy, hh: t.hh, mi: t.mi, ss: t.ss };
|
|
95
|
+
}
|
|
96
|
+
|
|
43
97
|
// Parse an HTTP-date to epoch ms, or NaN when its grammar, calendar, or time is invalid. Built from the
|
|
44
98
|
// extracted fields via Date.UTC (never Date.parse): every HTTP-date is UTC, the asctime form carries
|
|
45
99
|
// no GMT token so delegating would parse it in local time, and the round-trip check rejects an
|
|
46
100
|
// impossible date / time (Date.UTC normalizes Feb 31 -> Mar 2 just as Date.parse would).
|
|
47
101
|
function httpDateMs(s, refMs) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
var
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
var dmy = /(\d{2})[ -](\w{3})[ -](\d{2,4})/.exec(s); // IMF / rfc850: day month year
|
|
54
|
-
if (!dmy) return NaN;
|
|
55
|
-
day = parseInt(dmy[1], 10); monName = dmy[2]; year = parseInt(dmy[3], 10);
|
|
56
|
-
if (year < 100) year = _rfc850Year(year, refMs); // rfc850 two-digit year
|
|
57
|
-
}
|
|
58
|
-
var t = /(\d{2}):(\d{2}):(\d{2})/.exec(s);
|
|
59
|
-
var mon = MONTHS.indexOf(monName);
|
|
60
|
-
if (!t || mon < 0) return NaN;
|
|
61
|
-
var hh = parseInt(t[1], 10), mi = parseInt(t[2], 10), ss = parseInt(t[3], 10);
|
|
62
|
-
var when = Date.UTC(year, mon, day, hh, mi, ss);
|
|
102
|
+
var f = _scanImf(s) || _scanRfc850(s, refMs) || _scanAsctime(s);
|
|
103
|
+
if (!f) return NaN;
|
|
104
|
+
var mon = MONTHS.indexOf(f.monName);
|
|
105
|
+
if (mon < 0) return NaN;
|
|
106
|
+
var when = Date.UTC(f.year, mon, f.day, f.hh, f.mi, f.ss);
|
|
63
107
|
var d = new Date(when);
|
|
64
|
-
if (d.getUTCDate() !== day || d.getUTCMonth() !== mon || d.getUTCFullYear() !== year ||
|
|
65
|
-
d.getUTCHours() !== hh || d.getUTCMinutes() !== mi || d.getUTCSeconds() !== ss) return NaN;
|
|
108
|
+
if (d.getUTCDate() !== f.day || d.getUTCMonth() !== mon || d.getUTCFullYear() !== f.year ||
|
|
109
|
+
d.getUTCHours() !== f.hh || d.getUTCMinutes() !== f.mi || d.getUTCSeconds() !== f.ss) return NaN;
|
|
66
110
|
return when;
|
|
67
111
|
}
|
|
68
112
|
|
|
@@ -78,7 +122,7 @@ function parse(value, opts) {
|
|
|
78
122
|
opts = opts || {};
|
|
79
123
|
var raStr = String(value).trim();
|
|
80
124
|
var out = { retryAfterSeconds: null, retryAfterDate: null };
|
|
81
|
-
if (
|
|
125
|
+
if (_isAllDigits(raStr)) {
|
|
82
126
|
var n = parseInt(raStr, 10);
|
|
83
127
|
// opts.cap (seconds): a caller that will clamp anyway (e.g. an ARI poll cadence) wants a delay above its
|
|
84
128
|
// ceiling reduced to the ceiling, not rejected, so a valid-but-huge value never discards the response.
|
package/lib/http-transport.js
CHANGED
|
@@ -69,14 +69,38 @@ function _budget(value, key, dflt, max, E, code) {
|
|
|
69
69
|
// form (e.g. what pki.est.cacerts / parseCertsOnly return), is silently ignored there, so the
|
|
70
70
|
// connection would fail server authentication despite a valid anchor. Wrap a DER anchor as PEM
|
|
71
71
|
// (a Buffer already carrying PEM text, or a PEM string, passes through unchanged).
|
|
72
|
+
// The JS regex \s code points representable in a latin1 string (0..255): TAB LF VT FF CR SP NBSP.
|
|
73
|
+
// Built at load so the source stays pure ASCII. The armor scan runs over a latin1 prefix, where these
|
|
74
|
+
// are the only \s characters that can appear, so this is exactly /^\s*/ for that input.
|
|
75
|
+
var _LATIN1_WS = String.fromCharCode(0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x20, 0xa0);
|
|
76
|
+
|
|
77
|
+
// PEM armor at the START after optional whitespace, decided by character scan (no regex): skip the
|
|
78
|
+
// leading \s run, then require "-----BEGIN ".
|
|
79
|
+
function _looksLikePemArmor(s) {
|
|
80
|
+
var i = 0;
|
|
81
|
+
while (i < s.length && _LATIN1_WS.indexOf(s.charAt(i)) !== -1) i += 1;
|
|
82
|
+
return s.slice(i, i + 11) === "-----BEGIN ";
|
|
83
|
+
}
|
|
84
|
+
// Base64 wrapped to <=64-column lines, each terminated by LF (matching /.{1,64}/g -> "$&\n", which
|
|
85
|
+
// keeps a trailing LF on the final line).
|
|
86
|
+
function _wrap64Lines(s) {
|
|
87
|
+
var out = "";
|
|
88
|
+
for (var i = 0; i < s.length; i += 64) out += s.slice(i, i + 64) + "\n";
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
// The surrounding "[" "]" of an IPv6 literal removed, else the string unchanged (matching /^\[(.*)\]$/).
|
|
92
|
+
function _stripIpv6Brackets(s) {
|
|
93
|
+
return (s.length >= 2 && s.charAt(0) === "[" && s.charAt(s.length - 1) === "]") ? s.slice(1, s.length - 1) : s;
|
|
94
|
+
}
|
|
95
|
+
|
|
72
96
|
function _pemifyAnchor(a) {
|
|
73
97
|
if (typeof a === "string") return a;
|
|
74
98
|
if (Buffer.isBuffer(a)) {
|
|
75
99
|
// Detect PEM by its armor at the START (after optional whitespace) only: a DER certificate begins
|
|
76
100
|
// with the SEQUENCE tag 0x30 and may itself contain the bytes "-----BEGIN" in a subject / extension
|
|
77
101
|
// value, so an anywhere-search would leave such a DER anchor unwrapped and unusable.
|
|
78
|
-
if (
|
|
79
|
-
var b64 = a.toString("base64")
|
|
102
|
+
if (_looksLikePemArmor(a.toString("latin1", 0, 64))) return a;
|
|
103
|
+
var b64 = _wrap64Lines(a.toString("base64"));
|
|
80
104
|
return "-----BEGIN CERTIFICATE-----\n" + b64 + "-----END CERTIFICATE-----\n";
|
|
81
105
|
}
|
|
82
106
|
return a;
|
|
@@ -179,11 +203,41 @@ var _guardedLookup = _makeGuardedLookup(nodeDns.lookup);
|
|
|
179
203
|
// certificate / identity / handshake failure is a server-authentication failure;
|
|
180
204
|
// anything else is a generic transport error. Every arm threads the raw fault as
|
|
181
205
|
// `.cause`, so the diagnostic survives.
|
|
206
|
+
// SELF.?SIGNED : "self", an optional single non-line-terminator character, then "signed" (in a lowercased string).
|
|
207
|
+
function _hasSelfSigned(l) {
|
|
208
|
+
for (var i = 0; i + 4 <= l.length; i++) {
|
|
209
|
+
if (l.charCodeAt(i) === 0x73 && l.charCodeAt(i + 1) === 0x65 && l.charCodeAt(i + 2) === 0x6c && l.charCodeAt(i + 3) === 0x66) {
|
|
210
|
+
if (l.indexOf("signed", i + 4) === i + 4) return true;
|
|
211
|
+
var mid = l.charCodeAt(i + 4);
|
|
212
|
+
if (i + 4 < l.length && mid !== 0x0a && mid !== 0x0d && mid !== 0x2028 && mid !== 0x2029 && l.indexOf("signed", i + 5) === i + 5) return true;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
// \bWORD\b : `word` (lowercase) bounded on both sides by a non-word character ([a-z0-9_]) or a string edge.
|
|
218
|
+
function _hasWordBounded(l, word) {
|
|
219
|
+
var at = l.indexOf(word);
|
|
220
|
+
while (at !== -1) {
|
|
221
|
+
var before = at === 0 || !_isLowerWordChar(l.charCodeAt(at - 1));
|
|
222
|
+
var afterI = at + word.length;
|
|
223
|
+
var after = afterI === l.length || !_isLowerWordChar(l.charCodeAt(afterI));
|
|
224
|
+
if (before && after) return true;
|
|
225
|
+
at = l.indexOf(word, at + 1);
|
|
226
|
+
}
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
function _isLowerWordChar(c) { return (c >= 97 && c <= 122) || (c >= 48 && c <= 57) || c === 95; }
|
|
182
230
|
function _classifyError(e, C) {
|
|
183
231
|
if (e && e.pkiBlockedAddress) return C("blocked-address");
|
|
184
232
|
var s = String((e && e.code) || "") + " " + String((e && e.message) || "");
|
|
185
|
-
|
|
186
|
-
|
|
233
|
+
var l = s.toLowerCase();
|
|
234
|
+
// /PROTOCOL_VERSION|UNSUPPORTED_PROTOCOL|VERSION_TOO_LOW|WRONG_VERSION|NO_PROTOCOLS_AVAILABLE|INAPPROPRIATE_FALLBACK/i
|
|
235
|
+
if (l.indexOf("protocol_version") !== -1 || l.indexOf("unsupported_protocol") !== -1 || l.indexOf("version_too_low") !== -1 ||
|
|
236
|
+
l.indexOf("wrong_version") !== -1 || l.indexOf("no_protocols_available") !== -1 || l.indexOf("inappropriate_fallback") !== -1) return C("tls-floor");
|
|
237
|
+
// /CERT|SELF.?SIGNED|VERIFY|ALTNAME|HOSTNAME|DEPTH_ZERO|LOCAL_ISSUER|HANDSHAKE|\bSSL\b|\bTLS\b/i
|
|
238
|
+
if (l.indexOf("cert") !== -1 || l.indexOf("verify") !== -1 || l.indexOf("altname") !== -1 || l.indexOf("hostname") !== -1 ||
|
|
239
|
+
l.indexOf("depth_zero") !== -1 || l.indexOf("local_issuer") !== -1 || l.indexOf("handshake") !== -1 ||
|
|
240
|
+
_hasSelfSigned(l) || _hasWordBounded(l, "ssl") || _hasWordBounded(l, "tls")) return C("server-auth-failed");
|
|
187
241
|
return C("transport-error");
|
|
188
242
|
}
|
|
189
243
|
|
|
@@ -256,7 +310,7 @@ function httpsTransport(defaults) {
|
|
|
256
310
|
if (minVersion !== "TLSv1.2" && minVersion !== "TLSv1.3") throw E(C("bad-input"), "tls.minVersion must be 'TLSv1.2' or 'TLSv1.3' (never below the floor), got " + minVersion);
|
|
257
311
|
|
|
258
312
|
var body = request.body;
|
|
259
|
-
if (
|
|
313
|
+
if (guard.bytes.isByteSource(body)) body = guard.bytes.source(body, E, C("bad-input"), "the request body");
|
|
260
314
|
|
|
261
315
|
// Frame a body-bearing request with a fixed Content-Length computed from the EXACT body, so it is sent
|
|
262
316
|
// length-delimited rather than Transfer-Encoding: chunked (node's default when a body is written with no
|
|
@@ -273,7 +327,7 @@ function httpsTransport(defaults) {
|
|
|
273
327
|
|
|
274
328
|
// URL.hostname keeps the square brackets on an IPv6 literal ([2001:db8::1]); node's `hostname`
|
|
275
329
|
// option and its IP-detection expect the bare address, so strip the brackets once here.
|
|
276
|
-
var host = url.hostname
|
|
330
|
+
var host = _stripIpv6Brackets(url.hostname);
|
|
277
331
|
var options = {
|
|
278
332
|
method: request.method || "GET",
|
|
279
333
|
hostname: host,
|
package/lib/inspect.js
CHANGED
|
@@ -121,7 +121,7 @@ function _serial(cert, indent) {
|
|
|
121
121
|
// anything larger renders as a colon-hex block.
|
|
122
122
|
if (buf.length <= 6) {
|
|
123
123
|
var n = parseInt(buf.toString("hex") || "0", 16);
|
|
124
|
-
return "Serial Number: " + n + " (0x" + (buf.toString("hex")
|
|
124
|
+
return "Serial Number: " + n + " (0x" + (_stripLeadingZeros(buf.toString("hex")) || "0") + ")";
|
|
125
125
|
}
|
|
126
126
|
return "Serial Number:\n" + " ".repeat(indent) + _hexColon(buf, {});
|
|
127
127
|
}
|
|
@@ -905,11 +905,22 @@ _CMS_SHAPE[oid.byName("compressedData")] = function (o) { return _isObj(o.compre
|
|
|
905
905
|
// SEQUENCE is a real Certificate/CertificateList -> delegate to the full sub-report
|
|
906
906
|
// (guarded; a one-line summary on any failure); a CONTEXT-tagged CHOICE alternative
|
|
907
907
|
// (attribute certificate / other) renders a one-line tag+size summary, never parsed.
|
|
908
|
+
// Leading "0" characters removed, and a single trailing "\n" removed -- both by character scan
|
|
909
|
+
// (no regex): display helpers, matching the /^0+/ and /\n$/ replacements they succeed.
|
|
910
|
+
function _stripLeadingZeros(s) {
|
|
911
|
+
var i = 0;
|
|
912
|
+
while (i < s.length && s.charAt(i) === "0") i += 1;
|
|
913
|
+
return s.slice(i);
|
|
914
|
+
}
|
|
915
|
+
function _stripTrailingNewline(s) {
|
|
916
|
+
return (s.length > 0 && s.charAt(s.length - 1) === "\n") ? s.slice(0, s.length - 1) : s;
|
|
917
|
+
}
|
|
918
|
+
|
|
908
919
|
function _cmsEmbedded(kind, el, pad) {
|
|
909
920
|
if (el.tagClass === "universal") {
|
|
910
921
|
try {
|
|
911
922
|
var sub = (kind === "CRL") ? crlReport(el.bytes) : certificate(el.bytes);
|
|
912
|
-
return pad + kind + ":\n" + sub
|
|
923
|
+
return pad + kind + ":\n" + _stripTrailingNewline(sub).split("\n").map(function (l) { return pad + " " + l; }).join("\n");
|
|
913
924
|
} catch (_e) { /* fall through to the summary */ }
|
|
914
925
|
}
|
|
915
926
|
return pad + kind + " [" + el.tagClass + " " + el.tagNumber + "] (" + el.bytes.length + " bytes)";
|
package/lib/key.js
CHANGED
|
@@ -441,7 +441,7 @@ function _inferAlgorithm(detected) {
|
|
|
441
441
|
function _importUsages(name, isPublic) {
|
|
442
442
|
var n = String(name == null ? "" : name).toUpperCase();
|
|
443
443
|
if (n === "X25519" || n === "X448" || n === "ECDH") return isPublic ? [] : ["deriveBits", "deriveKey"];
|
|
444
|
-
if (
|
|
444
|
+
if (n.indexOf("ML-KEM") === 0) return isPublic ? ["encapsulateBits"] : ["decapsulateBits"];
|
|
445
445
|
if (n === "RSA-OAEP") return isPublic ? ["encrypt"] : ["decrypt"];
|
|
446
446
|
return isPublic ? ["verify"] : ["sign"]; // signing default (Ed / ECDSA / RSA-* sign / ML-DSA / SLH-DSA)
|
|
447
447
|
}
|
|
@@ -449,7 +449,7 @@ function _importUsages(name, isPublic) {
|
|
|
449
449
|
function _generateUsages(name) {
|
|
450
450
|
var n = String(name == null ? "" : name).toUpperCase();
|
|
451
451
|
if (n === "X25519" || n === "X448" || n === "ECDH") return ["deriveBits", "deriveKey"];
|
|
452
|
-
if (
|
|
452
|
+
if (n.indexOf("ML-KEM") === 0) return ["encapsulateBits", "decapsulateBits"];
|
|
453
453
|
if (n === "RSA-OAEP") return ["encrypt", "decrypt"];
|
|
454
454
|
return ["sign", "verify"];
|
|
455
455
|
}
|
package/lib/lint.js
CHANGED
|
@@ -97,7 +97,7 @@ function _finding(rule, detail) {
|
|
|
97
97
|
// would be the check computed and thrown away. Never throws: _ingest's contract is to return a
|
|
98
98
|
// finding for bad DATA and reserve throws for a wrong-TYPE argument.
|
|
99
99
|
function _derivedCert(o) {
|
|
100
|
-
if (!o || typeof o !== "object" ||
|
|
100
|
+
if (!o || typeof o !== "object" || guard.bytes.isByteSource(o)) return null;
|
|
101
101
|
try {
|
|
102
102
|
var p = guard.parsed.acceptDerived(o, "certificate", x509.parse, _cfg, "lint/bad-input", "the certificate");
|
|
103
103
|
return guard.parsed.isCert(p) ? p : null;
|