@blamejs/pki 0.5.6 → 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 +419 -378
  2. package/MIGRATING.md +65 -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 +24 -20
  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 +104 -104
  11. package/lib/cmc-verify.js +108 -32
  12. package/lib/cmp-build.js +31 -26
  13. package/lib/cmp-session.js +74 -72
  14. package/lib/cmp-verify.js +72 -58
  15. package/lib/cms-compress.js +8 -9
  16. package/lib/cms-decrypt.js +92 -76
  17. package/lib/cms-encrypt.js +33 -34
  18. package/lib/cms-sign.js +100 -54
  19. package/lib/cms-verify.js +141 -82
  20. package/lib/composite-sig.js +13 -13
  21. package/lib/constants.js +4 -4
  22. package/lib/crl-sign.js +31 -25
  23. package/lib/crl-verify.js +7 -6
  24. package/lib/crmf-sign.js +19 -15
  25. package/lib/csr-sign.js +13 -9
  26. package/lib/ct.js +37 -37
  27. package/lib/edwards-point.js +7 -7
  28. package/lib/est.js +103 -59
  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 +378 -15
  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 +144 -75
  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 +32 -20
  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 +71 -56
  63. package/lib/pki-build.js +23 -22
  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 +169 -36
  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 +19 -16
  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 +22 -18
  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 +21 -20
  99. package/lib/webauthn-mds.js +67 -67
  100. package/lib/webauthn.js +34 -34
  101. package/lib/webcrypto.js +15 -15
  102. package/lib/x509-sign.js +24 -15
  103. package/package.json +3 -2
  104. package/sbom.cdx.json +6 -6
package/lib/crl-sign.js CHANGED
@@ -19,8 +19,8 @@
19
19
  // The signature matrix comes from the shared sign-scheme resolver (the same registry pki.x509.sign /
20
20
  // pki.cms.sign drive), so a new algorithm is a registry row, never a branch here. The TBSCertList DER is
21
21
  // assembled through the canonical asn1.build.* layer + the shared pki-build producing primitives; the
22
- // strict schema-crl decoder round-trips it and OpenSSL cross-checks it. VERIFY composes the ONE
23
- // path-validate.verifyCrlSignature engine pki.path.crlChecker uses -- there is no second, weaker verifier.
22
+ // strict schema-crl decoder round-trips it and OpenSSL cross-checks it. Verify composes the single
23
+ // path-validate.verifyCrlSignature engine pki.path.crlChecker uses; there is no second, weaker verifier.
24
24
 
25
25
  var asn1 = require("./asn1-der");
26
26
  var oid = require("./oid");
@@ -51,7 +51,7 @@ function _signE(kind, message, cause) { return new CrlError("crl/" + kind, messa
51
51
  function O(n) { return oid.byName(n); }
52
52
 
53
53
  // The shared PKIX producing primitives (lib/pki-build.js), bound to the crl namespace so they keep the
54
- // frozen crl/* codes (Hard rule #5 -- one encode definition, no per-format hand-roll).
54
+ // frozen crl/* codes (Hard rule #5: one encode definition, no per-format hand-roll).
55
55
  var _b = pkiBuild.makeBuilder({ ErrorClass: CrlError, prefix: "crl", O: O, NS: NS, NAME_SCHEMA: NAME_SCHEMA, SPKI_SCHEMA: SPKI_SCHEMA, EXT_DECODERS: {} });
56
56
  var _encodeName = _b.encodeName, _isEmptyName = _b.isEmptyName, _reqDer = _b.reqDer,
57
57
  _assertValidSpki = _b.assertValidSpki, _assertValidExtension = _b.assertValidExtension,
@@ -88,16 +88,16 @@ var REQUIRED_ENTRY_CRITICALITY = {};
88
88
 
89
89
  // certificateIssuer (indirect CRLs) is deferred: pki.path.crlChecker skips a CRL carrying any critical entry
90
90
  // extension other than reasonCode (it does not track the per-entry issuer of an indirect CRL), so a CRL
91
- // signed with certificateIssuer would verify but never be authoritative for revocation. Reject it -- on both
92
- // the object form and the pre-encoded hatch -- until the checker processes indirect CRLs.
91
+ // signed with certificateIssuer would verify but never be authoritative for revocation. Reject it, on both
92
+ // the object form and the pre-encoded hatch, until the checker processes indirect CRLs.
93
93
  var _CERT_ISSUER_DEFERRED = "certificateIssuer (indirect CRLs) is not yet supported -- pki.path.crlChecker skips a CRL carrying any critical entry extension other than reasonCode, so an indirect CRL would never be authoritative for revocation. Re-enabled once the checker processes certificateIssuer / indirect CRLs.";
94
- // The IDP indirectCRL flag marks the whole CRL as indirect, which crlChecker skips outright -- deferred for
94
+ // The IDP indirectCRL flag marks the whole CRL as indirect, which crlChecker skips outright. Deferred for
95
95
  // the same reason as certificateIssuer, on both the object form and the pre-encoded hatch.
96
96
  var _INDIRECT_IDP_DEFERRED = "issuingDistributionPoint indirectCRL (indirect CRLs) is not yet supported -- pki.path.crlChecker skips a CRL whose IDP marks it indirect, so it would never be authoritative for revocation. Re-enabled once the checker processes indirect CRLs.";
97
97
 
98
98
  // ---- shared numeric / reason / key-id helpers ------------------------------
99
99
 
100
- // A non-negative INTEGER (0..MAX) of at most 20 content octets -- cRLNumber (sec. 5.2.3) and
100
+ // A non-negative INTEGER (0..MAX) of at most 20 content octets: cRLNumber (sec. 5.2.3) and
101
101
  // deltaCRLIndicator baseCRLNumber (sec. 5.2.4). b.integer has no size cap of its own (unlike serialInteger).
102
102
  function _boundedInteger(v, label) {
103
103
  var n;
@@ -113,7 +113,7 @@ function _boundedInteger(v, label) {
113
113
  }
114
114
 
115
115
  // Decode a pre-encoded Extension's extnValue (the trailing OCTET STRING's content) to one well-formed DER
116
- // node, or reject -- so a recognized extension's value is validated on the escape hatch, not emitted opaque.
116
+ // node, or reject, so a recognized extension's value is validated on the escape hatch, not emitted opaque.
117
117
  function _extInner(node, label) {
118
118
  try { return asn1.decode(asn1.read.octetString(node.children[node.children.length - 1])); }
119
119
  catch (e) { throw _err("crl/bad-input", "pre-encoded " + label + " extension value is not valid DER", e); }
@@ -144,7 +144,7 @@ function _resolveReason(reason, isDelta) {
144
144
  // The AKI keyIdentifier from the issuer: an explicit Buffer, or true -> the issuer cert's subjectKeyIdentifier,
145
145
  // else the SHA-1 of the issuer SPKI (RFC 5280 sec. 5.2.1 key-identifier method).
146
146
  function _akiKeyId(val, ctx) {
147
- if (Buffer.isBuffer(val)) return val;
147
+ if (Buffer.isBuffer(val)) return guard.bytes.snapshot(val, CrlError, "crl/bad-input", "the authorityKeyIdentifier keyIdentifier");
148
148
  if (val === true) {
149
149
  if (ctx.issuerCert) {
150
150
  var ski = (ctx.issuerCert.extensions || []).filter(function (e) { return e.oid === OID_SKI; })[0];
@@ -193,7 +193,7 @@ function _validatePreEncodedIdp(inner) {
193
193
  if (!inner.children || !inner.children.length) throw _err("crl/bad-idp", "pre-encoded issuingDistributionPoint MUST NOT be empty (RFC 5280 sec. 5.2.5)");
194
194
  var f = schema.walk(_PRE_ENCODED_IDP_SCHEMA, inner, NS).fields;
195
195
  // A present DEFAULT-FALSE flag encodes a default DER omits (X.690 sec. 11.5), so it is rejected
196
- // whichever value it carries rather than counted as the FALSE it spells.
196
+ // whichever value it carries, never counted as the FALSE it spells.
197
197
  ["onlyContainsUserCerts", "onlyContainsCACerts", "onlyContainsAttributeCerts"].forEach(function (name) {
198
198
  if (f[name].present && f[name].value !== true) throw _err("crl/bad-idp", "pre-encoded issuingDistributionPoint " + name + " encodes its DEFAULT FALSE, which DER omits (X.690 sec. 11.5)");
199
199
  });
@@ -305,7 +305,7 @@ function _buildCrlExtensions(spec, ctx) {
305
305
 
306
306
  // The revokedCertificates entries (sec. 5.1.2.6 / 5.3). Each entry: serial + revocationDate + optional
307
307
  // crlEntryExtensions (reasonCode ENUMERATED, invalidityDate GeneralizedTime-only, certificateIssuer, or a
308
- // pre-encoded escape hatch). Returns { entries, anyExt } -- anyExt forces v2 (sec. 5.1.2.1).
308
+ // pre-encoded escape hatch). Returns { entries, anyExt }, where anyExt forces v2 (sec. 5.1.2.1).
309
309
  function _buildRevoked(entryList, isDelta) {
310
310
  if (!Array.isArray(entryList)) throw _err("crl/bad-input", "revoked must be an array of revoked-certificate entries");
311
311
  var anyExt = false, seenSerials = {};
@@ -332,7 +332,7 @@ function _buildRevoked(entryList, isDelta) {
332
332
  }
333
333
  if (e.invalidityDate != null) {
334
334
  guard.time.assertValid(e.invalidityDate, _err, "crl/bad-input", "invalidityDate");
335
- // sec. 5.3.2 -- ALWAYS GeneralizedTime (never the UTCTime cutover), no fractional seconds.
335
+ // sec. 5.3.2: always GeneralizedTime (never the UTCTime cutover), no fractional seconds.
336
336
  pushE("invalidityDate", false, b.generalizedTime(e.invalidityDate));
337
337
  }
338
338
  if (e.certificateIssuer != null) throw _err("crl/bad-input", _CERT_ISSUER_DEFERRED);
@@ -482,15 +482,15 @@ function _sign(spec, issuer, opts) {
482
482
  *
483
483
  * The version is derived from the field set (v2 when any CRL or entry extension is present, else v1). The
484
484
  * outer `signatureAlgorithm` is emitted from the same source as `tbsCertList.signature` (sec. 5.1.1.2); an
485
- * empty revocation list omits `revokedCertificates` rather than emitting an empty SEQUENCE (sec. 5.1.2.6);
485
+ * empty revocation list omits `revokedCertificates` instead of emitting an empty SEQUENCE (sec. 5.1.2.6);
486
486
  * `reasonCode` is an ENUMERATED and `invalidityDate` is always GeneralizedTime (sec. 5.3.1/5.3.2);
487
487
  * per-extension criticality is fixed by the RFC; and the produced signature is verified under the issuer
488
- * key before return. A violation throws a typed `CrlError`; where the spec carries raw DER -- an issuer
489
- * `Name` Buffer or a pre-encoded `Extension` -- a malformed leaf inside those bytes throws `Asn1Error`.
488
+ * key before return. A violation throws a typed `CrlError`; where the spec carries raw DER (an issuer
489
+ * `Name` Buffer or a pre-encoded `Extension`), a malformed leaf inside those bytes throws `Asn1Error`.
490
490
  *
491
491
  * @opts
492
492
  * - `pem` (boolean) -- return a PEM `X509 CRL` string instead of DER.
493
- * - `pss` (boolean) -- sign an RSA key with RSASSA-PSS rather than PKCS#1 v1.5.
493
+ * - `pss` (boolean) -- sign an RSA key with RSASSA-PSS instead of PKCS#1 v1.5.
494
494
  * - `digestAlgorithm` (string) -- override the message digest where the algorithm permits a choice.
495
495
  * @example
496
496
  * var pair = await pki.key.generate("Ed25519");
@@ -507,7 +507,13 @@ function _sign(spec, issuer, opts) {
507
507
  * }, { cert: signerCertDer, key: signerKeyPkcs8 });
508
508
  * pki.schema.crl.parse(der).revokedCertificates[0].serialNumberHex; // "1234"
509
509
  */
510
- function sign(spec, issuer, opts) { return Promise.resolve().then(function () { return _sign(spec, issuer, opts); }); }
510
+ function sign(spec, issuer, opts) {
511
+ // Every caller-owned argument copied at entry and released when the call settles -- see the note
512
+ // on the same call in x509-sign.
513
+ return guard.bytes.fixedCall(CrlError, "crl/bad-input", [
514
+ [spec, "the CRL spec"], [issuer, "the issuer"], [opts, "pki.crl.sign options"],
515
+ ], _sign);
516
+ }
511
517
 
512
518
  // A CRL these verbs answer from is re-derived from the bytes its parser read. Completeness -- every
513
519
  // field present with the right type -- is not enough for a verdict: the signature covers a byte
@@ -578,8 +584,8 @@ function _issuerMaySign(parsed, cert) {
578
584
  * EdDSA low-order-point gates -- so there is no second, weaker CRL verifier. It fails closed to `false` on
579
585
  * any resolution, import, or verification fault; malformed input throws a typed `CrlError`.
580
586
  *
581
- * Given a CERTIFICATE rather than a bare key, it also asks what only a certificate can answer: that
582
- * the certificate is the issuer this CRL names, and that its keyUsage -- when it carries one -- asserts
587
+ * Given a certificate in place of a bare key, it also asks what only a certificate can answer: that
588
+ * the certificate is the issuer this CRL names, and that its keyUsage, when it carries one, asserts
583
589
  * `cRLSign` (RFC 5280 sec. 4.2.1.3, the same rule this module's signing side already enforces). Either
584
590
  * failing is `false`: a statement about the CRL, not about the caller's input, so trying each candidate
585
591
  * issuer in turn still works. A signature verifying says only that SOME key signed these bytes; without
@@ -598,7 +604,7 @@ function _issuerMaySign(parsed, cert) {
598
604
  * { cert: signerCertDer, key: signerKeyPkcs8 });
599
605
  * var ok = await pki.crl.verify(crlDer, { publicKey: signerSpki }); // true / false
600
606
  */
601
- function verify(crl, issuer) { return Promise.resolve().then(function () { return _verify(crl, issuer); }); }
607
+ function verify(crl, issuer) { return guard.async.deferred(function () { return _verify(crl, issuer); }); }
602
608
  function _verify(crl, issuer) {
603
609
  var parsed = _coerceCrl(crl);
604
610
  var resolved = _resolveIssuer(issuer);
@@ -633,17 +639,17 @@ function _serialHexOf(serial) {
633
639
  * PEM string, or a parsed CRL; `serialNumber` is a `BigInt`, a safe integer, a decimal / `0x`-hex string,
634
640
  * or a magnitude `Buffer`. Returns the matching revoked-certificate entry (`{ serialNumber, serialNumberHex,
635
641
  * revocationDate, crlEntryExtensions }`) or `null` when the serial is not listed. A structural lookup only --
636
- * it does NOT verify the CRL signature or its currency; call `pki.crl.verify` / `pki.path.crlChecker` for that.
642
+ * it does not verify the CRL signature or its currency; call `pki.crl.verify` / `pki.path.crlChecker` for that.
637
643
  *
638
- * It does check SCOPE first, because a serial number means something only within the set of
639
- * certificates a CRL speaks for, and this verb is given a serial and nothing else -- so a CRL that
640
- * speaks for part of its issuer's certificates is refused rather than answered from:
644
+ * It does check scope first, because a serial number means something only within the set of
645
+ * certificates a CRL speaks for, and this verb is given a serial and nothing else. So a CRL that
646
+ * speaks for part of its issuer's certificates is refused, never answered from:
641
647
  *
642
648
  * - A DELTA CRL lists changes since a base, so a serial in it may be there to say the certificate
643
649
  * was RELEASED; read alone, the entry meaning "no longer revoked" reads as "revoked"
644
650
  * (`crl/delta-not-authoritative`). Merge it with its base through `pki.path.crlChecker`.
645
651
  * - An INDIRECT CRL carries entries for other issuers, whose serials are unrelated to yours
646
- * (`crl/indirect-not-supported`) -- as does any CRL carrying `certificateIssuer` on an entry
652
+ * (`crl/indirect-not-supported`), as does any CRL carrying `certificateIssuer` on an entry
647
653
  * while not declaring itself indirect, a contradiction about whose certificates it lists.
648
654
  * - Any other `issuingDistributionPoint` narrows the CRL to one distribution point, one kind of
649
655
  * certificate, or a subset of revocation reasons (`crl/scope-not-authoritative`). Which part
package/lib/crl-verify.js CHANGED
@@ -2,12 +2,13 @@
2
2
  // Copyright (c) blamejs contributors
3
3
  "use strict";
4
4
  //
5
- // @internal -- the ONE CRL signature-verify seam, shared by pki.path.crlChecker and pki.crl.verify so both
6
- // route through the SAME signature engine (algorithm-confusion + EdDSA low-order + composite gates) and no
7
- // second, weaker CRL verifier can drift. path-validate owns that engine (_verifyWithSpki) and injects it
8
- // here at its module load (setEngine). Keeping the seam in this internal module -- rather than on
9
- // path-validate's module.exports -- keeps it OFF the public pki.path surface (it takes the path-internal
10
- // SubjectPublicKeyInfo bytes, not a documented issuer shape). NOT wired into index.js; reached only by require.
5
+ // @internal -- the single CRL signature-verify seam, shared by pki.path.crlChecker and pki.crl.verify
6
+ // so both route through one signature engine (algorithm-confusion + EdDSA low-order + composite
7
+ // gates) and no second, weaker CRL verifier can drift. path-validate owns that engine
8
+ // (_verifyWithSpki) and injects it here at its module load (setEngine). The seam lives in this
9
+ // internal module, not on path-validate's module.exports, which keeps it off the public pki.path
10
+ // surface: it takes the path-internal SubjectPublicKeyInfo bytes, not a documented issuer shape.
11
+ // It is not wired into index.js and is reached only by require.
11
12
 
12
13
  var guard = require("./guard-all");
13
14
 
package/lib/crmf-sign.js CHANGED
@@ -50,8 +50,8 @@ var KNOWN_SPEC_KEYS = { certReqId: 1, certTemplate: 1, controls: 1, regInfo: 1,
50
50
  var KNOWN_BATCH_KEYS = { messages: 1 };
51
51
  var KNOWN_TEMPLATE_KEYS = { version: 1, subject: 1, publicKey: 1, validity: 1, extensions: 1, issuer: 1 };
52
52
  var REVOCATION_TEMPLATE_KEYS = { version: 1, subject: 1, publicKey: 1, validity: 1, extensions: 1, issuer: 1, serialNumber: 1 };
53
- // The controls (RFC 4211 sec. 6) and regInfo (sec. 7) are DISJOINT AttributeTypeAndValue namespaces --
54
- // regToken/authenticator/oldCertID/protocolEncrKey are controls; utf8Pairs is regInfo -- so the object
53
+ // The controls (RFC 4211 sec. 6) and regInfo (sec. 7) are disjoint AttributeTypeAndValue namespaces:
54
+ // regToken/authenticator/oldCertID/protocolEncrKey are controls and utf8Pairs is regInfo, so the object
55
55
  // form validates each key against its own field's registry (a control name is not a valid regInfo, and
56
56
  // vice versa). The key name equals the registered OID name; complex values (pkiPublicationInfo, certReq)
57
57
  // ride the pre-encoded escape hatch. Each entry is the value encoder for that OID.
@@ -69,7 +69,7 @@ function _encodeOptionalValidity(validity) {
69
69
  var nb = validity.notBefore, na = validity.notAfter;
70
70
  if (nb == null && na == null) throw _err("crmf/bad-validity", "validity must contain notBefore or notAfter (RFC 4211 sec. 5)");
71
71
  var parts = [];
72
- // timeDer validates each instant (guard.time.assertValid throws on an Invalid Date) BEFORE the
72
+ // timeDer validates each instant (guard.time.assertValid throws on an Invalid Date) before the
73
73
  // inverted-window comparison, so getTime() below cannot be NaN.
74
74
  if (nb != null) parts.push(b.explicit(0, _b.timeDer(nb, "validity notBefore")));
75
75
  if (na != null) parts.push(b.explicit(1, _b.timeDer(na, "validity notAfter")));
@@ -77,13 +77,13 @@ function _encodeOptionalValidity(validity) {
77
77
  if (nb != null && na != null && nb.getTime() > na.getTime()) throw _err("crmf/bad-validity", "notBefore must not be after notAfter");
78
78
  return b.implicit(4, b.sequence(parts));
79
79
  }
80
- // CertTemplate ::= SEQUENCE { [0..9] all IMPLICIT OPTIONAL }. A REQUEST omits serialNumber [1] / signingAlg
81
- // [2] / issuerUID [7] / subjectUID [8] (CA-assigned or deprecated, RFC 4211 sec. 5) -- the builder never
80
+ // CertTemplate ::= SEQUENCE { [0..9] all IMPLICIT OPTIONAL }. A request omits serialNumber [1] / signingAlg
81
+ // [2] / issuerUID [7] / subjectUID [8] (CA-assigned or deprecated, RFC 4211 sec. 5), and the builder never
82
82
  // emits them. Fields are emitted in ascending tag order.
83
83
  function _encodeCertTemplate(tpl, opts) {
84
84
  if (!tpl || typeof tpl !== "object" || Buffer.isBuffer(tpl)) throw _err("crmf/bad-cert-template", "certTemplate must be an object");
85
- // A REQUEST template omits serialNumber (CA-assigned, RFC 4211 sec. 5); a REVOCATION template (CMP rr,
86
- // RFC 9810 sec. 5.3.9) carries serialNumber [1] to name the certificate to revoke -- allowed only then.
85
+ // A request template omits serialNumber (CA-assigned, RFC 4211 sec. 5); a revocation template (CMP rr,
86
+ // RFC 9810 sec. 5.3.9) carries serialNumber [1] to name the certificate to revoke, allowed only then.
87
87
  var allowed = (opts && opts.revocation) ? REVOCATION_TEMPLATE_KEYS : KNOWN_TEMPLATE_KEYS;
88
88
  guard.identifier.assertKnownKeys(tpl, allowed, _err, "crmf/bad-input", "unknown certTemplate field ");
89
89
  var fields = [];
@@ -158,8 +158,8 @@ function _encodeCertId(id) {
158
158
  return b.sequence([_b.encodeGeneralName(id.issuer), _b.serialInteger(id.serialNumber)]);
159
159
  }
160
160
 
161
- // ProofOfPossession. The signature arm (the default when a key is given): when the template carries BOTH
162
- // subject and publicKey (complete), sign the CertRequest DER and OMIT poposkInput; otherwise build a
161
+ // ProofOfPossession. The signature arm (the default when a key is given): when the template carries both
162
+ // subject and publicKey (complete), sign the CertRequest DER and omit poposkInput; otherwise build a
163
163
  // POPOSigningKeyInput (authInfo sender [0] GeneralName + the requested publicKey), sign its SEQUENCE, and
164
164
  // carry it as poposkInput [0] (RFC 4211 sec. 4.1). raVerified is emitted only on an explicit opt-in.
165
165
  function _buildProofOfPossession(pop, certReqDer, template, signingKey, opts) {
@@ -209,8 +209,8 @@ function _buildProofOfPossession(pop, certReqDer, template, signingKey, opts) {
209
209
  * @related pki.schema.crmf.parse, pki.csr.sign
210
210
  *
211
211
  * Build and DER-encode an RFC 4211 `CertReqMessages`. `spec` describes one certificate request message (or
212
- * pass `spec.messages` -- an array of specs -- for a batch): `certReqId` (an integer, default 0; the RFC
213
- * 9483 `-1` sentinel is allowed), `certTemplate` (the requested certificate fields -- `subject`, `publicKey`
212
+ * pass `spec.messages`, an array of specs, for a batch): `certReqId` (an integer, default 0; the RFC
213
+ * 9483 `-1` sentinel is allowed), `certTemplate` (the requested certificate fields: `subject`, `publicKey`
214
214
  * (the SPKI DER of the key being certified), `validity` ({ notBefore, notAfter } Dates), `extensions` (an
215
215
  * object of subjectAltName / keyUsage / extendedKeyUsage / basicConstraints / certificatePolicies /
216
216
  * subjectKeyIdentifier, or pre-encoded Extension DER), and an optional `version` (2)), optional `controls`
@@ -219,14 +219,14 @@ function _buildProofOfPossession(pop, certReqDer, template, signingKey, opts) {
219
219
  * REQUESTER's private key -- the private half of `certTemplate.publicKey`; the message carries a
220
220
  * `POPOSigningKey` proof of possession signed with it (verified before the message is returned), exactly
221
221
  * as a PKCS#10 CSR proves possession. The signature algorithm is resolved from the requested public key
222
- * (RSA PKCS#1 v1.5 / PSS, ECDSA, EdDSA, ML-DSA, SLH-DSA, or a composite arm). `key` is optional -- omit it
222
+ * (RSA PKCS#1 v1.5 / PSS, ECDSA, EdDSA, ML-DSA, SLH-DSA, or a composite arm). `key` is optional: omit it
223
223
  * for a `raVerified` proof (opt in with `pop: { type: 'raVerified', raVerified: true }`). Returns DER, or a
224
224
  * PEM block with `opts.pem` (the label is required). Malformed input throws a typed `CrmfError`.
225
225
  * Certificate-request-message parsing is `pki.schema.crmf.parse`.
226
226
  *
227
227
  * @opts
228
228
  * - `pem` (string) -- return a PEM block with this label instead of DER (e.g. "CERTIFICATE REQUEST MESSAGE").
229
- * - `pss` (boolean) -- sign an RSA key with RSASSA-PSS rather than PKCS#1 v1.5.
229
+ * - `pss` (boolean) -- sign an RSA key with RSASSA-PSS instead of PKCS#1 v1.5.
230
230
  * - `digestAlgorithm` (string) -- override the message digest where the algorithm permits a choice.
231
231
  * @example
232
232
  * var pair = await pki.key.generate("Ed25519");
@@ -238,7 +238,11 @@ function _buildProofOfPossession(pop, certReqDer, template, signingKey, opts) {
238
238
  * pki.schema.crmf.parse(msg).messages[0].certReq.certTemplate.subject.dn; // "CN=device-42"
239
239
  */
240
240
  function build(spec, key, opts) {
241
- return Promise.resolve().then(function () { return _build(spec, key, opts); });
241
+ // Every caller-owned argument copied at entry and released when the call settles -- see the note
242
+ // on the same call in x509-sign.
243
+ return guard.bytes.fixedCall(CrmfError, "crmf/bad-input", [
244
+ [spec, "the certificate-request-message spec"], [key, "the signing key"], [opts, "pki.crmf.build options"],
245
+ ], _build);
242
246
  }
243
247
 
244
248
  function _buildCertReqMsg(spec, key, opts) {
@@ -294,7 +298,7 @@ function _build(spec, key, opts) {
294
298
  * @spec RFC 4211
295
299
  * @related pki.crmf.build
296
300
  *
297
- * Encode a bare RFC 4211 `CertTemplate` (the requested-certificate fields -- `subject`, `publicKey`,
301
+ * Encode a bare RFC 4211 `CertTemplate` (the requested-certificate fields: `subject`, `publicKey`,
298
302
  * `validity`, requested `extensions`, an optional `version` 2, `issuer`) to canonical DER. This is the
299
303
  * `certTemplate` interior of `pki.crmf.build`, exposed for the RFC 9810 CMP `rr` (revocation request) body,
300
304
  * whose `RevDetails.certDetails` carries a `CertTemplate` naming the certificate to revoke. Returns the DER
package/lib/csr-sign.js CHANGED
@@ -7,8 +7,8 @@
7
7
  * @nav Signing
8
8
  * @title Certification requests
9
9
  * @intro The PKCS#10 certification-request producing side. `pki.csr.sign` builds a
10
- * `CertificationRequestInfo`, signs it with the SUBJECT's own private key (proof of possession -- a
11
- * CSR has no issuer), and emits a `CertificationRequest` (RFC 2986) that `pki.schema.csr.parse`,
10
+ * `CertificationRequestInfo`, signs it with the subject's own private key (proof of possession, since
11
+ * a CSR has no issuer), and emits a `CertificationRequest` (RFC 2986) that `pki.schema.csr.parse`,
12
12
  * OpenSSL, and a CA enrollment pipeline all accept. Requested v3 extensions ride in a PKCS#9
13
13
  * `extensionRequest` attribute (RFC 2985) a CA copies into the issued certificate. Parsing lives at
14
14
  * `pki.schema.csr.parse`.
@@ -73,22 +73,22 @@ function _challengePassword(pw) {
73
73
  * @defends forged-certification-request (CWE-347)
74
74
  * @related pki.schema.csr.parse, pki.x509.sign
75
75
  *
76
- * Build, sign, and DER-encode a PKCS#10 certification request. `spec` describes the request -- `subject`
76
+ * Build, sign, and DER-encode a PKCS#10 certification request. `spec` describes the request: `subject`
77
77
  * (a common-name string, an array of RDNs, or raw Name DER; MAY be empty), `subjectPublicKey` (the SPKI
78
- * DER of the key being certified), and optional `extensionRequest` (requested v3 extensions -- an object
78
+ * DER of the key being certified), and optional `extensionRequest` (requested v3 extensions, as an object
79
79
  * of subjectAltName / keyUsage / extendedKeyUsage / basicConstraints / certificatePolicies /
80
80
  * subjectKeyIdentifier, or an array of pre-encoded Extension DER) and `challengePassword`. `key` (or
81
- * `{ key }`) is the SUBJECT's own PKCS#8 private key / WebCrypto CryptoKey -- the request is self-signed
81
+ * `{ key }`) is the subject's own PKCS#8 private key / WebCrypto CryptoKey, so the request is self-signed
82
82
  * to prove possession of the private half of `subjectPublicKey`, and that proof is verified before the
83
83
  * request is returned. The signature algorithm is resolved from the subject key (RSA PKCS#1 v1.5 or PSS,
84
84
  * ECDSA, EdDSA, ML-DSA, SLH-DSA, or a composite arm). Returns DER, or a PEM `CERTIFICATE REQUEST` with
85
- * `opts.pem`. Malformed input throws a typed `CsrError`; where the spec carries raw DER -- a `Name`
86
- * Buffer, a pre-encoded requested `Extension` or `Attribute` -- a malformed leaf inside those bytes
85
+ * `opts.pem`. Malformed input throws a typed `CsrError`; where the spec carries raw DER (a `Name`
86
+ * Buffer, a pre-encoded requested `Extension` or `Attribute`) a malformed leaf inside those bytes
87
87
  * throws `Asn1Error` instead. Certificate-request parsing is `pki.schema.csr.parse`.
88
88
  *
89
89
  * @opts
90
90
  * - `pem` (boolean) -- return a PEM `CERTIFICATE REQUEST` string instead of DER.
91
- * - `pss` (boolean) -- sign an RSA key with RSASSA-PSS rather than PKCS#1 v1.5.
91
+ * - `pss` (boolean) -- sign an RSA key with RSASSA-PSS instead of PKCS#1 v1.5.
92
92
  * - `digestAlgorithm` (string) -- override the message digest where the algorithm permits a choice.
93
93
  * @example
94
94
  * var pair = await pki.key.generate("Ed25519");
@@ -101,7 +101,11 @@ function _challengePassword(pw) {
101
101
  * pki.schema.csr.parse(req).subject.dn; // "CN=req.example.com"
102
102
  */
103
103
  function sign(spec, key, opts) {
104
- return Promise.resolve().then(function () { return _sign(spec, key, opts); });
104
+ // Every caller-owned argument copied at entry and released when the call settles -- see the note
105
+ // on the same call in x509-sign.
106
+ return guard.bytes.fixedCall(CsrError, "csr/bad-input", [
107
+ [spec, "the certification-request spec"], [key, "the signing key"], [opts, "pki.csr.sign options"],
108
+ ], _sign);
105
109
  }
106
110
 
107
111
  function _sign(spec, key, opts) {
package/lib/ct.js CHANGED
@@ -15,19 +15,19 @@
15
15
  * certificate timestamps.
16
16
  *
17
17
  * The SCT payload is encoded in the TLS presentation language (RFC 8446 sec. 3 /
18
- * RFC 5246 sec. 4 conventions) -- positional, tag-less, fixed-width big-endian
19
- * integers and length-prefixed opaque vectors -- NOT ASN.1/DER. So this module
20
- * owns a bounded big-endian TLS-struct reader rather than composing the DER
18
+ * RFC 5246 sec. 4 conventions): positional, tag-less, fixed-width big-endian
19
+ * integers and length-prefixed opaque vectors, never ASN.1/DER. So this module
20
+ * owns a bounded big-endian TLS-struct reader instead of composing the DER
21
21
  * schema engine; the only ASN.1 surface is the sec. 3.3 double wrap (the
22
22
  * extension value is a DER OCTET STRING whose content is another DER OCTET
23
- * STRING whose content is the TLS list -- the certificate/OCSP layer peels the
23
+ * STRING whose content is the TLS list; the certificate/OCSP layer peels the
24
24
  * outer, this module peels the inner).
25
25
  *
26
- * Structure is decoded, crypto is surfaced RAW: each SCT surfaces its `logId`
27
- * (32 raw bytes -- SHA-256 of the log's SPKI, never recomputed), the exact
26
+ * Structure is decoded, crypto is surfaced raw: each SCT surfaces its `logId`
27
+ * (32 raw bytes, the SHA-256 of the log's SPKI, never recomputed), the exact
28
28
  * `timestamp` as a BigInt, the raw `extensions`, the named-but-not-interpreted
29
- * `hashAlg`/`sigAlg` code points, and the raw `signature`. The parser NEVER
30
- * verifies a signature, recomputes a LogID, or trusts a log -- a verifier
29
+ * `hashAlg`/`sigAlg` code points, and the raw `signature`. The parser never
30
+ * verifies a signature, recomputes a LogID, or trusts a log. A verifier
31
31
  * composes `webcrypto` over `reconstructSignedData(...)`, the exact
32
32
  * `digitally-signed` preimage. DER-only carrier, fail-closed.
33
33
  *
@@ -58,10 +58,10 @@ var C = constants;
58
58
 
59
59
  // (code, message[, cause]) -> CtError, the factory the composed guards + the shared transport throw
60
60
  // through so a malformed value keeps the ct/* typed verdict (the optional cause carries the underlying
61
- // transport / evaluation error; existing 2-arg callers pass undefined -- behavior-preserving).
61
+ // transport / evaluation error; existing 2-arg callers pass undefined, so the change preserves behavior).
62
62
  function _ctErr(c, m, cause) { return new CtError(c, m, cause); }
63
63
 
64
- // RFC 5246 sec. 7.4.1.4.1 code points -- 1-byte, NOT OIDs. Surfaced named; an
64
+ // RFC 5246 sec. 7.4.1.4.1 code points: 1-byte values, never OIDs. Surfaced named; an
65
65
  // unknown code surfaces as its numeric byte with a null name (never rejected --
66
66
  // off-profile-pair rejection is a verifier-tier log-conformance concern).
67
67
  var HASH_ALGORITHMS = {
@@ -75,7 +75,7 @@ var SCT_MIN_BODY = 47;
75
75
  var LOGID_BYTES = 32;
76
76
  var MAX_SAFE = 9007199254740991n; // 2^53 - 1; above this a Number loses precision
77
77
 
78
- // ---- TlsReader -- the RFC 6962 TLS-vector cursor, the shared bounded big-endian
78
+ // ---- TlsReader: the RFC 6962 TLS-vector cursor, the shared bounded big-endian
79
79
  // ByteReader engine primitive bound to the ct/* fault domain. A lying inner length
80
80
  // can overrun only the current sub-reader's `end`, never the parent buffer, so
81
81
  // bounds-before-slice is structural (see lib/byte-reader.js).
@@ -102,7 +102,7 @@ function _toBuffer(v, field) {
102
102
  // version this parser does not define is preserved OPAQUE, not rejected: RFC 6962
103
103
  // sec. 3.3 gives every SerializedSCT its own length prefix precisely so a client "can
104
104
  // still parse old SCTs while skipping over new SCTs whose versions they don't
105
- // understand" -- so an unknown version yields { unknown, version, rawSct } and the
105
+ // understand", so an unknown version yields { unknown, version, rawSct } and the
106
106
  // v1-specific field decode (and the 47-byte floor) is skipped.
107
107
  function _parseSct(r, sctLen) {
108
108
  var bodyStart = r.pos;
@@ -156,8 +156,8 @@ function _parseSct(r, sctLen) {
156
156
  * `extensions` (raw Buffer), `hashAlg` / `sigAlg` (1-byte code points) + a named
157
157
  * `signatureAlgorithm`, the raw `signature` Buffer, and `rawSct` (the full
158
158
  * SerializedSCT body). A SerializedSCT whose version this parser does not define
159
- * is preserved OPAQUE in `unknownScts` as `{ version, rawSct }` rather than
160
- * failing the list -- RFC 6962 sec. 3.3 frames each SerializedSCT with its own length
159
+ * is preserved opaque in `unknownScts` as `{ version, rawSct }` and does not fail
160
+ * the list: RFC 6962 sec. 3.3 frames each SerializedSCT with its own length
161
161
  * so unknown versions are skippable (forward compatibility). `all` lists every
162
162
  * SerializedSCT (known and unknown) in the exact wire order, so
163
163
  * `encodeSctList(all)` reproduces the list byte-identically even when the two
@@ -166,7 +166,7 @@ function _parseSct(r, sctLen) {
166
166
  * The extension value is a DER `OCTET STRING` wrapping the TLS-encoded list
167
167
  * (RFC 6962 sec. 3.3 double wrap); everything below that peel is TLS presentation
168
168
  * language, decoded with a bounded cursor. Structure is decoded, crypto is
169
- * surfaced RAW -- the signature is never verified and the LogID never recomputed.
169
+ * surfaced raw: the signature is never verified and the LogID never recomputed.
170
170
  *
171
171
  * Throws `CtError` with a stable `ct/*` code on any malformed input (a bad inner
172
172
  * DER wrap is `ct/bad-der` with the `asn1/*` fault as `.cause`), never a raw
@@ -249,7 +249,7 @@ function _u24Bytes(n) {
249
249
  * Rebuild the exact `digitally-signed` preimage bytes an external verifier
250
250
  * hashes to check an SCT's signature (RFC 6962 sec. 3.2), for a parsed `sct`.
251
251
  * `entry` selects the log-entry arm:
252
- * - `{ entryType: 0, leafCert: <DER Buffer> }` -- an SCT delivered over TLS /
252
+ * - `{ entryType: 0, leafCert: <DER Buffer> }`, an SCT delivered over TLS /
253
253
  * OCSP, signed over `x509_entry(0)` with the leaf certificate.
254
254
  * - `{ entryType: 1, tbsCertificate: <DER Buffer>, issuerKeyHash: <32B> }` --
255
255
  * an SCT EMBEDDED in a certificate, signed over `precert_entry(1)` with the
@@ -378,12 +378,12 @@ function _spkiAlg(spki) {
378
378
  * (`{ entryType: 0, leafCert }` or `{ entryType: 1, tbsCertificate, issuerKeyHash }`,
379
379
  * as for `reconstructSignedData`), `sct` a decoded v1 SCT from `parseSctList().scts[]`,
380
380
  * and `logPublicKey` the log's SubjectPublicKeyInfo (DER `Buffer`). Reconstructs the exact
381
- * signed data, imports the log key, and verifies the SCT signature -- an ECDSA signature is
381
+ * signed data, imports the log key, and verifies the SCT signature. An ECDSA signature is
382
382
  * routed through the strict DER ECDSA-Sig-Value conformance gate before conversion to the
383
383
  * raw r||s WebCrypto expects, an RSA signature verifies directly.
384
384
  *
385
385
  * Resolves `true` on a valid signature and `false` on a cryptographic mismatch (a false
386
- * verdict is a verdict). Throws a typed `CtError` on structural failure -- a malformed
386
+ * verdict is a verdict). Throws a typed `CtError` on structural failure: a malformed
387
387
  * entry/SCT, an unusable log key, or an unsupported hash/signature algorithm.
388
388
  *
389
389
  * @example
@@ -492,12 +492,12 @@ function _encodeSctBody(sct) {
492
492
  * @spec RFC 6962, RFC 5246
493
493
  * @related pki.ct.parseSctList, pki.ct.signSct
494
494
  *
495
- * Build the value of an RFC 6962 SCT-list extension from an array of SCTs -- the exact
495
+ * Build the value of an RFC 6962 SCT-list extension from an array of SCTs: the exact
496
496
  * inverse of `parseSctList`, such that `parseSctList(encodeSctList(list.all))` round-trips to
497
497
  * identical bytes. Each element is either a decoded v1 SCT (the shape `parseSctList().scts[]`
498
498
  * or `signSct` returns: `version` 0, 32-byte `logId`, `timestamp` BigInt, raw `extensions`,
499
- * `hashAlg` / `sigAlg` code points, raw `signature`) -- rebuilt from its fields in the RFC
500
- * 6962 sec. 3.2 field order -- or an opaque non-v1 entry (`{ version, rawSct }`) whose
499
+ * `hashAlg` / `sigAlg` code points, raw `signature`), rebuilt from its fields in the RFC
500
+ * 6962 sec. 3.2 field order, or an opaque non-v1 entry (`{ version, rawSct }`) whose
501
501
  * `rawSct` is re-emitted verbatim (forward compatibility, sec. 3.3). Pass `parseSctList().all`
502
502
  * (not `.scts`) to preserve the exact wire order and every unknown-version entry.
503
503
  *
@@ -568,7 +568,7 @@ function _logKeyMaterial(logKey) {
568
568
  * @related pki.ct.verifySct, pki.ct.reconstructSignedData, pki.ct.encodeSctList
569
569
  *
570
570
  * Perform a Certificate Transparency log's signing step (RFC 6962 sec. 3.2): rebuild the exact
571
- * `digitally-signed` preimage over `entry` (via `reconstructSignedData`, the SAME builder the
571
+ * `digitally-signed` preimage over `entry` (via `reconstructSignedData`, the same builder the
572
572
  * verifier hashes), sign it with the log's private key, and return a fully-formed v1 SCT that
573
573
  * `verifySct` accepts against the log's public key. `entry` is the log entry the SCT covers
574
574
  * (`{ entryType: 0, leafCert }` or `{ entryType: 1, tbsCertificate, issuerKeyHash }`, as for
@@ -576,7 +576,7 @@ function _logKeyMaterial(logKey) {
576
576
  * or a node `KeyObject`).
577
577
  *
578
578
  * The log-key profile is RFC 6962 sec. 2.1.4: ECDSA NIST P-256 (`sigAlg` 3) or RSA >= 2048
579
- * (`sigAlg` 1), SHA-256 only -- an unsupported key fails closed `ct/unsupported-algorithm`. The
579
+ * (`sigAlg` 1), SHA-256 only; an unsupported key fails closed `ct/unsupported-algorithm`. The
580
580
  * `logId` is derived as SHA-256 of the log SPKI (sec. 3.4); a supplied `opts.logId` must match.
581
581
  * The returned SCT is the parseSctList/verifySct shape and composes with `encodeSctList`.
582
582
  *
@@ -715,17 +715,17 @@ function _logsAgree(a, b) {
715
715
  * @related pki.ct.verifySctWithLogList, pki.ct.verifySct
716
716
  *
717
717
  * Ingest a Certificate Transparency log-list JSON document (the `log_list.json` browsers consume) into a
718
- * set of constraint-carrying trusted logs, keyed by log-id. `json` is a Buffer or string -- the caller
718
+ * set of constraint-carrying trusted logs, keyed by log-id. `json` is a Buffer or string; the caller
719
719
  * supplies the already-fetched, already-authenticated bytes (offline, no network fetch). Parsing routes
720
720
  * through the bounded, duplicate-member-rejecting JSON reader; for each log it base64-decodes the `key`
721
721
  * to its DER SubjectPublicKeyInfo, validates it as a well-formed on-profile key, **recomputes**
722
- * `SHA-256(SPKI)` and fail-closed **requires** it equal the stated `log_id` (RFC 6962 sec. 3.2 -- a log
722
+ * `SHA-256(SPKI)` and fail-closed **requires** it equal the stated `log_id` (RFC 6962 sec. 3.2; a log
723
723
  * whose stated id disagrees with its key is refused as `ct/log-id-mismatch`), and decodes the `state`
724
724
  * (exactly one of pending/qualified/usable/readonly/retired/rejected) and `temporal_interval`. Returns
725
725
  * `{ logs, byLogId, version, timestamp }` where each log is `{ logId, logIdHex, key, description, url, mmd,
726
726
  * operator, state: { name, since, trusted, conditional }, temporalInterval, trusted }`, `byLogId` is a
727
727
  * null-proto `{ logIdHex: log }` map, `version` is the document's version string (or null), and `timestamp`
728
- * is the parsed `log_list_timestamp` `Date` (or null when absent/unparseable -- the staleness surface, read
728
+ * is the parsed `log_list_timestamp` `Date` (or null when absent/unparseable; the staleness surface, read
729
729
  * leniently, never a throw). Every malformed / oversized / mis-bound input is a typed `CtError`.
730
730
  *
731
731
  * @example
@@ -810,8 +810,8 @@ function _resolveNotAfter(entry, opts) {
810
810
  * the log is resolved by `sct.logIdHex` (an unknown log is `ct/log-not-found`). The log's **state** gates
811
811
  * trust (usable/qualified/readonly proceed; a retired log proceeds only for an SCT timestamped before its
812
812
  * retirement instant; pending/rejected are `ct/log-untrusted`); its **temporal_interval** gates the
813
- * covered certificate (the cert's `notAfter` -- from `entry.leafCert` when `entryType` is 0, or
814
- * `opts.certNotAfter` -- must fall in `[start_inclusive, end_exclusive)`, and a windowed log with no
813
+ * covered certificate: the cert's `notAfter`, from `entry.leafCert` when `entryType` is 0 or from
814
+ * `opts.certNotAfter`, must fall in `[start_inclusive, end_exclusive)`, and a windowed log with no
815
815
  * resolvable notAfter is `ct/temporal-interval`, never silently skipped). Then the crypto is delegated to
816
816
  * the shipped `verifySct(entry, sct, log.key)` (which independently re-checks `logId == SHA-256(key)`).
817
817
  * Resolves `true` for a valid signature from a trusted, in-window log; `false` on a cryptographic
@@ -870,9 +870,9 @@ async function verifySctWithLogList(entry, sct, logList, opts) {
870
870
  * @related pki.ct.parseLogList, pki.ct.verifySct
871
871
  *
872
872
  * Verify the detached signature published alongside the Certificate Transparency log list (the
873
- * `log_list.sig` over `log_list.json`). `json` is the RAW log-list bytes (a Buffer, or the fetched text
874
- * as a string -- verified byte-for-byte, never re-serialized), `signature` is the detached signature, and
875
- * `publicKey` is the caller-PINNED signer SubjectPublicKeyInfo (DER; there is no baked-in key). The scheme
873
+ * `log_list.sig` over `log_list.json`). `json` is the raw log-list bytes (a Buffer, or the fetched text
874
+ * as a string, verified byte-for-byte and never re-serialized), `signature` is the detached signature, and
875
+ * `publicKey` is the caller-pinned signer SubjectPublicKeyInfo (DER; there is no baked-in key). The scheme
876
876
  * is RSASSA-PKCS1-v1.5 with SHA-256 over an RSA key (the deployed scheme; an EC P-256 / ECDSA-SHA-256 arm
877
877
  * is accepted for future-proofing). Resolves `true` for a valid signature, `false` on a cryptographic
878
878
  * mismatch (a verdict). Fail-closed forgery defenses throw before any verify: an RSA public exponent below
@@ -1010,27 +1010,27 @@ function _fetchBody(transport, url, req, label) {
1010
1010
  * @spec RFC 6962
1011
1011
  * @related pki.ct.parseLogList, pki.ct.verifyLogListSignature, pki.ct.verifySctWithLogList
1012
1012
  *
1013
- * Fetch the Certificate Transparency log list live and return the trusted-log set ONLY after the detached
1013
+ * Fetch the Certificate Transparency log list live and return the trusted-log set only after the detached
1014
1014
  * signature verifies against the caller-pinned distributor key. It GETs `opts.url` (the `log_list.json`)
1015
1015
  * and the detached `opts.sigUrl` (the `log_list.sig`, by default `opts.url` with a `.json` path suffix
1016
1016
  * rewritten to `.sig`) over the shared, fail-closed `pki.transport` (or an injected `opts.transport`), then
1017
- * verifies the detached signature over the RAW fetched JSON bytes against `opts.signerKey` and only on a
1018
- * strict `true` verdict ingests the SAME bytes through `parseLogList` -- so the client never parses, reads,
1017
+ * verifies the detached signature over the raw fetched JSON bytes against `opts.signerKey` and only on a
1018
+ * strict `true` verdict ingests those same bytes through `parseLogList`, so the client never parses, reads,
1019
1019
  * caches, or surfaces any field of an unverified document (verify-before-parse). No baked-in vendor URL and
1020
- * no baked-in key: the caller pins both out-of-band. Trust is EXPLICIT -- an `opts.tls.anchors` set or an
1020
+ * no baked-in key: the caller pins both out-of-band. Trust is explicit: an `opts.tls.anchors` set or an
1021
1021
  * `opts.tls.useSystemStore` opt-in, `rejectUnauthorized` always on. The returned `timestamp` is surfaced
1022
1022
  * (never policed) so the caller enforces its own freshness policy; chaining a resolved log to an SCT is the
1023
1023
  * caller's `verifySctWithLogList` step. Every fetch / verify / parse failure is a typed `CtError`.
1024
1024
  *
1025
1025
  * @opts url REQUIRED -- the `log_list.json` URL; must be https (no baked-in vendor URL).
1026
- * @opts signerKey REQUIRED, PINNED -- the distributor SubjectPublicKeyInfo as a DER Buffer; no baked-in key.
1026
+ * @opts signerKey REQUIRED and caller-pinned -- the distributor SubjectPublicKeyInfo as a DER Buffer; no baked-in key.
1027
1027
  * @opts sigUrl OPTIONAL -- the detached `log_list.sig` URL (https, must share the log-list URL's origin); default `url` with `.json` -> `.sig` (a non-.json url requires an explicit sigUrl).
1028
1028
  * @opts transport OPTIONAL injectable `transport(request) -> Promise<{status,headers,body}>` (default `pki.transport.https`); the test seam.
1029
1029
  * @opts tls OPTIONAL `{ anchors, useSystemStore, cert, key, minVersion, servername, checkServerIdentity }` threaded to the default transport (ignored when a transport is injected); `rejectUnauthorized` is always on.
1030
1030
  * @opts headers OPTIONAL extra request headers (the request-framing headers are stripped; the verb owns the GET method).
1031
1031
  * @opts timeout OPTIONAL ms budget, default 30s (cap-validated).
1032
1032
  * @opts maxResponseBytes OPTIONAL per-GET size cap, default 4 MiB, tightenable DOWNWARD only.
1033
- * @opts requireJsonContentType OPTIONAL boolean, default false -- opt in to a strict `ct/bad-content-type` gate on the JSON GET.
1033
+ * @opts requireJsonContentType OPTIONAL boolean (default false) -- opt in to a strict `ct/bad-content-type` gate on the JSON GET.
1034
1034
  * @example
1035
1035
  * // a live distributor uses the default pki.transport.https; here an injected transport returns the pair
1036
1036
  * var r = await pki.ct.fetchLogList({ url: "https://ct.example/log_list.json", signerKey: googleSignerSpki,
@@ -6,13 +6,13 @@
6
6
  // OKP (Ed25519 / Ed448) credential public-key point.
7
7
  //
8
8
  // edwards-point -- point validation for the Edwards curves EdDSA uses. node:crypto imports
9
- // an RFC 8410 OKP SubjectPublicKeyInfo WITHOUT checking the point is a valid, full-order
9
+ // an RFC 8410 OKP SubjectPublicKeyInfo without checking the point is a valid, full-order
10
10
  // Edwards point (an all-zeroes Ed25519/Ed448 key parses, and an all-zeroes key with an
11
- // all-zeroes signature even verifies TRUE -- a trivial forgery), so an OKP credential key
11
+ // all-zeroes signature even verifies true, a trivial forgery), so an OKP credential key
12
12
  // needs an explicit check. This decodes the compressed point per RFC 8032 sec. 5.1.3
13
- // (Ed25519) / sec. 5.2.3 (Ed448) -- which fails closed on an off-curve or non-canonical
14
- // encoding -- and rejects a low-order point via the cofactor check [8]P == identity (an
15
- // all-zeroes key is a low-order point ON the curve, so the decode alone does not catch it).
13
+ // (Ed25519) / sec. 5.2.3 (Ed448); the decode fails closed on an off-curve or non-canonical
14
+ // encoding. It then rejects a low-order point via the cofactor check [8]P == identity (an
15
+ // all-zeroes key is a low-order point on the curve, so the decode alone does not catch it).
16
16
  // A conformant authenticator never emits a low-order credential key.
17
17
  //
18
18
  // Pure BigInt modular arithmetic (constant-time is not a goal here: the input is a public
@@ -93,8 +93,8 @@ function validate(raw, crv) {
93
93
  // validateSpki(spkiBytes, crv, E, code) -> throws new E(code, ...) unless the
94
94
  // SubjectPublicKeyInfo's subjectPublicKey (its BIT STRING body, past the unused-bits octet) is a
95
95
  // canonical, on-curve, full-order Edwards point for the OKP curve (6 = Ed25519, 7 = Ed448). This
96
- // is the one home every EdDSA verify path routes an SPKI through before importKey/verify -- node
97
- // imports a low-order (e.g. all-zeroes) OKP SPKI without complaint and such a key verifies a
96
+ // is the one home every EdDSA verify path routes an SPKI through before importKey/verify, because
97
+ // node imports a low-order (e.g. all-zeroes) OKP SPKI without complaint and such a key verifies a
98
98
  // forged signature. Error-parameterized like the guard family: the caller passes its own typed
99
99
  // error CONSTRUCTOR `E` and domain `code`, so the shared gate keeps no error domain of its own.
100
100
  function validateSpki(spkiBytes, crv, E, code) {