diff mbox series

[RFC,2/5] firmware-utils/cros-vbutil: add Chrome OS vboot kernel-signing utility

Message ID 20200718205148.1743807-3-computersforpeace@gmail.com
State New
Headers show
Series Add support for Chromium OS and Google WiFi | expand

Commit Message

Brian Norris July 18, 2020, 8:51 p.m. UTC
Chrom{ium,e} OS based devices use a Coreboot+Depthcharge-based firmware,
which verifies and loads a kernel packed in a verified-boot payload. The
verification tooling (both for creating and verifying payloads) is
implemented here:

https://chromium.googlesource.com/chromiumos/platform/vboot_reference

Devices running such bootloaders also tend to support a "developer
mode," where a device can be unlocked to run arbitrary kernel payloads,
using the same verified-boot format plus well-known developer keys. More
information can be found here:

https://chromium.googlesource.com/chromiumos/docs/+/master/developer_mode.md

Rather than build and package the vboot_reference utilities as part of
the base OpenWRT tools, I chose to reimplement just the portion that's
required for signing payloads. I also embed the developer key directly
in the source for convenience, though it's certainly possible to
provide other keys too, if one were to build their own firmware that
accepts it.

This tool is essentially the same as running something like this, using
the Chromium OS tooling:

  vbutil_kernel --pack kernel_partition.bin \
   --keyblock /usr/share/vboot/devkeys/kernel.keyblock \
   --signprivate /usr/share/vboot/devkeys/kernel_data_key.vbprivk \
   --version 1 \
   --vmlinuz kernel.bin \
   --bootloader zero.txt \
   --config commnad_line.txt \
   --arch ${ARCH}

I have also packaged the Chromium OS vboot_reference tooling for the
packages feed, as it can be useful beyond simply creating a bootable
image (e.g., manipulating Chromium OS specific GPT attributes, handling
other NVRAM attributes, vboot packing/unpacking/verifying):
https://github.com/openwrt/packages/pull/12829

The vboot_reference tools are released by Google under a BSD 3-clause
license. I've provided the original license text as well as a GPL-2
notice for my modifications (essentially just borrowing the data
structures and rewriting everything else).

Signed-off-by: Brian Norris <computersforpeace@gmail.com>
---
 tools/firmware-utils/Makefile          |   1 +
 tools/firmware-utils/src/cros-vbutil.c | 609 +++++++++++++++++++++++++
 2 files changed, 610 insertions(+)
 create mode 100644 tools/firmware-utils/src/cros-vbutil.c
diff mbox series

Patch

diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile
index 3dd9ac5c2cc1..8216c97e9cfc 100644
--- a/tools/firmware-utils/Makefile
+++ b/tools/firmware-utils/Makefile
@@ -29,6 +29,7 @@  define Host/Compile
 	$(call cc,buffalo-enc buffalo-lib,-Wall)
 	$(call cc,buffalo-tag buffalo-lib,-Wall)
 	$(call cc,buffalo-tftp buffalo-lib,-Wall)
+	$(call cc,cros-vbutil,-lcrypto -lpthread)
 	$(call cc,dgfirmware)
 	$(call cc,dgn3500sum,-Wall)
 	$(call cc,dns313-header,-Wall)
diff --git a/tools/firmware-utils/src/cros-vbutil.c b/tools/firmware-utils/src/cros-vbutil.c
new file mode 100644
index 000000000000..2040c5901985
--- /dev/null
+++ b/tools/firmware-utils/src/cros-vbutil.c
@@ -0,0 +1,609 @@ 
+/*
+ * cros-vbutil - Tool for signing kernels using the Chromium OS verified boot
+ * format, using widely-shared "developer" keys. The output of this tool is
+ * intended to be written to a GPT partition of type "Chrome OS Kernel", such
+ * that a Chromium OS bootloader can find it.
+ *
+ * Much of this is adapted from Google's vboot_reference project found here:
+ *   https://chromium.googlesource.com/chromiumos/platform/vboot_reference
+ *
+ * Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *    * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Rewritten:
+ * Copyright (c) 2020 Brian Norris <computersforpeace@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+#include <openssl/sha.h>
+
+#define BLOCK_PAD 65536
+/* Sections are aligned to 4K blocks */
+#define ALIGN 4096
+#define ROUNDUP(x) (((x) + (ALIGN-1)) & ~(ALIGN-1))
+
+/*
+ * Signature size, in bytes. This is an RSA-2048 encryption of a SHA256 hash.
+ */
+#define SIG_SIZE (2048 / 8)
+
+/*
+ * From vboot_reference: tests/devkeys/kernel.keyblock
+ */
+static const unsigned char keyblock[] = {
+	0x43, 0x48, 0x52, 0x4f, 0x4d, 0x45, 0x4f, 0x53, 0x02, 0x00, 0x00, 0x00,
+	0x01, 0x00, 0x00, 0x00, 0xb8, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0xa0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x78, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x48, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x78, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xcb, 0x02, 0x40, 0x41,
+	0x1d, 0x2d, 0x86, 0x0f, 0x6d, 0x53, 0xcc, 0xcc, 0xa7, 0x9b, 0x1b, 0xca,
+	0xdc, 0x22, 0xc4, 0xc2, 0x3a, 0x72, 0xa5, 0x12, 0x21, 0xfe, 0x78, 0x1d,
+	0x9c, 0x03, 0x65, 0x0c, 0x94, 0xba, 0x3d, 0x5b, 0x5e, 0x98, 0x16, 0x8a,
+	0xaf, 0xaf, 0x67, 0x5a, 0x46, 0x88, 0x2c, 0xf0, 0x2a, 0x65, 0xbd, 0xc7,
+	0x59, 0xcc, 0x70, 0xe9, 0x01, 0x4e, 0x7d, 0x8c, 0x43, 0x2e, 0x1c, 0x6d,
+	0x72, 0x15, 0xaf, 0xd0, 0x71, 0x9f, 0xb4, 0x3a, 0xe7, 0x44, 0x82, 0xd1,
+	0x83, 0x8b, 0x9d, 0x6c, 0x64, 0x99, 0xaf, 0xdb, 0x8a, 0xcc, 0xd1, 0xd6,
+	0x83, 0xa6, 0xa4, 0xea, 0xa9, 0x81, 0x1d, 0x9f, 0x1b, 0x5a, 0x8c, 0xf8,
+	0xdb, 0x26, 0x21, 0xb2, 0x7b, 0x53, 0x92, 0xf0, 0x6a, 0xc5, 0x8c, 0x11,
+	0xba, 0x7e, 0x43, 0xcb, 0x1e, 0x57, 0xa1, 0x6e, 0xd5, 0xfe, 0x82, 0x28,
+	0x3d, 0x6d, 0xbd, 0x97, 0xb0, 0xe8, 0xfb, 0x1f, 0x88, 0x06, 0x1b, 0x0a,
+	0xc5, 0x8f, 0x96, 0x69, 0x48, 0x69, 0x41, 0x2a, 0x52, 0xd7, 0xb7, 0x33,
+	0x78, 0x8b, 0x6f, 0x97, 0xa7, 0x7d, 0xdc, 0xd0, 0x2b, 0xd9, 0x81, 0x67,
+	0xc9, 0x3f, 0xa9, 0xb4, 0x1e, 0x0b, 0x08, 0x54, 0xc6, 0x01, 0xc6, 0x20,
+	0xe6, 0xc6, 0x09, 0x5f, 0x0a, 0xc5, 0x02, 0x05, 0x6b, 0x40, 0x96, 0x11,
+	0x20, 0xe5, 0xa2, 0x88, 0x37, 0x6a, 0xd3, 0xda, 0xda, 0xda, 0x76, 0x1c,
+	0xe4, 0x3d, 0x2c, 0xfe, 0x65, 0x86, 0x98, 0xa2, 0xa8, 0x49, 0x7d, 0x26,
+	0x45, 0x14, 0x04, 0x10, 0xe3, 0x38, 0x4b, 0x66, 0x9c, 0x5c, 0x2c, 0xe8,
+	0xd9, 0x06, 0xd4, 0xc3, 0x05, 0x94, 0x1c, 0x7b, 0x23, 0x89, 0x86, 0x1d,
+	0x32, 0xbb, 0x23, 0x90, 0x2b, 0x72, 0xd7, 0x90, 0x53, 0x52, 0x10, 0x12,
+	0xd0, 0xc2, 0xe2, 0x72, 0x81, 0x70, 0x3c, 0xb1, 0x27, 0x42, 0xec, 0xbf,
+	0x2b, 0x54, 0xd6, 0xcb, 0x61, 0xea, 0xb4, 0xd4, 0xa8, 0xde, 0xf3, 0xdf,
+	0x3d, 0xd1, 0x17, 0xde, 0x86, 0x89, 0xf4, 0xa1, 0x28, 0x5e, 0xda, 0xb4,
+	0x47, 0x19, 0x9b, 0xc2, 0xce, 0x49, 0xea, 0x63, 0xda, 0x42, 0x1c, 0x85,
+	0x1c, 0xab, 0x6c, 0xd7, 0xc2, 0xfd, 0x34, 0x1b, 0xd5, 0x11, 0xa2, 0x44,
+	0xb7, 0x7a, 0x85, 0x61, 0x03, 0xba, 0xd4, 0x64, 0xe1, 0x49, 0x87, 0x92,
+	0xe6, 0xdc, 0x39, 0x94, 0x9c, 0xba, 0xef, 0x58, 0x9e, 0x0e, 0x36, 0x98,
+	0xd7, 0xd2, 0x4c, 0x14, 0x71, 0x94, 0x78, 0xaf, 0x86, 0xe7, 0x2f, 0xe9,
+	0xf9, 0x20, 0x4d, 0xfe, 0xda, 0xd7, 0x3f, 0x89, 0xc1, 0x59, 0xae, 0xbd,
+	0x60, 0x9b, 0x97, 0xa4, 0x66, 0x00, 0xbb, 0x7b, 0xd0, 0x49, 0x85, 0x6f,
+	0x7b, 0x01, 0x24, 0xc3, 0x48, 0xe2, 0xc7, 0x61, 0x98, 0x9c, 0xe5, 0x93,
+	0x87, 0x04, 0x09, 0x3a, 0x92, 0x54, 0x1f, 0xb5, 0x80, 0x29, 0x15, 0xe5,
+	0x7f, 0xfe, 0xdc, 0xaf, 0xd3, 0xe1, 0x62, 0xc4, 0xb6, 0xdb, 0x2f, 0x1a,
+	0x08, 0xf3, 0x54, 0x06, 0x3a, 0xce, 0x5e, 0x89, 0x71, 0xdf, 0x3b, 0x5c,
+	0x8c, 0x6b, 0x83, 0xa9, 0x0c, 0x05, 0x50, 0x7b, 0x84, 0x09, 0x18, 0x66,
+	0x84, 0xe1, 0x7e, 0x60, 0xd6, 0xdc, 0xbf, 0x1c, 0xac, 0x5c, 0xdc, 0xe6,
+	0x4c, 0x9e, 0xa0, 0x4a, 0x59, 0x30, 0xff, 0x91, 0x90, 0x1e, 0x21, 0x29,
+	0x50, 0x99, 0x76, 0xfd, 0x2f, 0x5a, 0x67, 0x6a, 0x00, 0x6b, 0x40, 0x27,
+	0xe7, 0xbd, 0x17, 0xe6, 0x5f, 0x9e, 0xe4, 0x6b, 0xe0, 0x05, 0x9e, 0x7d,
+	0xcb, 0x90, 0xe9, 0x3c, 0x9c, 0x79, 0xc9, 0x47, 0xf8, 0x58, 0xbc, 0x3f,
+	0xb1, 0xa5, 0xef, 0x67, 0xf0, 0x22, 0xa1, 0x70, 0x33, 0x36, 0xb0, 0x5c,
+	0xed, 0xea, 0x4e, 0xde, 0x24, 0x0c, 0x5b, 0xd8, 0x4d, 0xbe, 0xac, 0x6a,
+	0x12, 0x31, 0xa6, 0x5e, 0x5f, 0x04, 0xb6, 0x58, 0xa1, 0xd1, 0x29, 0xa3,
+	0x33, 0x9c, 0x64, 0xb1, 0x8e, 0x3b, 0xfa, 0xaf, 0x35, 0x94, 0x7a, 0xa4,
+	0xf5, 0xcb, 0x9e, 0x67, 0x1b, 0x8d, 0x35, 0xca, 0x10, 0x69, 0x8d, 0xea,
+	0x78, 0xc2, 0xf6, 0x63, 0xc0, 0x11, 0xf1, 0xf6, 0x66, 0x70, 0x2e, 0x96,
+	0xf0, 0xbc, 0x7b, 0x1e, 0x16, 0x26, 0xde, 0xe3, 0x7f, 0xc0, 0xf4, 0x6c,
+	0x3c, 0x0b, 0x52, 0xe2, 0x07, 0xde, 0x70, 0x8b, 0xaf, 0x84, 0x95, 0x1b,
+	0x46, 0x95, 0x69, 0x05, 0x92, 0xea, 0x36, 0xa5, 0xca, 0x50, 0xdb, 0x49,
+	0xc8, 0x44, 0xee, 0x7c, 0x49, 0x40, 0x34, 0x05, 0xf3, 0x06, 0x1f, 0x1c,
+	0xcc, 0x78, 0x62, 0x1b, 0x9d, 0xe2, 0x4f, 0x3e, 0x04, 0x96, 0xba, 0xd0,
+	0xc7, 0xa2, 0x3a, 0xaa, 0xf7, 0xc3, 0xae, 0xc0, 0x6b, 0xca, 0xc8, 0x59,
+	0x90, 0xe0, 0x45, 0x7b, 0x4b, 0x60, 0xbf, 0xae, 0x15, 0x25, 0xd0, 0xa2,
+	0x2f, 0x95, 0x6a, 0x2e, 0x37, 0x24, 0x15, 0x69, 0x0e, 0x3a, 0x6d, 0x40,
+	0x21, 0xf6, 0x74, 0x5c, 0xa6, 0x9f, 0xe0, 0x03, 0x60, 0x04, 0x59, 0x70,
+	0x70, 0xa0, 0x46, 0xf4, 0xdf, 0x0a, 0x4b, 0x14, 0xab, 0x87, 0x0c, 0xaa,
+	0x64, 0x9b, 0x82, 0xca, 0x21, 0xb0, 0x72, 0xfa, 0x8b, 0x37, 0xf5, 0x60,
+	0xd7, 0xb7, 0x4e, 0x72, 0x5e, 0x13, 0x23, 0x73, 0x84, 0x6c, 0xd1, 0x8d,
+	0x72, 0xa2, 0x15, 0x8b, 0x51, 0x38, 0x90, 0x68, 0x90, 0xd9, 0x8b, 0x65,
+	0x30, 0x61, 0xdb, 0x51, 0x80, 0x58, 0xdd, 0xc3, 0xf0, 0x29, 0x33, 0xdc,
+	0x39, 0x92, 0xbf, 0x27, 0xe6, 0xc7, 0x82, 0xf6, 0xea, 0xcf, 0x21, 0xd2,
+	0xa0, 0x38, 0x02, 0x57, 0x52, 0xf5, 0xdd, 0x07, 0x99, 0xe3, 0x09, 0xd7,
+	0xad, 0x9e, 0x1e, 0xe7, 0xf3, 0x5e, 0x72, 0xa0, 0xc6, 0x9d, 0x41, 0x86,
+	0x96, 0x82, 0x74, 0x46, 0x1f, 0x05, 0xd2, 0x05, 0x25, 0x29, 0x27, 0x66,
+	0xe1, 0xab, 0x5c, 0x06, 0xf4, 0xac, 0xe7, 0xd0, 0xf8, 0xe6, 0x5e, 0x8b,
+	0x19, 0x55, 0xc2, 0x99, 0x8e, 0xa7, 0x2e, 0x66, 0x0c, 0x2a, 0x61, 0xb0,
+	0x98, 0x41, 0x93, 0x05, 0x54, 0x90, 0xaa, 0xbe, 0x15, 0x26, 0x5b, 0x77,
+	0x7c, 0xb0, 0xf8, 0x08, 0x68, 0xb6, 0xb8, 0xd4, 0xdd, 0xd8, 0xa8, 0x4f,
+	0xdd, 0xf6, 0x93, 0xe5, 0xa1, 0x88, 0x1b, 0x7a, 0x05, 0x81, 0xc5, 0x98,
+	0x34, 0x1d, 0x88, 0x63, 0x55, 0xaf, 0x9a, 0xa1, 0xcb, 0x4b, 0xaf, 0x86,
+	0xb3, 0x45, 0xda, 0xa4, 0xb4, 0xa5, 0x10, 0x7a, 0x50, 0x6a, 0xe5, 0xda,
+	0x6f, 0x8f, 0xc0, 0x1d, 0x95, 0xb1, 0x91, 0x48, 0x53, 0xc3, 0xe5, 0x1f,
+	0x0e, 0x05, 0x75, 0xaa, 0xc7, 0xd8, 0x36, 0xd0, 0x6d, 0x50, 0x48, 0xd4,
+	0xe6, 0xbd, 0xd0, 0x2d, 0x97, 0x17, 0x2d, 0xb3, 0x46, 0xab, 0x19, 0x0e,
+	0xe4, 0xac, 0xfd, 0x93, 0xab, 0x0c, 0x4c, 0xad, 0xbf, 0x58, 0x1b, 0xaa,
+	0x26, 0xcd, 0x0d, 0xda, 0xaf, 0xcb, 0xeb, 0xb0, 0x19, 0x5c, 0x46, 0x1f,
+	0x9b, 0xb0, 0xb8, 0x12, 0x6c, 0xa7, 0x4e, 0xb8, 0xa7, 0x1f, 0x12, 0x6b,
+	0xf8, 0xbe, 0x4b, 0xa9, 0x04, 0xb4, 0xf9, 0x36, 0xcc, 0xd9, 0xd5, 0x91,
+	0xac, 0x7e, 0xe9, 0x13, 0xc0, 0x7f, 0xca, 0xd7, 0xdc, 0xbe, 0xa9, 0xb0,
+	0xd6, 0x28, 0x17, 0x2f, 0xd6, 0xdf, 0x0e, 0x6b, 0x48, 0x11, 0xd6, 0x51,
+	0xc7, 0x6d, 0x87, 0xf6, 0x08, 0xb7, 0x6c, 0x69, 0xf7, 0xd6, 0x28, 0x50,
+	0x7e, 0x74, 0xad, 0x96, 0x6a, 0x0c, 0xca, 0x6b, 0xb1, 0xf1, 0xde, 0xa3,
+	0x5f, 0x23, 0xf5, 0x2a, 0x44, 0x11, 0x0f, 0xa3, 0xf7, 0x24, 0x36, 0x1b,
+	0x33, 0xe6, 0x7e, 0xd9, 0x0e, 0xa5, 0x8b, 0xa0, 0xb8, 0x8b, 0x85, 0xee,
+	0xd0, 0xe4, 0x69, 0x43, 0xc6, 0xf9, 0x5f, 0x27, 0xa5, 0xc9, 0x61, 0x3b,
+	0x8e, 0xaf, 0x62, 0x77, 0x42, 0xb6, 0x27, 0x5f, 0xed, 0x96, 0x86, 0xb3,
+	0xef, 0x32, 0x31, 0x43, 0x2e, 0x7b, 0xaf, 0x5b, 0xe2, 0x75, 0x82, 0x72,
+	0xad, 0x3b, 0x5d, 0x92, 0xc1, 0x15, 0xaa, 0xfb, 0x21, 0x07, 0xe9, 0xaa,
+	0xc4, 0xe2, 0xd7, 0x99, 0x83, 0x6c, 0x72, 0x39, 0x6f, 0x35, 0x20, 0x9d,
+	0xe0, 0xd8, 0xf8, 0xdb, 0x72, 0x24, 0xe5, 0x8d, 0x29, 0xfd, 0x24, 0x7b,
+	0xbc, 0x48, 0xe9, 0xbb, 0xc2, 0x84, 0xc7, 0xb9
+};
+static unsigned int keyblock_len = sizeof(keyblock);
+
+/*
+ * RSA 2048-bit key from vboot_reference:
+ * tests/devkeys/kernel_data_key.vbprivk, minus the 8-byte custom header.
+ */
+static const unsigned char privk[] = {
+	0x30, 0x82, 0x04, 0xa3, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00,
+	0xcb, 0xd6, 0x54, 0x2b, 0xbf, 0xec, 0x42, 0x27, 0xb1, 0x3c, 0x70, 0x81,
+	0x72, 0xe2, 0xc2, 0xd0, 0x12, 0x10, 0x52, 0x53, 0x90, 0xd7, 0x72, 0x2b,
+	0x90, 0x23, 0xbb, 0x32, 0x1d, 0x86, 0x89, 0x23, 0x7b, 0x1c, 0x94, 0x05,
+	0xc3, 0xd4, 0x06, 0xd9, 0xe8, 0x2c, 0x5c, 0x9c, 0x66, 0x4b, 0x38, 0xe3,
+	0x10, 0x04, 0x14, 0x45, 0x26, 0x7d, 0x49, 0xa8, 0xa2, 0x98, 0x86, 0x65,
+	0xfe, 0x2c, 0x3d, 0xe4, 0x1c, 0x76, 0xda, 0xda, 0xda, 0xd3, 0x6a, 0x37,
+	0x88, 0xa2, 0xe5, 0x20, 0x11, 0x96, 0x40, 0x6b, 0x05, 0x02, 0xc5, 0x0a,
+	0x5f, 0x09, 0xc6, 0xe6, 0x20, 0xc6, 0x01, 0xc6, 0x54, 0x08, 0x0b, 0x1e,
+	0xb4, 0xa9, 0x3f, 0xc9, 0x67, 0x81, 0xd9, 0x2b, 0xd0, 0xdc, 0x7d, 0xa7,
+	0x97, 0x6f, 0x8b, 0x78, 0x33, 0xb7, 0xd7, 0x52, 0x2a, 0x41, 0x69, 0x48,
+	0x69, 0x96, 0x8f, 0xc5, 0x0a, 0x1b, 0x06, 0x88, 0x1f, 0xfb, 0xe8, 0xb0,
+	0x97, 0xbd, 0x6d, 0x3d, 0x28, 0x82, 0xfe, 0xd5, 0x6e, 0xa1, 0x57, 0x1e,
+	0xcb, 0x43, 0x7e, 0xba, 0x11, 0x8c, 0xc5, 0x6a, 0xf0, 0x92, 0x53, 0x7b,
+	0xb2, 0x21, 0x26, 0xdb, 0xf8, 0x8c, 0x5a, 0x1b, 0x9f, 0x1d, 0x81, 0xa9,
+	0xea, 0xa4, 0xa6, 0x83, 0xd6, 0xd1, 0xcc, 0x8a, 0xdb, 0xaf, 0x99, 0x64,
+	0x6c, 0x9d, 0x8b, 0x83, 0xd1, 0x82, 0x44, 0xe7, 0x3a, 0xb4, 0x9f, 0x71,
+	0xd0, 0xaf, 0x15, 0x72, 0x6d, 0x1c, 0x2e, 0x43, 0x8c, 0x7d, 0x4e, 0x01,
+	0xe9, 0x70, 0xcc, 0x59, 0xc7, 0xbd, 0x65, 0x2a, 0xf0, 0x2c, 0x88, 0x46,
+	0x5a, 0x67, 0xaf, 0xaf, 0x8a, 0x16, 0x98, 0x5e, 0x5b, 0x3d, 0xba, 0x94,
+	0x0c, 0x65, 0x03, 0x9c, 0x1d, 0x78, 0xfe, 0x21, 0x12, 0xa5, 0x72, 0x3a,
+	0xc2, 0xc4, 0x22, 0xdc, 0xca, 0x1b, 0x9b, 0xa7, 0xcc, 0xcc, 0x53, 0x6d,
+	0x0f, 0x86, 0x2d, 0x1d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01,
+	0x00, 0x4d, 0xb8, 0xea, 0x9d, 0x47, 0xef, 0xf1, 0x05, 0xab, 0x79, 0xed,
+	0x38, 0x1e, 0xb3, 0x42, 0x2f, 0x0e, 0x44, 0x34, 0xef, 0xf2, 0x31, 0x84,
+	0x32, 0x02, 0x22, 0xed, 0xb4, 0xdd, 0x37, 0x38, 0x41, 0x54, 0x27, 0x00,
+	0x75, 0xf6, 0x46, 0x79, 0x5a, 0x4b, 0x41, 0xe7, 0x0a, 0x27, 0xa3, 0x15,
+	0x0c, 0xd7, 0x45, 0x86, 0xe9, 0x1b, 0x14, 0xf8, 0xaa, 0x19, 0x5d, 0x69,
+	0x19, 0xa8, 0xc1, 0xfb, 0xd3, 0x67, 0xdb, 0x15, 0xf6, 0x0f, 0x2c, 0x3f,
+	0xba, 0xef, 0x19, 0x95, 0x9e, 0x15, 0xb6, 0xa1, 0xd7, 0x6c, 0xb0, 0xb2,
+	0xab, 0x12, 0xb3, 0xea, 0xa8, 0x42, 0xe4, 0xdd, 0x36, 0x5f, 0x42, 0x35,
+	0xe3, 0x3e, 0x65, 0xae, 0x91, 0xc0, 0x2a, 0xed, 0xc0, 0xdc, 0x41, 0xc6,
+	0x93, 0xc9, 0x95, 0x59, 0xab, 0xba, 0xa4, 0xa5, 0xb0, 0x89, 0xb6, 0x29,
+	0x4b, 0xd8, 0x1e, 0xf5, 0xb0, 0xde, 0xfa, 0x91, 0x11, 0x55, 0xd6, 0x9d,
+	0xe1, 0xa7, 0x9c, 0xf2, 0xf4, 0x9d, 0x7f, 0x96, 0x49, 0x7a, 0xab, 0xe9,
+	0x9a, 0x0c, 0xcb, 0x10, 0x5c, 0x67, 0xf0, 0x1d, 0xf3, 0x33, 0x84, 0xae,
+	0x6e, 0xe4, 0xbb, 0xa7, 0x57, 0x98, 0x99, 0x2b, 0xf3, 0x1d, 0xab, 0xec,
+	0x01, 0xa2, 0x2e, 0x3c, 0x10, 0x5c, 0xff, 0xe8, 0xcc, 0x50, 0x45, 0x21,
+	0x2f, 0x04, 0x69, 0xdd, 0x02, 0x5b, 0x10, 0xd6, 0x54, 0x36, 0x4c, 0x73,
+	0x0c, 0x1e, 0xc1, 0xff, 0xac, 0x60, 0x5d, 0xbc, 0x13, 0x84, 0xe8, 0xa4,
+	0xbd, 0x66, 0xca, 0xca, 0xaf, 0x55, 0x8a, 0xf8, 0x87, 0x17, 0x93, 0xbf,
+	0x66, 0xa4, 0x97, 0xe9, 0x47, 0x81, 0x03, 0xfa, 0x4c, 0x32, 0xf6, 0xec,
+	0x7a, 0x03, 0xbb, 0x2d, 0x3b, 0x9b, 0xea, 0xdc, 0x6c, 0x61, 0x7f, 0x05,
+	0xc8, 0x2d, 0x50, 0x89, 0xe9, 0x10, 0x3d, 0x3c, 0x02, 0xb0, 0x5d, 0x3e,
+	0x25, 0x71, 0xd2, 0xa1, 0xe1, 0x02, 0x81, 0x81, 0x00, 0xf6, 0x3c, 0x7f,
+	0x15, 0x63, 0x04, 0x0a, 0xd4, 0x68, 0xe8, 0xd9, 0x4b, 0x7f, 0xbc, 0xca,
+	0xb5, 0x84, 0x01, 0x71, 0x11, 0x33, 0x4d, 0xe1, 0x0a, 0xe8, 0xa7, 0x95,
+	0xcc, 0x23, 0x59, 0x19, 0xb5, 0x82, 0xaa, 0xae, 0xe4, 0x90, 0x33, 0x95,
+	0x20, 0x1a, 0xfd, 0xef, 0x8e, 0x36, 0xc1, 0x57, 0xde, 0x94, 0xdd, 0x99,
+	0x6f, 0x14, 0x0c, 0x8e, 0x30, 0xe0, 0xe7, 0x64, 0x33, 0xa7, 0x1d, 0xfd,
+	0x99, 0xdc, 0x58, 0x49, 0x4e, 0x56, 0x82, 0x43, 0xea, 0xd9, 0xf7, 0xcc,
+	0x1a, 0xf5, 0x09, 0x38, 0x4c, 0xf6, 0x87, 0x8f, 0xa6, 0x40, 0x96, 0xd1,
+	0x81, 0x62, 0xbb, 0x49, 0x53, 0x69, 0x56, 0xdf, 0x4a, 0x85, 0xfd, 0x72,
+	0x34, 0x21, 0x56, 0xa3, 0x7b, 0xda, 0x50, 0x24, 0x88, 0xc6, 0x0b, 0x03,
+	0x94, 0x0d, 0xe2, 0x03, 0x2a, 0x3c, 0x5a, 0xdd, 0x73, 0xe7, 0x07, 0x01,
+	0x7d, 0xdd, 0x38, 0x81, 0x19, 0x02, 0x81, 0x81, 0x00, 0xd3, 0xeb, 0x72,
+	0x3f, 0xf6, 0x9b, 0x03, 0x8c, 0xab, 0xd9, 0x41, 0x47, 0xbe, 0x6c, 0x07,
+	0x7f, 0xd7, 0xcb, 0xee, 0xbf, 0x45, 0x13, 0x1e, 0x53, 0x35, 0xd2, 0xe4,
+	0x6a, 0x13, 0xe9, 0xd8, 0xe3, 0xf4, 0x28, 0x5e, 0xeb, 0xd8, 0xd2, 0x87,
+	0x60, 0xdc, 0xa7, 0x21, 0xd8, 0x33, 0xce, 0xca, 0x60, 0x50, 0x1b, 0x87,
+	0xbf, 0x71, 0x88, 0x90, 0x71, 0x6f, 0xde, 0xdb, 0x33, 0x1d, 0x3c, 0xb1,
+	0x2f, 0x1b, 0x41, 0xb8, 0x55, 0xa1, 0x5c, 0xa8, 0xb2, 0x73, 0x1b, 0x39,
+	0x69, 0x5c, 0xa1, 0x57, 0x9b, 0x2e, 0xcd, 0x0d, 0x71, 0x42, 0xdf, 0x2a,
+	0xd3, 0x4f, 0x51, 0xdc, 0x35, 0x5c, 0x14, 0x86, 0x16, 0xb8, 0x22, 0x8c,
+	0x73, 0x37, 0x7a, 0x6c, 0xb7, 0xfe, 0x01, 0x1b, 0xa8, 0xc5, 0xf1, 0xff,
+	0xda, 0xd4, 0x63, 0x58, 0x81, 0xe1, 0x6a, 0xaf, 0xc2, 0x3d, 0xbb, 0x17,
+	0x3f, 0x2c, 0xf9, 0xb8, 0xa5, 0x02, 0x81, 0x81, 0x00, 0xd1, 0x1e, 0x6b,
+	0xa7, 0x54, 0x22, 0x18, 0xb8, 0x70, 0x28, 0x60, 0x72, 0x98, 0x17, 0x53,
+	0xe8, 0x78, 0x25, 0x69, 0x4a, 0xfc, 0x51, 0x45, 0xb0, 0x28, 0x0a, 0x3b,
+	0x53, 0xd5, 0x29, 0x54, 0x86, 0x2c, 0x64, 0x9c, 0x87, 0x8c, 0x57, 0xe2,
+	0x6c, 0x13, 0xc0, 0x59, 0x74, 0x63, 0xd8, 0x2b, 0xb1, 0xe2, 0xa7, 0x98,
+	0x29, 0x17, 0xd3, 0xd9, 0xe6, 0x74, 0xfa, 0xce, 0xc4, 0x77, 0x47, 0x27,
+	0x1b, 0x46, 0x60, 0xcc, 0x50, 0xf5, 0x73, 0x48, 0x0e, 0xe9, 0xf2, 0x62,
+	0xaa, 0xfa, 0x33, 0x4c, 0x57, 0x40, 0x7f, 0xbc, 0x65, 0xab, 0x30, 0xea,
+	0xd8, 0x81, 0x6b, 0x21, 0x48, 0xef, 0x8b, 0x51, 0xb3, 0x4e, 0xbc, 0x02,
+	0x03, 0x77, 0xd4, 0xc6, 0x9e, 0xe6, 0xb7, 0x6d, 0x6f, 0xa3, 0x11, 0xb3,
+	0x86, 0x47, 0x8c, 0x0c, 0xb8, 0x0f, 0xed, 0xf3, 0x68, 0xfd, 0x69, 0x9b,
+	0xc0, 0xb2, 0xe1, 0xcb, 0xf1, 0x02, 0x81, 0x80, 0x46, 0xc6, 0xdb, 0xe1,
+	0x80, 0xfb, 0x44, 0xb4, 0x36, 0xb0, 0xd8, 0x9c, 0x9d, 0x7c, 0x77, 0x04,
+	0xbb, 0x8d, 0xe0, 0xaf, 0x00, 0xe2, 0xe1, 0xbd, 0xe8, 0xbc, 0x06, 0x8d,
+	0x67, 0x19, 0x32, 0xd6, 0xef, 0x34, 0x8c, 0x7f, 0x38, 0x6b, 0x17, 0xcb,
+	0xc1, 0xe6, 0x0f, 0xe7, 0xa5, 0x65, 0xeb, 0x31, 0x3f, 0x8f, 0xf2, 0x46,
+	0x7e, 0x24, 0x9e, 0x19, 0x44, 0xc3, 0xa9, 0x71, 0xe8, 0xd1, 0xdc, 0x5b,
+	0x5f, 0x3f, 0x13, 0xd1, 0x85, 0x92, 0xec, 0xc4, 0xac, 0xac, 0xea, 0xff,
+	0xc5, 0x70, 0x1d, 0x52, 0x36, 0xcf, 0x45, 0x2e, 0x0e, 0xa7, 0x7a, 0x26,
+	0x18, 0xd7, 0xbe, 0x34, 0x14, 0x0c, 0xc6, 0xbb, 0xcf, 0xc3, 0xba, 0x24,
+	0xc0, 0xe2, 0xa2, 0x6c, 0xd0, 0xe8, 0x8d, 0xd4, 0x50, 0x48, 0x02, 0xab,
+	0x93, 0x43, 0x9a, 0xcb, 0xc5, 0xfd, 0x5b, 0xb6, 0x03, 0xe7, 0xf8, 0x5b,
+	0xa1, 0x29, 0xc6, 0x01, 0x02, 0x81, 0x80, 0x0e, 0x18, 0xbc, 0x00, 0xa3,
+	0x81, 0xda, 0xc2, 0xc8, 0xfb, 0x0a, 0xbd, 0x7f, 0xb7, 0x7f, 0xa1, 0x18,
+	0x7f, 0x49, 0xb8, 0x32, 0x83, 0x4b, 0x71, 0x35, 0x59, 0x51, 0x7b, 0xee,
+	0x8e, 0x4e, 0xd2, 0x42, 0xa8, 0x6c, 0x82, 0x57, 0xf7, 0x34, 0xf6, 0x82,
+	0x59, 0xe9, 0x75, 0x7a, 0x8f, 0x0a, 0x8d, 0x46, 0x1e, 0xa1, 0x77, 0xa1,
+	0x4c, 0xb3, 0x9e, 0x86, 0x93, 0x18, 0xdf, 0x3e, 0xfb, 0x81, 0xd0, 0xbc,
+	0x65, 0xa2, 0xb3, 0x80, 0xf0, 0x8f, 0x73, 0x2f, 0xb5, 0xc4, 0xc0, 0x03,
+	0xac, 0x24, 0xfa, 0x4f, 0x05, 0xe3, 0x16, 0x29, 0x7c, 0xff, 0x63, 0xd5,
+	0x0a, 0x0a, 0xbd, 0xd7, 0x65, 0x3a, 0xb5, 0x5e, 0xe7, 0x23, 0xfc, 0x3d,
+	0x08, 0xea, 0x62, 0xc2, 0xe3, 0x4a, 0xbb, 0x86, 0x5a, 0xc5, 0x93, 0xd7,
+	0xbc, 0xb4, 0x4c, 0xcf, 0xcf, 0x78, 0x9f, 0x52, 0x6e, 0xa1, 0x49, 0x21,
+	0xf7, 0x20, 0x42
+};
+static const unsigned int privk_len = sizeof(privk);
+
+/* Signature data (a secure hash, possibly signed) */
+struct vb2_signature {
+        /* Offset of signature data from start of this struct */
+        uint32_t sig_offset;
+        uint32_t reserved0;
+
+        /* Size of signature data in bytes */
+        uint32_t sig_size;
+        uint32_t reserved1;
+
+        /* Size of the data block which was signed in bytes */
+        uint32_t data_size;
+        uint32_t reserved2;
+} __attribute__((packed));
+
+/*
+ * Preamble block for kernel, version 2.2
+ *
+ * This should be followed by:
+ *   1) The signature data for the kernel body, pointed to by
+ *      body_signature.sig_offset.
+ *   2) The signature data for (vb2_kernel_preamble + body signature data),
+ *       pointed to by preamble_signature.sig_offset.
+ *   3) The 16-bit vmlinuz header, which is used for reconstruction of
+ *      vmlinuz image.
+ */
+struct vb2_kernel_preamble {
+	/*
+	 * Size of this preamble, including keys, signatures, vmlinuz header,
+	 * and padding, in bytes
+	 */
+	uint32_t preamble_size;
+	uint32_t reserved0;
+
+	/* Signature for this preamble (header + body signature) */
+	struct vb2_signature preamble_signature;
+
+	/* Version of this header format */
+	uint32_t header_version_major;
+	uint32_t header_version_minor;
+
+	/* Kernel version */
+	uint32_t kernel_version;
+	uint32_t reserved1;
+
+	/* Load address for kernel body */
+	uint64_t body_load_address;
+
+	/* Address of bootloader, after body is loaded at body_load_address */
+	uint64_t bootloader_address;
+
+	/* Size of bootloader in bytes */
+	uint32_t bootloader_size;
+	uint32_t reserved2;
+
+	/* Signature for the kernel body */
+	struct vb2_signature body_signature;
+
+	/* Address of 16-bit header for vmlinuz reassembly. */
+	uint64_t vmlinuz_header_address;
+
+	/* Size of 16-bit header for vmlinuz in bytes. */
+	uint32_t vmlinuz_header_size;
+	uint32_t reserved3;
+
+	uint32_t flags;
+} __attribute__((packed));
+
+/*
+ * Order matters. |data| may overlap with |siginfo|, so we need to fill out
+ * |siginfo| before computing the signature.
+ */
+static int sign(const void *data, size_t len, struct vb2_signature *siginfo,
+		void *sigout)
+{
+	unsigned char hash[SHA256_DIGEST_LENGTH];
+	SHA256_CTX sha256;
+	int ret;
+
+	memset(siginfo, 0, sizeof(*siginfo));
+	siginfo->sig_offset = (uintptr_t)(void *)sigout - (uintptr_t)(void *)siginfo;
+	siginfo->sig_size = SIG_SIZE;
+	siginfo->data_size = len;
+
+	const unsigned char *p = privk;
+	char digest_info[] = {
+		0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+		0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20,
+	};
+
+	uint8_t digest[sizeof(digest_info) + sizeof(hash)];
+
+	SHA256_Init(&sha256);
+	SHA256_Update(&sha256, data, len);
+	SHA256_Final(hash, &sha256);
+
+	memcpy(digest, digest_info, sizeof(digest_info));
+	memcpy(digest + sizeof(digest_info), hash, sizeof(hash));
+
+	RSA *key = d2i_RSAPrivateKey(0, &p, privk_len);
+	if (!key) {
+		fprintf(stderr, "Failed d2i_RSAPrivateKey()\n");
+		return -1;
+	}
+
+	ret = RSA_private_encrypt(sizeof(digest), digest, sigout, key,
+				  RSA_PKCS1_PADDING);
+	if (ret == -1) {
+		fprintf(stderr, "%s: RSA_private_encrypt() failed\n", __func__);
+		return -1;
+	}
+
+	return 0;
+}
+
+static struct vb2_kernel_preamble *
+generate_preamble(const void *kblob, size_t kblob_len,
+		  size_t kernel_len, size_t config_len)
+{
+	struct vb2_kernel_preamble *h;
+	uint32_t signed_size = sizeof(struct vb2_kernel_preamble) + SIG_SIZE;
+	uint32_t block_size = signed_size + SIG_SIZE;
+	int ret;
+
+	if (block_size + keyblock_len < BLOCK_PAD)
+		block_size = BLOCK_PAD - keyblock_len;
+	h = (struct vb2_kernel_preamble *)calloc(block_size, 1);
+	if (!h) {
+		perror("calloc");
+		return NULL;
+	}
+
+	/* Sign the body, place the signature in the preamble */
+	ret = sign(kblob, kblob_len, &h->body_signature, h + 1);
+	if (ret) {
+		fprintf(stderr, "sign() failed: %d\n", ret);
+		return NULL;
+	}
+
+	h->header_version_major = 2;
+	h->header_version_minor = 2;
+	h->preamble_size = block_size;
+	h->kernel_version = 1;
+	h->body_load_address = 0x100000;
+	/*
+	 * "Bootloder" area (currently empty) is after kernel + config +
+	 * parameter page.
+	 */
+	h->bootloader_address = h->body_load_address +
+				ROUNDUP(kernel_len) +
+				ROUNDUP(config_len) +
+				ALIGN;
+	h->bootloader_size = 0x1000;
+	h->vmlinuz_header_address = 0;
+	h->vmlinuz_header_size = 0;
+	h->flags = 0;
+
+	ret = sign(h, signed_size, &h->preamble_signature,
+		   (void *)(h + 1) + SIG_SIZE);
+	if (ret) {
+		fprintf(stderr, "failed to sign preamble: %d\n", ret);
+		return NULL;
+	}
+	return h;
+}
+
+
+/*
+ * Distilled from vboot_reference futility/cmd_vbutil_kernel.c pack command.
+ *
+ * NB: "config" is the kernel cmdline
+ */
+static int vbutil_pack(const char *kernel, size_t kernel_len,
+		       const char *config, size_t config_len,
+		       void **out, size_t *out_len)
+{
+	int ret;
+	size_t kblob_len = ROUNDUP(kernel_len) + ROUNDUP(config_len) + ALIGN + ALIGN;
+	void *kblob = malloc(kblob_len);
+	if (!kblob) {
+		perror("malloc");
+		return -1;
+	}
+	void *kp = kblob;
+	memset(kblob, 0, kblob_len);
+
+	/* Kernel */
+	memcpy(kp, kernel, kernel_len);
+	kp += ROUNDUP(kernel_len);
+
+	/* Kernel command line */
+	memcpy(kp, config, config_len);
+	kp += ROUNDUP(config_len);
+
+	/* "Parameters" -- empty, 4K-aligned */
+	/* "Bootloader" region -- empty, 4K-aligned */
+	/* Vmlinuz header -- only for x86 (not currently supported), empty */
+
+	struct vb2_kernel_preamble *h;
+	h = generate_preamble(kblob, kblob_len, kernel_len, config_len);
+	if (!h) {
+		fprintf(stderr, "Failed to generate preamble\n");
+		return -1;
+	}
+	size_t outlen = sizeof(keyblock) + h->preamble_size + kblob_len;
+	void *outbuf = malloc(outlen);
+	if (!outbuf)
+		return -1;
+
+	void *p = outbuf;
+	memcpy(p, keyblock, sizeof(keyblock));
+	p += sizeof(keyblock);
+
+	memcpy(p, h, h->preamble_size);
+	p += h->preamble_size;
+
+	memcpy(p, kblob, kblob_len);
+	p += kblob_len;
+
+	if (outbuf) {
+		*out = outbuf;
+		*out_len = outlen;
+	}
+
+	return 0;
+}
+
+static void * read_file(const char *path, size_t *out_len)
+{
+	struct stat st;
+	int fd = open(path, O_RDONLY);
+	if (fd == -1) {
+		perror("failed to open");
+		return NULL;
+	}
+
+	if (fstat(fd, &st) < 0) {
+		perror("fstat");
+		return NULL;
+	}
+
+	size_t len = st.st_size;
+	void *p = malloc(len);
+	if (!p)
+		return NULL;
+
+	size_t count = 0;
+	while (count < len) {
+		ssize_t ret = read(fd, p + count, len - count);
+		if (ret < 0) {
+			perror("read");
+			return NULL;
+		}
+		count += ret;
+	}
+
+	close(fd);
+
+	*out_len = len;
+	return p;
+}
+
+int main(int argc, char * const argv[])
+{
+	int i, ret;
+	const char *kernel_file = NULL;
+	const char *out_file = NULL;
+	const char *cmdline = NULL;
+	size_t cmdline_len;
+	void *kernel;
+	size_t kernel_len;
+	void *out;
+	size_t out_len;
+
+	int opt;
+	while ((opt = getopt(argc, argv, "k:c:o:")) != -1) {
+		switch (opt) {
+		case 'k':
+			kernel_file = optarg;
+			break;
+		case 'c':
+			cmdline = optarg;
+			break;
+		case 'o':
+			out_file = optarg;
+			break;
+		default:
+			fprintf(stderr, "Usage [-k <kernel>] [-c <command line>] -o <outfile>\n");
+			return -1;
+		}
+	}
+	if (optind < argc) {
+		fprintf(stderr, "Unexpected args?\n");
+		return -1;
+	}
+	if (!out_file || !cmdline || !kernel_file) {
+		fprintf(stderr, "Missing required argument\n");
+		return -1;
+	}
+
+	/* Include the \0 terminator */
+	cmdline_len = strlen(cmdline) + 1;
+
+	kernel = read_file(kernel_file, &kernel_len);
+	if (!kernel) {
+		fprintf(stderr, "failed to read kernel file: %s\n", kernel_file);
+		return -1;
+	}
+
+	ret = vbutil_pack(kernel, kernel_len, cmdline, cmdline_len, &out, &out_len);
+	if (ret)
+		return ret;
+
+	int fd = open(out_file, O_RDWR|O_CREAT, 0644);
+	if (fd == -1) {
+		perror("open");
+		return -1;
+	}
+
+	ret = write(fd, out, out_len);
+	if (ret == -1) {
+		perror("write");
+		return -1;
+	}
+	close(fd);
+
+	return 0;
+}