diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | firmware/2lib/include/2return_codes.h | 6 | ||||
-rw-r--r-- | firmware/lib20/include/vb2_common.h | 16 | ||||
-rw-r--r-- | firmware/lib20/include/vb2_struct.h | 88 | ||||
-rw-r--r-- | firmware/lib20/kernel.c | 112 | ||||
-rw-r--r-- | tests/vb20_common3_tests.c | 182 |
6 files changed, 402 insertions, 3 deletions
@@ -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); |