vboot2: Add support for kernel version secure data space

Holds kernel rollback information.  Will be used by vboot 2.0 kernel
verification.

BUG=chromium:487699
BRANCH=none
TEST=make -j runtests

Change-Id: Ib4a70e943ebd79aac06404df09cf4ce62d719201
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/270626
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
This commit is contained in:
Randall Spangler
2015-05-12 13:44:30 -07:00
committed by ChromeOS Commit Bot
parent 0140cd2906
commit bf9c2760d2
7 changed files with 414 additions and 8 deletions

View File

@@ -330,6 +330,7 @@ FWLIB2X_SRCS = \
firmware/2lib/2nvstorage.c \ firmware/2lib/2nvstorage.c \
firmware/2lib/2rsa.c \ firmware/2lib/2rsa.c \
firmware/2lib/2secdata.c \ firmware/2lib/2secdata.c \
firmware/2lib/2secdatak.c \
firmware/2lib/2sha1.c \ firmware/2lib/2sha1.c \
firmware/2lib/2sha256.c \ firmware/2lib/2sha256.c \
firmware/2lib/2sha512.c \ firmware/2lib/2sha512.c \
@@ -711,6 +712,7 @@ TEST2X_NAMES = \
tests/vb2_nvstorage_tests \ tests/vb2_nvstorage_tests \
tests/vb2_rsa_utility_tests \ tests/vb2_rsa_utility_tests \
tests/vb2_secdata_tests \ tests/vb2_secdata_tests \
tests/vb2_secdatak_tests \
tests/vb2_sha_tests tests/vb2_sha_tests
TEST20_NAMES = \ TEST20_NAMES = \
@@ -1376,6 +1378,7 @@ run2tests: test_setup
${RUNTEST} ${BUILD_RUN}/tests/vb2_nvstorage_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_nvstorage_tests
${RUNTEST} ${BUILD_RUN}/tests/vb2_rsa_utility_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_rsa_utility_tests
${RUNTEST} ${BUILD_RUN}/tests/vb2_secdata_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/vb2_sha_tests
${RUNTEST} ${BUILD_RUN}/tests/vb20_api_tests ${RUNTEST} ${BUILD_RUN}/tests/vb20_api_tests
${RUNTEST} ${BUILD_RUN}/tests/vb20_common_tests ${RUNTEST} ${BUILD_RUN}/tests/vb20_common_tests

112
firmware/2lib/2secdatak.c Normal file
View File

@@ -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;
}

View File

@@ -31,8 +31,9 @@
/* Size of non-volatile data used by vboot */ /* Size of non-volatile data used by vboot */
#define VB2_NVDATA_SIZE 16 #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_SECDATA_SIZE 10
#define VB2_SECDATAK_SIZE 14
/* /*
* Recommended size of work buffer. * Recommended size of work buffer.
@@ -93,6 +94,12 @@ enum vb2_context_flags {
/* Erase TPM developer mode state if it is enabled. */ /* Erase TPM developer mode state if it is enabled. */
VB2_DISABLE_DEVELOPER_MODE = (1 << 9), 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]; uint8_t nvdata[VB2_NVDATA_SIZE];
/* /*
* Secure data. Caller must fill this from some secure non-volatile * Secure data for firmware verification stage. Caller must fill this
* location. If the VB2_CONTEXT_SECDATA_CHANGED flag is set when a * from some secure non-volatile location. If the
* function returns, caller must save the data back to the secure * VB2_CONTEXT_SECDATA_CHANGED flag is set when a function returns,
* non-volatile location and then clear the flag. * caller must save the data back to the secure non-volatile location
* and then clear the flag.
*/ */
uint8_t secdata[VB2_SECDATA_SIZE]; uint8_t secdata[VB2_SECDATA_SIZE];
@@ -154,6 +162,19 @@ struct vb2_context {
* copied when relocating the work buffer. * copied when relocating the work buffer.
*/ */
uint32_t workbuf_used; 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 { enum vb2_resource_index {
@@ -234,8 +255,7 @@ enum vb2_pcr_digest {
* *
* At this point, firmware verification is done, and vb2_context contains the * At this point, firmware verification is done, and vb2_context contains the
* kernel key needed to verify the kernel. That context should be preserved * kernel key needed to verify the kernel. That context should be preserved
* and passed on to kernel selection. For now, that requires translating it * and passed on to kernel selection.
* into the old VbSharedData format (via a func which does not yet exist...)
*/ */
/** /**
@@ -266,6 +286,34 @@ int vb2api_secdata_check(const struct vb2_context *ctx);
*/ */
int vb2api_secdata_create(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. * Report firmware failure to vboot.
* *

View File

@@ -111,6 +111,30 @@ enum vb2_return_code {
/* Called vb2_secdata_set() with uninitialized secdata */ /* Called vb2_secdata_set() with uninitialized secdata */
VB2_ERROR_SECDATA_SET_UNINITIALIZED, 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 * Common code errors
*/ */

View File

@@ -8,6 +8,9 @@
#ifndef VBOOT_REFERENCE_VBOOT_SECDATA_H_ #ifndef VBOOT_REFERENCE_VBOOT_SECDATA_H_
#define VBOOT_REFERENCE_VBOOT_SECDATA_H_ #define VBOOT_REFERENCE_VBOOT_SECDATA_H_
/*****************************************************************************/
/* Firmware version space */
/* Expected value of vb2_secdata.version */ /* Expected value of vb2_secdata.version */
#define VB2_SECDATA_VERSION 2 #define VB2_SECDATA_VERSION 2
@@ -28,7 +31,7 @@ enum vb2_secdata_flags {
VB2_SECDATA_FLAG_DEV_MODE = (1 << 1), VB2_SECDATA_FLAG_DEV_MODE = (1 << 1),
}; };
/* Secure data area */ /* Secure data area (firmware space) */
struct vb2_secdata { struct vb2_secdata {
/* Struct version, for backwards compatibility */ /* Struct version, for backwards compatibility */
uint8_t struct_version; uint8_t struct_version;
@@ -55,6 +58,39 @@ enum vb2_secdata_param {
VB2_SECDATA_VERSIONS, 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. * 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, enum vb2_secdata_param param,
uint32_t value); 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_ */ #endif /* VBOOT_REFERENCE_VBOOT_2SECDATA_H_ */

View File

@@ -52,6 +52,9 @@ enum vb2_shared_data_status {
/* Chose a firmware slot */ /* Chose a firmware slot */
VB2_SD_STATUS_CHOSE_SLOT = (1 << 3), VB2_SD_STATUS_CHOSE_SLOT = (1 << 3),
/* Secure data kernel version space initialized */
VB2_SD_STATUS_SECDATAK_INIT = (1 << 4),
}; };
/* /*

114
tests/vb2_secdatak_tests.c Normal file
View File

@@ -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;
}