diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp index 3cb718ce1..41cd7dd9c 100644 --- a/fs_mgr/fs_mgr_slotselect.cpp +++ b/fs_mgr/fs_mgr_slotselect.cpp @@ -34,6 +34,12 @@ static std::string other_suffix(const std::string& slot_suffix) { 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 4a0594955..38f96c01f 100644 --- a/fs_mgr/include_fstab/fstab/fstab.h +++ b/fs_mgr/include_fstab/fstab/fstab.h @@ -98,6 +98,7 @@ int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab); int fs_mgr_is_fs_verity(const struct fstab_rec* fstab); std::string fs_mgr_get_slot_suffix(); +std::string fs_mgr_get_other_slot_suffix(); std::set fs_mgr_get_boot_devices(); struct FstabEntry { diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp index 6f368e4e2..191e8036f 100644 --- a/fs_mgr/libfs_avb/Android.bp +++ b/fs_mgr/libfs_avb/Android.bp @@ -56,6 +56,11 @@ cc_defaults { "tests/data/*", ], static_libs: [ + "libavb", + "libavb_host_sysdeps", + "libdm", + "libfs_avb", + "libfstab", "libgtest_host", ], shared_libs: [ @@ -86,8 +91,12 @@ cc_test_host { static_libs: [ "libfs_avb_test_util", ], + shared_libs: [ + "libcrypto", + ], srcs: [ "tests/basic_test.cpp", + "tests/fs_avb_test.cpp", ], } @@ -96,10 +105,11 @@ cc_test_host { 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 3efa79444..6a3e2c026 100644 --- a/fs_mgr/libfs_avb/avb_ops.cpp +++ b/fs_mgr/libfs_avb/avb_ops.cpp @@ -29,6 +29,7 @@ #include #include #include + #include #include @@ -190,7 +191,8 @@ AvbSlotVerifyResult FsManagerAvbOps::AvbSlotVerify(const std::string& ab_suffix, // 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 0ceb6ee11..f57c9d6eb 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 + #include #include #include +#include #include #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(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 VBMetaData::GetVBMetaHeader(bool update_vbmeta_size) { + auto vbmeta_header(std::make_unique()); + + 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 @@ bool GetHashtreeDescriptor(const std::string& partition_name, 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 GetAvbFooter(int fd) { + std::array footer_buf; + auto footer(std::make_unique()); + + 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 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 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(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(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 GetChainPartitionInfo(const VBMetaData& vbmeta, bool* fatal_error) { + CHECK(fatal_error != nullptr); + std::vector chain_partitions; + + size_t num_descriptors; + std::unique_ptr 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 device_path_constructor, + bool is_chained_vbmeta, std::vector* 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 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 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 b81e9316d..babbfef70 100644 --- a/fs_mgr/libfs_avb/avb_util.h +++ b/fs_mgr/libfs_avb/avb_util.h @@ -16,9 +16,11 @@ #pragma once +#include #include #include +#include #include #include @@ -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& vbmeta_images, @@ -41,5 +59,37 @@ bool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const AvbHashtreeDescriptor& 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 GetAvbFooter(int fd); + +std::unique_ptr 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 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 device_path_constructor, + bool is_chained_vbmeta, std::vector* 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 957aa871c..9f8ad5325 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 @@ std::pair VerifyVbmetaDigest(const std::vector& vbmeta return std::make_pair(total_size, matched); } +template +std::pair CalculateVbmetaDigest(const std::vector& 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(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 @@ class AvbVerifier { 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 @@ std::unique_ptr AvbVerifier::Create() { 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 @@ bool AvbVerifier::VerifyVbmetaImages(const std::vector& vbmeta_image 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(vbmeta_images, digest_); - } else if (hash_alg_ == kSHA512) { + } else if (hash_alg_ == HashAlgorithm::kSHA512) { std::tie(total_size, digest_matched) = VerifyVbmetaDigest(vbmeta_images, digest_); } @@ -162,6 +202,98 @@ bool AvbVerifier::VerifyVbmetaImages(const std::vector& vbmeta_image 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 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 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(avb_handle->vbmeta_images_); + } else if (hash_algorithm == HashAlgorithm::kSHA512) { + std::tie(digest, total_size) = + CalculateVbmetaDigest(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 @@ AvbUniquePtr AvbHandle::Open() { // 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 @@ AvbUniquePtr AvbHandle::Open() { 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 avb_verifier = AvbVerifier::Create(); @@ -237,7 +369,7 @@ AvbUniquePtr AvbHandle::Open() { 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 @@ AvbUniquePtr AvbHandle::Open() { } 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 eca69845a..7af3c7e95 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 #include #include #include @@ -32,23 +33,56 @@ enum class AvbHashtreeResult { 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 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 @@ class VBMetaData { static const size_t kMaxVBMetaSize = 64 * 1024; private: - std::string device_path_; std::unique_ptr vbmeta_ptr_; size_t vbmeta_size_; + std::string partition_name_; }; class FsManagerAvbOps; @@ -71,7 +105,7 @@ using AvbUniquePtr = std::unique_ptr; // 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 @@ class AvbHandle { // - 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 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 @@ class AvbHandle { 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 @@ class AvbHandle { 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 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 000000000..26b32948c --- /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 +#include +#include +#include + +#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(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 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 extracted_vbmeta_content(extracted_vbmeta_size); + EXPECT_TRUE(base::ReadFile(extracted_vbmeta_path, + reinterpret_cast(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(extracted_vbmeta_content.data()), + reinterpret_cast(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 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 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 file_content(file_size); + ASSERT_TRUE(base::ReadFile(file_path, reinterpret_cast(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(base::WriteFile( + file_path, reinterpret_cast(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 footer = GetAvbFooter(fd); + EXPECT_TRUE(footer != nullptr); + + VBMetaVerifyResult verify_result; + std::unique_ptr 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 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 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 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 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 000000000..2c819a929 --- /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 +#include + +#include +#include +#include +#include +#include + +#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 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 95b17d8ed..17f4c4e11 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 @@ std::string BaseFsAvbTest::CalcVBMetaDigest(const std::string& file_name, 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& include_descriptor_image_paths, @@ -107,17 +107,19 @@ void BaseFsAvbTest::GenerateVBMetaImage( 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(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 @@ void BaseFsAvbTest::ExtractVBMetaImage(const base::FilePath& image_path, " --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(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 @@ void BaseFsAvbTest::AddAvbFooter(const base::FilePath& image_path, const std::st 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 f80dc5f88..2e466440d 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 #include #include +#include +#include #include + #include #include +#include #include #include +#include #include // 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 @@ struct ChainPartitionConfig { 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 @@ class BaseFsAvbTest : public ::testing::Test { // 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& include_descriptor_image_paths, - const std::vector& 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& include_descriptor_image_paths, + const std::vector& 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 @@ class BaseFsAvbTest : public ::testing::Test { 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 835e8fd3e..9e37d2261 100644 --- a/fs_mgr/libfs_avb/tests/util_test.cpp +++ b/fs_mgr/libfs_avb/tests/util_test.cpp @@ -15,6 +15,7 @@ */ #include + #include #include #include diff --git a/fs_mgr/libfs_avb/util.cpp b/fs_mgr/libfs_avb/util.cpp index 17d47d9cd..9d4f05f57 100644 --- a/fs_mgr/libfs_avb/util.cpp +++ b/fs_mgr/libfs_avb/util.cpp @@ -17,6 +17,7 @@ #include "util.h" #include + #include #include