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