Skip to content

Commit 4d31ef5

Browse files
bigbrettdanielinux
authored andcommitted
Add self-header feature with support for sim and AURIX TC3xx
1 parent 9397988 commit 4d31ef5

21 files changed

+718
-26
lines changed

.github/workflows/test-sim-self-update.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Simulator self-update test
1+
name: Simulator self-update and self-header tests
22

33
on:
44
push:
@@ -71,3 +71,15 @@ jobs:
7171
make clean
7272
cp config/examples/sim-self-update-ext.config .config
7373
make test-sim-self-update-ext
74+
75+
- name: Run self-header verification test (internal flash)
76+
run: |
77+
make clean
78+
cp config/examples/sim-self-header.config .config
79+
make test-sim-self-header-verify
80+
81+
- name: Run self-header verification test (external flash)
82+
run: |
83+
make clean
84+
cp config/examples/sim-self-header-ext.config .config
85+
make test-sim-self-header-ext-verify

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,8 @@ include/target.h: $(TARGET_H_TEMPLATE) FORCE
603603
sed -e "s/@WOLFBOOT_DTS_BOOT_ADDRESS@/$(WOLFBOOT_DTS_BOOT_ADDRESS)/g" | \
604604
sed -e "s/@WOLFBOOT_DTS_UPDATE_ADDRESS@/$(WOLFBOOT_DTS_UPDATE_ADDRESS)/g" | \
605605
sed -e "s/@WOLFBOOT_LOAD_ADDRESS@/$(WOLFBOOT_LOAD_ADDRESS)/g" | \
606-
sed -e "s/@WOLFBOOT_LOAD_DTS_ADDRESS@/$(WOLFBOOT_LOAD_DTS_ADDRESS)/g" \
606+
sed -e "s/@WOLFBOOT_LOAD_DTS_ADDRESS@/$(WOLFBOOT_LOAD_DTS_ADDRESS)/g" | \
607+
sed -e "s/@WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS@/$(WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS)/g" \
607608
> $@
608609

609610
delta: tools/delta/bmdiff
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
ARCH?=AURIX_TC3
2+
TARGET?=aurix_tc3xx
3+
AURIX_TC3_HSM=1
4+
SIGN?=RSA4096
5+
HASH?=SHA256
6+
DEBUG?=0
7+
WOLFBOOT_VERSION?=1
8+
V?=0
9+
SPMATH?=1
10+
RAM_CODE?=1
11+
EXT_FLASH?=1
12+
EXT_BOOT=1
13+
EXT_UPDATE=1
14+
EXT_SWAP=1
15+
FLAGS_INVERT=1
16+
FLASH_MULTI_SECTOR_ERASE=1
17+
DEBUG_UART=1
18+
PRINTF_ENABLED=1
19+
20+
# wolfHSM options
21+
WOLFHSM_SERVER=1
22+
23+
# Cert chain options
24+
CERT_CHAIN_VERIFY=1
25+
26+
# RSA4096 cert chains need the larger header and stack
27+
WOLFBOOT_HUGE_STACK=1
28+
IMAGE_HEADER_SIZE=4096
29+
30+
# self-header feature (persist header in external flash)
31+
WOLFBOOT_SELF_HEADER=1
32+
SELF_HEADER_EXT=1
33+
34+
ARCH_FLASH_OFFSET=0x80028000
35+
WOLFBOOT_SECTOR_SIZE=0x4000
36+
WOLFBOOT_PARTITION_SIZE=0x30000
37+
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80038000
38+
WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x80068000
39+
WOLFBOOT_PARTITION_SWAP_ADDRESS=0x80098000
40+
WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS=0x8009C000
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
ARCH=sim
2+
TARGET=sim
3+
SIGN?=ED25519
4+
HASH?=SHA256
5+
WOLFBOOT_SMALL_STACK?=0
6+
SPI_FLASH=0
7+
EXT_FLASH=1
8+
DEBUG=1
9+
RAM_CODE=1
10+
WOLFBOOT_VERSION=1
11+
12+
# Partition size increased to accommodate test-app with verify APIs
13+
WOLFBOOT_PARTITION_SIZE=0x80000
14+
WOLFBOOT_SECTOR_SIZE=0x1000
15+
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80000
16+
# Update and swap on external flash (address 0x00000)
17+
WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x00000
18+
WOLFBOOT_PARTITION_SWAP_ADDRESS=0x80000
19+
20+
# Self-header feature (persist header in external flash)
21+
WOLFBOOT_SELF_HEADER=1
22+
SELF_HEADER_EXT=1
23+
WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS=0x90000
24+
25+
# Required for keytools
26+
WOLFBOOT_FIXED_PARTITIONS=1
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
ARCH=sim
2+
TARGET=sim
3+
SIGN?=ED25519
4+
HASH?=SHA256
5+
WOLFBOOT_SMALL_STACK?=0
6+
SPI_FLASH=0
7+
DEBUG=1
8+
RAM_CODE=1
9+
WOLFBOOT_VERSION=1
10+
11+
# Partition size increased to 512KB to accommodate test-app with verify APIs
12+
WOLFBOOT_PARTITION_SIZE=0x80000
13+
WOLFBOOT_SECTOR_SIZE=0x1000
14+
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80000
15+
WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x100000
16+
WOLFBOOT_PARTITION_SWAP_ADDRESS=0x180000
17+
18+
WOLFBOOT_FIXED_PARTITIONS=1
19+
20+
# Self-header feature
21+
WOLFBOOT_SELF_HEADER=1
22+
WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS=0x40000

docs/Signing.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,23 @@ the bootloader itself is stored.
165165
* `--wolfboot-update` Indicate that the image contains a signed self-update
166166
package for the bootloader. Equivalent to `--id 0`.
167167

168+
#### Header-only output (wolfBoot self header)
169+
170+
Use `--header-only` to emit only the manifest header without copying the
171+
firmware bytes into the output file. This is useful when persisting
172+
wolfBoot's own header at a separate flash address for external measurement:
173+
174+
```
175+
$ tools/keytools/sign --wolfboot-update --header-only wolfboot.bin key.der 1
176+
# Produces wolfboot_v1_header.bin (header only)
177+
$ tools/keytools/sign --wolfboot-update wolfboot.bin key.der 1
178+
# Produces wolfboot_v1_signed.bin (header + firmware)
179+
```
180+
181+
For complete documentation of the self-header feature — including
182+
configuration, update flow, and runtime verification — see
183+
[firmware_update.md](firmware_update.md#self-header-persisting-the-bootloader-manifest).
184+
168185
#### Encryption using a symmetric key
169186

170187
Although signed to be authenticated, by default the image is not encrypted and

docs/firmware_image.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,14 @@ In order to boot a different image, wolfBoot will have to swap the content of th
111111

112112
For more information on how firmware images are stored and managed within the two partitions, see [Flash partitions](flash_partitions.md)
113113

114+
The same manifest header format is also used by the **self-header** feature
115+
(`WOLFBOOT_SELF_HEADER`), where a copy of the bootloader's manifest header is
116+
persisted at a separate flash address. This allows external components to
117+
cryptographically verify the bootloader's authenticity and version using the
118+
standard wolfBoot verification APIs. See
119+
[firmware_update.md](firmware_update.md#self-header-persisting-the-bootloader-manifest)
120+
for full details.
121+
114122

115123

116124

docs/firmware_update.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,125 @@ rebooting.
8181
wolfBoot can be used to deploy new bootloader versions as well as
8282
update keys.
8383

84+
#### Self-header: persisting the bootloader manifest
85+
86+
In a typical wolfBoot deployment the bootloader verifies the application
87+
firmware, but no entity verifies the bootloader itself. When an external
88+
component — such as a [wolfHSM](wolfHSM.md) server, or another
89+
secure co-processor — needs to measure and authenticate the running
90+
bootloader, it must be able to read the bootloader's signed manifest
91+
header independently. The **self-header** feature makes this possible by
92+
persisting a copy of the bootloader's manifest header at a dedicated,
93+
fixed flash address after every self-update.
94+
95+
This completes the chain of trust:
96+
97+
```
98+
External verifier --verifies--> wolfBoot -->verifies--> Application
99+
(wolfHSM/TPM) (self-header) (BOOT partition)
100+
```
101+
102+
##### Configuration options
103+
104+
| Build option | Required | Description |
105+
|---|---|---|
106+
| `WOLFBOOT_SELF_HEADER=1` | Yes | Enable the self-header feature |
107+
| `WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS=<addr>` | Yes | Flash address where the header is stored. **Must be sector-aligned.** |
108+
| `WOLFBOOT_SELF_HEADER_SIZE=<size>` | No | Erase span at the header address. Defaults to `IMAGE_HEADER_SIZE`. Must be ≥ `IMAGE_HEADER_SIZE`. |
109+
| `SELF_HEADER_EXT=1` | No | Store the header in external flash. Requires `EXT_FLASH=1`. |
110+
111+
##### Flash layout
112+
113+
The self-header occupies its own region in the flash map, separate from
114+
the bootloader binary and the firmware partitions:
115+
116+
```
117+
Internal flash (example)
118+
┌─────────────────────┐ 0x00000
119+
│ wolfBoot │
120+
│ (bootloader) │
121+
├─────────────────────┤ WOLFBOOT_PARTITION_BOOT_ADDRESS
122+
│ BOOT partition │
123+
├─────────────────────┤ WOLFBOOT_PARTITION_UPDATE_ADDRESS
124+
│ UPDATE partition │
125+
├─────────────────────┤ WOLFBOOT_PARTITION_SWAP_ADDRESS
126+
│ SWAP partition │
127+
├─────────────────────┤ WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS
128+
│ Self-header │ (IMAGE_HEADER_SIZE or WOLFBOOT_SELF_HEADER_SIZE)
129+
└─────────────────────┘
130+
```
131+
132+
When `SELF_HEADER_EXT=1` is set the self-header is stored in external
133+
flash instead. See [Flash partitions](flash_partitions.md) for more
134+
detail on the overall partition layout.
135+
136+
##### Update flow
137+
138+
During a self-update with `WOLFBOOT_SELF_HEADER` enabled, the following
139+
steps occur:
140+
141+
1. A new signed bootloader image (created with `--wolfboot-update`) is
142+
placed in the UPDATE partition.
143+
2. The application triggers the update (e.g. `wolfBoot_update_trigger()`).
144+
3. On reboot, wolfBoot validates the new bootloader image — verifying both
145+
integrity and signature.
146+
4. The new bootloader binary is copied to flash, overwriting the old one in-place.
147+
5. **After** the firmware copy completes, the manifest header is written
148+
to `WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS`.
149+
6. The system reboots into the new bootloader.
150+
151+
##### Runtime verification API
152+
153+
Applications and external verifiers can use the following functions
154+
(declared in `include/image.h` and `include/wolfboot/wolfboot.h`) when linking
155+
against wolfBoot as a library to read and verify the persisted self-header:
156+
157+
- **`wolfBoot_get_self_header()`** — returns a pointer to the persisted
158+
header bytes, or `NULL` if the header is missing or invalid.
159+
- **`wolfBoot_get_self_version()`** — returns the version number stored
160+
in the persisted header, or `0` if the header is invalid.
161+
- **`wolfBoot_open_self(struct wolfBoot_image *img)`** — opens the
162+
self-header and populates `img` so that the standard verification
163+
functions can be used on it. Returns `0` on success.
164+
- **`wolfBoot_open_self_address(struct wolfBoot_image *img, uint8_t *hdr, uint8_t *image)`**
165+
— like `wolfBoot_open_self()` but accepts explicit header and firmware
166+
base addresses. Useful for opening any self-header and image combination.
167+
168+
After opening the image with `wolfBoot_open_self()`, the caller can
169+
verify the bootloader using the standard verification functions:
170+
171+
```c
172+
struct wolfBoot_image img;
173+
if (wolfBoot_open_self(&img) == 0) {
174+
wolfBoot_verify_integrity(&img);
175+
wolfBoot_verify_authenticity(&img);
176+
}
177+
```
178+
179+
**NOTE: An application verifying its own integrity and authenticity almost never provides meaningful security.**
180+
181+
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.
182+
This is intended for platforms where the silicon does not support ROM-based verification of a first-stage bootloader.
183+
184+
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.
185+
186+
##### Factory programming
187+
188+
At manufacturing time the self-header must be programmed alongside the
189+
bootloader binary. Use `--header-only` with the sign tool to generate a
190+
standalone header binary:
191+
192+
```
193+
tools/keytools/sign --wolfboot-update --header-only wolfboot.bin key.der 1
194+
```
195+
196+
This produces a `wolfboot_v1_header.bin` containing only the manifest
197+
header. Program it at `WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS` and the
198+
regular signed image at the bootloader origin.
199+
200+
See [Signing.md](Signing.md#header-only-output-wolfboot-self-header) for
201+
more detail on the `--header-only` sign tool option.
202+
84203
### Incremental updates (aka: 'delta' updates)
85204

86205
wolfBoot supports incremental updates, based on a specific older version. The sign tool

docs/flash_partitions.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ The flash memory of the target is partitioned into the following areas:
2424
- Swapping space (SWAP partition) starting at address `WOLFBOOT_PARTITION_SWAP_ADDRESS`
2525
- the swap space size is defined as `WOLFBOOT_SECTOR_SIZE` and must be as big as the
2626
largest sector used in either BOOT/UPDATE partitions.
27+
- (Optional) Self-header partition starting at address `WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS`,
28+
used when `WOLFBOOT_SELF_HEADER=1` is enabled. See [Self-header partition](#self-header-partition) below.
2729

2830
A proper partitioning configuration must be set up for the specific use, by setting
2931
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
3436
Public keys pre-authorized during factory image creations are automatically stored
3537
as part of the firmware image.
3638

39+
### Self-header partition
40+
41+
When `WOLFBOOT_SELF_HEADER=1` is enabled, an additional flash region is
42+
reserved for the bootloader's own signed manifest header. This allows
43+
external components (e.g. wolfHSM server, a secure co-processor, etc.) to read
44+
and cryptographically verify the bootloader.
45+
46+
Configuration:
47+
48+
- `WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS` — the flash address for the
49+
header. **Must be aligned to `WOLFBOOT_SECTOR_SIZE`.**
50+
- `WOLFBOOT_SELF_HEADER_SIZE` — (optional) the erase span at the header
51+
address. Defaults to `IMAGE_HEADER_SIZE` and must be at least
52+
`IMAGE_HEADER_SIZE`.
53+
- `SELF_HEADER_EXT=1` — store the self-header in external flash instead
54+
of internal flash. Requires `EXT_FLASH=1`.
55+
56+
The self-header partition sits alongside the other partitions in the
57+
flash map:
58+
59+
```
60+
┌─────────────────────┐
61+
│ wolfBoot │
62+
├─────────────────────┤ BOOT_ADDRESS
63+
│ BOOT partition │
64+
├─────────────────────┤ UPDATE_ADDRESS
65+
│ UPDATE partition │
66+
├─────────────────────┤ SWAP_ADDRESS
67+
│ SWAP partition │
68+
├─────────────────────┤ SELF_HEADER_ADDRESS
69+
│ Self-header │
70+
└─────────────────────┘
71+
```
72+
73+
The actual placement is flexible — the self-header can be located
74+
anywhere in the flash map as long as it does not overlap with any other
75+
partition and is sector-aligned.
76+
77+
For full details on the self-header feature — including the update flow,
78+
runtime verification API, and factory programming — see
79+
[firmware_update.md](firmware_update.md#self-header-persisting-the-bootloader-manifest).
80+
3781
### BOOT partition
3882

3983
This is the only partition from where it is possible to chain-load and execute a

hal/aurix_tc3xx.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,9 +301,11 @@ void hal_init(void)
301301
#ifdef DEBUG_UART
302302
uart_init();
303303
#ifndef WOLFBOOT_AURIX_TC3XX_HSM
304-
wolfBoot_printf("Hello from TC3xx wolfBoot on Tricore\n");
304+
wolfBoot_printf("Hello from TC3xx wolfBoot on Tricore: V%d\n",
305+
WOLFBOOT_VERSION);
305306
#else
306-
wolfBoot_printf("Hello from TC3xx wolfBoot on HSM\n");
307+
wolfBoot_printf("Hello from TC3xx wolfBoot on HSM: V%d\n",
308+
WOLFBOOT_VERSION);
307309
#endif
308310
#endif /* DEBUG_UART */
309311
}

0 commit comments

Comments
 (0)