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, | |
| 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 CHECK_EQ(leaf_hash_str.size(), crypto::kSHA256Length); | |
| 101 memcpy(leaf_hash->data, leaf_hash_str.data(), crypto::kSHA256Length); | |
| 102 return true; | |
| 103 } | |
| 104 | |
| 105 // Audit state of a log entry. | |
| 106 enum AuditState { | |
| 107 // Entry cannot be audited because a newer STH is needed. | |
| 108 PENDING_NEWER_STH, | |
| 109 // A leaf index has been obtained and the entry is now pending request | |
| 110 // of an inclusion proof. | |
| 111 PENDING_INCLUSION_PROOF_REQUEST, | |
| 112 // An inclusion proof for this entry has been requested from the log. | |
| 113 INCLUSION_PROOF_REQUESTED | |
| 114 }; | |
| 115 | |
| 116 // Maximal size of the checked entries cache. | |
| 117 size_t kCheckedEntriesCacheSize = 100; | |
| 118 | |
| 119 // Maximal size of the pending entries queue. | |
| 120 size_t kPendingEntriesQueueSize = 100; | |
| 121 | |
| 122 // Maximum Merge Delay - logs can have individual MMD, but all known logs | |
| 123 // currently have 24 hours MMD and Chrome's CT policy requires an MMD | |
| 124 // that's no greater than that. For simplicity, use 24 hours for all logs. | |
| 125 constexpr base::TimeDelta kMaximumMergeDelay = base::TimeDelta::FromHours(24); | |
| 126 | |
| 127 // The log MUST incorporate the a certificate in the tree within the Maximum | |
| 128 // Merge Delay, so an entry can be audited once the timestamp from the SCT + | |
| 129 // MMD has passed. | |
| 130 // Returns true if the timestamp from the STH is newer than SCT timestamp + MMD. | |
| 131 bool IsSCTReadyForAudit(base::Time sth_timestamp, base::Time sct_timestamp) { | |
| 132 return sct_timestamp + kMaximumMergeDelay < sth_timestamp; | |
| 133 } | |
| 134 | |
| 46 } // namespace | 135 } // namespace |
| 47 | 136 |
| 48 namespace certificate_transparency { | 137 namespace certificate_transparency { |
| 138 // The entry that is being audited. Immutable - once created, the entry | |
| 139 // to audit does not change. | |
| 140 struct SingleTreeTracker::EntryToAudit { | |
| 141 base::Time sct_timestamp; | |
| 142 SHA256HashValue leaf_hash; | |
| 143 | |
| 144 explicit EntryToAudit(base::Time timestamp) : sct_timestamp(timestamp) {} | |
| 145 }; | |
| 146 | |
| 147 // State of a log entry: its audit state and information necessary to | |
| 148 // validate an inclusion proof. Gets updated as the entry transitions | |
| 149 // between the different audit states. | |
| 150 struct SingleTreeTracker::EntryAuditState { | |
| 151 // Current phase of inclusion check. | |
| 152 AuditState state; | |
| 153 // The proof to be filled in by the LogDnsClient | |
| 154 net::ct::MerkleAuditProof proof; | |
| 155 // The root hash of the tree for which an inclusion proof was requested. | |
| 156 // The root hash is needed after the inclusion proof is fetched for validating | |
| 157 // the inclusion proof (each inclusion proof is valid for one particular leaf, | |
| 158 // denoted by the leaf index, in exactly one particular tree, denoted by the | |
| 159 // tree size in the proof). | |
| 160 // To avoid having to re-fetch the inclusion proof if a newer STH is provided | |
| 161 // to the SingleTreeTracker, the size of the original tree for which the | |
| 162 // inclusion proof was requested is stored in |proof| and the root hash | |
| 163 // in |root_hash|. | |
| 164 std::string root_hash; | |
| 165 | |
| 166 explicit EntryAuditState(AuditState state) : state(state) {} | |
| 167 }; | |
| 168 | |
| 169 // Orders entries by the SCT timestamp. In case of tie, which is very unlikely | |
| 170 // as it requires two SCTs issued from a log at exactly the same time, order | |
| 171 // by leaf hash. | |
| 172 bool SingleTreeTracker::OrderByTimestamp::operator()( | |
| 173 const EntryToAudit& lhs, | |
| 174 const EntryToAudit& rhs) const { | |
| 175 if (lhs.sct_timestamp != rhs.sct_timestamp) | |
| 176 return lhs.sct_timestamp < rhs.sct_timestamp; | |
| 177 | |
| 178 return net::SHA256HashValueLessThan()(lhs.leaf_hash, rhs.leaf_hash); | |
| 179 } | |
| 180 | |
| 181 struct SingleTreeTracker::EntryAuditResult {}; | |
| 49 | 182 |
| 50 SingleTreeTracker::SingleTreeTracker( | 183 SingleTreeTracker::SingleTreeTracker( |
| 51 scoped_refptr<const net::CTLogVerifier> ct_log) | 184 scoped_refptr<const net::CTLogVerifier> ct_log, |
| 52 : ct_log_(std::move(ct_log)) {} | 185 LogDnsClient* dns_client) |
| 186 : ct_log_(std::move(ct_log)), | |
| 187 checked_entries_(kCheckedEntriesCacheSize), | |
| 188 dns_client_(dns_client), | |
| 189 weak_factory_(this) { | |
| 190 memory_pressure_listener_.reset(new base::MemoryPressureListener(base::Bind( | |
| 191 &SingleTreeTracker::OnMemoryPressure, weak_factory_.GetWeakPtr()))); | |
|
Eran Messeri
2016/11/22 13:18:49
Follow up with MemoryPressureListener on whether a
| |
| 192 } | |
| 53 | 193 |
| 54 SingleTreeTracker::~SingleTreeTracker() {} | 194 SingleTreeTracker::~SingleTreeTracker() {} |
| 55 | 195 |
| 56 void SingleTreeTracker::OnSCTVerified( | 196 void SingleTreeTracker::OnSCTVerified( |
| 57 net::X509Certificate* cert, | 197 net::X509Certificate* cert, |
| 58 const net::ct::SignedCertificateTimestamp* sct) { | 198 const net::ct::SignedCertificateTimestamp* sct) { |
| 59 DCHECK_EQ(ct_log_->key_id(), sct->log_id); | 199 DCHECK_EQ(ct_log_->key_id(), sct->log_id); |
| 60 | 200 |
| 61 // SCT was previously observed, so its status should not be changed. | 201 EntryToAudit entry(sct->timestamp); |
| 62 if (entries_status_.find(sct->timestamp) != entries_status_.end()) | 202 if (!GetLogEntryLeafHash(cert, sct, &entry.leaf_hash)) |
| 63 return; | 203 return; |
| 64 | 204 |
| 205 // Avoid queueing multiple instances of the same entry. | |
| 206 if (GetAuditedEntryInclusionStatus(entry) != SCT_NOT_OBSERVED) | |
| 207 return; | |
| 208 | |
| 209 if (pending_entries_.size() >= kPendingEntriesQueueSize) { | |
| 210 // Queue is full - cannot audit SCT. | |
| 211 LogCanBeCheckedForInclusionToUMA(NOT_AUDITED_QUEUE_FULL); | |
| 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 |
| 79 return; | 228 return; |
| 80 } | 229 } |
| 81 | 230 |
| 82 LogCanBeCheckedForInclusionToUMA(CAN_BE_CHECKED); | 231 LogCanBeCheckedForInclusionToUMA(CAN_BE_CHECKED); |
| 83 // TODO(eranm): Check inclusion here. | 232 pending_entries_.insert(std::make_pair( |
| 84 entries_status_.insert( | 233 std::move(entry), EntryAuditState(PENDING_INCLUSION_PROOF_REQUEST))); |
| 85 std::make_pair(sct->timestamp, SCT_PENDING_INCLUSION_CHECK)); | 234 |
| 235 ProcessPendingEntries(); | |
| 86 } | 236 } |
| 87 | 237 |
| 88 void SingleTreeTracker::NewSTHObserved(const SignedTreeHead& sth) { | 238 void SingleTreeTracker::NewSTHObserved(const SignedTreeHead& sth) { |
| 89 DCHECK_EQ(ct_log_->key_id(), sth.log_id); | 239 DCHECK_EQ(ct_log_->key_id(), sth.log_id); |
| 90 | 240 |
| 91 if (!ct_log_->VerifySignedTreeHead(sth)) { | 241 if (!ct_log_->VerifySignedTreeHead(sth)) { |
| 92 // Sanity check the STH; the caller should have done this | 242 // Sanity check the STH; the caller should have done this |
| 93 // already, but being paranoid here. | 243 // already, but being paranoid here. |
| 94 // NOTE(eranm): Right now there's no way to get rid of this check here | 244 // 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 | 245 // as this is the first object in the chain that has an instance of |
| 96 // a CTLogVerifier to verify the STH. | 246 // a CTLogVerifier to verify the STH. |
| 97 return; | 247 return; |
| 98 } | 248 } |
| 99 | 249 |
| 100 // In order to avoid updating |verified_sth_| to an older STH in case | 250 // 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 | 251 // 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 | 252 // a larger tree size or that it is for the same tree size but has |
| 103 // a newer timestamp. | 253 // a newer timestamp. |
| 104 const bool sths_for_same_tree = verified_sth_.tree_size == sth.tree_size; | 254 const bool sths_for_same_tree = verified_sth_.tree_size == sth.tree_size; |
| 105 const bool received_sth_is_for_larger_tree = | 255 const bool received_sth_is_for_larger_tree = |
| 106 (verified_sth_.tree_size > sth.tree_size); | 256 (verified_sth_.tree_size < sth.tree_size); |
| 107 const bool received_sth_is_newer = (sth.timestamp > verified_sth_.timestamp); | 257 const bool received_sth_is_newer = (sth.timestamp > verified_sth_.timestamp); |
| 108 | 258 |
| 109 if (verified_sth_.timestamp.is_null() || received_sth_is_for_larger_tree || | 259 if (!verified_sth_.timestamp.is_null() && !received_sth_is_for_larger_tree && |
| 110 (sths_for_same_tree && received_sth_is_newer)) { | 260 !(sths_for_same_tree && received_sth_is_newer)) { |
| 111 verified_sth_ = sth; | 261 // Observed an old STH - do nothing. |
| 262 return; | |
| 112 } | 263 } |
| 113 | 264 |
| 114 // Find out which SCTs can now be checked for inclusion. | 265 verified_sth_ = sth; |
| 115 // TODO(eranm): Keep two maps of MerkleTreeLeaf instances, one for leaves | 266 |
| 116 // pending inclusion checks and one for leaves pending a new STH. | 267 // Find the first entry in the PENDING_NEWER_STH state - entries |
| 117 // The comparison function between MerkleTreeLeaf instances should use the | 268 // before that should be pending leaf index / inclusion proof, no |
| 118 // timestamp to determine sorting order, so that bulk moving from one | 269 // reason to inspect them. |
| 119 // map to the other can happen. | 270 auto first_entry_to_audit = std::find_if( |
| 120 auto entry = entries_status_.begin(); | 271 pending_entries_.begin(), pending_entries_.end(), |
| 121 while (entry != entries_status_.end() && | 272 [](std::pair<const EntryToAudit&, const EntryAuditState&> value) { |
| 122 entry->first < verified_sth_.timestamp) { | 273 return value.second.state == PENDING_NEWER_STH; |
| 123 entry->second = SCT_PENDING_INCLUSION_CHECK; | 274 }); |
| 124 ++entry; | 275 |
| 125 // TODO(eranm): Check inclusion here. | 276 // Find where to stop - this is the first entry whose timestamp + MMD |
| 277 // is greater than the STH's timestamp. | |
| 278 auto entry_to_stop_at = std::lower_bound( | |
| 279 first_entry_to_audit, pending_entries_.end(), sth.timestamp, | |
| 280 [](std::pair<const EntryToAudit&, const EntryAuditState&> value, | |
| 281 base::Time sth_timestamp) { | |
| 282 return IsSCTReadyForAudit(sth_timestamp, value.first.sct_timestamp); | |
| 283 }); | |
| 284 | |
| 285 // Update the state of all entries that can now be checked for inclusion. | |
| 286 for (auto curr_entry = first_entry_to_audit; curr_entry != entry_to_stop_at; | |
| 287 ++curr_entry) { | |
| 288 DCHECK(curr_entry->second.state == PENDING_NEWER_STH); | |
| 289 curr_entry->second.state = PENDING_INCLUSION_PROOF_REQUEST; | |
| 126 } | 290 } |
| 291 | |
| 292 ProcessPendingEntries(); | |
| 127 } | 293 } |
| 128 | 294 |
| 129 SingleTreeTracker::SCTInclusionStatus | 295 SingleTreeTracker::SCTInclusionStatus |
| 130 SingleTreeTracker::GetLogEntryInclusionStatus( | 296 SingleTreeTracker::GetLogEntryInclusionStatus( |
| 131 net::X509Certificate* cert, | 297 net::X509Certificate* cert, |
| 132 const net::ct::SignedCertificateTimestamp* sct) { | 298 const net::ct::SignedCertificateTimestamp* sct) { |
| 133 auto it = entries_status_.find(sct->timestamp); | 299 EntryToAudit entry(sct->timestamp); |
| 300 if (!GetLogEntryLeafHash(cert, sct, &entry.leaf_hash)) | |
| 301 return SCT_NOT_OBSERVED; | |
| 302 return GetAuditedEntryInclusionStatus(entry); | |
| 303 } | |
| 134 | 304 |
| 135 return it == entries_status_.end() ? SCT_NOT_OBSERVED : it->second; | 305 void SingleTreeTracker::ProcessPendingEntries() { |
| 306 for (auto it = pending_entries_.begin(); it != pending_entries_.end(); ++it) { | |
| 307 if (it->second.state != PENDING_INCLUSION_PROOF_REQUEST) { | |
| 308 continue; | |
| 309 } | |
| 310 | |
| 311 it->second.root_hash = | |
| 312 std::string(verified_sth_.sha256_root_hash, crypto::kSHA256Length); | |
| 313 | |
| 314 std::string leaf_hash( | |
| 315 reinterpret_cast<const char*>(it->first.leaf_hash.data), | |
| 316 crypto::kSHA256Length); | |
| 317 net::Error result = dns_client_->QueryAuditProof( | |
| 318 ct_log_->dns_domain(), leaf_hash, verified_sth_.tree_size, | |
| 319 &(it->second.proof), | |
| 320 base::Bind(&SingleTreeTracker::OnAuditProofObtained, | |
| 321 weak_factory_.GetWeakPtr(), it->first)); | |
| 322 // Handling proofs returned synchronously is not implemeted. | |
| 323 DCHECK_NE(result, net::OK); | |
| 324 if (result == net::ERR_IO_PENDING) { | |
| 325 // Successfully requested an inclusion proof - change entry state | |
| 326 // and continue to the next one. | |
| 327 it->second.state = INCLUSION_PROOF_REQUESTED; | |
| 328 } else if (result == net::ERR_TEMPORARILY_THROTTLED) { | |
| 329 dns_client_->NotifyWhenNotThrottled( | |
| 330 base::Bind(&SingleTreeTracker::ProcessPendingEntries, | |
| 331 weak_factory_.GetWeakPtr())); | |
| 332 // Exit the loop since all subsequent calls to QueryAuditProof | |
| 333 // will be throttled. | |
| 334 break; | |
| 335 } else if (result == net::ERR_NAME_RESOLUTION_FAILED) { | |
| 336 LogInclusionCheckResult(DNS_QUERY_NOT_POSSIBLE); | |
| 337 // Lookup failed due to bad DNS configuration, erase the entry and | |
| 338 // continue to the next one. | |
| 339 it = pending_entries_.erase(it); | |
| 340 // Break here if it's the last entry to avoid |it| being incremented | |
| 341 // by the for loop. | |
| 342 if (it == pending_entries_.end()) | |
| 343 break; | |
| 344 } else { | |
| 345 // BUG: an invalid argument was provided or an unexpected error | |
| 346 // was returned from LogDnsClient. | |
| 347 DCHECK_EQ(result, net::ERR_INVALID_ARGUMENT); | |
| 348 NOTREACHED(); | |
| 349 } | |
| 350 } | |
| 351 } | |
| 352 | |
| 353 SingleTreeTracker::SCTInclusionStatus | |
| 354 SingleTreeTracker::GetAuditedEntryInclusionStatus(const EntryToAudit& entry) { | |
| 355 const auto checked_entries_iterator = checked_entries_.Get( | |
| 356 std::string(reinterpret_cast<const char*>(entry.leaf_hash.data), | |
| 357 crypto::kSHA256Length)); | |
| 358 if (checked_entries_iterator != checked_entries_.end()) { | |
| 359 return SCT_INCLUDED_IN_LOG; | |
| 360 } | |
| 361 | |
| 362 auto pending_iterator = pending_entries_.find(entry); | |
| 363 if (pending_iterator != pending_entries_.end()) { | |
| 364 switch (pending_iterator->second.state) { | |
| 365 case PENDING_NEWER_STH: | |
| 366 return SCT_PENDING_NEWER_STH; | |
| 367 case PENDING_INCLUSION_PROOF_REQUEST: | |
| 368 case INCLUSION_PROOF_REQUESTED: | |
| 369 return SCT_PENDING_INCLUSION_CHECK; | |
| 370 } | |
| 371 NOTREACHED(); | |
| 372 } | |
| 373 | |
| 374 return SCT_NOT_OBSERVED; | |
| 375 } | |
| 376 | |
| 377 void SingleTreeTracker::OnAuditProofObtained(const EntryToAudit& entry, | |
| 378 int net_error) { | |
| 379 auto it = pending_entries_.find(entry); | |
| 380 // The entry may not be present if it was evacuated due to low memory | |
| 381 // pressure. | |
| 382 if (it == pending_entries_.end()) | |
| 383 return; | |
| 384 | |
| 385 DCHECK(it->second.state == INCLUSION_PROOF_REQUESTED); | |
| 386 | |
| 387 if (net_error == net::OK) { | |
| 388 std::string leaf_hash( | |
| 389 reinterpret_cast<const char*>(it->first.leaf_hash.data), | |
| 390 crypto::kSHA256Length); | |
| 391 | |
| 392 if (ct_log_->VerifyAuditProof(it->second.proof, it->second.root_hash, | |
| 393 leaf_hash)) { | |
| 394 checked_entries_.Put(leaf_hash, EntryAuditResult()); | |
| 395 LogInclusionCheckResult(GOT_VALID_INCLUSION_PROOF); | |
| 396 } else { | |
| 397 LogInclusionCheckResult(GOT_INVALID_INCLUSION_PROOF); | |
| 398 } | |
| 399 } else { | |
|
Ryan Sleevi
2016/12/14 01:34:15
Similar to previous notes
if (net_error != net::O
| |
| 400 // XXX(eranm): Should failures be cached? For now, they are not. | |
| 401 LogInclusionCheckResult(FAILED_GETTING_INCLUSION_PROOF); | |
| 402 } | |
| 403 | |
| 404 pending_entries_.erase(it); | |
| 405 } | |
| 406 | |
| 407 void SingleTreeTracker::OnMemoryPressure( | |
| 408 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { | |
| 409 switch (memory_pressure_level) { | |
| 410 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: | |
| 411 break; | |
| 412 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: | |
| 413 pending_entries_.clear(); | |
| 414 // Fall through to clearing the other cache. | |
| 415 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: | |
| 416 checked_entries_.Clear(); | |
| 417 break; | |
| 418 } | |
| 136 } | 419 } |
| 137 | 420 |
| 138 } // namespace certificate_transparency | 421 } // namespace certificate_transparency |
| OLD | NEW |