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

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: Simplified test. 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);
Ryan Sleevi 2016/12/22 21:50:28 constexpr base::TimeDelta kMoreThanMMD - avoid sta
Eran Messeri 2017/01/03 23:07:41 Done.
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);
Ryan Sleevi 2016/12/22 21:50:28 sizeof returns a size_t, not an int. However, you
Eran Messeri 2017/01/03 23:07:41 I tried, but couldn't get it to work, for two reas
Ryan Sleevi 2017/01/10 03:15:53 template <size_t N> void AssertSCTsMatchingState(.
Eran Messeri 2017/01/23 16:45:47 N/A anymore.
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_.reset(net::NetworkChangeNotifier::CreateMock());
185 mock_dns_.InitializeDnsConfig();
71 } 186 }
72 187
73 protected: 188 protected:
189 void CreateTreeTracker() {
190 log_dns_client_ = base::MakeUnique<LogDnsClient>(
191 mock_dns_.CreateDnsClient(), net_log_, 1);
192
193 tree_tracker_.reset(new SingleTreeTracker(log_, log_dns_client_.get()));
194 }
195
196 void CreateTreeTrackerWithDefaultDnsExpectation() {
197 // Default to throttling requests as it means observed log entries will
198 // be frozen in a pending state, simplifying testing of the
199 // SingleTreeTracker.
200 ASSERT_TRUE(ExpectLeafIndexRequestAndThrottle(chain_, cert_sct_));
201 CreateTreeTracker();
202 }
203
204 // Configured the |mock_dns_| to expect a request for the leaf index
205 // and have th mock DNS client throttle it.
206 bool ExpectLeafIndexRequestAndThrottle(
207 const scoped_refptr<net::X509Certificate>& chain,
208 const scoped_refptr<SignedCertificateTimestamp>& sct) {
209 return mock_dns_.ExpectRequestAndSocketError(
210 Base32LeafHash(chain.get(), sct.get()) + ".hash." + kDNSRequestSuffix,
211 net::Error::ERR_TEMPORARILY_THROTTLED);
212 }
213
214 base::MessageLoopForIO message_loop_;
215 MockLogDnsTraffic mock_dns_;
74 scoped_refptr<const net::CTLogVerifier> log_; 216 scoped_refptr<const net::CTLogVerifier> log_;
217 std::unique_ptr<net::NetworkChangeNotifier> net_change_notifier_;
218 std::unique_ptr<LogDnsClient> log_dns_client_;
75 std::unique_ptr<SingleTreeTracker> tree_tracker_; 219 std::unique_ptr<SingleTreeTracker> tree_tracker_;
76 scoped_refptr<net::X509Certificate> chain_; 220 scoped_refptr<net::X509Certificate> chain_;
77 scoped_refptr<net::ct::SignedCertificateTimestamp> cert_sct_; 221 scoped_refptr<SignedCertificateTimestamp> cert_sct_;
222 net::NetLogWithSource net_log_;
78 }; 223 };
79 224
80 // Test that an SCT is classified as pending for a newer STH if the 225 // Test that an SCT is classified as pending for a newer STH if the
81 // SingleTreeTracker has not seen any STHs so far. 226 // SingleTreeTracker has not seen any STHs so far.
82 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTNoSTH) { 227 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTNoSTH) {
228 CreateTreeTrackerWithDefaultDnsExpectation();
229
83 base::HistogramTester histograms; 230 base::HistogramTester histograms;
84 // First make sure the SCT has not been observed at all. 231 // First make sure the SCT has not been observed at all.
85 EXPECT_EQ( 232 EXPECT_EQ(
86 SingleTreeTracker::SCT_NOT_OBSERVED, 233 SingleTreeTracker::SCT_NOT_OBSERVED,
87 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 234 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
88 235
89 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); 236 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
90 237
91 // Since no STH was provided to the tree_tracker_ the status should be that 238 // Since no STH was provided to the tree_tracker_ the status should be that
92 // the SCT is pending a newer STH. 239 // the SCT is pending a newer STH.
93 EXPECT_EQ( 240 EXPECT_EQ(
94 SingleTreeTracker::SCT_PENDING_NEWER_STH, 241 SingleTreeTracker::SCT_PENDING_NEWER_STH,
95 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 242 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
96 243
97 // Expect logging of a value indicating a valid STH is required. 244 // Expect logging of a value indicating a valid STH is required.
98 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1); 245 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
99 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 0, 1); 246 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 0, 1);
100 } 247 }
101 248
102 // Test that an SCT is classified as pending an inclusion check if the 249 // Test that an SCT is classified as pending an inclusion check if the
103 // SingleTreeTracker has a fresh-enough STH to check inclusion against. 250 // SingleTreeTracker has a fresh-enough STH to check inclusion against.
104 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTWithRecentSTH) { 251 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTWithRecentSTH) {
252 CreateTreeTrackerWithDefaultDnsExpectation();
253
105 base::HistogramTester histograms; 254 base::HistogramTester histograms;
106 // Provide an STH to the tree_tracker_. 255 // Provide an STH to the tree_tracker_.
107 net::ct::SignedTreeHead sth; 256 SignedTreeHead sth;
108 net::ct::GetSampleSignedTreeHead(&sth); 257 GetSampleSignedTreeHead(&sth);
109 tree_tracker_->NewSTHObserved(sth); 258 tree_tracker_->NewSTHObserved(sth);
110 259
111 // Make sure the SCT status is the same as if there's no STH for 260 // Make sure the SCT status is the same as if there's no STH for
112 // this log. 261 // this log.
113 EXPECT_EQ( 262 EXPECT_EQ(
114 SingleTreeTracker::SCT_NOT_OBSERVED, 263 SingleTreeTracker::SCT_NOT_OBSERVED,
115 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 264 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
116 265
117 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); 266 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
118 267
119 // The status for this SCT should be 'pending inclusion check' since the STH 268 // 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. 269 // provided at the beginning of the test is newer than the SCT.
121 EXPECT_EQ( 270 EXPECT_EQ(
122 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK, 271 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
123 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 272 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
124 273
125 // Exactly one value should be logged, indicating the SCT can be checked for 274 // 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 275 // inclusion, as |tree_tracker_| did have a valid STH when it was notified
127 // of a new SCT. 276 // of a new SCT.
128 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1); 277 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
129 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 2, 1); 278 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 2, 1);
279 // Nothing should be logged in the result histogram since inclusion check
280 // didn't finish.
281 histograms.ExpectTotalCount(kInclusionCheckResultHistogramName, 0);
130 } 282 }
131 283
132 // Test that the SingleTreeTracker correctly queues verified SCTs for inclusion 284 // 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 285 // checking such that, upon receiving a fresh STH, it changes the SCT's status
134 // from pending newer STH to pending inclusion check. 286 // from pending newer STH to pending inclusion check.
135 TEST_F(SingleTreeTrackerTest, CorrectlyUpdatesSCTStatusOnNewSTH) { 287 TEST_F(SingleTreeTrackerTest, CorrectlyUpdatesSCTStatusOnNewSTH) {
288 CreateTreeTrackerWithDefaultDnsExpectation();
289
136 base::HistogramTester histograms; 290 base::HistogramTester histograms;
137 // Report an observed SCT and make sure it's in the pending newer STH 291 // Report an observed SCT and make sure it's in the pending newer STH
138 // state. 292 // state.
139 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); 293 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
140 EXPECT_EQ( 294 EXPECT_EQ(
141 SingleTreeTracker::SCT_PENDING_NEWER_STH, 295 SingleTreeTracker::SCT_PENDING_NEWER_STH,
142 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 296 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
143 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1); 297 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
144 298
145 // Provide with a fresh STH 299 // Provide with a fresh STH
146 net::ct::SignedTreeHead sth; 300 SignedTreeHead sth;
147 net::ct::GetSampleSignedTreeHead(&sth); 301 GetSampleSignedTreeHead(&sth);
148 tree_tracker_->NewSTHObserved(sth); 302 tree_tracker_->NewSTHObserved(sth);
149 303
150 // Test that its status has changed. 304 // Test that its status has changed.
151 EXPECT_EQ( 305 EXPECT_EQ(
152 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK, 306 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
153 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get())); 307 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
154 // Check that no additional UMA was logged for this case as the histogram is 308 // 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 309 // only supposed to measure the state of newly-observed SCTs, not pending
156 // ones. 310 // ones.
157 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1); 311 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
158 } 312 }
159 313
160 // Test that the SingleTreeTracker does not change an SCT's status if an STH 314 // 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 315 // from the log it was issued by is observed, but that STH is too old to check
162 // inclusion against. 316 // inclusion against.
163 TEST_F(SingleTreeTrackerTest, DoesNotUpdatesSCTStatusOnOldSTH) { 317 TEST_F(SingleTreeTrackerTest, DoesNotUpdatesSCTStatusOnOldSTH) {
318 CreateTreeTrackerWithDefaultDnsExpectation();
319
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) {
341 CreateTreeTrackerWithDefaultDnsExpectation();
342
185 base::HistogramTester histograms; 343 base::HistogramTester histograms;
186 // Provide an old STH for the same log. 344 // Provide an old STH for the same log.
187 net::ct::SignedTreeHead sth; 345 SignedTreeHead sth;
188 GetOldSignedTreeHead(&sth); 346 GetOldSignedTreeHead(&sth);
189 tree_tracker_->NewSTHObserved(sth); 347 tree_tracker_->NewSTHObserved(sth);
190 348
191 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 0); 349 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 0);
192 350
193 // Notify of an SCT and make sure it's in the 'pending newer STH' state. 351 // Notify of an SCT and make sure it's in the 'pending newer STH' state.
194 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get()); 352 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
195 353
196 // Exactly one value should be logged, indicating the SCT cannot be checked 354 // Exactly one value should be logged, indicating the SCT cannot be checked
197 // for inclusion as the STH is too old. 355 // for inclusion as the STH is too old.
198 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1); 356 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
199 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 1, 1); 357 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 1, 1);
200 } 358 }
201 359
360 // Test that an entry transitions to the "not found" state if the LogDnsClient
361 // fails to get a leaf index.
362 TEST_F(SingleTreeTrackerTest, TestEntryNotPendingAfterLeafIndexFetchFailure) {
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 // Return 12 as the index of this leaf.
390 ASSERT_TRUE(mock_dns_.ExpectLeafIndexRequestAndResponse(
391 Base32LeafHash(chain_.get(), cert_sct_.get()) + ".hash." +
392 kDNSRequestSuffix,
393 12));
394 // Expect a request for an inclusion proof for leaf #12 in a tree of size
395 // 21, which is the size of the tree in the STH returned by
396 // GetSampleSignedTreeHead.
397 ASSERT_TRUE(mock_dns_.ExpectRequestAndSocketError(
398 std::string("0.12.21.tree.") + kDNSRequestSuffix,
399 net::Error::ERR_FAILED));
400
401 CreateTreeTracker();
402
403 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
404 EXPECT_EQ(
405 SingleTreeTracker::SCT_PENDING_NEWER_STH,
406 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
407
408 // Provide with a fresh STH
409 SignedTreeHead sth;
410 GetSampleSignedTreeHead(&sth);
411 tree_tracker_->NewSTHObserved(sth);
412 base::RunLoop().RunUntilIdle();
413
414 EXPECT_EQ(
415 SingleTreeTracker::SCT_NOT_OBSERVED,
416 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
417 }
418
419 // Test that an entry transitions to the "included" state if the LogDnsClient
420 // succeeds to get a leaf index and an inclusion proof.
421 TEST_F(SingleTreeTrackerTest, TestEntryIncludedAfterInclusionCheckSuccess) {
422 std::vector<std::string> audit_proof;
423 FillVectorWithValidAuditProofForTreeOfSize2(&audit_proof);
424
425 // Return 0 as the index for this leaf, so the proof provided
426 // later on would verify.
427 ASSERT_TRUE(mock_dns_.ExpectLeafIndexRequestAndResponse(
428 Base32LeafHash(chain_.get(), cert_sct_.get()) + ".hash." +
429 kDNSRequestSuffix,
430 0));
431 // The STH (later on) is for a tree of size 2 and the entry has index 0
432 // in the tree, so expect an inclusion proof for entry 0 in a tree
433 // of size 2 (0.0.2).
434 ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
435 std::string("0.0.2.tree.") + kDNSRequestSuffix, audit_proof.begin(),
436 audit_proof.begin() + 1));
437
438 CreateTreeTracker();
439
440 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
441 EXPECT_EQ(
442 SingleTreeTracker::SCT_PENDING_NEWER_STH,
443 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
444
445 // Provide with a fresh STH, which is for a tree of size 2.
446 SignedTreeHead sth;
447 ASSERT_TRUE(GetSignedTreeHeadForTreeOfSize2(&sth));
448 ASSERT_TRUE(log_->VerifySignedTreeHead(sth));
449
450 tree_tracker_->NewSTHObserved(sth);
451 base::RunLoop().RunUntilIdle();
452
453 EXPECT_EQ(
454 SingleTreeTracker::SCT_INCLUDED_IN_LOG,
455 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
456 }
457
458 // Test that pending entries transition states correctly according to the
459 // STHs provided:
460 // * Start without an STH.
461 // * Add a collection of entries with mixed timestamps (i.e. SCTs not added
462 // in the order of their timestamps).
463 // * Provide an STH that covers some of the entries, test these are audited.
464 // * Provide another STH that covers more of the entries, test that the entries
465 // already audited are not audited again and that those that need to be
466 // audited are audited, while those that are not covered by that STH are
467 // not audited.
468 TEST_F(SingleTreeTrackerTest, TestMultipleEntriesTransitionStateCorrectly) {
469 SignedTreeHead old_sth;
470 GetOldSignedTreeHead(&old_sth);
471
472 SignedTreeHead new_sth;
473 GetSampleSignedTreeHead(&new_sth);
474
475 base::TimeDelta kLessThanMMD = base::TimeDelta::FromHours(23);
476
477 // Assert the gap between the two timestamps is big enough so that
478 // all assumptions below on which SCT can be audited with the
479 // new STH are true.
480 ASSERT_LT(old_sth.timestamp + (kMoreThanMMD * 2), new_sth.timestamp);
481
482 // Oldest SCT - auditable by the old and new STHs.
483 scoped_refptr<SignedCertificateTimestamp> oldest_sct(GetSCT());
484 oldest_sct->timestamp = old_sth.timestamp - kMoreThanMMD;
485
486 // SCT that's older than the old STH's timestamp but by less than the MMD,
487 // so not auditable by old STH.
488 scoped_refptr<SignedCertificateTimestamp> not_auditable_by_old_sth_sct(
489 GetSCT());
490 not_auditable_by_old_sth_sct->timestamp = old_sth.timestamp - kLessThanMMD;
491
492 // SCT that's newer than the old STH's timestamp so is only auditable by
493 // the new STH.
494 scoped_refptr<SignedCertificateTimestamp> newer_than_old_sth_sct(GetSCT());
495 newer_than_old_sth_sct->timestamp = old_sth.timestamp + kLessThanMMD;
496
497 // SCT that's older than the new STH's timestamp but by less than the MMD,
498 // so isn't auditable by the new STH.
499 scoped_refptr<SignedCertificateTimestamp> not_auditable_by_new_sth_sct(
500 GetSCT());
501 not_auditable_by_new_sth_sct->timestamp = new_sth.timestamp - kLessThanMMD;
502
503 // SCT that's newer than the new STH's timestamp so isn't auditable by the
504 // the new STH.
505 scoped_refptr<SignedCertificateTimestamp> newer_than_new_sth_sct(GetSCT());
506 newer_than_new_sth_sct->timestamp = new_sth.timestamp + kLessThanMMD;
507
508 // Set up DNS expectations based on inclusion proof request order.
509 ASSERT_TRUE(ExpectLeafIndexRequestAndThrottle(chain_, oldest_sct));
510 ASSERT_TRUE(
511 ExpectLeafIndexRequestAndThrottle(chain_, not_auditable_by_old_sth_sct));
512 ASSERT_TRUE(
513 ExpectLeafIndexRequestAndThrottle(chain_, newer_than_old_sth_sct));
514 CreateTreeTracker();
515
516 // Add SCTs in mixed order.
517 tree_tracker_->OnSCTVerified(chain_.get(), newer_than_new_sth_sct.get());
518 tree_tracker_->OnSCTVerified(chain_.get(), oldest_sct.get());
519 tree_tracker_->OnSCTVerified(chain_.get(),
520 not_auditable_by_new_sth_sct.get());
521 tree_tracker_->OnSCTVerified(chain_.get(), newer_than_old_sth_sct.get());
522 tree_tracker_->OnSCTVerified(chain_.get(),
523 not_auditable_by_old_sth_sct.get());
524
525 // Ensure all are in the PENDING_NEWER_STH state.
526 AssertSCTsMatchingState<SignedCertificateTimestamp*>(
Ryan Sleevi 2017/01/10 03:15:53 Does it make sense to continue the test if this pr
Eran Messeri 2017/01/23 16:45:47 Done.
527 tree_tracker_.get(), chain_.get(),
528 SingleTreeTracker::SCT_PENDING_NEWER_STH, oldest_sct.get(),
529 not_auditable_by_old_sth_sct.get(), newer_than_old_sth_sct.get(),
530 not_auditable_by_new_sth_sct.get(), newer_than_new_sth_sct.get());
531
532 // Provide the old STH, ensure only the oldest one is auditable.
533 tree_tracker_->NewSTHObserved(old_sth);
534 // Ensure all but the oldest are in the PENDING_NEWER_STH state.
535 AssertSCTsMatchingState<SignedCertificateTimestamp*>(
536 tree_tracker_.get(), chain_.get(),
537 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK, oldest_sct.get());
538
539 AssertSCTsMatchingState<SignedCertificateTimestamp*>(
540 tree_tracker_.get(), chain_.get(),
541 SingleTreeTracker::SCT_PENDING_NEWER_STH,
542 not_auditable_by_old_sth_sct.get(), newer_than_old_sth_sct.get(),
543 not_auditable_by_new_sth_sct.get(), newer_than_new_sth_sct.get());
544
545 // Provide the newer one, ensure two more are auditable but the
546 // rest aren't.
547 tree_tracker_->NewSTHObserved(new_sth);
548
549 AssertSCTsMatchingState<SignedCertificateTimestamp*>(
550 tree_tracker_.get(), chain_.get(),
551 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK, oldest_sct.get(),
552 not_auditable_by_old_sth_sct.get(), newer_than_old_sth_sct.get());
553
554 AssertSCTsMatchingState<SignedCertificateTimestamp*>(
555 tree_tracker_.get(), chain_.get(),
556 SingleTreeTracker::SCT_PENDING_NEWER_STH,
557 not_auditable_by_new_sth_sct.get(), newer_than_new_sth_sct.get());
558 }
559
560 // Test that if a request for an entry is throttled, it remains in a
561 // pending state.
562
563 // Test that if several entries are throttled, when the LogDnsClient notifies
564 // of un-throttling all entries are handled.
565 TEST_F(SingleTreeTrackerTest, TestThrottledEntryGetsHandledAfterUnthrottling) {
566 std::vector<std::string> audit_proof;
567 FillVectorWithValidAuditProofForTreeOfSize2(&audit_proof);
568
569 ASSERT_TRUE(mock_dns_.ExpectLeafIndexRequestAndResponse(
570 Base32LeafHash(chain_.get(), cert_sct_.get()) + ".hash." +
571 kDNSRequestSuffix,
572 0));
573 ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
574 std::string("0.0.2.tree.") + kDNSRequestSuffix, audit_proof.begin(),
575 audit_proof.begin() + 1));
576
577 scoped_refptr<SignedCertificateTimestamp> second_sct(GetSCT());
578 second_sct->timestamp -= base::TimeDelta::FromHours(1);
579
580 // Process request for |second_sct|
581 ASSERT_TRUE(mock_dns_.ExpectLeafIndexRequestAndResponse(
582 Base32LeafHash(chain_.get(), second_sct.get()) + ".hash." +
583 kDNSRequestSuffix,
584 1));
585 ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
586 std::string("0.1.2.tree.") + kDNSRequestSuffix, audit_proof.begin(),
587 audit_proof.begin() + 1));
588
589 CreateTreeTracker();
590
591 SignedTreeHead sth;
592 ASSERT_TRUE(GetSignedTreeHeadForTreeOfSize2(&sth));
593 tree_tracker_->NewSTHObserved(sth);
594
595 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
596 tree_tracker_->OnSCTVerified(chain_.get(), second_sct.get());
597
598 // Both entries should be in the pending state, the first because the
599 // LogDnsClient did not invoke the callback yet, the second one because
600 // the LogDnsClient is "busy" with the first entry and so would throttle.
601 ASSERT_EQ(
602 SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
603 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
604 ASSERT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
605 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(),
606 second_sct.get()));
607
608 // Process pending DNS queries so later assertions are on handling
609 // of the entries based on replies received.
610 base::RunLoop().RunUntilIdle();
611
612 // Check that the first sct is included in the log.
613 ASSERT_EQ(
614 SingleTreeTracker::SCT_INCLUDED_IN_LOG,
615 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
616
617 // Check that the second SCT got an invalid proof and is not included, rather
618 // than being in the pending state.
619 ASSERT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
620 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(),
621 second_sct.get()));
622 }
623
624 // Test that proof fetching failure due to DNS config errors is handled
625 // correctly:
626 // (1) Entry removed from pending queue.
627 // (2) UMA logged
628 TEST_F(SingleTreeTrackerTest,
629 TestProofLookupDueToBadDNSConfigHandledCorrectly) {
630 base::HistogramTester histograms;
631 // Provide an STH to the tree_tracker_.
632 SignedTreeHead sth;
633 GetSampleSignedTreeHead(&sth);
634
635 // Clear existing DNS configuration, so that the DnsClient created
636 // by the MockLogDnsTraffic has no valid DnsConfig.
637 net_change_notifier_.reset();
638 net_change_notifier_.reset(net::NetworkChangeNotifier::CreateMock());
639 CreateTreeTracker();
640
641 tree_tracker_->NewSTHObserved(sth);
642 tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
643
644 // Make sure the SCT status indicates the entry has been removed from
645 // the SingleTreeTracker's internal queue as the DNS lookup failed
646 // synchronously.
647 EXPECT_EQ(
648 SingleTreeTracker::SCT_NOT_OBSERVED,
649 tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
650
651 // Exactly one value should be logged, indicating the SCT can be checked for
652 // inclusion, as |tree_tracker_| did have a valid STH when it was notified
653 // of a new SCT.
654 histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
655 histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 2, 1);
656 // Failure due to DNS configuration should be logged in the result histogram.
657 histograms.ExpectTotalCount(kInclusionCheckResultHistogramName, 1);
658 histograms.ExpectBucketCount(kInclusionCheckResultHistogramName, 3, 1);
659 }
660
202 } // namespace certificate_transparency 661 } // namespace certificate_transparency
OLDNEW
« no previous file with comments | « components/certificate_transparency/single_tree_tracker.cc ('k') | components/certificate_transparency/tree_state_tracker.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698