• 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 "base/containers/span.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/string_util.h"
10 #include "crypto/sha2.h"
11 #include "net/cert/x509_certificate.h"
12 #include "net/cert/x509_util.h"
13 #include "net/test/cert_builder.h"
14 #include "net/test/cert_test_util.h"
15 #include "net/test/test_data_directory.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "third_party/boringssl/src/pki/cert_errors.h"
19 #include "third_party/boringssl/src/pki/parsed_certificate.h"
20 
21 namespace net {
22 namespace {
23 
24 #include "net/data/ssl/chrome_root_store/chrome-root-store-test-data-inc.cc"
25 
ToParsedCertificate(const X509Certificate & cert)26 std::shared_ptr<const bssl::ParsedCertificate> ToParsedCertificate(
27     const X509Certificate& cert) {
28   bssl::CertErrors errors;
29   std::shared_ptr<const bssl::ParsedCertificate> parsed =
30       bssl::ParsedCertificate::Create(
31           bssl::UpRef(cert.cert_buffer()),
32           x509_util::DefaultParseCertificateOptions(), &errors);
33   EXPECT_TRUE(parsed) << errors.ToDebugString();
34   return parsed;
35 }
36 
MakeTestRoot()37 scoped_refptr<X509Certificate> MakeTestRoot() {
38   auto builder = std::make_unique<CertBuilder>(nullptr, nullptr);
39   auto now = base::Time::Now();
40   builder->SetValidity(now - base::Days(1), now + base::Days(1));
41   builder->SetBasicConstraints(/*is_ca=*/true, /*path_len=*/-1);
42   builder->SetKeyUsages(
43       {bssl::KEY_USAGE_BIT_KEY_CERT_SIGN, bssl::KEY_USAGE_BIT_CRL_SIGN});
44   return builder->GetX509Certificate();
45 }
46 
TEST(TrustStoreChromeTestNoFixture,ContainsCert)47 TEST(TrustStoreChromeTestNoFixture, ContainsCert) {
48   std::unique_ptr<TrustStoreChrome> trust_store_chrome =
49       TrustStoreChrome::CreateTrustStoreForTesting(
50           base::span<const ChromeRootCertInfo>(kChromeRootCertList),
51           /*version=*/1);
52 
53   // Check every certificate in test_store.certs is included.
54   CertificateList certs = CreateCertificateListFromFile(
55       GetTestNetDataDirectory().AppendASCII("ssl/chrome_root_store"),
56       "test_store.certs", X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
57   ASSERT_EQ(certs.size(), 2u);
58 
59   for (const auto& cert : certs) {
60     std::shared_ptr<const bssl::ParsedCertificate> parsed =
61         ToParsedCertificate(*cert);
62     ASSERT_TRUE(trust_store_chrome->Contains(parsed.get()));
63     bssl::CertificateTrust trust = trust_store_chrome->GetTrust(parsed.get());
64     EXPECT_EQ(bssl::CertificateTrust::ForTrustAnchor().ToDebugString(),
65               trust.ToDebugString());
66   }
67 
68   // Other certificates should not be included. Which test cert used here isn't
69   // important as long as it isn't one of the certificates in the
70   // chrome_root_store/test_store.certs.
71   scoped_refptr<X509Certificate> other_cert =
72       ImportCertFromFile(GetTestCertsDirectory(), "root_ca_cert.pem");
73   ASSERT_TRUE(other_cert);
74   std::shared_ptr<const bssl::ParsedCertificate> other_parsed =
75       ToParsedCertificate(*other_cert);
76   ASSERT_FALSE(trust_store_chrome->Contains(other_parsed.get()));
77   bssl::CertificateTrust trust =
78       trust_store_chrome->GetTrust(other_parsed.get());
79   EXPECT_EQ(bssl::CertificateTrust::ForUnspecified().ToDebugString(),
80             trust.ToDebugString());
81 }
82 
TEST(TrustStoreChromeTestNoFixture,Constraints)83 TEST(TrustStoreChromeTestNoFixture, Constraints) {
84   std::unique_ptr<TrustStoreChrome> trust_store_chrome =
85       TrustStoreChrome::CreateTrustStoreForTesting(
86           base::span<const ChromeRootCertInfo>(kChromeRootCertList),
87           /*version=*/1);
88 
89   const std::string kUnconstrainedCertHash =
90       "568d6905a2c88708a4b3025190edcfedb1974a606a13c6e5290fcb2ae63edab5";
91   const std::string kConstrainedCertHash =
92       "6b9c08e86eb0f767cfad65cd98b62149e5494a67f5845e7bd1ed019f27b86bd6";
93 
94   std::shared_ptr<const bssl::ParsedCertificate> constrained_cert;
95   std::shared_ptr<const bssl::ParsedCertificate> unconstrained_cert;
96 
97   CertificateList certs = CreateCertificateListFromFile(
98       GetTestNetDataDirectory().AppendASCII("ssl/chrome_root_store"),
99       "test_store.certs", X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
100   ASSERT_EQ(certs.size(), 2u);
101   for (const auto& cert : certs) {
102     std::shared_ptr<const bssl::ParsedCertificate> parsed =
103         ToParsedCertificate(*cert);
104     std::string sha256_hex = base::ToLowerASCII(
105         base::HexEncode(crypto::SHA256Hash(parsed->der_cert())));
106     if (sha256_hex == kConstrainedCertHash) {
107       constrained_cert = parsed;
108     } else if (sha256_hex == kUnconstrainedCertHash) {
109       unconstrained_cert = parsed;
110     }
111   }
112 
113   ASSERT_TRUE(unconstrained_cert);
114   EXPECT_TRUE(
115       trust_store_chrome->GetConstraintsForCert(unconstrained_cert.get())
116           .empty());
117 
118   ASSERT_TRUE(constrained_cert);
119   base::span<const ChromeRootCertConstraints> constraints =
120       trust_store_chrome->GetConstraintsForCert(constrained_cert.get());
121   ASSERT_EQ(constraints.size(), 3U);
122 
123   EXPECT_FALSE(constraints[0].sct_all_after.has_value());
124   ASSERT_TRUE(constraints[0].sct_not_after.has_value());
125   EXPECT_EQ(
126       constraints[0].sct_not_after.value().InMillisecondsSinceUnixEpoch() /
127           1000,
128       0x5af);
129   EXPECT_FALSE(constraints[0].min_version.has_value());
130   ASSERT_TRUE(constraints[0].max_version_exclusive.has_value());
131   EXPECT_EQ(constraints[0].max_version_exclusive.value().components(),
132             std::vector<uint32_t>({125, 0, 6368, 2}));
133   EXPECT_THAT(constraints[0].permitted_dns_names,
134               testing::ElementsAre("foo.example.com", "bar.example.com"));
135 
136   EXPECT_FALSE(constraints[1].sct_not_after.has_value());
137   ASSERT_TRUE(constraints[1].sct_all_after.has_value());
138   EXPECT_EQ(
139       constraints[1].sct_all_after.value().InMillisecondsSinceUnixEpoch() /
140           1000,
141       0x2579);
142   ASSERT_TRUE(constraints[1].min_version.has_value());
143   EXPECT_FALSE(constraints[1].max_version_exclusive.has_value());
144   EXPECT_EQ(constraints[1].min_version.value().components(),
145             std::vector<uint32_t>({128}));
146   EXPECT_TRUE(constraints[1].permitted_dns_names.empty());
147 
148   EXPECT_THAT(constraints[2].permitted_dns_names,
149               testing::ElementsAre("baz.example.com"));
150 
151   // Other certificates should return nullptr if they are queried for CRS
152   // constraints. Which test cert used here isn't important as long as it isn't
153   // one of the certificates in the chrome_root_store/test_store.certs.
154   scoped_refptr<X509Certificate> other_cert =
155       ImportCertFromFile(GetTestCertsDirectory(), "root_ca_cert.pem");
156   ASSERT_TRUE(other_cert);
157   std::shared_ptr<const bssl::ParsedCertificate> other_parsed =
158       ToParsedCertificate(*other_cert);
159   ASSERT_TRUE(other_parsed);
160   EXPECT_FALSE(trust_store_chrome->Contains(other_parsed.get()));
161   EXPECT_TRUE(
162       trust_store_chrome->GetConstraintsForCert(other_parsed.get()).empty());
163 }
164 
TEST(TrustStoreChromeTestNoFixture,OverrideConstraints)165 TEST(TrustStoreChromeTestNoFixture, OverrideConstraints) {
166   // Root1: has no constraints and no override constraints
167   // Root2: has constraints and no override constraints
168   // Root3: has no constraints and has override constraints
169   // Root4: has constraints and has override constraints
170   // Root5: not present in CRS and no override constraints
171   // Root6: not present in CRS but has override constraints
172   scoped_refptr<X509Certificate> root1 = MakeTestRoot();
173   scoped_refptr<X509Certificate> root2 = MakeTestRoot();
174   scoped_refptr<X509Certificate> root3 = MakeTestRoot();
175   scoped_refptr<X509Certificate> root4 = MakeTestRoot();
176   scoped_refptr<X509Certificate> root5 = MakeTestRoot();
177   scoped_refptr<X509Certificate> root6 = MakeTestRoot();
178 
179   std::vector<StaticChromeRootCertConstraints> c2 = {{.min_version = "20"}};
180   std::vector<StaticChromeRootCertConstraints> c4 = {{.min_version = "40"}};
181   std::vector<ChromeRootCertInfo> root_cert_info = {
182       {root1->cert_span(), {}},
183       {root2->cert_span(), c2},
184       {root3->cert_span(), {}},
185       {root4->cert_span(), c4},
186   };
187 
188   base::flat_map<std::array<uint8_t, crypto::kSHA256Length>,
189                  std::vector<ChromeRootCertConstraints>>
190       override_constraints;
191 
192   override_constraints[crypto::SHA256Hash(root3->cert_span())] = {
193       {std::nullopt,
194        std::nullopt,
195        std::nullopt,
196        /*max_version_exclusive=*/std::make_optional(base::Version("31")),
197        {}}};
198 
199   override_constraints[crypto::SHA256Hash(root4->cert_span())] = {
200       {std::nullopt,
201        std::nullopt,
202        std::nullopt,
203        /*max_version_exclusive=*/std::make_optional(base::Version("41")),
204        {}}};
205 
206   override_constraints[crypto::SHA256Hash(root6->cert_span())] = {
207       {std::nullopt,
208        std::nullopt,
209        std::nullopt,
210        /*max_version_exclusive=*/std::make_optional(base::Version("61")),
211        {}}};
212 
213   std::unique_ptr<TrustStoreChrome> trust_store_chrome =
214       TrustStoreChrome::CreateTrustStoreForTesting(
215           std::move(root_cert_info),
216           /*version=*/1, std::move(override_constraints));
217 
218   {
219     std::shared_ptr<const bssl::ParsedCertificate> parsed =
220         ToParsedCertificate(*root1);
221     ASSERT_TRUE(parsed);
222     EXPECT_TRUE(trust_store_chrome->Contains(parsed.get()));
223     EXPECT_TRUE(
224         trust_store_chrome->GetConstraintsForCert(parsed.get()).empty());
225   }
226 
227   {
228     std::shared_ptr<const bssl::ParsedCertificate> parsed =
229         ToParsedCertificate(*root2);
230     ASSERT_TRUE(parsed);
231     EXPECT_TRUE(trust_store_chrome->Contains(parsed.get()));
232 
233     base::span<const ChromeRootCertConstraints> constraints =
234         trust_store_chrome->GetConstraintsForCert(parsed.get());
235     ASSERT_EQ(constraints.size(), 1U);
236     EXPECT_EQ(constraints[0].min_version.value().components(),
237               std::vector<uint32_t>({20}));
238     EXPECT_FALSE(constraints[0].max_version_exclusive.has_value());
239   }
240 
241   {
242     std::shared_ptr<const bssl::ParsedCertificate> parsed =
243         ToParsedCertificate(*root3);
244     ASSERT_TRUE(parsed);
245     EXPECT_TRUE(trust_store_chrome->Contains(parsed.get()));
246 
247     base::span<const ChromeRootCertConstraints> constraints =
248         trust_store_chrome->GetConstraintsForCert(parsed.get());
249     ASSERT_EQ(constraints.size(), 1U);
250     EXPECT_FALSE(constraints[0].min_version.has_value());
251     EXPECT_EQ(constraints[0].max_version_exclusive.value().components(),
252               std::vector<uint32_t>({31}));
253   }
254 
255   {
256     std::shared_ptr<const bssl::ParsedCertificate> parsed =
257         ToParsedCertificate(*root4);
258     ASSERT_TRUE(parsed);
259     EXPECT_TRUE(trust_store_chrome->Contains(parsed.get()));
260 
261     base::span<const ChromeRootCertConstraints> constraints =
262         trust_store_chrome->GetConstraintsForCert(parsed.get());
263     ASSERT_EQ(constraints.size(), 1U);
264     EXPECT_FALSE(constraints[0].min_version.has_value());
265     EXPECT_EQ(constraints[0].max_version_exclusive.value().components(),
266               std::vector<uint32_t>({41}));
267   }
268 
269   {
270     std::shared_ptr<const bssl::ParsedCertificate> parsed =
271         ToParsedCertificate(*root5);
272     ASSERT_TRUE(parsed);
273     EXPECT_FALSE(trust_store_chrome->Contains(parsed.get()));
274     EXPECT_TRUE(
275         trust_store_chrome->GetConstraintsForCert(parsed.get()).empty());
276   }
277 
278   {
279     std::shared_ptr<const bssl::ParsedCertificate> parsed =
280         ToParsedCertificate(*root6);
281     ASSERT_TRUE(parsed);
282     EXPECT_FALSE(trust_store_chrome->Contains(parsed.get()));
283 
284     base::span<const ChromeRootCertConstraints> constraints =
285         trust_store_chrome->GetConstraintsForCert(parsed.get());
286     ASSERT_EQ(constraints.size(), 1U);
287     EXPECT_FALSE(constraints[0].min_version.has_value());
288     EXPECT_EQ(constraints[0].max_version_exclusive.value().components(),
289               std::vector<uint32_t>({61}));
290   }
291 }
292 
TEST(TrustStoreChromeTestNoFixture,ParseCommandLineConstraintsEmpty)293 TEST(TrustStoreChromeTestNoFixture, ParseCommandLineConstraintsEmpty) {
294   EXPECT_TRUE(TrustStoreChrome::ParseCrsConstraintsSwitch("").empty());
295   EXPECT_TRUE(TrustStoreChrome::ParseCrsConstraintsSwitch("invalid").empty());
296   EXPECT_TRUE(TrustStoreChrome::ParseCrsConstraintsSwitch(
297                   "invalidhash:sctnotafter=123456")
298                   .empty());
299 }
300 
TEST(TrustStoreChromeTestNoFixture,ParseCommandLineConstraintsErrorHandling)301 TEST(TrustStoreChromeTestNoFixture, ParseCommandLineConstraintsErrorHandling) {
302   auto constraints = TrustStoreChrome::ParseCrsConstraintsSwitch(
303       // Valid hash and valid constraint name with invalid value (missing `,`
304       // between constraints, so sctallafter value will not be parsable as an
305       // integer). Should result in a constraintset with every constraint
306       // being nullopt.
307       "568c8ef6b526d1394bca052ba3e4d1f4d7a8d9c88c55a1a9ab7ca0fae2dc5473:"
308       "sctallafter=9876543sctnotafter=1234567890+"
309       // Invalid hash (valid hex, but too short).
310       "37a9761b69457987abbc8636182d8273498719659716397401f98e019b20a9:"
311       "sctallafter=9876543+"
312       // Invalid hash (valid hex, but too long).
313       "37a9761b69457987abbc8636182d8273498719659716397401f98e019b20a91111:"
314       "sctallafter=9876543+"
315       // Invalid constraint mapping (missing `:` between hash and constraint).
316       "737a9761b69457987abbc8636182d8273498719659716397401f98e019b20a98"
317       "sctallafter=9876543+"
318       // Invalid and valid hashes with both invalid and valid constraints.
319       "11,a7e0c75d7f772fccf26a6ac1f7b0a86a482e2f3d326bc911c95d56ff3d4906d5,22:"
320       "invalidconstraint=hello,sctnotafter=789012345+"
321       // Missing `+` between constraint mappings.
322       // This will parse the next hash and minversion all as an invalid
323       // sctallafter value and then the maxversionexclusive will apply to the
324       // previous root hash.
325       "65ee41e8a8c27b71b6bfcf44653c8e8370ec5e106e272592c2fbcbadf8dc5763:"
326       "sctnotafter=123456,sctallafter=54321"
327       "3333333333333333333333333333333333333333333333333333333333333333:"
328       "minversion=1,maxversionexclusive=2.3");
329   EXPECT_EQ(constraints.size(), 3U);
330 
331   {
332     constexpr uint8_t hash[] = {0x56, 0x8c, 0x8e, 0xf6, 0xb5, 0x26, 0xd1, 0x39,
333                                 0x4b, 0xca, 0x05, 0x2b, 0xa3, 0xe4, 0xd1, 0xf4,
334                                 0xd7, 0xa8, 0xd9, 0xc8, 0x8c, 0x55, 0xa1, 0xa9,
335                                 0xab, 0x7c, 0xa0, 0xfa, 0xe2, 0xdc, 0x54, 0x73};
336     auto it = constraints.find(base::span(hash));
337     ASSERT_NE(it, constraints.end());
338     ASSERT_EQ(it->second.size(), 1U);
339     const auto& constraint1 = it->second[0];
340     EXPECT_FALSE(constraint1.sct_not_after.has_value());
341     EXPECT_FALSE(constraint1.sct_all_after.has_value());
342     EXPECT_FALSE(constraint1.min_version.has_value());
343     EXPECT_FALSE(constraint1.max_version_exclusive.has_value());
344     EXPECT_THAT(constraint1.permitted_dns_names, testing::IsEmpty());
345   }
346   {
347     constexpr uint8_t hash[] = {0xa7, 0xe0, 0xc7, 0x5d, 0x7f, 0x77, 0x2f, 0xcc,
348                                 0xf2, 0x6a, 0x6a, 0xc1, 0xf7, 0xb0, 0xa8, 0x6a,
349                                 0x48, 0x2e, 0x2f, 0x3d, 0x32, 0x6b, 0xc9, 0x11,
350                                 0xc9, 0x5d, 0x56, 0xff, 0x3d, 0x49, 0x06, 0xd5};
351     auto it = constraints.find(base::span(hash));
352     ASSERT_NE(it, constraints.end());
353     ASSERT_EQ(it->second.size(), 1U);
354 
355     const auto& constraint1 = it->second[0];
356     ASSERT_TRUE(constraint1.sct_not_after.has_value());
357     EXPECT_EQ(constraint1.sct_not_after->InMillisecondsSinceUnixEpoch() / 1000,
358               789012345);
359     EXPECT_FALSE(constraint1.sct_all_after.has_value());
360     EXPECT_FALSE(constraint1.min_version.has_value());
361     EXPECT_FALSE(constraint1.max_version_exclusive.has_value());
362     EXPECT_THAT(constraint1.permitted_dns_names, testing::IsEmpty());
363   }
364 
365   {
366     unsigned char hash[] = {0x65, 0xee, 0x41, 0xe8, 0xa8, 0xc2, 0x7b, 0x71,
367                             0xb6, 0xbf, 0xcf, 0x44, 0x65, 0x3c, 0x8e, 0x83,
368                             0x70, 0xec, 0x5e, 0x10, 0x6e, 0x27, 0x25, 0x92,
369                             0xc2, 0xfb, 0xcb, 0xad, 0xf8, 0xdc, 0x57, 0x63};
370 
371     auto it = constraints.find(base::span(hash));
372     ASSERT_NE(it, constraints.end());
373     ASSERT_EQ(it->second.size(), 1U);
374     const auto& constraint = it->second[0];
375     ASSERT_TRUE(constraint.sct_not_after.has_value());
376     EXPECT_EQ(constraint.sct_not_after->InMillisecondsSinceUnixEpoch() / 1000,
377               123456);
378     EXPECT_FALSE(constraint.sct_all_after.has_value());
379     EXPECT_FALSE(constraint.min_version.has_value());
380     EXPECT_EQ(constraint.max_version_exclusive, base::Version({2, 3}));
381     EXPECT_THAT(constraint.permitted_dns_names, testing::IsEmpty());
382   }
383 }
384 
TEST(TrustStoreChromeTestNoFixture,ParseCommandLineConstraintsOneRootOneConstraint)385 TEST(TrustStoreChromeTestNoFixture,
386      ParseCommandLineConstraintsOneRootOneConstraint) {
387   auto constraints = TrustStoreChrome::ParseCrsConstraintsSwitch(
388       "65ee41e8a8c27b71b6bfcf44653c8e8370ec5e106e272592c2fbcbadf8dc5763:"
389       "sctnotafter=123456");
390   EXPECT_EQ(constraints.size(), 1U);
391   unsigned char hash[] = {0x65, 0xee, 0x41, 0xe8, 0xa8, 0xc2, 0x7b, 0x71,
392                           0xb6, 0xbf, 0xcf, 0x44, 0x65, 0x3c, 0x8e, 0x83,
393                           0x70, 0xec, 0x5e, 0x10, 0x6e, 0x27, 0x25, 0x92,
394                           0xc2, 0xfb, 0xcb, 0xad, 0xf8, 0xdc, 0x57, 0x63};
395 
396   auto it = constraints.find(base::span(hash));
397   ASSERT_NE(it, constraints.end());
398   ASSERT_EQ(it->second.size(), 1U);
399   const auto& constraint = it->second[0];
400   ASSERT_TRUE(constraint.sct_not_after.has_value());
401   EXPECT_EQ(constraint.sct_not_after->InMillisecondsSinceUnixEpoch() / 1000,
402             123456);
403   EXPECT_FALSE(constraint.sct_all_after.has_value());
404   EXPECT_FALSE(constraint.min_version.has_value());
405   EXPECT_FALSE(constraint.max_version_exclusive.has_value());
406 }
407 
TEST(TrustStoreChromeTestNoFixture,ParseCommandLineConstraintsMultipleRootsMultipleConstraints)408 TEST(TrustStoreChromeTestNoFixture,
409      ParseCommandLineConstraintsMultipleRootsMultipleConstraints) {
410   auto constraints = TrustStoreChrome::ParseCrsConstraintsSwitch(
411       "784ecaa8b9dfcc826547f806f759abd6b4481582fc7e377dc3e6a0a959025126,"
412       "a7e0c75d7f772fccf26a6ac1f7b0a86a482e2f3d326bc911c95d56ff3d4906d5:"
413       "sctnotafter=123456,sctallafter=7689,"
414       "minversion=1.2.3.4,maxversionexclusive=10,"
415       "dns=foo.com,dns=bar.com+"
416       "a7e0c75d7f772fccf26a6ac1f7b0a86a482e2f3d326bc911c95d56ff3d4906d5,"
417       "568c8ef6b526d1394bca052ba3e4d1f4d7a8d9c88c55a1a9ab7ca0fae2dc5473:"
418       "sctallafter=9876543,sctnotafter=1234567890");
419   EXPECT_EQ(constraints.size(), 3U);
420 
421   {
422     constexpr uint8_t hash1[] = {
423         0x78, 0x4e, 0xca, 0xa8, 0xb9, 0xdf, 0xcc, 0x82, 0x65, 0x47, 0xf8,
424         0x06, 0xf7, 0x59, 0xab, 0xd6, 0xb4, 0x48, 0x15, 0x82, 0xfc, 0x7e,
425         0x37, 0x7d, 0xc3, 0xe6, 0xa0, 0xa9, 0x59, 0x02, 0x51, 0x26};
426     auto it = constraints.find(base::span(hash1));
427     ASSERT_NE(it, constraints.end());
428     ASSERT_EQ(it->second.size(), 1U);
429     const auto& constraint1 = it->second[0];
430     ASSERT_TRUE(constraint1.sct_not_after.has_value());
431     EXPECT_EQ(constraint1.sct_not_after->InMillisecondsSinceUnixEpoch() / 1000,
432               123456);
433     ASSERT_TRUE(constraint1.sct_all_after.has_value());
434     EXPECT_EQ(constraint1.sct_all_after->InMillisecondsSinceUnixEpoch() / 1000,
435               7689);
436     EXPECT_EQ(constraint1.min_version, base::Version({1, 2, 3, 4}));
437     EXPECT_EQ(constraint1.max_version_exclusive, base::Version({10}));
438     EXPECT_THAT(constraint1.permitted_dns_names,
439                 testing::ElementsAre("foo.com", "bar.com"));
440   }
441 
442   {
443     constexpr uint8_t hash2[] = {
444         0xa7, 0xe0, 0xc7, 0x5d, 0x7f, 0x77, 0x2f, 0xcc, 0xf2, 0x6a, 0x6a,
445         0xc1, 0xf7, 0xb0, 0xa8, 0x6a, 0x48, 0x2e, 0x2f, 0x3d, 0x32, 0x6b,
446         0xc9, 0x11, 0xc9, 0x5d, 0x56, 0xff, 0x3d, 0x49, 0x06, 0xd5};
447     auto it = constraints.find(base::span(hash2));
448     ASSERT_NE(it, constraints.end());
449     ASSERT_EQ(it->second.size(), 2U);
450 
451     const auto& constraint1 = it->second[0];
452     ASSERT_TRUE(constraint1.sct_not_after.has_value());
453     EXPECT_EQ(constraint1.sct_not_after->InMillisecondsSinceUnixEpoch() / 1000,
454               123456);
455     ASSERT_TRUE(constraint1.sct_all_after.has_value());
456     EXPECT_EQ(constraint1.sct_all_after->InMillisecondsSinceUnixEpoch() / 1000,
457               7689);
458     EXPECT_EQ(constraint1.min_version, base::Version({1, 2, 3, 4}));
459     EXPECT_EQ(constraint1.max_version_exclusive, base::Version({10}));
460     EXPECT_THAT(constraint1.permitted_dns_names,
461                 testing::ElementsAre("foo.com", "bar.com"));
462 
463     const auto& constraint2 = it->second[1];
464     ASSERT_TRUE(constraint2.sct_not_after.has_value());
465     EXPECT_EQ(constraint2.sct_not_after->InMillisecondsSinceUnixEpoch() / 1000,
466               1234567890);
467     ASSERT_TRUE(constraint2.sct_all_after.has_value());
468     EXPECT_EQ(constraint2.sct_all_after->InMillisecondsSinceUnixEpoch() / 1000,
469               9876543);
470     EXPECT_FALSE(constraint2.min_version.has_value());
471     EXPECT_FALSE(constraint2.max_version_exclusive.has_value());
472     EXPECT_THAT(constraint2.permitted_dns_names, testing::IsEmpty());
473   }
474 
475   {
476     constexpr uint8_t hash3[] = {
477         0x56, 0x8c, 0x8e, 0xf6, 0xb5, 0x26, 0xd1, 0x39, 0x4b, 0xca, 0x05,
478         0x2b, 0xa3, 0xe4, 0xd1, 0xf4, 0xd7, 0xa8, 0xd9, 0xc8, 0x8c, 0x55,
479         0xa1, 0xa9, 0xab, 0x7c, 0xa0, 0xfa, 0xe2, 0xdc, 0x54, 0x73};
480     auto it = constraints.find(base::span(hash3));
481     ASSERT_NE(it, constraints.end());
482     ASSERT_EQ(it->second.size(), 1U);
483     const auto& constraint1 = it->second[0];
484     ASSERT_TRUE(constraint1.sct_not_after.has_value());
485     EXPECT_EQ(constraint1.sct_not_after->InMillisecondsSinceUnixEpoch() / 1000,
486               1234567890);
487     ASSERT_TRUE(constraint1.sct_all_after.has_value());
488     EXPECT_EQ(constraint1.sct_all_after->InMillisecondsSinceUnixEpoch() / 1000,
489               9876543);
490     EXPECT_FALSE(constraint1.min_version.has_value());
491     EXPECT_FALSE(constraint1.max_version_exclusive.has_value());
492     EXPECT_THAT(constraint1.permitted_dns_names, testing::IsEmpty());
493   }
494 }
495 
496 }  // namespace
497 }  // namespace net
498