xref: /aosp_15_r20/external/fsverity-utils/programs/test_compute_digest.c (revision b13c0e4024008a1f948ee8189745cb3371f4ac04)
1 // SPDX-License-Identifier: MIT
2 /*
3  * Test libfsverity_compute_digest().
4  *
5  * Copyright 2020 Google LLC
6  *
7  * Use of this source code is governed by an MIT-style
8  * license that can be found in the LICENSE file or at
9  * https://opensource.org/licenses/MIT.
10  */
11 
12 #include "utils.h"
13 
14 #include <ctype.h>
15 #include <inttypes.h>
16 #include <openssl/sha.h>
17 
18 struct mem_file {
19 	u8 *data;
20 	size_t size;
21 	size_t offset;
22 };
23 
read_fn(void * fd,void * buf,size_t count)24 static int read_fn(void *fd, void *buf, size_t count)
25 {
26 	struct mem_file *f = fd;
27 
28 	ASSERT(count <= f->size - f->offset);
29 	memcpy(buf, &f->data[f->offset], count);
30 	f->offset += count;
31 	return 0;
32 }
33 
error_read_fn(void * fd,void * buf,size_t count)34 static int error_read_fn(void *fd __attribute__((unused)),
35 			 void *buf __attribute__((unused)),
36 			 size_t count __attribute__((unused)))
37 {
38 	return -EIO;
39 }
40 
zeroes_read_fn(void * fd,void * buf,size_t count)41 static int zeroes_read_fn(void *fd __attribute__((unused)),
42 			  void *buf, size_t count)
43 {
44 	memset(buf, 0, count);
45 	return 0;
46 }
47 
48 static const struct test_case {
49 	u32 hash_algorithm;
50 	u32 block_size;
51 	const char *salt;
52 	u64 file_size;
53 	const char *digest;
54 } test_cases[] = {
55 	{ /* large file */
56 		.hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
57 		.file_size = 1000000,
58 		.block_size = 4096,
59 		.digest = "\x48\xdf\x0c\x46\x23\x29\xcd\x87"
60 			  "\x96\x61\xbd\x05\xb3\x9a\xa8\x1b"
61 			  "\x05\xcc\x16\xaf\xd2\x7a\x71\x96"
62 			  "\xa5\x59\xda\x83\x53\x1d\x39\xd9",
63 	}, { /* small file */
64 		.hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
65 		.file_size = 100000,
66 		.block_size = 4096,
67 		.digest = "\xf2\x09\x6a\x36\xc5\xcd\xca\x4f"
68 			  "\xa3\x3e\xe8\x85\x28\x33\x15\x0b"
69 			  "\xb3\x24\x99\x2e\x54\x17\xa9\xd5"
70 			  "\x71\xf1\xbf\xff\xf7\x3b\x9e\xfc",
71 	}, { /* single-block file */
72 		.hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
73 		.file_size = 4096,
74 		.block_size = 4096,
75 		.digest = "\x6a\xc3\x99\x79\x01\x6e\x3d\xdf"
76 			  "\x3d\x39\xff\xf6\xcb\x98\x4f\x7c"
77 			  "\x11\x8a\xcd\xf1\x85\x29\x19\xf5"
78 			  "\xc1\x00\xc4\xb1\x42\xc1\x81\x8e",
79 	}, { /* tiny file */
80 		.hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
81 		.file_size = 1,
82 		.block_size = 4096,
83 		.digest = "\xb8\x03\x42\x95\x03\xd9\x59\x15"
84 			  "\x82\x9b\x29\xfd\xbc\x8b\xba\xd1"
85 			  "\x42\xf3\xab\xfd\x11\xb1\xca\xdf"
86 			  "\x55\x26\x58\x2e\x68\x5c\x05\x51",
87 	}, { /* empty file */
88 		.hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
89 		.file_size = 0,
90 		.block_size = 4096,
91 		.digest = "\x3d\x24\x8c\xa5\x42\xa2\x4f\xc6"
92 			  "\x2d\x1c\x43\xb9\x16\xea\xe5\x01"
93 			  "\x68\x78\xe2\x53\x3c\x88\x23\x84"
94 			  "\x80\xb2\x61\x28\xa1\xf1\xaf\x95",
95 	}, { /* salt */
96 		.hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
97 		.file_size = 1000000,
98 		.block_size = 4096,
99 		.salt = "abcd",
100 		.digest = "\x91\x79\x00\xb0\xd2\x99\x45\x4a"
101 			  "\xa3\x04\xd5\xde\xbc\x6f\x39\xe4"
102 			  "\xaf\x7b\x5a\xbe\x33\xbd\xbc\x56"
103 			  "\x8d\x5d\x8f\x1e\x5c\x4d\x86\x52",
104 	}, { /* max length salt (32 bytes) */
105 		.hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
106 		.file_size = 1000000,
107 		.block_size = 4096,
108 		.salt = "0123456789:;<=>?@ABCDEFGHIJKLMNO",
109 		.digest = "\xbc\x2d\x70\x32\x4c\x04\x8c\x22"
110 			  "\x0a\x2c\xb1\x90\x83\x21\x40\x86"
111 			  "\x3e\xb2\x68\xe6\x80\x42\x79\x39"
112 			  "\xe5\xd4\x67\xbe\xa5\xec\x5a\xd9",
113 	}, { /* 1K block size */
114 		.hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
115 		.file_size = 1000000,
116 		.block_size = 1024,
117 		.digest = "\xe9\xdf\x92\x7c\x14\xfc\xb9\x61"
118 			  "\xd5\xf5\x1c\x66\x6d\x8a\xe4\xc1"
119 			  "\x4f\xe4\xff\x98\xa3\x74\xc7\x33"
120 			  "\xe8\x98\xd0\x0c\x9e\x74\xa8\xe3",
121 	}, { /* 512-byte block size */
122 		.hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
123 		.file_size = 1000000,
124 		.block_size = 512,
125 		.digest = "\x03\x93\xee\x3d\xfd\x4a\x28\x96"
126 			  "\x6e\x2a\xf4\xe0\x7c\xfa\x5b\x03"
127 			  "\x2c\x30\xda\x5b\xb8\xe8\xef\x63"
128 			  "\xb9\xa5\x5b\xf9\x63\x26\x23\x34",
129 	}, { /* 64K block size */
130 		.hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
131 		.file_size = 1000000,
132 		.block_size = 65536,
133 		.digest = "\xf3\xb6\x41\x8f\x26\xd4\xd0\xe7"
134 			  "\x47\x28\x19\x3b\xae\x76\xf1\x5c"
135 			  "\xb4\xbb\x2c\xe9\x77\x74\x48\xd7"
136 			  "\x6b\xd8\x13\x8b\x69\xec\x61\xc2",
137 	}, { /* SHA-512 */
138 		.hash_algorithm = FS_VERITY_HASH_ALG_SHA512,
139 		.file_size = 1000000,
140 		.block_size = 4096,
141 		.salt = "abcd",
142 		.digest = "\x84\x25\xc6\xd0\xc9\x4f\x84\xed"
143 			  "\x90\x4c\x12\x93\x68\x45\xfb\xb7"
144 			  "\xaf\x99\x53\x75\x37\x89\x71\x2d"
145 			  "\xcc\x3b\xe1\x42\xdb\x3d\x4b\x6b"
146 			  "\x47\xa3\x99\xad\x52\xaa\x60\x92"
147 			  "\x56\xce\x29\xa9\x60\xbf\x4b\xb0"
148 			  "\xe5\x95\xec\x38\x6c\xa5\x8c\x06"
149 			  "\x51\x9d\x54\x6d\xc5\xb1\x97\xbb",
150 	}, { /* default hash algorithm (SHA-256) and block size (4096) */
151 		.file_size = 100000,
152 		.digest = "\xf2\x09\x6a\x36\xc5\xcd\xca\x4f"
153 			  "\xa3\x3e\xe8\x85\x28\x33\x15\x0b"
154 			  "\xb3\x24\x99\x2e\x54\x17\xa9\xd5"
155 			  "\x71\xf1\xbf\xff\xf7\x3b\x9e\xfc",
156 	},
157 };
158 
fix_digest_and_print(const struct test_case * t,const struct libfsverity_digest * d)159 static void fix_digest_and_print(const struct test_case *t,
160 				 const struct libfsverity_digest *d)
161 {
162 	char alg_name[32] = {};
163 	size_t i;
164 
165 	strncpy(alg_name, libfsverity_get_hash_name(t->hash_algorithm),
166 		sizeof(alg_name) - 1);
167 	for (i = 0; i < sizeof(alg_name) - 1; i++)
168 		alg_name[i] = toupper((u8)alg_name[i]);
169 
170 	printf("\t}, {\n"
171 	       "\t\t.hash_algorithm = FS_VERITY_HASH_ALG_%s,\n"
172 	       "\t\t.file_size = %" PRIu64 ",\n"
173 	       "\t\t.block_size = %u,\n",
174 	       alg_name, t->file_size, t->block_size);
175 	if (t->salt != NULL)
176 		printf("\t\t.salt = \"%s\",\n", t->salt);
177 	for (i = 0; i < d->digest_size; i++) {
178 		if (i == 0)
179 			printf("\t\t.digest = \"");
180 		else if (i % 8 == 0)
181 			printf("\t\t\t  \"");
182 		printf("\\x%02x", d->digest[i]);
183 		if (i + 1 == d->digest_size)
184 			printf("\",\n");
185 		else if (i % 8 == 7)
186 			printf("\"\n");
187 	}
188 }
189 
test_invalid_params(void)190 static void test_invalid_params(void)
191 {
192 	struct mem_file f = { .data = (u8 *)"abcd", .size = 4 };
193 	struct libfsverity_merkle_tree_params good_params = {
194 		.version = 1,
195 		.hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
196 		.file_size = 4,
197 		.block_size = 4096,
198 	};
199 	struct libfsverity_merkle_tree_params params;
200 	struct libfsverity_digest *d = NULL;
201 
202 	libfsverity_set_error_callback(NULL);
203 
204 	ASSERT(libfsverity_compute_digest(&f, read_fn, &good_params, &d) == 0);
205 	f.offset = 0;
206 	free(d);
207 	d = NULL;
208 
209 	/* missing required arguments */
210 	ASSERT(libfsverity_compute_digest(&f, NULL, &good_params, &d) == -EINVAL);
211 	ASSERT(libfsverity_compute_digest(&f, read_fn, NULL, &d) == -EINVAL);
212 	ASSERT(libfsverity_compute_digest(&f, read_fn, &good_params, NULL) == -EINVAL);
213 
214 	/* bad version */
215 	params = good_params;
216 	params.version = 0;
217 	ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
218 	params.version = 1000;
219 	ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
220 
221 	/* bad hash_algorithm */
222 	params = good_params;
223 	params.hash_algorithm = 1000;
224 	ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
225 
226 	/* bad block_size */
227 	params = good_params;
228 	params.block_size = 1;
229 	ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
230 	params.block_size = 4097;
231 	ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
232 
233 	/* bad salt_size */
234 	params = good_params;
235 	params.salt_size = 1000;
236 	ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
237 	params.salt = (u8 *)"";
238 	ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
239 
240 	/* bad reserved fields */
241 	params = good_params;
242 	params.reserved1[0] = 1;
243 	ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
244 	params = good_params;
245 	params.reserved1[ARRAY_SIZE(params.reserved1) - 1] = 1;
246 	ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
247 	params = good_params;
248 	params.reserved2[0] = 1;
249 	ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
250 	params = good_params;
251 	params.reserved2[ARRAY_SIZE(params.reserved2) - 1] = 1;
252 	ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
253 
254 	/* error reading file */
255 	ASSERT(libfsverity_compute_digest(&f, error_read_fn, &good_params, &d) == -EIO);
256 
257 	ASSERT(d == NULL);
258 }
259 
260 static struct {
261 	u64 merkle_tree_size;
262 	u64 merkle_tree_block;
263 	u64 descriptor;
264 } metadata_callback_counts;
265 
handle_merkle_tree_size(void * ctx,u64 size)266 static int handle_merkle_tree_size(void *ctx, u64 size)
267 {
268 	metadata_callback_counts.merkle_tree_size++;
269 
270 	/* Test that the ctx argument is passed through correctly. */
271 	ASSERT(ctx == (void *)1);
272 
273 	/* Test that the expected Merkle tree size is reported. */
274 	ASSERT(size == 5 * 1024);
275 	return 0;
276 }
277 
handle_merkle_tree_block(void * ctx,const void * block,size_t size,u64 offset)278 static int handle_merkle_tree_block(void *ctx, const void *block, size_t size,
279 				    u64 offset)
280 {
281 	u8 digest[SHA256_DIGEST_LENGTH];
282 	u64 count = metadata_callback_counts.merkle_tree_block++;
283 	const char *expected_digest;
284 
285 	/* Test that ->merkle_tree_size() was called first. */
286 	ASSERT(metadata_callback_counts.merkle_tree_size == 1);
287 
288 	/* Test that the ctx argument is passed through correctly. */
289 	ASSERT(ctx == (void *)1);
290 
291 	/*
292 	 * Test that this Merkle tree block has the expected size, offset, and
293 	 * contents.  The 4 blocks at "level 0" should be reported first, in
294 	 * order; then the 1 block at "level 1" should be reported last (but the
295 	 * level 1 block should have the smallest offset).
296 	 */
297 	ASSERT(size == 1024);
298 	SHA256(block, size, digest);
299 	if (count == 4) {
300 		/* 1 block at level 1 */
301 		ASSERT(offset == 0);
302 		expected_digest = "\x68\xc5\x38\xe1\x19\x58\xd6\x5d"
303 				  "\x68\xb6\xfe\x8e\x9f\xb8\xcc\xab"
304 				  "\xec\xfd\x92\x8b\x01\xd0\x63\x44"
305 				  "\xe2\x23\xed\x41\xdd\xc4\x54\x4a";
306 	} else {
307 		/* 4 blocks at level 0 */
308 		ASSERT(offset == 1024 + (count * 1024));
309 		if (count < 3) {
310 			expected_digest = "\xf7\x89\xba\xab\x53\x85\x9f\xaf"
311 					  "\x36\xd6\xd7\x5d\x10\x42\x06\x42"
312 					  "\x94\x20\x2d\x6e\x13\xe7\x71\x6f"
313 					  "\x39\x4f\xba\x43\x4c\xcc\x49\x86";
314 		} else {
315 			expected_digest = "\x00\xfe\xd0\x3c\x5d\x6e\xab\x21"
316 					  "\x31\x43\xf3\xd9\x6a\x5c\xa3\x1c"
317 					  "\x2b\x89\xf5\x68\x4e\x6c\x8e\x07"
318 					  "\x87\x3e\x5e\x97\x65\x17\xb4\x8f";
319 		}
320 	}
321 	ASSERT(!memcmp(digest, expected_digest, SHA256_DIGEST_LENGTH));
322 	return 0;
323 }
324 
325 static const u8 expected_file_digest[SHA256_DIGEST_LENGTH] =
326 	"\x09\xcb\xba\xee\xd2\xa0\x4c\x2d\xa2\x42\xc1\x0e\x15\x68\xd9\x6f"
327 	"\x35\x8a\x16\xaa\x1e\xbe\x8c\xf0\x28\x61\x20\xc1\x3c\x93\x66\xd1";
328 
handle_descriptor(void * ctx,const void * descriptor,size_t size)329 static int handle_descriptor(void *ctx, const void *descriptor, size_t size)
330 {
331 	u8 digest[SHA256_DIGEST_LENGTH];
332 
333 	metadata_callback_counts.descriptor++;
334 	/* Test that the ctx argument is passed through correctly. */
335 	ASSERT(ctx == (void *)1);
336 
337 	/* Test that the fs-verity descriptor is reported correctly. */
338 	ASSERT(size == 256);
339 	SHA256(descriptor, size, digest);
340 	ASSERT(!memcmp(digest, expected_file_digest, SHA256_DIGEST_LENGTH));
341 	return 0;
342 }
343 
344 static const struct libfsverity_metadata_callbacks metadata_callbacks = {
345 	.ctx = (void *)1, /* arbitrary value for testing purposes */
346 	.merkle_tree_size = handle_merkle_tree_size,
347 	.merkle_tree_block = handle_merkle_tree_block,
348 	.descriptor = handle_descriptor,
349 };
350 
351 /* Test that the libfsverity_metadata_callbacks work correctly. */
test_metadata_callbacks(void)352 static void test_metadata_callbacks(void)
353 {
354 	/*
355 	 * For a useful test, we want a file whose Merkle tree will have at
356 	 * least 2 levels (this one will have exactly 2).  The contents of the
357 	 * file aren't too important.
358 	 */
359 	struct libfsverity_merkle_tree_params params = {
360 		.version = 1,
361 		.hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
362 		.block_size = 1024,
363 		.file_size = 100000,
364 		.metadata_callbacks = &metadata_callbacks,
365 	};
366 	struct libfsverity_digest *d;
367 
368 	ASSERT(libfsverity_compute_digest(NULL, zeroes_read_fn,
369 					  &params, &d) == 0);
370 
371 	/* Test that the callbacks were called the correct number of times. */
372 	ASSERT(metadata_callback_counts.merkle_tree_size == 1);
373 	ASSERT(metadata_callback_counts.merkle_tree_block == 5);
374 	ASSERT(metadata_callback_counts.descriptor == 1);
375 
376 	/* Test that the computed file digest is as expected. */
377 	ASSERT(d->digest_algorithm == FS_VERITY_HASH_ALG_SHA256);
378 	ASSERT(d->digest_size == SHA256_DIGEST_LENGTH);
379 	ASSERT(!memcmp(d->digest, expected_file_digest, SHA256_DIGEST_LENGTH));
380 
381 	free(d);
382 }
383 
main(int argc,char * argv[])384 int main(int argc, char *argv[])
385 {
386 	const bool update = (argc == 2 && !strcmp(argv[1], "--update"));
387 	size_t i;
388 	struct mem_file f = {};
389 	struct libfsverity_merkle_tree_params params;
390 	struct libfsverity_digest *d;
391 	int err;
392 
393 	install_libfsverity_error_handler();
394 
395 	for (i = 0; i < ARRAY_SIZE(test_cases); i++)
396 		f.size = max(f.size, test_cases[i].file_size);
397 
398 	f.data = xmalloc(f.size);
399 	for (i = 0; i < f.size; i++)
400 		f.data[i] = (i % 11) + (i % 439) + (i % 1103);
401 
402 	for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
403 		u32 expected_alg = test_cases[i].hash_algorithm ?:
404 				   FS_VERITY_HASH_ALG_SHA256;
405 
406 		memset(&params, 0, sizeof(params));
407 		params.version = 1;
408 		params.hash_algorithm = test_cases[i].hash_algorithm;
409 		params.file_size = test_cases[i].file_size;
410 		params.block_size = test_cases[i].block_size;
411 		if (test_cases[i].salt) {
412 			params.salt = (const u8 *)test_cases[i].salt;
413 			params.salt_size = strlen(test_cases[i].salt);
414 		}
415 
416 		f.size = test_cases[i].file_size;
417 		f.offset = 0;
418 
419 		err = libfsverity_compute_digest(&f, read_fn, &params, &d);
420 		ASSERT(err == 0);
421 
422 		ASSERT(d->digest_algorithm == expected_alg);
423 		ASSERT(d->digest_size ==
424 		       libfsverity_get_digest_size(expected_alg));
425 		if (update)
426 			fix_digest_and_print(&test_cases[i], d);
427 		else
428 			ASSERT(!memcmp(d->digest, test_cases[i].digest,
429 				       d->digest_size));
430 		free(d);
431 		d = NULL;
432 	}
433 	free(f.data);
434 	if (update) {
435 		printf("\t}\n");
436 		return 1;
437 	}
438 
439 	test_invalid_params();
440 	test_metadata_callbacks();
441 	printf("test_compute_digest passed\n");
442 	return 0;
443 }
444