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/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
);
85 CertVerifyResult verify_result
;
86 TestCompletionCallback callback
;
87 CertVerifier::RequestHandle request_handle
;
89 error
= verifier_
.Verify(test_cert
, "www.example.com", 0, NULL
,
90 &verify_result
, callback
.callback(),
91 &request_handle
, BoundNetLog());
92 ASSERT_EQ(ERR_IO_PENDING
, error
);
93 ASSERT_TRUE(request_handle
!= NULL
);
94 error
= callback
.WaitForResult();
95 ASSERT_TRUE(IsCertificateError(error
));
96 ASSERT_EQ(1u, verifier_
.requests());
97 ASSERT_EQ(0u, verifier_
.cache_hits());
98 ASSERT_EQ(0u, verifier_
.inflight_joins());
99 ASSERT_EQ(1u, verifier_
.GetCacheSize());
101 error
= verifier_
.Verify(test_cert
, "www.example.com", 0, NULL
,
102 &verify_result
, callback
.callback(),
103 &request_handle
, BoundNetLog());
104 // Synchronous completion.
105 ASSERT_NE(ERR_IO_PENDING
, error
);
106 ASSERT_TRUE(IsCertificateError(error
));
107 ASSERT_TRUE(request_handle
== NULL
);
108 ASSERT_EQ(2u, verifier_
.requests());
109 ASSERT_EQ(1u, verifier_
.cache_hits());
110 ASSERT_EQ(0u, verifier_
.inflight_joins());
111 ASSERT_EQ(1u, verifier_
.GetCacheSize());
114 // Tests the same server certificate with different intermediate CA
115 // certificates. These should be treated as different certificate chains even
116 // though the two X509Certificate objects contain the same server certificate.
117 TEST_F(MultiThreadedCertVerifierTest
, DifferentCACerts
) {
118 base::FilePath certs_dir
= GetTestCertsDirectory();
120 scoped_refptr
<X509Certificate
> server_cert
=
121 ImportCertFromFile(certs_dir
, "salesforce_com_test.pem");
122 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), server_cert
);
124 scoped_refptr
<X509Certificate
> intermediate_cert1
=
125 ImportCertFromFile(certs_dir
, "verisign_intermediate_ca_2011.pem");
126 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), intermediate_cert1
);
128 scoped_refptr
<X509Certificate
> intermediate_cert2
=
129 ImportCertFromFile(certs_dir
, "verisign_intermediate_ca_2016.pem");
130 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), intermediate_cert2
);
132 X509Certificate::OSCertHandles intermediates
;
133 intermediates
.push_back(intermediate_cert1
->os_cert_handle());
134 scoped_refptr
<X509Certificate
> cert_chain1
=
135 X509Certificate::CreateFromHandle(server_cert
->os_cert_handle(),
138 intermediates
.clear();
139 intermediates
.push_back(intermediate_cert2
->os_cert_handle());
140 scoped_refptr
<X509Certificate
> cert_chain2
=
141 X509Certificate::CreateFromHandle(server_cert
->os_cert_handle(),
145 CertVerifyResult verify_result
;
146 TestCompletionCallback callback
;
147 CertVerifier::RequestHandle request_handle
;
149 error
= verifier_
.Verify(cert_chain1
, "www.example.com", 0, NULL
,
150 &verify_result
, callback
.callback(),
151 &request_handle
, BoundNetLog());
152 ASSERT_EQ(ERR_IO_PENDING
, error
);
153 ASSERT_TRUE(request_handle
!= NULL
);
154 error
= callback
.WaitForResult();
155 ASSERT_TRUE(IsCertificateError(error
));
156 ASSERT_EQ(1u, verifier_
.requests());
157 ASSERT_EQ(0u, verifier_
.cache_hits());
158 ASSERT_EQ(0u, verifier_
.inflight_joins());
159 ASSERT_EQ(1u, verifier_
.GetCacheSize());
161 error
= verifier_
.Verify(cert_chain2
, "www.example.com", 0, NULL
,
162 &verify_result
, callback
.callback(),
163 &request_handle
, BoundNetLog());
164 ASSERT_EQ(ERR_IO_PENDING
, error
);
165 ASSERT_TRUE(request_handle
!= NULL
);
166 error
= callback
.WaitForResult();
167 ASSERT_TRUE(IsCertificateError(error
));
168 ASSERT_EQ(2u, verifier_
.requests());
169 ASSERT_EQ(0u, verifier_
.cache_hits());
170 ASSERT_EQ(0u, verifier_
.inflight_joins());
171 ASSERT_EQ(2u, verifier_
.GetCacheSize());
174 // Tests an inflight join.
175 TEST_F(MultiThreadedCertVerifierTest
, InflightJoin
) {
176 base::FilePath certs_dir
= GetTestCertsDirectory();
177 scoped_refptr
<X509Certificate
> test_cert(
178 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
179 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
);
182 CertVerifyResult verify_result
;
183 TestCompletionCallback callback
;
184 CertVerifier::RequestHandle request_handle
;
185 CertVerifyResult verify_result2
;
186 TestCompletionCallback callback2
;
187 CertVerifier::RequestHandle request_handle2
;
189 error
= verifier_
.Verify(test_cert
, "www.example.com", 0, NULL
,
190 &verify_result
, callback
.callback(),
191 &request_handle
, BoundNetLog());
192 ASSERT_EQ(ERR_IO_PENDING
, error
);
193 ASSERT_TRUE(request_handle
!= NULL
);
194 error
= verifier_
.Verify(
195 test_cert
, "www.example.com", 0, NULL
, &verify_result2
,
196 callback2
.callback(), &request_handle2
, BoundNetLog());
197 ASSERT_EQ(ERR_IO_PENDING
, error
);
198 ASSERT_TRUE(request_handle2
!= NULL
);
199 error
= callback
.WaitForResult();
200 ASSERT_TRUE(IsCertificateError(error
));
201 error
= callback2
.WaitForResult();
202 ASSERT_TRUE(IsCertificateError(error
));
203 ASSERT_EQ(2u, verifier_
.requests());
204 ASSERT_EQ(0u, verifier_
.cache_hits());
205 ASSERT_EQ(1u, verifier_
.inflight_joins());
208 // Tests that the callback of a canceled request is never made.
209 TEST_F(MultiThreadedCertVerifierTest
, CancelRequest
) {
210 base::FilePath certs_dir
= GetTestCertsDirectory();
211 scoped_refptr
<X509Certificate
> test_cert(
212 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
213 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
);
216 CertVerifyResult verify_result
;
217 CertVerifier::RequestHandle request_handle
;
219 error
= verifier_
.Verify(
220 test_cert
, "www.example.com", 0, NULL
, &verify_result
,
221 base::Bind(&FailTest
), &request_handle
, BoundNetLog());
222 ASSERT_EQ(ERR_IO_PENDING
, error
);
223 ASSERT_TRUE(request_handle
!= NULL
);
224 verifier_
.CancelRequest(request_handle
);
226 // Issue a few more requests to the worker pool and wait for their
227 // completion, so that the task of the canceled request (which runs on a
228 // worker thread) is likely to complete by the end of this test.
229 TestCompletionCallback callback
;
230 for (int i
= 0; i
< 5; ++i
) {
231 error
= verifier_
.Verify(
232 test_cert
, "www2.example.com", 0, NULL
, &verify_result
,
233 callback
.callback(), &request_handle
, BoundNetLog());
234 ASSERT_EQ(ERR_IO_PENDING
, error
);
235 ASSERT_TRUE(request_handle
!= NULL
);
236 error
= callback
.WaitForResult();
237 verifier_
.ClearCache();
241 // Tests that a canceled request is not leaked.
242 TEST_F(MultiThreadedCertVerifierTest
, CancelRequestThenQuit
) {
243 base::FilePath certs_dir
= GetTestCertsDirectory();
244 scoped_refptr
<X509Certificate
> test_cert(
245 ImportCertFromFile(certs_dir
, "ok_cert.pem"));
246 ASSERT_NE(static_cast<X509Certificate
*>(NULL
), test_cert
);
249 CertVerifyResult verify_result
;
250 TestCompletionCallback callback
;
251 CertVerifier::RequestHandle request_handle
;
253 error
= verifier_
.Verify(test_cert
, "www.example.com", 0, NULL
,
254 &verify_result
, callback
.callback(),
255 &request_handle
, BoundNetLog());
256 ASSERT_EQ(ERR_IO_PENDING
, error
);
257 ASSERT_TRUE(request_handle
!= NULL
);
258 verifier_
.CancelRequest(request_handle
);
259 // Destroy |verifier| by going out of scope.
262 TEST_F(MultiThreadedCertVerifierTest
, RequestParamsComparators
) {
264 memset(a_key
.data
, 'a', sizeof(a_key
.data
));
267 memset(z_key
.data
, 'z', sizeof(z_key
.data
));
269 const CertificateList empty_list
;
270 CertificateList test_list
;
272 ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
276 MultiThreadedCertVerifier::RequestParams key1
;
277 MultiThreadedCertVerifier::RequestParams key2
;
280 // -1 means key1 is less than key2
281 // 0 means key1 equals key2
282 // 1 means key1 is greater than key2
285 { // Test for basic equivalence.
286 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
288 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
292 { // Test that different certificates but with the same CA and for
293 // the same host are different validation keys.
294 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
296 MultiThreadedCertVerifier::RequestParams(z_key
, a_key
, "www.example.test",
300 { // Test that the same EE certificate for the same host, but with
301 // different chains are different validation keys.
302 MultiThreadedCertVerifier::RequestParams(a_key
, z_key
, "www.example.test",
304 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
308 { // The same certificate, with the same chain, but for different
309 // hosts are different validation keys.
310 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
,
311 "www1.example.test", 0,
313 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
,
314 "www2.example.test", 0,
318 { // The same certificate, chain, and host, but with different flags
319 // are different validation keys.
320 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
321 CertVerifier::VERIFY_EV_CERT
,
323 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
327 { // Different additional_trust_anchors.
328 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
330 MultiThreadedCertVerifier::RequestParams(a_key
, a_key
, "www.example.test",
335 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(tests
); ++i
) {
336 SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS
"]", i
));
338 const MultiThreadedCertVerifier::RequestParams
& key1
= tests
[i
].key1
;
339 const MultiThreadedCertVerifier::RequestParams
& key2
= tests
[i
].key2
;
341 switch (tests
[i
].expected_result
) {
343 EXPECT_TRUE(key1
< key2
);
344 EXPECT_FALSE(key2
< key1
);
347 EXPECT_FALSE(key1
< key2
);
348 EXPECT_FALSE(key2
< key1
);
351 EXPECT_FALSE(key1
< key2
);
352 EXPECT_TRUE(key2
< key1
);
355 FAIL() << "Invalid expectation. Can be only -1, 0, 1";
360 TEST_F(MultiThreadedCertVerifierTest
, CertTrustAnchorProvider
) {
361 MockCertTrustAnchorProvider trust_provider
;
362 verifier_
.SetCertTrustAnchorProvider(&trust_provider
);
364 scoped_refptr
<X509Certificate
> test_cert(
365 ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
366 ASSERT_TRUE(test_cert
);
368 const CertificateList empty_cert_list
;
369 CertificateList cert_list
;
370 cert_list
.push_back(test_cert
);
372 // Check that Verify() asks the |trust_provider| for the current list of
373 // additional trust anchors.
375 CertVerifyResult verify_result
;
376 TestCompletionCallback callback
;
377 CertVerifier::RequestHandle request_handle
;
378 EXPECT_CALL(trust_provider
, GetAdditionalTrustAnchors())
379 .WillOnce(ReturnRef(empty_cert_list
));
380 error
= verifier_
.Verify(test_cert
, "www.example.com", 0, NULL
,
381 &verify_result
, callback
.callback(),
382 &request_handle
, BoundNetLog());
383 Mock::VerifyAndClearExpectations(&trust_provider
);
384 ASSERT_EQ(ERR_IO_PENDING
, error
);
385 ASSERT_TRUE(request_handle
);
386 error
= callback
.WaitForResult();
387 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID
, error
);
388 ASSERT_EQ(1u, verifier_
.requests());
389 ASSERT_EQ(0u, verifier_
.cache_hits());
391 // The next Verify() uses the cached result.
392 EXPECT_CALL(trust_provider
, GetAdditionalTrustAnchors())
393 .WillOnce(ReturnRef(empty_cert_list
));
394 error
= verifier_
.Verify(test_cert
, "www.example.com", 0, NULL
,
395 &verify_result
, callback
.callback(),
396 &request_handle
, BoundNetLog());
397 Mock::VerifyAndClearExpectations(&trust_provider
);
398 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID
, error
);
399 EXPECT_FALSE(request_handle
);
400 ASSERT_EQ(2u, verifier_
.requests());
401 ASSERT_EQ(1u, verifier_
.cache_hits());
403 // Another Verify() for the same certificate but with a different list of
404 // trust anchors will not reuse the cache.
405 EXPECT_CALL(trust_provider
, GetAdditionalTrustAnchors())
406 .WillOnce(ReturnRef(cert_list
));
407 error
= verifier_
.Verify(test_cert
, "www.example.com", 0, NULL
,
408 &verify_result
, callback
.callback(),
409 &request_handle
, BoundNetLog());
410 Mock::VerifyAndClearExpectations(&trust_provider
);
411 ASSERT_EQ(ERR_IO_PENDING
, error
);
412 ASSERT_TRUE(request_handle
!= NULL
);
413 error
= callback
.WaitForResult();
414 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID
, error
);
415 ASSERT_EQ(3u, verifier_
.requests());
416 ASSERT_EQ(1u, verifier_
.cache_hits());