summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2014-09-23 22:17:02 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-11-06 01:16:26 +0000
commit3a02e41aa9607767a38ffd33852f4a942678797d (patch)
tree1734eb933cb0bada6f4993c12c554724464de013
parent8f754f0a7dee101724eb8dcf641e54cb3c2a459c (diff)
downloadvboot-3a02e41aa9607767a38ffd33852f4a942678797d.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> Reviewed-on: https://chromium-review.googlesource.com/227883 Tested-by: Daisuke Nojiri <dnojiri@chromium.org> Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org> Commit-Queue: Daisuke Nojiri <dnojiri@chromium.org>
-rw-r--r--futility/cmd_sign.c174
-rwxr-xr-xtests/futility/run_test_scripts.sh2
-rwxr-xr-xtests/futility/test_sign_fw_main.sh46
-rwxr-xr-xtests/futility/test_sign_keyblocks.sh110
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