mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-11-26 19:25:02 +00:00
We want a quick and human-friendly way to match keys with signatures, so we decided to give each key a unique GUID and carry that ID around when signing things. But then we realized that we could autogenerate a unique identifier from the .pem file itself, which is even better because then we can match our binary keypair structs with the openssl file used to generate them. This change replaces the GUID id with a sha1sum calculated from the public key's "keyb" blob. BUG=none BRANCH=none TEST=make runtests Also: futility show tests/testkeys/key_rsa4096.pem futility create tests/testkeys/key_rsa4096.pem foo futility show foo.vbp* Note that the GUID is the same for all files. Change-Id: Ie44e46c83433718b1ff0163c1e7c51ec331b99f9 Signed-off-by: Bill Richardson <wfrichar@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/256181 Reviewed-by: Randall Spangler <rspangler@chromium.org>
414 lines
9.3 KiB
C
414 lines
9.3 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 <getopt.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
|
|
#define OPENSSL_NO_SHA
|
|
#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 int force_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;
|
|
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_guid) {
|
|
uint8_t *digest = DigestBuf(keyb_data, keyb_size,
|
|
SHA1_DIGEST_ALGORITHM);
|
|
memcpy(&opt_guid, digest, sizeof(opt_guid));
|
|
free(digest);
|
|
}
|
|
|
|
privkey->guid = opt_guid;
|
|
memcpy((struct vb2_guid *)pubkey->guid, &opt_guid, sizeof(opt_guid));
|
|
|
|
/* Write them 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);
|
|
|
|
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;
|
|
}
|
|
force_guid = 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);
|