From 21ed8c7eb97232766478a1e0d043658ceab51c2d Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 24 Mar 2026 16:54:13 -0700 Subject: [PATCH] Example for wolfCrypt PUF on STM32H5 --- .gitignore | 2 + README.md | 11 ++ puf/Makefile | 98 ++++++++++++++++ puf/README.md | 150 ++++++++++++++++++++++++ puf/linker.ld | 137 ++++++++++++++++++++++ puf/main.c | 275 ++++++++++++++++++++++++++++++++++++++++++++ puf/startup.c | 136 ++++++++++++++++++++++ puf/stm32.c | 244 +++++++++++++++++++++++++++++++++++++++ puf/user_settings.h | 116 +++++++++++++++++++ 9 files changed, 1169 insertions(+) create mode 100644 puf/Makefile create mode 100644 puf/README.md create mode 100644 puf/linker.ld create mode 100644 puf/main.c create mode 100644 puf/startup.c create mode 100644 puf/stm32.c create mode 100644 puf/user_settings.h diff --git a/.gitignore b/.gitignore index 36c5375d..86369f9b 100644 --- a/.gitignore +++ b/.gitignore @@ -414,3 +414,5 @@ stsafe/wolfssl_stsafe_full_test # uefi-library generated filesystem content uefi-library/efifs + +puf/Build diff --git a/README.md b/README.md index acbde482..327127eb 100644 --- a/README.md +++ b/README.md @@ -247,6 +247,17 @@ verifying/decrypting operations. Please see the [pkcs7/README.md](pkcs7/README.md) for further usage and details. +
+ +#### 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. +
#### PSK (Pre-Shared Keys) diff --git a/puf/Makefile b/puf/Makefile new file mode 100644 index 00000000..7935f605 --- /dev/null +++ b/puf/Makefile @@ -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 diff --git a/puf/README.md b/puf/README.md new file mode 100644 index 00000000..510e7115 --- /dev/null +++ b/puf/README.md @@ -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 " \ + -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). diff --git a/puf/linker.ld b/puf/linker.ld new file mode 100644 index 00000000..c335659b --- /dev/null +++ b/puf/linker.ld @@ -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 +} diff --git a/puf/main.c b/puf/main.c new file mode 100644 index 00000000..2f9b430f --- /dev/null +++ b/puf/main.c @@ -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 +#include +#include +#include + +#include +#include +#include + +/* 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; +} diff --git a/puf/startup.c b/puf/startup.c new file mode 100644 index 00000000..27aa0521 --- /dev/null +++ b/puf/startup.c @@ -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 +#include + +/* 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 */ +}; diff --git a/puf/stm32.c b/puf/stm32.c new file mode 100644 index 00000000..8ab17b39 --- /dev/null +++ b/puf/stm32.c @@ -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 + +/* -------------------------------------------------------------------------- */ +/* 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++; +} diff --git a/puf/user_settings.h b/puf/user_settings.h new file mode 100644 index 00000000..94175bda --- /dev/null +++ b/puf/user_settings.h @@ -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 */