1 // Copyright 2017 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/revocation_checker.h"
6
7 #include <string_view>
8
9 #include "base/time/time.h"
10 #include "net/cert/mock_cert_net_fetcher.h"
11 #include "net/test/cert_builder.h"
12 #include "net/test/revocation_builder.h"
13 #include "testing/gmock/include/gmock/gmock.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "third_party/boringssl/src/pki/cert_errors.h"
16 #include "third_party/boringssl/src/pki/common_cert_errors.h"
17 #include "third_party/boringssl/src/pki/parse_certificate.h"
18 #include "third_party/boringssl/src/pki/parsed_certificate.h"
19 #include "url/gurl.h"
20
21 namespace net {
22
23 namespace {
24
25 using ::testing::_;
26 using ::testing::ByMove;
27 using ::testing::Mock;
28 using ::testing::Return;
29 using ::testing::StrictMock;
30
AddCertsToList(std::vector<CertBuilder * > builders,bssl::ParsedCertificateList * out_certs)31 bool AddCertsToList(std::vector<CertBuilder*> builders,
32 bssl::ParsedCertificateList* out_certs) {
33 for (auto* builder : builders) {
34 if (!bssl::ParsedCertificate::CreateAndAddToVector(
35 builder->DupCertBuffer(), {}, out_certs, /*errors=*/nullptr)) {
36 return false;
37 }
38 }
39 return true;
40 }
41
TEST(RevocationChecker,NoRevocationMechanism)42 TEST(RevocationChecker, NoRevocationMechanism) {
43 auto [leaf, root] = CertBuilder::CreateSimpleChain2();
44
45 bssl::ParsedCertificateList chain;
46 ASSERT_TRUE(AddCertsToList({leaf.get(), root.get()}, &chain));
47
48 RevocationPolicy policy;
49 policy.check_revocation = true;
50 policy.networking_allowed = true;
51 policy.crl_allowed = true;
52 policy.allow_unable_to_check = false;
53
54 {
55 // Require revocation methods to be presented.
56 policy.allow_missing_info = false;
57
58 // No methods on |mock_fetcher| should be called.
59 auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
60
61 bssl::CertPathErrors errors;
62 CheckValidatedChainRevocation(
63 chain, policy, /*deadline=*/base::TimeTicks(),
64 /*stapled_leaf_ocsp_response=*/std::string_view(), mock_fetcher.get(),
65 &errors, /*stapled_ocsp_verify_result=*/nullptr);
66
67 EXPECT_TRUE(errors.ContainsHighSeverityErrors());
68 EXPECT_TRUE(
69 errors.ContainsError(bssl::cert_errors::kNoRevocationMechanism));
70 }
71
72 {
73 // Allow certs without revocation methods.
74 policy.allow_missing_info = true;
75
76 // No methods on |mock_fetcher| should be called.
77 auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
78
79 bssl::CertPathErrors errors;
80 CheckValidatedChainRevocation(
81 chain, policy, /*deadline=*/base::TimeTicks(),
82 /*stapled_leaf_ocsp_response=*/std::string_view(), mock_fetcher.get(),
83 &errors, /*stapled_ocsp_verify_result=*/nullptr);
84
85 EXPECT_FALSE(errors.ContainsHighSeverityErrors());
86 }
87
88 {
89 // Revocation checking disabled.
90 policy.check_revocation = false;
91 // Require revocation methods to be presented, but this does not matter if
92 // check_revocation is false.
93 policy.allow_missing_info = false;
94
95 // No methods on |mock_fetcher| should be called.
96 auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
97
98 bssl::CertPathErrors errors;
99 CheckValidatedChainRevocation(
100 chain, policy, /*deadline=*/base::TimeTicks(),
101 /*stapled_leaf_ocsp_response=*/std::string_view(), mock_fetcher.get(),
102 &errors, /*stapled_ocsp_verify_result=*/nullptr);
103
104 EXPECT_FALSE(errors.ContainsHighSeverityErrors());
105 }
106 }
107
TEST(RevocationChecker,ValidCRL)108 TEST(RevocationChecker, ValidCRL) {
109 auto [leaf, root] = CertBuilder::CreateSimpleChain2();
110
111 const GURL kTestCrlUrl("http://example.com/crl1");
112 leaf->SetCrlDistributionPointUrl(kTestCrlUrl);
113
114 bssl::ParsedCertificateList chain;
115 ASSERT_TRUE(AddCertsToList({leaf.get(), root.get()}, &chain));
116
117 RevocationPolicy policy;
118 policy.check_revocation = true;
119 policy.allow_missing_info = false;
120 policy.allow_unable_to_check = false;
121
122 std::string crl_data_as_string_for_some_reason =
123 BuildCrl(root->GetSubject(), root->GetKey(),
124 /*revoked_serials=*/{});
125 std::vector<uint8_t> crl_data(crl_data_as_string_for_some_reason.begin(),
126 crl_data_as_string_for_some_reason.end());
127
128 {
129 policy.networking_allowed = true;
130 policy.crl_allowed = true;
131
132 auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
133 EXPECT_CALL(*mock_fetcher, FetchCrl(kTestCrlUrl, _, _))
134 .WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(crl_data))));
135
136 bssl::CertPathErrors errors;
137 CheckValidatedChainRevocation(
138 chain, policy, /*deadline=*/base::TimeTicks(),
139 /*stapled_leaf_ocsp_response=*/std::string_view(), mock_fetcher.get(),
140 &errors, /*stapled_ocsp_verify_result=*/nullptr);
141
142 EXPECT_FALSE(errors.ContainsHighSeverityErrors());
143 }
144
145 {
146 policy.networking_allowed = false;
147 policy.crl_allowed = true;
148
149 // No methods on |mock_fetcher| should be called.
150 auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
151
152 bssl::CertPathErrors errors;
153 CheckValidatedChainRevocation(
154 chain, policy, /*deadline=*/base::TimeTicks(),
155 /*stapled_leaf_ocsp_response=*/std::string_view(), mock_fetcher.get(),
156 &errors, /*stapled_ocsp_verify_result=*/nullptr);
157
158 EXPECT_TRUE(errors.ContainsHighSeverityErrors());
159 EXPECT_TRUE(
160 errors.ContainsError(bssl::cert_errors::kUnableToCheckRevocation));
161 }
162
163 {
164 policy.networking_allowed = true;
165 policy.crl_allowed = false;
166
167 // No methods on |mock_fetcher| should be called.
168 auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
169
170 bssl::CertPathErrors errors;
171 CheckValidatedChainRevocation(
172 chain, policy, /*deadline=*/base::TimeTicks(),
173 /*stapled_leaf_ocsp_response=*/std::string_view(), mock_fetcher.get(),
174 &errors, /*stapled_ocsp_verify_result=*/nullptr);
175
176 EXPECT_TRUE(errors.ContainsHighSeverityErrors());
177 // Since CRLs were not considered, the error should be "no revocation
178 // mechanism".
179 EXPECT_TRUE(
180 errors.ContainsError(bssl::cert_errors::kNoRevocationMechanism));
181 }
182 }
183
TEST(RevocationChecker,RevokedCRL)184 TEST(RevocationChecker, RevokedCRL) {
185 auto [leaf, root] = CertBuilder::CreateSimpleChain2();
186
187 const GURL kTestCrlUrl("http://example.com/crl1");
188 leaf->SetCrlDistributionPointUrl(kTestCrlUrl);
189
190 bssl::ParsedCertificateList chain;
191 ASSERT_TRUE(AddCertsToList({leaf.get(), root.get()}, &chain));
192
193 RevocationPolicy policy;
194 policy.check_revocation = true;
195 policy.networking_allowed = true;
196 policy.crl_allowed = true;
197
198 std::string crl_data_as_string_for_some_reason =
199 BuildCrl(root->GetSubject(), root->GetKey(),
200 /*revoked_serials=*/{leaf->GetSerialNumber()});
201 std::vector<uint8_t> crl_data(crl_data_as_string_for_some_reason.begin(),
202 crl_data_as_string_for_some_reason.end());
203
204 {
205 // These should have no effect on an affirmatively revoked response.
206 policy.allow_missing_info = false;
207 policy.allow_unable_to_check = false;
208
209 auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
210 EXPECT_CALL(*mock_fetcher, FetchCrl(kTestCrlUrl, _, _))
211 .WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(crl_data))));
212
213 bssl::CertPathErrors errors;
214 CheckValidatedChainRevocation(
215 chain, policy, /*deadline=*/base::TimeTicks(),
216 /*stapled_leaf_ocsp_response=*/std::string_view(), mock_fetcher.get(),
217 &errors, /*stapled_ocsp_verify_result=*/nullptr);
218
219 EXPECT_TRUE(errors.ContainsHighSeverityErrors());
220 EXPECT_TRUE(errors.ContainsError(bssl::cert_errors::kCertificateRevoked));
221 }
222
223 {
224 // These should have no effect on an affirmatively revoked response.
225 policy.allow_missing_info = true;
226 policy.allow_unable_to_check = true;
227
228 auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
229 EXPECT_CALL(*mock_fetcher, FetchCrl(kTestCrlUrl, _, _))
230 .WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(crl_data))));
231
232 bssl::CertPathErrors errors;
233 CheckValidatedChainRevocation(
234 chain, policy, /*deadline=*/base::TimeTicks(),
235 /*stapled_leaf_ocsp_response=*/std::string_view(), mock_fetcher.get(),
236 &errors, /*stapled_ocsp_verify_result=*/nullptr);
237
238 EXPECT_TRUE(errors.ContainsHighSeverityErrors());
239 EXPECT_TRUE(errors.ContainsError(bssl::cert_errors::kCertificateRevoked));
240 }
241 }
242
TEST(RevocationChecker,CRLRequestFails)243 TEST(RevocationChecker, CRLRequestFails) {
244 auto [leaf, root] = CertBuilder::CreateSimpleChain2();
245
246 const GURL kTestCrlUrl("http://example.com/crl1");
247 leaf->SetCrlDistributionPointUrl(kTestCrlUrl);
248
249 bssl::ParsedCertificateList chain;
250 ASSERT_TRUE(AddCertsToList({leaf.get(), root.get()}, &chain));
251
252 RevocationPolicy policy;
253 policy.check_revocation = true;
254 policy.networking_allowed = true;
255 policy.crl_allowed = true;
256
257 {
258 policy.allow_unable_to_check = false;
259 policy.allow_missing_info = false;
260
261 auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
262 EXPECT_CALL(*mock_fetcher, FetchCrl(kTestCrlUrl, _, _))
263 .WillOnce(Return(
264 ByMove(MockCertNetFetcherRequest::Create(ERR_CONNECTION_FAILED))));
265
266 bssl::CertPathErrors errors;
267 CheckValidatedChainRevocation(
268 chain, policy, /*deadline=*/base::TimeTicks(),
269 /*stapled_leaf_ocsp_response=*/std::string_view(), mock_fetcher.get(),
270 &errors, /*stapled_ocsp_verify_result=*/nullptr);
271
272 EXPECT_TRUE(errors.ContainsHighSeverityErrors());
273 EXPECT_TRUE(
274 errors.ContainsError(bssl::cert_errors::kUnableToCheckRevocation));
275 }
276
277 {
278 policy.allow_unable_to_check = false;
279 policy.allow_missing_info = true; // Should have no effect.
280
281 auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
282 EXPECT_CALL(*mock_fetcher, FetchCrl(kTestCrlUrl, _, _))
283 .WillOnce(Return(
284 ByMove(MockCertNetFetcherRequest::Create(ERR_CONNECTION_FAILED))));
285
286 bssl::CertPathErrors errors;
287 CheckValidatedChainRevocation(
288 chain, policy, /*deadline=*/base::TimeTicks(),
289 /*stapled_leaf_ocsp_response=*/std::string_view(), mock_fetcher.get(),
290 &errors, /*stapled_ocsp_verify_result=*/nullptr);
291
292 EXPECT_TRUE(errors.ContainsHighSeverityErrors());
293 EXPECT_TRUE(
294 errors.ContainsError(bssl::cert_errors::kUnableToCheckRevocation));
295 }
296
297 {
298 policy.allow_unable_to_check = true;
299 policy.allow_missing_info = false;
300
301 auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
302 EXPECT_CALL(*mock_fetcher, FetchCrl(kTestCrlUrl, _, _))
303 .WillOnce(Return(
304 ByMove(MockCertNetFetcherRequest::Create(ERR_CONNECTION_FAILED))));
305
306 bssl::CertPathErrors errors;
307 CheckValidatedChainRevocation(
308 chain, policy, /*deadline=*/base::TimeTicks(),
309 /*stapled_leaf_ocsp_response=*/std::string_view(), mock_fetcher.get(),
310 &errors, /*stapled_ocsp_verify_result=*/nullptr);
311
312 EXPECT_FALSE(errors.ContainsHighSeverityErrors());
313 }
314 }
315
TEST(RevocationChecker,CRLNonHttpUrl)316 TEST(RevocationChecker, CRLNonHttpUrl) {
317 auto [leaf, root] = CertBuilder::CreateSimpleChain2();
318
319 const GURL kTestCrlUrl("https://example.com/crl1");
320 leaf->SetCrlDistributionPointUrl(kTestCrlUrl);
321
322 bssl::ParsedCertificateList chain;
323 ASSERT_TRUE(AddCertsToList({leaf.get(), root.get()}, &chain));
324
325 RevocationPolicy policy;
326 policy.check_revocation = true;
327 policy.networking_allowed = true;
328 policy.crl_allowed = true;
329 policy.allow_unable_to_check = false;
330 policy.allow_missing_info = false;
331
332 // HTTPS CRL URLs should not be fetched.
333 auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
334
335 bssl::CertPathErrors errors;
336 CheckValidatedChainRevocation(
337 chain, policy, /*deadline=*/base::TimeTicks(),
338 /*stapled_leaf_ocsp_response=*/std::string_view(), mock_fetcher.get(),
339 &errors, /*stapled_ocsp_verify_result=*/nullptr);
340
341 EXPECT_TRUE(errors.ContainsHighSeverityErrors());
342 EXPECT_TRUE(errors.ContainsError(bssl::cert_errors::kNoRevocationMechanism));
343 }
344
TEST(RevocationChecker,SkipEntireInvalidCRLDistributionPoints)345 TEST(RevocationChecker, SkipEntireInvalidCRLDistributionPoints) {
346 auto [leaf, root] = CertBuilder::CreateSimpleChain2();
347
348 const GURL kSecondCrlUrl("http://www.example.com/bar.crl");
349
350 // SEQUENCE {
351 // # First distribution point: this is invalid, thus the entire
352 // # crlDistributionPoints extension should be ignored and revocation
353 // # checking should fail.
354 // SEQUENCE {
355 // [0] {
356 // [0] {
357 // # [9] is not a valid tag in bssl::GeneralNames
358 // [9 PRIMITIVE] { "foo" }
359 // }
360 // }
361 // }
362 // # Second distribution point. Even though this is an acceptable
363 // # distributionPoint, it should not be used.
364 // SEQUENCE {
365 // [0] {
366 // [0] {
367 // [6 PRIMITIVE] { "http://www.example.com/bar.crl" }
368 // }
369 // }
370 // }
371 // }
372 const uint8_t crldp[] = {0x30, 0x31, 0x30, 0x09, 0xa0, 0x07, 0xa0, 0x05, 0x89,
373 0x03, 0x66, 0x6f, 0x6f, 0x30, 0x24, 0xa0, 0x22, 0xa0,
374 0x20, 0x86, 0x1e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
375 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d,
376 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62,
377 0x61, 0x72, 0x2e, 0x63, 0x72, 0x6c};
378 leaf->SetExtension(
379 bssl::der::Input(bssl::kCrlDistributionPointsOid),
380 std::string(reinterpret_cast<const char*>(crldp), std::size(crldp)));
381
382 bssl::ParsedCertificateList chain;
383 ASSERT_TRUE(AddCertsToList({leaf.get(), root.get()}, &chain));
384
385 RevocationPolicy policy;
386 policy.check_revocation = true;
387 policy.networking_allowed = true;
388 policy.crl_allowed = true;
389 policy.allow_unable_to_check = false;
390 policy.allow_missing_info = false;
391
392 std::string crl_data_as_string_for_some_reason =
393 BuildCrl(root->GetSubject(), root->GetKey(),
394 /*revoked_serials=*/{});
395 std::vector<uint8_t> crl_data(crl_data_as_string_for_some_reason.begin(),
396 crl_data_as_string_for_some_reason.end());
397
398 // No methods on |mock_fetcher| should be called.
399 auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
400
401 bssl::CertPathErrors errors;
402 CheckValidatedChainRevocation(
403 chain, policy, /*deadline=*/base::TimeTicks(),
404 /*stapled_leaf_ocsp_response=*/std::string_view(), mock_fetcher.get(),
405 &errors, /*stapled_ocsp_verify_result=*/nullptr);
406
407 // Should fail since the entire cRLDistributionPoints extension was skipped
408 // and no other revocation method is present.
409 EXPECT_TRUE(errors.ContainsHighSeverityErrors());
410 EXPECT_TRUE(errors.ContainsError(bssl::cert_errors::kNoRevocationMechanism));
411 }
412
TEST(RevocationChecker,SkipUnsupportedCRLDistPointWithNonUriFullname)413 TEST(RevocationChecker, SkipUnsupportedCRLDistPointWithNonUriFullname) {
414 auto [leaf, root] = CertBuilder::CreateSimpleChain2();
415
416 const GURL kSecondCrlUrl("http://www.example.com/bar.crl");
417
418 // SEQUENCE {
419 // # First distribution point: this should be ignored since it has a non-URI
420 // # fullName field.
421 // SEQUENCE {
422 // [0] {
423 // [0] {
424 // [4] {
425 // SEQUENCE {
426 // SET {
427 // SEQUENCE {
428 // # countryName
429 // OBJECT_IDENTIFIER { 2.5.4.6 }
430 // PrintableString { "US" }
431 // }
432 // }
433 // SET {
434 // SEQUENCE {
435 // # commonName
436 // OBJECT_IDENTIFIER { 2.5.4.3 }
437 // PrintableString { "foo" }
438 // }
439 // }
440 // }
441 // }
442 // }
443 // }
444 // }
445 // # Second distribution point. This should be used since it only has a
446 // # fullName URI.
447 // SEQUENCE {
448 // [0] {
449 // [0] {
450 // [6 PRIMITIVE] { "http://www.example.com/bar.crl" }
451 // }
452 // }
453 // }
454 // }
455 const uint8_t crldp[] = {
456 0x30, 0x4b, 0x30, 0x23, 0xa0, 0x21, 0xa0, 0x1f, 0xa4, 0x1d, 0x30,
457 0x1b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
458 0x02, 0x55, 0x53, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x55, 0x04,
459 0x03, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x30, 0x24, 0xa0, 0x22, 0xa0,
460 0x20, 0x86, 0x1e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
461 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
462 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x72, 0x2e, 0x63, 0x72, 0x6c};
463 leaf->SetExtension(
464 bssl::der::Input(bssl::kCrlDistributionPointsOid),
465 std::string(reinterpret_cast<const char*>(crldp), std::size(crldp)));
466
467 bssl::ParsedCertificateList chain;
468 ASSERT_TRUE(AddCertsToList({leaf.get(), root.get()}, &chain));
469
470 RevocationPolicy policy;
471 policy.check_revocation = true;
472 policy.networking_allowed = true;
473 policy.crl_allowed = true;
474 policy.allow_unable_to_check = false;
475 policy.allow_missing_info = false;
476
477 std::string crl_data_as_string_for_some_reason =
478 BuildCrl(root->GetSubject(), root->GetKey(),
479 /*revoked_serials=*/{});
480 std::vector<uint8_t> crl_data(crl_data_as_string_for_some_reason.begin(),
481 crl_data_as_string_for_some_reason.end());
482
483 // The first crldp should be skipped, the second should be retrieved.
484 auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
485 EXPECT_CALL(*mock_fetcher, FetchCrl(kSecondCrlUrl, _, _))
486 .WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(crl_data))));
487
488 bssl::CertPathErrors errors;
489 CheckValidatedChainRevocation(
490 chain, policy, /*deadline=*/base::TimeTicks(),
491 /*stapled_leaf_ocsp_response=*/std::string_view(), mock_fetcher.get(),
492 &errors, /*stapled_ocsp_verify_result=*/nullptr);
493
494 EXPECT_FALSE(errors.ContainsHighSeverityErrors());
495 }
496
TEST(RevocationChecker,SkipUnsupportedCRLDistPointWithReasons)497 TEST(RevocationChecker, SkipUnsupportedCRLDistPointWithReasons) {
498 auto [leaf, root] = CertBuilder::CreateSimpleChain2();
499
500 const GURL kSecondCrlUrl("http://www.example.com/bar.crl");
501
502 // SEQUENCE {
503 // # First distribution point: this should be ignored since it has a reasons
504 // # field.
505 // SEQUENCE {
506 // [0] {
507 // [0] {
508 // [6 PRIMITIVE] { "http://www.example.com/foo.crl" }
509 // }
510 // }
511 // # reasons
512 // [1 PRIMITIVE] { b`011` }
513 // }
514 // # Second distribution point. This should be used since it only has a
515 // # fullName URI.
516 // SEQUENCE {
517 // [0] {
518 // [0] {
519 // [6 PRIMITIVE] { "http://www.example.com/bar.crl" }
520 // }
521 // }
522 // }
523 // }
524 const uint8_t crldp[] = {
525 0x30, 0x50, 0x30, 0x28, 0xa0, 0x22, 0xa0, 0x20, 0x86, 0x1e, 0x68, 0x74,
526 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61,
527 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x6f, 0x6f,
528 0x2e, 0x63, 0x72, 0x6c, 0x81, 0x02, 0x05, 0x60, 0x30, 0x24, 0xa0, 0x22,
529 0xa0, 0x20, 0x86, 0x1e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
530 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63,
531 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x72, 0x2e, 0x63, 0x72, 0x6c};
532 leaf->SetExtension(
533 bssl::der::Input(bssl::kCrlDistributionPointsOid),
534 std::string(reinterpret_cast<const char*>(crldp), std::size(crldp)));
535
536 bssl::ParsedCertificateList chain;
537 ASSERT_TRUE(AddCertsToList({leaf.get(), root.get()}, &chain));
538
539 RevocationPolicy policy;
540 policy.check_revocation = true;
541 policy.networking_allowed = true;
542 policy.crl_allowed = true;
543 policy.allow_unable_to_check = false;
544 policy.allow_missing_info = false;
545
546 std::string crl_data_as_string_for_some_reason =
547 BuildCrl(root->GetSubject(), root->GetKey(),
548 /*revoked_serials=*/{});
549 std::vector<uint8_t> crl_data(crl_data_as_string_for_some_reason.begin(),
550 crl_data_as_string_for_some_reason.end());
551
552 // The first crldp should be skipped, the second should be retrieved.
553 auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
554 EXPECT_CALL(*mock_fetcher, FetchCrl(kSecondCrlUrl, _, _))
555 .WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(crl_data))));
556
557 bssl::CertPathErrors errors;
558 CheckValidatedChainRevocation(
559 chain, policy, /*deadline=*/base::TimeTicks(),
560 /*stapled_leaf_ocsp_response=*/std::string_view(), mock_fetcher.get(),
561 &errors, /*stapled_ocsp_verify_result=*/nullptr);
562
563 EXPECT_FALSE(errors.ContainsHighSeverityErrors());
564 }
565
TEST(RevocationChecker,SkipUnsupportedCRLDistPointWithCrlIssuer)566 TEST(RevocationChecker, SkipUnsupportedCRLDistPointWithCrlIssuer) {
567 auto [leaf, root] = CertBuilder::CreateSimpleChain2();
568
569 const GURL kSecondCrlUrl("http://www.example.com/bar.crl");
570
571 // SEQUENCE {
572 // # First distribution point: this should be ignored since it has a
573 // crlIssuer field.
574 // SEQUENCE {
575 // [0] {
576 // [0] {
577 // [6 PRIMITIVE] { "http://www.example.com/foo.crl" }
578 // }
579 // }
580 // [2] {
581 // [4] {
582 // SEQUENCE {
583 // SET {
584 // SEQUENCE {
585 // # countryName
586 // OBJECT_IDENTIFIER { 2.5.4.6 }
587 // PrintableString { "US" }
588 // }
589 // }
590 // SET {
591 // SEQUENCE {
592 // # organizationName
593 // OBJECT_IDENTIFIER { 2.5.4.10 }
594 // PrintableString { "Test Certificates 2011" }
595 // }
596 // }
597 // SET {
598 // SEQUENCE {
599 // # organizationUnitName
600 // OBJECT_IDENTIFIER { 2.5.4.11 }
601 // PrintableString { "indirectCRL CA3 cRLIssuer" }
602 // }
603 // }
604 // }
605 // }
606 // }
607 // }
608 // # Second distribution point. This should be used since it only has a
609 // # fullName URI.
610 // SEQUENCE {
611 // [0] {
612 // [0] {
613 // [6 PRIMITIVE] { "http://www.example.com/bar.crl" }
614 // }
615 // }
616 // }
617 // }
618 const uint8_t crldp[] = {
619 0x30, 0x81, 0xa4, 0x30, 0x7c, 0xa0, 0x22, 0xa0, 0x20, 0x86, 0x1e, 0x68,
620 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78,
621 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x6f,
622 0x6f, 0x2e, 0x63, 0x72, 0x6c, 0xa2, 0x56, 0xa4, 0x54, 0x30, 0x52, 0x31,
623 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
624 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x16, 0x54,
625 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
626 0x61, 0x74, 0x65, 0x73, 0x20, 0x32, 0x30, 0x31, 0x31, 0x31, 0x22, 0x30,
627 0x20, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x19, 0x69, 0x6e, 0x64, 0x69,
628 0x72, 0x65, 0x63, 0x74, 0x43, 0x52, 0x4c, 0x20, 0x43, 0x41, 0x33, 0x20,
629 0x63, 0x52, 0x4c, 0x49, 0x73, 0x73, 0x75, 0x65, 0x72, 0x30, 0x24, 0xa0,
630 0x22, 0xa0, 0x20, 0x86, 0x1e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
631 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
632 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x72, 0x2e, 0x63, 0x72, 0x6c};
633 leaf->SetExtension(
634 bssl::der::Input(bssl::kCrlDistributionPointsOid),
635 std::string(reinterpret_cast<const char*>(crldp), std::size(crldp)));
636
637 bssl::ParsedCertificateList chain;
638 ASSERT_TRUE(AddCertsToList({leaf.get(), root.get()}, &chain));
639
640 RevocationPolicy policy;
641 policy.check_revocation = true;
642 policy.networking_allowed = true;
643 policy.crl_allowed = true;
644 policy.allow_unable_to_check = false;
645 policy.allow_missing_info = false;
646
647 std::string crl_data_as_string_for_some_reason =
648 BuildCrl(root->GetSubject(), root->GetKey(),
649 /*revoked_serials=*/{});
650 std::vector<uint8_t> crl_data(crl_data_as_string_for_some_reason.begin(),
651 crl_data_as_string_for_some_reason.end());
652
653 // The first crldp should be skipped, the second should be retrieved.
654 auto mock_fetcher = base::MakeRefCounted<StrictMock<MockCertNetFetcher>>();
655 EXPECT_CALL(*mock_fetcher, FetchCrl(kSecondCrlUrl, _, _))
656 .WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(crl_data))));
657
658 bssl::CertPathErrors errors;
659 CheckValidatedChainRevocation(
660 chain, policy, /*deadline=*/base::TimeTicks(),
661 /*stapled_leaf_ocsp_response=*/std::string_view(), mock_fetcher.get(),
662 &errors, /*stapled_ocsp_verify_result=*/nullptr);
663
664 EXPECT_FALSE(errors.ContainsHighSeverityErrors());
665 }
666
667 // TODO(mattm): Add more unittests (deadlines, OCSP, stapled OCSP, CRLSets).
668 // Currently those features are exercised indirectly through tests in
669 // url_request_unittest.cc, cert_verify_proc_unittest.cc, etc.
670
671 } // namespace
672
673 } // namespace net
674