diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | futility/cmd_sign.c | 85 | ||||
-rw-r--r-- | futility/file_type.inc | 4 | ||||
-rw-r--r-- | futility/file_type_rwsig.c | 185 | ||||
-rw-r--r-- | futility/file_type_usbpd1.c | 4 | ||||
-rw-r--r-- | futility/futility_options.h | 6 | ||||
-rw-r--r-- | tests/futility/test_file_types.c | 1 |
7 files changed, 282 insertions, 4 deletions
@@ -632,6 +632,7 @@ FUTIL_SRCS = \ futility/cmd_vbutil_keyblock.c \ futility/file_type.c \ futility/file_type_bios.c \ + futility/file_type_rwsig.c \ futility/file_type_usbpd1.c \ futility/vb1_helper.c \ futility/vb2_helper.c diff --git a/futility/cmd_sign.c b/futility/cmd_sign.c index 3152091c..e6184f43 100644 --- a/futility/cmd_sign.c +++ b/futility/cmd_sign.c @@ -43,6 +43,8 @@ struct sign_option_s sign_option = { .rw_size = 0xffffffff, .ro_offset = 0xffffffff, .rw_offset = 0xffffffff, + .pkey_offset = 0xffffffff, + .sig_offset = 0xffffffff, }; /* Helper to complain about invalid args. Returns num errors discovered */ @@ -462,6 +464,49 @@ static void print_help_usbpd1(int argc, char *argv[]) "\n"); } +/* The rwsig help is the same as the usbpd1 help, for now anyway. */ +static void print_help_rwsig(int argc, char *argv[]) +{ + printf("\n" + "Usage: " MYNAME " %s --type %s [options] INFILE [OUTFILE]\n" + "\n" + "This signs a %s.\n" + "\n" + "The INPUT is assumed to consist of equal-sized RO and RW" + " sections.\n" + "Signing the RW image will put the signature in the RW half." + " If the public\n" + "key is provided, it will be copied to the RO half.\n" + "\n" + "Options:\n" + "\n" + " --prikey FILE.vbprik2" + " Private key in vb2 format\n" + " --pubkey FILE.vbpubk2" + " Public key in vb2 format\n" + "\n" + "The size and offset assumptions can be overridden. " + "All numbers are in bytes.\n" + "Specify a size of 0 to ignore that section.\n" + "\n" + " --rw_size NUM" + " Size of the RW section (default half)\n" + " --rw_offset NUM" + " Start of the RW section (default half)\n" + " --sig_offset NUM" + " Where to place the signature (default is\n" + " " + " near the end of the RW image)\n" + " --pkey_offset NUM" + " Where to place the public key (default is\n" + " " + " near the end of the RO image)\n" + "\n", + argv[0], + futil_file_type_name(FILE_TYPE_RWSIG), + futil_file_type_desc(FILE_TYPE_RWSIG)); +} + static void (*help_type[NUM_FILE_TYPES])(int argc, char *argv[]) = { [FILE_TYPE_PUBKEY] = &print_help_pubkey, [FILE_TYPE_RAW_FIRMWARE] = &print_help_raw_firmware, @@ -469,6 +514,7 @@ static void (*help_type[NUM_FILE_TYPES])(int argc, char *argv[]) = { [FILE_TYPE_RAW_KERNEL] = &print_help_raw_kernel, [FILE_TYPE_KERN_PREAMBLE] = &print_help_kern_preamble, [FILE_TYPE_USBPD1] = &print_help_usbpd1, + [FILE_TYPE_RWSIG] = &print_help_rwsig, }; static const char usage_default[] = "\n" @@ -482,9 +528,11 @@ static const char usage_default[] = "\n" " full firmware image (bios.bin) same, or signed in-place\n" " raw linux kernel (vmlinuz) kernel partition image\n" " kernel partition (/dev/sda2) same, or signed in-place\n" + " usbpd1 firmware image same, or signed in-place\n" + " RO+RW firmware image same, or signed in-place\n" "\n" - "For more information, use \"" MYNAME " help %s TYPE\",\n" - "where TYPE is one of:\n\n"; + "For more information, use \"" MYNAME " help %s TYPE\", where\n" + "TYPE is one of:\n\n"; static void print_help_default(int argc, char *argv[]) { enum futil_file_type type; @@ -527,6 +575,10 @@ enum no_short_opts { OPT_RW_SIZE, OPT_RO_OFFSET, OPT_RW_OFFSET, + OPT_PKEY_OFFSET, + OPT_SIG_OFFSET, + OPT_PRIKEY, + OPT_PUBKEY, OPT_HELP, }; @@ -562,6 +614,11 @@ static const struct option long_opts[] = { {"rw_size", 1, NULL, OPT_RW_SIZE}, {"ro_offset", 1, NULL, OPT_RO_OFFSET}, {"rw_offset", 1, NULL, OPT_RW_OFFSET}, + {"pkey_offset", 1, NULL, OPT_PKEY_OFFSET}, + {"sig_offset", 1, NULL, OPT_SIG_OFFSET}, + {"prikey", 1, NULL, OPT_PRIKEY}, + {"privkey", 1, NULL, OPT_PRIKEY}, /* alias */ + {"pubkey", 1, NULL, OPT_PUBKEY}, {"help", 0, NULL, OPT_HELP}, {NULL, 0, NULL, 0}, }; @@ -728,6 +785,14 @@ static int do_sign(int argc, char *argv[]) errorcnt += parse_number_opt(optarg, "rw_offset", &sign_option.rw_offset); break; + case OPT_PKEY_OFFSET: + errorcnt += parse_number_opt(optarg, "pkey_offset", + &sign_option.pkey_offset); + break; + case OPT_SIG_OFFSET: + errorcnt += parse_number_opt(optarg, "sig_offset", + &sign_option.sig_offset); + break; case OPT_PEM_SIGNPRIV: sign_option.pem_signpriv = optarg; break; @@ -762,6 +827,19 @@ static int do_sign(int argc, char *argv[]) errorcnt++; } break; + case OPT_PRIKEY: + if (vb2_private_key_read(&sign_option.prikey, + optarg)) { + fprintf(stderr, "Error reading %s\n", optarg); + errorcnt++; + } + break; + case OPT_PUBKEY: + if (vb2_packed_key_read(&sign_option.pkey, optarg)) { + fprintf(stderr, "Error reading %s\n", optarg); + errorcnt++; + } + break; case OPT_HELP: helpind = optind - 1; break; @@ -895,6 +973,9 @@ static int do_sign(int argc, char *argv[]) errorcnt += no_opt_if(sign_option.hash_alg == VB2_HASH_INVALID, "hash_alg"); break; + case FILE_TYPE_RWSIG: + errorcnt += no_opt_if(!sign_option.prikey, "prikey"); + break; default: /* Anything else we don't care */ break; diff --git a/futility/file_type.inc b/futility/file_type.inc index 5433d03c..fd12286f 100644 --- a/futility/file_type.inc +++ b/futility/file_type.inc @@ -71,6 +71,10 @@ FILE_TYPE(CHROMIUMOS_DISK, "disk_img", "chromiumos disk image", NONE, NONE, NONE) +FILE_TYPE(RWSIG, "rwsig", "RO+RW firmware image", + NONE, + NONE, + S_(ft_sign_rwsig)) /* Firmware for USB Type-C power adapters */ FILE_TYPE(USBPD1, "usbpd1", "USB-PD charger image (v1.0)", R_(ft_recognize_usbpd1), diff --git a/futility/file_type_rwsig.c b/futility/file_type_rwsig.c new file mode 100644 index 00000000..6b757fff --- /dev/null +++ b/futility/file_type_rwsig.c @@ -0,0 +1,185 @@ +/* + * 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. + */ + +/* + * Some instances of the Chrome OS embedded controller firmware can't do a + * normal software sync handshake at boot, but will verify their own RW images + * instead. This is typically done by putting a struct vb2_packed_key in the RO + * image and a corresponding struct vb2_signature in the RW image. + * + * This file provides the basic implementation for that approach. + */ + +#include <stdint.h> +#include <stdio.h> +#include <unistd.h> + +#include "2sysincludes.h" +#include "2common.h" +#include "2rsa.h" +#include "2sha.h" +#include "file_type.h" +#include "futility.h" +#include "futility_options.h" +#include "vb2_common.h" +#include "host_common.h" +#include "host_key2.h" +#include "host_signature2.h" +#include "util_misc.h" + +/* + * Reserved space for the public key and signature. This may not be enough for + * larger key sizes since the vb2 structs are more than just the raw bits. + */ +#define PUBKEY_RSVD_SIZE 2048 +#define SIGNATURE_RSVD_SIZE 1024 + +/* True if start + size > max */ +static int bigger_than(uint32_t start, uint32_t size, uint32_t max) +{ + int r = start > max || size > max || start > max - size; + if (r) + Debug("%s: 0x%x + 0x%x > 0x%x\n", __func__, start, size, max); + return r; +} + +/* True if one region overlaps the other */ +static int overlaps(uint32_t start_a, uint32_t size_a, + uint32_t start_b, uint32_t size_b) +{ + if (start_a < start_b && start_a <= start_b - size_a) + return 0; + if (start_b < start_a && start_b <= start_a - size_b) + return 0; + Debug("%s: 0x%x + 0x%x overlaps 0x%x + 0x%x\n", + __func__, start_a, size_a, start_b, size_b); + return 1; +} + +/* Return 1 if okay, 0 if not */ +static int parse_size_opts(const uint8_t *buf, uint32_t len, + uint32_t *rw_offset_ptr, uint32_t *rw_size_ptr, + uint32_t *pkey_offset_ptr, uint32_t *sig_offset_ptr) +{ + uint32_t rw_offset, rw_size, pkey_offset, sig_offset; + + /* Start with defaults */ + + /* The image has both RO and RW, evenly split, RO first. */ + rw_size = rw_offset = len / 2; + + /* The public key is up against the end of the RO half */ + pkey_offset = rw_offset - PUBKEY_RSVD_SIZE; + + /* The signature key is up against the end of the whole image */ + sig_offset = len - SIGNATURE_RSVD_SIZE; + + /* The RW image to be signed doesn't include the signature */ + rw_size -= SIGNATURE_RSVD_SIZE; + + /* FIXME: Override the defaults here by looking for an FMAP or similar + * structure telling us where the parts are. */ + + /* We can override any of that with explicit args */ + if (sign_option.rw_offset != 0xffffffff) + rw_offset = sign_option.rw_offset; + if (sign_option.rw_size != 0xffffffff) + rw_size = sign_option.rw_size; + if (sign_option.pkey_offset != 0xffffffff) + pkey_offset = sign_option.pkey_offset; + if (sign_option.sig_offset != 0xffffffff) + sig_offset = sign_option.sig_offset; + + Debug("pkey_offset 0x%08x\n", pkey_offset); + Debug("rw_offset 0x%08x\n", rw_offset); + Debug("rw_size 0x%08x\n", rw_size); + Debug("sig_offset 0x%08x\n", sig_offset); + + /* Now let's do some sanity checks. */ + if (bigger_than(rw_offset, rw_size, len) || + overlaps(rw_offset, rw_size, pkey_offset, PUBKEY_RSVD_SIZE) || + overlaps(rw_offset, rw_size, sig_offset, SIGNATURE_RSVD_SIZE) || + overlaps(pkey_offset, PUBKEY_RSVD_SIZE, + sig_offset, SIGNATURE_RSVD_SIZE)) { + printf("size/offset values are bogus\n"); + return 0; + } + + *rw_offset_ptr = rw_offset; + *rw_size_ptr = rw_size; + *pkey_offset_ptr = pkey_offset; + *sig_offset_ptr = sig_offset; + + return 1; +} + +int ft_sign_rwsig(const char *name, uint8_t *buf, uint32_t len, void *data) +{ + struct vb2_signature *sig = 0; + int retval = 1; + uint32_t rw_offset, rw_size; /* what to sign */ + uint32_t pkey_offset, sig_offset; /* where to put blobs */ + uint32_t r; + + Debug("%s(): name %s\n", __func__, name); + Debug("%s(): len 0x%08x (%d)\n", __func__, len, len); + + /* Figure out what to sign and where to put the blobs */ + if (!parse_size_opts(buf, len, + &rw_offset, &rw_size, + &pkey_offset, &sig_offset)) + goto done; + + /* Sign the blob */ + r = vb2_sign_data(&sig, buf + rw_offset, rw_size, + sign_option.prikey, 0); + if (r) { + fprintf(stderr, + "Unable to sign data (error 0x%08x, if that helps)\n", + r); + goto done; + } + + Debug("sig_offset 0x%08x\n", sig_offset); + Debug("sig_size 0x%08x\n", sig->c.total_size); + + if (sig->c.total_size > SIGNATURE_RSVD_SIZE) + fprintf(stderr, "WARNING: The signature may be too large" + " (0x%08x > %08x)\n", + sig->c.total_size, SIGNATURE_RSVD_SIZE); + + /* Update the signature */ + memcpy(buf + sig_offset, sig, sig->c.total_size); + + /* If weren't given a public key, we're done */ + if (!sign_option.pkey) { + fprintf(stderr, "No public key given; not updating RO\n"); + retval = 0; + goto done; + } + + Debug("pkey_offset 0x%08x\n", pkey_offset); + Debug("pkey_size 0x%08x\n", sign_option.pkey->c.total_size); + + if (sign_option.pkey->c.total_size > PUBKEY_RSVD_SIZE) + fprintf(stderr, "WARNING: The public key may be too large" + " (0x%08x > %08x)\n", + sign_option.pkey->c.total_size, PUBKEY_RSVD_SIZE); + + /* Update the public key */ + memcpy(buf + pkey_offset, sign_option.pkey, + sign_option.pkey->c.total_size); + + /* Finally */ + retval = 0; +done: + if (sign_option.prikey) + vb2_private_key_free(sign_option.prikey); + if (sign_option.pkey) + free(sign_option.pkey); + + return retval; +} diff --git a/futility/file_type_usbpd1.c b/futility/file_type_usbpd1.c index acf3de06..36cb5cf2 100644 --- a/futility/file_type_usbpd1.c +++ b/futility/file_type_usbpd1.c @@ -12,7 +12,7 @@ * 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. + * new devices. Look at file_type_rwsig.c instead. */ #include <stdint.h> @@ -259,7 +259,7 @@ static enum vb2_signature_algorithm sigs[] = { VB2_SIG_RSA4096, VB2_SIG_RSA8192, }; -enum vb2_hash_algorithm hashes[] = { +static enum vb2_hash_algorithm hashes[] = { VB2_HASH_SHA256, VB2_HASH_SHA1, VB2_HASH_SHA512, diff --git a/futility/futility_options.h b/futility/futility_options.h index c8d0a8a6..e02ef2f6 100644 --- a/futility/futility_options.h +++ b/futility/futility_options.h @@ -16,6 +16,9 @@ #include "file_type.h" #include "2rsa.h" +struct vb2_private_key; +struct vb2_packed_key; + struct show_option_s { VbPublicKey *k; uint8_t *fv; @@ -58,6 +61,9 @@ struct sign_option_s { enum vb2_hash_algorithm hash_alg; uint32_t ro_size, rw_size; uint32_t ro_offset, rw_offset; + uint32_t pkey_offset, sig_offset; + struct vb2_private_key *prikey; + struct vb2_packed_key *pkey; }; extern struct sign_option_s sign_option; diff --git a/tests/futility/test_file_types.c b/tests/futility/test_file_types.c index 9f90a0f3..6b286293 100644 --- a/tests/futility/test_file_types.c +++ b/tests/futility/test_file_types.c @@ -37,6 +37,7 @@ static struct { {FILE_TYPE_VB2_PRIVKEY, "tests/futility/data/sample.vbprik2"}, {FILE_TYPE_PEM, "tests/testkeys/key_rsa2048.pem"}, {FILE_TYPE_USBPD1, "tests/futility/data/zinger_mp_image.bin"}, + {FILE_TYPE_RWSIG, }, /* need a test for this */ }; BUILD_ASSERT(ARRAY_SIZE(test_case) == NUM_FILE_TYPES); |