You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
401 lines
9.0 KiB
401 lines
9.0 KiB
/* Copyright 2015 The Chromium OS Authors. All rights reserved. |
|
* Use of this source code is governed by a BSD-style license that can be |
|
* found in the LICENSE file. |
|
*/ |
|
|
|
#include <getopt.h> |
|
#include <stdio.h> |
|
#include <unistd.h> |
|
|
|
#include <openssl/pem.h> |
|
|
|
#include "2sysincludes.h" |
|
#include "2common.h" |
|
#include "2guid.h" |
|
#include "2rsa.h" |
|
#include "util_misc.h" |
|
#include "vb2_common.h" |
|
#include "vb2_struct.h" |
|
|
|
#include "host_key.h" |
|
#include "host_key2.h" |
|
#include "host_misc2.h" |
|
|
|
#include "futility.h" |
|
|
|
/* Command line options */ |
|
enum { |
|
OPT_OUTFILE = 1000, |
|
OPT_VERSION, |
|
OPT_DESC, |
|
OPT_GUID, |
|
OPT_HASH_ALG, |
|
}; |
|
|
|
#define DEFAULT_VERSION 1 |
|
#define DEFAULT_HASH VB2_HASH_SHA256; |
|
|
|
static char *infile, *outfile, *outext; |
|
static uint32_t opt_version = DEFAULT_VERSION; |
|
enum vb2_hash_algorithm opt_hash_alg = DEFAULT_HASH; |
|
static char *opt_desc; |
|
static struct vb2_guid opt_guid; |
|
|
|
static const struct option long_opts[] = { |
|
{"version", 1, 0, OPT_VERSION}, |
|
{"desc", 1, 0, OPT_DESC}, |
|
{"guid", 1, 0, OPT_GUID}, |
|
{"hash_alg", 1, 0, OPT_HASH_ALG}, |
|
{NULL, 0, 0, 0} |
|
}; |
|
|
|
static void print_help(const char *progname) |
|
{ |
|
struct vb2_text_vs_enum *entry; |
|
|
|
printf("\n" |
|
"Usage: " MYNAME " %s [options] <INFILE> [<BASENAME>]\n", progname); |
|
printf("\n" |
|
"Create a keypair from an RSA key (.pem file).\n" |
|
"\n" |
|
"Options:\n" |
|
"\n" |
|
" --version <number> Key version (default %d)\n" |
|
" --hash_alg <number> Hashing algorithm to use:\n", |
|
DEFAULT_VERSION); |
|
for (entry = vb2_text_vs_hash; entry->name; entry++) |
|
printf(" %d / %s%s\n", |
|
entry->num, entry->name, |
|
entry->num == VB2_HASH_SHA256 ? " (default)" : ""); |
|
printf( |
|
" --guid <guid> Identifier for this keypair (vb21 only)\n" |
|
" --desc <text> Human-readable description (vb21 only)\n" |
|
"\n"); |
|
|
|
} |
|
|
|
static int vb1_make_keypair() |
|
{ |
|
VbPrivateKey *privkey = 0; |
|
VbPublicKey *pubkey = 0; |
|
RSA *rsa_key = 0; |
|
uint8_t *keyb_data = 0; |
|
uint32_t keyb_size; |
|
enum vb2_signature_algorithm sig_alg; |
|
uint64_t vb1_algorithm; |
|
FILE *fp; |
|
int ret = 1; |
|
|
|
fp = fopen(infile, "rb"); |
|
if (!fp) { |
|
fprintf(stderr, "Unable to open %s\n", infile); |
|
goto done; |
|
} |
|
|
|
rsa_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL); |
|
fclose(fp); |
|
|
|
if (!rsa_key) { |
|
fprintf(stderr, "Unable to read RSA key from %s\n", infile); |
|
goto done; |
|
} |
|
|
|
sig_alg = vb2_rsa_sig_alg(rsa_key); |
|
if (sig_alg == VB2_SIG_INVALID) { |
|
fprintf(stderr, "Unsupported sig algorithm in RSA key\n"); |
|
goto done; |
|
} |
|
|
|
/* combine the sig_alg with the hash_alg to get the vb1 algorithm */ |
|
vb1_algorithm = (sig_alg - VB2_SIG_RSA1024) * 3 |
|
+ opt_hash_alg - VB2_HASH_SHA1; |
|
|
|
/* Create the private key */ |
|
privkey = (VbPrivateKey *)malloc(sizeof(VbPrivateKey)); |
|
if (!privkey) |
|
goto done; |
|
|
|
privkey->rsa_private_key = rsa_key; |
|
privkey->algorithm = vb1_algorithm; |
|
|
|
/* Write it out */ |
|
strcpy(outext, ".vbprivk"); |
|
if (0 != PrivateKeyWrite(outfile, privkey)) { |
|
fprintf(stderr, "unable to write private key\n"); |
|
goto done; |
|
} |
|
fprintf(stderr, "wrote %s\n", outfile); |
|
|
|
/* Create the public key */ |
|
ret = vb_keyb_from_rsa(rsa_key, &keyb_data, &keyb_size); |
|
if (ret) { |
|
fprintf(stderr, "couldn't extract the public key\n"); |
|
goto done; |
|
} |
|
|
|
pubkey = PublicKeyAlloc(keyb_size, vb1_algorithm, opt_version); |
|
if (!pubkey) |
|
goto done; |
|
memcpy(GetPublicKeyData(pubkey), keyb_data, keyb_size); |
|
|
|
/* Write it out */ |
|
strcpy(outext, ".vbpubk"); |
|
if (0 != PublicKeyWrite(outfile, pubkey)) { |
|
fprintf(stderr, "unable to write public key\n"); |
|
goto done; |
|
} |
|
fprintf(stderr, "wrote %s\n", outfile); |
|
|
|
ret = 0; |
|
|
|
done: |
|
free(privkey); |
|
free(pubkey); |
|
free(keyb_data); |
|
RSA_free(rsa_key); |
|
return ret; |
|
} |
|
|
|
static int vb2_make_keypair() |
|
{ |
|
struct vb2_private_key *privkey = 0; |
|
struct vb2_public_key *pubkey = 0; |
|
RSA *rsa_key = 0; |
|
uint8_t *keyb_data = 0; |
|
uint32_t keyb_size; |
|
enum vb2_signature_algorithm sig_alg; |
|
uint8_t *pubkey_buf = 0; |
|
|
|
FILE *fp; |
|
int ret = 1; |
|
|
|
fp = fopen(infile, "rb"); |
|
if (!fp) { |
|
fprintf(stderr, "Unable to open %s\n", infile); |
|
goto done; |
|
} |
|
|
|
rsa_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL); |
|
fclose(fp); |
|
|
|
if (!rsa_key) { |
|
fprintf(stderr, "Unable to read RSA key from %s\n", infile); |
|
goto done; |
|
} |
|
|
|
sig_alg = vb2_rsa_sig_alg(rsa_key); |
|
if (sig_alg == VB2_SIG_INVALID) { |
|
fprintf(stderr, "Unsupported sig algorithm in RSA key\n"); |
|
goto done; |
|
} |
|
|
|
/* Create the private key */ |
|
privkey = calloc(1, sizeof(*privkey)); |
|
if (!privkey) { |
|
fprintf(stderr, "Unable to allocate the private key\n"); |
|
goto done; |
|
} |
|
privkey->rsa_private_key = rsa_key; |
|
privkey->sig_alg = sig_alg; |
|
privkey->hash_alg = opt_hash_alg; |
|
privkey->guid = opt_guid; |
|
if (opt_desc && vb2_private_key_set_desc(privkey, opt_desc)) { |
|
fprintf(stderr, "Unable to set the private key description\n"); |
|
goto done; |
|
} |
|
|
|
/* Write it out */ |
|
strcpy(outext, ".vbprik2"); |
|
if (vb2_private_key_write(privkey, outfile)) { |
|
fprintf(stderr, "unable to write private key\n"); |
|
goto done; |
|
} |
|
fprintf(stderr, "wrote %s\n", outfile); |
|
|
|
/* Create the public key */ |
|
if (vb2_public_key_alloc(&pubkey, sig_alg)) { |
|
fprintf(stderr, "Unable to allocate the public key\n"); |
|
goto done; |
|
} |
|
|
|
/* Extract the keyb blob */ |
|
if (vb_keyb_from_rsa(rsa_key, &keyb_data, &keyb_size)) { |
|
fprintf(stderr, "Couldn't extract the public key\n"); |
|
goto done; |
|
} |
|
|
|
/* |
|
* Copy the keyb blob to the public key's buffer, because that's where |
|
* vb2_unpack_key_data() and vb2_public_key_pack() expect to find it. |
|
*/ |
|
pubkey_buf = vb2_public_key_packed_data(pubkey); |
|
memcpy(pubkey_buf, keyb_data, keyb_size); |
|
|
|
/* Fill in the internal struct pointers */ |
|
if (vb2_unpack_key_data(pubkey, pubkey_buf, keyb_size)) { |
|
fprintf(stderr, "Unable to unpack the public key blob\n"); |
|
goto done; |
|
} |
|
|
|
pubkey->hash_alg = opt_hash_alg; |
|
pubkey->version = opt_version; |
|
memcpy((struct vb2_guid *)pubkey->guid, &opt_guid, sizeof(opt_guid)); |
|
if (opt_desc && vb2_public_key_set_desc(pubkey, opt_desc)) { |
|
fprintf(stderr, "Unable to set pubkey description\n"); |
|
goto done; |
|
} |
|
|
|
/* Write it out */ |
|
strcpy(outext, ".vbpubk2"); |
|
if (vb2_public_key_write(pubkey, outfile)) { |
|
fprintf(stderr, "unable to write public key\n"); |
|
goto done; |
|
} |
|
fprintf(stderr, "wrote %s\n", outfile); |
|
|
|
ret = 0; |
|
|
|
done: |
|
RSA_free(rsa_key); |
|
if (privkey) /* prevent double-free */ |
|
privkey->rsa_private_key = 0; |
|
vb2_private_key_free(privkey); |
|
vb2_public_key_free(pubkey); |
|
free(keyb_data); |
|
return ret; |
|
} |
|
|
|
static int do_create(int argc, char *argv[]) |
|
{ |
|
int errorcnt = 0; |
|
char *e, *s; |
|
int i, r, len, remove_ext = 0; |
|
const struct vb2_text_vs_enum *entry; |
|
|
|
while ((i = getopt_long(argc, argv, "", long_opts, NULL)) != -1) { |
|
switch (i) { |
|
|
|
case OPT_VERSION: |
|
opt_version = strtoul(optarg, &e, 0); |
|
if (!*optarg || (e && *e)) { |
|
fprintf(stderr, |
|
"invalid version \"%s\"\n", optarg); |
|
errorcnt = 1; |
|
} |
|
break; |
|
|
|
case OPT_DESC: |
|
opt_desc = optarg; |
|
break; |
|
|
|
case OPT_GUID: |
|
if (VB2_SUCCESS != vb2_str_to_guid(optarg, |
|
&opt_guid)) { |
|
fprintf(stderr, "invalid guid \"%s\"\n", |
|
optarg); |
|
errorcnt = 1; |
|
} |
|
break; |
|
|
|
case OPT_HASH_ALG: |
|
/* try string first */ |
|
entry = vb2_lookup_by_name(vb2_text_vs_hash, optarg); |
|
if (entry) { |
|
opt_hash_alg = entry->num; |
|
break; |
|
} |
|
/* fine, try number */ |
|
opt_hash_alg = strtoul(optarg, &e, 0); |
|
if (!*optarg || (e && *e)) { |
|
fprintf(stderr, |
|
"invalid hash_alg \"%s\"\n", optarg); |
|
errorcnt++; |
|
break; |
|
} |
|
if (!vb2_lookup_by_num(vb2_text_vs_hash, |
|
opt_hash_alg)) { |
|
fprintf(stderr, |
|
"Hash algorithm %d is unsupported\n", |
|
opt_hash_alg); |
|
errorcnt++; |
|
} |
|
break; |
|
|
|
case '?': |
|
if (optopt) |
|
fprintf(stderr, "Unrecognized option: -%c\n", |
|
optopt); |
|
else |
|
fprintf(stderr, "Unrecognized option\n"); |
|
errorcnt++; |
|
break; |
|
case ':': |
|
fprintf(stderr, "Missing argument to -%c\n", optopt); |
|
errorcnt++; |
|
break; |
|
case 0: /* handled option */ |
|
break; |
|
default: |
|
DIE; |
|
} |
|
} |
|
|
|
/* If we don't have an input file already, we need one */ |
|
if (!infile) { |
|
if (argc - optind <= 0) { |
|
fprintf(stderr, "ERROR: missing input filename\n"); |
|
errorcnt++; |
|
} else { |
|
infile = argv[optind++]; |
|
} |
|
} |
|
|
|
if (errorcnt) { |
|
print_help(argv[0]); |
|
return 1; |
|
} |
|
|
|
/* Decide how to determine the output filenames. */ |
|
if (argc > optind) { |
|
s = argv[optind++]; /* just use this */ |
|
} else { |
|
s = infile; /* based on pem file name */ |
|
remove_ext = 1; |
|
} |
|
|
|
/* Make an extra-large copy to leave room for filename extensions */ |
|
len = strlen(s) + 20; |
|
outfile = (char *)malloc(len); |
|
if (!outfile) { |
|
fprintf(stderr, "ERROR: malloc() failed\n"); |
|
return 1; |
|
} |
|
strcpy(outfile, s); |
|
|
|
if (remove_ext) { |
|
/* Find the last '/' if any, then the last '.' before that. */ |
|
s = strrchr(outfile, '/'); |
|
if (!s) |
|
s = outfile; |
|
s = strrchr(s, '.'); |
|
/* Cut off the extension */ |
|
if (s) |
|
*s = '\0'; |
|
} |
|
/* Remember that spot for later */ |
|
outext = outfile + strlen(outfile); |
|
|
|
/* Okay, do it */ |
|
if (vboot_version == VBOOT_VERSION_1_0) |
|
r = vb1_make_keypair(); |
|
else |
|
r = vb2_make_keypair(); |
|
|
|
free(outfile); |
|
return r; |
|
} |
|
|
|
DECLARE_FUTIL_COMMAND(create, do_create, |
|
VBOOT_VERSION_ALL, |
|
"Create a keypair from an RSA .pem file", |
|
print_help);
|
|
|