blob: 0ffc95f5b9a85e7a2d2eb7b0436222ff8a99e471 [file] [log] [blame]
/*
* Copyright (C) 2016 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 <iostream>
#include <endian.h>
#include <errno.h>
#include <inttypes.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <base/files/file_util.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include "avb_unittest_util.h"
#include "libavb.h"
struct MyAvbOps;
typedef struct MyAvbOps MyAvbOps;
class MyOps {
public:
MyOps();
~MyOps();
AvbOps* avb_ops() { return (AvbOps*)avb_ops_; }
void set_partition_dir(const base::FilePath& partition_dir) {
partition_dir_ = partition_dir;
}
void set_expected_public_key(const std::string& expected_public_key) {
expected_public_key_ = expected_public_key;
}
void set_stored_rollback_indexes(
const std::vector<uint64_t>& stored_rollback_indexes) {
stored_rollback_indexes_ = stored_rollback_indexes;
}
void set_stored_is_device_unlocked(bool stored_is_device_unlocked) {
stored_is_device_unlocked_ = stored_is_device_unlocked;
}
AvbIOResult read_from_partition(const char* partition, int64_t offset,
size_t num_bytes, void* buffer,
size_t* out_num_read) {
base::FilePath path =
partition_dir_.Append(std::string(partition)).AddExtension("img");
if (offset < 0) {
int64_t file_size;
if (!base::GetFileSize(path, &file_size)) {
fprintf(stderr, "Error getting size of file '%s'\n",
path.value().c_str());
return AVB_IO_RESULT_ERROR_IO;
}
offset = file_size - (-offset);
}
int fd = open(path.value().c_str(), O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Error opening file '%s': %s\n", path.value().c_str(),
strerror(errno));
return AVB_IO_RESULT_ERROR_IO;
}
if (lseek(fd, offset, SEEK_SET) != offset) {
fprintf(stderr, "Error seeking to pos %zd in file %s: %s\n", offset,
path.value().c_str(), strerror(errno));
close(fd);
return AVB_IO_RESULT_ERROR_IO;
}
ssize_t num_read = read(fd, buffer, num_bytes);
if (num_read < 0) {
fprintf(stderr,
"Error reading %zd bytes from pos %" PRId64 " in file %s: %s\n",
num_bytes, offset, path.value().c_str(), strerror(errno));
close(fd);
return AVB_IO_RESULT_ERROR_IO;
}
close(fd);
if (out_num_read != NULL) {
*out_num_read = num_read;
}
return AVB_IO_RESULT_OK;
}
AvbIOResult write_to_partition(const char* partition, int64_t offset,
size_t num_bytes, const void* buffer) {
base::FilePath path =
partition_dir_.Append(std::string(partition)).AddExtension("img");
if (offset < 0) {
int64_t file_size;
if (!base::GetFileSize(path, &file_size)) {
fprintf(stderr, "Error getting size of file '%s'\n",
path.value().c_str());
return AVB_IO_RESULT_ERROR_IO;
}
offset = file_size - (-offset);
}
int fd = open(path.value().c_str(), O_WRONLY);
if (fd < 0) {
fprintf(stderr, "Error opening file '%s': %s\n", path.value().c_str(),
strerror(errno));
return AVB_IO_RESULT_ERROR_IO;
}
if (lseek(fd, offset, SEEK_SET) != offset) {
fprintf(stderr, "Error seeking to pos %zd in file %s: %s\n", offset,
path.value().c_str(), strerror(errno));
close(fd);
return AVB_IO_RESULT_ERROR_IO;
}
ssize_t num_written = write(fd, buffer, num_bytes);
if (num_written < 0) {
fprintf(stderr,
"Error writing %zd bytes at pos %" PRId64 " in file %s: %s\n",
num_bytes, offset, path.value().c_str(), strerror(errno));
close(fd);
return AVB_IO_RESULT_ERROR_IO;
}
close(fd);
return AVB_IO_RESULT_OK;
}
int validate_vbmeta_public_key(AvbOps* ops, const uint8_t* public_key_data,
size_t public_key_length) {
if (public_key_length != expected_public_key_.size()) return 0;
return memcmp(expected_public_key_.c_str(), public_key_data,
public_key_length) == 0;
}
bool read_rollback_index(AvbOps* ops, size_t rollback_index_slot,
uint64_t* out_rollback_index) {
if (rollback_index_slot >= stored_rollback_indexes_.size()) {
fprintf(stderr, "No rollback index for slot %zd (has %zd slots).\n",
rollback_index_slot, stored_rollback_indexes_.size());
return false;
}
*out_rollback_index = stored_rollback_indexes_[rollback_index_slot];
return true;
}
bool write_rollback_index(AvbOps* ops, size_t rollback_index_slot,
uint64_t rollback_index) {
fprintf(stderr, "write_rollback_index not yet implemented.\n");
return false;
}
bool read_is_device_unlocked(AvbOps* ops, bool* out_is_device_unlocked) {
*out_is_device_unlocked = stored_is_device_unlocked_ ? 1 : 0;
return true;
}
bool get_unique_guid_for_partition(AvbOps* ops, const char* partition,
char* guid_buf, size_t guid_buf_size) {
// This is faking it a bit but makes testing easy. It works
// because avb_slot_verify.c doesn't check that the returned GUID
// is wellformed.
snprintf(guid_buf, guid_buf_size, "1234-fake-guid-for:%s", partition);
return true;
}
MyAvbOps* avb_ops_;
base::FilePath partition_dir_;
std::string expected_public_key_;
std::vector<uint64_t> stored_rollback_indexes_;
bool stored_is_device_unlocked_;
};
struct MyAvbOps {
AvbOps parent;
MyOps* my_ops;
};
static AvbIOResult my_ops_read_from_partition(AvbOps* ops,
const char* partition,
int64_t offset, size_t num_bytes,
void* buffer,
size_t* out_num_read) {
return ((MyAvbOps*)ops)
->my_ops->read_from_partition(partition, offset, num_bytes, buffer,
out_num_read);
}
static AvbIOResult my_ops_write_to_partition(AvbOps* ops, const char* partition,
int64_t offset, size_t num_bytes,
const void* buffer) {
return ((MyAvbOps*)ops)
->my_ops->write_to_partition(partition, offset, num_bytes, buffer);
}
static bool my_ops_validate_vbmeta_public_key(AvbOps* ops,
const uint8_t* public_key_data,
size_t public_key_length) {
return ((MyAvbOps*)ops)
->my_ops->validate_vbmeta_public_key(ops, public_key_data,
public_key_length);
}
static bool my_ops_read_rollback_index(AvbOps* ops, size_t rollback_index_slot,
uint64_t* out_rollback_index) {
return ((MyAvbOps*)ops)
->my_ops->read_rollback_index(ops, rollback_index_slot,
out_rollback_index);
}
static bool my_ops_write_rollback_index(AvbOps* ops, size_t rollback_index_slot,
uint64_t rollback_index) {
return ((MyAvbOps*)ops)
->my_ops->write_rollback_index(ops, rollback_index_slot, rollback_index);
}
static bool my_ops_read_is_device_unlocked(AvbOps* ops,
bool* out_is_device_unlocked) {
return ((MyAvbOps*)ops)
->my_ops->read_is_device_unlocked(ops, out_is_device_unlocked);
}
static bool my_ops_get_unique_guid_for_partition(AvbOps* ops,
const char* partition,
char* guid_buf,
size_t guid_buf_size) {
return ((MyAvbOps*)ops)
->my_ops->get_unique_guid_for_partition(ops, partition, guid_buf,
guid_buf_size);
}
MyOps::MyOps() {
avb_ops_ = new MyAvbOps;
avb_ops_->parent.read_from_partition = my_ops_read_from_partition;
avb_ops_->parent.write_to_partition = my_ops_write_to_partition;
avb_ops_->parent.validate_vbmeta_public_key =
my_ops_validate_vbmeta_public_key;
avb_ops_->parent.read_rollback_index = my_ops_read_rollback_index;
avb_ops_->parent.write_rollback_index = my_ops_write_rollback_index;
avb_ops_->parent.read_is_device_unlocked = my_ops_read_is_device_unlocked;
avb_ops_->parent.get_unique_guid_for_partition =
my_ops_get_unique_guid_for_partition;
avb_ops_->my_ops = this;
}
MyOps::~MyOps() { delete avb_ops_; }
class AvbSlotVerifyTest : public BaseAvbToolTest {
public:
AvbSlotVerifyTest() {}
virtual void SetUp() override {
BaseAvbToolTest::SetUp();
ops_.set_partition_dir(testdir_);
ops_.set_stored_rollback_indexes({0, 0, 0, 0});
ops_.set_stored_is_device_unlocked(false);
}
base::FilePath GenerateImage(const std::string file_name, size_t image_size) {
// Generate a 1025 KiB file with known content.
std::vector<uint8_t> image;
image.resize(image_size);
for (size_t n = 0; n < image_size; n++) {
image[n] = uint8_t(n);
}
base::FilePath image_path = testdir_.Append(file_name);
EXPECT_EQ(image_size,
static_cast<const size_t>(base::WriteFile(
image_path, reinterpret_cast<const char*>(image.data()),
image.size())));
return image_path;
}
MyOps ops_;
};
TEST_F(AvbSlotVerifyTest, Basic) {
GenerateVBMetaImage("vbmeta_a.img", "SHA256_RSA2048", 0,
base::FilePath("test/data/testkey_rsa2048.pem"));
ops_.set_expected_public_key(
PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
AvbSlotVerifyData* slot_data = NULL;
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
avb_slot_verify(ops_.avb_ops(), "_a", &slot_data));
EXPECT_NE(nullptr, slot_data);
EXPECT_EQ(
"androidboot.slot_suffix=_a androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1408 "
"androidboot.vbmeta.digest=22cda7342f5ba915f41662975f96f081",
std::string(slot_data->cmdline));
avb_slot_verify_data_free(slot_data);
}
TEST_F(AvbSlotVerifyTest, BasicSha512) {
GenerateVBMetaImage("vbmeta_a.img", "SHA512_RSA2048", 0,
base::FilePath("test/data/testkey_rsa2048.pem"));
ops_.set_expected_public_key(
PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
AvbSlotVerifyData* slot_data = NULL;
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
avb_slot_verify(ops_.avb_ops(), "_a", &slot_data));
EXPECT_NE(nullptr, slot_data);
EXPECT_EQ(
"androidboot.slot_suffix=_a androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha512 androidboot.vbmeta.size=1472 "
"androidboot.vbmeta.digest="
"125592a19a266efe6683de1afee53e2585ccfcf33adb5d6485e6fbfeabccf571",
std::string(slot_data->cmdline));
avb_slot_verify_data_free(slot_data);
}
TEST_F(AvbSlotVerifyTest, BasicUnlocked) {
GenerateVBMetaImage("vbmeta_a.img", "SHA256_RSA2048", 0,
base::FilePath("test/data/testkey_rsa2048.pem"));
ops_.set_expected_public_key(
PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
ops_.set_stored_is_device_unlocked(true);
AvbSlotVerifyData* slot_data = NULL;
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
avb_slot_verify(ops_.avb_ops(), "_a", &slot_data));
EXPECT_NE(nullptr, slot_data);
EXPECT_EQ(
"androidboot.slot_suffix=_a androidboot.vbmeta.device_state=unlocked "
"androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1408 "
"androidboot.vbmeta.digest=22cda7342f5ba915f41662975f96f081",
std::string(slot_data->cmdline));
avb_slot_verify_data_free(slot_data);
}
TEST_F(AvbSlotVerifyTest, SlotDataIsCorrect) {
GenerateVBMetaImage("vbmeta_a.img", "SHA256_RSA2048", 0,
base::FilePath("test/data/testkey_rsa2048.pem"));
ops_.set_expected_public_key(
PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
avb_slot_verify(ops_.avb_ops(), "_a", NULL));
}
TEST_F(AvbSlotVerifyTest, WrongPublicKey) {
GenerateVBMetaImage("vbmeta_a.img", "SHA256_RSA2048", 0,
base::FilePath("test/data/testkey_rsa2048.pem"));
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED,
avb_slot_verify(ops_.avb_ops(), "_a", NULL));
}
TEST_F(AvbSlotVerifyTest, NoImage) {
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_IO,
avb_slot_verify(ops_.avb_ops(), "_a", NULL));
}
TEST_F(AvbSlotVerifyTest, UnsignedVBMeta) {
GenerateVBMetaImage("vbmeta_a.img", "", 0, base::FilePath(""));
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
avb_slot_verify(ops_.avb_ops(), "_a", NULL));
}
TEST_F(AvbSlotVerifyTest, CorruptedImage) {
GenerateVBMetaImage("vbmeta_a.img", "SHA256_RSA2048", 0,
base::FilePath("test/data/testkey_rsa2048.pem"));
// Corrupt four bytes of data in the end of the image. Since the aux
// data is at the end and this data is signed, this will change the
// value of the computed hash.
uint8_t corrupt_data[4] = {0xff, 0xff, 0xff, 0xff};
EXPECT_EQ(AVB_IO_RESULT_OK, ops_.avb_ops()->write_to_partition(
ops_.avb_ops(), "vbmeta_a",
-sizeof corrupt_data, // offset from end
sizeof corrupt_data, corrupt_data));
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
avb_slot_verify(ops_.avb_ops(), "_a", NULL));
}
TEST_F(AvbSlotVerifyTest, RollbackIndex) {
GenerateVBMetaImage("vbmeta_a.img", "SHA256_RSA2048", 42,
base::FilePath("test/data/testkey_rsa2048.pem"));
ops_.set_expected_public_key(
PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
// First try with 42 as the stored rollback index - this should
// succeed since the image rollback index is 42 (as set above).
ops_.set_stored_rollback_indexes({42});
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
avb_slot_verify(ops_.avb_ops(), "_a", NULL));
// Then try with 43 for the stored rollback index - this should fail
// because the image has rollback index 42 which is less than 43.
ops_.set_stored_rollback_indexes({43});
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX,
avb_slot_verify(ops_.avb_ops(), "_a", NULL));
}
TEST_F(AvbSlotVerifyTest, HashDescriptorInVBMeta) {
const size_t boot_partition_size = 16 * 1024 * 1024;
const size_t boot_image_size = 5 * 1024 * 1024;
base::FilePath boot_path = GenerateImage("boot_a.img", boot_image_size);
EXPECT_COMMAND(
0,
"./avbtool add_hash_footer"
" --image %s"
" --rollback_index 0"
" --partition_name boot"
" --partition_size %zd"
" --kernel_cmdline 'cmdline in hash footer $(ANDROID_SYSTEM_PARTUUID)'"
" --salt deadbeef",
boot_path.value().c_str(), boot_partition_size);
GenerateVBMetaImage(
"vbmeta_a.img", "SHA256_RSA2048", 4,
base::FilePath("test/data/testkey_rsa2048.pem"),
base::StringPrintf(
"--include_descriptors_from_image %s"
" --kernel_cmdline 'cmdline in vbmeta $(ANDROID_BOOT_PARTUUID)'",
boot_path.value().c_str()));
EXPECT_EQ(
"VBMeta image version: 1.0\n"
"Header Block: 256 bytes\n"
"Authentication Block: 576 bytes\n"
"Auxiliary Block: 768 bytes\n"
"Algorithm: SHA256_RSA2048\n"
"Rollback Index: 4\n"
"Descriptors:\n"
" Kernel Cmdline descriptor:\n"
" Kernel Cmdline: 'cmdline in vbmeta "
"$(ANDROID_BOOT_PARTUUID)'\n"
" Hash descriptor:\n"
" Image Size: 5242880 bytes\n"
" Hash Algorithm: sha256\n"
" Partition Name: boot\n"
" Salt: deadbeef\n"
" Digest: "
"184cb36243adb8b87d2d8c4802de32125fe294ec46753d732144ee65df68a23d\n"
" Kernel Cmdline descriptor:\n"
" Kernel Cmdline: 'cmdline in hash footer "
"$(ANDROID_SYSTEM_PARTUUID)'\n",
InfoImage(vbmeta_image_path_));
EXPECT_COMMAND(0,
"./avbtool erase_footer"
" --image %s",
boot_path.value().c_str());
// With no footer, 'avbtool info_image' should fail (exit status 1).
EXPECT_COMMAND(1, "./avbtool info_image --image %s",
boot_path.value().c_str());
ops_.set_expected_public_key(
PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
AvbSlotVerifyData* slot_data = NULL;
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
avb_slot_verify(ops_.avb_ops(), "_a", &slot_data));
EXPECT_NE(nullptr, slot_data);
// Now verify the slot data. The vbmeta data should match our
// vbmeta_image_ member.
EXPECT_EQ(slot_data->vbmeta_size, vbmeta_image_.size());
EXPECT_EQ(0, memcmp(vbmeta_image_.data(), slot_data->vbmeta_data,
slot_data->vbmeta_size));
// The boot image data should match what is generated above with
// GenerateImage().
EXPECT_EQ(boot_image_size, slot_data->boot_size);
for (size_t n = 0; n < slot_data->boot_size; n++) {
EXPECT_EQ(slot_data->boot_data[n], uint8_t(n));
}
// This should match the two cmdlines with a space (U+0020) between
// them and the $(ANDROID_SYSTEM_PARTUUID) and
// $(ANDROID_BOOT_PARTUUID) variables replaced.
EXPECT_EQ(
"cmdline in vbmeta 1234-fake-guid-for:boot_a "
"cmdline in hash footer 1234-fake-guid-for:system_a "
"androidboot.slot_suffix=_a "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1600 "
"androidboot.vbmeta.digest=844308149e43d5db7b14cd5747def40a",
std::string(slot_data->cmdline));
EXPECT_EQ(4UL, slot_data->rollback_indexes[0]);
for (size_t n = 1; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_SLOTS; n++) {
EXPECT_EQ(0UL, slot_data->rollback_indexes[n]);
}
avb_slot_verify_data_free(slot_data);
}
TEST_F(AvbSlotVerifyTest, HashDescriptorInVBMetaCorruptBoot) {
size_t boot_partition_size = 16 * 1024 * 1024;
base::FilePath boot_path = GenerateImage("boot_a.img", 5 * 1024 * 1024);
EXPECT_COMMAND(0,
"./avbtool add_hash_footer"
" --image %s"
" --rollback_index 0"
" --partition_name boot"
" --partition_size %zd"
" --salt deadbeef",
boot_path.value().c_str(), boot_partition_size);
GenerateVBMetaImage("vbmeta_a.img", "SHA256_RSA2048", 0,
base::FilePath("test/data/testkey_rsa2048.pem"),
base::StringPrintf("--include_descriptors_from_image %s",
boot_path.value().c_str()));
EXPECT_COMMAND(0,
"./avbtool erase_footer"
" --image %s",
boot_path.value().c_str());
ops_.set_expected_public_key(
PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
// So far, so good.
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
avb_slot_verify(ops_.avb_ops(), "_a", NULL));
// Now corrupt boot_a.img and expect verification error.
uint8_t corrupt_data[4] = {0xff, 0xff, 0xff, 0xff};
EXPECT_EQ(AVB_IO_RESULT_OK, ops_.avb_ops()->write_to_partition(
ops_.avb_ops(), "boot_a",
1024 * 1024, // offset: 1 MiB
sizeof corrupt_data, corrupt_data));
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
avb_slot_verify(ops_.avb_ops(), "_a", NULL));
}
TEST_F(AvbSlotVerifyTest, HashDescriptorInChainedPartition) {
size_t boot_partition_size = 16 * 1024 * 1024;
const size_t boot_image_size = 5 * 1024 * 1024;
base::FilePath boot_path = GenerateImage("boot_a.img", boot_image_size);
EXPECT_COMMAND(0,
"./avbtool add_hash_footer"
" --image %s"
" --kernel_cmdline 'cmdline2 in hash footer'"
" --rollback_index 12"
" --partition_name boot"
" --partition_size %zd"
" --algorithm SHA256_RSA4096"
" --key test/data/testkey_rsa4096.pem"
" --salt deadbeef",
boot_path.value().c_str(), boot_partition_size);
base::FilePath pk_path = testdir_.Append("testkey_rsa4096.avbpubkey");
EXPECT_COMMAND(
0,
"./avbtool extract_public_key --key test/data/testkey_rsa4096.pem"
" --output %s",
pk_path.value().c_str());
GenerateVBMetaImage(
"vbmeta_a.img", "SHA256_RSA2048", 11,
base::FilePath("test/data/testkey_rsa2048.pem"),
base::StringPrintf("--chain_partition boot:1:%s"
" --kernel_cmdline 'cmdline2 in vbmeta'",
pk_path.value().c_str()));
EXPECT_EQ(
"VBMeta image version: 1.0\n"
"Header Block: 256 bytes\n"
"Authentication Block: 576 bytes\n"
"Auxiliary Block: 1664 bytes\n"
"Algorithm: SHA256_RSA2048\n"
"Rollback Index: 11\n"
"Descriptors:\n"
" Chain Partition descriptor:\n"
" Partition Name: boot\n"
" Rollback Index Slot: 1\n"
" Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n"
" Kernel Cmdline descriptor:\n"
" Kernel Cmdline: 'cmdline2 in vbmeta'\n",
InfoImage(vbmeta_image_path_));
ops_.set_expected_public_key(
PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
AvbSlotVerifyData* slot_data = NULL;
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
avb_slot_verify(ops_.avb_ops(), "_a", &slot_data));
EXPECT_NE(nullptr, slot_data);
// Now verify the slot data. The vbmeta data should match our
// vbmeta_image_ member.
EXPECT_EQ(slot_data->vbmeta_size, vbmeta_image_.size());
EXPECT_EQ(0, memcmp(vbmeta_image_.data(), slot_data->vbmeta_data,
slot_data->vbmeta_size));
// The boot image data should match what is generated above with
// GenerateImage().
EXPECT_EQ(boot_image_size, slot_data->boot_size);
for (size_t n = 0; n < slot_data->boot_size; n++) {
EXPECT_EQ(slot_data->boot_data[n], uint8_t(n));
}
// This should match the two cmdlines with a space (U+0020) between them.
EXPECT_EQ(
"cmdline2 in hash footer cmdline2 in vbmeta "
"androidboot.slot_suffix=_a "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=2496 "
"androidboot.vbmeta.digest=3924a4e4cdf9a4e6e77b0d87e6e9b464",
std::string(slot_data->cmdline));
EXPECT_EQ(11UL, slot_data->rollback_indexes[0]);
EXPECT_EQ(12UL, slot_data->rollback_indexes[1]);
for (size_t n = 2; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_SLOTS; n++) {
EXPECT_EQ(0UL, slot_data->rollback_indexes[n]);
}
avb_slot_verify_data_free(slot_data);
}
TEST_F(AvbSlotVerifyTest, HashDescriptorInChainedPartitionCorruptBoot) {
size_t boot_partition_size = 16 * 1024 * 1024;
base::FilePath boot_path = GenerateImage("boot_a.img", 5 * 1024 * 1024);
EXPECT_COMMAND(0,
"./avbtool add_hash_footer"
" --image %s"
" --rollback_index 0"
" --partition_name boot"
" --partition_size %zd"
" --algorithm SHA256_RSA4096"
" --key test/data/testkey_rsa4096.pem"
" --salt deadbeef",
boot_path.value().c_str(), boot_partition_size);
base::FilePath pk_path = testdir_.Append("testkey_rsa4096.avbpubkey");
EXPECT_COMMAND(
0,
"./avbtool extract_public_key --key test/data/testkey_rsa4096.pem"
" --output %s",
pk_path.value().c_str());
GenerateVBMetaImage("vbmeta_a.img", "SHA256_RSA2048", 0,
base::FilePath("test/data/testkey_rsa2048.pem"),
base::StringPrintf("--chain_partition boot:1:%s",
pk_path.value().c_str()));
ops_.set_expected_public_key(
PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
avb_slot_verify(ops_.avb_ops(), "_a", NULL));
// Now corrupt boot_a.img and expect verification error.
uint8_t corrupt_data[4] = {0xff, 0xff, 0xff, 0xff};
EXPECT_EQ(AVB_IO_RESULT_OK, ops_.avb_ops()->write_to_partition(
ops_.avb_ops(), "boot_a",
1024 * 1024, // offset: 1 MiB
sizeof corrupt_data, corrupt_data));
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
avb_slot_verify(ops_.avb_ops(), "_a", NULL));
}
TEST_F(AvbSlotVerifyTest, HashDescriptorInChainedPartitionKeyMismatch) {
size_t boot_partition_size = 16 * 1024 * 1024;
base::FilePath boot_path = GenerateImage("boot_a.img", 5 * 1024 * 1024);
// Use different key to sign vbmeta in boot_a (we use the 8192 bit
// key) than what's in the chained partition descriptor (which is
// the 4096 bit key) and expect
// AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED.
EXPECT_COMMAND(0,
"./avbtool add_hash_footer"
" --image %s"
" --rollback_index 0"
" --partition_name boot"
" --partition_size %zd"
" --algorithm SHA256_RSA8192"
" --key test/data/testkey_rsa8192.pem"
" --salt deadbeef",
boot_path.value().c_str(), boot_partition_size);
base::FilePath pk_path = testdir_.Append("testkey_rsa4096.avbpubkey");
EXPECT_COMMAND(
0,
"./avbtool extract_public_key --key test/data/testkey_rsa4096.pem"
" --output %s",
pk_path.value().c_str());
GenerateVBMetaImage("vbmeta_a.img", "SHA256_RSA2048", 0,
base::FilePath("test/data/testkey_rsa2048.pem"),
base::StringPrintf("--chain_partition boot:1:%s",
pk_path.value().c_str()));
ops_.set_expected_public_key(
PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED,
avb_slot_verify(ops_.avb_ops(), "_a", NULL));
}
TEST_F(AvbSlotVerifyTest, HashDescriptorInChainedPartitionRollbackIndexFail) {
size_t boot_partition_size = 16 * 1024 * 1024;
base::FilePath boot_path = GenerateImage("boot_a.img", 5 * 1024 * 1024);
EXPECT_COMMAND(0,
"./avbtool add_hash_footer"
" --image %s"
" --rollback_index 10"
" --partition_name boot"
" --partition_size %zd"
" --algorithm SHA256_RSA4096"
" --key test/data/testkey_rsa4096.pem"
" --salt deadbeef",
boot_path.value().c_str(), boot_partition_size);
base::FilePath pk_path = testdir_.Append("testkey_rsa4096.avbpubkey");
EXPECT_COMMAND(
0,
"./avbtool extract_public_key --key test/data/testkey_rsa4096.pem"
" --output %s",
pk_path.value().c_str());
GenerateVBMetaImage("vbmeta_a.img", "SHA256_RSA2048", 110,
base::FilePath("test/data/testkey_rsa2048.pem"),
base::StringPrintf("--chain_partition boot:1:%s",
pk_path.value().c_str()));
ops_.set_expected_public_key(
PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
// Both images (vbmeta_a and boot_a) have rollback index 10 and 11
// so it should work if the stored rollback indexes are 0 and 0.
ops_.set_stored_rollback_indexes({0, 0});
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
avb_slot_verify(ops_.avb_ops(), "_a", NULL));
// Check failure if we set the stored rollback index of the chained
// partition to 20 (see AvbSlotVerifyTest.RollbackIndex above
// where we test rollback index checks for the vbmeta partition).
ops_.set_stored_rollback_indexes({0, 20});
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX,
avb_slot_verify(ops_.avb_ops(), "_a", NULL));
// Check failure if there is no rollback index slot 1 - in that case
// we expect an I/O error since ops->read_rollback_index() will
// fail.
ops_.set_stored_rollback_indexes({0});
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_IO,
avb_slot_verify(ops_.avb_ops(), "_a", NULL));
}
TEST_F(AvbSlotVerifyTest, ChainedPartitionNoSlots) {
size_t boot_partition_size = 16 * 1024 * 1024;
const size_t boot_image_size = 5 * 1024 * 1024;
base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
EXPECT_COMMAND(0,
"./avbtool add_hash_footer"
" --image %s"
" --kernel_cmdline 'cmdline2 in hash footer'"
" --rollback_index 12"
" --partition_name boot"
" --partition_size %zd"
" --algorithm SHA256_RSA4096"
" --key test/data/testkey_rsa4096.pem"
" --salt deadbeef",
boot_path.value().c_str(), boot_partition_size);
base::FilePath pk_path = testdir_.Append("testkey_rsa4096.avbpubkey");
EXPECT_COMMAND(
0,
"./avbtool extract_public_key --key test/data/testkey_rsa4096.pem"
" --output %s",
pk_path.value().c_str());
GenerateVBMetaImage(
"vbmeta.img", "SHA256_RSA2048", 11,
base::FilePath("test/data/testkey_rsa2048.pem"),
base::StringPrintf("--chain_partition boot:1:%s"
" --kernel_cmdline 'cmdline2 in vbmeta'",
pk_path.value().c_str()));
EXPECT_EQ(
"VBMeta image version: 1.0\n"
"Header Block: 256 bytes\n"
"Authentication Block: 576 bytes\n"
"Auxiliary Block: 1664 bytes\n"
"Algorithm: SHA256_RSA2048\n"
"Rollback Index: 11\n"
"Descriptors:\n"
" Chain Partition descriptor:\n"
" Partition Name: boot\n"
" Rollback Index Slot: 1\n"
" Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n"
" Kernel Cmdline descriptor:\n"
" Kernel Cmdline: 'cmdline2 in vbmeta'\n",
InfoImage(vbmeta_image_path_));
ops_.set_expected_public_key(
PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
AvbSlotVerifyData* slot_data = NULL;
EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
avb_slot_verify(ops_.avb_ops(), "", &slot_data));
EXPECT_NE(nullptr, slot_data);
// Now verify the slot data. The vbmeta data should match our
// vbmeta_image_ member.
EXPECT_EQ(slot_data->vbmeta_size, vbmeta_image_.size());
EXPECT_EQ(0, memcmp(vbmeta_image_.data(), slot_data->vbmeta_data,
slot_data->vbmeta_size));
// The boot image data should match what is generated above with
// GenerateImage().
EXPECT_EQ(boot_image_size, slot_data->boot_size);
for (size_t n = 0; n < slot_data->boot_size; n++) {
EXPECT_EQ(slot_data->boot_data[n], uint8_t(n));
}
// This should match the two cmdlines with a space (U+0020) between
// them - note that androidboot.slot_suffix is not set since we
// don't have any slots in this setup.
EXPECT_EQ(
"cmdline2 in hash footer cmdline2 in vbmeta "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=2496 "
"androidboot.vbmeta.digest=3924a4e4cdf9a4e6e77b0d87e6e9b464",
std::string(slot_data->cmdline));
EXPECT_EQ(11UL, slot_data->rollback_indexes[0]);
EXPECT_EQ(12UL, slot_data->rollback_indexes[1]);
for (size_t n = 2; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_SLOTS; n++) {
EXPECT_EQ(0UL, slot_data->rollback_indexes[n]);
}
avb_slot_verify_data_free(slot_data);
}