1 #include <openssl/evp.h>
2 #include <sparse/sparse.h>
17 struct sparse_hash_ctx {
18 unsigned char *hashes;
19 const unsigned char *salt;
23 const unsigned char *zero_block_hash;
27 #define div_round_up(x,y) (((x) + (y) - 1)/(y))
29 #define round_up(x,y) (div_round_up(x,y)*(y))
31 #define FATAL(x...) { \
36 size_t verity_tree_blocks(uint64_t data_size, size_t block_size, size_t hash_size,
39 size_t level_blocks = div_round_up(data_size, block_size);
40 int hashes_per_block = div_round_up(block_size, hash_size);
43 level_blocks = div_round_up(level_blocks, hashes_per_block);
49 int hash_block(const EVP_MD *md,
50 const unsigned char *block, size_t len,
51 const unsigned char *salt, size_t salt_len,
52 unsigned char *out, size_t *out_size)
58 mdctx = EVP_MD_CTX_create();
60 ret &= EVP_DigestInit_ex(mdctx, md, NULL);
61 ret &= EVP_DigestUpdate(mdctx, salt, salt_len);
62 ret &= EVP_DigestUpdate(mdctx, block, len);
63 ret &= EVP_DigestFinal_ex(mdctx, out, &s);
64 EVP_MD_CTX_destroy(mdctx);
72 int hash_blocks(const EVP_MD *md,
73 const unsigned char *in, size_t in_size,
74 unsigned char *out, size_t *out_size,
75 const unsigned char *salt, size_t salt_size,
80 for (size_t i = 0; i < in_size; i += block_size) {
81 hash_block(md, in + i, block_size, salt, salt_size, out, &s);
89 int hash_chunk(void *priv, const void *data, int len)
91 struct sparse_hash_ctx *ctx = (struct sparse_hash_ctx *)priv;
92 assert(len % ctx->block_size == 0);
95 hash_blocks(ctx->md, (const unsigned char *)data, len,
97 ctx->salt, ctx->salt_size, ctx->block_size);
100 for (size_t i = 0; i < (size_t)len; i += ctx->block_size) {
101 memcpy(ctx->hashes, ctx->zero_block_hash, ctx->hash_size);
102 ctx->hashes += ctx->hash_size;
110 printf("usage: build_verity_tree [ <options> ] -s <size> | <data> <verity>\n"
112 " -a,--salt-str=<string> set salt to <string>\n"
113 " -A,--salt-hex=<hex digits> set salt to <hex digits>\n"
114 " -h show this help\n"
115 " -s,--verity-size=<data size> print the size of the verity tree\n"
116 " -S treat <data image> as a sparse file\n"
120 int main(int argc, char **argv)
123 char *verity_filename;
124 unsigned char *salt = NULL;
125 size_t salt_size = 0;
127 size_t block_size = 4096;
128 size_t calculate_size = 0;
131 const static struct option long_options[] = {
132 {"salt-str", required_argument, 0, 'a'},
133 {"salt-hex", required_argument, 0, 'A'},
134 {"help", no_argument, 0, 'h'},
135 {"sparse", no_argument, 0, 'S'},
136 {"verity-size", required_argument, 0, 's'},
138 int c = getopt_long(argc, argv, "a:A:hSs:", long_options, NULL);
145 salt_size = strlen(optarg);
146 salt = new unsigned char[salt_size]();
148 FATAL("failed to allocate memory for salt\n");
150 memcpy(salt, optarg, salt_size);
154 if(!BN_hex2bn(&bn, optarg)) {
155 FATAL("failed to convert salt from hex\n");
157 salt_size = BN_num_bytes(bn);
158 salt = new unsigned char[salt_size]();
160 FATAL("failed to allocate memory for salt\n");
162 if((size_t)BN_bn2bin(bn, salt) != salt_size) {
163 FATAL("failed to convert salt to bytes\n");
174 calculate_size = strtoul(optarg, NULL, 0);
187 const EVP_MD *md = EVP_sha256();
189 FATAL("failed to get digest\n");
192 size_t hash_size = EVP_MD_size(md);
193 assert(hash_size * 2 < block_size);
195 if (!salt || !salt_size) {
196 salt_size = hash_size;
197 salt = new unsigned char[salt_size];
199 FATAL("failed to allocate memory for salt\n");
202 int random_fd = open("/dev/urandom", O_RDONLY);
204 FATAL("failed to open /dev/urandom\n");
207 ssize_t ret = read(random_fd, salt, salt_size);
208 if (ret != (ssize_t)salt_size) {
209 FATAL("failed to read %zu bytes from /dev/urandom: %zd %d\n", salt_size, ret, errno);
214 if (calculate_size) {
219 size_t verity_blocks = 0;
223 level_blocks = verity_tree_blocks(calculate_size, block_size, hash_size, levels);
225 verity_blocks += level_blocks;
226 } while (level_blocks > 1);
228 printf("%zu\n", verity_blocks * block_size);
237 data_filename = argv[0];
238 verity_filename = argv[1];
240 int fd = open(data_filename, O_RDONLY);
242 FATAL("failed to open %s\n", data_filename);
245 struct sparse_file *file;
247 file = sparse_file_import(fd, false, false);
249 file = sparse_file_import_auto(fd, false);
253 FATAL("failed to read file %s\n", data_filename);
256 int64_t len = sparse_file_len(file, false, false);
257 if (len % block_size != 0) {
258 FATAL("file size %" PRIu64 " is not a multiple of %zu bytes\n",
263 size_t verity_blocks = 0;
267 level_blocks = verity_tree_blocks(len, block_size, hash_size, levels);
269 verity_blocks += level_blocks;
270 } while (level_blocks > 1);
272 unsigned char *verity_tree = new unsigned char[verity_blocks * block_size]();
273 unsigned char **verity_tree_levels = new unsigned char *[levels + 1]();
274 size_t *verity_tree_level_blocks = new size_t[levels]();
275 if (verity_tree == NULL || verity_tree_levels == NULL || verity_tree_level_blocks == NULL) {
276 FATAL("failed to allocate memory for verity tree\n");
279 unsigned char *ptr = verity_tree;
280 for (int i = levels - 1; i >= 0; i--) {
281 verity_tree_levels[i] = ptr;
282 verity_tree_level_blocks[i] = verity_tree_blocks(len, block_size, hash_size, i);
283 ptr += verity_tree_level_blocks[i] * block_size;
285 assert(ptr == verity_tree + verity_blocks * block_size);
286 assert(verity_tree_level_blocks[levels - 1] == 1);
288 unsigned char zero_block_hash[hash_size];
289 unsigned char zero_block[block_size];
290 memset(zero_block, 0, block_size);
291 hash_block(md, zero_block, block_size, salt, salt_size, zero_block_hash, NULL);
293 unsigned char root_hash[hash_size];
294 verity_tree_levels[levels] = root_hash;
296 struct sparse_hash_ctx ctx;
297 ctx.hashes = verity_tree_levels[0];
299 ctx.salt_size = salt_size;
300 ctx.hash_size = hash_size;
301 ctx.block_size = block_size;
302 ctx.zero_block_hash = zero_block_hash;
305 sparse_file_callback(file, false, false, hash_chunk, &ctx);
307 sparse_file_destroy(file);
310 for (int i = 0; i < levels; i++) {
313 verity_tree_levels[i], verity_tree_level_blocks[i] * block_size,
314 verity_tree_levels[i + 1], &out_size,
315 salt, salt_size, block_size);
316 if (i < levels - 1) {
317 assert(div_round_up(out_size, block_size) == verity_tree_level_blocks[i + 1]);
319 assert(out_size == hash_size);
323 for (size_t i = 0; i < hash_size; i++) {
324 printf("%02x", root_hash[i]);
327 for (size_t i = 0; i < salt_size; i++) {
328 printf("%02x", salt[i]);
332 fd = open(verity_filename, O_WRONLY|O_CREAT, 0666);
334 FATAL("failed to open output file '%s'\n", verity_filename);
336 write(fd, verity_tree, verity_blocks * block_size);
339 delete[] verity_tree_levels;
340 delete[] verity_tree_level_blocks;
341 delete[] verity_tree;