blob: e7bfa405d10411fe35081085e2b0c74b1f0fd3bc [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 <stdbool.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <unistd.h>
18
19struct sparse_hash_ctx {
20 unsigned char *hashes;
21 const unsigned char *salt;
22 uint64_t salt_size;
23 uint64_t hash_size;
24 uint64_t block_size;
25 const unsigned char *zero_block_hash;
26 const EVP_MD *md;
27};
28
29#define div_round_up(x,y) (((x) + (y) - 1)/(y))
30
31#define round_up(x,y) (div_round_up(x,y)*(y))
32
33#define FATAL(x...) { \
34 fprintf(stderr, x); \
35 exit(1); \
36}
37
38size_t verity_tree_blocks(uint64_t data_size, size_t block_size, size_t hash_size,
39 int level)
40{
41 size_t level_blocks = div_round_up(data_size, block_size);
42 int hashes_per_block = div_round_up(block_size, hash_size);
43
44 do {
45 level_blocks = div_round_up(level_blocks, hashes_per_block);
46 } while (level--);
47
48 return level_blocks;
49}
50
51int hash_block(const EVP_MD *md,
52 const unsigned char *block, size_t len,
53 const unsigned char *salt, size_t salt_len,
54 unsigned char *out, size_t *out_size)
55{
56 EVP_MD_CTX *mdctx;
57 unsigned int s;
58 int ret = 1;
59
60 mdctx = EVP_MD_CTX_create();
61 assert(mdctx);
62 ret &= EVP_DigestInit_ex(mdctx, md, NULL);
63 ret &= EVP_DigestUpdate(mdctx, salt, salt_len);
64 ret &= EVP_DigestUpdate(mdctx, block, len);
65 ret &= EVP_DigestFinal_ex(mdctx, out, &s);
66 EVP_MD_CTX_destroy(mdctx);
67 assert(ret == 1);
68 if (out_size) {
69 *out_size = s;
70 }
71 return 0;
72}
73
74int hash_blocks(const EVP_MD *md,
75 const unsigned char *in, size_t in_size,
76 unsigned char *out, size_t *out_size,
77 const unsigned char *salt, size_t salt_size,
78 size_t block_size)
79{
80 size_t s;
81 *out_size = 0;
82 for (size_t i = 0; i < in_size; i += block_size) {
83 hash_block(md, in + i, block_size, salt, salt_size, out, &s);
84 out += s;
85 *out_size += s;
86 }
87
88 return 0;
89}
90
91int hash_chunk(void *priv, const void *data, int len)
92{
93 struct sparse_hash_ctx *ctx = (struct sparse_hash_ctx *)priv;
94 assert(len % ctx->block_size == 0);
95 if (data) {
96 size_t s;
97 hash_blocks(ctx->md, (const unsigned char *)data, len,
98 ctx->hashes, &s,
99 ctx->salt, ctx->salt_size, ctx->block_size);
100 ctx->hashes += s;
101 } else {
102 for (size_t i = 0; i < (size_t)len; i += ctx->block_size) {
103 memcpy(ctx->hashes, ctx->zero_block_hash, ctx->hash_size);
104 ctx->hashes += ctx->hash_size;
105 }
106 }
107 return 0;
108}
109
110void usage(void)
111{
112 printf("usage: build_verity_tree [ <options> ] -s <size> | <data> <verity>\n"
113 "options:\n"
114 " -a,--salt-str=<string> set salt to <string>\n"
115 " -A,--salt-hex=<hex digits> set salt to <hex digits>\n"
116 " -h show this help\n"
117 " -s,--verity-size=<data size> print the size of the verity tree\n"
Mohamad Ayyashce7dad62015-03-31 12:27:29 -0700118 " -v, enable verbose logging\n"
Colin Cross724396e2014-04-17 14:09:23 -0700119 " -S treat <data image> as a sparse file\n"
120 );
121}
122
123int main(int argc, char **argv)
124{
125 char *data_filename;
126 char *verity_filename;
127 unsigned char *salt = NULL;
128 size_t salt_size = 0;
129 bool sparse = false;
130 size_t block_size = 4096;
Qin Ying88ea0062014-10-14 11:53:23 +0900131 uint64_t calculate_size = 0;
Mohamad Ayyashce7dad62015-03-31 12:27:29 -0700132 bool verbose = false;
Colin Cross724396e2014-04-17 14:09:23 -0700133
134 while (1) {
135 const static struct option long_options[] = {
136 {"salt-str", required_argument, 0, 'a'},
137 {"salt-hex", required_argument, 0, 'A'},
138 {"help", no_argument, 0, 'h'},
139 {"sparse", no_argument, 0, 'S'},
140 {"verity-size", required_argument, 0, 's'},
Mohamad Ayyashce7dad62015-03-31 12:27:29 -0700141 {"verbose", no_argument, 0, 'v'},
Qin Ying88ea0062014-10-14 11:53:23 +0900142 {NULL, 0, 0, 0}
Colin Cross724396e2014-04-17 14:09:23 -0700143 };
Mohamad Ayyashce7dad62015-03-31 12:27:29 -0700144 int c = getopt_long(argc, argv, "a:A:hSs:v", long_options, NULL);
Colin Cross724396e2014-04-17 14:09:23 -0700145 if (c < 0) {
146 break;
147 }
148
149 switch (c) {
150 case 'a':
151 salt_size = strlen(optarg);
152 salt = new unsigned char[salt_size]();
153 if (salt == NULL) {
154 FATAL("failed to allocate memory for salt\n");
155 }
156 memcpy(salt, optarg, salt_size);
157 break;
158 case 'A': {
Geremy Condra792cc082014-05-16 19:11:10 -0700159 BIGNUM *bn = NULL;
160 if(!BN_hex2bn(&bn, optarg)) {
161 FATAL("failed to convert salt from hex\n");
Colin Cross724396e2014-04-17 14:09:23 -0700162 }
Geremy Condra792cc082014-05-16 19:11:10 -0700163 salt_size = BN_num_bytes(bn);
Colin Cross724396e2014-04-17 14:09:23 -0700164 salt = new unsigned char[salt_size]();
165 if (salt == NULL) {
166 FATAL("failed to allocate memory for salt\n");
167 }
Geremy Condra792cc082014-05-16 19:11:10 -0700168 if((size_t)BN_bn2bin(bn, salt) != salt_size) {
169 FATAL("failed to convert salt to bytes\n");
Colin Cross724396e2014-04-17 14:09:23 -0700170 }
171 }
172 break;
173 case 'h':
174 usage();
175 return 1;
176 case 'S':
177 sparse = true;
178 break;
Qin Ying88ea0062014-10-14 11:53:23 +0900179 case 's': {
180 char* endptr;
181 errno = 0;
182 unsigned long long int inSize = strtoull(optarg, &endptr, 0);
183 if (optarg[0] == '\0' || *endptr != '\0' ||
184 (errno == ERANGE && inSize == ULLONG_MAX)) {
185 FATAL("invalid value of verity-size\n");
186 }
187 if (inSize > UINT64_MAX) {
188 FATAL("invalid value of verity-size\n");
189 }
190 calculate_size = (uint64_t)inSize;
191 }
Colin Cross724396e2014-04-17 14:09:23 -0700192 break;
Mohamad Ayyashce7dad62015-03-31 12:27:29 -0700193 case 'v':
194 verbose = true;
195 break;
Colin Cross724396e2014-04-17 14:09:23 -0700196 case '?':
197 usage();
198 return 1;
199 default:
200 abort();
201 }
202 }
203
204 argc -= optind;
205 argv += optind;
206
207 const EVP_MD *md = EVP_sha256();
208 if (!md) {
209 FATAL("failed to get digest\n");
210 }
211
212 size_t hash_size = EVP_MD_size(md);
213 assert(hash_size * 2 < block_size);
214
215 if (!salt || !salt_size) {
216 salt_size = hash_size;
217 salt = new unsigned char[salt_size];
218 if (salt == NULL) {
219 FATAL("failed to allocate memory for salt\n");
220 }
221
222 int random_fd = open("/dev/urandom", O_RDONLY);
223 if (random_fd < 0) {
224 FATAL("failed to open /dev/urandom\n");
225 }
226
227 ssize_t ret = read(random_fd, salt, salt_size);
228 if (ret != (ssize_t)salt_size) {
Ben Murdoche798a232014-05-01 10:53:47 +0100229 FATAL("failed to read %zu bytes from /dev/urandom: %zd %d\n", salt_size, ret, errno);
Colin Cross724396e2014-04-17 14:09:23 -0700230 }
231 close(random_fd);
232 }
233
234 if (calculate_size) {
235 if (argc != 0) {
236 usage();
237 return 1;
238 }
239 size_t verity_blocks = 0;
240 size_t level_blocks;
241 int levels = 0;
242 do {
243 level_blocks = verity_tree_blocks(calculate_size, block_size, hash_size, levels);
244 levels++;
245 verity_blocks += level_blocks;
246 } while (level_blocks > 1);
247
Qin Ying88ea0062014-10-14 11:53:23 +0900248 printf("%" PRIu64 "\n", (uint64_t)verity_blocks * block_size);
Colin Cross724396e2014-04-17 14:09:23 -0700249 return 0;
250 }
251
252 if (argc != 2) {
253 usage();
254 return 1;
255 }
256
257 data_filename = argv[0];
258 verity_filename = argv[1];
259
260 int fd = open(data_filename, O_RDONLY);
261 if (fd < 0) {
262 FATAL("failed to open %s\n", data_filename);
263 }
264
265 struct sparse_file *file;
266 if (sparse) {
267 file = sparse_file_import(fd, false, false);
268 } else {
Mohamad Ayyashce7dad62015-03-31 12:27:29 -0700269 file = sparse_file_import_auto(fd, false, verbose);
Colin Cross724396e2014-04-17 14:09:23 -0700270 }
271
272 if (!file) {
273 FATAL("failed to read file %s\n", data_filename);
274 }
275
276 int64_t len = sparse_file_len(file, false, false);
277 if (len % block_size != 0) {
278 FATAL("file size %" PRIu64 " is not a multiple of %zu bytes\n",
279 len, block_size);
280 }
281
282 int levels = 0;
283 size_t verity_blocks = 0;
284 size_t level_blocks;
285
286 do {
287 level_blocks = verity_tree_blocks(len, block_size, hash_size, levels);
288 levels++;
289 verity_blocks += level_blocks;
290 } while (level_blocks > 1);
291
292 unsigned char *verity_tree = new unsigned char[verity_blocks * block_size]();
293 unsigned char **verity_tree_levels = new unsigned char *[levels + 1]();
294 size_t *verity_tree_level_blocks = new size_t[levels]();
295 if (verity_tree == NULL || verity_tree_levels == NULL || verity_tree_level_blocks == NULL) {
296 FATAL("failed to allocate memory for verity tree\n");
297 }
298
299 unsigned char *ptr = verity_tree;
300 for (int i = levels - 1; i >= 0; i--) {
301 verity_tree_levels[i] = ptr;
302 verity_tree_level_blocks[i] = verity_tree_blocks(len, block_size, hash_size, i);
303 ptr += verity_tree_level_blocks[i] * block_size;
304 }
305 assert(ptr == verity_tree + verity_blocks * block_size);
306 assert(verity_tree_level_blocks[levels - 1] == 1);
307
308 unsigned char zero_block_hash[hash_size];
309 unsigned char zero_block[block_size];
310 memset(zero_block, 0, block_size);
311 hash_block(md, zero_block, block_size, salt, salt_size, zero_block_hash, NULL);
312
313 unsigned char root_hash[hash_size];
314 verity_tree_levels[levels] = root_hash;
315
316 struct sparse_hash_ctx ctx;
317 ctx.hashes = verity_tree_levels[0];
318 ctx.salt = salt;
319 ctx.salt_size = salt_size;
320 ctx.hash_size = hash_size;
321 ctx.block_size = block_size;
322 ctx.zero_block_hash = zero_block_hash;
323 ctx.md = md;
324
325 sparse_file_callback(file, false, false, hash_chunk, &ctx);
326
327 sparse_file_destroy(file);
328 close(fd);
329
330 for (int i = 0; i < levels; i++) {
331 size_t out_size;
332 hash_blocks(md,
333 verity_tree_levels[i], verity_tree_level_blocks[i] * block_size,
334 verity_tree_levels[i + 1], &out_size,
335 salt, salt_size, block_size);
336 if (i < levels - 1) {
337 assert(div_round_up(out_size, block_size) == verity_tree_level_blocks[i + 1]);
338 } else {
339 assert(out_size == hash_size);
340 }
341 }
342
343 for (size_t i = 0; i < hash_size; i++) {
344 printf("%02x", root_hash[i]);
345 }
346 printf(" ");
347 for (size_t i = 0; i < salt_size; i++) {
348 printf("%02x", salt[i]);
349 }
350 printf("\n");
351
352 fd = open(verity_filename, O_WRONLY|O_CREAT, 0666);
353 if (fd < 0) {
354 FATAL("failed to open output file '%s'\n", verity_filename);
355 }
356 write(fd, verity_tree, verity_blocks * block_size);
357 close(fd);
358
359 delete[] verity_tree_levels;
360 delete[] verity_tree_level_blocks;
361 delete[] verity_tree;
362 delete[] salt;
363}