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, memory may
256 // be leaked if the main thread shuts down before the worker thread
257 // completes. In particular MultiThreadedCertVerifier calls
258 // base::WorkerPool::PostTaskAndReply(), which leaks its "relay" when it
259 // can't post the reply back to the origin thread. See
260 // https://crbug.com/522514
261 ANNOTATE_SCOPED_MEMORY_LEAK
;
262 error
= verifier_
.Verify(test_cert
.get(), "www.example.com", std::string(),
263 0, NULL
, &verify_result
, callback
.callback(),
264 &request
, BoundNetLog());
266 ASSERT_EQ(ERR_IO_PENDING
, error
);
267 EXPECT_TRUE(request
);
269 // Destroy |verifier| by going out of scope.
272 TEST_F(MultiThreadedCertVerifierTest
, RequestParamsComparators
) {
274 memset(a_key
.data
, 'a', sizeof(a_key
.data
));
277 memset(z_key
.data
, 'z', sizeof(z_key
.data
));
279 const CertificateList empty_list
;
280 CertificateList test_list
;
282 ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
286 MultiThreadedCertVerifier::RequestParams key1
;
287 MultiThreadedCertVerifier::RequestParams key2
;
290 // -1 means key1 is less than key2
291 // 0 means key1 equals key2
292 // 1 means key1 is greater than key2
296 // Test for basic equivalence.
297 MultiThreadedCertVerifier::RequestParams(
298 a_key
, a_key
, "www.example.test", std::string(), 0, test_list
),
299 MultiThreadedCertVerifier::RequestParams(
300 a_key
, a_key
, "www.example.test", std::string(), 0, test_list
),
304 // Test that different certificates but with the same CA and for
305 // the same host are different validation keys.
306 MultiThreadedCertVerifier::RequestParams(
307 a_key
, a_key
, "www.example.test", std::string(), 0, test_list
),
308 MultiThreadedCertVerifier::RequestParams(
309 z_key
, a_key
, "www.example.test", std::string(), 0, test_list
),
313 // Test that the same EE certificate for the same host, but with
314 // different chains are different validation keys.
315 MultiThreadedCertVerifier::RequestParams(
316 a_key
, z_key
, "www.example.test", std::string(), 0, test_list
),
317 MultiThreadedCertVerifier::RequestParams(
318 a_key
, a_key
, "www.example.test", std::string(), 0, test_list
),
322 // The same certificate, with the same chain, but for different
323 // hosts are different validation keys.
324 MultiThreadedCertVerifier::RequestParams(
325 a_key
, a_key
, "www1.example.test", std::string(), 0, test_list
),
326 MultiThreadedCertVerifier::RequestParams(
327 a_key
, a_key
, "www2.example.test", std::string(), 0, test_list
),
331 // The same certificate, chain, and host, but with different flags
332 // are different validation keys.
333 MultiThreadedCertVerifier::RequestParams(
334 a_key
, a_key
, "www.example.test", std::string(),
335 CertVerifier::VERIFY_EV_CERT
, test_list
),
336 MultiThreadedCertVerifier::RequestParams(
337 a_key
, a_key
, "www.example.test", std::string(), 0, test_list
),
341 // Different additional_trust_anchors.
342 MultiThreadedCertVerifier::RequestParams(
343 a_key
, a_key
, "www.example.test", std::string(), 0, empty_list
),
344 MultiThreadedCertVerifier::RequestParams(
345 a_key
, a_key
, "www.example.test", std::string(), 0, test_list
),
349 // Different OCSP responses.
350 MultiThreadedCertVerifier::RequestParams(
351 a_key
, a_key
, "www.example.test", "ocsp response", 0, test_list
),
352 MultiThreadedCertVerifier::RequestParams(
353 a_key
, a_key
, "www.example.test", std::string(), 0, test_list
),
357 for (size_t i
= 0; i
< arraysize(tests
); ++i
) {
358 SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS
"]", i
));
360 const MultiThreadedCertVerifier::RequestParams
& key1
= tests
[i
].key1
;
361 const MultiThreadedCertVerifier::RequestParams
& key2
= tests
[i
].key2
;
363 switch (tests
[i
].expected_result
) {
365 EXPECT_TRUE(key1
< key2
);
366 EXPECT_FALSE(key2
< key1
);
369 EXPECT_FALSE(key1
< key2
);
370 EXPECT_FALSE(key2
< key1
);
373 EXPECT_FALSE(key1
< key2
);
374 EXPECT_TRUE(key2
< key1
);
377 FAIL() << "Invalid expectation. Can be only -1, 0, 1";
382 TEST_F(MultiThreadedCertVerifierTest
, CertTrustAnchorProvider
) {
383 MockCertTrustAnchorProvider trust_provider
;
384 verifier_
.SetCertTrustAnchorProvider(&trust_provider
);
386 scoped_refptr
<X509Certificate
> test_cert(
387 ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
388 ASSERT_TRUE(test_cert
.get());
390 const CertificateList empty_cert_list
;
391 CertificateList cert_list
;
392 cert_list
.push_back(test_cert
);
394 // Check that Verify() asks the |trust_provider| for the current list of
395 // additional trust anchors.
397 CertVerifyResult verify_result
;
398 TestCompletionCallback callback
;
399 scoped_ptr
<CertVerifier::Request
> request
;
400 EXPECT_CALL(trust_provider
, GetAdditionalTrustAnchors())
401 .WillOnce(ReturnRef(empty_cert_list
));
402 error
= verifier_
.Verify(test_cert
.get(), "www.example.com", std::string(), 0,
403 NULL
, &verify_result
, callback
.callback(), &request
,
405 Mock::VerifyAndClearExpectations(&trust_provider
);
406 ASSERT_EQ(ERR_IO_PENDING
, error
);
407 EXPECT_TRUE(request
);
408 error
= callback
.WaitForResult();
409 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID
, error
);
410 ASSERT_EQ(1u, verifier_
.requests());
411 ASSERT_EQ(0u, verifier_
.cache_hits());
413 // The next Verify() uses the cached result.
414 EXPECT_CALL(trust_provider
, GetAdditionalTrustAnchors())
415 .WillOnce(ReturnRef(empty_cert_list
));
416 error
= verifier_
.Verify(test_cert
.get(), "www.example.com", std::string(), 0,
417 NULL
, &verify_result
, callback
.callback(), &request
,
419 Mock::VerifyAndClearExpectations(&trust_provider
);
420 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID
, error
);
421 EXPECT_FALSE(request
);
422 ASSERT_EQ(2u, verifier_
.requests());
423 ASSERT_EQ(1u, verifier_
.cache_hits());
425 // Another Verify() for the same certificate but with a different list of
426 // trust anchors will not reuse the cache.
427 EXPECT_CALL(trust_provider
, GetAdditionalTrustAnchors())
428 .WillOnce(ReturnRef(cert_list
));
429 error
= verifier_
.Verify(test_cert
.get(), "www.example.com", std::string(), 0,
430 NULL
, &verify_result
, callback
.callback(), &request
,
432 Mock::VerifyAndClearExpectations(&trust_provider
);
433 ASSERT_EQ(ERR_IO_PENDING
, error
);
434 EXPECT_TRUE(request
);
435 error
= callback
.WaitForResult();
436 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID
, error
);
437 ASSERT_EQ(3u, verifier_
.requests());
438 ASSERT_EQ(1u, verifier_
.cache_hits());
441 // Tests de-duplication of requests.
442 // Starts up 5 requests, of which 3 are unique.
443 TEST_F(MultiThreadedCertVerifierTest
, MultipleInflightJoin
) {
444 base::FilePath certs_dir
= GetTestCertsDirectory();
445 scoped_refptr
<X509Certificate
> test_cert(
446 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
447 ASSERT_NE(static_cast<X509Certificate
*>(nullptr), test_cert
.get());
450 CertVerifyResult verify_result1
;
451 TestCompletionCallback callback1
;
452 scoped_ptr
<CertVerifier::Request
> request1
;
453 CertVerifyResult verify_result2
;
454 TestCompletionCallback callback2
;
455 scoped_ptr
<CertVerifier::Request
> request2
;
456 CertVerifyResult verify_result3
;
457 TestCompletionCallback callback3
;
458 scoped_ptr
<CertVerifier::Request
> request3
;
459 CertVerifyResult verify_result4
;
460 TestCompletionCallback callback4
;
461 scoped_ptr
<CertVerifier::Request
> request4
;
462 CertVerifyResult verify_result5
;
463 TestCompletionCallback callback5
;
464 scoped_ptr
<CertVerifier::Request
> request5
;
466 const char domain1
[] = "www.example1.com";
467 const char domain2
[] = "www.exampleB.com";
468 const char domain3
[] = "www.example3.com";
470 // Start 3 unique requests.
471 error
= verifier_
.Verify(test_cert
.get(), domain2
, std::string(), 0, nullptr,
472 &verify_result1
, callback1
.callback(), &request1
,
474 ASSERT_EQ(ERR_IO_PENDING
, error
);
475 EXPECT_TRUE(request1
);
477 error
= verifier_
.Verify(test_cert
.get(), domain2
, std::string(), 0, nullptr,
478 &verify_result2
, callback2
.callback(), &request2
,
480 EXPECT_EQ(ERR_IO_PENDING
, error
);
481 EXPECT_TRUE(request2
);
483 error
= verifier_
.Verify(test_cert
.get(), domain3
, std::string(), 0, nullptr,
484 &verify_result3
, callback3
.callback(), &request3
,
486 EXPECT_EQ(ERR_IO_PENDING
, error
);
487 EXPECT_TRUE(request3
);
489 // Start duplicate requests (which should join to existing jobs).
490 error
= verifier_
.Verify(test_cert
.get(), domain1
, std::string(), 0, nullptr,
491 &verify_result4
, callback4
.callback(), &request4
,
493 EXPECT_EQ(ERR_IO_PENDING
, error
);
494 EXPECT_TRUE(request4
);
496 error
= verifier_
.Verify(test_cert
.get(), domain2
, std::string(), 0, nullptr,
497 &verify_result5
, callback5
.callback(), &request5
,
499 EXPECT_EQ(ERR_IO_PENDING
, error
);
500 EXPECT_TRUE(request5
);
502 error
= callback1
.WaitForResult();
503 EXPECT_TRUE(IsCertificateError(error
));
504 error
= callback2
.WaitForResult();
505 ASSERT_TRUE(IsCertificateError(error
));
506 error
= callback4
.WaitForResult();
507 ASSERT_TRUE(IsCertificateError(error
));
509 // Let the other requests automatically cancel.
510 ASSERT_EQ(5u, verifier_
.requests());
511 ASSERT_EQ(0u, verifier_
.cache_hits());
512 ASSERT_EQ(2u, verifier_
.inflight_joins());