diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | firmware/include/vboot_api.h | 21 | ||||
-rw-r--r-- | firmware/lib/vboot_api_kernel.c | 136 | ||||
-rw-r--r-- | tests/vboot_api_kernel5_tests.c | 281 |
4 files changed, 440 insertions, 0 deletions
@@ -685,6 +685,7 @@ TEST_NAMES = \ tests/vboot_api_kernel2_tests \ tests/vboot_api_kernel3_tests \ tests/vboot_api_kernel4_tests \ + tests/vboot_api_kernel5_tests \ tests/vboot_audio_tests \ tests/vboot_common_tests \ tests/vboot_common2_tests \ @@ -1363,6 +1364,7 @@ runmisctests: test_setup ${RUNTEST} ${BUILD_RUN}/tests/vboot_api_kernel2_tests ${RUNTEST} ${BUILD_RUN}/tests/vboot_api_kernel3_tests ${RUNTEST} ${BUILD_RUN}/tests/vboot_api_kernel4_tests + ${RUNTEST} ${BUILD_RUN}/tests/vboot_api_kernel5_tests ${RUNTEST} ${BUILD_RUN}/tests/vboot_audio_tests ${RUNTEST} ${BUILD_RUN}/tests/vboot_common_tests ${RUNTEST} ${BUILD_RUN}/tests/vboot_common2_tests ${TEST_KEYS} diff --git a/firmware/include/vboot_api.h b/firmware/include/vboot_api.h index a818a7ca..e66a2fba 100644 --- a/firmware/include/vboot_api.h +++ b/firmware/include/vboot_api.h @@ -1004,4 +1004,25 @@ VbError_t VbExRegionRead(VbCommonParams *cparams, enum vb_firmware_region region, uint32_t offset, uint32_t size, void *buf); +/** + * Verify Kernel Image loaded in memory. + * + * This routine is used by fastboot boot command to verify the kernel image in + * memory sent by the host device using fastboot protocol. It checks if the + * image in memory is signed using official recovery keys. In case of GBB + * override to allow full fastboot functionality, it checks image integrity, but + * does not check the image signature. + * + * @param cparams Common parameters, e.g. use member caller_context + * to point to useful context data + * @param kparams kernel params + * @param boot_image Image in memory that needs to be verified + * @param image_size Size of the image in memory + * @return VBERROR_... error, VBERROR_SUCCESS on success. + */ +VbError_t VbVerifyMemoryBootImage(VbCommonParams *cparams, + VbSelectAndLoadKernelParams *kparams, + void *boot_image, + size_t image_size); + #endif /* VBOOT_REFERENCE_VBOOT_API_H_ */ diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c index fc6df72e..f3ff3ea1 100644 --- a/firmware/lib/vboot_api_kernel.c +++ b/firmware/lib/vboot_api_kernel.c @@ -1194,3 +1194,139 @@ VbError_t VbSelectAndLoadKernel(VbCommonParams *cparams, /* Pass through return value from boot path */ return retval; } + +VbError_t VbVerifyMemoryBootImage(VbCommonParams *cparams, + VbSelectAndLoadKernelParams *kparams, + void *boot_image, + size_t image_size) +{ + VbError_t retval; + VbPublicKey* kernel_subkey = NULL; + uint8_t *kbuf; + VbKeyBlockHeader *key_block; + VbSharedDataHeader *shared = + (VbSharedDataHeader *)cparams->shared_data_blob; + RSAPublicKey *data_key = NULL; + VbKernelPreambleHeader *preamble; + uint64_t body_offset; + int hash_only = 0; + int dev_switch; + + if ((boot_image == NULL) || (image_size == 0)) + return VBERROR_INVALID_PARAMETER; + + /* Clear output params in case we fail. */ + kparams->disk_handle = NULL; + kparams->partition_number = 0; + kparams->bootloader_address = 0; + kparams->bootloader_size = 0; + kparams->flags = 0; + Memset(kparams->partition_guid, 0, sizeof(kparams->partition_guid)); + + /* Populate pointers to all components in the image. */ + kbuf = boot_image; + key_block = (VbKeyBlockHeader *)kbuf; + preamble = (VbKernelPreambleHeader *)(kbuf + key_block->key_block_size); + body_offset = key_block->key_block_size + preamble->preamble_size; + + /* Read GBB Header */ + cparams->bmp = NULL; + cparams->gbb = VbExMalloc(sizeof(*cparams->gbb)); + retval = VbGbbReadHeader_static(cparams, cparams->gbb); + if (VBERROR_SUCCESS != retval) { + VBDEBUG(("Gbb read header failed.\n")); + return retval; + } + + /* + * We don't care verifying the image if: + * 1. dev-mode switch is on and + * 2. GBB_FLAG_FORCE_DEV_BOOT_FASTBOOT_FULL_CAP is set. + * + * Check only the integrity of the image. + */ + dev_switch = shared->flags & VBSD_BOOT_DEV_SWITCH_ON; + if (dev_switch && (cparams->gbb->flags & + GBB_FLAG_FORCE_DEV_BOOT_FASTBOOT_FULL_CAP)) { + VBDEBUG(("Only performing integrity-check.\n")); + hash_only = 1; + } else { + /* Get recovery key. */ + retval = VbGbbReadRecoveryKey(cparams, &kernel_subkey); + if (VBERROR_SUCCESS != retval) { + VBDEBUG(("Gbb Read Recovery key failed.\n")); + return retval; + } + } + + /* If we fail at any step, retval returned would be invalid kernel. */ + retval = VBERROR_INVALID_KERNEL_FOUND; + + /* Verify the key block. */ + if (0 != KeyBlockVerify(key_block, image_size, kernel_subkey, + hash_only)) { + VBDEBUG(("Verifying key block signature/hash failed.\n")); + goto fail; + } + + /* Check the key block flags against the current boot mode. */ + if (!(key_block->key_block_flags & + (dev_switch ? KEY_BLOCK_FLAG_DEVELOPER_1 : + KEY_BLOCK_FLAG_DEVELOPER_0))) { + VBDEBUG(("Key block developer flag mismatch.\n")); + if (hash_only == 0) + goto fail; + } + + if (!(key_block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_1)) { + VBDEBUG(("Key block recovery flag mismatch.\n")); + if (hash_only == 0) + goto fail; + } + + /* Get key for preamble/data verification from the key block. */ + data_key = PublicKeyToRSA(&key_block->data_key); + if (!data_key) { + VBDEBUG(("Data key bad.\n")); + goto fail; + } + + /* Verify the preamble, which follows the key block */ + if ((0 != VerifyKernelPreamble(preamble, + image_size - + key_block->key_block_size, + data_key))) { + VBDEBUG(("Preamble verification failed.\n")); + goto fail; + } + + VBDEBUG(("Kernel preamble is good.\n")); + + /* Verify kernel data */ + if (0 != VerifyData((const uint8_t *)(kbuf + body_offset), + image_size - body_offset, + &preamble->body_signature, data_key)) { + VBDEBUG(("Kernel data verification failed.\n")); + goto fail; + } + + VBDEBUG(("Kernel is good.\n")); + + /* Fill in output parameters. */ + kparams->kernel_buffer = kbuf + body_offset; + kparams->kernel_buffer_size = image_size - body_offset; + kparams->bootloader_address = preamble->bootloader_address; + kparams->bootloader_size = preamble->bootloader_size; + if (VbKernelHasFlags(preamble) == VBOOT_SUCCESS) + kparams->flags = preamble->flags; + + retval = VBERROR_SUCCESS; + +fail: + VbApiKernelFree(cparams); + if (NULL != data_key) + RSAPublicKeyFree(data_key); + if (NULL != kernel_subkey) + VbExFree(kernel_subkey); + return retval; +} diff --git a/tests/vboot_api_kernel5_tests.c b/tests/vboot_api_kernel5_tests.c new file mode 100644 index 00000000..8c59622f --- /dev/null +++ b/tests/vboot_api_kernel5_tests.c @@ -0,0 +1,281 @@ +/* Copyright (c) 2013 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. + * + * Tests for vboot_api_kernel.c + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "gbb_header.h" +#include "host_common.h" +#include "load_kernel_fw.h" +#include "test_common.h" +#include "vboot_api.h" +#include "vboot_common.h" +#include "vboot_kernel.h" +#include "vboot_nvstorage.h" +#include "vboot_struct.h" + +/* Mock data */ +static VbCommonParams cparams; +static VbSelectAndLoadKernelParams kparams; +static uint8_t shared_data[VB_SHARED_DATA_MIN_SIZE]; +static VbSharedDataHeader *shared = (VbSharedDataHeader *)shared_data; +static GoogleBinaryBlockHeader gbb; + +static uint8_t kernel_buffer[80000]; +static int key_block_verify_fail; /* 0=ok, 1=sig, 2=hash */ +static int preamble_verify_fail; +static int verify_data_fail; +static RSAPublicKey *mock_data_key; +static int mock_data_key_allocated; + +static VbNvContext vnc; +static VbKeyBlockHeader kbh; +static VbKernelPreambleHeader kph; + +static int hash_only_check; + +/** + * Reset mock data (for use before each test) + */ +static void ResetMocks(void) +{ + Memset(&cparams, 0, sizeof(cparams)); + cparams.shared_data_size = sizeof(shared_data); + cparams.shared_data_blob = shared_data; + cparams.gbb_data = &gbb; + cparams.gbb_size = sizeof(gbb); + + Memset(&kparams, 0, sizeof(kparams)); + + Memset(&gbb, 0, sizeof(gbb)); + gbb.major_version = GBB_MAJOR_VER; + gbb.minor_version = GBB_MINOR_VER; + gbb.flags = 0; + + memset(&vnc, 0, sizeof(vnc)); + VbNvSetup(&vnc); + VbNvTeardown(&vnc); /* So CRC gets generated */ + + memset(&shared_data, 0, sizeof(shared_data)); + VbSharedDataInit(shared, sizeof(shared_data)); + + key_block_verify_fail = 0; + preamble_verify_fail = 0; + verify_data_fail = 0; + + mock_data_key = (RSAPublicKey *)"TestDataKey"; + mock_data_key_allocated = 0; + + memset(&kbh, 0, sizeof(kbh)); + kbh.data_key.key_version = 2; + kbh.key_block_flags = -1; + kbh.key_block_size = sizeof(kbh); + + memset(&kph, 0, sizeof(kph)); + kph.kernel_version = 1; + kph.preamble_size = 4096 - kbh.key_block_size; + kph.body_signature.data_size = 70144; + kph.bootloader_address = 0xbeadd008; + kph.bootloader_size = 0x1234; + + memcpy(kernel_buffer, &kbh, sizeof(kbh)); + memcpy((kernel_buffer + kbh.key_block_size), &kph, sizeof(kph)); + + hash_only_check = -1; +} + +static void copy_kbh(void) +{ + memcpy(kernel_buffer, &kbh, sizeof(kbh)); +} + +/* Mocks */ +int KeyBlockVerify(const VbKeyBlockHeader *block, uint64_t size, + const VbPublicKey *key, int hash_only) { + hash_only_check = hash_only; + + if (key_block_verify_fail) + return VBERROR_SIMULATED; + + /* Use this as an opportunity to override the key block */ + memcpy((void *)block, &kbh, sizeof(kbh)); + return VBERROR_SUCCESS; +} + +RSAPublicKey *PublicKeyToRSA(const VbPublicKey *key) +{ + TEST_EQ(mock_data_key_allocated, 0, " mock data key not allocated"); + + if (mock_data_key) + mock_data_key_allocated++; + + return mock_data_key; +} + +void RSAPublicKeyFree(RSAPublicKey* key) +{ + TEST_EQ(mock_data_key_allocated, 1, " mock data key allocated"); + TEST_PTR_EQ(key, mock_data_key, " data key ptr"); + mock_data_key_allocated--; +} + +int VerifyKernelPreamble(const VbKernelPreambleHeader *preamble, + uint64_t size, const RSAPublicKey *key) +{ + if (preamble_verify_fail) + return VBERROR_SIMULATED; + + /* Use this as an opportunity to override the preamble */ + memcpy((void *)preamble, &kph, sizeof(kph)); + return VBERROR_SUCCESS; +} + +int VerifyData(const uint8_t *data, uint64_t size, const VbSignature *sig, + const RSAPublicKey *key) +{ + if (verify_data_fail) + return VBERROR_SIMULATED; + + return VBERROR_SUCCESS; +} + +static void VerifyMemoryBootImageTest(void) +{ + uint32_t u; + + int kernel_body_offset; + int kernel_body_size; + uintptr_t kernel_body_start; + size_t kernel_buffer_size = sizeof(kernel_buffer); + + ResetMocks(); + + kernel_body_offset = kbh.key_block_size + kph.preamble_size; + kernel_body_size = sizeof(kernel_buffer) - kernel_body_offset; + kernel_body_start = (uintptr_t)kernel_buffer + kernel_body_offset; + + u = VbVerifyMemoryBootImage(&cparams, &kparams, kernel_buffer, + kernel_buffer_size); + TEST_EQ(u, 0, "Image good"); + TEST_EQ(kparams.partition_number, 0, " part num"); + TEST_EQ(kparams.bootloader_address, 0xbeadd008, " bootloader addr"); + TEST_EQ(kparams.bootloader_size, 0x1234, " bootloader size"); + TEST_PTR_EQ(kparams.kernel_buffer, (void *)(kernel_body_start), + " kernel buffer"); + TEST_EQ(kparams.kernel_buffer_size, kernel_body_size, + " kernel buffer size"); + + /* Empty image buffer. */ + ResetMocks(); + TEST_EQ(VbVerifyMemoryBootImage(&cparams, &kparams, NULL, + kernel_buffer_size), + VBERROR_INVALID_PARAMETER, "Empty image"); + + /* Illegal image size. */ + ResetMocks(); + TEST_EQ(VbVerifyMemoryBootImage(&cparams, &kparams, kernel_buffer, 0), + VBERROR_INVALID_PARAMETER, "Illegal image size"); + + /* Key Block Verification Failure */ + ResetMocks(); + key_block_verify_fail = 1; + TEST_EQ(VbVerifyMemoryBootImage(&cparams, &kparams, kernel_buffer, + kernel_buffer_size), + VBERROR_INVALID_KERNEL_FOUND, "Key verify failed"); + TEST_EQ(hash_only_check, 0, " hash check"); + + /* Key Block Hash Failure */ + ResetMocks(); + shared->flags = VBSD_BOOT_DEV_SWITCH_ON; + gbb.flags = GBB_FLAG_FORCE_DEV_BOOT_FASTBOOT_FULL_CAP; + key_block_verify_fail = 1; + TEST_EQ(VbVerifyMemoryBootImage(&cparams, &kparams, kernel_buffer, + kernel_buffer_size), + VBERROR_INVALID_KERNEL_FOUND, "Key verify failed"); + TEST_EQ(hash_only_check, 1, " hash check"); + + /* Developer flag mismatch - dev switch on */ + ResetMocks(); + kbh.key_block_flags = KEY_BLOCK_FLAG_DEVELOPER_0 | + KEY_BLOCK_FLAG_RECOVERY_1; + copy_kbh(); + shared->flags = VBSD_BOOT_DEV_SWITCH_ON; + TEST_EQ(VbVerifyMemoryBootImage(&cparams, &kparams, kernel_buffer, + kernel_buffer_size), + VBERROR_INVALID_KERNEL_FOUND, + "Developer flag mismatch - dev switch on"); + + /* Developer flag mismatch - dev switch on with GBB override */ + ResetMocks(); + kbh.key_block_flags = KEY_BLOCK_FLAG_DEVELOPER_0 | + KEY_BLOCK_FLAG_RECOVERY_1; + copy_kbh(); + gbb.flags = GBB_FLAG_FORCE_DEV_BOOT_FASTBOOT_FULL_CAP; + shared->flags = VBSD_BOOT_DEV_SWITCH_ON; + TEST_EQ(VbVerifyMemoryBootImage(&cparams, &kparams, kernel_buffer, + kernel_buffer_size), + VBERROR_SUCCESS, + "Developer flag mismatch - dev switch on(gbb override)"); + + /* Recovery flag mismatch - dev switch on with GBB override */ + ResetMocks(); + kbh.key_block_flags = KEY_BLOCK_FLAG_DEVELOPER_0 | + KEY_BLOCK_FLAG_RECOVERY_0; + copy_kbh(); + shared->flags = VBSD_BOOT_DEV_SWITCH_ON; + gbb.flags = GBB_FLAG_FORCE_DEV_BOOT_FASTBOOT_FULL_CAP; + TEST_EQ(VbVerifyMemoryBootImage(&cparams, &kparams, kernel_buffer, + kernel_buffer_size), + VBERROR_SUCCESS, + "Recovery flag mismatch - dev switch on(gbb override)"); + + /* Developer flag mismatch - dev switch off */ + ResetMocks(); + kbh.key_block_flags = KEY_BLOCK_FLAG_DEVELOPER_1 | + KEY_BLOCK_FLAG_RECOVERY_1; + copy_kbh(); + TEST_EQ(VbVerifyMemoryBootImage(&cparams, &kparams, kernel_buffer, + kernel_buffer_size), + VBERROR_INVALID_KERNEL_FOUND, + "Developer flag mismatch - dev switch off"); + + /* Recovery flag mismatch */ + ResetMocks(); + kbh.key_block_flags = KEY_BLOCK_FLAG_DEVELOPER_0 | + KEY_BLOCK_FLAG_RECOVERY_0; + shared->flags = 0; + copy_kbh(); + TEST_EQ(VbVerifyMemoryBootImage(&cparams, &kparams, kernel_buffer, + kernel_buffer_size), + VBERROR_INVALID_KERNEL_FOUND, "Recovery flag mismatch"); + + /* Preamble verification */ + ResetMocks(); + preamble_verify_fail = 1; + TEST_EQ(VbVerifyMemoryBootImage(&cparams, &kparams, kernel_buffer, + kernel_buffer_size), + VBERROR_INVALID_KERNEL_FOUND, "Preamble verification"); + + /* Data verification */ + ResetMocks(); + verify_data_fail = 1; + TEST_EQ(VbVerifyMemoryBootImage(&cparams, &kparams, kernel_buffer, + kernel_buffer_size), + VBERROR_INVALID_KERNEL_FOUND, "Data verification"); +} + +int main(void) +{ + VerifyMemoryBootImageTest(); + + if (vboot_api_stub_check_memory()) + return 255; + + return gTestSuccess ? 0 : 255; +} |