Snap for 8564071 from 864186621d8d5fbe90e6eb17e34c6a0cc7e86f31 to mainline-tethering-release

Change-Id: If249e6037c39f00f21376eaae22f33f426272041
diff --git a/.clang-format b/.clang-format
index 6027082..d87b968 100644
--- a/.clang-format
+++ b/.clang-format
@@ -4,6 +4,7 @@
 ColumnLimit: 80
 IndentWidth: 2
 ContinuationIndentWidth: 4
+IncludeBlocks: Preserve
 ---
 Language: Java
 BasedOnStyle: Google
diff --git a/avb/Android.bp b/avb/Android.bp
index a65ebd4..15980c8 100644
--- a/avb/Android.bp
+++ b/avb/Android.bp
@@ -20,40 +20,66 @@
 
 cc_defaults {
     name: "vts_security_avb_defaults",
-    srcs: ["VtsSecurityAvbTest.cpp"],
-    shared_libs: [
-        "libbase",
-        "liblog",
+    srcs: [
+        "gsi_validation_utils.cpp",
     ],
     static_libs: [
         "libavb_user",
         "libavb",
+        "libbase",
         "libcrypto_static",
         "libfs_avb",
         "libfs_mgr",
+        "liblog",
     ],
     header_libs: [
-        "bootimg_headers",
+        "libstorage_literals_headers",
+    ],
+    data: [
+        ":q-gsi_avbpubkey",
+        ":r-gsi_avbpubkey",
+        ":s-gsi_avbpubkey",
+        ":t-gsi_avbpubkey",
+        ":qcar-gsi_avbpubkey",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
     ],
 }
 
 cc_test {
     name: "vts_security_avb_test",
     defaults: ["vts_security_avb_defaults"],
+    srcs: [
+        "VtsSecurityAvbTest.cpp",
+    ],
     test_config: "vts_security_avb_test.xml",
     test_suites: [
         "device-tests",
         "vts",
     ],
-    data: [
-        ":q-gsi_avbpubkey",
-        ":r-gsi_avbpubkey",
-        ":s-gsi_avbpubkey",
-        ":qcar-gsi_avbpubkey",
-    ],
 }
 
 cc_test {
-    name: "VtsSecurityAvbTest",
-    defaults: ["vts_security_avb_defaults"],
+    name: "vts_gki_compliance_test",
+    defaults: [
+        "vts_security_avb_defaults",
+        "libvintf_static_user_defaults",
+    ],
+    srcs: [
+        "vts_gki_compliance_test.cpp",
+    ],
+    static_libs: [
+        "libvintf",
+    ],
+    header_libs: [
+        "bootimg_headers",
+    ],
+    test_suites: [
+        "device-tests",
+        "vts",
+    ],
+
+    require_root: true,
 }
diff --git a/avb/VtsSecurityAvbTest.cpp b/avb/VtsSecurityAvbTest.cpp
index 016a19f..9c9d700 100644
--- a/avb/VtsSecurityAvbTest.cpp
+++ b/avb/VtsSecurityAvbTest.cpp
@@ -16,9 +16,6 @@
 
 #define LOG_TAG "VtsSecurityAvbTest"
 
-#include <sys/utsname.h>
-#include <unistd.h>
-
 #include <array>
 #include <list>
 #include <map>
@@ -32,7 +29,6 @@
 #include <android-base/result.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
-#include <bootimg.h>
 #include <fs_avb/fs_avb_util.h>
 #include <fs_mgr/roots.h>
 #include <fstab/fstab.h>
@@ -41,115 +37,12 @@
 #include <libavb_user/avb_ops_user.h>
 #include <libdm/dm.h>
 #include <log/log.h>
-#include <openssl/sha.h>
+
+#include "gsi_validation_utils.h"
 
 using android::base::Error;
 using android::base::Result;
 
-static uint8_t HexDigitToByte(char c) {
-  if (c >= '0' && c <= '9') {
-    return c - '0';
-  }
-  if (c >= 'a' && c <= 'f') {
-    return c - 'a' + 10;
-  }
-  if (c >= 'A' && c <= 'Z') {
-    return c - 'A' + 10;
-  }
-  return 0xff;
-}
-
-static bool HexToBytes(const std::string &hex, std::vector<uint8_t> *bytes) {
-  if (hex.size() % 2 != 0) {
-    return false;
-  }
-  bytes->resize(hex.size() / 2);
-  for (unsigned i = 0; i < bytes->size(); i++) {
-    uint8_t hi = HexDigitToByte(hex[i * 2]);
-    uint8_t lo = HexDigitToByte(hex[i * 2 + 1]);
-    if (lo > 0xf || hi > 0xf) {
-      return false;
-    }
-    bytes->at(i) = (hi << 4) | lo;
-  }
-  return true;
-}
-
-// The abstract class of SHA algorithms.
-class ShaHasher {
- protected:
-  const uint32_t digest_size_;
-
-  ShaHasher(uint32_t digest_size) : digest_size_(digest_size) {}
-
- public:
-  virtual ~ShaHasher() {}
-
-  uint32_t GetDigestSize() const { return digest_size_; }
-
-  virtual bool CalculateDigest(const void *buffer, size_t size,
-                               const void *salt, uint32_t block_length,
-                               uint8_t *digest) const = 0;
-};
-
-template <typename CTX_TYPE>
-class ShaHasherImpl : public ShaHasher {
- private:
-  typedef int (*InitFunc)(CTX_TYPE *);
-  typedef int (*UpdateFunc)(CTX_TYPE *sha, const void *data, size_t len);
-  typedef int (*FinalFunc)(uint8_t *md, CTX_TYPE *sha);
-
-  const InitFunc init_func_;
-  const UpdateFunc update_func_;
-  const FinalFunc final_func_;
-
- public:
-  ShaHasherImpl(InitFunc init_func, UpdateFunc update_func,
-                FinalFunc final_func, uint32_t digest_size)
-      : ShaHasher(digest_size),
-        init_func_(init_func),
-        update_func_(update_func),
-        final_func_(final_func) {}
-
-  ~ShaHasherImpl() {}
-
-  bool CalculateDigest(const void *buffer, size_t size, const void *salt,
-                       uint32_t salt_length, uint8_t *digest) const {
-    CTX_TYPE ctx;
-    if (init_func_(&ctx) != 1) {
-      return false;
-    }
-    if (update_func_(&ctx, salt, salt_length) != 1) {
-      return false;
-    }
-    if (update_func_(&ctx, buffer, size) != 1) {
-      return false;
-    }
-    if (final_func_(digest, &ctx) != 1) {
-      return false;
-    }
-    return true;
-  }
-};
-
-// Creates a hasher with the parameters corresponding to the algorithm name.
-static std::unique_ptr<ShaHasher> CreateShaHasher(
-    const std::string &algorithm) {
-  if (algorithm == "sha1") {
-    return std::make_unique<ShaHasherImpl<SHA_CTX>>(
-        SHA1_Init, SHA1_Update, SHA1_Final, SHA_DIGEST_LENGTH);
-  }
-  if (algorithm == "sha256") {
-    return std::make_unique<ShaHasherImpl<SHA256_CTX>>(
-        SHA256_Init, SHA256_Update, SHA256_Final, SHA256_DIGEST_LENGTH);
-  }
-  if (algorithm == "sha512") {
-    return std::make_unique<ShaHasherImpl<SHA512_CTX>>(
-        SHA512_Init, SHA512_Update, SHA512_Final, SHA512_DIGEST_LENGTH);
-  }
-  return nullptr;
-}
-
 // Calculates the digest of a block filled with 0.
 static bool CalculateZeroDigest(const ShaHasher &hasher, size_t size,
                                 const void *salt, int32_t block_length,
@@ -368,38 +261,6 @@
   return "";
 }
 
-// Converts descriptor.hash_algorithm to std::string.
-static std::string GetHashAlgorithm(const AvbHashtreeDescriptor &descriptor) {
-  return std::string(reinterpret_cast<const char *>(descriptor.hash_algorithm));
-}
-
-// Converts descriptor.hash_algorithm to std::string.
-static std::string GetHashAlgorithm(const AvbHashDescriptor &descriptor) {
-  return std::string(reinterpret_cast<const char *>(descriptor.hash_algorithm));
-}
-
-// Checks whether the public key is an official GSI key or not.
-static bool ValidatePublicKeyBlob(const std::string &key_blob_to_validate) {
-  if (key_blob_to_validate.empty()) {
-    ALOGE("Failed to validate an empty key");
-    return false;
-  }
-
-  std::string allowed_key_blob;
-  std::vector<std::string> allowed_key_paths = {
-      "/data/local/tmp/q-gsi.avbpubkey", "/data/local/tmp/r-gsi.avbpubkey",
-      "/data/local/tmp/s-gsi.avbpubkey", "/data/local/tmp/qcar-gsi.avbpubkey"};
-  for (const auto &path : allowed_key_paths) {
-    if (android::base::ReadFileToString(path, &allowed_key_blob)) {
-      if (key_blob_to_validate == allowed_key_blob) {
-        ALOGE("Found matching GSI key: %s", path.c_str());
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
 // Gets the system partition's AvbHashtreeDescriptor and device file path.
 //
 // Arguments:
@@ -466,332 +327,6 @@
   return descriptor;
 }
 
-// Returns true iff the device has the specified feature.
-bool DeviceSupportsFeature(const char *feature) {
-  bool device_supports_feature = false;
-  FILE *p = popen("pm list features", "re");
-  if (p) {
-    char *line = NULL;
-    size_t len = 0;
-    while (getline(&line, &len, p) > 0) {
-      if (strstr(line, feature)) {
-        device_supports_feature = true;
-        break;
-      }
-    }
-    pclose(p);
-  }
-  return device_supports_feature;
-}
-
-static int GetFirstApiLevel() {
-  int level = android::base::GetIntProperty("ro.product.first_api_level", 0);
-  if (level == 0) {
-    level = android::base::GetIntProperty("ro.build.version.sdk", 0);
-  }
-  if (level == 0) {
-    ADD_FAILURE() << "Failed to determine first API level";
-  }
-  return level;
-}
-
-bool ShouldSkipGkiTest() {
-  /* Skip for devices launched before Android R. */
-  constexpr auto R_API_LEVEL = 30;
-  int first_api_level = GetFirstApiLevel();
-  GTEST_LOG_(INFO) << "First API level is " << first_api_level;
-  if (first_api_level < R_API_LEVEL) {
-    GTEST_LOG_(INFO) << "Exempt from GKI test due to old starting API level";
-    return true;
-  }
-
-  /* Skip for form factors that do not mandate GKI yet */
-  const static bool tv_device =
-      DeviceSupportsFeature("android.software.leanback");
-  const static bool auto_device =
-      DeviceSupportsFeature("android.hardware.type.automotive");
-  if (tv_device || auto_device) {
-    GTEST_LOG_(INFO) << "Exempt from GKI test on TV/Auto devices";
-    return true;
-  }
-
-  return false;
-}
-
-// Returns a tuple of (version_major, version_minor and architecture).
-static Result<std::tuple<int, int, std::string>> GetKernelInfo() {
-  struct utsname buf;
-  int ret, kernel_version_major, kernel_version_minor;
-  ret = uname(&buf);
-  if (ret != 0) {
-    return Error() << "Failed to get kernel version.";
-  }
-
-  char unused;
-  ret = sscanf(buf.release, "%d.%d%c", &kernel_version_major,
-               &kernel_version_minor, &unused);
-  if (ret < 2) {
-    return Error() << "Failed to parse kernel version.";
-  }
-
-  return std::make_tuple(kernel_version_major, kernel_version_minor,
-                         buf.machine);
-}
-
-bool ShouldSkipGkiComplianceV1() {
-  auto kernel_info = GetKernelInfo();
-
-  if (!kernel_info.ok()) {
-    ADD_FAILURE() << "Failed to get kernel info";
-    return true;
-  }
-
-  const auto [kernel_version_major, kernel_version_minor, kernel_arch] =
-      (std::move(kernel_info).value());
-
-  /* Skip for devices if the kernel version is not 5.4. */
-  if (kernel_version_major != 5 || kernel_version_minor != 4) {
-    GTEST_LOG_(INFO)
-        << "Exempt from GKI 1.0 test due to unmatched kernel version: "
-        << kernel_version_major << "." << kernel_version_minor;
-    return true;
-  }
-  /* Skip for non arm64 that do not mandate GKI yet. */
-  if (kernel_arch != "aarch64") {
-    GTEST_LOG_(INFO) << "Exempt from GKI test on non-arm64 devices";
-    return true;
-  }
-
-  return false;
-}
-
-bool ShouldSkipGkiComplianceV2() {
-  auto kernel_info = GetKernelInfo();
-
-  if (!kernel_info.ok()) {
-    ADD_FAILURE() << "Failed to get kernel info";
-    return true;
-  }
-
-  const auto [kernel_version_major, kernel_version_minor, kernel_arch] =
-      (std::move(kernel_info).value());
-
-  /* Skip for devices if the kernel version is not >= 5.10. */
-  if (kernel_version_major < 5 ||
-      (kernel_version_major == 5 && kernel_version_minor < 10)) {
-    GTEST_LOG_(INFO)
-        << "Exempt from GKI 2.0 test due to unmatched kernel version: "
-        << kernel_version_major << "." << kernel_version_minor;
-    return true;
-  }
-
-  /* Skip for non arm64 that do not mandate GKI yet. */
-  if (kernel_arch != "aarch64") {
-    GTEST_LOG_(INFO) << "Exempt from GKI test on non-arm64 devices";
-    return true;
-  }
-
-  return false;
-}
-
-TEST(AvbTest, GkiComplianceV1) {
-  if (ShouldSkipGkiTest() || ShouldSkipGkiComplianceV1()) {
-    return;
-  }
-
-  /* load vbmeta struct from boot, verify struct integrity */
-  std::string out_public_key_data;
-  android::fs_mgr::VBMetaVerifyResult out_verify_result;
-  std::string boot_path = "/dev/block/by-name/boot" + fs_mgr_get_slot_suffix();
-  std::unique_ptr<android::fs_mgr::VBMetaData> vbmeta =
-      android::fs_mgr::LoadAndVerifyVbmetaByPath(
-          boot_path, "boot", "" /* expected_key_blob */,
-          true /* allow verification error */, false /* rollback_protection */,
-          false /* is_chained_vbmeta */, &out_public_key_data,
-          nullptr /* out_verification_disabled */, &out_verify_result);
-
-  ASSERT_TRUE(vbmeta) << "Verification of GKI vbmeta fails.";
-  ASSERT_FALSE(out_public_key_data.empty()) << "The GKI image is not signed.";
-  EXPECT_TRUE(ValidatePublicKeyBlob(out_public_key_data))
-      << "The GKI image is not signed by an official key.";
-  EXPECT_EQ(out_verify_result, android::fs_mgr::VBMetaVerifyResult::kSuccess)
-      << "Verification of the GKI vbmeta structure failed.";
-
-  /* verify boot partition according to vbmeta structure */
-  std::unique_ptr<android::fs_mgr::FsAvbHashDescriptor> descriptor =
-      android::fs_mgr::GetHashDescriptor("boot", std::move(*vbmeta));
-  ASSERT_TRUE(descriptor)
-      << "Failed to load hash descriptor from boot.img vbmeta";
-  const std::string &salt_str = descriptor->salt;
-  const std::string &expected_digest_str = descriptor->digest;
-
-  android::base::unique_fd fd(open(boot_path.c_str(), O_RDONLY));
-  ASSERT_GE(fd, 0) << "Fail to open boot partition. Try 'adb root'.";
-
-  const std::string hash_algorithm(GetHashAlgorithm(*descriptor));
-  GTEST_LOG_(INFO) << "hash_algorithm = " << hash_algorithm;
-
-  std::unique_ptr<ShaHasher> hasher = CreateShaHasher(hash_algorithm);
-  ASSERT_TRUE(hasher);
-
-  std::vector<uint8_t> salt, expected_digest, out_digest;
-  bool ok = HexToBytes(salt_str, &salt);
-  ASSERT_TRUE(ok) << "Invalid salt in descriptor: " << salt_str;
-  ok = HexToBytes(expected_digest_str, &expected_digest);
-  ASSERT_TRUE(ok) << "Invalid digest in descriptor: " << expected_digest_str;
-  ASSERT_EQ(expected_digest.size(), hasher->GetDigestSize());
-
-  std::vector<char> boot_partition_vector;
-  boot_partition_vector.resize(descriptor->image_size);
-  ASSERT_TRUE(android::base::ReadFully(fd, boot_partition_vector.data(),
-                                       descriptor->image_size))
-      << "Could not read boot partition to vector.";
-
-  out_digest.resize(hasher->GetDigestSize());
-  ASSERT_TRUE(hasher->CalculateDigest(
-      boot_partition_vector.data(), descriptor->image_size,
-      salt.data(), descriptor->salt_len, out_digest.data()))
-      << "Unable to calculate boot image digest.";
-
-  ASSERT_TRUE(out_digest.size() == expected_digest.size())
-      << "Calculated GKI boot digest size does not match expected digest size.";
-  ASSERT_TRUE(out_digest == expected_digest)
-      << "Calculated GKI boot digest does not match expected digest.";
-}
-
-// Loads GKI compliance V2 images.
-//
-// Arguments:
-//   out_boot_partition_vector: the boot.img content without boot_signature.
-//       It consists of a boot header, a kernel and a ramdisk.
-//   out_boot_signature_vector: the boot signature used to verify
-//       out_boot_partition_vector.
-//
-void LoadGkiComplianceV2Images(
-    std::vector<uint8_t> *out_boot_partition_vector,
-    std::vector<uint8_t> *out_boot_signature_vector) {
-  constexpr auto BOOT_HEADER_SIZE = 4096;
-  std::string boot_path = "/dev/block/by-name/boot" + fs_mgr_get_slot_suffix();
-
-  // Read boot header first.
-  android::base::unique_fd fd(open(boot_path.c_str(), O_RDONLY));
-  ASSERT_GE(fd, 0) << "Fail to open boot partition. Try 'adb root'.";
-
-  out_boot_partition_vector->resize(BOOT_HEADER_SIZE);
-  ASSERT_TRUE(android::base::ReadFully(fd, out_boot_partition_vector->data(),
-                                       BOOT_HEADER_SIZE))
-      << "Could not read boot partition header to vector.";
-
-  boot_img_hdr_v4 *boot_header =
-      reinterpret_cast<boot_img_hdr_v4 *>(out_boot_partition_vector->data());
-  std::string boot_magic(reinterpret_cast<const char *>(boot_header->magic),
-                         BOOT_MAGIC_SIZE);
-  ASSERT_EQ(boot_magic, BOOT_MAGIC) << "Incorrect boot magic: " << boot_magic;
-
-  GTEST_LOG_(INFO) << "kernel size: " << boot_header->kernel_size
-                   << ", ramdisk size: " << boot_header->ramdisk_size
-                   << ", signature size: " << boot_header->signature_size;
-
-  // Now reads kernel and ramdisk.
-  uint32_t kernel_pages = (boot_header->kernel_size + 4096 - 1) / 4096;
-  uint32_t ramdisk_pages = (boot_header->ramdisk_size + 4096 - 1) / 4096;
-  uint32_t kernel_ramdisk_size = (kernel_pages + ramdisk_pages) * 4096;
-
-  out_boot_partition_vector->resize(BOOT_HEADER_SIZE + kernel_ramdisk_size);
-  ASSERT_TRUE(android::base::ReadFully(
-      fd, out_boot_partition_vector->data() + BOOT_HEADER_SIZE,
-      kernel_ramdisk_size))
-      << "Could not read boot partition to vector.";
-
-  // Reads boot_signature.
-  uint32_t signature_pages = (boot_header->signature_size + 4096 - 1) / 4096;
-  uint32_t signature_size_aligned = signature_pages * 4096;
-  out_boot_signature_vector->resize(signature_size_aligned);
-  ASSERT_TRUE(android::base::ReadFully(fd, out_boot_signature_vector->data(),
-                                       signature_size_aligned))
-      << "Could not read boot signature to vector.";
-}
-
-// Verifies the GKI 2.0 boot.img against the boot signature.
-//
-// Arguments:
-//   boot_partition_vector: the boot.img content without boot_signature.
-//       It consists of a boot header, a kernel and a ramdisk.
-//   boot_signature_vector: the boot signature used to verify
-//       boot_partition_vector.
-//
-void VerifyGkiComplianceV2Signature(
-    const std::vector<uint8_t> &boot_partition_vector,
-    const std::vector<uint8_t> &boot_signature_vector) {
-  size_t pk_len;
-  const uint8_t *pk_data;
-  ::AvbVBMetaVerifyResult vbmeta_ret;
-
-  vbmeta_ret =
-      avb_vbmeta_image_verify(boot_signature_vector.data(),
-                              boot_signature_vector.size(), &pk_data, &pk_len);
-  ASSERT_EQ(vbmeta_ret, AVB_VBMETA_VERIFY_RESULT_OK)
-      << "Failed to verify boot_signature: " << vbmeta_ret;
-
-  std::string out_public_key_data(reinterpret_cast<const char *>(pk_data),
-                                  pk_len);
-  ASSERT_FALSE(out_public_key_data.empty()) << "The GKI image is not signed.";
-  EXPECT_TRUE(ValidatePublicKeyBlob(out_public_key_data))
-      << "The GKI image is not signed by an official key.";
-
-  android::fs_mgr::VBMetaData boot_signature(boot_signature_vector.data(),
-                                             boot_signature_vector.size(),
-                                             "boot_signature");
-
-  std::unique_ptr<android::fs_mgr::FsAvbHashDescriptor> descriptor =
-      android::fs_mgr::GetHashDescriptor("boot", std::move(boot_signature));
-  ASSERT_TRUE(descriptor)
-      << "Failed to load hash descriptor from the boot signature";
-  ASSERT_EQ(boot_partition_vector.size(), descriptor->image_size);
-
-  const std::string &salt_str = descriptor->salt;
-  const std::string &expected_digest_str = descriptor->digest;
-
-  const std::string hash_algorithm(GetHashAlgorithm(*descriptor));
-  GTEST_LOG_(INFO) << "hash_algorithm = " << hash_algorithm;
-
-  std::unique_ptr<ShaHasher> hasher = CreateShaHasher(hash_algorithm);
-  ASSERT_TRUE(hasher);
-
-  std::vector<uint8_t> salt, expected_digest, out_digest;
-
-  bool ok = HexToBytes(salt_str, &salt);
-  ASSERT_TRUE(ok) << "Invalid salt in descriptor: " << salt_str;
-  ok = HexToBytes(expected_digest_str, &expected_digest);
-  ASSERT_TRUE(ok) << "Invalid digest in descriptor: " << expected_digest_str;
-
-  ASSERT_EQ(expected_digest.size(), hasher->GetDigestSize());
-  out_digest.resize(hasher->GetDigestSize());
-
-  ASSERT_TRUE(hasher->CalculateDigest(boot_partition_vector.data(),
-                                      boot_partition_vector.size(), salt.data(),
-                                      descriptor->salt_len, out_digest.data()))
-      << "Unable to calculate boot image digest.";
-
-  ASSERT_EQ(out_digest.size(), expected_digest.size())
-      << "Calculated GKI boot digest size does not match expected digest size.";
-
-  ASSERT_EQ(out_digest, expected_digest)
-      << "Calculated GKI boot digest does not match expected digest.";
-}
-
-TEST(AvbTest, GkiComplianceV2) {
-  if (ShouldSkipGkiTest() || ShouldSkipGkiComplianceV2()) {
-    return;
-  }
-
-  std::vector<uint8_t> boot_partition_vector;
-  std::vector<uint8_t> boot_signature_vector;
-  ASSERT_NO_FATAL_FAILURE(LoadGkiComplianceV2Images(&boot_partition_vector,
-                                                    &boot_signature_vector));
-  VerifyGkiComplianceV2Signature(boot_partition_vector, boot_signature_vector);
-}
-
 // Loads contents and metadata of logical system partition, calculates
 // the hashtree, and compares with the metadata.
 TEST(AvbTest, SystemHashtree) {
@@ -815,7 +350,8 @@
   android::base::unique_fd fd(open(system_path.c_str(), O_RDONLY));
   ASSERT_GE(fd, 0) << "Fail to open system partition. Try 'adb root'.";
 
-  const std::string hash_algorithm(GetHashAlgorithm(*descriptor));
+  const std::string hash_algorithm(
+      reinterpret_cast<const char *>(descriptor->hash_algorithm));
   ALOGI("hash_algorithm = %s", hash_algorithm.c_str());
 
   std::unique_ptr<ShaHasher> hasher = CreateShaHasher(hash_algorithm);
@@ -913,7 +449,7 @@
                      descriptor->data_block_size),  // #blocks
       std::to_string(descriptor->image_size /
                      descriptor->data_block_size),  // hash_start
-      GetHashAlgorithm(*descriptor),
+      reinterpret_cast<const char *>(descriptor->hash_algorithm),
       descriptor->root_digest,
       descriptor->salt,
   };
@@ -1052,9 +588,9 @@
 TEST(AvbTest, HashtreeAlgorithm) {
   constexpr auto S_API_LEVEL = 31;
 
-  int first_api_level = GetFirstApiLevel();
-  GTEST_LOG_(INFO) << "First API level is " << first_api_level;
-  if (first_api_level < S_API_LEVEL) {
+  uint32_t board_api_level = GetBoardApiLevel();
+  GTEST_LOG_(INFO) << "Board API level is " << board_api_level;
+  if (board_api_level < S_API_LEVEL) {
     GTEST_LOG_(INFO)
         << "Exempt from avb hash tree test due to old starting API level";
     return;
diff --git a/avb/data/Android.bp b/avb/data/Android.bp
new file mode 100644
index 0000000..4d43951
--- /dev/null
+++ b/avb/data/Android.bp
@@ -0,0 +1,38 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+filegroup {
+    name: "q-gsi_avbpubkey",
+    srcs: [
+        "q-gsi.avbpubkey",
+    ],
+}
+
+filegroup {
+    name: "r-gsi_avbpubkey",
+    srcs: [
+        "r-gsi.avbpubkey",
+    ],
+}
+
+filegroup {
+    name: "s-gsi_avbpubkey",
+    srcs: [
+        "s-gsi.avbpubkey",
+    ],
+}
+
+filegroup {
+    name: "t-gsi_avbpubkey",
+    srcs: [
+        "t-gsi.avbpubkey",
+    ],
+}
+
+filegroup {
+    name: "qcar-gsi_avbpubkey",
+    srcs: [
+        "qcar-gsi.avbpubkey",
+    ],
+}
diff --git a/avb/data/q-gsi.avbpubkey b/avb/data/q-gsi.avbpubkey
new file mode 100644
index 0000000..5ed7543
--- /dev/null
+++ b/avb/data/q-gsi.avbpubkey
Binary files differ
diff --git a/avb/data/qcar-gsi.avbpubkey b/avb/data/qcar-gsi.avbpubkey
new file mode 100644
index 0000000..ce56646
--- /dev/null
+++ b/avb/data/qcar-gsi.avbpubkey
Binary files differ
diff --git a/avb/data/r-gsi.avbpubkey b/avb/data/r-gsi.avbpubkey
new file mode 100644
index 0000000..2609b30
--- /dev/null
+++ b/avb/data/r-gsi.avbpubkey
Binary files differ
diff --git a/avb/data/s-gsi.avbpubkey b/avb/data/s-gsi.avbpubkey
new file mode 100644
index 0000000..9065fb8
--- /dev/null
+++ b/avb/data/s-gsi.avbpubkey
Binary files differ
diff --git a/avb/data/t-gsi.avbpubkey b/avb/data/t-gsi.avbpubkey
new file mode 100644
index 0000000..de5991e
--- /dev/null
+++ b/avb/data/t-gsi.avbpubkey
Binary files differ
diff --git a/avb/gsi_validation_utils.cpp b/avb/gsi_validation_utils.cpp
new file mode 100644
index 0000000..5dad679
--- /dev/null
+++ b/avb/gsi_validation_utils.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <gtest/gtest.h>
+#include <openssl/sha.h>
+
+#include "gsi_validation_utils.h"
+
+uint8_t HexDigitToByte(char c) {
+  if (c >= '0' && c <= '9') {
+    return c - '0';
+  }
+  if (c >= 'a' && c <= 'f') {
+    return c - 'a' + 10;
+  }
+  if (c >= 'A' && c <= 'Z') {
+    return c - 'A' + 10;
+  }
+  return 0xff;
+}
+
+bool HexToBytes(const std::string &hex, std::vector<uint8_t> *bytes) {
+  if (hex.size() % 2 != 0) {
+    return false;
+  }
+  bytes->resize(hex.size() / 2);
+  for (unsigned i = 0; i < bytes->size(); i++) {
+    uint8_t hi = HexDigitToByte(hex[i * 2]);
+    uint8_t lo = HexDigitToByte(hex[i * 2 + 1]);
+    if (lo > 0xf || hi > 0xf) {
+      return false;
+    }
+    bytes->at(i) = (hi << 4) | lo;
+  }
+  return true;
+}
+
+std::unique_ptr<ShaHasher> CreateShaHasher(const std::string &algorithm) {
+  if (algorithm == "sha1") {
+    return std::make_unique<ShaHasherImpl<SHA_CTX>>(
+        SHA1_Init, SHA1_Update, SHA1_Final, SHA_DIGEST_LENGTH);
+  }
+  if (algorithm == "sha256") {
+    return std::make_unique<ShaHasherImpl<SHA256_CTX>>(
+        SHA256_Init, SHA256_Update, SHA256_Final, SHA256_DIGEST_LENGTH);
+  }
+  if (algorithm == "sha512") {
+    return std::make_unique<ShaHasherImpl<SHA512_CTX>>(
+        SHA512_Init, SHA512_Update, SHA512_Final, SHA512_DIGEST_LENGTH);
+  }
+  return nullptr;
+}
+
+bool ValidatePublicKeyBlob(const std::string &key_blob_to_validate) {
+  if (key_blob_to_validate.empty()) {
+    GTEST_LOG_(ERROR) << "Failed to validate an empty key";
+    return false;
+  }
+
+  const std::string exec_dir = android::base::GetExecutableDirectory();
+  std::vector<std::string> allowed_key_names = {
+      "q-gsi.avbpubkey", "r-gsi.avbpubkey",    "s-gsi.avbpubkey",
+      "t-gsi.avbpubkey", "qcar-gsi.avbpubkey",
+  };
+  for (const auto &key_name : allowed_key_names) {
+    const auto key_path = exec_dir + "/" + key_name;
+    std::string allowed_key_blob;
+    if (android::base::ReadFileToString(key_path, &allowed_key_blob)) {
+      if (key_blob_to_validate == allowed_key_blob) {
+        GTEST_LOG_(INFO) << "Found matching GSI key: " << key_path;
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+const uint32_t kCurrentApiLevel = 10000;
+
+static uint32_t ReadApiLevelProps(
+    const std::vector<std::string> &api_level_props) {
+  uint32_t api_level = kCurrentApiLevel;
+  for (const auto &api_level_prop : api_level_props) {
+    api_level = android::base::GetUintProperty<uint32_t>(api_level_prop,
+                                                         kCurrentApiLevel);
+    if (api_level != kCurrentApiLevel) {
+      break;
+    }
+  }
+  return api_level;
+}
+
+uint32_t GetProductFirstApiLevel() {
+  uint32_t product_api_level =
+      ReadApiLevelProps({"ro.product.first_api_level", "ro.build.version.sdk"});
+  if (product_api_level == kCurrentApiLevel) {
+    ADD_FAILURE() << "Failed to determine product first API level";
+    return 0;
+  }
+  return product_api_level;
+}
+
+uint32_t GetBoardApiLevel() {
+  // "ro.vendor.api_level" is added in Android T.
+  uint32_t vendor_api_level = ReadApiLevelProps({"ro.vendor.api_level"});
+  if (vendor_api_level != kCurrentApiLevel) {
+    return vendor_api_level;
+  }
+  // For pre-T devices, determine the board API level by ourselves.
+  uint32_t product_api_level = GetProductFirstApiLevel();
+  uint32_t board_api_level =
+      ReadApiLevelProps({"ro.board.api_level", "ro.board.first_api_level"});
+  uint32_t api_level = std::min(board_api_level, product_api_level);
+  if (api_level == kCurrentApiLevel) {
+    ADD_FAILURE() << "Failed to determine board API level";
+    return 0;
+  }
+  return api_level;
+}
diff --git a/avb/gsi_validation_utils.h b/avb/gsi_validation_utils.h
new file mode 100644
index 0000000..08a1a22
--- /dev/null
+++ b/avb/gsi_validation_utils.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <fs_avb/fs_avb_util.h>
+
+uint8_t HexDigitToByte(char c);
+
+bool HexToBytes(const std::string &hex, std::vector<uint8_t> *bytes);
+
+// The abstract class of SHA algorithms.
+class ShaHasher {
+ protected:
+  const uint32_t digest_size_;
+
+  ShaHasher(uint32_t digest_size) : digest_size_(digest_size) {}
+
+ public:
+  virtual ~ShaHasher() {}
+
+  uint32_t GetDigestSize() const { return digest_size_; }
+
+  virtual bool CalculateDigest(const void *buffer, size_t size,
+                               const void *salt, uint32_t block_length,
+                               uint8_t *digest) const = 0;
+};
+
+template <typename CTX_TYPE>
+class ShaHasherImpl : public ShaHasher {
+ private:
+  typedef int (*InitFunc)(CTX_TYPE *);
+  typedef int (*UpdateFunc)(CTX_TYPE *sha, const void *data, size_t len);
+  typedef int (*FinalFunc)(uint8_t *md, CTX_TYPE *sha);
+
+  const InitFunc init_func_;
+  const UpdateFunc update_func_;
+  const FinalFunc final_func_;
+
+ public:
+  ShaHasherImpl(InitFunc init_func, UpdateFunc update_func,
+                FinalFunc final_func, uint32_t digest_size)
+      : ShaHasher(digest_size),
+        init_func_(init_func),
+        update_func_(update_func),
+        final_func_(final_func) {}
+
+  ~ShaHasherImpl() {}
+
+  bool CalculateDigest(const void *buffer, size_t size, const void *salt,
+                       uint32_t salt_length, uint8_t *digest) const {
+    CTX_TYPE ctx;
+    if (init_func_(&ctx) != 1) {
+      return false;
+    }
+    if (update_func_(&ctx, salt, salt_length) != 1) {
+      return false;
+    }
+    if (update_func_(&ctx, buffer, size) != 1) {
+      return false;
+    }
+    if (final_func_(digest, &ctx) != 1) {
+      return false;
+    }
+    return true;
+  }
+};
+
+// Creates a hasher with the parameters corresponding to the algorithm name.
+std::unique_ptr<ShaHasher> CreateShaHasher(const std::string &algorithm);
+
+// Checks whether the public key is an official GSI key or not.
+bool ValidatePublicKeyBlob(const std::string &key_blob_to_validate);
+
+uint32_t GetProductFirstApiLevel();
+
+uint32_t GetBoardApiLevel();
diff --git a/avb/vts_gki_compliance_test.cpp b/avb/vts_gki_compliance_test.cpp
new file mode 100644
index 0000000..413043c
--- /dev/null
+++ b/avb/vts_gki_compliance_test.cpp
@@ -0,0 +1,565 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+#include <android/api-level.h>
+#include <bootimg.h>
+#include <fs_avb/fs_avb_util.h>
+#include <gtest/gtest.h>
+#include <libavb/libavb.h>
+#include <storage_literals/storage_literals.h>
+#include <vintf/VintfObject.h>
+#include <vintf/parse_string.h>
+
+#include "gsi_validation_utils.h"
+
+using namespace std::literals;
+using namespace android::storage_literals;
+
+namespace {
+
+std::string GetBlockDevicePath(const std::string &name) {
+  return "/dev/block/by-name/" + name + fs_mgr_get_slot_suffix();
+}
+
+class GkiBootImage {
+ public:
+  GkiBootImage(const uint8_t *data, size_t size) : data_(data, data + size) {}
+
+  static uint32_t GetBootHeaderVersion(const void *data) {
+    return static_cast<const boot_img_hdr_v0 *>(data)->header_version;
+  }
+
+  uint32_t header_version() const { return GetBootHeaderVersion(data()); }
+
+  uint32_t kernel_pages() const { return GetNumberOfPages(kernel_size()); }
+
+  uint32_t ramdisk_pages() const { return GetNumberOfPages(ramdisk_size()); }
+
+  uint32_t kernel_offset() const {
+    // The first page must be the boot image header.
+    return page_size();
+  }
+
+  uint32_t ramdisk_offset() const {
+    return kernel_offset() + kernel_pages() * page_size();
+  }
+
+  virtual uint32_t page_size() const = 0;
+  virtual uint32_t os_version() const = 0;
+  virtual uint32_t kernel_size() const = 0;
+  virtual uint32_t ramdisk_size() const = 0;
+  virtual uint32_t signature_size() const = 0;
+  virtual uint32_t signature_offset() const = 0;
+
+  uint32_t GetNumberOfPages(uint32_t value) const {
+    return (value + page_size() - 1) / page_size();
+  }
+
+  std::vector<uint8_t> GetKernel() const {
+    return Slice(kernel_offset(), kernel_size());
+  }
+
+  // Get "effective" boot image. The pure boot image without any boot signature.
+  std::vector<uint8_t> GetBootImage() const {
+    return Slice(0, signature_offset());
+  }
+
+  // Parse a vector of vbmeta image from the boot signature section.
+  std::vector<android::fs_mgr::VBMetaData> GetBootSignatures() const {
+    const auto begin_offset = std::clamp<size_t>(signature_offset(), 0, size());
+    const uint8_t *buffer = data() + begin_offset;
+    // begin_offset + remaining_bytes <= size() because boot_signature must be
+    // the last section.
+    size_t remaining_bytes =
+        std::clamp<size_t>(signature_size(), 0, size() - begin_offset);
+    // In case boot_signature is misaligned, shift to the first AVB magic, and
+    // treat it as the actual beginning of boot signature.
+    while (remaining_bytes >= AVB_MAGIC_LEN) {
+      if (!memcmp(buffer, AVB_MAGIC, AVB_MAGIC_LEN)) {
+        break;
+      }
+      ++buffer;
+      --remaining_bytes;
+    }
+    std::vector<android::fs_mgr::VBMetaData> vbmeta_images;
+    while (remaining_bytes >= sizeof(AvbVBMetaImageHeader)) {
+      if (memcmp(buffer, AVB_MAGIC, AVB_MAGIC_LEN) != 0) {
+        break;
+      }
+      // Extract only the header to calculate the vbmeta image size.
+      android::fs_mgr::VBMetaData vbmeta_header(
+          buffer, sizeof(AvbVBMetaImageHeader), "boot_signature");
+      if (!vbmeta_header.GetVBMetaHeader(/* update_vbmeta_size */ true)) {
+        GTEST_LOG_(ERROR) << __FUNCTION__
+                          << "(): VBMetaData::GetVBMetaHeader() failed.";
+        return {};
+      }
+      const auto vbmeta_image_size = vbmeta_header.size();
+      GTEST_LOG_(INFO) << __FUNCTION__ << "(): Found vbmeta image with size "
+                       << vbmeta_image_size;
+      if (vbmeta_image_size < sizeof(AvbVBMetaImageHeader)) {
+        GTEST_LOG_(ERROR) << __FUNCTION__
+                          << "(): Impossible-sized vbmeta image: "
+                          << vbmeta_image_size;
+        return {};
+      }
+
+      if (vbmeta_image_size > remaining_bytes) {
+        GTEST_LOG_(ERROR)
+            << __FUNCTION__
+            << "(): Premature EOF when parsing GKI boot signature.";
+        return {};
+      }
+
+      vbmeta_images.emplace_back(buffer, vbmeta_image_size, "boot_signature");
+      buffer += vbmeta_image_size;
+      remaining_bytes -= vbmeta_image_size;
+    }
+    return vbmeta_images;
+  }
+
+  virtual ~GkiBootImage() = default;
+
+ protected:
+  const uint8_t *data() const { return data_.data(); }
+
+  size_t size() const { return data_.size(); }
+
+  std::vector<uint8_t> Slice(size_t offset, size_t length) const {
+    const auto begin_offset = std::clamp<size_t>(offset, 0, size());
+    const auto end_offset =
+        std::clamp<size_t>(begin_offset + length, begin_offset, size());
+    const auto begin = data() + begin_offset;
+    const auto end = data() + end_offset;
+    return {begin, end};
+  }
+
+ private:
+  std::vector<uint8_t> data_;
+};
+
+class GkiBootImageV2 : public GkiBootImage {
+ public:
+  GkiBootImageV2(const uint8_t *data, size_t size) : GkiBootImage(data, size) {}
+
+  const boot_img_hdr_v2 *boot_header() const {
+    return reinterpret_cast<const boot_img_hdr_v2 *>(data());
+  }
+
+  uint32_t page_size() const override { return boot_header()->page_size; }
+
+  uint32_t os_version() const override { return boot_header()->os_version; }
+
+  uint32_t kernel_size() const override { return boot_header()->kernel_size; }
+
+  uint32_t ramdisk_size() const override { return boot_header()->ramdisk_size; }
+
+  uint32_t signature_size() const override {
+    // The last 16K bytes are by definition the GKI boot signature.
+    static constexpr uint32_t kBootSignatureSize = 16_KiB;
+    return kBootSignatureSize;
+  }
+
+  uint32_t signature_offset() const override {
+    if (size() < signature_size()) {
+      return 0;
+    }
+    return size() - signature_size();
+  }
+
+  uint32_t recovery_dtbo_size() const {
+    return boot_header()->recovery_dtbo_size;
+  }
+
+  uint64_t recovery_dtbo_offset() const {
+    return boot_header()->recovery_dtbo_offset;
+  }
+};
+
+class GkiBootImageV4 : public GkiBootImage {
+ public:
+  GkiBootImageV4(const uint8_t *data, size_t size) : GkiBootImage(data, size) {}
+
+  const boot_img_hdr_v4 *boot_header() const {
+    return reinterpret_cast<const boot_img_hdr_v4 *>(data());
+  }
+
+  uint32_t page_size() const override {
+    static constexpr uint32_t kPageSize = 4096;
+    return kPageSize;
+  }
+
+  uint32_t os_version() const override { return boot_header()->os_version; }
+
+  uint32_t kernel_size() const override { return boot_header()->kernel_size; }
+
+  uint32_t ramdisk_size() const override { return boot_header()->ramdisk_size; }
+
+  uint32_t signature_size() const override {
+    // For Android12 GKI, the |.signature_size| field is respected.
+    // For Android13+ GKI, the |.signature_size| field must be zero, and the
+    // last 16K bytes are by definition the GKI boot signature.
+    static constexpr uint32_t kBootSignatureSize = 16_KiB;
+    const uint32_t value = boot_header()->signature_size;
+    return value ? value : kBootSignatureSize;
+  }
+
+  uint32_t signature_offset() const override {
+    return ramdisk_offset() + ramdisk_pages() * page_size();
+  }
+};
+
+std::string GetAvbProperty(
+    const std::string &name,
+    const std::vector<android::fs_mgr::VBMetaData> &vbmeta_images) {
+  const std::string prop_name = "com.android.build." + name;
+  return android::fs_mgr::GetAvbPropertyDescriptor(prop_name, vbmeta_images);
+}
+
+std::unique_ptr<GkiBootImage> LoadAndVerifyGkiBootImage(
+    std::vector<android::fs_mgr::VBMetaData> *boot_signature_images) {
+  const std::string block_device_path = GetBlockDevicePath("boot");
+  const std::string TAG = __FUNCTION__ + "("s + block_device_path + ")";
+  SCOPED_TRACE(TAG);
+
+  std::string block_device_data;
+  if (!android::base::ReadFileToString(block_device_path, &block_device_data,
+                                       /* follow_symlinks */ true)) {
+    ADD_FAILURE() << "Failed to read '" << block_device_path
+                  << "': " << strerror(errno);
+    return nullptr;
+  }
+  if (block_device_data.size() <= 4096) {
+    ADD_FAILURE() << "Size of '" << block_device_path
+                  << "' is impossibly small: " << block_device_data.size();
+    return nullptr;
+  }
+
+  if (block_device_data.substr(0, BOOT_MAGIC_SIZE) != BOOT_MAGIC) {
+    ADD_FAILURE() << "Device has invalid boot magic: " << block_device_path;
+    return nullptr;
+  }
+
+  // Remove the AVB footer and chained vbmeta image if there is any.
+  if (block_device_data.size() > AVB_FOOTER_SIZE) {
+    const uint8_t *footer_address =
+        reinterpret_cast<const uint8_t *>(block_device_data.data()) +
+        block_device_data.size() - AVB_FOOTER_SIZE;
+    AvbFooter vbmeta_footer;
+    if (avb_footer_validate_and_byteswap(
+            reinterpret_cast<const AvbFooter *>(footer_address),
+            &vbmeta_footer)) {
+      block_device_data.resize(vbmeta_footer.original_image_size);
+    }
+  }
+
+  std::unique_ptr<GkiBootImage> boot_image;
+  const auto boot_header_version =
+      GkiBootImage::GetBootHeaderVersion(block_device_data.data());
+  if (boot_header_version == 4) {
+    boot_image = std::make_unique<GkiBootImageV4>(
+        reinterpret_cast<const uint8_t *>(block_device_data.data()),
+        block_device_data.size());
+  } else if (boot_header_version == 2) {
+    boot_image = std::make_unique<GkiBootImageV2>(
+        reinterpret_cast<const uint8_t *>(block_device_data.data()),
+        block_device_data.size());
+  } else {
+    ADD_FAILURE() << "Unexpected boot header version: " << boot_header_version;
+    return nullptr;
+  }
+
+  *boot_signature_images = boot_image->GetBootSignatures();
+  if (boot_signature_images->empty()) {
+    ADD_FAILURE() << "Failed to load the boot signature.";
+    return nullptr;
+  }
+
+  // Verify that the vbmeta images in boot_signature are certified.
+  for (const auto &vbmeta_image : *boot_signature_images) {
+    size_t pk_len;
+    const uint8_t *pk_data;
+    const auto vbmeta_verify_result = avb_vbmeta_image_verify(
+        vbmeta_image.data(), vbmeta_image.size(), &pk_data, &pk_len);
+    if (vbmeta_verify_result != AVB_VBMETA_VERIFY_RESULT_OK) {
+      ADD_FAILURE() << "Failed to verify boot_signature: "
+                    << avb_vbmeta_verify_result_to_string(vbmeta_verify_result);
+      return nullptr;
+    }
+    const std::string out_public_key_data(
+        reinterpret_cast<const char *>(pk_data), pk_len);
+    if (out_public_key_data.empty()) {
+      ADD_FAILURE() << "The GKI image descriptor is not signed.";
+      continue;
+    }
+    if (!ValidatePublicKeyBlob(out_public_key_data)) {
+      ADD_FAILURE()
+          << "The GKI image descriptor is not signed by an official key.";
+      continue;
+    }
+  }
+
+  GTEST_LOG_(INFO) << TAG << ": boot.fingerprint: "
+                   << GetAvbProperty("boot.fingerprint",
+                                     *boot_signature_images);
+  GTEST_LOG_(INFO) << TAG
+                   << ": header version: " << boot_image->header_version()
+                   << ", kernel size: " << boot_image->kernel_size()
+                   << ", ramdisk size: " << boot_image->ramdisk_size()
+                   << ", signature size: " << boot_image->signature_size();
+
+  return boot_image;
+}
+
+// Verify image data integrity with an AVB hash descriptor.
+void VerifyImageDescriptor(
+    const std::vector<uint8_t> &image,
+    const android::fs_mgr::FsAvbHashDescriptor &descriptor) {
+  const std::string TAG = __FUNCTION__ + "("s + descriptor.partition_name + ")";
+  SCOPED_TRACE(TAG);
+
+  ASSERT_EQ(image.size(), descriptor.image_size);
+
+  const std::string &salt_str = descriptor.salt;
+  const std::string &expected_digest_str = descriptor.digest;
+
+  const std::string hash_algorithm(
+      reinterpret_cast<const char *>(descriptor.hash_algorithm));
+  GTEST_LOG_(INFO) << TAG << ": hash_algorithm = " << hash_algorithm;
+
+  std::unique_ptr<ShaHasher> hasher = CreateShaHasher(hash_algorithm);
+  ASSERT_NE(nullptr, hasher);
+
+  std::vector<uint8_t> salt, expected_digest, out_digest;
+
+  ASSERT_TRUE(HexToBytes(salt_str, &salt))
+      << "Invalid salt in descriptor: " << salt_str;
+  ASSERT_TRUE(HexToBytes(expected_digest_str, &expected_digest))
+      << "Invalid digest in descriptor: " << expected_digest_str;
+
+  ASSERT_EQ(expected_digest.size(), hasher->GetDigestSize());
+  out_digest.resize(hasher->GetDigestSize());
+
+  ASSERT_TRUE(hasher->CalculateDigest(image.data(), image.size(), salt.data(),
+                                      descriptor.salt_len, out_digest.data()))
+      << "Unable to calculate image digest.";
+
+  ASSERT_EQ(out_digest.size(), expected_digest.size())
+      << "Calculated digest size does not match expected digest size.";
+
+  ASSERT_EQ(out_digest, expected_digest)
+      << "Calculated digest does not match expected digest.";
+}
+
+// Returns true iff the device has the specified feature.
+bool DeviceSupportsFeature(const char *feature) {
+  bool device_supports_feature = false;
+  FILE *p = popen("pm list features", "re");
+  if (p) {
+    char *line = NULL;
+    size_t len = 0;
+    while (getline(&line, &len, p) > 0) {
+      if (strstr(line, feature)) {
+        device_supports_feature = true;
+        break;
+      }
+    }
+    pclose(p);
+  }
+  return device_supports_feature;
+}
+
+}  // namespace
+
+class GkiComplianceTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    // Fetch device runtime information.
+    runtime_info = android::vintf::VintfObject::GetRuntimeInfo();
+    ASSERT_NE(nullptr, runtime_info);
+
+    product_first_api_level = GetProductFirstApiLevel();
+
+    /* Skip for non arm64 that do not mandate GKI yet. */
+    if (runtime_info->hardwareId() != "aarch64") {
+      GTEST_SKIP() << "Exempt from GKI test on non-arm64 devices";
+    }
+
+    /* Skip for form factors that do not mandate GKI yet */
+    const static bool tv_device =
+        DeviceSupportsFeature("android.software.leanback");
+    const static bool auto_device =
+        DeviceSupportsFeature("android.hardware.type.automotive");
+    if (tv_device || auto_device) {
+      GTEST_SKIP() << "Exempt from GKI test on TV/Auto devices";
+    }
+
+    GTEST_LOG_(INFO) << runtime_info->osName() << " "
+                     << runtime_info->osRelease();
+    GTEST_LOG_(INFO) << "Product first API level: " << product_first_api_level;
+  }
+
+  bool ShouldSkipGkiComplianceV2();
+
+  std::shared_ptr<const android::vintf::RuntimeInfo> runtime_info;
+  int product_first_api_level;
+};
+
+bool GkiComplianceTest::ShouldSkipGkiComplianceV2() {
+  /* Skip for devices if the kernel version is not >= 5.10. */
+  if (runtime_info->kernelVersion().dropMinor() <
+      android::vintf::Version{5, 10}) {
+    GTEST_LOG_(INFO) << "Exempt from GKI 2.0 test on kernel version: "
+                     << runtime_info->kernelVersion();
+    return true;
+  }
+  /* Skip for devices launched before Android S. */
+  if (product_first_api_level < __ANDROID_API_S__) {
+    GTEST_LOG_(INFO) << "Exempt from GKI 2.0 test on pre-S launched devices";
+    return true;
+  }
+  return false;
+}
+
+TEST_F(GkiComplianceTest, GkiComplianceV1) {
+  if (product_first_api_level < __ANDROID_API_R__) {
+    GTEST_SKIP() << "Exempt from GKI 1.0 test: product first API level ("
+                 << product_first_api_level << ") < " << __ANDROID_API_R__;
+  }
+  /* Skip for devices if the kernel version is not 5.4. */
+  if (runtime_info->kernelVersion().dropMinor() !=
+      android::vintf::Version{5, 4}) {
+    GTEST_SKIP() << "Exempt from GKI 1.0 test on kernel version: "
+                 << runtime_info->kernelVersion();
+  }
+
+  /* load vbmeta struct from boot, verify struct integrity */
+  std::string out_public_key_data;
+  android::fs_mgr::VBMetaVerifyResult out_verify_result;
+  const std::string boot_path = GetBlockDevicePath("boot");
+  std::unique_ptr<android::fs_mgr::VBMetaData> vbmeta =
+      android::fs_mgr::LoadAndVerifyVbmetaByPath(
+          boot_path, "boot", "" /* expected_key_blob */,
+          true /* allow verification error */, false /* rollback_protection */,
+          false /* is_chained_vbmeta */, &out_public_key_data,
+          nullptr /* out_verification_disabled */, &out_verify_result);
+
+  ASSERT_TRUE(vbmeta) << "Verification of GKI vbmeta fails.";
+  ASSERT_FALSE(out_public_key_data.empty()) << "The GKI image is not signed.";
+  EXPECT_TRUE(ValidatePublicKeyBlob(out_public_key_data))
+      << "The GKI image is not signed by an official key.";
+  EXPECT_EQ(out_verify_result, android::fs_mgr::VBMetaVerifyResult::kSuccess)
+      << "Verification of the GKI vbmeta structure failed.";
+
+  /* verify boot partition according to vbmeta structure */
+  std::unique_ptr<android::fs_mgr::FsAvbHashDescriptor> descriptor =
+      android::fs_mgr::GetHashDescriptor("boot", std::move(*vbmeta));
+  ASSERT_TRUE(descriptor)
+      << "Failed to load hash descriptor from boot.img vbmeta";
+
+  android::base::unique_fd fd(open(boot_path.c_str(), O_RDONLY));
+  ASSERT_TRUE(fd.ok()) << "Fail to open boot partition. Try 'adb root'.";
+
+  std::vector<uint8_t> boot_partition_vector;
+  boot_partition_vector.resize(descriptor->image_size);
+  ASSERT_TRUE(android::base::ReadFully(fd, boot_partition_vector.data(),
+                                       descriptor->image_size))
+      << "Could not read boot partition to vector.";
+
+  ASSERT_NO_FATAL_FAILURE(
+      VerifyImageDescriptor(boot_partition_vector, *descriptor));
+}
+
+// Verify the entire boot image.
+TEST_F(GkiComplianceTest, GkiComplianceV2) {
+  if (ShouldSkipGkiComplianceV2()) {
+    GTEST_SKIP() << "Skipping GkiComplianceV2 test";
+  }
+
+  // GKI 2.0 ensures getKernelLevel() to return valid value.
+  std::string error_msg;
+  const auto kernel_level =
+      android::vintf::VintfObject::GetInstance()->getKernelLevel(&error_msg);
+  ASSERT_NE(android::vintf::Level::UNSPECIFIED, kernel_level) << error_msg;
+
+  std::vector<android::fs_mgr::VBMetaData> boot_signature_images;
+  std::unique_ptr<GkiBootImage> boot_image =
+      LoadAndVerifyGkiBootImage(&boot_signature_images);
+  ASSERT_NE(nullptr, boot_image);
+  ASSERT_LE(1, boot_signature_images.size());
+  EXPECT_EQ(4, boot_image->header_version());
+
+  if (kernel_level >= android::vintf::Level::T) {
+    GTEST_LOG_(INFO)
+        << "Android T+ verification scheme. The GKI boot.img must contain only "
+           "the generic kernel but not the generic ramdisk.";
+    EXPECT_EQ(0, boot_image->ramdisk_size())
+        << "'boot' partition mustn't include a ramdisk image.";
+    EXPECT_EQ(0, boot_image->os_version())
+        << "OS version and security patch level should be defined in the "
+           "chained vbmeta image instead.";
+  }
+
+  std::unique_ptr<android::fs_mgr::FsAvbHashDescriptor> boot_descriptor =
+      android::fs_mgr::GetHashDescriptor("boot", boot_signature_images);
+  ASSERT_NE(nullptr, boot_descriptor)
+      << "Failed to load the 'boot' hash descriptor.";
+  ASSERT_NO_FATAL_FAILURE(
+      VerifyImageDescriptor(boot_image->GetBootImage(), *boot_descriptor));
+}
+
+// Verify only the 'generic_kernel' descriptor.
+TEST_F(GkiComplianceTest, GkiComplianceV2_kernel) {
+  if (ShouldSkipGkiComplianceV2()) {
+    GTEST_SKIP() << "Skipping GkiComplianceV2 test";
+  }
+
+  // GKI 2.0 ensures getKernelLevel() to return valid value.
+  std::string error_msg;
+  const auto kernel_level =
+      android::vintf::VintfObject::GetInstance()->getKernelLevel(&error_msg);
+  ASSERT_NE(android::vintf::Level::UNSPECIFIED, kernel_level) << error_msg;
+  if (kernel_level < android::vintf::Level::T) {
+    GTEST_SKIP() << "Skip for kernel level (" << kernel_level << ") < T ("
+                 << android::vintf::Level::T << ")";
+  }
+
+  std::vector<android::fs_mgr::VBMetaData> boot_signature_images;
+  std::unique_ptr<GkiBootImage> boot_image =
+      LoadAndVerifyGkiBootImage(&boot_signature_images);
+  ASSERT_NE(nullptr, boot_image);
+  ASSERT_LE(1, boot_signature_images.size());
+
+  std::unique_ptr<android::fs_mgr::FsAvbHashDescriptor>
+      generic_kernel_descriptor = android::fs_mgr::GetHashDescriptor(
+          "generic_kernel", boot_signature_images);
+  ASSERT_NE(nullptr, generic_kernel_descriptor)
+      << "Failed to load the 'generic_kernel' hash descriptor.";
+  ASSERT_NO_FATAL_FAILURE(VerifyImageDescriptor(boot_image->GetKernel(),
+                                                *generic_kernel_descriptor));
+}
+
+int main(int argc, char *argv[]) {
+  ::testing::InitGoogleTest(&argc, argv);
+  android::base::InitLogging(argv, android::base::StderrLogger);
+  return RUN_ALL_TESTS();
+}
diff --git a/avb/vts_security_avb_test.xml b/avb/vts_security_avb_test.xml
index b03303f..d830f24 100644
--- a/avb/vts_security_avb_test.xml
+++ b/avb/vts_security_avb_test.xml
@@ -21,6 +21,7 @@
         <option name="push" value="q-gsi.avbpubkey->/data/local/tmp/q-gsi.avbpubkey" />
         <option name="push" value="r-gsi.avbpubkey->/data/local/tmp/r-gsi.avbpubkey" />
         <option name="push" value="s-gsi.avbpubkey->/data/local/tmp/s-gsi.avbpubkey" />
+        <option name="push" value="t-gsi.avbpubkey->/data/local/tmp/t-gsi.avbpubkey" />
         <option name="push" value="qcar-gsi.avbpubkey->/data/local/tmp/qcar-gsi.avbpubkey" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.GTest" >