• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <iostream>
6 #include <string_view>
7 
8 #include "base/at_exit.h"
9 #include "base/command_line.h"
10 #include "base/functional/bind.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_pump_type.h"
13 #include "base/strings/string_split.h"
14 #include "base/synchronization/waitable_event.h"
15 #include "base/task/thread_pool/thread_pool_instance.h"
16 #include "base/threading/thread.h"
17 #include "base/time/time.h"
18 #include "build/build_config.h"
19 #include "net/cert/cert_net_fetcher.h"
20 #include "net/cert/cert_verify_proc.h"
21 #include "net/cert/cert_verify_proc_builtin.h"
22 #include "net/cert/crl_set.h"
23 #include "net/cert/do_nothing_ct_verifier.h"
24 #include "net/cert/internal/platform_trust_store.h"
25 #include "net/cert/internal/system_trust_store.h"
26 #include "net/cert/x509_util.h"
27 #include "net/cert_net/cert_net_fetcher_url_request.h"
28 #include "net/tools/cert_verify_tool/cert_verify_tool_util.h"
29 #include "net/tools/cert_verify_tool/verify_using_cert_verify_proc.h"
30 #include "net/tools/cert_verify_tool/verify_using_path_builder.h"
31 #include "net/url_request/url_request_context.h"
32 #include "net/url_request/url_request_context_builder.h"
33 #include "net/url_request/url_request_context_getter.h"
34 #include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
35 #include "third_party/boringssl/src/pki/trust_store.h"
36 #include "third_party/boringssl/src/pki/trust_store_collection.h"
37 
38 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
39 #include "net/proxy_resolution/proxy_config.h"
40 #include "net/proxy_resolution/proxy_config_service_fixed.h"
41 #endif
42 
43 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
44 #include "net/cert/internal/trust_store_chrome.h"
45 #endif
46 
47 namespace {
48 
49 enum class RootStoreType {
50   // No roots other than those explicitly passed in on the command line.
51   kEmpty,
52 #if !BUILDFLAG(CHROME_ROOT_STORE_ONLY)
53   // Use the system root store.
54   kSystem,
55 #endif
56   // Use the Chrome Root Store.
57   kChrome
58 };
59 
GetUserAgent()60 std::string GetUserAgent() {
61   return "cert_verify_tool/0.1";
62 }
63 
SetUpOnNetworkThread(std::unique_ptr<net::URLRequestContext> * context,scoped_refptr<net::CertNetFetcherURLRequest> * cert_net_fetcher,base::WaitableEvent * initialization_complete_event)64 void SetUpOnNetworkThread(
65     std::unique_ptr<net::URLRequestContext>* context,
66     scoped_refptr<net::CertNetFetcherURLRequest>* cert_net_fetcher,
67     base::WaitableEvent* initialization_complete_event) {
68   net::URLRequestContextBuilder url_request_context_builder;
69   url_request_context_builder.set_user_agent(GetUserAgent());
70 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
71   // On Linux, use a fixed ProxyConfigService, since the default one
72   // depends on glib.
73   //
74   // TODO(akalin): Remove this once http://crbug.com/146421 is fixed.
75   url_request_context_builder.set_proxy_config_service(
76       std::make_unique<net::ProxyConfigServiceFixed>(
77           net::ProxyConfigWithAnnotation()));
78 #endif
79   *context = url_request_context_builder.Build();
80 
81   // TODO(mattm): add command line flag to configure using
82   // CertNetFetcher
83   *cert_net_fetcher = base::MakeRefCounted<net::CertNetFetcherURLRequest>();
84   (*cert_net_fetcher)->SetURLRequestContext(context->get());
85   initialization_complete_event->Signal();
86 }
87 
ShutdownOnNetworkThread(std::unique_ptr<net::URLRequestContext> * context,scoped_refptr<net::CertNetFetcherURLRequest> * cert_net_fetcher)88 void ShutdownOnNetworkThread(
89     std::unique_ptr<net::URLRequestContext>* context,
90     scoped_refptr<net::CertNetFetcherURLRequest>* cert_net_fetcher) {
91   (*cert_net_fetcher)->Shutdown();
92   cert_net_fetcher->reset();
93   context->reset();
94 }
95 
96 // Base class to abstract running a particular implementation of certificate
97 // verification.
98 class CertVerifyImpl {
99  public:
100   virtual ~CertVerifyImpl() = default;
101 
102   virtual std::string GetName() const = 0;
103 
104   // Does certificate verification.
105   //
106   // Note that |hostname| may be empty to indicate that no name validation is
107   // requested, and a null value of |verify_time| means to use the current time.
108   virtual bool VerifyCert(const CertInput& target_der_cert,
109                           const std::string& hostname,
110                           const std::vector<CertInput>& intermediate_der_certs,
111                           const std::vector<CertInputWithTrustSetting>&
112                               der_certs_with_trust_settings,
113                           base::Time verify_time,
114                           net::CRLSet* crl_set,
115                           const base::FilePath& dump_prefix_path) = 0;
116 };
117 
118 // Runs certificate verification using a particular CertVerifyProc.
119 class CertVerifyImplUsingProc : public CertVerifyImpl {
120  public:
CertVerifyImplUsingProc(const std::string & name,scoped_refptr<net::CertVerifyProc> proc)121   CertVerifyImplUsingProc(const std::string& name,
122                           scoped_refptr<net::CertVerifyProc> proc)
123       : name_(name), proc_(std::move(proc)) {}
124 
GetName() const125   std::string GetName() const override { return name_; }
126 
VerifyCert(const CertInput & target_der_cert,const std::string & hostname,const std::vector<CertInput> & intermediate_der_certs,const std::vector<CertInputWithTrustSetting> & der_certs_with_trust_settings,base::Time verify_time,net::CRLSet * crl_set,const base::FilePath & dump_prefix_path)127   bool VerifyCert(const CertInput& target_der_cert,
128                   const std::string& hostname,
129                   const std::vector<CertInput>& intermediate_der_certs,
130                   const std::vector<CertInputWithTrustSetting>&
131                       der_certs_with_trust_settings,
132                   base::Time verify_time,
133                   net::CRLSet* crl_set,
134                   const base::FilePath& dump_prefix_path) override {
135     if (!verify_time.is_null()) {
136       std::cerr << "WARNING: --time is not supported by " << GetName()
137                 << ", will use current time.\n";
138     }
139 
140     if (hostname.empty()) {
141       std::cerr << "ERROR: --hostname is required for " << GetName()
142                 << ", skipping\n";
143       return true;  // "skipping" is considered a successful return.
144     }
145 
146     base::FilePath dump_path;
147     if (!dump_prefix_path.empty()) {
148       dump_path = dump_prefix_path.AddExtension(FILE_PATH_LITERAL(".pem"))
149                       .InsertBeforeExtensionASCII("." + GetName());
150     }
151 
152     return VerifyUsingCertVerifyProc(proc_.get(), target_der_cert, hostname,
153                                      intermediate_der_certs,
154                                      der_certs_with_trust_settings, dump_path);
155   }
156 
157  private:
158   const std::string name_;
159   scoped_refptr<net::CertVerifyProc> proc_;
160 };
161 
162 // Runs certificate verification using bssl::CertPathBuilder.
163 class CertVerifyImplUsingPathBuilder : public CertVerifyImpl {
164  public:
CertVerifyImplUsingPathBuilder(scoped_refptr<net::CertNetFetcher> cert_net_fetcher,std::unique_ptr<net::SystemTrustStore> system_trust_store)165   explicit CertVerifyImplUsingPathBuilder(
166       scoped_refptr<net::CertNetFetcher> cert_net_fetcher,
167       std::unique_ptr<net::SystemTrustStore> system_trust_store)
168       : cert_net_fetcher_(std::move(cert_net_fetcher)),
169         system_trust_store_(std::move(system_trust_store)) {}
170 
GetName() const171   std::string GetName() const override { return "CertPathBuilder"; }
172 
VerifyCert(const CertInput & target_der_cert,const std::string & hostname,const std::vector<CertInput> & intermediate_der_certs,const std::vector<CertInputWithTrustSetting> & der_certs_with_trust_settings,base::Time verify_time,net::CRLSet * crl_set,const base::FilePath & dump_prefix_path)173   bool VerifyCert(const CertInput& target_der_cert,
174                   const std::string& hostname,
175                   const std::vector<CertInput>& intermediate_der_certs,
176                   const std::vector<CertInputWithTrustSetting>&
177                       der_certs_with_trust_settings,
178                   base::Time verify_time,
179                   net::CRLSet* crl_set,
180                   const base::FilePath& dump_prefix_path) override {
181     if (!hostname.empty()) {
182       std::cerr << "WARNING: --hostname is not verified with CertPathBuilder\n";
183     }
184 
185     if (verify_time.is_null()) {
186       verify_time = base::Time::Now();
187     }
188 
189     return VerifyUsingPathBuilder(target_der_cert, intermediate_der_certs,
190                                   der_certs_with_trust_settings, verify_time,
191                                   dump_prefix_path, cert_net_fetcher_,
192                                   system_trust_store_.get());
193   }
194 
195  private:
196   scoped_refptr<net::CertNetFetcher> cert_net_fetcher_;
197   std::unique_ptr<net::SystemTrustStore> system_trust_store_;
198 };
199 
200 class DummySystemTrustStore : public net::SystemTrustStore {
201  public:
GetTrustStore()202   bssl::TrustStore* GetTrustStore() override { return &trust_store_; }
203 
IsKnownRoot(const bssl::ParsedCertificate * trust_anchor) const204   bool IsKnownRoot(const bssl::ParsedCertificate* trust_anchor) const override {
205     return false;
206   }
207 
208 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
GetPlatformTrustStore()209   net::PlatformTrustStore* GetPlatformTrustStore() override { return nullptr; }
210 
IsLocallyTrustedRoot(const bssl::ParsedCertificate * trust_anchor)211   bool IsLocallyTrustedRoot(
212       const bssl::ParsedCertificate* trust_anchor) override {
213     return false;
214   }
215 
chrome_root_store_version() const216   int64_t chrome_root_store_version() const override { return 0; }
217 
GetChromeRootConstraints(const bssl::ParsedCertificate * cert) const218   base::span<const net::ChromeRootCertConstraints> GetChromeRootConstraints(
219       const bssl::ParsedCertificate* cert) const override {
220     return {};
221   }
222 #endif
223 
224  private:
225   bssl::TrustStoreCollection trust_store_;
226 };
227 
CreateSystemTrustStore(std::string_view impl_name,RootStoreType root_store_type)228 std::unique_ptr<net::SystemTrustStore> CreateSystemTrustStore(
229     std::string_view impl_name,
230     RootStoreType root_store_type) {
231   switch (root_store_type) {
232 #if BUILDFLAG(IS_FUCHSIA)
233     case RootStoreType::kSystem:
234       std::cerr << impl_name
235                 << ": using system roots (--roots are in addition).\n";
236       return net::CreateSslSystemTrustStore();
237 #endif
238     case RootStoreType::kChrome:
239 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
240       std::cerr << impl_name
241                 << ": using Chrome Root Store (--roots are in addition).\n";
242       return net::CreateSslSystemTrustStoreChromeRoot(
243           std::make_unique<net::TrustStoreChrome>());
244 #else
245       std::cerr << impl_name << ": not supported.\n";
246       [[fallthrough]];
247 #endif
248 
249     case RootStoreType::kEmpty:
250     default:
251       std::cerr << impl_name << ": only using --roots specified.\n";
252       return std::make_unique<DummySystemTrustStore>();
253   }
254 }
255 
256 // Creates an subclass of CertVerifyImpl based on its name, or returns nullptr.
CreateCertVerifyImplFromName(std::string_view impl_name,scoped_refptr<net::CertNetFetcher> cert_net_fetcher,scoped_refptr<net::CRLSet> crl_set,RootStoreType root_store_type)257 std::unique_ptr<CertVerifyImpl> CreateCertVerifyImplFromName(
258     std::string_view impl_name,
259     scoped_refptr<net::CertNetFetcher> cert_net_fetcher,
260     scoped_refptr<net::CRLSet> crl_set,
261     RootStoreType root_store_type) {
262 #if !(BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(CHROME_ROOT_STORE_ONLY))
263   if (impl_name == "platform") {
264     if (root_store_type != RootStoreType::kSystem) {
265       std::cerr << "WARNING: platform verifier not supported with "
266                    "--no-system-roots and --use-chrome-root-store, using "
267                    "system roots (--roots are in addition).\n";
268     }
269 
270     return std::make_unique<CertVerifyImplUsingProc>(
271         "CertVerifyProc (system)",
272         net::CertVerifyProc::CreateSystemVerifyProc(std::move(cert_net_fetcher),
273                                                     std::move(crl_set)));
274   }
275 #endif
276 
277   if (impl_name == "builtin") {
278     return std::make_unique<CertVerifyImplUsingProc>(
279         "CertVerifyProcBuiltin",
280         net::CreateCertVerifyProcBuiltin(
281             std::move(cert_net_fetcher), std::move(crl_set),
282             // TODO(crbug.com/41392053): support CT.
283             std::make_unique<net::DoNothingCTVerifier>(),
284             base::MakeRefCounted<net::DefaultCTPolicyEnforcer>(),
285             CreateSystemTrustStore(impl_name, root_store_type), {},
286             std::nullopt));
287   }
288 
289   if (impl_name == "pathbuilder") {
290     return std::make_unique<CertVerifyImplUsingPathBuilder>(
291         std::move(cert_net_fetcher),
292         CreateSystemTrustStore(impl_name, root_store_type));
293   }
294 
295   std::cerr << "WARNING: Unrecognized impl: " << impl_name << "\n";
296   return nullptr;
297 }
298 
PrintCertHashAndSubject(CRYPTO_BUFFER * cert)299 void PrintCertHashAndSubject(CRYPTO_BUFFER* cert) {
300   std::cout << " " << FingerPrintCryptoBuffer(cert) << " "
301             << SubjectFromCryptoBuffer(cert) << "\n";
302 }
303 
PrintInputChain(const CertInput & target,const std::vector<CertInput> & intermediates)304 void PrintInputChain(const CertInput& target,
305                      const std::vector<CertInput>& intermediates) {
306   std::cout << "Input chain:\n";
307   PrintCertHashAndSubject(
308       net::x509_util::CreateCryptoBuffer(target.der_cert).get());
309   for (const auto& intermediate : intermediates) {
310     PrintCertHashAndSubject(
311         net::x509_util::CreateCryptoBuffer(intermediate.der_cert).get());
312   }
313   std::cout << "\n";
314 }
315 
PrintAdditionalRoots(const std::vector<CertInputWithTrustSetting> & der_certs_with_trust_settings)316 void PrintAdditionalRoots(const std::vector<CertInputWithTrustSetting>&
317                               der_certs_with_trust_settings) {
318   std::cout << "Additional roots:\n";
319   for (const auto& cert : der_certs_with_trust_settings) {
320     std::cout << " " << cert.trust.ToDebugString() << ":\n ";
321     PrintCertHashAndSubject(
322         net::x509_util::CreateCryptoBuffer(cert.cert_input.der_cert).get());
323   }
324   std::cout << "\n";
325 }
326 
327 const char kUsage[] =
328     " [flags] <target/chain>\n"
329     "\n"
330     " <target/chain> is a file containing certificates [1]. Minimally it\n"
331     " contains the target certificate. Optionally it may subsequently list\n"
332     " additional certificates needed to build a chain (this is equivalent to\n"
333     " specifying them through --intermediates)\n"
334     "\n"
335     "Flags:\n"
336     "\n"
337     " --hostname=<hostname>\n"
338     "      The hostname required to match the end-entity certificate.\n"
339     "      Required for the CertVerifyProc implementation.\n"
340     "\n"
341     " --roots=<certs path>\n"
342     "      <certs path> is a file containing certificates [1] to interpret as\n"
343     "      trust anchors (without any anchor constraints).\n"
344     "\n"
345     " --no-system-roots\n"
346     "      Do not use system provided trust roots, only trust roots specified\n"
347     "      by --roots or --trust-last-cert will be used. Only supported by\n"
348     "      the builtin and pathbuilter impls.\n"
349     "\n"
350     " --use-chrome-root-store\n"
351     "      Use the Chrome Root Store. Only supported by the builtin and \n"
352     "      pathbuilder impls; if set will override the --no-system-roots \n"
353     "      flag.\n"
354     "\n"
355     " --intermediates=<certs path>\n"
356     "      <certs path> is a file containing certificates [1] for use when\n"
357     "      path building is looking for intermediates.\n"
358     "\n"
359     " --impls=<ordered list of implementations>\n"
360     "      Ordered list of the verifier implementations to run. If omitted,\n"
361     "      will default to: \"platform,builtin,pathbuilder\".\n"
362     "      Changing this can lead to different results in cases where the\n"
363     "      platform verifier affects global caches (as in the case of NSS).\n"
364     "\n"
365     " --trust-last-cert\n"
366     "      Removes the final intermediate from the chain and instead adds it\n"
367     "      as a root. This is useful when providing a <target/chain>\n"
368     "      parameter whose final certificate is a trust anchor.\n"
369     "\n"
370     " --root-trust=<trust string>\n"
371     "      Roots trusted by --roots and --trust-last-cert will be trusted\n"
372     "      with the specified trust [2].\n"
373     "\n"
374     " --trust-leaf-cert=[trust string]\n"
375     "      The leaf cert will be considered trusted with the specified\n"
376     "      trust [2]. If [trust string] is omitted, defaults to TRUSTED_LEAF.\n"
377     "\n"
378     " --time=<time>\n"
379     "      Use <time> instead of the current system time. <time> is\n"
380     "      interpreted in local time if a timezone is not specified.\n"
381     "      Many common formats are supported, including:\n"
382     "        1994-11-15 12:45:26 GMT\n"
383     "        Tue, 15 Nov 1994 12:45:26 GMT\n"
384     "        Nov 15 12:45:26 1994 GMT\n"
385     "\n"
386     " --crlset=<crlset path>\n"
387     "      <crlset path> is a file containing a serialized CRLSet to use\n"
388     "      during revocation checking. For example:\n"
389     "        <chrome data dir>/CertificateRevocation/<number>/crl-set\n"
390     "\n"
391     " --dump=<file prefix>\n"
392     "      Dumps the verified chain to PEM files starting with\n"
393     "      <file prefix>.\n"
394     "\n"
395     "\n"
396     "[1] A \"file containing certificates\" means a path to a file that can\n"
397     "    either be:\n"
398     "    * A binary file containing a single DER-encoded RFC 5280 Certificate\n"
399     "    * A PEM file containing one or more CERTIFICATE blocks (DER-encoded\n"
400     "      RFC 5280 Certificate)\n"
401     "\n"
402     "[2] A \"trust string\" consists of a trust type and zero or more options\n"
403     "    separated by '+' characters. Note that these trust settings are only\n"
404     "    honored by the builtin & pathbuilder impls.\n"
405     "    Trust types: UNSPECIFIED, DISTRUSTED, TRUSTED_ANCHOR,\n"
406     "                 TRUSTED_ANCHOR_OR_LEAF, TRUSTED_LEAF\n"
407     "    Options: enforce_anchor_expiry, enforce_anchor_constraints,\n"
408     "             require_anchor_basic_constraints, require_leaf_selfsigned\n"
409     "    Ex: TRUSTED_ANCHOR+enforce_anchor_expiry+enforce_anchor_constraints\n";
410 
PrintUsage(const char * argv0)411 void PrintUsage(const char* argv0) {
412   std::cerr << "Usage: " << argv0 << kUsage;
413 
414   // TODO(mattm): allow <certs path> to be a directory containing DER/PEM files?
415   // TODO(mattm): allow target to specify an HTTPS URL to check the cert of?
416   // TODO(mattm): allow target to be a verify_certificate_chain_unittest .test
417   // file?
418   // TODO(mattm): allow specifying ocsp_response and sct_list inputs as well.
419 }
420 
421 }  // namespace
422 
main(int argc,char ** argv)423 int main(int argc, char** argv) {
424   base::AtExitManager at_exit_manager;
425   if (!base::CommandLine::Init(argc, argv)) {
426     std::cerr << "ERROR in CommandLine::Init\n";
427     return 1;
428   }
429   base::ThreadPoolInstance::CreateAndStartWithDefaultParams("cert_verify_tool");
430   absl::Cleanup cleanup = [] { base::ThreadPoolInstance::Get()->Shutdown(); };
431   base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
432   logging::LoggingSettings settings;
433   settings.logging_dest =
434       logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR;
435   logging::InitLogging(settings);
436 
437   base::CommandLine::StringVector args = command_line.GetArgs();
438   if (args.size() != 1U || command_line.HasSwitch("help")) {
439     PrintUsage(argv[0]);
440     return 1;
441   }
442 
443   std::string hostname = command_line.GetSwitchValueASCII("hostname");
444 
445   base::Time verify_time;
446   std::string time_flag = command_line.GetSwitchValueASCII("time");
447   if (!time_flag.empty()) {
448     if (!base::Time::FromString(time_flag.c_str(), &verify_time)) {
449       std::cerr << "Error parsing --time flag\n";
450       return 1;
451     }
452   }
453 
454 #if BUILDFLAG(CHROME_ROOT_STORE_ONLY)
455   RootStoreType root_store_type = RootStoreType::kChrome;
456 #else
457   RootStoreType root_store_type = RootStoreType::kSystem;
458 #endif
459 
460   if (command_line.HasSwitch("no-system-roots")) {
461     root_store_type = RootStoreType::kEmpty;
462   }
463   if (command_line.HasSwitch("use-chrome-root-store")) {
464     root_store_type = RootStoreType::kChrome;
465   }
466 
467   base::FilePath roots_path = command_line.GetSwitchValuePath("roots");
468   base::FilePath intermediates_path =
469       command_line.GetSwitchValuePath("intermediates");
470   base::FilePath target_path = base::FilePath(args[0]);
471 
472   base::FilePath crlset_path = command_line.GetSwitchValuePath("crlset");
473   scoped_refptr<net::CRLSet> crl_set = net::CRLSet::BuiltinCRLSet();
474   if (!crlset_path.empty()) {
475     std::string crl_set_bytes;
476     if (!ReadFromFile(crlset_path, &crl_set_bytes))
477       return 1;
478     if (!net::CRLSet::Parse(crl_set_bytes, &crl_set)) {
479       std::cerr << "Error parsing CRLSet\n";
480       return 1;
481     }
482   }
483 
484   base::FilePath dump_prefix_path = command_line.GetSwitchValuePath("dump");
485 
486   std::vector<CertInputWithTrustSetting> der_certs_with_trust_settings;
487   std::vector<CertInput> root_der_certs;
488   std::vector<CertInput> intermediate_der_certs;
489   CertInput target_der_cert;
490 
491   if (!roots_path.empty())
492     ReadCertificatesFromFile(roots_path, &root_der_certs);
493   if (!intermediates_path.empty())
494     ReadCertificatesFromFile(intermediates_path, &intermediate_der_certs);
495 
496   if (!ReadChainFromFile(target_path, &target_der_cert,
497                          &intermediate_der_certs)) {
498     std::cerr << "ERROR: Couldn't read certificate chain\n";
499     return 1;
500   }
501 
502   if (target_der_cert.der_cert.empty()) {
503     std::cerr << "ERROR: no target cert\n";
504     return 1;
505   }
506 
507   // If --trust-last-cert was specified, move the final intermediate to the
508   // roots list.
509   if (command_line.HasSwitch("trust-last-cert")) {
510     if (intermediate_der_certs.empty()) {
511       std::cerr << "ERROR: no intermediate certificates\n";
512       return 1;
513     }
514 
515     root_der_certs.push_back(intermediate_der_certs.back());
516     intermediate_der_certs.pop_back();
517   }
518 
519   if (command_line.HasSwitch("trust-leaf-cert")) {
520     bssl::CertificateTrust trust = bssl::CertificateTrust::ForTrustedLeaf();
521     std::string trust_str = command_line.GetSwitchValueASCII("trust-leaf-cert");
522     if (!trust_str.empty()) {
523       std::optional<bssl::CertificateTrust> parsed_trust =
524           bssl::CertificateTrust::FromDebugString(trust_str);
525       if (!parsed_trust) {
526         std::cerr << "ERROR: invalid leaf trust string " << trust_str << "\n";
527         return 1;
528       }
529       trust = *parsed_trust;
530     }
531     der_certs_with_trust_settings.push_back({target_der_cert, trust});
532   }
533 
534   // TODO(crbug.com/40888483): Maybe default to the trust setting that
535   // would be used for locally added anchors on the current platform?
536   bssl::CertificateTrust root_trust = bssl::CertificateTrust::ForTrustAnchor();
537 
538   if (command_line.HasSwitch("root-trust")) {
539     std::string trust_str = command_line.GetSwitchValueASCII("root-trust");
540     std::optional<bssl::CertificateTrust> parsed_trust =
541         bssl::CertificateTrust::FromDebugString(trust_str);
542     if (!parsed_trust) {
543       std::cerr << "ERROR: invalid root trust string " << trust_str << "\n";
544       return 1;
545     }
546     root_trust = *parsed_trust;
547   }
548 
549   for (const auto& cert_input : root_der_certs) {
550     der_certs_with_trust_settings.push_back({cert_input, root_trust});
551   }
552 
553   PrintInputChain(target_der_cert, intermediate_der_certs);
554   if (!der_certs_with_trust_settings.empty()) {
555     PrintAdditionalRoots(der_certs_with_trust_settings);
556   }
557 
558   // Create a network thread to be used for AIA fetches, and wait for a
559   // CertNetFetcher to be constructed on that thread.
560   base::Thread::Options options(base::MessagePumpType::IO, 0);
561   base::Thread thread("network_thread");
562   CHECK(thread.StartWithOptions(std::move(options)));
563   // Owned by this thread, but initialized, used, and shutdown on the network
564   // thread.
565   std::unique_ptr<net::URLRequestContext> context;
566   scoped_refptr<net::CertNetFetcherURLRequest> cert_net_fetcher;
567   base::WaitableEvent initialization_complete_event(
568       base::WaitableEvent::ResetPolicy::MANUAL,
569       base::WaitableEvent::InitialState::NOT_SIGNALED);
570   thread.task_runner()->PostTask(
571       FROM_HERE,
572       base::BindOnce(&SetUpOnNetworkThread, &context, &cert_net_fetcher,
573                      &initialization_complete_event));
574   initialization_complete_event.Wait();
575 
576   std::vector<std::unique_ptr<CertVerifyImpl>> impls;
577 
578   // Parse the ordered list of CertVerifyImpl passed via command line flags into
579   // |impls|.
580   std::string impls_str = command_line.GetSwitchValueASCII("impls");
581   if (impls_str.empty()) {
582     // Default value.
583 #if !(BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || \
584       BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(CHROME_ROOT_STORE_ONLY))
585     impls_str = "platform,";
586 #endif
587     impls_str += "builtin,pathbuilder";
588   }
589 
590   std::vector<std::string> impl_names = base::SplitString(
591       impls_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
592 
593   for (const std::string& impl_name : impl_names) {
594     auto verify_impl = CreateCertVerifyImplFromName(impl_name, cert_net_fetcher,
595                                                     crl_set, root_store_type);
596     if (verify_impl)
597       impls.push_back(std::move(verify_impl));
598   }
599 
600   // Sequentially run the chain with each of the selected verifier
601   // implementations.
602   bool all_impls_success = true;
603 
604   for (size_t i = 0; i < impls.size(); ++i) {
605     if (i != 0)
606       std::cout << "\n";
607 
608     std::cout << impls[i]->GetName() << ":\n";
609     if (!impls[i]->VerifyCert(target_der_cert, hostname, intermediate_der_certs,
610                               der_certs_with_trust_settings, verify_time,
611                               crl_set.get(), dump_prefix_path)) {
612       all_impls_success = false;
613     }
614   }
615 
616   // Clean up on the network thread and stop it (which waits for the clean up
617   // task to run).
618   thread.task_runner()->PostTask(
619       FROM_HERE,
620       base::BindOnce(&ShutdownOnNetworkThread, &context, &cert_net_fetcher));
621   thread.Stop();
622 
623   return all_impls_success ? 0 : 1;
624 }
625