blob: 216d1cb5cca391e4156bb18cb3110b8548308f44 [file] [log] [blame]
/*
* Copyright (C) 2019 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 "fs_avb_unittest_util.h"
#include <stdlib.h>
#include <android-base/file.h>
#include <base/files/file_util.h>
#include <base/strings/string_util.h>
namespace fs_avb_host_test {
void BaseFsAvbTest::SetUp() {
// Changes current directory to test executable directory so that relative path
// references to test dependencies don't rely on being manually run from
// the executable directory. With this, we can just open "./data/testkey_rsa2048.pem"
// from the source.
base::SetCurrentDirectory(base::FilePath(android::base::GetExecutableDirectory()));
// Creates a temporary directory, e.g., /tmp/libfs_avb-tests.XXXXXX to stash images in.
base::FilePath tmp_dir;
ASSERT_TRUE(GetTempDir(&tmp_dir));
base::CreateTemporaryDirInDir(tmp_dir, "libfs_avb-tests.", &test_dir_);
}
void BaseFsAvbTest::TearDown() {
// Nukes temporary directory.
ASSERT_NE(std::string::npos, test_dir_.value().find("libfs_avb-tests"));
ASSERT_TRUE(base::DeleteFile(test_dir_, true /* recursive */));
}
std::string BaseFsAvbTest::CalcVBMetaDigest(const std::string& file_name,
const std::string& hash_algorithm) {
auto iter = vbmeta_images_.find(file_name);
EXPECT_NE(iter, vbmeta_images_.end()); // ensures file_name is generated before.
// Gets the image path from iterator->second.path: VBMetaImage.path.
base::FilePath image_path = iter->second.path;
base::FilePath vbmeta_digest_path = test_dir_.Append("vbmeta_digest");
EXPECT_COMMAND(0,
"avbtool calculate_vbmeta_digest --image %s --hash_algorithm %s"
" --output %s",
image_path.value().c_str(), hash_algorithm.c_str(),
vbmeta_digest_path.value().c_str());
// Reads the content of the output digest file.
std::string vbmeta_digest_data;
EXPECT_TRUE(base::ReadFileToString(vbmeta_digest_path, &vbmeta_digest_data));
// Returns the trimmed digest.
std::string trimmed_digest_data;
base::TrimString(vbmeta_digest_data, " \t\n", &trimmed_digest_data);
return trimmed_digest_data;
}
void BaseFsAvbTest::GenerateVBMetaImage(
const std::string& file_name, const std::string& avb_algorithm, uint64_t rollback_index,
const base::FilePath& key_path,
const std::vector<base::FilePath>& include_descriptor_image_paths,
const std::vector<ChainPartitionConfig>& chain_partitions,
const std::string& additional_options) {
// --algorithm and --key
std::string signing_options;
if (avb_algorithm == "") {
signing_options = " --algorithm NONE ";
} else {
signing_options =
std::string(" --algorithm ") + avb_algorithm + " --key " + key_path.value() + " ";
}
// --include_descriptors_from_image
std::string include_descriptor_options;
for (const auto& path : include_descriptor_image_paths) {
include_descriptor_options += " --include_descriptors_from_image " + path.value();
}
// --chain_partitions
std::string chain_partition_options;
for (const auto& partition : chain_partitions) {
chain_partition_options += base::StringPrintf(
" --chain_partition %s:%u:%s", partition.partition_name.c_str(),
partition.rollback_index_location, partition.key_blob_path.value().c_str());
}
// Starts to 'make_vbmeta_image'.
VBMetaImage vbmeta_image;
vbmeta_image.path = test_dir_.Append(file_name);
EXPECT_COMMAND(0,
"avbtool make_vbmeta_image"
" --rollback_index %" PRIu64
" %s %s %s %s"
" --output %s",
rollback_index, signing_options.c_str(), include_descriptor_options.c_str(),
chain_partition_options.c_str(), additional_options.c_str(),
vbmeta_image.path.value().c_str());
int64_t file_size;
ASSERT_TRUE(base::GetFileSize(vbmeta_image.path, &file_size));
vbmeta_image.content.resize(file_size);
ASSERT_TRUE(base::ReadFile(vbmeta_image.path,
reinterpret_cast<char*>(vbmeta_image.content.data()), file_size));
// Stores the generated vbmeta image into vbmeta_images_ member object.
vbmeta_images_.emplace(file_name, std::move(vbmeta_image));
}
void BaseFsAvbTest::ExtractVBMetaImage(const base::FilePath& image_path,
const std::string& output_file_name,
const size_t padding_size) {
VBMetaImage vbmeta_image;
vbmeta_image.path = test_dir_.Append(output_file_name);
EXPECT_COMMAND(0,
"avbtool extract_vbmeta_image"
" --image %s"
" --output %s"
" --padding_size %zu",
image_path.value().c_str(), vbmeta_image.path.value().c_str(), padding_size);
int64_t file_size;
ASSERT_TRUE(base::GetFileSize(vbmeta_image.path, &file_size));
vbmeta_image.content.resize(file_size);
ASSERT_TRUE(base::ReadFile(vbmeta_image.path,
reinterpret_cast<char*>(vbmeta_image.content.data()), file_size));
// Stores the extracted vbmeta image into vbmeta_images_ member object.
vbmeta_images_.emplace(output_file_name, std::move(vbmeta_image));
}
// Generates a file with name |file_name| of size |image_size| with
// known content (0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
base::FilePath BaseFsAvbTest::GenerateImage(const std::string file_name, size_t image_size,
uint8_t start_byte) {
std::vector<uint8_t> image;
image.resize(image_size);
for (size_t n = 0; n < image_size; n++) {
image[n] = uint8_t(n + start_byte);
}
base::FilePath image_path = test_dir_.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;
}
void BaseFsAvbTest::AddAvbFooter(const base::FilePath& image_path, const std::string& footer_type,
const std::string& partition_name, const uint64_t partition_size,
const std::string& avb_algorithm, uint64_t rollback_index,
const base::FilePath& key_path, const std::string& salt,
const std::string& additional_options) {
// 'add_hash_footer' or 'add_hashtree_footer'.
EXPECT_TRUE(footer_type == "hash" or footer_type == "hashtree");
std::string add_footer_option = "add_" + footer_type + "_footer";
std::string signing_options;
if (avb_algorithm == "") {
signing_options = " --algorithm NONE ";
} else {
signing_options =
std::string(" --algorithm ") + avb_algorithm + " --key " + key_path.value() + " ";
}
EXPECT_COMMAND(0,
"avbtool %s"
" --image %s"
" --partition_name %s "
" --partition_size %" PRIu64 " --rollback_index %" PRIu64
" --salt %s"
" %s %s",
add_footer_option.c_str(), image_path.value().c_str(), partition_name.c_str(),
partition_size, rollback_index, salt.c_str(), signing_options.c_str(),
additional_options.c_str());
}
std::string BaseFsAvbTest::InfoImage(const base::FilePath& image_path) {
base::FilePath tmp_path = test_dir_.Append("info_output.txt");
EXPECT_COMMAND(0, "avbtool info_image --image %s --output %s", image_path.value().c_str(),
tmp_path.value().c_str());
std::string info_data;
EXPECT_TRUE(base::ReadFileToString(tmp_path, &info_data));
return info_data;
}
std::string BaseFsAvbTest::InfoImage(const std::string& file_name) {
auto iter = vbmeta_images_.find(file_name);
EXPECT_NE(iter, vbmeta_images_.end()); // ensures file_name is generated before.
// Gets the image path from iterator->second.path: VBMetaImage.path.
base::FilePath image_path = iter->second.path;
return InfoImage(image_path);
}
base::FilePath BaseFsAvbTest::ExtractPublicKeyAvb(const base::FilePath& key_path) {
std::string file_name = key_path.RemoveExtension().BaseName().value();
base::FilePath tmp_path = test_dir_.Append(file_name + "public_key.bin");
EXPECT_COMMAND(0,
"avbtool extract_public_key --key %s"
" --output %s",
key_path.value().c_str(), tmp_path.value().c_str());
return tmp_path;
}
std::string BaseFsAvbTest::ExtractPublicKeyAvbBlob(const base::FilePath& key_path) {
base::FilePath tmp_path = test_dir_.Append("public_key.bin");
EXPECT_COMMAND(0,
"avbtool extract_public_key --key %s"
" --output %s",
key_path.value().c_str(), tmp_path.value().c_str());
std::string key_data;
EXPECT_TRUE(base::ReadFileToString(tmp_path, &key_data));
return key_data;
}
TEST_F(BaseFsAvbTest, GenerateImage) {
const size_t image_size = 5 * 1024 * 1024;
base::FilePath boot_path = GenerateImage("boot.img", image_size);
EXPECT_NE(0U, boot_path.value().size());
// Checks file size is as expected.
int64_t file_size;
ASSERT_TRUE(base::GetFileSize(boot_path, &file_size));
EXPECT_EQ(file_size, image_size);
// Checks file content is as expected.
std::vector<uint8_t> expected_content;
expected_content.resize(image_size);
for (size_t n = 0; n < image_size; n++) {
expected_content[n] = uint8_t(n);
}
std::vector<uint8_t> actual_content;
actual_content.resize(image_size);
EXPECT_TRUE(
base::ReadFile(boot_path, reinterpret_cast<char*>(actual_content.data()), image_size));
EXPECT_EQ(expected_content, actual_content);
}
TEST_F(BaseFsAvbTest, GenerateVBMetaImage) {
GenerateVBMetaImage("vbmeta.img", "SHA256_RSA2048", 0,
base::FilePath("data/testkey_rsa2048.pem"),
{}, /* include_descriptor_image_paths */
{}, /* chain_partitions */
"--internal_release_string \"unit test\"");
EXPECT_EQ("5eba9ad4e775645e7eac441a563c200681ae868158d06f6a6cd36d06c07bd781",
CalcVBMetaDigest("vbmeta.img", "sha256"));
EXPECT_EQ(
"Minimum libavb version: 1.0\n"
"Header Block: 256 bytes\n"
"Authentication Block: 320 bytes\n"
"Auxiliary Block: 576 bytes\n"
"Algorithm: SHA256_RSA2048\n"
"Rollback Index: 0\n"
"Flags: 0\n"
"Release String: 'unit test'\n"
"Descriptors:\n"
" (none)\n",
InfoImage("vbmeta.img"));
}
TEST_F(BaseFsAvbTest, AddHashFooter) {
// Generates a raw boot.img
const size_t image_size = 5 * 1024 * 1024;
const size_t partition_size = 10 * 1024 * 1024;
base::FilePath boot_path = GenerateImage("boot.img", image_size);
EXPECT_NE(0U, boot_path.value().size());
// Checks file size is as expected.
int64_t file_size;
ASSERT_TRUE(base::GetFileSize(boot_path, &file_size));
EXPECT_EQ(file_size, image_size);
// Appends AVB Hash Footer.
AddAvbFooter(boot_path, "hash", "boot", partition_size, "SHA256_RSA4096", 10,
base::FilePath("data/testkey_rsa4096.pem"), "d00df00d",
"--internal_release_string \"unit test\"");
// Extracts boot vbmeta from boot.img into boot-vbmeta.img.
ExtractVBMetaImage(boot_path, "boot-vbmeta.img");
EXPECT_EQ(
"Minimum libavb version: 1.0\n"
"Header Block: 256 bytes\n"
"Authentication Block: 576 bytes\n"
"Auxiliary Block: 1216 bytes\n"
"Algorithm: SHA256_RSA4096\n"
"Rollback Index: 10\n"
"Flags: 0\n"
"Release String: 'unit test'\n"
"Descriptors:\n"
" Hash descriptor:\n"
" Image Size: 5242880 bytes\n"
" Hash Algorithm: sha256\n"
" Partition Name: boot\n"
" Salt: d00df00d\n"
" Digest: "
"222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\n"
" Flags: 0\n",
InfoImage("boot-vbmeta.img"));
}
TEST_F(BaseFsAvbTest, AddHashtreeFooter) {
// Generates a raw system.img
const size_t image_size = 50 * 1024 * 1024;
const size_t partition_size = 60 * 1024 * 1024;
base::FilePath system_path = GenerateImage("system.img", image_size);
EXPECT_NE(0U, system_path.value().size());
// Checks file size is as expected.
int64_t file_size;
ASSERT_TRUE(base::GetFileSize(system_path, &file_size));
EXPECT_EQ(file_size, image_size);
// Appends AVB Hashtree Footer.
AddAvbFooter(system_path, "hashtree", "system", partition_size, "SHA512_RSA8192", 20,
base::FilePath("data/testkey_rsa8192.pem"), "d00df00d",
"--internal_release_string \"unit test\"");
// Extracts system vbmeta from system.img into system-vbmeta.img.
ExtractVBMetaImage(system_path, "system-vbmeta.img");
EXPECT_EQ(
"Minimum libavb version: 1.0\n"
"Header Block: 256 bytes\n"
"Authentication Block: 1088 bytes\n"
"Auxiliary Block: 2304 bytes\n"
"Algorithm: SHA512_RSA8192\n"
"Rollback Index: 20\n"
"Flags: 0\n"
"Release String: 'unit test'\n"
"Descriptors:\n"
" Hashtree descriptor:\n"
" Version of dm-verity: 1\n"
" Image Size: 52428800 bytes\n"
" Tree Offset: 52428800\n"
" Tree Size: 413696 bytes\n"
" Data Block Size: 4096 bytes\n"
" Hash Block Size: 4096 bytes\n"
" FEC num roots: 2\n"
" FEC offset: 52842496\n"
" FEC size: 417792 bytes\n"
" Hash Algorithm: sha1\n"
" Partition Name: system\n"
" Salt: d00df00d\n"
" Root Digest: d20d40c02298e385ab6d398a61a3b91dc9947d99\n"
" Flags: 0\n",
InfoImage("system-vbmeta.img"));
}
TEST_F(BaseFsAvbTest, GenerateVBMetaImageWithDescriptors) {
// Generates a raw boot.img
const size_t boot_image_size = 5 * 1024 * 1024;
const size_t boot_partition_size = 10 * 1024 * 1024;
base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
// Adds AVB Hash Footer.
AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA4096", 10,
base::FilePath("data/testkey_rsa4096.pem"), "d00df00d",
"--internal_release_string \"unit test\"");
// Generates a raw system.img, use a smaller size to speed-up unit test.
const size_t system_image_size = 10 * 1024 * 1024;
const size_t system_partition_size = 15 * 1024 * 1024;
base::FilePath system_path = GenerateImage("system.img", system_image_size);
// Adds AVB Hashtree Footer.
AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA8192", 20,
base::FilePath("data/testkey_rsa8192.pem"), "d00df00d",
"--internal_release_string \"unit test\"");
// Makes a vbmeta.img including both 'boot' and 'system' descriptors.
GenerateVBMetaImage("vbmeta.img", "SHA256_RSA2048", 0,
base::FilePath("data/testkey_rsa2048.pem"),
{boot_path, system_path}, /* include_descriptor_image_paths */
{}, /* chain_partitions */
"--internal_release_string \"unit test\"");
EXPECT_EQ("a069cbfc30c816cddf3b53f1ad53b7ca5d61a3d93845eb596bbb1b40caa1c62f",
CalcVBMetaDigest("vbmeta.img", "sha256"));
EXPECT_EQ(
"Minimum libavb version: 1.0\n"
"Header Block: 256 bytes\n"
"Authentication Block: 320 bytes\n"
"Auxiliary Block: 960 bytes\n"
"Algorithm: SHA256_RSA2048\n"
"Rollback Index: 0\n"
"Flags: 0\n"
"Release String: 'unit test'\n"
"Descriptors:\n"
" Hash descriptor:\n"
" Image Size: 5242880 bytes\n"
" Hash Algorithm: sha256\n"
" Partition Name: boot\n"
" Salt: d00df00d\n"
" Digest: "
"222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\n"
" Flags: 0\n"
" Hashtree descriptor:\n"
" Version of dm-verity: 1\n"
" Image Size: 10485760 bytes\n"
" Tree Offset: 10485760\n"
" Tree Size: 86016 bytes\n"
" Data Block Size: 4096 bytes\n"
" Hash Block Size: 4096 bytes\n"
" FEC num roots: 2\n"
" FEC offset: 10571776\n"
" FEC size: 90112 bytes\n"
" Hash Algorithm: sha1\n"
" Partition Name: system\n"
" Salt: d00df00d\n"
" Root Digest: a3d5dd307341393d85de356c384ff543ec1ed81b\n"
" Flags: 0\n",
InfoImage("vbmeta.img"));
}
TEST_F(BaseFsAvbTest, GenerateVBMetaImageWithChainDescriptors) {
// Generates a raw boot.img
const size_t boot_image_size = 5 * 1024 * 1024;
const size_t boot_partition_size = 10 * 1024 * 1024;
base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
// Adds AVB Hash Footer.
AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
base::FilePath("data/testkey_rsa2048.pem"), "d00df00d",
"--internal_release_string \"unit test\"");
// Generates a raw system.img, use a smaller size to speed-up unit test.
const size_t system_image_size = 10 * 1024 * 1024;
const size_t system_partition_size = 15 * 1024 * 1024;
base::FilePath system_path = GenerateImage("system.img", system_image_size);
// Adds AVB Hashtree Footer.
AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
base::FilePath("data/testkey_rsa4096.pem"), "d00df00d",
"--internal_release_string \"unit test\"");
// Make a vbmeta image with chain partitions.
base::FilePath rsa2048_public_key =
ExtractPublicKeyAvb(base::FilePath("data/testkey_rsa2048.pem"));
base::FilePath rsa4096_public_key =
ExtractPublicKeyAvb(base::FilePath("data/testkey_rsa4096.pem"));
GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
base::FilePath("data/testkey_rsa8192.pem"),
{}, /* include_descriptor_image_paths */
{{"boot", 1, rsa2048_public_key}, /* chain_partitions */
{"system", 2, rsa4096_public_key}},
"--internal_release_string \"unit test\"");
// vbmeta digest calculation includes the chained vbmeta from boot.img and system.img.
EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
CalcVBMetaDigest("vbmeta.img", "sha256"));
EXPECT_EQ(
"Minimum libavb version: 1.0\n"
"Header Block: 256 bytes\n"
"Authentication Block: 1088 bytes\n"
"Auxiliary Block: 3840 bytes\n"
"Algorithm: SHA256_RSA8192\n"
"Rollback Index: 0\n"
"Flags: 0\n"
"Release String: 'unit test'\n"
"Descriptors:\n"
" Chain Partition descriptor:\n"
" Partition Name: boot\n"
" Rollback Index Location: 1\n"
" Public key (sha1): cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
" Chain Partition descriptor:\n"
" Partition Name: system\n"
" Rollback Index Location: 2\n"
" Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n",
InfoImage("vbmeta.img"));
}
} // namespace fs_avb_host_test
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}