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