/* 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);