Index: components/network_time/network_time_tracker.cc |
diff --git a/components/network_time/network_time_tracker.cc b/components/network_time/network_time_tracker.cc |
index e3dc58ecd754f6ab26550ecb56db4d9a9425a092..dfcaf74686dd54ba4be2268bd9e8cdc807c72adc 100644 |
--- a/components/network_time/network_time_tracker.cc |
+++ b/components/network_time/network_time_tracker.cc |
@@ -8,13 +8,19 @@ |
#include <utility> |
#include "base/i18n/time_formatting.h" |
+#include "base/json/json_reader.h" |
#include "base/logging.h" |
#include "base/strings/utf_string_conversions.h" |
#include "base/time/tick_clock.h" |
#include "build/build_config.h" |
+#include "components/client_update_protocol/ecdsa.h" |
#include "components/network_time/network_time_pref_names.h" |
#include "components/prefs/pref_registry_simple.h" |
#include "components/prefs/pref_service.h" |
+#include "net/base/load_flags.h" |
+#include "net/http/http_response_headers.h" |
+#include "net/url_request/url_fetcher.h" |
+#include "url/gurl.h" |
namespace network_time { |
@@ -41,6 +47,22 @@ const char kPrefUncertainty[] = "uncertainty"; |
// Name of a pref that stores the network time via |ToJsTime|. |
const char kPrefNetworkTime[] = "network"; |
+// DO NOT SUBMIT |
+const char kTimeServiceURL[] = "http://localhost:6125/time/1/current"; |
+ |
+// This is an ECDSA prime256v1 named-curve key. |
+// DO NOT SUBMIT. |
+const int kKeyVersion = 2; |
+const uint8_t kKeyPubBytes[] = { |
+ 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, |
+ 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, |
+ 0x42, 0x00, 0x04, 0xe0, 0x6b, 0x0d, 0x76, 0x75, 0xa3, 0x99, 0x7d, 0x7c, |
+ 0x1b, 0xd6, 0x3c, 0x73, 0xbb, 0x4b, 0xfe, 0x0a, 0xe7, 0x2f, 0x61, 0x3d, |
+ 0x77, 0x0a, 0xaa, 0x14, 0xd8, 0x5a, 0xbf, 0x14, 0x60, 0xec, 0xf6, 0x32, |
+ 0x77, 0xb5, 0xa7, 0xe6, 0x35, 0xa5, 0x61, 0xaf, 0xdc, 0xdf, 0x91, 0xce, |
+ 0x45, 0x34, 0x5f, 0x36, 0x85, 0x2f, 0xb9, 0x53, 0x00, 0x5d, 0x86, 0xe7, |
+ 0x04, 0x16, 0xe2, 0x3d, 0x21, 0x76, 0x2b}; |
+ |
} // namespace |
// static |
@@ -51,8 +73,10 @@ void NetworkTimeTracker::RegisterPrefs(PrefRegistrySimple* registry) { |
NetworkTimeTracker::NetworkTimeTracker(scoped_ptr<base::Clock> clock, |
scoped_ptr<base::TickClock> tick_clock, |
- PrefService* pref_service) |
- : clock_(std::move(clock)), |
+ PrefService* pref_service, |
+ net::URLRequestContextGetter* getter) |
+ : getter_(getter), |
+ clock_(std::move(clock)), |
tick_clock_(std::move(tick_clock)), |
pref_service_(pref_service) { |
const base::DictionaryValue* time_mapping = |
@@ -82,12 +106,101 @@ NetworkTimeTracker::NetworkTimeTracker(scoped_ptr<base::Clock> clock, |
pref_service_->ClearPref(prefs::kNetworkTimeMapping); |
network_time_at_last_measurement_ = base::Time(); // Reset. |
} |
+ if (getter != nullptr) { |
+ query_signer_ = client_update_protocol::Ecdsa::Create( |
+ kKeyVersion, |
+ {reinterpret_cast<const char*>(kKeyPubBytes), sizeof(kKeyPubBytes)}); |
+ base::TimeDelta period = base::TimeDelta::FromMinutes(60); |
+ query_timer_.Start(FROM_HERE, period, this, |
+ &NetworkTimeTracker::QueryTimeService); |
+ } |
} |
NetworkTimeTracker::~NetworkTimeTracker() { |
DCHECK(thread_checker_.CalledOnValidThread()); |
} |
+void NetworkTimeTracker::QueryTimeService() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ |
+ base::Time network_time; |
+ 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.
|
+ return; |
+ } |
+ |
+ std::string query_string; |
+ query_signer_->SignRequest({"", 0}, &query_string); |
+ GURL url(kTimeServiceURL); |
+ GURL::Replacements replacements; |
+ replacements.SetQueryStr(query_string); |
+ url = url.ReplaceComponents(replacements); |
+ |
+ // This cancels any outstanding fetch. |
+ time_fetcher_ = net::URLFetcher::Create(url, net::URLFetcher::GET, this); |
+ if (time_fetcher_ == nullptr) { |
+ VLOG(1) << "tried to make fetch happen; failed"; |
+ return; |
+ } |
+ time_fetcher_->SetRequestContext(getter_); |
+ // Not expecting any cookies, but just in case. |
+ time_fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE | |
+ net::LOAD_DO_NOT_SEND_COOKIES | |
+ 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.
|
+ time_fetcher_->Start(); |
+ fetch_started_ = tick_clock_->NowTicks(); |
+} |
+ |
+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.
|
+ 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.
|
+ if (response_headers == nullptr) { |
+ return std::string(); |
+ } |
+ std::string etag; |
+ return response_headers->EnumerateHeader(nullptr, "ETag", &etag) |
+ ? etag |
+ : std::string(); |
+} |
+ |
+void NetworkTimeTracker::OnURLFetchComplete(const net::URLFetcher* source) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ DCHECK(source); |
+ if (source->GetStatus().status() != net::URLRequestStatus::SUCCESS && |
+ source->GetResponseCode() != 200) { |
+ VLOG(1) << "fetch failed, status=" << source->GetStatus().status() |
+ << ",code=" << source->GetResponseCode(); |
+ return; |
+ } |
+ |
+ std::string response_body; |
+ 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.
|
+ DCHECK(query_signer_.get()); |
+ if (!query_signer_->ValidateResponse(response_body, GetServerETag(source))) { |
+ 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'
|
+ } |
+ response_body = response_body.substr(5); // Skips leading )]}'\n |
+ base::JSONReader reader; |
+ scoped_ptr<base::Value> value = reader.Read(response_body); |
+ if (value == nullptr) { |
+ VLOG(1) << "bad JSON"; |
+ return; |
+ } |
+ const base::DictionaryValue* dict; |
+ if (!value->GetAsDictionary(&dict)) { |
+ VLOG(1) << "not a dictionary"; |
+ return; |
+ } |
+ double current_time_millis; |
+ if (!dict->GetDouble("current_time_millis", ¤t_time_millis)) { |
+ VLOG(1) << "no current_time_millis"; |
+ return; |
+ } |
+ // There's also a "server_nonce" key, which we can ignore. |
+ base::Time current_time = base::Time::FromJsTime(current_time_millis); |
+ base::TimeDelta resolution = base::TimeDelta::FromMilliseconds(1); |
+ base::TimeDelta latency = tick_clock_->NowTicks() - fetch_started_; |
+ UpdateNetworkTime(current_time, resolution, latency, tick_clock_->NowTicks()); |
+} |
+ |
void NetworkTimeTracker::UpdateNetworkTime(base::Time network_time, |
base::TimeDelta resolution, |
base::TimeDelta latency, |
@@ -131,6 +244,8 @@ void NetworkTimeTracker::UpdateNetworkTime(base::Time network_time, |
time_mapping.SetDouble(kPrefNetworkTime, |
network_time_at_last_measurement_.ToJsTime()); |
pref_service_->Set(prefs::kNetworkTimeMapping, time_mapping); |
+ |
+ query_timer_.Reset(); |
} |
bool NetworkTimeTracker::GetNetworkTime(base::Time* network_time, |