summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2015-05-29 10:53:11 -0700
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-06-04 19:32:56 +0000
commit22da78ce594d1e9893fe418e4a021db17002f777 (patch)
treef6a0ab675426653a9cb9a5febd90e107b23d8bb0
parent7a1c0d1ec852ee86237aba7f3c87d642ca8e00b0 (diff)
downloadvboot-22da78ce594d1e9893fe418e4a021db17002f777.tar.gz
vboot2: Add routines to load kernel preamble
The kernel data itself will be read and verified by a subsequent change. BUG=chromium:487699 BRANCH=none TEST=make -j runtests Change-Id: Ife4f8250493ec6457f91fda57ae8d4d7bf18ec89 Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/274038 Reviewed-by: Bill Richardson <wfrichar@chromium.org>
-rw-r--r--firmware/2lib/include/2api.h13
-rw-r--r--firmware/2lib/include/2misc.h10
-rw-r--r--firmware/2lib/include/2return_codes.h16
-rw-r--r--firmware/lib20/kernel.c131
-rw-r--r--firmware/lib20/misc.c6
-rw-r--r--tests/vb20_kernel_tests.c89
6 files changed, 254 insertions, 11 deletions
diff --git a/firmware/2lib/include/2api.h b/firmware/2lib/include/2api.h
index 3fbd7edf..c7367b7c 100644
--- a/firmware/2lib/include/2api.h
+++ b/firmware/2lib/include/2api.h
@@ -36,13 +36,24 @@
#define VB2_SECDATAK_SIZE 14
/*
- * Recommended size of work buffer.
+ * Recommended size of work buffer for firmware verification stage
*
* TODO: The recommended size really depends on which key algorithms are
* used. Should have a better / more accurate recommendation than this.
*/
#define VB2_WORKBUF_RECOMMENDED_SIZE (12 * 1024)
+/*
+ * Recommended size of work buffer for kernel verification stage
+ *
+ * This is bigger because vboot 2.0 kernel preambles are usually padded to
+ * 64 KB.
+ *
+ * TODO: The recommended size really depends on which key algorithms are
+ * used. Should have a better / more accurate recommendation than this.
+ */
+#define VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE (80 * 1024)
+
/* Recommended buffer size for vb2api_get_pcr_digest */
#define VB2_PCR_DIGEST_RECOMMENDED_SIZE 32
diff --git a/firmware/2lib/include/2misc.h b/firmware/2lib/include/2misc.h
index 34d6fb82..5e3363cb 100644
--- a/firmware/2lib/include/2misc.h
+++ b/firmware/2lib/include/2misc.h
@@ -151,4 +151,14 @@ int vb2_load_fw_preamble(struct vb2_context *ctx);
*/
int vb2_load_kernel_keyblock(struct vb2_context *ctx);
+/**
+ * Verify the kernel preamble using the data subkey from the keyblock.
+ *
+ * After this call, the preamble is stored in the work buffer.
+ *
+ * @param ctx Vboot context
+ * @return VB2_SUCCESS, or error code on error.
+ */
+int vb2_load_kernel_preamble(struct vb2_context *ctx);
+
#endif /* VBOOT_REFERENCE_VBOOT_2MISC_H_ */
diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h
index 81a59571..848636ac 100644
--- a/firmware/2lib/include/2return_codes.h
+++ b/firmware/2lib/include/2return_codes.h
@@ -425,7 +425,23 @@ enum vb2_return_code {
VB2_ERROR_KERNEL_KEYBLOCK_DEV_FLAG,
VB2_ERROR_KERNEL_KEYBLOCK_REC_FLAG,
+ /* Missing firmware data key in vb2_load_kernel_preamble() */
+ VB2_ERROR_KERNEL_PREAMBLE2_DATA_KEY,
+ /* Work buffer too small for header in vb2_load_kernel_preamble() */
+ VB2_ERROR_KERNEL_PREAMBLE2_WORKBUF_HEADER,
+
+ /* Work buffer too small for preamble in vb2_load_kernel_preamble() */
+ VB2_ERROR_KERNEL_PREAMBLE2_WORKBUF,
+
+ /* Kernel version out of range in vb2_load_kernel_preamble() */
+ VB2_ERROR_KERNEL_PREAMBLE_VERSION_RANGE,
+
+ /* Kernel version rollback in vb2_load_kernel_preamble() */
+ VB2_ERROR_KERNEL_PREAMBLE_VERSION_ROLLBACK,
+
+ /* Kernel preamble not loaded before calling vb2api_get_kernel_size() */
+ VB2_ERROR_API_GET_KERNEL_SIZE_PREAMBLE,
/**********************************************************************
* API-level errors
diff --git a/firmware/lib20/kernel.c b/firmware/lib20/kernel.c
index 3650c7fe..609f2461 100644
--- a/firmware/lib20/kernel.c
+++ b/firmware/lib20/kernel.c
@@ -9,6 +9,7 @@
#include "2misc.h"
#include "2nvstorage.h"
#include "2rsa.h"
+#include "2secdata.h"
#include "2sha.h"
#include "vb2_common.h"
@@ -17,6 +18,27 @@ static const uint8_t *vb2_signature_data_const(const struct vb2_signature *sig)
return (uint8_t *)sig + sig->sig_offset;
}
+/**
+ * Returns non-zero if the kernel needs to have a valid signature, instead of
+ * just a valid hash.
+ */
+static int vb2_need_signed_kernel(struct vb2_context *ctx)
+{
+ /* Recovery kernels are always signed */
+ if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE)
+ return 1;
+
+ /* Normal mode kernels are always signed */
+ if (!(ctx->flags & VB2_CONTEXT_DEVELOPER_MODE))
+ return 1;
+
+ /* Developers may require signed kernels */
+ if (vb2_nv_get(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY))
+ return 1;
+
+ return 0;
+}
+
int vb2_verify_keyblock_hash(const struct vb2_keyblock *block,
uint32_t size,
const struct vb2_workbuf *wb)
@@ -83,7 +105,7 @@ int vb2_load_kernel_keyblock(struct vb2_context *ctx)
int rec_switch = (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) != 0;
int dev_switch = (ctx->flags & VB2_CONTEXT_DEVELOPER_MODE) != 0;
- int need_keyblock_valid = 1;
+ int need_keyblock_valid = vb2_need_signed_kernel(ctx);
int keyblock_is_valid = 1;
int rv;
@@ -91,14 +113,6 @@ int vb2_load_kernel_keyblock(struct vb2_context *ctx)
vb2_workbuf_from_ctx(ctx, &wb);
/*
- * The only time we don't need a valid keyblock is if we're in
- * developer mode and not set to require a signed kernel.
- */
- if (dev_switch && !rec_switch &&
- !vb2_nv_get(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY))
- need_keyblock_valid = 0;
-
- /*
* Clear any previous keyblock-valid flag (for example, from a previous
* kernel where the keyblock was signed but the preamble failed
* verification).
@@ -329,3 +343,102 @@ int vb2_verify_kernel_preamble(struct vb2_kernel_preamble *preamble,
/* Success */
return VB2_SUCCESS;
}
+
+int vb2_load_kernel_preamble(struct vb2_context *ctx)
+{
+ struct vb2_shared_data *sd = vb2_get_sd(ctx);
+ struct vb2_workbuf wb;
+
+ uint8_t *key_data = ctx->workbuf + sd->workbuf_data_key_offset;
+ uint32_t key_size = sd->workbuf_data_key_size;
+ struct vb2_public_key data_key;
+
+ /* Preamble goes in the next unused chunk of work buffer */
+ /* TODO: what's the minimum workbuf size? Kernel preamble is usually
+ * padded to around 64KB. */
+ struct vb2_kernel_preamble *pre;
+ uint32_t pre_size;
+
+ int rv;
+
+ vb2_workbuf_from_ctx(ctx, &wb);
+
+ /* Unpack the kernel data key */
+ if (!sd->workbuf_data_key_size)
+ return VB2_ERROR_KERNEL_PREAMBLE2_DATA_KEY;
+
+ rv = vb2_unpack_key(&data_key, key_data, key_size);
+ if (rv)
+ return rv;
+
+ /* Load the kernel preamble header */
+ pre = vb2_workbuf_alloc(&wb, sizeof(*pre));
+ if (!pre)
+ return VB2_ERROR_KERNEL_PREAMBLE2_WORKBUF_HEADER;
+
+ rv = vb2ex_read_resource(ctx, VB2_RES_KERNEL_VBLOCK,
+ sd->vblock_preamble_offset,
+ pre, sizeof(*pre));
+ if (rv)
+ return rv;
+
+ pre_size = pre->preamble_size;
+
+ /* Load the entire preamble, now that we know how big it is */
+ pre = vb2_workbuf_realloc(&wb, sizeof(*pre), pre_size);
+ if (!pre)
+ return VB2_ERROR_KERNEL_PREAMBLE2_WORKBUF;
+
+ rv = vb2ex_read_resource(ctx, VB2_RES_KERNEL_VBLOCK,
+ sd->vblock_preamble_offset,
+ pre, pre_size);
+ if (rv)
+ return rv;
+
+ /*
+ * Work buffer now contains:
+ * - vb2_shared_data
+ * - kernel key
+ * - packed kernel data key
+ * - kernel preamble
+ */
+
+ /* Verify the preamble */
+ rv = vb2_verify_kernel_preamble(pre, pre_size, &data_key, &wb);
+ if (rv)
+ return rv;
+
+ /*
+ * Kernel preamble version is the lower 16 bits of the composite kernel
+ * version.
+ */
+ if (pre->kernel_version > 0xffff)
+ return VB2_ERROR_KERNEL_PREAMBLE_VERSION_RANGE;
+
+ /* Combine with the key version from vb2_load_kernel_keyblock() */
+ sd->kernel_version |= pre->kernel_version;
+
+ if (vb2_need_signed_kernel(ctx) &&
+ sd->kernel_version < sd->kernel_version_secdatak)
+ return VB2_ERROR_KERNEL_PREAMBLE_VERSION_ROLLBACK;
+
+ /* Keep track of where we put the preamble */
+ sd->workbuf_preamble_offset = vb2_offset_of(ctx->workbuf, pre);
+ sd->workbuf_preamble_size = pre_size;
+
+ /*
+ * Preamble will persist in work buffer after we return.
+ *
+ * Work buffer now contains:
+ * - vb2_shared_data
+ * - kernel key
+ * - packed kernel data key
+ * - kernel preamble
+ *
+ * TODO: we could move the preamble down over the kernel data key
+ * since we don't need it anymore.
+ */
+ ctx->workbuf_used = sd->workbuf_preamble_offset + pre_size;
+
+ return VB2_SUCCESS;
+}
diff --git a/firmware/lib20/misc.c b/firmware/lib20/misc.c
index 13004c69..cdbd0127 100644
--- a/firmware/lib20/misc.c
+++ b/firmware/lib20/misc.c
@@ -272,6 +272,12 @@ int vb2_load_fw_preamble(struct vb2_context *ctx)
* If this is a newer version than in secure storage, and we
* successfully booted the same slot last boot, roll forward the
* version in secure storage.
+ *
+ * Note that this happens before we've verified the firmware data this
+ * boot; we're relying on the indicator that the last boot was
+ * successful. That's ok, because even if the firmware data has a
+ * valid hash, the only way we can know if it's functional is to trust
+ * the status from the last boot.
*/
if (sd->fw_version > sd->fw_version_secdata &&
sd->last_fw_slot == sd->fw_slot &&
diff --git a/tests/vb20_kernel_tests.c b/tests/vb20_kernel_tests.c
index 19c0ed91..2304b321 100644
--- a/tests/vb20_kernel_tests.c
+++ b/tests/vb20_kernel_tests.c
@@ -18,7 +18,7 @@
#include "test_common.h"
/* Common context for tests */
-static uint8_t workbuf[VB2_WORKBUF_RECOMMENDED_SIZE]
+static uint8_t workbuf[VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE]
__attribute__ ((aligned (VB2_WORKBUF_ALIGN)));
static struct vb2_workbuf wb;
static struct vb2_context cc;
@@ -50,6 +50,7 @@ static struct {
static int mock_read_res_fail_on_call;
static int mock_unpack_key_retval;
static int mock_verify_keyblock_retval;
+static int mock_verify_preamble_retval;
/* Type of test to reset for */
enum reset_type {
@@ -95,6 +96,7 @@ static void reset_common_data(enum reset_type t)
mock_read_res_fail_on_call = 0;
mock_unpack_key_retval = VB2_SUCCESS;
mock_verify_keyblock_retval = VB2_SUCCESS;
+ mock_verify_preamble_retval = VB2_SUCCESS;
/* Set up mock data for verifying keyblock */
sd->kernel_version_secdatak = 0x20002;
@@ -181,6 +183,14 @@ int vb2_verify_keyblock(struct vb2_keyblock *block,
return mock_verify_keyblock_retval;
}
+int vb2_verify_kernel_preamble(struct vb2_kernel_preamble *preamble,
+ uint32_t size,
+ const struct vb2_public_key *key,
+ const struct vb2_workbuf *wb)
+{
+ return mock_verify_preamble_retval;
+}
+
/* Tests */
static void verify_keyblock_hash_tests(void)
@@ -382,13 +392,90 @@ static void load_kernel_keyblock_tests(void)
cc.flags |= VB2_CONTEXT_RECOVERY_MODE;
TEST_SUCC(vb2_load_kernel_keyblock(&cc),
"Kernel keyblock rollback rec");
+}
+
+static void load_kernel_preamble_tests(void)
+{
+ struct vb2_kernel_preamble *pre = &mock_vblock.p.pre;
+ int wb_used_before;
+ //uint32_t v;
+
+ /* Test successful call */
+ reset_common_data(FOR_PREAMBLE);
+ wb_used_before = cc.workbuf_used;
+ TEST_SUCC(vb2_load_kernel_preamble(&cc), "preamble good");
+ TEST_EQ(sd->kernel_version, 0x20002, "combined version");
+ TEST_EQ(sd->workbuf_preamble_offset,
+ (wb_used_before + (VB2_WORKBUF_ALIGN - 1)) &
+ ~(VB2_WORKBUF_ALIGN - 1),
+ "preamble offset");
+ TEST_EQ(sd->workbuf_preamble_size, pre->preamble_size, "preamble size");
+ TEST_EQ(cc.workbuf_used,
+ sd->workbuf_preamble_offset + sd->workbuf_preamble_size,
+ "workbuf used");
+ /* Expected failures */
+ reset_common_data(FOR_PREAMBLE);
+ sd->workbuf_data_key_size = 0;
+ TEST_EQ(vb2_load_kernel_preamble(&cc),
+ VB2_ERROR_KERNEL_PREAMBLE2_DATA_KEY,
+ "preamble no data key");
+
+ reset_common_data(FOR_PREAMBLE);
+ mock_unpack_key_retval = VB2_ERROR_UNPACK_KEY_HASH_ALGORITHM;
+ TEST_EQ(vb2_load_kernel_preamble(&cc),
+ VB2_ERROR_UNPACK_KEY_HASH_ALGORITHM,
+ "preamble unpack data key");
+
+ reset_common_data(FOR_PREAMBLE);
+ cc.workbuf_used = cc.workbuf_size -
+ sizeof(struct vb2_kernel_preamble) + 8;
+ TEST_EQ(vb2_load_kernel_preamble(&cc),
+ VB2_ERROR_KERNEL_PREAMBLE2_WORKBUF_HEADER,
+ "preamble not enough workbuf for header");
+
+ reset_common_data(FOR_PREAMBLE);
+ sd->vblock_preamble_offset = sizeof(mock_vblock);
+ TEST_EQ(vb2_load_kernel_preamble(&cc),
+ VB2_ERROR_EX_READ_RESOURCE_SIZE,
+ "preamble read header");
+
+ reset_common_data(FOR_PREAMBLE);
+ cc.workbuf_used = cc.workbuf_size - sizeof(mock_vblock.p) + 8;
+ TEST_EQ(vb2_load_kernel_preamble(&cc),
+ VB2_ERROR_KERNEL_PREAMBLE2_WORKBUF,
+ "preamble not enough workbuf");
+
+ reset_common_data(FOR_PREAMBLE);
+ pre->preamble_size = sizeof(mock_vblock);
+ TEST_EQ(vb2_load_kernel_preamble(&cc),
+ VB2_ERROR_EX_READ_RESOURCE_SIZE,
+ "preamble read full");
+
+ reset_common_data(FOR_PREAMBLE);
+ mock_verify_preamble_retval = VB2_ERROR_MOCK;
+ TEST_EQ(vb2_load_kernel_preamble(&cc),
+ VB2_ERROR_MOCK,
+ "preamble verify");
+
+ reset_common_data(FOR_PREAMBLE);
+ pre->kernel_version = 0x10000;
+ TEST_EQ(vb2_load_kernel_preamble(&cc),
+ VB2_ERROR_KERNEL_PREAMBLE_VERSION_RANGE,
+ "preamble version range");
+
+ reset_common_data(FOR_PREAMBLE);
+ pre->kernel_version = 1;
+ TEST_EQ(vb2_load_kernel_preamble(&cc),
+ VB2_ERROR_KERNEL_PREAMBLE_VERSION_ROLLBACK,
+ "preamble version rollback");
}
int main(int argc, char* argv[])
{
verify_keyblock_hash_tests();
load_kernel_keyblock_tests();
+ load_kernel_preamble_tests();
return gTestSuccess ? 0 : 255;
}