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; }
45 int VerifyInternal(X509Certificate
* cert
,
46 const std::string
& hostname
,
49 const CertificateList
& additional_trust_anchors
,
50 CertVerifyResult
* verify_result
) override
{
51 verify_result
->Reset();
52 verify_result
->verified_cert
= cert
;
53 verify_result
->cert_status
= CERT_STATUS_COMMON_NAME_INVALID
;
54 return ERR_CERT_COMMON_NAME_INVALID
;
58 class MockCertTrustAnchorProvider
: public CertTrustAnchorProvider
{
60 MockCertTrustAnchorProvider() {}
61 virtual ~MockCertTrustAnchorProvider() {}
63 MOCK_METHOD0(GetAdditionalTrustAnchors
, const CertificateList
&());
68 class MultiThreadedCertVerifierTest
: public ::testing::Test
{
70 MultiThreadedCertVerifierTest() : verifier_(new MockCertVerifyProc()) {}
71 ~MultiThreadedCertVerifierTest() override
{}
74 MultiThreadedCertVerifier verifier_
;
77 TEST_F(MultiThreadedCertVerifierTest
, CacheHit
) {
78 base::FilePath certs_dir
= GetTestCertsDirectory();
79 scoped_refptr
<X509Certificate
> test_cert(
80 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
81 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
.get());
84 CertVerifyResult verify_result
;
85 TestCompletionCallback callback
;
86 CertVerifier::RequestHandle request_handle
;
88 error
= verifier_
.Verify(test_cert
.get(),
96 ASSERT_EQ(ERR_IO_PENDING
, error
);
97 EXPECT_TRUE(request_handle
);
98 error
= callback
.WaitForResult();
99 ASSERT_TRUE(IsCertificateError(error
));
100 ASSERT_EQ(1u, verifier_
.requests());
101 ASSERT_EQ(0u, verifier_
.cache_hits());
102 ASSERT_EQ(0u, verifier_
.inflight_joins());
103 ASSERT_EQ(1u, verifier_
.GetCacheSize());
105 error
= verifier_
.Verify(test_cert
.get(),
113 // Synchronous completion.
114 ASSERT_NE(ERR_IO_PENDING
, error
);
115 ASSERT_TRUE(IsCertificateError(error
));
116 ASSERT_TRUE(request_handle
== NULL
);
117 ASSERT_EQ(2u, verifier_
.requests());
118 ASSERT_EQ(1u, verifier_
.cache_hits());
119 ASSERT_EQ(0u, verifier_
.inflight_joins());
120 ASSERT_EQ(1u, verifier_
.GetCacheSize());
123 // Tests the same server certificate with different intermediate CA
124 // certificates. These should be treated as different certificate chains even
125 // though the two X509Certificate objects contain the same server certificate.
126 TEST_F(MultiThreadedCertVerifierTest
, DifferentCACerts
) {
127 base::FilePath certs_dir
= GetTestCertsDirectory();
129 scoped_refptr
<X509Certificate
> server_cert
=
130 ImportCertFromFile(certs_dir
, "salesforce_com_test.pem");
131 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), server_cert
.get());
133 scoped_refptr
<X509Certificate
> intermediate_cert1
=
134 ImportCertFromFile(certs_dir
, "verisign_intermediate_ca_2011.pem");
135 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), intermediate_cert1
.get());
137 scoped_refptr
<X509Certificate
> intermediate_cert2
=
138 ImportCertFromFile(certs_dir
, "verisign_intermediate_ca_2016.pem");
139 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), intermediate_cert2
.get());
141 X509Certificate::OSCertHandles intermediates
;
142 intermediates
.push_back(intermediate_cert1
->os_cert_handle());
143 scoped_refptr
<X509Certificate
> cert_chain1
=
144 X509Certificate::CreateFromHandle(server_cert
->os_cert_handle(),
147 intermediates
.clear();
148 intermediates
.push_back(intermediate_cert2
->os_cert_handle());
149 scoped_refptr
<X509Certificate
> cert_chain2
=
150 X509Certificate::CreateFromHandle(server_cert
->os_cert_handle(),
154 CertVerifyResult verify_result
;
155 TestCompletionCallback callback
;
156 CertVerifier::RequestHandle request_handle
;
158 error
= verifier_
.Verify(cert_chain1
.get(),
166 ASSERT_EQ(ERR_IO_PENDING
, error
);
167 EXPECT_TRUE(request_handle
);
168 error
= callback
.WaitForResult();
169 ASSERT_TRUE(IsCertificateError(error
));
170 ASSERT_EQ(1u, verifier_
.requests());
171 ASSERT_EQ(0u, verifier_
.cache_hits());
172 ASSERT_EQ(0u, verifier_
.inflight_joins());
173 ASSERT_EQ(1u, verifier_
.GetCacheSize());
175 error
= verifier_
.Verify(cert_chain2
.get(),
183 ASSERT_EQ(ERR_IO_PENDING
, error
);
184 EXPECT_TRUE(request_handle
);
185 error
= callback
.WaitForResult();
186 ASSERT_TRUE(IsCertificateError(error
));
187 ASSERT_EQ(2u, verifier_
.requests());
188 ASSERT_EQ(0u, verifier_
.cache_hits());
189 ASSERT_EQ(0u, verifier_
.inflight_joins());
190 ASSERT_EQ(2u, verifier_
.GetCacheSize());
193 // Tests an inflight join.
194 TEST_F(MultiThreadedCertVerifierTest
, InflightJoin
) {
195 base::FilePath certs_dir
= GetTestCertsDirectory();
196 scoped_refptr
<X509Certificate
> test_cert(
197 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
198 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
.get());
201 CertVerifyResult verify_result
;
202 TestCompletionCallback callback
;
203 CertVerifier::RequestHandle request_handle
;
204 CertVerifyResult verify_result2
;
205 TestCompletionCallback callback2
;
206 CertVerifier::RequestHandle request_handle2
;
208 error
= verifier_
.Verify(test_cert
.get(),
216 ASSERT_EQ(ERR_IO_PENDING
, error
);
217 EXPECT_TRUE(request_handle
);
218 error
= verifier_
.Verify(test_cert
.get(),
223 callback2
.callback(),
226 EXPECT_EQ(ERR_IO_PENDING
, error
);
227 EXPECT_TRUE(request_handle2
!= NULL
);
228 error
= callback
.WaitForResult();
229 EXPECT_TRUE(IsCertificateError(error
));
230 error
= callback2
.WaitForResult();
231 ASSERT_TRUE(IsCertificateError(error
));
232 ASSERT_EQ(2u, verifier_
.requests());
233 ASSERT_EQ(0u, verifier_
.cache_hits());
234 ASSERT_EQ(1u, verifier_
.inflight_joins());
237 // Tests that the callback of a canceled request is never made.
238 TEST_F(MultiThreadedCertVerifierTest
, CancelRequest
) {
239 base::FilePath certs_dir
= GetTestCertsDirectory();
240 scoped_refptr
<X509Certificate
> test_cert(
241 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
242 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
.get());
245 CertVerifyResult verify_result
;
246 CertVerifier::RequestHandle request_handle
;
248 error
= verifier_
.Verify(test_cert
.get(),
253 base::Bind(&FailTest
),
256 ASSERT_EQ(ERR_IO_PENDING
, error
);
257 ASSERT_TRUE(request_handle
!= NULL
);
258 verifier_
.CancelRequest(request_handle
);
260 // Issue a few more requests to the worker pool and wait for their
261 // completion, so that the task of the canceled request (which runs on a
262 // worker thread) is likely to complete by the end of this test.
263 TestCompletionCallback callback
;
264 for (int i
= 0; i
< 5; ++i
) {
265 error
= verifier_
.Verify(test_cert
.get(),
273 ASSERT_EQ(ERR_IO_PENDING
, error
);
274 EXPECT_TRUE(request_handle
);
275 error
= callback
.WaitForResult();
276 verifier_
.ClearCache();
280 // Tests that a canceled request is not leaked.
281 TEST_F(MultiThreadedCertVerifierTest
, CancelRequestThenQuit
) {
282 base::FilePath certs_dir
= GetTestCertsDirectory();
283 scoped_refptr
<X509Certificate
> test_cert(
284 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
285 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
.get());
288 CertVerifyResult verify_result
;
289 TestCompletionCallback callback
;
290 CertVerifier::RequestHandle request_handle
;
293 // Because shutdown intentionally doesn't join worker threads, a
294 // CertVerifyWorker may be leaked if the main thread shuts down before the
296 ANNOTATE_SCOPED_MEMORY_LEAK
;
297 error
= verifier_
.Verify(test_cert
.get(), "www.example.com", 0, NULL
,
298 &verify_result
, callback
.callback(),
299 &request_handle
, BoundNetLog());
301 ASSERT_EQ(ERR_IO_PENDING
, error
);
302 EXPECT_TRUE(request_handle
);
303 verifier_
.CancelRequest(request_handle
);
304 // Destroy |verifier| by going out of scope.
307 TEST_F(MultiThreadedCertVerifierTest
, RequestParamsComparators
) {
309 memset(a_key
.data
, 'a', sizeof(a_key
.data
));
312 memset(z_key
.data
, 'z', sizeof(z_key
.data
));
314 const CertificateList empty_list
;
315 CertificateList test_list
;
317 ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
321 MultiThreadedCertVerifier::RequestParams key1
;
322 MultiThreadedCertVerifier::RequestParams key2
;
325 // -1 means key1 is less than key2
326 // 0 means key1 equals key2
327 // 1 means key1 is greater than key2
330 { // Test for basic equivalence.
331 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
333 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
337 { // Test that different certificates but with the same CA and for
338 // the same host are different validation keys.
339 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
341 MultiThreadedCertVerifier::RequestParams(z_key
, a_key
, "www.example.test",
345 { // Test that the same EE certificate for the same host, but with
346 // different chains are different validation keys.
347 MultiThreadedCertVerifier::RequestParams(a_key
, z_key
, "www.example.test",
349 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
353 { // The same certificate, with the same chain, but for different
354 // hosts are different validation keys.
355 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
,
356 "www1.example.test", 0,
358 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
,
359 "www2.example.test", 0,
363 { // The same certificate, chain, and host, but with different flags
364 // are different validation keys.
365 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
366 CertVerifier::VERIFY_EV_CERT
,
368 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
372 { // Different additional_trust_anchors.
373 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
375 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
380 for (size_t i
= 0; i
< arraysize(tests
); ++i
) {
381 SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS
"]", i
));
383 const MultiThreadedCertVerifier::RequestParams
& key1
= tests
[i
].key1
;
384 const MultiThreadedCertVerifier::RequestParams
& key2
= tests
[i
].key2
;
386 switch (tests
[i
].expected_result
) {
388 EXPECT_TRUE(key1
< key2
);
389 EXPECT_FALSE(key2
< key1
);
392 EXPECT_FALSE(key1
< key2
);
393 EXPECT_FALSE(key2
< key1
);
396 EXPECT_FALSE(key1
< key2
);
397 EXPECT_TRUE(key2
< key1
);
400 FAIL() << "Invalid expectation. Can be only -1, 0, 1";
405 TEST_F(MultiThreadedCertVerifierTest
, CertTrustAnchorProvider
) {
406 MockCertTrustAnchorProvider trust_provider
;
407 verifier_
.SetCertTrustAnchorProvider(&trust_provider
);
409 scoped_refptr
<X509Certificate
> test_cert(
410 ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
411 ASSERT_TRUE(test_cert
.get());
413 const CertificateList empty_cert_list
;
414 CertificateList cert_list
;
415 cert_list
.push_back(test_cert
);
417 // Check that Verify() asks the |trust_provider| for the current list of
418 // additional trust anchors.
420 CertVerifyResult verify_result
;
421 TestCompletionCallback callback
;
422 CertVerifier::RequestHandle request_handle
;
423 EXPECT_CALL(trust_provider
, GetAdditionalTrustAnchors())
424 .WillOnce(ReturnRef(empty_cert_list
));
425 error
= verifier_
.Verify(test_cert
.get(),
433 Mock::VerifyAndClearExpectations(&trust_provider
);
434 ASSERT_EQ(ERR_IO_PENDING
, error
);
435 EXPECT_TRUE(request_handle
);
436 error
= callback
.WaitForResult();
437 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID
, error
);
438 ASSERT_EQ(1u, verifier_
.requests());
439 ASSERT_EQ(0u, verifier_
.cache_hits());
441 // The next Verify() uses the cached result.
442 EXPECT_CALL(trust_provider
, GetAdditionalTrustAnchors())
443 .WillOnce(ReturnRef(empty_cert_list
));
444 error
= verifier_
.Verify(test_cert
.get(),
452 Mock::VerifyAndClearExpectations(&trust_provider
);
453 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID
, error
);
454 EXPECT_FALSE(request_handle
);
455 ASSERT_EQ(2u, verifier_
.requests());
456 ASSERT_EQ(1u, verifier_
.cache_hits());
458 // Another Verify() for the same certificate but with a different list of
459 // trust anchors will not reuse the cache.
460 EXPECT_CALL(trust_provider
, GetAdditionalTrustAnchors())
461 .WillOnce(ReturnRef(cert_list
));
462 error
= verifier_
.Verify(test_cert
.get(),
470 Mock::VerifyAndClearExpectations(&trust_provider
);
471 ASSERT_EQ(ERR_IO_PENDING
, error
);
472 EXPECT_TRUE(request_handle
);
473 error
= callback
.WaitForResult();
474 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID
, error
);
475 ASSERT_EQ(3u, verifier_
.requests());
476 ASSERT_EQ(1u, verifier_
.cache_hits());