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, ¶ms, &d) == -EINVAL);
218 params.version = 1000;
219 ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &d) == -EINVAL);
220
221 /* bad hash_algorithm */
222 params = good_params;
223 params.hash_algorithm = 1000;
224 ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &d) == -EINVAL);
225
226 /* bad block_size */
227 params = good_params;
228 params.block_size = 1;
229 ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &d) == -EINVAL);
230 params.block_size = 4097;
231 ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &d) == -EINVAL);
232
233 /* bad salt_size */
234 params = good_params;
235 params.salt_size = 1000;
236 ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &d) == -EINVAL);
237 params.salt = (u8 *)"";
238 ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &d) == -EINVAL);
239
240 /* bad reserved fields */
241 params = good_params;
242 params.reserved1[0] = 1;
243 ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &d) == -EINVAL);
244 params = good_params;
245 params.reserved1[ARRAY_SIZE(params.reserved1) - 1] = 1;
246 ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &d) == -EINVAL);
247 params = good_params;
248 params.reserved2[0] = 1;
249 ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &d) == -EINVAL);
250 params = good_params;
251 params.reserved2[ARRAY_SIZE(params.reserved2) - 1] = 1;
252 ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &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 ¶ms, &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(¶ms, 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, ¶ms, &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