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
37 changes: 37 additions & 0 deletions .github/workflows/puf.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: PUF Tests

# START OF COMMON SECTION
on:
push:
branches: [ 'master', 'main', 'release/**' ]
pull_request:
branches: [ '*' ]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# END OF COMMON SECTION

jobs:
puf_host_test:
name: PUF host test
if: github.repository_owner == 'wolfssl'
runs-on: ubuntu-24.04
timeout-minutes: 6
steps:
- uses: actions/checkout@v4
name: Checkout wolfSSL

- name: Build and test PUF
run: |
./autogen.sh
./configure --enable-puf
make
./wolfcrypt/test/testwolfcrypt

- name: Print errors
if: ${{ failure() }}
run: |
if [ -f test-suite.log ] ; then
cat test-suite.log
fi
1 change: 1 addition & 0 deletions .wolfssl_known_macro_extras
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,7 @@ WC_NO_VERBOSE_RNG
WC_PKCS11_FIND_WITH_ID_ONLY
WC_PKCS12_PBKDF_USING_MP_API
WC_PROTECT_ENCRYPTED_MEM
WC_PUF_SHA3
WC_RNG_BLOCKING
WC_RSA_NONBLOCK
WC_RSA_NONBLOCK_TIME
Expand Down
12 changes: 12 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1730,6 +1730,18 @@ endif()

# TODO: - XCHACHA

# SRAM PUF
add_option("WOLFSSL_PUF"
"Enable SRAM PUF support (default: disabled)"
"no" "yes;no")

if(WOLFSSL_PUF)
list(APPEND WOLFSSL_DEFINITIONS
"-DWOLFSSL_PUF"
"-DWOLFSSL_PUF_SRAM"
"-DWOLFSSL_PUF_TEST")
endif()

# Hash DRBG
add_option("WOLFSSL_HASH_DRBG"
"Enable Hash DRBG support (default: enabled)"
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ certificate #3389). FIPS 140-3 validated (Certificate #4718). For additional
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`) and an example exists at
[SRAM PUF](https://github.com/wolfSSL/wolfssl-examples/tree/master/puf)


## Why Choose wolfSSL?

There are many reasons to choose wolfSSL as your embedded, desktop, mobile, or
Expand Down
16 changes: 16 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -7172,6 +7172,20 @@ then
AM_CFLAGS="$AM_CFLAGS -DHAVE_ASCON"
fi

# PUF
AC_ARG_ENABLE([puf],
[AS_HELP_STRING([--enable-puf],[Enable SRAM PUF support (default: disabled)])],
[ ENABLED_PUF=$enableval ],
[ ENABLED_PUF=no ]
)

if test "$ENABLED_PUF" = "yes"
then
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_PUF -DWOLFSSL_PUF_SRAM -DWOLFSSL_PUF_TEST"
AS_IF([test "$ENABLED_HKDF" != "yes"],
[ENABLED_HKDF="yes"; AM_CFLAGS="$AM_CFLAGS -DHAVE_HKDF"])
fi

# Hash DRBG
AC_ARG_ENABLE([hashdrbg],
[AS_HELP_STRING([--enable-hashdrbg],[Enable Hash DRBG support (default: enabled)])],
Expand Down Expand Up @@ -11548,6 +11562,7 @@ AM_CONDITIONAL([BUILD_CHACHA],[test "x$ENABLED_CHACHA" = "xyes" || test "x$ENABL
AM_CONDITIONAL([BUILD_CHACHA_NOASM],[test "$ENABLED_CHACHA" = "noasm"])
AM_CONDITIONAL([BUILD_XCHACHA],[test "x$ENABLED_XCHACHA" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
AM_CONDITIONAL([BUILD_ASCON],[test "x$ENABLED_ASCON" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
AM_CONDITIONAL([BUILD_PUF],[test "x$ENABLED_PUF" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
AM_CONDITIONAL([BUILD_SM2],[test "x$ENABLED_SM2" != "xno" || test "x$ENABLED_USERSETTINGS" = "xyes"])
AM_CONDITIONAL([BUILD_SM3],[test "x$ENABLED_SM3" != "xno" || test "x$ENABLED_USERSETTINGS" = "xyes"])
AM_CONDITIONAL([BUILD_SM4],[test "x$ENABLED_SM4" != "xno" || test "x$ENABLED_USERSETTINGS" = "xyes"])
Expand Down Expand Up @@ -12214,6 +12229,7 @@ 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"
echo " * PUF: $ENABLED_PUF"
echo ""
echo "---"

Expand Down
1 change: 1 addition & 0 deletions doc/dox_comments/header_files/doxygen_groups.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@
\defgroup PKCS11 Algorithms - PKCS11
\defgroup Password Algorithms - Password Based
\defgroup Poly1305 Algorithms - Poly1305
\defgroup PUF Algorithms - PUF
\defgroup RIPEMD Algorithms - RIPEMD
\defgroup RSA Algorithms - RSA
\defgroup SHA Algorithms - SHA 128/224/256/384/512
Expand Down
211 changes: 211 additions & 0 deletions doc/dox_comments/header_files/puf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
/*!
\ingroup PUF

For a complete bare-metal example (tested on NUCLEO-H563ZI), see
https://github.com/wolfSSL/wolfssl-examples/tree/master/puf
*/

/*!
\ingroup PUF

\brief Initialize a wc_PufCtx structure, zeroing all fields.
Must be called before any other PUF operations.

\return 0 on success
\return BAD_FUNC_ARG if ctx is NULL

\param ctx pointer to wc_PufCtx structure to initialize

_Example_
\code
wc_PufCtx ctx;
ret = wc_PufInit(&ctx);
\endcode

\sa wc_PufReadSram
\sa wc_PufEnroll
\sa wc_PufZeroize
*/
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.

\return 0 on success
\return BAD_FUNC_ARG if ctx or sramAddr is NULL
\return PUF_READ_E if sramSz < WC_PUF_RAW_BYTES

\param ctx pointer to wc_PufCtx structure
\param sramAddr pointer to raw SRAM memory region
\param sramSz size of SRAM buffer (must be >= WC_PUF_RAW_BYTES)

_Example_
\code
__attribute__((section(".puf_sram")))
static volatile uint8_t puf_sram[256];
wc_PufReadSram(&ctx, (const byte*)puf_sram, sizeof(puf_sram));
\endcode

\sa wc_PufInit
\sa wc_PufEnroll
\sa wc_PufReconstruct
*/
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.

\return 0 on success
\return BAD_FUNC_ARG if ctx is NULL
\return PUF_ENROLL_E if enrollment fails

\param ctx pointer to wc_PufCtx (must have SRAM data loaded)

_Example_
\code
wc_PufEnroll(&ctx);
XMEMCPY(helperData, ctx.helperData, WC_PUF_HELPER_BYTES);
\endcode

\sa wc_PufReadSram
\sa wc_PufReconstruct
\sa wc_PufDeriveKey
*/
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.

\return 0 on success
\return BAD_FUNC_ARG if ctx or helperData is NULL
\return PUF_RECONSTRUCT_E on failure (too many bit errors or helperSz
too small)

\param ctx pointer to wc_PufCtx (must have SRAM data loaded)
\param helperData pointer to helper data from previous enrollment
\param helperSz size of helper data (>= WC_PUF_HELPER_BYTES)

_Example_
\code
wc_PufReconstruct(&ctx, helperData, sizeof(helperData));
\endcode

\sa wc_PufEnroll
\sa wc_PufDeriveKey
\sa wc_PufGetIdentity
*/
int wc_PufReconstruct(wc_PufCtx* ctx, const byte* helperData, word32 helperSz);

/*!
\ingroup PUF

\brief Derive a cryptographic key from PUF stable bits using HKDF.
Uses SHA-256 by default, or SHA3-256 when WC_PUF_SHA3 is defined.
The info parameter provides domain separation for multiple keys.
Requires HAVE_HKDF.

\return 0 on success
\return BAD_FUNC_ARG if ctx or key is NULL, or keySz is 0
\return PUF_DERIVE_KEY_E if PUF not ready or HKDF fails

\param ctx pointer to wc_PufCtx (must be enrolled or reconstructed)
\param info optional context info for domain separation (may be NULL)
\param infoSz size of info in bytes
\param key output buffer for derived key
\param keySz desired key size in bytes

_Example_
\code
byte key[32];
const byte info[] = "my-app-key";
wc_PufDeriveKey(&ctx, info, sizeof(info), key, sizeof(key));
\endcode

\sa wc_PufEnroll
\sa wc_PufReconstruct
\sa wc_PufGetIdentity
*/
int wc_PufDeriveKey(wc_PufCtx* ctx, const byte* info, word32 infoSz,
byte* key, word32 keySz);

/*!
\ingroup PUF

\brief Retrieve the device identity hash (SHA-256 or SHA3-256 of stable
bits). Deterministic for a given device.

\return 0 on success
\return BAD_FUNC_ARG if ctx or id is NULL
\return PUF_IDENTITY_E if PUF not ready or idSz < WC_PUF_ID_SZ

\param ctx pointer to wc_PufCtx (must be enrolled or reconstructed)
\param id output buffer for identity hash
\param idSz size of id buffer (>= WC_PUF_ID_SZ, 32 bytes)

_Example_
\code
byte identity[WC_PUF_ID_SZ];
wc_PufGetIdentity(&ctx, identity, sizeof(identity));
\endcode

\sa wc_PufEnroll
\sa wc_PufReconstruct
\sa wc_PufDeriveKey
*/
int wc_PufGetIdentity(wc_PufCtx* ctx, byte* id, word32 idSz);

/*!
\ingroup PUF

\brief Securely zeroize all sensitive data in the PUF context using
ForceZero. Call when PUF is no longer needed.

\return 0 on success
\return BAD_FUNC_ARG if ctx is NULL

\param ctx pointer to wc_PufCtx to zeroize

_Example_
\code
wc_PufZeroize(&ctx);
\endcode

\sa wc_PufInit
*/
int wc_PufZeroize(wc_PufCtx* ctx);

/*!
\ingroup PUF

\brief Inject synthetic SRAM test data for testing without hardware.
Only available when WOLFSSL_PUF_TEST is defined.

\return 0 on success
\return BAD_FUNC_ARG if ctx or data is NULL
\return PUF_READ_E if sz < WC_PUF_RAW_BYTES

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

_Example_
\code
byte testSram[WC_PUF_RAW_BYTES];
wc_PufSetTestData(&ctx, testSram, sizeof(testSram));
\endcode

\sa wc_PufInit
\sa wc_PufReadSram
*/
int wc_PufSetTestData(wc_PufCtx* ctx, const byte* data, word32 sz);
4 changes: 4 additions & 0 deletions src/include.am
Original file line number Diff line number Diff line change
Expand Up @@ -1352,6 +1352,10 @@ if BUILD_ASCON
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/ascon.c
endif

if BUILD_PUF
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/puf.c
endif

if !BUILD_INLINE
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/misc.c
endif
Expand Down
18 changes: 18 additions & 0 deletions wolfcrypt/src/error.c
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,24 @@ const char* wc_GetErrorString(int error)
case SEQ_OVERFLOW_E:
return "Sequence counter would overflow";

case PUF_INIT_E:
return "PUF initialization failed";

case PUF_READ_E:
return "PUF SRAM read failed";

case PUF_ENROLL_E:
return "PUF enrollment failed";

case PUF_RECONSTRUCT_E:
return "PUF reconstruction failed";

case PUF_DERIVE_KEY_E:
return "PUF key derivation failed";

case PUF_IDENTITY_E:
return "PUF identity retrieval failed";

case MAX_CODE_E:
case WC_SPAN1_MIN_CODE_E:
case MIN_CODE_E:
Expand Down
Loading
Loading