@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/cms-verify.js CHANGED
@@ -16,9 +16,9 @@
16
16
  * detached content, single and multiple signers, and RSA / RSASSA-PSS / ECDSA / EdDSA, the
17
17
  * post-quantum ML-DSA (ML-DSA-44/65/87, RFC 9882) and SLH-DSA (the twelve FIPS 205 sets, RFC 9814),
18
18
  * and composite ML-DSA (pairing ML-DSA with a traditional RSA / ECDSA / EdDSA, accepted only when
19
- * BOTH components verify -- draft-ietf-lamps-cms-composite-sigs) are covered. It reports a
19
+ * both components verify, per draft-ietf-lamps-cms-composite-sigs) are covered. It reports a
20
20
  * per-signer verdict;
21
- * it does NOT chain the signer certificate to a trust anchor -- that is the caller's step through
21
+ * it does not chain the signer certificate to a trust anchor, which is the caller's step through
22
22
  * `pki.path.validate`.
23
23
  * @spec RFC 5652
24
24
  * @spec RFC 9882
@@ -63,11 +63,11 @@ var DIGEST_HASH = {
63
63
  // The SIGNATURE hash a bare-key-OID scheme (rsaEncryption, ecPublicKey) inherits from the
64
64
  // SignerInfo digestAlgorithm. Deliberately a SEPARATE table from DIGEST_HASH, and deliberately
65
65
  // without the extendable-output functions: RFC 8702 sec. 3.2 gives RSASSA-PKCS1-v1_5-with-SHAKE
66
- // and ECDSA-with-SHAKE their OWN signature OIDs and never pairs a bare key OID with a SHAKE
66
+ // and ECDSA-with-SHAKE their own signature OIDs and never pairs a bare key OID with a SHAKE
67
67
  // digestAlgorithm, so that combination is non-conformant and must keep its precise refusal.
68
68
  // Folding the two roles into one table would resolve a signature hash for it instead, letting
69
- // it past the fail-closed gate below and into the engine, where it surfaces as a relabeled
70
- // foreign fault rather than this module's own unsupported-algorithm verdict.
69
+ // it past the fail-closed gate below and into the engine, where the caller gets a relabeled
70
+ // foreign fault and loses this module's own unsupported-algorithm verdict.
71
71
  var SIG_HASH = { sha1: "SHA-1", sha256: "SHA-256", sha384: "SHA-384", sha512: "SHA-512" };
72
72
  // Is `name` a message-digest algorithm this verifier supports?
73
73
  function _supportedDigest(name) { return !!DIGEST_HASH[name]; }
@@ -86,7 +86,7 @@ var SIG_SCHEME = {
86
86
  rsassaPss: { kind: "rsapss" },
87
87
  ecPublicKey: { kind: "ec", params: "absent" },
88
88
  // One-shot families (EdDSA, ML-DSA): the same OID identifies the key and the signature, so the
89
- // signer cert SPKI algorithm OID MUST equal the signatureAlgorithm OID -- `sameKeyOid` enables
89
+ // signer cert SPKI algorithm OID MUST equal the signatureAlgorithm OID. `sameKeyOid` enables
90
90
  // that agreement check (RFC 8410 / RFC 9882; enforced in _verifyAgainstCandidates).
91
91
  Ed25519: { kind: "eddsa", name: "Ed25519", params: "absent", sameKeyOid: true },
92
92
  Ed448: { kind: "eddsa", name: "Ed448", params: "absent", sameKeyOid: true },
@@ -104,7 +104,7 @@ var SIG_SCHEME = {
104
104
  };
105
105
  // SLH-DSA (RFC 9814): the twelve pure FIPS 205 sets, seeded like webcrypto's SLH_DSA_NODE. Each is
106
106
  // a one-shot family (kind "slhdsa"), params absent, sig-OID == key-OID (sameKeyOid). The WebCrypto
107
- // name is "SLH-DSA-"+SET, matching path-validate's transform (drift rule sec. 5 -- one name map).
107
+ // name is "SLH-DSA-"+SET, matching path-validate's transform (drift rule sec. 5: one name map).
108
108
  ["sha2-128s", "sha2-128f", "sha2-192s", "sha2-192f", "sha2-256s", "sha2-256f",
109
109
  "shake-128s", "shake-128f", "shake-192s", "shake-192f", "shake-256s", "shake-256f"
110
110
  ].forEach(function (s) { SIG_SCHEME["id-slh-dsa-" + s] = { kind: "slhdsa", name: "SLH-DSA-" + s.toUpperCase(), params: "absent", sameKeyOid: true, digest: SLHDSA_BY_OID[oid.byName("id-slh-dsa-" + s)].digest }; });
@@ -140,7 +140,7 @@ function _findSignerCerts(sid, parsedCerts) {
140
140
  return out;
141
141
  }
142
142
 
143
- // Verify `sigBytes` over `signedBytes` against EACH matching candidate certificate in turn; the
143
+ // Verify `sigBytes` over `signedBytes` against each matching candidate certificate in turn; the
144
144
  // signer is valid iff one candidate verifies. A candidate whose key is structurally unusable (a
145
145
  // low-order EdDSA point, an unsupported curve) or whose signature simply does not verify falls
146
146
  // through to the next -- so a colliding certificate cannot make a valid signature read invalid.
@@ -156,7 +156,7 @@ function _verifyAgainstCandidates(scheme, sigHash, sigBytes, signedBytes, sid, c
156
156
  var c = candidates[idx];
157
157
  // One-shot family (EdDSA/ML-DSA): the signer cert public-key algorithm OID MUST equal the
158
158
  // SignerInfo signatureAlgorithm OID. A candidate whose SPKI disagrees is skipped with a precise
159
- // verdict, rather than a foreign webcrypto/data throw from importing under the wrong name.
159
+ // verdict; importing it under the wrong name would surface a foreign webcrypto/data throw.
160
160
  if (expectedKeyOid && c.cert.subjectPublicKeyInfo.algorithm.oid !== expectedKeyOid) {
161
161
  lastErr = _err("cms/unsupported-algorithm", "the signer certificate public-key algorithm does not match the SignerInfo signatureAlgorithm");
162
162
  return attempt(idx + 1);
@@ -206,11 +206,11 @@ function _hashAlgOid(seq) {
206
206
  }
207
207
 
208
208
  // Resolve RSASSA-PSS-params (RFC 4055) to { hash, saltLength }, or null if the parameters
209
- // deviate from the supported profile: an explicit SHA-2 hashAlgorithm, MGF1 keyed to the SAME
209
+ // deviate from the supported profile: an explicit SHA-2 hashAlgorithm, MGF1 keyed to the same
210
210
  // hash, saltLength equal to the hash length, and trailerField 1. The SHA-1 DEFAULTs are
211
211
  // rejected (params must be explicit), matching the certification-path validator, so a
212
- // non-conformant PSS AlgorithmIdentifier is a fail-closed verdict -- never verified under
213
- // WebCrypto's own defaults (a signatureAlgorithm bypass otherwise).
212
+ // non-conformant PSS AlgorithmIdentifier is a fail-closed verdict. Falling back to WebCrypto's
213
+ // own defaults would be a signatureAlgorithm bypass.
214
214
  function _resolvePss(paramsBytes) {
215
215
  if (!paramsBytes) return null;
216
216
  var n;
@@ -264,7 +264,7 @@ function _verifySignature(scheme, hashName, sigBytes, spki, signedBytes, curveOi
264
264
  var ec = EC_CURVE[curveOid];
265
265
  if (!ec) throw _err("cms/unsupported-algorithm", "the signer key is on an unsupported EC curve");
266
266
  // The ORDER-AWARE gate: r,s in [1, n-1] per FIPS 186-5 sec. 6.4.2 (rejecting an out-of-range r/s >= the
267
- // curve order n, not only the r=s=0 forgery) -- a verifier that knows the curve MUST use it.
267
+ // curve order n, not only the r=s=0 forgery). A verifier that knows the curve MUST use it.
268
268
  var raw = validator.sig.ecdsaDerToP1363(sigBytes, ec.curve, CmsError, "cms/bad-signature");
269
269
  return subtle.importKey("spki", spki, { name: "ECDSA", namedCurve: ec.curve }, false, ["verify"])
270
270
  .then(function (k) { return subtle.verify({ name: "ECDSA", hash: hashName }, k, raw, signedBytes); });
@@ -279,13 +279,13 @@ function _verifySignature(scheme, hashName, sigBytes, spki, signedBytes, curveOi
279
279
  }
280
280
  // EdDSA -- the WebCrypto name follows the signing key's SPKI OID (Ed25519 / Ed448). node/
281
281
  // OpenSSL imports any Ed25519/Ed448 SPKI without validating the point, and a low-order (e.g.
282
- // all-zeroes) key verifies a forged signature -- so reject a non-full-order point first.
282
+ // all-zeroes) key verifies a forged signature, so reject a non-full-order point first.
283
283
  _requireValidEdPoint(spki, scheme.name);
284
284
  return subtle.importKey("spki", spki, { name: scheme.name }, false, ["verify"])
285
285
  .then(function (k) { return subtle.verify({ name: scheme.name }, k, sigBytes, signedBytes); });
286
286
  }
287
287
 
288
- // The EdDSA signer key's point MUST be a valid, canonical, full-order Edwards point -- reject an
288
+ // The EdDSA signer key's point MUST be a valid, canonical, full-order Edwards point: reject an
289
289
  // off-curve or low-order key before it verifies a signature (WebCrypto import does not check it).
290
290
  // Routed through the shared edwards-point gate every EdDSA verify path uses; curve from the name.
291
291
  function _requireValidEdPoint(spkiBytes, name) {
@@ -303,9 +303,9 @@ function _certCurveOid(cert) {
303
303
 
304
304
  // Decode the SignedAttributes SET OF Attribute from the exact bytes the signature covers
305
305
  // (the re-tagged signedAttrsBytes), returning [{ type, values: [valueNode] }]. Binding the
306
- // content-type / message-digest checks to THESE decoded attributes -- not the caller-mutable
307
- // parsed si.signedAttrs -- means a parsed-object input cannot desync the checked attributes
308
- // from the verified preimage (RFC 5652 sec. 5.4).
306
+ // content-type / message-digest checks to these decoded attributes means a parsed-object input
307
+ // cannot desync the checked attributes from the verified preimage (RFC 5652 sec. 5.4). The
308
+ // caller-mutable parsed si.signedAttrs is deliberately left out of that decision.
309
309
  function _decodeSignedAttrs(setOfBytes) {
310
310
  var set = asn1.decode(setOfBytes);
311
311
  if (set.tagClass !== "universal" || set.tagNumber !== asn1.TAGS.SET || !set.children) throw _err("cms/bad-signed-attrs", "signedAttrs is not a SET OF Attribute");
@@ -317,8 +317,8 @@ function _decodeSignedAttrs(setOfBytes) {
317
317
  });
318
318
  }
319
319
 
320
- // Verify one SignerInfo. Returns { ok, sid, cert, ... } -- a resolved false is a verdict; a
321
- // structural failure carries a code.
320
+ // Verify one SignerInfo. Returns { ok, sid, cert, ... }, where a resolved false is a verdict and
321
+ // a structural failure carries a code.
322
322
  // `csTarget`, when set (a Buffer), makes this a COUNTERSIGNATURE verify (RFC 5652 sec. 11.4): the
323
323
  // preimage is the countersigned signature octets, not the sec. 5.4 content preimage, and no
324
324
  // content-type attribute is required or checked (_computeCountersigBytes). content/eContentType are
@@ -343,7 +343,7 @@ function _verifyOne(si, content, eContentType, parsedCerts, csTarget) {
343
343
  // The digestAlgorithm parameters, when present, MUST be a DER NULL (RFC 5754 sec. 2 accepts
344
344
  // absent OR NULL; a present non-NULL is malformed and, being outside the signed preimage,
345
345
  // must fail closed like the signatureAlgorithm parameters above). EXCEPT for an ML-DSA
346
- // SignerInfo WITHOUT signed attributes: RFC 9882 sec. 3.3 says the whole digestAlgorithm field
346
+ // SignerInfo carrying no signed attributes: RFC 9882 sec. 3.3 says the whole digestAlgorithm field
347
347
  // has no meaning there and MUST be ignored, so neither its parameters nor its name may reject.
348
348
  var dp = si.digestAlgorithm.parameters;
349
349
  var mldsaNoAttrs = scheme.kind === "mldsa" && !si.signedAttrsBytes;
@@ -398,7 +398,7 @@ function _computeCountersigBytes(si, csTarget) {
398
398
  attrs = _decodeSignedAttrs(reTagged);
399
399
  } catch (e) { if (e instanceof CmsError) throw e; throw _err("cms/bad-signed-attrs", "the countersignature signedAttrs is not a valid SET OF Attribute", e); }
400
400
  // sec. 11.4: a content-type attribute MUST NOT appear (the parser already rejects it at parse, but
401
- // verify never relies on the parse alone -- a parsed-object input could carry one).
401
+ // verify never relies on the parse alone: a parsed-object input could carry one).
402
402
  if (attrs.filter(function (a) { return a.type === OID_CONTENT_TYPE; }).length) throw _err("cms/misplaced-attr", "a countersignature must not carry a content-type attribute (RFC 5652 sec. 11.4)");
403
403
  var mdAttr = attrs.filter(function (a) { return a.type === OID_MESSAGE_DIGEST; })[0];
404
404
  if (!mdAttr || mdAttr.values.length !== 1) throw _err("cms/bad-signed-attrs", "a countersignature's signedAttrs must carry exactly one message-digest attribute (RFC 5652 sec. 11.4)");
@@ -437,9 +437,9 @@ function _computeSignedBytes(si, content, eContentType) {
437
437
  }
438
438
  return content;
439
439
  }
440
- // With signed attributes: decode them from the EXACT bytes the signature covers -- the
441
- // SignedAttributes SET OF, the on-wire [0] IMPLICIT tag replaced by a universal SET OF
442
- // (RFC 5652 sec. 5.4) -- so the content-type / message-digest checks bind the same bytes
440
+ // With signed attributes: decode them from the EXACT bytes the signature covers, namely the
441
+ // SignedAttributes SET OF with the on-wire [0] IMPLICIT tag replaced by a universal SET OF
442
+ // (RFC 5652 sec. 5.4). The content-type / message-digest checks then bind the same bytes
443
443
  // that are verified. Reading the caller-mutable parsed si.signedAttrs instead would let a
444
444
  // parsed-object input desync the checked attributes from the verified preimage.
445
445
  var reTagged = Buffer.from(si.signedAttrsBytes);
@@ -478,7 +478,7 @@ function _computeSignedBytes(si, content, eContentType) {
478
478
  // (sec. 3.4, defense-in-depth over the parse-time _PARAMS_ABSENT gate), the unsupported-arm refusal,
479
479
  // and the sec. 3.4/sec. 5 coherence gate binding the SignerInfo digestAlgorithm to the arm's Table-1
480
480
  // pre-hash. It reuses the sec. 5.4 preimage + message-digest binding UNCHANGED, then dispatches
481
- // compositeVerify per candidate certificate (BOTH components must verify -- never an AND->OR
481
+ // compositeVerify per candidate certificate (both components must verify, with no AND->OR
482
482
  // downgrade).
483
483
  function _verifyComposite(si, comp, content, eContentType, parsedCerts, csTarget) {
484
484
  if (si.signatureAlgorithm.parameters !== null && si.signatureAlgorithm.parameters !== undefined) {
@@ -490,8 +490,8 @@ function _verifyComposite(si, comp, content, eContentType, parsedCerts, csTarget
490
490
  return Promise.resolve({ ok: false, code: "cms/unsupported-algorithm", sid: si.sid, message: "composite " + comp.name + ": " + comp.trad.unsupported });
491
491
  }
492
492
  // draft sec. 3.4: for a composite SignerInfo the digestAlgorithm parameters MUST be OMITTED
493
- // (id-sha256/id-sha512 and id-shake256) -- stricter than the generic RFC 5754 absent-OR-NULL rule
494
- // (which the classical/ML-DSA paths follow), because the composite draft explicitly requires the
493
+ // (id-sha256/id-sha512 and id-shake256). That is stricter than the generic RFC 5754 absent-OR-NULL
494
+ // rule (which the classical/ML-DSA paths follow), because the composite draft explicitly requires the
495
495
  // field omitted. A present parameter -- even a DER NULL -- is non-conformant and fails closed
496
496
  // (it is outside the signed preimage, so a parser differential must not verify).
497
497
  var dp = si.digestAlgorithm.parameters;
@@ -515,7 +515,7 @@ function _verifyComposite(si, comp, content, eContentType, parsedCerts, csTarget
515
515
  });
516
516
  }
517
517
 
518
- // Dispatch compositeVerify against EACH matching candidate; the signer is valid iff one candidate
518
+ // Dispatch compositeVerify against each matching candidate; the signer is valid iff one candidate
519
519
  // verifies both components. A candidate whose SPKI composite OID != the SignerInfo signatureAlgorithm
520
520
  // OID is skipped with a precise verdict (RFC 9814 sec. 4 key<->signature agreement); a candidate that
521
521
  // returns a structural fault code (bad split length, unsupported) falls through, its code surfaced if
@@ -542,9 +542,10 @@ function _verifyCompositeAgainstCandidates(comp, sigBytes, signedBytes, sid, can
542
542
  return attempt(0);
543
543
  }
544
544
 
545
- // The decoded unsigned attributes of a SignerInfo, surfaced UNAUTHENTICATED (they are outside the
546
- // signature) with each OID resolved to a name -- so a caller can read an attached RFC 3161 timestamp
547
- // token or inspect a countersignature attribute. NEVER counted toward a signer's ok / res.valid.
545
+ // The decoded unsigned attributes of a SignerInfo, surfaced UNAUTHENTICATED (they are outside
546
+ // the signature) with each OID resolved to a name, so a caller can read an attached RFC 3161
547
+ // timestamp token or inspect a countersignature attribute. They are never counted toward a
548
+ // signer's ok / res.valid.
548
549
  function _surfaceUnsignedAttrs(si) {
549
550
  return (si.unsignedAttrs || []).map(function (a) {
550
551
  return { type: a.type, typeName: oid.name(a.type) || null, values: a.values };
@@ -553,17 +554,17 @@ function _surfaceUnsignedAttrs(si) {
553
554
 
554
555
  // Verify every countersignature attached to `si` (RFC 5652 sec. 11.4): each id-countersignature
555
556
  // value is a SignerInfo over `si`'s signature octets. Returns per-countersignature verdicts; a
556
- // countersignature's OWN countersignatures verify over ITS signature octets (recursive). A
557
- // countersignature that FAILS TO VERIFY is surfaced ok:false -- never silently dropped, and never
558
- // allowed to change the primary verdict. A countersignature value that is not a well-formed
557
+ // countersignature's own countersignatures verify over its signature octets (recursive). A
558
+ // countersignature that fails to verify is surfaced ok:false. It is never silently dropped, and
559
+ // never allowed to change the primary verdict. A countersignature value that is not a well-formed
559
560
  // SignerInfo does not reach here at all: the decoder validates every id-countersignature value by
560
- // content rather than on the attribute type, so such a message is refused whole. The recursion
561
- // terminates because it only walks the FINITE parsed structure: each nested countersignature value
562
- // is a sub-encoding of its parent, and the strict decoder already bounds total nesting by
563
- // C.LIMITS.DER_MAX_DEPTH at parse (CWE-834/770), so a hostile deep chain fails closed before verify.
561
+ // its content, so such a message is refused whole. The recursion terminates because it only walks
562
+ // the FINITE parsed structure: each nested countersignature value is a sub-encoding of its parent,
563
+ // and the strict decoder already bounds total nesting by C.LIMITS.DER_MAX_DEPTH at parse
564
+ // (CWE-834/770), so a hostile deep chain fails closed before verify.
564
565
  function _verifyCountersignatures(si, parsedCerts) {
565
566
  // si.signature is always a Buffer from the strict parser (and from walkCountersignature on the
566
- // recursive path), so _toBuf is a pass-through here -- the SignerInfo is already well-formed.
567
+ // recursive path), so _toBuf is a pass-through here. The SignerInfo is already well-formed.
567
568
  var targetSig = _toBuf(si.signature, "the countersigned signature");
568
569
  var values = [];
569
570
  (si.unsignedAttrs || []).forEach(function (a) {
@@ -575,6 +576,14 @@ function _verifyCountersignatures(si, parsedCerts) {
575
576
 
576
577
  function _verifyOneCountersig(vDer, targetSig, parsedCerts) {
577
578
  var csSi;
579
+ // Coverage residual, and deliberately kept. Every value reaching here was already walked by the
580
+ // decoder, which validates an id-countersignature by content and not by the attribute type. A
581
+ // malformed one therefore refused the whole message before verify was entered, so this catch
582
+ // cannot be reached through the public path. That is settled behaviour: a message the decoder
583
+ // has found to be malformed has no sound remainder to report a verdict over, so it is refused
584
+ // whole instead of surfaced as one failed countersignature.
585
+ // cms-verify.test.js pins that through the shipped verbs; this stays because the walk is also
586
+ // reachable recursively and a backstop that returns a NEGATIVE verdict costs nothing.
578
587
  try { csSi = cms.walkCountersignature(asn1.decode(Buffer.isBuffer(vDer) ? vDer : Buffer.from(vDer))); }
579
588
  catch (e) { return Promise.resolve({ ok: false, code: (e instanceof CmsError ? e.code : "cms/bad-countersignature"), message: e && e.message }); }
580
589
  return _verifyOne(csSi, targetSig, null, parsedCerts, targetSig).then(function (verdict) {
@@ -609,29 +618,29 @@ function _verifyOneCountersig(vDer, targetSig, parsedCerts) {
609
618
  * than RFC 5652's. Signing WITH attributes and signing the content directly are different claims --
610
619
  * attributes bind a content type and a signing time alongside the digest, content-only binds
611
620
  * nothing but the bytes -- and one message may carry a signer of each. A profile that requires
612
- * attributes (RFC 8551 S/MIME does) or a particular content type can enforce it from the verdict
613
- * rather than parsing the message a second time.
621
+ * attributes (RFC 8551 S/MIME does) or a particular content type can enforce it from the verdict,
622
+ * with no need to parse the message a second time.
614
623
  *
615
624
  * `valid` and `trusted` are DIFFERENT claims and neither implies the other. A SignedData carries
616
- * its own certificates, so `valid` establishes that the message is internally consistent -- the
625
+ * its own certificates, so `valid` establishes that the message is internally consistent: the
617
626
  * signature is sound under a certificate the message or `opts.certs` supplied. Anyone can mint a
618
627
  * certificate, sign with it, and embed it, so that says nothing about WHO signed. `trusted` says
619
628
  * every signer chained to a root named in `opts.trustAnchors`, validated through the same RFC 5280
620
629
  * path engine `pki.path.validate` uses. Without anchors there is nothing to chain to and `trusted`
621
630
  * is `false` -- a definite answer, not a missing one. Anchors that cannot be read are a
622
- * configuration fault and throw, rather than being absorbed into `trusted: false`, which would
623
- * report a verdict about the message for a check that never ran.
631
+ * configuration fault and throw. Absorbing them into `trusted: false` would report a verdict
632
+ * about the message for a check that never ran.
624
633
  *
625
- * Trust is decided from the certificate the SignerInfo selected -- the one reported as
626
- * `signers[i].cert` -- never from another certificate that happens to share its key. A
634
+ * Trust is decided from the certificate the SignerInfo selected, the one reported as
635
+ * `signers[i].cert`, never from another certificate that happens to share its key. A
627
636
  * `subjectKeyIdentifier` names a key, and several certificates can hold it with different
628
637
  * validity windows, key usage and policies; deciding from a sibling would let an expired or
629
638
  * wrong-purpose signer certificate be reported trusted because a different certificate chained.
630
639
  * Supply the certificate you want used. RSA (PKCS#1 v1.5 and RSASSA-PSS), ECDSA, EdDSA, and the post-quantum
631
- * ML-DSA (ML-DSA-44/65/87, RFC 9882) and SLH-DSA (the twelve FIPS 205 sets, RFC 9814) -- pure mode,
632
- * empty context -- signatures are recognized, as is composite ML-DSA
633
- * (draft-ietf-lamps-cms-composite-sigs), which pairs ML-DSA with a traditional RSA / ECDSA / EdDSA
634
- * and verifies only when BOTH components pass (never an AND-to-OR downgrade).
640
+ * ML-DSA (ML-DSA-44/65/87, RFC 9882) and SLH-DSA (the twelve FIPS 205 sets, RFC 9814) signatures
641
+ * are recognized, the post-quantum families in pure mode with an empty context, as is composite
642
+ * ML-DSA (draft-ietf-lamps-cms-composite-sigs), which pairs ML-DSA with a traditional
643
+ * RSA / ECDSA / EdDSA and verifies only when both components pass (never an AND-to-OR downgrade).
635
644
  *
636
645
  * @opts content The detached content (a `Buffer`) when the SignedData carries no
637
646
  * encapsulated eContent. Required for a detached signature.
@@ -643,7 +652,7 @@ function _verifyOneCountersig(vDer, targetSig, parsedCerts) {
643
652
  * @opts time The instant to validate the signer's chain at (default now). Only read when
644
653
  * `trustAnchors` is supplied.
645
654
  * @opts requiredEku Key purposes the SIGNER certificate must carry, as OID names or dotted OIDs.
646
- * @opts checkPurpose The purpose the ANCHOR's own trust metadata must permit -- a separate
655
+ * @opts checkPurpose The purpose the ANCHOR's own trust metadata must permit. This is a separate
647
656
  * question from `requiredEku`, since a root distributed with NSS trust bits can be
648
657
  * marked untrusted for one purpose and good for another. Those bits and
649
658
  * `distrustAfter` are consulted only when this names a purpose.
@@ -994,7 +1003,7 @@ function _addCert(out, der) {
994
1003
  * @spec RFC 9814
995
1004
  * @related pki.cms.verify, pki.schema.cms.parse
996
1005
  *
997
- * Produce a CMS SignedData (RFC 5652 sec. 5) over `content` (a `Buffer`) -- the structure
1006
+ * Produce a CMS SignedData (RFC 5652 sec. 5) over `content` (a `Buffer`): the structure
998
1007
  * S/MIME signed mail, RFC 3161 timestamp tokens, and code signing rest on, and exactly what
999
1008
  * `pki.cms.verify` consumes and OpenSSL `cms -verify` validates. Each `signers[i]` is
1000
1009
  * `{ cert, key, digestAlgorithm?, pss? }`: `cert` the signer certificate (PEM or DER), `key`
@@ -1004,9 +1013,10 @@ function _addCert(out, der) {
1004
1013
  * message-digest attribute is bound to the content digest and the signature is over the
1005
1014
  * canonical DER SET OF SignedAttributes; otherwise over the content directly. RSA (PKCS#1 v1.5
1006
1015
  * and, with `pss`, RSASSA-PSS), ECDSA (P-256/384/521), Ed25519, Ed448, and the post-quantum ML-DSA
1007
- * (ML-DSA-44/65/87, RFC 9882) and SLH-DSA (the twelve FIPS 205 sets, RFC 9814 -- the message digest
1008
- * pinned per parameter set) are covered, as is composite ML-DSA (draft-ietf-lamps-cms-composite-sigs)
1009
- * -- the arm's pre-hash fixes the digestAlgorithm and the two component keys are signed together.
1016
+ * (ML-DSA-44/65/87, RFC 9882) and SLH-DSA (the twelve FIPS 205 sets, RFC 9814, with the message
1017
+ * digest pinned per parameter set) are covered, as is composite ML-DSA
1018
+ * (draft-ietf-lamps-cms-composite-sigs), where the arm's pre-hash fixes the digestAlgorithm and
1019
+ * the two component keys are signed together.
1010
1020
  *
1011
1021
  * @opts detached Omit the encapsulated content (a detached signature; the verifier
1012
1022
  * supplies the content). Default false.
@@ -1019,7 +1029,7 @@ function _addCert(out, der) {
1019
1029
  * @opts pem Return a PEM string (`-----BEGIN CMS-----`) instead of a DER Buffer.
1020
1030
  * @opts unsignedAttributes Unsigned attributes for every SignerInfo, each `{ type, values }`
1021
1031
  * (`type` an OID name or dotted string, `values` an array of DER value
1022
- * `Buffer`s). Placed in the SignerInfo `[1]` unsignedAttrs -- OUTSIDE the
1032
+ * `Buffer`s). Placed in the SignerInfo `[1]` unsignedAttrs, outside the
1023
1033
  * signature, so they carry no cryptographic assurance and a verifier never
1024
1034
  * reports them authenticated. The vehicle for an RFC 3161 timestamp token
1025
1035
  * (`timeStampToken`); content-type / message-digest / signing-time are
@@ -1045,12 +1055,12 @@ var sign = cmsSign.sign;
1045
1055
  *
1046
1056
  * Add one or more countersignatures to an existing CMS SignedData (RFC 5652 sec. 11.4). A
1047
1057
  * countersignature is itself a `SignerInfo` whose signature covers the CONTENTS of the countersigned
1048
- * SignerInfo's signature value (NOT the content), attached as the `id-countersignature` unsigned
1058
+ * SignerInfo's signature value (not the content), attached as the `id-countersignature` unsigned
1049
1059
  * attribute -- the construction Authenticode timestamps and signature-attestation workflows rest on.
1050
- * `cms` is the SignedData (a DER `Buffer` or a PEM `CMS` string -- the wire bytes are preserved, so
1060
+ * `cms` is the SignedData (a DER `Buffer` or a PEM `CMS` string; the wire bytes are preserved, so
1051
1061
  * the countersigned primary still verifies byte-for-byte). Each `signers[i]` is the same descriptor
1052
- * `pki.cms.sign` takes -- `{ cert, key, digestAlgorithm?, pss? }`, any RSA / RSASSA-PSS / ECDSA /
1053
- * EdDSA / ML-DSA / SLH-DSA / composite key -- and countersigns over the target signature octets;
1062
+ * `pki.cms.sign` takes (`{ cert, key, digestAlgorithm?, pss? }`, any RSA / RSASSA-PSS / ECDSA /
1063
+ * EdDSA / ML-DSA / SLH-DSA / composite key), and countersigns over the target signature octets;
1054
1064
  * `pki.cms.verify` returns each countersignature verdict under `signers[i].countersignatures`. A
1055
1065
  * countersignature never carries a content-type attribute (sec. 11.4); multiple countersignatures on
1056
1066
  * one signer are multiple values of the one id-countersignature attribute.
@@ -1059,7 +1069,7 @@ var sign = cmsSign.sign;
1059
1069
  * array of indices, or `"all"`.
1060
1070
  * @opts countersignatureOf Countersign the Nth existing countersignature of the target signer
1061
1071
  * instead of the primary signature (a nested countersignature).
1062
- * @opts signedAttributes Include signed attributes (message-digest + signing-time; NEVER a
1072
+ * @opts signedAttributes Include signed attributes (message-digest + signing-time; never a
1063
1073
  * content-type). Default true; false signs the target signature octets
1064
1074
  * directly.
1065
1075
  * @opts signingTime A `Date` for the countersignature's signing-time, or false to omit it.
@@ -1088,11 +1098,11 @@ var countersign = cmsSign.countersign;
1088
1098
  * @spec RFC 5652, RFC 5083, RFC 5084, RFC 3560, RFC 5753, RFC 8418, RFC 9629, RFC 9936, RFC 3211, RFC 8018
1089
1099
  * @related pki.cms.decrypt, pki.schema.cms.parse
1090
1100
  *
1091
- * Encrypt content as a CMS EnvelopedData (CBC content), AuthEnvelopedData (AEAD content -- the
1101
+ * Encrypt content as a CMS EnvelopedData (CBC content), AuthEnvelopedData (AEAD content, the
1092
1102
  * default), or EncryptedData. `recipients` is an ARRAY of recipient descriptors for the enveloped
1093
- * family, each wrapping the SAME fresh content-encryption key: `{ cert }` auto-dispatches off the
1103
+ * family, each wrapping the same fresh content-encryption key: `{ cert }` auto-dispatches off the
1094
1104
  * recipient certificate's public-key algorithm -- RSA yields a ktri with RSAES-OAEP-SHA256 (PKCS#1
1095
- * v1.5 is NEVER emitted); EC P-256/384/521 a kari with ephemeral-static ECDH and the X9.63 KDF;
1105
+ * v1.5 is never emitted); EC P-256/384/521 a kari with ephemeral-static ECDH and the X9.63 KDF;
1096
1106
  * X25519/X448 a kari per RFC 8418 (HKDF); ML-KEM-512/768/1024 an ori/KEMRecipientInfo per RFC
1097
1107
  * 9629 + 9936. `{ password }` yields a pwri (PBKDF2 + RFC 3211 PWRI-KEK); `{ kek, kekId }` a kekri
1098
1108
  * (AES key wrap). For EncryptedData (no RecipientInfos), pass a single non-array `{ cek }` or
@@ -1126,8 +1136,8 @@ var encrypt = cmsEncrypt.encrypt;
1126
1136
  * @related pki.cms.decrypt, pki.schema.cms.parse
1127
1137
  *
1128
1138
  * Produce a CMS AuthenticatedData (RFC 5652 sec. 9): CLEARTEXT content plus a MAC (HMAC-SHA-2) --
1129
- * authenticated but NOT encrypted, the authenticated-only sibling of AuthEnvelopedData. A single
1130
- * fresh MAC key is minted and wrapped for every recipient with the SAME RecipientInfo model
1139
+ * authenticated but not encrypted, the authenticated-only sibling of AuthEnvelopedData. A single
1140
+ * fresh MAC key is minted and wrapped for every recipient with the same RecipientInfo model
1131
1141
  * `pki.cms.encrypt` uses for a content-encryption key, so `recipients` is the identical array of
1132
1142
  * descriptors: `{ cert }` (RSA -> ktri RSAES-OAEP, EC/X25519/X448 -> kari, ML-KEM -> ori/KEMRI),
1133
1143
  * `{ password }` (pwri), or `{ kek, kekId }` (kekri). By default the MAC covers the authenticated
@@ -1161,7 +1171,7 @@ var authenticate = cmsEncrypt.authenticate;
1161
1171
 
1162
1172
  /**
1163
1173
  * @primitive pki.cms.decrypt
1164
- * @signature pki.cms.decrypt(input, keyMaterial, opts?) -> Promise<{ content, contentType, contentTypeName, recipientType, recipientIndex, contentEncryptionAlgorithm, authenticated }>
1174
+ * @signature pki.cms.decrypt(input, keyMaterial, opts?) -> Promise<{ content, contentType, contentTypeName, recipientType, recipientIndex, contentEncryptionAlgorithm, authenticated, authenticatedBy, originAuthenticated, originatorInfo }>
1165
1175
  * @since 0.2.23
1166
1176
  * @status stable
1167
1177
  * @spec RFC 5652, RFC 5083, RFC 5084, RFC 3560, RFC 5753, RFC 8418, RFC 9629, RFC 9936, RFC 3211, RFC 8018, RFC 3218, RFC 2104
@@ -1173,16 +1183,28 @@ var authenticate = cmsEncrypt.authenticate;
1173
1183
  * ECDH / X25519 / X448; kekri; pwri; ori/ML-KEM), and decrypts (or MAC-verifies) the content.
1174
1184
  * `keyMaterial` is `{ key, cert }` (the recipient private key + its certificate, which drives recipient
1175
1185
  * matching), `{ password }`, `{ kek, kekId? }`, or `{ cek }` (EncryptedData raw-key mode). Fail-closed
1176
- * and oracle-free: every secret-dependent failure -- a bad key-wrap, a padding fault, a GCM tag
1177
- * mismatch, a PWRI check-byte mismatch, an AuthenticatedData MAC/message-digest mismatch -- collapses
1178
- * to the SINGLE uniform `cms/decrypt-failed` verdict (Bleichenbacher / EFAIL oracle freedom, and no
1179
- * unwrap-success bit for the MAC path), and the PKCS#1 v1.5 arm applies the RFC 3218 implicit-rejection
1180
- * countermeasure so its failure is indistinguishable. For an AuthenticatedData the MAC (HMAC-SHA-2)
1186
+ * and oracle-free: every secret-dependent failure collapses to the SINGLE uniform
1187
+ * `cms/decrypt-failed` verdict (Bleichenbacher / EFAIL oracle freedom, and no unwrap-success bit
1188
+ * for the MAC path). That covers a bad key-wrap, a padding fault, a GCM tag mismatch, a PWRI
1189
+ * check-byte mismatch, and an AuthenticatedData MAC/message-digest mismatch. The PKCS#1 v1.5 arm
1190
+ * applies the RFC 3218 implicit-rejection countermeasure so its failure is indistinguishable. For an AuthenticatedData the MAC (HMAC-SHA-2)
1181
1191
  * and, when authenticated attributes are present, the message-digest are verified before the content
1182
1192
  * is released, and the result carries `macAlgorithm` / `digestAlgorithm` in place of
1183
1193
  * `contentEncryptionAlgorithm`. `authenticated` is true for AuthEnvelopedData and AuthenticatedData; a
1184
1194
  * CBC EnvelopedData surfaces `authenticated: false` (the EFAIL caveat in the verdict itself).
1185
1195
  *
1196
+ * `authenticated` is a claim about the CONTENT and the key that opened it, never about who sent the
1197
+ * message, so the origin question is answered separately. `originAuthenticated` is `false` for every
1198
+ * recipient type: a `ktri` or ephemeral-static `kari` message is minted by anyone holding the recipient's
1199
+ * PUBLIC key, and a `pwri` or `kekri` message by any co-recipient sharing the secret. Read
1200
+ * `authenticated: true` as "these bytes were not altered after the key was chosen", and do not read it as
1201
+ * a sender identity -- to bind one, verify a signature (`pki.cms.verify`) over the plaintext.
1202
+ * `authenticatedBy` names what the integrity rests on (`"content-encryption-key"` for AEAD,
1203
+ * `"message-authentication-key"` for AuthenticatedData, `null` for an unauthenticated cipher).
1204
+ * `originatorInfo` is surfaced as the sender supplied it and is UNAUTHENTICATED: it sits outside the
1205
+ * AEAD's authenticated data, so it is a hint, never evidence, and any certificate it carries must be
1206
+ * validated before use.
1207
+ *
1186
1208
  * @opts recipientIndex Explicitly select the recipient by index (overrides key-material matching).
1187
1209
  * @opts maxIterations Lower the PBKDF2 iteration cap (a DoS bound; downward only).
1188
1210
  * @example
@@ -1208,7 +1230,7 @@ var decrypt = cmsDecrypt.decrypt;
1208
1230
  * ZLIB-compress `content` and wrap it as a CMS `CompressedData` ContentInfo (RFC 3274): version 0,
1209
1231
  * `compressionAlgorithm` = id-alg-zlibCompress (parameters omitted), `encapContentInfo` = the inner
1210
1232
  * content type plus the RFC 1950 ZLIB stream (RFC 1951 DEFLATE) as the eContent. There is exactly
1211
- * ONE compression algorithm in RFC 3274 -- ZLIB -- so there is no algorithm selector. CompressedData
1233
+ * one compression algorithm in RFC 3274, ZLIB, so there is no algorithm selector. CompressedData
1212
1234
  * is a size transform with NO integrity, confidentiality, or authentication (RFC 8551 sec. 2.4.5);
1213
1235
  * compress it, then sign or encrypt it if you need protection. Malformed input throws a typed `CmsError`.
1214
1236
  *
@@ -1231,7 +1253,7 @@ var compress = cmsCompress.compress;
1231
1253
  * Parse a CMS `CompressedData` (DER Buffer or PEM) and recover its content. It requires version 0,
1232
1254
  * `compressionAlgorithm` = id-alg-zlibCompress with absent-or-NULL parameters (RFC 3274 sec. 2), and a
1233
1255
  * present eContent, then inflates the RFC 1950 ZLIB stream. The inflate is BOUNDED at
1234
- * `C.LIMITS.COMPRESS_MAX_BYTES` (16 MiB) and stops before the output is materialized -- a
1256
+ * `C.LIMITS.COMPRESS_MAX_BYTES` (16 MiB) and stops before the output is materialized, a
1235
1257
  * decompression-bomb / resource-exhaustion defense (CWE-409): a cap breach throws
1236
1258
  * `cms/decompress-too-large`, and every malformed / truncated / corrupt stream collapses to the uniform
1237
1259
  * `cms/decompress-failed`. `opts.maxOutputBytes` tightens the cap DOWNWARD only. The verdict carries NO
@@ -13,9 +13,9 @@
13
13
  // mldsaSig || tradSig (sec. 4.3). Verification (sec. 2) reconstructs
14
14
  // M' = Prefix || Label || len(ctx) || ctx || PH(M),
15
15
  // verifies the ML-DSA component over M' with ctx = the composite Label, verifies the traditional
16
- // component over M' under its own hash, and accepts IFF BOTH pass (THREAT-MODEL: all components
17
- // must verify -- never an AND-to-OR downgrade). The ML-DSA component is the fixed-length FIRST
18
- // half; the split point is its length.
16
+ // component over M' under its own hash, and accepts if and only if both pass. Every component must
17
+ // verify; there is no AND-to-OR downgrade. The ML-DSA component is the fixed-length first half, and
18
+ // the split point is its length.
19
19
  //
20
20
  // Error-parameterized like the guard / validator families: each consumer passes its own typed
21
21
  // error CONSTRUCTOR `E` and its domain codes (path validation passes PathError + path/*; CMS
@@ -30,8 +30,8 @@ var edwardsPoint = require("./edwards-point");
30
30
  var subtle = webcrypto.webcrypto.subtle;
31
31
  var _b = asn1.build;
32
32
 
33
- // A caught error's OWN code if it shares the caller's domain prefix (the fallback's namespace,
34
- // e.g. path/ or cms/), else the fallback -- a foreign code (asn1/*) maps to the fallback so the
33
+ // A caught error's own code if it shares the caller's domain prefix (the fallback's namespace,
34
+ // e.g. path/ or cms/), else the fallback. A foreign code (asn1/*) maps to the fallback so the
35
35
  // verdict stays in the caller's error namespace. Mirrors path-validate's original pathCode when
36
36
  // the fallback is a path/* code.
37
37
  function _codeOf(e, fallback) {
@@ -55,8 +55,8 @@ var COMPOSITE_ALGS = {};
55
55
  // _comp(name, mldsa, ph, label, trad). `trad` is exactly one component shape:
56
56
  // { ec, hash } | { eddsa } | { rsaPss, hash, salt } | { rsaPkcs1, hash } |
57
57
  // { unsupported } for the arms Node's WebCrypto surface cannot verify (the two
58
- // brainpool curves) -- registered + params-guarded, deferred at verify to the
59
- // caller's unsupported-algorithm code rather than silently accepted.
58
+ // brainpool curves): registered and params-guarded, deferred at verify to the
59
+ // caller's unsupported-algorithm code instead of silently accepted.
60
60
  function _comp(name, mldsa, ph, label, trad) {
61
61
  var sz = MLDSA_COMPONENT[mldsa];
62
62
  COMPOSITE_ALGS[oid.byName(name)] = {
@@ -84,7 +84,7 @@ _comp("id-MLDSA87-RSA4096-PSS-SHA512", "ML-DSA-87", "SHA-512", "COMPSIG-MLDSA87-
84
84
  _comp("id-MLDSA87-ECDSA-P521-SHA512", "ML-DSA-87", "SHA-512", "COMPSIG-MLDSA87-ECDSA-P521-SHA512", { ec: "P-521", hash: "SHA-512" });
85
85
 
86
86
  // Wrap a raw component public key in the SPKI its WebCrypto import expects, so each half is
87
- // verified through the SAME import + verify seam the classical path uses (no second parallel
87
+ // verified through the same import and verify seam the classical path uses (no second parallel
88
88
  // verify path). tradPK for RSA is the RSAPublicKey DER; for EC the uncompressed point; for EdDSA
89
89
  // the raw public key.
90
90
  function _spkiFor(algNode, keyBytes) { return _b.sequence([algNode, _b.bitString(keyBytes, 0)]); }
@@ -120,10 +120,10 @@ function _verifyTradComponent(trad, tradPK, tradSig, mprime, E, badSig) {
120
120
  }
121
121
  if (trad.eddsa) {
122
122
  // node/OpenSSL imports any Ed25519/Ed448 SPKI without validating the point, and a low-order
123
- // (e.g. all-zeroes) key verifies a forged signature -- which would collapse the composite AND
124
- // to ML-DSA-only. Reject a non-full-order point before verify, through the same shared
123
+ // (e.g. all-zeroes) key verifies a forged signature, collapsing the composite conjunction to
124
+ // ML-DSA alone. Reject a non-full-order point before verify, through the same shared
125
125
  // edwards-point gate every other EdDSA verify path in the toolkit routes through, surfacing a
126
- // coded fault (like the ECDSA order-bound belt) rather than a silent false.
126
+ // coded fault (like the ECDSA order-bound belt) instead of a silent false.
127
127
  if (!edwardsPoint.validate(tradPK, trad.eddsa === "Ed25519" ? 6 : 7)) {
128
128
  throw new E(badSig, "the composite EdDSA component public key is not a valid, full-order Edwards point");
129
129
  }
@@ -132,7 +132,7 @@ function _verifyTradComponent(trad, tradPK, tradSig, mprime, E, badSig) {
132
132
  }
133
133
  if (trad.rsaPss || trad.rsaPkcs1) {
134
134
  // The composite OID fixes the RSA modulus size: a downgraded or mismatched modulus under the
135
- // declared OID (an id-MLDSA44-RSA2048-* whose component is really 1024-bit) is rejected BEFORE
135
+ // declared OID (an id-MLDSA44-RSA2048-* whose component is really 1024-bit) is rejected before
136
136
  // verify, so a weak RSA component cannot satisfy an arm that promises 2048/3072/4096 bits. A
137
137
  // malformed RSAPublicKey rejects the same way.
138
138
  var bits;
@@ -167,7 +167,7 @@ function compositeVerify(spkiBytes, sigBytes, message, d, E, unsupported, badSig
167
167
  if (bs.unusedBits !== 0) throw new E(badSig, "composite subjectPublicKey has unused bits");
168
168
  rawKey = bs.bytes;
169
169
  } catch (e) { return Promise.resolve({ ok: false, code: _codeOf(e, badSig), error: e }); }
170
- // The ML-DSA half is fixed-length and FIRST; the traditional half is the remainder. Both must be
170
+ // The ML-DSA half is fixed-length and comes first; the traditional half is the remainder. Both must be
171
171
  // non-empty for a well-formed composite.
172
172
  if (rawKey.length <= d.mldsaPk || sigBytes.length <= d.mldsaSig) {
173
173
  return Promise.resolve({ ok: false, code: badSig,
package/lib/constants.js CHANGED
@@ -19,7 +19,7 @@
19
19
  * Every scale helper is config-time / entry-point validation: it THROWS
20
20
  * `ConstantsError` on a non-finite or negative argument, and on a product
21
21
  * outside the safe-integer range, so an operator catches the typo at boot
22
- * rather than shipping a silently-wrong window or an Infinity that would
22
+ * instead of shipping a silently-wrong window or an Infinity that would
23
23
  * disable a size cap.
24
24
  *
25
25
  * @card
@@ -31,7 +31,7 @@ var frameworkError = require("./framework-error");
31
31
 
32
32
  var ConstantsError = frameworkError.ConstantsError;
33
33
 
34
- // _positive(n, who) -- the shared guard every scale helper runs. Config-
34
+ // _positive(n, who) is the shared guard every scale helper runs. Config-
35
35
  // time tier: a bad scale argument is an authoring bug, so it throws.
36
36
  function _positive(n, who) {
37
37
  if (typeof n !== "number" || !isFinite(n) || n < 0) {
@@ -43,10 +43,10 @@ function _positive(n, who) {
43
43
  return n;
44
44
  }
45
45
 
46
- // _scale(n, who, factor) -- validate the argument AND the product. A finite
46
+ // _scale(n, who, factor) validates the argument and the product. A finite
47
47
  // operand can still overflow the multiplication (days(1e304) -> Infinity),
48
48
  // and an Infinity handed onward silently disables any bound compared
49
- // against it (`len > Infinity` is always false) -- so a product outside the
49
+ // against it (`len > Infinity` is always false), so a product outside the
50
50
  // safe-integer range throws instead of returning.
51
51
  function _scale(n, who, factor) {
52
52
  var out = Math.round(_positive(n, who) * factor);