diff options
author | Randall Spangler <rspangler@chromium.org> | 2015-05-29 10:53:11 -0700 |
---|---|---|
committer | ChromeOS Commit Bot <chromeos-commit-bot@chromium.org> | 2015-06-04 19:32:56 +0000 |
commit | 22da78ce594d1e9893fe418e4a021db17002f777 (patch) | |
tree | f6a0ab675426653a9cb9a5febd90e107b23d8bb0 | |
parent | 7a1c0d1ec852ee86237aba7f3c87d642ca8e00b0 (diff) | |
download | vboot-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.h | 13 | ||||
-rw-r--r-- | firmware/2lib/include/2misc.h | 10 | ||||
-rw-r--r-- | firmware/2lib/include/2return_codes.h | 16 | ||||
-rw-r--r-- | firmware/lib20/kernel.c | 131 | ||||
-rw-r--r-- | firmware/lib20/misc.c | 6 | ||||
-rw-r--r-- | tests/vb20_kernel_tests.c | 89 |
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; } |