Merge "mark a VTS tests as not unit tests"
diff --git a/avb/Android.bp b/avb/Android.bp
index 62df460..a65ebd4 100644
--- a/avb/Android.bp
+++ b/avb/Android.bp
@@ -26,11 +26,15 @@
"liblog",
],
static_libs: [
+ "libavb_user",
"libavb",
"libcrypto_static",
"libfs_avb",
"libfs_mgr",
],
+ header_libs: [
+ "bootimg_headers",
+ ],
}
cc_test {
diff --git a/avb/VtsSecurityAvbTest.cpp b/avb/VtsSecurityAvbTest.cpp
index 0739f7c..a2d999b 100644
--- a/avb/VtsSecurityAvbTest.cpp
+++ b/avb/VtsSecurityAvbTest.cpp
@@ -23,21 +23,29 @@
#include <list>
#include <map>
#include <set>
+#include <tuple>
#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
+#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>
#include <gtest/gtest.h>
#include <libavb/libavb.h>
+#include <libavb_user/avb_ops_user.h>
#include <libdm/dm.h>
#include <log/log.h>
#include <openssl/sha.h>
+using android::base::Error;
+using android::base::Result;
+
static uint8_t HexDigitToByte(char c) {
if (c >= '0' && c <= '9') {
return c - '0';
@@ -476,30 +484,116 @@
return device_supports_feature;
}
-TEST(AvbTest, Boot) {
- /* Skip for devices running kernels older than 5.4. */
- struct utsname buf;
- int ret, kernel_version_major, kernel_version_minor;
- ret = uname(&buf);
- ASSERT_EQ(ret, 0) << "Failed to get kernel version.";
- char dummy;
- ret = sscanf(buf.release, "%d.%d%c", &kernel_version_major,
- &kernel_version_minor, &dummy);
- ASSERT_GE(ret, 2) << "Failed to parse kernel version.";
- if (kernel_version_major < 5 ||
- (kernel_version_major == 5 && kernel_version_minor < 4)) {
- return;
+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;
+}
- /* Skip for non arm64 that do not mandate GKI yet. */
- if (strcmp(buf.machine, "aarch64") != 0) {
- return;
+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");
if (tv_device) {
+ GTEST_LOG_(INFO) << "Exempt from GKI test on TV 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;
}
@@ -524,6 +618,8 @@
/* 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;
@@ -531,7 +627,7 @@
ASSERT_GE(fd, 0) << "Fail to open boot partition. Try 'adb root'.";
const std::string hash_algorithm(GetHashAlgorithm(*descriptor));
- ALOGI("hash_algorithm = %s", hash_algorithm.c_str());
+ GTEST_LOG_(INFO) << "hash_algorithm = " << hash_algorithm;
std::unique_ptr<ShaHasher> hasher = CreateShaHasher(hash_algorithm);
ASSERT_TRUE(hasher);
@@ -561,6 +657,139 @@
<< "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) {
@@ -750,3 +979,82 @@
ASSERT_EQ(NextWord(target.data, &next_pos), std::string::npos);
}
+
+static void VerifyHashAlgorithm(const AvbHashtreeDescriptor* descriptor) {
+ AvbHashtreeDescriptor hashtree_descriptor;
+ ASSERT_TRUE(avb_hashtree_descriptor_validate_and_byteswap(
+ descriptor, &hashtree_descriptor))
+ << "hash tree descriptor is invalid.";
+
+ auto partition_name_ptr = reinterpret_cast<const uint8_t*>(descriptor) +
+ sizeof(AvbHashtreeDescriptor);
+ std::string partition_name(
+ partition_name_ptr,
+ partition_name_ptr + hashtree_descriptor.partition_name_len);
+
+ if (avb_strcmp(
+ reinterpret_cast<const char*>(hashtree_descriptor.hash_algorithm),
+ "sha1") == 0) {
+ FAIL() << "The hash tree algorithm cannot be SHA1 for partition "
+ << partition_name;
+ }
+}
+
+static void LoadAndVerifyAvbSlotDataForCurrentSlot(
+ AvbSlotVerifyData** avb_slot_data) {
+ // Use an empty suffix string for non-A/B devices.
+ std::string suffix;
+ if (android::base::GetBoolProperty("ro.build.ab_update", false)) {
+ suffix = android::base::GetProperty("ro.boot.slot_suffix", "");
+ ASSERT_TRUE(!suffix.empty()) << "Failed to get suffix for the current slot";
+ }
+
+ const char* requested_partitions[] = {nullptr};
+
+ auto avb_ops = avb_ops_user_new();
+ auto verify_result = avb_slot_verify(
+ avb_ops, requested_partitions, suffix.c_str(), AVB_SLOT_VERIFY_FLAGS_NONE,
+ AVB_HASHTREE_ERROR_MODE_EIO, avb_slot_data);
+ ASSERT_EQ(AVB_SLOT_VERIFY_RESULT_OK, verify_result)
+ << "Failed to verify avb slot data " << verify_result;
+}
+
+// Check the correct hashtree algorithm is used.
+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) {
+ GTEST_LOG_(INFO)
+ << "Exempt from avb hash tree test due to old starting API level";
+ return;
+ }
+
+ // Note we don't iterate the entries in fstab; because we don't know if a
+ // partition uses hashtree or not.
+ AvbSlotVerifyData* avb_slot_data;
+ LoadAndVerifyAvbSlotDataForCurrentSlot(&avb_slot_data);
+ ASSERT_NE(nullptr, avb_slot_data) << "Failed to load avb slot verify data";
+ std::unique_ptr<AvbSlotVerifyData, decltype(&avb_slot_verify_data_free)>
+ scope_guard(avb_slot_data, avb_slot_verify_data_free);
+
+ // Iterate over the loaded vbmeta structs
+ for (size_t i = 0; i < avb_slot_data->num_vbmeta_images; i++) {
+ std::string partition_name = avb_slot_data->vbmeta_images[i].partition_name;
+ const auto& vbmeta_image = avb_slot_data->vbmeta_images[i];
+
+ size_t num_descriptors;
+ auto descriptors = avb_descriptor_get_all(
+ vbmeta_image.vbmeta_data, vbmeta_image.vbmeta_size, &num_descriptors);
+ // Iterate over the hashtree descriptors
+ for (size_t n = 0; n < num_descriptors; n++) {
+ if (avb_be64toh(descriptors[n]->tag) != AVB_DESCRIPTOR_TAG_HASHTREE) {
+ continue;
+ }
+
+ VerifyHashAlgorithm(
+ reinterpret_cast<const AvbHashtreeDescriptor*>(descriptors[n]));
+ }
+ }
+}