Calculate vbmetadigest of inactive slot

We want to improve the security of the keystore encryption key for resume
on reboot. One AI is to create the key with the vbmeta digest of the
next slot to boot into. After reboot, the decryption will fail if
ro.boot.vbmeta.digest of the new slot doesn't match the calculated value
before reboot.

Since update_engine manages the slot switch, let it calculate the digest
as well.

Bug: 177625570
Test: do a update, check that the sysprop is set.

Change-Id: I74b20fca72f6946d980b93d76990f1c8b8f246a9
diff --git a/Android.bp b/Android.bp
index ebe43a9..be37dba 100644
--- a/Android.bp
+++ b/Android.bp
@@ -307,9 +307,12 @@
     ],
 
     static_libs: [
+        "libavb",
+        "libavb_user",
         "gkiprops",
         "libpayload_consumer",
         "libupdate_engine_boot_control",
+        "PlatformProperties",
     ],
     shared_libs: [
         "libandroid_net",
diff --git a/aosp/hardware_android.cc b/aosp/hardware_android.cc
index 6f884d4..0ac82d6 100644
--- a/aosp/hardware_android.cc
+++ b/aosp/hardware_android.cc
@@ -23,16 +23,24 @@
 #include <string_view>
 
 #include <android/sysprop/GkiProperties.sysprop.h>
-#include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <base/files/file_util.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
 #include <bootloader_message/bootloader_message.h>
+#include <fstab/fstab.h>
+#include <libavb/libavb.h>
+#include <libavb_user/avb_ops_user.h>
 
 #include "update_engine/common/error_code_utils.h"
 #include "update_engine/common/hardware.h"
 #include "update_engine/common/platform_constants.h"
 #include "update_engine/common/utils.h"
 
+#ifndef __ANDROID_RECOVERY__
+#include <android/sysprop/OtaProperties.sysprop.h>
+#endif
+
 using android::base::GetBoolProperty;
 using android::base::GetIntProperty;
 using android::base::GetProperty;
@@ -68,6 +76,40 @@
   return error_code;
 }
 
+void SetVbmetaDigestProp(const std::string& value) {
+#ifndef __ANDROID_RECOVERY__
+  if (!android::sysprop::OtaProperties::other_vbmeta_digest(value)) {
+    LOG(WARNING) << "Failed to set other vbmeta digest to " << value;
+  }
+#endif
+}
+
+std::string CalculateVbmetaDigestForInactiveSlot() {
+  AvbSlotVerifyData* avb_slot_data;
+
+  auto suffix = fs_mgr_get_other_slot_suffix();
+  const char* requested_partitions[] = {nullptr};
+  auto avb_ops = avb_ops_user_new();
+  auto verify_result = avb_slot_verify(avb_ops,
+                                       requested_partitions,
+                                       suffix.c_str(),
+                                       AVB_SLOT_VERIFY_FLAGS_NONE,
+                                       AVB_HASHTREE_ERROR_MODE_EIO,
+                                       &avb_slot_data);
+  if (verify_result != AVB_SLOT_VERIFY_RESULT_OK) {
+    LOG(WARNING) << "Failed to verify avb slot data: " << verify_result;
+    return "";
+  }
+
+  uint8_t vbmeta_digest[AVB_SHA256_DIGEST_SIZE];
+  avb_slot_verify_data_calculate_vbmeta_digest(
+      avb_slot_data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest);
+
+  std::string encoded_digest =
+      base::HexEncode(vbmeta_digest, AVB_SHA256_DIGEST_SIZE);
+  return base::ToLowerASCII(encoded_digest);
+}
+
 }  // namespace
 
 namespace hardware {
@@ -239,6 +281,30 @@
   }
 }
 
+void HardwareAndroid::SetVbmetaDigestForInactiveSlot(bool reset) {
+  if constexpr (constants::kIsRecovery) {
+    return;
+  }
+
+  if (android::base::GetProperty("ro.boot.avb_version", "").empty() &&
+      android::base::GetProperty("ro.boot.vbmeta.avb_version", "").empty()) {
+    LOG(INFO) << "Device doesn't use avb, skipping setting vbmeta digest";
+    return;
+  }
+
+  if (reset) {
+    SetVbmetaDigestProp("");
+    return;
+  }
+
+  std::string digest = CalculateVbmetaDigestForInactiveSlot();
+  if (digest.empty()) {
+    LOG(WARNING) << "Failed to calculate the vbmeta digest for the other slot";
+    return;
+  }
+  SetVbmetaDigestProp(digest);
+}
+
 string HardwareAndroid::GetVersionForLogging(
     const string& partition_name) const {
   if (partition_name == "boot") {
diff --git a/aosp/hardware_android.h b/aosp/hardware_android.h
index 5ffd7c5..78f056e 100644
--- a/aosp/hardware_android.h
+++ b/aosp/hardware_android.h
@@ -58,6 +58,7 @@
   bool GetFirstActiveOmahaPingSent() const override;
   bool SetFirstActiveOmahaPingSent() override;
   void SetWarmReset(bool warm_reset) override;
+  void SetVbmetaDigestForInactiveSlot(bool reset) override;
   [[nodiscard]] std::string GetVersionForLogging(
       const std::string& partition_name) const override;
   [[nodiscard]] ErrorCode IsPartitionUpdateValid(
diff --git a/aosp/update_attempter_android.cc b/aosp/update_attempter_android.cc
index eb1ebe0..96bb67c 100644
--- a/aosp/update_attempter_android.cc
+++ b/aosp/update_attempter_android.cc
@@ -374,6 +374,9 @@
       // Resets the warm reset property since we won't switch the slot.
       hardware_->SetWarmReset(false);
 
+      // Resets the vbmeta digest.
+      hardware_->SetVbmetaDigestForInactiveSlot(true /* reset */);
+
       // Remove update progress for DeltaPerformer and remove snapshots.
       if (!boot_control_->GetDynamicPartitionControl()->ResetUpdate(prefs_))
         ret_value = false;
diff --git a/common/fake_hardware.h b/common/fake_hardware.h
index 00a212e..29ba607 100644
--- a/common/fake_hardware.h
+++ b/common/fake_hardware.h
@@ -194,6 +194,8 @@
 
   void SetWarmReset(bool warm_reset) override { warm_reset_ = warm_reset; }
 
+  void SetVbmetaDigestForInactiveSlot(bool reset) override {}
+
   // Getters to verify state.
   int GetMaxKernelKeyRollforward() const { return kernel_max_rollforward_; }
 
diff --git a/common/hardware_interface.h b/common/hardware_interface.h
index cad32fc..7460097 100644
--- a/common/hardware_interface.h
+++ b/common/hardware_interface.h
@@ -137,6 +137,10 @@
   // needed on the next reboot. Otherwise, clears the flag.
   virtual void SetWarmReset(bool warm_reset) = 0;
 
+  // If not reset, sets the vbmeta digest of the inactive slot as a sysprop.
+  // Otherwise, clears the sysprop.
+  virtual void SetVbmetaDigestForInactiveSlot(bool reset) = 0;
+
   // Return the version/timestamp for partition `partition_name`.
   // Don't make any assumption about the formatting of returned string.
   // Only used for logging/debugging purposes.
diff --git a/cros/hardware_chromeos.cc b/cros/hardware_chromeos.cc
index 14f2497..a57cd78 100644
--- a/cros/hardware_chromeos.cc
+++ b/cros/hardware_chromeos.cc
@@ -349,6 +349,8 @@
 
 void HardwareChromeOS::SetWarmReset(bool warm_reset) {}
 
+void HardwareChromeOS::SetVbmetaDigestForInactiveSlot(bool reset) {}
+
 std::string HardwareChromeOS::GetVersionForLogging(
     const std::string& partition_name) const {
   // TODO(zhangkelvin) Implement per-partition timestamp for Chrome OS.
diff --git a/cros/hardware_chromeos.h b/cros/hardware_chromeos.h
index de84d78..8a920ef 100644
--- a/cros/hardware_chromeos.h
+++ b/cros/hardware_chromeos.h
@@ -62,6 +62,7 @@
   bool GetFirstActiveOmahaPingSent() const override;
   bool SetFirstActiveOmahaPingSent() override;
   void SetWarmReset(bool warm_reset) override;
+  void SetVbmetaDigestForInactiveSlot(bool reset) override;
   std::string GetVersionForLogging(
       const std::string& partition_name) const override;
   ErrorCode IsPartitionUpdateValid(
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index dc444c7..58d045b 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -374,6 +374,8 @@
       } else {
         // Schedules warm reset on next reboot, ignores the error.
         hardware_->SetWarmReset(true);
+        // Sets the vbmeta digest for the other slot to boot into.
+        hardware_->SetVbmetaDigestForInactiveSlot(false);
       }
     } else {
       error_code = ErrorCode::kUpdatedButNotActive;