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