summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile3
-rw-r--r--firmware/2lib/2secdatak.c112
-rw-r--r--firmware/2lib/include/2api.h62
-rw-r--r--firmware/2lib/include/2return_codes.h24
-rw-r--r--firmware/2lib/include/2secdata.h104
-rw-r--r--firmware/2lib/include/2struct.h3
-rw-r--r--tests/vb2_secdatak_tests.c114
7 files changed, 414 insertions, 8 deletions
diff --git a/Makefile b/Makefile
index 00c6085c..aa82aa2a 100644
--- a/Makefile
+++ b/Makefile
@@ -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;
+}