Example for wolfCrypt PUF on STM32H5

pull/578/head
David Garske 2026-03-24 16:54:13 -07:00 committed by Daniele Lacamera
parent bfdda5612e
commit 87e913955f
9 changed files with 1169 additions and 0 deletions

2
.gitignore vendored
View File

@ -416,3 +416,5 @@ stsafe/wolfssl_stsafe_full_test
# uefi-library generated filesystem content
uefi-library/efifs
puf/Build

View File

@ -247,6 +247,17 @@ verifying/decrypting operations.
Please see the [pkcs7/README.md](pkcs7/README.md) for further usage and details.
<br />
#### PUF (SRAM Physically Unclonable Function)
This directory contains a bare-metal example demonstrating wolfCrypt's SRAM PUF
support. It derives device-unique cryptographic keys from the power-on state of
SRAM memory using a BCH(127,64,t=10) fuzzy extractor with HKDF key derivation.
Tested on NUCLEO-H563ZI (Cortex-M33).
Please see the [puf/README.md](puf/README.md) for further usage and details.
<br />
#### PSK (Pre-Shared Keys)

98
puf/Makefile 100644
View File

@ -0,0 +1,98 @@
# Makefile for wolfCrypt SRAM PUF bare-metal example
#
# Target: Cortex-M (tested on NUCLEO-H563ZI)
# Usage: make - build puf_example.elf and puf_example.hex
# make clean - remove build artifacts
# make PUF_TEST=0 - build for real hardware SRAM (no test mode)
#
# Copyright (C) 2006-2026 wolfSSL Inc.
# Toolchain
TOOLCHAIN ?= arm-none-eabi-
CC = $(TOOLCHAIN)gcc
LD = $(TOOLCHAIN)gcc
AR = $(TOOLCHAIN)ar
OBJCOPY = $(TOOLCHAIN)objcopy
OBJDUMP = $(TOOLCHAIN)objdump
SIZE = $(TOOLCHAIN)size
NM = $(TOOLCHAIN)nm
# wolfSSL root (relative to this directory)
WOLFSSL_ROOT ?= ../../wolfssl
# Build output
BUILD_DIR = ./Build
BIN = puf_example
# Architecture
ARCHFLAGS = -mcpu=cortex-m33 -mthumb -mabi=aapcs
# Compiler flags
CFLAGS = $(ARCHFLAGS) -std=gnu99 -Wall -Os
CFLAGS += -ffunction-sections -fdata-sections -fno-builtin
CFLAGS += -DWOLFSSL_USER_SETTINGS
CFLAGS += -I. -I$(WOLFSSL_ROOT)
# Linker flags
LDFLAGS = $(ARCHFLAGS)
LDFLAGS += --specs=nosys.specs --specs=nano.specs
LDFLAGS += -Wl,--gc-sections
LDFLAGS += -Wl,-Map=$(BUILD_DIR)/$(BIN).map
LDFLAGS += -T./linker.ld
# Math lib
LIBS = -lm
# Source files
SRC_C = main.c
SRC_C += startup.c
SRC_C += stm32.c
# wolfCrypt sources needed for PUF
SRC_C += $(WOLFSSL_ROOT)/wolfcrypt/src/puf.c
SRC_C += $(WOLFSSL_ROOT)/wolfcrypt/src/sha256.c
SRC_C += $(WOLFSSL_ROOT)/wolfcrypt/src/kdf.c
SRC_C += $(WOLFSSL_ROOT)/wolfcrypt/src/hmac.c
SRC_C += $(WOLFSSL_ROOT)/wolfcrypt/src/hash.c
SRC_C += $(WOLFSSL_ROOT)/wolfcrypt/src/memory.c
SRC_C += $(WOLFSSL_ROOT)/wolfcrypt/src/wc_port.c
SRC_C += $(WOLFSSL_ROOT)/wolfcrypt/src/error.c
SRC_C += $(WOLFSSL_ROOT)/wolfcrypt/src/misc.c
SRC_C += $(WOLFSSL_ROOT)/wolfcrypt/src/logging.c
SRC_C += $(WOLFSSL_ROOT)/wolfcrypt/src/random.c
SRC_C += $(WOLFSSL_ROOT)/wolfcrypt/src/sp_int.c
SRC_C += $(WOLFSSL_ROOT)/wolfcrypt/src/sha3.c
# Object files
FILENAMES_C = $(notdir $(SRC_C))
OBJS_C = $(addprefix $(BUILD_DIR)/, $(FILENAMES_C:.c=.o))
vpath %.c $(dir $(SRC_C))
# Targets
.PHONY: all clean
all: $(BUILD_DIR) $(BUILD_DIR)/$(BIN).hex
@echo ""
$(SIZE) $(BUILD_DIR)/$(BIN).elf
$(BUILD_DIR):
mkdir -p $(BUILD_DIR)
$(BUILD_DIR)/%.o: %.c
@echo "Compiling: $(notdir $<)"
$(CC) $(CFLAGS) -c -o $@ $<
$(BUILD_DIR)/$(BIN).elf: $(OBJS_C)
@echo "Linking: $(notdir $@)"
$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
@echo ""
$(NM) -n $@ > $(BUILD_DIR)/$(BIN).sym
$(OBJDUMP) -S $@ > $(BUILD_DIR)/$(BIN).disasm
$(BUILD_DIR)/$(BIN).hex: $(BUILD_DIR)/$(BIN).elf
@echo "Generating HEX: $(notdir $@)"
$(OBJCOPY) -O ihex $< $@
clean:
rm -f $(BUILD_DIR)/*.elf $(BUILD_DIR)/*.hex $(BUILD_DIR)/*.map
rm -f $(BUILD_DIR)/*.o $(BUILD_DIR)/*.sym $(BUILD_DIR)/*.disasm

150
puf/README.md 100644
View File

@ -0,0 +1,150 @@
# wolfCrypt SRAM PUF Example
Bare-metal example demonstrating SRAM PUF (Physically Unclonable Function)
on Cortex-M targets (tested on NUCLEO-H563ZI).
## Overview
SRAM PUF exploits the random power-on state of SRAM memory cells to derive
device-unique cryptographic keys. Each chip has a unique SRAM "fingerprint"
caused by manufacturing variations.
This example demonstrates:
1. **Enrollment** - Read raw SRAM, generate helper data using BCH(127,64,t=10)
error-correcting codes
2. **Reconstruction** - Re-read (noisy) SRAM, use helper data to recover the
same stable bits despite bit flips (corrects up to 10 per 127-bit codeword)
3. **Key derivation** - Use HKDF-SHA256 to derive a 256-bit cryptographic key
4. **Device identity** - SHA-256 hash of stable bits serves as a unique device ID
## Building
### Requirements
- `arm-none-eabi-gcc` toolchain
### Test Mode (default)
Uses synthetic SRAM data -- runs on any target, no real hardware needed:
```bash
make
```
### Real Hardware Mode
For actual SRAM PUF on hardware, edit `user_settings.h` and comment out
`WOLFSSL_PUF_TEST`, then build:
```bash
make
```
### Output
Build output is placed in `./Build/`:
- `puf_example.elf` - Loadable ELF binary
- `puf_example.hex` - Intel HEX for flash programmers
## Flashing
### OpenOCD (NUCLEO-H563ZI)
```bash
openocd -f interface/stlink.cfg -f target/stm32h5x.cfg \
-c "program Build/puf_example.elf verify reset exit"
```
When multiple ST-Links are connected, specify the serial number:
```bash
openocd -f interface/stlink.cfg \
-c "adapter serial <YOUR_SERIAL>" \
-f target/stm32h5x.cfg \
-c "program Build/puf_example.elf verify reset exit"
```
## UART Output
Connect to the board's UART (typically 115200 baud) to see output:
```
--- wolfCrypt SRAM PUF Example ---
PUF initialized.
Mode: TEST (synthetic SRAM data)
Enrollment complete.
Identity (enrollment): 3ad99904f92897bad1a21bc9cbc3ab8f2dc4bc40dfe6e161c741f98ef8dd7e01
Derived key (enrollment): aa8573f70a3253ca567500bdcd610face6a140e5fc68047e02d3f13958dcc480
--- Simulating power cycle (noisy SRAM) ---
Reconstruction complete (BCH corrected noisy bits).
Identity (reconstructed): 3ad99904f92897bad1a21bc9cbc3ab8f2dc4bc40dfe6e161c741f98ef8dd7e01
PASS: Identity matches after reconstruction.
Derived key (reconstructed): aa8573f70a3253ca567500bdcd610face6a140e5fc68047e02d3f13958dcc480
PASS: Derived key matches after reconstruction.
--- PUF example complete ---
```
## Customizing for Your MCU
### Linker Script
Edit `linker.ld` to match your MCU's memory map:
```ld
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* your flash size */
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 636K /* total - PUF_RAM */
PUF_RAM (rw) : ORIGIN = 0x2009F000, LENGTH = 4K /* end of SRAM */
}
```
The `PUF_RAM` region must be at the end of SRAM and marked `NOLOAD` so the
startup code does not zero it.
### Architecture
Edit the `ARCHFLAGS` in `Makefile`:
```makefile
ARCHFLAGS = -mcpu=cortex-m33 -mthumb
```
## API Usage
```c
wc_PufCtx ctx;
uint8_t helperData[WC_PUF_HELPER_BYTES];
uint8_t key[WC_PUF_KEY_SZ];
/* First boot: Enroll */
wc_PufInit(&ctx);
wc_PufReadSram(&ctx, sram_addr, sram_size);
wc_PufEnroll(&ctx);
memcpy(helperData, ctx.helperData, WC_PUF_HELPER_BYTES);
/* Store helperData to flash/NVM (it is NOT secret) */
/* Subsequent boots: Reconstruct */
wc_PufInit(&ctx);
wc_PufReadSram(&ctx, sram_addr, sram_size);
wc_PufReconstruct(&ctx, helperData, WC_PUF_HELPER_BYTES);
wc_PufDeriveKey(&ctx, info, infoSz, key, sizeof(key));
/* Always zeroize when done */
wc_PufZeroize(&ctx);
```
## Security Notes
- **Helper data is public** - It does not reveal the key. Safe to store
unencrypted in flash or transmit over the network.
- **SRAM must not be accessed before PUF read** - Any read or write to the
PUF SRAM region before `wc_PufReadSram()` will corrupt the power-on entropy.
- **Production RNG** - Replace the dummy `my_rng_seed_gen()` with your
MCU's hardware RNG (e.g., STM32 RNG peripheral).

137
puf/linker.ld 100644
View File

@ -0,0 +1,137 @@
/* linker.ld
*
* Linker script for bare-metal Cortex-M33 with PUF SRAM region.
* Default: NUCLEO-H563ZI (STM32H563ZI)
*
* Uses STM32-standard symbol names for compatibility with
* startup_stm32h563zitx.s from STM32CubeH5.
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*/
ENTRY(Reset_Handler)
_estack = ORIGIN(RAM) + LENGTH(RAM);
_Min_Heap_Size = 0x400;
_Min_Stack_Size = 0x2000;
MEMORY
{
/* STM32H563ZI: 2MB flash, 640KB SRAM */
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 636K
/* Reserve 4K at end of SRAM for PUF (not touched by .bss clear) */
PUF_RAM (rw) : ORIGIN = 0x2009F000, LENGTH = 4K
}
SECTIONS
{
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector))
. = ALIGN(4);
} > FLASH
.text :
{
. = ALIGN(4);
*(.text)
*(.text*)
*(.glue_7)
*(.glue_7t)
*(.eh_frame)
KEEP(*(.init))
KEEP(*(.fini))
. = ALIGN(4);
_etext = .;
} > FLASH
.rodata :
{
. = ALIGN(4);
*(.rodata)
*(.rodata*)
. = ALIGN(4);
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
.ARM :
{
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} > FLASH
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} > FLASH
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} > FLASH
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP(*(SORT(.fini_array.*)))
KEEP(*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
} > FLASH
_sidata = LOADADDR(.data);
.data :
{
. = ALIGN(4);
_sdata = .;
*(.data)
*(.data*)
. = ALIGN(4);
_edata = .;
} > RAM AT> FLASH
.bss :
{
. = ALIGN(4);
_sbss = .;
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
__bss_end__ = _ebss;
} > RAM
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE(end = .);
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} > RAM
/* PUF SRAM section: NOLOAD prevents startup code from zeroing.
* The raw power-on state of these bytes is the PUF source. */
.puf_sram (NOLOAD) :
{
. = ALIGN(4);
_puf_sram_start = .;
KEEP(*(.puf_sram))
KEEP(*(.puf_sram.*))
_puf_sram_end = .;
} > PUF_RAM
}

275
puf/main.c 100644
View File

@ -0,0 +1,275 @@
/* main.c
*
* Bare-metal SRAM PUF example for Cortex-M.
*
* Platform-portable PUF demonstration: enrollment, reconstruction,
* key derivation, and device identity. HAL-specific code (UART, RNG)
* is in a separate file (e.g., stm32.c).
*
* Supports both test mode (synthetic SRAM data) and real hardware SRAM PUF.
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/puf.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#include <wolfssl/wolfcrypt/wc_port.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
/* Platform HAL init (implemented in stm32.c or other target file) */
extern void hal_init(void);
/* -------------------------------------------------------------------------- */
/* PUF SRAM region (real hardware path) */
/* -------------------------------------------------------------------------- */
#ifndef WOLFSSL_PUF_TEST
/* This buffer is placed in the .puf_sram linker section (NOLOAD).
* The startup code must NOT zero this region - the raw power-on SRAM
* state is the PUF entropy source. */
__attribute__((section(".puf_sram")))
static volatile uint8_t puf_sram_region[WC_PUF_RAW_BYTES];
#endif
/* -------------------------------------------------------------------------- */
/* Helper: print hex buffer */
/* -------------------------------------------------------------------------- */
static void print_hex(const char* label, const uint8_t* data, uint32_t len)
{
uint32_t i;
printf("%s: ", label);
for (i = 0; i < len; i++)
printf("%02x", data[i]);
printf("\n");
}
/* -------------------------------------------------------------------------- */
/* Main PUF demonstration */
/* -------------------------------------------------------------------------- */
int main(void)
{
wc_PufCtx ctx;
int ret;
uint8_t identity[WC_PUF_ID_SZ];
uint8_t key[WC_PUF_KEY_SZ];
uint8_t helperData[WC_PUF_HELPER_BYTES];
const uint8_t info[] = "puf-example-key";
/* Initialize platform HAL (UART, clocks, etc.) */
hal_init();
printf("\n--- wolfCrypt SRAM PUF Example ---\n\n");
/* Initialize wolfCrypt */
ret = wolfCrypt_Init();
if (ret != 0) {
printf("ERROR: wolfCrypt_Init failed: %d\n", ret);
return ret;
}
/* Step 1: Initialize PUF context */
ret = wc_PufInit(&ctx);
if (ret != 0) {
printf("ERROR: wc_PufInit failed: %d\n", ret);
goto cleanup;
}
printf("PUF initialized.\n");
#ifdef WOLFSSL_PUF_TEST
/* ================================================================== */
/* TEST MODE: Use synthetic SRAM data */
/* ================================================================== */
{
/* Synthetic SRAM pattern for testing (deterministic) */
uint8_t testSram[WC_PUF_RAW_BYTES];
uint8_t noisySram[WC_PUF_RAW_BYTES];
uint8_t identity2[WC_PUF_ID_SZ];
uint8_t key2[WC_PUF_KEY_SZ];
uint32_t i;
printf("Mode: TEST (synthetic SRAM data)\n\n");
/* Generate deterministic test pattern */
for (i = 0; i < WC_PUF_RAW_BYTES; i++)
testSram[i] = (uint8_t)((i * 37 + 13) ^ (i >> 2));
/* Step 2: Load test SRAM data */
ret = wc_PufSetTestData(&ctx, testSram, sizeof(testSram));
if (ret != 0) {
printf("ERROR: wc_PufSetTestData failed: %d\n", ret);
goto cleanup;
}
/* Step 3: Enroll - generates helper data from SRAM pattern */
ret = wc_PufEnroll(&ctx);
if (ret != 0) {
printf("ERROR: wc_PufEnroll failed: %d\n", ret);
goto cleanup;
}
printf("Enrollment complete.\n");
/* Save helper data (in production, store to flash/NVM) */
memcpy(helperData, ctx.helperData, WC_PUF_HELPER_BYTES);
/* Get device identity */
ret = wc_PufGetIdentity(&ctx, identity, sizeof(identity));
if (ret != 0) {
printf("ERROR: wc_PufGetIdentity failed: %d\n", ret);
goto cleanup;
}
print_hex("Identity (enrollment)", identity, WC_PUF_ID_SZ);
/* Derive a key */
ret = wc_PufDeriveKey(&ctx, info, sizeof(info), key, sizeof(key));
if (ret != 0) {
printf("ERROR: wc_PufDeriveKey failed: %d\n", ret);
goto cleanup;
}
print_hex("Derived key (enrollment)", key, WC_PUF_KEY_SZ);
/* ---- Simulate power cycle with noisy SRAM ---- */
printf("\n--- Simulating power cycle (noisy SRAM) ---\n\n");
/* Create noisy copy: flip a few bits per 128-bit block */
memcpy(noisySram, testSram, WC_PUF_RAW_BYTES);
for (i = 0; i < 16; i++) {
/* Flip 2 bits in each 16-byte (128-bit) block */
noisySram[i * 16 + 3] ^= 0x04;
noisySram[i * 16 + 11] ^= 0x20;
}
/* Re-initialize and load noisy SRAM */
ret = wc_PufInit(&ctx);
if (ret != 0) {
printf("ERROR: wc_PufInit failed: %d\n", ret);
goto cleanup;
}
ret = wc_PufSetTestData(&ctx, noisySram, sizeof(noisySram));
if (ret != 0) {
printf("ERROR: wc_PufSetTestData failed: %d\n", ret);
goto cleanup;
}
/* Step 4: Reconstruct stable bits using helper data */
ret = wc_PufReconstruct(&ctx, helperData, WC_PUF_HELPER_BYTES);
if (ret != 0) {
printf("ERROR: wc_PufReconstruct failed: %d\n", ret);
goto cleanup;
}
printf("Reconstruction complete (BCH corrected noisy bits).\n");
/* Verify identity matches */
ret = wc_PufGetIdentity(&ctx, identity2, sizeof(identity2));
if (ret != 0) {
printf("ERROR: wc_PufGetIdentity failed: %d\n", ret);
goto cleanup;
}
print_hex("Identity (reconstructed)", identity2, WC_PUF_ID_SZ);
if (memcmp(identity, identity2, WC_PUF_ID_SZ) == 0) {
printf("PASS: Identity matches after reconstruction.\n");
}
else {
printf("FAIL: Identity mismatch!\n");
ret = -1;
goto cleanup;
}
/* Verify derived key matches */
ret = wc_PufDeriveKey(&ctx, info, sizeof(info), key2, sizeof(key2));
if (ret != 0) {
printf("ERROR: wc_PufDeriveKey failed: %d\n", ret);
goto cleanup;
}
print_hex("Derived key (reconstructed)", key2, WC_PUF_KEY_SZ);
if (memcmp(key, key2, WC_PUF_KEY_SZ) == 0) {
printf("PASS: Derived key matches after reconstruction.\n");
}
else {
printf("FAIL: Derived key mismatch!\n");
ret = -1;
goto cleanup;
}
}
#else
/* ================================================================== */
/* REAL HARDWARE: Read actual SRAM PUF */
/* ================================================================== */
printf("Mode: HARDWARE (real SRAM PUF)\n\n");
/* Step 2: Read raw SRAM (must be done before any other SRAM access) */
ret = wc_PufReadSram(&ctx, (const uint8_t*)puf_sram_region,
sizeof(puf_sram_region));
if (ret != 0) {
printf("ERROR: wc_PufReadSram failed: %d\n", ret);
goto cleanup;
}
printf("SRAM read complete (%d bytes).\n", (int)sizeof(puf_sram_region));
/* Step 3: Enroll (first boot only - save helper data to flash/NVM) */
ret = wc_PufEnroll(&ctx);
if (ret != 0) {
printf("ERROR: wc_PufEnroll failed: %d\n", ret);
goto cleanup;
}
printf("Enrollment complete.\n");
/* Save helper data for future reconstructions */
memcpy(helperData, ctx.helperData, WC_PUF_HELPER_BYTES);
print_hex("Helper data (store to NVM)", helperData, WC_PUF_HELPER_BYTES);
/* Get device identity */
ret = wc_PufGetIdentity(&ctx, identity, sizeof(identity));
if (ret != 0) {
printf("ERROR: wc_PufGetIdentity failed: %d\n", ret);
goto cleanup;
}
print_hex("Device identity", identity, WC_PUF_ID_SZ);
/* Derive a key */
ret = wc_PufDeriveKey(&ctx, info, sizeof(info), key, sizeof(key));
if (ret != 0) {
printf("ERROR: wc_PufDeriveKey failed: %d\n", ret);
goto cleanup;
}
print_hex("Derived key", key, WC_PUF_KEY_SZ);
/* Note: On subsequent boots, use wc_PufReconstruct() with the stored
* helper data instead of wc_PufEnroll(). The BCH error correction
* (t=10, corrects up to 10 bit flips per 127-bit codeword) will
* recover the same stable bits even with noisy SRAM. */
#endif /* WOLFSSL_PUF_TEST */
printf("\n--- PUF example complete ---\n");
cleanup:
/* Securely zeroize all PUF secrets */
wc_PufZeroize(&ctx);
wolfCrypt_Cleanup();
return ret;
}

136
puf/startup.c 100644
View File

@ -0,0 +1,136 @@
/* startup.c
*
* Minimal Cortex-M startup for PUF example.
* Provides vector table, Reset_Handler, and SystemInit.
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include <stdint.h>
#include <string.h>
/* Linker-provided symbols (STM32-standard names) */
extern uint32_t _estack;
extern uint32_t _sidata, _sdata, _edata;
extern uint32_t _sbss, _ebss;
extern void __libc_init_array(void);
extern int main(void);
/* Forward declarations */
void Reset_Handler(void);
void Default_Handler(void);
void SystemInit(void);
/* -------------------------------------------------------------------------- */
/* SystemInit - minimal clock setup */
/* -------------------------------------------------------------------------- */
void SystemInit(void)
{
/* Set VTOR to flash base */
*(volatile uint32_t *)0xE000ED08 = 0x08000000;
/* Default HSI clock (64 MHz) is sufficient for this example */
}
/* -------------------------------------------------------------------------- */
/* Reset Handler */
/* -------------------------------------------------------------------------- */
void __attribute__((naked)) Reset_Handler(void)
{
__asm volatile (
"ldr r0, =_estack \n"
"mov sp, r0 \n"
"bl SystemInit \n"
/* Copy .data from flash to SRAM */
"ldr r0, =_sdata \n"
"ldr r1, =_edata \n"
"ldr r2, =_sidata \n"
"movs r3, #0 \n"
"b .Ldata_check \n"
".Ldata_copy: \n"
"ldr r4, [r2, r3] \n"
"str r4, [r0, r3] \n"
"adds r3, r3, #4 \n"
".Ldata_check: \n"
"adds r4, r0, r3 \n"
"cmp r4, r1 \n"
"bcc .Ldata_copy \n"
/* Zero .bss */
"ldr r2, =_sbss \n"
"ldr r4, =_ebss \n"
"movs r3, #0 \n"
"b .Lbss_check \n"
".Lbss_zero: \n"
"str r3, [r2] \n"
"adds r2, r2, #4 \n"
".Lbss_check: \n"
"cmp r2, r4 \n"
"bcc .Lbss_zero \n"
/* Call static constructors, then main */
"bl __libc_init_array \n"
"bl main \n"
".Lhang: \n"
"b .Lhang \n"
);
}
void Default_Handler(void)
{
while (1) { }
}
/* -------------------------------------------------------------------------- */
/* Vector table */
/* -------------------------------------------------------------------------- */
/* Weak aliases so the application can override individual handlers */
void __attribute__((weak, alias("Default_Handler"))) NMI_Handler(void);
void __attribute__((weak, alias("Default_Handler"))) HardFault_Handler(void);
void __attribute__((weak, alias("Default_Handler"))) MemManage_Handler(void);
void __attribute__((weak, alias("Default_Handler"))) BusFault_Handler(void);
void __attribute__((weak, alias("Default_Handler"))) UsageFault_Handler(void);
void __attribute__((weak, alias("Default_Handler"))) SecureFault_Handler(void);
void __attribute__((weak, alias("Default_Handler"))) SVC_Handler(void);
void __attribute__((weak, alias("Default_Handler"))) DebugMon_Handler(void);
void __attribute__((weak, alias("Default_Handler"))) PendSV_Handler(void);
void __attribute__((weak, alias("Default_Handler"))) SysTick_Handler(void);
typedef void (*vector_fn)(void);
const vector_fn __isr_vector[] __attribute__((section(".isr_vector"), used)) = {
(vector_fn)(uintptr_t)&_estack, /* Initial SP */
Reset_Handler, /* Reset */
NMI_Handler, /* NMI */
HardFault_Handler, /* Hard Fault */
MemManage_Handler, /* MPU Fault */
BusFault_Handler, /* Bus Fault */
UsageFault_Handler, /* Usage Fault */
SecureFault_Handler, /* Secure Fault (M33) */
0, 0, 0, /* Reserved */
SVC_Handler, /* SVCall */
DebugMon_Handler, /* Debug Monitor */
0, /* Reserved */
PendSV_Handler, /* PendSV */
SysTick_Handler, /* SysTick */
/* Peripheral IRQs default to Default_Handler via unused slots */
};

244
puf/stm32.c 100644
View File

@ -0,0 +1,244 @@
/* stm32.c
*
* STM32H5 HAL support for PUF example (tested on NUCLEO-H563ZI).
* Provides USART3 init/output, printf retarget, RNG stub, and time stub.
*
* To port to a different MCU, replace this file with your platform's
* UART and RNG implementation. The interface is:
* void hal_init(void) - called once at startup before printf
* int my_rng_seed_gen(uint8_t* output, uint32_t sz) - RNG seed
* unsigned long my_time(unsigned long* timer) - monotonic time
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include <stdint.h>
/* -------------------------------------------------------------------------- */
/* STM32H5 USART3 (ST-LINK VCP) bare-metal driver */
/* -------------------------------------------------------------------------- */
/* STM32H563 register bases.
* TZEN=0: use non-secure aliases (0x4xxx).
* TZEN=1: use secure aliases (0x5xxx). */
#ifdef STM32H5_TZEN
#define RCC_BASE 0x54020C00u
#define GPIOD_BASE 0x52020C00u
#define USART3_BASE 0x50004800u
#else
#define RCC_BASE 0x44020C00u
#define GPIOD_BASE 0x42020C00u
#define USART3_BASE 0x40004800u
#endif
#define RCC_AHB2ENR (*(volatile uint32_t *)(RCC_BASE + 0x8Cu))
#define RCC_APB1ENR (*(volatile uint32_t *)(RCC_BASE + 0x9Cu))
/* GPIO registers */
#define GPIO_MODER(b) (*(volatile uint32_t *)((b) + 0x00u))
#define GPIO_OSPEEDR(b) (*(volatile uint32_t *)((b) + 0x08u))
#define GPIO_AFRH(b) (*(volatile uint32_t *)((b) + 0x24u))
/* USART3 registers */
#define USART3_CR1 (*(volatile uint32_t *)(USART3_BASE + 0x00u))
#define USART3_CR2 (*(volatile uint32_t *)(USART3_BASE + 0x04u))
#define USART3_CR3 (*(volatile uint32_t *)(USART3_BASE + 0x08u))
#define USART3_BRR (*(volatile uint32_t *)(USART3_BASE + 0x0Cu))
#define USART3_ISR (*(volatile uint32_t *)(USART3_BASE + 0x1Cu))
#define USART3_TDR (*(volatile uint32_t *)(USART3_BASE + 0x28u))
#define USART3_PRESC (*(volatile uint32_t *)(USART3_BASE + 0x2Cu))
static void delay(volatile uint32_t n)
{
while (n--) { }
}
static void uart_init(void)
{
uint32_t moder, afr;
/* Enable GPIOD clock */
RCC_AHB2ENR |= (1u << 3);
/* Enable USART3 clock (APB1LENR bit 18) */
RCC_APB1ENR |= (1u << 18);
delay(100);
/* Configure PD8 (TX) as AF7, push-pull, high speed */
moder = GPIO_MODER(GPIOD_BASE);
moder &= ~(3u << 16);
moder |= (2u << 16); /* Alternate function */
GPIO_MODER(GPIOD_BASE) = moder;
GPIO_OSPEEDR(GPIOD_BASE) |= (3u << 16); /* High speed for PD8 */
afr = GPIO_AFRH(GPIOD_BASE);
afr &= ~(0xFu << 0);
afr |= (7u << 0); /* AF7 = USART3 */
GPIO_AFRH(GPIOD_BASE) = afr;
/* Configure USART3: 115200 baud at default 32 MHz PCLK1 */
USART3_CR1 = 0;
USART3_CR2 = 0;
USART3_CR3 = 0;
USART3_PRESC = 0;
USART3_BRR = 32000000u / 115200u; /* ~278 */
USART3_CR1 = (1u << 3); /* TE */
delay(10);
USART3_CR1 |= (1u << 0); /* UE */
delay(100);
}
static void uart_putc(char c)
{
while ((USART3_ISR & (1u << 7)) == 0) { }
USART3_TDR = (uint32_t)c;
}
/* Retarget _write for printf via USART3 */
int _write(int fd, const char *buf, int len)
{
int i;
(void)fd;
for (i = 0; i < len; i++) {
if (buf[i] == '\n')
uart_putc('\r');
uart_putc(buf[i]);
}
return len;
}
/* -------------------------------------------------------------------------- */
/* STM32H5 Hardware RNG (TRNG) driver */
/* -------------------------------------------------------------------------- */
/* RCC clock control */
#define RCC_CR (*(volatile uint32_t *)(RCC_BASE + 0x00u))
#define RCC_CR_HSI48ON (1u << 12)
#define RCC_CR_HSI48RDY (1u << 13)
#define RCC_CCIPR5 (*(volatile uint32_t *)(RCC_BASE + 0xE8u))
#define RCC_CCIPR5_RNGSEL_Msk (3u << 4)
/* RNG peripheral */
#ifdef STM32H5_TZEN
#define RNG_BASE 0x520C0800u
#else
#define RNG_BASE 0x420C0800u
#endif
#define RNG_CR (*(volatile uint32_t *)(RNG_BASE + 0x00u))
#define RNG_SR (*(volatile uint32_t *)(RNG_BASE + 0x04u))
#define RNG_DR (*(volatile uint32_t *)(RNG_BASE + 0x08u))
#define RNG_CR_RNGEN (1u << 2)
#define RNG_CR_CONDRST (1u << 30)
#define RNG_CR_CONFIG3_SHIFT 8u
#define RNG_CR_CONFIG2_SHIFT 13u
#define RNG_CR_CLKDIV_SHIFT 16u
#define RNG_CR_CONFIG1_SHIFT 20u
#define RNG_SR_DRDY (1u << 0)
#define RNG_SR_CECS (1u << 1)
#define RNG_SR_SECS (1u << 2)
#define RNG_SR_CEIS (1u << 5)
#define RNG_SR_SEIS (1u << 6)
static void rng_init(void)
{
uint32_t rng_cr;
/* Enable HSI48 as RNG kernel clock source */
RCC_CR |= RCC_CR_HSI48ON;
while ((RCC_CR & RCC_CR_HSI48RDY) == 0u) { }
/* Select HSI48 for RNG clock */
RCC_CCIPR5 &= ~RCC_CCIPR5_RNGSEL_Msk;
RCC_AHB2ENR |= (1u << 18); /* RNG clock enable */
delay(100);
/* Configure and enable RNG with conditioning reset */
rng_cr = RNG_CR;
rng_cr &= ~(0x1Fu << RNG_CR_CONFIG1_SHIFT);
rng_cr &= ~(0x7u << RNG_CR_CLKDIV_SHIFT);
rng_cr &= ~(0x3u << RNG_CR_CONFIG2_SHIFT);
rng_cr &= ~(0x7u << RNG_CR_CONFIG3_SHIFT);
rng_cr |= 0x0Fu << RNG_CR_CONFIG1_SHIFT;
rng_cr |= 0x0Du << RNG_CR_CONFIG3_SHIFT;
RNG_CR = RNG_CR_CONDRST | rng_cr;
while ((RNG_CR & RNG_CR_CONDRST) == 0u) { }
RNG_CR = rng_cr | RNG_CR_RNGEN;
while ((RNG_SR & RNG_SR_DRDY) == 0u) { }
}
static int rng_get_word(uint32_t *out)
{
uint32_t timeout = 100000u;
while ((RNG_SR & RNG_SR_DRDY) == 0u) {
if ((RNG_SR & (RNG_SR_CECS | RNG_SR_SECS | RNG_SR_CEIS | RNG_SR_SEIS))
!= 0u) {
rng_init();
timeout = 100000u;
continue;
}
if (--timeout == 0u)
return -1;
}
*out = RNG_DR;
return 0;
}
/* wolfCrypt custom RNG block generator using STM32H5 TRNG */
int custom_rand_gen_block(unsigned char *output, unsigned int sz)
{
uint32_t word;
while (sz >= 4u) {
if (rng_get_word(&word) != 0)
return -1;
output[0] = (unsigned char)word;
output[1] = (unsigned char)(word >> 8);
output[2] = (unsigned char)(word >> 16);
output[3] = (unsigned char)(word >> 24);
output += 4;
sz -= 4;
}
if (sz > 0u) {
if (rng_get_word(&word) != 0)
return -1;
while (sz-- > 0u) {
*output++ = (unsigned char)word;
word >>= 8;
}
}
return 0;
}
/* -------------------------------------------------------------------------- */
/* hal_init - platform initialization entry point */
/* -------------------------------------------------------------------------- */
void hal_init(void)
{
uart_init();
rng_init();
}
/* Custom time function */
unsigned long my_time(unsigned long* timer)
{
static unsigned long t = 1000;
if (timer)
*timer = t;
return t++;
}

116
puf/user_settings.h 100644
View File

@ -0,0 +1,116 @@
/* user_settings.h
*
* Minimal wolfCrypt configuration for bare-metal SRAM PUF example.
* Target: Cortex-M (tested on NUCLEO-H563ZI)
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#ifndef WOLFSSL_USER_SETTINGS_H
#define WOLFSSL_USER_SETTINGS_H
#ifdef __cplusplus
extern "C" {
#endif
/* ------------------------------------------------------------------------- */
/* Platform */
/* ------------------------------------------------------------------------- */
#define WOLFCRYPT_ONLY
#define SINGLE_THREADED
#define WOLFSSL_SMALL_STACK
#define WOLFSSL_GENERAL_ALIGNMENT 4
#define WOLFSSL_USER_IO
#ifndef USE_WOLF_ARM_STARTUP
#define USE_WOLF_ARM_STARTUP
#endif
/* ------------------------------------------------------------------------- */
/* PUF (Physically Unclonable Function) */
/* ------------------------------------------------------------------------- */
#define WOLFSSL_PUF
#define WOLFSSL_PUF_SRAM
/* Enable test mode: allows synthetic SRAM data for testing without hardware.
* Comment out to use real SRAM PUF on actual hardware. */
#define WOLFSSL_PUF_TEST
/* ------------------------------------------------------------------------- */
/* Required Dependencies */
/* ------------------------------------------------------------------------- */
/* HKDF is required for PUF key derivation (wc_PufDeriveKey) */
#define HAVE_HKDF
/* SHA-256 is required for PUF identity and HKDF */
/* (enabled by default, do not define NO_SHA256) */
/* Uncomment to use SHA3-256 instead of SHA-256 for PUF identity and HKDF */
/* #define WC_PUF_SHA3 */
/* #define WOLFSSL_SHA3 */
/* ------------------------------------------------------------------------- */
/* Math Configuration */
/* ------------------------------------------------------------------------- */
#define WOLFSSL_SP_MATH_ALL
#define WOLFSSL_SP_SMALL
#define SP_WORD_SIZE 32
/* ------------------------------------------------------------------------- */
/* Disable Unused Features */
/* ------------------------------------------------------------------------- */
#define NO_RSA
#define NO_DH
#define NO_DSA
#define NO_DES3
#define NO_RC4
#define NO_MD4
#define NO_MD5
#define NO_SHA
#define NO_PSK
#define NO_OLD_TLS
#define NO_PWDBASED
#define NO_CERTS
#define NO_ASN
#define NO_CODING
#define NO_SIG_WRAPPER
#define NO_AES
/* ------------------------------------------------------------------------- */
/* System */
/* ------------------------------------------------------------------------- */
#define NO_FILESYSTEM
#define NO_WRITEV
#define NO_MAIN_DRIVER
#define NO_DEV_RANDOM
#define BENCH_EMBEDDED
#define WOLFSSL_IGNORE_FILE_WARN
/* Provide custom time function */
#define USER_TICKS
/* Use STM32H5 hardware TRNG (implemented in stm32.c) */
extern int custom_rand_gen_block(unsigned char *output, unsigned int sz);
#define CUSTOM_RAND_GENERATE_BLOCK custom_rand_gen_block
#ifdef __cplusplus
}
#endif
#endif /* WOLFSSL_USER_SETTINGS_H */