Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions docs/TPM.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ In wolfBoot we support TPM based root of trust, sealing/unsealing, cryptographic
| `WOLFBOOT_TPM_KEYSTORE=1` | `WOLFBOOT_TPM_KEYSTORE` | Enables TPM based root of trust. NV Index must store a hash of the trusted public key. |
| `WOLFBOOT_TPM_KEYSTORE_NV_BASE=0x` | `WOLFBOOT_TPM_KEYSTORE_NV_BASE=0x` | NV index in platform range 0x1400000 - 0x17FFFFF. |
| `WOLFBOOT_TPM_KEYSTORE_AUTH=secret` | `WOLFBOOT_TPM_KEYSTORE_AUTH` | Password for NV access |
| `MEASURED_BOOT=1` | `WOLFBOOT_MEASURED_BOOT` | Enable measured boot. Extend PCR with wolfBoot hash. |
| `MEASURED_BOOT=1` | `WOLFBOOT_MEASURED_BOOT` | Enable measured boot. Extends PCR with a hash of the wolfBoot bootloader code. |
| `MEASURED_PCR_A=16` | `WOLFBOOT_MEASURED_PCR_A=16` | The PCR index to use. See [docs/measured_boot.md](/docs/measured_boot.md). |
| `MEASURED_BOOT_APP_PARTITION=1` | `WOLFBOOT_MEASURED_BOOT_APP_PARTITION` | Legacy: measure the boot (application) partition instead of wolfBoot code. |
| `WOLFBOOT_TPM_SEAL=1` | `WOLFBOOT_TPM_SEAL` | Enables support for sealing/unsealing based on PCR policy signed externally. |
| `WOLFBOOT_TPM_SEAL_NV_BASE=0x01400300` | `WOLFBOOT_TPM_SEAL_NV_BASE` | To override the default sealed blob storage location in the platform hierarchy. |
| `WOLFBOOT_TPM_SEAL_AUTH=secret` | `WOLFBOOT_TPM_SEAL_AUTH` | Password for sealing/unsealing secrets, if omitted the PCR policy will be used |
Expand All @@ -30,7 +31,9 @@ NOTE: The TPM's RSA verify requires ASN.1 encoding, so use SIGN=RSA2048ENC

## Measured Boot

The wolfBoot image is hashed and extended to the indicated PCR. This can be used later in the application to prove the boot process was not tampered with. Enabled with `WOLFBOOT_MEASURED_BOOT` and exposes API `wolfBoot_tpm2_extend`.
The wolfBoot bootloader code is hashed and extended to the indicated PCR. This can be used later in the application to prove the boot process was not tampered with. Enabled with `WOLFBOOT_MEASURED_BOOT` and exposes API `wolfBoot_tpm2_extend`.

By default, the measurement covers wolfBoot's own code region (from `_start_text` to `_stored_data` linker symbols). To use the legacy behavior of measuring the boot (application) partition instead, set `MEASURED_BOOT_APP_PARTITION=1`.

## Sealing and Unsealing a secret

Expand Down
15 changes: 10 additions & 5 deletions docs/measured_boot.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,13 @@ Having TPM measurements provide a way for the firmware or Operating System(OS),
like Windows or Linux, to know that the software loaded before it gained control
over system, is trustworthy and not modified.

In wolfBoot the concept is simplified to measuring a single component, the main
firmware image. However, this can easily be extended by using more PCR registers.
In wolfBoot the concept is simplified to measuring a single component, the
wolfBoot bootloader code itself. This ensures the bootloader has not been
tampered with before it verifies and loads the application. However, this can
easily be extended by using more PCR registers.

To use the legacy behavior of measuring the boot (application) partition instead
of wolfBoot's own code, set `MEASURED_BOOT_APP_PARTITION=1` in your config.

## Configuration

Expand Down Expand Up @@ -81,6 +86,6 @@ MEASURED_PCR_A?=16
### Code

wolfBoot offers out-of-the-box solution. There is zero need of the developer to touch wolfBoot code
in order to use measured boot. If you would want to check the code, then look in `src/image.c` and
more specifically the `measure_boot()` function. There you would find several TPM2 native API calls
to wolfTPM. For more information about wolfTPM you can check its GitHub repository.
in order to use measured boot. If you would want to check the code, then look in `src/tpm.c` and
more specifically the `self_hash()` and `measure_boot()` functions. There you would find several TPM2
native API calls to wolfTPM. For more information about wolfTPM you can check its GitHub repository.
2 changes: 2 additions & 0 deletions hal/x86_fsp_qemu.ld.in
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ SECTIONS
_end_wolfboot = .;
} > RAM

_stored_data = _end_text;

_fsp_size = _end_fsp_s - _start_fsp_s;
.bss WOLFBOOT_LOAD_BASE + SIZEOF(.text) (NOLOAD):
{
Expand Down
1 change: 1 addition & 0 deletions hal/x86_fsp_qemu_stage1.ld.in
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ SECTIONS

.bootloader WOLFBOOT_ORIGIN :
{
_start_text = .;
KEEP(*(.boot*))
*(.text*)
*(.rodata*)
Expand Down
1 change: 1 addition & 0 deletions hal/x86_fsp_tgl.ld.in
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ SECTIONS
_end_wolfboot = .;
}

_stored_data = _end_text;
_fsp_size = _end_fsp_s - _start_fsp_s;
.bss WOLFBOOT_LOAD_BASE + SIZEOF(.text) (NOLOAD):
{
Expand Down
1 change: 1 addition & 0 deletions hal/x86_fsp_tgl_stage1.ld.in
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ SECTIONS

.bootloader WOLFBOOT_ORIGIN :
{
_start_text = .;
KEEP(./tgl_fsp.o(.boot))
KEEP(*(.boot*))
KEYSTORE_START = .;
Expand Down
3 changes: 3 additions & 0 deletions options.mk
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ ifeq ($(MEASURED_BOOT),1)
WOLFTPM:=1
CFLAGS+=-D"WOLFBOOT_MEASURED_BOOT"
CFLAGS+=-D"WOLFBOOT_MEASURED_PCR_A=$(MEASURED_PCR_A)"
ifeq ($(MEASURED_BOOT_APP_PARTITION),1)
CFLAGS+=-D"WOLFBOOT_MEASURED_BOOT_APP_PARTITION"
endif
endif

## TPM keystore
Expand Down
51 changes: 40 additions & 11 deletions src/tpm.c
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,39 @@ static int TPM2_IoCb(TPM2_CTX* ctx, const uint8_t* txBuf, uint8_t* rxBuf,

#ifdef WOLFBOOT_MEASURED_BOOT

#ifndef WOLFBOOT_NO_PARTITIONS
#ifdef WOLFBOOT_MEASURED_BOOT_APP_PARTITION
/* Legacy: measure the boot (application) partition */
#ifndef WOLFBOOT_NO_PARTITIONS
#define SELF_HASH_ADDR ((uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS)
#define SELF_HASH_SZ ((uint32_t)WOLFBOOT_PARTITION_SIZE)
#endif
#elif defined(ARCH_SIM)
/* Simulator: no linker script, use bootloader partition region */
#if defined(WOLFBOOT_PARTITION_BOOT_ADDRESS) && defined(ARCH_FLASH_OFFSET)
#define SELF_HASH_ADDR ((uintptr_t)ARCH_FLASH_OFFSET)
#define SELF_HASH_SZ ((uint32_t)((uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS - \
(uintptr_t)ARCH_FLASH_OFFSET))
#endif
#elif defined(WOLFBOOT_FSP)
/* FSP: stage1 boot_x86_fsp.c handles measurement via self_extend_pcr()
* and wolfBoot_image_measure(). Skip generic self-measurement here since
* stage2 .data is interleaved with .text making the hash non-deterministic */
#else
/* Default: measure wolfBoot's own code using linker script symbols */
extern unsigned int _start_text;
extern unsigned int _stored_data;
#define SELF_HASH_ADDR ((uintptr_t)&_start_text)
#define SELF_HASH_SZ ((uint32_t)((uintptr_t)&_stored_data - \
(uintptr_t)&_start_text))
#endif

#ifdef SELF_HASH_ADDR
#ifdef WOLFBOOT_HASH_SHA256
#include <wolfssl/wolfcrypt/sha256.h>
static int self_sha256(uint8_t *hash)
{
uintptr_t p = (uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS;
uint32_t sz = (uint32_t)WOLFBOOT_PARTITION_SIZE;
uintptr_t p = SELF_HASH_ADDR;
uint32_t sz = SELF_HASH_SZ;
uint32_t blksz, position = 0;
wc_Sha256 sha256_ctx;

Expand All @@ -244,7 +270,8 @@ static int self_sha256(uint8_t *hash)
blksz = WOLFBOOT_SHA_BLOCK_SIZE;
if (position + blksz > sz)
blksz = sz - position;
#if defined(EXT_FLASH) && defined(NO_XIP)
#if defined(EXT_FLASH) && defined(NO_XIP) && \
defined(WOLFBOOT_MEASURED_BOOT_APP_PARTITION)
rc = ext_flash_read(p, ext_hash_block, WOLFBOOT_SHA_BLOCK_SIZE);
if (rc != WOLFBOOT_SHA_BLOCK_SIZE)
return -1;
Expand All @@ -264,8 +291,8 @@ static int self_sha256(uint8_t *hash)
#include <wolfssl/wolfcrypt/sha512.h>
static int self_sha384(uint8_t *hash)
{
uintptr_t p = (uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS;
uint32_t sz = (uint32_t)WOLFBOOT_PARTITION_SIZE;
uintptr_t p = SELF_HASH_ADDR;
uint32_t sz = SELF_HASH_SZ;
uint32_t blksz, position = 0;
wc_Sha384 sha384_ctx;

Expand All @@ -274,7 +301,8 @@ static int self_sha384(uint8_t *hash)
blksz = WOLFBOOT_SHA_BLOCK_SIZE;
if (position + blksz > sz)
blksz = sz - position;
#if defined(EXT_FLASH) && defined(NO_XIP)
#if defined(EXT_FLASH) && defined(NO_XIP) && \
defined(WOLFBOOT_MEASURED_BOOT_APP_PARTITION)
rc = ext_flash_read(p, ext_hash_block, WOLFBOOT_SHA_BLOCK_SIZE);
if (rc != WOLFBOOT_SHA_BLOCK_SIZE)
return -1;
Expand All @@ -290,7 +318,7 @@ static int self_sha384(uint8_t *hash)
return 0;
}
#endif /* HASH type */
#endif /* WOLFBOOT_NO_PARTITIONS */
#endif /* SELF_HASH_ADDR */

/**
* @brief Extends a PCR in the TPM with a hash.
Expand Down Expand Up @@ -1434,8 +1462,9 @@ int wolfBoot_tpm2_init(void)
}
#endif /* WOLFBOOT_TPM_KEYSTORE | WOLFBOOT_TPM_SEAL */

#if defined(WOLFBOOT_MEASURED_BOOT) && !defined(WOLFBOOT_NO_PARTITIONS)
/* hash wolfBoot and extend PCR */
#if defined(WOLFBOOT_MEASURED_BOOT) && defined(SELF_HASH_ADDR)
/* measured boot: hash wolfBoot code (or boot partition if
* WOLFBOOT_MEASURED_BOOT_APP_PARTITION) and extend PCR */
if (rc == 0) {
rc = self_hash(digest);
if (rc == 0) {
Expand All @@ -1445,7 +1474,7 @@ int wolfBoot_tpm2_init(void)
wolfBoot_printf("Error %d performing wolfBoot measurement!\n", rc);
}
}
#endif /* defined(WOLFBOOT_MEASURED_BOOT) && !defined(WOLFBOOT_NO_PARTITIONS) */
#endif /* WOLFBOOT_MEASURED_BOOT && SELF_HASH_ADDR */

return rc;
}
Expand Down
28 changes: 19 additions & 9 deletions tools/scripts/x86_fsp/compute_pcr.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,21 @@ def get_sha256_hash_of_wolfboot_image(file_path: str):
return data[4:4+l]
data = data[4+l:]

def get_sym_addr(elf_file: str, sym_name: str) -> int:
"""
get the address of a symbol from ELF file
"""
symbols = subprocess.check_output(['nm', elf_file]).split(b'\n')
matches = list(filter(lambda x: sym_name.encode() in x, symbols))
if not matches:
return None
return int(matches[0].split(b' ')[0], 16)

def get_keystore_sym_addr() -> int:
"""
get the address of symbol keystore from ELF file image
"""
symbols = subprocess.check_output(['nm', 'stage1/loader_stage1.elf']).split(b'\n')
_start_keystore = int(list(filter(lambda x: b'_start_keystore' in x, symbols))[0].split(b' ')[0], 16)
return _start_keystore
return get_sym_addr('stage1/loader_stage1.elf', '_start_keystore')

def pcr_extend(pcr: bytearray, data: bytearray) -> bytearray:
"""
Expand Down Expand Up @@ -95,23 +103,25 @@ def pcr_extend(pcr: bytearray, data: bytearray) -> bytearray:

pcr0 = bytearray(b'\x00'*32)
if args.target == 'qemu':
# self_extend_pcr() in boot_x86_fsp.c
# Hashes from _start_keystore to end of 4GB (keystore + vectors)
keystore_addr = get_keystore_sym_addr()
keystore_off = addr_to_off(keystore_addr, image_size = len(image))
ibb = image[keystore_off:]
h = hashlib.sha256()
h.update(ibb)
pcr0_data_hash = h.digest()
pcr0 = pcr_extend(b'\x00'*32, pcr0_data_hash)
pcr0 = pcr_extend(pcr0, get_sha256_hash(ibb))

print(f"Initial PCR0: {pcr0.hex()}")

is_stage1_auth_enabled = get_config_value(config, 'STAGE1_AUTH') == '1'
print(f"stage1 auth is {'enabled' if is_stage1_auth_enabled else 'disabled'}")

if is_stage1_auth_enabled:
is_measured_boot = get_config_value(config, 'MEASURED_BOOT') == '1'

# wolfBoot_image_measure() extends PCR with wolfboot image hash
if is_measured_boot:
wb_hash = get_sha256_hash_of_wolfboot_image('stage1/wolfboot_raw_v1_signed.bin')
pcr0 = pcr_extend(pcr0, wb_hash)
print(f"PCR0 after wolfboot: {pcr0.hex()}")
print(f"PCR0 after wolfboot image measure: {pcr0.hex()}")

# the pcrdigest needed by policy_sign tool is the hash of the concatenation of all PCRs involved in the policy.
# we have only one PCR here
Expand Down
Loading