cert_verify_comparision_tool.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. // Tool used to do batch comparisons of cert verification results between
  5. // the platform verifier and the builtin verifier. Currently only tested on
  6. // Windows.
  7. #include <iostream>
  8. #include "base/at_exit.h"
  9. #include "base/bind.h"
  10. #include "base/callback_helpers.h"
  11. #include "base/command_line.h"
  12. #include "base/containers/span.h"
  13. #include "base/logging.h"
  14. #include "base/message_loop/message_pump_type.h"
  15. #include "base/strings/string_number_conversions.h"
  16. #include "base/strings/string_split.h"
  17. #include "base/synchronization/waitable_event.h"
  18. #include "base/task/thread_pool/thread_pool_instance.h"
  19. #include "base/threading/thread.h"
  20. #include "build/build_config.h"
  21. #include "net/cert/cert_net_fetcher.h"
  22. #include "net/cert/cert_verify_proc.h"
  23. #include "net/cert/cert_verify_proc_builtin.h"
  24. #include "net/cert/crl_set.h"
  25. #include "net/cert/internal/system_trust_store.h"
  26. #include "net/cert/trial_comparison_cert_verifier_util.h"
  27. #include "net/cert/x509_certificate.h"
  28. #include "net/cert_net/cert_net_fetcher_url_request.h"
  29. #include "net/tools/cert_verify_tool/cert_verify_tool_util.h"
  30. #include "net/tools/cert_verify_tool/dumper.pb.h"
  31. #include "net/tools/cert_verify_tool/verify_using_cert_verify_proc.h"
  32. #include "net/url_request/url_request_context.h"
  33. #include "net/url_request/url_request_context_builder.h"
  34. #include "net/url_request/url_request_context_getter.h"
  35. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  36. #include "net/proxy_resolution/proxy_config.h"
  37. #include "net/proxy_resolution/proxy_config_service_fixed.h"
  38. #endif
  39. #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
  40. #include "net/cert/internal/trust_store_chrome.h"
  41. #endif
  42. namespace {
  43. std::string GetUserAgent() {
  44. return "cert_verify_comparison_tool/0.1";
  45. }
  46. void SetUpOnNetworkThread(
  47. std::unique_ptr<net::URLRequestContext>* context,
  48. scoped_refptr<net::CertNetFetcherURLRequest>* cert_net_fetcher,
  49. base::WaitableEvent* initialization_complete_event) {
  50. net::URLRequestContextBuilder url_request_context_builder;
  51. url_request_context_builder.set_user_agent(GetUserAgent());
  52. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  53. // On Linux, use a fixed ProxyConfigService, since the default one
  54. // depends on glib.
  55. //
  56. // TODO(akalin): Remove this once http://crbug.com/146421 is fixed.
  57. url_request_context_builder.set_proxy_config_service(
  58. std::make_unique<net::ProxyConfigServiceFixed>(
  59. net::ProxyConfigWithAnnotation()));
  60. #endif
  61. *context = url_request_context_builder.Build();
  62. // TODO(mattm): add command line flag to configure using
  63. // CertNetFetcher
  64. *cert_net_fetcher = base::MakeRefCounted<net::CertNetFetcherURLRequest>();
  65. (*cert_net_fetcher)->SetURLRequestContext(context->get());
  66. initialization_complete_event->Signal();
  67. }
  68. void ShutdownOnNetworkThread(
  69. std::unique_ptr<net::URLRequestContext>* context,
  70. scoped_refptr<net::CertNetFetcherURLRequest>* cert_net_fetcher) {
  71. (*cert_net_fetcher)->Shutdown();
  72. cert_net_fetcher->reset();
  73. context->reset();
  74. }
  75. // Runs certificate verification using a particular CertVerifyProc.
  76. class CertVerifyImpl {
  77. public:
  78. CertVerifyImpl(const std::string& name,
  79. scoped_refptr<net::CertVerifyProc> proc)
  80. : name_(name), proc_(std::move(proc)) {}
  81. virtual ~CertVerifyImpl() = default;
  82. std::string GetName() const { return name_; }
  83. // Does certificate verification.
  84. bool VerifyCert(net::X509Certificate& x509_target_and_intermediates,
  85. const std::string& hostname,
  86. net::CertVerifyResult* result,
  87. int* error) {
  88. if (hostname.empty()) {
  89. std::cerr << "ERROR: --hostname is required for " << GetName()
  90. << ", skipping\n";
  91. return true; // "skipping" is considered a successful return.
  92. }
  93. scoped_refptr<net::CRLSet> crl_set = net::CRLSet::BuiltinCRLSet();
  94. // TODO(mattm): add command line flags to configure VerifyFlags.
  95. int flags = 0;
  96. // Don't add any additional trust anchors.
  97. net::CertificateList x509_additional_trust_anchors;
  98. // TODO(crbug.com/634484): use a real netlog and print the results?
  99. *error = proc_->Verify(&x509_target_and_intermediates, hostname,
  100. /*ocsp_response=*/std::string(),
  101. /*sct_list=*/std::string(), flags, crl_set.get(),
  102. x509_additional_trust_anchors, result,
  103. net::NetLogWithSource());
  104. return *error == net::OK;
  105. }
  106. private:
  107. const std::string name_;
  108. scoped_refptr<net::CertVerifyProc> proc_;
  109. };
  110. // Creates an subclass of CertVerifyImpl based on its name, or returns nullptr.
  111. std::unique_ptr<CertVerifyImpl> CreateCertVerifyImplFromName(
  112. base::StringPiece impl_name,
  113. scoped_refptr<net::CertNetFetcher> cert_net_fetcher) {
  114. #if !(BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
  115. if (impl_name == "platform") {
  116. return std::make_unique<CertVerifyImpl>(
  117. "CertVerifyProc (system)", net::CertVerifyProc::CreateSystemVerifyProc(
  118. std::move(cert_net_fetcher)));
  119. }
  120. #endif
  121. if (impl_name == "builtin") {
  122. #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
  123. return std::make_unique<CertVerifyImpl>(
  124. "CertVerifyProcBuiltin",
  125. net::CreateCertVerifyProcBuiltin(
  126. std::move(cert_net_fetcher),
  127. net::CreateSslSystemTrustStoreChromeRoot(
  128. std::make_unique<net::TrustStoreChrome>())));
  129. #endif
  130. }
  131. std::cerr << "WARNING: Unrecognized impl: " << impl_name << "\n";
  132. return nullptr;
  133. }
  134. const char kUsage[] =
  135. " --input=<file>\n"
  136. "\n"
  137. " <file> is a file containing serialized protos from trawler. Format \n"
  138. " of the file is a uint32 size, followed by that many bytes of a\n"
  139. " serialized proto message of type \n"
  140. " cert_verify_tool::CertChain. The path to the file must not\n"
  141. " contain any dot(.) characters."
  142. "\n";
  143. // Stats based on errors reading and parsing the input file.
  144. std::map<std::string, int> file_error_stats;
  145. std::map<std::string, int> chain_processing_stats;
  146. std::map<std::string, int> ignorable_difference_stats;
  147. void PrintStats() {
  148. std::cout << "\n\nFile processing stats:\n";
  149. for (const auto& error_stat : file_error_stats) {
  150. std::cout << " " << error_stat.first << ": " << error_stat.second << "\n";
  151. }
  152. std::cout << "\n\nChain processing stats:\n";
  153. for (const auto& chain_stat : chain_processing_stats) {
  154. std::cout << " " << chain_stat.first << ": " << chain_stat.second << "\n";
  155. }
  156. std::cout << "\n\nIgnorable difference stats:\n";
  157. for (const auto& ignorable_diff_stat : ignorable_difference_stats) {
  158. std::cout << " " << ignorable_diff_stat.first << ": "
  159. << ignorable_diff_stat.second << "\n";
  160. }
  161. }
  162. std::string TrialComparisonResultToString(net::TrialComparisonResult result) {
  163. switch (result) {
  164. case net::TrialComparisonResult::kInvalid:
  165. return "invalid";
  166. case net::TrialComparisonResult::kEqual:
  167. return "equal";
  168. case net::TrialComparisonResult::kPrimaryValidSecondaryError:
  169. return "primary_valid_secondary_error";
  170. case net::TrialComparisonResult::kPrimaryErrorSecondaryValid:
  171. return "primary_error_secondary_valid";
  172. case net::TrialComparisonResult::kBothValidDifferentDetails:
  173. return "both_valid_different_details";
  174. case net::TrialComparisonResult::kBothErrorDifferentDetails:
  175. return "both_error_different_details";
  176. case net::TrialComparisonResult::kIgnoredMacUndesiredRevocationChecking:
  177. return "ignored_mac_undesirable_rev_checking";
  178. case net::TrialComparisonResult::
  179. kIgnoredMultipleEVPoliciesAndOneMatchesRoot:
  180. return "ignored_multiple_ev_policies_one_matches_root";
  181. case net::TrialComparisonResult::kIgnoredDifferentPathReVerifiesEquivalent:
  182. return "ignored_different_path_reverifies_equivalent";
  183. case net::TrialComparisonResult::kIgnoredLocallyTrustedLeaf:
  184. return "ignored_locally_trusted_leaf";
  185. case net::TrialComparisonResult::kIgnoredConfigurationChanged:
  186. return "ignored_configuration_changed";
  187. case net::TrialComparisonResult::kIgnoredSHA1SignaturePresent:
  188. return "ignored_sha1_signature_present";
  189. case net::TrialComparisonResult::kIgnoredWindowsRevCheckingEnabled:
  190. return "ignored_windows_rev_checking_enabled";
  191. case net::TrialComparisonResult::kIgnoredBothAuthorityInvalid:
  192. return "ignored_both_authority_invalid";
  193. case net::TrialComparisonResult::kIgnoredBothKnownRoot:
  194. return "ignored_both_known_root";
  195. case net::TrialComparisonResult::
  196. kIgnoredBuiltinAuthorityInvalidPlatformSymantec:
  197. return "ignored_builtin_authority_invalid_platform_symantec";
  198. case net::TrialComparisonResult::kIgnoredLetsEncryptExpiredRoot:
  199. return "ignored_lets_encrypt_expired_root";
  200. }
  201. }
  202. void PrintUsage(const char* argv0) {
  203. std::cerr << "Usage: " << argv0 << kUsage;
  204. }
  205. // Returns -1 if an error occurred.
  206. int RunCert(base::File* input_file,
  207. const std::unique_ptr<CertVerifyImpl>& platform_proc,
  208. const std::unique_ptr<CertVerifyImpl>& builtin_proc) {
  209. // read 4 bytes, convert to uint32_t
  210. std::vector<char> size_bytes(4);
  211. int size_bytes_read = input_file->ReadAtCurrentPos(size_bytes.data(), 4);
  212. if (size_bytes_read != 4) {
  213. std::cerr << "Couldn't read 4 byte size field, read only "
  214. << size_bytes_read << "\n";
  215. file_error_stats["size_read_error"]++;
  216. return -1;
  217. }
  218. uint32_t proto_size;
  219. proto_size = (static_cast<uint8_t>(size_bytes[3]) << 24) +
  220. (static_cast<uint8_t>(size_bytes[2]) << 16) +
  221. (static_cast<uint8_t>(size_bytes[1]) << 8) +
  222. (static_cast<uint8_t>(size_bytes[0]));
  223. // read proto_size bytes, parse to proto.
  224. std::vector<char> proto_bytes(proto_size);
  225. int proto_bytes_read =
  226. input_file->ReadAtCurrentPos(proto_bytes.data(), proto_size);
  227. if ((proto_bytes_read - proto_size) != 0) {
  228. std::cerr << "Couldn't read expected proto of size " << proto_size
  229. << "read only " << proto_bytes_read << "\n";
  230. file_error_stats["proto_read_error"]++;
  231. return -1;
  232. }
  233. cert_verify_tool::CertChain cert_chain;
  234. if (!cert_chain.ParseFromArray(proto_bytes.data(), proto_bytes_read)) {
  235. std::cerr << "Proto parse error for proto of size " << proto_size << ", "
  236. << proto_bytes_read << " proto bytes read total, "
  237. << size_bytes_read << " size bytes read\n\n\n";
  238. file_error_stats["parse_error"]++;
  239. return -1;
  240. }
  241. std::vector<base::StringPiece> der_cert_chain;
  242. for (int i = 0; i < cert_chain.der_certs_size(); i++) {
  243. der_cert_chain.push_back(cert_chain.der_certs(i));
  244. }
  245. scoped_refptr<net::X509Certificate> x509_target_and_intermediates =
  246. net::X509Certificate::CreateFromDERCertChain(der_cert_chain);
  247. if (!x509_target_and_intermediates) {
  248. std::cerr << "X509Certificate::CreateFromDERCertChain failed for host"
  249. << cert_chain.host() << "\n\n\n";
  250. file_error_stats["chain_parse_error"]++;
  251. // We try to continue here; its possible that the cert chain contained
  252. // invalid certs for some reason so we don't bail out entirely.
  253. return 0;
  254. }
  255. net::CertVerifyResult platform_result;
  256. int platform_error;
  257. net::CertVerifyResult builtin_result;
  258. int builtin_error;
  259. platform_proc->VerifyCert(*x509_target_and_intermediates, cert_chain.host(),
  260. &platform_result, &platform_error);
  261. builtin_proc->VerifyCert(*x509_target_and_intermediates, cert_chain.host(),
  262. &builtin_result, &builtin_error);
  263. if (net::CertVerifyResultEqual(platform_result, builtin_result) &&
  264. platform_error == builtin_error) {
  265. chain_processing_stats["equal"]++;
  266. } else {
  267. net::TrialComparisonResult result = net::IsSynchronouslyIgnorableDifference(
  268. platform_error, platform_result, builtin_error, builtin_result,
  269. /*sha1_local_anchors_enabled=*/false);
  270. if (result != net::TrialComparisonResult::kInvalid) {
  271. chain_processing_stats["ignorable_diff"]++;
  272. ignorable_difference_stats[TrialComparisonResultToString(result)]++;
  273. return 0;
  274. }
  275. // Much of the below code is lifted from
  276. // TrialComparisonCertVerifier::Job::OnTrialJobCompleted as it wasn't
  277. // obvious how to easily refactor the code here to prevent copying this
  278. // section of code.
  279. const bool chains_equal =
  280. platform_result.verified_cert->EqualsIncludingChain(
  281. builtin_result.verified_cert.get());
  282. // Chains built were different with either builtin being OK or both not OK.
  283. // Pass builtin chain to platform, see if platform comes back the same.
  284. if (!chains_equal &&
  285. (builtin_error == net::OK || platform_error != net::OK)) {
  286. net::CertVerifyResult platform_reverification_result;
  287. int platform_reverification_error;
  288. platform_proc->VerifyCert(
  289. *builtin_result.verified_cert, cert_chain.host(),
  290. &platform_reverification_result, &platform_reverification_error);
  291. if (net::CertVerifyResultEqual(platform_reverification_result,
  292. builtin_result) &&
  293. platform_reverification_error == builtin_error) {
  294. chain_processing_stats["reverify_ignorable"]++;
  295. return 0;
  296. }
  297. }
  298. chain_processing_stats["different"]++;
  299. std::cout << "\n *************************** \n\n"
  300. << "Host " << cert_chain.host()
  301. << " has different verify results!\n";
  302. std::cout << "\nInput chain: \n "
  303. << FingerPrintCryptoBuffer(
  304. x509_target_and_intermediates->cert_buffer())
  305. << " "
  306. << SubjectFromX509Certificate(x509_target_and_intermediates.get())
  307. << "\n";
  308. for (const auto& intermediate :
  309. x509_target_and_intermediates->intermediate_buffers()) {
  310. std::cout << " " << FingerPrintCryptoBuffer(intermediate.get()) << " "
  311. << SubjectFromCryptoBuffer(intermediate.get()) << "\n";
  312. }
  313. std::cout << "\nPlatform: (error = "
  314. << net::ErrorToShortString(platform_error) << ")\n";
  315. PrintCertVerifyResult(platform_result);
  316. std::cout << "\nBuiltin: (error = "
  317. << net::ErrorToShortString(builtin_error) << ")\n";
  318. PrintCertVerifyResult(builtin_result);
  319. }
  320. return 0;
  321. }
  322. } // namespace
  323. int main(int argc, char** argv) {
  324. base::AtExitManager at_exit_manager;
  325. if (!base::CommandLine::Init(argc, argv)) {
  326. std::cerr << "ERROR in CommandLine::Init\n";
  327. return 1;
  328. }
  329. base::ThreadPoolInstance::CreateAndStartWithDefaultParams(
  330. "cert_verify_comparison_tool");
  331. base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
  332. logging::LoggingSettings settings;
  333. settings.logging_dest =
  334. logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR;
  335. logging::InitLogging(settings);
  336. base::CommandLine::StringVector args = command_line.GetArgs();
  337. if (command_line.HasSwitch("help")) {
  338. PrintUsage(argv[0]);
  339. return 1;
  340. }
  341. base::FilePath input_path = command_line.GetSwitchValuePath("input");
  342. if (input_path.empty()) {
  343. std::cerr << "Error: --input is required\n";
  344. return 1;
  345. }
  346. int flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
  347. std::unique_ptr<base::File> input_file =
  348. std::make_unique<base::File>(input_path, flags);
  349. if (!input_file->IsValid()) {
  350. std::cerr << "Error: --dump file " << input_path.MaybeAsASCII()
  351. << " is not valid \n";
  352. return 1;
  353. }
  354. // Create a network thread to be used for AIA fetches, and wait for a
  355. // CertNetFetcher to be constructed on that thread.
  356. base::Thread::Options options(base::MessagePumpType::IO, 0);
  357. base::Thread thread("network_thread");
  358. CHECK(thread.StartWithOptions(std::move(options)));
  359. // Owned by this thread, but initialized, used, and shutdown on the network
  360. // thread.
  361. std::unique_ptr<net::URLRequestContext> context;
  362. scoped_refptr<net::CertNetFetcherURLRequest> cert_net_fetcher;
  363. base::WaitableEvent initialization_complete_event(
  364. base::WaitableEvent::ResetPolicy::MANUAL,
  365. base::WaitableEvent::InitialState::NOT_SIGNALED);
  366. thread.task_runner()->PostTask(
  367. FROM_HERE,
  368. base::BindOnce(&SetUpOnNetworkThread, &context, &cert_net_fetcher,
  369. &initialization_complete_event));
  370. initialization_complete_event.Wait();
  371. // Initialize verifiers; platform and builtin.
  372. std::vector<std::unique_ptr<CertVerifyImpl>> impls;
  373. std::unique_ptr<CertVerifyImpl> platform_proc =
  374. CreateCertVerifyImplFromName("platform", cert_net_fetcher);
  375. if (!platform_proc) {
  376. std::cerr << "Error platform proc not sucessfully created";
  377. return 1;
  378. }
  379. std::unique_ptr<CertVerifyImpl> builtin_proc =
  380. CreateCertVerifyImplFromName("builtin", cert_net_fetcher);
  381. if (!builtin_proc) {
  382. std::cerr << "Error builtin proc not sucessfully created";
  383. return 1;
  384. }
  385. // Read file and process cert chains.
  386. while (RunCert(input_file.get(), platform_proc, builtin_proc) != -1) {
  387. }
  388. PrintStats();
  389. // Clean up on the network thread and stop it (which waits for the clean up
  390. // task to run).
  391. thread.task_runner()->PostTask(
  392. FROM_HERE,
  393. base::BindOnce(&ShutdownOnNetworkThread, &context, &cert_net_fetcher));
  394. thread.Stop();
  395. }