@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.
Files changed (63) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/lib/acme.js +15 -7
  3. package/lib/alt-sig.js +3 -1
  4. package/lib/asn1-der.js +82 -34
  5. package/lib/attrcert-sign.js +19 -16
  6. package/lib/byte-writer.js +63 -13
  7. package/lib/cbor-det.js +9 -3
  8. package/lib/cmc-build.js +12 -9
  9. package/lib/cmp-build.js +35 -33
  10. package/lib/cmp-session.js +19 -9
  11. package/lib/cms-decrypt.js +22 -8
  12. package/lib/cms-encrypt.js +23 -17
  13. package/lib/cms-sign.js +65 -43
  14. package/lib/cms-verify.js +8 -4
  15. package/lib/crl-sign.js +24 -17
  16. package/lib/csr-sign.js +5 -2
  17. package/lib/ct.js +27 -16
  18. package/lib/est.js +12 -9
  19. package/lib/guard-bytes.js +5 -1
  20. package/lib/guard-der.js +6 -1
  21. package/lib/guard-intrinsic.js +12 -4
  22. package/lib/guard-list.js +64 -0
  23. package/lib/guard-name.js +1 -1
  24. package/lib/hpke.js +10 -3
  25. package/lib/http-transport.js +12 -3
  26. package/lib/inspect.js +10 -2
  27. package/lib/jose.js +3 -1
  28. package/lib/lint.js +236 -152
  29. package/lib/mime.js +9 -5
  30. package/lib/ocsp.js +25 -22
  31. package/lib/path-validate.js +43 -13
  32. package/lib/pbes2.js +6 -2
  33. package/lib/pkcs11-uri.js +3 -1
  34. package/lib/pkcs12-build.js +14 -11
  35. package/lib/pki-build.js +14 -10
  36. package/lib/possession.js +3 -1
  37. package/lib/rc2.js +6 -2
  38. package/lib/revocation-fetch.js +78 -19
  39. package/lib/scep.js +14 -6
  40. package/lib/schema-attrcert.js +4 -2
  41. package/lib/schema-c509.js +94 -78
  42. package/lib/schema-cmc.js +5 -2
  43. package/lib/schema-cmp.js +6 -3
  44. package/lib/schema-cms.js +7 -4
  45. package/lib/schema-crl.js +1 -1
  46. package/lib/schema-crmf.js +1 -1
  47. package/lib/schema-csr.js +3 -3
  48. package/lib/schema-engine.js +36 -18
  49. package/lib/schema-pkcs1.js +7 -3
  50. package/lib/schema-pkcs12.js +9 -6
  51. package/lib/schema-pkix.js +19 -3
  52. package/lib/schema-sec1.js +4 -1
  53. package/lib/schema-tsp.js +4 -1
  54. package/lib/shbs.js +7 -3
  55. package/lib/smime.js +18 -14
  56. package/lib/tlog.js +17 -11
  57. package/lib/tuf.js +3 -1
  58. package/lib/validator-keydesc.js +4 -1
  59. package/lib/validator-tls.js +7 -2
  60. package/lib/webcrypto.js +9 -3
  61. package/lib/x509-sign.js +20 -15
  62. package/package.json +1 -1
  63. package/sbom.cdx.json +6 -6
@@ -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
- var list = holder.extensions || holder.crlExtensions || [];
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
- if (host.charAt(0) === "[" && host.charAt(host.length - 1) === "]") host = host.slice(1, -1);
179
- if (net.isIP(host) !== 0) {
180
- if (httpTransport.isBlockedIp(host)) return _no("is not one this client fetches");
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
- res = res || {};
289
- if (res.status !== 200) {
290
- throw E("path/revocation-fetch-failed", what + " returned HTTP " + res.status + " (only 200 carries a revocation object; no redirect is followed)");
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(res.headers) };
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
- var h = headers || {};
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].toLowerCase() !== "content-type") continue;
307
- try { return mime.parse(String(h[keys[i]])).type; }
308
- catch (_e) { return null; } // allow:swallow-unverified an unreadable media type is reported as absent, and it never selects a parser
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: " + skips.join("; "));
775
- if (notes.length) _append(parts, "noted: " + notes.join("; "));
776
- return parts.join(" | ");
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
- certs[i] = rc[i].bytes;
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[i] = rl[i].bytes;
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.push(_attr("scepFailInfo", b.printable(failCode)));
382
- if (spec.failInfoText != null) attrs.push(_attr("scepFailInfoText", b.utf8(String(spec.failInfoText))));
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.push(s.slice(start, i)); start = i + 1; }
798
- out.push(s.slice(start));
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
 
@@ -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) { names.push(_CLASSLIST_NAMES[bit]); flags[_CLASSLIST_NAMES[bit]] = true; }
488
- else reserved.push(bit);
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
  }