Skip to content

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 at 0x0C000000
  • Flash test-app/image_v1_signed.bin at 0x08040000

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
  • 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>