diff options
author | Joel Kitching <kitching@google.com> | 2019-08-28 17:45:05 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-09-23 17:54:09 +0000 |
commit | 967ba853d88b7803c73f3adb94b8717d001a077b (patch) | |
tree | 2ce2dc70ead38a5f687f2c5b822a2d19d38469f2 | |
parent | aaf394335cc4e287a1ffb6332311559b2b29c41f (diff) | |
download | vboot-967ba853d88b7803c73f3adb94b8717d001a077b.tar.gz |
vboot/secdata: implement vboot2 FWMP support
Implement FWMP support in vboot2. Currently, the data structure
is just accessed directly, checking to see whether its `flags`
member contains particular flags. We'd like to change this to
follow the same scheme as secdata_firmware and secdata_kernel.
This CL also updates some functions, comments, and tests related
to secdata_firmware and secdata_kernel to ensure consistency
between code for the secdata spaces.
BUG=b:124141368, chromium:972956
TEST=make clean && make runtests
BRANCH=none
Change-Id: Ia0d67532cc6e077e170ffb25d0bc587b1d53edf3
Signed-off-by: Joel Kitching <kitching@google.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/1773088
Reviewed-by: Joel Kitching <kitching@chromium.org>
Tested-by: Joel Kitching <kitching@chromium.org>
Commit-Queue: Joel Kitching <kitching@chromium.org>
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | firmware/2lib/2api.c | 2 | ||||
-rw-r--r-- | firmware/2lib/2misc.c | 6 | ||||
-rw-r--r-- | firmware/2lib/2secdata_firmware.c | 8 | ||||
-rw-r--r-- | firmware/2lib/2secdata_fwmp.c | 130 | ||||
-rw-r--r-- | firmware/2lib/2secdata_kernel.c | 6 | ||||
-rw-r--r-- | firmware/2lib/include/2api.h | 70 | ||||
-rw-r--r-- | firmware/2lib/include/2constants.h | 2 | ||||
-rw-r--r-- | firmware/2lib/include/2misc.h | 2 | ||||
-rw-r--r-- | firmware/2lib/include/2return_codes.h | 12 | ||||
-rw-r--r-- | firmware/2lib/include/2secdata.h | 85 | ||||
-rw-r--r-- | firmware/2lib/include/2struct.h | 3 | ||||
-rw-r--r-- | firmware/lib/mocked_secdata_tpm.c | 2 | ||||
-rw-r--r-- | tests/vb20_verify_fw.c | 8 | ||||
-rw-r--r-- | tests/vb2_api_tests.c | 12 | ||||
-rw-r--r-- | tests/vb2_misc_tests.c | 23 | ||||
-rw-r--r-- | tests/vb2_secdata_firmware_tests.c | 117 | ||||
-rw-r--r-- | tests/vb2_secdata_fwmp_tests.c | 242 | ||||
-rw-r--r-- | tests/vb2_secdata_kernel_tests.c | 100 |
19 files changed, 692 insertions, 141 deletions
@@ -359,6 +359,7 @@ FWLIB2X_SRCS = \ firmware/2lib/2packed_key.c \ firmware/2lib/2rsa.c \ firmware/2lib/2secdata_firmware.c \ + firmware/2lib/2secdata_fwmp.c \ firmware/2lib/2secdata_kernel.c \ firmware/2lib/2sha1.c \ firmware/2lib/2sha256.c \ @@ -712,6 +713,7 @@ TEST2X_NAMES = \ tests/vb2_nvstorage_tests \ tests/vb2_rsa_utility_tests \ tests/vb2_secdata_firmware_tests \ + tests/vb2_secdata_fwmp_tests \ tests/vb2_secdata_kernel_tests \ tests/vb2_sha_tests \ tests/hmac_test @@ -1279,6 +1281,7 @@ run2tests: test_setup ${RUNTEST} ${BUILD_RUN}/tests/vb2_nvstorage_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_rsa_utility_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_secdata_firmware_tests + ${RUNTEST} ${BUILD_RUN}/tests/vb2_secdata_fwmp_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_secdata_kernel_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_sha_tests ${RUNTEST} ${BUILD_RUN}/tests/vb20_api_tests diff --git a/firmware/2lib/2api.c b/firmware/2lib/2api.c index e93894d7..7e168cb0 100644 --- a/firmware/2lib/2api.c +++ b/firmware/2lib/2api.c @@ -63,7 +63,7 @@ vb2_error_t vb2api_fw_phase1(struct vb2_context *ctx) vb2_fail(ctx, VB2_RECOVERY_SECDATA_FIRMWARE_INIT, rv); /* Load and parse the GBB header */ - rv = vb2_fw_parse_gbb(ctx); + rv = vb2_fw_init_gbb(ctx); if (rv) vb2_fail(ctx, VB2_RECOVERY_GBB_HEADER, rv); diff --git a/firmware/2lib/2misc.c b/firmware/2lib/2misc.c index 98b5c33a..c3cae7c2 100644 --- a/firmware/2lib/2misc.c +++ b/firmware/2lib/2misc.c @@ -209,7 +209,7 @@ void vb2_check_recovery(struct vb2_context *ctx) } } -vb2_error_t vb2_fw_parse_gbb(struct vb2_context *ctx) +vb2_error_t vb2_fw_init_gbb(struct vb2_context *ctx) { struct vb2_shared_data *sd = vb2_get_sd(ctx); struct vb2_gbb_header *gbb; @@ -231,6 +231,10 @@ vb2_error_t vb2_fw_parse_gbb(struct vb2_context *ctx) sd->gbb_offset = vb2_offset_of(sd, gbb); ctx->workbuf_used = vb2_offset_of(ctx->workbuf, wb.buf); + /* Set any context flags based on GBB flags */ + if (gbb->flags & VB2_GBB_FLAG_DISABLE_FWMP) + ctx->flags |= VB2_CONTEXT_NO_SECDATA_FWMP; + return VB2_SUCCESS; } diff --git a/firmware/2lib/2secdata_firmware.c b/firmware/2lib/2secdata_firmware.c index 1ce5b29b..74b7ff1f 100644 --- a/firmware/2lib/2secdata_firmware.c +++ b/firmware/2lib/2secdata_firmware.c @@ -32,7 +32,7 @@ vb2_error_t vb2api_secdata_firmware_check(struct vb2_context *ctx) return VB2_SUCCESS; } -vb2_error_t vb2api_secdata_firmware_create(struct vb2_context *ctx) +uint32_t vb2api_secdata_firmware_create(struct vb2_context *ctx) { struct vb2_secdata_firmware *sec = (struct vb2_secdata_firmware *)ctx->secdata_firmware; @@ -49,7 +49,7 @@ vb2_error_t vb2api_secdata_firmware_create(struct vb2_context *ctx) /* Mark as changed */ ctx->flags |= VB2_CONTEXT_SECDATA_FIRMWARE_CHANGED; - return VB2_SUCCESS; + return sizeof(*sec); } vb2_error_t vb2_secdata_firmware_init(struct vb2_context *ctx) @@ -64,7 +64,7 @@ vb2_error_t vb2_secdata_firmware_init(struct vb2_context *ctx) /* Set status flag */ sd->status |= VB2_SD_STATUS_SECDATA_FIRMWARE_INIT; - /* Read this now to make sure crossystem has it even in rec mode. */ + /* Read this now to make sure crossystem has it even in rec mode */ rv = vb2_secdata_firmware_get(ctx, VB2_SECDATA_FIRMWARE_VERSIONS, &sd->fw_version_secdata); if (rv) @@ -109,7 +109,7 @@ vb2_error_t vb2_secdata_firmware_set(struct vb2_context *ctx, if (!(vb2_get_sd(ctx)->status & VB2_SD_STATUS_SECDATA_FIRMWARE_INIT)) return VB2_ERROR_SECDATA_FIRMWARE_SET_UNINITIALIZED; - /* If not changing the value, don't regenerate the CRC. */ + /* If not changing the value, don't regenerate the CRC */ if (vb2_secdata_firmware_get(ctx, param, &now) == VB2_SUCCESS && now == value) return VB2_SUCCESS; diff --git a/firmware/2lib/2secdata_fwmp.c b/firmware/2lib/2secdata_fwmp.c new file mode 100644 index 00000000..774a7fa8 --- /dev/null +++ b/firmware/2lib/2secdata_fwmp.c @@ -0,0 +1,130 @@ +/* Copyright 2019 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. + * + * Firmware management parameters (FWMP) APIs + */ + +#include "2crc8.h" +#include "2common.h" +#include "2misc.h" +#include "2secdata.h" + +uint32_t vb2_secdata_fwmp_crc(struct vb2_secdata_fwmp *sec) +{ + int version_offset = offsetof(struct vb2_secdata_fwmp, struct_version); + return vb2_crc8((void *)sec + version_offset, + sec->struct_size - version_offset); +} + +vb2_error_t vb2api_secdata_fwmp_check(struct vb2_context *ctx, uint8_t *size) +{ + struct vb2_secdata_fwmp *sec = + (struct vb2_secdata_fwmp *)&ctx->secdata_fwmp; + + /* Verify that at least the minimum size has been read */ + if (*size < VB2_SECDATA_FWMP_MIN_SIZE) { + VB2_DEBUG("FWMP: missing %d bytes for minimum size\n", + VB2_SECDATA_FWMP_MIN_SIZE - *size); + *size = VB2_SECDATA_FWMP_MIN_SIZE; + return VB2_ERROR_SECDATA_FWMP_INCOMPLETE; + } + + /* Verify that struct_size is reasonable */ + if (sec->struct_size < VB2_SECDATA_FWMP_MIN_SIZE || + sec->struct_size > VB2_SECDATA_FWMP_MAX_SIZE) { + VB2_DEBUG("FWMP: invalid size: %d\n", sec->struct_size); + return VB2_ERROR_SECDATA_FWMP_SIZE; + } + + /* Verify that we have read full structure */ + if (*size < sec->struct_size) { + VB2_DEBUG("FWMP: missing %d bytes\n", sec->struct_size - *size); + *size = sec->struct_size; + return VB2_ERROR_SECDATA_FWMP_INCOMPLETE; + } + *size = sec->struct_size; + + /* Verify CRC */ + if (sec->crc8 != vb2_secdata_fwmp_crc(sec)) { + VB2_DEBUG("FWMP: bad CRC\n"); + return VB2_ERROR_SECDATA_FWMP_CRC; + } + + /* Verify major version is compatible */ + if ((sec->struct_version >> 4) != (VB2_SECDATA_FWMP_VERSION >> 4)) { + VB2_DEBUG("FWMP: major version incompatible\n"); + return VB2_ERROR_SECDATA_FWMP_VERSION; + } + + /* + * If this were a 1.1+ reader and the source was a 1.0 struct, + * we would need to take care of initializing the extra fields + * added in 1.1+. But that's not an issue yet. + */ + return VB2_SUCCESS; +} + +vb2_error_t vb2_secdata_fwmp_init(struct vb2_context *ctx) +{ + struct vb2_shared_data *sd = vb2_get_sd(ctx); + struct vb2_secdata_fwmp *sec = + (struct vb2_secdata_fwmp *)&ctx->secdata_fwmp; + vb2_error_t rv; + + /* Skip checking if NO_SECDATA_FWMP is set. */ + if (!(ctx->flags & VB2_CONTEXT_NO_SECDATA_FWMP)) { + rv = vb2api_secdata_fwmp_check(ctx, &sec->struct_size); + if (rv) + return rv; + } + + /* Mark as initialized */ + sd->status |= VB2_SD_STATUS_SECDATA_FWMP_INIT; + + return VB2_SUCCESS; +} + +int vb2_secdata_fwmp_get_flag(struct vb2_context *ctx, + enum vb2_secdata_fwmp_flags flag) +{ + struct vb2_shared_data *sd = vb2_get_sd(ctx); + struct vb2_secdata_fwmp *sec = + (struct vb2_secdata_fwmp *)&ctx->secdata_fwmp; + + if (!(sd->status & VB2_SD_STATUS_SECDATA_FWMP_INIT)) { + if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) { + VB2_DEBUG("Assuming broken FWMP flag %d as 0\n", flag); + return 0; + } else { + VB2_DIE("Must init FWMP before retrieving flag\n"); + } + } + + if (ctx->flags & VB2_CONTEXT_NO_SECDATA_FWMP) + return 0; + + return !!(sec->flags & flag); +} + +uint8_t *vb2_secdata_fwmp_get_dev_key_hash(struct vb2_context *ctx) +{ + struct vb2_shared_data *sd = vb2_get_sd(ctx); + struct vb2_secdata_fwmp *sec = + (struct vb2_secdata_fwmp *)&ctx->secdata_fwmp; + + if (!(sd->status & VB2_SD_STATUS_SECDATA_FWMP_INIT)) { + if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) { + VB2_DEBUG("Assuming broken FWMP dev_key_hash " + "as empty\n"); + return NULL; + } else { + VB2_DIE("Must init FWMP before getting dev key hash\n"); + } + } + + if (ctx->flags & VB2_CONTEXT_NO_SECDATA_FWMP) + return NULL; + + return sec->dev_key_hash; +} diff --git a/firmware/2lib/2secdata_kernel.c b/firmware/2lib/2secdata_kernel.c index 47c7100f..fb9f0b95 100644 --- a/firmware/2lib/2secdata_kernel.c +++ b/firmware/2lib/2secdata_kernel.c @@ -38,7 +38,7 @@ vb2_error_t vb2api_secdata_kernel_check(struct vb2_context *ctx) return VB2_SUCCESS; } -vb2_error_t vb2api_secdata_kernel_create(struct vb2_context *ctx) +uint32_t vb2api_secdata_kernel_create(struct vb2_context *ctx) { struct vb2_secdata_kernel *sec = (struct vb2_secdata_kernel *)ctx->secdata_kernel; @@ -58,7 +58,7 @@ vb2_error_t vb2api_secdata_kernel_create(struct vb2_context *ctx) /* Mark as changed */ ctx->flags |= VB2_CONTEXT_SECDATA_KERNEL_CHANGED; - return VB2_SUCCESS; + return sizeof(*sec); } vb2_error_t vb2_secdata_kernel_init(struct vb2_context *ctx) @@ -109,7 +109,7 @@ vb2_error_t vb2_secdata_kernel_set(struct vb2_context *ctx, if (!(sd->status & VB2_SD_STATUS_SECDATA_KERNEL_INIT)) return VB2_ERROR_SECDATA_KERNEL_SET_UNINITIALIZED; - /* If not changing the value, don't regenerate the CRC. */ + /* If not changing the value, don't regenerate the CRC */ if (vb2_secdata_kernel_get(ctx, param, &now) == VB2_SUCCESS && now == value) return VB2_SUCCESS; diff --git a/firmware/2lib/include/2api.h b/firmware/2lib/include/2api.h index 24d85da0..9a8a2228 100644 --- a/firmware/2lib/include/2api.h +++ b/firmware/2lib/include/2api.h @@ -185,6 +185,15 @@ enum vb2_context_flags { * support. */ VB2_CONTEXT_DISPLAY_INIT = (1 << 20), + + /* + * Caller may set this before running vb2api_kernel_phase1. It means + * that there is no FWMP on this system, and thus default values should + * be used instead. + * + * Caller should *not* set this when FWMP is available but invalid. + */ + VB2_CONTEXT_NO_SECDATA_FWMP = (1 << 21), }; /* @@ -213,18 +222,19 @@ struct vb2_context { /* * Non-volatile data. Caller must fill this from some non-volatile - * location. If the VB2_CONTEXT_NVDATA_CHANGED flag is set when a - * vb2api function returns, caller must save the data back to the - * non-volatile location and then clear the flag. + * location before calling vb2api_fw_phase1. If the + * VB2_CONTEXT_NVDATA_CHANGED flag is set when a vb2api function + * returns, caller must save the data back to the non-volatile location + * and then clear the flag. */ uint8_t nvdata[VB2_NVDATA_SIZE_V2]; /* * Secure data for firmware verification stage. Caller must fill this - * from some secure non-volatile location. If the - * VB2_CONTEXT_SECDATA_CHANGED flag is set when a function returns, - * caller must save the data back to the secure non-volatile location - * and then clear the flag. + * from some secure non-volatile location before calling + * vb2api_fw_phase1. If the VB2_CONTEXT_SECDATA_CHANGED flag is set + * when a function returns, caller must save the data back to the + * secure non-volatile location and then clear the flag. */ uint8_t secdata_firmware[VB2_SECDATA_FIRMWARE_SIZE]; @@ -254,12 +264,24 @@ struct vb2_context { /* * Secure data for kernel verification stage. Caller must fill this - * from some secure non-volatile location. If the - * VB2_CONTEXT_SECDATA_KERNEL_CHANGED flag is set when a function + * from some secure non-volatile location before calling + * vb2api_kernel_phase1. If the VB2_CONTEXT_SECDATA_KERNEL_CHANGED + * flag is set when a function returns, caller must save the data back + * to the secure non-volatile location and then clear the flag. + */ + uint8_t secdata_kernel[VB2_SECDATA_KERNEL_SIZE]; + + /* + * Firmware management parameters (FWMP) secure data. Caller must fill + * this from some secure non-volatile location before calling + * vb2api_kernel_phase1. Since FWMP is a variable-size space, caller + * should initially fill in VB2_SECDATA_FWMP_MIN_SIZE bytes, and call + * vb2_secdata_fwmp_check() to see whether more should be read. If the + * VB2_CONTEXT_SECDATA_FWMP_CHANGED flag is set when a function * returns, caller must save the data back to the secure non-volatile * location and then clear the flag. */ - uint8_t secdata_kernel[VB2_SECDATA_KERNEL_SIZE]; + uint8_t secdata_fwmp[VB2_SECDATA_FWMP_MAX_SIZE]; }; /* Resource index for vb2ex_read_resource() */ @@ -404,7 +426,7 @@ enum vb2_pcr_digest { */ /** - * Check the validity of the firmware secure storage context. + * Check the validity of firmware secure storage context. * * Checks version and CRC. * @@ -414,7 +436,7 @@ enum vb2_pcr_digest { vb2_error_t vb2api_secdata_firmware_check(struct vb2_context *ctx); /** - * Create fresh data in the firmware secure storage context. + * Create fresh data in firmware secure storage context. * * Use this only when initializing the secure storage context on a new machine * the first time it boots. Do NOT simply use this if @@ -422,12 +444,12 @@ vb2_error_t vb2api_secdata_firmware_check(struct vb2_context *ctx); * that could allow the secure data to be rolled back to an insecure state. * * @param ctx Context pointer - * @return VB2_SUCCESS, or non-zero error code if error. + * @return size of created firmware secure storage data in bytes */ -vb2_error_t vb2api_secdata_firmware_create(struct vb2_context *ctx); +uint32_t vb2api_secdata_firmware_create(struct vb2_context *ctx); /** - * Check the validity of the kernel secure storage context. + * Check the validity of kernel secure storage context. * * Checks version, UID, and CRC. * @@ -437,7 +459,7 @@ vb2_error_t vb2api_secdata_firmware_create(struct vb2_context *ctx); vb2_error_t vb2api_secdata_kernel_check(struct vb2_context *ctx); /** - * Create fresh data in the kernel secure storage context. + * Create fresh data in kernel secure storage context. * * Use this only when initializing the secure storage context on a new machine * the first time it boots. Do NOT simply use this if @@ -445,9 +467,23 @@ vb2_error_t vb2api_secdata_kernel_check(struct vb2_context *ctx); * could allow the secure data to be rolled back to an insecure state. * * @param ctx Context pointer + * @return size of created kernel secure storage data in bytes + */ +uint32_t vb2api_secdata_kernel_create(struct vb2_context *ctx); + +/** + * Check the validity of firmware management parameters (FWMP) space. + * + * Checks size, version, and CRC. If the struct size is larger than the size + * passed in, the size pointer is set to the expected full size of the struct, + * and VB2_ERROR_SECDATA_FWMP_INCOMPLETE is returned. The caller should + * re-read the returned number of bytes, and call this function again. + * + * @param ctx Context pointer + * @param size Amount of struct which has been read * @return VB2_SUCCESS, or non-zero error code if error. */ -vb2_error_t vb2api_secdata_kernel_create(struct vb2_context *ctx); +vb2_error_t vb2api_secdata_fwmp_check(struct vb2_context *ctx, uint8_t *size); /** * Report firmware failure to vboot. diff --git a/firmware/2lib/include/2constants.h b/firmware/2lib/include/2constants.h index f8d0d317..3f80b9ae 100644 --- a/firmware/2lib/include/2constants.h +++ b/firmware/2lib/include/2constants.h @@ -24,6 +24,8 @@ /* Size of secure data spaces used by vboot */ #define VB2_SECDATA_FIRMWARE_SIZE 10 #define VB2_SECDATA_KERNEL_SIZE 13 +#define VB2_SECDATA_FWMP_MIN_SIZE 40 +#define VB2_SECDATA_FWMP_MAX_SIZE 64 /* TODO(chromium:972956): Remove once coreboot is using updated names */ #define VB2_SECDATA_SIZE 10 diff --git a/firmware/2lib/include/2misc.h b/firmware/2lib/include/2misc.h index c7970d68..b03df5f3 100644 --- a/firmware/2lib/include/2misc.h +++ b/firmware/2lib/include/2misc.h @@ -117,7 +117,7 @@ void vb2_check_recovery(struct vb2_context *ctx); * @param ctx Vboot context * @return VB2_SUCCESS, or error code on error. */ -vb2_error_t vb2_fw_parse_gbb(struct vb2_context *ctx); +vb2_error_t vb2_fw_init_gbb(struct vb2_context *ctx); /** * Check developer switch position. diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h index 865ea7cf..bc612344 100644 --- a/firmware/2lib/include/2return_codes.h +++ b/firmware/2lib/include/2return_codes.h @@ -197,6 +197,18 @@ enum vb2_return_code { /* Called vb2_secdata_kernel_set() with uninitialized secdata_kernel */ VB2_ERROR_SECDATA_KERNEL_SET_UNINITIALIZED, + /* Bad size in vb2api_secdata_fwmp_check() */ + VB2_ERROR_SECDATA_FWMP_SIZE, + + /* Incomplete structure in vb2api_secdata_fwmp_check() */ + VB2_ERROR_SECDATA_FWMP_INCOMPLETE, + + /* Bad CRC in vb2api_secdata_fwmp_check() */ + VB2_ERROR_SECDATA_FWMP_CRC, + + /* Bad struct version in vb2_secdata_fwmp_check() */ + VB2_ERROR_SECDATA_FWMP_VERSION, + /********************************************************************** * Common code errors */ diff --git a/firmware/2lib/include/2secdata.h b/firmware/2lib/include/2secdata.h index 0bbce4f3..4e6fdda2 100644 --- a/firmware/2lib/include/2secdata.h +++ b/firmware/2lib/include/2secdata.h @@ -89,6 +89,44 @@ enum vb2_secdata_kernel_param { }; /*****************************************************************************/ +/* Firmware management parameters (FWMP) space */ + +#define VB2_SECDATA_FWMP_VERSION 0x10 /* 1.0 */ +#define VB2_SECDATA_FWMP_HASH_SIZE 32 /* enough for SHA-256 */ + +/* Flags for FWMP space */ +enum vb2_secdata_fwmp_flags { + VB2_SECDATA_FWMP_DEV_DISABLE_BOOT = (1 << 0), + VB2_SECDATA_FWMP_DEV_DISABLE_RECOVERY = (1 << 1), + VB2_SECDATA_FWMP_DEV_ENABLE_USB = (1 << 2), + VB2_SECDATA_FWMP_DEV_ENABLE_LEGACY = (1 << 3), + VB2_SECDATA_FWMP_DEV_ENABLE_OFFICIAL_ONLY = (1 << 4), + VB2_SECDATA_FWMP_DEV_USE_KEY_HASH = (1 << 5), + /* CCD = case-closed debugging on cr50; flag implemented on cr50 */ + VB2_SECDATA_FWMP_DEV_DISABLE_CCD_UNLOCK = (1 << 6), +}; + +struct vb2_secdata_fwmp { + /* CRC-8 of fields following struct_size */ + uint8_t crc8; + + /* Structure size in bytes */ + uint8_t struct_size; + + /* Structure version (4 bits major, 4 bits minor) */ + uint8_t struct_version; + + /* Reserved; ignored by current reader */ + uint8_t reserved0; + + /* Flags; see enum vb2_secdata_fwmp_flags */ + uint32_t flags; + + /* Hash of developer kernel key */ + uint8_t dev_key_hash[VB2_SECDATA_FWMP_HASH_SIZE]; +}; + +/*****************************************************************************/ /* Firmware secure storage space functions */ /** @@ -166,4 +204,51 @@ vb2_error_t vb2_secdata_kernel_set(struct vb2_context *ctx, enum vb2_secdata_kernel_param param, uint32_t value); +/*****************************************************************************/ +/* Firmware management parameters (FWMP) space functions */ + +/** + * Generate CRC for FWMP secure storage space. + * + * Calculate CRC hash from struct_version onward. Should not be used; + * prototype only in header for use by unittests. + * + * In valid FWMP data, this CRC value should match the crc8 field. + * + * @param sec Pointer to FWMP struct + * @return 32-bit CRC hash of FWMP data + */ +uint32_t vb2_secdata_fwmp_crc(struct vb2_secdata_fwmp *sec); + +/** + * Initialize FWMP secure storage context and verify its CRC. + * + * This must be called before vb2_secdata_fwmp_get_flag/get_dev_key_hash(). + * + * @param ctx Context pointer + * @return VB2_SUCCESS, or non-zero error code if error. + */ +vb2_error_t vb2_secdata_fwmp_init(struct vb2_context *ctx); + +/** + * Read a FWMP secure storage flag value. + * + * It is unsupported to call before successfully running vb2_secdata_fwmp_init. + * In this case, vboot will fail and exit. + * + * @param ctx Context pointer + * @param flag Flag to read + * @return current flag value (0 or 1) + */ +int vb2_secdata_fwmp_get_flag(struct vb2_context *ctx, + enum vb2_secdata_fwmp_flags flag); + +/** + * Return a pointer to FWMP dev key hash. + * + * @param ctx Context pointer + * @return uint8_t pointer to dev_key_hash field + */ +uint8_t *vb2_secdata_fwmp_get_dev_key_hash(struct vb2_context *ctx); + #endif /* VBOOT_REFERENCE_2SECDATA_H_ */ diff --git a/firmware/2lib/include/2struct.h b/firmware/2lib/include/2struct.h index 4aff7ca7..52d905ae 100644 --- a/firmware/2lib/include/2struct.h +++ b/firmware/2lib/include/2struct.h @@ -79,6 +79,9 @@ enum vb2_shared_data_status { /* Secure data kernel version space initialized */ VB2_SD_STATUS_SECDATA_KERNEL_INIT = (1 << 4), + + /* FWMP secure data initialized */ + VB2_SD_STATUS_SECDATA_FWMP_INIT = (1 << 5), }; /* "V2SD" = vb2_shared_data.magic */ diff --git a/firmware/lib/mocked_secdata_tpm.c b/firmware/lib/mocked_secdata_tpm.c index 4271f768..b2f0670f 100644 --- a/firmware/lib/mocked_secdata_tpm.c +++ b/firmware/lib/mocked_secdata_tpm.c @@ -33,6 +33,6 @@ uint32_t RollbackKernelLock(int recovery_mode) uint32_t RollbackFwmpRead(struct RollbackSpaceFwmp *fwmp) { - memset(fwmp, 0, sizeof(*fwmp)); + ctx->flags |= VB2_CONTEXT_NO_SECDATA_FWMP; return TPM_SUCCESS; } diff --git a/tests/vb20_verify_fw.c b/tests/vb20_verify_fw.c index 5cf2b765..1fe662c2 100644 --- a/tests/vb20_verify_fw.c +++ b/tests/vb20_verify_fw.c @@ -164,13 +164,7 @@ int main(int argc, char *argv[]) ctx.workbuf_size = sizeof(workbuf); /* Initialize secure context */ - rv = vb2api_secdata_firmware_create(&ctx); - if (rv) { - fprintf(stderr, - "error: vb2api_secdata_firmware_create() failed (%d)\n", - rv); - return 1; - } + vb2api_secdata_firmware_create(&ctx); // TODO: optional args to set contents for nvdata, secdata? diff --git a/tests/vb2_api_tests.c b/tests/vb2_api_tests.c index 6429ca31..4ce5798e 100644 --- a/tests/vb2_api_tests.c +++ b/tests/vb2_api_tests.c @@ -36,7 +36,7 @@ static const uint8_t mock_hwid_digest[VB2_GBB_HWID_DIGEST_SIZE] = { /* Mocked function data */ static int force_dev_mode; -static vb2_error_t retval_vb2_fw_parse_gbb; +static vb2_error_t retval_vb2_fw_init_gbb; static vb2_error_t retval_vb2_check_dev_switch; static vb2_error_t retval_vb2_check_tpm_clear; static vb2_error_t retval_vb2_select_fw_slot; @@ -63,7 +63,7 @@ static void reset_common_data(enum reset_type t) vb2_secdata_firmware_init(&ctx); force_dev_mode = 0; - retval_vb2_fw_parse_gbb = VB2_SUCCESS; + retval_vb2_fw_init_gbb = VB2_SUCCESS; retval_vb2_check_dev_switch = VB2_SUCCESS; retval_vb2_check_tpm_clear = VB2_SUCCESS; retval_vb2_select_fw_slot = VB2_SUCCESS; @@ -78,9 +78,9 @@ struct vb2_gbb_header *vb2_get_gbb(struct vb2_context *c) return &gbb; } -vb2_error_t vb2_fw_parse_gbb(struct vb2_context *c) +vb2_error_t vb2_fw_init_gbb(struct vb2_context *c) { - return retval_vb2_fw_parse_gbb; + return retval_vb2_fw_init_gbb; } vb2_error_t vb2_check_dev_switch(struct vb2_context *c) @@ -111,7 +111,7 @@ static void misc_tests(void) TEST_EQ(vb2api_secdata_firmware_check(&ctx), VB2_ERROR_SECDATA_FIRMWARE_CRC, "secdata_firmware check"); - TEST_SUCC(vb2api_secdata_firmware_create(&ctx), + TEST_EQ(vb2api_secdata_firmware_create(&ctx), VB2_SECDATA_FIRMWARE_SIZE, "secdata_firmware create"); TEST_SUCC(vb2api_secdata_firmware_check(&ctx), "secdata_firmware check 2"); @@ -138,7 +138,7 @@ static void phase1_tests(void) 0, " display available SD flag"); reset_common_data(FOR_MISC); - retval_vb2_fw_parse_gbb = VB2_ERROR_GBB_MAGIC; + retval_vb2_fw_init_gbb = VB2_ERROR_GBB_MAGIC; TEST_EQ(vb2api_fw_phase1(&ctx), VB2_ERROR_API_PHASE1_RECOVERY, "phase1 gbb"); TEST_EQ(sd->recovery_reason, VB2_RECOVERY_GBB_HEADER, diff --git a/tests/vb2_misc_tests.c b/tests/vb2_misc_tests.c index 43c97e4e..ee73da4f 100644 --- a/tests/vb2_misc_tests.c +++ b/tests/vb2_misc_tests.c @@ -219,13 +219,13 @@ static void gbb_tests(void) gbbsrc.header_size--; TEST_EQ(vb2_read_gbb_header(&ctx, &gbbdest), VB2_ERROR_GBB_HEADER_SIZE, "read gbb header size"); - TEST_EQ(vb2_fw_parse_gbb(&ctx), - VB2_ERROR_GBB_HEADER_SIZE, "parse gbb failure"); + TEST_EQ(vb2_fw_init_gbb(&ctx), + VB2_ERROR_GBB_HEADER_SIZE, "init gbb failure"); gbbsrc.header_size++; - /* Parse GBB */ + /* Init GBB */ int used_before = ctx.workbuf_used; - TEST_SUCC(vb2_fw_parse_gbb(&ctx), "parse gbb"); + TEST_SUCC(vb2_fw_init_gbb(&ctx), "init gbb"); /* Manually calculate the location of GBB since we have mocked out the original definition of vb2_get_gbb. */ struct vb2_gbb_header *current_gbb = vb2_member_of(sd, sd->gbb_offset); @@ -237,8 +237,19 @@ static void gbb_tests(void) /* Workbuf failure */ reset_common_data(); ctx.workbuf_used = ctx.workbuf_size - 4; - TEST_EQ(vb2_fw_parse_gbb(&ctx), - VB2_ERROR_GBB_WORKBUF, "parse gbb no workbuf"); + TEST_EQ(vb2_fw_init_gbb(&ctx), + VB2_ERROR_GBB_WORKBUF, "init gbb no workbuf"); + + /* Check for setting NO_SECDATA_FWMP context flag */ + reset_common_data(); + TEST_SUCC(vb2_fw_init_gbb(&ctx), "init gbb"); + TEST_EQ(ctx.flags & VB2_CONTEXT_NO_SECDATA_FWMP, 0, + "without DISABLE_FWMP: NO_SECDATA_FWMP shouldn't be set"); + reset_common_data(); + gbbsrc.flags |= VB2_GBB_FLAG_DISABLE_FWMP; + TEST_SUCC(vb2_fw_init_gbb(&ctx), "init gbb"); + TEST_NEQ(ctx.flags & VB2_CONTEXT_NO_SECDATA_FWMP, 0, + "with DISABLE_FWMP: NO_SECDATA_FWMP should be set"); } static void fail_tests(void) diff --git a/tests/vb2_secdata_firmware_tests.c b/tests/vb2_secdata_firmware_tests.c index 3564cfa5..d20ee709 100644 --- a/tests/vb2_secdata_firmware_tests.c +++ b/tests/vb2_secdata_firmware_tests.c @@ -14,6 +14,26 @@ #include "test_common.h" #include "vboot_common.h" +static uint8_t workbuf[VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE] + __attribute__ ((aligned (VB2_WORKBUF_ALIGN))); +static struct vb2_context ctx; +static struct vb2_shared_data *sd; +static struct vb2_secdata_firmware *sec; + +static void reset_common_data(void) +{ + memset(workbuf, 0xaa, sizeof(workbuf)); + + memset(&ctx, 0, sizeof(ctx)); + ctx.workbuf = workbuf; + ctx.workbuf_size = sizeof(workbuf); + + vb2_init_context(&ctx); + sd = vb2_get_sd(&ctx); + + sec = (struct vb2_secdata_firmware *)ctx.secdata_firmware; +} + static void test_changed(struct vb2_context *c, int changed, const char *why) { if (changed) @@ -28,118 +48,115 @@ static void test_changed(struct vb2_context *c, int changed, const char *why) static void secdata_firmware_test(void) { - uint8_t workbuf[VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE] - __attribute__ ((aligned (VB2_WORKBUF_ALIGN))); - struct vb2_context c = { - .flags = 0, - .workbuf = workbuf, - .workbuf_size = sizeof(workbuf), - }; - struct vb2_secdata_firmware *sec = - (struct vb2_secdata_firmware *)c.secdata_firmware; - struct vb2_shared_data *sd = vb2_get_sd(&c); uint32_t v = 1; + reset_common_data(); /* Check size constant */ TEST_EQ(VB2_SECDATA_FIRMWARE_SIZE, sizeof(struct vb2_secdata_firmware), "Struct size constant"); /* Blank data is invalid */ - memset(c.secdata_firmware, 0xa6, sizeof(c.secdata_firmware)); - TEST_EQ(vb2api_secdata_firmware_check(&c), + memset(ctx.secdata_firmware, 0xa6, sizeof(ctx.secdata_firmware)); + TEST_EQ(vb2api_secdata_firmware_check(&ctx), VB2_ERROR_SECDATA_FIRMWARE_CRC, "Check blank CRC"); - TEST_EQ(vb2_secdata_firmware_init(&c), + TEST_EQ(vb2_secdata_firmware_init(&ctx), VB2_ERROR_SECDATA_FIRMWARE_CRC, "Init blank CRC"); /* Ensure zeroed buffers are invalid (coreboot relies on this) */ - memset(c.secdata_firmware, 0, sizeof(c.secdata_firmware)); - TEST_EQ(vb2_secdata_firmware_init(&c), + memset(ctx.secdata_firmware, 0, sizeof(ctx.secdata_firmware)); + TEST_EQ(vb2_secdata_firmware_init(&ctx), VB2_ERROR_SECDATA_FIRMWARE_VERSION, "Zeroed buffer (invalid version)"); /* Try with bad version */ - TEST_SUCC(vb2api_secdata_firmware_create(&c), "Create"); + TEST_EQ(vb2api_secdata_firmware_create(&ctx), VB2_SECDATA_FIRMWARE_SIZE, + "Create"); sec->struct_version -= 1; sec->crc8 = vb2_crc8(sec, offsetof(struct vb2_secdata_firmware, crc8)); - TEST_EQ(vb2api_secdata_firmware_check(&c), + TEST_EQ(vb2api_secdata_firmware_check(&ctx), VB2_ERROR_SECDATA_FIRMWARE_VERSION, "Check invalid version"); - TEST_EQ(vb2_secdata_firmware_init(&c), + TEST_EQ(vb2_secdata_firmware_init(&ctx), VB2_ERROR_SECDATA_FIRMWARE_VERSION, "Init invalid version"); /* Create good data */ - TEST_SUCC(vb2api_secdata_firmware_create(&c), "Create"); - TEST_SUCC(vb2api_secdata_firmware_check(&c), "Check created CRC"); - TEST_SUCC(vb2_secdata_firmware_init(&c), "Init created CRC"); + vb2api_secdata_firmware_create(&ctx); + TEST_SUCC(vb2api_secdata_firmware_check(&ctx), "Check created CRC"); + TEST_SUCC(vb2_secdata_firmware_init(&ctx), "Init created CRC"); TEST_NEQ(sd->status & VB2_SD_STATUS_SECDATA_FIRMWARE_INIT, 0, "Init set SD status"); sd->status &= ~VB2_SD_STATUS_SECDATA_FIRMWARE_INIT; - test_changed(&c, 1, "Create changes data"); + test_changed(&ctx, 1, "Create changes data"); /* Now corrupt it */ - c.secdata_firmware[2]++; - TEST_EQ(vb2api_secdata_firmware_check(&c), + ctx.secdata_firmware[2]++; + TEST_EQ(vb2api_secdata_firmware_check(&ctx), VB2_ERROR_SECDATA_FIRMWARE_CRC, "Check invalid CRC"); - TEST_EQ(vb2_secdata_firmware_init(&c), + TEST_EQ(vb2_secdata_firmware_init(&ctx), VB2_ERROR_SECDATA_FIRMWARE_CRC, "Init invalid CRC"); /* Read/write flags */ - vb2api_secdata_firmware_create(&c); - vb2_secdata_firmware_init(&c); - c.flags = 0; - TEST_SUCC(vb2_secdata_firmware_get(&c, VB2_SECDATA_FIRMWARE_FLAGS, &v), + vb2api_secdata_firmware_create(&ctx); + vb2_secdata_firmware_init(&ctx); + ctx.flags = 0; + TEST_SUCC(vb2_secdata_firmware_get(&ctx, VB2_SECDATA_FIRMWARE_FLAGS, + &v), "Get flags"); TEST_EQ(v, 0, "Flags created 0"); - test_changed(&c, 0, "Get doesn't change data"); - TEST_SUCC(vb2_secdata_firmware_set(&c, VB2_SECDATA_FIRMWARE_FLAGS, + test_changed(&ctx, 0, "Get doesn't change data"); + TEST_SUCC(vb2_secdata_firmware_set(&ctx, VB2_SECDATA_FIRMWARE_FLAGS, 0x12), "Set flags"); - test_changed(&c, 1, "Set changes data"); - TEST_SUCC(vb2_secdata_firmware_set(&c, VB2_SECDATA_FIRMWARE_FLAGS, + test_changed(&ctx, 1, "Set changes data"); + TEST_SUCC(vb2_secdata_firmware_set(&ctx, VB2_SECDATA_FIRMWARE_FLAGS, 0x12), "Set flags 2"); - test_changed(&c, 0, "Set again doesn't change data"); - TEST_SUCC(vb2_secdata_firmware_get(&c, VB2_SECDATA_FIRMWARE_FLAGS, &v), + test_changed(&ctx, 0, "Set again doesn't change data"); + TEST_SUCC(vb2_secdata_firmware_get(&ctx, VB2_SECDATA_FIRMWARE_FLAGS, + &v), "Get flags 2"); TEST_EQ(v, 0x12, "Flags changed"); - TEST_EQ(vb2_secdata_firmware_set(&c, VB2_SECDATA_FIRMWARE_FLAGS, 0x100), + TEST_EQ(vb2_secdata_firmware_set(&ctx, VB2_SECDATA_FIRMWARE_FLAGS, + 0x100), VB2_ERROR_SECDATA_FIRMWARE_SET_FLAGS, "Bad flags"); /* Read/write versions */ - TEST_SUCC(vb2_secdata_firmware_get(&c, VB2_SECDATA_FIRMWARE_VERSIONS, + TEST_SUCC(vb2_secdata_firmware_get(&ctx, VB2_SECDATA_FIRMWARE_VERSIONS, &v), "Get versions"); TEST_EQ(v, 0, "Versions created 0"); - test_changed(&c, 0, "Get doesn't change data"); - TEST_SUCC(vb2_secdata_firmware_set(&c, VB2_SECDATA_FIRMWARE_VERSIONS, + test_changed(&ctx, 0, "Get doesn't change data"); + TEST_SUCC(vb2_secdata_firmware_set(&ctx, VB2_SECDATA_FIRMWARE_VERSIONS, 0x123456ff), "Set versions"); - test_changed(&c, 1, "Set changes data"); - TEST_SUCC(vb2_secdata_firmware_set(&c, VB2_SECDATA_FIRMWARE_VERSIONS, + test_changed(&ctx, 1, "Set changes data"); + TEST_SUCC(vb2_secdata_firmware_set(&ctx, VB2_SECDATA_FIRMWARE_VERSIONS, 0x123456ff), "Set versions 2"); - test_changed(&c, 0, "Set again doesn't change data"); - TEST_SUCC(vb2_secdata_firmware_get(&c, VB2_SECDATA_FIRMWARE_VERSIONS, &v), + test_changed(&ctx, 0, "Set again doesn't change data"); + TEST_SUCC(vb2_secdata_firmware_get(&ctx, VB2_SECDATA_FIRMWARE_VERSIONS, + &v), "Get versions 2"); TEST_EQ(v, 0x123456ff, "Versions changed"); /* Invalid field fails */ - TEST_EQ(vb2_secdata_firmware_get(&c, -1, &v), + TEST_EQ(vb2_secdata_firmware_get(&ctx, -1, &v), VB2_ERROR_SECDATA_FIRMWARE_GET_PARAM, "Get invalid"); - TEST_EQ(vb2_secdata_firmware_set(&c, -1, 456), + TEST_EQ(vb2_secdata_firmware_set(&ctx, -1, 456), VB2_ERROR_SECDATA_FIRMWARE_SET_PARAM, "Set invalid"); - test_changed(&c, 0, "Set invalid field doesn't change data"); + test_changed(&ctx, 0, "Set invalid field doesn't change data"); /* Read/write uninitialized data fails */ sd->status &= ~VB2_SD_STATUS_SECDATA_FIRMWARE_INIT; - TEST_EQ(vb2_secdata_firmware_get(&c, VB2_SECDATA_FIRMWARE_VERSIONS, &v), + TEST_EQ(vb2_secdata_firmware_get(&ctx, VB2_SECDATA_FIRMWARE_VERSIONS, + &v), VB2_ERROR_SECDATA_FIRMWARE_GET_UNINITIALIZED, "Get uninitialized"); - test_changed(&c, 0, "Get uninitialized doesn't change data"); - TEST_EQ(vb2_secdata_firmware_set(&c, VB2_SECDATA_FIRMWARE_VERSIONS, + test_changed(&ctx, 0, "Get uninitialized doesn't change data"); + TEST_EQ(vb2_secdata_firmware_set(&ctx, VB2_SECDATA_FIRMWARE_VERSIONS, 0x123456ff), VB2_ERROR_SECDATA_FIRMWARE_SET_UNINITIALIZED, "Set uninitialized"); - test_changed(&c, 0, "Set uninitialized doesn't change data"); + test_changed(&ctx, 0, "Set uninitialized doesn't change data"); } int main(int argc, char* argv[]) diff --git a/tests/vb2_secdata_fwmp_tests.c b/tests/vb2_secdata_fwmp_tests.c new file mode 100644 index 00000000..06a89fdf --- /dev/null +++ b/tests/vb2_secdata_fwmp_tests.c @@ -0,0 +1,242 @@ +/* Copyright 2019 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 firmware management parameters (FWMP) library. + */ + +#include "2common.h" +#include "2misc.h" +#include "2secdata.h" +#include "2struct.h" +#include "test_common.h" + +static uint8_t workbuf[VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE] + __attribute__ ((aligned (VB2_WORKBUF_ALIGN))); +static struct vb2_context ctx; +static struct vb2_gbb_header gbb; +static struct vb2_shared_data *sd; +static struct vb2_secdata_fwmp *sec; + +static void reset_common_data(void) +{ + memset(workbuf, 0xaa, sizeof(workbuf)); + + memset(&ctx, 0, sizeof(ctx)); + ctx.workbuf = workbuf; + ctx.workbuf_size = sizeof(workbuf); + ctx.flags = 0; + + vb2_init_context(&ctx); + sd = vb2_get_sd(&ctx); + sd->status = VB2_SD_STATUS_SECDATA_FWMP_INIT; + + memset(&gbb, 0, sizeof(gbb)); + + sec = (struct vb2_secdata_fwmp *)ctx.secdata_fwmp; + sec->struct_size = VB2_SECDATA_FWMP_MIN_SIZE; + sec->struct_version = VB2_SECDATA_FWMP_VERSION; + sec->flags = 0; + sec->crc8 = vb2_secdata_fwmp_crc(sec); +} + +/* Mocked functions */ + +struct vb2_gbb_header *vb2_get_gbb(struct vb2_context *c) +{ + return &gbb; +} + +static void check_init_test(void) +{ + uint8_t size; + + /* Check size constants */ + TEST_TRUE(sizeof(struct vb2_secdata_fwmp) >= VB2_SECDATA_FWMP_MIN_SIZE, + "Struct min size constant"); + TEST_TRUE(sizeof(struct vb2_secdata_fwmp) <= VB2_SECDATA_FWMP_MAX_SIZE, + "Struct max size constant"); + + /* struct_size too large */ + reset_common_data(); + sec->struct_size = VB2_SECDATA_FWMP_MAX_SIZE + 1; + sec->crc8 = vb2_secdata_fwmp_crc(sec); + size = sec->struct_size; + TEST_EQ(vb2api_secdata_fwmp_check(&ctx, &size), + VB2_ERROR_SECDATA_FWMP_SIZE, "Check struct_size too large"); + TEST_EQ(vb2_secdata_fwmp_init(&ctx), + VB2_ERROR_SECDATA_FWMP_SIZE, "Init struct_size too large"); + + /* struct_size too small */ + reset_common_data(); + sec->struct_size = VB2_SECDATA_FWMP_MIN_SIZE - 1; + sec->crc8 = vb2_secdata_fwmp_crc(sec); + size = VB2_SECDATA_FWMP_MIN_SIZE; + TEST_EQ(vb2api_secdata_fwmp_check(&ctx, &size), + VB2_ERROR_SECDATA_FWMP_SIZE, "Check struct_size too small"); + + /* Need more data to reach minimum size */ + reset_common_data(); + sec->struct_size = VB2_SECDATA_FWMP_MIN_SIZE - 1; + sec->crc8 = vb2_secdata_fwmp_crc(sec); + size = 0; + TEST_EQ(vb2api_secdata_fwmp_check(&ctx, &size), + VB2_ERROR_SECDATA_FWMP_INCOMPLETE, "Check more to reach MIN"); + TEST_EQ(vb2_secdata_fwmp_init(&ctx), + VB2_ERROR_SECDATA_FWMP_INCOMPLETE, "Init more to reach MIN"); + + /* Need more data to reach full size */ + reset_common_data(); + sec->struct_size = VB2_SECDATA_FWMP_MIN_SIZE + 1; + sec->crc8 = vb2_secdata_fwmp_crc(sec); + size = VB2_SECDATA_FWMP_MIN_SIZE; + TEST_EQ(vb2api_secdata_fwmp_check(&ctx, &size), + VB2_ERROR_SECDATA_FWMP_INCOMPLETE, "Check more for full size"); + + /* Bad data is invalid */ + reset_common_data(); + memset(ctx.secdata_fwmp, 0xa6, sizeof(ctx.secdata_fwmp)); + sec->struct_size = VB2_SECDATA_FWMP_MIN_SIZE; + size = sec->struct_size; + TEST_EQ(vb2api_secdata_fwmp_check(&ctx, &size), + VB2_ERROR_SECDATA_FWMP_CRC, "Check bad data CRC"); + TEST_EQ(vb2_secdata_fwmp_init(&ctx), + VB2_ERROR_SECDATA_FWMP_CRC, "Init bad data CRC"); + + /* Bad CRC with corruption past minimum size */ + reset_common_data(); + sec->struct_size = VB2_SECDATA_FWMP_MIN_SIZE + 1; + sec->crc8 = vb2_secdata_fwmp_crc(sec); + size = sec->struct_size; + *((uint8_t *)sec + sec->struct_size - 1) += 1; + TEST_EQ(vb2api_secdata_fwmp_check(&ctx, &size), + VB2_ERROR_SECDATA_FWMP_CRC, "Check corruption CRC"); + TEST_EQ(vb2_secdata_fwmp_init(&ctx), + VB2_ERROR_SECDATA_FWMP_CRC, "Init corruption CRC"); + + /* Zeroed data is invalid */ + reset_common_data(); + memset(ctx.secdata_fwmp, 0, sizeof(ctx.secdata_fwmp)); + sec->struct_size = VB2_SECDATA_FWMP_MIN_SIZE; + size = sec->struct_size; + TEST_EQ(vb2api_secdata_fwmp_check(&ctx, &size), + VB2_ERROR_SECDATA_FWMP_VERSION, "Check zeroed data CRC"); + TEST_EQ(vb2_secdata_fwmp_init(&ctx), + VB2_ERROR_SECDATA_FWMP_VERSION, "Init zeroed data CRC"); + + /* Major version too high */ + reset_common_data(); + sec->struct_version = ((VB2_SECDATA_FWMP_VERSION >> 4) + 1) << 4; + sec->crc8 = vb2_secdata_fwmp_crc(sec); + TEST_EQ(vb2api_secdata_fwmp_check(&ctx, &size), + VB2_ERROR_SECDATA_FWMP_VERSION, "Check major too high"); + TEST_EQ(vb2_secdata_fwmp_init(&ctx), + VB2_ERROR_SECDATA_FWMP_VERSION, "Init major too high"); + + /* Major version too low */ + reset_common_data(); + sec->struct_version = ((VB2_SECDATA_FWMP_VERSION >> 4) - 1) << 4; + sec->crc8 = vb2_secdata_fwmp_crc(sec); + TEST_EQ(vb2api_secdata_fwmp_check(&ctx, &size), + VB2_ERROR_SECDATA_FWMP_VERSION, "Check major too low"); + TEST_EQ(vb2_secdata_fwmp_init(&ctx), + VB2_ERROR_SECDATA_FWMP_VERSION, "Init major too low"); + + /* Minor version difference okay */ + reset_common_data(); + sec->struct_version += 1; + sec->crc8 = vb2_secdata_fwmp_crc(sec); + TEST_SUCC(vb2api_secdata_fwmp_check(&ctx, &size), "Check minor okay"); + TEST_SUCC(vb2_secdata_fwmp_init(&ctx), "Init minor okay"); + + /* Good FWMP data at minimum size */ + reset_common_data(); + TEST_SUCC(vb2api_secdata_fwmp_check(&ctx, &size), "Check good (min)"); + TEST_SUCC(vb2_secdata_fwmp_init(&ctx), "Init good (min)"); + TEST_NEQ(sd->status & VB2_SD_STATUS_SECDATA_FWMP_INIT, 0, + "Init flag set"); + + /* Good FWMP data at minimum + N size */ + reset_common_data(); + sec->struct_size = VB2_SECDATA_FWMP_MIN_SIZE + 1; + sec->crc8 = vb2_secdata_fwmp_crc(sec); + size = sec->struct_size; + TEST_SUCC(vb2api_secdata_fwmp_check(&ctx, &size), "Check good (min+N)"); + TEST_SUCC(vb2_secdata_fwmp_init(&ctx), "Init good (min+N)"); + TEST_NEQ(sd->status & VB2_SD_STATUS_SECDATA_FWMP_INIT, 0, + "Init flag set"); + + /* Skip data check when NO_SECDATA_FWMP set */ + reset_common_data(); + memset(ctx.secdata_fwmp, 0xa6, sizeof(ctx.secdata_fwmp)); + ctx.flags |= VB2_CONTEXT_NO_SECDATA_FWMP; + TEST_EQ(vb2_secdata_fwmp_init(&ctx), 0, + "Init skip data check when NO_SECDATA_FWMP set"); + TEST_NEQ(sd->status & VB2_SD_STATUS_SECDATA_FWMP_INIT, 0, + "Init flag set"); +} + +static void get_flag_test(void) +{ + /* Successfully returns value */ + reset_common_data(); + sec->flags |= 1; + TEST_EQ(vb2_secdata_fwmp_get_flag(&ctx, 1), 1, + "Successfully returns flag value"); + + /* NO_SECDATA_FWMP */ + reset_common_data(); + sec->flags |= 1; + ctx.flags |= VB2_CONTEXT_NO_SECDATA_FWMP; + TEST_EQ(vb2_secdata_fwmp_get_flag(&ctx, 1), 0, + "NO_SECDATA_FWMP forces default flag value"); + + /* FWMP hasn't been initialized (recovery mode) */ + reset_common_data(); + sd->status &= ~VB2_SD_STATUS_SECDATA_FWMP_INIT; + ctx.flags |= VB2_CONTEXT_RECOVERY_MODE; + TEST_EQ(vb2_secdata_fwmp_get_flag(&ctx, 0), 0, + "non-init in recovery mode forces default flag value"); + + /* FWMP hasn't been initialized (normal mode) */ + reset_common_data(); + sd->status &= ~VB2_SD_STATUS_SECDATA_FWMP_INIT; + TEST_ABORT(vb2_secdata_fwmp_get_flag(&ctx, 0), + "non-init in normal mode triggers abort"); +} + +static void get_dev_key_hash_test(void) +{ + /* CONTEXT_NO_SECDATA_FWMP */ + reset_common_data(); + ctx.flags |= VB2_CONTEXT_NO_SECDATA_FWMP; + TEST_TRUE(vb2_secdata_fwmp_get_dev_key_hash(&ctx) == NULL, + "NO_SECDATA_FWMP forces NULL pointer"); + + /* FWMP hasn't been initialized (recovery mode) */ + reset_common_data(); + sd->status &= ~VB2_SD_STATUS_SECDATA_FWMP_INIT; + ctx.flags |= VB2_CONTEXT_RECOVERY_MODE; + TEST_TRUE(vb2_secdata_fwmp_get_dev_key_hash(&ctx) == NULL, + "non-init in recovery mode forces NULL pointer"); + + /* FWMP hasn't been initialized (normal mode) */ + reset_common_data(); + sd->status &= ~VB2_SD_STATUS_SECDATA_FWMP_INIT; + TEST_ABORT(vb2_secdata_fwmp_get_dev_key_hash(&ctx), + "non-init in normal mode triggers abort"); + + /* Success case */ + reset_common_data(); + TEST_TRUE(vb2_secdata_fwmp_get_dev_key_hash(&ctx) == + sec->dev_key_hash, "proper dev_key_hash pointer returned"); +} + +int main(int argc, char* argv[]) +{ + check_init_test(); + get_flag_test(); + get_dev_key_hash_test(); + + return gTestSuccess ? 0 : 255; +} diff --git a/tests/vb2_secdata_kernel_tests.c b/tests/vb2_secdata_kernel_tests.c index c1ae2e16..e278b86e 100644 --- a/tests/vb2_secdata_kernel_tests.c +++ b/tests/vb2_secdata_kernel_tests.c @@ -14,6 +14,26 @@ #include "test_common.h" #include "vboot_common.h" +static uint8_t workbuf[VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE] + __attribute__ ((aligned (VB2_WORKBUF_ALIGN))); +static struct vb2_context ctx; +static struct vb2_shared_data *sd; +static struct vb2_secdata_kernel *sec; + +static void reset_common_data(void) +{ + memset(workbuf, 0xaa, sizeof(workbuf)); + + memset(&ctx, 0, sizeof(ctx)); + ctx.workbuf = workbuf; + ctx.workbuf_size = sizeof(workbuf); + + vb2_init_context(&ctx); + sd = vb2_get_sd(&ctx); + + sec = (struct vb2_secdata_kernel *)ctx.secdata_kernel; +} + static void test_changed(struct vb2_context *c, int changed, const char *why) { if (changed) @@ -26,105 +46,97 @@ static void test_changed(struct vb2_context *c, int changed, const char *why) static void secdata_kernel_test(void) { - uint8_t workbuf[VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE] - __attribute__ ((aligned (VB2_WORKBUF_ALIGN))); - struct vb2_context c = { - .flags = 0, - .workbuf = workbuf, - .workbuf_size = sizeof(workbuf), - }; - struct vb2_secdata_kernel *sec = - (struct vb2_secdata_kernel *)c.secdata_kernel; - struct vb2_shared_data *sd = vb2_get_sd(&c); uint32_t v = 1; + reset_common_data(); /* Check size constant */ TEST_EQ(VB2_SECDATA_KERNEL_SIZE, sizeof(struct vb2_secdata_kernel), "Struct size constant"); /* Blank data is invalid */ - memset(c.secdata_kernel, 0xa6, sizeof(c.secdata_kernel)); - TEST_EQ(vb2api_secdata_kernel_check(&c), + memset(ctx.secdata_kernel, 0xa6, sizeof(ctx.secdata_kernel)); + TEST_EQ(vb2api_secdata_kernel_check(&ctx), VB2_ERROR_SECDATA_KERNEL_CRC, "Check blank CRC"); - TEST_EQ(vb2_secdata_kernel_init(&c), + TEST_EQ(vb2_secdata_kernel_init(&ctx), VB2_ERROR_SECDATA_KERNEL_CRC, "Init blank CRC"); /* Ensure zeroed buffers are invalid */ - memset(c.secdata_kernel, 0, sizeof(c.secdata_kernel)); - TEST_EQ(vb2_secdata_kernel_init(&c), VB2_ERROR_SECDATA_KERNEL_VERSION, + memset(ctx.secdata_kernel, 0, sizeof(ctx.secdata_kernel)); + TEST_EQ(vb2_secdata_kernel_init(&ctx), VB2_ERROR_SECDATA_KERNEL_VERSION, "Zeroed buffer (invalid version)"); /* Try with bad version */ - TEST_SUCC(vb2api_secdata_kernel_create(&c), "Create"); + TEST_EQ(vb2api_secdata_kernel_create(&ctx), VB2_SECDATA_KERNEL_SIZE, + "Create"); sec->struct_version -= 1; sec->crc8 = vb2_crc8(sec, offsetof(struct vb2_secdata_kernel, crc8)); - TEST_EQ(vb2api_secdata_kernel_check(&c), + TEST_EQ(vb2api_secdata_kernel_check(&ctx), VB2_ERROR_SECDATA_KERNEL_VERSION, "Check invalid version"); - TEST_EQ(vb2_secdata_kernel_init(&c), + TEST_EQ(vb2_secdata_kernel_init(&ctx), VB2_ERROR_SECDATA_KERNEL_VERSION, "Init invalid version"); /* Create good data */ - TEST_SUCC(vb2api_secdata_kernel_create(&c), "Create"); - TEST_SUCC(vb2api_secdata_kernel_check(&c), "Check created CRC"); - TEST_SUCC(vb2_secdata_kernel_init(&c), "Init created CRC"); + vb2api_secdata_kernel_create(&ctx); + TEST_SUCC(vb2api_secdata_kernel_check(&ctx), "Check created CRC"); + TEST_SUCC(vb2_secdata_kernel_init(&ctx), "Init created CRC"); TEST_NEQ(sd->status & VB2_SD_STATUS_SECDATA_KERNEL_INIT, 0, "Init set SD status"); sd->status &= ~VB2_SD_STATUS_SECDATA_KERNEL_INIT; - test_changed(&c, 1, "Create changes data"); + test_changed(&ctx, 1, "Create changes data"); /* Now corrupt it */ - c.secdata_kernel[2]++; - TEST_EQ(vb2api_secdata_kernel_check(&c), + ctx.secdata_kernel[2]++; + TEST_EQ(vb2api_secdata_kernel_check(&ctx), VB2_ERROR_SECDATA_KERNEL_CRC, "Check invalid CRC"); - TEST_EQ(vb2_secdata_kernel_init(&c), + TEST_EQ(vb2_secdata_kernel_init(&ctx), VB2_ERROR_SECDATA_KERNEL_CRC, "Init invalid CRC"); /* Make sure UID is checked */ - vb2api_secdata_kernel_create(&c); + vb2api_secdata_kernel_create(&ctx); sec->uid++; sec->crc8 = vb2_crc8(sec, offsetof(struct vb2_secdata_kernel, crc8)); - TEST_EQ(vb2_secdata_kernel_init(&c), VB2_ERROR_SECDATA_KERNEL_UID, + TEST_EQ(vb2_secdata_kernel_init(&ctx), VB2_ERROR_SECDATA_KERNEL_UID, "Init invalid struct UID"); /* Read/write versions */ - vb2api_secdata_kernel_create(&c); - vb2_secdata_kernel_init(&c); - c.flags = 0; - TEST_SUCC(vb2_secdata_kernel_get(&c, VB2_SECDATA_KERNEL_VERSIONS, &v), + vb2api_secdata_kernel_create(&ctx); + vb2_secdata_kernel_init(&ctx); + ctx.flags = 0; + TEST_SUCC(vb2_secdata_kernel_get(&ctx, VB2_SECDATA_KERNEL_VERSIONS, &v), "Get versions"); TEST_EQ(v, 0, "Versions created 0"); - test_changed(&c, 0, "Get doesn't change data"); - TEST_SUCC(vb2_secdata_kernel_set(&c, VB2_SECDATA_KERNEL_VERSIONS, + test_changed(&ctx, 0, "Get doesn't change data"); + TEST_SUCC(vb2_secdata_kernel_set(&ctx, VB2_SECDATA_KERNEL_VERSIONS, 0x123456ff), "Set versions"); - test_changed(&c, 1, "Set changes data"); - TEST_SUCC(vb2_secdata_kernel_set(&c, VB2_SECDATA_KERNEL_VERSIONS, + test_changed(&ctx, 1, "Set changes data"); + TEST_SUCC(vb2_secdata_kernel_set(&ctx, VB2_SECDATA_KERNEL_VERSIONS, 0x123456ff), "Set versions 2"); - test_changed(&c, 0, "Set again doesn't change data"); - TEST_SUCC(vb2_secdata_kernel_get(&c, VB2_SECDATA_KERNEL_VERSIONS, &v), + test_changed(&ctx, 0, "Set again doesn't change data"); + TEST_SUCC(vb2_secdata_kernel_get(&ctx, VB2_SECDATA_KERNEL_VERSIONS, &v), "Get versions 2"); TEST_EQ(v, 0x123456ff, "Versions changed"); /* Invalid field fails */ - TEST_EQ(vb2_secdata_kernel_get(&c, -1, &v), + TEST_EQ(vb2_secdata_kernel_get(&ctx, -1, &v), VB2_ERROR_SECDATA_KERNEL_GET_PARAM, "Get invalid"); - TEST_EQ(vb2_secdata_kernel_set(&c, -1, 456), + TEST_EQ(vb2_secdata_kernel_set(&ctx, -1, 456), VB2_ERROR_SECDATA_KERNEL_SET_PARAM, "Set invalid"); - test_changed(&c, 0, "Set invalid field doesn't change data"); + test_changed(&ctx, 0, "Set invalid field doesn't change data"); /* Read/write uninitialized data fails */ sd->status &= ~VB2_SD_STATUS_SECDATA_KERNEL_INIT; - TEST_EQ(vb2_secdata_kernel_get(&c, VB2_SECDATA_KERNEL_VERSIONS, &v), + TEST_EQ(vb2_secdata_kernel_get(&ctx, VB2_SECDATA_KERNEL_VERSIONS, &v), VB2_ERROR_SECDATA_KERNEL_GET_UNINITIALIZED, "Get uninitialized"); - test_changed(&c, 0, "Get uninitialized doesn't change data"); - TEST_EQ(vb2_secdata_kernel_set(&c, VB2_SECDATA_KERNEL_VERSIONS, + test_changed(&ctx, 0, "Get uninitialized doesn't change data"); + TEST_EQ(vb2_secdata_kernel_set(&ctx, VB2_SECDATA_KERNEL_VERSIONS, 0x123456ff), VB2_ERROR_SECDATA_KERNEL_SET_UNINITIALIZED, "Set uninitialized"); - test_changed(&c, 0, "Set uninitialized doesn't change data"); + test_changed(&ctx, 0, "Set uninitialized doesn't change data"); } int main(int argc, char* argv[]) |