• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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/cert/internal/trust_store_chrome.h"
6 
7 #include <optional>
8 
9 #include "base/command_line.h"
10 #include "base/containers/span.h"
11 #include "base/containers/to_vector.h"
12 #include "base/logging.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17 #include "crypto/sha2.h"
18 #include "net/cert/root_store_proto_lite/root_store.pb.h"
19 #include "net/cert/x509_certificate.h"
20 #include "net/cert/x509_util.h"
21 #include "third_party/boringssl/src/include/openssl/pool.h"
22 #include "third_party/boringssl/src/pki/cert_errors.h"
23 #include "third_party/boringssl/src/pki/parsed_certificate.h"
24 
25 namespace net {
26 
27 namespace {
28 #include "net/data/ssl/chrome_root_store/chrome-root-store-inc.cc"
29 }  // namespace
30 
31 ChromeRootCertConstraints::ChromeRootCertConstraints() = default;
ChromeRootCertConstraints(std::optional<base::Time> sct_not_after,std::optional<base::Time> sct_all_after,std::optional<base::Version> min_version,std::optional<base::Version> max_version_exclusive,std::vector<std::string> permitted_dns_names)32 ChromeRootCertConstraints::ChromeRootCertConstraints(
33     std::optional<base::Time> sct_not_after,
34     std::optional<base::Time> sct_all_after,
35     std::optional<base::Version> min_version,
36     std::optional<base::Version> max_version_exclusive,
37     std::vector<std::string> permitted_dns_names)
38     : sct_not_after(sct_not_after),
39       sct_all_after(sct_all_after),
40       min_version(std::move(min_version)),
41       max_version_exclusive(std::move(max_version_exclusive)),
42       permitted_dns_names(std::move(permitted_dns_names)) {}
43 
ChromeRootCertConstraints(const StaticChromeRootCertConstraints & constraints)44 ChromeRootCertConstraints::ChromeRootCertConstraints(
45     const StaticChromeRootCertConstraints& constraints)
46     : sct_not_after(constraints.sct_not_after),
47       sct_all_after(constraints.sct_all_after),
48       min_version(constraints.min_version),
49       max_version_exclusive(constraints.max_version_exclusive) {
50   for (std::string_view name : constraints.permitted_dns_names) {
51     permitted_dns_names.emplace_back(name);
52   }
53   if (min_version) {
54     CHECK(min_version->IsValid());
55   }
56   if (max_version_exclusive) {
57     CHECK(max_version_exclusive->IsValid());
58   }
59 }
60 
61 ChromeRootCertConstraints::~ChromeRootCertConstraints() = default;
62 ChromeRootCertConstraints::ChromeRootCertConstraints(
63     const ChromeRootCertConstraints& other) = default;
64 ChromeRootCertConstraints::ChromeRootCertConstraints(
65     ChromeRootCertConstraints&& other) = default;
66 ChromeRootCertConstraints& ChromeRootCertConstraints::operator=(
67     const ChromeRootCertConstraints& other) = default;
68 ChromeRootCertConstraints& ChromeRootCertConstraints::operator=(
69     ChromeRootCertConstraints&& other) = default;
70 
Anchor(std::shared_ptr<const bssl::ParsedCertificate> certificate,std::vector<ChromeRootCertConstraints> constraints)71 ChromeRootStoreData::Anchor::Anchor(
72     std::shared_ptr<const bssl::ParsedCertificate> certificate,
73     std::vector<ChromeRootCertConstraints> constraints)
74     : certificate(std::move(certificate)),
75       constraints(std::move(constraints)) {}
76 ChromeRootStoreData::Anchor::~Anchor() = default;
77 
78 ChromeRootStoreData::Anchor::Anchor(const Anchor& other) = default;
79 ChromeRootStoreData::Anchor::Anchor(Anchor&& other) = default;
80 ChromeRootStoreData::Anchor& ChromeRootStoreData::Anchor::operator=(
81     const ChromeRootStoreData::Anchor& other) = default;
82 ChromeRootStoreData::Anchor& ChromeRootStoreData::Anchor::operator=(
83     ChromeRootStoreData::Anchor&& other) = default;
84 
85 ChromeRootStoreData::ChromeRootStoreData() = default;
86 ChromeRootStoreData::~ChromeRootStoreData() = default;
87 
88 ChromeRootStoreData::ChromeRootStoreData(const ChromeRootStoreData& other) =
89     default;
90 ChromeRootStoreData::ChromeRootStoreData(ChromeRootStoreData&& other) = default;
91 ChromeRootStoreData& ChromeRootStoreData::operator=(
92     const ChromeRootStoreData& other) = default;
93 ChromeRootStoreData& ChromeRootStoreData::operator=(
94     ChromeRootStoreData&& other) = default;
95 
96 std::optional<ChromeRootStoreData>
CreateChromeRootStoreData(const chrome_root_store::RootStore & proto)97 ChromeRootStoreData::CreateChromeRootStoreData(
98     const chrome_root_store::RootStore& proto) {
99   ChromeRootStoreData root_store_data;
100 
101   for (auto& anchor : proto.trust_anchors()) {
102     if (anchor.der().empty()) {
103       LOG(ERROR) << "Error anchor with empty DER in update";
104       return std::nullopt;
105     }
106 
107     auto parsed = bssl::ParsedCertificate::Create(
108         net::x509_util::CreateCryptoBuffer(anchor.der()),
109         net::x509_util::DefaultParseCertificateOptions(), nullptr);
110     if (!parsed) {
111       LOG(ERROR) << "Error parsing cert for update";
112       return std::nullopt;
113     }
114 
115     std::vector<ChromeRootCertConstraints> constraints;
116     for (const auto& constraint : anchor.constraints()) {
117       std::optional<base::Version> min_version;
118       if (constraint.has_min_version()) {
119         min_version = base::Version(constraint.min_version());
120         if (!min_version->IsValid()) {
121           LOG(ERROR) << "Error parsing version";
122           return std::nullopt;
123         }
124       }
125 
126       std::optional<base::Version> max_version_exclusive;
127       if (constraint.has_max_version_exclusive()) {
128         max_version_exclusive =
129             base::Version(constraint.max_version_exclusive());
130         if (!max_version_exclusive->IsValid()) {
131           LOG(ERROR) << "Error parsing version";
132           return std::nullopt;
133         }
134       }
135 
136       constraints.emplace_back(
137           constraint.has_sct_not_after_sec()
138               ? std::optional(base::Time::UnixEpoch() +
139                               base::Seconds(constraint.sct_not_after_sec()))
140               : std::nullopt,
141           constraint.has_sct_all_after_sec()
142               ? std::optional(base::Time::UnixEpoch() +
143                               base::Seconds(constraint.sct_all_after_sec()))
144               : std::nullopt,
145           min_version, max_version_exclusive,
146           base::ToVector(constraint.permitted_dns_names()));
147     }
148     root_store_data.anchors_.emplace_back(std::move(parsed),
149                                           std::move(constraints));
150   }
151 
152   root_store_data.version_ = proto.version_major();
153 
154   return root_store_data;
155 }
156 
TrustStoreChrome()157 TrustStoreChrome::TrustStoreChrome()
158     : TrustStoreChrome(
159           kChromeRootCertList,
160           /*certs_are_static=*/true,
161           /*version=*/CompiledChromeRootStoreVersion(),
162           /*override_constraints=*/InitializeConstraintsOverrides()) {}
163 
TrustStoreChrome(base::span<const ChromeRootCertInfo> certs,bool certs_are_static,int64_t version,ConstraintOverrideMap override_constraints)164 TrustStoreChrome::TrustStoreChrome(base::span<const ChromeRootCertInfo> certs,
165                                    bool certs_are_static,
166                                    int64_t version,
167                                    ConstraintOverrideMap override_constraints)
168     : override_constraints_(std::move(override_constraints)) {
169   std::vector<
170       std::pair<std::string_view, std::vector<ChromeRootCertConstraints>>>
171       constraints;
172 
173   // TODO(hchao, sleevi): Explore keeping a CRYPTO_BUFFER of just the DER
174   // certificate and subject name. This would hopefully save memory compared
175   // to keeping the full parsed representation in memory, especially when
176   // there are multiple instances of TrustStoreChrome.
177   for (const auto& cert_info : certs) {
178     bssl::UniquePtr<CRYPTO_BUFFER> cert;
179     if (certs_are_static) {
180       // TODO(mattm,hchao): Ensure the static data crypto_buffers for the
181       // compiled-in roots are kept alive, so that roots from the component
182       // updater data will de-dupe against them. This currently works if the
183       // new components roots are the same as the compiled in roots, but
184       // fails if a component update drops a root and then the next component
185       // update readds the root without a restart.
186       cert = x509_util::CreateCryptoBufferFromStaticDataUnsafe(
187           cert_info.root_cert_der);
188     } else {
189       cert = x509_util::CreateCryptoBuffer(cert_info.root_cert_der);
190     }
191     bssl::CertErrors errors;
192     auto parsed = bssl::ParsedCertificate::Create(
193         std::move(cert), x509_util::DefaultParseCertificateOptions(), &errors);
194     // There should always be a valid cert, because we should be parsing Chrome
195     // Root Store static data compiled in.
196     CHECK(parsed);
197     if (!cert_info.constraints.empty()) {
198       std::vector<ChromeRootCertConstraints> cert_constraints;
199       for (const auto& constraint : cert_info.constraints) {
200         cert_constraints.emplace_back(constraint);
201       }
202       constraints.emplace_back(parsed->der_cert().AsStringView(),
203                                std::move(cert_constraints));
204     }
205     trust_store_.AddTrustAnchor(std::move(parsed));
206   }
207 
208   constraints_ = base::flat_map(std::move(constraints));
209   version_ = version;
210 }
211 
TrustStoreChrome(const ChromeRootStoreData & root_store_data)212 TrustStoreChrome::TrustStoreChrome(const ChromeRootStoreData& root_store_data)
213     : override_constraints_(InitializeConstraintsOverrides()) {
214   std::vector<
215       std::pair<std::string_view, std::vector<ChromeRootCertConstraints>>>
216       constraints;
217 
218   for (const auto& anchor : root_store_data.anchors()) {
219     if (!anchor.constraints.empty()) {
220       constraints.emplace_back(anchor.certificate->der_cert().AsStringView(),
221                                anchor.constraints);
222     }
223     trust_store_.AddTrustAnchor(anchor.certificate);
224   }
225 
226   constraints_ = base::flat_map(std::move(constraints));
227   version_ = root_store_data.version();
228 }
229 
230 TrustStoreChrome::~TrustStoreChrome() = default;
231 
232 TrustStoreChrome::ConstraintOverrideMap
InitializeConstraintsOverrides()233 TrustStoreChrome::InitializeConstraintsOverrides() {
234   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
235   if (command_line->HasSwitch(kTestCrsConstraintsSwitch)) {
236     return ParseCrsConstraintsSwitch(
237         command_line->GetSwitchValueASCII(kTestCrsConstraintsSwitch));
238   }
239 
240   return {};
241 }
242 
243 TrustStoreChrome::ConstraintOverrideMap
ParseCrsConstraintsSwitch(std::string_view switch_value)244 TrustStoreChrome::ParseCrsConstraintsSwitch(std::string_view switch_value) {
245   // This function constructs a flat_map on the fly rather than the more
246   // efficient approach of creating a vector first and then constructing the
247   // flat_map from that. It is expected that there will only be a small number
248   // of elements in the map, and that this is only used for testing, therefore
249   // simplicity of the implementation is weighted higher than theoretical
250   // efficiency.
251   ConstraintOverrideMap constraints;
252 
253   base::StringPairs roots_and_constraints_pairs;
254   base::SplitStringIntoKeyValuePairs(switch_value, ':', '+',
255                                      &roots_and_constraints_pairs);
256   for (const auto& [root_hashes_hex, root_constraints] :
257        roots_and_constraints_pairs) {
258     std::vector<std::array<uint8_t, crypto::kSHA256Length>> root_hashes;
259     for (std::string_view root_hash_hex :
260          base::SplitStringPiece(root_hashes_hex, ",", base::TRIM_WHITESPACE,
261                                 base::SPLIT_WANT_NONEMPTY)) {
262       std::array<uint8_t, crypto::kSHA256Length> root_hash;
263       if (!base::HexStringToSpan(root_hash_hex, root_hash)) {
264         LOG(ERROR) << "invalid root hash: " << root_hash_hex;
265         continue;
266       }
267       root_hashes.push_back(std::move(root_hash));
268     }
269     if (root_hashes.empty()) {
270       LOG(ERROR) << "skipped constraintset with no valid root hashes";
271       continue;
272     }
273     ChromeRootCertConstraints constraint;
274     base::StringPairs constraint_value_pairs;
275     base::SplitStringIntoKeyValuePairs(root_constraints, '=', ',',
276                                        &constraint_value_pairs);
277     for (const auto& [constraint_name, constraint_value] :
278          constraint_value_pairs) {
279       std::string constraint_name_lower = base::ToLowerASCII(constraint_name);
280       if (constraint_name_lower == "sctnotafter") {
281         int64_t value;
282         if (!base::StringToInt64(constraint_value, &value)) {
283           LOG(ERROR) << "invalid sctnotafter: " << constraint_value;
284           continue;
285         }
286         constraint.sct_not_after =
287             base::Time::UnixEpoch() + base::Seconds(value);
288       } else if (constraint_name_lower == "sctallafter") {
289         int64_t value;
290         if (!base::StringToInt64(constraint_value, &value)) {
291           LOG(ERROR) << "invalid sctallafter: " << constraint_value;
292           continue;
293         }
294         constraint.sct_all_after =
295             base::Time::UnixEpoch() + base::Seconds(value);
296       } else if (constraint_name_lower == "minversion") {
297         base::Version version(constraint_value);
298         if (!version.IsValid()) {
299           LOG(ERROR) << "invalid minversion: " << constraint_value;
300           continue;
301         }
302         constraint.min_version = version;
303       } else if (constraint_name_lower == "maxversionexclusive") {
304         base::Version version(constraint_value);
305         if (!version.IsValid()) {
306           LOG(ERROR) << "invalid maxversionexclusive: " << constraint_value;
307           continue;
308         }
309         constraint.max_version_exclusive = version;
310       } else if (constraint_name_lower == "dns") {
311         constraint.permitted_dns_names.push_back(constraint_value);
312       } else {
313         LOG(ERROR) << "unrecognized constraint " << constraint_name_lower;
314       }
315       // TODO(crbug.com/40941039): add other constraint types here when they
316       // are implemented
317     }
318     for (const auto& root_hash : root_hashes) {
319       constraints[root_hash].push_back(constraint);
320     }
321   }
322 
323   return constraints;
324 }
325 
SyncGetIssuersOf(const bssl::ParsedCertificate * cert,bssl::ParsedCertificateList * issuers)326 void TrustStoreChrome::SyncGetIssuersOf(const bssl::ParsedCertificate* cert,
327                                         bssl::ParsedCertificateList* issuers) {
328   trust_store_.SyncGetIssuersOf(cert, issuers);
329 }
330 
GetTrust(const bssl::ParsedCertificate * cert)331 bssl::CertificateTrust TrustStoreChrome::GetTrust(
332     const bssl::ParsedCertificate* cert) {
333   return trust_store_.GetTrust(cert);
334 }
335 
Contains(const bssl::ParsedCertificate * cert) const336 bool TrustStoreChrome::Contains(const bssl::ParsedCertificate* cert) const {
337   return trust_store_.Contains(cert);
338 }
339 
340 base::span<const ChromeRootCertConstraints>
GetConstraintsForCert(const bssl::ParsedCertificate * cert) const341 TrustStoreChrome::GetConstraintsForCert(
342     const bssl::ParsedCertificate* cert) const {
343   if (!override_constraints_.empty()) {
344     const std::array<uint8_t, crypto::kSHA256Length> cert_hash =
345         crypto::SHA256Hash(cert->der_cert());
346     auto it = override_constraints_.find(cert_hash);
347     if (it != override_constraints_.end()) {
348       return it->second;
349     }
350   }
351 
352   auto it = constraints_.find(cert->der_cert().AsStringView());
353   if (it != constraints_.end()) {
354     return it->second;
355   }
356   return {};
357 }
358 
359 // static
CreateTrustStoreForTesting(base::span<const ChromeRootCertInfo> certs,int64_t version,ConstraintOverrideMap override_constraints)360 std::unique_ptr<TrustStoreChrome> TrustStoreChrome::CreateTrustStoreForTesting(
361     base::span<const ChromeRootCertInfo> certs,
362     int64_t version,
363     ConstraintOverrideMap override_constraints) {
364   // Note: wrap_unique is used because the constructor is private.
365   return base::WrapUnique(new TrustStoreChrome(
366       certs,
367       /*certs_are_static=*/false,
368       /*version=*/version, std::move(override_constraints)));
369 }
370 
CompiledChromeRootStoreVersion()371 int64_t CompiledChromeRootStoreVersion() {
372   return kRootStoreVersion;
373 }
374 
CompiledChromeRootStoreAnchors()375 std::vector<ChromeRootStoreData::Anchor> CompiledChromeRootStoreAnchors() {
376   std::vector<ChromeRootStoreData::Anchor> anchors;
377   for (const auto& cert_info : kChromeRootCertList) {
378     bssl::UniquePtr<CRYPTO_BUFFER> cert =
379         x509_util::CreateCryptoBufferFromStaticDataUnsafe(
380             cert_info.root_cert_der);
381     bssl::CertErrors errors;
382     auto parsed = bssl::ParsedCertificate::Create(
383         std::move(cert), x509_util::DefaultParseCertificateOptions(), &errors);
384     DCHECK(parsed);
385 
386     std::vector<ChromeRootCertConstraints> cert_constraints;
387     for (const auto& constraint : cert_info.constraints) {
388       cert_constraints.emplace_back(constraint);
389     }
390     anchors.emplace_back(std::move(parsed), std::move(cert_constraints));
391   }
392 
393   return anchors;
394 }
395 
396 }  // namespace net
397