/*
* 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;
}