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>
diff --git a/src/vendorcode/eltan/security/verified_boot/vboot_check.c b/src/vendorcode/eltan/security/verified_boot/vboot_check.c
new file mode 100644
index 0000000..e2258b9
--- /dev/null
+++ b/src/vendorcode/eltan/security/verified_boot/vboot_check.c
@@ -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
+};