network_time_tracker: add temporary time protocol.
This adds queries of the temporary secure time server, with signature
verification, and a very conservative scheduler. (Checks once per hour,
queries only if sync believed to be lost.)
BUG=589700
Committed: https://crrev.com/ee2016a374b73ba35508ae26837755a23b3defa4
Cr-Commit-Position: refs/heads/master@{#392208}
Review-Url: https://codereview.chromium.org/1835823002
Cr-Commit-Position: refs/heads/master@{#392711}
diff --git a/components/client_update_protocol/ecdsa.cc b/components/client_update_protocol/ecdsa.cc
index dc41a73..7154793 100644
--- a/components/client_update_protocol/ecdsa.cc
+++ b/components/client_update_protocol/ecdsa.cc
@@ -98,9 +98,13 @@
return base::WrapUnique(new Ecdsa(key_version, public_key));
}
+void Ecdsa::OverrideNonceForTesting(int key_version, uint32_t nonce) {
+ DCHECK(!request_query_cup2key_.empty());
+ request_query_cup2key_ = base::StringPrintf("%d:%u", pub_key_version_, nonce);
+}
+
void Ecdsa::SignRequest(const base::StringPiece& request_body,
std::string* query_params) {
- DCHECK(!request_body.empty());
DCHECK(query_params);
// Generate a random nonce to use for freshness, build the cup2key query
diff --git a/components/client_update_protocol/ecdsa.h b/components/client_update_protocol/ecdsa.h
index 889a396..24e4171 100644
--- a/components/client_update_protocol/ecdsa.h
+++ b/components/client_update_protocol/ecdsa.h
@@ -51,16 +51,18 @@
// Validates a response given to a ping previously signed with
// SignRequest(). |response_body| contains the body of the response in
- // UTF-8. |server_proof| contains the ECDSA signature and observed request
- // hash, which is passed in the ETag HTTP header. Returns true if the response
- // is valid and the observed request hash matches the sent hash. This method
- // uses internal state that is set by a prior SignRequest() call.
+ // UTF-8. |signature| contains the ECDSA signature and observed request
+ // hash. Returns true if the response is valid and the observed request hash
+ // matches the sent hash. This method uses internal state that is set by a
+ // prior SignRequest() call.
bool ValidateResponse(const base::StringPiece& response_body,
- const base::StringPiece& server_etag);
+ const base::StringPiece& signature);
+
+ // Sets the key and nonce that were used to generate a signature that is baked
+ // into a unit test.
+ void OverrideNonceForTesting(int key_version, uint32_t nonce);
private:
- friend class CupEcdsaTest;
-
Ecdsa(int key_version, const base::StringPiece& public_key);
// The server keeps multiple signing keys; a version must be sent so that
diff --git a/components/client_update_protocol/ecdsa_unittest.cc b/components/client_update_protocol/ecdsa_unittest.cc
index ecb4341..8c1e933 100644
--- a/components/client_update_protocol/ecdsa_unittest.cc
+++ b/components/client_update_protocol/ecdsa_unittest.cc
@@ -46,11 +46,6 @@
ASSERT_TRUE(cup_.get());
}
- void OverrideNonce(uint32_t nonce) {
- cup_->request_query_cup2key_ =
- base::StringPrintf("%d:%u", cup_->pub_key_version_, nonce);
- }
-
Ecdsa& CUP() { return *cup_.get(); }
private:
@@ -86,7 +81,7 @@
// Invalid ETags must be gracefully rejected without a crash.
std::string query_discard;
CUP().SignRequest("Request_A", &query_discard);
- OverrideNonce(12345);
+ CUP().OverrideNonceForTesting(8, 12345);
// Expect a pass for a well-formed etag.
EXPECT_TRUE(CUP().ValidateResponse(
@@ -238,7 +233,7 @@
TEST_F(CupEcdsaTest, ValidateResponse_TestSigning) {
std::string query_discard;
CUP().SignRequest("Request_A", &query_discard);
- OverrideNonce(12345);
+ CUP().OverrideNonceForTesting(8, 12345);
// How to generate an ECDSA signature:
// echo -n Request_A | sha256sum | cut -d " " -f 1 > h
diff --git a/components/network_time/BUILD.gn b/components/network_time/BUILD.gn
index 15d67a5f..5bb5fb7 100644
--- a/components/network_time/BUILD.gn
+++ b/components/network_time/BUILD.gn
@@ -13,7 +13,9 @@
deps = [
"//base",
"//base:i18n",
+ "//components/client_update_protocol",
"//components/prefs",
+ "//net",
]
}
@@ -27,7 +29,10 @@
":network_time",
"//base",
"//base/test:test_support",
+ "//components/client_update_protocol",
"//components/prefs:test_support",
+ "//net",
+ "//net:test_support",
"//testing/gtest",
]
}
diff --git a/components/network_time/DEPS b/components/network_time/DEPS
index eac0761..f216f9c 100644
--- a/components/network_time/DEPS
+++ b/components/network_time/DEPS
@@ -1,3 +1,5 @@
include_rules = [
+ "+components/client_update_protocol",
"+components/prefs",
+ "+net",
]
diff --git a/components/network_time/network_time_tracker.cc b/components/network_time/network_time_tracker.cc
index 73f66ddf..1dbfd82 100644
--- a/components/network_time/network_time_tracker.cc
+++ b/components/network_time/network_time_tracker.cc
@@ -5,21 +5,35 @@
#include "components/network_time/network_time_tracker.h"
#include <stdint.h>
+#include <string>
#include <utility>
+#include "base/feature_list.h"
#include "base/i18n/time_formatting.h"
+#include "base/json/json_reader.h"
#include "base/logging.h"
+#include "base/message_loop/message_loop.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/base/net_errors.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_response_writer.h"
+#include "net/url_request/url_request_context_getter.h"
namespace network_time {
namespace {
+// Minimum number of minutes between time queries.
+const uint32_t kMinimumQueryDelayMinutes = 60;
+
// Number of time measurements performed in a given network time calculation.
const uint32_t kNumTimeMeasurements = 7;
@@ -41,6 +55,61 @@
// Name of a pref that stores the network time via |ToJsTime|.
const char kPrefNetworkTime[] = "network";
+// Time server's maximum allowable clock skew, in seconds. (This is a property
+// of the time server that we happen to know. It's unlikely that it would ever
+// be that badly wrong, but all the same it's included here to document the very
+// rough nature of the time service provided by this class.)
+const uint32_t kTimeServerMaxSkewSeconds = 10;
+
+const char kTimeServiceURL[] = "http://clients2.google.com/time/1/current";
+
+// Finch feature that enables network time service querying.
+const base::Feature kNetworkTimeServiceQuerying{
+ "NetworkTimeServiceQuerying", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// This is an ECDSA prime256v1 named-curve key.
+const int kKeyVersion = 1;
+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, 0xeb, 0xd8, 0xad, 0x0b, 0x8f, 0x75, 0xe8, 0x84, 0x36,
+ 0x23, 0x48, 0x14, 0x24, 0xd3, 0x93, 0x42, 0x25, 0x43, 0xc1, 0xde, 0x36,
+ 0x29, 0xc6, 0x95, 0xca, 0xeb, 0x28, 0x85, 0xff, 0x09, 0xdc, 0x08, 0xec,
+ 0x45, 0x74, 0x6e, 0x4b, 0xc3, 0xa5, 0xfd, 0x8a, 0x2f, 0x02, 0xa0, 0x4b,
+ 0xc3, 0xc6, 0xa4, 0x7b, 0xa4, 0x41, 0xfc, 0xa7, 0x02, 0x54, 0xab, 0xe3,
+ 0xe4, 0xb1, 0x00, 0xf5, 0xd5, 0x09, 0x11};
+
+std::string GetServerProof(const net::URLFetcher* source) {
+ const net::HttpResponseHeaders* response_headers =
+ source->GetResponseHeaders();
+ if (!response_headers) {
+ return std::string();
+ }
+ std::string proof;
+ return response_headers->EnumerateHeader(nullptr, "x-cup-server-proof",
+ &proof)
+ ? proof
+ : std::string();
+}
+
+// Limits the amount of data that will be buffered from the server's response.
+class SizeLimitingStringWriter : public net::URLFetcherStringWriter {
+ public:
+ explicit SizeLimitingStringWriter(size_t limit) : limit_(limit) {}
+
+ int Write(net::IOBuffer* buffer,
+ int num_bytes,
+ const net::CompletionCallback& callback) override {
+ if (data().length() + num_bytes > limit_) {
+ return net::ERR_FILE_TOO_BIG;
+ }
+ return net::URLFetcherStringWriter::Write(buffer, num_bytes, callback);
+ }
+
+ private:
+ size_t limit_;
+};
+
} // namespace
// static
@@ -52,8 +121,13 @@
NetworkTimeTracker::NetworkTimeTracker(
std::unique_ptr<base::Clock> clock,
std::unique_ptr<base::TickClock> tick_clock,
- PrefService* pref_service)
- : clock_(std::move(clock)),
+ PrefService* pref_service,
+ scoped_refptr<net::URLRequestContextGetter> getter)
+ : server_url_(kTimeServiceURL),
+ max_response_size_(1024),
+ getter_(std::move(getter)),
+ loop_(nullptr),
+ clock_(std::move(clock)),
tick_clock_(std::move(tick_clock)),
pref_service_(pref_service) {
const base::DictionaryValue* time_mapping =
@@ -83,6 +157,13 @@
pref_service_->ClearPref(prefs::kNetworkTimeMapping);
network_time_at_last_measurement_ = base::Time(); // Reset.
}
+
+ base::StringPiece public_key = {reinterpret_cast<const char*>(kKeyPubBytes),
+ sizeof(kKeyPubBytes)};
+ query_signer_ =
+ client_update_protocol::Ecdsa::Create(kKeyVersion, public_key);
+
+ QueueTimeQuery(base::TimeDelta::FromMinutes(kMinimumQueryDelayMinutes));
}
NetworkTimeTracker::~NetworkTimeTracker() {
@@ -132,6 +213,40 @@
time_mapping.SetDouble(kPrefNetworkTime,
network_time_at_last_measurement_.ToJsTime());
pref_service_->Set(prefs::kNetworkTimeMapping, time_mapping);
+
+ // Calls to update the network time can (as of this writing) come from various
+ // sources, e.g. organically from Omaha update checks. In that even, we may
+ // as well delay the next time server query. If |UpdateNetworkTime| is ever
+ // made into a private method, this can be removed.
+ query_timer_.Reset();
+}
+
+void NetworkTimeTracker::SetTimeServerURLForTesting(const GURL& url) {
+ server_url_ = url;
+}
+
+void NetworkTimeTracker::SetMaxResponseSizeForTesting(size_t limit) {
+ max_response_size_ = limit;
+}
+
+void NetworkTimeTracker::SetPublicKeyForTesting(const base::StringPiece& key) {
+ query_signer_ = client_update_protocol::Ecdsa::Create(kKeyVersion, key);
+}
+
+bool NetworkTimeTracker::QueryTimeServiceForTesting() {
+ QueryTimeService();
+ loop_ = base::MessageLoop::current(); // Gets Quit on completion.
+ return time_fetcher_ != nullptr;
+}
+
+void NetworkTimeTracker::WaitForFetchForTesting(uint32_t nonce) {
+ query_signer_->OverrideNonceForTesting(kKeyVersion, nonce);
+ base::MessageLoop::current()->Run();
+}
+
+base::TimeDelta NetworkTimeTracker::GetTimerDelayForTesting() const {
+ DCHECK(query_timer_.IsRunning());
+ return query_timer_.GetCurrentDelay();
}
bool NetworkTimeTracker::GetNetworkTime(base::Time* network_time,
@@ -167,4 +282,117 @@
return true;
}
+void NetworkTimeTracker::QueryTimeService() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Do not query the time service if not enabled via Finch.
+ if (!base::FeatureList::IsEnabled(kNetworkTimeServiceQuerying)) {
+ return;
+ }
+
+ // If GetNetworkTime() returns true, the NetworkTimeTracker thinks it is in
+ // sync, so there is no need to query.
+ base::Time network_time;
+ if (GetNetworkTime(&network_time, nullptr)) {
+ return;
+ }
+
+ std::string query_string;
+ query_signer_->SignRequest(nullptr, &query_string);
+ GURL url = server_url_;
+ 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_) {
+ DVLOG(1) << "tried to make fetch happen; failed";
+ return;
+ }
+ time_fetcher_->SaveResponseWithWriter(
+ std::unique_ptr<net::URLFetcherResponseWriter>(
+ new SizeLimitingStringWriter(max_response_size_)));
+ DCHECK(getter_);
+ time_fetcher_->SetRequestContext(getter_.get());
+ // Not expecting any cookies, but just in case.
+ time_fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
+ net::LOAD_DO_NOT_SAVE_COOKIES |
+ net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SEND_AUTH_DATA);
+ time_fetcher_->Start();
+ fetch_started_ = tick_clock_->NowTicks();
+}
+
+bool NetworkTimeTracker::UpdateTimeFromResponse() {
+ if (time_fetcher_->GetStatus().status() != net::URLRequestStatus::SUCCESS &&
+ time_fetcher_->GetResponseCode() != 200) {
+ DVLOG(1) << "fetch failed, status=" << time_fetcher_->GetStatus().status()
+ << ",code=" << time_fetcher_->GetResponseCode();
+ return false;
+ }
+
+ std::string response_body;
+ if (!time_fetcher_->GetResponseAsString(&response_body)) {
+ DVLOG(1) << "failed to get response";
+ return false;
+ }
+ DCHECK(query_signer_);
+ if (!query_signer_->ValidateResponse(response_body,
+ GetServerProof(time_fetcher_.get()))) {
+ DVLOG(1) << "invalid signature";
+ return false;
+ }
+ response_body = response_body.substr(5); // Skips leading )]}'\n
+ std::unique_ptr<base::Value> value = base::JSONReader::Read(response_body);
+ if (!value) {
+ DVLOG(1) << "bad JSON";
+ return false;
+ }
+ const base::DictionaryValue* dict;
+ if (!value->GetAsDictionary(&dict)) {
+ DVLOG(1) << "not a dictionary";
+ return false;
+ }
+ double current_time_millis;
+ if (!dict->GetDouble("current_time_millis", ¤t_time_millis)) {
+ DVLOG(1) << "no current_time_millis";
+ return false;
+ }
+ // There is a "server_nonce" key here too, but it serves no purpose other than
+ // to make the server's response unpredictable.
+ base::Time current_time = base::Time::FromJsTime(current_time_millis);
+ base::TimeDelta resolution =
+ base::TimeDelta::FromMilliseconds(1) +
+ base::TimeDelta::FromSeconds(kTimeServerMaxSkewSeconds);
+ base::TimeDelta latency = tick_clock_->NowTicks() - fetch_started_;
+ UpdateNetworkTime(current_time, resolution, latency, tick_clock_->NowTicks());
+ return true;
+}
+
+void NetworkTimeTracker::OnURLFetchComplete(const net::URLFetcher* source) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(time_fetcher_);
+ DCHECK_EQ(source, time_fetcher_.get());
+
+ if (!UpdateTimeFromResponse()) { // On error, back off.
+ DCHECK(query_timer_.IsRunning());
+ base::TimeDelta delay = query_timer_.GetCurrentDelay();
+ if (delay < base::TimeDelta::FromDays(2)) {
+ delay *= 2;
+ }
+ QueueTimeQuery(delay);
+ }
+ time_fetcher_.reset();
+ if (loop_ != nullptr) {
+ loop_->QuitWhenIdle();
+ loop_ = nullptr;
+ }
+}
+
+void NetworkTimeTracker::QueueTimeQuery(base::TimeDelta delay) {
+ query_timer_.Start(FROM_HERE, delay, this,
+ &NetworkTimeTracker::QueryTimeService);
+}
+
} // namespace network_time
diff --git a/components/network_time/network_time_tracker.h b/components/network_time/network_time_tracker.h
index 7f2eb87..69268ff 100644
--- a/components/network_time/network_time_tracker.h
+++ b/components/network_time/network_time_tracker.h
@@ -5,19 +5,35 @@
#ifndef COMPONENTS_NETWORK_TIME_NETWORK_TIME_TRACKER_H_
#define COMPONENTS_NETWORK_TIME_NETWORK_TIME_TRACKER_H_
+#include <stdint.h>
#include <memory>
+#include "base/gtest_prod_util.h"
#include "base/macros.h"
+#include "base/memory/ref_counted.h"
#include "base/threading/thread_checker.h"
#include "base/time/clock.h"
#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "url/gurl.h"
class PrefRegistrySimple;
class PrefService;
namespace base {
+class MessageLoop;
class TickClock;
-}
+} // namespace base
+
+namespace client_update_protocol {
+class Ecdsa;
+} // namespace client_update_protocol
+
+namespace net {
+class URLFetcher;
+class URLRequestContextGetter;
+} // namespace net
namespace network_time {
@@ -30,14 +46,18 @@
// A class that receives network time updates and can provide the network time
// for a corresponding local time. This class is not thread safe.
-class NetworkTimeTracker {
+class NetworkTimeTracker : public net::URLFetcherDelegate {
public:
static void RegisterPrefs(PrefRegistrySimple* registry);
+ // Constructor. Arguments may be stubbed out for tests. |getter|, if not
+ // null, will cause automatic queries to a time server. Otherwise, time is
+ // available only if |UpdateNetworkTime| is called.
NetworkTimeTracker(std::unique_ptr<base::Clock> clock,
std::unique_ptr<base::TickClock> tick_clock,
- PrefService* pref_service);
- ~NetworkTimeTracker();
+ PrefService* pref_service,
+ scoped_refptr<net::URLRequestContextGetter> getter);
+ ~NetworkTimeTracker() override;
// Sets |network_time| to an estimate of the true time. Returns true if time
// is available, and false otherwise. If |uncertainty| is non-NULL, it will
@@ -61,7 +81,44 @@
base::TimeDelta latency,
base::TimeTicks post_time);
+ void SetMaxResponseSizeForTesting(size_t limit);
+
+ void SetPublicKeyForTesting(const base::StringPiece& key);
+
+ void SetTimeServerURLForTesting(const GURL& url);
+
+ bool QueryTimeServiceForTesting();
+
+ void WaitForFetchForTesting(uint32_t nonce);
+
+ base::TimeDelta GetTimerDelayForTesting() const;
+
private:
+ // If synchronization has been lost, sends a query to the secure time service.
+ // Upon response, execution resumes in |OnURLFetchComplete|.
+ void QueryTimeService();
+
+ // Updates network time from a time server response, returning true
+ // if successful.
+ bool UpdateTimeFromResponse();
+
+ // net::URLFetcherDelegate:
+ // Called to process responses from the secure time service.
+ void OnURLFetchComplete(const net::URLFetcher* source) override;
+
+ // Sets the next time query to be run at the specified time.
+ void QueueTimeQuery(base::TimeDelta delay);
+
+ // State variables for internally-managed secure time service queries.
+ GURL server_url_;
+ size_t max_response_size_;
+ base::RepeatingTimer query_timer_;
+ scoped_refptr<net::URLRequestContextGetter> getter_;
+ std::unique_ptr<net::URLFetcher> time_fetcher_;
+ base::TimeTicks fetch_started_;
+ std::unique_ptr<client_update_protocol::Ecdsa> query_signer_;
+ base::MessageLoop* loop_; // For testing; quit on fetch complete.
+
// The |Clock| and |TickClock| are used to sanity-check one another, allowing
// the NetworkTimeTracker to notice e.g. suspend/resume events and clock
// resets.
diff --git a/components/network_time/network_time_tracker_unittest.cc b/components/network_time/network_time_tracker_unittest.cc
index b42196cf..6b7d75d 100644
--- a/components/network_time/network_time_tracker_unittest.cc
+++ b/components/network_time/network_time_tracker_unittest.cc
@@ -5,12 +5,21 @@
#include "components/network_time/network_time_tracker.h"
#include <memory>
+#include <utility>
#include "base/compiler_specific.h"
+#include "base/feature_list.h"
+#include "base/strings/stringprintf.h"
#include "base/test/simple_test_clock.h"
#include "base/test/simple_test_tick_clock.h"
+#include "components/client_update_protocol/ecdsa.h"
#include "components/network_time/network_time_pref_names.h"
#include "components/prefs/testing_pref_service.h"
+#include "net/http/http_response_headers.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace network_time {
@@ -19,25 +28,35 @@
public:
~NetworkTimeTrackerTest() override {}
- void SetUp() override {
+ NetworkTimeTrackerTest()
+ : io_thread_("IO thread"),
+ clock_(new base::SimpleTestClock),
+ tick_clock_(new base::SimpleTestTickClock),
+ test_server_(new net::EmbeddedTestServer) {
+ base::Thread::Options thread_options;
+ thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
+ EXPECT_TRUE(io_thread_.StartWithOptions(thread_options));
NetworkTimeTracker::RegisterPrefs(pref_service_.registry());
- clock_ = new base::SimpleTestClock();
- tick_clock_ = new base::SimpleTestTickClock();
- // Do this to be sure that |is_null| returns false.
- clock_->Advance(base::TimeDelta::FromDays(111));
- tick_clock_->Advance(base::TimeDelta::FromDays(222));
-
tracker_.reset(new NetworkTimeTracker(
std::unique_ptr<base::Clock>(clock_),
- std::unique_ptr<base::TickClock>(tick_clock_), &pref_service_));
+ std::unique_ptr<base::TickClock>(tick_clock_), &pref_service_,
+ new net::TestURLRequestContextGetter(io_thread_.task_runner())));
+
+ // Do this to be sure that |is_null| returns false.
+ clock_->Advance(base::TimeDelta::FromDays(111));
+ tick_clock_->Advance(base::TimeDelta::FromDays(222));
// Can not be smaller than 15, it's the NowFromSystemTime() resolution.
resolution_ = base::TimeDelta::FromMilliseconds(17);
latency_ = base::TimeDelta::FromMilliseconds(50);
adjustment_ = 7 * base::TimeDelta::FromMilliseconds(kTicksResolutionMs);
+
+ SetNetworkQueriesWithFinch(true);
}
+ void TearDown() override { io_thread_.Stop(); }
+
// Replaces |tracker_| with a new object, while preserving the
// testing clocks.
void Reset() {
@@ -46,10 +65,81 @@
base::SimpleTestTickClock* new_tick_clock = new base::SimpleTestTickClock();
new_tick_clock->SetNowTicks(tick_clock_->NowTicks());
clock_ = new_clock;
- tick_clock_= new_tick_clock;
+ tick_clock_ = new_tick_clock;
tracker_.reset(new NetworkTimeTracker(
std::unique_ptr<base::Clock>(clock_),
- std::unique_ptr<base::TickClock>(tick_clock_), &pref_service_));
+ std::unique_ptr<base::TickClock>(tick_clock_), &pref_service_,
+ new net::TestURLRequestContextGetter(io_thread_.task_runner())));
+ }
+
+ // Returns a valid time response. Update as follows:
+ //
+ // curl http://clients2.google.com/time/1/current?cup2key=1:123123123
+ //
+ // where 1 is the key version and 123123123 is the nonce. Copy the nonce, the
+ // response, and the x-cup-server-proof header into the test.
+ static std::unique_ptr<net::test_server::HttpResponse>
+ GoodTimeResponseHandler(const net::test_server::HttpRequest& request) {
+ net::test_server::BasicHttpResponse* response =
+ new net::test_server::BasicHttpResponse();
+ response->set_code(net::HTTP_OK);
+ response->set_content(
+ ")]}'\n"
+ "{\"current_time_millis\":1461621971825,\"server_nonce\":-6."
+ "006853099049523E85}");
+ response->AddCustomHeader(
+ "x-cup-server-proof",
+ "304402202e0f24db1ea69f1bbe81da4108f381fcf7a2781c53cf7663cb47083cb5fe8e"
+ "fd"
+ "022009d2b67c0deceaaf849f7c529be96701ed5f15d5efcaf401a94e0801accc9832:"
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
+ return std::unique_ptr<net::test_server::HttpResponse>(response);
+ }
+
+ // Good signature over invalid data, though made with a non-production key.
+ static std::unique_ptr<net::test_server::HttpResponse> BadDataResponseHandler(
+ const net::test_server::HttpRequest& request) {
+ net::test_server::BasicHttpResponse* response =
+ new net::test_server::BasicHttpResponse();
+ response->set_code(net::HTTP_OK);
+ response->set_content(
+ ")]}'\n"
+ "{\"current_time_millis\":NaN,\"server_nonce\":9.420921002039447E182}");
+ response->AddCustomHeader(
+ "x-cup-server-proof",
+ "3046022100a07aa437b24f1f6bb7ff6f6d1e004dd4bcb717c93e21d6bae5ef8d6d984c"
+ "86a7022100e423419ff49fae37b421ef6cdeab348b45c63b236ab365f36f4cd3b4d4d6"
+ "d852:"
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b85"
+ "5");
+ return std::unique_ptr<net::test_server::HttpResponse>(response);
+ }
+
+ static std::unique_ptr<net::test_server::HttpResponse>
+ BadSignatureResponseHandler(const net::test_server::HttpRequest& request) {
+ net::test_server::BasicHttpResponse* response =
+ new net::test_server::BasicHttpResponse();
+ response->set_code(net::HTTP_OK);
+ response->set_content(
+ ")]}'\n"
+ "{\"current_time_millis\":1461621971825,\"server_nonce\":-6."
+ "006853099049523E85}");
+ response->AddCustomHeader("x-cup-server-proof", "dead:beef");
+ return std::unique_ptr<net::test_server::HttpResponse>(response);
+ }
+
+ static std::unique_ptr<net::test_server::HttpResponse>
+ ServerErrorResponseHandler(const net::test_server::HttpRequest& request) {
+ net::test_server::BasicHttpResponse* response =
+ new net::test_server::BasicHttpResponse();
+ response->set_code(net::HTTP_INTERNAL_SERVER_ERROR);
+ return std::unique_ptr<net::test_server::HttpResponse>(response);
+ }
+
+ static std::unique_ptr<net::test_server::HttpResponse>
+ NetworkErrorResponseHandler(const net::test_server::HttpRequest& request) {
+ return std::unique_ptr<net::test_server::HttpResponse>(
+ new net::test_server::RawHttpResponse("", ""));
}
// Updates the notifier's time with the specified parameters.
@@ -69,6 +159,17 @@
}
protected:
+ void SetNetworkQueriesWithFinch(bool enable) {
+ base::FeatureList::ClearInstanceForTesting();
+ std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
+ if (enable) {
+ feature_list->InitializeFromCommandLine("NetworkTimeServiceQuerying", "");
+ }
+ base::FeatureList::SetInstance(std::move(feature_list));
+ }
+
+ base::Thread io_thread_;
+ base::MessageLoop message_loop_;
base::TimeDelta resolution_;
base::TimeDelta latency_;
base::TimeDelta adjustment_;
@@ -76,6 +177,7 @@
base::SimpleTestTickClock* tick_clock_;
TestingPrefServiceSimple pref_service_;
std::unique_ptr<NetworkTimeTracker> tracker_;
+ std::unique_ptr<net::EmbeddedTestServer> test_server_;
};
TEST_F(NetworkTimeTrackerTest, Uninitialized) {
@@ -131,7 +233,7 @@
tick_clock_->NowTicks());
base::Time out_network_time;
- EXPECT_TRUE(tracker_->GetNetworkTime(&out_network_time, NULL));
+ EXPECT_TRUE(tracker_->GetNetworkTime(&out_network_time, nullptr));
EXPECT_EQ(in_network_time, out_network_time);
}
@@ -185,7 +287,7 @@
tick_clock_->Advance(base::TimeDelta::FromSeconds(1));
clock_->Advance(base::TimeDelta::FromDays(1));
base::Time out_network_time;
- EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, NULL));
+ EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, nullptr));
}
TEST_F(NetworkTimeTrackerTest, FallBack) {
@@ -195,7 +297,7 @@
tick_clock_->Advance(base::TimeDelta::FromSeconds(1));
clock_->Advance(base::TimeDelta::FromDays(-1));
base::Time out_network_time;
- EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, NULL));
+ EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, nullptr));
}
TEST_F(NetworkTimeTrackerTest, SuspendAndResume) {
@@ -205,7 +307,7 @@
tick_clock_->NowTicks());
clock_->Advance(base::TimeDelta::FromHours(1));
base::Time out_network_time;
- EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, NULL));
+ EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, nullptr));
}
TEST_F(NetworkTimeTrackerTest, Serialize) {
@@ -237,7 +339,7 @@
tick_clock_->NowTicks());
base::Time out_network_time;
- EXPECT_TRUE(tracker_->GetNetworkTime(&out_network_time, NULL));
+ EXPECT_TRUE(tracker_->GetNetworkTime(&out_network_time, nullptr));
double local, network;
const base::DictionaryValue* saved_prefs =
pref_service_.GetDictionary(prefs::kNetworkTimeMapping);
@@ -248,7 +350,7 @@
prefs.SetDouble("network", network);
pref_service_.Set(prefs::kNetworkTimeMapping, prefs);
Reset();
- EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, NULL));
+ EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, nullptr));
}
TEST_F(NetworkTimeTrackerTest, SerializeWithLongDelay) {
@@ -258,10 +360,10 @@
UpdateNetworkTime(in_network_time - latency_ / 2, resolution_, latency_,
tick_clock_->NowTicks());
base::Time out_network_time;
- EXPECT_TRUE(tracker_->GetNetworkTime(&out_network_time, NULL));
+ EXPECT_TRUE(tracker_->GetNetworkTime(&out_network_time, nullptr));
AdvanceBoth(base::TimeDelta::FromDays(8));
Reset();
- EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, NULL));
+ EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, nullptr));
}
TEST_F(NetworkTimeTrackerTest, SerializeWithTickClockAdvance) {
@@ -271,10 +373,10 @@
UpdateNetworkTime(in_network_time - latency_ / 2, resolution_, latency_,
tick_clock_->NowTicks());
base::Time out_network_time;
- EXPECT_TRUE(tracker_->GetNetworkTime(&out_network_time, NULL));
+ EXPECT_TRUE(tracker_->GetNetworkTime(&out_network_time, nullptr));
tick_clock_->Advance(base::TimeDelta::FromDays(1));
Reset();
- EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, NULL));
+ EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, nullptr));
}
TEST_F(NetworkTimeTrackerTest, SerializeWithWallClockAdvance) {
@@ -285,10 +387,138 @@
tick_clock_->NowTicks());
base::Time out_network_time;
- EXPECT_TRUE(tracker_->GetNetworkTime(&out_network_time, NULL));
+ EXPECT_TRUE(tracker_->GetNetworkTime(&out_network_time, nullptr));
clock_->Advance(base::TimeDelta::FromDays(1));
Reset();
- EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, NULL));
+ EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, nullptr));
+}
+
+TEST_F(NetworkTimeTrackerTest, UpdateFromNetwork) {
+ base::Time out_network_time;
+ EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, nullptr));
+
+ test_server_->RegisterRequestHandler(
+ base::Bind(&NetworkTimeTrackerTest::GoodTimeResponseHandler));
+ EXPECT_TRUE(test_server_->Start());
+ tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
+ tracker_->WaitForFetchForTesting(123123123);
+
+ EXPECT_TRUE(tracker_->GetNetworkTime(&out_network_time, nullptr));
+ EXPECT_EQ(base::Time::UnixEpoch() +
+ base::TimeDelta::FromMilliseconds(1461621971825),
+ out_network_time);
+ // Should see no backoff in the success case.
+ EXPECT_EQ(base::TimeDelta::FromMinutes(60),
+ tracker_->GetTimerDelayForTesting());
+}
+
+TEST_F(NetworkTimeTrackerTest, NoNetworkQueryWhileSynced) {
+ base::Time in_network_time = clock_->Now();
+ UpdateNetworkTime(in_network_time, resolution_, latency_,
+ tick_clock_->NowTicks());
+ EXPECT_FALSE(
+ tracker_->QueryTimeServiceForTesting()); // No query should be started.
+}
+
+TEST_F(NetworkTimeTrackerTest, NoNetworkQueryWhileFeatureDisabled) {
+ // Disable network time queries and check that a query is not sent.
+ SetNetworkQueriesWithFinch(false);
+ EXPECT_FALSE(tracker_->QueryTimeServiceForTesting());
+
+ // Enable time queries and check that a query is sent.
+ SetNetworkQueriesWithFinch(true);
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
+ tracker_->WaitForFetchForTesting(123123123);
+}
+
+TEST_F(NetworkTimeTrackerTest, UpdateFromNetworkBadSignature) {
+ test_server_->RegisterRequestHandler(
+ base::Bind(&NetworkTimeTrackerTest::BadSignatureResponseHandler));
+ EXPECT_TRUE(test_server_->Start());
+ tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
+ tracker_->WaitForFetchForTesting(123123123);
+
+ base::Time out_network_time;
+ EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, nullptr));
+ EXPECT_EQ(base::TimeDelta::FromMinutes(120),
+ tracker_->GetTimerDelayForTesting());
+}
+
+static const uint8_t kDevKeyPubBytes[] = {
+ 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};
+
+TEST_F(NetworkTimeTrackerTest, UpdateFromNetworkBadData) {
+ test_server_->RegisterRequestHandler(
+ base::Bind(&NetworkTimeTrackerTest::BadDataResponseHandler));
+ EXPECT_TRUE(test_server_->Start());
+ base::StringPiece key = {reinterpret_cast<const char*>(kDevKeyPubBytes),
+ sizeof(kDevKeyPubBytes)};
+ tracker_->SetPublicKeyForTesting(key);
+ tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
+ tracker_->WaitForFetchForTesting(123123123);
+ base::Time out_network_time;
+ EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, nullptr));
+ EXPECT_EQ(base::TimeDelta::FromMinutes(120),
+ tracker_->GetTimerDelayForTesting());
+}
+
+TEST_F(NetworkTimeTrackerTest, UpdateFromNetworkServerError) {
+ test_server_->RegisterRequestHandler(
+ base::Bind(&NetworkTimeTrackerTest::ServerErrorResponseHandler));
+ EXPECT_TRUE(test_server_->Start());
+ tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
+ tracker_->WaitForFetchForTesting(123123123);
+
+ base::Time out_network_time;
+ EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, nullptr));
+ // Should see backoff in the error case.
+ EXPECT_EQ(base::TimeDelta::FromMinutes(120),
+ tracker_->GetTimerDelayForTesting());
+}
+
+TEST_F(NetworkTimeTrackerTest, UpdateFromNetworNetworkError) {
+ test_server_->RegisterRequestHandler(
+ base::Bind(&NetworkTimeTrackerTest::NetworkErrorResponseHandler));
+ EXPECT_TRUE(test_server_->Start());
+ tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
+ tracker_->WaitForFetchForTesting(123123123);
+
+ base::Time out_network_time;
+ EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, nullptr));
+ // Should see backoff in the error case.
+ EXPECT_EQ(base::TimeDelta::FromMinutes(120),
+ tracker_->GetTimerDelayForTesting());
+}
+
+TEST_F(NetworkTimeTrackerTest, UpdateFromNetworkLargeResponse) {
+ test_server_->RegisterRequestHandler(
+ base::Bind(&NetworkTimeTrackerTest::GoodTimeResponseHandler));
+ EXPECT_TRUE(test_server_->Start());
+ tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
+
+ base::Time out_network_time;
+
+ tracker_->SetMaxResponseSizeForTesting(3);
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
+ tracker_->WaitForFetchForTesting(123123123);
+ EXPECT_FALSE(tracker_->GetNetworkTime(&out_network_time, nullptr));
+
+ tracker_->SetMaxResponseSizeForTesting(1024);
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
+ tracker_->WaitForFetchForTesting(123123123);
+ EXPECT_TRUE(tracker_->GetNetworkTime(&out_network_time, nullptr));
}
} // namespace network_time
diff --git a/components/ssl_errors/error_classification_unittest.cc b/components/ssl_errors/error_classification_unittest.cc
index a4bb9eb..642eb73 100644
--- a/components/ssl_errors/error_classification_unittest.cc
+++ b/components/ssl_errors/error_classification_unittest.cc
@@ -17,6 +17,7 @@
#include "net/cert/x509_certificate.h"
#include "net/test/cert_test_util.h"
#include "net/test/test_certificate_data.h"
+#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -189,9 +190,13 @@
// |GetClockState|.
TestingPrefServiceSimple pref_service;
network_time::NetworkTimeTracker::RegisterPrefs(pref_service.registry());
+ base::MessageLoop loop;
network_time::NetworkTimeTracker network_time_tracker(
base::WrapUnique(new base::DefaultClock()),
- base::WrapUnique(new base::DefaultTickClock()), &pref_service);
+ base::WrapUnique(new base::DefaultTickClock()), &pref_service,
+ new net::TestURLRequestContextGetter(
+ base::ThreadTaskRunnerHandle::Get()));
+
EXPECT_EQ(
ssl_errors::ClockState::CLOCK_STATE_UNKNOWN,
ssl_errors::GetClockState(base::Time::Now(), &network_time_tracker));