Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/network_time/network_time_tracker.h" | 5 #include "components/network_time/network_time_tracker.h" |
| 6 | 6 |
| 7 #include <stdint.h> | 7 #include <stdint.h> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/i18n/time_formatting.h" | 10 #include "base/i18n/time_formatting.h" |
| 11 #include "base/json/json_reader.h" | |
| 11 #include "base/logging.h" | 12 #include "base/logging.h" |
| 12 #include "base/strings/utf_string_conversions.h" | 13 #include "base/strings/utf_string_conversions.h" |
| 13 #include "base/time/tick_clock.h" | 14 #include "base/time/tick_clock.h" |
| 14 #include "build/build_config.h" | 15 #include "build/build_config.h" |
| 16 #include "components/client_update_protocol/ecdsa.h" | |
| 15 #include "components/network_time/network_time_pref_names.h" | 17 #include "components/network_time/network_time_pref_names.h" |
| 16 #include "components/prefs/pref_registry_simple.h" | 18 #include "components/prefs/pref_registry_simple.h" |
| 17 #include "components/prefs/pref_service.h" | 19 #include "components/prefs/pref_service.h" |
| 20 #include "net/base/load_flags.h" | |
| 21 #include "net/base/net_errors.h" | |
| 22 #include "net/http/http_response_headers.h" | |
| 23 #include "net/url_request/url_fetcher.h" | |
| 24 #include "net/url_request/url_fetcher_response_writer.h" | |
| 25 #include "net/url_request/url_request_context_getter.h" | |
| 18 | 26 |
| 19 namespace network_time { | 27 namespace network_time { |
| 20 | 28 |
| 21 namespace { | 29 namespace { |
| 22 | 30 |
| 31 // Minimum number of minutes between time queries. | |
| 32 const uint32_t kMinimumQueryDelayMinutes = 60; | |
| 33 | |
| 23 // Number of time measurements performed in a given network time calculation. | 34 // Number of time measurements performed in a given network time calculation. |
| 24 const uint32_t kNumTimeMeasurements = 7; | 35 const uint32_t kNumTimeMeasurements = 7; |
| 25 | 36 |
| 26 // Amount of divergence allowed between wall clock and tick clock. | 37 // Amount of divergence allowed between wall clock and tick clock. |
| 27 const uint32_t kClockDivergenceSeconds = 60; | 38 const uint32_t kClockDivergenceSeconds = 60; |
| 28 | 39 |
| 29 // Maximum time lapse before deserialized data are considered stale. | 40 // Maximum time lapse before deserialized data are considered stale. |
| 30 const uint32_t kSerializedDataMaxAgeDays = 7; | 41 const uint32_t kSerializedDataMaxAgeDays = 7; |
| 31 | 42 |
| 32 // Name of a pref that stores the wall clock time, via |ToJsTime|. | 43 // Name of a pref that stores the wall clock time, via |ToJsTime|. |
| 33 const char kPrefTime[] = "local"; | 44 const char kPrefTime[] = "local"; |
| 34 | 45 |
| 35 // Name of a pref that stores the tick clock time, via |ToInternalValue|. | 46 // Name of a pref that stores the tick clock time, via |ToInternalValue|. |
| 36 const char kPrefTicks[] = "ticks"; | 47 const char kPrefTicks[] = "ticks"; |
| 37 | 48 |
| 38 // Name of a pref that stores the time uncertainty, via |ToInternalValue|. | 49 // Name of a pref that stores the time uncertainty, via |ToInternalValue|. |
| 39 const char kPrefUncertainty[] = "uncertainty"; | 50 const char kPrefUncertainty[] = "uncertainty"; |
| 40 | 51 |
| 41 // Name of a pref that stores the network time via |ToJsTime|. | 52 // Name of a pref that stores the network time via |ToJsTime|. |
| 42 const char kPrefNetworkTime[] = "network"; | 53 const char kPrefNetworkTime[] = "network"; |
| 43 | 54 |
| 55 // Time server's maximum allowable clock skew, in seconds. (This is a property | |
| 56 // of the time server that we happen to know. It's unlikely that it would ever | |
| 57 // be that badly wrong, but all the same it's included here to document the very | |
| 58 // rough nature of the time service provided by this class.) | |
| 59 const uint32_t kTimeServerMaxSkewSeconds = 10; | |
| 60 | |
| 61 const char kTimeServiceURL[] = "http://clients2.google.com/time/1/current"; | |
| 62 | |
| 63 // This is an ECDSA prime256v1 named-curve key. | |
| 64 const int kKeyVersion = 1; | |
| 65 const uint8_t kKeyPubBytes[] = { | |
| 66 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, | |
| 67 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, | |
| 68 0x42, 0x00, 0x04, 0xeb, 0xd8, 0xad, 0x0b, 0x8f, 0x75, 0xe8, 0x84, 0x36, | |
| 69 0x23, 0x48, 0x14, 0x24, 0xd3, 0x93, 0x42, 0x25, 0x43, 0xc1, 0xde, 0x36, | |
| 70 0x29, 0xc6, 0x95, 0xca, 0xeb, 0x28, 0x85, 0xff, 0x09, 0xdc, 0x08, 0xec, | |
| 71 0x45, 0x74, 0x6e, 0x4b, 0xc3, 0xa5, 0xfd, 0x8a, 0x2f, 0x02, 0xa0, 0x4b, | |
| 72 0xc3, 0xc6, 0xa4, 0x7b, 0xa4, 0x41, 0xfc, 0xa7, 0x02, 0x54, 0xab, 0xe3, | |
| 73 0xe4, 0xb1, 0x00, 0xf5, 0xd5, 0x09, 0x11}; | |
| 74 | |
| 75 std::string GetServerProof(const net::URLFetcher* source) { | |
| 76 const net::HttpResponseHeaders* response_headers = | |
| 77 source->GetResponseHeaders(); | |
| 78 if (!response_headers) { | |
| 79 return std::string(); | |
| 80 } | |
| 81 std::string proof; | |
| 82 return response_headers->EnumerateHeader(nullptr, "x-cup-server-proof", | |
| 83 &proof) | |
| 84 ? proof | |
| 85 : std::string(); | |
| 86 } | |
| 87 | |
| 88 // Limits the amount of data that will be buffered from the server's response. | |
| 89 class SizeLimitingStringWriter : public net::URLFetcherStringWriter { | |
| 90 public: | |
| 91 SizeLimitingStringWriter(size_t limit) : limit_(limit) {} | |
| 92 | |
| 93 int Write(net::IOBuffer* buffer, | |
| 94 int num_bytes, | |
| 95 const net::CompletionCallback& callback) override { | |
| 96 if (data().length() + num_bytes > limit_) { | |
| 97 return net::ERR_FILE_TOO_BIG; | |
| 98 } | |
| 99 return net::URLFetcherStringWriter::Write(buffer, num_bytes, callback); | |
| 100 } | |
| 101 | |
| 102 private: | |
| 103 size_t limit_; | |
| 104 }; | |
| 105 | |
| 44 } // namespace | 106 } // namespace |
| 45 | 107 |
| 46 // static | 108 // static |
| 47 void NetworkTimeTracker::RegisterPrefs(PrefRegistrySimple* registry) { | 109 void NetworkTimeTracker::RegisterPrefs(PrefRegistrySimple* registry) { |
| 48 registry->RegisterDictionaryPref(prefs::kNetworkTimeMapping, | 110 registry->RegisterDictionaryPref(prefs::kNetworkTimeMapping, |
| 49 new base::DictionaryValue()); | 111 new base::DictionaryValue()); |
| 50 } | 112 } |
| 51 | 113 |
| 52 NetworkTimeTracker::NetworkTimeTracker( | 114 NetworkTimeTracker::NetworkTimeTracker( |
| 53 std::unique_ptr<base::Clock> clock, | 115 std::unique_ptr<base::Clock> clock, |
| 54 std::unique_ptr<base::TickClock> tick_clock, | 116 std::unique_ptr<base::TickClock> tick_clock, |
| 55 PrefService* pref_service) | 117 PrefService* pref_service, |
| 56 : clock_(std::move(clock)), | 118 scoped_refptr<net::URLRequestContextGetter> getter) |
| 119 : server_url_(kTimeServiceURL), | |
| 120 max_response_size_(1024), | |
| 121 getter_(std::move(getter)), | |
| 122 clock_(std::move(clock)), | |
| 57 tick_clock_(std::move(tick_clock)), | 123 tick_clock_(std::move(tick_clock)), |
| 58 pref_service_(pref_service) { | 124 pref_service_(pref_service) { |
| 59 const base::DictionaryValue* time_mapping = | 125 const base::DictionaryValue* time_mapping = |
| 60 pref_service_->GetDictionary(prefs::kNetworkTimeMapping); | 126 pref_service_->GetDictionary(prefs::kNetworkTimeMapping); |
| 61 double time_js = 0; | 127 double time_js = 0; |
| 62 double ticks_js = 0; | 128 double ticks_js = 0; |
| 63 double network_time_js = 0; | 129 double network_time_js = 0; |
| 64 double uncertainty_js = 0; | 130 double uncertainty_js = 0; |
| 65 if (time_mapping->GetDouble(kPrefTime, &time_js) && | 131 if (time_mapping->GetDouble(kPrefTime, &time_js) && |
| 66 time_mapping->GetDouble(kPrefTicks, &ticks_js) && | 132 time_mapping->GetDouble(kPrefTicks, &ticks_js) && |
| 67 time_mapping->GetDouble(kPrefUncertainty, &uncertainty_js) && | 133 time_mapping->GetDouble(kPrefUncertainty, &uncertainty_js) && |
| 68 time_mapping->GetDouble(kPrefNetworkTime, &network_time_js)) { | 134 time_mapping->GetDouble(kPrefNetworkTime, &network_time_js)) { |
| 69 time_at_last_measurement_ = base::Time::FromJsTime(time_js); | 135 time_at_last_measurement_ = base::Time::FromJsTime(time_js); |
| 70 ticks_at_last_measurement_ = base::TimeTicks::FromInternalValue( | 136 ticks_at_last_measurement_ = base::TimeTicks::FromInternalValue( |
| 71 static_cast<int64_t>(ticks_js)); | 137 static_cast<int64_t>(ticks_js)); |
| 72 network_time_uncertainty_ = base::TimeDelta::FromInternalValue( | 138 network_time_uncertainty_ = base::TimeDelta::FromInternalValue( |
| 73 static_cast<int64_t>(uncertainty_js)); | 139 static_cast<int64_t>(uncertainty_js)); |
| 74 network_time_at_last_measurement_ = base::Time::FromJsTime(network_time_js); | 140 network_time_at_last_measurement_ = base::Time::FromJsTime(network_time_js); |
| 75 } | 141 } |
| 76 base::Time now = clock_->Now(); | 142 base::Time now = clock_->Now(); |
| 77 if (ticks_at_last_measurement_ > tick_clock_->NowTicks() || | 143 if (ticks_at_last_measurement_ > tick_clock_->NowTicks() || |
| 78 time_at_last_measurement_ > now || | 144 time_at_last_measurement_ > now || |
| 79 now - time_at_last_measurement_ > | 145 now - time_at_last_measurement_ > |
| 80 base::TimeDelta::FromDays(kSerializedDataMaxAgeDays)) { | 146 base::TimeDelta::FromDays(kSerializedDataMaxAgeDays)) { |
| 81 // Drop saved mapping if either clock has run backward, or the data are too | 147 // Drop saved mapping if either clock has run backward, or the data are too |
| 82 // old. | 148 // old. |
| 83 pref_service_->ClearPref(prefs::kNetworkTimeMapping); | 149 pref_service_->ClearPref(prefs::kNetworkTimeMapping); |
| 84 network_time_at_last_measurement_ = base::Time(); // Reset. | 150 network_time_at_last_measurement_ = base::Time(); // Reset. |
| 85 } | 151 } |
| 152 | |
| 153 base::StringPiece public_key = {reinterpret_cast<const char*>(kKeyPubBytes), | |
| 154 sizeof(kKeyPubBytes)}; | |
| 155 query_signer_ = | |
| 156 client_update_protocol::Ecdsa::Create(kKeyVersion, public_key); | |
| 157 | |
| 158 QueueTimeQuery(base::TimeDelta::FromMinutes(kMinimumQueryDelayMinutes)); | |
| 86 } | 159 } |
| 87 | 160 |
| 88 NetworkTimeTracker::~NetworkTimeTracker() { | 161 NetworkTimeTracker::~NetworkTimeTracker() { |
| 89 DCHECK(thread_checker_.CalledOnValidThread()); | 162 DCHECK(thread_checker_.CalledOnValidThread()); |
| 90 } | 163 } |
| 91 | 164 |
| 92 void NetworkTimeTracker::UpdateNetworkTime(base::Time network_time, | 165 void NetworkTimeTracker::UpdateNetworkTime(base::Time network_time, |
| 93 base::TimeDelta resolution, | 166 base::TimeDelta resolution, |
| 94 base::TimeDelta latency, | 167 base::TimeDelta latency, |
| 95 base::TimeTicks post_time) { | 168 base::TimeTicks post_time) { |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 125 | 198 |
| 126 base::DictionaryValue time_mapping; | 199 base::DictionaryValue time_mapping; |
| 127 time_mapping.SetDouble(kPrefTime, time_at_last_measurement_.ToJsTime()); | 200 time_mapping.SetDouble(kPrefTime, time_at_last_measurement_.ToJsTime()); |
| 128 time_mapping.SetDouble(kPrefTicks, static_cast<double>( | 201 time_mapping.SetDouble(kPrefTicks, static_cast<double>( |
| 129 ticks_at_last_measurement_.ToInternalValue())); | 202 ticks_at_last_measurement_.ToInternalValue())); |
| 130 time_mapping.SetDouble(kPrefUncertainty, static_cast<double>( | 203 time_mapping.SetDouble(kPrefUncertainty, static_cast<double>( |
| 131 network_time_uncertainty_.ToInternalValue())); | 204 network_time_uncertainty_.ToInternalValue())); |
| 132 time_mapping.SetDouble(kPrefNetworkTime, | 205 time_mapping.SetDouble(kPrefNetworkTime, |
| 133 network_time_at_last_measurement_.ToJsTime()); | 206 network_time_at_last_measurement_.ToJsTime()); |
| 134 pref_service_->Set(prefs::kNetworkTimeMapping, time_mapping); | 207 pref_service_->Set(prefs::kNetworkTimeMapping, time_mapping); |
| 208 | |
| 209 query_timer_.Reset(); | |
|
mmenke
2016/05/02 17:47:28
This seems really unexpected. Suggest moving it t
mab
2016/05/05 19:57:42
Added a comment explaining why this is here.
| |
| 210 } | |
| 211 | |
| 212 void NetworkTimeTracker::SetTimeServerURLForTesting(const GURL& url) { | |
| 213 server_url_ = url; | |
| 214 } | |
| 215 | |
| 216 void NetworkTimeTracker::SetMaxResponseSizeForTesting(size_t limit) { | |
| 217 max_response_size_ = limit; | |
| 135 } | 218 } |
| 136 | 219 |
| 137 bool NetworkTimeTracker::GetNetworkTime(base::Time* network_time, | 220 bool NetworkTimeTracker::GetNetworkTime(base::Time* network_time, |
| 138 base::TimeDelta* uncertainty) const { | 221 base::TimeDelta* uncertainty) const { |
| 139 DCHECK(thread_checker_.CalledOnValidThread()); | 222 DCHECK(thread_checker_.CalledOnValidThread()); |
| 140 DCHECK(network_time); | 223 DCHECK(network_time); |
| 141 if (network_time_at_last_measurement_.is_null()) { | 224 if (network_time_at_last_measurement_.is_null()) { |
| 142 return false; | 225 return false; |
| 143 } | 226 } |
| 144 DCHECK(!ticks_at_last_measurement_.is_null()); | 227 DCHECK(!ticks_at_last_measurement_.is_null()); |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 160 network_time_at_last_measurement_ = base::Time(); | 243 network_time_at_last_measurement_ = base::Time(); |
| 161 return false; | 244 return false; |
| 162 } | 245 } |
| 163 *network_time = network_time_at_last_measurement_ + tick_delta; | 246 *network_time = network_time_at_last_measurement_ + tick_delta; |
| 164 if (uncertainty) { | 247 if (uncertainty) { |
| 165 *uncertainty = network_time_uncertainty_ + divergence; | 248 *uncertainty = network_time_uncertainty_ + divergence; |
| 166 } | 249 } |
| 167 return true; | 250 return true; |
| 168 } | 251 } |
| 169 | 252 |
| 253 void NetworkTimeTracker::QueryTimeService() { | |
| 254 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 255 | |
| 256 // If GetNetworkTime() returns true, the NetworkTimeTracker thinks it is in | |
| 257 // sync, so there is no need to query. | |
| 258 base::Time network_time; | |
| 259 if (GetNetworkTime(&network_time, nullptr)) { | |
| 260 return; | |
| 261 } | |
| 262 | |
| 263 std::string query_string; | |
| 264 query_signer_->SignRequest(nullptr, &query_string); | |
| 265 GURL url = server_url_; | |
| 266 GURL::Replacements replacements; | |
| 267 replacements.SetQueryStr(query_string); | |
| 268 url = url.ReplaceComponents(replacements); | |
| 269 | |
| 270 // This cancels any outstanding fetch. | |
| 271 time_fetcher_ = net::URLFetcher::Create(url, net::URLFetcher::GET, this); | |
| 272 if (!time_fetcher_) { | |
| 273 DVLOG(1) << "tried to make fetch happen; failed"; | |
| 274 return; | |
| 275 } | |
| 276 time_fetcher_->SaveResponseWithWriter( | |
| 277 std::unique_ptr<net::URLFetcherResponseWriter>( | |
| 278 new SizeLimitingStringWriter(max_response_size_))); | |
| 279 DCHECK(getter_); | |
| 280 time_fetcher_->SetRequestContext(getter_.get()); | |
| 281 // Not expecting any cookies, but just in case. | |
| 282 time_fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | | |
| 283 net::LOAD_DO_NOT_SAVE_COOKIES | | |
| 284 net::LOAD_DO_NOT_SEND_COOKIES | | |
| 285 net::LOAD_DO_NOT_SEND_AUTH_DATA); | |
| 286 time_fetcher_->Start(); | |
| 287 fetch_started_ = tick_clock_->NowTicks(); | |
| 288 } | |
| 289 | |
| 290 void NetworkTimeTracker::OnURLFetchComplete(const net::URLFetcher* source) { | |
| 291 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 292 DCHECK(time_fetcher_); | |
| 293 DCHECK_EQ(source, time_fetcher_.get()); | |
| 294 | |
| 295 // Since the timer is a repeating timer, it's unnecessary to reset it | |
| 296 // explicitly, but doing so is a convenient way to back off in the event of an | |
| 297 // error. Do that by default, and (if the query was successful) reset it to | |
| 298 // the standard interval. | |
|
mmenke
2016/05/02 17:47:28
Since you always set the timer delay, any reason n
mab
2016/05/05 19:57:42
I guess the main reason is that it obviates the ne
| |
| 299 DCHECK(query_timer_.IsRunning()); | |
| 300 base::TimeDelta delay = query_timer_.GetCurrentDelay(); | |
| 301 if (delay < base::TimeDelta::FromDays(2)) { | |
| 302 delay *= 2; | |
| 303 } | |
| 304 QueueTimeQuery(delay); | |
| 305 | |
| 306 if (time_fetcher_->GetStatus().status() != net::URLRequestStatus::SUCCESS && | |
| 307 time_fetcher_->GetResponseCode() != 200) { | |
| 308 DVLOG(1) << "fetch failed, status=" << time_fetcher_->GetStatus().status() | |
| 309 << ",code=" << time_fetcher_->GetResponseCode(); | |
| 310 time_fetcher_.reset(); | |
|
mmenke
2016/05/02 17:47:28
optional: May want to extract most of this functi
mab
2016/05/05 19:57:42
That's a great idea. Done.
| |
| 311 return; | |
| 312 } | |
| 313 | |
| 314 std::string response_body; | |
| 315 if (!time_fetcher_->GetResponseAsString(&response_body)) { | |
| 316 DVLOG(1) << "failed to get response"; | |
| 317 time_fetcher_.reset(); | |
| 318 return; | |
| 319 } | |
| 320 DCHECK(query_signer_); | |
| 321 if (!query_signer_->ValidateResponse(response_body, | |
| 322 GetServerProof(time_fetcher_.get()))) { | |
| 323 DVLOG(1) << "invalid signature"; | |
| 324 time_fetcher_.reset(); | |
| 325 return; | |
| 326 } | |
| 327 time_fetcher_.reset(); | |
| 328 response_body = response_body.substr(5); // Skips leading )]}'\n | |
| 329 std::unique_ptr<base::Value> value = base::JSONReader::Read(response_body); | |
| 330 if (!value) { | |
| 331 DVLOG(1) << "bad JSON"; | |
| 332 return; | |
| 333 } | |
| 334 const base::DictionaryValue* dict; | |
| 335 if (!value->GetAsDictionary(&dict)) { | |
| 336 DVLOG(1) << "not a dictionary"; | |
| 337 return; | |
| 338 } | |
| 339 double current_time_millis; | |
| 340 if (!dict->GetDouble("current_time_millis", ¤t_time_millis)) { | |
| 341 DVLOG(1) << "no current_time_millis"; | |
| 342 return; | |
| 343 } | |
| 344 // There is a "server_nonce" key here too, but it serves no purpose other than | |
| 345 // to make the server's response unpredictable. | |
| 346 base::Time current_time = base::Time::FromJsTime(current_time_millis); | |
| 347 base::TimeDelta resolution = | |
| 348 base::TimeDelta::FromMilliseconds(1) + | |
| 349 base::TimeDelta::FromSeconds(kTimeServerMaxSkewSeconds); | |
| 350 base::TimeDelta latency = tick_clock_->NowTicks() - fetch_started_; | |
| 351 UpdateNetworkTime(current_time, resolution, latency, tick_clock_->NowTicks()); | |
| 352 | |
| 353 // Normal completion, so, no backoff. | |
| 354 QueueTimeQuery(base::TimeDelta::FromMinutes(kMinimumQueryDelayMinutes)); | |
| 355 } | |
| 356 | |
| 357 void NetworkTimeTracker::QueueTimeQuery(base::TimeDelta delay) { | |
| 358 query_timer_.Start(FROM_HERE, delay, this, | |
| 359 &NetworkTimeTracker::QueryTimeService); | |
| 360 } | |
| 361 | |
| 170 } // namespace network_time | 362 } // namespace network_time |
| OLD | NEW |