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 virtual ~MockCertVerifyProc() {}
41 // CertVerifyProc implementation
42 virtual bool SupportsAdditionalTrustAnchors() const OVERRIDE
{
46 virtual int VerifyInternal(X509Certificate
* cert
,
47 const std::string
& hostname
,
50 const CertificateList
& additional_trust_anchors
,
51 CertVerifyResult
* verify_result
) OVERRIDE
{
52 verify_result
->Reset();
53 verify_result
->verified_cert
= cert
;
54 verify_result
->cert_status
= CERT_STATUS_COMMON_NAME_INVALID
;
55 return ERR_CERT_COMMON_NAME_INVALID
;
59 class MockCertTrustAnchorProvider
: public CertTrustAnchorProvider
{
61 MockCertTrustAnchorProvider() {}
62 virtual ~MockCertTrustAnchorProvider() {}
64 MOCK_METHOD0(GetAdditionalTrustAnchors
, const CertificateList
&());
69 class MultiThreadedCertVerifierTest
: public ::testing::Test
{
71 MultiThreadedCertVerifierTest() : verifier_(new MockCertVerifyProc()) {}
72 virtual ~MultiThreadedCertVerifierTest() {}
75 MultiThreadedCertVerifier verifier_
;
78 TEST_F(MultiThreadedCertVerifierTest
, CacheHit
) {
79 base::FilePath certs_dir
= GetTestCertsDirectory();
80 scoped_refptr
<X509Certificate
> test_cert(
81 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
82 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
.get());
85 CertVerifyResult verify_result
;
86 TestCompletionCallback callback
;
87 CertVerifier::RequestHandle request_handle
;
89 error
= verifier_
.Verify(test_cert
.get(),
97 ASSERT_EQ(ERR_IO_PENDING
, error
);
98 EXPECT_TRUE(request_handle
);
99 error
= callback
.WaitForResult();
100 ASSERT_TRUE(IsCertificateError(error
));
101 ASSERT_EQ(1u, verifier_
.requests());
102 ASSERT_EQ(0u, verifier_
.cache_hits());
103 ASSERT_EQ(0u, verifier_
.inflight_joins());
104 ASSERT_EQ(1u, verifier_
.GetCacheSize());
106 error
= verifier_
.Verify(test_cert
.get(),
114 // Synchronous completion.
115 ASSERT_NE(ERR_IO_PENDING
, error
);
116 ASSERT_TRUE(IsCertificateError(error
));
117 ASSERT_TRUE(request_handle
== NULL
);
118 ASSERT_EQ(2u, verifier_
.requests());
119 ASSERT_EQ(1u, verifier_
.cache_hits());
120 ASSERT_EQ(0u, verifier_
.inflight_joins());
121 ASSERT_EQ(1u, verifier_
.GetCacheSize());
124 // Tests the same server certificate with different intermediate CA
125 // certificates. These should be treated as different certificate chains even
126 // though the two X509Certificate objects contain the same server certificate.
127 TEST_F(MultiThreadedCertVerifierTest
, DifferentCACerts
) {
128 base::FilePath certs_dir
= GetTestCertsDirectory();
130 scoped_refptr
<X509Certificate
> server_cert
=
131 ImportCertFromFile(certs_dir
, "salesforce_com_test.pem");
132 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), server_cert
.get());
134 scoped_refptr
<X509Certificate
> intermediate_cert1
=
135 ImportCertFromFile(certs_dir
, "verisign_intermediate_ca_2011.pem");
136 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), intermediate_cert1
.get());
138 scoped_refptr
<X509Certificate
> intermediate_cert2
=
139 ImportCertFromFile(certs_dir
, "verisign_intermediate_ca_2016.pem");
140 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), intermediate_cert2
.get());
142 X509Certificate::OSCertHandles intermediates
;
143 intermediates
.push_back(intermediate_cert1
->os_cert_handle());
144 scoped_refptr
<X509Certificate
> cert_chain1
=
145 X509Certificate::CreateFromHandle(server_cert
->os_cert_handle(),
148 intermediates
.clear();
149 intermediates
.push_back(intermediate_cert2
->os_cert_handle());
150 scoped_refptr
<X509Certificate
> cert_chain2
=
151 X509Certificate::CreateFromHandle(server_cert
->os_cert_handle(),
155 CertVerifyResult verify_result
;
156 TestCompletionCallback callback
;
157 CertVerifier::RequestHandle request_handle
;
159 error
= verifier_
.Verify(cert_chain1
.get(),
167 ASSERT_EQ(ERR_IO_PENDING
, error
);
168 EXPECT_TRUE(request_handle
);
169 error
= callback
.WaitForResult();
170 ASSERT_TRUE(IsCertificateError(error
));
171 ASSERT_EQ(1u, verifier_
.requests());
172 ASSERT_EQ(0u, verifier_
.cache_hits());
173 ASSERT_EQ(0u, verifier_
.inflight_joins());
174 ASSERT_EQ(1u, verifier_
.GetCacheSize());
176 error
= verifier_
.Verify(cert_chain2
.get(),
184 ASSERT_EQ(ERR_IO_PENDING
, error
);
185 EXPECT_TRUE(request_handle
);
186 error
= callback
.WaitForResult();
187 ASSERT_TRUE(IsCertificateError(error
));
188 ASSERT_EQ(2u, verifier_
.requests());
189 ASSERT_EQ(0u, verifier_
.cache_hits());
190 ASSERT_EQ(0u, verifier_
.inflight_joins());
191 ASSERT_EQ(2u, verifier_
.GetCacheSize());
194 // Tests an inflight join.
195 TEST_F(MultiThreadedCertVerifierTest
, InflightJoin
) {
196 base::FilePath certs_dir
= GetTestCertsDirectory();
197 scoped_refptr
<X509Certificate
> test_cert(
198 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
199 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
.get());
202 CertVerifyResult verify_result
;
203 TestCompletionCallback callback
;
204 CertVerifier::RequestHandle request_handle
;
205 CertVerifyResult verify_result2
;
206 TestCompletionCallback callback2
;
207 CertVerifier::RequestHandle request_handle2
;
209 error
= verifier_
.Verify(test_cert
.get(),
217 ASSERT_EQ(ERR_IO_PENDING
, error
);
218 EXPECT_TRUE(request_handle
);
219 error
= verifier_
.Verify(test_cert
.get(),
224 callback2
.callback(),
227 EXPECT_EQ(ERR_IO_PENDING
, error
);
228 EXPECT_TRUE(request_handle2
!= NULL
);
229 error
= callback
.WaitForResult();
230 EXPECT_TRUE(IsCertificateError(error
));
231 error
= callback2
.WaitForResult();
232 ASSERT_TRUE(IsCertificateError(error
));
233 ASSERT_EQ(2u, verifier_
.requests());
234 ASSERT_EQ(0u, verifier_
.cache_hits());
235 ASSERT_EQ(1u, verifier_
.inflight_joins());
238 // Tests that the callback of a canceled request is never made.
239 TEST_F(MultiThreadedCertVerifierTest
, CancelRequest
) {
240 base::FilePath certs_dir
= GetTestCertsDirectory();
241 scoped_refptr
<X509Certificate
> test_cert(
242 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
243 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
.get());
246 CertVerifyResult verify_result
;
247 CertVerifier::RequestHandle request_handle
;
249 error
= verifier_
.Verify(test_cert
.get(),
254 base::Bind(&FailTest
),
257 ASSERT_EQ(ERR_IO_PENDING
, error
);
258 ASSERT_TRUE(request_handle
!= NULL
);
259 verifier_
.CancelRequest(request_handle
);
261 // Issue a few more requests to the worker pool and wait for their
262 // completion, so that the task of the canceled request (which runs on a
263 // worker thread) is likely to complete by the end of this test.
264 TestCompletionCallback callback
;
265 for (int i
= 0; i
< 5; ++i
) {
266 error
= verifier_
.Verify(test_cert
.get(),
274 ASSERT_EQ(ERR_IO_PENDING
, error
);
275 EXPECT_TRUE(request_handle
);
276 error
= callback
.WaitForResult();
277 verifier_
.ClearCache();
281 // Tests that a canceled request is not leaked.
282 #if !defined(LEAK_SANITIZER)
283 #define MAYBE_CancelRequestThenQuit CancelRequestThenQuit
285 // See PR303886. LeakSanitizer flags a leak here.
286 #define MAYBE_CancelRequestThenQuit DISABLED_CancelRequestThenQuit
288 TEST_F(MultiThreadedCertVerifierTest
, MAYBE_CancelRequestThenQuit
) {
289 base::FilePath certs_dir
= GetTestCertsDirectory();
290 scoped_refptr
<X509Certificate
> test_cert(
291 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
292 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
.get());
295 CertVerifyResult verify_result
;
296 TestCompletionCallback callback
;
297 CertVerifier::RequestHandle request_handle
;
299 error
= verifier_
.Verify(test_cert
.get(),
307 ASSERT_EQ(ERR_IO_PENDING
, error
);
308 EXPECT_TRUE(request_handle
);
309 verifier_
.CancelRequest(request_handle
);
310 // Destroy |verifier| by going out of scope.
313 TEST_F(MultiThreadedCertVerifierTest
, RequestParamsComparators
) {
315 memset(a_key
.data
, 'a', sizeof(a_key
.data
));
318 memset(z_key
.data
, 'z', sizeof(z_key
.data
));
320 const CertificateList empty_list
;
321 CertificateList test_list
;
323 ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
327 MultiThreadedCertVerifier::RequestParams key1
;
328 MultiThreadedCertVerifier::RequestParams key2
;
331 // -1 means key1 is less than key2
332 // 0 means key1 equals key2
333 // 1 means key1 is greater than key2
336 { // Test for basic equivalence.
337 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
339 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
343 { // Test that different certificates but with the same CA and for
344 // the same host are different validation keys.
345 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
347 MultiThreadedCertVerifier::RequestParams(z_key
, a_key
, "www.example.test",
351 { // Test that the same EE certificate for the same host, but with
352 // different chains are different validation keys.
353 MultiThreadedCertVerifier::RequestParams(a_key
, z_key
, "www.example.test",
355 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
359 { // The same certificate, with the same chain, but for different
360 // hosts are different validation keys.
361 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
,
362 "www1.example.test", 0,
364 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
,
365 "www2.example.test", 0,
369 { // The same certificate, chain, and host, but with different flags
370 // are different validation keys.
371 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
372 CertVerifier::VERIFY_EV_CERT
,
374 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
378 { // Different additional_trust_anchors.
379 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
381 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
386 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(tests
); ++i
) {
387 SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS
"]", i
));
389 const MultiThreadedCertVerifier::RequestParams
& key1
= tests
[i
].key1
;
390 const MultiThreadedCertVerifier::RequestParams
& key2
= tests
[i
].key2
;
392 switch (tests
[i
].expected_result
) {
394 EXPECT_TRUE(key1
< key2
);
395 EXPECT_FALSE(key2
< key1
);
398 EXPECT_FALSE(key1
< key2
);
399 EXPECT_FALSE(key2
< key1
);
402 EXPECT_FALSE(key1
< key2
);
403 EXPECT_TRUE(key2
< key1
);
406 FAIL() << "Invalid expectation. Can be only -1, 0, 1";
411 TEST_F(MultiThreadedCertVerifierTest
, CertTrustAnchorProvider
) {
412 MockCertTrustAnchorProvider trust_provider
;
413 verifier_
.SetCertTrustAnchorProvider(&trust_provider
);
415 scoped_refptr
<X509Certificate
> test_cert(
416 ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
417 ASSERT_TRUE(test_cert
.get());
419 const CertificateList empty_cert_list
;
420 CertificateList cert_list
;
421 cert_list
.push_back(test_cert
);
423 // Check that Verify() asks the |trust_provider| for the current list of
424 // additional trust anchors.
426 CertVerifyResult verify_result
;
427 TestCompletionCallback callback
;
428 CertVerifier::RequestHandle request_handle
;
429 EXPECT_CALL(trust_provider
, GetAdditionalTrustAnchors())
430 .WillOnce(ReturnRef(empty_cert_list
));
431 error
= verifier_
.Verify(test_cert
.get(),
439 Mock::VerifyAndClearExpectations(&trust_provider
);
440 ASSERT_EQ(ERR_IO_PENDING
, error
);
441 EXPECT_TRUE(request_handle
);
442 error
= callback
.WaitForResult();
443 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID
, error
);
444 ASSERT_EQ(1u, verifier_
.requests());
445 ASSERT_EQ(0u, verifier_
.cache_hits());
447 // The next Verify() uses the cached result.
448 EXPECT_CALL(trust_provider
, GetAdditionalTrustAnchors())
449 .WillOnce(ReturnRef(empty_cert_list
));
450 error
= verifier_
.Verify(test_cert
.get(),
458 Mock::VerifyAndClearExpectations(&trust_provider
);
459 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID
, error
);
460 EXPECT_FALSE(request_handle
);
461 ASSERT_EQ(2u, verifier_
.requests());
462 ASSERT_EQ(1u, verifier_
.cache_hits());
464 // Another Verify() for the same certificate but with a different list of
465 // trust anchors will not reuse the cache.
466 EXPECT_CALL(trust_provider
, GetAdditionalTrustAnchors())
467 .WillOnce(ReturnRef(cert_list
));
468 error
= verifier_
.Verify(test_cert
.get(),
476 Mock::VerifyAndClearExpectations(&trust_provider
);
477 ASSERT_EQ(ERR_IO_PENDING
, error
);
478 EXPECT_TRUE(request_handle
);
479 error
= callback
.WaitForResult();
480 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID
, error
);
481 ASSERT_EQ(3u, verifier_
.requests());
482 ASSERT_EQ(1u, verifier_
.cache_hits());