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
19 changes: 17 additions & 2 deletions .github/workflows/puf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,33 @@ permissions:

jobs:
puf_host_test:
name: PUF host test
name: PUF host test (${{ matrix.config }})
if: ${{ (github.repository_owner == 'wolfssl') && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) }}
runs-on: ubuntu-24.04
timeout-minutes: 6
strategy:
fail-fast: false
matrix:
# Cover every shipped BCH profile (t=7/10/13/15) and a non-default
# codeword count so the profile-specific genpoly, parity register and
# bit-packed paths are all built and exercised, not just t=10.
config:
- "--enable-puf --enable-puf-test"
- "--enable-puf=small --enable-puf-test"
- "--enable-puf=strong --enable-puf-test"
- "--enable-puf=strongest --enable-puf-test"
- "--enable-puf --enable-puf-test CPPFLAGS=-DWC_PUF_NUM_CODEWORDS=32"
steps:
- uses: actions/checkout@v5
name: Checkout wolfSSL

- name: Validate BCH generator polynomials
run: python3 scripts/puf_bch_genpoly.py > /dev/null

- name: Build and test PUF
run: |
./autogen.sh
./configure --enable-puf --enable-puf-test
./configure ${{ matrix.config }}
make
./wolfcrypt/test/testwolfcrypt

Expand Down
16 changes: 16 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2316,10 +2316,26 @@ add_option("WOLFSSL_PUF"
"no" "yes;no")

if(WOLFSSL_PUF)
# PUF BCH error-correction profile (only surfaced when PUF is enabled).
# Other t values and WC_PUF_NUM_CODEWORDS can still be set via CFLAGS.
add_option("WOLFSSL_PUF_PROFILE"
"PUF BCH profile: balanced (t=10), small (t=7), strong (t=13), strongest (t=15)"
"balanced" "balanced;small;strong;strongest")

list(APPEND WOLFSSL_DEFINITIONS
"-DWOLFSSL_PUF"
"-DWOLFSSL_PUF_SRAM"
"-DHAVE_HKDF")
if(WOLFSSL_PUF_PROFILE STREQUAL "small")
list(APPEND WOLFSSL_DEFINITIONS "-DWC_PUF_BCH_T=7")
elseif(WOLFSSL_PUF_PROFILE STREQUAL "strong")
list(APPEND WOLFSSL_DEFINITIONS "-DWC_PUF_BCH_T=13")
elseif(WOLFSSL_PUF_PROFILE STREQUAL "strongest")
list(APPEND WOLFSSL_DEFINITIONS "-DWC_PUF_BCH_T=15")
elseif(NOT WOLFSSL_PUF_PROFILE STREQUAL "balanced")
message(FATAL_ERROR
"WOLFSSL_PUF_PROFILE must be balanced, small, strong, or strongest")
endif()
override_cache(WOLFSSL_HKDF "yes")
endif()

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ information, visit the [wolfCrypt FIPS FAQ](https://www.wolfssl.com/license/fips
or contact fips@wolfssl.com.

wolfCrypt also includes support for deriving device-unique keys from hardware entropy
(`--enable-puf`). An example exists at
(`--enable-puf[=small|balanced|strong|strongest]`, selecting the BCH error-correction
strength). An example exists at
[SRAM PUF](https://github.com/wolfSSL/wolfssl-examples/tree/master/puf).

## Why Choose wolfSSL?
Expand Down
10 changes: 10 additions & 0 deletions cmake/options.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,16 @@ extern "C" {
#cmakedefine HAVE_HASHDRBG
#undef HAVE_HKDF
#cmakedefine HAVE_HKDF
#undef WOLFSSL_PUF
#cmakedefine WOLFSSL_PUF
#undef WOLFSSL_PUF_SRAM
#cmakedefine WOLFSSL_PUF_SRAM
#undef WOLFSSL_PUF_TEST
#cmakedefine WOLFSSL_PUF_TEST
#undef WC_PUF_BCH_T
#cmakedefine WC_PUF_BCH_T @WC_PUF_BCH_T@
#undef WC_PUF_NUM_CODEWORDS
#cmakedefine WC_PUF_NUM_CODEWORDS @WC_PUF_NUM_CODEWORDS@
#undef HAVE_HPKE
#cmakedefine HAVE_HPKE
#undef HAVE_KEYING_MATERIAL
Expand Down
23 changes: 21 additions & 2 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -8425,16 +8425,31 @@ fi

# PUF
AC_ARG_ENABLE([puf],
[AS_HELP_STRING([--enable-puf],[Enable SRAM PUF support (default: disabled)])],
[AS_HELP_STRING([--enable-puf],[Enable SRAM PUF: profiles small/balanced/strong/strongest = t 7/10/13/15 (default: disabled)])],
[ ENABLED_PUF=$enableval ],
[ ENABLED_PUF=no ]
)

if test "$ENABLED_PUF" = "yes"
if test "$ENABLED_PUF" != "no"
then
# Map the profile keyword to a BCH error-correction strength (t). An
# empty PUF_BCH_T keeps the puf.h default of t=10.
PUF_BCH_T=""
PUF_PROFILE="balanced (t=10)"
case "$ENABLED_PUF" in
yes|balanced) PUF_BCH_T="" ; PUF_PROFILE="balanced (t=10)" ;;
small) PUF_BCH_T="7" ; PUF_PROFILE="small (t=7)" ;;
strong) PUF_BCH_T="13" ; PUF_PROFILE="strong (t=13)" ;;
strongest) PUF_BCH_T="15" ; PUF_PROFILE="strongest (t=15)" ;;
*) AC_MSG_ERROR([unknown --enable-puf value "$ENABLED_PUF"; use yes, small, balanced, strong, or strongest]) ;;
esac
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_PUF -DWOLFSSL_PUF_SRAM"
AS_IF([test "x$PUF_BCH_T" != "x"],
[AM_CFLAGS="$AM_CFLAGS -DWC_PUF_BCH_T=$PUF_BCH_T"])
AS_IF([test "$ENABLED_HKDF" != "yes"],
[ENABLED_HKDF="yes"; AM_CFLAGS="$AM_CFLAGS -DHAVE_HKDF"])
# normalize so downstream conditionals/summary see a plain yes
ENABLED_PUF="yes"
fi

# PUF test mode
Expand Down Expand Up @@ -13771,7 +13786,11 @@ echo " * AutoSAR : $ENABLED_AUTOSAR"
echo " * ML-KEM standalone: $ENABLED_MLKEM_STANDALONE"
echo " * PQ/T hybrids: $ENABLED_PQC_HYBRIDS"
echo " * Extra PQ/T hybrids: $ENABLED_EXTRA_PQC_HYBRIDS"
if test "$ENABLED_PUF" = "yes"; then
echo " * PUF: $ENABLED_PUF, profile $PUF_PROFILE"
else
echo " * PUF: $ENABLED_PUF"
fi
echo ""
echo "---"

Expand Down
60 changes: 52 additions & 8 deletions doc/dox_comments/header_files/puf.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
/*!
\ingroup PUF

The SRAM PUF uses a configurable BCH(127,k,t) fuzzy extractor over GF(2^7)
with HKDF key derivation. WC_PUF_BCH_T selects the error-correction
strength (t=7, 10 default, 13, or 15) and WC_PUF_NUM_CODEWORDS (default 16)
trades SRAM footprint and helper-data size (WC_PUF_HELPER_BYTES) against
derived-key entropy. Enrollment and reconstruction must use identical
WC_PUF_BCH_T and WC_PUF_NUM_CODEWORDS; persist WC_PUF_PROFILE_ID (or the
values from wc_PufGetParams) with the helper data and compare on
reconstruction.

For a complete bare-metal example (tested on NUCLEO-H563ZI), see
https://github.com/wolfSSL/wolfssl-examples/tree/master/puf
*/
Expand Down Expand Up @@ -32,7 +41,9 @@ int wc_PufInit(wc_PufCtx* ctx);
\ingroup PUF

\brief Read raw SRAM data into the PUF context. The sramAddr should
point to a NOLOAD linker section to preserve the power-on state.
point to a NOLOAD linker section to preserve the power-on state. The
required size, WC_PUF_RAW_BYTES, scales with WC_PUF_NUM_CODEWORDS
(256 bytes at the default 16 codewords).

\return 0 on success
\return BAD_FUNC_ARG if ctx or sramAddr is NULL
Expand All @@ -45,7 +56,7 @@ int wc_PufInit(wc_PufCtx* ctx);
_Example_
\code
__attribute__((section(".puf_sram")))
static volatile uint8_t puf_sram[256];
static volatile uint8_t puf_sram[WC_PUF_RAW_BYTES];
wc_PufReadSram(&ctx, (const byte*)puf_sram, sizeof(puf_sram));
\endcode

Expand All @@ -58,9 +69,10 @@ int wc_PufReadSram(wc_PufCtx* ctx, const byte* sramAddr, word32 sramSz);
/*!
\ingroup PUF

\brief Perform PUF enrollment. Encodes raw SRAM using BCH(127,64,t=10)
and generates public helper data. After enrollment the context is ready
for key derivation and identity retrieval.
\brief Perform PUF enrollment. Encodes raw SRAM using the selected
BCH(127,k,t) profile (WC_PUF_BCH_T) and generates public helper data
(WC_PUF_HELPER_BYTES). After enrollment the context is ready for key
derivation and identity retrieval.

\return 0 on success
\return BAD_FUNC_ARG if ctx is NULL
Expand All @@ -84,8 +96,9 @@ int wc_PufEnroll(wc_PufCtx* ctx);
\ingroup PUF

\brief Reconstruct stable PUF bits from noisy SRAM using stored helper
data. BCH error correction (t=10) corrects up to 10 bit flips per
127-bit codeword.
data. BCH error correction corrects up to WC_PUF_BCH_T bit flips per
127-bit codeword. The helper data and build configuration must match the
enrollment that produced them.

\return 0 on success
\return BAD_FUNC_ARG if ctx or helperData is NULL
Expand Down Expand Up @@ -166,6 +179,37 @@ int wc_PufDeriveKey(wc_PufCtx* ctx, const byte* info, word32 infoSz,
*/
int wc_PufGetIdentity(wc_PufCtx* ctx, byte* id, word32 idSz);

/*!
\ingroup PUF

\brief Report the compile-time PUF profile parameters: field size m,
codeword length n, message length k, error-correction capability t, and
the number of codewords. Each output pointer is optional (may be NULL), but
an all-NULL call is treated as a usage error. Enrollment and reconstruction
firmware must agree on all of these (and the hash); persist
WC_PUF_PROFILE_ID (which also encodes the hash selection) with the helper
data and compare before reconstruction to detect a build mismatch.

\return 0 on success
\return BAD_FUNC_ARG if every output pointer is NULL

\param m optional output for the GF field exponent (7 for GF(2^7))
\param n optional output for the codeword length (127)
\param k optional output for the message length (per WC_PUF_BCH_T)
\param t optional output for the error-correction capability (WC_PUF_BCH_T)
\param numCodewords optional output for WC_PUF_NUM_CODEWORDS

_Example_
\code
int t, numCodewords;
wc_PufGetParams(NULL, NULL, NULL, &t, &numCodewords);
\endcode

\sa wc_PufEnroll
\sa wc_PufReconstruct
*/
int wc_PufGetParams(int* m, int* n, int* k, int* t, int* numCodewords);

/*!
\ingroup PUF

Expand Down Expand Up @@ -198,7 +242,7 @@ int wc_PufZeroize(wc_PufCtx* ctx);

\param ctx pointer to wc_PufCtx
\param data pointer to synthetic SRAM data
\param sz size of data (>= WC_PUF_RAW_BYTES, 256 bytes)
\param sz size of data (>= WC_PUF_RAW_BYTES)

_Example_
\code
Expand Down
Loading
Loading