summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);