xref: /aosp_15_r20/external/cronet/net/cert/caching_cert_verifier_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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