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