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 */
27enum {
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
38static char *infile, *outfile, *outext;
39static uint32_t opt_version = DEFAULT_VERSION;
40enum vb2_hash_algorithm opt_hash_alg = DEFAULT_HASH;
41static char *opt_desc;
42static struct vb2_guid opt_guid;
43
44static 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
52static 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
77static 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
151done:
152	free(privkey);
153	free(pubkey);
154	free(keyb_data);
155	RSA_free(rsa_key);
156	return ret;
157}
158
159static 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
258done:
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
268static 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
398DECLARE_FUTIL_COMMAND(create, do_create,
399		      VBOOT_VERSION_ALL,
400		      "Create a keypair from an RSA .pem file",
401		      print_help);
402