xref: /aosp_15_r20/external/cronet/net/cert/multi_log_ct_verifier_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2013 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/multi_log_ct_verifier.h"
6 
7 #include <memory>
8 #include <string>
9 #include <string_view>
10 
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/metrics/histogram.h"
14 #include "base/metrics/histogram_samples.h"
15 #include "base/metrics/statistics_recorder.h"
16 #include "base/values.h"
17 #include "net/base/net_errors.h"
18 #include "net/cert/ct_log_verifier.h"
19 #include "net/cert/ct_serialization.h"
20 #include "net/cert/sct_status_flags.h"
21 #include "net/cert/signed_certificate_timestamp.h"
22 #include "net/cert/signed_certificate_timestamp_and_status.h"
23 #include "net/cert/x509_certificate.h"
24 #include "net/log/net_log_source_type.h"
25 #include "net/log/net_log_with_source.h"
26 #include "net/log/test_net_log.h"
27 #include "net/log/test_net_log_util.h"
28 #include "net/test/cert_test_util.h"
29 #include "net/test/ct_test_util.h"
30 #include "net/test/test_data_directory.h"
31 #include "testing/gmock/include/gmock/gmock.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33 
34 using testing::_;
35 using testing::Mock;
36 
37 namespace net {
38 
39 namespace {
40 
41 const char kLogDescription[] = "somelog";
42 
43 class MultiLogCTVerifierTest : public ::testing::Test {
44  public:
SetUp()45   void SetUp() override {
46     scoped_refptr<const CTLogVerifier> log(
47         CTLogVerifier::Create(ct::GetTestPublicKey(), kLogDescription));
48     ASSERT_TRUE(log);
49     log_verifiers_.push_back(log);
50 
51     verifier_ = std::make_unique<MultiLogCTVerifier>(log_verifiers_);
52     std::string der_test_cert(ct::GetDerEncodedX509Cert());
53     chain_ =
54         X509Certificate::CreateFromBytes(base::as_byte_span(der_test_cert));
55     ASSERT_TRUE(chain_.get());
56 
57     embedded_sct_chain_ =
58         CreateCertificateChainFromFile(GetTestCertsDirectory(),
59                                        "ct-test-embedded-cert.pem",
60                                        X509Certificate::FORMAT_AUTO);
61     ASSERT_TRUE(embedded_sct_chain_.get());
62   }
63 
CheckForEmbeddedSCTInNetLog(const RecordingNetLogObserver & net_log_observer)64   bool CheckForEmbeddedSCTInNetLog(
65       const RecordingNetLogObserver& net_log_observer) {
66     auto entries = net_log_observer.GetEntries();
67     if (entries.size() != 2)
68       return false;
69 
70     auto embedded_scts =
71         GetOptionalStringValueFromParams(entries[0], "embedded_scts");
72     if (!embedded_scts || embedded_scts->empty())
73       return false;
74 
75     const NetLogEntry& parsed = entries[1];
76     if (parsed.params.empty()) {
77       return false;
78     }
79 
80     const base::Value::List* scts = parsed.params.FindList("scts");
81     if (!scts || scts->size() != 1)
82       return false;
83 
84     const base::Value& the_sct = (*scts)[0];
85     if (!the_sct.is_dict())
86       return false;
87 
88     const std::string* origin = the_sct.GetDict().FindString("origin");
89     if (!origin || *origin != "Embedded in certificate")
90       return false;
91 
92     const std::string* verification_status =
93         the_sct.GetDict().FindString("verification_status");
94     if (!verification_status || *verification_status != "Verified")
95       return false;
96 
97     return true;
98   }
99 
100   // Returns true if |chain| is a certificate with embedded SCTs that can be
101   // successfully extracted.
VerifySinglePrecertificateChain(scoped_refptr<X509Certificate> chain)102   bool VerifySinglePrecertificateChain(scoped_refptr<X509Certificate> chain) {
103     SignedCertificateTimestampAndStatusList scts;
104     verifier_->Verify(chain.get(), std::string_view(), std::string_view(),
105                       &scts, NetLogWithSource());
106     return !scts.empty();
107   }
108 
109   // Returns true if |chain| is a certificate with a single embedded SCT that
110   // can be successfully extracted and matched to the test log indicated by
111   // |kLogDescription|.
CheckPrecertificateVerification(scoped_refptr<X509Certificate> chain)112   bool CheckPrecertificateVerification(scoped_refptr<X509Certificate> chain) {
113     SignedCertificateTimestampAndStatusList scts;
114     RecordingNetLogObserver net_log_observer(NetLogCaptureMode::kDefault);
115     NetLogWithSource net_log = NetLogWithSource::Make(
116         NetLog::Get(), NetLogSourceType::SSL_CONNECT_JOB);
117     verifier_->Verify(chain.get(), std::string_view(), std::string_view(),
118                       &scts, net_log);
119     return ct::CheckForSingleVerifiedSCTInResult(scts, kLogDescription) &&
120            ct::CheckForSCTOrigin(
121                scts, ct::SignedCertificateTimestamp::SCT_EMBEDDED) &&
122            CheckForEmbeddedSCTInNetLog(net_log_observer);
123   }
124 
125   // Histogram-related helper methods
GetValueFromHistogram(const std::string & histogram_name,int sample_index)126   int GetValueFromHistogram(const std::string& histogram_name,
127                             int sample_index) {
128     base::Histogram* histogram = static_cast<base::Histogram*>(
129         base::StatisticsRecorder::FindHistogram(histogram_name));
130 
131     if (histogram == nullptr)
132       return 0;
133 
134     std::unique_ptr<base::HistogramSamples> samples =
135         histogram->SnapshotSamples();
136     return samples->GetCount(sample_index);
137   }
138 
NumEmbeddedSCTsInHistogram()139   int NumEmbeddedSCTsInHistogram() {
140     return GetValueFromHistogram("Net.CertificateTransparency.SCTOrigin",
141                                  ct::SignedCertificateTimestamp::SCT_EMBEDDED);
142   }
143 
NumValidSCTsInStatusHistogram()144   int NumValidSCTsInStatusHistogram() {
145     return GetValueFromHistogram("Net.CertificateTransparency.SCTStatus",
146                                  ct::SCT_STATUS_OK);
147   }
148 
149  protected:
150   std::unique_ptr<MultiLogCTVerifier> verifier_;
151   scoped_refptr<X509Certificate> chain_;
152   scoped_refptr<X509Certificate> embedded_sct_chain_;
153   std::vector<scoped_refptr<const CTLogVerifier>> log_verifiers_;
154 };
155 
TEST_F(MultiLogCTVerifierTest,VerifiesEmbeddedSCT)156 TEST_F(MultiLogCTVerifierTest, VerifiesEmbeddedSCT) {
157   ASSERT_TRUE(CheckPrecertificateVerification(embedded_sct_chain_));
158 }
159 
TEST_F(MultiLogCTVerifierTest,VerifiesEmbeddedSCTWithPreCA)160 TEST_F(MultiLogCTVerifierTest, VerifiesEmbeddedSCTWithPreCA) {
161   scoped_refptr<X509Certificate> chain(
162       CreateCertificateChainFromFile(GetTestCertsDirectory(),
163                                      "ct-test-embedded-with-preca-chain.pem",
164                                      X509Certificate::FORMAT_AUTO));
165   ASSERT_TRUE(chain.get());
166   ASSERT_TRUE(CheckPrecertificateVerification(chain));
167 }
168 
TEST_F(MultiLogCTVerifierTest,VerifiesEmbeddedSCTWithIntermediate)169 TEST_F(MultiLogCTVerifierTest, VerifiesEmbeddedSCTWithIntermediate) {
170   scoped_refptr<X509Certificate> chain(CreateCertificateChainFromFile(
171       GetTestCertsDirectory(),
172       "ct-test-embedded-with-intermediate-chain.pem",
173       X509Certificate::FORMAT_AUTO));
174   ASSERT_TRUE(chain.get());
175   ASSERT_TRUE(CheckPrecertificateVerification(chain));
176 }
177 
TEST_F(MultiLogCTVerifierTest,VerifiesEmbeddedSCTWithIntermediateAndPreCA)178 TEST_F(MultiLogCTVerifierTest,
179        VerifiesEmbeddedSCTWithIntermediateAndPreCA) {
180   scoped_refptr<X509Certificate> chain(CreateCertificateChainFromFile(
181       GetTestCertsDirectory(),
182       "ct-test-embedded-with-intermediate-preca-chain.pem",
183       X509Certificate::FORMAT_AUTO));
184   ASSERT_TRUE(chain.get());
185   ASSERT_TRUE(CheckPrecertificateVerification(chain));
186 }
187 
TEST_F(MultiLogCTVerifierTest,VerifiesSCTOverX509Cert)188 TEST_F(MultiLogCTVerifierTest, VerifiesSCTOverX509Cert) {
189   std::string sct_list = ct::GetSCTListForTesting();
190 
191   SignedCertificateTimestampAndStatusList scts;
192   verifier_->Verify(chain_.get(), std::string_view(), sct_list, &scts,
193                     NetLogWithSource());
194   ASSERT_TRUE(ct::CheckForSingleVerifiedSCTInResult(scts, kLogDescription));
195   ASSERT_TRUE(ct::CheckForSCTOrigin(
196       scts, ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION));
197 }
198 
TEST_F(MultiLogCTVerifierTest,IdentifiesSCTFromUnknownLog)199 TEST_F(MultiLogCTVerifierTest, IdentifiesSCTFromUnknownLog) {
200   std::string sct_list = ct::GetSCTListWithInvalidSCT();
201   SignedCertificateTimestampAndStatusList scts;
202 
203   verifier_->Verify(chain_.get(), std::string_view(), sct_list, &scts,
204                     NetLogWithSource());
205   EXPECT_EQ(1U, scts.size());
206   EXPECT_EQ("", scts[0].sct->log_description);
207   EXPECT_EQ(ct::SCT_STATUS_LOG_UNKNOWN, scts[0].status);
208 }
209 
TEST_F(MultiLogCTVerifierTest,CountsValidSCTsInStatusHistogram)210 TEST_F(MultiLogCTVerifierTest, CountsValidSCTsInStatusHistogram) {
211   int num_valid_scts = NumValidSCTsInStatusHistogram();
212 
213   ASSERT_TRUE(VerifySinglePrecertificateChain(embedded_sct_chain_));
214 
215   EXPECT_EQ(num_valid_scts + 1, NumValidSCTsInStatusHistogram());
216 }
217 
TEST_F(MultiLogCTVerifierTest,CountsInvalidSCTsInStatusHistogram)218 TEST_F(MultiLogCTVerifierTest, CountsInvalidSCTsInStatusHistogram) {
219   std::string sct_list = ct::GetSCTListWithInvalidSCT();
220   SignedCertificateTimestampAndStatusList scts;
221 
222   int num_valid_scts = NumValidSCTsInStatusHistogram();
223   int num_invalid_scts = GetValueFromHistogram(
224       "Net.CertificateTransparency.SCTStatus", ct::SCT_STATUS_LOG_UNKNOWN);
225 
226   verifier_->Verify(chain_.get(), std::string_view(), sct_list, &scts,
227                     NetLogWithSource());
228 
229   ASSERT_EQ(num_valid_scts, NumValidSCTsInStatusHistogram());
230   ASSERT_EQ(num_invalid_scts + 1,
231             GetValueFromHistogram("Net.CertificateTransparency.SCTStatus",
232                                   ct::SCT_STATUS_LOG_UNKNOWN));
233 }
234 
TEST_F(MultiLogCTVerifierTest,CountsSingleEmbeddedSCTInOriginsHistogram)235 TEST_F(MultiLogCTVerifierTest, CountsSingleEmbeddedSCTInOriginsHistogram) {
236   int old_embedded_count = NumEmbeddedSCTsInHistogram();
237   ASSERT_TRUE(CheckPrecertificateVerification(embedded_sct_chain_));
238   EXPECT_EQ(old_embedded_count + 1, NumEmbeddedSCTsInHistogram());
239 }
240 
241 }  // namespace
242 
243 }  // namespace net
244