1 // SPDX-License-Identifier: MIT
2 /*
3 * The 'fsverity sign' command
4 *
5 * Copyright 2018 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 "fsverity.h"
13
14 #include <fcntl.h>
15 #include <getopt.h>
16
write_signature(const char * filename,const u8 * sig,u32 sig_size)17 static bool write_signature(const char *filename, const u8 *sig, u32 sig_size)
18 {
19 struct filedes file;
20 bool ok;
21
22 if (!open_file(&file, filename, O_WRONLY|O_CREAT|O_TRUNC, 0644))
23 return false;
24 ok = full_write(&file, sig, sig_size);
25 ok &= filedes_close(&file);
26 return ok;
27 }
28
29 static const struct option longopts[] = {
30 {"key", required_argument, NULL, OPT_KEY},
31 {"cert", required_argument, NULL, OPT_CERT},
32 {"pkcs11-engine", required_argument, NULL, OPT_PKCS11_ENGINE},
33 {"pkcs11-module", required_argument, NULL, OPT_PKCS11_MODULE},
34 {"pkcs11-keyid", required_argument, NULL, OPT_PKCS11_KEYID},
35 {"hash-alg", required_argument, NULL, OPT_HASH_ALG},
36 {"block-size", required_argument, NULL, OPT_BLOCK_SIZE},
37 {"salt", required_argument, NULL, OPT_SALT},
38 {"out-merkle-tree", required_argument, NULL, OPT_OUT_MERKLE_TREE},
39 {"out-descriptor", required_argument, NULL, OPT_OUT_DESCRIPTOR},
40 {NULL, 0, NULL, 0}
41 };
42
43 /* Sign a file for fs-verity by computing its digest, then signing it. */
fsverity_cmd_sign(const struct fsverity_command * cmd,int argc,char * argv[])44 int fsverity_cmd_sign(const struct fsverity_command *cmd,
45 int argc, char *argv[])
46 {
47 struct filedes file = { .fd = -1 };
48 struct libfsverity_merkle_tree_params tree_params = { .version = 1 };
49 struct libfsverity_signature_params sig_params = {};
50 struct libfsverity_digest *digest = NULL;
51 char digest_hex[FS_VERITY_MAX_DIGEST_SIZE * 2 + 1];
52 u8 *sig = NULL;
53 size_t sig_size;
54 int status;
55 int c;
56
57 while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
58 switch (c) {
59 case OPT_KEY:
60 if (sig_params.keyfile != NULL) {
61 error_msg("--key can only be specified once");
62 goto out_usage;
63 }
64 sig_params.keyfile = optarg;
65 break;
66 case OPT_CERT:
67 if (sig_params.certfile != NULL) {
68 error_msg("--cert can only be specified once");
69 goto out_usage;
70 }
71 sig_params.certfile = optarg;
72 break;
73 case OPT_PKCS11_ENGINE:
74 if (sig_params.pkcs11_engine != NULL) {
75 error_msg("--pkcs11-engine can only be specified once");
76 goto out_usage;
77 }
78 sig_params.pkcs11_engine = optarg;
79 break;
80 case OPT_PKCS11_MODULE:
81 if (sig_params.pkcs11_module != NULL) {
82 error_msg("--pkcs11-module can only be specified once");
83 goto out_usage;
84 }
85 sig_params.pkcs11_module = optarg;
86 break;
87 case OPT_PKCS11_KEYID:
88 if (sig_params.pkcs11_keyid != NULL) {
89 error_msg("--pkcs11-keyid can only be specified once");
90 goto out_usage;
91 }
92 sig_params.pkcs11_keyid = optarg;
93 break;
94 case OPT_HASH_ALG:
95 case OPT_BLOCK_SIZE:
96 case OPT_SALT:
97 case OPT_OUT_MERKLE_TREE:
98 case OPT_OUT_DESCRIPTOR:
99 if (!parse_tree_param(c, optarg, &tree_params))
100 goto out_usage;
101 break;
102 default:
103 goto out_usage;
104 }
105 }
106
107 argv += optind;
108 argc -= optind;
109
110 if (argc != 2)
111 goto out_usage;
112
113 if (sig_params.certfile == NULL)
114 sig_params.certfile = sig_params.keyfile;
115
116 if (!open_file(&file, argv[0], O_RDONLY, 0))
117 goto out_err;
118
119 if (!get_file_size(&file, &tree_params.file_size))
120 goto out_err;
121
122 if (libfsverity_compute_digest(&file, read_callback,
123 &tree_params, &digest) != 0) {
124 error_msg("failed to compute digest");
125 goto out_err;
126 }
127
128 if (libfsverity_sign_digest(digest, &sig_params,
129 &sig, &sig_size) != 0) {
130 error_msg("failed to sign digest");
131 goto out_err;
132 }
133
134 if (!write_signature(argv[1], sig, sig_size))
135 goto out_err;
136
137 ASSERT(digest->digest_size <= FS_VERITY_MAX_DIGEST_SIZE);
138 bin2hex(digest->digest, digest->digest_size, digest_hex);
139 printf("Signed file '%s' (%s:%s)\n", argv[0],
140 libfsverity_get_hash_name(digest->digest_algorithm), digest_hex);
141 status = 0;
142 out:
143 filedes_close(&file);
144 if (!destroy_tree_params(&tree_params) && status == 0)
145 status = 1;
146 free(digest);
147 free(sig);
148 return status;
149
150 out_err:
151 status = 1;
152 goto out;
153
154 out_usage:
155 usage(cmd, stderr);
156 status = 2;
157 goto out;
158 }
159