• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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>(&notifier);
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