mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-10 17:41:54 +00:00
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>
This commit is contained in:
committed by
chrome-bot
parent
dec50797af
commit
7c5d3b2240
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
BIN
tests/futility/data/hammer_dev.bin
Executable file
BIN
tests/futility/data/hammer_dev.bin
Executable file
Binary file not shown.
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user