AU: Verify that the applied delta update matches the server-sent hashes.

Also, don't try to resume any delta update that fails for any reason
other than download transfer errors.

BUG=7348
TEST=unit tests, gmerged on device

Change-Id: Ice464b8d421256717d7909fd5fa46d762bd48952

Review URL: http://codereview.chromium.org/3599025
diff --git a/action_processor.h b/action_processor.h
index b2c1392..fc42a24 100644
--- a/action_processor.h
+++ b/action_processor.h
@@ -36,6 +36,7 @@
   kActionCodeDownloadHashMismatchError = 10,
   kActionCodeDownloadSizeMismatchError = 11,
   kActionCodeDownloadPayloadVerificationError = 12,
+  kActionCodeDownloadAppliedUpdateVerificationError = 13,
 };
 
 class AbstractAction;
diff --git a/delta_performer.cc b/delta_performer.cc
index 3c24d5b..7ee9d4f 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -545,8 +545,6 @@
                                                        key_path,
                                                        &signed_hash_data));
   OmahaHashCalculator signed_hasher;
-  // TODO(petkov): Make sure signed_hash_context_ is loaded when resuming an
-  // update.
   TEST_AND_RETURN_FALSE(signed_hasher.SetContext(signed_hash_context_));
   TEST_AND_RETURN_FALSE(signed_hasher.Finalize());
   const vector<char>& hash_data = signed_hasher.raw_hash();
@@ -555,6 +553,28 @@
   return true;
 }
 
+bool DeltaPerformer::VerifyAppliedUpdate(const string& path,
+                                         const string& kernel_path) {
+  LOG(INFO) << "Verifying applied update.";
+  TEST_AND_RETURN_FALSE(manifest_valid_ &&
+                        manifest_.has_new_kernel_info() &&
+                        manifest_.has_new_rootfs_info());
+  const string* paths[] = { &kernel_path, &path };
+  const PartitionInfo* infos[] = {
+    &manifest_.new_kernel_info(), &manifest_.new_rootfs_info()
+  };
+  for (size_t i = 0; i < arraysize(paths); ++i) {
+    OmahaHashCalculator hasher;
+    TEST_AND_RETURN_FALSE(hasher.UpdateFile(*paths[i], infos[i]->size()));
+    TEST_AND_RETURN_FALSE(hasher.Finalize());
+    TEST_AND_RETURN_FALSE(hasher.raw_hash().size() == infos[i]->hash().size());
+    TEST_AND_RETURN_FALSE(memcmp(hasher.raw_hash().data(),
+                                 infos[i]->hash().data(),
+                                 hasher.raw_hash().size()) == 0);
+  }
+  return true;
+}
+
 void DeltaPerformer::DiscardBufferHeadBytes(size_t count) {
   hash_calculator_.Update(&buffer_[0], count);
   buffer_.erase(buffer_.begin(), buffer_.begin() + count);
diff --git a/delta_performer.h b/delta_performer.h
index 16073d8..7394f94 100644
--- a/delta_performer.h
+++ b/delta_performer.h
@@ -63,6 +63,11 @@
                      const std::string& update_check_response_hash,
                      const uint64_t update_check_response_size);
 
+  // Verifies that the generated update is correct based on the hashes sent by
+  // the server. Returns true on success, false otherwise.
+  bool VerifyAppliedUpdate(const std::string& path,
+                           const std::string& kernel_path);
+
   // Converts an ordered collection of Extent objects which contain data of
   // length full_length to a comma-separated string. For each Extent, the
   // string will have the start offset and then the length in bytes.
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index b8d6554..a3c43f6 100755
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -286,6 +286,7 @@
   EXPECT_EQ(0, performer.Close());
 
   CompareFilesByBlock(old_kernel, new_kernel);
+  CompareFilesByBlock(a_img, b_img);
 
   vector<char> updated_kernel_partition;
   EXPECT_TRUE(utils::ReadFile(old_kernel, &updated_kernel_partition));
@@ -297,6 +298,7 @@
       kUnittestPublicKeyPath,
       OmahaHashCalculator::OmahaHashOfData(delta),
       delta.size()));
+  EXPECT_TRUE(performer.VerifyAppliedUpdate(a_img, old_kernel));
 }
 
 TEST(DeltaPerformerTest, NewFullUpdateTest) {
diff --git a/download_action.cc b/download_action.cc
index b88e448..2920d16 100644
--- a/download_action.cc
+++ b/download_action.cc
@@ -147,6 +147,11 @@
         LOG(ERROR) << "Download of " << install_plan_.download_url
                    << " failed due to payload verification error.";
         code = kActionCodeDownloadPayloadVerificationError;
+      } else if (!delta_performer_->VerifyAppliedUpdate(
+          install_plan_.install_path, install_plan_.kernel_install_path)) {
+        LOG(ERROR) << "Download of " << install_plan_.download_url
+                   << " failed due to applied update verification error.";
+        code = kActionCodeDownloadAppliedUpdateVerificationError;
       }
     } else {
       // Makes sure the hash and size are correct for an old-style full update.
diff --git a/update_attempter.cc b/update_attempter.cc
index f408d82..1e3bf5d 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -519,11 +519,8 @@
 
 void UpdateAttempter::MarkDeltaUpdateFailure() {
   CHECK(!is_full_update_);
-  // If a delta update fails after the downloading phase, don't try to resume it
-  // the next time.
-  if (status_ > UPDATE_STATUS_DOWNLOADING) {
-    DeltaPerformer::ResetUpdateProgress(prefs_, false);
-  }
+  // Don't try to resume a failed delta update.
+  DeltaPerformer::ResetUpdateProgress(prefs_, false);
   int64_t delta_failures;
   if (!prefs_->GetInt64(kPrefsDeltaUpdateFailures, &delta_failures) ||
       delta_failures < 0) {
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index b8d85a1..bb86af7 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -17,6 +17,7 @@
 using testing::_;
 using testing::DoAll;
 using testing::InSequence;
+using testing::Ne;
 using testing::Property;
 using testing::Return;
 using testing::SetArgumentPointee;
@@ -131,6 +132,8 @@
       .WillOnce(DoAll(
           SetArgumentPointee<1>(UpdateAttempter::kMaxDeltaUpdateFailures),
           Return(true)));
+  EXPECT_CALL(prefs_, SetInt64(Ne(kPrefsDeltaUpdateFailures), _))
+      .WillRepeatedly(Return(true));
   EXPECT_CALL(prefs_, SetInt64(kPrefsDeltaUpdateFailures, 1)).Times(2);
   EXPECT_CALL(prefs_, SetInt64(kPrefsDeltaUpdateFailures, 2)).Times(1);
   EXPECT_CALL(prefs_, SetInt64(kPrefsDeltaUpdateFailures,