diff options
author | Nicolas Boichat <drinkcat@google.com> | 2017-02-20 17:01:40 +0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2017-02-25 10:32:03 -0800 |
commit | 7c5d3b22407ee08f09bb4cc388f96d87f9b6a0d2 (patch) | |
tree | e4f45e76fbe32120e6105a9dd511c245984f1a53 | |
parent | dec50797af68464ba85017e01b3f0dfc684d90cb (diff) | |
download | vboot-7c5d3b22407ee08f09bb4cc388f96d87f9b6a0d2.tar.gz |
futility: rwsig: Add support for images with FMAP
If an FMAP is detected in the rwsig image file, use it
to determine the location of:
- RW region
- RW signature
- public key in RO region
futility show uses that information to verify the signature,
and futility sign uses it is correctly resign the image,
and replace the public key a well.
This also adds tests for this use case. hammer_dev.bin sample
image uses huge RO public key and RW signature regions to make
sure all keys up to RSA-8192 can be used.
BRANCH=none
BUG=chrome-os-partner:62321
TEST=make -j
TEST=./build/futility/futility --debug show \
--pubkey hammer.vbpubk2 hammer.bin
TEST=./build/futility/futility --debug show hammer.bin
TEST=cp hammer.bin hammer.bin.orig
./build/futility/futility --debug sign \
--prikey hammer.vbprik2 hammer.bin
diff hammer.bin hammer.bin.orig => identical
TEST=openssl genrsa -3 -out hammer2.pem 2048
futility create --desc="Hammer 2nd key" hammer2.pem \
hammer2
./build/futility/futility --debug sign \
--version 2 --prikey hammer2.vbprik2 hammer.bin
These 2 commands succeed, but show different keys:
./build/futility/futility --debug show hammer.bin
./build/futility/futility --debug show hammer.bin.orig
TEST=make runtests
Change-Id: I2cebc421eaf97d1b92c9a58afc238d41487d0f6d
Reviewed-on: https://chromium-review.googlesource.com/445536
Commit-Ready: Nicolas Boichat <drinkcat@chromium.org>
Tested-by: Nicolas Boichat <drinkcat@chromium.org>
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r-- | futility/cmd_sign.c | 33 | ||||
-rw-r--r-- | futility/file_type_rwsig.c | 250 | ||||
-rwxr-xr-x | tests/futility/data/hammer_dev.bin | bin | 0 -> 131072 bytes | |||
-rwxr-xr-x | tests/futility/run_test_scripts.sh | 2 | ||||
-rwxr-xr-x | tests/futility/test_rwsig.sh (renamed from tests/futility/test_show_rwsig.sh) | 24 |
5 files changed, 253 insertions, 56 deletions
diff --git a/futility/cmd_sign.c b/futility/cmd_sign.c index d0c053b4..5b686342 100644 --- a/futility/cmd_sign.c +++ b/futility/cmd_sign.c @@ -479,30 +479,27 @@ static void print_help_rwsig(int argc, char *argv[]) "\n" "This signs a %s.\n" "\n" - "The INFILE is a binary blob of arbitrary size." - " It is signed using the\n" + "The INFILE is a binary blob of arbitrary size. It is signed using the\n" "private key and the vb21_signature blob emitted.\n" "\n" - "If no OUTFILE is specified, the INFILE should contain" - " an existing\n" - "vb21_signature blob near its end. The data_size from that" - " signature is\n" - "used to re-sign a portion of the INFILE, and the old" - " signature blob is\n" + "If no OUTFILE is specified, the INFILE should contain an existing\n" + "vb21_signature blob near its end. The data_size from that signature is\n" + "used to re-sign a portion of the INFILE, and the old signature blob is\n" "replaced.\n" + "Alternatively, if INFILE contains an FMAP, RW and signatures offsets\n" + "are read from that table, and, if a public key is provided, the\n" + "public key is replaced in the RO image.\n" "\n" "Options:\n" "\n" - " --prikey FILE.vbprik2 " - "Private key in vb2 format (required)\n" - " --sig_size NUM " - "Offset from the end of INFILE where the\n" - " " - "signature blob should be located\n" - " " - "(default 1024 bytes)\n" - " --data_size NUM " - "Number of bytes of INFILE to sign\n" + " --prikey FILE.vbprik2 Private key in vb2 format (required)\n" + " --version NUM Public key version if we are replacing" + " the key in INFILE (default: 1)\n" + " --sig_size NUM Offset from the end of INFILE where the\n" + " signature blob should be located, if\n" + " the file does not contain an FMAP.\n" + " (default 1024 bytes)\n" + " --data_size NUM Number of bytes of INFILE to sign\n" "\n", argv[0], futil_file_type_name(FILE_TYPE_RWSIG), diff --git a/futility/file_type_rwsig.c b/futility/file_type_rwsig.c index 9561f6cf..1323da41 100644 --- a/futility/file_type_rwsig.c +++ b/futility/file_type_rwsig.c @@ -22,6 +22,7 @@ #include "2rsa.h" #include "2sha.h" #include "file_type.h" +#include "fmap.h" #include "futility.h" #include "futility_options.h" #include "vb21_common.h" @@ -61,12 +62,16 @@ static void show_sig(const char *name, const struct vb21_signature *sig) int ft_show_rwsig(const char *name, uint8_t *buf, uint32_t len, void *nuthin) { const struct vb21_signature *sig = 0; + const struct vb21_packed_key *pkey = show_option.pkey; struct vb2_public_key key; uint8_t workbuf[VB2_VERIFY_DATA_WORKBUF_BYTES] __attribute__ ((aligned (VB2_WORKBUF_ALIGN))); struct vb2_workbuf wb; uint32_t data_size, sig_size = SIGNATURE_RSVD_SIZE; + uint32_t total_data_size = 0; uint8_t *data; + FmapHeader *fmap; + int i; Debug("%s(): name %s\n", __func__, name); Debug("%s(): len 0x%08x (%d)\n", __func__, len, len); @@ -82,8 +87,55 @@ int ft_show_rwsig(const char *name, uint8_t *buf, uint32_t len, void *nuthin) } data = show_option.fv; data_size = show_option.fv_size; + total_data_size = show_option.fv_size; + } else if ((fmap = fmap_find(buf, len))) { + /* This looks like a full image. */ + FmapAreaHeader *fmaparea; + + Debug("Found an FMAP!\n"); + + /* If no public key is provided, use the one packed in RO + * image, and print that. */ + if (!pkey) { + pkey = (const struct vb21_packed_key *) + fmap_find_by_name(buf, len, fmap, "KEY_RO", 0); + + if (pkey) + ft_show_vb21_pubkey(name, (uint8_t *)pkey, + pkey->c.total_size, NULL); + } + + sig = (const struct vb21_signature *) + fmap_find_by_name(buf, len, fmap, "SIG_RW", &fmaparea); + if (!sig) { + Debug("No SIG_RW in FMAP.\n"); + return 1; + } + + sig_size = fmaparea->area_size; + + Debug("Looking for signature at 0x%x (0x%x)\n", + (uint8_t*)sig - buf, sig_size); + + if (VB2_SUCCESS != vb21_verify_signature(sig, sig_size)) + return 1; + + show_sig(name, sig); + data = fmap_find_by_name(buf, len, fmap, "EC_RW", &fmaparea); + data_size = sig->data_size; + /* + * TODO(crosbug.com/p/62231): EC_RW region should not include + * the signature. + */ + total_data_size = fmaparea->area_size-sig_size; + + if (!data) { + Debug("No EC_RW in FMAP.\n"); + return 1; + } } else { - /* Where would it be? */ + /* Or maybe this is just the RW portion, that does not + * contain a FMAP. */ if (show_option.sig_size) sig_size = show_option.sig_size; @@ -99,24 +151,30 @@ int ft_show_rwsig(const char *name, uint8_t *buf, uint32_t len, void *nuthin) show_sig(name, sig); data = buf; data_size = sig->data_size; + total_data_size = len - sig_size; } else { return 1; } } - if (!show_option.pkey) { + if (!pkey) { printf("No public key available to verify with\n"); return show_option.strict; } /* We already did this once, so it should work again */ if (vb21_unpack_key(&key, - (const uint8_t *)show_option.pkey, - show_option.pkey->c.total_size)) { + (const uint8_t *)pkey, + pkey->c.total_size)) { Debug("Can't unpack pubkey\n"); return 1; } + if (data_size > total_data_size) { + Debug("Invalid signature data_size: bigger than total area size.\n"); + return 1; + } + /* The sig is destroyed by the verify operation, so make a copy */ { uint8_t sigbuf[sig->c.total_size]; @@ -128,7 +186,15 @@ int ft_show_rwsig(const char *name, uint8_t *buf, uint32_t len, void *nuthin) (struct vb21_signature *)sigbuf, (const struct vb2_public_key *)&key, &wb)) { - printf("Signature verification failed\n"); + fprintf(stderr, "Signature verification failed\n"); + return 1; + } + } + + /* Check that the rest of region is padded with 0xff. */ + for (i = data_size; i < total_data_size; i++) { + if (data[i] != 0xff) { + fprintf(stderr, "Padding verification failed\n"); return 1; } } @@ -137,11 +203,18 @@ int ft_show_rwsig(const char *name, uint8_t *buf, uint32_t len, void *nuthin) return 0; } -int ft_sign_rwsig(const char *name, uint8_t *buf, uint32_t len, void *data) +int ft_sign_rwsig(const char *name, uint8_t *buf, uint32_t len, void *nuthin) { - struct vb21_signature *sig = 0; + struct vb21_signature *tmp_sig = 0; + struct vb2_public_key *pubkey = 0; + struct vb21_packed_key *packedkey = 0; + uint8_t *keyb_data = 0; + uint32_t keyb_size; + uint8_t* data = buf; /* data to be signed */ uint32_t r, data_size = len, sig_size = SIGNATURE_RSVD_SIZE; int retval = 1; + FmapHeader *fmap = NULL; + FmapAreaHeader *fmaparea; Debug("%s(): name %s\n", __func__, name); Debug("%s(): len 0x%08x (%d)\n", __func__, len, len); @@ -150,25 +223,56 @@ int ft_sign_rwsig(const char *name, uint8_t *buf, uint32_t len, void *data) if (sign_option.inout_file_count < 2) { const struct vb21_signature *old_sig; - /* Where would it be? */ - if (sign_option.sig_size) - sig_size = sign_option.sig_size; + fmap = fmap_find(buf, len); - Debug("Looking for old signature at 0x%x\n", len - sig_size); + if (fmap) { + /* This looks like a full image. */ + Debug("Found an FMAP!\n"); - if (len < sig_size) { - fprintf(stderr, "File is too small\n"); - return 1; + old_sig = (const struct vb21_signature *) + fmap_find_by_name(buf, len, fmap, "SIG_RW", + &fmaparea); + if (!old_sig) { + Debug("No SIG_RW in FMAP.\n"); + goto done; + } + + sig_size = fmaparea->area_size; + + Debug("Looking for signature at 0x%x (0x%x)\n", + (uint8_t*)old_sig - buf, sig_size); + + data = fmap_find_by_name(buf, len, fmap, "EC_RW", + &fmaparea); + if (!data) { + Debug("No EC_RW in FMAP.\n"); + goto done; + } + } else { + /* Or maybe this is just the RW portion, that does not + * contain a FMAP. */ + if (sign_option.sig_size) + sig_size = sign_option.sig_size; + + Debug("Looking for old signature at 0x%x\n", + len - sig_size); + + if (len < sig_size) { + fprintf(stderr, "File is too small\n"); + goto done; + } + + /* Take a look */ + old_sig = (const struct vb21_signature *) + (buf + len - sig_size); } - /* Take a look */ - old_sig = (const struct vb21_signature *)(buf + len - sig_size); if (vb21_verify_signature(old_sig, sig_size)) { fprintf(stderr, "Can't find a valid signature\n"); - return 1; + goto done; } - /* Use the same exent again */ + /* Use the same extent again */ data_size = old_sig->data_size; Debug("Found sig: data_size is 0x%x (%d)\n", data_size, @@ -180,7 +284,7 @@ int ft_sign_rwsig(const char *name, uint8_t *buf, uint32_t len, void *data) data_size = sign_option.data_size; /* Sign the blob */ - r = vb21_sign_data(&sig, buf, data_size, sign_option.prikey, 0); + r = vb21_sign_data(&tmp_sig, data, data_size, sign_option.prikey, 0); if (r) { fprintf(stderr, "Unable to sign data (error 0x%08x, if that helps)\n", @@ -190,16 +294,16 @@ int ft_sign_rwsig(const char *name, uint8_t *buf, uint32_t len, void *data) if (sign_option.inout_file_count < 2) { /* Overwrite the old signature */ - if (sig->c.total_size > sig_size) { + if (tmp_sig->c.total_size > sig_size) { fprintf(stderr, "New sig is too large (%d > %d)\n", - sig->c.total_size, sig_size); + tmp_sig->c.total_size, sig_size); goto done; } memset(buf + len - sig_size, 0xff, sig_size); - memcpy(buf + len - sig_size, sig, sig->c.total_size); + memcpy(buf + len - sig_size, tmp_sig, tmp_sig->c.total_size); } else { /* Write the signature to a new file */ - r = vb21_write_object(sign_option.outfile, sig); + r = vb21_write_object(sign_option.outfile, tmp_sig); if (r) { fprintf(stderr, "Unable to write sig" " (error 0x%08x, if that helps)\n", r); @@ -207,24 +311,112 @@ int ft_sign_rwsig(const char *name, uint8_t *buf, uint32_t len, void *data) } } + /* For full images, let's replace the public key in RO. */ + if (fmap) { + uint8_t *new_pubkey; + uint8_t *pubkey_buf = 0; + + /* Create the public key */ + if (vb2_public_key_alloc(&pubkey, + sign_option.prikey->sig_alg)) { + fprintf(stderr, "Unable to allocate the public key\n"); + goto done; + } + + /* Extract the keyb blob */ + if (vb_keyb_from_rsa(sign_option.prikey->rsa_private_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 = sign_option.prikey->hash_alg; + pubkey->version = sign_option.version_specified ? + sign_option.version : 1; + vb2_public_key_set_desc(pubkey, sign_option.prikey->desc); + + memcpy((struct vb2_id *)pubkey->id, &sign_option.prikey->id, + sizeof(*(pubkey->id))); + + if (vb21_public_key_pack(&packedkey, pubkey)) { + goto done; + } + + new_pubkey = fmap_find_by_name(buf, len, fmap, "KEY_RO", + &fmaparea); + if (!new_pubkey) { + Debug("No KEY_RO in FMAP.\n"); + goto done; + } + /* Overwrite the old signature */ + if (packedkey->c.total_size > fmaparea->area_size) { + fprintf(stderr, "New sig is too large (%d > %d)\n", + packedkey->c.total_size, sig_size); + goto done; + } + + memset(new_pubkey, 0xff, fmaparea->area_size); + memcpy(new_pubkey, packedkey, packedkey->c.total_size); + } + /* Finally */ retval = 0; done: - if (sig) - free(sig); + if (tmp_sig) + free(tmp_sig); + if (pubkey) + vb2_public_key_free(pubkey); + if (packedkey) + free(packedkey); + if (keyb_data) + free(keyb_data); return retval; } enum futil_file_type ft_recognize_rwsig(uint8_t *buf, uint32_t len) { + FmapHeader *fmap; + const struct vb21_signature *sig = NULL; + uint32_t sig_size; + if (!vb21_verify_signature((const struct vb21_signature *)buf, len)) return FILE_TYPE_RWSIG; - if (len >= SIGNATURE_RSVD_SIZE && - !vb21_verify_signature((const struct vb21_signature *) - (buf + len - SIGNATURE_RSVD_SIZE), - SIGNATURE_RSVD_SIZE)) + fmap = fmap_find(buf, len); + if (fmap) { + /* This looks like a full image. */ + FmapAreaHeader *fmaparea; + + sig = (const struct vb21_signature *) + fmap_find_by_name(buf, len, fmap, "SIG_RW", &fmaparea); + + if (!sig) + return FILE_TYPE_UNKNOWN; + + sig_size = fmaparea->area_size; + } else { + /* RW-only image */ + sig = (const struct vb21_signature *) + (buf + len - SIGNATURE_RSVD_SIZE); + sig_size = SIGNATURE_RSVD_SIZE; + } + + if (len >= sig_size && !vb21_verify_signature(sig, sig_size)) return FILE_TYPE_RWSIG; return FILE_TYPE_UNKNOWN; diff --git a/tests/futility/data/hammer_dev.bin b/tests/futility/data/hammer_dev.bin Binary files differnew file mode 100755 index 00000000..d124fb6b --- /dev/null +++ b/tests/futility/data/hammer_dev.bin diff --git a/tests/futility/run_test_scripts.sh b/tests/futility/run_test_scripts.sh index b817c430..15a2580c 100755 --- a/tests/futility/run_test_scripts.sh +++ b/tests/futility/run_test_scripts.sh @@ -46,9 +46,9 @@ ${SCRIPTDIR}/test_dump_fmap.sh ${SCRIPTDIR}/test_gbb_utility.sh ${SCRIPTDIR}/test_load_fmap.sh ${SCRIPTDIR}/test_main.sh +${SCRIPTDIR}/test_rwsig.sh ${SCRIPTDIR}/test_show_contents.sh ${SCRIPTDIR}/test_show_kernel.sh -${SCRIPTDIR}/test_show_rwsig.sh ${SCRIPTDIR}/test_show_vs_verify.sh ${SCRIPTDIR}/test_show_usbpd1.sh ${SCRIPTDIR}/test_sign_firmware.sh diff --git a/tests/futility/test_show_rwsig.sh b/tests/futility/test_rwsig.sh index 915842d6..efdb6dbb 100755 --- a/tests/futility/test_show_rwsig.sh +++ b/tests/futility/test_rwsig.sh @@ -12,8 +12,7 @@ cd "$OUTDIR" DATADIR="${SCRIPTDIR}/data" TESTKEYS=${SRCDIR}/tests/testkeys -# Do not test 8192 as the signature length is > 1024 bytes -SIGS="1024 2048 4096 2048_exp3" +SIGS="1024 2048 2048_exp3 4096 8192" HASHES="SHA1 SHA256 SHA512" set -o pipefail @@ -24,20 +23,29 @@ for s in $SIGS; do for h in $HASHES; do pemfile=${TESTKEYS}/key_rsa${s}.pem outfile=${TMP}.${s}_${h}.new - infile=${DATADIR}/random_noise.bin + infile=${DATADIR}/hammer_dev.bin outkeys=${TMP}.${s}_${h} - outsig=${TMP}.${s}_${h}.signature + outfile=${TMP}.${s}_${h}.bin ${FUTILITY} create --desc "Test key" --hash_alg ${h} \ ${pemfile} ${outkeys} + # The input file should be correctly signed to start with + ${FUTILITY} show --type rwsig ${infile} + + # Using the wrong key to verify it should fail + if ${FUTILITY} show --type rwsig --pubkey ${outkeys}.vbpubk2 \ + ${infile}; then + exit 1 + fi + + cp ${infile} ${outfile} + ${FUTILITY} sign --type rwsig --prikey ${outkeys}.vbprik2 \ - ${infile} ${outsig} - dd if=/dev/zero bs=$((4096 + 1024)) count=1 of=${outfile} - dd if=${infile} of=${outfile} conv=notrunc - dd if=${outsig} of=${outfile} bs=4096 seek=1 conv=notrunc + --version 2 ${outfile} ${FUTILITY} show --type rwsig --pubkey ${outkeys}.vbpubk2 ${outfile} + ${FUTILITY} show --type rwsig ${outfile} done done |