wolfssl-examples/puf/README.md

179 lines
5.1 KiB
Markdown

# 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).