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", &current_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));