Implement VerifyPayloadApplicable.

Parse the payload metadata and validate the source hash of all operations.
Return false if any error occur or hash mismatch.

Bug: 65283633
Test: mma

Change-Id: I6f5ae3cb69f4de973cecd0e3d7b733a48b2462f1
(cherry picked from commit 8371c1c852eae461043d8a3d911394a0ec0db909)
diff --git a/binder_service_android.cc b/binder_service_android.cc
index ccae3bf..1702ead 100644
--- a/binder_service_android.cc
+++ b/binder_service_android.cc
@@ -145,9 +145,11 @@
       android::String8{metadata_filename}.string()};
   LOG(INFO) << "Received a request of verifying payload metadata in "
             << payload_metadata << ".";
-
-  // FIXME: Do the actual verification work.
-  *return_value = true;
+  brillo::ErrorPtr error;
+  *return_value =
+      service_delegate_->VerifyPayloadApplicable(payload_metadata, &error);
+  if (error != nullptr)
+    return ErrorPtrToStatus(error);
   return Status::ok();
 }
 
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 5303e03..2445c6d 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -1031,15 +1031,10 @@
   return true;
 }
 
-namespace {
-
-// Compare |calculated_hash| with source hash in |operation|, return false and
-// dump hash and set |error| if don't match.
-// |source_fd| is the file descriptor of the source partition.
-bool ValidateSourceHash(const brillo::Blob& calculated_hash,
-                        const InstallOperation& operation,
-                        const FileDescriptorPtr source_fd,
-                        ErrorCode* error) {
+bool DeltaPerformer::ValidateSourceHash(const brillo::Blob& calculated_hash,
+                                        const InstallOperation& operation,
+                                        const FileDescriptorPtr source_fd,
+                                        ErrorCode* error) {
   brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
                                     operation.src_sha256_hash().end());
   if (calculated_hash != expected_source_hash) {
@@ -1074,8 +1069,6 @@
   return true;
 }
 
-}  // namespace
-
 bool DeltaPerformer::PerformSourceCopyOperation(
     const InstallOperation& operation, ErrorCode* error) {
   if (operation.has_src_length())
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index 55a19d8..b156e2f 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -165,6 +165,14 @@
   // it returns that value, otherwise it returns the default value.
   uint32_t GetMinorVersion() const;
 
+  // Compare |calculated_hash| with source hash in |operation|, return false and
+  // dump hash and set |error| if don't match.
+  // |source_fd| is the file descriptor of the source partition.
+  static bool ValidateSourceHash(const brillo::Blob& calculated_hash,
+                                 const InstallOperation& operation,
+                                 const FileDescriptorPtr source_fd,
+                                 ErrorCode* error);
+
  private:
   friend class DeltaPerformerTest;
   friend class DeltaPerformerIntegrationTest;
diff --git a/service_delegate_android_interface.h b/service_delegate_android_interface.h
index 7dae40f..5267bb0 100644
--- a/service_delegate_android_interface.h
+++ b/service_delegate_android_interface.h
@@ -70,6 +70,12 @@
   // of error, returns false and sets |error| accordingly.
   virtual bool ResetStatus(brillo::ErrorPtr* error) = 0;
 
+  // Verifies whether a payload (delegated by the payload metadata) can be
+  // applied to the current device. Returns whether the payload is applicable.
+  // In case of error, returns false and sets |error| accordingly.
+  virtual bool VerifyPayloadApplicable(const std::string& metadata_filename,
+                                       brillo::ErrorPtr* error) = 0;
+
  protected:
   ServiceDelegateAndroidInterface() = default;
 };
diff --git a/update_attempter_android.cc b/update_attempter_android.cc
index 4ec58d0..7df093d 100644
--- a/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -31,14 +31,20 @@
 #include <brillo/strings/string_utils.h>
 
 #include "update_engine/common/constants.h"
+#include "update_engine/common/error_code_utils.h"
 #include "update_engine/common/file_fetcher.h"
 #include "update_engine/common/utils.h"
 #include "update_engine/daemon_state_interface.h"
 #include "update_engine/metrics_reporter_interface.h"
 #include "update_engine/metrics_utils.h"
 #include "update_engine/network_selector.h"
+#include "update_engine/payload_consumer/delta_performer.h"
 #include "update_engine/payload_consumer/download_action.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
+#include "update_engine/payload_consumer/file_descriptor_utils.h"
 #include "update_engine/payload_consumer/filesystem_verifier_action.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_consumer/payload_metadata.h"
 #include "update_engine/payload_consumer/postinstall_runner_action.h"
 #include "update_engine/update_status_utils.h"
 
@@ -326,6 +332,96 @@
   }
 }
 
+bool UpdateAttempterAndroid::VerifyPayloadApplicable(
+    const std::string& metadata_filename, brillo::ErrorPtr* error) {
+  FileDescriptorPtr fd(new EintrSafeFileDescriptor);
+  if (!fd->Open(metadata_filename.c_str(), O_RDONLY)) {
+    return LogAndSetError(
+        error, FROM_HERE, "Failed to open " + metadata_filename);
+  }
+  brillo::Blob metadata(kMaxPayloadHeaderSize);
+  if (!fd->Read(metadata.data(), metadata.size())) {
+    return LogAndSetError(
+        error,
+        FROM_HERE,
+        "Failed to read payload header from " + metadata_filename);
+  }
+  ErrorCode errorcode;
+  PayloadMetadata payload_metadata;
+  if (payload_metadata.ParsePayloadHeader(
+          metadata, kBrilloMajorPayloadVersion, &errorcode) !=
+      MetadataParseResult::kSuccess) {
+    return LogAndSetError(error,
+                          FROM_HERE,
+                          "Failed to parse payload header: " +
+                              utils::ErrorCodeToString(errorcode));
+  }
+  metadata.resize(payload_metadata.GetMetadataSize() +
+                  payload_metadata.GetMetadataSignatureSize());
+  if (metadata.size() < kMaxPayloadHeaderSize) {
+    return LogAndSetError(
+        error,
+        FROM_HERE,
+        "Metadata size too small: " + std::to_string(metadata.size()));
+  }
+  if (!fd->Read(metadata.data() + kMaxPayloadHeaderSize,
+                metadata.size() - kMaxPayloadHeaderSize)) {
+    return LogAndSetError(
+        error,
+        FROM_HERE,
+        "Failed to read metadata and signature from " + metadata_filename);
+  }
+  fd->Close();
+  errorcode = payload_metadata.ValidateMetadataSignature(
+      metadata, "", base::FilePath(constants::kUpdatePayloadPublicKeyPath));
+  if (errorcode != ErrorCode::kSuccess) {
+    return LogAndSetError(error,
+                          FROM_HERE,
+                          "Failed to validate metadata signature: " +
+                              utils::ErrorCodeToString(errorcode));
+  }
+  DeltaArchiveManifest manifest;
+  if (!payload_metadata.GetManifest(metadata, &manifest)) {
+    return LogAndSetError(error, FROM_HERE, "Failed to parse manifest.");
+  }
+
+  BootControlInterface::Slot current_slot = boot_control_->GetCurrentSlot();
+  for (const PartitionUpdate& partition : manifest.partitions()) {
+    if (!partition.has_old_partition_info())
+      continue;
+    string partition_path;
+    if (!boot_control_->GetPartitionDevice(
+            partition.partition_name(), current_slot, &partition_path)) {
+      return LogAndSetError(
+          error,
+          FROM_HERE,
+          "Failed to get partition device for " + partition.partition_name());
+    }
+    if (!fd->Open(partition_path.c_str(), O_RDONLY)) {
+      return LogAndSetError(
+          error, FROM_HERE, "Failed to open " + partition_path);
+    }
+    for (const InstallOperation& operation : partition.operations()) {
+      if (!operation.has_src_sha256_hash())
+        continue;
+      brillo::Blob source_hash;
+      if (!fd_utils::ReadAndHashExtents(fd,
+                                        operation.src_extents(),
+                                        manifest.block_size(),
+                                        &source_hash)) {
+        return LogAndSetError(
+            error, FROM_HERE, "Failed to hash " + partition_path);
+      }
+      if (!DeltaPerformer::ValidateSourceHash(
+              source_hash, operation, fd, &errorcode)) {
+        return false;
+      }
+    }
+    fd->Close();
+  }
+  return true;
+}
+
 void UpdateAttempterAndroid::ProcessingDone(const ActionProcessor* processor,
                                             ErrorCode code) {
   LOG(INFO) << "Processing Done.";
diff --git a/update_attempter_android.h b/update_attempter_android.h
index 28bf90a..f00692e 100644
--- a/update_attempter_android.h
+++ b/update_attempter_android.h
@@ -69,6 +69,8 @@
   bool ResumeUpdate(brillo::ErrorPtr* error) override;
   bool CancelUpdate(brillo::ErrorPtr* error) override;
   bool ResetStatus(brillo::ErrorPtr* error) override;
+  bool VerifyPayloadApplicable(const std::string& metadata_filename,
+                               brillo::ErrorPtr* error) override;
 
   // ActionProcessorDelegate methods:
   void ProcessingDone(const ActionProcessor* processor,