avb: check vbmeta digest matches property am: 7a29595ab5

Original change: https://android-review.googlesource.com/c/platform/test/vts-testcase/security/+/2927631

Change-Id: I7056c891bd905cb0d1607293501582d5a61aea1f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/avb/VtsSecurityAvbTest.cpp b/avb/VtsSecurityAvbTest.cpp
index f2dc667..e262675 100644
--- a/avb/VtsSecurityAvbTest.cpp
+++ b/avb/VtsSecurityAvbTest.cpp
@@ -19,6 +19,7 @@
 #include <array>
 #include <list>
 #include <map>
+#include <optional>
 #include <set>
 #include <tuple>
 #include <vector>
@@ -574,7 +575,7 @@
   const char* requested_partitions[] = {nullptr};
 
   // AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is needed for boot-debug.img
-  // or vendor_boot-debug.img, which is not releae key signed.
+  // or vendor_boot-debug.img, which is not release key signed.
   auto avb_ops = avb_ops_user_new();
   auto verify_result =
       avb_slot_verify(avb_ops, requested_partitions, suffix.c_str(),
@@ -623,3 +624,51 @@
     }
   }
 }
+
+static constexpr char VBMETA_PROPERTY[] = "ro.boot.vbmeta.digest";
+
+static std::optional<std::vector<uint8_t>> GetVbmetaDigestProperty() {
+  std::string default_value = "not found";
+  auto vbmeta_string =
+      ::android::base::GetProperty(VBMETA_PROPERTY, default_value);
+  if (vbmeta_string == default_value) {
+    return std::nullopt;
+  }
+
+  std::vector<uint8_t> vbmeta_digest;
+  if (HexToBytes(vbmeta_string, &vbmeta_digest)) {
+    return vbmeta_digest;
+  } else {
+    return std::nullopt;
+  }
+}
+
+// Check that a calculated vbmeta digest matches the Android property value.
+TEST(AvbTest, CalculatedVbmetaMatchesProperty) {
+  // Get the vbmeta digest value from the Android property.
+  auto vbmeta_digest = GetVbmetaDigestProperty();
+  if (!vbmeta_digest.has_value()) {
+    GTEST_SKIP() << "No " << VBMETA_PROPERTY << " property value available";
+  }
+
+  AvbSlotVerifyData *avb_slot_data;
+  LoadAndVerifyAvbSlotDataForCurrentSlot(&avb_slot_data);
+
+  // Unfortunately, bootloader is not required to report the algorithm used
+  // to calculate the digest. There are only two supported options though,
+  // SHA256 and SHA512. The VBMeta digest property value must match one of
+  // these.
+  std::vector<uint8_t> digest256(AVB_SHA256_DIGEST_SIZE);
+  std::vector<uint8_t> digest512(AVB_SHA512_DIGEST_SIZE);
+
+  avb_slot_verify_data_calculate_vbmeta_digest(
+      avb_slot_data, AVB_DIGEST_TYPE_SHA256, digest256.data());
+  avb_slot_verify_data_calculate_vbmeta_digest(
+      avb_slot_data, AVB_DIGEST_TYPE_SHA512, digest512.data());
+
+  ASSERT_TRUE((vbmeta_digest == digest256) || (vbmeta_digest == digest512))
+      << "vbmeta digest from property (" << VBMETA_PROPERTY << "="
+      << BytesToHex(vbmeta_digest.value())
+      << ") does not match computed digest (sha256: " << BytesToHex(digest256)
+      << ", sha512: " << BytesToHex(digest512) << ")";
+}
diff --git a/avb/gsi_validation_utils.cpp b/avb/gsi_validation_utils.cpp
index 5503b91..ec8769f 100644
--- a/avb/gsi_validation_utils.cpp
+++ b/avb/gsi_validation_utils.cpp
@@ -50,6 +50,19 @@
   return true;
 }
 
+const char kNibble2Hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+                              '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+std::string BytesToHex(const std::vector<uint8_t> &bytes) {
+  std::string retval;
+  retval.reserve(bytes.size() * 2 + 1);
+  for (uint8_t byte : bytes) {
+    retval.push_back(kNibble2Hex[0x0F & (byte >> 4)]);
+    retval.push_back(kNibble2Hex[0x0F & byte]);
+  }
+  return retval;
+}
+
 std::unique_ptr<ShaHasher> CreateShaHasher(const std::string &algorithm) {
   if (algorithm == "sha1") {
     return std::make_unique<ShaHasherImpl<SHA_CTX>>(
diff --git a/avb/gsi_validation_utils.h b/avb/gsi_validation_utils.h
index 6b29a4b..2c7c083 100644
--- a/avb/gsi_validation_utils.h
+++ b/avb/gsi_validation_utils.h
@@ -26,6 +26,8 @@
 
 bool HexToBytes(const std::string &hex, std::vector<uint8_t> *bytes);
 
+std::string BytesToHex(const std::vector<uint8_t> &bytes);
+
 // The abstract class of SHA algorithms.
 class ShaHasher {
  protected: