From 69ffa15338503376c2dad1e33d23ebb42fba60d1 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 1 Aug 2018 16:40:00 -0700 Subject: Add an updater function to compute hash tree The new command is part of the transfer.list and allows us to compute the hash tree on non-ab devices. The required arguments for the hash_tree computation are: hash_tree_ranges source_ranges hash_algorithm salt_hex root_hash Bug: 25170618 Test: unit tests pass; run simulator with compute_hash_tree Change-Id: I8ff0d582cc8adabb8a060db7845f38b35b28e62c --- updater/Android.mk | 1 + updater/blockimg.cpp | 141 ++++++++++++++++++++++++++++++++----- updater/commands.cpp | 3 + updater/include/private/commands.h | 5 ++ 4 files changed, 132 insertions(+), 18 deletions(-) (limited to 'updater') diff --git a/updater/Android.mk b/updater/Android.mk index ac9aecb04..78d0bd451 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -34,6 +34,7 @@ updater_common_static_libraries := \ libext4_utils \ libfec \ libfec_rs \ + libverity_tree \ libfs_mgr \ libgtest_prod \ liblog \ diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 2a2ab19a3..96b2d9feb 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include "edify/expr.h" @@ -1495,6 +1496,105 @@ static int PerformCommandAbort(CommandParameters&) { return -1; } +// Computes the hash_tree bytes based on the parameters, checks if the root hash of the tree +// matches the expected hash and writes the result to the specified range on the block_device. +// Hash_tree computation arguments: +// hash_tree_ranges +// source_ranges +// hash_algorithm +// salt_hex +// root_hash +static int PerformCommandComputeHashTree(CommandParameters& params) { + if (params.cpos + 5 != params.tokens.size()) { + LOG(ERROR) << "Invaild arguments count in hash computation " << params.cmdline; + return -1; + } + + // Expects the hash_tree data to be contiguous. + RangeSet hash_tree_ranges = RangeSet::Parse(params.tokens[params.cpos++]); + if (!hash_tree_ranges || hash_tree_ranges.size() != 1) { + LOG(ERROR) << "Invalid hash tree ranges in " << params.cmdline; + return -1; + } + + RangeSet source_ranges = RangeSet::Parse(params.tokens[params.cpos++]); + if (!source_ranges) { + LOG(ERROR) << "Invalid source ranges in " << params.cmdline; + return -1; + } + + auto hash_function = HashTreeBuilder::HashFunction(params.tokens[params.cpos++]); + if (hash_function == nullptr) { + LOG(ERROR) << "Invalid hash algorithm in " << params.cmdline; + return -1; + } + + std::vector salt; + std::string salt_hex = params.tokens[params.cpos++]; + if (salt_hex.empty() || !HashTreeBuilder::ParseBytesArrayFromString(salt_hex, &salt)) { + LOG(ERROR) << "Failed to parse salt in " << params.cmdline; + return -1; + } + + std::string expected_root_hash = params.tokens[params.cpos++]; + if (expected_root_hash.empty()) { + LOG(ERROR) << "Invalid root hash in " << params.cmdline; + return -1; + } + + // Starts the hash_tree computation. + HashTreeBuilder builder(BLOCKSIZE, hash_function); + if (!builder.Initialize(source_ranges.blocks() * BLOCKSIZE, salt)) { + LOG(ERROR) << "Failed to initialize hash tree computation, source " << source_ranges.ToString() + << ", salt " << salt_hex; + return -1; + } + + // Iterates through every block in the source_ranges and updates the hash tree structure + // accordingly. + for (const auto& range : source_ranges) { + uint8_t buffer[BLOCKSIZE]; + if (!check_lseek(params.fd, static_cast(range.first) * BLOCKSIZE, SEEK_SET)) { + PLOG(ERROR) << "Failed to seek to block: " << range.first; + return -1; + } + + for (size_t i = range.first; i < range.second; i++) { + if (read_all(params.fd, buffer, BLOCKSIZE) == -1) { + LOG(ERROR) << "Failed to read data in " << range.first << ":" << range.second; + return -1; + } + + if (!builder.Update(reinterpret_cast(buffer), BLOCKSIZE)) { + LOG(ERROR) << "Failed to update hash tree builder"; + return -1; + } + } + } + + if (!builder.BuildHashTree()) { + LOG(ERROR) << "Failed to build hash tree"; + return -1; + } + + std::string root_hash_hex = HashTreeBuilder::BytesArrayToString(builder.root_hash()); + if (root_hash_hex != expected_root_hash) { + LOG(ERROR) << "Root hash of the verity hash tree doesn't match the expected value. Expected: " + << expected_root_hash << ", actual: " << root_hash_hex; + return -1; + } + + uint64_t write_offset = static_cast(hash_tree_ranges.GetBlockNumber(0)) * BLOCKSIZE; + if (params.canwrite && !builder.WriteHashTreeToFd(params.fd, write_offset)) { + LOG(ERROR) << "Failed to write hash tree to output"; + return -1; + } + + // TODO(xunchang) validates the written bytes + + return 0; +} + using CommandFunction = std::function; using CommandMap = std::unordered_map; @@ -1737,6 +1837,9 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, if (performer(params) == -1) { LOG(ERROR) << "failed to execute command [" << line << "]"; + if (cmd_type == Command::Type::COMPUTE_HASH_TREE && failure_type == kNoCause) { + failure_type = kHashTreeComputationFailure; + } goto pbiudone; } @@ -1894,15 +1997,16 @@ Value* BlockImageVerifyFn(const char* name, State* state, // Commands which are not allowed are set to nullptr to skip them completely. const CommandMap command_map{ // clang-format off - { Command::Type::ABORT, PerformCommandAbort }, - { Command::Type::BSDIFF, PerformCommandDiff }, - { Command::Type::ERASE, nullptr }, - { Command::Type::FREE, PerformCommandFree }, - { Command::Type::IMGDIFF, PerformCommandDiff }, - { Command::Type::MOVE, PerformCommandMove }, - { Command::Type::NEW, nullptr }, - { Command::Type::STASH, PerformCommandStash }, - { Command::Type::ZERO, nullptr }, + { Command::Type::ABORT, PerformCommandAbort }, + { Command::Type::BSDIFF, PerformCommandDiff }, + { Command::Type::COMPUTE_HASH_TREE, PerformCommandComputeHashTree }, + { Command::Type::ERASE, nullptr }, + { Command::Type::FREE, PerformCommandFree }, + { Command::Type::IMGDIFF, PerformCommandDiff }, + { Command::Type::MOVE, PerformCommandMove }, + { Command::Type::NEW, nullptr }, + { Command::Type::STASH, PerformCommandStash }, + { Command::Type::ZERO, nullptr }, // clang-format on }; CHECK_EQ(static_cast(Command::Type::LAST), command_map.size()); @@ -1915,15 +2019,16 @@ Value* BlockImageUpdateFn(const char* name, State* state, const std::vector>& argv) { const CommandMap command_map{ // clang-format off - { Command::Type::ABORT, PerformCommandAbort }, - { Command::Type::BSDIFF, PerformCommandDiff }, - { Command::Type::ERASE, PerformCommandErase }, - { Command::Type::FREE, PerformCommandFree }, - { Command::Type::IMGDIFF, PerformCommandDiff }, - { Command::Type::MOVE, PerformCommandMove }, - { Command::Type::NEW, PerformCommandNew }, - { Command::Type::STASH, PerformCommandStash }, - { Command::Type::ZERO, PerformCommandZero }, + { Command::Type::ABORT, PerformCommandAbort }, + { Command::Type::BSDIFF, PerformCommandDiff }, + { Command::Type::COMPUTE_HASH_TREE, PerformCommandComputeHashTree }, + { Command::Type::ERASE, PerformCommandErase }, + { Command::Type::FREE, PerformCommandFree }, + { Command::Type::IMGDIFF, PerformCommandDiff }, + { Command::Type::MOVE, PerformCommandMove }, + { Command::Type::NEW, PerformCommandNew }, + { Command::Type::STASH, PerformCommandStash }, + { Command::Type::ZERO, PerformCommandZero }, // clang-format on }; CHECK_EQ(static_cast(Command::Type::LAST), command_map.size()); diff --git a/updater/commands.cpp b/updater/commands.cpp index e88149678..15a787c51 100644 --- a/updater/commands.cpp +++ b/updater/commands.cpp @@ -40,6 +40,8 @@ Command::Type Command::ParseType(const std::string& type_str) { return Type::ABORT; } else if (type_str == "bsdiff") { return Type::BSDIFF; + } else if (type_str == "compute_hash_tree") { + return Type::COMPUTE_HASH_TREE; } else if (type_str == "erase") { return Type::ERASE; } else if (type_str == "free") { @@ -175,6 +177,7 @@ Command Command::Parse(const std::string& line, size_t index, std::string* err) SourceInfo source_info; StashInfo stash_info; + // TODO(xunchang) add the parse code of compute_hash_tree if (op == Type::ZERO || op == Type::NEW || op == Type::ERASE) { // zero/new/erase if (pos + 1 != tokens.size()) { diff --git a/updater/include/private/commands.h b/updater/include/private/commands.h index 087d7cfbf..7f9dc79f4 100644 --- a/updater/include/private/commands.h +++ b/updater/include/private/commands.h @@ -213,6 +213,10 @@ class PatchInfo { // - Free the given stash data. // - Meaningful args: StashInfo // +// compute_hash_tree +// - Computes the hash_tree bytes and writes the result to the specified range on the +// block_device. +// // abort // - Abort the current update. Allowed for testing code only. // @@ -221,6 +225,7 @@ class Command { enum class Type { ABORT, BSDIFF, + COMPUTE_HASH_TREE, ERASE, FREE, IMGDIFF, -- cgit v1.2.3