mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-25 01:07:22 +00:00
The USB Type-C chargers released with Samus ("Pixel (2015)") have
upgradable firmware. Those firmware images are currently signed
by some custom scripts. This adds support for those images to
futility, so that those custom scripts can be replaced.
Note that due to space considerations, the usbpd firmware images
don't have room for handy things like an FMAP or headers for the
signatures. Accordingly, all the normally variable factors (image
size, signature algorithms, etc.) are hard coded and the image
itself just looks like a bunch of random numbers. Because of
this:
1. There's no way to recognize this type of file, and nothing to
display about it.
2. You have to give the "--type usbpd1" arg to the sign command.
3. I'm designating this file type "v1" because I hope that the
firmware for any future adapters will be more readily
identifiable.
BUG=chromium:231574
BRANCH=none
TEST=make runtests
This adds a new test that signs usbpd1 firmware images using
futility, and compares the result to the same files signed by the
custom scripts, ensuring that they are bitwise identical.
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Change-Id: Idbe061db5b3c8e474ada47b40bcbe04bbecdba3a
Reviewed-on: https://chromium-review.googlesource.com/262899
Reviewed-by: Randall Spangler <rspangler@chromium.org>
401 lines
8.9 KiB
C
401 lines
8.9 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 "2id.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"
|
|
#include "futility_options.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[])
|
|
{
|
|
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()
|
|
{
|
|
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_id) {
|
|
uint8_t *digest = DigestBuf(keyb_data, keyb_size,
|
|
SHA1_DIGEST_ALGORITHM);
|
|
memcpy(&opt_id, digest, sizeof(opt_id));
|
|
free(digest);
|
|
}
|
|
|
|
privkey->id = opt_id;
|
|
memcpy((struct vb2_id *)pubkey->id, &opt_id, sizeof(opt_id));
|
|
|
|
/* 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;
|
|
|
|
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:
|
|
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(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");
|