summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2014-09-23 22:03:56 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-11-06 01:16:23 +0000
commit8f754f0a7dee101724eb8dcf641e54cb3c2a459c (patch)
tree78fe9db3fc891145366e3130dbf6ce30367b6fd3
parent07043c957bd487ba6cbc323f6acec13addee069d (diff)
downloadvboot-8f754f0a7dee101724eb8dcf641e54cb3c2a459c.tar.gz
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 <wfrichar@chromium.org> Change-Id: Ibc659f134cc83982e3f0c0bcc108cc0eddbe228e Reviewed-on: https://chromium-review.googlesource.com/219730 Reviewed-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/227882 Tested-by: Daisuke Nojiri <dnojiri@chromium.org> Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org> Commit-Queue: Daisuke Nojiri <dnojiri@chromium.org>
-rw-r--r--Makefile1
-rw-r--r--futility/cmd_show.c159
-rw-r--r--futility/cmd_sign.c561
-rw-r--r--futility/cmd_vbutil_kernel0.c974
-rw-r--r--futility/traversal.c24
-rw-r--r--futility/traversal.h9
-rwxr-xr-xtests/futility/run_test_scripts.sh1
-rwxr-xr-xtests/futility/test_show_kernel.sh55
-rwxr-xr-xtests/futility/test_sign_kernel.sh98
9 files changed, 741 insertions, 1141 deletions
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: <invalid>\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 : "<none>";
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 <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <inttypes.h> /* For PRIu64 */
-#include <sys/ioctl.h>
-#include <linux/fs.h> /* For BLKGETSIZE64 */
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#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 <file> [PARAMETERS]\n"
- "\n"
- " Required parameters:\n"
- " --keyblock <file> Key block in .keyblock format\n"
- " --signprivate <file> Private key to sign kernel data,\n"
- " in .vbprivk format\n"
- " --version <number> Kernel version\n"
- " --vmlinuz <file> Linux kernel bzImage file\n"
- " --bootloader <file> Bootloader stub\n"
- " --config <file> Command line file\n"
- " --arch <arch> Cpu architecture (default x86)\n"
- "\n"
- " Optional:\n"
- " --kloadaddr <address> Assign kernel body load address\n"
- " --pad <number> Verification padding size in bytes\n"
- " --vblockonly Emit just the verification blob\n"
- "\nOR\n\n"
- "Usage: " MYNAME " %s --repack <file> [PARAMETERS]\n"
- "\n"
- " Required parameters:\n"
- " --signprivate <file> Private key to sign kernel data,\n"
- " in .vbprivk format\n"
- " --oldblob <file> Previously packed kernel blob\n"
- " (including verfication blob)\n"
- "\n"
- " Optional:\n"
- " --keyblock <file> Key block in .keyblock format\n"
- " --config <file> New command line file\n"
- " --version <number> Kernel version\n"
- " --kloadaddr <address> Assign kernel body load address\n"
- " --pad <number> Verification blob size in bytes\n"
- " --vblockonly Emit just the verification blob\n"
- "\nOR\n\n"
- "Usage: " MYNAME " %s --verify <file> [PARAMETERS]\n"
- "\n"
- " Optional:\n"
- " --signpubkey <file>"
- " Public key to verify kernel keyblock,\n"
- " in .vbpubk format\n"
- " --verbose Print a more detailed report\n"
- " --keyblock <file> Outputs the verified key block,\n"
- " in .keyblock format\n"
- " --pad <number> Verification padding size in bytes\n"
- " --minversion <number> 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