/* * 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 #include #include #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; VB2_DEBUG("ro_size 0x%08x\n", ro_size); VB2_DEBUG("ro_offset 0x%08x\n", ro_offset); VB2_DEBUG("rw_size 0x%08x\n", rw_size); VB2_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; VB2_DEBUG("%s(): name %s len 0x%08x (%d)\n", name, 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; VB2_DEBUG("rw_size => 0x%08x\n", rw_size); VB2_DEBUG("rw_offset => 0x%08x\n", rw_offset); VB2_DEBUG("sig_size 0x%08x\n", sig_size); VB2_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; } VB2_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; VB2_DEBUG("len 0x%08x ro_size 0x%08x ro_offset 0x%08x\n", len, ro_size, ro_offset); VB2_DEBUG("pub_size 0x%08x pub_offset 0x%08x nbytes 0x%08x\n", pub_size, pub_offset, nbytes); VB2_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_RSA2048_EXP3, 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: case VB2_SIG_RSA2048_EXP3: 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 vb2_error_t 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 vb2_error_t 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_FIRMWARE_WORKBUF_RECOMMENDED_SIZE] __attribute__ ((aligned (VB2_WORKBUF_ALIGN))); struct vb2_workbuf wb = { .buf = buf, .size = sizeof(buf), }; vb2_error_t 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 vb2_error_t 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; vb2_error_t 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; VB2_DEBUG("name %s len 0x%08x (%d)\n", name, 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; }