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 "crypto/sha2.h" | |
| 16 #include "net/base/hash_value.h" | |
| 17 #include "net/base/net_errors.h" | |
| 10 #include "net/cert/ct_log_verifier.h" | 18 #include "net/cert/ct_log_verifier.h" |
| 19 #include "net/cert/merkle_audit_proof.h" | |
| 20 #include "net/cert/merkle_tree_leaf.h" | |
| 11 #include "net/cert/signed_certificate_timestamp.h" | 21 #include "net/cert/signed_certificate_timestamp.h" |
| 12 #include "net/cert/x509_certificate.h" | 22 #include "net/cert/x509_certificate.h" |
| 13 | 23 |
| 24 using net::SHA256HashValue; | |
| 25 using net::ct::LogEntry; | |
| 26 using net::ct::MerkleTreeLeaf; | |
| 27 using net::ct::SignedCertificateTimestamp; | |
| 14 using net::ct::SignedTreeHead; | 28 using net::ct::SignedTreeHead; |
| 15 | 29 |
| 16 namespace { | 30 namespace { |
| 17 | 31 |
| 18 // Enum indicating whether an SCT can be checked for inclusion and if not, | 32 // Enum indicating whether an SCT can be checked for inclusion and if not, |
| 19 // the reason it cannot. | 33 // the reason it cannot. |
| 20 // | 34 // |
| 21 // Note: The numeric values are used within a histogram and should not change | 35 // Note: The numeric values are used within a histogram and should not change |
| 22 // or be re-assigned. | 36 // or be re-assigned. |
| 23 enum SCTCanBeCheckedForInclusion { | 37 enum SCTCanBeCheckedForInclusion { |
| 24 // If the SingleTreeTracker does not have a valid STH, then a valid STH is | 38 // 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 | 39 // first required to evaluate whether the SCT can be checked for inclusion |
| 26 // or not. | 40 // or not. |
| 27 VALID_STH_REQUIRED = 0, | 41 VALID_STH_REQUIRED = 0, |
| 28 // If the STH does not cover the SCT (the timestamp in the SCT is greater than | 42 // 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. | 43 // MMD + timestamp in the STH), then a newer STH is needed. |
| 30 NEWER_STH_REQUIRED = 1, | 44 NEWER_STH_REQUIRED = 1, |
| 31 // When an SCT is observed, if the SingleTreeTracker instance has a valid STH | 45 // 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 + | 46 // 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. | 47 // timestamp in the STH), then it can be checked for inclusion. |
| 34 CAN_BE_CHECKED = 2, | 48 CAN_BE_CHECKED = 2, |
| 49 // This SCT was not audited because the queue of pending entries was | |
| 50 // full. | |
| 51 NOT_AUDITED_QUEUE_FULL = 3, | |
| 35 SCT_CAN_BE_CHECKED_MAX | 52 SCT_CAN_BE_CHECKED_MAX |
| 36 }; | 53 }; |
| 37 | 54 |
| 38 // Measure how often clients encounter very new SCTs, by measuring whether an | 55 // Measure how often clients encounter very new SCTs, by measuring whether an |
| 39 // SCT can be checked for inclusion upon first observation. | 56 // SCT can be checked for inclusion upon first observation. |
| 40 void LogCanBeCheckedForInclusionToUMA( | 57 void LogCanBeCheckedForInclusionToUMA( |
| 41 SCTCanBeCheckedForInclusion can_be_checked) { | 58 SCTCanBeCheckedForInclusion can_be_checked) { |
| 42 UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.CanInclusionCheckSCT", | 59 UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.CanInclusionCheckSCT", |
| 43 can_be_checked, SCT_CAN_BE_CHECKED_MAX); | 60 can_be_checked, SCT_CAN_BE_CHECKED_MAX); |
| 44 } | 61 } |
| 45 | 62 |
| 63 // Enum indicating the outcome of an inclusion check for a particular log | |
| 64 // entry. | |
| 65 // | |
| 66 // Note: The numeric values are used within a histogram and should not change | |
| 67 // or be re-assigned. | |
| 68 enum LogEntryInclusionCheckResult { | |
| 69 // Inclusion check succeeded: Proof obtained and validated successfully. | |
| 70 GOT_VALID_INCLUSION_PROOF = 0, | |
|
Ryan Sleevi
2016/12/22 21:33:21
nit: My inclination is to suggest vertical whitesp
Eran Messeri
2017/01/03 23:07:41
Done here and lines 32-52.
| |
| 71 // Could not get an inclusion proof. | |
| 72 FAILED_GETTING_INCLUSION_PROOF = 1, | |
| 73 // An inclusion proof was obtained but it is invalid. | |
| 74 GOT_INVALID_INCLUSION_PROOF = 2, | |
| 75 // The SCT could not be audited because the client's DNS configuration | |
| 76 // is faulty. | |
| 77 DNS_QUERY_NOT_POSSIBLE = 3, | |
| 78 LOG_ENTRY_INCLUSION_CHECK_RESULT_MAX | |
| 79 }; | |
| 80 | |
| 81 void LogInclusionCheckResult(LogEntryInclusionCheckResult result) { | |
| 82 UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.InclusionCheckResult", | |
| 83 result, LOG_ENTRY_INCLUSION_CHECK_RESULT_MAX); | |
| 84 } | |
| 85 | |
| 86 // Calculate the leaf hash of the the entry in the log represented by | |
| 87 // the given |cert| and |sct|. If leaf hash calculation succeeds returns | |
| 88 // true, false otherwise. | |
| 89 bool GetLogEntryLeafHash(const net::X509Certificate* cert, | |
| 90 const SignedCertificateTimestamp* sct, | |
| 91 SHA256HashValue* leaf_hash) { | |
| 92 MerkleTreeLeaf leaf; | |
| 93 if (!GetMerkleTreeLeaf(cert, sct, &leaf)) | |
| 94 return false; | |
| 95 | |
| 96 std::string leaf_hash_str; | |
| 97 if (!HashMerkleTreeLeaf(leaf, &leaf_hash_str)) | |
| 98 return false; | |
| 99 | |
| 100 memcpy(leaf_hash->data, leaf_hash_str.data(), crypto::kSHA256Length); | |
| 101 return true; | |
| 102 } | |
| 103 | |
| 104 // Audit state of a log entry. | |
|
Ryan Sleevi
2016/12/22 21:33:21
Since this represents a finite flow, perhaps it's
Eran Messeri
2017/01/03 23:07:41
Done - I've added documentation with an overview o
| |
| 105 enum AuditState { | |
| 106 // Entry cannot be audited because a newer STH is needed. | |
| 107 PENDING_NEWER_STH, | |
| 108 // A leaf index has been obtained and the entry is now pending request | |
| 109 // of an inclusion proof. | |
| 110 PENDING_INCLUSION_PROOF_REQUEST, | |
| 111 // An inclusion proof for this entry has been requested from the log. | |
| 112 INCLUSION_PROOF_REQUESTED | |
| 113 }; | |
| 114 | |
| 115 // Maximal size of the checked entries cache. | |
| 116 size_t kCheckedEntriesCacheSize = 100; | |
| 117 | |
| 118 // Maximal size of the pending entries queue. | |
| 119 size_t kPendingEntriesQueueSize = 100; | |
| 120 | |
| 121 // Maximum Merge Delay - logs can have individual MMD, but all known logs | |
| 122 // currently have 24 hours MMD and Chrome's CT policy requires an MMD | |
| 123 // that's no greater than that. For simplicity, use 24 hours for all logs. | |
| 124 constexpr base::TimeDelta kMaximumMergeDelay = base::TimeDelta::FromHours(24); | |
| 125 | |
| 126 // The log MUST incorporate the a certificate in the tree within the Maximum | |
| 127 // Merge Delay, so an entry can be audited once the timestamp from the SCT + | |
| 128 // MMD has passed. | |
| 129 // Returns true if the timestamp from the STH is newer than SCT timestamp + MMD. | |
| 130 bool IsSCTReadyForAudit(base::Time sth_timestamp, base::Time sct_timestamp) { | |
| 131 return sct_timestamp + kMaximumMergeDelay < sth_timestamp; | |
| 132 } | |
| 133 | |
| 46 } // namespace | 134 } // namespace |
| 47 | 135 |
| 48 namespace certificate_transparency { | 136 namespace certificate_transparency { |
|
Ryan Sleevi
2016/12/22 21:33:21
note: Nothing wrong with moving this line up to 29
Eran Messeri
2017/01/03 23:07:41
Done.
| |
| 137 // The entry that is being audited. Immutable - once created, the entry | |
|
Ryan Sleevi
2016/12/22 21:33:21
linebreak between 136/137
Eran Messeri
2017/01/03 23:07:41
Shouldn't be needed now that the namespace declara
| |
| 138 // to audit does not change. | |
|
Ryan Sleevi
2016/12/22 21:33:21
As a class comment, "Immutable" isn't a guarantee
Eran Messeri
2017/01/03 23:07:41
Done - removed the 2nd sentence entirely.
| |
| 139 struct SingleTreeTracker::EntryToAudit { | |
| 140 base::Time sct_timestamp; | |
| 141 SHA256HashValue leaf_hash; | |
| 142 | |
| 143 explicit EntryToAudit(base::Time timestamp) : sct_timestamp(timestamp) {} | |
| 144 }; | |
| 145 | |
| 146 // State of a log entry: its audit state and information necessary to | |
| 147 // validate an inclusion proof. Gets updated as the entry transitions | |
| 148 // between the different audit states. | |
| 149 struct SingleTreeTracker::EntryAuditState { | |
| 150 // Current phase of inclusion check. | |
| 151 AuditState state; | |
|
Ryan Sleevi
2016/12/22 21:33:21
same remarks about newlines for large blocks, espe
Eran Messeri
2017/01/03 23:07:41
Done.
| |
| 152 // The proof to be filled in by the LogDnsClient | |
| 153 net::ct::MerkleAuditProof proof; | |
|
Ryan Sleevi
2016/12/22 21:33:21
It's a little surprising (and seemingly inconsiste
Eran Messeri
2017/01/03 23:07:41
Done - added a using declaration at the top, remov
| |
| 154 // The root hash of the tree for which an inclusion proof was requested. | |
| 155 // The root hash is needed after the inclusion proof is fetched for validating | |
| 156 // the inclusion proof (each inclusion proof is valid for one particular leaf, | |
| 157 // denoted by the leaf index, in exactly one particular tree, denoted by the | |
| 158 // tree size in the proof). | |
| 159 // To avoid having to re-fetch the inclusion proof if a newer STH is provided | |
| 160 // to the SingleTreeTracker, the size of the original tree for which the | |
| 161 // inclusion proof was requested is stored in |proof| and the root hash | |
| 162 // in |root_hash|. | |
| 163 std::string root_hash; | |
| 164 | |
| 165 explicit EntryAuditState(AuditState state) : state(state) {} | |
| 166 }; | |
| 167 | |
| 168 // Orders entries by the SCT timestamp. In case of tie, which is very unlikely | |
| 169 // as it requires two SCTs issued from a log at exactly the same time, order | |
| 170 // by leaf hash. | |
| 171 bool SingleTreeTracker::OrderByTimestamp::operator()( | |
| 172 const EntryToAudit& lhs, | |
| 173 const EntryToAudit& rhs) const { | |
| 174 if (lhs.sct_timestamp != rhs.sct_timestamp) | |
| 175 return lhs.sct_timestamp < rhs.sct_timestamp; | |
| 176 | |
| 177 return net::SHA256HashValueLessThan()(lhs.leaf_hash, rhs.leaf_hash); | |
| 178 } | |
| 179 | |
| 180 struct SingleTreeTracker::EntryAuditResult {}; | |
| 49 | 181 |
| 50 SingleTreeTracker::SingleTreeTracker( | 182 SingleTreeTracker::SingleTreeTracker( |
| 51 scoped_refptr<const net::CTLogVerifier> ct_log) | 183 scoped_refptr<const net::CTLogVerifier> ct_log, |
| 52 : ct_log_(std::move(ct_log)) {} | 184 LogDnsClient* dns_client) |
| 185 : ct_log_(std::move(ct_log)), | |
| 186 checked_entries_(kCheckedEntriesCacheSize), | |
| 187 dns_client_(dns_client), | |
| 188 weak_factory_(this) { | |
| 189 memory_pressure_listener_.reset(new base::MemoryPressureListener(base::Bind( | |
| 190 &SingleTreeTracker::OnMemoryPressure, base::Unretained(this)))); | |
| 191 } | |
| 53 | 192 |
| 54 SingleTreeTracker::~SingleTreeTracker() {} | 193 SingleTreeTracker::~SingleTreeTracker() {} |
| 55 | 194 |
| 56 void SingleTreeTracker::OnSCTVerified( | 195 void SingleTreeTracker::OnSCTVerified( |
| 57 net::X509Certificate* cert, | 196 net::X509Certificate* cert, |
| 58 const net::ct::SignedCertificateTimestamp* sct) { | 197 const net::ct::SignedCertificateTimestamp* sct) { |
| 59 DCHECK_EQ(ct_log_->key_id(), sct->log_id); | 198 DCHECK_EQ(ct_log_->key_id(), sct->log_id); |
| 60 | 199 |
| 61 // SCT was previously observed, so its status should not be changed. | 200 EntryToAudit entry(sct->timestamp); |
| 62 if (entries_status_.find(sct->timestamp) != entries_status_.end()) | 201 if (!GetLogEntryLeafHash(cert, sct, &entry.leaf_hash)) |
| 63 return; | 202 return; |
| 64 | 203 |
| 204 // Avoid queueing multiple instances of the same entry. | |
| 205 if (GetAuditedEntryInclusionStatus(entry) != SCT_NOT_OBSERVED) | |
| 206 return; | |
| 207 | |
| 208 if (pending_entries_.size() >= kPendingEntriesQueueSize) { | |
| 209 // Queue is full - cannot audit SCT. | |
| 210 LogCanBeCheckedForInclusionToUMA(NOT_AUDITED_QUEUE_FULL); | |
| 211 return; | |
| 212 } | |
| 213 | |
| 65 // If there isn't a valid STH or the STH is not fresh enough to check | 214 // 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. | 215 // inclusion against, store the SCT for future checking and return. |
| 67 if (verified_sth_.timestamp.is_null() || | 216 if (verified_sth_.timestamp.is_null() || |
| 68 (verified_sth_.timestamp < | 217 !IsSCTReadyForAudit(verified_sth_.timestamp, entry.sct_timestamp)) { |
| 69 (sct->timestamp + base::TimeDelta::FromHours(24)))) { | 218 pending_entries_.insert( |
| 70 entries_status_.insert( | 219 std::make_pair(std::move(entry), EntryAuditState(PENDING_NEWER_STH))); |
| 71 std::make_pair(sct->timestamp, SCT_PENDING_NEWER_STH)); | |
| 72 | 220 |
| 73 if (!verified_sth_.timestamp.is_null()) { | 221 if (verified_sth_.timestamp.is_null()) { |
| 222 LogCanBeCheckedForInclusionToUMA(VALID_STH_REQUIRED); | |
| 223 } else { | |
| 74 LogCanBeCheckedForInclusionToUMA(NEWER_STH_REQUIRED); | 224 LogCanBeCheckedForInclusionToUMA(NEWER_STH_REQUIRED); |
| 75 } else { | |
| 76 LogCanBeCheckedForInclusionToUMA(VALID_STH_REQUIRED); | |
| 77 } | 225 } |
| 78 | 226 |
| 79 return; | 227 return; |
| 80 } | 228 } |
| 81 | 229 |
| 82 LogCanBeCheckedForInclusionToUMA(CAN_BE_CHECKED); | 230 LogCanBeCheckedForInclusionToUMA(CAN_BE_CHECKED); |
| 83 // TODO(eranm): Check inclusion here. | 231 pending_entries_.insert(std::make_pair( |
| 84 entries_status_.insert( | 232 std::move(entry), EntryAuditState(PENDING_INCLUSION_PROOF_REQUEST))); |
| 85 std::make_pair(sct->timestamp, SCT_PENDING_INCLUSION_CHECK)); | 233 |
| 234 ProcessPendingEntries(); | |
| 86 } | 235 } |
| 87 | 236 |
| 88 void SingleTreeTracker::NewSTHObserved(const SignedTreeHead& sth) { | 237 void SingleTreeTracker::NewSTHObserved(const SignedTreeHead& sth) { |
| 89 DCHECK_EQ(ct_log_->key_id(), sth.log_id); | 238 DCHECK_EQ(ct_log_->key_id(), sth.log_id); |
| 90 | 239 |
| 91 if (!ct_log_->VerifySignedTreeHead(sth)) { | 240 if (!ct_log_->VerifySignedTreeHead(sth)) { |
| 92 // Sanity check the STH; the caller should have done this | 241 // Sanity check the STH; the caller should have done this |
| 93 // already, but being paranoid here. | 242 // already, but being paranoid here. |
| 94 // NOTE(eranm): Right now there's no way to get rid of this check here | 243 // 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 | 244 // as this is the first object in the chain that has an instance of |
| 96 // a CTLogVerifier to verify the STH. | 245 // a CTLogVerifier to verify the STH. |
| 97 return; | 246 return; |
| 98 } | 247 } |
| 99 | 248 |
| 100 // In order to avoid updating |verified_sth_| to an older STH in case | 249 // 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 | 250 // 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 | 251 // a larger tree size or that it is for the same tree size but has |
| 103 // a newer timestamp. | 252 // a newer timestamp. |
| 104 const bool sths_for_same_tree = verified_sth_.tree_size == sth.tree_size; | 253 const bool sths_for_same_tree = verified_sth_.tree_size == sth.tree_size; |
| 105 const bool received_sth_is_for_larger_tree = | 254 const bool received_sth_is_for_larger_tree = |
| 106 (verified_sth_.tree_size > sth.tree_size); | 255 (verified_sth_.tree_size < sth.tree_size); |
| 107 const bool received_sth_is_newer = (sth.timestamp > verified_sth_.timestamp); | 256 const bool received_sth_is_newer = (sth.timestamp > verified_sth_.timestamp); |
| 108 | 257 |
| 109 if (verified_sth_.timestamp.is_null() || received_sth_is_for_larger_tree || | 258 if (!verified_sth_.timestamp.is_null() && !received_sth_is_for_larger_tree && |
| 110 (sths_for_same_tree && received_sth_is_newer)) { | 259 !(sths_for_same_tree && received_sth_is_newer)) { |
| 111 verified_sth_ = sth; | 260 // Observed an old STH - do nothing. |
| 261 return; | |
| 112 } | 262 } |
| 113 | 263 |
| 114 // Find out which SCTs can now be checked for inclusion. | 264 verified_sth_ = sth; |
| 115 // TODO(eranm): Keep two maps of MerkleTreeLeaf instances, one for leaves | 265 |
| 116 // pending inclusion checks and one for leaves pending a new STH. | 266 // Find the first entry in the PENDING_NEWER_STH state - entries |
| 117 // The comparison function between MerkleTreeLeaf instances should use the | 267 // before that should be pending leaf index / inclusion proof, no |
| 118 // timestamp to determine sorting order, so that bulk moving from one | 268 // reason to inspect them. |
| 119 // map to the other can happen. | 269 auto first_entry_to_audit = std::find_if( |
| 120 auto entry = entries_status_.begin(); | 270 pending_entries_.begin(), pending_entries_.end(), |
| 121 while (entry != entries_status_.end() && | 271 [](std::pair<const EntryToAudit&, const EntryAuditState&> value) { |
| 122 entry->first < verified_sth_.timestamp) { | 272 return value.second.state == PENDING_NEWER_STH; |
| 123 entry->second = SCT_PENDING_INCLUSION_CHECK; | 273 }); |
| 124 ++entry; | 274 |
| 125 // TODO(eranm): Check inclusion here. | 275 // Find where to stop - this is the first entry whose timestamp + MMD |
| 276 // is greater than the STH's timestamp. | |
| 277 auto entry_to_stop_at = std::lower_bound( | |
|
Ryan Sleevi
2016/12/22 21:33:21
nit: another symmetry remark: [first_entry_to_audi
Eran Messeri
2017/01/03 23:07:41
Done - changed to auditable_entries_begin / audita
| |
| 278 first_entry_to_audit, pending_entries_.end(), sth.timestamp, | |
| 279 [](std::pair<const EntryToAudit&, const EntryAuditState&> value, | |
| 280 base::Time sth_timestamp) { | |
| 281 return IsSCTReadyForAudit(sth_timestamp, value.first.sct_timestamp); | |
| 282 }); | |
| 283 | |
| 284 // Update the state of all entries that can now be checked for inclusion. | |
| 285 for (auto curr_entry = first_entry_to_audit; curr_entry != entry_to_stop_at; | |
| 286 ++curr_entry) { | |
| 287 DCHECK_EQ(curr_entry->second.state, PENDING_NEWER_STH); | |
| 288 curr_entry->second.state = PENDING_INCLUSION_PROOF_REQUEST; | |
| 126 } | 289 } |
| 290 | |
| 291 ProcessPendingEntries(); | |
| 127 } | 292 } |
| 128 | 293 |
| 129 SingleTreeTracker::SCTInclusionStatus | 294 SingleTreeTracker::SCTInclusionStatus |
| 130 SingleTreeTracker::GetLogEntryInclusionStatus( | 295 SingleTreeTracker::GetLogEntryInclusionStatus( |
| 131 net::X509Certificate* cert, | 296 net::X509Certificate* cert, |
| 132 const net::ct::SignedCertificateTimestamp* sct) { | 297 const net::ct::SignedCertificateTimestamp* sct) { |
| 133 auto it = entries_status_.find(sct->timestamp); | 298 EntryToAudit entry(sct->timestamp); |
| 299 if (!GetLogEntryLeafHash(cert, sct, &entry.leaf_hash)) | |
| 300 return SCT_NOT_OBSERVED; | |
| 301 return GetAuditedEntryInclusionStatus(entry); | |
| 302 } | |
| 134 | 303 |
| 135 return it == entries_status_.end() ? SCT_NOT_OBSERVED : it->second; | 304 void SingleTreeTracker::ProcessPendingEntries() { |
| 305 for (auto it = pending_entries_.begin(); it != pending_entries_.end(); ++it) { | |
| 306 if (it->second.state != PENDING_INCLUSION_PROOF_REQUEST) { | |
| 307 continue; | |
| 308 } | |
| 309 | |
| 310 it->second.root_hash = | |
| 311 std::string(verified_sth_.sha256_root_hash, crypto::kSHA256Length); | |
| 312 | |
| 313 std::string leaf_hash( | |
| 314 reinterpret_cast<const char*>(it->first.leaf_hash.data), | |
| 315 crypto::kSHA256Length); | |
|
Ryan Sleevi
2016/12/22 21:33:21
nit: More copying that we might want to optimize i
Eran Messeri
2017/01/03 23:07:41
Acknowledged - as mentioned, I'll do a pass to see
| |
| 316 net::Error result = dns_client_->QueryAuditProof( | |
| 317 ct_log_->dns_domain(), leaf_hash, verified_sth_.tree_size, | |
| 318 &(it->second.proof), | |
| 319 base::Bind(&SingleTreeTracker::OnAuditProofObtained, | |
| 320 weak_factory_.GetWeakPtr(), it->first)); | |
| 321 // Handling proofs returned synchronously is not implemeted. | |
| 322 DCHECK_NE(result, net::OK); | |
| 323 if (result == net::ERR_IO_PENDING) { | |
| 324 // Successfully requested an inclusion proof - change entry state | |
| 325 // and continue to the next one. | |
| 326 it->second.state = INCLUSION_PROOF_REQUESTED; | |
| 327 } else if (result == net::ERR_TEMPORARILY_THROTTLED) { | |
| 328 dns_client_->NotifyWhenNotThrottled( | |
| 329 base::Bind(&SingleTreeTracker::ProcessPendingEntries, | |
| 330 weak_factory_.GetWeakPtr())); | |
| 331 // Exit the loop since all subsequent calls to QueryAuditProof | |
| 332 // will be throttled. | |
| 333 break; | |
| 334 } else if (result == net::ERR_NAME_RESOLUTION_FAILED) { | |
| 335 LogInclusionCheckResult(DNS_QUERY_NOT_POSSIBLE); | |
| 336 // Lookup failed due to bad DNS configuration, erase the entry and | |
| 337 // continue to the next one. | |
| 338 it = pending_entries_.erase(it); | |
| 339 // Break here if it's the last entry to avoid |it| being incremented | |
| 340 // by the for loop. | |
| 341 if (it == pending_entries_.end()) | |
| 342 break; | |
| 343 } else { | |
| 344 // BUG: an invalid argument was provided or an unexpected error | |
| 345 // was returned from LogDnsClient. | |
| 346 DCHECK_EQ(result, net::ERR_INVALID_ARGUMENT); | |
| 347 NOTREACHED(); | |
| 348 } | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 SingleTreeTracker::SCTInclusionStatus | |
| 353 SingleTreeTracker::GetAuditedEntryInclusionStatus(const EntryToAudit& entry) { | |
| 354 const auto checked_entries_iterator = checked_entries_.Get( | |
| 355 std::string(reinterpret_cast<const char*>(entry.leaf_hash.data), | |
| 356 crypto::kSHA256Length)); | |
| 357 if (checked_entries_iterator != checked_entries_.end()) { | |
| 358 return SCT_INCLUDED_IN_LOG; | |
| 359 } | |
| 360 | |
| 361 auto pending_iterator = pending_entries_.find(entry); | |
| 362 if (pending_iterator != pending_entries_.end()) { | |
| 363 switch (pending_iterator->second.state) { | |
| 364 case PENDING_NEWER_STH: | |
| 365 return SCT_PENDING_NEWER_STH; | |
| 366 case PENDING_INCLUSION_PROOF_REQUEST: | |
| 367 case INCLUSION_PROOF_REQUESTED: | |
| 368 return SCT_PENDING_INCLUSION_CHECK; | |
| 369 } | |
| 370 NOTREACHED(); | |
| 371 } | |
| 372 | |
| 373 return SCT_NOT_OBSERVED; | |
|
Ryan Sleevi
2016/12/22 21:33:21
Does it make more sense to inverse this conditiona
Eran Messeri
2017/01/03 23:07:41
Done (I suspect you have to make this comment a lo
| |
| 374 } | |
| 375 | |
| 376 void SingleTreeTracker::OnAuditProofObtained(const EntryToAudit& entry, | |
| 377 int net_error) { | |
| 378 auto it = pending_entries_.find(entry); | |
| 379 // The entry may not be present if it was evacuated due to low memory | |
| 380 // pressure. | |
| 381 if (it == pending_entries_.end()) | |
| 382 return; | |
| 383 | |
| 384 DCHECK_EQ(it->second.state, INCLUSION_PROOF_REQUESTED); | |
| 385 | |
| 386 if (net_error != net::OK) { | |
| 387 // XXX(eranm): Should failures be cached? For now, they are not. | |
| 388 LogInclusionCheckResult(FAILED_GETTING_INCLUSION_PROOF); | |
| 389 pending_entries_.erase(it); | |
| 390 return; | |
| 391 } | |
| 392 | |
| 393 std::string leaf_hash(reinterpret_cast<const char*>(entry.leaf_hash.data), | |
| 394 crypto::kSHA256Length); | |
| 395 | |
| 396 if (ct_log_->VerifyAuditProof(it->second.proof, it->second.root_hash, | |
| 397 leaf_hash)) { | |
| 398 checked_entries_.Put(leaf_hash, EntryAuditResult()); | |
| 399 LogInclusionCheckResult(GOT_VALID_INCLUSION_PROOF); | |
| 400 } else { | |
| 401 LogInclusionCheckResult(GOT_INVALID_INCLUSION_PROOF); | |
| 402 } | |
|
Ryan Sleevi
2016/12/22 21:33:20
Similar remarks about error handling first. Does i
Eran Messeri
2017/01/03 23:07:41
Partly done:
Because I've changed checked_entries_
| |
| 403 pending_entries_.erase(it); | |
| 404 } | |
| 405 | |
| 406 void SingleTreeTracker::OnMemoryPressure( | |
| 407 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { | |
| 408 switch (memory_pressure_level) { | |
| 409 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: | |
| 410 break; | |
| 411 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: | |
| 412 pending_entries_.clear(); | |
| 413 // Fall through to clearing the other cache. | |
| 414 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: | |
| 415 checked_entries_.Clear(); | |
| 416 break; | |
| 417 } | |
| 136 } | 418 } |
| 137 | 419 |
| 138 } // namespace certificate_transparency | 420 } // namespace certificate_transparency |
| OLD | NEW |