@blamejs/pki 0.8.59 → 0.8.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/lib/acme.js +15 -7
- package/lib/alt-sig.js +3 -1
- package/lib/asn1-der.js +82 -34
- package/lib/attrcert-sign.js +19 -16
- package/lib/byte-writer.js +63 -13
- package/lib/cbor-det.js +9 -3
- package/lib/cmc-build.js +12 -9
- package/lib/cmp-build.js +35 -33
- package/lib/cmp-session.js +19 -9
- package/lib/cms-decrypt.js +22 -8
- package/lib/cms-encrypt.js +23 -17
- package/lib/cms-sign.js +65 -43
- package/lib/cms-verify.js +8 -4
- package/lib/crl-sign.js +24 -17
- package/lib/csr-sign.js +5 -2
- package/lib/ct.js +27 -16
- package/lib/est.js +12 -9
- package/lib/guard-bytes.js +5 -1
- package/lib/guard-der.js +6 -1
- package/lib/guard-intrinsic.js +12 -4
- package/lib/guard-list.js +64 -0
- package/lib/guard-name.js +1 -1
- package/lib/hpke.js +10 -3
- package/lib/http-transport.js +12 -3
- package/lib/inspect.js +10 -2
- package/lib/jose.js +3 -1
- package/lib/lint.js +236 -152
- package/lib/mime.js +9 -5
- package/lib/ocsp.js +25 -22
- package/lib/path-validate.js +43 -13
- package/lib/pbes2.js +6 -2
- package/lib/pkcs11-uri.js +3 -1
- package/lib/pkcs12-build.js +14 -11
- package/lib/pki-build.js +14 -10
- package/lib/possession.js +3 -1
- package/lib/rc2.js +6 -2
- package/lib/revocation-fetch.js +78 -19
- package/lib/scep.js +14 -6
- package/lib/schema-attrcert.js +4 -2
- package/lib/schema-c509.js +94 -78
- package/lib/schema-cmc.js +5 -2
- package/lib/schema-cmp.js +6 -3
- package/lib/schema-cms.js +7 -4
- package/lib/schema-crl.js +1 -1
- package/lib/schema-crmf.js +1 -1
- package/lib/schema-csr.js +3 -3
- package/lib/schema-engine.js +36 -18
- package/lib/schema-pkcs1.js +7 -3
- package/lib/schema-pkcs12.js +9 -6
- package/lib/schema-pkix.js +19 -3
- package/lib/schema-sec1.js +4 -1
- package/lib/schema-tsp.js +4 -1
- package/lib/shbs.js +7 -3
- package/lib/smime.js +18 -14
- package/lib/tlog.js +17 -11
- package/lib/tuf.js +3 -1
- package/lib/validator-keydesc.js +4 -1
- package/lib/validator-tls.js +7 -2
- package/lib/webcrypto.js +9 -3
- package/lib/x509-sign.js +20 -15
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/revocation-fetch.js
CHANGED
|
@@ -14,10 +14,19 @@ var ocsp = require("./ocsp");
|
|
|
14
14
|
var crlSchema = require("./schema-crl");
|
|
15
15
|
var crlVerify = require("./crl-verify");
|
|
16
16
|
var httpTransport = require("./http-transport");
|
|
17
|
-
var mime = require("./mime");
|
|
18
17
|
var pathValidate = require("./path-validate");
|
|
19
18
|
var ocspSchema = require("./schema-ocsp");
|
|
20
19
|
|
|
20
|
+
/** @internal Taken when the module loads, so a replaced prototype cannot decide what a host, a media
|
|
21
|
+
* type or a reason string reads as at the moment one is used. `isIP` is taken for the same reason and
|
|
22
|
+
* decides more: read off the module object at call time, a replacement answering 0 made every
|
|
23
|
+
* address literal look like a name, so the blocked-address check was skipped and a distribution point
|
|
24
|
+
* naming a loopback or link-local address was fetched. */
|
|
25
|
+
var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
26
|
+
var _strSlice = intrinsic.uncurry(String.prototype.slice);
|
|
27
|
+
var _isIP = net.isIP;
|
|
28
|
+
var _isBlockedIp = httpTransport.isBlockedIp;
|
|
29
|
+
|
|
21
30
|
var PathError = frameworkError.PathError;
|
|
22
31
|
function E(code, message, cause) { return new PathError(code, message, cause); }
|
|
23
32
|
|
|
@@ -64,7 +73,12 @@ var TRANSPORT_SHAPE = "(request) => Promise<{ status, headers, body }>";
|
|
|
64
73
|
* status, so the difference lives in the reason, and the refusal is recorded as a skip rather than
|
|
65
74
|
* swallowed. */
|
|
66
75
|
function _softDecode(holder, extOid, skips) {
|
|
67
|
-
|
|
76
|
+
/** @internal Own reads. A certificate or CRL carrying no extensions has no own slot for either
|
|
77
|
+
* name, so the read answered from `Object.prototype`, and an inherited getter supplied a list whose
|
|
78
|
+
* entries this then decoded and reported on. */
|
|
79
|
+
var list = intrinsic.hasOwn(holder, "extensions") ? holder.extensions
|
|
80
|
+
: intrinsic.hasOwn(holder, "crlExtensions") ? holder.crlExtensions : [];
|
|
81
|
+
if (!intrinsic.isArray(list)) list = [];
|
|
68
82
|
var found = guard.list.anyMatches(list, function (e) { return !!e && e.oid === extOid; })
|
|
69
83
|
? _firstWithOid(list, extOid) : null;
|
|
70
84
|
if (found === null) return null;
|
|
@@ -175,9 +189,10 @@ function _acceptableUrl(value, net_) {
|
|
|
175
189
|
var scheme = _urlProtocol(u);
|
|
176
190
|
if (scheme !== "https:" && !(net_.allowHttp && scheme === "http:")) return _no("is not one this client fetches");
|
|
177
191
|
var host = _urlHostname(u);
|
|
178
|
-
|
|
179
|
-
if (
|
|
180
|
-
|
|
192
|
+
/** @internal 0x5b and 0x5d are `[` and `]`, which bracket an IPv6 literal in an authority. */
|
|
193
|
+
if (_charCodeAt(host, 0) === 0x5b && _charCodeAt(host, host.length - 1) === 0x5d) host = _strSlice(host, 1, -1);
|
|
194
|
+
if (_isIP(host) !== 0) {
|
|
195
|
+
if (_isBlockedIp(host)) return _no("is not one this client fetches");
|
|
181
196
|
} else if (!net_.guardsAddresses) {
|
|
182
197
|
return _no("names a host whose resolved address opts.transport does not declare it filters");
|
|
183
198
|
}
|
|
@@ -285,27 +300,71 @@ async function _fetchOne(state, req, what) {
|
|
|
285
300
|
allowPlaintextHttp: state.allowPlaintextHttp }],
|
|
286
301
|
E, "path/bad-input", "opts.transport"),
|
|
287
302
|
timeout, E, "path/revocation-fetch-failed", what);
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
303
|
+
/** @internal The response read as a record of its OWN fields. The transport contract is a plain
|
|
304
|
+
* `{ status, headers, body, tls }`, and a transport can hand back an object carrying none of them:
|
|
305
|
+
* each read then answered from `Object.prototype`, so an inherited status of 200 with an inherited
|
|
306
|
+
* body and headers was accepted as a revocation response. The status is reported through the safe
|
|
307
|
+
* formatter rather than concatenated, since concatenating it ran the value's own conversion. */
|
|
308
|
+
var out = guard.identifier.ownOptions(res);
|
|
309
|
+
if (out.status !== 200) {
|
|
310
|
+
throw E("path/revocation-fetch-failed", what + " returned HTTP " + guard.text.showValue(out.status) +
|
|
311
|
+
" (only 200 carries a revocation object; no redirect is followed)");
|
|
312
|
+
}
|
|
313
|
+
/** @internal A byte source, a primitive string, or absent. Converting anything else through
|
|
314
|
+
* `String` ran the value's OWN `toString`, so a transport handing back an object decided the bytes
|
|
315
|
+
* a CRL or an OCSP response is then parsed from, and capturing `String` does not close that: the
|
|
316
|
+
* capture fixes which function runs, not which hook it calls on its argument. */
|
|
317
|
+
var raw = out.body;
|
|
318
|
+
var body;
|
|
319
|
+
if (guard.bytes.isByteSource(raw)) {
|
|
320
|
+
body = guard.bytes.source(raw, PathError, "path/revocation-fetch-failed", "the revocation response body");
|
|
321
|
+
} else if (raw === null || raw === undefined) {
|
|
322
|
+
body = intrinsic.bufferFrom("", "latin1");
|
|
323
|
+
} else if (typeof raw === "string") {
|
|
324
|
+
body = intrinsic.bufferFrom(raw, "latin1");
|
|
325
|
+
} else {
|
|
326
|
+
throw E("path/revocation-fetch-failed", what + " returned a body that is neither bytes nor a " +
|
|
327
|
+
"string, so what it holds would be decided by converting it");
|
|
291
328
|
}
|
|
292
|
-
var body = guard.bytes.isByteSource(res.body)
|
|
293
|
-
? guard.bytes.source(res.body, PathError, "path/revocation-fetch-failed", "the revocation response body")
|
|
294
|
-
: Buffer.from(res.body == null ? "" : String(res.body), "latin1");
|
|
295
329
|
if (body.length === 0) throw E("path/revocation-fetch-failed", what + " returned an empty body");
|
|
296
330
|
if (body.length > state.maxResponseBytes) {
|
|
297
331
|
throw E("path/revocation-fetch-failed", what + " returned " + body.length + " bytes, over the " + state.maxResponseBytes + "-byte cap");
|
|
298
332
|
}
|
|
299
|
-
return { body: body, contentType: _contentType(
|
|
333
|
+
return { body: body, contentType: _contentType(out.headers) };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** @internal 0x20 space, 0x09 tab, 0x0a line feed, 0x0d carriage return. */
|
|
337
|
+
function _isAsciiSpace(code) {
|
|
338
|
+
return code === 0x20 || code === 0x09 || code === 0x0a || code === 0x0d;
|
|
300
339
|
}
|
|
301
340
|
|
|
302
341
|
function _contentType(headers) {
|
|
303
|
-
|
|
342
|
+
/** @internal Snapshotted to its own fields ONCE, then read from the snapshot. Enumerated and then
|
|
343
|
+
* re-read, the two reads could disagree: a headers proxy deleted its own `content-type` during the
|
|
344
|
+
* enumeration, so the read that followed answered from the prototype and named a media type the
|
|
345
|
+
* headers no longer carried. */
|
|
346
|
+
var h = guard.identifier.ownOptions(headers);
|
|
304
347
|
var keys = guard.identifier.optionNames(h);
|
|
305
348
|
for (var i = 0; i < keys.length; i++) {
|
|
306
|
-
if (keys[i]
|
|
307
|
-
|
|
308
|
-
|
|
349
|
+
if (intrinsic.toLowerCase(keys[i]) !== "content-type") continue;
|
|
350
|
+
/** @internal Only a primitive string. Converted through `String`, a header holding an object ran
|
|
351
|
+
* that object's own `toString` and named the media type from it. An absent media type never
|
|
352
|
+
* selects a parser, so anything else is reported as absent. */
|
|
353
|
+
var value = h[keys[i]];
|
|
354
|
+
if (typeof value !== "string") return null;
|
|
355
|
+
/** @internal The media type is the span before the first `;`, with surrounding ASCII space
|
|
356
|
+
* dropped and the case folded. `mime.parse` reads a whole MIME ENTITY out of BYTES, so handing
|
|
357
|
+
* it this header value threw on every response and the catch reported EVERY media type as
|
|
358
|
+
* absent, which left the notes that compare it unable to fire at all. 0x3b is `;`. */
|
|
359
|
+
var end = value.length;
|
|
360
|
+
for (var c = 0; c < value.length; c++) {
|
|
361
|
+
if (_charCodeAt(value, c) === 0x3b) { end = c; break; }
|
|
362
|
+
}
|
|
363
|
+
var start = 0;
|
|
364
|
+
while (start < end && _isAsciiSpace(_charCodeAt(value, start))) start += 1;
|
|
365
|
+
while (end > start && _isAsciiSpace(_charCodeAt(value, end - 1))) end -= 1;
|
|
366
|
+
if (end === start) return null;
|
|
367
|
+
return intrinsic.toLowerCase(_strSlice(value, start, end));
|
|
309
368
|
}
|
|
310
369
|
return null;
|
|
311
370
|
}
|
|
@@ -771,9 +830,9 @@ function _unknown(reason, notes, skips) {
|
|
|
771
830
|
* second error code. */
|
|
772
831
|
function _reason(head, notes, skips) {
|
|
773
832
|
var parts = [head];
|
|
774
|
-
if (skips.length) _append(parts, "skipped: " +
|
|
775
|
-
if (notes.length) _append(parts, "noted: " +
|
|
776
|
-
return
|
|
833
|
+
if (skips.length) _append(parts, "skipped: " + intrinsic.join(skips, "; "));
|
|
834
|
+
if (notes.length) _append(parts, "noted: " + intrinsic.join(notes, "; "));
|
|
835
|
+
return intrinsic.join(parts, " | ");
|
|
777
836
|
}
|
|
778
837
|
|
|
779
838
|
module.exports = { fetchingChecker: fetchingChecker };
|
package/lib/scep.js
CHANGED
|
@@ -38,6 +38,11 @@
|
|
|
38
38
|
var frameworkError = require("./framework-error");
|
|
39
39
|
var ScepError = frameworkError.ScepError;
|
|
40
40
|
var intrinsic = require("./guard-intrinsic");
|
|
41
|
+
/** @internal The append every list here is built with. `push` STORES at the index, and a store walks
|
|
42
|
+
* the prototype chain for a setter, so an accessor inherited at an array index receives each element
|
|
43
|
+
* as it is appended and the index reads back whatever its getter answers. A CertRep's attribute list
|
|
44
|
+
* is signed, so a discarded failInfo is a FAILURE response signed with other failure semantics. */
|
|
45
|
+
var _push = intrinsic.append;
|
|
41
46
|
/** @internal The URL parser, taken at load: what it returns names the host an operation is sent to,
|
|
42
47
|
* and a redirect is resolved against the current operation URL with it. The conversion the parser is
|
|
43
48
|
* handed is taken with it, since a replacement answers with a host of its own. */
|
|
@@ -136,13 +141,16 @@ function _readIssuancePayload(der) {
|
|
|
136
141
|
if (rc[i].tagClass !== "universal") throw _err("scep/bad-certificate", "a SUCCESS CertRep certificates field carried a tagged CertificateChoices alternative, not a plain X.509 certificate (RFC 8894 sec. 3.4)");
|
|
137
142
|
try { schemaX509.parse(rc[i].bytes); }
|
|
138
143
|
catch (e) { throw _err("scep/bad-certificate", "a SUCCESS CertRep carried a non-certificate in its certificates field", e); }
|
|
139
|
-
|
|
144
|
+
/** @internal Appended by defining the index. A store consults a setter inherited at the index,
|
|
145
|
+
* which would take the bytes the parse above just accepted and leave the list reading back
|
|
146
|
+
* whatever the getter answers, so the certificate returned is not the one that was checked. */
|
|
147
|
+
intrinsic.append(certs, rc[i].bytes);
|
|
140
148
|
}
|
|
141
149
|
for (i = 0; i < rl.length; i++) {
|
|
142
150
|
if (rl[i].tagClass !== "universal") throw _err("scep/bad-crl", "a SUCCESS CertRep crls field carried a tagged RevocationInfoChoice alternative, not a plain X.509 CRL (RFC 5652 sec. 10.2.1)");
|
|
143
151
|
try { schemaCrl.parse(rl[i].bytes); }
|
|
144
152
|
catch (e) { throw _err("scep/bad-crl", "a SUCCESS CertRep carried a non-CRL in its crls field", e); }
|
|
145
|
-
crls
|
|
153
|
+
intrinsic.append(crls, rl[i].bytes);
|
|
146
154
|
}
|
|
147
155
|
if (certs.length === 0 && crls.length === 0) {
|
|
148
156
|
throw _err("scep/empty-response", "a SUCCESS CertRep messageData carried neither a certificate nor a CRL (RFC 8894 sec. 3.3.2)");
|
|
@@ -378,8 +386,8 @@ async function _buildCertRep(spec) {
|
|
|
378
386
|
if (spec.pkiStatus === "FAILURE") {
|
|
379
387
|
var failCode = FAIL_INFO[guard.text.keyOf(spec.failInfo)];
|
|
380
388
|
if (failCode == null) throw _err("scep/bad-input", "a FAILURE CertRep must carry a failInfo of badAlg / badMessageCheck / badRequest / badTime / badCertId (RFC 8894 sec. 3.2.1.4), got " + _showValue(spec.failInfo));
|
|
381
|
-
attrs
|
|
382
|
-
if (spec.failInfoText != null) attrs
|
|
389
|
+
_push(attrs,_attr("scepFailInfo", b.printable(failCode)));
|
|
390
|
+
if (spec.failInfoText != null) _push(attrs,_attr("scepFailInfoText", b.utf8(String(spec.failInfoText))));
|
|
383
391
|
}
|
|
384
392
|
}
|
|
385
393
|
var signOpts = { additionalSignedAttributes: attrs };
|
|
@@ -794,8 +802,8 @@ var CAP_CANON = Object.assign(Object.create(null), { AES: "AES", DES3: "DES3", G
|
|
|
794
802
|
RENEWAL: "Renewal", "SHA-1": "SHA-1", "SHA-256": "SHA-256", "SHA-512": "SHA-512", SCEPSTANDARD: "SCEPStandard" });
|
|
795
803
|
function _splitLines(s) {
|
|
796
804
|
var out = [], start = 0, i, n = s.length;
|
|
797
|
-
for (i = 0; i < n; i++) if (s.charCodeAt(i) === 0x0a) { out
|
|
798
|
-
out
|
|
805
|
+
for (i = 0; i < n; i++) if (s.charCodeAt(i) === 0x0a) { _push(out,s.slice(start, i)); start = i + 1; }
|
|
806
|
+
_push(out,s.slice(start));
|
|
799
807
|
return out;
|
|
800
808
|
}
|
|
801
809
|
|
package/lib/schema-attrcert.js
CHANGED
|
@@ -479,13 +479,15 @@ function _decodeClassList(node, C) {
|
|
|
479
479
|
var bs;
|
|
480
480
|
try { bs = asn1.read.bitString(node); } catch (e) { throw NS.E(C, "Clearance classList must be a BIT STRING", e); }
|
|
481
481
|
schema.assertMinimalNamedBits(bs.unusedBits, bs.bytes, function (m) { throw NS.E(C, m); });
|
|
482
|
+
/** @internal Both lists are APPENDED to by defining the index: a store at an index an array has no
|
|
483
|
+
* own property at walks the prototype chain for a setter, which takes the element being appended. */
|
|
482
484
|
// allow:registry-table-inherits-object-prototype -- the classList record an operator reads by field name, whose keys are the fixed _CLASSLIST_NAMES; the omitted-classList default at the foot of this file builds the same shape
|
|
483
485
|
var names = [], flags = {}, reserved = [];
|
|
484
486
|
var total = bs.bytes.length * 8 - bs.unusedBits;
|
|
485
487
|
for (var bit = 0; bit < total; bit++) {
|
|
486
488
|
if ((bs.bytes[bit >> 3] & (0x80 >> (bit & 7))) === 0) continue;
|
|
487
|
-
if (bit < _CLASSLIST_NAMES.length) {
|
|
488
|
-
else
|
|
489
|
+
if (bit < _CLASSLIST_NAMES.length) { intrinsic.append(names, _CLASSLIST_NAMES[bit]); flags[_CLASSLIST_NAMES[bit]] = true; }
|
|
490
|
+
else intrinsic.append(reserved, bit);
|
|
489
491
|
}
|
|
490
492
|
return { names: names, flags: flags, reservedBits: reserved };
|
|
491
493
|
}
|