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

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: Fix Windows compilation issue Created 3 years, 11 months 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 constexpr 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,
Ryan Sleevi 2017/01/20 12:53:21 Did you see the previous remarks ( https://coderev
Eran Messeri 2017/01/23 16:45:47 My bad, I've missed it, changed to your suggestion
107 SingleTreeTracker::SCTInclusionStatus status,
108 T... scts) {
109 const size_t 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 net_change_notifier_ =
185 base::WrapUnique(net::NetworkChangeNotifier::CreateMock());
186 mock_dns_.InitializeDnsConfig();
71 } 187 }
72 188
73 protected: 189 protected:
190 void CreateTreeTracker() {
191 log_dns_client_ = base::MakeUnique<LogDnsClient>(
192 mock_dns_.CreateDnsClient(), net_log_, 1);
193
194 tree_tracker_ =
195 base::MakeUnique<SingleTreeTracker>(log_, log_dns_client_.get());
196 }
197
198 void CreateTreeTrackerWithDefaultDnsExpectation() {
199 // Default to throttling requests as it means observed log entries will
200 // be frozen in a pending state, simplifying testing of the
201 // SingleTreeTracker.
202 ASSERT_TRUE(ExpectLeafIndexRequestAndThrottle(chain_, cert_sct_));
203 CreateTreeTracker();
204 }
205
206 // Configured the |mock_dns_| to expect a request for the leaf index
207 // and have th mock DNS client throttle it.
208 bool ExpectLeafIndexRequestAndThrottle(
209 const scoped_refptr<net::X509Certificate>& chain,
210 const scoped_refptr<SignedCertificateTimestamp>& sct) {
211 return mock_dns_.ExpectRequestAndSocketError(
212 Base32LeafHash(chain.get(), sct.get()) + ".hash." + kDNSRequestSuffix,
213 net::Error::ERR_TEMPORARILY_THROTTLED);
214 }
215
216 base::MessageLoopForIO message_loop_;
217 MockLogDnsTraffic mock_dns_;
74 scoped_refptr<const net::CTLogVerifier> log_; 218 scoped_refptr<const net::CTLogVerifier> log_;
219 std::unique_ptr<net::NetworkChangeNotifier> net_change_notifier_;
220 std::unique_ptr<LogDnsClient> log_dns_client_;
75 std::unique_ptr<SingleTreeTracker> tree_tracker_; 221 std::unique_ptr<SingleTreeTracker> tree_tracker_;
76 scoped_refptr<net::X509Certificate> chain_; 222 scoped_refptr<net::X509Certificate> chain_;
77 scoped_refptr<net::ct::SignedCertificateTimestamp> cert_sct_; 223 scoped_refptr<SignedCertificateTimestamp> cert_sct_;
224 net::NetLogWithSource net_log_;
78 }; 225 };
79 226
80 // Test that an SCT is classified as pending for a newer STH if the 227 // Test that an SCT is classified as pending for a newer STH if the
81 // SingleTreeTracker has not seen any STHs so far. 228 // SingleTreeTracker has not seen any STHs so far.
82 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTNoSTH) { 229 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTNoSTH) {
230 CreateTreeTrackerWithDefaultDnsExpectation();
231
83 base::HistogramTester histograms; 232 base::HistogramTester histograms;
84 // First make sure the SCT has not been observed at all. 233 // First make sure the SCT has not been observed at all.
85 EXPECT_EQ( 234 EXPECT_EQ(
86 SingleTreeTracker::SCT_NOT_OBSERVED, 235 SingleTreeTracker::SCT_NOT_OBSERVED,
87 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 236 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
88 237
89 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); 238 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
90 239
91 // Since no STH was provided to the tree_tracker_ the status should be that 240 // Since no STH was provided to the tree_tracker_ the status should be that
92 // the SCT is pending a newer STH. 241 // the SCT is pending a newer STH.
93 EXPECT_EQ( 242 EXPECT_EQ(
94 SingleTreeTracker::SCT_PENDING_NEWER_STH, 243 SingleTreeTracker::SCT_PENDING_NEWER_STH,
95 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 244 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
96 245
97 // Expect logging of a value indicating a valid STH is required. 246 // Expect logging of a value indicating a valid STH is required.
98 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1); 247 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
99 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 0, 1); 248 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 0, 1);
100 } 249 }
101 250
102 // Test that an SCT is classified as pending an inclusion check if the 251 // Test that an SCT is classified as pending an inclusion check if the
103 // SingleTreeTracker has a fresh-enough STH to check inclusion against. 252 // SingleTreeTracker has a fresh-enough STH to check inclusion against.
104 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTWithRecentSTH) { 253 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTWithRecentSTH) {
254 CreateTreeTrackerWithDefaultDnsExpectation();
255
105 base::HistogramTester histograms; 256 base::HistogramTester histograms;
106 // Provide an STH to the tree_tracker_. 257 // Provide an STH to the tree_tracker_.
107 net::ct::SignedTreeHead sth; 258 SignedTreeHead sth;
108 net::ct::GetSampleSignedTreeHead(&sth); 259 GetSampleSignedTreeHead(&sth);
109 tree_tracker_->NewSTHObserved(sth); 260 tree_tracker_->NewSTHObserved(sth);
110 261
111 // Make sure the SCT status is the same as if there's no STH for 262 // Make sure the SCT status is the same as if there's no STH for
112 // this log. 263 // this log.
113 EXPECT_EQ( 264 EXPECT_EQ(
114 SingleTreeTracker::SCT_NOT_OBSERVED, 265 SingleTreeTracker::SCT_NOT_OBSERVED,
115 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 266 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
116 267
117 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); 268 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
118 269
119 // The status for this SCT should be 'pending inclusion check' since the STH 270 // 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. 271 // provided at the beginning of the test is newer than the SCT.
121 EXPECT_EQ( 272 EXPECT_EQ(
122 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK, 273 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
123 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 274 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
124 275
125 // Exactly one value should be logged, indicating the SCT can be checked for 276 // 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 277 // inclusion, as |tree_tracker_| did have a valid STH when it was notified
127 // of a new SCT. 278 // of a new SCT.
128 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1); 279 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
129 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 2, 1); 280 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 2, 1);
281 // Nothing should be logged in the result histogram since inclusion check
282 // didn't finish.
283 histograms.ExpectTotalCount(kInclusionCheckResultHistogramName, 0);
130 } 284 }
131 285
132 // Test that the SingleTreeTracker correctly queues verified SCTs for inclusion 286 // 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 287 // checking such that, upon receiving a fresh STH, it changes the SCT's status
134 // from pending newer STH to pending inclusion check. 288 // from pending newer STH to pending inclusion check.
135 TEST_F(SingleTreeTrackerTest, CorrectlyUpdatesSCTStatusOnNewSTH) { 289 TEST_F(SingleTreeTrackerTest, CorrectlyUpdatesSCTStatusOnNewSTH) {
290 CreateTreeTrackerWithDefaultDnsExpectation();
291
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) {
320 CreateTreeTrackerWithDefaultDnsExpectation();
321
164 // Notify of an SCT and make sure it's in the 'pending newer STH' state. 322 // Notify of an SCT and make sure it's in the 'pending newer STH' state.
165 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); 323 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
166 EXPECT_EQ( 324 EXPECT_EQ(
167 SingleTreeTracker::SCT_PENDING_NEWER_STH, 325 SingleTreeTracker::SCT_PENDING_NEWER_STH,
168 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 326 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
169 327
170 // Provide an old STH for the same log. 328 // Provide an old STH for the same log.
171 net::ct::SignedTreeHead sth; 329 SignedTreeHead sth;
172 GetOldSignedTreeHead(&sth); 330 GetOldSignedTreeHead(&sth);
173 tree_tracker_->NewSTHObserved(sth); 331 tree_tracker_->NewSTHObserved(sth);
174 332
175 // Make sure the SCT's state hasn't changed. 333 // Make sure the SCT's state hasn't changed.
176 EXPECT_EQ( 334 EXPECT_EQ(
177 SingleTreeTracker::SCT_PENDING_NEWER_STH, 335 SingleTreeTracker::SCT_PENDING_NEWER_STH,
178 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 336 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
179 } 337 }
180 338
181 // Test that the SingleTreeTracker correctly logs that an SCT is pending a new 339 // 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 340 // STH, when it has a valid STH, but the observed SCT is not covered by the
183 // STH. 341 // STH.
184 TEST_F(SingleTreeTrackerTest, LogsUMAForNewSCTAndOldSTH) { 342 TEST_F(SingleTreeTrackerTest, LogsUMAForNewSCTAndOldSTH) {
343 CreateTreeTrackerWithDefaultDnsExpectation();
344
185 base::HistogramTester histograms; 345 base::HistogramTester histograms;
186 // Provide an old STH for the same log. 346 // Provide an old STH for the same log.
187 net::ct::SignedTreeHead sth; 347 SignedTreeHead sth;
188 GetOldSignedTreeHead(&sth); 348 GetOldSignedTreeHead(&sth);
189 tree_tracker_->NewSTHObserved(sth); 349 tree_tracker_->NewSTHObserved(sth);
190 350
191 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 0); 351 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 0);
192 352
193 // Notify of an SCT and make sure it's in the 'pending newer STH' state. 353 // Notify of an SCT and make sure it's in the 'pending newer STH' state.
194 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); 354 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
195 355
196 // Exactly one value should be logged, indicating the SCT cannot be checked 356 // Exactly one value should be logged, indicating the SCT cannot be checked
197 // for inclusion as the STH is too old. 357 // for inclusion as the STH is too old.
198 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1); 358 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
199 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 1, 1); 359 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 1, 1);
200 } 360 }
201 361
362 // Test that an entry transitions to the "not found" state if the LogDnsClient
363 // fails to get a leaf index.
364 TEST_F(SingleTreeTrackerTest, TestEntryNotPendingAfterLeafIndexFetchFailure) {
365 ASSERT_TRUE(mock_dns_.ExpectRequestAndSocketError(
366 Base32LeafHash(chain_.get(), cert_sct_.get()) + ".hash." +
367 kDNSRequestSuffix,
368 net::Error::ERR_FAILED));
369
370 CreateTreeTracker();
371
372 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
373 EXPECT_EQ(
374 SingleTreeTracker::SCT_PENDING_NEWER_STH,
375 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
376
377 // Provide with a fresh STH
378 SignedTreeHead sth;
379 GetSampleSignedTreeHead(&sth);
380 tree_tracker_->NewSTHObserved(sth);
381 base::RunLoop().RunUntilIdle();
382
383 EXPECT_EQ(
384 SingleTreeTracker::SCT_NOT_OBSERVED,
385 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
386 }
387
388 // Test that an entry transitions to the "not found" state if the LogDnsClient
389 // succeeds to get a leaf index but fails to get an inclusion proof.
390 TEST_F(SingleTreeTrackerTest, TestEntryNotPendingAfterInclusionCheckFailure) {
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 // Return 0 as the index for this leaf, so the proof provided
428 // later on would verify.
429 ASSERT_TRUE(mock_dns_.ExpectLeafIndexRequestAndResponse(
430 Base32LeafHash(chain_.get(), cert_sct_.get()) + ".hash." +
431 kDNSRequestSuffix,
432 0));
433 // The STH (later on) is for a tree of size 2 and the entry has index 0
434 // in the tree, so expect an inclusion proof for entry 0 in a tree
435 // of size 2 (0.0.2).
436 ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
437 std::string("0.0.2.tree.") + kDNSRequestSuffix, audit_proof.begin(),
438 audit_proof.begin() + 1));
439
440 CreateTreeTracker();
441
442 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
443 EXPECT_EQ(
444 SingleTreeTracker::SCT_PENDING_NEWER_STH,
445 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
446
447 // Provide with a fresh STH, which is for a tree of size 2.
448 SignedTreeHead sth;
449 ASSERT_TRUE(GetSignedTreeHeadForTreeOfSize2(&sth));
450 ASSERT_TRUE(log_->VerifySignedTreeHead(sth));
451
452 tree_tracker_->NewSTHObserved(sth);
453 base::RunLoop().RunUntilIdle();
454
455 EXPECT_EQ(
456 SingleTreeTracker::SCT_INCLUDED_IN_LOG,
457 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
458 }
459
460 // Test that pending entries transition states correctly according to the
461 // STHs provided:
462 // * Start without an STH.
463 // * Add a collection of entries with mixed timestamps (i.e. SCTs not added
464 // in the order of their timestamps).
465 // * Provide an STH that covers some of the entries, test these are audited.
466 // * Provide another STH that covers more of the entries, test that the entries
467 // already audited are not audited again and that those that need to be
468 // audited are audited, while those that are not covered by that STH are
469 // not audited.
470 TEST_F(SingleTreeTrackerTest, TestMultipleEntriesTransitionStateCorrectly) {
471 SignedTreeHead old_sth;
472 GetOldSignedTreeHead(&old_sth);
473
474 SignedTreeHead new_sth;
475 GetSampleSignedTreeHead(&new_sth);
476
477 base::TimeDelta kLessThanMMD = base::TimeDelta::FromHours(23);
478
479 // Assert the gap between the two timestamps is big enough so that
480 // all assumptions below on which SCT can be audited with the
481 // new STH are true.
482 ASSERT_LT(old_sth.timestamp + (kMoreThanMMD * 2), new_sth.timestamp);
483
484 // Oldest SCT - auditable by the old and new STHs.
485 scoped_refptr<SignedCertificateTimestamp> oldest_sct(GetSCT());
486 oldest_sct->timestamp = old_sth.timestamp - kMoreThanMMD;
487
488 // SCT that's older than the old STH's timestamp but by less than the MMD,
489 // so not auditable by old STH.
490 scoped_refptr<SignedCertificateTimestamp> not_auditable_by_old_sth_sct(
491 GetSCT());
492 not_auditable_by_old_sth_sct->timestamp = old_sth.timestamp - kLessThanMMD;
493
494 // SCT that's newer than the old STH's timestamp so is only auditable by
495 // the new STH.
496 scoped_refptr<SignedCertificateTimestamp> newer_than_old_sth_sct(GetSCT());
497 newer_than_old_sth_sct->timestamp = old_sth.timestamp + kLessThanMMD;
498
499 // SCT that's older than the new STH's timestamp but by less than the MMD,
500 // so isn't auditable by the new STH.
501 scoped_refptr<SignedCertificateTimestamp> not_auditable_by_new_sth_sct(
502 GetSCT());
503 not_auditable_by_new_sth_sct->timestamp = new_sth.timestamp - kLessThanMMD;
504
505 // SCT that's newer than the new STH's timestamp so isn't auditable by the
506 // the new STH.
507 scoped_refptr<SignedCertificateTimestamp> newer_than_new_sth_sct(GetSCT());
508 newer_than_new_sth_sct->timestamp = new_sth.timestamp + kLessThanMMD;
509
510 // Set up DNS expectations based on inclusion proof request order.
511 ASSERT_TRUE(ExpectLeafIndexRequestAndThrottle(chain_, oldest_sct));
512 ASSERT_TRUE(
513 ExpectLeafIndexRequestAndThrottle(chain_, not_auditable_by_old_sth_sct));
514 ASSERT_TRUE(
515 ExpectLeafIndexRequestAndThrottle(chain_, newer_than_old_sth_sct));
516 CreateTreeTracker();
517
518 // Add SCTs in mixed order.
519 tree_tracker_->OnSCTVerified(chain_.get(), newer_than_new_sth_sct.get());
520 tree_tracker_->OnSCTVerified(chain_.get(), oldest_sct.get());
521 tree_tracker_->OnSCTVerified(chain_.get(),
522 not_auditable_by_new_sth_sct.get());
523 tree_tracker_->OnSCTVerified(chain_.get(), newer_than_old_sth_sct.get());
524 tree_tracker_->OnSCTVerified(chain_.get(),
525 not_auditable_by_old_sth_sct.get());
526
527 // Ensure all are in the PENDING_NEWER_STH state.
528 AssertSCTsMatchingState<SignedCertificateTimestamp*>(
529 tree_tracker_.get(), chain_.get(),
530 SingleTreeTracker::SCT_PENDING_NEWER_STH, oldest_sct.get(),
531 not_auditable_by_old_sth_sct.get(), newer_than_old_sth_sct.get(),
532 not_auditable_by_new_sth_sct.get(), newer_than_new_sth_sct.get());
533
534 // Provide the old STH, ensure only the oldest one is auditable.
535 tree_tracker_->NewSTHObserved(old_sth);
536 // Ensure all but the oldest are in the PENDING_NEWER_STH state.
537 AssertSCTsMatchingState<SignedCertificateTimestamp*>(
538 tree_tracker_.get(), chain_.get(),
539 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK, oldest_sct.get());
540
541 AssertSCTsMatchingState<SignedCertificateTimestamp*>(
542 tree_tracker_.get(), chain_.get(),
543 SingleTreeTracker::SCT_PENDING_NEWER_STH,
544 not_auditable_by_old_sth_sct.get(), newer_than_old_sth_sct.get(),
545 not_auditable_by_new_sth_sct.get(), newer_than_new_sth_sct.get());
546
547 // Provide the newer one, ensure two more are auditable but the
548 // rest aren't.
549 tree_tracker_->NewSTHObserved(new_sth);
550
551 AssertSCTsMatchingState<SignedCertificateTimestamp*>(
552 tree_tracker_.get(), chain_.get(),
553 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK, oldest_sct.get(),
554 not_auditable_by_old_sth_sct.get(), newer_than_old_sth_sct.get());
555
556 AssertSCTsMatchingState<SignedCertificateTimestamp*>(
557 tree_tracker_.get(), chain_.get(),
558 SingleTreeTracker::SCT_PENDING_NEWER_STH,
559 not_auditable_by_new_sth_sct.get(), newer_than_new_sth_sct.get());
560 }
561
562 // Test that if a request for an entry is throttled, it remains in a
563 // pending state.
564
565 // Test that if several entries are throttled, when the LogDnsClient notifies
566 // of un-throttling all entries are handled.
567 TEST_F(SingleTreeTrackerTest, TestThrottledEntryGetsHandledAfterUnthrottling) {
568 std::vector<std::string> audit_proof;
569 FillVectorWithValidAuditProofForTreeOfSize2(&audit_proof);
570
571 ASSERT_TRUE(mock_dns_.ExpectLeafIndexRequestAndResponse(
572 Base32LeafHash(chain_.get(), cert_sct_.get()) + ".hash." +
573 kDNSRequestSuffix,
574 0));
575 ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
576 std::string("0.0.2.tree.") + kDNSRequestSuffix, audit_proof.begin(),
577 audit_proof.begin() + 1));
578
579 scoped_refptr<SignedCertificateTimestamp> second_sct(GetSCT());
580 second_sct->timestamp -= base::TimeDelta::FromHours(1);
581
582 // Process request for |second_sct|
583 ASSERT_TRUE(mock_dns_.ExpectLeafIndexRequestAndResponse(
584 Base32LeafHash(chain_.get(), second_sct.get()) + ".hash." +
585 kDNSRequestSuffix,
586 1));
587 ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
588 std::string("0.1.2.tree.") + kDNSRequestSuffix, audit_proof.begin(),
589 audit_proof.begin() + 1));
590
591 CreateTreeTracker();
592
593 SignedTreeHead sth;
594 ASSERT_TRUE(GetSignedTreeHeadForTreeOfSize2(&sth));
595 tree_tracker_->NewSTHObserved(sth);
596
597 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
598 tree_tracker_->OnSCTVerified(chain_.get(), second_sct.get());
599
600 // Both entries should be in the pending state, the first because the
601 // LogDnsClient did not invoke the callback yet, the second one because
602 // the LogDnsClient is "busy" with the first entry and so would throttle.
603 ASSERT_EQ(
604 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
605 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
606 ASSERT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
607 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(),
608 second_sct.get()));
609
610 // Process pending DNS queries so later assertions are on handling
611 // of the entries based on replies received.
612 base::RunLoop().RunUntilIdle();
613
614 // Check that the first sct is included in the log.
615 ASSERT_EQ(
616 SingleTreeTracker::SCT_INCLUDED_IN_LOG,
617 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
618
619 // Check that the second SCT got an invalid proof and is not included, rather
620 // than being in the pending state.
621 ASSERT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
622 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(),
623 second_sct.get()));
624 }
625
626 // Test that proof fetching failure due to DNS config errors is handled
627 // correctly:
628 // (1) Entry removed from pending queue.
629 // (2) UMA logged
630 TEST_F(SingleTreeTrackerTest,
631 TestProofLookupDueToBadDNSConfigHandledCorrectly) {
632 base::HistogramTester histograms;
633 // Provide an STH to the tree_tracker_.
634 SignedTreeHead sth;
635 GetSampleSignedTreeHead(&sth);
636
637 // Clear existing DNS configuration, so that the DnsClient created
638 // by the MockLogDnsTraffic has no valid DnsConfig.
639 net_change_notifier_.reset();
640 net_change_notifier_ =
641 base::WrapUnique(net::NetworkChangeNotifier::CreateMock());
642 CreateTreeTracker();
643
644 tree_tracker_->NewSTHObserved(sth);
645 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
646
647 // Make sure the SCT status indicates the entry has been removed from
648 // the SingleTreeTracker's internal queue as the DNS lookup failed
649 // synchronously.
650 EXPECT_EQ(
651 SingleTreeTracker::SCT_NOT_OBSERVED,
652 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
653
654 // Exactly one value should be logged, indicating the SCT can be checked for
655 // inclusion, as |tree_tracker_| did have a valid STH when it was notified
656 // of a new SCT.
657 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
658 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 2, 1);
659 // Failure due to DNS configuration should be logged in the result histogram.
660 histograms.ExpectTotalCount(kInclusionCheckResultHistogramName, 1);
661 histograms.ExpectBucketCount(kInclusionCheckResultHistogramName, 3, 1);
662 }
663
202 } // namespace certificate_transparency 664 } // namespace certificate_transparency
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698