Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 |
| OLD | NEW |