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