mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-11-24 02:05:01 +00:00
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>
This commit is contained in:
committed by
ChromeOS Commit Bot
parent
b87d1ec118
commit
3d5cd88f90
2
Makefile
2
Makefile
@@ -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
|
||||
|
||||
@@ -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() */
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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 */
|
||||
@@ -100,6 +104,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
|
||||
@@ -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));
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
@@ -156,6 +156,22 @@ int vb2_verify_keyblock(struct vb2_keyblock *block,
|
||||
const struct vb2_public_key *key,
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -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,
|
||||
|
||||
394
tests/vb20_kernel_tests.c
Normal file
394
tests/vb20_kernel_tests.c
Normal file
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user