From 8f754f0a7dee101724eb8dcf641e54cb3c2a459c Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Tue, 23 Sep 2014 22:03:56 -0700 Subject: futility: Add support for [re]signing kernel partitions BUG=none BRANCH=ToT TEST=make runtests This also modifies the tests to compare the futility sign command results against the vbutil_kernel results. Signed-off-by: Bill Richardson Change-Id: Ibc659f134cc83982e3f0c0bcc108cc0eddbe228e Reviewed-on: https://chromium-review.googlesource.com/219730 Reviewed-by: Randall Spangler Reviewed-on: https://chromium-review.googlesource.com/227882 Tested-by: Daisuke Nojiri Reviewed-by: Daisuke Nojiri Commit-Queue: Daisuke Nojiri --- Makefile | 1 - futility/cmd_show.c | 159 ++++-- futility/cmd_sign.c | 561 +++++++++++++++++---- futility/cmd_vbutil_kernel0.c | 974 ------------------------------------- futility/traversal.c | 24 +- futility/traversal.h | 9 +- tests/futility/run_test_scripts.sh | 1 + tests/futility/test_show_kernel.sh | 55 +++ tests/futility/test_sign_kernel.sh | 98 ++-- 9 files changed, 741 insertions(+), 1141 deletions(-) delete mode 100644 futility/cmd_vbutil_kernel0.c create mode 100755 tests/futility/test_show_kernel.sh diff --git a/Makefile b/Makefile index d5f00885..b26e2ab3 100644 --- a/Makefile +++ b/Makefile @@ -545,7 +545,6 @@ FUTIL_SRCS = \ futility/cmd_sign.c \ futility/cmd_vbutil_firmware.c \ futility/cmd_vbutil_kernel.c \ - futility/cmd_vbutil_kernel0.c \ futility/cmd_vbutil_key.c \ futility/cmd_vbutil_keyblock.c \ futility/cmd_verify_kernel.c \ diff --git a/futility/cmd_show.c b/futility/cmd_show.c index 9fc0a328..b8f06014 100644 --- a/futility/cmd_show.c +++ b/futility/cmd_show.c @@ -23,6 +23,7 @@ #include "host_common.h" #include "traversal.h" #include "util_misc.h" +#include "vb1_helper.h" #include "vboot_common.h" /* Local values for cb_area_s._flags */ @@ -33,9 +34,13 @@ enum callback_flags { /* Local structure for args, etc. */ static struct local_data_s { VbPublicKey *k; - uint8_t *f; - uint64_t f_size; -} option; + uint8_t *fv; + uint64_t fv_size; + uint32_t padding; + int strict; +} option = { + .padding = 65536, +}; static void show_key(VbPublicKey *pubkey, const char *sp) { @@ -170,8 +175,6 @@ int futil_cb_show_gbb(struct futil_traverse_state_s *state) bmp = (BmpBlockHeader *)(buf + gbb->bmpfv_offset); if (0 != memcmp(bmp, BMPBLOCK_SIGNATURE, BMPBLOCK_SIGNATURE_SIZE)) { printf(" BmpBlock: \n"); - /* We don't support old formats, so it's not always an error */ - /* TODO: Add a --strict option to make this fatal? */ } else { printf(" BmpBlock:\n"); printf(" Version: %d.%d\n", @@ -236,9 +239,9 @@ int futil_cb_show_fw_preamble(struct futil_traverse_state_s *state) { VbKeyBlockHeader *key_block = (VbKeyBlockHeader *)state->my_area->buf; uint32_t len = state->my_area->len; - VbPublicKey *sign_key = 0; - uint8_t *fv_data = 0; - uint64_t fv_size = 0; + VbPublicKey *sign_key = option.k; + uint8_t *fv_data = option.fv; + uint64_t fv_size = option.fv_size; struct cb_area_s *fw_body_area = 0; int good_sig = 0; @@ -250,24 +253,21 @@ int futil_cb_show_fw_preamble(struct futil_traverse_state_s *state) switch (state->component) { case CB_FMAP_VBLOCK_A: - /* BIOS should have a rootkey in the GBB */ - if (state->rootkey._flags & AREA_IS_VALID) + if (!sign_key && (state->rootkey._flags & AREA_IS_VALID)) + /* BIOS should have a rootkey in the GBB */ sign_key = (VbPublicKey *)state->rootkey.buf; /* And we should have already seen the firmware body */ fw_body_area = &state->cb_area[CB_FMAP_FW_MAIN_A]; break; case CB_FMAP_VBLOCK_B: - /* BIOS should have a rootkey in the GBB */ - if (state->rootkey._flags & AREA_IS_VALID) + if (!sign_key && (state->rootkey._flags & AREA_IS_VALID)) + /* BIOS should have a rootkey in the GBB */ sign_key = (VbPublicKey *)state->rootkey.buf; /* And we should have already seen the firmware body */ fw_body_area = &state->cb_area[CB_FMAP_FW_MAIN_B]; break; case CB_FW_PREAMBLE: - /* We'll have to get a signature and body from elsewhere */ - sign_key = option.k; - fv_data = option.f; - fv_size = option.f_size; + /* We have to provide a signature and body in the options. */ break; default: DIE; @@ -335,7 +335,6 @@ int futil_cb_show_fw_preamble(struct futil_traverse_state_s *state) if (!fv_data) { printf("No firmware body available to verify.\n"); - /* TODO: Add a --strict option to make this fatal? */ return 0; } @@ -355,12 +354,98 @@ done: state->my_area->_flags |= AREA_IS_VALID; } else { printf("Seems legit, but the signature is unverified.\n"); - /* TODO: Add a --strict option to make this fatal? */ } return 0; } +int futil_cb_show_kernel_preamble(struct futil_traverse_state_s *state) +{ + + VbKeyBlockHeader *key_block = (VbKeyBlockHeader *)state->my_area->buf; + uint32_t len = state->my_area->len; + VbPublicKey *sign_key = option.k; + uint8_t *kernel_blob = 0; + uint64_t kernel_size; + int good_sig = 0; + + /* Check the hash... */ + if (VBOOT_SUCCESS != KeyBlockVerify(key_block, len, NULL, 1)) { + printf("%s keyblock component is invalid\n", state->name); + return 1; + } + + /* If we have a key, check the signature too */ + if (sign_key && VBOOT_SUCCESS == + KeyBlockVerify(key_block, len, sign_key, 0)) + good_sig = 1; + + printf("Kernel partition: %s\n", state->in_filename); + show_keyblock(key_block, NULL, !!sign_key, good_sig); + + RSAPublicKey *rsa = PublicKeyToRSA(&key_block->data_key); + if (!rsa) { + fprintf(stderr, "Error parsing data key in %s\n", state->name); + return 1; + } + uint32_t more = key_block->key_block_size; + VbKernelPreambleHeader *preamble = + (VbKernelPreambleHeader *)(state->my_area->buf + more); + + if (VBOOT_SUCCESS != VerifyKernelPreamble(preamble, + len - more, rsa)) { + printf("%s is invalid\n", state->name); + return 1; + } + + printf("Kernel Preamble:\n"); + printf(" Size: 0x%" PRIx64 "\n", + preamble->preamble_size); + printf(" Header version: %" PRIu32 ".%" PRIu32 "\n", + preamble->header_version_major, + preamble->header_version_minor); + printf(" Kernel version: %" PRIu64 "\n", + preamble->kernel_version); + printf(" Body load address: 0x%" PRIx64 "\n", + preamble->body_load_address); + printf(" Body size: 0x%" PRIx64 "\n", + preamble->body_signature.data_size); + printf(" Bootloader address: 0x%" PRIx64 "\n", + preamble->bootloader_address); + printf(" Bootloader size: 0x%" PRIx64 "\n", + preamble->bootloader_size); + + + /* Verify kernel body */ + if (option.fv) { + /* It's in a separate file, which we've already read in */ + kernel_blob = option.fv; + kernel_size = option.fv_size; + } else { + /* It should be at an offset within the input file. */ + kernel_blob = state->my_area->buf + option.padding; + kernel_size = state->my_area->len - option.padding; + } + + if (!kernel_blob) { + /* TODO: Is this always a failure? The preamble is okay. */ + fprintf(stderr, "No kernel blob available to verify.\n"); + return 1; + } + + if (0 != VerifyData(kernel_blob, kernel_size, + &preamble->body_signature, rsa)) { + fprintf(stderr, "Error verifying kernel body.\n"); + return 1; + } + + printf("Body verification succeeded.\n"); + + printf("Config:\n%s\n", kernel_blob + KernelCmdLineOffset(preamble)); + + return 0; +} + int futil_cb_show_begin(struct futil_traverse_state_s *state) { switch (state->in_type) { @@ -380,21 +465,25 @@ int futil_cb_show_begin(struct futil_traverse_state_s *state) return 0; } +enum no_short_opts { + OPT_PADDING = 1000, +}; + static const char usage[] = "\n" "Usage: " MYNAME " %s [OPTIONS] FILE\n" "\n" "Where FILE could be a\n" "\n" - " public key (.vbpubk)\n" " keyblock (.keyblock)\n" " firmware preamble signature (VBLOCK_A/B)\n" " firmware image (bios.bin)\n" " kernel partition (/dev/sda2, /dev/mmcblk0p2)\n" "\n" "Options:\n" - " -k|--publickey FILE Use this public key for validation\n" - " -f|--fv FILE|OFFSET Verify this payload (FW_MAIN_A/B, or\n" - " kernel vblock padding size)\n" + " -k|--publickey FILE" + " Use this public key for validation\n" + " -f|--fv FILE Verify this payload (FW_MAIN_A/B)\n" + " --pad NUM Kernel vblock padding size\n" "\n"; static void print_help(const char *prog) @@ -404,8 +493,9 @@ static void print_help(const char *prog) static const struct option long_opts[] = { /* name hasarg *flag val */ - {"publickey", 1, 0, 'k'}, - {"fv", 1, 0, 'f'}, + {"publickey", 1, 0, 'k'}, + {"fv", 1, 0, 'f'}, + {"pad", 1, NULL, OPT_PADDING}, {"debug", 0, &debugging_enabled, 1}, {NULL, 0, NULL, 0}, }; @@ -419,14 +509,16 @@ static int do_show(int argc, char *argv[]) struct futil_traverse_state_s state; uint8_t *buf; uint32_t buf_len; + char *e = 0; opterr = 0; /* quiet, you */ while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) { switch (i) { case 'f': - option.f = ReadFile(optarg, &option.f_size); - if (!option.f) { - fprintf(stderr, "Error reading %s\n", optarg); + option.fv = ReadFile(optarg, &option.fv_size); + if (!option.fv) { + fprintf(stderr, "Error reading %s: %s\n", + optarg, strerror(errno)); errorcnt++; } break; @@ -437,6 +529,14 @@ static int do_show(int argc, char *argv[]) errorcnt++; } break; + case OPT_PADDING: + option.padding = strtoul(optarg, &e, 0); + if (!*optarg || (e && *e)) { + fprintf(stderr, + "Invalid --padding \"%s\"\n", optarg); + errorcnt++; + } + break; case '?': if (optopt) @@ -490,6 +590,7 @@ static int do_show(int argc, char *argv[]) errorcnt += futil_traverse(buf, buf_len, &state, FILE_TYPE_UNKNOWN); + errorcnt += futil_unmap_file(ifd, MAP_RO, buf, buf_len); boo: @@ -502,8 +603,8 @@ boo: if (option.k) free(option.k); - if (option.f) - free(option.f); + if (option.fv) + free(option.fv); return !!errorcnt; } diff --git a/futility/cmd_sign.c b/futility/cmd_sign.c index dd28aec4..f4c682ee 100644 --- a/futility/cmd_sign.c +++ b/futility/cmd_sign.c @@ -22,8 +22,10 @@ #include "futility.h" #include "gbb_header.h" #include "host_common.h" +#include "kernel_blob.h" #include "traversal.h" #include "util_misc.h" +#include "vb1_helper.h" #include "vboot_common.h" /* Local values for cb_area_s._flags */ @@ -32,37 +34,55 @@ enum callback_flags { }; /* Local structure for args, etc. */ -struct local_data_s { +static struct local_data_s { VbPrivateKey *signprivate; VbKeyBlockHeader *keyblock; VbPublicKey *kernel_subkey; VbPrivateKey *devsignprivate; VbKeyBlockHeader *devkeyblock; uint32_t version; + int version_specified; uint32_t flags; int flags_specified; char *loemdir; char *loemid; + uint8_t *bootloader_data; + uint64_t bootloader_size; + uint8_t *config_data; + uint64_t config_size; + enum arch_t arch; + uint32_t kloadaddr; + uint32_t padding; + int vblockonly; + char *outfile; + int create_new_outfile; } option = { .version = 1, + .arch = ARCH_UNSPECIFIED, + .kloadaddr = CROS_32BIT_ENTRY_ADDR, + .padding = 65536, }; -int futil_cb_sign_bogus(struct futil_traverse_state_s *state) +/* Helper to complain about invalid args. Returns num errors discovered */ +static int no_opt_if(int expr, const char *optname) { - fprintf(stderr, "Don't know how to sign %s\n", state->name); - return 1; + if (expr) { + fprintf(stderr, "Missing --%s option\n", optname); + return 1; + } + return 0; } -int futil_cb_sign_notyet(struct futil_traverse_state_s *state) +/* This wraps/signs a public key, producing a keyblock. */ +int futil_cb_sign_pubkey(struct futil_traverse_state_s *state) { - fprintf(stderr, "Signing %s is not yet implemented\n", state->name); + fprintf(stderr, "Don't know how to sign %s yet\n", state->name); return 1; } /* * This handles FW_MAIN_A and FW_MAIN_B while processing a BIOS image. - * * The data in state->my_area is just the RW firmware blob, so there's nothing * useful to show about it. We'll just mark it as present so when we encounter * corresponding VBLOCK area, we'll have this to verify. @@ -78,15 +98,11 @@ int futil_cb_sign_fw_main(struct futil_traverse_state_s *state) * We don't do any signing here. We just check to see if the VBLOCK * area contains a firmware preamble. */ -int futil_cb_sign_fw_preamble(struct futil_traverse_state_s *state) +int futil_cb_sign_fw_vblock(struct futil_traverse_state_s *state) { VbKeyBlockHeader *key_block = (VbKeyBlockHeader *)state->my_area->buf; uint32_t len = state->my_area->len; - /* We don't (yet) handle standalone VBLOCKs */ - if (state->component == CB_FW_PREAMBLE) - return futil_cb_sign_notyet(state); - /* * If we have a valid keyblock and fw_preamble, then we can use them to * determine the size of the firmware body. Otherwise, we'll have to @@ -142,6 +158,140 @@ whatever: return 0; } +int futil_cb_create_kernel_part(struct futil_traverse_state_s *state) +{ + uint8_t *vmlinuz_data, *kblob_data, *vblock_data; + uint64_t vmlinuz_size, kblob_size, vblock_size; + int rv; + + vmlinuz_data = state->my_area->buf; + vmlinuz_size = state->my_area->len; + + kblob_data = CreateKernelBlob( + vmlinuz_data, vmlinuz_size, + option.arch, option.kloadaddr, + option.config_data, option.config_size, + option.bootloader_data, option.bootloader_size, + &kblob_size); + if (!kblob_data) { + fprintf(stderr, "Unable to create kernel blob\n"); + return 1; + } + Debug("kblob_size = 0x%" PRIx64 "\n", kblob_size); + + vblock_data = SignKernelBlob(kblob_data, kblob_size, option.padding, + option.version, option.kloadaddr, + option.keyblock, option.signprivate, + &vblock_size); + if (!vblock_data) { + fprintf(stderr, "Unable to sign kernel blob\n"); + free(kblob_data); + return 1; + } + Debug("vblock_size = 0x%" PRIx64 "\n", vblock_size); + + /* We should be creating a completely new output file. + * If not, something's wrong. */ + if (!option.create_new_outfile) + DIE; + + if (option.vblockonly) + rv = WriteSomeParts(option.outfile, + vblock_data, vblock_size, + NULL, 0); + else + rv = WriteSomeParts(option.outfile, + vblock_data, vblock_size, + kblob_data, kblob_size); + + free(vblock_data); + free(kblob_data); + return rv; +} + +int futil_cb_resign_kernel_part(struct futil_traverse_state_s *state) +{ + uint8_t *kpart_data, *kblob_data, *vblock_data; + uint64_t kpart_size, kblob_size, vblock_size; + VbKeyBlockHeader *keyblock = NULL; + VbKernelPreambleHeader *preamble = NULL; + int rv = 0; + + kpart_data = state->my_area->buf; + kpart_size = state->my_area->len; + + /* Note: This just sets some static pointers. It doesn't malloc. */ + kblob_data = UnpackKPart(kpart_data, kpart_size, option.padding, + &keyblock, &preamble, &kblob_size); + + if (!kblob_data) { + fprintf(stderr, "Unable to unpack kernel partition\n"); + return 1; + } + + /* + * We don't let --kloadaddr change when resigning, because the original + * vbutil_kernel program didn't do it right. Since obviously no one + * ever noticed, we'll maintain bug-compatibility by just not allowing + * it here either. To enable it, we'd need to update the zeropage + * table's cmd_line_ptr as well as the preamble. + */ + option.kloadaddr = preamble->body_load_address; + + /* Replace the config if asked */ + if (option.config_data && + 0 != UpdateKernelBlobConfig(kblob_data, kblob_size, + option.config_data, + option.config_size)) { + fprintf(stderr, "Unable to update config\n"); + return 1; + } + + /* Preserve the version unless a new one is given */ + if (!option.version_specified) + option.version = preamble->kernel_version; + + /* Replace the keyblock if asked */ + if (option.keyblock) + keyblock = option.keyblock; + + /* Compute the new signature */ + vblock_data = SignKernelBlob(kblob_data, kblob_size, option.padding, + option.version, option.kloadaddr, + keyblock, option.signprivate, + &vblock_size); + if (!vblock_data) { + fprintf(stderr, "Unable to sign kernel blob\n"); + return 1; + } + Debug("vblock_size = 0x%" PRIx64 "\n", vblock_size); + + if (option.vblockonly) { + /* If we're only writing the vblock, then we should be doing it + * into a new file. */ + rv = WriteSomeParts(option.outfile, + vblock_data, vblock_size, + NULL, 0); + } else { + /* If we're writing the whole thing, then the output is + * the same size (and possibly the same file) as the input. + * Either way, it's mmap'ed so modifications to the buffer + * will get flushed to disk when we close the file. */ + Memcpy(kpart_data, vblock_data, vblock_size); + } + + free(vblock_data); + return rv; +} + + +int futil_cb_sign_raw_firmware(struct futil_traverse_state_s *state) +{ + fprintf(stderr, "Don't know how to sign %s yet\n", state->name); + return 1; +} + + int futil_cb_sign_begin(struct futil_traverse_state_s *state) { if (state->in_type == FILE_TYPE_UNKNOWN) { @@ -224,7 +374,8 @@ static int write_loem(const char *ab, struct cb_area_s *vblock) return 0; } -int futil_cb_sign_end(struct futil_traverse_state_s *state) +/* This signs a full BIOS image after it's been traversed. */ +static int sign_bios_at_end(struct futil_traverse_state_s *state) { struct cb_area_s *vblock_a = &state->cb_area[CB_FMAP_VBLOCK_A]; struct cb_area_s *vblock_b = &state->cb_area[CB_FMAP_VBLOCK_B]; @@ -275,23 +426,48 @@ int futil_cb_sign_end(struct futil_traverse_state_s *state) return retval; } +int futil_cb_sign_end(struct futil_traverse_state_s *state) +{ + switch (state->in_type) { + case FILE_TYPE_BIOS_IMAGE: + case FILE_TYPE_OLD_BIOS_IMAGE: + return sign_bios_at_end(state); + + default: + /* Any other cleanup needed? */ + break; + } + + return state->errors; +} + static const char usage[] = "\n" - "Usage: " MYNAME " %s [OPTIONS] FILE [OUTFILE]\n" + "Usage: " MYNAME " %s [PARAMS] INFILE [OUTFILE]\n" "\n" - "[Re]Sign the specified BIOS image\n" + "Where INFILE is a\n" + "\n" + " complete firmware image (bios.bin)\n" + " raw linux kernel; OUTFILE is a kernel partition image\n" + " kernel partition image (/dev/sda2, /dev/mmcblk0p2)\n"; + +static const char usage_bios[] = "\n" + "-----------------------------------------------------------------\n" + "To sign a complete firmware image (bios.bin):\n" "\n" - "Required OPTIONS:\n" + "Required PARAMS:\n" " -s|--signprivate FILE.vbprivk The private firmware data key\n" " -b|--keyblock FILE.keyblock The keyblock containing the\n" " public firmware data key\n" " -k|--kernelkey FILE.vbpubk The public kernel subkey\n" + " [--infile] INFILE Input firmware image (modified\n" + " in place if no OUTFILE given)\n" "\n" "These are required if the A and B firmware differ:\n" " -S|--devsign FILE.vbprivk The DEV private firmware data key\n" " -B|--devkeyblock FILE.keyblock The keyblock containing the\n" " DEV public firmware data key\n" "\n" - "Optional OPTIONS:\n" + "Optional PARAMS:\n" " -v|--version NUM The firmware version number" " (default %d)\n" " -f|--flags NUM The preamble flags value" @@ -299,39 +475,115 @@ static const char usage[] = "\n" " unchanged, or 0 if unknown)\n" " -d|--loemdir DIR Local OEM output vblock directory\n" " -l|--loemid STRING Local OEM vblock suffix\n" + " [--outfile] OUTFILE Output firmware image\n"; + +static const char usage_new_kpart[] = "\n" + "-----------------------------------------------------------------\n" + "To create a new kernel parition image (/dev/sda2, /dev/mmcblk0p2):\n" + "\n" + "Required PARAMS:\n" + " -s|--signprivate FILE.vbprivk" + " The private key to sign the kernel blob\n" + " -b|--keyblock FILE.keyblock The keyblock containing the public\n" + " key to verify the kernel blob\n" + " -v|--version NUM The kernel version number\n" + " --bootloader FILE Bootloader stub\n" + " --config FILE The kernel commandline file\n" + " --arch ARCH The CPU architecture (one of\n" + " x86|amd64, arm|aarch64, mips)\n" + " [--vmlinuz] INFILE Linux kernel bzImage file\n" + " [--outfile] OUTFILE Output kernel partition or vblock\n" + "\n" + "Optional PARAMS:\n" + " --kloadaddr NUM" + " RAM address to load the kernel body\n" + " (default 0x%x)\n" + " --pad NUM The vblock padding size in bytes\n" + " (default 0x%x)\n" + " --vblockonly Emit just the vblock (requires a\n" + " distinct outfile)\n"; + +static const char usage_old_kpart[] = "\n" + "-----------------------------------------------------------------\n" + "To resign an existing kernel parition (/dev/sda2, /dev/mmcblk0p2):\n" + "\n" + "Required PARAMS:\n" + " -s|--signprivate FILE.vbprivk" + " The private key to sign the kernel blob\n" + " [--infile] INFILE Input kernel partition (modified\n" + " in place if no OUTFILE given)\n" + "\n" + "Optional PARAMS:\n" + " -b|--keyblock FILE.keyblock The keyblock containing the public\n" + " key to verify the kernel blob\n" + " -v|--version NUM The kernel version number\n" + " --config FILE The kernel commandline file\n" + " --pad NUM The vblock padding size in bytes\n" + " (default 0x%x)\n" + " [--outfile] OUTFILE Output kernel partition or vblock\n" + " --vblockonly Emit just the vblock (requires a\n" + " distinct OUTFILE)\n" "\n"; static void print_help(const char *prog) { - printf(usage, prog, option.version); + printf(usage, prog); + printf(usage_bios, option.version); + printf(usage_new_kpart, option.kloadaddr, option.padding); + printf(usage_old_kpart, option.padding); } +enum no_short_opts { + OPT_FV = 1000, + OPT_INFILE, /* aka "--vmlinuz" */ + OPT_OUTFILE, + OPT_BOOTLOADER, + OPT_CONFIG, + OPT_ARCH, + OPT_KLOADADDR, + OPT_PADDING, +}; + static const struct option long_opts[] = { /* name hasarg *flag val */ - {"signprivate", 1, NULL, 's'}, - {"keyblock", 1, NULL, 'b'}, - {"kernelkey", 1, NULL, 'k'}, - {"devsign", 1, NULL, 'S'}, - {"devkeyblock", 1, NULL, 'B'}, - {"version", 1, NULL, 'v'}, - {"flags", 1, NULL, 'f'}, - {"loemdir", 1, NULL, 'd'}, - {"loemid", 1, NULL, 'l'}, - {"debug", 0, &debugging_enabled, 1}, - {NULL, 0, NULL, 0}, + {"signprivate", 1, NULL, 's'}, + {"keyblock", 1, NULL, 'b'}, + {"kernelkey", 1, NULL, 'k'}, + {"devsign", 1, NULL, 'S'}, + {"devkeyblock", 1, NULL, 'B'}, + {"version", 1, NULL, 'v'}, + {"flags", 1, NULL, 'f'}, + {"loemdir", 1, NULL, 'd'}, + {"loemid", 1, NULL, 'l'}, + {"fv", 1, NULL, OPT_FV}, + {"infile", 1, NULL, OPT_INFILE}, + {"datapubkey", 1, NULL, OPT_INFILE}, /* alias */ + {"vmlinuz", 1, NULL, OPT_INFILE}, /* alias */ + {"outfile", 1, NULL, OPT_OUTFILE}, + {"bootloader", 1, NULL, OPT_BOOTLOADER}, + {"config", 1, NULL, OPT_CONFIG}, + {"arch", 1, NULL, OPT_ARCH}, + {"kloadaddr", 1, NULL, OPT_KLOADADDR}, + {"pad", 1, NULL, OPT_PADDING}, + {"vblockonly", 0, &option.vblockonly, 1}, + {"debug", 0, &debugging_enabled, 1}, + {NULL, 0, NULL, 0}, }; static char *short_opts = ":s:b:k:S:B:v:f:d:l:"; static int do_sign(int argc, char *argv[]) { char *infile = 0; - char *outfile = 0; - int fd, i; + int i; + int ifd = -1; int errorcnt = 0; struct futil_traverse_state_s state; - char *e = 0; uint8_t *buf; uint32_t buf_len; + char *e = 0; + enum futil_file_type type; + int inout_file_count = 0; + int mapping; opterr = 0; /* quiet, you */ while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) { @@ -372,6 +624,7 @@ static int do_sign(int argc, char *argv[]) } break; case 'v': + option.version_specified = 1; option.version = strtoul(optarg, &e, 0); if (!*optarg || (e && *e)) { fprintf(stderr, @@ -395,96 +648,233 @@ static int do_sign(int argc, char *argv[]) case 'l': option.loemid = optarg; break; - + case OPT_INFILE: /* aka "--vmlinuz" */ + inout_file_count++; + infile = optarg; + break; + case OPT_OUTFILE: + inout_file_count++; + option.outfile = optarg; + break; + case OPT_BOOTLOADER: + option.bootloader_data = ReadFile( + optarg, &option.bootloader_size); + if (!option.bootloader_data) { + fprintf(stderr, + "Error reading bootloader file: %s\n", + strerror(errno)); + errorcnt++; + } + Debug("bootloader file size=0x%" PRIx64 "\n", + option.bootloader_size); + break; + case OPT_CONFIG: + option.config_data = ReadConfigFile( + optarg, &option.config_size); + if (!option.config_data) { + fprintf(stderr, + "Error reading config file: %s\n", + strerror(errno)); + errorcnt++; + } + break; + case OPT_ARCH: + /* check the first 3 characters to also match x86_64 */ + if ((!strncasecmp(optarg, "x86", 3)) || + (!strcasecmp(optarg, "amd64"))) + option.arch = ARCH_X86; + else if ((!strcasecmp(optarg, "arm")) || + (!strcasecmp(optarg, "aarch64"))) + option.arch = ARCH_ARM; + else if (!strcasecmp(optarg, "mips")) + option.arch = ARCH_MIPS; + else { + fprintf(stderr, + "Unknown architecture: \"%s\"\n", + optarg); + errorcnt++; + } + break; + case OPT_KLOADADDR: + option.kloadaddr = strtoul(optarg, &e, 0); + if (!*optarg || (e && *e)) { + fprintf(stderr, + "Invalid --kloadaddr \"%s\"\n", optarg); + errorcnt++; + } + break; + case OPT_PADDING: + option.padding = strtoul(optarg, &e, 0); + if (!*optarg || (e && *e)) { + fprintf(stderr, + "Invalid --padding \"%s\"\n", optarg); + errorcnt++; + } + break; case '?': if (optopt) fprintf(stderr, "Unrecognized option: -%c\n", optopt); else - fprintf(stderr, "Unrecognized option\n"); + fprintf(stderr, "Unrecognized option: %s\n", + argv[optind - 1]); errorcnt++; break; case ':': fprintf(stderr, "Missing argument to -%c\n", optopt); errorcnt++; break; + case 0: /* handled option */ + break; default: + Debug("i=%d\n", i); DIE; } } - if (!option.signprivate) { - fprintf(stderr, - "Missing required private firmware data key\n"); - errorcnt++; + /* If we don't have an input file already, we need one */ + if (!infile) { + if (argc - optind <= 0) { + errorcnt++; + fprintf(stderr, "ERROR: missing input filename\n"); + goto done; + } else { + inout_file_count++; + infile = argv[optind++]; + } } - if (!option.keyblock) { - fprintf(stderr, - "Missing required keyblock\n"); - errorcnt++; + /* What are we looking at? */ + type = futil_what_file_type(infile); + + /* We may be able to infer the type based on the other args */ + if (type == FILE_TYPE_UNKNOWN) { + if (option.bootloader_data || option.config_data + || option.arch != ARCH_UNSPECIFIED) + type = FILE_TYPE_RAW_KERNEL; } - if (!option.kernel_subkey) { + /* Check the arguments for the type of thing we want to sign */ + switch (type) { + case FILE_TYPE_UNKNOWN: fprintf(stderr, - "Missing required kernel subkey\n"); + "Unable to determine the type of the input file\n"); + errorcnt++; + goto done; + case FILE_TYPE_KEYBLOCK: + fprintf(stderr, "Resigning a keyblock is kind of pointless.\n"); + fprintf(stderr, "Just create a new one.\n"); + errorcnt++; + break; + case FILE_TYPE_FW_PREAMBLE: + fprintf(stderr, + "%s IS a signature. Sign the firmware instead\n", + infile); + break; + case FILE_TYPE_GBB: + fprintf(stderr, "There's no way to sign a GBB\n"); errorcnt++; - } - - if (errorcnt) { - print_help(argv[0]); - return 1; - } - - switch (argc - optind) { - case 2: - infile = argv[optind++]; - outfile = argv[optind++]; - futil_copy_file_or_die(infile, outfile); break; - case 1: - /* Stomping right on it. Errors will leave it garbled. */ - /* TODO: Use a tempfile (mkstemp) for normal files. */ - infile = argv[optind++]; - outfile = infile; + case FILE_TYPE_BIOS_IMAGE: + case FILE_TYPE_OLD_BIOS_IMAGE: + errorcnt += no_opt_if(!option.signprivate, "signprivate"); + errorcnt += no_opt_if(!option.keyblock, "keyblock"); + errorcnt += no_opt_if(!option.kernel_subkey, "kernelkey"); break; - case 0: - fprintf(stderr, "ERROR: missing input filename\n"); - print_help(argv[0]); - return 1; + case FILE_TYPE_KERN_PREAMBLE: + errorcnt += no_opt_if(!option.signprivate, "signprivate"); + if (option.vblockonly) + option.create_new_outfile = 1; + break; + case FILE_TYPE_RAW_KERNEL: + option.create_new_outfile = 1; + errorcnt += no_opt_if(!option.signprivate, "signprivate"); + errorcnt += no_opt_if(!option.keyblock, "keyblock"); + errorcnt += no_opt_if(!option.version_specified, "version"); + errorcnt += no_opt_if(!option.bootloader_data, "bootloader"); + errorcnt += no_opt_if(!option.config_data, "config"); + errorcnt += no_opt_if(option.arch == ARCH_UNSPECIFIED, "arch"); break; default: - fprintf(stderr, "ERROR: too many arguments left over\n"); - print_help(argv[0]); - return 1; + DIE; } - - fd = open(outfile, O_RDWR); - if (fd < 0) { - fprintf(stderr, "Can't open %s: %s\n", - outfile, strerror(errno)); - return 1; + /* If we don't have an output file, we may need one */ + if (!option.outfile) { + if (argc - optind > 0) { + /* We have an outfile arg, so use it. */ + inout_file_count++; + option.outfile = argv[optind++]; + } else { + if (option.create_new_outfile) { + /* A distinct outfile is required */ + errorcnt++; + fprintf(stderr, "Missing output filename\n"); + goto done; + } else { + /* We'll just modify the input file */ + option.outfile = infile; + } + } } - if (0 != futil_map_file(fd, MAP_RW, &buf, &buf_len)) { + Debug("type=%d\n", type); + Debug("option.create_new_outfile=%d\n", option.create_new_outfile); + Debug("inout_file_count=%d\n", inout_file_count); + Debug("infile=%s\n", infile); + Debug("option.outfile=%s\n", option.outfile); + + if (argc - optind > 0) { errorcnt++; - goto boo; + fprintf(stderr, "ERROR: too many arguments left over\n"); } + if (errorcnt) + goto done; + memset(&state, 0, sizeof(state)); - state.in_filename = outfile ? outfile : ""; state.op = FUTIL_OP_SIGN; - errorcnt += futil_traverse(buf, buf_len, &state, FILE_TYPE_UNKNOWN); + if (option.create_new_outfile) { + /* The input is read-only, the output is write-only. */ + mapping = MAP_RO; + state.in_filename = infile; + ifd = open(infile, O_RDONLY); + if (ifd < 0) { + errorcnt++; + fprintf(stderr, "Can't open %s for reading: %s\n", + infile, strerror(errno)); + goto done; + } + } else { + /* We'll read-modify-write the output file */ + mapping = MAP_RW; + state.in_filename = option.outfile; + if (inout_file_count > 1) + futil_copy_file_or_die(infile, option.outfile); + ifd = open(option.outfile, O_RDWR); + if (ifd < 0) { + errorcnt++; + fprintf(stderr, "Can't open %s for writing: %s\n", + option.outfile, strerror(errno)); + goto done; + } + } + + if (0 != futil_map_file(ifd, mapping, &buf, &buf_len)) { + errorcnt++; + goto done; + } + + errorcnt += futil_traverse(buf, buf_len, &state, type); - errorcnt += futil_unmap_file(fd, MAP_RO, buf, buf_len); + errorcnt += futil_unmap_file(ifd, MAP_RW, buf, buf_len); -boo: - if (close(fd)) { +done: + if (ifd >= 0 && close(ifd)) { errorcnt++; - fprintf(stderr, "Error when closing %s: %s\n", - outfile, strerror(errno)); + fprintf(stderr, "Error when closing ifd: %s\n", + strerror(errno)); } if (option.signprivate) @@ -494,9 +884,12 @@ boo: if (option.kernel_subkey) free(option.kernel_subkey); + if (errorcnt) + fprintf(stderr, "Use --help for usage instructions\n"); + return !!errorcnt; } DECLARE_FUTIL_COMMAND(sign, do_sign, - "[Re]Sign a BIOS image", + "Sign / resign various binary components", print_help); diff --git a/futility/cmd_vbutil_kernel0.c b/futility/cmd_vbutil_kernel0.c deleted file mode 100644 index 78f3d506..00000000 --- a/futility/cmd_vbutil_kernel0.c +++ /dev/null @@ -1,974 +0,0 @@ -/* Copyright 2012 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. - * - * Verified boot kernel utility - */ - -#include -#include -#include -#include /* For PRIu64 */ -#include -#include /* For BLKGETSIZE64 */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cryptolib.h" -#include "futility.h" -#include "host_common.h" -#include "kernel_blob.h" -#include "util_misc.h" -#include "vboot_common.h" - -/* Global opts */ -static int opt_verbose; -static int opt_vblockonly; -static uint64_t opt_pad = 65536; - -/* Command line options */ -enum { - OPT_MODE_PACK = 1000, - OPT_MODE_REPACK, - OPT_MODE_VERIFY, - OPT_ARCH, - OPT_OLDBLOB, - OPT_KLOADADDR, - OPT_KEYBLOCK, - OPT_SIGNPUBKEY, - OPT_SIGNPRIVATE, - OPT_VERSION, - OPT_VMLINUZ, - OPT_BOOTLOADER, - OPT_CONFIG, - OPT_VBLOCKONLY, - OPT_PAD, - OPT_VERBOSE, - OPT_MINVERSION, -}; - -static const struct option long_opts[] = { - {"pack", 1, 0, OPT_MODE_PACK}, - {"repack", 1, 0, OPT_MODE_REPACK}, - {"verify", 1, 0, OPT_MODE_VERIFY}, - {"arch", 1, 0, OPT_ARCH}, - {"oldblob", 1, 0, OPT_OLDBLOB}, - {"kloadaddr", 1, 0, OPT_KLOADADDR}, - {"keyblock", 1, 0, OPT_KEYBLOCK}, - {"signpubkey", 1, 0, OPT_SIGNPUBKEY}, - {"signprivate", 1, 0, OPT_SIGNPRIVATE}, - {"version", 1, 0, OPT_VERSION}, - {"minversion", 1, 0, OPT_MINVERSION}, - {"vmlinuz", 1, 0, OPT_VMLINUZ}, - {"bootloader", 1, 0, OPT_BOOTLOADER}, - {"config", 1, 0, OPT_CONFIG}, - {"vblockonly", 0, 0, OPT_VBLOCKONLY}, - {"pad", 1, 0, OPT_PAD}, - {"verbose", 0, &opt_verbose, 1}, - {"debug", 0, &debugging_enabled, 1}, - {NULL, 0, 0, 0} -}; - - - -static const char usage[] = - "\n" - "Usage: " MYNAME " %s --pack [PARAMETERS]\n" - "\n" - " Required parameters:\n" - " --keyblock Key block in .keyblock format\n" - " --signprivate Private key to sign kernel data,\n" - " in .vbprivk format\n" - " --version Kernel version\n" - " --vmlinuz Linux kernel bzImage file\n" - " --bootloader Bootloader stub\n" - " --config Command line file\n" - " --arch Cpu architecture (default x86)\n" - "\n" - " Optional:\n" - " --kloadaddr
Assign kernel body load address\n" - " --pad Verification padding size in bytes\n" - " --vblockonly Emit just the verification blob\n" - "\nOR\n\n" - "Usage: " MYNAME " %s --repack [PARAMETERS]\n" - "\n" - " Required parameters:\n" - " --signprivate Private key to sign kernel data,\n" - " in .vbprivk format\n" - " --oldblob Previously packed kernel blob\n" - " (including verfication blob)\n" - "\n" - " Optional:\n" - " --keyblock Key block in .keyblock format\n" - " --config New command line file\n" - " --version Kernel version\n" - " --kloadaddr
Assign kernel body load address\n" - " --pad Verification blob size in bytes\n" - " --vblockonly Emit just the verification blob\n" - "\nOR\n\n" - "Usage: " MYNAME " %s --verify [PARAMETERS]\n" - "\n" - " Optional:\n" - " --signpubkey " - " Public key to verify kernel keyblock,\n" - " in .vbpubk format\n" - " --verbose Print a more detailed report\n" - " --keyblock Outputs the verified key block,\n" - " in .keyblock format\n" - " --pad Verification padding size in bytes\n" - " --minversion Minimum combined kernel key version\n" - " and kernel version\n" - "\n"; - - -/* Print help and return error */ -static void print_help(const char *progname) -{ - printf(usage, progname, progname, progname); -} - -static void Fatal(const char *format, ...) -{ - va_list ap; - va_start(ap, format); - fprintf(stderr, "ERROR: "); - vfprintf(stderr, format, ap); - va_end(ap); - exit(1); -} - -/* Return an explanation when fread() fails. */ -static const char *error_fread(FILE *fp) -{ - const char *retval = "beats me why"; - if (feof(fp)) - retval = "EOF"; - else if (ferror(fp)) - retval = strerror(errno); - clearerr(fp); - return retval; -} - -/* Return the smallest integral multiple of [alignment] that is equal - * to or greater than [val]. Used to determine the number of - * pages/sectors/blocks/whatever needed to contain [val] - * items/bytes/etc. */ -static uint64_t roundup(uint64_t val, uint64_t alignment) -{ - uint64_t rem = val % alignment; - if (rem) - return val + (alignment - rem); - return val; -} - -/* Match regexp /\b--\b/ to delimit the start of the kernel commandline. If we - * don't find one, we'll use the whole thing. */ -static unsigned int find_cmdline_start(char *input, unsigned int max_len) -{ - int start = 0; - int i; - for (i = 0; i < max_len - 1 && input[i]; i++) { - if ('-' == input[i] && '-' == input[i + 1]) { - if ((i == 0 || ' ' == input[i - 1]) && - (i + 2 >= max_len || ' ' == input[i + 2])) { - /* found "--" with nothing before or after it */ - start = i + 2; /* hope for a trailing '\0' */ - break; - } - } - } - while (' ' == input[start]) /* skip leading spaces */ - start++; - - return start; -} - -/****************************************************************************/ -/* Here are globals containing all the bits & pieces I'm working on. */ - -/* The individual parts that go into the kernel blob */ -static uint8_t *g_kernel_data; -static uint64_t g_kernel_size; -static uint8_t *g_param_data; -static uint64_t g_param_size; -static uint8_t *g_config_data; -static uint64_t g_config_size; -static uint8_t *g_bootloader_data; -static uint64_t g_bootloader_size; -static uint64_t g_bootloader_address; - -/* The individual parts of the verification blob (including the data that - * immediately follows the headers) */ -static VbKeyBlockHeader *g_keyblock; -static VbKernelPreambleHeader *g_preamble; - -/****************************************************************************/ - -/* - * Read the kernel command line from a file. Get rid of \n characters along - * the way and verify that the line fits into a 4K buffer. - * - * Return the buffer contaning the line on success (and set the line length - * using the passed in parameter), or NULL in case something goes wrong. - */ -static uint8_t *sReadConfigFile(const char *config_file, uint64_t *config_size) -{ - uint8_t *config_buf; - int ii; - - config_buf = ReadFile(config_file, config_size); - Debug(" config file size=0x%" PRIx64 "\n", *config_size); - if (CROS_CONFIG_SIZE <= *config_size) { /* room for trailing '\0' */ - VbExError("Config file %s is too large (>= %d bytes)\n", - config_file, CROS_CONFIG_SIZE); - return NULL; - } - - /* Replace newlines with spaces */ - for (ii = 0; ii < *config_size; ii++) - if ('\n' == config_buf[ii]) - config_buf[ii] = ' '; - - return config_buf; -} - -/* Offset of kernel command line string from start of packed kernel blob */ -static uint64_t CmdLineOffset(VbKernelPreambleHeader *preamble) -{ - return preamble->bootloader_address - preamble->body_load_address - - CROS_CONFIG_SIZE - CROS_PARAMS_SIZE; -} - -/* This initializes g_vmlinuz and g_param from a standard vmlinuz file. - * It returns 0 on error. */ -static int ImportVmlinuzFile(const char *vmlinuz_file, enum arch_t arch, - uint64_t kernel_body_load_address) -{ - uint8_t *kernel_buf; - uint64_t kernel_size; - uint64_t kernel32_start = 0; - uint64_t kernel32_size = 0; - struct linux_kernel_params *params = NULL, *lh = NULL; - - /* Read the kernel */ - Debug("Reading %s\n", vmlinuz_file); - kernel_buf = ReadFile(vmlinuz_file, &kernel_size); - if (!kernel_buf) - return 0; - Debug(" kernel file size=0x%" PRIx64 "\n", kernel_size); - if (!kernel_size) - Fatal("Empty kernel file\n"); - - /* Go ahead and allocate the param region anyway. I don't think we need - * it for non-x86, but let's keep it for now. */ - g_param_size = CROS_PARAMS_SIZE; - g_param_data = VbExMalloc(g_param_size); - Memset(g_param_data, 0, g_param_size); - - /* Unless we're handling x86, the kernel is the kernel; we're done. */ - if (arch != ARCH_X86) { - g_kernel_data = kernel_buf; - g_kernel_size = kernel_size; - return 1; - } - - /* The first part of the x86 vmlinuz is a header, followed by a - * real-mode boot stub. We only want the 32-bit part. */ - lh = (struct linux_kernel_params *)kernel_buf; - kernel32_start = (lh->setup_sects + 1) << 9; - if (kernel32_start >= kernel_size) - Fatal("Malformed kernel\n"); - kernel32_size = kernel_size - kernel32_start; - - Debug(" kernel32_start=0x%" PRIx64 "\n", kernel32_start); - Debug(" kernel32_size=0x%" PRIx64 "\n", kernel32_size); - - /* Keep just the 32-bit kernel. */ - if (kernel32_size) { - g_kernel_size = kernel32_size; - g_kernel_data = VbExMalloc(g_kernel_size); - Memcpy(g_kernel_data, kernel_buf + kernel32_start, - kernel32_size); - } - - /* Copy the original zeropage data from kernel_buf into g_param_data, - * then tweak a few fields for our purposes */ - params = (struct linux_kernel_params *)(g_param_data); - Memcpy(&(params->setup_sects), &(lh->setup_sects), - offsetof(struct linux_kernel_params, e820_entries) - - offsetof(struct linux_kernel_params, setup_sects)); - params->boot_flag = 0; - params->ramdisk_image = 0; /* we don't support initrd */ - params->ramdisk_size = 0; - params->type_of_loader = 0xff; - /* We need to point to the kernel commandline arg. On disk, it will come - * right after the 32-bit part of the kernel. */ - params->cmd_line_ptr = kernel_body_load_address + - roundup(kernel32_size, CROS_ALIGN) + - find_cmdline_start((char *)g_config_data, g_config_size); - Debug(" cmdline_addr=0x%x\n", params->cmd_line_ptr); - Debug(" version=0x%x\n", params->version); - Debug(" kernel_alignment=0x%x\n", params->kernel_alignment); - Debug(" relocatable_kernel=0x%x\n", params->relocatable_kernel); - /* A fake e820 memory map with 2 entries */ - params->n_e820_entry = 2; - params->e820_entries[0].start_addr = 0x00000000; - params->e820_entries[0].segment_size = 0x00001000; - params->e820_entries[0].segment_type = E820_TYPE_RAM; - params->e820_entries[1].start_addr = 0xfffff000; - params->e820_entries[1].segment_size = 0x00001000; - params->e820_entries[1].segment_type = E820_TYPE_RESERVED; - - /* done */ - free(kernel_buf); - return 1; -} - -/* This returns just the kernel blob, with the verification blob separated - * and copied to new memory in g_keyblock and g_preamble. */ -static uint8_t *ReadOldBlobFromFileOrDie(const char *filename, - uint64_t *size_ptr) -{ - FILE *fp = NULL; - struct stat statbuf; - VbKeyBlockHeader *key_block; - VbKernelPreambleHeader *preamble; - uint64_t now = 0; - uint8_t *buf; - uint8_t *kernel_blob_data; - uint64_t kernel_blob_size; - uint64_t file_size = 0; - - if (0 != stat(filename, &statbuf)) - Fatal("Unable to stat %s: %s\n", filename, strerror(errno)); - - if (S_ISBLK(statbuf.st_mode)) { - int fd = open(filename, O_RDONLY); - if (fd >= 0) { - ioctl(fd, BLKGETSIZE64, &file_size); - close(fd); - } - } else { - file_size = statbuf.st_size; - } - Debug("%s size is 0x%" PRIx64 "\n", filename, file_size); - if (file_size < opt_pad) - Fatal("%s is too small to be a valid kernel blob\n"); - - Debug("Reading %s\n", filename); - fp = fopen(filename, "rb"); - if (!fp) - Fatal("Unable to open file %s: %s\n", filename, - strerror(errno)); - - buf = VbExMalloc(opt_pad); - if (1 != fread(buf, opt_pad, 1, fp)) - Fatal("Unable to read header from %s: %s\n", filename, - error_fread(fp)); - - /* Sanity-check the key_block */ - key_block = (VbKeyBlockHeader *) buf; - Debug("Keyblock is 0x%" PRIx64 " bytes\n", key_block->key_block_size); - now += key_block->key_block_size; - if (now > file_size) - Fatal("key_block_size advances past the end of the blob\n"); - if (now > opt_pad) - Fatal("key_block_size advances past %" PRIu64 " byte padding\n", - opt_pad); - /* LGTM */ - g_keyblock = (VbKeyBlockHeader *) VbExMalloc(key_block->key_block_size); - Memcpy(g_keyblock, key_block, key_block->key_block_size); - - /* And the preamble */ - preamble = (VbKernelPreambleHeader *) (buf + now); - Debug("Preamble is 0x%" PRIx64 " bytes\n", preamble->preamble_size); - now += preamble->preamble_size; - if (now > file_size) - Fatal("preamble_size advances past the end of the blob\n"); - if (now > opt_pad) - Fatal("preamble_size advances past %" PRIu64 " byte padding\n", - opt_pad); - /* LGTM */ - Debug(" kernel_version = %d\n", preamble->kernel_version); - Debug(" bootloader_address = 0x%" PRIx64 "\n", - preamble->bootloader_address); - Debug(" bootloader_size = 0x%" PRIx64 "\n", preamble->bootloader_size); - Debug(" kern_blob_size = 0x%" PRIx64 "\n", - preamble->body_signature.data_size); - g_preamble = - (VbKernelPreambleHeader *) VbExMalloc(preamble->preamble_size); - Memcpy(g_preamble, preamble, preamble->preamble_size); - - /* Now for the kernel blob */ - Debug("kernel blob is at offset 0x%" PRIx64 "\n", now); - if (0 != fseek(fp, now, SEEK_SET)) - Fatal("Unable to seek to 0x%" PRIx64 " in %s: %s\n", now, - filename, strerror(errno)); - - /* Sanity check */ - kernel_blob_size = file_size - now; - if (!kernel_blob_size) - Fatal("No kernel blob found\n"); - if (kernel_blob_size < preamble->body_signature.data_size) - fprintf(stderr, - "Warning: kernel file only has 0x%" PRIx64 " bytes\n", - kernel_blob_size); - kernel_blob_data = VbExMalloc(kernel_blob_size); - - /* Read it in */ - if (1 != fread(kernel_blob_data, kernel_blob_size, 1, fp)) - Fatal("Unable to read kernel blob from %s: %s\n", filename, - error_fread(fp)); - - /* Done */ - VbExFree(buf); - - if (size_ptr) - *size_ptr = kernel_blob_size; - - return kernel_blob_data; -} - -/* Split a kernel blob into separate g_kernel, g_param, g_config, and - * g_bootloader parts. */ -static void UnpackKernelBlob(uint8_t *kernel_blob_data, - uint64_t kernel_blob_size) -{ - - uint64_t k_blob_size = g_preamble->body_signature.data_size; - uint64_t k_blob_ofs = 0; - uint64_t b_size = g_preamble->bootloader_size; - uint64_t b_ofs = k_blob_ofs + g_preamble->bootloader_address - - g_preamble->body_load_address; - uint64_t p_ofs = b_ofs - CROS_CONFIG_SIZE; - uint64_t c_ofs = p_ofs - CROS_PARAMS_SIZE; - - Debug("k_blob_size = 0x%" PRIx64 "\n", k_blob_size); - Debug("k_blob_ofs = 0x%" PRIx64 "\n", k_blob_ofs); - Debug("b_size = 0x%" PRIx64 "\n", b_size); - Debug("b_ofs = 0x%" PRIx64 "\n", b_ofs); - Debug("p_ofs = 0x%" PRIx64 "\n", p_ofs); - Debug("c_ofs = 0x%" PRIx64 "\n", c_ofs); - - g_kernel_size = c_ofs; - g_kernel_data = VbExMalloc(g_kernel_size); - Memcpy(g_kernel_data, kernel_blob_data, g_kernel_size); - - g_param_size = CROS_PARAMS_SIZE; - g_param_data = VbExMalloc(g_param_size); - Memcpy(g_param_data, kernel_blob_data + p_ofs, g_param_size); - - g_config_size = CROS_CONFIG_SIZE; - g_config_data = VbExMalloc(g_config_size); - Memcpy(g_config_data, kernel_blob_data + c_ofs, g_config_size); - - g_bootloader_size = b_size; - g_bootloader_data = VbExMalloc(g_bootloader_size); - Memcpy(g_bootloader_data, kernel_blob_data + b_ofs, g_bootloader_size); -} - -/****************************************************************************/ - -static uint8_t *CreateKernBlob(uint64_t kernel_body_load_address, - enum arch_t arch, uint64_t *size_ptr) -{ - uint8_t *kern_blob; - uint64_t kern_blob_size; - uint64_t now; - uint64_t bootloader_size = roundup(g_bootloader_size, CROS_ALIGN); - - /* Put the kernel blob together */ - kern_blob_size = roundup(g_kernel_size, CROS_ALIGN) + - CROS_CONFIG_SIZE + CROS_PARAMS_SIZE + bootloader_size; - Debug("kern_blob_size=0x%" PRIx64 "\n", kern_blob_size); - kern_blob = VbExMalloc(kern_blob_size); - Memset(kern_blob, 0, kern_blob_size); - now = 0; - - Debug("kernel goes at kern_blob+0x%" PRIx64 "\n", now); - - Memcpy(kern_blob + now, g_kernel_data, g_kernel_size); - now += roundup(g_kernel_size, CROS_ALIGN); - - Debug("config goes at kern_blob+0x%" PRIx64 "\n", now); - if (g_config_size) - Memcpy(kern_blob + now, g_config_data, g_config_size); - now += CROS_CONFIG_SIZE; - - Debug("params goes at kern_blob+0x%" PRIx64 "\n", now); - if (g_param_size) - Memcpy(kern_blob + now, g_param_data, g_param_size); - now += CROS_PARAMS_SIZE; - - Debug("bootloader goes at kern_blob+0x%" PRIx64 "\n", now); - g_bootloader_address = kernel_body_load_address + now; - Debug(" bootloader_address=0x%" PRIx64 "\n", g_bootloader_address); - Debug(" bootloader_size=0x%" PRIx64 "\n", bootloader_size); - if (bootloader_size) - Memcpy(kern_blob + now, g_bootloader_data, g_bootloader_size); - now += bootloader_size; - Debug("end of kern_blob at kern_blob+0x%" PRIx64 "\n", now); - - /* Done */ - if (size_ptr) - *size_ptr = kern_blob_size; - - return kern_blob; -} - -static int Pack(const char *outfile, - uint8_t *kernel_blob, - uint64_t kernel_size, - int version, - uint64_t kernel_body_load_address, - VbPrivateKey *signpriv_key) -{ - VbSignature *body_sig; - FILE *f; - uint64_t i; - uint64_t written = 0; - - /* Sign the kernel data */ - body_sig = CalculateSignature(kernel_blob, kernel_size, signpriv_key); - if (!body_sig) - Fatal("Error calculating body signature\n"); - - /* Create preamble */ - g_preamble = CreateKernelPreamble(version, - kernel_body_load_address, - g_bootloader_address, - roundup(g_bootloader_size, - CROS_ALIGN), body_sig, - opt_pad - g_keyblock->key_block_size, - signpriv_key); - if (!g_preamble) { - VbExError("Error creating preamble.\n"); - return 1; - } - /* Write the output file */ - Debug("writing %s...\n", outfile); - f = fopen(outfile, "wb"); - if (!f) { - VbExError("Can't open output file %s\n", outfile); - return 1; - } - Debug("0x%" PRIx64 " bytes of key_block\n", g_keyblock->key_block_size); - Debug("0x%" PRIx64 " bytes of preamble\n", g_preamble->preamble_size); - i = ((1 != fwrite(g_keyblock, g_keyblock->key_block_size, 1, f)) || - (1 != fwrite(g_preamble, g_preamble->preamble_size, 1, f))); - if (i) { - VbExError("Can't write output file %s\n", outfile); - fclose(f); - unlink(outfile); - return 1; - } - written += g_keyblock->key_block_size; - written += g_preamble->preamble_size; - - if (!opt_vblockonly) { - Debug("0x%" PRIx64 " bytes of kern_blob\n", kernel_size); - i = (1 != fwrite(kernel_blob, kernel_size, 1, f)); - if (i) { - fclose(f); - unlink(outfile); - Fatal("Can't write output file %s\n", outfile); - } - written += kernel_size; - } - Debug("0x%" PRIx64 " bytes total\n", written); - fclose(f); - - /* Success */ - return 0; -} - -static int Verify(uint8_t *kernel_blob, - uint64_t kernel_size, - VbPublicKey *signpub_key, - const char *keyblock_outfile, - uint64_t min_version) -{ - VbPublicKey *data_key; - RSAPublicKey *rsa; - - if (0 != KeyBlockVerify(g_keyblock, g_keyblock->key_block_size, - signpub_key, (0 == signpub_key))) - Fatal("Error verifying key block.\n"); - - printf("Key block:\n"); - data_key = &g_keyblock->data_key; - if (opt_verbose) - printf(" Signature: %s\n", - signpub_key ? "valid" : "ignored"); - printf(" Size: 0x%" PRIx64 "\n", - g_keyblock->key_block_size); - printf(" Flags: %" PRIu64 " ", - g_keyblock->key_block_flags); - if (g_keyblock->key_block_flags & KEY_BLOCK_FLAG_DEVELOPER_0) - printf(" !DEV"); - if (g_keyblock->key_block_flags & KEY_BLOCK_FLAG_DEVELOPER_1) - printf(" DEV"); - if (g_keyblock->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_0) - printf(" !REC"); - if (g_keyblock->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_1) - printf(" REC"); - printf("\n"); - printf(" Data key algorithm: %" PRIu64 " %s\n", data_key->algorithm, - (data_key->algorithm < kNumAlgorithms ? - algo_strings[data_key->algorithm] : "(invalid)")); - printf(" Data key version: %" PRIu64 "\n", data_key->key_version); - printf(" Data key sha1sum: "); - PrintPubKeySha1Sum(data_key); - printf("\n"); - - if (keyblock_outfile) { - FILE *f = NULL; - f = fopen(keyblock_outfile, "wb"); - if (!f) - Fatal("Can't open key block file %s\n", - keyblock_outfile); - if (1 != fwrite(g_keyblock, g_keyblock->key_block_size, 1, f)) - Fatal("Can't write key block file %s\n", - keyblock_outfile); - fclose(f); - } - - if (data_key->key_version < (min_version >> 16)) - Fatal("Data key version %" PRIu64 - " is lower than minimum %" PRIu64 ".\n", - data_key->key_version, (min_version >> 16)); - - rsa = PublicKeyToRSA(data_key); - if (!rsa) - Fatal("Error parsing data key.\n"); - - /* Verify preamble */ - if (0 != - VerifyKernelPreamble(g_preamble, g_preamble->preamble_size, rsa)) - Fatal("Error verifying preamble.\n"); - - printf("Preamble:\n"); - printf(" Size: 0x%" PRIx64 "\n", - g_preamble->preamble_size); - printf(" Header version: %" PRIu32 ".%" PRIu32 "\n", - g_preamble->header_version_major, - g_preamble->header_version_minor); - printf(" Kernel version: %" PRIu64 "\n", - g_preamble->kernel_version); - printf(" Body load address: 0x%" PRIx64 "\n", - g_preamble->body_load_address); - printf(" Body size: 0x%" PRIx64 "\n", - g_preamble->body_signature.data_size); - printf(" Bootloader address: 0x%" PRIx64 "\n", - g_preamble->bootloader_address); - printf(" Bootloader size: 0x%" PRIx64 "\n", - g_preamble->bootloader_size); - - if (g_preamble->kernel_version < (min_version & 0xFFFF)) - Fatal("Kernel version %" PRIu64 " is lower than minimum %" - PRIu64 ".\n", g_preamble->kernel_version, - (min_version & 0xFFFF)); - - /* Verify body */ - if (0 != VerifyData(kernel_blob, kernel_size, - &g_preamble->body_signature, rsa)) - Fatal("Error verifying kernel body.\n"); - printf("Body verification succeeded.\n"); - - if (opt_verbose) - printf("Config:\n%s\n", - kernel_blob + CmdLineOffset(g_preamble)); - - return 0; -} - -/****************************************************************************/ - -static int do_vbutil_kernel(int argc, char *argv[]) -{ - char *filename = NULL; - char *oldfile = NULL; - char *keyblock_file = NULL; - char *signpubkey_file = NULL; - char *signprivkey_file = NULL; - char *version_str = NULL; - int version = -1; - char *vmlinuz_file = NULL; - char *bootloader_file = NULL; - char *config_file = NULL; - enum arch_t arch = ARCH_X86; - char *address_str = NULL; - uint64_t kernel_body_load_address = CROS_32BIT_ENTRY_ADDR; - int mode = 0; - int parse_error = 0; - uint64_t min_version = 0; - char *e; - int i; - VbPrivateKey *signpriv_key = NULL; - VbPublicKey *signpub_key = NULL; - uint8_t *kernel_blob = NULL; - uint64_t kernel_size = 0; - - while (((i = getopt_long(argc, argv, ":", long_opts, NULL)) != -1) && - !parse_error) { - switch (i) { - default: - case '?': - /* Unhandled option */ - parse_error = 1; - break; - - case 0: - /* silently handled option */ - break; - - case OPT_MODE_PACK: - case OPT_MODE_REPACK: - case OPT_MODE_VERIFY: - if (mode && (mode != i)) { - fprintf(stderr, - "Only one mode can be specified\n"); - parse_error = 1; - break; - } - mode = i; - filename = optarg; - break; - - case OPT_ARCH: - /* check the first 3 characters to also detect x86_64 */ - if ((!strncasecmp(optarg, "x86", 3)) || - (!strcasecmp(optarg, "amd64"))) - arch = ARCH_X86; - else if ((!strcasecmp(optarg, "arm")) || - (!strcasecmp(optarg, "aarch64"))) - arch = ARCH_ARM; - else if (!strcasecmp(optarg, "mips")) - arch = ARCH_MIPS; - else { - fprintf(stderr, - "Unknown architecture string: %s\n", - optarg); - parse_error = 1; - } - break; - - case OPT_OLDBLOB: - oldfile = optarg; - break; - - case OPT_KLOADADDR: - address_str = optarg; - kernel_body_load_address = strtoul(optarg, &e, 0); - if (!*optarg || (e && *e)) { - fprintf(stderr, "Invalid --kloadaddr\n"); - parse_error = 1; - } - break; - - case OPT_KEYBLOCK: - keyblock_file = optarg; - break; - - case OPT_SIGNPUBKEY: - signpubkey_file = optarg; - break; - - case OPT_SIGNPRIVATE: - signprivkey_file = optarg; - break; - - case OPT_VMLINUZ: - vmlinuz_file = optarg; - break; - - case OPT_BOOTLOADER: - bootloader_file = optarg; - break; - - case OPT_CONFIG: - config_file = optarg; - break; - - case OPT_VBLOCKONLY: - opt_vblockonly = 1; - break; - - case OPT_VERSION: - version_str = optarg; - version = strtoul(optarg, &e, 0); - if (!*optarg || (e && *e)) { - fprintf(stderr, "Invalid --version\n"); - parse_error = 1; - } - break; - - case OPT_MINVERSION: - min_version = strtoul(optarg, &e, 0); - if (!*optarg || (e && *e)) { - fprintf(stderr, "Invalid --minversion\n"); - parse_error = 1; - } - break; - - case OPT_PAD: - opt_pad = strtoul(optarg, &e, 0); - if (!*optarg || (e && *e)) { - fprintf(stderr, "Invalid --pad\n"); - parse_error = 1; - } - break; - } - } - - if (parse_error) { - print_help(argv[0]); - return 1; - } - - switch (mode) { - case OPT_MODE_PACK: - - /* Required */ - - if (!keyblock_file) - Fatal("Missing required keyblock file.\n"); - - g_keyblock = (VbKeyBlockHeader *) ReadFile(keyblock_file, 0); - if (!g_keyblock) - Fatal("Error reading key block.\n"); - - if (!signprivkey_file) - Fatal("Missing required signprivate file.\n"); - - signpriv_key = PrivateKeyRead(signprivkey_file); - if (!signpriv_key) - Fatal("Error reading signing key.\n"); - - /* Optional */ - - if (config_file) { - Debug("Reading %s\n", config_file); - g_config_data = - sReadConfigFile(config_file, &g_config_size); - if (!g_config_data) - Fatal("Error reading config file.\n"); - } - - if (vmlinuz_file) - if (!ImportVmlinuzFile - (vmlinuz_file, arch, kernel_body_load_address)) - Fatal("Error reading kernel file.\n"); - - if (bootloader_file) { - Debug("Reading %s\n", bootloader_file); - g_bootloader_data = - ReadFile(bootloader_file, &g_bootloader_size); - if (!g_bootloader_data) - Fatal("Error reading bootloader file.\n"); - Debug(" bootloader file size=0x%" PRIx64 "\n", - g_bootloader_size); - } - - /* Do it */ - - kernel_blob = CreateKernBlob(kernel_body_load_address, arch, - &kernel_size); - - return Pack(filename, kernel_blob, kernel_size, - version, kernel_body_load_address, signpriv_key); - - case OPT_MODE_REPACK: - - /* Required */ - - if (!signprivkey_file) - Fatal("Missing required signprivate file.\n"); - - signpriv_key = PrivateKeyRead(signprivkey_file); - if (!signpriv_key) - Fatal("Error reading signing key.\n"); - - if (!oldfile) - Fatal("Missing previously packed blob.\n"); - - /* Load the old blob */ - - kernel_blob = ReadOldBlobFromFileOrDie(oldfile, &kernel_size); - if (0 != Verify(kernel_blob, kernel_size, 0, 0, 0)) - Fatal("The oldblob doesn't verify\n"); - - /* Take it apart */ - - UnpackKernelBlob(kernel_blob, kernel_size); - free(kernel_blob); - - /* Load optional params */ - - if (!version_str) - version = g_preamble->kernel_version; - - if (!address_str) - kernel_body_load_address = - g_preamble->body_load_address; - - if (config_file) { - if (g_config_data) - free(g_config_data); - Debug("Reading %s\n", config_file); - g_config_data = - sReadConfigFile(config_file, &g_config_size); - if (!g_config_data) - Fatal("Error reading config file.\n"); - } - - if (keyblock_file) { - if (g_keyblock) - free(g_keyblock); - g_keyblock = - (VbKeyBlockHeader *) ReadFile(keyblock_file, 0); - if (!g_keyblock) - Fatal("Error reading key block.\n"); - } - - /* Put it back together */ - - kernel_blob = CreateKernBlob(kernel_body_load_address, arch, - &kernel_size); - - return Pack(filename, kernel_blob, kernel_size, - version, kernel_body_load_address, signpriv_key); - - case OPT_MODE_VERIFY: - - /* Optional */ - - if (signpubkey_file) { - signpub_key = PublicKeyRead(signpubkey_file); - if (!signpub_key) - Fatal("Error reading public key.\n"); - } - - /* Do it */ - - kernel_blob = ReadOldBlobFromFileOrDie(filename, &kernel_size); - - return Verify(kernel_blob, kernel_size, signpub_key, - keyblock_file, min_version); - } - - fprintf(stderr, - "You must specify a mode: --pack, --repack or --verify\n"); - print_help(argv[0]); - return 1; -} - -DECLARE_FUTIL_COMMAND(vbutil_kernel0, do_vbutil_kernel, - "Creates, signs, and verifies the kernel blob", - print_help); diff --git a/futility/traversal.c b/futility/traversal.c index b392fa97..ef60fde2 100644 --- a/futility/traversal.c +++ b/futility/traversal.c @@ -35,7 +35,7 @@ static int (* const cb_show_funcs[])(struct futil_traverse_state_s *state) = { futil_cb_show_keyblock, /* CB_KEYBLOCK */ futil_cb_show_gbb, /* CB_GBB */ futil_cb_show_fw_preamble, /* CB_FW_PREAMBLE */ - NULL, /* CB_KERN_PREAMBLE */ + futil_cb_show_kernel_preamble, /* CB_KERN_PREAMBLE */ NULL, /* CB_RAW_FIRMWARE */ NULL, /* CB_RAW_KERNEL */ }; @@ -46,17 +46,17 @@ static int (* const cb_sign_funcs[])(struct futil_traverse_state_s *state) = { futil_cb_sign_begin, /* CB_BEGIN_TRAVERSAL */ futil_cb_sign_end, /* CB_END_TRAVERSAL */ NULL, /* CB_FMAP_GBB */ - futil_cb_sign_fw_preamble, /* CB_FMAP_VBLOCK_A */ - futil_cb_sign_fw_preamble, /* CB_FMAP_VBLOCK_B */ - futil_cb_sign_fw_main, /* CB_FMAP_FW_MAIN_A */ - futil_cb_sign_fw_main, /* CB_FMAP_FW_MAIN_B */ - futil_cb_sign_bogus, /* CB_PUBKEY */ - futil_cb_sign_notyet, /* CB_KEYBLOCK */ - futil_cb_sign_bogus, /* CB_GBB */ - futil_cb_sign_fw_preamble, /* CB_FW_PREAMBLE */ - NULL, /* CB_KERN_PREAMBLE */ - NULL, /* CB_RAW_FIRMWARE */ - NULL, /* CB_RAW_KERNEL */ + futil_cb_sign_fw_vblock, /* CB_FMAP_VBLOCK_A */ + futil_cb_sign_fw_vblock, /* CB_FMAP_VBLOCK_B */ + futil_cb_sign_fw_main, /* CB_FMAP_FW_MAIN_A */ + futil_cb_sign_fw_main, /* CB_FMAP_FW_MAIN_B */ + futil_cb_sign_pubkey, /* CB_PUBKEY */ + NULL, /* CB_KEYBLOCK */ + NULL, /* CB_GBB */ + NULL, /* CB_FW_PREAMBLE */ + futil_cb_resign_kernel_part, /* CB_KERN_PREAMBLE */ + futil_cb_sign_raw_firmware, /* CB_RAW_FIRMWARE */ + futil_cb_create_kernel_part, /* CB_RAW_KERNEL */ }; BUILD_ASSERT(ARRAY_SIZE(cb_sign_funcs) == NUM_CB_COMPONENTS); diff --git a/futility/traversal.h b/futility/traversal.h index 531ad6aa..faf8d27c 100644 --- a/futility/traversal.h +++ b/futility/traversal.h @@ -117,11 +117,14 @@ int futil_cb_show_gbb(struct futil_traverse_state_s *state); int futil_cb_show_keyblock(struct futil_traverse_state_s *state); int futil_cb_show_fw_main(struct futil_traverse_state_s *state); int futil_cb_show_fw_preamble(struct futil_traverse_state_s *state); +int futil_cb_show_kernel_preamble(struct futil_traverse_state_s *state); -int futil_cb_sign_bogus(struct futil_traverse_state_s *state); -int futil_cb_sign_notyet(struct futil_traverse_state_s *state); +int futil_cb_sign_pubkey(struct futil_traverse_state_s *state); int futil_cb_sign_fw_main(struct futil_traverse_state_s *state); -int futil_cb_sign_fw_preamble(struct futil_traverse_state_s *state); +int futil_cb_sign_fw_vblock(struct futil_traverse_state_s *state); +int futil_cb_sign_raw_firmware(struct futil_traverse_state_s *state); +int futil_cb_resign_kernel_part(struct futil_traverse_state_s *state); +int futil_cb_create_kernel_part(struct futil_traverse_state_s *state); int futil_cb_sign_begin(struct futil_traverse_state_s *state); int futil_cb_sign_end(struct futil_traverse_state_s *state); diff --git a/tests/futility/run_test_scripts.sh b/tests/futility/run_test_scripts.sh index bb59a0c8..bb3a600b 100755 --- a/tests/futility/run_test_scripts.sh +++ b/tests/futility/run_test_scripts.sh @@ -44,6 +44,7 @@ ${SCRIPTDIR}/test_main.sh ${SCRIPTDIR}/test_dump_fmap.sh ${SCRIPTDIR}/test_load_fmap.sh ${SCRIPTDIR}/test_gbb_utility.sh +${SCRIPTDIR}/test_show_kernel.sh ${SCRIPTDIR}/test_sign_firmware.sh ${SCRIPTDIR}/test_sign_kernel.sh " diff --git a/tests/futility/test_show_kernel.sh b/tests/futility/test_show_kernel.sh new file mode 100755 index 00000000..d4322451 --- /dev/null +++ b/tests/futility/test_show_kernel.sh @@ -0,0 +1,55 @@ +#!/bin/bash -eux +# Copyright (c) 2014 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" + +DEVKEYS=${SRCDIR}/tests/devkeys +TESTKEYS=${SRCDIR}/tests/testkeys + +echo 'Creating test kernel' + +# Dummy kernel data +echo "hi there" > ${TMP}.config.txt +dd if=/dev/urandom bs=16384 count=1 of=${TMP}.bootloader.bin +dd if=/dev/urandom bs=32768 count=1 of=${TMP}.kernel.bin + +# Pack kernel data key using original vboot utilities. +${FUTILITY} vbutil_key --pack ${TMP}.datakey.test \ + --key ${TESTKEYS}/key_rsa2048.keyb --algorithm 4 + +# Keyblock with kernel data key is signed by kernel subkey +# Flags=5 means dev=0 rec=0 +${FUTILITY} vbutil_keyblock --pack ${TMP}.keyblock.test \ + --datapubkey ${TMP}.datakey.test \ + --flags 5 \ + --signprivate ${DEVKEYS}/kernel_subkey.vbprivk + +# Kernel preamble is signed with the kernel data key +${FUTILITY} vbutil_kernel \ + --pack ${TMP}.kernel.test \ + --keyblock ${TMP}.keyblock.test \ + --signprivate ${TESTKEYS}/key_rsa2048.sha256.vbprivk \ + --version 1 \ + --arch arm \ + --vmlinuz ${TMP}.kernel.bin \ + --bootloader ${TMP}.bootloader.bin \ + --config ${TMP}.config.txt + +echo 'Verifying test kernel' + +# Verify the kernel +${FUTILITY} show ${TMP}.kernel.test \ + --publickey ${DEVKEYS}/kernel_subkey.vbpubk \ + | egrep 'Signature.*valid' + +echo 'Test kernel blob looks good' + +# cleanup +rm -rf ${TMP}* +exit 0 diff --git a/tests/futility/test_sign_kernel.sh b/tests/futility/test_sign_kernel.sh index f6fe1a1a..0fdb6259 100755 --- a/tests/futility/test_sign_kernel.sh +++ b/tests/futility/test_sign_kernel.sh @@ -17,15 +17,15 @@ dd if=/dev/urandom bs=512 count=1 of=${TMP}.bootloader.bin dd if=/dev/urandom bs=512 count=1 of=${TMP}.bootloader2.bin # default padding -padding=65536 +padding=49152 try_arch () { local arch=$1 - echo -n "${arch}.a " 1>&3 + echo -n "${arch}: 1 " 1>&3 # pack it up the old way - ${FUTILITY} vbutil_kernel0 --debug \ + ${FUTILITY} vbutil_kernel --debug \ --pack ${TMP}.blob1.${arch} \ --keyblock ${DEVKEYS}/recovery_kernel.keyblock \ --signprivate ${DEVKEYS}/recovery_kernel_data_key.vbprivk \ @@ -34,17 +34,19 @@ try_arch () { --bootloader ${TMP}.bootloader.bin \ --vmlinuz ${SCRIPTDIR}/data/vmlinuz-${arch}.bin \ --arch ${arch} \ + --pad ${padding} \ --kloadaddr 0x11000 # verify the old way - ${FUTILITY} vbutil_kernel0 --verify ${TMP}.blob1.${arch} \ + ${FUTILITY} vbutil_kernel --verify ${TMP}.blob1.${arch} \ + --pad ${padding} \ --signpubkey ${DEVKEYS}/recovery_key.vbpubk ${FUTILITY} vbutil_kernel --verify ${TMP}.blob1.${arch} \ + --pad ${padding} \ --signpubkey ${DEVKEYS}/recovery_key.vbpubk --debug # pack it up the new way - ${FUTILITY} vbutil_kernel --debug \ - --pack ${TMP}.blob2.${arch} \ + ${FUTILITY} sign --debug \ --keyblock ${DEVKEYS}/recovery_kernel.keyblock \ --signprivate ${DEVKEYS}/recovery_kernel_data_key.vbprivk \ --version 1 \ @@ -52,45 +54,70 @@ try_arch () { --bootloader ${TMP}.bootloader.bin \ --vmlinuz ${SCRIPTDIR}/data/vmlinuz-${arch}.bin \ --arch ${arch} \ - --kloadaddr 0x11000 + --pad ${padding} \ + --kloadaddr 0x11000 \ + --outfile ${TMP}.blob2.${arch} # they should be identical cmp ${TMP}.blob1.${arch} ${TMP}.blob2.${arch} + echo -n "2 " 1>&3 + # repack it the old way - ${FUTILITY} vbutil_kernel0 \ + ${FUTILITY} vbutil_kernel \ --repack ${TMP}.blob3.${arch} \ --oldblob ${TMP}.blob1.${arch} \ --signprivate ${DEVKEYS}/kernel_data_key.vbprivk \ --keyblock ${DEVKEYS}/kernel.keyblock \ --version 2 \ + --pad ${padding} \ --config ${TMP}.config2.txt \ --bootloader ${TMP}.bootloader2.bin # verify the old way - ${FUTILITY} vbutil_kernel0 --verify ${TMP}.blob3.${arch} \ + ${FUTILITY} vbutil_kernel --verify ${TMP}.blob3.${arch} \ + --pad ${padding} \ --signpubkey ${DEVKEYS}/kernel_subkey.vbpubk ${FUTILITY} vbutil_kernel --verify ${TMP}.blob3.${arch} \ + --pad ${padding} \ --signpubkey ${DEVKEYS}/kernel_subkey.vbpubk # repack it the new way - ${FUTILITY} vbutil_kernel \ - --repack ${TMP}.blob4.${arch} \ - --oldblob ${TMP}.blob2.${arch} \ + ${FUTILITY} sign --debug \ --signprivate ${DEVKEYS}/kernel_data_key.vbprivk \ --keyblock ${DEVKEYS}/kernel.keyblock \ --version 2 \ + --pad ${padding} \ --config ${TMP}.config2.txt \ - --bootloader ${TMP}.bootloader2.bin + --bootloader ${TMP}.bootloader2.bin \ + ${TMP}.blob2.${arch} \ + ${TMP}.blob4.${arch} # they should be identical cmp ${TMP}.blob3.${arch} ${TMP}.blob4.${arch} + echo -n "3 " 1>&3 + + # repack it the new way, in-place + cp ${TMP}.blob2.${arch} ${TMP}.blob5.${arch} + ${FUTILITY} sign --debug \ + --signprivate ${DEVKEYS}/kernel_data_key.vbprivk \ + --keyblock ${DEVKEYS}/kernel.keyblock \ + --version 2 \ + --pad ${padding} \ + --config ${TMP}.config2.txt \ + --bootloader ${TMP}.bootloader2.bin \ + ${TMP}.blob5.${arch} + + # they should be identical + cmp ${TMP}.blob3.${arch} ${TMP}.blob5.${arch} + + # and now just the vblocks... - echo -n "${arch}.v " 1>&3 + echo -n "4 " 1>&3 dd bs=${padding} count=1 if=${TMP}.blob1.${arch} of=${TMP}.blob1.${arch}.vb0 - ${FUTILITY} vbutil_kernel0 \ + ${FUTILITY} vbutil_kernel \ --pack ${TMP}.blob1.${arch}.vb1 \ --vblockonly \ --keyblock ${DEVKEYS}/recovery_kernel.keyblock \ @@ -100,13 +127,12 @@ try_arch () { --bootloader ${TMP}.bootloader.bin \ --vmlinuz ${SCRIPTDIR}/data/vmlinuz-${arch}.bin \ --arch ${arch} \ + --pad ${padding} \ --kloadaddr 0x11000 cmp ${TMP}.blob1.${arch}.vb0 ${TMP}.blob1.${arch}.vb1 dd bs=${padding} count=1 if=${TMP}.blob2.${arch} of=${TMP}.blob2.${arch}.vb0 - ${FUTILITY} vbutil_kernel \ - --pack ${TMP}.blob2.${arch}.vb1 \ - --vblockonly \ + ${FUTILITY} sign --debug \ --keyblock ${DEVKEYS}/recovery_kernel.keyblock \ --signprivate ${DEVKEYS}/recovery_kernel_data_key.vbprivk \ --version 1 \ @@ -114,50 +140,46 @@ try_arch () { --bootloader ${TMP}.bootloader.bin \ --vmlinuz ${SCRIPTDIR}/data/vmlinuz-${arch}.bin \ --arch ${arch} \ - --kloadaddr 0x11000 + --pad ${padding} \ + --kloadaddr 0x11000 \ + --vblockonly \ + ${TMP}.blob2.${arch}.vb1 + cmp ${TMP}.blob2.${arch}.vb0 ${TMP}.blob2.${arch}.vb1 + echo -n "5 " 1>&3 + dd bs=${padding} count=1 if=${TMP}.blob3.${arch} of=${TMP}.blob3.${arch}.vb0 - ${FUTILITY} vbutil_kernel0 \ + ${FUTILITY} vbutil_kernel \ --repack ${TMP}.blob3.${arch}.vb1 \ --vblockonly \ --oldblob ${TMP}.blob1.${arch} \ --signprivate ${DEVKEYS}/kernel_data_key.vbprivk \ --keyblock ${DEVKEYS}/kernel.keyblock \ --version 2 \ + --pad ${padding} \ --config ${TMP}.config2.txt \ --bootloader ${TMP}.bootloader2.bin cmp ${TMP}.blob3.${arch}.vb0 ${TMP}.blob3.${arch}.vb1 dd bs=${padding} count=1 if=${TMP}.blob4.${arch} of=${TMP}.blob4.${arch}.vb0 - ${FUTILITY} vbutil_kernel \ - --repack ${TMP}.blob4.${arch}.vb1 \ - --vblockonly \ - --oldblob ${TMP}.blob2.${arch} \ + ${FUTILITY} sign --debug \ --signprivate ${DEVKEYS}/kernel_data_key.vbprivk \ --keyblock ${DEVKEYS}/kernel.keyblock \ --version 2 \ --config ${TMP}.config2.txt \ - --bootloader ${TMP}.bootloader2.bin - cmp ${TMP}.blob4.${arch}.vb0 ${TMP}.blob4.${arch}.vb1 + --bootloader ${TMP}.bootloader2.bin \ + --pad ${padding} \ + --vblockonly \ + ${TMP}.blob2.${arch} \ + ${TMP}.blob4.${arch}.vb1 \ + cmp ${TMP}.blob4.${arch}.vb0 ${TMP}.blob4.${arch}.vb1 # Note: We specifically do not test repacking with a different --kloadaddr, # because the old way has a bug and does not update params->cmd_line_ptr to # point at the new on-disk location. Apparently (and not surprisingly), no # one has ever done that. - -#HEY # pack it up the new way -#HEY ${FUTILITY} sign --debug \ -#HEY --vmlinuz ${SCRIPTDIR}/data/vmlinuz-${arch}.bin \ -#HEY --config ${TMP}.config.txt \ -#HEY --bootloader ${TMP}.bootloader.bin \ -#HEY --arch ${arch} \ -#HEY --keyblock ${DEVKEYS}/recovery_kernel.keyblock \ -#HEY --signprivate ${DEVKEYS}/recovery_kernel_data_key.vbprivk \ -#HEY --version 1 \ -#HEY --outfile ${TMP}.blob2.${arch} - } try_arch amd64 -- cgit v1.2.1