@blamejs/pki 0.6.13 → 0.6.15

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 (114) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/README.md +1 -1
  3. package/index.js +1 -1
  4. package/lib/acme.js +59 -815
  5. package/lib/asn1-der.js +7 -245
  6. package/lib/attrcert-sign.js +12 -260
  7. package/lib/attrcert-verify.js +1 -7
  8. package/lib/byte-reader.js +0 -8
  9. package/lib/byte-writer.js +0 -11
  10. package/lib/cbor-det.js +0 -54
  11. package/lib/cmc-build.js +15 -357
  12. package/lib/cmc-verify.js +6 -239
  13. package/lib/cmp-build.js +41 -317
  14. package/lib/cmp-session.js +73 -760
  15. package/lib/cmp-verify.js +32 -488
  16. package/lib/cms-compress.js +3 -32
  17. package/lib/cms-decrypt.js +29 -294
  18. package/lib/cms-encrypt.js +14 -234
  19. package/lib/cms-sign.js +29 -324
  20. package/lib/cms-verify.js +9 -500
  21. package/lib/composite-kem.js +2 -102
  22. package/lib/composite-sig.js +1 -77
  23. package/lib/constants.js +0 -249
  24. package/lib/crl-sign.js +9 -237
  25. package/lib/crl-verify.js +1 -10
  26. package/lib/crmf-sign.js +19 -198
  27. package/lib/crmf-verify.js +1 -12
  28. package/lib/csr-sign.js +3 -34
  29. package/lib/csr-verify.js +1 -12
  30. package/lib/ct.js +28 -253
  31. package/lib/edwards-point.js +10 -44
  32. package/lib/est.js +28 -764
  33. package/lib/framework-error.js +4 -182
  34. package/lib/guard-all.js +1 -58
  35. package/lib/guard-async.js +1 -36
  36. package/lib/guard-bytes.js +6 -650
  37. package/lib/guard-compress.js +5 -102
  38. package/lib/guard-crypto.js +1 -40
  39. package/lib/guard-der.js +2 -93
  40. package/lib/guard-encoding.js +2 -60
  41. package/lib/guard-header.js +1 -36
  42. package/lib/guard-identifier.js +7 -531
  43. package/lib/guard-intrinsic.js +1 -150
  44. package/lib/guard-json.js +10 -73
  45. package/lib/guard-limits.js +1 -77
  46. package/lib/guard-list.js +1 -49
  47. package/lib/guard-name.js +3 -209
  48. package/lib/guard-parsed.js +7 -335
  49. package/lib/guard-range.js +1 -71
  50. package/lib/guard-secret.js +1 -102
  51. package/lib/guard-text.js +1 -64
  52. package/lib/guard-time.js +1 -97
  53. package/lib/hpke.js +3 -105
  54. package/lib/http-digest.js +14 -142
  55. package/lib/http-retry-after.js +1 -47
  56. package/lib/http-transport.js +21 -131
  57. package/lib/inspect.js +30 -284
  58. package/lib/ip-utils.js +6 -47
  59. package/lib/jose.js +7 -142
  60. package/lib/key.js +2 -67
  61. package/lib/lint.js +9 -194
  62. package/lib/merkle.js +1 -27
  63. package/lib/mime.js +9 -82
  64. package/lib/ocsp-request-verify.js +1 -14
  65. package/lib/ocsp-verify.js +1 -82
  66. package/lib/ocsp.js +11 -169
  67. package/lib/oid.js +5 -337
  68. package/lib/path-validate.js +103 -1492
  69. package/lib/pbes2.js +2 -89
  70. package/lib/pkcs12-build.js +31 -379
  71. package/lib/pki-build.js +64 -290
  72. package/lib/rc2.js +2 -18
  73. package/lib/rfc3339.js +4 -27
  74. package/lib/scep.js +59 -233
  75. package/lib/schema-all.js +0 -122
  76. package/lib/schema-attrcert.js +4 -146
  77. package/lib/schema-c509.js +153 -802
  78. package/lib/schema-cmc.js +5 -220
  79. package/lib/schema-cmp.js +2 -223
  80. package/lib/schema-cms.js +13 -427
  81. package/lib/schema-crl.js +5 -68
  82. package/lib/schema-crmf.js +6 -167
  83. package/lib/schema-csr.js +1 -54
  84. package/lib/schema-csrattrs.js +2 -87
  85. package/lib/schema-engine.js +0 -152
  86. package/lib/schema-ocsp.js +0 -117
  87. package/lib/schema-pkcs12.js +2 -93
  88. package/lib/schema-pkcs8.js +3 -35
  89. package/lib/schema-pkix.js +24 -655
  90. package/lib/schema-smime.js +0 -54
  91. package/lib/schema-tsp.js +3 -99
  92. package/lib/schema-x509.js +4 -63
  93. package/lib/shbs.js +11 -95
  94. package/lib/sign-scheme.js +2 -97
  95. package/lib/sigstore.js +9 -169
  96. package/lib/sleep.js +2 -8
  97. package/lib/smime.js +15 -408
  98. package/lib/tls-cert-compress.js +0 -62
  99. package/lib/trust.js +19 -185
  100. package/lib/tsp-sign.js +16 -270
  101. package/lib/validator-all.js +1 -30
  102. package/lib/validator-attcert.js +2 -46
  103. package/lib/validator-cose.js +2 -153
  104. package/lib/validator-keydesc.js +1 -37
  105. package/lib/validator-sig.js +1 -46
  106. package/lib/validator-tls.js +1 -88
  107. package/lib/validator-tpm.js +19 -122
  108. package/lib/verify-seam.js +1 -24
  109. package/lib/webauthn-mds.js +3 -400
  110. package/lib/webauthn.js +12 -833
  111. package/lib/webcrypto.js +5 -485
  112. package/lib/x509-sign.js +4 -108
  113. package/package.json +1 -1
  114. package/sbom.cdx.json +6 -6
package/lib/ocsp.js CHANGED
@@ -25,14 +25,14 @@
25
25
 
26
26
  var nodeCrypto = require("crypto");
27
27
  var asn1 = require("./asn1-der");
28
- var pkiBuild = require("./pki-build"); // reqDenseArray: reject a sparse/nullish caller array as a typed ocsp/bad-input
28
+ var pkiBuild = require("./pki-build");
29
29
  var oid = require("./oid");
30
30
  var x509 = require("./schema-x509");
31
31
  var ocspSchema = require("./schema-ocsp");
32
32
  var pathValidate = require("./path-validate");
33
33
  var signScheme = require("./sign-scheme");
34
34
  var ocspVerify = require("./ocsp-verify");
35
- var ocspRequestVerify = require("./ocsp-request-verify"); // path-validate (required above) injects its signature engine into this seam at load
35
+ var ocspRequestVerify = require("./ocsp-request-verify");
36
36
  var webcrypto = require("./webcrypto");
37
37
  var subtle = webcrypto.webcrypto.subtle;
38
38
  var guard = require("./guard-all");
@@ -43,15 +43,9 @@ var OcspError = frameworkError.OcspError;
43
43
  var b = asn1.build;
44
44
  function O(name) { return oid.byName(name); }
45
45
  function _err(code, message, cause) { return new OcspError(code, message, cause); }
46
- // The domain error factory the shared sign-scheme resolver/signer throws through (kind ->
47
- // ocsp/<kind>), so its faults keep the ocsp/* codes.
48
46
  function _signE(kind, message, cause) { return new OcspError("ocsp/" + kind, message, cause); }
49
47
 
50
- // CertID / responder-ID hash algorithm name -> WebCrypto digest name. SHA-1 is the RFC 5019 interop
51
- // default for the CertID identity hash (not a signature; collision resistance is irrelevant to the
52
- // lookup, so SHAttered does not bar it, the same deliberate split path-validate makes).
53
48
  var HASH_WC = { sha1: "SHA-1", sha256: "SHA-256", sha384: "SHA-384", sha512: "SHA-512" };
54
- // A CRLReason name -> its enumerated value (RFC 5280 sec. 5.3.1), reverse of pki.C.NAMES.CRL_REASON.
55
49
  var REASON_CODE = {};
56
50
  Object.keys(constants.NAMES.CRL_REASON).forEach(function (k) { REASON_CODE[constants.NAMES.CRL_REASON[k]] = parseInt(k, 10); });
57
51
  var OID_OCSP_NONCE = O("ocspNonce");
@@ -61,10 +55,6 @@ var OID_EXTENDED_REVOKE = O("ocspExtendedRevoke");
61
55
  function _digest(wcHash, buf) { return subtle.digest(wcHash, buf).then(function (h) { return Buffer.from(h); }); }
62
56
  function _certOf(arg, what) {
63
57
  var label = what || "a certificate";
64
- // Re-derived from the bytes its parser read, like every other certificate door. A CertID is an
65
- // IDENTITY -- the issuer's name and key hashed, plus the serial -- so a certificate that names one
66
- // identity while carrying another's signed bytes would have this verb ask about, or answer for,
67
- // a certificate nobody issued.
68
58
  return guard.parsed.acceptDerived(arg, "certificate", function (bytes) {
69
59
  var der = bytes;
70
60
  if (typeof bytes === "string") {
@@ -73,10 +63,6 @@ function _certOf(arg, what) {
73
63
  try { return x509.parse(der); } catch (e) { throw _err("ocsp/bad-input", label + " is not a well-formed X.509 certificate", e); }
74
64
  }, _err, "ocsp/bad-input", label);
75
65
  }
76
- // The response, always parsed from the bytes the caller handed over. See verify's own comment for
77
- // why an object cannot be accepted here: the three parts of a signature check would come from three
78
- // independently-chosen properties. The claim is detected on any of the parsed-response fields, so a
79
- // caller who passes one is told what happened, never given a byte-parse fault about its type.
80
66
  var _RESPONSE_CLAIM = ["responseStatus", "basicResponse", "tbsResponseDataBytes"];
81
67
  function _responseFromBytes(response) {
82
68
  return guard.parsed.fromTrustedSource(response, "ocspResponse", _RESPONSE_CLAIM, function (bytes) {
@@ -90,17 +76,13 @@ function _toDer(input, what) {
90
76
  if (typeof input === "string") { try { return ocspSchema.pemDecode(input); } catch (e) { throw _err("ocsp/bad-input", (what || "input") + " PEM could not be decoded", e); } }
91
77
  throw _err("ocsp/bad-input", (what || "input") + " must be a DER Buffer, Uint8Array, or PEM string");
92
78
  }
93
- // The subjectPublicKey BIT STRING value an issuerKeyHash / byKey KeyHash hashes over.
94
79
  function _keyValue(spkiDer) { return ocspVerify.ocspKeyValue(spkiDer); }
95
- // A non-critical id-pkix-ocsp-nonce Extension carrying the raw nonce (RFC 9654): the extnValue
96
- // OCTET STRING wraps the Nonce ::= OCTET STRING.
97
80
  function _nonceExt(nonceBytes) { return b.sequence([b.oid(OID_OCSP_NONCE), b.octetString(b.octetString(nonceBytes))]); }
98
81
  function _assertNonce(nonceBytes) {
99
82
  if (!Buffer.isBuffer(nonceBytes) || nonceBytes.length < 1 || nonceBytes.length > 128) {
100
83
  throw _err("ocsp/bad-input", "a nonce must be a Buffer of 1..128 octets (RFC 9654 sec. 2.1)");
101
84
  }
102
85
  }
103
- // The CertID SEQUENCE for target `cert` under `issuer`, hashing under `hashName` (RFC 6960 sec. 4.1.1).
104
86
  function _buildCertID(cert, issuer, hashName) {
105
87
  var wc = HASH_WC[hashName];
106
88
  if (!wc) throw _err("ocsp/bad-input", "unsupported CertID hashAlgorithm " + JSON.stringify(hashName) + " (sha1 / sha256 / sha384 / sha512)");
@@ -108,8 +90,6 @@ function _buildCertID(cert, issuer, hashName) {
108
90
  _digest(wc, issuer.subject.bytes),
109
91
  _digest(wc, _keyValue(issuer.subjectPublicKeyInfo.bytes)),
110
92
  ]).then(function (hashes) {
111
- // SHA-1 carries NULL parameters (RFC 3279); a SHA-2 digest AlgorithmIdentifier omits them
112
- // (RFC 5754 sec. 2). The CertID matcher keys on the OID and ignores parameters either way.
113
93
  var hashAlgId = hashName === "sha1" ? b.sequence([b.oid(O(hashName)), b.nullValue()]) : b.sequence([b.oid(O(hashName))]);
114
94
  return b.sequence([
115
95
  hashAlgId,
@@ -154,19 +134,11 @@ function _buildCertID(cert, issuer, hashName) {
154
134
  * var der = await pki.ocsp.buildRequest({ cert: leafDer, issuer: caDer }, { nonce: true });
155
135
  */
156
136
  function buildRequest(query, opts) {
157
- // Both arguments copied at entry and released when the call settles -- see the note on the same
158
- // call in x509-sign. The request is assembled across several promise turns (each CertID is a
159
- // hash), and opts carries byte fields -- the nonce and requestorName -- that reach the encoding.
160
137
  return guard.bytes.fixedCall(OcspError, "ocsp/bad-input", [
161
138
  [query, "the OCSP request query"], [opts, "pki.ocsp.buildRequest options"],
162
139
  ], _buildRequest);
163
140
  }
164
141
 
165
- // Every option pki.ocsp.buildRequest reads. `nonce` earns the named refusal: it is the
166
- // anti-replay binding, and a misspelling ships a request with no nonce at all, which the
167
- // responder answers happily and the caller cannot later bind to.
168
- // Taken from this verb's own @opts block, which is the published contract; a grep of the
169
- // function body misses `pem`, read by the shared emitter rather than here.
170
142
  var _BUILD_REQUEST_OPTS = {
171
143
  hashAlgorithm: 1, nonce: 1, pem: 1, profile: 1, requestorName: 1, signer: 1
172
144
  };
@@ -178,8 +150,6 @@ function _buildRequest(query, opts) {
178
150
  var lightweight = opts.profile === "lightweight";
179
151
  var hashName = opts.hashAlgorithm || "sha1";
180
152
  if (lightweight && hashName !== "sha1") throw _err("ocsp/bad-input", "the lightweight profile requires a SHA-1 CertID (RFC 5019 sec. 2.1.1)");
181
- // A batch array is density-checked before the map below (which would otherwise skip a hole and reach
182
- // Promise.all/b.sequence with an undefined); a single {cert,issuer} wraps to a dense one-element array.
183
153
  var queries = Array.isArray(query) ? pkiBuild.reqDenseArray(query, "queries", _err, "ocsp/bad-input") : [query];
184
154
  if (!queries.length) throw _err("ocsp/bad-input", "buildRequest needs at least one { cert, issuer } query");
185
155
  if (lightweight && queries.length !== 1) throw _err("ocsp/bad-input", "the lightweight profile permits exactly one Request (RFC 5019 sec. 2.1.1)");
@@ -200,14 +170,13 @@ function _buildRequest(query, opts) {
200
170
  })).then(function (requests) {
201
171
  var tbsChildren = [];
202
172
  if (opts.requestorName) {
203
- // requestorName [1] EXPLICIT GeneralName; a Name is carried as directoryName [4].
204
173
  var nameDer = _nameDer(opts.requestorName);
205
174
  tbsChildren.push(b.explicit(1, b.contextConstructed(4, nameDer)));
206
175
  }
207
176
  tbsChildren.push(b.sequence(requests));
208
177
  var reqExts = [];
209
178
  if (opts.nonce) {
210
- var nonceBytes = opts.nonce === true ? nodeCrypto.randomBytes(32) : opts.nonce; // >= 32 octets, CSPRNG (RFC 9654 sec. 2.1)
179
+ var nonceBytes = opts.nonce === true ? nodeCrypto.randomBytes(32) : opts.nonce;
211
180
  _assertNonce(nonceBytes);
212
181
  reqExts.push(_nonceExt(nonceBytes));
213
182
  }
@@ -226,13 +195,9 @@ function _buildRequest(query, opts) {
226
195
  function _emitReq(der, opts) { return opts.pem ? ocspSchema.pemEncode(der, "OCSP REQUEST") : der; }
227
196
  function _nameDer(name) {
228
197
  if (guard.bytes.isByteSource(name)) return guard.bytes.snapshotSource(name, OcspError, "ocsp/bad-input", "requestorName");
229
- if (name && name.bytes) return name.bytes; // a parsed Name
198
+ if (name && name.bytes) return name.bytes;
230
199
  throw _err("ocsp/bad-input", "requestorName must be a DER Name BufferSource or a parsed Name");
231
200
  }
232
- // The RAW DER of a certificate that will be embedded verbatim (the responder cert in a
233
- // BasicOCSPResponse, the signer cert in a signed OCSPRequest). It must be supplied as bytes:
234
- // a parsed certificate does not retain its full DER encoding, and re-encoding it would risk
235
- // byte drift from the signed original, so a parsed cert is rejected rather than reconstructed.
236
201
  function _normCertDer(cert, what) {
237
202
  if (guard.bytes.isByteSource(cert)) return guard.bytes.snapshotSource(cert, OcspError, "ocsp/bad-input", what || "a certificate");
238
203
  if (typeof cert === "string") { try { return x509.pemDecode(cert); } catch (e) { throw _err("ocsp/bad-input", (what || "a certificate") + " PEM could not be decoded", e); } }
@@ -279,19 +244,12 @@ function _normCertDer(cert, what) {
279
244
  * { responderID: "byName", responses: [{ cert: leafDer, issuer: caDer, status: "good" }] },
280
245
  * { cert: responderCertDer, key: responderPkcs8 });
281
246
  */
282
- // Documented `-> Promise`, so a fault leaves as a REJECTION (guard-async); the checks stay
283
- // synchronous because they read the responder's mutable cert and key.
284
247
  function sign(responseData, responder, opts) {
285
- // Every caller-owned argument copied at entry and released when the call settles -- see the note
286
- // on the same call in x509-sign.
287
248
  return guard.bytes.fixedCall(OcspError, "ocsp/bad-input", [
288
249
  [responseData, "the OCSP responseData"], [responder, "the responder"], [opts, "pki.ocsp.sign options"],
289
250
  ], _sign);
290
251
  }
291
252
 
292
- // Every option pki.ocsp.sign reads. `nonce` here is the request nonce this response echoes back.
293
- // A misspelling emits a response that answers nobody's request, and the client that sent a nonce
294
- // then reads it as unbound.
295
253
  var _SIGN_OPTS = { embedCert: 1, extendedRevoke: 1, nonce: 1, pem: 1 };
296
254
 
297
255
  function _sign(responseData, responder, opts) {
@@ -302,27 +260,8 @@ function _sign(responseData, responder, opts) {
302
260
  if (!responder || responder.cert == null || responder.key == null) throw _err("ocsp/bad-input", "a responder must be { cert, key }");
303
261
  var respCertDer = _normCertDer(responder.cert, "the responder certificate");
304
262
  var respCert = _certOf(respCertDer, "the responder certificate");
305
- // Read with the certificate, not after the awaits below. The ResponderID and the
306
- // embedded certificate are both fixed from `respCert` here; the signature is made
307
- // several promise turns later, so a `responder` whose key was replaced in between
308
- // would produce a response naming one responder and signed by another key.
309
- //
310
- // Capturing the REFERENCE only closes half of that: it stops `responder.key = other`,
311
- // but PKCS#8 arrives as caller-owned BYTES and a composite key as a caller-owned
312
- // { mldsa, trad } object, either of which can be rewritten in place across the same
313
- // gap with the same result -- a response carrying this responder's ID and certificate
314
- // over a signature made by a different key, which no relying party can verify. So the
315
- // material itself is snapshotted here, not just the binding to it. A CryptoKey is
316
- // opaque and a PEM string is immutable, so both are already safe by reference.
317
263
  var ownedKeyBytes = [];
318
264
  var responderKey = _snapshotSignerKey(responder.key, ownedKeyBytes);
319
- // The copy is cleared on the failing path as well as the succeeding one -- a wrong or
320
- // malformed key is the case an attacker can force, so a success-only wipe would keep the
321
- // secret exactly when it matters. The wipe covers the WHOLE window the copy exists, not
322
- // just the signing call: the copy is taken before the response list, the responder ID, the
323
- // SingleResponses, the dates, the nonce and the signature scheme are validated, and every
324
- // one of those can fail. Attaching cleanup to signing alone leaves the secret in the heap
325
- // on each of those earlier exits, which are the easiest ones for a caller to reach.
326
265
  function _wipeOwnedKey() {
327
266
  if (ownedKeyBytes.length) guard.secret.zeroizeAll(ownedKeyBytes, OcspError, "ocsp/bad-input", "the responder key copy");
328
267
  ownedKeyBytes.length = 0;
@@ -335,9 +274,6 @@ function _sign(responseData, responder, opts) {
335
274
  function (e) { _wipeOwnedKey(); throw e; });
336
275
  }
337
276
 
338
- // Everything from the key snapshot onward, so a single cleanup in the caller covers every
339
- // exit -- sync throw and async rejection alike -- rather than each fallible step needing to
340
- // remember. `responderKey` is the private copy; it is never the caller's object.
341
277
  function _signResponse(responseData, responder, respCert, respCertDer, responderKey, opts) {
342
278
  var responses = responseData.responses != null ? pkiBuild.reqDenseArray(responseData.responses, "responses", _err, "ocsp/bad-input") : [];
343
279
  if (!responses.length) throw _err("ocsp/bad-input", "a response MUST include at least one SingleResponse (RFC 6960 sec. 4.2.1)");
@@ -361,39 +297,18 @@ function _signResponse(responseData, responder, respCert, respCertDer, responder
361
297
  });
362
298
  });
363
299
  }
364
- // A date value -> Date, or null when absent (the caller defaults). An unparseable value is a
365
- // config-time error, not a silently-emitted Invalid Date: a NaN date would encode as a malformed
366
- // GeneralizedTime in a SIGNED response, and compare false against every currency bound on verify.
367
300
  function _asDate(d) {
368
301
  if (d == null) return null;
369
302
  var dt = guard.time.isDate(d) ? d : new Date(d);
370
303
  if (isNaN(guard.time.instantOf(dt))) throw _err("ocsp/bad-input", "an invalid date value " + JSON.stringify(d));
371
304
  return dt;
372
305
  }
373
- // A private copy of whatever signer-key material the caller owns, taken at the entry
374
- // point because signing happens several promise turns later (see `sign`). Bytes are
375
- // copied; a composite { mldsa, trad } is rebuilt with each component copied, since
376
- // rewriting a component is the same attack one level down. Anything else -- a CryptoKey,
377
- // a PEM string -- is not caller-mutable in a way that changes the key, so it rides as-is
378
- // rather than being coerced into a shape this function does not understand.
379
- // `owned` collects every buffer THIS function allocated, so the caller can clear them once
380
- // signing is done. A copy of a private key is a second copy of a secret, which is precisely
381
- // what this project's secret-lifetime discipline exists to avoid -- so the copy that closes
382
- // the aliasing window is wiped rather than left to the garbage collector. Only our own
383
- // allocations are listed: the caller's key is never written to.
384
306
  function _snapshotSignerKey(key, owned) {
385
307
  if (Buffer.isBuffer(key) || key instanceof Uint8Array) { // allow:byte-source-narrow -- responder private key on the key-ownership contract (Buffer / Uint8Array / PEM / CryptoKey); widening keys is a separate root decision
386
308
  var copy = guard.bytes.snapshot(key, OcspError, "ocsp/bad-input", "the responder key");
387
309
  owned.push(copy);
388
310
  return copy;
389
311
  }
390
- // The test is "is this a composite DESCRIPTOR", not "does it currently hold bytes". A
391
- // composite whose components are both PEM strings carries nothing mutable inside it, but
392
- // the OBJECT is still the caller's: reassigning `key.mldsa` after the call reaches the
393
- // deferred signing operation just as rewriting a buffer would, and yields a response whose
394
- // responder ID and embedded certificate describe one responder over a signature made by
395
- // another key. So the container is always rebuilt, and each component is snapshotted by
396
- // its own type -- bytes copied, an immutable PEM string passed through.
397
312
  if (key && typeof key === "object" && (key.mldsa != null || key.trad != null)) {
398
313
  return Object.assign({}, key, { mldsa: _snapshotSignerKey(key.mldsa, owned), trad: _snapshotSignerKey(key.trad, owned) });
399
314
  }
@@ -401,16 +316,13 @@ function _snapshotSignerKey(key, owned) {
401
316
  }
402
317
 
403
318
  function _responderID(rid, respCert) {
404
- if (rid == null || rid === "byName") return Promise.resolve(b.explicit(1, b.raw(respCert.subject.bytes))); // byName [1] EXPLICIT Name
319
+ if (rid == null || rid === "byName") return Promise.resolve(b.explicit(1, b.raw(respCert.subject.bytes)));
405
320
  if (rid === "byKey") return _digest("SHA-1", _keyValue(respCert.subjectPublicKeyInfo.bytes)).then(function (kh) { return b.explicit(2, b.octetString(kh)); });
406
321
  throw _err("ocsp/bad-input", "responderID must be \"byName\" or \"byKey\"");
407
322
  }
408
323
  function _buildSingleResponse(r, opts) {
409
324
  r = r || {};
410
325
  var certIdP;
411
- // A pre-encoded CertID is spliced in RAW, so its type is checked rather than coerced:
412
- // Buffer.from(20) would allocate twenty zero octets and emit a structurally broken
413
- // CertID inside a response this responder then SIGNS.
414
326
  if (r.certID != null) certIdP = Promise.resolve(b.raw(guard.bytes.view(r.certID, OcspError, "ocsp/bad-input", "a response entry's certID")));
415
327
  else if (r.cert != null && r.issuer != null) certIdP = _buildCertID(_certOf(r.cert, "a response certificate"), _certOf(r.issuer, "a response issuer"), r.hashAlgorithm || "sha1");
416
328
  else return Promise.reject(_err("ocsp/bad-input", "each response entry needs { certID } or { cert, issuer }"));
@@ -424,16 +336,16 @@ function _buildSingleResponse(r, opts) {
424
336
  }
425
337
  function _defaultNextUpdate() { return new Date(Date.now() + constants.TIME.days(7)); }
426
338
  function _certStatusNode(status, opts) {
427
- if (status == null || status === "good") return b.contextPrimitive(0, Buffer.alloc(0)); // good [0] IMPLICIT NULL
428
- if (status === "unknown") return b.contextPrimitive(2, Buffer.alloc(0)); // unknown [2] IMPLICIT NULL
339
+ if (status == null || status === "good") return b.contextPrimitive(0, Buffer.alloc(0));
340
+ if (status === "unknown") return b.contextPrimitive(2, Buffer.alloc(0));
429
341
  if (typeof status === "object" && status.revoked != null) {
430
- var ri = [b.generalizedTime(_asDate(status.revoked))]; // revoked != null is guaranteed above
342
+ var ri = [b.generalizedTime(_asDate(status.revoked))];
431
343
  if (status.revocationReason != null) {
432
344
  var code = typeof status.revocationReason === "number" ? status.revocationReason : REASON_CODE[status.revocationReason];
433
345
  if (code == null) throw _err("ocsp/bad-input", "unknown revocationReason " + JSON.stringify(status.revocationReason));
434
- ri.push(b.explicit(0, b.enumerated(BigInt(code)))); // revocationReason [0] EXPLICIT CRLReason
346
+ ri.push(b.explicit(0, b.enumerated(BigInt(code))));
435
347
  }
436
- return b.contextConstructed(1, Buffer.concat(ri)); // revoked [1] IMPLICIT RevokedInfo
348
+ return b.contextConstructed(1, Buffer.concat(ri));
437
349
  }
438
350
  throw _err("ocsp/bad-input", "a response status must be \"good\", \"unknown\", or { revoked: <Date>, revocationReason? }");
439
351
  }
@@ -508,14 +420,9 @@ function buildErrorResponse(status) {
508
420
  * var res = await pki.ocsp.verify(responseDer, { cert: leafDer, issuer: caDer });
509
421
  * res.status; // "good" | "revoked" | "unknown"
510
422
  */
511
- // Every option pki.ocsp.verify reads. `requestNonce` is the one worth refusing by name: it is
512
- // what binds the response to this request. A misspelling leaves nonceMatched null on a call
513
- // that did ask, and null is the value meaning "the client never asked".
514
423
  var _VERIFY_OPTS = { cert: 1, historicalMode: 1, issuer: 1, requestNonce: 1, time: 1 };
515
424
 
516
425
  function verify(response, opts) {
517
- // This verb reports faults by rejecting, not throwing, so both entry guards run inside the
518
- // same try and surface as a rejection like every other fault on this path.
519
426
  try {
520
427
  opts = guard.identifier.optionsObject(opts, _err, "ocsp/bad-input", "pki.ocsp.verify options");
521
428
  guard.identifier.assertKnownKeys(opts, _VERIFY_OPTS, _err, "ocsp/bad-input",
@@ -524,49 +431,18 @@ function verify(response, opts) {
524
431
  if (opts.cert == null || opts.issuer == null) return Promise.reject(_err("ocsp/bad-input", "verify requires opts.cert and opts.issuer"));
525
432
  var parsed, cert, issuerCert, time;
526
433
  try {
527
- // The response is parsed from BYTES, always. A claimed-parsed response carries the signature,
528
- // the algorithm that verifies it and the byte range it covers as three independent properties,
529
- // so an object could pair one structure's tbsResponseDataBytes with another's signature -- and a
530
- // signature the issuing CA made over a certificate it issued would verify as a ResponseData
531
- // signature, returning status "good" for a certificate the responder never spoke about.
532
- // Parsing here binds all three to one byte string. pki.schema.ocsp.parseResponse remains the
533
- // parse-only route for a caller who wants the structure without a verdict.
534
434
  parsed = _responseFromBytes(response);
535
435
  cert = _certOf(opts.cert, "the target certificate");
536
436
  issuerCert = _certOf(opts.issuer, "the issuer certificate");
537
- // The time drives the currency + responder-cert validity windows; an invalid Date fails closed
538
- // via _asDate (a NaN compares false against every bound, silently disabling both), never defaults.
539
437
  time = opts.time == null ? new Date() : _asDate(opts.time);
540
438
  } catch (e) { return Promise.reject(e); }
541
- // The object parsed HERE goes to the verdict verb, not the caller's argument again. It carries the
542
- // parser's record, so the verdict verb re-derives from the same recorded bytes -- one snapshot,
543
- // read by both. Passing the caller's argument a second time would take a SECOND snapshot of it,
544
- // and a shared-memory view can differ between the two: the nonce compared below would belong to
545
- // one response and the signature verified to another, which is the split this whole mechanism
546
- // exists to close.
547
439
  return pathValidate.verifyOcspResponse(parsed, cert, issuerCert, time, { historicalMode: opts.historicalMode === true }).then(function (verdict) {
548
- // A client that sent no nonce still gets the field, as null. Leaving it absent would make
549
- // "not requested" indistinguishable from "the field is not there yet" for a consumer reading
550
- // res.nonceMatched, and the three-state contract is the whole point: true bound, false not
551
- // bound, null never asked. The verdict is copied rather than mutated, since it is the lower
552
- // primitive's object and this layer does not own it.
553
440
  if (opts.requestNonce == null) return Object.assign({}, verdict, { nonceMatched: null });
554
- // A client that sent a nonce binds it (RFC 9654 / RFC 5019 sec. 4): a missing or mismatched
555
- // response nonce fails the verdict closed, even if the status/signature were otherwise good.
556
441
  var respNonce = _responseNonce(parsed);
557
442
  var reqNonce = guard.bytes.isByteSource(opts.requestNonce)
558
443
  ? guard.bytes.snapshotSource(opts.requestNonce, OcspError, "ocsp/bad-input", "opts.requestNonce") : null;
559
444
  var matched = respNonce != null && reqNonce != null && guard.crypto.constantTimeEqual(respNonce, reqNonce);
560
445
  var out = Object.assign({}, verdict, { nonceMatched: matched });
561
- // The downgrade applies to `good` ONLY. `unknown` is the closed direction for a
562
- // response claiming the certificate is fine, because an unmatched nonce means
563
- // this is not an answer to this request and the "fine" may be stale. It is NOT
564
- // the closed direction for `revoked`: revocation does not expire the way
565
- // non-revocation does, so discarding a signed, current, authorized revoked
566
- // verdict because it was replayed would hand a soft-fail caller the certificate
567
- // the responder just refused -- turning the anti-replay defense into the thing
568
- // that accepts a revoked certificate. `nonceMatched: false` still reports that
569
- // this response was not bound to this request.
570
446
  if (!matched && verdict.status === "good") {
571
447
  return Object.assign(out, { status: "unknown", reason: "the OCSP response nonce does not echo the request nonce (RFC 9654)" });
572
448
  }
@@ -581,19 +457,13 @@ function _responseNonce(parsed) {
581
457
  }
582
458
 
583
459
  var _VERIFY_REQUEST_OPTS = { certs: 1 };
584
- // Parsed from BYTES so the signature, the algorithm that verifies it and the tbsRequest it covers
585
- // are bound to one byte string, the same discipline pki.ocsp.verify follows for a response. A
586
- // BufferSource is SNAPSHOT first: parseRequest surfaces tbsRequest / signature / embedded certs as
587
- // views into it, and the signature check awaits, so without a copy a caller could mutate the buffer
588
- // mid-verification and be judged on bytes other than the parser read. A PEM string decodes to a
589
- // fresh buffer already; parseRequest owns that decode and a non-bytes input is its typed refusal.
590
460
  function _requestFromBytes(request) {
591
461
  if (guard.bytes.isByteSource(request)) request = guard.bytes.snapshotSource(request, OcspError, "ocsp/bad-input", "the OCSP request");
592
462
  return ocspSchema.parseRequest(request);
593
463
  }
594
464
  function _reqVerdict(parsed, extra) {
595
465
  return {
596
- valid: extra.signed === true && extra.signatureValid === true, // canonical alias: the request was signed AND the signature verified
466
+ valid: extra.signed === true && extra.signatureValid === true,
597
467
  signed: extra.signed, signatureValid: extra.signatureValid,
598
468
  signerCert: extra.signerCert != null ? extra.signerCert : null,
599
469
  signerCerts: extra.signerCerts != null ? extra.signerCerts : [],
@@ -604,33 +474,13 @@ function _reqVerdict(parsed, extra) {
604
474
  reason: extra.reason != null ? extra.reason : null,
605
475
  };
606
476
  }
607
- // Collect EVERY candidate certificate whose key verified the request signature. RFC 6960 sec. 4.1.1
608
- // certs is an unordered SEQUENCE OF Certificate, and several may share the signing key (an expired
609
- // certificate carried beside its renewal), so all matches are surfaced rather than one
610
- // position-dependent pick: the responder builds a trusted path to one of them to select a usable
611
- // one, and no valid match is hidden behind an unusable one that happened to appear first. `strict` separates the
612
- // responder's own opts.certs (a malformed entry is its config error, a typed throw) from the
613
- // request's untrusted embedded certs (a malformed one is skipped, never fatal).
614
477
  async function _findRequestSigners(candidates, strict, parsed, sig, sigBits) {
615
478
  var parsedList = [];
616
479
  for (var i = 0; i < candidates.length; i++) {
617
480
  try { parsedList.push({ der: candidates[i], cert: x509.parse(candidates[i]) }); }
618
481
  catch (e) { if (strict) throw _err("ocsp/bad-signer-cert", "an opts.certs entry does not parse as an X.509 certificate", e); }
619
482
  }
620
- // The certs surfaced for path building are the PARSEABLE ones: a malformed embedded entry (skipped
621
- // above rather than fatal) is excluded so it is never handed to pki.path.build as a candidate.
622
483
  var parseable = parsedList.map(function (p) { return p.der; });
623
- // signerCerts is every candidate whose key VERIFIES the request signature, identified by the
624
- // verification itself rather than by comparing encoded key bytes. A request carries one signature
625
- // under one key, but that key can appear across several certificates in the unordered certs field
626
- // (an expired certificate beside its renewal), and the one key can be encoded differently between
627
- // them -- rsaEncryption with NULL versus absent parameters, an EC point compressed versus
628
- // uncompressed -- so a byte comparison would drop a genuine match, while a coincidental byte match
629
- // under a different algorithm would be a false one. Verifying each candidate answers both exactly,
630
- // through the algorithm-confusion gate (RFC 9814 sec. 4) in the shared engine. The work is bounded:
631
- // the parser already caps the certificate count at OCSP_MAX_CERTS, and the engine returns false
632
- // (never throws) for a candidate whose key does not match the signature, so a non-signer is skipped
633
- // rather than aborting the verdict.
634
484
  var matches = [];
635
485
  for (var j = 0; j < parsedList.length; j++) {
636
486
  if (await ocspRequestVerify.verifyRequestSignature(sig.signatureAlgorithm, sigBits, parsedList[j].cert.subjectPublicKeyInfo.bytes, parsed.tbsRequestBytes)) {
@@ -639,11 +489,6 @@ async function _findRequestSigners(candidates, strict, parsed, sig, sigBits) {
639
489
  }
640
490
  return { matches: matches, parseable: parseable };
641
491
  }
642
- // The verdict from `_findRequestSigners`: signerCerts is every match (so the caller's trust decision
643
- // does not depend on ordering), signerCert / signerSubject the first for the common single-signer
644
- // case. `certs` is every PARSEABLE certificate the request carried (or opts.certs supplied), so the
645
- // responder has the intermediates it needs to chain a signer to an anchor -- those do not sign the
646
- // request, so they never appear in signerCerts. No match is signatureValid:false with the reason.
647
492
  function _signerVerdict(parsed, found, failReason) {
648
493
  if (!found.matches.length) return _reqVerdict(parsed, { signed: true, signatureValid: false, certs: found.parseable, reason: failReason });
649
494
  return _reqVerdict(parsed, {
@@ -714,10 +559,7 @@ async function verifyRequest(request, opts) {
714
559
  return _reqVerdict(parsed, { signed: false, signatureValid: false,
715
560
  reason: "the OCSP request is unsigned (RFC 6960 sec. 4.1.1 makes optionalSignature OPTIONAL); no requestor is authenticated" });
716
561
  }
717
- // sig.signature is the raw signature the parser already refused unless octet-aligned; the seam
718
- // re-checks alignment, so it is handed the BIT STRING shape the seam verifies.
719
562
  var sigBits = { unusedBits: 0, bytes: sig.signature };
720
- // Prefer the certificates the request carries; fall back to opts.certs only when it carries none.
721
563
  if (sig.certs.length) {
722
564
  return _signerVerdict(parsed, await _findRequestSigners(sig.certs, false, parsed, sig, sigBits),
723
565
  "the OCSP request signature does not verify over tbsRequest under any certificate the request carries (RFC 6960 sec. 4.1.1)");