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

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

Powered by Google App Engine
This is Rietveld 408576698