struct sparse_file *sparse_file_import_auto(int fd, bool crc, bool verbose) { struct sparse_file *s; int64_t len; int ret; s = sparse_file_import(fd, verbose, crc); if (s) { return s; } len = lseek64(fd, 0, SEEK_END); if (len < 0) { return NULL; } lseek64(fd, 0, SEEK_SET); s = sparse_file_new(4096, len); if (!s) { return NULL; } ret = sparse_file_read_normal(s, fd); if (ret < 0) { sparse_file_destroy(s); return NULL; } return s; }
int main(int argc, char *argv[]) { int in; int out; int i; int ret; struct sparse_file *s; if (argc < 3) { usage(); exit(-1); } out = open(argv[argc - 1], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664); if (out < 0) { fprintf(stderr, "Cannot open output file %s\n", argv[argc - 1]); exit(-1); } for (i = 1; i < argc - 1; i++) { if (strcmp(argv[i], "-") == 0) { in = STDIN_FILENO; } else { in = open(argv[i], O_RDONLY | O_BINARY); if (in < 0) { fprintf(stderr, "Cannot open input file %s\n", argv[i]); exit(-1); } } s = sparse_file_import(in, true, false); if (!s) { fprintf(stderr, "Failed to read sparse file\n"); exit(-1); } lseek(out, SEEK_SET, 0); ret = sparse_file_write(s, out, false, false, false); if (ret < 0) { fprintf(stderr, "Cannot write output file\n"); exit(-1); } sparse_file_destroy(s); close(in); } close(out); exit(0); }
bool generate_verity_tree(const std::string& data_filename, const std::string& verity_filename, HashTreeBuilder* builder, const std::vector<unsigned char>& salt_content, size_t block_size, bool sparse, bool verbose) { android::base::unique_fd data_fd(open(data_filename.c_str(), O_RDONLY)); if (data_fd == -1) { PLOG(ERROR) << "failed to open " << data_filename; return false; } struct sparse_file* file; if (sparse) { file = sparse_file_import(data_fd, false, false); } else { file = sparse_file_import_auto(data_fd, false, verbose); } if (!file) { LOG(ERROR) << "failed to read file " << data_filename; return false; } int64_t len = sparse_file_len(file, false, false); if (len % block_size != 0) { LOG(ERROR) << "file size " << len << " is not a multiple of " << block_size << " byte"; return false; } // Initialize the builder to compute the hash tree. if (!builder->Initialize(len, salt_content)) { LOG(ERROR) << "Failed to initialize HashTreeBuilder"; return false; } auto hash_callback = [](void* priv, const void* data, size_t len) { auto sparse_hasher = static_cast<HashTreeBuilder*>(priv); return sparse_hasher->Update(static_cast<const unsigned char*>(data), len) ? 0 : 1; }; sparse_file_callback(file, false, false, hash_callback, builder); sparse_file_destroy(file); if (!builder->BuildHashTree()) { return false; } return builder->WriteHashTreeToFile(verity_filename); }
bool handle_command(Socket *client, std::string cmd, std::vector<std::string> args) { const char *trampfile = "/data/misc/fastbootd/mid.bin"; if (cmd == "getvar") { if (args[0] == "max-download-size") { return send_reply(client, "OKAY", "%d", kMaxDownloadSize); } else if (args[0] == "partition-type") { for (size_t i = 0; i < sizeof(part_info) / sizeof(part_info[0]); i++) { if (args[1] == part_info[i].name) { return send_reply(client, "OKAY", part_info[i].type); } } } else if (args[0] == "product") { char property[PROPERTY_VALUE_MAX]; property_get("ro.product.board", property, ""); return send_reply(client, "OKAY", property); } else if (args[0] == "serialno") { char property[PROPERTY_VALUE_MAX]; property_get("ro.serialno", property, ""); return send_reply(client, "OKAY", property); } else if (args[0] == "version-bootloader") { return send_reply(client, "OKAY", "0.1"); } return send_reply(client, "OKAY", ""); } else if (cmd == "download") { uint32_t size = strtol(args[0].c_str(), 0, 16); send_reply(client, "DATA", "%08x", size); int fd = open(trampfile, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd < 0) { send_reply(client, "FAIL", "fail to create trampoline file to store data!"); return false; } while (size > 0) { char buffer[4096]; ssize_t read = client->Receive(buffer, 8, 0); if (read != 8) { send_reply(client, "FAIL", "fail to receive data!"); close(fd); return false; } size_t length = ExtractMessageLength(buffer); do { read = client->Receive(buffer, std::min(length, sizeof(buffer)), 0); if (read < 0) { close(fd); return false; } write(fd, buffer, read); length -= read; size -= read; } while (length > 0); } close(fd); return send_reply(client, "OKAY", ""); } else if (cmd == "flash") { std::unique_ptr<char, int (*)(const char *)> tmpfile((char *)trampfile, unlink); int fd = open(tmpfile.get(), O_RDONLY); if (fd < 0) { send_reply(client, "FAIL", "please run download command first!"); return false; } const char *devname = NULL; const char *partname = NULL; for (size_t i = 0; i < sizeof(part_info) / sizeof(part_info[0]); i++) { if (args[0] == part_info[i].name) { devname = part_info[i].device; partname = part_info[i].name; break; } } if (devname == NULL) { close(fd); send_reply(client, "FAIL", "partition: %s does not exist!", args[0].c_str()); return false; } if (!strcmp("boot", partname)) { close(fd); return handle_command_flash_boot_partition(client, tmpfile.get(), devname); } int fddev = open(devname, O_WRONLY | O_CREAT, 0600); if (fddev < 0) { close(fd); send_reply(client, "FAIL", "failed to open partition: %s", args[0].c_str()); return false; } struct sparse_file *s = sparse_file_import(fd, true, false); if (!s) { close(fd); close(fddev); send_reply(client, "FAIL", "failed to read sparse file!"); return false; } sparse_file_write(s, fddev, false, false, false); sparse_file_destroy(s); close(fd); close(fddev); sync(); return send_reply(client, "OKAY", ""); } else if (cmd == "erase") { const char *devname = NULL; for (size_t i = 0; i < sizeof(part_info) / sizeof(part_info[0]); i++) { if (args[0] == part_info[i].name) { devname = part_info[i].device; break; } } if (devname == NULL) { send_reply(client, "FAIL", "partition: %s does not exist!", args[0].c_str()); return false; } uint64_t devsize = 0; int fd = open(devname, O_RDONLY); ioctl(fd, BLKGETSIZE64, &devsize); const uint64_t blksize = 64 * 1024; const uint64_t numblk = (devsize + blksize - 1) / blksize; const uint64_t updsize = (numblk / 10) * blksize; for (uint64_t offset = 0; offset < devsize; offset += updsize) { uint64_t realsize = std::min(updsize, devsize - offset); const char *argv[] = { "/system/bin/dd", "if=/dev/zero", android::base::StringPrintf("of=%s", devname).c_str(), android::base::StringPrintf("seek=%lld", offset).c_str(), android::base::StringPrintf("bs=%lld", realsize).c_str(), "count=1", }; int status; android_fork_execvp(sizeof(argv) / sizeof(argv[0]), (char **)argv, &status, true, true); send_reply(client, "INFO", android::base::StringPrintf("erase %s: %3lld/100", devname, (offset + realsize) * 100 / devsize).c_str()); } return send_reply(client, "OKAY", ""); } else if (cmd == "continue") { android::base::WriteStringToFile("5", "/sys/module/bcm2709/parameters/reboot_part"); android_reboot(ANDROID_RB_RESTART, 0, NULL); // while (true) { pause(); } return send_reply(client, "OKAY", ""); } else if (cmd == "reboot" || cmd == "reboot-bootloader") { android::base::WriteStringToFile("0", "/sys/module/bcm2709/parameters/reboot_part"); android_reboot(ANDROID_RB_RESTART, 0, NULL); // while (true) { pause(); } return send_reply(client, "OKAY", ""); } return send_reply(client, "FAIL", "unknown command: %s", cmd.c_str()); }
int main(int argc, char **argv) { char *data_filename; char *verity_filename; unsigned char *salt = NULL; size_t salt_size = 0; bool sparse = false; size_t block_size = 4096; uint64_t calculate_size = 0; bool verbose = false; while (1) { const static struct option long_options[] = { {"salt-str", required_argument, 0, 'a'}, {"salt-hex", required_argument, 0, 'A'}, {"help", no_argument, 0, 'h'}, {"sparse", no_argument, 0, 'S'}, {"verity-size", required_argument, 0, 's'}, {"verbose", no_argument, 0, 'v'}, {NULL, 0, 0, 0} }; int c = getopt_long(argc, argv, "a:A:hSs:v", long_options, NULL); if (c < 0) { break; } switch (c) { case 'a': salt_size = strlen(optarg); salt = new unsigned char[salt_size](); if (salt == NULL) { FATAL("failed to allocate memory for salt\n"); } memcpy(salt, optarg, salt_size); break; case 'A': { BIGNUM *bn = NULL; if(!BN_hex2bn(&bn, optarg)) { FATAL("failed to convert salt from hex\n"); } salt_size = BN_num_bytes(bn); salt = new unsigned char[salt_size](); if (salt == NULL) { FATAL("failed to allocate memory for salt\n"); } if((size_t)BN_bn2bin(bn, salt) != salt_size) { FATAL("failed to convert salt to bytes\n"); } } break; case 'h': usage(); return 1; case 'S': sparse = true; break; case 's': { char* endptr; errno = 0; unsigned long long int inSize = strtoull(optarg, &endptr, 0); if (optarg[0] == '\0' || *endptr != '\0' || (errno == ERANGE && inSize == ULLONG_MAX)) { FATAL("invalid value of verity-size\n"); } if (inSize > UINT64_MAX) { FATAL("invalid value of verity-size\n"); } calculate_size = (uint64_t)inSize; } break; case 'v': verbose = true; break; case '?': usage(); return 1; default: abort(); } } argc -= optind; argv += optind; const EVP_MD *md = EVP_sha256(); if (!md) { FATAL("failed to get digest\n"); } size_t hash_size = EVP_MD_size(md); assert(hash_size * 2 < block_size); if (!salt || !salt_size) { salt_size = hash_size; salt = new unsigned char[salt_size]; if (salt == NULL) { FATAL("failed to allocate memory for salt\n"); } int random_fd = open("/dev/urandom", O_RDONLY); if (random_fd < 0) { FATAL("failed to open /dev/urandom\n"); } ssize_t ret = read(random_fd, salt, salt_size); if (ret != (ssize_t)salt_size) { FATAL("failed to read %zu bytes from /dev/urandom: %zd %d\n", salt_size, ret, errno); } close(random_fd); } if (calculate_size) { if (argc != 0) { usage(); return 1; } size_t verity_blocks = 0; size_t level_blocks; int levels = 0; do { level_blocks = verity_tree_blocks(calculate_size, block_size, hash_size, levels); levels++; verity_blocks += level_blocks; } while (level_blocks > 1); printf("%" PRIu64 "\n", (uint64_t)verity_blocks * block_size); return 0; } if (argc != 2) { usage(); return 1; } data_filename = argv[0]; verity_filename = argv[1]; int fd = open(data_filename, O_RDONLY); if (fd < 0) { FATAL("failed to open %s\n", data_filename); } struct sparse_file *file; if (sparse) { file = sparse_file_import(fd, false, false); } else { file = sparse_file_import_auto(fd, false, verbose); } if (!file) { FATAL("failed to read file %s\n", data_filename); } int64_t len = sparse_file_len(file, false, false); if (len % block_size != 0) { FATAL("file size %" PRIu64 " is not a multiple of %zu bytes\n", len, block_size); } int levels = 0; size_t verity_blocks = 0; size_t level_blocks; do { level_blocks = verity_tree_blocks(len, block_size, hash_size, levels); levels++; verity_blocks += level_blocks; } while (level_blocks > 1); unsigned char *verity_tree = new unsigned char[verity_blocks * block_size](); unsigned char **verity_tree_levels = new unsigned char *[levels + 1](); size_t *verity_tree_level_blocks = new size_t[levels](); if (verity_tree == NULL || verity_tree_levels == NULL || verity_tree_level_blocks == NULL) { FATAL("failed to allocate memory for verity tree\n"); } unsigned char *ptr = verity_tree; for (int i = levels - 1; i >= 0; i--) { verity_tree_levels[i] = ptr; verity_tree_level_blocks[i] = verity_tree_blocks(len, block_size, hash_size, i); ptr += verity_tree_level_blocks[i] * block_size; } assert(ptr == verity_tree + verity_blocks * block_size); assert(verity_tree_level_blocks[levels - 1] == 1); unsigned char zero_block_hash[hash_size]; unsigned char zero_block[block_size]; memset(zero_block, 0, block_size); hash_block(md, zero_block, block_size, salt, salt_size, zero_block_hash, NULL); unsigned char root_hash[hash_size]; verity_tree_levels[levels] = root_hash; struct sparse_hash_ctx ctx; ctx.hashes = verity_tree_levels[0]; ctx.salt = salt; ctx.salt_size = salt_size; ctx.hash_size = hash_size; ctx.block_size = block_size; ctx.zero_block_hash = zero_block_hash; ctx.md = md; sparse_file_callback(file, false, false, hash_chunk, &ctx); sparse_file_destroy(file); close(fd); for (int i = 0; i < levels; i++) { size_t out_size; hash_blocks(md, verity_tree_levels[i], verity_tree_level_blocks[i] * block_size, verity_tree_levels[i + 1], &out_size, salt, salt_size, block_size); if (i < levels - 1) { assert(div_round_up(out_size, block_size) == verity_tree_level_blocks[i + 1]); } else { assert(out_size == hash_size); } } for (size_t i = 0; i < hash_size; i++) { printf("%02x", root_hash[i]); } printf(" "); for (size_t i = 0; i < salt_size; i++) { printf("%02x", salt[i]); } printf("\n"); fd = open(verity_filename, O_WRONLY|O_CREAT, 0666); if (fd < 0) { FATAL("failed to open output file '%s'\n", verity_filename); } write(fd, verity_tree, verity_blocks * block_size); close(fd); delete[] verity_tree_levels; delete[] verity_tree_level_blocks; delete[] verity_tree; delete[] salt; }