diff options
-rw-r--r-- | futility/cmd_show.c | 14 | ||||
-rw-r--r-- | futility/cmd_sign.c | 100 | ||||
-rw-r--r-- | futility/file_type.inc | 6 | ||||
-rw-r--r-- | futility/file_type_rwsig.c | 274 | ||||
-rw-r--r-- | futility/file_type_usbpd1.c | 2 | ||||
-rw-r--r-- | futility/futility_options.h | 6 |
6 files changed, 223 insertions, 179 deletions
diff --git a/futility/cmd_show.c b/futility/cmd_show.c index 5d6c7c2b..8b33d1af 100644 --- a/futility/cmd_show.c +++ b/futility/cmd_show.c @@ -29,6 +29,8 @@ #include "util_misc.h" #include "vb1_helper.h" #include "vboot_common.h" +#include "2api.h" +#include "host_key2.h" /* Options */ struct show_option_s show_option = { @@ -395,6 +397,7 @@ int ft_show_kernel_preamble(const char *name, uint8_t *buf, uint32_t len, enum no_short_opts { OPT_PADDING = 1000, OPT_TYPE, + OPT_PUBKEY, OPT_HELP, }; @@ -415,8 +418,8 @@ static const char usage[] = "\n" " --type TYPE Override the detected file type\n" " Use \"--type help\" for a list\n" "Type-specific options:\n" - " -k|--publickey FILE" - " Use this public key for validation\n" + " -k|--publickey FILE.vbpubk Public key in vb1 format\n" + " --pubkey FILE.vpubk2 Public key in vb2 format\n" " -f|--fv FILE Verify this payload (FW_MAIN_A/B)\n" " --pad NUM Kernel vblock padding size\n" " --strict " @@ -441,6 +444,7 @@ static const struct option long_opts[] = { {"pad", 1, NULL, OPT_PADDING}, {"type", 1, NULL, OPT_TYPE}, {"strict", 0, &show_option.strict, 1}, + {"pubkey", 1, NULL, OPT_PUBKEY}, {"help", 0, NULL, OPT_HELP}, {NULL, 0, NULL, 0}, }; @@ -528,6 +532,12 @@ static int do_show(int argc, char *argv[]) } type_override = 1; break; + case OPT_PUBKEY: + if (vb2_packed_key_read(&show_option.pkey, optarg)) { + fprintf(stderr, "Error reading %s\n", optarg); + errorcnt++; + } + break; case OPT_HELP: print_help(argc, argv); return !!errorcnt; diff --git a/futility/cmd_sign.c b/futility/cmd_sign.c index e6184f43..c27f71e8 100644 --- a/futility/cmd_sign.c +++ b/futility/cmd_sign.c @@ -43,8 +43,7 @@ struct sign_option_s sign_option = { .rw_size = 0xffffffff, .ro_offset = 0xffffffff, .rw_offset = 0xffffffff, - .pkey_offset = 0xffffffff, - .sig_offset = 0xffffffff, + .sig_size = 1024, }; /* Helper to complain about invalid args. Returns num errors discovered */ @@ -424,7 +423,7 @@ static void print_help_usbpd1(int argc, char *argv[]) "\n" "This signs a %s.\n" "\n" - "The INPUT is assumed to consist of equal-sized RO and RW" + "The INFILE 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" @@ -464,7 +463,6 @@ 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" @@ -472,35 +470,30 @@ static void print_help_rwsig(int argc, char *argv[]) "\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" + "The INFILE is a binary blob of arbitrary size." + " It is signed using the\n" + "private key and the vb2_signature blob emitted.\n" "\n" - "Options:\n" - "\n" - " --prikey FILE.vbprik2" - " Private key in vb2 format\n" - " --pubkey FILE.vbpubk2" - " Public key in vb2 format\n" + "If no OUTFILE is specified, the INFILE should contain" + " an existing\n" + "vb2_signature blob near its end. The data_size from that" + " signature is\n" + "used to re-sign a portion of the INFILE, and the old" + " signature blob is\n" + "replaced.\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" + "Options:\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" + " --prikey FILE.vbprik2 " + "Private key in vb2 format (required)\n" + " --sig_size NUM " + "Offset from the end of INFILE where the\n" + " " + "signature blob should be located\n" + " " + "(default 1024 bytes)\n" + " --data_size NUM " + "Number of bytes of INFILE to sign\n" "\n", argv[0], futil_file_type_name(FILE_TYPE_RWSIG), @@ -529,7 +522,7 @@ static const char usage_default[] = "\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" + " RW device image same, or signed in-place\n" "\n" "For more information, use \"" MYNAME " help %s TYPE\", where\n" "TYPE is one of:\n\n"; @@ -575,10 +568,9 @@ enum no_short_opts { OPT_RW_SIZE, OPT_RO_OFFSET, OPT_RW_OFFSET, - OPT_PKEY_OFFSET, - OPT_SIG_OFFSET, + OPT_DATA_SIZE, + OPT_SIG_SIZE, OPT_PRIKEY, - OPT_PUBKEY, OPT_HELP, }; @@ -614,11 +606,10 @@ 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}, + {"data_size", 1, NULL, OPT_DATA_SIZE}, + {"sig_size", 1, NULL, OPT_SIG_SIZE}, {"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}, }; @@ -646,7 +637,6 @@ static int do_sign(int argc, char *argv[]) uint8_t *buf; uint32_t buf_len; char *e = 0; - int inout_file_count = 0; int mapping; int helpind = 0; int longindex; @@ -715,11 +705,11 @@ static int do_sign(int argc, char *argv[]) sign_option.fv_specified = 1; /* fallthrough */ case OPT_INFILE: - inout_file_count++; + sign_option.inout_file_count++; infile = optarg; break; case OPT_OUTFILE: - inout_file_count++; + sign_option.inout_file_count++; sign_option.outfile = optarg; break; case OPT_BOOTLOADER: @@ -785,13 +775,13 @@ 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); + case OPT_DATA_SIZE: + errorcnt += parse_number_opt(optarg, "data_size", + &sign_option.data_size); break; - case OPT_SIG_OFFSET: - errorcnt += parse_number_opt(optarg, "sig_offset", - &sign_option.sig_offset); + case OPT_SIG_SIZE: + errorcnt += parse_number_opt(optarg, "sig_size", + &sign_option.sig_size); break; case OPT_PEM_SIGNPRIV: sign_option.pem_signpriv = optarg; @@ -834,12 +824,6 @@ static int do_sign(int argc, char *argv[]) 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; @@ -882,14 +866,14 @@ static int do_sign(int argc, char *argv[]) fprintf(stderr, "ERROR: missing input filename\n"); goto done; } else { - inout_file_count++; + sign_option.inout_file_count++; infile = argv[optind++]; } } /* Look for an output file if we don't have one, just in case. */ if (!sign_option.outfile && argc - optind > 0) { - inout_file_count++; + sign_option.inout_file_count++; sign_option.outfile = argv[optind++]; } @@ -945,7 +929,7 @@ static int do_sign(int argc, char *argv[]) break; case FILE_TYPE_KERN_PREAMBLE: errorcnt += no_opt_if(!sign_option.signprivate, "signprivate"); - if (sign_option.vblockonly || inout_file_count > 1) + if (sign_option.vblockonly || sign_option.inout_file_count > 1) sign_option.create_new_outfile = 1; break; case FILE_TYPE_RAW_FIRMWARE: @@ -982,7 +966,7 @@ static int do_sign(int argc, char *argv[]) } Debug("infile=%s\n", infile); - Debug("inout_file_count=%d\n", inout_file_count); + Debug("sign_option.inout_file_count=%d\n", sign_option.inout_file_count); Debug("sign_option.create_new_outfile=%d\n", sign_option.create_new_outfile); @@ -1021,7 +1005,7 @@ 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) + if (sign_option.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; @@ -1042,7 +1026,7 @@ static int do_sign(int argc, char *argv[]) errorcnt += futil_file_type_sign(sign_option.type, infile, buf, buf_len); - errorcnt += futil_unmap_file(ifd, MAP_RW, buf, buf_len); + errorcnt += futil_unmap_file(ifd, mapping, buf, buf_len); done: if (ifd >= 0 && close(ifd)) { diff --git a/futility/file_type.inc b/futility/file_type.inc index fd12286f..07db5011 100644 --- a/futility/file_type.inc +++ b/futility/file_type.inc @@ -71,9 +71,9 @@ FILE_TYPE(CHROMIUMOS_DISK, "disk_img", "chromiumos disk image", NONE, NONE, NONE) -FILE_TYPE(RWSIG, "rwsig", "RO+RW firmware image", - NONE, - NONE, +FILE_TYPE(RWSIG, "rwsig", "RW device image", + R_(ft_recognize_rwsig), + S_(ft_show_rwsig), S_(ft_sign_rwsig)) /* Firmware for USB Type-C power adapters */ FILE_TYPE(USBPD1, "usbpd1", "USB-PD charger image (v1.0)", diff --git a/futility/file_type_rwsig.c b/futility/file_type_rwsig.c index 6b757fff..e50ff32f 100644 --- a/futility/file_type_rwsig.c +++ b/futility/file_type_rwsig.c @@ -30,112 +30,160 @@ #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) +static inline void vb2_print_bytes(const void *ptr, uint32_t len) { - 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; + const uint8_t *buf = (const uint8_t *)ptr; + int i; + for (i = 0; i < len; i++) + printf("%02x", *buf++); } -/* 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) +static void show_sig(const char *name, const struct vb2_signature *sig) { - 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; + const struct vb2_text_vs_enum *entry; + printf("Signature: %s\n", name); + printf(" Vboot API: 2.1\n"); + printf(" Desc: \"%s\"\n", vb2_common_desc(sig)); + entry = vb2_lookup_by_num(vb2_text_vs_sig, sig->sig_alg); + printf(" Signature Algorithm: %d %s\n", sig->sig_alg, + entry ? entry->name : "(invalid)"); + entry = vb2_lookup_by_num(vb2_text_vs_hash, sig->hash_alg); + printf(" Hash Algorithm: %d %s\n", sig->hash_alg, + entry ? entry->name : "(invalid)"); + printf(" Total size: 0x%x (%d)\n", sig->c.total_size, + sig->c.total_size); + printf(" ID: "); + vb2_print_bytes(&sig->id, sizeof(sig->id)); + printf("\n"); + printf(" Data size: 0x%x (%d)\n", sig->data_size, + sig->data_size); } -/* 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) +int ft_show_rwsig(const char *name, uint8_t *buf, uint32_t len, void *nuthin) { - 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; + const struct vb2_signature *sig = 0; + struct vb2_public_key key; + uint8_t workbuf[VB2_VERIFY_DATA_WORKBUF_BYTES] + __attribute__ ((aligned (VB2_WORKBUF_ALIGN))); + struct vb2_workbuf wb; + uint32_t data_size, sig_size = SIGNATURE_RSVD_SIZE; + uint8_t *data; + + Debug("%s(): name %s\n", __func__, name); + Debug("%s(): len 0x%08x (%d)\n", __func__, len, len); + + /* Am I just looking at a signature file? */ + Debug("Looking for signature at 0x0\n"); + sig = (const struct vb2_signature *)buf; + if (VB2_SUCCESS == vb2_verify_signature(sig, len)) { + show_sig(name, sig); + if (!show_option.fv) { + printf("No data available to verify\n"); + return show_option.strict; + } + data = show_option.fv; + data_size = show_option.fv_size; + } else { + /* Where would it be? */ + if (show_option.sig_size) + sig_size = show_option.sig_size; + + Debug("Looking for signature at 0x%x\n", len - sig_size); + + if (len < sig_size) { + Debug("File is too small\n"); + return 1; + } + + sig = (const struct vb2_signature *)(buf + len - sig_size); + if (VB2_SUCCESS == vb2_verify_signature(sig, sig_size)) { + show_sig(name, sig); + data = buf; + data_size = sig->data_size; + } else { + return 1; + } + } + + if (!show_option.pkey) { + printf("No public key available to verify with\n"); + return show_option.strict; } - *rw_offset_ptr = rw_offset; - *rw_size_ptr = rw_size; - *pkey_offset_ptr = pkey_offset; - *sig_offset_ptr = sig_offset; + /* We already did this once, so it should work again */ + if (vb2_unpack_key(&key, + (const uint8_t *)show_option.pkey, + show_option.pkey->c.total_size)) { + Debug("Can't unpack pubkey\n"); + return 1; + } - return 1; + /* The sig is destroyed by the verify operation, so make a copy */ + { + uint8_t sigbuf[sig->c.total_size]; + memcpy(sigbuf, sig, sizeof(sigbuf)); + + vb2_workbuf_init(&wb, workbuf, sizeof(workbuf)); + + if (vb2_verify_data(data, data_size, + (struct vb2_signature *)sigbuf, + (const struct vb2_public_key *)&key, + &wb)) { + printf("Signature verification failed\n"); + return 1; + } + } + + printf("Signature verification succeeded.\n"); + return 0; } int ft_sign_rwsig(const char *name, uint8_t *buf, uint32_t len, void *data) { struct vb2_signature *sig = 0; + uint32_t r, data_size = len, sig_size = SIGNATURE_RSVD_SIZE; 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; + /* If we don't have a distinct OUTFILE, look for an existing sig */ + if (sign_option.inout_file_count < 2) { + const struct vb2_signature *old_sig; + + /* Where would it be? */ + if (sign_option.sig_size) + sig_size = sign_option.sig_size; + + Debug("Looking for old signature at 0x%x\n", len - sig_size); + + if (len < sig_size) { + fprintf(stderr, "File is too small\n"); + return 1; + } + + /* Take a look */ + old_sig = (const struct vb2_signature *)(buf + len - sig_size); + if (vb2_verify_signature(old_sig, sig_size)) { + fprintf(stderr, "Can't find a valid signature\n"); + return 1; + } + + /* Use the same exent again */ + data_size = old_sig->data_size; + + Debug("Found sig: data_size is 0x%x (%d)\n", data_size, + data_size); + } + + /* Unless overridden */ + if (sign_option.data_size) + data_size = sign_option.data_size; /* Sign the blob */ - r = vb2_sign_data(&sig, buf + rw_offset, rw_size, - sign_option.prikey, 0); + r = vb2_sign_data(&sig, buf, data_size, sign_option.prikey, 0); if (r) { fprintf(stderr, "Unable to sign data (error 0x%08x, if that helps)\n", @@ -143,43 +191,45 @@ int ft_sign_rwsig(const char *name, uint8_t *buf, uint32_t len, void *data) 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; + if (sign_option.inout_file_count < 2) { + /* Overwrite the old signature */ + if (sig->c.total_size > sig_size) { + fprintf(stderr, "New sig is too large (%d > %d)\n", + sig->c.total_size, sig_size); + goto done; + } + memset(buf + len - sig_size, 0xff, sig_size); + memcpy(buf + len - sig_size, sig, sig->c.total_size); + } else { + /* Write the signature to a new file */ + r = vb2_write_object(sign_option.outfile, sig); + if (r) { + fprintf(stderr, "Unable to write sig" + " (error 0x%08x, if that helps)\n", r); + 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 (sig) + free(sig); if (sign_option.prikey) vb2_private_key_free(sign_option.prikey); - if (sign_option.pkey) - free(sign_option.pkey); return retval; } + +enum futil_file_type ft_recognize_rwsig(uint8_t *buf, uint32_t len) +{ + if (!vb2_verify_signature((const struct vb2_signature *)buf, len)) + return FILE_TYPE_RWSIG; + + if (!vb2_verify_signature((const struct vb2_signature *) + (buf + len - SIGNATURE_RSVD_SIZE), + SIGNATURE_RSVD_SIZE)) + return FILE_TYPE_RWSIG; + + return FILE_TYPE_UNKNOWN; +} diff --git a/futility/file_type_usbpd1.c b/futility/file_type_usbpd1.c index 36cb5cf2..c0f5397e 100644 --- a/futility/file_type_usbpd1.c +++ b/futility/file_type_usbpd1.c @@ -465,8 +465,6 @@ 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; - Debug("%s(): len 0x%08x (%d)\n", __func__, len, len); - /* * 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 diff --git a/futility/futility_options.h b/futility/futility_options.h index e02ef2f6..3a6aecb6 100644 --- a/futility/futility_options.h +++ b/futility/futility_options.h @@ -27,6 +27,8 @@ struct show_option_s { int strict; int t_flag; enum futil_file_type type; + struct vb2_packed_key *pkey; + uint32_t sig_size; }; extern struct show_option_s show_option; @@ -53,6 +55,7 @@ struct sign_option_s { int vblockonly; char *outfile; int create_new_outfile; + int inout_file_count; char *pem_signpriv; int pem_algo_specified; uint32_t pem_algo; @@ -61,9 +64,8 @@ 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; + uint32_t data_size, sig_size; struct vb2_private_key *prikey; - struct vb2_packed_key *pkey; }; extern struct sign_option_s sign_option; |