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 "net/tools/cert_verify_tool/verify_using_cert_verify_proc.h"
6
7 #include <algorithm>
8 #include <iostream>
9
10 #include "base/strings/strcat.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "crypto/sha2.h"
14 #include "net/base/net_errors.h"
15 #include "net/cert/cert_verifier.h"
16 #include "net/cert/cert_verify_proc.h"
17 #include "net/cert/cert_verify_result.h"
18 #include "net/cert/test_root_certs.h"
19 #include "net/cert/x509_certificate.h"
20 #include "net/cert/x509_util.h"
21 #include "net/log/net_log_with_source.h"
22 #include "net/tools/cert_verify_tool/cert_verify_tool_util.h"
23
24 namespace {
25
26 // Associates a printable name with an integer constant. Useful for providing
27 // human-readable decoding of bitmask values.
28 struct StringToConstant {
29 const char* name;
30 const int constant;
31 };
32
33 const StringToConstant kCertStatusFlags[] = {
34 #define CERT_STATUS_FLAG(label, value) {#label, value},
35 #include "net/cert/cert_status_flags_list.h"
36 #undef CERT_STATUS_FLAG
37 };
38
39 // Writes a PEM-encoded file of |cert| and its chain.
DumpX509CertificateChain(const base::FilePath & file_path,const net::X509Certificate * cert)40 bool DumpX509CertificateChain(const base::FilePath& file_path,
41 const net::X509Certificate* cert) {
42 std::vector<std::string> pem_encoded;
43 if (!cert->GetPEMEncodedChain(&pem_encoded)) {
44 std::cerr << "ERROR: X509Certificate::GetPEMEncodedChain failed.\n";
45 return false;
46 }
47 return WriteToFile(file_path, base::StrCat(pem_encoded));
48 }
49
PrintCertStatus(int cert_status)50 void PrintCertStatus(int cert_status) {
51 std::cout << base::StringPrintf("CertStatus: 0x%x\n", cert_status);
52
53 for (const auto& flag : kCertStatusFlags) {
54 if ((cert_status & flag.constant) == flag.constant)
55 std::cout << " " << flag.name << "\n";
56 }
57 }
58
59 } // namespace
60
PrintCertVerifyResult(const net::CertVerifyResult & result)61 void PrintCertVerifyResult(const net::CertVerifyResult& result) {
62 PrintCertStatus(result.cert_status);
63 if (result.has_sha1)
64 std::cout << "has_sha1\n";
65 if (result.is_issued_by_known_root)
66 std::cout << "is_issued_by_known_root\n";
67 if (result.is_issued_by_additional_trust_anchor)
68 std::cout << "is_issued_by_additional_trust_anchor\n";
69
70 if (result.verified_cert) {
71 std::cout << "chain:\n "
72 << FingerPrintCryptoBuffer(result.verified_cert->cert_buffer())
73 << " " << SubjectFromX509Certificate(result.verified_cert.get())
74 << "\n";
75 for (const auto& intermediate :
76 result.verified_cert->intermediate_buffers()) {
77 std::cout << " " << FingerPrintCryptoBuffer(intermediate.get()) << " "
78 << SubjectFromCryptoBuffer(intermediate.get()) << "\n";
79 }
80 }
81 }
82
VerifyUsingCertVerifyProc(net::CertVerifyProc * cert_verify_proc,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,const base::FilePath & dump_path)83 bool VerifyUsingCertVerifyProc(
84 net::CertVerifyProc* cert_verify_proc,
85 const CertInput& target_der_cert,
86 const std::string& hostname,
87 const std::vector<CertInput>& intermediate_der_certs,
88 const std::vector<CertInputWithTrustSetting>& der_certs_with_trust_settings,
89 const base::FilePath& dump_path) {
90 std::vector<base::StringPiece> der_cert_chain;
91 der_cert_chain.push_back(target_der_cert.der_cert);
92 for (const auto& cert : intermediate_der_certs)
93 der_cert_chain.push_back(cert.der_cert);
94
95 scoped_refptr<net::X509Certificate> x509_target_and_intermediates =
96 net::X509Certificate::CreateFromDERCertChain(der_cert_chain);
97 if (!x509_target_and_intermediates) {
98 std::cerr
99 << "ERROR: X509Certificate::CreateFromDERCertChain failed on one or "
100 "more of:\n";
101 PrintCertError(" (target)", target_der_cert);
102 for (const auto& cert : intermediate_der_certs)
103 PrintCertError(" (intermediate)", cert);
104 return false;
105 }
106
107 net::TestRootCerts* test_root_certs = net::TestRootCerts::GetInstance();
108 CHECK(test_root_certs->IsEmpty());
109
110 std::vector<net::ScopedTestRoot> scoped_test_roots;
111 for (const auto& cert_input_with_trust : der_certs_with_trust_settings) {
112 scoped_refptr<net::X509Certificate> x509_root =
113 net::X509Certificate::CreateFromBytes(base::as_bytes(
114 base::make_span(cert_input_with_trust.cert_input.der_cert)));
115
116 if (!x509_root) {
117 PrintCertError("ERROR: X509Certificate::CreateFromBytes failed:",
118 cert_input_with_trust.cert_input);
119 } else {
120 scoped_test_roots.emplace_back(x509_root, cert_input_with_trust.trust);
121 }
122 }
123
124 // TODO(mattm): add command line flags to configure VerifyFlags.
125 int flags = 0;
126
127 // TODO(crbug.com/634484): use a real netlog and print the results?
128 net::CertVerifyResult result;
129 int rv = cert_verify_proc->Verify(
130 x509_target_and_intermediates.get(), hostname,
131 /*ocsp_response=*/std::string(), /*sct_list=*/std::string(), flags,
132 &result, net::NetLogWithSource());
133
134 std::cout << "CertVerifyProc result: " << net::ErrorToShortString(rv) << "\n";
135 PrintCertVerifyResult(result);
136 if (!dump_path.empty() && result.verified_cert) {
137 if (!DumpX509CertificateChain(dump_path, result.verified_cert.get())) {
138 return false;
139 }
140 }
141
142 return rv == net::OK;
143 }
144