Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(22)

Side by Side Diff: components/certificate_transparency/single_tree_tracker_unittest.cc

Issue 2017563002: Add Certificate Transparency logs auditing (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Ready for review Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/certificate_transparency/single_tree_tracker.h" 5 #include "components/certificate_transparency/single_tree_tracker.h"
6 6
7 #include <string> 7 #include <string>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/memory/ptr_util.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/run_loop.h"
10 #include "base/strings/string_piece.h" 13 #include "base/strings/string_piece.h"
11 #include "base/test/histogram_tester.h" 14 #include "base/test/histogram_tester.h"
15 #include "components/base32/base32.h"
16 #include "components/certificate_transparency/log_dns_client.h"
17 #include "components/certificate_transparency/mock_log_dns_traffic.h"
18 #include "crypto/sha2.h"
19 #include "net/base/network_change_notifier.h"
12 #include "net/cert/ct_log_verifier.h" 20 #include "net/cert/ct_log_verifier.h"
13 #include "net/cert/ct_serialization.h" 21 #include "net/cert/ct_serialization.h"
22 #include "net/cert/merkle_tree_leaf.h"
14 #include "net/cert/signed_certificate_timestamp.h" 23 #include "net/cert/signed_certificate_timestamp.h"
15 #include "net/cert/signed_tree_head.h" 24 #include "net/cert/signed_tree_head.h"
16 #include "net/cert/x509_certificate.h" 25 #include "net/cert/x509_certificate.h"
26 #include "net/dns/dns_client.h"
27 #include "net/log/net_log.h"
17 #include "net/test/ct_test_util.h" 28 #include "net/test/ct_test_util.h"
18 #include "testing/gtest/include/gtest/gtest.h" 29 #include "testing/gtest/include/gtest/gtest.h"
19 30
31 using net::ct::SignedCertificateTimestamp;
32 using net::ct::SignedTreeHead;
33
20 namespace certificate_transparency { 34 namespace certificate_transparency {
21 35
22 namespace { 36 namespace {
23 37
24 const char kCanCheckForInclusionHistogramName[] = 38 const char kCanCheckForInclusionHistogramName[] =
25 "Net.CertificateTransparency.CanInclusionCheckSCT"; 39 "Net.CertificateTransparency.CanInclusionCheckSCT";
40 const char kInclusionCheckResultHistogramName[] =
41 "Net.CertificateTransparency.InclusionCheckResult";
26 42
27 bool GetOldSignedTreeHead(net::ct::SignedTreeHead* sth) { 43 base::TimeDelta kMoreThanMMD = base::TimeDelta::FromHours(25);
28 sth->version = net::ct::SignedTreeHead::V1; 44
45 bool GetOldSignedTreeHead(SignedTreeHead* sth) {
46 sth->version = SignedTreeHead::V1;
29 sth->timestamp = base::Time::UnixEpoch() + 47 sth->timestamp = base::Time::UnixEpoch() +
30 base::TimeDelta::FromMilliseconds(INT64_C(1348589665525)); 48 base::TimeDelta::FromMilliseconds(INT64_C(1348589665525));
31 sth->tree_size = 12u; 49 sth->tree_size = 12u;
32 50
33 const uint8_t kOldSTHRootHash[] = { 51 const uint8_t kOldSTHRootHash[] = {
34 0x18, 0x04, 0x1b, 0xd4, 0x66, 0x50, 0x83, 0x00, 0x1f, 0xba, 0x8c, 52 0x18, 0x04, 0x1b, 0xd4, 0x66, 0x50, 0x83, 0x00, 0x1f, 0xba, 0x8c,
35 0x54, 0x11, 0xd2, 0xd7, 0x48, 0xe8, 0xab, 0xbf, 0xdc, 0xdf, 0xd9, 53 0x54, 0x11, 0xd2, 0xd7, 0x48, 0xe8, 0xab, 0xbf, 0xdc, 0xdf, 0xd9,
36 0x21, 0x8c, 0xb0, 0x2b, 0x68, 0xa7, 0x8e, 0x7d, 0x4c, 0x23}; 54 0x21, 0x8c, 0xb0, 0x2b, 0x68, 0xa7, 0x8e, 0x7d, 0x4c, 0x23};
37 memcpy(sth->sha256_root_hash, kOldSTHRootHash, net::ct::kSthRootHashLength); 55 memcpy(sth->sha256_root_hash, kOldSTHRootHash, net::ct::kSthRootHashLength);
38 56
39 sth->log_id = net::ct::GetTestPublicKeyId(); 57 sth->log_id = net::ct::GetTestPublicKeyId();
40 58
41 const uint8_t kOldSTHSignatureData[] = { 59 const uint8_t kOldSTHSignatureData[] = {
42 0x04, 0x03, 0x00, 0x47, 0x30, 0x45, 0x02, 0x20, 0x15, 0x7b, 0x23, 60 0x04, 0x03, 0x00, 0x47, 0x30, 0x45, 0x02, 0x20, 0x15, 0x7b, 0x23,
43 0x42, 0xa2, 0x5f, 0x88, 0xc9, 0x0b, 0x30, 0xa6, 0xb4, 0x49, 0x50, 61 0x42, 0xa2, 0x5f, 0x88, 0xc9, 0x0b, 0x30, 0xa6, 0xb4, 0x49, 0x50,
44 0xb3, 0xab, 0xf5, 0x25, 0xfe, 0x27, 0xf0, 0x3f, 0x9a, 0xbf, 0xc1, 62 0xb3, 0xab, 0xf5, 0x25, 0xfe, 0x27, 0xf0, 0x3f, 0x9a, 0xbf, 0xc1,
45 0x16, 0x5a, 0x7a, 0xc0, 0x62, 0x2b, 0xbb, 0x02, 0x21, 0x00, 0xe6, 63 0x16, 0x5a, 0x7a, 0xc0, 0x62, 0x2b, 0xbb, 0x02, 0x21, 0x00, 0xe6,
46 0x57, 0xa3, 0xfe, 0xfc, 0x5a, 0x82, 0x9b, 0x29, 0x46, 0x15, 0x1d, 64 0x57, 0xa3, 0xfe, 0xfc, 0x5a, 0x82, 0x9b, 0x29, 0x46, 0x15, 0x1d,
47 0xbc, 0xfd, 0x9e, 0x87, 0x7f, 0xd0, 0x00, 0x5d, 0x62, 0x4f, 0x9a, 65 0xbc, 0xfd, 0x9e, 0x87, 0x7f, 0xd0, 0x00, 0x5d, 0x62, 0x4f, 0x9a,
48 0x1a, 0x9f, 0x20, 0x79, 0xd0, 0xc1, 0x34, 0x2e, 0x08}; 66 0x1a, 0x9f, 0x20, 0x79, 0xd0, 0xc1, 0x34, 0x2e, 0x08};
49 base::StringPiece sp(reinterpret_cast<const char*>(kOldSTHSignatureData), 67 base::StringPiece sp(reinterpret_cast<const char*>(kOldSTHSignatureData),
50 sizeof(kOldSTHSignatureData)); 68 sizeof(kOldSTHSignatureData));
51 return DecodeDigitallySigned(&sp, &(sth->signature)) && sp.empty(); 69 return DecodeDigitallySigned(&sp, &(sth->signature)) && sp.empty();
52 } 70 }
53 71
72 scoped_refptr<SignedCertificateTimestamp> GetSCT() {
73 scoped_refptr<SignedCertificateTimestamp> sct;
74
75 // TODO(eranm): Move setting of the origin field to ct_test_util.cc
76 net::ct::GetX509CertSCT(&sct);
77 sct->origin = SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE;
78 return sct;
79 }
80
81 std::string Base32LeafHash(const net::X509Certificate* cert,
82 const SignedCertificateTimestamp* sct) {
83 net::ct::MerkleTreeLeaf leaf;
84 if (!GetMerkleTreeLeaf(cert, sct, &leaf))
85 return std::string();
86
87 std::string leaf_hash;
88 if (!HashMerkleTreeLeaf(leaf, &leaf_hash))
89 return std::string();
90
91 return base32::Base32Encode(leaf_hash,
92 base32::Base32EncodePolicy::OMIT_PADDING);
93 }
94
95 template <typename... T>
96 void AssertSCTsMatchingState(SingleTreeTracker* tree_tracker,
97 net::X509Certificate* chain,
98 SingleTreeTracker::SCTInclusionStatus status,
99 T... scts) {
100 const int sct_list_size = sizeof...(scts);
101 SignedCertificateTimestamp* sct_list[sct_list_size] = {scts...};
102 for (const auto& sct : sct_list) {
103 EXPECT_EQ(status, tree_tracker->GetLogEntryInclusionStatus(chain, sct))
104 << "SCT age: " << sct->timestamp;
105 }
106 };
107
108 bool GetSignedTreeHeadForTreeOfSize2(SignedTreeHead* sth) {
109 sth->version = SignedTreeHead::V1;
110 sth->timestamp = base::Time::UnixEpoch() +
111 base::TimeDelta::FromMilliseconds(INT64_C(1365354256089));
112 sth->tree_size = 2;
113 const uint8_t kRootHash[] = {0x16, 0x80, 0xbd, 0x5a, 0x1b, 0xc1, 0xb6, 0xcf,
114 0x1b, 0x7e, 0x77, 0x41, 0xeb, 0xed, 0x86, 0x8b,
115 0x73, 0x81, 0x87, 0xf5, 0xab, 0x93, 0x6d, 0xb2,
116 0x0a, 0x79, 0x0d, 0x9e, 0x40, 0x55, 0xc3, 0xe6};
117 std::string decoded_root_hash(reinterpret_cast<const char*>(kRootHash),
118 net::ct::kSthRootHashLength);
119
120 memcpy(sth->sha256_root_hash, decoded_root_hash.data(),
121 decoded_root_hash.size());
122 sth->log_id = net::ct::GetTestPublicKeyId();
123
124 const uint8_t kTreeHeadSignatureData[] = {
125 0x04, 0x03, 0x00, 0x46, 0x30, 0x44, 0x02, 0x20, 0x25, 0xa1, 0x9d,
126 0x7b, 0xf6, 0xe6, 0xfc, 0x47, 0xa7, 0x2d, 0xef, 0x6b, 0xf4, 0x84,
127 0x71, 0xb7, 0x7b, 0x7e, 0xd4, 0x4c, 0x7a, 0x5c, 0x4f, 0x9a, 0xb7,
128 0x04, 0x71, 0x6e, 0xd0, 0xa8, 0x0f, 0x53, 0x02, 0x20, 0x27, 0xe5,
129 0xed, 0x7d, 0xc3, 0x5d, 0x4c, 0xf0, 0x67, 0x35, 0x5d, 0x8a, 0x10,
130 0xae, 0x25, 0x87, 0x1a, 0xef, 0xea, 0xd2, 0xf7, 0xe3, 0x73, 0x2f,
131 0x07, 0xb3, 0x4b, 0xea, 0x5b, 0xdd, 0x81, 0x2d};
132
133 base::StringPiece sp(reinterpret_cast<const char*>(kTreeHeadSignatureData),
134 sizeof(kTreeHeadSignatureData));
135 return DecodeDigitallySigned(&sp, &sth->signature);
136 }
137
138 void FillVectorWithValidAuditProofForTreeOfSize2(
139 std::vector<std::string>* out_proof) {
140 std::string node(crypto::kSHA256Length, '\0');
141 for (size_t i = 0; i < crypto::kSHA256Length; ++i) {
142 node[i] = static_cast<char>(0x0a);
143 }
144 out_proof->push_back(node);
145 }
146
147 int FakeRandInt(int min, int max) {
148 return min;
149 }
150
54 } // namespace 151 } // namespace
55 152
56 class SingleTreeTrackerTest : public ::testing::Test { 153 class SingleTreeTrackerTest : public ::testing::Test {
57 void SetUp() override { 154 void SetUp() override {
58 log_ = 155 log_ =
59 net::CTLogVerifier::Create(net::ct::GetTestPublicKey(), "testlog", 156 net::CTLogVerifier::Create(net::ct::GetTestPublicKey(), "testlog",
60 "https://ct.example.com", "dns.example.com"); 157 "https://ct.example.com", "dns.example.com");
61 158
62 ASSERT_TRUE(log_); 159 ASSERT_TRUE(log_);
63 ASSERT_EQ(log_->key_id(), net::ct::GetTestPublicKeyId()); 160 ASSERT_EQ(log_->key_id(), net::ct::GetTestPublicKeyId());
64 161
65 tree_tracker_.reset(new SingleTreeTracker(log_));
66 const std::string der_test_cert(net::ct::GetDerEncodedX509Cert()); 162 const std::string der_test_cert(net::ct::GetDerEncodedX509Cert());
67 chain_ = net::X509Certificate::CreateFromBytes(der_test_cert.data(), 163 chain_ = net::X509Certificate::CreateFromBytes(der_test_cert.data(),
68 der_test_cert.length()); 164 der_test_cert.length());
69 ASSERT_TRUE(chain_.get()); 165 ASSERT_TRUE(chain_.get());
70 net::ct::GetX509CertSCT(&cert_sct_); 166 net::ct::GetX509CertSCT(&cert_sct_);
167 cert_sct_->origin = SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE;
168
169 ClearDnsConfig();
170 // Default to throttling requests as it means observed log entries will
171 // be frozen in a pending state, simplifying testing of the
172 // SingleTreeTracker.
173 ASSERT_TRUE(ExpectLeafHashRequestAndThrottle(chain_, cert_sct_));
174 CreateTreeTracker();
71 } 175 }
72 176
73 protected: 177 protected:
178 void ClearDnsConfig() {
179 // Must clear the global one first.
180 net_change_notifier_.reset();
181 net_change_notifier_.reset(net::NetworkChangeNotifier::CreateMock());
182
183 mock_dns_.reset(new MockLogDnsTraffic);
184 mock_dns_->InitializeDnsConfig();
185 }
186
187 void CreateTreeTracker() {
188 log_dns_client_ = base::MakeUnique<LogDnsClient>(
189 mock_dns_->CreateDnsClient(), net_log_, 1);
190
191 tree_tracker_.reset(new SingleTreeTracker(log_, log_dns_client_.get()));
192 }
193
194 bool ExpectLeafHashRequestAndThrottle(
195 scoped_refptr<net::X509Certificate> chain,
196 scoped_refptr<SignedCertificateTimestamp> sct) {
197 return mock_dns_->ExpectRequestAndSocketError(
198 Base32LeafHash(chain.get(), sct.get()) +
199 ".hash.dns.example."
200 "com",
201 net::Error::ERR_TEMPORARILY_THROTTLED);
202 }
203
204 base::MessageLoopForIO message_loop_;
205 std::unique_ptr<MockLogDnsTraffic> mock_dns_;
74 scoped_refptr<const net::CTLogVerifier> log_; 206 scoped_refptr<const net::CTLogVerifier> log_;
207 std::unique_ptr<net::NetworkChangeNotifier> net_change_notifier_;
208 std::unique_ptr<LogDnsClient> log_dns_client_;
75 std::unique_ptr<SingleTreeTracker> tree_tracker_; 209 std::unique_ptr<SingleTreeTracker> tree_tracker_;
76 scoped_refptr<net::X509Certificate> chain_; 210 scoped_refptr<net::X509Certificate> chain_;
77 scoped_refptr<net::ct::SignedCertificateTimestamp> cert_sct_; 211 scoped_refptr<SignedCertificateTimestamp> cert_sct_;
212 net::NetLogWithSource net_log_;
78 }; 213 };
79 214
80 // Test that an SCT is classified as pending for a newer STH if the 215 // Test that an SCT is classified as pending for a newer STH if the
81 // SingleTreeTracker has not seen any STHs so far. 216 // SingleTreeTracker has not seen any STHs so far.
82 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTNoSTH) { 217 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTNoSTH) {
83 base::HistogramTester histograms; 218 base::HistogramTester histograms;
84 // First make sure the SCT has not been observed at all. 219 // First make sure the SCT has not been observed at all.
85 EXPECT_EQ( 220 EXPECT_EQ(
86 SingleTreeTracker::SCT_NOT_OBSERVED, 221 SingleTreeTracker::SCT_NOT_OBSERVED,
87 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 222 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
88 223
89 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); 224 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
90 225
91 // Since no STH was provided to the tree_tracker_ the status should be that 226 // Since no STH was provided to the tree_tracker_ the status should be that
92 // the SCT is pending a newer STH. 227 // the SCT is pending a newer STH.
93 EXPECT_EQ( 228 EXPECT_EQ(
94 SingleTreeTracker::SCT_PENDING_NEWER_STH, 229 SingleTreeTracker::SCT_PENDING_NEWER_STH,
95 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 230 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
96 231
97 // Expect logging of a value indicating a valid STH is required. 232 // Expect logging of a value indicating a valid STH is required.
98 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1); 233 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
99 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 0, 1); 234 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 0, 1);
100 } 235 }
101 236
102 // Test that an SCT is classified as pending an inclusion check if the 237 // Test that an SCT is classified as pending an inclusion check if the
103 // SingleTreeTracker has a fresh-enough STH to check inclusion against. 238 // SingleTreeTracker has a fresh-enough STH to check inclusion against.
104 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTWithRecentSTH) { 239 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTWithRecentSTH) {
105 base::HistogramTester histograms; 240 base::HistogramTester histograms;
106 // Provide an STH to the tree_tracker_. 241 // Provide an STH to the tree_tracker_.
107 net::ct::SignedTreeHead sth; 242 SignedTreeHead sth;
108 net::ct::GetSampleSignedTreeHead(&sth); 243 net::ct::GetSampleSignedTreeHead(&sth);
109 tree_tracker_->NewSTHObserved(sth); 244 tree_tracker_->NewSTHObserved(sth);
110 245
111 // Make sure the SCT status is the same as if there's no STH for 246 // Make sure the SCT status is the same as if there's no STH for
112 // this log. 247 // this log.
113 EXPECT_EQ( 248 EXPECT_EQ(
114 SingleTreeTracker::SCT_NOT_OBSERVED, 249 SingleTreeTracker::SCT_NOT_OBSERVED,
115 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 250 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
116 251
117 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); 252 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
118 253
119 // The status for this SCT should be 'pending inclusion check' since the STH 254 // The status for this SCT should be 'pending inclusion check' since the STH
120 // provided at the beginning of the test is newer than the SCT. 255 // provided at the beginning of the test is newer than the SCT.
121 EXPECT_EQ( 256 EXPECT_EQ(
122 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK, 257 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
123 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 258 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
124 259
125 // Exactly one value should be logged, indicating the SCT can be checked for 260 // Exactly one value should be logged, indicating the SCT can be checked for
126 // inclusion, as |tree_tracker_| did have a valid STH when it was notified 261 // inclusion, as |tree_tracker_| did have a valid STH when it was notified
127 // of a new SCT. 262 // of a new SCT.
128 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1); 263 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
129 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 2, 1); 264 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 2, 1);
265 // Nothing should be logged in the result histogram since inclusion check
266 // didn't finish.
267 histograms.ExpectTotalCount(kInclusionCheckResultHistogramName, 0);
130 } 268 }
131 269
132 // Test that the SingleTreeTracker correctly queues verified SCTs for inclusion 270 // Test that the SingleTreeTracker correctly queues verified SCTs for inclusion
133 // checking such that, upon receiving a fresh STH, it changes the SCT's status 271 // checking such that, upon receiving a fresh STH, it changes the SCT's status
134 // from pending newer STH to pending inclusion check. 272 // from pending newer STH to pending inclusion check.
135 TEST_F(SingleTreeTrackerTest, CorrectlyUpdatesSCTStatusOnNewSTH) { 273 TEST_F(SingleTreeTrackerTest, CorrectlyUpdatesSCTStatusOnNewSTH) {
136 base::HistogramTester histograms; 274 base::HistogramTester histograms;
137 // Report an observed SCT and make sure it's in the pending newer STH 275 // Report an observed SCT and make sure it's in the pending newer STH
138 // state. 276 // state.
139 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); 277 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
140 EXPECT_EQ( 278 EXPECT_EQ(
141 SingleTreeTracker::SCT_PENDING_NEWER_STH, 279 SingleTreeTracker::SCT_PENDING_NEWER_STH,
142 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 280 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
143 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1); 281 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
144 282
145 // Provide with a fresh STH 283 // Provide with a fresh STH
146 net::ct::SignedTreeHead sth; 284 SignedTreeHead sth;
147 net::ct::GetSampleSignedTreeHead(&sth); 285 net::ct::GetSampleSignedTreeHead(&sth);
148 tree_tracker_->NewSTHObserved(sth); 286 tree_tracker_->NewSTHObserved(sth);
149 287
150 // Test that its status has changed. 288 // Test that its status has changed.
151 EXPECT_EQ( 289 EXPECT_EQ(
152 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK, 290 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
153 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 291 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
154 // Check that no additional UMA was logged for this case as the histogram is 292 // Check that no additional UMA was logged for this case as the histogram is
155 // only supposed to measure the state of newly-observed SCTs, not pending 293 // only supposed to measure the state of newly-observed SCTs, not pending
156 // ones. 294 // ones.
157 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1); 295 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
158 } 296 }
159 297
160 // Test that the SingleTreeTracker does not change an SCT's status if an STH 298 // Test that the SingleTreeTracker does not change an SCT's status if an STH
161 // from the log it was issued by is observed, but that STH is too old to check 299 // from the log it was issued by is observed, but that STH is too old to check
162 // inclusion against. 300 // inclusion against.
163 TEST_F(SingleTreeTrackerTest, DoesNotUpdatesSCTStatusOnOldSTH) { 301 TEST_F(SingleTreeTrackerTest, DoesNotUpdatesSCTStatusOnOldSTH) {
164 // Notify of an SCT and make sure it's in the 'pending newer STH' state. 302 // Notify of an SCT and make sure it's in the 'pending newer STH' state.
165 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); 303 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
166 EXPECT_EQ( 304 EXPECT_EQ(
167 SingleTreeTracker::SCT_PENDING_NEWER_STH, 305 SingleTreeTracker::SCT_PENDING_NEWER_STH,
168 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 306 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
169 307
170 // Provide an old STH for the same log. 308 // Provide an old STH for the same log.
171 net::ct::SignedTreeHead sth; 309 SignedTreeHead sth;
172 GetOldSignedTreeHead(&sth); 310 GetOldSignedTreeHead(&sth);
173 tree_tracker_->NewSTHObserved(sth); 311 tree_tracker_->NewSTHObserved(sth);
174 312
175 // Make sure the SCT's state hasn't changed. 313 // Make sure the SCT's state hasn't changed.
176 EXPECT_EQ( 314 EXPECT_EQ(
177 SingleTreeTracker::SCT_PENDING_NEWER_STH, 315 SingleTreeTracker::SCT_PENDING_NEWER_STH,
178 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 316 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
179 } 317 }
180 318
181 // Test that the SingleTreeTracker correctly logs that an SCT is pending a new 319 // Test that the SingleTreeTracker correctly logs that an SCT is pending a new
182 // STH, when it has a valid STH, but the observed SCT is not covered by the 320 // STH, when it has a valid STH, but the observed SCT is not covered by the
183 // STH. 321 // STH.
184 TEST_F(SingleTreeTrackerTest, LogsUMAForNewSCTAndOldSTH) { 322 TEST_F(SingleTreeTrackerTest, LogsUMAForNewSCTAndOldSTH) {
185 base::HistogramTester histograms; 323 base::HistogramTester histograms;
186 // Provide an old STH for the same log. 324 // Provide an old STH for the same log.
187 net::ct::SignedTreeHead sth; 325 SignedTreeHead sth;
188 GetOldSignedTreeHead(&sth); 326 GetOldSignedTreeHead(&sth);
189 tree_tracker_->NewSTHObserved(sth); 327 tree_tracker_->NewSTHObserved(sth);
190 328
191 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 0); 329 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 0);
192 330
193 // Notify of an SCT and make sure it's in the 'pending newer STH' state. 331 // Notify of an SCT and make sure it's in the 'pending newer STH' state.
194 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); 332 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
195 333
196 // Exactly one value should be logged, indicating the SCT cannot be checked 334 // Exactly one value should be logged, indicating the SCT cannot be checked
197 // for inclusion as the STH is too old. 335 // for inclusion as the STH is too old.
198 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1); 336 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
199 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 1, 1); 337 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 1, 1);
200 } 338 }
201 339
340 // Test that an entry transitions to the "not found" state if the LogDnsClient
341 // fails to get a leaf index.
342 TEST_F(SingleTreeTrackerTest, TestEntryNotPendingAfterLeafIndexFetchFailure) {
343 ClearDnsConfig();
344 ASSERT_TRUE(mock_dns_->ExpectRequestAndSocketError(
345 Base32LeafHash(chain_.get(), cert_sct_.get()) + ".hash.dns.example."
346 "com",
347 net::Error::ERR_FAILED));
348
349 CreateTreeTracker();
350
351 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
352 EXPECT_EQ(
353 SingleTreeTracker::SCT_PENDING_NEWER_STH,
354 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
355
356 // Provide with a fresh STH
357 SignedTreeHead sth;
358 net::ct::GetSampleSignedTreeHead(&sth);
359 tree_tracker_->NewSTHObserved(sth);
360 base::RunLoop().RunUntilIdle();
361
362 EXPECT_EQ(
363 SingleTreeTracker::SCT_NOT_OBSERVED,
364 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
365 }
366
367 // Test that an entry transitions to the "not found" state if the LogDnsClient
368 // succeeds to get a leaf index but fails to get an inclusion proof.
369 TEST_F(SingleTreeTrackerTest, TestEntryNotPendingAfterInclusionCheckFailure) {
370 ClearDnsConfig();
371 ASSERT_TRUE(mock_dns_->ExpectLeafIndexRequestAndResponse(
372 Base32LeafHash(chain_.get(), cert_sct_.get()) + ".hash.dns.example."
373 "com",
374 12));
375 ASSERT_TRUE(mock_dns_->ExpectRequestAndSocketError(
376 "0.12.21.tree.dns.example.com", net::Error::ERR_FAILED));
377
378 CreateTreeTracker();
379
380 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
381 EXPECT_EQ(
382 SingleTreeTracker::SCT_PENDING_NEWER_STH,
383 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
384
385 // Provide with a fresh STH
386 SignedTreeHead sth;
387 net::ct::GetSampleSignedTreeHead(&sth);
388 tree_tracker_->NewSTHObserved(sth);
389 base::RunLoop().RunUntilIdle();
390
391 EXPECT_EQ(
392 SingleTreeTracker::SCT_NOT_OBSERVED,
393 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
394 }
395
396 // Test that an entry transitions to the "included" state if the LogDnsClient
397 // succeeds to get a leaf index and an inclusion proof.
398 TEST_F(SingleTreeTrackerTest, TestEntryIncludedAfterInclusionCheckSuccess) {
399 std::vector<std::string> audit_proof;
400 FillVectorWithValidAuditProofForTreeOfSize2(&audit_proof);
401
402 ClearDnsConfig();
403 ASSERT_TRUE(mock_dns_->ExpectLeafIndexRequestAndResponse(
404 Base32LeafHash(chain_.get(), cert_sct_.get()) + ".hash.dns.example."
405 "com",
406 0));
407 ASSERT_TRUE(mock_dns_->ExpectAuditProofRequestAndResponse(
408 "0.0.2.tree.dns.example.com", audit_proof.begin(),
409 audit_proof.begin() + 1));
410
411 CreateTreeTracker();
412
413 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
414 EXPECT_EQ(
415 SingleTreeTracker::SCT_PENDING_NEWER_STH,
416 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
417
418 // Provide with a fresh STH
419 SignedTreeHead sth;
420 ASSERT_TRUE(GetSignedTreeHeadForTreeOfSize2(&sth));
421 ASSERT_TRUE(log_->VerifySignedTreeHead(sth));
422
423 tree_tracker_->NewSTHObserved(sth);
424 base::RunLoop().RunUntilIdle();
425
426 EXPECT_EQ(
427 SingleTreeTracker::SCT_INCLUDED_IN_LOG,
428 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
429 }
430
431 // Test that pending entries transition states correctly according to the
432 // STHs provided:
433 // * Start without an STH.
434 // * Add a collection of entries with mixed timestamps (i.e. SCTs not added
435 // in the order of their timestamps).
436 // * Provide an STH that covers some of the entries, test these are audited.
437 // * Provide another STH that covers more of the entries, test that the entries
438 // already audited are not audited again and that those that need to be
439 // audited are audited, while those that are not covered by that STH are
440 // not audited.
441 TEST_F(SingleTreeTrackerTest, TestMultipleEntriesTransitionStateCorrectly) {
442 SignedTreeHead old_sth;
443 GetOldSignedTreeHead(&old_sth);
444
445 SignedTreeHead new_sth;
446 net::ct::GetSampleSignedTreeHead(&new_sth);
447
448 base::TimeDelta kLessThanMMD = base::TimeDelta::FromHours(23);
449
450 // Assert the gap between the two timestamps is big enough so that
451 // all assumptions below on which SCT can be audited with the
452 // new STH are true.
453 ASSERT_LT(old_sth.timestamp + (kMoreThanMMD * 2), new_sth.timestamp);
454
455 // Oldest SCT - auditable by the old and new STHs.
456 scoped_refptr<SignedCertificateTimestamp> oldest_sct(GetSCT());
457 oldest_sct->timestamp = old_sth.timestamp - kMoreThanMMD;
458
459 // SCT that's older than the old STH's timestamp but by less than the MMD,
460 // so not auditable by old STH.
461 scoped_refptr<SignedCertificateTimestamp> not_auditable_by_old_sth_sct(
462 GetSCT());
463 not_auditable_by_old_sth_sct->timestamp = old_sth.timestamp - kLessThanMMD;
464
465 // SCT that's newer than the old STH's timestamp so is only auditable by
466 // the new STH.
467 scoped_refptr<SignedCertificateTimestamp> newer_than_old_sth_sct(GetSCT());
468 newer_than_old_sth_sct->timestamp = old_sth.timestamp + kLessThanMMD;
469
470 // SCT that's older than the new STH's timestamp but by less than the MMD,
471 // so isn't auditable by the new STH.
472 scoped_refptr<SignedCertificateTimestamp> not_auditable_by_new_sth_sct(
473 GetSCT());
474 not_auditable_by_new_sth_sct->timestamp = new_sth.timestamp - kLessThanMMD;
475
476 // SCT that's newer than the new STH's timestamp so isn't auditable by the
477 // the new STH.
478 scoped_refptr<SignedCertificateTimestamp> newer_than_new_sth_sct(GetSCT());
479 newer_than_new_sth_sct->timestamp = new_sth.timestamp + kLessThanMMD;
480
481 // Set up DNS expectations based on inclusion proof request order.
482 ClearDnsConfig();
483 ASSERT_TRUE(ExpectLeafHashRequestAndThrottle(chain_, oldest_sct));
484 ASSERT_TRUE(
485 ExpectLeafHashRequestAndThrottle(chain_, not_auditable_by_old_sth_sct));
486 ASSERT_TRUE(ExpectLeafHashRequestAndThrottle(chain_, newer_than_old_sth_sct));
487 CreateTreeTracker();
488
489 // Add SCTs in mixed order.
490 tree_tracker_->OnSCTVerified(chain_.get(), newer_than_new_sth_sct.get());
491 tree_tracker_->OnSCTVerified(chain_.get(), oldest_sct.get());
492 tree_tracker_->OnSCTVerified(chain_.get(),
493 not_auditable_by_new_sth_sct.get());
494 tree_tracker_->OnSCTVerified(chain_.get(), newer_than_old_sth_sct.get());
495 tree_tracker_->OnSCTVerified(chain_.get(),
496 not_auditable_by_old_sth_sct.get());
497
498 // Ensure all are in the PENDING_NEWER_STH state.
499 AssertSCTsMatchingState<SignedCertificateTimestamp*>(
500 tree_tracker_.get(), chain_.get(),
501 SingleTreeTracker::SCT_PENDING_NEWER_STH, oldest_sct.get(),
502 not_auditable_by_old_sth_sct.get(), newer_than_old_sth_sct.get(),
503 not_auditable_by_new_sth_sct.get(), newer_than_new_sth_sct.get());
504
505 // Provide the old STH, ensure only the oldest one is auditable.
506 tree_tracker_->NewSTHObserved(old_sth);
507 // Ensure all but the oldest are in the PENDING_NEWER_STH state.
508 AssertSCTsMatchingState<SignedCertificateTimestamp*>(
509 tree_tracker_.get(), chain_.get(),
510 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK, oldest_sct.get());
511
512 AssertSCTsMatchingState<SignedCertificateTimestamp*>(
513 tree_tracker_.get(), chain_.get(),
514 SingleTreeTracker::SCT_PENDING_NEWER_STH,
515 not_auditable_by_old_sth_sct.get(), newer_than_old_sth_sct.get(),
516 not_auditable_by_new_sth_sct.get(), newer_than_new_sth_sct.get());
517
518 // Provide the newer one, ensure two more are auditable but the
519 // rest aren't.
520 tree_tracker_->NewSTHObserved(new_sth);
521
522 AssertSCTsMatchingState<SignedCertificateTimestamp*>(
523 tree_tracker_.get(), chain_.get(),
524 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK, oldest_sct.get(),
525 not_auditable_by_old_sth_sct.get(), newer_than_old_sth_sct.get());
526
527 AssertSCTsMatchingState<SignedCertificateTimestamp*>(
528 tree_tracker_.get(), chain_.get(),
529 SingleTreeTracker::SCT_PENDING_NEWER_STH,
530 not_auditable_by_new_sth_sct.get(), newer_than_new_sth_sct.get());
531 }
532
533 // Test that if a request for an entry is throttled, it remains in a
534 // pending state.
535
536 // Test that if several entries are throttled, when the LogDnsClient notifies
537 // of un-throttling all entries are handled.
538 TEST_F(SingleTreeTrackerTest, TestThrottledEntryGetsHandledAfterUnthrottling) {
539 std::vector<std::string> audit_proof;
540 FillVectorWithValidAuditProofForTreeOfSize2(&audit_proof);
541
542 ClearDnsConfig();
543 ASSERT_TRUE(mock_dns_->ExpectLeafIndexRequestAndResponse(
544 Base32LeafHash(chain_.get(), cert_sct_.get()) + ".hash.dns.example."
545 "com",
546 0));
547 ASSERT_TRUE(mock_dns_->ExpectAuditProofRequestAndResponse(
548 "0.0.2.tree.dns.example.com", audit_proof.begin(),
549 audit_proof.begin() + 1));
550
551 scoped_refptr<SignedCertificateTimestamp> second_sct(GetSCT());
552 second_sct->timestamp -= base::TimeDelta::FromHours(1);
553
554 // Process request for |second_sct|
555 ASSERT_TRUE(mock_dns_->ExpectLeafIndexRequestAndResponse(
556 Base32LeafHash(chain_.get(), second_sct.get()) + ".hash.dns.example."
557 "com",
558 1));
559 ASSERT_TRUE(mock_dns_->ExpectAuditProofRequestAndResponse(
560 "0.1.2.tree.dns.example.com", audit_proof.begin(),
561 audit_proof.begin() + 1));
562
563 CreateTreeTracker();
564
565 SignedTreeHead sth;
566 ASSERT_TRUE(GetSignedTreeHeadForTreeOfSize2(&sth));
567 tree_tracker_->NewSTHObserved(sth);
568
569 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
570 tree_tracker_->OnSCTVerified(chain_.get(), second_sct.get());
571
572 // Both entries should be in the pending state, the first because the
573 // LogDnsClient did not invoke the callback yet, the second one because
574 // the LogDnsClient is "busy" with the first entry and so would throttle.
575 ASSERT_EQ(
576 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
577 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
578 ASSERT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
579 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(),
580 second_sct.get()));
581
582 // Pump the message loop.
583 base::RunLoop().RunUntilIdle();
584
585 // Check that the first sct is included in the log.
586 ASSERT_EQ(
587 SingleTreeTracker::SCT_INCLUDED_IN_LOG,
588 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
589
590 // Check that the second SCT got an invalid proof and is not included, rather
591 // than being in the pending state.
592 ASSERT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
593 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(),
594 second_sct.get()));
595 }
596
597 // Test that proof fetching failure due to DNS config errors is handled
598 // correctly:
599 // (1) Entry removed from pending queue.
600 // (2) UMA logged
601 TEST_F(SingleTreeTrackerTest,
602 TestProofLookupDueToBadDNSConfigHandledCorrectly) {
603 base::HistogramTester histograms;
604 // Provide an STH to the tree_tracker_.
605 SignedTreeHead sth;
606 net::ct::GetSampleSignedTreeHead(&sth);
607
608 // Clear existing DNS configuration, so that the DnsClient created
609 // by the MockLogDnsTraffic has no valid DnsConfig.
610 net_change_notifier_.reset();
611 net_change_notifier_.reset(net::NetworkChangeNotifier::CreateMock());
612 CreateTreeTracker();
613
614 tree_tracker_->NewSTHObserved(sth);
615 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
616
617 // Make sure the SCT status indicates the entry has been removed from
618 // the SingleTreeTracker's internal queue as the DNS lookup failed
619 // synchronously.
620 EXPECT_EQ(
621 SingleTreeTracker::SCT_NOT_OBSERVED,
622 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
623
624 // Exactly one value should be logged, indicating the SCT can be checked for
625 // inclusion, as |tree_tracker_| did have a valid STH when it was notified
626 // of a new SCT.
627 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
628 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 2, 1);
629 // Failure due to DNS configuration should be logged in the result histogram.
630 histograms.ExpectTotalCount(kInclusionCheckResultHistogramName, 1);
631 histograms.ExpectBucketCount(kInclusionCheckResultHistogramName, 3, 1);
632 }
633
202 } // namespace certificate_transparency 634 } // namespace certificate_transparency
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698