summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Android.mk6
-rw-r--r--tests/component/update_verifier_test.cpp113
-rw-r--r--tests/component/updater_test.cpp81
-rw-r--r--tests/unit/commands_test.cpp1
-rw-r--r--tests/unit/screen_ui_test.cpp39
5 files changed, 218 insertions, 22 deletions
diff --git a/tests/Android.mk b/tests/Android.mk
index 1fa259ebc..daf4853b9 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -113,7 +113,8 @@ LOCAL_SRC_FILES := \
component/verifier_test.cpp
LOCAL_SHARED_LIBRARIES := \
- libhidlbase
+ libhidlbase \
+ libprotobuf-cpp-lite
tune2fs_static_libraries := \
libext2_com_err \
@@ -136,6 +137,7 @@ libupdater_static_libraries := \
libext4_utils \
libfec \
libfec_rs \
+ libverity_tree \
libfs_mgr \
libgtest_prod \
liblog \
@@ -154,10 +156,10 @@ libupdater_static_libraries := \
librecovery_static_libraries := \
librecovery \
- $(TARGET_RECOVERY_UI_LIB) \
libbootloader_message \
libfusesideload \
libminadbd \
+ librecovery_ui_default \
librecovery_ui \
libminui \
libverifier \
diff --git a/tests/component/update_verifier_test.cpp b/tests/component/update_verifier_test.cpp
index f6ef6dcfd..a97071635 100644
--- a/tests/component/update_verifier_test.cpp
+++ b/tests/component/update_verifier_test.cpp
@@ -14,14 +14,20 @@
* limitations under the License.
*/
+#include <update_verifier/update_verifier.h>
+
#include <string>
+#include <unordered_map>
+#include <vector>
#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 <update_verifier/update_verifier.h>
+
+#include "care_map.pb.h"
class UpdateVerifierTest : public ::testing::Test {
protected:
@@ -30,7 +36,30 @@ class UpdateVerifierTest : public ::testing::Test {
verity_supported = android::base::EqualsIgnoreCase(verity_mode, "enforcing");
}
+ // 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;
+ for (const auto& partition : partitions) {
+ UpdateVerifier::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("fingerprint") != partition.end()) {
+ info.set_fingerprint(partition.at("fingerprint"));
+ }
+
+ *result.add_partitions() = info;
+ }
+
+ return result.SerializeAsString();
+ }
+
bool verity_supported;
+ TemporaryFile care_map_file;
};
TEST_F(UpdateVerifierTest, verify_image_no_care_map) {
@@ -45,26 +74,26 @@ TEST_F(UpdateVerifierTest, verify_image_smoke) {
return;
}
- TemporaryFile temp_file;
std::string content = "system\n2,0,1";
- ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
- ASSERT_TRUE(verify_image(temp_file.path));
+ 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", temp_file.path));
- ASSERT_TRUE(verify_image(temp_file.path));
+ ASSERT_TRUE(android::base::WriteStringToFile("\n" + content + "\n\n", care_map_file.path));
+ ASSERT_TRUE(verify_image(care_map_file.path));
+}
+
+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.
- TemporaryFile temp_file;
- ASSERT_FALSE(verify_image(temp_file.path));
+ ASSERT_TRUE(android::base::WriteStringToFile("line1", care_map_file.path));
+ ASSERT_FALSE(verify_image(care_map_file.path));
- ASSERT_TRUE(android::base::WriteStringToFile("line1", temp_file.path));
- ASSERT_FALSE(verify_image(temp_file.path));
-
- ASSERT_TRUE(android::base::WriteStringToFile("line1\nline2\nline3", temp_file.path));
- ASSERT_FALSE(verify_image(temp_file.path));
+ ASSERT_TRUE(android::base::WriteStringToFile("line1\nline2\nline3", care_map_file.path));
+ ASSERT_FALSE(verify_image(care_map_file.path));
}
TEST_F(UpdateVerifierTest, verify_image_malformed_care_map) {
@@ -74,10 +103,9 @@ TEST_F(UpdateVerifierTest, verify_image_malformed_care_map) {
return;
}
- TemporaryFile temp_file;
std::string content = "system\n2,1,0";
- ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
- ASSERT_FALSE(verify_image(temp_file.path));
+ ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_file.path));
+ ASSERT_FALSE(verify_image(care_map_file.path));
}
TEST_F(UpdateVerifierTest, verify_image_legacy_care_map) {
@@ -87,8 +115,55 @@ TEST_F(UpdateVerifierTest, verify_image_legacy_care_map) {
return;
}
- TemporaryFile temp_file;
std::string content = "/dev/block/bootdevice/by-name/system\n2,1,0";
- ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
- ASSERT_TRUE(verify_image(temp_file.path));
+ ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_file.path));
+ ASSERT_TRUE(verify_image(care_map_file.path));
+}
+
+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::vector<std::unordered_map<std::string, std::string>> partitions = {
+ { { "name", "system" }, { "ranges", "2,0,1" } },
+ };
+
+ std::string proto = ConstructProto(partitions);
+ ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_file.path));
+ ASSERT_TRUE(verify_image(care_map_file.path));
+}
+
+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::vector<std::unordered_map<std::string, std::string>> partitions = {
+ { { "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));
+}
+
+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.";
+ return;
+ }
+
+ std::vector<std::unordered_map<std::string, std::string>> partitions = {
+ { { "name", "system" }, { "ranges", "3,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));
}
diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp
index 9fcf17f13..248b469b0 100644
--- a/tests/component/updater_test.cpp
+++ b/tests/component/updater_test.cpp
@@ -37,6 +37,7 @@
#include <brotli/encode.h>
#include <bsdiff/bsdiff.h>
#include <gtest/gtest.h>
+#include <verity/hash_tree_builder.h>
#include <ziparchive/zip_archive.h>
#include <ziparchive/zip_writer.h>
@@ -389,6 +390,86 @@ TEST_F(UpdaterTest, read_file) {
expect("", script, kNoCause);
}
+TEST_F(UpdaterTest, compute_hash_tree_smoke) {
+ std::string data;
+ for (unsigned char i = 0; i < 128; i++) {
+ data += std::string(4096, i);
+ }
+ // Appends an additional block for verity data.
+ data += std::string(4096, 0);
+ ASSERT_EQ(129 * 4096, data.size());
+ ASSERT_TRUE(android::base::WriteStringToFile(data, image_file_));
+
+ std::string salt = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7";
+ std::string expected_root_hash =
+ "7e0a8d8747f54384014ab996f5b2dc4eb7ff00c630eede7134c9e3f05c0dd8ca";
+ // hash_tree_ranges, source_ranges, hash_algorithm, salt_hex, root_hash
+ std::vector<std::string> tokens{ "compute_hash_tree", "2,128,129", "2,0,128", "sha256", salt,
+ expected_root_hash };
+ std::string hash_tree_command = android::base::Join(tokens, " ");
+
+ std::vector<std::string> transfer_list{
+ "4", "2", "0", "2", hash_tree_command,
+ };
+
+ PackageEntries entries{
+ { "new_data", "" },
+ { "patch_data", "" },
+ { "transfer_list", android::base::Join(transfer_list, "\n") },
+ };
+
+ RunBlockImageUpdate(false, entries, image_file_, "t");
+
+ std::string updated;
+ ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated));
+ ASSERT_EQ(129 * 4096, updated.size());
+ ASSERT_EQ(data.substr(0, 128 * 4096), updated.substr(0, 128 * 4096));
+
+ // Computes the SHA256 of the salt + hash_tree_data and expects the result to match with the
+ // root_hash.
+ std::vector<unsigned char> salt_bytes;
+ ASSERT_TRUE(HashTreeBuilder::ParseBytesArrayFromString(salt, &salt_bytes));
+ std::vector<unsigned char> hash_tree = std::move(salt_bytes);
+ hash_tree.insert(hash_tree.end(), updated.begin() + 128 * 4096, updated.end());
+
+ std::vector<unsigned char> digest(SHA256_DIGEST_LENGTH);
+ SHA256(hash_tree.data(), hash_tree.size(), digest.data());
+ ASSERT_EQ(expected_root_hash, HashTreeBuilder::BytesArrayToString(digest));
+}
+
+TEST_F(UpdaterTest, compute_hash_tree_root_mismatch) {
+ std::string data;
+ for (size_t i = 0; i < 128; i++) {
+ data += std::string(4096, i);
+ }
+ // Appends an additional block for verity data.
+ data += std::string(4096, 0);
+ ASSERT_EQ(129 * 4096, data.size());
+ // Corrupts one bit
+ data[4096] = 'A';
+ ASSERT_TRUE(android::base::WriteStringToFile(data, image_file_));
+
+ std::string salt = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7";
+ std::string expected_root_hash =
+ "7e0a8d8747f54384014ab996f5b2dc4eb7ff00c630eede7134c9e3f05c0dd8ca";
+ // hash_tree_ranges, source_ranges, hash_algorithm, salt_hex, root_hash
+ std::vector<std::string> tokens{ "compute_hash_tree", "2,128,129", "2,0,128", "sha256", salt,
+ expected_root_hash };
+ std::string hash_tree_command = android::base::Join(tokens, " ");
+
+ std::vector<std::string> transfer_list{
+ "4", "2", "0", "2", hash_tree_command,
+ };
+
+ PackageEntries entries{
+ { "new_data", "" },
+ { "patch_data", "" },
+ { "transfer_list", android::base::Join(transfer_list, "\n") },
+ };
+
+ RunBlockImageUpdate(false, entries, image_file_, "", kHashTreeComputationFailure);
+}
+
TEST_F(UpdaterTest, write_value) {
// write_value() expects two arguments.
expect(nullptr, "write_value()", kArgsParsingFailure);
diff --git a/tests/unit/commands_test.cpp b/tests/unit/commands_test.cpp
index 3daa58f33..9679a9e73 100644
--- a/tests/unit/commands_test.cpp
+++ b/tests/unit/commands_test.cpp
@@ -30,6 +30,7 @@ TEST(CommandsTest, ParseType) {
ASSERT_EQ(Command::Type::IMGDIFF, Command::ParseType("imgdiff"));
ASSERT_EQ(Command::Type::STASH, Command::ParseType("stash"));
ASSERT_EQ(Command::Type::FREE, Command::ParseType("free"));
+ ASSERT_EQ(Command::Type::COMPUTE_HASH_TREE, Command::ParseType("compute_hash_tree"));
}
TEST(CommandsTest, ParseType_InvalidCommand) {
diff --git a/tests/unit/screen_ui_test.cpp b/tests/unit/screen_ui_test.cpp
index 4c0a868f0..7d97a006b 100644
--- a/tests/unit/screen_ui_test.cpp
+++ b/tests/unit/screen_ui_test.cpp
@@ -264,6 +264,10 @@ int TestableScreenRecoveryUI::KeyHandler(int key, bool) const {
}
int TestableScreenRecoveryUI::WaitKey() {
+ if (IsKeyInterrupted()) {
+ return static_cast<int>(RecoveryUI::KeyError::INTERRUPTED);
+ }
+
CHECK_LT(key_buffer_index_, key_buffer_.size());
return static_cast<int>(key_buffer_[key_buffer_index_++]);
}
@@ -391,7 +395,8 @@ TEST_F(ScreenRecoveryUITest, ShowMenu_TimedOut) {
ui_->SetKeyBuffer({
KeyCode::TIMEOUT,
});
- ASSERT_EQ(static_cast<size_t>(-1), ui_->ShowMenu(HEADERS, ITEMS, 3, true, nullptr));
+ ASSERT_EQ(static_cast<size_t>(RecoveryUI::KeyError::TIMED_OUT),
+ ui_->ShowMenu(HEADERS, ITEMS, 3, true, nullptr));
}
TEST_F(ScreenRecoveryUITest, ShowMenu_TimedOut_TextWasEverVisible) {
@@ -412,6 +417,38 @@ TEST_F(ScreenRecoveryUITest, ShowMenu_TimedOut_TextWasEverVisible) {
std::placeholders::_1, std::placeholders::_2)));
}
+TEST_F(ScreenRecoveryUITest, ShowMenuWithInterrupt) {
+ RETURN_IF_NO_GRAPHICS;
+
+ ASSERT_TRUE(ui_->Init(kTestLocale));
+ ui_->SetKeyBuffer({
+ KeyCode::UP,
+ KeyCode::DOWN,
+ KeyCode::UP,
+ KeyCode::DOWN,
+ KeyCode::ENTER,
+ });
+
+ ui_->InterruptKey();
+ ASSERT_EQ(static_cast<size_t>(RecoveryUI::KeyError::INTERRUPTED),
+ ui_->ShowMenu(HEADERS, ITEMS, 3, true,
+ std::bind(&TestableScreenRecoveryUI::KeyHandler, ui_.get(),
+ std::placeholders::_1, std::placeholders::_2)));
+
+ ui_->SetKeyBuffer({
+ KeyCode::UP,
+ KeyCode::UP,
+ KeyCode::NO_OP,
+ KeyCode::NO_OP,
+ KeyCode::UP,
+ KeyCode::ENTER,
+ });
+ ASSERT_EQ(static_cast<size_t>(RecoveryUI::KeyError::INTERRUPTED),
+ ui_->ShowMenu(HEADERS, ITEMS, 0, true,
+ std::bind(&TestableScreenRecoveryUI::KeyHandler, ui_.get(),
+ std::placeholders::_1, std::placeholders::_2)));
+}
+
TEST_F(ScreenRecoveryUITest, LoadAnimation) {
RETURN_IF_NO_GRAPHICS;