summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2015-05-12 16:39:01 -0700
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-05-21 03:44:13 +0000
commit2d25e837ccc1c4f3123fedc056a396b3e6e3aa5f (patch)
tree54ce9bede8f2b3cc08e2304e3acb90d4d61972f0
parentea71df260e9041dd5260425dae3d6f5412516b9b (diff)
downloadvboot-2d25e837ccc1c4f3123fedc056a396b3e6e3aa5f.tar.gz
vboot2: Add routine to verify kernel preamble
This also checks that the bootloader and vmlinuz headers, if present, are within the signed part of the kernel blob; the vboot1 routines didn't do that. That wasn't harmful at firmware boot time because the vboot1 routines would only load as much data as was signed, but in vboot2 loading the kernel data is the responsibility of the caller so we need to check. BUG=chromium:487699 BRANCH=none TEST=make -j runtests Change-Id: I73eb4831e5d3d7a642b6cb85cb55857d87fcc0af Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/270797
-rw-r--r--Makefile1
-rw-r--r--firmware/2lib/include/2return_codes.h6
-rw-r--r--firmware/lib20/include/vb2_common.h16
-rw-r--r--firmware/lib20/include/vb2_struct.h88
-rw-r--r--firmware/lib20/kernel.c112
-rw-r--r--tests/vb20_common3_tests.c182
6 files changed, 402 insertions, 3 deletions
diff --git a/Makefile b/Makefile
index aa82aa2a..0e96c5c9 100644
--- a/Makefile
+++ b/Makefile
@@ -340,6 +340,7 @@ FWLIB2X_SRCS = \
FWLIB20_SRCS = \
firmware/lib20/api.c \
firmware/lib20/common.c \
+ firmware/lib20/kernel.c \
firmware/lib20/misc.c \
firmware/lib20/packed_key.c
diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h
index cd8cc270..671e3d27 100644
--- a/firmware/2lib/include/2return_codes.h
+++ b/firmware/2lib/include/2return_codes.h
@@ -341,6 +341,12 @@ enum vb2_return_code {
/* Hash is signed */
VB2_ERROR_PREAMBLE_HASH_SIGNED,
+ /* Bootloader outside signed portion of body */
+ VB2_ERROR_PREAMBLE_BOOTLOADER_OUTSIDE,
+
+ /* Vmlinuz header outside signed portion of body */
+ VB2_ERROR_PREAMBLE_VMLINUZ_HEADER_OUTSIDE,
+
/**********************************************************************
* Misc higher-level code errors
*/
diff --git a/firmware/lib20/include/vb2_common.h b/firmware/lib20/include/vb2_common.h
index 4067d694..a71cfe6c 100644
--- a/firmware/lib20/include/vb2_common.h
+++ b/firmware/lib20/include/vb2_common.h
@@ -156,4 +156,20 @@ int vb2_verify_fw_preamble(struct vb2_fw_preamble *preamble,
const struct vb2_public_key *key,
const struct vb2_workbuf *wb);
+/**
+ * Check the sanity of a kernel preamble using a public key.
+ *
+ * The signature in the preamble is destroyed during the check.
+ *
+ * @param preamble Preamble to verify
+ * @param size Size of preamble buffer
+ * @param key Key to use to verify preamble
+ * @param wb Work buffer
+ * @return VB2_SUCCESS, or non-zero error code if error.
+ */
+int vb2_verify_kernel_preamble(struct vb2_kernel_preamble *preamble,
+ uint32_t size,
+ const struct vb2_public_key *key,
+ const struct vb2_workbuf *wb);
+
#endif /* VBOOT_REFERENCE_VB2_COMMON_H_ */
diff --git a/firmware/lib20/include/vb2_struct.h b/firmware/lib20/include/vb2_struct.h
index ec28e719..d3e2f400 100644
--- a/firmware/lib20/include/vb2_struct.h
+++ b/firmware/lib20/include/vb2_struct.h
@@ -81,8 +81,6 @@ struct vb2_keyblock {
/* Version of this header format */
uint32_t header_version_major;
-
- /* Version of this header format */
uint32_t header_version_minor;
/*
@@ -121,7 +119,7 @@ struct vb2_keyblock {
#define FIRMWARE_PREAMBLE_HEADER_VERSION_MAJOR 2
#define FIRMWARE_PREAMBLE_HEADER_VERSION_MINOR 1
-/* Flags for VbFirmwarePreambleHeader.flags */
+/* Flags for vb2_fw_preamble.flags */
/* Reserved; do not use */
#define VB2_FIRMWARE_PREAMBLE_RESERVED0 0x00000001
/* Do not allow use of any hardware crypto accelerators. */
@@ -178,4 +176,88 @@ struct vb2_fw_preamble {
#define EXPECTED_VB2_FW_PREAMBLE_SIZE 108
+/* Kernel preamble header */
+#define KERNEL_PREAMBLE_HEADER_VERSION_MAJOR 2
+#define KERNEL_PREAMBLE_HEADER_VERSION_MINOR 2
+
+/* Flags for vb2_kernel_preamble.flags */
+/* Kernel image type = bits 1:0 */
+#define VB2_KERNEL_PREAMBLE_KERNEL_TYPE_MASK 0x00000003
+#define VB2_KERNEL_PREAMBLE_KERNEL_TYPE_CROS 0
+#define VB2_KERNEL_PREAMBLE_KERNEL_TYPE_BOOTIMG 1
+/* Kernel types 2,3 are reserved for future use */
+
+/*
+ * 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;
+
+ /*
+ * Fields added in header version 2.1. You must verify the header
+ * version before reading these fields!
+ */
+
+ /*
+ * Address of 16-bit header for vmlinuz reassembly. Readers should
+ * return 0 for header version < 2.1.
+ */
+ uint64_t vmlinuz_header_address;
+
+ /* Size of 16-bit header for vmlinuz in bytes. Readers should return 0
+ for header version < 2.1 */
+ uint32_t vmlinuz_header_size;
+ uint32_t reserved3;
+
+ /*
+ * Fields added in header version 2.2. You must verify the header
+ * version before reading these fields!
+ */
+
+ /*
+ * Flags; see VB2_KERNEL_PREAMBLE_*. Readers should return 0 for
+ * header version < 2.2.
+ */
+ uint32_t flags;
+} __attribute__((packed));
+
+#define EXPECTED_VB2_KERNEL_PREAMBLE_SIZE 116
+
#endif /* VBOOT_REFERENCE_VB2_STRUCT_H_ */
diff --git a/firmware/lib20/kernel.c b/firmware/lib20/kernel.c
new file mode 100644
index 00000000..ec47ce77
--- /dev/null
+++ b/firmware/lib20/kernel.c
@@ -0,0 +1,112 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Kernel verified boot functions
+ */
+
+#include "2sysincludes.h"
+#include "2rsa.h"
+#include "2sha.h"
+#include "vb2_common.h"
+
+int vb2_verify_kernel_preamble(struct vb2_kernel_preamble *preamble,
+ uint32_t size,
+ const struct vb2_public_key *key,
+ const struct vb2_workbuf *wb)
+{
+ struct vb2_signature *sig = &preamble->preamble_signature;
+
+ VB2_DEBUG("Verifying kernel preamble.\n");
+
+ /* Sanity checks before attempting signature of data */
+ if(size < sizeof(*preamble)) {
+ VB2_DEBUG("Not enough data for preamble header.\n");
+ return VB2_ERROR_PREAMBLE_TOO_SMALL_FOR_HEADER;
+ }
+ if (preamble->header_version_major !=
+ KERNEL_PREAMBLE_HEADER_VERSION_MAJOR) {
+ VB2_DEBUG("Incompatible kernel preamble header version.\n");
+ return VB2_ERROR_PREAMBLE_HEADER_VERSION;
+ }
+ if (preamble->header_version_minor < 2) {
+ VB2_DEBUG("Old preamble header format not supported\n");
+ return VB2_ERROR_PREAMBLE_HEADER_OLD;
+ }
+ if (size < preamble->preamble_size) {
+ VB2_DEBUG("Not enough data for preamble.\n");
+ return VB2_ERROR_PREAMBLE_SIZE;
+ }
+
+ /* Check signature */
+ if (vb2_verify_signature_inside(preamble, preamble->preamble_size,
+ sig)) {
+ VB2_DEBUG("Preamble signature off end of preamble\n");
+ return VB2_ERROR_PREAMBLE_SIG_OUTSIDE;
+ }
+
+ /* Make sure advertised signature data sizes are sane. */
+ if (preamble->preamble_size < sig->data_size) {
+ VB2_DEBUG("Signature calculated past end of the block\n");
+ return VB2_ERROR_PREAMBLE_SIGNED_TOO_MUCH;
+ }
+
+ if (vb2_verify_data((const uint8_t *)preamble, size, sig, key, wb)) {
+ VB2_DEBUG("Preamble signature validation failed\n");
+ return VB2_ERROR_PREAMBLE_SIG_INVALID;
+ }
+
+ /* Verify we signed enough data */
+ if (sig->data_size < sizeof(struct vb2_fw_preamble)) {
+ VB2_DEBUG("Didn't sign enough data\n");
+ return VB2_ERROR_PREAMBLE_SIGNED_TOO_LITTLE;
+ }
+
+ /* Verify body signature is inside the signed data */
+ if (vb2_verify_signature_inside(preamble, sig->data_size,
+ &preamble->body_signature)) {
+ VB2_DEBUG("Body signature off end of preamble\n");
+ return VB2_ERROR_PREAMBLE_BODY_SIG_OUTSIDE;
+ }
+
+ /*
+ * If bootloader is present, verify it's covered by the body
+ * signature.
+ */
+ if (preamble->bootloader_size) {
+ const void *body_ptr =
+ (const void *)(uintptr_t)preamble->body_load_address;
+ const void *bootloader_ptr =
+ (const void *)(uintptr_t)preamble->bootloader_address;
+ if (vb2_verify_member_inside(body_ptr,
+ preamble->body_signature.data_size,
+ bootloader_ptr,
+ preamble->bootloader_size,
+ 0, 0)) {
+ VB2_DEBUG("Bootloader off end of signed data\n");
+ return VB2_ERROR_PREAMBLE_BOOTLOADER_OUTSIDE;
+ }
+ }
+
+ /*
+ * If vmlinuz header is present, verify it's covered by the body
+ * signature.
+ */
+ if (preamble->vmlinuz_header_size) {
+ const void *body_ptr =
+ (const void *)(uintptr_t)preamble->body_load_address;
+ const void *vmlinuz_header_ptr = (const void *)
+ (uintptr_t)preamble->vmlinuz_header_address;
+ if (vb2_verify_member_inside(body_ptr,
+ preamble->body_signature.data_size,
+ vmlinuz_header_ptr,
+ preamble->vmlinuz_header_size,
+ 0, 0)) {
+ VB2_DEBUG("Vmlinuz header off end of signed data\n");
+ return VB2_ERROR_PREAMBLE_VMLINUZ_HEADER_OUTSIDE;
+ }
+ }
+
+ /* Success */
+ return VB2_SUCCESS;
+}
diff --git a/tests/vb20_common3_tests.c b/tests/vb20_common3_tests.c
index 9d4cc50b..b41300c7 100644
--- a/tests/vb20_common3_tests.c
+++ b/tests/vb20_common3_tests.c
@@ -300,6 +300,187 @@ static void test_verify_fw_preamble(const VbPublicKey *public_key,
free(hdr);
}
+static void resign_kernel_preamble(struct vb2_kernel_preamble *h,
+ const VbPrivateKey *key)
+{
+ VbSignature *sig = CalculateSignature(
+ (const uint8_t *)h, h->preamble_signature.data_size, key);
+
+ SignatureCopy((VbSignature *)&h->preamble_signature, sig);
+ free(sig);
+}
+
+static void test_verify_kernel_preamble(const VbPublicKey *public_key,
+ const VbPrivateKey *private_key)
+{
+ struct vb2_kernel_preamble *hdr;
+ struct vb2_kernel_preamble *h;
+ struct vb2_public_key rsa;
+ // TODO: how many workbuf bytes?
+ uint8_t workbuf[VB2_VERIFY_FIRMWARE_PREAMBLE_WORKBUF_BYTES]
+ __attribute__ ((aligned (VB2_WORKBUF_ALIGN)));
+ struct vb2_workbuf wb;
+ uint32_t hsize;
+
+ vb2_workbuf_init(&wb, workbuf, sizeof(workbuf));
+
+ /* Create a dummy signature */
+ VbSignature *body_sig = SignatureAlloc(56, 0x214000);
+
+ TEST_SUCC(vb2_unpack_key(&rsa, (uint8_t *)public_key,
+ public_key->key_offset + public_key->key_size),
+ "vb2_verify_kernel_preamble() prereq key");
+
+ hdr = (struct vb2_kernel_preamble *)
+ CreateKernelPreamble(0x1234, 0x100000, 0x300000, 0x4000,
+ body_sig, 0x304000, 0x10000, 0, 0,
+ private_key);
+ TEST_PTR_NEQ(hdr, NULL,
+ "vb2_verify_kernel_preamble() prereq test preamble");
+ if (!hdr)
+ return;
+ hsize = (uint32_t) hdr->preamble_size;
+ h = (struct vb2_kernel_preamble *)malloc(hsize + 16384);
+
+ Memcpy(h, hdr, hsize);
+ TEST_SUCC(vb2_verify_kernel_preamble(h, hsize, &rsa, &wb),
+ "vb2_verify_kernel_preamble() ok using key");
+
+ Memcpy(h, hdr, hsize);
+ TEST_EQ(vb2_verify_kernel_preamble(h, 4, &rsa, &wb),
+ VB2_ERROR_PREAMBLE_TOO_SMALL_FOR_HEADER,
+ "vb2_verify_kernel_preamble() size tiny");
+
+ Memcpy(h, hdr, hsize);
+ TEST_EQ(vb2_verify_kernel_preamble(h, hsize - 1, &rsa, &wb),
+ VB2_ERROR_PREAMBLE_SIZE,
+ "vb2_verify_kernel_preamble() size--");
+
+ /* Buffer is allowed to be bigger than preamble */
+ Memcpy(h, hdr, hsize);
+ TEST_SUCC(vb2_verify_kernel_preamble(h, hsize + 1, &rsa, &wb),
+ "vb2_verify_kernel_preamble() size++");
+
+ /* Care about major version but not minor */
+ Memcpy(h, hdr, hsize);
+ h->header_version_major++;
+ resign_kernel_preamble(h, private_key);
+ TEST_EQ(vb2_verify_kernel_preamble(h, hsize, &rsa, &wb),
+ VB2_ERROR_PREAMBLE_HEADER_VERSION
+ , "vb2_verify_kernel_preamble() major++");
+
+ Memcpy(h, hdr, hsize);
+ h->header_version_major--;
+ resign_kernel_preamble(h, private_key);
+ TEST_EQ(vb2_verify_kernel_preamble(h, hsize, &rsa, &wb),
+ VB2_ERROR_PREAMBLE_HEADER_VERSION,
+ "vb2_verify_kernel_preamble() major--");
+
+ Memcpy(h, hdr, hsize);
+ h->header_version_minor++;
+ resign_kernel_preamble(h, private_key);
+ TEST_SUCC(vb2_verify_kernel_preamble(h, hsize, &rsa, &wb),
+ "vb2_verify_kernel_preamble() minor++");
+
+ Memcpy(h, hdr, hsize);
+ h->header_version_minor--;
+ resign_kernel_preamble(h, private_key);
+ TEST_EQ(vb2_verify_kernel_preamble(h, hsize, &rsa, &wb),
+ VB2_ERROR_PREAMBLE_HEADER_OLD,
+ "vb2_verify_kernel_preamble() 2.1 not supported");
+
+ /* Check signature */
+ Memcpy(h, hdr, hsize);
+ h->preamble_signature.sig_offset = hsize;
+ resign_kernel_preamble(h, private_key);
+ TEST_EQ(vb2_verify_kernel_preamble(h, hsize, &rsa, &wb),
+ VB2_ERROR_PREAMBLE_SIG_OUTSIDE,
+ "vb2_verify_kernel_preamble() sig off end");
+
+ Memcpy(h, hdr, hsize);
+ h->preamble_signature.sig_size--;
+ resign_kernel_preamble(h, private_key);
+ TEST_EQ(vb2_verify_kernel_preamble(h, hsize, &rsa, &wb),
+ VB2_ERROR_PREAMBLE_SIG_INVALID,
+ "vb2_verify_kernel_preamble() sig too small");
+
+ Memcpy(h, hdr, hsize);
+ h->flags++;
+ TEST_EQ(vb2_verify_kernel_preamble(h, hsize, &rsa, &wb),
+ VB2_ERROR_PREAMBLE_SIG_INVALID,
+ "vb2_verify_kernel_preamble() sig mismatch");
+
+ /* Check that we signed header and body sig */
+ Memcpy(h, hdr, hsize);
+ h->preamble_signature.data_size = 4;
+ h->body_signature.sig_offset = 0;
+ h->body_signature.sig_size = 0;
+ resign_kernel_preamble(h, private_key);
+ TEST_EQ(vb2_verify_kernel_preamble(h, hsize, &rsa, &wb),
+ VB2_ERROR_PREAMBLE_SIGNED_TOO_LITTLE,
+ "vb2_verify_kernel_preamble() didn't sign header");
+
+ Memcpy(h, hdr, hsize);
+ h->body_signature.sig_offset = hsize;
+ resign_kernel_preamble(h, private_key);
+ TEST_EQ(vb2_verify_kernel_preamble(h, hsize, &rsa, &wb),
+ VB2_ERROR_PREAMBLE_BODY_SIG_OUTSIDE,
+ "vb2_verify_kernel_preamble() body sig off end");
+
+ /* Check bootloader inside signed body */
+ Memcpy(h, hdr, hsize);
+ h->bootloader_address = h->body_load_address - 1;
+ resign_kernel_preamble(h, private_key);
+ TEST_EQ(vb2_verify_kernel_preamble(h, hsize, &rsa, &wb),
+ VB2_ERROR_PREAMBLE_BOOTLOADER_OUTSIDE,
+ "vb2_verify_kernel_preamble() bootloader before body");
+
+ Memcpy(h, hdr, hsize);
+ h->bootloader_address = h->body_load_address +
+ h->body_signature.data_size + 1;
+ resign_kernel_preamble(h, private_key);
+ TEST_EQ(vb2_verify_kernel_preamble(h, hsize, &rsa, &wb),
+ VB2_ERROR_PREAMBLE_BOOTLOADER_OUTSIDE,
+ "vb2_verify_kernel_preamble() bootloader off end of body");
+
+ Memcpy(h, hdr, hsize);
+ h->bootloader_address = h->body_load_address +
+ h->body_signature.data_size + 1;
+ h->bootloader_size = 0;
+ resign_kernel_preamble(h, private_key);
+ TEST_SUCC(vb2_verify_kernel_preamble(h, hsize, &rsa, &wb),
+ "vb2_verify_kernel_preamble() no bootloader");
+
+ /* Check vmlinuz inside signed body */
+ Memcpy(h, hdr, hsize);
+ h->vmlinuz_header_address = h->body_load_address - 1;
+ resign_kernel_preamble(h, private_key);
+ TEST_EQ(vb2_verify_kernel_preamble(h, hsize, &rsa, &wb),
+ VB2_ERROR_PREAMBLE_VMLINUZ_HEADER_OUTSIDE,
+ "vb2_verify_kernel_preamble() vmlinuz_header before body");
+
+ Memcpy(h, hdr, hsize);
+ h->vmlinuz_header_address = h->body_load_address +
+ h->body_signature.data_size + 1;
+ resign_kernel_preamble(h, private_key);
+ TEST_EQ(vb2_verify_kernel_preamble(h, hsize, &rsa, &wb),
+ VB2_ERROR_PREAMBLE_VMLINUZ_HEADER_OUTSIDE,
+ "vb2_verify_kernel_preamble() vmlinuz_header off end of body");
+
+ Memcpy(h, hdr, hsize);
+ h->vmlinuz_header_address = h->body_load_address +
+ h->body_signature.data_size + 1;
+ h->vmlinuz_header_size = 0;
+ resign_kernel_preamble(h, private_key);
+ TEST_SUCC(vb2_verify_kernel_preamble(h, hsize, &rsa, &wb),
+ "vb2_verify_kernel_preamble() no vmlinuz_header");
+
+ /* TODO: verify with extra padding at end of header. */
+
+ free(h);
+ free(hdr);
+}
+
int test_permutation(int signing_key_algorithm, int data_key_algorithm,
const char *keys_dir)
{
@@ -347,6 +528,7 @@ int test_permutation(int signing_key_algorithm, int data_key_algorithm,
data_public_key);
test_verify_fw_preamble(signing_public_key, signing_private_key,
data_public_key);
+ test_verify_kernel_preamble(signing_public_key, signing_private_key);
if (signing_public_key)
free(signing_public_key);