Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -414,3 +414,5 @@ stsafe/wolfssl_stsafe_full_test

# uefi-library generated filesystem content
uefi-library/efifs

puf/Build
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
98 changes: 98 additions & 0 deletions puf/Makefile
Original file line number Diff line number Diff line change
@@ -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 changes: 150 additions & 0 deletions puf/README.md
Original file line number Diff line number Diff line change
@@ -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).
Loading