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

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