diff --git a/docs/TPM.md b/docs/TPM.md index d936a74013..94fd77f2af 100644 --- a/docs/TPM.md +++ b/docs/TPM.md @@ -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 | @@ -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 diff --git a/docs/measured_boot.md b/docs/measured_boot.md index f02d42a605..78d774bc03 100644 --- a/docs/measured_boot.md +++ b/docs/measured_boot.md @@ -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 @@ -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. diff --git a/hal/x86_fsp_qemu.ld.in b/hal/x86_fsp_qemu.ld.in index cfecbbfcb5..3e08865cb4 100644 --- a/hal/x86_fsp_qemu.ld.in +++ b/hal/x86_fsp_qemu.ld.in @@ -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): { diff --git a/hal/x86_fsp_qemu_stage1.ld.in b/hal/x86_fsp_qemu_stage1.ld.in index 962cd8f09b..dc43b4c2a9 100644 --- a/hal/x86_fsp_qemu_stage1.ld.in +++ b/hal/x86_fsp_qemu_stage1.ld.in @@ -56,6 +56,7 @@ SECTIONS .bootloader WOLFBOOT_ORIGIN : { + _start_text = .; KEEP(*(.boot*)) *(.text*) *(.rodata*) diff --git a/hal/x86_fsp_tgl.ld.in b/hal/x86_fsp_tgl.ld.in index 1ec85c53f1..9c4c156984 100644 --- a/hal/x86_fsp_tgl.ld.in +++ b/hal/x86_fsp_tgl.ld.in @@ -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): { diff --git a/hal/x86_fsp_tgl_stage1.ld.in b/hal/x86_fsp_tgl_stage1.ld.in index 4b7c0ca7bd..923d3bbae8 100644 --- a/hal/x86_fsp_tgl_stage1.ld.in +++ b/hal/x86_fsp_tgl_stage1.ld.in @@ -53,6 +53,7 @@ SECTIONS .bootloader WOLFBOOT_ORIGIN : { + _start_text = .; KEEP(./tgl_fsp.o(.boot)) KEEP(*(.boot*)) KEYSTORE_START = .; diff --git a/options.mk b/options.mk index be66a7dd2e..fd8091be89 100644 --- a/options.mk +++ b/options.mk @@ -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 diff --git a/src/tpm.c b/src/tpm.c index 6a6edc72b5..e8e37eca19 100644 --- a/src/tpm.c +++ b/src/tpm.c @@ -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 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; @@ -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; @@ -264,8 +291,8 @@ static int self_sha256(uint8_t *hash) #include 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; @@ -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; @@ -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. @@ -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) { @@ -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; } diff --git a/tools/scripts/x86_fsp/compute_pcr.py b/tools/scripts/x86_fsp/compute_pcr.py index db3af5012b..7e3c1048e9 100644 --- a/tools/scripts/x86_fsp/compute_pcr.py +++ b/tools/scripts/x86_fsp/compute_pcr.py @@ -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: """ @@ -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