-
Notifications
You must be signed in to change notification settings - Fork 199
Example for wolfCrypt PUF on STM32H5 #565
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -416,3 +416,5 @@ stsafe/wolfssl_stsafe_full_test | |
|
|
||
| # uefi-library generated filesystem content | ||
| uefi-library/efifs | ||
|
|
||
| puf/Build | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| # 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 | ||
|
|
||
| # PUF test mode (default on): synthetic SRAM data for testing without hardware. | ||
| # Set PUF_TEST=0 to build for real hardware SRAM. | ||
| PUF_TEST ?= 1 | ||
|
|
||
| # 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) | ||
|
|
||
| ifeq ($(PUF_TEST),1) | ||
| CFLAGS += -DWOLFSSL_PUF_TEST | ||
| endif | ||
|
|
||
| # 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 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,178 @@ | ||
| # 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 | ||
|
|
||
| Test mode is the default and is selected by the Makefile (not the | ||
| header). To build for the real SRAM PUF on hardware, override the | ||
| `PUF_TEST` variable: | ||
|
|
||
| ```bash | ||
| make PUF_TEST=0 | ||
| ``` | ||
|
|
||
| This drops the `-DWOLFSSL_PUF_TEST` define and includes `puf_sram_region` | ||
| (placed in the `.puf_sram` NOLOAD section) so `wc_PufReadSram()` reads | ||
| the real power-on SRAM contents. | ||
|
|
||
| ### 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** - This example wires wolfCrypt's RNG through | ||
| `CUSTOM_RAND_GENERATE_BLOCK` (in `user_settings.h`) to | ||
| `custom_rand_gen_block()` in `stm32.c`, which uses the STM32H5 RNG | ||
| peripheral over HSI48. When porting to another MCU, replace the | ||
| implementation behind `custom_rand_gen_block()` (or remap | ||
| `CUSTOM_RAND_GENERATE_BLOCK` to your platform's hardware RNG hook). | ||
|
|
||
| ## Reproducing on the m33mu Emulator | ||
|
|
||
| The m33mu Cortex-M33 emulator can simulate cold-boot SRAM and seeded | ||
| noise so the BCH reconstruction path can be exercised without rebooting | ||
| real hardware: | ||
|
|
||
| ```bash | ||
| # Deterministic SRAM, boot 0 - enrolls and reconstructs cleanly | ||
| m33mu --puf-seed 0xDEADBEEF --puf-cold-boot 0 Build/puf_example.elf | ||
|
|
||
| # Same seed/boot with 2 bit flips per 127-bit codeword - within BCH(t=10) | ||
| m33mu --puf-seed 0xDEADBEEF --puf-cold-boot 0 --puf-noise 2 \ | ||
| Build/puf_example.elf | ||
| ``` | ||
|
|
||
| Identity must match between the enrollment and reconstruction prints as | ||
| long as noise stays within the BCH correction budget (10 flips per | ||
| 127-bit codeword; safe margin 2-4). |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.