@blamejs/pki 0.5.7 → 0.5.8

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 (104) hide show
  1. package/CHANGELOG.md +405 -386
  2. package/MIGRATING.md +43 -0
  3. package/README.md +12 -12
  4. package/lib/acme.js +31 -31
  5. package/lib/asn1-der.js +10 -10
  6. package/lib/attrcert-sign.js +19 -19
  7. package/lib/byte-reader.js +6 -6
  8. package/lib/byte-writer.js +5 -5
  9. package/lib/cbor-det.js +27 -24
  10. package/lib/cmc-build.js +97 -92
  11. package/lib/cmc-verify.js +106 -30
  12. package/lib/cmp-build.js +25 -25
  13. package/lib/cmp-session.js +70 -70
  14. package/lib/cmp-verify.js +71 -57
  15. package/lib/cms-compress.js +7 -7
  16. package/lib/cms-decrypt.js +90 -72
  17. package/lib/cms-encrypt.js +32 -32
  18. package/lib/cms-sign.js +74 -55
  19. package/lib/cms-verify.js +97 -75
  20. package/lib/composite-sig.js +13 -13
  21. package/lib/constants.js +4 -4
  22. package/lib/crl-sign.js +22 -22
  23. package/lib/crl-verify.js +7 -6
  24. package/lib/crmf-sign.js +14 -14
  25. package/lib/csr-sign.js +8 -8
  26. package/lib/ct.js +37 -37
  27. package/lib/edwards-point.js +7 -7
  28. package/lib/est.js +98 -55
  29. package/lib/framework-error.js +5 -5
  30. package/lib/guard-all.js +3 -3
  31. package/lib/guard-async.js +4 -4
  32. package/lib/guard-bytes.js +79 -79
  33. package/lib/guard-compress.js +17 -17
  34. package/lib/guard-crypto.js +1 -1
  35. package/lib/guard-encoding.js +15 -15
  36. package/lib/guard-header.js +3 -3
  37. package/lib/guard-identifier.js +16 -16
  38. package/lib/guard-json.js +15 -15
  39. package/lib/guard-limits.js +7 -7
  40. package/lib/guard-name.js +81 -16
  41. package/lib/guard-parsed.js +80 -80
  42. package/lib/guard-range.js +19 -19
  43. package/lib/guard-secret.js +11 -10
  44. package/lib/guard-text.js +6 -6
  45. package/lib/guard-time.js +10 -10
  46. package/lib/hpke.js +18 -17
  47. package/lib/http-digest.js +35 -35
  48. package/lib/http-retry-after.js +13 -13
  49. package/lib/http-transport.js +20 -19
  50. package/lib/inspect.js +53 -53
  51. package/lib/ip-utils.js +2 -2
  52. package/lib/jose.js +13 -13
  53. package/lib/key.js +16 -16
  54. package/lib/lint.js +51 -51
  55. package/lib/merkle.js +51 -36
  56. package/lib/mime.js +18 -18
  57. package/lib/ocsp-verify.js +10 -10
  58. package/lib/ocsp.js +13 -13
  59. package/lib/oid.js +29 -29
  60. package/lib/path-validate.js +114 -113
  61. package/lib/pbes2.js +16 -16
  62. package/lib/pkcs12-build.js +53 -53
  63. package/lib/pki-build.js +21 -19
  64. package/lib/rc2.js +1 -1
  65. package/lib/rfc3339.js +5 -5
  66. package/lib/schema-all.js +31 -31
  67. package/lib/schema-attrcert.js +12 -12
  68. package/lib/schema-c509.js +144 -142
  69. package/lib/schema-cmc.js +58 -58
  70. package/lib/schema-cmp.js +43 -43
  71. package/lib/schema-cms.js +45 -45
  72. package/lib/schema-crl.js +7 -7
  73. package/lib/schema-crmf.js +28 -28
  74. package/lib/schema-csr.js +12 -12
  75. package/lib/schema-csrattrs.js +16 -16
  76. package/lib/schema-engine.js +18 -18
  77. package/lib/schema-ocsp.js +15 -15
  78. package/lib/schema-pkcs12.js +20 -20
  79. package/lib/schema-pkcs8.js +2 -2
  80. package/lib/schema-pkix.js +131 -126
  81. package/lib/schema-smime.js +19 -19
  82. package/lib/schema-tsp.js +12 -12
  83. package/lib/schema-x509.js +3 -3
  84. package/lib/shbs.js +18 -18
  85. package/lib/sign-scheme.js +13 -13
  86. package/lib/sigstore.js +10 -11
  87. package/lib/sleep.js +1 -1
  88. package/lib/smime.js +308 -96
  89. package/lib/tls-cert-compress.js +18 -18
  90. package/lib/trust.js +27 -27
  91. package/lib/tsp-sign.js +17 -17
  92. package/lib/validator-all.js +1 -1
  93. package/lib/validator-attcert.js +1 -1
  94. package/lib/validator-cose.js +43 -44
  95. package/lib/validator-keydesc.js +3 -3
  96. package/lib/validator-sig.js +13 -13
  97. package/lib/validator-tls.js +11 -11
  98. package/lib/validator-tpm.js +20 -19
  99. package/lib/webauthn-mds.js +66 -66
  100. package/lib/webauthn.js +33 -33
  101. package/lib/webcrypto.js +15 -15
  102. package/lib/x509-sign.js +13 -13
  103. package/package.json +3 -2
  104. package/sbom.cdx.json +6 -6
package/lib/inspect.js CHANGED
@@ -5,19 +5,19 @@
5
5
  * @module pki.inspect
6
6
  * @nav Tooling
7
7
  * @title Inspect
8
- * @intro Human-readable inspection of a parsed certificate -- the pure-JS
8
+ * @intro Human-readable inspection of a parsed certificate: the pure-JS
9
9
  * equivalent of `openssl x509 -text`. `certificate(input)` ingests a PEM string,
10
10
  * a DER Buffer, or an already-parsed certificate and returns a familiar
11
11
  * OpenSSL-style report: version, serial, signature algorithm, the issuer and
12
12
  * subject distinguished names, the validity window, the public-key details
13
13
  * (curve or modulus size plus the raw point/modulus), every decoded extension
14
14
  * with its critical flag, and the signature. It renders purely from the toolkit's
15
- * own strict parser and two-way OID registry -- no OpenSSL dependency, and no
16
- * drift-prone second naming table -- so it names extension and algorithm OIDs an
17
- * OpenSSL build shows only as raw bytes. The format is stable and OpenSSL-*familiar*
18
- * rather than byte-identical to any one OpenSSL version (those disagree across
15
+ * own strict parser and two-way OID registry, with no OpenSSL dependency and no
16
+ * drift-prone second naming table, so it names extension and algorithm OIDs an
17
+ * OpenSSL build shows only as raw bytes. The format is stable and OpenSSL-*familiar*,
18
+ * never byte-identical to any one OpenSSL version (those disagree across
19
19
  * releases). Rendering is best-effort: a malformed extension falls back to a hex
20
- * dump rather than throwing.
20
+ * dump and does not throw.
21
21
  * @spec RFC 5280
22
22
  * @card Read a certificate like `openssl x509 -text`, in pure JS.
23
23
  */
@@ -70,10 +70,10 @@ function _hexColon(buf, opts) {
70
70
  return lines.join("\n");
71
71
  }
72
72
 
73
- // Coverage residual -- two _hexColon default arms are unreachable through the public API:
74
- // * `opts = opts || {}` -- all call sites pass an explicit opts object literal, so the
73
+ // Coverage residual: two _hexColon default arms are unreachable through the public API.
74
+ // * `opts = opts || {}`. All call sites pass an explicit opts object literal, so the
75
75
  // `|| {}` default never fires.
76
- // * `" ".repeat(opts.indent || 0)` -- every wrap-mode caller passes a positive indent
76
+ // * `" ".repeat(opts.indent || 0)`. Every wrap-mode caller passes a positive indent
77
77
  // (pad.length + 8 >= 8, or inner.length == 16), so `opts.indent` is never falsy.
78
78
 
79
79
  // Control-byte neutralization for a GeneralName string value routes through the
@@ -83,7 +83,7 @@ var _clean = guard.name.escapeControlBytes;
83
83
 
84
84
  // The DN display string. pki.schema.pkix already assembles a fully RFC 4514-escaped
85
85
  // dn (short names from pki.C.NAMES.DN_SHORT, values escaped via guard.name.escapeDnValue,
86
- // with the '#'-hex / leading-'\' sentinel handled), so reuse it rather than re-escaping
86
+ // with the '#'-hex / leading-'\' sentinel handled), so reuse it and never re-escape
87
87
  // the already-escaped values (which would double-escape a leading '#' / '\').
88
88
  function _dnString(name) { return (name && name.dn) || ""; }
89
89
 
@@ -108,7 +108,7 @@ function _serial(cert, indent) {
108
108
  if (hex.length % 2) hex = "0" + hex;
109
109
  var buf = Buffer.from(hex, "hex");
110
110
  // Strip a single DER positive-sign 00 byte (present when the value's high bit is
111
- // set) so the printed serial is the integer VALUE, matching OpenSSL -- not the
111
+ // set) so the printed serial is the integer value, matching OpenSSL, and not the
112
112
  // encoding's leading octet.
113
113
  if (buf.length > 1 && buf[0] === 0x00 && (buf[1] & 0x80)) buf = buf.subarray(1);
114
114
  // Small non-negative serials render inline as decimal (0xhex), like OpenSSL;
@@ -122,9 +122,9 @@ function _serial(cert, indent) {
122
122
 
123
123
  var CURVE_BITS = { "P-256": 256, "P-384": 384, "P-521": 521, "prime256v1": 256, "secp384r1": 384, "secp521r1": 521 };
124
124
  var NIST_NAME = NAMES.NIST_CURVE;
125
- // Every RSA-family key algorithm carries the same SPKI subjectPublicKey -- an
125
+ // Every RSA-family key algorithm carries the same SPKI subjectPublicKey, an
126
126
  // RSAPublicKey SEQUENCE { modulus, publicExponent } (RFC 4055 sec. 1.2 for
127
- // id-RSASSA-PSS / id-RSAES-OAEP) -- so all decode to modulus + exponent, not raw bytes.
127
+ // id-RSASSA-PSS / id-RSAES-OAEP), so all decode to modulus + exponent, not raw bytes.
128
128
  var RSA_KEY_ALGS = { rsaEncryption: 1, rsassaPss: 1, rsaesOaep: 1 };
129
129
  function _keyBlock(spki, pad) {
130
130
  var algName = _algName(spki.algorithm);
@@ -200,8 +200,8 @@ function _ipString(buf) {
200
200
  // RFC 4514-equivalent escaping profile, and escaping a legitimate comma in a URI
201
201
  // would misrepresent it).
202
202
  // The DN of a decoded directoryName GeneralName value. Shared so every renderer that meets this
203
- // form -- the SAN/AKI GeneralName path and the AIA/SIA accessLocation path, which carry different
204
- // decoded shapes -- prints the same DN rather than one of them falling back to a bare tag number.
203
+ // form (the SAN/AKI GeneralName path and the AIA/SIA accessLocation path, which carry different
204
+ // decoded shapes) prints the same DN, with neither falling back to a bare tag number.
205
205
  function _gnDn(value) {
206
206
  return (value && Array.isArray(value.rdns)) ? _dnString(value) : ((value && value.dn) || "");
207
207
  }
@@ -219,7 +219,7 @@ function _gn(g) {
219
219
  }
220
220
 
221
221
  // Format a GeneralName still in its raw DER TLV (a CRL distribution point leaves
222
- // its fullName entries undecoded) -- decode the context tag and render its content.
222
+ // its fullName entries undecoded): decode the context tag and render its content.
223
223
  function _gnRaw(buf) {
224
224
  if (!Buffer.isBuffer(buf)) return "";
225
225
  try {
@@ -233,26 +233,26 @@ function _gnRaw(buf) {
233
233
  } catch (_e) { return _hexColon(buf, {}); }
234
234
  }
235
235
 
236
- // Coverage residual -- the GeneralName render fallbacks are unreachable because the strict
236
+ // Coverage residual: the GeneralName render fallbacks are unreachable because the strict
237
237
  // generalName decoder and each caller already narrow the shape (the _gnRaw catch above is
238
- // separately documented):
239
- // * _ipString `if (!Buffer.isBuffer(buf)) return "";` -- every caller passes a Buffer (the
238
+ // separately documented).
239
+ // * _ipString `if (!Buffer.isBuffer(buf)) return "";`. Every caller passes a Buffer (the
240
240
  // recursion slices a Buffer, _gn guards Buffer.isBuffer(g.value), _gnRaw passes
241
241
  // asn1.decode(...).content).
242
- // * _ipString trailing `return _hexColon(buf, {});` -- generalName enforces iPAddress to
242
+ // * _ipString trailing `return _hexColon(buf, {});`. generalName enforces iPAddress to
243
243
  // 4/16 octets (8/32 for a name-constraints subtree base), so length is only ever 4/8/16/32.
244
- // * _gn `if (!g || typeof g !== "object") return "";` -- every _gn call maps a decoder-produced
244
+ // * _gn `if (!g || typeof g !== "object") return "";`. Every _gn call maps a decoder-produced
245
245
  // GeneralName object; the decoders never emit a null element.
246
- // * _gn directoryName `: ((g.value && g.value.dn) || "")` -- a decoded directoryName [4] always
246
+ // * _gn directoryName `: ((g.value && g.value.dn) || "")`. A decoded directoryName [4] always
247
247
  // carries a Name with an rdns array, so the rdns arm is always taken.
248
- // * _gn otherName `: "<unsupported>"` -- a decoded GeneralName always carries its raw bytes TLV.
249
- // * _gn `|| ("tag" + t)` -- GeneralName CHOICE tags are 0..8, all mapped in NAMES.GENERAL_NAME;
248
+ // * _gn otherName `: "<unsupported>"`. A decoded GeneralName always carries its raw bytes TLV.
249
+ // * _gn `|| ("tag" + t)`. GeneralName CHOICE tags are 0..8, all mapped in NAMES.GENERAL_NAME;
250
250
  // generalName rejects a tag outside 0..8.
251
- // * _gn `? _hexColon(g.value, {})` -- iPAddress [7] is the only choice whose decoded value is a
251
+ // * _gn `? _hexColon(g.value, {})`. iPAddress [7] is the only choice whose decoded value is a
252
252
  // Buffer, handled at the t === 7 branch before this ternary; other tags' value is string or null.
253
- // * _gn trailing `: ""` -- a decoded GeneralName always carries a bytes Buffer, so the
253
+ // * _gn trailing `: ""`. A decoded GeneralName always carries a bytes Buffer, so the
254
254
  // Buffer.isBuffer(g.bytes) arm is always taken.
255
- // * _gnRaw `if (!Buffer.isBuffer(buf)) return "";` -- the sole caller (the CRL-DP fullName loop)
255
+ // * _gnRaw `if (!Buffer.isBuffer(buf)) return "";`. The sole caller (the CRL-DP fullName loop)
256
256
  // guards Buffer.isBuffer(nm) before calling _gnRaw.
257
257
 
258
258
  // Shared value renderers reused by more than one extension key.
@@ -281,7 +281,7 @@ function _renderCrlDp(decoded, inner) {
281
281
  if (rf.length) { dpLines.push(inner + "Reasons: " + rf.join(", ")); wrote = true; }
282
282
  }
283
283
  // A DistributionPoint may carry only cRLIssuer (an indirect CRL, no
284
- // distributionPoint) -- render the issuer GeneralNames rather than dropping them.
284
+ // distributionPoint): render the issuer GeneralNames, never drop them.
285
285
  if (dp.cRLIssuer && Array.isArray(dp.cRLIssuer.names)) {
286
286
  dpLines.push(inner + "CRL Issuer:");
287
287
  dp.cRLIssuer.names.forEach(function (g) { dpLines.push(inner + " " + _gn(g)); });
@@ -296,17 +296,17 @@ function _renderCrlDp(decoded, inner) {
296
296
  // strict decoders already narrow the shape:
297
297
  // * _renderAltName `(decoded.names || [])` -- the subjectAltName/issuerAltName decoder always
298
298
  // yields a names array.
299
- // * _renderCrlDp `(decoded || [])` -- the cRLDistributionPoints/freshestCRL decoder always
299
+ // * _renderCrlDp `(decoded || [])`. The cRLDistributionPoints/freshestCRL decoder always
300
300
  // yields an array.
301
- // * _renderCrlDp `: _gn(nm)` -- distributionPointName surfaces fullName entries as raw
301
+ // * _renderCrlDp `: _gn(nm)`. distributionPointName surfaces fullName entries as raw
302
302
  // GeneralName Buffers, so Buffer.isBuffer(nm) is always true.
303
- // * _renderCrlDp `if (!wrote) ... "(distribution point)"` -- crlDistributionPoints throws unless
303
+ // * _renderCrlDp `if (!wrote) ... "(distribution point)"`. crlDistributionPoints throws unless
304
304
  // a distributionPoint (always fullName/rdn) or cRLIssuer (sets wrote) is present, so !wrote
305
305
  // never holds.
306
306
 
307
307
  // Declarative extension-value renderer registry: extension name -> (decoded, inner) ->
308
308
  // text. Data-driven dispatch (the schema family's "registry, not switch" shape) so a
309
- // new extension is a row rather than another hand-coded branch, and the set an
309
+ // new extension is a row, never another hand-coded branch, and the set an
310
310
  // operator sees rendered is visible in one place. An extension with no row here
311
311
  // hex-dumps its raw value (best-effort); each row's output is pinned by an
312
312
  // inspect.test.js conformance vector.
@@ -366,7 +366,7 @@ var EXT_RENDERERS = {
366
366
  lines.push(inner + "Policy: " + p.policyIdentifier);
367
367
  if (!Buffer.isBuffer(p.qualifiersBytes)) return;
368
368
  // Render each PolicyQualifierInfo { policyQualifierId, qualifier }: a printable
369
- // qualifier (a CPS URI is an IA5String) shows as text, else a hex dump -- never
369
+ // qualifier (a CPS URI is an IA5String) shows as text, else a hex dump, and is never
370
370
  // dropped (which would make a qualified policy look unqualified).
371
371
  try {
372
372
  (asn1.decode(p.qualifiersBytes).children || []).forEach(function (pqi) {
@@ -379,9 +379,9 @@ var EXT_RENDERERS = {
379
379
  // qualifier exists to display. Render its DisplayText members through the shared pkix
380
380
  // reader (the same one pki.lint measures), so both agree on what the notice says.
381
381
  if (qid === OID_UNOTICE) {
382
- // Render only when EVERY member decoded under its declared string type. A null text means
382
+ // Render only when every member decoded under its declared string type. A null text means
383
383
  // the value did not, and showing the members that happened to decode would present a
384
- // partial notice as a complete one -- so the whole qualifier falls through to the hex
384
+ // partial notice as a complete one, so the whole qualifier falls through to the hex
385
385
  // dump, where the operator sees the bytes the certificate actually holds.
386
386
  // A null noticeNumbers means the reference did not fully decode, and is refused for the
387
387
  // same reason as a null text: a partially decoded notice must not be shown as a whole one.
@@ -414,15 +414,15 @@ var EXT_RENDERERS = {
414
414
  authorityInfoAccess: function (decoded, inner) {
415
415
  // AccessDescription list: <accessMethod> - <accessLocation>. The method resolves to its name (caIssuers /
416
416
  // ocsp); the accessLocation is a GeneralName (a URI in the common case). An unregistered method / an
417
- // uncommon accessLocation tag falls back to the raw OID / bracketed tag rather than dropping the entry.
417
+ // uncommon accessLocation tag falls back to the raw OID / bracketed tag, and the entry is never dropped.
418
418
  var LABEL = { caIssuers: "CA Issuers", ocsp: "OCSP" };
419
419
  return (decoded || []).map(function (ad) {
420
420
  var m = null;
421
421
  try { m = oid.name(ad.accessMethod); } catch (_e) { /* allow:swallow-unverified display best-effort: an unregistered accessMethod OID falls back to the raw dotted OID below (inspection is best-effort, never a verdict) */ }
422
422
  var loc = ad.accessLocation || {}, lv;
423
423
  // The string choices (URI/DNS/email) are IA5String values already control-byte-rejected at decode by the
424
- // CVE-2009-2408 guard, so they are safe to emit directly. The iPAddress choice is a RAW 4/16-byte Buffer --
425
- // render it through _ipString (never raw), so a byte such as 0x0a cannot inject a line and spoof a field.
424
+ // CVE-2009-2408 guard, so they are safe to emit directly. The iPAddress choice is a raw 4/16-byte Buffer,
425
+ // so render it through _ipString (never raw) and a byte such as 0x0a cannot inject a line and spoof a field.
426
426
  if (loc.tag === 6) lv = "URI:" + loc.value;
427
427
  else if (loc.tag === 2) lv = "DNS:" + loc.value;
428
428
  else if (loc.tag === 1) lv = "email:" + loc.value;
@@ -477,7 +477,7 @@ var EXT_RENDERERS = {
477
477
  },
478
478
  authorityKeyIdentifier: function (decoded, inner) {
479
479
  // Any of the three fields may be present; the issuer+serial form carries no
480
- // keyIdentifier, so render whichever the decoder populated rather than claiming
480
+ // keyIdentifier, so render whichever the decoder populated, never claiming
481
481
  // "keyid:(none)" and dropping the certificate's real authority identifier.
482
482
  var akiLines = [];
483
483
  if (Buffer.isBuffer(decoded.keyIdentifier)) akiLines.push(inner + "keyid:" + _hexColon(decoded.keyIdentifier, { upper: true }));
@@ -499,29 +499,29 @@ EXT_RENDERERS.msApplicationPolicies = EXT_RENDERERS.certificatePolicies;
499
499
  // Coverage residual -- the EXT_RENDERERS entry fallbacks are unreachable because each shared
500
500
  // decoder (and asn1.read.oid) already narrows the shape before the renderer runs:
501
501
  // * extKeyUsage `catch (_e) { /* unregistered EKU OID */ }` and certificatePolicies
502
- // `catch (_e) { /* unregistered qualifier */ }` -- oid.name returns undefined (never throws)
502
+ // `catch (_e) { /* unregistered qualifier */ }`. oid.name returns undefined (never throws)
503
503
  // for a well-formed unregistered OID; it throws only on a non-dotted argument, and both OIDs
504
504
  // come from asn1.read.oid (always a valid dotted OID).
505
- // * certificatePolicies `(asn1.decode(p.qualifiersBytes).children || [])` -- asn1.decode of the
505
+ // * certificatePolicies `(asn1.decode(p.qualifiersBytes).children || [])`. asn1.decode of the
506
506
  // assertPolicyQualifiers-validated qualifiers SEQUENCE always yields a children array.
507
- // * certificatePolicies `: Buffer.alloc(0)` -- assertPolicyQualifiers requires every
507
+ // * certificatePolicies `: Buffer.alloc(0)`. assertPolicyQualifiers requires every
508
508
  // PolicyQualifierInfo to be a 2-child SEQUENCE, so pqi.children[1] is always a present node
509
509
  // carrying a bytes Buffer.
510
- // * certificatePolicies outer `catch (_e) { ... _hexColon(p.qualifiersBytes, {}) ... }` --
510
+ // * certificatePolicies outer `catch (_e) { ... _hexColon(p.qualifiersBytes, {}) ... }`.
511
511
  // certificatePolicies already validated qualifiersBytes as a SEQUENCE of 2-child PQIs each
512
512
  // leading with a valid OID, so the re-decode + asn1.read.oid cannot throw.
513
- // * policyConstraints `: inner + "(empty)"` -- policyConstraints rejects an empty SEQUENCE
513
+ // * policyConstraints `: inner + "(empty)"`. policyConstraints rejects an empty SEQUENCE
514
514
  // (>= 1 context field), so requireExplicitPolicy or inhibitPolicyMapping is non-null;
515
515
  // pc.length is never 0.
516
- // * SCT `(decoded.scts || [])` / `(decoded.unknownScts || [])` -- ct.parseSctList always returns
516
+ // * SCT `(decoded.scts || [])` / `(decoded.unknownScts || [])`. ct.parseSctList always returns
517
517
  // both arrays.
518
- // * SCT `: 0` -- every scts entry is a decoded v1 SCT with numeric version 0 (unknown-version
518
+ // * SCT `: 0`. Every scts entry is a decoded v1 SCT with numeric version 0 (unknown-version
519
519
  // SCTs go to unknownScts and are not iterated here).
520
- // * SCT `: inner + "(empty SCT list)"` -- ct.parseSctList rejects an empty list and routes every
520
+ // * SCT `: inner + "(empty SCT list)"`. ct.parseSctList rejects an empty list and routes every
521
521
  // SerializedSCT into scts/unknownScts, so at least one line is always emitted.
522
- // * subjectKeyIdentifier `: (decoded.bytes || Buffer.alloc(0))` -- the subjectKeyIdentifier
522
+ // * subjectKeyIdentifier `: (decoded.bytes || Buffer.alloc(0))`. The subjectKeyIdentifier
523
523
  // decoder returns the KeyIdentifier as a Buffer, so Buffer.isBuffer(decoded) is always true.
524
- // * authorityKeyIdentifier `: BigInt(decoded.authorityCertSerialNumber)` -- the AKI decoder reads
524
+ // * authorityKeyIdentifier `: BigInt(decoded.authorityCertSerialNumber)`. The AKI decoder reads
525
525
  // authorityCertSerialNumber via asn1.read.integerImplicit (a bigint), so typeof === "bigint"
526
526
  // is always true.
527
527
 
@@ -535,7 +535,7 @@ function _renderExtValue(ext, decoded, inner) {
535
535
  // string; a DER-wrapped character string is decoded and shown; otherwise a hex
536
536
  // dump. Never throws.
537
537
  var _STRING_TAGS = { 12: 1, 19: 1, 22: 1, 20: 1, 26: 1, 27: 1, 30: 1 }; // UTF8/Printable/IA5/Teletex/Visible/General/BMP
538
- // A value renders as text only when EVERY byte is a printable, non-control ASCII
538
+ // A value renders as text only when every byte is a printable, non-control ASCII
539
539
  // character. A control byte (a bare CR / LF / TAB, etc.) is rejected here so a
540
540
  // hostile private-extension value cannot forge or overwrite report lines in a
541
541
  // terminal or log -- such a value falls through to the hex dump instead.
@@ -573,7 +573,7 @@ function _extension(ext, pad) {
573
573
 
574
574
  // A genuine pki.schema.x509.parse result carries this whole shape. The fast path
575
575
  // accepts a pre-parsed certificate to skip re-parsing, but a bare or partial object
576
- // with only a tbsBytes property is NOT a certificate: without this check the renderer
576
+ // with only a tbsBytes property is not a certificate: without this check the renderer
577
577
  // would dereference a missing field (c.validity.notBefore, ...) and throw a raw
578
578
  // TypeError instead of the documented typed inspect/bad-input (the API's error contract).
579
579
  function _looksParsed(o) {
@@ -615,7 +615,7 @@ function _parse(input) {
615
615
  * is a PEM string, a DER Buffer, or a `pki.schema.x509.parse` result. A value that
616
616
  * is none of those throws `inspect/bad-input`; a malformed certificate throws
617
617
  * `inspect/bad-certificate`; but a malformed individual extension is rendered as a
618
- * hex dump rather than failing the whole report. Pure -- no OpenSSL dependency.
618
+ * hex dump and does not fail the whole report. Pure, with no OpenSSL dependency.
619
619
  *
620
620
  * @example
621
621
  * var pair = await pki.key.generate("Ed25519");
@@ -766,7 +766,7 @@ function _crlExtension(ext, pad) {
766
766
  * revocation date, entry extensions), and the signature. `input` is a PEM string, a
767
767
  * DER Buffer, or a `pki.schema.crl.parse` result; a non-CRL throws
768
768
  * `inspect/bad-crl`, a wrong-type input `inspect/bad-input`. A malformed individual
769
- * extension renders as hex rather than failing the report.
769
+ * extension renders as hex and does not fail the report.
770
770
  *
771
771
  * @example
772
772
  * var pair = await pki.key.generate("Ed25519");
package/lib/ip-utils.js CHANGED
@@ -10,7 +10,7 @@
10
10
  // sec. 2.2 (8 groups of 1-4 hex, one "::" run compressing a contiguous zero run) plus the
11
11
  // RFC 4291 sec. 2.5.5.2 / RFC 5952 sec. 5 IPv4-mapped + dual-stack "::ffff:1.2.3.4" tail.
12
12
  // This mirrors the vetted validation in the sibling blamejs framework (lib/ip-utils.js);
13
- // this toolkit keeps its own copy rather than take a runtime dependency (Hard rule #1).
13
+ // this toolkit keeps its own copy, since Hard rule #1 rules out a runtime dependency.
14
14
 
15
15
  // Strict RFC 791 dotted-quad: four 0-255 octets. Anchored + per-octet repeat-capped (no
16
16
  // ReDoS on unbounded input).
@@ -56,7 +56,7 @@ function expandIpv6Hex(ip) {
56
56
  // Is `s` a syntactically valid IPv4 or IPv6 textual literal?
57
57
  function isIpLiteral(s) { return isIPv4(s) || expandIpv6Hex(s) !== null; }
58
58
 
59
- // Pack an IPv4/IPv6 textual literal to its network-order octets -- 4 for IPv4, 16 for IPv6 --
59
+ // Pack an IPv4/IPv6 textual literal to its network-order octets (4 for IPv4, 16 for IPv6),
60
60
  // or null when `s` is not a valid literal. The binary inverse of the textual forms isIPv4 /
61
61
  // expandIpv6Hex validate, so a SAN/GeneralName iPAddress (RFC 5280 sec. 4.2.1.6, always a bare
62
62
  // host address of 4 or 16 octets) can be given as a string instead of a pre-packed Buffer.
package/lib/jose.js CHANGED
@@ -11,12 +11,12 @@
11
11
  * @intro
12
12
  * The JOSE message envelope (RFC 7515 JWS, RFC 7518 JWA, RFC 7638 JWK
13
13
  * thumbprint), profiled for RFC 8555 ACME but usable on its own. A JWS here
14
- * is the Flattened JSON Serialization only -- `{ protected, payload,
15
- * signature }` -- with the multi-signature `signatures` member, the
14
+ * is the Flattened JSON Serialization only, `{ protected, payload,
15
+ * signature }`, with the multi-signature `signatures` member, the
16
16
  * unprotected `header` member, and the RFC 7797 unencoded-payload `b64`
17
17
  * option all structurally forbidden. Every base64url field is decoded by a
18
- * STRICT codec (Node's `Buffer.from(s, "base64url")` accepts padding,
19
- * whitespace, and non-canonical trailing bits -- all of which are rejected
18
+ * strict codec (Node's `Buffer.from(s, "base64url")` accepts padding,
19
+ * whitespace, and non-canonical trailing bits, all of which are rejected
20
20
  * here), and every JSON document is read by a bounded reader that rejects a
21
21
  * duplicate member at any nesting depth (the parser-differential smuggling
22
22
  * class `JSON.parse` silently allows).
@@ -24,10 +24,10 @@
24
24
  * Algorithms resolve through an `alg`-keyed registry (ES256/384/512,
25
25
  * RS256/384/512, PS256/384/512, EdDSA, and the RFC 9964 ML-DSA-44/65/87 PQC
26
26
  * rows), never a switch: the registry binds `alg` to a key type and pins the
27
- * exact signature byte length BEFORE any crypto call, so `alg:"none"`, a MAC
27
+ * exact signature byte length before any crypto call, so `alg:"none"`, a MAC
28
28
  * algorithm on the outer profile, an ES256/RSA-key confusion, and a DER-vs-raw
29
- * ECDSA signature all fail closed. `sign` and `verify` are driven by ONE
30
- * declarative profile table each -- the same data drives both directions.
29
+ * ECDSA signature all fail closed. `sign` and `verify` are each driven by one
30
+ * declarative profile table, so the same data drives both directions.
31
31
  *
32
32
  * @card
33
33
  * RFC 7515 Flattened JWS sign/verify + RFC 7638 JWK thumbprints, ACME-profiled:
@@ -306,10 +306,10 @@ var PRIVATE_JWK_MEMBERS = ["d", "p", "q", "dp", "dq", "qi", "k", "priv"];
306
306
  * @spec RFC 7517, RFC 7518
307
307
  * @related pki.jose.sign, pki.jose.thumbprint
308
308
  *
309
- * Assert that a JWK is PUBLIC-ONLY before it is published (embedded in a JWS
309
+ * Assert that a JWK is public-only before it is published (embedded in a JWS
310
310
  * protected header or an ACME External Account Binding payload). A JWK carrying any
311
311
  * private member (`d`, the RSA CRT parameters `p`/`q`/`dp`/`dq`/`qi`, the symmetric
312
- * `k`, or the AKP `priv`) throws `jose/private-key-material` -- so an accidentally
312
+ * `k`, or the AKP `priv`) throws `jose/private-key-material`, so an accidentally
313
313
  * exported private JWK is never sent to a server. Returns the JWK.
314
314
  *
315
315
  * @example
@@ -334,13 +334,13 @@ function assertPublicJwk(jwk) {
334
334
  * @related pki.jose.sign, pki.jose.parseJson
335
335
  *
336
336
  * Verify a Flattened JSON JWS against a profile (`opts.profile`, default
337
- * `"acme-outer"`). Structural rules fail closed BEFORE any crypto: the
337
+ * `"acme-outer"`). Structural rules fail closed before any crypto: the
338
338
  * `signatures`/`header` members and a detached payload are rejected, the
339
339
  * protected header is validated against the profile (alg registry, nonce, url,
340
340
  * exactly-one-of jwk/kid, crit), and the signature byte length is pinned per alg.
341
341
  *
342
342
  * `opts.key` names the key the message must be signed under, and it governs: where
343
- * the profile also permits an embedded header `jwk`, the two must be the SAME key
343
+ * the profile also permits an embedded header `jwk`, the two must be the same key
344
344
  * or the message is refused with `jose/key-mismatch`. They are compared as RFC 7638
345
345
  * thumbprints, so member order and members outside the key itself cannot make equal
346
346
  * keys look different. Without `opts.key` the embedded `jwk` is used where the
@@ -545,7 +545,7 @@ var THUMBPRINT_MEMBERS = {
545
545
  * @spec RFC 7638, RFC 8037, RFC 9964
546
546
  * @related pki.jose.verify
547
547
  *
548
- * The RFC 7638 JWK SHA-256 thumbprint as base64url: the canonical JSON of ONLY
548
+ * The RFC 7638 JWK SHA-256 thumbprint as base64url: the canonical JSON of just
549
549
  * the key type's required members, lexicographically ordered, no whitespace,
550
550
  * hashed. Optional members (`alg`, `use`, `kid`) are excluded, so the same key
551
551
  * always yields the same thumbprint (the ACME key-authorization anchor).
@@ -593,7 +593,7 @@ async function thumbprint(jwk) {
593
593
  * listing it beside `RS256` is how the HMAC key-confusion class starts. `none`
594
594
  * does not exist here at all.
595
595
  *
596
- * Each call returns a fresh array of fresh rows -- the registry that drives
596
+ * Each call returns a fresh array of fresh rows. The registry that drives
597
597
  * verification is never handed out, so nothing a caller does to the result can
598
598
  * widen what a signature check accepts.
599
599
  *
package/lib/key.js CHANGED
@@ -8,7 +8,7 @@
8
8
  * @intro The key-material lifecycle: export / import a private key as PKCS#8 (`OneAsymmetricKey`,
9
9
  * RFC 5958) or a public key as SPKI (RFC 5280 sec. 4.1.2.7), encrypt / decrypt a private key under
10
10
  * RFC 8018 PBES2 (`EncryptedPrivateKeyInfo`, PBKDF2 + AES-CBC-Pad), and `generate` /
11
- * `publicFromPrivate` over every algorithm the WebCrypto engine drives -- RSA, EC, Ed25519/Ed448,
11
+ * `publicFromPrivate` over every algorithm the WebCrypto engine drives: RSA, EC, Ed25519/Ed448,
12
12
  * X25519/X448, and the FIPS post-quantum ML-DSA / ML-KEM. Unencrypted export / import DELEGATES to the
13
13
  * WebCrypto `exportKey` / `importKey` PKCS#8 / SPKI encoders (which already emit each algorithm's
14
14
  * `AlgorithmIdentifier.parameters` correctly -- RSA NULL, EC namedCurve, Ed/X ABSENT), so the wrapper
@@ -18,15 +18,15 @@
18
18
  * @card Export / import PKCS#8 and SPKI keys and encrypt a private key under RFC 8018 PBES2.
19
19
  */
20
20
  //
21
- // PBES2 is composed from lib/pbes2.js (the ONE PBKDF2 + AES-CBC home shared with pki.cms), bound to the key
21
+ // PBES2 is composed from lib/pbes2.js (the single PBKDF2 + AES-CBC home shared with pki.cms), bound to the key
22
22
  // namespace through the `_err` error FACTORY + the "key" domain prefix so every reject keeps a key/* code.
23
- // A MAC-less PBES2-CBC decrypt is not a padding oracle (RFC 8018 sec. 8): every post-derivation failure --
24
- // a bad PKCS#7 pad OR a valid pad that does not re-parse as a PrivateKeyInfo -- collapses into ONE uniform
23
+ // A MAC-less PBES2-CBC decrypt is not a padding oracle (RFC 8018 sec. 8): every post-derivation failure
24
+ // (a bad PKCS#7 pad, or a valid pad that does not re-parse as a PrivateKeyInfo) collapses into one uniform
25
25
  // key/decrypt-failed; the structural pre-derivation faults (a non-PBKDF2 KDF, a non-AES-CBC scheme, an
26
26
  // over-cap salt / iteration count, a wrong-length IV, a malformed parameter SEQUENCE) stay distinct and are
27
- // thrown BEFORE any key derivation, so they leak nothing password-dependent. Unencrypted export / import
28
- // and publicFromPrivate DELEGATE to the WebCrypto / node key engines rather than re-serialize an
29
- // AlgorithmIdentifier -- a re-encode is exactly where a params-absent-vs-NULL divergence sneaks back in.
27
+ // thrown before any key derivation, so they leak nothing password-dependent. Unencrypted export / import
28
+ // and publicFromPrivate delegate to the WebCrypto / node key engines and re-serialize no
29
+ // AlgorithmIdentifier. A re-encode is exactly where a params-absent-vs-NULL divergence sneaks back in.
30
30
 
31
31
  var nodeCrypto = require("crypto");
32
32
  var asn1 = require("./asn1-der");
@@ -43,17 +43,17 @@ var subtle = webcrypto.webcrypto.subtle;
43
43
  var KeyError = frameworkError.KeyError;
44
44
  var PemError = frameworkError.PemError;
45
45
  function O(n) { return oid.byName(n); }
46
- // The guard / pbes2 error convention: a (code, msg, cause) FACTORY, never the KeyError class -- pbes2 and
47
- // the guards invoke it as E(code, msg) with no `new` (a class there crashes on the error path).
46
+ // The guard / pbes2 error convention: a (code, msg, cause) factory, never the KeyError class, because pbes2
47
+ // and the guards invoke it as E(code, msg) with no `new` (a class there crashes on the error path).
48
48
  function _err(code, msg, cause) { return new KeyError(code, msg, cause); }
49
49
 
50
50
  // The operator-facing node cipher name <-> the AES-CBC registry name the PBES2 home speaks.
51
51
  var CIPHER_NAME = { "aes-128-cbc": "aes128-CBC", "aes-192-cbc": "aes192-CBC", "aes-256-cbc": "aes256-CBC" };
52
52
 
53
- // import inference: the algorithm OIDs that name exactly ONE WebCrypto algorithm and need no parameters --
53
+ // import inference: the algorithm OIDs that name exactly one WebCrypto algorithm and need no parameters --
54
54
  // the Edwards/Montgomery curves and the FIPS post-quantum ML-DSA, ML-KEM, and SLH-DSA (all signing-only or
55
- // agreement-only, so unambiguous). RSA (sign vs OAEP) and EC (ECDSA vs ECDH) are deliberately ABSENT -- they
56
- // are ambiguous, so import fails closed and asks for opts.algorithm rather than guess a plausible use.
55
+ // agreement-only, so unambiguous). RSA (sign vs OAEP) and EC (ECDSA vs ECDH) are deliberately absent. Each
56
+ // is ambiguous, so import fails closed there and asks for opts.algorithm.
57
57
  var INFER_ALG = {};
58
58
  ["Ed25519", "Ed448", "X25519", "X448"].forEach(function (n) { INFER_ALG[O(n)] = { name: n }; });
59
59
  [["id-ml-dsa-44", "ML-DSA-44"], ["id-ml-dsa-65", "ML-DSA-65"], ["id-ml-dsa-87", "ML-DSA-87"],
@@ -153,9 +153,9 @@ async function encrypt(privateKey, password, opts) {
153
153
  * PBKDF2 key-derivation function and an AES-CBC encryption scheme is accepted; PBES1, PBMAC1, scrypt, and any
154
154
  * other `encryptionAlgorithm` fail closed with `key/unsupported-algorithm`.
155
155
  *
156
- * The salt and iteration count are attacker-controlled work: both caps are enforced BEFORE any derivation
156
+ * The salt and iteration count are attacker-controlled work: both caps are enforced before any derivation
157
157
  * (`opts.maxIterations` may lower the cap, never raise it), and a wrong-length IV or malformed parameter set
158
- * is a typed `key/bad-algorithm-parameters`. A MAC-less PBES2-CBC decrypt is not a padding oracle -- a wrong
158
+ * is a typed `key/bad-algorithm-parameters`. A MAC-less PBES2-CBC decrypt is not a padding oracle: a wrong
159
159
  * password and a valid-pad-but-not-a-PrivateKeyInfo both surface the single uniform `key/decrypt-failed`.
160
160
  *
161
161
  * @opts
@@ -252,7 +252,7 @@ async function export_(key, opts) {
252
252
  * KEY` -- auto-detecting the structure -- into a `CryptoKey`. The WebCrypto algorithm is inferred from the
253
253
  * key's OID for the algorithms that name exactly one (Ed25519 / Ed448 / X25519 / X448 / ML-DSA / ML-KEM /
254
254
  * SLH-DSA); RSA and EC are ambiguous between signing and key agreement, so `opts.algorithm` must be supplied
255
- * for them (import fails closed rather than guess a use). Default key usages follow the algorithm and key type.
255
+ * for them (without it the import fails closed). Default key usages follow the algorithm and key type.
256
256
  *
257
257
  * @opts
258
258
  * - `algorithm` (string | object) -- the WebCrypto algorithm (required for RSA / EC; overrides inference).
@@ -324,7 +324,7 @@ async function generate(algorithm, opts) {
324
324
  *
325
325
  * Derive the SubjectPublicKeyInfo (SPKI) public key from a PKCS#8 private key (DER `Buffer`, `PRIVATE KEY`
326
326
  * PEM, or extractable private `CryptoKey`). The derivation is delegated to the node key engine, which infers
327
- * the algorithm from the key structure, so no `AlgorithmIdentifier` is re-encoded -- Ed25519 stays
327
+ * the algorithm from the key structure, so no `AlgorithmIdentifier` is re-encoded: Ed25519 stays
328
328
  * parameters-absent, RSA keeps its NULL, EC keeps its namedCurve.
329
329
  *
330
330
  * @opts