vendorcode/eltan/security/verified_boot: Add verified boot support

Create verified boot support, which includes verifiication of bootblock.
This feature use the vendorcode/eltan/security/lib.

cbfs_locator is used to init the verified boot support.
vendor_secure_prepare() and vendor_secure_locate() are used to preform the
required action in each stage.

The next lists will be used for verification:
 * bootblock_verify_list
 * postcar_verify_list
 * romstage_verify_list
 * ramstage_verify_list

BUG=N/A
TEST=Created binary and verify logging on Facebook FBG-1701

Change-Id: If6c1423b0b4a309cefb7fe7a29d5100ba289e0b4
Signed-off-by: Frans Hendriks <fhendriks@eltan.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/30835
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Lance Zhao <lance.zhao@gmail.com>
Reviewed-by: Philipp Deppenwiese <zaolin.daisuki@gmail.com>
This commit is contained in:
Frans Hendriks 2019-07-26 07:59:05 +02:00 committed by Philipp Deppenwiese
parent 7c82dbcc51
commit 72b3c3c838
4 changed files with 645 additions and 0 deletions

View File

@ -0,0 +1,63 @@
## This file is part of the coreboot project.
##
## Copyright (C) 2018-2019 Eltan B.V.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; version 2 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
menu "Verified Boot (verified_boot)"
config VENDORCODE_ELTAN_VBOOT
bool "Enable Verified Boot"
depends on !VBOOT
default n
config VENDORCODE_ELTAN_VBOOT_SIGNED_MANIFEST
bool "Enable Signed Manifest"
depends on VENDORCODE_ELTAN_VBOOT
default n
config VENDORCODE_ELTAN_VBOOT_USE_SHA512
bool "SHA512 hashes"
depends on VENDORCODE_ELTAN_VBOOT
default n
help
Use SHA512 for the vboot operations, this applies to the digest in
the manifest and the manifest digest.
config VENDORCODE_ELTAN_OEM_MANIFEST_LOC
hex "Manifest Location"
default 0xFFFFF840
config VENDORCODE_ELTAN_VBOOT_MANIFEST
string "Verified boot manifest file"
default "mainboard/$(MAINBOARD_DIR)/manifest.h"
config VENDORCODE_ELTAN_OEM_MANIFEST_ITEMS
int "Manifest Items"
default 11 if POSTCAR_STAGE
default 10
config VENDORCODE_ELTAN_OEM_MANIFEST_ITEM_SIZE
int
default 64 if VENDORCODE_ELTAN_VBOOT_USE_SHA512
default 32
config VENDORCODE_ELTAN_VBOOT_KEY_LOCATION
hex "Verified boot Key Location"
depends on VENDORCODE_ELTAN_VBOOT_SIGNED_MANIFEST
default 0xFFFFF500
config VENDORCODE_ELTAN_VBOOT_KEY_SIZE
int
default 554 if VENDORCODE_ELTAN_VBOOT_USE_SHA512
default 520
endmenu # Verified Boot (verified_boot)

View File

@ -0,0 +1,49 @@
##
## This file is part of the coreboot project.
##
## Copyright (C) 2018-2019 Eltan B.V.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; version 2 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
ifneq ($(filter y,$(CONFIG_VENDORCODE_ELTAN_VBOOT) $(CONFIG_VENDORCODE_ELTAN_MBOOT)),)
CPPFLAGS_common += -I$(src)/security/vboot
bootblock-$(CONFIG_C_ENVIRONMENT_BOOTBLOCK) += vboot_check.c
postcar-y += vboot_check.c
romstage-y += vboot_check.c
ramstage-y += vboot_check.c
ifeq ($(CONFIG_VENDORCODE_ELTAN_VBOOT),y)
cbfs-files-y += oemmanifest.bin
oemmanifest.bin-file := $(obj)/oemmanifest.bin
oemmanifest.bin-position := $(CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_LOC)
oemmanifest.bin-type := raw
$(obj)/oemmanifest.bin:
ifeq ($(CONFIG_VENDORCODE_ELTAN_VBOOT_SIGNED_MANIFEST),y)
dd if=/dev/zero of=$@ seek=8 bs=$(CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_ITEM_SIZE) count=$(CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_ITEMS)
else # ($(CONFIG_VERIFIED_BOOT_SIGNED_MANIFEST),y)
dd if=/dev/zero of=$@ bs=$(CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_ITEM_SIZE) count=$(CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_ITEMS)
endif # ($(CONFIG_VENDORCODE_ELTAN_VBOOT_SIGNED_MANIFEST),y)
ifeq ($(CONFIG_VENDORCODE_ELTAN_VBOOT_SIGNED_MANIFEST),y)
cbfs-files-y += vboot_public_key.bin
vboot_public_key.bin-file := $(obj)/vboot_public_key.bin
vboot_public_key.bin-position := $(CONFIG_VENDORCODE_ELTAN_VBOOT_KEY_LOCATION)
vboot_public_key.bin-type := raw
$(obj)/vboot_public_key.bin:
dd if=/dev/zero of=$@ bs=$(CONFIG_VENDORCODE_ELTAN_VBOOT_KEY_SIZE) count=1
endif # ($(CONFIG_VENDORCODE_ELTAN_VBOOT_SIGNED_MANIFEST),y)
endif # CONFIG_VENDORCODE_ELTAN_VBOOT
endif # CONFIG_VENDORCODE_ELTAN_VBOOT or CONFIG_VENDORCODE_ELTAN_MBOOT

View File

@ -0,0 +1,455 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2016 Intel Corp.
* Copyright (C) 2017-2019 Eltan B.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <boot_device.h>
#include <cbfs.h>
#include <vboot_check.h>
#include <vboot_common.h>
#include "fmap_config.h"
#define RSA_PUBLICKEY_FILE_NAME "vboot_public_key.bin"
#if CONFIG(VENDORCODE_ELTAN_VBOOT_USE_SHA512)
#define DIGEST_SIZE VB2_SHA512_DIGEST_SIZE
#else
#define DIGEST_SIZE VB2_SHA256_DIGEST_SIZE
#endif
int verified_boot_check_manifest(void)
{
struct vb2_public_key key;
const struct vb2_workbuf wb;
uint8_t *buffer;
uint8_t digest[DIGEST_SIZE];
uint8_t *signature = NULL;
size_t size = 0;
int hash_algorithm;
int status;
cbfs_boot_map_with_leak("oemmanifest.bin", CBFS_TYPE_RAW, &size);
if (size != (CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_ITEMS *
DIGEST_SIZE) + 256) {
printk(BIOS_ERR, "ERROR: Incorrect manifest size!\n");
goto fail;
}
buffer = cbfs_boot_map_with_leak(RSA_PUBLICKEY_FILE_NAME,
CBFS_TYPE_RAW, &size);
size = DIGEST_SIZE;
if (!vb2_unpack_key_data(&key, buffer, size)) {
printk(BIOS_ERR, "ERROR: Unable to create RSA Public Key !\n");
goto fail;
}
if (CONFIG(VENDORCODE_ELTAN_VBOOT_USE_SHA512)) {
key.hash_alg = VB2_HASH_SHA512;
hash_algorithm = VB2_HASH_SHA512;
} else {
key.sig_alg = VB2_HASH_SHA256;
hash_algorithm = VB2_HASH_SHA256;
}
/* Create a big endian digest */
status = cb_sha_endian(hash_algorithm,
(const uint8_t *)CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_LOC,
CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_ITEMS * DIGEST_SIZE,
digest, BIG_ENDIAN_ALGORITHM);
if (status)
goto fail;
signature = (uint8_t *)CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_LOC +
CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_ITEMS * DIGEST_SIZE;
if (!vb2_rsa_verify_digest(&key, signature, digest, &wb)) {
printk(BIOS_ERR, "ERROR: Signature verification failed for"
"hash table !!\n");
goto fail;
}
printk(BIOS_DEBUG, "%s: Successfully verified hash_table signature.\n",
__func__);
return 0;
fail:
die("HASH table verification failed!\n");
return -1;
}
static int vendor_secure_locate(struct cbfs_props *props)
{
struct cbfs_header header;
const struct region_device *bdev;
int32_t rel_offset;
size_t offset;
bdev = boot_device_ro();
if (bdev == NULL)
return -1;
size_t fmap_top = ___FMAP__COREBOOT_BASE + ___FMAP__COREBOOT_SIZE;
/* Find location of header using signed 32-bit offset from
* end of CBFS region. */
offset = fmap_top - sizeof(int32_t);
if (rdev_readat(bdev, &rel_offset, offset, sizeof(int32_t)) < 0)
return -1;
offset = fmap_top + rel_offset;
if (rdev_readat(bdev, &header, offset, sizeof(header)) < 0)
return -1;
header.magic = ntohl(header.magic);
header.romsize = ntohl(header.romsize);
header.offset = ntohl(header.offset);
if (header.magic != CBFS_HEADER_MAGIC)
return -1;
props->offset = header.offset;
props->size = header.romsize;
props->size -= props->offset;
printk(BIOS_SPEW, "CBFS @ %zx size %zx\n", props->offset, props->size);
return 0;
}
#ifndef __BOOTBLOCK__
/*
*
* measure_item
*
* extends the defined pcr using the hash calculated by the verified boot
* routines.
*
* @param[in] pcr PCR to extend
* @param[in] *hashData Pointer to the hash data
* @param[in] hashDataLen Length of the hash data
* @param[in] *event_msg Message to log or display
* @param[in] eventType Event type to use when logging
* @retval TPM_SUCCESS Operation completed successfully.
* @retval TPM_E_IOERROR Unexpected device behavior.
*/
static int measure_item(uint32_t pcr, uint8_t *hashData, uint32_t hashDataLen,
int8_t *event_msg, TCG_EVENTTYPE eventType)
{
int status = TPM_SUCCESS;
EFI_TCG2_EVENT_ALGORITHM_BITMAP ActivePcrs;
TCG_PCR_EVENT2_HDR tcgEventHdr;
ActivePcrs = tpm2_get_active_pcrs();
memset(&tcgEventHdr, 0, sizeof(tcgEventHdr));
tcgEventHdr.pcrIndex = pcr;
tcgEventHdr.eventType = eventType;
if (event_msg) {
status = mboot_hash_extend_log(ActivePcrs, MBOOT_HASH_PROVIDED,
hashData, hashDataLen, &tcgEventHdr,
(uint8_t *)event_msg, 0);
if (status == TPM_SUCCESS) {
printk(BIOS_DEBUG, "%s: Success! %s measured to pcr"
"%d.\n", __func__, event_msg, pcr);
} else {
printk(BIOS_DEBUG, "%s: Fail! %s can't be measured. "
"ABORTING!!!\n", __func__, event_msg);
return status;
}
}
return status;
}
#endif
static void verified_boot_check_buffer(const char *name, void *start,
size_t size, uint32_t hash_index, int32_t pcr)
{
uint8_t digest[DIGEST_SIZE];
int hash_algorithm;
int status;
printk(BIOS_DEBUG, "%s: %s HASH verification buffer %p size %d\n",
__func__, name, start, (int) size);
if (start && size) {
if (CONFIG(VENDORCODE_ELTAN_VBOOT_USE_SHA512))
hash_algorithm = VB2_HASH_SHA512;
else
hash_algorithm = VB2_HASH_SHA256;
status = cb_sha_endian(hash_algorithm, (const uint8_t *)start,
size, digest, LITTLE_ENDIAN_ALGORITHM);
if ((CONFIG(VENDORCODE_ELTAN_VBOOT) && memcmp((void *)(
(uint8_t *)CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_LOC +
sizeof(digest) * hash_index), digest, sizeof(digest))) ||
status) {
printk(BIOS_DEBUG, "%s: buffer hash\n", __func__);
hexdump(digest, sizeof(digest));
printk(BIOS_DEBUG, "%s: manifest hash\n", __func__);
hexdump((void *)(
(uint8_t *)CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_LOC +
sizeof(digest) * hash_index), sizeof(digest));
printk(BIOS_EMERG, "%s ", name);
die("HASH verification failed!\n");
} else {
#ifndef __BOOTBLOCK__
if (CONFIG(VENDORCODE_ELTAN_MBOOT)) {
if (pcr != -1) {
printk(BIOS_DEBUG, "%s: measuring %s\n",
__func__, name);
status = measure_item(pcr, digest,
sizeof(digest),
(int8_t *)name, 0);
}
}
#endif
if (CONFIG(VENDORCODE_ELTAN_VBOOT))
printk(BIOS_DEBUG, "%s HASH verification "
"success\n", name);
}
} else {
printk(BIOS_EMERG, "Invalid buffer ");
die("HASH verification failed!\n");
}
}
void verified_boot_check_cbfsfile(const char *name, uint32_t type,
uint32_t hash_index, void **buffer, uint32_t *filesize,
int32_t pcr)
{
void *start;
size_t size;
start = cbfs_boot_map_with_leak(name, type & ~VERIFIED_BOOT_COPY_BLOCK,
&size);
if (start && size) {
/*
* Speed up processing by copying the file content to memory
* first
*/
#ifndef __PRE_RAM__
if ((type & VERIFIED_BOOT_COPY_BLOCK) && (buffer) &&
(*buffer) &&
((uint32_t) start > (uint32_t)(~(CONFIG_CBFS_SIZE-1)))) {
printk(BIOS_DEBUG, "%s: move buffer to "
"memory\n", __func__);
/* Move the file to a memory bufferof which we know it
* doesn't harm
*/
memcpy(*buffer, start, size);
start = *buffer;
printk(BIOS_DEBUG, "%s: done\n", __func__);
}
#endif // __PRE_RAM__
verified_boot_check_buffer(name, start, size, hash_index, pcr);
} else {
printk(BIOS_EMERG, "CBFS Failed to get file content for %s\n",
name);
die("HASH verification failed!\n");
}
if (buffer)
*buffer = start;
if (filesize)
*filesize = size;
}
void process_verify_list(const verify_item_t list[])
{
int i = 0;
while (list[i].type != VERIFY_TERMINATOR) {
switch (list[i].type) {
case VERIFY_FILE:
verified_boot_check_cbfsfile(list[i].name,
list[i].data.file.cbfs_type,
list[i].hash_index, NULL, NULL,
list[i].pcr);
if (list[i].data.file.related_items) {
printk(BIOS_SPEW, "process related items\n");
process_verify_list((verify_item_t *)
list[i].data.file.related_items);
}
break;
case VERIFY_BLOCK:
verified_boot_check_buffer(list[i].name,
(void *) list[i].data.block.start,
list[i].data.block.size,
list[i].hash_index, list[i].pcr);
break;
default:
printk(BIOS_EMERG, "INVALID TYPE IN VERIFY"
"LIST 0x%x\n", list[i].type);
die("HASH verification failed!\n");
}
i++;
}
}
#ifdef __BOOTBLOCK__
/*
* BOOTBLOCK
*/
extern verify_item_t bootblock_verify_list[];
void verified_boot_bootblock_check(void)
{
printk(BIOS_SPEW, "%s: processing bootblock items\n", __func__);
if (CONFIG(VENDORCODE_ELTAN_VBOOT_SIGNED_MANIFEST)) {
printk(BIOS_SPEW, "%s: check the manifest\n", __func__);
if (verified_boot_check_manifest() != 0)
die("invalid manifest");
}
printk(BIOS_SPEW, "%s: process bootblock verify list\n", __func__);
process_verify_list(bootblock_verify_list);
}
static void vendor_secure_prepare(void)
{
printk(BIOS_SPEW, "%s: bootblock\n", __func__);
verified_boot_bootblock_check();
}
#endif //__BOOTBLOCK__
#ifdef __ROMSTAGE__
/*
* ROMSTAGE
*/
extern verify_item_t romstage_verify_list[];
void verified_boot_early_check(void)
{
printk(BIOS_SPEW, "%s: processing early items\n", __func__);
if (!CONFIG(C_ENVIRONMENT_BOOTBLOCK) &&
CONFIG(VENDORCODE_ELTAN_VBOOT_SIGNED_MANIFEST)) {
printk(BIOS_SPEW, "%s: check the manifest\n", __func__);
if (verified_boot_check_manifest() != 0)
die("invalid manifest");
}
if (CONFIG(VENDORCODE_ELTAN_MBOOT)) {
printk(BIOS_DEBUG, "mb_measure returned 0x%x\n",
mb_measure(vboot_platform_is_resuming()));
}
printk(BIOS_SPEW, "%s: process early verify list\n", __func__);
process_verify_list(romstage_verify_list);
}
static int prepare_romstage = 0;
static void vendor_secure_prepare(void)
{
printk(BIOS_SPEW, "%s: romstage\n", __func__);
if (!prepare_romstage) {
verified_boot_early_check();
prepare_romstage = 1;
}
}
#endif //__ROMSTAGE__
#ifdef __POSTCAR__
/*
* POSTCAR
*/
extern verify_item_t postcar_verify_list[];
static void vendor_secure_prepare(void)
{
printk(BIOS_SPEW, "%s: postcar\n", __func__);
process_verify_list(postcar_verify_list);
}
#endif //__POSTCAR__
#ifdef __RAMSTAGE__
/*
* RAM STAGE
*/
static int process_oprom_list(const verify_item_t list[],
struct rom_header *rom_header)
{
int i = 0;
struct pci_data *rom_data;
uint32_t viddevid = 0;
if (le32_to_cpu(rom_header->signature) != PCI_ROM_HDR) {
printk(BIOS_ERR, "Incorrect expansion ROM header "
"signature %04x DONT START\n",
le32_to_cpu(rom_header->signature));
return 0;
}
rom_data = (((void *)rom_header) + le32_to_cpu(rom_header->data));
viddevid |= (rom_data->vendor << 16);
viddevid |= rom_data->device;
while (list[i].type != VERIFY_TERMINATOR) {
switch (list[i].type) {
case VERIFY_OPROM:
if (viddevid == list[i].data.oprom.viddev) {
verified_boot_check_buffer(list[i].name,
(void *) rom_header,
rom_header->size * 512,
list[i].hash_index, list[i].pcr);
if (list[i].data.oprom.related_items) {
printk(BIOS_SPEW, "%s: process"
" related items\n", __func__);
process_verify_list((verify_item_t *)list[i].data.oprom.related_items);
}
printk(BIOS_SPEW, "%s: option rom can be"
" started\n", __func__);
return 1;
}
break;
default:
printk(BIOS_EMERG, "%s: INVALID TYPE IN OPTION ROM LIST"
"0x%x\n", __func__, list[i].type);
die("HASH verification failed!\n");
}
i++;
}
printk(BIOS_ERR, "%s: option rom not in list DONT START\n", __func__);
return 0;
}
extern verify_item_t payload_verify_list[];
extern verify_item_t oprom_verify_list[];
int verified_boot_should_run_oprom(struct rom_header *rom_header)
{
return process_oprom_list(oprom_verify_list, rom_header);
}
static void vendor_secure_prepare(void)
{
printk(BIOS_SPEW, "%s: ramstage\n", __func__);
process_verify_list(payload_verify_list);
}
#endif //__RAMSTAGE__
const struct cbfs_locator cbfs_master_header_locator = {
.name = "Vendorcode Header Locator",
.prepare = vendor_secure_prepare,
.locate = vendor_secure_locate
};

View File

@ -0,0 +1,78 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2016 Intel Corp.
* Copyright (C) 2017-2019 Eltan B.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef VBOOT_CHECK_H
#define VBOOT_CHECK_H
#include <cbfs.h>
#include <device/device.h>
#include <device/pci.h>
#include <lib.h>
#include CONFIG_VENDORCODE_ELTAN_VBOOT_MANIFEST
#include <console/console.h>
#include <cb_sha.h>
#include <string.h>
#include <program_loading.h>
#include <mboot.h>
#define VERIFIED_BOOT_COPY_BLOCK 0x80000000
/* These method verifies the SHA256 hash over the 'named' CBFS component.
* 'type' denotes the type of CBFS component i.e. stage, payload or fsp.
*/
#ifdef __BOOTBLOCK__
void verified_boot_bootblock_check(void);
#endif
#ifdef __ROMSTAGE__
void verified_boot_early_check(void);
#endif
int verified_boot_check_manifest(void);
void verified_boot_check_cbfsfile(const char *name, uint32_t type,
uint32_t hash_index, void **buffer, uint32_t *filesize, int32_t pcr);
typedef enum {
VERIFY_TERMINATOR = 0,
VERIFY_FILE,
VERIFY_BLOCK,
VERIFY_OPROM
} verify_type;
typedef struct {
verify_type type;
const char *name;
union {
struct {
const void *related_items;
uint32_t cbfs_type;
} file;
struct {
const void *start;
uint32_t size;
} block;
struct {
const void *related_items;
uint32_t viddev;
} oprom;
} data;
uint32_t hash_index;
int32_t pcr;
} verify_item_t;
void process_verify_list(const verify_item_t list[]);
#endif //VBOOT_CHECK_H