summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--futility/cmd_sign.c85
-rw-r--r--futility/file_type.inc4
-rw-r--r--futility/file_type_rwsig.c185
-rw-r--r--futility/file_type_usbpd1.c4
-rw-r--r--futility/futility_options.h6
-rw-r--r--tests/futility/test_file_types.c1
7 files changed, 282 insertions, 4 deletions
diff --git a/Makefile b/Makefile
index 225097fc..40d2422c 100644
--- a/Makefile
+++ b/Makefile
@@ -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);