libfs_avb: support loading vbmeta structs from any partition
This commits adds the following two public functions into
class AvbHandle, and verified by 'libfs_avb_test':
static AvbUniquePtr LoadAndVerifyVbmeta(); // loads inline vbmeta.
static AvbUniquePtr LoadAndVerifyVbmeta( // loads offline vbmeta.
const std::string& partition_name, const std::string& ab_suffix,
const std::string& ab_other_suffix, const std::string& expected_public_key,
const HashAlgorithm& hash_algorithm, bool allow_verification_error,
bool load_chained_vbmeta, bool rollback_protection,
std::function<std::string(const std::string&)> custom_device_path = nullptr);
The first function LoadAndVerifyVbmeta() will be used to replace Open(),
in a separate CL in the future. Many libfs_avb internal utils are added
into avb_util.cpp as well, which are verified by
'libfs_avb_internal_test'.
Bug: 112103720
Bug: 117960205
Test: atest libfs_avb_test
Test: atest libfs_avb_internal_test
Change-Id: I807b8af0b69c9a4511f6f120e9754aca5442830e
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index 3cb718c..41cd7dd 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -34,6 +34,12 @@
return "";
}
+// Returns "_b" or "_a", which is *the other* slot of androidboot.slot_suffix
+// in kernel cmdline, or an empty string if that parameter does not exist.
+std::string fs_mgr_get_other_slot_suffix() {
+ return other_suffix(fs_mgr_get_slot_suffix());
+}
+
// Returns "_a" or "_b" based on androidboot.slot_suffix in kernel cmdline, or an empty string
// if that parameter does not exist.
std::string fs_mgr_get_slot_suffix() {
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 5d8496d..68ce4b0 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -97,6 +97,7 @@
int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab);
std::string fs_mgr_get_slot_suffix();
+std::string fs_mgr_get_other_slot_suffix();
std::set<std::string> fs_mgr_get_boot_devices();
struct FstabEntry {
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index 6f368e4..191e803 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -56,6 +56,11 @@
"tests/data/*",
],
static_libs: [
+ "libavb",
+ "libavb_host_sysdeps",
+ "libdm",
+ "libfs_avb",
+ "libfstab",
"libgtest_host",
],
shared_libs: [
@@ -86,8 +91,12 @@
static_libs: [
"libfs_avb_test_util",
],
+ shared_libs: [
+ "libcrypto",
+ ],
srcs: [
"tests/basic_test.cpp",
+ "tests/fs_avb_test.cpp",
],
}
@@ -96,10 +105,11 @@
defaults: ["libfs_avb_host_test_defaults"],
static_libs: [
"libfs_avb_test_util",
- "libfstab",
],
srcs: [
+ "avb_util.cpp",
"util.cpp",
+ "tests/avb_util_test.cpp",
"tests/util_test.cpp",
],
}
diff --git a/fs_mgr/libfs_avb/avb_ops.cpp b/fs_mgr/libfs_avb/avb_ops.cpp
index 3efa794..6a3e2c0 100644
--- a/fs_mgr/libfs_avb/avb_ops.cpp
+++ b/fs_mgr/libfs_avb/avb_ops.cpp
@@ -29,6 +29,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
+
#include <string>
#include <android-base/macros.h>
@@ -190,7 +191,8 @@
// Copies avb_slot_data->vbmeta_images[].
for (size_t i = 0; i < avb_slot_data->num_vbmeta_images; i++) {
out_vbmeta_images->emplace_back(VBMetaData(avb_slot_data->vbmeta_images[i].vbmeta_data,
- avb_slot_data->vbmeta_images[i].vbmeta_size));
+ avb_slot_data->vbmeta_images[i].vbmeta_size,
+ avb_slot_data->vbmeta_images[i].partition_name));
}
// Free the local resource.
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index 0ceb6ee..f57c9d6 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -16,19 +16,67 @@
#include "avb_util.h"
+#include <unistd.h>
+
#include <array>
#include <sstream>
#include <android-base/file.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include "util.h"
+using android::base::StartsWith;
using android::base::unique_fd;
namespace android {
namespace fs_mgr {
+// Helper functions to print enum class VBMetaVerifyResult.
+const char* VBMetaVerifyResultToString(VBMetaVerifyResult result) {
+ // clang-format off
+ static const char* const name[] = {
+ "ResultSuccess",
+ "ResultError",
+ "ResultErrorVerification",
+ "ResultUnknown",
+ };
+ // clang-format on
+
+ uint32_t index = static_cast<uint32_t>(result);
+ uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;
+ if (index >= unknown_index) {
+ index = unknown_index;
+ }
+
+ return name[index];
+}
+
+std::ostream& operator<<(std::ostream& os, VBMetaVerifyResult result) {
+ os << VBMetaVerifyResultToString(result);
+ return os;
+}
+
+// class VBMetaData
+// ----------------
+std::unique_ptr<AvbVBMetaImageHeader> VBMetaData::GetVBMetaHeader(bool update_vbmeta_size) {
+ auto vbmeta_header(std::make_unique<AvbVBMetaImageHeader>());
+
+ if (!vbmeta_header) return nullptr;
+
+ /* Byteswap the header. */
+ avb_vbmeta_image_header_to_host_byte_order((AvbVBMetaImageHeader*)vbmeta_ptr_.get(),
+ vbmeta_header.get());
+ if (update_vbmeta_size) {
+ vbmeta_size_ = sizeof(AvbVBMetaImageHeader) +
+ vbmeta_header->authentication_data_block_size +
+ vbmeta_header->auxiliary_data_block_size;
+ }
+
+ return vbmeta_header;
+}
+
// Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
// See the following link for more details:
// https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
@@ -173,5 +221,300 @@
return true;
}
+// Converts a AVB partition_name (without A/B suffix) to a device partition name.
+// e.g., "system" => "system_a",
+// "system_other" => "system_b".
+//
+// If the device is non-A/B, converts it to a partition name without suffix.
+// e.g., "system" => "system",
+// "system_other" => "system".
+std::string AvbPartitionToDevicePatition(const std::string& avb_partition_name,
+ const std::string& ab_suffix,
+ const std::string& ab_other_suffix) {
+ bool is_other_slot = false;
+ std::string sanitized_partition_name(avb_partition_name);
+
+ auto other_suffix = sanitized_partition_name.rfind("_other");
+ if (other_suffix != std::string::npos) {
+ sanitized_partition_name.erase(other_suffix); // converts system_other => system
+ is_other_slot = true;
+ }
+
+ auto append_suffix = is_other_slot ? ab_other_suffix : ab_suffix;
+ return sanitized_partition_name + append_suffix;
+}
+
+off64_t GetTotalSize(int fd) {
+ off64_t saved_current = lseek64(fd, 0, SEEK_CUR);
+ if (saved_current == -1) {
+ PERROR << "Failed to get current position";
+ return -1;
+ }
+
+ // lseek64() returns the resulting offset location from the beginning of the file.
+ off64_t total_size = lseek64(fd, 0, SEEK_END);
+ if (total_size == -1) {
+ PERROR << "Failed to lseek64 to end of the partition";
+ return -1;
+ }
+
+ // Restores the original offset.
+ if (lseek64(fd, saved_current, SEEK_SET) == -1) {
+ PERROR << "Failed to lseek64 to the original offset: " << saved_current;
+ }
+
+ return total_size;
+}
+
+std::unique_ptr<AvbFooter> GetAvbFooter(int fd) {
+ std::array<uint8_t, AVB_FOOTER_SIZE> footer_buf;
+ auto footer(std::make_unique<AvbFooter>());
+
+ off64_t footer_offset = GetTotalSize(fd) - AVB_FOOTER_SIZE;
+
+ ssize_t num_read =
+ TEMP_FAILURE_RETRY(pread64(fd, footer_buf.data(), AVB_FOOTER_SIZE, footer_offset));
+ if (num_read < 0 || num_read != AVB_FOOTER_SIZE) {
+ PERROR << "Failed to read AVB footer";
+ return nullptr;
+ }
+
+ if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_buf.data(), footer.get())) {
+ PERROR << "AVB footer verification failed.";
+ return nullptr;
+ }
+
+ return footer;
+}
+
+bool VerifyPublicKeyBlob(const uint8_t* key, size_t length, const std::string& expected_key_blob) {
+ if (expected_key_blob.empty()) { // no expectation of the key, return true.
+ return true;
+ }
+ if (expected_key_blob.size() != length) {
+ return false;
+ }
+ if (0 == memcmp(key, expected_key_blob.data(), length)) {
+ return true;
+ }
+ return false;
+}
+
+VBMetaVerifyResult VerifyVBMetaSignature(const VBMetaData& vbmeta,
+ const std::string& expected_public_key_blob) {
+ const uint8_t* pk_data;
+ size_t pk_len;
+ ::AvbVBMetaVerifyResult vbmeta_ret;
+
+ vbmeta_ret = avb_vbmeta_image_verify(vbmeta.data(), vbmeta.size(), &pk_data, &pk_len);
+
+ switch (vbmeta_ret) {
+ case AVB_VBMETA_VERIFY_RESULT_OK:
+ if (pk_data == nullptr || pk_len <= 0) {
+ LERROR << vbmeta.partition()
+ << ": Error verifying vbmeta image: failed to get public key";
+ return VBMetaVerifyResult::kError;
+ }
+ if (!VerifyPublicKeyBlob(pk_data, pk_len, expected_public_key_blob)) {
+ LERROR << vbmeta.partition() << ": Error verifying vbmeta image: public key used to"
+ << " sign data does not match key in chain descriptor";
+ return VBMetaVerifyResult::kErrorVerification;
+ }
+ return VBMetaVerifyResult::kSuccess;
+ case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED:
+ case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH:
+ case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH:
+ LERROR << vbmeta.partition() << ": Error verifying vbmeta image: "
+ << avb_vbmeta_verify_result_to_string(vbmeta_ret);
+ return VBMetaVerifyResult::kErrorVerification;
+ case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER:
+ // No way to continue this case.
+ LERROR << vbmeta.partition() << ": Error verifying vbmeta image: invalid vbmeta header";
+ break;
+ case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION:
+ // No way to continue this case.
+ LERROR << vbmeta.partition()
+ << ": Error verifying vbmeta image: unsupported AVB version";
+ break;
+ default:
+ LERROR << "Unknown vbmeta image verify return value: " << vbmeta_ret;
+ break;
+ }
+
+ return VBMetaVerifyResult::kError;
+}
+
+std::unique_ptr<VBMetaData> VerifyVBMetaData(int fd, const std::string& partition_name,
+ const std::string& expected_public_key_blob,
+ VBMetaVerifyResult* out_verify_result) {
+ uint64_t vbmeta_offset = 0;
+ uint64_t vbmeta_size = VBMetaData::kMaxVBMetaSize;
+ bool is_vbmeta_partition = StartsWith(partition_name, "vbmeta");
+
+ if (!is_vbmeta_partition) {
+ std::unique_ptr<AvbFooter> footer = GetAvbFooter(fd);
+ if (!footer) {
+ return nullptr;
+ }
+ vbmeta_offset = footer->vbmeta_offset;
+ vbmeta_size = footer->vbmeta_size;
+ }
+
+ if (vbmeta_size > VBMetaData::kMaxVBMetaSize) {
+ LERROR << "VbMeta size in footer exceeds kMaxVBMetaSize";
+ return nullptr;
+ }
+
+ auto vbmeta = std::make_unique<VBMetaData>(vbmeta_size, partition_name);
+ ssize_t num_read = TEMP_FAILURE_RETRY(pread64(fd, vbmeta->data(), vbmeta_size, vbmeta_offset));
+ // Allows partial read for vbmeta partition, because its vbmeta_size is kMaxVBMetaSize.
+ if (num_read < 0 || (!is_vbmeta_partition && static_cast<uint64_t>(num_read) != vbmeta_size)) {
+ PERROR << partition_name << ": Failed to read vbmeta at offset " << vbmeta_offset
+ << " with size " << vbmeta_size;
+ return nullptr;
+ }
+
+ auto verify_result = VerifyVBMetaSignature(*vbmeta, expected_public_key_blob);
+ if (out_verify_result != nullptr) *out_verify_result = verify_result;
+
+ if (verify_result == VBMetaVerifyResult::kSuccess ||
+ verify_result == VBMetaVerifyResult::kErrorVerification) {
+ return vbmeta;
+ }
+
+ return nullptr;
+}
+
+bool RollbackDetected(const std::string& partition_name ATTRIBUTE_UNUSED,
+ uint64_t rollback_index ATTRIBUTE_UNUSED) {
+ // TODO(bowgotsai): Support rollback protection.
+ return false;
+}
+
+std::vector<ChainInfo> GetChainPartitionInfo(const VBMetaData& vbmeta, bool* fatal_error) {
+ CHECK(fatal_error != nullptr);
+ std::vector<ChainInfo> chain_partitions;
+
+ size_t num_descriptors;
+ std::unique_ptr<const AvbDescriptor* [], decltype(&avb_free)> descriptors(
+ avb_descriptor_get_all(vbmeta.data(), vbmeta.size(), &num_descriptors), avb_free);
+
+ if (!descriptors || num_descriptors < 1) {
+ return {};
+ }
+
+ for (size_t i = 0; i < num_descriptors; i++) {
+ AvbDescriptor desc;
+ if (!avb_descriptor_validate_and_byteswap(descriptors[i], &desc)) {
+ LERROR << "Descriptor[" << i << "] is invalid in vbmeta: " << vbmeta.partition();
+ *fatal_error = true;
+ return {};
+ }
+ if (desc.tag == AVB_DESCRIPTOR_TAG_CHAIN_PARTITION) {
+ AvbChainPartitionDescriptor chain_desc;
+ if (!avb_chain_partition_descriptor_validate_and_byteswap(
+ (AvbChainPartitionDescriptor*)descriptors[i], &chain_desc)) {
+ LERROR << "Chain descriptor[" << i
+ << "] is invalid in vbmeta: " << vbmeta.partition();
+ *fatal_error = true;
+ return {};
+ }
+ const char* chain_partition_name =
+ ((const char*)descriptors[i]) + sizeof(AvbChainPartitionDescriptor);
+ const char* chain_public_key_blob =
+ chain_partition_name + chain_desc.partition_name_len;
+ chain_partitions.emplace_back(
+ std::string(chain_partition_name, chain_desc.partition_name_len),
+ std::string(chain_public_key_blob, chain_desc.public_key_len));
+ }
+ }
+
+ return chain_partitions;
+}
+
+VBMetaVerifyResult LoadAndVerifyVbmetaImpl(
+ const std::string& partition_name, const std::string& ab_suffix,
+ const std::string& ab_other_suffix, const std::string& expected_public_key_blob,
+ bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection,
+ std::function<std::string(const std::string&)> device_path_constructor,
+ bool is_chained_vbmeta, std::vector<VBMetaData>* out_vbmeta_images) {
+ // Ensures the device path (might be a symlink created by init) is ready to access.
+ auto device_path = device_path_constructor(
+ AvbPartitionToDevicePatition(partition_name, ab_suffix, ab_other_suffix));
+ if (!WaitForFile(device_path, 1s)) {
+ PERROR << "No such partition: " << device_path;
+ return VBMetaVerifyResult::kError;
+ }
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(device_path.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) {
+ PERROR << "Failed to open: " << device_path;
+ return VBMetaVerifyResult::kError;
+ }
+
+ VBMetaVerifyResult verify_result;
+ std::unique_ptr<VBMetaData> vbmeta =
+ VerifyVBMetaData(fd, partition_name, expected_public_key_blob, &verify_result);
+ if (!vbmeta) {
+ LERROR << partition_name << ": Failed to load vbmeta, result: " << verify_result;
+ return VBMetaVerifyResult::kError;
+ }
+
+ if (!allow_verification_error && verify_result == VBMetaVerifyResult::kErrorVerification) {
+ LERROR << partition_name << ": allow verification error is not allowed";
+ return VBMetaVerifyResult::kError;
+ }
+
+ std::unique_ptr<AvbVBMetaImageHeader> vbmeta_header =
+ vbmeta->GetVBMetaHeader(true /* update_vbmeta_size */);
+ if (!vbmeta_header) {
+ LERROR << partition_name << ": Failed to get vbmeta header";
+ return VBMetaVerifyResult::kError;
+ }
+
+ if (rollback_protection && RollbackDetected(partition_name, vbmeta_header->rollback_index)) {
+ return VBMetaVerifyResult::kError;
+ }
+
+ // vbmeta flags can only be set by the top-level vbmeta image.
+ if (is_chained_vbmeta && vbmeta_header->flags != 0) {
+ LERROR << partition_name << ": chained vbmeta image has non-zero flags";
+ return VBMetaVerifyResult::kError;
+ }
+
+ if (out_vbmeta_images) {
+ out_vbmeta_images->emplace_back(std::move(*vbmeta));
+ }
+
+ // If verification has been disabled by setting a bit in the image, we're done.
+ if (vbmeta_header->flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) {
+ LWARNING << "VERIFICATION_DISABLED bit is set for partition: " << partition_name;
+ return verify_result;
+ }
+
+ if (load_chained_vbmeta) {
+ bool fatal_error = false;
+ auto chain_partitions = GetChainPartitionInfo(*out_vbmeta_images->rbegin(), &fatal_error);
+ if (fatal_error) {
+ return VBMetaVerifyResult::kError;
+ }
+ for (auto& chain : chain_partitions) {
+ auto sub_ret = LoadAndVerifyVbmetaImpl(
+ chain.partition_name, ab_suffix, ab_other_suffix, chain.public_key_blob,
+ allow_verification_error, load_chained_vbmeta, rollback_protection,
+ device_path_constructor, true, /* is_chained_vbmeta */
+ out_vbmeta_images);
+ if (sub_ret != VBMetaVerifyResult::kSuccess) {
+ verify_result = sub_ret; // might be 'ERROR' or 'ERROR VERIFICATION'.
+ if (verify_result == VBMetaVerifyResult::kError) {
+ return verify_result; // stop here if we got an 'ERROR'.
+ }
+ }
+ }
+ }
+
+ return verify_result;
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libfs_avb/avb_util.h b/fs_mgr/libfs_avb/avb_util.h
index b81e931..babbfef 100644
--- a/fs_mgr/libfs_avb/avb_util.h
+++ b/fs_mgr/libfs_avb/avb_util.h
@@ -16,9 +16,11 @@
#pragma once
+#include <ostream>
#include <string>
#include <vector>
+#include <fstab/fstab.h>
#include <libavb/libavb.h>
#include <libdm/dm.h>
@@ -27,6 +29,22 @@
namespace android {
namespace fs_mgr {
+enum class VBMetaVerifyResult {
+ kSuccess = 0,
+ kError = 1,
+ kErrorVerification = 2,
+};
+
+std::ostream& operator<<(std::ostream& os, VBMetaVerifyResult);
+
+struct ChainInfo {
+ std::string partition_name;
+ std::string public_key_blob;
+
+ ChainInfo(const std::string& chain_partition_name, const std::string& chain_public_key_blob)
+ : partition_name(chain_partition_name), public_key_blob(chain_public_key_blob) {}
+};
+
// AvbHashtreeDescriptor to dm-verity table setup.
bool GetHashtreeDescriptor(const std::string& partition_name,
const std::vector<VBMetaData>& vbmeta_images,
@@ -41,5 +59,37 @@
const std::string& salt, const std::string& root_digest,
bool wait_for_verity_dev);
+// Maps AVB partition name to a device partition name.
+std::string AvbPartitionToDevicePatition(const std::string& avb_partition_name,
+ const std::string& ab_suffix,
+ const std::string& ab_other_suffix);
+
+// AvbFooter and AvbMetaImage maninpulations.
+off64_t GetTotalSize(int fd);
+
+std::unique_ptr<AvbFooter> GetAvbFooter(int fd);
+
+std::unique_ptr<VBMetaData> VerifyVBMetaData(int fd, const std::string& partition_name,
+ const std::string& expected_public_key_blob,
+ VBMetaVerifyResult* out_verify_result);
+
+VBMetaVerifyResult VerifyVBMetaSignature(const VBMetaData& vbmeta,
+ const std::string& expected_public_key_blob);
+
+bool VerifyPublicKeyBlob(const uint8_t* key, size_t length, const std::string& expected_key_blob);
+
+// Detects if whether a partition contains a rollback image.
+bool RollbackDetected(const std::string& partition_name, uint64_t rollback_index);
+
+// Extracts chain partition info.
+std::vector<ChainInfo> GetChainPartitionInfo(const VBMetaData& vbmeta, bool* fatal_error);
+
+VBMetaVerifyResult LoadAndVerifyVbmetaImpl(
+ const std::string& partition_name, const std::string& ab_suffix,
+ const std::string& ab_other_suffix, const std::string& expected_public_key_blob,
+ bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection,
+ std::function<std::string(const std::string&)> device_path_constructor,
+ bool is_chained_vbmeta, std::vector<VBMetaData>* out_vbmeta_images);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index 957aa87..9f8ad53 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -39,6 +39,7 @@
using android::base::Basename;
using android::base::ParseUint;
+using android::base::ReadFileToString;
using android::base::StringPrintf;
namespace android {
@@ -59,6 +60,51 @@
return std::make_pair(total_size, matched);
}
+template <typename Hasher>
+std::pair<std::string, size_t> CalculateVbmetaDigest(const std::vector<VBMetaData>& vbmeta_images) {
+ std::string digest;
+ size_t total_size = 0;
+
+ Hasher hasher;
+ for (const auto& vbmeta : vbmeta_images) {
+ hasher.update(vbmeta.data(), vbmeta.size());
+ total_size += vbmeta.size();
+ }
+
+ // Converts digest bytes to a hex string.
+ digest = BytesToHex(hasher.finalize(), Hasher::DIGEST_SIZE);
+ return std::make_pair(digest, total_size);
+}
+
+// Helper functions to dump enum class AvbHandleStatus.
+const char* AvbHandleStatusToString(AvbHandleStatus status) {
+ // clang-format off
+ static const char* const name[] = {
+ "Success",
+ "Uninitialized",
+ "HashtreeDisabled",
+ "VerificationDisabled",
+ "VerificationError",
+ "Unknown",
+ };
+ // clang-format on
+
+ uint32_t index = static_cast<uint32_t>(status);
+ uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;
+ if (index >= unknown_index) {
+ index = unknown_index;
+ }
+
+ return name[index];
+}
+
+std::ostream& operator<<(std::ostream& os, AvbHandleStatus status) {
+ os << AvbHandleStatusToString(status);
+ return os;
+}
+
+// class AvbVerifier
+// -----------------
// Reads the following values from kernel cmdline and provides the
// VerifyVbmetaImages() to verify AvbSlotVerifyData.
// - androidboot.vbmeta.hash_alg
@@ -74,12 +120,6 @@
AvbVerifier() = default;
private:
- enum HashAlgorithm {
- kInvalid = 0,
- kSHA256 = 1,
- kSHA512 = 2,
- };
-
HashAlgorithm hash_alg_;
uint8_t digest_[SHA512_DIGEST_LENGTH];
size_t vbmeta_size_;
@@ -105,10 +145,10 @@
fs_mgr_get_boot_config("vbmeta.hash_alg", &hash_alg);
if (hash_alg == "sha256") {
expected_digest_size = SHA256_DIGEST_LENGTH * 2;
- avb_verifier->hash_alg_ = kSHA256;
+ avb_verifier->hash_alg_ = HashAlgorithm::kSHA256;
} else if (hash_alg == "sha512") {
expected_digest_size = SHA512_DIGEST_LENGTH * 2;
- avb_verifier->hash_alg_ = kSHA512;
+ avb_verifier->hash_alg_ = HashAlgorithm::kSHA512;
} else {
LERROR << "Unknown hash algorithm: " << hash_alg.c_str();
return nullptr;
@@ -140,10 +180,10 @@
size_t total_size = 0;
bool digest_matched = false;
- if (hash_alg_ == kSHA256) {
+ if (hash_alg_ == HashAlgorithm::kSHA256) {
std::tie(total_size, digest_matched) =
VerifyVbmetaDigest<SHA256Hasher>(vbmeta_images, digest_);
- } else if (hash_alg_ == kSHA512) {
+ } else if (hash_alg_ == HashAlgorithm::kSHA512) {
std::tie(total_size, digest_matched) =
VerifyVbmetaDigest<SHA512Hasher>(vbmeta_images, digest_);
}
@@ -162,6 +202,98 @@
return true;
}
+// class AvbHandle
+// ---------------
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(
+ const std::string& partition_name, const std::string& ab_suffix,
+ const std::string& ab_other_suffix, const std::string& expected_public_key_path,
+ const HashAlgorithm& hash_algorithm, bool allow_verification_error,
+ bool load_chained_vbmeta, bool rollback_protection,
+ std::function<std::string(const std::string&)> custom_device_path) {
+ AvbUniquePtr avb_handle(new AvbHandle());
+ if (!avb_handle) {
+ LERROR << "Failed to allocate AvbHandle";
+ return nullptr;
+ }
+
+ std::string expected_key_blob;
+ if (!expected_public_key_path.empty()) {
+ if (access(expected_public_key_path.c_str(), F_OK) != 0) {
+ LERROR << "Expected public key path doesn't exist: " << expected_public_key_path;
+ return nullptr;
+ } else if (!ReadFileToString(expected_public_key_path, &expected_key_blob)) {
+ LERROR << "Failed to load: " << expected_public_key_path;
+ return nullptr;
+ }
+ }
+
+ auto android_by_name_symlink = [](const std::string& partition_name_with_ab) {
+ return "/dev/block/by-name/" + partition_name_with_ab;
+ };
+
+ auto device_path = custom_device_path ? custom_device_path : android_by_name_symlink;
+
+ auto verify_result = LoadAndVerifyVbmetaImpl(
+ partition_name, ab_suffix, ab_other_suffix, expected_key_blob, allow_verification_error,
+ load_chained_vbmeta, rollback_protection, device_path, false,
+ /* is_chained_vbmeta */ &avb_handle->vbmeta_images_);
+ switch (verify_result) {
+ case VBMetaVerifyResult::kSuccess:
+ avb_handle->status_ = AvbHandleStatus::kSuccess;
+ break;
+ case VBMetaVerifyResult::kErrorVerification:
+ avb_handle->status_ = AvbHandleStatus::kVerificationError;
+ break;
+ default:
+ LERROR << "LoadAndVerifyVbmetaImpl failed, result: " << verify_result;
+ return nullptr;
+ }
+
+ // Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
+ avb_handle->avb_version_ = StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
+
+ // Checks any disabled flag is set.
+ std::unique_ptr<AvbVBMetaImageHeader> vbmeta_header =
+ avb_handle->vbmeta_images_[0].GetVBMetaHeader();
+ bool verification_disabled = ((AvbVBMetaImageFlags)vbmeta_header->flags &
+ AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+ bool hashtree_disabled =
+ ((AvbVBMetaImageFlags)vbmeta_header->flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+ if (verification_disabled) {
+ avb_handle->status_ = AvbHandleStatus::kVerificationDisabled;
+ } else if (hashtree_disabled) {
+ avb_handle->status_ = AvbHandleStatus::kHashtreeDisabled;
+ }
+
+ // Calculates the summary info for all vbmeta_images_;
+ std::string digest;
+ size_t total_size;
+ if (hash_algorithm == HashAlgorithm::kSHA256) {
+ std::tie(digest, total_size) =
+ CalculateVbmetaDigest<SHA256Hasher>(avb_handle->vbmeta_images_);
+ } else if (hash_algorithm == HashAlgorithm::kSHA512) {
+ std::tie(digest, total_size) =
+ CalculateVbmetaDigest<SHA512Hasher>(avb_handle->vbmeta_images_);
+ } else {
+ LERROR << "Invalid hash algorithm";
+ return nullptr;
+ }
+ avb_handle->vbmeta_info_ = VBMetaInfo(digest, hash_algorithm, total_size);
+
+ LINFO << "Returning avb_handle with status: " << avb_handle->status_;
+ return avb_handle;
+}
+
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta() {
+ // Loads inline vbmeta images, starting from /vbmeta.
+ return LoadAndVerifyVbmeta("vbmeta", fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix(),
+ {} /* expected_public_key, already checked by bootloader */,
+ HashAlgorithm::kSHA256,
+ IsDeviceUnlocked(), /* allow_verification_error */
+ true, /* load_chained_vbmeta */
+ false, /* rollback_protection, already checked by bootloader */
+ nullptr /* custom_device_path */);
+}
AvbUniquePtr AvbHandle::Open() {
bool is_device_unlocked = IsDeviceUnlocked();
@@ -192,14 +324,14 @@
// for more details.
switch (verify_result) {
case AVB_SLOT_VERIFY_RESULT_OK:
- avb_handle->status_ = kAvbHandleSuccess;
+ avb_handle->status_ = AvbHandleStatus::kSuccess;
break;
case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
if (!is_device_unlocked) {
LERROR << "ERROR_VERIFICATION isn't allowed when the device is LOCKED";
return nullptr;
}
- avb_handle->status_ = kAvbHandleVerificationError;
+ avb_handle->status_ = AvbHandleStatus::kVerificationError;
break;
default:
LERROR << "avb_slot_verify failed, result: " << verify_result;
@@ -220,7 +352,7 @@
AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
if (verification_disabled) {
- avb_handle->status_ = kAvbHandleVerificationDisabled;
+ avb_handle->status_ = AvbHandleStatus::kVerificationDisabled;
} else {
// Verifies vbmeta structs against the digest passed from bootloader in kernel cmdline.
std::unique_ptr<AvbVerifier> avb_verifier = AvbVerifier::Create();
@@ -237,7 +369,7 @@
bool hashtree_disabled = ((AvbVBMetaImageFlags)vbmeta_header.flags &
AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
if (hashtree_disabled) {
- avb_handle->status_ = kAvbHandleHashtreeDisabled;
+ avb_handle->status_ = AvbHandleStatus::kHashtreeDisabled;
}
}
@@ -246,11 +378,12 @@
}
AvbHashtreeResult AvbHandle::SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev) {
- if (!fstab_entry || status_ == kAvbHandleUninitialized || vbmeta_images_.size() < 1) {
+ if (!fstab_entry || status_ == AvbHandleStatus::kUninitialized || vbmeta_images_.size() < 1) {
return AvbHashtreeResult::kFail;
}
- if (status_ == kAvbHandleHashtreeDisabled || status_ == kAvbHandleVerificationDisabled) {
+ if (status_ == AvbHandleStatus::kHashtreeDisabled ||
+ status_ == AvbHandleStatus::kVerificationDisabled) {
LINFO << "AVB HASHTREE disabled on: " << fstab_entry->mount_point;
return AvbHashtreeResult::kDisabled;
}
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
index eca6984..7af3c7e 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
@@ -16,6 +16,7 @@
#pragma once
+#include <functional>
#include <memory>
#include <string>
#include <vector>
@@ -32,23 +33,56 @@
kDisabled,
};
+enum class HashAlgorithm {
+ kInvalid = 0,
+ kSHA256 = 1,
+ kSHA512 = 2,
+};
+
+enum class AvbHandleStatus {
+ kSuccess = 0,
+ kUninitialized = 1,
+ kHashtreeDisabled = 2,
+ kVerificationDisabled = 3,
+ kVerificationError = 4,
+};
+
+struct VBMetaInfo {
+ std::string digest;
+ HashAlgorithm hash_algorithm;
+ size_t total_size;
+
+ VBMetaInfo() {}
+
+ VBMetaInfo(std::string digest_value, HashAlgorithm algorithm, size_t size)
+ : digest(std::move(digest_value)), hash_algorithm(algorithm), total_size(size) {}
+};
+
class VBMetaData {
public:
// Constructors
VBMetaData() : vbmeta_ptr_(nullptr), vbmeta_size_(0){};
- VBMetaData(const uint8_t* data, size_t size)
- : vbmeta_ptr_(new (std::nothrow) uint8_t[size]), vbmeta_size_(size) {
+ VBMetaData(const uint8_t* data, size_t size, const std::string& partition_name)
+ : vbmeta_ptr_(new (std::nothrow) uint8_t[size]),
+ vbmeta_size_(size),
+ partition_name_(partition_name) {
// The ownership of data is NOT transferred, i.e., the caller still
// needs to release the memory as we make a copy here.
memcpy(vbmeta_ptr_.get(), data, size * sizeof(uint8_t));
}
- explicit VBMetaData(size_t size)
- : vbmeta_ptr_(new (std::nothrow) uint8_t[size]), vbmeta_size_(size) {}
+ explicit VBMetaData(size_t size, const std::string& partition_name)
+ : vbmeta_ptr_(new (std::nothrow) uint8_t[size]),
+ vbmeta_size_(size),
+ partition_name_(partition_name) {}
+
+ // Extracts vbmeta header from the vbmeta buffer, set update_vbmeta_size to
+ // true to update vbmeta_size_ to the actual size with valid content.
+ std::unique_ptr<AvbVBMetaImageHeader> GetVBMetaHeader(bool update_vbmeta_size = false);
// Get methods for each data member.
- const std::string& device_path() const { return device_path_; }
+ const std::string& partition() const { return partition_name_; }
uint8_t* data() const { return vbmeta_ptr_.get(); }
const size_t& size() const { return vbmeta_size_; }
@@ -56,9 +90,9 @@
static const size_t kMaxVBMetaSize = 64 * 1024;
private:
- std::string device_path_;
std::unique_ptr<uint8_t[]> vbmeta_ptr_;
size_t vbmeta_size_;
+ std::string partition_name_;
};
class FsManagerAvbOps;
@@ -71,7 +105,7 @@
// descriptors to load verity table into kernel through ioctl.
class AvbHandle {
public:
- // The factory method to return a AvbUniquePtr that holds
+ // The factory methods to return a AvbUniquePtr that holds
// the verified AVB (external/avb) metadata of all verified partitions
// in vbmeta_images_.
//
@@ -79,31 +113,40 @@
// - androidboot.vbmeta.{hash_alg, size, digest}.
//
// A typical usage will be:
- // - AvbUniquePtr handle = AvbHandle::Open();
+ // - AvbUniquePtr handle = AvbHandle::Open(); or
+ // - AvbUniquePtr handle = AvbHandle::LoadAndVerifyVbmeta();
//
// Possible return values:
// - nullptr: any error when reading and verifying the metadata,
// e.g., I/O error, digest value mismatch, size mismatch, etc.
//
- // - a valid unique_ptr with status kAvbHandleHashtreeDisabled:
+ // - a valid unique_ptr with status AvbHandleStatus::HashtreeDisabled:
// to support the existing 'adb disable-verity' feature in Android.
// It's very helpful for developers to make the filesystem writable to
// allow replacing binaries on the device.
//
- // - a valid unique_ptr with status kAvbHandleVerificationDisabled:
+ // - a valid unique_ptr with status AvbHandleStatus::VerificationDisabled:
// to support 'avbctl disable-verification': only the top-level
// vbmeta is read, vbmeta structs in other partitions are not processed.
// It's needed to bypass AVB when using the generic system.img to run
// VTS for project Treble.
//
- // - a valid unique_ptr with status kAvbHandleVerificationError:
+ // - a valid unique_ptr with status AvbHandleStatus::VerificationError:
// there is verification error when libavb loads vbmeta from each
// partition. This is only allowed when the device is unlocked.
//
- // - a valid unique_ptr with status kAvbHandleSuccess: the metadata
+ // - a valid unique_ptr with status AvbHandleStatus::Success: the metadata
// is verified and can be trusted.
//
- static AvbUniquePtr Open();
+ // TODO(bowgotsai): remove Open() and switch to LoadAndVerifyVbmeta().
+ static AvbUniquePtr Open(); // loads inline vbmeta, via libavb.
+ static AvbUniquePtr LoadAndVerifyVbmeta(); // loads inline vbmeta.
+ static AvbUniquePtr LoadAndVerifyVbmeta( // loads offline vbmeta.
+ const std::string& partition_name, const std::string& ab_suffix,
+ const std::string& ab_other_suffix, const std::string& expected_public_key,
+ const HashAlgorithm& hash_algorithm, bool allow_verification_error,
+ bool load_chained_vbmeta, bool rollback_protection,
+ std::function<std::string(const std::string&)> custom_device_path = nullptr);
// Sets up dm-verity on the given fstab entry.
// The 'wait_for_verity_dev' parameter makes this function wait for the
@@ -118,6 +161,8 @@
AvbHashtreeResult SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev);
const std::string& avb_version() const { return avb_version_; }
+ const VBMetaInfo& vbmeta_info() const { return vbmeta_info_; }
+ AvbHandleStatus status() const { return status_; }
AvbHandle(const AvbHandle&) = delete; // no copy
AvbHandle& operator=(const AvbHandle&) = delete; // no assignment
@@ -126,17 +171,10 @@
AvbHandle& operator=(AvbHandle&&) noexcept = delete; // no move assignment
private:
- enum AvbHandleStatus {
- kAvbHandleSuccess = 0,
- kAvbHandleUninitialized,
- kAvbHandleHashtreeDisabled,
- kAvbHandleVerificationDisabled,
- kAvbHandleVerificationError,
- };
-
- AvbHandle() : status_(kAvbHandleUninitialized) {}
+ AvbHandle() : status_(AvbHandleStatus::kUninitialized) {}
std::vector<VBMetaData> vbmeta_images_;
+ VBMetaInfo vbmeta_info_; // A summary info for vbmeta_images_.
AvbHandleStatus status_;
std::string avb_version_;
};
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
new file mode 100644
index 0000000..26b3294
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -0,0 +1,1088 @@
+/*
+ * 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 <android-base/unique_fd.h>
+#include <base/files/file_util.h>
+#include <base/rand_util.h>
+#include <base/strings/string_util.h>
+
+#include "avb_util.h"
+#include "fs_avb_test_util.h"
+
+// Target classes or functions to test:
+using android::fs_mgr::AvbPartitionToDevicePatition;
+using android::fs_mgr::GetAvbFooter;
+using android::fs_mgr::GetChainPartitionInfo;
+using android::fs_mgr::GetTotalSize;
+using android::fs_mgr::LoadAndVerifyVbmetaImpl;
+using android::fs_mgr::VBMetaData;
+using android::fs_mgr::VBMetaVerifyResult;
+using android::fs_mgr::VerifyPublicKeyBlob;
+using android::fs_mgr::VerifyVBMetaData;
+using android::fs_mgr::VerifyVBMetaSignature;
+
+namespace fs_avb_host_test {
+
+class AvbUtilTest : public BaseFsAvbTest {
+ public:
+ AvbUtilTest(){};
+
+ protected:
+ ~AvbUtilTest(){};
+ // Helper function for VerifyVBMetaSignature test. Modifies vbmeta.data()
+ // in a number of places at |offset| of size |length| and checks that
+ // VerifyVBMetaSignature() returns |expected_result|.
+ bool TestVBMetaModification(VBMetaVerifyResult expected_result, const VBMetaData& vbmeta,
+ size_t offset, size_t length);
+ // Modifies a random bit for a file, in the range of [offset, offset + length - 1].
+ void ModifyFile(const base::FilePath& file_path, size_t offset, ssize_t length);
+
+ // Loads the content of avb_image_path and comparies it with the content of vbmeta.
+ bool CompareVBMeta(const base::FilePath& avb_image_path, const VBMetaData& expected_vbmeta);
+};
+
+TEST_F(AvbUtilTest, AvbPartitionToDevicePatition) {
+ EXPECT_EQ("system", AvbPartitionToDevicePatition("system", "", ""));
+ EXPECT_EQ("system", AvbPartitionToDevicePatition("system", "", "_b"));
+
+ EXPECT_EQ("system_a", AvbPartitionToDevicePatition("system", "_a", ""));
+ EXPECT_EQ("system_a", AvbPartitionToDevicePatition("system", "_a", "_b"));
+
+ EXPECT_EQ("system_b", AvbPartitionToDevicePatition("system_other", "", "_b"));
+ EXPECT_EQ("system_b", AvbPartitionToDevicePatition("system_other", "_a", "_b"));
+}
+
+TEST_F(AvbUtilTest, GetFdTotalSize) {
+ // Generates a raw test.img via BaseFsAvbTest.
+ const size_t image_size = 5 * 1024 * 1024;
+ base::FilePath image_path = GenerateImage("test.img", image_size);
+
+ // Checks file size is as expected via base::GetFileSize().
+ int64_t file_size;
+ ASSERT_TRUE(base::GetFileSize(image_path, &file_size));
+ EXPECT_EQ(image_size, file_size);
+
+ // Checks file size is expected via libfs_avb internal utils.
+ auto fd = OpenUniqueReadFd(image_path);
+ EXPECT_EQ(image_size, GetTotalSize(fd));
+}
+
+TEST_F(AvbUtilTest, GetFdTotalSizeWithOffset) {
+ // Generates a raw test.img via BaseFsAvbTest.
+ const size_t image_size = 10 * 1024 * 1024;
+ base::FilePath image_path = GenerateImage("test.img", image_size);
+
+ // Checks file size is expected even with a non-zero offset at the beginning.
+ auto fd = OpenUniqueReadFd(image_path);
+ off_t initial_offset = 2019;
+ EXPECT_EQ(initial_offset, lseek(fd, initial_offset, SEEK_SET));
+ EXPECT_EQ(image_size, GetTotalSize(fd)); // checks that total size is still returned.
+ EXPECT_EQ(initial_offset, lseek(fd, 0, SEEK_CUR)); // checks original offset is restored.
+}
+
+TEST_F(AvbUtilTest, GetAvbFooter) {
+ // Generates a raw system.img
+ const size_t image_size = 10 * 1024 * 1024;
+ const size_t partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", image_size);
+ EXPECT_NE(0U, system_path.value().size());
+
+ // Checks image size is as expected.
+ int64_t file_size;
+ ASSERT_TRUE(base::GetFileSize(system_path, &file_size));
+ EXPECT_EQ(image_size, file_size);
+
+ // Appends AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", partition_size, "SHA512_RSA8192", 20,
+ data_dir_.Append("testkey_rsa8192.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Checks partition size is as expected, after adding footer.
+ ASSERT_TRUE(base::GetFileSize(system_path, &file_size));
+ EXPECT_EQ(partition_size, file_size);
+
+ // Checks avb footer and avb vbmeta.
+ EXPECT_EQ(
+ "Footer version: 1.0\n"
+ "Image size: 15728640 bytes\n"
+ "Original image size: 10485760 bytes\n"
+ "VBMeta offset: 10661888\n"
+ "VBMeta size: 3648 bytes\n"
+ "--\n"
+ "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: 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(system_path));
+
+ // Checks each field from GetAvbFooter(fd).
+ auto fd = OpenUniqueReadFd(system_path);
+ auto footer = GetAvbFooter(fd);
+ EXPECT_NE(nullptr, footer);
+ EXPECT_EQ(10485760, footer->original_image_size);
+ EXPECT_EQ(10661888, footer->vbmeta_offset);
+ EXPECT_EQ(3648, footer->vbmeta_size);
+}
+
+TEST_F(AvbUtilTest, GetAvbFooterErrorVerification) {
+ // Generates a raw system.img
+ const size_t image_size = 5 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", image_size);
+
+ // Checks each field from GetAvbFooter(fd).
+ auto fd = OpenUniqueReadFd(system_path);
+ auto footer = GetAvbFooter(fd);
+ EXPECT_EQ(nullptr, footer);
+}
+
+TEST_F(AvbUtilTest, GetAvbFooterInsufficientSize) {
+ // Generates a raw system.img
+ const size_t image_size = AVB_FOOTER_SIZE - 10;
+ base::FilePath system_path = GenerateImage("system.img", image_size);
+
+ // Checks each field from GetAvbFooter(fd).
+ auto fd = OpenUniqueReadFd(system_path);
+ auto footer = GetAvbFooter(fd);
+ EXPECT_EQ(nullptr, footer);
+}
+
+TEST_F(AvbUtilTest, GetVBMetaHeader) {
+ // 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);
+ // Appends AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", partition_size, "SHA256_RSA4096", 10,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+ // Extracts boot vbmeta from boot.img into boot-vbmeta.img.
+ base::FilePath boot_vbmeta = 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"));
+
+ // Creates a VBMetaData with the content from boot-vbmeta.img.
+ std::string content;
+ EXPECT_TRUE(base::ReadFileToString(boot_vbmeta, &content));
+ VBMetaData vbmeta((uint8_t*)content.data(), content.size(), "boot-vbmeta");
+ EXPECT_EQ(content.size(), vbmeta.size());
+
+ // Checks each field returned from GetVBMetaHeader().
+ auto vbmeta_header = vbmeta.GetVBMetaHeader(false /* update_vbmeta_size */);
+ EXPECT_NE(nullptr, vbmeta_header);
+ EXPECT_EQ(576, vbmeta_header->authentication_data_block_size);
+ EXPECT_EQ(1216, vbmeta_header->auxiliary_data_block_size);
+ EXPECT_EQ(AVB_ALGORITHM_TYPE_SHA256_RSA4096, vbmeta_header->algorithm_type);
+ EXPECT_EQ(0, vbmeta_header->hash_offset);
+ EXPECT_EQ(32, vbmeta_header->hash_size);
+ EXPECT_EQ(32, vbmeta_header->signature_offset);
+ EXPECT_EQ(512, vbmeta_header->signature_size);
+ EXPECT_EQ(176, vbmeta_header->public_key_offset);
+ EXPECT_EQ(1032, vbmeta_header->public_key_size);
+ EXPECT_EQ(0, vbmeta_header->descriptors_offset);
+ EXPECT_EQ(176, vbmeta_header->descriptors_size);
+ EXPECT_EQ(10, vbmeta_header->rollback_index);
+ EXPECT_EQ(0, vbmeta_header->flags);
+ EXPECT_EQ("unit test", std::string((const char*)vbmeta_header->release_string));
+
+ // Appends some garbage to the end of the vbmeta buffer, checks it still can work.
+ std::string padding(2020, 'A'); // Generate a padding with length 2020.
+ std::string content_padding = content + padding;
+ VBMetaData vbmeta_padding((const uint8_t*)content_padding.data(), content_padding.size(),
+ "boot");
+ EXPECT_EQ(content_padding.size(), vbmeta_padding.size());
+
+ // Checks each field still can be parsed properly, even with garbage padding.
+ vbmeta_header = vbmeta_padding.GetVBMetaHeader(false /* update_vbmeta_size */);
+ EXPECT_NE(nullptr, vbmeta_header);
+ EXPECT_EQ(576, vbmeta_header->authentication_data_block_size);
+ EXPECT_EQ(1216, vbmeta_header->auxiliary_data_block_size);
+ EXPECT_EQ(AVB_ALGORITHM_TYPE_SHA256_RSA4096, vbmeta_header->algorithm_type);
+ EXPECT_EQ(0, vbmeta_header->hash_offset);
+ EXPECT_EQ(32, vbmeta_header->hash_size);
+ EXPECT_EQ(32, vbmeta_header->signature_offset);
+ EXPECT_EQ(512, vbmeta_header->signature_size);
+ EXPECT_EQ(176, vbmeta_header->public_key_offset);
+ EXPECT_EQ(1032, vbmeta_header->public_key_size);
+ EXPECT_EQ(0, vbmeta_header->descriptors_offset);
+ EXPECT_EQ(176, vbmeta_header->descriptors_size);
+ EXPECT_EQ(10, vbmeta_header->rollback_index);
+ EXPECT_EQ(0, vbmeta_header->flags);
+ EXPECT_EQ("unit test", std::string((const char*)vbmeta_header->release_string));
+
+ // Checks vbmeta size is updated to the actual size without padding.
+ vbmeta_header = vbmeta_padding.GetVBMetaHeader(true /* update_vbmeta_size */);
+ EXPECT_EQ(content_padding.size() - padding.size(), vbmeta_padding.size());
+}
+
+TEST_F(AvbUtilTest, VerifyPublicKeyBlob) {
+ // Generates a raw key.bin
+ const size_t key_size = 2048;
+ base::FilePath key_path = GenerateImage("key.bin", key_size);
+
+ uint8_t key_data[key_size];
+ EXPECT_EQ(key_size, base::ReadFile(key_path, (char*)key_data, key_size));
+
+ std::string expected_key_blob;
+ EXPECT_TRUE(base::ReadFileToString(key_path, &expected_key_blob));
+ EXPECT_TRUE(VerifyPublicKeyBlob(key_data, key_size, expected_key_blob));
+
+ key_data[10] ^= 0x80; // toggles a bit and expects a failure
+ EXPECT_FALSE(VerifyPublicKeyBlob(key_data, key_size, expected_key_blob));
+ key_data[10] ^= 0x80; // toggles the bit again, should pass
+ EXPECT_TRUE(VerifyPublicKeyBlob(key_data, key_size, expected_key_blob));
+}
+
+TEST_F(AvbUtilTest, VerifyEmptyPublicKeyBlob) {
+ // Generates a raw key.bin
+ const size_t key_size = 2048;
+ base::FilePath key_path = GenerateImage("key.bin", key_size);
+
+ uint8_t key_data[key_size];
+ EXPECT_EQ(key_size, base::ReadFile(key_path, (char*)key_data, key_size));
+
+ std::string expected_key_blob = ""; // empty means no expectation, thus return true.
+ EXPECT_TRUE(VerifyPublicKeyBlob(key_data, key_size, expected_key_blob));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaSignature) {
+ const size_t image_size = 10 * 1024 * 1024;
+ const size_t partition_size = 15 * 1024 * 1024;
+ auto signing_key = data_dir_.Append("testkey_rsa4096.pem");
+ auto vbmeta = GenerateImageAndExtractVBMetaData("system", image_size, partition_size,
+ "hashtree", signing_key, "SHA256_RSA4096",
+ 10 /* rollback_index */);
+
+ auto expected_public_key = ExtractPublicKeyAvbBlob(signing_key);
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess, VerifyVBMetaSignature(vbmeta, expected_public_key));
+
+ // Converts the expected key into an 'unexpected' key.
+ expected_public_key[10] ^= 0x80;
+ EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+ VerifyVBMetaSignature(vbmeta, expected_public_key));
+}
+
+bool AvbUtilTest::TestVBMetaModification(VBMetaVerifyResult expected_result,
+ const VBMetaData& vbmeta, size_t offset, size_t length) {
+ uint8_t* d = reinterpret_cast<uint8_t*>(vbmeta.data());
+ const int kNumCheckIntervals = 8;
+
+ // Tests |kNumCheckIntervals| modifications in the start, middle, and
+ // end of the given sub-array at offset with size.
+ for (int n = 0; n <= kNumCheckIntervals; n++) {
+ size_t o = std::min(length * n / kNumCheckIntervals, length - 1) + offset;
+ d[o] ^= 0x80;
+ VBMetaVerifyResult result = VerifyVBMetaSignature(vbmeta, "" /* expected_public_key */);
+ d[o] ^= 0x80;
+ if (result != expected_result) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaSignatureWithModification) {
+ const size_t image_size = 10 * 1024 * 1024;
+ const size_t partition_size = 15 * 1024 * 1024;
+ auto signing_key = data_dir_.Append("testkey_rsa4096.pem");
+ auto vbmeta = GenerateImageAndExtractVBMetaData("system", image_size, partition_size,
+ "hashtree", signing_key, "SHA256_RSA4096",
+ 10 /* rollback_index */);
+
+ auto header = vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);
+ size_t header_block_offset = 0;
+ size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);
+ size_t auxiliary_block_offset =
+ authentication_block_offset + header->authentication_data_block_size;
+
+ // Should detect modifications in the auxiliary data block.
+ EXPECT_TRUE(TestVBMetaModification(VBMetaVerifyResult::kErrorVerification, vbmeta,
+ auxiliary_block_offset, header->auxiliary_data_block_size));
+
+ // Sholud detect modifications in the hash part of authentication data block.
+ EXPECT_TRUE(TestVBMetaModification(VBMetaVerifyResult::kErrorVerification, vbmeta,
+ authentication_block_offset + header->hash_offset,
+ header->hash_size));
+
+ // Sholud detect modifications in the signature part of authentication data block.
+ EXPECT_TRUE(TestVBMetaModification(VBMetaVerifyResult::kErrorVerification, vbmeta,
+ authentication_block_offset + header->signature_offset,
+ header->signature_size));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaSignatureNotSigned) {
+ const size_t image_size = 10 * 1024 * 1024;
+ const size_t partition_size = 15 * 1024 * 1024;
+ auto vbmeta = GenerateImageAndExtractVBMetaData(
+ "system", image_size, partition_size, "hashtree", {} /* avb_signing_key */,
+ "" /* avb_algorithm */, 10 /* rollback_index */);
+
+ EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, VerifyVBMetaSignature(vbmeta, ""));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaSignatureInvalidVBMeta) {
+ const size_t buffer_size = 5 * 1024 * 1024;
+ std::vector<uint8_t> vbmeta_buffer(buffer_size);
+ for (size_t n = 0; n < buffer_size; n++) {
+ vbmeta_buffer[n] = uint8_t(n);
+ }
+
+ VBMetaData invalid_vbmeta((const uint8_t*)vbmeta_buffer.data(), vbmeta_buffer.size(),
+ "invalid_vbmeta");
+ EXPECT_EQ(VBMetaVerifyResult::kError, VerifyVBMetaSignature(invalid_vbmeta, ""));
+}
+
+bool AvbUtilTest::CompareVBMeta(const base::FilePath& avb_image_path,
+ const VBMetaData& expected_vbmeta) {
+ if (!base::PathExists(avb_image_path)) return false;
+
+ std::string image_file_name = avb_image_path.RemoveExtension().BaseName().value();
+
+ base::FilePath extracted_vbmeta_path;
+ if (base::StartsWith(image_file_name, "vbmeta", base::CompareCase::INSENSITIVE_ASCII)) {
+ extracted_vbmeta_path = avb_image_path; // no need to extract if it's a vbmeta image.
+ } else {
+ extracted_vbmeta_path = ExtractVBMetaImage(avb_image_path, image_file_name + "-vbmeta.img");
+ }
+
+ // Gets file size of the vbmeta image.
+ int64_t extracted_vbmeta_size;
+ EXPECT_TRUE(base::GetFileSize(extracted_vbmeta_path, &extracted_vbmeta_size));
+
+ // Reads the vbmeta into a vector.
+ std::vector<uint8_t> extracted_vbmeta_content(extracted_vbmeta_size);
+ EXPECT_TRUE(base::ReadFile(extracted_vbmeta_path,
+ reinterpret_cast<char*>(extracted_vbmeta_content.data()),
+ extracted_vbmeta_size));
+
+ // Compares extracted_vbmeta_content with the expected_vbmeta.
+ EXPECT_EQ(expected_vbmeta.size(), extracted_vbmeta_size);
+ return memcmp(reinterpret_cast<void*>(extracted_vbmeta_content.data()),
+ reinterpret_cast<void*>(expected_vbmeta.data()), extracted_vbmeta_size) == 0;
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaDataWithoutFooter) {
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+ // Makes a vbmeta image includeing 'boot' and 'system' chained descriptors.
+ auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+ data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+ {"system", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+ 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"));
+
+ android::base::unique_fd fd(open(vbmeta_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_TRUE(fd > 0);
+
+ VBMetaVerifyResult verify_result;
+ std::unique_ptr<VBMetaData> vbmeta =
+ VerifyVBMetaData(fd, "vbmeta", "" /*expected_public_key_blob */, &verify_result);
+ EXPECT_TRUE(vbmeta != nullptr);
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+
+ // Checkes the returned vbmeta content is the same as that extracted via avbtool.
+ vbmeta->GetVBMetaHeader(true /* update_vbmeta_size */);
+ EXPECT_TRUE(CompareVBMeta(vbmeta_path, *vbmeta));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaDataWithFooter) {
+ const size_t image_size = 10 * 1024 * 1024;
+ const size_t partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", image_size);
+
+ // Appends AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", partition_size, "SHA512_RSA8192", 20,
+ data_dir_.Append("testkey_rsa8192.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ android::base::unique_fd fd(open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_TRUE(fd > 0);
+
+ VBMetaVerifyResult verify_result;
+ std::unique_ptr<VBMetaData> vbmeta =
+ VerifyVBMetaData(fd, "system", "" /*expected_public_key_blob */, &verify_result);
+ EXPECT_TRUE(vbmeta != nullptr);
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+
+ // Checkes the returned vbmeta content is the same as that extracted via avbtool.
+ EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+}
+
+// Modifies a random bit for a file, in the range of [offset, offset + length - 1].
+// Length < 0 means only resets previous modification without introducing new modification.
+void AvbUtilTest::ModifyFile(const base::FilePath& file_path, size_t offset, ssize_t length) {
+ static int last_modified_location = -1;
+ static std::string last_file_path;
+
+ int64_t file_size;
+ ASSERT_TRUE(base::GetFileSize(file_path, &file_size));
+
+ std::vector<uint8_t> file_content(file_size);
+ ASSERT_TRUE(base::ReadFile(file_path, reinterpret_cast<char*>(file_content.data()), file_size));
+
+ // Resets previous modification for consecutive calls on the same file.
+ if (last_file_path == file_path.value()) {
+ file_content[last_modified_location] ^= 0x80;
+ }
+
+ // Introduces a new modification.
+ if (length > 0) {
+ int modify_location = base::RandInt(offset, offset + length - 1);
+ file_content[modify_location] ^= 0x80;
+ last_file_path = file_path.value();
+ last_modified_location = modify_location;
+ }
+
+ ASSERT_EQ(file_size, static_cast<const size_t>(base::WriteFile(
+ file_path, reinterpret_cast<const char*>(file_content.data()),
+ file_content.size())));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaDataError) {
+ const size_t image_size = 10 * 1024 * 1024;
+ const size_t partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", image_size);
+
+ // Appends AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", partition_size, "SHA512_RSA8192", 20,
+ data_dir_.Append("testkey_rsa8192.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ android::base::unique_fd fd(open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_TRUE(fd > 0);
+
+ std::unique_ptr<AvbFooter> footer = GetAvbFooter(fd);
+ EXPECT_TRUE(footer != nullptr);
+
+ VBMetaVerifyResult verify_result;
+ std::unique_ptr<VBMetaData> vbmeta =
+ VerifyVBMetaData(fd, "system", "" /*expected_public_key_blob */, &verify_result);
+ EXPECT_NE(nullptr, vbmeta);
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+
+ // Modifies hash and signature, checks there is verification error.
+ auto header = vbmeta->GetVBMetaHeader(true /* update_vbmeta_size */);
+ size_t header_block_offset = 0;
+ size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);
+
+ // Modifies the hash.
+ ModifyFile(system_path,
+ footer->vbmeta_offset + authentication_block_offset + header->hash_offset,
+ header->hash_size);
+ android::base::unique_fd hash_modified_fd(
+ open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_TRUE(hash_modified_fd > 0);
+ // Should return ErrorVerification.
+ vbmeta = VerifyVBMetaData(hash_modified_fd, "system", "" /*expected_public_key_blob */,
+ &verify_result);
+ EXPECT_NE(nullptr, vbmeta);
+ EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+ EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
+
+ // Modifies the auxiliary data block.
+ size_t auxiliary_block_offset =
+ authentication_block_offset + header->authentication_data_block_size;
+ ModifyFile(system_path, footer->vbmeta_offset + auxiliary_block_offset,
+ header->auxiliary_data_block_size);
+ android::base::unique_fd aux_modified_fd(
+ open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_TRUE(aux_modified_fd > 0);
+ // Should return ErrorVerification.
+ vbmeta = VerifyVBMetaData(aux_modified_fd, "system", "" /*expected_public_key_blob */,
+ &verify_result);
+ EXPECT_NE(nullptr, vbmeta);
+ EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+ EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
+
+ // Resets previous modification by setting offset to -1, and checks the verification can pass.
+ ModifyFile(system_path, 0 /* offset */, -1 /* length */);
+ android::base::unique_fd ok_fd(open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_TRUE(ok_fd > 0);
+ // Should return ResultOK..
+ vbmeta = VerifyVBMetaData(ok_fd, "system", "" /*expected_public_key_blob */, &verify_result);
+ EXPECT_NE(nullptr, vbmeta);
+ EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+}
+
+TEST_F(AvbUtilTest, GetChainPartitionInfo) {
+ // 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,
+ data_dir_.Append("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,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+ // Makes a vbmeta_system.img including the 'system' chained descriptor.
+ GenerateVBMetaImage("vbmeta_system.img", "SHA256_RSA4096", 0,
+ data_dir_.Append("testkey_rsa4096.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+ "--internal_release_string \"unit test\"");
+
+ // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+ GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0, data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+ {"vbmeta_system", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+
+ // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+ EXPECT_EQ("6f4bf815a651aa35ec7102a88b7906b91aef284bc5e20d0bf527c7d460da3266",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+ // Loads the key blobs for comparison.
+ std::string expected_key_blob_2048;
+ EXPECT_TRUE(base::ReadFileToString(rsa2048_public_key, &expected_key_blob_2048));
+ std::string expected_key_blob_4096;
+ EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));
+
+ // Checks chain descriptors in vbmeta.img
+ 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: vbmeta_system\n"
+ " Rollback Index Location: 2\n"
+ " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n",
+ InfoImage("vbmeta.img"));
+
+ bool fatal_error = false;
+ auto chained_descriptors = GetChainPartitionInfo(LoadVBMetaData("vbmeta.img"), &fatal_error);
+ EXPECT_EQ(2, chained_descriptors.size()); // contains 'boot' and 'vbmeta_system'.
+ EXPECT_EQ(false, fatal_error);
+
+ EXPECT_EQ("boot", chained_descriptors[0].partition_name);
+ EXPECT_EQ(expected_key_blob_2048, chained_descriptors[0].public_key_blob);
+
+ EXPECT_EQ("vbmeta_system", chained_descriptors[1].partition_name);
+ EXPECT_EQ(expected_key_blob_4096, chained_descriptors[1].public_key_blob);
+
+ // Checks chain descriptors in vbmeta_system.img
+ EXPECT_EQ(
+ "Minimum libavb version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 576 bytes\n"
+ "Auxiliary Block: 2176 bytes\n"
+ "Algorithm: SHA256_RSA4096\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Release String: 'unit test'\n"
+ "Descriptors:\n"
+ " Chain Partition descriptor:\n"
+ " Partition Name: system\n"
+ " Rollback Index Location: 3\n"
+ " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n",
+ InfoImage("vbmeta_system.img"));
+
+ chained_descriptors = GetChainPartitionInfo(LoadVBMetaData("vbmeta_system.img"), &fatal_error);
+ EXPECT_EQ(1, chained_descriptors.size()); // contains 'system' only.
+ EXPECT_EQ(false, fatal_error);
+ EXPECT_EQ("system", chained_descriptors[0].partition_name);
+ EXPECT_EQ(expected_key_blob_4096, chained_descriptors[0].public_key_blob);
+}
+
+TEST_F(AvbUtilTest, GetChainPartitionInfoNone) {
+ // 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,
+ data_dir_.Append("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,
+ data_dir_.Append("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, data_dir_.Append("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"));
+
+ // Checks none of chain descriptors is found.
+ bool fatal_error = false;
+ auto chained_descriptors = GetChainPartitionInfo(LoadVBMetaData("vbmeta.img"), &fatal_error);
+ EXPECT_EQ(0, chained_descriptors.size()); // There is no chain descriptors.
+ EXPECT_EQ(false, fatal_error);
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImpl) {
+ // 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,
+ data_dir_.Append("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,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+ // Makes a vbmeta_system.img including the 'system' chained descriptor.
+ auto vbmeta_system_path = GenerateVBMetaImage(
+ "vbmeta_system.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+ "--internal_release_string \"unit test\"");
+
+ // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+ auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+ data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+ {"vbmeta_system", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+
+ // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+ EXPECT_EQ("6f4bf815a651aa35ec7102a88b7906b91aef284bc5e20d0bf527c7d460da3266",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+ // Starts to test LoadAndVerifyVbmetaImpl.
+ std::vector<VBMetaData> vbmeta_images;
+ auto vbmeta_image_path = [this](const std::string& partition_name) {
+ return test_dir_.Append(partition_name + ".img").value();
+ };
+
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+ LoadAndVerifyVbmetaImpl(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */,
+ vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+
+ EXPECT_EQ(4UL, vbmeta_images.size()); // vbmeta, boot, vbmeta_system and system
+ // Binary comparison for each vbmeta image.
+ EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+ EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+ EXPECT_TRUE(CompareVBMeta(vbmeta_system_path, vbmeta_images[2]));
+ EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[3]));
+
+ // Skip loading chained vbmeta images.
+ vbmeta_images.clear();
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+ LoadAndVerifyVbmetaImpl(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+ false /* load_chained_vbmeta */, true /* rollback_protection */,
+ vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+ // Only vbmeta is loaded.
+ EXPECT_EQ(1UL, vbmeta_images.size());
+ EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImplWithSuffixes) {
+ // Tests the following chained partitions.
+ // vbmeta_a.img
+ // |--> boot_b.img (boot_other)
+ // |--> vbmeta_system_b.img (vbmeta_system_other)
+ // |--> system_a.img
+
+ // Generates a raw boot_b.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_b.img", boot_image_size);
+
+ // Adds AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+ data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates a raw system_a.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_a.img", system_image_size);
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+ // Makes a vbmeta_system_b.img including the 'system' chained descriptor.
+ auto vbmeta_system_path = GenerateVBMetaImage(
+ "vbmeta_system_b.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+ "--internal_release_string \"unit test\"");
+
+ // Makes a vbmeta_a.img includeing 'boot_other' and 'vbmeta_system_other' chained descriptors.
+ auto vbmeta_path = GenerateVBMetaImage(
+ "vbmeta_a.img", "SHA256_RSA8192", 0, data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot_other", 1, rsa2048_public_key}, /* chain_partitions */
+ {"vbmeta_system_other", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+
+ // Starts to test LoadAndVerifyVbmetaImpl with ab_suffix and ab_other_suffix.
+ auto vbmeta_image_path = [this](const std::string& partition_name) {
+ return test_dir_.Append(partition_name + ".img").value();
+ };
+
+ std::vector<VBMetaData> vbmeta_images;
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+ LoadAndVerifyVbmetaImpl(
+ "vbmeta" /* partition_name */, "_a" /* ab_suffix */, "_b" /* other_suffix */,
+ "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */,
+ vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+
+ EXPECT_EQ(4UL, vbmeta_images.size()); // vbmeta, boot_other, vbmeta_system_other and system
+ // Binary comparison for each vbmeta image.
+ EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+ EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+ EXPECT_TRUE(CompareVBMeta(vbmeta_system_path, vbmeta_images[2]));
+ EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[3]));
+
+ // Skips loading chained vbmeta images.
+ vbmeta_images.clear();
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+ LoadAndVerifyVbmetaImpl(
+ "vbmeta" /* partition_name */, "_a" /* ab_suffix */, "_b" /* other_suffix */,
+ "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+ false /* load_chained_vbmeta */, true /* rollback_protection */,
+ vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+ // Only vbmeta is loaded.
+ EXPECT_EQ(1UL, vbmeta_images.size());
+ EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+
+ // Using an invalid suffix for 'other' slot, checks it returns error.
+ EXPECT_EQ(VBMetaVerifyResult::kError,
+ LoadAndVerifyVbmetaImpl(
+ "vbmeta" /* partition_name */, "_a" /* ab_suffix */,
+ "_invalid_suffix" /* other_suffix */, "" /* expected_public_key_blob*/,
+ false /* allow_verification_error */, true /* load_chained_vbmeta */,
+ true /* rollback_protection */, vbmeta_image_path,
+ false /* is_chained_vbmeta*/, &vbmeta_images));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImplErrorVerification) {
+ // 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,
+ data_dir_.Append("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,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+ // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+ auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+ data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+ {"system", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+
+ // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+ EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+ auto vbmeta = LoadVBMetaData("vbmeta.img");
+
+ // Modifies hash, checks there is error if allow_verification_error is false.
+ auto header = vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);
+ size_t header_block_offset = 0;
+ size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);
+
+ // Modifies the hash.
+ ModifyFile(vbmeta_path, authentication_block_offset + header->hash_offset, header->hash_size);
+
+ // Starts to test LoadAndVerifyVbmetaImpl.
+ std::vector<VBMetaData> vbmeta_images;
+ auto vbmeta_image_path = [this](const std::string& partition_name) {
+ return test_dir_.Append(partition_name + ".img").value();
+ };
+ EXPECT_EQ(VBMetaVerifyResult::kError,
+ LoadAndVerifyVbmetaImpl(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */,
+ vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+ // Stops to load vbmeta because the top-level vbmeta has verification error.
+ EXPECT_EQ(0UL, vbmeta_images.size());
+
+ // Tries again with verification error allowed.
+ EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+ LoadAndVerifyVbmetaImpl(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "", /* other_suffix */
+ "" /* expected_public_key_blob*/, true /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */,
+ vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+
+ EXPECT_EQ(3UL, vbmeta_images.size()); // vbmeta, boot, and system
+ // Binary comparison for each vbmeta image.
+ EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+ EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+ EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[2]));
+
+ // Resets the modification of the hash.
+ ModifyFile(vbmeta_path, 0 /* offset */, -1 /* length */);
+
+ // Modifies the auxiliary data of system.img
+ auto fd = OpenUniqueReadFd(system_path);
+ auto system_footer = GetAvbFooter(fd);
+ auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, "system-vbmeta.img");
+ auto system_header = system_vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);
+ size_t auxiliary_block_offset =
+ authentication_block_offset + system_header->authentication_data_block_size;
+
+ // Modifies the auxiliary data block.
+ ModifyFile(system_path, system_footer->vbmeta_offset + auxiliary_block_offset,
+ system_header->auxiliary_data_block_size);
+ vbmeta_images.clear();
+ EXPECT_EQ(VBMetaVerifyResult::kError,
+ LoadAndVerifyVbmetaImpl(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */,
+ vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+ // 'vbmeta', 'boot' but no 'system', because of verification error.
+ EXPECT_EQ(2UL, vbmeta_images.size());
+ // Binary comparison for the loaded 'vbmeta' and 'boot'.
+ EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+ EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+
+ // Resets the modification of the auxiliary data.
+ ModifyFile(vbmeta_path, 0 /* offset */, -1 /* length */);
+
+ // Sets the vbmeta header flags on a chained partition, which introduces an error.
+ ModifyFile(system_path, system_footer->vbmeta_offset + offsetof(AvbVBMetaImageHeader, flags),
+ sizeof(uint32_t));
+ EXPECT_EQ(VBMetaVerifyResult::kError,
+ LoadAndVerifyVbmetaImpl(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, true /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */,
+ vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImplUnexpectedPublicKey) {
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+ base::FilePath rsa8192_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa8192.pem"));
+
+ // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+ auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+ data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+ {"system", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+ std::string expected_key_blob_4096;
+ EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));
+ std::string expected_key_blob_8192;
+ EXPECT_TRUE(base::ReadFileToString(rsa8192_public_key, &expected_key_blob_8192));
+
+ auto vbmeta_image_path = [this](const std::string& partition_name) {
+ return test_dir_.Append(partition_name + ".img").value();
+ };
+ std::vector<VBMetaData> vbmeta_images;
+ // Uses the correct expected public key.
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+ LoadAndVerifyVbmetaImpl(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ expected_key_blob_8192, true /* allow_verification_error */,
+ false /* load_chained_vbmeta */, true /* rollback_protection */,
+ vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+
+ // Uses the wrong expected public key with allow_verification_error set to true.
+ vbmeta_images.clear();
+ EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+ LoadAndVerifyVbmetaImpl(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ expected_key_blob_4096, true /* allow_verification_error */,
+ false /* load_chained_vbmeta */, true /* rollback_protection */,
+ vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+
+ // Uses the wrong expected public key with allow_verification_error set to false.
+ vbmeta_images.clear();
+ EXPECT_EQ(VBMetaVerifyResult::kError,
+ LoadAndVerifyVbmetaImpl(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ expected_key_blob_4096, false /* allow_verification_error */,
+ false /* load_chained_vbmeta */, true /* rollback_protection */,
+ vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+}
+
+} // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_test.cpp b/fs_mgr/libfs_avb/tests/fs_avb_test.cpp
new file mode 100644
index 0000000..2c819a9
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_test.cpp
@@ -0,0 +1,311 @@
+/*
+ * 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 <endian.h>
+#include <stdlib.h>
+
+#include <android-base/file.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <fs_avb/fs_avb.h>
+#include <libavb/libavb.h>
+
+#include "fs_avb_test_util.h"
+
+// Target classes or functions to test:
+using android::fs_mgr::AvbHandle;
+using android::fs_mgr::AvbHandleStatus;
+using android::fs_mgr::HashAlgorithm;
+
+namespace fs_avb_host_test {
+
+class PublicFsAvbTest : public BaseFsAvbTest {
+ public:
+ PublicFsAvbTest(){};
+
+ protected:
+ ~PublicFsAvbTest(){};
+ // Modifies |flags| field in the vbmeta header in an Avb image.
+ // e.g., AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED.
+ void ModifyVBMetaHeaderFlags(const base::FilePath& vbmeta_image_path, uint32_t flags);
+};
+
+void PublicFsAvbTest::ModifyVBMetaHeaderFlags(const base::FilePath& vbmeta_image_path,
+ uint32_t flags) {
+ if (!base::PathExists(vbmeta_image_path)) return;
+
+ // Only support modifying the flags in vbmeta*.img.
+ std::string image_file_name = vbmeta_image_path.RemoveExtension().BaseName().value();
+ ASSERT_TRUE(base::StartsWith(image_file_name, "vbmeta", base::CompareCase::INSENSITIVE_ASCII));
+
+ android::base::unique_fd fd(open(vbmeta_image_path.value().c_str(), O_RDWR | O_CLOEXEC));
+ EXPECT_TRUE(fd > 0);
+
+ auto flags_offset = offsetof(AvbVBMetaImageHeader, flags);
+ uint32_t flags_data = htobe32(flags);
+ EXPECT_EQ(flags_offset, lseek64(fd, flags_offset, SEEK_SET));
+ EXPECT_EQ(sizeof flags_data, write(fd, &flags_data, sizeof flags_data));
+}
+
+TEST_F(PublicFsAvbTest, LoadAndVerifyVbmeta) {
+ // 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,
+ data_dir_.Append("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,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+ // Makes a vbmeta image includeing 'boot' and 'system' chained descriptors.
+ auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+ data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+ {"system", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+
+ // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+ EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+ // Invokes the public API from fs_avb.h.
+ auto vbmeta_image_path = [this](const std::string& partition_name) {
+ return test_dir_.Append(partition_name + ".img").value();
+ };
+ auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+ false /* allow_verification_error */, true /* load_chained_vbmeta */,
+ true /* rollback_protection */, vbmeta_image_path);
+ EXPECT_NE(nullptr, avb_handle);
+ EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+
+ // Checks the summary info for all vbmeta images.
+ // Checks the digest matches the value calculated by CalcVBMetaDigest().
+ EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+ avb_handle->vbmeta_info().digest);
+ EXPECT_EQ(8576UL, avb_handle->vbmeta_info().total_size);
+ EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);
+
+ // Skip loading chained vbmeta.
+ avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+ false /* allow_verification_error */, false /* load_chained_vbmeta */,
+ true /* rollback_protection */, vbmeta_image_path);
+ EXPECT_NE(nullptr, avb_handle);
+ EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+ EXPECT_EQ("5c31197992b3c72a854ec7dc0eb9609ffebcffab7917ffd381a99ecee328f09c",
+ avb_handle->vbmeta_info().digest);
+ EXPECT_EQ(5184UL, avb_handle->vbmeta_info().total_size);
+ EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);
+}
+
+TEST_F(PublicFsAvbTest, LoadAndVerifyVbmetaWithModifications) {
+ // 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,
+ data_dir_.Append("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,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+ // Makes a vbmeta image includeing 'boot' and 'system' chained descriptors.
+ auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+ data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+ {"system", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+
+ // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+ EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+ // Sets AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED in the vbmeta.img.
+ ModifyVBMetaHeaderFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+
+ auto vbmeta_image_path = [this](const std::string& partition_name) {
+ return test_dir_.Append(partition_name + ".img").value();
+ };
+ auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+ false /* allow_verification_error */, true /* load_chained_vbmeta */,
+ true /* rollback_protection */, vbmeta_image_path);
+ // Returns a null handler because allow_verification is not True.
+ EXPECT_EQ(nullptr, avb_handle);
+
+ // Try again with allow_verification_error set to true.
+ avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+ true /* allow_verification_error */, true /* load_chained_vbmeta */,
+ true /* rollback_protection */, vbmeta_image_path);
+ EXPECT_NE(nullptr, avb_handle);
+ EXPECT_EQ(AvbHandleStatus::kHashtreeDisabled, avb_handle->status());
+
+ // Checks the summary info for all vbmeta images.
+ // Checks the digest matches the value calculated by CalcVBMetaDigest().
+ EXPECT_EQ("ae8f7ad95cbb7ce4f0feeeedc2a0a39824af5cd29dad4d028597cab4b8c2e83c",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+ EXPECT_EQ("ae8f7ad95cbb7ce4f0feeeedc2a0a39824af5cd29dad4d028597cab4b8c2e83c",
+ avb_handle->vbmeta_info().digest);
+ EXPECT_EQ(8576UL, avb_handle->vbmeta_info().total_size);
+ EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);
+
+ // Sets AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED in the vbmeta.img.
+ ModifyVBMetaHeaderFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+ // Loads the vbmeta with allow_verification_error set to true.
+ avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+ true /* allow_verification_error */, true /* load_chained_vbmeta */,
+ true /* rollback_protection */, vbmeta_image_path);
+ EXPECT_NE(nullptr, avb_handle);
+ EXPECT_EQ(AvbHandleStatus::kVerificationDisabled, avb_handle->status());
+ // Only the top-level vbmeta.img is loaded, when VERIFICATION_DISABLED is set.
+ // However, CalcVBMetaDigest() reads all vbmeta structs to calculate the digest,
+ // including vbmeta.img, boot.img and syste.img. So we don't compare the digest here.
+ EXPECT_EQ(5184UL, avb_handle->vbmeta_info().total_size);
+
+ // Sets a unknown flag in the vbmeta.imgm and expects to get
+ // AvbHandleStatus::kVerificationError.
+ ModifyVBMetaHeaderFlags(vbmeta_path, 0x10000000);
+ // Loads the vbmeta with allow_verification_error set to true.
+ avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+ true /* allow_verification_error */, true /* load_chained_vbmeta */,
+ true /* rollback_protection */, vbmeta_image_path);
+ EXPECT_NE(nullptr, avb_handle);
+ EXPECT_EQ(AvbHandleStatus::kVerificationError, avb_handle->status());
+ // Checks the digest matches the value calculated by CalcVBMetaDigest().
+ EXPECT_EQ("8fb99c4f54500053c3582df5eaf04e9a533137398879188aad9968ec19a664f1",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+ EXPECT_EQ("8fb99c4f54500053c3582df5eaf04e9a533137398879188aad9968ec19a664f1",
+ avb_handle->vbmeta_info().digest);
+ EXPECT_EQ(8576UL, avb_handle->vbmeta_info().total_size);
+ EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);
+}
+
+TEST_F(PublicFsAvbTest, LoadAndVerifyVbmetaWithPublicKeys) {
+ // 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,
+ data_dir_.Append("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,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+ base::FilePath rsa8192_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa8192.pem"));
+
+ // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+ auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+ data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+ {"system", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+
+ auto vbmeta_image_path = [this](const std::string& partition_name) {
+ return test_dir_.Append(partition_name + ".img").value();
+ };
+ std::vector<VBMetaData> vbmeta_images;
+ // Uses the correct expected public key.
+ auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ rsa8192_public_key.value(), HashAlgorithm::kSHA256, true /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path);
+ EXPECT_NE(nullptr, avb_handle);
+ EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+
+ // Uses a non-existed public key.
+ avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "/path/to/non-existed/key", HashAlgorithm::kSHA256, true /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path);
+ EXPECT_EQ(nullptr, avb_handle);
+
+ // Uses an incorrect public key, with allow_verification_error false.
+ avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ rsa4096_public_key.value(), HashAlgorithm::kSHA256,
+ false /* allow_verification_error */, true /* load_chained_vbmeta */,
+ true /* rollback_protection */, vbmeta_image_path);
+ EXPECT_EQ(nullptr, avb_handle);
+
+ // Uses an incorrect public key, with allow_verification_error true.
+ avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ rsa4096_public_key.value(), HashAlgorithm::kSHA256, true /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path);
+ EXPECT_NE(nullptr, avb_handle);
+ EXPECT_EQ(AvbHandleStatus::kVerificationError, avb_handle->status());
+}
+
+} // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp b/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp
index 95b17d8..17f4c4e 100644
--- a/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp
+++ b/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp
@@ -69,7 +69,7 @@
return trimmed_digest_data;
}
-void BaseFsAvbTest::GenerateVBMetaImage(
+base::FilePath 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,
@@ -107,17 +107,19 @@
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));
+ EXPECT_TRUE(base::GetFileSize(vbmeta_image.path, &file_size));
vbmeta_image.content.resize(file_size);
- ASSERT_TRUE(base::ReadFile(vbmeta_image.path,
+ EXPECT_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));
+
+ return vbmeta_images_[file_name].path; // returns the path.
}
-void BaseFsAvbTest::ExtractVBMetaImage(const base::FilePath& image_path,
- const std::string& output_file_name,
- const size_t padding_size) {
+base::FilePath 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,
@@ -127,12 +129,15 @@
" --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));
+ EXPECT_TRUE(base::GetFileSize(vbmeta_image.path, &file_size));
vbmeta_image.content.resize(file_size);
- ASSERT_TRUE(base::ReadFile(vbmeta_image.path,
+ EXPECT_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));
+
+ // Returns the output file path.
+ return vbmeta_images_[output_file_name].path;
}
// Generates a file with name |file_name| of size |image_size| with
@@ -179,6 +184,49 @@
additional_options.c_str());
}
+VBMetaData BaseFsAvbTest::GenerateImageAndExtractVBMetaData(
+ const std::string& partition_name, const size_t image_size, const size_t partition_size,
+ const std::string& footer_type, const base::FilePath& avb_signing_key,
+ const std::string& avb_algorithm, const uint64_t rollback_index) {
+ // Generates a raw image first
+ base::FilePath image_path = GenerateImage(partition_name + ".img", image_size);
+
+ // Appends AVB Hashtree Footer.
+ AddAvbFooter(image_path, footer_type, partition_name, partition_size, avb_algorithm,
+ rollback_index, avb_signing_key, "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Extracts vbmeta from the ram image into another *-vbmeta.img.
+ auto vbmeta_image = ExtractVBMetaImage(image_path, partition_name + "-vbmeta.img");
+
+ // Loads *-vbmeta.img into a VBMetaData.
+ std::string vbmeta_buffer;
+ EXPECT_TRUE(base::ReadFileToString(vbmeta_image, &vbmeta_buffer));
+
+ return {(const uint8_t*)vbmeta_buffer.data(), vbmeta_buffer.size(), partition_name};
+}
+
+VBMetaData BaseFsAvbTest::LoadVBMetaData(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;
+
+ // Loads the vbmeta_image into a VBMetaData.
+ std::string vbmeta_buffer;
+ EXPECT_TRUE(base::ReadFileToString(image_path, &vbmeta_buffer));
+
+ std::string partition_name = image_path.RemoveExtension().BaseName().value();
+ return {(const uint8_t*)vbmeta_buffer.data(), vbmeta_buffer.size(), partition_name};
+}
+
+VBMetaData BaseFsAvbTest::ExtractAndLoadVBMetaData(const base::FilePath& image_path,
+ const std::string& output_file_name) {
+ ExtractVBMetaImage(image_path, output_file_name);
+ return LoadVBMetaData(output_file_name);
+}
+
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(),
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_test_util.h b/fs_mgr/libfs_avb/tests/fs_avb_test_util.h
index f80dc5f..2e46644 100644
--- a/fs_mgr/libfs_avb/tests/fs_avb_test_util.h
+++ b/fs_mgr/libfs_avb/tests/fs_avb_test_util.h
@@ -16,14 +16,20 @@
#pragma once
+#include <fcntl.h>
#include <inttypes.h>
#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <sys/wait.h>
+
#include <string>
#include <vector>
+#include <android-base/unique_fd.h>
#include <base/files/file_path.h>
#include <base/strings/stringprintf.h>
+#include <fs_avb/fs_avb.h>
#include <gtest/gtest.h>
// Utility macro to run the command expressed by the printf()-style string
@@ -36,6 +42,8 @@
EXPECT_EQ(WEXITSTATUS(rc), expected_exit_status); \
} while (0);
+using android::fs_mgr::VBMetaData;
+
namespace fs_avb_host_test {
struct VBMetaImage {
@@ -51,6 +59,10 @@
base::FilePath key_blob_path;
};
+inline android::base::unique_fd OpenUniqueReadFd(const base::FilePath& file_path) {
+ return android::base::unique_fd(open(file_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+}
+
/* Base-class used for unit test. */
class BaseFsAvbTest : public ::testing::Test {
public:
@@ -66,16 +78,18 @@
// Generates a vbmeta image with |file_name| by avbtool.
// The generated vbmeta image will be written to disk, see the
// |vbmeta_images_| variable for its path and the content.
- void 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 = "");
+ base::FilePath 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 = "");
// Similar to above, but extracts a vbmeta image from the given image_path.
// The extracted vbmeta image will be written to disk, with |output_file_name|.
// See the |vbmeta_images_| variable for its path and the content.
- void ExtractVBMetaImage(const base::FilePath& image_path, const std::string& output_file_name,
- const size_t padding_size = 0);
+ base::FilePath ExtractVBMetaImage(const base::FilePath& image_path,
+ const std::string& output_file_name,
+ const size_t padding_size = 0);
// Generate a file with name |file_name| of size |image_size| with
// known content (0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
@@ -86,9 +100,19 @@
void 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 = "d00df00d",
+ const base::FilePath& avb_signing_key, const std::string& salt = "d00df00d",
const std::string& additional_options = "");
+ VBMetaData GenerateImageAndExtractVBMetaData(
+ const std::string& partition_name, const size_t image_size, const size_t partition_size,
+ const std::string& footer_type, const base::FilePath& avb_signing_key,
+ const std::string& avb_algorithm, const uint64_t rollback_index);
+
+ VBMetaData ExtractAndLoadVBMetaData(const base::FilePath& image_path,
+ const std::string& output_file_name);
+
+ VBMetaData LoadVBMetaData(const std::string& file_name);
+
// Returns the output of 'avbtool info_image' for the |image_path|.
std::string InfoImage(const base::FilePath& image_path);
// Same as above, but for an internal vbmeta image with |file_name| in |vbmeta_images_|.
diff --git a/fs_mgr/libfs_avb/tests/util_test.cpp b/fs_mgr/libfs_avb/tests/util_test.cpp
index 835e8fd..9e37d22 100644
--- a/fs_mgr/libfs_avb/tests/util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/util_test.cpp
@@ -15,6 +15,7 @@
*/
#include <unistd.h>
+
#include <future>
#include <string>
#include <thread>
diff --git a/fs_mgr/libfs_avb/util.cpp b/fs_mgr/libfs_avb/util.cpp
index 17d47d9..9d4f05f 100644
--- a/fs_mgr/libfs_avb/util.cpp
+++ b/fs_mgr/libfs_avb/util.cpp
@@ -17,6 +17,7 @@
#include "util.h"
#include <sys/ioctl.h>
+
#include <thread>
#include <android-base/unique_fd.h>