@blamejs/pki 0.8.30 → 0.8.48

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 (87) hide show
  1. package/CHANGELOG.md +411 -0
  2. package/README.md +67 -16
  3. package/bin/pki.js +716 -30
  4. package/examples/issue-a-certificate.js +90 -0
  5. package/examples/post-quantum.js +66 -0
  6. package/examples/read-a-certificate.js +56 -0
  7. package/examples/revoke-and-check.js +86 -0
  8. package/examples/sign-and-verify-cms.js +63 -0
  9. package/index.js +18 -5
  10. package/lib/acme.js +223 -74
  11. package/lib/alt-sig.js +441 -0
  12. package/lib/asn1-der.js +48 -25
  13. package/lib/attrcert-sign.js +27 -19
  14. package/lib/cbor-det.js +24 -14
  15. package/lib/cmc-build.js +9 -2
  16. package/lib/cmc-verify.js +14 -3
  17. package/lib/cmp-build.js +17 -5
  18. package/lib/cmp-session.js +24 -11
  19. package/lib/cmp-verify.js +28 -19
  20. package/lib/cms-compress.js +3 -1
  21. package/lib/cms-decrypt.js +102 -7
  22. package/lib/cms-encrypt.js +59 -7
  23. package/lib/cms-sign.js +31 -9
  24. package/lib/cms-verify.js +22 -18
  25. package/lib/composite-kem.js +30 -18
  26. package/lib/composite-sig.js +29 -14
  27. package/lib/constants.js +28 -0
  28. package/lib/crl-sign.js +111 -19
  29. package/lib/crmf-sign.js +167 -15
  30. package/lib/csr-sign.js +117 -6
  31. package/lib/ct.js +939 -48
  32. package/lib/est.js +29 -16
  33. package/lib/framework-error.js +12 -0
  34. package/lib/guard-async.js +1 -1
  35. package/lib/guard-bytes.js +103 -20
  36. package/lib/guard-crypto.js +205 -3
  37. package/lib/guard-encoding.js +6 -1
  38. package/lib/guard-identifier.js +55 -8
  39. package/lib/guard-intrinsic.js +43 -0
  40. package/lib/guard-json.js +122 -9
  41. package/lib/guard-limits.js +12 -1
  42. package/lib/guard-list.js +63 -8
  43. package/lib/guard-name.js +167 -20
  44. package/lib/guard-range.js +43 -5
  45. package/lib/guard-text.js +41 -0
  46. package/lib/guard-verdict.js +39 -8
  47. package/lib/hpke.js +688 -42
  48. package/lib/http-transport.js +156 -4
  49. package/lib/identity-match.js +4 -4
  50. package/lib/inspect.js +448 -2
  51. package/lib/jose.js +93 -25
  52. package/lib/key.js +18 -13
  53. package/lib/lint.js +165 -1
  54. package/lib/merkle.js +233 -27
  55. package/lib/ocsp.js +27 -14
  56. package/lib/oid.js +14 -4
  57. package/lib/path-validate.js +143 -61
  58. package/lib/pkcs12-build.js +7 -2
  59. package/lib/pki-build.js +31 -7
  60. package/lib/possession.js +575 -0
  61. package/lib/related-cert.js +708 -0
  62. package/lib/revocation-fetch.js +6 -6
  63. package/lib/scep.js +15 -5
  64. package/lib/schema-c509.js +5 -2
  65. package/lib/schema-cmc.js +1 -1
  66. package/lib/schema-cmp.js +8 -8
  67. package/lib/schema-cms.js +2 -15
  68. package/lib/schema-csr.js +69 -0
  69. package/lib/schema-engine.js +10 -3
  70. package/lib/schema-pkix.js +267 -4
  71. package/lib/schema-tsp.js +7 -6
  72. package/lib/sign-scheme.js +22 -9
  73. package/lib/sigstore.js +684 -168
  74. package/lib/sleep.js +8 -1
  75. package/lib/smime.js +22 -1
  76. package/lib/tlog.js +1235 -0
  77. package/lib/tls-cert-compress.js +25 -8
  78. package/lib/tsp-sign.js +120 -35
  79. package/lib/tuf.js +1395 -0
  80. package/lib/validator-tpm.js +4 -1
  81. package/lib/verify-seam.js +7 -1
  82. package/lib/webauthn-mds.js +41 -4
  83. package/lib/webauthn.js +43 -25
  84. package/lib/webcrypto.js +48 -46
  85. package/lib/x509-sign.js +225 -19
  86. package/package.json +2 -1
  87. package/sbom.cdx.json +6 -6
@@ -19,6 +19,9 @@ var compositeKem = require("./composite-kem");
19
19
  var C = require("./constants");
20
20
  var b = asn1.build;
21
21
  var subtle = webcrypto.webcrypto.subtle;
22
+ /** @internal The verify operation, captured at load. It checks the AuthenticatedData MAC, and one
23
+ * answering true accepts content nothing authenticated while returning it as plaintext. */
24
+ var _subtleVerify = intrinsic.uncurry(subtle.verify);
22
25
  var CmsError = frameworkError.CmsError;
23
26
  var WRAP_KEK_LENGTHS = schemaCms.WRAP_KEK_LENGTHS;
24
27
  var KEM_CT_LENGTHS = schemaCms.KEM_CT_LENGTHS;
@@ -61,6 +64,7 @@ async function decryptEnvelopedData(parsed, keyMaterial, opts, contentTypeName)
61
64
  for (var ci = 0; ci < candidates.length; ci++) {
62
65
  try {
63
66
  _assertSupported(candidates[ci].ri, keyMaterial);
67
+ _assertAuthenticatedOrOptedIn(candidates[ci].ri, eci, opts);
64
68
  var cek = await _acquireCek(candidates[ci].ri, keyMaterial, opts);
65
69
  try {
66
70
  var streamMode = opts.stream === true ? (candidates.length > 1 ? "eager" : "lazy") : null;
@@ -144,7 +148,10 @@ function _ridEq(rid, cert) {
144
148
  try { return guard.name.dnEqual(cert.issuer.rdns, rid.issuer.rdns, _err, "cms/bad-input", "recipient issuer") && cert.serialNumber === rid.serialNumber; }
145
149
  catch (_e) { return false; }
146
150
  }
147
- if (rid.subjectKeyIdentifier) { var ski = _skiOf(cert); return !!ski && Buffer.compare(ski, rid.subjectKeyIdentifier) === 0; }
151
+ /** @internal Through the captured comparator: this answers WHICH recipient a certificate is, and the
152
+ * plaintext a caller receives is the one that recipient's key unwraps, so a replacement answering
153
+ * zero picks the recipient. */
154
+ if (rid.subjectKeyIdentifier) { var ski = _skiOf(cert); return !!ski && intrinsic.compare(ski, rid.subjectKeyIdentifier) === 0; }
148
155
  return false;
149
156
  }
150
157
  function _skiOf(cert) {
@@ -157,7 +164,7 @@ function _skiOf(cert) {
157
164
  function _kekIdMatches(ri, kekId) {
158
165
  var id = ri.kekid && ri.kekid.keyIdentifier;
159
166
  if (!id) return false;
160
- return Buffer.compare(id, guard.bytes.view(kekId, CmsError, "cms/bad-input", "kekId")) === 0;
167
+ return intrinsic.compare(id, guard.bytes.view(kekId, CmsError, "cms/bad-input", "kekId")) === 0;
161
168
  }
162
169
 
163
170
  var _passThrough = intrinsic.assign(intrinsic.create(null), { "cms/unsupported-algorithm": 1, "cms/unsupported-recipient-type": 1, "cms/bad-input": 1, "cms/iteration-limit": 1, "cms/missing-key-derivation": 1, "cms/no-encrypted-content": 1, "cms/too-many-recipient-keys": 1 });
@@ -176,6 +183,35 @@ async function _acquireCek(ri, km, opts) {
176
183
  throw _err("cms/unsupported-recipient-type", "unsupported recipient type " + ri.type);
177
184
  }
178
185
 
186
+ /** @internal RSAES-PKCS1-v1_5 key transport over a content with no integrity tag is refused unless the
187
+ * caller opts in, and the refusal comes BEFORE the unwrap so no decision depends on whether the unwrap
188
+ * conformed. The combination cannot be made both correct and indistinguishable: the CBC padding check is
189
+ * the only integrity signal the content offers, so either a failed unwrap is accepted and the verb can
190
+ * return the plaintext of a substituted key, or it is refused and acceptance then tells an attacker that
191
+ * their chosen ciphertext decoded. MEASURED, 512 chosen ciphertexts per arm against a one-block
192
+ * AES-256-CBC content: 2 accepted where the unwrap conformed and 0 where it did not, which is the
193
+ * Bleichenbacher distinction at the padding check's own rate of about one in 256.
194
+ * THE OPT-IN EXISTS BECAUSE THIS IS WHAT OPENSSL PRODUCES BY DEFAULT. `openssl cms -encrypt` with no
195
+ * algorithm named emits exactly `rsaEncryption` with `aes-256-cbc`, decrypts it, and reports
196
+ * `ossl_cipher_unpadblock: bad decrypt` on a wrong key, so it leaks the same signal and names the step.
197
+ * Refusing outright would make those messages unreadable here, so the default is the safe one and the
198
+ * operator who must read them says so. An AEAD content needs no opt-in: the tag rejects a wrong key
199
+ * before any plaintext is released, which is why AES-GCM is the encrypt default. RSAES-OAEP needs none
200
+ * either, its own padding check being the integrity signal RFC 8017 sec. 7.1 gives it. */
201
+ function _assertAuthenticatedOrOptedIn(ri, eci, opts) {
202
+ if (opts.allowUnauthenticatedRsa15 === true) return;
203
+ if (ri.type !== "ktri") return;
204
+ if (ri.keyEncryptionAlgorithm.oid !== O("rsaEncryption")) return;
205
+ if (intrinsic.hasOwn(AEAD_ALGS, eci.contentEncryptionAlgorithm.oid)) return;
206
+ throw _err("cms/unauthenticated-rsa-v15",
207
+ "this message uses RSAES-PKCS1-v1_5 key transport over a content with no integrity tag (" +
208
+ (eci.contentEncryptionAlgorithm.name || eci.contentEncryptionAlgorithm.oid) + "), a combination whose " +
209
+ "only integrity check is the content padding: decrypting it either returns the plaintext of a " +
210
+ "substituted key or tells an attacker submitting chosen ciphertexts that theirs decoded. Pass " +
211
+ "allowUnauthenticatedRsa15 to read it anyway, which is what reading an `openssl cms -encrypt` default " +
212
+ "message needs, or re-encrypt to an AEAD content such as aes-256-gcm");
213
+ }
214
+
179
215
  async function _ktriCek(ri, km) {
180
216
  var kea = ri.keyEncryptionAlgorithm;
181
217
  var k = _normKeyDer(km.key);
@@ -187,7 +223,7 @@ async function _ktriCek(ri, km) {
187
223
  }
188
224
  if (kea.oid === O("rsaEncryption")) {
189
225
  var keyObj = nodeCrypto.createPrivateKey({ key: k.der, format: "der", type: "pkcs8" });
190
- try { return nodeCrypto.privateDecrypt({ key: keyObj, padding: nodeCrypto.constants.RSA_PKCS1_PADDING }, ri.encryptedKey); }
226
+ try { return nodeCrypto.privateDecrypt({ key: keyObj, padding: guard.crypto.padding.RSA_PKCS1 }, ri.encryptedKey); }
191
227
  catch (_e) {
192
228
  return null;
193
229
  }
@@ -349,6 +385,18 @@ function _assertContentCipherMode(eci, ct) {
349
385
  throw _err("cms/unsupported-algorithm", "contentEncryptionAlgorithm " + oidStr + " is an authenticated encryption algorithm, which belongs in an authEnvelopedData rather than a " + ct + " (RFC 5083 sec. 2)");
350
386
  }
351
387
 
388
+ /** @internal A candidate whose key unwrap did not produce a content key of the right length has FAILED,
389
+ * and the decrypt under a substitute runs only so that the failure costs the same work and the same time
390
+ * as a success: a recipient whose RSA v1.5 unwrap is implicitly rejected must not be distinguishable from
391
+ * one whose key simply does not match. Its result is therefore never a result. Without this the content
392
+ * decrypt decided it, and a random substitute key leaves a final CBC block that is valid PKCS#7 padding
393
+ * about one time in 256, so a known-bad candidate won that often and the verb returned WRONG PLAINTEXT
394
+ * for it while reporting that recipient as the one it used. An AEAD content algorithm rejected it on the
395
+ * tag already; this closes the non-AEAD modes, where padding is the only check the content offers. */
396
+ function _assertNotSubstituted(substitute) {
397
+ if (substitute !== null) throw _fail();
398
+ }
399
+
352
400
  async function _openContent(parsed, eci, cek, ct, streamMode) {
353
401
  var alg = eci.contentEncryptionAlgorithm;
354
402
  var keyBits = intrinsic.hasOwn(CONTENT_KEYBITS, alg.oid) ? CONTENT_KEYBITS[alg.oid] : 0;
@@ -365,13 +413,32 @@ async function _openContent(parsed, eci, cek, ct, streamMode) {
365
413
  if (ct === "authEnvelopedData") {
366
414
  var aad = parsed.authAttrsBytes != null ? _explicitSetOf(parsed.authAttrsBytes) : Buffer.alloc(0);
367
415
  var pt = _gcmOpen(cek, parsed.aead.nonce, eci.encryptedContent, parsed.mac, aad, keyBits, parsed.aead.icvLen);
416
+ _assertNotSubstituted(substitute);
368
417
  return streamMode ? _bufferChunks(pt) : pt;
369
418
  }
370
419
  var iv = asn1.read.octetString(asn1.decode(alg.parameters));
371
420
  if (iv.length !== 16) throw _fail();
372
- if (streamMode === "lazy") return _cbcStream(cek, iv, eci.encryptedContent, keyBits);
373
- if (streamMode === "eager") return _bufferChunks(pbes2.cbcDecrypt(cek, iv, eci.encryptedContent, keyBits, CmsError, "cms/decrypt-failed"));
374
- return pbes2.cbcDecrypt(cek, iv, eci.encryptedContent, keyBits, CmsError, "cms/decrypt-failed");
421
+ if (streamMode === "lazy") {
422
+ var lazy = _cbcStream(cek, iv, eci.encryptedContent, keyBits);
423
+ /** @internal A substituted candidate streams exactly as any other wrong key does and then fails,
424
+ * and all three of those words are load-bearing. On this path a key of the RIGHT length but the
425
+ * wrong value yields its garbage chunks and only then fails the padding check, so a substituted
426
+ * one that refused at the call, or that yielded nothing, would say which of the two happened, and
427
+ * whether the v1.5 padding was valid is exactly what the substitute exists to hide: the stage, the
428
+ * chunks and the code all have to match, not just the code. So the decrypt runs, its chunks are
429
+ * delivered, and the verdict is a refusal whether or not the padding happened to pass, which is
430
+ * what keeps a lucky substitute from becoming an answer. Chunks from a CBC stream are unverified
431
+ * until it completes either way, which is why the verdict carries `authenticated: false`. */
432
+ return substitute !== null ? _streamThenRefuse(lazy) : lazy;
433
+ }
434
+ if (streamMode === "eager") {
435
+ var eager = _bufferChunks(pbes2.cbcDecrypt(cek, iv, eci.encryptedContent, keyBits, CmsError, "cms/decrypt-failed"));
436
+ _assertNotSubstituted(substitute);
437
+ return eager;
438
+ }
439
+ var buffered = pbes2.cbcDecrypt(cek, iv, eci.encryptedContent, keyBits, CmsError, "cms/decrypt-failed");
440
+ _assertNotSubstituted(substitute);
441
+ return buffered;
375
442
  } catch (e) {
376
443
  if (e instanceof CmsError && e.code !== "cms/decrypt-failed") throw e;
377
444
  throw _fail();
@@ -401,6 +468,34 @@ async function* _translateCbcStream(engineIter) {
401
468
  try { for await (var chunk of engineIter) yield chunk; }
402
469
  catch (_e) { throw _fail(); }
403
470
  }
471
+ /** @internal Everything the inner stream yields, then a refusal, for a candidate the implicit rejection
472
+ * substituted for on the lazy path. The chunks and the stage are then the ones any other wrong key on
473
+ * that path produces, and the refusal does not wait on the padding check to decide, so a substitute
474
+ * whose final block happens to be valid padding is still refused. */
475
+ async function* _streamThenRefuse(inner) {
476
+ /** @internal One chunk of lookahead, so the chunk COUNTS match too and not merely the code and the
477
+ * stage. An ordinary wrong key's stream yields the padded content less its final block and then
478
+ * refuses, the final block never arriving because the padding check is what rejects it. A substitute
479
+ * whose final block happens to be valid padding, about one time in 256, instead COMPLETES and yields
480
+ * that last stripped chunk, which is a chunk no wrong key ever emits: forwarding it would say the
481
+ * substitute was lucky, and a one-in-256 signal is still a signal. So the last chunk is held back, and
482
+ * released only on the path where a wrong key would also have produced everything it produced. */
483
+ /** @internal The held chunk lives in a list of at most one, so releasing it is `yield*` and needs no
484
+ * test: an empty list yields nothing. The branch a test would need is reachable, the inner stream
485
+ * rejecting on its first read when the content length is not a block multiple, so the test would be
486
+ * load-bearing rather than defensive; expressing the absence as an empty list removes the question. */
487
+ var held = [];
488
+ try {
489
+ for await (var chunk of inner) {
490
+ yield* held;
491
+ held = [chunk];
492
+ }
493
+ } catch (_e) {
494
+ yield* held;
495
+ throw _fail();
496
+ }
497
+ throw _fail();
498
+ }
404
499
 
405
500
  async function _decryptEncryptedData(parsed, km, opts) {
406
501
  var eci = parsed.encryptedContentInfo;
@@ -607,7 +702,7 @@ async function _verifyAuthenticatedData(parsed, km, opts) {
607
702
  if (macKey == null || macKey.length < MAC_KEY_MIN_OCTETS) macSubstitute = nodeCrypto.randomBytes(MAC_KEY_MIN_OCTETS);
608
703
  try {
609
704
  var key = await subtle.importKey("raw", macSubstitute || macKey, { name: "HMAC", hash: hash }, false, ["verify"]);
610
- if (!(await subtle.verify({ name: "HMAC" }, key, Buffer.from(parsed.mac), preimage))) throw _fail();
705
+ if (!(await _subtleVerify(subtle, { name: "HMAC" }, key, Buffer.from(parsed.mac), preimage))) throw _fail();
611
706
  if (mdCheck) {
612
707
  var actual = Buffer.from(await subtle.digest(mdCheck.hash, content));
613
708
  if (!actual.equals(mdCheck.declared)) throw _fail();
@@ -374,12 +374,28 @@ var _AUTHENTICATE_OPTS_BARE = intrinsic.assign(intrinsic.create(null), {
374
374
  keyIdentifier: 1, oaepHash: 1, ukm: 1, pem: 1,
375
375
  });
376
376
 
377
+ /** @internal A byte content is fixed first, ahead of the recipients and the options. Copying either of
378
+ * those reads the caller's own objects, so a property on one of them is caller code that runs before
379
+ * the content would otherwise have been read, and over a view it could replace what gets encrypted
380
+ * after this verb had accepted the content it was handed. The discriminator is a type test, which reads
381
+ * no property and so cannot be that accessor itself. A streamed content has no fixed form here: it is
382
+ * read through its iterator, which `_encryptImpl` probes only after the options have been copied, and
383
+ * is held to the same rule where it is consumed. */
377
384
  function encrypt(content, recipients, opts) {
378
- return guard.bytes.fixedCall(CmsError, "cms/bad-input", [
379
- [recipients, "recipients"], [opts, "pki.cms.encrypt options"],
380
- ], function (recipientsCopy, optsCopy) {
381
- return _encryptImpl(content, recipientsCopy, optsCopy);
382
- });
385
+ if (!guard.bytes.isByteSource(content)) {
386
+ return guard.bytes.fixedCall(CmsError, "cms/bad-input",
387
+ [[recipients, "recipients"], [opts, "pki.cms.encrypt options"]],
388
+ function (recipientsCopy, optsCopy) { return _encryptImpl(content, recipientsCopy, optsCopy); });
389
+ }
390
+ /** @internal The argument list is written out rather than assembled from a shorter one. Built with
391
+ * `Array.prototype.concat`, the list a fixed call fixes was chosen by that method: a replacement
392
+ * could keep the caller's recipients and options and put OTHER bytes in the content slot, and this
393
+ * verb then encrypted those. A literal is not a dispatch, so there is nothing to replace. */
394
+ return guard.bytes.fixedCall(CmsError, "cms/bad-input",
395
+ [[content, "content"], [recipients, "recipients"], [opts, "pki.cms.encrypt options"]],
396
+ function (contentCopy, recipientsCopy, optsCopy) {
397
+ return _encryptImpl(contentCopy, recipientsCopy, optsCopy);
398
+ });
383
399
  }
384
400
 
385
401
  async function _encryptImpl(content, recipients, opts) {
@@ -395,6 +411,9 @@ async function _encryptImpl(content, recipients, opts) {
395
411
  : "");
396
412
  });
397
413
  var contentStream = guard.bytes.asyncStreamOf(content);
414
+ /** @internal A view is enough here: `encrypt` fixed a byte content at the door, ahead of the recipient
415
+ * and option copies, so these bytes are already this module's own and no caller accessor reaches
416
+ * them. */
398
417
  var contentBytes = contentStream ? null : guard.bytes.view(content, CmsError, "cms/bad-input", "content");
399
418
  var contentType = opts.contentType || "data";
400
419
  var cek = nodeCrypto.randomBytes(ca.keyBits / 8);
@@ -565,7 +584,32 @@ var MAC_ALGS = intrinsic.assign(intrinsic.create(null), {
565
584
  var MAC_KEY_OCTETS = 32;
566
585
  var SUPPORTED_DIGEST = intrinsic.assign(intrinsic.create(null), { sha256: 1, sha384: 1, sha512: 1 });
567
586
 
568
- async function authenticate(content, recipients, opts) {
587
+ /** @internal The same door `encrypt` has, and for the same reason. This verb reads the caller's options
588
+ * before anything else, to decide whether authenticated attributes are built, so an accessor there is
589
+ * caller code that runs before the content would otherwise have been read; a getter on a recipient runs
590
+ * later, while the RecipientInfos are built. Over a view of the content either one could replace what
591
+ * the message authenticates and carries, after this verb had accepted the content it was handed, so a
592
+ * byte content is fixed first and the recipients and options are copies from there on. Closing one of
593
+ * those two routes and not the other leaves the rule holding for the argument that was looked at. The
594
+ * discriminator is a type test, which reads no property and so cannot be the accessor itself; a content
595
+ * that is not a byte source is passed through and refused where it is read, as before. */
596
+ function authenticate(content, recipients, opts) {
597
+ if (!guard.bytes.isByteSource(content)) {
598
+ return guard.bytes.fixedCall(CmsError, "cms/bad-input",
599
+ [[recipients, "recipients"], [opts, "pki.cms.authenticate options"]],
600
+ function (recipientsCopy, optsCopy) { return _authenticateImpl(content, recipientsCopy, optsCopy); });
601
+ }
602
+ /** @internal Written out for the reason `encrypt` above gives: a list assembled by a live
603
+ * `Array.prototype.concat` is a list that method chooses, and here it decides which bytes the MAC
604
+ * is computed over. */
605
+ return guard.bytes.fixedCall(CmsError, "cms/bad-input",
606
+ [[content, "content"], [recipients, "recipients"], [opts, "pki.cms.authenticate options"]],
607
+ function (contentCopy, recipientsCopy, optsCopy) {
608
+ return _authenticateImpl(contentCopy, recipientsCopy, optsCopy);
609
+ });
610
+ }
611
+
612
+ async function _authenticateImpl(content, recipients, opts) {
569
613
  opts = opts || {};
570
614
  var withAttrs = opts.authenticatedAttributes !== false;
571
615
  guard.identifier.assertKnownKeys(opts, withAttrs ? _AUTHENTICATE_OPTS_ATTRS : _AUTHENTICATE_OPTS_BARE,
@@ -575,6 +619,9 @@ async function authenticate(content, recipients, opts) {
575
619
  ? " with authenticatedAttributes: false; the MAC then covers the content octets directly and no attribute set is built"
576
620
  : "");
577
621
  });
622
+ /** @internal A view is enough here: `authenticate` fixed a byte content at the door, ahead of the
623
+ * recipient and option copies, so these bytes are already this module's own and no caller accessor
624
+ * reaches them. */
578
625
  var contentBytes = guard.bytes.view(content, CmsError, "cms/bad-input", "content");
579
626
  var macName = opts.macAlgorithm || "hmac-sha256";
580
627
  var mac = MAC_ALGS[guard.text.keyOf(macName)];
@@ -595,7 +642,12 @@ async function authenticate(content, recipients, opts) {
595
642
  if (withAttrs) {
596
643
  var digestName = opts.digestAlgorithm || mac.node;
597
644
  if (!SUPPORTED_DIGEST[guard.text.keyOf(digestName)]) throw _err("cms/bad-input", "unsupported digestAlgorithm " + guard.text.showValue(digestName) + " (sha256/384/512)");
598
- var mdDigest = nodeCrypto.createHash(digestName).update(contentBytes).digest();
645
+ /** @internal Through the captured operations: `update` and `digest` are ordinary writable
646
+ * properties of the hash prototype, so a replacement decided what this digest was OVER, and the
647
+ * MAC below covers the attribute set carrying it. Measured, a replaced `update` put the digest
648
+ * of other bytes into the messageDigest attribute and the message no longer verified against
649
+ * the content it carries. */
650
+ var mdDigest = guard.crypto.digest(digestName, contentBytes);
599
651
  var pairs = [
600
652
  b.sequence([b.oid(O("contentType")), b.setOf([b.oid(O(contentType))])]),
601
653
  b.sequence([b.oid(O("messageDigest")), b.setOf([b.octetString(mdDigest)])]),
package/lib/cms-sign.js CHANGED
@@ -15,6 +15,14 @@ var webcrypto = require("./webcrypto");
15
15
  var signScheme = require("./sign-scheme");
16
16
  var guard = require("./guard-all");
17
17
  var intrinsic = require("./guard-intrinsic");
18
+ /** @internal `resolve` and `all` build their promise from the RECEIVER, so reading the global binding
19
+ * at the call hands the construction to whatever that binding holds. Each signer's digest and
20
+ * signature are awaited through one of these, and a replacement that settles without awaiting emits a
21
+ * SignedData whose SignerInfos were never completed. The constructor and both operations are taken at
22
+ * load. */
23
+ var _Promise = intrinsic.Promise;
24
+ var _promiseResolve = intrinsic.uncurry(intrinsic.promiseResolve);
25
+ var _promiseAll = intrinsic.uncurry(intrinsic.promiseAll);
18
26
  var pkiBuild = require("./pki-build");
19
27
  var cms = require("./schema-cms");
20
28
 
@@ -72,10 +80,16 @@ function _buildSid(cert, useSki) {
72
80
  return { sid: sid, version: useSki ? 3 : 1 };
73
81
  }
74
82
 
83
+ /** @internal The signed side keeps its blanket refusal of a repeated type, and the citation is what
84
+ * changes. RFC 5652 clause 5.3 does not state that rule: what it states is per type, "The
85
+ * SignedAttributes in a signerInfo MUST include only one instance of the message-digest attribute".
86
+ * Refusing every repeat is this builder's posture, stricter than the RFC and in the safe direction for
87
+ * something being emitted, so it stands; naming a clause that does not say it did not. The unsigned
88
+ * side had the same misattribution and could not keep the rule, a repeat being ordinary there. */
75
89
  function _buildSignedAttrs(pairs) {
76
90
  var seenTypes = intrinsic.create(null);
77
91
  var attrs = intrinsic.map(pairs, function (p) {
78
- if (intrinsic.hasOwn(seenTypes, p.type)) throw _err("cms/bad-input", "signedAttrs must not repeat an attribute type (RFC 5652 sec. 5.3): " + p.type);
92
+ if (intrinsic.hasOwn(seenTypes, p.type)) throw _err("cms/bad-input", "this builder emits one instance of each signed attribute type, and " + p.type + " is repeated; RFC 5652 sec. 11.2 requires it of the message-digest attribute and this build asks it of all of them");
79
93
  seenTypes[p.type] = 1;
80
94
  return b.sequence([b.oid(p.type), b.set(p.values)]);
81
95
  });
@@ -105,11 +119,17 @@ function _buildUnsignedAttrs(list) {
105
119
  if (!intrinsic.isArray(list)) throw _err("cms/bad-input", "opts.unsignedAttributes must be an array of { type, values }");
106
120
  if (!list.length) return null;
107
121
  var pairs = _resolveAttrPairs(list, "an unsigned attribute value");
108
- var seen = intrinsic.create(null);
122
+ /** @internal A TYPE MAY REPEAT HERE, and the RFC is the authority for that rather than against it.
123
+ * `UnsignedAttributes ::= SET SIZE (1..MAX) OF Attribute` imposes no per-type uniqueness; clause 5.3
124
+ * says of the field only that it "is a collection of attributes that are not signed. The field is
125
+ * optional"; and the uniqueness rules the RFC does state are per type, "The SignedAttributes in a
126
+ * signerInfo MUST include only one instance of the message-digest attribute", while clause 11.4's
127
+ * countersignature type "specifies one or more signatures". A blanket refusal citing clause 5.3
128
+ * therefore denied a conforming message, and the shape it denied is the ordinary one: a signature
129
+ * re-timestamped by several authorities carries one timeStampToken attribute per authority. The
130
+ * per-type rules below are kept, and they are the ones with a clause behind them. */
109
131
  intrinsic.forEach(pairs, function (p) {
110
132
  if (intrinsic.hasOwn(UNSIGNED_FORBIDDEN, p.type)) throw _err("cms/bad-input", "the " + UNSIGNED_FORBIDDEN[p.type] + " attribute must not appear as an unsigned attribute (RFC 5652 sec. 11)");
111
- if (intrinsic.hasOwn(seen, p.type)) throw _err("cms/bad-input", "unsignedAttrs must not repeat an attribute type (RFC 5652 sec. 5.3): " + p.type);
112
- seen[p.type] = 1;
113
133
  });
114
134
  var setOf = b.set(intrinsic.map(pairs, function (p) { return b.sequence([b.oid(p.type), b.set(p.values)]); }));
115
135
  var wire = intrinsic.bufferFrom(setOf); wire[0] = 0xA1;
@@ -157,7 +177,7 @@ async function _finishSignerInfo(rc, md, content, eContentType, opts) {
157
177
 
158
178
  function _buildSignerInfo(signer, content, eContentType, opts) {
159
179
  var rc = _resolveSignerContext(signer, opts);
160
- var mdP = opts.signedAttributes === false ? Promise.resolve(null) : _digest(rc.scheme.digest, content);
180
+ var mdP = opts.signedAttributes === false ? _promiseResolve(_Promise, null) : _digest(rc.scheme.digest, content);
161
181
  return mdP.then(function (md) { return _finishSignerInfo(rc, md, content, eContentType, opts); });
162
182
  }
163
183
 
@@ -323,7 +343,7 @@ function _sign(content, signers, opts) {
323
343
  }
324
344
  if (opts.signingTime != null && opts.signingTime !== false) guard.time.assertEncodable(opts.signingTime, _err, "cms/bad-input", "signingTime");
325
345
 
326
- return Promise.all(list.map(function (s) { return _buildSignerInfo(s, contentBuf, eContentType, opts); }))
346
+ return _promiseAll(_Promise, list.map(function (s) { return _buildSignerInfo(s, contentBuf, eContentType, opts); }))
327
347
  .then(function (built) { return _assembleSignedData(built, eContentType, opts, contentBuf); });
328
348
  }
329
349
 
@@ -435,7 +455,7 @@ function _buildCountersignature(targetSigOctets, countersigner, opts) {
435
455
  var scheme = signScheme.resolveSignScheme(cert, so, opts.signedAttributes === false, _signE);
436
456
  var sidv = _buildSid(cert, opts.sid === "ski");
437
457
  var soKey = so.key;
438
- return Promise.resolve().then(function () {
458
+ return _promiseResolve(_Promise, undefined).then(function () {
439
459
  if (opts.signedAttributes === false) return null;
440
460
  return _digest(scheme.digest, targetSigOctets).then(function (md) {
441
461
  var pairs = [{ type: O("messageDigest"), values: [b.octetString(md)] }];
@@ -551,7 +571,7 @@ function _countersign(cmsInput, signers, opts) {
551
571
  var preimage = _targetPreimage(siSet.children[t], opts);
552
572
  list.forEach(function (cs) { jobs.push({ t: t, p: _buildCountersignature(preimage, cs, opts) }); });
553
573
  });
554
- return Promise.all(jobs.map(function (j) { return j.p; })).then(function (built) {
574
+ return _promiseAll(_Promise, jobs.map(function (j) { return j.p; })).then(function (built) {
555
575
  var byTarget = intrinsic.create(null), certDers = [], csDigestAlgs = [];
556
576
  intrinsic.forEach(built, function (res, i) {
557
577
  var slot = byTarget[jobs[i].t] || (byTarget[jobs[i].t] = []);
@@ -573,7 +593,9 @@ function _countersign(cmsInput, signers, opts) {
573
593
  var existing = [];
574
594
  if (certsNode) certsNode.children.forEach(function (c) { existing.push(c.bytes); });
575
595
  if (opts.certificates !== false) certDers.forEach(function (d) { existing.push(d); });
576
- var allCerts = _dedupe(existing).sort(Buffer.compare);
596
+ /** @internal The DER SET ordering through the captured copy, sort AND comparator: read off its own
597
+ * home, any of the three decides which certificate lands where in the set this message carries. */
598
+ var allCerts = guard.list.sortedCopy(_dedupe(existing), intrinsic.compare);
577
599
 
578
600
  /** @internal RFC 5652 sec. 5.1 has this collection list "the message digest algorithms employed
579
601
  * by all of the signers", and sec. 11.4 makes a countersignature a SignerInfo, so sec. 5.3's
package/lib/cms-verify.js CHANGED
@@ -49,9 +49,14 @@ var intrinsic = require("./guard-intrinsic");
49
49
  var frameworkError = require("./framework-error");
50
50
  var pkix = require("./schema-pkix");
51
51
 
52
+ /** @internal The verify operation, captured at load. The object was held and its METHOD fetched per
53
+ * call, which leaves it replaceable: one answering true accepts a SignerInfo no signer signed, which
54
+ * is the whole verdict this module reports. `path-validate` and `ct` hold it the same way. */
55
+ var _subtleVerify = intrinsic.uncurry(subtle.verify);
56
+
52
57
  var CmsError = frameworkError.CmsError;
53
58
  function _err(code, message, cause) { return new CmsError(code, message, cause); }
54
- var _Promise = Promise, _promiseResolveRaw = Promise.resolve;
59
+ var _Promise = intrinsic.Promise, _promiseResolveRaw = intrinsic.promiseResolve;
55
60
  /**
56
61
  * @internal Every value resolved here is a signer row, and resolving a promise reads `then` off the
57
62
  * value. Building the row through the verdict guard ends that lookup on the row itself, so an
@@ -214,26 +219,26 @@ function _resolvePss(paramsBytes) {
214
219
  async function _verifySignature(scheme, hashName, sigBytes, spki, signedBytes, curveOid, pssSalt) {
215
220
  if (scheme.kind === "rsa") {
216
221
  var kRsa = await subtle.importKey("spki", spki, { name: "RSASSA-PKCS1-v1_5", hash: hashName }, false, ["verify"]);
217
- return subtle.verify({ name: "RSASSA-PKCS1-v1_5" }, kRsa, sigBytes, signedBytes);
222
+ return _subtleVerify(subtle, { name: "RSASSA-PKCS1-v1_5" }, kRsa, sigBytes, signedBytes);
218
223
  }
219
224
  if (scheme.kind === "rsapss") {
220
225
  var kPss = await subtle.importKey("spki", spki, { name: "RSA-PSS", hash: hashName }, false, ["verify"]);
221
- return subtle.verify({ name: "RSA-PSS", saltLength: pssSalt }, kPss, sigBytes, signedBytes);
226
+ return _subtleVerify(subtle, { name: "RSA-PSS", saltLength: pssSalt }, kPss, sigBytes, signedBytes);
222
227
  }
223
228
  if (scheme.kind === "ec") {
224
229
  var ec = EC_CURVE[curveOid];
225
230
  if (!ec) throw _err("cms/unsupported-algorithm", "the signer key is on an unsupported EC curve");
226
231
  var raw = validator.sig.ecdsaDerToP1363(sigBytes, ec.curve, CmsError, "cms/bad-signature");
227
232
  var kEc = await subtle.importKey("spki", spki, { name: "ECDSA", namedCurve: ec.curve }, false, ["verify"]);
228
- return subtle.verify({ name: "ECDSA", hash: hashName }, kEc, raw, signedBytes);
233
+ return _subtleVerify(subtle, { name: "ECDSA", hash: hashName }, kEc, raw, signedBytes);
229
234
  }
230
235
  if (scheme.kind === "mldsa" || scheme.kind === "slhdsa") {
231
236
  var kPq = await subtle.importKey("spki", spki, { name: scheme.name }, false, ["verify"]);
232
- return subtle.verify({ name: scheme.name }, kPq, sigBytes, signedBytes);
237
+ return _subtleVerify(subtle, { name: scheme.name }, kPq, sigBytes, signedBytes);
233
238
  }
234
239
  _requireValidEdPoint(spki, scheme.name);
235
240
  var kEd = await subtle.importKey("spki", spki, { name: scheme.name }, false, ["verify"]);
236
- return subtle.verify({ name: scheme.name }, kEd, sigBytes, signedBytes);
241
+ return _subtleVerify(subtle, { name: scheme.name }, kEd, sigBytes, signedBytes);
237
242
  }
238
243
 
239
244
  function _requireValidEdPoint(spkiBytes, name) {
@@ -562,7 +567,11 @@ async function _verify(input, opts) {
562
567
  content = _toBuf(_snapshotIfBytes(opts.content, "opts.content"), "opts.content");
563
568
  }
564
569
  } else if (opts.content != null) {
565
- if (Buffer.compare(_toBuf(opts.content, "opts.content"), Buffer.from(content)) !== 0) {
570
+ /** @internal The comparison goes through the captured operation. Read off `Buffer`, it decides
571
+ * whether the content a caller supplied is the content this message carries, so a replacement
572
+ * answering zero would have this verb accept a detached content that disagrees with the embedded
573
+ * one and then report a verdict about whichever of the two it went on to digest. */
574
+ if (intrinsic.compare(_toBuf(opts.content, "opts.content"), Buffer.from(content)) !== 0) {
566
575
  throw _err("cms/content-conflict", "opts.content disagrees with this SignedData's own encapsulated content (a detached signature must omit eContent)");
567
576
  }
568
577
  }
@@ -631,21 +640,15 @@ function _snapshotTrust(opts) {
631
640
  };
632
641
  }
633
642
 
634
- var _CERT_EXT_DECODERS = pkix.certExtensionDecoders(pkix.makeNS("cms", CmsError, oid)).byOid;
643
+ /** @internal A signature over content is authorized by digitalSignature, contentCommitment or
644
+ * nonRepudiation alike; the shared helper carries the rest of the rule. */
645
+ var _permitsSigning = pkix.keyUsagePermits(pkix.makeNS("cms", CmsError, oid),
646
+ ["digitalSignature", "contentCommitment", "nonRepudiation"]);
635
647
 
636
648
  function _keyUsagePermitsSigning(parsedCerts, der) {
637
649
  var entry = intrinsic.filter(parsedCerts, function (c) { return intrinsic.bufferEquals(c.der, der); })[0];
638
650
  if (!entry) return true;
639
- var kuOid = oid.byName("keyUsage");
640
- var exts = entry.cert.extensions || [];
641
- for (var i = 0; i < exts.length; i++) {
642
- if (exts[i].oid !== kuOid) continue;
643
- var ku;
644
- try { ku = _CERT_EXT_DECODERS[kuOid](exts[i].value); }
645
- catch (_e) { return false; }
646
- return ku.digitalSignature === true || ku.contentCommitment === true || ku.nonRepudiation === true;
647
- }
648
- return true;
651
+ return _permitsSigning(entry.cert);
649
652
  }
650
653
 
651
654
  function _allTrusted(signers) {
@@ -951,6 +954,7 @@ var authenticate = cmsEncrypt.authenticate;
951
954
  * @opts recipientIndex Explicitly select the recipient by index (overrides key-material matching).
952
955
  * @opts maxIterations Lower the PBKDF2 iteration cap (a DoS bound; downward only).
953
956
  * @opts stream Return `content` as an async iterable of plaintext `Buffer` chunks instead of a single `Buffer`.
957
+ * @opts allowUnauthenticatedRsa15 Read a message that uses RSAES-PKCS1-v1_5 key transport over a content with no integrity tag, such as AES-CBC. Refused by default with `cms/unauthenticated-rsa-v15`: the content padding is that combination's only integrity check, so decrypting it either returns the plaintext of a substituted key or tells an attacker submitting chosen ciphertexts that theirs decoded (RFC 8017 sec. 7.2). `openssl cms -encrypt` emits exactly this combination when no algorithm is named, so reading one of its messages needs this option. An AEAD content or an RSAES-OAEP recipient needs none.
954
958
  * @example
955
959
  * var rsa = { name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" };
956
960
  * var pair = await pki.key.generate(rsa);
@@ -26,6 +26,18 @@ var webcrypto = require("./webcrypto");
26
26
  var nodeCrypto = require("node:crypto");
27
27
  var frameworkError = require("./framework-error");
28
28
  var guard = require("./guard-all");
29
+ var intrinsic = require("./guard-intrinsic");
30
+ /** @internal `resolve`, `reject` and `allSettled` build their promise from the RECEIVER, so reading the
31
+ * global binding at the call hands the construction to whatever that binding holds. Two things here
32
+ * turn on that. Each component refusal is returned as a rejected promise, so a replacement whose
33
+ * `reject` resolves turns a `kem/bad-key` or `kem/decapsulation-failed` refusal into a pass. And the
34
+ * pair of component results is settled through `allSettled` before either shared secret is used or
35
+ * wiped, so a replacement reporting both fulfilled with values of its own both skips the wipe and
36
+ * hands the combiner material it chose, which is the hybrid's whole premise. Taken at load. */
37
+ var _Promise = intrinsic.Promise;
38
+ var _promiseResolve = intrinsic.uncurry(intrinsic.promiseResolve);
39
+ var _promiseReject = intrinsic.uncurry(intrinsic.promiseReject);
40
+ var _promiseAllSettled = intrinsic.uncurry(intrinsic.promiseAllSettled);
29
41
  var pkix = require("./schema-pkix");
30
42
  var schemaPkcs8 = require("./schema-pkcs8");
31
43
  var subtle = webcrypto.webcrypto.subtle;
@@ -125,16 +137,16 @@ function _tradEncaps(d, tradPK) {
125
137
  try {
126
138
  asn1.decode(tradPK);
127
139
  pub = nodeCrypto.createPublicKey({ key: tradPK, format: "der", type: "pkcs1" });
128
- } catch (e) { return Promise.reject(_err("kem/bad-key", "the RSA component public key is not a valid RSAPublicKey", e)); }
129
- if (pub.asymmetricKeyDetails.modulusLength !== trad.bits) return Promise.reject(_err("kem/bad-key", "the RSA component modulus is not " + trad.bits + " bits (draft sec. 6)"));
140
+ } catch (e) { return _promiseReject(_Promise, _err("kem/bad-key", "the RSA component public key is not a valid RSAPublicKey", e)); }
141
+ if (pub.asymmetricKeyDetails.modulusLength !== trad.bits) return _promiseReject(_Promise, _err("kem/bad-key", "the RSA component modulus is not " + trad.bits + " bits (draft sec. 6)"));
130
142
  var ss = Buffer.from(nodeCrypto.randomBytes(RSA_SS));
131
143
  var ct;
132
- try { ct = Buffer.from(nodeCrypto.publicEncrypt({ key: pub, padding: nodeCrypto.constants.RSA_PKCS1_OAEP_PADDING, oaepHash: "sha256" }, ss)); }
144
+ try { ct = Buffer.from(nodeCrypto.publicEncrypt({ key: pub, padding: guard.crypto.padding.RSA_PKCS1_OAEP, oaepHash: "sha256" }, ss)); }
133
145
  catch (e) {
134
146
  guard.secret.zeroize(ss, KemError, "kem/bad-input", "the RSA-OAEP component secret");
135
- return Promise.reject(_err("kem/bad-key", "the RSA-OAEP component encryption failed", e));
147
+ return _promiseReject(_Promise, _err("kem/bad-key", "the RSA-OAEP component encryption failed", e));
136
148
  }
137
- return Promise.resolve({ ss: ss, ct: ct });
149
+ return _promiseResolve(_Promise, { ss: ss, ct: ct });
138
150
  }
139
151
  var dh, dhCt;
140
152
  try {
@@ -142,8 +154,8 @@ function _tradEncaps(d, tradPK) {
142
154
  var eph = trad.kind === "ec" ? nodeCrypto.generateKeyPairSync("ec", { namedCurve: trad.node }) : nodeCrypto.generateKeyPairSync(trad.curve.toLowerCase());
143
155
  dh = Buffer.from(nodeCrypto.diffieHellman({ privateKey: eph.privateKey, publicKey: recip }));
144
156
  dhCt = _rawPubOf(eph.publicKey);
145
- } catch (e) { return Promise.reject(_err("kem/bad-key", "the " + trad.curve + " public key is invalid", e)); }
146
- return Promise.resolve({ ss: dh, ct: dhCt });
157
+ } catch (e) { return _promiseReject(_Promise, _err("kem/bad-key", "the " + trad.curve + " public key is invalid", e)); }
158
+ return _promiseResolve(_Promise, { ss: dh, ct: dhCt });
147
159
  }
148
160
 
149
161
  function _tradDecaps(d, tradSK, tradCT) {
@@ -153,16 +165,16 @@ function _tradDecaps(d, tradSK, tradCT) {
153
165
  try {
154
166
  asn1.decode(tradSK);
155
167
  priv = nodeCrypto.createPrivateKey({ key: tradSK, format: "der", type: "pkcs1" });
156
- } catch (e) { return Promise.reject(_err("kem/bad-key", "the RSA component private key is not a valid RSAPrivateKey", e)); }
157
- if (priv.asymmetricKeyDetails.modulusLength !== trad.bits) return Promise.reject(_err("kem/bad-key", "the RSA component modulus is not " + trad.bits + " bits (draft sec. 6)"));
168
+ } catch (e) { return _promiseReject(_Promise, _err("kem/bad-key", "the RSA component private key is not a valid RSAPrivateKey", e)); }
169
+ if (priv.asymmetricKeyDetails.modulusLength !== trad.bits) return _promiseReject(_Promise, _err("kem/bad-key", "the RSA component modulus is not " + trad.bits + " bits (draft sec. 6)"));
158
170
  var ss;
159
- try { ss = Buffer.from(nodeCrypto.privateDecrypt({ key: priv, padding: nodeCrypto.constants.RSA_PKCS1_OAEP_PADDING, oaepHash: "sha256" }, tradCT)); }
160
- catch (_e) { return Promise.reject(_err("kem/decapsulation-failed", "the RSA-OAEP component decryption failed")); }
171
+ try { ss = Buffer.from(nodeCrypto.privateDecrypt({ key: priv, padding: guard.crypto.padding.RSA_PKCS1_OAEP, oaepHash: "sha256" }, tradCT)); }
172
+ catch (_e) { return _promiseReject(_Promise, _err("kem/decapsulation-failed", "the RSA-OAEP component decryption failed")); }
161
173
  if (ss.length !== RSA_SS) {
162
174
  guard.secret.zeroize(ss, KemError, "kem/bad-input", "the RSA-OAEP component secret");
163
- return Promise.reject(_err("kem/decapsulation-failed", "the RSA-OAEP component decryption failed"));
175
+ return _promiseReject(_Promise, _err("kem/decapsulation-failed", "the RSA-OAEP component decryption failed"));
164
176
  }
165
- return Promise.resolve({ ss: ss, tradPK: Buffer.from(nodeCrypto.createPublicKey(priv).export({ format: "der", type: "pkcs1" })) });
177
+ return _promiseResolve(_Promise, { ss: ss, tradPK: Buffer.from(nodeCrypto.createPublicKey(priv).export({ format: "der", type: "pkcs1" })) });
166
178
  }
167
179
  var priv2, eph;
168
180
  try {
@@ -173,10 +185,10 @@ function _tradDecaps(d, tradSK, tradCT) {
173
185
  priv2 = _okpPrivFromRaw(d, tradSK);
174
186
  }
175
187
  eph = trad.kind === "ec" ? _ecPubFromPoint(d, tradCT) : _okpPubFromRaw(d, tradCT);
176
- } catch (e) { return Promise.reject(_err("kem/bad-key", "the " + trad.curve + " key material is invalid", e)); }
188
+ } catch (e) { return _promiseReject(_Promise, _err("kem/bad-key", "the " + trad.curve + " key material is invalid", e)); }
177
189
  var dh;
178
190
  try { dh = Buffer.from(nodeCrypto.diffieHellman({ privateKey: priv2, publicKey: eph })); }
179
- catch (e) { return Promise.reject(_err("kem/decapsulation-failed", "the " + trad.curve + " Diffie-Hellman failed", e)); }
191
+ catch (e) { return _promiseReject(_Promise, _err("kem/decapsulation-failed", "the " + trad.curve + " Diffie-Hellman failed", e)); }
180
192
  /** @internal The same canonical point the derivation states, read from the one function that spells
181
193
  * it. The combiner hashes these bytes, so a stored spelling here and a generated one there would
182
194
  * reach different secrets from the two sides of one key. */
@@ -188,9 +200,9 @@ function _tradDecaps(d, tradSK, tradCT) {
188
200
  * secret is wiped here: the caller's settle-or-wipe covers a value it was handed, and a rejected
189
201
  * promise hands it nothing. */
190
202
  guard.secret.zeroize(dh, KemError, "kem/bad-input", "the " + trad.curve + " component secret");
191
- return Promise.reject(e);
203
+ return _promiseReject(_Promise, e);
192
204
  }
193
- return Promise.resolve({ ss: dh, tradPK: tradPK });
205
+ return _promiseResolve(_Promise, { ss: dh, tradPK: tradPK });
194
206
  }
195
207
 
196
208
  function _combine(d, mlkemSS, tradSS, tradCT, tradPK) {
@@ -203,7 +215,7 @@ function _combine(d, mlkemSS, tradSS, tradCT, tradPK) {
203
215
  }
204
216
 
205
217
  async function _settleOrWipe(pA, pB, secA, secB) {
206
- var s = await Promise.allSettled([pA, pB]);
218
+ var s = await _promiseAllSettled(_Promise, [pA, pB]);
207
219
  if (s[0].status === "rejected" || s[1].status === "rejected") {
208
220
  if (s[0].status === "fulfilled") guard.secret.zeroize(secA(s[0].value), KemError, "kem/bad-input", "an ML-KEM shared secret");
209
221
  if (s[1].status === "fulfilled") guard.secret.zeroize(secB(s[1].value), KemError, "kem/bad-input", "a traditional shared secret");