@blamejs/pki 0.6.14 → 0.6.16

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 (112) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/lib/acme.js +59 -815
  3. package/lib/asn1-der.js +7 -245
  4. package/lib/attrcert-sign.js +12 -260
  5. package/lib/attrcert-verify.js +1 -7
  6. package/lib/byte-reader.js +0 -8
  7. package/lib/byte-writer.js +0 -11
  8. package/lib/cbor-det.js +0 -54
  9. package/lib/cmc-build.js +15 -357
  10. package/lib/cmc-verify.js +6 -239
  11. package/lib/cmp-build.js +41 -317
  12. package/lib/cmp-session.js +73 -760
  13. package/lib/cmp-verify.js +32 -488
  14. package/lib/cms-compress.js +3 -32
  15. package/lib/cms-decrypt.js +29 -294
  16. package/lib/cms-encrypt.js +14 -234
  17. package/lib/cms-sign.js +29 -324
  18. package/lib/cms-verify.js +9 -500
  19. package/lib/composite-kem.js +2 -102
  20. package/lib/composite-sig.js +1 -77
  21. package/lib/constants.js +0 -249
  22. package/lib/crl-sign.js +9 -237
  23. package/lib/crl-verify.js +1 -10
  24. package/lib/crmf-sign.js +19 -198
  25. package/lib/crmf-verify.js +1 -12
  26. package/lib/csr-sign.js +3 -34
  27. package/lib/csr-verify.js +1 -12
  28. package/lib/ct.js +28 -253
  29. package/lib/edwards-point.js +10 -44
  30. package/lib/est.js +28 -764
  31. package/lib/framework-error.js +4 -182
  32. package/lib/guard-all.js +1 -58
  33. package/lib/guard-async.js +1 -36
  34. package/lib/guard-bytes.js +6 -650
  35. package/lib/guard-compress.js +5 -102
  36. package/lib/guard-crypto.js +1 -40
  37. package/lib/guard-der.js +2 -93
  38. package/lib/guard-encoding.js +2 -60
  39. package/lib/guard-header.js +1 -36
  40. package/lib/guard-identifier.js +7 -531
  41. package/lib/guard-intrinsic.js +1 -150
  42. package/lib/guard-json.js +10 -73
  43. package/lib/guard-limits.js +1 -77
  44. package/lib/guard-list.js +1 -49
  45. package/lib/guard-name.js +3 -209
  46. package/lib/guard-parsed.js +7 -335
  47. package/lib/guard-range.js +1 -71
  48. package/lib/guard-secret.js +1 -102
  49. package/lib/guard-text.js +1 -64
  50. package/lib/guard-time.js +1 -97
  51. package/lib/hpke.js +3 -105
  52. package/lib/http-digest.js +14 -142
  53. package/lib/http-retry-after.js +1 -47
  54. package/lib/http-transport.js +21 -131
  55. package/lib/inspect.js +30 -284
  56. package/lib/ip-utils.js +6 -47
  57. package/lib/jose.js +7 -142
  58. package/lib/key.js +2 -67
  59. package/lib/lint.js +9 -194
  60. package/lib/merkle.js +1 -27
  61. package/lib/mime.js +9 -82
  62. package/lib/ocsp-request-verify.js +1 -14
  63. package/lib/ocsp-verify.js +1 -82
  64. package/lib/ocsp.js +11 -169
  65. package/lib/oid.js +5 -337
  66. package/lib/path-validate.js +103 -1492
  67. package/lib/pbes2.js +2 -89
  68. package/lib/pkcs12-build.js +31 -379
  69. package/lib/pki-build.js +64 -290
  70. package/lib/rc2.js +2 -18
  71. package/lib/rfc3339.js +4 -27
  72. package/lib/scep.js +140 -286
  73. package/lib/schema-all.js +0 -122
  74. package/lib/schema-attrcert.js +4 -146
  75. package/lib/schema-c509.js +153 -802
  76. package/lib/schema-cmc.js +5 -220
  77. package/lib/schema-cmp.js +2 -223
  78. package/lib/schema-cms.js +13 -427
  79. package/lib/schema-crl.js +5 -68
  80. package/lib/schema-crmf.js +6 -167
  81. package/lib/schema-csr.js +1 -54
  82. package/lib/schema-csrattrs.js +2 -87
  83. package/lib/schema-engine.js +0 -152
  84. package/lib/schema-ocsp.js +0 -117
  85. package/lib/schema-pkcs12.js +2 -93
  86. package/lib/schema-pkcs8.js +3 -35
  87. package/lib/schema-pkix.js +24 -655
  88. package/lib/schema-smime.js +0 -54
  89. package/lib/schema-tsp.js +3 -99
  90. package/lib/schema-x509.js +4 -63
  91. package/lib/shbs.js +11 -95
  92. package/lib/sign-scheme.js +2 -97
  93. package/lib/sigstore.js +9 -169
  94. package/lib/sleep.js +2 -8
  95. package/lib/smime.js +15 -408
  96. package/lib/tls-cert-compress.js +0 -62
  97. package/lib/trust.js +19 -185
  98. package/lib/tsp-sign.js +16 -270
  99. package/lib/validator-all.js +1 -30
  100. package/lib/validator-attcert.js +2 -46
  101. package/lib/validator-cose.js +2 -153
  102. package/lib/validator-keydesc.js +1 -37
  103. package/lib/validator-sig.js +1 -46
  104. package/lib/validator-tls.js +1 -88
  105. package/lib/validator-tpm.js +19 -122
  106. package/lib/verify-seam.js +1 -24
  107. package/lib/webauthn-mds.js +3 -400
  108. package/lib/webauthn.js +12 -833
  109. package/lib/webcrypto.js +5 -485
  110. package/lib/x509-sign.js +4 -108
  111. package/package.json +1 -1
  112. package/sbom.cdx.json +6 -6
package/lib/shbs.js CHANGED
@@ -30,30 +30,19 @@ var constants = require("./constants");
30
30
  var ShbsError = frameworkError.ShbsError;
31
31
  function _err(code, message, cause) { return new ShbsError(code, message, cause); }
32
32
 
33
- // ---- big-endian serialization (RFC 8554 sec. 3.1: network byte order) --------
34
33
 
35
34
  function u8(x) { return Buffer.from([x & 0xff]); }
36
35
  function u16(x) { return Buffer.from([(x >> 8) & 0xff, x & 0xff]); }
37
36
  function u32(x) { return Buffer.from([(x >>> 24) & 0xff, (x >>> 16) & 0xff, (x >>> 8) & 0xff, x & 0xff]); }
38
37
 
39
- // Domain separators (RFC 8554 sec. 7.1). All exceed 264 (the max Winternitz
40
- // digit index i) so a security-string D can never collide with a chain-hash i,
41
- // which occupies the same 2-byte offset (sec. 9.1).
42
- var D_PBLC = 0x8080; // LM-OTS public-key finalize
43
- var D_MESG = 0x8181; // message hash
44
- var D_LEAF = 0x8282; // LMS leaf hash
45
- var D_INTR = 0x8383; // LMS interior node hash
38
+ var D_PBLC = 0x8080;
39
+ var D_MESG = 0x8181;
40
+ var D_LEAF = 0x8282;
41
+ var D_INTR = 0x8383;
46
42
 
47
- // ---- typecode registry (IANA Leighton-Micali Signatures; NIST SP 800-208) ----
48
- // LMS 0x05..0x18 : {m, h, hashFamily}. LM-OTS 0x01..0x10 : {n, w, hashFamily}.
49
- // p and ls are DERIVED from (n, w) per RFC 8554 sec. 4.1 / Appendix B so the
50
- // table stays data, not a switch, and a wrong hardcoded p cannot creep in.
51
43
 
52
44
  function _ilog2(x) { var r = 0; while (x > 1) { x = Math.floor(x / 2); r += 1; } return r; }
53
45
 
54
- // RFC 8554 Appendix B: u = ceil(8n/w) hash digits; v = the checksum digit count;
55
- // p = u + v total Winternitz digits; ls = the left-shift aligning the checksum's
56
- // significant bits into the digit positions coef reads.
57
46
  function _deriveWinternitz(n, w) {
58
47
  var u = Math.ceil((8 * n) / w);
59
48
  var v = Math.ceil((_ilog2((Math.pow(2, w) - 1) * u) + 1) / w);
@@ -63,16 +52,12 @@ function _deriveWinternitz(n, w) {
63
52
  var LMS_SETS = {};
64
53
  var LMOTS_SETS = {};
65
54
  (function seed() {
66
- var heights = [5, 10, 15, 20, 25]; // H5..H25 in typecode order
67
- var ws = [1, 2, 4, 8]; // W1..W8 in typecode order
68
- // LMS families in IANA order: SHA-256/M32 (0x05), SHA-256/M24 (0x0A),
69
- // SHAKE/M32 (0x0F), SHAKE/M24 (0x14).
55
+ var heights = [5, 10, 15, 20, 25];
56
+ var ws = [1, 2, 4, 8];
70
57
  [{ base: 0x05, m: 32, f: "sha256" }, { base: 0x0A, m: 24, f: "sha256" },
71
58
  { base: 0x0F, m: 32, f: "shake256" }, { base: 0x14, m: 24, f: "shake256" }].forEach(function (fam) {
72
59
  heights.forEach(function (h, i) { LMS_SETS[fam.base + i] = { code: fam.base + i, m: fam.m, h: h, hashFamily: fam.f }; });
73
60
  });
74
- // LM-OTS families: SHA-256/N32 (0x01), SHA-256/N24 (0x05), SHAKE/N32 (0x09),
75
- // SHAKE/N24 (0x0D).
76
61
  [{ base: 0x01, n: 32, f: "sha256" }, { base: 0x05, n: 24, f: "sha256" },
77
62
  { base: 0x09, n: 32, f: "shake256" }, { base: 0x0D, n: 24, f: "shake256" }].forEach(function (fam) {
78
63
  ws.forEach(function (w, i) {
@@ -93,8 +78,6 @@ function _lmotsSet(code) {
93
78
  return s;
94
79
  }
95
80
 
96
- // hashFamily + output length -> the n-byte hash of the concatenated inputs.
97
- // SHA-256 truncated to n (n=24 is SHA-256/192); SHAKE256 with an n-byte output.
98
81
  function _hash(family, n, parts) {
99
82
  var h;
100
83
  if (family === "shake256") h = nodeCrypto.createHash("shake256", { outputLength: n });
@@ -104,9 +87,6 @@ function _hash(family, n, parts) {
104
87
  return (family === "shake256" || n === 32) ? d : d.subarray(0, n);
105
88
  }
106
89
 
107
- // ---- bounded big-endian reader (bounds-before-slice; the ct.js TlsReader model
108
- // for a non-DER positional wire, kept shbs-local because shbs needs u32 typecodes
109
- // and fixed-n reads, not CT's length-prefixed vector helpers) ----------------
110
90
 
111
91
  function Reader(buf, code, label) { this.buf = buf; this.pos = 0; this.code = code; this.label = label; }
112
92
  Reader.prototype._need = function (k) {
@@ -119,10 +99,7 @@ Reader.prototype.take = function (k) { this._need(k); var b = this.buf.subarray(
119
99
  Reader.prototype.remaining = function () { return this.buf.length - this.pos; };
120
100
  Reader.prototype.atEnd = function () { return this.pos === this.buf.length; };
121
101
 
122
- // ---- Winternitz digit + checksum (RFC 8554 sec. 3.1.3 / sec. 4.4) ------------
123
102
 
124
- // coef(S, i, w): the i-th w-bit digit of S, big-endian WITHIN each byte (digit 0
125
- // is the high-order w bits of byte 0). RFC 8554 sec. 3.1.3.
126
103
  function _coef(S, i, w) {
127
104
  var idx = Math.floor((i * w) / 8);
128
105
  if (idx >= S.length) throw _err("shbs/bad-signature", "Winternitz coefficient index out of range");
@@ -130,9 +107,6 @@ function _coef(S, i, w) {
130
107
  return ((1 << w) - 1) & (S[idx] >> shift);
131
108
  }
132
109
 
133
- // Cksm(Q) per RFC 8554 sec. 4.4: sum over the u message digits of (2^w-1 - digit),
134
- // left-shifted by ls, as a 2-byte big-endian value. Computed over Q ALONE; the
135
- // coefficient index space in the chain walk spans Q || Cksm(Q).
136
110
  function _cksm(Q, set) {
137
111
  var w = set.w, sum = 0, u = Math.ceil((8 * set.n) / w);
138
112
  for (var i = 0; i < u; i++) sum += ((1 << w) - 1) - _coef(Q, i, w);
@@ -140,9 +114,6 @@ function _cksm(Q, set) {
140
114
  return u16(sum);
141
115
  }
142
116
 
143
- // ---- LM-OTS public-key candidate Kc (RFC 8554 Algorithm 4b) ------------------
144
- // I, q identify the LMS leaf; otsSet is resolved from the PUBLIC KEY's otstype
145
- // (the authority); C + y[] come from the signature. Returns the n-byte Kc.
146
117
 
147
118
  function _lmotsKc(otsSet, I, q, C, y, message) {
148
119
  var n = otsSet.n, w = otsSet.w, p = otsSet.p, f = otsSet.hashFamily;
@@ -153,21 +124,14 @@ function _lmotsKc(otsSet, I, q, C, y, message) {
153
124
  for (var i = 0; i < p; i++) {
154
125
  var a = _coef(Qc, i, w);
155
126
  var tmp = y[i];
156
- // Chain to 2^w - 1 total applications; the bound is EXCLUSIVE (last j = 2^w-2),
157
- // matching key generation (RFC 8554 sec. 4.5 vs Algorithm 4b step 3).
158
127
  for (var j = a; j < (1 << w) - 1; j++) tmp = _hash(f, n, [I, qb, u16(i), u8(j), tmp]);
159
128
  z.push(tmp);
160
129
  }
161
130
  return _hash(f, n, z);
162
131
  }
163
132
 
164
- // ---- LMS verification core (RFC 8554 Algorithm 6 / 6a) -----------------------
165
- // pubBytes = lmstype(4) || otstype(4) || I(16) || T[1](m); sigBytes an LMS
166
- // signature. Returns true iff the recomputed Merkle root equals T[1]. Throws
167
- // ShbsError on any structural fault; a well-formed-but-wrong signature -> false.
168
133
 
169
134
  function _lmsVerify(pubBytes, message, sigBytes) {
170
- // -- public key (the authority for both typecodes; RFC 8554 Algorithm 6) --
171
135
  if (pubBytes.length < 8) throw _err("shbs/bad-public-key", "LMS public key is shorter than 8 bytes");
172
136
  var pr = new Reader(pubBytes, "shbs/bad-public-key", "LMS public key");
173
137
  var lmsSet = _lmsSet(pr.u32());
@@ -177,81 +141,46 @@ function _lmsVerify(pubBytes, message, sigBytes) {
177
141
  var I = pr.take(16);
178
142
  var T1 = pr.take(m);
179
143
 
180
- // -- signature (RFC 8554 Algorithm 6a). q first, then the LM-OTS signature,
181
- // then the LMS typecode (after the LM-OTS sig), then the auth path. --
182
144
  if (sigBytes.length < 8) throw _err("shbs/bad-signature", "LMS signature is shorter than 8 bytes");
183
145
  var sr = new Reader(sigBytes, "shbs/bad-signature", "LMS signature");
184
146
  var q = sr.u32();
185
147
  var otsSigType = sr.u32();
186
- // The public key -- never the attacker-controlled signature -- is the authority
187
- // (downgrade defense): a signature whose OTS typecode does not equal the one the
188
- // public key commits to cannot verify against it. RFC 8554 Algorithm 6a checks
189
- // the typecode (step 2c, and 2g for the LMS type below) before the length (steps
190
- // 2d / 2i), and returns INVALID, a verification failure (false) and not a
191
- // structural error. So a mismatch is `false` at this point even for a blob too
192
- // short to be a complete signature: the mismatch is decidable once q + typecode
193
- // are read (the bounded reader already threw if those 8 bytes are absent). A
194
- // matching typecode with a truncated body still throws below (bounds-before-
195
- // slice). Re-sizing the body by the signature's own mismatched typecode, so as to
196
- // "fully validate before returning false", would violate this order and reject
197
- // a legitimate typecode-mutation test vector as malformed instead of INVALID.
198
148
  if (otsSigType !== otsSet.code) return false;
199
149
  var n = otsSet.n, p = otsSet.p;
200
150
  var C = sr.take(n);
201
151
  var y = [];
202
152
  for (var yi = 0; yi < p; yi++) y.push(sr.take(n));
203
153
  var sigLmsType = sr.u32();
204
- if (sigLmsType !== lmsSet.code) return false; // RFC 8554 Algorithm 6a step 2g -> INVALID
154
+ if (sigLmsType !== lmsSet.code) return false;
205
155
  var path = [];
206
156
  for (var pi = 0; pi < h; pi++) path.push(sr.take(m));
207
157
  if (!sr.atEnd()) throw _err("shbs/bad-signature", "LMS signature has " + sr.remaining() + " trailing byte(s)");
208
- // RFC 8554 Algorithm 6a step 2i: a leaf index q >= 2^h is INVALID, a
209
- // verification failure (false) and not a structural error. Checked here, after
210
- // the exact-length validation above (a truncated or trailing blob already threw
211
- // typed), against the registry height and never the blob. 2^h fits Number (h<=25).
212
158
  if (q >= Math.pow(2, h)) return false;
213
159
 
214
- // -- recompute the LM-OTS public-key candidate, then fold the Merkle path --
215
160
  var Kc = _lmotsKc(otsSet, I, q, C, y, message);
216
161
  var nodeNum = Math.pow(2, h) + q;
217
162
  var tmp = _hash(lmsSet.hashFamily, m, [I, u32(nodeNum), u16(D_LEAF), Kc]);
218
163
  for (var i2 = 0; nodeNum > 1; i2++) {
219
164
  var parent = Math.floor(nodeNum / 2);
220
165
  if (nodeNum % 2 === 1) {
221
- // odd node_num: the current node is a RIGHT child, sibling on the LEFT.
222
166
  tmp = _hash(lmsSet.hashFamily, m, [I, u32(parent), u16(D_INTR), path[i2], tmp]);
223
167
  } else {
224
168
  tmp = _hash(lmsSet.hashFamily, m, [I, u32(parent), u16(D_INTR), tmp, path[i2]]);
225
169
  }
226
170
  nodeNum = parent;
227
171
  }
228
- // The root comparison is over PUBLIC values (T1 is in the public key, tmp is
229
- // derived from public inputs -- no secret, hence the module's "no side-channel
230
- // surface" note), so constant time is not strictly required. It routes through
231
- // the shared crypto guard anyway, for one length-checked comparison primitive
232
- // across the hash-tree verifiers (pki.merkle folds its root the same way).
233
172
  return guard.crypto.constantTimeEqual(tmp, T1);
234
173
  }
235
174
 
236
- // Consume exactly one LMS public key from a reader (an HSS inner key), returning
237
- // its raw bytes; sized off the LMS typecode it leads with.
238
175
  function _consumeLmsPublicKey(r) {
239
176
  var start = r.pos;
240
- var code = r.u32(); // lmstype
241
- r.u32(); // otstype (validated when the key is verified)
177
+ var code = r.u32();
178
+ r.u32();
242
179
  var m = _lmsSet(code).m;
243
- r.take(16 + m); // I || T[1]
180
+ r.take(16 + m);
244
181
  return r.buf.subarray(start, r.pos);
245
182
  }
246
183
 
247
- // The exact byte length (12 + n*(p+1) + m*h) of an LMS signature verified under
248
- // `keyBytes` -- the AUTHORITATIVE public key for its level. The concatenated HSS
249
- // blob is split by the public key's typecodes, never the attacker-controlled
250
- // signature's own: so a typecode-mutated signature is sliced to the length the
251
- // key expects and then _lmsVerify returns false (a verification failure, RFC 8554
252
- // Algorithm 6a 2c/2g), consistent with verifyLms, instead of throwing on a
253
- // mis-sized slice. Bounds-guarded (no direct read that could escape as a raw
254
- // RangeError on a truncated inner key).
255
184
  function _lmsSigLen(keyBytes) {
256
185
  if (keyBytes.length < 8) throw _err("shbs/bad-public-key", "LMS public key is shorter than 8 bytes");
257
186
  var lmsSet = _lmsSet(keyBytes.readUInt32BE(0));
@@ -259,41 +188,28 @@ function _lmsSigLen(keyBytes) {
259
188
  return 12 + otsSet.n * (otsSet.p + 1) + lmsSet.m * lmsSet.h;
260
189
  }
261
190
 
262
- // ---- HSS verification (RFC 8554 sec. 6.3) ------------------------------------
263
191
 
264
192
  function _hssVerify(pubBytes, message, sigBytes) {
265
193
  if (pubBytes.length < 4) throw _err("shbs/bad-public-key", "HSS public key is shorter than 4 bytes");
266
194
  var pr = new Reader(pubBytes, "shbs/bad-public-key", "HSS public key");
267
195
  var L = pr.u32();
268
196
  if (L < 1 || L > constants.LIMITS.HSS_MAX_LEVELS) throw _err("shbs/bad-public-key", "HSS level count L=" + L + " is outside 1.." + constants.LIMITS.HSS_MAX_LEVELS);
269
- var topKey = pr.buf.subarray(pr.pos); // the top-level LMS public key -- its
270
- // structure (including an under-length blob) is validated by _lmsVerify below
271
- // (the < 8 and exact-24+m checks), so no direct read here that would bypass the
272
- // bounded reader and escape as a raw RangeError on a truncated key.
197
+ var topKey = pr.buf.subarray(pr.pos);
273
198
 
274
199
  var sr = new Reader(sigBytes, "shbs/bad-signature", "HSS signature");
275
200
  var Nspk = sr.u32();
276
- // The level-count gate, checked before parsing any component (RFC 8554 sec. 6.3).
277
201
  if (Nspk + 1 !== L) throw _err("shbs/bad-signature", "HSS Nspk+1 (" + (Nspk + 1) + ") does not equal the public-key level count L (" + L + ")");
278
202
 
279
203
  var key = topKey;
280
204
  for (var i = 0; i < Nspk; i++) {
281
- // Slice this level's LMS signature by the AUTHORITATIVE key's length (so a
282
- // typecode mismatch is a false verdict via _lmsVerify, not a mis-sized throw).
283
205
  var sig = sr.take(_lmsSigLen(key));
284
206
  var nextKey = _consumeLmsPublicKey(sr);
285
- // Each level signs the SERIALIZED next-level LMS public key; that recovered
286
- // key becomes the verification key for the level below (chain of trust).
287
207
  if (!_lmsVerify(key, nextKey, sig)) return false;
288
208
  key = nextKey;
289
209
  }
290
- // The final signature is the remainder; _lmsVerify parses it, applies the
291
- // public-key-authoritative typecode check (mismatch -> false), and rejects a
292
- // trailing / truncated remainder (bounds-before-slice + its own atEnd check).
293
210
  return _lmsVerify(key, message, sr.buf.subarray(sr.pos));
294
211
  }
295
212
 
296
- // ---- public surface ----------------------------------------------------------
297
213
 
298
214
  function _asBytes(x, label) { return guard.bytes.source(x, ShbsError, "shbs/bad-input", label); }
299
215
 
@@ -1,20 +1,7 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // Copyright (c) blamejs contributors
3
3
  "use strict";
4
- //
5
- // @internal -- no operator-facing namespace. The signature-scheme resolver + signer shared by
6
- // every producer that signs a raw preimage with a certificate's key: pki.cms.sign (SignerInfo),
7
- // pki.ocsp.sign (BasicOCSPResponse), and future X.509 / CRL issuance. It owns the single key ->
8
- // { signatureAlgorithm, WebCrypto import/sign params } dispatch across the whole algorithm set
9
- // (RSA-PKCS1 / RSA-PSS / ECDSA P-256/384/521 / Ed25519 / Ed448 / ML-DSA / SLH-DSA / composite
10
- // ML-DSA), so a new signer surface inherits the full registry, PQC included, instead of
11
- // re-deriving a partial, drifting copy (Hard rule #2: registry not switch; and no second signer).
12
- //
13
- // Error-parameterized like the guard / validator families: the caller passes its typed error
14
- // factory `E(kind, message, cause)` where kind is "bad-input" | "unsupported-algorithm", so every
15
- // domain keeps its own code (cms/bad-input vs ocsp/bad-input). The digest choice (a CMS
16
- // digestAlgorithm concern) is surfaced as `scheme.digest`; the caller that has a digestAlgorithm
17
- // field (CMS) builds it, the caller that signs raw bytes (OCSP) ignores it.
4
+ // @internal
18
5
 
19
6
  var asn1 = require("./asn1-der");
20
7
  var oid = require("./oid");
@@ -28,10 +15,6 @@ var compositeSig = require("./composite-sig");
28
15
  var b = asn1.build;
29
16
  function O(name) { return oid.byName(name); }
30
17
 
31
- // Captured at load. This module decides which algorithm a signature runs under and whether the
32
- // caller's key matches the certificate it claims, so an operation read at call time is one the
33
- // caller answers: a replaced case fold reports two different algorithm names equal, and a replaced
34
- // `Buffer.isBuffer` admits a key that is not bytes.
35
18
  var _BigInt = intrinsic.BigInt;
36
19
  var _String = intrinsic.String;
37
20
  var _filter = intrinsic.filter;
@@ -39,19 +22,12 @@ var _stringify = intrinsic.stringify;
39
22
  var _toUpperCase = intrinsic.toUpperCase;
40
23
  var _isBuffer = intrinsic.isBuffer;
41
24
  var _bufferFrom = intrinsic.bufferFrom;
42
- // Asks the value's internal slot rather than `instanceof`, which reads the `Uint8Array` global at
43
- // call time and answers about whichever constructor is bound then. It also settles the two cases
44
- // `instanceof` gets wrong on their own terms: a real typed array from another realm is one, and an
45
- // object merely built on the prototype is not.
46
25
  var _isU8 = intrinsic.types.isUint8Array;
47
26
 
48
27
  var HASH = { sha256: "SHA-256", sha384: "SHA-384", sha512: "SHA-512" };
49
28
  var NODE_DIGEST = { sha256: "sha256", sha384: "sha384", sha512: "sha512", shake128: "shake128", shake256: "shake256" };
50
29
  var PSS_SALT = { "SHA-256": 32, "SHA-384": 48, "SHA-512": 64 };
51
30
  var ECDSA_ALG = { sha256: "ecdsaWithSHA256", sha384: "ecdsaWithSHA384", sha512: "ecdsaWithSHA512" };
52
- // RSA-PKCS1 signatures fold the digest into a combined signature OID when there is no separate
53
- // digestAlgorithm field to carry it (X.509 / CRL / OCSP; RFC 5280 sec. 4.1.1.2, RFC 6960). CMS
54
- // SignerInfo has that field, so it signs under the bare rsaEncryption key OID instead.
55
31
  var RSA_PKCS1_SIG = { sha256: "sha256WithRSAEncryption", sha384: "sha384WithRSAEncryption", sha512: "sha512WithRSAEncryption" };
56
32
  var HASH_NAME_BY_OID = {};
57
33
  HASH_NAME_BY_OID[O("sha256")] = "sha256";
@@ -70,14 +46,7 @@ var MLDSA_SUITABLE_DIGEST = {
70
46
  "ML-DSA-65": { sha384: 1, sha512: 1, shake256: 1 },
71
47
  "ML-DSA-87": { sha512: 1, shake256: 1 },
72
48
  };
73
- // mldsaDigestSuitable(mlName, md) -- is `md` listed as meeting `mlName`'s security strength?
74
- // Both levels are asked for their OWN entries. The gate rejects on a falsy lookup, so a digest name
75
- // planted on Object.prototype answers for every name the row omits and a below-strength digest
76
- // passes the RFC 9882 sec. 3.3 check. This is the single home for that question: the signer and the
77
- // verifier both ask it, and a copy at either would let the two drift.
78
49
  // @enforced-by behavioral -- an own-property table read has no rename-proof shape distinct from a
79
- // legitimate one; the RED vectors (a below-strength digest still refused with its name planted on
80
- // Object.prototype, on both the sign and verify paths) are the guard.
81
50
  function mldsaDigestSuitable(mlName, md) {
82
51
  if (!intrinsic.hasOwn(MLDSA_SUITABLE_DIGEST, mlName)) return false;
83
52
  var row = MLDSA_SUITABLE_DIGEST[mlName];
@@ -90,29 +59,13 @@ var SLHDSA_BY_OID = {};
90
59
  ["shake-192s", "shake256"], ["shake-192f", "shake256"], ["shake-256s", "shake256"], ["shake-256f", "shake256"]
91
60
  ].forEach(function (r) { SLHDSA_BY_OID[O("id-slh-dsa-" + r[0])] = { wc: "SLH-DSA-" + r[0].toUpperCase(), digest: r[1] }; });
92
61
 
93
- // An AlgorithmIdentifier { OID } (absent parameters) or { OID, NULL }.
94
62
  function _algId(name, shape) { return shape === "null" ? b.sequence([b.oid(O(name)), b.nullValue()]) : b.sequence([b.oid(O(name))]); }
95
- // The RSASSA-PSS AlgorithmIdentifier: explicit SHA-2 hash, MGF1 keyed to it, hash-length salt,
96
- // default trailerField omitted (RFC 4055).
97
63
  function _pssAlgId(digestName) {
98
64
  var hashAlg = b.sequence([b.oid(O(digestName)), b.nullValue()]);
99
65
  var mgf = b.sequence([b.oid(O("mgf1")), hashAlg]);
100
66
  var params = b.sequence([b.explicit(0, hashAlg), b.explicit(1, mgf), b.explicit(2, b.integer(_BigInt(PSS_SALT[HASH[digestName]])))]);
101
67
  return b.sequence([b.oid(O("rsassaPss")), params]);
102
68
  }
103
- // The hash an id-RSASSA-PSS SPKI restricts its key to, or null when it restricts none.
104
- //
105
- // RFC 4055 sec. 3.1 draws the line at whether the parameters are THERE: "if present, the parameters
106
- // field MUST contain RSASSA-PSS-params", and "if RSASSA-PSS-params is present, the certificate user
107
- // MUST perform those operations using the one-way hash function ... identified in the ...
108
- // parameters". Absent parameters therefore restrict nothing, and null says so.
109
- //
110
- // PRESENT parameters are a restriction even where they look empty. `hashAlgorithm` is
111
- // `[0] HashAlgorithm DEFAULT sha1Identifier`, so a params SEQUENCE that omits it names SHA-1 --
112
- // it does not decline to name anything. Reading the omission as "no restriction" is the fail-open
113
- // that matters here: it turns a key its own certificate confines to SHA-1 into one that will verify
114
- // a SHA-512 signature. And parameters that are present but unreadable are a restriction this code
115
- // cannot honor, which is not the same as no restriction either, so they are refused.
116
69
  function _pssHashFromSpki(cert, E) {
117
70
  var params = cert.subjectPublicKeyInfo.algorithm.parameters;
118
71
  if (params == null) return null;
@@ -123,8 +76,6 @@ function _pssHashFromSpki(cert, E) {
123
76
  throw E("unsupported-algorithm", "the id-RSASSA-PSS key parameters are not an RSASSA-PSS-params SEQUENCE (RFC 4055 sec. 3.1)");
124
77
  }
125
78
  var hashField = _filter(node.children, function (c) { return c.tagClass === "context" && c.tagNumber === 0; })[0];
126
- // DEFAULT sha1Identifier -- an omitted hashAlgorithm names SHA-1, which this toolkit does not
127
- // sign or verify with, so it is reported as the pin it is and refused by the caller's own table.
128
79
  if (!hashField) return "sha1";
129
80
  if (!hashField.children || !hashField.children[0] || !hashField.children[0].children) {
130
81
  throw E("unsupported-algorithm", "the id-RSASSA-PSS key parameters carry a malformed hashAlgorithm");
@@ -139,28 +90,14 @@ function _pssHashFromSpki(cert, E) {
139
90
  return name;
140
91
  }
141
92
 
142
- // @internal -- the WebCrypto hash name an id-RSASSA-PSS SPKI pins, or null when it pins none.
143
- // Verifiers need the same restriction the signer above honors: a key whose certificate says
144
- // SHA-256 must not be handed a SHA-512 signature to check, and reading the pin in two places is
145
- // how the two directions come to disagree. Throws through the caller's E on a hash this toolkit
146
- // does not implement, so an unreadable restriction is never treated as no restriction.
93
+ // @internal
147
94
  function pssSpkiPinnedHash(cert, E) {
148
95
  var d = _pssHashFromSpki(cert, E);
149
96
  if (!d) return null;
150
- // A pin this toolkit has no WebCrypto hash for (SHA-1, which RSASSA-PSS-params names by DEFAULT)
151
- // is still a pin. Returning undefined here would hand the caller a falsy value it reads as
152
- // "unrestricted", which is the same fail-open the DEFAULT reading above exists to close, one
153
- // layer up.
154
97
  if (!HASH[d]) throw E("unsupported-algorithm", "the id-RSASSA-PSS key is restricted to " + d + ", which this toolkit does not verify with");
155
98
  return HASH[d];
156
99
  }
157
100
 
158
- // resolveSignScheme(cert, so, noSignedAttrs, E) -> the signature scheme from the signer cert's
159
- // public-key algorithm + per-signer opts (so.digestAlgorithm / so.pss / so.combinedRsaSig, the
160
- // last folds the digest into a combined RSA signature OID for a caller with no digestAlgorithm
161
- // field). Returns
162
- // { composite?, digest, digestAlgId, sigAlgId, imp?, sign?, ecdsaDer?, coordLen? }. `digest`/
163
- // `digestAlgId` matter only to a caller with a CMS digestAlgorithm field; OCSP ignores them.
164
101
  function resolveSignScheme(cert, so, noSignedAttrs, E) {
165
102
  so = so || {};
166
103
  var alg = cert.subjectPublicKeyInfo.algorithm;
@@ -217,14 +154,9 @@ function resolveSignScheme(cert, so, noSignedAttrs, E) {
217
154
  throw E("unsupported-algorithm", "unsupported signer key algorithm " + keyOid);
218
155
  }
219
156
 
220
- // A CryptoKey's algorithm must match the resolved scheme (name / hash / curve).
221
157
  function _assertKeyMatchesScheme(key, imp, E) {
222
158
  var ka = key.algorithm || {};
223
- // WebCrypto algorithm names are ASCII-case-insensitive (RSASSA-PKCS1-v1_5 and RSASSA-PKCS1-V1_5 are one
224
- // algorithm), so compare case-folded; a genuine mismatch (e.g. RSASSA-PKCS1-v1_5 vs ECDSA) still fails.
225
159
  if (_toUpperCase(_String(ka.name)) !== _toUpperCase(_String(imp.name))) {
226
- // A WebCrypto RSA-PSS CryptoKey is bound to PSS and cannot produce the default RSASSA-PKCS1-v1_5
227
- // signature (which a raw PKCS#8 RSA key can), so name the fix rather than only the mismatch.
228
160
  var hint = (_toUpperCase(_String(ka.name)) === "RSA-PSS" && _toUpperCase(_String(imp.name)) === "RSASSA-PKCS1-V1_5")
229
161
  ? "; this CryptoKey is bound to RSASSA-PSS -- pass opts.pss to sign with RSASSA-PSS" : "";
230
162
  throw E("bad-input", "the signer CryptoKey algorithm (" + ka.name + ") does not match the certificate's key algorithm (" + imp.name + ")" + hint);
@@ -233,9 +165,6 @@ function _assertKeyMatchesScheme(key, imp, E) {
233
165
  if (imp.namedCurve && ka.namedCurve !== imp.namedCurve) throw E("bad-input", "the signer CryptoKey curve (" + ka.namedCurve + ") does not match the certificate curve (" + imp.namedCurve + ")");
234
166
  }
235
167
  function _normPkcs8(k, label, E) {
236
- // A caller's own Buffer is borrowed, not copied, the same rule _importKey states below. Every
237
- // other form is copied, and neither copy is duplicated further, so a private key never gains a
238
- // plaintext duplicate this module cannot account for.
239
168
  if (_isBuffer(k)) return guard.bytes.view(k, E, "bad-input", label);
240
169
  if (_isU8(k)) return guard.bytes.snapshot(k, E, "bad-input", label);
241
170
  if (typeof k === "string") { try { return pkcs8.pemDecode(k); } catch (e) { throw E("bad-input", label + " PEM could not be decoded", e); } }
@@ -248,27 +177,13 @@ function _normCompositeKeys(key, comp, E) {
248
177
  return { mldsa: _normPkcs8(key.mldsa, "the composite ML-DSA component key", E), trad: _normPkcs8(key.trad, "the composite traditional component key", E) };
249
178
  }
250
179
  function _importKey(key, imp, E) {
251
- // A WebCrypto CryptoKey is detected structurally (a `.type` string + an `.algorithm` object, and not
252
- // a byte view), never by instanceof (cross-realm / a second installed copy) or util.types.isCryptoKey
253
- // (false for this engine's own keys). The gate is broader than webcrypto.isCryptoKeyLike on purpose:
254
- // an incomplete CryptoKey-shaped object (e.g. one missing its `usages`) still routes here so adoptKey
255
- // reports the specific reason (usages / not-exportable) rather than the generic byte-form message. It
256
- // signs, so it must be a PRIVATE key: a public or secret CryptoKey cannot produce a signature and is
257
- // refused precisely.
258
180
  if (key && typeof key === "object" && !_isBuffer(key) && !_isU8(key) && typeof key.type === "string" && key.algorithm != null) {
259
181
  if (key.type !== "private") {
260
182
  throw E("bad-input", "the signing key is a WebCrypto CryptoKey of type " + _stringify(key.type) + "; a signing key must be a private key (type \"private\")");
261
183
  }
262
184
  _assertKeyMatchesScheme(key, imp, E);
263
- // A CryptoKey created outside this engine signs here too -- it is re-imported through this engine,
264
- // because it carries none of the key material this engine signs with. One whose material this
265
- // process cannot reach is refused with that as the reason.
266
185
  return webcrypto.adoptKey(key, imp, ["sign"], E, "bad-input");
267
186
  }
268
- // A Uint8Array is copied here and a PEM string is decoded here, so both leave a second copy of a
269
- // private key that nothing else can reach, wiped once the engine has imported it, which is the
270
- // only thing that reads it. A caller's own Buffer is passed through untouched: they hold a live
271
- // reference and will use it again, so clearing it would destroy their key.
272
187
  var der, owned = false;
273
188
  if (_isBuffer(key)) der = guard.bytes.view(key, E, "bad-input", "the signer private key");
274
189
  else if (_isU8(key)) { der = guard.bytes.snapshot(key, E, "bad-input", "the signer private key"); owned = true; }
@@ -277,13 +192,10 @@ function _importKey(key, imp, E) {
277
192
  catch (e) { throw E("bad-input", "the signer PEM private key could not be decoded", e); }
278
193
  owned = true;
279
194
  } else if (intrinsic.types.isKeyObject(key)) {
280
- // A node:crypto KeyObject carries a `.type` but not the WebCrypto surface (algorithm / usages), so
281
- // it fails the CryptoKey gate above and would otherwise land on the generic message -- name it precisely.
282
195
  throw E("bad-input", "the signing key is a node:crypto KeyObject, not a WebCrypto CryptoKey; pass a CryptoKey (e.g. from pki.key.generate) or export it to a PKCS#8 DER Buffer or PEM string");
283
196
  } else throw E("bad-input", "a signer key must be a WebCrypto CryptoKey, a PKCS#8 DER Buffer, or a PKCS#8 PEM string");
284
197
  var imported = subtle.importKey("pkcs8", der, imp, false, ["sign"]);
285
198
  if (!owned) return imported;
286
- // Wiped on the reject path too: a malformed key is not a way to leave the copy in memory.
287
199
  return imported.then(function (k) {
288
200
  guard.secret.zeroize(der, E, "bad-input", "the signer private-key copy");
289
201
  return k;
@@ -293,9 +205,6 @@ function _importKey(key, imp, E) {
293
205
  });
294
206
  }
295
207
 
296
- // signOverTbs(scheme, key, signedBytes, E) -> Promise<Buffer> the raw signature over signedBytes.
297
- // The classical path imports the key + signs (re-encoding ECDSA to canonical DER); the composite
298
- // path signs both component keys and returns mldsaSig || tradSig (composite-sig.js owns it).
299
208
  function signOverTbs(scheme, key, signedBytes, E) {
300
209
  if (scheme.composite) {
301
210
  return compositeSig.compositeSign(scheme.composite, _normCompositeKeys(key, scheme.composite, E), signedBytes).then(function (sig) { return _bufferFrom(sig); });
@@ -309,10 +218,6 @@ function signOverTbs(scheme, key, signedBytes, E) {
309
218
  });
310
219
  }
311
220
 
312
- // MLDSA_SUITABLE_DIGEST + SLHDSA_BY_OID are the RFC 9882 sec. 3.3 / RFC 9814 sec. 4 per-parameter-
313
- // set digest policy, exported so the VERIFY side (cms-verify) shares the EXACT same table this
314
- // resolver signs under -- a digest accepted on sign is precisely the set accepted on verify, with
315
- // no drift between the two.
316
221
  module.exports = {
317
222
  resolveSignScheme: resolveSignScheme,
318
223
  pssSpkiPinnedHash: pssSpkiPinnedHash,