futility: Allow signing raw firmware blob and keyblocks

BUG=none
BRANCH=ToT
TEST=make runtests

Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Change-Id: Ib1cf55301fd4c54e3280ef01b7d67a780e7e56fe
Reviewed-on: https://chromium-review.googlesource.com/219731
Reviewed-by: Randall Spangler <rspangler@chromium.org>
This commit is contained in:
Bill Richardson
2014-09-23 22:17:02 -07:00
committed by chrome-internal-fetch
parent 5f2696d2ff
commit c540f59be0
4 changed files with 328 additions and 4 deletions

View File

@@ -51,11 +51,16 @@ static struct local_data_s {
uint8_t *config_data; uint8_t *config_data;
uint64_t config_size; uint64_t config_size;
enum arch_t arch; enum arch_t arch;
int fv_specified;
uint32_t kloadaddr; uint32_t kloadaddr;
uint32_t padding; uint32_t padding;
int vblockonly; int vblockonly;
char *outfile; char *outfile;
int create_new_outfile; int create_new_outfile;
char *pem_signpriv;
int pem_algo_specified;
uint32_t pem_algo;
char *pem_external;
} option = { } option = {
.version = 1, .version = 1,
.arch = ARCH_UNSPECIFIED, .arch = ARCH_UNSPECIFIED,
@@ -77,9 +82,40 @@ static int no_opt_if(int expr, const char *optname)
/* This wraps/signs a public key, producing a keyblock. */ /* This wraps/signs a public key, producing a keyblock. */
int futil_cb_sign_pubkey(struct futil_traverse_state_s *state) int futil_cb_sign_pubkey(struct futil_traverse_state_s *state)
{ {
fprintf(stderr, "Don't know how to sign %s yet\n", state->name); VbPublicKey *data_key = (VbPublicKey *)state->my_area->buf;
VbKeyBlockHeader *vblock;
if (option.pem_signpriv) {
if (option.pem_external) {
/* External signing uses the PEM file directly. */
vblock = KeyBlockCreate_external(
data_key,
option.pem_signpriv,
option.pem_algo, option.flags,
option.pem_external);
} else {
option.signprivate = PrivateKeyReadPem(
option.pem_signpriv, option.pem_algo);
if (!option.signprivate) {
fprintf(stderr,
"Unable to read PEM signing key: %s\n",
strerror(errno));
return 1; return 1;
} }
vblock = KeyBlockCreate(data_key, option.signprivate,
option.flags);
}
} else {
/* Not PEM. Should already have a signing key. */
vblock = KeyBlockCreate(data_key, option.signprivate,
option.flags);
}
/* Write it out */
return WriteSomeParts(option.outfile,
vblock, vblock->key_block_size,
NULL, 0);
}
/* /*
* This handles FW_MAIN_A and FW_MAIN_B while processing a BIOS image. * This handles FW_MAIN_A and FW_MAIN_B while processing a BIOS image.
@@ -287,10 +323,38 @@ int futil_cb_resign_kernel_part(struct futil_traverse_state_s *state)
int futil_cb_sign_raw_firmware(struct futil_traverse_state_s *state) int futil_cb_sign_raw_firmware(struct futil_traverse_state_s *state)
{ {
fprintf(stderr, "Don't know how to sign %s yet\n", state->name); VbSignature *body_sig;
VbFirmwarePreambleHeader *preamble;
int rv;
body_sig = CalculateSignature(state->my_area->buf, state->my_area->len,
option.signprivate);
if (!body_sig) {
fprintf(stderr, "Error calculating body signature\n");
return 1; return 1;
} }
preamble = CreateFirmwarePreamble(option.version,
option.kernel_subkey,
body_sig,
option.signprivate,
option.flags);
if (!preamble) {
fprintf(stderr, "Error creating firmware preamble.\n");
free(body_sig);
return 1;
}
rv = WriteSomeParts(option.outfile,
option.keyblock, option.keyblock->key_block_size,
preamble, preamble->preamble_size);
free(preamble);
free(body_sig);
return rv;
}
int futil_cb_sign_begin(struct futil_traverse_state_s *state) int futil_cb_sign_begin(struct futil_traverse_state_s *state)
{ {
@@ -446,10 +510,53 @@ static const char usage[] = "\n"
"\n" "\n"
"Where INFILE is a\n" "Where INFILE is a\n"
"\n" "\n"
" public key (.vbpubk); OUTFILE is a keyblock\n"
" raw firmware blob (FW_MAIN_A/B); OUTFILE is a VBLOCK_A/B\n"
" complete firmware image (bios.bin)\n" " complete firmware image (bios.bin)\n"
" raw linux kernel; OUTFILE is a kernel partition image\n" " raw linux kernel; OUTFILE is a kernel partition image\n"
" kernel partition image (/dev/sda2, /dev/mmcblk0p2)\n"; " kernel partition image (/dev/sda2, /dev/mmcblk0p2)\n";
static const char usage_pubkey[] = "\n"
"-----------------------------------------------------------------\n"
"To sign a public key / create a new keyblock:\n"
"\n"
"Required PARAMS:\n"
" [--datapubkey] INFILE The public key to wrap\n"
" [--outfile] OUTFILE The resulting keyblock\n"
"\n"
"Optional PARAMS:\n"
" A private signing key, specified as either\n"
" -s|--signprivate FILE.vbprivk Signing key in .vbprivk format\n"
" Or\n"
" --pem_signpriv FILE.pem Signing key in PEM format...\n"
" --pem_algo NUM AND the algorithm to use (0 - %d)\n"
"\n"
" If a signing key is not given, the keyblock will not be signed (duh)."
"\n\n"
"And these, too:\n\n"
" -f|--flags NUM Flags specifying use conditions\n"
" --pem_external PROGRAM"
" External program to compute the signature\n"
" (requires a PEM signing key)\n";
static const char usage_fw_main[] = "\n"
"-----------------------------------------------------------------\n"
"To sign a raw firmware blob (FW_MAIN_A/B):\n"
"\n"
"Required PARAMS:\n"
" -s|--signprivate FILE.vbprivk The private firmware data key\n"
" -b|--keyblock FILE.keyblock The keyblock containing the\n"
" public firmware data key\n"
" -k|--kernelkey FILE.vbpubk The public kernel subkey\n"
" -v|--version NUM The firmware version number\n"
" [--fv] INFILE"
" The raw firmware blob (FW_MAIN_A/B)\n"
" [--outfile] OUTFILE Output VBLOCK_A/B\n"
"\n"
"Optional PARAMS:\n"
" -f|--flags NUM The preamble flags value"
" (default is 0)\n";
static const char usage_bios[] = "\n" static const char usage_bios[] = "\n"
"-----------------------------------------------------------------\n" "-----------------------------------------------------------------\n"
"To sign a complete firmware image (bios.bin):\n" "To sign a complete firmware image (bios.bin):\n"
@@ -528,6 +635,8 @@ static const char usage_old_kpart[] = "\n"
static void print_help(const char *prog) static void print_help(const char *prog)
{ {
printf(usage, prog); printf(usage, prog);
printf(usage_pubkey, kNumAlgorithms - 1);
puts(usage_fw_main);
printf(usage_bios, option.version); printf(usage_bios, option.version);
printf(usage_new_kpart, option.kloadaddr, option.padding); printf(usage_new_kpart, option.kloadaddr, option.padding);
printf(usage_old_kpart, option.padding); printf(usage_old_kpart, option.padding);
@@ -542,6 +651,9 @@ enum no_short_opts {
OPT_ARCH, OPT_ARCH,
OPT_KLOADADDR, OPT_KLOADADDR,
OPT_PADDING, OPT_PADDING,
OPT_PEM_SIGNPRIV,
OPT_PEM_ALGO,
OPT_PEM_EXTERNAL,
}; };
static const struct option long_opts[] = { static const struct option long_opts[] = {
@@ -565,6 +677,9 @@ static const struct option long_opts[] = {
{"arch", 1, NULL, OPT_ARCH}, {"arch", 1, NULL, OPT_ARCH},
{"kloadaddr", 1, NULL, OPT_KLOADADDR}, {"kloadaddr", 1, NULL, OPT_KLOADADDR},
{"pad", 1, NULL, OPT_PADDING}, {"pad", 1, NULL, OPT_PADDING},
{"pem_signpriv", 1, NULL, OPT_PEM_SIGNPRIV},
{"pem_algo", 1, NULL, OPT_PEM_ALGO},
{"pem_external", 1, NULL, OPT_PEM_EXTERNAL},
{"vblockonly", 0, &option.vblockonly, 1}, {"vblockonly", 0, &option.vblockonly, 1},
{"debug", 0, &debugging_enabled, 1}, {"debug", 0, &debugging_enabled, 1},
{NULL, 0, NULL, 0}, {NULL, 0, NULL, 0},
@@ -648,6 +763,9 @@ static int do_sign(int argc, char *argv[])
case 'l': case 'l':
option.loemid = optarg; option.loemid = optarg;
break; break;
case OPT_FV:
option.fv_specified = 1;
/* fallthrough */
case OPT_INFILE: /* aka "--vmlinuz" */ case OPT_INFILE: /* aka "--vmlinuz" */
inout_file_count++; inout_file_count++;
infile = optarg; infile = optarg;
@@ -711,6 +829,23 @@ static int do_sign(int argc, char *argv[])
errorcnt++; errorcnt++;
} }
break; break;
case OPT_PEM_SIGNPRIV:
option.pem_signpriv = optarg;
break;
case OPT_PEM_ALGO:
option.pem_algo_specified = 1;
option.pem_algo = strtoul(optarg, &e, 0);
if (!*optarg || (e && *e) ||
(option.pem_algo >= kNumAlgorithms)) {
fprintf(stderr,
"Invalid --pem_algo \"%s\"\n", optarg);
errorcnt++;
}
break;
case OPT_PEM_EXTERNAL:
option.pem_external = optarg;
break;
case '?': case '?':
if (optopt) if (optopt)
fprintf(stderr, "Unrecognized option: -%c\n", fprintf(stderr, "Unrecognized option: -%c\n",
@@ -752,6 +887,8 @@ static int do_sign(int argc, char *argv[])
if (option.bootloader_data || option.config_data if (option.bootloader_data || option.config_data
|| option.arch != ARCH_UNSPECIFIED) || option.arch != ARCH_UNSPECIFIED)
type = FILE_TYPE_RAW_KERNEL; type = FILE_TYPE_RAW_KERNEL;
else if (option.kernel_subkey || option.fv_specified)
type = FILE_TYPE_RAW_FIRMWARE;
} }
/* Check the arguments for the type of thing we want to sign */ /* Check the arguments for the type of thing we want to sign */
@@ -761,6 +898,28 @@ static int do_sign(int argc, char *argv[])
"Unable to determine the type of the input file\n"); "Unable to determine the type of the input file\n");
errorcnt++; errorcnt++;
goto done; goto done;
case FILE_TYPE_PUBKEY:
option.create_new_outfile = 1;
if (option.signprivate && option.pem_signpriv) {
fprintf(stderr,
"Only one of --signprivate and --pem_signpriv"
" can be specified\n");
errorcnt++;
}
if ((option.signprivate && option.pem_algo_specified) ||
(option.pem_signpriv && !option.pem_algo_specified)) {
fprintf(stderr, "--pem_algo must be used with"
" --pem_signpriv\n");
errorcnt++;
}
if (option.pem_external && !option.pem_signpriv) {
fprintf(stderr, "--pem_external must be used with"
" --pem_signpriv\n");
errorcnt++;
}
/* We'll wait to read the PEM file, since the external signer
* may want to read it instead. */
break;
case FILE_TYPE_KEYBLOCK: case FILE_TYPE_KEYBLOCK:
fprintf(stderr, "Resigning a keyblock is kind of pointless.\n"); fprintf(stderr, "Resigning a keyblock is kind of pointless.\n");
fprintf(stderr, "Just create a new one.\n"); fprintf(stderr, "Just create a new one.\n");
@@ -786,6 +945,13 @@ static int do_sign(int argc, char *argv[])
if (option.vblockonly) if (option.vblockonly)
option.create_new_outfile = 1; option.create_new_outfile = 1;
break; break;
case FILE_TYPE_RAW_FIRMWARE:
option.create_new_outfile = 1;
errorcnt += no_opt_if(!option.signprivate, "signprivate");
errorcnt += no_opt_if(!option.keyblock, "keyblock");
errorcnt += no_opt_if(!option.kernel_subkey, "kernelkey");
errorcnt += no_opt_if(!option.version_specified, "version");
break;
case FILE_TYPE_RAW_KERNEL: case FILE_TYPE_RAW_KERNEL:
option.create_new_outfile = 1; option.create_new_outfile = 1;
errorcnt += no_opt_if(!option.signprivate, "signprivate"); errorcnt += no_opt_if(!option.signprivate, "signprivate");

View File

@@ -45,6 +45,8 @@ ${SCRIPTDIR}/test_dump_fmap.sh
${SCRIPTDIR}/test_load_fmap.sh ${SCRIPTDIR}/test_load_fmap.sh
${SCRIPTDIR}/test_gbb_utility.sh ${SCRIPTDIR}/test_gbb_utility.sh
${SCRIPTDIR}/test_show_kernel.sh ${SCRIPTDIR}/test_show_kernel.sh
${SCRIPTDIR}/test_sign_keyblocks.sh
${SCRIPTDIR}/test_sign_fw_main.sh
${SCRIPTDIR}/test_sign_firmware.sh ${SCRIPTDIR}/test_sign_firmware.sh
${SCRIPTDIR}/test_sign_kernel.sh ${SCRIPTDIR}/test_sign_kernel.sh
" "

View File

@@ -0,0 +1,46 @@
#!/bin/bash -eux
# Copyright 2014 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.
me=${0##*/}
TMP="$me.tmp"
# Work in scratch directory
cd "$OUTDIR"
KEYDIR=${SRCDIR}/tests/devkeys
# create a firmware blob
dd bs=1024 count=16 if=/dev/urandom of=${TMP}.fw_main
# try the old way
${FUTILITY} vbutil_firmware --vblock ${TMP}.vblock.old \
--keyblock ${KEYDIR}/firmware.keyblock \
--signprivate ${KEYDIR}/firmware_data_key.vbprivk \
--version 12 \
--fv ${TMP}.fw_main \
--kernelkey ${KEYDIR}/kernel_subkey.vbpubk \
--flags 42
# verify
${FUTILITY} vbutil_firmware --verify ${TMP}.vblock.old \
--signpubkey ${KEYDIR}/root_key.vbpubk \
--fv ${TMP}.fw_main
# and the new way
${FUTILITY} sign --debug \
--signprivate ${KEYDIR}/firmware_data_key.vbprivk \
--keyblock ${KEYDIR}/firmware.keyblock \
--kernelkey ${KEYDIR}/kernel_subkey.vbpubk \
--version 12 \
--fv ${TMP}.fw_main \
--flags 42 \
${TMP}.vblock.new
# They should match
cmp ${TMP}.vblock.old ${TMP}.vblock.new
# cleanup
rm -rf ${TMP}*
exit 0

View File

@@ -0,0 +1,110 @@
#!/bin/bash -eux
# Copyright 2014 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.
me=${0##*/}
TMP="$me.tmp"
# Work in scratch directory
cd "$OUTDIR"
# some stuff we'll need
DEVKEYS=${SRCDIR}/tests/devkeys
TESTKEYS=${SRCDIR}/tests/testkeys
SIGNER=${SRCDIR}/tests/external_rsa_signer.sh
# Create a copy of an existing keyblock, using the old way
${FUTILITY} vbutil_keyblock --pack ${TMP}.keyblock0 \
--datapubkey ${DEVKEYS}/firmware_data_key.vbpubk \
--flags 7 \
--signprivate ${DEVKEYS}/root_key.vbprivk
# Check it.
${FUTILITY} vbutil_keyblock --unpack ${TMP}.keyblock0 \
--signpubkey ${DEVKEYS}/root_key.vbpubk
# It should be the same as the dev-key firmware keyblock
cmp ${DEVKEYS}/firmware.keyblock ${TMP}.keyblock0
# Now create it the new way
${FUTILITY} sign --debug \
--datapubkey ${DEVKEYS}/firmware_data_key.vbpubk \
--flags 7 \
--signprivate ${DEVKEYS}/root_key.vbprivk \
--outfile ${TMP}.keyblock1
# It should be the same too.
cmp ${DEVKEYS}/firmware.keyblock ${TMP}.keyblock1
# Create a keyblock without signing it.
# old way
${FUTILITY} vbutil_keyblock --pack ${TMP}.keyblock0 \
--datapubkey ${DEVKEYS}/firmware_data_key.vbpubk \
--flags 14
# new way
${FUTILITY} sign --debug \
--flags 14 \
${DEVKEYS}/firmware_data_key.vbpubk \
${TMP}.keyblock1
cmp ${TMP}.keyblock0 ${TMP}.keyblock1
# Create one using PEM args
# old way
${FUTILITY} vbutil_keyblock --pack ${TMP}.keyblock2 \
--datapubkey ${DEVKEYS}/firmware_data_key.vbpubk \
--signprivate_pem ${TESTKEYS}/key_rsa4096.pem \
--pem_algorithm 8 \
--flags 9
# verify it
${FUTILITY} vbutil_keyblock --unpack ${TMP}.keyblock2 \
--signpubkey ${TESTKEYS}/key_rsa4096.sha512.vbpubk
# new way
${FUTILITY} sign --debug \
--pem_signpriv ${TESTKEYS}/key_rsa4096.pem \
--pem_algo 8 \
--flags 9 \
${DEVKEYS}/firmware_data_key.vbpubk \
${TMP}.keyblock3
cmp ${TMP}.keyblock2 ${TMP}.keyblock3
# Try it with an external signer
# old way
${FUTILITY} vbutil_keyblock --pack ${TMP}.keyblock4 \
--datapubkey ${DEVKEYS}/firmware_data_key.vbpubk \
--signprivate_pem ${TESTKEYS}/key_rsa4096.pem \
--pem_algorithm 8 \
--flags 19 \
--externalsigner ${SIGNER}
# verify it
${FUTILITY} vbutil_keyblock --unpack ${TMP}.keyblock4 \
--signpubkey ${TESTKEYS}/key_rsa4096.sha512.vbpubk
# new way
${FUTILITY} sign --debug \
--pem_signpriv ${TESTKEYS}/key_rsa4096.pem \
--pem_algo 8 \
--pem_external ${SIGNER} \
--flags 19 \
${DEVKEYS}/firmware_data_key.vbpubk \
${TMP}.keyblock5
cmp ${TMP}.keyblock4 ${TMP}.keyblock5
# cleanup
rm -rf ${TMP}*
exit 0