 Chromium Code Reviews
 Chromium Code Reviews Issue 1835823002:
  network_time_tracker: add temporary time protocol.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 1835823002:
  network_time_tracker: add temporary time protocol.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| 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/http/http_response_headers.h" | |
| 22 #include "net/url_request/url_fetcher.h" | |
| 23 #include "url/gurl.h" | |
| 18 | 24 | 
| 19 namespace network_time { | 25 namespace network_time { | 
| 20 | 26 | 
| 21 namespace { | 27 namespace { | 
| 22 | 28 | 
| 23 // Number of time measurements performed in a given network time calculation. | 29 // Number of time measurements performed in a given network time calculation. | 
| 24 const uint32_t kNumTimeMeasurements = 7; | 30 const uint32_t kNumTimeMeasurements = 7; | 
| 25 | 31 | 
| 26 // Amount of divergence allowed between wall clock and tick clock. | 32 // Amount of divergence allowed between wall clock and tick clock. | 
| 27 const uint32_t kClockDivergenceSeconds = 60; | 33 const uint32_t kClockDivergenceSeconds = 60; | 
| 28 | 34 | 
| 29 // Maximum time lapse before deserialized data are considered stale. | 35 // Maximum time lapse before deserialized data are considered stale. | 
| 30 const uint32_t kSerializedDataMaxAgeDays = 7; | 36 const uint32_t kSerializedDataMaxAgeDays = 7; | 
| 31 | 37 | 
| 32 // Name of a pref that stores the wall clock time, via |ToJsTime|. | 38 // Name of a pref that stores the wall clock time, via |ToJsTime|. | 
| 33 const char kPrefTime[] = "local"; | 39 const char kPrefTime[] = "local"; | 
| 34 | 40 | 
| 35 // Name of a pref that stores the tick clock time, via |ToInternalValue|. | 41 // Name of a pref that stores the tick clock time, via |ToInternalValue|. | 
| 36 const char kPrefTicks[] = "ticks"; | 42 const char kPrefTicks[] = "ticks"; | 
| 37 | 43 | 
| 38 // Name of a pref that stores the time uncertainty, via |ToInternalValue|. | 44 // Name of a pref that stores the time uncertainty, via |ToInternalValue|. | 
| 39 const char kPrefUncertainty[] = "uncertainty"; | 45 const char kPrefUncertainty[] = "uncertainty"; | 
| 40 | 46 | 
| 41 // Name of a pref that stores the network time via |ToJsTime|. | 47 // Name of a pref that stores the network time via |ToJsTime|. | 
| 42 const char kPrefNetworkTime[] = "network"; | 48 const char kPrefNetworkTime[] = "network"; | 
| 43 | 49 | 
| 50 // DO NOT SUBMIT | |
| 51 const char kTimeServiceURL[] = "http://localhost:6125/time/1/current"; | |
| 52 | |
| 53 // This is an ECDSA prime256v1 named-curve key. | |
| 54 // DO NOT SUBMIT. | |
| 55 const int kKeyVersion = 2; | |
| 56 const uint8_t kKeyPubBytes[] = { | |
| 57 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, | |
| 58 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, | |
| 59 0x42, 0x00, 0x04, 0xe0, 0x6b, 0x0d, 0x76, 0x75, 0xa3, 0x99, 0x7d, 0x7c, | |
| 60 0x1b, 0xd6, 0x3c, 0x73, 0xbb, 0x4b, 0xfe, 0x0a, 0xe7, 0x2f, 0x61, 0x3d, | |
| 61 0x77, 0x0a, 0xaa, 0x14, 0xd8, 0x5a, 0xbf, 0x14, 0x60, 0xec, 0xf6, 0x32, | |
| 62 0x77, 0xb5, 0xa7, 0xe6, 0x35, 0xa5, 0x61, 0xaf, 0xdc, 0xdf, 0x91, 0xce, | |
| 63 0x45, 0x34, 0x5f, 0x36, 0x85, 0x2f, 0xb9, 0x53, 0x00, 0x5d, 0x86, 0xe7, | |
| 64 0x04, 0x16, 0xe2, 0x3d, 0x21, 0x76, 0x2b}; | |
| 65 | |
| 44 } // namespace | 66 } // namespace | 
| 45 | 67 | 
| 46 // static | 68 // static | 
| 47 void NetworkTimeTracker::RegisterPrefs(PrefRegistrySimple* registry) { | 69 void NetworkTimeTracker::RegisterPrefs(PrefRegistrySimple* registry) { | 
| 48 registry->RegisterDictionaryPref(prefs::kNetworkTimeMapping, | 70 registry->RegisterDictionaryPref(prefs::kNetworkTimeMapping, | 
| 49 new base::DictionaryValue()); | 71 new base::DictionaryValue()); | 
| 50 } | 72 } | 
| 51 | 73 | 
| 52 NetworkTimeTracker::NetworkTimeTracker(scoped_ptr<base::Clock> clock, | 74 NetworkTimeTracker::NetworkTimeTracker(scoped_ptr<base::Clock> clock, | 
| 53 scoped_ptr<base::TickClock> tick_clock, | 75 scoped_ptr<base::TickClock> tick_clock, | 
| 54 PrefService* pref_service) | 76 PrefService* pref_service, | 
| 55 : clock_(std::move(clock)), | 77 net::URLRequestContextGetter* getter) | 
| 78 : getter_(getter), | |
| 79 clock_(std::move(clock)), | |
| 56 tick_clock_(std::move(tick_clock)), | 80 tick_clock_(std::move(tick_clock)), | 
| 57 pref_service_(pref_service) { | 81 pref_service_(pref_service) { | 
| 58 const base::DictionaryValue* time_mapping = | 82 const base::DictionaryValue* time_mapping = | 
| 59 pref_service_->GetDictionary(prefs::kNetworkTimeMapping); | 83 pref_service_->GetDictionary(prefs::kNetworkTimeMapping); | 
| 60 double time_js = 0; | 84 double time_js = 0; | 
| 61 double ticks_js = 0; | 85 double ticks_js = 0; | 
| 62 double network_time_js = 0; | 86 double network_time_js = 0; | 
| 63 double uncertainty_js = 0; | 87 double uncertainty_js = 0; | 
| 64 if (time_mapping->GetDouble(kPrefTime, &time_js) && | 88 if (time_mapping->GetDouble(kPrefTime, &time_js) && | 
| 65 time_mapping->GetDouble(kPrefTicks, &ticks_js) && | 89 time_mapping->GetDouble(kPrefTicks, &ticks_js) && | 
| 66 time_mapping->GetDouble(kPrefUncertainty, &uncertainty_js) && | 90 time_mapping->GetDouble(kPrefUncertainty, &uncertainty_js) && | 
| 67 time_mapping->GetDouble(kPrefNetworkTime, &network_time_js)) { | 91 time_mapping->GetDouble(kPrefNetworkTime, &network_time_js)) { | 
| 68 time_at_last_measurement_ = base::Time::FromJsTime(time_js); | 92 time_at_last_measurement_ = base::Time::FromJsTime(time_js); | 
| 69 ticks_at_last_measurement_ = base::TimeTicks::FromInternalValue( | 93 ticks_at_last_measurement_ = base::TimeTicks::FromInternalValue( | 
| 70 static_cast<int64_t>(ticks_js)); | 94 static_cast<int64_t>(ticks_js)); | 
| 71 network_time_uncertainty_ = base::TimeDelta::FromInternalValue( | 95 network_time_uncertainty_ = base::TimeDelta::FromInternalValue( | 
| 72 static_cast<int64_t>(uncertainty_js)); | 96 static_cast<int64_t>(uncertainty_js)); | 
| 73 network_time_at_last_measurement_ = base::Time::FromJsTime(network_time_js); | 97 network_time_at_last_measurement_ = base::Time::FromJsTime(network_time_js); | 
| 74 } | 98 } | 
| 75 base::Time now = clock_->Now(); | 99 base::Time now = clock_->Now(); | 
| 76 if (ticks_at_last_measurement_ > tick_clock_->NowTicks() || | 100 if (ticks_at_last_measurement_ > tick_clock_->NowTicks() || | 
| 77 time_at_last_measurement_ > now || | 101 time_at_last_measurement_ > now || | 
| 78 now - time_at_last_measurement_ > | 102 now - time_at_last_measurement_ > | 
| 79 base::TimeDelta::FromDays(kSerializedDataMaxAgeDays)) { | 103 base::TimeDelta::FromDays(kSerializedDataMaxAgeDays)) { | 
| 80 // Drop saved mapping if either clock has run backward, or the data are too | 104 // Drop saved mapping if either clock has run backward, or the data are too | 
| 81 // old. | 105 // old. | 
| 82 pref_service_->ClearPref(prefs::kNetworkTimeMapping); | 106 pref_service_->ClearPref(prefs::kNetworkTimeMapping); | 
| 83 network_time_at_last_measurement_ = base::Time(); // Reset. | 107 network_time_at_last_measurement_ = base::Time(); // Reset. | 
| 84 } | 108 } | 
| 109 if (getter != nullptr) { | |
| 110 query_signer_ = client_update_protocol::Ecdsa::Create( | |
| 111 kKeyVersion, | |
| 112 {reinterpret_cast<const char*>(kKeyPubBytes), sizeof(kKeyPubBytes)}); | |
| 113 base::TimeDelta period = base::TimeDelta::FromMinutes(60); | |
| 114 query_timer_.Start(FROM_HERE, period, this, | |
| 115 &NetworkTimeTracker::QueryTimeService); | |
| 116 } | |
| 85 } | 117 } | 
| 86 | 118 | 
| 87 NetworkTimeTracker::~NetworkTimeTracker() { | 119 NetworkTimeTracker::~NetworkTimeTracker() { | 
| 88 DCHECK(thread_checker_.CalledOnValidThread()); | 120 DCHECK(thread_checker_.CalledOnValidThread()); | 
| 89 } | 121 } | 
| 90 | 122 | 
| 123 void NetworkTimeTracker::QueryTimeService() { | |
| 124 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 125 | |
| 126 base::Time network_time; | |
| 127 if (GetNetworkTime(&network_time, nullptr)) { | |
| 
estark
2016/03/28 17:35:09
suggested comment:
// If GetNetworkTime() returns
 
mab
2016/03/28 19:56:26
Done.
 | |
| 128 return; | |
| 129 } | |
| 130 | |
| 131 std::string query_string; | |
| 132 query_signer_->SignRequest({"", 0}, &query_string); | |
| 133 GURL url(kTimeServiceURL); | |
| 134 GURL::Replacements replacements; | |
| 135 replacements.SetQueryStr(query_string); | |
| 136 url = url.ReplaceComponents(replacements); | |
| 137 | |
| 138 // This cancels any outstanding fetch. | |
| 139 time_fetcher_ = net::URLFetcher::Create(url, net::URLFetcher::GET, this); | |
| 140 if (time_fetcher_ == nullptr) { | |
| 141 VLOG(1) << "tried to make fetch happen; failed"; | |
| 142 return; | |
| 143 } | |
| 144 time_fetcher_->SetRequestContext(getter_); | |
| 145 // Not expecting any cookies, but just in case. | |
| 146 time_fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE | | |
| 147 net::LOAD_DO_NOT_SEND_COOKIES | | |
| 148 net::LOAD_DO_NOT_SAVE_COOKIES); | |
| 
estark
2016/03/28 17:35:09
might also want LOAD_BYPASS_CACHE and, for extra p
 
mab
2016/03/28 19:56:26
Done.
 | |
| 149 time_fetcher_->Start(); | |
| 150 fetch_started_ = tick_clock_->NowTicks(); | |
| 151 } | |
| 152 | |
| 153 static std::string GetServerETag(const net::URLFetcher* source) { | |
| 
estark
2016/03/28 17:35:09
This should go in an anonymous namespace at the to
 
mab
2016/03/28 19:56:26
Done.
 | |
| 154 const auto response_headers(source->GetResponseHeaders()); | |
| 
estark
2016/03/28 17:35:09
personal style nit: I'm not a huge fan of auto her
 
mab
2016/03/28 19:56:26
Done.
 | |
| 155 if (response_headers == nullptr) { | |
| 156 return std::string(); | |
| 157 } | |
| 158 std::string etag; | |
| 159 return response_headers->EnumerateHeader(nullptr, "ETag", &etag) | |
| 160 ? etag | |
| 161 : std::string(); | |
| 162 } | |
| 163 | |
| 164 void NetworkTimeTracker::OnURLFetchComplete(const net::URLFetcher* source) { | |
| 165 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 166 DCHECK(source); | |
| 167 if (source->GetStatus().status() != net::URLRequestStatus::SUCCESS && | |
| 168 source->GetResponseCode() != 200) { | |
| 169 VLOG(1) << "fetch failed, status=" << source->GetStatus().status() | |
| 170 << ",code=" << source->GetResponseCode(); | |
| 171 return; | |
| 172 } | |
| 173 | |
| 174 std::string response_body; | |
| 175 CHECK(source->GetResponseAsString(&response_body)); | |
| 
estark
2016/03/28 17:35:09
CHECK seems heavy-handed here. Can we just log and
 
mab
2016/03/28 19:56:26
Quite right, done.
 | |
| 176 DCHECK(query_signer_.get()); | |
| 177 if (!query_signer_->ValidateResponse(response_body, GetServerETag(source))) { | |
| 178 VLOG(1) << "invalid signature"; | |
| 
estark
2016/03/28 17:35:09
missing return here, I think?
 
mab
2016/03/28 19:56:26
Done, sorry.  I've been cheating a bunch since we'
 | |
| 179 } | |
| 180 response_body = response_body.substr(5); // Skips leading )]}'\n | |
| 181 base::JSONReader reader; | |
| 182 scoped_ptr<base::Value> value = reader.Read(response_body); | |
| 183 if (value == nullptr) { | |
| 184 VLOG(1) << "bad JSON"; | |
| 185 return; | |
| 186 } | |
| 187 const base::DictionaryValue* dict; | |
| 188 if (!value->GetAsDictionary(&dict)) { | |
| 189 VLOG(1) << "not a dictionary"; | |
| 190 return; | |
| 191 } | |
| 192 double current_time_millis; | |
| 193 if (!dict->GetDouble("current_time_millis", ¤t_time_millis)) { | |
| 194 VLOG(1) << "no current_time_millis"; | |
| 195 return; | |
| 196 } | |
| 197 // There's also a "server_nonce" key, which we can ignore. | |
| 198 base::Time current_time = base::Time::FromJsTime(current_time_millis); | |
| 199 base::TimeDelta resolution = base::TimeDelta::FromMilliseconds(1); | |
| 200 base::TimeDelta latency = tick_clock_->NowTicks() - fetch_started_; | |
| 201 UpdateNetworkTime(current_time, resolution, latency, tick_clock_->NowTicks()); | |
| 202 } | |
| 203 | |
| 91 void NetworkTimeTracker::UpdateNetworkTime(base::Time network_time, | 204 void NetworkTimeTracker::UpdateNetworkTime(base::Time network_time, | 
| 92 base::TimeDelta resolution, | 205 base::TimeDelta resolution, | 
| 93 base::TimeDelta latency, | 206 base::TimeDelta latency, | 
| 94 base::TimeTicks post_time) { | 207 base::TimeTicks post_time) { | 
| 95 DCHECK(thread_checker_.CalledOnValidThread()); | 208 DCHECK(thread_checker_.CalledOnValidThread()); | 
| 96 DVLOG(1) << "Network time updating to " | 209 DVLOG(1) << "Network time updating to " | 
| 97 << base::UTF16ToUTF8( | 210 << base::UTF16ToUTF8( | 
| 98 base::TimeFormatFriendlyDateAndTime(network_time)); | 211 base::TimeFormatFriendlyDateAndTime(network_time)); | 
| 99 // Update network time on every request to limit dependency on ticks lag. | 212 // Update network time on every request to limit dependency on ticks lag. | 
| 100 // TODO(mad): Find a heuristic to avoid augmenting the | 213 // TODO(mad): Find a heuristic to avoid augmenting the | 
| (...skipping 23 matching lines...) Expand all Loading... | |
| 124 | 237 | 
| 125 base::DictionaryValue time_mapping; | 238 base::DictionaryValue time_mapping; | 
| 126 time_mapping.SetDouble(kPrefTime, time_at_last_measurement_.ToJsTime()); | 239 time_mapping.SetDouble(kPrefTime, time_at_last_measurement_.ToJsTime()); | 
| 127 time_mapping.SetDouble(kPrefTicks, static_cast<double>( | 240 time_mapping.SetDouble(kPrefTicks, static_cast<double>( | 
| 128 ticks_at_last_measurement_.ToInternalValue())); | 241 ticks_at_last_measurement_.ToInternalValue())); | 
| 129 time_mapping.SetDouble(kPrefUncertainty, static_cast<double>( | 242 time_mapping.SetDouble(kPrefUncertainty, static_cast<double>( | 
| 130 network_time_uncertainty_.ToInternalValue())); | 243 network_time_uncertainty_.ToInternalValue())); | 
| 131 time_mapping.SetDouble(kPrefNetworkTime, | 244 time_mapping.SetDouble(kPrefNetworkTime, | 
| 132 network_time_at_last_measurement_.ToJsTime()); | 245 network_time_at_last_measurement_.ToJsTime()); | 
| 133 pref_service_->Set(prefs::kNetworkTimeMapping, time_mapping); | 246 pref_service_->Set(prefs::kNetworkTimeMapping, time_mapping); | 
| 247 | |
| 248 query_timer_.Reset(); | |
| 134 } | 249 } | 
| 135 | 250 | 
| 136 bool NetworkTimeTracker::GetNetworkTime(base::Time* network_time, | 251 bool NetworkTimeTracker::GetNetworkTime(base::Time* network_time, | 
| 137 base::TimeDelta* uncertainty) const { | 252 base::TimeDelta* uncertainty) const { | 
| 138 DCHECK(thread_checker_.CalledOnValidThread()); | 253 DCHECK(thread_checker_.CalledOnValidThread()); | 
| 139 DCHECK(network_time); | 254 DCHECK(network_time); | 
| 140 if (network_time_at_last_measurement_.is_null()) { | 255 if (network_time_at_last_measurement_.is_null()) { | 
| 141 return false; | 256 return false; | 
| 142 } | 257 } | 
| 143 DCHECK(!ticks_at_last_measurement_.is_null()); | 258 DCHECK(!ticks_at_last_measurement_.is_null()); | 
| (...skipping 16 matching lines...) Expand all Loading... | |
| 160 return false; | 275 return false; | 
| 161 } | 276 } | 
| 162 *network_time = network_time_at_last_measurement_ + tick_delta; | 277 *network_time = network_time_at_last_measurement_ + tick_delta; | 
| 163 if (uncertainty) { | 278 if (uncertainty) { | 
| 164 *uncertainty = network_time_uncertainty_ + divergence; | 279 *uncertainty = network_time_uncertainty_ + divergence; | 
| 165 } | 280 } | 
| 166 return true; | 281 return true; | 
| 167 } | 282 } | 
| 168 | 283 | 
| 169 } // namespace network_time | 284 } // namespace network_time | 
| OLD | NEW |