From 41614d10ed6e2cd4a6e1f4350db4af919c621d39 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 19 Feb 2026 09:11:02 -0800 Subject: [PATCH] Add STM32 hardware HMAC support --- .wolfssl_known_macro_extras | 2 + wolfcrypt/src/hmac.c | 84 +++++++++++ wolfcrypt/src/port/st/README.md | 1 + wolfcrypt/src/port/st/stm32.c | 232 +++++++++++++++++++++++++++--- wolfssl/wolfcrypt/hmac.h | 7 + wolfssl/wolfcrypt/port/st/stm32.h | 16 +++ wolfssl/wolfcrypt/settings.h | 4 + 7 files changed, 326 insertions(+), 20 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 9611aed33f3..f59a604a4a9 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -252,6 +252,7 @@ GOAHEAD_WS HAL_RTC_MODULE_ENABLED HARDWARE_CACHE_COHERENCY HASH_AlgoMode_HASH +HASH_AlgoMode_HMAC HASH_BYTE_SWAP HASH_CR_LKEY HASH_DIGEST @@ -432,6 +433,7 @@ NO_SESSION_CACHE_ROW_LOCK NO_SKID NO_SKIP_PREVIEW NO_STDIO_FGETS_REMAP +NO_STM32_HMAC NO_TKERNEL_MEM_POOL NO_TLSX_PSKKEM_PLAIN_ANNOUNCE NO_VERIFY_OID diff --git a/wolfcrypt/src/hmac.c b/wolfcrypt/src/hmac.c index 1493a1df46e..9d1576d1b8e 100644 --- a/wolfcrypt/src/hmac.c +++ b/wolfcrypt/src/hmac.c @@ -533,6 +533,59 @@ int wc_HmacSetKey_ex(Hmac* hmac, int type, const byte* key, word32 length, return 0; #else +#if defined(STM32_HASH) && defined(STM32_HMAC) + { + word32 stmAlgo, stmBlockSize, stmDigestSize; + /* Check if this hash type is supported by STM32 HMAC hardware */ + if (wc_Stm32_Hmac_GetAlgoInfo(type, &stmAlgo, &stmBlockSize, + &stmDigestSize) == 0) { + /* Cache algo info for Update/Final */ + hmac->stmAlgo = stmAlgo; + hmac->stmBlockSize = stmBlockSize; + hmac->stmDigestSize = stmDigestSize; + + /* Store raw key in ipad (unused in HW HMAC mode). + * Pre-hash if longer than hash block size. */ + if (length <= stmBlockSize) { + if (key != NULL) { + XMEMCPY(hmac->ipad, key, length); + } + hmac->stmKeyLen = length; + } + else { + /* Pre-hash long key using stmCtx (re-initialized below) */ + wc_Stm32_Hash_Init(&hmac->stmCtx); + ret = wolfSSL_CryptHwMutexLock(); + if (ret == 0) { + ret = wc_Stm32_Hash_Update(&hmac->stmCtx, stmAlgo, + key, length, stmBlockSize); + if (ret == 0) { + ret = wc_Stm32_Hash_Final(&hmac->stmCtx, stmAlgo, + (byte*)hmac->ipad, stmDigestSize); + } + wolfSSL_CryptHwMutexUnLock(); + } + if (ret != 0) + return ret; + hmac->stmKeyLen = stmDigestSize; + } + + /* HW HMAC Phase 1: feed key */ + ret = wolfSSL_CryptHwMutexLock(); + if (ret == 0) { + ret = wc_Stm32_Hmac_SetKey(&hmac->stmCtx, type, + (const byte*)hmac->ipad, hmac->stmKeyLen); + wolfSSL_CryptHwMutexUnLock(); + } + if (ret == 0) { + hmac->innerHashKeyed = WC_HMAC_INNER_HASH_KEYED_DEV; + } + return ret; + } + /* Unsupported algo falls through to software */ + } +#endif /* STM32_HASH && STM32_HMAC */ + ip = (byte*)hmac->ipad; op = (byte*)hmac->opad; @@ -853,6 +906,18 @@ int wc_HmacUpdate(Hmac* hmac, const byte* msg, word32 length) } #endif /* WOLFSSL_ASYNC_CRYPT */ +#if defined(STM32_HASH) && defined(STM32_HMAC) + if (hmac->innerHashKeyed == WC_HMAC_INNER_HASH_KEYED_DEV) { + ret = wolfSSL_CryptHwMutexLock(); + if (ret == 0) { + ret = wc_Stm32_Hmac_Update(&hmac->stmCtx, hmac->stmAlgo, + msg, length, hmac->stmBlockSize); + wolfSSL_CryptHwMutexUnLock(); + } + return ret; + } +#endif /* STM32_HASH && STM32_HMAC */ + if (!hmac->innerHashKeyed) { #ifndef WOLFSSL_HMAC_COPY_HASH ret = HmacKeyHashUpdate(hmac->macType, &hmac->hash, (byte*)hmac->ipad); @@ -970,6 +1035,25 @@ int wc_HmacFinal(Hmac* hmac, byte* hash) } #endif /* WOLFSSL_ASYNC_CRYPT */ +#if defined(STM32_HASH) && defined(STM32_HMAC) + if (hmac->innerHashKeyed == WC_HMAC_INNER_HASH_KEYED_DEV) { + ret = wolfSSL_CryptHwMutexLock(); + if (ret == 0) { + ret = wc_Stm32_Hmac_Final(&hmac->stmCtx, hmac->stmAlgo, + (const byte*)hmac->ipad, hmac->stmKeyLen, hash, + hmac->stmDigestSize); + /* Re-run Phase 1 so HMAC is ready for next Update/Final cycle + * (needed for PRF/HKDF loops that reuse the same key) */ + if (ret == 0) { + ret = wc_Stm32_Hmac_SetKey(&hmac->stmCtx, hmac->macType, + (const byte*)hmac->ipad, hmac->stmKeyLen); + } + wolfSSL_CryptHwMutexUnLock(); + } + return ret; + } +#endif /* STM32_HASH && STM32_HMAC */ + if (!hmac->innerHashKeyed) { #ifndef WOLFSSL_HMAC_COPY_HASH ret = HmacKeyHashUpdate(hmac->macType, &hmac->hash, (byte*)hmac->ipad); diff --git a/wolfcrypt/src/port/st/README.md b/wolfcrypt/src/port/st/README.md index cc032eeaa4b..0bcd0244bdf 100644 --- a/wolfcrypt/src/port/st/README.md +++ b/wolfcrypt/src/port/st/README.md @@ -41,6 +41,7 @@ To disable portions of the hardware acceleration you can optionally define: #define NO_STM32_RNG #define NO_STM32_CRYPTO #define NO_STM32_HASH +#define NO_STM32_HMAC ``` ### Coding diff --git a/wolfcrypt/src/port/st/stm32.c b/wolfcrypt/src/port/st/stm32.c index 1dcc04fa792..429c256a6fe 100644 --- a/wolfcrypt/src/port/st/stm32.c +++ b/wolfcrypt/src/port/st/stm32.c @@ -162,12 +162,13 @@ static void wc_Stm32_Hash_SaveContext(STM32_HASH_Context* ctx) #endif } -static void wc_Stm32_Hash_RestoreContext(STM32_HASH_Context* ctx, int algo) +static void wc_Stm32_Hash_RestoreContext(STM32_HASH_Context* ctx, word32 algo, + word32 mode) { int i; if (ctx->HASH_CR == 0) { - /* init content */ + /* init context */ #if defined(HASH_IMR_DINIE) && defined(HASH_IMR_DCIE) /* Disable IRQ's - wolfSSL does not use the HASH/RNG IRQ @@ -175,37 +176,31 @@ static void wc_Stm32_Hash_RestoreContext(STM32_HASH_Context* ctx, int algo) HASH->IMR &= ~(HASH_IMR_DINIE | HASH_IMR_DCIE); #endif - /* reset the control register */ - HASH->CR &= ~(HASH_CR_ALGO | HASH_CR_MODE | HASH_CR_DATATYPE - #ifdef HASH_CR_LKEY - | HASH_CR_LKEY - #endif - ); - - /* configure algorithm, mode and data type */ - HASH->CR |= (algo | HASH_ALGOMODE_HASH | HASH_DATATYPE_8B); - - /* reset HASH processor */ - HASH->CR |= HASH_CR_INIT; + /* Configure algorithm, mode, data type and initialize HASH processor. + * INIT must be written in the same register write as ALGO because + * setting INIT resets ALGO bits to their default value (MD5). */ + HASH->CR = (algo | mode | HASH_DATATYPE_8B | HASH_CR_INIT); /* by default mark all bits valid */ wc_Stm32_Hash_NumValidBits(0); #ifdef DEBUG_STM32_HASH - printf("STM Init algo %x\n", algo); + printf("STM Init algo %x, mode %x, CR %lx, SR %lx\n", + (unsigned int)algo, (unsigned int)mode, + HASH->CR, HASH->SR); #endif } else { /* restore context registers */ HASH->IMR = ctx->HASH_IMR; HASH->STR = ctx->HASH_STR; - HASH->CR = ctx->HASH_CR; #ifdef STM32_HASH_SHA3 HASH->SHA3CFGR = ctx->SHA3CFGR; #endif - /* Initialize the hash processor */ - HASH->CR |= HASH_CR_INIT; + /* Restore CR with INIT in a single write - setting INIT resets ALGO + * bits, so we must include the saved CR value in the same write. */ + HASH->CR = ctx->HASH_CR | HASH_CR_INIT; /* continue restoring context registers */ for (i=0; ibuffLen > 0) { @@ -444,6 +439,203 @@ int wc_Stm32_Hash_Final(STM32_HASH_Context* stmCtx, word32 algo, return ret; } +#if defined(STM32_HMAC) && !defined(NO_HMAC) + +/* STM32 Port HMAC Functions */ +#include + +int wc_Stm32_Hmac_GetAlgoInfo(int macType, word32* algo, word32* blockSize, + word32* digestSize) +{ + int ret = 0; + + switch (macType) { + #if !defined(NO_MD5) && !defined(STM32_NOMD5) + case WC_MD5: + if (algo) *algo = HASH_AlgoSelection_MD5; + if (blockSize) *blockSize = WC_MD5_BLOCK_SIZE; + if (digestSize) *digestSize = WC_MD5_DIGEST_SIZE; + break; + #endif + #ifndef NO_SHA + case WC_SHA: + if (algo) *algo = HASH_AlgoSelection_SHA1; + if (blockSize) *blockSize = WC_SHA_BLOCK_SIZE; + if (digestSize) *digestSize = WC_SHA_DIGEST_SIZE; + break; + #endif + #ifdef WOLFSSL_SHA224 + case WC_SHA224: + if (algo) *algo = HASH_AlgoSelection_SHA224; + if (blockSize) *blockSize = WC_SHA224_BLOCK_SIZE; + if (digestSize) *digestSize = WC_SHA224_DIGEST_SIZE; + break; + #endif + #ifndef NO_SHA256 + case WC_SHA256: + if (algo) *algo = HASH_AlgoSelection_SHA256; + if (blockSize) *blockSize = WC_SHA256_BLOCK_SIZE; + if (digestSize) *digestSize = WC_SHA256_DIGEST_SIZE; + break; + #endif + #if defined(STM32_HASH_SHA384) && defined(WOLFSSL_SHA384) + case WC_SHA384: + if (algo) *algo = HASH_ALGOSELECTION_SHA384; + if (blockSize) *blockSize = WC_SHA384_BLOCK_SIZE; + if (digestSize) *digestSize = WC_SHA384_DIGEST_SIZE; + break; + #endif + #if defined(STM32_HASH_SHA512) && defined(WOLFSSL_SHA512) + case WC_SHA512: + if (algo) *algo = HASH_ALGOSELECTION_SHA512; + if (blockSize) *blockSize = WC_SHA512_BLOCK_SIZE; + if (digestSize) *digestSize = WC_SHA512_DIGEST_SIZE; + break; + #endif + default: + ret = BAD_FUNC_ARG; + break; + } + + return ret; +} + +static void wc_Stm32_Hmac_FeedKey(const byte* key, word32 keySz) +{ + word32 i, blocks; + word32 tmp; + + /* feed key words into HASH->DIN */ + blocks = keySz / STM32_HASH_REG_SIZE; + for (i = 0; i < blocks; i++) { + XMEMCPY(&tmp, key + (i * STM32_HASH_REG_SIZE), STM32_HASH_REG_SIZE); + HASH->DIN = tmp; + } + /* handle remaining bytes in last partial word */ + if (keySz % STM32_HASH_REG_SIZE) { + tmp = 0; + XMEMCPY(&tmp, key + (blocks * STM32_HASH_REG_SIZE), + keySz % STM32_HASH_REG_SIZE); + HASH->DIN = tmp; + } + +#ifdef DEBUG_STM32_HASH + printf("STM HMAC FeedKey %d bytes\n", (int)keySz); +#endif +} + + +/* STM32 HMAC Exposed Functions */ + +int wc_Stm32_Hmac_SetKey(STM32_HASH_Context* stmCtx, int macType, + const byte* key, word32 keySz) +{ + int ret; + word32 algo, blockSize, digestSize; + word32 mode; + + if (stmCtx == NULL || key == NULL) + return BAD_FUNC_ARG; + + ret = wc_Stm32_Hmac_GetAlgoInfo(macType, &algo, &blockSize, &digestSize); + if (ret != 0) + return ret; + +#ifdef DEBUG_STM32_HASH + printf("STM HMAC SetKey: macType %d, keySz %d\n", macType, (int)keySz); +#endif + + /* clear context for fresh HMAC */ + wc_Stm32_Hash_Init(stmCtx); + + /* turn on hash clock */ + STM32_HASH_CLOCK_ENABLE(stmCtx); + + /* initialize hardware for HMAC mode. + * Keys are always pre-hashed in software before reaching this point + * (see hmac.c), so keySz will always be <= blockSize here. */ + mode = HASH_ALGOMODE_HMAC; + wc_Stm32_Hash_RestoreContext(stmCtx, algo, mode); + + /* Phase 1: Feed key into HASH->DIN */ + wc_Stm32_Hmac_FeedKey(key, keySz); + + /* set number of valid bits in last word and trigger DCAL */ + wc_Stm32_Hash_NumValidBits(keySz); + HASH->STR |= HASH_STR_DCAL; + + /* wait for data input ready (phase 1 complete) */ + ret = wc_Stm32_Hash_WaitDataReady(stmCtx); + + if (ret == 0) { + /* save context for context switching */ + wc_Stm32_Hash_SaveContext(stmCtx); + } + + /* turn off hash clock */ + STM32_HASH_CLOCK_DISABLE(stmCtx); + + return ret; +} + +int wc_Stm32_Hmac_Final(STM32_HASH_Context* stmCtx, word32 algo, + const byte* key, word32 keySz, byte* hash, word32 digestSize) +{ + int ret; + + if (stmCtx == NULL || key == NULL || hash == NULL) + return BAD_FUNC_ARG; + +#ifdef DEBUG_STM32_HASH + printf("STM HMAC Final: algo %x, keySz %d, buffLen %d, fifoBytes %d\n", + (unsigned int)algo, (int)keySz, (int)stmCtx->buffLen, + (int)stmCtx->fifoBytes); +#endif + + /* turn on hash clock */ + STM32_HASH_CLOCK_ENABLE(stmCtx); + + /* restore HMAC context */ + wc_Stm32_Hash_RestoreContext(stmCtx, algo, HASH_ALGOMODE_HMAC); + + /* finish reading any trailing bytes into FIFO */ + if (stmCtx->buffLen > 0) { + wc_Stm32_Hash_Data(stmCtx, stmCtx->buffLen); + } + + /* Phase 2 complete: set valid bits and trigger DCAL */ + wc_Stm32_Hash_NumValidBits(stmCtx->loLen + stmCtx->buffLen); + HASH->STR |= HASH_STR_DCAL; + + /* wait for data input ready (phase 2 complete, ready for phase 3) */ + ret = wc_Stm32_Hash_WaitDataReady(stmCtx); + if (ret != 0) { + STM32_HASH_CLOCK_DISABLE(stmCtx); + return ret; + } + + /* Phase 3: Feed key again into HASH->DIN */ + wc_Stm32_Hmac_FeedKey(key, keySz); + + /* set valid bits for key and trigger DCAL */ + wc_Stm32_Hash_NumValidBits(keySz); + HASH->STR |= HASH_STR_DCAL; + + /* wait for hash done (digest computation complete) */ + ret = wc_Stm32_Hash_WaitCalcComp(stmCtx); + if (ret == 0) { + /* read message digest */ + wc_Stm32_Hash_GetDigest(hash, digestSize); + } + + /* turn off hash clock */ + STM32_HASH_CLOCK_DISABLE(stmCtx); + + return ret; +} + +#endif /* STM32_HMAC && !NO_HMAC */ + #endif /* STM32_HASH */ diff --git a/wolfssl/wolfcrypt/hmac.h b/wolfssl/wolfcrypt/hmac.h index a73ddf41dad..1dd946f3d40 100644 --- a/wolfssl/wolfcrypt/hmac.h +++ b/wolfssl/wolfcrypt/hmac.h @@ -158,6 +158,13 @@ struct Hmac { #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB) word16 keyLen; /* hmac key length (key in ipad) */ #endif +#if defined(STM32_HASH) && defined(STM32_HMAC) + STM32_HASH_Context stmCtx; + word32 stmAlgo; /* cached STM32 HASH algo selection */ + word32 stmBlockSize; /* cached hash block size */ + word32 stmDigestSize; /* cached digest size */ + word32 stmKeyLen; /* key length (raw key stored in ipad) */ +#endif }; #ifndef WC_HMAC_TYPE_DEFINED diff --git a/wolfssl/wolfcrypt/port/st/stm32.h b/wolfssl/wolfcrypt/port/st/stm32.h index 142b5222466..e0739766191 100644 --- a/wolfssl/wolfcrypt/port/st/stm32.h +++ b/wolfssl/wolfcrypt/port/st/stm32.h @@ -76,6 +76,9 @@ #if !defined(HASH_ALGOMODE_HASH) && defined(HASH_AlgoMode_HASH) #define HASH_ALGOMODE_HASH HASH_AlgoMode_HASH #endif +#if !defined(HASH_ALGOMODE_HMAC) && defined(HASH_AlgoMode_HMAC) + #define HASH_ALGOMODE_HMAC HASH_AlgoMode_HMAC +#endif #if !defined(HASH_DATATYPE_8B) #if defined(HASH_DataType_8b) #define HASH_DATATYPE_8B HASH_DataType_8b @@ -131,6 +134,19 @@ int wc_Stm32_Hash_Update(STM32_HASH_Context* stmCtx, word32 algo, int wc_Stm32_Hash_Final(STM32_HASH_Context* stmCtx, word32 algo, byte* hash, word32 digestSize); +#ifdef STM32_HMAC +/* STM32 Hardware HMAC API */ +int wc_Stm32_Hmac_GetAlgoInfo(int macType, word32* algo, word32* blockSize, + word32* digestSize); +int wc_Stm32_Hmac_SetKey(STM32_HASH_Context* stmCtx, int macType, + const byte* key, word32 keySz); +/* HMAC Update uses the same data feeding as Hash Update */ +#define wc_Stm32_Hmac_Update(stmCtx, algo, data, len, blockSize) \ + wc_Stm32_Hash_Update((stmCtx), (algo), (data), (len), (blockSize)) +int wc_Stm32_Hmac_Final(STM32_HASH_Context* stmCtx, word32 algo, + const byte* key, word32 keySz, byte* hash, word32 digestSize); +#endif /* STM32_HMAC */ + #endif /* STM32_HASH */ diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index e9943fb270b..0020a05a339 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -2192,6 +2192,10 @@ extern void uITRON4_free(void *p) ; #undef STM32_HASH #define STM32_HASH #endif + #ifndef NO_STM32_HMAC + #undef STM32_HMAC + #define STM32_HMAC + #endif #if !defined(__GNUC__) && !defined(__ICCARM__) #define KEIL_INTRINSICS #endif