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 |
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", ¤t_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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |