diff options
author | Bowgo Tsai <bowgotsai@google.com> | 2019-01-25 12:07:09 +0800 |
---|---|---|
committer | Bowgo Tsai <bowgotsai@google.com> | 2019-01-29 22:42:24 +0800 |
commit | defe1cb5e78ddbf258b431c2b25192c1555c2d64 (patch) | |
tree | a7f1c64c3a149c4f0781b03c7979a56249281128 | |
parent | 47a4082f88a7b4a6db3c893ca5824cda85db7c6f (diff) | |
download | system_core-defe1cb5e78ddbf258b431c2b25192c1555c2d64.tar.gz system_core-defe1cb5e78ddbf258b431c2b25192c1555c2d64.tar.bz2 system_core-defe1cb5e78ddbf258b431c2b25192c1555c2d64.zip |
libfs_avb: support enable verity for a standalone partition
The following static function has been added into class AvbHandle to
support loading the AVB hashtree descriptor to enable dm-verity for
a FstabEntry.
static AvbHashtreeResult SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry);
Bug: 112103720
Bug: 117960205
Test: atest libfs_avb_test
Test: atest libfs_avb_internal_test
Test: Add /system/etc/system_other.avbpubkey, then add
avb_key=/system/etc/system_other.avbpubkey into /system/etc/fstab.postinstall.
factory reset, boot a device and checks that system_other is
mounted with verity (see the serial log below)
Serial log:
-----------
init: [libfs_avb]Built verity table: '1 /dev/block/by-name/system_a
/dev/block/by-name/system_a 4096 4096 8521
8521 sha1 895ba03023a35172b393429fadad9ee228b39203
3405d16fec2cd12ad9e6b36d3bc983e1e83b5e09 10 use_fec_from_device
/dev/block/by-name/system_a fec_roots 2 fec_blocks 8589 fec_start 8589
restart_on_corruption ignore_zero_blocks'
init: [libfs_mgr]superblock s_max_mnt_count:65535,/dev/block/dm-7
init: [libfs_mgr]__mount(source=/dev/block/dm-7,target=/postinstall,type=ext4)=0:
Success
Change-Id: Ie339a43ff9c6a7d170e12ef466df666b98ddec19
-rw-r--r-- | fs_mgr/fs_mgr.cpp | 14 | ||||
-rw-r--r-- | fs_mgr/fs_mgr_fstab.cpp | 2 | ||||
-rw-r--r-- | fs_mgr/include_fstab/fstab/fstab.h | 1 | ||||
-rw-r--r-- | fs_mgr/libfs_avb/avb_util.cpp | 187 | ||||
-rw-r--r-- | fs_mgr/libfs_avb/avb_util.h | 40 | ||||
-rw-r--r-- | fs_mgr/libfs_avb/fs_avb.cpp | 91 | ||||
-rw-r--r-- | fs_mgr/libfs_avb/include/fs_avb/fs_avb.h | 11 | ||||
-rw-r--r-- | fs_mgr/libfs_avb/tests/avb_util_test.cpp | 527 |
8 files changed, 712 insertions, 161 deletions
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp index 26ce3b255..878dbb338 100644 --- a/fs_mgr/fs_mgr.cpp +++ b/fs_mgr/fs_mgr.cpp @@ -1090,6 +1090,13 @@ int fs_mgr_mount_all(Fstab* fstab, int mount_mode) { // Skips mounting the device. continue; } + } else if (!current_entry.avb_key.empty()) { + if (AvbHandle::SetUpStandaloneAvbHashtree(¤t_entry) == AvbHashtreeResult::kFail) { + LERROR << "Failed to set up AVB on standalone partition: " + << current_entry.mount_point << ", skipping!"; + // Skips mounting the device. + continue; + } } else if ((current_entry.fs_mgr_flags.verify)) { int rc = fs_mgr_setup_verity(¤t_entry, true); if (__android_log_is_debuggable() && @@ -1326,6 +1333,13 @@ static int fs_mgr_do_mount_helper(Fstab* fstab, const std::string& n_name, // Skips mounting the device. continue; } + } else if (!fstab_entry.avb_key.empty()) { + if (AvbHandle::SetUpStandaloneAvbHashtree(&fstab_entry) == AvbHashtreeResult::kFail) { + LERROR << "Failed to set up AVB on standalone partition: " + << fstab_entry.mount_point << ", skipping!"; + // Skips mounting the device. + continue; + } } else if (fstab_entry.fs_mgr_flags.verify) { int rc = fs_mgr_setup_verity(&fstab_entry, true); if (__android_log_is_debuggable() && diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp index 0482f6c2e..7f6145dd2 100644 --- a/fs_mgr/fs_mgr_fstab.cpp +++ b/fs_mgr/fs_mgr_fstab.cpp @@ -338,6 +338,8 @@ static void ParseFsMgrFlags(const std::string& flags, FstabEntry* entry) { } else if (StartsWith(flag, "zram_backing_dev_path=")) { entry->fs_mgr_flags.zram_backing_dev_path = true; entry->zram_backing_dev_path = arg; + } else if (StartsWith(flag, "avb_key=")) { + entry->avb_key = arg; } else { LWARNING << "Warning: unknown flag: " << flag; } diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h index 100e076f2..a9b386abe 100644 --- a/fs_mgr/include_fstab/fstab/fstab.h +++ b/fs_mgr/include_fstab/fstab/fstab.h @@ -116,6 +116,7 @@ struct FstabEntry { std::string zram_loopback_path; uint64_t zram_loopback_size = 512 * 1024 * 1024; // 512MB by default; std::string zram_backing_dev_path; + std::string avb_key; // TODO: Remove this union once fstab_rec is deprecated. It only serves as a // convenient way to convert between fstab_rec::fs_mgr_flags and these bools. diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp index f57c9d6eb..945087fd4 100644 --- a/fs_mgr/libfs_avb/avb_util.cpp +++ b/fs_mgr/libfs_avb/avb_util.cpp @@ -27,6 +27,7 @@ #include "util.h" +using android::base::Basename; using android::base::StartsWith; using android::base::unique_fd; @@ -61,7 +62,7 @@ std::ostream& operator<<(std::ostream& os, VBMetaVerifyResult result) { // class VBMetaData // ---------------- std::unique_ptr<AvbVBMetaImageHeader> VBMetaData::GetVBMetaHeader(bool update_vbmeta_size) { - auto vbmeta_header(std::make_unique<AvbVBMetaImageHeader>()); + auto vbmeta_header = std::make_unique<AvbVBMetaImageHeader>(); if (!vbmeta_header) return nullptr; @@ -136,7 +137,7 @@ bool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const AvbHashtreeDescriptor& } table.set_readonly(true); - const std::string mount_point(basename(fstab_entry->mount_point.c_str())); + const std::string mount_point(Basename(fstab_entry->mount_point)); android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance(); if (!dm.CreateDevice(mount_point, table)) { LERROR << "Couldn't create verity device!"; @@ -163,12 +164,12 @@ bool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const AvbHashtreeDescriptor& return true; } -bool GetHashtreeDescriptor(const std::string& partition_name, - const std::vector<VBMetaData>& vbmeta_images, - AvbHashtreeDescriptor* out_hashtree_desc, std::string* out_salt, - std::string* out_digest) { +std::unique_ptr<AvbHashtreeDescriptor> GetHashtreeDescriptor( + const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images, + std::string* out_salt, std::string* out_digest) { bool found = false; const uint8_t* desc_partition_name; + auto hashtree_desc = std::make_unique<AvbHashtreeDescriptor>(); for (const auto& vbmeta : vbmeta_images) { size_t num_descriptors; @@ -189,15 +190,15 @@ bool GetHashtreeDescriptor(const std::string& partition_name, desc_partition_name = (const uint8_t*)descriptors[n] + sizeof(AvbHashtreeDescriptor); if (!avb_hashtree_descriptor_validate_and_byteswap( - (AvbHashtreeDescriptor*)descriptors[n], out_hashtree_desc)) { + (AvbHashtreeDescriptor*)descriptors[n], hashtree_desc.get())) { continue; } - if (out_hashtree_desc->partition_name_len != partition_name.length()) { + if (hashtree_desc->partition_name_len != partition_name.length()) { continue; } // Notes that desc_partition_name is not NUL-terminated. std::string hashtree_partition_name((const char*)desc_partition_name, - out_hashtree_desc->partition_name_len); + hashtree_desc->partition_name_len); if (hashtree_partition_name == partition_name) { found = true; } @@ -209,16 +210,43 @@ bool GetHashtreeDescriptor(const std::string& partition_name, if (!found) { LERROR << "Partition descriptor not found: " << partition_name.c_str(); - return false; + return nullptr; } - const uint8_t* desc_salt = desc_partition_name + out_hashtree_desc->partition_name_len; - *out_salt = BytesToHex(desc_salt, out_hashtree_desc->salt_len); + const uint8_t* desc_salt = desc_partition_name + hashtree_desc->partition_name_len; + *out_salt = BytesToHex(desc_salt, hashtree_desc->salt_len); - const uint8_t* desc_digest = desc_salt + out_hashtree_desc->salt_len; - *out_digest = BytesToHex(desc_digest, out_hashtree_desc->root_digest_len); + const uint8_t* desc_digest = desc_salt + hashtree_desc->salt_len; + *out_digest = BytesToHex(desc_digest, hashtree_desc->root_digest_len); - return true; + return hashtree_desc; +} + +bool LoadAvbHashtreeToEnableVerity(FstabEntry* fstab_entry, bool wait_for_verity_dev, + const std::vector<VBMetaData>& vbmeta_images, + const std::string& ab_suffix, + const std::string& ab_other_suffix) { + // Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor + // to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix. + std::string partition_name = DeriveAvbPartitionName(*fstab_entry, ab_suffix, ab_other_suffix); + + if (partition_name.empty()) { + LERROR << "partition name is empty, cannot lookup AVB descriptors"; + return false; + } + + std::string salt; + std::string root_digest; + std::unique_ptr<AvbHashtreeDescriptor> hashtree_descriptor = + GetHashtreeDescriptor(partition_name, vbmeta_images, &salt, &root_digest); + if (!hashtree_descriptor) { + return false; + } + + // Converts HASHTREE descriptor to verity table to load into kernel. + // When success, the new device path will be returned, e.g., /dev/block/dm-2. + return HashtreeDmVeritySetup(fstab_entry, *hashtree_descriptor, salt, root_digest, + wait_for_verity_dev); } // Converts a AVB partition_name (without A/B suffix) to a device partition name. @@ -244,6 +272,38 @@ std::string AvbPartitionToDevicePatition(const std::string& avb_partition_name, return sanitized_partition_name + append_suffix; } +// Converts fstab_entry.blk_device (with ab_suffix) to a AVB partition name. +// e.g., "/dev/block/by-name/system_a", slot_select => "system", +// "/dev/block/by-name/system_b", slot_select_other => "system_other". +// +// Or for a logical partition (with ab_suffix): +// e.g., "system_a", slot_select => "system", +// "system_b", slot_select_other => "system_other". +std::string DeriveAvbPartitionName(const FstabEntry& fstab_entry, const std::string& ab_suffix, + const std::string& ab_other_suffix) { + std::string partition_name; + if (fstab_entry.fs_mgr_flags.logical) { + partition_name = fstab_entry.logical_partition_name; + } else { + partition_name = Basename(fstab_entry.blk_device); + } + + if (fstab_entry.fs_mgr_flags.slot_select) { + auto found = partition_name.rfind(ab_suffix); + if (found != std::string::npos) { + partition_name.erase(found); // converts system_a => system + } + } else if (fstab_entry.fs_mgr_flags.slot_select_other) { + auto found = partition_name.rfind(ab_other_suffix); + if (found != std::string::npos) { + partition_name.erase(found); // converts system_b => system + } + partition_name += "_other"; // converts system => system_other + } + + return partition_name; +} + off64_t GetTotalSize(int fd) { off64_t saved_current = lseek64(fd, 0, SEEK_CUR); if (saved_current == -1) { @@ -268,19 +328,19 @@ off64_t GetTotalSize(int fd) { std::unique_ptr<AvbFooter> GetAvbFooter(int fd) { std::array<uint8_t, AVB_FOOTER_SIZE> footer_buf; - auto footer(std::make_unique<AvbFooter>()); + auto footer = std::make_unique<AvbFooter>(); off64_t footer_offset = GetTotalSize(fd) - AVB_FOOTER_SIZE; ssize_t num_read = TEMP_FAILURE_RETRY(pread64(fd, footer_buf.data(), AVB_FOOTER_SIZE, footer_offset)); if (num_read < 0 || num_read != AVB_FOOTER_SIZE) { - PERROR << "Failed to read AVB footer"; + PERROR << "Failed to read AVB footer at offset: " << footer_offset; return nullptr; } if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_buf.data(), footer.get())) { - PERROR << "AVB footer verification failed."; + PERROR << "AVB footer verification failed at offset " << footer_offset; return nullptr; } @@ -432,24 +492,22 @@ std::vector<ChainInfo> GetChainPartitionInfo(const VBMetaData& vbmeta, bool* fat return chain_partitions; } -VBMetaVerifyResult LoadAndVerifyVbmetaImpl( - const std::string& partition_name, const std::string& ab_suffix, - const std::string& ab_other_suffix, const std::string& expected_public_key_blob, - bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection, - std::function<std::string(const std::string&)> device_path_constructor, - bool is_chained_vbmeta, std::vector<VBMetaData>* out_vbmeta_images) { +// Loads the vbmeta from a given path. +std::unique_ptr<VBMetaData> LoadAndVerifyVbmetaByPath( + const std::string& image_path, const std::string& partition_name, + const std::string& expected_public_key_blob, bool allow_verification_error, + bool rollback_protection, bool is_chained_vbmeta, bool* out_verification_disabled, + VBMetaVerifyResult* out_verify_result) { // 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; + if (!WaitForFile(image_path, 1s)) { + PERROR << "No such path: " << image_path; + return nullptr; } - unique_fd fd(TEMP_FAILURE_RETRY(open(device_path.c_str(), O_RDONLY | O_CLOEXEC))); + unique_fd fd(TEMP_FAILURE_RETRY(open(image_path.c_str(), O_RDONLY | O_CLOEXEC))); if (fd < 0) { - PERROR << "Failed to open: " << device_path; - return VBMetaVerifyResult::kError; + PERROR << "Failed to open: " << image_path; + return nullptr; } VBMetaVerifyResult verify_result; @@ -457,53 +515,84 @@ VBMetaVerifyResult LoadAndVerifyVbmetaImpl( 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; + return nullptr; } + vbmeta->set_vbmeta_path(image_path); if (!allow_verification_error && verify_result == VBMetaVerifyResult::kErrorVerification) { LERROR << partition_name << ": allow verification error is not allowed"; - return VBMetaVerifyResult::kError; + return nullptr; } std::unique_ptr<AvbVBMetaImageHeader> vbmeta_header = vbmeta->GetVBMetaHeader(true /* update_vbmeta_size */); if (!vbmeta_header) { LERROR << partition_name << ": Failed to get vbmeta header"; - return VBMetaVerifyResult::kError; + return nullptr; } if (rollback_protection && RollbackDetected(partition_name, vbmeta_header->rollback_index)) { - return VBMetaVerifyResult::kError; + return nullptr; } // 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; + return nullptr; } - if (out_vbmeta_images) { - out_vbmeta_images->emplace_back(std::move(*vbmeta)); + // Checks if verification has been disabled by setting a bit in the image. + if (out_verification_disabled) { + if (vbmeta_header->flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) { + LWARNING << "VERIFICATION_DISABLED bit is set for partition: " << partition_name; + *out_verification_disabled = true; + } else { + *out_verification_disabled = false; + } } - // 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 (out_verify_result) { + *out_verify_result = verify_result; + } + + return vbmeta; +} + +VBMetaVerifyResult LoadAndVerifyVbmetaByPartition( + const std::string& partition_name, const std::string& ab_suffix, + const std::string& ab_other_suffix, const std::string& expected_public_key_blob, + bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection, + std::function<std::string(const std::string&)> device_path_constructor, bool is_chained_vbmeta, + std::vector<VBMetaData>* out_vbmeta_images) { + auto image_path = device_path_constructor( + AvbPartitionToDevicePatition(partition_name, ab_suffix, ab_other_suffix)); + + bool verification_disabled = false; + VBMetaVerifyResult verify_result; + auto vbmeta = LoadAndVerifyVbmetaByPath( + image_path, partition_name, expected_public_key_blob, allow_verification_error, + rollback_protection, is_chained_vbmeta, &verification_disabled, &verify_result); + + if (!vbmeta) { + return VBMetaVerifyResult::kError; + } + if (out_vbmeta_images) { + out_vbmeta_images->emplace_back(std::move(*vbmeta)); } - if (load_chained_vbmeta) { + // Only loads chained vbmeta if AVB verification is NOT disabled. + if (!verification_disabled && 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); + auto sub_ret = LoadAndVerifyVbmetaByPartition( + 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) { diff --git a/fs_mgr/libfs_avb/avb_util.h b/fs_mgr/libfs_avb/avb_util.h index babbfef70..14918f27f 100644 --- a/fs_mgr/libfs_avb/avb_util.h +++ b/fs_mgr/libfs_avb/avb_util.h @@ -46,10 +46,9 @@ struct ChainInfo { }; // AvbHashtreeDescriptor to dm-verity table setup. -bool GetHashtreeDescriptor(const std::string& partition_name, - const std::vector<VBMetaData>& vbmeta_images, - AvbHashtreeDescriptor* out_hashtree_desc, std::string* out_salt, - std::string* out_digest); +std::unique_ptr<AvbHashtreeDescriptor> GetHashtreeDescriptor( + const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images, + std::string* out_salt, std::string* out_digest); bool ConstructVerityTable(const AvbHashtreeDescriptor& hashtree_desc, const std::string& salt, const std::string& root_digest, const std::string& blk_device, @@ -59,11 +58,20 @@ 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. +// Searches a Avb hashtree descriptor in vbmeta_images for fstab_entry, to enable dm-verity. +bool LoadAvbHashtreeToEnableVerity(FstabEntry* fstab_entry, bool wait_for_verity_dev, + const std::vector<VBMetaData>& vbmeta_images, + const std::string& ab_suffix, const std::string& ab_other_suffix); + +// Converts 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); +// Converts by-name symlink to AVB partition name. +std::string DeriveAvbPartitionName(const FstabEntry& fstab_entry, const std::string& ab_suffix, + const std::string& ab_other_suffix); + // AvbFooter and AvbMetaImage maninpulations. off64_t GetTotalSize(int fd); @@ -84,12 +92,22 @@ bool RollbackDetected(const std::string& partition_name, uint64_t rollback_index // Extracts chain partition info. std::vector<ChainInfo> GetChainPartitionInfo(const VBMetaData& vbmeta, bool* fatal_error); -VBMetaVerifyResult LoadAndVerifyVbmetaImpl( - const std::string& partition_name, const std::string& ab_suffix, - const std::string& ab_other_suffix, const std::string& expected_public_key_blob, - bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection, - std::function<std::string(const std::string&)> device_path_constructor, - bool is_chained_vbmeta, std::vector<VBMetaData>* out_vbmeta_images); +// Loads the single vbmeta from a given path. +std::unique_ptr<VBMetaData> LoadAndVerifyVbmetaByPath( + const std::string& image_path, const std::string& partition_name, + const std::string& expected_public_key_blob, bool allow_verification_error, + bool rollback_protection, bool is_chained_vbmeta, bool* out_verification_disabled, + VBMetaVerifyResult* out_verify_result); + +// Loads the top-level vbmeta and all its chained vbmeta images. +// The actual device path is constructed at runtime by: +// partition_name, ab_suffix, ab_other_suffix, and device_path_constructor. +VBMetaVerifyResult LoadAndVerifyVbmetaByPartition( + const std::string& partition_name, const std::string& ab_suffix, + const std::string& ab_other_suffix, const std::string& expected_public_key_blob, + bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection, + std::function<std::string(const std::string&)> device_path_constructor, bool is_chained_vbmeta, + std::vector<VBMetaData>* out_vbmeta_images); } // namespace fs_mgr } // namespace android diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp index 9f8ad5325..773baf456 100644 --- a/fs_mgr/libfs_avb/fs_avb.cpp +++ b/fs_mgr/libfs_avb/fs_avb.cpp @@ -233,10 +233,10 @@ AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta( 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_); + auto verify_result = LoadAndVerifyVbmetaByPartition( + 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; @@ -245,10 +245,16 @@ AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta( avb_handle->status_ = AvbHandleStatus::kVerificationError; break; default: - LERROR << "LoadAndVerifyVbmetaImpl failed, result: " << verify_result; + LERROR << "LoadAndVerifyVbmetaByPartition failed, result: " << verify_result; return nullptr; } + // Sanity check here because we have to use vbmeta_images_[0] below. + if (avb_handle->vbmeta_images_.size() < 1) { + LERROR << "LoadAndVerifyVbmetaByPartition failed, no vbmeta loaded"; + 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); @@ -377,44 +383,67 @@ AvbUniquePtr AvbHandle::Open() { return avb_handle; } -AvbHashtreeResult AvbHandle::SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev) { - if (!fstab_entry || status_ == AvbHandleStatus::kUninitialized || vbmeta_images_.size() < 1) { +AvbHashtreeResult AvbHandle::SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry) { + if (fstab_entry->avb_key.empty()) { + LERROR << "avb_key=/path/to/key is missing for " << fstab_entry->mount_point; return AvbHashtreeResult::kFail; } - if (status_ == AvbHandleStatus::kHashtreeDisabled || - status_ == AvbHandleStatus::kVerificationDisabled) { - LINFO << "AVB HASHTREE disabled on: " << fstab_entry->mount_point; - return AvbHashtreeResult::kDisabled; + // Binds allow_verification_error and rollback_protection to device unlock state. + bool allow_verification_error = IsDeviceUnlocked(); + bool rollback_protection = !allow_verification_error; + + std::string expected_key_blob; + if (!ReadFileToString(fstab_entry->avb_key, &expected_key_blob)) { + if (!allow_verification_error) { + LERROR << "Failed to load avb_key: " << fstab_entry->avb_key + << " for mount point: " << fstab_entry->mount_point; + return AvbHashtreeResult::kFail; + } + // Use empty key blob, which means no expectation, if allow verification error. + expected_key_blob.clear(); } - // Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor - // to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix. - std::string partition_name; - if (fstab_entry->fs_mgr_flags.logical) { - partition_name = fstab_entry->logical_partition_name; - } else { - partition_name = Basename(fstab_entry->blk_device); + bool verification_disabled = false; + std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath( + fstab_entry->blk_device, "" /* partition_name, no need for a standalone path */, + expected_key_blob, allow_verification_error, rollback_protection, + false /* not is_chained_vbmeta */, &verification_disabled, nullptr /* out_verify_result */); + + if (!vbmeta) { + LERROR << "Failed to load vbmeta: " << fstab_entry->blk_device; + return AvbHashtreeResult::kFail; } - if (fstab_entry->fs_mgr_flags.slot_select) { - auto ab_suffix = partition_name.rfind(fs_mgr_get_slot_suffix()); - if (ab_suffix != std::string::npos) { - partition_name.erase(ab_suffix); - } + if (verification_disabled) { + LINFO << "AVB verification disabled on: " << fstab_entry->mount_point; + return AvbHashtreeResult::kDisabled; + } + + // Puts the vbmeta into a vector, for LoadAvbHashtreeToEnableVerity() to use. + std::vector<VBMetaData> vbmeta_images; + vbmeta_images.emplace_back(std::move(*vbmeta)); + if (!LoadAvbHashtreeToEnableVerity(fstab_entry, true /* wait_for_verity_dev */, vbmeta_images, + fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix())) { + return AvbHashtreeResult::kFail; } - AvbHashtreeDescriptor hashtree_descriptor; - std::string salt; - std::string root_digest; - if (!GetHashtreeDescriptor(partition_name, vbmeta_images_, &hashtree_descriptor, &salt, - &root_digest)) { + return AvbHashtreeResult::kSuccess; +} + +AvbHashtreeResult AvbHandle::SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev) { + if (!fstab_entry || status_ == AvbHandleStatus::kUninitialized || vbmeta_images_.size() < 1) { return AvbHashtreeResult::kFail; } - // Converts HASHTREE descriptor to verity_table_params. - if (!HashtreeDmVeritySetup(fstab_entry, hashtree_descriptor, salt, root_digest, - wait_for_verity_dev)) { + if (status_ == AvbHandleStatus::kHashtreeDisabled || + status_ == AvbHandleStatus::kVerificationDisabled) { + LINFO << "AVB HASHTREE disabled on: " << fstab_entry->mount_point; + return AvbHashtreeResult::kDisabled; + } + + if (!LoadAvbHashtreeToEnableVerity(fstab_entry, wait_for_verity_dev, vbmeta_images_, + fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix())) { return AvbHashtreeResult::kFail; } 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 7af3c7e95..55a320e94 100644 --- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h +++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h @@ -81,8 +81,15 @@ class VBMetaData { // true to update vbmeta_size_ to the actual size with valid content. std::unique_ptr<AvbVBMetaImageHeader> GetVBMetaHeader(bool update_vbmeta_size = false); + // Sets the vbmeta_path where we load the vbmeta data. Could be a partition or a file. + // e.g., + // - /dev/block/by-name/system_a + // - /path/to/system_other.img. + void set_vbmeta_path(std::string vbmeta_path) { vbmeta_path_ = std::move(vbmeta_path); } + // Get methods for each data member. const std::string& partition() const { return partition_name_; } + const std::string& vbmeta_path() const { return vbmeta_path_; } uint8_t* data() const { return vbmeta_ptr_.get(); } const size_t& size() const { return vbmeta_size_; } @@ -93,6 +100,7 @@ class VBMetaData { std::unique_ptr<uint8_t[]> vbmeta_ptr_; size_t vbmeta_size_; std::string partition_name_; + std::string vbmeta_path_; }; class FsManagerAvbOps; @@ -160,6 +168,9 @@ class AvbHandle { // - kDisabled: hashtree is disabled. AvbHashtreeResult SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev); + // Similar to above, but loads the offline vbmeta from the end of fstab_entry->blk_device. + static AvbHashtreeResult SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry); + const std::string& avb_version() const { return avb_version_; } const VBMetaInfo& vbmeta_info() const { return vbmeta_info_; } AvbHandleStatus status() const { return status_; } diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp index 26b32948c..23faffcbd 100644 --- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp +++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp @@ -14,20 +14,25 @@ * limitations under the License. */ +#include <endian.h> + #include <android-base/unique_fd.h> #include <base/files/file_util.h> #include <base/rand_util.h> #include <base/strings/string_util.h> +#include <libavb/libavb.h> #include "avb_util.h" #include "fs_avb_test_util.h" // Target classes or functions to test: using android::fs_mgr::AvbPartitionToDevicePatition; +using android::fs_mgr::DeriveAvbPartitionName; using android::fs_mgr::GetAvbFooter; using android::fs_mgr::GetChainPartitionInfo; using android::fs_mgr::GetTotalSize; -using android::fs_mgr::LoadAndVerifyVbmetaImpl; +using android::fs_mgr::LoadAndVerifyVbmetaByPartition; +using android::fs_mgr::LoadAndVerifyVbmetaByPath; using android::fs_mgr::VBMetaData; using android::fs_mgr::VBMetaVerifyResult; using android::fs_mgr::VerifyPublicKeyBlob; @@ -52,8 +57,34 @@ class AvbUtilTest : public BaseFsAvbTest { // 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); + + // Sets the flas in vbmeta header, the image_path could be a vbmeta.img or a system.img. + void SetVBMetaFlags(const base::FilePath& image_path, uint32_t flags); }; +void AvbUtilTest::SetVBMetaFlags(const base::FilePath& image_path, uint32_t flags) { + if (!base::PathExists(image_path)) return; + + std::string image_file_name = image_path.RemoveExtension().BaseName().value(); + bool is_vbmeta_partition = + base::StartsWith(image_file_name, "vbmeta", base::CompareCase::INSENSITIVE_ASCII); + + android::base::unique_fd fd(open(image_path.value().c_str(), O_RDWR | O_CLOEXEC)); + EXPECT_TRUE(fd > 0); + + uint64_t vbmeta_offset = 0; // for vbmeta.img + if (!is_vbmeta_partition) { + std::unique_ptr<AvbFooter> footer = GetAvbFooter(fd); + EXPECT_NE(nullptr, footer); + vbmeta_offset = footer->vbmeta_offset; + } + + auto flags_offset = vbmeta_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(AvbUtilTest, AvbPartitionToDevicePatition) { EXPECT_EQ("system", AvbPartitionToDevicePatition("system", "", "")); EXPECT_EQ("system", AvbPartitionToDevicePatition("system", "", "_b")); @@ -65,6 +96,63 @@ TEST_F(AvbUtilTest, AvbPartitionToDevicePatition) { EXPECT_EQ("system_b", AvbPartitionToDevicePatition("system_other", "_a", "_b")); } +TEST_F(AvbUtilTest, DeriveAvbPartitionName) { + // The fstab_entry to test. + FstabEntry fstab_entry = { + .blk_device = "/dev/block/dm-1", // a dm-linear device (logical) + .mount_point = "/system", + .fs_type = "ext4", + .logical_partition_name = "system", + }; + + // Logical partitions. + // non-A/B + fstab_entry.fs_mgr_flags.logical = true; + EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_dont_care", "_dont_care")); + EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_b")); + EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "", "")); + // Active slot. + fstab_entry.fs_mgr_flags.slot_select = true; + fstab_entry.logical_partition_name = "system_a"; + EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_dont_care")); + EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_b")); + EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "")); + EXPECT_EQ("system_a", DeriveAvbPartitionName(fstab_entry, "_wont_erase_a", "_dont_care")); + // The other slot. + fstab_entry.fs_mgr_flags.slot_select = false; + fstab_entry.fs_mgr_flags.slot_select_other = true; + fstab_entry.logical_partition_name = "system_b"; + EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "_dont_care", "_b")); + EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "_a", "_b")); + EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "", "_b")); + EXPECT_EQ("system_b_other", DeriveAvbPartitionName(fstab_entry, "_dont_care", "_wont_erase_b")); + + // Non-logical partitions. + // non-A/B. + fstab_entry.fs_mgr_flags.logical = false; + fstab_entry.fs_mgr_flags.slot_select = false; + fstab_entry.fs_mgr_flags.slot_select_other = false; + fstab_entry.blk_device = "/dev/block/by-name/system"; + EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_dont_care", "_dont_care")); + EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_b")); + EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "", "")); + // Active slot _a. + fstab_entry.fs_mgr_flags.slot_select = true; + fstab_entry.blk_device = "/dev/block/by-name/system_a"; + EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_dont_care")); + EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_b")); + EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "")); + EXPECT_EQ("system_a", DeriveAvbPartitionName(fstab_entry, "_wont_erase_a", "_dont_care")); + // Inactive slot _b. + fstab_entry.fs_mgr_flags.slot_select = false; + fstab_entry.fs_mgr_flags.slot_select_other = true; + fstab_entry.blk_device = "/dev/block/by-name/system_b"; + EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "dont_care", "_b")); + EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "_a", "_b")); + EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "", "_b")); + EXPECT_EQ("system_b_other", DeriveAvbPartitionName(fstab_entry, "dont_care", "_wont_erase_b")); +} + TEST_F(AvbUtilTest, GetFdTotalSize) { // Generates a raw test.img via BaseFsAvbTest. const size_t image_size = 5 * 1024 * 1024; @@ -751,7 +839,213 @@ TEST_F(AvbUtilTest, GetChainPartitionInfoNone) { EXPECT_EQ(false, fatal_error); } -TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImpl) { +TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPath) { + // Generates a raw system_other.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_other.img", system_image_size); + + // Adds AVB Hashtree Footer. + AddAvbFooter(system_path, "hashtree", "system_other", system_partition_size, "SHA512_RSA4096", + 20, data_dir_.Append("testkey_rsa4096.pem"), "d00df00d", + "--internal_release_string \"unit test\""); + + base::FilePath rsa4096_public_key = + ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem")); + + std::string expected_key_blob_4096; + EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096)); + + bool verification_disabled; + VBMetaVerifyResult verify_result; + std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath( + system_path.value(), "system_other", expected_key_blob_4096, + false /* allow_verification_error */, false /* rollback_protection */, + false /* is_chained_vbmeta */, &verification_disabled, &verify_result); + + EXPECT_NE(nullptr, vbmeta); + EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result); + EXPECT_EQ(false, verification_disabled); + + EXPECT_EQ(2112UL, vbmeta->size()); + EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path()); + EXPECT_EQ("system_other", vbmeta->partition()); + EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); +} + +TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPathErrorVerification) { + // Generates a raw system_other.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_other.img", system_image_size); + + // Adds AVB Hashtree Footer. + AddAvbFooter(system_path, "hashtree", "system_other", system_partition_size, "SHA512_RSA4096", + 20, data_dir_.Append("testkey_rsa4096.pem"), "d00df00d", + "--internal_release_string \"unit test\""); + + base::FilePath rsa4096_public_key = + ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem")); + + std::string expected_key_blob_4096; + EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096)); + + // Modifies the auxiliary data of system_other.img + auto fd = OpenUniqueReadFd(system_path); + auto system_footer = GetAvbFooter(fd); + auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, "system_other-vbmeta.img"); + auto system_header = system_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 + system_header->authentication_data_block_size; + + // Modifies the hash. + ModifyFile( + system_path, + (system_footer->vbmeta_offset + authentication_block_offset + system_header->hash_offset), + system_header->hash_size); + + VBMetaVerifyResult verify_result; + // Not allow verification error. + std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath( + system_path.value(), "system_other", expected_key_blob_4096, + false /* allow_verification_error */, false /* rollback_protection */, + false /* is_chained_vbmeta */, nullptr /* verification_disabled */, &verify_result); + EXPECT_EQ(nullptr, vbmeta); + + // Allow verification error. + vbmeta = LoadAndVerifyVbmetaByPath( + system_path.value(), "system_other", expected_key_blob_4096, + true /* allow_verification_error */, false /* rollback_protection */, + false /* is_chained_vbmeta */, nullptr /* verification_disabled */, &verify_result); + EXPECT_NE(nullptr, vbmeta); + EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result); + + EXPECT_EQ(2112UL, vbmeta->size()); + EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path()); + EXPECT_EQ("system_other", vbmeta->partition()); + EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); + + // Modifies the auxiliary data block. + ModifyFile(system_path, system_footer->vbmeta_offset + auxiliary_block_offset, + system_header->auxiliary_data_block_size); + + // Not allow verification error. + vbmeta = LoadAndVerifyVbmetaByPath( + system_path.value(), "system_other", expected_key_blob_4096, + false /* allow_verification_error */, false /* rollback_protection */, + false /* is_chained_vbmeta */, nullptr /* verification_disabled */, &verify_result); + EXPECT_EQ(nullptr, vbmeta); + + // Allow verification error. + vbmeta = LoadAndVerifyVbmetaByPath( + system_path.value(), "system_other", expected_key_blob_4096, + true /* allow_verification_error */, false /* rollback_protection */, + false /* is_chained_vbmeta */, nullptr /* verification_disabled */, &verify_result); + EXPECT_NE(nullptr, vbmeta); + EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result); +} + +TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPathUnexpectedPublicKey) { + // Generates a raw system_other.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_other.img", system_image_size); + + // Adds AVB Hashtree Footer. + AddAvbFooter(system_path, "hashtree", "system_other", system_partition_size, "SHA512_RSA4096", + 20, data_dir_.Append("testkey_rsa4096.pem"), "d00df00d", + "--internal_release_string \"unit test\""); + + base::FilePath rsa2048_public_key = + ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem")); + base::FilePath rsa4096_public_key = + ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem")); + + std::string expected_key_blob_4096; + EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096)); + std::string unexpected_key_blob_2048; + EXPECT_TRUE(base::ReadFileToString(rsa2048_public_key, &unexpected_key_blob_2048)); + + // Uses the correct expected public key. + VBMetaVerifyResult verify_result; + std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath( + system_path.value(), "system_other", expected_key_blob_4096, + false /* allow_verification_error */, false /* rollback_protection */, + false /* is_chained_vbmeta */, nullptr /* verification_disabled */, &verify_result); + EXPECT_NE(nullptr, vbmeta); + EXPECT_EQ(verify_result, VBMetaVerifyResult::kSuccess); + EXPECT_EQ(2112UL, vbmeta->size()); + EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path()); + EXPECT_EQ("system_other", vbmeta->partition()); + EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); + + // Uses the wrong expected public key with allow_verification_error set to false. + vbmeta = LoadAndVerifyVbmetaByPath( + system_path.value(), "system_other", unexpected_key_blob_2048, + false /* allow_verification_error */, false /* rollback_protection */, + false /* is_chained_vbmeta */, nullptr /* verification_disabled */, &verify_result); + EXPECT_EQ(nullptr, vbmeta); + + // Uses the wrong expected public key with allow_verification_error set to true. + vbmeta = LoadAndVerifyVbmetaByPath( + system_path.value(), "system_other", unexpected_key_blob_2048, + true /* allow_verification_error */, false /* rollback_protection */, + false /* is_chained_vbmeta */, nullptr /* verification_disabled */, &verify_result); + EXPECT_NE(nullptr, vbmeta); + EXPECT_EQ(verify_result, VBMetaVerifyResult::kErrorVerification); + EXPECT_EQ(2112UL, vbmeta->size()); + EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path()); + EXPECT_EQ("system_other", vbmeta->partition()); + EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); +} + +TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPathVerificationDisabled) { + // Generates a raw system_other.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_other.img", system_image_size); + + // Adds AVB Hashtree Footer. + AddAvbFooter(system_path, "hashtree", "system_other", system_partition_size, "SHA512_RSA4096", + 20, data_dir_.Append("testkey_rsa4096.pem"), "d00df00d", + "--internal_release_string \"unit test\""); + + base::FilePath rsa4096_public_key = + ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem")); + + std::string expected_key_blob_4096; + EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096)); + + // Sets disabled flag and expect the returned verification_disabled is true. + SetVBMetaFlags(system_path, AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED); + bool verification_disabled; + VBMetaVerifyResult verify_result; + std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath( + system_path.value(), "system_other", expected_key_blob_4096, + true /* allow_verification_error */, false /* rollback_protection */, + false /* is_chained_vbmeta */, &verification_disabled, &verify_result); + + EXPECT_NE(nullptr, vbmeta); + EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result); + EXPECT_EQ(true, verification_disabled); // should be true. + + EXPECT_EQ(2112UL, vbmeta->size()); + EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path()); + EXPECT_EQ("system_other", vbmeta->partition()); + EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); + + // Since the vbmeta flags is modified, vbmeta will be nullptr + // if verification error isn't allowed. + vbmeta = LoadAndVerifyVbmetaByPath( + system_path.value(), "system_other", expected_key_blob_4096, + false /* allow_verification_error */, false /* rollback_protection */, + false /* is_chained_vbmeta */, &verification_disabled, &verify_result); + EXPECT_EQ(nullptr, vbmeta); +} + +TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartition) { // Generates a raw boot.img const size_t boot_image_size = 5 * 1024 * 1024; const size_t boot_partition_size = 10 * 1024 * 1024; @@ -795,18 +1089,18 @@ TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImpl) { EXPECT_EQ("6f4bf815a651aa35ec7102a88b7906b91aef284bc5e20d0bf527c7d460da3266", CalcVBMetaDigest("vbmeta.img", "sha256")); - // Starts to test LoadAndVerifyVbmetaImpl. + // Starts to test LoadAndVerifyVbmetaByPartition. std::vector<VBMetaData> vbmeta_images; auto vbmeta_image_path = [this](const std::string& partition_name) { return test_dir_.Append(partition_name + ".img").value(); }; EXPECT_EQ(VBMetaVerifyResult::kSuccess, - LoadAndVerifyVbmetaImpl( - "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */, - "" /* expected_public_key_blob*/, false /* allow_verification_error */, - true /* load_chained_vbmeta */, true /* rollback_protection */, - vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images)); + LoadAndVerifyVbmetaByPartition( + "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. @@ -818,17 +1112,17 @@ TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImpl) { // 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)); + LoadAndVerifyVbmetaByPartition( + "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) { +TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionWithSuffixes) { // Tests the following chained partitions. // vbmeta_a.img // |--> boot_b.img (boot_other) @@ -874,18 +1168,18 @@ TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImplWithSuffixes) { {"vbmeta_system_other", 2, rsa4096_public_key}}, "--internal_release_string \"unit test\""); - // Starts to test LoadAndVerifyVbmetaImpl with ab_suffix and ab_other_suffix. + // Starts to test LoadAndVerifyVbmetaByPartition with ab_suffix and ab_other_suffix. auto vbmeta_image_path = [this](const std::string& partition_name) { return test_dir_.Append(partition_name + ".img").value(); }; std::vector<VBMetaData> vbmeta_images; EXPECT_EQ(VBMetaVerifyResult::kSuccess, - LoadAndVerifyVbmetaImpl( - "vbmeta" /* partition_name */, "_a" /* ab_suffix */, "_b" /* other_suffix */, - "" /* expected_public_key_blob*/, false /* allow_verification_error */, - true /* load_chained_vbmeta */, true /* rollback_protection */, - vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images)); + LoadAndVerifyVbmetaByPartition( + "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. @@ -897,26 +1191,26 @@ TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImplWithSuffixes) { // 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)); + LoadAndVerifyVbmetaByPartition( + "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)); + LoadAndVerifyVbmetaByPartition( + "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) { +TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionErrorVerification) { // Generates a raw boot.img const size_t boot_image_size = 5 * 1024 * 1024; const size_t boot_partition_size = 10 * 1024 * 1024; @@ -964,27 +1258,27 @@ TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImplErrorVerification) { // Modifies the hash. ModifyFile(vbmeta_path, authentication_block_offset + header->hash_offset, header->hash_size); - // Starts to test LoadAndVerifyVbmetaImpl. + // Starts to test LoadAndVerifyVbmetaByPartition. std::vector<VBMetaData> vbmeta_images; auto vbmeta_image_path = [this](const std::string& partition_name) { return test_dir_.Append(partition_name + ".img").value(); }; EXPECT_EQ(VBMetaVerifyResult::kError, - LoadAndVerifyVbmetaImpl( - "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */, - "" /* expected_public_key_blob*/, false /* allow_verification_error */, - true /* load_chained_vbmeta */, true /* rollback_protection */, - vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images)); + LoadAndVerifyVbmetaByPartition( + "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)); + LoadAndVerifyVbmetaByPartition( + "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. @@ -1008,11 +1302,11 @@ TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImplErrorVerification) { 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)); + LoadAndVerifyVbmetaByPartition( + "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'. @@ -1020,20 +1314,113 @@ TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImplErrorVerification) { EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1])); // Resets the modification of the auxiliary data. - ModifyFile(vbmeta_path, 0 /* offset */, -1 /* length */); + ModifyFile(system_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)); + LoadAndVerifyVbmetaByPartition( + "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, LoadAndVerifyVbmetaByPartitionVerificationDisabled) { + // 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 LoadAndVerifyVbmetaByPartition. + std::vector<VBMetaData> vbmeta_images; + auto vbmeta_image_path = [this](const std::string& partition_name) { + return test_dir_.Append(partition_name + ".img").value(); + }; + + EXPECT_EQ(VBMetaVerifyResult::kSuccess, + LoadAndVerifyVbmetaByPartition( + "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])); + + // Sets VERIFICATION_DISABLED to the top-level vbmeta.img + SetVBMetaFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED); + vbmeta_images.clear(); + EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, + LoadAndVerifyVbmetaByPartition( + "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(1UL, vbmeta_images.size()); // Only vbmeta is loaded + EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0])); + + // HASHTREE_DISABLED still loads the chained vbmeta. + SetVBMetaFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED); + vbmeta_images.clear(); + EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, + LoadAndVerifyVbmetaByPartition( + "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(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])); } -TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImplUnexpectedPublicKey) { +TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionUnexpectedPublicKey) { // Generates chain partition descriptors. base::FilePath rsa2048_public_key = ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem")); @@ -1060,29 +1447,29 @@ TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImplUnexpectedPublicKey) { std::vector<VBMetaData> vbmeta_images; // Uses the correct expected public key. EXPECT_EQ(VBMetaVerifyResult::kSuccess, - LoadAndVerifyVbmetaImpl( - "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */, - expected_key_blob_8192, true /* allow_verification_error */, - false /* load_chained_vbmeta */, true /* rollback_protection */, - vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images)); + LoadAndVerifyVbmetaByPartition( + "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)); + LoadAndVerifyVbmetaByPartition( + "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)); + LoadAndVerifyVbmetaByPartition( + "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 |