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" >