summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2015-05-19 13:41:09 -0700
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-06-09 21:30:39 +0000
commitd7f0f93fa878e6dd7435adbe034e1fc7474f94b0 (patch)
tree7206dcafd5f53b9a16e9908fd9a0470697f72db4
parent3479e84e3041336f1e967c302c5a35cb64819927 (diff)
downloadvboot-d7f0f93fa878e6dd7435adbe034e1fc7474f94b0.tar.gz
vboot2: Add 2.0 api layer to verify kernel partition
This allows the caller to load the kernel partition and then pass it to vboot for verification, rather than having vboot assume the kernel partitions are all on a block storage device. Next up, APIs for the caller to parse partition information from a GPT (yes, that's cgptlib, but we'll make it more easily callable by depthcharge). BUG=chromium:487699 BRANCH=none TEST=make -j runtests Change-Id: I388085c7023f4c76d416f37df0607019bea844ac Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/275646 Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org>
-rw-r--r--Makefile3
-rw-r--r--firmware/2lib/include/2api.h124
-rw-r--r--firmware/2lib/include/2recovery_reasons.h6
-rw-r--r--firmware/2lib/include/2return_codes.h18
-rw-r--r--firmware/lib20/api_kernel.c273
-rw-r--r--firmware/lib20/include/vb2_struct.h10
-rw-r--r--tests/vb20_api_kernel_tests.c446
7 files changed, 878 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index e5535a7a..1273d80c 100644
--- a/Makefile
+++ b/Makefile
@@ -339,6 +339,7 @@ FWLIB2X_SRCS = \
FWLIB20_SRCS = \
firmware/lib20/api.c \
+ firmware/lib20/api_kernel.c \
firmware/lib20/common.c \
firmware/lib20/kernel.c \
firmware/lib20/misc.c \
@@ -722,6 +723,7 @@ TEST2X_NAMES = \
TEST20_NAMES = \
tests/vb20_api_tests \
+ tests/vb20_api_kernel_tests \
tests/vb20_common_tests \
tests/vb20_common2_tests \
tests/vb20_verify_fw.c \
@@ -1389,6 +1391,7 @@ run2tests: test_setup
${RUNTEST} ${BUILD_RUN}/tests/vb2_secdatak_tests
${RUNTEST} ${BUILD_RUN}/tests/vb2_sha_tests
${RUNTEST} ${BUILD_RUN}/tests/vb20_api_tests
+ ${RUNTEST} ${BUILD_RUN}/tests/vb20_api_kernel_tests
${RUNTEST} ${BUILD_RUN}/tests/vb20_common_tests
${RUNTEST} ${BUILD_RUN}/tests/vb20_common2_tests ${TEST_KEYS}
${RUNTEST} ${BUILD_RUN}/tests/vb20_common3_tests ${TEST_KEYS}
diff --git a/firmware/2lib/include/2api.h b/firmware/2lib/include/2api.h
index c7367b7c..95af636d 100644
--- a/firmware/2lib/include/2api.h
+++ b/firmware/2lib/include/2api.h
@@ -110,7 +110,13 @@ enum vb2_context_flags {
* Verified boot has changed secdatak[]. Caller must save secdatak[]
* back to its underlying storage, then may clear this flag.
*/
- VB2_CONTEXT_SECDATAK_CHANGED = (1 << 11),
+ VB2_CONTEXT_SECDATAK_CHANGED = (1 << 10),
+
+ /*
+ * Allow kernel verification to roll forward the version in secdatak[].
+ * Caller may set this flag before calling vb2api_kernel_phase3().
+ */
+ VB2_CONTEXT_ALLOW_KERNEL_ROLL_FORWARD = (1 << 11),
/* Boot optimistically: don't touch failure counters */
VB2_CONTEXT_NOFAIL_BOOT = (1 << 12),
@@ -279,7 +285,57 @@ enum vb2_pcr_digest {
*
* At this point, firmware verification is done, and vb2_context contains the
* kernel key needed to verify the kernel. That context should be preserved
- * and passed on to kernel selection.
+ * and passed on to kernel selection. The kernel selection process may be
+ * done by the same firmware image, or may be done by the RW firmware. The
+ * recommended order is:
+ *
+ * Load secdatak from wherever you keep it.
+ *
+ * If it wasn't there at all (for example, this is the first boot
+ * of a new system in the factory), call vb2api_secdatak_create()
+ * to initialize the data.
+ *
+ * If access to your storage is unreliable (reads/writes may
+ * contain corrupt data), you may call vb2api_secdatak_check() to
+ * determine if the data was valid, and retry reading if it
+ * wasn't. (In that case, you should also read back and check the
+ * data after any time you write it, to make sure it was written
+ * correctly.)
+ *
+ * Call vb2api_kernel_phase1(). At present, this decides which key to
+ * use to verify kernel data - the recovery key from the GBB, or the
+ * kernel subkey from the firmware verification stage.
+ *
+ * Kernel phase 2 is finding loading, and verifying the kernel partition.
+ *
+ * Find a boot device (you're on your own here).
+ *
+ * Call vb2api_load_kernel_vblock() for each kernel partition on the
+ * boot device, until one succeeds.
+ *
+ * When that succeeds, call vb2api_get_kernel_size() to determine where
+ * the kernel is located in the stream and how big it is. Load or map
+ * the kernel. (Again, you're on your own. This is the responsibility of
+ * the caller so that the caller can choose whether to allocate a buffer,
+ * load the kernel data into a predefined area of RAM, or directly map a
+ * kernel file into the address space. Note that technically it doesn't
+ * matter whether the kernel data is even in the same file or stream as
+ * the vblock, as long as the caller loads the right data.
+ *
+ * Call vb2api_verify_kernel_data() on the kernel data.
+ *
+ * If you ran out of kernels before finding a good one, call vb2api_fail()
+ * with an appropriate recovery reason.
+ *
+ * Set the VB2_CONTEXT_ALLOW_KERNEL_ROLL_FORWARD flag if the current
+ * kernel partition has the successful flag (that is, it's already known
+ * or assumed to be a functional kernel partition).
+ *
+ * Call vb2api_kernel_phase3(). This cleans up from kernel verification
+ * and updates the secure data if needed.
+ *
+ * Lock down wherever you keep secdatak. It should no longer be writable
+ * this boot.
*/
/**
@@ -444,6 +500,70 @@ int vb2api_get_pcr_digest(struct vb2_context *ctx,
uint8_t *dest,
uint32_t *dest_size);
+/**
+ * Prepare for kernel verification stage.
+ *
+ * Must be called before other vb2api kernel functions.
+ *
+ * @param ctx Vboot context
+ * @return VB2_SUCCESS, or error code on error.
+ */
+int vb2api_kernel_phase1(struct vb2_context *ctx);
+
+/**
+ * Load the verified boot block (vblock) for a kernel.
+ *
+ * This function may be called multiple times, to load and verify the
+ * vblocks from multiple kernel partitions.
+ *
+ * @param ctx Vboot context
+ * @param stream Kernel stream
+ * @return VB2_SUCCESS, or error code on error.
+ */
+int vb2api_load_kernel_vblock(struct vb2_context *ctx);
+
+/**
+ * Get the size and offset of the kernel data for the most recent vblock.
+ *
+ * Valid after a successful call to vb2api_load_kernel_vblock().
+ *
+ * @param ctx Vboot context
+ * @param offset_ptr Destination for offset in bytes of kernel data as
+ * reported by vblock.
+ * @param size_ptr Destination for size of kernel data in bytes.
+ * @return VB2_SUCCESS, or error code on error.
+ */
+int vb2api_get_kernel_size(struct vb2_context *ctx,
+ uint32_t *offset_ptr,
+ uint32_t *size_ptr);
+
+/**
+ * Verify kernel data using the previously loaded kernel vblock.
+ *
+ * Valid after a successful call to vb2api_load_kernel_vblock(). This allows
+ * the caller to load or map the kernel data, as appropriate, and pass the
+ * pointer to the kernel data into vboot.
+ *
+ * @param ctx Vboot context
+ * @param buf Pointer to kernel data
+ * @param size Size of kernel data in bytes
+ * @return VB2_SUCCESS, or error code on error.
+ */
+int vb2api_verify_kernel_data(struct vb2_context *ctx,
+ const void *buf,
+ uint32_t size);
+
+/**
+ * Clean up after kernel verification.
+ *
+ * Call this after successfully loading a vblock and verifying kernel data,
+ * or if you've run out of boot devices and/or kernel partitions.
+ *
+ * This cleans up intermediate data structures in the vboot context, and
+ * updates the version in the secure data if necessary.
+ */
+int vb2api_kernel_phase3(struct vb2_context *ctx);
+
/*****************************************************************************/
/* APIs provided by the caller to verified boot */
diff --git a/firmware/2lib/include/2recovery_reasons.h b/firmware/2lib/include/2recovery_reasons.h
index 1ced50e1..7bc1f278 100644
--- a/firmware/2lib/include/2recovery_reasons.h
+++ b/firmware/2lib/include/2recovery_reasons.h
@@ -193,6 +193,12 @@ enum vb2_nv_recovery {
/* BCB related error in RW firmware */
VB2_RECOVERY_RW_BCB_ERROR = 0x5c,
+ /* New error codes from VB2 */
+ /* TODO: may need to add strings for these in the original fwlib */
+
+ /* Secure data inititalization error */
+ VB2_RECOVERY_SECDATAK_INIT = 0x5d,
+
/* Unspecified/unknown error in rewritable firmware */
VB2_RECOVERY_RW_UNSPECIFIED = 0x7f,
diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h
index 848636ac..40a4c284 100644
--- a/firmware/2lib/include/2return_codes.h
+++ b/firmware/2lib/include/2return_codes.h
@@ -502,6 +502,24 @@ enum vb2_return_code {
/* Buffer size for the digest is too small for vb2api_get_pcr_digest */
VB2_ERROR_API_PCR_DIGEST_BUF,
+ /* Work buffer too small for recovery key in vb2api_kernel_phase1() */
+ VB2_ERROR_API_KPHASE1_WORKBUF_REC_KEY,
+
+ /* Firmware preamble not present for vb2api_kernel_phase1() */
+ VB2_ERROR_API_KPHASE1_PREAMBLE,
+
+ /* Wrong amount of kernel data in vb2api_verify_kernel_data() */
+ VB2_ERROR_API_VERIFY_KDATA_SIZE,
+
+ /* Kernel preamble not present for vb2api_verify_kernel_data() */
+ VB2_ERROR_API_VERIFY_KDATA_PREAMBLE,
+
+ /* Insufficient workbuf for hashing in vb2api_verify_kernel_data() */
+ VB2_ERROR_API_VERIFY_KDATA_WORKBUF,
+
+ /* Bad data key in vb2api_verify_kernel_data() */
+ VB2_ERROR_API_VERIFY_KDATA_KEY,
+
/**********************************************************************
* Errors which may be generated by implementations of vb2ex functions.
* Implementation may also return its own specific errors, which should
diff --git a/firmware/lib20/api_kernel.c b/firmware/lib20/api_kernel.c
new file mode 100644
index 00000000..0c8b598b
--- /dev/null
+++ b/firmware/lib20/api_kernel.c
@@ -0,0 +1,273 @@
+/* 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.
+ *
+ * Externally-callable APIs
+ * (Kernel portion)
+ */
+
+#include "2sysincludes.h"
+#include "2api.h"
+#include "2misc.h"
+#include "2nvstorage.h"
+#include "2secdata.h"
+#include "2sha.h"
+#include "2rsa.h"
+#include "vb2_common.h"
+
+int vb2api_kernel_phase1(struct vb2_context *ctx)
+{
+ struct vb2_shared_data *sd = vb2_get_sd(ctx);
+ struct vb2_workbuf wb;
+ uint8_t *key_data;
+ uint32_t key_size;
+ int rv;
+
+ vb2_workbuf_from_ctx(ctx, &wb);
+
+ /* Initialize secure kernel data and read version */
+ rv = vb2_secdatak_init(ctx);
+ if (!rv) {
+ rv = vb2_secdatak_get(ctx, VB2_SECDATAK_VERSIONS,
+ &sd->kernel_version_secdatak);
+ }
+
+ if (rv) {
+ if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) {
+ /* Ignore failure to get kernel version in recovery */
+ sd->kernel_version_secdatak = 0;
+ } else {
+ vb2_fail(ctx, VB2_RECOVERY_SECDATAK_INIT, rv);
+ return rv;
+ }
+ }
+
+ /* Find the key to use to verify the kernel keyblock */
+ if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) {
+ /* Recovery key from GBB */
+ struct vb2_gbb_header *gbb;
+ uint32_t key_offset;
+
+ /* Read GBB header into next chunk of work buffer */
+ gbb = vb2_workbuf_alloc(&wb, sizeof(*gbb));
+ if (!gbb)
+ return VB2_ERROR_GBB_WORKBUF;
+
+ rv = vb2_read_gbb_header(ctx, gbb);
+ if (rv)
+ return rv;
+
+ /* Only need the recovery key position and size */
+ key_offset = gbb->recovery_key_offset;
+ key_size = gbb->recovery_key_size;
+
+ /* Free the GBB header */
+ vb2_workbuf_free(&wb, sizeof(*gbb));
+
+ /* Load the recovery key itself */
+ key_data = vb2_workbuf_alloc(&wb, key_size);
+ if (!key_data)
+ return VB2_ERROR_API_KPHASE1_WORKBUF_REC_KEY;
+
+ rv = vb2ex_read_resource(ctx, VB2_RES_GBB, key_offset,
+ key_data, key_size);
+ if (rv)
+ return rv;
+ } else {
+ /* Kernel subkey from firmware preamble */
+ struct vb2_fw_preamble *pre;
+ struct vb2_packed_key *pre_key, *packed_key;
+
+ /* Make sure we have a firmware preamble loaded */
+ if (!sd->workbuf_preamble_size)
+ return VB2_ERROR_API_KPHASE1_PREAMBLE;
+
+ pre = (struct vb2_fw_preamble *)
+ (ctx->workbuf + sd->workbuf_preamble_offset);
+ pre_key = &pre->kernel_subkey;
+
+ /*
+ * At this point, we no longer need the packed firmware
+ * data key, firmware preamble, or hash data. So move the
+ * kernel key from the preamble down after the shared data.
+ */
+ key_data = (uint8_t *)(sd + 1);
+ packed_key = (struct vb2_packed_key *)key_data;
+ memmove(packed_key, pre_key, sizeof(*packed_key));
+ packed_key->key_offset = sizeof(*packed_key);
+ memmove(key_data + packed_key->key_offset,
+ (uint8_t *)pre_key + pre_key->key_offset,
+ pre_key->key_size);
+
+ key_size = packed_key->key_offset + packed_key->key_size;
+ }
+
+ /* Firmware stage structs are no longer present */
+ sd->workbuf_data_key_size = 0;
+ sd->workbuf_preamble_size = 0;
+ sd->workbuf_hash_size = 0;
+
+ /*
+ * Kernel key will persist in the workbuf after we return.
+ *
+ * Work buffer now contains:
+ * - vb2_shared_data
+ * - kernel key
+ */
+ sd->workbuf_kernel_key_offset =
+ vb2_offset_of(ctx->workbuf, key_data);
+ sd->workbuf_kernel_key_size = key_size;
+ ctx->workbuf_used = sd->workbuf_kernel_key_offset +
+ sd->workbuf_kernel_key_size;
+
+ return VB2_SUCCESS;
+}
+
+int vb2api_load_kernel_vblock(struct vb2_context *ctx)
+{
+ int rv;
+
+ /* Verify kernel keyblock */
+ rv = vb2_load_kernel_keyblock(ctx);
+ if (rv)
+ return rv;
+
+ /* Verify kernel preamble */
+ rv = vb2_load_kernel_preamble(ctx);
+ if (rv)
+ return rv;
+
+ return VB2_SUCCESS;
+}
+
+int vb2api_get_kernel_size(struct vb2_context *ctx,
+ uint32_t *offset_ptr,
+ uint32_t *size_ptr)
+{
+ struct vb2_shared_data *sd = vb2_get_sd(ctx);
+ const struct vb2_kernel_preamble *pre;
+
+ /* Get preamble pointer */
+ if (!sd->workbuf_preamble_size)
+ return VB2_ERROR_API_GET_KERNEL_SIZE_PREAMBLE;
+
+ pre = (const struct vb2_kernel_preamble *)
+ (ctx->workbuf + sd->workbuf_preamble_offset);
+
+ if (offset_ptr) {
+ /* The kernel implicitly follows the preamble */
+ *offset_ptr = sd->vblock_preamble_offset +
+ sd->workbuf_preamble_size;
+ }
+
+ if (size_ptr) {
+ /* Expect the kernel to be the size of data we signed */
+ *size_ptr = pre->body_signature.data_size;
+ }
+
+ return VB2_SUCCESS;
+}
+
+int vb2api_verify_kernel_data(struct vb2_context *ctx,
+ const void *buf,
+ uint32_t size)
+{
+ struct vb2_shared_data *sd = vb2_get_sd(ctx);
+ struct vb2_kernel_preamble *pre;
+ struct vb2_digest_context *dc;
+ struct vb2_public_key key;
+ struct vb2_workbuf wb;
+
+ uint8_t *digest;
+ uint32_t digest_size;
+
+ int rv;
+
+ vb2_workbuf_from_ctx(ctx, &wb);
+
+ /* Get preamble pointer */
+ if (!sd->workbuf_preamble_size)
+ return VB2_ERROR_API_VERIFY_KDATA_PREAMBLE;
+
+ pre = (struct vb2_kernel_preamble *)
+ (ctx->workbuf + sd->workbuf_preamble_offset);
+
+ /* Make sure we were passed the right amount of data */
+ if (size != pre->body_signature.data_size)
+ return VB2_ERROR_API_VERIFY_KDATA_SIZE;
+
+ /* Allocate workbuf space for the hash */
+ dc = vb2_workbuf_alloc(&wb, sizeof(*dc));
+ if (!dc)
+ return VB2_ERROR_API_VERIFY_KDATA_WORKBUF;
+
+ /*
+ * Unpack the kernel data key to see which hashing algorithm we
+ * should use.
+ *
+ * TODO: really, the kernel body should be hashed, and not signed,
+ * because the signature we're checking is already signed as part of
+ * the kernel preamble. But until we can change the signing scripts,
+ * we're stuck with a signature here instead of a hash.
+ */
+ if (!sd->workbuf_data_key_size)
+ return VB2_ERROR_API_VERIFY_KDATA_KEY;
+
+ rv = vb2_unpack_key(&key,
+ ctx->workbuf + sd->workbuf_data_key_offset,
+ sd->workbuf_data_key_size);
+ if (rv)
+ return rv;
+
+ rv = vb2_digest_init(dc, key.hash_alg);
+ if (rv)
+ return rv;
+
+ rv = vb2_digest_extend(dc, buf, size);
+ if (rv)
+ return rv;
+
+ digest_size = vb2_digest_size(key.hash_alg);
+ digest = vb2_workbuf_alloc(&wb, digest_size);
+ if (!digest)
+ return VB2_ERROR_API_CHECK_HASH_WORKBUF_DIGEST;
+
+ rv = vb2_digest_finalize(dc, digest, digest_size);
+ if (rv)
+ return rv;
+
+ /*
+ * The body signature is currently a *signature* of the body data, not
+ * just its hash. So we need to verify the signature.
+ */
+
+ /*
+ * Check digest vs. signature. Note that this destroys the signature.
+ * That's ok, because we only check each signature once per boot.
+ */
+ return vb2_verify_digest(&key, &pre->body_signature, digest, &wb);
+}
+
+int vb2api_kernel_phase3(struct vb2_context *ctx)
+{
+ struct vb2_shared_data *sd = vb2_get_sd(ctx);
+ int rv;
+
+ /*
+ * If the kernel is a newer version than in secure storage, and the
+ * kernel signature is valid, and we're not in recovery mode, and we're
+ * allowed to, roll forward the version in secure storage.
+ */
+ if (sd->kernel_version > sd->kernel_version_secdatak &&
+ (sd->flags & VB2_SD_FLAG_KERNEL_SIGNED) &&
+ !(ctx->flags & VB2_CONTEXT_RECOVERY_MODE) &&
+ (ctx->flags & VB2_CONTEXT_ALLOW_KERNEL_ROLL_FORWARD)) {
+ rv = vb2_secdatak_set(ctx, VB2_SECDATAK_VERSIONS,
+ sd->kernel_version);
+ if (rv)
+ return rv;
+ sd->kernel_version_secdatak = sd->kernel_version;
+ }
+
+ return VB2_SUCCESS;
+}
diff --git a/firmware/lib20/include/vb2_struct.h b/firmware/lib20/include/vb2_struct.h
index 1e39061f..f644a5fa 100644
--- a/firmware/lib20/include/vb2_struct.h
+++ b/firmware/lib20/include/vb2_struct.h
@@ -219,9 +219,11 @@ struct vb2_kernel_preamble {
/* Load address for kernel body */
uint64_t body_load_address;
+ /* TODO (vboot 2.1): we never used that */
/* Address of bootloader, after body is loaded at body_load_address */
uint64_t bootloader_address;
+ /* TODO (vboot 2.1): should be a 32-bit offset */
/* Size of bootloader in bytes */
uint32_t bootloader_size;
@@ -231,6 +233,14 @@ struct vb2_kernel_preamble {
struct vb2_signature body_signature;
/*
+ * TODO (vboot 2.1): fields for kernel offset and size. Right now the
+ * size is implicitly the same as the size of data signed by the body
+ * signature, and the offset is implicitly at the end of the preamble.
+ * But that forces us to pad the preamble to 64KB rather than just
+ * having a tiny preamble and an offset field.
+ */
+
+ /*
* Fields added in header version 2.1. You must verify the header
* version before reading these fields!
*/
diff --git a/tests/vb20_api_kernel_tests.c b/tests/vb20_api_kernel_tests.c
new file mode 100644
index 00000000..0c8cb59a
--- /dev/null
+++ b/tests/vb20_api_kernel_tests.c
@@ -0,0 +1,446 @@
+/* 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.
+ *
+ * Tests for kernel verification library, api layer
+ */
+
+#include <stdio.h>
+
+#include "2sysincludes.h"
+#include "2api.h"
+#include "2common.h"
+#include "2misc.h"
+#include "2nvstorage.h"
+#include "2rsa.h"
+#include "2secdata.h"
+#include "vb2_common.h"
+#include "test_common.h"
+
+/* Common context for tests */
+static uint8_t workbuf[VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE]
+ __attribute__ ((aligned (VB2_WORKBUF_ALIGN)));
+static struct vb2_workbuf wb;
+static struct vb2_context cc;
+static struct vb2_shared_data *sd;
+static struct vb2_fw_preamble *fwpre;
+static struct vb2_kernel_preamble *kpre;
+static struct vb2_packed_key *kdkey;
+static const char fw_kernel_key_data[36] = "Test kernel key data";
+static char kernel_data[0x4008] = "Sure it's a kernel...";
+
+/* Mocked function data */
+
+static struct {
+ struct vb2_gbb_header h;
+ struct vb2_packed_key recovery_key;
+ char recovery_key_data[32];
+} mock_gbb;
+
+static int mock_read_res_fail_on_call;
+static int mock_unpack_key_retval;
+static int mock_read_gbb_header_retval;
+static int mock_load_kernel_keyblock_retval;
+static int mock_load_kernel_preamble_retval;
+
+/* Type of test to reset for */
+enum reset_type {
+ FOR_PHASE1,
+ FOR_PHASE2,
+ FOR_PHASE3,
+};
+
+static void reset_common_data(enum reset_type t)
+{
+ struct vb2_packed_key *k;
+
+ memset(workbuf, 0xaa, sizeof(workbuf));
+
+ memset(&cc, 0, sizeof(cc));
+ cc.workbuf = workbuf;
+ cc.workbuf_size = sizeof(workbuf);
+ vb2_workbuf_from_ctx(&cc, &wb);
+
+ vb2_init_context(&cc);
+ sd = vb2_get_sd(&cc);
+
+ vb2_nv_init(&cc);
+
+ vb2_secdatak_create(&cc);
+ vb2_secdatak_init(&cc);
+ vb2_secdatak_set(&cc, VB2_SECDATAK_VERSIONS, 0x20002);
+
+ mock_read_res_fail_on_call = 0;
+ mock_unpack_key_retval = VB2_SUCCESS;
+ mock_read_gbb_header_retval = VB2_SUCCESS;
+ mock_load_kernel_keyblock_retval = VB2_SUCCESS;
+ mock_load_kernel_preamble_retval = VB2_SUCCESS;
+
+ /* Recovery key in mock GBB */
+ mock_gbb.recovery_key.algorithm = 11;
+ mock_gbb.recovery_key.key_offset =
+ vb2_offset_of(&mock_gbb.recovery_key,
+ &mock_gbb.recovery_key_data);
+ mock_gbb.recovery_key.key_size = sizeof(mock_gbb.recovery_key_data);
+ strcpy(mock_gbb.recovery_key_data, "The recovery key");
+ mock_gbb.h.recovery_key_offset =
+ vb2_offset_of(&mock_gbb, &mock_gbb.recovery_key);
+ mock_gbb.h.recovery_key_size =
+ mock_gbb.recovery_key.key_offset +
+ mock_gbb.recovery_key.key_size;
+
+ if (t == FOR_PHASE1) {
+ uint8_t *kdata;
+
+ /* Create mock firmware preamble in the context */
+ sd->workbuf_preamble_offset = cc.workbuf_used;
+ fwpre = (struct vb2_fw_preamble *)
+ (cc.workbuf + sd->workbuf_preamble_offset);
+ k = &fwpre->kernel_subkey;
+ kdata = (uint8_t *)fwpre + sizeof(*fwpre);
+ memcpy(kdata, fw_kernel_key_data, sizeof(fw_kernel_key_data));
+ k->algorithm = 7;
+ k->key_offset = vb2_offset_of(k, kdata);
+ k->key_size = sizeof(fw_kernel_key_data);
+ sd->workbuf_preamble_size = sizeof(*fwpre) + k->key_size;
+ cc.workbuf_used += sd->workbuf_preamble_size;
+
+ } else if (t == FOR_PHASE2) {
+ struct vb2_signature *sig;
+ struct vb2_digest_context dc;
+ uint8_t *sdata;
+
+ /* Create mock kernel data key */
+ sd->workbuf_data_key_offset = cc.workbuf_used;
+ kdkey = (struct vb2_packed_key *)
+ (cc.workbuf + sd->workbuf_data_key_offset);
+ kdkey->algorithm = VB2_ALG_RSA2048_SHA256;
+ sd->workbuf_data_key_size = sizeof(*kdkey);
+ cc.workbuf_used += sd->workbuf_data_key_size;
+
+ /* Create mock kernel preamble in the context */
+ sd->workbuf_preamble_offset = cc.workbuf_used;
+ kpre = (struct vb2_kernel_preamble *)
+ (cc.workbuf + sd->workbuf_preamble_offset);
+ sdata = (uint8_t *)kpre + sizeof(*kpre);
+
+ sig = &kpre->body_signature;
+ sig->data_size = sizeof(kernel_data);
+ sig->sig_offset = vb2_offset_of(sig, sdata);
+ sig->sig_size = VB2_SHA512_DIGEST_SIZE;
+
+ vb2_digest_init(&dc, VB2_HASH_SHA256);
+ vb2_digest_extend(&dc, (const uint8_t *)kernel_data,
+ sizeof(kernel_data));
+ vb2_digest_finalize(&dc, sdata, sig->sig_size);
+
+ sd->workbuf_preamble_size = sizeof(*kpre) + sig->sig_size;
+ sd->vblock_preamble_offset =
+ 0x10000 - sd->workbuf_preamble_size;
+ cc.workbuf_used += sd->workbuf_preamble_size;
+
+ } else {
+ /* Set flags and versions for roll-forward */
+ sd->kernel_version = 0x20004;
+ sd->kernel_version_secdatak = 0x20002;
+ sd->flags |= VB2_SD_FLAG_KERNEL_SIGNED;
+ cc.flags |= VB2_CONTEXT_ALLOW_KERNEL_ROLL_FORWARD;
+ }
+};
+
+/* Mocked functions */
+
+int vb2ex_read_resource(struct vb2_context *ctx,
+ enum vb2_resource_index index,
+ uint32_t offset,
+ void *buf,
+ uint32_t size)
+{
+ uint8_t *rptr;
+ uint32_t rsize;
+
+ if (--mock_read_res_fail_on_call == 0)
+ return VB2_ERROR_MOCK;
+
+ switch(index) {
+ case VB2_RES_GBB:
+ rptr = (uint8_t *)&mock_gbb;
+ rsize = sizeof(mock_gbb);
+ break;
+ default:
+ return VB2_ERROR_EX_READ_RESOURCE_INDEX;
+ }
+
+ if (offset > rsize || offset + size > rsize)
+ return VB2_ERROR_EX_READ_RESOURCE_SIZE;
+
+ memcpy(buf, rptr + offset, size);
+ return VB2_SUCCESS;
+}
+
+int vb2_read_gbb_header(struct vb2_context *ctx, struct vb2_gbb_header *gbb)
+{
+ memcpy(gbb, &mock_gbb.h, sizeof(*gbb));
+ return mock_read_gbb_header_retval;
+}
+
+int vb2_load_kernel_keyblock(struct vb2_context *ctx)
+{
+ return mock_load_kernel_keyblock_retval;
+}
+
+int vb2_load_kernel_preamble(struct vb2_context *ctx)
+{
+ return mock_load_kernel_preamble_retval;
+}
+
+int vb2_unpack_key(struct vb2_public_key *key,
+ const uint8_t *buf,
+ uint32_t size)
+{
+ const struct vb2_packed_key *k = (const struct vb2_packed_key *)buf;
+
+ key->arrsize = 0;
+ key->hash_alg = vb2_crypto_to_hash(k->algorithm);
+ return mock_unpack_key_retval;
+}
+
+int vb2_verify_digest(const struct vb2_public_key *key,
+ struct vb2_signature *sig,
+ const uint8_t *digest,
+ const struct vb2_workbuf *wb)
+{
+ if (memcmp(digest, (uint8_t *)sig + sig->sig_offset, sig->sig_size))
+ return VB2_ERROR_VDATA_VERIFY_DIGEST;
+
+ return VB2_SUCCESS;
+}
+
+/* Tests */
+
+static void phase1_tests(void)
+{
+ struct vb2_packed_key *k;
+ uint32_t old_preamble_offset;
+
+ /* Test successful call */
+ reset_common_data(FOR_PHASE1);
+ old_preamble_offset = sd->workbuf_preamble_offset;
+ TEST_SUCC(vb2api_kernel_phase1(&cc), "phase1 good");
+ TEST_EQ(sd->workbuf_preamble_size, 0, " no more fw preamble");
+ /* Make sure normal key was loaded */
+ TEST_EQ(sd->workbuf_kernel_key_offset, old_preamble_offset,
+ " workbuf key offset");
+ k = (struct vb2_packed_key *)
+ (cc.workbuf + sd->workbuf_kernel_key_offset);
+ TEST_EQ(sd->workbuf_kernel_key_size, k->key_offset + k->key_size,
+ " workbuf key size");
+ TEST_EQ(cc.workbuf_used, sd->workbuf_kernel_key_offset +
+ sd->workbuf_kernel_key_size, " workbuf used");
+ TEST_EQ(k->algorithm, 7, " key algorithm");
+ TEST_EQ(k->key_size, sizeof(fw_kernel_key_data), " key_size");
+ TEST_EQ(memcmp((uint8_t *)k + k->key_offset, fw_kernel_key_data,
+ k->key_size), 0, " key data");
+ TEST_EQ(sd->kernel_version_secdatak, 0x20002, " secdatak version");
+
+ /* Test successful call in recovery mode */
+ reset_common_data(FOR_PHASE1);
+ cc.flags |= VB2_CONTEXT_RECOVERY_MODE;
+ /* No preamble loaded in recovery mode */
+ cc.workbuf_used = old_preamble_offset = sd->workbuf_preamble_offset;
+ sd->workbuf_preamble_offset = sd->workbuf_preamble_size = 0;
+ TEST_SUCC(vb2api_kernel_phase1(&cc), "phase1 rec good");
+ TEST_EQ(sd->workbuf_preamble_size, 0, "no more fw preamble");
+ /* Make sure recovery key was loaded */
+ TEST_EQ(sd->workbuf_kernel_key_offset, old_preamble_offset,
+ " workbuf key offset");
+ k = (struct vb2_packed_key *)
+ (cc.workbuf + sd->workbuf_kernel_key_offset);
+ TEST_EQ(sd->workbuf_kernel_key_size, k->key_offset + k->key_size,
+ " workbuf key size");
+ TEST_EQ(cc.workbuf_used, sd->workbuf_kernel_key_offset +
+ sd->workbuf_kernel_key_size, " workbuf used");
+ TEST_EQ(k->algorithm, 11, " key algorithm");
+ TEST_EQ(k->key_size, sizeof(mock_gbb.recovery_key_data), " key_size");
+ TEST_EQ(memcmp((uint8_t *)k + k->key_offset,
+ mock_gbb.recovery_key_data, k->key_size), 0,
+ " key data");
+ TEST_EQ(sd->kernel_version_secdatak, 0x20002, " secdatak version");
+
+ /* Bad secdatak causes failure in normal mode only */
+ reset_common_data(FOR_PHASE1);
+ cc.secdatak[0] ^= 0x33;
+ TEST_EQ(vb2api_kernel_phase1(&cc), VB2_ERROR_SECDATAK_CRC,
+ "phase1 bad secdata");
+ reset_common_data(FOR_PHASE1);
+
+ cc.secdatak[0] ^= 0x33;
+ cc.flags |= VB2_CONTEXT_RECOVERY_MODE;
+ TEST_SUCC(vb2api_kernel_phase1(&cc), "phase1 bad secdata rec");
+ TEST_EQ(sd->kernel_version_secdatak, 0, " secdatak version");
+
+ /* Failures while reading recovery key */
+ reset_common_data(FOR_PHASE1);
+ cc.flags |= VB2_CONTEXT_RECOVERY_MODE;
+ cc.workbuf_used = cc.workbuf_size - sizeof(struct vb2_gbb_header) + 1;
+ TEST_EQ(vb2api_kernel_phase1(&cc), VB2_ERROR_GBB_WORKBUF,
+ "phase1 rec workbuf gbb header");
+
+ reset_common_data(FOR_PHASE1);
+ cc.flags |= VB2_CONTEXT_RECOVERY_MODE;
+ mock_read_gbb_header_retval = VB2_ERROR_MOCK;
+ TEST_EQ(vb2api_kernel_phase1(&cc), VB2_ERROR_MOCK,
+ "phase1 rec gbb read header");
+
+ reset_common_data(FOR_PHASE1);
+ cc.flags |= VB2_CONTEXT_RECOVERY_MODE;
+ mock_gbb.h.recovery_key_size = cc.workbuf_size - 1;
+ TEST_EQ(vb2api_kernel_phase1(&cc), VB2_ERROR_API_KPHASE1_WORKBUF_REC_KEY,
+ "phase1 rec workbuf key");
+
+ reset_common_data(FOR_PHASE1);
+ cc.flags |= VB2_CONTEXT_RECOVERY_MODE;
+ mock_read_res_fail_on_call = 1;
+ TEST_EQ(vb2api_kernel_phase1(&cc), VB2_ERROR_MOCK,
+ "phase1 rec gbb read key");
+
+ /* Failures while parsing subkey from firmware preamble */
+ reset_common_data(FOR_PHASE1);
+ sd->workbuf_preamble_size = 0;
+ TEST_EQ(vb2api_kernel_phase1(&cc), VB2_ERROR_API_KPHASE1_PREAMBLE,
+ "phase1 fw preamble");
+}
+
+static void load_kernel_vblock_tests(void)
+{
+ reset_common_data(FOR_PHASE1);
+ TEST_SUCC(vb2api_load_kernel_vblock(&cc), "load vblock good");
+
+ reset_common_data(FOR_PHASE1);
+ mock_load_kernel_keyblock_retval = VB2_ERROR_MOCK;
+ TEST_EQ(vb2api_load_kernel_vblock(&cc), VB2_ERROR_MOCK,
+ "load vblock bad keyblock");
+
+ reset_common_data(FOR_PHASE1);
+ mock_load_kernel_preamble_retval = VB2_ERROR_MOCK;
+ TEST_EQ(vb2api_load_kernel_vblock(&cc), VB2_ERROR_MOCK,
+ "load vblock bad preamble");
+}
+
+static void get_kernel_size_tests(void)
+{
+ uint32_t offs, size;
+
+ reset_common_data(FOR_PHASE2);
+ offs = size = 0;
+ TEST_SUCC(vb2api_get_kernel_size(&cc, &offs, &size), "get size good");
+ TEST_EQ(offs, 0x10000, " offset");
+ TEST_EQ(size, sizeof(kernel_data), " size");
+
+ /* Don't need to pass pointers */
+ reset_common_data(FOR_PHASE2);
+ TEST_SUCC(vb2api_get_kernel_size(&cc, NULL, NULL), "get size null");
+
+ reset_common_data(FOR_PHASE2);
+ sd->workbuf_preamble_size = 0;
+ TEST_EQ(vb2api_get_kernel_size(&cc, &offs, &size),
+ VB2_ERROR_API_GET_KERNEL_SIZE_PREAMBLE,
+ "get size no preamble");
+}
+
+static void verify_kernel_data_tests(void)
+{
+ reset_common_data(FOR_PHASE2);
+ TEST_SUCC(vb2api_verify_kernel_data(&cc, kernel_data, sizeof(kernel_data)),
+ "verify data good");
+
+ reset_common_data(FOR_PHASE2);
+ sd->workbuf_preamble_size = 0;
+ TEST_EQ(vb2api_verify_kernel_data(&cc, kernel_data, sizeof(kernel_data)),
+ VB2_ERROR_API_VERIFY_KDATA_PREAMBLE, "verify no preamble");
+
+ reset_common_data(FOR_PHASE2);
+ TEST_EQ(vb2api_verify_kernel_data(&cc, kernel_data, sizeof(kernel_data) + 1),
+ VB2_ERROR_API_VERIFY_KDATA_SIZE, "verify size");
+
+ reset_common_data(FOR_PHASE2);
+ cc.workbuf_used = cc.workbuf_size - sizeof(struct vb2_digest_context) + 1;
+ TEST_EQ(vb2api_verify_kernel_data(&cc, kernel_data, sizeof(kernel_data)),
+ VB2_ERROR_API_VERIFY_KDATA_WORKBUF, "verify workbuf");
+
+ reset_common_data(FOR_PHASE2);
+ sd->workbuf_data_key_size = 0;
+ TEST_EQ(vb2api_verify_kernel_data(&cc, kernel_data, sizeof(kernel_data)),
+ VB2_ERROR_API_VERIFY_KDATA_KEY, "verify no key");
+
+ reset_common_data(FOR_PHASE2);
+ mock_unpack_key_retval = VB2_ERROR_MOCK;
+ TEST_EQ(vb2api_verify_kernel_data(&cc, kernel_data, sizeof(kernel_data)),
+ VB2_ERROR_MOCK, "verify unpack key");
+
+ reset_common_data(FOR_PHASE2);
+ kdkey->algorithm = VB2_ALG_COUNT;
+ TEST_EQ(vb2api_verify_kernel_data(&cc, kernel_data, sizeof(kernel_data)),
+ VB2_ERROR_SHA_INIT_ALGORITHM, "verify hash init");
+
+ reset_common_data(FOR_PHASE2);
+ cc.workbuf_used = cc.workbuf_size - sizeof(struct vb2_digest_context) - 4;
+ TEST_EQ(vb2api_verify_kernel_data(&cc, kernel_data, sizeof(kernel_data)),
+ VB2_ERROR_API_CHECK_HASH_WORKBUF_DIGEST, "verify hash workbuf");
+
+ reset_common_data(FOR_PHASE2);
+ kernel_data[3] ^= 0xd0;
+ TEST_EQ(vb2api_verify_kernel_data(&cc, kernel_data, sizeof(kernel_data)),
+ VB2_ERROR_VDATA_VERIFY_DIGEST, "verify hash digest");
+ kernel_data[3] ^= 0xd0;
+}
+
+static void phase3_tests(void)
+{
+ uint32_t v;
+
+ reset_common_data(FOR_PHASE3);
+ TEST_SUCC(vb2api_kernel_phase3(&cc), "phase3 good");
+ vb2_secdatak_get(&cc, VB2_SECDATAK_VERSIONS, &v);
+ TEST_EQ(v, 0x20004, " version");
+
+ reset_common_data(FOR_PHASE3);
+ sd->kernel_version = 0x20001;
+ TEST_SUCC(vb2api_kernel_phase3(&cc), "phase3 no rollback");
+ vb2_secdatak_get(&cc, VB2_SECDATAK_VERSIONS, &v);
+ TEST_EQ(v, 0x20002, " version");
+
+ reset_common_data(FOR_PHASE3);
+ sd->flags &= ~VB2_SD_FLAG_KERNEL_SIGNED;
+ TEST_SUCC(vb2api_kernel_phase3(&cc), "phase3 unsigned kernel");
+ vb2_secdatak_get(&cc, VB2_SECDATAK_VERSIONS, &v);
+ TEST_EQ(v, 0x20002, " version");
+
+ reset_common_data(FOR_PHASE3);
+ cc.flags |= VB2_CONTEXT_RECOVERY_MODE;
+ TEST_SUCC(vb2api_kernel_phase3(&cc), "phase3 recovery");
+ vb2_secdatak_get(&cc, VB2_SECDATAK_VERSIONS, &v);
+ TEST_EQ(v, 0x20002, " version");
+
+ reset_common_data(FOR_PHASE3);
+ cc.flags &= ~VB2_CONTEXT_ALLOW_KERNEL_ROLL_FORWARD;
+ TEST_SUCC(vb2api_kernel_phase3(&cc), "phase3 no rollforward");
+ vb2_secdatak_get(&cc, VB2_SECDATAK_VERSIONS, &v);
+ TEST_EQ(v, 0x20002, " version");
+
+ reset_common_data(FOR_PHASE3);
+ sd->status &= ~VB2_SD_STATUS_SECDATAK_INIT;
+ TEST_EQ(vb2api_kernel_phase3(&cc),
+ VB2_ERROR_SECDATAK_SET_UNINITIALIZED, "phase3 set fail");
+}
+
+int main(int argc, char* argv[])
+{
+ phase1_tests();
+ load_kernel_vblock_tests();
+ get_kernel_size_tests();
+ verify_kernel_data_tests();
+ phase3_tests();
+
+ return gTestSuccess ? 0 : 255;
+}