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