1*b13c0e40SEric Biggers // SPDX-License-Identifier: MIT
2*b13c0e40SEric Biggers /*
3*b13c0e40SEric Biggers * The 'fsverity digest' command
4*b13c0e40SEric Biggers *
5*b13c0e40SEric Biggers * Copyright 2020 Microsoft
6*b13c0e40SEric Biggers *
7*b13c0e40SEric Biggers * Use of this source code is governed by an MIT-style
8*b13c0e40SEric Biggers * license that can be found in the LICENSE file or at
9*b13c0e40SEric Biggers * https://opensource.org/licenses/MIT.
10*b13c0e40SEric Biggers */
11*b13c0e40SEric Biggers
12*b13c0e40SEric Biggers #include "fsverity.h"
13*b13c0e40SEric Biggers
14*b13c0e40SEric Biggers #include <fcntl.h>
15*b13c0e40SEric Biggers #include <getopt.h>
16*b13c0e40SEric Biggers
17*b13c0e40SEric Biggers static const struct option longopts[] = {
18*b13c0e40SEric Biggers {"hash-alg", required_argument, NULL, OPT_HASH_ALG},
19*b13c0e40SEric Biggers {"block-size", required_argument, NULL, OPT_BLOCK_SIZE},
20*b13c0e40SEric Biggers {"salt", required_argument, NULL, OPT_SALT},
21*b13c0e40SEric Biggers {"out-merkle-tree", required_argument, NULL, OPT_OUT_MERKLE_TREE},
22*b13c0e40SEric Biggers {"out-descriptor", required_argument, NULL, OPT_OUT_DESCRIPTOR},
23*b13c0e40SEric Biggers {"compact", no_argument, NULL, OPT_COMPACT},
24*b13c0e40SEric Biggers {"for-builtin-sig", no_argument, NULL, OPT_FOR_BUILTIN_SIG},
25*b13c0e40SEric Biggers {NULL, 0, NULL, 0}
26*b13c0e40SEric Biggers };
27*b13c0e40SEric Biggers
28*b13c0e40SEric Biggers /*
29*b13c0e40SEric Biggers * Compute the fs-verity digest of the given file(s), for offline signing.
30*b13c0e40SEric Biggers */
fsverity_cmd_digest(const struct fsverity_command * cmd,int argc,char * argv[])31*b13c0e40SEric Biggers int fsverity_cmd_digest(const struct fsverity_command *cmd,
32*b13c0e40SEric Biggers int argc, char *argv[])
33*b13c0e40SEric Biggers {
34*b13c0e40SEric Biggers struct filedes file = { .fd = -1 };
35*b13c0e40SEric Biggers struct libfsverity_merkle_tree_params tree_params = { .version = 1 };
36*b13c0e40SEric Biggers bool compact = false, for_builtin_sig = false;
37*b13c0e40SEric Biggers int status;
38*b13c0e40SEric Biggers int c;
39*b13c0e40SEric Biggers
40*b13c0e40SEric Biggers while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
41*b13c0e40SEric Biggers switch (c) {
42*b13c0e40SEric Biggers case OPT_HASH_ALG:
43*b13c0e40SEric Biggers case OPT_BLOCK_SIZE:
44*b13c0e40SEric Biggers case OPT_SALT:
45*b13c0e40SEric Biggers case OPT_OUT_MERKLE_TREE:
46*b13c0e40SEric Biggers case OPT_OUT_DESCRIPTOR:
47*b13c0e40SEric Biggers if (!parse_tree_param(c, optarg, &tree_params))
48*b13c0e40SEric Biggers goto out_usage;
49*b13c0e40SEric Biggers break;
50*b13c0e40SEric Biggers case OPT_COMPACT:
51*b13c0e40SEric Biggers compact = true;
52*b13c0e40SEric Biggers break;
53*b13c0e40SEric Biggers case OPT_FOR_BUILTIN_SIG:
54*b13c0e40SEric Biggers for_builtin_sig = true;
55*b13c0e40SEric Biggers break;
56*b13c0e40SEric Biggers default:
57*b13c0e40SEric Biggers goto out_usage;
58*b13c0e40SEric Biggers }
59*b13c0e40SEric Biggers }
60*b13c0e40SEric Biggers
61*b13c0e40SEric Biggers argv += optind;
62*b13c0e40SEric Biggers argc -= optind;
63*b13c0e40SEric Biggers
64*b13c0e40SEric Biggers if (argc < 1)
65*b13c0e40SEric Biggers goto out_usage;
66*b13c0e40SEric Biggers
67*b13c0e40SEric Biggers for (int i = 0; i < argc; i++) {
68*b13c0e40SEric Biggers struct fsverity_formatted_digest *d = NULL;
69*b13c0e40SEric Biggers struct libfsverity_digest *digest = NULL;
70*b13c0e40SEric Biggers char digest_hex[FS_VERITY_MAX_DIGEST_SIZE * 2 +
71*b13c0e40SEric Biggers sizeof(*d) * 2 + 1];
72*b13c0e40SEric Biggers
73*b13c0e40SEric Biggers if (!open_file(&file, argv[i], O_RDONLY, 0))
74*b13c0e40SEric Biggers goto out_err;
75*b13c0e40SEric Biggers
76*b13c0e40SEric Biggers if (!get_file_size(&file, &tree_params.file_size))
77*b13c0e40SEric Biggers goto out_err;
78*b13c0e40SEric Biggers
79*b13c0e40SEric Biggers if (libfsverity_compute_digest(&file, read_callback,
80*b13c0e40SEric Biggers &tree_params, &digest) != 0) {
81*b13c0e40SEric Biggers error_msg("failed to compute digest");
82*b13c0e40SEric Biggers goto out_err;
83*b13c0e40SEric Biggers }
84*b13c0e40SEric Biggers
85*b13c0e40SEric Biggers ASSERT(digest->digest_size <= FS_VERITY_MAX_DIGEST_SIZE);
86*b13c0e40SEric Biggers
87*b13c0e40SEric Biggers if (for_builtin_sig) {
88*b13c0e40SEric Biggers /*
89*b13c0e40SEric Biggers * Format the digest for use with the built-in signature
90*b13c0e40SEric Biggers * support.
91*b13c0e40SEric Biggers */
92*b13c0e40SEric Biggers d = xzalloc(sizeof(*d) + digest->digest_size);
93*b13c0e40SEric Biggers memcpy(d->magic, "FSVerity", 8);
94*b13c0e40SEric Biggers d->digest_algorithm =
95*b13c0e40SEric Biggers cpu_to_le16(digest->digest_algorithm);
96*b13c0e40SEric Biggers d->digest_size = cpu_to_le16(digest->digest_size);
97*b13c0e40SEric Biggers memcpy(d->digest, digest->digest, digest->digest_size);
98*b13c0e40SEric Biggers
99*b13c0e40SEric Biggers bin2hex((const u8 *)d, sizeof(*d) + digest->digest_size,
100*b13c0e40SEric Biggers digest_hex);
101*b13c0e40SEric Biggers } else {
102*b13c0e40SEric Biggers bin2hex(digest->digest, digest->digest_size,
103*b13c0e40SEric Biggers digest_hex);
104*b13c0e40SEric Biggers }
105*b13c0e40SEric Biggers
106*b13c0e40SEric Biggers if (compact)
107*b13c0e40SEric Biggers printf("%s\n", digest_hex);
108*b13c0e40SEric Biggers else if (for_builtin_sig)
109*b13c0e40SEric Biggers printf("%s %s\n", digest_hex, argv[i]);
110*b13c0e40SEric Biggers else
111*b13c0e40SEric Biggers printf("%s:%s %s\n",
112*b13c0e40SEric Biggers libfsverity_get_hash_name(digest->digest_algorithm),
113*b13c0e40SEric Biggers digest_hex, argv[i]);
114*b13c0e40SEric Biggers
115*b13c0e40SEric Biggers filedes_close(&file);
116*b13c0e40SEric Biggers free(digest);
117*b13c0e40SEric Biggers free(d);
118*b13c0e40SEric Biggers }
119*b13c0e40SEric Biggers status = 0;
120*b13c0e40SEric Biggers out:
121*b13c0e40SEric Biggers if (!destroy_tree_params(&tree_params) && status == 0)
122*b13c0e40SEric Biggers status = 1;
123*b13c0e40SEric Biggers return status;
124*b13c0e40SEric Biggers
125*b13c0e40SEric Biggers out_err:
126*b13c0e40SEric Biggers filedes_close(&file);
127*b13c0e40SEric Biggers status = 1;
128*b13c0e40SEric Biggers goto out;
129*b13c0e40SEric Biggers
130*b13c0e40SEric Biggers out_usage:
131*b13c0e40SEric Biggers usage(cmd, stderr);
132*b13c0e40SEric Biggers status = 2;
133*b13c0e40SEric Biggers goto out;
134*b13c0e40SEric Biggers }
135