xref: /aosp_15_r20/external/cronet/net/tools/cert_verify_tool/verify_using_path_builder.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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_path_builder.h"
6 
7 #include <iostream>
8 #include <memory>
9 
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/time/time.h"
13 #include "crypto/sha2.h"
14 #include "net/cert/cert_net_fetcher.h"
15 #include "net/cert/internal/cert_issuer_source_aia.h"
16 #include "net/cert/internal/system_trust_store.h"
17 #include "net/cert/time_conversions.h"
18 #include "net/cert/x509_certificate.h"
19 #include "net/cert/x509_util.h"
20 #include "net/tools/cert_verify_tool/cert_verify_tool_util.h"
21 #include "third_party/boringssl/src/include/openssl/bytestring.h"
22 #include "third_party/boringssl/src/include/openssl/mem.h"
23 #include "third_party/boringssl/src/pki/cert_issuer_source_static.h"
24 #include "third_party/boringssl/src/pki/parse_name.h"
25 #include "third_party/boringssl/src/pki/parsed_certificate.h"
26 #include "third_party/boringssl/src/pki/path_builder.h"
27 #include "third_party/boringssl/src/pki/simple_path_builder_delegate.h"
28 #include "third_party/boringssl/src/pki/trust_store_collection.h"
29 #include "third_party/boringssl/src/pki/trust_store_in_memory.h"
30 
31 namespace {
32 
AddPemEncodedCert(const bssl::ParsedCertificate * cert,std::vector<std::string> * pem_encoded_chain)33 bool AddPemEncodedCert(const bssl::ParsedCertificate* cert,
34                        std::vector<std::string>* pem_encoded_chain) {
35   std::string der_cert(cert->der_cert().AsStringView());
36   std::string pem;
37   if (!net::X509Certificate::GetPEMEncodedFromDER(der_cert, &pem)) {
38     std::cerr << "ERROR: GetPEMEncodedFromDER failed\n";
39     return false;
40   }
41   pem_encoded_chain->push_back(pem);
42   return true;
43 }
44 
45 // Dumps a chain of bssl::ParsedCertificate objects to a PEM file.
DumpParsedCertificateChain(const base::FilePath & file_path,const bssl::CertPathBuilderResultPath & path)46 bool DumpParsedCertificateChain(const base::FilePath& file_path,
47                                 const bssl::CertPathBuilderResultPath& path) {
48   std::vector<std::string> pem_encoded_chain;
49   for (const auto& cert : path.certs) {
50     if (!AddPemEncodedCert(cert.get(), &pem_encoded_chain))
51       return false;
52   }
53 
54   return WriteToFile(file_path, base::JoinString(pem_encoded_chain, ""));
55 }
56 
57 // Returns a hex-encoded sha256 of the DER-encoding of |cert|.
FingerPrintParsedCertificate(const bssl::ParsedCertificate * cert)58 std::string FingerPrintParsedCertificate(const bssl::ParsedCertificate* cert) {
59   std::string hash = crypto::SHA256HashString(cert->der_cert().AsStringView());
60   return base::HexEncode(hash);
61 }
62 
SubjectToString(const bssl::RDNSequence & parsed_subject)63 std::string SubjectToString(const bssl::RDNSequence& parsed_subject) {
64   std::string subject_str;
65   if (!bssl::ConvertToRFC2253(parsed_subject, &subject_str)) {
66     return std::string();
67   }
68   return subject_str;
69 }
70 
71 // Returns a textual representation of the Subject of |cert|.
SubjectFromParsedCertificate(const bssl::ParsedCertificate * cert)72 std::string SubjectFromParsedCertificate(const bssl::ParsedCertificate* cert) {
73   bssl::RDNSequence parsed_subject;
74   if (!bssl::ParseName(cert->tbs().subject_tlv, &parsed_subject)) {
75     return std::string();
76   }
77   return SubjectToString(parsed_subject);
78 }
79 
80 // Dumps a ResultPath to std::cout.
PrintResultPath(const bssl::CertPathBuilderResultPath * result_path,size_t index,bool is_best)81 void PrintResultPath(const bssl::CertPathBuilderResultPath* result_path,
82                      size_t index,
83                      bool is_best) {
84   std::cout << "path " << index << " "
85             << (result_path->IsValid() ? "valid" : "invalid")
86             << (is_best ? " (best)" : "") << "\n";
87 
88   // Print the certificate chain.
89   for (const auto& cert : result_path->certs) {
90     std::cout << " " << FingerPrintParsedCertificate(cert.get()) << " "
91               << SubjectFromParsedCertificate(cert.get()) << "\n";
92   }
93 
94   // Print the certificate policies.
95   if (!result_path->user_constrained_policy_set.empty()) {
96     std::cout << "Certificate policies:\n";
97     for (const auto& policy : result_path->user_constrained_policy_set) {
98       CBS cbs;
99       CBS_init(&cbs, policy.data(), policy.size());
100       bssl::UniquePtr<char> policy_text(CBS_asn1_oid_to_text(&cbs));
101       if (policy_text) {
102         std::cout << " " << policy_text.get() << "\n";
103       } else {
104         std::cout << " (invalid OID)\n";
105       }
106     }
107   }
108 
109   // Print the errors/warnings if there were any.
110   std::string errors_str =
111       result_path->errors.ToDebugString(result_path->certs);
112   if (!errors_str.empty()) {
113     std::cout << "Errors:\n";
114     std::cout << errors_str << "\n";
115   }
116 }
117 
ParseCertificate(const CertInput & input)118 std::shared_ptr<const bssl::ParsedCertificate> ParseCertificate(
119     const CertInput& input) {
120   bssl::CertErrors errors;
121   std::shared_ptr<const bssl::ParsedCertificate> cert =
122       bssl::ParsedCertificate::Create(
123           net::x509_util::CreateCryptoBuffer(input.der_cert), {}, &errors);
124   if (!cert) {
125     PrintCertError("ERROR: ParseCertificate failed:", input);
126     std::cout << errors.ToDebugString() << "\n";
127   }
128 
129   // TODO(crbug.com/634443): Print errors if there are any on success too (i.e.
130   //                         warnings).
131 
132   return cert;
133 }
134 
135 }  // namespace
136 
137 // Verifies |target_der_cert| using bssl::CertPathBuilder.
VerifyUsingPathBuilder(const CertInput & target_der_cert,const std::vector<CertInput> & intermediate_der_certs,const std::vector<CertInputWithTrustSetting> & der_certs_with_trust_settings,const base::Time at_time,const base::FilePath & dump_prefix_path,scoped_refptr<net::CertNetFetcher> cert_net_fetcher,net::SystemTrustStore * system_trust_store)138 bool VerifyUsingPathBuilder(
139     const CertInput& target_der_cert,
140     const std::vector<CertInput>& intermediate_der_certs,
141     const std::vector<CertInputWithTrustSetting>& der_certs_with_trust_settings,
142     const base::Time at_time,
143     const base::FilePath& dump_prefix_path,
144     scoped_refptr<net::CertNetFetcher> cert_net_fetcher,
145     net::SystemTrustStore* system_trust_store) {
146   bssl::der::GeneralizedTime time;
147   if (!net::EncodeTimeAsGeneralizedTime(at_time, &time)) {
148     return false;
149   }
150 
151   bssl::TrustStoreInMemory additional_roots;
152   for (const auto& cert_input_with_trust : der_certs_with_trust_settings) {
153     std::shared_ptr<const bssl::ParsedCertificate> cert =
154         ParseCertificate(cert_input_with_trust.cert_input);
155     if (cert) {
156       additional_roots.AddCertificate(std::move(cert),
157                                       cert_input_with_trust.trust);
158     }
159   }
160   bssl::TrustStoreCollection trust_store;
161   trust_store.AddTrustStore(&additional_roots);
162   trust_store.AddTrustStore(system_trust_store->GetTrustStore());
163 
164   bssl::CertIssuerSourceStatic intermediate_cert_issuer_source;
165   for (const auto& der_cert : intermediate_der_certs) {
166     std::shared_ptr<const bssl::ParsedCertificate> cert =
167         ParseCertificate(der_cert);
168     if (cert)
169       intermediate_cert_issuer_source.AddCert(cert);
170   }
171 
172   std::shared_ptr<const bssl::ParsedCertificate> target_cert =
173       ParseCertificate(target_der_cert);
174   if (!target_cert)
175     return false;
176 
177   // Verify the chain.
178   bssl::SimplePathBuilderDelegate delegate(
179       2048, bssl::SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1);
180   bssl::CertPathBuilder path_builder(target_cert, &trust_store, &delegate, time,
181                                      bssl::KeyPurpose::SERVER_AUTH,
182                                      bssl::InitialExplicitPolicy::kFalse,
183                                      {bssl::der::Input(bssl::kAnyPolicyOid)},
184                                      bssl::InitialPolicyMappingInhibit::kFalse,
185                                      bssl::InitialAnyPolicyInhibit::kFalse);
186   path_builder.AddCertIssuerSource(&intermediate_cert_issuer_source);
187 
188   std::unique_ptr<net::CertIssuerSourceAia> aia_cert_issuer_source;
189   if (cert_net_fetcher.get()) {
190     aia_cert_issuer_source =
191         std::make_unique<net::CertIssuerSourceAia>(std::move(cert_net_fetcher));
192     path_builder.AddCertIssuerSource(aia_cert_issuer_source.get());
193   }
194 
195   // TODO(mattm): should this be a command line flag?
196   path_builder.SetExploreAllPaths(true);
197 
198   // Run the path builder.
199   bssl::CertPathBuilder::Result result = path_builder.Run();
200 
201   // TODO(crbug.com/634443): Display any errors/warnings associated with path
202   //                         building that were not part of a particular
203   //                         PathResult.
204   std::cout << "CertPathBuilder result: "
205             << (result.HasValidPath() ? "SUCCESS" : "FAILURE") << "\n";
206 
207   for (size_t i = 0; i < result.paths.size(); ++i) {
208     PrintResultPath(result.paths[i].get(), i, i == result.best_result_index);
209   }
210 
211   // TODO(mattm): add flag to dump all paths, not just the final one?
212   if (!dump_prefix_path.empty() && !result.paths.empty()) {
213     if (!DumpParsedCertificateChain(
214             dump_prefix_path.AddExtension(
215                 FILE_PATH_LITERAL(".CertPathBuilder.pem")),
216             *result.GetBestPathPossiblyInvalid())) {
217       return false;
218     }
219   }
220 
221   return result.HasValidPath();
222 }
223