1 /* Copyright 2015 The Chromium OS Authors. All rights reserved.
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 <getopt.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 
10 #include <openssl/pem.h>
11 
12 #include "2sysincludes.h"
13 #include "2common.h"
14 #include "2guid.h"
15 #include "2rsa.h"
16 #include "util_misc.h"
17 #include "vb2_common.h"
18 #include "vb2_struct.h"
19 
20 #include "host_key.h"
21 #include "host_key2.h"
22 #include "host_misc2.h"
23 
24 #include "futility.h"
25 
26 /* Command line options */
27 enum {
28 	OPT_OUTFILE = 1000,
29 	OPT_VERSION,
30 	OPT_DESC,
31 	OPT_GUID,
32 	OPT_HASH_ALG,
33 };
34 
35 #define DEFAULT_VERSION 1
36 #define DEFAULT_HASH VB2_HASH_SHA256;
37 
38 static char *infile, *outfile, *outext;
39 static uint32_t opt_version = DEFAULT_VERSION;
40 enum vb2_hash_algorithm opt_hash_alg = DEFAULT_HASH;
41 static char *opt_desc;
42 static struct vb2_guid opt_guid;
43 
44 static const struct option long_opts[] = {
45 	{"version",  1, 0, OPT_VERSION},
46 	{"desc",     1, 0, OPT_DESC},
47 	{"guid",     1, 0, OPT_GUID},
48 	{"hash_alg", 1, 0, OPT_HASH_ALG},
49 	{NULL, 0, 0, 0}
50 };
51 
print_help(const char * progname)52 static void print_help(const char *progname)
53 {
54 	struct vb2_text_vs_enum *entry;
55 
56 	printf("\n"
57 "Usage:  " MYNAME " %s [options] <INFILE> [<BASENAME>]\n", progname);
58 	printf("\n"
59 "Create a keypair from an RSA key (.pem file).\n"
60 "\n"
61 "Options:\n"
62 "\n"
63 "  --version <number>          Key version (default %d)\n"
64 "  --hash_alg <number>         Hashing algorithm to use:\n",
65 		DEFAULT_VERSION);
66 	for (entry = vb2_text_vs_hash; entry->name; entry++)
67 		printf("                                %d / %s%s\n",
68 		       entry->num, entry->name,
69 		       entry->num == VB2_HASH_SHA256 ? " (default)" : "");
70 	printf(
71 "  --guid <guid>               Identifier for this keypair (vb21 only)\n"
72 "  --desc <text>               Human-readable description (vb21 only)\n"
73 "\n");
74 
75 }
76 
vb1_make_keypair()77 static int vb1_make_keypair()
78 {
79 	VbPrivateKey *privkey = 0;
80 	VbPublicKey *pubkey = 0;
81 	RSA *rsa_key = 0;
82 	uint8_t *keyb_data = 0;
83 	uint32_t keyb_size;
84 	enum vb2_signature_algorithm sig_alg;
85 	uint64_t vb1_algorithm;
86 	FILE *fp;
87 	int ret = 1;
88 
89 	fp = fopen(infile, "rb");
90 	if (!fp) {
91 		fprintf(stderr, "Unable to open %s\n", infile);
92 		goto done;
93 	}
94 
95 	rsa_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
96 	fclose(fp);
97 
98 	if (!rsa_key) {
99 		fprintf(stderr, "Unable to read RSA key from %s\n", infile);
100 		goto done;
101 	}
102 
103 	sig_alg = vb2_rsa_sig_alg(rsa_key);
104 	if (sig_alg == VB2_SIG_INVALID) {
105 		fprintf(stderr, "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 	vb1_algorithm = (sig_alg - VB2_SIG_RSA1024) * 3
111 		+ opt_hash_alg - VB2_HASH_SHA1;
112 
113 	/* Create the private key */
114 	privkey = (VbPrivateKey *)malloc(sizeof(VbPrivateKey));
115 	if (!privkey)
116 		goto done;
117 
118 	privkey->rsa_private_key = rsa_key;
119 	privkey->algorithm = vb1_algorithm;
120 
121 	/* Write it out */
122 	strcpy(outext, ".vbprivk");
123 	if (0 != PrivateKeyWrite(outfile, privkey)) {
124 		fprintf(stderr, "unable to write private key\n");
125 		goto done;
126 	}
127 	fprintf(stderr, "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 		fprintf(stderr, "couldn't extract the public key\n");
133 		goto done;
134 	}
135 
136 	pubkey = PublicKeyAlloc(keyb_size, vb1_algorithm, opt_version);
137 	if (!pubkey)
138 		goto done;
139 	memcpy(GetPublicKeyData(pubkey), keyb_data, keyb_size);
140 
141 	/* Write it out */
142 	strcpy(outext, ".vbpubk");
143 	if (0 != PublicKeyWrite(outfile, pubkey)) {
144 		fprintf(stderr, "unable to write public key\n");
145 		goto done;
146 	}
147 	fprintf(stderr, "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()159 static int vb2_make_keypair()
160 {
161 	struct vb2_private_key *privkey = 0;
162 	struct vb2_public_key *pubkey = 0;
163 	RSA *rsa_key = 0;
164 	uint8_t *keyb_data = 0;
165 	uint32_t keyb_size;
166 	enum vb2_signature_algorithm sig_alg;
167 	uint8_t *pubkey_buf = 0;
168 
169 	FILE *fp;
170 	int ret = 1;
171 
172 	fp = fopen(infile, "rb");
173 	if (!fp) {
174 		fprintf(stderr, "Unable to open %s\n", infile);
175 		goto done;
176 	}
177 
178 	rsa_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
179 	fclose(fp);
180 
181 	if (!rsa_key) {
182 		fprintf(stderr, "Unable to read RSA key from %s\n", infile);
183 		goto done;
184 	}
185 
186 	sig_alg = vb2_rsa_sig_alg(rsa_key);
187 	if (sig_alg == VB2_SIG_INVALID) {
188 		fprintf(stderr, "Unsupported sig algorithm in RSA key\n");
189 		goto done;
190 	}
191 
192 	/* Create the private key */
193 	privkey = calloc(1, sizeof(*privkey));
194 	if (!privkey) {
195 		fprintf(stderr, "Unable to allocate the private key\n");
196 		goto done;
197 	}
198 	privkey->rsa_private_key = rsa_key;
199 	privkey->sig_alg = sig_alg;
200 	privkey->hash_alg = opt_hash_alg;
201 	privkey->guid = opt_guid;
202 	if (opt_desc && vb2_private_key_set_desc(privkey, opt_desc)) {
203 		fprintf(stderr, "Unable to set the private key description\n");
204 		goto done;
205 	}
206 
207 	/* Write it out */
208 	strcpy(outext, ".vbprik2");
209 	if (vb2_private_key_write(privkey, outfile)) {
210 		fprintf(stderr, "unable to write private key\n");
211 		goto done;
212 	}
213 	fprintf(stderr, "wrote %s\n", outfile);
214 
215 	/* Create the public key */
216 	if (vb2_public_key_alloc(&pubkey, sig_alg)) {
217 		fprintf(stderr, "Unable to allocate the public key\n");
218 		goto done;
219 	}
220 
221 	/* Extract the keyb blob */
222 	if (vb_keyb_from_rsa(rsa_key, &keyb_data, &keyb_size)) {
223 		fprintf(stderr, "Couldn't extract the public key\n");
224 		goto done;
225 	}
226 
227 	/*
228 	 * Copy the keyb blob to the public key's buffer, because that's where
229 	 * vb2_unpack_key_data() and vb2_public_key_pack() expect to find it.
230 	 */
231 	pubkey_buf = vb2_public_key_packed_data(pubkey);
232 	memcpy(pubkey_buf, keyb_data, keyb_size);
233 
234 	/* Fill in the internal struct pointers */
235 	if (vb2_unpack_key_data(pubkey, pubkey_buf, keyb_size)) {
236 		fprintf(stderr, "Unable to unpack the public key blob\n");
237 		goto done;
238 	}
239 
240 	pubkey->hash_alg = opt_hash_alg;
241 	pubkey->version = opt_version;
242 	memcpy((struct vb2_guid *)pubkey->guid, &opt_guid, sizeof(opt_guid));
243 	if (opt_desc && vb2_public_key_set_desc(pubkey, opt_desc)) {
244 		fprintf(stderr, "Unable to set pubkey description\n");
245 		goto done;
246 	}
247 
248 	/* Write it out */
249 	strcpy(outext, ".vbpubk2");
250 	if (vb2_public_key_write(pubkey, outfile)) {
251 		fprintf(stderr, "unable to write public key\n");
252 		goto done;
253 	}
254 	fprintf(stderr, "wrote %s\n", outfile);
255 
256 	ret = 0;
257 
258 done:
259 	RSA_free(rsa_key);
260 	if (privkey)				/* prevent double-free */
261 		privkey->rsa_private_key = 0;
262 	vb2_private_key_free(privkey);
263 	vb2_public_key_free(pubkey);
264 	free(keyb_data);
265 	return ret;
266 }
267 
do_create(int argc,char * argv[])268 static int do_create(int argc, char *argv[])
269 {
270 	int errorcnt = 0;
271 	char *e, *s;
272 	int i, r, len, remove_ext = 0;
273 	const struct vb2_text_vs_enum *entry;
274 
275 	while ((i = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
276 		switch (i) {
277 
278 		case OPT_VERSION:
279 			opt_version = strtoul(optarg, &e, 0);
280 			if (!*optarg || (e && *e)) {
281 				fprintf(stderr,
282 					"invalid version \"%s\"\n", optarg);
283 				errorcnt = 1;
284 			}
285 			break;
286 
287 		case OPT_DESC:
288 			opt_desc = optarg;
289 			break;
290 
291 		case OPT_GUID:
292 			if (VB2_SUCCESS != vb2_str_to_guid(optarg,
293 							   &opt_guid)) {
294 				fprintf(stderr, "invalid guid \"%s\"\n",
295 					optarg);
296 				errorcnt = 1;
297 			}
298 			break;
299 
300 		case OPT_HASH_ALG:
301 			/* try string first */
302 			entry = vb2_lookup_by_name(vb2_text_vs_hash, optarg);
303 			if (entry) {
304 				opt_hash_alg = entry->num;
305 				break;
306 			}
307 			/* fine, try number */
308 			opt_hash_alg = strtoul(optarg, &e, 0);
309 			if (!*optarg || (e && *e)) {
310 				fprintf(stderr,
311 					"invalid hash_alg \"%s\"\n", optarg);
312 				errorcnt++;
313 				break;
314 			}
315 			if (!vb2_lookup_by_num(vb2_text_vs_hash,
316 					       opt_hash_alg)) {
317 				fprintf(stderr,
318 					"Hash algorithm %d is unsupported\n",
319 					opt_hash_alg);
320 				errorcnt++;
321 			}
322 			break;
323 
324 		case '?':
325 			if (optopt)
326 				fprintf(stderr, "Unrecognized option: -%c\n",
327 					optopt);
328 			else
329 				fprintf(stderr, "Unrecognized option\n");
330 			errorcnt++;
331 			break;
332 		case ':':
333 			fprintf(stderr, "Missing argument to -%c\n", optopt);
334 			errorcnt++;
335 			break;
336 		case 0:				/* handled option */
337 			break;
338 		default:
339 			DIE;
340 		}
341 	}
342 
343 	/* If we don't have an input file already, we need one */
344 	if (!infile) {
345 		if (argc - optind <= 0) {
346 			fprintf(stderr, "ERROR: missing input filename\n");
347 			errorcnt++;
348 		} else {
349 			infile = argv[optind++];
350 		}
351 	}
352 
353 	if (errorcnt) {
354 		print_help(argv[0]);
355 		return 1;
356 	}
357 
358 	/* Decide how to determine the output filenames. */
359 	if (argc > optind) {
360 		s = argv[optind++];		/* just use this */
361 	} else {
362 		s = infile;			/* based on pem file name */
363 		remove_ext = 1;
364 	}
365 
366 	/* Make an extra-large copy to leave room for filename extensions */
367 	len = strlen(s) + 20;
368 	outfile = (char *)malloc(len);
369 	if (!outfile) {
370 		fprintf(stderr, "ERROR: malloc() failed\n");
371 		return 1;
372 	}
373 	strcpy(outfile, s);
374 
375 	if (remove_ext) {
376 		/* Find the last '/' if any, then the last '.' before that. */
377 		s = strrchr(outfile, '/');
378 		if (!s)
379 			s = outfile;
380 		s = strrchr(s, '.');
381 		/* Cut off the extension */
382 		if (s)
383 			*s = '\0';
384 	}
385 	/* Remember that spot for later */
386 	outext = outfile + strlen(outfile);
387 
388 	/* Okay, do it */
389 	if (vboot_version == VBOOT_VERSION_1_0)
390 		r = vb1_make_keypair();
391 	else
392 		r = vb2_make_keypair();
393 
394 	free(outfile);
395 	return r;
396 }
397 
398 DECLARE_FUTIL_COMMAND(create, do_create,
399 		      VBOOT_VERSION_ALL,
400 		      "Create a keypair from an RSA .pem file",
401 		      print_help);
402