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

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

Powered by Google App Engine
This is Rietveld 408576698