diff options
author | Bill Richardson <wfrichar@chromium.org> | 2014-09-23 22:17:02 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-09-27 00:28:51 +0000 |
commit | c540f59be047d69251b7f9ce0637a8a0c6fe150f (patch) | |
tree | 1734eb933cb0bada6f4993c12c554724464de013 | |
parent | 5f2696d2ff09d7c9c5c6125e9f0a62e56e54e0b8 (diff) | |
download | vboot-c540f59be047d69251b7f9ce0637a8a0c6fe150f.tar.gz |
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>
-rw-r--r-- | futility/cmd_sign.c | 174 | ||||
-rwxr-xr-x | tests/futility/run_test_scripts.sh | 2 | ||||
-rwxr-xr-x | tests/futility/test_sign_fw_main.sh | 46 | ||||
-rwxr-xr-x | tests/futility/test_sign_keyblocks.sh | 110 |
4 files changed, 328 insertions, 4 deletions
diff --git a/futility/cmd_sign.c b/futility/cmd_sign.c index f4c682ee..c28aee99 100644 --- a/futility/cmd_sign.c +++ b/futility/cmd_sign.c @@ -51,11 +51,16 @@ static struct local_data_s { uint8_t *config_data; uint64_t config_size; enum arch_t arch; + int fv_specified; uint32_t kloadaddr; uint32_t padding; int vblockonly; char *outfile; int create_new_outfile; + char *pem_signpriv; + int pem_algo_specified; + uint32_t pem_algo; + char *pem_external; } option = { .version = 1, .arch = ARCH_UNSPECIFIED, @@ -77,8 +82,39 @@ static int no_opt_if(int expr, const char *optname) /* This wraps/signs a public key, producing a keyblock. */ int futil_cb_sign_pubkey(struct futil_traverse_state_s *state) { - fprintf(stderr, "Don't know how to sign %s yet\n", state->name); - return 1; + 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; + } + 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); } /* @@ -287,8 +323,36 @@ int futil_cb_resign_kernel_part(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); - return 1; + 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; + } + + 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; } @@ -446,10 +510,53 @@ static const char usage[] = "\n" "\n" "Where INFILE is a\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" " raw linux kernel; OUTFILE is a kernel partition image\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" "-----------------------------------------------------------------\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) { printf(usage, prog); + printf(usage_pubkey, kNumAlgorithms - 1); + puts(usage_fw_main); printf(usage_bios, option.version); printf(usage_new_kpart, option.kloadaddr, option.padding); printf(usage_old_kpart, option.padding); @@ -542,6 +651,9 @@ enum no_short_opts { OPT_ARCH, OPT_KLOADADDR, OPT_PADDING, + OPT_PEM_SIGNPRIV, + OPT_PEM_ALGO, + OPT_PEM_EXTERNAL, }; static const struct option long_opts[] = { @@ -565,6 +677,9 @@ static const struct option long_opts[] = { {"arch", 1, NULL, OPT_ARCH}, {"kloadaddr", 1, NULL, OPT_KLOADADDR}, {"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}, {"debug", 0, &debugging_enabled, 1}, {NULL, 0, NULL, 0}, @@ -648,6 +763,9 @@ static int do_sign(int argc, char *argv[]) case 'l': option.loemid = optarg; break; + case OPT_FV: + option.fv_specified = 1; + /* fallthrough */ case OPT_INFILE: /* aka "--vmlinuz" */ inout_file_count++; infile = optarg; @@ -711,6 +829,23 @@ static int do_sign(int argc, char *argv[]) errorcnt++; } 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 '?': if (optopt) 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 || option.arch != ARCH_UNSPECIFIED) 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 */ @@ -761,6 +898,28 @@ static int do_sign(int argc, char *argv[]) "Unable to determine the type of the input file\n"); errorcnt++; 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: fprintf(stderr, "Resigning a keyblock is kind of pointless.\n"); fprintf(stderr, "Just create a new one.\n"); @@ -786,6 +945,13 @@ static int do_sign(int argc, char *argv[]) if (option.vblockonly) option.create_new_outfile = 1; 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: option.create_new_outfile = 1; errorcnt += no_opt_if(!option.signprivate, "signprivate"); diff --git a/tests/futility/run_test_scripts.sh b/tests/futility/run_test_scripts.sh index bb3a600b..f4508461 100755 --- a/tests/futility/run_test_scripts.sh +++ b/tests/futility/run_test_scripts.sh @@ -45,6 +45,8 @@ ${SCRIPTDIR}/test_dump_fmap.sh ${SCRIPTDIR}/test_load_fmap.sh ${SCRIPTDIR}/test_gbb_utility.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_kernel.sh " diff --git a/tests/futility/test_sign_fw_main.sh b/tests/futility/test_sign_fw_main.sh new file mode 100755 index 00000000..eec68a6c --- /dev/null +++ b/tests/futility/test_sign_fw_main.sh @@ -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 diff --git a/tests/futility/test_sign_keyblocks.sh b/tests/futility/test_sign_keyblocks.sh new file mode 100755 index 00000000..1cccd346 --- /dev/null +++ b/tests/futility/test_sign_keyblocks.sh @@ -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 |