diff options
author | Randall Spangler <rspangler@chromium.org> | 2015-05-20 17:22:17 -0700 |
---|---|---|
committer | ChromeOS Commit Bot <chromeos-commit-bot@chromium.org> | 2015-05-22 01:22:04 +0000 |
commit | 3d5cd88f90ca38e7b2736bb1569c50049e710453 (patch) | |
tree | 691881177554996b0a9dd51c00976b69491e2600 | |
parent | b87d1ec11813e0f75dafd15fa0a309b506208bc6 (diff) | |
download | vboot-3d5cd88f90ca38e7b2736bb1569c50049e710453.tar.gz |
vboot2: Add routines to load and verify kernel keyblock
These are slightly more complex than the firmware versions, because
they need to deal with developer-signed keyblocks and keyblock flags.
BUG=chromium:487699
BRANCH=none
TEST=make -j runtests
Change-Id: I682c14ddfe729984f2629dfbe66750e5cd5ab75e
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/272541
Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org>
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | firmware/2lib/include/2api.h | 16 | ||||
-rw-r--r-- | firmware/2lib/include/2misc.h | 10 | ||||
-rw-r--r-- | firmware/2lib/include/2return_codes.h | 21 | ||||
-rw-r--r-- | firmware/2lib/include/2struct.h | 42 | ||||
-rw-r--r-- | firmware/lib20/include/vb2_common.h | 16 | ||||
-rw-r--r-- | firmware/lib20/kernel.c | 219 | ||||
-rw-r--r-- | tests/vb20_kernel_tests.c | 394 |
8 files changed, 717 insertions, 3 deletions
@@ -722,6 +722,7 @@ TEST20_NAMES = \ tests/vb20_common2_tests \ tests/vb20_verify_fw.c \ tests/vb20_common3_tests \ + tests/vb20_kernel_tests \ tests/vb20_misc_tests \ tests/vb20_rsa_padding_tests \ tests/vb20_verify_fw @@ -1385,6 +1386,7 @@ run2tests: test_setup ${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} + ${RUNTEST} ${BUILD_RUN}/tests/vb20_kernel_tests ${RUNTEST} ${BUILD_RUN}/tests/vb20_misc_tests ${RUNTEST} ${BUILD_RUN}/tests/vb21_api_tests ${RUNTEST} ${BUILD_RUN}/tests/vb21_common_tests diff --git a/firmware/2lib/include/2api.h b/firmware/2lib/include/2api.h index 0b436842..1341528b 100644 --- a/firmware/2lib/include/2api.h +++ b/firmware/2lib/include/2api.h @@ -177,17 +177,27 @@ struct vb2_context { uint8_t secdatak[VB2_SECDATAK_SIZE]; }; +/* Resource index for vb2ex_read_resource() */ enum vb2_resource_index { /* Google binary block */ VB2_RES_GBB, /* - * Verified boot block (keyblock+preamble). Use VB2_CONTEXT_FW_SLOT_B - * to determine whether this refers to slot A or slot B; vboot will - * set that flag to the proper state before reading the vblock. + * Firmware verified boot block (keyblock+preamble). Use + * VB2_CONTEXT_FW_SLOT_B to determine whether this refers to slot A or + * slot B; vboot will set that flag to the proper state before reading + * the vblock. */ VB2_RES_FW_VBLOCK, + + /* + * Kernel verified boot block (keyblock+preamble) for the current + * kernel partition. Used only by vb2api_kernel_load_vblock(). + * Contents are allowed to change between calls to that function (to + * allow multiple kernels to be examined). + */ + VB2_RES_KERNEL_VBLOCK, }; /* Digest ID for vbapi_get_pcr_digest() */ diff --git a/firmware/2lib/include/2misc.h b/firmware/2lib/include/2misc.h index 1eccde9b..34d6fb82 100644 --- a/firmware/2lib/include/2misc.h +++ b/firmware/2lib/include/2misc.h @@ -141,4 +141,14 @@ int vb2_load_fw_keyblock(struct vb2_context *ctx); */ int vb2_load_fw_preamble(struct vb2_context *ctx); +/** + * Verify the kernel keyblock using the previously-loaded kernel key. + * + * After this call, the data key is stored in the work buffer. + * + * @param ctx Vboot context + * @return VB2_SUCCESS, or error code on error. + */ +int vb2_load_kernel_keyblock(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 671e3d27..d05cd58b 100644 --- a/firmware/2lib/include/2return_codes.h +++ b/firmware/2lib/include/2return_codes.h @@ -406,6 +406,27 @@ enum vb2_return_code { /* Not enough space in work buffer for resource object */ VB2_ERROR_READ_RESOURCE_OBJECT_BUF, + /* Work buffer too small for header in vb2_load_kernel_keyblock() */ + VB2_ERROR_KERNEL_KEYBLOCK_WORKBUF_HEADER, + + /* Work buffer too small for keyblock in vb2_load_kernel_keyblock() */ + VB2_ERROR_KERNEL_KEYBLOCK_WORKBUF, + + /* Keyblock version out of range in vb2_load_kernel_keyblock() */ + VB2_ERROR_KERNEL_KEYBLOCK_VERSION_RANGE, + + /* Keyblock version rollback in vb2_load_kernel_keyblock() */ + VB2_ERROR_KERNEL_KEYBLOCK_VERSION_ROLLBACK, + + /* + * Keyblock flags don't match current mode in + * vb2_load_kernel_keyblock(). + */ + VB2_ERROR_KERNEL_KEYBLOCK_DEV_FLAG, + VB2_ERROR_KERNEL_KEYBLOCK_REC_FLAG, + + + /********************************************************************** * API-level errors */ diff --git a/firmware/2lib/include/2struct.h b/firmware/2lib/include/2struct.h index aa820b70..ca05dfca 100644 --- a/firmware/2lib/include/2struct.h +++ b/firmware/2lib/include/2struct.h @@ -31,12 +31,16 @@ enum vb2_shared_data_flags { VB2_SD_FLAG_MANUAL_RECOVERY = (1 << 0), /* Developer mode is enabled */ + /* TODO: should have been VB2_SD_FLAG_DEV_MODE_ENABLED */ VB2_SD_DEV_MODE_ENABLED = (1 << 1), /* * TODO: might be nice to add flags for why dev mode is enabled - via * gbb, virtual dev switch, or forced on for testing. */ + + /* Kernel keyblock was verified by signature (not just hash) */ + VB2_SD_FLAG_KERNEL_SIGNED = (1 << 2), }; /* Flags for vb2_shared_data.status */ @@ -101,6 +105,25 @@ struct vb2_shared_data { uint32_t status; /********************************************************************** + * Data from kernel verification stage. + * + * TODO: shouldn't be part of the main struct, since that needlessly + * uses more memory during firmware verification. + */ + + /* + * Version for the current kernel (top 16 bits = key, lower 16 bits = + * kernel preamble). + * + * TODO: Make this a union to allow getting/setting those versions + * separately? + */ + uint32_t kernel_version; + + /* Kernel version from secdatak (must be <= kernel_version to boot) */ + uint32_t kernel_version_secdatak; + + /********************************************************************** * Temporary variables used during firmware verification. These don't * really need to persist through to the OS, but there's nowhere else * we can put them. @@ -151,6 +174,25 @@ struct vb2_shared_data { /* Amount of data we still expect to hash */ uint32_t hash_remaining_size; + /********************************************************************** + * Temporary variables used during kernel verification. These don't + * really need to persist through to the OS, but there's nowhere else + * we can put them. + * + * TODO: make a union with the firmware verification temp variables, + * or make both of them workbuf-allocated sub-structs, so that we can + * overlap them so kernel variables don't bloat firmware verification + * stage memory requirements. + */ + + /* + * Offset and size of packed kernel key in work buffer. Size is 0 if + * subkey is not stored in the work buffer. Note that kernel key may + * be inside the firmware preamble. + */ + uint32_t workbuf_kernel_key_offset; + uint32_t workbuf_kernel_key_size; + } __attribute__((packed)); /****************************************************************************/ diff --git a/firmware/lib20/include/vb2_common.h b/firmware/lib20/include/vb2_common.h index be891ca9..0ab89cab 100644 --- a/firmware/lib20/include/vb2_common.h +++ b/firmware/lib20/include/vb2_common.h @@ -157,6 +157,22 @@ int vb2_verify_keyblock(struct vb2_keyblock *block, const struct vb2_workbuf *wb); /** + * Verify a key block using its hash. + * + * Header fields are also checked for sanity. Does not verify key index or key + * block flags. Use this for self-signed keyblocks in developer mode. + * + * @param block Key block to verify + * @param size Size of key block buffer + * @param key Key to use to verify block + * @param wb Work buffer + * @return VB2_SUCCESS, or non-zero error code if error. + */ +int vb2_verify_keyblock_hash(const struct vb2_keyblock *block, + uint32_t size, + const struct vb2_workbuf *wb); + +/** * Check the sanity of a firmware preamble using a public key. * * The signature in the preamble is destroyed during the check. diff --git a/firmware/lib20/kernel.c b/firmware/lib20/kernel.c index ec47ce77..3650c7fe 100644 --- a/firmware/lib20/kernel.c +++ b/firmware/lib20/kernel.c @@ -6,10 +6,229 @@ */ #include "2sysincludes.h" +#include "2misc.h" +#include "2nvstorage.h" #include "2rsa.h" #include "2sha.h" #include "vb2_common.h" +static const uint8_t *vb2_signature_data_const(const struct vb2_signature *sig) +{ + return (uint8_t *)sig + sig->sig_offset; +} + +int vb2_verify_keyblock_hash(const struct vb2_keyblock *block, + uint32_t size, + const struct vb2_workbuf *wb) +{ + const struct vb2_signature *sig = &block->keyblock_hash; + struct vb2_workbuf wblocal = *wb; + struct vb2_digest_context *dc; + uint8_t *digest; + uint32_t digest_size; + int rv; + + /* Sanity check keyblock before attempting hash check of data */ + rv = vb2_check_keyblock(block, size, sig); + if (rv) + return rv; + + VB2_DEBUG("Checking key block hash...\n"); + + /* Digest goes at start of work buffer */ + digest_size = vb2_digest_size(VB2_HASH_SHA512); + digest = vb2_workbuf_alloc(&wblocal, digest_size); + if (!digest) + return VB2_ERROR_VDATA_WORKBUF_DIGEST; + + /* Hashing requires temp space for the context */ + dc = vb2_workbuf_alloc(&wblocal, sizeof(*dc)); + if (!dc) + return VB2_ERROR_VDATA_WORKBUF_HASHING; + + rv = vb2_digest_init(dc, VB2_HASH_SHA512); + if (rv) + return rv; + + rv = vb2_digest_extend(dc, (const uint8_t *)block, sig->data_size); + if (rv) + return rv; + + rv = vb2_digest_finalize(dc, digest, digest_size); + if (rv) + return rv; + + if (vb2_safe_memcmp(vb2_signature_data_const(sig), digest, + digest_size) != 0) { + VB2_DEBUG("Invalid key block hash.\n"); + return VB2_ERROR_KEYBLOCK_SIG_INVALID; + } + + /* Success */ + return VB2_SUCCESS; +} + +int vb2_load_kernel_keyblock(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; + struct vb2_packed_key *packed_key; + struct vb2_public_key kernel_key; + + struct vb2_keyblock *kb; + uint32_t block_size; + + 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 keyblock_is_valid = 1; + + int rv; + + 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). + */ + sd->flags &= ~VB2_SD_FLAG_KERNEL_SIGNED; + + /* Unpack the kernel key */ + key_data = ctx->workbuf + sd->workbuf_kernel_key_offset; + key_size = sd->workbuf_kernel_key_size; + rv = vb2_unpack_key(&kernel_key, key_data, key_size); + if (rv) + return rv; + + /* Load the kernel keyblock header after the root key */ + kb = vb2_workbuf_alloc(&wb, sizeof(*kb)); + if (!kb) + return VB2_ERROR_KERNEL_KEYBLOCK_WORKBUF_HEADER; + + rv = vb2ex_read_resource(ctx, VB2_RES_KERNEL_VBLOCK, 0, kb, + sizeof(*kb)); + if (rv) + return rv; + + block_size = kb->keyblock_size; + + /* + * Load the entire keyblock, now that we know how big it is. Note that + * we're loading the entire keyblock instead of just the piece after + * the header. That means we re-read the header. But that's a tiny + * amount of data, and it makes the code much more straightforward. + */ + kb = vb2_workbuf_realloc(&wb, sizeof(*kb), block_size); + if (!kb) + return VB2_ERROR_KERNEL_KEYBLOCK_WORKBUF; + + rv = vb2ex_read_resource(ctx, VB2_RES_KERNEL_VBLOCK, 0, kb, block_size); + if (rv) + return rv; + + /* Verify the keyblock */ + rv = vb2_verify_keyblock(kb, block_size, &kernel_key, &wb); + if (rv) { + keyblock_is_valid = 0; + if (need_keyblock_valid) + return rv; + + /* Signature is invalid, but hash may be fine */ + rv = vb2_verify_keyblock_hash(kb, block_size, &wb); + if (rv) + return rv; + } + + /* Check the key block flags against the current boot mode */ + if (!(kb->keyblock_flags & + (dev_switch ? VB2_KEY_BLOCK_FLAG_DEVELOPER_1 : + VB2_KEY_BLOCK_FLAG_DEVELOPER_0))) { + VB2_DEBUG("Key block developer flag mismatch.\n"); + keyblock_is_valid = 0; + if (need_keyblock_valid) + return VB2_ERROR_KERNEL_KEYBLOCK_DEV_FLAG; + } + if (!(kb->keyblock_flags & + (rec_switch ? VB2_KEY_BLOCK_FLAG_RECOVERY_1 : + VB2_KEY_BLOCK_FLAG_RECOVERY_0))) { + VB2_DEBUG("Key block recovery flag mismatch.\n"); + keyblock_is_valid = 0; + if (need_keyblock_valid) + return VB2_ERROR_KERNEL_KEYBLOCK_REC_FLAG; + } + + /* Check for keyblock rollback if not in recovery mode */ + /* Key version is the upper 16 bits of the composite version */ + if (!rec_switch && kb->data_key.key_version > 0xffff) { + keyblock_is_valid = 0; + if (need_keyblock_valid) + return VB2_ERROR_KERNEL_KEYBLOCK_VERSION_RANGE; + } + if (!rec_switch && kb->data_key.key_version < + (sd->kernel_version_secdatak >> 16)) { + keyblock_is_valid = 0; + if (need_keyblock_valid) + return VB2_ERROR_KERNEL_KEYBLOCK_VERSION_ROLLBACK; + } + + sd->kernel_version = kb->data_key.key_version << 16; + + /* + * At this point, we've checked everything. The kernel keyblock is at + * least self-consistent, and has either a valid signature or a valid + * hash. Track if it had a valid signature (that is, would we have + * been willing to boot it even if developer mode was off). + */ + if (keyblock_is_valid) + sd->flags |= VB2_SD_FLAG_KERNEL_SIGNED; + + /* Preamble follows the keyblock in the vblock */ + sd->vblock_preamble_offset = kb->keyblock_size; + + /* + * Keep just the data key from the vblock. This follows the kernel key + * (which we might still need to verify the next kernel, if the + * assoiciated kernel preamble and data don't verify). + */ + sd->workbuf_data_key_offset = ctx->workbuf_used; + key_data = ctx->workbuf + sd->workbuf_data_key_offset; + packed_key = (struct vb2_packed_key *)key_data; + memmove(packed_key, &kb->data_key, sizeof(*packed_key)); + packed_key->key_offset = sizeof(*packed_key); + memmove(key_data + packed_key->key_offset, + (uint8_t*)&kb->data_key + kb->data_key.key_offset, + packed_key->key_size); + + /* Save the packed key size */ + sd->workbuf_data_key_size = + packed_key->key_offset + packed_key->key_size; + + /* + * Data key will persist in the workbuf after we return. + * + * Work buffer now contains: + * - vb2_shared_data + * - kernel key + * - packed kernel data key + */ + ctx->workbuf_used = sd->workbuf_data_key_offset + + sd->workbuf_data_key_size; + + return VB2_SUCCESS; +} + int vb2_verify_kernel_preamble(struct vb2_kernel_preamble *preamble, uint32_t size, const struct vb2_public_key *key, diff --git a/tests/vb20_kernel_tests.c b/tests/vb20_kernel_tests.c new file mode 100644 index 00000000..19c0ed91 --- /dev/null +++ b/tests/vb20_kernel_tests.c @@ -0,0 +1,394 @@ +/* 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 + */ + +#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_WORKBUF_RECOMMENDED_SIZE] + __attribute__ ((aligned (VB2_WORKBUF_ALIGN))); +static struct vb2_workbuf wb; +static struct vb2_context cc; +static struct vb2_shared_data *sd; + +/* Mocked function data */ + +static struct { + struct vb2_gbb_header h; + struct vb2_packed_key recovery_key; + char recovery_key_data[32]; +} mock_gbb; + +static struct { + /* Keyblock */ + struct { + struct vb2_keyblock kb; + char data_key_data[16]; + uint8_t kbdata[128]; + uint8_t hash[VB2_SHA512_DIGEST_SIZE]; + } k; + /* Preamble follows keyblock */ + struct { + struct vb2_kernel_preamble pre; + uint8_t predata[128]; + } p; +} mock_vblock; + +static int mock_read_res_fail_on_call; +static int mock_unpack_key_retval; +static int mock_verify_keyblock_retval; + +/* Type of test to reset for */ +enum reset_type { + FOR_KEYBLOCK, + FOR_PREAMBLE +}; + +static void rehash_keyblock(void) +{ + struct vb2_keyblock *kb = &mock_vblock.k.kb; + struct vb2_signature *hashsig = &mock_vblock.k.kb.keyblock_hash; + struct vb2_digest_context dc; + + + hashsig->sig_offset = vb2_offset_of(hashsig, mock_vblock.k.hash); + hashsig->sig_size = sizeof(mock_vblock.k.hash); + hashsig->data_size = hashsig->sig_offset; + vb2_digest_init(&dc, VB2_HASH_SHA512); + vb2_digest_extend(&dc, (const uint8_t *)kb, hashsig->data_size); + vb2_digest_finalize(&dc, mock_vblock.k.hash, hashsig->sig_size); +} + +static void reset_common_data(enum reset_type t) +{ + struct vb2_keyblock *kb = &mock_vblock.k.kb; + struct vb2_kernel_preamble *pre = &mock_vblock.p.pre; + + 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); + + mock_read_res_fail_on_call = 0; + mock_unpack_key_retval = VB2_SUCCESS; + mock_verify_keyblock_retval = VB2_SUCCESS; + + /* Set up mock data for verifying keyblock */ + sd->kernel_version_secdatak = 0x20002; + vb2_secdatak_set(&cc, VB2_SECDATAK_VERSIONS, 0x20002); + + 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); + + kb->keyblock_size = sizeof(mock_vblock.k); + memcpy(kb->magic, KEY_BLOCK_MAGIC, KEY_BLOCK_MAGIC_SIZE); + + kb->keyblock_flags = VB2_KEY_BLOCK_FLAG_DEVELOPER_1 | + VB2_KEY_BLOCK_FLAG_DEVELOPER_0 | + VB2_KEY_BLOCK_FLAG_RECOVERY_1 | VB2_KEY_BLOCK_FLAG_RECOVERY_0; + kb->header_version_major = KEY_BLOCK_HEADER_VERSION_MAJOR; + kb->header_version_minor = KEY_BLOCK_HEADER_VERSION_MINOR; + kb->data_key.algorithm = 7; + kb->data_key.key_version = 2; + kb->data_key.key_offset = + vb2_offset_of(&mock_vblock.k, &mock_vblock.k.data_key_data) - + vb2_offset_of(&mock_vblock.k, &kb->data_key); + kb->data_key.key_size = sizeof(mock_vblock.k.data_key_data); + strcpy(mock_vblock.k.data_key_data, "data key data!!"); + rehash_keyblock(); + + pre->preamble_size = sizeof(mock_vblock.p); + pre->kernel_version = 2; + + /* If verifying preamble, verify keyblock first to set up data key */ + if (t == FOR_PREAMBLE) + vb2_load_kernel_keyblock(&cc); +}; + +/* 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; + case VB2_RES_KERNEL_VBLOCK: + rptr = (uint8_t *)&mock_vblock; + rsize = sizeof(mock_vblock); + 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_unpack_key(struct vb2_public_key *key, + const uint8_t *buf, + uint32_t size) +{ + key->arrsize = 0; + return mock_unpack_key_retval; +} + +int vb2_verify_keyblock(struct vb2_keyblock *block, + uint32_t size, + const struct vb2_public_key *key, + const struct vb2_workbuf *wb) +{ + return mock_verify_keyblock_retval; +} + +/* Tests */ + +static void verify_keyblock_hash_tests(void) +{ + struct vb2_keyblock *kb = &mock_vblock.k.kb; + + /* Test successful call */ + reset_common_data(FOR_KEYBLOCK); + TEST_SUCC(vb2_verify_keyblock_hash(kb, kb->keyblock_size, &wb), + "Keyblock hash good"); + + /* Sanity check keyblock */ + reset_common_data(FOR_KEYBLOCK); + kb->magic[0] ^= 0xd0; + TEST_EQ(vb2_verify_keyblock_hash(kb, kb->keyblock_size, &wb), + VB2_ERROR_KEYBLOCK_MAGIC, "Keyblock sanity check"); + + /* + * Sanity check should be looking at the keyblock hash struct, not the + * keyblock signature struct. + */ + reset_common_data(FOR_KEYBLOCK); + kb->keyblock_hash.data_size = sizeof(*kb) - 1; + TEST_EQ(vb2_verify_keyblock_hash(kb, kb->keyblock_size, &wb), + VB2_ERROR_KEYBLOCK_SIGNED_TOO_LITTLE, + "Keyblock check hash sig"); + + reset_common_data(FOR_KEYBLOCK); + wb.size = VB2_SHA512_DIGEST_SIZE - 1; + TEST_EQ(vb2_verify_keyblock_hash(kb, kb->keyblock_size, &wb), + VB2_ERROR_VDATA_WORKBUF_DIGEST, + "Keyblock check hash workbuf digest"); + + reset_common_data(FOR_KEYBLOCK); + wb.size = VB2_SHA512_DIGEST_SIZE + + sizeof(struct vb2_digest_context) - 1; + TEST_EQ(vb2_verify_keyblock_hash(kb, kb->keyblock_size, &wb), + VB2_ERROR_VDATA_WORKBUF_HASHING, + "Keyblock check hash workbuf hashing"); + + reset_common_data(FOR_KEYBLOCK); + mock_vblock.k.data_key_data[0] ^= 0xa0; + TEST_EQ(vb2_verify_keyblock_hash(kb, kb->keyblock_size, &wb), + VB2_ERROR_KEYBLOCK_SIG_INVALID, + "Keyblock check hash invalid"); +} + +static void load_kernel_keyblock_tests(void) +{ + struct vb2_keyblock *kb = &mock_vblock.k.kb; + struct vb2_packed_key *k; + int wb_used_before; + + /* Test successful call */ + reset_common_data(FOR_KEYBLOCK); + wb_used_before = cc.workbuf_used; + TEST_SUCC(vb2_load_kernel_keyblock(&cc), "Kernel keyblock good"); + TEST_NEQ(sd->flags & VB2_SD_FLAG_KERNEL_SIGNED, 0, " Kernel signed"); + TEST_EQ(sd->kernel_version, 0x20000, "keyblock version"); + TEST_EQ(sd->vblock_preamble_offset, sizeof(mock_vblock.k), + "preamble offset"); + TEST_EQ(sd->workbuf_data_key_offset, + (wb_used_before + (VB2_WORKBUF_ALIGN - 1)) & + ~(VB2_WORKBUF_ALIGN - 1), + "keyblock data key offset"); + TEST_EQ(cc.workbuf_used, + sd->workbuf_data_key_offset + sd->workbuf_data_key_size, + "workbuf used"); + + /* Make sure data key was properly saved */ + k = (struct vb2_packed_key *)(cc.workbuf + sd->workbuf_data_key_offset); + TEST_EQ(k->algorithm, 7, "data key algorithm"); + TEST_EQ(k->key_version, 2, "data key version"); + TEST_EQ(k->key_size, sizeof(mock_vblock.k.data_key_data), + "data key size"); + TEST_EQ(memcmp(cc.workbuf + sd->workbuf_data_key_offset + + k->key_offset, mock_vblock.k.data_key_data, + sizeof(mock_vblock.k.data_key_data)), + 0, "data key data"); + TEST_EQ(cc.workbuf_used, + sd->workbuf_data_key_offset + sd->workbuf_data_key_size, + "workbuf used after"); + + /* Test failures */ + reset_common_data(FOR_KEYBLOCK); + mock_unpack_key_retval = VB2_ERROR_MOCK; + TEST_EQ(vb2_load_kernel_keyblock(&cc), + VB2_ERROR_MOCK, "Kernel keyblock unpack key"); + + reset_common_data(FOR_KEYBLOCK); + cc.workbuf_used = cc.workbuf_size - (sizeof(*kb) - 1); + TEST_EQ(vb2_load_kernel_keyblock(&cc), + VB2_ERROR_KERNEL_KEYBLOCK_WORKBUF_HEADER, + "Kernel keyblock workbuf header"); + + reset_common_data(FOR_KEYBLOCK); + mock_read_res_fail_on_call = 1; + TEST_EQ(vb2_load_kernel_keyblock(&cc), + VB2_ERROR_MOCK, "Kernel keyblock read header"); + + reset_common_data(FOR_KEYBLOCK); + cc.workbuf_used = cc.workbuf_size - (kb->keyblock_size - 1); + TEST_EQ(vb2_load_kernel_keyblock(&cc), + VB2_ERROR_KERNEL_KEYBLOCK_WORKBUF, + "Kernel keyblock workbuf"); + + reset_common_data(FOR_KEYBLOCK); + mock_read_res_fail_on_call = 2; + TEST_EQ(vb2_load_kernel_keyblock(&cc), + VB2_ERROR_MOCK, "Kernel keyblock read"); + + /* Normally, require signed keyblock */ + reset_common_data(FOR_KEYBLOCK); + mock_verify_keyblock_retval = VB2_ERROR_MOCK; + TEST_EQ(vb2_load_kernel_keyblock(&cc), + VB2_ERROR_MOCK, "Verify keyblock"); + + /* Not in dev mode */ + reset_common_data(FOR_KEYBLOCK); + cc.flags |= VB2_CONTEXT_DEVELOPER_MODE; + mock_verify_keyblock_retval = VB2_ERROR_MOCK; + TEST_SUCC(vb2_load_kernel_keyblock(&cc), "Kernel keyblock hash good"); + TEST_EQ(sd->flags & VB2_SD_FLAG_KERNEL_SIGNED, 0, " Kernel signed"); + + /* But we do in dev+rec mode */ + reset_common_data(FOR_KEYBLOCK); + cc.flags |= VB2_CONTEXT_DEVELOPER_MODE | VB2_CONTEXT_RECOVERY_MODE; + mock_verify_keyblock_retval = VB2_ERROR_MOCK; + TEST_EQ(vb2_load_kernel_keyblock(&cc), + VB2_ERROR_MOCK, "Kernel keyblock dev+rec"); + + /* Test keyblock flags matching mode */ + reset_common_data(FOR_KEYBLOCK); + kb->keyblock_flags &= ~VB2_KEY_BLOCK_FLAG_DEVELOPER_0; + TEST_EQ(vb2_load_kernel_keyblock(&cc), + VB2_ERROR_KERNEL_KEYBLOCK_DEV_FLAG, + "Kernel keyblock dev only"); + + reset_common_data(FOR_KEYBLOCK); + kb->keyblock_flags &= ~VB2_KEY_BLOCK_FLAG_RECOVERY_0; + TEST_EQ(vb2_load_kernel_keyblock(&cc), + VB2_ERROR_KERNEL_KEYBLOCK_REC_FLAG, + "Kernel keyblock rec only"); + + reset_common_data(FOR_KEYBLOCK); + kb->keyblock_flags &= ~VB2_KEY_BLOCK_FLAG_RECOVERY_1; + cc.flags |= VB2_CONTEXT_RECOVERY_MODE; + TEST_EQ(vb2_load_kernel_keyblock(&cc), + VB2_ERROR_KERNEL_KEYBLOCK_REC_FLAG, + "Kernel keyblock not rec"); + + reset_common_data(FOR_KEYBLOCK); + kb->keyblock_flags &= ~VB2_KEY_BLOCK_FLAG_DEVELOPER_0; + kb->keyblock_flags &= ~VB2_KEY_BLOCK_FLAG_RECOVERY_0; + cc.flags |= VB2_CONTEXT_RECOVERY_MODE; + TEST_EQ(vb2_load_kernel_keyblock(&cc), + VB2_ERROR_KERNEL_KEYBLOCK_DEV_FLAG, + "Kernel keyblock rec but not dev+rec"); + + reset_common_data(FOR_KEYBLOCK); + kb->keyblock_flags &= ~VB2_KEY_BLOCK_FLAG_DEVELOPER_0; + kb->keyblock_flags &= ~VB2_KEY_BLOCK_FLAG_RECOVERY_0; + cc.flags |= VB2_CONTEXT_DEVELOPER_MODE | VB2_CONTEXT_RECOVERY_MODE; + TEST_SUCC(vb2_load_kernel_keyblock(&cc), + "Kernel keyblock flags dev+rec"); + + /* System in dev mode ignores flags */ + reset_common_data(FOR_KEYBLOCK); + cc.flags |= VB2_CONTEXT_DEVELOPER_MODE; + kb->keyblock_flags = 0; + TEST_SUCC(vb2_load_kernel_keyblock(&cc), "Kernel keyblock dev flags"); + + /* Test rollback */ + reset_common_data(FOR_KEYBLOCK); + kb->data_key.key_version = 0x10000; + TEST_EQ(vb2_load_kernel_keyblock(&cc), + VB2_ERROR_KERNEL_KEYBLOCK_VERSION_RANGE, + "Kernel keyblock version range"); + + reset_common_data(FOR_KEYBLOCK); + kb->data_key.key_version = 1; + TEST_EQ(vb2_load_kernel_keyblock(&cc), + VB2_ERROR_KERNEL_KEYBLOCK_VERSION_ROLLBACK, + "Kernel keyblock rollback"); + + /* Rollback ok in developer mode */ + reset_common_data(FOR_KEYBLOCK); + kb->data_key.key_version = 1; + cc.flags |= VB2_CONTEXT_DEVELOPER_MODE; + TEST_SUCC(vb2_load_kernel_keyblock(&cc), + "Kernel keyblock rollback dev"); + + /* + * Recovery keyblocks aren't versioned (and even if they were, it + * wouldn't be with the same version as a normal kernel). + */ + reset_common_data(FOR_KEYBLOCK); + kb->data_key.key_version = 1; + cc.flags |= VB2_CONTEXT_RECOVERY_MODE; + TEST_SUCC(vb2_load_kernel_keyblock(&cc), + "Kernel keyblock rollback rec"); + +} + +int main(int argc, char* argv[]) +{ + verify_keyblock_hash_tests(); + load_kernel_keyblock_tests(); + + return gTestSuccess ? 0 : 255; +} |