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.
4
5#include "net/cert/multi_threaded_cert_verifier.h"
6
7#include "base/bind.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"
22
23using testing::Mock;
24using testing::ReturnRef;
25
26namespace net {
27
28namespace {
29
30void FailTest(int /* result */) {
31  FAIL();
32}
33
34class MockCertVerifyProc : public CertVerifyProc {
35 public:
36  MockCertVerifyProc() {}
37
38 private:
39  virtual ~MockCertVerifyProc() {}
40
41  // CertVerifyProc implementation
42  virtual bool SupportsAdditionalTrustAnchors() const OVERRIDE {
43    return false;
44  }
45
46  virtual int VerifyInternal(X509Certificate* cert,
47                             const std::string& hostname,
48                             int flags,
49                             CRLSet* crl_set,
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;
56  }
57};
58
59class MockCertTrustAnchorProvider : public CertTrustAnchorProvider {
60 public:
61  MockCertTrustAnchorProvider() {}
62  virtual ~MockCertTrustAnchorProvider() {}
63
64  MOCK_METHOD0(GetAdditionalTrustAnchors, const CertificateList&());
65};
66
67}  // namespace
68
69class MultiThreadedCertVerifierTest : public ::testing::Test {
70 public:
71  MultiThreadedCertVerifierTest() : verifier_(new MockCertVerifyProc()) {}
72  virtual ~MultiThreadedCertVerifierTest() {}
73
74 protected:
75  MultiThreadedCertVerifier verifier_;
76};
77
78TEST_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);
83
84  int error;
85  CertVerifyResult verify_result;
86  TestCompletionCallback callback;
87  CertVerifier::RequestHandle request_handle;
88
89  error = verifier_.Verify(test_cert.get(),
90                           "www.example.com",
91                           0,
92                           NULL,
93                           &verify_result,
94                           callback.callback(),
95                           &request_handle,
96                           BoundNetLog());
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());
105
106  error = verifier_.Verify(test_cert.get(),
107                           "www.example.com",
108                           0,
109                           NULL,
110                           &verify_result,
111                           callback.callback(),
112                           &request_handle,
113                           BoundNetLog());
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());
122}
123
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.
127TEST_F(MultiThreadedCertVerifierTest, DifferentCACerts) {
128  base::FilePath certs_dir = GetTestCertsDirectory();
129
130  scoped_refptr<X509Certificate> server_cert =
131      ImportCertFromFile(certs_dir, "salesforce_com_test.pem");
132  ASSERT_NE(static_cast<X509Certificate*>(NULL), server_cert);
133
134  scoped_refptr<X509Certificate> intermediate_cert1 =
135      ImportCertFromFile(certs_dir, "verisign_intermediate_ca_2011.pem");
136  ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate_cert1);
137
138  scoped_refptr<X509Certificate> intermediate_cert2 =
139      ImportCertFromFile(certs_dir, "verisign_intermediate_ca_2016.pem");
140  ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate_cert2);
141
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(),
146                                        intermediates);
147
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(),
152                                        intermediates);
153
154  int error;
155  CertVerifyResult verify_result;
156  TestCompletionCallback callback;
157  CertVerifier::RequestHandle request_handle;
158
159  error = verifier_.Verify(cert_chain1.get(),
160                           "www.example.com",
161                           0,
162                           NULL,
163                           &verify_result,
164                           callback.callback(),
165                           &request_handle,
166                           BoundNetLog());
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());
175
176  error = verifier_.Verify(cert_chain2.get(),
177                           "www.example.com",
178                           0,
179                           NULL,
180                           &verify_result,
181                           callback.callback(),
182                           &request_handle,
183                           BoundNetLog());
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());
192}
193
194// Tests an inflight join.
195TEST_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);
200
201  int error;
202  CertVerifyResult verify_result;
203  TestCompletionCallback callback;
204  CertVerifier::RequestHandle request_handle;
205  CertVerifyResult verify_result2;
206  TestCompletionCallback callback2;
207  CertVerifier::RequestHandle request_handle2;
208
209  error = verifier_.Verify(test_cert.get(),
210                           "www.example.com",
211                           0,
212                           NULL,
213                           &verify_result,
214                           callback.callback(),
215                           &request_handle,
216                           BoundNetLog());
217  ASSERT_EQ(ERR_IO_PENDING, error);
218  EXPECT_TRUE(request_handle);
219  error = verifier_.Verify(test_cert.get(),
220                           "www.example.com",
221                           0,
222                           NULL,
223                           &verify_result2,
224                           callback2.callback(),
225                           &request_handle2,
226                           BoundNetLog());
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());
236}
237
238// Tests that the callback of a canceled request is never made.
239TEST_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);
244
245  int error;
246  CertVerifyResult verify_result;
247  CertVerifier::RequestHandle request_handle;
248
249  error = verifier_.Verify(test_cert.get(),
250                           "www.example.com",
251                           0,
252                           NULL,
253                           &verify_result,
254                           base::Bind(&FailTest),
255                           &request_handle,
256                           BoundNetLog());
257  ASSERT_EQ(ERR_IO_PENDING, error);
258  ASSERT_TRUE(request_handle != NULL);
259  verifier_.CancelRequest(request_handle);
260
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(),
267                             "www2.example.com",
268                             0,
269                             NULL,
270                             &verify_result,
271                             callback.callback(),
272                             &request_handle,
273                             BoundNetLog());
274    ASSERT_EQ(ERR_IO_PENDING, error);
275    EXPECT_TRUE(request_handle);
276    error = callback.WaitForResult();
277    verifier_.ClearCache();
278  }
279}
280
281// Tests that a canceled request is not leaked.
282TEST_F(MultiThreadedCertVerifierTest, CancelRequestThenQuit) {
283  base::FilePath certs_dir = GetTestCertsDirectory();
284  scoped_refptr<X509Certificate> test_cert(
285      ImportCertFromFile(certs_dir, "ok_cert.pem"));
286  ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert);
287
288  int error;
289  CertVerifyResult verify_result;
290  TestCompletionCallback callback;
291  CertVerifier::RequestHandle request_handle;
292
293  error = verifier_.Verify(test_cert.get(),
294                           "www.example.com",
295                           0,
296                           NULL,
297                           &verify_result,
298                           callback.callback(),
299                           &request_handle,
300                           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.
305}
306
307TEST_F(MultiThreadedCertVerifierTest, RequestParamsComparators) {
308  SHA1HashValue a_key;
309  memset(a_key.data, 'a', sizeof(a_key.data));
310
311  SHA1HashValue z_key;
312  memset(z_key.data, 'z', sizeof(z_key.data));
313
314  const CertificateList empty_list;
315  CertificateList test_list;
316  test_list.push_back(
317      ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
318
319  struct {
320    // Keys to test
321    MultiThreadedCertVerifier::RequestParams key1;
322    MultiThreadedCertVerifier::RequestParams key2;
323
324    // Expectation:
325    // -1 means key1 is less than key2
326    //  0 means key1 equals key2
327    //  1 means key1 is greater than key2
328    int expected_result;
329  } tests[] = {
330    {  // Test for basic equivalence.
331      MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test",
332                                               0, test_list),
333      MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test",
334                                               0, test_list),
335      0,
336    },
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",
340                                               0, test_list),
341      MultiThreadedCertVerifier::RequestParams(z_key, a_key, "www.example.test",
342                                               0, test_list),
343      -1,
344    },
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",
348                                               0, test_list),
349      MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test",
350                                               0, test_list),
351      1,
352    },
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,
357                                               test_list),
358      MultiThreadedCertVerifier::RequestParams(a_key, a_key,
359                                               "www2.example.test", 0,
360                                               test_list),
361      -1,
362    },
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,
367                                               test_list),
368      MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test",
369                                               0, test_list),
370      1,
371    },
372    {  // Different additional_trust_anchors.
373      MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test",
374                                               0, empty_list),
375      MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test",
376                                               0, test_list),
377      -1,
378    },
379  };
380  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
381    SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]", i));
382
383    const MultiThreadedCertVerifier::RequestParams& key1 = tests[i].key1;
384    const MultiThreadedCertVerifier::RequestParams& key2 = tests[i].key2;
385
386    switch (tests[i].expected_result) {
387      case -1:
388        EXPECT_TRUE(key1 < key2);
389        EXPECT_FALSE(key2 < key1);
390        break;
391      case 0:
392        EXPECT_FALSE(key1 < key2);
393        EXPECT_FALSE(key2 < key1);
394        break;
395      case 1:
396        EXPECT_FALSE(key1 < key2);
397        EXPECT_TRUE(key2 < key1);
398        break;
399      default:
400        FAIL() << "Invalid expectation. Can be only -1, 0, 1";
401    }
402  }
403}
404
405TEST_F(MultiThreadedCertVerifierTest, CertTrustAnchorProvider) {
406  MockCertTrustAnchorProvider trust_provider;
407  verifier_.SetCertTrustAnchorProvider(&trust_provider);
408
409  scoped_refptr<X509Certificate> test_cert(
410      ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
411  ASSERT_TRUE(test_cert.get());
412
413  const CertificateList empty_cert_list;
414  CertificateList cert_list;
415  cert_list.push_back(test_cert);
416
417  // Check that Verify() asks the |trust_provider| for the current list of
418  // additional trust anchors.
419  int error;
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(),
426                           "www.example.com",
427                           0,
428                           NULL,
429                           &verify_result,
430                           callback.callback(),
431                           &request_handle,
432                           BoundNetLog());
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());
440
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(),
445                           "www.example.com",
446                           0,
447                           NULL,
448                           &verify_result,
449                           callback.callback(),
450                           &request_handle,
451                           BoundNetLog());
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());
457
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(),
463                           "www.example.com",
464                           0,
465                           NULL,
466                           &verify_result,
467                           callback.callback(),
468                           &request_handle,
469                           BoundNetLog());
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());
477}
478
479}  // namespace net
480