@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/lint.js CHANGED
@@ -12,11 +12,11 @@
12
12
  * a spec-clause citation, and a human message. It ships the RFC 5280 certificate profile
13
13
  * plus a representative CA/Browser Forum TLS Baseline Requirements subset.
14
14
  *
15
- * Unlike every other toolkit entry, the lint DATA path NEVER throws. A linter surveys a
15
+ * Unlike every other toolkit entry, the lint data path never throws. A linter surveys a
16
16
  * corpus that includes malformed members, so `pki.lint.certificate(hostileBytes)` returns
17
17
  * a report whose worst finding is a `fatal` id `lint/unparseable` (carrying the inner
18
- * `PkiError.code`) rather than raising. The SOLE throw path is CONFIG-time misuse -- an
19
- * unknown profile, an out-of-range severity threshold, or a wrong-type input -- which
18
+ * `PkiError.code`) and does not raise. The one throw path is config-time misuse (an
19
+ * unknown profile, an out-of-range severity threshold, or a wrong-type input), which
20
20
  * raises a typed `LintError`. This deliberate inversion of the toolkit's fail-closed-throw
21
21
  * posture is what lets an operator lint a whole directory without a try/catch per file.
22
22
  * @spec RFC 5280, CA/Browser Forum TLS Baseline Requirements
@@ -41,7 +41,7 @@ function _cfg(code, message, cause) { return new LintError(code, message, cause)
41
41
  // Strict-parse pre-emption: this toolkit's parser is strict-DER, so several RFC 5280
42
42
  // profile violations a lenient linter (zlint/pkilint) would report as a SPECIFIC finding
43
43
  // are instead rejected fail-closed at PARSE and surface here as a single `fatal`
44
- // lint/unparseable whose context.code names the exact structural violation -- e.g. a
44
+ // lint/unparseable whose context.code names the exact structural violation: e.g. a
45
45
  // duplicate extension OID (x509/duplicate-extension), a pathLenConstraint without cA
46
46
  // (bad-basic-constraints), an explicit cA=FALSE, or a signatureAlgorithm/tbs mismatch.
47
47
  // The registry therefore ships no dedicated rule for a violation the parser already
@@ -53,14 +53,14 @@ function _cfg(code, message, cause) { return new LintError(code, message, cause)
53
53
  var NS = pkix.makeNS("lint", LintError, oid);
54
54
  var EXT_DECODERS = pkix.certExtensionDecoders(NS).byOid;
55
55
 
56
- // Coverage note: the rules and helpers below carry defensive guards -- `|| []` / `|| ""`
57
- // on fields the strict parser ALWAYS sets (extensions, serialNumberHex, subject.rdns), and
56
+ // Coverage note: the rules and helpers below carry defensive guards, `|| []` / `|| ""`
57
+ // on fields the strict parser always sets (extensions, serialNumberHex, subject.rdns), and
58
58
  // `!Buffer.isBuffer` / `instanceof Date` / minimal-encoding checks against malformed-but-
59
59
  // parsed shapes the strict parser never produces. These are fail-safe belts, not reachable
60
- // paths; they stay verified-unreachable (documented) rather than removed, since the lint
60
+ // paths; they stay verified-unreachable (documented) and are kept, since the lint
61
61
  // data path must never throw even on an unexpected upstream shape.
62
62
 
63
- // ---- the advisory result surface (the Do-FIRST: graded findings, not a throw) ----
63
+ // ---- the advisory result surface: graded findings, not a throw ----
64
64
 
65
65
  // Severity ordering (zlint's LintStatus). NA (not applicable) and NE (not effective --
66
66
  // outside a rule's date window) are rule OUTCOMES, tracked in counts, not severities.
@@ -91,8 +91,8 @@ function _finding(rule, detail) {
91
91
  // on. That makes it a decision like any other, so it takes the same derivation: the parser's record,
92
92
  // not the caller's object.
93
93
  //
94
- // Returns the DERIVED certificate or null, rather than a boolean, because the derived value is what
95
- // the rules must run against -- answering "yes, that is a certificate" and then linting the object
94
+ // Returns the derived certificate or null, never a boolean, because the derived value is what
95
+ // the rules must run against: answering "yes, that is a certificate" and then linting the object
96
96
  // would be the check computed and thrown away. Never throws: _ingest's contract is to return a
97
97
  // finding for bad DATA and reserve throws for a wrong-TYPE argument.
98
98
  function _derivedCert(o) {
@@ -106,7 +106,7 @@ function _derivedCert(o) {
106
106
  }
107
107
 
108
108
  // Returns { cert } on success, or { fatal: <Finding> } when hostile bytes do not parse
109
- // (the never-throw data path). Throws LintError ONLY on a wrong-TYPE input (config misuse).
109
+ // (the never-throw data path). Throws LintError only on a wrong-type input (config misuse).
110
110
  function _ingest(input) {
111
111
  var derived = _derivedCert(input);
112
112
  if (derived) return { cert: derived };
@@ -151,7 +151,7 @@ function _hasEku(cert, ekuName) {
151
151
  function _makeCtx(cert, profile) {
152
152
  var explicitTls = profile === "cabf-tls";
153
153
  // A CA certificate is not a TLS server (leaf) certificate even when it carries a
154
- // serverAuth EKU, so the default profile must NOT apply the CABF leaf rules (SAN
154
+ // serverAuth EKU, so the default profile must not apply the CABF leaf rules (SAN
155
155
  // required, CN-in-SAN, validity ceiling, ...) to it. An explicit cabf-tls selection
156
156
  // still lints whatever the caller hands it as a server cert.
157
157
  var bc = _decodeOrNull(cert, "basicConstraints");
@@ -211,7 +211,7 @@ function _serialOctets(cert) {
211
211
  // so the linter and the identity comparators agree on what a well-formed dNSName is. Returns a reason or null.
212
212
  function _dnsNameProblem(s) { return pkix.dnsNameProblem(s); }
213
213
 
214
- // A genuine IPv4 or IPv6 literal -- a CN validated against an iPAddress SAN rather than a
214
+ // A genuine IPv4 or IPv6 literal, so a CN is validated against an iPAddress SAN and not a
215
215
  // dNSName. Routed through the shared strict validator (no node:net, so the toolkit needs no
216
216
  // networking module): a per-octet-bounded dotted quad, or a full RFC 4291 IPv6 form
217
217
  // (including the IPv4-mapped / dual-stack tail). Rejects 999.999.999.999 and "api:443".
@@ -231,26 +231,26 @@ function _subjectCNs(cert) {
231
231
  return out;
232
232
  }
233
233
 
234
- // An extension's CRITICALITY is honoured (RFC 5280 4.2: a consumer MUST reject a critical extension
235
- // it cannot PROCESS) only when this toolkit actually processes its semantics. That authority is
236
- // path-validate's PROCESSED_EXTENSIONS (the RFC 5280 sec. 6.1 path-processing set), NOT the decoder
237
- // table: certExtensionDecoders also DECODES many extensions purely for display (qcStatements, the MS
234
+ // An extension's criticality is honoured (RFC 5280 4.2: a consumer MUST reject a critical extension
235
+ // it cannot process) only when this toolkit actually processes its semantics. That authority is
236
+ // path-validate's PROCESSED_EXTENSIONS (the RFC 5280 sec. 6.1 path-processing set), never the decoder
237
+ // table: certExtensionDecoders also decodes many extensions purely for display (qcStatements, the MS
238
238
  // enterprise-CA extensions, the SCT list, the *KeyIdentifiers, freshestCRL, issuerAltName) whose
239
- // critical semantics are not enforced -- path-validate rejects a critical instance of any of those
239
+ // critical semantics are not enforced. path-validate rejects a critical instance of any of those
240
240
  // as unrecognized-critical, so the linter flags them too (structural decodability is not validation
241
- // processing). The one decode-only extension that is LEGITIMATELY critical is precertificatePoison
242
- // (RFC 6962 sec. 3.1 REQUIRES it critical), so it is allow-listed. Everything else -- a processed
243
- // extension, or an unregistered OID (algorithm / EKU-purpose OID the name registry resolves but that
244
- // is no extension) -- falls out correctly: processed -> recognized, unregistered -> unknown. Driving
241
+ // processing). The one decode-only extension that is legitimately critical is precertificatePoison
242
+ // (RFC 6962 sec. 3.1 REQUIRES it critical), so it is allow-listed. Everything else, a processed
243
+ // extension or an unregistered OID (algorithm / EKU-purpose OID the name registry resolves but that
244
+ // is no extension), falls out correctly: processed -> recognized, unregistered -> unknown. Driving
245
245
  // this off the validator's set (not the decoder table) means a newly-decoded extension is
246
246
  // flagged-when-critical automatically until its semantics are actually processed.
247
247
  var _CRITICAL_LEGIT = {};
248
248
  _CRITICAL_LEGIT[oid.byName("precertificatePoison")] = true;
249
249
  // Mirrors path-validate's unrecognizedCriticalExtension. An extension not in PROCESSED_EXTENSIONS is
250
250
  // unknown everywhere (precertificatePoison is the one legitimately-critical exception). An extension
251
- // that IS processed for an intermediate but is unprocessed on the TARGET (policyMappings) is also
252
- // flagged: path-validate decides target status purely by path position (i === n), so ANY certificate
253
- // -- a CA included -- can be validated as the final/target certificate, where a critical instance is
251
+ // that is processed for an intermediate but unprocessed on the target (policyMappings) is also
252
+ // flagged: path-validate decides target status purely by path position (i === n), so any certificate,
253
+ // a CA included, can be validated as the final/target certificate, where a critical instance is
254
254
  // rejected. A linter has no path context and cannot rule that out, so it flags these regardless of
255
255
  // cA-ness (policyMappings is SHOULD-be-non-critical everywhere, so this never mis-flags conformant input).
256
256
  function _isUnknownExtension(extOid) {
@@ -267,7 +267,7 @@ function _rsaModulusBits(spki) {
267
267
  var pk = spki.publicKey && (spki.publicKey.bytes || spki.publicKey);
268
268
  if (!Buffer.isBuffer(pk)) return null;
269
269
  var seq = asn1.decode(pk);
270
- // RSAPublicKey ::= SEQUENCE { modulus INTEGER, publicExponent INTEGER } -- exactly two
270
+ // RSAPublicKey ::= SEQUENCE { modulus INTEGER, publicExponent INTEGER }: exactly two
271
271
  // INTEGERs, and the modulus MUST be positive. A negative/zero modulus (a high-bit key
272
272
  // mis-encoded without a sign pad) or a missing exponent is malformed, not a real key.
273
273
  if (!seq.children || seq.children.length !== 2) return null;
@@ -292,17 +292,17 @@ function _ecCurveName(spki) {
292
292
 
293
293
  // RFC 5280 marks several extensions MUST (error) or SHOULD (warn) be critical. The shape is
294
294
  // uniform: applies when the extension is present, fires when its raw `critical` flag is not
295
- // true. The rule reads `ctx.raw(name).critical` WITHOUT decoding the value -- criticality is
296
- // a structural property of the extension, independent of its contents.
295
+ // true. The rule reads `ctx.raw(name).critical` without decoding the value, because criticality
296
+ // is a structural property of the extension, independent of its contents.
297
297
  // ---- RFC 5280 4.2.1.4 userNotice DisplayText ----
298
- // certificatePolicies surfaces each policy's qualifiers as RAW bytes (an external verifier may hash
298
+ // certificatePolicies surfaces each policy's qualifiers as raw bytes (an external verifier may hash
299
299
  // them), so the DisplayText values are read out of those bytes here. The UserNotice walk itself lives
300
300
  // in schema-pkix (pki.inspect renders the same values), so the two consumers cannot disagree about
301
301
  // which members are DisplayText or how a BMPString decodes.
302
302
  var OID_UNOTICE = oid.byName("unotice");
303
303
  var _T_BMP = asn1.TAGS.BMP_STRING, _T_VISIBLE = asn1.TAGS.VISIBLE_STRING, _T_UTF8 = asn1.TAGS.UTF8_STRING;
304
- // The C0 and C1 control ranges, tested by code point rather than a regex: the characters this rule is
305
- // ABOUT cannot appear in the source, and a regex holding them would be a control byte here.
304
+ // The C0 and C1 control ranges, tested by code point instead of a regex: the characters this rule is
305
+ // about cannot appear in the source, and a regex holding them would be a control byte here.
306
306
  function _hasControlChar(str) {
307
307
  for (var i = 0; i < str.length; i++) {
308
308
  var c = str.charCodeAt(i);
@@ -316,7 +316,7 @@ function _hasControlChar(str) {
316
316
  // The two decodes below are fail-safe belts on the never-throw data path, not reachable paths: the
317
317
  // shared decoder ran pkix.assertPolicyQualifiers before surfacing qualifiersBytes, so the bytes are
318
318
  // already known to decode as a SEQUENCE whose every element is a two-member PolicyQualifierInfo led
319
- // by a readable OID. An undecodable extension never reaches here at all -- it is the separate
319
+ // by a readable OID. An undecodable extension never reaches here at all; it is the separate
320
320
  // extension-undecodable finding.
321
321
  function _policyDisplayTexts(ctx) {
322
322
  var d = ctx.decode("certificatePolicies");
@@ -333,8 +333,8 @@ function _policyDisplayTexts(ctx) {
333
333
  try { qid = asn1.read.oid(pq.children[0]); } catch (_e2) { return; }
334
334
  if (qid !== OID_UNOTICE) return;
335
335
  // Entries whose contents did not decode arrive with a null `text` and keep their tag: the rules
336
- // below that read the text skip them, while the encoding rule -- which the ASN.1 tag alone
337
- // answers -- still sees them.
336
+ // below that read the text skip them, while the encoding rule, which the ASN.1 tag alone
337
+ // answers, still sees them.
338
338
  out = out.concat(pkix.userNoticeTexts(pq.children[1]));
339
339
  });
340
340
  });
@@ -344,8 +344,8 @@ function _hasPolicyDisplayText(cert, ctx) { return _policyDisplayTexts(ctx).leng
344
344
 
345
345
  // RFC 5280 marks several extensions MUST (error) or SHOULD (warn) be critical. The shape is
346
346
  // uniform: applies when the extension is present, fires when its raw `critical` flag is not
347
- // true. The rule reads `ctx.raw(name).critical` WITHOUT decoding the value -- criticality is
348
- // a structural property of the extension, independent of its contents.
347
+ // true. The rule reads `ctx.raw(name).critical` without decoding the value, because criticality
348
+ // is a structural property of the extension, independent of its contents.
349
349
  function _criticalityRule(name, id, severity, citation, message) {
350
350
  return {
351
351
  id: id, severity: severity, source: "rfc5280", citation: citation, message: message,
@@ -441,7 +441,7 @@ var RFC5280_RULES = [
441
441
  // public keys used to validate digital signatures on certificates" (keyCertSign). A CA
442
442
  // key used exclusively for other purposes (CRL signing, key management) MAY carry a
443
443
  // non-critical basicConstraints, so the rule applies only when keyCertSign is asserted
444
- // (or keyUsage is absent) -- gating on cA alone would false-positive on a CRL-signing CA.
444
+ // (or keyUsage is absent); gating on cA alone would false-positive on a CRL-signing CA.
445
445
  id: "lint/rfc5280/basic-constraints-not-critical", severity: "error", source: "rfc5280", citation: "RFC 5280 4.2.1.9",
446
446
  message: "a CA certificate that validates certificate signatures must mark basicConstraints critical",
447
447
  appliesTo: function (cert, ctx) {
@@ -466,8 +466,8 @@ var RFC5280_RULES = [
466
466
  _criticalityRule("inhibitAnyPolicy", "lint/rfc5280/inhibit-any-policy-not-critical", "error",
467
467
  "RFC 5280 4.2.1.14", "the inhibitAnyPolicy extension must be marked critical"),
468
468
  // 4.2.1.3: "When present, conforming CAs SHOULD mark this extension as critical." The
469
- // recommendation directs the ISSUING CA whenever it includes keyUsage -- in ANY certificate,
470
- // including an end-entity leaf -- and does NOT restrict it to certificates whose subject is
469
+ // recommendation directs the issuing CA whenever it includes keyUsage, in any certificate
470
+ // including an end-entity leaf, and does not restrict it to certificates whose subject is
471
471
  // itself a CA. So the rule is present-gated (matching zlint's w_ext_key_usage_not_critical),
472
472
  // not CA-subject-gated; a SHOULD, hence warn.
473
473
  _criticalityRule("keyUsage", "lint/rfc5280/key-usage-not-critical", "warn",
@@ -487,9 +487,9 @@ var RFC5280_RULES = [
487
487
  // would refuse certificates that are in the wild and otherwise valid. Reporting them is exactly what
488
488
  // a linter is for, so each rule carries the severity its normative word does.
489
489
  {
490
- // DisplayText is SIZE (1..200) -- both ends -- but the two ends get SEPARATE ids rather than one
490
+ // DisplayText is SIZE (1..200) at both ends, but the two ends get separate ids instead of one
491
491
  // length rule, because the section treats them differently: it tells certificate users to handle a
492
- // notice ABOVE 200 gracefully, and says nothing of the sort about an empty one. An operator acting
492
+ // notice above 200 gracefully, and says nothing of the sort about an empty one. An operator acting
493
493
  // on that advice suppresses the over-long finding; folding both into a single id would silently
494
494
  // suppress the empty case along with it, which has no such carve-out.
495
495
  id: "lint/rfc5280/explicit-text-too-long", severity: "warn", source: "rfc5280", citation: "RFC 5280 4.2.1.4",
@@ -604,9 +604,9 @@ var CABF_TLS_RULES = [
604
604
  {
605
605
  id: "lint/cabf-tls/eku-missing-serverauth", severity: "error", source: "cabf-tls", citation: "CABF TLS BR 7.1.2.7.6",
606
606
  message: "a TLS server certificate's extKeyUsage must include id-kp-serverAuth",
607
- // Only meaningful when the caller EXPLICITLY lints against the TLS profile; under the
608
- // default profile a cert is recognized as TLS BY its serverAuth EKU, so the check is
609
- // vacuous and reports NA rather than firing on every non-TLS certificate.
607
+ // Only meaningful when the caller explicitly lints against the TLS profile; under the
608
+ // default profile a cert is recognized as TLS by its serverAuth EKU, so the check is
609
+ // vacuous and reports NA, never firing on every non-TLS certificate.
610
610
  appliesTo: function (cert, ctx) { return ctx.explicitTlsProfile; },
611
611
  check: function (cert, ctx) { return _hasEku(cert, "serverAuth") ? null : true; },
612
612
  },
@@ -618,8 +618,8 @@ var CABF_TLS_RULES = [
618
618
  var spki = cert.subjectPublicKeyInfo, name = spki && spki.algorithm && spki.algorithm.name;
619
619
  // Fail closed: a key whose size/curve cannot be confirmed to meet the minimum is
620
620
  // flagged, not passed. RSA below 2048 bits (or an unreadable modulus), or an EC key
621
- // NOT on an approved named curve (including absent / explicit / invalid parameters,
622
- // which resolve to a null curve) is a finding -- CABF permits only P-256/384/521.
621
+ // that is not on an approved named curve (including absent / explicit / invalid parameters,
622
+ // which resolve to a null curve) is a finding: CABF permits only P-256/384/521.
623
623
  if (RSA_KEY_ALGS[name]) { var bits = _rsaModulusBits(spki); return (bits === null || bits < 2048) ? { context: { rsaBits: bits } } : null; }
624
624
  if (name === "ecPublicKey") { var curve = _ecCurveName(spki); return APPROVED_EC_CURVES.indexOf(curve) === -1 ? { context: { curve: curve } } : null; }
625
625
  // Any other key type (e.g. EdDSA) is out of this rule's RSA-size / EC-curve scope --
@@ -694,7 +694,7 @@ function _selectRules(profile) {
694
694
  if (profile == null || profile === "all" || profile === "default") return ALL_RULES;
695
695
  if (!PROFILES[profile]) throw _cfg("lint/unknown-profile", "unknown lint profile \"" + profile + "\" (known: " + Object.keys(PROFILES).join(", ") + ")");
696
696
  // An explicit cabf-tls run still needs the always-on rfc5280 structural rules? No: the
697
- // caller asked for ONE profile. Return exactly that profile's rules.
697
+ // caller asked for one profile. Return exactly that profile's rules.
698
698
  return PROFILES[profile];
699
699
  }
700
700
 
@@ -721,10 +721,10 @@ function _applyThreshold(report, severity) {
721
721
  * `pki.schema.x509` object. Returns a `LintReport`
722
722
  * `{ findings: [{id, severity, source, citation, message, context?}], counts, worst, ran }`.
723
723
  *
724
- * The DATA path never throws: hostile bytes produce a single `fatal` finding
725
- * `lint/unparseable` rather than raising. The ONLY throw path is config-time misuse
726
- * (`opts.profile` unknown, `opts.severity` out of range, or a wrong-type input) -- a
727
- * typed `LintError`.
724
+ * The data path never throws: hostile bytes produce a single `fatal` finding
725
+ * `lint/unparseable` and no exception. The one throw path is config-time misuse
726
+ * (`opts.profile` unknown, `opts.severity` out of range, or a wrong-type input), which
727
+ * raises a typed `LintError`.
728
728
  *
729
729
  * @opts profile One of `pki.lint.profiles()` (default runs every profile). Selecting
730
730
  * `"cabf-tls"` lints the input AS a TLS server certificate.
package/lib/merkle.js CHANGED
@@ -12,11 +12,11 @@
12
12
  * RFC 6962 (Certificate Transparency) / RFC 9162 (CT 2.0) Merkle-tree hash
13
13
  * and proof-verification core -- the load-bearing primitive a static-CT
14
14
  * client, a Merkle-Tree-Certificates relying party, and a sigstore / Rekor
15
- * inclusion check all compose. It is ALL strict verification over SHA-256:
16
- * zero new crypto.
15
+ * inclusion check all compose. Every verb is strict verification over
16
+ * SHA-256: zero new crypto.
17
17
  *
18
18
  * `leafHash` / `nodeHash` / `emptyRootHash` build the tree hashes with the
19
- * two domain-separation prefixes fixed by the spec -- a leaf is
19
+ * two domain-separation prefixes fixed by the spec: a leaf is
20
20
  * `SHA-256(0x00 || entry)`, an interior node is `SHA-256(0x01 || left ||
21
21
  * right)`, the empty tree is `SHA-256("")`. Those `0x00` / `0x01` prefixes
22
22
  * are the second-preimage defense: without them a leaf whose bytes equal a
@@ -24,16 +24,16 @@
24
24
  *
25
25
  * `verifyInclusion` folds an audit path back to a root and constant-time-
26
26
  * compares it to a trusted checkpoint root; `verifyConsistency` reconstructs
27
- * BOTH the old and the new root from a consistency proof (the append-only
27
+ * both the old and the new root from a consistency proof (the append-only
28
28
  * guarantee lives in the old-root leg). Both are fail-closed: a malformed
29
29
  * coordinate, an out-of-range index, an inverted window, a wrong hash length,
30
30
  * or a proof whose node count does not match the tree geometry throws a typed
31
- * `merkle/*` error; the ONLY boolean-`false` result is the final root
31
+ * `merkle/*` error; the one boolean-`false` result is the final root
32
32
  * comparison ("root matched" vs "did not"). A `false` from `verifyInclusion`
33
- * means "not proven present against this root", never "validly absent" -- an
33
+ * means "not proven present against this root", never "validly absent": an
34
34
  * inclusion proof cannot express absence. Tree coordinates are uint64, carried
35
- * as `BigInt` so a large index is never `Number`-narrowed. This is NOT a DER
36
- * format: like `pki.ct` it is a companion module reached explicitly, never
35
+ * as `BigInt` so a large index is never `Number`-narrowed. This is not a DER
36
+ * format. Like `pki.ct` it is a companion module reached explicitly, never
37
37
  * routed by the detect-and-parse orchestrator.
38
38
  *
39
39
  * @card
@@ -171,11 +171,11 @@ function emptyRootHash() {
171
171
  * Verify an RFC 6962 / RFC 9162 audit (inclusion) proof: fold `leafHash` up the
172
172
  * audit path and constant-time-compare the reconstructed root to `rootHash`.
173
173
  * Returns `true` iff the proof binds the leaf to the root; a well-formed proof
174
- * that does not match returns `false` ("not proven present against this root",
175
- * NEVER "validly absent"). A malformed input -- a coordinate that is not a
176
- * non-negative integer (or a Number >= 2^53), `treeSize` 0, `leafIndex >=
177
- * treeSize`, a non-32-byte hash, or a proof whose node count does not match the
178
- * tree geometry -- throws a typed `merkle/*` error.
174
+ * that does not match returns `false`, meaning "not proven present against this
175
+ * root" and never "validly absent". A malformed input throws a typed `merkle/*`
176
+ * error: a coordinate that is not a non-negative integer (or a Number >= 2^53),
177
+ * `treeSize` 0, `leafIndex >= treeSize`, a non-32-byte hash, or a proof whose
178
+ * node count does not match the tree geometry.
179
179
  *
180
180
  * @opts
181
181
  * leafIndex: number | bigint, // 0-based leaf position (uint64; pass BigInt above 2^53)
@@ -235,19 +235,26 @@ function verifyInclusion(opts) {
235
235
  *
236
236
  * Verify an RFC 6962 / RFC 9162 consistency proof between an older tree of
237
237
  * `oldSize` leaves (root `oldRoot`) and a newer tree of `newSize` leaves (root
238
- * `newRoot`). Reconstructs BOTH roots from the proof and constant-time-compares
239
- * each; returns `true` iff both match (the append-only guarantee lives in the
240
- * old-root leg -- a proof that yields a valid `newRoot` but the wrong `oldRoot`
241
- * is a rewritten history and returns `false`). The empty tree is a prefix of
242
- * every tree, so `oldSize` 0 checks `oldRoot == emptyRootHash()` -- and, when
243
- * `newSize` is also 0, `newRoot == emptyRootHash()` too (both trees empty); for
244
- * `newSize > 0` an empty-old proof places no binding on `newRoot` (an empty prior
245
- * tree carries no append-only guarantee -- authenticate `newRoot` separately,
246
- * e.g. via its checkpoint signature). Equal non-zero sizes require an empty proof
247
- * and `oldRoot == newRoot`. A malformed input --
248
- * `oldSize > newSize`, a non-empty proof where
249
- * the geometry requires none (or empty where it requires one), a non-32-byte
250
- * hash, or a wrong node count -- throws a typed `merkle/*` error.
238
+ * `newRoot`). Reconstructs both roots from the proof and constant-time-compares
239
+ * each; returns `true` iff both match. The append-only guarantee lives in the
240
+ * old-root leg: a proof that yields a valid `newRoot` but the wrong `oldRoot`
241
+ * is a rewritten history and returns `false`. Equal non-zero sizes require an
242
+ * empty proof and `oldRoot == newRoot`.
243
+ *
244
+ * An `oldSize` of 0 with a non-empty newer tree is refused as
245
+ * `merkle/no-consistency-claim`. RFC 6962 sec. 2.1.2 defines the proof for
246
+ * `0 < oldSize < newSize`: the empty tree is a prefix of every tree by
247
+ * definition, so there is no proof to check and nothing at all binds `newRoot`.
248
+ * Returning `true` there would say a step was proven append-only when nothing had
249
+ * been proven, and a monitor written `if (!verifyConsistency(...)) alarm()` would
250
+ * take that silence for evidence. A caller with no prior tree has an inclusion
251
+ * question about the new one, or should start from a checkpoint it already
252
+ * trusts. Two empty trees are still answered:
253
+ * that is the degenerate identity case, and both roots must be `emptyRootHash()`.
254
+ *
255
+ * A malformed input throws a typed `merkle/*` error: `oldSize > newSize`, a
256
+ * non-empty proof where the geometry requires none (or empty where it requires
257
+ * one), a non-32-byte hash, or a wrong node count.
251
258
  *
252
259
  * @opts
253
260
  * oldSize: number | bigint, // leaf count of the older tree (uint64)
@@ -273,18 +280,26 @@ function verifyConsistency(opts) {
273
280
 
274
281
  if (oldSize > newSize) throw new MerkleError("merkle/old-size-exceeds-new", "oldSize " + oldSize + " exceeds newSize " + newSize);
275
282
  if (oldSize === 0n) {
276
- if (proof.length !== 0) throw new MerkleError("merkle/bad-proof-length", "an empty older tree admits only the empty consistency proof");
277
283
  var er = emptyRootHash();
278
- var oldEmpty = _ctEq(oldRoot, er);
279
- // The empty tree is a prefix of every tree, so its own root must be the
280
- // empty root. When the NEW tree is also empty, its root must be the empty
281
- // root too (a bogus newRoot must not pass); when newSize > 0 an empty-old
282
- // proof places no binding on newRoot.
284
+ // newSize 0 is the degenerate identity case, not a consistency proof: both trees are the empty
285
+ // tree, so both roots must be the empty root and a bogus newRoot must not pass.
283
286
  if (newSize === 0n) {
284
- var newEmpty = _ctEq(newRoot, er);
285
- return oldEmpty && newEmpty;
287
+ if (proof.length !== 0) throw new MerkleError("merkle/bad-proof-length", "two empty trees admit only the empty consistency proof");
288
+ return _ctEq(oldRoot, er) && _ctEq(newRoot, er);
286
289
  }
287
- return oldEmpty;
290
+ // newSize > 0 falls outside the definition. RFC 6962 sec. 2.1.2 defines PROOF(m, D[n]) for
291
+ // 0 < m < n, and an empty older tree is a prefix of every tree by definition: there is no
292
+ // proof to check and nothing whatever is bound about newRoot, which is why the value is not
293
+ // read on this branch. Returning the same `true` the honest path returns would state that a
294
+ // step was proven append-only when nothing was proven, and a monitor written
295
+ // `if (!verifyConsistency(...)) alarm()` would take silence for evidence. A boolean cannot
296
+ // carry the difference, so the question is refused. A caller with no prior tree has an
297
+ // inclusion question about the new tree, not a consistency question.
298
+ throw new MerkleError("merkle/no-consistency-claim",
299
+ "an empty older tree (oldSize 0) makes no consistency claim about a non-empty newer tree: " +
300
+ "RFC 6962 sec. 2.1.2 defines the proof for 0 < oldSize < newSize, and nothing here binds " +
301
+ "newRoot. Verify an inclusion proof against the new tree, or start from a signed tree head " +
302
+ "you already trust");
288
303
  }
289
304
  if (oldSize === newSize) {
290
305
  if (proof.length !== 0) throw new MerkleError("merkle/sizes-equal-nonempty-proof", "equal tree sizes require an empty consistency proof");
@@ -319,7 +334,7 @@ function verifyConsistency(opts) {
319
334
  sn >>= 1n;
320
335
  }
321
336
  if (sn !== 0n) throw new MerkleError("merkle/bad-proof-length", "consistency proof is shorter than the geometry requires");
322
- // Evaluate BOTH constant-time compares unconditionally before combining, so a
337
+ // Evaluate both constant-time compares unconditionally before combining, so a
323
338
  // `&&` never short-circuits the second `timingSafeEqual` (which would leak,
324
339
  // via timing, whether the old-root leg matched).
325
340
  var okOld = _ctEq(fr, oldRoot);
package/lib/mime.js CHANGED
@@ -9,9 +9,9 @@
9
9
  * splits a multipart body on its boundary, and builds an entity/multipart back. There is NO crypto
10
10
  * here -- the CMS layer signs/verifies the canonical bytes this module produces.
11
11
  *
12
- * The LOAD-BEARING rule is canonicalization: the detached signature over a multipart/signed first part
13
- * is computed over that part's canonical MIME form, so the signer and verifier MUST share ONE
14
- * canonicalizer -- this module. It carries the caller's typed ErrorClass `E` (constructed
12
+ * The load-bearing rule is canonicalization: the detached signature over a multipart/signed first part
13
+ * is computed over that part's canonical MIME form, so the signer and verifier MUST share one
14
+ * canonicalizer, this module. It carries the caller's typed ErrorClass `E` (constructed
15
15
  * `new E(code, message)`), exactly as the byte-reader / guard family do, so every consumer keeps its
16
16
  * own `domain/reason` fault code.
17
17
  */
@@ -50,14 +50,14 @@ function _unfoldHeaders(headerText) {
50
50
  return out;
51
51
  }
52
52
 
53
- // Parse a Content-Type / any structured header value into { value, type, params } -- the media type
53
+ // Parse a Content-Type / any structured header value into { value, type, params }: the media type
54
54
  // (lowercased) plus its parameters (names lowercased, quotes stripped). Tolerant of extra whitespace.
55
55
  function _parseStructured(headerValue) {
56
56
  var parts = _splitSemicolons(headerValue); // always >= 1 element
57
57
  var type = _stripComments(parts[0]).trim().toLowerCase();
58
58
  var params = {};
59
59
  for (var i = 1; i < parts.length; i++) {
60
- // RFC 5322 CFWS: a comment around the name/value is folding whitespace, not part of either -- strip it
60
+ // RFC 5322 CFWS: a comment around the name/value is folding whitespace, not part of either, so strip it
61
61
  // so "(note) hp" resolves to the "hp" parameter and a trailing comment does not leak into the value.
62
62
  var p = _stripComments(parts[i]);
63
63
  var eq = p.indexOf("=");
@@ -70,7 +70,7 @@ function _parseStructured(headerValue) {
70
70
  return { value: headerValue.trim(), type: type, params: params };
71
71
  }
72
72
 
73
- // Remove RFC 5322 comments -- a "(...)" span (nesting; a backslash quoted-pair escapes the next byte) OUTSIDE
73
+ // Remove RFC 5322 comments: a "(...)" span (nesting; a backslash quoted-pair escapes the next byte) outside
74
74
  // a double-quoted string. A "(" inside a quoted string is literal qtext, not a comment. Used to drop CFWS
75
75
  // from a structured-header token before the name/value is read, so a comment cannot forge or hide a parameter.
76
76
  function _stripComments(s) {
@@ -93,10 +93,10 @@ function _splitSemicolons(s) {
93
93
  for (var i = 0; i < s.length; i++) {
94
94
  var ch = s[i];
95
95
  // RFC 5322 quoted-pair: inside a quoted-string OR a comment a backslash escapes the next char (so an
96
- // escaped `"` / `)` does not end the span, and a `;` in an escaped value does not split) -- keep both bytes.
96
+ // escaped `"` / `)` does not end the span, and a `;` in an escaped value does not split), so keep both bytes.
97
97
  if ((inQ || depth > 0) && ch === "\\" && i + 1 < s.length) { cur += ch + s[i + 1]; i++; }
98
98
  else if (ch === '"' && depth === 0) { inQ = !inQ; cur += ch; }
99
- // RFC 5322 comment: parentheses (outside a quoted-string) delimit a nesting comment -- a `;` or `=` inside
99
+ // RFC 5322 comment: parentheses (outside a quoted-string) delimit a nesting comment, so a `;` or `=` inside
100
100
  // one is CFWS, not structural, so a Content-Type comment like `charset=x (note; hp=y)` cannot forge a parameter.
101
101
  else if (ch === "(" && !inQ) { depth++; cur += ch; }
102
102
  else if (ch === ")" && !inQ && depth > 0) { depth--; cur += ch; }
@@ -127,7 +127,7 @@ function parse(input, E, code) {
127
127
  var nm = rawHeaders[i].slice(0, colon).trim();
128
128
  var body = rawHeaders[i].slice(colon + 1);
129
129
  // value: the trimmed field body (structured-header parsing wants it trimmed). rawValue: the field body
130
- // with only the single conventional ": " delimiter space removed -- leading/trailing whitespace INSIDE
130
+ // with only the single conventional ": " delimiter space removed. Leading/trailing whitespace inside
131
131
  // the body is preserved, so a consumer that must reproduce the exact authenticated field body (S/MIME
132
132
  // header protection) is not handed a lossily-trimmed value.
133
133
  headers.push({ name: nm, lname: nm.toLowerCase(), value: body.trim(), rawValue: body.replace(/^ /, "") });
@@ -151,9 +151,9 @@ function canonicalizeText(bodyBytes) {
151
151
  }
152
152
 
153
153
  // The canonical MIME form of a part for signing (RFC 8551 sec. 3.1.1): line endings normalized to the
154
- // CRLF pair over the EXACT bytes -- never a header re-serialization, which would diverge from the
154
+ // CRLF pair over the exact bytes, never a header re-serialization, which would diverge from the
155
155
  // signer's digest (and would break a header-less clear-signed part, as OpenSSL's `smime -sign` emits).
156
- // The signer and verifier both call THIS, so their digests cannot diverge; a transport that mangles
156
+ // The signer and verifier both call this function, so their digests cannot diverge; a transport that mangles
157
157
  // CR/LF is repaired identically on both sides. (A binary clear-signed part must carry a base64
158
158
  // Content-Transfer-Encoding first -- line-ending canonicalization is defined for text, sec. 3.1.1.)
159
159
  function canonicalize(input, E, code) {
@@ -192,7 +192,7 @@ function splitMultipart(bodyBytes, boundary, E, code) {
192
192
  // a line (at the body start, or immediately after an LF) AND its line MUST be a complete delimiter --
193
193
  // the token, then an optional "--" (close), then only transport-padding whitespace, then CRLF / LF /
194
194
  // end-of-buffer. So a "--boundary" substring mid-content, a "--boundaryextra" (a different token), a
195
- // "--boundary garbage" line, or one not at a line start is NOT matched -- an attacker cannot inject a
195
+ // "--boundary garbage" line, or one not at a line start is not matched, so an attacker cannot inject a
196
196
  // fake delimiter inside a part's content.
197
197
  function _findDelim(body, delim, from) {
198
198
  for (var at = body.indexOf(delim, from); at >= 0; at = body.indexOf(delim, at + delim.length)) {
@@ -217,15 +217,15 @@ function _afterLine(buf, from, E, code) {
217
217
 
218
218
  // Serialize an ordered list of header fields + a body into a canonical MIME entity: each field as
219
219
  // "Name: value\r\n" (routed through the header-injection guard so a CR/LF/NUL in a value can never inject a
220
- // header or split the message), then the blank-line separator, then the body -- all CRLF-canonicalized so
220
+ // header or split the message), then the blank-line separator, then the body, all CRLF-canonicalized so
221
221
  // the bytes match a CMS signer's/verifier's digest. `fields` is [{ name, value }]; `body` is a byte source
222
- // (or null/empty). This is the single header/entity emitter -- an inline "Content-Type: ...\r\n" string
222
+ // (or null/empty). This is the single header/entity emitter: an inline "Content-Type: ...\r\n" string
223
223
  // concatenation is the shape this replaces, so a composer cannot re-inline an unguarded header.
224
224
  function buildEntity(fields, body, E, code) {
225
225
  var head = "";
226
226
  for (var i = 0; i < fields.length; i++) {
227
227
  var f = fields[i];
228
- // Serialize the VALUE the guard validated + returned -- never a second coercion of f.value (a stateful
228
+ // Serialize the value the guard validated and returned, never a second coercion of f.value (a stateful
229
229
  // toString() could return CRLF on the second call and inject a header past the check).
230
230
  var v = guard.header.assertField(f.name, f.value, E, code);
231
231
  head += f.name + ": " + v + "\r\n";
@@ -249,10 +249,10 @@ function paramCount(headerValue, name) {
249
249
  return n;
250
250
  }
251
251
 
252
- // Count parameters with this attribute name -- WITH OR WITHOUT a value. paramCount only counts "name=value"
252
+ // Count parameters with this attribute name, with or without a value. paramCount only counts "name=value"
253
253
  // parts, so a bare/valueless attribute (e.g. "Content-Type: text/plain; hp") is invisible to it;
254
- // paramNameCount still sees the attribute name, so a consumer can fail closed on a malformed bare parameter --
255
- // or on a bare+valued duplicate ("hp; hp=x") that paramCount undercounts -- rather than silently ignore it.
254
+ // paramNameCount still sees the attribute name, so a consumer can fail closed on a malformed bare parameter,
255
+ // or on a bare+valued duplicate ("hp; hp=x") that paramCount undercounts, instead of silently ignoring it.
256
256
  // Comment- + quoted-string-aware, like paramCount; part 0 is the media type (not a parameter), so attributes
257
257
  // start at index 1.
258
258
  function paramNameCount(headerValue, name) {
@@ -4,7 +4,7 @@
4
4
  //
5
5
  // @internal -- no operator-facing namespace. The documented surfaces are pki.path.ocspChecker
6
6
  // (revocation during path validation) and pki.ocsp.verify (standalone RFC 6960 sec. 3.2 client
7
- // acceptance), which BOTH compose this ONE verify core. There is deliberately no second, weaker
7
+ // acceptance), which both compose this single verify core. There is deliberately no second, weaker
8
8
  // OCSP response-verification path: a standalone verify that re-derived the responder-authorization
9
9
  // gates would be the exact fail-open the out-of-path-signer-cert-full-validation discipline exists
10
10
  // to prevent.
@@ -18,11 +18,11 @@
18
18
  //
19
19
  // Rule set (gap-checked verbatim against RFC 6960 sec. 3.2 / 4.1.1 / 4.2.2.2 / 4.2.2.2.1):
20
20
  // - authorizeResponder: the issuing CA directly (responderID identifies the issuer) OR a
21
- // CA-issued delegate valid at `time` bearing id-kp-OCSPSigning (anyEKU / absent EKU do NOT
21
+ // CA-issued delegate valid at `time` bearing id-kp-OCSPSigning (anyEKU / absent EKU do not
22
22
  // authorize), keyUsage-permits-signing, no unknown/malformed critical extension, and
23
23
  // id-pkix-ocsp-nocheck (a transport-free verify cannot otherwise confirm the responder is
24
24
  // unrevoked); the delegate's own issuance signature is verified under the issuer key.
25
- // - certIdMatches: serial + issuerNameHash + issuerKeyHash under the CertID's OWN hashAlgorithm
25
+ // - certIdMatches: serial + issuerNameHash + issuerKeyHash under the CertID's own hashAlgorithm
26
26
  // (a serial-only match is a cross-CA substitution and is rejected).
27
27
  // - currency: thisUpdate <= time and a bounded nextUpdate > time (a nextUpdate-less response is
28
28
  // unusable per the lightweight profile).
@@ -50,7 +50,7 @@ var OID_KEY_USAGE = oid.byName("keyUsage");
50
50
 
51
51
  function ocspDigest(alg, buf) { return subtle.digest(alg, buf).then(function (h) { return Buffer.from(h); }); }
52
52
 
53
- // The subjectPublicKey BIT STRING VALUE (past the unused-bits octet) of an SPKI DER -- the exact
53
+ // The subjectPublicKey BIT STRING value (past the unused-bits octet) of an SPKI DER: the exact
54
54
  // bytes an OCSP CertID issuerKeyHash / byKey KeyHash hash over (RFC 6960 sec. 4.1.1). Throws on a
55
55
  // malformed SPKI; the caller fails closed.
56
56
  function ocspKeyValue(spkiDer) {
@@ -59,7 +59,7 @@ function ocspKeyValue(spkiDer) {
59
59
 
60
60
  // makeOcspVerify(deps) -> the bound OCSP verify core. deps = { verifyWithSpki, decodeExt, findExt,
61
61
  // unrecognizedCriticalExtension, validateCriticalExtensionStructure, compositeKeyUsageCheck,
62
- // isNullOrAbsentParams, spliceSpkiParameters, dnEqual } -- the signature engine + RFC 5280
62
+ // isNullOrAbsentParams, spliceSpkiParameters, dnEqual }, the signature engine and RFC 5280
63
63
  // cert-profile gates owned by the caller.
64
64
  function makeOcspVerify(deps) {
65
65
  var verifyWithSpki = deps.verifyWithSpki;
@@ -99,8 +99,8 @@ function makeOcspVerify(deps) {
99
99
  return false;
100
100
  }
101
101
 
102
- // A SingleResponse's CertID names the target cert IFF serial AND issuerNameHash + issuerKeyHash
103
- // (under the CertID's OWN hashAlgorithm) match the issuer. `issuerNameCandidates` is every RFC
102
+ // A SingleResponse's CertID names the target cert if and only if serial, issuerNameHash and issuerKeyHash
103
+ // (under the CertID's own hashAlgorithm) match the issuer. `issuerNameCandidates` is every RFC
104
104
  // 5280 sec. 7.1-equal byte encoding of the validated issuer DN to try.
105
105
  async function ocspCertIdMatches(certID, cert, issuerNameCandidates, issuerKeyBits) {
106
106
  if (certID.serialNumberHex !== cert.serialNumberHex) return false;
@@ -170,13 +170,13 @@ function makeOcspVerify(deps) {
170
170
  return null;
171
171
  }
172
172
 
173
- // A BIT STRING is octet-aligned iff its unused-bits count is 0 -- reused from the caller's
173
+ // A BIT STRING is octet-aligned iff its unused-bits count is 0, reused from the caller's
174
174
  // guard so a non-octet-aligned responder signature is never verified.
175
175
  function isOctetAligned(bitString) { return !!bitString && bitString.unusedBits === 0; }
176
176
 
177
177
  // evaluateResponse(resp, cert, issuer, issuerKeyBits, issuerNameCandidates, time, historical) ->
178
- // a granular per-response SUMMARY for ONE parsed OCSPResponse, aggregating over ALL of its
179
- // matching SingleResponses (a revoked SingleResponse shadows a good one WITHIN the response, the
178
+ // a granular per-response summary for one parsed OCSPResponse, aggregating over all of its
179
+ // matching SingleResponses (a revoked SingleResponse shadows a good one within the response, the
180
180
  // same fail-closed law the multi-response aggregator applies). Never throws (fail-closed):
181
181
  // { applicable, responderAuthorized, signatureValid, matched, revoked:{reason,revocationReason}?,
182
182
  // sawGood, sawUnknownStatus, thisUpdate?, nextUpdate?, reason }