@blamejs/pki 0.5.6 → 0.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/CHANGELOG.md +419 -378
  2. package/MIGRATING.md +65 -0
  3. package/README.md +12 -12
  4. package/lib/acme.js +31 -31
  5. package/lib/asn1-der.js +10 -10
  6. package/lib/attrcert-sign.js +24 -20
  7. package/lib/byte-reader.js +6 -6
  8. package/lib/byte-writer.js +5 -5
  9. package/lib/cbor-det.js +27 -24
  10. package/lib/cmc-build.js +104 -104
  11. package/lib/cmc-verify.js +108 -32
  12. package/lib/cmp-build.js +31 -26
  13. package/lib/cmp-session.js +74 -72
  14. package/lib/cmp-verify.js +72 -58
  15. package/lib/cms-compress.js +8 -9
  16. package/lib/cms-decrypt.js +92 -76
  17. package/lib/cms-encrypt.js +33 -34
  18. package/lib/cms-sign.js +100 -54
  19. package/lib/cms-verify.js +141 -82
  20. package/lib/composite-sig.js +13 -13
  21. package/lib/constants.js +4 -4
  22. package/lib/crl-sign.js +31 -25
  23. package/lib/crl-verify.js +7 -6
  24. package/lib/crmf-sign.js +19 -15
  25. package/lib/csr-sign.js +13 -9
  26. package/lib/ct.js +37 -37
  27. package/lib/edwards-point.js +7 -7
  28. package/lib/est.js +103 -59
  29. package/lib/framework-error.js +5 -5
  30. package/lib/guard-all.js +3 -3
  31. package/lib/guard-async.js +4 -4
  32. package/lib/guard-bytes.js +378 -15
  33. package/lib/guard-compress.js +17 -17
  34. package/lib/guard-crypto.js +1 -1
  35. package/lib/guard-encoding.js +15 -15
  36. package/lib/guard-header.js +3 -3
  37. package/lib/guard-identifier.js +16 -16
  38. package/lib/guard-json.js +15 -15
  39. package/lib/guard-limits.js +7 -7
  40. package/lib/guard-name.js +81 -16
  41. package/lib/guard-parsed.js +144 -75
  42. package/lib/guard-range.js +19 -19
  43. package/lib/guard-secret.js +11 -10
  44. package/lib/guard-text.js +6 -6
  45. package/lib/guard-time.js +10 -10
  46. package/lib/hpke.js +18 -17
  47. package/lib/http-digest.js +35 -35
  48. package/lib/http-retry-after.js +13 -13
  49. package/lib/http-transport.js +20 -19
  50. package/lib/inspect.js +53 -53
  51. package/lib/ip-utils.js +2 -2
  52. package/lib/jose.js +13 -13
  53. package/lib/key.js +16 -16
  54. package/lib/lint.js +51 -51
  55. package/lib/merkle.js +51 -36
  56. package/lib/mime.js +18 -18
  57. package/lib/ocsp-verify.js +10 -10
  58. package/lib/ocsp.js +32 -20
  59. package/lib/oid.js +29 -29
  60. package/lib/path-validate.js +114 -113
  61. package/lib/pbes2.js +16 -16
  62. package/lib/pkcs12-build.js +71 -56
  63. package/lib/pki-build.js +23 -22
  64. package/lib/rc2.js +1 -1
  65. package/lib/rfc3339.js +5 -5
  66. package/lib/schema-all.js +31 -31
  67. package/lib/schema-attrcert.js +12 -12
  68. package/lib/schema-c509.js +144 -142
  69. package/lib/schema-cmc.js +58 -58
  70. package/lib/schema-cmp.js +43 -43
  71. package/lib/schema-cms.js +169 -36
  72. package/lib/schema-crl.js +7 -7
  73. package/lib/schema-crmf.js +28 -28
  74. package/lib/schema-csr.js +12 -12
  75. package/lib/schema-csrattrs.js +16 -16
  76. package/lib/schema-engine.js +18 -18
  77. package/lib/schema-ocsp.js +15 -15
  78. package/lib/schema-pkcs12.js +20 -20
  79. package/lib/schema-pkcs8.js +2 -2
  80. package/lib/schema-pkix.js +131 -126
  81. package/lib/schema-smime.js +19 -19
  82. package/lib/schema-tsp.js +12 -12
  83. package/lib/schema-x509.js +3 -3
  84. package/lib/shbs.js +18 -18
  85. package/lib/sign-scheme.js +19 -16
  86. package/lib/sigstore.js +10 -11
  87. package/lib/sleep.js +1 -1
  88. package/lib/smime.js +308 -96
  89. package/lib/tls-cert-compress.js +18 -18
  90. package/lib/trust.js +27 -27
  91. package/lib/tsp-sign.js +22 -18
  92. package/lib/validator-all.js +1 -1
  93. package/lib/validator-attcert.js +1 -1
  94. package/lib/validator-cose.js +43 -44
  95. package/lib/validator-keydesc.js +3 -3
  96. package/lib/validator-sig.js +13 -13
  97. package/lib/validator-tls.js +11 -11
  98. package/lib/validator-tpm.js +21 -20
  99. package/lib/webauthn-mds.js +67 -67
  100. package/lib/webauthn.js +34 -34
  101. package/lib/webcrypto.js +15 -15
  102. package/lib/x509-sign.js +24 -15
  103. package/package.json +3 -2
  104. package/sbom.cdx.json +6 -6
package/lib/pbes2.js CHANGED
@@ -2,12 +2,12 @@
2
2
  // Copyright (c) blamejs contributors
3
3
  "use strict";
4
4
  //
5
- // @internal -- the RFC 8018 PBES2 / PBKDF2 primitives, the ONE home shared by pki.cms EncryptedData / PWRI
5
+ // @internal -- the RFC 8018 PBES2 / PBKDF2 primitives, the single home shared by pki.cms EncryptedData / PWRI
6
6
  // password encryption and pki.key EncryptedPrivateKeyInfo (RFC 5958). Error-factory-parameterized like the
7
- // guard family: every reject goes through the CALLER's `E(code, msg, cause)` full-code factory plus a domain
8
- // `prefix`, so pki.cms keeps cms/* codes and pki.key keeps key/* codes off ONE implementation. Extracted
9
- // behavior-preservingly from cms-encrypt.js + cms-decrypt.js -- the shipped CMS PBES2 tests are the guard.
10
- // A shape here that CANNOT route through a guard stays with its caller; the reusable PBES2 shape lives here
7
+ // guard family: every reject goes through the caller's `E(code, msg, cause)` full-code factory plus a domain
8
+ // `prefix`, so pki.cms keeps cms/* codes and pki.key keeps key/* codes off one implementation. Extracted
9
+ // behavior-preservingly from cms-encrypt.js and cms-decrypt.js; the shipped CMS PBES2 tests are the guard.
10
+ // A shape here that cannot route through a guard stays with its caller; the reusable PBES2 shape lives here
11
11
  // so a fourth copy (the drift a reviewer flags) cannot re-accrete.
12
12
 
13
13
  var nodeCrypto = require("crypto");
@@ -26,7 +26,7 @@ var PRF_NODE_BY_NAME = { hmacWithSHA1: "sha1", hmacWithSHA256: "sha256", hmacWit
26
26
  var PRF_NODE_BY_OID = {}; Object.keys(PRF_NODE_BY_NAME).forEach(function (n) { PRF_NODE_BY_OID[O(n)] = PRF_NODE_BY_NAME[n]; });
27
27
 
28
28
  // content-encryption OID -> AES key bits (CBC + GCM). The PBES2 encryptionScheme + CMS content cipher table.
29
- // CONTENT_MODE rides the SAME rows so a cipher can never be present for its key length but absent for its
29
+ // CONTENT_MODE rides the same rows so a cipher can never be present for its key length but absent for its
30
30
  // mode: key length alone does not distinguish an AEAD cipher from a plain one, and a consumer that resolves
31
31
  // only the length would open content in the wrong mode whenever the two happen to share a key size.
32
32
  var CONTENT_KEYBITS = {}, CONTENT_MODE = {};
@@ -79,8 +79,8 @@ function pbkdf2ParamsSeq(salt, iterations, prf) {
79
79
  }
80
80
 
81
81
  // The PBES2 AlgorithmIdentifier: SEQUENCE { id-PBES2, SEQUENCE { keyDerivationFunc PBKDF2, encryptionScheme } }.
82
- // The ONE home for the CMS EncryptedData contentEncryptionAlgorithm AND the RFC 5958 EncryptedPrivateKeyInfo
83
- // encryptionAlgorithm -- both wrap the same PBES2 structure around a PBKDF2 KDF plus an AES-CBC scheme whose
82
+ // The single home for the CMS EncryptedData contentEncryptionAlgorithm and the RFC 5958 EncryptedPrivateKeyInfo
83
+ // encryptionAlgorithm: both wrap the same PBES2 structure around a PBKDF2 KDF plus an AES-CBC scheme whose
84
84
  // IV rides in the encryptionScheme parameter. `cipherName` is a registry NAME (an AES-CBC OID name).
85
85
  function pbes2AlgId(salt, iterations, prf, cipherName, iv) {
86
86
  var kdf = b.sequence([b.oid(O("pbkdf2")), pbkdf2ParamsSeq(salt, iterations, prf)]);
@@ -100,7 +100,7 @@ function seqChildren(paramsDer, minLen, what, E, prefix) {
100
100
  return requireChildren(asn1.decode(paramsDer), minLen, what, E, prefix);
101
101
  }
102
102
 
103
- // Strict PBKDF2-params decode: salt (capped BEFORE derivation), iterationCount (positiveInt31 + cap, downward-
103
+ // Strict PBKDF2-params decode: salt (capped before derivation), iterationCount (positiveInt31 + cap, downward-
104
104
  // overridable via a validated-positive-int opts.maxIterations so Math.min(NaN, cap) cannot silently disable
105
105
  // the DoS cap), prf. Returns { salt, iterations, prfNode }.
106
106
  function parsePbkdf2Params(paramsDer, opts, E, prefix, strictPrf) {
@@ -131,7 +131,7 @@ function parsePbkdf2Params(paramsDer, opts, E, prefix, strictPrf) {
131
131
  return { salt: salt, iterations: iterations, prfNode: prfNode };
132
132
  }
133
133
 
134
- // AES-CBC content encryption / decryption (PKCS#7 pad). decrypt's final() throws on bad pad -- the caller
134
+ // AES-CBC content encryption / decryption (PKCS#7 pad). decrypt's final() throws on bad pad, and the caller
135
135
  // collapses that into its uniform decrypt-failed verdict.
136
136
  function cbcEncrypt(key, iv, plaintext, keyBits) {
137
137
  var c = nodeCrypto.createCipheriv("aes-" + keyBits + "-cbc", key, iv);
@@ -155,9 +155,9 @@ function pbes2Encrypt(pwBytes, plaintext, opts, E, prefix) {
155
155
  var iterations = assertIterations(opts.iterations == null ? 2048 : opts.iterations, E, prefix);
156
156
  var salt = opts.salt != null ? assertSalt(opts.salt, E, prefix) : nodeCrypto.randomBytes(16);
157
157
  var iv = opts.iv != null ? opts.iv : nodeCrypto.randomBytes(16);
158
- // The derived key is this module's allocation and protects the caller's plaintext -- a private key
159
- // in the pkcs8 / pkcs12 callers -- so it is cleared once the encryption has consumed it. pwBytes is
160
- // NOT: the caller formatted it and may still own the buffer.
158
+ // The derived key is this module's allocation and protects the caller's plaintext (a private key
159
+ // in the pkcs8 and pkcs12 callers), so it is cleared once the encryption has consumed it. pwBytes
160
+ // is not cleared: the caller formatted it and may still own the buffer.
161
161
  var key = nodeCrypto.pbkdf2Sync(pwBytes, salt, iterations, keyBits / 8, prfNode);
162
162
  try {
163
163
  return { algId: pbes2AlgId(salt, iterations, prf, cipherName, iv), ct: cbcEncrypt(key, iv, plaintext, keyBits) };
@@ -202,7 +202,7 @@ function pbes2Decrypt(pwBytes, params, ciphertext, opts, E, prefix, budget) {
202
202
  }
203
203
  throw E(prefix + "/bad-algorithm-parameters", "malformed PBES2 parameters", e);
204
204
  }
205
- // Charge the shared budget BEFORE deriving, so the work is refused rather than performed.
205
+ // Charge the shared budget before deriving, so the work is refused instead of performed.
206
206
  if (budget) {
207
207
  budget.rounds -= pb.iterations;
208
208
  if (budget.rounds < 0) throw E(prefix + "/iteration-limit", "the aggregate PBKDF2 key-derivation work exceeds the budget (a hostile many-element input)");
@@ -218,7 +218,7 @@ function pbes2Decrypt(pwBytes, params, ciphertext, opts, E, prefix, budget) {
218
218
  function _hmacAlgId(name) { return b.sequence([b.oid(O(name)), b.nullValue()]); }
219
219
 
220
220
  // The id-PBMAC1 AlgorithmIdentifier: id-PBMAC1 + PBMAC1-params { keyDerivationFunc PBKDF2, messageAuthScheme
221
- // HMAC } -- the byte-exact inverse of schema-pkcs12 / schema-cmp's PBMAC1_PARAMS. `desc` = { salt,
221
+ // HMAC }, the byte-exact inverse of schema-pkcs12 / schema-cmp's PBMAC1_PARAMS. `desc` = { salt,
222
222
  // iterationCount, keyLength, prfName, macName }. The prf is omitted iff it equals the default hmacWithSHA1.
223
223
  function pbmac1AlgId(desc) {
224
224
  var pbkdf2Params = [b.octetString(desc.salt), b.integer(BigInt(desc.iterationCount)), b.integer(BigInt(desc.keyLength))];
@@ -238,7 +238,7 @@ function pbmac1(pwBytes, salt, iterationCount, keyLength, prfHash, macHash, mess
238
238
  }).then(function (bits) {
239
239
  // The derived MAC key is this function's allocation, in two forms: the ArrayBuffer deriveBits
240
240
  // returned and the Buffer view over it that is imported. Clearing the view clears both, and it
241
- // runs whether the signing succeeds or throws -- the sibling PBES2 encrypt/decrypt above clear
241
+ // runs whether the signing succeeds or throws. The sibling PBES2 encrypt/decrypt above clear
242
242
  // their derived keys, and every PBMAC1 consumer (PKCS#12 build and verification) inherits this.
243
243
  var mk = Buffer.from(bits);
244
244
  return subtle.importKey("raw", mk, { name: "HMAC", hash: macHash }, false, ["sign"]).then(function (hmacKey) {
@@ -17,20 +17,20 @@
17
17
  */
18
18
  //
19
19
  // The PBES2 bag encryption and the PBMAC1 MAC compose the shared lib/pbes2.js home (the same PBKDF2 + AES-CBC
20
- // + HMAC primitives pki.cms and pki.key use), fed PKCS#12 App. B.1-formatted password bytes -- NOT the CMS
20
+ // + HMAC primitives pki.cms and pki.key use), fed PKCS#12 App. B.1-formatted password bytes, never the CMS
21
21
  // UTF-8 encoding, the single most common PKCS#12 interop failure. The classic Appendix B.2 KDF (ID=3, the MAC
22
22
  // integrity purpose) is bespoke PKCS#12 crypto with no in-tree equivalent, built here as one primitive and
23
- // cross-checked against OpenSSL. The MAC is computed over the CONTENT octets of the id-data authSafe OCTET
24
- // STRING (excluding its TLV header) -- exactly the parser's `macedBytes`, the canonical off-by-the-header
25
- // trap designed out. Public-key integrity (an id-signedData authSafe -- a CMS SignedData over the exact
23
+ // cross-checked against OpenSSL. The MAC is computed over the content octets of the id-data authSafe OCTET
24
+ // STRING (excluding its TLV header), exactly the parser's `macedBytes`, the canonical off-by-the-header
25
+ // trap designed out. Public-key integrity (an id-signedData authSafe, a CMS SignedData over the exact
26
26
  // AuthenticatedSafe, no MacData) is produced by composing pki.cms.sign and verified on open by pki.cms.verify
27
27
  // as the integrity gate before any bag is trusted; the signer is surfaced, never trust-chained (the caller's
28
28
  // pki.path.validate step). Public-key PRIVACY (an id-envelopedData safe encrypting the SafeContents to a
29
29
  // recipient public key, RFC 7292 sec. 3.1) is produced by composing pki.cms.encrypt (AES-CBC EnvelopedData,
30
- // never GCM/AuthEnvelopedData) and opened by composing pki.cms.decrypt with the recipient key -- AFTER the
30
+ // never GCM/AuthEnvelopedData) and opened by composing pki.cms.decrypt with the recipient key, after the
31
31
  // integrity gate, with every recipient-side fault collapsed to the uniform pkcs12/decrypt-failed. Privacy is
32
32
  // independent of integrity: all four combinations (password/public-key integrity x password/public-key privacy)
33
- // are permitted, and the recipient key is a privacy credential only -- never a MAC key, signer, or PBES2 password.
33
+ // are permitted, and the recipient key is a privacy credential only: never a MAC key, signer, or PBES2 password.
34
34
 
35
35
  var nodeCrypto = require("crypto");
36
36
  var asn1 = require("./asn1-der");
@@ -55,13 +55,13 @@ var PemError = frameworkError.PemError;
55
55
  function O(n) { return oid.byName(n); }
56
56
  function _err(code, msg, cause) { return new Pkcs12Error(code, msg, cause); }
57
57
 
58
- // PKCS#12 integrity/privacy defaults (named, not call-site literals -- rule 12).
58
+ // PKCS#12 integrity/privacy defaults, named here so no call site writes them as literals (rule 12).
59
59
  var DEFAULT_MAC_ITER = 2048;
60
60
  var DEFAULT_PBMAC1_ITER = 2048;
61
61
  var MAC_SALT_BYTES = 8;
62
- var MAX_PBMAC1_KEYLEN = 1024; // an HMAC key beyond a hash block is pointless -- bound the derived length
63
- // Both MAC paths cap iterations to bound the SYNCHRONOUS derivation that blocks the event loop, but at
64
- // different counts BECAUSE the per-iteration cost differs by ~5x -- the two ceilings target a comparable
62
+ var MAX_PBMAC1_KEYLEN = 1024; // an HMAC key beyond a hash block is pointless, so bound the derived length
63
+ // Both MAC paths cap iterations to bound the synchronous derivation that blocks the event loop, but at
64
+ // different counts, because the per-iteration cost differs by ~5x: the two ceilings target a comparable
65
65
  // ~1-2 second wall-clock budget, not inconsistent DoS budgets. The classic Appendix B KDF is a hand-rolled
66
66
  // JS hash loop (a createHash per iteration, ~1.1us each, so 1e6 ~= 1s); PBMAC1 derives via native
67
67
  // crypto.pbkdf2Sync (one FFI call, ~0.2us per iteration, so the shared C.LIMITS.PBKDF2_MAX_ITERATIONS of 1e7
@@ -69,12 +69,12 @@ var MAX_PBMAC1_KEYLEN = 1024; // an HMAC key beyond a hash block is pointless
69
69
  // pkcs12-local (its KDF is bespoke here) while PBMAC1 reuses the toolkit-wide PBKDF2 ceiling. 1e6 is ~500x
70
70
  // the OpenSSL default (2048) yet still bounds the loop to ~1 second.
71
71
  var CLASSIC_MAC_MAX_ITERATIONS = 1000000;
72
- // The AGGREGATE cap on synchronous KDF work across a single open() -- rounds summed over every encrypted
72
+ // The aggregate cap on synchronous KDF work across a single open(): rounds summed over every encrypted
73
73
  // bag and safe, whichever scheme it uses: the legacy App. B KDF (its block count x iterations) and PBKDF2
74
74
  // alike. The per-bag iteration cap resets per bag, so a hostile store that duplicates a costly bag up to the
75
75
  // parser's 1024-element limit would otherwise block the event loop for minutes; this bounds the total to
76
- // ~the classic MAC ceiling. It covers BOTH schemes because covering only the legacy one left the modern
77
- // path -- the one every current producer emits -- free to multiply its cap by the element limit.
76
+ // ~the classic MAC ceiling. It covers both schemes because covering only the legacy one left the modern
77
+ // path (the one every current producer emits) free to multiply its cap by the element limit.
78
78
  // A conforming store runs only a few thousand rounds (its handful of bags at ~2048 iterations).
79
79
  var KDF_MAX_ROUNDS = CLASSIC_MAC_MAX_ITERATIONS;
80
80
 
@@ -104,10 +104,10 @@ var DIGEST_NAME = { sha1: "sha1", sha256: "sha256", sha384: "sha384", sha512: "s
104
104
  // is taken verbatim as already-formatted bytes (an escape hatch for a caller that pre-encodes).
105
105
  function _p12Password(pw) { return _p12PasswordOwned(pw).bytes; }
106
106
 
107
- // An ABSENT password is refused rather than encoded as the empty one. The two are not the same
107
+ // An absent password is refused, never encoded as the empty one. The two are not the same
108
108
  // credential, and the difference is invisible at the call site: a caller who misspells the option,
109
109
  // or threads it through a layer that drops it, otherwise gets a store whose private key is
110
- // protected by nothing and no error anywhere saying so. The empty password remains available --
110
+ // protected by nothing and no error anywhere saying so. The empty password remains available:
111
111
  // it just has to be asked for, as "".
112
112
  var _MISSING_PASSWORD = "a password must be a string, Buffer, or Uint8Array -- an omitted password " +
113
113
  "is not the empty password; pass \"\" to use the empty one deliberately";
@@ -116,17 +116,19 @@ var _MISSING_PASSWORD = "a password must be a string, Buffer, or Uint8Array -- a
116
116
  // capability cannot arrive with its option silently ignored at this boundary.
117
117
  var _INTEGRITY_OPTS = { mode: 1, signer: 1, signers: 1, certificates: 1, sid: 1, signingTime: 1 };
118
118
 
119
- // The same encoding, reporting OWNERSHIP -- mirroring pbes2.passwordBytesOwned. A caller-supplied
120
- // Buffer is returned AS-IS and is BORROWED: clearing it would destroy the caller's own credential,
119
+ // The same encoding, reporting ownership, mirroring pbes2.passwordBytesOwned. A caller-supplied
120
+ // Buffer is returned as-is and is borrowed: clearing it would destroy the caller's own credential,
121
121
  // which is a worse defect than leaving a copy readable. Every other input is re-encoded into a
122
122
  // buffer this module allocated, which it must clear once a derivation has consumed it.
123
123
  function _p12PasswordOwned(pw) {
124
- if (Buffer.isBuffer(pw)) return { bytes: pw, owned: false };
124
+ if (Buffer.isBuffer(pw)) return { bytes: guard.bytes.view(pw, Pkcs12Error, "pkcs12/bad-input", "the password"), owned: false };
125
125
  return { bytes: _p12Encode(pw), owned: true };
126
126
  }
127
127
  function _p12Encode(pw) {
128
- if (Buffer.isBuffer(pw)) return pw;
129
- if (pw instanceof Uint8Array) return Buffer.from(pw);
128
+ // A view first, so a detached backing store is a reject and never an empty password; then a copy,
129
+ // because everything this function returns is reported owned and gets zeroized after derivation,
130
+ // and handing back a view of a caller's Uint8Array would wipe the caller's own credential.
131
+ if (Buffer.isBuffer(pw) || pw instanceof Uint8Array) return guard.bytes.snapshot(pw, Pkcs12Error, "pkcs12/bad-input", "the password");
130
132
  if (typeof pw !== "string") throw _err("pkcs12/bad-input", _MISSING_PASSWORD);
131
133
  var out = Buffer.alloc(pw.length * 2 + 2); // + the 2-byte NULL terminator
132
134
  for (var i = 0; i < pw.length; i++) {
@@ -139,18 +141,18 @@ function _p12Encode(pw) {
139
141
  }
140
142
 
141
143
  // The password bytes for the RFC 8018 PBES2 bag ciphers and the RFC 9579 PBMAC1 MAC. Although RFC 9579 sec. 6
142
- // specifies the BMPString encoding here too, OpenSSL and NSS -- and thus the interoperable ecosystem -- feed
144
+ // specifies the BMPString encoding here too, OpenSSL and NSS (and thus the interoperable ecosystem) feed
143
145
  // PBKDF2 the raw UTF-8 password for these modern schemes (confirmed byte-for-byte against `openssl pkcs12`),
144
146
  // reserving the BMPString+NULL form for the bespoke Appendix B KDF only. A file we emit must open in OpenSSL,
145
147
  // so the modern schemes use UTF-8 here; only the classic Appendix B MAC uses `_p12Password`.
146
- // The UTF-8 encoding, reporting OWNERSHIP -- the sibling of _p12PasswordOwned, and it exists
147
- // for the same reason. A caller-supplied Buffer is BORROWED and left alone; every other input is
148
+ // The UTF-8 encoding, reporting ownership: the sibling of _p12PasswordOwned, and it exists
149
+ // for the same reason. A caller-supplied Buffer is borrowed and left alone; every other input is
148
150
  // re-encoded into a buffer this module allocated, and a plaintext password copy this module made
149
151
  // is cleared once the derivation has consumed it. Without it the App. B.1 copy taken from the same
150
152
  // argument in the same call was wiped while this one was not.
151
153
  function _pbePasswordOwned(pw) {
152
- if (Buffer.isBuffer(pw)) return { bytes: pw, owned: false };
153
- if (pw instanceof Uint8Array) return { bytes: Buffer.from(pw), owned: true };
154
+ if (Buffer.isBuffer(pw)) return { bytes: guard.bytes.view(pw, Pkcs12Error, "pkcs12/bad-input", "the password"), owned: false };
155
+ if (pw instanceof Uint8Array) return { bytes: guard.bytes.snapshot(pw, Pkcs12Error, "pkcs12/bad-input", "the password"), owned: true };
154
156
  if (typeof pw !== "string") throw _err("pkcs12/bad-input", _MISSING_PASSWORD);
155
157
  return { bytes: Buffer.from(pw, "utf8"), owned: true };
156
158
  }
@@ -169,7 +171,7 @@ function _blockFill(src, blockSize) {
169
171
  }
170
172
 
171
173
  // RFC 7292 Appendix B.2 KDF: derive `nBytes` of key material for purpose `id` (1 = cipher key, 2 = IV,
172
- // 3 = MAC) from the App. B.1 password bytes + salt over `iterations` rounds of `hashName`. NOT PBKDF2. The
174
+ // 3 = MAC) from the App. B.1 password bytes + salt over `iterations` rounds of `hashName`. This is not PBKDF2. The
173
175
  // block count c = ceil(nBytes/u): the MAC always requests exactly the hash output u (c = 1, one diversified,
174
176
  // salted, iterated hash), while a legacy-PBE cipher key wider than u (e.g. 24-byte 3-key-3DES from SHA-1's
175
177
  // 20-byte output, c = 2) exercises the multi-block feedback (step 6C: I = (I + B + 1) mod 2^(8v) between
@@ -179,7 +181,7 @@ function _p12Kdf(hashName, id, pwBytes, salt, iterations, nBytes) {
179
181
  var uv = P12_KDF_UV[hashName], u = uv.u, v = uv.v;
180
182
  var D = Buffer.alloc(v, id); // step 1: v copies of the ID diversifier
181
183
  // Each fill is a full block-repeated copy of the salt / password; only their concatenation is
182
- // used, so the sources are cleared rather than abandoned.
184
+ // used, so the sources are cleared and never abandoned.
183
185
  var sFill = _blockFill(salt, v), pFill = _blockFill(pwBytes, v);
184
186
  var I = Buffer.concat([sFill, pFill]); // steps 2-4: I = S || P
185
187
  guard.secret.zeroizeAll([sFill, pFill], Pkcs12Error, "pkcs12/bad-input", "a key-derivation fill");
@@ -204,7 +206,7 @@ function _p12Kdf(hashName, id, pwBytes, salt, iterations, nBytes) {
204
206
  }
205
207
  guard.secret.zeroize(A, Pkcs12Error, "pkcs12/bad-input", "a key-derivation block");
206
208
  }
207
- // I is S || P -- it carries the password bytes -- and is finished with here.
209
+ // I is S || P, so it carries the password bytes, and is finished with here.
208
210
  guard.secret.zeroize(I, Pkcs12Error, "pkcs12/bad-input", "the key-derivation input block");
209
211
  // Step 8 is "the first nBytes of A", but returning a SUBARRAY would hand back a view over a
210
212
  // larger allocation: a caller clearing what it received would leave the rest of the final digest
@@ -356,7 +358,7 @@ function _pbeOpts(enc) {
356
358
  };
357
359
  }
358
360
 
359
- // SafeContents ::= SEQUENCE OF SafeBag (ordered, NOT a SET OF).
361
+ // SafeContents ::= SEQUENCE OF SafeBag (ordered, and not a SET OF).
360
362
  function _buildSafeContents(bags, opts, depth) {
361
363
  if (!Array.isArray(bags)) throw _err("pkcs12/bad-input", "bags must be an array");
362
364
  if (bags.length > C.LIMITS.PKCS12_MAX_ELEMENTS) throw _err("pkcs12/bad-input", "a SafeContents exceeds the element cap " + C.LIMITS.PKCS12_MAX_ELEMENTS);
@@ -364,13 +366,13 @@ function _buildSafeContents(bags, opts, depth) {
364
366
  }
365
367
 
366
368
  // One AuthenticatedSafe element: a plaintext id-data ContentInfo, an id-encryptedData ContentInfo whose
367
- // EncryptedData is the PBES2 encryption of the SafeContents (RFC 7292 sec. 5.1 step 2A/2B), or -- for
368
- // PUBLIC-KEY privacy (sec. 3.1) -- an id-envelopedData ContentInfo whose CMS EnvelopedData encrypts the
369
+ // EncryptedData is the PBES2 encryption of the SafeContents (RFC 7292 sec. 5.1 step 2A/2B), or, for
370
+ // public-key privacy (sec. 3.1), an id-envelopedData ContentInfo whose CMS EnvelopedData encrypts the
369
371
  // SafeContents to one or more recipient public keys. Async because the envelope path awaits pki.cms.encrypt.
370
372
  async function _buildAuthSafeElement(sc, opts) {
371
373
  if (!sc || typeof sc !== "object") throw _err("pkcs12/bad-input", "each safeContents entry must be an object");
372
- // A privacy directive is honored whenever its field is PRESENT (even if falsy). A present-but-falsy `recipients`
373
- // or `encrypt` (null / false / "") is a configuration error that intended privacy -- it must fail closed, NEVER
374
+ // A privacy directive is honored whenever its field is present (even if falsy). A present-but-falsy `recipients`
375
+ // or `encrypt` (null / false / "") is a configuration error that intended privacy, so it must fail closed and never
374
376
  // silently fall through to a plaintext id-data safe that would emit the bags in cleartext (privacy downgraded to
375
377
  // none while still producing a valid MAC-protected PFX). Omit the field entirely for a genuinely plaintext safe.
376
378
  var hasRecipients = sc.recipients !== undefined;
@@ -379,10 +381,10 @@ async function _buildAuthSafeElement(sc, opts) {
379
381
  var safeContentsDer = _buildSafeContents(sc.bags || [], opts, 0);
380
382
  if (hasRecipients) {
381
383
  if (!Array.isArray(sc.recipients) || !sc.recipients.length) throw _err("pkcs12/bad-input", "safeContents.recipients must be a non-empty array of recipient descriptors (RFC 5652 sec. 6.1)");
382
- // Public-key privacy admits only CERTIFICATE recipients (ktri / kari / kemri -- RSA / ECDH / X25519 / X448 /
383
- // ML-KEM, dispatched off the cert key). A password (pwri) or KEK (kekri) recipient is NOT public-key privacy
384
- // and pki.pkcs12.open (which forwards only { recipientKey, recipientCert } to cms.decrypt) cannot reopen it --
385
- // so reject it here rather than emit a store the toolkit's own reader cannot process. (Re-open: pwri/kekri
384
+ // Public-key privacy admits only certificate recipients (ktri / kari / kemri: RSA / ECDH / X25519 / X448 /
385
+ // ML-KEM, dispatched off the cert key). A password (pwri) or KEK (kekri) recipient is not public-key privacy
386
+ // and pki.pkcs12.open (which forwards only { recipientKey, recipientCert } to cms.decrypt) cannot reopen it,
387
+ // so reject it here and never emit a store the toolkit's own reader cannot process. (Re-open: pwri/kekri
386
388
  // privacy when open forwards the full cms.decrypt key-material model.)
387
389
  sc.recipients.forEach(function (r) {
388
390
  if (!r || typeof r !== "object" || r.cert == null) throw _err("pkcs12/bad-input", "a public-key privacy recipient must be a certificate recipient { cert } (a password or KEK recipient is not public-key privacy and cannot be reopened by pkcs12.open)");
@@ -472,9 +474,9 @@ function _normalizeSpec(spec, opts) {
472
474
  (spec.ca || []).forEach(function (ca) { certBags.push({ type: "cert", cert: ca }); });
473
475
  var sc = [];
474
476
  if (enveloped) {
475
- // Public-key-privacy convenience: the cert bags + a PLAINTEXT keyBag in ONE recipient-enveloped safe (the
477
+ // Public-key-privacy convenience: the cert bags plus a plaintext keyBag in one recipient-enveloped safe (the
476
478
  // envelope supplies confidentiality; there is no recipient-encrypted key-bag type, RFC 7292 sec. 4.2). The
477
- // caller password, if any, remains the MAC (integrity) password -- privacy and integrity are independent.
479
+ // caller password, if any, remains the MAC (integrity) password; privacy and integrity are independent.
478
480
  var bags = certBags.slice();
479
481
  if (spec.key != null) bags.push({ type: "key", key: spec.key, friendlyName: spec.friendlyName, localKeyId: spec.localKeyId });
480
482
  sc.push({ recipients: opts.recipientCerts.map(function (c) { return { cert: c }; }), bags: bags });
@@ -503,7 +505,7 @@ function _normalizeSpec(spec, opts) {
503
505
  * plaintext or PBES2-encrypted `SafeContents` of key / shroudedKey / cert / crl / secret / nested
504
506
  * safeContents bags. Keys and certs are validated before wrapping. Password integrity (the default) MACs the
505
507
  * AuthenticatedSafe with a classic Appendix B HMAC or an RFC 9579 PBMAC1. Public-key integrity
506
- * (`opts.integrity.mode: "public-key"`) instead wraps the AuthenticatedSafe in a CMS SignedData -- a
508
+ * (`opts.integrity.mode: "public-key"`) instead wraps the AuthenticatedSafe in a CMS SignedData: a
507
509
  * signature from a keypair, no MacData (RFC 7292 sec. 4). Privacy (PBES2 bag encryption via `password`) is
508
510
  * independent of the integrity mode. The store is re-parsed before return.
509
511
  *
@@ -523,7 +525,20 @@ function _normalizeSpec(spec, opts) {
523
525
  * { type: 'shroudedKey', key: signerKeyPkcs8, encrypt: { password: 'changeit' } } ] }] },
524
526
  * { password: 'changeit', mac: { algorithm: 'hmac', hash: 'sha256' } });
525
527
  */
526
- async function build(spec, opts) {
528
+ function build(spec, opts) {
529
+ // Both arguments copied at entry and released when the call settles -- see the note on the same
530
+ // call in x509-sign. A PKCS#12 file is assembled over many promise turns (a key derivation per
531
+ // bag, then the outer MAC) while the caller still owns the object holding the passwords and the
532
+ // bag contents. Rewriting a password buffer partway through produced a file whose MAC and whose
533
+ // bag encryption were keyed to two different values, so it opened with neither the password
534
+ // passed in nor the one written over it. The release is what keeps the copy of that password
535
+ // from outliving the call.
536
+ return guard.bytes.fixedCall(Pkcs12Error, "pkcs12/bad-input", [
537
+ [spec, "the PKCS#12 spec"], [opts, "pki.pkcs12.build options"],
538
+ ], _build);
539
+ }
540
+
541
+ async function _build(spec, opts) {
527
542
  opts = opts || {};
528
543
  // The integrity mode is checked against the permitted set rather than compared to one literal.
529
544
  // Compared, any other spelling reads as "not public-key" and silently selects password integrity,
@@ -660,33 +675,33 @@ function _capWork(iterations, salt, opts, keyLength, hardCap) {
660
675
  * @defends pkcs12-unauthenticated-decrypt (CWE-347), pbes2-padding-oracle (CWE-208)
661
676
  * @related pki.pkcs12.build, pki.pkcs12.verifyMac, pki.schema.pkcs12.parse
662
677
  *
663
- * Read a PKCS#12 store: verify its integrity FIRST -- a password store's MAC, or a public-key store's CMS
664
- * SignedData signature (RFC 7292 sec. 4 / 5.1 -- never trust a bag from a store whose integrity check fails) --
665
- * then decrypt every PBES2 privacy safe and pkcs8ShroudedKeyBag with the password -- and every id-envelopedData
666
- * (public-key privacy) safe with `opts.recipientKey` (RFC 7292 sec. 3.1, via `pki.cms.decrypt`) -- returning a
667
- * structured bundle `{ integrityMode, macVerified, signers, keys, certs, crls, secrets }` -- each private key as PKCS#8
678
+ * Read a PKCS#12 store: verify its integrity first (a password store's MAC, or a public-key store's CMS
679
+ * SignedData signature, per RFC 7292 sec. 4 / 5.1, so no bag from a store whose integrity check fails is trusted),
680
+ * then decrypt every PBES2 privacy safe and pkcs8ShroudedKeyBag with the password, and every id-envelopedData
681
+ * (public-key privacy) safe with `opts.recipientKey` (RFC 7292 sec. 3.1, via `pki.cms.decrypt`), returning a
682
+ * structured bundle `{ integrityMode, macVerified, signers, keys, certs, crls, secrets }` with each private key as PKCS#8
668
683
  * `PrivateKeyInfo` DER (re-validated), each certificate / CRL / secret as raw DER, all carrying their
669
684
  * `friendlyName` / `localKeyId` for pairing. `pfx` is the store's DER `Buffer`, a PEM string, or an
670
- * unmodified `pki.schema.pkcs12.parse` result; a REBUILT parsed store is refused, since the bytes whose
685
+ * unmodified `pki.schema.pkcs12.parse` result; a rebuilt parsed store is refused, since the bytes whose
671
686
  * integrity is checked and the bags returned as checked are separate properties of it.
672
687
  *
673
688
  * A MAC-less store is refused (`pkcs12/no-integrity`) unless `opts.allowUnauthenticated` is set. A public-key
674
689
  * integrity store is verified through `pki.cms.verify` before any bag is trusted; a signature failure is
675
690
  * `pkcs12/signature-invalid`, and `signers` carries the per-signer verdict `[{ ok, sid, cert }]` (`null` in
676
- * password / MAC-less mode). The signer is surfaced, NEVER trust-chained -- anchoring `signers[i].cert` to a
691
+ * password / MAC-less mode). The signer is surfaced, never trust-chained: anchoring `signers[i].cert` to a
677
692
  * trust root is the caller's `pki.path.validate` step (the out-of-path signer contract). Privacy is
678
693
  * independent of integrity, so the bag `password` still decrypts a public-key store's bags; a wrong bag
679
694
  * password there is the uniform `pkcs12/decrypt-failed` (no MAC to catch it first). Bags encrypted under
680
- * PBES2 (AES-CBC), or the RFC 7292 App. C legacy schemes an `openssl pkcs12 -legacy` store uses -- 3-key /
681
- * 2-key Triple-DES-CBC and 40-/128-bit RC2-CBC (the App. B KDF over the BMPString password, RC2 via an in-tree
682
- * RFC 2268 cipher) -- are decrypted; the legacy RC4 schemes are named and refused.
695
+ * PBES2 (AES-CBC), or the RFC 7292 App. C legacy schemes an `openssl pkcs12 -legacy` store uses (3-key /
696
+ * 2-key Triple-DES-CBC and 40-/128-bit RC2-CBC, the App. B KDF over the BMPString password, RC2 via an in-tree
697
+ * RFC 2268 cipher), are decrypted; the legacy RC4 schemes are named and refused.
683
698
  *
684
699
  * @opts
685
- * - `allowUnauthenticated` (boolean) -- open a MAC-less store anyway (result carries `macVerified: false`).
686
- * - `signerCerts` (array of cert DER) -- signer certificate(s) for a public-key store built with `certificates: false` (forwarded to `pki.cms.verify`).
687
- * - `recipientKey` (PKCS#8 DER|PEM) + `recipientCert` (cert DER|PEM, or `recipientIndex`) -- decrypt an id-envelopedData (public-key privacy) safe. The recipient key is a privacy credential only; a wrong key / tampered envelope is the uniform `pkcs12/decrypt-failed`, and an enveloped safe with no `recipientKey` is `pkcs12/no-recipient-key`.
688
- * - `maxIterations` (number) -- lower the PBKDF2 / MAC iteration cap for this call (downward-only).
689
- * - `keys` (string) -- `der` (default) or `crypto` (also `pki.key.import` each private key to a CryptoKey).
700
+ * - `allowUnauthenticated` (boolean): open a MAC-less store anyway (result carries `macVerified: false`).
701
+ * - `signerCerts` (array of cert DER): signer certificate(s) for a public-key store built with `certificates: false` (forwarded to `pki.cms.verify`).
702
+ * - `recipientKey` (PKCS#8 DER|PEM) + `recipientCert` (cert DER|PEM, or `recipientIndex`): decrypt an id-envelopedData (public-key privacy) safe. The recipient key is a privacy credential only; a wrong key / tampered envelope is the uniform `pkcs12/decrypt-failed`, and an enveloped safe with no `recipientKey` is `pkcs12/no-recipient-key`.
703
+ * - `maxIterations` (number): lower the PBKDF2 / MAC iteration cap for this call (downward-only).
704
+ * - `keys` (string): `der` (default) or `crypto` (also `pki.key.import` each private key to a CryptoKey).
690
705
  * - `importAlgorithm` -- forwarded to `pki.key.import` for the ambiguous RSA / EC arms when `keys: crypto`.
691
706
  * @example
692
707
  * var pair = await pki.key.generate("Ed25519");
package/lib/pki-build.js CHANGED
@@ -6,7 +6,7 @@
6
6
  // binds a domain namespace once and returns the shared PKIX producing primitives every signer module
7
7
  // composes: the distinguished-name encoder, the GeneralName + RFC 5280 sec. 4.2.1 extension-value
8
8
  // encoders, the embedded-input validators (a raw Name / a SubjectPublicKeyInfo / a pre-encoded Extension
9
- // run through the SAME parser the decoder uses), the sign-scheme bridge, and the post-sign signature
9
+ // run through the same parser the decoder uses), the sign-scheme bridge, and the post-sign signature
10
10
  // self-check (the key-match / proof-of-possession verify). Each is parameterized on the CALLER's error
11
11
  // class + code prefix, so pki.x509.sign keeps x509/* codes and pki.csr.sign keeps csr/*.
12
12
 
@@ -64,7 +64,7 @@ function makeBuilder(ctx) {
64
64
  }
65
65
  function encodeAtv(attrName, value) {
66
66
  if (value == null || value === "") throw E("bad-name", "the " + attrName + " attribute value must be a non-empty string");
67
- // oid.byName returns undefined (does not throw) for an unrecognized name -- reject it explicitly.
67
+ // For an unrecognized name oid.byName returns undefined and does not throw, so reject explicitly.
68
68
  var typeOid = O(attrName);
69
69
  if (typeOid == null) throw E("bad-name", "unknown distinguished-name attribute " + JSON.stringify(attrName));
70
70
  var valueTlv;
@@ -119,8 +119,8 @@ function makeBuilder(ctx) {
119
119
  case "uniformResourceIdentifier": case "uri": return b.contextPrimitive(6, ia5Content(v));
120
120
  case "iPAddress":
121
121
  // Accept a dotted-quad / colon-hex string (packed to its 4/16 network octets) as well as a
122
- // pre-packed Buffer, so an iPAddress SAN reads like dNSName/URI rather than forcing the caller
123
- // to pack octets by hand (RFC 5280 sec. 4.2.1.6 -- a GeneralName iPAddress is a bare host address).
122
+ // pre-packed Buffer, so an iPAddress SAN reads like dNSName/URI and the caller does not pack
123
+ // octets by hand (a GeneralName iPAddress is a bare host address, RFC 5280 sec. 4.2.1.6).
124
124
  var ipBuf = v;
125
125
  if (typeof v === "string") {
126
126
  ipBuf = ipUtils.packIpLiteral(v);
@@ -144,15 +144,15 @@ function makeBuilder(ctx) {
144
144
  return b.namedBitString(positions);
145
145
  }
146
146
  // Resolve an OID token to a dotted-decimal OID: a registered name via the registry, OR a raw dotted-OID
147
- // string accepted directly (an unregistered KeyPurposeId / policy OID -- BIMI, document-signing, vendor
148
- // purposes -- is a common valid input). A token that is neither fails closed (never silently accept a
147
+ // string accepted directly (an unregistered KeyPurposeId / policy OID for BIMI, document-signing or a
148
+ // vendor purpose is a common valid input). A token that is neither fails closed (never silently accept a
149
149
  // typo'd name); a dotted string that passes the shape check is still arc-validated by b.oid's encoder.
150
150
  function _resolveOid(n, label) {
151
151
  var dotted = O(n);
152
152
  if (dotted != null) return dotted;
153
153
  if (typeof n === "string" && DOTTED_OID_RE.test(n)) {
154
154
  // The regex fixes the general shape; b.oid's encoder is the authoritative X.660 arc-bounds check
155
- // (a first arc 0/1 caps the second at 39; an arc must DER-encode). A failure there is THIS producer's
155
+ // (a first arc 0/1 caps the second at 39; an arc must DER-encode). A failure there is this producer's
156
156
  // bad-input, not a leaked oid/* code, so every shared-builder consumer keeps its own error contract.
157
157
  try { b.oid(n); }
158
158
  catch (e) { throw E("bad-input", "invalid " + label + " OID " + JSON.stringify(n) + " (violates the X.660 arc bounds)", e); }
@@ -184,7 +184,7 @@ function makeBuilder(ctx) {
184
184
  function extAki(keyid) { return b.sequence([b.contextPrimitive(0, keyid)]); } // keyIdentifier [0] IMPLICIT OCTET STRING
185
185
  // GeneralNames ::= SEQUENCE SIZE(1..MAX) OF GeneralName. With no implicitTag it is a universal SEQUENCE
186
186
  // (subjectAltName / issuerAltName / IssuerSerial.issuer). With an implicitTag it is a context-constructed
187
- // [n] whose tag REPLACES the SEQUENCE tag (RFC 5755 IMPLICIT TAGS -- Holder.entityName [1],
187
+ // [n] whose tag REPLACES the SEQUENCE tag (RFC 5755 IMPLICIT TAGS: Holder.entityName [1],
188
188
  // RoleSyntax.roleAuthority [0], IetfAttrSyntax.policyAuthority [0]); the [n] node's children ARE the
189
189
  // GeneralName members, no inner SEQUENCE wrapper.
190
190
  function encodeGeneralNames(entries, implicitTag) {
@@ -213,7 +213,8 @@ function makeBuilder(ctx) {
213
213
  }
214
214
 
215
215
  // The SHA-1 subjectKeyIdentifier (RFC 5280 sec. 4.2.1.2 method 1): SHA-1 of the subjectPublicKey BIT
216
- // STRING CONTENT (past the unused-bits octet), NOT the whole SPKI or the BIT STRING TLV.
216
+ // STRING content, taken past the unused-bits octet. Hashing the whole SPKI or the BIT STRING TLV
217
+ // gives a different, wrong identifier.
217
218
  function spkiKeyId(spkiDer) {
218
219
  var keyBytes = asn1.read.bitString(asn1.decode(spkiDer).children[1]).bytes;
219
220
  // RFC 5280 sec. 4.2.1.2 method 1 DEFINES the subjectKeyIdentifier as the SHA-1 of the
@@ -223,18 +224,17 @@ function makeBuilder(ctx) {
223
224
  return nodeCrypto.createHash("sha1").update(keyBytes).digest();
224
225
  }
225
226
  function skiKeyId(val, spkiDer) {
226
- if (Buffer.isBuffer(val)) return val;
227
+ if (Buffer.isBuffer(val)) return guard.bytes.snapshot(val, ErrorClass, ctx.prefix + "/bad-input", "subjectKeyIdentifier");
227
228
  if (val === true) return spkiKeyId(spkiDer);
228
229
  throw E("bad-input", "subjectKeyIdentifier must be true (auto-derive) or a Buffer key id");
229
230
  }
230
231
 
231
232
  // ---- embedded-input validators ----
232
233
  function reqDer(v, what) {
233
- if (Buffer.isBuffer(v)) return v;
234
- if (v instanceof Uint8Array) return Buffer.from(v);
234
+ if (Buffer.isBuffer(v) || v instanceof Uint8Array) return guard.bytes.snapshot(v, ErrorClass, ctx.prefix + "/bad-input", what);
235
235
  throw E("bad-input", what + " must be a DER Buffer");
236
236
  }
237
- // Full validation of an embedded SubjectPublicKeyInfo via the SAME parser the decoder uses.
237
+ // Full validation of an embedded SubjectPublicKeyInfo, walked through the parser the decoder uses.
238
238
  function assertValidSpki(spkiDer, what) {
239
239
  var node;
240
240
  try { node = asn1.decode(spkiDer); }
@@ -345,10 +345,11 @@ function makeBuilder(ctx) {
345
345
  return { subjectPublicKeyInfo: { algorithm: { oid: keyOid, parameters: alg.children.length > 1 ? alg.children[1].bytes : undefined } } };
346
346
  }
347
347
 
348
- // Confirm the produced signature verifies under `spki` -- key-type-agnostic (a PKCS#8 key, a WebCrypto
349
- // CryptoKey, or any signer), where deriving-and-comparing the public key cannot (a non-extractable
350
- // CryptoKey has no exportable public half). This is the x509 chain self-check AND the CSR proof of
351
- // possession. Composite verifies both components. Returns a promise for composite, sync-throws for classical.
348
+ // Confirm the produced signature verifies under `spki`. This works for any key type (a PKCS#8
349
+ // key, a WebCrypto CryptoKey, or any signer), where deriving-and-comparing the public key cannot
350
+ // (a non-extractable CryptoKey has no exportable public half). This is the x509 chain self-check
351
+ // AND the CSR proof of possession. Composite verifies both components. Returns a promise for
352
+ // composite, sync-throws for classical.
352
353
  function assertSignatureVerifies(preimage, sig, spki, scheme) {
353
354
  if (scheme.composite) {
354
355
  return compositeSig.compositeVerify(spki, sig, preimage, scheme.composite, ErrorClass, code("unsupported-algorithm"), code("bad-input")).then(function (r) {
@@ -395,11 +396,11 @@ function tbsNameField(cert, which) {
395
396
  return tbs.children[(hasVersion ? 1 : 0) + (which === "subject" ? 4 : 2)].bytes;
396
397
  }
397
398
 
398
- // The serialNumber of a parsed X.509 certificate, read from the SIGNED bytes rather than off the
399
- // object. Same tbs layout as above, and the same reason: where a producer binds an identity to an
400
- // existing certificate, that identity is issuer AND serial together. Deriving one from the bytes
401
- // and reading the other from the object lets the halves name different certificates -- a Holder
402
- // whose issuer is genuine and whose serial is whatever the caller wrote.
399
+ // The serialNumber of a parsed X.509 certificate, read from the SIGNED bytes. Same tbs layout as
400
+ // above, and the same reason: where a producer binds an identity to an existing certificate, that
401
+ // identity is issuer AND serial together. Deriving one from the bytes and reading the other from
402
+ // the object lets the halves name different certificates, producing a Holder whose issuer is
403
+ // genuine and whose serial is whatever the caller wrote.
403
404
  function tbsSerialNumber(cert) {
404
405
  var tbs = asn1.decode(cert.tbsBytes);
405
406
  var hasVersion = tbs.children[0].tagClass === "context" && tbs.children[0].tagNumber === 0;
package/lib/rc2.js CHANGED
@@ -8,7 +8,7 @@
8
8
  //
9
9
  // rc2 -- a from-scratch RFC 2268 RC2-CBC. OpenSSL 3.x moved RC2 to the legacy provider (not loaded by Node),
10
10
  // so node:crypto cannot decrypt these bags; this in-tree primitive fills that single gap (Hard rule #1: own
11
- // code, no npm hop -- MANIFEST stays empty; a lib primitive like webcrypto.js, not a lib/vendor/ bundle). The
11
+ // code, no npm hop, so MANIFEST stays empty; it is a lib primitive alongside webcrypto.js, outside lib/vendor/). The
12
12
  // RFC 2268 sec. 5 known-answer vectors pin the cipher; the `openssl -legacy` store is the end-to-end KAT.
13
13
 
14
14
  // RFC 2268 sec. 2: PITABLE, a fixed 256-byte permutation of the digits of pi used by the key schedule.
package/lib/rfc3339.js CHANGED
@@ -41,13 +41,13 @@ function parse(v, E, code, label) {
41
41
  return new Date(v);
42
42
  }
43
43
 
44
- // RFC 3339 sec. 5.6 `full-date`: a calendar date with NO time component. A distinct production from
45
- // the date-time above, and deliberately a separate pair of functions rather than a flag: a consumer
44
+ // RFC 3339 sec. 5.6 `full-date`: a calendar date with no time component. A distinct production from
45
+ // the date-time above, and deliberately a separate pair of functions instead of a flag: a consumer
46
46
  // that expects one and is handed the other has a malformed value, not a permissible variant.
47
47
  var FULL_DATE_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
48
48
 
49
49
  // isValidDate(v) -> boolean: v is a syntactically well-formed AND calendar-valid RFC 3339 full-date.
50
- // Calendar validity matters as much as shape -- "2021-02-30" parses as a Date in JS by rolling over
50
+ // Calendar validity matters as much as shape: "2021-02-30" parses as a Date in JS by rolling over
51
51
  // into March, so a shape-only check would silently accept a day that does not exist.
52
52
  function isValidDate(v) {
53
53
  if (typeof v !== "string") return false;
@@ -61,8 +61,8 @@ function isValidDate(v) {
61
61
  }
62
62
 
63
63
  // parseDate(v, E, code, label) -> Date at UTC midnight. Anchored explicitly at UTC ("T00:00:00Z")
64
- // rather than left to `new Date("YYYY-MM-DD")`, so the instant does not shift with the host time
65
- // zone -- a freshness or rollback comparison must not depend on where it runs.
64
+ // instead of left to `new Date("YYYY-MM-DD")`, so the instant does not shift with the host time
65
+ // zone. A freshness or rollback comparison must not depend on where it runs.
66
66
  function parseDate(v, E, code, label) {
67
67
  if (!isValidDate(v)) throw E(code, (label || "the value") + " is not a valid RFC 3339 full-date (YYYY-MM-DD)");
68
68
  return new Date(v + "T00:00:00Z");