xref: /aosp_15_r20/external/vboot_reference/futility/cmd_create.c (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
1 /* Copyright 2015 The ChromiumOS Authors
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file.
4  */
5 
6 #include <openssl/pem.h>
7 
8 #include <getopt.h>
9 #include <stdio.h>
10 #include <unistd.h>
11 
12 #include "2common.h"
13 #include "2id.h"
14 #include "2rsa.h"
15 #include "2sha.h"
16 #include "2sysincludes.h"
17 #include "futility.h"
18 #include "futility_options.h"
19 #include "host_common21.h"
20 #include "host_key.h"
21 #include "host_key21.h"
22 #include "host_misc21.h"
23 #include "openssl_compat.h"
24 #include "util_misc.h"
25 #include "vboot_host.h"
26 
27 /* Command line options */
28 enum {
29 	OPT_OUTFILE = 1000,
30 	OPT_VERSION,
31 	OPT_DESC,
32 	OPT_ID,
33 	OPT_HASH_ALG,
34 	OPT_HELP,
35 };
36 
37 #define DEFAULT_VERSION 1
38 #define DEFAULT_HASH VB2_HASH_SHA256;
39 
40 static const struct option long_opts[] = {
41 	{"version",  1, 0, OPT_VERSION},
42 	{"desc",     1, 0, OPT_DESC},
43 	{"id",       1, 0, OPT_ID},
44 	{"hash_alg", 1, 0, OPT_HASH_ALG},
45 	{"help",     0, 0, OPT_HELP},
46 	{NULL, 0, 0, 0}
47 };
48 
print_help(int argc,char * argv[])49 static void print_help(int argc, char *argv[])
50 {
51 	enum vb2_hash_algorithm alg;
52 
53 	printf("\n"
54 "Usage:  " MYNAME " %s [options] <INFILE> [<BASENAME>]\n", argv[0]);
55 	printf("\n"
56 "Create a keypair from an RSA key (.pem file).\n"
57 "\n"
58 "Options:\n"
59 "\n"
60 "  --version <number>          Key version (default %d)\n"
61 "  --hash_alg <number>         Hashing algorithm to use:\n",
62 		DEFAULT_VERSION);
63 	for (alg = 0; alg < VB2_HASH_ALG_COUNT; alg++) {
64 		const char *name = vb2_get_hash_algorithm_name(alg);
65 		if (strcmp(name, VB2_INVALID_ALG_NAME) != 0)
66 			printf("                                %d / %s%s\n",
67 			       alg, name,
68 			       alg == VB2_HASH_SHA256 ? " (default)" : "");
69 	}
70 	printf(
71 "  --id <id>                   Identifier for this keypair (vb21 only)\n"
72 "  --desc <text>               Human-readable description (vb21 only)\n"
73 "\n");
74 
75 }
76 
vb1_make_keypair(const char * infile,const char * outfile,char * outext,uint32_t version,enum vb2_hash_algorithm hash_alg)77 static int vb1_make_keypair(const char *infile, const char *outfile,
78 			    char *outext, uint32_t version,
79 			    enum vb2_hash_algorithm hash_alg)
80 {
81 	struct vb2_private_key *privkey = NULL;
82 	struct vb2_packed_key *pubkey = NULL;
83 	struct rsa_st *rsa_key = NULL;
84 	uint8_t *keyb_data = 0;
85 	uint32_t keyb_size;
86 	int ret = 1;
87 
88 	FILE *fp = fopen(infile, "rb");
89 	if (!fp) {
90 		ERROR("Unable to open %s\n", infile);
91 		goto done;
92 	}
93 
94 	/* TODO: this is very similar to vb2_read_private_key_pem() */
95 
96 	rsa_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
97 	fclose(fp);
98 	if (!rsa_key) {
99 		ERROR("Unable to read RSA key from %s\n", infile);
100 		goto done;
101 	}
102 
103 	enum vb2_signature_algorithm sig_alg = vb2_rsa_sig_alg(rsa_key);
104 	if (sig_alg == VB2_SIG_INVALID) {
105 		ERROR("Unsupported sig algorithm in RSA key\n");
106 		goto done;
107 	}
108 
109 	/* Combine the sig_alg with the hash_alg to get the vb1 algorithm */
110 	uint64_t vb1_algorithm = vb2_get_crypto_algorithm(hash_alg, sig_alg);
111 
112 	/* Create the private key */
113 	privkey = (struct vb2_private_key *)calloc(sizeof(*privkey), 1);
114 	if (!privkey)
115 		goto done;
116 
117 	privkey->rsa_private_key = rsa_key;
118 	privkey->sig_alg = sig_alg;
119 	privkey->hash_alg = hash_alg;
120 
121 	/* Write it out */
122 	strcpy(outext, ".vbprivk");
123 	if (vb2_write_private_key(outfile, privkey)) {
124 		ERROR("Unable to write private key\n");
125 		goto done;
126 	}
127 	printf("wrote %s\n", outfile);
128 
129 	/* Create the public key */
130 	ret = vb_keyb_from_rsa(rsa_key, &keyb_data, &keyb_size);
131 	if (ret) {
132 		ERROR("Couldn't extract the public key\n");
133 		goto done;
134 	}
135 
136 	pubkey = vb2_alloc_packed_key(keyb_size, vb1_algorithm, version);
137 	if (!pubkey)
138 		goto done;
139 	memcpy((uint8_t *)vb2_packed_key_data(pubkey), keyb_data, keyb_size);
140 
141 	/* Write it out */
142 	strcpy(outext, ".vbpubk");
143 	if (VB2_SUCCESS != vb2_write_packed_key(outfile, pubkey)) {
144 		ERROR("Unable to write public key\n");
145 		goto done;
146 	}
147 	printf("wrote %s\n", outfile);
148 
149 	ret = 0;
150 
151 done:
152 	free(privkey);
153 	free(pubkey);
154 	free(keyb_data);
155 	RSA_free(rsa_key);
156 	return ret;
157 }
158 
vb2_make_keypair(const char * infile,const char * outfile,char * outext,char * desc,struct vb2_id * id,bool force_id,uint32_t version,enum vb2_hash_algorithm hash_alg)159 static int vb2_make_keypair(const char *infile, const char *outfile,
160 			    char *outext, char *desc, struct vb2_id *id,
161 			    bool force_id, uint32_t version,
162 			    enum vb2_hash_algorithm hash_alg)
163 {
164 	struct vb2_private_key *privkey = 0;
165 	struct vb2_public_key *pubkey = 0;
166 	RSA *rsa_key = 0;
167 	uint8_t *keyb_data = 0;
168 	uint32_t keyb_size;
169 	enum vb2_signature_algorithm sig_alg;
170 	uint8_t *pubkey_buf = 0;
171 	int has_priv = 0;
172 	const BIGNUM *rsa_d;
173 
174 	FILE *fp;
175 	int ret = 1;
176 
177 	fp = fopen(infile, "rb");
178 	if (!fp) {
179 		ERROR("Unable to open %s\n", infile);
180 		goto done;
181 	}
182 
183 	rsa_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
184 
185 	if (!rsa_key) {
186 		/* Check if the PEM contains only a public key */
187 		if (fseek(fp, 0, SEEK_SET)) {
188 			ERROR("Seeking in %s\n", infile);
189 			goto done;
190 		}
191 		rsa_key = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
192 	}
193 	fclose(fp);
194 	if (!rsa_key) {
195 		ERROR("Unable to read RSA key from %s\n", infile);
196 		goto done;
197 	}
198 	/* Public keys doesn't have the private exponent */
199 	RSA_get0_key(rsa_key, NULL, NULL, &rsa_d);
200 	has_priv = !!rsa_d;
201 	if (!has_priv)
202 		ERROR("%s has a public key only.\n", infile);
203 
204 	sig_alg = vb2_rsa_sig_alg(rsa_key);
205 	if (sig_alg == VB2_SIG_INVALID) {
206 		ERROR("Unsupported sig algorithm in RSA key\n");
207 		goto done;
208 	}
209 
210 	if (has_priv) {
211 		/* Create the private key */
212 		privkey = calloc(1, sizeof(*privkey));
213 		if (!privkey) {
214 			ERROR("Unable to allocate the private key\n");
215 			goto done;
216 		}
217 
218 		privkey->rsa_private_key = rsa_key;
219 		privkey->sig_alg = sig_alg;
220 		privkey->hash_alg = hash_alg;
221 		if (desc && vb2_private_key_set_desc(privkey, desc)) {
222 			ERROR("Unable to set the private key description\n");
223 			goto done;
224 		}
225 	}
226 
227 	/* Create the public key */
228 	if (vb2_public_key_alloc(&pubkey, sig_alg)) {
229 		ERROR("Unable to allocate the public key\n");
230 		goto done;
231 	}
232 
233 	/* Extract the keyb blob */
234 	if (vb_keyb_from_rsa(rsa_key, &keyb_data, &keyb_size)) {
235 		ERROR("Couldn't extract the public key\n");
236 		goto done;
237 	}
238 
239 	/*
240 	 * Copy the keyb blob to the public key's buffer, because that's where
241 	 * vb2_unpack_key_data() and vb2_public_key_pack() expect to find it.
242 	 */
243 	pubkey_buf = vb2_public_key_packed_data(pubkey);
244 	memcpy(pubkey_buf, keyb_data, keyb_size);
245 
246 	/* Fill in the internal struct pointers */
247 	if (vb2_unpack_key_data(pubkey, pubkey_buf, keyb_size)) {
248 		ERROR("Unable to unpack the public key blob\n");
249 		goto done;
250 	}
251 
252 	pubkey->hash_alg = hash_alg;
253 	pubkey->version = version;
254 	if (desc && vb2_public_key_set_desc(pubkey, desc)) {
255 		ERROR("Unable to set pubkey description\n");
256 		goto done;
257 	}
258 
259 	/* Update the IDs */
260 	if (!force_id) {
261 		struct vb2_hash hash;
262 		vb2_hash_calculate(false, keyb_data, keyb_size, VB2_HASH_SHA1,
263 				   &hash);
264 		memcpy(id->raw, hash.raw, sizeof(id->raw));
265 	}
266 
267 	memcpy((struct vb2_id *)pubkey->id, id, sizeof(*id));
268 
269 	/* Write them out */
270 	if (has_priv) {
271 		privkey->id = *id;
272 		strcpy(outext, ".vbprik2");
273 		if (vb21_private_key_write(privkey, outfile)) {
274 			ERROR("Unable to write private key\n");
275 			goto done;
276 		}
277 		printf("wrote %s\n", outfile);
278 	}
279 
280 	strcpy(outext, ".vbpubk2");
281 	if (vb21_public_key_write(pubkey, outfile)) {
282 		ERROR("Unable to write public key\n");
283 		goto done;
284 	}
285 	printf("wrote %s\n", outfile);
286 
287 	ret = 0;
288 
289 done:
290 	RSA_free(rsa_key);
291 	if (privkey)				/* prevent double-free */
292 		privkey->rsa_private_key = 0;
293 	vb2_free_private_key(privkey);
294 	vb2_public_key_free(pubkey);
295 	free(keyb_data);
296 	return ret;
297 }
298 
do_create(int argc,char * argv[])299 static int do_create(int argc, char *argv[])
300 {
301 	int errorcnt = 0;
302 	int i;
303 	char *e;
304 	char *opt_desc = NULL;
305 	struct vb2_id opt_id;
306 	bool force_id = false;
307 	uint32_t opt_version = DEFAULT_VERSION;
308 	enum vb2_hash_algorithm opt_hash_alg = DEFAULT_HASH;
309 
310 
311 	while ((i = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
312 		switch (i) {
313 
314 		case OPT_VERSION:
315 			opt_version = strtoul(optarg, &e, 0);
316 			if (!*optarg || (e && *e)) {
317 				ERROR("Invalid version \"%s\"\n", optarg);
318 				errorcnt = 1;
319 			}
320 			break;
321 
322 		case OPT_DESC:
323 			opt_desc = optarg;
324 			break;
325 
326 		case OPT_ID:
327 			if (VB2_SUCCESS != vb2_str_to_id(optarg, &opt_id)) {
328 				ERROR("Invalid id \"%s\"\n", optarg);
329 				errorcnt = 1;
330 			}
331 			force_id = true;
332 			break;
333 
334 		case OPT_HASH_ALG:
335 			if (!vb2_lookup_hash_alg(optarg, &opt_hash_alg)) {
336 				ERROR("Invalid hash_alg \"%s\"\n", optarg);
337 				errorcnt++;
338 			}
339 			break;
340 
341 		case OPT_HELP:
342 			print_help(argc, argv);
343 			return !!errorcnt;
344 
345 		case '?':
346 			if (optopt)
347 				ERROR("Unrecognized option: -%c\n",
348 					optopt);
349 			else
350 				ERROR("Unrecognized option\n");
351 			errorcnt++;
352 			break;
353 		case ':':
354 			ERROR("Missing argument to -%c\n", optopt);
355 			errorcnt++;
356 			break;
357 		case 0:				/* handled option */
358 			break;
359 		default:
360 			FATAL("Unrecognized getopt output: %d\n", i);
361 		}
362 	}
363 
364 	if (argc - optind <= 0) {
365 		ERROR("Missing input filename\n");
366 		errorcnt++;
367 	}
368 	if (errorcnt) {
369 		print_help(argc, argv);
370 		return 1;
371 	}
372 	char *infile = argv[optind++];
373 
374 	/* Decide how to determine the output filenames. */
375 	bool remove_ext = false;
376 	char *s;
377 	if (argc > optind) {
378 		s = argv[optind++];		/* just use this */
379 	} else {
380 		s = infile;			/* based on pem file name */
381 		remove_ext = true;
382 	}
383 
384 	/* Make an extra-large copy to leave room for filename extensions */
385 	char *outfile = (char *)malloc(strlen(s) + 20);
386 	if (!outfile) {
387 		ERROR("malloc() failed\n");
388 		return 1;
389 	}
390 	strcpy(outfile, s);
391 
392 	if (remove_ext) {
393 		/* Find the last '/' if any, then the last '.' before that. */
394 		s = strrchr(outfile, '/');
395 		if (!s)
396 			s = outfile;
397 		s = strrchr(s, '.');
398 		/* Cut off the extension */
399 		if (s)
400 			*s = '\0';
401 	}
402 	/* Remember that spot for later */
403 	char *outext = outfile + strlen(outfile);
404 
405 	/* Okay, do it */
406 	int r;
407 	if (vboot_version == VBOOT_VERSION_1_0)
408 		r = vb1_make_keypair(infile, outfile, outext, opt_version,
409 				     opt_hash_alg);
410 	else
411 		r = vb2_make_keypair(infile, outfile, outext, opt_desc, &opt_id,
412 				     force_id, opt_version, opt_hash_alg);
413 
414 	free(outfile);
415 	return r;
416 }
417 
418 DECLARE_FUTIL_COMMAND(create, do_create, VBOOT_VERSION_ALL,
419 		      "Create a keypair from an RSA .pem file");
420