Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(725)

Side by Side Diff: components/network_time/network_time_tracker.cc

Issue 1835823002: network_time_tracker: add temporary time protocol. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: zea review 2, and fix test Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
29 // Minimum number of minutes between time queries.
30 const uint32_t kMinimumQueryDelayMinutes = 60;
31
23 // Number of time measurements performed in a given network time calculation. 32 // Number of time measurements performed in a given network time calculation.
24 const uint32_t kNumTimeMeasurements = 7; 33 const uint32_t kNumTimeMeasurements = 7;
25 34
26 // Amount of divergence allowed between wall clock and tick clock. 35 // Amount of divergence allowed between wall clock and tick clock.
27 const uint32_t kClockDivergenceSeconds = 60; 36 const uint32_t kClockDivergenceSeconds = 60;
28 37
29 // Maximum time lapse before deserialized data are considered stale. 38 // Maximum time lapse before deserialized data are considered stale.
30 const uint32_t kSerializedDataMaxAgeDays = 7; 39 const uint32_t kSerializedDataMaxAgeDays = 7;
31 40
32 // Name of a pref that stores the wall clock time, via |ToJsTime|. 41 // Name of a pref that stores the wall clock time, via |ToJsTime|.
33 const char kPrefTime[] = "local"; 42 const char kPrefTime[] = "local";
34 43
35 // Name of a pref that stores the tick clock time, via |ToInternalValue|. 44 // Name of a pref that stores the tick clock time, via |ToInternalValue|.
36 const char kPrefTicks[] = "ticks"; 45 const char kPrefTicks[] = "ticks";
37 46
38 // Name of a pref that stores the time uncertainty, via |ToInternalValue|. 47 // Name of a pref that stores the time uncertainty, via |ToInternalValue|.
39 const char kPrefUncertainty[] = "uncertainty"; 48 const char kPrefUncertainty[] = "uncertainty";
40 49
41 // Name of a pref that stores the network time via |ToJsTime|. 50 // Name of a pref that stores the network time via |ToJsTime|.
42 const char kPrefNetworkTime[] = "network"; 51 const char kPrefNetworkTime[] = "network";
43 52
53 const char kTimeServiceURL[] = "http://clients2.google.com/time/1/current";
54
55 // This is an ECDSA prime256v1 named-curve key.
56 const int kKeyVersion = 1;
57 const uint8_t kKeyPubBytes[] = {
58 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
59 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
60 0x42, 0x00, 0x04, 0xeb, 0xd8, 0xad, 0x0b, 0x8f, 0x75, 0xe8, 0x84, 0x36,
61 0x23, 0x48, 0x14, 0x24, 0xd3, 0x93, 0x42, 0x25, 0x43, 0xc1, 0xde, 0x36,
62 0x29, 0xc6, 0x95, 0xca, 0xeb, 0x28, 0x85, 0xff, 0x09, 0xdc, 0x08, 0xec,
63 0x45, 0x74, 0x6e, 0x4b, 0xc3, 0xa5, 0xfd, 0x8a, 0x2f, 0x02, 0xa0, 0x4b,
64 0xc3, 0xc6, 0xa4, 0x7b, 0xa4, 0x41, 0xfc, 0xa7, 0x02, 0x54, 0xab, 0xe3,
65 0xe4, 0xb1, 0x00, 0xf5, 0xd5, 0x09, 0x11};
66
67 static std::string GetServerProof(const net::URLFetcher* source) {
Ryan Sleevi 2016/04/28 01:08:34 This is in an unnamed namespace; the static is unn
mab 2016/04/28 02:29:04 Done.
68 const net::HttpResponseHeaders* response_headers =
69 source->GetResponseHeaders();
70 if (response_headers == nullptr) {
Ryan Sleevi 2016/04/28 01:08:34 if (!response_headers) ?
mab 2016/04/28 02:29:04 Sorry, didn't know that was preferred style. Done
71 return std::string();
72 }
73 std::string proof;
74 return response_headers->EnumerateHeader(nullptr, "x-cup-server-proof",
75 &proof)
76 ? proof
77 : std::string();
78 }
79
44 } // namespace 80 } // namespace
45 81
46 // static 82 // static
47 void NetworkTimeTracker::RegisterPrefs(PrefRegistrySimple* registry) { 83 void NetworkTimeTracker::RegisterPrefs(PrefRegistrySimple* registry) {
48 registry->RegisterDictionaryPref(prefs::kNetworkTimeMapping, 84 registry->RegisterDictionaryPref(prefs::kNetworkTimeMapping,
49 new base::DictionaryValue()); 85 new base::DictionaryValue());
50 } 86 }
51 87
52 NetworkTimeTracker::NetworkTimeTracker( 88 NetworkTimeTracker::NetworkTimeTracker(
53 std::unique_ptr<base::Clock> clock, 89 std::unique_ptr<base::Clock> clock,
54 std::unique_ptr<base::TickClock> tick_clock, 90 std::unique_ptr<base::TickClock> tick_clock,
55 PrefService* pref_service) 91 PrefService* pref_service,
56 : clock_(std::move(clock)), 92 net::URLRequestContextGetter* getter)
93 : getter_(getter),
94 clock_(std::move(clock)),
57 tick_clock_(std::move(tick_clock)), 95 tick_clock_(std::move(tick_clock)),
58 pref_service_(pref_service) { 96 pref_service_(pref_service) {
59 const base::DictionaryValue* time_mapping = 97 const base::DictionaryValue* time_mapping =
60 pref_service_->GetDictionary(prefs::kNetworkTimeMapping); 98 pref_service_->GetDictionary(prefs::kNetworkTimeMapping);
61 double time_js = 0; 99 double time_js = 0;
62 double ticks_js = 0; 100 double ticks_js = 0;
63 double network_time_js = 0; 101 double network_time_js = 0;
64 double uncertainty_js = 0; 102 double uncertainty_js = 0;
65 if (time_mapping->GetDouble(kPrefTime, &time_js) && 103 if (time_mapping->GetDouble(kPrefTime, &time_js) &&
66 time_mapping->GetDouble(kPrefTicks, &ticks_js) && 104 time_mapping->GetDouble(kPrefTicks, &ticks_js) &&
67 time_mapping->GetDouble(kPrefUncertainty, &uncertainty_js) && 105 time_mapping->GetDouble(kPrefUncertainty, &uncertainty_js) &&
68 time_mapping->GetDouble(kPrefNetworkTime, &network_time_js)) { 106 time_mapping->GetDouble(kPrefNetworkTime, &network_time_js)) {
69 time_at_last_measurement_ = base::Time::FromJsTime(time_js); 107 time_at_last_measurement_ = base::Time::FromJsTime(time_js);
70 ticks_at_last_measurement_ = base::TimeTicks::FromInternalValue( 108 ticks_at_last_measurement_ = base::TimeTicks::FromInternalValue(
71 static_cast<int64_t>(ticks_js)); 109 static_cast<int64_t>(ticks_js));
72 network_time_uncertainty_ = base::TimeDelta::FromInternalValue( 110 network_time_uncertainty_ = base::TimeDelta::FromInternalValue(
73 static_cast<int64_t>(uncertainty_js)); 111 static_cast<int64_t>(uncertainty_js));
74 network_time_at_last_measurement_ = base::Time::FromJsTime(network_time_js); 112 network_time_at_last_measurement_ = base::Time::FromJsTime(network_time_js);
75 } 113 }
76 base::Time now = clock_->Now(); 114 base::Time now = clock_->Now();
77 if (ticks_at_last_measurement_ > tick_clock_->NowTicks() || 115 if (ticks_at_last_measurement_ > tick_clock_->NowTicks() ||
78 time_at_last_measurement_ > now || 116 time_at_last_measurement_ > now ||
79 now - time_at_last_measurement_ > 117 now - time_at_last_measurement_ >
80 base::TimeDelta::FromDays(kSerializedDataMaxAgeDays)) { 118 base::TimeDelta::FromDays(kSerializedDataMaxAgeDays)) {
81 // Drop saved mapping if either clock has run backward, or the data are too 119 // Drop saved mapping if either clock has run backward, or the data are too
82 // old. 120 // old.
83 pref_service_->ClearPref(prefs::kNetworkTimeMapping); 121 pref_service_->ClearPref(prefs::kNetworkTimeMapping);
84 network_time_at_last_measurement_ = base::Time(); // Reset. 122 network_time_at_last_measurement_ = base::Time(); // Reset.
85 } 123 }
124 if (getter != nullptr) {
Ryan Sleevi 2016/04/28 01:08:34 Chromium base/ owners chided me enough, but I beli
mab 2016/04/28 02:29:03 Done.
125 query_signer_ = client_update_protocol::Ecdsa::Create(
126 kKeyVersion,
127 {reinterpret_cast<const char*>(kKeyPubBytes), sizeof(kKeyPubBytes)});
Ryan Sleevi 2016/04/28 01:08:34 Officially, Uniform Initialization Syntax is not a
mab 2016/04/28 02:29:03 Rewritten to follow the super-fresh advice at http
128 base::TimeDelta period =
129 base::TimeDelta::FromMinutes(kMinimumQueryDelayMinutes);
130 query_timer_.Start(FROM_HERE, period, this,
131 &NetworkTimeTracker::QueryTimeService);
132 }
86 } 133 }
87 134
88 NetworkTimeTracker::~NetworkTimeTracker() { 135 NetworkTimeTracker::~NetworkTimeTracker() {
89 DCHECK(thread_checker_.CalledOnValidThread()); 136 DCHECK(thread_checker_.CalledOnValidThread());
90 } 137 }
91 138
139 void NetworkTimeTracker::QueryTimeService() {
140 DCHECK(thread_checker_.CalledOnValidThread());
141
142 // If GetNetworkTime() returns true, the NetworkTimeTracker thinks
143 // it is in sync, so there is no need to query.
144 base::Time network_time;
145 if (GetNetworkTime(&network_time, nullptr)) {
146 return;
147 }
148
149 std::string query_string;
150 query_signer_->SignRequest({"", 0}, &query_string);
Ryan Sleevi 2016/04/28 01:08:34 Same with UIS here - I believe you should just use
mab 2016/04/28 02:29:03 Done.
151 GURL url(kTimeServiceURL);
152 GURL::Replacements replacements;
153 replacements.SetQueryStr(query_string);
Ryan Sleevi 2016/04/28 01:08:33 Is the assumption that kTimeServiceURL can never c
mab 2016/04/28 02:29:04 I guess so? That's the case now, and I wasn't ant
154 url = url.ReplaceComponents(replacements);
155
156 // This cancels any outstanding fetch.
157 time_fetcher_ = net::URLFetcher::Create(url, net::URLFetcher::GET, this);
158 if (time_fetcher_ == nullptr) {
Ryan Sleevi 2016/04/28 01:08:34 if (time_fetcher_)
mab 2016/04/28 02:29:04 if (!time_fetcher_) rather, but done.
159 DVLOG(1) << "tried to make fetch happen; failed";
160 return;
161 }
162 time_fetcher_->SetRequestContext(getter_);
163 // Not expecting any cookies, but just in case.
164 time_fetcher_->SetLoadFlags(
165 net::LOAD_DISABLE_CACHE | net::LOAD_DO_NOT_SEND_COOKIES |
166 net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_BYPASS_CACHE |
Ryan Sleevi 2016/04/28 01:08:34 Perhaps structure the cache and cookie directives
mab 2016/04/28 02:29:03 Done.
167 net::LOAD_DO_NOT_SEND_AUTH_DATA);
168 time_fetcher_->Start();
169 fetch_started_ = tick_clock_->NowTicks();
170 }
171
172 void NetworkTimeTracker::OnURLFetchComplete(const net::URLFetcher* source) {
173 DCHECK(thread_checker_.CalledOnValidThread());
174 DCHECK(source);
175 if (source->GetStatus().status() != net::URLRequestStatus::SUCCESS &&
176 source->GetResponseCode() != 200) {
177 DVLOG(1) << "fetch failed, status=" << source->GetStatus().status()
178 << ",code=" << source->GetResponseCode();
179 return;
180 }
181
182 std::string response_body;
183 if (!source->GetResponseAsString(&response_body)) {
Ryan Sleevi 2016/04/28 01:08:34 Note: Usually this is discouraged, because it may
mab 2016/04/28 02:29:04 What's the best alternative? I could check the le
mmenke 2016/04/28 15:17:08 You can't, actually - by the point OnURLFetchCompl
mab 2016/04/29 19:42:07 OK, I did the latter (= provided a size-limiting i
184 DVLOG(1) << "failed to get response";
185 return;
186 }
187 DCHECK(query_signer_.get());
Ryan Sleevi 2016/04/28 01:08:34 I believe you drop the .get(), it should still hav
mab 2016/04/28 02:29:04 Done.
188 if (!query_signer_->ValidateResponse(response_body, GetServerProof(source))) {
189 DVLOG(1) << "invalid signature";
190 return;
191 }
192 response_body = response_body.substr(5); // Skips leading )]}'\n
193 base::JSONReader reader;
194 std::unique_ptr<base::Value> value = reader.Read(response_body);
Ryan Sleevi 2016/04/28 01:08:34 Parsing JSON in the browser process is generally d
Robert Sesek 2016/04/28 16:51:23 How trusted is this data? Fetched from pinned, htt
Robert Sesek 2016/04/28 16:51:23 You can just use the static ::Read if you're not h
mmenke 2016/04/28 16:54:29 We look to be fetching from an http connection, so
waffles 2016/04/28 16:58:57 We fetch over HTTP protected by CUP with a pinned
Robert Sesek 2016/04/28 22:37:32 OK, then lgtm.
mab 2016/04/29 19:42:07 Done.
195 if (value == nullptr) {
196 DVLOG(1) << "bad JSON";
197 return;
198 }
199 const base::DictionaryValue* dict;
200 if (!value->GetAsDictionary(&dict)) {
201 DVLOG(1) << "not a dictionary";
202 return;
203 }
204 double current_time_millis;
205 if (!dict->GetDouble("current_time_millis", &current_time_millis)) {
206 DVLOG(1) << "no current_time_millis";
207 return;
208 }
209 // There's also a "server_nonce" key, which we can ignore.
Ryan Sleevi 2016/04/28 01:08:34 Who is the "we" here? https://groups.google.com/a
mab 2016/04/28 02:29:04 Tried to address both. If you ask me "why" again,
210 base::Time current_time = base::Time::FromJsTime(current_time_millis);
211 // The extra 10 seconds comes from a property of the time server that we
212 // happen to know, which is its maximum allowable clock skew. It's unlikely
213 // that it would ever be that badly wrong, but all the same it's included here
214 // to document the very rough nature of the time service provided by this
215 // class.
Ryan Sleevi 2016/04/28 01:08:34 // The 10 seconds happens to be a known property o
mab 2016/04/28 02:29:03 Done.
216 base::TimeDelta resolution =
217 base::TimeDelta::FromMilliseconds(1) + base::TimeDelta::FromSeconds(10);
218 base::TimeDelta latency = tick_clock_->NowTicks() - fetch_started_;
219 UpdateNetworkTime(current_time, resolution, latency, tick_clock_->NowTicks());
220 }
221
92 void NetworkTimeTracker::UpdateNetworkTime(base::Time network_time, 222 void NetworkTimeTracker::UpdateNetworkTime(base::Time network_time,
93 base::TimeDelta resolution, 223 base::TimeDelta resolution,
94 base::TimeDelta latency, 224 base::TimeDelta latency,
95 base::TimeTicks post_time) { 225 base::TimeTicks post_time) {
96 DCHECK(thread_checker_.CalledOnValidThread()); 226 DCHECK(thread_checker_.CalledOnValidThread());
97 DVLOG(1) << "Network time updating to " 227 DVLOG(1) << "Network time updating to "
98 << base::UTF16ToUTF8( 228 << base::UTF16ToUTF8(
99 base::TimeFormatFriendlyDateAndTime(network_time)); 229 base::TimeFormatFriendlyDateAndTime(network_time));
100 // Update network time on every request to limit dependency on ticks lag. 230 // Update network time on every request to limit dependency on ticks lag.
101 // TODO(mad): Find a heuristic to avoid augmenting the 231 // TODO(mad): Find a heuristic to avoid augmenting the
(...skipping 23 matching lines...) Expand all
125 255
126 base::DictionaryValue time_mapping; 256 base::DictionaryValue time_mapping;
127 time_mapping.SetDouble(kPrefTime, time_at_last_measurement_.ToJsTime()); 257 time_mapping.SetDouble(kPrefTime, time_at_last_measurement_.ToJsTime());
128 time_mapping.SetDouble(kPrefTicks, static_cast<double>( 258 time_mapping.SetDouble(kPrefTicks, static_cast<double>(
129 ticks_at_last_measurement_.ToInternalValue())); 259 ticks_at_last_measurement_.ToInternalValue()));
130 time_mapping.SetDouble(kPrefUncertainty, static_cast<double>( 260 time_mapping.SetDouble(kPrefUncertainty, static_cast<double>(
131 network_time_uncertainty_.ToInternalValue())); 261 network_time_uncertainty_.ToInternalValue()));
132 time_mapping.SetDouble(kPrefNetworkTime, 262 time_mapping.SetDouble(kPrefNetworkTime,
133 network_time_at_last_measurement_.ToJsTime()); 263 network_time_at_last_measurement_.ToJsTime());
134 pref_service_->Set(prefs::kNetworkTimeMapping, time_mapping); 264 pref_service_->Set(prefs::kNetworkTimeMapping, time_mapping);
265
266 if (!query_timer_.user_task().is_null()) {
267 query_timer_.Reset();
Ryan Sleevi 2016/04/28 01:08:34 Why the conditional? Why not explicitly call it al
mab 2016/04/28 02:29:03 Lamely, it's for unit tests. (You can't Reset the
mmenke 2016/04/28 15:17:08 I'd suggest using a real getter in unit tests. ne
mab 2016/04/29 19:42:07 Done. Solid unit tests are very much in scope for
268 }
135 } 269 }
136 270
137 bool NetworkTimeTracker::GetNetworkTime(base::Time* network_time, 271 bool NetworkTimeTracker::GetNetworkTime(base::Time* network_time,
138 base::TimeDelta* uncertainty) const { 272 base::TimeDelta* uncertainty) const {
139 DCHECK(thread_checker_.CalledOnValidThread()); 273 DCHECK(thread_checker_.CalledOnValidThread());
140 DCHECK(network_time); 274 DCHECK(network_time);
141 if (network_time_at_last_measurement_.is_null()) { 275 if (network_time_at_last_measurement_.is_null()) {
142 return false; 276 return false;
143 } 277 }
144 DCHECK(!ticks_at_last_measurement_.is_null()); 278 DCHECK(!ticks_at_last_measurement_.is_null());
(...skipping 16 matching lines...) Expand all
161 return false; 295 return false;
162 } 296 }
163 *network_time = network_time_at_last_measurement_ + tick_delta; 297 *network_time = network_time_at_last_measurement_ + tick_delta;
164 if (uncertainty) { 298 if (uncertainty) {
165 *uncertainty = network_time_uncertainty_ + divergence; 299 *uncertainty = network_time_uncertainty_ + divergence;
166 } 300 }
167 return true; 301 return true;
168 } 302 }
169 303
170 } // namespace network_time 304 } // namespace network_time
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698