Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

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:

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:

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)

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:

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:

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:

ARCHFLAGS = -mcpu=cortex-m33 -mthumb

API Usage

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:

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