summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2015-09-24 10:59:41 -0700
committerchrome-bot <chrome-bot@chromium.org>2015-09-25 10:31:24 -0700
commit82db93d5fc924860e4f1fb4cf24f29b5b335a480 (patch)
tree084dfc2c4854fa2fd4b378a496c935a35abb162e
parent5b202a94f0ef30421c532b2aed963ac9f6f78521 (diff)
downloadvboot-82db93d5fc924860e4f1fb4cf24f29b5b335a480.tar.gz
futility: Add show capability for usbpd1 images
The firmware for the USB Type-C power adapters uses raw binary blobs for the public keys and signatures instead of readily-identifiable structs. We've been able to sign these firmware images for some time, but verifying the result generally required testing them on hardware. This CL adds some futilty support for recognizing and verifying those images too. It just tries various sig and hash algorithms, until it finds a combination for which the image is self-consistent (where the pubkey blob verifies the signature blob). BUG=none BRANCH=none TEST=make runtests This change also adds additional tests for usbpd1 images. We ensure that we correctly recognize and verify an MP-signed firmware, plus test signing and verifying usbpd1 images using multiple signature and hash algorithms. Change-Id: I4fbe8b37a694992f635d5469ae1c2449b1610dfd Signed-off-by: Bill Richardson <wfrichar@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/302415 Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--futility/file_type.inc7
-rw-r--r--futility/file_type_usbpd1.c322
-rw-r--r--tests/futility/data/zinger_mp_image.binbin0 -> 32768 bytes
-rwxr-xr-xtests/futility/run_test_scripts.sh1
-rw-r--r--tests/futility/test_file_types.c9
-rwxr-xr-xtests/futility/test_show_usbpd1.sh46
6 files changed, 346 insertions, 39 deletions
diff --git a/futility/file_type.inc b/futility/file_type.inc
index 4014c0b4..5433d03c 100644
--- a/futility/file_type.inc
+++ b/futility/file_type.inc
@@ -71,9 +71,8 @@ FILE_TYPE(CHROMIUMOS_DISK, "disk_img", "chromiumos disk image",
NONE,
NONE,
NONE)
-
-/* Firmware for Samus' USB Type-C power adapters */
+/* Firmware for USB Type-C power adapters */
FILE_TYPE(USBPD1, "usbpd1", "USB-PD charger image (v1.0)",
- NONE,
- NONE,
+ R_(ft_recognize_usbpd1),
+ S_(ft_show_usbpd1),
S_(ft_sign_usbpd1))
diff --git a/futility/file_type_usbpd1.c b/futility/file_type_usbpd1.c
index 7230b0ca..acf3de06 100644
--- a/futility/file_type_usbpd1.c
+++ b/futility/file_type_usbpd1.c
@@ -31,41 +31,22 @@
#include "host_signature2.h"
#include "util_misc.h"
-int ft_sign_usbpd1(const char *name, uint8_t *buf, uint32_t len, void *data)
+/* Return 1 if okay, 0 if not */
+static int parse_size_opts(uint32_t len,
+ uint32_t *ro_size_ptr, uint32_t *rw_size_ptr,
+ uint32_t *ro_offset_ptr, uint32_t * rw_offset_ptr)
{
- struct vb2_private_key *key_ptr = 0;
- struct vb2_signature *sig_ptr = 0;
- uint8_t *keyb_data = 0;
- uint32_t keyb_size;
- int retval = 1;
- uint32_t sig_size;
- uint32_t sig_offset;
- uint32_t pub_size;
- uint32_t pub_offset;
- uint32_t ro_size;
- uint32_t rw_size;
- uint32_t ro_offset;
- uint32_t rw_offset;
- uint32_t r;
-
- Debug("%s(): name %s\n", __func__, name);
- Debug("%s(): len 0x%08x (%d)\n", __func__, len, len);
+ uint32_t ro_size, rw_size, ro_offset, rw_offset;
- /*
- * Check for size args. Note that we're NOT worrying about rollover,
- * overlapping regions, out of bounds, etc.
- */
+ /* Assume the image has both RO and RW, evenly split. */
ro_offset = 0;
ro_size = rw_size = rw_offset = len / 2;
- /* Override some stuff? */
+ /* Unless told otherwise... */
if (sign_option.ro_size != 0xffffffff)
ro_size = sign_option.ro_size;
- if (sign_option.rw_size != 0xffffffff)
- rw_size = sign_option.rw_size;
-
- Debug("ro_size 0x%08x\n", ro_size);
- Debug("ro_offset 0x%08x\n", ro_offset);
+ if (sign_option.ro_offset != 0xffffffff)
+ ro_offset = sign_option.ro_offset;
/* If RO is missing, the whole thing must be RW */
if (!ro_size) {
@@ -74,14 +55,55 @@ int ft_sign_usbpd1(const char *name, uint8_t *buf, uint32_t len, void *data)
}
/* Unless that's overridden too */
- if (sign_option.ro_offset != 0xffffffff)
- ro_offset = sign_option.ro_offset;
+ if (sign_option.rw_size != 0xffffffff)
+ rw_size = sign_option.rw_size;
if (sign_option.rw_offset != 0xffffffff)
rw_offset = sign_option.rw_offset;
+ Debug("ro_size 0x%08x\n", ro_size);
+ Debug("ro_offset 0x%08x\n", ro_offset);
Debug("rw_size 0x%08x\n", rw_size);
Debug("rw_offset 0x%08x\n", rw_offset);
+ /* Now let's do some sanity checks. */
+ if (ro_size > len || ro_offset > len - ro_size ||
+ rw_size > len || rw_offset > len - rw_size) {
+ printf("size/offset values are bogus\n");
+ return 0;
+ }
+
+ *ro_size_ptr = ro_size;
+ *rw_size_ptr = rw_size;
+ *ro_offset_ptr = ro_offset;
+ *rw_offset_ptr = rw_offset;
+
+ return 1;
+}
+
+int ft_sign_usbpd1(const char *name, uint8_t *buf, uint32_t len, void *data)
+{
+ struct vb2_private_key *key_ptr = 0;
+ struct vb2_signature *sig_ptr = 0;
+ uint8_t *keyb_data = 0;
+ uint32_t keyb_size;
+ int retval = 1;
+ uint32_t sig_size;
+ uint32_t sig_offset;
+ uint32_t pub_size;
+ uint32_t pub_offset;
+ uint32_t ro_size;
+ uint32_t rw_size;
+ uint32_t ro_offset;
+ uint32_t rw_offset;
+ uint32_t r;
+
+ Debug("%s(): name %s\n", __func__, name);
+ Debug("%s(): len 0x%08x (%d)\n", __func__, len, len);
+
+ /* Get image locations */
+ if (!parse_size_opts(len, &ro_size, &rw_size, &ro_offset, &rw_offset))
+ goto done;
+
/* Read the signing keypair file */
if (vb2_private_key_read_pem(&key_ptr, sign_option.pem_signpriv)) {
fprintf(stderr, "Unable to read keypair from %s\n",
@@ -225,3 +247,243 @@ done:
return retval;
}
+
+
+/*
+ * Algorithms that we want to try, in order. We've only ever shipped with
+ * RSA2048 / SHA256, but the others should work in tests.
+ */
+static enum vb2_signature_algorithm sigs[] = {
+ VB2_SIG_RSA2048,
+ VB2_SIG_RSA1024,
+ VB2_SIG_RSA4096,
+ VB2_SIG_RSA8192,
+};
+enum vb2_hash_algorithm hashes[] = {
+ VB2_HASH_SHA256,
+ VB2_HASH_SHA1,
+ VB2_HASH_SHA512,
+};
+
+/*
+ * The size of the public key structure used by usbpd1 is
+ * 2 x RSANUMBYTES for n and rr fields
+ * plus 4 for n0inv, aligned on a multiple of 16
+ */
+static uint32_t usbpd1_packed_key_size(enum vb2_signature_algorithm sig_alg)
+{
+ switch (sig_alg) {
+ case VB2_SIG_RSA1024:
+ return 272;
+ case VB2_SIG_RSA2048:
+ return 528;
+ case VB2_SIG_RSA4096:
+ return 1040;
+ case VB2_SIG_RSA8192:
+ return 2064;
+ default:
+ return 0;
+ }
+}
+static void vb2_pubkey_from_usbpd1(struct vb2_public_key *key,
+ enum vb2_signature_algorithm sig_alg,
+ enum vb2_hash_algorithm hash_alg,
+ const uint8_t *o_pubkey,
+ uint32_t o_pubkey_size)
+{
+ key->arrsize = vb2_rsa_sig_size(sig_alg) / sizeof(uint32_t);
+ key->n0inv = *((uint32_t *)o_pubkey + 2 * key->arrsize);
+ key->n = (uint32_t *)o_pubkey;
+ key->rr = (uint32_t *)o_pubkey + key->arrsize;
+ key->sig_alg = sig_alg;
+ key->hash_alg = hash_alg;
+ key->desc = 0;
+ key->version = 0;
+ key->id = vb2_hash_id(hash_alg);
+}
+
+static int vb2_sig_from_usbpd1(struct vb2_signature **sig,
+ enum vb2_signature_algorithm sig_alg,
+ enum vb2_hash_algorithm hash_alg,
+ const uint8_t *o_sig,
+ uint32_t o_sig_size,
+ uint32_t data_size)
+{
+ struct vb2_signature s = {
+ .c.magic = VB2_MAGIC_SIGNATURE,
+ .c.struct_version_major = VB2_SIGNATURE_VERSION_MAJOR,
+ .c.struct_version_minor = VB2_SIGNATURE_VERSION_MINOR,
+ .c.fixed_size = sizeof(s),
+ .sig_alg = sig_alg,
+ .hash_alg = hash_alg,
+ .data_size = data_size,
+ .sig_size = vb2_rsa_sig_size(sig_alg),
+ .sig_offset = sizeof(s),
+ };
+ uint32_t total_size = sizeof(s) + o_sig_size;
+ uint8_t *buf = calloc(1, total_size);
+ if (!buf)
+ return VB2_ERROR_UNKNOWN;
+
+ memcpy(buf, &s, sizeof(s));
+ memcpy(buf + sizeof(s), o_sig, o_sig_size);
+
+ *sig = (struct vb2_signature *)buf;
+ return VB2_SUCCESS;
+}
+
+static void show_usbpd1_stuff(const char *name,
+ enum vb2_signature_algorithm sig_alg,
+ enum vb2_hash_algorithm hash_alg,
+ const uint8_t *o_pubkey, uint32_t o_pubkey_size)
+{
+ struct vb2_public_key key;
+ struct vb2_packed_key *pkey;
+ uint8_t *sha1sum;
+ int i;
+
+ vb2_pubkey_from_usbpd1(&key, sig_alg, hash_alg,
+ o_pubkey, o_pubkey_size);
+
+ if (vb2_public_key_pack(&pkey, &key))
+ return;
+
+ sha1sum = DigestBuf((uint8_t *)pkey + pkey->key_offset,
+ pkey->key_size, SHA1_DIGEST_ALGORITHM);
+
+ printf("USB-PD v1 image: %s\n", name);
+ printf(" Algorithm: %s %s\n",
+ vb2_lookup_by_num(vb2_text_vs_sig, sig_alg)->name,
+ vb2_lookup_by_num(vb2_text_vs_hash, hash_alg)->name);
+ printf(" Key sha1sum: ");
+ for (i = 0; i < SHA1_DIGEST_SIZE; i++)
+ printf("%02x", sha1sum[i]);
+ printf("\n");
+
+ free(sha1sum);
+ free(pkey);
+}
+
+
+/* Returns VB2_SUCCESS or random error code */
+static int try_our_own(enum vb2_signature_algorithm sig_alg,
+ enum vb2_hash_algorithm hash_alg,
+ const uint8_t *o_pubkey, uint32_t o_pubkey_size,
+ const uint8_t *o_sig, uint32_t o_sig_size,
+ const uint8_t *data, uint32_t data_size)
+{
+ struct vb2_public_key pubkey;
+ struct vb2_signature *sig;
+ uint8_t buf[VB2_WORKBUF_RECOMMENDED_SIZE]
+ __attribute__ ((aligned (VB2_WORKBUF_ALIGN)));
+ struct vb2_workbuf wb = {
+ .buf = buf,
+ .size = sizeof(buf),
+ };
+ int rv = VB2_ERROR_UNKNOWN;
+
+ vb2_pubkey_from_usbpd1(&pubkey, sig_alg, hash_alg,
+ o_pubkey, o_pubkey_size);
+
+ if ((rv = vb2_sig_from_usbpd1(&sig, sig_alg, hash_alg,
+ o_sig, o_sig_size, data_size)))
+ return rv;
+
+ rv = vb2_verify_data(data, data_size, sig, &pubkey, &wb);
+
+ free(sig);
+
+ return rv;
+}
+
+/* Returns VB2_SUCCESS if the image validates itself */
+static int check_self_consistency(const uint8_t *buf,
+ const char *name,
+ uint32_t ro_size, uint32_t rw_size,
+ uint32_t ro_offset, uint32_t rw_offset,
+ enum vb2_signature_algorithm sig_alg,
+ enum vb2_hash_algorithm hash_alg)
+{
+ /* Where are the important bits? */
+ uint32_t sig_size = vb2_rsa_sig_size(sig_alg);
+ uint32_t sig_offset = rw_offset + rw_size - sig_size;
+ uint32_t pubkey_size = usbpd1_packed_key_size(sig_alg);
+ uint32_t pubkey_offset = ro_offset + ro_size - pubkey_size;
+ int rv;
+
+ /* Skip stuff that obviously doesn't work */
+ if (sig_size > rw_size || pubkey_size > ro_size)
+ return VB2_ERROR_UNKNOWN;
+
+ rv = try_our_own(sig_alg, hash_alg, /* algs */
+ buf + pubkey_offset, pubkey_size, /* pubkey blob */
+ buf + sig_offset, sig_size, /* sig blob */
+ buf + rw_offset, rw_size - sig_size); /* RW image */
+
+ if (rv == VB2_SUCCESS && name)
+ show_usbpd1_stuff(name, sig_alg, hash_alg,
+ buf + pubkey_offset, pubkey_size);
+
+ return rv;
+}
+
+
+int ft_show_usbpd1(const char *name, uint8_t *buf, uint32_t len, void *data)
+{
+ uint32_t ro_size, rw_size, ro_offset, rw_offset;
+ int s, h;
+
+ Debug("%s(): name %s\n", __func__, name);
+ Debug("%s(): len 0x%08x (%d)\n", __func__, len, len);
+
+ /* Get image locations */
+ if (!parse_size_opts(len, &ro_size, &rw_size, &ro_offset, &rw_offset))
+ return 1;
+
+ /* TODO: If we don't have a RO image, ask for a public key
+ * TODO: If we're given an external public key, use it (and its alg) */
+ if (!ro_size) {
+ printf("Can't find the public key\n");
+ return 1;
+ }
+
+ /* TODO: Only loop through the numbers we haven't been given */
+ for (s = 0; s < ARRAY_SIZE(sigs); s++)
+ for (h = 0; h < ARRAY_SIZE(hashes); h++)
+ if (!check_self_consistency(buf, name,
+ ro_size, rw_size,
+ ro_offset, rw_offset,
+ sigs[s], hashes[h]))
+ return 0;
+
+ printf("This doesn't appear to be a complete usbpd1 image\n");
+ return 1;
+}
+
+enum futil_file_type ft_recognize_usbpd1(uint8_t *buf, uint32_t len)
+{
+ uint32_t ro_size, rw_size, ro_offset, rw_offset;
+ int s, h;
+
+ Debug("%s(): len 0x%08x (%d)\n", __func__, len, len);
+
+ /*
+ * Since we don't use any headers to identify or locate the pubkey and
+ * signature, in order to identify blob as the right type we have to
+ * just assume that the RO & RW are 1) both present, and 2) evenly
+ * split. Then we just try to use what we think might be the pubkey to
+ * validate what we think might be the signature.
+ */
+ ro_offset = 0;
+ ro_size = rw_size = rw_offset = len / 2;
+
+ for (s = 0; s < ARRAY_SIZE(sigs); s++)
+ for (h = 0; h < ARRAY_SIZE(hashes); h++)
+ if (!check_self_consistency(buf, 0,
+ ro_size, rw_size,
+ ro_offset, rw_offset,
+ sigs[s], hashes[h]))
+ return FILE_TYPE_USBPD1;
+
+ return FILE_TYPE_UNKNOWN;
+}
diff --git a/tests/futility/data/zinger_mp_image.bin b/tests/futility/data/zinger_mp_image.bin
new file mode 100644
index 00000000..68152c02
--- /dev/null
+++ b/tests/futility/data/zinger_mp_image.bin
Binary files differ
diff --git a/tests/futility/run_test_scripts.sh b/tests/futility/run_test_scripts.sh
index 8e6281a1..a0d9e471 100755
--- a/tests/futility/run_test_scripts.sh
+++ b/tests/futility/run_test_scripts.sh
@@ -47,6 +47,7 @@ ${SCRIPTDIR}/test_load_fmap.sh
${SCRIPTDIR}/test_main.sh
${SCRIPTDIR}/test_show_kernel.sh
${SCRIPTDIR}/test_show_vs_verify.sh
+${SCRIPTDIR}/test_show_usbpd1.sh
${SCRIPTDIR}/test_sign_firmware.sh
${SCRIPTDIR}/test_sign_fw_main.sh
${SCRIPTDIR}/test_sign_kernel.sh
diff --git a/tests/futility/test_file_types.c b/tests/futility/test_file_types.c
index 3fb21cc6..9f90a0f3 100644
--- a/tests/futility/test_file_types.c
+++ b/tests/futility/test_file_types.c
@@ -29,15 +29,14 @@ static struct {
{FILE_TYPE_BIOS_IMAGE, "tests/futility/data/bios_zgb_mp.bin"},
{FILE_TYPE_OLD_BIOS_IMAGE, "tests/futility/data/bios_mario_mp.bin"},
{FILE_TYPE_KERN_PREAMBLE, "tests/futility/data/kern_preamble.bin"},
- /* We don't have a way to identify these (yet?) */
- {FILE_TYPE_RAW_FIRMWARE, },
- {FILE_TYPE_RAW_KERNEL, },
- {FILE_TYPE_CHROMIUMOS_DISK, },
+ {FILE_TYPE_RAW_FIRMWARE, }, /* need a test for this */
+ {FILE_TYPE_RAW_KERNEL, }, /* need a test for this */
+ {FILE_TYPE_CHROMIUMOS_DISK, }, /* need a test for this */
{FILE_TYPE_PRIVKEY, "tests/devkeys/root_key.vbprivk"},
{FILE_TYPE_VB2_PUBKEY, "tests/futility/data/sample.vbpubk2"},
{FILE_TYPE_VB2_PRIVKEY, "tests/futility/data/sample.vbprik2"},
{FILE_TYPE_PEM, "tests/testkeys/key_rsa2048.pem"},
- {FILE_TYPE_USBPD1, },
+ {FILE_TYPE_USBPD1, "tests/futility/data/zinger_mp_image.bin"},
};
BUILD_ASSERT(ARRAY_SIZE(test_case) == NUM_FILE_TYPES);
diff --git a/tests/futility/test_show_usbpd1.sh b/tests/futility/test_show_usbpd1.sh
new file mode 100755
index 00000000..5fa5b93c
--- /dev/null
+++ b/tests/futility/test_show_usbpd1.sh
@@ -0,0 +1,46 @@
+#!/bin/bash -eux
+# Copyright 2015 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"
+
+DATADIR="${SCRIPTDIR}/data"
+TESTS="dingdong hoho minimuffin zinger"
+TESTKEYS=${SRCDIR}/tests/testkeys
+
+SIGS="1024 2048 4096 8192"
+HASHES="SHA1 SHA256 SHA512"
+
+set -o pipefail
+
+for s in $SIGS; do
+
+ echo -n "$s " 1>&3
+
+ for test in $TESTS; do
+
+ infile=${DATADIR}/${test}.unsigned
+
+ for h in $HASHES; do
+
+ pemfile=${TESTKEYS}/key_rsa${s}.pem
+ outfile=${TMP}.${test}_${s}_${h}.new
+
+ # sign it
+ ${FUTILITY} sign --type usbpd1 --pem ${pemfile} ${infile} ${outfile}
+
+ # make sure it identifies correctly
+ ${FUTILITY} verify ${outfile}
+
+ done
+ done
+done
+
+# cleanup
+rm -rf ${TMP}*
+exit 0