Add UMA metrics for detecting and reporting update certificate changes.

LibcurlHttpFetcher checks if the update server certificate has changed
since last update, and stores an eventual report in prefs. UpdateCheckScheduler
submits to UMA reports from the previous update.

BUG=chromium-os:19842
TEST=Included unittest for the new class and tested locally on an Alex device,
since we need to verify against the actual server certificates.

Change-Id: I5bee5d648982cd7618db09b67d5bff377eaa1fc1
Reviewed-on: http://gerrit.chromium.org/gerrit/7565
Reviewed-by: Bruno Pontes Soares Rocha <bpontes@chromium.org>
Tested-by: Bruno Pontes Soares Rocha <bpontes@chromium.org>
Reviewed-by: Andrew de los Reyes <adlr@chromium.org>
Reviewed-by: Darin Petkov <petkov@chromium.org>
diff --git a/certificate_checker_unittest.cc b/certificate_checker_unittest.cc
new file mode 100644
index 0000000..ccff258
--- /dev/null
+++ b/certificate_checker_unittest.cc
@@ -0,0 +1,166 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include <base/string_util.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <metrics/metrics_library_mock.h>
+
+#include "update_engine/certificate_checker.h"
+#include "update_engine/certificate_checker_mock.h"
+#include "update_engine/prefs_mock.h"
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+using ::testing::SetArrayArgument;
+using std::string;
+
+namespace chromeos_update_engine {
+
+class CertificateCheckerTest : public testing::Test {
+ public:
+  CertificateCheckerTest() {}
+
+ protected:
+  virtual void SetUp() {
+    depth_ = 0;
+    length_ = 4;
+    digest_[0] = 0x17;
+    digest_[1] = 0x7D;
+    digest_[2] = 0x07;
+    digest_[3] = 0x5F;
+    digest_hex_ = "177D075F";
+    diff_digest_hex_ = "1234ABCD";
+    cert_key_prefix_ = kPrefsUpdateServerCertificate;
+    server_to_check_ = CertificateChecker::kUpdate;
+    cert_key_ = StringPrintf("%s-%d-%d",
+                             cert_key_prefix_.c_str(),
+                             server_to_check_,
+                             depth_);
+    kCertChanged = "Updater.ServerCertificateChanged";
+    kCertFailed = "Updater.ServerCertificateFailed";
+    CertificateChecker::set_metrics_lib(&metrics_lib_);
+    CertificateChecker::set_prefs(&prefs_);
+    CertificateChecker::set_openssl_wrapper(&openssl_wrapper_);
+  }
+
+  virtual void TearDown() {}
+
+  MetricsLibraryMock metrics_lib_;
+  PrefsMock prefs_;
+  OpenSSLWrapperMock openssl_wrapper_;
+  // Parameters of our mock certificate digest.
+  int depth_;
+  unsigned int length_;
+  unsigned char digest_[4];
+  string digest_hex_;
+  string diff_digest_hex_;
+  string cert_key_prefix_;
+  CertificateChecker::ServerToCheck server_to_check_;
+  string cert_key_;
+  string kCertChanged;
+  string kCertFailed;
+};
+
+// check certificate change, new
+TEST_F(CertificateCheckerTest, NewCertificate) {
+  EXPECT_CALL(openssl_wrapper_, GetCertificateDigest(NULL, _, _, _))
+      .WillOnce(DoAll(
+          SetArgumentPointee<1>(depth_),
+          SetArgumentPointee<2>(length_),
+          SetArrayArgument<3>(digest_, digest_ + 4),
+          Return(true)));
+  EXPECT_CALL(prefs_, GetString(cert_key_, _))
+      .WillOnce(Return(false));
+  EXPECT_CALL(prefs_, SetString(cert_key_, digest_hex_))
+      .WillOnce(Return(true));
+  ASSERT_TRUE(CertificateChecker::CheckCertificateChange(
+      server_to_check_, 1, NULL));
+}
+
+// check certificate change, unchanged
+TEST_F(CertificateCheckerTest, SameCertificate) {
+  EXPECT_CALL(openssl_wrapper_, GetCertificateDigest(NULL, _, _, _))
+      .WillOnce(DoAll(
+          SetArgumentPointee<1>(depth_),
+          SetArgumentPointee<2>(length_),
+          SetArrayArgument<3>(digest_, digest_ + 4),
+          Return(true)));
+  EXPECT_CALL(prefs_, GetString(cert_key_, _))
+      .WillOnce(DoAll(
+          SetArgumentPointee<1>(digest_hex_),
+          Return(true)));
+  EXPECT_CALL(prefs_, SetString(_, _)).Times(0);
+  ASSERT_TRUE(CertificateChecker::CheckCertificateChange(
+      server_to_check_, 1, NULL));
+}
+
+// check certificate change, changed
+TEST_F(CertificateCheckerTest, ChangedCertificate) {
+  EXPECT_CALL(openssl_wrapper_, GetCertificateDigest(NULL, _, _, _))
+      .WillOnce(DoAll(
+          SetArgumentPointee<1>(depth_),
+          SetArgumentPointee<2>(length_),
+          SetArrayArgument<3>(digest_, digest_ + 4),
+          Return(true)));
+  EXPECT_CALL(prefs_, GetString(cert_key_, _))
+      .WillOnce(DoAll(
+          SetArgumentPointee<1>(diff_digest_hex_),
+          Return(true)));
+  EXPECT_CALL(prefs_, SetString(kPrefsCertificateReportToSendUpdate,
+                                kCertChanged))
+      .WillOnce(Return(true));
+  EXPECT_CALL(prefs_, SetString(cert_key_, digest_hex_))
+      .WillOnce(Return(true));
+  ASSERT_TRUE(CertificateChecker::CheckCertificateChange(
+      server_to_check_, 1, NULL));
+}
+
+// check certificate change, failed
+TEST_F(CertificateCheckerTest, FailedCertificate) {
+  EXPECT_CALL(prefs_, SetString(kPrefsCertificateReportToSendUpdate,
+                                kCertFailed))
+      .WillOnce(Return(true));
+  EXPECT_CALL(prefs_, GetString(_,_)).Times(0);
+  EXPECT_CALL(openssl_wrapper_, GetCertificateDigest(_,_,_,_)).Times(0);
+  ASSERT_FALSE(CertificateChecker::CheckCertificateChange(
+      server_to_check_, 0, NULL));
+}
+
+// flush send report
+TEST_F(CertificateCheckerTest, FlushReport) {
+  EXPECT_CALL(prefs_, GetString(kPrefsCertificateReportToSendUpdate, _))
+      .WillOnce(DoAll(
+          SetArgumentPointee<1>(kCertChanged),
+          Return(true)));
+  EXPECT_CALL(prefs_, GetString(kPrefsCertificateReportToSendDownload, _))
+      .WillOnce(Return(false));
+  EXPECT_CALL(metrics_lib_, SendUserActionToUMA(kCertChanged))
+      .WillOnce(Return(true));
+  EXPECT_CALL(prefs_, SetString(kPrefsCertificateReportToSendUpdate, ""))
+      .WillOnce(Return(true));
+  EXPECT_CALL(prefs_, SetString(kPrefsCertificateReportToSendDownload, _))
+      .Times(0);
+  CertificateChecker::FlushReport();
+}
+
+// flush nothing to report
+TEST_F(CertificateCheckerTest, FlushNothingToReport) {
+  string empty = "";
+  EXPECT_CALL(prefs_, GetString(kPrefsCertificateReportToSendUpdate, _))
+      .WillOnce(DoAll(
+          SetArgumentPointee<1>(empty),
+          Return(true)));
+  EXPECT_CALL(prefs_, GetString(kPrefsCertificateReportToSendDownload, _))
+      .WillOnce(Return(false));
+  EXPECT_CALL(metrics_lib_, SendUserActionToUMA(_)).Times(0);
+  EXPECT_CALL(prefs_, SetString(_,_)).Times(0);
+  CertificateChecker::FlushReport();
+}
+
+}  // namespace chromeos_update_engine