blob: ad812ed8f7ba743659d19d9b28d5f64d42895a8e [file] [log] [blame]
Adam Langley77dd1972015-01-20 18:25:27 -08001#include <openssl/bn.h>
Colin Cross724396e2014-04-17 14:09:23 -07002#include <openssl/evp.h>
3#include <sparse/sparse.h>
4
5#undef NDEBUG
6
7#include <assert.h>
8#include <errno.h>
9#include <getopt.h>
10#include <fcntl.h>
11#include <inttypes.h>
Qin Ying88ea0062014-10-14 11:53:23 +090012#include <limits.h>
Colin Cross724396e2014-04-17 14:09:23 -070013#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <unistd.h>
Yunlian Jiang6eb04932017-08-30 09:50:05 -070017#include <vector>
Colin Cross724396e2014-04-17 14:09:23 -070018
Elliott Hughes66dd09e2015-12-04 14:00:57 -080019#include <android-base/file.h>
Elliott Hughesb1040442015-05-12 21:34:57 -070020
Colin Cross724396e2014-04-17 14:09:23 -070021struct sparse_hash_ctx {
22 unsigned char *hashes;
23 const unsigned char *salt;
24 uint64_t salt_size;
25 uint64_t hash_size;
26 uint64_t block_size;
27 const unsigned char *zero_block_hash;
28 const EVP_MD *md;
29};
30
31#define div_round_up(x,y) (((x) + (y) - 1)/(y))
32
33#define round_up(x,y) (div_round_up(x,y)*(y))
34
35#define FATAL(x...) { \
36 fprintf(stderr, x); \
37 exit(1); \
38}
39
40size_t verity_tree_blocks(uint64_t data_size, size_t block_size, size_t hash_size,
41 int level)
42{
43 size_t level_blocks = div_round_up(data_size, block_size);
44 int hashes_per_block = div_round_up(block_size, hash_size);
45
46 do {
47 level_blocks = div_round_up(level_blocks, hashes_per_block);
48 } while (level--);
49
50 return level_blocks;
51}
52
53int hash_block(const EVP_MD *md,
54 const unsigned char *block, size_t len,
55 const unsigned char *salt, size_t salt_len,
56 unsigned char *out, size_t *out_size)
57{
58 EVP_MD_CTX *mdctx;
59 unsigned int s;
60 int ret = 1;
61
62 mdctx = EVP_MD_CTX_create();
63 assert(mdctx);
64 ret &= EVP_DigestInit_ex(mdctx, md, NULL);
65 ret &= EVP_DigestUpdate(mdctx, salt, salt_len);
66 ret &= EVP_DigestUpdate(mdctx, block, len);
67 ret &= EVP_DigestFinal_ex(mdctx, out, &s);
68 EVP_MD_CTX_destroy(mdctx);
69 assert(ret == 1);
70 if (out_size) {
71 *out_size = s;
72 }
73 return 0;
74}
75
76int hash_blocks(const EVP_MD *md,
77 const unsigned char *in, size_t in_size,
78 unsigned char *out, size_t *out_size,
79 const unsigned char *salt, size_t salt_size,
80 size_t block_size)
81{
82 size_t s;
83 *out_size = 0;
84 for (size_t i = 0; i < in_size; i += block_size) {
85 hash_block(md, in + i, block_size, salt, salt_size, out, &s);
86 out += s;
87 *out_size += s;
88 }
89
90 return 0;
91}
92
Tao Baoff0d6052018-04-23 15:24:06 -070093int hash_chunk(void *priv, const void *data, size_t len)
Colin Cross724396e2014-04-17 14:09:23 -070094{
95 struct sparse_hash_ctx *ctx = (struct sparse_hash_ctx *)priv;
96 assert(len % ctx->block_size == 0);
97 if (data) {
98 size_t s;
99 hash_blocks(ctx->md, (const unsigned char *)data, len,
100 ctx->hashes, &s,
101 ctx->salt, ctx->salt_size, ctx->block_size);
102 ctx->hashes += s;
103 } else {
Tao Baoff0d6052018-04-23 15:24:06 -0700104 for (size_t i = 0; i < len; i += ctx->block_size) {
Colin Cross724396e2014-04-17 14:09:23 -0700105 memcpy(ctx->hashes, ctx->zero_block_hash, ctx->hash_size);
106 ctx->hashes += ctx->hash_size;
107 }
108 }
109 return 0;
110}
111
112void usage(void)
113{
114 printf("usage: build_verity_tree [ <options> ] -s <size> | <data> <verity>\n"
115 "options:\n"
116 " -a,--salt-str=<string> set salt to <string>\n"
117 " -A,--salt-hex=<hex digits> set salt to <hex digits>\n"
118 " -h show this help\n"
119 " -s,--verity-size=<data size> print the size of the verity tree\n"
Mohamad Ayyash70f72fb2015-03-31 12:27:29 -0700120 " -v, enable verbose logging\n"
Colin Cross724396e2014-04-17 14:09:23 -0700121 " -S treat <data image> as a sparse file\n"
122 );
123}
124
125int main(int argc, char **argv)
126{
127 char *data_filename;
128 char *verity_filename;
Yunlian Jiang6eb04932017-08-30 09:50:05 -0700129 std::vector<unsigned char> salt;
Colin Cross724396e2014-04-17 14:09:23 -0700130 bool sparse = false;
131 size_t block_size = 4096;
Qin Ying88ea0062014-10-14 11:53:23 +0900132 uint64_t calculate_size = 0;
Mohamad Ayyash70f72fb2015-03-31 12:27:29 -0700133 bool verbose = false;
Colin Cross724396e2014-04-17 14:09:23 -0700134
135 while (1) {
136 const static struct option long_options[] = {
137 {"salt-str", required_argument, 0, 'a'},
138 {"salt-hex", required_argument, 0, 'A'},
139 {"help", no_argument, 0, 'h'},
140 {"sparse", no_argument, 0, 'S'},
141 {"verity-size", required_argument, 0, 's'},
Mohamad Ayyash70f72fb2015-03-31 12:27:29 -0700142 {"verbose", no_argument, 0, 'v'},
Qin Ying88ea0062014-10-14 11:53:23 +0900143 {NULL, 0, 0, 0}
Colin Cross724396e2014-04-17 14:09:23 -0700144 };
Mohamad Ayyash70f72fb2015-03-31 12:27:29 -0700145 int c = getopt_long(argc, argv, "a:A:hSs:v", long_options, NULL);
Colin Cross724396e2014-04-17 14:09:23 -0700146 if (c < 0) {
147 break;
148 }
149
150 switch (c) {
151 case 'a':
Yunlian Jiang6eb04932017-08-30 09:50:05 -0700152 salt.clear();
153 salt.insert(salt.end(), optarg, &optarg[strlen(optarg)]);
Colin Cross724396e2014-04-17 14:09:23 -0700154 break;
155 case 'A': {
Geremy Condra792cc082014-05-16 19:11:10 -0700156 BIGNUM *bn = NULL;
157 if(!BN_hex2bn(&bn, optarg)) {
158 FATAL("failed to convert salt from hex\n");
Colin Cross724396e2014-04-17 14:09:23 -0700159 }
Tao Bao88d51352017-09-22 22:09:32 -0700160 size_t salt_size = BN_num_bytes(bn);
Yunlian Jiang6eb04932017-08-30 09:50:05 -0700161 salt.resize(salt_size);
Tao Bao88d51352017-09-22 22:09:32 -0700162 if (BN_bn2bin(bn, salt.data()) != salt_size) {
Geremy Condra792cc082014-05-16 19:11:10 -0700163 FATAL("failed to convert salt to bytes\n");
Colin Cross724396e2014-04-17 14:09:23 -0700164 }
165 }
166 break;
167 case 'h':
168 usage();
169 return 1;
170 case 'S':
171 sparse = true;
172 break;
Qin Ying88ea0062014-10-14 11:53:23 +0900173 case 's': {
174 char* endptr;
175 errno = 0;
176 unsigned long long int inSize = strtoull(optarg, &endptr, 0);
177 if (optarg[0] == '\0' || *endptr != '\0' ||
178 (errno == ERANGE && inSize == ULLONG_MAX)) {
179 FATAL("invalid value of verity-size\n");
180 }
181 if (inSize > UINT64_MAX) {
182 FATAL("invalid value of verity-size\n");
183 }
184 calculate_size = (uint64_t)inSize;
185 }
Colin Cross724396e2014-04-17 14:09:23 -0700186 break;
Mohamad Ayyash70f72fb2015-03-31 12:27:29 -0700187 case 'v':
188 verbose = true;
189 break;
Colin Cross724396e2014-04-17 14:09:23 -0700190 case '?':
191 usage();
192 return 1;
193 default:
194 abort();
195 }
196 }
197
198 argc -= optind;
199 argv += optind;
200
201 const EVP_MD *md = EVP_sha256();
202 if (!md) {
203 FATAL("failed to get digest\n");
204 }
205
206 size_t hash_size = EVP_MD_size(md);
207 assert(hash_size * 2 < block_size);
208
Tao Bao88d51352017-09-22 22:09:32 -0700209 if (salt.empty()) {
210 salt.resize(hash_size);
Colin Cross724396e2014-04-17 14:09:23 -0700211
212 int random_fd = open("/dev/urandom", O_RDONLY);
213 if (random_fd < 0) {
214 FATAL("failed to open /dev/urandom\n");
215 }
216
Tao Bao88d51352017-09-22 22:09:32 -0700217 ssize_t ret = read(random_fd, salt.data(), salt.size());
218 if (ret != static_cast<ssize_t>(salt.size())) {
219 FATAL("failed to read %zu bytes from /dev/urandom: %zd %d\n", salt.size(), ret, errno);
Colin Cross724396e2014-04-17 14:09:23 -0700220 }
221 close(random_fd);
222 }
223
224 if (calculate_size) {
225 if (argc != 0) {
226 usage();
227 return 1;
228 }
229 size_t verity_blocks = 0;
230 size_t level_blocks;
231 int levels = 0;
232 do {
233 level_blocks = verity_tree_blocks(calculate_size, block_size, hash_size, levels);
234 levels++;
235 verity_blocks += level_blocks;
236 } while (level_blocks > 1);
237
Qin Ying88ea0062014-10-14 11:53:23 +0900238 printf("%" PRIu64 "\n", (uint64_t)verity_blocks * block_size);
Colin Cross724396e2014-04-17 14:09:23 -0700239 return 0;
240 }
241
242 if (argc != 2) {
243 usage();
244 return 1;
245 }
246
247 data_filename = argv[0];
248 verity_filename = argv[1];
249
250 int fd = open(data_filename, O_RDONLY);
251 if (fd < 0) {
252 FATAL("failed to open %s\n", data_filename);
253 }
254
255 struct sparse_file *file;
256 if (sparse) {
257 file = sparse_file_import(fd, false, false);
258 } else {
Mohamad Ayyash70f72fb2015-03-31 12:27:29 -0700259 file = sparse_file_import_auto(fd, false, verbose);
Colin Cross724396e2014-04-17 14:09:23 -0700260 }
261
262 if (!file) {
263 FATAL("failed to read file %s\n", data_filename);
264 }
265
266 int64_t len = sparse_file_len(file, false, false);
267 if (len % block_size != 0) {
268 FATAL("file size %" PRIu64 " is not a multiple of %zu bytes\n",
269 len, block_size);
270 }
271
272 int levels = 0;
273 size_t verity_blocks = 0;
274 size_t level_blocks;
275
276 do {
277 level_blocks = verity_tree_blocks(len, block_size, hash_size, levels);
278 levels++;
279 verity_blocks += level_blocks;
280 } while (level_blocks > 1);
281
282 unsigned char *verity_tree = new unsigned char[verity_blocks * block_size]();
283 unsigned char **verity_tree_levels = new unsigned char *[levels + 1]();
284 size_t *verity_tree_level_blocks = new size_t[levels]();
285 if (verity_tree == NULL || verity_tree_levels == NULL || verity_tree_level_blocks == NULL) {
286 FATAL("failed to allocate memory for verity tree\n");
287 }
288
289 unsigned char *ptr = verity_tree;
290 for (int i = levels - 1; i >= 0; i--) {
291 verity_tree_levels[i] = ptr;
292 verity_tree_level_blocks[i] = verity_tree_blocks(len, block_size, hash_size, i);
293 ptr += verity_tree_level_blocks[i] * block_size;
294 }
295 assert(ptr == verity_tree + verity_blocks * block_size);
296 assert(verity_tree_level_blocks[levels - 1] == 1);
297
298 unsigned char zero_block_hash[hash_size];
299 unsigned char zero_block[block_size];
300 memset(zero_block, 0, block_size);
Tao Bao88d51352017-09-22 22:09:32 -0700301 hash_block(md, zero_block, block_size, salt.data(), salt.size(), zero_block_hash, NULL);
Colin Cross724396e2014-04-17 14:09:23 -0700302
303 unsigned char root_hash[hash_size];
304 verity_tree_levels[levels] = root_hash;
305
306 struct sparse_hash_ctx ctx;
307 ctx.hashes = verity_tree_levels[0];
Yunlian Jiang6eb04932017-08-30 09:50:05 -0700308 ctx.salt = salt.data();
Tao Bao88d51352017-09-22 22:09:32 -0700309 ctx.salt_size = salt.size();
Colin Cross724396e2014-04-17 14:09:23 -0700310 ctx.hash_size = hash_size;
311 ctx.block_size = block_size;
312 ctx.zero_block_hash = zero_block_hash;
313 ctx.md = md;
314
315 sparse_file_callback(file, false, false, hash_chunk, &ctx);
316
317 sparse_file_destroy(file);
318 close(fd);
319
320 for (int i = 0; i < levels; i++) {
321 size_t out_size;
322 hash_blocks(md,
323 verity_tree_levels[i], verity_tree_level_blocks[i] * block_size,
324 verity_tree_levels[i + 1], &out_size,
Tao Bao88d51352017-09-22 22:09:32 -0700325 salt.data(), salt.size(), block_size);
Colin Cross724396e2014-04-17 14:09:23 -0700326 if (i < levels - 1) {
327 assert(div_round_up(out_size, block_size) == verity_tree_level_blocks[i + 1]);
328 } else {
329 assert(out_size == hash_size);
330 }
331 }
332
333 for (size_t i = 0; i < hash_size; i++) {
334 printf("%02x", root_hash[i]);
335 }
336 printf(" ");
Tao Bao88d51352017-09-22 22:09:32 -0700337 for (size_t i = 0; i < salt.size(); i++) {
338 printf("%02x", salt[i]);
Colin Cross724396e2014-04-17 14:09:23 -0700339 }
340 printf("\n");
341
342 fd = open(verity_filename, O_WRONLY|O_CREAT, 0666);
343 if (fd < 0) {
344 FATAL("failed to open output file '%s'\n", verity_filename);
345 }
Elliott Hughesb1040442015-05-12 21:34:57 -0700346 if (!android::base::WriteFully(fd, verity_tree, verity_blocks * block_size)) {
347 FATAL("failed to write '%s'\n", verity_filename);
348 }
Colin Cross724396e2014-04-17 14:09:23 -0700349 close(fd);
350
351 delete[] verity_tree_levels;
352 delete[] verity_tree_level_blocks;
353 delete[] verity_tree;
Colin Cross724396e2014-04-17 14:09:23 -0700354}