@blamejs/pki 0.8.30 → 0.8.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/CHANGELOG.md +411 -0
  2. package/README.md +67 -16
  3. package/bin/pki.js +716 -30
  4. package/examples/issue-a-certificate.js +90 -0
  5. package/examples/post-quantum.js +66 -0
  6. package/examples/read-a-certificate.js +56 -0
  7. package/examples/revoke-and-check.js +86 -0
  8. package/examples/sign-and-verify-cms.js +63 -0
  9. package/index.js +18 -5
  10. package/lib/acme.js +223 -74
  11. package/lib/alt-sig.js +441 -0
  12. package/lib/asn1-der.js +48 -25
  13. package/lib/attrcert-sign.js +27 -19
  14. package/lib/cbor-det.js +24 -14
  15. package/lib/cmc-build.js +9 -2
  16. package/lib/cmc-verify.js +14 -3
  17. package/lib/cmp-build.js +17 -5
  18. package/lib/cmp-session.js +24 -11
  19. package/lib/cmp-verify.js +28 -19
  20. package/lib/cms-compress.js +3 -1
  21. package/lib/cms-decrypt.js +102 -7
  22. package/lib/cms-encrypt.js +59 -7
  23. package/lib/cms-sign.js +31 -9
  24. package/lib/cms-verify.js +22 -18
  25. package/lib/composite-kem.js +30 -18
  26. package/lib/composite-sig.js +29 -14
  27. package/lib/constants.js +28 -0
  28. package/lib/crl-sign.js +111 -19
  29. package/lib/crmf-sign.js +167 -15
  30. package/lib/csr-sign.js +117 -6
  31. package/lib/ct.js +939 -48
  32. package/lib/est.js +29 -16
  33. package/lib/framework-error.js +12 -0
  34. package/lib/guard-async.js +1 -1
  35. package/lib/guard-bytes.js +103 -20
  36. package/lib/guard-crypto.js +205 -3
  37. package/lib/guard-encoding.js +6 -1
  38. package/lib/guard-identifier.js +55 -8
  39. package/lib/guard-intrinsic.js +43 -0
  40. package/lib/guard-json.js +122 -9
  41. package/lib/guard-limits.js +12 -1
  42. package/lib/guard-list.js +63 -8
  43. package/lib/guard-name.js +167 -20
  44. package/lib/guard-range.js +43 -5
  45. package/lib/guard-text.js +41 -0
  46. package/lib/guard-verdict.js +39 -8
  47. package/lib/hpke.js +688 -42
  48. package/lib/http-transport.js +156 -4
  49. package/lib/identity-match.js +4 -4
  50. package/lib/inspect.js +448 -2
  51. package/lib/jose.js +93 -25
  52. package/lib/key.js +18 -13
  53. package/lib/lint.js +165 -1
  54. package/lib/merkle.js +233 -27
  55. package/lib/ocsp.js +27 -14
  56. package/lib/oid.js +14 -4
  57. package/lib/path-validate.js +143 -61
  58. package/lib/pkcs12-build.js +7 -2
  59. package/lib/pki-build.js +31 -7
  60. package/lib/possession.js +575 -0
  61. package/lib/related-cert.js +708 -0
  62. package/lib/revocation-fetch.js +6 -6
  63. package/lib/scep.js +15 -5
  64. package/lib/schema-c509.js +5 -2
  65. package/lib/schema-cmc.js +1 -1
  66. package/lib/schema-cmp.js +8 -8
  67. package/lib/schema-cms.js +2 -15
  68. package/lib/schema-csr.js +69 -0
  69. package/lib/schema-engine.js +10 -3
  70. package/lib/schema-pkix.js +267 -4
  71. package/lib/schema-tsp.js +7 -6
  72. package/lib/sign-scheme.js +22 -9
  73. package/lib/sigstore.js +684 -168
  74. package/lib/sleep.js +8 -1
  75. package/lib/smime.js +22 -1
  76. package/lib/tlog.js +1235 -0
  77. package/lib/tls-cert-compress.js +25 -8
  78. package/lib/tsp-sign.js +120 -35
  79. package/lib/tuf.js +1395 -0
  80. package/lib/validator-tpm.js +4 -1
  81. package/lib/verify-seam.js +7 -1
  82. package/lib/webauthn-mds.js +41 -4
  83. package/lib/webauthn.js +43 -25
  84. package/lib/webcrypto.js +48 -46
  85. package/lib/x509-sign.js +225 -19
  86. package/package.json +2 -1
  87. package/sbom.cdx.json +6 -6
package/lib/acme.js CHANGED
@@ -53,6 +53,14 @@ var retryAfter = require("./http-retry-after");
53
53
  var guard = require("./guard-all");
54
54
  var intrinsic = require("./guard-intrinsic");
55
55
  var _resolve = intrinsic.uncurry(intrinsic.promiseResolve);
56
+ /** @internal The RECEIVER for that capture, captured too. `Promise.resolve` builds through its
57
+ * receiver, so handing it the live `globalThis.Promise` put the capture back under a constructor a
58
+ * replacement installed after load could take over, and what it decides is the value that gets
59
+ * awaited. */
60
+ var _Promise = intrinsic.Promise;
61
+ /** @internal A refusal returned as a REJECTED promise is a verdict too: read from the global binding, a
62
+ * replacement whose `reject` resolves instead turns the refusal into a pass. */
63
+ var _reject = intrinsic.uncurry(intrinsic.promiseReject);
56
64
  var _hasOwn = intrinsic.hasOwn;
57
65
  var _isBuffer = intrinsic.isBuffer;
58
66
  var _isArrayI = intrinsic.isArray;
@@ -65,6 +73,8 @@ var _lower = intrinsic.toLowerCase;
65
73
  var _StringI = intrinsic.String;
66
74
  var _stringify = intrinsic.stringify;
67
75
  var _push = intrinsic.push;
76
+ var _pop = intrinsic.pop;
77
+ var _shift = intrinsic.shift;
68
78
  var _join = intrinsic.join;
69
79
  var _concat = intrinsic.concat;
70
80
  var _split = intrinsic.uncurry(String.prototype.split);
@@ -293,21 +303,75 @@ function _codeSlug(kind) {
293
303
  return _lower(out);
294
304
  }
295
305
 
296
- function _validate(kind, obj) {
306
+ /** @internal The NAMED members of a caller's resource object, read ONCE each into a plain record. A
307
+ * validator that reads the object again for a second check lets an accessor answer the two differently,
308
+ * so the reading is taken here and every check works from it. A caller that runs two validators over
309
+ * one object passes the same reading to both rather than reading it twice.
310
+ *
311
+ * Only the named members are read. An ACME resource may carry extension members and this surface
312
+ * ignores them, and ignoring a member means not reading it: an accessor on a name no specification
313
+ * lists is caller code with nothing to decide here, and reading every own member ran it. A throwing
314
+ * one then left by a path carrying no `acme/` code, and a side-effecting one ran before the listed
315
+ * members had been captured, so it could change one the reading had not reached yet. */
316
+ function _readOnce(obj, names) {
317
+ var read = intrinsic.create(null);
318
+ for (var k = 0; k < names.length; k++) {
319
+ var name = names[k];
320
+ if (_hasOwn(obj, name)) read[name] = obj[name];
321
+ }
322
+ return read;
323
+ }
324
+
325
+ /** @internal The members read from a prior `renewalWindow` result, and the bounds read from a window. */
326
+ var PREVIOUS_WINDOW_NAMES = ["suggestedWindow", "selectedTime"];
327
+ var WINDOW_BOUND_NAMES = ["start", "end"];
328
+
329
+ /** @internal The field names of each resource specification, derived from SPECS once so a reading names
330
+ * what it reads and a new field cannot be left out of one. */
331
+ var SPEC_NAMES = intrinsic.create(null);
332
+ (function () {
333
+ var kinds = _objectKeys(SPECS);
334
+ for (var i = 0; i < kinds.length; i++) {
335
+ var fields = SPECS[kinds[i]], names = [];
336
+ for (var j = 0; j < fields.length; j++) names[j] = fields[j].name;
337
+ SPEC_NAMES[kinds[i]] = names;
338
+ }
339
+ })();
340
+
341
+ function _validate(kind, obj, pre) {
297
342
  if (!_isObject(obj)) throw E("acme/bad-" + _codeSlug(kind), "an ACME " + kind + " must be a JSON object");
298
343
  var spec = SPECS[kind];
344
+ /** @internal Every own member is read ONCE, here, before any check runs, and every check below decides
345
+ * from this reading. The walk used to read `obj[field.name]` three times per field, for the closed-enum
346
+ * check, the message and the type check, and a `requiredWhen` predicate then read the object again:
347
+ * `o.status === "pending" || o.status === "valid"` is two more reads on its own. An accessor answers
348
+ * each read separately, so a `status` could pass the closed enum as "valid", answer a predicate
349
+ * differently to make a required field optional, and be a third value by the time the type check
350
+ * looked, while this reported the object as conforming. A closed status enum is what validating an
351
+ * ACME state is for.
352
+ *
353
+ * The ORIGINAL object is still returned, which the documented signature fixes; what this changes is
354
+ * that the verdict is about one reading of it. A caller that has already taken the reading passes it
355
+ * in, so two validators over one object do not read it twice between them. */
356
+ var read = pre || _readOnce(obj, SPEC_NAMES[kind]);
299
357
  for (var f = 0; f < spec.length; f++) {
300
358
  var field = spec[f];
301
- var present = _hasOwn(obj, field.name);
302
- var required = field.required || (field.requiredWhen && field.requiredWhen(obj));
359
+ var present = _hasOwn(read, field.name);
360
+ var required = field.required || (field.requiredWhen && field.requiredWhen(read));
303
361
  if (!present) {
304
362
  if (required) throw E("acme/missing-field", "an ACME " + kind + " is missing the required field " + JSON.stringify(field.name));
305
363
  continue;
306
364
  }
307
- if (field.enum && field.enum.indexOf(obj[field.name]) === -1) {
308
- throw E("acme/bad-status", "the " + kind + " " + field.name + " " + JSON.stringify(obj[field.name]) + " is not a recognized value");
365
+ /** @internal ONE read of the field, which the enum check, the message and the type check all read.
366
+ * It was read three times, and an accessor answers each separately: a `status` could pass the closed
367
+ * enum as "valid" and be another value by the time the type check looked, while this reported the
368
+ * object as conforming. A closed status enum is what validating an ACME state is for, so the check
369
+ * passing for a value the object does not carry is the check not happening. */
370
+ var value = read[field.name];
371
+ if (field.enum && field.enum.indexOf(value) === -1) {
372
+ throw E("acme/bad-status", "the " + kind + " " + field.name + " " + JSON.stringify(value) + " is not a recognized value");
309
373
  }
310
- var err = _checkType(kind, field, obj[field.name], obj);
374
+ var err = _checkType(kind, field, value, read);
311
375
  if (err) throw E("acme/bad-" + _codeSlug(kind), "the " + kind + " field " + JSON.stringify(field.name) + " " + err);
312
376
  }
313
377
  return obj;
@@ -413,12 +477,16 @@ var ERROR_NAMESPACE = "urn:ietf:params:acme:error:";
413
477
  function validateProblem(obj) {
414
478
  if (!_isObject(obj)) throw E("acme/bad-problem", "a problem document must be a JSON object");
415
479
  if (_hasOwn(obj, "identifier")) throw E("acme/bad-problem", "a top-level problem document must not carry an identifier (RFC 8555 sec. 6.7.1)");
416
- if (!_isString(obj.type) || obj.type.indexOf(ERROR_NAMESPACE) !== 0) throw E("acme/bad-problem", "an ACME problem type must be in the " + ERROR_NAMESPACE + " namespace (RFC 8555 sec. 6.7)");
480
+ /** @internal ONE read of `type`, for both the string test and the namespace test. Read twice, a
481
+ * non-string on the second reached `.indexOf` and raised a bare TypeError carrying no code. */
482
+ var problemType = obj.type;
483
+ if (!_isString(problemType) || problemType.indexOf(ERROR_NAMESPACE) !== 0) throw E("acme/bad-problem", "an ACME problem type must be in the " + ERROR_NAMESPACE + " namespace (RFC 8555 sec. 6.7)");
417
484
  if (_hasOwn(obj, "subproblems")) {
418
485
  if (!Array.isArray(obj.subproblems)) throw E("acme/bad-problem", "subproblems must be an array");
419
486
  for (var i = 0; i < obj.subproblems.length; i++) {
420
487
  var sub = obj.subproblems[i];
421
- if (!_isObject(sub) || !_isString(sub.type) || sub.type.indexOf(ERROR_NAMESPACE) !== 0) throw E("acme/bad-problem", "each subproblem must be an ACME problem document in the error namespace");
488
+ var subType = _isObject(sub) ? sub.type : undefined;
489
+ if (!_isObject(sub) || !_isString(subType) || subType.indexOf(ERROR_NAMESPACE) !== 0) throw E("acme/bad-problem", "each subproblem must be an ACME problem document in the error namespace");
422
490
  if (_hasOwn(sub, "identifier")) _validateOrderIdentifier(sub.identifier);
423
491
  }
424
492
  }
@@ -478,8 +546,12 @@ function validate(kind, obj) {
478
546
  function identify(obj) {
479
547
  if (!_isObject(obj)) return "unknown";
480
548
  var has = function (k) { return _hasOwn(obj, k); };
549
+ /** @internal ONE read of `type`, for both tests below. This is a DISPATCHER: what it returns decides
550
+ * which validator a caller reaches for, and read twice the kind it names could be chosen from one
551
+ * value while the object carries another. */
552
+ var type = obj.type;
481
553
  if (has("protected") && has("signature") && _isString(obj.protected) && _isString(obj.signature)) return "jws";
482
- if (_isString(obj.type) && obj.type.indexOf(ERROR_NAMESPACE) === 0) return "problem";
554
+ if (_isString(type) && type.indexOf(ERROR_NAMESPACE) === 0) return "problem";
483
555
  if (has("newNonce") && has("newAccount")) return "directory";
484
556
  if (has("suggestedWindow")) return "renewalInfo";
485
557
  if (has("finalize") && has("authorizations")) return "order";
@@ -650,6 +722,14 @@ function _readAcmeIdentifier(extnValue) {
650
722
  * await pki.acme.verifyTlsAlpn01(certDer, token, accountJwk, { type: "dns", value: "example.org" });
651
723
  */
652
724
  async function verifyTlsAlpn01(certDer, token, accountJwk, identifier) {
725
+ /** @internal The identifier's type and value are read ONCE, at entry, and every check below runs on those
726
+ * locals. Reading them after the key-authorization await let a caller answer the validity check with one
727
+ * name and the comparison against the certificate with another, so a name nothing validated could be
728
+ * matched against the SAN and the identifier would be reported as confirmed. */
729
+ if (!_isObject(identifier)) throw E("acme/bad-input", "an identifier { type, value } is required");
730
+ var idType = identifier.type;
731
+ var idValue = identifier.value;
732
+ if (!_isString(idType)) throw E("acme/bad-input", "an identifier { type, value } is required");
653
733
  var cert = x509.parse(certDer);
654
734
  var exts = cert.extensions || [];
655
735
  var acmeExt = exts.filter(function (e) { return e.oid === OID_ACME_IDENTIFIER; })[0];
@@ -664,18 +744,17 @@ async function verifyTlsAlpn01(certDer, token, accountJwk, identifier) {
664
744
  var san = _extDecoders.byOid[OID_SAN](sanExt.value);
665
745
  if (!san.names || san.names.length !== 1) throw E("acme/bad-tlsalpn", "the SubjectAltName must carry EXACTLY ONE entry (RFC 8737 sec. 3)");
666
746
  var entry = san.names[0];
667
- if (!_isObject(identifier) || !_isString(identifier.type)) throw E("acme/bad-input", "an identifier { type, value } is required");
668
- if (identifier.type === "dns") {
747
+ if (idType === "dns") {
669
748
  if (entry.tagNumber !== 2) throw E("acme/bad-tlsalpn", "a dns identifier requires a dNSName SAN");
670
- _assertDnsName(identifier.value);
671
- if (String(entry.value).toLowerCase() !== identifier.value) throw E("acme/bad-tlsalpn", "the SAN dNSName does not match the identifier");
672
- } else if (identifier.type === "ip") {
749
+ _assertDnsName(idValue);
750
+ if (String(entry.value).toLowerCase() !== idValue) throw E("acme/bad-tlsalpn", "the SAN dNSName does not match the identifier");
751
+ } else if (idType === "ip") {
673
752
  if (entry.tagNumber !== 7) throw E("acme/bad-tlsalpn", "an ip identifier requires a single iPAddress SAN (RFC 8738 sec. 6)");
674
- _assertIpAddress(identifier.value);
753
+ _assertIpAddress(idValue);
675
754
  var sanIp = _ipBytesToText(entry.value);
676
- if (sanIp === null || sanIp !== identifier.value) throw E("acme/bad-tlsalpn", "the iPAddress SAN does not match the ip identifier (RFC 8738 sec. 6)");
755
+ if (sanIp === null || sanIp !== idValue) throw E("acme/bad-tlsalpn", "the iPAddress SAN does not match the ip identifier (RFC 8738 sec. 6)");
677
756
  } else {
678
- throw E("acme/bad-tlsalpn", "unsupported tls-alpn-01 identifier type " + guard.text.showValue(identifier.type));
757
+ throw E("acme/bad-tlsalpn", "unsupported tls-alpn-01 identifier type " + guard.text.showValue(idType));
679
758
  }
680
759
  }
681
760
 
@@ -716,6 +795,7 @@ function _signOuter(o, payloadObj) {
716
795
  function postAsGet(o) {
717
796
  return _promised(function () {
718
797
  if (!_isObject(o)) throw E("acme/bad-input", "a request options object is required");
798
+ o = _gateRequestOpts(o, "postAsGet");
719
799
  return _signOuter({ key: o.key, alg: o.alg, nonce: o.nonce, url: o.url, kid: o.kid }, undefined);
720
800
  });
721
801
  }
@@ -754,7 +834,9 @@ function _assertContacts(contacts) {
754
834
  */
755
835
  function newAccount(o) {
756
836
  return _promised(function () {
757
- if (!_isObject(o) || !_isObject(o.jwk)) throw E("acme/bad-input", "newAccount must embed the account public jwk (RFC 8555 sec. 7.3)");
837
+ if (!_isObject(o)) throw E("acme/bad-input", "newAccount must embed the account public jwk (RFC 8555 sec. 7.3)");
838
+ o = _gateRequestOpts(o, "newAccount");
839
+ if (!_isObject(o.jwk)) throw E("acme/bad-input", "newAccount must embed the account public jwk (RFC 8555 sec. 7.3)");
758
840
  var payload = {};
759
841
  if (o.contact !== undefined) { _assertContacts(o.contact); payload.contact = o.contact; }
760
842
  if (o.termsOfServiceAgreed !== undefined) {
@@ -797,19 +879,23 @@ var _HMAC_HASH = intrinsic.assign(intrinsic.create(null), { HS256: "SHA-256", HS
797
879
  * var eab = await pki.acme.externalAccountBinding({ macKey, kid: "abc123", url, accountJwk });
798
880
  */
799
881
  async function externalAccountBinding(o) {
800
- if (!_isObject(o) || !_isObject(o.accountJwk)) throw E("acme/bad-input", "externalAccountBinding requires the account public jwk (sec. 7.3.4)");
801
- jose.assertPublicJwk(o.accountJwk);
802
- if (!_isString(o.kid)) throw E("acme/bad-input", "externalAccountBinding requires the CA-issued kid");
882
+ if (!_isObject(o)) throw E("acme/bad-input", "externalAccountBinding requires the account public jwk (sec. 7.3.4)");
883
+ /** @internal Every member is read ONCE, at entry, and the checks and the signature both run on those
884
+ * locals. The MAC key import is an await, so reading a member again after it let a caller have one jwk
885
+ * validated and a different one bound into the JWS. */
886
+ var accountJwk = o.accountJwk, kid = o.kid, url = o.url, key = o.macKey;
803
887
  var alg = o.alg || "HS256";
888
+ if (!_isObject(accountJwk)) throw E("acme/bad-input", "externalAccountBinding requires the account public jwk (sec. 7.3.4)");
889
+ jose.assertPublicJwk(accountJwk);
890
+ if (!_isString(kid)) throw E("acme/bad-input", "externalAccountBinding requires the CA-issued kid");
804
891
  if (!_HMAC_HASH[guard.text.keyOf(alg)]) throw E("acme/bad-input", "an EAB inner JWS must use an HS* MAC algorithm (sec. 7.3.4), not " + guard.text.showValue(alg));
805
- var key = o.macKey;
806
892
  if (Buffer.isBuffer(key)) {
807
893
  try { key = await subtle.importKey("raw", key, { name: "HMAC", hash: _HMAC_HASH[alg] }, false, ["sign"]); }
808
894
  catch (e) { throw E("acme/bad-input", "the EAB macKey could not be imported as an HMAC key", e); }
809
895
  } else if (!key || typeof key !== "object" || key.type !== "secret") {
810
896
  throw E("acme/bad-input", "the EAB macKey must be a raw Buffer or an HMAC (secret) CryptoKey");
811
897
  }
812
- return jose.sign({ protected: { alg: alg, kid: o.kid, url: o.url }, payload: _payloadBuf(o.accountJwk), key: key, profile: "eab-inner" });
898
+ return jose.sign({ protected: { alg: alg, kid: kid, url: url }, payload: _payloadBuf(accountJwk), key: key, profile: "eab-inner" });
813
899
  }
814
900
 
815
901
  function _validateOrderIdentifier(id) {
@@ -851,7 +937,9 @@ function _validateOrderIdentifier(id) {
851
937
  */
852
938
  function newOrder(o) {
853
939
  return _promised(function () {
854
- if (!_isObject(o) || !Array.isArray(o.identifiers) || o.identifiers.length === 0) throw E("acme/bad-order", "newOrder requires a non-empty identifiers array (RFC 8555 sec. 7.4)");
940
+ if (!_isObject(o)) throw E("acme/bad-order", "newOrder requires a non-empty identifiers array (RFC 8555 sec. 7.4)");
941
+ o = _gateRequestOpts(o, "newOrder");
942
+ if (!Array.isArray(o.identifiers) || o.identifiers.length === 0) throw E("acme/bad-order", "newOrder requires a non-empty identifiers array (RFC 8555 sec. 7.4)");
855
943
  var payload = { identifiers: o.identifiers.map(_validateOrderIdentifier) };
856
944
  if (o.notBefore !== undefined) { if (!_isRfc3339(o.notBefore)) throw E("acme/bad-order", "notBefore must be an RFC 3339 date-time"); payload.notBefore = o.notBefore; }
857
945
  if (o.notAfter !== undefined) { if (!_isRfc3339(o.notAfter)) throw E("acme/bad-order", "notAfter must be an RFC 3339 date-time"); payload.notAfter = o.notAfter; }
@@ -879,7 +967,9 @@ function newOrder(o) {
879
967
  * await pki.acme.newAuthz({ key, alg: "ES256", nonce, url, kid, identifier: { type: "dns", value: "example.org" } });
880
968
  */
881
969
  function newAuthz(o) {
882
- if (!_isObject(o) || !_isObject(o.identifier)) throw E("acme/bad-identifier", "newAuthz requires a single identifier object (RFC 8555 sec. 7.4.1)");
970
+ if (!_isObject(o)) throw E("acme/bad-identifier", "newAuthz requires a single identifier object (RFC 8555 sec. 7.4.1)");
971
+ o = _gateRequestOpts(o, "newAuthz");
972
+ if (!_isObject(o.identifier)) throw E("acme/bad-identifier", "newAuthz requires a single identifier object (RFC 8555 sec. 7.4.1)");
883
973
  return _signOuter({ key: o.key, alg: o.alg, nonce: o.nonce, url: o.url, kid: o.kid }, { identifier: _validateIdentifier(o.identifier) });
884
974
  }
885
975
 
@@ -1026,26 +1116,37 @@ function _assertCsrIdentifiers(parsedCsr, identifiers) {
1026
1116
  * await pki.acme.finalize({ key, alg: "ES256", nonce, url, kid, csr: csrDer, identifiers, accountJwk });
1027
1117
  */
1028
1118
  async function finalize(o) {
1029
- if (!_isObject(o) || !guard.bytes.isByteSource(o.csr)) throw E("acme/bad-input", "finalize requires a DER CSR BufferSource (opts.csr)");
1119
+ if (!_isObject(o)) throw E("acme/bad-input", "finalize requires a DER CSR BufferSource (opts.csr)");
1120
+ /** @internal Every member is read ONCE, at entry. The account-key comparison awaits, so reading
1121
+ * `identifiers` after it let a caller have one list validated and the CSR asserted against another,
1122
+ * which is the check that binds the request to the order. The identifier array is copied element by
1123
+ * element for the same reason: the elements are the caller's too. */
1124
+ var accountJwk = o.accountJwk, identifiers = o.identifiers;
1125
+ var outer = { key: o.key, alg: o.alg, nonce: o.nonce, url: o.url, kid: o.kid };
1126
+ if (!guard.bytes.isByteSource(o.csr)) throw E("acme/bad-input", "finalize requires a DER CSR BufferSource (opts.csr)");
1030
1127
  var csrBuf = guard.bytes.snapshotSource(o.csr, E, "acme/bad-input", "the finalize CSR");
1128
+ var ids = null;
1129
+ if (identifiers !== undefined) {
1130
+ if (!Array.isArray(identifiers) || identifiers.length === 0) throw E("acme/bad-input", "finalize identifiers must be the non-empty order identifier array");
1131
+ ids = [];
1132
+ for (var ii = 0; ii < identifiers.length; ii++) {
1133
+ if (!_hasOwn(identifiers, ii)) throw E("acme/bad-input", "finalize identifiers[" + ii + "] is missing (a sparse array is not allowed)");
1134
+ var one = identifiers[ii];
1135
+ _validateOrderIdentifier(one);
1136
+ guard.list.append(ids, { type: one.type, value: one.value });
1137
+ }
1138
+ }
1031
1139
  var parsed = csr.parse(csrBuf);
1032
- if (o.accountJwk !== undefined) {
1033
- var accountSpki = await _jwkToSpki(o.accountJwk);
1140
+ if (accountJwk !== undefined) {
1141
+ var accountSpki = await _jwkToSpki(accountJwk);
1034
1142
  if (parsed.subjectPublicKeyInfo && Buffer.isBuffer(parsed.subjectPublicKeyInfo.bytes) &&
1035
1143
  parsed.subjectPublicKeyInfo.bytes.equals(accountSpki)) {
1036
1144
  throw E("acme/key-reuse", "the finalize CSR public key must not be the account key (RFC 8555 sec. 11.1)");
1037
1145
  }
1038
1146
  }
1039
- if (o.identifiers !== undefined) {
1040
- if (!Array.isArray(o.identifiers) || o.identifiers.length === 0) throw E("acme/bad-input", "finalize identifiers must be the non-empty order identifier array");
1041
- for (var ii = 0; ii < o.identifiers.length; ii++) {
1042
- if (!_hasOwn(o.identifiers, ii)) throw E("acme/bad-input", "finalize identifiers[" + ii + "] is missing (a sparse array is not allowed)");
1043
- _validateOrderIdentifier(o.identifiers[ii]);
1044
- }
1045
- _assertCsrIdentifiers(parsed, o.identifiers);
1046
- }
1147
+ if (ids !== null) _assertCsrIdentifiers(parsed, ids);
1047
1148
  var payload = { csr: jose.base64url.encode(csrBuf) };
1048
- return _signOuter({ key: o.key, alg: o.alg, nonce: o.nonce, url: o.url, kid: o.kid }, payload);
1149
+ return _signOuter(outer, payload);
1049
1150
  }
1050
1151
 
1051
1152
  /**
@@ -1072,6 +1173,7 @@ async function finalize(o) {
1072
1173
  function challengeResponse(o) {
1073
1174
  return _promised(function () {
1074
1175
  if (!_isObject(o)) throw E("acme/bad-input", "a request options object is required");
1176
+ o = _gateRequestOpts(o, "challengeResponse");
1075
1177
  var payload = o.payload !== undefined ? o.payload : {};
1076
1178
  if (!_isObject(payload)) throw E("acme/bad-input", "a challenge response payload must be a JSON object (RFC 8555 sec. 7.5.1)");
1077
1179
  return _signOuter({ key: o.key, alg: o.alg, nonce: o.nonce, url: o.url, kid: o.kid }, payload);
@@ -1100,6 +1202,7 @@ function challengeResponse(o) {
1100
1202
  function deactivate(o) {
1101
1203
  return _promised(function () {
1102
1204
  if (!_isObject(o)) throw E("acme/bad-input", "a request options object is required");
1205
+ o = _gateRequestOpts(o, "deactivate");
1103
1206
  return _signOuter({ key: o.key, alg: o.alg, nonce: o.nonce, url: o.url, kid: o.kid }, { status: "deactivated" });
1104
1207
  });
1105
1208
  }
@@ -1128,6 +1231,7 @@ function deactivate(o) {
1128
1231
  function updateAccount(o) {
1129
1232
  return _promised(function () {
1130
1233
  if (!_isObject(o)) throw E("acme/bad-input", "a request options object is required");
1234
+ o = _gateRequestOpts(o, "updateAccount");
1131
1235
  _assertContacts(o.contact);
1132
1236
  return _signOuter({ key: o.key, alg: o.alg, nonce: o.nonce, url: o.url, kid: o.kid }, { contact: o.contact });
1133
1237
  });
@@ -1160,7 +1264,9 @@ function updateAccount(o) {
1160
1264
  */
1161
1265
  function revokeCert(o) {
1162
1266
  return _promised(function () {
1163
- if (!_isObject(o) || !guard.bytes.isByteSource(o.certificate)) throw E("acme/bad-input", "revokeCert requires a DER certificate BufferSource (opts.certificate)");
1267
+ if (!_isObject(o)) throw E("acme/bad-input", "revokeCert requires a DER certificate BufferSource (opts.certificate)");
1268
+ o = _gateRequestOpts(o, "revokeCert");
1269
+ if (!guard.bytes.isByteSource(o.certificate)) throw E("acme/bad-input", "revokeCert requires a DER certificate BufferSource (opts.certificate)");
1164
1270
  var certBuf = guard.bytes.snapshotSource(o.certificate, E, "acme/bad-input", "the revokeCert certificate");
1165
1271
  x509.parse(certBuf);
1166
1272
  var hasKid = _hasOwn(o, "kid");
@@ -1207,17 +1313,23 @@ function revokeCert(o) {
1207
1313
  */
1208
1314
  async function keyChange(o) {
1209
1315
  if (!_isObject(o)) throw E("acme/bad-input", "a keyChange options object is required");
1210
- if (!_isString(o.account)) throw E("acme/bad-input", "keyChange requires the account URL (payload account)");
1211
- if (!_isObject(o.oldKey)) throw E("acme/bad-input", "keyChange requires the old account public jwk (payload oldKey)");
1212
- if (!_isObject(o.newJwk)) throw E("acme/bad-input", "keyChange requires the new account public jwk (inner header jwk)");
1213
- jose.assertPublicJwk(o.oldKey);
1214
- jose.assertPublicJwk(o.newJwk);
1316
+ /** @internal Every member is read ONCE, at entry, because the inner signature is an await and the outer
1317
+ * header is built after it. `url` in particular went into both headers from two reads, so a caller
1318
+ * could sign the inner JWS for one URL and the outer for another; RFC 8555 sec. 7.3.5 has the two name
1319
+ * the same key-change resource. */
1320
+ var account = o.account, oldKey = o.oldKey, newJwk = o.newJwk, newAlg = o.newAlg, newKey = o.newKey;
1321
+ var url = o.url, alg = o.alg, nonce = o.nonce, kid = o.kid, key = o.key;
1322
+ if (!_isString(account)) throw E("acme/bad-input", "keyChange requires the account URL (payload account)");
1323
+ if (!_isObject(oldKey)) throw E("acme/bad-input", "keyChange requires the old account public jwk (payload oldKey)");
1324
+ if (!_isObject(newJwk)) throw E("acme/bad-input", "keyChange requires the new account public jwk (inner header jwk)");
1325
+ jose.assertPublicJwk(oldKey);
1326
+ jose.assertPublicJwk(newJwk);
1215
1327
  var inner = await jose.sign({
1216
- protected: { alg: o.newAlg, url: o.url, jwk: o.newJwk },
1217
- payload: _payloadBuf({ account: o.account, oldKey: o.oldKey }),
1218
- key: o.newKey, profile: "keychange-inner",
1328
+ protected: { alg: newAlg, url: url, jwk: newJwk },
1329
+ payload: _payloadBuf({ account: account, oldKey: oldKey }),
1330
+ key: newKey, profile: "keychange-inner",
1219
1331
  });
1220
- return jose.sign({ protected: { alg: o.alg, nonce: o.nonce, url: o.url, kid: o.kid }, payload: _payloadBuf(inner), key: o.key, profile: "acme-outer" });
1332
+ return jose.sign({ protected: { alg: alg, nonce: nonce, url: url, kid: kid }, payload: _payloadBuf(inner), key: key, profile: "acme-outer" });
1221
1333
  }
1222
1334
 
1223
1335
 
@@ -1303,11 +1415,20 @@ function parseAriCertId(certId) {
1303
1415
  * pki.acme.validateRenewalInfo({ suggestedWindow: { start: "2026-01-01T00:00:00Z", end: "2026-01-08T00:00:00Z" } });
1304
1416
  */
1305
1417
  function validateRenewalInfo(obj) {
1306
- _validate("renewalInfo", obj);
1307
- var w = obj.suggestedWindow;
1308
- if (!_isObject(w) || !_isRfc3339(w.start) || !_isRfc3339(w.end)) throw E("acme/bad-renewal-window", "a renewalInfo suggestedWindow must carry RFC 3339 start and end (RFC 9773 sec. 4.2)");
1418
+ /** @internal ONE reading of the object, shared with `_validate`, and one read of each bound inside the
1419
+ * window. The window was read twice, once by the field walk and once here, so an accessor could pass
1420
+ * the walk's type check and hand this a non-object, which reached `.start` and raised a bare
1421
+ * TypeError; and each bound was read twice more, for its RFC 3339 test and then for the comparison
1422
+ * that orders the two. */
1423
+ if (!_isObject(obj)) throw E("acme/bad-renewal-info", "an ACME renewalInfo must be a JSON object");
1424
+ var read = _readOnce(obj, SPEC_NAMES.renewalInfo);
1425
+ _validate("renewalInfo", obj, read);
1426
+ var w = read.suggestedWindow;
1427
+ var wStart = _isObject(w) ? w.start : undefined;
1428
+ var wEnd = _isObject(w) ? w.end : undefined;
1429
+ if (!_isObject(w) || !_isRfc3339(wStart) || !_isRfc3339(wEnd)) throw E("acme/bad-renewal-window", "a renewalInfo suggestedWindow must carry RFC 3339 start and end (RFC 9773 sec. 4.2)");
1309
1430
  // allow:nan-date-comparison-unguarded -- source-validated by the rfc3339.isValid check above.
1310
- if (Date.parse(w.end) <= Date.parse(w.start)) throw E("acme/bad-renewal-window", "the renewal window end must be strictly after start (RFC 9773 sec. 4.2)");
1431
+ if (Date.parse(wEnd) <= Date.parse(wStart)) throw E("acme/bad-renewal-window", "the renewal window end must be strictly after start (RFC 9773 sec. 4.2)");
1311
1432
  return obj;
1312
1433
  }
1313
1434
 
@@ -1742,6 +1863,19 @@ function _gateOpts(bag, whitelist, name) {
1742
1863
  guard.identifier.assertKnownKeys(bag, whitelist, E, "acme/bad-input", "unknown " + name + " option ");
1743
1864
  return bag;
1744
1865
  }
1866
+ /** @internal The options gate for the module-level request builders, which take no option whitelist. It is
1867
+ * `optionsObject` alone, and what that buys is the REFUSAL of an accessor-backed field.
1868
+ *
1869
+ * These verbs validate a member and then serialize it into the payload they SIGN, and each read of an
1870
+ * accessor answers separately: the RFC 5280 CRLReason whitelist in `revokeCert` ran on one reading while
1871
+ * the signed request carried another, so the single value the whitelist exists to exclude could reach the
1872
+ * CA; the same gap put unvalidated contacts, identifiers and dates into signed `newAccount`, `newOrder`
1873
+ * and `updateAccount` payloads. The client methods were never exposed, because they already pass their
1874
+ * options through `_gateOpts` and this same refusal. Routing these through it is the module keeping one
1875
+ * rule rather than two. */
1876
+ function _gateRequestOpts(bag, name) {
1877
+ return guard.identifier.optionsObject(bag, E, "acme/bad-input", "pki.acme " + name + " options");
1878
+ }
1745
1879
  /** @internal Refused at the door and again where the value is used. The checked value is RETURNED
1746
1880
  * and it is that one the caller uses: reading the option again would ask a second time, and an
1747
1881
  * accessor can answer differently on each read. */
@@ -1845,8 +1979,8 @@ function client(directoryUrl, opts) {
1845
1979
  if (typeof v !== "string" || v === "") return;
1846
1980
  try { jose.base64url.decode(v); }
1847
1981
  catch (_e) { return; }
1848
- while (nonces.length >= CLIENT_MAX_NONCE_POOL) nonces.shift();
1849
- nonces.push(v);
1982
+ while (nonces.length >= CLIENT_MAX_NONCE_POOL) _shift(nonces);
1983
+ _push(nonces, v);
1850
1984
  }
1851
1985
  function _isProblem(res) { return intrinsic.stringIndexOf(_lower(String(res.headers["content-type"] || "")), "application/problem+json") !== -1; }
1852
1986
  function _serverProblem(res) {
@@ -1870,25 +2004,31 @@ function client(directoryUrl, opts) {
1870
2004
  return guard.async.invoked(transport, [{ method: method, url: url, headers: reqHeaders, body: body, tls: _tlsFor(url), proxy: budgets.proxy, timeout: budgets.timeout, maxResponseBytes: budgets.maxResponseBytes }], E, "acme/bad-input", "opts.transport");
1871
2005
  }).then(function (res) {
1872
2006
  res = res || {};
2007
+ /** @internal The transport is a caller-supplied option, so its response is a caller object: the body
2008
+ * and the status are read ONCE here and every check and the returned value read those locals. Read
2009
+ * per use, the body whose length was capped need not be the body handed back, which is how a
2010
+ * response larger than maxResponseBytes would pass the cap. */
2011
+ var rbody = res.body;
2012
+ var status = res.status;
1873
2013
  var h = guard.header.lowerCased(res.headers);
1874
- var blen = res.body == null ? 0 : (typeof res.body === "string" ? Buffer.byteLength(res.body, "utf8")
1875
- : (Buffer.isBuffer(res.body) ? guard.bytes.lengthOf(res.body) : guard.bytes.lengthOf(guard.bytes.source(res.body, E, "acme/bad-response", "the response body"))));
2014
+ var blen = rbody == null ? 0 : (typeof rbody === "string" ? Buffer.byteLength(rbody, "utf8")
2015
+ : (Buffer.isBuffer(rbody) ? guard.bytes.lengthOf(rbody) : guard.bytes.lengthOf(guard.bytes.source(rbody, E, "acme/bad-response", "the response body"))));
1876
2016
  if (blen > budgets.maxResponseBytes) throw E("acme/response-too-large", "the response body (" + blen + " bytes) exceeds the " + budgets.maxResponseBytes + "-byte cap");
1877
- if ((method === "GET" || method === "HEAD") && res.status >= 300 && res.status < 400) {
2017
+ if ((method === "GET" || method === "HEAD") && status >= 300 && status < 400) {
1878
2018
  if (redirects >= budgets.maxRedirects) throw E("acme/too-many-redirects", "the redirect budget of " + budgets.maxRedirects + " was exceeded");
1879
- if (!_isString(h["location"])) throw E("acme/bad-redirect", "a " + res.status + " redirect carried no Location header (RFC 7231 sec. 6.4)");
2019
+ if (!_isString(h["location"])) throw E("acme/bad-redirect", "a " + status + " redirect carried no Location header (RFC 7231 sec. 6.4)");
1880
2020
  var next;
1881
2021
  try { next = new URL(h["location"], url).href; }
1882
2022
  catch (e) { throw E("acme/bad-redirect", "the redirect Location did not resolve to a valid URL", e); }
1883
2023
  return _sendFollowing(method, _clientUrl(next), headers, body, redirects + 1);
1884
2024
  }
1885
2025
  _harvestNonce(h["replay-nonce"]);
1886
- return { status: res.status, headers: h, body: res.body == null ? "" : res.body };
2026
+ return guard.verdict.of({ status: status, headers: h, body: rbody == null ? "" : rbody });
1887
2027
  });
1888
2028
  }
1889
2029
 
1890
2030
  function _getDirectory() {
1891
- if (dirCache) return Promise.resolve(dirCache);
2031
+ if (dirCache) return _resolve(_Promise, dirCache);
1892
2032
  return _send("GET", dirUrl, { accept: "application/json" }, null).then(function (res) {
1893
2033
  if (res.status !== 200) throw E("acme/bad-directory", "the ACME directory request returned HTTP " + res.status);
1894
2034
  var obj = _json(res);
@@ -1904,12 +2044,12 @@ function client(directoryUrl, opts) {
1904
2044
  });
1905
2045
  }
1906
2046
  function _takeNonce() {
1907
- if (nonces.length) return Promise.resolve(nonces.pop());
2047
+ if (nonces.length) return _resolve(_Promise, _pop(nonces));
1908
2048
  var before = nonces.length;
1909
2049
  return _resource("newNonce").then(function (nn) { return _send("HEAD", nn, {}, null); }).then(function (res) {
1910
2050
  if (res.status !== 200 && res.status !== 204) { nonces.length = before; throw _serverProblem(res); }
1911
2051
  if (!nonces.length) throw E("acme/no-nonce", "the server did not provide a usable Replay-Nonce (RFC 8555 sec. 7.2)");
1912
- return nonces.pop();
2052
+ return _pop(nonces);
1913
2053
  });
1914
2054
  }
1915
2055
 
@@ -1976,7 +2116,7 @@ function client(directoryUrl, opts) {
1976
2116
  });
1977
2117
  }
1978
2118
  function _newAuthz(identifier) {
1979
- return Promise.resolve().then(function () {
2119
+ return _resolve(_Promise, undefined).then(function () {
1980
2120
  var canon = _validateIdentifier(identifier);
1981
2121
  return _resource("newAuthz").then(function (url) {
1982
2122
  return _post(url, newAuthz, { identifier: canon }, "kid", undefined, [200, 201]).then(function (res) {
@@ -2010,7 +2150,7 @@ function client(directoryUrl, opts) {
2010
2150
  capPolls = budget.maxPolls != null ? guard.limits.cap(budget.maxPolls, "maxPolls", maxPolls, { E: E, code: "acme/bad-input", min: 1, max: 1000 }) : maxPolls;
2011
2151
  capWait = budget.maxTotalWait != null ? guard.limits.cap(budget.maxTotalWait, "maxTotalWait", maxTotalWait, { E: E, code: "acme/bad-input", min: 0, max: retryAfter.MAX_RETRY_AFTER_SECONDS }) : maxTotalWait;
2012
2152
  url = _clientUrl(url);
2013
- } catch (e) { return Promise.reject(e); }
2153
+ } catch (e) { return _reject(_Promise, e); }
2014
2154
  var terminal = kind === "order" ? { ready: true, valid: true, invalid: true } : { valid: true, invalid: true, deactivated: true, expired: true, revoked: true };
2015
2155
  var last = null, count = 0, waited = 0;
2016
2156
  function step() {
@@ -2027,7 +2167,7 @@ function client(directoryUrl, opts) {
2027
2167
  waited += delaySec;
2028
2168
  if (waited > capWait) throw E("acme/poll-exhausted", "the " + kind + " poll exceeded the total wait budget of " + capWait + " seconds");
2029
2169
  if (typeof budget.onRetryAfter === "function") budget.onRetryAfter(delaySec);
2030
- return Promise.resolve(sleep(delaySec * constants.TIME.seconds(1))).then(step);
2170
+ return _resolve(_Promise, sleep(delaySec * constants.TIME.seconds(1))).then(step);
2031
2171
  });
2032
2172
  }
2033
2173
  return step();
@@ -2092,7 +2232,7 @@ function client(directoryUrl, opts) {
2092
2232
  }
2093
2233
 
2094
2234
  function _downloadCertificate(url, opts) {
2095
- return Promise.resolve().then(function () {
2235
+ return _resolve(_Promise, undefined).then(function () {
2096
2236
  opts = _gateOpts(opts, _METHOD_OPTS.download, "downloadCertificate");
2097
2237
  var select = opts.selectChain;
2098
2238
  if (select !== undefined && typeof select !== "function") throw E("acme/bad-input", "downloadCertificate opts.selectChain must be a function");
@@ -2111,7 +2251,7 @@ function client(directoryUrl, opts) {
2111
2251
  boundToKey: binding.spki !== undefined, boundToIdentifiers: binding.ids !== undefined };
2112
2252
  }
2113
2253
  if (!select) return result(primary);
2114
- return Promise.resolve(select(primary)).then(function (primaryMatch) {
2254
+ return _resolve(_Promise, select(primary)).then(function (primaryMatch) {
2115
2255
  if (primaryMatch) return result(primary);
2116
2256
  if (linkError) throw linkError;
2117
2257
  var leaf = primary.certificate, toFetch = alternates.slice(0, maxAlt), overBudget = alternates.length > maxAlt, idx = 0;
@@ -2122,7 +2262,7 @@ function client(directoryUrl, opts) {
2122
2262
  }
2123
2263
  return _fetchCertChain(toFetch[idx++]).then(function (cand) {
2124
2264
  if (!cand.certificate.equals(leaf)) throw E("acme/bad-alternate", "an alternate chain did not start with the same end-entity certificate (RFC 8555 sec. 7.4.2)");
2125
- return Promise.resolve(select(cand)).then(function (m) { return m ? result(cand) : next(); });
2265
+ return _resolve(_Promise, select(cand)).then(function (m) { return m ? result(cand) : next(); });
2126
2266
  });
2127
2267
  }
2128
2268
  return next();
@@ -2243,13 +2383,22 @@ function client(directoryUrl, opts) {
2243
2383
  }
2244
2384
 
2245
2385
  function _renewalWindow(certDer, o) {
2246
- return Promise.resolve().then(function () {
2386
+ return _resolve(_Promise, undefined).then(function () {
2247
2387
  o = _gateOpts(o, _METHOD_OPTS.renewalWindow, "renewalWindow");
2248
2388
  if (!guard.bytes.isByteSource(certDer)) throw E("acme/bad-input", "renewalWindow requires a DER certificate BufferSource");
2249
2389
  if (o.random !== undefined && typeof o.random !== "function") throw E("acme/bad-input", "renewalWindow opts.random must be a function returning a number in [0, 1]");
2250
2390
  if (o.clock !== undefined && typeof o.clock !== "function") throw E("acme/bad-input", "renewalWindow opts.clock must be a function returning epoch milliseconds");
2251
2391
  if (o.replaced !== undefined && typeof o.replaced !== "boolean") throw E("acme/bad-input", "renewalWindow opts.replaced must be a boolean");
2252
2392
  if (o.previous !== undefined && !_isObject(o.previous)) throw E("acme/bad-input", "renewalWindow opts.previous must be a prior renewalWindow result object");
2393
+ /** @internal `previous` is READ ONCE, here, before the await below, and the stickiness decision
2394
+ * reads this reading. It was validated as an object here and then read three more times after the
2395
+ * renewalInfo fetch: `o.previous.suggestedWindow` twice in one expression, and
2396
+ * `o.previous.selectedTime` once for its string test and again for the parse. An accessor answers
2397
+ * each read separately and the fetch is the window in which to change its answer, so the prior
2398
+ * window this compares against need not be the one the caller validated, and a non-object arriving
2399
+ * after the await reached `.start` and raised a bare TypeError. */
2400
+ var prev = o.previous === undefined ? undefined : _readOnce(o.previous, PREVIOUS_WINDOW_NAMES);
2401
+ var prevWindow = prev && _isObject(prev.suggestedWindow) ? _readOnce(prev.suggestedWindow, WINDOW_BOUND_NAMES) : null;
2253
2402
  var _clk = typeof o.clock === "function" ? o.clock : clock;
2254
2403
  function clk() { var t = _clk(); if (typeof t !== "number" || !isFinite(t)) throw E("acme/bad-input", "the renewalWindow clock returned a non-finite value"); return t; }
2255
2404
  var notAfter = x509.parse(certDer).validity.notAfter;
@@ -2262,10 +2411,10 @@ function client(directoryUrl, opts) {
2262
2411
  var startMs = Date.parse(w.start), endMs = Date.parse(w.end);
2263
2412
  var notAfterMs = guard.time.instantOf(notAfter);
2264
2413
  var effEnd = Math.min(endMs, notAfterMs), effStart = Math.min(startMs, effEnd);
2265
- var pw = o.previous && _isObject(o.previous.suggestedWindow) ? o.previous.suggestedWindow : null;
2414
+ var pw = prevWindow;
2266
2415
  var selectedMs;
2267
- if (pw && pw.start === w.start && pw.end === w.end && _isString(o.previous.selectedTime)) {
2268
- var prevMs = Date.parse(o.previous.selectedTime);
2416
+ if (pw && pw.start === w.start && pw.end === w.end && _isString(prev.selectedTime)) {
2417
+ var prevMs = Date.parse(prev.selectedTime);
2269
2418
  if (!isFinite(prevMs)) throw E("acme/bad-input", "renewalWindow opts.previous.selectedTime must be an RFC 3339 date-time");
2270
2419
  selectedMs = Math.max(effStart, Math.min(effEnd, prevMs));
2271
2420
  } else {
@@ -2289,7 +2438,7 @@ function client(directoryUrl, opts) {
2289
2438
  }
2290
2439
 
2291
2440
  function _scheduleRenewal(certDer, o) {
2292
- return _resolve(Promise).then(function () {
2441
+ return _resolve(_Promise).then(function () {
2293
2442
  o = _gateOpts(o, _METHOD_OPTS.scheduleRenewal, "scheduleRenewal");
2294
2443
  if (!guard.bytes.isByteSource(certDer)) throw E("acme/bad-input", "scheduleRenewal requires a DER certificate BufferSource");
2295
2444
  if (o.random !== undefined && typeof o.random !== "function") throw E("acme/bad-input", "scheduleRenewal opts.random must be a function returning a number in [0, 1]");
@@ -2308,7 +2457,7 @@ function client(directoryUrl, opts) {
2308
2457
  function _clk() { var t = clock(); if (typeof t !== "number" || !intrinsic.isFinite(t)) throw E("acme/bad-input", "the scheduleRenewal clock returned a non-finite value"); return t; }
2309
2458
  var startMs = _clk(), checks = 0, tempBackoffMs = tempBaseMs, previous = null;
2310
2459
  var cert = guard.bytes.snapshotSource(certDer, AcmeError, "acme/bad-input", "the scheduleRenewal certificate");
2311
- function _boundedSleep(ms) { if (maxWaitMs != null) { ms = intrinsic.min(ms, intrinsic.max(0, maxWaitMs - (_clk() - startMs))); } return _resolve(Promise, sleep(ms)); }
2460
+ function _boundedSleep(ms) { if (maxWaitMs != null) { ms = intrinsic.min(ms, intrinsic.max(0, maxWaitMs - (_clk() - startMs))); } return _resolve(_Promise, sleep(ms)); }
2312
2461
  function _sleepThenLoop(ms) { return (maxChecks != null && checks >= maxChecks) ? { reason: "budget" } : _boundedSleep(ms).then(loop); }
2313
2462
  function _stop() {
2314
2463
  if (!o.shouldStop) return false;
@@ -2333,7 +2482,7 @@ function client(directoryUrl, opts) {
2333
2482
  var _rc;
2334
2483
  try { _rc = o.renew(decision); }
2335
2484
  catch (e) { throw E("acme/bad-input", "the scheduleRenewal renew callback threw", e); }
2336
- return _resolve(Promise, _rc).then(function (next) {
2485
+ return _resolve(_Promise, _rc).then(function (next) {
2337
2486
  if (next == null) return { reason: "renewed", decision: decision };
2338
2487
  if (!guard.bytes.isByteSource(next)) throw E("acme/bad-input", "scheduleRenewal opts.renew must return a DER certificate BufferSource or a nullish value");
2339
2488
  cert = guard.bytes.snapshotSource(next, AcmeError, "acme/bad-input", "the scheduleRenewal renewed certificate"); previous = null; return loop();