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