@blamejs/pki 0.9.5 → 0.9.7

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 (47) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.md +7 -6
  3. package/bin/pki.js +110 -34
  4. package/lib/acme.js +91 -46
  5. package/lib/asn1-der.js +6 -5
  6. package/lib/attrcert-sign.js +4 -3
  7. package/lib/cmp-build.js +16 -6
  8. package/lib/cmp-session.js +26 -3
  9. package/lib/cmp-verify.js +24 -2
  10. package/lib/cms-compress.js +3 -3
  11. package/lib/cms-digest.js +3 -3
  12. package/lib/cms-verify.js +18 -8
  13. package/lib/compromised-keys.js +114 -0
  14. package/lib/constants.js +1 -0
  15. package/lib/crl-sign.js +3 -3
  16. package/lib/crmf-sign.js +2 -0
  17. package/lib/csr-sign.js +5 -1
  18. package/lib/ct.js +16 -3
  19. package/lib/est.js +44 -29
  20. package/lib/framework-error.js +20 -7
  21. package/lib/guard-all.js +2 -0
  22. package/lib/guard-distrust.js +344 -0
  23. package/lib/guard-json.js +1 -1
  24. package/lib/guard-verdict.js +16 -3
  25. package/lib/http-transport.js +412 -41
  26. package/lib/jose.js +1 -1
  27. package/lib/lint.js +49 -7
  28. package/lib/ocsp-verify.js +9 -3
  29. package/lib/ocsp.js +8 -4
  30. package/lib/oid.js +4 -0
  31. package/lib/path-validate.js +131 -16
  32. package/lib/possession.js +6 -2
  33. package/lib/related-cert.js +2 -2
  34. package/lib/revocation-fetch.js +39 -9
  35. package/lib/scep.js +28 -13
  36. package/lib/schema-pkix.js +3 -3
  37. package/lib/sigstore.js +11 -6
  38. package/lib/smime.js +18 -13
  39. package/lib/tls-cert-compress.js +6 -5
  40. package/lib/trust.js +159 -20
  41. package/lib/trustanchor-build.js +16 -1
  42. package/lib/tsp-sign.js +7 -1
  43. package/lib/webauthn-mds.js +30 -8
  44. package/lib/webauthn.js +46 -14
  45. package/lib/x509-sign.js +16 -5
  46. package/package.json +1 -1
  47. package/sbom.cdx.json +6 -6
package/lib/cms-digest.js CHANGED
@@ -51,7 +51,7 @@ var _DIGEST_OPTS = intrinsic.assign(intrinsic.create(null), {
51
51
  var _VERIFY_DIGEST_OPTS = intrinsic.assign(intrinsic.create(null), { content: 1 });
52
52
 
53
53
  function _digestName(opts) {
54
- var name = opts.digestAlgorithm === undefined ? "sha256" : opts.digestAlgorithm;
54
+ var name = opts.digestAlgorithm == null ? "sha256" : opts.digestAlgorithm;
55
55
  var key = guard.text.keyOf(name);
56
56
  if (!NODE_DIGEST[key]) {
57
57
  throw _err("cms/unsupported-algorithm", "unsupported digest algorithm " + guard.text.showValue(name) +
@@ -101,7 +101,7 @@ async function _digest(content, opts) {
101
101
  "pki.cms.digest opts.detached");
102
102
  var raw = guard.bytes.source(content, CmsError, "cms/bad-input", "content");
103
103
  var alg = _digestName(opts);
104
- var ctName = opts.contentType === undefined ? "data" : opts.contentType;
104
+ var ctName = opts.contentType == null ? "data" : opts.contentType;
105
105
  var ctOid = O(ctName);
106
106
  if (!ctOid) throw _err("cms/bad-input", "opts.contentType is not a known OID name: " + guard.text.showValue(ctName));
107
107
 
@@ -169,7 +169,7 @@ async function _verifyDigest(input, opts) {
169
169
  guard.der.assertParams(parsed.digestAlgorithm.parameters, "absentOrNull", _err,
170
170
  "cms/unsupported-algorithm", parsed.digestAlgorithm.name + " digest algorithm (RFC 5754 sec. 2)");
171
171
  var embedded = parsed.encapContentInfo.eContent;
172
- var supplied = opts.content === undefined ? null
172
+ var supplied = opts.content == null ? null
173
173
  : guard.bytes.snapshotSource(opts.content, CmsError, "cms/bad-input", "opts.content");
174
174
  if (embedded != null && supplied != null) {
175
175
  throw _err("cms/bad-input", "the DigestedData carries its own content, so opts.content is redundant");
package/lib/cms-verify.js CHANGED
@@ -761,6 +761,7 @@ async function _verifyOneTimestamp(attr, targetSig, claimedSigningTime, opts) {
761
761
  trustAnchors: opts.timestampTrustAnchors,
762
762
  revocationChecker: opts.timestampRevocationChecker,
763
763
  allowWeakDigests: opts.allowWeakDigests === true,
764
+ distrustedKeys: opts.distrustedKeys,
764
765
  });
765
766
  } catch (e) {
766
767
  return guard.verdict.of({ valid: false, trusted: false, genTime: null,
@@ -975,6 +976,10 @@ async function _verifyOneCountersig(vDer, targetSig, parsedCerts, allowWeakDiges
975
976
  * trust decision from the signer: an operator may anchor one and not the other.
976
977
  * @opts timestampRevocationChecker A revocation checker for the TSA chain, as `pki.path.validate`
977
978
  * takes one. Each row reports what it established in `revocationChecked`.
979
+ * @opts distrustedKeys SHA-256 digests of SubjectPublicKeyInfo DER (64 hexadecimal characters or
980
+ * 32 bytes each, see `pki.trust.spkiDigest`), applied to the signer chain and to the
981
+ * TSA chain of every timestamp row. A listed key on a chain, the anchor included,
982
+ * reports that signer or that row as not trusted.
978
983
  * @opts requireAlgorithmProtection Fail a signer that carries no RFC 6211
979
984
  * `CMSAlgorithmProtection` attribute, with `cms/missing-algorithm-protection`. Off
980
985
  * by default: RFC 6211 says nothing about absence and RFC 8933 sec. 4.1 makes
@@ -1026,7 +1031,7 @@ var _VERIFY_OPTS = intrinsic.assign(intrinsic.create(null), { certs: 1, content:
1026
1031
  * leaving the signer unanchored, or the reverse, and one `trustAnchors` for both would make those
1027
1032
  * two answers one. */
1028
1033
  timestampCerts: 1, timestampTrustAnchors: 1, timestampRevocationChecker: 1,
1029
- requireAlgorithmProtection: 1 });
1034
+ requireAlgorithmProtection: 1, distrustedKeys: 1 });
1030
1035
 
1031
1036
  function verify(input, opts) {
1032
1037
  return guard.async.deferred(function () { return _verify(input, opts); });
@@ -1043,12 +1048,15 @@ async function _verify(input, opts) {
1043
1048
  var allowWeakDigests = opts.allowWeakDigests === true;
1044
1049
  var requireProtection = guard.identifier.booleanOption(opts.requireAlgorithmProtection, _err,
1045
1050
  "cms/bad-input", "pki.cms.verify opts.requireAlgorithmProtection");
1051
+ /** @internal Typed here, before any signer is judged: the signer chain is built only for a signer
1052
+ * that verified, and every throw from a timestamp verify becomes a row. One list serves both. */
1053
+ var distrustedKeys = guard.distrust.keys(opts.distrustedKeys, _err, "cms/bad-input", "pki.cms.verify opts.distrustedKeys");
1046
1054
  var parsed = guard.parsed.acceptDerived(input, "cms", function (bytes) {
1047
1055
  return cms.parse(_snapshotIfBytes(bytes, "pki.cms.verify"));
1048
1056
  }, _err, "cms/bad-input", "the SignedData");
1049
1057
  if (!Array.isArray(parsed.signerInfos)) throw _err("cms/bad-input", "input is not a CMS SignedData");
1050
- var trustCfg = _snapshotTrust(opts);
1051
- var tsTrustCfg = _snapshotTimestampTrust(opts, allowWeakDigests);
1058
+ var trustCfg = _snapshotTrust(opts, distrustedKeys);
1059
+ var tsTrustCfg = _snapshotTimestampTrust(opts, allowWeakDigests, distrustedKeys);
1052
1060
  var parsedCerts = [];
1053
1061
  (parsed.certificates || []).forEach(function (c) { _addCert(parsedCerts, c && c.bytes ? c.bytes : c); });
1054
1062
  (opts.certs || []).forEach(function (c) { _addCert(parsedCerts, c); });
@@ -1130,7 +1138,7 @@ function _cloneAnchorValue(v, depth) {
1130
1138
  }
1131
1139
  return out;
1132
1140
  }
1133
- function _snapshotTrust(opts) {
1141
+ function _snapshotTrust(opts, distrustedKeys) {
1134
1142
  var raw = opts.trustAnchors;
1135
1143
  var anchors = null;
1136
1144
  if (raw != null) {
@@ -1140,9 +1148,10 @@ function _snapshotTrust(opts) {
1140
1148
  }
1141
1149
  return {
1142
1150
  trustAnchors: anchors,
1143
- time: guard.time.isDate(opts.time) ? new intrinsic.Date(guard.time.instantOf(opts.time)) : (opts.time === undefined ? new intrinsic.Date() : opts.time),
1151
+ time: guard.time.isDate(opts.time) ? new intrinsic.Date(guard.time.instantOf(opts.time)) : (opts.time == null ? new intrinsic.Date() : opts.time),
1144
1152
  requiredEku: Array.isArray(opts.requiredEku) ? guard.list.snapshot(opts.requiredEku) : opts.requiredEku,
1145
1153
  checkPurpose: opts.checkPurpose,
1154
+ distrustedKeys: distrustedKeys,
1146
1155
  };
1147
1156
  }
1148
1157
 
@@ -1153,7 +1162,7 @@ function _snapshotTrust(opts) {
1153
1162
  * authorities. The certificates are copied by value, since a caller holding the array could swap an
1154
1163
  * entry after the row that read it. The checker is a function and is captured as the one reference
1155
1164
  * every row calls. */
1156
- function _snapshotTimestampTrust(opts, allowWeakDigests) {
1165
+ function _snapshotTimestampTrust(opts, allowWeakDigests, distrustedKeys) {
1157
1166
  var certs = opts.timestampCerts;
1158
1167
  var anchors = opts.timestampTrustAnchors;
1159
1168
  return {
@@ -1167,6 +1176,7 @@ function _snapshotTimestampTrust(opts, allowWeakDigests) {
1167
1176
  }),
1168
1177
  timestampRevocationChecker: opts.timestampRevocationChecker,
1169
1178
  allowWeakDigests: allowWeakDigests,
1179
+ distrustedKeys: distrustedKeys,
1170
1180
  };
1171
1181
  }
1172
1182
 
@@ -1200,7 +1210,7 @@ async function _applyTrust(res, parsedCerts, cfg) {
1200
1210
  if (!anchors.length) {
1201
1211
  throw _err("cms/bad-input", "opts.trustAnchors is empty -- name at least one root, or omit it to state that the signer is not being anchored");
1202
1212
  }
1203
- if (cfg.time !== undefined) guard.time.assertValid(cfg.time, _err, "cms/bad-input", "opts.time");
1213
+ if (cfg.time != null) guard.time.assertValid(cfg.time, _err, "cms/bad-input", "opts.time");
1204
1214
  var purposes = _engine.resolvePurposeOpts({ requiredEku: cfg.requiredEku, checkPurpose: cfg.checkPurpose });
1205
1215
  intrinsic.forEach(anchors, function (a) {
1206
1216
  _engine.toAnchor(a);
@@ -1210,7 +1220,7 @@ async function _applyTrust(res, parsedCerts, cfg) {
1210
1220
  var s = res.signers[_si];
1211
1221
  {
1212
1222
  if (s.ok !== true || !s.cert) { s.trusted = false; continue; }
1213
- var buildOpts = { trustAnchors: anchors, intermediates: pool, validate: true, time: at };
1223
+ var buildOpts = { trustAnchors: anchors, intermediates: pool, validate: true, time: at, distrustedKeys: cfg.distrustedKeys };
1214
1224
  if (cfg.requiredEku != null) buildOpts.requiredEku = cfg.requiredEku;
1215
1225
  if (cfg.checkPurpose != null) buildOpts.checkPurpose = cfg.checkPurpose;
1216
1226
  if (!_keyUsagePermitsSigning(parsedCerts, s.cert)) { s.trusted = false; continue; }
@@ -0,0 +1,114 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) blamejs contributors
3
+ "use strict";
4
+ // @internal
5
+
6
+ /** @internal The keys every verifier in this toolkit refuses: certificate keys whose private key is
7
+ * public, whether published or derivable from the public key. Each entry is the SHA-256 digest of
8
+ * the key's SubjectPublicKeyInfo in canonical form (the value `pki.trust.spkiDigest` returns), the
9
+ * subject common name of a certificate that carries it (the product's CA name for a key published
10
+ * without a certificate), why the private key is public, and where that is recorded. An entry is added only when its private key has been recovered or obtained and
11
+ * checked against the public key. `pki.trust.knownCompromisedKeys()` returns a copy. */
12
+ var _freeze = require("./guard-intrinsic").freeze;
13
+
14
+ function _entry(digest, name, reason, sources) {
15
+ return _freeze({ spkiSha256: digest, name: name, reason: reason, sources: _freeze(sources) });
16
+ }
17
+
18
+ var EMUDHRA_SOURCES = [
19
+ "https://bugzilla.mozilla.org/show_bug.cgi?id=2083449",
20
+ "https://blog.cloudflare.com/emudhra-certificate-compromise/",
21
+ ];
22
+ var EMUDHRA_REASON = "the P-384 private key is an integer below 256, which anyone can recover from the public " +
23
+ "key by trying each candidate; eMudhra and Sectigo both issued CA certificates for the key";
24
+ function _emudhra(digest, name) { return _entry(digest, name, EMUDHRA_REASON, EMUDHRA_SOURCES); }
25
+
26
+ var KOMODIA_SOURCES = [
27
+ "https://gist.github.com/Wack0/17c56b77a90073be81d3",
28
+ "https://www.kb.cert.org/vuls/id/529496",
29
+ "https://github.com/hannob/superfishy/tree/afbad9c2635bfd5c4bfa2630c27263f59efecd86/certificates",
30
+ ];
31
+ function _komodia(digest, name, product) {
32
+ return _entry(digest, name, product + " uses the Komodia interception library and installs this root with one " +
33
+ "private key built into the software; the key was published in February 2015", KOMODIA_SOURCES);
34
+ }
35
+
36
+ var ADGUARD_SOURCES = [
37
+ "https://blog.hboeck.de/archives/874-More-TLS-Man-in-the-Middle-failures-Adguard,-Privdog-again-and-ProtocolFilters.dll.html",
38
+ "https://github.com/hannob/superfishy/tree/afbad9c2635bfd5c4bfa2630c27263f59efecd86/certificates",
39
+ ];
40
+ function _adguard(digest, n) {
41
+ return _entry(digest, n === 3 ? "Adguard CA R73" : "Adguard CA", "Adguard ships ten CA private keys in ProtocolFilters.dll and certifies one of " +
42
+ "them as its root on each installation; this is key " + n + " of the ten, and all ten were published in 2015", ADGUARD_SOURCES);
43
+ }
44
+
45
+ var DELL_SOURCES = [
46
+ "https://blog.hboeck.de/archives/876-Superfish-2.0-Dangerous-Certificate-on-Dell-Laptops-breaks-encrypted-HTTPS-Connections.html",
47
+ "https://bugzilla.mozilla.org/show_bug.cgi?id=1227258",
48
+ ];
49
+
50
+ var KEYS = _freeze([
51
+ _emudhra("ebed6475c2c2d80fcb199f3fe8dd6e6b3ce60b77eb15d4e42c3dec0c6f5b5105", "EM DV TLS CA - G3B"),
52
+ _emudhra("43bffa19d6ef651451f6841e019931e96f6f2eaeff28ab4b6f1ce4576de2717b", "EM OV TLS CA - G3B"),
53
+ _emudhra("e0254c7fcdc265e2f0f8a428dabb75ab4ae039b4872d62f5ee57f0877d5a8a41", "EM EV TLS CA - G3B"),
54
+ _emudhra("49dd353dc2b919a3770645117d0e60c16115e7d1bc259eefe734b00ea434fe5f", "EM DV TLS CA - G3C"),
55
+ _emudhra("63ecefee431d49523bc1136c4e72d0206a477086caf606cd5cf025686c109cf8", "EM OV TLS CA - G3C"),
56
+ _emudhra("5c0660a2b164910d8ff589c18e14552e9e0ec237dcefe2cb736d41a4811f81a0", "EM EV TLS CA - G3C"),
57
+ _emudhra("ec4ffa1ad0ac88081314b7aaffef6a91f97cd57291bb5ba686c5415f5eb4f6b6", "EM OV CS CA - G3B"),
58
+ _emudhra("92c063d67e8f14db71d288e11266ffdaf69cbf115f7bab14f3bc2f68d25e0e30", "EM OV CS CA - G3C"),
59
+
60
+ _entry("4bb8f35ba1e126f8dde1b0c420625ed86dce61a7bddadbdea9aba578ff13145e", "Superfish, Inc.",
61
+ "Superfish Visual Discovery, preinstalled on Lenovo consumer laptops, installs this root with one private key " +
62
+ "built into the software; the key and its password were published in February 2015", [
63
+ "https://blog.erratasec.com/2015/02/extracting-superfish-certificate.html",
64
+ "https://www.kb.cert.org/vuls/id/529496",
65
+ "https://github.com/hannob/superfishy/tree/afbad9c2635bfd5c4bfa2630c27263f59efecd86/certificates",
66
+ ]),
67
+ _komodia("2698846608c82e14834b0a99da116097801ae712b1637897c8d305ee1f29b9ce", "KeepMyFamilySecure", "Keep My Family Secure"),
68
+ _komodia("c35169fdc7781d149081f60d6e81f259bd504ce19b53a0370d1dbabecb195437", "Qustodio", "Qustodio for Windows"),
69
+ _komodia("0ab38aff3afae30102ee3713bade6b83c9d27cd78045a9a0372b92031274b48b", "Qustodio CA", "Qustodio for macOS"),
70
+ _komodia("338124a8fdeef46002656293c677a0f73c69b664f847915d20fd6431851df423", "Kurupira.NET", "Kurupira WebFilter"),
71
+ _komodia("eb6aaaedf0a08c2e20a07f5d97125ba66c97f52994f1791462791f4f98af6baa", "AtomPark Software Inc", "StaffCop"),
72
+ _komodia("163b33dd8ee4f658fbbd681cef3abb091d890b39f0fc78289a1d1f7b8d39000f", "EasyTech", "Easy Hide IP Classic"),
73
+ _komodia("aabf9d9003d73799f3c6cc1577ab2b109dbcbf6371eef1e633b9e36bea563db0", "Lavasoft Limited", "Ad-Aware Web Companion"),
74
+ _komodia("5305c2c7f001e9c18269be5d914bc0c767e953d5f8540f2f0db27244c010ff37", "Sendori, Inc", "Sendori and PureLeads"),
75
+ _komodia("6a743e1ff94f445184971182955d919232085a0276e5c6dd9dfff22b367cb7e5", "InfoWeise", "SecureTeen"),
76
+ _komodia("30f397f1523cdd1c1511144ea2c37638ba779b8c49c30d08f682f878bc0c8cbf", "ImpresX OU", "DiscountCow"),
77
+ _komodia("86d8efafc3ede0edaa22a99ea8bd78e0decbae6585da94b438a2af1632e52a83", "Objectify Media Inc", "WebProtect"),
78
+ _komodia("37f8ccfa97ee243ff7ba4e799899bd6524355ee72a0ec9d90ae7cefb81409174", "Covenant Eyes", "Covenant Eyes"),
79
+
80
+ _entry("10900ceb22cbcef9e431f6801225df3755f7a75eae0e22f164cae1cbbc81424c", "PrivDog Secure Connection Inspector CA",
81
+ "PrivDog installs this root with the same private key on every installation; the key was published in 2015", [
82
+ "https://blog.hboeck.de/archives/874-More-TLS-Man-in-the-Middle-failures-Adguard,-Privdog-again-and-ProtocolFilters.dll.html",
83
+ "https://github.com/hannob/superfishy/tree/afbad9c2635bfd5c4bfa2630c27263f59efecd86/certificates",
84
+ ]),
85
+ _adguard("f2971080d115de5d766fcf121561a591b20aa053846dc5a45f201080016c69db", 0),
86
+ _adguard("ddeb6f867663dc1e2d298f4abfad1f5e79a5021a2f8930c28e20b45b4bd1ccb1", 1),
87
+ _adguard("acbfc3190dd8588904410d746ba9f1f26727ea722718c3ffd5999f924d50cecf", 2),
88
+ _adguard("ade08a07590eabeac543e87b992d23f11022023d8ecd29c46a16e4082a0e7d96", 3),
89
+ _adguard("bf10a7a8ecacac2f33f65b255eda3f64633557683b604324cfe04ef0cdfe3e5e", 4),
90
+ _adguard("3bf92d4350bab7f85c7eb94c625d7e4101c3750b1f7ea0ccb2107db542486f78", 5),
91
+ _adguard("14b8d90aa31112c29773362edae7b4815f3af4329fa7780d5fc96eba6c78a0e7", 6),
92
+ _adguard("1fd5924ce5b7565b752a6ce8edfe783e04a9bae76fa60da744ca673b0991f528", 7),
93
+ _adguard("1732ba84e3ec722470e3a5166f04825db6e66c6b85ec2648aedbefbef4ed9d81", 8),
94
+ _adguard("cc9ce8160876312f0b510ab58ebd6782675bf595e8d14e1ffe6ce81efa27b3ea", 9),
95
+
96
+ _entry("cdee9f3305572a677e1a6c82dc1e02a35b11cae6a684338c9f37fe1ac8daec23", "eDellRoot",
97
+ "Dell installed this root with its private key on Dell computers in 2015; the key was extracted and published", DELL_SOURCES),
98
+ _entry("130ad4e0633521290531b6651f5759b0bc7bc656709ff8f365c2143b0389b6f6", "DSDTestProvider",
99
+ "Dell System Detect installs this root with its private key; the key was extracted and published in 2015", DELL_SOURCES),
100
+ _entry("8ebf425f93b2c7ef59edb0fa840aae95f689503fa2e38a447a0d87975ea0ff75", "bealocalhost.de",
101
+ "the German Federal Bar told lawyers to trust this root in December 2017, and its private key shipped inside the " +
102
+ "beA Client Security software", [
103
+ "https://www.feistyduck.com/newsletter/issue_36_private_keys_in_software",
104
+ "https://dserver.bundestag.de/btd/19/008/1900898.pdf",
105
+ ]),
106
+ _entry("b72c2308eb6129308900bd53995fdb6025aa72c30e43f3527254b3ec87c236db", "DO NOT TRUST - AMI Test PK",
107
+ "the AMI test UEFI Secure Boot platform key (PKfail, CVE-2024-8105); its private key was committed to a public " +
108
+ "repository with its password, and the certificate shipped in production firmware", [
109
+ "https://github.com/binarly-io/Vulnerability-REsearch/blob/main/PKfail/BRLY-2024-005.md",
110
+ "https://www.cve.org/CVERecord?id=CVE-2024-8105",
111
+ ]),
112
+ ]);
113
+
114
+ module.exports = _freeze({ KEYS: KEYS });
package/lib/constants.js CHANGED
@@ -165,6 +165,7 @@ var LIMITS = Object.assign(Object.create(null), {
165
165
  MERKLE_MAX_PROOF_NODES: 65,
166
166
  MERKLE_MAX_LEAVES: 1048576,
167
167
  PROXY_MAX_TLS_ANCHORS: 256,
168
+ DISTRUSTED_KEYS_MAX: 4096,
168
169
  PATH_MAX_CERTS: 100,
169
170
  PATH_MAX_POLICY_NODES: 4096,
170
171
  PATH_BUILD_MAX_DEPTH: 20,
package/lib/crl-sign.js CHANGED
@@ -770,16 +770,16 @@ function isRevoked(crl, serialNumber, opts) {
770
770
  return "pki.crl.isRevoked has an unknown option " + JSON.stringify(k) +
771
771
  "; it takes `time` and `historicalMode`";
772
772
  });
773
- if (o.historicalMode !== undefined && o.time === undefined) {
773
+ if (o.historicalMode != null && o.time == null) {
774
774
  throw _err("crl/bad-input", "pki.crl.isRevoked opts.historicalMode says how to read an entry " +
775
775
  "against the instant asked about, so it needs opts.time as well");
776
776
  }
777
- if (o.historicalMode !== undefined && typeof o.historicalMode !== "boolean") {
777
+ if (o.historicalMode != null && typeof o.historicalMode !== "boolean") {
778
778
  throw _err("crl/bad-input", "pki.crl.isRevoked opts.historicalMode must be a boolean");
779
779
  }
780
780
  var parsed = _coerceCrl(crl);
781
781
  var hex = _serialHexOf(serialNumber);
782
- if (o.time !== undefined) {
782
+ if (o.time != null) {
783
783
  guard.time.assertValid(o.time, _err, "crl/bad-input", "pki.crl.isRevoked opts.time");
784
784
  var at = guard.time.instantOf(o.time);
785
785
  if (at < guard.time.instantOf(parsed.thisUpdate)) {
package/lib/crmf-sign.js CHANGED
@@ -1177,6 +1177,7 @@ function _buildCertReqMsg(spec, key, opts) {
1177
1177
 
1178
1178
  var signingKey = (key && typeof key === "object" && !_isBuffer(key) && !(key instanceof Uint8Array) && key.type == null && "key" in key) ? key.key : key;
1179
1179
  var template = _encodeCertTemplate(spec.certTemplate);
1180
+ if (template.spki !== null) guard.distrust.refuseCompromised(template.spki, _err, "crmf/compromised-key", "certTemplate.publicKey");
1180
1181
  var certReqChildren = [b.integer(_certReqId(spec.certReqId)), template.der];
1181
1182
  var encodedControls = [];
1182
1183
  if (spec.controls != null) guard.list.append(certReqChildren, _buildAttrTypeAndValues(spec.controls, "crmf/bad-controls", "controls", CONTROL_VALUE, encodedControls));
@@ -1203,6 +1204,7 @@ function _buildCertReqMsg(spec, key, opts) {
1203
1204
  * second walk over the same bytes, and read back through the one statement reader. */
1204
1205
  possessionSpki = _encodedPossessionSpki(encodedControls);
1205
1206
  }
1207
+ if (possessionSpki !== null) guard.distrust.refuseCompromised(possessionSpki, _err, "crmf/compromised-key", "the key of the statementOfPossession certificate");
1206
1208
 
1207
1209
  var popoP = _promiseResolve(_Promise,
1208
1210
  _buildProofOfPossession(spec.pop, certReqDer, template, signingKey, opts, possessionSpki));
package/lib/csr-sign.js CHANGED
@@ -114,7 +114,7 @@ function _possessionStatement(spec) {
114
114
  "privateKeyPossessionStatement needs the certificate: this verb resolves the signature algorithm from its key and checks the signature against it. Pass includeCertificate: false to leave it off the wire, which RFC 9883 sec. 3 allows");
115
115
  }
116
116
  var certDer = _b.reqDer(spec.certificate, "privateKeyPossessionStatement certificate");
117
- var include = spec.includeCertificate === undefined ? true : spec.includeCertificate;
117
+ var include = spec.includeCertificate == null ? true : spec.includeCertificate;
118
118
  if (include !== true && include !== false) {
119
119
  throw _err("csr/bad-input", "privateKeyPossessionStatement includeCertificate is a flag, so it is true, false or omitted");
120
120
  }
@@ -188,6 +188,8 @@ function _relatedCertRequest(spec) {
188
188
  *
189
189
  * Before the key is used, the request the spec describes is linted against the RFC 2986 profile and
190
190
  * refused with `csr/profile-violation` if any rule grades it `error`, naming the rule and its clause.
191
+ * A `subjectPublicKey`, or the key of a possession statement's certificate, whose private key is
192
+ * public (see `pki.trust.knownCompromisedKeys`) is refused with `csr/compromised-key`.
191
193
  *
192
194
  * @opts
193
195
  * - `pem` (boolean) -- return a PEM `CERTIFICATE REQUEST` string instead of DER.
@@ -225,6 +227,7 @@ function _sign(spec, key, opts) {
225
227
 
226
228
  var subjectSpki = _b.reqDer(spec.subjectPublicKey, "spec.subjectPublicKey (the SPKI DER of the requested key)");
227
229
  _b.assertValidSpki(subjectSpki, "spec.subjectPublicKey");
230
+ guard.distrust.refuseCompromised(subjectSpki, _err, "csr/compromised-key", "spec.subjectPublicKey");
228
231
  var subjectDer = _b.encodeName(spec.subject == null ? [] : spec.subject);
229
232
 
230
233
  var attrs = [], seenAttr = Object.create(null);
@@ -245,6 +248,7 @@ function _sign(spec, key, opts) {
245
248
  var statement = _possessionStatement(spec.privateKeyPossessionStatement);
246
249
  addAttr(O("statementOfPossession"), b.raw(statement.der));
247
250
  possessionCertSpki = x509Schema.parse(statement.certificate).subjectPublicKeyInfo.bytes;
251
+ guard.distrust.refuseCompromised(possessionCertSpki, _err, "csr/compromised-key", "the key of the possession statement's certificate");
248
252
  }
249
253
  if (spec.attributes != null) {
250
254
  if (!Array.isArray(spec.attributes)) throw _err("csr/bad-input", "spec.attributes must be an array of pre-encoded Attribute DER");
package/lib/ct.js CHANGED
@@ -1216,6 +1216,9 @@ function _trustValue(v, field) {
1216
1216
  return v;
1217
1217
  }
1218
1218
 
1219
+ /** @internal The settings `_tlsForFetch` reads. A setting not on the list is refused at the door. */
1220
+ var _TLS_KNOWN = intrinsic.freeze(guard.identifier.pickSettings(null, ["anchors", "useSystemStore", "cert", "key",
1221
+ "minVersion", "servername", "checkServerIdentity", "distrustedKeys"]));
1219
1222
  function _tlsForFetch(opts) {
1220
1223
  /** @internal A record with NO prototype where the caller supplied no TLS at all, because the empty
1221
1224
  * object a literal gives inherits from `Object.prototype` and every field below is then answerable
@@ -1232,9 +1235,12 @@ function _tlsForFetch(opts) {
1232
1235
  anchors = intrinsic.isArray(anchors)
1233
1236
  ? guard.list.copyMap(anchors, function (a, i) { return _trustValue(a, "opts.tls.anchors[" + i + "]"); })
1234
1237
  : _trustValue(anchors, "opts.tls.anchors");
1238
+ /** @internal The distrust list is typed into a frozen copy. `guard.distrust.keys` reads each entry
1239
+ * through its descriptor and refuses an accessor, a hole and a Proxy, so no caller code runs. */
1240
+ var distrustedKeys = guard.distrust.keys(t.distrustedKeys, _ctErr, "ct/bad-input", "opts.tls.distrustedKeys");
1235
1241
  return { anchors: anchors, useSystemStore: t.useSystemStore, cert: _trustValue(t.cert, "opts.tls.cert"),
1236
1242
  key: _trustValue(t.key, "opts.tls.key"), minVersion: t.minVersion, servername: t.servername,
1237
- checkServerIdentity: t.checkServerIdentity };
1243
+ checkServerIdentity: t.checkServerIdentity, distrustedKeys: distrustedKeys };
1238
1244
  }
1239
1245
 
1240
1246
  function _sameOrigin(a, b) { return _urlOrigin(new _URL(a)) === _urlOrigin(new _URL(b)); }
@@ -1418,6 +1424,7 @@ function _settleDecisive(o, keyName) {
1418
1424
  guard.identifier.refuseAccessorFields(o.tls,
1419
1425
  guard.identifier.readableNames(o.tls, _ctErr, "ct/bad-input", "opts.tls"),
1420
1426
  _ctErr, "ct/bad-input", "opts.tls");
1427
+ guard.identifier.assertKnownKeys(o.tls, _TLS_KNOWN, _ctErr, "ct/bad-input", "unknown opts.tls setting ");
1421
1428
  /** @internal And the ELEMENTS of the anchor list, which refusing accessors on `tls` itself does not
1422
1429
  * reach. An accessor on `anchors[0]` runs when the list is copied, and one that overwrote
1423
1430
  * `sth.rootHash` with the leaf being proven had an empty audit path verify. The refusal has to go
@@ -1652,11 +1659,17 @@ async function verifySth(sth, logKey) {
1652
1659
  *
1653
1660
  * `treeSize` and `timestamp` come back as `BigInt`, since both are uint64 on the wire.
1654
1661
  *
1662
+ * `opts.tls.distrustedKeys` lists SHA-256 SubjectPublicKeyInfo digests (see pki.trust.spkiDigest). A
1663
+ * log server whose chain carries a listed key is refused with `ct/server-key-distrusted`, and a
1664
+ * malformed list is `ct/bad-input`. A `tls` setting other than `anchors`, `useSystemStore`, `cert`,
1665
+ * `key`, `minVersion`, `servername`, `checkServerIdentity` and `distrustedKeys` is refused
1666
+ * `ct/bad-input`. The other sec. 4 messages read it the same way.
1667
+ *
1655
1668
  * @opts
1656
1669
  * url: string, // the log's base URL, with or without the ct/v1/ prefix
1657
1670
  * logKey: BufferSource, // the log's SubjectPublicKeyInfo, pinned by the caller
1658
1671
  * transport: function, // optional (request) => Promise<{ status, headers, body }>
1659
- * tls: object, // optional { anchors, useSystemStore, ... } when no transport is given
1672
+ * tls: object, // optional { anchors, useSystemStore, distrustedKeys, ... } when no transport is given
1660
1673
  * headers: object, // optional extra request headers
1661
1674
  * timeout: number, // optional per-request timeout in milliseconds
1662
1675
  * maxResponseBytes: number, // optional response cap
@@ -2194,7 +2207,7 @@ function _b64Array(obj, name, code, label, exactLen, maxCount) {
2194
2207
  * @opts signerKey REQUIRED and caller-pinned -- the distributor SubjectPublicKeyInfo as a DER Buffer; no baked-in key.
2195
2208
  * @opts sigUrl OPTIONAL -- the detached `log_list.sig` URL (https, must share the log-list URL's origin); default `url` with `.json` -> `.sig` (a non-.json url requires an explicit sigUrl).
2196
2209
  * @opts transport OPTIONAL injectable `transport(request) -> Promise<{status,headers,body}>` (default `pki.transport.https`); the test seam.
2197
- * @opts tls OPTIONAL `{ anchors, useSystemStore, cert, key, minVersion, servername, checkServerIdentity }` threaded to the default transport (ignored when a transport is injected); `rejectUnauthorized` is always on.
2210
+ * @opts tls OPTIONAL `{ anchors, useSystemStore, cert, key, minVersion, servername, checkServerIdentity, distrustedKeys }` threaded to the default transport (ignored when a transport is injected); `rejectUnauthorized` is always on. `distrustedKeys` lists SHA-256 SubjectPublicKeyInfo digests (see pki.trust.spkiDigest): a distributor whose chain carries a listed key is refused with `ct/server-key-distrusted`, and a malformed list is `ct/bad-input`. A setting not named in that list is refused `ct/bad-input`.
2198
2211
  * @opts headers OPTIONAL extra request headers (the request-framing headers are stripped; the verb owns the GET method).
2199
2212
  * @opts timeout OPTIONAL ms budget, default 30s (cap-validated).
2200
2213
  * @opts maxResponseBytes OPTIONAL per-GET size cap, default 4 MiB, tightenable DOWNWARD only.
package/lib/est.js CHANGED
@@ -918,10 +918,23 @@ function _parseUrl(urlStr) {
918
918
  return url;
919
919
  }
920
920
 
921
- /** @internal The settings this client forwards to its transport. */
922
- var TLS_SETTINGS = ["anchors", "useSystemStore", "cert", "key", "minVersion", "servername", "checkServerIdentity"];
923
- function _tlsForRequest(opts) {
924
- return guard.identifier.pickSettings(opts.tls, TLS_SETTINGS);
921
+ /** @internal The settings this client forwards to its transport. The distrust list is typed here, once
922
+ * per verb, and the frozen copy is the one every hop carries. A setting not on the list is refused. */
923
+ var TLS_SETTINGS = ["anchors", "useSystemStore", "cert", "key", "minVersion", "servername", "checkServerIdentity", "distrustedKeys"];
924
+ var TLS_KNOWN = intrinsic.freeze(guard.identifier.pickSettings(null, TLS_SETTINGS));
925
+ function _tlsForRequest(tls) {
926
+ guard.identifier.assertKnownKeys(tls, TLS_KNOWN, E, "est/bad-input", "unknown opts.tls setting ");
927
+ var t = guard.identifier.pickSettings(tls, TLS_SETTINGS);
928
+ t.distrustedKeys = guard.distrust.keys(t.distrustedKeys, E, "est/bad-input", "opts.tls.distrustedKeys");
929
+ return t;
930
+ }
931
+ /** @internal The proxy and the tls settings, taken at the synchronous verb door. Every request and
932
+ * redirect hop of the verb uses this record. */
933
+ function _doorSnapshot(opts) {
934
+ var proxy, tls;
935
+ try { proxy = _snapProxy(opts); tls = opts.tls; }
936
+ catch (_pe) { throw E("est/bad-input", "the options object could not be read", _pe); }
937
+ return { proxy: proxy, tls: _tlsForRequest(tls) };
925
938
  }
926
939
 
927
940
  function _redirectTarget(current, location, method, opts) {
@@ -980,7 +993,7 @@ function _authScheme(opts) {
980
993
  if (sc !== "basic" && sc !== "digest") throw E("est/bad-input", "opts.auth.scheme must be \"basic\" or \"digest\"");
981
994
  return sc;
982
995
  }
983
- if (opts.username !== undefined || opts.password !== undefined) return "basic";
996
+ if (opts.username != null || opts.password != null) return "basic";
984
997
  return null;
985
998
  }
986
999
  function _authUser(opts) { return opts.auth && opts.auth.username !== undefined ? opts.auth.username : opts.username; }
@@ -1095,7 +1108,7 @@ function _drive(method, url, body, headers, opts, transport, budgets) {
1095
1108
  /** @internal `checked` is the transport the verb already took at its door. Taking it again here
1096
1109
  * would ask the caller's options a second time, and anything read in between could change the
1097
1110
  * answer, so a verb that holds one passes it and this reads the option only when none was held. */
1098
- function _client(op, method, baseUrl, body, headers, opts, proxySnap, onAttempt, checked) {
1111
+ function _client(op, method, baseUrl, body, headers, opts, snap, onAttempt, checked) {
1099
1112
  var base;
1100
1113
  try { base = new _URL(_stringOf(baseUrl)); }
1101
1114
  catch (e) { throw E("est/bad-url", "the EST base URL did not parse: " + guard.text.showValue(baseUrl), e); }
@@ -1103,14 +1116,14 @@ function _client(op, method, baseUrl, body, headers, opts, proxySnap, onAttempt,
1103
1116
  var url = _parseUrl(paths(baseUrl, { label: opts.label })[op]);
1104
1117
  var transport = checked !== undefined ? checked : _assertTransport(opts);
1105
1118
  if (!transport) {
1106
- var t = opts.tls != null ? opts.tls : intrinsic.create(null);
1119
+ var t = snap.tls;
1107
1120
  var hasAnchors = t.anchors !== undefined && t.anchors !== null && !(Array.isArray(t.anchors) && t.anchors.length === 0);
1108
1121
  if (!hasAnchors && t.useSystemStore !== true) throw E("est/no-trust-anchors", "no explicit trust anchor and tls.useSystemStore not set to true -- refusing an unpinned server (RFC 7030 sec. 3.6)");
1109
1122
  transport = httpTransport.https({ E: E, errPrefix: "est" });
1110
1123
  }
1111
1124
  var budgets = {
1112
- tls: _tlsForRequest(opts),
1113
- proxy: proxySnap,
1125
+ tls: snap.tls,
1126
+ proxy: snap.proxy,
1114
1127
  timeout: guard.limits.cap(opts.timeout, "timeout", DEFAULT_TIMEOUT, { E: E, code: "est/bad-input", min: 1, max: MAX_TIMEOUT }),
1115
1128
  maxResponseBytes: guard.limits.cap(opts.maxResponseBytes, "maxResponseBytes", constants.LIMITS.HTTP_MAX_RESPONSE_BYTES, { E: E, code: "est/bad-input", min: 1, max: constants.LIMITS.HTTP_MAX_RESPONSE_BYTES }),
1116
1129
  maxRedirects: guard.limits.cap(opts.maxRedirects, "maxRedirects", 5, { E: E, code: "est/bad-input", min: 0, max: 32 }),
@@ -1241,18 +1254,19 @@ function fullcmc(baseUrl, request, opts) {
1241
1254
  * caller's nested objects, and one of those reads must not be able to change which transport
1242
1255
  * the request goes out on, so the value that was checked is the value carried below. */
1243
1256
  var carriedTransport = opts.transport;
1244
- var proxySnap;
1245
- try { proxySnap = _snapProxy(opts); } catch (_pe) { throw E("est/bad-input", "the options object could not be read", _pe); }
1257
+ /** @internal Taken before the own-property copy below, so a tls a defaults bag carries on its
1258
+ * prototype is the one the request uses. */
1259
+ var snap = _doorSnapshot(opts);
1246
1260
  der = _cmcRequestDer(request);
1247
1261
  wanted = _requestedPublicKeys(der);
1248
1262
  sent = _cmcSent(opts, der);
1249
1263
  /** @internal The copy below keeps own properties only, and a defaults bag holds its options on
1250
1264
  * a prototype, so the transport is put back from the value held at the door. */
1251
1265
  opts = _shallowCopy(opts);
1252
- if (carriedTransport !== undefined) opts.transport = carriedTransport;
1266
+ if (carriedTransport != null) opts.transport = carriedTransport;
1253
1267
  var body = transferEncode(der);
1254
1268
  return _client("fullcmc", "POST", baseUrl, body,
1255
- { accept: "application/pkcs7-mime", "content-type": "application/pkcs7-mime; smime-type=CMC-request" }, opts, proxySnap, undefined, carriedTransport)
1269
+ { accept: "application/pkcs7-mime", "content-type": "application/pkcs7-mime; smime-type=CMC-request" }, opts, snap, undefined, carriedTransport)
1256
1270
  .then(function (res) { return _fullcmcResult(res, opts, wanted, sent); });
1257
1271
  } catch (e) {
1258
1272
  return _rej(e);
@@ -1571,7 +1585,7 @@ function _causedBy(e, target) {
1571
1585
 
1572
1586
  var ENROLL_HEADERS = intrinsic.assign(intrinsic.create(null), { accept: "application/pkcs7-mime", "content-type": "application/pkcs10" });
1573
1587
 
1574
- function _enrollBound(op, baseUrl, builder, opts, proxySnap) {
1588
+ function _enrollBound(op, baseUrl, builder, opts, snap) {
1575
1589
  /** @internal Held before anything else the caller supplied is read, and carried to the client:
1576
1590
  * the builder runs during the handshake, so a later read could answer with a different value. */
1577
1591
  var checkedTransport = opts.transport;
@@ -1601,7 +1615,7 @@ function _enrollBound(op, baseUrl, builder, opts, proxySnap) {
1601
1615
  .then(null, function (e) { attempt.error = e; attempt.done = true; throw e; });
1602
1616
  };
1603
1617
  }
1604
- return _client(op, "POST", baseUrl, hopBody, ENROLL_HEADERS, opts, proxySnap, hopBody, checkedTransport)
1618
+ return _client(op, "POST", baseUrl, hopBody, ENROLL_HEADERS, opts, snap, hopBody, checkedTransport)
1605
1619
  .then(function (res) {
1606
1620
  if (current && current.error) throw current.error;
1607
1621
  if (!current || !current.done) {
@@ -1617,13 +1631,13 @@ function _enrollBound(op, baseUrl, builder, opts, proxySnap) {
1617
1631
  });
1618
1632
  }
1619
1633
 
1620
- function _enroll(op, baseUrl, csrInput, opts, proxySnap) {
1621
- if (typeof csrInput === "function") return _enrollBound(op, baseUrl, csrInput, opts, proxySnap);
1634
+ function _enroll(op, baseUrl, csrInput, opts, snap) {
1635
+ if (typeof csrInput === "function") return _enrollBound(op, baseUrl, csrInput, opts, snap);
1622
1636
  var checkedTransport = opts.transport;
1623
1637
  var csrDer = _csrDer(csrInput);
1624
1638
  var spki = csr.parse(csrDer).subjectPublicKeyInfo;
1625
1639
  var body = transferEncode(csrDer);
1626
- return _client(op, "POST", baseUrl, body, ENROLL_HEADERS, opts, proxySnap, undefined, checkedTransport)
1640
+ return _client(op, "POST", baseUrl, body, ENROLL_HEADERS, opts, snap, undefined, checkedTransport)
1627
1641
  .then(function (res) { return _certsResult(op, res, opts, spki); });
1628
1642
  }
1629
1643
 
@@ -1646,7 +1660,7 @@ function _enroll(op, baseUrl, csrInput, opts, proxySnap) {
1646
1660
  *
1647
1661
  * @opts
1648
1662
  * - `transport`: an injected `transport(request) -> Promise<{status, headers, body, tls}>`; default pki.transport.https. It must return a promise of the response; anything else is `est/bad-input`.
1649
- * - `tls` -- { anchors, useSystemStore, cert, key, minVersion, servername, checkServerIdentity }.
1663
+ * - `tls` -- { anchors, useSystemStore, cert, key, minVersion, servername, checkServerIdentity, distrustedKeys }. `distrustedKeys` lists SHA-256 SubjectPublicKeyInfo digests (see pki.trust.spkiDigest); a server whose chain carries a listed key is refused with `est/server-key-distrusted`, on every hop including a cross-origin redirect, and a malformed list is `est/bad-input`. A setting not named in that list is refused `est/bad-input`. The verb reads `tls` when it is called, so a change the caller makes to it afterward does not reach the request.
1650
1664
  * - `label` -- an OPTIONAL CA label path segment; `timeout` / `maxResponseBytes` / `maxRedirects` -- budgets.
1651
1665
  * - `proxy` -- reach the CA through a forward HTTP proxy (`{ url, auth?, tls? }`; see pki.transport).
1652
1666
  * - `now` -- receipt time (epoch ms) to render a 202 Retry-After HTTP-date as seconds.
@@ -1664,10 +1678,10 @@ function _enroll(op, baseUrl, csrInput, opts, proxySnap) {
1664
1678
  */
1665
1679
  function cacerts(baseUrl, opts) {
1666
1680
  opts = opts || intrinsic.create(null);
1667
- var proxySnap; try { proxySnap = _snapProxy(opts); } catch (_pe) { return _rej(E("est/bad-input", "the options object could not be read", _pe)); }
1681
+ var snap; try { snap = _doorSnapshot(opts); } catch (e) { return _rej(e); }
1668
1682
  return _promiseResolve(_Promise, undefined).then(function () {
1669
1683
  opts = _knownOpts(opts, CACERTS_OPTS, "cacerts");
1670
- return _client("cacerts", "GET", baseUrl, null, { accept: "application/pkcs7-mime" }, opts, proxySnap, undefined, opts.transport);
1684
+ return _client("cacerts", "GET", baseUrl, null, { accept: "application/pkcs7-mime" }, opts, snap, undefined, opts.transport);
1671
1685
  }).then(function (res) { return _certsResult("cacerts", res, opts, null); });
1672
1686
  }
1673
1687
 
@@ -1732,10 +1746,10 @@ function cacerts(baseUrl, opts) {
1732
1746
  */
1733
1747
  function simpleenroll(baseUrl, csrInput, opts) {
1734
1748
  opts = opts || intrinsic.create(null);
1735
- var proxySnap; try { proxySnap = _snapProxy(opts); } catch (_pe) { return _rej(E("est/bad-input", "the options object could not be read", _pe)); }
1749
+ var snap; try { snap = _doorSnapshot(opts); } catch (e) { return _rej(e); }
1736
1750
  return _promiseResolve(_Promise, undefined).then(function () {
1737
1751
  opts = _knownOpts(opts, SIMPLEENROLL_OPTS, "simpleenroll");
1738
- return _enroll("simpleenroll", baseUrl, csrInput, opts, proxySnap);
1752
+ return _enroll("simpleenroll", baseUrl, csrInput, opts, snap);
1739
1753
  });
1740
1754
  }
1741
1755
 
@@ -1772,12 +1786,12 @@ function simpleenroll(baseUrl, csrInput, opts) {
1772
1786
  */
1773
1787
  function simplereenroll(baseUrl, csrInput, opts) {
1774
1788
  opts = opts || intrinsic.create(null);
1775
- var proxySnap; try { proxySnap = _snapProxy(opts); } catch (_pe) { return _rej(E("est/bad-input", "the options object could not be read", _pe)); }
1789
+ var snap; try { snap = _doorSnapshot(opts); } catch (e) { return _rej(e); }
1776
1790
  return _promiseResolve(_Promise, undefined).then(function () {
1777
1791
  opts = _knownOpts(opts, SIMPLEREENROLL_OPTS, "simplereenroll");
1778
1792
  if (!opts.oldCert) throw E("est/bad-input", "simplereenroll requires opts.oldCert (the certificate being renewed, RFC 7030 sec. 4.2.2)");
1779
1793
  if (typeof csrInput !== "function") reenrollGuard(opts.oldCert, _csrDer(csrInput));
1780
- return _enroll("simplereenroll", baseUrl, csrInput, opts, proxySnap);
1794
+ return _enroll("simplereenroll", baseUrl, csrInput, opts, snap);
1781
1795
  });
1782
1796
  }
1783
1797
 
@@ -1944,7 +1958,7 @@ async function _serverkeygenResult(res, opts, derived) {
1944
1958
  */
1945
1959
  function serverkeygen(baseUrl, csrInput, opts) {
1946
1960
  opts = opts || intrinsic.create(null);
1947
- var proxySnap; try { proxySnap = _snapProxy(opts); } catch (_pe) { return _rej(E("est/bad-input", "the options object could not be read", _pe)); }
1961
+ var snap; try { snap = _doorSnapshot(opts); } catch (e) { return _rej(e); }
1948
1962
  return _promiseResolve(_Promise, undefined).then(function () {
1949
1963
  opts = _knownOpts(opts, SERVERKEYGEN_OPTS, "serverkeygen");
1950
1964
  var checkedTransport = opts.transport;
@@ -1955,6 +1969,7 @@ function serverkeygen(baseUrl, csrInput, opts) {
1955
1969
  * coerced to `false` and contradicted a CSR the caller never disagreed with. The value is a
1956
1970
  * boolean by the time it reaches here, so it is compared rather than coerced. */
1957
1971
  if (opts.requestedEncryption != null && opts.requestedEncryption !== derived.requestedEncryption) throw E("est/bad-input", "opts.requestedEncryption (" + opts.requestedEncryption + ") contradicts the CSR's advertised key-encryption attribute (" + derived.requestedEncryption + ") (RFC 7030 sec. 4.4.1)");
1972
+ // allow:caller-option-presence-tested-against-undefined -- absence means the recipient is not checked against the CSR, so a null must be refused
1958
1973
  if (opts.expectedRecipientKeyId !== undefined) {
1959
1974
  if (!Buffer.isBuffer(opts.expectedRecipientKeyId)) throw E("est/bad-input", "opts.expectedRecipientKeyId must be a Buffer");
1960
1975
  if (derived.expectedRecipientKeyId && !opts.expectedRecipientKeyId.equals(derived.expectedRecipientKeyId)) throw E("est/bad-input", "opts.expectedRecipientKeyId contradicts the key identifier the CSR advertised (RFC 7030 sec. 4.4.1)");
@@ -1968,7 +1983,7 @@ function serverkeygen(baseUrl, csrInput, opts) {
1968
1983
  if ((opts.expectedRecipientKeyId !== undefined || opts.expectedRecipientIssuerSerial != null) && !derived.requestedEncryption) {
1969
1984
  throw E("est/bad-input", "a recipient expectation (expectedRecipientKeyId / expectedRecipientIssuerSerial) implies an encrypted key, but the CSR advertised no DecryptKeyIdentifier / AsymmetricDecryptKeyIdentifier attribute (RFC 7030 sec. 4.4.1)");
1970
1985
  }
1971
- return _client("serverkeygen", "POST", baseUrl, transferEncode(csrDer), { accept: "multipart/mixed", "content-type": "application/pkcs10" }, opts, proxySnap, undefined, checkedTransport)
1986
+ return _client("serverkeygen", "POST", baseUrl, transferEncode(csrDer), { accept: "multipart/mixed", "content-type": "application/pkcs10" }, opts, snap, undefined, checkedTransport)
1972
1987
  .then(function (res) { return _serverkeygenResult(res, opts, derived); });
1973
1988
  });
1974
1989
  }
@@ -2010,10 +2025,10 @@ function _csrattrsResult(res, opts) {
2010
2025
  */
2011
2026
  function csrattrs(baseUrl, opts) {
2012
2027
  opts = opts || intrinsic.create(null);
2013
- var proxySnap; try { proxySnap = _snapProxy(opts); } catch (_pe) { return _rej(E("est/bad-input", "the options object could not be read", _pe)); }
2028
+ var snap; try { snap = _doorSnapshot(opts); } catch (e) { return _rej(e); }
2014
2029
  return _promiseResolve(_Promise, undefined).then(function () {
2015
2030
  opts = _knownOpts(opts, CSRATTRS_OPTS, "csrattrs");
2016
- return _client("csrattrs", "GET", baseUrl, null, { accept: "application/csrattrs" }, opts, proxySnap, undefined, opts.transport);
2031
+ return _client("csrattrs", "GET", baseUrl, null, { accept: "application/csrattrs" }, opts, snap, undefined, opts.transport);
2017
2032
  }).then(function (res) { return _csrattrsResult(res, opts); });
2018
2033
  }
2019
2034