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

Side by Side Diff: components/certificate_transparency/single_tree_tracker.cc

Issue 2017563002: Add Certificate Transparency logs auditing (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Simplified STT with throttling, memory pressure handling Created 4 years, 3 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 <algorithm>
8 #include <iterator>
9 #include <list>
7 #include <utility> 10 #include <utility>
8 11
12 #include "base/bind.h"
9 #include "base/metrics/histogram_macros.h" 13 #include "base/metrics/histogram_macros.h"
14 #include "components/certificate_transparency/log_dns_client.h"
15 #include "net/base/hash_value.h"
16 #include "net/base/net_errors.h"
10 #include "net/cert/ct_log_verifier.h" 17 #include "net/cert/ct_log_verifier.h"
18 #include "net/cert/merkle_audit_proof.h"
19 #include "net/cert/merkle_tree_leaf.h"
11 #include "net/cert/signed_certificate_timestamp.h" 20 #include "net/cert/signed_certificate_timestamp.h"
12 #include "net/cert/x509_certificate.h" 21 #include "net/cert/x509_certificate.h"
13 22
23 using net::ct::LogEntry;
24 using net::ct::MerkleTreeLeaf;
25 using net::ct::SignedCertificateTimestamp;
14 using net::ct::SignedTreeHead; 26 using net::ct::SignedTreeHead;
15 27
16 namespace { 28 namespace {
17 29
18 // Enum indicating whether an SCT can be checked for inclusion and if not, 30 // Enum indicating whether an SCT can be checked for inclusion and if not,
19 // the reason it cannot. 31 // the reason it cannot.
20 // 32 //
21 // Note: The numeric values are used within a histogram and should not change 33 // Note: The numeric values are used within a histogram and should not change
22 // or be re-assigned. 34 // or be re-assigned.
23 enum SCTCanBeCheckedForInclusion { 35 enum SCTCanBeCheckedForInclusion {
24 // If the SingleTreeTracker does not have a valid STH, then a valid STH is 36 // If the SingleTreeTracker does not have a valid STH, then a valid STH is
25 // first required to evaluate whether the SCT can be checked for inclusion 37 // first required to evaluate whether the SCT can be checked for inclusion
26 // or not. 38 // or not.
27 VALID_STH_REQUIRED = 0, 39 VALID_STH_REQUIRED = 0,
28 // If the STH does not cover the SCT (the timestamp in the SCT is greater than 40 // If the STH does not cover the SCT (the timestamp in the SCT is greater than
29 // MMD + timestamp in the STH), then a newer STH is needed. 41 // MMD + timestamp in the STH), then a newer STH is needed.
30 NEWER_STH_REQUIRED = 1, 42 NEWER_STH_REQUIRED = 1,
31 // When an SCT is observed, if the SingleTreeTracker instance has a valid STH 43 // When an SCT is observed, if the SingleTreeTracker instance has a valid STH
32 // and the STH covers the SCT (the timestamp in the SCT is less than MMD + 44 // and the STH covers the SCT (the timestamp in the SCT is less than MMD +
33 // timestamp in the STH), then it can be checked for inclusion. 45 // timestamp in the STH), then it can be checked for inclusion.
34 CAN_BE_CHECKED = 2, 46 CAN_BE_CHECKED = 2,
47 // This SCT was not audited because the queue of pending entries was
48 // full.
49 NOT_AUDITED = 3,
35 SCT_CAN_BE_CHECKED_MAX 50 SCT_CAN_BE_CHECKED_MAX
36 }; 51 };
37 52
38 // Measure how often clients encounter very new SCTs, by measuring whether an 53 // Measure how often clients encounter very new SCTs, by measuring whether an
39 // SCT can be checked for inclusion upon first observation. 54 // SCT can be checked for inclusion upon first observation.
40 void LogCanBeCheckedForInclusionToUMA( 55 void LogCanBeCheckedForInclusionToUMA(
41 SCTCanBeCheckedForInclusion can_be_checked) { 56 SCTCanBeCheckedForInclusion can_be_checked) {
42 UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.CanInclusionCheckSCT", 57 UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.CanInclusionCheckSCT",
43 can_be_checked, SCT_CAN_BE_CHECKED_MAX); 58 can_be_checked, SCT_CAN_BE_CHECKED_MAX);
44 } 59 }
45 60
61 // Return the leaf hash of the the entry in the log represented by
62 // the given |cert| and |sct|. If leaf hash calculation failed, returns
63 // an empty string.
64 std::string GetLogEntryLeafHash(const net::X509Certificate* cert,
65 const SignedCertificateTimestamp* sct) {
66 MerkleTreeLeaf leaf;
67 if (!GetMerkleTreeLeaf(cert, sct, &leaf))
68 return std::string("");
Ryan Sleevi 2016/09/22 07:08:29 std::string()
Eran Messeri 2016/09/22 20:10:21 Done.
69
70 std::string leaf_hash;
71 if (!HashMerkleTreeLeaf(leaf, &leaf_hash))
72 return std::string("");
Ryan Sleevi 2016/09/22 07:08:28 std::string()
Eran Messeri 2016/09/22 20:10:21 Done.
73
74 return leaf_hash;
75 }
76
77 // Audit state of a log entry.
78 enum AuditState {
79 // Entry cannot be audited because a newer STH is needed.
80 PENDING_NEWER_STH,
81 // The entry is pending request of its leaf index.
82 PENDING_LEAF_INDEX_REQUEST,
83 // The leaf index of this entry has been requested from the log.
84 LEAF_INDEX_REQUESTED,
85 // A leaf index has been obtained and the entry is now pending request
86 // of an inclusion proof.
87 PENDING_INCLUSION_PROOF_REQUEST,
88 // An inclusion proof for this entry has been requested from the log.
89 INCLUSION_PROOF_REQUESTED
90 };
Ryan Sleevi 2016/09/22 07:08:28 Unfortunately, I found this "spread-apart" state a
Eran Messeri 2016/09/22 20:10:20 Acknowledged; Having the state transitions in one
91
92 // Maximal size of the checked entries cache.
93 size_t kCheckedEntriesCacheSize = 100;
94
95 // Maximal size of the pending entries queue.
96 size_t kPendingEntriesQueueSize = 100;
97
98 // Maximum Merge Delay - logs can have individual MMD, but all known logs
99 // currently have 24 hours MMD and Chrome's CT policy requires an MMD
100 // that's no greater than that. For simplicity, use 24 hours for all logs.
101 int kMaximumMergeDelayInHours = 24;
102
103 // The log MUST incorporate the a certificate in the tree within the Maximum
104 // Merge Delay, so an entry can be audited once the timestamp from the SCT +
105 // MMD has passed.
106 // Returns true if the timestamp from the STH is newer than SCT timestamp + MMD.
107 bool IsSCTReadyForAudit(base::Time sth_timestamp, base::Time sct_timestamp) {
108 static const base::TimeDelta kMaximumMergeDelay =
109 base::TimeDelta::FromHours(kMaximumMergeDelayInHours);
Ryan Sleevi 2016/09/22 07:08:28 Why do this as a function-level static? Why not ju
Eran Messeri 2016/09/22 20:10:20 Done; haven't noticed the exception to https://goo
110 return sct_timestamp + kMaximumMergeDelay < sth_timestamp;
111 }
112
46 } // namespace 113 } // namespace
47 114
48 namespace certificate_transparency { 115 namespace certificate_transparency {
116 // The entry that is being audited. Immutable - once created, the entry
117 // to audit does not change.
118 struct SingleTreeTracker::EntryToAudit {
119 const base::Time sct_timestamp;
120 const std::string leaf_hash;
Ryan Sleevi 2016/09/22 07:08:28 Why string rather than SHA256HashValue? (see a bun
Eran Messeri 2016/09/22 20:10:21 Done.
121
122 // Returns true if the leaf hash was calculated correctly and is valid, false
123 // otherwise.
124 bool is_valid() const { return !leaf_hash.empty(); }
125
126 EntryToAudit(net::X509Certificate* cert,
127 const net::ct::SignedCertificateTimestamp* sct)
128 : sct_timestamp(sct->timestamp),
129 leaf_hash(GetLogEntryLeafHash(cert, sct)) {}
Ryan Sleevi 2016/09/22 07:08:28 https://google.github.io/styleguide/cppguide.html#
Eran Messeri 2016/09/22 20:10:21 Done - changed GetLogEntryLeafHash to be called be
130 };
131
132 // State of a log entry: its audit state and information necessary to
133 // validate an inclusion proof. Gets updated as the entry transitions
134 // between the different audit states.
135 struct SingleTreeTracker::EntryAuditState {
136 // Current phase of inclusion check.
137 AuditState state;
138 // The index of this entry in the log. Only valid if |state| is
139 // PENDING_INCLUSION_PROOF_REQUEST or INCLUSION_PROOF_REQUESTED
140 uint64_t leaf_index;
141 // The size of the tree for which an inclusion proof was requested.
142 // It is copied from the verified STH in the SingleTreeTracker when the
143 // leaf index is requested so that if a newer STH is provided to the
144 // SingleTreeTracker while the inclusion proof is fetched, it won't be
145 // necessary to re-fetch it.
Ryan Sleevi 2016/09/22 07:08:29 "it won't be necessary to re-fetch it" reads weird
Eran Messeri 2016/09/22 20:10:20 Done - I've re-worded that comment a bit. The gist
146 // Valid when |leaf_index| is valid or when |state| is LEAF_INDEX_REQUESTED.
147 uint64_t tree_size;
148 // TODO(eranm): Add a root_hash field that's copied from the verified STH
149 // for the same reason |tree_size| is copied, when the inclusion proof
150 // checking code lands and we can use it.
151 // See https://codereview.chromium.org/2182533002/ and
152 // https://crbug.com/631087
153
154 EntryAuditState(AuditState state)
155 : state(state), leaf_index(0), tree_size(0) {}
156 };
157
158 // Orders entries by the SCT timestamp. In case of tie, which is very unlikely
159 // as it requires two SCTs issued from a log at exactly the same time, order
160 // by leaf hash.
161 bool SingleTreeTracker::OrderByTimestamp::operator()(
162 const EntryToAudit& lhs,
163 const EntryToAudit& rhs) const {
164 if (lhs.sct_timestamp != rhs.sct_timestamp)
165 return lhs.sct_timestamp < rhs.sct_timestamp;
166
167 // TODO(eranm): Copying the hashes just for comparison may be avoided by
168 // using SHA256HashValue as the type for EntryToAudit.leaf_hash.
169 // However as this code should be rarely executed and std::string is easier
170 // to work with, that type is chosen for |leaf_hash|.
Ryan Sleevi 2016/09/22 07:08:28 It's like you already knew where my objection was
Eran Messeri 2016/09/22 20:10:21 Done - EntryToAudit now holds a SHA256HashValue.
171 net::SHA256HashValue lhs_hash, rhs_hash;
172 memcpy(lhs_hash.data, lhs.leaf_hash.c_str(),
173 std::min(32ul, lhs.leaf_hash.size()));
Ryan Sleevi 2016/09/22 07:08:28 When is it ever valid for leaf_hash.size() to != 3
Eran Messeri 2016/09/22 20:10:20 Done - obsolete - I can now use SHA256HashValueLes
174 memcpy(rhs_hash.data, rhs.leaf_hash.c_str(),
175 std::min(32ul, rhs.leaf_hash.size()));
176 return net::SHA256HashValueLessThan().operator()(lhs_hash, rhs_hash);
177 }
49 178
50 SingleTreeTracker::SingleTreeTracker( 179 SingleTreeTracker::SingleTreeTracker(
51 scoped_refptr<const net::CTLogVerifier> ct_log) 180 scoped_refptr<const net::CTLogVerifier> ct_log,
52 : ct_log_(std::move(ct_log)) {} 181 LogDnsClient* dns_client)
182 : ct_log_(std::move(ct_log)),
183 checked_entries_(kCheckedEntriesCacheSize),
184 dns_client_(dns_client),
185 weak_factory_(this) {
186 memory_pressure_listener_.reset(new base::MemoryPressureListener(base::Bind(
187 &SingleTreeTracker::OnMemoryPressure, base::Unretained(this))));
188 }
53 189
54 SingleTreeTracker::~SingleTreeTracker() {} 190 SingleTreeTracker::~SingleTreeTracker() {}
55 191
56 void SingleTreeTracker::OnSCTVerified( 192 void SingleTreeTracker::OnSCTVerified(
57 net::X509Certificate* cert, 193 net::X509Certificate* cert,
58 const net::ct::SignedCertificateTimestamp* sct) { 194 const net::ct::SignedCertificateTimestamp* sct) {
59 DCHECK_EQ(ct_log_->key_id(), sct->log_id); 195 DCHECK_EQ(ct_log_->key_id(), sct->log_id);
60 196
61 // SCT was previously observed, so its status should not be changed. 197 EntryToAudit entry(cert, sct);
62 if (entries_status_.find(sct->timestamp) != entries_status_.end()) 198 if (!entry.is_valid())
63 return; 199 return;
64 200
201 // Enqueue an entry for auditing if it was not previously observed
202 // and there's space in the queue.
203 if (GetLogEntryInclusionStatus(entry) != SCT_NOT_OBSERVED) {
204 ProcessPendingEntries();
205 return;
206 }
207
208 if (pending_entries_.size() >= kPendingEntriesQueueSize) {
209 // Queue is full - cannot audit SCT.
210 LogCanBeCheckedForInclusionToUMA(NOT_AUDITED);
211 ProcessPendingEntries();
212 return;
213 }
214
65 // If there isn't a valid STH or the STH is not fresh enough to check 215 // If there isn't a valid STH or the STH is not fresh enough to check
66 // inclusion against, store the SCT for future checking and return. 216 // inclusion against, store the SCT for future checking and return.
67 if (verified_sth_.timestamp.is_null() || 217 if (verified_sth_.timestamp.is_null() ||
68 (verified_sth_.timestamp < 218 !IsSCTReadyForAudit(verified_sth_.timestamp, entry.sct_timestamp)) {
69 (sct->timestamp + base::TimeDelta::FromHours(24)))) { 219 pending_entries_.insert(
70 entries_status_.insert( 220 std::make_pair(std::move(entry), EntryAuditState(PENDING_NEWER_STH)));
71 std::make_pair(sct->timestamp, SCT_PENDING_NEWER_STH));
72 221
73 if (!verified_sth_.timestamp.is_null()) { 222 if (verified_sth_.timestamp.is_null()) {
223 LogCanBeCheckedForInclusionToUMA(VALID_STH_REQUIRED);
224 } else {
74 LogCanBeCheckedForInclusionToUMA(NEWER_STH_REQUIRED); 225 LogCanBeCheckedForInclusionToUMA(NEWER_STH_REQUIRED);
75 } else {
76 LogCanBeCheckedForInclusionToUMA(VALID_STH_REQUIRED);
77 } 226 }
78 227 } else {
79 return; 228 LogCanBeCheckedForInclusionToUMA(CAN_BE_CHECKED);
229 pending_entries_.insert(std::make_pair(
230 std::move(entry), EntryAuditState(PENDING_LEAF_INDEX_REQUEST)));
80 } 231 }
81 232
82 LogCanBeCheckedForInclusionToUMA(CAN_BE_CHECKED); 233 // TODO(eranm): Call this method only if an entry was queued.
83 // TODO(eranm): Check inclusion here. 234 // The only reason this method is called even if a new entry was not queued
84 entries_status_.insert( 235 // is the way throttling in the LogDnsClient is currently implemented:
85 std::make_pair(sct->timestamp, SCT_PENDING_INCLUSION_CHECK)); 236 // Since the LogDnsClient will invoke the callback with
Ryan Sleevi 2016/09/22 07:08:29 Right, LogDnsClient absolutely needs a way to dire
Eran Messeri 2016/09/22 20:10:21 Thanks, that's exactly the advice I was after on h
237 // ERR_TEMPORARILY_THROTTLED to let the caller know a request has been
238 // throttled, it is possible that the |pending_entries_| queue is full with
239 // pending entries whose requests have been throttled.
240 // In that case the queue will never get emptied since nothing will trigger
241 // ProcessPendingEntries.
242 // Trigger handling of pending requests here to handle the case of a full
243 // |pending_entries_| queue.
244 ProcessPendingEntries();
86 } 245 }
87 246
88 void SingleTreeTracker::NewSTHObserved(const SignedTreeHead& sth) { 247 void SingleTreeTracker::NewSTHObserved(const SignedTreeHead& sth) {
89 DCHECK_EQ(ct_log_->key_id(), sth.log_id); 248 DCHECK_EQ(ct_log_->key_id(), sth.log_id);
90 249
91 if (!ct_log_->VerifySignedTreeHead(sth)) { 250 if (!ct_log_->VerifySignedTreeHead(sth)) {
92 // Sanity check the STH; the caller should have done this 251 // Sanity check the STH; the caller should have done this
93 // already, but being paranoid here. 252 // already, but being paranoid here.
94 // NOTE(eranm): Right now there's no way to get rid of this check here 253 // NOTE(eranm): Right now there's no way to get rid of this check here
95 // as this is the first object in the chain that has an instance of 254 // as this is the first object in the chain that has an instance of
96 // a CTLogVerifier to verify the STH. 255 // a CTLogVerifier to verify the STH.
97 return; 256 return;
98 } 257 }
99 258
100 // In order to avoid updating |verified_sth_| to an older STH in case 259 // In order to avoid updating |verified_sth_| to an older STH in case
101 // an older STH is observed, check that either the observed STH is for 260 // an older STH is observed, check that either the observed STH is for
102 // a larger tree size or that it is for the same tree size but has 261 // a larger tree size or that it is for the same tree size but has
103 // a newer timestamp. 262 // a newer timestamp.
104 const bool sths_for_same_tree = verified_sth_.tree_size == sth.tree_size; 263 const bool sths_for_same_tree = verified_sth_.tree_size == sth.tree_size;
105 const bool received_sth_is_for_larger_tree = 264 const bool received_sth_is_for_larger_tree =
106 (verified_sth_.tree_size > sth.tree_size); 265 (verified_sth_.tree_size > sth.tree_size);
107 const bool received_sth_is_newer = (sth.timestamp > verified_sth_.timestamp); 266 const bool received_sth_is_newer = (sth.timestamp > verified_sth_.timestamp);
108 267
109 if (verified_sth_.timestamp.is_null() || received_sth_is_for_larger_tree || 268 if (verified_sth_.timestamp.is_null() || received_sth_is_for_larger_tree ||
110 (sths_for_same_tree && received_sth_is_newer)) { 269 (sths_for_same_tree && received_sth_is_newer)) {
111 verified_sth_ = sth; 270 verified_sth_ = sth;
271 } else {
272 // Observed an old STH - do nothing.
273 return;
Ryan Sleevi 2016/09/22 07:08:28 Rewrite the condition then to make the 'return' th
Eran Messeri 2016/09/22 20:10:21 Done.
112 } 274 }
113 275
114 // Find out which SCTs can now be checked for inclusion. 276 // Find out which SCTs can now be checked for inclusion.
115 // TODO(eranm): Keep two maps of MerkleTreeLeaf instances, one for leaves 277 auto pending_sth_state_compare =
116 // pending inclusion checks and one for leaves pending a new STH. 278 [](std::pair<const EntryToAudit&, const EntryAuditState&> value) {
117 // The comparison function between MerkleTreeLeaf instances should use the 279 return value.second.state == PENDING_NEWER_STH;
118 // timestamp to determine sorting order, so that bulk moving from one 280 };
Ryan Sleevi 2016/09/22 07:08:28 My same remarks about callback temporaries apply t
Eran Messeri 2016/09/22 20:10:21 Done.
119 // map to the other can happen. 281
120 auto entry = entries_status_.begin(); 282 // Find the first entry in the PENDING_NEWER_STH state - entries
121 while (entry != entries_status_.end() && 283 // before that should be pending leaf index / inclusion proof, no
122 entry->first < verified_sth_.timestamp) { 284 // reason to inspect them.
123 entry->second = SCT_PENDING_INCLUSION_CHECK; 285 auto first_entry_to_audit =
124 ++entry; 286 std::find_if(pending_entries_.begin(), pending_entries_.end(),
125 // TODO(eranm): Check inclusion here. 287 pending_sth_state_compare);
126 } 288
289 auto sth_timestamp_compare = [](
290 std::pair<const EntryToAudit&, const EntryAuditState&> value,
291 base::Time sth_timestamp) {
292 return IsSCTReadyForAudit(sth_timestamp, value.first.sct_timestamp);
293 };
294 // Find where to stop - this is the first entry whose timestamp + MMD
295 // is greater than the STH's timestamp.
296 auto entry_to_stop_at =
297 std::lower_bound(pending_entries_.begin(), pending_entries_.end(),
Ryan Sleevi 2016/09/22 07:08:28 BUG? Shouldn't this be first_entry_to_audit, not p
Eran Messeri 2016/09/22 20:10:20 Changed to first_entry_to_audit rather than pendin
298 sth.timestamp, sth_timestamp_compare);
299
300 auto curr_entry = first_entry_to_audit;
Ryan Sleevi 2016/09/22 07:08:28 for (auto curr_entry = first_entry_to_audit; curr_
Eran Messeri 2016/09/22 20:10:21 Done.
301 // Update the state of all entries that can now be checked for inclusion.
302 while (curr_entry != entry_to_stop_at) {
303 DCHECK(curr_entry->second.state == PENDING_NEWER_STH);
304 curr_entry->second.state = PENDING_LEAF_INDEX_REQUEST;
305 ++curr_entry;
306 }
307
308 ProcessPendingEntries();
127 } 309 }
128 310
129 SingleTreeTracker::SCTInclusionStatus 311 SingleTreeTracker::SCTInclusionStatus
130 SingleTreeTracker::GetLogEntryInclusionStatus( 312 SingleTreeTracker::GetLogEntryInclusionStatus(
131 net::X509Certificate* cert, 313 net::X509Certificate* cert,
132 const net::ct::SignedCertificateTimestamp* sct) { 314 const net::ct::SignedCertificateTimestamp* sct) {
133 auto it = entries_status_.find(sct->timestamp); 315 EntryToAudit entry(cert, sct);
134 316 return GetLogEntryInclusionStatus(entry);
Ryan Sleevi 2016/09/22 07:08:29 There's a noticeable asymmetry here with the calle
Eran Messeri 2016/09/22 20:10:20 Obsolete - the entry no longer has is_valid method
135 return it == entries_status_.end() ? SCT_NOT_OBSERVED : it->second; 317 }
318
319 void SingleTreeTracker::FetchIndexForEntry(const EntryToAudit& entry) {
320 const EntryToAudit* entry_ptr = &entry;
Ryan Sleevi 2016/09/22 07:08:28 This makes me *really* nervous - |entry| could be
Eran Messeri 2016/09/22 20:10:21 Obsolete - inlined this function (though my prefer
321
322 LogDnsClient::LeafIndexCallback leaf_index_callback =
323 base::Bind(&SingleTreeTracker::OnLeafIndexObtained,
324 weak_factory_.GetWeakPtr(), entry_ptr);
Ryan Sleevi 2016/09/22 07:08:28 I think I may have mentioned in other CLs, but I p
Eran Messeri 2016/09/22 20:10:20 Done - in-lined callback creation. You may have m
325
326 dns_client_->QueryLeafIndex(ct_log_->dns_domain(), entry.leaf_hash,
327 leaf_index_callback);
328 }
329
330 void SingleTreeTracker::FetchInclusionForEntry(const EntryToAudit& entry,
331 uint64_t leaf_index,
332 uint64_t tree_size) {
333 LogDnsClient::AuditProofCallback audit_proof_callback =
334 base::Bind(&SingleTreeTracker::OnAuditProofObtained,
335 weak_factory_.GetWeakPtr(), &entry, tree_size);
336
337 dns_client_->QueryAuditProof(ct_log_->dns_domain(), leaf_index, tree_size,
338 audit_proof_callback);
339 }
340
341 void SingleTreeTracker::ProcessPendingEntries() {
342 auto it = pending_entries_.begin();
343 while (it != pending_entries_.end()) {
344 if (it->second.state == PENDING_LEAF_INDEX_REQUEST) {
345 it->second.tree_size = verified_sth_.tree_size;
346 FetchIndexForEntry(it->first);
347 it->second.state = LEAF_INDEX_REQUESTED;
348 break;
349 } else if (it->second.state == PENDING_INCLUSION_PROOF_REQUEST) {
350 FetchInclusionForEntry(it->first, it->second.leaf_index,
351 it->second.tree_size);
352 it->second.state = INCLUSION_PROOF_REQUESTED;
353 break;
354 }
355 ++it;
356 }
357 }
358
359 SingleTreeTracker::SCTInclusionStatus
360 SingleTreeTracker::GetLogEntryInclusionStatus(const EntryToAudit& entry) {
361 if (!entry.is_valid())
362 return SCT_NOT_OBSERVED;
363
364 auto checked_entries_iterator = checked_entries_.Get(entry.leaf_hash);
365 if (checked_entries_iterator != checked_entries_.end()) {
366 // Only success is cached, so all values in |checked_entries_|
367 // should be true.
368 DCHECK(checked_entries_iterator->second);
369 return SCT_INCLUDED_IN_LOG;
370 }
371
372 auto pending_iterator = pending_entries_.find(entry);
373 if (pending_iterator != pending_entries_.end()) {
374 switch (pending_iterator->second.state) {
375 case PENDING_NEWER_STH:
376 return SCT_PENDING_NEWER_STH;
377 case PENDING_LEAF_INDEX_REQUEST:
378 case LEAF_INDEX_REQUESTED:
379 case PENDING_INCLUSION_PROOF_REQUEST:
380 case INCLUSION_PROOF_REQUESTED:
381 return SCT_PENDING_INCLUSION_CHECK;
382 }
383 NOTREACHED();
384 }
385
386 return SCT_NOT_OBSERVED;
387 }
388
389 void SingleTreeTracker::OnLeafIndexObtained(const EntryToAudit* entry,
390 int net_error,
391 uint64_t leaf_index) {
392 auto it = pending_entries_.find(*entry);
393 // The entry may not be present if it was evacuated due to low memory
394 // pressure.
395 if (it == pending_entries_.end()) {
396 ProcessPendingEntries();
397 return;
398 }
399
400 DCHECK(it->second.state == LEAF_INDEX_REQUESTED);
401
402 if (net_error == net::OK) {
403 it->second.state = PENDING_INCLUSION_PROOF_REQUEST;
404 it->second.leaf_index = leaf_index;
405 } else if (net_error == net::Error::ERR_TEMPORARILY_THROTTLED) {
406 // We've been throttled - revert the state of this entry to
407 // the pending state and indicate the tree tracker should
408 // no longer issue new requests.
409 it->second.state = PENDING_LEAF_INDEX_REQUEST;
410 // Break early to avoid calling ProcessPendingEntries - as requests
411 // are being throttled, there's no point in sending more of them.
412 return;
413 } else {
414 // XXX(eranm): Should failures be cached? For now, they are not.
415 // TODO(eranm): Log failure.
416 pending_entries_.erase(it);
417 }
418
419 ProcessPendingEntries();
420 }
421
422 void SingleTreeTracker::OnAuditProofObtained(
423 const EntryToAudit* entry,
424 uint64_t tree_size,
425 int net_error,
426 std::unique_ptr<net::ct::MerkleAuditProof> proof) {
427 auto it = pending_entries_.find(*entry);
428 // The entry may not be present if it was evacuated due to low memory
429 // pressure.
430 if (it == pending_entries_.end()) {
431 ProcessPendingEntries();
432 return;
433 }
434
435 DCHECK(it->second.state == INCLUSION_PROOF_REQUESTED);
436
437 if (net_error == net::OK) {
438 // TODO(eranm): Once the CTLogVerifier supports that, actually verify the
439 // proof.
440 // TODO(eranm): Log success.
441 checked_entries_.Put(it->first.leaf_hash, true);
442 pending_entries_.erase(it);
443 } else if (net_error == net::Error::ERR_TEMPORARILY_THROTTLED) {
444 // See the documentation in OnLeafIndexObtained on handling the throttled
445 // case.
446 it->second.state = PENDING_INCLUSION_PROOF_REQUEST;
447 return;
448 } else {
449 // XXX(eranm): Should failures be cached? For now, they are not.
450 // TODO(eranm): Log failure.
451 pending_entries_.erase(it);
452 }
453
454 ProcessPendingEntries();
455 }
456
457 void SingleTreeTracker::OnMemoryPressure(
458 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
459 switch (memory_pressure_level) {
460 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
461 break;
Ryan Sleevi 2016/09/22 07:08:28 Is this necessary? I'm guessing yes (Compiler warn
Eran Messeri 2016/09/22 20:10:21 Yes, it is. Are you suggesting I document it?
462 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
463 pending_entries_.clear();
Ryan Sleevi 2016/09/22 07:08:29 https://google.github.io/styleguide/cppguide.html#
Eran Messeri 2016/09/22 20:10:21 Done.
464 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
465 checked_entries_.Clear();
466 break;
467 }
136 } 468 }
137 469
138 } // namespace certificate_transparency 470 } // namespace certificate_transparency
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698