1// Copyright (c) 2010 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/base/cert_verifier.h"
6
7#include "base/callback.h"
8#include "base/file_path.h"
9#include "base/stringprintf.h"
10#include "net/base/cert_test_util.h"
11#include "net/base/net_errors.h"
12#include "net/base/test_completion_callback.h"
13#include "net/base/x509_certificate.h"
14#include "testing/gtest/include/gtest/gtest.h"
15
16namespace net {
17
18class TestTimeService : public CertVerifier::TimeService {
19 public:
20  // CertVerifier::TimeService methods:
21  virtual base::Time Now() { return current_time_; }
22
23  void set_current_time(base::Time now) { current_time_ = now; }
24
25 private:
26  base::Time current_time_;
27};
28
29class CertVerifierTest : public testing::Test {
30};
31
32class ExplodingCallback : public CallbackRunner<Tuple1<int> > {
33 public:
34  virtual void RunWithParams(const Tuple1<int>& params) {
35    FAIL();
36  }
37};
38
39// Tests a cache hit, which should results in synchronous completion.
40TEST_F(CertVerifierTest, CacheHit) {
41  TestTimeService* time_service = new TestTimeService;
42  base::Time current_time = base::Time::Now();
43  time_service->set_current_time(current_time);
44  CertVerifier verifier(time_service);
45
46  FilePath certs_dir = GetTestCertsDirectory();
47  scoped_refptr<X509Certificate> google_cert(
48      ImportCertFromFile(certs_dir, "google.single.der"));
49  ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert);
50
51  int error;
52  CertVerifyResult verify_result;
53  TestCompletionCallback callback;
54  CertVerifier::RequestHandle request_handle;
55
56  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result,
57                          &callback, &request_handle);
58  ASSERT_EQ(ERR_IO_PENDING, error);
59  ASSERT_TRUE(request_handle != NULL);
60  error = callback.WaitForResult();
61  ASSERT_TRUE(IsCertificateError(error));
62  ASSERT_EQ(1u, verifier.requests());
63  ASSERT_EQ(0u, verifier.cache_hits());
64  ASSERT_EQ(0u, verifier.inflight_joins());
65
66  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result,
67                          &callback, &request_handle);
68  // Synchronous completion.
69  ASSERT_NE(ERR_IO_PENDING, error);
70  ASSERT_TRUE(IsCertificateError(error));
71  ASSERT_TRUE(request_handle == NULL);
72  ASSERT_EQ(2u, verifier.requests());
73  ASSERT_EQ(1u, verifier.cache_hits());
74  ASSERT_EQ(0u, verifier.inflight_joins());
75}
76
77// Tests an inflight join.
78TEST_F(CertVerifierTest, InflightJoin) {
79  TestTimeService* time_service = new TestTimeService;
80  base::Time current_time = base::Time::Now();
81  time_service->set_current_time(current_time);
82  CertVerifier verifier(time_service);
83
84  FilePath certs_dir = GetTestCertsDirectory();
85  scoped_refptr<X509Certificate> google_cert(
86      ImportCertFromFile(certs_dir, "google.single.der"));
87  ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert);
88
89  int error;
90  CertVerifyResult verify_result;
91  TestCompletionCallback callback;
92  CertVerifier::RequestHandle request_handle;
93  CertVerifyResult verify_result2;
94  TestCompletionCallback callback2;
95  CertVerifier::RequestHandle request_handle2;
96
97  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result,
98                          &callback, &request_handle);
99  ASSERT_EQ(ERR_IO_PENDING, error);
100  ASSERT_TRUE(request_handle != NULL);
101  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result2,
102                          &callback2, &request_handle2);
103  ASSERT_EQ(ERR_IO_PENDING, error);
104  ASSERT_TRUE(request_handle2 != NULL);
105  error = callback.WaitForResult();
106  ASSERT_TRUE(IsCertificateError(error));
107  error = callback2.WaitForResult();
108  ASSERT_TRUE(IsCertificateError(error));
109  ASSERT_EQ(2u, verifier.requests());
110  ASSERT_EQ(0u, verifier.cache_hits());
111  ASSERT_EQ(1u, verifier.inflight_joins());
112}
113
114// Tests cache entry expiration.
115TEST_F(CertVerifierTest, ExpiredCacheEntry) {
116  TestTimeService* time_service = new TestTimeService;
117  base::Time current_time = base::Time::Now();
118  time_service->set_current_time(current_time);
119  CertVerifier verifier(time_service);
120
121  FilePath certs_dir = GetTestCertsDirectory();
122  scoped_refptr<X509Certificate> google_cert(
123      ImportCertFromFile(certs_dir, "google.single.der"));
124  ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert);
125
126  int error;
127  CertVerifyResult verify_result;
128  TestCompletionCallback callback;
129  CertVerifier::RequestHandle request_handle;
130
131  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result,
132                          &callback, &request_handle);
133  ASSERT_EQ(ERR_IO_PENDING, error);
134  ASSERT_TRUE(request_handle != NULL);
135  error = callback.WaitForResult();
136  ASSERT_TRUE(IsCertificateError(error));
137  ASSERT_EQ(1u, verifier.requests());
138  ASSERT_EQ(0u, verifier.cache_hits());
139  ASSERT_EQ(0u, verifier.inflight_joins());
140
141  // Before expiration, should have a cache hit.
142  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result,
143                          &callback, &request_handle);
144  // Synchronous completion.
145  ASSERT_NE(ERR_IO_PENDING, error);
146  ASSERT_TRUE(IsCertificateError(error));
147  ASSERT_TRUE(request_handle == NULL);
148  ASSERT_EQ(2u, verifier.requests());
149  ASSERT_EQ(1u, verifier.cache_hits());
150  ASSERT_EQ(0u, verifier.inflight_joins());
151
152  // After expiration, should not have a cache hit.
153  ASSERT_EQ(1u, verifier.GetCacheSize());
154  current_time += base::TimeDelta::FromMinutes(60);
155  time_service->set_current_time(current_time);
156  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result,
157                          &callback, &request_handle);
158  ASSERT_EQ(ERR_IO_PENDING, error);
159  ASSERT_TRUE(request_handle != NULL);
160  ASSERT_EQ(0u, verifier.GetCacheSize());
161  error = callback.WaitForResult();
162  ASSERT_TRUE(IsCertificateError(error));
163  ASSERT_EQ(3u, verifier.requests());
164  ASSERT_EQ(1u, verifier.cache_hits());
165  ASSERT_EQ(0u, verifier.inflight_joins());
166}
167
168// Tests a full cache.
169TEST_F(CertVerifierTest, FullCache) {
170  TestTimeService* time_service = new TestTimeService;
171  base::Time current_time = base::Time::Now();
172  time_service->set_current_time(current_time);
173  CertVerifier verifier(time_service);
174
175  FilePath certs_dir = GetTestCertsDirectory();
176  scoped_refptr<X509Certificate> google_cert(
177      ImportCertFromFile(certs_dir, "google.single.der"));
178  ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert);
179
180  int error;
181  CertVerifyResult verify_result;
182  TestCompletionCallback callback;
183  CertVerifier::RequestHandle request_handle;
184
185  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result,
186                          &callback, &request_handle);
187  ASSERT_EQ(ERR_IO_PENDING, error);
188  ASSERT_TRUE(request_handle != NULL);
189  error = callback.WaitForResult();
190  ASSERT_TRUE(IsCertificateError(error));
191  ASSERT_EQ(1u, verifier.requests());
192  ASSERT_EQ(0u, verifier.cache_hits());
193  ASSERT_EQ(0u, verifier.inflight_joins());
194
195  const unsigned kCacheSize = 256;
196
197  for (unsigned i = 0; i < kCacheSize; i++) {
198    std::string hostname = base::StringPrintf("www%d.example.com", i + 1);
199    error = verifier.Verify(google_cert, hostname, 0, &verify_result,
200                            &callback, &request_handle);
201    ASSERT_EQ(ERR_IO_PENDING, error);
202    ASSERT_TRUE(request_handle != NULL);
203    error = callback.WaitForResult();
204    ASSERT_TRUE(IsCertificateError(error));
205  }
206  ASSERT_EQ(kCacheSize + 1, verifier.requests());
207  ASSERT_EQ(0u, verifier.cache_hits());
208  ASSERT_EQ(0u, verifier.inflight_joins());
209
210  ASSERT_EQ(kCacheSize, verifier.GetCacheSize());
211  current_time += base::TimeDelta::FromMinutes(60);
212  time_service->set_current_time(current_time);
213  error = verifier.Verify(google_cert, "www999.example.com", 0, &verify_result,
214                          &callback, &request_handle);
215  ASSERT_EQ(ERR_IO_PENDING, error);
216  ASSERT_TRUE(request_handle != NULL);
217  ASSERT_EQ(kCacheSize, verifier.GetCacheSize());
218  error = callback.WaitForResult();
219  ASSERT_EQ(1u, verifier.GetCacheSize());
220  ASSERT_TRUE(IsCertificateError(error));
221  ASSERT_EQ(kCacheSize + 2, verifier.requests());
222  ASSERT_EQ(0u, verifier.cache_hits());
223  ASSERT_EQ(0u, verifier.inflight_joins());
224}
225
226// Tests that the callback of a canceled request is never made.
227TEST_F(CertVerifierTest, CancelRequest) {
228  CertVerifier verifier;
229
230  FilePath certs_dir = GetTestCertsDirectory();
231  scoped_refptr<X509Certificate> google_cert(
232      ImportCertFromFile(certs_dir, "google.single.der"));
233  ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert);
234
235  int error;
236  CertVerifyResult verify_result;
237  ExplodingCallback exploding_callback;
238  CertVerifier::RequestHandle request_handle;
239
240  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result,
241                          &exploding_callback, &request_handle);
242  ASSERT_EQ(ERR_IO_PENDING, error);
243  ASSERT_TRUE(request_handle != NULL);
244  verifier.CancelRequest(request_handle);
245
246  // Issue a few more requests to the worker pool and wait for their
247  // completion, so that the task of the canceled request (which runs on a
248  // worker thread) is likely to complete by the end of this test.
249  TestCompletionCallback callback;
250  for (int i = 0; i < 5; ++i) {
251    error = verifier.Verify(google_cert, "www2.example.com", 0, &verify_result,
252                            &callback, &request_handle);
253    ASSERT_EQ(ERR_IO_PENDING, error);
254    ASSERT_TRUE(request_handle != NULL);
255    error = callback.WaitForResult();
256    verifier.ClearCache();
257  }
258}
259
260// Tests that a canceled request is not leaked.
261TEST_F(CertVerifierTest, CancelRequestThenQuit) {
262  CertVerifier verifier;
263
264  FilePath certs_dir = GetTestCertsDirectory();
265  scoped_refptr<X509Certificate> google_cert(
266      ImportCertFromFile(certs_dir, "google.single.der"));
267  ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert);
268
269  int error;
270  CertVerifyResult verify_result;
271  TestCompletionCallback callback;
272  CertVerifier::RequestHandle request_handle;
273
274  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result,
275                          &callback, &request_handle);
276  ASSERT_EQ(ERR_IO_PENDING, error);
277  ASSERT_TRUE(request_handle != NULL);
278  verifier.CancelRequest(request_handle);
279  // Destroy |verifier| by going out of scope.
280}
281
282}  // namespace net
283