@blamejs/pki 0.5.7 → 0.5.9

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 (105) hide show
  1. package/CHANGELOG.md +418 -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 +5 -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-der.js +152 -0
  36. package/lib/guard-encoding.js +15 -15
  37. package/lib/guard-header.js +3 -3
  38. package/lib/guard-identifier.js +16 -16
  39. package/lib/guard-json.js +15 -15
  40. package/lib/guard-limits.js +7 -7
  41. package/lib/guard-name.js +81 -16
  42. package/lib/guard-parsed.js +80 -80
  43. package/lib/guard-range.js +19 -19
  44. package/lib/guard-secret.js +11 -10
  45. package/lib/guard-text.js +6 -6
  46. package/lib/guard-time.js +10 -10
  47. package/lib/hpke.js +18 -17
  48. package/lib/http-digest.js +35 -35
  49. package/lib/http-retry-after.js +13 -13
  50. package/lib/http-transport.js +20 -19
  51. package/lib/inspect.js +53 -53
  52. package/lib/ip-utils.js +2 -2
  53. package/lib/jose.js +13 -13
  54. package/lib/key.js +16 -16
  55. package/lib/lint.js +51 -51
  56. package/lib/merkle.js +51 -36
  57. package/lib/mime.js +18 -18
  58. package/lib/ocsp-verify.js +10 -10
  59. package/lib/ocsp.js +13 -13
  60. package/lib/oid.js +29 -29
  61. package/lib/path-validate.js +114 -113
  62. package/lib/pbes2.js +16 -16
  63. package/lib/pkcs12-build.js +53 -53
  64. package/lib/pki-build.js +48 -20
  65. package/lib/rc2.js +1 -1
  66. package/lib/rfc3339.js +5 -5
  67. package/lib/schema-all.js +31 -31
  68. package/lib/schema-attrcert.js +12 -12
  69. package/lib/schema-c509.js +149 -197
  70. package/lib/schema-cmc.js +58 -58
  71. package/lib/schema-cmp.js +43 -43
  72. package/lib/schema-cms.js +45 -45
  73. package/lib/schema-crl.js +7 -7
  74. package/lib/schema-crmf.js +28 -28
  75. package/lib/schema-csr.js +12 -12
  76. package/lib/schema-csrattrs.js +16 -16
  77. package/lib/schema-engine.js +18 -18
  78. package/lib/schema-ocsp.js +15 -15
  79. package/lib/schema-pkcs12.js +20 -20
  80. package/lib/schema-pkcs8.js +2 -2
  81. package/lib/schema-pkix.js +131 -126
  82. package/lib/schema-smime.js +19 -19
  83. package/lib/schema-tsp.js +12 -12
  84. package/lib/schema-x509.js +3 -3
  85. package/lib/shbs.js +18 -18
  86. package/lib/sign-scheme.js +13 -13
  87. package/lib/sigstore.js +10 -11
  88. package/lib/sleep.js +1 -1
  89. package/lib/smime.js +308 -96
  90. package/lib/tls-cert-compress.js +18 -18
  91. package/lib/trust.js +27 -27
  92. package/lib/tsp-sign.js +17 -17
  93. package/lib/validator-all.js +1 -1
  94. package/lib/validator-attcert.js +1 -1
  95. package/lib/validator-cose.js +43 -44
  96. package/lib/validator-keydesc.js +3 -3
  97. package/lib/validator-sig.js +13 -13
  98. package/lib/validator-tls.js +11 -11
  99. package/lib/validator-tpm.js +20 -19
  100. package/lib/webauthn-mds.js +66 -66
  101. package/lib/webauthn.js +33 -33
  102. package/lib/webcrypto.js +15 -15
  103. package/lib/x509-sign.js +13 -13
  104. package/package.json +3 -2
  105. package/sbom.cdx.json +6 -6
@@ -13,7 +13,7 @@
13
13
  // material (RSA -> ktri OAEP; EC -> kari stdDH; X25519/X448 -> kari HKDF; ML-KEM -> ori/KEMRI;
14
14
  // password -> pwri; symmetric KEK -> kekri) through the OID-keyed registry, never a hardcoded
15
15
  // switch. AEAD (AES-GCM) content is the default and yields AuthEnvelopedData; CBC yields
16
- // EnvelopedData. PKCS#1 v1.5 is NEVER emitted.
16
+ // EnvelopedData. PKCS#1 v1.5 is never emitted.
17
17
 
18
18
  var nodeCrypto = require("crypto");
19
19
  var asn1 = require("./asn1-der");
@@ -61,13 +61,13 @@ function _wrapOidForKek(keyBytes) {
61
61
  if (keyBytes === 16) return "aes128-wrap";
62
62
  if (keyBytes === 24) return "aes192-wrap";
63
63
  if (keyBytes === 32) return "aes256-wrap";
64
- // Coverage residual: unreachable via the API -- a KEK/CEK is always an AES key size (16/24/32);
64
+ // Coverage residual: unreachable via the API, since a KEK/CEK is always an AES key size (16/24/32);
65
65
  // a defensive throw for a future caller that hands an off-size key.
66
66
  throw _err("cms/bad-input", "no AES key-wrap algorithm for a " + keyBytes + "-octet key-encryption key");
67
67
  }
68
68
 
69
- // GCMParameters ::= SEQUENCE { aes-nonce OCTET STRING, aes-ICVlen INTEGER DEFAULT 12 } -- the
70
- // DEFAULT 12 is OMITTED on emit (RFC 5084 sec. 3.2 / canonical DER).
69
+ // GCMParameters ::= SEQUENCE { aes-nonce OCTET STRING, aes-ICVlen INTEGER DEFAULT 12 }, where the
70
+ // DEFAULT 12 is omitted on emit (RFC 5084 sec. 3.2 / canonical DER).
71
71
  function _gcmParams(nonce, icvLen) {
72
72
  var kids = [b.octetString(nonce)];
73
73
  if (icvLen !== 12) kids.push(b.integer(BigInt(icvLen)));
@@ -75,7 +75,7 @@ function _gcmParams(nonce, icvLen) {
75
75
  }
76
76
 
77
77
  // The keyIdentifier option selects the RecipientIdentifier form. "issuerAndSerial" is the documented
78
- // default (its RFC name issuerAndSerialNumber is accepted too); reject anything else rather than
78
+ // default (its RFC name issuerAndSerialNumber is accepted too); reject anything else instead of
79
79
  // silently emitting issuerAndSerialNumber, so a typo surfaces instead of a rid the caller never asked for.
80
80
  function _assertKeyIdentifier(form) {
81
81
  if (form != null && form !== "issuerAndSerial" && form !== "issuerAndSerialNumber" && form !== "subjectKeyIdentifier") {
@@ -97,7 +97,7 @@ function _rid(cert, form) {
97
97
  // unsupported-curve / low-order / unsupported-KEM-cert throws further below): these validate the
98
98
  // CALLER's own recipient certificate at config time (tier-1 THROW). They fire only when a caller
99
99
  // supplies a certificate whose SKI/keyUsage extension is malformed, or whose key is an unsupported
100
- // curve / a low-order Montgomery point / an unsupported KEM -- inputs the toolkit never produces.
100
+ // curve, a low-order Montgomery point, or an unsupported KEM: inputs the toolkit never produces.
101
101
  function _skiOf(cert) {
102
102
  var exts = cert.extensions || [];
103
103
  for (var i = 0; i < exts.length; i++) if (exts[i].name === "subjectKeyIdentifier" && exts[i].value != null) {
@@ -120,7 +120,7 @@ var OAEP_HASH = { sha256: "SHA-256", sha384: "SHA-384", sha512: "SHA-512" };
120
120
  function _oaepParams(hashName) {
121
121
  var hAlg = _algId(hashName, "null");
122
122
  var mgf = b.sequence([b.oid(O("mgf1")), hAlg]);
123
- // pSourceAlgorithm [2] DEFAULT pSpecifiedEmpty -- the empty-label default MUST be omitted (X.690).
123
+ // pSourceAlgorithm [2] DEFAULT pSpecifiedEmpty: the empty-label default MUST be omitted (X.690).
124
124
  return b.sequence([b.explicit(0, hAlg), b.explicit(1, mgf)]);
125
125
  }
126
126
  async function _buildKtri(cek, cert, opts) {
@@ -144,8 +144,8 @@ MONT_KA[O("X25519")] = { name: "X25519", hkdf: "SHA-256", scheme: "dhSinglePass-
144
144
  MONT_KA[O("X448")] = { name: "X448", hkdf: "SHA-512", scheme: "dhSinglePass-stdDH-hkdf-sha512-scheme" };
145
145
 
146
146
  // ECC-CMS-SharedInfo ::= SEQUENCE { keyInfo AlgorithmIdentifier (the wrap, params ABSENT),
147
- // entityUInfo [0] EXPLICIT OCTET STRING OPTIONAL, suppPubInfo [2] EXPLICIT OCTET STRING } --
148
- // suppPubInfo = the KEK length in BITS, 4-octet big-endian (RFC 5753 sec. 7.2). ONE builder,
147
+ // entityUInfo [0] EXPLICIT OCTET STRING OPTIONAL, suppPubInfo [2] EXPLICIT OCTET STRING }, where
148
+ // suppPubInfo is the KEK length in bits, 4-octet big-endian (RFC 5753 sec. 7.2). One builder,
149
149
  // shared by encrypt + decrypt so the two sides cannot diverge.
150
150
  function _eccSharedInfo(wrapName, ukm, kekBytes) {
151
151
  var kids = [_algId(wrapName, "absent")];
@@ -184,12 +184,12 @@ async function _buildKari(cek, cert, opts) {
184
184
  mz = Buffer.from(await subtle.deriveBits({ name: mka.name, public: rPub }, meph.privateKey, null));
185
185
  if (mz.every(function (x) { return x === 0; })) throw _err("cms/bad-input", "the X25519/X448 shared secret is all-zero (low-order point)");
186
186
  var mzKey = await subtle.importKey("raw", mz, { name: "HKDF" }, false, ["deriveBits"]);
187
- // RFC 8418 sec. 2.2: when a ukm is present it is used BOTH as the HKDF salt AND as the
188
- // ECC-CMS-SharedInfo entityUInfo (the HKDF info) -- omitting it from the info diverges the KEK
187
+ // RFC 8418 sec. 2.2: when a ukm is present it is used as both the HKDF salt and the
188
+ // ECC-CMS-SharedInfo entityUInfo (the HKDF info). Omitting it from the info diverges the KEK
189
189
  // from any conformant peer that reads the transmitted ukm.
190
190
  kek = Buffer.from(await subtle.deriveBits({ name: "HKDF", hash: mka.hkdf, salt: ukm || Buffer.alloc(0), info: _eccSharedInfo(wrapName, ukm, cek.length) }, mzKey, cek.length * 8));
191
191
  } else {
192
- // Coverage residual: unreachable -- _buildRecipient routes only ecPublicKey / X25519 / X448
192
+ // Coverage residual: unreachable, since _buildRecipient routes only ecPublicKey / X25519 / X448
193
193
  // keys into _buildKari; a defensive throw against a future dispatch change.
194
194
  throw _err("cms/unsupported-algorithm", "unsupported recipient key algorithm for kari");
195
195
  }
@@ -199,7 +199,7 @@ async function _buildKari(cek, cert, opts) {
199
199
  var origPubBits = origSpki.children[1]; // BIT STRING node
200
200
  var originatorKey = b.contextConstructed(1, Buffer.concat([origSpki.children[0].bytes, origPubBits.bytes]));
201
201
  // KeyAgreeRecipientIdentifier CHOICE { issuerAndSerialNumber, rKeyId [0] IMPLICIT
202
- // RecipientKeyIdentifier } -- the SKI form here wraps a SEQUENCE (rKeyId), unlike ktri's bare
202
+ // RecipientKeyIdentifier }, where the SKI form wraps a SEQUENCE (rKeyId), unlike ktri's bare
203
203
  // subjectKeyIdentifier [0] IMPLICIT OCTET STRING.
204
204
  var ridNode;
205
205
  _assertKeyIdentifier(opts.keyIdentifier);
@@ -234,13 +234,13 @@ async function _buildKekri(cek, desc) {
234
234
  return { tag: 2, node: b.sequence([b.integer(4n), kekid, _algId(wrapName, "absent"), b.octetString(encryptedKey)]) };
235
235
  }
236
236
 
237
- // A PBKDF2 iterationCount MUST be a positive integer within the same cap the decryptor enforces -- a
237
+ // A PBKDF2 iterationCount MUST be a positive integer within the same cap the decryptor enforces, so a
238
238
  // ---- pwri (password) : PBKDF2 + RFC 3211 double-CBC PWRI-KEK ---------------
239
239
  async function _buildPwri(cek, desc) {
240
- // A string / Uint8Array password is encoded into a buffer THIS toolkit allocated -- a credential
241
- // copy -- so it is cleared once the derivation has consumed it. A caller-supplied Buffer is
240
+ // A string / Uint8Array password is encoded into a buffer this toolkit allocated, a credential
241
+ // copy, so it is cleared once the derivation has consumed it. A caller-supplied Buffer is
242
242
  // borrowed and left intact.
243
- // The option validation runs BEFORE the password is encoded: a rejected iteration count or salt
243
+ // The option validation runs before the password is encoded: a rejected iteration count or salt
244
244
  // would otherwise abandon an owned credential copy on the way out.
245
245
  var iterations = pbes2.assertIterations(desc.iterations == null ? 600000 : desc.iterations, _err, "cms");
246
246
  var salt = desc.salt ? pbes2.assertSalt(guard.bytes.view(desc.salt, CmsError, "cms/bad-input", "salt"), _err, "cms") : nodeCrypto.randomBytes(16);
@@ -250,9 +250,9 @@ async function _buildPwri(cek, desc) {
250
250
  var pwOwn = pbes2.passwordBytesOwned(desc.password, _err, "cms");
251
251
  var password = pwOwn.bytes;
252
252
  var kekKey = await subtle.importKey("raw", password, { name: "PBKDF2" }, false, ["deriveBits"]);
253
- // The derived KEK is this function's allocation and is cleared below. `password` is NOT: when the
253
+ // The derived KEK is this function's allocation and is cleared below. `password` is not: when the
254
254
  // caller passes a Buffer, passwordBytes hands back that very buffer, so wiping it would destroy
255
- // the caller's own memory -- the one failure this rule must never cause.
255
+ // the caller's own memory, the one failure this rule must never cause.
256
256
  var kek = Buffer.from(await subtle.deriveBits({ name: "PBKDF2", hash: _prfHash(prf), salt: salt, iterations: iterations }, kekKey, innerKeyBytes * 8));
257
257
  if (pwOwn.owned) guard.secret.zeroize(password, CmsError, "cms/bad-input", "the password encoding");
258
258
  try {
@@ -278,7 +278,7 @@ KEM_WRAP[O("id-ml-kem-1024")] = "aes256-wrap";
278
278
  var KEM_WC = {}; KEM_WC[O("id-ml-kem-512")] = "ML-KEM-512"; KEM_WC[O("id-ml-kem-768")] = "ML-KEM-768"; KEM_WC[O("id-ml-kem-1024")] = "ML-KEM-1024";
279
279
 
280
280
  // CMSORIforKEMOtherInfo ::= SEQUENCE { wrap AlgorithmIdentifier, kekLength INTEGER,
281
- // ukm [0] EXPLICIT OCTET STRING OPTIONAL } -- the RFC 9629 sec. 5 KDF info, one builder both sides.
281
+ // ukm [0] EXPLICIT OCTET STRING OPTIONAL }, the RFC 9629 sec. 5 KDF info, one builder both sides.
282
282
  function _kemOtherInfo(wrapName, kekBytes, ukm) {
283
283
  var kids = [_algId(wrapName, "absent"), b.integer(BigInt(kekBytes))];
284
284
  if (ukm) kids.push(b.explicit(0, b.octetString(ukm)));
@@ -309,7 +309,7 @@ async function _buildKemri(cek, cert, opts) {
309
309
  return { tag: 4, node: b.sequence([b.oid(O("kem")), kemri]) };
310
310
  } finally {
311
311
  // RFC 9629 sec. 7 asks the SENDER to discard the shared secret and KEK once the recipient entry
312
- // is built -- and to use a fresh secret per recipient, so this runs per call rather than once at
312
+ // is built, and to use a fresh secret per recipient, so this runs per call and not once at
313
313
  // the end of a multi-recipient message. In a `finally`, so a wrap or encoding failure does not
314
314
  // leave them behind. The CEK is the caller's and is wiped by no one here; kemct is public.
315
315
  // kem.sharedKey is the ArrayBuffer the engine returned and ss is this function's copy of it;
@@ -340,7 +340,7 @@ function _pwriFormat(cek) {
340
340
  var padLen = body.length % blk === 0 ? 0 : blk - (body.length % blk);
341
341
  if (body.length + padLen < 2 * blk) padLen += (2 * blk - (body.length + padLen));
342
342
  // and each hold a plaintext copy of the CEK; only the padded result is returned, so
343
- // the intermediates are cleared here rather than abandoned.
343
+ // the intermediates are cleared here and never abandoned.
344
344
  var wrapped = Buffer.concat([body, nodeCrypto.randomBytes(padLen)]);
345
345
  guard.secret.zeroizeAll([check, body], CmsError, "cms/bad-input", "a PWRI formatting intermediate");
346
346
  return wrapped;
@@ -381,7 +381,7 @@ async function _buildRecipient(cek, desc, opts) {
381
381
  function mergeOpts(opts, desc) {
382
382
  return { oaepHash: desc.oaepHash || opts.oaepHash, keyIdentifier: desc.keyIdentifier || opts.keyIdentifier, ukm: desc.ukm != null ? desc.ukm : opts.ukm };
383
383
  }
384
- // RecipientInfo CHOICE: ktri untagged; kari [1], kekri [2], pwri [3], ori [4] -- all IMPLICIT,
384
+ // RecipientInfo CHOICE: ktri untagged; kari [1], kekri [2], pwri [3], ori [4], all IMPLICIT,
385
385
  // so the arm's SEQUENCE tag is replaced by the context tag.
386
386
  function _taggedRecipient(r) {
387
387
  if (r.tag == null) return r.node;
@@ -406,8 +406,8 @@ async function encrypt(content, recipients, opts) {
406
406
  var contentType = opts.contentType || "data";
407
407
  var cek = nodeCrypto.randomBytes(ca.keyBits / 8);
408
408
 
409
- // The CEK protects the content for EVERY recipient, so unlike a per-recipient shared secret it is
410
- // cleared ONCE, after the last recipient entry is built and the content is encrypted -- wiping it
409
+ // The CEK protects the content for every recipient, so unlike a per-recipient shared secret it is
410
+ // cleared once, after the last recipient entry is built and the content is encrypted. Wiping it
411
411
  // inside the recipient loop would destroy the key the remaining recipients must be given.
412
412
  try {
413
413
  // EncryptedData: a single non-array { cek } or { password } descriptor, no RecipientInfos.
@@ -450,9 +450,9 @@ function _authEnvelopedData(contentBytes, cek, ca, contentType, opts, riNodes, r
450
450
  var setOf = b.setOf(opts.authAttrs);
451
451
  aad = setOf; authAttrsDer = b.contextConstructed(1, setOf.subarray(_tlvHeaderLen(setOf)));
452
452
  }
453
- // A 16-octet (128-bit) GCM tag -- the strongest ICV and what OpenSSL emits, so the message
453
+ // A 16-octet (128-bit) GCM tag, the strongest ICV and what OpenSSL emits, so the message
454
454
  // interops across OpenSSL 3.5 / 4.x. The aes-ICVlen (16) is carried explicitly (RFC 5084 sec. 3.2
455
- // omits it ONLY when it equals the DEFAULT 12); it MUST equal the mac octet length (M42).
455
+ // omits it only when it equals the DEFAULT 12); it MUST equal the mac octet length (M42).
456
456
  var g = _gcmEncrypt(cek, nonce, contentBytes, aad, ca.keyBits, 16);
457
457
  var eci = b.sequence([b.oid(O(contentType)), b.sequence([b.oid(O(ca.oid)), _gcmParams(nonce, 16)]), b.contextPrimitive(0, g.ct)]);
458
458
  var kids = [b.integer(0n), b.setOf(riNodes), eci];
@@ -482,8 +482,8 @@ function _encryptedData(contentBytes, desc, ca, contentType, opts, cek) {
482
482
  }
483
483
 
484
484
  function _encryptedDataPbes2(contentBytes, desc, ca, contentType, iv, opts) {
485
- // A string / Uint8Array password is encoded into a buffer THIS toolkit allocated -- a credential
486
- // copy -- so it is cleared once the derivation has consumed it. A caller-supplied Buffer is
485
+ // A string / Uint8Array password is encoded into a buffer this toolkit allocated, a credential
486
+ // copy, so it is cleared once the derivation has consumed it. A caller-supplied Buffer is
487
487
  // borrowed and left intact.
488
488
  var pwOwn2 = pbes2.passwordBytesOwned(desc.password, _err, "cms");
489
489
  var password = pwOwn2.bytes;
@@ -554,7 +554,7 @@ async function authenticate(content, recipients, opts) {
554
554
  if (contentType !== "data" && !withAttrs) throw _err("cms/bad-input", "AuthenticatedData with a non-data contentType requires authenticated attributes (RFC 5652 sec. 9.1)");
555
555
 
556
556
  // The MAC key is this path's content-encryption key: every recipient is given it, so it is cleared
557
- // ONCE at the end rather than per recipient, and only after the MAC has been computed over it.
557
+ // once at the end and not per recipient, and only after the MAC has been computed over it.
558
558
  var macKey = nodeCrypto.randomBytes(MAC_KEY_OCTETS);
559
559
  try {
560
560
  var recips = [];
@@ -574,8 +574,8 @@ async function authenticate(content, recipients, opts) {
574
574
  ];
575
575
  if (opts.authAttrs && opts.authAttrs.length) pairs = pairs.concat(opts.authAttrs);
576
576
  // Every authAttr (auto-built or caller-supplied) MUST be a well-formed Attribute SEQUENCE
577
- // { type OBJECT IDENTIFIER, values SET OF } and each type appears at most once (RFC 5652) -- so a
578
- // malformed or duplicate caller attribute is rejected BEFORE it is MACed and emitted, never left to
577
+ // { type OBJECT IDENTIFIER, values SET OF } and each type appears at most once (RFC 5652), so a
578
+ // malformed or duplicate caller attribute is rejected before it is MACed and emitted, never left to
579
579
  // fail an operator's parser downstream.
580
580
  var seenTypes = {};
581
581
  pairs.forEach(function (p) {
package/lib/cms-sign.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // Copyright (c) blamejs contributors
3
3
  "use strict";
4
4
  //
5
- // @internal -- the pki.cms.sign implementation. So the pki.cms namespace has ONE @module home,
5
+ // @internal -- the pki.cms.sign implementation. So the pki.cms namespace has one @module home,
6
6
  // the operator-facing @module pki.cms + the @primitive pki.cms.sign documentation block live in
7
7
  // cms-verify.js, which re-exports this sign function.
8
8
  //
@@ -33,10 +33,27 @@ function _err(code, message, cause) { return new CmsError(code, message, cause);
33
33
  // cms/<kind>), so its faults keep the cms/* codes.
34
34
  function _signE(kind, message, cause) { return new CmsError("cms/" + kind, message, cause); }
35
35
  function O(name) { return oid.byName(name); }
36
+
37
+ // Every option each verb READS, and nothing else. Derived from the signing paths themselves rather
38
+ // than from what callers happen to pass: an option this list omits is one the verb would ignore in
39
+ // silence, and the ones that matter most are the ones a caller would most expect to work.
40
+ // A misspelled option name is the shape this closes: it does not select the mode the caller meant,
41
+ // and without the gate the message is signed the other way with nothing said about it.
42
+ var KNOWN_SIGN_OPTS = {
43
+ signedAttributes: 1, signingTime: 1, additionalSignedAttributes: 1, unsignedAttributes: 1,
44
+ sid: 1, eContentType: 1, detached: 1, certificates: 1, pem: 1,
45
+ };
46
+ // Countersign has its own set: it takes signerIndex and countersignatureOf to SELECT which
47
+ // signature it attaches to, and has no content of its own, so eContentType, detached and
48
+ // unsignedAttributes are not among the options it reads.
49
+ var KNOWN_COUNTERSIGN_OPTS = {
50
+ signerIndex: 1, countersignatureOf: 1, signingTime: 1, certificates: 1, pem: 1,
51
+ signedAttributes: 1, additionalSignedAttributes: 1, sid: 1,
52
+ };
36
53
  // The shared producing-side helpers, bound to this domain's error class. Only the
37
- // post-sign self-check is used here -- the same one every other signer in the
38
- // toolkit runs -- so a key-only signer's declared public key is bound by evidence
39
- // rather than by the caller's word.
54
+ // post-sign self-check is used here (the same one every other signer in the
55
+ // toolkit runs), so a key-only signer's declared public key is bound by evidence
56
+ // and never by the caller's word.
40
57
  var NS = pkix.makeNS("cms", CmsError, oid);
41
58
  var _b = pkiBuild.makeBuilder({
42
59
  ErrorClass: CmsError, prefix: "cms", O: O, NS: NS,
@@ -85,10 +102,10 @@ function _buildSid(cert, useSki) {
85
102
  // Assemble a SignedAttributes SET from resolved attribute pairs (each { type: <dotted OID>, values:
86
103
  // [<build node or DER Buffer>] }), returning { setOf, wire }: `setOf` is the canonical DER SET OF
87
104
  // (tag 0x31, build.set SET-OF-sorts) the signature covers (RFC 5652 sec. 5.4); `wire` is the same
88
- // bytes with the on-wire [0] IMPLICIT tag (0xA0). Each attribute type appears AT MOST ONCE (sec.
89
- // 5.3) -- a duplicate throws. Shared by the top-level signer (content-type + message-digest +
105
+ // bytes with the on-wire [0] IMPLICIT tag (0xA0). Each attribute type appears at most once (sec.
106
+ // 5.3), so a duplicate throws. Shared by the top-level signer (content-type + message-digest +
90
107
  // signing-time + caller attrs) and the countersignature builder (message-digest over the target
91
- // signature octets + signing-time, content-type OMITTED per sec. 11.4).
108
+ // signature octets + signing-time, content-type omitted per sec. 11.4).
92
109
  function _buildSignedAttrs(pairs) {
93
110
  var seenTypes = {};
94
111
  var attrs = pairs.map(function (p) {
@@ -112,16 +129,16 @@ function _resolveAttrPairs(list, what) {
112
129
  });
113
130
  }
114
131
 
115
- // content-type / message-digest / signing-time are content-binding SIGNED attributes and MUST NOT
116
- // appear as unsigned attributes (RFC 5652 sec. 11.1/11.2/11.3 -- the parser's ATTR_FORBIDDEN_IN).
132
+ // content-type / message-digest / signing-time are content-binding signed attributes and MUST NOT
133
+ // appear as unsigned attributes (RFC 5652 sec. 11.1/11.2/11.3, the parser's ATTR_FORBIDDEN_IN).
117
134
  var UNSIGNED_FORBIDDEN = {};
118
135
  UNSIGNED_FORBIDDEN[O("contentType")] = "content-type";
119
136
  UNSIGNED_FORBIDDEN[O("messageDigest")] = "message-digest";
120
137
  UNSIGNED_FORBIDDEN[O("signingTime")] = "signing-time";
121
138
 
122
139
  // Build the [1] IMPLICIT unsignedAttrs SET OF (on-wire tag 0xA1) from opts.unsignedAttributes
123
- // (each { type, values: [DER] } -- a countersignature, an RFC 3161 timestamp token), or null when
124
- // absent/empty. Unsigned attributes are OUTSIDE the signature; a placement-forbidden type or a
140
+ // (each { type, values: [DER] }, such as a countersignature or an RFC 3161 timestamp token), or null when
141
+ // absent/empty. Unsigned attributes sit outside the signature; a placement-forbidden type or a
125
142
  // duplicate type (RFC 5652 sec. 11 / sec. 5.3) is a config-time cms/bad-input.
126
143
  function _buildUnsignedAttrs(list) {
127
144
  if (list == null) return null;
@@ -144,9 +161,9 @@ function _buildUnsignedAttrs(list) {
144
161
  // SignedData digestAlgorithms + certificates sets).
145
162
  function _buildSignerInfo(signer, content, eContentType, opts) {
146
163
  var so = signer || {};
147
- // A KEY-ONLY signer: `{ key, spki, keyIdentifier }` with no certificate. RFC
164
+ // A key-only signer: `{ key, spki, keyIdentifier }` with no certificate. RFC
148
165
  // 5272 sec. 3.2 requires exactly this when a Full PKI Request is signed with the
149
- // key of a certification request it carries -- there is no certificate yet, the
166
+ // key of a certification request it carries: there is no certificate yet, the
150
167
  // sid MUST be the subjectKeyIdentifier form, and its value MUST be the SKI the
151
168
  // request itself declares. The scheme resolver only ever reads
152
169
  // `cert.subjectPublicKeyInfo.algorithm`, so the request's own parsed SPKI stands
@@ -217,9 +234,9 @@ function _buildSignerInfo(signer, content, eContentType, opts) {
217
234
  // comparing it works only for key material this process can export, and the keys
218
235
  // this signer serves are often exactly the ones that cannot be: a non-extractable
219
236
  // CryptoKey, an HSM handle, or a composite `{ mldsa, trad }` pair that is two keys
220
- // rather than one. Every one of those can still be checked the direct way -- the
237
+ // and not one. Every one of those can still be checked the direct way: the
221
238
  // signature it just produced either verifies under the declared public key or it
222
- // does not -- so there is no key kind this has to take on trust.
239
+ // does not, so there is no key kind this has to take on trust.
223
240
  //
224
241
  // This is the same post-sign self-check every other signer in the toolkit runs,
225
242
  // through the same shared helper. It costs one verification per SignerInfo, which
@@ -227,8 +244,8 @@ function _buildSignerInfo(signer, content, eContentType, opts) {
227
244
  function _assertKeyMatchesSpki(keyOnly, soKey, soSpki, scheme, sig, signedBytes, cert) {
228
245
  var declared = keyOnly ? soSpki : (cert && cert.subjectPublicKeyInfo && cert.subjectPublicKeyInfo.bytes);
229
246
  // A parsed certificate that does not retain its SPKI bytes cannot be checked this
230
- // way. That is not a shape x509.parse produces -- and if it ever were, refusing is
231
- // the answer rather than skipping, since a skip is the fail-open this prevents.
247
+ // way. That is not a shape x509.parse produces, and if it ever were, refusing is
248
+ // the answer, never skipping, since a skip is the fail-open this prevents.
232
249
  if (!declared) {
233
250
  throw _signE("bad-input",
234
251
  "a signer certificate did not surface its subjectPublicKeyInfo, so the signature it produced could " +
@@ -253,21 +270,21 @@ function _timeValue(when) {
253
270
 
254
271
  // Normalize a signer certificate input to its raw DER (DER Buffer / PEM string / Uint8Array).
255
272
  // The same bytes drive scheme resolution and the certificates [0] embedding, so a parsed
256
- // certificate (which does not retain its full DER) is rejected -- pass DER or PEM.
273
+ // certificate (which does not retain its full DER) is rejected; pass DER or PEM.
257
274
  // The stand-in a key-only signer resolves its signature scheme from: the parsed
258
275
  // SubjectPublicKeyInfo of the key that will sign. Only `.subjectPublicKeyInfo` is
259
- // read downstream, so this deliberately carries nothing else -- a fuller fake
276
+ // read downstream, so this deliberately carries nothing else; a fuller fake
260
277
  // would invite code to start trusting fields no certificate actually backs.
261
278
  function _keyOnlyCertStandIn(spkiDer) {
262
279
  var alg;
263
280
  try {
264
281
  // SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING }
265
- // The WHOLE structure, not just the field this function goes on to read. For an
266
- // OPAQUE key handle the derivation check downstream is deliberately skipped, so
267
- // this is the ONLY thing standing between a caller's bytes and a SignerInfo that
282
+ // The whole structure, not just the field this function goes on to read. For an
283
+ // opaque key handle the derivation check downstream is deliberately skipped, so
284
+ // this is the only thing standing between a caller's bytes and a SignerInfo that
268
285
  // declares them: a SEQUENCE carrying an algorithm and nothing else, or a third
269
286
  // field, would be emitted as the signer's public key and resolve to nothing for
270
- // anyone trying to verify. The key VALUE is not interpreted -- only that there
287
+ // anyone trying to verify. The key value is not interpreted, only that there
271
288
  // is one, in the field the structure reserves for it.
272
289
  var node = asn1.decode(spkiDer);
273
290
  if (node.tagClass !== "universal" || node.tagNumber !== asn1.TAGS.SEQUENCE || !node.children ||
@@ -288,7 +305,7 @@ function _keyOnlyCertStandIn(spkiDer) {
288
305
  "a key-only signer's spki algorithm is SEQUENCE { algorithm OID, parameters OPTIONAL } (RFC 5280 sec. 4.1.1.2)");
289
306
  }
290
307
  // The same shape schema-x509 surfaces: the algorithm OID plus its parameters
291
- // as the RAW TLV (or null when absent) -- the resolver re-decodes those bytes
308
+ // as the raw TLV (or null when absent); the resolver re-decodes those bytes
292
309
  // for an EC named curve and for the RSASSA-PSS hash pinning, so handing it a
293
310
  // different representation here would break exactly those two algorithms.
294
311
  alg = {
@@ -312,9 +329,9 @@ function _keyOnlyKeyId(so) {
312
329
  throw _err("cms/bad-input",
313
330
  "a key-only signer requires keyIdentifier -- the subjectKeyIdentifier the certification request declares (RFC 5272 sec. 3.2)");
314
331
  }
315
- // A key identifier is BYTES, so the type is checked rather than coerced. Buffer.from
316
- // ACCEPTS far more than it should mean here: Buffer.from(20) allocates twenty zero
317
- // octets and Buffer.from("a1b2") takes the ASCII of the text rather than the two
332
+ // A key identifier is bytes, so the type is checked and never coerced. Buffer.from
333
+ // accepts far more than it should mean here: Buffer.from(20) allocates twenty zero
334
+ // octets and Buffer.from("a1b2") takes the ASCII of the text, not the two
318
335
  // octets a reader means by it. Either would emit a structurally valid
319
336
  // SignerIdentifier carrying an identifier the caller never asked for, which no
320
337
  // verifier can match back to the certification request (RFC 5272 sec. 3.2).
@@ -342,7 +359,7 @@ function _pemToDer(text) {
342
359
  // Documented `-> Promise`, so a fault leaves as a REJECTION (guard-async); the checks stay
343
360
  // synchronous because they read the caller's mutable content and signer list.
344
361
  function sign(content, signers, opts) {
345
- // Every caller-owned argument copied at entry and released when the call settles -- see the note
362
+ // Every caller-owned argument copied at entry and released when the call settles; see the note
346
363
  // on the same call in x509-sign. Here it is what makes the attribute-shaped-content refusal below
347
364
  // hold: the value that decides it and the value that gets signed are now the same read.
348
365
  return guard.bytes.fixedCall(CmsError, "cms/bad-input", [
@@ -353,10 +370,11 @@ function sign(content, signers, opts) {
353
370
  function _sign(content, signers, opts) {
354
371
  opts = opts || {};
355
372
  if (typeof opts !== "object" || Buffer.isBuffer(opts)) throw _err("cms/bad-input", "pki.cms.sign options must be an object");
373
+ guard.identifier.assertKnownKeys(opts, KNOWN_SIGN_OPTS, _err, "cms/bad-input", "unknown opts field ");
356
374
  // The arguments were copied at entry (see `sign` above), which is what makes the refusal below
357
375
  // hold: flipping signedAttributes from true to false after the call returns would otherwise skip
358
- // the attribute-shaped-content check while the signer signs that content directly -- the very
359
- // signature the stripping attack needs.
376
+ // the attribute-shaped-content check while the signer signs that content directly, which is the
377
+ // very signature the stripping attack needs.
360
378
  var contentBuf = _toBuf(content, "content");
361
379
  var list = Array.isArray(signers) ? signers : [signers];
362
380
  if (!list.length) throw _err("cms/bad-input", "pki.cms.sign requires at least one signer");
@@ -374,7 +392,7 @@ function _sign(content, signers, opts) {
374
392
  "a key-only signer must be the ONLY SignerInfo in a Full PKI Request (RFC 5272 sec. 3.2)");
375
393
  }
376
394
  // RFC 5652 sec. 5.3: signed attributes MUST be present (carrying a content-type attribute)
377
- // whenever the encapsulated content type is not id-data -- so signedAttributes:false is only
395
+ // whenever the encapsulated content type is not id-data, so signedAttributes:false is only
378
396
  // valid for id-data content. Refusing it here keeps cms.sign from emitting a non-conformant
379
397
  // SignedData (e.g. a timestamp token, id-ct-TSTInfo, with no signed attributes).
380
398
  if (opts.signedAttributes === false && eContentType !== OID_DATA) {
@@ -382,9 +400,9 @@ function _sign(content, signers, opts) {
382
400
  }
383
401
  // The signer's half of the signed-attribute stripping problem
384
402
  // (draft-vangeest-lamps-cms-euf-cma-signeddata, Attack Type 2). Signing attribute-shaped content
385
- // WITHOUT attributes produces a signature that can afterwards be promoted into an
403
+ // without attributes produces a signature that can afterwards be promoted into an
386
404
  // attributes-present message, because the signature does not commit to which mode was used: the
387
- // attacker attaches the signed bytes AS the SignedAttributes and swaps in whatever content their
405
+ // attacker attaches the signed bytes as the SignedAttributes and swaps in whatever content their
388
406
  // message-digest attribute names. Refusing to mint the ambiguous signature is the only point at
389
407
  // which this direction can be stopped -- by the time it is a message, the damage is done.
390
408
  if (opts.signedAttributes === false && cms.looksLikeSignedAttributes(contentBuf)) {
@@ -392,7 +410,7 @@ function _sign(content, signers, opts) {
392
410
  "it WITHOUT signed attributes would produce a signature that could be re-presented as one over " +
393
411
  "attributes (RFC 5652 sec. 5.4); sign it with signed attributes instead");
394
412
  }
395
- // A supplied signing-time MUST be a valid Date (or false to omit the attribute) -- never a
413
+ // A supplied signing-time MUST be a valid Date (or false to omit the attribute), never a
396
414
  // silently-ignored non-Date or an Invalid Date that would encode a garbage Time.
397
415
  if (opts.signingTime != null && opts.signingTime !== false) guard.time.assertValid(opts.signingTime, _err, "cms/bad-input", "signingTime");
398
416
 
@@ -426,7 +444,7 @@ function _sign(content, signers, opts) {
426
444
  });
427
445
  }
428
446
 
429
- // Dedupe certificate DERs (two signers may share a cert -- embed it once).
447
+ // Dedupe certificate DERs (two signers may share a cert, so embed it once).
430
448
  function _dedupe(ders) {
431
449
  var seen = {}, out = [];
432
450
  ders.forEach(function (d) { var k = d.toString("hex"); if (!seen[k]) { seen[k] = 1; out.push(d); } });
@@ -439,11 +457,11 @@ function _toBuf(v, what) {
439
457
  }
440
458
 
441
459
  // ---- pki.cms.countersign (RFC 5652 sec. 11.4) ------------------------------
442
- // A countersignature is a SignerInfo (Countersignature ::= SignerInfo) over the CONTENTS of the
443
- // countersigned SignerInfo's signature OCTET STRING -- NOT the eContent -- attached as the
460
+ // A countersignature is a SignerInfo (Countersignature ::= SignerInfo) over the contents of the
461
+ // countersigned SignerInfo's signature OCTET STRING, never the eContent, attached as the
444
462
  // id-countersignature unsigned attribute. It reuses the whole build+sign flow (resolveSignScheme /
445
463
  // _buildSid / _buildSignedAttrs / signOverTbs); only the preimage (the target signature octets) and
446
- // the content-type OMISSION are the deltas, and the orchestrator splices the [1] unsignedAttrs into
464
+ // the omitted content-type are the deltas, and the orchestrator splices the [1] unsignedAttrs into
447
465
  // an existing SignedData while preserving the targeted SignerInfo's signed bytes BYTE-FOR-BYTE.
448
466
 
449
467
  // Resolve opts.signerIndex (a number, an array of numbers, or "all"; default 0) to primary indices.
@@ -457,8 +475,8 @@ function _resolveSignerIndices(spec, n) {
457
475
  }
458
476
 
459
477
  // Build one countersignature value (RFC 5652 sec. 11.4) over `targetSigOctets`: message-digest bound
460
- // to digest(targetSigOctets) under the countersignature's own digestAlgorithm, content-type OMITTED,
461
- // signed through the SAME per-algorithm scheme machinery as a top-level signer.
478
+ // to digest(targetSigOctets) under the countersignature's own digestAlgorithm, content-type omitted,
479
+ // signed through the same per-algorithm scheme machinery as a top-level signer.
462
480
  function _buildCountersignature(targetSigOctets, countersigner, opts) {
463
481
  var so = countersigner || {};
464
482
  var certDer = _normCertDer(so.cert);
@@ -485,9 +503,9 @@ function _buildCountersignature(targetSigOctets, countersigner, opts) {
485
503
  });
486
504
  }
487
505
 
488
- // Build the [1] IMPLICIT unsignedAttrs bytes for a SignerInfo, merging `newCsValues` into the ONE
489
- // id-countersignature attribute (RFC 5652 sec. 11: single instance per type, MULTIPLE values), and
490
- // keeping every OTHER unsigned attribute and every EXISTING countersignature value verbatim.
506
+ // Build the [1] IMPLICIT unsignedAttrs bytes for a SignerInfo, merging `newCsValues` into the single
507
+ // id-countersignature attribute (RFC 5652 sec. 11: one instance per type, multiple values), and
508
+ // keeping every other unsigned attribute and every existing countersignature value verbatim.
491
509
  function _mergeCountersig(uaNode, newCsValues) {
492
510
  var CS = O("countersignature");
493
511
  var others = [], csValues = [];
@@ -561,10 +579,10 @@ function _targetPreimage(siNode, opts) {
561
579
  }
562
580
 
563
581
  // pki.cms.countersign -- documented by the @primitive block in cms-verify.js (the @module pki.cms home).
564
- // Documented `-> Promise`, so a fault leaves as a REJECTION (guard-async).
582
+ // Documented `-> Promise`, so a fault leaves as a rejection (guard-async).
565
583
  function countersign(cmsInput, signers, opts) {
566
- // Every caller-owned argument copied at entry and released when the call settles -- see the note
567
- // on the same call in x509-sign. `signerIndex` and `countersignatureOf` SELECT which signature is
584
+ // Every caller-owned argument copied at entry and released when the call settles; see the note
585
+ // on the same call in x509-sign. `signerIndex` and `countersignatureOf` select which signature is
568
586
  // countersigned, so a late read could attach the countersignature to a different one.
569
587
  return guard.bytes.fixedCall(CmsError, "cms/bad-input", [
570
588
  [cmsInput, "the CMS message"], [signers, "the signer list"], [opts, "pki.cms.countersign options"],
@@ -574,6 +592,7 @@ function countersign(cmsInput, signers, opts) {
574
592
  function _countersign(cmsInput, signers, opts) {
575
593
  opts = opts || {};
576
594
  if (typeof opts !== "object" || Buffer.isBuffer(opts)) throw _err("cms/bad-input", "pki.cms.countersign options must be an object");
595
+ guard.identifier.assertKnownKeys(opts, KNOWN_COUNTERSIGN_OPTS, _err, "cms/bad-input", "unknown opts field ");
577
596
  var list = Array.isArray(signers) ? signers : [signers];
578
597
  if (!list.length) throw _err("cms/bad-input", "pki.cms.countersign requires at least one countersigner");
579
598
  if (opts.signingTime != null && opts.signingTime !== false) guard.time.assertValid(opts.signingTime, _err, "cms/bad-input", "signingTime");
@@ -603,7 +622,7 @@ function _countersign(cmsInput, signers, opts) {
603
622
  return opts.countersignatureOf == null ? _appendCountersigs(siNode, byTarget[idx]) : _spliceNested(siNode, opts.countersignatureOf, byTarget[idx]);
604
623
  }));
605
624
 
606
- // Rebuild SignedData: version, digestAlgorithms (UNCHANGED -- a countersignature digest is not a
625
+ // Rebuild SignedData: version, digestAlgorithms (unchanged, since a countersignature digest is not a
607
626
  // SignedData digestAlgorithm), encapContentInfo, certificates [0]?, crls [1]?, the new signerInfos.
608
627
  var certsNode = null, crlsNode = null;
609
628
  for (var i = 3; i < sdKids.length - 1; i++) {
@@ -624,21 +643,21 @@ function _countersign(cmsInput, signers, opts) {
624
643
  });
625
644
  }
626
645
 
627
- // Coverage residual -- three defensive branches are unreachable through the shipped path:
628
- // * `_skiValue`'s `cert.extensions || []` fallback -- x509.parse always surfaces `extensions`
646
+ // Coverage residual: three defensive branches are unreachable through the shipped path.
647
+ // * `_skiValue`'s `cert.extensions || []` fallback. x509.parse always surfaces `extensions`
629
648
  // as an array (empty when absent), so the `|| []` never fires.
630
- // * `_assertKeyMatchesScheme`'s `key.algorithm || {}` -- a WebCrypto CryptoKey always carries
649
+ // * `_assertKeyMatchesScheme`'s `key.algorithm || {}`. A WebCrypto CryptoKey always carries
631
650
  // an `algorithm`, so the `|| {}` fallback never fires.
632
- // * `_assertKeyMatchesScheme`'s `!ka.hash` guard -- an `imp.hash` is set only for an RSA
651
+ // * `_assertKeyMatchesScheme`'s `!ka.hash` guard. An `imp.hash` is set only for an RSA
633
652
  // scheme, which requires `ka.name` to already equal the RSA name (else the earlier name
634
653
  // check throws); an RSA CryptoKey always carries a `hash`, so `!ka.hash` never fires.
635
654
  // Countersign-side residuals also unreachable through the shipped path:
636
- // * `_resolveSignerIndices`'s `n < 1` throw -- a parsed SignedData always carries at least one
655
+ // * `_resolveSignerIndices`'s `n < 1` throw. A parsed SignedData always carries at least one
637
656
  // SignerInfo, so the default index [0] is always in range.
638
- // * `_spliceNested`'s no-countersignature / index-out-of-range / not-found throws -- the same node
639
- // is validated by `_targetPreimage` FIRST (it computes the nested preimage before the build), so
657
+ // * `_spliceNested`'s no-countersignature / index-out-of-range / not-found throws. The same node
658
+ // is validated by `_targetPreimage` first (it computes the nested preimage before the build), so
640
659
  // by the time `_spliceNested` re-walks it those conditions cannot hold; the checks are
641
660
  // belt-and-suspenders against a future caller reordering the two.
642
- // * the `crls [1]` preservation branches in `countersign` -- pki.cms.sign never emits a crls field,
661
+ // * the `crls [1]` preservation branches in `countersign`. pki.cms.sign never emits a crls field,
643
662
  // so a store this producer countersigns never carries one to preserve.
644
663
  module.exports = { sign: sign, countersign: countersign };