1 // Copyright 2016 The Chromium Authors
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/caching_cert_verifier.h"
6
7 #include <memory>
8
9 #include "base/files/file_path.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/test/task_environment.h"
12 #include "net/base/net_errors.h"
13 #include "net/base/test_completion_callback.h"
14 #include "net/cert/cert_database.h"
15 #include "net/cert/cert_verifier.h"
16 #include "net/cert/cert_verify_result.h"
17 #include "net/cert/mock_cert_verifier.h"
18 #include "net/cert/x509_certificate.h"
19 #include "net/cert/x509_util.h"
20 #include "net/log/net_log_with_source.h"
21 #include "net/test/cert_test_util.h"
22 #include "net/test/ct_test_util.h"
23 #include "net/test/gtest_util.h"
24 #include "net/test/test_data_directory.h"
25 #include "net/test/test_with_task_environment.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28
29 using net::test::IsError;
30 using net::test::IsOk;
31
32 using testing::_;
33 using testing::Mock;
34 using testing::Return;
35 using testing::ReturnRef;
36
37 namespace net {
38
39 class CachingCertVerifierTest : public TestWithTaskEnvironment {
40 public:
CachingCertVerifierTest()41 CachingCertVerifierTest() : verifier_(std::make_unique<MockCertVerifier>()) {}
42 ~CachingCertVerifierTest() override = default;
43
44 protected:
45 CachingCertVerifier verifier_;
46 };
47
TEST_F(CachingCertVerifierTest,CacheHit)48 TEST_F(CachingCertVerifierTest, CacheHit) {
49 base::FilePath certs_dir = GetTestCertsDirectory();
50 scoped_refptr<X509Certificate> test_cert(
51 ImportCertFromFile(certs_dir, "ok_cert.pem"));
52 ASSERT_TRUE(test_cert.get());
53
54 int error;
55 CertVerifyResult verify_result;
56 TestCompletionCallback callback;
57 std::unique_ptr<CertVerifier::Request> request;
58
59 error = callback.GetResult(verifier_.Verify(
60 CertVerifier::RequestParams(test_cert, "www.example.com", 0,
61 /*ocsp_response=*/std::string(),
62 /*sct_list=*/std::string()),
63 &verify_result, callback.callback(), &request, NetLogWithSource()));
64 ASSERT_TRUE(IsCertificateError(error));
65 ASSERT_EQ(1u, verifier_.requests());
66 ASSERT_EQ(0u, verifier_.cache_hits());
67 ASSERT_EQ(1u, verifier_.GetCacheSize());
68
69 error = verifier_.Verify(
70 CertVerifier::RequestParams(test_cert, "www.example.com", 0,
71 /*ocsp_response=*/std::string(),
72 /*sct_list=*/std::string()),
73 &verify_result, callback.callback(), &request, NetLogWithSource());
74 // Synchronous completion.
75 ASSERT_NE(ERR_IO_PENDING, error);
76 ASSERT_TRUE(IsCertificateError(error));
77 ASSERT_FALSE(request);
78 ASSERT_EQ(2u, verifier_.requests());
79 ASSERT_EQ(1u, verifier_.cache_hits());
80 ASSERT_EQ(1u, verifier_.GetCacheSize());
81 }
82
TEST_F(CachingCertVerifierTest,CacheHitCTResultsCached)83 TEST_F(CachingCertVerifierTest, CacheHitCTResultsCached) {
84 base::FilePath certs_dir = GetTestCertsDirectory();
85 scoped_refptr<X509Certificate> test_cert(
86 ImportCertFromFile(certs_dir, "ok_cert.pem"));
87 ASSERT_TRUE(test_cert.get());
88
89 auto cert_verifier = std::make_unique<MockCertVerifier>();
90 // Mock the cert verification and CT verification results.
91 CertVerifyResult mock_result;
92 mock_result.cert_status = OK;
93 mock_result.verified_cert = test_cert;
94
95 scoped_refptr<ct::SignedCertificateTimestamp> sct;
96 ct::GetX509CertSCT(&sct);
97 SignedCertificateTimestampAndStatus sct_and_status(sct, ct::SCT_STATUS_OK);
98 SignedCertificateTimestampAndStatusList sct_list{sct_and_status};
99 mock_result.scts = sct_list;
100 cert_verifier->AddResultForCert(test_cert, mock_result, OK);
101
102 // We don't use verifier_ here because we needed to call AddResultForCert from
103 // the mock verifier.
104 CachingCertVerifier cache_verifier(std::move(cert_verifier));
105
106 int result;
107 CertVerifyResult verify_result;
108 TestCompletionCallback callback;
109 std::unique_ptr<CertVerifier::Request> request;
110
111 result = callback.GetResult(cache_verifier.Verify(
112 CertVerifier::RequestParams(test_cert, "www.example.com", 0,
113 /*ocsp_response=*/std::string(),
114 /*sct_list=*/std::string()),
115 &verify_result, callback.callback(), &request, NetLogWithSource()));
116 ASSERT_EQ(OK, result);
117 ASSERT_EQ(1u, verify_result.scts.size());
118 ASSERT_EQ(ct::SCT_STATUS_OK, verify_result.scts[0].status);
119 ASSERT_EQ(1u, cache_verifier.requests());
120 ASSERT_EQ(0u, cache_verifier.cache_hits());
121 ASSERT_EQ(1u, cache_verifier.GetCacheSize());
122
123 result = cache_verifier.Verify(
124 CertVerifier::RequestParams(test_cert, "www.example.com", 0,
125 /*ocsp_response=*/std::string(),
126 /*sct_list=*/std::string()),
127 &verify_result, callback.callback(), &request, NetLogWithSource());
128 // Synchronous completion.
129 ASSERT_EQ(OK, result);
130 ASSERT_FALSE(request);
131 ASSERT_EQ(1u, verify_result.scts.size());
132 ASSERT_EQ(ct::SCT_STATUS_OK, verify_result.scts[0].status);
133 ASSERT_EQ(2u, cache_verifier.requests());
134 ASSERT_EQ(1u, cache_verifier.cache_hits());
135 ASSERT_EQ(1u, cache_verifier.GetCacheSize());
136 }
137
138 // Tests the same server certificate with different intermediate CA
139 // certificates. These should be treated as different certificate chains even
140 // though the two X509Certificate objects contain the same server certificate.
TEST_F(CachingCertVerifierTest,DifferentCACerts)141 TEST_F(CachingCertVerifierTest, DifferentCACerts) {
142 base::FilePath certs_dir = GetTestCertsDirectory();
143
144 scoped_refptr<X509Certificate> server_cert =
145 ImportCertFromFile(certs_dir, "salesforce_com_test.pem");
146 ASSERT_TRUE(server_cert);
147
148 scoped_refptr<X509Certificate> intermediate_cert1 =
149 ImportCertFromFile(certs_dir, "verisign_intermediate_ca_2011.pem");
150 ASSERT_TRUE(intermediate_cert1);
151
152 scoped_refptr<X509Certificate> intermediate_cert2 =
153 ImportCertFromFile(certs_dir, "verisign_intermediate_ca_2016.pem");
154 ASSERT_TRUE(intermediate_cert2);
155
156 std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
157 intermediates.push_back(bssl::UpRef(intermediate_cert1->cert_buffer()));
158 scoped_refptr<X509Certificate> cert_chain1 =
159 X509Certificate::CreateFromBuffer(bssl::UpRef(server_cert->cert_buffer()),
160 std::move(intermediates));
161 ASSERT_TRUE(cert_chain1);
162
163 intermediates.clear();
164 intermediates.push_back(bssl::UpRef(intermediate_cert2->cert_buffer()));
165 scoped_refptr<X509Certificate> cert_chain2 =
166 X509Certificate::CreateFromBuffer(bssl::UpRef(server_cert->cert_buffer()),
167 std::move(intermediates));
168 ASSERT_TRUE(cert_chain2);
169
170 int error;
171 CertVerifyResult verify_result;
172 TestCompletionCallback callback;
173 std::unique_ptr<CertVerifier::Request> request;
174
175 error = callback.GetResult(verifier_.Verify(
176 CertVerifier::RequestParams(cert_chain1, "www.example.com", 0,
177 /*ocsp_response=*/std::string(),
178 /*sct_list=*/std::string()),
179 &verify_result, callback.callback(), &request, NetLogWithSource()));
180 ASSERT_TRUE(IsCertificateError(error));
181 ASSERT_EQ(1u, verifier_.requests());
182 ASSERT_EQ(0u, verifier_.cache_hits());
183 ASSERT_EQ(1u, verifier_.GetCacheSize());
184
185 error = callback.GetResult(verifier_.Verify(
186 CertVerifier::RequestParams(cert_chain2, "www.example.com", 0,
187 /*ocsp_response=*/std::string(),
188 /*sct_list=*/std::string()),
189 &verify_result, callback.callback(), &request, NetLogWithSource()));
190 ASSERT_TRUE(IsCertificateError(error));
191 ASSERT_EQ(2u, verifier_.requests());
192 ASSERT_EQ(0u, verifier_.cache_hits());
193 ASSERT_EQ(2u, verifier_.GetCacheSize());
194 }
195
TEST_F(CachingCertVerifierTest,ObserverIsForwarded)196 TEST_F(CachingCertVerifierTest, ObserverIsForwarded) {
197 auto mock_cert_verifier = std::make_unique<MockCertVerifier>();
198 MockCertVerifier* mock_cert_verifier_ptr = mock_cert_verifier.get();
199 CachingCertVerifier cache_verifier(std::move(mock_cert_verifier));
200
201 CertVerifierObserverCounter observer_(&cache_verifier);
202 EXPECT_EQ(observer_.change_count(), 0u);
203 // A CertVerifierChanged event on the wrapped verifier should be forwarded to
204 // observers registered on CachingCertVerifier.
205 mock_cert_verifier_ptr->SimulateOnCertVerifierChanged();
206 EXPECT_EQ(observer_.change_count(), 1u);
207 }
208
209 namespace {
210 enum class ChangeType {
211 kSetConfig,
212 kCertVerifierChanged,
213 kCertDBChanged,
214 };
215 } // namespace
216
217 class CachingCertVerifierCacheClearingTest
218 : public testing::TestWithParam<ChangeType> {
219 public:
CachingCertVerifierCacheClearingTest()220 CachingCertVerifierCacheClearingTest() {
221 auto mock_cert_verifier = std::make_unique<MockCertVerifier>();
222 mock_verifier_ = mock_cert_verifier.get();
223 verifier_ =
224 std::make_unique<CachingCertVerifier>(std::move(mock_cert_verifier));
225 }
226
change_type() const227 ChangeType change_type() const { return GetParam(); }
228
DoCacheClearingAction()229 void DoCacheClearingAction() {
230 switch (change_type()) {
231 case ChangeType::kSetConfig:
232 verifier_->SetConfig({});
233 break;
234 case ChangeType::kCertVerifierChanged:
235 mock_verifier_->SimulateOnCertVerifierChanged();
236 break;
237 case ChangeType::kCertDBChanged:
238 CertDatabase::GetInstance()->NotifyObserversTrustStoreChanged();
239 base::RunLoop().RunUntilIdle();
240 break;
241 }
242 }
243
244 protected:
245 base::test::SingleThreadTaskEnvironment task_environment_;
246 std::unique_ptr<CachingCertVerifier> verifier_;
247 raw_ptr<MockCertVerifier> mock_verifier_;
248 };
249
TEST_P(CachingCertVerifierCacheClearingTest,CacheClearedSyncVerification)250 TEST_P(CachingCertVerifierCacheClearingTest, CacheClearedSyncVerification) {
251 base::FilePath certs_dir = GetTestCertsDirectory();
252 scoped_refptr<X509Certificate> test_cert(
253 ImportCertFromFile(certs_dir, "ok_cert.pem"));
254 ASSERT_TRUE(test_cert.get());
255
256 mock_verifier_->set_async(false);
257
258 int error;
259 CertVerifyResult verify_result;
260 TestCompletionCallback callback;
261 std::unique_ptr<CertVerifier::Request> request;
262
263 error = verifier_->Verify(
264 CertVerifier::RequestParams(test_cert, "www.example.com", 0,
265 /*ocsp_response=*/std::string(),
266 /*sct_list=*/std::string()),
267 &verify_result, callback.callback(), &request, NetLogWithSource());
268 ASSERT_TRUE(IsCertificateError(error));
269 ASSERT_EQ(1u, verifier_->requests());
270 ASSERT_EQ(0u, verifier_->cache_hits());
271 ASSERT_EQ(1u, verifier_->GetCacheSize());
272
273 DoCacheClearingAction();
274 ASSERT_EQ(0u, verifier_->GetCacheSize());
275
276 error = verifier_->Verify(
277 CertVerifier::RequestParams(test_cert, "www.example.com", 0,
278 /*ocsp_response=*/std::string(),
279 /*sct_list=*/std::string()),
280 &verify_result, callback.callback(), &request, NetLogWithSource());
281 ASSERT_TRUE(IsCertificateError(error));
282 ASSERT_FALSE(request);
283 ASSERT_EQ(2u, verifier_->requests());
284 ASSERT_EQ(0u, verifier_->cache_hits());
285 ASSERT_EQ(1u, verifier_->GetCacheSize());
286 }
287
TEST_P(CachingCertVerifierCacheClearingTest,CacheClearedAsyncVerification)288 TEST_P(CachingCertVerifierCacheClearingTest, CacheClearedAsyncVerification) {
289 base::FilePath certs_dir = GetTestCertsDirectory();
290 scoped_refptr<X509Certificate> test_cert(
291 ImportCertFromFile(certs_dir, "ok_cert.pem"));
292 ASSERT_TRUE(test_cert.get());
293
294 mock_verifier_->set_async(true);
295
296 int error;
297 CertVerifyResult verify_result;
298 TestCompletionCallback callback;
299 std::unique_ptr<CertVerifier::Request> request;
300
301 error = verifier_->Verify(
302 CertVerifier::RequestParams(test_cert, "www.example.com", 0,
303 /*ocsp_response=*/std::string(),
304 /*sct_list=*/std::string()),
305 &verify_result, callback.callback(), &request, NetLogWithSource());
306 ASSERT_EQ(ERR_IO_PENDING, error);
307 ASSERT_TRUE(request);
308 ASSERT_EQ(1u, verifier_->requests());
309 ASSERT_EQ(0u, verifier_->cache_hits());
310 ASSERT_EQ(0u, verifier_->GetCacheSize());
311
312 DoCacheClearingAction();
313 ASSERT_EQ(0u, verifier_->GetCacheSize());
314
315 error = callback.WaitForResult();
316 ASSERT_TRUE(IsCertificateError(error));
317 // Async result should not have been cached since it was from a verification
318 // started before the config changed.
319 ASSERT_EQ(0u, verifier_->GetCacheSize());
320
321 error = verifier_->Verify(
322 CertVerifier::RequestParams(test_cert, "www.example.com", 0,
323 /*ocsp_response=*/std::string(),
324 /*sct_list=*/std::string()),
325 &verify_result, callback.callback(), &request, NetLogWithSource());
326 ASSERT_EQ(ERR_IO_PENDING, error);
327 ASSERT_TRUE(request);
328 ASSERT_EQ(2u, verifier_->requests());
329 ASSERT_EQ(0u, verifier_->cache_hits());
330 ASSERT_EQ(0u, verifier_->GetCacheSize());
331
332 error = callback.WaitForResult();
333 ASSERT_TRUE(IsCertificateError(error));
334 // New async result should be cached since it was from a verification started
335 // after the config changed.
336 ASSERT_EQ(1u, verifier_->GetCacheSize());
337
338 // Verify again. Result should be synchronous this time since it will get the
339 // cached result.
340 error = verifier_->Verify(
341 CertVerifier::RequestParams(test_cert, "www.example.com", 0,
342 /*ocsp_response=*/std::string(),
343 /*sct_list=*/std::string()),
344 &verify_result, callback.callback(), &request, NetLogWithSource());
345 ASSERT_TRUE(IsCertificateError(error));
346 ASSERT_FALSE(request);
347 ASSERT_EQ(3u, verifier_->requests());
348 ASSERT_EQ(1u, verifier_->cache_hits());
349 ASSERT_EQ(1u, verifier_->GetCacheSize());
350 }
351
352 INSTANTIATE_TEST_SUITE_P(All,
353 CachingCertVerifierCacheClearingTest,
354 testing::Values(ChangeType::kSetConfig,
355 ChangeType::kCertVerifierChanged,
356 ChangeType::kCertDBChanged));
357
358 } // namespace net
359