blob: 5506a62a6513038aa88d35c25168e7c068879d52 [file] [log] [blame]
/*
* 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/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 <vintf/VintfObject.h>
#include <vintf/parse_string.h>
#include "gsi_validation_utils.h"
namespace {
// 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(
reinterpret_cast<const char *>(descriptor->hash_algorithm));
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.";
}
// 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 {
auto vintf = android::vintf::VintfObject::GetInstance();
ASSERT_NE(nullptr, vintf);
runtime_info = vintf->getRuntimeInfo(
android::vintf::RuntimeInfo::FetchFlag::CPU_VERSION);
ASSERT_NE(nullptr, runtime_info);
/* 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";
}
product_first_api_level =
android::base::GetIntProperty("ro.product.first_api_level", 0);
ASSERT_TRUE(product_first_api_level)
<< "ro.product.first_api_level is undefined";
}
std::shared_ptr<const android::vintf::RuntimeInfo> runtime_info;
int product_first_api_level;
};
TEST_F(GkiComplianceTest, GkiComplianceV1) {
if (product_first_api_level < __ANDROID_API_R__) {
GTEST_SKIP() << "Exempt from GKI 1.0 test: ro.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;
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(
reinterpret_cast<const char *>(descriptor->hash_algorithm));
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.";
}
TEST_F(GkiComplianceTest, GkiComplianceV2) {
/* Skip for devices if the kernel version is not >= 5.10. */
if (runtime_info->kernelVersion().dropMinor() <
android::vintf::Version{5, 10}) {
GTEST_SKIP() << "Exempt from GKI 2.0 test on kernel version: "
<< runtime_info->kernelVersion();
}
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);
}