Skip to content

Commit

Permalink
Merge pull request wolfSSL#449 from danielinux/stm32h7_otp
Browse files Browse the repository at this point in the history
Support for OTP Flash as trust anchor for keystore
  • Loading branch information
dgarske authored May 24, 2024
2 parents 2d699b4 + 8834e34 commit 971cbe0
Show file tree
Hide file tree
Showing 26 changed files with 1,497 additions and 309 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/test-configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,12 @@ jobs:
arch: arm
config-file: ./config/examples/stm32h5-wolfcrypt-tz.config

stm32h5_tz_dualbank_otp:
uses: ./.github/workflows/test-build.yml
with:
arch: arm
config-file: ./config/examples/stm32h5-tz-dualbank-otp.config

stm32h7_test:
uses: ./.github/workflows/test-build.yml
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ tools/keytools/keygen.exe
tools/keytools/x64
tools/keytools/Debug
tools/keytools/Release
tools/keytools/otp/otp-keystore-primer

# delta binaries
tools/delta/bmdiff
Expand Down
55 changes: 40 additions & 15 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,20 @@ SIGN_ALG=
OBJCOPY_FLAGS=

OBJS:= \
./hal/$(TARGET).o \
./src/string.o \
./src/image.o \
./src/libwolfboot.o
./src/libwolfboot.o \
./hal/$(TARGET).o

ifeq ($(SIGN),NONE)
PRIVATE_KEY=
else
PRIVATE_KEY=wolfboot_signing_private_key.der
OBJS+=./src/keystore.o
ifeq ($(FLASH_OTP_KEYSTORE),1)
OBJS+=./src/flash_otp_keystore.o
else
OBJS+=./src/keystore.o
endif
endif

WOLFCRYPT_OBJS:=
Expand Down Expand Up @@ -81,46 +85,50 @@ TARGET_H_TEMPLATE:=include/target.h.in
ifeq ($(TZEN),1)
ifeq ($(TARGET),stm32l5)
# Don't build a contiguous image
MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin
MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin
endif

ifeq ($(TARGET),stm32u5)
# Don't build a contiguous image
MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin
MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin
endif

ifeq ($(TARGET),stm32h5)
# Don't build a contiguous image
MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin
MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin
endif
endif # TZEN=1

ifeq ($(TARGET),x86_64_efi)
MAIN_TARGET:=wolfboot.efi
MAIN_TARGET:=wolfboot.efi
endif

ifeq ($(FSP), 1)
MAIN_TARGET:=wolfboot_stage1.bin
MAIN_TARGET:=wolfboot_stage1.bin
endif

ifeq ($(TARGET),library)
CFLAGS+=-g
MAIN_TARGET:=test-lib
CFLAGS+=-g
MAIN_TARGET:=test-lib
endif

ifeq ($(TARGET),raspi3)
MAIN_TARGET:=wolfboot.bin
MAIN_TARGET:=wolfboot.bin
endif

ifeq ($(TARGET),sim)
MAIN_TARGET:=wolfboot.bin tools/bin-assemble/bin-assemble test-app/image_v1_signed.bin internal_flash.dd
MAIN_TARGET:=wolfboot.bin tools/bin-assemble/bin-assemble test-app/image_v1_signed.bin internal_flash.dd
endif

ifeq ($(TARGET),nxp_p1021)
MAIN_TARGET:=factory_wstage1.bin
MAIN_TARGET:=factory_wstage1.bin
endif
ifeq ($(TARGET),nxp_t1024)
MAIN_TARGET:=factory_wstage1.bin
MAIN_TARGET:=factory_wstage1.bin
endif

ifeq ($(FLASH_OTP_KEYSTORE),1)
MAIN_TARGET+=tools/keytools/otp/otp-keystore-primer.bin
endif

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


include tools/test.mk
include tools/test-enc.mk
include tools/test-delta.mk
include tools/test-renode.mk

hal/$(TARGET).o:

keytools_check: keytools FORCE

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

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

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

src/keystore.c: $(PRIVATE_KEY)

flash_keystore: src/flash_otp_keystore.o

src/flash_otp_keystore.o: $(PRIVATE_KEY) src/flash_otp_keystore.c
$(Q)$(MAKE) src/keystore.c
$(Q)$(CC) -c $(CFLAGS) src/flash_otp_keystore.c -o $(@)

keys: $(PRIVATE_KEY)

clean:
Expand All @@ -298,6 +316,7 @@ utilsclean: clean
$(Q)$(MAKE) -C tools/test-update-server -s clean
$(Q)$(MAKE) -C tools/uart-flash-server -s clean
$(Q)$(MAKE) -C tools/unit-tests -s clean
$(Q)$(MAKE) -C tools/keytools/otp -s clean

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

otp: tools/keytools/otp/otp-keystore-primer.bin FORCE

tools/keytools/otp/otp-keystore-primer.bin: FORCE
make -C tools/keytools/otp clean
make -C tools/keytools/otp

%.o:%.c
@echo "\t[CC-$(ARCH)] $@"
$(Q)$(CC) $(CFLAGS) -c $(OUTPUT_FLAG) $@ $^
Expand Down
30 changes: 30 additions & 0 deletions config/examples/stm32h5-tz-dualbank-otp.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
ARCH?=ARM
TZEN?=1
TARGET?=stm32h5
SIGN?=ECC256
HASH?=SHA256
DEBUG?=0
VTOR?=1
CORTEX_M0?=0
CORTEX_M33?=1
NO_ASM?=0
NO_MPU=1
EXT_FLASH?=0
SPI_FLASH?=0
ALLOW_DOWNGRADE?=0
NVM_FLASH_WRITEONCE?=1
WOLFBOOT_VERSION?=1
V?=0
SPMATH?=1
RAM_CODE?=0
DUALBANK_SWAP?=1
WOLFBOOT_PARTITION_SIZE?=0xC0000
WOLFBOOT_SECTOR_SIZE?=0x2000
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x08040000
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x8140000
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0xFFFFFFFF
FLAGS_HOME=0
DISABLE_BACKUP=0
FLASH_OTP_KEYSTORE=1
WOLFCRYPT_TZ=1
WOLFCRYPT_TZ_PKCS11=1
5 changes: 5 additions & 0 deletions docs/Targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,11 @@ arm-none-eabi-gdb
Like [STM32L5](#stm32l5) and [STM32U5](#stm32u5), STM32H5 support is also demonstrated
through different scenarios.

Additionally, wolfBoot can be compiled with `FLASH_OTP_KEYSTORE` option, to store
the public key(s) used for firmware authentication into a dedicated, one-time
programmable flash area that can be write protected.
For more information, see [/docs/flash-OTP.md](/docs/flash-OTP.md).

### Scenario 1: TrustZone enabled, staging non-secure application

#### Example description
Expand Down
4 changes: 4 additions & 0 deletions docs/compile.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,10 @@ You can also manually override the fill bytes using `FILL_BYTE=` at build-time.

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.

### Using One-time programmable (OTP) flash as keystore

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.
For more information, see [/docs/OTP-keystore.md](/docs/OTP-keystore.md).

### Using Mac OS/X

Expand Down
44 changes: 44 additions & 0 deletions docs/flash-OTP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
## 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 section.

### Provisioning the public keys to the OTP area

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. **
71 changes: 56 additions & 15 deletions hal/stm32_tz.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,29 +60,67 @@ static void RAMFUNCTION hal_flash_nonsecure_lock(void)
FLASH_NS_CR |= FLASH_CR_LOCK;
}

static int is_range_nonsecure(uint32_t address, int len)
{
#ifndef DUALBANK_SWAP
/* The non secure area begins at the BOOT partition */
uint32_t min = WOLFBOOT_PARTITION_BOOT_ADDRESS;
uint32_t max = FLASH_TOP + 1;
uint32_t end;
if (len < 0)
return 0;
end = (uint32_t)(address + len);
if ((address >= min) && (end <= max))
return 1;
return 0;
#else
/* In this case, the secure area is in the lower side of both banks. */
uint32_t boot_offset = WOLFBOOT_PARTITION_BOOT_ADDRESS - ARCH_FLASH_OFFSET;
uint32_t min1 = WOLFBOOT_PARTITION_BOOT_ADDRESS;
uint32_t max1 = FLASH_BANK2_BASE + 1;
uint32_t min2 = WOLFBOOT_PARTITION_UPDATE_ADDRESS;
uint32_t max2 = FLASH_TOP + 1;
uint32_t end;
if (len < 0)
return 0;
end = (uint32_t)(address + len);
if (((address >= min1) && (end <= max1)) ||
((address >= min2) && (end <= max2)) )
return 1;
return 0;
#endif
}


void hal_tz_claim_nonsecure_area(uint32_t address, int len)
{
int page_n, reg_idx;
uint32_t reg;
uint32_t end = address + len;
uint32_t bank = 0;
int pos;


if (address < FLASH_BANK2_BASE)
if (!is_range_nonsecure(address, len))
return;
if (end > (FLASH_TOP + 1))
return;

hal_flash_wait_complete(0);
hal_flash_clear_errors(0);
while (address < end) {
page_n = (address - FLASH_BANK2_BASE) / FLASH_PAGE_SIZE;
if (address < FLASH_BANK2_BASE) {
page_n = (address - ARCH_FLASH_OFFSET) / FLASH_PAGE_SIZE;
bank = 1;
} else {
page_n = (address - FLASH_BANK2_BASE) / FLASH_PAGE_SIZE;
bank = 2;
}
reg_idx = page_n / 32;
int pos;
pos = page_n % 32;
hal_flash_wait_complete(bank);
hal_flash_clear_errors(bank);
hal_flash_nonsecure_unlock();
FLASH_SECBB2[reg_idx] |= ( 1 << pos);
if (bank == 1)
FLASH_SECBB1[reg_idx] |= ( 1 << pos);
else
FLASH_SECBB2[reg_idx] |= ( 1 << pos);
ISB();
hal_flash_wait_complete(0);
hal_flash_wait_complete(bank);
hal_flash_nonsecure_lock();
/* Erase claimed non-secure page, in secure mode */
#ifndef PLATFORM_stm32h5
Expand All @@ -96,7 +134,7 @@ void hal_tz_claim_nonsecure_area(uint32_t address, int len)
DMB();
FLASH_CR |= FLASH_CR_STRT;
ISB();
hal_flash_wait_complete(0);
hal_flash_wait_complete(bank);
address += FLASH_PAGE_SIZE;
}
#ifndef PLATFORM_stm32h5
Expand Down Expand Up @@ -198,15 +236,15 @@ void hal_gtzc_init(void)
void hal_tz_sau_init(void)
{
uint32_t page_n = 0;
/* WIP: SAU is set up before staging */
/* SAU is set up before staging. Set up all areas as secure. */
/* Non-secure callable: NSC functions area */
sau_init_region(0, 0x0C038000, 0x0C040000, 1);

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

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

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

/* Set as non-secure: OTP + RO area */
sau_init_region(6, 0x08FFF000, 0x08FFFFFF, 0);

/* Enable SAU */
SAU_CTRL = SAU_INIT_CTRL_ENABLE;

Expand Down
Loading

0 comments on commit 971cbe0

Please sign in to comment.