Using One-Time Programmable (OTP) flash area for keystore
Some microcontrollers provide a special area in flash memory that can only be written once and cannot be erased.
This feature comes particularly handy when you want to store the public keys required to authenticate the firmware update images, which has exactly the same requirements. A public key is a cryptographic key that can be freely distributed and is used to verify the signature of the firmware update image. By storing the public keys in the OTP area, you can ensure that they are immutable and cannot be tampered with.
Compiling wolfBoot to access OTP as keystore
To use the OTP area as a keystore, you need to compile wolfBoot with the FLASH_OTP_KEYSTORE
option enabled. This option is disabled by default, which means that the keystore is incorporated into
the wolfBoot binary itself.
When wolfBoot uses the OTP area as a keystore, it reads the public keys from the OTP area at runtime. The public keys are stored in the OTP area, after an initial 16-byte header that contains the number of keys stored, the size of each key, and other information.
In order for wolfBoot to start authenticating the firmware images at boot and upon update, the public keys must be provisioned to the OTP area in a separate step, as described in the next sections.
Depending on the target device, you can either prepare a binary image of the OTP area content, or use otp-keystore-primer
firmware to directly provision the keys on the target.
Creating an image of the OTP area content
It is possible to create a binary image of the content for the OTP area. The resulting file (otp.bin) can be manually provisioned using any external tool that allows writing to the target OTP area.
To compile the otp-keystore-gen tool using the current keystore content:
make otpgen
And then, to create the image file otp.bin
:
./tools/keytools/otp/otp-keystore-gen
Directly provisioning the public keys to the OTP area (primer)
After enabling the FLASH_OTP_KEYSTORE
option in your .config
file, when you compile wolfBoot by running "make",
an additional application called otp-keystore-primer
is generated under tools/keytools/otp
. This application is used to
provision the public keys to the OTP area. By flashing this application to the microcontroller, the public keys contained
in your keystore (previously generated by keygen
) are written to the OTP area.
The otp-keystore-primer
application is generated with the public keys embedded in it. The keys are retrieved from the keystore.c
file,
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.
After generating a new keystore.c
with the keygen
application, you can generate the otp-keystore-primer
application again, by running make otp
.
[!WARNING] 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, 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, 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.[!CAUTION] Be very careful when using the
otp-keystore-primer
application. Use it at your own risk.
Examples
STM32H5 OTP KeyStore
Example for NULCLEO-STM32H563ZI with TrustZone (via PKCS11), DualBank and signing with PQ LMS:
1) Setup the configuration and key tools:
cp config/examples/stm32h5-tz-dualbank-otp-lms.config .config
make include/target.h
make keytools
2) Generate key(s) to write to OTP
./examples/keytools/keygen --lms -g 1.key -g 2.key -g 3.key -g 4.key -g 5.key
3) Backup the generated keys and src/keystore.c
- Save to safe place outside of the wolfBoot tree
4) Set the signing key to use
- Copy one of the generated keys to
wolfboot_signing_private_key.der
cp 1.key wolfboot_signing_private_key.der
5) Setup the OTP keystore
Flash the OTP keystore primer:
- Run make otp
- Flash ./tools/keytools/otp/otp-keystore-primer.bin
to 0x08000000
- Disconnect the tool and hit reset button
- The primer will run and flash keystore.c to OTP and enable write protection on those blocks
OR
Generate OTP (otp.bin) and flash using external tool
- Run make otpgen
- Run ./tools/keytools/otp/otp-keystore-gen
to generate an otp.bin file
- Program otp.bin to 0x08FFF000
using external tool like STM32CubeProgrammer
6) Verify OTP keystore
- Read memory at address 0x08FFF000
(should start with ASCII "WOLFBOOT")
- Typically use STM32CubeProgrammer for this
7) Setup the option bytes - User Configuration 2 -> TrustZone Enable (TZEN=0xB4) - Bank1 - Flash Watermark area (SECWM1_START=0x00, SECWM1_END=0x1F) - Bank2 - Flash Watermark area (SECWM2_START=0x00, SECWM2_END=0x1F)
8) Mass erase the device - STM32CubeProgrammer -> Full chip erase
9) Build wolfBoot and test application using make
10) Flash wolfBoot and test-app
- Flash
wolfboot.bin
at0x0C000000
- Flash
test-app/image_v1_signed.bin
at0x08040000
11) Disconnect and reboot, the red LED should turn on.
12) Connect to USB UART on NUCLEO board for console
Explore the command line (run help)
========================
STM32H5 wolfBoot demo Application
Copyright 2024 wolfSSL Inc
GPL v3
Version : 0x1
========================
cmd> help
help : shows this help message
info : display information about the system and partitions
success : confirm a successful update
pkcs11 : enable and test crypto calls with PKCS11 in secure mode
random : generate a random number
timestamp : print the current timestamp
benchmark : run the wolfCrypt benchmark
test : run the wolfCrypt test
update : update the firmware via XMODEM
reboot : reboot the system
13) Test Update
- Sign a new version of the firmware:
./tools/keytools/sign --lms test-app/image.bin wolfboot_signing_private_key.der 2
- Run "update" command on the shell and wait for xmodem transfer
- Use serial terminal that supports xmodem like "minicom" or "CoolTerm".
- Run
minicom
on/dev/ttyACM0
and start file transfer using "CTRL+A; S" - Select xmodem then navigate to the new signed firmware file
test-app/image_v2_signed.bin
- Run
- During the transfer, the yellow LED will flash.
- The green LED is dim because it's sync with the UART RX
- At the end of the transfer, the new image will be in the update partition.
- Reset board to install new firmware and confirm new version number.
Example update output:
cmd> update
Erasing update partition...Done.
Waiting for XMODEM transfer...
.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
End of transfer. ret: 0
New firmware version: 0x2
Triggering update...
Update completed successfully.
cmd> reboot
========================
STM32H5 wolfBoot demo Application
Copyright 2024 wolfSSL Inc
GPL v3
Version : 0x2
========================
cmd>