summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--firmware/include/vboot_api.h21
-rw-r--r--firmware/lib/vboot_api_kernel.c136
-rw-r--r--tests/vboot_api_kernel5_tests.c281
4 files changed, 440 insertions, 0 deletions
diff --git a/Makefile b/Makefile
index 2d9ab423..23916256 100644
--- a/Makefile
+++ b/Makefile
@@ -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;
+}