summaryrefslogtreecommitdiff
path: root/futility/cmd_create.c
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2015-02-03 17:07:15 -0800
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-03-10 20:44:43 +0000
commit4e4c19602edf3834b50d66d3ba067e895aca6fa0 (patch)
tree11b9408e5e6a9c3e3fde95c21656e67562bb3faf /futility/cmd_create.c
parent26af0da4f7e0fd5cc9410011ca05ff6539bbf42d (diff)
downloadvboot-4e4c19602edf3834b50d66d3ba067e895aca6fa0.tar.gz
futility: Add create command to make keypairs from RSA files
This command reads a single .pem file and emits the public and private keys generated from it. It can produce both the old-style vboot 1.0 keys (.vbpubk and .vbprivk), or the new vboot 2.1 format keys (.vbpubk2 and .vbprik2). The default is the new format, but you can give futility the --vb1 arg to force the old format. A test is included. BUG=chromium:231547 BRANCH=ToT TEST=make runtests Change-Id: I4713dc5bf34151052870f88ba52ddccf9d4dab50 Signed-off-by: Bill Richardson <wfrichar@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/246766 Reviewed-by: Randall Spangler <rspangler@chromium.org>
Diffstat (limited to 'futility/cmd_create.c')
-rw-r--r--futility/cmd_create.c402
1 files changed, 402 insertions, 0 deletions
diff --git a/futility/cmd_create.c b/futility/cmd_create.c
new file mode 100644
index 00000000..f4eb3f2a
--- /dev/null
+++ b/futility/cmd_create.c
@@ -0,0 +1,402 @@
+/* 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 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);