summaryrefslogtreecommitdiff
path: root/futility/file_type_usbpd1.c
diff options
context:
space:
mode:
Diffstat (limited to 'futility/file_type_usbpd1.c')
-rw-r--r--futility/file_type_usbpd1.c322
1 files changed, 292 insertions, 30 deletions
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;
+}