123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- /*
- * 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.
- */
- /*
- * The USB Type-C chargers released with Samus ("Pixel (2015)") have upgradable
- * firmware. Due to space considerations, we don't have room for handy things
- * like an FMAP or headers for the signatures. Accordingly, all the normally
- * variable factors (image size, signature algorithms, etc.) are hard coded
- * and the image itself just looks like a bunch of random numbers.
- *
- * This file handles those images, but PLEASE don't use it as a template for
- * new devices. Look at file_type_rwsig.c instead.
- */
- #include <stdint.h>
- #include <stdio.h>
- #include <unistd.h>
- #include "2sysincludes.h"
- #include "2common.h"
- #include "2sha.h"
- #include "2rsa.h"
- #include "file_type.h"
- #include "futility.h"
- #include "futility_options.h"
- #include "vb21_common.h"
- #include "host_common.h"
- #include "host_key2.h"
- #include "host_signature2.h"
- #include "util_misc.h"
- /* 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)
- {
- uint32_t ro_size, rw_size, ro_offset, rw_offset;
- /* Assume the image has both RO and RW, evenly split. */
- ro_offset = 0;
- ro_size = rw_size = rw_offset = len / 2;
- /* Unless told otherwise... */
- if (sign_option.ro_size != 0xffffffff)
- ro_size = sign_option.ro_size;
- 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) {
- rw_size = len;
- rw_offset = 0;
- }
- /* Unless that's overridden too */
- 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 vb21_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",
- sign_option.pem_signpriv);
- goto done;
- }
- /* Set the algs */
- key_ptr->hash_alg = sign_option.hash_alg;
- key_ptr->sig_alg = vb2_rsa_sig_alg(key_ptr->rsa_private_key);
- if (key_ptr->sig_alg == VB2_SIG_INVALID) {
- fprintf(stderr, "Unsupported sig algorithm in RSA key\n");
- goto done;
- }
- /* Figure out what needs signing */
- sig_size = vb2_rsa_sig_size(key_ptr->sig_alg);
- if (rw_size < sig_size) {
- fprintf(stderr,
- "The RW image is too small to hold the signature"
- " (0x%08x < %08x)\n", rw_size, sig_size);
- goto done;
- }
- rw_size -= sig_size;
- sig_offset = rw_offset + rw_size;
- Debug("rw_size => 0x%08x\n", rw_size);
- Debug("rw_offset => 0x%08x\n", rw_offset);
- Debug("sig_size 0x%08x\n", sig_size);
- Debug("sig_offset 0x%08x\n", sig_offset);
- /* Sign the blob */
- r = vb21_sign_data(&sig_ptr, buf + rw_offset, rw_size, key_ptr, "Bah");
- if (r) {
- fprintf(stderr,
- "Unable to sign data (error 0x%08x, if that helps)\n",
- r);
- goto done;
- }
- /* Double-check the size */
- if (sig_ptr->sig_size != sig_size) {
- fprintf(stderr,
- "ERROR: sig size is %d bytes, not %d as expected.\n",
- sig_ptr->sig_size, sig_size);
- goto done;
- }
- /* Okay, looking good. Update the signature. */
- memcpy(buf + sig_offset,
- (uint8_t *)sig_ptr + sig_ptr->sig_offset,
- sig_ptr->sig_size);
- /* If there's no RO section, we're done. */
- if (!ro_size) {
- retval = 0;
- goto done;
- }
- /* Otherwise, now update the public key */
- if (vb_keyb_from_rsa(key_ptr->rsa_private_key,
- &keyb_data, &keyb_size)) {
- fprintf(stderr, "Couldn't extract the public key\n");
- goto done;
- }
- Debug("keyb_size is 0x%x (%d):\n", keyb_size, keyb_size);
- /*
- * Of course the packed public key format is different. Why would you
- * think otherwise? Since the dawn of time, vboot has used this:
- *
- * uint32_t nwords size of RSA key in 32-bit words
- * uint32_t n0inv magic RSA n0inv
- * uint32_t n[nwords] magic RSA modulus little endian array
- * uint32_t rr[nwords] magic RSA R^2 little endian array
- *
- * But for no discernable reason, the usbpd1 format uses this:
- *
- * uint32_t n[nwords] magic RSA modulus little endian array
- * uint32_t rr[nwords] magic RSA R^2 little endian array
- * uint32_t n0inv magic RSA n0inv
- *
- * There's no nwords field, and n0inv is last insted of first. Sigh.
- */
- pub_size = keyb_size - 4;
- /* align pubkey size to 16-byte boundary */
- uint32_t pub_pad = pub_size;
- pub_size = (pub_size + 16) / 16 * 16;
- pub_pad = pub_size - pub_pad;
- pub_offset = ro_offset + ro_size - pub_size;
- if (ro_size < pub_size) {
- fprintf(stderr,
- "The RO image is too small to hold the public key"
- " (0x%08x < %08x)\n", ro_size, pub_size);
- goto done;
- }
- /* How many bytes in the arrays? */
- uint32_t nbytes = 4 * (*(uint32_t *)keyb_data);
- /* Source offsets from keyb_data */
- uint32_t src_ofs_n0inv = 4;
- uint32_t src_ofs_n = src_ofs_n0inv + 4;
- uint32_t src_ofs_rr = src_ofs_n + nbytes;
- /* Dest offsets from buf */
- uint32_t dst_ofs_n = pub_offset + 0;
- uint32_t dst_ofs_rr = dst_ofs_n + nbytes;
- uint32_t dst_ofs_n0inv = dst_ofs_rr + nbytes;
- Debug("len 0x%08x ro_size 0x%08x ro_offset 0x%08x\n",
- len, ro_size, ro_offset);
- Debug("pub_size 0x%08x pub_offset 0x%08x nbytes 0x%08x\n",
- pub_size, pub_offset, nbytes);
- Debug("pub_pad 0x%08x\n", pub_pad);
- /* Copy n[nwords] */
- memcpy(buf + dst_ofs_n,
- keyb_data + src_ofs_n,
- nbytes);
- /* Copy rr[nwords] */
- memcpy(buf + dst_ofs_rr,
- keyb_data + src_ofs_rr,
- nbytes);
- /* Copy n0inv */
- memcpy(buf + dst_ofs_n0inv,
- keyb_data + src_ofs_n0inv,
- 4);
- /* Pad with 0xff */
- memset(buf + dst_ofs_n0inv + 4, 0xff, pub_pad);
- /* Finally */
- retval = 0;
- done:
- if (key_ptr)
- vb2_private_key_free(key_ptr);
- if (keyb_data)
- free(keyb_data);
- 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,
- };
- static 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 vb21_sig_from_usbpd1(struct vb21_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 vb21_signature s = {
- .c.magic = VB21_MAGIC_SIGNATURE,
- .c.struct_version_major = VB21_SIGNATURE_VERSION_MAJOR,
- .c.struct_version_minor = VB21_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 vb21_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 vb21_packed_key *pkey;
- uint8_t sha1sum[VB2_SHA1_DIGEST_SIZE];
- int i;
- vb2_pubkey_from_usbpd1(&key, sig_alg, hash_alg,
- o_pubkey, o_pubkey_size);
- if (vb21_public_key_pack(&pkey, &key))
- return;
- vb2_digest_buffer((uint8_t *)pkey + pkey->key_offset, pkey->key_size,
- VB2_HASH_SHA1, sha1sum, sizeof(sha1sum));
- printf("USB-PD v1 image: %s\n", name);
- printf(" Algorithm: %s %s\n",
- vb2_get_sig_algorithm_name(sig_alg),
- vb2_get_hash_algorithm_name(hash_alg));
- printf(" Key sha1sum: ");
- for (i = 0; i < VB2_SHA1_DIGEST_SIZE; i++)
- printf("%02x", sha1sum[i]);
- printf("\n");
- 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 vb21_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 = vb21_sig_from_usbpd1(&sig, sig_alg, hash_alg,
- o_sig, o_sig_size, data_size)))
- return rv;
- rv = vb21_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;
- /*
- * 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;
- }
|