417 lines
9.6 KiB
C
417 lines
9.6 KiB
C
/* 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 <openssl/pem.h>
|
|
|
|
#include <getopt.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
|
|
#include "2common.h"
|
|
#include "2id.h"
|
|
#include "2rsa.h"
|
|
#include "2sha.h"
|
|
#include "2sysincludes.h"
|
|
#include "futility.h"
|
|
#include "futility_options.h"
|
|
#include "host_key2.h"
|
|
#include "host_key.h"
|
|
#include "host_misc2.h"
|
|
#include "openssl_compat.h"
|
|
#include "util_misc.h"
|
|
#include "vb21_common.h"
|
|
#include "vb2_common.h"
|
|
|
|
/* Command line options */
|
|
enum {
|
|
OPT_OUTFILE = 1000,
|
|
OPT_VERSION,
|
|
OPT_DESC,
|
|
OPT_ID,
|
|
OPT_HASH_ALG,
|
|
OPT_HELP,
|
|
};
|
|
|
|
#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_id opt_id;
|
|
static int force_id;
|
|
|
|
static const struct option long_opts[] = {
|
|
{"version", 1, 0, OPT_VERSION},
|
|
{"desc", 1, 0, OPT_DESC},
|
|
{"id", 1, 0, OPT_ID},
|
|
{"hash_alg", 1, 0, OPT_HASH_ALG},
|
|
{"help", 0, 0, OPT_HELP},
|
|
{NULL, 0, 0, 0}
|
|
};
|
|
|
|
static void print_help(int argc, char *argv[])
|
|
{
|
|
const struct vb2_text_vs_enum *entry;
|
|
|
|
printf("\n"
|
|
"Usage: " MYNAME " %s [options] <INFILE> [<BASENAME>]\n", argv[0]);
|
|
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(
|
|
" --id <id> Identifier for this keypair (vb21 only)\n"
|
|
" --desc <text> Human-readable description (vb21 only)\n"
|
|
"\n");
|
|
|
|
}
|
|
|
|
static int vb1_make_keypair(void)
|
|
{
|
|
struct vb2_private_key *privkey = NULL;
|
|
struct vb2_packed_key *pubkey = NULL;
|
|
struct rsa_st *rsa_key = NULL;
|
|
uint8_t *keyb_data = 0;
|
|
uint32_t keyb_size;
|
|
int ret = 1;
|
|
|
|
FILE *fp = fopen(infile, "rb");
|
|
if (!fp) {
|
|
fprintf(stderr, "Unable to open %s\n", infile);
|
|
goto done;
|
|
}
|
|
|
|
/* TODO: this is very similar to vb2_read_private_key_pem() */
|
|
|
|
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;
|
|
}
|
|
|
|
enum vb2_signature_algorithm 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 */
|
|
uint64_t vb1_algorithm =
|
|
vb2_get_crypto_algorithm(opt_hash_alg, sig_alg);
|
|
|
|
/* Create the private key */
|
|
privkey = (struct vb2_private_key *)calloc(sizeof(*privkey), 1);
|
|
if (!privkey)
|
|
goto done;
|
|
|
|
privkey->rsa_private_key = rsa_key;
|
|
privkey->sig_alg = sig_alg;
|
|
privkey->hash_alg = opt_hash_alg;
|
|
|
|
/* Write it out */
|
|
strcpy(outext, ".vbprivk");
|
|
if (0 != vb2_write_private_key(outfile, privkey)) {
|
|
fprintf(stderr, "unable to write private key\n");
|
|
goto done;
|
|
}
|
|
printf("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 = vb2_alloc_packed_key(keyb_size, vb1_algorithm, opt_version);
|
|
if (!pubkey)
|
|
goto done;
|
|
memcpy((uint8_t *)vb2_packed_key_data(pubkey), keyb_data, keyb_size);
|
|
|
|
/* Write it out */
|
|
strcpy(outext, ".vbpubk");
|
|
if (VB2_SUCCESS != vb2_write_packed_key(outfile, pubkey)) {
|
|
fprintf(stderr, "unable to write public key\n");
|
|
goto done;
|
|
}
|
|
printf("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(void)
|
|
{
|
|
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;
|
|
int has_priv = 0;
|
|
const BIGNUM *rsa_d;
|
|
|
|
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);
|
|
|
|
if (!rsa_key) {
|
|
/* Check if the PEM contains only a public key */
|
|
if (0 != fseek(fp, 0, SEEK_SET)) {
|
|
fprintf(stderr, "Error seeking in %s\n", infile);
|
|
goto done;
|
|
}
|
|
rsa_key = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
|
|
}
|
|
fclose(fp);
|
|
if (!rsa_key) {
|
|
fprintf(stderr, "Unable to read RSA key from %s\n", infile);
|
|
goto done;
|
|
}
|
|
/* Public keys doesn't have the private exponent */
|
|
RSA_get0_key(rsa_key, NULL, NULL, &rsa_d);
|
|
has_priv = !!rsa_d;
|
|
if (!has_priv)
|
|
fprintf(stderr, "%s has a public key only.\n", infile);
|
|
|
|
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;
|
|
}
|
|
|
|
if (has_priv) {
|
|
/* 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;
|
|
if (opt_desc && vb2_private_key_set_desc(privkey, opt_desc)) {
|
|
fprintf(stderr,
|
|
"Unable to set the private key description\n");
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
if (opt_desc && vb2_public_key_set_desc(pubkey, opt_desc)) {
|
|
fprintf(stderr, "Unable to set pubkey description\n");
|
|
goto done;
|
|
}
|
|
|
|
/* Update the IDs */
|
|
if (!force_id) {
|
|
vb2_digest_buffer(keyb_data, keyb_size, VB2_HASH_SHA1,
|
|
opt_id.raw, sizeof(opt_id.raw));
|
|
}
|
|
|
|
memcpy((struct vb2_id *)pubkey->id, &opt_id, sizeof(opt_id));
|
|
|
|
/* Write them out */
|
|
if (has_priv) {
|
|
privkey->id = opt_id;
|
|
strcpy(outext, ".vbprik2");
|
|
if (vb21_private_key_write(privkey, outfile)) {
|
|
fprintf(stderr, "unable to write private key\n");
|
|
goto done;
|
|
}
|
|
printf("wrote %s\n", outfile);
|
|
}
|
|
|
|
strcpy(outext, ".vbpubk2");
|
|
if (vb21_public_key_write(pubkey, outfile)) {
|
|
fprintf(stderr, "unable to write public key\n");
|
|
goto done;
|
|
}
|
|
printf("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;
|
|
|
|
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_ID:
|
|
if (VB2_SUCCESS != vb2_str_to_id(optarg, &opt_id)) {
|
|
fprintf(stderr, "invalid id \"%s\"\n",
|
|
optarg);
|
|
errorcnt = 1;
|
|
}
|
|
force_id = 1;
|
|
break;
|
|
|
|
case OPT_HASH_ALG:
|
|
if (!vb2_lookup_hash_alg(optarg, &opt_hash_alg)) {
|
|
fprintf(stderr,
|
|
"invalid hash_alg \"%s\"\n", optarg);
|
|
errorcnt++;
|
|
}
|
|
break;
|
|
|
|
case OPT_HELP:
|
|
print_help(argc, argv);
|
|
return !!errorcnt;
|
|
|
|
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:
|
|
FATAL("Unrecognized getopt output: %d\n", i);
|
|
}
|
|
}
|
|
|
|
/* 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(argc, argv);
|
|
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");
|