mirror of https://github.com/wolfSSL/wolfBoot.git
Add self-header feature with support for sim and AURIX TC3xx
parent
9397988f01
commit
4d31ef5502
|
|
@ -1,4 +1,4 @@
|
|||
name: Simulator self-update test
|
||||
name: Simulator self-update and self-header tests
|
||||
|
||||
on:
|
||||
push:
|
||||
|
|
@ -71,3 +71,15 @@ jobs:
|
|||
make clean
|
||||
cp config/examples/sim-self-update-ext.config .config
|
||||
make test-sim-self-update-ext
|
||||
|
||||
- name: Run self-header verification test (internal flash)
|
||||
run: |
|
||||
make clean
|
||||
cp config/examples/sim-self-header.config .config
|
||||
make test-sim-self-header-verify
|
||||
|
||||
- name: Run self-header verification test (external flash)
|
||||
run: |
|
||||
make clean
|
||||
cp config/examples/sim-self-header-ext.config .config
|
||||
make test-sim-self-header-ext-verify
|
||||
|
|
|
|||
3
Makefile
3
Makefile
|
|
@ -603,7 +603,8 @@ include/target.h: $(TARGET_H_TEMPLATE) FORCE
|
|||
sed -e "s/@WOLFBOOT_DTS_BOOT_ADDRESS@/$(WOLFBOOT_DTS_BOOT_ADDRESS)/g" | \
|
||||
sed -e "s/@WOLFBOOT_DTS_UPDATE_ADDRESS@/$(WOLFBOOT_DTS_UPDATE_ADDRESS)/g" | \
|
||||
sed -e "s/@WOLFBOOT_LOAD_ADDRESS@/$(WOLFBOOT_LOAD_ADDRESS)/g" | \
|
||||
sed -e "s/@WOLFBOOT_LOAD_DTS_ADDRESS@/$(WOLFBOOT_LOAD_DTS_ADDRESS)/g" \
|
||||
sed -e "s/@WOLFBOOT_LOAD_DTS_ADDRESS@/$(WOLFBOOT_LOAD_DTS_ADDRESS)/g" | \
|
||||
sed -e "s/@WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS@/$(WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS)/g" \
|
||||
> $@
|
||||
|
||||
delta: tools/delta/bmdiff
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
ARCH?=AURIX_TC3
|
||||
TARGET?=aurix_tc3xx
|
||||
AURIX_TC3_HSM=1
|
||||
SIGN?=RSA4096
|
||||
HASH?=SHA256
|
||||
DEBUG?=0
|
||||
WOLFBOOT_VERSION?=1
|
||||
V?=0
|
||||
SPMATH?=1
|
||||
RAM_CODE?=1
|
||||
EXT_FLASH?=1
|
||||
EXT_BOOT=1
|
||||
EXT_UPDATE=1
|
||||
EXT_SWAP=1
|
||||
FLAGS_INVERT=1
|
||||
FLASH_MULTI_SECTOR_ERASE=1
|
||||
DEBUG_UART=1
|
||||
PRINTF_ENABLED=1
|
||||
|
||||
# wolfHSM options
|
||||
WOLFHSM_SERVER=1
|
||||
|
||||
# Cert chain options
|
||||
CERT_CHAIN_VERIFY=1
|
||||
|
||||
# RSA4096 cert chains need the larger header and stack
|
||||
WOLFBOOT_HUGE_STACK=1
|
||||
IMAGE_HEADER_SIZE=4096
|
||||
|
||||
# self-header feature (persist header in external flash)
|
||||
WOLFBOOT_SELF_HEADER=1
|
||||
SELF_HEADER_EXT=1
|
||||
|
||||
ARCH_FLASH_OFFSET=0x80028000
|
||||
WOLFBOOT_SECTOR_SIZE=0x4000
|
||||
WOLFBOOT_PARTITION_SIZE=0x30000
|
||||
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80038000
|
||||
WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x80068000
|
||||
WOLFBOOT_PARTITION_SWAP_ADDRESS=0x80098000
|
||||
WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS=0x8009C000
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
ARCH=sim
|
||||
TARGET=sim
|
||||
SIGN?=ED25519
|
||||
HASH?=SHA256
|
||||
WOLFBOOT_SMALL_STACK?=0
|
||||
SPI_FLASH=0
|
||||
EXT_FLASH=1
|
||||
DEBUG=1
|
||||
RAM_CODE=1
|
||||
WOLFBOOT_VERSION=1
|
||||
|
||||
# Partition size increased to accommodate test-app with verify APIs
|
||||
WOLFBOOT_PARTITION_SIZE=0x80000
|
||||
WOLFBOOT_SECTOR_SIZE=0x1000
|
||||
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80000
|
||||
# Update and swap on external flash (address 0x00000)
|
||||
WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x00000
|
||||
WOLFBOOT_PARTITION_SWAP_ADDRESS=0x80000
|
||||
|
||||
# Self-header feature (persist header in external flash)
|
||||
WOLFBOOT_SELF_HEADER=1
|
||||
SELF_HEADER_EXT=1
|
||||
WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS=0x90000
|
||||
|
||||
# Required for keytools
|
||||
WOLFBOOT_FIXED_PARTITIONS=1
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
ARCH=sim
|
||||
TARGET=sim
|
||||
SIGN?=ED25519
|
||||
HASH?=SHA256
|
||||
WOLFBOOT_SMALL_STACK?=0
|
||||
SPI_FLASH=0
|
||||
DEBUG=1
|
||||
RAM_CODE=1
|
||||
WOLFBOOT_VERSION=1
|
||||
|
||||
# Partition size increased to 512KB to accommodate test-app with verify APIs
|
||||
WOLFBOOT_PARTITION_SIZE=0x80000
|
||||
WOLFBOOT_SECTOR_SIZE=0x1000
|
||||
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80000
|
||||
WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x100000
|
||||
WOLFBOOT_PARTITION_SWAP_ADDRESS=0x180000
|
||||
|
||||
WOLFBOOT_FIXED_PARTITIONS=1
|
||||
|
||||
# Self-header feature
|
||||
WOLFBOOT_SELF_HEADER=1
|
||||
WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS=0x40000
|
||||
|
|
@ -165,6 +165,23 @@ the bootloader itself is stored.
|
|||
* `--wolfboot-update` Indicate that the image contains a signed self-update
|
||||
package for the bootloader. Equivalent to `--id 0`.
|
||||
|
||||
#### Header-only output (wolfBoot self header)
|
||||
|
||||
Use `--header-only` to emit only the manifest header without copying the
|
||||
firmware bytes into the output file. This is useful when persisting
|
||||
wolfBoot's own header at a separate flash address for external measurement:
|
||||
|
||||
```
|
||||
$ tools/keytools/sign --wolfboot-update --header-only wolfboot.bin key.der 1
|
||||
# Produces wolfboot_v1_header.bin (header only)
|
||||
$ tools/keytools/sign --wolfboot-update wolfboot.bin key.der 1
|
||||
# Produces wolfboot_v1_signed.bin (header + firmware)
|
||||
```
|
||||
|
||||
For complete documentation of the self-header feature — including
|
||||
configuration, update flow, and runtime verification — see
|
||||
[firmware_update.md](firmware_update.md#self-header-persisting-the-bootloader-manifest).
|
||||
|
||||
#### Encryption using a symmetric key
|
||||
|
||||
Although signed to be authenticated, by default the image is not encrypted and
|
||||
|
|
|
|||
|
|
@ -111,6 +111,14 @@ In order to boot a different image, wolfBoot will have to swap the content of th
|
|||
|
||||
For more information on how firmware images are stored and managed within the two partitions, see [Flash partitions](flash_partitions.md)
|
||||
|
||||
The same manifest header format is also used by the **self-header** feature
|
||||
(`WOLFBOOT_SELF_HEADER`), where a copy of the bootloader's manifest header is
|
||||
persisted at a separate flash address. This allows external components to
|
||||
cryptographically verify the bootloader's authenticity and version using the
|
||||
standard wolfBoot verification APIs. See
|
||||
[firmware_update.md](firmware_update.md#self-header-persisting-the-bootloader-manifest)
|
||||
for full details.
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,125 @@ rebooting.
|
|||
wolfBoot can be used to deploy new bootloader versions as well as
|
||||
update keys.
|
||||
|
||||
#### Self-header: persisting the bootloader manifest
|
||||
|
||||
In a typical wolfBoot deployment the bootloader verifies the application
|
||||
firmware, but no entity verifies the bootloader itself. When an external
|
||||
component — such as a [wolfHSM](wolfHSM.md) server, or another
|
||||
secure co-processor — needs to measure and authenticate the running
|
||||
bootloader, it must be able to read the bootloader's signed manifest
|
||||
header independently. The **self-header** feature makes this possible by
|
||||
persisting a copy of the bootloader's manifest header at a dedicated,
|
||||
fixed flash address after every self-update.
|
||||
|
||||
This completes the chain of trust:
|
||||
|
||||
```
|
||||
External verifier --verifies--> wolfBoot -->verifies--> Application
|
||||
(wolfHSM/TPM) (self-header) (BOOT partition)
|
||||
```
|
||||
|
||||
##### Configuration options
|
||||
|
||||
| Build option | Required | Description |
|
||||
|---|---|---|
|
||||
| `WOLFBOOT_SELF_HEADER=1` | Yes | Enable the self-header feature |
|
||||
| `WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS=<addr>` | Yes | Flash address where the header is stored. **Must be sector-aligned.** |
|
||||
| `WOLFBOOT_SELF_HEADER_SIZE=<size>` | No | Erase span at the header address. Defaults to `IMAGE_HEADER_SIZE`. Must be ≥ `IMAGE_HEADER_SIZE`. |
|
||||
| `SELF_HEADER_EXT=1` | No | Store the header in external flash. Requires `EXT_FLASH=1`. |
|
||||
|
||||
##### Flash layout
|
||||
|
||||
The self-header occupies its own region in the flash map, separate from
|
||||
the bootloader binary and the firmware partitions:
|
||||
|
||||
```
|
||||
Internal flash (example)
|
||||
┌─────────────────────┐ 0x00000
|
||||
│ wolfBoot │
|
||||
│ (bootloader) │
|
||||
├─────────────────────┤ WOLFBOOT_PARTITION_BOOT_ADDRESS
|
||||
│ BOOT partition │
|
||||
├─────────────────────┤ WOLFBOOT_PARTITION_UPDATE_ADDRESS
|
||||
│ UPDATE partition │
|
||||
├─────────────────────┤ WOLFBOOT_PARTITION_SWAP_ADDRESS
|
||||
│ SWAP partition │
|
||||
├─────────────────────┤ WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS
|
||||
│ Self-header │ (IMAGE_HEADER_SIZE or WOLFBOOT_SELF_HEADER_SIZE)
|
||||
└─────────────────────┘
|
||||
```
|
||||
|
||||
When `SELF_HEADER_EXT=1` is set the self-header is stored in external
|
||||
flash instead. See [Flash partitions](flash_partitions.md) for more
|
||||
detail on the overall partition layout.
|
||||
|
||||
##### Update flow
|
||||
|
||||
During a self-update with `WOLFBOOT_SELF_HEADER` enabled, the following
|
||||
steps occur:
|
||||
|
||||
1. A new signed bootloader image (created with `--wolfboot-update`) is
|
||||
placed in the UPDATE partition.
|
||||
2. The application triggers the update (e.g. `wolfBoot_update_trigger()`).
|
||||
3. On reboot, wolfBoot validates the new bootloader image — verifying both
|
||||
integrity and signature.
|
||||
4. The new bootloader binary is copied to flash, overwriting the old one in-place.
|
||||
5. **After** the firmware copy completes, the manifest header is written
|
||||
to `WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS`.
|
||||
6. The system reboots into the new bootloader.
|
||||
|
||||
##### Runtime verification API
|
||||
|
||||
Applications and external verifiers can use the following functions
|
||||
(declared in `include/image.h` and `include/wolfboot/wolfboot.h`) when linking
|
||||
against wolfBoot as a library to read and verify the persisted self-header:
|
||||
|
||||
- **`wolfBoot_get_self_header()`** — returns a pointer to the persisted
|
||||
header bytes, or `NULL` if the header is missing or invalid.
|
||||
- **`wolfBoot_get_self_version()`** — returns the version number stored
|
||||
in the persisted header, or `0` if the header is invalid.
|
||||
- **`wolfBoot_open_self(struct wolfBoot_image *img)`** — opens the
|
||||
self-header and populates `img` so that the standard verification
|
||||
functions can be used on it. Returns `0` on success.
|
||||
- **`wolfBoot_open_self_address(struct wolfBoot_image *img, uint8_t *hdr, uint8_t *image)`**
|
||||
— like `wolfBoot_open_self()` but accepts explicit header and firmware
|
||||
base addresses. Useful for opening any self-header and image combination.
|
||||
|
||||
After opening the image with `wolfBoot_open_self()`, the caller can
|
||||
verify the bootloader using the standard verification functions:
|
||||
|
||||
```c
|
||||
struct wolfBoot_image img;
|
||||
if (wolfBoot_open_self(&img) == 0) {
|
||||
wolfBoot_verify_integrity(&img);
|
||||
wolfBoot_verify_authenticity(&img);
|
||||
}
|
||||
```
|
||||
|
||||
**NOTE: An application verifying its own integrity and authenticity almost never provides meaningful security.**
|
||||
|
||||
The self-header feature exists to support verification of an *untrusted* wolfBoot image by an external entity that has its own independent root of trust, before execution is transferred to wolfBoot.
|
||||
This is intended for platforms where the silicon does not support ROM-based verification of a first-stage bootloader.
|
||||
|
||||
A common use case is in automotive multicore systems used with wolfHSM, where an HSM core boots first and is responsible for authenticating and releasing the remaining cores in the system.
|
||||
|
||||
##### Factory programming
|
||||
|
||||
At manufacturing time the self-header must be programmed alongside the
|
||||
bootloader binary. Use `--header-only` with the sign tool to generate a
|
||||
standalone header binary:
|
||||
|
||||
```
|
||||
tools/keytools/sign --wolfboot-update --header-only wolfboot.bin key.der 1
|
||||
```
|
||||
|
||||
This produces a `wolfboot_v1_header.bin` containing only the manifest
|
||||
header. Program it at `WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS` and the
|
||||
regular signed image at the bootloader origin.
|
||||
|
||||
See [Signing.md](Signing.md#header-only-output-wolfboot-self-header) for
|
||||
more detail on the `--header-only` sign tool option.
|
||||
|
||||
### Incremental updates (aka: 'delta' updates)
|
||||
|
||||
wolfBoot supports incremental updates, based on a specific older version. The sign tool
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ The flash memory of the target is partitioned into the following areas:
|
|||
- Swapping space (SWAP partition) starting at address `WOLFBOOT_PARTITION_SWAP_ADDRESS`
|
||||
- the swap space size is defined as `WOLFBOOT_SECTOR_SIZE` and must be as big as the
|
||||
largest sector used in either BOOT/UPDATE partitions.
|
||||
- (Optional) Self-header partition starting at address `WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS`,
|
||||
used when `WOLFBOOT_SELF_HEADER=1` is enabled. See [Self-header partition](#self-header-partition) below.
|
||||
|
||||
A proper partitioning configuration must be set up for the specific use, by setting
|
||||
the values for offsets and sizes in [include/target.h](../include/target.h).
|
||||
|
|
@ -34,6 +36,48 @@ This partition is usually very small, and only contains the bootloader code and
|
|||
Public keys pre-authorized during factory image creations are automatically stored
|
||||
as part of the firmware image.
|
||||
|
||||
### Self-header partition
|
||||
|
||||
When `WOLFBOOT_SELF_HEADER=1` is enabled, an additional flash region is
|
||||
reserved for the bootloader's own signed manifest header. This allows
|
||||
external components (e.g. wolfHSM server, a secure co-processor, etc.) to read
|
||||
and cryptographically verify the bootloader.
|
||||
|
||||
Configuration:
|
||||
|
||||
- `WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS` — the flash address for the
|
||||
header. **Must be aligned to `WOLFBOOT_SECTOR_SIZE`.**
|
||||
- `WOLFBOOT_SELF_HEADER_SIZE` — (optional) the erase span at the header
|
||||
address. Defaults to `IMAGE_HEADER_SIZE` and must be at least
|
||||
`IMAGE_HEADER_SIZE`.
|
||||
- `SELF_HEADER_EXT=1` — store the self-header in external flash instead
|
||||
of internal flash. Requires `EXT_FLASH=1`.
|
||||
|
||||
The self-header partition sits alongside the other partitions in the
|
||||
flash map:
|
||||
|
||||
```
|
||||
┌─────────────────────┐
|
||||
│ wolfBoot │
|
||||
├─────────────────────┤ BOOT_ADDRESS
|
||||
│ BOOT partition │
|
||||
├─────────────────────┤ UPDATE_ADDRESS
|
||||
│ UPDATE partition │
|
||||
├─────────────────────┤ SWAP_ADDRESS
|
||||
│ SWAP partition │
|
||||
├─────────────────────┤ SELF_HEADER_ADDRESS
|
||||
│ Self-header │
|
||||
└─────────────────────┘
|
||||
```
|
||||
|
||||
The actual placement is flexible — the self-header can be located
|
||||
anywhere in the flash map as long as it does not overlap with any other
|
||||
partition and is sector-aligned.
|
||||
|
||||
For full details on the self-header feature — including the update flow,
|
||||
runtime verification API, and factory programming — see
|
||||
[firmware_update.md](firmware_update.md#self-header-persisting-the-bootloader-manifest).
|
||||
|
||||
### BOOT partition
|
||||
|
||||
This is the only partition from where it is possible to chain-load and execute a
|
||||
|
|
|
|||
|
|
@ -301,9 +301,11 @@ void hal_init(void)
|
|||
#ifdef DEBUG_UART
|
||||
uart_init();
|
||||
#ifndef WOLFBOOT_AURIX_TC3XX_HSM
|
||||
wolfBoot_printf("Hello from TC3xx wolfBoot on Tricore\n");
|
||||
wolfBoot_printf("Hello from TC3xx wolfBoot on Tricore: V%d\n",
|
||||
WOLFBOOT_VERSION);
|
||||
#else
|
||||
wolfBoot_printf("Hello from TC3xx wolfBoot on HSM\n");
|
||||
wolfBoot_printf("Hello from TC3xx wolfBoot on HSM: V%d\n",
|
||||
WOLFBOOT_VERSION);
|
||||
#endif
|
||||
#endif /* DEBUG_UART */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1255,6 +1255,11 @@ int wolfBoot_open_image(struct wolfBoot_image *img, uint8_t part);
|
|||
int wolfBoot_open_image_external(struct wolfBoot_image* img, uint8_t part, uint8_t* addr);
|
||||
#endif
|
||||
int wolfBoot_open_image_address(struct wolfBoot_image* img, uint8_t* image);
|
||||
#ifdef WOLFBOOT_SELF_HEADER
|
||||
int wolfBoot_open_self(struct wolfBoot_image *img);
|
||||
int wolfBoot_open_self_address(struct wolfBoot_image *img, uint8_t *hdr,
|
||||
uint8_t *image);
|
||||
#endif
|
||||
int wolfBoot_verify_integrity(struct wolfBoot_image *img);
|
||||
int wolfBoot_verify_authenticity(struct wolfBoot_image *img);
|
||||
int wolfBoot_set_partition_state(uint8_t part, uint8_t newst);
|
||||
|
|
@ -1293,10 +1298,16 @@ int keyslot_id_by_sha(const uint8_t *hint);
|
|||
# else
|
||||
# define SWAP_EXT 0
|
||||
# endif
|
||||
# if defined(WOLFBOOT_SELF_HEADER_EXT)
|
||||
# define SELF_HEADER_EXT 1
|
||||
# else
|
||||
# define SELF_HEADER_EXT 0
|
||||
# endif
|
||||
# define PARTN_IS_EXT(pn) \
|
||||
((pn == PART_BOOT || pn == PART_DTS_BOOT) ? BOOT_EXT: \
|
||||
((pn == PART_UPDATE || pn == PART_DTS_UPDATE) ? UPDATE_EXT : \
|
||||
((pn == PART_SWAP) ? SWAP_EXT : 0)))
|
||||
((pn == PART_SWAP) ? SWAP_EXT : \
|
||||
((pn == PART_SELF) ? SELF_HEADER_EXT : 0))))
|
||||
# define PART_IS_EXT(x) (!(x)->not_ext) && PARTN_IS_EXT(((x)->part))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -95,6 +95,16 @@
|
|||
#ifndef WOLFBOOT_PARTITION_SIZE
|
||||
#define WOLFBOOT_PARTITION_SIZE @WOLFBOOT_PARTITION_SIZE@
|
||||
#endif
|
||||
#ifdef WOLFBOOT_SELF_HEADER
|
||||
#if defined(WOLFBOOT_PART_USE_ARCH_OFFSET)
|
||||
#if !defined(EXT_FLASH) || (defined(EXT_FLASH) && !defined(WOLFBOOT_SELF_HEADER_EXT))
|
||||
#define WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS (ARCH_FLASH_OFFSET + @WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS@)
|
||||
#endif
|
||||
#endif
|
||||
#ifndef WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS
|
||||
#define WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS @WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS@
|
||||
#endif
|
||||
#endif /* WOLFBOOT_SELF_HEADER */
|
||||
#endif
|
||||
|
||||
#define WOLFBOOT_DTS_BOOT_ADDRESS @WOLFBOOT_DTS_BOOT_ADDRESS@
|
||||
|
|
|
|||
|
|
@ -152,6 +152,27 @@ extern "C" {
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef WOLFBOOT_SELF_HEADER
|
||||
#ifndef WOLFBOOT_SELF_HEADER_SIZE
|
||||
#define WOLFBOOT_SELF_HEADER_SIZE IMAGE_HEADER_SIZE
|
||||
#endif
|
||||
#if (WOLFBOOT_SELF_HEADER_SIZE < IMAGE_HEADER_SIZE)
|
||||
#error "WOLFBOOT_SELF_HEADER_SIZE must be at least IMAGE_HEADER_SIZE"
|
||||
#endif
|
||||
#ifdef __WOLFBOOT
|
||||
#if !defined(WOLFBOOT_PART_USE_ARCH_OFFSET)
|
||||
#if (WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS % WOLFBOOT_SECTOR_SIZE)
|
||||
#error "WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS must be sector aligned"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#ifdef WOLFBOOT_SELF_HEADER_EXT
|
||||
#ifndef EXT_FLASH
|
||||
#error "WOLFBOOT_SELF_HEADER_EXT requires EXT_FLASH"
|
||||
#endif
|
||||
#endif
|
||||
#endif /* WOLFBOOT_SELF_HEADER */
|
||||
|
||||
#ifdef BIG_ENDIAN_ORDER
|
||||
# define WOLFBOOT_MAGIC 0x574F4C46 /* WOLF */
|
||||
# define WOLFBOOT_MAGIC_TRAIL 0x424F4F54 /* BOOT */
|
||||
|
|
@ -433,6 +454,7 @@ extern "C" {
|
|||
#define PART_BOOT 0
|
||||
#define PART_UPDATE 1
|
||||
#define PART_SWAP 2
|
||||
#define PART_SELF 3
|
||||
#define PART_NONE 0xFF
|
||||
|
||||
#define PART_DTS (0x10)
|
||||
|
|
@ -473,6 +495,10 @@ uint32_t wolfBoot_image_size(uint8_t *image);
|
|||
uint32_t wolfBoot_get_blob_version(uint8_t *blob);
|
||||
uint16_t wolfBoot_get_blob_type(uint8_t *blob);
|
||||
uint32_t wolfBoot_get_blob_diffbase_version(uint8_t *blob);
|
||||
#ifdef WOLFBOOT_SELF_HEADER
|
||||
uint8_t* wolfBoot_get_self_header(void);
|
||||
uint32_t wolfBoot_get_self_version(void);
|
||||
#endif
|
||||
|
||||
uint16_t wolfBoot_find_header(uint8_t *haystack, uint16_t type, uint8_t **ptr);
|
||||
|
||||
|
|
|
|||
15
options.mk
15
options.mk
|
|
@ -74,6 +74,21 @@ ifeq ($(WOLFBOOT_TPM_SEAL),1)
|
|||
endif
|
||||
endif
|
||||
|
||||
## Persist wolfBoot self header at fixed address
|
||||
## Invariants and defaults are enforced in wolfboot.h
|
||||
ifeq ($(WOLFBOOT_SELF_HEADER),1)
|
||||
CFLAGS+=-DWOLFBOOT_SELF_HEADER
|
||||
ifeq ($(WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS),)
|
||||
$(error WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS must be set when WOLFBOOT_SELF_HEADER=1)
|
||||
endif
|
||||
ifneq ($(WOLFBOOT_SELF_HEADER_SIZE),)
|
||||
CFLAGS+=-D"WOLFBOOT_SELF_HEADER_SIZE=$(WOLFBOOT_SELF_HEADER_SIZE)"
|
||||
endif
|
||||
ifeq ($(SELF_HEADER_EXT),1)
|
||||
CFLAGS+=-DWOLFBOOT_SELF_HEADER_EXT
|
||||
endif
|
||||
endif
|
||||
|
||||
## DSA Settings
|
||||
ifeq ($(SIGN),NONE)
|
||||
SIGN_OPTIONS+=--no-sign
|
||||
|
|
|
|||
61
src/image.c
61
src/image.c
|
|
@ -1436,6 +1436,67 @@ int wolfBoot_open_image_external(struct wolfBoot_image* img, uint8_t part,
|
|||
|
||||
#endif /* WOLFBOOT_FIXED_PARTITIONS */
|
||||
|
||||
#ifdef WOLFBOOT_SELF_HEADER
|
||||
/**
|
||||
* @brief Open wolfBoot's own image for verification.
|
||||
*
|
||||
* This function initializes a wolfBoot_image structure to represent wolfBoot
|
||||
* itself, using the persisted self-header and the bootloader's flash location.
|
||||
* The resulting image can be passed to wolfBoot_verify_integrity() and
|
||||
* wolfBoot_verify_authenticity() to verify the bootloader.
|
||||
*
|
||||
* @param img Pointer to a wolfBoot_image structure to be initialized.
|
||||
*
|
||||
* @return 0 on success, -1 on failure (NULL pointer or invalid self-header).
|
||||
*/
|
||||
int wolfBoot_open_self(struct wolfBoot_image* img)
|
||||
{
|
||||
uint8_t* hdr;
|
||||
int ret;
|
||||
|
||||
if (img == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdr = wolfBoot_get_self_header();
|
||||
if (hdr == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = wolfBoot_open_self_address(img, hdr, (uint8_t*)ARCH_FLASH_OFFSET);
|
||||
if (ret == 0) {
|
||||
/* PART_SELF may be marked external for header storage, but wolfBoot
|
||||
* firmware bytes are always in internal flash at ARCH_FLASH_OFFSET. */
|
||||
img->not_ext = 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Directly accesses flash, suitable for non-internal-wolfBoot usage
|
||||
*/
|
||||
int wolfBoot_open_self_address(struct wolfBoot_image* img, uint8_t* hdr,
|
||||
uint8_t* image)
|
||||
{
|
||||
uint32_t magic;
|
||||
|
||||
XMEMSET(img, 0, sizeof(struct wolfBoot_image));
|
||||
|
||||
magic = *((uint32_t*)hdr);
|
||||
if (magic != WOLFBOOT_MAGIC) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
img->hdr = hdr;
|
||||
img->fw_size = wolfBoot_image_size(hdr);
|
||||
img->fw_base = image;
|
||||
img->part = PART_SELF;
|
||||
img->hdr_ok = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Verify the integrity of the image using the stored SHA hash.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1174,6 +1174,45 @@ static uint8_t* wolfBoot_get_image_from_part(uint8_t part)
|
|||
return image;
|
||||
}
|
||||
|
||||
#ifdef WOLFBOOT_SELF_HEADER
|
||||
uint8_t* wolfBoot_get_self_header(void)
|
||||
{
|
||||
#if defined(EXT_FLASH) && defined(WOLFBOOT_SELF_HEADER_EXT)
|
||||
static uint8_t hdr_buf[IMAGE_HEADER_SIZE];
|
||||
uint32_t magic;
|
||||
|
||||
ext_flash_read((uintptr_t)WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS, hdr_buf,
|
||||
IMAGE_HEADER_SIZE);
|
||||
magic = *((uint32_t*)hdr_buf);
|
||||
if (magic != WOLFBOOT_MAGIC) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return hdr_buf;
|
||||
#else
|
||||
uint8_t* hdr = (uint8_t*)WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS;
|
||||
uint32_t magic = *((uint32_t*)hdr);
|
||||
|
||||
if (magic != WOLFBOOT_MAGIC) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return hdr;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t wolfBoot_get_self_version(void)
|
||||
{
|
||||
uint8_t* hdr = wolfBoot_get_self_header();
|
||||
if (hdr == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return wolfBoot_get_blob_version(hdr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Get image version for a partition.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include "hal.h"
|
||||
#include "spi_flash.h"
|
||||
#include "target.h"
|
||||
#include "wolfboot/wolfboot.h"
|
||||
|
||||
#include "delta.h"
|
||||
#include "printf.h"
|
||||
|
|
@ -75,6 +76,73 @@ static void RAMFUNCTION wolfBoot_erase_bootloader(void)
|
|||
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WOLFBOOT_SELF_HEADER
|
||||
static void RAMFUNCTION wolfBoot_update_self_header(struct wolfBoot_image* src)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
uint8_t buffer[FLASHBUFFER_SIZE];
|
||||
int dst_ext = 0;
|
||||
uintptr_t dst_int_addr;
|
||||
uintptr_t dst_ext_addr = WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS;
|
||||
dst_int_addr = WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS;
|
||||
|
||||
/* Determine destination flash type */
|
||||
#if defined(EXT_FLASH) && defined(WOLFBOOT_SELF_HEADER_EXT)
|
||||
dst_ext = 1;
|
||||
#endif
|
||||
|
||||
#ifdef EXT_FLASH
|
||||
/* Erase the self-header sector - sets all bytes to 0xFF */
|
||||
if (dst_ext) {
|
||||
ext_flash_unlock();
|
||||
ext_flash_erase(dst_ext_addr, WOLFBOOT_SELF_HEADER_SIZE);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
hal_flash_erase(dst_int_addr, WOLFBOOT_SELF_HEADER_SIZE);
|
||||
}
|
||||
|
||||
/* Write only the actual header data (IMAGE_HEADER_SIZE bytes).
|
||||
* Any reserved space beyond IMAGE_HEADER_SIZE remains 0xFF from erase. */
|
||||
while (offset < IMAGE_HEADER_SIZE) {
|
||||
uint32_t chunk = IMAGE_HEADER_SIZE - offset;
|
||||
if (chunk > FLASHBUFFER_SIZE) {
|
||||
chunk = FLASHBUFFER_SIZE;
|
||||
}
|
||||
|
||||
#ifdef EXT_FLASH
|
||||
if (PART_IS_EXT(src)) {
|
||||
ext_flash_check_read((uintptr_t)(src->hdr) + offset, (void*)buffer,
|
||||
chunk);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
memcpy(buffer, (uint8_t*)(src->hdr + offset), chunk);
|
||||
}
|
||||
|
||||
#ifdef EXT_FLASH
|
||||
if (dst_ext) {
|
||||
ext_flash_write(dst_ext_addr + offset, buffer, chunk);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
hal_flash_write(dst_int_addr + offset, buffer, chunk);
|
||||
}
|
||||
|
||||
offset += chunk;
|
||||
}
|
||||
|
||||
#ifdef EXT_FLASH
|
||||
if (dst_ext) {
|
||||
ext_flash_lock();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void RAMFUNCTION wolfBoot_self_update(struct wolfBoot_image *src)
|
||||
{
|
||||
uintptr_t pos = 0;
|
||||
|
|
@ -109,6 +177,11 @@ static void RAMFUNCTION wolfBoot_self_update(struct wolfBoot_image *src)
|
|||
pos += FLASHBUFFER_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WOLFBOOT_SELF_HEADER
|
||||
wolfBoot_update_self_header(src);
|
||||
#endif
|
||||
|
||||
hal_flash_lock();
|
||||
arch_reboot();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -360,6 +360,20 @@ ifeq ($(TARGET),sim)
|
|||
endif
|
||||
endif
|
||||
|
||||
# Self-header verification requires image.c (for verify_integrity/verify_authenticity)
|
||||
# and keystore (for public key lookup), plus wolfcrypt for crypto operations
|
||||
ifeq ($(WOLFBOOT_SELF_HEADER),1)
|
||||
CFLAGS+=-D"WOLFBOOT_SELF_HEADER"
|
||||
CFLAGS+=-I"$(WOLFBOOT_LIB_WOLFSSL)"
|
||||
# Test app needs addtional image, keystore, and crypto objects to do verification
|
||||
APP_OBJS+=../src/image.o ../src/keystore.o
|
||||
# Only add wolfcrypt objs if WOLFHSM blocks won't provide them
|
||||
ifeq ($(WOLFHSM_CLIENT)$(WOLFHSM_SERVER),)
|
||||
# remove duplicates for certain object files
|
||||
APP_OBJS+=$(sort $(WOLFCRYPT_OBJS))
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(EXT_FLASH),1)
|
||||
CFLAGS+=-D"EXT_FLASH=1" -D"PART_UPDATE_EXT=1"
|
||||
ifeq ($(NO_XIP),1)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@
|
|||
#include "target.h"
|
||||
|
||||
#include "wolfboot/wolfboot.h"
|
||||
#ifdef WOLFBOOT_SELF_HEADER
|
||||
#include "image.h"
|
||||
#endif
|
||||
|
||||
#ifdef DUALBANK_SWAP
|
||||
uint32_t hal_sim_get_dualbank_state(void);
|
||||
|
|
@ -119,6 +122,42 @@ int do_cmd(const char *cmd)
|
|||
printf("TLV 0x%x: not found!\r\n", tlv);
|
||||
}
|
||||
}
|
||||
#ifdef WOLFBOOT_SELF_HEADER
|
||||
if (strcmp(cmd, "verify_self") == 0) {
|
||||
struct wolfBoot_image img;
|
||||
int ret;
|
||||
|
||||
printf("=== Self-Header Verification Test ===\n");
|
||||
|
||||
/* Open bootloader image using persisted self-header */
|
||||
ret = wolfBoot_open_self(&img);
|
||||
if (ret != 0) {
|
||||
printf("FAIL: wolfBoot_open_self returned %d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
printf("open_self: OK (fw_size=%u, part=%d)\n", (unsigned)img.fw_size,
|
||||
img.part);
|
||||
|
||||
/* Verify integrity (hash check) */
|
||||
ret = wolfBoot_verify_integrity(&img);
|
||||
if (ret != 0) {
|
||||
printf("FAIL: wolfBoot_verify_integrity returned %d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
printf("verify_integrity: OK\n");
|
||||
|
||||
/* Verify authenticity (signature check) */
|
||||
ret = wolfBoot_verify_authenticity(&img);
|
||||
if (ret != 0) {
|
||||
printf("FAIL: wolfBoot_verify_authenticity returned %d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
printf("verify_authenticity: OK\n");
|
||||
|
||||
printf("=== Self-header verification PASSED ===\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
/* wrong command */
|
||||
return -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -251,6 +251,7 @@ struct cmd_options {
|
|||
int policy_sign;
|
||||
int self_update;
|
||||
int sha_only;
|
||||
int header_only;
|
||||
int encrypt;
|
||||
int hash_algo;
|
||||
int sign;
|
||||
|
|
@ -1113,7 +1114,7 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz,
|
|||
{
|
||||
uint32_t header_idx;
|
||||
uint8_t *header;
|
||||
FILE *f, *f2, *fek, *fef;
|
||||
FILE *f = NULL, *f2 = NULL, *fek = NULL, *fef = NULL;
|
||||
uint32_t fw_version32;
|
||||
struct stat attrib;
|
||||
uint16_t image_type;
|
||||
|
|
@ -1747,20 +1748,26 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz,
|
|||
}
|
||||
fwrite(header, 1, header_idx, f);
|
||||
/* Copy image to output */
|
||||
f2 = fopen(image_file, "rb");
|
||||
pos = 0;
|
||||
while (pos < image_sz) {
|
||||
read_sz = image_sz;
|
||||
if (read_sz > sizeof(buf))
|
||||
read_sz = sizeof(buf);
|
||||
read_sz = (uint32_t)fread(buf, 1, read_sz, f2);
|
||||
if ((read_sz == 0) && (feof(f2)))
|
||||
break;
|
||||
fwrite(buf, 1, read_sz, f);
|
||||
pos += read_sz;
|
||||
if (!CMD.header_only) {
|
||||
f2 = fopen(image_file, "rb");
|
||||
pos = 0;
|
||||
while (pos < image_sz) {
|
||||
read_sz = image_sz;
|
||||
if (read_sz > sizeof(buf)) {
|
||||
read_sz = sizeof(buf);
|
||||
}
|
||||
read_sz = (uint32_t)fread(buf, 1, read_sz, f2);
|
||||
if ((read_sz == 0) && (feof(f2))) {
|
||||
break;
|
||||
}
|
||||
fwrite(buf, 1, read_sz, f);
|
||||
pos += read_sz;
|
||||
}
|
||||
fclose(f2);
|
||||
f2 = NULL;
|
||||
}
|
||||
|
||||
if ((CMD.encrypt != ENC_OFF) && CMD.encrypt_key_file) {
|
||||
if (!CMD.header_only && (CMD.encrypt != ENC_OFF) && CMD.encrypt_key_file) {
|
||||
uint8_t key[ENC_MAX_KEY_SZ], iv[ENC_MAX_IV_SZ];
|
||||
uint8_t enc_buf[ENC_MAX_BLOCK_SZ];
|
||||
int ivSz, keySz, encBlockSz;
|
||||
|
|
@ -1855,8 +1862,12 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz,
|
|||
}
|
||||
printf("Output image(s) successfully created.\n");
|
||||
ret = 0;
|
||||
fclose(f2);
|
||||
fclose(f);
|
||||
if (f2) {
|
||||
fclose(f2);
|
||||
}
|
||||
if (f) {
|
||||
fclose(f);
|
||||
}
|
||||
failure:
|
||||
if (cert_chain)
|
||||
free(cert_chain);
|
||||
|
|
@ -2602,6 +2613,9 @@ int main(int argc, char** argv)
|
|||
CMD.self_update = 1;
|
||||
CMD.partition_id = 0;
|
||||
}
|
||||
else if (strcmp(argv[i], "--header-only") == 0) {
|
||||
CMD.header_only = 1;
|
||||
}
|
||||
else if (strcmp(argv[i], "--id") == 0) {
|
||||
long id = strtol(argv[++i], NULL, 10);
|
||||
if ((id < 0 || id > 15) || ((id == 0) && (argv[i][0] != '0'))) {
|
||||
|
|
@ -2811,9 +2825,12 @@ int main(int argc, char** argv)
|
|||
if (tmpstr) {
|
||||
*tmpstr = '\0'; /* null terminate at last "." */
|
||||
}
|
||||
snprintf(CMD.output_image_file, sizeof(CMD.output_image_file) - 1,
|
||||
"%s_v%s_%s.bin", (char*)buf, CMD.fw_version,
|
||||
CMD.sha_only ? "digest" : "signed");
|
||||
{
|
||||
const char* artifact =
|
||||
CMD.header_only ? "header" : (CMD.sha_only ? "digest" : "signed");
|
||||
snprintf(CMD.output_image_file, sizeof(CMD.output_image_file) - 1,
|
||||
"%s_v%s_%s.bin", (char*)buf, CMD.fw_version, artifact);
|
||||
}
|
||||
|
||||
snprintf(CMD.output_encrypted_image_file,
|
||||
sizeof(CMD.output_encrypted_image_file),
|
||||
|
|
@ -2855,9 +2872,10 @@ int main(int argc, char** argv)
|
|||
"%s_v%s_signed_diff_encrypted.bin",
|
||||
(char*)buf, CMD.fw_version);
|
||||
}
|
||||
printf("Output %6s: %s\n", CMD.sha_only ? "digest" : "image",
|
||||
CMD.output_image_file);
|
||||
if (CMD.encrypt) {
|
||||
printf("Output %6s: %s\n",
|
||||
CMD.header_only ? "header" : (CMD.sha_only ? "digest" : "image"),
|
||||
CMD.output_image_file);
|
||||
if (CMD.encrypt && !CMD.header_only) {
|
||||
printf("Encrypted output: %s\n", CMD.output_encrypted_image_file);
|
||||
}
|
||||
printf("Target partition id : %hu ", CMD.partition_id);
|
||||
|
|
|
|||
|
|
@ -266,6 +266,101 @@ test-sim-self-update: wolfboot.bin FORCE
|
|||
@# Verify dummy payload was written to bootloader region, indicating the self update swapped images as expected
|
||||
$(Q)cmp -n $$(wc -c < dummy_update.bin | awk '{print $$1}') dummy_update.bin internal_flash.dd && echo "=== Self-update test PASSED ==="
|
||||
|
||||
# Test self-header cryptographic verification (hash + signature validation)
|
||||
#
|
||||
# Verifies that an application can cryptographically verify the bootloader using
|
||||
# the persisted self-header. Uses the real wolfboot.bin (not a dummy) so the
|
||||
# header hash matches the actual firmware bytes in flash.
|
||||
#
|
||||
# First triggers a self-update so the bootloader persists its signed header,
|
||||
# then verifies the persisted header matches the signing tool's --header-only
|
||||
# output byte-for-byte, and finally boots into the test-app which calls
|
||||
# open_self/verify_integrity/verify_authenticity to simulate an external entity
|
||||
# veriyfing the bootloader image
|
||||
#
|
||||
test-sim-self-header-verify: wolfboot.bin test-app/image_v1_signed.bin FORCE
|
||||
@echo "=== Simulator Self-Header Verification Test ==="
|
||||
@# Sign real wolfboot.bin as v2 update (header hash will match firmware bytes)
|
||||
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) --wolfboot-update wolfboot.bin $(PRIVATE_KEY) 2
|
||||
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) --wolfboot-update --header-only wolfboot.bin $(PRIVATE_KEY) 2
|
||||
@# Create partition images and assemble flash with pBOOT trigger to initiate self-update
|
||||
$(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_PARTITION_SIZE))) count=1 2>/dev/null | tr '\000' '\377' > update_part.dd
|
||||
$(Q)dd if=wolfboot_v2_signed.bin of=update_part.dd bs=1 conv=notrunc
|
||||
$(Q)printf "pBOOT" | dd of=update_part.dd bs=1 seek=$$(($(WOLFBOOT_PARTITION_SIZE) - 5)) conv=notrunc
|
||||
$(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_PARTITION_SIZE))) count=1 2>/dev/null | tr '\000' '\377' > boot_part.dd
|
||||
$(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_SECTOR_SIZE))) count=1 2>/dev/null | tr '\000' '\377' > erased_sec.dd
|
||||
$(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_SECTOR_SIZE))) count=1 2>/dev/null | tr '\000' '\377' > self_hdr.dd
|
||||
$(Q)$(BINASSEMBLE) internal_flash.dd \
|
||||
0 wolfboot.bin \
|
||||
$$(($(WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS) - $(ARCH_FLASH_OFFSET))) self_hdr.dd \
|
||||
$$(($(WOLFBOOT_PARTITION_BOOT_ADDRESS) - $(ARCH_FLASH_OFFSET))) boot_part.dd \
|
||||
$$(($(WOLFBOOT_PARTITION_UPDATE_ADDRESS) - $(ARCH_FLASH_OFFSET))) update_part.dd \
|
||||
$$(($(WOLFBOOT_PARTITION_SWAP_ADDRESS) - $(ARCH_FLASH_OFFSET))) erased_sec.dd
|
||||
@# Run simulator — triggers self-update, which persists the header to flash
|
||||
$(Q)./wolfboot.elf get_version || true
|
||||
@# Extract the persisted header that the self-update wrote
|
||||
$(Q)dd if=internal_flash.dd bs=1 skip=$$(($(WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS) - $(ARCH_FLASH_OFFSET))) count=$$(($(WOLFBOOT_SECTOR_SIZE))) of=persisted_hdr.dd 2>/dev/null
|
||||
@# Verify persisted header matches the signing tool's --header-only output
|
||||
$(Q)cmp -n 256 persisted_hdr.dd wolfboot_v2_header.bin
|
||||
@# Reassemble flash with test-app in boot partition so the app runs after wolfboot
|
||||
$(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_PARTITION_SIZE))) count=1 2>/dev/null | tr '\000' '\377' > update_part_empty.dd
|
||||
$(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_PARTITION_SIZE))) count=1 2>/dev/null | tr '\000' '\377' > boot_with_app.dd
|
||||
$(Q)dd if=test-app/image_v1_signed.bin of=boot_with_app.dd bs=1 conv=notrunc
|
||||
$(Q)$(BINASSEMBLE) internal_flash.dd \
|
||||
0 wolfboot.bin \
|
||||
$$(($(WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS) - $(ARCH_FLASH_OFFSET))) persisted_hdr.dd \
|
||||
$$(($(WOLFBOOT_PARTITION_BOOT_ADDRESS) - $(ARCH_FLASH_OFFSET))) boot_with_app.dd \
|
||||
$$(($(WOLFBOOT_PARTITION_UPDATE_ADDRESS) - $(ARCH_FLASH_OFFSET))) update_part_empty.dd \
|
||||
$$(($(WOLFBOOT_PARTITION_SWAP_ADDRESS) - $(ARCH_FLASH_OFFSET))) erased_sec.dd
|
||||
@# Boot into test-app which calls verify_self to validate bootloader hash + signature
|
||||
$(Q)./wolfboot.elf verify_self && echo "=== Self-Header Cryptographic Verification PASSED ==="
|
||||
|
||||
# Test self-header cryptographic verification with external self-header storage.
|
||||
#
|
||||
# Same verification chain as test-sim-self-header-verify, but the self-header is
|
||||
# persisted to external flash instead of internal.
|
||||
#
|
||||
test-sim-self-header-ext-verify: wolfboot.bin test-app/image_v1_signed.bin FORCE
|
||||
@echo "=== Simulator External Self-Header Verification Test ==="
|
||||
@# Sign real wolfboot.bin as v2 update (header hash will match firmware bytes)
|
||||
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) --wolfboot-update wolfboot.bin $(PRIVATE_KEY) 2
|
||||
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) --wolfboot-update --header-only wolfboot.bin $(PRIVATE_KEY) 2
|
||||
@# Create partition images and assemble internal + external flash
|
||||
$(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_PARTITION_SIZE))) count=1 2>/dev/null | tr '\000' '\377' > update_part.dd
|
||||
$(Q)dd if=wolfboot_v2_signed.bin of=update_part.dd bs=1 conv=notrunc
|
||||
$(Q)printf "pBOOT" | dd of=update_part.dd bs=1 seek=$$(($(WOLFBOOT_PARTITION_SIZE) - 5)) conv=notrunc
|
||||
$(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_PARTITION_SIZE))) count=1 2>/dev/null | tr '\000' '\377' > boot_part.dd
|
||||
$(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_SECTOR_SIZE))) count=1 2>/dev/null | tr '\000' '\377' > erased_sec.dd
|
||||
$(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_SECTOR_SIZE))) count=1 2>/dev/null | tr '\000' '\377' > self_hdr.dd
|
||||
$(Q)$(BINASSEMBLE) internal_flash.dd \
|
||||
0 wolfboot.bin \
|
||||
$$(($(WOLFBOOT_PARTITION_BOOT_ADDRESS) - $(ARCH_FLASH_OFFSET))) boot_part.dd
|
||||
$(Q)$(BINASSEMBLE) external_flash.dd \
|
||||
0 update_part.dd \
|
||||
$(WOLFBOOT_PARTITION_SIZE) erased_sec.dd \
|
||||
$(WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS) self_hdr.dd
|
||||
@# Run simulator — triggers self-update, which persists the header to external flash
|
||||
$(Q)./wolfboot.elf get_version || true
|
||||
@# Confirm internal flash self-header location remains erased (header went to external only)
|
||||
$(Q)dd if=/dev/zero bs=256 count=1 2>/dev/null | tr '\000' '\377' > erased_256.dd
|
||||
$(Q)dd if=internal_flash.dd bs=1 skip=$$(($(WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS) - $(ARCH_FLASH_OFFSET))) count=256 2>/dev/null | cmp -s erased_256.dd - && echo "=== Internal Self-Header location remains erased PASSED ==="
|
||||
@# Extract persisted header from external flash and verify it matches the signed header
|
||||
$(Q)dd if=external_flash.dd bs=1 skip=$(WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS) count=$$(($(WOLFBOOT_SECTOR_SIZE))) of=persisted_hdr.dd 2>/dev/null
|
||||
$(Q)cmp -n 256 persisted_hdr.dd wolfboot_v2_header.bin
|
||||
@# Reassemble flash with test-app in boot and the persisted external header
|
||||
$(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_PARTITION_SIZE))) count=1 2>/dev/null | tr '\000' '\377' > update_part_empty.dd
|
||||
$(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_PARTITION_SIZE))) count=1 2>/dev/null | tr '\000' '\377' > boot_with_app.dd
|
||||
$(Q)dd if=test-app/image_v1_signed.bin of=boot_with_app.dd bs=1 conv=notrunc
|
||||
$(Q)$(BINASSEMBLE) internal_flash.dd \
|
||||
0 wolfboot.bin \
|
||||
$$(($(WOLFBOOT_PARTITION_BOOT_ADDRESS) - $(ARCH_FLASH_OFFSET))) boot_with_app.dd
|
||||
$(Q)$(BINASSEMBLE) external_flash.dd \
|
||||
0 update_part_empty.dd \
|
||||
$(WOLFBOOT_PARTITION_SIZE) erased_sec.dd \
|
||||
$(WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS) persisted_hdr.dd
|
||||
@# Boot into test-app which calls verify_self to validate bootloader hash + signature
|
||||
$(Q)./wolfboot.elf verify_self && echo "=== External Self-Header Cryptographic Verification PASSED ==="
|
||||
|
||||
# Test bootloader self-update mechanism with external flash
|
||||
test-sim-self-update-ext: wolfboot.bin FORCE
|
||||
@echo "=== Simulator Self-Update Test (External Flash) ==="
|
||||
|
|
|
|||
Loading…
Reference in New Issue