diff options
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | firmware/2lib/2secdatak.c | 112 | ||||
-rw-r--r-- | firmware/2lib/include/2api.h | 62 | ||||
-rw-r--r-- | firmware/2lib/include/2return_codes.h | 24 | ||||
-rw-r--r-- | firmware/2lib/include/2secdata.h | 104 | ||||
-rw-r--r-- | firmware/2lib/include/2struct.h | 3 | ||||
-rw-r--r-- | tests/vb2_secdatak_tests.c | 114 |
7 files changed, 414 insertions, 8 deletions
@@ -330,6 +330,7 @@ FWLIB2X_SRCS = \ firmware/2lib/2nvstorage.c \ firmware/2lib/2rsa.c \ firmware/2lib/2secdata.c \ + firmware/2lib/2secdatak.c \ firmware/2lib/2sha1.c \ firmware/2lib/2sha256.c \ firmware/2lib/2sha512.c \ @@ -711,6 +712,7 @@ TEST2X_NAMES = \ tests/vb2_nvstorage_tests \ tests/vb2_rsa_utility_tests \ tests/vb2_secdata_tests \ + tests/vb2_secdatak_tests \ tests/vb2_sha_tests TEST20_NAMES = \ @@ -1376,6 +1378,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_tests + ${RUNTEST} ${BUILD_RUN}/tests/vb2_secdatak_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_sha_tests ${RUNTEST} ${BUILD_RUN}/tests/vb20_api_tests ${RUNTEST} ${BUILD_RUN}/tests/vb20_common_tests diff --git a/firmware/2lib/2secdatak.c b/firmware/2lib/2secdatak.c new file mode 100644 index 00000000..af11aef4 --- /dev/null +++ b/firmware/2lib/2secdatak.c @@ -0,0 +1,112 @@ +/* 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. + * + * Secure storage APIs - kernel version space + */ + +#include "2sysincludes.h" +#include "2common.h" +#include "2crc8.h" +#include "2misc.h" +#include "2secdata.h" + +int vb2_secdatak_check_crc(const struct vb2_context *ctx) +{ + const struct vb2_secdatak *sec = + (const struct vb2_secdatak *)ctx->secdatak; + + /* Verify CRC */ + if (sec->crc8 != vb2_crc8(sec, offsetof(struct vb2_secdatak, crc8))) + return VB2_ERROR_SECDATAK_CRC; + + return VB2_SUCCESS; +} + +int vb2_secdatak_create(struct vb2_context *ctx) +{ + struct vb2_secdatak *sec = (struct vb2_secdatak *)ctx->secdatak; + + /* Clear the entire struct */ + memset(sec, 0, sizeof(*sec)); + + /* Set to current version */ + sec->struct_version = VB2_SECDATAK_VERSION; + + /* Set UID */ + sec->uid = VB2_SECDATAK_UID; + + /* Calculate initial CRC */ + sec->crc8 = vb2_crc8(sec, offsetof(struct vb2_secdatak, crc8)); + ctx->flags |= VB2_CONTEXT_SECDATAK_CHANGED; + return VB2_SUCCESS; +} + +int vb2_secdatak_init(struct vb2_context *ctx) +{ + struct vb2_secdatak *sec = (struct vb2_secdatak *)ctx->secdatak; + struct vb2_shared_data *sd = vb2_get_sd(ctx); + int rv; + + rv = vb2_secdatak_check_crc(ctx); + if (rv) + return rv; + + /* Make sure the UID is correct */ + if (sec->uid != VB2_SECDATAK_UID) + return VB2_ERROR_SECDATAK_UID; + + /* Set status flag */ + sd->status |= VB2_SD_STATUS_SECDATAK_INIT; + /* TODO: unit test for that */ + + return VB2_SUCCESS; +} + +int vb2_secdatak_get(struct vb2_context *ctx, + enum vb2_secdatak_param param, + uint32_t *dest) +{ + struct vb2_secdatak *sec = (struct vb2_secdatak *)ctx->secdatak; + + if (!(vb2_get_sd(ctx)->status & VB2_SD_STATUS_SECDATAK_INIT)) + return VB2_ERROR_SECDATAK_GET_UNINITIALIZED; + + switch(param) { + case VB2_SECDATAK_VERSIONS: + *dest = sec->kernel_versions; + return VB2_SUCCESS; + + default: + return VB2_ERROR_SECDATAK_GET_PARAM; + } +} + +int vb2_secdatak_set(struct vb2_context *ctx, + enum vb2_secdatak_param param, + uint32_t value) +{ + struct vb2_secdatak *sec = (struct vb2_secdatak *)ctx->secdatak; + uint32_t now; + + if (!(vb2_get_sd(ctx)->status & VB2_SD_STATUS_SECDATAK_INIT)) + return VB2_ERROR_SECDATAK_SET_UNINITIALIZED; + + /* If not changing the value, don't regenerate the CRC. */ + if (vb2_secdatak_get(ctx, param, &now) == VB2_SUCCESS && now == value) + return VB2_SUCCESS; + + switch(param) { + case VB2_SECDATAK_VERSIONS: + sec->kernel_versions = value; + break; + + default: + return VB2_ERROR_SECDATAK_SET_PARAM; + } + + /* Regenerate CRC */ + sec->crc8 = vb2_crc8(sec, offsetof(struct vb2_secdatak, crc8)); + ctx->flags |= VB2_CONTEXT_SECDATAK_CHANGED; + return VB2_SUCCESS; +} diff --git a/firmware/2lib/include/2api.h b/firmware/2lib/include/2api.h index d8cc9b8c..0b436842 100644 --- a/firmware/2lib/include/2api.h +++ b/firmware/2lib/include/2api.h @@ -31,8 +31,9 @@ /* Size of non-volatile data used by vboot */ #define VB2_NVDATA_SIZE 16 -/* Size of secure data used by vboot */ +/* Size of secure data spaces used by vboot */ #define VB2_SECDATA_SIZE 10 +#define VB2_SECDATAK_SIZE 14 /* * Recommended size of work buffer. @@ -93,6 +94,12 @@ enum vb2_context_flags { /* Erase TPM developer mode state if it is enabled. */ VB2_DISABLE_DEVELOPER_MODE = (1 << 9), + + /* + * Verified boot has changed secdatak[]. Caller must save secdatak[] + * back to its underlying storage, then may clear this flag. + */ + VB2_CONTEXT_SECDATAK_CHANGED = (1 << 11), }; /* @@ -128,10 +135,11 @@ struct vb2_context { uint8_t nvdata[VB2_NVDATA_SIZE]; /* - * Secure data. 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. + * 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. */ uint8_t secdata[VB2_SECDATA_SIZE]; @@ -154,6 +162,19 @@ struct vb2_context { * copied when relocating the work buffer. */ uint32_t workbuf_used; + + /********************************************************************** + * Fields caller must initialize before calling vb2api_kernel_phase1(). + */ + + /* + * Secure data for kernel verification stage. Caller must fill this + * from some secure non-volatile location. If the + * VB2_CONTEXT_SECDATAK_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 secdatak[VB2_SECDATAK_SIZE]; }; enum vb2_resource_index { @@ -234,8 +255,7 @@ enum vb2_pcr_digest { * * At this point, firmware verification is done, and vb2_context contains the * kernel key needed to verify the kernel. That context should be preserved - * and passed on to kernel selection. For now, that requires translating it - * into the old VbSharedData format (via a func which does not yet exist...) + * and passed on to kernel selection. */ /** @@ -267,6 +287,34 @@ int vb2api_secdata_check(const struct vb2_context *ctx); int vb2api_secdata_create(struct vb2_context *ctx); /** + * Sanity-check the contents of the kernel version secure storage context. + * + * Use this if reading from secure storage may be flaky, and you want to retry + * reading it several times. + * + * This may be called before vb2api_phase1(). + * + * @param ctx Context pointer + * @return VB2_SUCCESS, or non-zero error code if error. + */ +int vb2api_secdatak_check(const struct vb2_context *ctx); + +/** + * Create fresh data in the kernel version 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 vb2api_secdatak_check() + * (or any other API in this library) fails; that could allow the secure data + * to be rolled back to an insecure state. + * + * This may be called before vb2api_phase1(). + * + * @param ctx Context pointer + * @return VB2_SUCCESS, or non-zero error code if error. + */ +int vb2api_secdatak_create(struct vb2_context *ctx); + +/** * Report firmware failure to vboot. * * This may be called before vb2api_phase1() to indicate errors in the boot diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h index 20a7947c..cd8cc270 100644 --- a/firmware/2lib/include/2return_codes.h +++ b/firmware/2lib/include/2return_codes.h @@ -111,6 +111,30 @@ enum vb2_return_code { /* Called vb2_secdata_set() with uninitialized secdata */ VB2_ERROR_SECDATA_SET_UNINITIALIZED, + /* Bad CRC in vb2_secdatak_check_crc() */ + VB2_ERROR_SECDATAK_CRC, + + /* Bad struct version in vb2_secdatak_init() */ + VB2_ERROR_SECDATAK_VERSION, + + /* Bad uid in vb2_secdatak_init() */ + VB2_ERROR_SECDATAK_UID, + + /* Invalid param in vb2_secdatak_get() */ + VB2_ERROR_SECDATAK_GET_PARAM, + + /* Invalid param in vb2_secdatak_set() */ + VB2_ERROR_SECDATAK_SET_PARAM, + + /* Invalid flags passed to vb2_secdatak_set() */ + VB2_ERROR_SECDATAK_SET_FLAGS, + + /* Called vb2_secdatak_get() with uninitialized secdatak */ + VB2_ERROR_SECDATAK_GET_UNINITIALIZED, + + /* Called vb2_secdatak_set() with uninitialized secdatak */ + VB2_ERROR_SECDATAK_SET_UNINITIALIZED, + /********************************************************************** * Common code errors */ diff --git a/firmware/2lib/include/2secdata.h b/firmware/2lib/include/2secdata.h index 25a051d9..d27432e8 100644 --- a/firmware/2lib/include/2secdata.h +++ b/firmware/2lib/include/2secdata.h @@ -8,6 +8,9 @@ #ifndef VBOOT_REFERENCE_VBOOT_SECDATA_H_ #define VBOOT_REFERENCE_VBOOT_SECDATA_H_ +/*****************************************************************************/ +/* Firmware version space */ + /* Expected value of vb2_secdata.version */ #define VB2_SECDATA_VERSION 2 @@ -28,7 +31,7 @@ enum vb2_secdata_flags { VB2_SECDATA_FLAG_DEV_MODE = (1 << 1), }; -/* Secure data area */ +/* Secure data area (firmware space) */ struct vb2_secdata { /* Struct version, for backwards compatibility */ uint8_t struct_version; @@ -55,6 +58,39 @@ enum vb2_secdata_param { VB2_SECDATA_VERSIONS, }; +/*****************************************************************************/ +/* Kernel version space */ + +/* Kernel space - KERNEL_NV_INDEX, locked with physical presence. */ +#define VB2_SECDATAK_VERSION 2 +#define VB2_SECDATAK_UID 0x4752574c /* 'GRWL' */ + +struct vb2_secdatak { + /* Struct version, for backwards compatibility */ + uint8_t struct_version; + + /* Unique ID to detect space redefinition */ + uint32_t uid; + + /* Kernel versions */ + uint32_t kernel_versions; + + /* Reserved for future expansion */ + uint8_t reserved[3]; + + /* CRC; must be last field in struct */ + uint8_t crc8; +} __attribute__((packed)); + +/* Which param to get/set for vb2_secdatak_get() / vb2_secdatak_set() */ +enum vb2_secdatak_param { + /* Kernel versions */ + VB2_SECDATAK_VERSIONS = 0, +}; + +/*****************************************************************************/ +/* Firmware version space functions */ + /** * Check the CRC of the secure storage context. * @@ -114,4 +150,70 @@ int vb2_secdata_set(struct vb2_context *ctx, enum vb2_secdata_param param, uint32_t value); +/*****************************************************************************/ +/* Kernel version space functions. + * + * These are separate functions so that they don't bloat the size of the early + * boot code which uses the firmware version space functions. + */ + +/** + * Check the CRC of the kernel version secure storage context. + * + * Use this if reading from secure storage may be flaky, and you want to retry + * reading it several times. + * + * This may be called before vb2_context_init(). + * + * @param ctx Context pointer + * @return VB2_SUCCESS, or non-zero error code if error. + */ +int vb2_secdatak_check_crc(const struct vb2_context *ctx); + +/** + * Create fresh data in the 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 vb2_secdatak_check_crc() + * (or any other API in this library) fails; that could allow the secure data + * to be rolled back to an insecure state. + * + * This may be called before vb2_context_init(). + */ +int vb2_secdatak_create(struct vb2_context *ctx); + +/** + * Initialize the secure storage context and verify its CRC. + * + * This must be called before vb2_secdatak_get() or vb2_secdatak_set(). + * + * @param ctx Context pointer + * @return VB2_SUCCESS, or non-zero error code if error. + */ +int vb2_secdatak_init(struct vb2_context *ctx); + +/** + * Read a secure storage value. + * + * @param ctx Context pointer + * @param param Parameter to read + * @param dest Destination for value + * @return VB2_SUCCESS, or non-zero error code if error. + */ +int vb2_secdatak_get(struct vb2_context *ctx, + enum vb2_secdatak_param param, + uint32_t *dest); + +/** + * Write a secure storage value. + * + * @param ctx Context pointer + * @param param Parameter to write + * @param value New value + * @return VB2_SUCCESS, or non-zero error code if error. + */ +int vb2_secdatak_set(struct vb2_context *ctx, + enum vb2_secdatak_param param, + uint32_t value); + #endif /* VBOOT_REFERENCE_VBOOT_2SECDATA_H_ */ diff --git a/firmware/2lib/include/2struct.h b/firmware/2lib/include/2struct.h index b64f4ba8..cbf08901 100644 --- a/firmware/2lib/include/2struct.h +++ b/firmware/2lib/include/2struct.h @@ -52,6 +52,9 @@ enum vb2_shared_data_status { /* Chose a firmware slot */ VB2_SD_STATUS_CHOSE_SLOT = (1 << 3), + + /* Secure data kernel version space initialized */ + VB2_SD_STATUS_SECDATAK_INIT = (1 << 4), }; /* diff --git a/tests/vb2_secdatak_tests.c b/tests/vb2_secdatak_tests.c new file mode 100644 index 00000000..1aa059ed --- /dev/null +++ b/tests/vb2_secdatak_tests.c @@ -0,0 +1,114 @@ +/* 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 secure storage library. + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "test_common.h" +#include "vboot_common.h" + +#include "2common.h" +#include "2api.h" +#include "2crc8.h" +#include "2misc.h" +#include "2secdata.h" + +static void test_changed(struct vb2_context *ctx, int changed, const char *why) +{ + if (changed) + TEST_NEQ(ctx->flags & VB2_CONTEXT_SECDATAK_CHANGED, 0, why); + else + TEST_EQ(ctx->flags & VB2_CONTEXT_SECDATAK_CHANGED, 0, why); + + ctx->flags &= ~VB2_CONTEXT_SECDATAK_CHANGED; +}; + +static void secdatak_test(void) +{ + uint8_t workbuf[VB2_WORKBUF_RECOMMENDED_SIZE] + __attribute__ ((aligned (VB2_WORKBUF_ALIGN))); + struct vb2_context c = { + .flags = 0, + .workbuf = workbuf, + .workbuf_size = sizeof(workbuf), + }; + uint32_t v = 1; + + /* Blank data is invalid */ + memset(c.secdatak, 0xa6, sizeof(c.secdatak)); + TEST_EQ(vb2_secdatak_check_crc(&c), + VB2_ERROR_SECDATAK_CRC, "Check blank CRC"); + TEST_EQ(vb2_secdatak_init(&c), + VB2_ERROR_SECDATAK_CRC, "Init blank CRC"); + + /* Create good data */ + TEST_SUCC(vb2_secdatak_create(&c), "Create"); + TEST_SUCC(vb2_secdatak_check_crc(&c), "Check created CRC"); + TEST_SUCC(vb2_secdatak_init(&c), "Init created CRC"); + test_changed(&c, 1, "Create changes data"); + + /* Now corrupt it */ + c.secdatak[2]++; + TEST_EQ(vb2_secdatak_check_crc(&c), + VB2_ERROR_SECDATAK_CRC, "Check invalid CRC"); + TEST_EQ(vb2_secdatak_init(&c), + VB2_ERROR_SECDATAK_CRC, "Init invalid CRC"); + + /* Make sure UID is checked */ + { + struct vb2_secdatak *sec = (struct vb2_secdatak *)c.secdatak; + + vb2_secdatak_create(&c); + sec->uid++; + sec->crc8 = vb2_crc8(sec, offsetof(struct vb2_secdatak, crc8)); + + TEST_EQ(vb2_secdatak_init(&c), VB2_ERROR_SECDATAK_UID, + "Init invalid struct UID"); + } + + /* Read/write versions */ + vb2_secdatak_create(&c); + c.flags = 0; + TEST_SUCC(vb2_secdatak_get(&c, VB2_SECDATAK_VERSIONS, &v), + "Get versions"); + TEST_EQ(v, 0, "Versions created 0"); + test_changed(&c, 0, "Get doesn't change data"); + TEST_SUCC(vb2_secdatak_set(&c, VB2_SECDATAK_VERSIONS, 0x123456ff), + "Set versions"); + test_changed(&c, 1, "Set changes data"); + TEST_SUCC(vb2_secdatak_set(&c, VB2_SECDATAK_VERSIONS, 0x123456ff), + "Set versions 2"); + test_changed(&c, 0, "Set again doesn't change data"); + TEST_SUCC(vb2_secdatak_get(&c, VB2_SECDATAK_VERSIONS, &v), + "Get versions 2"); + TEST_EQ(v, 0x123456ff, "Versions changed"); + + /* Invalid field fails */ + TEST_EQ(vb2_secdatak_get(&c, -1, &v), + VB2_ERROR_SECDATAK_GET_PARAM, "Get invalid"); + TEST_EQ(vb2_secdatak_set(&c, -1, 456), + VB2_ERROR_SECDATAK_SET_PARAM, "Set invalid"); + test_changed(&c, 0, "Set invalid field doesn't change data"); + + /* Read/write uninitialized data fails */ + vb2_get_sd(&c)->status &= ~VB2_SD_STATUS_SECDATAK_INIT; + TEST_EQ(vb2_secdatak_get(&c, VB2_SECDATAK_VERSIONS, &v), + VB2_ERROR_SECDATAK_GET_UNINITIALIZED, "Get uninitialized"); + test_changed(&c, 0, "Get uninitialized doesn't change data"); + TEST_EQ(vb2_secdatak_set(&c, VB2_SECDATAK_VERSIONS, 0x123456ff), + VB2_ERROR_SECDATAK_SET_UNINITIALIZED, "Set uninitialized"); + test_changed(&c, 0, "Set uninitialized doesn't change data"); +} + +int main(int argc, char* argv[]) +{ + secdatak_test(); + + return gTestSuccess ? 0 : 255; +} |