Skip to content

Commit 971cbe0

Browse files
authored
Merge pull request #449 from danielinux/stm32h7_otp
Support for OTP Flash as trust anchor for keystore
2 parents 2d699b4 + 8834e34 commit 971cbe0

26 files changed

+1497
-309
lines changed

.github/workflows/test-configs.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,12 @@ jobs:
257257
arch: arm
258258
config-file: ./config/examples/stm32h5-wolfcrypt-tz.config
259259

260+
stm32h5_tz_dualbank_otp:
261+
uses: ./.github/workflows/test-build.yml
262+
with:
263+
arch: arm
264+
config-file: ./config/examples/stm32h5-tz-dualbank-otp.config
265+
260266
stm32h7_test:
261267
uses: ./.github/workflows/test-build.yml
262268
with:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ tools/keytools/keygen.exe
6969
tools/keytools/x64
7070
tools/keytools/Debug
7171
tools/keytools/Release
72+
tools/keytools/otp/otp-keystore-primer
7273

7374
# delta binaries
7475
tools/delta/bmdiff

Makefile

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,20 @@ SIGN_ALG=
2626
OBJCOPY_FLAGS=
2727

2828
OBJS:= \
29-
./hal/$(TARGET).o \
3029
./src/string.o \
3130
./src/image.o \
32-
./src/libwolfboot.o
31+
./src/libwolfboot.o \
32+
./hal/$(TARGET).o
3333

3434
ifeq ($(SIGN),NONE)
3535
PRIVATE_KEY=
3636
else
3737
PRIVATE_KEY=wolfboot_signing_private_key.der
38-
OBJS+=./src/keystore.o
38+
ifeq ($(FLASH_OTP_KEYSTORE),1)
39+
OBJS+=./src/flash_otp_keystore.o
40+
else
41+
OBJS+=./src/keystore.o
42+
endif
3943
endif
4044

4145
WOLFCRYPT_OBJS:=
@@ -81,46 +85,50 @@ TARGET_H_TEMPLATE:=include/target.h.in
8185
ifeq ($(TZEN),1)
8286
ifeq ($(TARGET),stm32l5)
8387
# Don't build a contiguous image
84-
MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin
88+
MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin
8589
endif
8690

8791
ifeq ($(TARGET),stm32u5)
8892
# Don't build a contiguous image
89-
MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin
93+
MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin
9094
endif
9195

9296
ifeq ($(TARGET),stm32h5)
9397
# Don't build a contiguous image
94-
MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin
98+
MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin
9599
endif
96100
endif # TZEN=1
97101

98102
ifeq ($(TARGET),x86_64_efi)
99-
MAIN_TARGET:=wolfboot.efi
103+
MAIN_TARGET:=wolfboot.efi
100104
endif
101105

102106
ifeq ($(FSP), 1)
103-
MAIN_TARGET:=wolfboot_stage1.bin
107+
MAIN_TARGET:=wolfboot_stage1.bin
104108
endif
105109

106110
ifeq ($(TARGET),library)
107-
CFLAGS+=-g
108-
MAIN_TARGET:=test-lib
111+
CFLAGS+=-g
112+
MAIN_TARGET:=test-lib
109113
endif
110114

111115
ifeq ($(TARGET),raspi3)
112-
MAIN_TARGET:=wolfboot.bin
116+
MAIN_TARGET:=wolfboot.bin
113117
endif
114118

115119
ifeq ($(TARGET),sim)
116-
MAIN_TARGET:=wolfboot.bin tools/bin-assemble/bin-assemble test-app/image_v1_signed.bin internal_flash.dd
120+
MAIN_TARGET:=wolfboot.bin tools/bin-assemble/bin-assemble test-app/image_v1_signed.bin internal_flash.dd
117121
endif
118122

119123
ifeq ($(TARGET),nxp_p1021)
120-
MAIN_TARGET:=factory_wstage1.bin
124+
MAIN_TARGET:=factory_wstage1.bin
121125
endif
122126
ifeq ($(TARGET),nxp_t1024)
123-
MAIN_TARGET:=factory_wstage1.bin
127+
MAIN_TARGET:=factory_wstage1.bin
128+
endif
129+
130+
ifeq ($(FLASH_OTP_KEYSTORE),1)
131+
MAIN_TARGET+=tools/keytools/otp/otp-keystore-primer.bin
124132
endif
125133

126134
ASFLAGS:=$(CFLAGS)
@@ -169,17 +177,21 @@ standalone:
169177
$(Q)$(OBJCOPY) $(OBJCOPY_FLAGS) -O binary test-app/image.elf standalone.bin
170178
$(Q)$(SIZE) test-app/image.elf
171179

180+
172181
include tools/test.mk
173182
include tools/test-enc.mk
174183
include tools/test-delta.mk
175184
include tools/test-renode.mk
176185

186+
hal/$(TARGET).o:
187+
177188
keytools_check: keytools FORCE
178189

179190
$(PRIVATE_KEY):
180191
$(Q)$(MAKE) keytools_check
181192
$(Q)(test $(SIGN) = NONE) || ("$(KEYGEN_TOOL)" $(KEYGEN_OPTIONS) -g $(PRIVATE_KEY)) || true
182193
$(Q)(test $(SIGN) = NONE) && (echo "// SIGN=NONE" > src/keystore.c) || true
194+
$(Q)(test $(FLASH_OTP_KEYSTORE) = 0) || (make -C tools/keytools/otp) || true
183195

184196
keytools: include/target.h
185197
@echo "Building key tools"
@@ -235,7 +247,7 @@ wolfboot_stage1.bin: wolfboot.elf stage1/loader_stage1.bin
235247
$(Q) cp stage1/loader_stage1.bin wolfboot_stage1.bin
236248

237249
wolfboot.elf: include/target.h $(LSCRIPT) $(OBJS) $(LIBS) $(BINASSEMBLE) FORCE
238-
$(Q)(test $(SIGN) = NONE) || (grep -q $(SIGN_ALG) src/keystore.c) || \
250+
$(Q)(test $(SIGN) = NONE) || (test $(FLASH_OTP_KEYSTORE) = 1) || (grep -q $(SIGN_ALG) src/keystore.c) || \
239251
(echo "Key mismatch: please run 'make distclean' to remove all keys if you want to change algorithm" && false)
240252
@echo "\t[LD] $@"
241253
@echo $(OBJS)
@@ -275,6 +287,12 @@ hex: wolfboot.hex
275287

276288
src/keystore.c: $(PRIVATE_KEY)
277289

290+
flash_keystore: src/flash_otp_keystore.o
291+
292+
src/flash_otp_keystore.o: $(PRIVATE_KEY) src/flash_otp_keystore.c
293+
$(Q)$(MAKE) src/keystore.c
294+
$(Q)$(CC) -c $(CFLAGS) src/flash_otp_keystore.c -o $(@)
295+
278296
keys: $(PRIVATE_KEY)
279297

280298
clean:
@@ -298,6 +316,7 @@ utilsclean: clean
298316
$(Q)$(MAKE) -C tools/test-update-server -s clean
299317
$(Q)$(MAKE) -C tools/uart-flash-server -s clean
300318
$(Q)$(MAKE) -C tools/unit-tests -s clean
319+
$(Q)$(MAKE) -C tools/keytools/otp -s clean
301320

302321
keysclean: clean
303322
$(Q)rm -f *.pem *.der tags ./src/*_pub_key.c ./src/keystore.c include/target.h
@@ -355,6 +374,12 @@ cppcheck:
355374
--suppress="objectIndex" --suppress="comparePointers" \
356375
--error-exitcode=89 --std=c89 src/*.c hal/*.c hal/spi/*.c hal/uart/*.c
357376

377+
otp: tools/keytools/otp/otp-keystore-primer.bin FORCE
378+
379+
tools/keytools/otp/otp-keystore-primer.bin: FORCE
380+
make -C tools/keytools/otp clean
381+
make -C tools/keytools/otp
382+
358383
%.o:%.c
359384
@echo "\t[CC-$(ARCH)] $@"
360385
$(Q)$(CC) $(CFLAGS) -c $(OUTPUT_FLAG) $@ $^
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
ARCH?=ARM
2+
TZEN?=1
3+
TARGET?=stm32h5
4+
SIGN?=ECC256
5+
HASH?=SHA256
6+
DEBUG?=0
7+
VTOR?=1
8+
CORTEX_M0?=0
9+
CORTEX_M33?=1
10+
NO_ASM?=0
11+
NO_MPU=1
12+
EXT_FLASH?=0
13+
SPI_FLASH?=0
14+
ALLOW_DOWNGRADE?=0
15+
NVM_FLASH_WRITEONCE?=1
16+
WOLFBOOT_VERSION?=1
17+
V?=0
18+
SPMATH?=1
19+
RAM_CODE?=0
20+
DUALBANK_SWAP?=1
21+
WOLFBOOT_PARTITION_SIZE?=0xC0000
22+
WOLFBOOT_SECTOR_SIZE?=0x2000
23+
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x08040000
24+
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x8140000
25+
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0xFFFFFFFF
26+
FLAGS_HOME=0
27+
DISABLE_BACKUP=0
28+
FLASH_OTP_KEYSTORE=1
29+
WOLFCRYPT_TZ=1
30+
WOLFCRYPT_TZ_PKCS11=1

docs/Targets.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,11 @@ arm-none-eabi-gdb
850850
Like [STM32L5](#stm32l5) and [STM32U5](#stm32u5), STM32H5 support is also demonstrated
851851
through different scenarios.
852852

853+
Additionally, wolfBoot can be compiled with `FLASH_OTP_KEYSTORE` option, to store
854+
the public key(s) used for firmware authentication into a dedicated, one-time
855+
programmable flash area that can be write protected.
856+
For more information, see [/docs/flash-OTP.md](/docs/flash-OTP.md).
857+
853858
### Scenario 1: TrustZone enabled, staging non-secure application
854859

855860
#### Example description

docs/compile.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,10 @@ You can also manually override the fill bytes using `FILL_BYTE=` at build-time.
279279

280280
Note: if you are using an external FLASH (e.g. SPI) in combination with a flash with inverted logic, ensure that you store all the flags in one partition, by using the `FLAGS_HOME=1` option described above.
281281

282+
### Using One-time programmable (OTP) flash as keystore
283+
284+
By default, keys are directly incorporated in the firmware image. To store the keys in a separate, one-time programmable (OTP) flash memory, use the `FLASH_OTP_KEYSTORE=1` option.
285+
For more information, see [/docs/OTP-keystore.md](/docs/OTP-keystore.md).
282286

283287
### Using Mac OS/X
284288

docs/flash-OTP.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
## Using One-Time Programmable (OTP) flash area for keystore
2+
3+
Some microcontrollers provide a special area in flash memory that can
4+
only be written once and cannot be erased.
5+
6+
This feature comes particularly handy when you want to store the public keys required
7+
to authenticate the firmware update images, which has exactly the same requirements. A public
8+
key is a cryptographic key that can be freely distributed and is used to verify the signature
9+
of the firmware update image. By storing the public keys in the OTP area, you can ensure that
10+
they are immutable and cannot be tampered with.
11+
12+
### Compiling wolfBoot to access OTP as keystore
13+
14+
To use the OTP area as a keystore, you need to compile wolfBoot with the `FLASH_OTP_KEYSTORE`
15+
option enabled. This option is disabled by default, which means that the keystore is incorporated into
16+
the wolfBoot binary itself.
17+
18+
When wolfBoot uses the OTP area as a keystore, it reads the public keys from the OTP area at runtime.
19+
The public keys are stored in the OTP area, after an initial 16-byte header that contains the number of
20+
keys stored, the size of each key, and other information.
21+
22+
In order for wolfBoot to start authenticating the firmware images at boot and upon update, the public keys
23+
must be provisioned to the OTP area in a separate step, as described in the next section.
24+
25+
### Provisioning the public keys to the OTP area
26+
27+
After enabling the `FLASH_OTP_KEYSTORE` option in your `.config` file, when you compile wolfBoot by running "make",
28+
an additional application called `otp-keystore-primer` is generated under `tools/keytools/otp`. This application is used to
29+
provision the public keys to the OTP area. By flashing this application to the microcontroller, the public keys contained
30+
in your keystore (previously generated by `keygen`) are written to the OTP area.
31+
32+
The `otp-keystore-primer` application is generated with the public keys embedded in it. The keys are retrieved from the `keystore.c` file,
33+
generated by the `keygen` command. The `otp-keystore-primer` application reads the public keys from the `keystore.c` file and writes them to the OTP area.
34+
35+
After generating a new `keystore.c` with the `keygen` application, you can generate the `otp-keystore-primer` application again, by running `make otp`.
36+
37+
> [!WARNING]
38+
> The `otp-keystore-primer` application is a one-time use application. Once the application runs on your target, the public keys are written to the OTP area,
39+
> and it will be impossible to erase them. Therefore, it is important to ensure that the public keys are correct before provisioning them to the OTP area,
40+
> and that the associated private keys are stored securely. Accidentally losing the private keys will render the public keys stored in the OTP area useless.
41+
42+
> [!CAUTION]
43+
> ** Be very careful when using the `otp-keystore-primer` application. Use it at your own risk. **
44+

hal/stm32_tz.c

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60,29 +60,67 @@ static void RAMFUNCTION hal_flash_nonsecure_lock(void)
6060
FLASH_NS_CR |= FLASH_CR_LOCK;
6161
}
6262

63+
static int is_range_nonsecure(uint32_t address, int len)
64+
{
65+
#ifndef DUALBANK_SWAP
66+
/* The non secure area begins at the BOOT partition */
67+
uint32_t min = WOLFBOOT_PARTITION_BOOT_ADDRESS;
68+
uint32_t max = FLASH_TOP + 1;
69+
uint32_t end;
70+
if (len < 0)
71+
return 0;
72+
end = (uint32_t)(address + len);
73+
if ((address >= min) && (end <= max))
74+
return 1;
75+
return 0;
76+
#else
77+
/* In this case, the secure area is in the lower side of both banks. */
78+
uint32_t boot_offset = WOLFBOOT_PARTITION_BOOT_ADDRESS - ARCH_FLASH_OFFSET;
79+
uint32_t min1 = WOLFBOOT_PARTITION_BOOT_ADDRESS;
80+
uint32_t max1 = FLASH_BANK2_BASE + 1;
81+
uint32_t min2 = WOLFBOOT_PARTITION_UPDATE_ADDRESS;
82+
uint32_t max2 = FLASH_TOP + 1;
83+
uint32_t end;
84+
if (len < 0)
85+
return 0;
86+
end = (uint32_t)(address + len);
87+
if (((address >= min1) && (end <= max1)) ||
88+
((address >= min2) && (end <= max2)) )
89+
return 1;
90+
return 0;
91+
#endif
92+
}
93+
94+
6395
void hal_tz_claim_nonsecure_area(uint32_t address, int len)
6496
{
6597
int page_n, reg_idx;
6698
uint32_t reg;
6799
uint32_t end = address + len;
100+
uint32_t bank = 0;
101+
int pos;
68102

69-
70-
if (address < FLASH_BANK2_BASE)
103+
if (!is_range_nonsecure(address, len))
71104
return;
72-
if (end > (FLASH_TOP + 1))
73-
return;
74-
75-
hal_flash_wait_complete(0);
76-
hal_flash_clear_errors(0);
77105
while (address < end) {
78-
page_n = (address - FLASH_BANK2_BASE) / FLASH_PAGE_SIZE;
106+
if (address < FLASH_BANK2_BASE) {
107+
page_n = (address - ARCH_FLASH_OFFSET) / FLASH_PAGE_SIZE;
108+
bank = 1;
109+
} else {
110+
page_n = (address - FLASH_BANK2_BASE) / FLASH_PAGE_SIZE;
111+
bank = 2;
112+
}
79113
reg_idx = page_n / 32;
80-
int pos;
81114
pos = page_n % 32;
115+
hal_flash_wait_complete(bank);
116+
hal_flash_clear_errors(bank);
82117
hal_flash_nonsecure_unlock();
83-
FLASH_SECBB2[reg_idx] |= ( 1 << pos);
118+
if (bank == 1)
119+
FLASH_SECBB1[reg_idx] |= ( 1 << pos);
120+
else
121+
FLASH_SECBB2[reg_idx] |= ( 1 << pos);
84122
ISB();
85-
hal_flash_wait_complete(0);
123+
hal_flash_wait_complete(bank);
86124
hal_flash_nonsecure_lock();
87125
/* Erase claimed non-secure page, in secure mode */
88126
#ifndef PLATFORM_stm32h5
@@ -96,7 +134,7 @@ void hal_tz_claim_nonsecure_area(uint32_t address, int len)
96134
DMB();
97135
FLASH_CR |= FLASH_CR_STRT;
98136
ISB();
99-
hal_flash_wait_complete(0);
137+
hal_flash_wait_complete(bank);
100138
address += FLASH_PAGE_SIZE;
101139
}
102140
#ifndef PLATFORM_stm32h5
@@ -198,15 +236,15 @@ void hal_gtzc_init(void)
198236
void hal_tz_sau_init(void)
199237
{
200238
uint32_t page_n = 0;
201-
/* WIP: SAU is set up before staging */
239+
/* SAU is set up before staging. Set up all areas as secure. */
202240
/* Non-secure callable: NSC functions area */
203241
sau_init_region(0, 0x0C038000, 0x0C040000, 1);
204242

205243
/* Non-Secure: application flash area (first bank) */
206-
sau_init_region(1, 0x08040000, 0x080FFFFF, 0);
244+
sau_init_region(1, WOLFBOOT_PARTITION_BOOT_ADDRESS, FLASH_BANK2_BASE - 1, 0);
207245

208246
/* Non-Secure: application flash area (second bank) */
209-
sau_init_region(2, 0x08140000, 0x081FFFFF, 0);
247+
sau_init_region(2, WOLFBOOT_PARTITION_UPDATE_ADDRESS, FLASH_TOP -1, 0);
210248

211249
/* Secure RAM regions in SRAM1/SRAM2 */
212250
sau_init_region(3, 0x30000000, 0x3004FFFF, 1);
@@ -217,6 +255,9 @@ void hal_tz_sau_init(void)
217255
/* Non-secure: internal peripherals */
218256
sau_init_region(5, 0x40000000, 0x4FFFFFFF, 0);
219257

258+
/* Set as non-secure: OTP + RO area */
259+
sau_init_region(6, 0x08FFF000, 0x08FFFFFF, 0);
260+
220261
/* Enable SAU */
221262
SAU_CTRL = SAU_INIT_CTRL_ENABLE;
222263

0 commit comments

Comments
 (0)