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 DCHECK(leaf_hash_str.size() == crypto::kSHA256Length); | |
|
Ryan Sleevi
2016/11/14 05:03:28
1) Why not DCHECK_EQ
2) Is DCHECK vs CHECK the rig
Eran Messeri
2016/11/16 14:24:10
(1) Done
(2) Switched to CHECK, good point.
| |
| 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 EntryToAudit(base::Time timestamp) : sct_timestamp(timestamp) {} | |
|
Ryan Sleevi
2016/11/14 05:03:27
explicit? Necessary?
Eran Messeri
2016/11/16 14:24:10
Done, added.
| |
| 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 EntryAuditState(AuditState state) : state(state) {} | |
|
Ryan Sleevi
2016/11/14 05:03:28
explicit? Necessary?
Eran Messeri
2016/11/16 14:24:10
Done, added.
| |
| 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 } | |
| 49 | 180 |
| 50 SingleTreeTracker::SingleTreeTracker( | 181 SingleTreeTracker::SingleTreeTracker( |
| 51 scoped_refptr<const net::CTLogVerifier> ct_log) | 182 scoped_refptr<const net::CTLogVerifier> ct_log, |
| 52 : ct_log_(std::move(ct_log)) {} | 183 LogDnsClient* dns_client) |
| 184 : ct_log_(std::move(ct_log)), | |
| 185 checked_entries_(kCheckedEntriesCacheSize), | |
| 186 dns_client_(dns_client), | |
| 187 weak_factory_(this) { | |
| 188 memory_pressure_listener_.reset(new base::MemoryPressureListener(base::Bind( | |
| 189 &SingleTreeTracker::OnMemoryPressure, base::Unretained(this)))); | |
|
Ryan Sleevi
2016/11/14 05:03:28
Are you guaranteed this is safe? That is, I can't
Eran Messeri
2016/11/16 14:24:10
Switched to using a weakptr. The use of base::Unre
Ryan Sleevi
2016/12/14 01:34:15
Apologies for not providing better guidance. While
| |
| 190 } | |
| 53 | 191 |
| 54 SingleTreeTracker::~SingleTreeTracker() {} | 192 SingleTreeTracker::~SingleTreeTracker() {} |
| 55 | 193 |
| 56 void SingleTreeTracker::OnSCTVerified( | 194 void SingleTreeTracker::OnSCTVerified( |
| 57 net::X509Certificate* cert, | 195 net::X509Certificate* cert, |
| 58 const net::ct::SignedCertificateTimestamp* sct) { | 196 const net::ct::SignedCertificateTimestamp* sct) { |
| 59 DCHECK_EQ(ct_log_->key_id(), sct->log_id); | 197 DCHECK_EQ(ct_log_->key_id(), sct->log_id); |
| 60 | 198 |
| 61 // SCT was previously observed, so its status should not be changed. | 199 EntryToAudit entry(sct->timestamp); |
| 62 if (entries_status_.find(sct->timestamp) != entries_status_.end()) | 200 if (!GetLogEntryLeafHash(cert, sct, &entry.leaf_hash)) |
| 63 return; | 201 return; |
| 64 | 202 |
| 203 // Avoid queueing multiple instances of the same entry. | |
| 204 if (GetLogEntryInclusionStatus(entry) != SCT_NOT_OBSERVED) | |
| 205 return; | |
| 206 | |
| 207 if (pending_entries_.size() >= kPendingEntriesQueueSize) { | |
| 208 // Queue is full - cannot audit SCT. | |
| 209 LogCanBeCheckedForInclusionToUMA(NOT_AUDITED_QUEUE_FULL); | |
| 210 return; | |
| 211 } | |
| 212 | |
| 65 // If there isn't a valid STH or the STH is not fresh enough to check | 213 // 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. | 214 // inclusion against, store the SCT for future checking and return. |
| 67 if (verified_sth_.timestamp.is_null() || | 215 if (verified_sth_.timestamp.is_null() || |
| 68 (verified_sth_.timestamp < | 216 !IsSCTReadyForAudit(verified_sth_.timestamp, entry.sct_timestamp)) { |
| 69 (sct->timestamp + base::TimeDelta::FromHours(24)))) { | 217 pending_entries_.insert( |
| 70 entries_status_.insert( | 218 std::make_pair(std::move(entry), EntryAuditState(PENDING_NEWER_STH))); |
| 71 std::make_pair(sct->timestamp, SCT_PENDING_NEWER_STH)); | |
| 72 | 219 |
| 73 if (!verified_sth_.timestamp.is_null()) { | 220 if (verified_sth_.timestamp.is_null()) { |
| 221 LogCanBeCheckedForInclusionToUMA(VALID_STH_REQUIRED); | |
| 222 } else { | |
| 74 LogCanBeCheckedForInclusionToUMA(NEWER_STH_REQUIRED); | 223 LogCanBeCheckedForInclusionToUMA(NEWER_STH_REQUIRED); |
| 75 } else { | |
| 76 LogCanBeCheckedForInclusionToUMA(VALID_STH_REQUIRED); | |
| 77 } | 224 } |
| 225 } else { | |
|
Ryan Sleevi
2016/11/14 05:03:28
Should these } else { have short-circuited returns
Eran Messeri
2016/11/16 14:24:10
Done.
| |
| 226 LogCanBeCheckedForInclusionToUMA(CAN_BE_CHECKED); | |
| 227 pending_entries_.insert(std::make_pair( | |
| 228 std::move(entry), EntryAuditState(PENDING_INCLUSION_PROOF_REQUEST))); | |
| 78 | 229 |
| 79 return; | 230 ProcessPendingEntries(); |
| 80 } | 231 } |
| 81 | |
| 82 LogCanBeCheckedForInclusionToUMA(CAN_BE_CHECKED); | |
| 83 // TODO(eranm): Check inclusion here. | |
| 84 entries_status_.insert( | |
| 85 std::make_pair(sct->timestamp, SCT_PENDING_INCLUSION_CHECK)); | |
| 86 } | 232 } |
| 87 | 233 |
| 88 void SingleTreeTracker::NewSTHObserved(const SignedTreeHead& sth) { | 234 void SingleTreeTracker::NewSTHObserved(const SignedTreeHead& sth) { |
| 89 DCHECK_EQ(ct_log_->key_id(), sth.log_id); | 235 DCHECK_EQ(ct_log_->key_id(), sth.log_id); |
| 90 | 236 |
| 91 if (!ct_log_->VerifySignedTreeHead(sth)) { | 237 if (!ct_log_->VerifySignedTreeHead(sth)) { |
| 92 // Sanity check the STH; the caller should have done this | 238 // Sanity check the STH; the caller should have done this |
| 93 // already, but being paranoid here. | 239 // already, but being paranoid here. |
| 94 // NOTE(eranm): Right now there's no way to get rid of this check here | 240 // 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 | 241 // as this is the first object in the chain that has an instance of |
| 96 // a CTLogVerifier to verify the STH. | 242 // a CTLogVerifier to verify the STH. |
| 97 return; | 243 return; |
| 98 } | 244 } |
| 99 | 245 |
| 100 // In order to avoid updating |verified_sth_| to an older STH in case | 246 // 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 | 247 // 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 | 248 // a larger tree size or that it is for the same tree size but has |
| 103 // a newer timestamp. | 249 // a newer timestamp. |
| 104 const bool sths_for_same_tree = verified_sth_.tree_size == sth.tree_size; | 250 const bool sths_for_same_tree = verified_sth_.tree_size == sth.tree_size; |
| 105 const bool received_sth_is_for_larger_tree = | 251 const bool received_sth_is_for_larger_tree = |
| 106 (verified_sth_.tree_size > sth.tree_size); | 252 (verified_sth_.tree_size < sth.tree_size); |
| 107 const bool received_sth_is_newer = (sth.timestamp > verified_sth_.timestamp); | 253 const bool received_sth_is_newer = (sth.timestamp > verified_sth_.timestamp); |
| 108 | 254 |
| 109 if (verified_sth_.timestamp.is_null() || received_sth_is_for_larger_tree || | 255 if (!verified_sth_.timestamp.is_null() && !received_sth_is_for_larger_tree && |
| 110 (sths_for_same_tree && received_sth_is_newer)) { | 256 !(sths_for_same_tree && received_sth_is_newer)) { |
| 111 verified_sth_ = sth; | 257 // Observed an old STH - do nothing. |
| 258 return; | |
| 112 } | 259 } |
| 113 | 260 |
| 114 // Find out which SCTs can now be checked for inclusion. | 261 verified_sth_ = sth; |
| 115 // TODO(eranm): Keep two maps of MerkleTreeLeaf instances, one for leaves | 262 |
| 116 // pending inclusion checks and one for leaves pending a new STH. | 263 // Find the first entry in the PENDING_NEWER_STH state - entries |
| 117 // The comparison function between MerkleTreeLeaf instances should use the | 264 // before that should be pending leaf index / inclusion proof, no |
| 118 // timestamp to determine sorting order, so that bulk moving from one | 265 // reason to inspect them. |
| 119 // map to the other can happen. | 266 auto first_entry_to_audit = std::find_if( |
| 120 auto entry = entries_status_.begin(); | 267 pending_entries_.begin(), pending_entries_.end(), |
| 121 while (entry != entries_status_.end() && | 268 [](std::pair<const EntryToAudit&, const EntryAuditState&> value) { |
| 122 entry->first < verified_sth_.timestamp) { | 269 return value.second.state == PENDING_NEWER_STH; |
| 123 entry->second = SCT_PENDING_INCLUSION_CHECK; | 270 }); |
| 124 ++entry; | 271 |
| 125 // TODO(eranm): Check inclusion here. | 272 // Find where to stop - this is the first entry whose timestamp + MMD |
| 273 // is greater than the STH's timestamp. | |
| 274 auto entry_to_stop_at = std::lower_bound( | |
| 275 first_entry_to_audit, pending_entries_.end(), sth.timestamp, | |
| 276 [](std::pair<const EntryToAudit&, const EntryAuditState&> value, | |
| 277 base::Time sth_timestamp) { | |
| 278 return IsSCTReadyForAudit(sth_timestamp, value.first.sct_timestamp); | |
| 279 }); | |
| 280 | |
| 281 // Update the state of all entries that can now be checked for inclusion. | |
| 282 for (auto curr_entry = first_entry_to_audit; curr_entry != entry_to_stop_at; | |
| 283 ++curr_entry) { | |
| 284 DCHECK(curr_entry->second.state == PENDING_NEWER_STH); | |
| 285 curr_entry->second.state = PENDING_INCLUSION_PROOF_REQUEST; | |
| 126 } | 286 } |
| 287 | |
| 288 ProcessPendingEntries(); | |
| 127 } | 289 } |
| 128 | 290 |
| 129 SingleTreeTracker::SCTInclusionStatus | 291 SingleTreeTracker::SCTInclusionStatus |
| 130 SingleTreeTracker::GetLogEntryInclusionStatus( | 292 SingleTreeTracker::GetLogEntryInclusionStatus( |
| 131 net::X509Certificate* cert, | 293 net::X509Certificate* cert, |
| 132 const net::ct::SignedCertificateTimestamp* sct) { | 294 const net::ct::SignedCertificateTimestamp* sct) { |
| 133 auto it = entries_status_.find(sct->timestamp); | 295 EntryToAudit entry(sct->timestamp); |
| 296 if (!GetLogEntryLeafHash(cert, sct, &entry.leaf_hash)) | |
| 297 return SCT_NOT_OBSERVED; | |
| 298 return GetLogEntryInclusionStatus(entry); | |
| 299 } | |
| 134 | 300 |
| 135 return it == entries_status_.end() ? SCT_NOT_OBSERVED : it->second; | 301 void SingleTreeTracker::ProcessPendingEntries() { |
| 302 auto it = pending_entries_.begin(); | |
| 303 while (it != pending_entries_.end()) { | |
|
Ryan Sleevi
2016/11/14 05:03:28
DANGER: Why a while loop versus a for loop? It see
Eran Messeri
2016/11/16 14:24:10
Done - switched to a for loop.
| |
| 304 if (it->second.state == PENDING_INCLUSION_PROOF_REQUEST) { | |
| 305 it->second.root_hash = | |
| 306 std::string(verified_sth_.sha256_root_hash, crypto::kSHA256Length); | |
| 307 | |
| 308 std::string leaf_hash( | |
| 309 reinterpret_cast<const char*>(it->first.leaf_hash.data), | |
| 310 crypto::kSHA256Length); | |
| 311 net::Error result = dns_client_->QueryAuditProof( | |
| 312 ct_log_->dns_domain(), leaf_hash, verified_sth_.tree_size, | |
| 313 &(it->second.proof), | |
| 314 base::Bind(&SingleTreeTracker::OnAuditProofObtained, | |
| 315 weak_factory_.GetWeakPtr(), &(it->first))); | |
|
Ryan Sleevi
2016/11/14 05:03:27
DANGER: Messing with the key on a map strikes me a
Eran Messeri
2016/11/16 14:24:10
I wast taking a pointer that was used in a const c
| |
| 316 DCHECK(result != net::OK) | |
|
Ryan Sleevi
2016/11/14 05:03:28
DCHECK_EQ
Eran Messeri
2016/11/16 14:24:10
Done.
| |
| 317 << "Handling proofs returned synchronously is not implemeted."; | |
|
Ryan Sleevi
2016/11/14 05:03:27
Unnecessary
Eran Messeri
2016/11/16 14:24:10
Done.
| |
| 318 if (result == net::ERR_IO_PENDING) { | |
| 319 // Successfully requested an inclusion proof - change entry state | |
| 320 // and continue to the next one. | |
| 321 it->second.state = INCLUSION_PROOF_REQUESTED; | |
| 322 ++it; | |
| 323 } else if (result == net::ERR_TEMPORARILY_THROTTLED) { | |
| 324 dns_client_->NotifyWhenNotThrottled( | |
| 325 base::Bind(&SingleTreeTracker::ProcessPendingEntries, | |
| 326 weak_factory_.GetWeakPtr())); | |
| 327 // Exit the loop since all subsequent calls to QueryAuditProof | |
| 328 // will be throttled. | |
| 329 break; | |
| 330 } else if (result == net::ERR_NAME_RESOLUTION_FAILED) { | |
| 331 LogInclusionCheckResult(DNS_QUERY_NOT_POSSIBLE); | |
| 332 // Lookup failed due to bad DNS configuration, erase the entry and | |
| 333 // continue to the next one. | |
| 334 pending_entries_.erase(it++); | |
|
Ryan Sleevi
2016/11/14 05:03:28
BUG: This triggers all of the heuristics for "This
Eran Messeri
2016/11/16 14:24:10
Done.
| |
| 335 } else { | |
| 336 // BUG: an invalid argument was provided or an unexpected error | |
|
Ryan Sleevi
2016/11/14 05:03:27
Is this a bug in your code? Or are you saying it's
Eran Messeri
2016/11/16 14:24:10
There are two cases:
* ERR_INVALID_ARGUMENT was r
| |
| 337 // was returned from LogDnsClient. | |
| 338 DCHECK(result == net::ERR_INVALID_ARGUMENT); | |
|
Ryan Sleevi
2016/11/14 05:03:27
DCHECK_EQ
Eran Messeri
2016/11/16 14:24:10
Done.
| |
| 339 NOTREACHED(); | |
| 340 } | |
| 341 } else { | |
| 342 // Entry not pending inclusion proof request, continue to next one. | |
| 343 ++it; | |
|
Ryan Sleevi
2016/11/14 05:03:27
nit: error control goes first
Eran Messeri
2016/11/16 14:24:10
Done.
| |
| 344 } | |
| 345 } | |
| 346 } | |
| 347 | |
| 348 SingleTreeTracker::SCTInclusionStatus | |
| 349 SingleTreeTracker::GetLogEntryInclusionStatus(const EntryToAudit& entry) { | |
| 350 auto checked_entries_iterator = checked_entries_.Get( | |
|
Ryan Sleevi
2016/11/14 05:03:28
auto or const auto?
Eran Messeri
2016/11/16 14:24:10
Done.
| |
| 351 std::string(reinterpret_cast<const char*>(entry.leaf_hash.data), | |
| 352 crypto::kSHA256Length)); | |
| 353 if (checked_entries_iterator != checked_entries_.end()) { | |
| 354 // Only success is cached, so all values in |checked_entries_| | |
| 355 // should be true. | |
| 356 DCHECK(checked_entries_iterator->second); | |
| 357 return SCT_INCLUDED_IN_LOG; | |
| 358 } | |
| 359 | |
| 360 auto pending_iterator = pending_entries_.find(entry); | |
| 361 if (pending_iterator != pending_entries_.end()) { | |
| 362 switch (pending_iterator->second.state) { | |
| 363 case PENDING_NEWER_STH: | |
| 364 return SCT_PENDING_NEWER_STH; | |
| 365 case PENDING_INCLUSION_PROOF_REQUEST: | |
| 366 case INCLUSION_PROOF_REQUESTED: | |
| 367 return SCT_PENDING_INCLUSION_CHECK; | |
| 368 } | |
| 369 NOTREACHED(); | |
| 370 } | |
| 371 | |
| 372 return SCT_NOT_OBSERVED; | |
| 373 } | |
| 374 | |
| 375 void SingleTreeTracker::OnAuditProofObtained(const EntryToAudit* entry, | |
| 376 int net_error) { | |
| 377 auto it = pending_entries_.find(*entry); | |
| 378 // The entry may not be present if it was evacuated due to low memory | |
| 379 // pressure. | |
| 380 if (it == pending_entries_.end()) { | |
| 381 ProcessPendingEntries(); | |
| 382 return; | |
| 383 } | |
| 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, true); | |
| 395 LogInclusionCheckResult(GOT_VALID_INCLUSION_PROOF); | |
| 396 } else { | |
| 397 LogInclusionCheckResult(GOT_INVALID_INCLUSION_PROOF); | |
| 398 } | |
| 399 } else { | |
| 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 |