summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--futility/cmd_create.c21
-rw-r--r--futility/cmd_sign.c162
-rw-r--r--futility/file_type.inc6
-rw-r--r--futility/file_type_usbpd1.c227
-rw-r--r--futility/futility_options.h8
-rw-r--r--futility/vb2_helper.c27
-rw-r--r--tests/futility/data/dingdong.pem27
-rw-r--r--tests/futility/data/dingdong.signedbin0 -> 131072 bytes
-rw-r--r--tests/futility/data/dingdong.unsignedbin0 -> 131072 bytes
-rw-r--r--tests/futility/data/hoho.pem27
-rw-r--r--tests/futility/data/hoho.signedbin0 -> 131072 bytes
-rw-r--r--tests/futility/data/hoho.unsignedbin0 -> 131072 bytes
-rw-r--r--tests/futility/data/minimuffin.pem27
-rw-r--r--tests/futility/data/minimuffin.signedbin0 -> 32768 bytes
-rw-r--r--tests/futility/data/minimuffin.unsignedbin0 -> 32768 bytes
-rw-r--r--tests/futility/data/zinger.pem27
-rw-r--r--tests/futility/data/zinger.signedbin0 -> 32768 bytes
-rw-r--r--tests/futility/data/zinger.unsignedbin0 -> 32768 bytes
-rwxr-xr-xtests/futility/run_test_scripts.sh1
-rw-r--r--tests/futility/test_file_types.c1
-rwxr-xr-xtests/futility/test_sign_usbpd1.sh56
22 files changed, 569 insertions, 49 deletions
diff --git a/Makefile b/Makefile
index 108f92c3..00c6085c 100644
--- a/Makefile
+++ b/Makefile
@@ -626,6 +626,7 @@ FUTIL_SRCS = \
futility/cmd_vbutil_keyblock.c \
futility/file_type.c \
futility/file_type_bios.c \
+ futility/file_type_usbpd1.c \
futility/vb1_helper.c \
futility/vb2_helper.c
diff --git a/futility/cmd_create.c b/futility/cmd_create.c
index c919eab3..e3fafd30 100644
--- a/futility/cmd_create.c
+++ b/futility/cmd_create.c
@@ -23,6 +23,7 @@
#include "host_misc2.h"
#include "futility.h"
+#include "futility_options.h"
/* Command line options */
enum {
@@ -283,7 +284,6 @@ static int do_create(int argc, char *argv[])
int errorcnt = 0;
char *e, *s;
int i, r, len, remove_ext = 0;
- const struct vb2_text_vs_enum *entry;
while ((i = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
switch (i) {
@@ -311,28 +311,13 @@ static int do_create(int argc, char *argv[])
break;
case OPT_HASH_ALG:
- /* try string first */
- entry = vb2_lookup_by_name(vb2_text_vs_hash, optarg);
- if (entry) {
- opt_hash_alg = entry->num;
- break;
- }
- /* fine, try number */
- opt_hash_alg = strtoul(optarg, &e, 0);
- if (!*optarg || (e && *e)) {
+ if (!vb2_lookup_hash_alg(optarg, &opt_hash_alg)) {
fprintf(stderr,
"invalid hash_alg \"%s\"\n", optarg);
errorcnt++;
- break;
- }
- if (!vb2_lookup_by_num(vb2_text_vs_hash,
- opt_hash_alg)) {
- fprintf(stderr,
- "Hash algorithm %d is unsupported\n",
- opt_hash_alg);
- errorcnt++;
}
break;
+
case OPT_HELP:
print_help(argc, argv);
return !!errorcnt;
diff --git a/futility/cmd_sign.c b/futility/cmd_sign.c
index 9ec99f6f..3152091c 100644
--- a/futility/cmd_sign.c
+++ b/futility/cmd_sign.c
@@ -27,6 +27,8 @@
#include "kernel_blob.h"
#include "util_misc.h"
#include "vb1_helper.h"
+#include "vb2_common.h"
+#include "host_key2.h"
#include "vboot_common.h"
/* Options */
@@ -36,6 +38,11 @@ struct sign_option_s sign_option = {
.kloadaddr = CROS_32BIT_ENTRY_ADDR,
.padding = 65536,
.type = FILE_TYPE_UNKNOWN,
+ .hash_alg = VB2_HASH_SHA256, /* default */
+ .ro_size = 0xffffffff,
+ .rw_size = 0xffffffff,
+ .ro_offset = 0xffffffff,
+ .rw_offset = 0xffffffff,
};
/* Helper to complain about invalid args. Returns num errors discovered */
@@ -406,12 +413,62 @@ static void print_help_kern_preamble(int argc, char *argv[])
printf(usage_old_kpart, sign_option.padding);
}
+static void print_help_usbpd1(int argc, char *argv[])
+{
+ struct vb2_text_vs_enum *entry;
+
+ 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"
+ "with the public key at the end of of the RO section and the"
+ " signature\n"
+ "at the end of the RW section (both in an opaque binary"
+ " format).\n"
+ "Signing the image will update both binary blobs, so both"
+ " public and\n"
+ "private keys are required.\n"
+ "\n"
+ "The signing key is specified with:\n"
+ "\n"
+ " --pem "
+ "FILE Signing keypair in PEM format\n"
+ "\n"
+ " --hash_alg "
+ "NUM Hash algorithm to use:\n",
+ argv[0],
+ futil_file_type_name(FILE_TYPE_USBPD1),
+ futil_file_type_desc(FILE_TYPE_USBPD1));
+ for (entry = vb2_text_vs_hash; entry->name; entry++)
+ printf(" %d / %s%s\n",
+ entry->num, entry->name,
+ entry->num == VB2_HASH_SHA256 ? " (default)" : "");
+ printf("\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"
+ " --ro_size NUM"
+ " Size of the RO section (default half)\n"
+ " --rw_size NUM"
+ " Size of the RW section (default half)\n"
+ " --ro_offset NUM"
+ " Start of the RO section (default 0)\n"
+ " --rw_offset NUM"
+ " Start of the RW section (default half)\n"
+ "\n");
+}
+
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,
[FILE_TYPE_BIOS_IMAGE] = &print_help_bios_image,
[FILE_TYPE_RAW_KERNEL] = &print_help_raw_kernel,
[FILE_TYPE_KERN_PREAMBLE] = &print_help_kern_preamble,
+ [FILE_TYPE_USBPD1] = &print_help_usbpd1,
};
static const char usage_default[] = "\n"
@@ -465,6 +522,11 @@ enum no_short_opts {
OPT_PEM_ALGO,
OPT_PEM_EXTERNAL,
OPT_TYPE,
+ OPT_HASH_ALG,
+ OPT_RO_SIZE,
+ OPT_RW_SIZE,
+ OPT_RO_OFFSET,
+ OPT_RW_OFFSET,
OPT_HELP,
};
@@ -490,15 +552,34 @@ static const struct option long_opts[] = {
{"kloadaddr", 1, NULL, OPT_KLOADADDR},
{"pad", 1, NULL, OPT_PADDING},
{"pem_signpriv", 1, NULL, OPT_PEM_SIGNPRIV},
+ {"pem", 1, NULL, OPT_PEM_SIGNPRIV}, /* alias */
{"pem_algo", 1, NULL, OPT_PEM_ALGO},
{"pem_external", 1, NULL, OPT_PEM_EXTERNAL},
{"type", 1, NULL, OPT_TYPE},
{"vblockonly", 0, &sign_option.vblockonly, 1},
+ {"hash_alg", 1, NULL, OPT_HASH_ALG},
+ {"ro_size", 1, NULL, OPT_RO_SIZE},
+ {"rw_size", 1, NULL, OPT_RW_SIZE},
+ {"ro_offset", 1, NULL, OPT_RO_OFFSET},
+ {"rw_offset", 1, NULL, OPT_RW_OFFSET},
{"help", 0, NULL, OPT_HELP},
{NULL, 0, NULL, 0},
};
static char *short_opts = ":s:b:k:S:B:v:f:d:l:";
+/* Return zero on success */
+static int parse_number_opt(const char *arg, const char *name, uint32_t *dest)
+{
+ char *e;
+ uint32_t val = strtoul(arg, &e, 0);
+ if (!*arg || (e && *e)) {
+ fprintf(stderr, "Invalid --%s \"%s\"\n", name, arg);
+ return 1;
+ }
+ *dest = val;
+ return 0;
+}
+
static int do_sign(int argc, char *argv[])
{
char *infile = 0;
@@ -511,9 +592,11 @@ static int do_sign(int argc, char *argv[])
int inout_file_count = 0;
int mapping;
int helpind = 0;
+ int longindex;
opterr = 0; /* quiet, you */
- while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
+ while ((i = getopt_long(argc, argv, short_opts, long_opts,
+ &longindex)) != -1) {
switch (i) {
case 's':
sign_option.signprivate = PrivateKeyRead(optarg);
@@ -562,12 +645,8 @@ static int do_sign(int argc, char *argv[])
case 'f':
sign_option.flags_specified = 1;
- sign_option.flags = strtoul(optarg, &e, 0);
- if (!*optarg || (e && *e)) {
- fprintf(stderr,
- "Invalid --flags \"%s\"\n", optarg);
- errorcnt++;
- }
+ errorcnt += parse_number_opt(optarg, "flags",
+ &sign_option.flags);
break;
case 'd':
sign_option.loemdir = optarg;
@@ -626,20 +705,28 @@ static int do_sign(int argc, char *argv[])
}
break;
case OPT_KLOADADDR:
- sign_option.kloadaddr = strtoul(optarg, &e, 0);
- if (!*optarg || (e && *e)) {
- fprintf(stderr,
- "Invalid --kloadaddr \"%s\"\n", optarg);
- errorcnt++;
- }
+ errorcnt += parse_number_opt(optarg, "kloadaddr",
+ &sign_option.kloadaddr);
break;
case OPT_PADDING:
- sign_option.padding = strtoul(optarg, &e, 0);
- if (!*optarg || (e && *e)) {
- fprintf(stderr,
- "Invalid --padding \"%s\"\n", optarg);
- errorcnt++;
- }
+ errorcnt += parse_number_opt(optarg, "padding",
+ &sign_option.padding);
+ break;
+ case OPT_RO_SIZE:
+ errorcnt += parse_number_opt(optarg, "ro_size",
+ &sign_option.ro_size);
+ break;
+ case OPT_RW_SIZE:
+ errorcnt += parse_number_opt(optarg, "rw_size",
+ &sign_option.rw_size);
+ break;
+ case OPT_RO_OFFSET:
+ errorcnt += parse_number_opt(optarg, "ro_offset",
+ &sign_option.ro_offset);
+ break;
+ case OPT_RW_OFFSET:
+ errorcnt += parse_number_opt(optarg, "rw_offset",
+ &sign_option.rw_offset);
break;
case OPT_PEM_SIGNPRIV:
sign_option.pem_signpriv = optarg;
@@ -654,6 +741,14 @@ static int do_sign(int argc, char *argv[])
errorcnt++;
}
break;
+ case OPT_HASH_ALG:
+ if (!vb2_lookup_hash_alg(optarg,
+ &sign_option.hash_alg)) {
+ fprintf(stderr,
+ "invalid hash_alg \"%s\"\n", optarg);
+ errorcnt++;
+ }
+ break;
case OPT_PEM_EXTERNAL:
sign_option.pem_external = optarg;
break;
@@ -795,6 +890,11 @@ static int do_sign(int argc, char *argv[])
errorcnt += no_opt_if(sign_option.arch == ARCH_UNSPECIFIED,
"arch");
break;
+ case FILE_TYPE_USBPD1:
+ errorcnt += no_opt_if(!sign_option.pem_signpriv, "pem");
+ errorcnt += no_opt_if(sign_option.hash_alg == VB2_HASH_INVALID,
+ "hash_alg");
+ break;
default:
/* Anything else we don't care */
break;
@@ -839,18 +939,18 @@ static int do_sign(int argc, char *argv[])
}
} else {
/* We'll read-modify-write the output file */
- mapping = MAP_RW;
- if (inout_file_count > 1)
- futil_copy_file_or_die(infile, sign_option.outfile);
- Debug("open RW %s\n", sign_option.outfile);
- infile = sign_option.outfile;
- ifd = open(sign_option.outfile, O_RDWR);
- if (ifd < 0) {
- errorcnt++;
- fprintf(stderr, "Can't open %s for writing: %s\n",
- sign_option.outfile, strerror(errno));
- goto done;
- }
+ mapping = MAP_RW;
+ if (inout_file_count > 1)
+ futil_copy_file_or_die(infile, sign_option.outfile);
+ Debug("open RW %s\n", sign_option.outfile);
+ infile = sign_option.outfile;
+ ifd = open(sign_option.outfile, O_RDWR);
+ if (ifd < 0) {
+ errorcnt++;
+ fprintf(stderr, "Can't open %s for writing: %s\n",
+ sign_option.outfile, strerror(errno));
+ goto done;
+ }
}
if (0 != futil_map_file(ifd, mapping, &buf, &buf_len)) {
diff --git a/futility/file_type.inc b/futility/file_type.inc
index 7d40d45c..4014c0b4 100644
--- a/futility/file_type.inc
+++ b/futility/file_type.inc
@@ -71,3 +71,9 @@ FILE_TYPE(CHROMIUMOS_DISK, "disk_img", "chromiumos disk image",
NONE,
NONE,
NONE)
+
+/* Firmware for Samus' USB Type-C power adapters */
+FILE_TYPE(USBPD1, "usbpd1", "USB-PD charger image (v1.0)",
+ NONE,
+ NONE,
+ S_(ft_sign_usbpd1))
diff --git a/futility/file_type_usbpd1.c b/futility/file_type_usbpd1.c
new file mode 100644
index 00000000..7230b0ca
--- /dev/null
+++ b/futility/file_type_usbpd1.c
@@ -0,0 +1,227 @@
+/*
+ * 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.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "2sysincludes.h"
+#include "2common.h"
+#include "2rsa.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"
+
+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);
+
+ /*
+ * Check for size args. Note that we're NOT worrying about rollover,
+ * overlapping regions, out of bounds, etc.
+ */
+ ro_offset = 0;
+ ro_size = rw_size = rw_offset = len / 2;
+
+ /* Override some stuff? */
+ 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 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.ro_offset != 0xffffffff)
+ ro_offset = sign_option.ro_offset;
+ if (sign_option.rw_offset != 0xffffffff)
+ rw_offset = sign_option.rw_offset;
+
+ Debug("rw_size 0x%08x\n", rw_size);
+ Debug("rw_offset 0x%08x\n", rw_offset);
+
+ /* 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 = vb2_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;
+}
diff --git a/futility/futility_options.h b/futility/futility_options.h
index 3d416d5b..c8d0a8a6 100644
--- a/futility/futility_options.h
+++ b/futility/futility_options.h
@@ -13,6 +13,8 @@
#define VBOOT_REFERENCE_FUTILITY_OPTIONS_H_
#include <stdint.h>
#include "vboot_common.h"
+#include "file_type.h"
+#include "2rsa.h"
struct show_option_s {
VbPublicKey *k;
@@ -53,7 +55,13 @@ struct sign_option_s {
uint32_t pem_algo;
char *pem_external;
enum futil_file_type type;
+ enum vb2_hash_algorithm hash_alg;
+ uint32_t ro_size, rw_size;
+ uint32_t ro_offset, rw_offset;
};
extern struct sign_option_s sign_option;
+/* Return true if hash_alg was identified, either by name or number */
+int vb2_lookup_hash_alg(const char *str, enum vb2_hash_algorithm *alg);
+
#endif /* VBOOT_REFERENCE_FUTILITY_OPTIONS_H_ */
diff --git a/futility/vb2_helper.c b/futility/vb2_helper.c
index c10190ff..4ee0a28b 100644
--- a/futility/vb2_helper.c
+++ b/futility/vb2_helper.c
@@ -22,6 +22,33 @@
#include "file_type.h"
#include "futility.h"
+int vb2_lookup_hash_alg(const char *str, enum vb2_hash_algorithm *alg)
+{
+ const struct vb2_text_vs_enum *entry;
+ uint32_t val;
+ char *e;
+
+ /* try string first */
+ entry = vb2_lookup_by_name(vb2_text_vs_hash, str);
+ if (entry) {
+ *alg = entry->num;
+ return 1;
+ }
+
+ /* fine, try number */
+ val = strtoul(str, &e, 0);
+ if (!*str || (e && *e))
+ /* that's not a number */
+ return 0;
+
+ if (!vb2_lookup_by_num(vb2_text_vs_hash, val))
+ /* That's not a valid alg */
+ return 0;
+
+ *alg = val;
+ return 1;
+}
+
enum futil_file_type ft_recognize_vb2_key(uint8_t *buf, uint32_t len)
{
struct vb2_public_key pubkey;
diff --git a/tests/futility/data/dingdong.pem b/tests/futility/data/dingdong.pem
new file mode 100644
index 00000000..e2ff6781
--- /dev/null
+++ b/tests/futility/data/dingdong.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAqhcIe02T0+guB+QDIKTy578gwH0W0BpZM4B0scTq0ozQ6YJe
+9O74HBWYlWUkbx+/AaM50yeroqx6DC4ZLgm8PcBFpCkxhuOdP6BZbjEqEoI4nVOg
+nDsW3XpNODYueA7IpqiOjUgAcAeNTEfdxIGGh6XWwsJmGdbJCWu4SiWsKyJVThCr
+1DPh3nPEmzDpX4CYU93qFeoJptKKH0DJcMKEP3CR8H0OU9S5wLoA10s9TECsWMN9
+z7vZQaelxsFrzHPgY2amX1uruSI4BaZ8aGYTtKzcTkzSnT/jD8QTiDnSYsMXs7h4
+6af24h3SZdApB5yFYQqcS80DWgHcubeT24ulXwIDAQABAoIBAAslnUmvaNu/YJzl
+xYqzJLQpY3UZ/Y+/2k60wXERDa6kyeAzyhNVQe9dPvWzfjLGKtdpohWDiQ0NLqZz
+svTAcJS/cBD1HijP6/NKh/HfyPkTjbBJ1cHHYZU8OalQa7U0itPZQhZiPJ0a8Zip
+MRB6yJ1FMhDrepOA7wXuCFLbqy/cYcY+MdKL6Fny5FFIBMq16EeFOKBOR+DZsLEg
+R71n1rV7IzxTIfcjD8ws43bRM0sbwykoaHUIYuwXO/AIII9QX75V7nQjB0JUOSYo
+Z9OrrUaf+rP8l4Rd76tTHxrpMU3dy8C/ht5jpXbiMYViOl0pNDAzJfCvIx0+q9Iy
+BrMLKUkCgYEA21jJ6Yqz9Nwkv/kcovYpiBuUHhMjmdsMv0LZnWWELCpXmisemeWJ
+8FrnaMTTrYscbIn+MPkLQbb2FQHyT+HHtHchsps8i+snYEBBky0fyAlWG0LL/Rvh
+GPFkKNXeMFRcGg7rTp51L0DhG6hbWgCkck3AtcHy86LgehsDaWhEi+0CgYEAxoMi
+35F1Q0PMlpftK5sRYvwO5jSM2RvYxhqDImghyW43Bnc0tu7bVK25V+Vd3ZRBnjm5
+8E6A6UpP0By4qaEQuG1kMoZ2TTOix2q0AbltOGYuzLq41PirvINqj3DVzw3M4IZE
+dL6PtiJcOGeFodL12Sz1QRZVksMfpxz0XaVpxPsCgYAmDDi58f1VM/qL8kItYlXB
+7ka7EMbUIVMMuiPVUY6jupSHgYNFXrOWpa4OVlYBfGfpy+XzyL9THtGAw12szZU+
+kIuf152hB6FE6OB3DxS8NiJhiCyqMvPQx85/5tkruPZg7sWSVZouICrsCUAPVJ0x
+1pre7E2gRVh61cS5vARn4QKBgQCNxp6jeal8LvHxI/R5Tjiur0Kc2y806BR79/ds
+HV70E8kszvpRJGp1IdXblq7hT79FmAjaPdcHxtEV201vqN7eORJ0m1/mZ1h8gBKr
+oJkGzMPj5/+V6zwMWPdEFtw9EqgeOwatMmRFOmkOx7DDEH4Ra3CF2cOoG7+BhMZq
+E3dk/QKBgQCYXqptB56sUrjnCXKEAdR96SDlVCmL7BBI893xDSAYIKhbpQsI2YY1
+dcFb02bnMbpqjkwHqpjHD8MJOWvXf8q/5FxDjHBSLSL8fM2PL9DI65c8MmwpHUSZ
+ZdcRhMrlN1iTkzw7WdfFCqb0HNl73sP5baqbRZgC+gysmDtgTZxBTQ==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/futility/data/dingdong.signed b/tests/futility/data/dingdong.signed
new file mode 100644
index 00000000..4b2a18bf
--- /dev/null
+++ b/tests/futility/data/dingdong.signed
Binary files differ
diff --git a/tests/futility/data/dingdong.unsigned b/tests/futility/data/dingdong.unsigned
new file mode 100644
index 00000000..4b18fbf5
--- /dev/null
+++ b/tests/futility/data/dingdong.unsigned
Binary files differ
diff --git a/tests/futility/data/hoho.pem b/tests/futility/data/hoho.pem
new file mode 100644
index 00000000..08d5bd41
--- /dev/null
+++ b/tests/futility/data/hoho.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEApfbLqgOYIM6AfRI6SJcj1Crengsp+yjHXtwZFOH6WDVP5Q9c
+KSbwqoKHEKTbWJ90ymjqjfi382hl64L/V6J8SfIqGhrFztwXLhJOwFXRK5Jgzkk+
+YUByDzAKTKOlzvvRqk10Tq5a3wphg1SxGVLLOOsTGoKhGI93Wkf2j8AibMlVVZzz
+Q8DmVszkYZL+Kchv6h1FgSvBW0oZa5tVod+0XToWSrPEYnBWs0zZEywCusIXMy7D
+LaqMPFB4LTkDZ9Ne8jnB5xRad+ME4CgxZqUwGC7tdFdHdiiXpIwzIoxVk6xFIZUF
+uusG4RR3O2ubaPJ/Fpf3UuuCWmddk37WaC7o7QIDAQABAoIBAAG4L94AEYhte0lQ
+cggkgLuHAi1zAilW/9HMx/m+aaCWVNCTuym1/JJXrdyPSLJ/XG9obN2xsP41m7C3
+97tJtK3zc1o34srE3vycNfKqMPOZnaUlfx700vmzTrgCjgo5868nBEh4Z/qdmesJ
+aphPkklxrg39QnwFqH/n9PcCT5j+7LyCeWeGbWxKfpzP2CT6v8XxT3XY1mtFSa4j
+dfYaqb+aunYAhjEb4gqa48hyNTQAZskDOUr1TK433wbGqRughXXrQQix+FBW483u
+IGo8aGgiQsjYxHX+ynNTMKW1Oap9WZRWVxF09Ph1f3MT+k3gKqM/0AejlDfBuTDu
+aLxiKIUCgYEA1FZmfGn4RNlghv/ZCAlfWqbf5NA1/wA/Knk8u0R+kMQ71e8NFjOc
+Ym3Uix+89KcKDBIgHn1360pNvSCeTyVU28wQ2bst5s6pvu4FYDvjym2nTgXcFJX6
+DDnZfVZ+WLSFR8E76LQLJGd00DSq0/uBw3ULyRSirkuQnFI3w3u4BH8CgYEAyBdD
+UMV83kwQaDMuGgKqZtD4Ou3s/MDzMwcNgUSjLIueFdsXVnlzYQwwJXuLFkrp5COx
+Zyoha/d1QQawnYehKmHWWy7qN/l0CO+F2DGb1E6pNXJrn+zn33Mgz9ms8421eqqn
+ATQbq6ZQInk1IrkLfyZ3t09l6cyBMJuJjkoBrJMCgYA2Hfsq1FtJONnILmbjDHh4
+AzXm/EX2wtpWeeXHmLJlNQ5G/REpymeeEn3sI1+mPvhpkSkMfE/W8O4VOL4AT/Rr
+vHvC8ljFjYBnwAQwvbLVwdK1KPspZ/v9p7TNpAC5nPCnFBGvwktgsNltwy6SrnQp
+G6iwTAkWQP4PSUkbEmoZAwKBgF0OLJlQ70y3FV5Qhx1DphohD4DgjDnURoaxvf8j
+e7vIxuGlPgpSe21j7LRR65KXjoUycFvpRRfgQyDVyqfInxSF4doQTI9xrRxGwPmV
+wMIRPzKDHziGRiQud9ESjBPNENyWpwqxQDkpJNWThzm503Xz3vNasqv0FxUTEPsi
+wfqPAoGABXPl7baUkpYzppAJqRlGNxsMjpbWscDPjmPosrGs6d81DP287s/IjfDR
+ysQptvhJRK/lubM8As+d0/VLd6P8wk8dyZR1nRELwnVaPC55cS5+YIjgXK9TBmLA
+hC0BIgujJS2qbXQRQF7yX925Gg77WLN2sJqtVg1Brine056pHTA=
+-----END RSA PRIVATE KEY-----
diff --git a/tests/futility/data/hoho.signed b/tests/futility/data/hoho.signed
new file mode 100644
index 00000000..6a6bc1ae
--- /dev/null
+++ b/tests/futility/data/hoho.signed
Binary files differ
diff --git a/tests/futility/data/hoho.unsigned b/tests/futility/data/hoho.unsigned
new file mode 100644
index 00000000..6a605d12
--- /dev/null
+++ b/tests/futility/data/hoho.unsigned
Binary files differ
diff --git a/tests/futility/data/minimuffin.pem b/tests/futility/data/minimuffin.pem
new file mode 100644
index 00000000..6912b1f4
--- /dev/null
+++ b/tests/futility/data/minimuffin.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpgIBAAKCAQEAvI3KBubudlZyX1oBNzWhQ+bNemHNVC5bO7TjJMTYTIJeKTaW
+IyHCFLF9ztpe8tT9Y+ga4VO9PEktP1WJpdU0ecG6VwK3951/cElosfnIPmIY7dVp
+PQtGtGS/Zih1nTMRV5hqtGa9SRg0i2kdph+quFXGQoMriTl0StmvNVtD51nRPGwc
+ZsG9+P0yfnyo7l3qXtKu5gBx/jDne2kl5/isPHkKfl9le+aNQaNjWeJyB4XDqHXM
+AjuW1G7FxoKlU4b363gQbE84Q80X1Qd9iAoRj/HLmDrx9h6FDFs+HbKDfCYtg2fK
+upEHr1bFUCxjc6AWuFglSq0yn5kyp1Bh5CD3PQIDAQABAoIBAQCNO2NlEhrx9sSK
+mX8pnHkjxECK97D16hcaPN6azvr5K/ldw25n+ERIfb4vb7AJEfKOn+9qce/ftSw4
+MVj+Jxm8TZjGzdmAlq87KzFJhkAwQghMNTszpzuZqZEX8xxN2E+YHilm4UHM+114
+Qw8bPMMCefUcIuY8ThXGbxFm1Oqi4YHHfqE6waRc0XXnLZOYr7NDCmhgyUG1dpAH
+kW0EYuJ1UNGaKE4LsWKvi0SYBQ48Mqh1XPkyiL/5I2whewsU2K4KEjynZp0+ULUG
+Dxfv3uCywsSsLuNR+EV5tTUp4eY1BLKULJQTMH3hyV1Xf1qEt2YN/3ZHsv7MPQzS
+sPIdN+LhAoGBAN6wbcxPnfkJROOVRUzE05IEEBalVULLo1cA1ss/7RjIeUvdRCAa
+12OxF4LSNzrSxcPCLsDnYq+j4HoS6KZ31c1TbaKcaUOPfRohrtGBZMxPgDTZgEBa
+JlsVtD2vzYvfUIpVQFz6Tnix3F29Gq5RaZdW5/qwOYyx0wtUrPf+pwZ5AoGBANjC
+MjgoGtcubR6chDhZcFU4vopdL7IEhMOZ1qxLFTQnINGXXDJpgVvdJRKdDV29DjNZ
+zF9wgmoiVm+uM/344bquUV7KHl5bEsZ+4KH6EA4y3IKVgxaxU7dpF6Q6L+rAuYp/
+j0N9XoVnS3aq30HkTkt+jQe0Hl6eEDOJqHEjolXlAoGBAJbMqs3cbIGkQT5May1d
+bFhI4Aw10dL1y5qzOsFQfOJ3f4xcPjHve0RLPDye1j/DU6EI8lg3WKDQPMbt3xY7
+uFDe2jNv7+iMVo9Hl/bPxM6GV69ySmNJqQetXu0XC/5YL1Y9/OP5rQIWj7/6uwKo
+pvSRKW6dv5sDIINfx/H4RGshAoGBAMIs7Tn7S1gaoev7QEMOdCAT7jUbF3/8pkZn
+SLUdqcgHiVHYquIKO7TknbJX+MJReygrOHcC3gFf81imkLLiQqyuPfyRSbUzFtW0
+kVzpG3rsuzdL4pvwjNNQFLqs2YIN1eipLtjBtWwCRcrvdYKcmDrvCj2tcEtIg7D3
+j2qTBni1AoGBAI58xPHxB0cNclhWiFHPNgk98GkwADWxfeTZduoyfpraSrpbseu8
+Cfgq1p5E2nM9jWx4jdKA/fxdD40bneupPi5w5SE2gmwtmQFR3TehI8gxNbEL2Gq6
+6ZkgxnGNxFaE6saHVDHKU8Q2bgzCI8JlOOtSjzKvbr+hsQMYHcEJxom6
+-----END RSA PRIVATE KEY-----
diff --git a/tests/futility/data/minimuffin.signed b/tests/futility/data/minimuffin.signed
new file mode 100644
index 00000000..7d60a743
--- /dev/null
+++ b/tests/futility/data/minimuffin.signed
Binary files differ
diff --git a/tests/futility/data/minimuffin.unsigned b/tests/futility/data/minimuffin.unsigned
new file mode 100644
index 00000000..e5c23018
--- /dev/null
+++ b/tests/futility/data/minimuffin.unsigned
Binary files differ
diff --git a/tests/futility/data/zinger.pem b/tests/futility/data/zinger.pem
new file mode 100644
index 00000000..6912b1f4
--- /dev/null
+++ b/tests/futility/data/zinger.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpgIBAAKCAQEAvI3KBubudlZyX1oBNzWhQ+bNemHNVC5bO7TjJMTYTIJeKTaW
+IyHCFLF9ztpe8tT9Y+ga4VO9PEktP1WJpdU0ecG6VwK3951/cElosfnIPmIY7dVp
+PQtGtGS/Zih1nTMRV5hqtGa9SRg0i2kdph+quFXGQoMriTl0StmvNVtD51nRPGwc
+ZsG9+P0yfnyo7l3qXtKu5gBx/jDne2kl5/isPHkKfl9le+aNQaNjWeJyB4XDqHXM
+AjuW1G7FxoKlU4b363gQbE84Q80X1Qd9iAoRj/HLmDrx9h6FDFs+HbKDfCYtg2fK
+upEHr1bFUCxjc6AWuFglSq0yn5kyp1Bh5CD3PQIDAQABAoIBAQCNO2NlEhrx9sSK
+mX8pnHkjxECK97D16hcaPN6azvr5K/ldw25n+ERIfb4vb7AJEfKOn+9qce/ftSw4
+MVj+Jxm8TZjGzdmAlq87KzFJhkAwQghMNTszpzuZqZEX8xxN2E+YHilm4UHM+114
+Qw8bPMMCefUcIuY8ThXGbxFm1Oqi4YHHfqE6waRc0XXnLZOYr7NDCmhgyUG1dpAH
+kW0EYuJ1UNGaKE4LsWKvi0SYBQ48Mqh1XPkyiL/5I2whewsU2K4KEjynZp0+ULUG
+Dxfv3uCywsSsLuNR+EV5tTUp4eY1BLKULJQTMH3hyV1Xf1qEt2YN/3ZHsv7MPQzS
+sPIdN+LhAoGBAN6wbcxPnfkJROOVRUzE05IEEBalVULLo1cA1ss/7RjIeUvdRCAa
+12OxF4LSNzrSxcPCLsDnYq+j4HoS6KZ31c1TbaKcaUOPfRohrtGBZMxPgDTZgEBa
+JlsVtD2vzYvfUIpVQFz6Tnix3F29Gq5RaZdW5/qwOYyx0wtUrPf+pwZ5AoGBANjC
+MjgoGtcubR6chDhZcFU4vopdL7IEhMOZ1qxLFTQnINGXXDJpgVvdJRKdDV29DjNZ
+zF9wgmoiVm+uM/344bquUV7KHl5bEsZ+4KH6EA4y3IKVgxaxU7dpF6Q6L+rAuYp/
+j0N9XoVnS3aq30HkTkt+jQe0Hl6eEDOJqHEjolXlAoGBAJbMqs3cbIGkQT5May1d
+bFhI4Aw10dL1y5qzOsFQfOJ3f4xcPjHve0RLPDye1j/DU6EI8lg3WKDQPMbt3xY7
+uFDe2jNv7+iMVo9Hl/bPxM6GV69ySmNJqQetXu0XC/5YL1Y9/OP5rQIWj7/6uwKo
+pvSRKW6dv5sDIINfx/H4RGshAoGBAMIs7Tn7S1gaoev7QEMOdCAT7jUbF3/8pkZn
+SLUdqcgHiVHYquIKO7TknbJX+MJReygrOHcC3gFf81imkLLiQqyuPfyRSbUzFtW0
+kVzpG3rsuzdL4pvwjNNQFLqs2YIN1eipLtjBtWwCRcrvdYKcmDrvCj2tcEtIg7D3
+j2qTBni1AoGBAI58xPHxB0cNclhWiFHPNgk98GkwADWxfeTZduoyfpraSrpbseu8
+Cfgq1p5E2nM9jWx4jdKA/fxdD40bneupPi5w5SE2gmwtmQFR3TehI8gxNbEL2Gq6
+6ZkgxnGNxFaE6saHVDHKU8Q2bgzCI8JlOOtSjzKvbr+hsQMYHcEJxom6
+-----END RSA PRIVATE KEY-----
diff --git a/tests/futility/data/zinger.signed b/tests/futility/data/zinger.signed
new file mode 100644
index 00000000..72d98a88
--- /dev/null
+++ b/tests/futility/data/zinger.signed
Binary files differ
diff --git a/tests/futility/data/zinger.unsigned b/tests/futility/data/zinger.unsigned
new file mode 100644
index 00000000..f36c37d0
--- /dev/null
+++ b/tests/futility/data/zinger.unsigned
Binary files differ
diff --git a/tests/futility/run_test_scripts.sh b/tests/futility/run_test_scripts.sh
index e8bbd232..8e6281a1 100755
--- a/tests/futility/run_test_scripts.sh
+++ b/tests/futility/run_test_scripts.sh
@@ -51,6 +51,7 @@ ${SCRIPTDIR}/test_sign_firmware.sh
${SCRIPTDIR}/test_sign_fw_main.sh
${SCRIPTDIR}/test_sign_kernel.sh
${SCRIPTDIR}/test_sign_keyblocks.sh
+${SCRIPTDIR}/test_sign_usbpd1.sh
${SCRIPTDIR}/test_file_types.sh
"
diff --git a/tests/futility/test_file_types.c b/tests/futility/test_file_types.c
index a2d03bf7..3fb21cc6 100644
--- a/tests/futility/test_file_types.c
+++ b/tests/futility/test_file_types.c
@@ -37,6 +37,7 @@ static struct {
{FILE_TYPE_VB2_PUBKEY, "tests/futility/data/sample.vbpubk2"},
{FILE_TYPE_VB2_PRIVKEY, "tests/futility/data/sample.vbprik2"},
{FILE_TYPE_PEM, "tests/testkeys/key_rsa2048.pem"},
+ {FILE_TYPE_USBPD1, },
};
BUILD_ASSERT(ARRAY_SIZE(test_case) == NUM_FILE_TYPES);
diff --git a/tests/futility/test_sign_usbpd1.sh b/tests/futility/test_sign_usbpd1.sh
new file mode 100755
index 00000000..dc3b6420
--- /dev/null
+++ b/tests/futility/test_sign_usbpd1.sh
@@ -0,0 +1,56 @@
+#!/bin/bash -eux
+# 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.
+
+me=${0##*/}
+TMP="$me.tmp"
+
+# Work in scratch directory
+cd "$OUTDIR"
+
+# The signed input images are signed with dev keys. We resign the unsigned
+# images with the same keypair, to make sure that we're producing identical
+# binaries.
+
+DATADIR="${SCRIPTDIR}/data"
+TESTS="dingdong hoho minimuffin zinger"
+
+set -o pipefail
+
+count=0
+for test in $TESTS; do
+
+ : $(( count++ ))
+ echo -n "$count " 1>&3
+
+ pemfile=${DATADIR}/${test}.pem
+ infile=${DATADIR}/${test}.unsigned
+ goodfile=${DATADIR}/${test}.signed
+ outfile=${TMP}.${test}.new
+
+ # Signing the whole thing with futility should produce identical results
+ ${FUTILITY} sign --type usbpd1 --pem ${pemfile} ${infile} ${outfile}
+ cmp ${goodfile} ${outfile}
+
+ # Now try signing just the RW part
+ size=$(stat -c '%s' ${infile})
+ half=$(( size / 2 ))
+
+ newin=${TMP}.${test}.rw_in
+ dd if=${infile} bs=${half} count=1 skip=1 of=${newin}
+ newgood=${TMP}.${test}.rw_ok
+ dd if=${goodfile} bs=${half} count=1 skip=1 of=${newgood}
+ newout=${TMP}.${test}.rw_out
+
+ # Sign the RW part alone
+ ${FUTILITY} sign --type usbpd1 --pem ${pemfile} \
+ --ro_size 0 \
+ ${newin} ${newout}
+ cmp ${newgood} ${newout}
+
+done
+
+# cleanup
+rm -rf ${TMP}*
+exit 0