1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/cert/multi_threaded_cert_verifier.h"
8 #include "base/debug/leak_annotations.h"
9 #include "base/files/file_path.h"
10 #include "base/format_macros.h"
11 #include "base/strings/stringprintf.h"
12 #include "net/base/net_errors.h"
13 #include "net/base/test_completion_callback.h"
14 #include "net/base/test_data_directory.h"
15 #include "net/cert/cert_trust_anchor_provider.h"
16 #include "net/cert/cert_verify_proc.h"
17 #include "net/cert/cert_verify_result.h"
18 #include "net/cert/x509_certificate.h"
19 #include "net/log/net_log.h"
20 #include "net/test/cert_test_util.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
25 using testing::ReturnRef
;
31 void FailTest(int /* result */) {
35 class MockCertVerifyProc
: public CertVerifyProc
{
37 MockCertVerifyProc() {}
40 ~MockCertVerifyProc() override
{}
42 // CertVerifyProc implementation
43 bool SupportsAdditionalTrustAnchors() const override
{ return false; }
44 bool SupportsOCSPStapling() const override
{ return false; }
46 int VerifyInternal(X509Certificate
* cert
,
47 const std::string
& hostname
,
48 const std::string
& ocsp_response
,
51 const CertificateList
& additional_trust_anchors
,
52 CertVerifyResult
* verify_result
) override
{
53 verify_result
->Reset();
54 verify_result
->verified_cert
= cert
;
55 verify_result
->cert_status
= CERT_STATUS_COMMON_NAME_INVALID
;
56 return ERR_CERT_COMMON_NAME_INVALID
;
60 class MockCertTrustAnchorProvider
: public CertTrustAnchorProvider
{
62 MockCertTrustAnchorProvider() {}
63 virtual ~MockCertTrustAnchorProvider() {}
65 MOCK_METHOD0(GetAdditionalTrustAnchors
, const CertificateList
&());
70 class MultiThreadedCertVerifierTest
: public ::testing::Test
{
72 MultiThreadedCertVerifierTest() : verifier_(new MockCertVerifyProc()) {}
73 ~MultiThreadedCertVerifierTest() override
{}
76 MultiThreadedCertVerifier verifier_
;
79 TEST_F(MultiThreadedCertVerifierTest
, CacheHit
) {
80 base::FilePath certs_dir
= GetTestCertsDirectory();
81 scoped_refptr
<X509Certificate
> test_cert(
82 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
83 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
.get());
86 CertVerifyResult verify_result
;
87 TestCompletionCallback callback
;
88 scoped_ptr
<CertVerifier::Request
> request
;
90 error
= verifier_
.Verify(test_cert
.get(), "www.example.com", std::string(), 0,
91 NULL
, &verify_result
, callback
.callback(), &request
,
93 ASSERT_EQ(ERR_IO_PENDING
, error
);
95 error
= callback
.WaitForResult();
96 ASSERT_TRUE(IsCertificateError(error
));
97 ASSERT_EQ(1u, verifier_
.requests());
98 ASSERT_EQ(0u, verifier_
.cache_hits());
99 ASSERT_EQ(0u, verifier_
.inflight_joins());
100 ASSERT_EQ(1u, verifier_
.GetCacheSize());
102 error
= verifier_
.Verify(test_cert
.get(), "www.example.com", std::string(), 0,
103 NULL
, &verify_result
, callback
.callback(), &request
,
105 // Synchronous completion.
106 ASSERT_NE(ERR_IO_PENDING
, error
);
107 ASSERT_TRUE(IsCertificateError(error
));
108 ASSERT_FALSE(request
);
109 ASSERT_EQ(2u, verifier_
.requests());
110 ASSERT_EQ(1u, verifier_
.cache_hits());
111 ASSERT_EQ(0u, verifier_
.inflight_joins());
112 ASSERT_EQ(1u, verifier_
.GetCacheSize());
115 // Tests the same server certificate with different intermediate CA
116 // certificates. These should be treated as different certificate chains even
117 // though the two X509Certificate objects contain the same server certificate.
118 TEST_F(MultiThreadedCertVerifierTest
, DifferentCACerts
) {
119 base::FilePath certs_dir
= GetTestCertsDirectory();
121 scoped_refptr
<X509Certificate
> server_cert
=
122 ImportCertFromFile(certs_dir
, "salesforce_com_test.pem");
123 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), server_cert
.get());
125 scoped_refptr
<X509Certificate
> intermediate_cert1
=
126 ImportCertFromFile(certs_dir
, "verisign_intermediate_ca_2011.pem");
127 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), intermediate_cert1
.get());
129 scoped_refptr
<X509Certificate
> intermediate_cert2
=
130 ImportCertFromFile(certs_dir
, "verisign_intermediate_ca_2016.pem");
131 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), intermediate_cert2
.get());
133 X509Certificate::OSCertHandles intermediates
;
134 intermediates
.push_back(intermediate_cert1
->os_cert_handle());
135 scoped_refptr
<X509Certificate
> cert_chain1
=
136 X509Certificate::CreateFromHandle(server_cert
->os_cert_handle(),
139 intermediates
.clear();
140 intermediates
.push_back(intermediate_cert2
->os_cert_handle());
141 scoped_refptr
<X509Certificate
> cert_chain2
=
142 X509Certificate::CreateFromHandle(server_cert
->os_cert_handle(),
146 CertVerifyResult verify_result
;
147 TestCompletionCallback callback
;
148 scoped_ptr
<CertVerifier::Request
> request
;
150 error
= verifier_
.Verify(cert_chain1
.get(), "www.example.com", std::string(),
151 0, NULL
, &verify_result
, callback
.callback(),
152 &request
, BoundNetLog());
153 ASSERT_EQ(ERR_IO_PENDING
, error
);
154 EXPECT_TRUE(request
);
155 error
= callback
.WaitForResult();
156 ASSERT_TRUE(IsCertificateError(error
));
157 ASSERT_EQ(1u, verifier_
.requests());
158 ASSERT_EQ(0u, verifier_
.cache_hits());
159 ASSERT_EQ(0u, verifier_
.inflight_joins());
160 ASSERT_EQ(1u, verifier_
.GetCacheSize());
162 error
= verifier_
.Verify(cert_chain2
.get(), "www.example.com", std::string(),
163 0, NULL
, &verify_result
, callback
.callback(),
164 &request
, BoundNetLog());
165 ASSERT_EQ(ERR_IO_PENDING
, error
);
166 EXPECT_TRUE(request
);
167 error
= callback
.WaitForResult();
168 ASSERT_TRUE(IsCertificateError(error
));
169 ASSERT_EQ(2u, verifier_
.requests());
170 ASSERT_EQ(0u, verifier_
.cache_hits());
171 ASSERT_EQ(0u, verifier_
.inflight_joins());
172 ASSERT_EQ(2u, verifier_
.GetCacheSize());
175 // Tests an inflight join.
176 TEST_F(MultiThreadedCertVerifierTest
, InflightJoin
) {
177 base::FilePath certs_dir
= GetTestCertsDirectory();
178 scoped_refptr
<X509Certificate
> test_cert(
179 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
180 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
.get());
183 CertVerifyResult verify_result
;
184 TestCompletionCallback callback
;
185 scoped_ptr
<CertVerifier::Request
> request
;
186 CertVerifyResult verify_result2
;
187 TestCompletionCallback callback2
;
188 scoped_ptr
<CertVerifier::Request
> request2
;
190 error
= verifier_
.Verify(test_cert
.get(), "www.example.com", std::string(), 0,
191 NULL
, &verify_result
, callback
.callback(), &request
,
193 ASSERT_EQ(ERR_IO_PENDING
, error
);
194 EXPECT_TRUE(request
);
195 error
= verifier_
.Verify(test_cert
.get(), "www.example.com", std::string(), 0,
196 NULL
, &verify_result2
, callback2
.callback(),
197 &request2
, BoundNetLog());
198 EXPECT_EQ(ERR_IO_PENDING
, error
);
199 EXPECT_TRUE(request2
);
200 error
= callback
.WaitForResult();
201 EXPECT_TRUE(IsCertificateError(error
));
202 error
= callback2
.WaitForResult();
203 ASSERT_TRUE(IsCertificateError(error
));
204 ASSERT_EQ(2u, verifier_
.requests());
205 ASSERT_EQ(0u, verifier_
.cache_hits());
206 ASSERT_EQ(1u, verifier_
.inflight_joins());
209 // Tests that the callback of a canceled request is never made.
210 TEST_F(MultiThreadedCertVerifierTest
, CancelRequest
) {
211 base::FilePath certs_dir
= GetTestCertsDirectory();
212 scoped_refptr
<X509Certificate
> test_cert(
213 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
214 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
.get());
217 CertVerifyResult verify_result
;
218 scoped_ptr
<CertVerifier::Request
> request
;
220 error
= verifier_
.Verify(test_cert
.get(), "www.example.com", std::string(), 0,
221 NULL
, &verify_result
, base::Bind(&FailTest
),
222 &request
, BoundNetLog());
223 ASSERT_EQ(ERR_IO_PENDING
, error
);
224 ASSERT_TRUE(request
);
227 // Issue a few more requests to the worker pool and wait for their
228 // completion, so that the task of the canceled request (which runs on a
229 // worker thread) is likely to complete by the end of this test.
230 TestCompletionCallback callback
;
231 for (int i
= 0; i
< 5; ++i
) {
232 error
= verifier_
.Verify(test_cert
.get(), "www2.example.com", std::string(),
233 0, NULL
, &verify_result
, callback
.callback(),
234 &request
, BoundNetLog());
235 ASSERT_EQ(ERR_IO_PENDING
, error
);
236 EXPECT_TRUE(request
);
237 error
= callback
.WaitForResult();
238 verifier_
.ClearCache();
242 // Tests that a canceled request is not leaked.
243 TEST_F(MultiThreadedCertVerifierTest
, CancelRequestThenQuit
) {
244 base::FilePath certs_dir
= GetTestCertsDirectory();
245 scoped_refptr
<X509Certificate
> test_cert(
246 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
247 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
.get());
250 CertVerifyResult verify_result
;
251 TestCompletionCallback callback
;
252 scoped_ptr
<CertVerifier::Request
> request
;
255 // Because shutdown intentionally doesn't join worker threads, a
256 // CertVerifyWorker may be leaked if the main thread shuts down before the
258 ANNOTATE_SCOPED_MEMORY_LEAK
;
259 error
= verifier_
.Verify(test_cert
.get(), "www.example.com", std::string(),
260 0, NULL
, &verify_result
, callback
.callback(),
261 &request
, BoundNetLog());
263 ASSERT_EQ(ERR_IO_PENDING
, error
);
264 EXPECT_TRUE(request
);
266 // Destroy |verifier| by going out of scope.
269 TEST_F(MultiThreadedCertVerifierTest
, RequestParamsComparators
) {
271 memset(a_key
.data
, 'a', sizeof(a_key
.data
));
274 memset(z_key
.data
, 'z', sizeof(z_key
.data
));
276 const CertificateList empty_list
;
277 CertificateList test_list
;
279 ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
283 MultiThreadedCertVerifier::RequestParams key1
;
284 MultiThreadedCertVerifier::RequestParams key2
;
287 // -1 means key1 is less than key2
288 // 0 means key1 equals key2
289 // 1 means key1 is greater than key2
293 // Test for basic equivalence.
294 MultiThreadedCertVerifier::RequestParams(
295 a_key
, a_key
, "www.example.test", std::string(), 0, test_list
),
296 MultiThreadedCertVerifier::RequestParams(
297 a_key
, a_key
, "www.example.test", std::string(), 0, test_list
),
301 // Test that different certificates but with the same CA and for
302 // the same host are different validation keys.
303 MultiThreadedCertVerifier::RequestParams(
304 a_key
, a_key
, "www.example.test", std::string(), 0, test_list
),
305 MultiThreadedCertVerifier::RequestParams(
306 z_key
, a_key
, "www.example.test", std::string(), 0, test_list
),
310 // Test that the same EE certificate for the same host, but with
311 // different chains are different validation keys.
312 MultiThreadedCertVerifier::RequestParams(
313 a_key
, z_key
, "www.example.test", std::string(), 0, test_list
),
314 MultiThreadedCertVerifier::RequestParams(
315 a_key
, a_key
, "www.example.test", std::string(), 0, test_list
),
319 // The same certificate, with the same chain, but for different
320 // hosts are different validation keys.
321 MultiThreadedCertVerifier::RequestParams(
322 a_key
, a_key
, "www1.example.test", std::string(), 0, test_list
),
323 MultiThreadedCertVerifier::RequestParams(
324 a_key
, a_key
, "www2.example.test", std::string(), 0, test_list
),
328 // The same certificate, chain, and host, but with different flags
329 // are different validation keys.
330 MultiThreadedCertVerifier::RequestParams(
331 a_key
, a_key
, "www.example.test", std::string(),
332 CertVerifier::VERIFY_EV_CERT
, test_list
),
333 MultiThreadedCertVerifier::RequestParams(
334 a_key
, a_key
, "www.example.test", std::string(), 0, test_list
),
338 // Different additional_trust_anchors.
339 MultiThreadedCertVerifier::RequestParams(
340 a_key
, a_key
, "www.example.test", std::string(), 0, empty_list
),
341 MultiThreadedCertVerifier::RequestParams(
342 a_key
, a_key
, "www.example.test", std::string(), 0, test_list
),
346 // Different OCSP responses.
347 MultiThreadedCertVerifier::RequestParams(
348 a_key
, a_key
, "www.example.test", "ocsp response", 0, test_list
),
349 MultiThreadedCertVerifier::RequestParams(
350 a_key
, a_key
, "www.example.test", std::string(), 0, test_list
),
354 for (size_t i
= 0; i
< arraysize(tests
); ++i
) {
355 SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS
"]", i
));
357 const MultiThreadedCertVerifier::RequestParams
& key1
= tests
[i
].key1
;
358 const MultiThreadedCertVerifier::RequestParams
& key2
= tests
[i
].key2
;
360 switch (tests
[i
].expected_result
) {
362 EXPECT_TRUE(key1
< key2
);
363 EXPECT_FALSE(key2
< key1
);
366 EXPECT_FALSE(key1
< key2
);
367 EXPECT_FALSE(key2
< key1
);
370 EXPECT_FALSE(key1
< key2
);
371 EXPECT_TRUE(key2
< key1
);
374 FAIL() << "Invalid expectation. Can be only -1, 0, 1";
379 TEST_F(MultiThreadedCertVerifierTest
, CertTrustAnchorProvider
) {
380 MockCertTrustAnchorProvider trust_provider
;
381 verifier_
.SetCertTrustAnchorProvider(&trust_provider
);
383 scoped_refptr
<X509Certificate
> test_cert(
384 ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
385 ASSERT_TRUE(test_cert
.get());
387 const CertificateList empty_cert_list
;
388 CertificateList cert_list
;
389 cert_list
.push_back(test_cert
);
391 // Check that Verify() asks the |trust_provider| for the current list of
392 // additional trust anchors.
394 CertVerifyResult verify_result
;
395 TestCompletionCallback callback
;
396 scoped_ptr
<CertVerifier::Request
> request
;
397 EXPECT_CALL(trust_provider
, GetAdditionalTrustAnchors())
398 .WillOnce(ReturnRef(empty_cert_list
));
399 error
= verifier_
.Verify(test_cert
.get(), "www.example.com", std::string(), 0,
400 NULL
, &verify_result
, callback
.callback(), &request
,
402 Mock::VerifyAndClearExpectations(&trust_provider
);
403 ASSERT_EQ(ERR_IO_PENDING
, error
);
404 EXPECT_TRUE(request
);
405 error
= callback
.WaitForResult();
406 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID
, error
);
407 ASSERT_EQ(1u, verifier_
.requests());
408 ASSERT_EQ(0u, verifier_
.cache_hits());
410 // The next Verify() uses the cached result.
411 EXPECT_CALL(trust_provider
, GetAdditionalTrustAnchors())
412 .WillOnce(ReturnRef(empty_cert_list
));
413 error
= verifier_
.Verify(test_cert
.get(), "www.example.com", std::string(), 0,
414 NULL
, &verify_result
, callback
.callback(), &request
,
416 Mock::VerifyAndClearExpectations(&trust_provider
);
417 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID
, error
);
418 EXPECT_FALSE(request
);
419 ASSERT_EQ(2u, verifier_
.requests());
420 ASSERT_EQ(1u, verifier_
.cache_hits());
422 // Another Verify() for the same certificate but with a different list of
423 // trust anchors will not reuse the cache.
424 EXPECT_CALL(trust_provider
, GetAdditionalTrustAnchors())
425 .WillOnce(ReturnRef(cert_list
));
426 error
= verifier_
.Verify(test_cert
.get(), "www.example.com", std::string(), 0,
427 NULL
, &verify_result
, callback
.callback(), &request
,
429 Mock::VerifyAndClearExpectations(&trust_provider
);
430 ASSERT_EQ(ERR_IO_PENDING
, error
);
431 EXPECT_TRUE(request
);
432 error
= callback
.WaitForResult();
433 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID
, error
);
434 ASSERT_EQ(3u, verifier_
.requests());
435 ASSERT_EQ(1u, verifier_
.cache_hits());
438 // Tests de-duplication of requests.
439 // Starts up 5 requests, of which 3 are unique.
440 TEST_F(MultiThreadedCertVerifierTest
, MultipleInflightJoin
) {
441 base::FilePath certs_dir
= GetTestCertsDirectory();
442 scoped_refptr
<X509Certificate
> test_cert(
443 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
444 ASSERT_NE(static_cast<X509Certificate
*>(nullptr), test_cert
.get());
447 CertVerifyResult verify_result1
;
448 TestCompletionCallback callback1
;
449 scoped_ptr
<CertVerifier::Request
> request1
;
450 CertVerifyResult verify_result2
;
451 TestCompletionCallback callback2
;
452 scoped_ptr
<CertVerifier::Request
> request2
;
453 CertVerifyResult verify_result3
;
454 TestCompletionCallback callback3
;
455 scoped_ptr
<CertVerifier::Request
> request3
;
456 CertVerifyResult verify_result4
;
457 TestCompletionCallback callback4
;
458 scoped_ptr
<CertVerifier::Request
> request4
;
459 CertVerifyResult verify_result5
;
460 TestCompletionCallback callback5
;
461 scoped_ptr
<CertVerifier::Request
> request5
;
463 const char domain1
[] = "www.example1.com";
464 const char domain2
[] = "www.exampleB.com";
465 const char domain3
[] = "www.example3.com";
467 // Start 3 unique requests.
468 error
= verifier_
.Verify(test_cert
.get(), domain2
, std::string(), 0, nullptr,
469 &verify_result1
, callback1
.callback(), &request1
,
471 ASSERT_EQ(ERR_IO_PENDING
, error
);
472 EXPECT_TRUE(request1
);
474 error
= verifier_
.Verify(test_cert
.get(), domain2
, std::string(), 0, nullptr,
475 &verify_result2
, callback2
.callback(), &request2
,
477 EXPECT_EQ(ERR_IO_PENDING
, error
);
478 EXPECT_TRUE(request2
);
480 error
= verifier_
.Verify(test_cert
.get(), domain3
, std::string(), 0, nullptr,
481 &verify_result3
, callback3
.callback(), &request3
,
483 EXPECT_EQ(ERR_IO_PENDING
, error
);
484 EXPECT_TRUE(request3
);
486 // Start duplicate requests (which should join to existing jobs).
487 error
= verifier_
.Verify(test_cert
.get(), domain1
, std::string(), 0, nullptr,
488 &verify_result4
, callback4
.callback(), &request4
,
490 EXPECT_EQ(ERR_IO_PENDING
, error
);
491 EXPECT_TRUE(request4
);
493 error
= verifier_
.Verify(test_cert
.get(), domain2
, std::string(), 0, nullptr,
494 &verify_result5
, callback5
.callback(), &request5
,
496 EXPECT_EQ(ERR_IO_PENDING
, error
);
497 EXPECT_TRUE(request5
);
499 error
= callback1
.WaitForResult();
500 EXPECT_TRUE(IsCertificateError(error
));
501 error
= callback2
.WaitForResult();
502 ASSERT_TRUE(IsCertificateError(error
));
503 error
= callback4
.WaitForResult();
504 ASSERT_TRUE(IsCertificateError(error
));
506 // Let the other requests automatically cancel.
507 ASSERT_EQ(5u, verifier_
.requests());
508 ASSERT_EQ(0u, verifier_
.cache_hits());
509 ASSERT_EQ(2u, verifier_
.inflight_joins());