/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <fcntl.h> #include <getopt.h> #include <inttypes.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <limits> #include <string> #include <vector> #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/parseint.h> #include <android-base/unique_fd.h> #include "verity/build_verity_tree.h" static void usage(void) { printf( "usage: build_verity_tree [ <options> ] -s <size> | <data> <verity>\n" "options:\n" " -a,--salt-str=<string> set salt to <string>\n" " -A,--salt-hex=<hex digits> set salt to <hex digits>\n" " -h show this help\n" " -s,--verity-size=<data size> print the size of the verity tree\n" " -v, enable verbose logging\n" " -S treat <data image> as a sparse file\n"); } int main(int argc, char** argv) { constexpr size_t kBlockSize = 4096; std::vector<unsigned char> salt; bool sparse = false; uint64_t calculate_size = 0; bool verbose = false; std::string hash_algorithm; while (1) { constexpr struct option long_options[] = { {"salt-str", required_argument, nullptr, 'a'}, {"salt-hex", required_argument, nullptr, 'A'}, {"help", no_argument, nullptr, 'h'}, {"sparse", no_argument, nullptr, 'S'}, {"verity-size", required_argument, nullptr, 's'}, {"verbose", no_argument, nullptr, 'v'}, {"hash-algorithm", required_argument, nullptr, 0}, {nullptr, 0, nullptr, 0}}; int option_index; int c = getopt_long(argc, argv, "a:A:hSs:v", long_options, &option_index); if (c < 0) { break; } switch (c) { case 'a': salt.clear(); salt.insert(salt.end(), optarg, &optarg[strlen(optarg)]); break; case 'A': if (!HashTreeBuilder::ParseBytesArrayFromString(optarg, &salt)) { return 1; } break; case 'h': usage(); return 1; case 'S': sparse = true; break; case 's': { if (!android::base::ParseUint(optarg, &calculate_size, std::numeric_limits<uint64_t>::max())) { LOG(ERROR) << "Invalid input size: " << optarg; return 1; } } break; case 'v': verbose = true; break; case 0: { std::string option = long_options[option_index].name; if (option == "hash-algorithm") { hash_algorithm = optarg; } } break; case '?': usage(); return 1; default: abort(); } } argc -= optind; argv += optind; auto hash_function = hash_algorithm.empty() ? EVP_sha256() : HashTreeBuilder::HashFunction(hash_algorithm); if (hash_function == nullptr) { return 1; } HashTreeBuilder builder(kBlockSize, hash_function); if (calculate_size) { if (argc != 0) { usage(); return 1; } uint64_t tree_size = builder.CalculateSize(calculate_size); printf("%" PRIu64 "\n", tree_size); return 0; } if (argc != 2) { usage(); return 1; } if (salt.empty()) { salt.resize(builder.hash_size()); android::base::unique_fd random_fd(open("/dev/urandom", O_RDONLY)); if (random_fd < 0) { PLOG(ERROR) << "failed to open /dev/urandom"; return 1; } ssize_t ret = read(random_fd, salt.data(), salt.size()); if (ret != static_cast<ssize_t>(salt.size())) { PLOG(ERROR) << "failed to read " << salt.size() << " bytes from /dev/urandom: " << ret; return 1; } } if (!generate_verity_tree(argv[0], argv[1], &builder, salt, kBlockSize, sparse, verbose)) { return 1; } // Output the root hash and the salt. std::string root_hash_string = HashTreeBuilder::BytesArrayToString(builder.root_hash()); std::string salt_string = HashTreeBuilder::BytesArrayToString(salt); printf("%s %s\n", root_hash_string.c_str(), salt_string.c_str()); return 0; }