diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 583422bea44..488328659db 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -1,4 +1,10 @@ +AES +AES1 +AES_CR_CCFC AES_GCM_GMULT_NCT +AES_ICR_CCF +AES_ISR_CCF +AES_SR_CCF AFX_RESOURCE_DLL AFX_TARG_ENU ALLOW_BINARY_MISMATCH_INTROSPECTION @@ -269,7 +275,11 @@ HARDWARE_CACHE_COHERENCY HASH_AlgoMode_HASH HASH_AlgoMode_HMAC HASH_BYTE_SWAP +HASH_CR_ALGO_1 +HASH_CR_DATATYPE_0 +HASH_CR_DATATYPE_1 HASH_CR_LKEY +HASH_CR_MODE HASH_DIGEST HASH_DataType_8b HASH_IMR_DCIE @@ -491,7 +501,11 @@ OTHER_BOARD O_CLOEXEC PEER_INFO PERF_FLAG_FD_CLOEXEC +PKA_CLRFR_OPERRFC +PKA_CR_OPERRIE PKA_ECC_SCALAR_MUL_IN_B_COEFF +PKA_SR_INITOK +PKA_SR_OPERRF PLATFORMIO PLUTON_CRYPTO_ECC PRINT_SESSION_STATS @@ -499,6 +513,24 @@ PTHREAD_STACK_MIN QAT_ENABLE_HASH QAT_ENABLE_RNG QAT_USE_POLLING_CHECK +RCC_AHB1ENR_PKAEN +RCC_AHB2ENR1_AESEN +RCC_AHB2ENR1_HASHEN +RCC_AHB2ENR1_PKAEN +RCC_AHB2ENR1_SAESEN +RCC_AHB2ENR_AESEN +RCC_AHB2ENR_HASHEN +RCC_AHB2ENR_PKAEN +RCC_AHB2ENR_SAESEN +RCC_AHB3ENR_AESEN +RCC_AHB3ENR_CRYPEN +RCC_AHB3ENR_HASHEN +RCC_AHB3ENR_PKAEN +RCC_AHB3ENR_RNGEN +RCC_AHB3ENR_SAESEN +RCC_MP_AHB5ENSETR_CRYP1EN +RCC_MP_AHB5ENSETR_HASH1EN +RCC_MP_AHB5ENSETR_RNG1EN RC_NO_RNG REDIRECTION_IN3_KEYELMID REDIRECTION_IN3_KEYID @@ -509,11 +541,18 @@ REDIRECTION_OUT2_KEYID RENESAS_T4_USE RHEL_MAJOR RHEL_RELEASE_CODE +RNG_CAND_NIST_CR_VALUE +RNG_CAND_NIST_HTCR_VALUE +RNG_CAND_NIST_NSCR_VALUE +RNG_CR_CONDRST +RNG_SR_BUSY RTC_ALARMSUBSECONDMASK_ALL RTE_CMSIS_RTOS_RTX RTOS_MODULE_NET_AVAIL RTPLATFORM SAL_IOMMU_CODE +SAES +SAES_CR_EN SA_INTERRUPT SCEKEY_INSTALLED SHA256_MANY_REGISTERS @@ -575,6 +614,7 @@ STM32WB55xx STM32WBA52xx STM32WL55xx STM32_AESGCM_PARTIAL +STM32_AES_CLEAR_INST STM32_HW_CLOCK_AUTO STM32_NUTTX_RNG STSAFE_HOST_KEY_CIPHER @@ -679,6 +719,11 @@ WC_SLHDSA_NO_ASM WC_SLHDSA_VERBOSE_DEBUG WC_SSIZE_TYPE WC_STRICT_SIG +WC_STM32_PKA_DIAG +WC_STM32_RNG_CED_DISABLE +WC_STM32_RNG_DIAG +WC_STM32_RNG_NO_NIST_INIT +WC_STM32_SAES_DIAG WC_USE_PIE_FENCEPOSTS_FOR_FIPS WC_WANT_FLAG_DONT_USE_VECTOR_OPS WIFIESPAT @@ -918,9 +963,13 @@ WOLFSSL_SP_ARM32_UDIV WOLFSSL_SP_FAST_NCT_EXPTMOD WOLFSSL_SP_INT_SQR_VOLATILE WOLFSSL_STACK_CHECK +WOLFSSL_STM32C5 +WOLFSSL_STM32F3 WOLFSSL_STM32F427_RNG -WOLFSSL_STM32U5_DHUK -WOLFSSL_STM32_RNG_NOLIB +WOLFSSL_STM32U0 +WOLFSSL_STM32_BARE +WOLFSSL_STM32_DHUK_UNWRAP +WOLFSSL_STM32_USE_SAES WOLFSSL_STRONGEST_HASH_SIG WOLFSSL_STSAFE_TAKES_SLOT WOLFSSL_TELIT_M2MB diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 7ba2317d55c..c7af1425d14 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -233,6 +233,16 @@ block cipher mechanism that uses n-bit binary string parameter key with 128-bits static WARN_UNUSED_RESULT int wc_AesEncrypt( Aes* aes, const byte* inBlock, byte* outBlock) { + #ifdef WOLFSSL_STM32_BARE + /* Bare-metal driver handles mutex, clock and key/IV internally. */ + #ifdef WOLFSSL_DHUK + if (aes->devId == WOLFSSL_DHUK_WRAPPED_DEVID) { + return wc_Stm32_Aes_DhukOp(aes, outBlock, inBlock, + WC_AES_BLOCK_SIZE, 1 /* encrypt */); + } + #endif + return wc_Stm32_Aes_Ecb(aes, outBlock, inBlock, WC_AES_BLOCK_SIZE, 1); + #else int ret = 0; #ifdef WOLFSSL_STM32_CUBEMX CRYP_HandleTypeDef hcryp; @@ -247,13 +257,13 @@ block cipher mechanism that uses n-bit binary string parameter key with 128-bits return ret; #endif - #ifdef WOLFSSL_STM32U5_DHUK + #ifdef WOLFSSL_DHUK ret = wolfSSL_CryptHwMutexLock(); if (ret != 0) return ret; /* Handle making use of wrapped key */ - if (aes->devId == WOLFSSL_STM32U5_DHUK_WRAPPED_DEVID) { + if (aes->devId == WOLFSSL_DHUK_WRAPPED_DEVID) { CRYP_ConfigTypeDef Config = {0}; ret = wc_Stm32_Aes_UnWrap(aes, &hcryp, (const byte*)aes->key, @@ -373,6 +383,7 @@ block cipher mechanism that uses n-bit binary string parameter key with 128-bits wc_Stm32_Aes_Cleanup(); return ret; + #endif /* !WOLFSSL_STM32_BARE */ } #endif /* WOLFSSL_AES_DIRECT || HAVE_AESGCM || HAVE_AESCCM */ @@ -381,6 +392,15 @@ block cipher mechanism that uses n-bit binary string parameter key with 128-bits static WARN_UNUSED_RESULT int wc_AesDecrypt( Aes* aes, const byte* inBlock, byte* outBlock) { + #ifdef WOLFSSL_STM32_BARE + #ifdef WOLFSSL_DHUK + if (aes->devId == WOLFSSL_DHUK_WRAPPED_DEVID) { + return wc_Stm32_Aes_DhukOp(aes, outBlock, inBlock, + WC_AES_BLOCK_SIZE, 0 /* decrypt */); + } + #endif + return wc_Stm32_Aes_Ecb(aes, outBlock, inBlock, WC_AES_BLOCK_SIZE, 0); + #else int ret = 0; #ifdef WOLFSSL_STM32_CUBEMX CRYP_HandleTypeDef hcryp; @@ -395,13 +415,13 @@ block cipher mechanism that uses n-bit binary string parameter key with 128-bits return ret; #endif - #ifdef WOLFSSL_STM32U5_DHUK + #ifdef WOLFSSL_DHUK ret = wolfSSL_CryptHwMutexLock(); if (ret != 0) return ret; /* Handle making use of wrapped key */ - if (aes->devId == WOLFSSL_STM32U5_DHUK_WRAPPED_DEVID) { + if (aes->devId == WOLFSSL_DHUK_WRAPPED_DEVID) { CRYP_ConfigTypeDef Config; XMEMSET(&Config, 0, sizeof(Config)); @@ -527,6 +547,7 @@ block cipher mechanism that uses n-bit binary string parameter key with 128-bits wc_Stm32_Aes_Cleanup(); return ret; + #endif /* !WOLFSSL_STM32_BARE */ } #endif /* WOLFSSL_AES_DIRECT */ #endif /* HAVE_AES_DECRYPT */ @@ -5594,7 +5615,34 @@ int wc_AesSetIV(Aes* aes, const byte* iv) #ifdef HAVE_AES_CBC #if defined(STM32_CRYPTO) -#ifdef WOLFSSL_STM32U5_DHUK +#ifdef WOLFSSL_STM32_BARE + int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) + { + #ifdef WOLFSSL_AES_CBC_LENGTH_CHECKS + if (sz % WC_AES_BLOCK_SIZE) { + return BAD_LENGTH_E; + } + #endif + if (sz == 0) { + return 0; + } + return wc_Stm32_Aes_Cbc(aes, out, in, sz, 1); + } + #ifdef HAVE_AES_DECRYPT + int wc_AesCbcDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) + { + #ifdef WOLFSSL_AES_CBC_LENGTH_CHECKS + if (sz % WC_AES_BLOCK_SIZE) { + return BAD_LENGTH_E; + } + #endif + if (sz == 0) { + return 0; + } + return wc_Stm32_Aes_Cbc(aes, out, in, sz, 0); + } + #endif /* HAVE_AES_DECRYPT */ +#elif defined(WOLFSSL_DHUK) int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { int ret = 0; @@ -5614,7 +5662,7 @@ int wc_AesSetIV(Aes* aes, const byte* iv) return ret; } - if (aes->devId == WOLFSSL_STM32U5_DHUK_WRAPPED_DEVID) { + if (aes->devId == WOLFSSL_DHUK_WRAPPED_DEVID) { CRYP_ConfigTypeDef Config; XMEMSET(&Config, 0, sizeof(Config)); @@ -5680,7 +5728,7 @@ int wc_AesSetIV(Aes* aes, const byte* iv) return ret; } - if (aes->devId == WOLFSSL_STM32U5_DHUK_WRAPPED_DEVID) { + if (aes->devId == WOLFSSL_DHUK_WRAPPED_DEVID) { CRYP_ConfigTypeDef Config; XMEMSET(&Config, 0, sizeof(Config)); @@ -6977,6 +7025,11 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) int wc_AesCtrEncryptBlock(Aes* aes, byte* out, const byte* in) { + #ifdef WOLFSSL_STM32_BARE + /* CTR per-block transform: ECB-encrypt the counter (passed in + * 'in'); aes.c handles counter increment and XOR with plaintext. */ + return wc_Stm32_Aes_Ecb(aes, out, in, WC_AES_BLOCK_SIZE, 1); + #else int ret = 0; #ifdef WOLFSSL_STM32_CUBEMX CRYP_HandleTypeDef hcryp; @@ -7087,6 +7140,7 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) wolfSSL_CryptHwMutexUnLock(); wc_Stm32_Aes_Cleanup(); return ret; + #endif /* !WOLFSSL_STM32_BARE */ } @@ -10166,6 +10220,7 @@ int wc_AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz, authTag, authTagSz, authIn, authInSz); #endif + #if defined(WOLFSSL_MICROCHIP_TA100) && defined(WOLFSSL_MICROCHIP_AESGCM) #ifndef TA_AES_GCM_MAX_DATA_SIZE #define TA_AES_GCM_MAX_DATA_SIZE 996u @@ -10183,6 +10238,17 @@ int wc_AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz, authIn, authInSz); } #endif + +#if defined(WOLFSSL_STM32_BARE) && defined(STM32_CRYPTO) + ret = wc_Stm32_Aes_Gcm(aes, out, in, sz, iv, ivSz, + authTag, authTagSz, + authIn, authInSz, 1 /* enc */); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return ret; + /* fall through to SW GCM (still uses HW AES via wc_AesEncrypt) */ +#endif /* WOLFSSL_STM32_BARE && STM32_CRYPTO */ + + #ifdef STM32_CRYPTO_AES_GCM return wc_AesGcmEncrypt_STM32( aes, out, in, sz, iv, ivSz, @@ -10927,6 +10993,10 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, } #endif + /* BARE: GCM decrypt always uses SW path (with HW AES blocks via + * wc_AesEncrypt). Encrypt is HW-accelerated above; decrypt + tag + * verification stays in well-tested SW for now. */ + #ifdef STM32_CRYPTO_AES_GCM /* The STM standard peripheral library API's doesn't support partial blocks */ return wc_AesGcmDecrypt_STM32( @@ -13751,7 +13821,7 @@ int wc_AesInit(Aes* aes, void* heap, int devId) aes->heap = heap; -#if defined(WOLF_CRYPTO_CB) || defined(WOLFSSL_STM32U5_DHUK) +#if defined(WOLF_CRYPTO_CB) || defined(WOLFSSL_DHUK) aes->devId = devId; aes->devCtx = NULL; #else diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 57b29197d66..b9f1abc1bfc 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -287,8 +287,9 @@ ECC Curve Sizes: !defined(WOLFSSL_MICROCHIP_TA100) && \ !defined(WOLFSSL_CRYPTOCELL) && !defined(WOLFSSL_SILABS_SE_ACCEL) && \ !defined(WOLFSSL_KCAPI_ECC) && !defined(WOLFSSL_SE050) && \ - !defined(WOLFSSL_STM32_PKA) && !defined(WOLFSSL_PSOC6_CRYPTO) && \ - !defined(WOLFSSL_XILINX_CRYPT_VERSAL) + !defined(WOLFSSL_XILINX_CRYPT_VERSAL) && \ + !defined(WOLFSSL_STM32_PKA) && \ + !defined(WOLFSSL_PSOC6_CRYPTO) #undef HAVE_ECC_VERIFY_HELPER #define HAVE_ECC_VERIFY_HELPER #endif @@ -7018,10 +7019,42 @@ static int deterministic_sign_helper(const byte* in, word32 inlen, ecc_key* key) #endif /* WOLFSSL_ECDSA_DETERMINISTIC_K || WOLFSSL_ECDSA_DETERMINISTIC_K_VARIANT */ +/* WOLFSSL_STM32_PKA routes HW ECDSA sign/verify through the STM32 PKA + * (HAL_PKA_ECDSASign / Verify). Works under both the CubeMX-HAL path + * and the bare-metal direct-register path (WOLFSSL_STM32_BARE) -- the + * bare-metal driver implements the same HAL_PKA_ECDSA* surface. + * + * The non-FIPS input-validation checks (length range, all-zero digest + * rejection) live inside the SW body of wc_ecc_sign_hash_ex below. + * Since the STM32_PKA branch returns early without reaching them, + * mirror those checks here so HW + SW paths share the same input + * contract. Without this, an all-zero digest reaches the PKA IP and + * succeeds at the HW layer -- the wolfcrypt_test ECC sweep then fails + * at the post-call assertion that expected ECC_BAD_ARG_E for a zero + * digest. */ #if defined(WOLFSSL_STM32_PKA) int wc_ecc_sign_hash_ex(const byte* in, word32 inlen, WC_RNG* rng, ecc_key* key, mp_int *r, mp_int *s) { +#ifndef WC_ALLOW_ECC_ZERO_HASH + byte hashIsZero = 0; + word32 zIdx; +#endif + + if (in == NULL || r == NULL || s == NULL || key == NULL || rng == NULL) { + return ECC_BAD_ARG_E; + } + if ((inlen > WC_MAX_DIGEST_SIZE) || (inlen < WC_MIN_DIGEST_SIZE)) { + return BAD_LENGTH_E; + } +#ifndef WC_ALLOW_ECC_ZERO_HASH + /* reject all 0's hash */ + for (zIdx = 0; zIdx < inlen; zIdx++) + hashIsZero |= in[zIdx]; + if (hashIsZero == 0) + return ECC_BAD_ARG_E; +#endif + return stm32_ecc_sign_hash_ex(in, inlen, rng, key, r, s); } #elif !defined(WOLFSSL_ATECC508A) && !defined(WOLFSSL_ATECC608A) && \ @@ -8836,7 +8869,8 @@ int wc_ecc_verify_hash(const byte* sig, word32 siglen, const byte* hash, #ifndef WOLF_CRYPTO_CB_ONLY_ECC -#if !defined(WOLFSSL_STM32_PKA) && !defined(WOLFSSL_PSOC6_CRYPTO) && \ +#if !defined(WOLFSSL_STM32_PKA) && \ + !defined(WOLFSSL_PSOC6_CRYPTO) && \ !defined(WOLF_CRYPTO_CB_ONLY_ECC) static int wc_ecc_check_r_s_range(ecc_key* key, mp_int* r, mp_int* s) { @@ -9354,6 +9388,29 @@ int wc_ecc_verify_hash_ex(mp_int *r, mp_int *s, const byte* hash, word32 hashlen, int* res, ecc_key* key) { #if defined(WOLFSSL_STM32_PKA) + /* HW ECDSA verify via STM32 PKA. Works under both the CubeMX-HAL + * and the bare-metal direct-register paths. Mirror the non-FIPS + * input-validation from the SW body below (length range, all-zero + * digest rejection) so HW + SW share the same input contract. */ +#ifndef WC_ALLOW_ECC_ZERO_HASH + byte hashIsZero = 0; + word32 zIdx; +#endif + + if (r == NULL || s == NULL || hash == NULL || res == NULL || key == NULL) { + return ECC_BAD_ARG_E; + } + if ((hashlen > WC_MAX_DIGEST_SIZE) || (hashlen < WC_MIN_DIGEST_SIZE)) { + return BAD_LENGTH_E; + } +#ifndef WC_ALLOW_ECC_ZERO_HASH + /* reject all 0's hash */ + for (zIdx = 0; zIdx < hashlen; zIdx++) + hashIsZero |= hash[zIdx]; + if (hashIsZero == 0) + return ECC_BAD_ARG_E; +#endif + return stm32_ecc_verify_hash_ex(r, s, hash, hashlen, res, key); #elif defined(WOLFSSL_PSOC6_CRYPTO) return psoc6_ecc_verify_hash_ex(r, s, hash, hashlen, res, key); diff --git a/wolfcrypt/src/port/st/stm32.c b/wolfcrypt/src/port/st/stm32.c index 644b85634f7..93d80c6763d 100644 --- a/wolfcrypt/src/port/st/stm32.c +++ b/wolfcrypt/src/port/st/stm32.c @@ -46,6 +46,13 @@ #ifdef WOLFSSL_STM32_PKA #include +#ifdef WOLFSSL_STM32_BARE +/* Bare-metal: CMSIS device header is pulled in by settings.h. The + * PKA_HandleTypeDef and the PKA_ECC / PKA_ECDSA IO typedefs are + * provided by above. The HAL_PKA_* + * entry points are implemented further down in this file under the + * matching guard. */ +#else #if defined(WOLFSSL_STM32L5) #include #include @@ -76,7 +83,107 @@ #else #error Please add the hal_pk.h include #endif +#endif /* !WOLFSSL_STM32_BARE */ + +#if defined(WOLFSSL_STM32_BARE) && defined(WOLFSSL_STM32_PKA) + +#include + +/* Bare-metal stand-ins for the slice of HAL surface that wc_ecc_*() and + * the local HAL_PKA_* shims reference. Kept private to this translation + * unit so they don't collide with ST HAL headers in projects that include + * those for non-crypto code. */ +typedef enum { + HAL_OK = 0x00U, + HAL_ERROR = 0x01U, + HAL_BUSY = 0x02U, + HAL_TIMEOUT = 0x03U +} HAL_StatusTypeDef; + +#ifndef HAL_MAX_DELAY +#define HAL_MAX_DELAY 0xFFFFFFFFU +#endif + +typedef struct { + PKA_TypeDef *Instance; + /* V2 PKA clobbers RAM[PKA_ECDSA_SIGN_IN_MOD_NB_BITS] during the + * sign operation -- it cannot be read back to determine the + * result size. Mirror the HAL handle and save the modulus size + * (in bytes) at sign-setup time so GetResult can use it. V1 HAL + * reads from RAM and works fine; V2 HAL keeps it on the handle. */ + uint32_t primeordersize; +} PKA_HandleTypeDef; + +typedef struct { + uint32_t modulusSize; + uint32_t coefSign; + const uint8_t *coefA; + const uint8_t *coefB; /* V2 only */ + const uint8_t *modulus; + const uint8_t *primeOrder; /* V2 only */ + uint32_t scalarMulSize; + const uint8_t *scalarMul; + const uint8_t *pointX; + const uint8_t *pointY; +} PKA_ECCMulInTypeDef; + +typedef struct { + uint8_t *ptX; + uint8_t *ptY; +} PKA_ECCMulOutTypeDef; + +typedef struct { + uint32_t primeOrderSize; + uint32_t modulusSize; + uint32_t coefSign; + const uint8_t *coef; + const uint8_t *coefB; /* V2 only */ + const uint8_t *modulus; + const uint8_t *basePointX; + const uint8_t *basePointY; + const uint8_t *primeOrder; + const uint8_t *pPubKeyCurvePtX; + const uint8_t *pPubKeyCurvePtY; + const uint8_t *RSign; + const uint8_t *SSign; + const uint8_t *hash; +} PKA_ECDSAVerifInTypeDef; + +typedef struct { + uint32_t primeOrderSize; + uint32_t modulusSize; + uint32_t coefSign; + const uint8_t *coef; + const uint8_t *coefB; /* V2 only */ + const uint8_t *modulus; + const uint8_t *basePointX; + const uint8_t *basePointY; + const uint8_t *primeOrder; + const uint8_t *hash; + const uint8_t *integer; + const uint8_t *privateKey; +} PKA_ECDSASignInTypeDef; + +typedef struct { + uint8_t *RSign; + uint8_t *SSign; +} PKA_ECDSASignOutTypeDef; + +typedef struct { + uint8_t *ptX; + uint8_t *ptY; +} PKA_ECDSASignOutExtParamTypeDef; + +#endif /* WOLFSSL_STM32_BARE && WOLFSSL_STM32_PKA */ + +#ifdef WOLFSSL_STM32_BARE +/* Provide the global PKA handle that the wc_ecc_mulmod_ex2() and + * stm32_ecc_*_hash_ex() paths reference via &hpka. Under HAL builds, + * the application supplies this; under BARE we own it (file-local). */ +static PKA_HandleTypeDef hpka = { 0 }; +#else extern PKA_HandleTypeDef hpka; +#endif #if !defined(WOLFSSL_STM32_PKA_V2) && defined(PKA_ECC_SCALAR_MUL_IN_B_COEFF) /* PKA hardware like in U5 added coefB and primeOrder */ @@ -92,6 +199,614 @@ extern PKA_HandleTypeDef hpka; #define WOLFSSL_HAVE_ECC_KEY_GET_PRIV #endif #endif /* HAVE_ECC */ + +/* Bare-metal HAL_PKA_* shims -- direct-register slice of ST HAL surface + * used by the wolfssl PKA path. V1 layout (WB55/WL/MP13); V2 PKA (H5/ + * U5+PKA/WBA) adds coefB / primeOrder / pointCheck slots at different + * offsets but shares the start sequence and SR/CLRFR bit names, so the + * V2 differences fold into the same code path under WOLFSSL_STM32_PKA_V2 + * (auto-set when the CMSIS header defines PKA_ECC_SCALAR_MUL_IN_B_COEFF). + * Reference: STM32WBxx_HAL_Driver/Src/stm32wbxx_hal_pka.c. */ +#ifdef WOLFSSL_STM32_BARE + +/* PKA RAM occupies addresses PKA_BASE+0x400 .. PKA_BASE+0x11F4 on V1 and + * a slightly larger window on V2. The CMSIS device header sizes the + * RAM[] array correctly for the part. */ +#ifndef PKA_RAM_PARAM_END +/* The HAL macro `__PKA_RAM_PARAM_END(TAB,IDX)` differs by PKA IP rev: + * - V1 PKA (WB / WL / L5): writes a single zero word at IDX. + * - V2 PKA (WBA / U5 / H5 / N6 / C5 / H7S): writes TWO consecutive + * zero words at IDX and IDX+1. + * On V1 the operand RAM slots are packed tightly and a stray second + * zero overwrites the first word of the next operand -- on WL55 this + * silently corrupts ECDSA sign input (HASH_E or PRIVATE_KEY_D depending + * on which operand is being terminated), producing R/S that don't + * verify against their own pubkey. On V2 the slot spacing is wider and + * the spec requires the double-zero terminator (the PKA microcode + * scans until it sees two zeros). + * + * Match the HAL flow exactly by gating on WOLFSSL_STM32_PKA_V2. */ +#ifdef WOLFSSL_STM32_PKA_V2 +#define PKA_RAM_PARAM_END(RAM, IDX) \ + do { \ + (RAM)[(IDX)] = 0UL; \ + (RAM)[(IDX) + 1U] = 0UL; \ + } while (0) +#else +#define PKA_RAM_PARAM_END(RAM, IDX) \ + do { (RAM)[(IDX)] = 0UL; } while (0) +#endif +#endif + +/* Mode encoding constants (from stm32wbxx_hal_pka.h and equivalent). + * Same numeric values across V1 and V2. */ +#ifndef PKA_MODE_ECC_MUL +#define PKA_MODE_ECC_MUL (0x00000020U) +#endif +#ifndef PKA_MODE_ECDSA_VERIFICATION +#define PKA_MODE_ECDSA_VERIFICATION (0x00000026U) +#endif +#ifndef PKA_MODE_ECDSA_SIGNATURE +#define PKA_MODE_ECDSA_SIGNATURE (0x00000024U) +#endif + +/* Number of word slots in the PKA RAM array (per the CMSIS device + * header; e.g. 894 on WB55 V1). */ +#define WC_STM32_PKA_RAM_WORDS \ + (sizeof(((PKA_TypeDef*)0)->RAM) / sizeof(((PKA_TypeDef*)0)->RAM[0])) + +/* Big-endian byte buffer -> PKA RAM (little-endian word order). The + * destination is the PKA RAM slot indexed by 'word_idx'; n is the byte + * count of the source. Mirrors PKA_Memcpy_u8_to_u32 in the HAL. */ +static void wc_stm32_pka_load_be(volatile uint32_t* dst, const uint8_t* src, + uint32_t n) +{ + uint32_t index = 0; + if (dst == NULL || src == NULL) return; + + for (; index < (n / 4U); index++) { + dst[index] = + ((uint32_t)src[(n - (index * 4U) - 1U)]) | + ((uint32_t)src[(n - (index * 4U) - 2U)] << 8) | + ((uint32_t)src[(n - (index * 4U) - 3U)] << 16) | + ((uint32_t)src[(n - (index * 4U) - 4U)] << 24); + } + if ((n % 4U) == 1U) { + dst[index] = (uint32_t)src[(n - (index * 4U) - 1U)]; + } + else if ((n % 4U) == 2U) { + dst[index] = + ((uint32_t)src[(n - (index * 4U) - 1U)]) | + ((uint32_t)src[(n - (index * 4U) - 2U)] << 8); + } + else if ((n % 4U) == 3U) { + dst[index] = + ((uint32_t)src[(n - (index * 4U) - 1U)]) | + ((uint32_t)src[(n - (index * 4U) - 2U)] << 8) | + ((uint32_t)src[(n - (index * 4U) - 3U)] << 16); + } +} + +/* Load an operand into the PKA RAM at `slot` and append the two-word + * PARAM_END terminator immediately after it. Combines wc_stm32_pka_load_be + * with PKA_RAM_PARAM_END(), which appear paired at every operand-load + * site in HAL_PKA_ECCMul / ECDSAVerif / ECDSASign. */ +static void wc_stm32_pka_load_param_be(volatile uint32_t* ram, uint32_t slot, + const uint8_t* src, uint32_t bytes) +{ + wc_stm32_pka_load_be(&ram[slot], src, bytes); + PKA_RAM_PARAM_END(ram, slot + ((bytes + 3U) / 4U)); +} + +/* Forward decl -- defined later (HAL_PKA_Init lives after this point but + * the helper below is referenced from the ECC/ECDSA shim entries which + * also live later, so the call-site ordering is fine). */ +static HAL_StatusTypeDef wc_stm32_pka_ensure_init(PKA_HandleTypeDef *hpkah); + +/* Common preamble for the PKA setup entries (ECCMul / ECDSAVerif / + * ECDSASign): NULL-guard `hpkah`, run ensure_init, NULL-guard the + * resolved instance, and hand back the RAM pointer. Returns NULL on + * any failure -- caller maps NULL -> HAL_ERROR. */ +static volatile uint32_t* wc_stm32_pka_prep_ram(PKA_HandleTypeDef* hpkah) +{ + HAL_StatusTypeDef st; + if (hpkah == NULL) return NULL; + st = wc_stm32_pka_ensure_init(hpkah); + if (st != HAL_OK) { +#ifdef WC_STM32_PKA_DIAG + printf("PKA prep_ram init failed=%d\n", (int)st); +#endif + return NULL; + } + if (hpkah->Instance == NULL) { +#ifdef WC_STM32_PKA_DIAG + printf("PKA prep_ram Instance NULL\n"); +#endif + return NULL; + } + return hpkah->Instance->RAM; +} + +/* PKA RAM (little-endian word order) -> big-endian byte buffer. */ +static void wc_stm32_pka_read_be(uint8_t* dst, volatile const uint32_t* src, + uint32_t n) +{ + uint32_t i = 0; + if (dst == NULL || src == NULL) return; + + for (; i < (n / 4U); i++) { + uint32_t off = n - 4U - (i * 4U); + dst[off + 3U] = (uint8_t)((src[i] ) & 0xFFU); + dst[off + 2U] = (uint8_t)((src[i] >> 8) & 0xFFU); + dst[off + 1U] = (uint8_t)((src[i] >> 16) & 0xFFU); + dst[off + 0U] = (uint8_t)((src[i] >> 24) & 0xFFU); + } + if ((n % 4U) == 1U) { + dst[0U] = (uint8_t)(src[i] & 0xFFU); + } + else if ((n % 4U) == 2U) { + dst[1U] = (uint8_t)((src[i] ) & 0xFFU); + dst[0U] = (uint8_t)((src[i] >> 8) & 0xFFU); + } + else if ((n % 4U) == 3U) { + dst[2U] = (uint8_t)((src[i] ) & 0xFFU); + dst[1U] = (uint8_t)((src[i] >> 8) & 0xFFU); + dst[0U] = (uint8_t)((src[i] >> 16) & 0xFFU); + } +} + +/* Optimal bit-size: bytes * 8 minus the leading-zero count of the MSB + * (matches PKA_GetOptBitSize_u8 in the HAL). */ +static uint32_t wc_stm32_pka_optbits(uint32_t byteNumber, uint8_t msb) +{ + uint32_t pos = 0; + uint32_t v = msb; + while (v != 0U) { + v >>= 1; + pos++; + } + if (byteNumber == 0U) { + return 0U; + } + return ((byteNumber - 1U) * 8U) + pos; +} + +#ifndef WC_STM32_PKA_INIT_TIMEOUT + #define WC_STM32_PKA_INIT_TIMEOUT 0x40000 +#endif + +static HAL_StatusTypeDef HAL_PKA_Init(PKA_HandleTypeDef *hpkah) +{ + uint32_t t; + + if (hpkah == NULL) { + return HAL_ERROR; + } + if (hpkah->Instance == NULL) { + hpkah->Instance = PKA; + } + +#ifdef WC_STM32_PKA_CLK_ENABLE + WC_STM32_PKA_CLK_ENABLE(); +#endif + + /* Enable the PKA. On L5 / U5 / H5 and friends the IP runs an + * automatic PKA-RAM erase after the first clock-enable; writes to + * CR.EN are silently dropped until the erase completes. Mirror the + * HAL behaviour and spin writing EN until the readback sticks. + * On timeout, clear the Instance pointer so wc_stm32_pka_ensure_init + * will retry on the next call instead of running ops against a + * still-disabled IP. */ + t = 0; + while ((hpkah->Instance->CR & PKA_CR_EN) != PKA_CR_EN) { + hpkah->Instance->CR = PKA_CR_EN; + if (++t >= WC_STM32_PKA_INIT_TIMEOUT) { +#ifdef WC_STM32_PKA_DIAG + printf("PKA Init CR.EN timeout CR=%lx SR=%lx\n", + (unsigned long)hpkah->Instance->CR, + (unsigned long)hpkah->Instance->SR); +#endif + hpkah->Instance = NULL; + return HAL_TIMEOUT; + } + } + +#ifdef PKA_SR_INITOK + /* V2 PKA additionally exposes an INITOK status flag in SR that is + * set when the RAM-erase + self-check sequence completes. The V2 + * HAL_PKA_Init waits for INITOK before returning. Without this + * wait, an immediate ECDSA SIGN can race the init and silently + * fail with OUT_ERROR = 0xCBC9 (PKA_FAILED_COMPUTATION) on U5 / H5 + * / WBA / N6 / C5 / H7S. V1 PKA does not have INITOK and the bit + * is undefined there. */ + t = 0; + while ((hpkah->Instance->SR & PKA_SR_INITOK) == 0U) { + if (++t >= WC_STM32_PKA_INIT_TIMEOUT) { +#ifdef WC_STM32_PKA_DIAG + printf("PKA Init INITOK timeout CR=%lx SR=%lx\n", + (unsigned long)hpkah->Instance->CR, + (unsigned long)hpkah->Instance->SR); +#endif + hpkah->Instance = NULL; + return HAL_TIMEOUT; + } + } +#endif + + /* Clear any pending flags. */ + hpkah->Instance->CLRFR = PKA_CLRFR_PROCENDFC | PKA_CLRFR_RAMERRFC | + PKA_CLRFR_ADDRERRFC; + return HAL_OK; +} + +/* Lazy one-shot init helper. Safe to call from every entry point. + * Returns HAL_OK if the PKA is ready, HAL_ERROR / HAL_TIMEOUT otherwise. + * On failure HAL_PKA_Init resets hpkah->Instance back to NULL so the + * next call retries instead of running ops against a disabled IP. */ +static HAL_StatusTypeDef wc_stm32_pka_ensure_init(PKA_HandleTypeDef *hpkah) +{ + if (hpkah == NULL) return HAL_ERROR; + if (hpkah->Instance == NULL) { + return HAL_PKA_Init(hpkah); + } + return HAL_OK; +} + +static void HAL_PKA_RAMReset(PKA_HandleTypeDef *hpkah) +{ + uint32_t i; + if (hpkah == NULL || hpkah->Instance == NULL) return; + for (i = 0; i < WC_STM32_PKA_RAM_WORDS; i++) { + hpkah->Instance->RAM[i] = 0UL; + } +} + +/* Generic start-and-poll sequence with bounded timeout. The default + * spin budget covers a P-521 scalar mul on a slow PKA (worst case on + * the parts wolfSSL targets is ~2 sec; the budget here is well above + * that). Override at compile time via WC_STM32_PKA_TIMEOUT_LOOPS. */ +#ifndef WC_STM32_PKA_TIMEOUT_LOOPS +#define WC_STM32_PKA_TIMEOUT_LOOPS 0x10000000U +#endif + +static HAL_StatusTypeDef wc_stm32_pka_process(PKA_HandleTypeDef *hpkah, + uint32_t mode) +{ + PKA_TypeDef *p; + uint32_t cr, t; + + if (hpkah == NULL || hpkah->Instance == NULL) { + return HAL_ERROR; + } + p = hpkah->Instance; + + /* PKA must be enabled before MODE/START are written. */ + if ((p->CR & PKA_CR_EN) == 0U) { + p->CR = PKA_CR_EN; + } + + /* Update the mode field in CR; clear ALL interrupt enables including + * OPERRIE (operation-error) on V2 PKA. The HAL MODIFY_REG clears + * PROCENDIE | RAMERRIE | ADDRERRIE | OPERRIE -- missing OPERRIE was + * harmless under polling but inconsistent with the HAL flow. */ + cr = p->CR; + cr &= ~(PKA_CR_MODE | PKA_CR_PROCENDIE | PKA_CR_RAMERRIE | + PKA_CR_ADDRERRIE); +#ifdef PKA_CR_OPERRIE + cr &= ~PKA_CR_OPERRIE; +#endif + cr |= (mode << PKA_CR_MODE_Pos) & PKA_CR_MODE; + p->CR = cr; + __DMB(); + + /* Start the operation. */ + p->CR = cr | PKA_CR_START; + __DMB(); + + /* Wait for end-of-operation flag, OR an error flag, OR timeout. + * Also watch OPERRF on V2 PKA -- the IP silently rejects invalid + * operand combinations with OPERRF=1 + BUSY=0 + PROCENDF=0, which + * looks like a hang to a poller that only watches PROCENDF/RAMERRF/ + * ADDRERRF. */ + t = 0; + while ((p->SR & PKA_SR_PROCENDF) == 0U) { + uint32_t err_mask = PKA_SR_RAMERRF | PKA_SR_ADDRERRF; +#ifdef PKA_SR_OPERRF + err_mask |= PKA_SR_OPERRF; +#endif + if ((p->SR & err_mask) != 0U) { +#ifdef WC_STM32_PKA_DIAG + printf("PKA err mode=%lx CR=%lx SR=%lx\n", + (unsigned long)mode, (unsigned long)p->CR, + (unsigned long)p->SR); +#endif + p->CLRFR = PKA_CLRFR_PROCENDFC | PKA_CLRFR_RAMERRFC | + PKA_CLRFR_ADDRERRFC; +#ifdef PKA_CLRFR_OPERRFC + p->CLRFR = PKA_CLRFR_OPERRFC; +#endif + return HAL_ERROR; + } + if (++t >= WC_STM32_PKA_TIMEOUT_LOOPS) { +#ifdef WC_STM32_PKA_DIAG + printf("PKA timeout mode=%lx CR=%lx SR=%lx\n", + (unsigned long)mode, (unsigned long)p->CR, + (unsigned long)p->SR); +#endif + p->CLRFR = PKA_CLRFR_PROCENDFC | PKA_CLRFR_RAMERRFC | + PKA_CLRFR_ADDRERRFC; + return HAL_TIMEOUT; + } + } + + /* Clear all status flags. */ + p->CLRFR = PKA_CLRFR_PROCENDFC | PKA_CLRFR_RAMERRFC | PKA_CLRFR_ADDRERRFC; + + return HAL_OK; +} + +static HAL_StatusTypeDef HAL_PKA_ECCMul(PKA_HandleTypeDef *hpkah, + PKA_ECCMulInTypeDef *in, uint32_t Timeout) +{ + volatile uint32_t *RAM; + + (void)Timeout; + if (in == NULL) return HAL_ERROR; + RAM = wc_stm32_pka_prep_ram(hpkah); + if (RAM == NULL) return HAL_ERROR; + + /* Scalar 'k' bit length, modulus bit length, and 'a' coefficient + * sign indicator -- exactly as the HAL writes them. The HAL takes + * the leading byte of the curve ORDER (not the scalar itself) when + * computing the optimal scalar bit-size on V2 PKA; a small scalar + * with a zero MSB byte would otherwise report 8 fewer bits than + * required and the IP accepts the operation but PROCENDF never + * asserts (timeout, SR=INITOK only). */ + RAM[PKA_ECC_SCALAR_MUL_IN_EXP_NB_BITS] = +#ifdef WOLFSSL_STM32_PKA_V2 + (in->primeOrder != NULL) ? + wc_stm32_pka_optbits(in->scalarMulSize, *(in->primeOrder)) : +#endif + wc_stm32_pka_optbits(in->scalarMulSize, *(in->scalarMul)); + RAM[PKA_ECC_SCALAR_MUL_IN_OP_NB_BITS] = + wc_stm32_pka_optbits(in->modulusSize, *(in->modulus)); + RAM[PKA_ECC_SCALAR_MUL_IN_A_COEFF_SIGN] = in->coefSign; + + /* Match the V2 HAL's RAM write order EXACTLY: + * A_COEFF, B_COEFF, MOD_GF, K, INITIAL_POINT_X, INITIAL_POINT_Y, + * N_PRIME_ORDER. + * (V1 PKA has no B_COEFF / N_PRIME_ORDER -- skip those slots.) + * The RAM slots have disjoint addresses so write order shouldn't + * matter in theory, but the V2 PKA IP appears to latch SOME state + * from the write sequence that produces the PROCENDF-never-asserts + * symptom on every V2 chip if the order differs from the HAL. */ + wc_stm32_pka_load_param_be(RAM, PKA_ECC_SCALAR_MUL_IN_A_COEFF, + in->coefA, in->modulusSize); +#ifdef WOLFSSL_STM32_PKA_V2 + if (in->coefB != NULL) { + wc_stm32_pka_load_param_be(RAM, PKA_ECC_SCALAR_MUL_IN_B_COEFF, + in->coefB, in->modulusSize); + } +#endif + wc_stm32_pka_load_param_be(RAM, PKA_ECC_SCALAR_MUL_IN_MOD_GF, + in->modulus, in->modulusSize); + wc_stm32_pka_load_param_be(RAM, PKA_ECC_SCALAR_MUL_IN_K, + in->scalarMul, in->scalarMulSize); + wc_stm32_pka_load_param_be(RAM, PKA_ECC_SCALAR_MUL_IN_INITIAL_POINT_X, + in->pointX, in->modulusSize); + wc_stm32_pka_load_param_be(RAM, PKA_ECC_SCALAR_MUL_IN_INITIAL_POINT_Y, + in->pointY, in->modulusSize); +#ifdef WOLFSSL_STM32_PKA_V2 + if (in->primeOrder != NULL) { + wc_stm32_pka_load_param_be(RAM, PKA_ECC_SCALAR_MUL_IN_N_PRIME_ORDER, + in->primeOrder, in->modulusSize); + } +#endif /* WOLFSSL_STM32_PKA_V2 */ + + return wc_stm32_pka_process(hpkah, PKA_MODE_ECC_MUL); +} + +static void HAL_PKA_ECCMul_GetResult(PKA_HandleTypeDef *hpkah, + PKA_ECCMulOutTypeDef *out) +{ + uint32_t size; + volatile const uint32_t *RAM; + + if (hpkah == NULL || hpkah->Instance == NULL || out == NULL) return; + RAM = hpkah->Instance->RAM; + + /* The HAL recomputes the byte size from the saved IN_OP_NB_BITS + * slot. We do the same. */ + size = (RAM[PKA_ECC_SCALAR_MUL_IN_OP_NB_BITS] + 7U) / 8U; + + if (out->ptX != NULL) { + wc_stm32_pka_read_be(out->ptX, + &RAM[PKA_ECC_SCALAR_MUL_OUT_RESULT_X], size); + } + if (out->ptY != NULL) { + wc_stm32_pka_read_be(out->ptY, + &RAM[PKA_ECC_SCALAR_MUL_OUT_RESULT_Y], size); + } +} + +static HAL_StatusTypeDef HAL_PKA_ECDSAVerif(PKA_HandleTypeDef *hpkah, + PKA_ECDSAVerifInTypeDef *in, uint32_t Timeout) +{ + volatile uint32_t *RAM; + + (void)Timeout; + if (in == NULL) return HAL_ERROR; + RAM = wc_stm32_pka_prep_ram(hpkah); + if (RAM == NULL) return HAL_ERROR; + + RAM[PKA_ECDSA_VERIF_IN_ORDER_NB_BITS] = + wc_stm32_pka_optbits(in->primeOrderSize, *(in->primeOrder)); + RAM[PKA_ECDSA_VERIF_IN_MOD_NB_BITS] = + wc_stm32_pka_optbits(in->modulusSize, *(in->modulus)); + RAM[PKA_ECDSA_VERIF_IN_A_COEFF_SIGN] = in->coefSign; + + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_VERIF_IN_A_COEFF, + in->coef, in->modulusSize); + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_VERIF_IN_MOD_GF, + in->modulus, in->modulusSize); + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_VERIF_IN_INITIAL_POINT_X, + in->basePointX, in->modulusSize); + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_VERIF_IN_INITIAL_POINT_Y, + in->basePointY, in->modulusSize); + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_VERIF_IN_PUBLIC_KEY_POINT_X, + in->pPubKeyCurvePtX, in->modulusSize); + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_VERIF_IN_PUBLIC_KEY_POINT_Y, + in->pPubKeyCurvePtY, in->modulusSize); + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_VERIF_IN_SIGNATURE_R, + in->RSign, in->primeOrderSize); + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_VERIF_IN_SIGNATURE_S, + in->SSign, in->primeOrderSize); + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_VERIF_IN_HASH_E, + in->hash, in->primeOrderSize); + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_VERIF_IN_ORDER_N, + in->primeOrder, in->primeOrderSize); + + return wc_stm32_pka_process(hpkah, PKA_MODE_ECDSA_VERIFICATION); +} + +static uint32_t HAL_PKA_ECDSAVerif_IsValidSignature( + PKA_HandleTypeDef const *const hpkah) +{ + if (hpkah == NULL || hpkah->Instance == NULL) return 0U; + /* The "valid" sentinel differs by PKA IP rev: + * - V1 PKA (WB / WL / L5): RAM[VERIF_OUT_RESULT] == 0 means valid. + * - V2 PKA (WBA / U5 / H5 / N6 / C5 / H7S): == 0xD60D means valid + * (PKA_NO_ERROR). 0 is NOT success on V2. + * Match the HAL semantic for the active IP. */ +#ifdef WOLFSSL_STM32_PKA_V2 + return (hpkah->Instance->RAM[PKA_ECDSA_VERIF_OUT_RESULT] == 0xD60DUL) + ? 1U : 0U; +#else + return (hpkah->Instance->RAM[PKA_ECDSA_VERIF_OUT_RESULT] == 0UL) + ? 1U : 0U; +#endif +} + +static HAL_StatusTypeDef HAL_PKA_ECDSASign(PKA_HandleTypeDef *hpkah, + PKA_ECDSASignInTypeDef *in, uint32_t Timeout) +{ + volatile uint32_t *RAM; + HAL_StatusTypeDef st; + + (void)Timeout; + if (in == NULL) return HAL_ERROR; + RAM = wc_stm32_pka_prep_ram(hpkah); + if (RAM == NULL) return HAL_ERROR; + + /* Capture sizes on the handle BEFORE the operation -- V2 PKA + * clobbers RAM[MOD_NB_BITS] during compute. GetResult reads from + * the handle on V2 (matches HAL behaviour). */ + hpkah->primeordersize = in->modulusSize; + RAM[PKA_ECDSA_SIGN_IN_ORDER_NB_BITS] = + wc_stm32_pka_optbits(in->primeOrderSize, *(in->primeOrder)); + RAM[PKA_ECDSA_SIGN_IN_MOD_NB_BITS] = + wc_stm32_pka_optbits(in->modulusSize, *(in->modulus)); + RAM[PKA_ECDSA_SIGN_IN_A_COEFF_SIGN] = in->coefSign; + + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_SIGN_IN_A_COEFF, + in->coef, in->modulusSize); +#ifdef WOLFSSL_STM32_PKA_V2 + /* V2 PKA ECDSA SIGN requires the curve `b` coefficient loaded + * between A_COEFF and MOD_GF. V1 PKA has no B_COEFF slot for sign. + * Without B_COEFF the V2 sign operation reports + * OUT_ERROR = 0xCBC9 (PKA_FAILED_COMPUTATION) and aborts. The HAL + * `PKA_ECDSASign_Set` writes this between A_COEFF and MOD_GF. */ + if (in->coefB != NULL) { + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_SIGN_IN_B_COEFF, + in->coefB, in->modulusSize); + } +#endif + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_SIGN_IN_MOD_GF, + in->modulus, in->modulusSize); + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_SIGN_IN_K, + in->integer, in->primeOrderSize); + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_SIGN_IN_INITIAL_POINT_X, + in->basePointX, in->modulusSize); + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_SIGN_IN_INITIAL_POINT_Y, + in->basePointY, in->modulusSize); + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_SIGN_IN_HASH_E, + in->hash, in->primeOrderSize); + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_SIGN_IN_PRIVATE_KEY_D, + in->privateKey, in->primeOrderSize); + wc_stm32_pka_load_param_be(RAM, PKA_ECDSA_SIGN_IN_ORDER_N, + in->primeOrder, in->primeOrderSize); + + st = wc_stm32_pka_process(hpkah, PKA_MODE_ECDSA_SIGNATURE); + if (st != HAL_OK) { + return st; + } + /* Sign reports failure via PKA_ECDSA_SIGN_OUT_ERROR. The success + * code differs by PKA IP rev: + * - V1 PKA (WB / WL / L5): success = 0; non-zero = error (e.g. + * unsuitable random k -- caller is expected to retry). + * - V2 PKA (WBA / U5 / H5 / N6 / C5 / H7S): success = 0xD60D + * (PKA_NO_ERROR); other codes include 0xCBC9 + * (PKA_FAILED_COMPUTATION), 0xA3B7 (RPART_NULL), 0xF946 + * (SPART_NULL). 0 is ALSO not success on V2. + * The V2 HAL `PKA_CheckError` redefines `EDCSA_SIGN_NOERROR` to + * `PKA_NO_ERROR`. Mirror that. */ + { + uint32_t err_code = RAM[PKA_ECDSA_SIGN_OUT_ERROR]; +#ifdef WOLFSSL_STM32_PKA_V2 + if (err_code != 0xD60DUL) { +#else + if (err_code != 0UL) { +#endif +#ifdef WC_STM32_PKA_DIAG + printf("PKA sign OUT_ERROR=%lx\n", (unsigned long)err_code); +#endif + return HAL_ERROR; + } + } + return HAL_OK; +} + +static void HAL_PKA_ECDSASign_GetResult(PKA_HandleTypeDef *hpkah, + PKA_ECDSASignOutTypeDef *out, + PKA_ECDSASignOutExtParamTypeDef *outExt) +{ + uint32_t size; + volatile const uint32_t *RAM; + + if (hpkah == NULL || hpkah->Instance == NULL) return; + RAM = hpkah->Instance->RAM; + /* V2 PKA clobbers RAM[MOD_NB_BITS] during compute; use the size + * saved on the handle. V1 still reads from RAM. */ +#ifdef WOLFSSL_STM32_PKA_V2 + size = hpkah->primeordersize; +#else + size = (RAM[PKA_ECDSA_SIGN_IN_MOD_NB_BITS] + 7U) / 8U; +#endif + + if (out != NULL) { + if (out->RSign != NULL) { + wc_stm32_pka_read_be(out->RSign, + &RAM[PKA_ECDSA_SIGN_OUT_SIGNATURE_R], size); + } + if (out->SSign != NULL) { + wc_stm32_pka_read_be(out->SSign, + &RAM[PKA_ECDSA_SIGN_OUT_SIGNATURE_S], size); + } + } + if (outExt != NULL) { + if (outExt->ptX != NULL) { + wc_stm32_pka_read_be(outExt->ptX, + &RAM[PKA_ECDSA_SIGN_OUT_FINAL_POINT_X], size); + } + if (outExt->ptY != NULL) { + wc_stm32_pka_read_be(outExt->ptY, + &RAM[PKA_ECDSA_SIGN_OUT_FINAL_POINT_Y], size); + } + } +} + +#endif /* WOLFSSL_STM32_BARE */ + #endif /* WOLFSSL_STM32_PKA */ @@ -99,11 +814,19 @@ extern PKA_HandleTypeDef hpka; /* #define DEBUG_STM32_HASH */ +#if defined(WOLFSSL_STM32_BARE) && !defined(WC_STM32_HASH_CLK_ENABLE) + #error "WOLFSSL_STM32_BARE: HASH clock-enable not mapped for this STM32 \ + family. Add WC_STM32_HASH_CLK_ENABLE() to \ + wolfssl/wolfcrypt/port/st/stm32.h, or define NO_STM32_HASH." +#endif + /* User can override STM32_HASH_CLOCK_ENABLE and STM32_HASH_CLOCK_DISABLE */ #ifndef STM32_HASH_CLOCK_ENABLE static WC_INLINE void wc_Stm32_Hash_Clock_Enable(STM32_HASH_Context* stmCtx) { - #ifdef WOLFSSL_STM32_CUBEMX + #if defined(WOLFSSL_STM32_BARE) + WC_STM32_HASH_CLK_ENABLE(); + #elif defined(WOLFSSL_STM32_CUBEMX) __HAL_RCC_HASH_CLK_ENABLE(); #else RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_HASH, ENABLE); @@ -116,7 +839,9 @@ extern PKA_HandleTypeDef hpka; #ifndef STM32_HASH_CLOCK_DISABLE static WC_INLINE void wc_Stm32_Hash_Clock_Disable(STM32_HASH_Context* stmCtx) { - #ifdef WOLFSSL_STM32_CUBEMX + #if defined(WOLFSSL_STM32_BARE) + WC_STM32_HASH_CLK_DISABLE(); + #elif defined(WOLFSSL_STM32_CUBEMX) __HAL_RCC_HASH_CLK_DISABLE(); #else RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_HASH, DISABLE); @@ -224,9 +949,18 @@ static void wc_Stm32_Hash_GetDigest(byte* hash, int digestSize) sz = digestSize; while (sz > 0) { - /* first 20 bytes come from instance HR */ + /* first 20 bytes come from the instance digest registers. The + * new-generation HASH IP (gated via WC_STM32_HASH_INSTANCE_HRA + * in stm32.h based on the per-family CMSIS shape) renames this + * from `HR[5]` to `HRA[5]` and adds a separate `HASH_DIGEST->HR[16]` + * for the full digest; the legacy F4/F7/L4 layout still exposes + * `HR[5]` directly on the instance. */ if (i < 5) { + #ifdef WC_STM32_HASH_INSTANCE_HRA + digest[i] = HASH->HRA[i]; + #else digest[i] = HASH->HR[i]; + #endif } #ifdef HASH_DIGEST /* reset comes from HASH_DIGEST */ @@ -643,9 +1377,1288 @@ int wc_Stm32_Hmac_Final(STM32_HASH_Context* stmCtx, word32 algo, #ifdef STM32_CRYPTO #ifndef NO_AES -#ifdef WOLFSSL_STM32_CUBEMX +#ifdef WOLFSSL_STM32_BARE + +/* Only complain if the user actually asked for STM32 HW AES. + * `STM32_CRYPTO` is the umbrella enable; without it the BARE driver + * is dead code and missing clock-enable macros for the family are + * harmless (e.g. F767 / F303 / G491 ship NO_STM32_CRYPTO). */ +#if defined(STM32_CRYPTO) && !defined(WC_STM32_AES_CLK_ENABLE) + #error "WOLFSSL_STM32_BARE: AES clock-enable not mapped for this STM32 \ + family. Add WC_STM32_AES_CLK_ENABLE() to \ + wolfssl/wolfcrypt/port/st/stm32.h, or define NO_STM32_CRYPTO." +#endif + +/* ===== Bare-metal direct-register AES driver ===== + * No HAL or StdPeriph. Two IP variants: + * - CRYP (FIFO-based): F2/F4/F7/H7/MP13 + * - AES/SAES (TinyAES): L4/L5/U5/H573/G0/G4/WB/WL/WBA/H7S(via SAES) + * + * H7S3 has both a "fat" CRYP (same register shape as H753) AND a + * TinyAES-shape SAES. ST's H7S Cube examples drive AES exclusively via + * SAES -- the plain CRYP is gated behind the security domain. The H7S + * arm therefore goes through the TinyAES branch with WC_STM32_AES_INST + * = SAES (forced via WOLFSSL_STM32_USE_SAES in the per-board settings). + * Variant selected via family ifdefs below. */ + +#if defined(WOLFSSL_STM32F2) || defined(WOLFSSL_STM32F4) || \ + defined(WOLFSSL_STM32F7) || defined(WOLFSSL_STM32H7) || \ + defined(WOLFSSL_STM32MP13) +/* ----- CRYP IP (FIFO-based) ----- */ + +#ifndef STM32_BARE_AES_TIMEOUT + #define STM32_BARE_AES_TIMEOUT 0x10000 +#endif + +/* DATATYPE = 10b (byte) so CRYP byte-swaps DR/DOUT for us; key/IV regs are + * still big-endian. Key arrives pre-reversed via wc_AesSetKey (aes.c:4161); + * IV is byte-reversed locally before write. */ +#define STM32_CRYP_DATATYPE_BYTE CRYP_CR_DATATYPE_1 + +static int Stm32AesWaitBusy(void) +{ + int t = 0; + while ((CRYP->SR & CRYP_SR_BUSY) != 0) { + if (++t >= STM32_BARE_AES_TIMEOUT) { + return WC_TIMEOUT_E; + } + } + return 0; +} + +static int Stm32AesWaitInNotFull(void) +{ + int t = 0; + while ((CRYP->SR & CRYP_SR_IFNF) == 0) { + if (++t >= STM32_BARE_AES_TIMEOUT) { + return WC_TIMEOUT_E; + } + } + return 0; +} + +static int Stm32AesWaitOutNotEmpty(void) +{ + int t = 0; + while ((CRYP->SR & CRYP_SR_OFNE) == 0) { + if (++t >= STM32_BARE_AES_TIMEOUT) { + return WC_TIMEOUT_E; + } + } + return 0; +} + +static word32 Stm32AesKeySizeBits(word32 keyLen) +{ + if (keyLen == 24) { + return CRYP_CR_KEYSIZE_0; /* 192-bit */ + } + if (keyLen == 32) { + return CRYP_CR_KEYSIZE_1; /* 256-bit */ + } + return 0; /* 128-bit */ +} + +/* aes->key is pre-byte-reversed by wc_AesSetKey under BARE (aes.c:4161), + * so the key words go straight into the K registers in big-endian form. */ +static void Stm32AesLoadKey(const word32* key, word32 keyLen) +{ + if (keyLen == 16) { + CRYP->K2LR = key[0]; CRYP->K2RR = key[1]; + CRYP->K3LR = key[2]; CRYP->K3RR = key[3]; + } + else if (keyLen == 24) { + CRYP->K1LR = key[0]; CRYP->K1RR = key[1]; + CRYP->K2LR = key[2]; CRYP->K2RR = key[3]; + CRYP->K3LR = key[4]; CRYP->K3RR = key[5]; + } + else { /* 32 */ + CRYP->K0LR = key[0]; CRYP->K0RR = key[1]; + CRYP->K1LR = key[2]; CRYP->K1RR = key[3]; + CRYP->K2LR = key[4]; CRYP->K2RR = key[5]; + CRYP->K3LR = key[6]; CRYP->K3RR = key[7]; + } +} + +/* aes->reg (IV) is NOT pre-reversed by wc_AesSetIV, so byte-reverse here so + * the IV registers see big-endian words. */ +static void Stm32AesLoadIV(const byte* iv, word32 ivLen) +{ + word32 v[4]; + word32 copyLen = (ivLen > 16) ? 16 : ivLen; + + XMEMSET(v, 0, sizeof(v)); + if (iv != NULL && copyLen > 0) { + XMEMCPY(v, iv, copyLen); + ByteReverseWords(v, v, 16); + } + CRYP->IV0LR = v[0]; CRYP->IV0RR = v[1]; + CRYP->IV1LR = v[2]; CRYP->IV1RR = v[3]; +} + +/* Push 4 input words then drain 4 output words. */ +static int Stm32AesXferBlock(const byte* in, byte* out) +{ + int ret; + word32 i; + word32 buf[WC_AES_BLOCK_SIZE/sizeof(word32)]; + + /* Local word-aligned copy so callers may pass byte-aligned ptrs. */ + XMEMCPY(buf, in, WC_AES_BLOCK_SIZE); + + for (i = 0; i < 4; i++) { + ret = Stm32AesWaitInNotFull(); + if (ret != 0) { + return ret; + } + CRYP->DIN = buf[i]; + } + for (i = 0; i < 4; i++) { + ret = Stm32AesWaitOutNotEmpty(); + if (ret != 0) { + return ret; + } + buf[i] = CRYP->DOUT; + } + XMEMCPY(out, buf, WC_AES_BLOCK_SIZE); + return 0; +} + +/* CBC/ECB decrypt requires a key-prep pass first (per F4/H7 reference manual: + * load key, run ALGOMODE=AES_KEY, wait BUSY=0, then start the actual op). */ +static int Stm32AesPrepareKey(word32 keyLen) +{ + int ret; + + CRYP->CR = CRYP_CR_ALGOMODE_AES_KEY | + STM32_CRYP_DATATYPE_BYTE | + Stm32AesKeySizeBits(keyLen); + CRYP->CR |= CRYP_CR_CRYPEN; + ret = Stm32AesWaitBusy(); + CRYP->CR &= ~CRYP_CR_CRYPEN; + return ret; +} + +int wc_Stm32_Aes_Ecb(struct Aes* aes, byte* out, const byte* in, + word32 sz, int isEnc) +{ + int ret; + word32 keyLen, blocks, b; + word32 cr; + + if (aes == NULL || out == NULL || in == NULL) { + return BAD_FUNC_ARG; + } + if (sz == 0 || (sz % WC_AES_BLOCK_SIZE) != 0) { + return BAD_FUNC_ARG; + } + + ret = wc_AesGetKeySize(aes, &keyLen); + if (ret != 0) { + return ret; + } + + ret = wolfSSL_CryptHwMutexLock(); + if (ret != 0) { + return ret; + } + + WC_STM32_AES_CLK_ENABLE(); + + Stm32AesLoadKey(aes->key, keyLen); + if (!isEnc) { + ret = Stm32AesPrepareKey(keyLen); + if (ret != 0) { + goto exit; + } + } + + cr = CRYP_CR_ALGOMODE_AES_ECB | + STM32_CRYP_DATATYPE_BYTE | + Stm32AesKeySizeBits(keyLen); + if (!isEnc) { + cr |= CRYP_CR_ALGODIR; + } + CRYP->CR = cr; + CRYP->CR |= CRYP_CR_FFLUSH; + CRYP->CR |= CRYP_CR_CRYPEN; + + blocks = sz / WC_AES_BLOCK_SIZE; + for (b = 0; b < blocks; b++) { + ret = Stm32AesXferBlock(in + b * WC_AES_BLOCK_SIZE, + out + b * WC_AES_BLOCK_SIZE); + if (ret != 0) { + break; + } + } + +exit: + CRYP->CR &= ~CRYP_CR_CRYPEN; + wolfSSL_CryptHwMutexUnLock(); + return ret; +} + +int wc_Stm32_Aes_Cbc(struct Aes* aes, byte* out, const byte* in, + word32 sz, int isEnc) +{ + int ret; + word32 keyLen, blocks, b; + word32 cr; + + if (aes == NULL || out == NULL || in == NULL) { + return BAD_FUNC_ARG; + } + if (sz == 0 || (sz % WC_AES_BLOCK_SIZE) != 0) { + return BAD_FUNC_ARG; + } + + ret = wc_AesGetKeySize(aes, &keyLen); + if (ret != 0) { + return ret; + } + + ret = wolfSSL_CryptHwMutexLock(); + if (ret != 0) { + return ret; + } + + WC_STM32_AES_CLK_ENABLE(); + + Stm32AesLoadKey(aes->key, keyLen); + if (!isEnc) { + ret = Stm32AesPrepareKey(keyLen); + if (ret != 0) { + goto exit; + } + } + Stm32AesLoadIV((const byte*)aes->reg, WC_AES_BLOCK_SIZE); + + cr = CRYP_CR_ALGOMODE_AES_CBC | + STM32_CRYP_DATATYPE_BYTE | + Stm32AesKeySizeBits(keyLen); + if (!isEnc) { + cr |= CRYP_CR_ALGODIR; + } + CRYP->CR = cr; + CRYP->CR |= CRYP_CR_FFLUSH; + CRYP->CR |= CRYP_CR_CRYPEN; + + /* For in-place decrypt (out == in) the block loop overwrites the + * source ciphertext, so the next-IV ciphertext block has to be + * captured before processing starts. */ + if (!isEnc) { + XMEMCPY(aes->tmp, in + sz - WC_AES_BLOCK_SIZE, WC_AES_BLOCK_SIZE); + } + + blocks = sz / WC_AES_BLOCK_SIZE; + for (b = 0; b < blocks; b++) { + ret = Stm32AesXferBlock(in + b * WC_AES_BLOCK_SIZE, + out + b * WC_AES_BLOCK_SIZE); + if (ret != 0) { + break; + } + } + + if (ret == 0) { + /* Update aes->reg with new IV (last cipher block for enc; saved + * pre-loop ciphertext for dec). aes.c CBC dispatcher expects + * aes->reg updated for the next call. */ + if (isEnc) { + XMEMCPY(aes->reg, out + (blocks - 1) * WC_AES_BLOCK_SIZE, + WC_AES_BLOCK_SIZE); + } + else { + XMEMCPY(aes->reg, aes->tmp, WC_AES_BLOCK_SIZE); + } + } + +exit: + CRYP->CR &= ~CRYP_CR_CRYPEN; + wolfSSL_CryptHwMutexUnLock(); + return ret; +} + +/* CTR: handled via the ECB-as-transform path in aes.c (XTRANSFORM_AESCTRBLOCK). + * Each per-block ECB call comes through wc_Stm32_Aes_Ecb above; aes.c manages + * the counter and the XOR with plaintext. */ + +/* === HW GCM (CRYP IP phase machine) ========================================== + * Native HW GCM for the case the CRYP IP supports directly: + * - IV is 96 bits (12 bytes) -- the standard GCM IV + * - AAD and PT lengths are whole 16-byte blocks (no partial last block) + * Returns CRYPTOCB_UNAVAILABLE for unsupported parameter combos, so the + * caller (aes.c BARE GCM dispatcher) falls back to SW GHASH + HW ECB. */ +static int Stm32AesXferDiscardOut(const byte* in) +{ + int ret; + word32 i; + word32 buf[WC_AES_BLOCK_SIZE/sizeof(word32)]; + + XMEMCPY(buf, in, WC_AES_BLOCK_SIZE); + for (i = 0; i < 4; i++) { + ret = Stm32AesWaitInNotFull(); + if (ret != 0) { + return ret; + } + CRYP->DIN = buf[i]; + } + return Stm32AesWaitBusy(); +} + +/* GCM init phase (GCMPH=00): caller has already written cr_base|phase=0 + * and loaded key/IV. FFLUSH + CRYPEN; wait for CRYPEN to auto-clear + * (H7-documented mechanism for end-of-init-phase; F4 behaves the same). */ +static int Stm32GcmInitPhase(void) +{ + int t; + CRYP->CR |= CRYP_CR_FFLUSH; + CRYP->CR |= CRYP_CR_CRYPEN; + t = 0; + while ((CRYP->CR & CRYP_CR_CRYPEN) != 0) { + if (++t >= STM32_BARE_AES_TIMEOUT) return WC_TIMEOUT_E; + } + return 0; +} + +/* GCM header/AAD phase (GCMPH=01). Whole blocks via DIN (no DOUT + * read); partial last block padded with zeros -- GHASH math uses + * aadSz bits in the final phase to truncate correctly. */ +static int Stm32GcmAadPhase(const byte* aad, word32 aadSz, word32 cr_base) +{ + word32 b, aadBlocks, aadPartial; + int ret; + + if (aadSz == 0) return 0; + aadBlocks = aadSz / WC_AES_BLOCK_SIZE; + aadPartial = aadSz % WC_AES_BLOCK_SIZE; + + CRYP->CR = cr_base | (1u << CRYP_CR_GCM_CCMPH_Pos); + CRYP->CR |= CRYP_CR_CRYPEN; + for (b = 0; b < aadBlocks; b++) { + ret = Stm32AesXferDiscardOut(aad + b * WC_AES_BLOCK_SIZE); + if (ret != 0) return ret; + } + if (aadPartial > 0) { + byte pad[WC_AES_BLOCK_SIZE]; + XMEMSET(pad, 0, sizeof(pad)); + XMEMCPY(pad, aad + aadBlocks * WC_AES_BLOCK_SIZE, aadPartial); + ret = Stm32AesXferDiscardOut(pad); + if (ret != 0) return ret; + } + ret = Stm32AesWaitBusy(); + if (ret != 0) return ret; + CRYP->CR &= ~CRYP_CR_CRYPEN; + return 0; +} + +/* GCM payload phase (GCMPH=10). */ +static int Stm32GcmPayloadPhase(const byte* in, byte* out, word32 sz, + word32 cr_base, int isEnc) +{ + word32 b, blocks; + int ret; + + if (sz == 0) return 0; + blocks = sz / WC_AES_BLOCK_SIZE; + CRYP->CR = cr_base | (2u << CRYP_CR_GCM_CCMPH_Pos); + if (!isEnc) CRYP->CR |= CRYP_CR_ALGODIR; + CRYP->CR |= CRYP_CR_CRYPEN; + for (b = 0; b < blocks; b++) { + ret = Stm32AesXferBlock(in + b * WC_AES_BLOCK_SIZE, + out + b * WC_AES_BLOCK_SIZE); + if (ret != 0) return ret; + } + ret = Stm32AesWaitBusy(); + if (ret != 0) return ret; + CRYP->CR &= ~CRYP_CR_CRYPEN; + return 0; +} + +/* GCM final phase (GCMPH=11). Feeds 64-bit AAD-bit-len then 64-bit + * PT-bit-len, then reads 4 DOUT words for the tag. + * + * H7 rev.B+ / MP13 (CRYP_VER_2_2): DIN final-phase writes use DATATYPE + * swap normally -- write plain uint32s. + * + * F2/F4/F7 (older CRYP IP, behaves like H7 rev.A): DATATYPE swap does + * NOT apply to the final-phase length block; SW must pre-swap via + * __REV. The two HAL families disagree on this and so do their + * reference drivers -- match each. */ +static int Stm32GcmFinalPhase(word32 aadSz, word32 sz, word32 cr_base, + word32* hwTag) +{ + word32 i; + int ret; + word64 aadBits = (word64)aadSz * 8u; + word64 ptBits = (word64)sz * 8u; + word32 aadBitsHi = (word32)(aadBits >> 32); + word32 aadBitsLo = (word32)aadBits; + word32 ptBitsHi = (word32)(ptBits >> 32); + word32 ptBitsLo = (word32)ptBits; + +#if defined(WOLFSSL_STM32F2) || defined(WOLFSSL_STM32F4) || \ + defined(WOLFSSL_STM32F7) + aadBitsHi = __REV(aadBitsHi); + aadBitsLo = __REV(aadBitsLo); + ptBitsHi = __REV(ptBitsHi); + ptBitsLo = __REV(ptBitsLo); +#endif + + CRYP->CR = cr_base | (3u << CRYP_CR_GCM_CCMPH_Pos); + CRYP->CR |= CRYP_CR_CRYPEN; + + ret = Stm32AesWaitInNotFull(); if (ret != 0) return ret; + CRYP->DIN = aadBitsHi; + ret = Stm32AesWaitInNotFull(); if (ret != 0) return ret; + CRYP->DIN = aadBitsLo; + ret = Stm32AesWaitInNotFull(); if (ret != 0) return ret; + CRYP->DIN = ptBitsHi; + ret = Stm32AesWaitInNotFull(); if (ret != 0) return ret; + CRYP->DIN = ptBitsLo; + + for (i = 0; i < 4; i++) { + ret = Stm32AesWaitOutNotEmpty(); + if (ret != 0) return ret; + hwTag[i] = CRYP->DOUT; + } + return 0; +} + +int wc_Stm32_Aes_Gcm(struct Aes* aes, byte* out, const byte* in, word32 sz, + const byte* iv, word32 ivSz, + byte* tag, word32 tagSz, + const byte* aad, word32 aadSz, int isEnc) +{ + int ret; + word32 keyLen; + word32 cr_base; + word32 ivBuf[4]; + word32 hwTag[4]; + + if (aes == NULL || iv == NULL || tag == NULL) return BAD_FUNC_ARG; + if (sz > 0 && (in == NULL || out == NULL)) return BAD_FUNC_ARG; + + /* HW only supports 12-byte IV (J0 = IV || 0x00000001 form) and whole- + * block PT (CRYP IP v1 can't natively handle a partial last block). + * AAD partial is OK -- pad with zeros; GHASH math uses aadSz bits. */ + if (ivSz != GCM_NONCE_MID_SZ) { + #ifdef DEBUG_STM32_BARE_GCM + printf("[STM32 BARE GCM] -> SW (ivSz=%u not 12)\n", ivSz); + #endif + return CRYPTOCB_UNAVAILABLE; + } + if (sz % WC_AES_BLOCK_SIZE != 0) { + #ifdef DEBUG_STM32_BARE_GCM + printf("[STM32 BARE GCM] -> SW (sz=%u not whole-block)\n", sz); + #endif + return CRYPTOCB_UNAVAILABLE; + } +#ifdef DEBUG_STM32_BARE_GCM + printf("[STM32 BARE GCM] -> HW (sz=%u aadSz=%u)\n", sz, aadSz); +#endif + + ret = wc_AesGetKeySize(aes, &keyLen); + if (ret != 0) return ret; + ret = wolfSSL_CryptHwMutexLock(); + if (ret != 0) return ret; + WC_STM32_AES_CLK_ENABLE(); + + /* Set CR (ALGOMODE=AES-GCM, DATATYPE, KEYSIZE, phase=init) BEFORE + * loading key/IV. H7 reference HAL sets ALGOMODE first then K/IV; + * the other order on H7 produces a wrong tag even though CT comes + * out right. */ + cr_base = CRYP_CR_ALGOMODE_AES_GCM | STM32_CRYP_DATATYPE_BYTE | + Stm32AesKeySizeBits(keyLen); + CRYP->CR = cr_base | (0u << CRYP_CR_GCM_CCMPH_Pos); + + Stm32AesLoadKey(aes->key, keyLen); + + /* 12-byte IV || counter=0x00000002 (HW pre-increments to 2 for the + * first payload block; init phase sets up J0). */ + XMEMSET(ivBuf, 0, 16); + XMEMCPY(ivBuf, iv, 12); + ((byte*)ivBuf)[15] = 0x02; + ByteReverseWords(ivBuf, ivBuf, 16); + CRYP->IV0LR = ivBuf[0]; CRYP->IV0RR = ivBuf[1]; + CRYP->IV1LR = ivBuf[2]; CRYP->IV1RR = ivBuf[3]; + + ret = Stm32GcmInitPhase(); + if (ret != 0) goto exit; + ret = Stm32GcmAadPhase(aad, aadSz, cr_base); + if (ret != 0) goto exit; + ret = Stm32GcmPayloadPhase(in, out, sz, cr_base, isEnc); + if (ret != 0) goto exit; + ret = Stm32GcmFinalPhase(aadSz, sz, cr_base, hwTag); + if (ret != 0) goto exit; + XMEMCPY(tag, hwTag, tagSz < 16 ? tagSz : 16); + +exit: + CRYP->CR &= ~CRYP_CR_CRYPEN; + wolfSSL_CryptHwMutexUnLock(); + return ret; +} + +#else /* TinyAES IP (L4/L5/U5/H5/H573/G0/G4/WB/WL/WBA) */ + +/* ----- TinyAES IP (single-register, polled) ----- + * Different from CRYP: no FIFO; one DINR / DOUTR pair processed per + * 16-byte block. KEYRx are written in *reversed* word order + * (KEYR3 = MSB key word for 128-bit; KEYR7 = MSB for 256-bit). + * AES-192 not supported by hardware (only 128 and 256). */ + +#ifndef STM32_BARE_AES_TIMEOUT + #define STM32_BARE_AES_TIMEOUT 0x10000 +#endif + +/* CCF (computation-complete) wait/clear, parameterized on the AES + * instance so the same helpers drive both AES and SAES (DHUK) -- on + * chips that have both, the IP layout is identical. + * + * Clear: newer IPs (U3/U5/L4/L5/H5/G4/WBA/C5) use AES_ICR; older WB/WL/ + * G0 use AES_CR.CCFC; U0 has ICR but it only clears ISR.CCF (we poll + * SR.CCF on U0), so U0 also uses CR.CCFC. Trailing __DMB() prevents the + * C5-at-PLL race where the next CCF poll catches an in-flight clear. + * + * Wait: C5 polls AES_ISR.CCF; older TinyAES polls AES_SR.CCF; U0 polls + * SR.CCF (its ISR.CCF only asserts when the matching IER bit is on). */ +#if defined(WOLFSSL_STM32U0) && defined(AES_CR_CCFC) + #define STM32_AES_CLEAR_INST(inst) do { \ + (inst)->CR |= AES_CR_CCFC; __DMB(); } while (0) +#elif defined(AES_ICR_CCF) + #define STM32_AES_CLEAR_INST(inst) do { \ + (inst)->ICR = AES_ICR_CCF; __DMB(); } while (0) +#elif defined(AES_CR_CCFC) + #define STM32_AES_CLEAR_INST(inst) do { \ + (inst)->CR |= AES_CR_CCFC; __DMB(); } while (0) +#else + #error "STM32 AES IP variant: no CCF-clear mechanism known" +#endif + +#if defined(WOLFSSL_STM32U0) && defined(AES_SR_CCF) + #define STM32_AES_CCF_BIT AES_SR_CCF + #define STM32_AES_CCF_REG SR +#elif defined(AES_ISR_CCF) + #define STM32_AES_CCF_BIT AES_ISR_CCF + #define STM32_AES_CCF_REG ISR +#elif defined(AES_SR_CCF) + #define STM32_AES_CCF_BIT AES_SR_CCF + #define STM32_AES_CCF_REG SR +#else + #error "STM32 AES IP variant: no CCF status register known" +#endif + +/* Back-compat alias for the unparameterized regular-AES call sites. */ +#define STM32_AES_CLEAR_CCF() STM32_AES_CLEAR_INST(WC_STM32_AES_INST) + +#define STM32_AES_DATATYPE_BYTE AES_CR_DATATYPE_1 /* 0b10 */ +#define STM32_AES_CHMOD_ECB 0u +#define STM32_AES_CHMOD_CBC AES_CR_CHMOD_0 +#define STM32_AES_CHMOD_CTR AES_CR_CHMOD_1 +#define STM32_AES_CHMOD_GCM (AES_CR_CHMOD_0 | AES_CR_CHMOD_1) +#define STM32_AES_MODE_ENC 0u +#define STM32_AES_MODE_KEYDERIVE AES_CR_MODE_0 +#define STM32_AES_MODE_DEC AES_CR_MODE_1 +#define STM32_AES_MODE_KD_DEC (AES_CR_MODE_0 | AES_CR_MODE_1) + +/* Poll CCF on either AES instance (regular or SAES). Force prior + * config / DINR writes to retire before polling -- required on the C5 + * family at PLL clock, where without the barrier the write buffer can + * defer the last DINR write past the first CCF read, latching us on a + * stale (still-zero) status. */ +static int Stm32AesPollCCF(AES_TypeDef* inst, int timeout) +{ + int t = 0; + __DMB(); + while ((inst->STM32_AES_CCF_REG & STM32_AES_CCF_BIT) == 0) { + if (++t >= timeout) { + #if defined(DEBUG_STM32_BARE_GCM) || defined(WC_STM32_SAES_DIAG) + printf("[STM32 BARE AES] CCF timeout: CCFreg=0x%08lx CR=0x%08lx " + "ISR=0x%08lx SR=0x%08lx\n", + (unsigned long)(inst->STM32_AES_CCF_REG), + (unsigned long)inst->CR, + (unsigned long)inst->ISR, + (unsigned long)inst->SR); + #endif + return WC_TIMEOUT_E; + } + } + return 0; +} + +/* Back-compat wrapper for the regular-AES (`WC_STM32_AES_INST`) call sites. + * DHUK / SAES call Stm32AesPollCCF(SAES, STM32_BARE_SAES_TIMEOUT) directly. */ +static int Stm32AesWaitCCF(void) +{ + return Stm32AesPollCCF(WC_STM32_AES_INST, STM32_BARE_AES_TIMEOUT); +} + +static word32 Stm32AesKeySizeBits(word32 keyLen) +{ + if (keyLen == 32) { + return AES_CR_KEYSIZE; /* 256-bit */ + } + return 0; /* 128-bit (192 not supported by HW) */ +} + +/* Load a pre-byte-reversed AES key into KEYR0..KEYR(N-1) of `inst`. + * KEYR(N-1) holds the high word; KEYR0 must be written first per RM. + * 16-byte (AES-128) and 32-byte (AES-256) keys only -- TinyAES HW does + * not support AES-192. */ +static int Stm32AesLoadKeyInst(AES_TypeDef* inst, const word32* key, + word32 keyLen) +{ + if (keyLen == 16) { + inst->KEYR0 = key[3]; inst->KEYR1 = key[2]; + inst->KEYR2 = key[1]; inst->KEYR3 = key[0]; + return 0; + } + if (keyLen == 32) { + inst->KEYR0 = key[7]; inst->KEYR1 = key[6]; + inst->KEYR2 = key[5]; inst->KEYR3 = key[4]; + inst->KEYR4 = key[3]; inst->KEYR5 = key[2]; + inst->KEYR6 = key[1]; inst->KEYR7 = key[0]; + return 0; + } + return BAD_FUNC_ARG; +} + +static int Stm32AesLoadKey(const word32* key, word32 keyLen) +{ + return Stm32AesLoadKeyInst(WC_STM32_AES_INST, key, keyLen); +} + +static void Stm32AesLoadIV(const byte* iv, word32 ivLen) +{ + word32 v[4]; + word32 copyLen = (ivLen > 16) ? 16 : ivLen; + + XMEMSET(v, 0, sizeof(v)); + if (iv != NULL && copyLen > 0) { + XMEMCPY(v, iv, copyLen); + ByteReverseWords(v, v, 16); + } + /* IVRx ordering matches keyword: IVR3 = MSB */ + WC_STM32_AES_INST->IVR3 = v[0]; + WC_STM32_AES_INST->IVR2 = v[1]; + WC_STM32_AES_INST->IVR1 = v[2]; + WC_STM32_AES_INST->IVR0 = v[3]; +} + +/* One 16-byte block in / out. */ +static int Stm32AesXferBlock(const byte* in, byte* out) +{ + int ret; + word32 i; + word32 buf[WC_AES_BLOCK_SIZE/sizeof(word32)]; + + XMEMCPY(buf, in, WC_AES_BLOCK_SIZE); + for (i = 0; i < 4; i++) { + WC_STM32_AES_INST->DINR = buf[i]; + } + ret = Stm32AesWaitCCF(); + if (ret != 0) { + return ret; + } + for (i = 0; i < 4; i++) { + buf[i] = WC_STM32_AES_INST->DOUTR; + } + XMEMCPY(out, buf, WC_AES_BLOCK_SIZE); + /* Clear CCF for next block */ + STM32_AES_CLEAR_CCF(); + return 0; +} + +/* Run the key-derivation pass before decrypt (CBC/ECB). */ +static int Stm32AesPrepareKey(word32 keyLen, word32 chmod) +{ + int ret; + word32 cr = STM32_AES_MODE_KEYDERIVE | STM32_AES_DATATYPE_BYTE | + Stm32AesKeySizeBits(keyLen) | chmod; + WC_STM32_AES_INST->CR = cr; + WC_STM32_AES_INST->CR |= AES_CR_EN; + ret = Stm32AesWaitCCF(); + STM32_AES_CLEAR_CCF(); + WC_STM32_AES_INST->CR &= ~AES_CR_EN; + return ret; +} + +/* Forward decls for the SAES self-init helpers defined further down + * (inside the WOLFSSL_DHUK || WOLFSSL_STM32_USE_SAES block). Needed + * because the TinyAES ECB/CBC entry points have to drive the SAES + * init dance before the first CR write when routed via SAES. */ +#ifdef WOLFSSL_STM32_USE_SAES +static int Stm32SaesWaitInit(void); +static void Stm32SaesEnsureRng(void); +#endif + +/* Shared setup for TinyAES Ecb/Cbc: clock enable, SAES self-init + * (when routed), CR=0 / config program, key load, decrypt key- + * derivation pass. Caller follows up with the IV (Cbc) and the + * single-write CR | EN to start the data path. + * + * SAES quirk: KEYSIZE/MODE/CHMOD are only writable when EN=0 AND + * BUSY=0, so a Stm32SaesWaitInit() drain is inserted after every + * write that can leave BUSY set (cold-enable, CR=0 reset, KEYR + * load). */ +static int Stm32AesSetupCR(struct Aes* aes, int isEnc, word32 chmod, + word32 keyLen, word32* outCr) +{ + int ret; + word32 cr = STM32_AES_DATATYPE_BYTE | Stm32AesKeySizeBits(keyLen) | + chmod | + (isEnc ? STM32_AES_MODE_ENC : STM32_AES_MODE_DEC); + + WC_STM32_AES_CLK_ENABLE_INST(); +#ifdef WOLFSSL_STM32_USE_SAES + Stm32SaesEnsureRng(); + ret = Stm32SaesWaitInit(); + if (ret != 0) return ret; +#endif + + WC_STM32_AES_INST->CR = 0; +#ifdef WOLFSSL_STM32_USE_SAES + ret = Stm32SaesWaitInit(); + if (ret != 0) return ret; +#endif + + WC_STM32_AES_INST->CR = cr; + STM32_AES_CLEAR_CCF(); + + ret = Stm32AesLoadKey(aes->key, keyLen); + if (ret != 0) return ret; +#ifdef WOLFSSL_STM32_USE_SAES + ret = Stm32SaesWaitInit(); + if (ret != 0) return ret; +#endif + + if (!isEnc) { + WC_STM32_AES_INST->CR = ((cr & ~AES_CR_MODE_Msk) | + STM32_AES_MODE_KEYDERIVE); + WC_STM32_AES_INST->CR |= AES_CR_EN; + ret = Stm32AesWaitCCF(); + STM32_AES_CLEAR_CCF(); + WC_STM32_AES_INST->CR &= ~AES_CR_EN; + if (ret != 0) return ret; + WC_STM32_AES_INST->CR = cr; + } + *outCr = cr; + return 0; +} + +/* Single-write CR | EN. OR-RMW would lose KEYSIZE/MODE/CHMOD on SAES + * if BUSY happens to be set when the second write lands. */ +static int Stm32AesBlockLoop(const byte* in, byte* out, word32 sz) +{ + word32 blocks = sz / WC_AES_BLOCK_SIZE; + word32 b; + int ret = 0; + for (b = 0; b < blocks; b++) { + ret = Stm32AesXferBlock(in + b * WC_AES_BLOCK_SIZE, + out + b * WC_AES_BLOCK_SIZE); + if (ret != 0) break; + } + return ret; +} + +int wc_Stm32_Aes_Ecb(struct Aes* aes, byte* out, const byte* in, + word32 sz, int isEnc) +{ + int ret; + word32 keyLen, cr; + + if (aes == NULL || out == NULL || in == NULL) return BAD_FUNC_ARG; + if (sz == 0 || (sz % WC_AES_BLOCK_SIZE) != 0) return BAD_FUNC_ARG; + ret = wc_AesGetKeySize(aes, &keyLen); + if (ret != 0) return ret; + if (keyLen != 16 && keyLen != 32) return BAD_FUNC_ARG; + + ret = wolfSSL_CryptHwMutexLock(); + if (ret != 0) return ret; + + ret = Stm32AesSetupCR(aes, isEnc, STM32_AES_CHMOD_ECB, keyLen, &cr); + if (ret != 0) goto exit; + + WC_STM32_AES_INST->CR = cr | AES_CR_EN; + ret = Stm32AesBlockLoop(in, out, sz); + +exit: + WC_STM32_AES_INST->CR &= ~AES_CR_EN; + wolfSSL_CryptHwMutexUnLock(); + return ret; +} + +int wc_Stm32_Aes_Cbc(struct Aes* aes, byte* out, const byte* in, + word32 sz, int isEnc) +{ + int ret; + word32 keyLen, cr, blocks; + + if (aes == NULL || out == NULL || in == NULL) return BAD_FUNC_ARG; + if (sz == 0 || (sz % WC_AES_BLOCK_SIZE) != 0) return BAD_FUNC_ARG; + ret = wc_AesGetKeySize(aes, &keyLen); + if (ret != 0) return ret; + if (keyLen != 16 && keyLen != 32) return BAD_FUNC_ARG; + + ret = wolfSSL_CryptHwMutexLock(); + if (ret != 0) return ret; + + ret = Stm32AesSetupCR(aes, isEnc, STM32_AES_CHMOD_CBC, keyLen, &cr); + if (ret != 0) goto exit; + + Stm32AesLoadIV((const byte*)aes->reg, WC_AES_BLOCK_SIZE); + WC_STM32_AES_INST->CR = cr | AES_CR_EN; + + /* In-place decrypt overwrites the last ciphertext block, so capture + * it for the next IV before the block loop. */ + if (!isEnc) { + XMEMCPY(aes->tmp, in + sz - WC_AES_BLOCK_SIZE, WC_AES_BLOCK_SIZE); + } + ret = Stm32AesBlockLoop(in, out, sz); + if (ret == 0) { + blocks = sz / WC_AES_BLOCK_SIZE; + if (isEnc) { + XMEMCPY(aes->reg, out + (blocks - 1) * WC_AES_BLOCK_SIZE, + WC_AES_BLOCK_SIZE); + } + else { + XMEMCPY(aes->reg, aes->tmp, WC_AES_BLOCK_SIZE); + } + } + +exit: + WC_STM32_AES_INST->CR &= ~AES_CR_EN; + wolfSSL_CryptHwMutexUnLock(); + return ret; +} + +/* TinyAES HW GCM: deferred. Falls back to software GCM (with HW ECB + * blocks via wc_AesEncrypt -> wc_Stm32_Aes_Ecb). */ +int wc_Stm32_Aes_Gcm(struct Aes* aes, byte* out, const byte* in, word32 sz, + const byte* iv, word32 ivSz, + byte* tag, word32 tagSz, + const byte* aad, word32 aadSz, int isEnc) +{ + (void)aes; (void)out; (void)in; (void)sz; + (void)iv; (void)ivSz; + (void)tag; (void)tagSz; + (void)aad; (void)aadSz; (void)isEnc; + return CRYPTOCB_UNAVAILABLE; +} + +#endif /* CRYP IP vs TinyAES IP */ + + +#if defined(WOLFSSL_DHUK) || defined(WOLFSSL_STM32_USE_SAES) +/* ----- BARE SAES helpers (shared by DHUK and the TinyAES SAES route) + * Direct-register SAES self-init / RNG enable used by both the DHUK + * wrap/unwrap path and the TinyAES BARE path when routed to SAES via + * WOLFSSL_STM32_USE_SAES. + * + * SAES on H5/U3/U5/WBA/C5/N6 fetches random data from the RNG on the + * first clock-enable; SR.BUSY stays set until that init completes and + * the IP silently rejects any CR/KEYR/IVR writes during that window. + * The regular AES IP has no such dance, so the TinyAES path that + * targets WC_STM32_AES_INST = CRYP doesn't need these helpers, but the + * SAES routing does. */ + +#ifndef SAES + #error "WOLFSSL_DHUK / WOLFSSL_STM32_USE_SAES require SAES symbol from \ + CMSIS device header" +#endif + +#ifndef STM32_BARE_SAES_TIMEOUT + #define STM32_BARE_SAES_TIMEOUT 0x10000 +#endif + +/* SAES self-init: the IP fetches random data from the RNG on first + * clock-enable. SR.BUSY stays set until init completes. SAES rejects + * config writes during this window. Must be called once after + * WC_STM32_SAES_CLK_ENABLE() before touching CR / KEYR / DINR. */ +static int Stm32SaesWaitInit(void) +{ + int t = 0; + __DMB(); + while ((SAES->SR & AES_SR_BUSY) != 0U) { + if (++t >= STM32_BARE_SAES_TIMEOUT) { + return WC_TIMEOUT_E; + } + } + return 0; +} + +/* Ensure the RNG IP is producing data. SAES init pulls from the + * RNG, so RNGEN must be set before the SAES clock-enable triggers + * SAES self-init. wc_GenerateSeed sets RNGEN on its first call, + * but DHUK / the SAES TinyAES route may run before any RNG consumer. */ +static void Stm32SaesEnsureRng(void) +{ +#ifdef WC_STM32_RNG_CLK_ENABLE + WC_STM32_RNG_CLK_ENABLE(); +#endif + if ((RNG->CR & RNG_CR_RNGEN) == 0U) { + RNG->CR |= RNG_CR_RNGEN; + __DMB(); + } +} + +#endif /* WOLFSSL_DHUK || WOLFSSL_STM32_USE_SAES */ + +#if defined(WOLFSSL_DHUK) +/* BARE DHUK / SAES key wrap+unwrap. Mirrors STM32Cube U5 + * stm32u5xx_hal_cryp_ex.c. + * wc_Stm32_Aes_Wrap -- wrap a plain key for provisioning. + * wc_Stm32_Aes_DhukOp -- combined unwrap + ECB enc/dec using the + * wrapped key in aes->key (KMOD=WRAPPED, + * KEYSEL=HW). ECB only for now. */ + +/* The DHUK code calls Stm32AesPollCCF(SAES, STM32_BARE_SAES_TIMEOUT) and + * STM32_AES_CLEAR_INST(SAES) directly -- unified with the regular AES + * path; see the Stm32AesPollCCF / STM32_AES_CLEAR_INST definitions + * above. */ +#define Stm32SaesWaitCCF() Stm32AesPollCCF(SAES, STM32_BARE_SAES_TIMEOUT) +#define Stm32SaesClearCCF() STM32_AES_CLEAR_INST(SAES) + +/* Set the DHUK IV (used on the matching UnWrap; pure book-keeping + * here). */ +int wc_Stm32_Aes_SetDHUK_IV(struct Aes* aes, const byte* iv, int ivSz) +{ + if (aes == NULL || iv == NULL) { + return BAD_FUNC_ARG; + } + if (ivSz != (int)sizeof(aes->dhukIV)) { + return BAD_FUNC_ARG; + } + XMEMCPY(aes->dhukIV, iv, (word32)ivSz); + aes->dhukIVLen = ivSz; + return 0; +} + +/* Wrap an AES key via SAES. Wrap-key source selected by aes->devId: + * WOLFSSL_DHUK_DEVID -- KEYSEL=HW: encrypt under silicon DHUK + * (chip-bound blob); aes->key is ignored. + * anything else -- KEYSEL=NORMAL: encrypt under aes->key + * (loaded into KEYR). */ +int wc_Stm32_Aes_Wrap(struct Aes* aes, const byte* in, word32 inSz, + byte* out, word32* outSz, const byte* iv, int ivSz) +{ + int ret; + int useDhuk; + word32 cr; + word32 i; + word32 nWords; + word32 buf[8]; /* up to 256-bit key */ + + if (aes == NULL || in == NULL || out == NULL || outSz == NULL) { + return BAD_FUNC_ARG; + } + if (inSz != 16 && inSz != 32) { + return BAD_FUNC_ARG; + } + if (iv != NULL && ivSz != 16) { + return BAD_FUNC_ARG; + } + + useDhuk = (aes->devId == WOLFSSL_DHUK_DEVID); + + ret = wolfSSL_CryptHwMutexLock(); + if (ret != 0) { + return ret; + } + + /* RNG must be running before SAES clock-enable -- SAES self-init + * pulls entropy from the RNG. */ + Stm32SaesEnsureRng(); +#ifdef WC_STM32_SAES_CLK_ENABLE + WC_STM32_SAES_CLK_ENABLE(); +#endif + + /* Wait for SAES self-init (SR.BUSY) to clear before configuring. */ + ret = Stm32SaesWaitInit(); + if (ret != 0) { + wolfSSL_CryptHwMutexUnLock(); + return ret; + } + + /* Disable SAES before reconfiguring CR (per RM). Clear any stale + * CCF before we begin. */ + SAES->CR = 0; + Stm32SaesClearCCF(); + + /* CR: byte data type, KMOD = WRAPPED, MODE = ENCRYPT (= 0), + * CHMOD = ECB (default) or CBC (when IV given), KEYSIZE = 256 + * if 32-byte key. KEYSEL = HW (DHUK) under useDhuk, else NORMAL. */ + cr = AES_CR_DATATYPE_1; /* 0b10 -- byte */ + cr |= AES_CR_KMOD_0; /* KMOD = WRAPPED */ + if (useDhuk) { + cr |= AES_CR_KEYSEL_0; /* KEYSEL = HW (DHUK) */ + } + if (inSz == 32) { + cr |= AES_CR_KEYSIZE; + } + if (iv != NULL) { + cr |= AES_CR_CHMOD_0; /* CHMOD = CBC */ + } + SAES->CR = cr; + + /* Load KEYR only for the software-key path. With KEYSEL = HW the + * IP reads DHUK directly and ignores KEYR. */ + if (!useDhuk) { + (void)Stm32AesLoadKeyInst(SAES, (const word32*)aes->key, inSz); + } + + if (iv != NULL) { + /* Alignment-safe IV copy via local buffer (iv is a byte* and + * may not be 4-byte aligned). IVR{3..0} are written in the + * same word order the existing TinyAES Stm32AesLoadIv() helper + * uses (high-significance word -> IVR3, low -> IVR0), and the + * IV bytes are taken as-is to match the caller's convention. */ + word32 ivWords[4]; + XMEMCPY(ivWords, iv, 16); + SAES->IVR3 = ivWords[0]; + SAES->IVR2 = ivWords[1]; + SAES->IVR1 = ivWords[2]; + SAES->IVR0 = ivWords[3]; + } + (void)ivSz; + + /* Stage input. */ + XMEMCPY(buf, in, inSz); + + /* Enable SAES. */ + SAES->CR |= AES_CR_EN; + + /* Process one block (4 words) at a time. 128-bit key = 1 block, + * 256-bit key = 2 blocks. */ + nWords = inSz / 4u; + for (i = 0; i < nWords; i += 4u) { + SAES->DINR = buf[i + 0u]; + SAES->DINR = buf[i + 1u]; + SAES->DINR = buf[i + 2u]; + SAES->DINR = buf[i + 3u]; + + ret = Stm32SaesWaitCCF(); + if (ret != 0) { + goto exit; + } + + buf[i + 0u] = SAES->DOUTR; + buf[i + 1u] = SAES->DOUTR; + buf[i + 2u] = SAES->DOUTR; + buf[i + 3u] = SAES->DOUTR; + + Stm32SaesClearCCF(); + } + + SAES->CR &= ~AES_CR_EN; + + XMEMCPY(out, buf, inSz); + *outSz = inSz; + +exit: + ForceZero(buf, sizeof(buf)); + wolfSSL_CryptHwMutexUnLock(); + return ret; +} + +/* Combined DHUK unwrap + ECB encrypt or decrypt. The caller's aes + * struct holds the wrapped 256-bit key; SAES unwraps it under the + * silicon-bound DHUK and runs ECB enc/dec on the input blocks. + * + * Default-off: the unwrap-decrypt pass needs secure-state context + * (TZ-enabled build) -- on the silicon we have on hand (U3, WBA52, + * both TZEN=0 from factory) the wrapped-key DECRYPT hangs with + * SR.KEYVALID=1 but CCF never asserts. Wrap is silicon-validated + * deterministic chip-bound output via wc_Stm32_Aes_Wrap; DhukOp + * stays gated until a TZ-secure validation lands. Define + * WOLFSSL_STM32_DHUK_UNWRAP to opt into the experimental path. */ +#ifndef WOLFSSL_STM32_DHUK_UNWRAP +int wc_Stm32_Aes_DhukOp(struct Aes* aes, byte* out, const byte* in, + word32 sz, int isEnc) +{ + (void)aes; (void)out; (void)in; (void)sz; (void)isEnc; + return CRYPTOCB_UNAVAILABLE; +} +#else +int wc_Stm32_Aes_DhukOp(struct Aes* aes, byte* out, const byte* in, + word32 sz, int isEnc) +{ + int ret; + word32 cr; + word32 i; + word32 blocks; + word32 wrappedKey[8]; + + if (aes == NULL || out == NULL || in == NULL) { + return BAD_FUNC_ARG; + } + if (sz == 0 || (sz % WC_AES_BLOCK_SIZE) != 0) { + return BAD_FUNC_ARG; + } + /* DHUK is 256-bit only. */ + if (aes->keylen != 32) { + return BAD_FUNC_ARG; + } + + ret = wolfSSL_CryptHwMutexLock(); + if (ret != 0) { + return ret; + } + + Stm32SaesEnsureRng(); +#ifdef WC_STM32_SAES_CLK_ENABLE + WC_STM32_SAES_CLK_ENABLE(); +#endif + + /* Wait for SAES self-init (SR.BUSY) before configuring. */ + ret = Stm32SaesWaitInit(); + if (ret != 0) { + wolfSSL_CryptHwMutexUnLock(); + return ret; + } + + /* Stage the wrapped key (256-bit) for DINR push. aes->key arrives + * byte-reversed (BARE convention). */ + XMEMCPY(wrappedKey, aes->key, 32); + + /* Phase 1: Unwrap. Mirrors HAL CRYPEx_KeyDecrypt verbatim, using + * MODIFY_REG-style writes that preserve EN across MODE transitions: + * + * (1a) CR = KMOD=WRAPPED + KEYSEL=HW + KEYSIZE=256 + CHMOD=ECB + * + DATATYPE=byte + MODE=KEYDERIVATION. EN=0 initially. + * (1b) Set EN. Wait CCF. Clear CCF. + * (2a) MODIFY MODE -> DECRYPT, keep EN set. + * (2b) Push 8 wrapped key words via DINR in 2 four-word blocks, + * wait CCF + clear CCF between blocks. The IP decrypts each + * block using DHUK and deposits the unwrapped key into KEYR. + * (2c) Clear EN. + * + * Earlier attempts that wrote CR=0 between phases (or that skipped + * the KEYDERIVATION pre-pass) timed out -- SR.KEYVALID asserts but + * CCF never fires. The HAL approach keeps EN set across MODE + * changes via MODIFY_REG. */ + Stm32SaesClearCCF(); + + /* Phase 1a: full CR setup with MODE=KEYDERIVATION, EN=0. + * + * On U3 and WBA52 with TZEN=0 and KEYSEL=HW (DHUK), the + * KEYDERIVATION pass completes but the subsequent DECRYPT pass + * that deposits the unwrapped key into KEYR does not complete + * (CCF never asserts; SR.KEYVALID=1; no ISR error). Setting + * AES_CR_KEYPROT did not help. The wrapped-key-to-KEYR deposit + * appears to be a secure-state-only operation on this silicon + * even with TZEN=0. DHUK Wrap (encrypt-with-DHUK) is reachable + * from NS; DhukOp's unwrap-and-load is not. Documented; caller + * falls back. */ + cr = AES_CR_DATATYPE_1 | AES_CR_KEYSIZE | AES_CR_KMOD_0 | + AES_CR_KEYSEL_0 | /* KEYSEL = HW (DHUK) */ + AES_CR_MODE_0; /* MODE = KEYDERIVATION */ + SAES->CR = cr; + + /* Phase 1b: enable, wait CCF for prep pass, clear CCF. */ + SAES->CR |= AES_CR_EN; + ret = Stm32SaesWaitCCF(); + if (ret != 0) { + goto exit; + } + Stm32SaesClearCCF(); + + /* Phase 2a: switch MODE to DECRYPT via MODIFY_REG-style write. + * Read-modify-write preserves EN and all other bits. */ + { + uint32_t cr2 = SAES->CR; + cr2 = (cr2 & ~AES_CR_MODE) | AES_CR_MODE_1; /* DECRYPT */ + SAES->CR = cr2; + } + + /* Phase 2b: push 8 wrapped-key words via DINR in 2 four-word + * blocks, wait CCF + clear between. No DOUTR read on unwrap -- + * the result is internally moved to KEYR. */ + for (i = 0; i < 8u; i += 4u) { + SAES->DINR = wrappedKey[i + 0u]; + SAES->DINR = wrappedKey[i + 1u]; + SAES->DINR = wrappedKey[i + 2u]; + SAES->DINR = wrappedKey[i + 3u]; + ret = Stm32SaesWaitCCF(); + if (ret != 0) { + goto exit; + } + Stm32SaesClearCCF(); + } + + /* Phase 2c: disable EN. KEYR now holds the unwrapped key. */ + SAES->CR &= ~AES_CR_EN; + ForceZero(wrappedKey, sizeof(wrappedKey)); + + /* Phase 2: ECB operation with the unwrapped key (now in KEYR). + * Switch KMOD back to NORMAL, KEYSEL back to NORMAL so the IP + * reads the plain key from KEYR. For decrypt insert the usual + * AES key-derivation prep pass first (last-round-first schedule). */ + cr = AES_CR_DATATYPE_1 | AES_CR_KEYSIZE; /* KMOD=0, KEYSEL=0, CHMOD=ECB */ + if (!isEnc) { + SAES->CR = cr | AES_CR_MODE_0; /* MODE = KEYDERIVATION */ + SAES->CR |= AES_CR_EN; + ret = Stm32SaesWaitCCF(); + if (ret != 0) { + goto exit; + } + Stm32SaesClearCCF(); + SAES->CR &= ~AES_CR_EN; + cr |= AES_CR_MODE_1; /* MODE = DECRYPT */ + } + SAES->CR = cr; + SAES->CR |= AES_CR_EN; + + /* Process input blocks. */ + blocks = sz / WC_AES_BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + word32 buf[4]; + word32 j; + XMEMCPY(buf, in + i * WC_AES_BLOCK_SIZE, WC_AES_BLOCK_SIZE); + for (j = 0; j < 4u; j++) { + SAES->DINR = buf[j]; + } + ret = Stm32SaesWaitCCF(); + if (ret != 0) { + goto exit; + } + for (j = 0; j < 4u; j++) { + buf[j] = SAES->DOUTR; + } + Stm32SaesClearCCF(); + XMEMCPY(out + i * WC_AES_BLOCK_SIZE, buf, WC_AES_BLOCK_SIZE); + } + + SAES->CR &= ~AES_CR_EN; + ret = 0; + +exit: + /* Scrub the in-flight wrapped-key buffer and the SAES key/IV + * state. After DhukOp the unwrapped key would otherwise be + * resident in KEYR until the next operation overwrote it; on a + * platform where a privileged or debug reader can sample the + * register file, that would defeat the DHUK threat model. Force + * a hardware reset of the IP via IPRST when the CMSIS exposes + * it (newer SAES variants); always disable EN and zero our local + * staging buffer. */ + SAES->CR &= ~AES_CR_EN; +#ifdef AES_CR_IPRST + SAES->CR |= AES_CR_IPRST; + __DSB(); + SAES->CR &= ~AES_CR_IPRST; +#endif + /* CCF clear after IP reset; harmless if IPRST already cleared CCF. */ + Stm32SaesClearCCF(); + ForceZero(wrappedKey, sizeof(wrappedKey)); + wolfSSL_CryptHwMutexUnLock(); + return ret; +} +#endif /* WOLFSSL_STM32_DHUK_UNWRAP */ +#endif /* WOLFSSL_DHUK */ + + +#elif defined(WOLFSSL_STM32_CUBEMX) -#if defined(WOLFSSL_STM32U5_DHUK) +#if defined(WOLFSSL_DHUK) /* Set the DHUK IV to be used when unwrapping an AES key * return 0 on success */ int wc_Stm32_Aes_SetDHUK_IV(struct Aes* aes, const byte* iv, int ivSz) @@ -768,10 +2781,10 @@ int wc_Stm32_Aes_Init(Aes* aes, CRYP_HandleTypeDef* hcryp, int useSaes) break; } -#ifdef WOLFSSL_STM32U5_DHUK +#ifdef WOLFSSL_DHUK /* Use hardware key */ - if (useSaes && (aes->devId == WOLFSSL_STM32U5_DHUK_DEVID || - aes->devId == WOLFSSL_STM32U5_SAES_DEVID)) { + if (useSaes && (aes->devId == WOLFSSL_DHUK_DEVID || + aes->devId == WOLFSSL_SAES_DEVID)) { /* SAES requires use of the RNG -- HAL_RNG_DeInit() calls from random.c turn off the RNG clock -- re-enable the clock here */ @@ -781,7 +2794,7 @@ int wc_Stm32_Aes_Init(Aes* aes, CRYP_HandleTypeDef* hcryp, int useSaes) hcryp->Init.DataType = CRYP_DATATYPE_8B; /* Key select (HW, or Normal) */ - if (aes->devId == WOLFSSL_STM32U5_DHUK_DEVID) { + if (aes->devId == WOLFSSL_DHUK_DEVID) { hcryp->Init.KeySelect = CRYP_KEYSEL_HW; } else { @@ -878,7 +2891,7 @@ int wc_Stm32_Aes_Init(Aes* aes, CRYP_InitTypeDef* cryptInit, void wc_Stm32_Aes_Cleanup(void) { } -#endif /* WOLFSSL_STM32_CUBEMX */ +#endif /* WOLFSSL_STM32_BARE / WOLFSSL_STM32_CUBEMX / StdPeriph */ #endif /* !NO_AES */ #endif /* STM32_CRYPTO */ @@ -939,7 +2952,15 @@ static int stm32_getabs_from_mp_int(uint8_t *dst, const mp_int *a, int sz, defined(WOLFSSL_SP_INT_NEGATIVE)) *abs_sign = x.sign; #else - *abs_sign = 1; /* default to negative */ + /* See companion comment in stm32_getabs_from_hexstr. sp_int + * without WOLFSSL_SP_INT_NEGATIVE has no sign field; the mp_int + * is the modular representative of `a` (e.g. P-256 Af = p-3, + * a large positive integer). Default to POSITIVE so PKA reads + * coef + sign self-consistently. Was incorrectly 1 (negative) + * which made the V2 PKA ECCMul compute on a wrong curve and + * hang/error; also caused the V1 PKA ECDSA sign+verify + * roundtrip to fail on WL55. */ + *abs_sign = 0; /* positive */ #endif res = mp_abs((mp_int*)a, &x); if (res == MP_OKAY) @@ -969,7 +2990,18 @@ static int stm32_getabs_from_hexstr(const char* hex, uint8_t* dst, int sz, defined(WOLFSSL_SP_INT_NEGATIVE)) *abs_sign = x.sign; #else - *abs_sign = 1; /* default to negative */ + /* sp_int without WOLFSSL_SP_INT_NEGATIVE has no sign field; + * mp_read_radix returns the absolute value as a positive + * integer. The wolfssl ECC table stores the coefficient `a` + * as its modular representative (e.g. P-256 Af = p-3, a + * large positive number), so the sign here is POSITIVE + * (a = +(p-3) which mod p equals -3 -- mathematically the + * same as -3 with coefSign = negative, but the PKA expects + * coef + coefSign to be self-consistent). Defaulting to 1 + * (negative) caused the PKA to compute on curve a=+3 + * instead of a=-3, producing R/S that don't verify against + * the SW-generated pubkey. */ + *abs_sign = 0; /* positive */ #endif res = mp_abs(&x, &x); } diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 4631734d7cf..762ad729345 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -4243,6 +4243,9 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz) #elif defined(STM32_RNG) /* Generate a RNG seed using the hardware random number generator * on the STM32F2/F4/F7/L4. */ + #include + /* Pulls in WC_STM32_RNG_CLK_ENABLE for WOLFSSL_STM32_BARE builds */ + #ifdef WOLFSSL_STM32_CUBEMX int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz) @@ -4311,6 +4314,21 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz) #endif + /* Bounded poll for DRDY, plus recovery from SECS / CECS. The + * unbounded `while (DRDY == 0)` loop in the original code spins + * forever on chips where the RNG kernel clock is unstable + * (e.g. WL55 with RNGSEL = MSI under sustained ECDSA-key-gen + * load), because once the IP latches a Seed-error or Clock- + * error condition it stops asserting DRDY. Per the STM32 RM + * recovery sequence: clear SEIS/CEIS, toggle RNGEN, discard the + * stale words sitting in the RNG output, then retry. */ + #ifndef STM32_BARE_RNG_BYTE_TIMEOUT + #define STM32_BARE_RNG_BYTE_TIMEOUT 0x40000 + #endif + #ifndef STM32_BARE_RNG_MAX_RETRIES + #define STM32_BARE_RNG_MAX_RETRIES 8 + #endif + /* Generate a RNG seed using the hardware RNG on the STM32F427 * directly, following steps outlined in STM32F4 Reference * Manual (Chapter 24) for STM32F4xx family. */ @@ -4318,6 +4336,9 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz) { int ret; word32 i; + word32 t; + word32 retries; + word32 sr; (void)os; ret = wolfSSL_CryptHwMutexLock(); @@ -4327,29 +4348,168 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz) #ifndef STM32_NUTTX_RNG /* enable RNG peripheral clock */ - RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; + #ifdef WC_STM32_RNG_CLK_ENABLE + WC_STM32_RNG_CLK_ENABLE(); + #else + /* Default for F4/F7/L4/L5/U5/H5/H7 -- RNG on AHB2 */ + RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; + #endif + #endif + + /* On the new-gen STM32C5 RNG IP the CR register is locked at + * reset (CONFIGLOCK clear, but the IP refuses to produce data + * until a NIST-compliant CONFIG1/2/3 + NSCR + HTCR sequence + * has been written under CONDRST). The HAL ships canonical + * candidate values in the device header (RNG_CAND_NIST_*). + * Detect the family by presence of that symbol -- on chips + * without it (F4/F7/L4/U5/H7/H5/WL/etc.) skip. Do this only + * on the first call (RNGEN clear) so subsequent calls don't + * disturb a running peripheral. */ + #if defined(RNG_CAND_NIST_CR_VALUE) && defined(RNG_CR_CONDRST) && \ + !defined(WC_STM32_RNG_NO_NIST_INIT) + if ((WC_RNG_CR & RNG_CR_RNGEN) == 0U) { + #ifdef RNG_SR_BUSY + /* HAL flow: drain BUSY before writing CR. */ + t = 0; + while ((WC_RNG_SR & RNG_SR_BUSY) != 0U) { + if (++t >= STM32_BARE_RNG_BYTE_TIMEOUT) { + break; + } + } + #endif + WC_RNG_CR = (uint32_t)RNG_CAND_NIST_CR_VALUE | + (uint32_t)RNG_CR_CONDRST; + #ifdef RNG_CAND_NIST_NSCR_VALUE + RNG->NSCR = (uint32_t)RNG_CAND_NIST_NSCR_VALUE; + #endif + #ifdef RNG_CAND_NIST_HTCR_VALUE + RNG->HTCR[0] = (uint32_t)RNG_CAND_NIST_HTCR_VALUE; + #endif + /* Clear CONDRST and wait for the IP to mirror it back. The + * STM32 HAL polls AES_CR.CONDRST (not SR.BUSY) for completion + * of the conditioning soft-reset; SR.BUSY drops earlier in + * the seed-pull pipeline on at least the C5 IP and reading + * it as "conditioning done" trips a SECS=1 a few microseconds + * later when RNGEN goes high. Bounded so a misconfigured + * kernel clock returns a clean error instead of hanging. */ + WC_RNG_CR &= ~(uint32_t)RNG_CR_CONDRST; + t = 0; + while ((WC_RNG_CR & RNG_CR_CONDRST) != 0U) { + if (++t >= STM32_BARE_RNG_BYTE_TIMEOUT) { +#ifdef WC_STM32_RNG_DIAG + printf("[RNG] CONDRST stuck CR=%08lx SR=%08lx\n", + (unsigned long)WC_RNG_CR, + (unsigned long)WC_RNG_SR); +#endif + wolfSSL_CryptHwMutexUnLock(); + return RNG_FAILURE_E; + } + } +#ifdef WC_STM32_RNG_DIAG + printf("[RNG] post-NIST CR=%08lx SR=%08lx t=%lu\n", + (unsigned long)WC_RNG_CR, + (unsigned long)WC_RNG_SR, + (unsigned long)t); +#endif + } #endif /* enable RNG interrupt, set IE bit in RNG->CR register */ WC_RNG_CR |= RNG_CR_IE; /* enable RNG, set RNGEN bit in RNG->CR. Activates RNG, - * RNG_LFSR, and error detector */ + * RNG_LFSR, and error detector. WC_STM32_RNG_CED_DISABLE + * additionally sets CR.CED=1 to suppress the clock-error + * detection -- the Linux STM32 RNG driver does this and on + * the C5 silicon the CED detector trips on a (perfectly fine) + * 48 MHz kernel clock for reasons unclear in the RM. */ +#ifdef WC_STM32_RNG_CED_DISABLE + WC_RNG_CR |= RNG_CR_RNGEN | RNG_CR_CED; +#else WC_RNG_CR |= RNG_CR_RNGEN; +#endif - /* verify no errors, make sure SEIS and CEIS bits are 0 - * in RNG->SR register */ - if (WC_RNG_SR & (RNG_SR_SECS | RNG_SR_CECS)) { - wolfSSL_CryptHwMutexUnLock(); - return RNG_FAILURE_E; - } + /* (No early SECS/CECS bail here.) The HAL doesn't check error + * status immediately after RNGEN -- the IP needs a few cycles + * after enable for the first seed pull, and a transient SEIS/SECS + * can latch and resolve itself through the auto-reset that the + * retry loop below already handles. Bailing here returned + * RNG_FAILURE_E (-199) on first-init on the STM32C5 silicon. */ for (i = 0; i < sz; i++) { - /* wait until RNG number is ready */ - while ((WC_RNG_SR & RNG_SR_DRDY) == 0) { } + retries = 0; + for (;;) { + t = 0; + /* Sample SR once before the loop so the post-loop + * happy-path / error checks have a defined value even + * if STM32_BARE_RNG_BYTE_TIMEOUT is configured to 0. */ + sr = WC_RNG_SR; + /* Bounded DRDY poll -- breaks on either DRDY or any + * error indication (SECS/CECS). */ + while (t < STM32_BARE_RNG_BYTE_TIMEOUT) { + sr = WC_RNG_SR; + if ((sr & (RNG_SR_DRDY | RNG_SR_SECS | + RNG_SR_CECS)) != 0U) { + break; + } + t++; + } - /* get value */ - output[i] = WC_RNG_DR; + /* Happy path: data ready and no error. */ + if ((sr & RNG_SR_DRDY) != 0U && + (sr & (RNG_SR_SECS | RNG_SR_CECS)) == 0U) { + output[i] = WC_RNG_DR; + break; + } + + /* Either timed out or an error latched. Recover. */ + if (++retries > STM32_BARE_RNG_MAX_RETRIES) { +#ifdef WC_STM32_RNG_DIAG + printf("[RNG] retries exhausted byte=%lu sr=%08lx CR=%08lx\n", + (unsigned long)i, + (unsigned long)sr, + (unsigned long)WC_RNG_CR); +#endif + wolfSSL_CryptHwMutexUnLock(); + return RNG_FAILURE_E; + } +#ifdef WC_STM32_RNG_DIAG + printf("[RNG] retry byte=%lu retries=%lu sr=%08lx\n", + (unsigned long)i, + (unsigned long)retries, + (unsigned long)sr); +#endif + + /* Recovery sequence (per STM32 RM RNG chapter): + * 1. Clear SEIS / CEIS interrupt status by writing 0 + * to those bits. All other SR bits are read-only + * status indicators (DRDY / BUSY / SECS / CECS); + * writing 0 to them has no effect per the RM, so + * a plain 0 write is safe and avoids the + * read-modify-write hitting any reserved bits the + * IP revision may add later. + * 2. Toggle RNGEN off then on to drop any stale + * LFSR state that may be tainted by the error. + * 3. Discard four DR reads to flush the pipeline + * (only meaningful when DRDY is set; otherwise + * the reads are harmless and bounded). */ + WC_RNG_SR = 0; + WC_RNG_CR &= ~RNG_CR_RNGEN; + WC_RNG_CR |= RNG_CR_RNGEN; + t = 0; + while (t < 4U) { + if ((WC_RNG_SR & RNG_SR_DRDY) != 0U) { + (void)WC_RNG_DR; + t++; + } + else if ((WC_RNG_SR & + (RNG_SR_SECS | RNG_SR_CECS)) != 0U) { + /* Clock-error during recovery -- bail and + * let the outer retry handle it. */ + break; + } + } + } } wolfSSL_CryptHwMutexUnLock(); diff --git a/wolfssl/wolfcrypt/aes.h b/wolfssl/wolfcrypt/aes.h index 0afc129009d..752ef1f2998 100644 --- a/wolfssl/wolfcrypt/aes.h +++ b/wolfssl/wolfcrypt/aes.h @@ -301,7 +301,7 @@ struct Aes { #endif #ifdef HAVE_AESGCM Gcm gcm; -#ifdef WOLFSSL_STM32U5_DHUK +#ifdef WOLFSSL_DHUK byte dhukIV[16]; /* Used when unwrapping an encrypted key */ int dhukIVLen; #endif @@ -341,7 +341,7 @@ struct Aes { byte use_sha3_hw_crypto; #endif #endif /* __aarch64__ && WOLFSSL_ARMASM && !WOLFSSL_ARMASM_NO_HW_CRYPTO */ -#if defined(WOLF_CRYPTO_CB) || defined(WOLFSSL_STM32U5_DHUK) +#if defined(WOLF_CRYPTO_CB) || defined(WOLFSSL_DHUK) int devId; void* devCtx; /* Opaque handle for CryptoCB device */ #endif diff --git a/wolfssl/wolfcrypt/port/st/stm32.h b/wolfssl/wolfcrypt/port/st/stm32.h index 9aa0d418ae1..df4bf669b26 100644 --- a/wolfssl/wolfcrypt/port/st/stm32.h +++ b/wolfssl/wolfcrypt/port/st/stm32.h @@ -23,11 +23,498 @@ #define _WOLFPORT_STM32_H_ /* Generic STM32 Hashing and Crypto Functions */ -/* Supports CubeMX HAL or Standard Peripheral Library */ +/* Supports CubeMX HAL, Standard Peripheral Library, or bare-metal direct + * register access (WOLFSSL_STM32_BARE). */ #include #include /* for MATH_INT_T */ +#ifdef WOLFSSL_STM32_BARE +/* Per-family direct-register clock-enable macros. CMSIS device header is + * already included via settings.h. RCC->...ENR bit names come from CMSIS. */ +#if defined(WOLFSSL_STM32H5) + #define WC_STM32_AES_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_AESEN; (void)RCC->AHB2ENR; } while (0) + #define WC_STM32_AES_CLK_DISABLE() \ + do { RCC->AHB2ENR &= ~RCC_AHB2ENR_AESEN; } while (0) + #ifdef RCC_AHB2ENR_SAESEN + #define WC_STM32_SAES_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_SAESEN; (void)RCC->AHB2ENR; } \ + while (0) + #define WC_STM32_SAES_CLK_DISABLE() \ + do { RCC->AHB2ENR &= ~RCC_AHB2ENR_SAESEN; } while (0) + #endif + #define WC_STM32_HASH_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_HASHEN; (void)RCC->AHB2ENR; } while (0) + #define WC_STM32_HASH_CLK_DISABLE() \ + do { RCC->AHB2ENR &= ~RCC_AHB2ENR_HASHEN; } while (0) + #define WC_STM32_RNG_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; (void)RCC->AHB2ENR; } while (0) +#elif defined(WOLFSSL_STM32F2) || defined(WOLFSSL_STM32F4) || \ + defined(WOLFSSL_STM32F7) || defined(WOLFSSL_STM32H7) + /* F2/F4/F7/H7 -- CRYP + HASH + RNG all on AHB2 with identical + * RCC bit names. */ + #define WC_STM32_AES_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_CRYPEN; (void)RCC->AHB2ENR; } while (0) + #define WC_STM32_AES_CLK_DISABLE() \ + do { RCC->AHB2ENR &= ~RCC_AHB2ENR_CRYPEN; } while (0) + #define WC_STM32_HASH_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_HASHEN; (void)RCC->AHB2ENR; } while (0) + #define WC_STM32_HASH_CLK_DISABLE() \ + do { RCC->AHB2ENR &= ~RCC_AHB2ENR_HASHEN; } while (0) + #define WC_STM32_RNG_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; (void)RCC->AHB2ENR; } while (0) +#elif defined(WOLFSSL_STM32MP13) + /* MP13 -- CRYP1/HASH1/RNG1 on AHB5; CMSIS device header may use + * symbol-suffixed RCC names. Gate each macro on the CMSIS bit so + * a partial device header still compiles. */ + #if defined(RCC_MP_AHB5ENSETR_CRYP1EN) + #define WC_STM32_AES_CLK_ENABLE() \ + do { RCC->MP_AHB5ENSETR |= RCC_MP_AHB5ENSETR_CRYP1EN; \ + (void)RCC->MP_AHB5ENSETR; } while (0) + #define WC_STM32_AES_CLK_DISABLE() \ + do { RCC->MP_AHB5ENCLRR = RCC_MP_AHB5ENSETR_CRYP1EN; } while (0) + #endif + #if defined(RCC_MP_AHB5ENSETR_HASH1EN) + #define WC_STM32_HASH_CLK_ENABLE() \ + do { RCC->MP_AHB5ENSETR |= RCC_MP_AHB5ENSETR_HASH1EN; \ + (void)RCC->MP_AHB5ENSETR; } while (0) + #define WC_STM32_HASH_CLK_DISABLE() \ + do { RCC->MP_AHB5ENCLRR = RCC_MP_AHB5ENSETR_HASH1EN; } while (0) + #endif + #if defined(RCC_MP_AHB5ENSETR_RNG1EN) + #define WC_STM32_RNG_CLK_ENABLE() \ + do { RCC->MP_AHB5ENSETR |= RCC_MP_AHB5ENSETR_RNG1EN; \ + (void)RCC->MP_AHB5ENSETR; } while (0) + #endif +#elif defined(WOLFSSL_STM32L4) + #define WC_STM32_AES_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_AESEN; (void)RCC->AHB2ENR; } while (0) + #define WC_STM32_AES_CLK_DISABLE() \ + do { RCC->AHB2ENR &= ~RCC_AHB2ENR_AESEN; } while (0) + #define WC_STM32_HASH_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_HASHEN; (void)RCC->AHB2ENR; } while (0) + #define WC_STM32_HASH_CLK_DISABLE() \ + do { RCC->AHB2ENR &= ~RCC_AHB2ENR_HASHEN; } while (0) + #define WC_STM32_RNG_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; (void)RCC->AHB2ENR; } while (0) +#elif defined(WOLFSSL_STM32L5) + /* L5: HASH + RNG on AHB2 (L552). L562 also adds AES + PKA. AES + * clock-enable is gated on the CMSIS symbol so headers that don't + * expose AESEN (L552) skip the define. */ + #ifdef RCC_AHB2ENR_AESEN + #define WC_STM32_AES_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_AESEN; (void)RCC->AHB2ENR; } \ + while (0) + #define WC_STM32_AES_CLK_DISABLE() \ + do { RCC->AHB2ENR &= ~RCC_AHB2ENR_AESEN; } while (0) + #endif + #define WC_STM32_HASH_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_HASHEN; (void)RCC->AHB2ENR; } while (0) + #define WC_STM32_HASH_CLK_DISABLE() \ + do { RCC->AHB2ENR &= ~RCC_AHB2ENR_HASHEN; } while (0) + #define WC_STM32_RNG_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; (void)RCC->AHB2ENR; } while (0) +#elif defined(WOLFSSL_STM32U5) || defined(WOLFSSL_STM32U3) + /* U5 / U3 RCC uses AHB2ENR1 (not AHB2ENR). AES bit only present on + * variants that have the peripheral (U585+, U385+). SAES is on the + * same AHB2ENR1; gate on the CMSIS bit so headers without it (e.g. + * U575 which has neither AES nor SAES) skip. */ + #ifdef RCC_AHB2ENR1_AESEN + #define WC_STM32_AES_CLK_ENABLE() \ + do { RCC->AHB2ENR1 |= RCC_AHB2ENR1_AESEN; (void)RCC->AHB2ENR1; } \ + while (0) + #define WC_STM32_AES_CLK_DISABLE() \ + do { RCC->AHB2ENR1 &= ~RCC_AHB2ENR1_AESEN; } while (0) + #endif + #ifdef RCC_AHB2ENR1_SAESEN + #define WC_STM32_SAES_CLK_ENABLE() \ + do { RCC->AHB2ENR1 |= RCC_AHB2ENR1_SAESEN; (void)RCC->AHB2ENR1; } \ + while (0) + #define WC_STM32_SAES_CLK_DISABLE() \ + do { RCC->AHB2ENR1 &= ~RCC_AHB2ENR1_SAESEN; } while (0) + #endif + #ifdef RCC_AHB2ENR1_HASHEN + #define WC_STM32_HASH_CLK_ENABLE() \ + do { RCC->AHB2ENR1 |= RCC_AHB2ENR1_HASHEN; (void)RCC->AHB2ENR1; } \ + while (0) + #define WC_STM32_HASH_CLK_DISABLE() \ + do { RCC->AHB2ENR1 &= ~RCC_AHB2ENR1_HASHEN; } while (0) + #endif + #define WC_STM32_RNG_CLK_ENABLE() \ + do { RCC->AHB2ENR1 |= RCC_AHB2ENR1_RNGEN; (void)RCC->AHB2ENR1; } \ + while (0) +#elif defined(WOLFSSL_STM32G0) + #define WC_STM32_AES_CLK_ENABLE() \ + do { RCC->AHBENR |= RCC_AHBENR_AESEN; (void)RCC->AHBENR; } while (0) + #define WC_STM32_AES_CLK_DISABLE() \ + do { RCC->AHBENR &= ~RCC_AHBENR_AESEN; } while (0) + #define WC_STM32_RNG_CLK_ENABLE() \ + do { RCC->AHBENR |= RCC_AHBENR_RNGEN; (void)RCC->AHBENR; } while (0) +#elif defined(WOLFSSL_STM32WB) + /* WB55 dual-core: AES1 is the M4 (CPU1) application AES, on AHB2. + * AES2 sits on AHB4/AHB3 and is reserved for the M0+ side / shared use. + * The wolfcrypt port maps CRYP -> AES1 (see CRYP alias above), so use + * AES1's clock-enable bit. RNG is on AHB3. */ + #define WC_STM32_AES_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_AES1EN; (void)RCC->AHB2ENR; } while (0) + #define WC_STM32_AES_CLK_DISABLE() \ + do { RCC->AHB2ENR &= ~RCC_AHB2ENR_AES1EN; } while (0) + #define WC_STM32_RNG_CLK_ENABLE() \ + do { RCC->AHB3ENR |= RCC_AHB3ENR_RNGEN; (void)RCC->AHB3ENR; } while (0) +#elif defined(WOLFSSL_STM32WL) + /* WL55 dual-core: TinyAES + RNG + PKA on M4 (CPU1) side. AES on AHB3, + * RNG on AHB3, PKA on AHB3. No HASH peripheral. V1 PKA layout. */ + #ifdef RCC_AHB3ENR_AESEN + #define WC_STM32_AES_CLK_ENABLE() \ + do { RCC->AHB3ENR |= RCC_AHB3ENR_AESEN; (void)RCC->AHB3ENR; } \ + while (0) + #define WC_STM32_AES_CLK_DISABLE() \ + do { RCC->AHB3ENR &= ~RCC_AHB3ENR_AESEN; } while (0) + #endif + #define WC_STM32_RNG_CLK_ENABLE() \ + do { RCC->AHB3ENR |= RCC_AHB3ENR_RNGEN; (void)RCC->AHB3ENR; } while (0) +#elif defined(WOLFSSL_STM32G4) + /* G4: TinyAES + RNG + PKA on AHB2. No HASH peripheral. */ + #define WC_STM32_AES_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_AESEN; (void)RCC->AHB2ENR; } while (0) + #define WC_STM32_AES_CLK_DISABLE() \ + do { RCC->AHB2ENR &= ~RCC_AHB2ENR_AESEN; } while (0) + #define WC_STM32_RNG_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; (void)RCC->AHB2ENR; } while (0) +#elif defined(WOLFSSL_STM32WBA) + /* WBA: TinyAES + HASH + RNG + PKA + SAES on AHB2 (PKA on AHB1). */ + #define WC_STM32_AES_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_AESEN; (void)RCC->AHB2ENR; } while (0) + #define WC_STM32_AES_CLK_DISABLE() \ + do { RCC->AHB2ENR &= ~RCC_AHB2ENR_AESEN; } while (0) + #ifdef RCC_AHB2ENR_SAESEN + #define WC_STM32_SAES_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_SAESEN; (void)RCC->AHB2ENR; } \ + while (0) + #define WC_STM32_SAES_CLK_DISABLE() \ + do { RCC->AHB2ENR &= ~RCC_AHB2ENR_SAESEN; } while (0) + #endif + #ifdef RCC_AHB2ENR_HASHEN + #define WC_STM32_HASH_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_HASHEN; (void)RCC->AHB2ENR; } \ + while (0) + #define WC_STM32_HASH_CLK_DISABLE() \ + do { RCC->AHB2ENR &= ~RCC_AHB2ENR_HASHEN; } while (0) + #endif + #define WC_STM32_RNG_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; (void)RCC->AHB2ENR; } while (0) +#elif defined(WOLFSSL_STM32C5) + /* C5: TinyAES + HASH + RNG + SAES + PKA all on AHB2. New-gen HASH IP + * (4-bit ALGO field, same as H5/U3/N6). */ + #define WC_STM32_AES_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_AESEN; (void)RCC->AHB2ENR; } while (0) + #define WC_STM32_AES_CLK_DISABLE() \ + do { RCC->AHB2ENR &= ~RCC_AHB2ENR_AESEN; } while (0) + #ifdef RCC_AHB2ENR_SAESEN + #define WC_STM32_SAES_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_SAESEN; (void)RCC->AHB2ENR; } \ + while (0) + #define WC_STM32_SAES_CLK_DISABLE() \ + do { RCC->AHB2ENR &= ~RCC_AHB2ENR_SAESEN; } while (0) + #endif + #define WC_STM32_HASH_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_HASHEN; (void)RCC->AHB2ENR; } while (0) + #define WC_STM32_HASH_CLK_DISABLE() \ + do { RCC->AHB2ENR &= ~RCC_AHB2ENR_HASHEN; } while (0) + #define WC_STM32_RNG_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; (void)RCC->AHB2ENR; } while (0) +#elif defined(WOLFSSL_STM32U0) + /* U0: Cortex-M0+ low-end. AES + RNG only (no SAES, no HASH, no PKA, + * no CRYP). Both on the single AHBENR. TinyAES IP, KEYSIZE field + * for 128/256-bit. */ + #define WC_STM32_AES_CLK_ENABLE() \ + do { RCC->AHBENR |= RCC_AHBENR_AESEN; (void)RCC->AHBENR; } while (0) + #define WC_STM32_AES_CLK_DISABLE() \ + do { RCC->AHBENR &= ~RCC_AHBENR_AESEN; } while (0) + #define WC_STM32_RNG_CLK_ENABLE() \ + do { RCC->AHBENR |= RCC_AHBENR_RNGEN; (void)RCC->AHBENR; } while (0) +#elif defined(WOLFSSL_STM32N6) + /* N6: CRYP + HASH + RNG + SAES + PKA all on AHB3. Note that on N6 + * the AES IP is the older "fat" CRYP (with AAD/header handling in + * register) -- SAES is the newer TinyAES-shape IP and is the one + * routed by the BARE driver when WOLFSSL_STM32_USE_SAES is set. */ + #ifdef RCC_AHB3ENR_CRYPEN + #define WC_STM32_AES_CLK_ENABLE() \ + do { RCC->AHB3ENR |= RCC_AHB3ENR_CRYPEN; (void)RCC->AHB3ENR; } \ + while (0) + #define WC_STM32_AES_CLK_DISABLE() \ + do { RCC->AHB3ENR &= ~RCC_AHB3ENR_CRYPEN; } while (0) + #endif + #ifdef RCC_AHB3ENR_SAESEN + #define WC_STM32_SAES_CLK_ENABLE() \ + do { RCC->AHB3ENR |= RCC_AHB3ENR_SAESEN; (void)RCC->AHB3ENR; } \ + while (0) + #define WC_STM32_SAES_CLK_DISABLE() \ + do { RCC->AHB3ENR &= ~RCC_AHB3ENR_SAESEN; } while (0) + #endif + #define WC_STM32_HASH_CLK_ENABLE() \ + do { RCC->AHB3ENR |= RCC_AHB3ENR_HASHEN; (void)RCC->AHB3ENR; } while (0) + #define WC_STM32_HASH_CLK_DISABLE() \ + do { RCC->AHB3ENR &= ~RCC_AHB3ENR_HASHEN; } while (0) + #define WC_STM32_RNG_CLK_ENABLE() \ + do { RCC->AHB3ENR |= RCC_AHB3ENR_RNGEN; (void)RCC->AHB3ENR; } while (0) +#elif defined(WOLFSSL_STM32H7S) + /* H7RS/H7S3: classic H7 fat CRYP + classic H7 HASH (same register + * shapes as H753) but RCC clock-enable bits moved to AHB3ENR, and + * V2 PKA + SAES added. All five (CRYP/HASH/RNG/SAES/PKA) live on + * AHB3ENR. */ + #ifdef RCC_AHB3ENR_CRYPEN + #define WC_STM32_AES_CLK_ENABLE() \ + do { RCC->AHB3ENR |= RCC_AHB3ENR_CRYPEN; (void)RCC->AHB3ENR; } \ + while (0) + #define WC_STM32_AES_CLK_DISABLE() \ + do { RCC->AHB3ENR &= ~RCC_AHB3ENR_CRYPEN; } while (0) + #endif + #ifdef RCC_AHB3ENR_SAESEN + #define WC_STM32_SAES_CLK_ENABLE() \ + do { RCC->AHB3ENR |= RCC_AHB3ENR_SAESEN; (void)RCC->AHB3ENR; } \ + while (0) + #define WC_STM32_SAES_CLK_DISABLE() \ + do { RCC->AHB3ENR &= ~RCC_AHB3ENR_SAESEN; } while (0) + #endif + #define WC_STM32_HASH_CLK_ENABLE() \ + do { RCC->AHB3ENR |= RCC_AHB3ENR_HASHEN; (void)RCC->AHB3ENR; } while (0) + #define WC_STM32_HASH_CLK_DISABLE() \ + do { RCC->AHB3ENR &= ~RCC_AHB3ENR_HASHEN; } while (0) + #define WC_STM32_RNG_CLK_ENABLE() \ + do { RCC->AHB3ENR |= RCC_AHB3ENR_RNGEN; (void)RCC->AHB3ENR; } while (0) +#endif + +/* Build-time AES IP instance selector. Default is the regular AES/CRYP + * peripheral; defining WOLFSSL_STM32_USE_SAES routes the BARE TinyAES + * register-access block to SAES (Secure AES) instead. The TinyAES + * register layout is identical for AES and SAES on H5/U5/WBA/C5. */ +#ifndef WC_STM32_AES_INST + #if defined(WOLFSSL_STM32_USE_SAES) && defined(SAES) + #define WC_STM32_AES_INST SAES + #elif defined(CRYP) + #define WC_STM32_AES_INST CRYP + #elif defined(AES) + /* AES-only chips (G0/L0/U0) -- no fat CRYP, no SAES. The + * symbol CRYP is the legacy alias many newer family headers + * keep for compatibility, but bottom-end chips drop it. */ + #define WC_STM32_AES_INST AES + #elif defined(WOLFSSL_STM32WB) && defined(AES1) + /* WB55 dual-core: CMSIS exposes AES1 (M4-side application AES) + * and AES2 (M0+ Cortex-M0+ radio-side, not addressable from + * the application core). There is no plain `AES` or `CRYP` + * alias in the device header, so pick AES1 directly. */ + #define WC_STM32_AES_INST AES1 + #elif defined(STM32_CRYPTO) + /* Only error when the board has actually asked for STM32_CRYPTO + * but no AES IP is reachable. Chips with NO_STM32_CRYPTO (e.g. + * F767/G491/F767 -- RNG-only parts) don't need an instance. */ + #error "STM32 BARE: no AES/CRYP/SAES instance pointer found" + #endif +#endif + +/* Companion macro for the IP-instance clock enable. Routes to + * WC_STM32_SAES_CLK_ENABLE when WOLFSSL_STM32_USE_SAES is set and the + * family arm above provided the SAES variant; otherwise falls back to + * the regular AES clock. The two are separate AHB enable bits on + * H5/U5/WBA/C5, so toggling the wrong one leaves the IP disabled. */ +#ifndef WC_STM32_AES_CLK_ENABLE_INST + #if defined(WOLFSSL_STM32_USE_SAES) && defined(WC_STM32_SAES_CLK_ENABLE) + #define WC_STM32_AES_CLK_ENABLE_INST() WC_STM32_SAES_CLK_ENABLE() + #else + #define WC_STM32_AES_CLK_ENABLE_INST() WC_STM32_AES_CLK_ENABLE() + #endif +#endif + +/* Some new-gen chips (STM32N6, STM32H7S3) ship CMSIS headers that + * define SAES_TypeDef but not AES_TypeDef. The BARE TinyAES driver + * helpers in stm32.c declare their parameter as `AES_TypeDef*` so the + * same function pointer can target both the regular AES and SAES + * instances on chips that have both. Provide a typedef alias when AES + * is missing -- the IP layout is identical between AES and SAES on + * every family in scope. Gated on having SAES but not the AES_CR_EN + * symbol (used as a sentinel that the CMSIS lacks the AES alias). */ +#if defined(SAES) && !defined(AES_CR_EN) && \ + !defined(WOLFSSL_STM32_AES_TYPEDEF_ALIAS) + typedef SAES_TypeDef AES_TypeDef; + #define WOLFSSL_STM32_AES_TYPEDEF_ALIAS +#endif + +/* SAES-only chips (e.g. STM32N6) have the TinyAES register layout but + * the CMSIS device header only defines SAES_CR_*, SAES_SR_*, SAES_ISR_*, + * SAES_ICR_* without companion AES_CR_* aliases. The BARE driver in + * stm32.c uses the AES_CR_* names directly; provide aliases here so the + * existing code compiles on SAES-only parts. */ +#if !defined(AES_CR_EN) && defined(SAES_CR_EN) + #define AES_CR_EN SAES_CR_EN + #define AES_CR_DATATYPE_1 SAES_CR_DATATYPE_1 + #define AES_CR_MODE SAES_CR_MODE + #define AES_CR_MODE_M SAES_CR_MODE_Msk + #define AES_CR_MODE_Msk SAES_CR_MODE_Msk + #define AES_CR_MODE_0 SAES_CR_MODE_0 + #define AES_CR_MODE_1 SAES_CR_MODE_1 + #define AES_CR_CHMOD_0 SAES_CR_CHMOD_0 + #define AES_CR_CHMOD_1 SAES_CR_CHMOD_1 + #define AES_CR_KEYSIZE SAES_CR_KEYSIZE + #define AES_CR_KEYSEL_0 SAES_CR_KEYSEL_0 + #define AES_CR_KMOD_0 SAES_CR_KMOD_0 + #define AES_CR_KEYPROT SAES_CR_KEYPROT + #define AES_CR_CCFC SAES_CR_CCFC + #define AES_CR_IPRST SAES_CR_IPRST + #define AES_SR_BUSY SAES_SR_BUSY + #define AES_SR_CCF SAES_SR_CCF + #define AES_ISR_CCF SAES_ISR_CCF + #define AES_ICR_CCF SAES_ICR_CCF +#endif + +/* Per-family direct-register clock-enable macro for the PKA peripheral. */ +#if defined(WOLFSSL_STM32WB) || defined(WOLFSSL_STM32WL) + /* WB55 / WL55: PKA clock is on AHB3 (V1 layout) */ + #define WC_STM32_PKA_CLK_ENABLE() \ + do { RCC->AHB3ENR |= RCC_AHB3ENR_PKAEN; (void)RCC->AHB3ENR; } while (0) +#elif defined(WOLFSSL_STM32U5) || defined(WOLFSSL_STM32U3) + /* U5 / U3: AHB2ENR1.PKAEN */ + #ifdef RCC_AHB2ENR1_PKAEN + #define WC_STM32_PKA_CLK_ENABLE() \ + do { RCC->AHB2ENR1 |= RCC_AHB2ENR1_PKAEN; (void)RCC->AHB2ENR1; } \ + while (0) + #endif +#elif defined(WOLFSSL_STM32H5) + #ifdef RCC_AHB2ENR_PKAEN + #define WC_STM32_PKA_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_PKAEN; (void)RCC->AHB2ENR; } \ + while (0) + #endif +#elif defined(WOLFSSL_STM32L5) + /* L5: PKA on AHB2ENR.PKAEN (bit 19). Only present on L562/L592 + * variants -- L552 has no PKA so the CMSIS bit is absent. */ + #ifdef RCC_AHB2ENR_PKAEN + #define WC_STM32_PKA_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_PKAEN; (void)RCC->AHB2ENR; } \ + while (0) + #endif +#elif defined(WOLFSSL_STM32G4) + #ifdef RCC_AHB2ENR_PKAEN + #define WC_STM32_PKA_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_PKAEN; (void)RCC->AHB2ENR; } \ + while (0) + #endif +#elif defined(WOLFSSL_STM32WBA) + /* WBA52: PKA on AHB2ENR.PKAEN bit 21 (NOT AHB1 like the rest of + * the WBA crypto IPs in some other variants -- the WBA52 RM places + * PKA, SAES, RNG all on AHB2 alongside other crypto). The earlier + * AHB1 placement was a copy-paste error from another family; + * `RCC_AHB1ENR_PKAEN` doesn't exist on WBA52 so the macro never + * defined, the clock never got enabled, and HAL_PKA_Init timed + * out at the CR.EN-stick check (CR readback = 0 = clock gated). */ + #ifdef RCC_AHB2ENR_PKAEN + #define WC_STM32_PKA_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_PKAEN; (void)RCC->AHB2ENR; } \ + while (0) + #endif +#elif defined(WOLFSSL_STM32C5) + #ifdef RCC_AHB2ENR_PKAEN + #define WC_STM32_PKA_CLK_ENABLE() \ + do { RCC->AHB2ENR |= RCC_AHB2ENR_PKAEN; (void)RCC->AHB2ENR; } \ + while (0) + #endif +#elif defined(WOLFSSL_STM32N6) + /* N6: PKA on AHB3 (same bank as HASH/RNG/CRYP/SAES). V2 layout. */ + #ifdef RCC_AHB3ENR_PKAEN + #define WC_STM32_PKA_CLK_ENABLE() \ + do { RCC->AHB3ENR |= RCC_AHB3ENR_PKAEN; (void)RCC->AHB3ENR; } \ + while (0) + #endif +#elif defined(WOLFSSL_STM32H7S) + /* H7S: PKA on AHB3 alongside HASH/RNG/CRYP/SAES. V2 layout. */ + #ifdef RCC_AHB3ENR_PKAEN + #define WC_STM32_PKA_CLK_ENABLE() \ + do { RCC->AHB3ENR |= RCC_AHB3ENR_PKAEN; (void)RCC->AHB3ENR; } \ + while (0) + #endif +#endif + +/* HAL-legacy macros that the existing direct-register HASH path depends on. + * Without HAL these aren't otherwise visible. */ +#if defined(WOLFSSL_STM32H5) || defined(WOLFSSL_STM32MP13) || \ + defined(WOLFSSL_STM32N6) || defined(WOLFSSL_STM32H7S) || \ + defined(WOLFSSL_STM32U3) || defined(WOLFSSL_STM32C5) + /* New-generation HASH IP. The CMSIS struct shape varies within the + * family list -- H5 renames the instance digest registers from + * `HR[5]` to `HRA[5]`, but U3 / N6 keep the legacy `HR[5]` name + * even though the IP otherwise behaves like the new generation. + * Gate the macro on H5 only (verified by inspection of each + * family's CMSIS header). */ + #if defined(WOLFSSL_STM32H5) + #define WC_STM32_HASH_INSTANCE_HRA + #endif + /* 4-bit ALGO field at bits 20:17 */ + #define HASH_ALGOSELECTION_SHA1 0u + #define HASH_ALGOSELECTION_SHA224 HASH_CR_ALGO_1 + #define HASH_ALGOSELECTION_SHA256 (HASH_CR_ALGO_0 | HASH_CR_ALGO_1) + #define HASH_ALGOSELECTION_SHA384 (HASH_CR_ALGO_2 | HASH_CR_ALGO_3) + #define HASH_ALGOSELECTION_SHA512 (HASH_CR_ALGO_0 | HASH_CR_ALGO_1 | \ + HASH_CR_ALGO_2 | HASH_CR_ALGO_3) + #define HASH_ALGOSELECTION_SHA512_224 (HASH_CR_ALGO_0 | HASH_CR_ALGO_2 | \ + HASH_CR_ALGO_3) + #define HASH_ALGOSELECTION_SHA512_256 (HASH_CR_ALGO_1 | HASH_CR_ALGO_2 | \ + HASH_CR_ALGO_3) +#else + /* Older HASH IP (F4/F7/L4 family) ALGO bit mapping (per HAL): + * SHA1 = 0 + * MD5 = ALGO_0 + * SHA224 = ALGO_1 + * SHA256 = ALGO_0 | ALGO_1 + */ + #define HASH_ALGOSELECTION_SHA1 0u + #define HASH_ALGOSELECTION_MD5 HASH_CR_ALGO_0 + #ifdef HASH_CR_ALGO_1 + #define HASH_ALGOSELECTION_SHA224 HASH_CR_ALGO_1 + #define HASH_ALGOSELECTION_SHA256 (HASH_CR_ALGO_0 | HASH_CR_ALGO_1) + #endif +#endif + +/* Legacy CamelCase aliases */ +#ifdef HASH_ALGOSELECTION_SHA1 + #define HASH_AlgoSelection_SHA1 HASH_ALGOSELECTION_SHA1 +#endif +#ifdef HASH_ALGOSELECTION_SHA224 + #define HASH_AlgoSelection_SHA224 HASH_ALGOSELECTION_SHA224 +#endif +#ifdef HASH_ALGOSELECTION_SHA256 + #define HASH_AlgoSelection_SHA256 HASH_ALGOSELECTION_SHA256 +#endif +#ifdef HASH_ALGOSELECTION_SHA384 + #define HASH_AlgoSelection_SHA384 HASH_ALGOSELECTION_SHA384 +#endif +#ifdef HASH_ALGOSELECTION_SHA512 + #define HASH_AlgoSelection_SHA512 HASH_ALGOSELECTION_SHA512 +#endif +#ifdef HASH_ALGOSELECTION_SHA512_224 + #define HASH_AlgoSelection_SHA512_224 HASH_ALGOSELECTION_SHA512_224 +#endif +#ifdef HASH_ALGOSELECTION_SHA512_256 + #define HASH_AlgoSelection_SHA512_256 HASH_ALGOSELECTION_SHA512_256 +#endif +#ifdef HASH_ALGOSELECTION_MD5 + #define HASH_AlgoSelection_MD5 HASH_ALGOSELECTION_MD5 +#endif + +#define HASH_ALGOMODE_HASH 0u +#ifdef HASH_CR_MODE + #define HASH_ALGOMODE_HMAC HASH_CR_MODE +#endif +/* Byte-stream input (auto byte-swap) */ +#ifdef HASH_CR_DATATYPE_1 + #define HASH_DATATYPE_8B HASH_CR_DATATYPE_1 +#elif defined(HASH_CR_DATATYPE_0) + #define HASH_DATATYPE_8B HASH_CR_DATATYPE_0 +#endif + +#endif /* WOLFSSL_STM32_BARE */ + + #ifdef STM32_HASH #include /* for uint32_t */ @@ -38,7 +525,8 @@ /* The HASH_DIGEST register indicates SHA224/SHA256 support */ #define STM32_HASH_SHA2 #if defined(WOLFSSL_STM32MP13) || defined(WOLFSSL_STM32H7S) || \ - defined(WOLFSSL_STM32N6) || defined(WOLFSSL_STM32H5) + defined(WOLFSSL_STM32N6) || defined(WOLFSSL_STM32H5) || \ + defined(WOLFSSL_STM32U3) #define HASH_CR_SIZE 103 #define HASH_MAX_DIGEST 64 /* Up to SHA512 */ @@ -68,7 +556,8 @@ /* These HASH HAL's have no MD5 implementation */ #if defined(WOLFSSL_STM32MP13) || defined(WOLFSSL_STM32H7S) || \ - defined(WOLFSSL_STM32N6) || defined(WOLFSSL_STM32H5) + defined(WOLFSSL_STM32N6) || defined(WOLFSSL_STM32H5) || \ + defined(WOLFSSL_STM32U3) || defined(WOLFSSL_STM32C5) #define STM32_NOMD5 #endif @@ -163,7 +652,8 @@ int wc_Stm32_Hmac_Final(STM32_HASH_Context* stmCtx, word32 algo, #endif #ifndef NO_AES - #if !defined(STM32_CRYPTO_AES_GCM) && (defined(WOLFSSL_STM32F4) || \ + #if !defined(STM32_CRYPTO_AES_GCM) && !defined(WOLFSSL_STM32_BARE) && \ + (defined(WOLFSSL_STM32F4) || \ defined(WOLFSSL_STM32F7) || defined(WOLFSSL_STM32L4) || \ defined(WOLFSSL_STM32L5) || defined(WOLFSSL_STM32H7) || \ defined(WOLFSSL_STM32U5) || defined(WOLFSSL_STM32U3) || \ @@ -173,6 +663,13 @@ int wc_Stm32_Hmac_Final(STM32_HASH_Context* stmCtx, word32 algo, /* Hardware supports AES GCM acceleration */ #define STM32_CRYPTO_AES_GCM #endif + /* Under WOLFSSL_STM32_BARE on the CRYP IP (F2/F4/F7/H7/MP13), the GCM + * HW phase machine (init/header/payload/final) is engaged for whole- + * block PT with a 12-byte IV; partial blocks and non-12B IVs return + * CRYPTOCB_UNAVAILABLE so aes.c falls back to SW GHASH + HW ECB. On + * the TinyAES IP the BARE driver always returns CRYPTOCB_UNAVAILABLE + * for GCM (no HW phase machine) and the SW GHASH + HW ECB path is + * used. GCM decrypt is always SW + HW ECB on both IPs in v1. */ #if defined(WOLFSSL_STM32WB) || defined(WOLFSSL_STM32WL) || \ defined(WOLFSSL_STM32WBA) @@ -186,12 +683,14 @@ int wc_Stm32_Hmac_Final(STM32_HASH_Context* stmCtx, word32 algo, #endif #if defined(WOLFSSL_STM32L4) || defined(WOLFSSL_STM32L5) || \ defined(WOLFSSL_STM32U5) || defined(WOLFSSL_STM32U3) || \ - defined(WOLFSSL_STM32H5) || defined(WOLFSSL_STM32G0) + defined(WOLFSSL_STM32H5) || defined(WOLFSSL_STM32G0) || \ + defined(WOLFSSL_STM32G4) || defined(WOLFSSL_STM32C5) #if defined(WOLFSSL_STM32L4) || defined(WOLFSSL_STM32U5) || \ - defined(WOLFSSL_STM32U3) || defined(WOLFSSL_STM32G0) + defined(WOLFSSL_STM32U3) || defined(WOLFSSL_STM32G0) || \ + defined(WOLFSSL_STM32G4) || defined(WOLFSSL_STM32C5) #define STM32_CRYPTO_AES_ONLY /* crypto engine only supports AES */ #endif - #if defined(WOLFSSL_STM32H5) + #if defined(WOLFSSL_STM32H5) || defined(WOLFSSL_STM32C5) #define __HAL_RCC_CRYP_CLK_DISABLE __HAL_RCC_AES_CLK_DISABLE #define __HAL_RCC_CRYP_CLK_ENABLE __HAL_RCC_AES_CLK_ENABLE #endif @@ -234,7 +733,23 @@ int wc_Stm32_Hmac_Final(STM32_HASH_Context* stmCtx, word32 algo, #define STM32_GCM_IV_START 2 struct Aes; - #ifdef WOLFSSL_STM32_CUBEMX + #ifdef WOLFSSL_STM32_BARE + /* Bare-metal direct-register AES driver. ECB and CBC are HW-native; + * CTR is provided automatically via the ECB-as-transform path in + * aes.c (XTRANSFORM_AESCTRBLOCK); GCM is HW-native for the case + * the CRYP IP supports (12-byte IV + whole-block PT) and returns + * CRYPTOCB_UNAVAILABLE otherwise so aes.c can fall back to SW + * GHASH (which still uses HW ECB for the underlying AES blocks). */ + int wc_Stm32_Aes_Ecb(struct Aes* aes, byte* out, const byte* in, + word32 sz, int isEnc); + int wc_Stm32_Aes_Cbc(struct Aes* aes, byte* out, const byte* in, + word32 sz, int isEnc); + int wc_Stm32_Aes_Gcm(struct Aes* aes, byte* out, const byte* in, + word32 sz, + const byte* iv, word32 ivSz, + byte* tag, word32 tagSz, + const byte* aad, word32 aadSz, int isEnc); + #elif defined(WOLFSSL_STM32_CUBEMX) int wc_Stm32_Aes_Init(struct Aes* aes, CRYP_HandleTypeDef* hcryp, int useSAES); void wc_Stm32_Aes_Cleanup(void); @@ -242,20 +757,63 @@ int wc_Stm32_Hmac_Final(STM32_HASH_Context* stmCtx, word32 algo, int wc_Stm32_Aes_Init(struct Aes* aes, CRYP_InitTypeDef* cryptInit, CRYP_KeyInitTypeDef* keyInit); void wc_Stm32_Aes_Cleanup(void); - #endif /* WOLFSSL_STM32_CUBEMX */ + #endif /* WOLFSSL_STM32_BARE / WOLFSSL_STM32_CUBEMX / StdPeriph */ #endif /* !NO_AES */ #endif /* STM32_CRYPTO */ -#if defined(WOLFSSL_STM32U5_DHUK) && !defined(WOLFSSL_STM32U5_DHUK_DEVID) - #define WOLFSSL_STM32U5_DHUK_DEVID 808 - #define WOLFSSL_STM32U5_SAES_DEVID 807 - #define WOLFSSL_STM32U5_DHUK_WRAPPED_DEVID 809 +/* DHUK (Device Hardware Unique Key) -- SAES key wrap / unwrap using a + * silicon-bound key. Originally introduced for STM32U5 only; the + * underlying SAES + DHUK infrastructure is also present on U3, H5, + * WBA, and C5. Use WOLFSSL_DHUK going forward; WOLFSSL_STM32U5_DHUK + * is kept as a backwards-compatible alias for one release cycle. */ +#if defined(WOLFSSL_STM32U5_DHUK) && !defined(WOLFSSL_DHUK) + #define WOLFSSL_DHUK +#endif +#if defined(WOLFSSL_DHUK) && !defined(WOLFSSL_STM32U5_DHUK) + #define WOLFSSL_STM32U5_DHUK +#endif + +/* Family gate: only families that actually have SAES + DHUK silicon. + * L5 has a "secure AES" instance but its CR layout does not include + * KMOD / KEYSEL fields -- it does not implement the same DHUK key- + * wrap protocol as U5/U3/H5/WBA/C5. L5 is intentionally excluded. */ +#if defined(WOLFSSL_DHUK) && \ + (defined(WOLFSSL_STM32U5) || defined(WOLFSSL_STM32U3) || \ + defined(WOLFSSL_STM32H5) || defined(WOLFSSL_STM32WBA) || \ + defined(WOLFSSL_STM32C5) || defined(WOLFSSL_STM32H7S)) + #define WC_STM32_HAS_DHUK +#endif + +#if defined(WOLFSSL_DHUK) && !defined(WOLFSSL_DHUK_DEVID) + /* Generic DHUK / SAES device IDs (used with WOLF_CRYPTO_CB devId + * dispatch in aes.c hot paths). */ + #define WOLFSSL_DHUK_DEVID 808 + #define WOLFSSL_SAES_DEVID 807 + #define WOLFSSL_DHUK_WRAPPED_DEVID 809 + /* Legacy aliases preserved verbatim for any out-of-tree caller. */ + #define WOLFSSL_STM32U5_DHUK_DEVID WOLFSSL_DHUK_DEVID + #define WOLFSSL_STM32U5_SAES_DEVID WOLFSSL_SAES_DEVID + #define WOLFSSL_STM32U5_DHUK_WRAPPED_DEVID WOLFSSL_DHUK_WRAPPED_DEVID + int wc_Stm32_Aes_Wrap(struct Aes* aes, const byte* in, word32 inSz, byte* out, word32* outSz, const byte* iv, int ivSz); + int wc_Stm32_Aes_SetDHUK_IV(struct Aes* aes, const byte* iv, int ivSz); +#ifdef WOLFSSL_STM32_BARE + /* Combined DHUK unwrap + ECB op (BARE only). aes->key holds the + * wrapped key; the IP unwraps it with DHUK then runs the op. */ + int wc_Stm32_Aes_DhukOp(struct Aes* aes, byte* out, const byte* in, + word32 sz, int isEnc); +#endif +#ifndef WOLFSSL_STM32_BARE + /* The HAL UnWrap path returns its hcryp configuration to the + * caller for reuse on the follow-on encrypt / decrypt. Under BARE + * there is no CRYP_HandleTypeDef and the SAES keystore is held by + * the IP itself, so a different integration is needed -- this + * prototype stays HAL-only until that follow-up lands. */ int wc_Stm32_Aes_UnWrap(struct Aes* aes, CRYP_HandleTypeDef* hcryp, const byte* in, word32 inSz, const byte* iv, int ivSz); - int wc_Stm32_Aes_SetDHUK_IV(struct Aes* aes, const byte* iv, int ivSz); +#endif #endif #if defined(WOLFSSL_STM32_PKA) && defined(HAVE_ECC) diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 12f25cc5346..71fd86ca3e4 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -2206,13 +2206,16 @@ extern void uITRON4_free(void *p) ; #if defined(WOLFSSL_STM32F2) || defined(WOLFSSL_STM32F4) || \ defined(WOLFSSL_STM32F7) || defined(WOLFSSL_STM32F1) || \ + defined(WOLFSSL_STM32F3) || \ defined(WOLFSSL_STM32L4) || defined(WOLFSSL_STM32L5) || \ defined(WOLFSSL_STM32WB) || defined(WOLFSSL_STM32H7) || \ defined(WOLFSSL_STM32G0) || defined(WOLFSSL_STM32U5) || \ - defined(WOLFSSL_STM32U3) || defined(WOLFSSL_STM32H5) || \ + defined(WOLFSSL_STM32U3) || defined(WOLFSSL_STM32U0) || \ + defined(WOLFSSL_STM32H5) || \ defined(WOLFSSL_STM32WL) || defined(WOLFSSL_STM32G4) || \ defined(WOLFSSL_STM32MP13) || defined(WOLFSSL_STM32H7S) || \ - defined(WOLFSSL_STM32WBA) || defined(WOLFSSL_STM32N6) + defined(WOLFSSL_STM32WBA) || defined(WOLFSSL_STM32N6) || \ + defined(WOLFSSL_STM32C5) #define SIZEOF_LONG_LONG 8 #ifndef CHAR_BIT @@ -2234,7 +2237,7 @@ extern void uITRON4_free(void *p) ; #if defined(WOLFSSL_STM32L4) || defined(WOLFSSL_STM32L5) || \ defined(WOLFSSL_STM32WB) || defined(WOLFSSL_STM32U5) || \ defined(WOLFSSL_STM32U3) || defined(WOLFSSL_STM32WL) || \ - defined(WOLFSSL_STM32WBA) + defined(WOLFSSL_STM32WBA) || defined(WOLFSSL_STM32C5) #define NO_AES_192 /* hardware does not support 192-bit */ #endif #endif @@ -2250,7 +2253,72 @@ extern void uITRON4_free(void *p) ; #define KEIL_INTRINSICS #endif #define NO_OLD_RNGNAME - #ifdef WOLFSSL_STM32_CUBEMX + + #if defined(WOLFSSL_STM32_BARE) && defined(WOLFSSL_STM32_CUBEMX) + #error "WOLFSSL_STM32_BARE and WOLFSSL_STM32_CUBEMX are mutually \ + exclusive" + #endif + /* WOLFSSL_STM32_PKA is now supported under WOLFSSL_STM32_BARE via the + * direct-register PKA driver in wolfcrypt/src/port/st/stm32.c. */ + + #ifdef WOLFSSL_STM32_BARE + /* Direct register access; no HAL or StdPeriph driver. Pull in only the + * CMSIS device header. Existing direct-register HASH path is reused; + * RNG goes through the existing WOLFSSL_STM32_RNG_NOLIB path. */ + #ifndef WOLFSSL_STM32_RNG_NOLIB + #define WOLFSSL_STM32_RNG_NOLIB + #endif + #if defined(WOLFSSL_STM32F1) + #include "stm32f1xx.h" + #elif defined(WOLFSSL_STM32F2) + #include "stm32f2xx.h" + #elif defined(WOLFSSL_STM32F3) + #include "stm32f3xx.h" + #elif defined(WOLFSSL_STM32F4) + #include "stm32f4xx.h" + #elif defined(WOLFSSL_STM32F7) + #include "stm32f7xx.h" + #elif defined(WOLFSSL_STM32L4) + #include "stm32l4xx.h" + #elif defined(WOLFSSL_STM32L5) + #include "stm32l5xx.h" + #elif defined(WOLFSSL_STM32H7S) + #include "stm32h7rsxx.h" + #elif defined(WOLFSSL_STM32H7) + #include "stm32h7xx.h" + #elif defined(WOLFSSL_STM32WB) + #include "stm32wbxx.h" + #elif defined(WOLFSSL_STM32WL) + #include "stm32wlxx.h" + #elif defined(WOLFSSL_STM32G0) + #include "stm32g0xx.h" + #elif defined(WOLFSSL_STM32G4) + #include "stm32g4xx.h" + #elif defined(WOLFSSL_STM32U5) + #include "stm32u5xx.h" + #elif defined(WOLFSSL_STM32U3) + #include "stm32u3xx.h" + #elif defined(WOLFSSL_STM32U0) + #include "stm32u0xx.h" + #elif defined(WOLFSSL_STM32H5) + #include "stm32h5xx.h" + #elif defined(WOLFSSL_STM32C5) + #include "stm32c5xx.h" + #elif defined(WOLFSSL_STM32N6) + #include "stm32n6xx.h" + #elif defined(WOLFSSL_STM32MP13) + #ifndef __ASSEMBLER__ + #include "stm32mp13xx.h" + #endif + #elif defined(WOLFSSL_STM32WBA) + #include "stm32wbaxx.h" + #else + #error "WOLFSSL_STM32_BARE requires a STM32 family macro \ + (e.g. WOLFSSL_STM32F4, WOLFSSL_STM32H5, WOLFSSL_STM32U5, \ + WOLFSSL_STM32N6, ...). Define the matching family flag in \ + user_settings.h." + #endif + #elif defined(WOLFSSL_STM32_CUBEMX) #if defined(WOLFSSL_STM32F1) #include "stm32f1xx_hal.h" #elif defined(WOLFSSL_STM32F2) @@ -2283,6 +2351,8 @@ extern void uITRON4_free(void *p) ; #include "stm32u3xx_hal.h" #elif defined(WOLFSSL_STM32H5) #include "stm32h5xx_hal.h" + #elif defined(WOLFSSL_STM32C5) + #include "stm32c5xx_hal.h" #elif defined(WOLFSSL_STM32N6) #include "stm32n6xx_hal.h" #elif defined(WOLFSSL_STM32MP13)