diff options
Diffstat (limited to 'tests/component')
-rw-r--r-- | tests/component/applypatch_modes_test.cpp | 1 | ||||
-rw-r--r-- | tests/component/bootloader_message_test.cpp | 2 | ||||
-rw-r--r-- | tests/component/imgdiff_test.cpp | 1 | ||||
-rw-r--r-- | tests/component/install_test.cpp | 440 | ||||
-rw-r--r-- | tests/component/resources_test.cpp | 2 | ||||
-rw-r--r-- | tests/component/sideload_test.cpp | 1 | ||||
-rw-r--r-- | tests/component/uncrypt_test.cpp | 1 | ||||
-rw-r--r-- | tests/component/update_verifier_test.cpp | 136 | ||||
-rw-r--r-- | tests/component/updater_test.cpp | 140 | ||||
-rw-r--r-- | tests/component/verifier_test.cpp | 316 |
10 files changed, 736 insertions, 304 deletions
diff --git a/tests/component/applypatch_modes_test.cpp b/tests/component/applypatch_modes_test.cpp index ce01f4fd5..08414b796 100644 --- a/tests/component/applypatch_modes_test.cpp +++ b/tests/component/applypatch_modes_test.cpp @@ -23,7 +23,6 @@ #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/strings.h> -#include <android-base/test_utils.h> #include <bsdiff/bsdiff.h> #include <gtest/gtest.h> #include <openssl/sha.h> diff --git a/tests/component/bootloader_message_test.cpp b/tests/component/bootloader_message_test.cpp index 6cc59a495..b005d199c 100644 --- a/tests/component/bootloader_message_test.cpp +++ b/tests/component/bootloader_message_test.cpp @@ -17,8 +17,8 @@ #include <string> #include <vector> +#include <android-base/file.h> #include <android-base/strings.h> -#include <android-base/test_utils.h> #include <bootloader_message/bootloader_message.h> #include <gtest/gtest.h> diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index cb4868a4a..e76ccbdfb 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -25,7 +25,6 @@ #include <android-base/memory.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> -#include <android-base/test_utils.h> #include <applypatch/imgdiff.h> #include <applypatch/imgdiff_image.h> #include <applypatch/imgpatch.h> diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp index 08b429000..969805b42 100644 --- a/tests/component/install_test.cpp +++ b/tests/component/install_test.cpp @@ -20,13 +20,13 @@ #include <unistd.h> #include <algorithm> +#include <random> #include <string> #include <vector> #include <android-base/file.h> #include <android-base/properties.h> #include <android-base/strings.h> -#include <android-base/test_utils.h> #include <gtest/gtest.h> #include <vintf/VintfObjectRecovery.h> #include <ziparchive/zip_archive.h> @@ -36,15 +36,23 @@ #include "otautil/paths.h" #include "private/install.h" -TEST(InstallTest, verify_package_compatibility_no_entry) { - TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); +static void BuildZipArchive(const std::map<std::string, std::string>& file_map, int fd, + int compression_type) { + FILE* zip_file = fdopen(fd, "w"); ZipWriter writer(zip_file); - // The archive must have something to be opened correctly. - ASSERT_EQ(0, writer.StartEntry("dummy_entry", 0)); - ASSERT_EQ(0, writer.FinishEntry()); + for (const auto& [name, content] : file_map) { + ASSERT_EQ(0, writer.StartEntry(name.c_str(), compression_type)); + ASSERT_EQ(0, writer.WriteBytes(content.data(), content.size())); + ASSERT_EQ(0, writer.FinishEntry()); + } ASSERT_EQ(0, writer.Finish()); ASSERT_EQ(0, fclose(zip_file)); +} + +TEST(InstallTest, verify_package_compatibility_no_entry) { + TemporaryFile temp_file; + // The archive must have something to be opened correctly. + BuildZipArchive({ { "dummy_entry", "" } }, temp_file.release(), kCompressStored); // Doesn't contain compatibility zip entry. ZipArchiveHandle zip; @@ -55,12 +63,7 @@ TEST(InstallTest, verify_package_compatibility_no_entry) { TEST(InstallTest, verify_package_compatibility_invalid_entry) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); - ZipWriter writer(zip_file); - ASSERT_EQ(0, writer.StartEntry("compatibility.zip", 0)); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); + BuildZipArchive({ { "compatibility.zip", "" } }, temp_file.release(), kCompressStored); // Empty compatibility zip entry. ZipArchiveHandle zip; @@ -71,77 +74,77 @@ TEST(InstallTest, verify_package_compatibility_invalid_entry) { TEST(InstallTest, read_metadata_from_package_smoke) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); - ZipWriter writer(zip_file); - ASSERT_EQ(0, writer.StartEntry("META-INF/com/android/metadata", kCompressStored)); - const std::string content("abcdefg"); - ASSERT_EQ(0, writer.WriteBytes(content.data(), content.size())); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); + const std::string content("abc=defg"); + BuildZipArchive({ { "META-INF/com/android/metadata", content } }, temp_file.release(), + kCompressStored); ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - std::string metadata; - ASSERT_TRUE(read_metadata_from_package(zip, &metadata)); - ASSERT_EQ(content, metadata); + std::map<std::string, std::string> metadata; + ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata)); + ASSERT_EQ("defg", metadata["abc"]); CloseArchive(zip); TemporaryFile temp_file2; - FILE* zip_file2 = fdopen(temp_file2.release(), "w"); - ZipWriter writer2(zip_file2); - ASSERT_EQ(0, writer2.StartEntry("META-INF/com/android/metadata", kCompressDeflated)); - ASSERT_EQ(0, writer2.WriteBytes(content.data(), content.size())); - ASSERT_EQ(0, writer2.FinishEntry()); - ASSERT_EQ(0, writer2.Finish()); - ASSERT_EQ(0, fclose(zip_file2)); + BuildZipArchive({ { "META-INF/com/android/metadata", content } }, temp_file2.release(), + kCompressDeflated); ASSERT_EQ(0, OpenArchive(temp_file2.path, &zip)); metadata.clear(); - ASSERT_TRUE(read_metadata_from_package(zip, &metadata)); - ASSERT_EQ(content, metadata); + ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata)); + ASSERT_EQ("defg", metadata["abc"]); CloseArchive(zip); } TEST(InstallTest, read_metadata_from_package_no_entry) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); - ZipWriter writer(zip_file); - ASSERT_EQ(0, writer.StartEntry("dummy_entry", kCompressStored)); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); + BuildZipArchive({ { "dummy_entry", "" } }, temp_file.release(), kCompressStored); ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - std::string metadata; - ASSERT_FALSE(read_metadata_from_package(zip, &metadata)); + std::map<std::string, std::string> metadata; + ASSERT_FALSE(ReadMetadataFromPackage(zip, &metadata)); CloseArchive(zip); } +TEST(InstallTest, read_wipe_ab_partition_list) { + std::vector<std::string> partition_list = { + "/dev/block/bootdevice/by-name/system_a", "/dev/block/bootdevice/by-name/system_b", + "/dev/block/bootdevice/by-name/vendor_a", "/dev/block/bootdevice/by-name/vendor_b", + "/dev/block/bootdevice/by-name/userdata", "# Wipe the boot partitions last", + "/dev/block/bootdevice/by-name/boot_a", "/dev/block/bootdevice/by-name/boot_b", + }; + TemporaryFile temp_file; + BuildZipArchive({ { "recovery.wipe", android::base::Join(partition_list, '\n') } }, + temp_file.release(), kCompressDeflated); + std::string wipe_package; + ASSERT_TRUE(android::base::ReadFileToString(temp_file.path, &wipe_package)); + + auto package = Package::CreateMemoryPackage( + std::vector<uint8_t>(wipe_package.begin(), wipe_package.end()), nullptr); + + auto read_partition_list = GetWipePartitionList(package.get()); + std::vector<std::string> expected = { + "/dev/block/bootdevice/by-name/system_a", "/dev/block/bootdevice/by-name/system_b", + "/dev/block/bootdevice/by-name/vendor_a", "/dev/block/bootdevice/by-name/vendor_b", + "/dev/block/bootdevice/by-name/userdata", "/dev/block/bootdevice/by-name/boot_a", + "/dev/block/bootdevice/by-name/boot_b", + }; + ASSERT_EQ(expected, read_partition_list); +} + TEST(InstallTest, verify_package_compatibility_with_libvintf_malformed_xml) { TemporaryFile compatibility_zip_file; - FILE* compatibility_zip = fdopen(compatibility_zip_file.release(), "w"); - ZipWriter compatibility_zip_writer(compatibility_zip); - ASSERT_EQ(0, compatibility_zip_writer.StartEntry("system_manifest.xml", kCompressDeflated)); std::string malformed_xml = "malformed"; - ASSERT_EQ(0, compatibility_zip_writer.WriteBytes(malformed_xml.data(), malformed_xml.size())); - ASSERT_EQ(0, compatibility_zip_writer.FinishEntry()); - ASSERT_EQ(0, compatibility_zip_writer.Finish()); - ASSERT_EQ(0, fclose(compatibility_zip)); + BuildZipArchive({ { "system_manifest.xml", malformed_xml } }, compatibility_zip_file.release(), + kCompressDeflated); TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); - ZipWriter writer(zip_file); - ASSERT_EQ(0, writer.StartEntry("compatibility.zip", kCompressStored)); std::string compatibility_zip_content; ASSERT_TRUE( android::base::ReadFileToString(compatibility_zip_file.path, &compatibility_zip_content)); - ASSERT_EQ(0, - writer.WriteBytes(compatibility_zip_content.data(), compatibility_zip_content.size())); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); + BuildZipArchive({ { "compatibility.zip", compatibility_zip_content } }, temp_file.release(), + kCompressStored); ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); @@ -166,27 +169,15 @@ TEST(InstallTest, verify_package_compatibility_with_libvintf_system_manifest_xml ASSERT_TRUE( android::base::ReadFileToString(system_manifest_xml_path, &system_manifest_xml_content)); TemporaryFile compatibility_zip_file; - FILE* compatibility_zip = fdopen(compatibility_zip_file.release(), "w"); - ZipWriter compatibility_zip_writer(compatibility_zip); - ASSERT_EQ(0, compatibility_zip_writer.StartEntry("system_manifest.xml", kCompressDeflated)); - ASSERT_EQ(0, compatibility_zip_writer.WriteBytes(system_manifest_xml_content.data(), - system_manifest_xml_content.size())); - ASSERT_EQ(0, compatibility_zip_writer.FinishEntry()); - ASSERT_EQ(0, compatibility_zip_writer.Finish()); - ASSERT_EQ(0, fclose(compatibility_zip)); + BuildZipArchive({ { "system_manifest.xml", system_manifest_xml_content } }, + compatibility_zip_file.release(), kCompressDeflated); TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); - ZipWriter writer(zip_file); - ASSERT_EQ(0, writer.StartEntry("compatibility.zip", kCompressStored)); std::string compatibility_zip_content; ASSERT_TRUE( android::base::ReadFileToString(compatibility_zip_file.path, &compatibility_zip_content)); - ASSERT_EQ(0, - writer.WriteBytes(compatibility_zip_content.data(), compatibility_zip_content.size())); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); + BuildZipArchive({ { "compatibility.zip", compatibility_zip_content } }, temp_file.release(), + kCompressStored); ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); @@ -202,13 +193,8 @@ TEST(InstallTest, verify_package_compatibility_with_libvintf_system_manifest_xml TEST(InstallTest, SetUpNonAbUpdateCommands) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); - ZipWriter writer(zip_file); static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary"; - ASSERT_EQ(0, writer.StartEntry(UPDATE_BINARY_NAME, kCompressStored)); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); + BuildZipArchive({ { UPDATE_BINARY_NAME, "" } }, temp_file.release(), kCompressStored); ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); @@ -246,13 +232,8 @@ TEST(InstallTest, SetUpNonAbUpdateCommands) { TEST(InstallTest, SetUpNonAbUpdateCommands_MissingUpdateBinary) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); - ZipWriter writer(zip_file); // The archive must have something to be opened correctly. - ASSERT_EQ(0, writer.StartEntry("dummy_entry", 0)); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); + BuildZipArchive({ { "dummy_entry", "" } }, temp_file.release(), kCompressStored); // Missing update binary. ZipArchiveHandle zip; @@ -268,16 +249,8 @@ TEST(InstallTest, SetUpNonAbUpdateCommands_MissingUpdateBinary) { static void VerifyAbUpdateCommands(const std::string& serialno, bool success = true) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); - ZipWriter writer(zip_file); - ASSERT_EQ(0, writer.StartEntry("payload.bin", kCompressStored)); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.StartEntry("payload_properties.txt", kCompressStored)); + const std::string properties = "some_properties"; - ASSERT_EQ(0, writer.WriteBytes(properties.data(), properties.size())); - ASSERT_EQ(0, writer.FinishEntry()); - // A metadata entry is mandatory. - ASSERT_EQ(0, writer.StartEntry("META-INF/com/android/metadata", kCompressStored)); std::string device = android::base::GetProperty("ro.product.device", ""); ASSERT_NE("", device); std::string timestamp = android::base::GetProperty("ro.build.date.utc", ""); @@ -288,21 +261,27 @@ static void VerifyAbUpdateCommands(const std::string& serialno, bool success = t if (!serialno.empty()) { meta.push_back("serialno=" + serialno); } - std::string metadata = android::base::Join(meta, "\n"); - ASSERT_EQ(0, writer.WriteBytes(metadata.data(), metadata.size())); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); + std::string metadata_string = android::base::Join(meta, "\n"); + + BuildZipArchive({ { "payload.bin", "" }, + { "payload_properties.txt", properties }, + { "META-INF/com/android/metadata", metadata_string } }, + temp_file.release(), kCompressStored); ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); ZipString payload_name("payload.bin"); ZipEntry payload_entry; ASSERT_EQ(0, FindEntry(zip, payload_name, &payload_entry)); - int status_fd = 10; - std::string package = "/path/to/update.zip"; - std::vector<std::string> cmd; + + std::map<std::string, std::string> metadata; + ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata)); if (success) { + ASSERT_EQ(0, CheckPackageMetadata(metadata, OtaType::AB)); + + int status_fd = 10; + std::string package = "/path/to/update.zip"; + std::vector<std::string> cmd; ASSERT_EQ(0, SetUpAbUpdateCommands(package, zip, status_fd, &cmd)); ASSERT_EQ(5U, cmd.size()); ASSERT_EQ("/system/bin/update_engine_sideload", cmd[0]); @@ -311,7 +290,7 @@ static void VerifyAbUpdateCommands(const std::string& serialno, bool success = t ASSERT_EQ("--headers=" + properties, cmd[3]); ASSERT_EQ("--status_fd=" + std::to_string(status_fd), cmd[4]); } else { - ASSERT_EQ(INSTALL_ERROR, SetUpAbUpdateCommands(package, zip, status_fd, &cmd)); + ASSERT_EQ(INSTALL_ERROR, CheckPackageMetadata(metadata, OtaType::AB)); } CloseArchive(zip); } @@ -323,13 +302,7 @@ TEST(InstallTest, SetUpAbUpdateCommands) { TEST(InstallTest, SetUpAbUpdateCommands_MissingPayloadPropertiesTxt) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); - ZipWriter writer(zip_file); - // Missing payload_properties.txt. - ASSERT_EQ(0, writer.StartEntry("payload.bin", kCompressStored)); - ASSERT_EQ(0, writer.FinishEntry()); - // A metadata entry is mandatory. - ASSERT_EQ(0, writer.StartEntry("META-INF/com/android/metadata", kCompressStored)); + std::string device = android::base::GetProperty("ro.product.device", ""); ASSERT_NE("", device); std::string timestamp = android::base::GetProperty("ro.build.date.utc", ""); @@ -339,10 +312,13 @@ TEST(InstallTest, SetUpAbUpdateCommands_MissingPayloadPropertiesTxt) { "ota-type=AB", "pre-device=" + device, "post-timestamp=" + timestamp, }, "\n"); - ASSERT_EQ(0, writer.WriteBytes(metadata.data(), metadata.size())); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); + + BuildZipArchive( + { + { "payload.bin", "" }, + { "META-INF/com/android/metadata", metadata }, + }, + temp_file.release(), kCompressStored); ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); @@ -381,3 +357,241 @@ TEST(InstallTest, SetUpAbUpdateCommands_MultipleSerialnos) { // String with the matching serialno should pass the verification. VerifyAbUpdateCommands(long_serialno); } + +static void test_check_package_metadata(const std::string& metadata_string, OtaType ota_type, + int exptected_result) { + TemporaryFile temp_file; + BuildZipArchive( + { + { "META-INF/com/android/metadata", metadata_string }, + }, + temp_file.release(), kCompressStored); + + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + + std::map<std::string, std::string> metadata; + ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata)); + ASSERT_EQ(exptected_result, CheckPackageMetadata(metadata, ota_type)); + CloseArchive(zip); +} + +TEST(InstallTest, CheckPackageMetadata_ota_type) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + + // ota-type must be present + std::string metadata = android::base::Join( + std::vector<std::string>{ + "pre-device=" + device, + "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); + + // Checks if ota-type matches + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, 0); + + test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); +} + +TEST(InstallTest, CheckPackageMetadata_device_type) { + // device type can not be empty + std::string metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=BRICK", + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); + + // device type mismatches + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=BRICK", + "pre-device=dummy_device_type", + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); +} + +TEST(InstallTest, CheckPackageMetadata_serial_number_smoke) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + + // Serial number doesn't need to exist + std::string metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=BRICK", + "pre-device=" + device, + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, 0); + + // Serial number mismatches + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=BRICK", + "pre-device=" + device, + "serialno=dummy_serial", + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); + + std::string serialno = android::base::GetProperty("ro.serialno", ""); + ASSERT_NE("", serialno); + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=BRICK", + "pre-device=" + device, + "serialno=" + serialno, + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, 0); +} + +TEST(InstallTest, CheckPackageMetadata_multiple_serial_number) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + + std::string serialno = android::base::GetProperty("ro.serialno", ""); + ASSERT_NE("", serialno); + + std::vector<std::string> serial_numbers; + // Creates a dummy serial number string. + for (char c = 'a'; c <= 'z'; c++) { + serial_numbers.emplace_back(serialno.size(), c); + } + + // No matched serialno found. + std::string metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=BRICK", + "pre-device=" + device, + "serialno=" + android::base::Join(serial_numbers, '|'), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); + + serial_numbers.emplace_back(serialno); + std::shuffle(serial_numbers.begin(), serial_numbers.end(), std::default_random_engine()); + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=BRICK", + "pre-device=" + device, + "serialno=" + android::base::Join(serial_numbers, '|'), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, 0); +} + +TEST(InstallTest, CheckPackageMetadata_ab_build_version) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + + std::string build_version = android::base::GetProperty("ro.build.version.incremental", ""); + ASSERT_NE("", build_version); + + std::string metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + "pre-build-incremental=" + build_version, + "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, 0); + + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + "pre-build-incremental=dummy_build", + "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); +} + +TEST(InstallTest, CheckPackageMetadata_ab_fingerprint) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + + std::string finger_print = android::base::GetProperty("ro.build.fingerprint", ""); + ASSERT_NE("", finger_print); + + std::string metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + "pre-build=" + finger_print, + "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, 0); + + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + "pre-build=dummy_build_fingerprint", + "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); +} + +TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + + // post timestamp is required for upgrade. + std::string metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); + + // post timestamp should be larger than the timestamp on device. + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + "post-timestamp=0", + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); + + // fingerprint is required for downgrade + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + "post-timestamp=0", + "ota-downgrade=yes", + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); + + std::string finger_print = android::base::GetProperty("ro.build.fingerprint", ""); + ASSERT_NE("", finger_print); + + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + "post-timestamp=0", + "pre-build=" + finger_print, + "ota-downgrade=yes", + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, 0); +} diff --git a/tests/component/resources_test.cpp b/tests/component/resources_test.cpp index 54329db22..d7fdb8fa0 100644 --- a/tests/component/resources_test.cpp +++ b/tests/component/resources_test.cpp @@ -101,7 +101,7 @@ TEST_P(ResourcesTest, ValidateLocale) { EXPECT_LT(0, len) << "Locale string should be non-empty."; EXPECT_NE(0, row[5]) << "Locale string is missing."; - ASSERT_GT(png_->height(), y + 1 + h) << "Locale: " << kLocale << " is not found in the file."; + ASSERT_GE(png_->height(), y + 1 + h) << "Locale: " << kLocale << " is not found in the file."; char* loc = reinterpret_cast<char*>(&row[5]); if (matches_locale(loc, kLocale.c_str())) { EXPECT_TRUE(android::base::StartsWith(loc, kLocale)); diff --git a/tests/component/sideload_test.cpp b/tests/component/sideload_test.cpp index b7109fcc2..d5e074c63 100644 --- a/tests/component/sideload_test.cpp +++ b/tests/component/sideload_test.cpp @@ -21,7 +21,6 @@ #include <android-base/file.h> #include <android-base/strings.h> -#include <android-base/test_utils.h> #include <gtest/gtest.h> #include "fuse_sideload.h" diff --git a/tests/component/uncrypt_test.cpp b/tests/component/uncrypt_test.cpp index 55baca2e3..e97d589a6 100644 --- a/tests/component/uncrypt_test.cpp +++ b/tests/component/uncrypt_test.cpp @@ -26,7 +26,6 @@ #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/properties.h> -#include <android-base/test_utils.h> #include <android-base/unique_fd.h> #include <bootloader_message/bootloader_message.h> #include <gtest/gtest.h> diff --git a/tests/component/update_verifier_test.cpp b/tests/component/update_verifier_test.cpp index a97071635..e27e58c22 100644 --- a/tests/component/update_verifier_test.cpp +++ b/tests/component/update_verifier_test.cpp @@ -16,6 +16,7 @@ #include <update_verifier/update_verifier.h> +#include <functional> #include <string> #include <unordered_map> #include <vector> @@ -23,31 +24,54 @@ #include <android-base/file.h> #include <android-base/properties.h> #include <android-base/strings.h> -#include <android-base/test_utils.h> #include <google/protobuf/repeated_field.h> #include <gtest/gtest.h> #include "care_map.pb.h" +using namespace std::string_literals; + class UpdateVerifierTest : public ::testing::Test { protected: void SetUp() override { std::string verity_mode = android::base::GetProperty("ro.boot.veritymode", ""); verity_supported = android::base::EqualsIgnoreCase(verity_mode, "enforcing"); + + care_map_prefix_ = care_map_dir_.path + "/care_map"s; + care_map_pb_ = care_map_dir_.path + "/care_map.pb"s; + care_map_txt_ = care_map_dir_.path + "/care_map.txt"s; + // Overrides the the care_map_prefix. + verifier_.set_care_map_prefix(care_map_prefix_); + + property_id_ = "ro.build.fingerprint"; + fingerprint_ = android::base::GetProperty(property_id_, ""); + // Overrides the property_reader if we cannot read the given property on the device. + if (fingerprint_.empty()) { + fingerprint_ = "mock_fingerprint"; + verifier_.set_property_reader([](const std::string& /* id */) { return "mock_fingerprint"; }); + } + } + + void TearDown() override { + unlink(care_map_pb_.c_str()); + unlink(care_map_txt_.c_str()); } // Returns a serialized string of the proto3 message according to the given partition info. std::string ConstructProto( std::vector<std::unordered_map<std::string, std::string>>& partitions) { - UpdateVerifier::CareMap result; + recovery_update_verifier::CareMap result; for (const auto& partition : partitions) { - UpdateVerifier::CareMap::PartitionInfo info; + recovery_update_verifier::CareMap::PartitionInfo info; if (partition.find("name") != partition.end()) { info.set_name(partition.at("name")); } if (partition.find("ranges") != partition.end()) { info.set_ranges(partition.at("ranges")); } + if (partition.find("id") != partition.end()) { + info.set_id(partition.at("id")); + } if (partition.find("fingerprint") != partition.end()) { info.set_fingerprint(partition.at("fingerprint")); } @@ -59,15 +83,22 @@ class UpdateVerifierTest : public ::testing::Test { } bool verity_supported; - TemporaryFile care_map_file; + UpdateVerifier verifier_; + + TemporaryDir care_map_dir_; + std::string care_map_prefix_; + std::string care_map_pb_; + std::string care_map_txt_; + + std::string property_id_; + std::string fingerprint_; }; TEST_F(UpdateVerifierTest, verify_image_no_care_map) { - // Non-existing care_map is allowed. - ASSERT_TRUE(verify_image("/doesntexist")); + ASSERT_FALSE(verifier_.ParseCareMap()); } -TEST_F(UpdateVerifierTest, verify_image_smoke) { +TEST_F(UpdateVerifierTest, verify_image_text_format) { // This test relies on dm-verity support. if (!verity_supported) { GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; @@ -75,52 +106,58 @@ TEST_F(UpdateVerifierTest, verify_image_smoke) { } std::string content = "system\n2,0,1"; - ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_file.path)); - ASSERT_TRUE(verify_image(care_map_file.path)); - - // Leading and trailing newlines should be accepted. - ASSERT_TRUE(android::base::WriteStringToFile("\n" + content + "\n\n", care_map_file.path)); - ASSERT_TRUE(verify_image(care_map_file.path)); + ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_txt_)); + // CareMap in text format is no longer supported. + ASSERT_FALSE(verifier_.ParseCareMap()); } TEST_F(UpdateVerifierTest, verify_image_empty_care_map) { - ASSERT_FALSE(verify_image(care_map_file.path)); -} - -TEST_F(UpdateVerifierTest, verify_image_wrong_lines) { - // The care map file can have only 2 / 4 / 6 lines. - ASSERT_TRUE(android::base::WriteStringToFile("line1", care_map_file.path)); - ASSERT_FALSE(verify_image(care_map_file.path)); - - ASSERT_TRUE(android::base::WriteStringToFile("line1\nline2\nline3", care_map_file.path)); - ASSERT_FALSE(verify_image(care_map_file.path)); + ASSERT_FALSE(verifier_.ParseCareMap()); } -TEST_F(UpdateVerifierTest, verify_image_malformed_care_map) { +TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_smoke) { // This test relies on dm-verity support. if (!verity_supported) { GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; return; } - std::string content = "system\n2,1,0"; - ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_file.path)); - ASSERT_FALSE(verify_image(care_map_file.path)); + std::vector<std::unordered_map<std::string, std::string>> partitions = { + { + { "name", "system" }, + { "ranges", "2,0,1" }, + { "id", property_id_ }, + { "fingerprint", fingerprint_ }, + }, + }; + + std::string proto = ConstructProto(partitions); + ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_pb_)); + ASSERT_TRUE(verifier_.ParseCareMap()); + ASSERT_TRUE(verifier_.VerifyPartitions()); } -TEST_F(UpdateVerifierTest, verify_image_legacy_care_map) { +TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_missing_name) { // This test relies on dm-verity support. if (!verity_supported) { GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; return; } - std::string content = "/dev/block/bootdevice/by-name/system\n2,1,0"; - ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_file.path)); - ASSERT_TRUE(verify_image(care_map_file.path)); + std::vector<std::unordered_map<std::string, std::string>> partitions = { + { + { "ranges", "2,0,1" }, + { "id", property_id_ }, + { "fingerprint", fingerprint_ }, + }, + }; + + std::string proto = ConstructProto(partitions); + ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_pb_)); + ASSERT_FALSE(verifier_.ParseCareMap()); } -TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_smoke) { +TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_bad_ranges) { // This test relies on dm-verity support. if (!verity_supported) { GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; @@ -128,15 +165,20 @@ TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_smoke) { } std::vector<std::unordered_map<std::string, std::string>> partitions = { - { { "name", "system" }, { "ranges", "2,0,1" } }, + { + { "name", "system" }, + { "ranges", "3,0,1" }, + { "id", property_id_ }, + { "fingerprint", fingerprint_ }, + }, }; std::string proto = ConstructProto(partitions); - ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_file.path)); - ASSERT_TRUE(verify_image(care_map_file.path)); + ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_pb_)); + ASSERT_FALSE(verifier_.ParseCareMap()); } -TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_missing_name) { +TEST_F(UpdateVerifierTest, verify_image_protobuf_empty_fingerprint) { // This test relies on dm-verity support. if (!verity_supported) { GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; @@ -144,15 +186,18 @@ TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_missing_name) { } std::vector<std::unordered_map<std::string, std::string>> partitions = { - { { "ranges", "2,0,1" } }, + { + { "name", "system" }, + { "ranges", "2,0,1" }, + }, }; std::string proto = ConstructProto(partitions); - ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_file.path)); - ASSERT_FALSE(verify_image(care_map_file.path)); + ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_pb_)); + ASSERT_FALSE(verifier_.ParseCareMap()); } -TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_bad_ranges) { +TEST_F(UpdateVerifierTest, verify_image_protobuf_fingerprint_mismatch) { // This test relies on dm-verity support. if (!verity_supported) { GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; @@ -160,10 +205,15 @@ TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_bad_ranges) { } std::vector<std::unordered_map<std::string, std::string>> partitions = { - { { "name", "system" }, { "ranges", "3,0,1" } }, + { + { "name", "system" }, + { "ranges", "2,0,1" }, + { "id", property_id_ }, + { "fingerprint", "unsupported_fingerprint" }, + }, }; std::string proto = ConstructProto(partitions); - ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_file.path)); - ASSERT_FALSE(verify_image(care_map_file.path)); + ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_pb_)); + ASSERT_FALSE(verifier_.ParseCareMap()); } diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 24c63e776..a0a7b66ab 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -23,6 +23,7 @@ #include <algorithm> #include <memory> #include <string> +#include <string_view> #include <unordered_map> #include <vector> @@ -32,7 +33,6 @@ #include <android-base/properties.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> -#include <android-base/test_utils.h> #include <bootloader_message/bootloader_message.h> #include <brotli/encode.h> #include <bsdiff/bsdiff.h> @@ -134,9 +134,9 @@ static void RunBlockImageUpdate(bool is_verify, const PackageEntries& entries, CloseArchive(handle); } -static std::string get_sha1(const std::string& content) { +static std::string GetSha1(std::string_view content) { uint8_t digest[SHA_DIGEST_LENGTH]; - SHA1(reinterpret_cast<const uint8_t*>(content.c_str()), content.size(), digest); + SHA1(reinterpret_cast<const uint8_t*>(content.data()), content.size(), digest); return print_sha1(digest); } @@ -187,7 +187,7 @@ class UpdaterTest : public ::testing::Test { // Clear partition updated marker if any. std::string updated_marker{ temp_stash_base_.path }; - updated_marker += "/" + get_sha1(image_temp_file_.path) + ".UPDATED"; + updated_marker += "/" + GetSha1(image_temp_file_.path) + ".UPDATED"; ASSERT_TRUE(android::base::RemoveFileIfExists(updated_marker)); } @@ -223,14 +223,14 @@ TEST_F(UpdaterTest, patch_partition_check) { std::string source_content; ASSERT_TRUE(android::base::ReadFileToString(source_file, &source_content)); size_t source_size = source_content.size(); - std::string source_hash = get_sha1(source_content); + std::string source_hash = GetSha1(source_content); Partition source(source_file, source_size, source_hash); std::string target_file = from_testdata_base("recovery.img"); std::string target_content; ASSERT_TRUE(android::base::ReadFileToString(target_file, &target_content)); size_t target_size = target_content.size(); - std::string target_hash = get_sha1(target_content); + std::string target_hash = GetSha1(target_content); Partition target(target_file, target_size, target_hash); // One argument is not valid. @@ -619,54 +619,100 @@ TEST_F(UpdaterTest, block_image_update_parsing_error) { RunBlockImageUpdate(false, entries, image_file_, "", kArgsParsingFailure); } -TEST_F(UpdaterTest, block_image_update_patch_data) { - std::string src_content = std::string(4096, 'a') + std::string(4096, 'c'); - std::string tgt_content = std::string(4096, 'b') + std::string(4096, 'd'); - +// Generates the bsdiff of the given source and target images, and writes the result entries. +// target_blocks specifies the block count to be written into the `bsdiff` command, which may be +// different from the given target size in order to trigger overrun / underrun paths. +static void GetEntriesForBsdiff(std::string_view source, std::string_view target, + size_t target_blocks, PackageEntries* entries) { // Generate the patch data. TemporaryFile patch_file; - ASSERT_EQ(0, - bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src_content.data()), src_content.size(), - reinterpret_cast<const uint8_t*>(tgt_content.data()), tgt_content.size(), - patch_file.path, nullptr)); + ASSERT_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(source.data()), source.size(), + reinterpret_cast<const uint8_t*>(target.data()), target.size(), + patch_file.path, nullptr)); std::string patch_content; ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch_content)); // Create the transfer list that contains a bsdiff. - std::string src_hash = get_sha1(src_content); - std::string tgt_hash = get_sha1(tgt_content); + std::string src_hash = GetSha1(source); + std::string tgt_hash = GetSha1(target); + size_t source_blocks = source.size() / 4096; std::vector<std::string> transfer_list{ // clang-format off "4", - "2", + std::to_string(target_blocks), "0", - "2", - "stash " + src_hash + " 2,0,2", - android::base::StringPrintf("bsdiff 0 %zu %s %s 2,0,2 2 - %s:2,0,2", patch_content.size(), - src_hash.c_str(), tgt_hash.c_str(), src_hash.c_str()), - "free " + src_hash, + "0", + // bsdiff patch_offset patch_length source_hash target_hash target_range source_block_count + // source_range + android::base::StringPrintf("bsdiff 0 %zu %s %s 2,0,%zu %zu 2,0,%zu", patch_content.size(), + src_hash.c_str(), tgt_hash.c_str(), target_blocks, source_blocks, + source_blocks), // clang-format on }; - PackageEntries entries{ + *entries = { { "new_data", "" }, { "patch_data", patch_content }, { "transfer_list", android::base::Join(transfer_list, '\n') }, }; +} - ASSERT_TRUE(android::base::WriteStringToFile(src_content, image_file_)); - +TEST_F(UpdaterTest, block_image_update_patch_data) { + // Both source and target images have 10 blocks. + std::string source = + std::string(4096, 'a') + std::string(4096, 'c') + std::string(4096 * 3, '\0'); + std::string target = + std::string(4096, 'b') + std::string(4096, 'd') + std::string(4096 * 3, '\0'); + ASSERT_TRUE(android::base::WriteStringToFile(source, image_file_)); + + PackageEntries entries; + GetEntriesForBsdiff(std::string_view(source).substr(0, 4096 * 2), + std::string_view(target).substr(0, 4096 * 2), 2, &entries); RunBlockImageUpdate(false, entries, image_file_, "t"); // The update_file should be patched correctly. - std::string updated_content; - ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated_content)); - ASSERT_EQ(tgt_content, updated_content); + std::string updated; + ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated)); + ASSERT_EQ(target, updated); +} + +TEST_F(UpdaterTest, block_image_update_patch_overrun) { + // Both source and target images have 10 blocks. + std::string source = + std::string(4096, 'a') + std::string(4096, 'c') + std::string(4096 * 3, '\0'); + std::string target = + std::string(4096, 'b') + std::string(4096, 'd') + std::string(4096 * 3, '\0'); + ASSERT_TRUE(android::base::WriteStringToFile(source, image_file_)); + + // Provide one less block to trigger the overrun path. + PackageEntries entries; + GetEntriesForBsdiff(std::string_view(source).substr(0, 4096 * 2), + std::string_view(target).substr(0, 4096 * 2), 1, &entries); + + // The update should fail due to overrun. + RunBlockImageUpdate(false, entries, image_file_, "", kPatchApplicationFailure); +} + +TEST_F(UpdaterTest, block_image_update_patch_underrun) { + // Both source and target images have 10 blocks. + std::string source = + std::string(4096, 'a') + std::string(4096, 'c') + std::string(4096 * 3, '\0'); + std::string target = + std::string(4096, 'b') + std::string(4096, 'd') + std::string(4096 * 3, '\0'); + ASSERT_TRUE(android::base::WriteStringToFile(source, image_file_)); + + // Provide one more block to trigger the overrun path. + PackageEntries entries; + GetEntriesForBsdiff(std::string_view(source).substr(0, 4096 * 2), + std::string_view(target).substr(0, 4096 * 2), 3, &entries); + + // The update should fail due to underrun. + RunBlockImageUpdate(false, entries, image_file_, "", kPatchApplicationFailure); } TEST_F(UpdaterTest, block_image_update_fail) { std::string src_content(4096 * 2, 'e'); - std::string src_hash = get_sha1(src_content); + std::string src_hash = GetSha1(src_content); // Stash and free some blocks, then fail the update intentionally. std::vector<std::string> transfer_list{ // clang-format off @@ -692,7 +738,7 @@ TEST_F(UpdaterTest, block_image_update_fail) { RunBlockImageUpdate(false, entries, image_file_, ""); // Updater generates the stash name based on the input file name. - std::string name_digest = get_sha1(image_file_); + std::string name_digest = GetSha1(image_file_); std::string stash_base = std::string(temp_stash_base_.path) + "/" + name_digest; ASSERT_EQ(0, access(stash_base.c_str(), F_OK)); // Expect the stashed blocks to be freed. @@ -796,9 +842,9 @@ TEST_F(UpdaterTest, last_command_update) { std::string block1(4096, '1'); std::string block2(4096, '2'); std::string block3(4096, '3'); - std::string block1_hash = get_sha1(block1); - std::string block2_hash = get_sha1(block2); - std::string block3_hash = get_sha1(block3); + std::string block1_hash = GetSha1(block1); + std::string block2_hash = GetSha1(block2); + std::string block3_hash = GetSha1(block3); // Compose the transfer list to fail the first update. std::vector<std::string> transfer_list_fail{ @@ -864,8 +910,8 @@ TEST_F(UpdaterTest, last_command_update) { TEST_F(UpdaterTest, last_command_update_unresumable) { std::string block1(4096, '1'); std::string block2(4096, '2'); - std::string block1_hash = get_sha1(block1); - std::string block2_hash = get_sha1(block2); + std::string block1_hash = GetSha1(block1); + std::string block2_hash = GetSha1(block2); // Construct an unresumable update with source blocks mismatch. std::vector<std::string> transfer_list_unresumable{ @@ -901,9 +947,9 @@ TEST_F(UpdaterTest, last_command_verify) { std::string block1(4096, '1'); std::string block2(4096, '2'); std::string block3(4096, '3'); - std::string block1_hash = get_sha1(block1); - std::string block2_hash = get_sha1(block2); - std::string block3_hash = get_sha1(block3); + std::string block1_hash = GetSha1(block1); + std::string block2_hash = GetSha1(block2); + std::string block3_hash = GetSha1(block3); std::vector<std::string> transfer_list_verify{ // clang-format off @@ -972,7 +1018,7 @@ class ResumableUpdaterTest : public testing::TestWithParam<size_t> { // Clear partition updated marker if any. std::string updated_marker{ temp_stash_base_.path }; - updated_marker += "/" + get_sha1(image_temp_file_.path) + ".UPDATED"; + updated_marker += "/" + GetSha1(image_temp_file_.path) + ".UPDATED"; ASSERT_TRUE(android::base::RemoveFileIfExists(updated_marker)); } @@ -1003,10 +1049,10 @@ static std::vector<std::string> GenerateTransferList() { std::string i(4096, 'i'); std::string zero(4096, '\0'); - std::string a_hash = get_sha1(a); - std::string b_hash = get_sha1(b); - std::string c_hash = get_sha1(c); - std::string e_hash = get_sha1(e); + std::string a_hash = GetSha1(a); + std::string b_hash = GetSha1(b); + std::string c_hash = GetSha1(c); + std::string e_hash = GetSha1(e); auto loc = [](const std::string& range_text) { std::vector<std::string> pieces = android::base::Split(range_text, "-"); @@ -1027,8 +1073,8 @@ static std::vector<std::string> GenerateTransferList() { // patch 1: "b d c" -> "g" TemporaryFile patch_file_bdc_g; std::string bdc = b + d + c; - std::string bdc_hash = get_sha1(bdc); - std::string g_hash = get_sha1(g); + std::string bdc_hash = GetSha1(bdc); + std::string g_hash = GetSha1(g); CHECK_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(bdc.data()), bdc.size(), reinterpret_cast<const uint8_t*>(g.data()), g.size(), patch_file_bdc_g.path, nullptr)); @@ -1038,9 +1084,9 @@ static std::vector<std::string> GenerateTransferList() { // patch 2: "a b c d" -> "d c b" TemporaryFile patch_file_abcd_dcb; std::string abcd = a + b + c + d; - std::string abcd_hash = get_sha1(abcd); + std::string abcd_hash = GetSha1(abcd); std::string dcb = d + c + b; - std::string dcb_hash = get_sha1(dcb); + std::string dcb_hash = GetSha1(dcb); CHECK_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(abcd.data()), abcd.size(), reinterpret_cast<const uint8_t*>(dcb.data()), dcb.size(), patch_file_abcd_dcb.path, nullptr)); diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp index 3246ecdbc..c26d76d73 100644 --- a/tests/component/verifier_test.cpp +++ b/tests/component/verifier_test.cpp @@ -26,32 +26,232 @@ #include <android-base/file.h> #include <android-base/stringprintf.h> -#include <android-base/test_utils.h> +#include <android-base/strings.h> +#include <android-base/unique_fd.h> #include <gtest/gtest.h> +#include <openssl/bn.h> +#include <openssl/ec.h> +#include <openssl/nid.h> +#include <ziparchive/zip_writer.h> #include "common/test_constants.h" #include "otautil/sysutil.h" +#include "package.h" #include "verifier.h" using namespace std::string_literals; +static void LoadKeyFromFile(const std::string& file_name, Certificate* cert) { + std::string testkey_string; + ASSERT_TRUE(android::base::ReadFileToString(file_name, &testkey_string)); + ASSERT_TRUE(LoadCertificateFromBuffer( + std::vector<uint8_t>(testkey_string.begin(), testkey_string.end()), cert)); +} + +static void VerifyFile(const std::string& content, const std::vector<Certificate>& keys, + int expected) { + auto package = + Package::CreateMemoryPackage(std::vector<uint8_t>(content.begin(), content.end()), nullptr); + ASSERT_NE(nullptr, package); + + ASSERT_EQ(expected, verify_file(package.get(), keys)); +} + +static void VerifyPackageWithCertificates(const std::string& name, + const std::vector<Certificate>& certs) { + std::string path = from_testdata_base(name); + auto package = Package::CreateMemoryPackage(path, nullptr); + ASSERT_NE(nullptr, package); + + ASSERT_EQ(VERIFY_SUCCESS, verify_file(package.get(), certs)); +} + +static void VerifyPackageWithSingleCertificate(const std::string& name, Certificate&& cert) { + std::vector<Certificate> certs; + certs.emplace_back(std::move(cert)); + VerifyPackageWithCertificates(name, certs); +} + +static void BuildCertificateArchive(const std::vector<std::string>& file_names, int fd) { + FILE* zip_file_ptr = fdopen(fd, "wb"); + ZipWriter zip_writer(zip_file_ptr); + + for (const auto& name : file_names) { + std::string content; + ASSERT_TRUE(android::base::ReadFileToString(name, &content)); + + // Makes sure the zip entry name has the correct suffix. + std::string entry_name = name; + if (!android::base::EndsWith(entry_name, "x509.pem")) { + entry_name += "x509.pem"; + } + ASSERT_EQ(0, zip_writer.StartEntry(entry_name.c_str(), ZipWriter::kCompress)); + ASSERT_EQ(0, zip_writer.WriteBytes(content.data(), content.size())); + ASSERT_EQ(0, zip_writer.FinishEntry()); + } + + ASSERT_EQ(0, zip_writer.Finish()); + ASSERT_EQ(0, fclose(zip_file_ptr)); +} + +TEST(VerifierTest, LoadCertificateFromBuffer_failure) { + Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + std::string testkey_string; + ASSERT_TRUE( + android::base::ReadFileToString(from_testdata_base("testkey_v1.txt"), &testkey_string)); + ASSERT_FALSE(LoadCertificateFromBuffer( + std::vector<uint8_t>(testkey_string.begin(), testkey_string.end()), &cert)); +} + +TEST(VerifierTest, LoadCertificateFromBuffer_sha1_exponent3) { + Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + LoadKeyFromFile(from_testdata_base("testkey_v1.x509.pem"), &cert); + + ASSERT_EQ(SHA_DIGEST_LENGTH, cert.hash_len); + ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); + ASSERT_EQ(nullptr, cert.ec); + + VerifyPackageWithSingleCertificate("otasigned_v1.zip", std::move(cert)); +} + +TEST(VerifierTest, LoadCertificateFromBuffer_sha1_exponent65537) { + Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + LoadKeyFromFile(from_testdata_base("testkey_v2.x509.pem"), &cert); + + ASSERT_EQ(SHA_DIGEST_LENGTH, cert.hash_len); + ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); + ASSERT_EQ(nullptr, cert.ec); + + VerifyPackageWithSingleCertificate("otasigned_v2.zip", std::move(cert)); +} + +TEST(VerifierTest, LoadCertificateFromBuffer_sha256_exponent3) { + Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + LoadKeyFromFile(from_testdata_base("testkey_v3.x509.pem"), &cert); + + ASSERT_EQ(SHA256_DIGEST_LENGTH, cert.hash_len); + ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); + ASSERT_EQ(nullptr, cert.ec); + + VerifyPackageWithSingleCertificate("otasigned_v3.zip", std::move(cert)); +} + +TEST(VerifierTest, LoadCertificateFromBuffer_sha256_exponent65537) { + Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + LoadKeyFromFile(from_testdata_base("testkey_v4.x509.pem"), &cert); + + ASSERT_EQ(SHA256_DIGEST_LENGTH, cert.hash_len); + ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); + ASSERT_EQ(nullptr, cert.ec); + + VerifyPackageWithSingleCertificate("otasigned_v4.zip", std::move(cert)); +} + +TEST(VerifierTest, LoadCertificateFromBuffer_sha256_ec256bits) { + Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + LoadKeyFromFile(from_testdata_base("testkey_v5.x509.pem"), &cert); + + ASSERT_EQ(SHA256_DIGEST_LENGTH, cert.hash_len); + ASSERT_EQ(Certificate::KEY_TYPE_EC, cert.key_type); + ASSERT_EQ(nullptr, cert.rsa); + + VerifyPackageWithSingleCertificate("otasigned_v5.zip", std::move(cert)); +} + +TEST(VerifierTest, LoadCertificateFromBuffer_check_rsa_keys) { + std::unique_ptr<RSA, RSADeleter> rsa(RSA_new()); + std::unique_ptr<BIGNUM, decltype(&BN_free)> exponent(BN_new(), BN_free); + BN_set_word(exponent.get(), 3); + RSA_generate_key_ex(rsa.get(), 2048, exponent.get(), nullptr); + ASSERT_TRUE(CheckRSAKey(rsa)); + + // Exponent is expected to be 3 or 65537 + BN_set_word(exponent.get(), 17); + RSA_generate_key_ex(rsa.get(), 2048, exponent.get(), nullptr); + ASSERT_FALSE(CheckRSAKey(rsa)); + + // Modulus is expected to be 2048. + BN_set_word(exponent.get(), 3); + RSA_generate_key_ex(rsa.get(), 1024, exponent.get(), nullptr); + ASSERT_FALSE(CheckRSAKey(rsa)); +} + +TEST(VerifierTest, LoadCertificateFromBuffer_check_ec_keys) { + std::unique_ptr<EC_KEY, ECKEYDeleter> ec(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + ASSERT_EQ(1, EC_KEY_generate_key(ec.get())); + ASSERT_TRUE(CheckECKey(ec)); + + // Expects 256-bit EC key with curve NIST P-256 + ec.reset(EC_KEY_new_by_curve_name(NID_secp224r1)); + ASSERT_EQ(1, EC_KEY_generate_key(ec.get())); + ASSERT_FALSE(CheckECKey(ec)); +} + +TEST(VerifierTest, LoadKeysFromZipfile_empty_archive) { + TemporaryFile otacerts; + BuildCertificateArchive({}, otacerts.release()); + std::vector<Certificate> certs = LoadKeysFromZipfile(otacerts.path); + ASSERT_TRUE(certs.empty()); +} + +TEST(VerifierTest, LoadKeysFromZipfile_single_key) { + TemporaryFile otacerts; + BuildCertificateArchive({ from_testdata_base("testkey_v1.x509.pem") }, otacerts.release()); + std::vector<Certificate> certs = LoadKeysFromZipfile(otacerts.path); + ASSERT_EQ(1, certs.size()); + + VerifyPackageWithCertificates("otasigned_v1.zip", certs); +} + +TEST(VerifierTest, LoadKeysFromZipfile_corrupted_key) { + TemporaryFile corrupted_key; + std::string content; + ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v1.x509.pem"), &content)); + content = "random-contents" + content; + ASSERT_TRUE(android::base::WriteStringToFd(content, corrupted_key.release())); + + TemporaryFile otacerts; + BuildCertificateArchive({ from_testdata_base("testkey_v2.x509.pem"), corrupted_key.path }, + otacerts.release()); + std::vector<Certificate> certs = LoadKeysFromZipfile(otacerts.path); + ASSERT_EQ(0, certs.size()); +} + +TEST(VerifierTest, LoadKeysFromZipfile_multiple_key) { + TemporaryFile otacerts; + BuildCertificateArchive( + { + from_testdata_base("testkey_v3.x509.pem"), + from_testdata_base("testkey_v4.x509.pem"), + from_testdata_base("testkey_v5.x509.pem"), + + }, + otacerts.release()); + std::vector<Certificate> certs = LoadKeysFromZipfile(otacerts.path); + ASSERT_EQ(3, certs.size()); + + VerifyPackageWithCertificates("otasigned_v3.zip", certs); + VerifyPackageWithCertificates("otasigned_v4.zip", certs); + VerifyPackageWithCertificates("otasigned_v5.zip", certs); +} + class VerifierTest : public testing::TestWithParam<std::vector<std::string>> { protected: void SetUp() override { std::vector<std::string> args = GetParam(); - std::string package = from_testdata_base(args[0]); - if (!memmap.MapFile(package)) { - FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n"; - } + std::string path = from_testdata_base(args[0]); + package_ = Package::CreateMemoryPackage(path, nullptr); + ASSERT_NE(nullptr, package_); for (auto it = ++args.cbegin(); it != args.cend(); ++it) { - std::string public_key_file = from_testdata_base("testkey_" + *it + ".txt"); - ASSERT_TRUE(load_keys(public_key_file.c_str(), certs)); + std::string public_key_file = from_testdata_base("testkey_" + *it + ".x509.pem"); + certs_.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + LoadKeyFromFile(public_key_file, &certs_.back()); } } - MemMapping memmap; - std::vector<Certificate> certs; + std::unique_ptr<Package> package_; + std::vector<Certificate> certs_; }; class VerifierSuccessTest : public VerifierTest { @@ -60,70 +260,10 @@ class VerifierSuccessTest : public VerifierTest { class VerifierFailureTest : public VerifierTest { }; -TEST(VerifierTest, load_keys_multiple_keys) { - std::string testkey_v4; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v4.txt"), &testkey_v4)); - - std::string testkey_v3; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3)); - - std::string keys = testkey_v4 + "," + testkey_v3 + "," + testkey_v4; - TemporaryFile key_file1; - ASSERT_TRUE(android::base::WriteStringToFile(keys, key_file1.path)); - std::vector<Certificate> certs; - ASSERT_TRUE(load_keys(key_file1.path, certs)); - ASSERT_EQ(3U, certs.size()); -} - -TEST(VerifierTest, load_keys_invalid_keys) { - std::vector<Certificate> certs; - ASSERT_FALSE(load_keys("/doesntexist", certs)); - - // Empty file. - TemporaryFile key_file1; - ASSERT_FALSE(load_keys(key_file1.path, certs)); - - // Invalid contents. - ASSERT_TRUE(android::base::WriteStringToFile("invalid", key_file1.path)); - ASSERT_FALSE(load_keys(key_file1.path, certs)); - - std::string testkey_v4; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v4.txt"), &testkey_v4)); - - // Invalid key version: "v4 ..." => "v6 ...". - std::string invalid_key2(testkey_v4); - invalid_key2[1] = '6'; - TemporaryFile key_file2; - ASSERT_TRUE(android::base::WriteStringToFile(invalid_key2, key_file2.path)); - ASSERT_FALSE(load_keys(key_file2.path, certs)); - - // Invalid key content: inserted extra bytes ",2209831334". - std::string invalid_key3(testkey_v4); - invalid_key3.insert(invalid_key2.size() - 2, ",2209831334"); - TemporaryFile key_file3; - ASSERT_TRUE(android::base::WriteStringToFile(invalid_key3, key_file3.path)); - ASSERT_FALSE(load_keys(key_file3.path, certs)); - - // Invalid key: the last key must not end with an extra ','. - std::string invalid_key4 = testkey_v4 + ","; - TemporaryFile key_file4; - ASSERT_TRUE(android::base::WriteStringToFile(invalid_key4, key_file4.path)); - ASSERT_FALSE(load_keys(key_file4.path, certs)); - - // Invalid key separator. - std::string invalid_key5 = testkey_v4 + ";" + testkey_v4; - TemporaryFile key_file5; - ASSERT_TRUE(android::base::WriteStringToFile(invalid_key5, key_file5.path)); - ASSERT_FALSE(load_keys(key_file5.path, certs)); -} - TEST(VerifierTest, BadPackage_AlteredFooter) { - std::string testkey_v3; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3)); - TemporaryFile key_file1; - ASSERT_TRUE(android::base::WriteStringToFile(testkey_v3, key_file1.path)); std::vector<Certificate> certs; - ASSERT_TRUE(load_keys(key_file1.path, certs)); + certs.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + LoadKeyFromFile(from_testdata_base("testkey_v3.x509.pem"), &certs.back()); std::string package; ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("otasigned_v3.zip"), &package)); @@ -131,18 +271,13 @@ TEST(VerifierTest, BadPackage_AlteredFooter) { // Alter the footer. package[package.size() - 5] = '\x05'; - ASSERT_EQ(VERIFY_FAILURE, - verify_file(reinterpret_cast<const unsigned char*>(package.data()), package.size(), - certs)); + VerifyFile(package, certs, VERIFY_FAILURE); } TEST(VerifierTest, BadPackage_AlteredContent) { - std::string testkey_v3; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3)); - TemporaryFile key_file1; - ASSERT_TRUE(android::base::WriteStringToFile(testkey_v3, key_file1.path)); std::vector<Certificate> certs; - ASSERT_TRUE(load_keys(key_file1.path, certs)); + certs.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + LoadKeyFromFile(from_testdata_base("testkey_v3.x509.pem"), &certs.back()); std::string package; ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("otasigned_v3.zip"), &package)); @@ -151,38 +286,29 @@ TEST(VerifierTest, BadPackage_AlteredContent) { // Alter the content. std::string altered1(package); altered1[50] += 1; - ASSERT_EQ(VERIFY_FAILURE, - verify_file(reinterpret_cast<const unsigned char*>(altered1.data()), altered1.size(), - certs)); + VerifyFile(altered1, certs, VERIFY_FAILURE); std::string altered2(package); altered2[10] += 1; - ASSERT_EQ(VERIFY_FAILURE, - verify_file(reinterpret_cast<const unsigned char*>(altered2.data()), altered2.size(), - certs)); + VerifyFile(altered2, certs, VERIFY_FAILURE); } TEST(VerifierTest, BadPackage_SignatureStartOutOfBounds) { - std::string testkey_v3; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3)); - - TemporaryFile key_file; - ASSERT_TRUE(android::base::WriteStringToFile(testkey_v3, key_file.path)); std::vector<Certificate> certs; - ASSERT_TRUE(load_keys(key_file.path, certs)); + certs.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + LoadKeyFromFile(from_testdata_base("testkey_v3.x509.pem"), &certs.back()); // Signature start is 65535 (0xffff) while comment size is 0 (Bug: 31914369). std::string package = "\x50\x4b\x05\x06"s + std::string(12, '\0') + "\xff\xff\xff\xff\x00\x00"s; - ASSERT_EQ(VERIFY_FAILURE, verify_file(reinterpret_cast<const unsigned char*>(package.data()), - package.size(), certs)); + VerifyFile(package, certs, VERIFY_FAILURE); } TEST_P(VerifierSuccessTest, VerifySucceed) { - ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs, nullptr), VERIFY_SUCCESS); + ASSERT_EQ(VERIFY_SUCCESS, verify_file(package_.get(), certs_)); } TEST_P(VerifierFailureTest, VerifyFailure) { - ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs, nullptr), VERIFY_FAILURE); + ASSERT_EQ(VERIFY_FAILURE, verify_file(package_.get(), certs_)); } INSTANTIATE_TEST_CASE_P(SingleKeySuccess, VerifierSuccessTest, |