Home | History | Annotate | Download | only in futility
      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 
     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 
     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 
    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 
    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