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