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/files/file_path.h"
9 #include "base/format_macros.h"
10 #include "base/strings/stringprintf.h"
11 #include "net/base/net_errors.h"
12 #include "net/base/net_log.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/test/cert_test_util.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "testing/gtest/include/gtest/gtest.h"
24 using testing::ReturnRef
;
30 void FailTest(int /* result */) {
34 class MockCertVerifyProc
: public CertVerifyProc
{
36 MockCertVerifyProc() {}
39 ~MockCertVerifyProc() override
{}
41 // CertVerifyProc implementation
42 bool SupportsAdditionalTrustAnchors() const override
{ return false; }
44 int VerifyInternal(X509Certificate
* cert
,
45 const std::string
& hostname
,
48 const CertificateList
& additional_trust_anchors
,
49 CertVerifyResult
* verify_result
) override
{
50 verify_result
->Reset();
51 verify_result
->verified_cert
= cert
;
52 verify_result
->cert_status
= CERT_STATUS_COMMON_NAME_INVALID
;
53 return ERR_CERT_COMMON_NAME_INVALID
;
57 class MockCertTrustAnchorProvider
: public CertTrustAnchorProvider
{
59 MockCertTrustAnchorProvider() {}
60 virtual ~MockCertTrustAnchorProvider() {}
62 MOCK_METHOD0(GetAdditionalTrustAnchors
, const CertificateList
&());
67 class MultiThreadedCertVerifierTest
: public ::testing::Test
{
69 MultiThreadedCertVerifierTest() : verifier_(new MockCertVerifyProc()) {}
70 virtual ~MultiThreadedCertVerifierTest() {}
73 MultiThreadedCertVerifier verifier_
;
76 TEST_F(MultiThreadedCertVerifierTest
, CacheHit
) {
77 base::FilePath certs_dir
= GetTestCertsDirectory();
78 scoped_refptr
<X509Certificate
> test_cert(
79 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
80 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
.get());
83 CertVerifyResult verify_result
;
84 TestCompletionCallback callback
;
85 CertVerifier::RequestHandle request_handle
;
87 error
= verifier_
.Verify(test_cert
.get(),
95 ASSERT_EQ(ERR_IO_PENDING
, error
);
96 EXPECT_TRUE(request_handle
);
97 error
= callback
.WaitForResult();
98 ASSERT_TRUE(IsCertificateError(error
));
99 ASSERT_EQ(1u, verifier_
.requests());
100 ASSERT_EQ(0u, verifier_
.cache_hits());
101 ASSERT_EQ(0u, verifier_
.inflight_joins());
102 ASSERT_EQ(1u, verifier_
.GetCacheSize());
104 error
= verifier_
.Verify(test_cert
.get(),
112 // Synchronous completion.
113 ASSERT_NE(ERR_IO_PENDING
, error
);
114 ASSERT_TRUE(IsCertificateError(error
));
115 ASSERT_TRUE(request_handle
== NULL
);
116 ASSERT_EQ(2u, verifier_
.requests());
117 ASSERT_EQ(1u, verifier_
.cache_hits());
118 ASSERT_EQ(0u, verifier_
.inflight_joins());
119 ASSERT_EQ(1u, verifier_
.GetCacheSize());
122 // Tests the same server certificate with different intermediate CA
123 // certificates. These should be treated as different certificate chains even
124 // though the two X509Certificate objects contain the same server certificate.
125 TEST_F(MultiThreadedCertVerifierTest
, DifferentCACerts
) {
126 base::FilePath certs_dir
= GetTestCertsDirectory();
128 scoped_refptr
<X509Certificate
> server_cert
=
129 ImportCertFromFile(certs_dir
, "salesforce_com_test.pem");
130 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), server_cert
.get());
132 scoped_refptr
<X509Certificate
> intermediate_cert1
=
133 ImportCertFromFile(certs_dir
, "verisign_intermediate_ca_2011.pem");
134 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), intermediate_cert1
.get());
136 scoped_refptr
<X509Certificate
> intermediate_cert2
=
137 ImportCertFromFile(certs_dir
, "verisign_intermediate_ca_2016.pem");
138 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), intermediate_cert2
.get());
140 X509Certificate::OSCertHandles intermediates
;
141 intermediates
.push_back(intermediate_cert1
->os_cert_handle());
142 scoped_refptr
<X509Certificate
> cert_chain1
=
143 X509Certificate::CreateFromHandle(server_cert
->os_cert_handle(),
146 intermediates
.clear();
147 intermediates
.push_back(intermediate_cert2
->os_cert_handle());
148 scoped_refptr
<X509Certificate
> cert_chain2
=
149 X509Certificate::CreateFromHandle(server_cert
->os_cert_handle(),
153 CertVerifyResult verify_result
;
154 TestCompletionCallback callback
;
155 CertVerifier::RequestHandle request_handle
;
157 error
= verifier_
.Verify(cert_chain1
.get(),
165 ASSERT_EQ(ERR_IO_PENDING
, error
);
166 EXPECT_TRUE(request_handle
);
167 error
= callback
.WaitForResult();
168 ASSERT_TRUE(IsCertificateError(error
));
169 ASSERT_EQ(1u, verifier_
.requests());
170 ASSERT_EQ(0u, verifier_
.cache_hits());
171 ASSERT_EQ(0u, verifier_
.inflight_joins());
172 ASSERT_EQ(1u, verifier_
.GetCacheSize());
174 error
= verifier_
.Verify(cert_chain2
.get(),
182 ASSERT_EQ(ERR_IO_PENDING
, error
);
183 EXPECT_TRUE(request_handle
);
184 error
= callback
.WaitForResult();
185 ASSERT_TRUE(IsCertificateError(error
));
186 ASSERT_EQ(2u, verifier_
.requests());
187 ASSERT_EQ(0u, verifier_
.cache_hits());
188 ASSERT_EQ(0u, verifier_
.inflight_joins());
189 ASSERT_EQ(2u, verifier_
.GetCacheSize());
192 // Tests an inflight join.
193 TEST_F(MultiThreadedCertVerifierTest
, InflightJoin
) {
194 base::FilePath certs_dir
= GetTestCertsDirectory();
195 scoped_refptr
<X509Certificate
> test_cert(
196 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
197 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
.get());
200 CertVerifyResult verify_result
;
201 TestCompletionCallback callback
;
202 CertVerifier::RequestHandle request_handle
;
203 CertVerifyResult verify_result2
;
204 TestCompletionCallback callback2
;
205 CertVerifier::RequestHandle request_handle2
;
207 error
= verifier_
.Verify(test_cert
.get(),
215 ASSERT_EQ(ERR_IO_PENDING
, error
);
216 EXPECT_TRUE(request_handle
);
217 error
= verifier_
.Verify(test_cert
.get(),
222 callback2
.callback(),
225 EXPECT_EQ(ERR_IO_PENDING
, error
);
226 EXPECT_TRUE(request_handle2
!= NULL
);
227 error
= callback
.WaitForResult();
228 EXPECT_TRUE(IsCertificateError(error
));
229 error
= callback2
.WaitForResult();
230 ASSERT_TRUE(IsCertificateError(error
));
231 ASSERT_EQ(2u, verifier_
.requests());
232 ASSERT_EQ(0u, verifier_
.cache_hits());
233 ASSERT_EQ(1u, verifier_
.inflight_joins());
236 // Tests that the callback of a canceled request is never made.
237 TEST_F(MultiThreadedCertVerifierTest
, CancelRequest
) {
238 base::FilePath certs_dir
= GetTestCertsDirectory();
239 scoped_refptr
<X509Certificate
> test_cert(
240 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
241 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
.get());
244 CertVerifyResult verify_result
;
245 CertVerifier::RequestHandle request_handle
;
247 error
= verifier_
.Verify(test_cert
.get(),
252 base::Bind(&FailTest
),
255 ASSERT_EQ(ERR_IO_PENDING
, error
);
256 ASSERT_TRUE(request_handle
!= NULL
);
257 verifier_
.CancelRequest(request_handle
);
259 // Issue a few more requests to the worker pool and wait for their
260 // completion, so that the task of the canceled request (which runs on a
261 // worker thread) is likely to complete by the end of this test.
262 TestCompletionCallback callback
;
263 for (int i
= 0; i
< 5; ++i
) {
264 error
= verifier_
.Verify(test_cert
.get(),
272 ASSERT_EQ(ERR_IO_PENDING
, error
);
273 EXPECT_TRUE(request_handle
);
274 error
= callback
.WaitForResult();
275 verifier_
.ClearCache();
279 // Tests that a canceled request is not leaked.
280 #if !defined(LEAK_SANITIZER)
281 #define MAYBE_CancelRequestThenQuit CancelRequestThenQuit
283 // See PR303886. LeakSanitizer flags a leak here.
284 #define MAYBE_CancelRequestThenQuit DISABLED_CancelRequestThenQuit
286 TEST_F(MultiThreadedCertVerifierTest
, MAYBE_CancelRequestThenQuit
) {
287 base::FilePath certs_dir
= GetTestCertsDirectory();
288 scoped_refptr
<X509Certificate
> test_cert(
289 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
290 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
.get());
293 CertVerifyResult verify_result
;
294 TestCompletionCallback callback
;
295 CertVerifier::RequestHandle request_handle
;
297 error
= verifier_
.Verify(test_cert
.get(),
305 ASSERT_EQ(ERR_IO_PENDING
, error
);
306 EXPECT_TRUE(request_handle
);
307 verifier_
.CancelRequest(request_handle
);
308 // Destroy |verifier| by going out of scope.
311 TEST_F(MultiThreadedCertVerifierTest
, RequestParamsComparators
) {
313 memset(a_key
.data
, 'a', sizeof(a_key
.data
));
316 memset(z_key
.data
, 'z', sizeof(z_key
.data
));
318 const CertificateList empty_list
;
319 CertificateList test_list
;
321 ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
325 MultiThreadedCertVerifier::RequestParams key1
;
326 MultiThreadedCertVerifier::RequestParams key2
;
329 // -1 means key1 is less than key2
330 // 0 means key1 equals key2
331 // 1 means key1 is greater than key2
334 { // Test for basic equivalence.
335 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
337 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
341 { // Test that different certificates but with the same CA and for
342 // the same host are different validation keys.
343 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
345 MultiThreadedCertVerifier::RequestParams(z_key
, a_key
, "www.example.test",
349 { // Test that the same EE certificate for the same host, but with
350 // different chains are different validation keys.
351 MultiThreadedCertVerifier::RequestParams(a_key
, z_key
, "www.example.test",
353 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
357 { // The same certificate, with the same chain, but for different
358 // hosts are different validation keys.
359 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
,
360 "www1.example.test", 0,
362 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
,
363 "www2.example.test", 0,
367 { // The same certificate, chain, and host, but with different flags
368 // are different validation keys.
369 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
370 CertVerifier::VERIFY_EV_CERT
,
372 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
376 { // Different additional_trust_anchors.
377 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
379 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
384 for (size_t i
= 0; i
< arraysize(tests
); ++i
) {
385 SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS
"]", i
));
387 const MultiThreadedCertVerifier::RequestParams
& key1
= tests
[i
].key1
;
388 const MultiThreadedCertVerifier::RequestParams
& key2
= tests
[i
].key2
;
390 switch (tests
[i
].expected_result
) {
392 EXPECT_TRUE(key1
< key2
);
393 EXPECT_FALSE(key2
< key1
);
396 EXPECT_FALSE(key1
< key2
);
397 EXPECT_FALSE(key2
< key1
);
400 EXPECT_FALSE(key1
< key2
);
401 EXPECT_TRUE(key2
< key1
);
404 FAIL() << "Invalid expectation. Can be only -1, 0, 1";
409 TEST_F(MultiThreadedCertVerifierTest
, CertTrustAnchorProvider
) {
410 MockCertTrustAnchorProvider trust_provider
;
411 verifier_
.SetCertTrustAnchorProvider(&trust_provider
);
413 scoped_refptr
<X509Certificate
> test_cert(
414 ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
415 ASSERT_TRUE(test_cert
.get());
417 const CertificateList empty_cert_list
;
418 CertificateList cert_list
;
419 cert_list
.push_back(test_cert
);
421 // Check that Verify() asks the |trust_provider| for the current list of
422 // additional trust anchors.
424 CertVerifyResult verify_result
;
425 TestCompletionCallback callback
;
426 CertVerifier::RequestHandle request_handle
;
427 EXPECT_CALL(trust_provider
, GetAdditionalTrustAnchors())
428 .WillOnce(ReturnRef(empty_cert_list
));
429 error
= verifier_
.Verify(test_cert
.get(),
437 Mock::VerifyAndClearExpectations(&trust_provider
);
438 ASSERT_EQ(ERR_IO_PENDING
, error
);
439 EXPECT_TRUE(request_handle
);
440 error
= callback
.WaitForResult();
441 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID
, error
);
442 ASSERT_EQ(1u, verifier_
.requests());
443 ASSERT_EQ(0u, verifier_
.cache_hits());
445 // The next Verify() uses the cached result.
446 EXPECT_CALL(trust_provider
, GetAdditionalTrustAnchors())
447 .WillOnce(ReturnRef(empty_cert_list
));
448 error
= verifier_
.Verify(test_cert
.get(),
456 Mock::VerifyAndClearExpectations(&trust_provider
);
457 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID
, error
);
458 EXPECT_FALSE(request_handle
);
459 ASSERT_EQ(2u, verifier_
.requests());
460 ASSERT_EQ(1u, verifier_
.cache_hits());
462 // Another Verify() for the same certificate but with a different list of
463 // trust anchors will not reuse the cache.
464 EXPECT_CALL(trust_provider
, GetAdditionalTrustAnchors())
465 .WillOnce(ReturnRef(cert_list
));
466 error
= verifier_
.Verify(test_cert
.get(),
474 Mock::VerifyAndClearExpectations(&trust_provider
);
475 ASSERT_EQ(ERR_IO_PENDING
, error
);
476 EXPECT_TRUE(request_handle
);
477 error
= callback
.WaitForResult();
478 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID
, error
);
479 ASSERT_EQ(3u, verifier_
.requests());
480 ASSERT_EQ(1u, verifier_
.cache_hits());