mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-16 21:07:26 +00:00
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:
committed by
chrome-internal-fetch
parent
5f2696d2ff
commit
c540f59be0
@@ -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");
|
||||
|
||||
@@ -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
|
||||
"
|
||||
|
||||
46
tests/futility/test_sign_fw_main.sh
Executable file
46
tests/futility/test_sign_fw_main.sh
Executable 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
|
||||
110
tests/futility/test_sign_keyblocks.sh
Executable file
110
tests/futility/test_sign_keyblocks.sh
Executable 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
|
||||
Reference in New Issue
Block a user