From 42e5de830ff1c4c161ff132ecc1ea185fe1e28d4 Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Tue, 13 Oct 2026 10:10:59 -0400 Subject: [PATCH 01/22] [crypto, stm32wb, stm32wba, stm32n6] Encode GCM lengths as 64-bit values - Write both 32-bit halves of the AAD and payload bit lengths in the WB/WBA GCM and GMAC final block - Store the streaming GCM payload byte count as uint64_t on WB, WBA and N6 --- src/crypto/stm32n6_cryp.c | 2 +- src/crypto/stm32wb_aes.c | 30 ++++++++++++++++++++---------- src/crypto/stm32wba_aes.c | 30 ++++++++++++++++++++---------- wolfHAL/crypto/stm32n6_cryp.h | 2 +- wolfHAL/crypto/stm32wb_aes.h | 2 +- wolfHAL/crypto/stm32wba_aes.h | 2 +- 6 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/crypto/stm32n6_cryp.c b/src/crypto/stm32n6_cryp.c index 921fd2f..b712fe7 100644 --- a/src/crypto/stm32n6_cryp.c +++ b/src/crypto/stm32n6_cryp.c @@ -1069,7 +1069,7 @@ whal_Error whal_Stm32n6_CrypAesGcm_Finalize(whal_AesGcm *dev, Enable(base); aadBits = (uint64_t)g_aesGcmState.aadSz * 8; - payloadBits = (uint64_t)g_aesGcmState.dataSz * 8; + payloadBits = g_aesGcmState.dataSz * 8; whal_Reg_Write(base, CRYP_DINR_REG, (uint32_t)(aadBits >> 32)); whal_Reg_Write(base, CRYP_DINR_REG, (uint32_t)aadBits); whal_Reg_Write(base, CRYP_DINR_REG, (uint32_t)(payloadBits >> 32)); diff --git a/src/crypto/stm32wb_aes.c b/src/crypto/stm32wb_aes.c index 2ddf314..cd71e4d 100644 --- a/src/crypto/stm32wb_aes.c +++ b/src/crypto/stm32wb_aes.c @@ -620,6 +620,8 @@ whal_Error whal_Stm32wb_AesGcm_Oneshot(whal_AesGcm *dev, whal_Crypto_Dir dir, size_t mode; size_t i; uint8_t tagBuf[16]; + uint64_t aadBits; + uint64_t payloadBits; whal_Error err; (void)dev; @@ -767,10 +769,12 @@ whal_Error whal_Stm32wb_AesGcm_Oneshot(whal_AesGcm *dev, whal_Crypto_Dir dir, if (aadSz == 0 && sz == 0) whal_Reg_Update(base, AES_CR_REG, AES_CR_EN_Msk, AES_CR_EN_Msk); - whal_Reg_Write(base, AES_DINR_REG, 0); - whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(aadSz * 8)); - whal_Reg_Write(base, AES_DINR_REG, 0); - whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(sz * 8)); + aadBits = (uint64_t)aadSz * 8; + payloadBits = (uint64_t)sz * 8; + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(aadBits >> 32)); + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)aadBits); + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(payloadBits >> 32)); + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)payloadBits); err = WaitForCCF(base, cfg->timeout); if (err) @@ -998,6 +1002,8 @@ whal_Error whal_Stm32wb_AesGcm_Finalize(whal_AesGcm *dev, whal_Stm32wb_AesGcm_State *st = (whal_Stm32wb_AesGcm_State *)whal_Stm32wb_AesGcm_Dev.state; uint8_t tagBuf[16]; + uint64_t aadBits; + uint64_t payloadBits; size_t i; whal_Error err; @@ -1020,10 +1026,12 @@ whal_Error whal_Stm32wb_AesGcm_Finalize(whal_AesGcm *dev, if (st->aadSz == 0 && st->dataSz == 0) whal_Reg_Update(base, AES_CR_REG, AES_CR_EN_Msk, AES_CR_EN_Msk); - whal_Reg_Write(base, AES_DINR_REG, 0); - whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(st->aadSz * 8)); - whal_Reg_Write(base, AES_DINR_REG, 0); - whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(st->dataSz * 8)); + aadBits = (uint64_t)st->aadSz * 8; + payloadBits = st->dataSz * 8; + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(aadBits >> 32)); + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)aadBits); + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(payloadBits >> 32)); + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)payloadBits); err = WaitForCCF(base, cfg->timeout); if (err) { @@ -1067,6 +1075,7 @@ whal_Error whal_Stm32wb_AesGmac_Oneshot(whal_AesGmac *dev, size_t keySizeBit; size_t i; uint8_t tagBuf[16]; + uint64_t aadBits; whal_Error err; (void)dev; @@ -1161,8 +1170,9 @@ whal_Error whal_Stm32wb_AesGmac_Oneshot(whal_AesGmac *dev, if (aadSz == 0) whal_Reg_Update(base, AES_CR_REG, AES_CR_EN_Msk, AES_CR_EN_Msk); - whal_Reg_Write(base, AES_DINR_REG, 0); - whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(aadSz * 8)); + aadBits = (uint64_t)aadSz * 8; + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(aadBits >> 32)); + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)aadBits); whal_Reg_Write(base, AES_DINR_REG, 0); whal_Reg_Write(base, AES_DINR_REG, 0); diff --git a/src/crypto/stm32wba_aes.c b/src/crypto/stm32wba_aes.c index 33a786d..16f3947 100644 --- a/src/crypto/stm32wba_aes.c +++ b/src/crypto/stm32wba_aes.c @@ -726,6 +726,8 @@ whal_Error whal_Stm32wba_AesGcm_Oneshot(whal_AesGcm *dev, whal_Crypto_Dir dir, size_t mode; size_t i; uint8_t tagBuf[16]; + uint64_t aadBits; + uint64_t payloadBits; whal_Error err; (void)dev; @@ -875,10 +877,12 @@ whal_Error whal_Stm32wba_AesGcm_Oneshot(whal_AesGcm *dev, whal_Crypto_Dir dir, if (aadSz == 0 && sz == 0) whal_Reg_Update(base, AES_CR_REG, AES_CR_EN_Msk, AES_CR_EN_Msk); - whal_Reg_Write(base, AES_DINR_REG, 0); - whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(aadSz * 8)); - whal_Reg_Write(base, AES_DINR_REG, 0); - whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(sz * 8)); + aadBits = (uint64_t)aadSz * 8; + payloadBits = (uint64_t)sz * 8; + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(aadBits >> 32)); + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)aadBits); + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(payloadBits >> 32)); + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)payloadBits); err = WaitForCCF(base, cfg->timeout); if (err) @@ -1108,6 +1112,8 @@ whal_Error whal_Stm32wba_AesGcm_Finalize(whal_AesGcm *dev, whal_Stm32wba_AesGcm_State *st = (whal_Stm32wba_AesGcm_State *)whal_Stm32wba_AesGcm_Dev.state; uint8_t tagBuf[16]; + uint64_t aadBits; + uint64_t payloadBits; size_t i; whal_Error err; @@ -1130,10 +1136,12 @@ whal_Error whal_Stm32wba_AesGcm_Finalize(whal_AesGcm *dev, if (st->aadSz == 0 && st->dataSz == 0) whal_Reg_Update(base, AES_CR_REG, AES_CR_EN_Msk, AES_CR_EN_Msk); - whal_Reg_Write(base, AES_DINR_REG, 0); - whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(st->aadSz * 8)); - whal_Reg_Write(base, AES_DINR_REG, 0); - whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(st->dataSz * 8)); + aadBits = (uint64_t)st->aadSz * 8; + payloadBits = st->dataSz * 8; + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(aadBits >> 32)); + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)aadBits); + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(payloadBits >> 32)); + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)payloadBits); err = WaitForCCF(base, cfg->timeout); if (err) { @@ -1177,6 +1185,7 @@ whal_Error whal_Stm32wba_AesGmac_Oneshot(whal_AesGmac *dev, size_t keySizeBit; size_t i; uint8_t tagBuf[16]; + uint64_t aadBits; whal_Error err; (void)dev; @@ -1273,8 +1282,9 @@ whal_Error whal_Stm32wba_AesGmac_Oneshot(whal_AesGmac *dev, if (aadSz == 0) whal_Reg_Update(base, AES_CR_REG, AES_CR_EN_Msk, AES_CR_EN_Msk); - whal_Reg_Write(base, AES_DINR_REG, 0); - whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(aadSz * 8)); + aadBits = (uint64_t)aadSz * 8; + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)(aadBits >> 32)); + whal_Reg_Write(base, AES_DINR_REG, (uint32_t)aadBits); whal_Reg_Write(base, AES_DINR_REG, 0); whal_Reg_Write(base, AES_DINR_REG, 0); diff --git a/wolfHAL/crypto/stm32n6_cryp.h b/wolfHAL/crypto/stm32n6_cryp.h index ff26707..6a94624 100644 --- a/wolfHAL/crypto/stm32n6_cryp.h +++ b/wolfHAL/crypto/stm32n6_cryp.h @@ -48,7 +48,7 @@ typedef struct { */ typedef struct { size_t aadSz; - size_t dataSz; + uint64_t dataSz; uint8_t partial; /* A partial block was processed; no further Process */ } whal_Stm32n6_AesGcm_State; diff --git a/wolfHAL/crypto/stm32wb_aes.h b/wolfHAL/crypto/stm32wb_aes.h index 8acc1e9..06d14f3 100644 --- a/wolfHAL/crypto/stm32wb_aes.h +++ b/wolfHAL/crypto/stm32wb_aes.h @@ -48,7 +48,7 @@ typedef struct { */ typedef struct { size_t aadSz; - size_t dataSz; + uint64_t dataSz; uint8_t partial; /* A partial block was processed; no further Process */ } whal_Stm32wb_AesGcm_State; diff --git a/wolfHAL/crypto/stm32wba_aes.h b/wolfHAL/crypto/stm32wba_aes.h index b640877..ae5b41b 100644 --- a/wolfHAL/crypto/stm32wba_aes.h +++ b/wolfHAL/crypto/stm32wba_aes.h @@ -48,7 +48,7 @@ typedef struct { */ typedef struct { size_t aadSz; - size_t dataSz; + uint64_t dataSz; uint8_t partial; /* A partial block was processed; no further Process */ } whal_Stm32wba_AesGcm_State; From 9bfb88ab6967704cffc801c45ef9da073869654d Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Tue, 13 Oct 2026 10:43:53 -0400 Subject: [PATCH 02/22] [crypto, stm32wb, stm32wba, stm32n6, test] Add Stop to AES ECB/CBC/CTR streaming - Add whal_AesEcb_Stop, whal_AesCbc_Stop and whal_AesCtr_Stop with a Stop vtable entry - Implement Stop on WB, WBA/U5 and N6: disable the engine and clear the key and IV registers - Document Stop in writing_a_driver.md - Add Start/Process/Stop streaming tests for ECB, CBC and CTR that check against Oneshot output --- docs/writing_a_driver.md | 6 +++++- src/crypto/aes_cbc.c | 9 +++++++++ src/crypto/aes_ctr.c | 9 +++++++++ src/crypto/aes_ecb.c | 9 +++++++++ src/crypto/stm32n6_cryp.c | 33 +++++++++++++++++++++++++++++++++ src/crypto/stm32wb_aes.c | 31 +++++++++++++++++++++++++++++++ src/crypto/stm32wba_aes.c | 31 +++++++++++++++++++++++++++++++ tests/crypto/test_aes_cbc.c | 35 +++++++++++++++++++++++++++++++++++ tests/crypto/test_aes_ctr.c | 35 +++++++++++++++++++++++++++++++++++ tests/crypto/test_aes_ecb.c | 35 +++++++++++++++++++++++++++++++++++ wolfHAL/crypto/crypto.h | 21 +++++++++++++++++++++ wolfHAL/crypto/stm32n6_cryp.h | 24 ++++++++++++++++++++++++ wolfHAL/crypto/stm32wb_aes.h | 24 ++++++++++++++++++++++++ wolfHAL/crypto/stm32wba_aes.h | 24 ++++++++++++++++++++++++ 14 files changed, 325 insertions(+), 1 deletion(-) diff --git a/docs/writing_a_driver.md b/docs/writing_a_driver.md index 9f1cfa9..156ec55 100644 --- a/docs/writing_a_driver.md +++ b/docs/writing_a_driver.md @@ -1180,7 +1180,7 @@ the HMAC key for the outer hash pass). Each algorithm defines a vtable with typed function pointers. The available operations depend on the algorithm category: -**Block ciphers** have `Oneshot`, `Start`, and `Process`: +**Block ciphers** have `Oneshot`, `Start`, `Process`, and `Stop`: ```c typedef struct { @@ -1193,6 +1193,7 @@ typedef struct { const void *iv); whal_Error (*Process)(whal_AesCbc *dev, const void *in, void *out, size_t sz); + whal_Error (*Stop)(whal_AesCbc *dev); } whal_AesCbcDriver; ``` @@ -1272,6 +1273,8 @@ typedef struct { for AEAD, digest for hash/HMAC). Only present on algorithms that need a finalization step. Block ciphers (ECB, CBC, CTR) do not have Finalize because each Process call produces complete output. +- **Stop** ends a block cipher (ECB, CBC, CTR) streaming session. It disables + the engine and clears the key and IV from the hardware. Unsupported parameter combinations (e.g. AES-192 on hardware that only supports 128/256) return `WHAL_ENOTSUP`. @@ -1332,6 +1335,7 @@ Streaming (block cipher): whal_AesCbc_Start(BOARD_AES_CBC_DEV, WHAL_CRYPTO_ENCRYPT, key, 32, iv); whal_AesCbc_Process(BOARD_AES_CBC_DEV, block1, out1, 16); whal_AesCbc_Process(BOARD_AES_CBC_DEV, block2, out2, 16); +whal_AesCbc_Stop(BOARD_AES_CBC_DEV); ``` ### Math Primitives diff --git a/src/crypto/aes_cbc.c b/src/crypto/aes_cbc.c index ea3607b..d6957f3 100644 --- a/src/crypto/aes_cbc.c +++ b/src/crypto/aes_cbc.c @@ -53,3 +53,12 @@ whal_Error whal_AesCbc_Process(whal_AesCbc *dev, return WHAL_ENOTSUP; return dev->driver->Process(dev, in, out, sz); } + +whal_Error whal_AesCbc_Stop(whal_AesCbc *dev) +{ + if (!dev) + return WHAL_EINVAL; + if (!dev->driver || !dev->driver->Stop) + return WHAL_ENOTSUP; + return dev->driver->Stop(dev); +} diff --git a/src/crypto/aes_ctr.c b/src/crypto/aes_ctr.c index 6824fe1..4c3e05e 100644 --- a/src/crypto/aes_ctr.c +++ b/src/crypto/aes_ctr.c @@ -53,3 +53,12 @@ whal_Error whal_AesCtr_Process(whal_AesCtr *dev, return WHAL_ENOTSUP; return dev->driver->Process(dev, in, out, sz); } + +whal_Error whal_AesCtr_Stop(whal_AesCtr *dev) +{ + if (!dev) + return WHAL_EINVAL; + if (!dev->driver || !dev->driver->Stop) + return WHAL_ENOTSUP; + return dev->driver->Stop(dev); +} diff --git a/src/crypto/aes_ecb.c b/src/crypto/aes_ecb.c index 76109d6..90b90cc 100644 --- a/src/crypto/aes_ecb.c +++ b/src/crypto/aes_ecb.c @@ -51,3 +51,12 @@ whal_Error whal_AesEcb_Process(whal_AesEcb *dev, return WHAL_ENOTSUP; return dev->driver->Process(dev, in, out, sz); } + +whal_Error whal_AesEcb_Stop(whal_AesEcb *dev) +{ + if (!dev) + return WHAL_EINVAL; + if (!dev->driver || !dev->driver->Stop) + return WHAL_ENOTSUP; + return dev->driver->Stop(dev); +} diff --git a/src/crypto/stm32n6_cryp.c b/src/crypto/stm32n6_cryp.c index b712fe7..f5794b0 100644 --- a/src/crypto/stm32n6_cryp.c +++ b/src/crypto/stm32n6_cryp.c @@ -324,6 +324,15 @@ static whal_Error Process_BlockCipher(const uint8_t *in, uint8_t *out, size_t sz return WHAL_SUCCESS; } +static whal_Error Stop_BlockCipher(void) +{ + size_t base = whal_Stm32n6_Cryp_Dev.base; + + Disable(base); + ZeroKeyIv(base); + return WHAL_SUCCESS; +} + /* ---- Direct API mapping ---- */ @@ -335,16 +344,19 @@ static whal_Error Process_BlockCipher(const uint8_t *in, uint8_t *out, size_t sz #define whal_Stm32n6_CrypAesEcb_Oneshot whal_AesEcb_Oneshot #define whal_Stm32n6_CrypAesEcb_Start whal_AesEcb_Start #define whal_Stm32n6_CrypAesEcb_Process whal_AesEcb_Process +#define whal_Stm32n6_CrypAesEcb_Stop whal_AesEcb_Stop #endif #ifdef WHAL_CFG_STM32N6_CRYP_CBC_DIRECT_API_MAPPING #define whal_Stm32n6_CrypAesCbc_Oneshot whal_AesCbc_Oneshot #define whal_Stm32n6_CrypAesCbc_Start whal_AesCbc_Start #define whal_Stm32n6_CrypAesCbc_Process whal_AesCbc_Process +#define whal_Stm32n6_CrypAesCbc_Stop whal_AesCbc_Stop #endif #ifdef WHAL_CFG_STM32N6_CRYP_CTR_DIRECT_API_MAPPING #define whal_Stm32n6_CrypAesCtr_Oneshot whal_AesCtr_Oneshot #define whal_Stm32n6_CrypAesCtr_Start whal_AesCtr_Start #define whal_Stm32n6_CrypAesCtr_Process whal_AesCtr_Process +#define whal_Stm32n6_CrypAesCtr_Stop whal_AesCtr_Stop #endif #ifdef WHAL_CFG_STM32N6_CRYP_GCM_DIRECT_API_MAPPING #define whal_Stm32n6_CrypAesGcm_Oneshot whal_AesGcm_Oneshot @@ -486,10 +498,17 @@ whal_Error whal_Stm32n6_CrypAesEcb_Process(whal_AesEcb *dev, return Process_BlockCipher(in, out, sz); } +whal_Error whal_Stm32n6_CrypAesEcb_Stop(whal_AesEcb *dev) +{ + (void)dev; + return Stop_BlockCipher(); +} + const whal_AesEcbDriver whal_Stm32n6_Cryp_EcbDriver = { .Oneshot = whal_Stm32n6_CrypAesEcb_Oneshot, .Start = whal_Stm32n6_CrypAesEcb_Start, .Process = whal_Stm32n6_CrypAesEcb_Process, + .Stop = whal_Stm32n6_CrypAesEcb_Stop, }; @@ -600,10 +619,17 @@ whal_Error whal_Stm32n6_CrypAesCbc_Process(whal_AesCbc *dev, return Process_BlockCipher(in, out, sz); } +whal_Error whal_Stm32n6_CrypAesCbc_Stop(whal_AesCbc *dev) +{ + (void)dev; + return Stop_BlockCipher(); +} + const whal_AesCbcDriver whal_Stm32n6_Cryp_CbcDriver = { .Oneshot = whal_Stm32n6_CrypAesCbc_Oneshot, .Start = whal_Stm32n6_CrypAesCbc_Start, .Process = whal_Stm32n6_CrypAesCbc_Process, + .Stop = whal_Stm32n6_CrypAesCbc_Stop, }; @@ -698,10 +724,17 @@ whal_Error whal_Stm32n6_CrypAesCtr_Process(whal_AesCtr *dev, return Process_BlockCipher(in, out, sz); } +whal_Error whal_Stm32n6_CrypAesCtr_Stop(whal_AesCtr *dev) +{ + (void)dev; + return Stop_BlockCipher(); +} + const whal_AesCtrDriver whal_Stm32n6_Cryp_CtrDriver = { .Oneshot = whal_Stm32n6_CrypAesCtr_Oneshot, .Start = whal_Stm32n6_CrypAesCtr_Start, .Process = whal_Stm32n6_CrypAesCtr_Process, + .Stop = whal_Stm32n6_CrypAesCtr_Stop, }; diff --git a/src/crypto/stm32wb_aes.c b/src/crypto/stm32wb_aes.c index cd71e4d..d0fba95 100644 --- a/src/crypto/stm32wb_aes.c +++ b/src/crypto/stm32wb_aes.c @@ -233,6 +233,13 @@ static void DisableAes(whal_Crypto *cryptoDev) whal_Reg_Update(cryptoDev->base, AES_CR_REG, AES_CR_EN_Msk, 0); } +static whal_Error StopBlockCipher(whal_Crypto *cryptoDev) +{ + DisableAes(cryptoDev); + ZeroKeyIv(cryptoDev->base); + return WHAL_SUCCESS; +} + /* ---- Direct API mapping ---- */ @@ -244,16 +251,19 @@ static void DisableAes(whal_Crypto *cryptoDev) #define whal_Stm32wb_AesEcb_Oneshot whal_AesEcb_Oneshot #define whal_Stm32wb_AesEcb_Start whal_AesEcb_Start #define whal_Stm32wb_AesEcb_Process whal_AesEcb_Process +#define whal_Stm32wb_AesEcb_Stop whal_AesEcb_Stop #endif #ifdef WHAL_CFG_STM32WB_AES_CBC_DIRECT_API_MAPPING #define whal_Stm32wb_AesCbc_Oneshot whal_AesCbc_Oneshot #define whal_Stm32wb_AesCbc_Start whal_AesCbc_Start #define whal_Stm32wb_AesCbc_Process whal_AesCbc_Process +#define whal_Stm32wb_AesCbc_Stop whal_AesCbc_Stop #endif #ifdef WHAL_CFG_STM32WB_AES_CTR_DIRECT_API_MAPPING #define whal_Stm32wb_AesCtr_Oneshot whal_AesCtr_Oneshot #define whal_Stm32wb_AesCtr_Start whal_AesCtr_Start #define whal_Stm32wb_AesCtr_Process whal_AesCtr_Process +#define whal_Stm32wb_AesCtr_Stop whal_AesCtr_Stop #endif #ifdef WHAL_CFG_STM32WB_AES_GCM_DIRECT_API_MAPPING #define whal_Stm32wb_AesGcm_Oneshot whal_AesGcm_Oneshot @@ -388,10 +398,17 @@ whal_Error whal_Stm32wb_AesEcb_Process(whal_AesEcb *dev, return ProcessBlockCipher(whal_Stm32wb_AesEcb_Dev.crypto, in, out, sz); } +whal_Error whal_Stm32wb_AesEcb_Stop(whal_AesEcb *dev) +{ + (void)dev; + return StopBlockCipher(whal_Stm32wb_AesEcb_Dev.crypto); +} + const whal_AesEcbDriver whal_Stm32wb_Aes_EcbDriver = { .Oneshot = whal_Stm32wb_AesEcb_Oneshot, .Start = whal_Stm32wb_AesEcb_Start, .Process = whal_Stm32wb_AesEcb_Process, + .Stop = whal_Stm32wb_AesEcb_Stop, }; @@ -493,10 +510,17 @@ whal_Error whal_Stm32wb_AesCbc_Process(whal_AesCbc *dev, return ProcessBlockCipher(whal_Stm32wb_AesCbc_Dev.crypto, in, out, sz); } +whal_Error whal_Stm32wb_AesCbc_Stop(whal_AesCbc *dev) +{ + (void)dev; + return StopBlockCipher(whal_Stm32wb_AesCbc_Dev.crypto); +} + const whal_AesCbcDriver whal_Stm32wb_Aes_CbcDriver = { .Oneshot = whal_Stm32wb_AesCbc_Oneshot, .Start = whal_Stm32wb_AesCbc_Start, .Process = whal_Stm32wb_AesCbc_Process, + .Stop = whal_Stm32wb_AesCbc_Stop, }; @@ -594,10 +618,17 @@ whal_Error whal_Stm32wb_AesCtr_Process(whal_AesCtr *dev, return ProcessBlockCipher(whal_Stm32wb_AesCtr_Dev.crypto, in, out, sz); } +whal_Error whal_Stm32wb_AesCtr_Stop(whal_AesCtr *dev) +{ + (void)dev; + return StopBlockCipher(whal_Stm32wb_AesCtr_Dev.crypto); +} + const whal_AesCtrDriver whal_Stm32wb_Aes_CtrDriver = { .Oneshot = whal_Stm32wb_AesCtr_Oneshot, .Start = whal_Stm32wb_AesCtr_Start, .Process = whal_Stm32wb_AesCtr_Process, + .Stop = whal_Stm32wb_AesCtr_Stop, }; diff --git a/src/crypto/stm32wba_aes.c b/src/crypto/stm32wba_aes.c index 16f3947..4b80f9c 100644 --- a/src/crypto/stm32wba_aes.c +++ b/src/crypto/stm32wba_aes.c @@ -254,6 +254,13 @@ static void DisableAes(whal_Crypto *cryptoDev) whal_Reg_Update(cryptoDev->base, AES_CR_REG, AES_CR_EN_Msk, 0); } +static whal_Error StopBlockCipher(whal_Crypto *cryptoDev) +{ + DisableAes(cryptoDev); + ZeroKeyIv(cryptoDev->base); + return WHAL_SUCCESS; +} + /* ---- Direct API mapping ---- */ @@ -265,16 +272,19 @@ static void DisableAes(whal_Crypto *cryptoDev) #define whal_Stm32wba_AesEcb_Oneshot whal_AesEcb_Oneshot #define whal_Stm32wba_AesEcb_Start whal_AesEcb_Start #define whal_Stm32wba_AesEcb_Process whal_AesEcb_Process +#define whal_Stm32wba_AesEcb_Stop whal_AesEcb_Stop #endif #ifdef WHAL_CFG_STM32WBA_AES_CBC_DIRECT_API_MAPPING #define whal_Stm32wba_AesCbc_Oneshot whal_AesCbc_Oneshot #define whal_Stm32wba_AesCbc_Start whal_AesCbc_Start #define whal_Stm32wba_AesCbc_Process whal_AesCbc_Process +#define whal_Stm32wba_AesCbc_Stop whal_AesCbc_Stop #endif #ifdef WHAL_CFG_STM32WBA_AES_CTR_DIRECT_API_MAPPING #define whal_Stm32wba_AesCtr_Oneshot whal_AesCtr_Oneshot #define whal_Stm32wba_AesCtr_Start whal_AesCtr_Start #define whal_Stm32wba_AesCtr_Process whal_AesCtr_Process +#define whal_Stm32wba_AesCtr_Stop whal_AesCtr_Stop #endif #ifdef WHAL_CFG_STM32WBA_AES_GCM_DIRECT_API_MAPPING #define whal_Stm32wba_AesGcm_Oneshot whal_AesGcm_Oneshot @@ -444,10 +454,17 @@ whal_Error whal_Stm32wba_AesEcb_Process(whal_AesEcb *dev, return ProcessBlockCipher(whal_Stm32wba_AesEcb_Dev.crypto, in, out, sz); } +whal_Error whal_Stm32wba_AesEcb_Stop(whal_AesEcb *dev) +{ + (void)dev; + return StopBlockCipher(whal_Stm32wba_AesEcb_Dev.crypto); +} + const whal_AesEcbDriver whal_Stm32wba_Aes_EcbDriver = { .Oneshot = whal_Stm32wba_AesEcb_Oneshot, .Start = whal_Stm32wba_AesEcb_Start, .Process = whal_Stm32wba_AesEcb_Process, + .Stop = whal_Stm32wba_AesEcb_Stop, }; @@ -584,10 +601,17 @@ whal_Error whal_Stm32wba_AesCbc_Process(whal_AesCbc *dev, return ProcessBlockCipher(whal_Stm32wba_AesCbc_Dev.crypto, in, out, sz); } +whal_Error whal_Stm32wba_AesCbc_Stop(whal_AesCbc *dev) +{ + (void)dev; + return StopBlockCipher(whal_Stm32wba_AesCbc_Dev.crypto); +} + const whal_AesCbcDriver whal_Stm32wba_Aes_CbcDriver = { .Oneshot = whal_Stm32wba_AesCbc_Oneshot, .Start = whal_Stm32wba_AesCbc_Start, .Process = whal_Stm32wba_AesCbc_Process, + .Stop = whal_Stm32wba_AesCbc_Stop, }; @@ -700,10 +724,17 @@ whal_Error whal_Stm32wba_AesCtr_Process(whal_AesCtr *dev, return ProcessBlockCipher(whal_Stm32wba_AesCtr_Dev.crypto, in, out, sz); } +whal_Error whal_Stm32wba_AesCtr_Stop(whal_AesCtr *dev) +{ + (void)dev; + return StopBlockCipher(whal_Stm32wba_AesCtr_Dev.crypto); +} + const whal_AesCtrDriver whal_Stm32wba_Aes_CtrDriver = { .Oneshot = whal_Stm32wba_AesCtr_Oneshot, .Start = whal_Stm32wba_AesCtr_Start, .Process = whal_Stm32wba_AesCtr_Process, + .Stop = whal_Stm32wba_AesCtr_Stop, }; diff --git a/tests/crypto/test_aes_cbc.c b/tests/crypto/test_aes_cbc.c index cee4290..89f952d 100644 --- a/tests/crypto/test_aes_cbc.c +++ b/tests/crypto/test_aes_cbc.c @@ -127,11 +127,46 @@ static void Test_AesCbc_KnownAnswer128(void) WHAL_ASSERT_MEM_EQ(pt, nistPt, sizeof(nistPt)); } +static void Test_AesCbc_Streaming(void) +{ + uint8_t expected[32] = {0}; + uint8_t ct[32] = {0}; + uint8_t pt[32] = {0}; + + WHAL_ASSERT_EQ(whal_AesCbc_Oneshot(BOARD_AES_CBC_DEV, WHAL_CRYPTO_ENCRYPT, + key, 32, iv, plaintext, expected, + sizeof(plaintext)), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_AesCbc_Start(BOARD_AES_CBC_DEV, WHAL_CRYPTO_ENCRYPT, + key, 32, iv), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCbc_Process(BOARD_AES_CBC_DEV, plaintext, ct, 16), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCbc_Process(BOARD_AES_CBC_DEV, + plaintext + 16, ct + 16, 16), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCbc_Stop(BOARD_AES_CBC_DEV), WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(ct, expected, sizeof(expected)); + + WHAL_ASSERT_EQ(whal_AesCbc_Start(BOARD_AES_CBC_DEV, WHAL_CRYPTO_DECRYPT, + key, 32, iv), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCbc_Process(BOARD_AES_CBC_DEV, ct, pt, 16), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCbc_Process(BOARD_AES_CBC_DEV, + ct + 16, pt + 16, 16), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCbc_Stop(BOARD_AES_CBC_DEV), WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(pt, plaintext, sizeof(plaintext)); +} + void whal_Test_AesCbc(void) { WHAL_TEST_SUITE_START("aes_cbc"); WHAL_TEST(Test_AesCbc_Basic); WHAL_TEST(Test_AesCbc_KnownAnswer); WHAL_TEST(Test_AesCbc_KnownAnswer128); + WHAL_TEST(Test_AesCbc_Streaming); WHAL_TEST_SUITE_END(); } diff --git a/tests/crypto/test_aes_ctr.c b/tests/crypto/test_aes_ctr.c index c889bc3..66cb14c 100644 --- a/tests/crypto/test_aes_ctr.c +++ b/tests/crypto/test_aes_ctr.c @@ -96,10 +96,45 @@ static void Test_AesCtr_KnownAnswer(void) WHAL_ASSERT_MEM_EQ(ct, nistCtrCt, sizeof(nistCtrCt)); } +static void Test_AesCtr_Streaming(void) +{ + uint8_t expected[32] = {0}; + uint8_t ct[32] = {0}; + uint8_t pt[32] = {0}; + + WHAL_ASSERT_EQ(whal_AesCtr_Oneshot(BOARD_AES_CTR_DEV, WHAL_CRYPTO_ENCRYPT, + key, 32, iv, plaintext, expected, + sizeof(plaintext)), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_AesCtr_Start(BOARD_AES_CTR_DEV, WHAL_CRYPTO_ENCRYPT, + key, 32, iv), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCtr_Process(BOARD_AES_CTR_DEV, plaintext, ct, 16), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCtr_Process(BOARD_AES_CTR_DEV, + plaintext + 16, ct + 16, 16), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCtr_Stop(BOARD_AES_CTR_DEV), WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(ct, expected, sizeof(expected)); + + WHAL_ASSERT_EQ(whal_AesCtr_Start(BOARD_AES_CTR_DEV, WHAL_CRYPTO_DECRYPT, + key, 32, iv), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCtr_Process(BOARD_AES_CTR_DEV, ct, pt, 16), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCtr_Process(BOARD_AES_CTR_DEV, + ct + 16, pt + 16, 16), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCtr_Stop(BOARD_AES_CTR_DEV), WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(pt, plaintext, sizeof(plaintext)); +} + void whal_Test_AesCtr(void) { WHAL_TEST_SUITE_START("aes_ctr"); WHAL_TEST(Test_AesCtr_Basic); WHAL_TEST(Test_AesCtr_KnownAnswer); + WHAL_TEST(Test_AesCtr_Streaming); WHAL_TEST_SUITE_END(); } diff --git a/tests/crypto/test_aes_ecb.c b/tests/crypto/test_aes_ecb.c index 6719ae1..939b84d 100644 --- a/tests/crypto/test_aes_ecb.c +++ b/tests/crypto/test_aes_ecb.c @@ -116,11 +116,46 @@ static void Test_AesEcb_KnownAnswer128(void) WHAL_ASSERT_MEM_EQ(pt, nistPt, sizeof(nistPt)); } +static void Test_AesEcb_Streaming(void) +{ + uint8_t expected[32] = {0}; + uint8_t ct[32] = {0}; + uint8_t pt[32] = {0}; + + WHAL_ASSERT_EQ(whal_AesEcb_Oneshot(BOARD_AES_ECB_DEV, WHAL_CRYPTO_ENCRYPT, + key, 32, plaintext, expected, + sizeof(plaintext)), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_AesEcb_Start(BOARD_AES_ECB_DEV, WHAL_CRYPTO_ENCRYPT, + key, 32), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesEcb_Process(BOARD_AES_ECB_DEV, plaintext, ct, 16), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesEcb_Process(BOARD_AES_ECB_DEV, + plaintext + 16, ct + 16, 16), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesEcb_Stop(BOARD_AES_ECB_DEV), WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(ct, expected, sizeof(expected)); + + WHAL_ASSERT_EQ(whal_AesEcb_Start(BOARD_AES_ECB_DEV, WHAL_CRYPTO_DECRYPT, + key, 32), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesEcb_Process(BOARD_AES_ECB_DEV, ct, pt, 16), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesEcb_Process(BOARD_AES_ECB_DEV, + ct + 16, pt + 16, 16), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesEcb_Stop(BOARD_AES_ECB_DEV), WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(pt, plaintext, sizeof(plaintext)); +} + void whal_Test_AesEcb(void) { WHAL_TEST_SUITE_START("aes_ecb"); WHAL_TEST(Test_AesEcb_Basic); WHAL_TEST(Test_AesEcb_KnownAnswer); WHAL_TEST(Test_AesEcb_KnownAnswer128); + WHAL_TEST(Test_AesEcb_Streaming); WHAL_TEST_SUITE_END(); } diff --git a/wolfHAL/crypto/crypto.h b/wolfHAL/crypto/crypto.h index 60f5863..a683130 100644 --- a/wolfHAL/crypto/crypto.h +++ b/wolfHAL/crypto/crypto.h @@ -98,6 +98,7 @@ typedef struct { const void *key, size_t keySz); whal_Error (*Process)(whal_AesEcb *dev, const void *in, void *out, size_t sz); + whal_Error (*Stop)(whal_AesEcb *dev); } whal_AesEcbDriver; /** @@ -142,6 +143,12 @@ whal_Error whal_AesEcb_Start(whal_AesEcb *dev, whal_Crypto_Dir dir, */ whal_Error whal_AesEcb_Process(whal_AesEcb *dev, const void *in, void *out, size_t sz); +/** + * @brief End an AES-ECB streaming session and clear the key. + * + * @param dev AES-ECB device instance. + */ +whal_Error whal_AesEcb_Stop(whal_AesEcb *dev); /* AES-CBC */ @@ -160,6 +167,7 @@ typedef struct { const void *iv); whal_Error (*Process)(whal_AesCbc *dev, const void *in, void *out, size_t sz); + whal_Error (*Stop)(whal_AesCbc *dev); } whal_AesCbcDriver; /** @@ -208,6 +216,12 @@ whal_Error whal_AesCbc_Start(whal_AesCbc *dev, whal_Crypto_Dir dir, */ whal_Error whal_AesCbc_Process(whal_AesCbc *dev, const void *in, void *out, size_t sz); +/** + * @brief End an AES-CBC streaming session and clear the key and IV. + * + * @param dev AES-CBC device instance. + */ +whal_Error whal_AesCbc_Stop(whal_AesCbc *dev); /* AES-CTR */ @@ -226,6 +240,7 @@ typedef struct { const void *iv); whal_Error (*Process)(whal_AesCtr *dev, const void *in, void *out, size_t sz); + whal_Error (*Stop)(whal_AesCtr *dev); } whal_AesCtrDriver; /** @@ -274,6 +289,12 @@ whal_Error whal_AesCtr_Start(whal_AesCtr *dev, whal_Crypto_Dir dir, */ whal_Error whal_AesCtr_Process(whal_AesCtr *dev, const void *in, void *out, size_t sz); +/** + * @brief End an AES-CTR streaming session and clear the key and counter. + * + * @param dev AES-CTR device instance. + */ +whal_Error whal_AesCtr_Stop(whal_AesCtr *dev); /* AES-GCM */ diff --git a/wolfHAL/crypto/stm32n6_cryp.h b/wolfHAL/crypto/stm32n6_cryp.h index 6a94624..e9d2da7 100644 --- a/wolfHAL/crypto/stm32n6_cryp.h +++ b/wolfHAL/crypto/stm32n6_cryp.h @@ -79,18 +79,21 @@ typedef struct { #define whal_Stm32n6_CrypAesEcb_Oneshot whal_AesEcb_Oneshot #define whal_Stm32n6_CrypAesEcb_Start whal_AesEcb_Start #define whal_Stm32n6_CrypAesEcb_Process whal_AesEcb_Process +#define whal_Stm32n6_CrypAesEcb_Stop whal_AesEcb_Stop #endif #ifdef WHAL_CFG_STM32N6_CRYP_CBC_DIRECT_API_MAPPING #define whal_Stm32n6_CrypAesCbc_Oneshot whal_AesCbc_Oneshot #define whal_Stm32n6_CrypAesCbc_Start whal_AesCbc_Start #define whal_Stm32n6_CrypAesCbc_Process whal_AesCbc_Process +#define whal_Stm32n6_CrypAesCbc_Stop whal_AesCbc_Stop #endif #ifdef WHAL_CFG_STM32N6_CRYP_CTR_DIRECT_API_MAPPING #define whal_Stm32n6_CrypAesCtr_Oneshot whal_AesCtr_Oneshot #define whal_Stm32n6_CrypAesCtr_Start whal_AesCtr_Start #define whal_Stm32n6_CrypAesCtr_Process whal_AesCtr_Process +#define whal_Stm32n6_CrypAesCtr_Stop whal_AesCtr_Stop #endif #ifdef WHAL_CFG_STM32N6_CRYP_GCM_DIRECT_API_MAPPING @@ -170,6 +173,13 @@ whal_Error whal_Stm32n6_CrypAesEcb_Process(whal_AesEcb *dev, const void *in, void *out, size_t sz); +/** + * @brief End an AES-ECB streaming session and clear the key. + * + * @param dev AES-ECB device instance. + */ +whal_Error whal_Stm32n6_CrypAesEcb_Stop(whal_AesEcb *dev); + /* ---- AES-CBC ---- */ /** @@ -217,6 +227,13 @@ whal_Error whal_Stm32n6_CrypAesCbc_Process(whal_AesCbc *dev, const void *in, void *out, size_t sz); +/** + * @brief End an AES-CBC streaming session and clear the key and IV. + * + * @param dev AES-CBC device instance. + */ +whal_Error whal_Stm32n6_CrypAesCbc_Stop(whal_AesCbc *dev); + /* ---- AES-CTR ---- */ /** @@ -264,6 +281,13 @@ whal_Error whal_Stm32n6_CrypAesCtr_Process(whal_AesCtr *dev, const void *in, void *out, size_t sz); +/** + * @brief End an AES-CTR streaming session and clear the key and counter. + * + * @param dev AES-CTR device instance. + */ +whal_Error whal_Stm32n6_CrypAesCtr_Stop(whal_AesCtr *dev); + /* ---- AES-GCM ---- */ /** diff --git a/wolfHAL/crypto/stm32wb_aes.h b/wolfHAL/crypto/stm32wb_aes.h index 06d14f3..306d661 100644 --- a/wolfHAL/crypto/stm32wb_aes.h +++ b/wolfHAL/crypto/stm32wb_aes.h @@ -75,18 +75,21 @@ typedef struct { #define whal_Stm32wb_AesEcb_Oneshot whal_AesEcb_Oneshot #define whal_Stm32wb_AesEcb_Start whal_AesEcb_Start #define whal_Stm32wb_AesEcb_Process whal_AesEcb_Process +#define whal_Stm32wb_AesEcb_Stop whal_AesEcb_Stop #endif #ifdef WHAL_CFG_STM32WB_AES_CBC_DIRECT_API_MAPPING #define whal_Stm32wb_AesCbc_Oneshot whal_AesCbc_Oneshot #define whal_Stm32wb_AesCbc_Start whal_AesCbc_Start #define whal_Stm32wb_AesCbc_Process whal_AesCbc_Process +#define whal_Stm32wb_AesCbc_Stop whal_AesCbc_Stop #endif #ifdef WHAL_CFG_STM32WB_AES_CTR_DIRECT_API_MAPPING #define whal_Stm32wb_AesCtr_Oneshot whal_AesCtr_Oneshot #define whal_Stm32wb_AesCtr_Start whal_AesCtr_Start #define whal_Stm32wb_AesCtr_Process whal_AesCtr_Process +#define whal_Stm32wb_AesCtr_Stop whal_AesCtr_Stop #endif #ifdef WHAL_CFG_STM32WB_AES_GCM_DIRECT_API_MAPPING @@ -162,6 +165,13 @@ whal_Error whal_Stm32wb_AesEcb_Start(whal_AesEcb *dev, whal_Crypto_Dir dir, whal_Error whal_Stm32wb_AesEcb_Process(whal_AesEcb *dev, const void *in, void *out, size_t sz); +/** + * @brief End an AES-ECB streaming session and clear the key. + * + * @param dev AES-ECB device instance. + */ +whal_Error whal_Stm32wb_AesEcb_Stop(whal_AesEcb *dev); + /* ---- AES-CBC ---- */ /** @@ -205,6 +215,13 @@ whal_Error whal_Stm32wb_AesCbc_Start(whal_AesCbc *dev, whal_Crypto_Dir dir, whal_Error whal_Stm32wb_AesCbc_Process(whal_AesCbc *dev, const void *in, void *out, size_t sz); +/** + * @brief End an AES-CBC streaming session and clear the key and IV. + * + * @param dev AES-CBC device instance. + */ +whal_Error whal_Stm32wb_AesCbc_Stop(whal_AesCbc *dev); + /* ---- AES-CTR ---- */ /** @@ -248,6 +265,13 @@ whal_Error whal_Stm32wb_AesCtr_Start(whal_AesCtr *dev, whal_Crypto_Dir dir, whal_Error whal_Stm32wb_AesCtr_Process(whal_AesCtr *dev, const void *in, void *out, size_t sz); +/** + * @brief End an AES-CTR streaming session and clear the key and counter. + * + * @param dev AES-CTR device instance. + */ +whal_Error whal_Stm32wb_AesCtr_Stop(whal_AesCtr *dev); + /* ---- AES-GCM ---- */ /** diff --git a/wolfHAL/crypto/stm32wba_aes.h b/wolfHAL/crypto/stm32wba_aes.h index ae5b41b..f3fc0ef 100644 --- a/wolfHAL/crypto/stm32wba_aes.h +++ b/wolfHAL/crypto/stm32wba_aes.h @@ -75,18 +75,21 @@ typedef struct { #define whal_Stm32wba_AesEcb_Oneshot whal_AesEcb_Oneshot #define whal_Stm32wba_AesEcb_Start whal_AesEcb_Start #define whal_Stm32wba_AesEcb_Process whal_AesEcb_Process +#define whal_Stm32wba_AesEcb_Stop whal_AesEcb_Stop #endif #ifdef WHAL_CFG_STM32WBA_AES_CBC_DIRECT_API_MAPPING #define whal_Stm32wba_AesCbc_Oneshot whal_AesCbc_Oneshot #define whal_Stm32wba_AesCbc_Start whal_AesCbc_Start #define whal_Stm32wba_AesCbc_Process whal_AesCbc_Process +#define whal_Stm32wba_AesCbc_Stop whal_AesCbc_Stop #endif #ifdef WHAL_CFG_STM32WBA_AES_CTR_DIRECT_API_MAPPING #define whal_Stm32wba_AesCtr_Oneshot whal_AesCtr_Oneshot #define whal_Stm32wba_AesCtr_Start whal_AesCtr_Start #define whal_Stm32wba_AesCtr_Process whal_AesCtr_Process +#define whal_Stm32wba_AesCtr_Stop whal_AesCtr_Stop #endif #ifdef WHAL_CFG_STM32WBA_AES_GCM_DIRECT_API_MAPPING @@ -162,6 +165,13 @@ whal_Error whal_Stm32wba_AesEcb_Start(whal_AesEcb *dev, whal_Crypto_Dir dir, whal_Error whal_Stm32wba_AesEcb_Process(whal_AesEcb *dev, const void *in, void *out, size_t sz); +/** + * @brief End an AES-ECB streaming session and clear the key. + * + * @param dev AES-ECB device instance. + */ +whal_Error whal_Stm32wba_AesEcb_Stop(whal_AesEcb *dev); + /* ---- AES-CBC ---- */ /** @@ -205,6 +215,13 @@ whal_Error whal_Stm32wba_AesCbc_Start(whal_AesCbc *dev, whal_Crypto_Dir dir, whal_Error whal_Stm32wba_AesCbc_Process(whal_AesCbc *dev, const void *in, void *out, size_t sz); +/** + * @brief End an AES-CBC streaming session and clear the key and IV. + * + * @param dev AES-CBC device instance. + */ +whal_Error whal_Stm32wba_AesCbc_Stop(whal_AesCbc *dev); + /* ---- AES-CTR ---- */ /** @@ -248,6 +265,13 @@ whal_Error whal_Stm32wba_AesCtr_Start(whal_AesCtr *dev, whal_Crypto_Dir dir, whal_Error whal_Stm32wba_AesCtr_Process(whal_AesCtr *dev, const void *in, void *out, size_t sz); +/** + * @brief End an AES-CTR streaming session and clear the key and counter. + * + * @param dev AES-CTR device instance. + */ +whal_Error whal_Stm32wba_AesCtr_Stop(whal_AesCtr *dev); + /* ---- AES-GCM ---- */ /** From f4195148bf43ac9564b56c86e43a67545c601dd7 Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Tue, 13 Oct 2026 10:44:08 -0400 Subject: [PATCH 03/22] [crypto, stm32wb, stm32wba] Clear stale key words on Start and drain DOUTR on timeout - Clear all WB key and IV registers at the start of ECB/CBC/CTR Start so a shorter key does not inherit old key words - Drain DOUTR when WaitForCCF times out and in Deinit on WB and WBA/U5 --- src/crypto/stm32wb_aes.c | 17 ++++++++++++++++- src/crypto/stm32wba_aes.c | 14 +++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/crypto/stm32wb_aes.c b/src/crypto/stm32wb_aes.c index d0fba95..3102311 100644 --- a/src/crypto/stm32wb_aes.c +++ b/src/crypto/stm32wb_aes.c @@ -184,13 +184,24 @@ static void ReadBlock(size_t base, uint8_t *out) whal_StoreBe32(out + 12, whal_Reg_Read(base, AES_DOUTR_REG)); } +/* Discard any output block left in DOUTR. */ +static void DrainOutput(size_t base) +{ + (void)whal_Reg_Read(base, AES_DOUTR_REG); + (void)whal_Reg_Read(base, AES_DOUTR_REG); + (void)whal_Reg_Read(base, AES_DOUTR_REG); + (void)whal_Reg_Read(base, AES_DOUTR_REG); +} + static whal_Error WaitForCCF(size_t base, whal_Timeout *timeout) { whal_Error err; err = whal_Reg_ReadPoll(base, AES_SR_REG, AES_SR_CCF_Msk, AES_SR_CCF_Msk, timeout); - if (err) + if (err) { + DrainOutput(base); return err; + } whal_Reg_Update(base, AES_CR_REG, AES_CR_CCFC_Msk, AES_CR_CCFC_Msk); return WHAL_SUCCESS; } @@ -293,6 +304,7 @@ whal_Error whal_Stm32wb_Aes_Init(whal_Crypto *dev) whal_Error whal_Stm32wb_Aes_Deinit(whal_Crypto *dev) { (void)dev; + DrainOutput(whal_Stm32wb_Aes_Dev.base); DisableAes((whal_Crypto *)&whal_Stm32wb_Aes_Dev); ZeroKeyIv(whal_Stm32wb_Aes_Dev.base); return WHAL_SUCCESS; @@ -372,6 +384,7 @@ whal_Error whal_Stm32wb_AesEcb_Start(whal_AesEcb *dev, whal_Crypto_Dir dir, ? AES_MODE_ENCRYPT : AES_MODE_KEYDERIV_DECRYPT; whal_Reg_Update(base, AES_CR_REG, AES_CR_EN_Msk, 0); + ZeroKeyIv(base); whal_Reg_Update(base, AES_CR_REG, AES_CR_MODE_Msk | AES_CR_CHMOD_Msk | AES_CR_CHMOD2_Msk | @@ -483,6 +496,7 @@ whal_Error whal_Stm32wb_AesCbc_Start(whal_AesCbc *dev, whal_Crypto_Dir dir, ? AES_MODE_ENCRYPT : AES_MODE_KEYDERIV_DECRYPT; whal_Reg_Update(base, AES_CR_REG, AES_CR_EN_Msk, 0); + ZeroKeyIv(base); whal_Reg_Update(base, AES_CR_REG, AES_CR_MODE_Msk | AES_CR_CHMOD_Msk | AES_CR_CHMOD2_Msk | @@ -590,6 +604,7 @@ whal_Error whal_Stm32wb_AesCtr_Start(whal_AesCtr *dev, whal_Crypto_Dir dir, keySizeBit = (keySz == 32) ? 1 : 0; whal_Reg_Update(base, AES_CR_REG, AES_CR_EN_Msk, 0); + ZeroKeyIv(base); whal_Reg_Update(base, AES_CR_REG, AES_CR_MODE_Msk | AES_CR_CHMOD_Msk | AES_CR_CHMOD2_Msk | diff --git a/src/crypto/stm32wba_aes.c b/src/crypto/stm32wba_aes.c index 4b80f9c..486f782 100644 --- a/src/crypto/stm32wba_aes.c +++ b/src/crypto/stm32wba_aes.c @@ -195,13 +195,24 @@ static void ReadBlock(size_t base, uint8_t *out) whal_StoreBe32(out + 12, whal_Reg_Read(base, AES_DOUTR_REG)); } +/* Discard any output block left in DOUTR. */ +static void DrainOutput(size_t base) +{ + (void)whal_Reg_Read(base, AES_DOUTR_REG); + (void)whal_Reg_Read(base, AES_DOUTR_REG); + (void)whal_Reg_Read(base, AES_DOUTR_REG); + (void)whal_Reg_Read(base, AES_DOUTR_REG); +} + static whal_Error WaitForCCF(size_t base, whal_Timeout *timeout) { whal_Error err; err = whal_Reg_ReadPoll(base, AES_ISR_REG, AES_ISR_CCF_Msk, AES_ISR_CCF_Msk, timeout); - if (err) + if (err) { + DrainOutput(base); return err; + } whal_Reg_Write(base, AES_ICR_REG, AES_ICR_CCF_Msk); return WHAL_SUCCESS; } @@ -314,6 +325,7 @@ whal_Error whal_Stm32wba_Aes_Init(whal_Crypto *dev) whal_Error whal_Stm32wba_Aes_Deinit(whal_Crypto *dev) { (void)dev; + DrainOutput(whal_Stm32wba_Aes_Dev.base); DisableAes((whal_Crypto *)&whal_Stm32wba_Aes_Dev); ZeroKeyIv(whal_Stm32wba_Aes_Dev.base); return WHAL_SUCCESS; From 0967eb3ce6d787c57f69bbe28077411ffe46df28 Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Tue, 13 Oct 2026 12:09:25 -0400 Subject: [PATCH 04/22] [crypto, stm32n6] Reset CRYP with IPRST on every teardown path - Replace Disable plus ZeroKeyIv with an IPRST reset that also clears the FIFOs - Use it on all error, cleanup and success paths, in Stop and in Deinit --- src/crypto/stm32n6_cryp.c | 102 ++++++++++++-------------------------- 1 file changed, 31 insertions(+), 71 deletions(-) diff --git a/src/crypto/stm32n6_cryp.c b/src/crypto/stm32n6_cryp.c index f5794b0..9156d62 100644 --- a/src/crypto/stm32n6_cryp.c +++ b/src/crypto/stm32n6_cryp.c @@ -195,24 +195,11 @@ static void WriteIv16(size_t base, const uint8_t *iv) whal_Reg_Write(base, CRYP_IV1RR_REG, whal_LoadBe32(iv + 12)); } -static void ZeroKeyIv(size_t base) +/* Reset CRYP to clear its FIFOs, key, IV and configuration. */ +static void ResetCryp(size_t base) { - /* KEYSIZE must cover K0LR..K3RR or the write sequence raises KERF */ - whal_Reg_Update(base, CRYP_CR_REG, CRYP_CR_KEYSIZE_Msk, - whal_SetBits(CRYP_CR_KEYSIZE_Msk, CRYP_CR_KEYSIZE_Pos, - CRYP_KEYSIZE_256)); - whal_Reg_Write(base, CRYP_K0LR_REG, 0); - whal_Reg_Write(base, CRYP_K0RR_REG, 0); - whal_Reg_Write(base, CRYP_K1LR_REG, 0); - whal_Reg_Write(base, CRYP_K1RR_REG, 0); - whal_Reg_Write(base, CRYP_K2LR_REG, 0); - whal_Reg_Write(base, CRYP_K2RR_REG, 0); - whal_Reg_Write(base, CRYP_K3LR_REG, 0); - whal_Reg_Write(base, CRYP_K3RR_REG, 0); - whal_Reg_Write(base, CRYP_IV0LR_REG, 0); - whal_Reg_Write(base, CRYP_IV0RR_REG, 0); - whal_Reg_Write(base, CRYP_IV1LR_REG, 0); - whal_Reg_Write(base, CRYP_IV1RR_REG, 0); + whal_Reg_Update(base, CRYP_CR_REG, CRYP_CR_IPRST_Msk, CRYP_CR_IPRST_Msk); + whal_Reg_Update(base, CRYP_CR_REG, CRYP_CR_IPRST_Msk, 0); } static void WriteBlock(size_t base, const uint8_t *in) @@ -305,8 +292,7 @@ static whal_Error Process_BlockCipher(const uint8_t *in, uint8_t *out, size_t sz return WHAL_SUCCESS; if (!in || !out || (sz & 0xF) != 0) { - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return WHAL_EINVAL; } @@ -314,8 +300,7 @@ static whal_Error Process_BlockCipher(const uint8_t *in, uint8_t *out, size_t sz WriteBlock(base, in + i); err = WaitOutputReady(base, cfg->timeout); if (err) { - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return err; } ReadBlock(base, out + i); @@ -328,8 +313,7 @@ static whal_Error Stop_BlockCipher(void) { size_t base = whal_Stm32n6_Cryp_Dev.base; - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return WHAL_SUCCESS; } @@ -386,8 +370,7 @@ whal_Error whal_Stm32n6_Cryp_Init(whal_Crypto *dev) whal_Error whal_Stm32n6_Cryp_Deinit(whal_Crypto *dev) { (void)dev; - Disable(whal_Stm32n6_Cryp_Dev.base); - ZeroKeyIv(whal_Stm32n6_Cryp_Dev.base); + ResetCryp(whal_Stm32n6_Cryp_Dev.base); return WHAL_SUCCESS; } @@ -440,8 +423,7 @@ whal_Error whal_Stm32n6_CrypAesEcb_Oneshot(whal_AesEcb *dev, err = Process_BlockCipher(in, out, sz); cleanup: - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return err; } @@ -485,8 +467,7 @@ whal_Error whal_Stm32n6_CrypAesEcb_Start(whal_AesEcb *dev, return WHAL_SUCCESS; cleanup: - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return err; } @@ -558,8 +539,7 @@ whal_Error whal_Stm32n6_CrypAesCbc_Oneshot(whal_AesCbc *dev, err = Process_BlockCipher(in, out, sz); cleanup: - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return err; } @@ -606,8 +586,7 @@ whal_Error whal_Stm32n6_CrypAesCbc_Start(whal_AesCbc *dev, return WHAL_SUCCESS; cleanup: - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return err; } @@ -671,8 +650,7 @@ whal_Error whal_Stm32n6_CrypAesCtr_Oneshot(whal_AesCtr *dev, err = Process_BlockCipher(in, out, sz); cleanup: - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return err; } @@ -711,8 +689,7 @@ whal_Error whal_Stm32n6_CrypAesCtr_Start(whal_AesCtr *dev, return WHAL_SUCCESS; cleanup: - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return err; } @@ -936,8 +913,7 @@ whal_Error whal_Stm32n6_CrypAesGcm_Oneshot(whal_AesGcm *dev, ((uint8_t *)tag)[i] = tagBuf[i]; cleanup: - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return err; } @@ -993,8 +969,7 @@ whal_Error whal_Stm32n6_CrypAesGcm_Start(whal_AesGcm *dev, return WHAL_SUCCESS; cleanup: - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return err; } @@ -1014,15 +989,13 @@ whal_Error whal_Stm32n6_CrypAesGcm_Process(whal_AesGcm *dev, return WHAL_SUCCESS; if (!in || !out) { - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return WHAL_EINVAL; } /* Only the last Process call may end in a partial block */ if (g_aesGcmState.partial) { - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return WHAL_EINVAL; } @@ -1052,8 +1025,7 @@ whal_Error whal_Stm32n6_CrypAesGcm_Process(whal_AesGcm *dev, err = WaitOutputReady(base, cfg->timeout); if (err) { - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return err; } @@ -1087,8 +1059,7 @@ whal_Error whal_Stm32n6_CrypAesGcm_Finalize(whal_AesGcm *dev, (void)dev; if (!tag || tagSz == 0 || tagSz > 16) { - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return WHAL_EINVAL; } @@ -1110,8 +1081,7 @@ whal_Error whal_Stm32n6_CrypAesGcm_Finalize(whal_AesGcm *dev, err = WaitOutputReady(base, cfg->timeout); if (err) { - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return err; } @@ -1119,8 +1089,7 @@ whal_Error whal_Stm32n6_CrypAesGcm_Finalize(whal_AesGcm *dev, for (i = 0; i < tagSz; i++) ((uint8_t *)tag)[i] = tagBuf[i]; - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return WHAL_SUCCESS; } @@ -1199,8 +1168,7 @@ whal_Error whal_Stm32n6_CrypAesGmac_Oneshot(whal_AesGmac *dev, ((uint8_t *)tag)[i] = tagBuf[i]; cleanup: - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return err; } @@ -1427,8 +1395,7 @@ whal_Error whal_Stm32n6_CrypAesCcm_Oneshot(whal_AesCcm *dev, ((uint8_t *)tag)[i] = tagBuf[i]; cleanup: - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return err; } @@ -1565,8 +1532,7 @@ whal_Error whal_Stm32n6_CrypAesCcm_Start(whal_AesCcm *dev, return WHAL_SUCCESS; cleanup: - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return err; } @@ -1586,8 +1552,7 @@ whal_Error whal_Stm32n6_CrypAesCcm_Process(whal_AesCcm *dev, return WHAL_SUCCESS; if (!in || !out) { - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return WHAL_EINVAL; } @@ -1595,8 +1560,7 @@ whal_Error whal_Stm32n6_CrypAesCcm_Process(whal_AesCcm *dev, * total may not exceed the payload length declared at Start */ if (g_aesCcmState.partial || sz > g_aesCcmState.msgSz - g_aesCcmState.dataSz) { - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return WHAL_EINVAL; } @@ -1627,8 +1591,7 @@ whal_Error whal_Stm32n6_CrypAesCcm_Process(whal_AesCcm *dev, err = WaitOutputReady(base, cfg->timeout); if (err) { - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return err; } @@ -1663,8 +1626,7 @@ whal_Error whal_Stm32n6_CrypAesCcm_Finalize(whal_AesCcm *dev, if (!tag || tagSz < 4 || tagSz > 16 || (tagSz & 1) != 0 || tagSz != g_aesCcmState.tagSz || g_aesCcmState.dataSz != g_aesCcmState.msgSz) { - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return WHAL_EINVAL; } @@ -1681,8 +1643,7 @@ whal_Error whal_Stm32n6_CrypAesCcm_Finalize(whal_AesCcm *dev, err = WaitOutputReady(base, cfg->timeout); if (err) { - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return err; } @@ -1690,8 +1651,7 @@ whal_Error whal_Stm32n6_CrypAesCcm_Finalize(whal_AesCcm *dev, for (i = 0; i < tagSz; i++) ((uint8_t *)tag)[i] = tagBuf[i]; - Disable(base); - ZeroKeyIv(base); + ResetCryp(base); return WHAL_SUCCESS; } From b1da06cf52471e8dc593eeb581ca937e1f5682f8 Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Tue, 13 Oct 2026 12:35:03 -0400 Subject: [PATCH 05/22] [crypto, test] Test the generic crypto API layer on the host - Build crypto.c and the per-algorithm AES, SHA and HMAC dispatch sources in tests/core - Add NULL device, NULL driver and valid dispatch tests for every crypto API - Check that a missing Stop, Finalize or GMAC Oneshot slot returns WHAL_ENOTSUP --- tests/core/Makefile | 15 +- tests/core/test_dispatch.c | 589 +++++++++++++++++++++++++++++++++++++ 2 files changed, 603 insertions(+), 1 deletion(-) diff --git a/tests/core/Makefile b/tests/core/Makefile index dd23488..f1d6b53 100644 --- a/tests/core/Makefile +++ b/tests/core/Makefile @@ -34,7 +34,20 @@ WHAL_SRC = $(WHAL_DIR)/src/gpio/gpio.c \ $(WHAL_DIR)/src/rng/rng.c \ $(WHAL_DIR)/src/block/block.c \ $(WHAL_DIR)/src/pwm/pwm.c \ - $(WHAL_DIR)/src/display/display.c + $(WHAL_DIR)/src/display/display.c \ + $(WHAL_DIR)/src/crypto/crypto.c \ + $(WHAL_DIR)/src/crypto/aes_ecb.c \ + $(WHAL_DIR)/src/crypto/aes_cbc.c \ + $(WHAL_DIR)/src/crypto/aes_ctr.c \ + $(WHAL_DIR)/src/crypto/aes_gcm.c \ + $(WHAL_DIR)/src/crypto/aes_gmac.c \ + $(WHAL_DIR)/src/crypto/aes_ccm.c \ + $(WHAL_DIR)/src/crypto/sha1.c \ + $(WHAL_DIR)/src/crypto/sha224.c \ + $(WHAL_DIR)/src/crypto/sha256.c \ + $(WHAL_DIR)/src/crypto/hmac_sha1.c \ + $(WHAL_DIR)/src/crypto/hmac_sha224.c \ + $(WHAL_DIR)/src/crypto/hmac_sha256.c SOURCE = $(TEST_SRC) $(WHAL_SRC) OBJECTS = $(patsubst %.c,%.o,$(SOURCE)) diff --git a/tests/core/test_dispatch.c b/tests/core/test_dispatch.c index 957edce..a189d9b 100644 --- a/tests/core/test_dispatch.c +++ b/tests/core/test_dispatch.c @@ -440,6 +440,550 @@ static void Test_Display_ValidDispatch(void) WHAL_ASSERT_EQ(whal_Display_Deinit(&dev), WHAL_SUCCESS); } +/* --- Crypto dispatch tests --- */ + +static uint8_t cIn[16]; +static uint8_t cOut[32]; +static uint8_t cTag[16]; + +static whal_Error MockCryptoInit(whal_Crypto *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockCryptoDeinit(whal_Crypto *d) { (void)d; return WHAL_SUCCESS; } + +static const whal_CryptoDriver mockCryptoDriver = { + .Init = MockCryptoInit, + .Deinit = MockCryptoDeinit, +}; + +static void Test_Crypto_NullDev(void) +{ + WHAL_ASSERT_EQ(whal_Crypto_Init(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Crypto_Deinit(NULL), WHAL_EINVAL); +} + +static void Test_Crypto_NullDriver(void) +{ + whal_Crypto dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_Crypto_Init(&dev), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_Crypto_Deinit(&dev), WHAL_ENOTSUP); +} + +static void Test_Crypto_ValidDispatch(void) +{ + whal_Crypto dev = { .driver = &mockCryptoDriver }; + WHAL_ASSERT_EQ(whal_Crypto_Init(&dev), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Crypto_Deinit(&dev), WHAL_SUCCESS); +} + +static whal_Error MockAesEcbOneshot(whal_AesEcb *d, whal_Crypto_Dir dir, const void *k, size_t kSz, const void *in, void *out, size_t sz) { (void)d; (void)dir; (void)k; (void)kSz; (void)in; (void)out; (void)sz; return WHAL_SUCCESS; } +static whal_Error MockAesEcbStart(whal_AesEcb *d, whal_Crypto_Dir dir, const void *k, size_t kSz) { (void)d; (void)dir; (void)k; (void)kSz; return WHAL_SUCCESS; } +static whal_Error MockAesEcbProcess(whal_AesEcb *d, const void *in, void *out, size_t sz) { (void)d; (void)in; (void)out; (void)sz; return WHAL_SUCCESS; } +static whal_Error MockAesEcbStop(whal_AesEcb *d) { (void)d; return WHAL_SUCCESS; } + +static const whal_AesEcbDriver mockAesEcbDriver = { + .Oneshot = MockAesEcbOneshot, + .Start = MockAesEcbStart, + .Process = MockAesEcbProcess, + .Stop = MockAesEcbStop, +}; + +static const whal_AesEcbDriver mockAesEcbDriverNoStop = { + .Oneshot = MockAesEcbOneshot, + .Start = MockAesEcbStart, + .Process = MockAesEcbProcess, +}; + +static void Test_AesEcb_NullDev(void) +{ + WHAL_ASSERT_EQ(whal_AesEcb_Oneshot(NULL, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, cOut, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_AesEcb_Start(NULL, WHAL_CRYPTO_ENCRYPT, cIn, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_AesEcb_Process(NULL, cIn, cOut, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_AesEcb_Stop(NULL), WHAL_EINVAL); +} + +static void Test_AesEcb_NullDriver(void) +{ + whal_AesEcb dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_AesEcb_Oneshot(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, cOut, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_AesEcb_Start(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_AesEcb_Process(&dev, cIn, cOut, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_AesEcb_Stop(&dev), WHAL_ENOTSUP); +} + +static void Test_AesEcb_NoStop(void) +{ + whal_AesEcb dev = { .driver = &mockAesEcbDriverNoStop }; + WHAL_ASSERT_EQ(whal_AesEcb_Stop(&dev), WHAL_ENOTSUP); +} + +static void Test_AesEcb_ValidDispatch(void) +{ + whal_AesEcb dev = { .driver = &mockAesEcbDriver }; + WHAL_ASSERT_EQ(whal_AesEcb_Oneshot(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, cOut, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesEcb_Start(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesEcb_Process(&dev, cIn, cOut, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesEcb_Stop(&dev), WHAL_SUCCESS); +} + +static whal_Error MockAesCbcOneshot(whal_AesCbc *d, whal_Crypto_Dir dir, const void *k, size_t kSz, const void *iv, const void *in, void *out, size_t sz) { (void)d; (void)dir; (void)k; (void)kSz; (void)iv; (void)in; (void)out; (void)sz; return WHAL_SUCCESS; } +static whal_Error MockAesCbcStart(whal_AesCbc *d, whal_Crypto_Dir dir, const void *k, size_t kSz, const void *iv) { (void)d; (void)dir; (void)k; (void)kSz; (void)iv; return WHAL_SUCCESS; } +static whal_Error MockAesCbcProcess(whal_AesCbc *d, const void *in, void *out, size_t sz) { (void)d; (void)in; (void)out; (void)sz; return WHAL_SUCCESS; } +static whal_Error MockAesCbcStop(whal_AesCbc *d) { (void)d; return WHAL_SUCCESS; } + +static const whal_AesCbcDriver mockAesCbcDriver = { + .Oneshot = MockAesCbcOneshot, + .Start = MockAesCbcStart, + .Process = MockAesCbcProcess, + .Stop = MockAesCbcStop, +}; + +static const whal_AesCbcDriver mockAesCbcDriverNoStop = { + .Oneshot = MockAesCbcOneshot, + .Start = MockAesCbcStart, + .Process = MockAesCbcProcess, +}; + +static void Test_AesCbc_NullDev(void) +{ + WHAL_ASSERT_EQ(whal_AesCbc_Oneshot(NULL, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, cIn, cOut, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_AesCbc_Start(NULL, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_AesCbc_Process(NULL, cIn, cOut, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_AesCbc_Stop(NULL), WHAL_EINVAL); +} + +static void Test_AesCbc_NullDriver(void) +{ + whal_AesCbc dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_AesCbc_Oneshot(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, cIn, cOut, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_AesCbc_Start(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_AesCbc_Process(&dev, cIn, cOut, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_AesCbc_Stop(&dev), WHAL_ENOTSUP); +} + +static void Test_AesCbc_NoStop(void) +{ + whal_AesCbc dev = { .driver = &mockAesCbcDriverNoStop }; + WHAL_ASSERT_EQ(whal_AesCbc_Stop(&dev), WHAL_ENOTSUP); +} + +static void Test_AesCbc_ValidDispatch(void) +{ + whal_AesCbc dev = { .driver = &mockAesCbcDriver }; + WHAL_ASSERT_EQ(whal_AesCbc_Oneshot(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, cIn, cOut, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCbc_Start(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCbc_Process(&dev, cIn, cOut, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCbc_Stop(&dev), WHAL_SUCCESS); +} + +static whal_Error MockAesCtrOneshot(whal_AesCtr *d, whal_Crypto_Dir dir, const void *k, size_t kSz, const void *iv, const void *in, void *out, size_t sz) { (void)d; (void)dir; (void)k; (void)kSz; (void)iv; (void)in; (void)out; (void)sz; return WHAL_SUCCESS; } +static whal_Error MockAesCtrStart(whal_AesCtr *d, whal_Crypto_Dir dir, const void *k, size_t kSz, const void *iv) { (void)d; (void)dir; (void)k; (void)kSz; (void)iv; return WHAL_SUCCESS; } +static whal_Error MockAesCtrProcess(whal_AesCtr *d, const void *in, void *out, size_t sz) { (void)d; (void)in; (void)out; (void)sz; return WHAL_SUCCESS; } +static whal_Error MockAesCtrStop(whal_AesCtr *d) { (void)d; return WHAL_SUCCESS; } + +static const whal_AesCtrDriver mockAesCtrDriver = { + .Oneshot = MockAesCtrOneshot, + .Start = MockAesCtrStart, + .Process = MockAesCtrProcess, + .Stop = MockAesCtrStop, +}; + +static const whal_AesCtrDriver mockAesCtrDriverNoStop = { + .Oneshot = MockAesCtrOneshot, + .Start = MockAesCtrStart, + .Process = MockAesCtrProcess, +}; + +static void Test_AesCtr_NullDev(void) +{ + WHAL_ASSERT_EQ(whal_AesCtr_Oneshot(NULL, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, cIn, cOut, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_AesCtr_Start(NULL, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_AesCtr_Process(NULL, cIn, cOut, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_AesCtr_Stop(NULL), WHAL_EINVAL); +} + +static void Test_AesCtr_NullDriver(void) +{ + whal_AesCtr dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_AesCtr_Oneshot(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, cIn, cOut, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_AesCtr_Start(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_AesCtr_Process(&dev, cIn, cOut, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_AesCtr_Stop(&dev), WHAL_ENOTSUP); +} + +static void Test_AesCtr_NoStop(void) +{ + whal_AesCtr dev = { .driver = &mockAesCtrDriverNoStop }; + WHAL_ASSERT_EQ(whal_AesCtr_Stop(&dev), WHAL_ENOTSUP); +} + +static void Test_AesCtr_ValidDispatch(void) +{ + whal_AesCtr dev = { .driver = &mockAesCtrDriver }; + WHAL_ASSERT_EQ(whal_AesCtr_Oneshot(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, cIn, cOut, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCtr_Start(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCtr_Process(&dev, cIn, cOut, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCtr_Stop(&dev), WHAL_SUCCESS); +} + +static whal_Error MockAesGcmOneshot(whal_AesGcm *d, whal_Crypto_Dir dir, const void *k, size_t kSz, const void *iv, size_t ivSz, const void *aad, size_t aadSz, const void *in, void *out, size_t sz, void *tag, size_t tagSz) { (void)d; (void)dir; (void)k; (void)kSz; (void)iv; (void)ivSz; (void)aad; (void)aadSz; (void)in; (void)out; (void)sz; (void)tag; (void)tagSz; return WHAL_SUCCESS; } +static whal_Error MockAesGcmStart(whal_AesGcm *d, whal_Crypto_Dir dir, const void *k, size_t kSz, const void *iv, size_t ivSz, const void *aad, size_t aadSz) { (void)d; (void)dir; (void)k; (void)kSz; (void)iv; (void)ivSz; (void)aad; (void)aadSz; return WHAL_SUCCESS; } +static whal_Error MockAesGcmProcess(whal_AesGcm *d, const void *in, void *out, size_t sz) { (void)d; (void)in; (void)out; (void)sz; return WHAL_SUCCESS; } +static whal_Error MockAesGcmFinalize(whal_AesGcm *d, void *tag, size_t tagSz) { (void)d; (void)tag; (void)tagSz; return WHAL_SUCCESS; } + +static const whal_AesGcmDriver mockAesGcmDriver = { + .Oneshot = MockAesGcmOneshot, + .Start = MockAesGcmStart, + .Process = MockAesGcmProcess, + .Finalize = MockAesGcmFinalize, +}; + +static const whal_AesGcmDriver mockAesGcmDriverNoFinalize = { + .Oneshot = MockAesGcmOneshot, + .Start = MockAesGcmStart, + .Process = MockAesGcmProcess, +}; + +static void Test_AesGcm_NullDev(void) +{ + WHAL_ASSERT_EQ(whal_AesGcm_Oneshot(NULL, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, 12, cIn, 16, cIn, cOut, 16, cTag, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_AesGcm_Start(NULL, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, 12, cIn, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_AesGcm_Process(NULL, cIn, cOut, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_AesGcm_Finalize(NULL, cTag, 16), WHAL_EINVAL); +} + +static void Test_AesGcm_NullDriver(void) +{ + whal_AesGcm dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_AesGcm_Oneshot(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, 12, cIn, 16, cIn, cOut, 16, cTag, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_AesGcm_Start(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, 12, cIn, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_AesGcm_Process(&dev, cIn, cOut, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_AesGcm_Finalize(&dev, cTag, 16), WHAL_ENOTSUP); +} + +static void Test_AesGcm_NoFinalize(void) +{ + whal_AesGcm dev = { .driver = &mockAesGcmDriverNoFinalize }; + WHAL_ASSERT_EQ(whal_AesGcm_Finalize(&dev, cTag, 16), WHAL_ENOTSUP); +} + +static void Test_AesGcm_ValidDispatch(void) +{ + whal_AesGcm dev = { .driver = &mockAesGcmDriver }; + WHAL_ASSERT_EQ(whal_AesGcm_Oneshot(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, 12, cIn, 16, cIn, cOut, 16, cTag, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesGcm_Start(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, 12, cIn, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesGcm_Process(&dev, cIn, cOut, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesGcm_Finalize(&dev, cTag, 16), WHAL_SUCCESS); +} + +static whal_Error MockAesGmacOneshot(whal_AesGmac *d, const void *k, size_t kSz, const void *iv, size_t ivSz, const void *aad, size_t aadSz, void *tag, size_t tagSz) { (void)d; (void)k; (void)kSz; (void)iv; (void)ivSz; (void)aad; (void)aadSz; (void)tag; (void)tagSz; return WHAL_SUCCESS; } + +static const whal_AesGmacDriver mockAesGmacDriver = { + .Oneshot = MockAesGmacOneshot, +}; + +static const whal_AesGmacDriver mockAesGmacDriverNoOneshot = { +}; + +static void Test_AesGmac_NullDev(void) +{ + WHAL_ASSERT_EQ(whal_AesGmac_Oneshot(NULL, cIn, 16, cIn, 12, cIn, 16, cTag, 16), WHAL_EINVAL); +} + +static void Test_AesGmac_NullDriver(void) +{ + whal_AesGmac dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_AesGmac_Oneshot(&dev, cIn, 16, cIn, 12, cIn, 16, cTag, 16), WHAL_ENOTSUP); +} + +static void Test_AesGmac_NoOneshot(void) +{ + whal_AesGmac dev = { .driver = &mockAesGmacDriverNoOneshot }; + WHAL_ASSERT_EQ(whal_AesGmac_Oneshot(&dev, cIn, 16, cIn, 12, cIn, 16, cTag, 16), WHAL_ENOTSUP); +} + +static void Test_AesGmac_ValidDispatch(void) +{ + whal_AesGmac dev = { .driver = &mockAesGmacDriver }; + WHAL_ASSERT_EQ(whal_AesGmac_Oneshot(&dev, cIn, 16, cIn, 12, cIn, 16, cTag, 16), WHAL_SUCCESS); +} + +static whal_Error MockAesCcmOneshot(whal_AesCcm *d, whal_Crypto_Dir dir, const void *k, size_t kSz, const void *n, size_t nSz, const void *aad, size_t aadSz, const void *in, void *out, size_t sz, void *tag, size_t tagSz) { (void)d; (void)dir; (void)k; (void)kSz; (void)n; (void)nSz; (void)aad; (void)aadSz; (void)in; (void)out; (void)sz; (void)tag; (void)tagSz; return WHAL_SUCCESS; } +static whal_Error MockAesCcmStart(whal_AesCcm *d, whal_Crypto_Dir dir, const void *k, size_t kSz, const void *n, size_t nSz, const void *aad, size_t aadSz, size_t tagSz, size_t sz) { (void)d; (void)dir; (void)k; (void)kSz; (void)n; (void)nSz; (void)aad; (void)aadSz; (void)tagSz; (void)sz; return WHAL_SUCCESS; } +static whal_Error MockAesCcmProcess(whal_AesCcm *d, const void *in, void *out, size_t sz) { (void)d; (void)in; (void)out; (void)sz; return WHAL_SUCCESS; } +static whal_Error MockAesCcmFinalize(whal_AesCcm *d, void *tag, size_t tagSz) { (void)d; (void)tag; (void)tagSz; return WHAL_SUCCESS; } + +static const whal_AesCcmDriver mockAesCcmDriver = { + .Oneshot = MockAesCcmOneshot, + .Start = MockAesCcmStart, + .Process = MockAesCcmProcess, + .Finalize = MockAesCcmFinalize, +}; + +static const whal_AesCcmDriver mockAesCcmDriverNoFinalize = { + .Oneshot = MockAesCcmOneshot, + .Start = MockAesCcmStart, + .Process = MockAesCcmProcess, +}; + +static void Test_AesCcm_NullDev(void) +{ + WHAL_ASSERT_EQ(whal_AesCcm_Oneshot(NULL, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, 13, cIn, 16, cIn, cOut, 16, cTag, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_AesCcm_Start(NULL, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, 13, cIn, 16, 16, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_AesCcm_Process(NULL, cIn, cOut, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_AesCcm_Finalize(NULL, cTag, 16), WHAL_EINVAL); +} + +static void Test_AesCcm_NullDriver(void) +{ + whal_AesCcm dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_AesCcm_Oneshot(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, 13, cIn, 16, cIn, cOut, 16, cTag, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_AesCcm_Start(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, 13, cIn, 16, 16, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_AesCcm_Process(&dev, cIn, cOut, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_AesCcm_Finalize(&dev, cTag, 16), WHAL_ENOTSUP); +} + +static void Test_AesCcm_NoFinalize(void) +{ + whal_AesCcm dev = { .driver = &mockAesCcmDriverNoFinalize }; + WHAL_ASSERT_EQ(whal_AesCcm_Finalize(&dev, cTag, 16), WHAL_ENOTSUP); +} + +static void Test_AesCcm_ValidDispatch(void) +{ + whal_AesCcm dev = { .driver = &mockAesCcmDriver }; + WHAL_ASSERT_EQ(whal_AesCcm_Oneshot(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, 13, cIn, 16, cIn, cOut, 16, cTag, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCcm_Start(&dev, WHAL_CRYPTO_ENCRYPT, cIn, 16, cIn, 13, cIn, 16, 16, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCcm_Process(&dev, cIn, cOut, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCcm_Finalize(&dev, cTag, 16), WHAL_SUCCESS); +} + +static whal_Error MockSha1Oneshot(whal_Sha1 *d, const void *in, size_t inSz, void *dig, size_t digSz) { (void)d; (void)in; (void)inSz; (void)dig; (void)digSz; return WHAL_SUCCESS; } +static whal_Error MockSha1Start(whal_Sha1 *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockSha1Process(whal_Sha1 *d, const void *in, size_t inSz) { (void)d; (void)in; (void)inSz; return WHAL_SUCCESS; } +static whal_Error MockSha1Finalize(whal_Sha1 *d, void *dig, size_t digSz) { (void)d; (void)dig; (void)digSz; return WHAL_SUCCESS; } + +static const whal_Sha1Driver mockSha1Driver = { + .Oneshot = MockSha1Oneshot, + .Start = MockSha1Start, + .Process = MockSha1Process, + .Finalize = MockSha1Finalize, +}; + +static void Test_Sha1_NullDev(void) +{ + WHAL_ASSERT_EQ(whal_Sha1_Oneshot(NULL, cIn, 16, cOut, 32), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Sha1_Start(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Sha1_Process(NULL, cIn, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Sha1_Finalize(NULL, cOut, 32), WHAL_EINVAL); +} + +static void Test_Sha1_NullDriver(void) +{ + whal_Sha1 dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_Sha1_Oneshot(&dev, cIn, 16, cOut, 32), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_Sha1_Start(&dev), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_Sha1_Process(&dev, cIn, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_Sha1_Finalize(&dev, cOut, 32), WHAL_ENOTSUP); +} + +static void Test_Sha1_ValidDispatch(void) +{ + whal_Sha1 dev = { .driver = &mockSha1Driver }; + WHAL_ASSERT_EQ(whal_Sha1_Oneshot(&dev, cIn, 16, cOut, 32), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Sha1_Start(&dev), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Sha1_Process(&dev, cIn, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Sha1_Finalize(&dev, cOut, 32), WHAL_SUCCESS); +} + +static whal_Error MockSha224Oneshot(whal_Sha224 *d, const void *in, size_t inSz, void *dig, size_t digSz) { (void)d; (void)in; (void)inSz; (void)dig; (void)digSz; return WHAL_SUCCESS; } +static whal_Error MockSha224Start(whal_Sha224 *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockSha224Process(whal_Sha224 *d, const void *in, size_t inSz) { (void)d; (void)in; (void)inSz; return WHAL_SUCCESS; } +static whal_Error MockSha224Finalize(whal_Sha224 *d, void *dig, size_t digSz) { (void)d; (void)dig; (void)digSz; return WHAL_SUCCESS; } + +static const whal_Sha224Driver mockSha224Driver = { + .Oneshot = MockSha224Oneshot, + .Start = MockSha224Start, + .Process = MockSha224Process, + .Finalize = MockSha224Finalize, +}; + +static void Test_Sha224_NullDev(void) +{ + WHAL_ASSERT_EQ(whal_Sha224_Oneshot(NULL, cIn, 16, cOut, 32), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Sha224_Start(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Sha224_Process(NULL, cIn, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Sha224_Finalize(NULL, cOut, 32), WHAL_EINVAL); +} + +static void Test_Sha224_NullDriver(void) +{ + whal_Sha224 dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_Sha224_Oneshot(&dev, cIn, 16, cOut, 32), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_Sha224_Start(&dev), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_Sha224_Process(&dev, cIn, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_Sha224_Finalize(&dev, cOut, 32), WHAL_ENOTSUP); +} + +static void Test_Sha224_ValidDispatch(void) +{ + whal_Sha224 dev = { .driver = &mockSha224Driver }; + WHAL_ASSERT_EQ(whal_Sha224_Oneshot(&dev, cIn, 16, cOut, 32), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Sha224_Start(&dev), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Sha224_Process(&dev, cIn, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Sha224_Finalize(&dev, cOut, 32), WHAL_SUCCESS); +} + +static whal_Error MockSha256Oneshot(whal_Sha256 *d, const void *in, size_t inSz, void *dig, size_t digSz) { (void)d; (void)in; (void)inSz; (void)dig; (void)digSz; return WHAL_SUCCESS; } +static whal_Error MockSha256Start(whal_Sha256 *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockSha256Process(whal_Sha256 *d, const void *in, size_t inSz) { (void)d; (void)in; (void)inSz; return WHAL_SUCCESS; } +static whal_Error MockSha256Finalize(whal_Sha256 *d, void *dig, size_t digSz) { (void)d; (void)dig; (void)digSz; return WHAL_SUCCESS; } + +static const whal_Sha256Driver mockSha256Driver = { + .Oneshot = MockSha256Oneshot, + .Start = MockSha256Start, + .Process = MockSha256Process, + .Finalize = MockSha256Finalize, +}; + +static void Test_Sha256_NullDev(void) +{ + WHAL_ASSERT_EQ(whal_Sha256_Oneshot(NULL, cIn, 16, cOut, 32), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Sha256_Start(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Sha256_Process(NULL, cIn, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Sha256_Finalize(NULL, cOut, 32), WHAL_EINVAL); +} + +static void Test_Sha256_NullDriver(void) +{ + whal_Sha256 dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_Sha256_Oneshot(&dev, cIn, 16, cOut, 32), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_Sha256_Start(&dev), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_Sha256_Process(&dev, cIn, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_Sha256_Finalize(&dev, cOut, 32), WHAL_ENOTSUP); +} + +static void Test_Sha256_ValidDispatch(void) +{ + whal_Sha256 dev = { .driver = &mockSha256Driver }; + WHAL_ASSERT_EQ(whal_Sha256_Oneshot(&dev, cIn, 16, cOut, 32), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Sha256_Start(&dev), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Sha256_Process(&dev, cIn, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Sha256_Finalize(&dev, cOut, 32), WHAL_SUCCESS); +} + +static whal_Error MockHmacSha1Oneshot(whal_HmacSha1 *d, const void *k, size_t kSz, const void *in, size_t inSz, void *dig, size_t digSz) { (void)d; (void)k; (void)kSz; (void)in; (void)inSz; (void)dig; (void)digSz; return WHAL_SUCCESS; } +static whal_Error MockHmacSha1Start(whal_HmacSha1 *d, const void *k, size_t kSz) { (void)d; (void)k; (void)kSz; return WHAL_SUCCESS; } +static whal_Error MockHmacSha1Process(whal_HmacSha1 *d, const void *in, size_t inSz) { (void)d; (void)in; (void)inSz; return WHAL_SUCCESS; } +static whal_Error MockHmacSha1Finalize(whal_HmacSha1 *d, void *dig, size_t digSz) { (void)d; (void)dig; (void)digSz; return WHAL_SUCCESS; } + +static const whal_HmacSha1Driver mockHmacSha1Driver = { + .Oneshot = MockHmacSha1Oneshot, + .Start = MockHmacSha1Start, + .Process = MockHmacSha1Process, + .Finalize = MockHmacSha1Finalize, +}; + +static void Test_HmacSha1_NullDev(void) +{ + WHAL_ASSERT_EQ(whal_HmacSha1_Oneshot(NULL, cIn, 16, cIn, 16, cOut, 32), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_HmacSha1_Start(NULL, cIn, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_HmacSha1_Process(NULL, cIn, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_HmacSha1_Finalize(NULL, cOut, 32), WHAL_EINVAL); +} + +static void Test_HmacSha1_NullDriver(void) +{ + whal_HmacSha1 dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_HmacSha1_Oneshot(&dev, cIn, 16, cIn, 16, cOut, 32), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_HmacSha1_Start(&dev, cIn, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_HmacSha1_Process(&dev, cIn, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_HmacSha1_Finalize(&dev, cOut, 32), WHAL_ENOTSUP); +} + +static void Test_HmacSha1_ValidDispatch(void) +{ + whal_HmacSha1 dev = { .driver = &mockHmacSha1Driver }; + WHAL_ASSERT_EQ(whal_HmacSha1_Oneshot(&dev, cIn, 16, cIn, 16, cOut, 32), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_HmacSha1_Start(&dev, cIn, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_HmacSha1_Process(&dev, cIn, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_HmacSha1_Finalize(&dev, cOut, 32), WHAL_SUCCESS); +} + +static whal_Error MockHmacSha224Oneshot(whal_HmacSha224 *d, const void *k, size_t kSz, const void *in, size_t inSz, void *dig, size_t digSz) { (void)d; (void)k; (void)kSz; (void)in; (void)inSz; (void)dig; (void)digSz; return WHAL_SUCCESS; } +static whal_Error MockHmacSha224Start(whal_HmacSha224 *d, const void *k, size_t kSz) { (void)d; (void)k; (void)kSz; return WHAL_SUCCESS; } +static whal_Error MockHmacSha224Process(whal_HmacSha224 *d, const void *in, size_t inSz) { (void)d; (void)in; (void)inSz; return WHAL_SUCCESS; } +static whal_Error MockHmacSha224Finalize(whal_HmacSha224 *d, void *dig, size_t digSz) { (void)d; (void)dig; (void)digSz; return WHAL_SUCCESS; } + +static const whal_HmacSha224Driver mockHmacSha224Driver = { + .Oneshot = MockHmacSha224Oneshot, + .Start = MockHmacSha224Start, + .Process = MockHmacSha224Process, + .Finalize = MockHmacSha224Finalize, +}; + +static void Test_HmacSha224_NullDev(void) +{ + WHAL_ASSERT_EQ(whal_HmacSha224_Oneshot(NULL, cIn, 16, cIn, 16, cOut, 32), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_HmacSha224_Start(NULL, cIn, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_HmacSha224_Process(NULL, cIn, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_HmacSha224_Finalize(NULL, cOut, 32), WHAL_EINVAL); +} + +static void Test_HmacSha224_NullDriver(void) +{ + whal_HmacSha224 dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_HmacSha224_Oneshot(&dev, cIn, 16, cIn, 16, cOut, 32), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_HmacSha224_Start(&dev, cIn, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_HmacSha224_Process(&dev, cIn, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_HmacSha224_Finalize(&dev, cOut, 32), WHAL_ENOTSUP); +} + +static void Test_HmacSha224_ValidDispatch(void) +{ + whal_HmacSha224 dev = { .driver = &mockHmacSha224Driver }; + WHAL_ASSERT_EQ(whal_HmacSha224_Oneshot(&dev, cIn, 16, cIn, 16, cOut, 32), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_HmacSha224_Start(&dev, cIn, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_HmacSha224_Process(&dev, cIn, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_HmacSha224_Finalize(&dev, cOut, 32), WHAL_SUCCESS); +} + +static whal_Error MockHmacSha256Oneshot(whal_HmacSha256 *d, const void *k, size_t kSz, const void *in, size_t inSz, void *dig, size_t digSz) { (void)d; (void)k; (void)kSz; (void)in; (void)inSz; (void)dig; (void)digSz; return WHAL_SUCCESS; } +static whal_Error MockHmacSha256Start(whal_HmacSha256 *d, const void *k, size_t kSz) { (void)d; (void)k; (void)kSz; return WHAL_SUCCESS; } +static whal_Error MockHmacSha256Process(whal_HmacSha256 *d, const void *in, size_t inSz) { (void)d; (void)in; (void)inSz; return WHAL_SUCCESS; } +static whal_Error MockHmacSha256Finalize(whal_HmacSha256 *d, void *dig, size_t digSz) { (void)d; (void)dig; (void)digSz; return WHAL_SUCCESS; } + +static const whal_HmacSha256Driver mockHmacSha256Driver = { + .Oneshot = MockHmacSha256Oneshot, + .Start = MockHmacSha256Start, + .Process = MockHmacSha256Process, + .Finalize = MockHmacSha256Finalize, +}; + +static void Test_HmacSha256_NullDev(void) +{ + WHAL_ASSERT_EQ(whal_HmacSha256_Oneshot(NULL, cIn, 16, cIn, 16, cOut, 32), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_HmacSha256_Start(NULL, cIn, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_HmacSha256_Process(NULL, cIn, 16), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_HmacSha256_Finalize(NULL, cOut, 32), WHAL_EINVAL); +} + +static void Test_HmacSha256_NullDriver(void) +{ + whal_HmacSha256 dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_HmacSha256_Oneshot(&dev, cIn, 16, cIn, 16, cOut, 32), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_HmacSha256_Start(&dev, cIn, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_HmacSha256_Process(&dev, cIn, 16), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_HmacSha256_Finalize(&dev, cOut, 32), WHAL_ENOTSUP); +} + +static void Test_HmacSha256_ValidDispatch(void) +{ + whal_HmacSha256 dev = { .driver = &mockHmacSha256Driver }; + WHAL_ASSERT_EQ(whal_HmacSha256_Oneshot(&dev, cIn, 16, cIn, 16, cOut, 32), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_HmacSha256_Start(&dev, cIn, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_HmacSha256_Process(&dev, cIn, 16), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_HmacSha256_Finalize(&dev, cOut, 32), WHAL_SUCCESS); +} + void whal_Test_Dispatch(void) { WHAL_TEST_SUITE_START("dispatch"); @@ -473,5 +1017,50 @@ void whal_Test_Dispatch(void) WHAL_TEST(Test_Display_NullDriver); WHAL_TEST(Test_Display_BadArgs); WHAL_TEST(Test_Display_ValidDispatch); + WHAL_TEST(Test_Crypto_NullDev); + WHAL_TEST(Test_Crypto_NullDriver); + WHAL_TEST(Test_Crypto_ValidDispatch); + WHAL_TEST(Test_AesEcb_NullDev); + WHAL_TEST(Test_AesEcb_NullDriver); + WHAL_TEST(Test_AesEcb_NoStop); + WHAL_TEST(Test_AesEcb_ValidDispatch); + WHAL_TEST(Test_AesCbc_NullDev); + WHAL_TEST(Test_AesCbc_NullDriver); + WHAL_TEST(Test_AesCbc_NoStop); + WHAL_TEST(Test_AesCbc_ValidDispatch); + WHAL_TEST(Test_AesCtr_NullDev); + WHAL_TEST(Test_AesCtr_NullDriver); + WHAL_TEST(Test_AesCtr_NoStop); + WHAL_TEST(Test_AesCtr_ValidDispatch); + WHAL_TEST(Test_AesGcm_NullDev); + WHAL_TEST(Test_AesGcm_NullDriver); + WHAL_TEST(Test_AesGcm_NoFinalize); + WHAL_TEST(Test_AesGcm_ValidDispatch); + WHAL_TEST(Test_AesGmac_NullDev); + WHAL_TEST(Test_AesGmac_NullDriver); + WHAL_TEST(Test_AesGmac_NoOneshot); + WHAL_TEST(Test_AesGmac_ValidDispatch); + WHAL_TEST(Test_AesCcm_NullDev); + WHAL_TEST(Test_AesCcm_NullDriver); + WHAL_TEST(Test_AesCcm_NoFinalize); + WHAL_TEST(Test_AesCcm_ValidDispatch); + WHAL_TEST(Test_Sha1_NullDev); + WHAL_TEST(Test_Sha1_NullDriver); + WHAL_TEST(Test_Sha1_ValidDispatch); + WHAL_TEST(Test_Sha224_NullDev); + WHAL_TEST(Test_Sha224_NullDriver); + WHAL_TEST(Test_Sha224_ValidDispatch); + WHAL_TEST(Test_Sha256_NullDev); + WHAL_TEST(Test_Sha256_NullDriver); + WHAL_TEST(Test_Sha256_ValidDispatch); + WHAL_TEST(Test_HmacSha1_NullDev); + WHAL_TEST(Test_HmacSha1_NullDriver); + WHAL_TEST(Test_HmacSha1_ValidDispatch); + WHAL_TEST(Test_HmacSha224_NullDev); + WHAL_TEST(Test_HmacSha224_NullDriver); + WHAL_TEST(Test_HmacSha224_ValidDispatch); + WHAL_TEST(Test_HmacSha256_NullDev); + WHAL_TEST(Test_HmacSha256_NullDriver); + WHAL_TEST(Test_HmacSha256_ValidDispatch); WHAL_TEST_SUITE_END(); } From 4c73d1beb8fd033759460098b33f3a343def3d50 Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Tue, 13 Oct 2026 12:35:03 -0400 Subject: [PATCH 06/22] [crypto, test] Add AES decrypt, AAD/nonce length and key-size tests - Decrypt GCM and CCM with a partial final block, one-shot and streaming - Test GMAC with partial and empty AAD and CCM with AAD of 14 bytes or less - Test CCM with every nonce length from 7 to 12 and a 300-byte payload - Reject out-of-range GCM/GMAC IV and CCM nonce sizes - Add AES-128 known-answer tests for CTR, GCM, GMAC and CCM, and AES-192 ECB on STM32N6 - Generate the new vectors with gen_test_vectors.py --- tests/crypto/gen_test_vectors.py | 93 ++++++ tests/crypto/test_aes_ccm.c | 479 ++++++++++++++++++++++++++++ tests/crypto/test_aes_ctr.c | 31 ++ tests/crypto/test_aes_gcm.c | 152 +++++++++ tests/crypto/test_aes_gmac.c | 127 ++++++++ tests/crypto/test_stm32n6_aes_ecb.c | 68 ++++ tests/main.c | 6 + 7 files changed, 956 insertions(+) create mode 100644 tests/crypto/test_stm32n6_aes_ecb.c diff --git a/tests/crypto/gen_test_vectors.py b/tests/crypto/gen_test_vectors.py index 7e25fff..b3cbd5c 100644 --- a/tests/crypto/gen_test_vectors.py +++ b/tests/crypto/gen_test_vectors.py @@ -39,6 +39,7 @@ import sys try: + from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives.ciphers.aead import AESGCM, AESCCM from cryptography.hazmat.primitives import hashes, hmac as hmac_mod except ImportError: @@ -133,6 +134,98 @@ def banner(text): emit("ccmStreamNoPtTag", ccm_nopt_tag) + +# ---- AES-128 / AES-192 known-answer vectors ---- +# NIST SP 800-38A F.1.3 / F.5.1 inputs. The AES-128 AEAD vectors reuse the +# first 16 bytes of the existing AES-256 keys with the same IV/nonce, AAD and +# plaintext as the matching AES-256 KAT. +nist_pt = bytes.fromhex("6BC1BEE22E409F96E93D7E117393172A") +nist_key128 = bytes.fromhex("2B7E151628AED2A6ABF7158809CF4F3C") +nist_key192 = bytes.fromhex("8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B") +nist_ctr_iv = bytes.fromhex("F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF") + + +def aes_block(key, mode, data): + enc = Cipher(algorithms.AES(key), mode).encryptor() + return enc.update(data) + enc.finalize() + + +banner("test_aes_ctr.c: AES-128 vector") +emit("nistCtrCt128", aes_block(nist_key128, modes.CTR(nist_ctr_iv), nist_pt)) + +banner("test_stm32n6_aes_ecb.c: AES-192 vector") +emit("nistKey192", nist_key192) +emit("nistEcbCt192", aes_block(nist_key192, modes.ECB(), nist_pt)) + +gcm16_pt = bytes.fromhex( + "D9313225F88406E5A55909C5AFF5269A86A7A9531534F7DA2E4C303D8A318A72" + "1C3C0C95956809532FCF0E2449A6B525B16AEDF5AA0DE657BA637B39" +) +gcm16_aad = bytes.fromhex("FEEDFACEDEADBEEFFEEDFACEDEADBEEFABADDAD2") +ct_full = AESGCM(gcm_key[:16]).encrypt(gcm_iv, gcm16_pt, gcm16_aad) +banner("test_aes_gcm.c: AES-128 vector (gcmKey[0..15], gcm16Pt, gcm16Aad)") +emit("gcm128Ct", ct_full[:-16]) +emit("gcm128Tag", ct_full[-16:]) + +gmac_key = bytes.fromhex( + "78DC4E0AAF52D935C3C01EEA57428F00CA1FD475F5DA86A49C8DD73D68C8E223" +) +gmac_iv = bytes.fromhex("D79CF22D504CC793C3FB6C8A") +gmac_aad = bytes.fromhex("B96BAA8C1C75A671BFB2D08D06BE5F36") +banner("test_aes_gmac.c: AES-128 vector (gmacKey[0..15], gmacIv, gmacAad)") +emit("gmac128Tag", AESGCM(gmac_key[:16]).encrypt(gmac_iv, b"", gmac_aad)) + +ccm_pt = bytes.fromhex("E8DE970F6EE8E80EDE933581B5BCF4D837E2B72BAA8B00C3") +ccm_aad = bytes.fromhex( + "3C0E2815D37D844F7AC240BA9D6E3A0B2A86F706E885959E09A1005E024F6907" +) +ct_full = AESCCM(ccm_key[:16], tag_length=16).encrypt(ccm_nonce, ccm_pt, + ccm_aad) +banner("test_aes_ccm.c: AES-128 vector (ccmKey[0..15], ccmNonce/Aad/Pt)") +emit("ccm128Ct", ct_full[:-16]) +emit("ccm128Tag", ct_full[-16:]) + + +# ---- GMAC with AAD that is not a multiple of the block size ---- +# Tags over prefixes of one 31-byte AAD buffer, with gmacKey/gmacIv. +GMAC_AAD = bytes((i * 13 + 7) & 0xFF for i in range(31)) + +banner("test_aes_gmac.c: partial-AAD vectors") +emit("gmacPartialAad", GMAC_AAD) +for n in (0, 1, 15, 17, 20, 31): + emit(f"gmacTagAad{n}", AESGCM(gmac_key).encrypt(gmac_iv, b"", GMAC_AAD[:n])) + + +# ---- CCM with AAD short enough to fit in the first framing block ---- +# Tags over prefixes of one 15-byte AAD buffer, with ccmKey/ccmNonce/ccmPt. +# The ciphertext does not depend on the AAD, so it is always ccmCt. +CCM_SHORT_AAD = bytes((i * 17 + 9) & 0xFF for i in range(15)) +aesccm = AESCCM(ccm_key, tag_length=16) + +banner("test_aes_ccm.c: short-AAD vectors") +emit("ccmShortAad", CCM_SHORT_AAD) +for n in (1, 13, 14, 15): + ct_full = aesccm.encrypt(ccm_nonce, ccm_pt, CCM_SHORT_AAD[:n]) + assert ct_full[:-16] == aesccm.encrypt(ccm_nonce, ccm_pt, ccm_aad)[:-16] + emit(f"ccmTagShortAad{n}", ct_full[-16:]) + + +# ---- CCM with every accepted nonce length below 13 ---- +# Nonces are prefixes of ccmNonce, with ccmKey/ccmAad/ccmPt. +banner("test_aes_ccm.c: nonce-length vectors") +for n in range(7, 13): + ct_full = aesccm.encrypt(ccm_nonce[:n], ccm_pt, ccm_aad) + emit(f"ccmCtNonce{n}", ct_full[:-16]) + emit(f"ccmTagNonce{n}", ct_full[-16:]) + +# 300-byte payload with a 12-byte nonce, so the 3-byte B0 length field holds a +# value above 255. The plaintext is generated in the test with the same formula. +CCM_LONG_PT = bytes((i * 5 + 1) & 0xFF for i in range(300)) +ct_full = aesccm.encrypt(ccm_nonce[:12], CCM_LONG_PT, ccm_aad) +banner("test_aes_ccm.c: 300-byte payload with a 12-byte nonce") +emit("ccmLongCt", ct_full[:-16]) +emit("ccmLongTag", ct_full[-16:]) + # ---- SHA streaming (100-byte message) ---- def sha_digest(algo, data): h = hashes.Hash(algo) diff --git a/tests/crypto/test_aes_ccm.c b/tests/crypto/test_aes_ccm.c index 621558c..6312295 100644 --- a/tests/crypto/test_aes_ccm.c +++ b/tests/crypto/test_aes_ccm.c @@ -85,6 +85,192 @@ static const uint8_t ccmTag[16] = { 0x3C, 0xA5, 0x07, 0x95, 0xAC, 0xD9, 0x02, 0x03, }; +/* Vectors generated by tests/crypto/gen_test_vectors.py from ccmKey, ccmNonce, + * ccmAad and ccmPt above: + * - ccm128*: ccmKey[0..15]. + * - ccmTagShortAad: the first n bytes of ccmShortAad as AAD. The ciphertext + * does not depend on the AAD, so it is ccmCt. + * - ccm*Nonce: the first n bytes of ccmNonce. + * - ccmLong*: a 12-byte nonce over the 300 bytes CcmLongPt() builds. */ +static const uint8_t ccm128Ct[24] = { + 0xC3, 0x37, 0x1C, 0xE3, 0xCC, 0xDE, 0x32, 0x71, + 0x57, 0x0D, 0x3A, 0xFE, 0x18, 0x0E, 0xF0, 0x3A, + 0xC8, 0xE4, 0x0B, 0xED, 0x58, 0xFF, 0x6A, 0x7D, +}; + +static const uint8_t ccm128Tag[16] = { + 0xAB, 0x76, 0x60, 0x5E, 0x9A, 0x63, 0x49, 0xE5, + 0x56, 0x7F, 0xC9, 0xFB, 0x04, 0x40, 0x72, 0xE0, +}; + +static const uint8_t ccmShortAad[15] = { + 0x09, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E, 0x6F, 0x80, + 0x91, 0xA2, 0xB3, 0xC4, 0xD5, 0xE6, 0xF7, +}; + +static const uint8_t ccmTagShortAad1[16] = { + 0x19, 0x4C, 0xB4, 0x98, 0xBD, 0x77, 0x8C, 0xE4, + 0x34, 0x64, 0xBE, 0xFA, 0xC6, 0x99, 0x28, 0xF9, +}; + +static const uint8_t ccmTagShortAad13[16] = { + 0xEA, 0x69, 0x50, 0x87, 0x6B, 0x6F, 0x8C, 0x64, + 0x12, 0x7E, 0xBC, 0x4C, 0xFA, 0x0A, 0x5C, 0x2E, +}; + +static const uint8_t ccmTagShortAad14[16] = { + 0xCF, 0x61, 0x59, 0xF9, 0xF0, 0x38, 0x53, 0x8A, + 0xD7, 0xCC, 0x55, 0x6C, 0xBC, 0x50, 0x2E, 0x94, +}; + +static const uint8_t ccmTagShortAad15[16] = { + 0x68, 0x2A, 0xBE, 0x99, 0xFD, 0xCB, 0x46, 0x47, + 0x05, 0x9B, 0xF0, 0x89, 0x77, 0xD4, 0x19, 0x4F, +}; + +static const uint8_t ccmCtNonce7[24] = { + 0x63, 0x64, 0x63, 0x2D, 0xAA, 0x39, 0x67, 0x7E, + 0x4E, 0x74, 0x90, 0x69, 0x2B, 0xC1, 0x60, 0x14, + 0xBE, 0x82, 0x6A, 0x71, 0x01, 0xC5, 0xC7, 0x94, +}; + +static const uint8_t ccmTagNonce7[16] = { + 0x61, 0xF0, 0x71, 0x99, 0x05, 0x15, 0xEE, 0xE4, + 0x7E, 0xF6, 0x00, 0x63, 0xD1, 0x17, 0x02, 0x67, +}; + +static const uint8_t ccmCtNonce8[24] = { + 0x61, 0x42, 0xE6, 0xE3, 0x45, 0x1C, 0x33, 0xD9, + 0xF9, 0xF2, 0xF7, 0x2B, 0x6D, 0x84, 0xE8, 0xA7, + 0x66, 0xB9, 0x6B, 0x74, 0x4B, 0xBB, 0xDC, 0xE3, +}; + +static const uint8_t ccmTagNonce8[16] = { + 0x32, 0xDC, 0x9A, 0x2E, 0x1C, 0x65, 0xF4, 0xF7, + 0x1B, 0x9A, 0x0F, 0x60, 0x84, 0x7F, 0x4B, 0x49, +}; + +static const uint8_t ccmCtNonce9[24] = { + 0x35, 0xF0, 0x1D, 0x35, 0xA3, 0x0B, 0xC1, 0x66, + 0xA7, 0xBC, 0xFE, 0x57, 0xC4, 0x18, 0x14, 0x77, + 0xC6, 0x16, 0xA8, 0x1D, 0xB5, 0x61, 0xE9, 0x28, +}; + +static const uint8_t ccmTagNonce9[16] = { + 0x0F, 0x8E, 0x86, 0x37, 0x25, 0xF0, 0xDA, 0x2B, + 0x04, 0x9C, 0xB1, 0x50, 0x55, 0x45, 0x52, 0xE7, +}; + +static const uint8_t ccmCtNonce10[24] = { + 0x3C, 0xCC, 0x85, 0x08, 0x08, 0xEB, 0xFC, 0xC9, + 0x28, 0x57, 0x90, 0x24, 0x7A, 0x85, 0xC3, 0xB2, + 0x2C, 0xB8, 0x8C, 0x1A, 0x59, 0xF1, 0x02, 0xF8, +}; + +static const uint8_t ccmTagNonce10[16] = { + 0x67, 0x7E, 0xD0, 0x54, 0xC4, 0xE3, 0xBE, 0x2D, + 0xBE, 0xB7, 0xAC, 0x7A, 0xEB, 0x03, 0xC3, 0x55, +}; + +static const uint8_t ccmCtNonce11[24] = { + 0xBA, 0xF2, 0x19, 0x63, 0x21, 0x79, 0x18, 0x6C, + 0x09, 0x0A, 0x5B, 0x56, 0x35, 0x68, 0xA0, 0x58, + 0xD7, 0x73, 0x98, 0x82, 0xD6, 0xDD, 0x81, 0xA7, +}; + +static const uint8_t ccmTagNonce11[16] = { + 0xE9, 0xDF, 0xD1, 0x88, 0xEF, 0xA8, 0x14, 0xA3, + 0xB4, 0xDF, 0x0E, 0x67, 0xC3, 0xF9, 0x47, 0x6C, +}; + +static const uint8_t ccmCtNonce12[24] = { + 0x89, 0x26, 0xE0, 0x51, 0x63, 0xD0, 0x11, 0x25, + 0x60, 0x47, 0x02, 0x5F, 0xE8, 0xB7, 0x85, 0xCA, + 0x48, 0x53, 0x03, 0x16, 0x3F, 0x43, 0xBF, 0x15, +}; + +static const uint8_t ccmTagNonce12[16] = { + 0xE9, 0x09, 0x9F, 0x83, 0xC7, 0x02, 0x4F, 0x00, + 0xB8, 0xC4, 0x92, 0x8B, 0xC3, 0x7E, 0x2F, 0x15, +}; + +static const uint8_t ccmLongCt[300] = { + 0x60, 0xFE, 0x7C, 0x4E, 0x18, 0x22, 0xE6, 0x0F, + 0x97, 0xFA, 0x04, 0xE6, 0x60, 0x49, 0x36, 0x5E, + 0x2E, 0xE7, 0xEF, 0x5D, 0xF0, 0xA2, 0xD0, 0xA2, + 0xC0, 0xDA, 0x83, 0x63, 0x9A, 0xD2, 0x7C, 0x1E, + 0x49, 0xE3, 0x13, 0xF0, 0xDB, 0xA8, 0xB6, 0xE3, + 0x0E, 0x22, 0xF0, 0x4F, 0x8F, 0xE5, 0x5B, 0xC2, + 0x5E, 0x47, 0x1C, 0x0F, 0x98, 0xEF, 0x7B, 0xB5, + 0xFB, 0x9A, 0xE1, 0xE9, 0x47, 0xD1, 0x7F, 0xD0, + 0x8A, 0x5A, 0xB5, 0x84, 0xA9, 0x1A, 0x37, 0xAC, + 0xE5, 0xE3, 0xE0, 0x18, 0x6A, 0x63, 0x68, 0x47, + 0x02, 0x88, 0x4D, 0x7B, 0x5B, 0xB2, 0x2F, 0x1C, + 0x40, 0xDA, 0x33, 0x5B, 0x90, 0x44, 0xC2, 0x96, + 0xB6, 0xE0, 0x1A, 0x53, 0x59, 0xD2, 0x5F, 0x65, + 0x36, 0xB7, 0x3C, 0x49, 0x53, 0xDD, 0xD5, 0xD2, + 0x8E, 0xC2, 0x1C, 0x8A, 0x0C, 0x14, 0xFA, 0xD2, + 0x19, 0x3D, 0x2A, 0x36, 0x34, 0x2A, 0xFA, 0x4A, + 0x81, 0x49, 0x9A, 0x18, 0x81, 0x1F, 0xDD, 0x41, + 0x1E, 0xE5, 0x30, 0x14, 0x73, 0xAA, 0x1E, 0x8D, + 0xE9, 0x0C, 0xBE, 0x0B, 0x5B, 0xA2, 0x22, 0x7D, + 0x1B, 0xA3, 0x7A, 0xB6, 0x51, 0x2F, 0xBF, 0xF3, + 0xE6, 0xD0, 0x33, 0x0E, 0x66, 0x63, 0x04, 0x7B, + 0x48, 0x74, 0xDD, 0x89, 0x73, 0x27, 0x02, 0xAE, + 0x46, 0x38, 0x64, 0x34, 0x5C, 0xBC, 0x37, 0xA8, + 0xB8, 0xA9, 0x2F, 0x7A, 0x89, 0x2D, 0x7A, 0x2B, + 0x1A, 0x93, 0x07, 0x55, 0x00, 0x12, 0x15, 0x31, + 0xAD, 0xA7, 0xF5, 0xDD, 0x5E, 0x98, 0x24, 0xD3, + 0xBC, 0x8E, 0x0A, 0x42, 0x03, 0x51, 0x1B, 0xC3, + 0x80, 0x4A, 0x70, 0x9C, 0x13, 0x3C, 0x10, 0xB8, + 0x33, 0x3D, 0x68, 0x1C, 0x4E, 0xAD, 0x22, 0xEC, + 0x16, 0x08, 0xDD, 0x62, 0xD8, 0x8C, 0x1C, 0xD5, + 0x7E, 0x1C, 0x45, 0x13, 0xCF, 0xD6, 0xC5, 0x63, + 0x31, 0xDA, 0x19, 0xDA, 0xE4, 0xE0, 0xB9, 0x74, + 0x4A, 0xFB, 0x94, 0x4A, 0x35, 0xD8, 0x88, 0x3E, + 0xAF, 0x8F, 0x39, 0x07, 0xCC, 0xD6, 0x4D, 0x04, + 0xD9, 0x7D, 0x17, 0x9C, 0x06, 0x44, 0x4A, 0x9D, + 0xA1, 0xCD, 0x00, 0xF5, 0x82, 0x8D, 0x32, 0x03, + 0x1B, 0xB7, 0x57, 0xCE, 0x2F, 0xB2, 0x73, 0x58, + 0x1B, 0xBD, 0xA5, 0x93, +}; + +static const uint8_t ccmLongTag[16] = { + 0x8A, 0x6B, 0xF6, 0x42, 0xAD, 0x12, 0x89, 0xEE, + 0x63, 0x51, 0xEC, 0xEC, 0xC7, 0xCB, 0x53, 0x43, +}; + +static const struct { + size_t aadSz; + const uint8_t *tag; +} ccmShortAadCases[] = { + { 1, ccmTagShortAad1 }, + { 13, ccmTagShortAad13 }, + { 14, ccmTagShortAad14 }, + { 15, ccmTagShortAad15 }, +}; + +static const struct { + size_t nonceSz; + const uint8_t *ct; + const uint8_t *tag; +} ccmNonceCases[] = { + { 7, ccmCtNonce7, ccmTagNonce7 }, + { 8, ccmCtNonce8, ccmTagNonce8 }, + { 9, ccmCtNonce9, ccmTagNonce9 }, + { 10, ccmCtNonce10, ccmTagNonce10 }, + { 11, ccmCtNonce11, ccmTagNonce11 }, + { 12, ccmCtNonce12, ccmTagNonce12 }, +}; + +static void CcmLongPt(uint8_t *pt, size_t sz) +{ + size_t i; + + for (i = 0; i < sz; i++) + pt[i] = (uint8_t)(i * 5 + 1); +} + static void Test_AesCcm_Basic(void) { uint8_t ct[32] = {0}; @@ -129,6 +315,145 @@ static void Test_AesCcm_KnownAnswer(void) WHAL_ASSERT_MEM_EQ(tag, ccmTag, sizeof(ccmTag)); } +static void Test_AesCcm_Decrypt_KnownAnswer(void) +{ + uint8_t pt[sizeof(ccmCt)] = {0}; + uint8_t tag[16] = {0}; + + WHAL_ASSERT_EQ(whal_AesCcm_Oneshot(BOARD_AES_CCM_DEV, WHAL_CRYPTO_DECRYPT, + ccmKey, 32, + ccmNonce, sizeof(ccmNonce), + ccmAad, sizeof(ccmAad), + ccmCt, pt, sizeof(ccmCt), + tag, sizeof(tag)), + WHAL_SUCCESS); + + WHAL_ASSERT_MEM_EQ(pt, ccmPt, sizeof(ccmPt)); + WHAL_ASSERT_MEM_EQ(tag, ccmTag, sizeof(ccmTag)); +} + +static void Test_AesCcm_KnownAnswer128(void) +{ + uint8_t ct[sizeof(ccmPt)] = {0}; + uint8_t pt[sizeof(ccmPt)] = {0}; + uint8_t tag[16] = {0}; + + WHAL_ASSERT_EQ(whal_AesCcm_Oneshot(BOARD_AES_CCM_DEV, WHAL_CRYPTO_ENCRYPT, + ccmKey, 16, + ccmNonce, sizeof(ccmNonce), + ccmAad, sizeof(ccmAad), + ccmPt, ct, sizeof(ccmPt), + tag, sizeof(tag)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(ct, ccm128Ct, sizeof(ccm128Ct)); + WHAL_ASSERT_MEM_EQ(tag, ccm128Tag, sizeof(ccm128Tag)); + + WHAL_ASSERT_EQ(whal_AesCcm_Oneshot(BOARD_AES_CCM_DEV, WHAL_CRYPTO_DECRYPT, + ccmKey, 16, + ccmNonce, sizeof(ccmNonce), + ccmAad, sizeof(ccmAad), + ccm128Ct, pt, sizeof(ccm128Ct), + tag, sizeof(tag)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(pt, ccmPt, sizeof(ccmPt)); + WHAL_ASSERT_MEM_EQ(tag, ccm128Tag, sizeof(ccm128Tag)); +} + +static void Test_AesCcm_ShortAad(void) +{ + size_t i; + + for (i = 0; i < sizeof(ccmShortAadCases) / + sizeof(ccmShortAadCases[0]); i++) { + uint8_t ct[sizeof(ccmPt)] = {0}; + uint8_t tag[16] = {0}; + + WHAL_ASSERT_EQ(whal_AesCcm_Oneshot(BOARD_AES_CCM_DEV, + WHAL_CRYPTO_ENCRYPT, + ccmKey, 32, + ccmNonce, sizeof(ccmNonce), + ccmShortAad, + ccmShortAadCases[i].aadSz, + ccmPt, ct, sizeof(ccmPt), + tag, sizeof(tag)), + WHAL_SUCCESS); + + WHAL_ASSERT_MEM_EQ(ct, ccmCt, sizeof(ccmCt)); + WHAL_ASSERT_MEM_EQ(tag, ccmShortAadCases[i].tag, sizeof(tag)); + } +} + +static void Test_AesCcm_NonceLengths(void) +{ + size_t i; + + for (i = 0; i < sizeof(ccmNonceCases) / sizeof(ccmNonceCases[0]); i++) { + uint8_t ct[sizeof(ccmPt)] = {0}; + uint8_t tag[16] = {0}; + + WHAL_ASSERT_EQ(whal_AesCcm_Oneshot(BOARD_AES_CCM_DEV, + WHAL_CRYPTO_ENCRYPT, + ccmKey, 32, + ccmNonce, ccmNonceCases[i].nonceSz, + ccmAad, sizeof(ccmAad), + ccmPt, ct, sizeof(ccmPt), + tag, sizeof(tag)), + WHAL_SUCCESS); + + WHAL_ASSERT_MEM_EQ(ct, ccmNonceCases[i].ct, sizeof(ct)); + WHAL_ASSERT_MEM_EQ(tag, ccmNonceCases[i].tag, sizeof(tag)); + } +} + +static void Test_AesCcm_LongPayload(void) +{ + static uint8_t pt[sizeof(ccmLongCt)]; + static uint8_t ct[sizeof(ccmLongCt)]; + uint8_t tag[16] = {0}; + + CcmLongPt(pt, sizeof(pt)); + + WHAL_ASSERT_EQ(whal_AesCcm_Oneshot(BOARD_AES_CCM_DEV, WHAL_CRYPTO_ENCRYPT, + ccmKey, 32, + ccmNonce, 12, + ccmAad, sizeof(ccmAad), + pt, ct, sizeof(pt), + tag, sizeof(tag)), + WHAL_SUCCESS); + + WHAL_ASSERT_MEM_EQ(ct, ccmLongCt, sizeof(ccmLongCt)); + WHAL_ASSERT_MEM_EQ(tag, ccmLongTag, sizeof(ccmLongTag)); +} + +static void Test_AesCcm_BadNonceSize(void) +{ + static const size_t badSz[] = { 0, 6, 14 }; + const uint8_t badNonce[16] = {0}; + uint8_t ct[sizeof(plaintext)] = {0}; + uint8_t tag[16] = {0}; + const uint8_t zero[sizeof(plaintext)] = {0}; + size_t i; + + for (i = 0; i < sizeof(badSz) / sizeof(badSz[0]); i++) { + WHAL_ASSERT_EQ(whal_AesCcm_Oneshot(BOARD_AES_CCM_DEV, + WHAL_CRYPTO_ENCRYPT, + key, 32, badNonce, badSz[i], + aad, sizeof(aad), + plaintext, ct, sizeof(plaintext), + tag, sizeof(tag)), + WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_AesCcm_Start(BOARD_AES_CCM_DEV, + WHAL_CRYPTO_ENCRYPT, + key, 32, badNonce, badSz[i], + aad, sizeof(aad), + sizeof(tag), sizeof(plaintext)), + WHAL_EINVAL); + } + + WHAL_ASSERT_MEM_EQ(ct, zero, sizeof(ct)); + WHAL_ASSERT_MEM_EQ(tag, zero, sizeof(tag)); +} + /* Streaming KAT vectors generated by tests/crypto/gen_test_vectors.py from * ccmKey + ccmNonce above, AEAD_PT (50-byte deterministic plaintext) and * AEAD_AAD (20-byte deterministic AAD). Re-run the script if these inputs @@ -279,14 +604,168 @@ static void Test_AesCcm_Streaming_ThreeCall(void) WHAL_ASSERT_MEM_EQ(tag, ccmStreamTag, sizeof(ccmStreamTag)); } +static void Test_AesCcm_Streaming_Decrypt(void) +{ + uint8_t pt[sizeof(ccmStreamCt)] = {0}; + uint8_t tag[16] = {0}; + const size_t split = 32; + + WHAL_ASSERT_EQ(whal_AesCcm_Start(BOARD_AES_CCM_DEV, WHAL_CRYPTO_DECRYPT, + ccmKey, 32, + ccmNonce, sizeof(ccmNonce), + ccmStreamAad, sizeof(ccmStreamAad), + sizeof(tag), sizeof(ccmStreamCt)), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_AesCcm_Process(BOARD_AES_CCM_DEV, + ccmStreamCt, pt, split), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_AesCcm_Process(BOARD_AES_CCM_DEV, + ccmStreamCt + split, pt + split, + sizeof(ccmStreamCt) - split), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_AesCcm_Finalize(BOARD_AES_CCM_DEV, tag, sizeof(tag)), + WHAL_SUCCESS); + + WHAL_ASSERT_MEM_EQ(pt, ccmStreamPt, sizeof(ccmStreamPt)); + WHAL_ASSERT_MEM_EQ(tag, ccmStreamTag, sizeof(ccmStreamTag)); +} + +static void Test_AesCcm_Streaming_Decrypt_ThreeCall(void) +{ + uint8_t pt[sizeof(ccmStreamCt)] = {0}; + uint8_t tag[16] = {0}; + + WHAL_ASSERT_EQ(whal_AesCcm_Start(BOARD_AES_CCM_DEV, WHAL_CRYPTO_DECRYPT, + ccmKey, 32, + ccmNonce, sizeof(ccmNonce), + ccmStreamAad, sizeof(ccmStreamAad), + sizeof(tag), sizeof(ccmStreamCt)), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_AesCcm_Process(BOARD_AES_CCM_DEV, + ccmStreamCt, pt, 16), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCcm_Process(BOARD_AES_CCM_DEV, + ccmStreamCt + 16, pt + 16, 16), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCcm_Process(BOARD_AES_CCM_DEV, + ccmStreamCt + 32, pt + 32, + sizeof(ccmStreamCt) - 32), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_AesCcm_Finalize(BOARD_AES_CCM_DEV, tag, sizeof(tag)), + WHAL_SUCCESS); + + WHAL_ASSERT_MEM_EQ(pt, ccmStreamPt, sizeof(ccmStreamPt)); + WHAL_ASSERT_MEM_EQ(tag, ccmStreamTag, sizeof(ccmStreamTag)); +} + +static void Test_AesCcm_Streaming_ShortAad(void) +{ + size_t i; + + for (i = 0; i < sizeof(ccmShortAadCases) / + sizeof(ccmShortAadCases[0]); i++) { + uint8_t ct[sizeof(ccmPt)] = {0}; + uint8_t tag[16] = {0}; + + WHAL_ASSERT_EQ(whal_AesCcm_Start(BOARD_AES_CCM_DEV, + WHAL_CRYPTO_ENCRYPT, + ccmKey, 32, + ccmNonce, sizeof(ccmNonce), + ccmShortAad, + ccmShortAadCases[i].aadSz, + sizeof(tag), sizeof(ccmPt)), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCcm_Process(BOARD_AES_CCM_DEV, + ccmPt, ct, sizeof(ccmPt)), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCcm_Finalize(BOARD_AES_CCM_DEV, + tag, sizeof(tag)), + WHAL_SUCCESS); + + WHAL_ASSERT_MEM_EQ(ct, ccmCt, sizeof(ccmCt)); + WHAL_ASSERT_MEM_EQ(tag, ccmShortAadCases[i].tag, sizeof(tag)); + } +} + +static void Test_AesCcm_Streaming_NonceLengths(void) +{ + size_t i; + + for (i = 0; i < sizeof(ccmNonceCases) / sizeof(ccmNonceCases[0]); i++) { + uint8_t ct[sizeof(ccmPt)] = {0}; + uint8_t tag[16] = {0}; + + WHAL_ASSERT_EQ(whal_AesCcm_Start(BOARD_AES_CCM_DEV, + WHAL_CRYPTO_ENCRYPT, + ccmKey, 32, + ccmNonce, ccmNonceCases[i].nonceSz, + ccmAad, sizeof(ccmAad), + sizeof(tag), sizeof(ccmPt)), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCcm_Process(BOARD_AES_CCM_DEV, + ccmPt, ct, sizeof(ccmPt)), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCcm_Finalize(BOARD_AES_CCM_DEV, + tag, sizeof(tag)), + WHAL_SUCCESS); + + WHAL_ASSERT_MEM_EQ(ct, ccmNonceCases[i].ct, sizeof(ct)); + WHAL_ASSERT_MEM_EQ(tag, ccmNonceCases[i].tag, sizeof(tag)); + } +} + +static void Test_AesCcm_Streaming_LongPayload(void) +{ + static uint8_t pt[sizeof(ccmLongCt)]; + static uint8_t ct[sizeof(ccmLongCt)]; + uint8_t tag[16] = {0}; + const size_t split = 160; + + CcmLongPt(pt, sizeof(pt)); + + WHAL_ASSERT_EQ(whal_AesCcm_Start(BOARD_AES_CCM_DEV, WHAL_CRYPTO_ENCRYPT, + ccmKey, 32, + ccmNonce, 12, + ccmAad, sizeof(ccmAad), + sizeof(tag), sizeof(pt)), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCcm_Process(BOARD_AES_CCM_DEV, pt, ct, split), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCcm_Process(BOARD_AES_CCM_DEV, + pt + split, ct + split, + sizeof(pt) - split), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesCcm_Finalize(BOARD_AES_CCM_DEV, tag, sizeof(tag)), + WHAL_SUCCESS); + + WHAL_ASSERT_MEM_EQ(ct, ccmLongCt, sizeof(ccmLongCt)); + WHAL_ASSERT_MEM_EQ(tag, ccmLongTag, sizeof(ccmLongTag)); +} + void whal_Test_AesCcm(void) { WHAL_TEST_SUITE_START("aes_ccm"); WHAL_TEST(Test_AesCcm_Basic); WHAL_TEST(Test_AesCcm_KnownAnswer); + WHAL_TEST(Test_AesCcm_Decrypt_KnownAnswer); + WHAL_TEST(Test_AesCcm_KnownAnswer128); + WHAL_TEST(Test_AesCcm_ShortAad); + WHAL_TEST(Test_AesCcm_NonceLengths); + WHAL_TEST(Test_AesCcm_LongPayload); + WHAL_TEST(Test_AesCcm_BadNonceSize); WHAL_TEST(Test_AesCcm_Streaming); WHAL_TEST(Test_AesCcm_Streaming_NoAad); WHAL_TEST(Test_AesCcm_Streaming_NoPayload); WHAL_TEST(Test_AesCcm_Streaming_ThreeCall); + WHAL_TEST(Test_AesCcm_Streaming_Decrypt); + WHAL_TEST(Test_AesCcm_Streaming_Decrypt_ThreeCall); + WHAL_TEST(Test_AesCcm_Streaming_ShortAad); + WHAL_TEST(Test_AesCcm_Streaming_NonceLengths); + WHAL_TEST(Test_AesCcm_Streaming_LongPayload); WHAL_TEST_SUITE_END(); } diff --git a/tests/crypto/test_aes_ctr.c b/tests/crypto/test_aes_ctr.c index 66cb14c..de8f424 100644 --- a/tests/crypto/test_aes_ctr.c +++ b/tests/crypto/test_aes_ctr.c @@ -67,6 +67,17 @@ static const uint8_t nistCtrCt[16] = { 0xB7, 0xA7, 0xF5, 0x04, 0xBB, 0xF3, 0xD2, 0x28, }; +/* NIST SP 800-38A F.5.1 AES-128-CTR (reuses nistPt and nistCtrIv) */ +static const uint8_t nistKey128[16] = { + 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C, +}; + +static const uint8_t nistCtrCt128[16] = { + 0x87, 0x4D, 0x61, 0x91, 0xB6, 0x20, 0xE3, 0x26, + 0x1B, 0xEF, 0x68, 0x64, 0x99, 0x0D, 0xB6, 0xCE, +}; + static void Test_AesCtr_Basic(void) { uint8_t ct[32] = {0}; @@ -96,6 +107,25 @@ static void Test_AesCtr_KnownAnswer(void) WHAL_ASSERT_MEM_EQ(ct, nistCtrCt, sizeof(nistCtrCt)); } +static void Test_AesCtr_KnownAnswer128(void) +{ + uint8_t ct[16] = {0}; + uint8_t pt[16] = {0}; + + WHAL_ASSERT_EQ(whal_AesCtr_Oneshot(BOARD_AES_CTR_DEV, WHAL_CRYPTO_ENCRYPT, + nistKey128, 16, nistCtrIv, nistPt, ct, + sizeof(nistPt)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(ct, nistCtrCt128, sizeof(nistCtrCt128)); + + WHAL_ASSERT_EQ(whal_AesCtr_Oneshot(BOARD_AES_CTR_DEV, WHAL_CRYPTO_DECRYPT, + nistKey128, 16, nistCtrIv, + nistCtrCt128, pt, + sizeof(nistCtrCt128)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(pt, nistPt, sizeof(nistPt)); +} + static void Test_AesCtr_Streaming(void) { uint8_t expected[32] = {0}; @@ -135,6 +165,7 @@ void whal_Test_AesCtr(void) WHAL_TEST_SUITE_START("aes_ctr"); WHAL_TEST(Test_AesCtr_Basic); WHAL_TEST(Test_AesCtr_KnownAnswer); + WHAL_TEST(Test_AesCtr_KnownAnswer128); WHAL_TEST(Test_AesCtr_Streaming); WHAL_TEST_SUITE_END(); } diff --git a/tests/crypto/test_aes_gcm.c b/tests/crypto/test_aes_gcm.c index 22fe940..67cbeec 100644 --- a/tests/crypto/test_aes_gcm.c +++ b/tests/crypto/test_aes_gcm.c @@ -123,6 +123,24 @@ static const uint8_t gcm16Tag[16] = { 0xCD, 0xDF, 0x88, 0x53, 0xBB, 0x2D, 0x55, 0x1B, }; +/* AES-128-GCM over gcm16Pt/gcm16Aad with gcmKey[0..15] and gcmIv (GCM spec + * Test Case 4), generated by tests/crypto/gen_test_vectors.py. */ +static const uint8_t gcm128Ct[60] = { + 0x42, 0x83, 0x1E, 0xC2, 0x21, 0x77, 0x74, 0x24, + 0x4B, 0x72, 0x21, 0xB7, 0x84, 0xD0, 0xD4, 0x9C, + 0xE3, 0xAA, 0x21, 0x2F, 0x2C, 0x02, 0xA4, 0xE0, + 0x35, 0xC1, 0x7E, 0x23, 0x29, 0xAC, 0xA1, 0x2E, + 0x21, 0xD5, 0x14, 0xB2, 0x54, 0x66, 0x93, 0x1C, + 0x7D, 0x8F, 0x6A, 0x5A, 0xAC, 0x84, 0xAA, 0x05, + 0x1B, 0xA3, 0x0B, 0x39, 0x6A, 0x0A, 0xAC, 0x97, + 0x3D, 0x58, 0xE0, 0x91, +}; + +static const uint8_t gcm128Tag[16] = { + 0x5B, 0xC9, 0x4F, 0xBC, 0x32, 0x21, 0xA5, 0xDB, + 0x94, 0xFA, 0xE9, 0x5A, 0xE7, 0x12, 0x1A, 0x47, +}; + static void Test_AesGcm_Basic(void) { uint8_t ct[32] = {0}; @@ -184,6 +202,78 @@ static void Test_AesGcm_KnownAnswer_PartialBlocks(void) WHAL_ASSERT_MEM_EQ(tag, gcm16Tag, sizeof(gcm16Tag)); } +static void Test_AesGcm_Decrypt_PartialBlocks(void) +{ + uint8_t pt[sizeof(gcm16Ct)] = {0}; + uint8_t tag[16] = {0}; + + WHAL_ASSERT_EQ(whal_AesGcm_Oneshot(BOARD_AES_GCM_DEV, WHAL_CRYPTO_DECRYPT, + gcmKey, 32, + gcmIv, sizeof(gcmIv), + gcm16Aad, sizeof(gcm16Aad), + gcm16Ct, pt, sizeof(gcm16Ct), + tag, sizeof(tag)), + WHAL_SUCCESS); + + WHAL_ASSERT_MEM_EQ(pt, gcm16Pt, sizeof(gcm16Pt)); + WHAL_ASSERT_MEM_EQ(tag, gcm16Tag, sizeof(gcm16Tag)); +} + +static void Test_AesGcm_KnownAnswer128(void) +{ + uint8_t ct[sizeof(gcm16Pt)] = {0}; + uint8_t pt[sizeof(gcm16Pt)] = {0}; + uint8_t tag[16] = {0}; + + WHAL_ASSERT_EQ(whal_AesGcm_Oneshot(BOARD_AES_GCM_DEV, WHAL_CRYPTO_ENCRYPT, + gcmKey, 16, + gcmIv, sizeof(gcmIv), + gcm16Aad, sizeof(gcm16Aad), + gcm16Pt, ct, sizeof(gcm16Pt), + tag, sizeof(tag)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(ct, gcm128Ct, sizeof(gcm128Ct)); + WHAL_ASSERT_MEM_EQ(tag, gcm128Tag, sizeof(gcm128Tag)); + + WHAL_ASSERT_EQ(whal_AesGcm_Oneshot(BOARD_AES_GCM_DEV, WHAL_CRYPTO_DECRYPT, + gcmKey, 16, + gcmIv, sizeof(gcmIv), + gcm16Aad, sizeof(gcm16Aad), + gcm128Ct, pt, sizeof(gcm128Ct), + tag, sizeof(tag)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(pt, gcm16Pt, sizeof(gcm16Pt)); + WHAL_ASSERT_MEM_EQ(tag, gcm128Tag, sizeof(gcm128Tag)); +} + +static void Test_AesGcm_BadIvSize(void) +{ + static const size_t badSz[] = { 0, 8, 13, 16 }; + const uint8_t iv[16] = {0}; + uint8_t ct[sizeof(plaintext)] = {0}; + uint8_t tag[16] = {0}; + const uint8_t zero[sizeof(plaintext)] = {0}; + size_t i; + + for (i = 0; i < sizeof(badSz) / sizeof(badSz[0]); i++) { + WHAL_ASSERT_EQ(whal_AesGcm_Oneshot(BOARD_AES_GCM_DEV, + WHAL_CRYPTO_ENCRYPT, + key, 32, iv, badSz[i], + aad, sizeof(aad), + plaintext, ct, sizeof(plaintext), + tag, sizeof(tag)), + WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_AesGcm_Start(BOARD_AES_GCM_DEV, + WHAL_CRYPTO_ENCRYPT, + key, 32, iv, badSz[i], + aad, sizeof(aad)), + WHAL_ENOTSUP); + } + + WHAL_ASSERT_MEM_EQ(ct, zero, sizeof(ct)); + WHAL_ASSERT_MEM_EQ(tag, zero, sizeof(tag)); +} + /* Streaming KAT vectors generated by tests/crypto/gen_test_vectors.py from * gcmKey + gcmIv above, AEAD_PT (50-byte deterministic plaintext) and * AEAD_AAD (20-byte deterministic AAD). Re-run the script if these inputs @@ -331,15 +421,77 @@ static void Test_AesGcm_Streaming_ThreeCall(void) WHAL_ASSERT_MEM_EQ(tag, gcmStreamTag, sizeof(gcmStreamTag)); } +static void Test_AesGcm_Streaming_Decrypt(void) +{ + uint8_t pt[sizeof(gcmStreamCt)] = {0}; + uint8_t tag[16] = {0}; + const size_t split = 32; + + WHAL_ASSERT_EQ(whal_AesGcm_Start(BOARD_AES_GCM_DEV, WHAL_CRYPTO_DECRYPT, + gcmKey, 32, + gcmIv, sizeof(gcmIv), + gcmStreamAad, sizeof(gcmStreamAad)), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_AesGcm_Process(BOARD_AES_GCM_DEV, + gcmStreamCt, pt, split), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_AesGcm_Process(BOARD_AES_GCM_DEV, + gcmStreamCt + split, pt + split, + sizeof(gcmStreamCt) - split), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_AesGcm_Finalize(BOARD_AES_GCM_DEV, tag, sizeof(tag)), + WHAL_SUCCESS); + + WHAL_ASSERT_MEM_EQ(pt, gcmStreamPt, sizeof(gcmStreamPt)); + WHAL_ASSERT_MEM_EQ(tag, gcmStreamTag, sizeof(gcmStreamTag)); +} + +static void Test_AesGcm_Streaming_Decrypt_ThreeCall(void) +{ + uint8_t pt[sizeof(gcmStreamCt)] = {0}; + uint8_t tag[16] = {0}; + + WHAL_ASSERT_EQ(whal_AesGcm_Start(BOARD_AES_GCM_DEV, WHAL_CRYPTO_DECRYPT, + gcmKey, 32, + gcmIv, sizeof(gcmIv), + gcmStreamAad, sizeof(gcmStreamAad)), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_AesGcm_Process(BOARD_AES_GCM_DEV, + gcmStreamCt, pt, 16), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesGcm_Process(BOARD_AES_GCM_DEV, + gcmStreamCt + 16, pt + 16, 16), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_AesGcm_Process(BOARD_AES_GCM_DEV, + gcmStreamCt + 32, pt + 32, + sizeof(gcmStreamCt) - 32), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_AesGcm_Finalize(BOARD_AES_GCM_DEV, tag, sizeof(tag)), + WHAL_SUCCESS); + + WHAL_ASSERT_MEM_EQ(pt, gcmStreamPt, sizeof(gcmStreamPt)); + WHAL_ASSERT_MEM_EQ(tag, gcmStreamTag, sizeof(gcmStreamTag)); +} + void whal_Test_AesGcm(void) { WHAL_TEST_SUITE_START("aes_gcm"); WHAL_TEST(Test_AesGcm_Basic); WHAL_TEST(Test_AesGcm_KnownAnswer); WHAL_TEST(Test_AesGcm_KnownAnswer_PartialBlocks); + WHAL_TEST(Test_AesGcm_Decrypt_PartialBlocks); + WHAL_TEST(Test_AesGcm_KnownAnswer128); + WHAL_TEST(Test_AesGcm_BadIvSize); WHAL_TEST(Test_AesGcm_Streaming); WHAL_TEST(Test_AesGcm_Streaming_NoAad); WHAL_TEST(Test_AesGcm_Streaming_NoPayload); WHAL_TEST(Test_AesGcm_Streaming_ThreeCall); + WHAL_TEST(Test_AesGcm_Streaming_Decrypt); + WHAL_TEST(Test_AesGcm_Streaming_Decrypt_ThreeCall); WHAL_TEST_SUITE_END(); } diff --git a/tests/crypto/test_aes_gmac.c b/tests/crypto/test_aes_gmac.c index 51ab5de..63d7ba9 100644 --- a/tests/crypto/test_aes_gmac.c +++ b/tests/crypto/test_aes_gmac.c @@ -64,6 +64,62 @@ static const uint8_t gmacTag[16] = { 0xE0, 0x40, 0xB8, 0x57, 0x23, 0xA0, 0x6E, 0x76, }; +/* Vectors generated by tests/crypto/gen_test_vectors.py. gmac128Tag uses + * gmacKey[0..15] with gmacIv/gmacAad. gmacTagAad use gmacKey/gmacIv over + * the first n bytes of gmacPartialAad. */ +static const uint8_t gmac128Tag[16] = { + 0xE5, 0xE4, 0x9D, 0x25, 0x2B, 0xC1, 0x5C, 0xCA, + 0xBD, 0xE2, 0x80, 0x41, 0xF5, 0xEE, 0x0B, 0xF7, +}; + +static const uint8_t gmacPartialAad[31] = { + 0x07, 0x14, 0x21, 0x2E, 0x3B, 0x48, 0x55, 0x62, + 0x6F, 0x7C, 0x89, 0x96, 0xA3, 0xB0, 0xBD, 0xCA, + 0xD7, 0xE4, 0xF1, 0xFE, 0x0B, 0x18, 0x25, 0x32, + 0x3F, 0x4C, 0x59, 0x66, 0x73, 0x80, 0x8D, +}; + +static const uint8_t gmacTagAad0[16] = { + 0xD2, 0xCD, 0x4E, 0x34, 0x89, 0x70, 0xB2, 0x11, + 0xC5, 0xF1, 0x27, 0x6A, 0xAA, 0x76, 0xE2, 0xE1, +}; + +static const uint8_t gmacTagAad1[16] = { + 0x92, 0x3B, 0x29, 0x22, 0x55, 0xA3, 0xF4, 0xDD, + 0x7F, 0x76, 0xD0, 0x91, 0xF6, 0x58, 0xBC, 0xC8, +}; + +static const uint8_t gmacTagAad15[16] = { + 0x6C, 0x4B, 0x33, 0xAD, 0x98, 0xB1, 0xB7, 0x41, + 0x7C, 0xC3, 0x1B, 0x2E, 0xC2, 0xED, 0xA6, 0x8A, +}; + +static const uint8_t gmacTagAad17[16] = { + 0xE9, 0xA2, 0x3E, 0x8E, 0xC5, 0x53, 0x30, 0xB3, + 0xE2, 0xDA, 0x58, 0xCC, 0x1E, 0xC9, 0xAD, 0x0F, +}; + +static const uint8_t gmacTagAad20[16] = { + 0x43, 0xB5, 0x55, 0xBA, 0xBF, 0x29, 0xE2, 0xC3, + 0x8B, 0x5C, 0xB4, 0x00, 0x06, 0x6C, 0x18, 0xD9, +}; + +static const uint8_t gmacTagAad31[16] = { + 0xA5, 0x57, 0x1D, 0xA0, 0x2E, 0xBC, 0x83, 0xED, + 0xEB, 0x53, 0x50, 0x9A, 0x99, 0xB1, 0xCE, 0x9A, +}; + +static const struct { + size_t aadSz; + const uint8_t *tag; +} gmacPartialAadCases[] = { + { 1, gmacTagAad1 }, + { 15, gmacTagAad15 }, + { 17, gmacTagAad17 }, + { 20, gmacTagAad20 }, + { 31, gmacTagAad31 }, +}; + static void Test_AesGmac_Basic(void) { uint8_t tag1[16] = {0}; @@ -100,10 +156,81 @@ static void Test_AesGmac_KnownAnswer(void) WHAL_ASSERT_MEM_EQ(tag, gmacTag, sizeof(gmacTag)); } +static void Test_AesGmac_KnownAnswer128(void) +{ + uint8_t tag[16] = {0}; + + WHAL_ASSERT_EQ(whal_AesGmac_Oneshot(BOARD_AES_GMAC_DEV, + gmacKey, 16, + gmacIv, sizeof(gmacIv), + gmacAad, sizeof(gmacAad), + tag, sizeof(tag)), + WHAL_SUCCESS); + + WHAL_ASSERT_MEM_EQ(tag, gmac128Tag, sizeof(gmac128Tag)); +} + +static void Test_AesGmac_PartialAad(void) +{ + size_t i; + + for (i = 0; i < sizeof(gmacPartialAadCases) / + sizeof(gmacPartialAadCases[0]); i++) { + uint8_t tag[16] = {0}; + + WHAL_ASSERT_EQ(whal_AesGmac_Oneshot(BOARD_AES_GMAC_DEV, + gmacKey, 32, + gmacIv, sizeof(gmacIv), + gmacPartialAad, + gmacPartialAadCases[i].aadSz, + tag, sizeof(tag)), + WHAL_SUCCESS); + + WHAL_ASSERT_MEM_EQ(tag, gmacPartialAadCases[i].tag, sizeof(tag)); + } +} + +static void Test_AesGmac_EmptyAad(void) +{ + uint8_t tag[16] = {0}; + + WHAL_ASSERT_EQ(whal_AesGmac_Oneshot(BOARD_AES_GMAC_DEV, + gmacKey, 32, + gmacIv, sizeof(gmacIv), + NULL, 0, + tag, sizeof(tag)), + WHAL_SUCCESS); + + WHAL_ASSERT_MEM_EQ(tag, gmacTagAad0, sizeof(gmacTagAad0)); +} + +static void Test_AesGmac_BadIvSize(void) +{ + static const size_t badSz[] = { 0, 8, 13, 16 }; + const uint8_t iv[16] = {0}; + const uint8_t zero[16] = {0}; + uint8_t tag[16] = {0}; + size_t i; + + for (i = 0; i < sizeof(badSz) / sizeof(badSz[0]); i++) { + WHAL_ASSERT_EQ(whal_AesGmac_Oneshot(BOARD_AES_GMAC_DEV, + key, 32, iv, badSz[i], + aad, sizeof(aad), + tag, sizeof(tag)), + WHAL_ENOTSUP); + } + + WHAL_ASSERT_MEM_EQ(tag, zero, sizeof(tag)); +} + void whal_Test_AesGmac(void) { WHAL_TEST_SUITE_START("aes_gmac"); WHAL_TEST(Test_AesGmac_Basic); WHAL_TEST(Test_AesGmac_KnownAnswer); + WHAL_TEST(Test_AesGmac_KnownAnswer128); + WHAL_TEST(Test_AesGmac_PartialAad); + WHAL_TEST(Test_AesGmac_EmptyAad); + WHAL_TEST(Test_AesGmac_BadIvSize); WHAL_TEST_SUITE_END(); } diff --git a/tests/crypto/test_stm32n6_aes_ecb.c b/tests/crypto/test_stm32n6_aes_ecb.c new file mode 100644 index 0000000..3146b9a --- /dev/null +++ b/tests/crypto/test_stm32n6_aes_ecb.c @@ -0,0 +1,68 @@ +/* test_stm32n6_aes_ecb.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHAL. + * + * wolfHAL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHAL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include "wolfHAL_board.h" +#include "test.h" + +/* NIST SP 800-38A F.1.3 AES-192-ECB, generated by + * tests/crypto/gen_test_vectors.py */ +static const uint8_t nistKey192[24] = { + 0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52, + 0xC8, 0x10, 0xF3, 0x2B, 0x80, 0x90, 0x79, 0xE5, + 0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C, 0x6B, 0x7B, +}; + +static const uint8_t nistPt[16] = { + 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, + 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A, +}; + +static const uint8_t nistEcbCt192[16] = { + 0xBD, 0x33, 0x4F, 0x1D, 0x6E, 0x45, 0xF2, 0x5F, + 0xF7, 0x12, 0xA2, 0x14, 0x57, 0x1F, 0xA5, 0xCC, +}; + +static void Test_AesEcb_KnownAnswer192(void) +{ + uint8_t ct[16] = {0}; + uint8_t pt[16] = {0}; + + WHAL_ASSERT_EQ(whal_AesEcb_Oneshot(BOARD_AES_ECB_DEV, WHAL_CRYPTO_ENCRYPT, + nistKey192, 24, nistPt, ct, + sizeof(nistPt)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(ct, nistEcbCt192, sizeof(nistEcbCt192)); + + WHAL_ASSERT_EQ(whal_AesEcb_Oneshot(BOARD_AES_ECB_DEV, WHAL_CRYPTO_DECRYPT, + nistKey192, 24, nistEcbCt192, pt, + sizeof(nistEcbCt192)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(pt, nistPt, sizeof(nistPt)); +} + +void whal_Test_AesEcb_Platform(void) +{ + WHAL_TEST_SUITE_START("aes_ecb_platform"); + WHAL_TEST(Test_AesEcb_KnownAnswer192); + WHAL_TEST_SUITE_END(); +} diff --git a/tests/main.c b/tests/main.c index 8eebac6..47efc3e 100644 --- a/tests/main.c +++ b/tests/main.c @@ -76,6 +76,9 @@ void whal_Test_Crypto_Platform(void); #ifdef WHAL_TEST_ENABLE_AES_ECB void whal_Test_AesEcb(void); +#ifdef WHAL_TEST_ENABLE_AES_ECB_PLATFORM +void whal_Test_AesEcb_Platform(void); +#endif #endif #ifdef WHAL_TEST_ENABLE_AES_CBC @@ -272,6 +275,9 @@ void main(void) #ifdef WHAL_TEST_ENABLE_AES_ECB whal_Test_AesEcb(); +#ifdef WHAL_TEST_ENABLE_AES_ECB_PLATFORM + whal_Test_AesEcb_Platform(); +#endif #endif #ifdef WHAL_TEST_ENABLE_AES_CBC From ed15e7a3ebc41e9a40f534e4c476fa8ea7e23d95 Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Tue, 13 Oct 2026 12:57:12 -0400 Subject: [PATCH 07/22] [crypto, stm32wba, test] Reset HASH on HMAC timeouts and Deinit, add hash tests - Set HASH_CR.INIT and clear HASH_SR.DINIS on every HMAC timeout exit and in Deinit - Add a WBA readback test for the HASH context swap and digest registers after INIT and Deinit - Add HMAC known-answer tests with 64-byte and 131-byte keys, one-shot and streaming - Add SHA and HMAC streaming tests that leave 0 to 3 trailing bytes for Finalize and match Oneshot - Generate the new vectors with gen_test_vectors.py --- src/crypto/stm32wba_hash.c | 83 ++++++++--- tests/crypto/gen_test_vectors.py | 31 +++++ tests/crypto/test_hmac_sha1.c | 159 +++++++++++++++++++++ tests/crypto/test_hmac_sha224.c | 167 +++++++++++++++++++++++ tests/crypto/test_hmac_sha256.c | 167 +++++++++++++++++++++++ tests/crypto/test_sha1.c | 92 +++++++++++++ tests/crypto/test_sha224.c | 99 ++++++++++++++ tests/crypto/test_sha256.c | 99 ++++++++++++++ tests/crypto/test_stm32wba_hmac_sha256.c | 120 ++++++++++++++++ tests/main.c | 6 + 10 files changed, 1005 insertions(+), 18 deletions(-) create mode 100644 tests/crypto/test_stm32wba_hmac_sha256.c diff --git a/src/crypto/stm32wba_hash.c b/src/crypto/stm32wba_hash.c index e48f48d..abe7d5a 100644 --- a/src/crypto/stm32wba_hash.c +++ b/src/crypto/stm32wba_hash.c @@ -69,6 +69,9 @@ const whal_HmacSha256 whal_Stm32wba_HmacSha256_Dev = WHAL_CFG_STM32WBA_HASH_HMAC /* Status Register */ #define HASH_SR_REG 0x24 +#define HASH_SR_DINIS_Pos 0 +#define HASH_SR_DINIS_Msk (1UL << HASH_SR_DINIS_Pos) + #define HASH_SR_BUSY_Pos 3 #define HASH_SR_BUSY_Msk (1UL << HASH_SR_BUSY_Pos) @@ -106,6 +109,13 @@ static whal_Error WaitForReady(size_t base, whal_Timeout *timeout) return whal_Reg_ReadPoll(base, HASH_SR_REG, HASH_SR_BUSY_Msk, 0, timeout); } +/* Reset the hash core and clear DINIS so the context swap registers read 0. */ +static void ResetHash(size_t base) +{ + whal_Reg_Update(base, HASH_CR_REG, HASH_CR_INIT_Msk, HASH_CR_INIT_Msk); + whal_Reg_Update(base, HASH_SR_REG, HASH_SR_DINIS_Msk, 0); +} + /* Reset the stream staging buffer; called from each Start. */ static void StreamReset(void) { @@ -239,6 +249,7 @@ whal_Error whal_Stm32wba_Hash_Init(whal_Crypto *dev) whal_Error whal_Stm32wba_Hash_Deinit(whal_Crypto *dev) { (void)dev; + ResetHash(whal_Stm32wba_Hash_Dev.base); return WHAL_SUCCESS; } @@ -580,8 +591,10 @@ whal_Error whal_Stm32wba_HmacSha1_Oneshot(whal_HmacSha1 *dev, HASH_STR_DCAL_Msk); err = WaitForReady(base, cfg->timeout); - if (err) + if (err) { + ResetHash(base); return err; + } /* Message */ whal_Reg_Update(base, HASH_STR_REG, HASH_STR_NBLW_Msk, 0); @@ -592,8 +605,10 @@ whal_Error whal_Stm32wba_HmacSha1_Oneshot(whal_HmacSha1 *dev, HASH_STR_DCAL_Msk); err = WaitForReady(base, cfg->timeout); - if (err) + if (err) { + ResetHash(base); return err; + } /* Outer key */ whal_Reg_Update(base, HASH_STR_REG, HASH_STR_NBLW_Msk, 0); @@ -603,8 +618,10 @@ whal_Error whal_Stm32wba_HmacSha1_Oneshot(whal_HmacSha1 *dev, HASH_STR_DCAL_Msk); err = WaitForReady(base, cfg->timeout); - if (err) + if (err) { + ResetHash(base); return err; + } ReadDigest(base, (uint8_t *)digest, digestSz); return WHAL_SUCCESS; @@ -643,8 +660,10 @@ whal_Error whal_Stm32wba_HmacSha1_Start(whal_HmacSha1 *dev, HASH_STR_DCAL_Msk); err = WaitForReady(base, cfg->timeout); - if (err) + if (err) { + ResetHash(base); return err; + } /* Store key for outer-key phase in Finalize */ g_hmacSha1State.key = key; @@ -686,8 +705,10 @@ whal_Error whal_Stm32wba_HmacSha1_Finalize(whal_HmacSha1 *dev, HASH_STR_DCAL_Msk); err = WaitForReady(base, cfg->timeout); - if (err) + if (err) { + ResetHash(base); return err; + } /* Outer key */ whal_Reg_Update(base, HASH_STR_REG, HASH_STR_NBLW_Msk, 0); @@ -697,8 +718,10 @@ whal_Error whal_Stm32wba_HmacSha1_Finalize(whal_HmacSha1 *dev, HASH_STR_DCAL_Msk); err = WaitForReady(base, cfg->timeout); - if (err) + if (err) { + ResetHash(base); return err; + } ReadDigest(base, (uint8_t *)digest, digestSz); return WHAL_SUCCESS; @@ -750,8 +773,10 @@ whal_Error whal_Stm32wba_HmacSha224_Oneshot(whal_HmacSha224 *dev, HASH_STR_DCAL_Msk); err = WaitForReady(base, cfg->timeout); - if (err) + if (err) { + ResetHash(base); return err; + } /* Message */ whal_Reg_Update(base, HASH_STR_REG, HASH_STR_NBLW_Msk, 0); @@ -762,8 +787,10 @@ whal_Error whal_Stm32wba_HmacSha224_Oneshot(whal_HmacSha224 *dev, HASH_STR_DCAL_Msk); err = WaitForReady(base, cfg->timeout); - if (err) + if (err) { + ResetHash(base); return err; + } /* Outer key */ whal_Reg_Update(base, HASH_STR_REG, HASH_STR_NBLW_Msk, 0); @@ -773,8 +800,10 @@ whal_Error whal_Stm32wba_HmacSha224_Oneshot(whal_HmacSha224 *dev, HASH_STR_DCAL_Msk); err = WaitForReady(base, cfg->timeout); - if (err) + if (err) { + ResetHash(base); return err; + } ReadDigest(base, (uint8_t *)digest, digestSz); return WHAL_SUCCESS; @@ -813,8 +842,10 @@ whal_Error whal_Stm32wba_HmacSha224_Start(whal_HmacSha224 *dev, HASH_STR_DCAL_Msk); err = WaitForReady(base, cfg->timeout); - if (err) + if (err) { + ResetHash(base); return err; + } /* Store key for outer-key phase in Finalize */ g_hmacSha224State.key = key; @@ -856,8 +887,10 @@ whal_Error whal_Stm32wba_HmacSha224_Finalize(whal_HmacSha224 *dev, HASH_STR_DCAL_Msk); err = WaitForReady(base, cfg->timeout); - if (err) + if (err) { + ResetHash(base); return err; + } /* Outer key */ whal_Reg_Update(base, HASH_STR_REG, HASH_STR_NBLW_Msk, 0); @@ -867,8 +900,10 @@ whal_Error whal_Stm32wba_HmacSha224_Finalize(whal_HmacSha224 *dev, HASH_STR_DCAL_Msk); err = WaitForReady(base, cfg->timeout); - if (err) + if (err) { + ResetHash(base); return err; + } ReadDigest(base, (uint8_t *)digest, digestSz); return WHAL_SUCCESS; @@ -920,8 +955,10 @@ whal_Error whal_Stm32wba_HmacSha256_Oneshot(whal_HmacSha256 *dev, HASH_STR_DCAL_Msk); err = WaitForReady(base, cfg->timeout); - if (err) + if (err) { + ResetHash(base); return err; + } /* Message */ whal_Reg_Update(base, HASH_STR_REG, HASH_STR_NBLW_Msk, 0); @@ -932,8 +969,10 @@ whal_Error whal_Stm32wba_HmacSha256_Oneshot(whal_HmacSha256 *dev, HASH_STR_DCAL_Msk); err = WaitForReady(base, cfg->timeout); - if (err) + if (err) { + ResetHash(base); return err; + } /* Outer key */ whal_Reg_Update(base, HASH_STR_REG, HASH_STR_NBLW_Msk, 0); @@ -943,8 +982,10 @@ whal_Error whal_Stm32wba_HmacSha256_Oneshot(whal_HmacSha256 *dev, HASH_STR_DCAL_Msk); err = WaitForReady(base, cfg->timeout); - if (err) + if (err) { + ResetHash(base); return err; + } ReadDigest(base, (uint8_t *)digest, digestSz); return WHAL_SUCCESS; @@ -983,8 +1024,10 @@ whal_Error whal_Stm32wba_HmacSha256_Start(whal_HmacSha256 *dev, HASH_STR_DCAL_Msk); err = WaitForReady(base, cfg->timeout); - if (err) + if (err) { + ResetHash(base); return err; + } /* Store key for outer-key phase in Finalize */ g_hmacSha256State.key = key; @@ -1026,8 +1069,10 @@ whal_Error whal_Stm32wba_HmacSha256_Finalize(whal_HmacSha256 *dev, HASH_STR_DCAL_Msk); err = WaitForReady(base, cfg->timeout); - if (err) + if (err) { + ResetHash(base); return err; + } /* Outer key */ whal_Reg_Update(base, HASH_STR_REG, HASH_STR_NBLW_Msk, 0); @@ -1037,8 +1082,10 @@ whal_Error whal_Stm32wba_HmacSha256_Finalize(whal_HmacSha256 *dev, HASH_STR_DCAL_Msk); err = WaitForReady(base, cfg->timeout); - if (err) + if (err) { + ResetHash(base); return err; + } ReadDigest(base, (uint8_t *)digest, digestSz); return WHAL_SUCCESS; diff --git a/tests/crypto/gen_test_vectors.py b/tests/crypto/gen_test_vectors.py index b3cbd5c..4f0337b 100644 --- a/tests/crypto/gen_test_vectors.py +++ b/tests/crypto/gen_test_vectors.py @@ -274,6 +274,37 @@ def hmac_mac(algo, key, data): hmac_mac(hashes.SHA256(), hmac_unaligned_key, hmac_unaligned_msg)) + +# ---- HMAC with block-size and longer-than-block-size keys ---- +# RFC 4231 Test Case 6 message with a 64-byte key (no pre-hash) and the +# 131-byte RFC 4231 key (pre-hashed through LKEY). +hmac_long_msg = b"Test Using Larger Than Block-Size Key - Hash Key First" + +banner("test_hmac_sha*.c: 64-byte and 131-byte key vectors") +emit("hmacLongMsg", hmac_long_msg) +for bits, algo in (("1", hashes.SHA1()), ("224", hashes.SHA224()), + ("256", hashes.SHA256())): + emit(f"hmacSha{bits}Key64Mac", hmac_mac(algo, bytes([0xAA] * 64), + hmac_long_msg)) + emit(f"hmacSha{bits}Key131Mac", hmac_mac(algo, bytes([0xAA] * 131), + hmac_long_msg)) + + +# ---- SHA/HMAC over message lengths that leave 0-3 trailing bytes ---- +# Digests of prefixes of a 101-byte buffer whose first 100 bytes are LONG_BUF. +# The tests build the buffer with the same formula. +RESIDUE_MSG = bytes((i * 7 + 3) & 0xFF for i in range(101)) +RESIDUE_LENS = (0, 1, 3, 7, 101) + +banner("test_sha*.c / test_hmac_sha*.c: residue vectors") +for bits, algo in (("1", hashes.SHA1()), ("224", hashes.SHA224()), + ("256", hashes.SHA256())): + for n in RESIDUE_LENS: + emit(f"sha{bits}Residue{n}", sha_digest(algo, RESIDUE_MSG[:n])) + for n in RESIDUE_LENS: + emit(f"hmacSha{bits}Residue{n}", + hmac_mac(algo, hmac_key, RESIDUE_MSG[:n])) + # ---- RSA-1024 raw primitive KAT ---- # Fixed key matches the rsaN / rsaE / rsaD / ... arrays in test_pka.c. # Test plaintext is 0x00..0x7F (128 bytes), guaranteed < n (which starts diff --git a/tests/crypto/test_hmac_sha1.c b/tests/crypto/test_hmac_sha1.c index 5b91e89..3164b68 100644 --- a/tests/crypto/test_hmac_sha1.c +++ b/tests/crypto/test_hmac_sha1.c @@ -145,11 +145,170 @@ static void Test_HmacSha1_UnalignedKey(void) sizeof(hmacSha1UnalignedMac)); } +/* RFC 4231 Test Case 6 message with a 64-byte and a 131-byte 0xAA key, + * generated by tests/crypto/gen_test_vectors.py. */ +static const uint8_t hmacLongMsg[54] = { + 0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x69, + 0x6E, 0x67, 0x20, 0x4C, 0x61, 0x72, 0x67, 0x65, + 0x72, 0x20, 0x54, 0x68, 0x61, 0x6E, 0x20, 0x42, + 0x6C, 0x6F, 0x63, 0x6B, 0x2D, 0x53, 0x69, 0x7A, + 0x65, 0x20, 0x4B, 0x65, 0x79, 0x20, 0x2D, 0x20, + 0x48, 0x61, 0x73, 0x68, 0x20, 0x4B, 0x65, 0x79, + 0x20, 0x46, 0x69, 0x72, 0x73, 0x74, +}; + +static const uint8_t hmacSha1Key64Mac[20] = { + 0x07, 0x0A, 0x98, 0x99, 0x2C, 0x4C, 0x1A, 0x83, + 0x47, 0x4C, 0xB7, 0x80, 0xFC, 0x56, 0x46, 0x08, + 0xDF, 0x3C, 0xF5, 0x03, +}; + +static const uint8_t hmacSha1Key131Mac[20] = { + 0x90, 0xD0, 0xDA, 0xCE, 0x1C, 0x1B, 0xDC, 0x95, + 0x73, 0x39, 0x30, 0x78, 0x03, 0x16, 0x03, 0x35, + 0xBD, 0xE6, 0xDF, 0x2B, +}; + +static void Test_HmacSha1_LongKey(void) +{ + static const struct { + size_t keySz; + const uint8_t *mac; + } cases[] = { + { 64, hmacSha1Key64Mac }, + { 131, hmacSha1Key131Mac }, + }; + uint8_t key[131]; + size_t i; + + for (i = 0; i < sizeof(key); i++) + key[i] = 0xAA; + + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + uint8_t digest[20] = {0}; + + WHAL_ASSERT_EQ(whal_HmacSha1_Oneshot(BOARD_HMAC_SHA1_DEV, key, + cases[i].keySz, hmacLongMsg, + sizeof(hmacLongMsg), digest, + sizeof(digest)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(digest, cases[i].mac, sizeof(digest)); + + WHAL_ASSERT_EQ(whal_HmacSha1_Start(BOARD_HMAC_SHA1_DEV, key, + cases[i].keySz), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_HmacSha1_Process(BOARD_HMAC_SHA1_DEV, hmacLongMsg, + sizeof(hmacLongMsg)), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_HmacSha1_Finalize(BOARD_HMAC_SHA1_DEV, digest, + sizeof(digest)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(digest, cases[i].mac, sizeof(digest)); + } +} + +/* Vectors generated by tests/crypto/gen_test_vectors.py with hmacKey over the + * first n bytes of the 101-byte message ResidueMsg() builds. */ +static const uint8_t hmacSha1Residue0[20] = { + 0x12, 0x3F, 0xD7, 0x8B, 0xDA, 0x01, 0x00, 0x78, + 0x6A, 0xE8, 0x6B, 0x76, 0xF5, 0x0F, 0x01, 0xBD, + 0x18, 0xE4, 0x77, 0xF3, +}; + +static const uint8_t hmacSha1Residue1[20] = { + 0x60, 0xF3, 0xD5, 0x39, 0x18, 0x88, 0x9F, 0x96, + 0x8B, 0x6B, 0xAF, 0x61, 0xF5, 0xC8, 0xEB, 0x62, + 0x34, 0xDB, 0x37, 0x83, +}; + +static const uint8_t hmacSha1Residue3[20] = { + 0x89, 0xB6, 0x9E, 0xD0, 0x18, 0x3B, 0x5A, 0x42, + 0x24, 0xD3, 0x16, 0x39, 0xBB, 0x97, 0x13, 0xAE, + 0xAA, 0xA5, 0x49, 0x27, +}; + +static const uint8_t hmacSha1Residue7[20] = { + 0x75, 0x65, 0x25, 0x4B, 0x6A, 0x20, 0x13, 0x5E, + 0x55, 0x7A, 0xD8, 0x8A, 0x97, 0x90, 0xC2, 0x34, + 0xDF, 0x8D, 0x31, 0x46, +}; + +static const uint8_t hmacSha1Residue101[20] = { + 0x38, 0xCB, 0xE9, 0x67, 0x23, 0x17, 0xAD, 0x7F, + 0x7F, 0xE0, 0x85, 0x1C, 0x3D, 0x34, 0x0F, 0x61, + 0x34, 0x3A, 0xF9, 0x81, +}; + +static const struct { + size_t msgSz; + size_t chunkCnt; + size_t chunks[5]; + const uint8_t *digest; +} hmacSha1ResidueCases[] = { + { 0, 0, { 0 }, hmacSha1Residue0 }, + { 0, 1, { 0 }, hmacSha1Residue0 }, + { 1, 1, { 1 }, hmacSha1Residue1 }, + { 3, 1, { 3 }, hmacSha1Residue3 }, + { 7, 5, { 1, 1, 1, 1, 3 }, hmacSha1Residue7 }, + { 101, 2, { 50, 51 }, hmacSha1Residue101 }, + { 101, 3, { 50, 0, 51 }, hmacSha1Residue101 }, +}; + +static void ResidueMsg(uint8_t *msg, size_t sz) +{ + size_t i; + + for (i = 0; i < sz; i++) + msg[i] = (uint8_t)(i * 7 + 3); +} + +static void Test_HmacSha1_StreamingResidue(void) +{ + uint8_t msg[101]; + size_t i; + size_t j; + + ResidueMsg(msg, sizeof(msg)); + + for (i = 0; i < sizeof(hmacSha1ResidueCases) / + sizeof(hmacSha1ResidueCases[0]); i++) { + uint8_t digest[20] = {0}; + uint8_t oneshot[20] = {0}; + size_t off = 0; + + WHAL_ASSERT_EQ(whal_HmacSha1_Start(BOARD_HMAC_SHA1_DEV, hmacKey, + sizeof(hmacKey)), + WHAL_SUCCESS); + for (j = 0; j < hmacSha1ResidueCases[i].chunkCnt; j++) { + size_t chunk = hmacSha1ResidueCases[i].chunks[j]; + + WHAL_ASSERT_EQ(whal_HmacSha1_Process(BOARD_HMAC_SHA1_DEV, + msg + off, chunk), + WHAL_SUCCESS); + off += chunk; + } + WHAL_ASSERT_EQ(whal_HmacSha1_Finalize(BOARD_HMAC_SHA1_DEV, digest, + sizeof(digest)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(digest, hmacSha1ResidueCases[i].digest, + sizeof(digest)); + + WHAL_ASSERT_EQ(whal_HmacSha1_Oneshot(BOARD_HMAC_SHA1_DEV, hmacKey, + sizeof(hmacKey), msg, + hmacSha1ResidueCases[i].msgSz, + oneshot, sizeof(oneshot)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(oneshot, digest, sizeof(digest)); + } +} + void whal_Test_HmacSha1(void) { WHAL_TEST_SUITE_START("hmac_sha1"); WHAL_TEST(Test_HmacSha1_KnownAnswer); WHAL_TEST(Test_HmacSha1_Streaming); WHAL_TEST(Test_HmacSha1_UnalignedKey); + WHAL_TEST(Test_HmacSha1_LongKey); + WHAL_TEST(Test_HmacSha1_StreamingResidue); WHAL_TEST_SUITE_END(); } diff --git a/tests/crypto/test_hmac_sha224.c b/tests/crypto/test_hmac_sha224.c index f88a1cd..ae146ef 100644 --- a/tests/crypto/test_hmac_sha224.c +++ b/tests/crypto/test_hmac_sha224.c @@ -148,11 +148,178 @@ static void Test_HmacSha224_UnalignedKey(void) sizeof(hmacSha224UnalignedMac)); } +/* RFC 4231 Test Case 6 message with a 64-byte and a 131-byte 0xAA key, + * generated by tests/crypto/gen_test_vectors.py. */ +static const uint8_t hmacLongMsg[54] = { + 0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x69, + 0x6E, 0x67, 0x20, 0x4C, 0x61, 0x72, 0x67, 0x65, + 0x72, 0x20, 0x54, 0x68, 0x61, 0x6E, 0x20, 0x42, + 0x6C, 0x6F, 0x63, 0x6B, 0x2D, 0x53, 0x69, 0x7A, + 0x65, 0x20, 0x4B, 0x65, 0x79, 0x20, 0x2D, 0x20, + 0x48, 0x61, 0x73, 0x68, 0x20, 0x4B, 0x65, 0x79, + 0x20, 0x46, 0x69, 0x72, 0x73, 0x74, +}; + +static const uint8_t hmacSha224Key64Mac[28] = { + 0x05, 0xB0, 0x1D, 0xF0, 0x41, 0x6B, 0x54, 0xB6, + 0x68, 0xEC, 0xBD, 0x5F, 0x3F, 0x2F, 0x9F, 0x65, + 0x22, 0x00, 0xE0, 0x44, 0x58, 0xDF, 0xA6, 0xE2, + 0x1F, 0x25, 0x3B, 0x81, +}; + +static const uint8_t hmacSha224Key131Mac[28] = { + 0x95, 0xE9, 0xA0, 0xDB, 0x96, 0x20, 0x95, 0xAD, + 0xAE, 0xBE, 0x9B, 0x2D, 0x6F, 0x0D, 0xBC, 0xE2, + 0xD4, 0x99, 0xF1, 0x12, 0xF2, 0xD2, 0xB7, 0x27, + 0x3F, 0xA6, 0x87, 0x0E, +}; + +static void Test_HmacSha224_LongKey(void) +{ + static const struct { + size_t keySz; + const uint8_t *mac; + } cases[] = { + { 64, hmacSha224Key64Mac }, + { 131, hmacSha224Key131Mac }, + }; + uint8_t key[131]; + size_t i; + + for (i = 0; i < sizeof(key); i++) + key[i] = 0xAA; + + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + uint8_t digest[28] = {0}; + + WHAL_ASSERT_EQ(whal_HmacSha224_Oneshot(BOARD_HMAC_SHA224_DEV, key, + cases[i].keySz, hmacLongMsg, + sizeof(hmacLongMsg), digest, + sizeof(digest)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(digest, cases[i].mac, sizeof(digest)); + + WHAL_ASSERT_EQ(whal_HmacSha224_Start(BOARD_HMAC_SHA224_DEV, key, + cases[i].keySz), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_HmacSha224_Process(BOARD_HMAC_SHA224_DEV, + hmacLongMsg, + sizeof(hmacLongMsg)), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_HmacSha224_Finalize(BOARD_HMAC_SHA224_DEV, digest, + sizeof(digest)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(digest, cases[i].mac, sizeof(digest)); + } +} + +/* Vectors generated by tests/crypto/gen_test_vectors.py with hmacKey over the + * first n bytes of the 101-byte message ResidueMsg() builds. */ +static const uint8_t hmacSha224Residue0[28] = { + 0xBA, 0x7D, 0x81, 0x02, 0x8E, 0x07, 0xB3, 0x04, + 0x66, 0xB8, 0x67, 0xD8, 0xFE, 0xFA, 0xA5, 0x2D, + 0xB1, 0x11, 0xD5, 0x6B, 0x45, 0xDF, 0x5A, 0x0E, + 0x14, 0x65, 0xBF, 0x39, +}; + +static const uint8_t hmacSha224Residue1[28] = { + 0x18, 0x43, 0xA8, 0x31, 0x03, 0x49, 0xF4, 0x14, + 0x29, 0x76, 0x80, 0xA6, 0x43, 0xC9, 0xE1, 0x0D, + 0x66, 0xBE, 0x22, 0xCC, 0xBB, 0xE0, 0x0B, 0xE2, + 0x87, 0xFC, 0xFB, 0x14, +}; + +static const uint8_t hmacSha224Residue3[28] = { + 0x22, 0x44, 0x0C, 0xB9, 0xC7, 0x17, 0x87, 0x7C, + 0x65, 0xE1, 0x6D, 0xC2, 0x0A, 0xFA, 0x07, 0xB4, + 0x80, 0x02, 0xA8, 0xB8, 0x5F, 0xDF, 0x0D, 0x40, + 0x40, 0x06, 0xA4, 0x57, +}; + +static const uint8_t hmacSha224Residue7[28] = { + 0xF3, 0x4A, 0x9B, 0x17, 0xDD, 0xA5, 0xE2, 0xAC, + 0x92, 0x03, 0x1E, 0x0F, 0xBB, 0x60, 0x6D, 0xBA, + 0x7E, 0x17, 0x4A, 0x10, 0x79, 0xB9, 0xF2, 0x65, + 0xFB, 0x9A, 0x7E, 0x25, +}; + +static const uint8_t hmacSha224Residue101[28] = { + 0x52, 0x5C, 0x77, 0xA3, 0xD2, 0xC1, 0x39, 0x26, + 0x6D, 0x6C, 0xA6, 0x23, 0x19, 0x92, 0x3D, 0xC3, + 0xE7, 0x9A, 0x68, 0xD1, 0x62, 0x97, 0x75, 0xE0, + 0x79, 0xED, 0xEB, 0xC4, +}; + +static const struct { + size_t msgSz; + size_t chunkCnt; + size_t chunks[5]; + const uint8_t *digest; +} hmacSha224ResidueCases[] = { + { 0, 0, { 0 }, hmacSha224Residue0 }, + { 0, 1, { 0 }, hmacSha224Residue0 }, + { 1, 1, { 1 }, hmacSha224Residue1 }, + { 3, 1, { 3 }, hmacSha224Residue3 }, + { 7, 5, { 1, 1, 1, 1, 3 }, hmacSha224Residue7 }, + { 101, 2, { 50, 51 }, hmacSha224Residue101 }, + { 101, 3, { 50, 0, 51 }, hmacSha224Residue101 }, +}; + +static void ResidueMsg(uint8_t *msg, size_t sz) +{ + size_t i; + + for (i = 0; i < sz; i++) + msg[i] = (uint8_t)(i * 7 + 3); +} + +static void Test_HmacSha224_StreamingResidue(void) +{ + uint8_t msg[101]; + size_t i; + size_t j; + + ResidueMsg(msg, sizeof(msg)); + + for (i = 0; i < sizeof(hmacSha224ResidueCases) / + sizeof(hmacSha224ResidueCases[0]); i++) { + uint8_t digest[28] = {0}; + uint8_t oneshot[28] = {0}; + size_t off = 0; + + WHAL_ASSERT_EQ(whal_HmacSha224_Start(BOARD_HMAC_SHA224_DEV, hmacKey, + sizeof(hmacKey)), + WHAL_SUCCESS); + for (j = 0; j < hmacSha224ResidueCases[i].chunkCnt; j++) { + size_t chunk = hmacSha224ResidueCases[i].chunks[j]; + + WHAL_ASSERT_EQ(whal_HmacSha224_Process(BOARD_HMAC_SHA224_DEV, + msg + off, chunk), + WHAL_SUCCESS); + off += chunk; + } + WHAL_ASSERT_EQ(whal_HmacSha224_Finalize(BOARD_HMAC_SHA224_DEV, digest, + sizeof(digest)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(digest, hmacSha224ResidueCases[i].digest, + sizeof(digest)); + + WHAL_ASSERT_EQ(whal_HmacSha224_Oneshot(BOARD_HMAC_SHA224_DEV, hmacKey, + sizeof(hmacKey), msg, + hmacSha224ResidueCases[i].msgSz, + oneshot, sizeof(oneshot)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(oneshot, digest, sizeof(digest)); + } +} + void whal_Test_HmacSha224(void) { WHAL_TEST_SUITE_START("hmac_sha224"); WHAL_TEST(Test_HmacSha224_KnownAnswer); WHAL_TEST(Test_HmacSha224_Streaming); WHAL_TEST(Test_HmacSha224_UnalignedKey); + WHAL_TEST(Test_HmacSha224_LongKey); + WHAL_TEST(Test_HmacSha224_StreamingResidue); WHAL_TEST_SUITE_END(); } diff --git a/tests/crypto/test_hmac_sha256.c b/tests/crypto/test_hmac_sha256.c index d922284..9671ea9 100644 --- a/tests/crypto/test_hmac_sha256.c +++ b/tests/crypto/test_hmac_sha256.c @@ -148,11 +148,178 @@ static void Test_HmacSha256_UnalignedKey(void) sizeof(hmacSha256UnalignedMac)); } +/* RFC 4231 Test Case 6 message with a 64-byte and a 131-byte 0xAA key, + * generated by tests/crypto/gen_test_vectors.py. */ +static const uint8_t hmacLongMsg[54] = { + 0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x69, + 0x6E, 0x67, 0x20, 0x4C, 0x61, 0x72, 0x67, 0x65, + 0x72, 0x20, 0x54, 0x68, 0x61, 0x6E, 0x20, 0x42, + 0x6C, 0x6F, 0x63, 0x6B, 0x2D, 0x53, 0x69, 0x7A, + 0x65, 0x20, 0x4B, 0x65, 0x79, 0x20, 0x2D, 0x20, + 0x48, 0x61, 0x73, 0x68, 0x20, 0x4B, 0x65, 0x79, + 0x20, 0x46, 0x69, 0x72, 0x73, 0x74, +}; + +static const uint8_t hmacSha256Key64Mac[32] = { + 0x84, 0x33, 0x2A, 0x75, 0x80, 0xED, 0x3C, 0xF7, + 0x5D, 0xE8, 0x3C, 0x64, 0x4C, 0x8D, 0x2C, 0x1C, + 0x26, 0x2A, 0xD9, 0x0E, 0x01, 0x90, 0xE5, 0xC5, + 0xAE, 0x4B, 0x82, 0xB2, 0x10, 0x2E, 0x8E, 0x75, +}; + +static const uint8_t hmacSha256Key131Mac[32] = { + 0x60, 0xE4, 0x31, 0x59, 0x1E, 0xE0, 0xB6, 0x7F, + 0x0D, 0x8A, 0x26, 0xAA, 0xCB, 0xF5, 0xB7, 0x7F, + 0x8E, 0x0B, 0xC6, 0x21, 0x37, 0x28, 0xC5, 0x14, + 0x05, 0x46, 0x04, 0x0F, 0x0E, 0xE3, 0x7F, 0x54, +}; + +static void Test_HmacSha256_LongKey(void) +{ + static const struct { + size_t keySz; + const uint8_t *mac; + } cases[] = { + { 64, hmacSha256Key64Mac }, + { 131, hmacSha256Key131Mac }, + }; + uint8_t key[131]; + size_t i; + + for (i = 0; i < sizeof(key); i++) + key[i] = 0xAA; + + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + uint8_t digest[32] = {0}; + + WHAL_ASSERT_EQ(whal_HmacSha256_Oneshot(BOARD_HMAC_SHA256_DEV, key, + cases[i].keySz, hmacLongMsg, + sizeof(hmacLongMsg), digest, + sizeof(digest)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(digest, cases[i].mac, sizeof(digest)); + + WHAL_ASSERT_EQ(whal_HmacSha256_Start(BOARD_HMAC_SHA256_DEV, key, + cases[i].keySz), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_HmacSha256_Process(BOARD_HMAC_SHA256_DEV, + hmacLongMsg, + sizeof(hmacLongMsg)), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_HmacSha256_Finalize(BOARD_HMAC_SHA256_DEV, digest, + sizeof(digest)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(digest, cases[i].mac, sizeof(digest)); + } +} + +/* Vectors generated by tests/crypto/gen_test_vectors.py with hmacKey over the + * first n bytes of the 101-byte message ResidueMsg() builds. */ +static const uint8_t hmacSha256Residue0[32] = { + 0x99, 0x9A, 0x90, 0x12, 0x19, 0xF0, 0x32, 0xCD, + 0x49, 0x7C, 0xAD, 0xB5, 0xE6, 0x05, 0x1E, 0x97, + 0xB6, 0xA2, 0x9A, 0xB2, 0x97, 0xBD, 0x6A, 0xE7, + 0x22, 0xBD, 0x60, 0x62, 0xA2, 0xF5, 0x95, 0x42, +}; + +static const uint8_t hmacSha256Residue1[32] = { + 0x56, 0xC0, 0x64, 0x27, 0xD5, 0x31, 0x76, 0x64, + 0x94, 0xD6, 0x96, 0x49, 0x7D, 0x6E, 0x6D, 0x8A, + 0x02, 0x73, 0x1D, 0x77, 0xD1, 0x35, 0x94, 0xF7, + 0x87, 0x96, 0x53, 0x1E, 0x58, 0xA6, 0xAD, 0x62, +}; + +static const uint8_t hmacSha256Residue3[32] = { + 0x81, 0x7B, 0x61, 0x5A, 0x86, 0xFC, 0xFE, 0xE8, + 0xD1, 0x15, 0x31, 0xAB, 0x0C, 0xA9, 0xF2, 0x66, + 0x3C, 0x94, 0x17, 0x5A, 0xD0, 0xCD, 0xFD, 0x2D, + 0x3B, 0x16, 0x7F, 0x54, 0xAA, 0x5E, 0x16, 0x7A, +}; + +static const uint8_t hmacSha256Residue7[32] = { + 0xB9, 0xC6, 0xD5, 0xC4, 0x23, 0x11, 0x84, 0x0A, + 0x69, 0xCE, 0x12, 0x78, 0x40, 0x77, 0xBD, 0x41, + 0x2E, 0xE7, 0x99, 0x76, 0x0F, 0xC4, 0xC0, 0x30, + 0x5E, 0xC8, 0xD3, 0x89, 0x07, 0x24, 0x56, 0x61, +}; + +static const uint8_t hmacSha256Residue101[32] = { + 0x06, 0xCE, 0xA7, 0xB4, 0x0C, 0x38, 0x96, 0x2D, + 0x2B, 0x8F, 0xF4, 0xF5, 0xCF, 0x3C, 0x35, 0x1B, + 0x92, 0x33, 0xA5, 0xE9, 0xA9, 0xE3, 0x2B, 0xA1, + 0x22, 0x61, 0x86, 0x27, 0x8D, 0x58, 0x8E, 0xE6, +}; + +static const struct { + size_t msgSz; + size_t chunkCnt; + size_t chunks[5]; + const uint8_t *digest; +} hmacSha256ResidueCases[] = { + { 0, 0, { 0 }, hmacSha256Residue0 }, + { 0, 1, { 0 }, hmacSha256Residue0 }, + { 1, 1, { 1 }, hmacSha256Residue1 }, + { 3, 1, { 3 }, hmacSha256Residue3 }, + { 7, 5, { 1, 1, 1, 1, 3 }, hmacSha256Residue7 }, + { 101, 2, { 50, 51 }, hmacSha256Residue101 }, + { 101, 3, { 50, 0, 51 }, hmacSha256Residue101 }, +}; + +static void ResidueMsg(uint8_t *msg, size_t sz) +{ + size_t i; + + for (i = 0; i < sz; i++) + msg[i] = (uint8_t)(i * 7 + 3); +} + +static void Test_HmacSha256_StreamingResidue(void) +{ + uint8_t msg[101]; + size_t i; + size_t j; + + ResidueMsg(msg, sizeof(msg)); + + for (i = 0; i < sizeof(hmacSha256ResidueCases) / + sizeof(hmacSha256ResidueCases[0]); i++) { + uint8_t digest[32] = {0}; + uint8_t oneshot[32] = {0}; + size_t off = 0; + + WHAL_ASSERT_EQ(whal_HmacSha256_Start(BOARD_HMAC_SHA256_DEV, hmacKey, + sizeof(hmacKey)), + WHAL_SUCCESS); + for (j = 0; j < hmacSha256ResidueCases[i].chunkCnt; j++) { + size_t chunk = hmacSha256ResidueCases[i].chunks[j]; + + WHAL_ASSERT_EQ(whal_HmacSha256_Process(BOARD_HMAC_SHA256_DEV, + msg + off, chunk), + WHAL_SUCCESS); + off += chunk; + } + WHAL_ASSERT_EQ(whal_HmacSha256_Finalize(BOARD_HMAC_SHA256_DEV, digest, + sizeof(digest)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(digest, hmacSha256ResidueCases[i].digest, + sizeof(digest)); + + WHAL_ASSERT_EQ(whal_HmacSha256_Oneshot(BOARD_HMAC_SHA256_DEV, hmacKey, + sizeof(hmacKey), msg, + hmacSha256ResidueCases[i].msgSz, + oneshot, sizeof(oneshot)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(oneshot, digest, sizeof(digest)); + } +} + void whal_Test_HmacSha256(void) { WHAL_TEST_SUITE_START("hmac_sha256"); WHAL_TEST(Test_HmacSha256_KnownAnswer); WHAL_TEST(Test_HmacSha256_Streaming); WHAL_TEST(Test_HmacSha256_UnalignedKey); + WHAL_TEST(Test_HmacSha256_LongKey); + WHAL_TEST(Test_HmacSha256_StreamingResidue); WHAL_TEST_SUITE_END(); } diff --git a/tests/crypto/test_sha1.c b/tests/crypto/test_sha1.c index 40ce433..0f3be20 100644 --- a/tests/crypto/test_sha1.c +++ b/tests/crypto/test_sha1.c @@ -87,10 +87,102 @@ static void Test_Sha1_Streaming(void) WHAL_ASSERT_MEM_EQ(digest, sha1StreamDigest, sizeof(sha1StreamDigest)); } +/* Vectors generated by tests/crypto/gen_test_vectors.py over the first n bytes + * of the 101-byte message ResidueMsg() builds. */ +static const uint8_t sha1Residue0[20] = { + 0xDA, 0x39, 0xA3, 0xEE, 0x5E, 0x6B, 0x4B, 0x0D, + 0x32, 0x55, 0xBF, 0xEF, 0x95, 0x60, 0x18, 0x90, + 0xAF, 0xD8, 0x07, 0x09, +}; + +static const uint8_t sha1Residue1[20] = { + 0x98, 0x42, 0x92, 0x6A, 0xF7, 0xCA, 0x0A, 0x8C, + 0xCA, 0x12, 0x60, 0x4F, 0x94, 0x54, 0x14, 0xF0, + 0x7B, 0x01, 0xE1, 0x3D, +}; + +static const uint8_t sha1Residue3[20] = { + 0x42, 0x01, 0xDE, 0x9F, 0x98, 0xCB, 0x0A, 0x9B, + 0x8C, 0xF5, 0x23, 0x98, 0xBE, 0x78, 0x02, 0xB5, + 0x5D, 0x45, 0x26, 0x6A, +}; + +static const uint8_t sha1Residue7[20] = { + 0x8F, 0x61, 0xE0, 0x51, 0xDB, 0x8F, 0x5B, 0xFA, + 0x67, 0xD8, 0xEF, 0xF0, 0xC4, 0x0C, 0xF8, 0x68, + 0xF6, 0x8E, 0x70, 0xBB, +}; + +static const uint8_t sha1Residue101[20] = { + 0x2A, 0xE4, 0x28, 0xC5, 0xF5, 0xDF, 0xAF, 0xF2, + 0xDD, 0x18, 0xCE, 0x37, 0x38, 0x95, 0xC4, 0xB0, + 0xF6, 0x3E, 0x71, 0xA4, +}; + +static const struct { + size_t msgSz; + size_t chunkCnt; + size_t chunks[5]; + const uint8_t *digest; +} sha1ResidueCases[] = { + { 0, 0, { 0 }, sha1Residue0 }, + { 0, 1, { 0 }, sha1Residue0 }, + { 1, 1, { 1 }, sha1Residue1 }, + { 3, 1, { 3 }, sha1Residue3 }, + { 7, 5, { 1, 1, 1, 1, 3 }, sha1Residue7 }, + { 101, 2, { 50, 51 }, sha1Residue101 }, + { 101, 3, { 50, 0, 51 }, sha1Residue101 }, +}; + +static void ResidueMsg(uint8_t *msg, size_t sz) +{ + size_t i; + + for (i = 0; i < sz; i++) + msg[i] = (uint8_t)(i * 7 + 3); +} + +static void Test_Sha1_StreamingResidue(void) +{ + uint8_t msg[101]; + size_t i; + size_t j; + + ResidueMsg(msg, sizeof(msg)); + + for (i = 0; i < sizeof(sha1ResidueCases) / + sizeof(sha1ResidueCases[0]); i++) { + uint8_t digest[20] = {0}; + uint8_t oneshot[20] = {0}; + size_t off = 0; + + WHAL_ASSERT_EQ(whal_Sha1_Start(BOARD_SHA1_DEV), + WHAL_SUCCESS); + for (j = 0; j < sha1ResidueCases[i].chunkCnt; j++) { + size_t chunk = sha1ResidueCases[i].chunks[j]; + + WHAL_ASSERT_EQ(whal_Sha1_Process(BOARD_SHA1_DEV, msg + off, chunk), + WHAL_SUCCESS); + off += chunk; + } + WHAL_ASSERT_EQ(whal_Sha1_Finalize(BOARD_SHA1_DEV, digest, + sizeof(digest)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(digest, sha1ResidueCases[i].digest, sizeof(digest)); + + WHAL_ASSERT_EQ(whal_Sha1_Oneshot(BOARD_SHA1_DEV, msg, + sha1ResidueCases[i].msgSz, oneshot, + sizeof(oneshot)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(oneshot, digest, sizeof(digest)); + } +} + void whal_Test_Sha1(void) { WHAL_TEST_SUITE_START("sha1"); WHAL_TEST(Test_Sha1_KnownAnswer); WHAL_TEST(Test_Sha1_Streaming); + WHAL_TEST(Test_Sha1_StreamingResidue); WHAL_TEST_SUITE_END(); } diff --git a/tests/crypto/test_sha224.c b/tests/crypto/test_sha224.c index 4c0b033..8dc187f 100644 --- a/tests/crypto/test_sha224.c +++ b/tests/crypto/test_sha224.c @@ -90,10 +90,109 @@ static void Test_Sha224_Streaming(void) WHAL_ASSERT_MEM_EQ(digest, sha224StreamDigest, sizeof(sha224StreamDigest)); } +/* Vectors generated by tests/crypto/gen_test_vectors.py over the first n bytes + * of the 101-byte message ResidueMsg() builds. */ +static const uint8_t sha224Residue0[28] = { + 0xD1, 0x4A, 0x02, 0x8C, 0x2A, 0x3A, 0x2B, 0xC9, + 0x47, 0x61, 0x02, 0xBB, 0x28, 0x82, 0x34, 0xC4, + 0x15, 0xA2, 0xB0, 0x1F, 0x82, 0x8E, 0xA6, 0x2A, + 0xC5, 0xB3, 0xE4, 0x2F, +}; + +static const uint8_t sha224Residue1[28] = { + 0x15, 0xEB, 0xD4, 0xB8, 0xFD, 0xFC, 0x69, 0x05, + 0xEA, 0x00, 0x2A, 0x3E, 0xFC, 0xC8, 0xFA, 0xA7, + 0x19, 0x54, 0x73, 0x9E, 0xE6, 0x2B, 0xBB, 0xF1, + 0x68, 0x36, 0xEB, 0x5C, +}; + +static const uint8_t sha224Residue3[28] = { + 0x95, 0x6A, 0x7E, 0x9E, 0xEE, 0xFC, 0x4F, 0x61, + 0x75, 0xE8, 0xCD, 0x33, 0x7C, 0xD2, 0x8A, 0x80, + 0xF6, 0xA0, 0xA9, 0x88, 0xB3, 0xFE, 0x0E, 0xD6, + 0x47, 0xB4, 0xA9, 0x53, +}; + +static const uint8_t sha224Residue7[28] = { + 0xED, 0xC1, 0xF8, 0xC0, 0x46, 0xCC, 0xDA, 0x69, + 0x85, 0xA6, 0xA4, 0x0C, 0xBE, 0xBC, 0xD1, 0x0D, + 0xD6, 0x4A, 0xDA, 0x1B, 0xBE, 0x56, 0xC5, 0xBA, + 0x09, 0xC5, 0xDB, 0x08, +}; + +static const uint8_t sha224Residue101[28] = { + 0x1E, 0x18, 0x6E, 0x9D, 0x08, 0x61, 0x9B, 0x9D, + 0x3D, 0xC3, 0x2D, 0xED, 0x6A, 0x7A, 0x97, 0xB3, + 0xFD, 0x2B, 0xBF, 0x7E, 0x23, 0x76, 0x9C, 0xF8, + 0x0D, 0xA3, 0x83, 0xE7, +}; + +static const struct { + size_t msgSz; + size_t chunkCnt; + size_t chunks[5]; + const uint8_t *digest; +} sha224ResidueCases[] = { + { 0, 0, { 0 }, sha224Residue0 }, + { 0, 1, { 0 }, sha224Residue0 }, + { 1, 1, { 1 }, sha224Residue1 }, + { 3, 1, { 3 }, sha224Residue3 }, + { 7, 5, { 1, 1, 1, 1, 3 }, sha224Residue7 }, + { 101, 2, { 50, 51 }, sha224Residue101 }, + { 101, 3, { 50, 0, 51 }, sha224Residue101 }, +}; + +static void ResidueMsg(uint8_t *msg, size_t sz) +{ + size_t i; + + for (i = 0; i < sz; i++) + msg[i] = (uint8_t)(i * 7 + 3); +} + +static void Test_Sha224_StreamingResidue(void) +{ + uint8_t msg[101]; + size_t i; + size_t j; + + ResidueMsg(msg, sizeof(msg)); + + for (i = 0; i < sizeof(sha224ResidueCases) / + sizeof(sha224ResidueCases[0]); i++) { + uint8_t digest[28] = {0}; + uint8_t oneshot[28] = {0}; + size_t off = 0; + + WHAL_ASSERT_EQ(whal_Sha224_Start(BOARD_SHA224_DEV), + WHAL_SUCCESS); + for (j = 0; j < sha224ResidueCases[i].chunkCnt; j++) { + size_t chunk = sha224ResidueCases[i].chunks[j]; + + WHAL_ASSERT_EQ(whal_Sha224_Process(BOARD_SHA224_DEV, + msg + off, chunk), + WHAL_SUCCESS); + off += chunk; + } + WHAL_ASSERT_EQ(whal_Sha224_Finalize(BOARD_SHA224_DEV, digest, + sizeof(digest)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(digest, sha224ResidueCases[i].digest, + sizeof(digest)); + + WHAL_ASSERT_EQ(whal_Sha224_Oneshot(BOARD_SHA224_DEV, msg, + sha224ResidueCases[i].msgSz, + oneshot, sizeof(oneshot)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(oneshot, digest, sizeof(digest)); + } +} + void whal_Test_Sha224(void) { WHAL_TEST_SUITE_START("sha224"); WHAL_TEST(Test_Sha224_KnownAnswer); WHAL_TEST(Test_Sha224_Streaming); + WHAL_TEST(Test_Sha224_StreamingResidue); WHAL_TEST_SUITE_END(); } diff --git a/tests/crypto/test_sha256.c b/tests/crypto/test_sha256.c index 7286792..e1a5639 100644 --- a/tests/crypto/test_sha256.c +++ b/tests/crypto/test_sha256.c @@ -90,10 +90,109 @@ static void Test_Sha256_Streaming(void) WHAL_ASSERT_MEM_EQ(digest, sha256StreamDigest, sizeof(sha256StreamDigest)); } +/* Vectors generated by tests/crypto/gen_test_vectors.py over the first n bytes + * of the 101-byte message ResidueMsg() builds. */ +static const uint8_t sha256Residue0[32] = { + 0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14, + 0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24, + 0x27, 0xAE, 0x41, 0xE4, 0x64, 0x9B, 0x93, 0x4C, + 0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55, +}; + +static const uint8_t sha256Residue1[32] = { + 0x08, 0x4F, 0xED, 0x08, 0xB9, 0x78, 0xAF, 0x4D, + 0x7D, 0x19, 0x6A, 0x74, 0x46, 0xA8, 0x6B, 0x58, + 0x00, 0x9E, 0x63, 0x6B, 0x61, 0x1D, 0xB1, 0x62, + 0x11, 0xB6, 0x5A, 0x9A, 0xAD, 0xFF, 0x29, 0xC5, +}; + +static const uint8_t sha256Residue3[32] = { + 0x6A, 0xB0, 0xDB, 0xA1, 0xF4, 0xF1, 0xDF, 0xBB, + 0x37, 0xB4, 0xF9, 0xEE, 0xB0, 0x92, 0xC0, 0x9F, + 0xCA, 0x49, 0x00, 0xAD, 0x32, 0xBD, 0xCD, 0x14, + 0x7D, 0x8D, 0xDE, 0x35, 0xD6, 0xC8, 0x7C, 0x35, +}; + +static const uint8_t sha256Residue7[32] = { + 0xAB, 0x76, 0x4D, 0xB4, 0x6A, 0x4B, 0x50, 0x4F, + 0x03, 0x46, 0x19, 0xA5, 0x14, 0x75, 0xE9, 0x00, + 0x36, 0x6A, 0x04, 0x73, 0x86, 0x93, 0xB1, 0x3A, + 0x74, 0x28, 0x45, 0x93, 0x0D, 0xAF, 0x74, 0xE4, +}; + +static const uint8_t sha256Residue101[32] = { + 0x48, 0x71, 0x9C, 0xF7, 0x31, 0x25, 0xF9, 0x24, + 0x29, 0x4A, 0x14, 0x0D, 0xE9, 0xD4, 0x1E, 0xFE, + 0x9F, 0xDA, 0xD8, 0xE5, 0x20, 0xC5, 0x5C, 0x54, + 0x11, 0x27, 0xEA, 0x70, 0x9E, 0xF0, 0x55, 0x4E, +}; + +static const struct { + size_t msgSz; + size_t chunkCnt; + size_t chunks[5]; + const uint8_t *digest; +} sha256ResidueCases[] = { + { 0, 0, { 0 }, sha256Residue0 }, + { 0, 1, { 0 }, sha256Residue0 }, + { 1, 1, { 1 }, sha256Residue1 }, + { 3, 1, { 3 }, sha256Residue3 }, + { 7, 5, { 1, 1, 1, 1, 3 }, sha256Residue7 }, + { 101, 2, { 50, 51 }, sha256Residue101 }, + { 101, 3, { 50, 0, 51 }, sha256Residue101 }, +}; + +static void ResidueMsg(uint8_t *msg, size_t sz) +{ + size_t i; + + for (i = 0; i < sz; i++) + msg[i] = (uint8_t)(i * 7 + 3); +} + +static void Test_Sha256_StreamingResidue(void) +{ + uint8_t msg[101]; + size_t i; + size_t j; + + ResidueMsg(msg, sizeof(msg)); + + for (i = 0; i < sizeof(sha256ResidueCases) / + sizeof(sha256ResidueCases[0]); i++) { + uint8_t digest[32] = {0}; + uint8_t oneshot[32] = {0}; + size_t off = 0; + + WHAL_ASSERT_EQ(whal_Sha256_Start(BOARD_SHA256_DEV), + WHAL_SUCCESS); + for (j = 0; j < sha256ResidueCases[i].chunkCnt; j++) { + size_t chunk = sha256ResidueCases[i].chunks[j]; + + WHAL_ASSERT_EQ(whal_Sha256_Process(BOARD_SHA256_DEV, + msg + off, chunk), + WHAL_SUCCESS); + off += chunk; + } + WHAL_ASSERT_EQ(whal_Sha256_Finalize(BOARD_SHA256_DEV, digest, + sizeof(digest)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(digest, sha256ResidueCases[i].digest, + sizeof(digest)); + + WHAL_ASSERT_EQ(whal_Sha256_Oneshot(BOARD_SHA256_DEV, msg, + sha256ResidueCases[i].msgSz, + oneshot, sizeof(oneshot)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(oneshot, digest, sizeof(digest)); + } +} + void whal_Test_Sha256(void) { WHAL_TEST_SUITE_START("sha256"); WHAL_TEST(Test_Sha256_KnownAnswer); WHAL_TEST(Test_Sha256_Streaming); + WHAL_TEST(Test_Sha256_StreamingResidue); WHAL_TEST_SUITE_END(); } diff --git a/tests/crypto/test_stm32wba_hmac_sha256.c b/tests/crypto/test_stm32wba_hmac_sha256.c new file mode 100644 index 0000000..a43f0a0 --- /dev/null +++ b/tests/crypto/test_stm32wba_hmac_sha256.c @@ -0,0 +1,120 @@ +/* test_stm32wba_hmac_sha256.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHAL. + * + * wolfHAL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHAL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include +#include "wolfHAL_board.h" +#include "test.h" + +#define HASH_CR_REG 0x00 +#define HASH_CR_INIT_Msk (1UL << 2) +#define HASH_SR_REG 0x24 +#define HASH_SR_DINIS_Msk (1UL << 0) +#define HASH_HR_REG(x) (0x310 + (x) * 4) +#define HASH_CSR_REG(x) (0xF8 + (x) * 4) + +/* HASH_CSR38..53 hold the extra context saved only for HMAC (RM0493 27.4.8) */ +#define HASH_CSR_HMAC_FIRST 38 +#define HASH_CSR_COUNT 54 + +static const uint8_t hmacKey[20] = { + 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, + 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, + 0x0B, 0x0B, 0x0B, 0x0B, +}; + +static void ReadCsrs(size_t base, uint32_t *out, size_t first, size_t last) +{ + size_t i; + + for (i = first; i < last; i++) + out[i - first] = (uint32_t)whal_Reg_Read(base, HASH_CSR_REG(i)); +} + +static int AnyNonZero(const uint32_t *words, size_t count) +{ + size_t i; + + for (i = 0; i < count; i++) { + if (words[i] != 0) + return 1; + } + return 0; +} + +static void Test_Hash_InitChangesHmacContext(void) +{ + size_t base = whal_Stm32wba_Hash_Dev.base; + uint32_t before[HASH_CSR_COUNT - HASH_CSR_HMAC_FIRST]; + uint32_t after[HASH_CSR_COUNT - HASH_CSR_HMAC_FIRST]; + + WHAL_ASSERT_EQ(whal_HmacSha256_Start(BOARD_HMAC_SHA256_DEV, + hmacKey, sizeof(hmacKey)), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_Reg_Read(base, HASH_SR_REG) & HASH_SR_DINIS_Msk, + HASH_SR_DINIS_Msk); + ReadCsrs(base, before, HASH_CSR_HMAC_FIRST, HASH_CSR_COUNT); + WHAL_ASSERT_EQ(AnyNonZero(before, sizeof(before) / sizeof(before[0])), 1); + + whal_Reg_Update(base, HASH_CR_REG, HASH_CR_INIT_Msk, HASH_CR_INIT_Msk); + + if ((whal_Reg_Read(base, HASH_SR_REG) & HASH_SR_DINIS_Msk) == 0) + WHAL_SKIP(); + + ReadCsrs(base, after, HASH_CSR_HMAC_FIRST, HASH_CSR_COUNT); + WHAL_ASSERT_MEM_NEQ(after, before, sizeof(before)); +} + +static void Test_Hash_DeinitClearsContext(void) +{ + size_t base = whal_Stm32wba_Hash_Dev.base; + uint32_t csr[HASH_CSR_COUNT]; + size_t i; + + WHAL_ASSERT_EQ(whal_HmacSha256_Start(BOARD_HMAC_SHA256_DEV, + hmacKey, sizeof(hmacKey)), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_Stm32wba_Hash_Deinit( + (whal_Crypto *)&whal_Stm32wba_Hash_Dev), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_Reg_Read(base, HASH_SR_REG) & HASH_SR_DINIS_Msk, 0); + ReadCsrs(base, csr, 0, HASH_CSR_COUNT); + WHAL_ASSERT_EQ(AnyNonZero(csr, HASH_CSR_COUNT), 0); + + for (i = 0; i < 8; i++) + WHAL_ASSERT_EQ(whal_Reg_Read(base, HASH_HR_REG(i)), 0); + + WHAL_ASSERT_EQ(whal_Stm32wba_Hash_Init( + (whal_Crypto *)&whal_Stm32wba_Hash_Dev), + WHAL_SUCCESS); +} + +void whal_Test_HmacSha256_Platform(void) +{ + WHAL_TEST_SUITE_START("hmac_sha256_platform"); + WHAL_TEST(Test_Hash_InitChangesHmacContext); + WHAL_TEST(Test_Hash_DeinitClearsContext); + WHAL_TEST_SUITE_END(); +} diff --git a/tests/main.c b/tests/main.c index 47efc3e..9a2ad30 100644 --- a/tests/main.c +++ b/tests/main.c @@ -127,6 +127,9 @@ void whal_Test_HmacSha224(void); #ifdef WHAL_TEST_ENABLE_HMAC_SHA256 void whal_Test_HmacSha256(void); +#ifdef WHAL_TEST_ENABLE_HMAC_SHA256_PLATFORM +void whal_Test_HmacSha256_Platform(void); +#endif #endif #ifdef WHAL_TEST_ENABLE_BLOCK @@ -326,6 +329,9 @@ void main(void) #ifdef WHAL_TEST_ENABLE_HMAC_SHA256 whal_Test_HmacSha256(); +#ifdef WHAL_TEST_ENABLE_HMAC_SHA256_PLATFORM + whal_Test_HmacSha256_Platform(); +#endif #endif #ifdef WHAL_TEST_ENABLE_BLOCK From c519a1dbcb54c3ea3481d5f77c101d4ee2fcc9ff Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Tue, 13 Oct 2026 13:03:51 -0400 Subject: [PATCH 08/22] [crypto, stm32wb, test] Erase PKA RAM on Deinit, add PKA argument tests - Zero all 894 words of PKA RAM in Deinit before clearing PKA_CR.EN - Add NULL, zero-size, mismatched-size and over-limit tests for every PKA primitive --- src/crypto/stm32wb_pka.c | 12 +++ tests/crypto/test_pka.c | 213 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 225 insertions(+) diff --git a/src/crypto/stm32wb_pka.c b/src/crypto/stm32wb_pka.c index c15f5ee..be09346 100644 --- a/src/crypto/stm32wb_pka.c +++ b/src/crypto/stm32wb_pka.c @@ -100,6 +100,9 @@ const whal_Crypto whal_Stm32wb_Pka_Dev = WHAL_CFG_STM32WB_PKA_DEV; #define PKA_MODE_ECDSA_VERIFY 0x26 #define PKA_MODE_ECC_POINT_CHECK 0x28 +#define PKA_RAM_REG 0x400 +#define PKA_RAM_SIZE 3576 + /* ---- PKA RAM operand offsets (from PKA base address) ---- */ /* MODE 0x00 — Modular exponentiation (normal) */ @@ -211,6 +214,14 @@ static void ZeroOperand(size_t base, size_t offset, size_t dataSz) whal_Reg_Write(base, offset + i * 4, 0); } +static void ZeroRam(size_t base) +{ + size_t off; + + for (off = 0; off < PKA_RAM_SIZE; off += 4) + whal_Reg_Write(base, PKA_RAM_REG + off, 0); +} + static whal_Error WaitForProcEnd(size_t base, whal_Timeout *timeout) { const uint32_t errFlags = PKA_SR_ADDRERRF_Msk | PKA_SR_RAMERRF_Msk; @@ -251,6 +262,7 @@ whal_Error whal_Stm32wb_Pka_Deinit(whal_Crypto *dev) { size_t base = whal_Stm32wb_Pka_Dev.base; (void)dev; + ZeroRam(base); whal_Reg_Update(base, PKA_CR_REG, PKA_CR_EN_Msk, 0); return WHAL_SUCCESS; } diff --git a/tests/crypto/test_pka.c b/tests/crypto/test_pka.c index c7b6618..851e622 100644 --- a/tests/crypto/test_pka.c +++ b/tests/crypto/test_pka.c @@ -372,6 +372,213 @@ static void Test_Pka_IntSub_KnownAnswer(void) WHAL_ASSERT_MEM_EQ(out, expected, sizeof(expected)); } +/* Zeroed operands one byte past the 3136-bit (392-byte) hardware limit */ +static uint8_t g_oversize[2 * 393]; + +static void Test_Pka_ModExp_BadArgs(void) +{ + uint8_t out[128]; + + WHAL_ASSERT_EQ(whal_Pka_ModExp(NULL, sizeof(testInput), + rsaE, sizeof(rsaE), + rsaN, sizeof(rsaN), + out, sizeof(out)), + WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Pka_ModExp(testInput, sizeof(testInput), + rsaE, sizeof(rsaE), + rsaN, 0, + out, sizeof(out)), + WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Pka_ModExp(testInput, sizeof(testInput), + rsaE, sizeof(rsaE), + rsaN, sizeof(rsaN), + NULL, sizeof(out)), + WHAL_EINVAL); + + WHAL_ASSERT_EQ(whal_Pka_ModExp(testInput, 64, + rsaE, sizeof(rsaE), + rsaN, sizeof(rsaN), + out, sizeof(out)), + WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_Pka_ModExp(testInput, sizeof(testInput), + rsaE, 64, + rsaN, sizeof(rsaN), + out, sizeof(out)), + WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_Pka_ModExp(testInput, sizeof(testInput), + rsaE, sizeof(rsaE), + rsaN, sizeof(rsaN), + out, 64), + WHAL_ENOTSUP); + + WHAL_ASSERT_EQ(whal_Pka_ModExp(g_oversize, 393, + g_oversize, 393, + g_oversize, 393, + g_oversize, 393), + WHAL_ENOTSUP); +} + +static void Test_Pka_ModInv_BadArgs(void) +{ + uint8_t out[128]; + + WHAL_ASSERT_EQ(whal_Pka_ModInv(NULL, sizeof(testInput), + rsaN, sizeof(rsaN), + out, sizeof(out)), + WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Pka_ModInv(testInput, sizeof(testInput), + rsaN, sizeof(rsaN), + out, 0), + WHAL_EINVAL); + + WHAL_ASSERT_EQ(whal_Pka_ModInv(testInput, 64, + rsaN, sizeof(rsaN), + out, sizeof(out)), + WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_Pka_ModInv(testInput, sizeof(testInput), + rsaN, sizeof(rsaN), + out, 64), + WHAL_ENOTSUP); + + WHAL_ASSERT_EQ(whal_Pka_ModInv(g_oversize, 393, + g_oversize, 393, + g_oversize, 393), + WHAL_ENOTSUP); +} + +static void Test_Pka_ModReduce_BadArgs(void) +{ + uint8_t out[128]; + + WHAL_ASSERT_EQ(whal_Pka_ModReduce(testInput, sizeof(testInput), + NULL, sizeof(rsaN), + out, sizeof(out)), + WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Pka_ModReduce(testInput, 0, + rsaN, sizeof(rsaN), + out, sizeof(out)), + WHAL_EINVAL); + + WHAL_ASSERT_EQ(whal_Pka_ModReduce(testInput, 64, + rsaN, sizeof(rsaN), + out, sizeof(out)), + WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_Pka_ModReduce(testInput, sizeof(testInput), + rsaN, sizeof(rsaN), + out, 64), + WHAL_ENOTSUP); + + WHAL_ASSERT_EQ(whal_Pka_ModReduce(g_oversize, 393, + g_oversize, 393, + g_oversize, 393), + WHAL_ENOTSUP); +} + +static void Test_Pka_IntMul_BadArgs(void) +{ + uint8_t out[128]; + + WHAL_ASSERT_EQ(whal_Pka_IntMul(rsaP, sizeof(rsaP), + NULL, sizeof(rsaQ), + out, sizeof(out)), + WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Pka_IntMul(rsaP, sizeof(rsaP), + rsaQ, 0, + out, sizeof(out)), + WHAL_EINVAL); + + WHAL_ASSERT_EQ(whal_Pka_IntMul(rsaP, sizeof(rsaP), + rsaQ, 32, + out, sizeof(out)), + WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_Pka_IntMul(rsaP, sizeof(rsaP), + rsaQ, sizeof(rsaQ), + out, sizeof(rsaP)), + WHAL_ENOTSUP); + + WHAL_ASSERT_EQ(whal_Pka_IntMul(g_oversize, 393, + g_oversize, 393, + g_oversize, 2 * 393), + WHAL_ENOTSUP); +} + +static void Test_Pka_IntSub_BadArgs(void) +{ + uint8_t out[64]; + + WHAL_ASSERT_EQ(whal_Pka_IntSub(rsaP, sizeof(rsaP), + rsaQ, sizeof(rsaQ), + NULL, sizeof(out)), + WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Pka_IntSub(rsaP, 0, + rsaQ, sizeof(rsaQ), + out, sizeof(out)), + WHAL_EINVAL); + + WHAL_ASSERT_EQ(whal_Pka_IntSub(rsaP, sizeof(rsaP), + rsaQ, 32, + out, sizeof(out)), + WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_Pka_IntSub(rsaP, sizeof(rsaP), + rsaQ, sizeof(rsaQ), + out, 32), + WHAL_ENOTSUP); + + WHAL_ASSERT_EQ(whal_Pka_IntSub(g_oversize, 393, + g_oversize, 393, + g_oversize, 393), + WHAL_ENOTSUP); +} + +static void Test_Pka_RsaCrtExp_BadArgs(void) +{ + uint8_t out[128]; + + WHAL_ASSERT_EQ(whal_Pka_RsaCrtExp(testInput, sizeof(testInput), + rsaDP, sizeof(rsaDP), + rsaDQ, sizeof(rsaDQ), + NULL, sizeof(rsaQInv), + rsaP, sizeof(rsaP), + rsaQ, sizeof(rsaQ), + out, sizeof(out)), + WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Pka_RsaCrtExp(testInput, sizeof(testInput), + rsaDP, sizeof(rsaDP), + rsaDQ, sizeof(rsaDQ), + rsaQInv, sizeof(rsaQInv), + rsaP, 0, + rsaQ, sizeof(rsaQ), + out, sizeof(out)), + WHAL_EINVAL); + + WHAL_ASSERT_EQ(whal_Pka_RsaCrtExp(testInput, sizeof(testInput), + rsaDP, 32, + rsaDQ, sizeof(rsaDQ), + rsaQInv, sizeof(rsaQInv), + rsaP, sizeof(rsaP), + rsaQ, sizeof(rsaQ), + out, sizeof(out)), + WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_Pka_RsaCrtExp(testInput, sizeof(testInput), + rsaDP, sizeof(rsaDP), + rsaDQ, sizeof(rsaDQ), + rsaQInv, sizeof(rsaQInv), + rsaP, sizeof(rsaP), + rsaQ, sizeof(rsaQ), + out, 64), + WHAL_ENOTSUP); + + /* p and q of 197 bytes each put the modulus at 394 bytes */ + WHAL_ASSERT_EQ(whal_Pka_RsaCrtExp(g_oversize, 394, + g_oversize, 197, + g_oversize, 197, + g_oversize, 197, + g_oversize, 197, + g_oversize, 197, + g_oversize, 394), + WHAL_ENOTSUP); +} + void whal_Test_Pka(void) { WHAL_TEST_SUITE_START("pka"); @@ -385,5 +592,11 @@ void whal_Test_Pka(void) WHAL_TEST(Test_Pka_IntMul_KnownAnswer); WHAL_TEST(Test_Pka_ModReduce_KnownAnswer); WHAL_TEST(Test_Pka_IntSub_KnownAnswer); + WHAL_TEST(Test_Pka_ModExp_BadArgs); + WHAL_TEST(Test_Pka_ModInv_BadArgs); + WHAL_TEST(Test_Pka_ModReduce_BadArgs); + WHAL_TEST(Test_Pka_IntMul_BadArgs); + WHAL_TEST(Test_Pka_IntSub_BadArgs); + WHAL_TEST(Test_Pka_RsaCrtExp_BadArgs); WHAL_TEST_SUITE_END(); } From 23633856502dc642201db62356ed8ff2fab5999a Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Tue, 13 Oct 2026 13:07:19 -0400 Subject: [PATCH 09/22] [rng, test] Test RNG Generate with lengths that end mid-sample - Generate 1, 3, 5 and 7 bytes into a 0xA5-filled buffer and check no byte outside the request changes --- tests/rng/test_rng.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/rng/test_rng.c b/tests/rng/test_rng.c index ccd3f47..63ba8ff 100644 --- a/tests/rng/test_rng.c +++ b/tests/rng/test_rng.c @@ -66,11 +66,32 @@ static void Test_Rng_GenerateUnique(void) WHAL_ASSERT_EQ(same, 0); } +static void Test_Rng_GenerateOddLength(void) +{ + static const size_t lens[] = { 1, 3, 5, 7 }; + const size_t pre = 4; + uint8_t buf[16]; + + for (size_t i = 0; i < sizeof(lens) / sizeof(lens[0]); i++) { + for (size_t j = 0; j < sizeof(buf); j++) + buf[j] = 0xA5; + + WHAL_ASSERT_EQ(whal_Rng_Generate(BOARD_RNG_DEV, buf + pre, lens[i]), + WHAL_SUCCESS); + + for (size_t j = 0; j < sizeof(buf); j++) { + if (j < pre || j >= pre + lens[i]) + WHAL_ASSERT_EQ(buf[j], 0xA5); + } + } +} + void whal_Test_Rng(void) { WHAL_TEST_SUITE_START("rng"); WHAL_TEST(Test_Rng_Api); WHAL_TEST(Test_Rng_GenerateNonzero); WHAL_TEST(Test_Rng_GenerateUnique); + WHAL_TEST(Test_Rng_GenerateOddLength); WHAL_TEST_SUITE_END(); } From 1a396d76b233322de7eef8fe43200688a310e4f5 Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Tue, 13 Oct 2026 13:12:05 -0400 Subject: [PATCH 10/22] [flash, stm32l1, stm32wb0] Verify L1 unlock, lock WB0 with PAGEPROT, document no-op locks - Return WHAL_EHARDWARE from L1 Unlock if PELOCK or PRGLOCK stays set after the key sequences - Lock WB0 flash with a PAGEPROT segment covering every page; Unlock clears both PAGEPROT registers - Document that Lock and Unlock are driver-specific and may return success without applying protection --- src/flash/stm32l1_flash.c | 5 +++++ src/flash/stm32wb0_flash.c | 22 ++++++++++++++++++++-- wolfHAL/flash/flash.h | 14 ++++++++++---- wolfHAL/flash/stm32wb0_flash.h | 8 ++++++-- 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/flash/stm32l1_flash.c b/src/flash/stm32l1_flash.c index 3b25d25..9889adf 100644 --- a/src/flash/stm32l1_flash.c +++ b/src/flash/stm32l1_flash.c @@ -166,7 +166,12 @@ whal_Error whal_Stm32l1_Flash_Unlock(whal_Flash *flashDev, size_t addr, size_t l /* Unlock PECR then program memory */ UnlockPecr(base); + if (whal_Reg_Read(base, FLASH_PECR_REG) & FLASH_PECR_PELOCK_Msk) + return WHAL_EHARDWARE; + UnlockProgram(base); + if (whal_Reg_Read(base, FLASH_PECR_REG) & FLASH_PECR_PRGLOCK_Msk) + return WHAL_EHARDWARE; return WHAL_SUCCESS; } diff --git a/src/flash/stm32wb0_flash.c b/src/flash/stm32wb0_flash.c index f8e076e..04b2818 100644 --- a/src/flash/stm32wb0_flash.c +++ b/src/flash/stm32wb0_flash.c @@ -69,6 +69,14 @@ const whal_Flash whal_Stm32wb0_Flash_Dev = WHAL_CFG_STM32WB0_FLASH_DEV; #define FLASH_IRQ_ALL_ERR_Msk (FLASH_IRQ_CMDERR_Msk | FLASH_IRQ_ILLCMD_Msk) #define FLASH_ADDRESS_REG 0x18 + +/* PAGEPROTx: two write-protect segments per register (RM0529 9.4.9) */ +#define FLASH_PAGEPROT0_REG 0x34 +#define FLASH_PAGEPROT1_REG 0x38 +#define FLASH_PAGEPROT_SEG0_SIZE_Pos 0 +#define FLASH_PAGEPROT_SEG0_SIZE_Msk (0x7FUL << FLASH_PAGEPROT_SEG0_SIZE_Pos) +#define FLASH_PAGEPROT_SEG0_OFFSET_Pos 8 +#define FLASH_PAGEPROT_SEG0_OFFSET_Msk (0x7FUL << FLASH_PAGEPROT_SEG0_OFFSET_Pos) #define FLASH_DATA0_REG 0x40 #define FLASH_DATA1_REG 0x44 #define FLASH_DATA2_REG 0x48 @@ -175,20 +183,30 @@ whal_Error whal_Stm32wb0_Flash_Deinit(whal_Flash *flashDev) whal_Error whal_Stm32wb0_Flash_Lock(whal_Flash *flashDev, size_t addr, size_t len) { + size_t base = whal_Stm32wb0_Flash_Dev.base; (void)flashDev; (void)addr; (void)len; - /* The WB0 flash controller has no software lock register — all - * writes are gated only by the per-page PAGEPROT bits. */ + + /* A segment from page 0 with the largest SIZE ends at the last page */ + whal_Reg_Write(base, FLASH_PAGEPROT0_REG, + whal_SetBits(FLASH_PAGEPROT_SEG0_OFFSET_Msk, + FLASH_PAGEPROT_SEG0_OFFSET_Pos, 0) | + whal_SetBits(FLASH_PAGEPROT_SEG0_SIZE_Msk, + FLASH_PAGEPROT_SEG0_SIZE_Pos, 0x7F)); return WHAL_SUCCESS; } whal_Error whal_Stm32wb0_Flash_Unlock(whal_Flash *flashDev, size_t addr, size_t len) { + size_t base = whal_Stm32wb0_Flash_Dev.base; (void)flashDev; (void)addr; (void)len; + + whal_Reg_Write(base, FLASH_PAGEPROT0_REG, 0); + whal_Reg_Write(base, FLASH_PAGEPROT1_REG, 0); return WHAL_SUCCESS; } diff --git a/wolfHAL/flash/flash.h b/wolfHAL/flash/flash.h index 5302cda..3183753 100644 --- a/wolfHAL/flash/flash.h +++ b/wolfHAL/flash/flash.h @@ -85,13 +85,16 @@ whal_Error whal_Flash_Deinit(whal_Flash *flashDev); /* * @brief Lock flash to prevent modification. * - * Current drivers lock the whole device and ignore @p addr and @p len. + * Behavior is driver-specific. Drivers that implement locking protect the + * whole device and ignore @p addr and @p len. Drivers without a lock + * mechanism return WHAL_SUCCESS without applying any protection; see the + * driver header. * * @param flashDev Flash instance to lock. * @param addr Byte address of the region to lock (driver-defined). * @param len Number of bytes to lock (driver-defined). * - * @retval WHAL_SUCCESS Lock applied. + * @retval WHAL_SUCCESS Lock applied, or the driver has no lock mechanism. * @retval WHAL_EINVAL Null pointer. * @retval WHAL_ENOTSUP Operation not implemented by this driver. */ @@ -99,13 +102,16 @@ whal_Error whal_Flash_Lock(whal_Flash *flashDev, size_t addr, size_t len); /* * @brief Unlock flash to allow modification. * - * Current drivers unlock the whole device and ignore @p addr and @p len. + * Behavior is driver-specific. Drivers that implement locking unlock the + * whole device and ignore @p addr and @p len. Drivers without a lock + * mechanism return WHAL_SUCCESS without changing anything; see the driver + * header. * * @param flashDev Flash instance to unlock. * @param addr Byte address of the region to unlock (driver-defined). * @param len Number of bytes to unlock (driver-defined). * - * @retval WHAL_SUCCESS Unlock applied. + * @retval WHAL_SUCCESS Unlock applied, or the driver has no lock mechanism. * @retval WHAL_EINVAL Null pointer. * @retval WHAL_ENOTSUP Operation not implemented by this driver. */ diff --git a/wolfHAL/flash/stm32wb0_flash.h b/wolfHAL/flash/stm32wb0_flash.h index 70a5152..b634cbc 100644 --- a/wolfHAL/flash/stm32wb0_flash.h +++ b/wolfHAL/flash/stm32wb0_flash.h @@ -101,7 +101,9 @@ whal_Error whal_Stm32wb0_Flash_Init(whal_Flash *flashDev); whal_Error whal_Stm32wb0_Flash_Deinit(whal_Flash *flashDev); /** - * @brief No-op lock (WB0 controller has no software lock register). + * @brief Write-protect the whole flash with a PAGEPROT segment. + * + * Ignores @p addr and @p len. * * @retval WHAL_SUCCESS Always. */ @@ -109,7 +111,9 @@ whal_Error whal_Stm32wb0_Flash_Lock(whal_Flash *flashDev, size_t addr, size_t len); /** - * @brief No-op unlock (WB0 controller has no KEYR unlock sequence). + * @brief Clear every PAGEPROT segment so the whole flash is writable. + * + * Ignores @p addr and @p len. * * @retval WHAL_SUCCESS Always. */ From 8e0691384fcb42f23cf3e846dfa32689f0b2a3a3 Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Tue, 13 Oct 2026 13:32:09 -0400 Subject: [PATCH 11/22] [flash, stm32f4, stm32h5, stm32f0, stm32wb, stm32c0, test] Fix flash geometry and CFGBSY cleanup, add erase extent tests - Encode F4 sectors 12 and up into the 5-bit dual-bank SNB field - Derive H5 sectors per bank from the configured flash size instead of assuming 2 MB - Add pageSize to the F0/F3 flash config and use it for erase; set 2 KB on the F091 and F302R8 boards - Skip FLASH_CR writes in WB and C0 cleanup while CFGBSY is set; reject CFGBSY and clear stale PG/PER on entry - Test multi-sector erase extents on every flash device and a one-byte-past-sector erase on on-chip flash - Add a PIC32CZ platform test for single-doubleword and mixed single/quad programming --- boards/stm32f091rc_nucleo/wolfHAL_board.h | 1 + boards/stm32f302r8_nucleo/wolfHAL_board.h | 1 + src/flash/stm32c0_flash.c | 12 ++- src/flash/stm32f0_flash.c | 11 ++- src/flash/stm32f4_flash.c | 11 ++- src/flash/stm32h5_flash.c | 12 +-- src/flash/stm32wb_flash.c | 12 ++- tests/flash/test_flash.c | 111 +++++++++++++++++++++- tests/flash/test_pic32cz_flash.c | 85 +++++++++++++++++ wolfHAL/flash/stm32f0_flash.h | 1 + 10 files changed, 236 insertions(+), 21 deletions(-) create mode 100644 tests/flash/test_pic32cz_flash.c diff --git a/boards/stm32f091rc_nucleo/wolfHAL_board.h b/boards/stm32f091rc_nucleo/wolfHAL_board.h index bedc7c8..9bf4a73 100644 --- a/boards/stm32f091rc_nucleo/wolfHAL_board.h +++ b/boards/stm32f091rc_nucleo/wolfHAL_board.h @@ -92,6 +92,7 @@ enum { .cfg = (void *)&(const whal_Stm32f0_Flash_Cfg){ \ .startAddr = 0x08000000, \ .size = 0x40000, \ + .pageSize = 0x800, \ .timeout = &g_whalTimeout, \ }, \ } diff --git a/boards/stm32f302r8_nucleo/wolfHAL_board.h b/boards/stm32f302r8_nucleo/wolfHAL_board.h index 3b37980..b1dd663 100644 --- a/boards/stm32f302r8_nucleo/wolfHAL_board.h +++ b/boards/stm32f302r8_nucleo/wolfHAL_board.h @@ -94,6 +94,7 @@ enum { .cfg = (void *)&(const whal_Stm32f3_Flash_Cfg){ \ .startAddr = 0x08000000, \ .size = 0x10000, \ + .pageSize = 0x800, \ .timeout = &g_whalTimeout, \ }, \ } diff --git a/src/flash/stm32c0_flash.c b/src/flash/stm32c0_flash.c index 08a8fcf..a83db46 100644 --- a/src/flash/stm32c0_flash.c +++ b/src/flash/stm32c0_flash.c @@ -226,10 +226,13 @@ static whal_Error whal_Stm32c0_Flash_WriteOrErase(whal_Flash *flashDev, size_t a /* Check if flash is busy */ whal_Reg_Get(base, FLASH_SR_REG, FLASH_SR_BSY_Msk, FLASH_SR_BSY_Pos, &bsy); - if (bsy) { + if (bsy || (whal_Reg_Read(base, FLASH_SR_REG) & FLASH_SR_CFGBSY_Msk)) { return WHAL_ENOTREADY; } + /* Drop PG/PER left set by an earlier operation that timed out */ + whal_Reg_Update(base, FLASH_CR_REG, FLASH_CR_PG_Msk | FLASH_CR_PER_Msk, 0); + /* Clear all error flags by writing 1 to each */ whal_Reg_Update(base, FLASH_SR_REG, FLASH_SR_ALL_ERR, 0xffffffff); @@ -301,9 +304,10 @@ static whal_Error whal_Stm32c0_Flash_WriteOrErase(whal_Flash *flashDev, size_t a } cleanup: - /* Clear programming and erase mode bits */ - whal_Reg_Update(base, FLASH_CR_REG, - FLASH_CR_PG_Msk | FLASH_CR_PER_Msk, 0); + /* FLASH_CR must not be written while CFGBSY is set */ + if (!(whal_Reg_Read(base, FLASH_SR_REG) & FLASH_SR_CFGBSY_Msk)) + whal_Reg_Update(base, FLASH_CR_REG, + FLASH_CR_PG_Msk | FLASH_CR_PER_Msk, 0); return err; } diff --git a/src/flash/stm32f0_flash.c b/src/flash/stm32f0_flash.c index ae82cb4..527a3d5 100644 --- a/src/flash/stm32f0_flash.c +++ b/src/flash/stm32f0_flash.c @@ -163,6 +163,9 @@ static whal_Error whal_Stm32f0_Flash_WriteOrErase(whal_Flash *flashDev, if (!write && dataSz == 0) return WHAL_SUCCESS; + if (!write && cfg->pageSize == 0) + return WHAL_EINVAL; + whal_Reg_Get(base, FLASH_SR_REG, FLASH_SR_BSY_Msk, FLASH_SR_BSY_Pos, &bsy); if (bsy) @@ -198,9 +201,9 @@ static whal_Error whal_Stm32f0_Flash_WriteOrErase(whal_Flash *flashDev, } } } else { - /* Calculate page range (2 KB per page) */ - size_t startPage = (addr - cfg->startAddr) >> 11; - size_t endPage = ((addr - cfg->startAddr) + dataSz - 1) >> 11; + size_t startPage = (addr - cfg->startAddr) / cfg->pageSize; + size_t endPage = ((addr - cfg->startAddr) + dataSz - 1) / + cfg->pageSize; for (size_t page = startPage; page <= endPage; ++page) { /* Enable page erase */ @@ -209,7 +212,7 @@ static whal_Error whal_Stm32f0_Flash_WriteOrErase(whal_Flash *flashDev, /* Write page start address to AR */ whal_Reg_Write(base, FLASH_AR_REG, - cfg->startAddr + (page << 11)); + cfg->startAddr + page * cfg->pageSize); /* Start erase */ whal_Reg_Update(base, FLASH_CR_REG, FLASH_CR_STRT_Msk, diff --git a/src/flash/stm32f4_flash.c b/src/flash/stm32f4_flash.c index de4ab05..e27071f 100644 --- a/src/flash/stm32f4_flash.c +++ b/src/flash/stm32f4_flash.c @@ -87,7 +87,7 @@ const whal_Flash whal_Stm32f4_Flash_Dev = WHAL_CFG_STM32F4_FLASH_DEV; #define FLASH_CR_SER_Msk (1UL << FLASH_CR_SER_Pos) #define FLASH_CR_SNB_Pos 3 -#define FLASH_CR_SNB_Msk (WHAL_BITMASK(4) << FLASH_CR_SNB_Pos) +#define FLASH_CR_SNB_Msk (WHAL_BITMASK(5) << FLASH_CR_SNB_Pos) #define FLASH_CR_PSIZE_Pos 8 #define FLASH_CR_PSIZE_Msk (WHAL_BITMASK(2) << FLASH_CR_PSIZE_Pos) @@ -243,6 +243,12 @@ whal_Error whal_Stm32f4_Flash_Write(whal_Flash *flashDev, size_t addr, return err; } +/* Dual-bank parts encode sectors 12 and up as 0b1xxxx (RM0090 FLASH_CR.SNB) */ +static size_t SectorNumber(size_t sector) +{ + return (sector < 12) ? sector : sector + 4; +} + whal_Error whal_Stm32f4_Flash_Erase(whal_Flash *flashDev, size_t addr, size_t dataSz) { @@ -281,7 +287,8 @@ whal_Error whal_Stm32f4_Flash_Erase(whal_Flash *flashDev, size_t addr, whal_Reg_Update(base, FLASH_CR_REG, FLASH_CR_SER_Msk | FLASH_CR_SNB_Msk | FLASH_CR_PSIZE_Msk, whal_SetBits(FLASH_CR_SER_Msk, FLASH_CR_SER_Pos, 1) | - whal_SetBits(FLASH_CR_SNB_Msk, FLASH_CR_SNB_Pos, s) | + whal_SetBits(FLASH_CR_SNB_Msk, FLASH_CR_SNB_Pos, + SectorNumber(s)) | whal_SetBits(FLASH_CR_PSIZE_Msk, FLASH_CR_PSIZE_Pos, FLASH_PSIZE_WORD)); /* Start erase */ diff --git a/src/flash/stm32h5_flash.c b/src/flash/stm32h5_flash.c index de92890..839c581 100644 --- a/src/flash/stm32h5_flash.c +++ b/src/flash/stm32h5_flash.c @@ -128,11 +128,6 @@ const whal_Flash whal_Stm32h5_Flash_Dev = WHAL_CFG_STM32H5_FLASH_DEV; #define FLASH_SECTOR_SIZE 0x2000 #define FLASH_SECTOR_SHIFT 13 -/* Sectors per bank */ -#define FLASH_SECTORS_PER_BANK 128 - -/* Bank size: 1 MB */ -#define FLASH_BANK_SIZE (FLASH_SECTORS_PER_BANK * FLASH_SECTOR_SIZE) #ifdef WHAL_CFG_STM32H5_FLASH_DIRECT_API_MAPPING #define whal_Stm32h5_Flash_Init whal_Flash_Init @@ -295,6 +290,7 @@ whal_Error whal_Stm32h5_Flash_Erase(whal_Flash *flashDev, size_t addr, whal_Error err; size_t offset; size_t startSector, endSector; + size_t sectorsPerBank; (void)flashDev; if (dataSz == 0) @@ -312,13 +308,15 @@ whal_Error whal_Stm32h5_Flash_Erase(whal_Flash *flashDev, size_t addr, /* Clear all error and status flags */ whal_Reg_Write(base, FLASH_NSCCR_REG, FLASH_NSCCR_CLR_ALL); + /* Every STM32H5 is dual-bank with the flash split evenly between banks */ + sectorsPerBank = (cfg->size / 2) >> FLASH_SECTOR_SHIFT; offset = addr - cfg->startAddr; startSector = offset >> FLASH_SECTOR_SHIFT; endSector = (offset + dataSz - 1) >> FLASH_SECTOR_SHIFT; for (size_t sector = startSector; sector <= endSector; sector++) { - size_t bank = (sector >= FLASH_SECTORS_PER_BANK) ? 1 : 0; - size_t sectorInBank = sector - (bank * FLASH_SECTORS_PER_BANK); + size_t bank = (sector >= sectorsPerBank) ? 1 : 0; + size_t sectorInBank = sector - (bank * sectorsPerBank); /* Configure and start sector erase in one write */ whal_Reg_Update(base, FLASH_NSCR_REG, diff --git a/src/flash/stm32wb_flash.c b/src/flash/stm32wb_flash.c index a25b0fd..56de0c1 100644 --- a/src/flash/stm32wb_flash.c +++ b/src/flash/stm32wb_flash.c @@ -229,10 +229,14 @@ static whal_Error whal_Stm32wb_Flash_WriteOrErase(whal_Flash *flashDev, size_t a whal_Reg_Get(base, FLASH_SR_REG, FLASH_SR_BSY_Msk, FLASH_SR_BSY_Pos, &bsy); whal_Reg_Get(base, FLASH_SR_REG, FLASH_SR_PESD_Msk, FLASH_SR_PESD_Pos, &pesd); - if (bsy || pesd) { + if (bsy || pesd || + (whal_Reg_Read(base, FLASH_SR_REG) & FLASH_SR_CFGBSY_Msk)) { return WHAL_ENOTREADY; } + /* Drop PG/PER left set by an earlier operation that timed out */ + whal_Reg_Update(base, FLASH_CR_REG, FLASH_CR_PG_Msk | FLASH_CR_PER_Msk, 0); + /* Clear all error flags by writing 1 to each */ whal_Reg_Update(base, FLASH_SR_REG, FLASH_SR_ALL_ERR, 0xffffffff); @@ -300,8 +304,10 @@ static whal_Error whal_Stm32wb_Flash_WriteOrErase(whal_Flash *flashDev, size_t a } cleanup: - /* Disable flash programming and page erase modes */ - whal_Reg_Update(base, FLASH_CR_REG, FLASH_CR_PG_Msk | FLASH_CR_PER_Msk, 0); + /* FLASH_CR must not be written while CFGBSY is set */ + if (!(whal_Reg_Read(base, FLASH_SR_REG) & FLASH_SR_CFGBSY_Msk)) + whal_Reg_Update(base, FLASH_CR_REG, + FLASH_CR_PG_Msk | FLASH_CR_PER_Msk, 0); return err; } diff --git a/tests/flash/test_flash.c b/tests/flash/test_flash.c index 18de868..bf8050e 100644 --- a/tests/flash/test_flash.c +++ b/tests/flash/test_flash.c @@ -26,9 +26,17 @@ #include "wolfHAL_peripheral.h" static whal_Flash *g_testFlashDev; +static size_t g_testFlashStart; static size_t g_testFlashAddr; static size_t g_testFlashSectorSz; +static const uint8_t g_extentPattern[32] = { + 0x5A, 0xA5, 0x3C, 0xC3, 0x69, 0x96, 0x0F, 0xF0, + 0x5A, 0xA5, 0x3C, 0xC3, 0x69, 0x96, 0x0F, 0xF0, + 0x5A, 0xA5, 0x3C, 0xC3, 0x69, 0x96, 0x0F, 0xF0, + 0x5A, 0xA5, 0x3C, 0xC3, 0x69, 0x96, 0x0F, 0xF0, +}; + static void Test_Flash_Api(void) { WHAL_ASSERT_EQ(whal_Flash_Read(g_testFlashDev, 0, NULL, 8), WHAL_EINVAL); @@ -78,6 +86,102 @@ static void Test_Flash_WriteReadErase(void) g_testFlashSectorSz), WHAL_SUCCESS); } +static whal_Error WriteExtentPattern(size_t addr) +{ + whal_Error err; + + do { + err = whal_Flash_Write(g_testFlashDev, addr, g_extentPattern, + sizeof(g_extentPattern)); + } while (err == WHAL_ENOTREADY); + + return err; +} + +static int HasExtentPattern(size_t addr) +{ + uint8_t readback[sizeof(g_extentPattern)]; + size_t i; + + if (whal_Flash_Read(g_testFlashDev, addr, readback, + sizeof(readback)) != WHAL_SUCCESS) + return -1; + + for (i = 0; i < sizeof(readback); i++) { + if (readback[i] != g_extentPattern[i]) + return 0; + } + return 1; +} + +/* Three sectors ending at the test sector, or starting at it if the test + * sector is too close to the start of the device */ +static size_t ExtentBase(void) +{ + if (g_testFlashAddr - g_testFlashStart >= 2 * g_testFlashSectorSz) + return g_testFlashAddr - 2 * g_testFlashSectorSz; + return g_testFlashAddr; +} + +static void Test_Flash_EraseExtent(void) +{ + const size_t sz = g_testFlashSectorSz; + const size_t base = ExtentBase(); + + WHAL_ASSERT_EQ(whal_Flash_Unlock(g_testFlashDev, base, 3 * sz), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Erase(g_testFlashDev, base, 3 * sz), + WHAL_SUCCESS); + + /* Two whole sectors: both erased, the third untouched */ + WHAL_ASSERT_EQ(WriteExtentPattern(base), WHAL_SUCCESS); + WHAL_ASSERT_EQ(WriteExtentPattern(base + sz), WHAL_SUCCESS); + WHAL_ASSERT_EQ(WriteExtentPattern(base + 2 * sz), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Erase(g_testFlashDev, base, 2 * sz), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(HasExtentPattern(base), 0); + WHAL_ASSERT_EQ(HasExtentPattern(base + sz), 0); + WHAL_ASSERT_EQ(HasExtentPattern(base + 2 * sz), 1); + + /* Exactly one sector: the next sector is untouched */ + WHAL_ASSERT_EQ(WriteExtentPattern(base), WHAL_SUCCESS); + WHAL_ASSERT_EQ(WriteExtentPattern(base + sz), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Erase(g_testFlashDev, base, sz), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(HasExtentPattern(base), 0); + WHAL_ASSERT_EQ(HasExtentPattern(base + sz), 1); + + WHAL_ASSERT_EQ(whal_Flash_Erase(g_testFlashDev, base, 3 * sz), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Lock(g_testFlashDev, base, 3 * sz), + WHAL_SUCCESS); +} + +static void Test_Flash_EraseOneBytePastSector(void) +{ + const size_t sz = g_testFlashSectorSz; + const size_t base = ExtentBase(); + + WHAL_ASSERT_EQ(whal_Flash_Unlock(g_testFlashDev, base, 3 * sz), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Erase(g_testFlashDev, base, 3 * sz), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(WriteExtentPattern(base), WHAL_SUCCESS); + WHAL_ASSERT_EQ(WriteExtentPattern(base + sz), WHAL_SUCCESS); + WHAL_ASSERT_EQ(WriteExtentPattern(base + 2 * sz), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Erase(g_testFlashDev, base, sz + 1), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(HasExtentPattern(base), 0); + WHAL_ASSERT_EQ(HasExtentPattern(base + sz), 0); + WHAL_ASSERT_EQ(HasExtentPattern(base + 2 * sz), 1); + + WHAL_ASSERT_EQ(whal_Flash_Erase(g_testFlashDev, base, 3 * sz), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Lock(g_testFlashDev, base, 3 * sz), + WHAL_SUCCESS); +} + static void Test_Flash_OutOfBounds(void) { const size_t start = BOARD_FLASH_START_ADDR; @@ -125,8 +229,11 @@ static void run_flash_tests(const char *name, int onChip) whal_Test_Printf(" device: %s\n", name); WHAL_TEST(Test_Flash_Api); WHAL_TEST(Test_Flash_WriteReadErase); - if (onChip) + WHAL_TEST(Test_Flash_EraseExtent); + if (onChip) { + WHAL_TEST(Test_Flash_EraseOneBytePastSector); WHAL_TEST(Test_Flash_OutOfBounds); + } WHAL_TEST_SUITE_END(); } @@ -134,6 +241,7 @@ void whal_Test_Flash(void) { /* Test on-chip flash */ g_testFlashDev = BOARD_FLASH_DEV; + g_testFlashStart = BOARD_FLASH_START_ADDR; g_testFlashAddr = BOARD_FLASH_TEST_ADDR; g_testFlashSectorSz = BOARD_FLASH_SECTOR_SZ; run_flash_tests("on-chip", 1); @@ -141,6 +249,7 @@ void whal_Test_Flash(void) /* Test peripheral flash devices */ for (size_t i = 0; g_peripheralFlash[i].dev; i++) { g_testFlashDev = g_peripheralFlash[i].dev; + g_testFlashStart = 0; g_testFlashAddr = 0; g_testFlashSectorSz = g_peripheralFlash[i].sectorSz; run_flash_tests(g_peripheralFlash[i].name, 0); diff --git a/tests/flash/test_pic32cz_flash.c b/tests/flash/test_pic32cz_flash.c new file mode 100644 index 0000000..0ec52c5 --- /dev/null +++ b/tests/flash/test_pic32cz_flash.c @@ -0,0 +1,85 @@ +/* test_pic32cz_flash.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHAL. + * + * wolfHAL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHAL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include "wolfHAL_board.h" +#include "test.h" + +/* Each case gets its own 128-byte slot in the erased test sector */ +#define SLOT_SZ 128 +#define ERASED_BYTE 0xFF + +static void Test_Flash_DoublewordWrites(void) +{ + static const size_t offsets[] = { 0, 8, 16, 24 }; + static const size_t lens[] = { 8, 16, 24, 32, 40, 64 }; + uint8_t pattern[64]; + uint8_t readback[SLOT_SZ]; + size_t slot = 0; + size_t i; + size_t j; + size_t k; + + for (i = 0; i < sizeof(pattern); i++) + pattern[i] = (uint8_t)(i * 7 + 3); + + WHAL_ASSERT_EQ(whal_Flash_Unlock(BOARD_FLASH_DEV, BOARD_FLASH_TEST_ADDR, + BOARD_FLASH_SECTOR_SZ), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Erase(BOARD_FLASH_DEV, BOARD_FLASH_TEST_ADDR, + BOARD_FLASH_SECTOR_SZ), + WHAL_SUCCESS); + + for (i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) { + for (j = 0; j < sizeof(lens) / sizeof(lens[0]); j++, slot++) { + size_t slotAddr = BOARD_FLASH_TEST_ADDR + slot * SLOT_SZ; + + WHAL_ASSERT_EQ(whal_Flash_Write(BOARD_FLASH_DEV, + slotAddr + offsets[i], + pattern, lens[j]), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Read(BOARD_FLASH_DEV, slotAddr, + readback, sizeof(readback)), + WHAL_SUCCESS); + + for (k = 0; k < sizeof(readback); k++) { + if (k < offsets[i] || k >= offsets[i] + lens[j]) + WHAL_ASSERT_EQ(readback[k], ERASED_BYTE); + } + WHAL_ASSERT_MEM_EQ(readback + offsets[i], pattern, lens[j]); + } + } + + WHAL_ASSERT_EQ(whal_Flash_Erase(BOARD_FLASH_DEV, BOARD_FLASH_TEST_ADDR, + BOARD_FLASH_SECTOR_SZ), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Lock(BOARD_FLASH_DEV, BOARD_FLASH_TEST_ADDR, + BOARD_FLASH_SECTOR_SZ), + WHAL_SUCCESS); +} + +void whal_Test_Flash_Platform(void) +{ + WHAL_TEST_SUITE_START("flash_platform"); + WHAL_TEST(Test_Flash_DoublewordWrites); + WHAL_TEST_SUITE_END(); +} diff --git a/wolfHAL/flash/stm32f0_flash.h b/wolfHAL/flash/stm32f0_flash.h index ab8aa4f..6518d24 100644 --- a/wolfHAL/flash/stm32f0_flash.h +++ b/wolfHAL/flash/stm32f0_flash.h @@ -42,6 +42,7 @@ typedef struct whal_Stm32f0_Flash_Cfg { size_t startAddr; /* Flash region start address */ size_t size; /* Flash region size in bytes */ + size_t pageSize; /* Erase page size in bytes (1 KB or 2 KB) */ whal_Timeout *timeout; /* Optional timeout for poll loops */ } whal_Stm32f0_Flash_Cfg; From c249433ded2a1b39f6b35180fffe5c26504696d2 Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Wed, 14 Oct 2026 11:18:52 -0400 Subject: [PATCH 12/22] [flash, block, test] Require power-of-two SPI-NOR pages, add page-crossing and SD extent tests - SPI-NOR Init rejects a pageSz that is not a power of two, since the write path splits pages with a mask - Add pageSz to the peripheral flash config; set 256 on the W25Q64 - Add a SPI-NOR cross-page write test - Add SD block tests for erase extent and high block addresses --- boards/peripheral/wolfHAL_peripheral.c | 1 + boards/peripheral/wolfHAL_peripheral.h | 1 + src/flash/spi_nor_flash.c | 4 +- tests/block/test_block.c | 103 +++++++++++++++++++++++++ tests/flash/test_flash.c | 51 ++++++++++++ wolfHAL/flash/spi_nor_flash.h | 2 +- 6 files changed, 160 insertions(+), 2 deletions(-) diff --git a/boards/peripheral/wolfHAL_peripheral.c b/boards/peripheral/wolfHAL_peripheral.c index 30523b4..124f38c 100644 --- a/boards/peripheral/wolfHAL_peripheral.c +++ b/boards/peripheral/wolfHAL_peripheral.c @@ -53,6 +53,7 @@ whal_PeripheralFlash_Cfg g_peripheralFlash[] = { .name = "spi_nor_w25q64", .dev = &g_whalSpiNorW25q64, .sectorSz = 4096, + .pageSz = 256, }, #endif {0}, /* sentinel */ diff --git a/boards/peripheral/wolfHAL_peripheral.h b/boards/peripheral/wolfHAL_peripheral.h index 550e3aa..21e9701 100644 --- a/boards/peripheral/wolfHAL_peripheral.h +++ b/boards/peripheral/wolfHAL_peripheral.h @@ -44,6 +44,7 @@ typedef struct { const char *name; whal_Flash *dev; size_t sectorSz; /* Sector (erase) size in bytes */ + size_t pageSz; /* Program page size in bytes */ } whal_PeripheralFlash_Cfg; /* Peripheral sensor device test configuration */ diff --git a/src/flash/spi_nor_flash.c b/src/flash/spi_nor_flash.c index 8df7aeb..0715fde 100644 --- a/src/flash/spi_nor_flash.c +++ b/src/flash/spi_nor_flash.c @@ -190,7 +190,9 @@ whal_Error whal_SpiNor_Init(whal_Flash *flashDev) if (!cfg->spiDev || !cfg->spiComCfg || !cfg->gpioDev) return WHAL_EINVAL; - if (cfg->pageSz == 0 || cfg->capacity == 0) + /* Writes split pages with a mask, so pageSz must be a power of two */ + if (cfg->pageSz == 0 || (cfg->pageSz & (cfg->pageSz - 1)) != 0 || + cfg->capacity == 0) return WHAL_EINVAL; /* Ensure CS is deasserted */ diff --git a/tests/block/test_block.c b/tests/block/test_block.c index 9c59a04..3afe5f2 100644 --- a/tests/block/test_block.c +++ b/tests/block/test_block.c @@ -102,6 +102,107 @@ static void Test_Block_MultiWriteRead(void) } } +/* Distinct nonzero address bytes catch a swapped or dropped byte in the + * 32-bit command argument */ +#define HIGH_BLOCK 0x00010203u + +static void FillBlocks(uint8_t seed, size_t blocks) +{ + size_t i; + + for (i = 0; i < blocks * g_testBlockSz; i++) + g_testBuf[i] = (uint8_t)(seed + i * 7); +} + +static int CheckBlocks(uint8_t seed, size_t blocks) +{ + size_t i; + + for (i = 0; i < blocks * g_testBlockSz; i++) { + if (g_testBuf[i] != (uint8_t)(seed + i * 7)) + return 0; + } + return 1; +} + +static int CheckErased(size_t blocks) +{ + size_t i; + + for (i = 0; i < blocks * g_testBlockSz; i++) { + if (g_testBuf[i] != g_testErasedByte) + return 0; + } + return 1; +} + +static void Test_Block_EraseExtent(void) +{ + /* Erase blocks 0-1: both erased, block 2 untouched */ + FillBlocks(0x20, 1); + WHAL_ASSERT_EQ(whal_Block_Write(g_testBlockDev, 2, g_testBuf, 1), + WHAL_SUCCESS); + FillBlocks(0x10, 2); + WHAL_ASSERT_EQ(whal_Block_Write(g_testBlockDev, 0, g_testBuf, 2), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_Block_Erase(g_testBlockDev, 0, 2), WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_Block_Read(g_testBlockDev, 0, g_testBuf, 2), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(CheckErased(2), 1); + WHAL_ASSERT_EQ(whal_Block_Read(g_testBlockDev, 2, g_testBuf, 1), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(CheckBlocks(0x20, 1), 1); + + /* Erase block 1 only: blocks 0 and 2 untouched */ + FillBlocks(0x30, 1); + WHAL_ASSERT_EQ(whal_Block_Write(g_testBlockDev, 0, g_testBuf, 1), + WHAL_SUCCESS); + FillBlocks(0x40, 1); + WHAL_ASSERT_EQ(whal_Block_Write(g_testBlockDev, 1, g_testBuf, 1), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_Block_Erase(g_testBlockDev, 1, 1), WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_Block_Read(g_testBlockDev, 0, g_testBuf, 1), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(CheckBlocks(0x30, 1), 1); + WHAL_ASSERT_EQ(whal_Block_Read(g_testBlockDev, 1, g_testBuf, 1), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(CheckErased(1), 1); + WHAL_ASSERT_EQ(whal_Block_Read(g_testBlockDev, 2, g_testBuf, 1), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(CheckBlocks(0x20, 1), 1); +} + +static void Test_Block_HighAddress(void) +{ + /* Single-block write and read */ + FillBlocks(0x50, 1); + WHAL_ASSERT_EQ(whal_Block_Write(g_testBlockDev, HIGH_BLOCK, g_testBuf, 1), + WHAL_SUCCESS); + FillBlocks(0x00, 1); + WHAL_ASSERT_EQ(whal_Block_Read(g_testBlockDev, HIGH_BLOCK, g_testBuf, 1), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(CheckBlocks(0x50, 1), 1); + + /* Multi-block write and read */ + FillBlocks(0x60, 2); + WHAL_ASSERT_EQ(whal_Block_Write(g_testBlockDev, HIGH_BLOCK, g_testBuf, 2), + WHAL_SUCCESS); + FillBlocks(0x00, 2); + WHAL_ASSERT_EQ(whal_Block_Read(g_testBlockDev, HIGH_BLOCK, g_testBuf, 2), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(CheckBlocks(0x60, 2), 1); + + WHAL_ASSERT_EQ(whal_Block_Erase(g_testBlockDev, HIGH_BLOCK, 2), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Block_Read(g_testBlockDev, HIGH_BLOCK, g_testBuf, 2), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(CheckErased(2), 1); +} + static void Test_Block_Api(void) { WHAL_ASSERT_EQ(whal_Block_Read(g_testBlockDev, 0, NULL, 1), WHAL_EINVAL); @@ -117,6 +218,8 @@ static void run_block_tests(const char *name) WHAL_TEST(Test_Block_EraseBlank); WHAL_TEST(Test_Block_WriteRead); WHAL_TEST(Test_Block_MultiWriteRead); + WHAL_TEST(Test_Block_EraseExtent); + WHAL_TEST(Test_Block_HighAddress); WHAL_TEST_SUITE_END(); } diff --git a/tests/flash/test_flash.c b/tests/flash/test_flash.c index bf8050e..2957812 100644 --- a/tests/flash/test_flash.c +++ b/tests/flash/test_flash.c @@ -29,6 +29,7 @@ static whal_Flash *g_testFlashDev; static size_t g_testFlashStart; static size_t g_testFlashAddr; static size_t g_testFlashSectorSz; +static size_t g_testFlashPageSz; static const uint8_t g_extentPattern[32] = { 0x5A, 0xA5, 0x3C, 0xC3, 0x69, 0x96, 0x0F, 0xF0, @@ -182,6 +183,53 @@ static void Test_Flash_EraseOneBytePastSector(void) WHAL_SUCCESS); } +static void Test_Flash_CrossPageWrite(void) +{ + static uint8_t pattern[300]; + static uint8_t readback[300]; + const size_t page = g_testFlashPageSz; + const struct { + size_t offset; + size_t len; + } cases[] = { + { 100, 300 }, /* crosses the first page boundary */ + { 2 * page - 8, page + 8 }, /* starts 8 bytes before a boundary */ + { 3 * page, page }, /* exactly one aligned page */ + }; + size_t i; + size_t j; + whal_Error err; + + for (i = 0; i < sizeof(pattern); i++) + pattern[i] = (uint8_t)(i * 7 + 3); + + WHAL_ASSERT_EQ(whal_Flash_Unlock(g_testFlashDev, g_testFlashAddr, + g_testFlashSectorSz), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Erase(g_testFlashDev, g_testFlashAddr, + g_testFlashSectorSz), WHAL_SUCCESS); + + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + size_t addr = g_testFlashAddr + cases[i].offset; + + do { + err = whal_Flash_Write(g_testFlashDev, addr, pattern, + cases[i].len); + } while (err == WHAL_ENOTREADY); + WHAL_ASSERT_EQ(err, WHAL_SUCCESS); + + for (j = 0; j < cases[i].len; j++) + readback[j] = 0; + WHAL_ASSERT_EQ(whal_Flash_Read(g_testFlashDev, addr, readback, + cases[i].len), WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(readback, pattern, cases[i].len); + } + + WHAL_ASSERT_EQ(whal_Flash_Erase(g_testFlashDev, g_testFlashAddr, + g_testFlashSectorSz), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Lock(g_testFlashDev, g_testFlashAddr, + g_testFlashSectorSz), WHAL_SUCCESS); +} + static void Test_Flash_OutOfBounds(void) { const size_t start = BOARD_FLASH_START_ADDR; @@ -233,6 +281,8 @@ static void run_flash_tests(const char *name, int onChip) if (onChip) { WHAL_TEST(Test_Flash_EraseOneBytePastSector); WHAL_TEST(Test_Flash_OutOfBounds); + } else { + WHAL_TEST(Test_Flash_CrossPageWrite); } WHAL_TEST_SUITE_END(); } @@ -252,6 +302,7 @@ void whal_Test_Flash(void) g_testFlashStart = 0; g_testFlashAddr = 0; g_testFlashSectorSz = g_peripheralFlash[i].sectorSz; + g_testFlashPageSz = g_peripheralFlash[i].pageSz; run_flash_tests(g_peripheralFlash[i].name, 0); } } diff --git a/wolfHAL/flash/spi_nor_flash.h b/wolfHAL/flash/spi_nor_flash.h index cd90515..5ef9ccf 100644 --- a/wolfHAL/flash/spi_nor_flash.h +++ b/wolfHAL/flash/spi_nor_flash.h @@ -65,7 +65,7 @@ typedef struct whal_SpiNor_Cfg { whal_Gpio *gpioDev; /* GPIO device for chip select */ size_t csPin; /* GPIO pin index for chip select */ whal_Timeout *timeout; - size_t pageSz; /* Page program size in bytes (typically 256) */ + size_t pageSz; /* Page program size in bytes, a power of two */ size_t capacity; /* Total flash capacity in bytes */ } whal_SpiNor_Cfg; From 8d9acefeebc8e4e8418c6ee69dbd9f26af407bad Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Wed, 14 Oct 2026 11:46:35 -0400 Subject: [PATCH 13/22] [dma, uart, stm32wb, stm32wba, test] Fix DMA error dispatch and teardown, add WB DMA tests - WB DMA IRQ checks TEIF first and reports one error when TCIF and TEIF are both pending - GPDMA Deinit aborts each enabled channel (suspend, idle wait, reset), since RESET has no effect on a running channel; the abort sequence is shared with Stop - GPDMA Configure rejects an unknown direction - WBA UART-DMA Deinit runs every teardown step and returns the first error; blocking Send and Recv return a DMA stop failure - Document that a one-byte WBA UART-DMA send is copied to an internal buffer - Add a WB55 DMA platform test for CCR/CPAR/CMAR/CNDTR/DMAMUX composition per direction, mem-to-mem transfers, and TC/TE IRQ dispatch --- boards/stm32wb55xx_nucleo/wolfHAL_board.h | 3 + src/dma/stm32wb_dma.c | 14 +- src/dma/stm32wba_gpdma.c | 51 ++-- src/uart/stm32wba_uart_dma.c | 25 +- tests/dma/test_stm32wb_dma.c | 290 ++++++++++++++++++++++ wolfHAL/uart/stm32wba_uart_dma.h | 7 +- 6 files changed, 359 insertions(+), 31 deletions(-) create mode 100644 tests/dma/test_stm32wb_dma.c diff --git a/boards/stm32wb55xx_nucleo/wolfHAL_board.h b/boards/stm32wb55xx_nucleo/wolfHAL_board.h index 91444c8..35217f6 100644 --- a/boards/stm32wb55xx_nucleo/wolfHAL_board.h +++ b/boards/stm32wb55xx_nucleo/wolfHAL_board.h @@ -32,6 +32,9 @@ extern whal_Spi g_whalSpi; extern whal_Crypto g_whalCrypto; extern whal_I2c g_whalI2c; extern whal_Pwm g_whalPwm; +#ifdef BOARD_DMA +extern whal_Dma g_whalDma1; +#endif extern whal_Timeout g_whalTimeout; extern volatile uint32_t g_tick; diff --git a/src/dma/stm32wb_dma.c b/src/dma/stm32wb_dma.c index f6d4ad5..ecc7c05 100644 --- a/src/dma/stm32wb_dma.c +++ b/src/dma/stm32wb_dma.c @@ -418,16 +418,18 @@ void whal_Stm32wb_Dma_IRQHandler(whal_Dma *dmaDev, size_t ch, isr = whal_Reg_Read(base, DMA_ISR_REG); - if (isr & (1UL << DMA_ISR_TCIF_Pos(hw_ch))) { - whal_Reg_Write(base, DMA_IFCR_REG, DMA_IFCR_CTCIF(hw_ch)); + if (isr & (1UL << DMA_ISR_TEIF_Pos(hw_ch))) { + whal_Reg_Write(base, DMA_IFCR_REG, + DMA_IFCR_CTEIF(hw_ch) | DMA_IFCR_CTCIF(hw_ch)); if (cb) - cb(ctx, WHAL_SUCCESS); + cb(ctx, WHAL_EHARDWARE); + return; } - if (isr & (1UL << DMA_ISR_TEIF_Pos(hw_ch))) { - whal_Reg_Write(base, DMA_IFCR_REG, DMA_IFCR_CTEIF(hw_ch)); + if (isr & (1UL << DMA_ISR_TCIF_Pos(hw_ch))) { + whal_Reg_Write(base, DMA_IFCR_REG, DMA_IFCR_CTCIF(hw_ch)); if (cb) - cb(ctx, WHAL_EHARDWARE); + cb(ctx, WHAL_SUCCESS); } } diff --git a/src/dma/stm32wba_gpdma.c b/src/dma/stm32wba_gpdma.c index 9aabedc..5ccad64 100644 --- a/src/dma/stm32wba_gpdma.c +++ b/src/dma/stm32wba_gpdma.c @@ -140,6 +140,27 @@ const whal_Dma whal_Stm32wba_Gpdma_Dev = WHAL_CFG_STM32WBA_GPDMA_DEV; #endif +/* Abort an enabled channel: suspend, wait for idle, then reset */ +static whal_Error Gpdma_AbortChannel(size_t base, size_t ch, + whal_Timeout *timeout) +{ + size_t cr; + whal_Error err; + + cr = whal_Reg_Read(base, GPDMA_CxCR(ch)); + if (cr & GPDMA_CxCR_EN_Msk) { + whal_Reg_Write(base, GPDMA_CxCR(ch), + (cr & ~GPDMA_CxCR_EN_Msk) | GPDMA_CxCR_SUSP_Msk); + err = whal_Reg_ReadPoll(base, GPDMA_CxSR(ch), GPDMA_CxSR_IDLEF_Msk, + GPDMA_CxSR_IDLEF_Msk, timeout); + if (err) + return err; + whal_Reg_Write(base, GPDMA_CxCR(ch), GPDMA_CxCR_RESET_Msk); + } + + return WHAL_SUCCESS; +} + whal_Error whal_Stm32wba_Gpdma_Init(whal_Dma *dmaDev) { #ifdef WHAL_CFG_STM32WBA_GPDMA_SINGLE_INSTANCE @@ -154,6 +175,8 @@ whal_Error whal_Stm32wba_Gpdma_Init(whal_Dma *dmaDev) whal_Error whal_Stm32wba_Gpdma_Deinit(whal_Dma *dmaDev) { + whal_Error err = WHAL_SUCCESS; + whal_Error abortErr; #ifdef WHAL_CFG_STM32WBA_GPDMA_SINGLE_INSTANCE const whal_Stm32wba_Gpdma_Cfg *cfg = (const whal_Stm32wba_Gpdma_Cfg *)whal_Stm32wba_Gpdma_Dev.cfg; @@ -170,13 +193,15 @@ whal_Error whal_Stm32wba_Gpdma_Deinit(whal_Dma *dmaDev) base = dmaDev->base; #endif - /* Reset all channels */ for (uint8_t ch = 0; ch < cfg->numChannels; ch++) { + abortErr = Gpdma_AbortChannel(base, ch, cfg->timeout); + if (abortErr && !err) + err = abortErr; whal_Reg_Write(base, GPDMA_CxCR(ch), GPDMA_CxCR_RESET_Msk); } - return WHAL_SUCCESS; + return err; } whal_Error whal_Stm32wba_Gpdma_Configure(whal_Dma *dmaDev, size_t ch, @@ -211,6 +236,11 @@ whal_Error whal_Stm32wba_Gpdma_Configure(whal_Dma *dmaDev, size_t ch, if (ccfg->nbytes == 0 || ccfg->nbytes > 0xFFFF) return WHAL_EINVAL; + if (ccfg->dir != WHAL_STM32WBA_GPDMA_DIR_PERIPH_TO_MEM && + ccfg->dir != WHAL_STM32WBA_GPDMA_DIR_MEM_TO_PERIPH && + ccfg->dir != WHAL_STM32WBA_GPDMA_DIR_MEM_TO_MEM) + return WHAL_EINVAL; + /* Reset the channel and wait for idle */ whal_Reg_Write(base, GPDMA_CxCR(ch), GPDMA_CxCR_RESET_Msk); err = whal_Reg_ReadPoll(base, GPDMA_CxSR(ch), GPDMA_CxSR_IDLEF_Msk, @@ -300,7 +330,6 @@ whal_Error whal_Stm32wba_Gpdma_Start(whal_Dma *dmaDev, size_t ch) whal_Error whal_Stm32wba_Gpdma_Stop(whal_Dma *dmaDev, size_t ch) { - size_t cr; whal_Error err; #ifdef WHAL_CFG_STM32WBA_GPDMA_SINGLE_INSTANCE const whal_Stm32wba_Gpdma_Cfg *cfg = @@ -321,18 +350,10 @@ whal_Error whal_Stm32wba_Gpdma_Stop(whal_Dma *dmaDev, size_t ch) if (ch >= cfg->numChannels) return WHAL_EINVAL; - /* EN cannot be cleared by software; abort by suspending, waiting for - * idle, then resetting the channel (RM0493 17.4.4) */ - cr = whal_Reg_Read(base, GPDMA_CxCR(ch)); - if (cr & GPDMA_CxCR_EN_Msk) { - whal_Reg_Write(base, GPDMA_CxCR(ch), - (cr & ~GPDMA_CxCR_EN_Msk) | GPDMA_CxCR_SUSP_Msk); - err = whal_Reg_ReadPoll(base, GPDMA_CxSR(ch), GPDMA_CxSR_IDLEF_Msk, - GPDMA_CxSR_IDLEF_Msk, cfg->timeout); - if (err) - return err; - whal_Reg_Write(base, GPDMA_CxCR(ch), GPDMA_CxCR_RESET_Msk); - } + /* EN cannot be cleared by software (RM0493 17.4.4) */ + err = Gpdma_AbortChannel(base, ch, cfg->timeout); + if (err) + return err; whal_Reg_Write(base, GPDMA_CxFCR(ch), GPDMA_CxFCR_ALL); diff --git a/src/uart/stm32wba_uart_dma.c b/src/uart/stm32wba_uart_dma.c index bd26458..8fff2f5 100644 --- a/src/uart/stm32wba_uart_dma.c +++ b/src/uart/stm32wba_uart_dma.c @@ -191,6 +191,7 @@ whal_Error whal_Stm32wba_UartDma_Send(whal_Uart *uartDev, const void *data, whal_Stm32wba_UartDma_Cfg *cfg; size_t base; whal_Error err; + whal_Error stopErr; err = whal_Stm32wba_UartDma_SendAsync(uartDev, data, dataSz); if (err || dataSz == 0) @@ -224,7 +225,9 @@ whal_Error whal_Stm32wba_UartDma_Send(whal_Uart *uartDev, const void *data, cfg->base.timeout); cleanup: - whal_Dma_Stop(cfg->dma, cfg->txCh); + stopErr = whal_Dma_Stop(cfg->dma, cfg->txCh); + if (stopErr) + err = stopErr; whal_Reg_Update(base, UART_CR3_REG, UART_CR3_DMAT_Msk, 0); cfg->txResult = err; @@ -237,6 +240,7 @@ whal_Error whal_Stm32wba_UartDma_Recv(whal_Uart *uartDev, void *data, whal_Stm32wba_UartDma_Cfg *cfg; size_t base; whal_Error err; + whal_Error stopErr; err = whal_Stm32wba_UartDma_RecvAsync(uartDev, data, dataSz); if (err || dataSz == 0) @@ -261,7 +265,9 @@ whal_Error whal_Stm32wba_UartDma_Recv(whal_Uart *uartDev, void *data, err = cfg->rxResult; cleanup: - whal_Dma_Stop(cfg->dma, cfg->rxCh); + stopErr = whal_Dma_Stop(cfg->dma, cfg->rxCh); + if (stopErr) + err = stopErr; whal_Reg_Update(base, UART_CR3_REG, UART_CR3_DMAR_Msk, 0); cfg->rxResult = err; @@ -273,6 +279,7 @@ whal_Error whal_Stm32wba_UartDma_Deinit(whal_Uart *uartDev) whal_Stm32wba_UartDma_Cfg *cfg; size_t base; whal_Error err; + whal_Error stepErr; #ifdef WHAL_CFG_STM32WBA_UART_DMA_SINGLE_INSTANCE cfg = (whal_Stm32wba_UartDma_Cfg *)whal_Stm32wba_UartDma_Dev.cfg; @@ -289,15 +296,17 @@ whal_Error whal_Stm32wba_UartDma_Deinit(whal_Uart *uartDev) whal_Reg_Update(base, UART_CR3_REG, UART_CR3_DMAT_Msk | UART_CR3_DMAR_Msk, 0); err = whal_Dma_Stop(cfg->dma, cfg->txCh); - if (err) - return err; - err = whal_Dma_Stop(cfg->dma, cfg->rxCh); - if (err) - return err; + stepErr = whal_Dma_Stop(cfg->dma, cfg->rxCh); + if (stepErr && !err) + err = stepErr; cfg->txResult = WHAL_SUCCESS; cfg->rxResult = WHAL_SUCCESS; - return whal_Stm32wb_Uart_Deinit(uartDev); + stepErr = whal_Stm32wb_Uart_Deinit(uartDev); + if (stepErr && !err) + err = stepErr; + + return err; } void whal_Stm32wba_UartDma_TxCallback(void *ctx, whal_Error err) diff --git a/tests/dma/test_stm32wb_dma.c b/tests/dma/test_stm32wb_dma.c new file mode 100644 index 0000000..cc21a56 --- /dev/null +++ b/tests/dma/test_stm32wb_dma.c @@ -0,0 +1,290 @@ +/* test_stm32wb_dma.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHAL. + * + * wolfHAL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHAL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include "wolfHAL_board.h" +#include "test.h" + +#ifdef BOARD_DMA + +/* Channel 1 is unused by the board and its NVIC line stays disabled */ +#define TEST_CH 0 +#define TEST_HW_CH (TEST_CH + 1) + +#define DMA_ISR 0x00 +#define DMA_CCR (0x08 + 0x14 * (TEST_HW_CH - 1)) +#define DMA_CNDTR (0x0C + 0x14 * (TEST_HW_CH - 1)) +#define DMA_CPAR (0x10 + 0x14 * (TEST_HW_CH - 1)) +#define DMA_CMAR (0x14 + 0x14 * (TEST_HW_CH - 1)) +#define DMAMUX_CCR (0x04 * TEST_CH) + +#define CCR_TCIE (1UL << 1) +#define CCR_TEIE (1UL << 3) +#define CCR_DIR (1UL << 4) +#define CCR_CIRC (1UL << 5) +#define CCR_PINC (1UL << 6) +#define CCR_MINC (1UL << 7) +#define CCR_PSIZE(n) ((uint32_t)(n) << 8) +#define CCR_MSIZE(n) ((uint32_t)(n) << 10) +#define CCR_MEM2MEM (1UL << 14) + +#define ISR_TCIF (1UL << (4 * (TEST_HW_CH - 1) + 1)) +#define ISR_TEIF (1UL << (4 * (TEST_HW_CH - 1) + 3)) + +/* Arbitrary peripheral-side address; Configure only stores it */ +#define FAKE_PERIPH 0x40013828u + +/* Reserved AHB2 address space; a DMA read here raises a transfer error */ +#define RESERVED_ADDR 0x48001400u + +#define XFER_SZ 32 + +static uint8_t g_src[XFER_SZ] __attribute__((aligned(4))); +static uint8_t g_dst[XFER_SZ] __attribute__((aligned(4))); + +typedef struct { + int calls; + whal_Error err; +} CbRecord; + +static void RecordCb(void *ctx, whal_Error err) +{ + CbRecord *rec = (CbRecord *)ctx; + + rec->calls++; + rec->err = err; +} + +static size_t DmaBase(void) +{ + return g_whalDma1.base; +} + +static size_t MuxBase(void) +{ + return ((whal_Stm32wb_Dma_Cfg *)g_whalDma1.cfg)->dmamuxBase; +} + +static void FillBuffers(void) +{ + for (size_t i = 0; i < XFER_SZ; i++) { + g_src[i] = (uint8_t)(i * 7 + 3); + g_dst[i] = 0; + } +} + +static void Test_Dma_PeriphToMem(void) +{ + whal_Stm32wb_Dma_ChCfg cfg = { + .dir = WHAL_STM32WB_DMA_DIR_PERIPH_TO_MEM, + .srcAddr = FAKE_PERIPH, + .dstAddr = (uint32_t)(size_t)g_dst, + .length = 7, + .width = WHAL_STM32WB_DMA_WIDTH_16BIT, + .srcInc = WHAL_STM32WB_DMA_INC_DISABLE, + .dstInc = WHAL_STM32WB_DMA_INC_ENABLE, + .dmamuxReqId = 14, + }; + + WHAL_ASSERT_EQ(whal_Dma_Configure(&g_whalDma1, TEST_CH, &cfg), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_Reg_Read(DmaBase(), DMA_CCR), + CCR_TCIE | CCR_TEIE | CCR_MINC | + CCR_PSIZE(1) | CCR_MSIZE(1)); + WHAL_ASSERT_EQ(whal_Reg_Read(DmaBase(), DMA_CNDTR), 7); + WHAL_ASSERT_EQ(whal_Reg_Read(DmaBase(), DMA_CPAR), FAKE_PERIPH); + WHAL_ASSERT_EQ(whal_Reg_Read(DmaBase(), DMA_CMAR), + (uint32_t)(size_t)g_dst); + WHAL_ASSERT_EQ(whal_Reg_Read(MuxBase(), DMAMUX_CCR) & 0x3F, 14); + + WHAL_ASSERT_EQ(whal_Dma_Stop(&g_whalDma1, TEST_CH), WHAL_SUCCESS); +} + +static void Test_Dma_MemToPeriph(void) +{ + whal_Stm32wb_Dma_ChCfg cfg = { + .dir = WHAL_STM32WB_DMA_DIR_MEM_TO_PERIPH, + .srcAddr = (uint32_t)(size_t)g_src, + .dstAddr = FAKE_PERIPH, + .length = 5, + .width = WHAL_STM32WB_DMA_WIDTH_32BIT, + .srcInc = WHAL_STM32WB_DMA_INC_ENABLE, + .dstInc = WHAL_STM32WB_DMA_INC_DISABLE, + .dmamuxReqId = 15, + .circular = 1, + }; + + WHAL_ASSERT_EQ(whal_Dma_Configure(&g_whalDma1, TEST_CH, &cfg), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_Reg_Read(DmaBase(), DMA_CCR), + CCR_TCIE | CCR_TEIE | CCR_DIR | CCR_CIRC | CCR_MINC | + CCR_PSIZE(2) | CCR_MSIZE(2)); + WHAL_ASSERT_EQ(whal_Reg_Read(DmaBase(), DMA_CNDTR), 5); + WHAL_ASSERT_EQ(whal_Reg_Read(DmaBase(), DMA_CPAR), FAKE_PERIPH); + WHAL_ASSERT_EQ(whal_Reg_Read(DmaBase(), DMA_CMAR), + (uint32_t)(size_t)g_src); + WHAL_ASSERT_EQ(whal_Reg_Read(MuxBase(), DMAMUX_CCR) & 0x3F, 15); + + WHAL_ASSERT_EQ(whal_Dma_Stop(&g_whalDma1, TEST_CH), WHAL_SUCCESS); +} + +static void Test_Dma_MemToMemRegs(void) +{ + whal_Stm32wb_Dma_ChCfg cfg = { + .dir = WHAL_STM32WB_DMA_DIR_MEM_TO_MEM, + .srcAddr = (uint32_t)(size_t)g_src, + .dstAddr = (uint32_t)(size_t)g_dst, + .length = XFER_SZ, + .width = WHAL_STM32WB_DMA_WIDTH_8BIT, + .srcInc = WHAL_STM32WB_DMA_INC_ENABLE, + .dstInc = WHAL_STM32WB_DMA_INC_ENABLE, + }; + + WHAL_ASSERT_EQ(whal_Dma_Configure(&g_whalDma1, TEST_CH, &cfg), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_Reg_Read(DmaBase(), DMA_CCR), + CCR_TCIE | CCR_TEIE | CCR_PINC | CCR_MINC | CCR_MEM2MEM); + WHAL_ASSERT_EQ(whal_Reg_Read(DmaBase(), DMA_CNDTR), XFER_SZ); + WHAL_ASSERT_EQ(whal_Reg_Read(DmaBase(), DMA_CPAR), + (uint32_t)(size_t)g_src); + WHAL_ASSERT_EQ(whal_Reg_Read(DmaBase(), DMA_CMAR), + (uint32_t)(size_t)g_dst); + + WHAL_ASSERT_EQ(whal_Dma_Stop(&g_whalDma1, TEST_CH), WHAL_SUCCESS); +} + +static void Test_Dma_Reject(void) +{ + whal_Stm32wb_Dma_ChCfg cfg = { + .dir = WHAL_STM32WB_DMA_DIR_MEM_TO_MEM, + .srcAddr = (uint32_t)(size_t)g_src, + .dstAddr = (uint32_t)(size_t)g_dst, + .length = XFER_SZ, + }; + size_t numCh = ((whal_Stm32wb_Dma_Cfg *)g_whalDma1.cfg)->numChannels; + + WHAL_ASSERT_EQ(whal_Dma_Configure(&g_whalDma1, numCh, &cfg), + WHAL_EINVAL); + + cfg.dir = (whal_Stm32wb_Dma_Dir)(WHAL_STM32WB_DMA_DIR_MEM_TO_MEM + 1); + WHAL_ASSERT_EQ(whal_Dma_Configure(&g_whalDma1, TEST_CH, &cfg), + WHAL_EINVAL); +} + +static void RunMemToMem(whal_Stm32wb_Dma_Width width, uint16_t items) +{ + whal_Stm32wb_Dma_ChCfg cfg = { + .dir = WHAL_STM32WB_DMA_DIR_MEM_TO_MEM, + .srcAddr = (uint32_t)(size_t)g_src, + .dstAddr = (uint32_t)(size_t)g_dst, + .length = items, + .width = width, + .srcInc = WHAL_STM32WB_DMA_INC_ENABLE, + .dstInc = WHAL_STM32WB_DMA_INC_ENABLE, + }; + CbRecord rec = {0}; + + FillBuffers(); + + WHAL_ASSERT_EQ(whal_Dma_Configure(&g_whalDma1, TEST_CH, &cfg), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Dma_Start(&g_whalDma1, TEST_CH), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Reg_ReadPoll(DmaBase(), DMA_ISR, ISR_TCIF, ISR_TCIF, + &g_whalTimeout), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Reg_Read(DmaBase(), DMA_ISR) & ISR_TEIF, 0); + + whal_Stm32wb_Dma_IRQHandler(&g_whalDma1, TEST_CH, RecordCb, &rec); + WHAL_ASSERT_EQ(rec.calls, 1); + WHAL_ASSERT_EQ(rec.err, WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Reg_Read(DmaBase(), DMA_ISR) & ISR_TCIF, 0); + + WHAL_ASSERT_MEM_EQ(g_dst, g_src, XFER_SZ); + + WHAL_ASSERT_EQ(whal_Dma_Stop(&g_whalDma1, TEST_CH), WHAL_SUCCESS); +} + +static void Test_Dma_MemToMem8(void) +{ + RunMemToMem(WHAL_STM32WB_DMA_WIDTH_8BIT, XFER_SZ); +} + +static void Test_Dma_MemToMem32(void) +{ + RunMemToMem(WHAL_STM32WB_DMA_WIDTH_32BIT, XFER_SZ / 4); +} + +static void Test_Dma_TransferError(void) +{ + whal_Stm32wb_Dma_ChCfg cfg = { + .dir = WHAL_STM32WB_DMA_DIR_MEM_TO_MEM, + .srcAddr = RESERVED_ADDR, + .dstAddr = (uint32_t)(size_t)g_dst, + .length = 4, + .width = WHAL_STM32WB_DMA_WIDTH_32BIT, + .srcInc = WHAL_STM32WB_DMA_INC_ENABLE, + .dstInc = WHAL_STM32WB_DMA_INC_ENABLE, + }; + CbRecord rec = {0}; + + WHAL_ASSERT_EQ(whal_Dma_Configure(&g_whalDma1, TEST_CH, &cfg), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Dma_Start(&g_whalDma1, TEST_CH), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Reg_ReadPoll(DmaBase(), DMA_ISR, ISR_TEIF, ISR_TEIF, + &g_whalTimeout), WHAL_SUCCESS); + + whal_Stm32wb_Dma_IRQHandler(&g_whalDma1, TEST_CH, RecordCb, &rec); + WHAL_ASSERT_EQ(rec.calls, 1); + WHAL_ASSERT_EQ(rec.err, WHAL_EHARDWARE); + WHAL_ASSERT_EQ(whal_Reg_Read(DmaBase(), DMA_ISR) & + (ISR_TEIF | ISR_TCIF), 0); + + WHAL_ASSERT_EQ(whal_Dma_Stop(&g_whalDma1, TEST_CH), WHAL_SUCCESS); +} + +#else + +static void Test_Dma_NoBoardDma(void) +{ + WHAL_SKIP(); +} + +#endif /* BOARD_DMA */ + +void whal_Test_Dma_Platform(void) +{ + WHAL_TEST_SUITE_START("dma_platform"); +#ifdef BOARD_DMA + WHAL_TEST(Test_Dma_PeriphToMem); + WHAL_TEST(Test_Dma_MemToPeriph); + WHAL_TEST(Test_Dma_MemToMemRegs); + WHAL_TEST(Test_Dma_Reject); + WHAL_TEST(Test_Dma_MemToMem8); + WHAL_TEST(Test_Dma_MemToMem32); + WHAL_TEST(Test_Dma_TransferError); +#else + WHAL_TEST(Test_Dma_NoBoardDma); +#endif + WHAL_TEST_SUITE_END(); +} diff --git a/wolfHAL/uart/stm32wba_uart_dma.h b/wolfHAL/uart/stm32wba_uart_dma.h index 7c9b563..b90d354 100644 --- a/wolfHAL/uart/stm32wba_uart_dma.h +++ b/wolfHAL/uart/stm32wba_uart_dma.h @@ -95,7 +95,8 @@ whal_Error whal_Stm32wba_UartDma_Deinit(whal_Uart *uartDev); * @brief Send `dataSz` bytes via GPDMA. Blocks until the transfer completes. * * @param uartDev UART device instance. - * @param data Source buffer (must be DMA-accessible). + * @param data Source buffer. Must be DMA-accessible unless dataSz is 1, + * which is copied into an internal buffer. * @param dataSz Number of bytes to send. * * @retval WHAL_SUCCESS All bytes sent. @@ -126,7 +127,9 @@ whal_Error whal_Stm32wba_UartDma_Recv(whal_Uart *uartDev, void *data, * has been queued; completion is reported via the TX callback. * * @param uartDev UART device instance. - * @param data Source buffer (must be DMA-accessible until completion). + * @param data Source buffer. Must be DMA-accessible and stay valid until + * completion unless dataSz is 1, which is copied into an + * internal buffer before this returns. * @param dataSz Number of bytes to send. * * @retval WHAL_SUCCESS Transfer started. From 141b8e748d39bef6256a40e37bd092cd6a871d74 Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Wed, 14 Oct 2026 12:33:04 -0400 Subject: [PATCH 14/22] [uart, i2c, stm32wb, stm32f0, stm32f4, stm32l1, pic32cz, test] Report UART line errors, validate I2C StartCom, add I2C timing tests - Polled UART Recv (WB family, F0/F3, F4/L1, PIC32CZ) checks parity, framing, noise and overrun per byte, clears them and returns WHAL_EHARDWARE - Blocking WB and WBA UART-DMA Recv check the same flags after the transfer; async callbacks are unchanged - PIC32CZ Send returns immediately for zero bytes instead of waiting on TXC - WB-family UART Deinit clears FIFOEN after disabling the USART - I2C StartCom (WB family, L1) rejects addresses wider than addrSz - L1 I2C rejects frequencies above 400 kHz; WB-family I2C rejects a pclk below 1 MHz - Add StartCom rejection tests for every I2C board - Add a WB55 test that decodes TIMINGR against the I2C-bus minimum SCL times and checks the CR2 address encoding - Add an L1 test for the Fast-mode limit --- src/i2c/stm32l1_i2c.c | 6 +- src/i2c/stm32wb_i2c.c | 9 ++ src/uart/pic32cz_uart.c | 21 ++++ src/uart/stm32f0_uart.c | 20 ++++ src/uart/stm32f4_uart.c | 22 +++- src/uart/stm32wb_uart.c | 30 +++++ src/uart/stm32wb_uart_dma.c | 21 ++++ src/uart/stm32wba_uart_dma.c | 19 +++ tests/i2c/test_i2c.c | 26 ++++ tests/i2c/test_stm32l1_i2c.c | 51 ++++++++ tests/i2c/test_stm32wb_i2c.c | 200 +++++++++++++++++++++++++++++++ wolfHAL/i2c/stm32l1_i2c.h | 3 +- wolfHAL/i2c/stm32wb_i2c.h | 3 +- wolfHAL/uart/pic32cz_uart.h | 1 + wolfHAL/uart/stm32f0_uart.h | 2 + wolfHAL/uart/stm32f4_uart.h | 2 + wolfHAL/uart/stm32wb_uart.h | 2 + wolfHAL/uart/stm32wb_uart_dma.h | 2 + wolfHAL/uart/stm32wba_uart_dma.h | 3 +- wolfHAL/uart/uart.h | 2 + 20 files changed, 440 insertions(+), 5 deletions(-) create mode 100644 tests/i2c/test_stm32l1_i2c.c create mode 100644 tests/i2c/test_stm32wb_i2c.c diff --git a/src/i2c/stm32l1_i2c.c b/src/i2c/stm32l1_i2c.c index aad5d6e..b0ac256 100644 --- a/src/i2c/stm32l1_i2c.c +++ b/src/i2c/stm32l1_i2c.c @@ -378,7 +378,11 @@ whal_Error whal_Stm32l1_I2c_StartCom(whal_I2c *i2cDev, whal_I2c_ComCfg *comCfg) return WHAL_EINVAL; #endif - if ((comCfg->addrSz != 7 && comCfg->addrSz != 10) || comCfg->freq == 0) + if ((comCfg->addrSz != 7 && comCfg->addrSz != 10) || comCfg->freq == 0 || + comCfg->freq > 400000) + return WHAL_EINVAL; + + if (comCfg->addr >= (1U << comCfg->addrSz)) return WHAL_EINVAL; #ifndef WHAL_CFG_STM32L1_I2C_SINGLE_INSTANCE diff --git a/src/i2c/stm32wb_i2c.c b/src/i2c/stm32wb_i2c.c index e32930f..f2a69f9 100644 --- a/src/i2c/stm32wb_i2c.c +++ b/src/i2c/stm32wb_i2c.c @@ -483,11 +483,20 @@ whal_Error whal_Stm32wb_I2c_StartCom(whal_I2c *i2cDev, whal_I2c_ComCfg *comCfg) return WHAL_EINVAL; } + if (comCfg->addr >= (1U << comCfg->addrSz)) { + return WHAL_EINVAL; + } + #ifndef WHAL_CFG_STM32WB_I2C_SINGLE_INSTANCE base = i2cDev->base; cfg = (whal_Stm32wb_I2c_Cfg *)i2cDev->cfg; #endif + /* Timing is computed in whole MHz of the peripheral clock */ + if (cfg->pclk < 1000000) { + return WHAL_EINVAL; + } + /* Disable PE to configure timing */ whal_Reg_Update(base, I2C_CR1_REG, I2C_CR1_PE_Msk, whal_SetBits(I2C_CR1_PE_Msk, I2C_CR1_PE_Pos, 0)); diff --git a/src/uart/pic32cz_uart.c b/src/uart/pic32cz_uart.c index 2fc70e9..44be8f5 100644 --- a/src/uart/pic32cz_uart.c +++ b/src/uart/pic32cz_uart.c @@ -169,6 +169,13 @@ #define USART_STATUS_TXE_Pos 6 #define USART_STATUS_TXE_Msk (1UL << USART_STATUS_TXE_Pos) +#define USART_STATUS_ERR_Msk (USART_STATUS_PERR_Msk | \ + USART_STATUS_FERR_Msk | \ + USART_STATUS_BUFOVF_Msk) + +/* STATUS is the upper halfword of the 32-bit word at INTFLAG */ +#define USART_STATUS_WORD_Pos 16 + /* SYNCBUSY Register Bit Definitions */ #define USART_SYNCBUSY_SWRST_Pos 0 #define USART_SYNCBUSY_SWRST_Msk (1UL << USART_SYNCBUSY_SWRST_Pos) @@ -350,6 +357,9 @@ whal_Error whal_Pic32cz_Uart_Send(whal_Uart *uartDev, const void *data, size_t d cfg = (whal_Pic32cz_Uart_Cfg *)uartDev->cfg; #endif + if (dataSz == 0) + return WHAL_SUCCESS; + for (size_t i = 0; i < dataSz; ++i) { /* Wait for data register to be empty */ err = whal_Reg_ReadPoll(base, USART_INTFLAG_REG, @@ -403,6 +413,7 @@ whal_Error whal_Pic32cz_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz) for (size_t i = 0; i < dataSz; ++i) { size_t rxData; + size_t status; whal_Error err; /* Wait for receive complete */ @@ -412,11 +423,21 @@ whal_Error whal_Pic32cz_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz) if (err) return err; + status = (whal_Reg_Read(base, USART_INTFLAG_REG) >> + USART_STATUS_WORD_Pos) & USART_STATUS_ERR_Msk; + /* Read received data */ whal_Reg_Get(base, USART_DATA_REG, USART_DATA_Msk, USART_DATA_Pos, &rxData); buf[i] = (uint8_t)rxData; + + if (status) { + whal_Reg_Write(base, USART_INTFLAG_REG, + (status << USART_STATUS_WORD_Pos) | + USART_INTFLAG_ERROR_Msk); + return WHAL_EHARDWARE; + } } return WHAL_SUCCESS; diff --git a/src/uart/stm32f0_uart.c b/src/uart/stm32f0_uart.c index fe8b397..2aedef5 100644 --- a/src/uart/stm32f0_uart.c +++ b/src/uart/stm32f0_uart.c @@ -43,6 +43,16 @@ #define UART_BRR_Msk (WHAL_BITMASK(16) << UART_BRR_Pos) #define UART_ISR_REG 0x1C +#define UART_ISR_PE_Pos 0 +#define UART_ISR_PE_Msk (1UL << UART_ISR_PE_Pos) +#define UART_ISR_FE_Pos 1 +#define UART_ISR_FE_Msk (1UL << UART_ISR_FE_Pos) +#define UART_ISR_NF_Pos 2 +#define UART_ISR_NF_Msk (1UL << UART_ISR_NF_Pos) +#define UART_ISR_ORE_Pos 3 +#define UART_ISR_ORE_Msk (1UL << UART_ISR_ORE_Pos) +#define UART_ISR_ERR_Msk (UART_ISR_PE_Msk | UART_ISR_FE_Msk | \ + UART_ISR_NF_Msk | UART_ISR_ORE_Msk) #define UART_ISR_RXNE_Pos 5 #define UART_ISR_RXNE_Msk (1UL << UART_ISR_RXNE_Pos) #define UART_ISR_TC_Pos 6 @@ -50,6 +60,9 @@ #define UART_ISR_TXE_Pos 7 #define UART_ISR_TXE_Msk (1UL << UART_ISR_TXE_Pos) +/* ICR clear bits share the ISR error bit positions */ +#define UART_ICR_REG 0x20 + #define UART_RDR_REG 0x24 #define UART_RDR_Pos 0 #define UART_RDR_Msk (WHAL_BITMASK(9) << UART_RDR_Pos) @@ -189,14 +202,21 @@ whal_Error whal_Stm32f0_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz) for (size_t i = 0; i < dataSz; ++i) { size_t d; + size_t isr; whal_Error err = whal_Reg_ReadPoll(base, UART_ISR_REG, UART_ISR_RXNE_Msk, UART_ISR_RXNE_Msk, cfg->timeout); if (err) return err; + isr = whal_Reg_Read(base, UART_ISR_REG); whal_Reg_Get(base, UART_RDR_REG, UART_RDR_Msk, UART_RDR_Pos, &d); buf[i] = d; + + if (isr & UART_ISR_ERR_Msk) { + whal_Reg_Write(base, UART_ICR_REG, isr & UART_ISR_ERR_Msk); + return WHAL_EHARDWARE; + } } return WHAL_SUCCESS; diff --git a/src/uart/stm32f4_uart.c b/src/uart/stm32f4_uart.c index 813da48..ef71e15 100644 --- a/src/uart/stm32f4_uart.c +++ b/src/uart/stm32f4_uart.c @@ -39,6 +39,21 @@ /* Status Register */ #define UART_SR_REG 0x00 +#define UART_SR_PE_Pos 0 /* Parity error */ +#define UART_SR_PE_Msk (1UL << UART_SR_PE_Pos) + +#define UART_SR_FE_Pos 1 /* Framing error */ +#define UART_SR_FE_Msk (1UL << UART_SR_FE_Pos) + +#define UART_SR_NF_Pos 2 /* Noise detected */ +#define UART_SR_NF_Msk (1UL << UART_SR_NF_Pos) + +#define UART_SR_ORE_Pos 3 /* Overrun error */ +#define UART_SR_ORE_Msk (1UL << UART_SR_ORE_Pos) + +#define UART_SR_ERR_Msk (UART_SR_PE_Msk | UART_SR_FE_Msk | \ + UART_SR_NF_Msk | UART_SR_ORE_Msk) + #define UART_SR_RXNE_Pos 5 /* Receive data register not empty */ #define UART_SR_RXNE_Msk (1UL << UART_SR_RXNE_Pos) @@ -194,6 +209,7 @@ whal_Error whal_Stm32f4_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz) { uint8_t *buf = data; size_t d; + size_t sr; #ifdef WHAL_CFG_STM32F4_UART_SINGLE_INSTANCE const whal_Stm32f4_Uart_Cfg *cfg = (const whal_Stm32f4_Uart_Cfg *)whal_Stm32f4_Uart_Dev.cfg; @@ -221,11 +237,15 @@ whal_Error whal_Stm32f4_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz) if (err) return err; - /* Read received byte */ + /* Reading SR then DR also clears the error flags */ + sr = whal_Reg_Read(base, UART_SR_REG); whal_Reg_Get(base, UART_DR_REG, UART_DR_Msk, UART_DR_Pos, &d); buf[i] = d; + + if (sr & UART_SR_ERR_Msk) + return WHAL_EHARDWARE; } return WHAL_SUCCESS; diff --git a/src/uart/stm32wb_uart.c b/src/uart/stm32wb_uart.c index c43a946..ca4e06d 100644 --- a/src/uart/stm32wb_uart.c +++ b/src/uart/stm32wb_uart.c @@ -48,6 +48,21 @@ #define UART_BRR_Msk (WHAL_BITMASK(20) << UART_BRR_Pos) #define UART_ISR_REG 0x1C +#define UART_ISR_PE_Pos 0 +#define UART_ISR_PE_Msk (1UL << UART_ISR_PE_Pos) + +#define UART_ISR_FE_Pos 1 +#define UART_ISR_FE_Msk (1UL << UART_ISR_FE_Pos) + +#define UART_ISR_NE_Pos 2 +#define UART_ISR_NE_Msk (1UL << UART_ISR_NE_Pos) + +#define UART_ISR_ORE_Pos 3 +#define UART_ISR_ORE_Msk (1UL << UART_ISR_ORE_Pos) + +#define UART_ISR_ERR_Msk (UART_ISR_PE_Msk | UART_ISR_FE_Msk | \ + UART_ISR_NE_Msk | UART_ISR_ORE_Msk) + #define UART_ISR_RXFNE_Pos 5 #define UART_ISR_RXFNE_Msk (1UL << UART_ISR_RXFNE_Pos) @@ -57,6 +72,9 @@ #define UART_ISR_TXE_Pos 7 #define UART_ISR_TXE_Msk (1UL << UART_ISR_TXE_Pos) +/* ICR clear bits share the ISR error bit positions */ +#define UART_ICR_REG 0x20 + #define UART_RDR_REG 0x24 #define UART_RDR_Pos 0 #define UART_RDR_Msk (WHAL_BITMASK(9) << UART_RDR_Pos) @@ -139,6 +157,10 @@ whal_Error whal_Stm32wb_Uart_Deinit(whal_Uart *uartDev) whal_SetBits(UART_CR1_RE_Msk, UART_CR1_RE_Pos, 0) | whal_SetBits(UART_CR1_TE_Msk, UART_CR1_TE_Pos, 0)); + /* FIFOEN is only writable while UE is 0 */ + whal_Reg_Update(base, UART_CR1_REG, UART_CR1_FIFOEN_Msk, + whal_SetBits(UART_CR1_FIFOEN_Msk, UART_CR1_FIFOEN_Pos, 0)); + whal_Reg_Update(base, UART_BRR_REG, UART_BRR_Msk, whal_SetBits(UART_BRR_Msk, UART_BRR_Pos, 0)); @@ -208,6 +230,7 @@ whal_Error whal_Stm32wb_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz) cfg = (whal_Stm32wb_Uart_Cfg *)uartDev->cfg; #endif size_t d; + size_t isr; for (size_t i = 0; i < dataSz; ++i) { whal_Error err = whal_Reg_ReadPoll(base, UART_ISR_REG, @@ -216,10 +239,17 @@ whal_Error whal_Stm32wb_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz) if (err) return err; + isr = whal_Reg_Read(base, UART_ISR_REG); + whal_Reg_Get(base, UART_RDR_REG, UART_RDR_Msk, UART_RDR_Pos, &d); buf[i] = d; + + if (isr & UART_ISR_ERR_Msk) { + whal_Reg_Write(base, UART_ICR_REG, isr & UART_ISR_ERR_Msk); + return WHAL_EHARDWARE; + } } return WHAL_SUCCESS; diff --git a/src/uart/stm32wb_uart_dma.c b/src/uart/stm32wb_uart_dma.c index 1189db9..7675092 100644 --- a/src/uart/stm32wb_uart_dma.c +++ b/src/uart/stm32wb_uart_dma.c @@ -39,9 +39,22 @@ #define UART_CR3_DMAT_Msk (1UL << UART_CR3_DMAT_Pos) #define UART_ISR_REG 0x1C +#define UART_ISR_PE_Pos 0 +#define UART_ISR_PE_Msk (1UL << UART_ISR_PE_Pos) +#define UART_ISR_FE_Pos 1 +#define UART_ISR_FE_Msk (1UL << UART_ISR_FE_Pos) +#define UART_ISR_NE_Pos 2 +#define UART_ISR_NE_Msk (1UL << UART_ISR_NE_Pos) +#define UART_ISR_ORE_Pos 3 +#define UART_ISR_ORE_Msk (1UL << UART_ISR_ORE_Pos) +#define UART_ISR_ERR_Msk (UART_ISR_PE_Msk | UART_ISR_FE_Msk | \ + UART_ISR_NE_Msk | UART_ISR_ORE_Msk) #define UART_ISR_TC_Pos 6 #define UART_ISR_TC_Msk (1UL << UART_ISR_TC_Pos) +/* ICR clear bits share the ISR error bit positions */ +#define UART_ICR_REG 0x20 + #define UART_RDR_REG 0x24 #define UART_TDR_REG 0x28 @@ -223,6 +236,7 @@ whal_Error whal_Stm32wb_UartDma_Recv(whal_Uart *uartDev, void *data, whal_Stm32wb_UartDma_Cfg *cfg; size_t base; whal_Error err; + size_t isr; err = whal_Stm32wb_UartDma_RecvAsync(uartDev, data, dataSz); if (err || dataSz == 0) @@ -245,6 +259,13 @@ whal_Error whal_Stm32wb_UartDma_Recv(whal_Uart *uartDev, void *data, } err = cfg->rxResult; + if (err == WHAL_SUCCESS) { + isr = whal_Reg_Read(base, UART_ISR_REG) & UART_ISR_ERR_Msk; + if (isr) { + whal_Reg_Write(base, UART_ICR_REG, isr); + err = WHAL_EHARDWARE; + } + } cleanup: whal_Dma_Stop(cfg->dma, cfg->rxCh); diff --git a/src/uart/stm32wba_uart_dma.c b/src/uart/stm32wba_uart_dma.c index 8fff2f5..ec5dc44 100644 --- a/src/uart/stm32wba_uart_dma.c +++ b/src/uart/stm32wba_uart_dma.c @@ -38,9 +38,20 @@ #define UART_CR3_DMAT_Msk (1UL << UART_CR3_DMAT_Pos) #define UART_ISR_REG 0x1C +#define UART_ISR_PE_Pos 0 +#define UART_ISR_PE_Msk (1UL << UART_ISR_PE_Pos) +#define UART_ISR_FE_Pos 1 +#define UART_ISR_FE_Msk (1UL << UART_ISR_FE_Pos) +#define UART_ISR_NE_Pos 2 +#define UART_ISR_NE_Msk (1UL << UART_ISR_NE_Pos) +#define UART_ISR_ORE_Pos 3 +#define UART_ISR_ORE_Msk (1UL << UART_ISR_ORE_Pos) +#define UART_ISR_ERR_Msk (UART_ISR_PE_Msk | UART_ISR_FE_Msk | \ + UART_ISR_NE_Msk | UART_ISR_ORE_Msk) #define UART_ISR_TC_Pos 6 #define UART_ISR_TC_Msk (1UL << UART_ISR_TC_Pos) +/* ICR clear bits share the ISR error bit positions */ #define UART_ICR_REG 0x20 #define UART_ICR_TCCF_Pos 6 #define UART_ICR_TCCF_Msk (1UL << UART_ICR_TCCF_Pos) @@ -241,6 +252,7 @@ whal_Error whal_Stm32wba_UartDma_Recv(whal_Uart *uartDev, void *data, size_t base; whal_Error err; whal_Error stopErr; + size_t isr; err = whal_Stm32wba_UartDma_RecvAsync(uartDev, data, dataSz); if (err || dataSz == 0) @@ -263,6 +275,13 @@ whal_Error whal_Stm32wba_UartDma_Recv(whal_Uart *uartDev, void *data, } err = cfg->rxResult; + if (err == WHAL_SUCCESS) { + isr = whal_Reg_Read(base, UART_ISR_REG) & UART_ISR_ERR_Msk; + if (isr) { + whal_Reg_Write(base, UART_ICR_REG, isr); + err = WHAL_EHARDWARE; + } + } cleanup: stopErr = whal_Dma_Stop(cfg->dma, cfg->rxCh); diff --git a/tests/i2c/test_i2c.c b/tests/i2c/test_i2c.c index 0c8b134..8812958 100644 --- a/tests/i2c/test_i2c.c +++ b/tests/i2c/test_i2c.c @@ -29,9 +29,35 @@ static void Test_I2c_Api(void) WHAL_ASSERT_EQ(whal_I2c_Transfer(BOARD_I2C_DEV, NULL, 1), WHAL_EINVAL); } +/* Addresses wider than addrSz and a zero frequency are rejected before any + * register is touched. */ +static void Test_I2c_StartComReject(void) +{ + whal_I2c_ComCfg com = { + .freq = 100000, + .addr = 0x80, + .addrSz = 7, + }; + + WHAL_ASSERT_EQ(whal_I2c_StartCom(BOARD_I2C_DEV, &com), WHAL_EINVAL); + + com.addr = 0xFFFF; + WHAL_ASSERT_EQ(whal_I2c_StartCom(BOARD_I2C_DEV, &com), WHAL_EINVAL); + + com.addr = 0x400; + com.addrSz = 10; + WHAL_ASSERT_EQ(whal_I2c_StartCom(BOARD_I2C_DEV, &com), WHAL_EINVAL); + + com.addr = 0x50; + com.addrSz = 7; + com.freq = 0; + WHAL_ASSERT_EQ(whal_I2c_StartCom(BOARD_I2C_DEV, &com), WHAL_EINVAL); +} + void whal_Test_I2c(void) { WHAL_TEST_SUITE_START("i2c"); WHAL_TEST(Test_I2c_Api); + WHAL_TEST(Test_I2c_StartComReject); WHAL_TEST_SUITE_END(); } diff --git a/tests/i2c/test_stm32l1_i2c.c b/tests/i2c/test_stm32l1_i2c.c new file mode 100644 index 0000000..95226fd --- /dev/null +++ b/tests/i2c/test_stm32l1_i2c.c @@ -0,0 +1,51 @@ +/* test_stm32l1_i2c.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHAL. + * + * wolfHAL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHAL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include "wolfHAL_board.h" +#include "test.h" + +/* The L1 I2C peripheral supports Standard and Fast mode only */ +static void Test_Stm32l1_I2c_FreqLimit(void) +{ + whal_I2c_ComCfg com = { + .freq = 400001, + .addr = 0x50, + .addrSz = 7, + }; + + WHAL_ASSERT_EQ(whal_I2c_StartCom(BOARD_I2C_DEV, &com), WHAL_EINVAL); + + com.freq = 1000000; + WHAL_ASSERT_EQ(whal_I2c_StartCom(BOARD_I2C_DEV, &com), WHAL_EINVAL); + + com.freq = 400000; + WHAL_ASSERT_EQ(whal_I2c_StartCom(BOARD_I2C_DEV, &com), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_I2c_EndCom(BOARD_I2C_DEV), WHAL_SUCCESS); +} + +void whal_Test_I2c_Platform(void) +{ + WHAL_TEST_SUITE_START("i2c_platform"); + WHAL_TEST(Test_Stm32l1_I2c_FreqLimit); + WHAL_TEST_SUITE_END(); +} diff --git a/tests/i2c/test_stm32wb_i2c.c b/tests/i2c/test_stm32wb_i2c.c new file mode 100644 index 0000000..cdd1c20 --- /dev/null +++ b/tests/i2c/test_stm32wb_i2c.c @@ -0,0 +1,200 @@ +/* test_stm32wb_i2c.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHAL. + * + * wolfHAL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHAL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include "wolfHAL_board.h" +#include "test.h" + +/* StartCom only programs TIMINGR and CR2; nothing is driven on the bus, so + * no target device is needed. */ + +#define I2C_CR2 0x04 +#define I2C_TIMINGR 0x10 + +#define CR2_SADD_Msk 0x3FFu +#define CR2_ADD10_Msk (1UL << 11) + +/* I2C-bus specification (UM10204) minimum SCL low and high times */ +#define SM_TLOW_NS 4700 +#define SM_THIGH_NS 4000 +#define FM_TLOW_NS 1300 +#define FM_THIGH_NS 600 +#define FMP_TLOW_NS 500 +#define FMP_THIGH_NS 260 + +typedef struct { + uint32_t presc; + uint32_t scldel; + uint32_t sdadel; + uint32_t sclh; + uint32_t scll; +} Timing; + +static size_t I2cBase(void) +{ + return BOARD_I2C_DEV->base; +} + +static uint32_t I2cPclk(void) +{ + return ((whal_Stm32wb_I2c_Cfg *)BOARD_I2C_DEV->cfg)->pclk; +} + +static Timing DecodeTimingr(uint32_t t) +{ + Timing d; + + d.presc = (t >> 28) & 0xF; + d.scldel = (t >> 20) & 0xF; + d.sdadel = (t >> 16) & 0xF; + d.sclh = (t >> 8) & 0xFF; + d.scll = t & 0xFF; + + return d; +} + +/* Duration of n prescaled I2C clock ticks, in nanoseconds (pclk is a whole + * number of MHz) */ +static uint32_t TicksNs(uint32_t presc, uint32_t n) +{ + return (presc + 1) * n * 1000u / (I2cPclk() / 1000000u); +} + +static void StartAndRead(uint32_t freq, Timing *t) +{ + whal_I2c_ComCfg com = { + .freq = freq, + .addr = 0x50, + .addrSz = 7, + }; + + WHAL_ASSERT_EQ(whal_I2c_StartCom(BOARD_I2C_DEV, &com), WHAL_SUCCESS); + *t = DecodeTimingr(whal_Reg_Read(I2cBase(), I2C_TIMINGR)); + WHAL_ASSERT_EQ(whal_I2c_EndCom(BOARD_I2C_DEV), WHAL_SUCCESS); +} + +static void CheckMode(uint32_t freq, uint32_t tLowMin, uint32_t tHighMin, + uint32_t sdadel, uint32_t scldel, uint32_t *periodNs) +{ + Timing t = {0}; + + StartAndRead(freq, &t); + if (g_whalTestCurFailed) + return; + + WHAL_ASSERT_EQ(t.sdadel, sdadel); + WHAL_ASSERT_EQ(t.scldel, scldel); + + WHAL_ASSERT_EQ(TicksNs(t.presc, t.scll + 1) >= tLowMin, 1); + WHAL_ASSERT_EQ(TicksNs(t.presc, t.sclh + 1) >= tHighMin, 1); + + *periodNs = TicksNs(t.presc, t.scll + 1 + t.sclh + 1); +} + +static void Test_Stm32wb_I2c_Timing(void) +{ + uint32_t p10k = 0, p100k = 0, p400k = 0, p1m = 0; + + CheckMode(10000, SM_TLOW_NS, SM_THIGH_NS, 2, 4, &p10k); + if (g_whalTestCurFailed) + return; + CheckMode(100000, SM_TLOW_NS, SM_THIGH_NS, 2, 4, &p100k); + if (g_whalTestCurFailed) + return; + CheckMode(400000, FM_TLOW_NS, FM_THIGH_NS, 1, 3, &p400k); + if (g_whalTestCurFailed) + return; + CheckMode(1000000, FMP_TLOW_NS, FMP_THIGH_NS, 0, 1, &p1m); + if (g_whalTestCurFailed) + return; + + /* Faster requests give shorter SCL periods */ + WHAL_ASSERT_EQ(p1m < p400k, 1); + WHAL_ASSERT_EQ(p400k < p100k, 1); + WHAL_ASSERT_EQ(p100k < p10k, 1); + + /* A tenth of the frequency within a mode gives about ten times the + * period (within 5%) */ + WHAL_ASSERT_EQ(p10k >= p100k * 95 / 10, 1); + WHAL_ASSERT_EQ(p10k <= p100k * 105 / 10, 1); +} + +static void Test_Stm32wb_I2c_Address(void) +{ + whal_I2c_ComCfg com = { + .freq = 100000, + .addr = 0x50, + .addrSz = 7, + }; + size_t cr2; + + /* 7-bit addresses occupy SADD[7:1] */ + WHAL_ASSERT_EQ(whal_I2c_StartCom(BOARD_I2C_DEV, &com), WHAL_SUCCESS); + cr2 = whal_Reg_Read(I2cBase(), I2C_CR2); + WHAL_ASSERT_EQ(cr2 & CR2_SADD_Msk, 0x50 << 1); + WHAL_ASSERT_EQ(cr2 & CR2_ADD10_Msk, 0); + WHAL_ASSERT_EQ(whal_I2c_EndCom(BOARD_I2C_DEV), WHAL_SUCCESS); + + /* Highest 7-bit address */ + com.addr = 0x7F; + WHAL_ASSERT_EQ(whal_I2c_StartCom(BOARD_I2C_DEV, &com), WHAL_SUCCESS); + cr2 = whal_Reg_Read(I2cBase(), I2C_CR2); + WHAL_ASSERT_EQ(cr2 & CR2_SADD_Msk, 0x7F << 1); + WHAL_ASSERT_EQ(whal_I2c_EndCom(BOARD_I2C_DEV), WHAL_SUCCESS); + + /* 10-bit addresses occupy SADD[9:0] with ADD10 set */ + com.addr = 0x2A5; + com.addrSz = 10; + WHAL_ASSERT_EQ(whal_I2c_StartCom(BOARD_I2C_DEV, &com), WHAL_SUCCESS); + cr2 = whal_Reg_Read(I2cBase(), I2C_CR2); + WHAL_ASSERT_EQ(cr2 & CR2_SADD_Msk, 0x2A5); + WHAL_ASSERT_EQ(cr2 & CR2_ADD10_Msk, CR2_ADD10_Msk); + + /* EndCom clears the address and addressing mode */ + WHAL_ASSERT_EQ(whal_I2c_EndCom(BOARD_I2C_DEV), WHAL_SUCCESS); + cr2 = whal_Reg_Read(I2cBase(), I2C_CR2); + WHAL_ASSERT_EQ(cr2 & (CR2_SADD_Msk | CR2_ADD10_Msk), 0); +} + +static void Test_Stm32wb_I2c_Reject(void) +{ + whal_I2c_ComCfg com = { + .freq = 1000001, + .addr = 0x50, + .addrSz = 7, + }; + + WHAL_ASSERT_EQ(whal_I2c_StartCom(BOARD_I2C_DEV, &com), WHAL_EINVAL); + + com.freq = 100000; + com.addrSz = 8; + WHAL_ASSERT_EQ(whal_I2c_StartCom(BOARD_I2C_DEV, &com), WHAL_EINVAL); +} + +void whal_Test_I2c_Platform(void) +{ + WHAL_TEST_SUITE_START("i2c_platform"); + WHAL_TEST(Test_Stm32wb_I2c_Timing); + WHAL_TEST(Test_Stm32wb_I2c_Address); + WHAL_TEST(Test_Stm32wb_I2c_Reject); + WHAL_TEST_SUITE_END(); +} diff --git a/wolfHAL/i2c/stm32l1_i2c.h b/wolfHAL/i2c/stm32l1_i2c.h index 6ca01ed..22f6398 100644 --- a/wolfHAL/i2c/stm32l1_i2c.h +++ b/wolfHAL/i2c/stm32l1_i2c.h @@ -97,7 +97,8 @@ whal_Error whal_Stm32l1_I2c_Deinit(whal_I2c *i2cDev); * @param comCfg Per-session communication parameters. * * @retval WHAL_SUCCESS Communication session started. - * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_EINVAL Invalid arguments, freq is 0 or above 400 kHz, or + * addr does not fit in addrSz bits. */ whal_Error whal_Stm32l1_I2c_StartCom(whal_I2c *i2cDev, whal_I2c_ComCfg *comCfg); diff --git a/wolfHAL/i2c/stm32wb_i2c.h b/wolfHAL/i2c/stm32wb_i2c.h index f0097c9..83fe09c 100644 --- a/wolfHAL/i2c/stm32wb_i2c.h +++ b/wolfHAL/i2c/stm32wb_i2c.h @@ -100,7 +100,8 @@ whal_Error whal_Stm32wb_I2c_Deinit(whal_I2c *i2cDev); * @param comCfg Per-session communication parameters. * * @retval WHAL_SUCCESS Communication session started. - * @retval WHAL_EINVAL Invalid arguments, or freq is 0 or above 1 MHz. + * @retval WHAL_EINVAL Invalid arguments, freq is 0 or above 1 MHz, addr + * does not fit in addrSz bits, or pclk is below 1 MHz. */ whal_Error whal_Stm32wb_I2c_StartCom(whal_I2c *i2cDev, whal_I2c_ComCfg *comCfg); diff --git a/wolfHAL/uart/pic32cz_uart.h b/wolfHAL/uart/pic32cz_uart.h index 0244539..80d9c98 100644 --- a/wolfHAL/uart/pic32cz_uart.h +++ b/wolfHAL/uart/pic32cz_uart.h @@ -116,6 +116,7 @@ whal_Error whal_Pic32cz_Uart_Send(whal_Uart *uartDev, const void *data, size_t d * * @retval WHAL_SUCCESS Transfer completed. * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_EHARDWARE A parity, framing or overflow error was detected. */ whal_Error whal_Pic32cz_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz); #endif /* !WHAL_CFG_PIC32CZ_UART_DIRECT_API_MAPPING */ diff --git a/wolfHAL/uart/stm32f0_uart.h b/wolfHAL/uart/stm32f0_uart.h index cf7c205..bfb15ea 100644 --- a/wolfHAL/uart/stm32f0_uart.h +++ b/wolfHAL/uart/stm32f0_uart.h @@ -101,6 +101,8 @@ whal_Error whal_Stm32f0_Uart_Send(whal_Uart *uartDev, const void *data, * @retval WHAL_SUCCESS All bytes received. * @retval WHAL_EINVAL Null pointer. * @retval WHAL_ETIMEOUT Hardware did not assert RXNE within the configured timeout. + * @retval WHAL_EHARDWARE A parity, framing, noise or overrun error was + * detected. */ whal_Error whal_Stm32f0_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz); diff --git a/wolfHAL/uart/stm32f4_uart.h b/wolfHAL/uart/stm32f4_uart.h index d3a040a..101f5a3 100644 --- a/wolfHAL/uart/stm32f4_uart.h +++ b/wolfHAL/uart/stm32f4_uart.h @@ -119,6 +119,8 @@ whal_Error whal_Stm32f4_Uart_Send(whal_Uart *uartDev, const void *data, size_t d * * @retval WHAL_SUCCESS Transfer completed. * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_EHARDWARE A parity, framing, noise or overrun error was + * detected. */ whal_Error whal_Stm32f4_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz); #endif /* !WHAL_CFG_STM32F4_UART_DIRECT_API_MAPPING */ diff --git a/wolfHAL/uart/stm32wb_uart.h b/wolfHAL/uart/stm32wb_uart.h index c9b92e4..89d1323 100644 --- a/wolfHAL/uart/stm32wb_uart.h +++ b/wolfHAL/uart/stm32wb_uart.h @@ -117,6 +117,8 @@ whal_Error whal_Stm32wb_Uart_Send(whal_Uart *uartDev, const void *data, size_t d * * @retval WHAL_SUCCESS All bytes received. * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_EHARDWARE A parity, framing, noise or overrun error was + * detected. */ whal_Error whal_Stm32wb_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz); #endif /* !WHAL_CFG_UART_API_MAPPING */ diff --git a/wolfHAL/uart/stm32wb_uart_dma.h b/wolfHAL/uart/stm32wb_uart_dma.h index 8756686..f2e0b6e 100644 --- a/wolfHAL/uart/stm32wb_uart_dma.h +++ b/wolfHAL/uart/stm32wb_uart_dma.h @@ -107,6 +107,8 @@ whal_Error whal_Stm32wb_UartDma_Send(whal_Uart *uartDev, const void *data, * @retval WHAL_SUCCESS All bytes received. * @retval WHAL_ETIMEOUT DMA transfer timed out. * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_EHARDWARE A parity, framing, noise or overrun error was + * detected. */ whal_Error whal_Stm32wb_UartDma_Recv(whal_Uart *uartDev, void *data, size_t dataSz); diff --git a/wolfHAL/uart/stm32wba_uart_dma.h b/wolfHAL/uart/stm32wba_uart_dma.h index b90d354..1c4c993 100644 --- a/wolfHAL/uart/stm32wba_uart_dma.h +++ b/wolfHAL/uart/stm32wba_uart_dma.h @@ -117,7 +117,8 @@ whal_Error whal_Stm32wba_UartDma_Send(whal_Uart *uartDev, const void *data, * @retval WHAL_SUCCESS All bytes received. * @retval WHAL_EINVAL Null pointer. * @retval WHAL_ETIMEOUT DMA transfer did not complete within the timeout. - * @retval WHAL_EHARDWARE GPDMA reported a transfer error. + * @retval WHAL_EHARDWARE GPDMA reported a transfer error, or a parity, + * framing, noise or overrun error was detected. */ whal_Error whal_Stm32wba_UartDma_Recv(whal_Uart *uartDev, void *data, size_t dataSz); diff --git a/wolfHAL/uart/uart.h b/wolfHAL/uart/uart.h index 52096b3..d854c91 100644 --- a/wolfHAL/uart/uart.h +++ b/wolfHAL/uart/uart.h @@ -105,6 +105,8 @@ whal_Error whal_Uart_Send(whal_Uart *uartDev, const void *data, size_t dataSz); * @retval WHAL_SUCCESS Buffer was filled or receive started. * @retval WHAL_EINVAL Null pointer. * @retval WHAL_ENOTSUP Operation not implemented by this driver. + * @retval WHAL_EHARDWARE A receive line error (parity, framing, noise or + * overrun) was detected. */ whal_Error whal_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz); From 95a5ad825e8ee160f0f9ce3de0912e343b3c70a4 Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Wed, 14 Oct 2026 12:47:44 -0400 Subject: [PATCH 15/22] [gpio, stm32wb, stm32wb0, pic32cz, test] Set pins atomically, add pin configuration readback tests - STM32 and WB0 GPIO Set write BSRR instead of read-modify-writing ODR - PIC32CZ GPIO Set writes OUTSET or OUTCLR instead of read-modify-writing OUT - Any nonzero Set value drives the pin high - Add an STM32 test that reads back MODER, OTYPER, OSPEEDR, PUPDR and the AF nibble for every board pin, and a WB0 version - Add a PIC32CZ test that reads back PINCFG, PMUX and DIR - Add GPIO platform test stubs for F4, H5, U5 and WBA - Test that a nonzero Set value drives the pin high --- src/gpio/pic32cz_gpio.c | 10 +-- src/gpio/stm32wb0_gpio.c | 6 +- src/gpio/stm32wb_gpio.c | 9 ++- tests/gpio/test_pic32cz_gpio.c | 58 +++++++++++++++++ tests/gpio/test_stm32f4_gpio.c | 22 +++++++ tests/gpio/test_stm32h5_gpio.c | 22 +++++++ tests/gpio/test_stm32u5_gpio.c | 22 +++++++ tests/gpio/test_stm32wb0_gpio.c | 109 ++++++++++++++++++++++++++++++++ tests/gpio/test_stm32wb_gpio.c | 59 +++++++++++++++++ tests/gpio/test_stm32wba_gpio.c | 22 +++++++ wolfHAL/gpio/gpio.h | 2 +- 11 files changed, 331 insertions(+), 10 deletions(-) create mode 100644 tests/gpio/test_stm32f4_gpio.c create mode 100644 tests/gpio/test_stm32h5_gpio.c create mode 100644 tests/gpio/test_stm32u5_gpio.c create mode 100644 tests/gpio/test_stm32wb0_gpio.c create mode 100644 tests/gpio/test_stm32wba_gpio.c diff --git a/src/gpio/pic32cz_gpio.c b/src/gpio/pic32cz_gpio.c index 9944516..ae12a34 100644 --- a/src/gpio/pic32cz_gpio.c +++ b/src/gpio/pic32cz_gpio.c @@ -44,6 +44,10 @@ const whal_Gpio whal_Pic32cz_Gpio_Dev = WHAL_CFG_PIC32CZ_GPIO_DEV; /* Output register - bit N is the output value for pin N */ #define OUT_REG(port) (0x10 + (port * 0x80)) +/* Output clear and set registers - writing 1 to bit N clears or sets pin N */ +#define OUTCLR_REG(port) (0x14 + (port * 0x80)) +#define OUTSET_REG(port) (0x18 + (port * 0x80)) + /* Input register - bit N is the sampled input value for pin N */ #define IN_REG(port) (0x20 + (port * 0x80)) @@ -207,10 +211,8 @@ whal_Error whal_Pic32cz_Gpio_Set(whal_Gpio *gpioDev, size_t pin, size_t value) pinCfg = &cfg->pinCfg[pin]; pinMask = (1UL << (pinCfg->pin)); - /* Update the output register to drive the new value */ - whal_Reg_Update(base, OUT_REG(pinCfg->port), - pinMask, - whal_SetBits(pinMask, pinCfg->pin, value)); + whal_Reg_Write(base, value ? OUTSET_REG(pinCfg->port) : + OUTCLR_REG(pinCfg->port), pinMask); return WHAL_SUCCESS; } diff --git a/src/gpio/stm32wb0_gpio.c b/src/gpio/stm32wb0_gpio.c index 88ea630..bfd71a7 100644 --- a/src/gpio/stm32wb0_gpio.c +++ b/src/gpio/stm32wb0_gpio.c @@ -42,6 +42,8 @@ const whal_Gpio whal_Stm32wb0_Gpio_Dev = WHAL_CFG_STM32WB0_GPIO_DEV; #define GPIO_PULL_REG 0x0C /* PUPDR: 2 bits/pin */ #define GPIO_IDR_REG 0x10 #define GPIO_ODR_REG 0x14 +#define GPIO_BSRR_REG 0x18 /* BSRR: 0..15 set, 16..31 reset */ +#define GPIO_BSRR_BR_Pos 16 #define GPIO_ALTFNL_REG 0x20 /* AFRL: 4 bits/pin for pins 0..7 */ #define GPIO_ALTFNH_REG 0x24 /* AFRH: 4 bits/pin for pins 8..15 */ @@ -132,8 +134,8 @@ static whal_Error whal_Stm32wb0_Gpio_SetOrGet(whal_Gpio *gpioDev, size_t idx, mask = 1UL << pin; if (set) { - whal_Reg_Update(portBase, GPIO_ODR_REG, mask, - whal_SetBits(mask, pin, *value)); + whal_Reg_Write(portBase, GPIO_BSRR_REG, + *value ? mask : (mask << GPIO_BSRR_BR_Pos)); } else { whal_Reg_Get(portBase, GPIO_IDR_REG, mask, pin, value); } diff --git a/src/gpio/stm32wb_gpio.c b/src/gpio/stm32wb_gpio.c index eb56c42..9bd105a 100644 --- a/src/gpio/stm32wb_gpio.c +++ b/src/gpio/stm32wb_gpio.c @@ -51,6 +51,9 @@ const whal_Gpio whal_Stm32wb_Gpio_Dev = WHAL_CFG_STM32WB_GPIO_DEV; #define GPIO_IDR_REG 0x10 /* Output data register - 1 bit per pin */ #define GPIO_ODR_REG 0x14 +/* Bit set/reset register - bits 0-15 set pins, bits 16-31 reset pins */ +#define GPIO_BSRR_REG 0x18 +#define GPIO_BSRR_BR_Pos 16 /* Alternate function low register - 4 bits per pin for pins 0-7 */ #define GPIO_ALTFNL_REG 0x20 /* Alternate function high register - 4 bits per pin for pins 8-15 */ @@ -134,7 +137,7 @@ whal_Error whal_Stm32wb_Gpio_Deinit(whal_Gpio *gpioDev) /* * Shared helper for reading or writing GPIO pin values. * - * For set operations (set=1): writes value to ODR register + * For set operations (set=1): sets or resets the pin through BSRR * For get operations (set=0): reads value from IDR register */ static whal_Error whal_Stm32wb_Gpio_SetOrGet(whal_Gpio *gpioDev, size_t idx, @@ -159,8 +162,8 @@ static whal_Error whal_Stm32wb_Gpio_SetOrGet(whal_Gpio *gpioDev, size_t idx, mask = 1UL << pin; if (set) { - whal_Reg_Update(portBase, GPIO_ODR_REG, mask, - whal_SetBits(mask, pin, *value)); + whal_Reg_Write(portBase, GPIO_BSRR_REG, + *value ? mask : (mask << GPIO_BSRR_BR_Pos)); } else { whal_Reg_Get(portBase, GPIO_IDR_REG, mask, pin, value); } diff --git a/tests/gpio/test_pic32cz_gpio.c b/tests/gpio/test_pic32cz_gpio.c index 388dd0e..ecab0c3 100644 --- a/tests/gpio/test_pic32cz_gpio.c +++ b/tests/gpio/test_pic32cz_gpio.c @@ -32,6 +32,16 @@ #define PORT_DIR_REG(port) (0x00 + ((port) * 0x80)) #define PORT_OUT_REG(port) (0x10 + ((port) * 0x80)) +/* PMUX and PINCFG are byte registers, read here as aligned 32-bit words */ +#define PORT_PMUX_REG(port, pin) (0x30 + (((pin) / 8) * 4) + ((port) * 0x80)) +#define PORT_PMUX_Pos(pin) (((pin) % 8) * 4) +#define PORT_PINCFG_REG(port, pin) (0x40 + ((pin) & ~3) + ((port) * 0x80)) +#define PORT_PINCFG_Pos(pin) (((pin) & 3) * 8) + +#define PINCFG_PMUXEN (1UL << 0) +#define PINCFG_INEN (1UL << 1) +#define PINCFG_PULLEN (1UL << 2) + /* LED pin: port B, pin 21 */ #define LED_PORT 1 #define LED_HW_PIN 21 @@ -45,6 +55,40 @@ static void Test_Gpio_DirRegister(void) WHAL_ASSERT_EQ(val, 1); } +/* Peripheral pins read back with PMUXEN set and their PMUX function; GPIO + * pins read back with their direction, input enable and pull enable. */ +static void Test_Gpio_PinTableRegisters(void) +{ + const whal_Pic32cz_Gpio_Cfg *cfg = + (const whal_Pic32cz_Gpio_Cfg *)whal_Pic32cz_Gpio_Dev.cfg; + size_t base = whal_Pic32cz_Gpio_Dev.base; + + for (size_t i = 0; i < cfg->pinCfgCount; i++) { + const whal_Pic32cz_Gpio_PinCfg *p = &cfg->pinCfg[i]; + size_t pincfg = (whal_Reg_Read(base, + PORT_PINCFG_REG(p->port, p->pin)) >> + PORT_PINCFG_Pos(p->pin)) & 0xFF; + size_t val = 0; + + if (p->pmuxEn) { + WHAL_ASSERT_EQ(pincfg & PINCFG_PMUXEN, PINCFG_PMUXEN); + whal_Reg_Get(base, PORT_PMUX_REG(p->port, p->pin), + WHAL_BITMASK(4) << PORT_PMUX_Pos(p->pin), + PORT_PMUX_Pos(p->pin), &val); + WHAL_ASSERT_EQ(val, p->pmux); + continue; + } + + WHAL_ASSERT_EQ(pincfg & PINCFG_PMUXEN, 0); + WHAL_ASSERT_EQ((pincfg & PINCFG_INEN) != 0, p->inEn); + WHAL_ASSERT_EQ((pincfg & PINCFG_PULLEN) != 0, p->pullEn); + + whal_Reg_Get(base, PORT_DIR_REG(p->port), 1UL << p->pin, p->pin, + &val); + WHAL_ASSERT_EQ(val, p->dir); + } +} + static void Test_Gpio_SetHighReg(void) { WHAL_ASSERT_EQ(whal_Gpio_Set(BOARD_GPIO_DEV, BOARD_LED_PIN, 1), WHAL_SUCCESS); @@ -66,9 +110,23 @@ static void Test_Gpio_SetLowReg(void) WHAL_ASSERT_EQ(val, 0); } +static void Test_Gpio_SetNonzeroReg(void) +{ + size_t val = 0; + + WHAL_ASSERT_EQ(whal_Gpio_Set(BOARD_GPIO_DEV, BOARD_LED_PIN, 2), WHAL_SUCCESS); + whal_Reg_Get(whal_Pic32cz_Gpio_Dev.base, PORT_OUT_REG(LED_PORT), + (1UL << LED_HW_PIN), LED_HW_PIN, &val); + WHAL_ASSERT_EQ(val, 1); + + WHAL_ASSERT_EQ(whal_Gpio_Set(BOARD_GPIO_DEV, BOARD_LED_PIN, 0), WHAL_SUCCESS); +} + void whal_Test_Gpio_Platform(void) { WHAL_TEST(Test_Gpio_DirRegister); + WHAL_TEST(Test_Gpio_PinTableRegisters); WHAL_TEST(Test_Gpio_SetHighReg); WHAL_TEST(Test_Gpio_SetLowReg); + WHAL_TEST(Test_Gpio_SetNonzeroReg); } diff --git a/tests/gpio/test_stm32f4_gpio.c b/tests/gpio/test_stm32f4_gpio.c new file mode 100644 index 0000000..abd77ed --- /dev/null +++ b/tests/gpio/test_stm32f4_gpio.c @@ -0,0 +1,22 @@ +/* test_stm32f4_gpio.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHAL. + * + * wolfHAL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHAL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include "test_stm32wb_gpio.c" diff --git a/tests/gpio/test_stm32h5_gpio.c b/tests/gpio/test_stm32h5_gpio.c new file mode 100644 index 0000000..77e3036 --- /dev/null +++ b/tests/gpio/test_stm32h5_gpio.c @@ -0,0 +1,22 @@ +/* test_stm32h5_gpio.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHAL. + * + * wolfHAL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHAL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include "test_stm32wb_gpio.c" diff --git a/tests/gpio/test_stm32u5_gpio.c b/tests/gpio/test_stm32u5_gpio.c new file mode 100644 index 0000000..076aa67 --- /dev/null +++ b/tests/gpio/test_stm32u5_gpio.c @@ -0,0 +1,22 @@ +/* test_stm32u5_gpio.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHAL. + * + * wolfHAL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHAL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include "test_stm32wb_gpio.c" diff --git a/tests/gpio/test_stm32wb0_gpio.c b/tests/gpio/test_stm32wb0_gpio.c new file mode 100644 index 0000000..fb850c6 --- /dev/null +++ b/tests/gpio/test_stm32wb0_gpio.c @@ -0,0 +1,109 @@ +/* test_stm32wb0_gpio.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHAL. + * + * wolfHAL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHAL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include +#include "wolfHAL_board.h" +#include "test.h" + +/* STM32WB0 ports are 0x100000 apart; the per-port offsets match other STM32 + * families. */ +#define GPIOx_MODE_REG 0x00 +#define GPIOx_OTYPER_REG 0x04 +#define GPIOx_OSPEEDR_REG 0x08 +#define GPIOx_PUPDR_REG 0x0C +#define GPIOx_ODR_REG 0x14 +#define GPIOx_AFRL_REG 0x20 +#define GPIOx_AFRH_REG 0x24 +#define GPIOx_STRIDE 0x100000 + +static const whal_Stm32wb0_Gpio_Cfg *GpioCfg(void) +{ + return (const whal_Stm32wb0_Gpio_Cfg *)whal_Stm32wb0_Gpio_Dev.cfg; +} + +static size_t PortBase(whal_Stm32wb0_Gpio_PinCfg p) +{ + return whal_Stm32wb0_Gpio_Dev.base + + WHAL_STM32WB0_GPIO_GET_PORT(p) * GPIOx_STRIDE; +} + +/* Every pin in the board table reads back with its configured mode, output + * type, speed, pull and, for alternate-function pins, AF number. */ +static void Test_Gpio_PinTableRegisters(void) +{ + const whal_Stm32wb0_Gpio_Cfg *cfg = GpioCfg(); + + for (size_t i = 0; i < cfg->pinCount; i++) { + whal_Stm32wb0_Gpio_PinCfg p = cfg->pinCfg[i]; + size_t base = PortBase(p); + size_t pin = WHAL_STM32WB0_GPIO_GET_PIN(p); + size_t pos2 = pin << 1; + size_t val = 0; + + whal_Reg_Get(base, GPIOx_MODE_REG, WHAL_BITMASK(2) << pos2, pos2, + &val); + WHAL_ASSERT_EQ(val, WHAL_STM32WB0_GPIO_GET_MODE(p)); + + whal_Reg_Get(base, GPIOx_OTYPER_REG, 1UL << pin, pin, &val); + WHAL_ASSERT_EQ(val, WHAL_STM32WB0_GPIO_GET_OUTTYPE(p)); + + whal_Reg_Get(base, GPIOx_OSPEEDR_REG, WHAL_BITMASK(2) << pos2, pos2, + &val); + WHAL_ASSERT_EQ(val, WHAL_STM32WB0_GPIO_GET_SPEED(p)); + + whal_Reg_Get(base, GPIOx_PUPDR_REG, WHAL_BITMASK(2) << pos2, pos2, + &val); + WHAL_ASSERT_EQ(val, WHAL_STM32WB0_GPIO_GET_PULL(p)); + + if (WHAL_STM32WB0_GPIO_GET_MODE(p) == WHAL_STM32WB0_GPIO_MODE_ALTFN) { + size_t afPos = (pin & 7) << 2; + + whal_Reg_Get(base, (pin < 8) ? GPIOx_AFRL_REG : GPIOx_AFRH_REG, + WHAL_BITMASK(4) << afPos, afPos, &val); + WHAL_ASSERT_EQ(val, WHAL_STM32WB0_GPIO_GET_ALTFN(p)); + } + } +} + +static void Test_Gpio_SetLedReg(void) +{ + whal_Stm32wb0_Gpio_PinCfg led = GpioCfg()->pinCfg[BOARD_LED_PIN]; + size_t pin = WHAL_STM32WB0_GPIO_GET_PIN(led); + size_t val = 0; + + WHAL_ASSERT_EQ(whal_Gpio_Set(BOARD_GPIO_DEV, BOARD_LED_PIN, 1), + WHAL_SUCCESS); + whal_Reg_Get(PortBase(led), GPIOx_ODR_REG, 1UL << pin, pin, &val); + WHAL_ASSERT_EQ(val, 1); + + WHAL_ASSERT_EQ(whal_Gpio_Set(BOARD_GPIO_DEV, BOARD_LED_PIN, 0), + WHAL_SUCCESS); + whal_Reg_Get(PortBase(led), GPIOx_ODR_REG, 1UL << pin, pin, &val); + WHAL_ASSERT_EQ(val, 0); +} + +void whal_Test_Gpio_Platform(void) +{ + WHAL_TEST(Test_Gpio_PinTableRegisters); + WHAL_TEST(Test_Gpio_SetLedReg); +} diff --git a/tests/gpio/test_stm32wb_gpio.c b/tests/gpio/test_stm32wb_gpio.c index 58bd9d0..aa5c2bc 100644 --- a/tests/gpio/test_stm32wb_gpio.c +++ b/tests/gpio/test_stm32wb_gpio.c @@ -31,7 +31,12 @@ * supported STM32 families. */ #define GPIOx_MODE_REG 0x00 +#define GPIOx_OTYPER_REG 0x04 +#define GPIOx_OSPEEDR_REG 0x08 +#define GPIOx_PUPDR_REG 0x0C #define GPIOx_ODR_REG 0x14 +#define GPIOx_AFRL_REG 0x20 +#define GPIOx_AFRH_REG 0x24 #define GPIOx_STRIDE 0x400 static inline size_t Board_LedPortBase(void) @@ -91,6 +96,45 @@ static void Test_Gpio_ModeRegister(void) WHAL_ASSERT_EQ(val, WHAL_STM32WB_GPIO_MODE_OUT); } +/* Every pin in the board table reads back with its configured mode, output + * type, speed, pull and, for alternate-function pins, AF number. */ +static void Test_Gpio_PinTableRegisters(void) +{ + const whal_Stm32wb_Gpio_Cfg *cfg = (const whal_Stm32wb_Gpio_Cfg *)whal_Stm32wb_Gpio_Dev.cfg; + + for (size_t i = 0; i < cfg->pinCount; i++) { + whal_Stm32wb_Gpio_PinCfg p = cfg->pinCfg[i]; + size_t base = whal_Stm32wb_Gpio_Dev.base + + WHAL_STM32WB_GPIO_GET_PORT(p) * GPIOx_STRIDE; + size_t pin = WHAL_STM32WB_GPIO_GET_PIN(p); + size_t pos2 = pin << 1; + size_t val = 0; + + whal_Reg_Get(base, GPIOx_MODE_REG, WHAL_BITMASK(2) << pos2, pos2, + &val); + WHAL_ASSERT_EQ(val, WHAL_STM32WB_GPIO_GET_MODE(p)); + + whal_Reg_Get(base, GPIOx_OTYPER_REG, 1UL << pin, pin, &val); + WHAL_ASSERT_EQ(val, WHAL_STM32WB_GPIO_GET_OUTTYPE(p)); + + whal_Reg_Get(base, GPIOx_OSPEEDR_REG, WHAL_BITMASK(2) << pos2, pos2, + &val); + WHAL_ASSERT_EQ(val, WHAL_STM32WB_GPIO_GET_SPEED(p)); + + whal_Reg_Get(base, GPIOx_PUPDR_REG, WHAL_BITMASK(2) << pos2, pos2, + &val); + WHAL_ASSERT_EQ(val, WHAL_STM32WB_GPIO_GET_PULL(p)); + + if (WHAL_STM32WB_GPIO_GET_MODE(p) == WHAL_STM32WB_GPIO_MODE_ALTFN) { + size_t afPos = (pin & 7) << 2; + + whal_Reg_Get(base, (pin < 8) ? GPIOx_AFRL_REG : GPIOx_AFRH_REG, + WHAL_BITMASK(4) << afPos, afPos, &val); + WHAL_ASSERT_EQ(val, WHAL_STM32WB_GPIO_GET_ALTFN(p)); + } + } +} + static void Test_Gpio_SetHighReg(void) { WHAL_ASSERT_EQ(whal_Gpio_Set(BOARD_GPIO_DEV, BOARD_LED_PIN, 1), WHAL_SUCCESS); @@ -113,11 +157,26 @@ static void Test_Gpio_SetLowReg(void) WHAL_ASSERT_EQ(val, 0); } +static void Test_Gpio_SetNonzeroReg(void) +{ + size_t pinNum = Board_LedPinNum(); + size_t val = 0; + + WHAL_ASSERT_EQ(whal_Gpio_Set(BOARD_GPIO_DEV, BOARD_LED_PIN, 2), WHAL_SUCCESS); + whal_Reg_Get(Board_LedPortBase(), GPIOx_ODR_REG, (1UL << pinNum), + pinNum, &val); + WHAL_ASSERT_EQ(val, 1); + + WHAL_ASSERT_EQ(whal_Gpio_Set(BOARD_GPIO_DEV, BOARD_LED_PIN, 0), WHAL_SUCCESS); +} + void whal_Test_Gpio_Platform(void) { WHAL_TEST(Test_Gpio_PinCfgRoundTrip); WHAL_TEST(Test_Gpio_NoDuplicatePins); WHAL_TEST(Test_Gpio_ModeRegister); + WHAL_TEST(Test_Gpio_PinTableRegisters); WHAL_TEST(Test_Gpio_SetHighReg); WHAL_TEST(Test_Gpio_SetLowReg); + WHAL_TEST(Test_Gpio_SetNonzeroReg); } diff --git a/tests/gpio/test_stm32wba_gpio.c b/tests/gpio/test_stm32wba_gpio.c new file mode 100644 index 0000000..05a7081 --- /dev/null +++ b/tests/gpio/test_stm32wba_gpio.c @@ -0,0 +1,22 @@ +/* test_stm32wba_gpio.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHAL. + * + * wolfHAL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHAL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include "test_stm32wb_gpio.c" diff --git a/wolfHAL/gpio/gpio.h b/wolfHAL/gpio/gpio.h index a0e35ad..175d5d6 100644 --- a/wolfHAL/gpio/gpio.h +++ b/wolfHAL/gpio/gpio.h @@ -92,7 +92,7 @@ whal_Error whal_Gpio_Get(whal_Gpio *gpioDev, size_t pin, size_t *value); * * @param gpioDev GPIO instance containing the pin. * @param pin Pin index into the configured pin table. - * @param value Output value to drive (typically 0 or 1). + * @param value Output value to drive; any nonzero value drives the pin high. * * @retval WHAL_SUCCESS Pin updated. * @retval WHAL_EINVAL Null pointer or bad pin. From 3a9336e4d63d5437db4554a39e8674749f1784af Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Wed, 14 Oct 2026 12:57:42 -0400 Subject: [PATCH 16/22] [eth, stm32h5, stm32n6, test] Validate descriptor and buffer config, add send length tests - H5 Init rejects missing descriptor or buffer memory, zero counts and zero buffer sizes, matching N6 - H5 and N6 Init reject RX buffers that are not a multiple of the RBSZ granularity (4 bytes on H5, 8 on N6) or exceed 14 bits, and TX buffers that exceed the 14-bit length field - Document that N6 descriptors and buffers must be in non-cacheable memory - Test that Send rejects zero and oversize lengths - Add H5 and N6 loopback tests that a frame of exactly the TX buffer size is accepted --- src/eth/stm32h5_eth.c | 13 +++++++++++++ src/eth/stm32n6_eth.c | 10 +++++++++- tests/eth/test_eth.c | 11 +++++++++++ tests/eth/test_stm32h5_eth.c | 34 ++++++++++++++++++++++++++++++++++ tests/eth/test_stm32n6_eth.c | 34 ++++++++++++++++++++++++++++++++++ wolfHAL/eth/stm32h5_eth.h | 7 ++++--- wolfHAL/eth/stm32n6_eth.h | 11 ++++++++--- 7 files changed, 113 insertions(+), 7 deletions(-) diff --git a/src/eth/stm32h5_eth.c b/src/eth/stm32h5_eth.c index 06e1693..44e2168 100644 --- a/src/eth/stm32h5_eth.c +++ b/src/eth/stm32h5_eth.c @@ -117,6 +117,12 @@ static struct { #define ETH_DMACRXCR_SR_Msk (1UL << ETH_DMACRXCR_SR_Pos) #define ETH_DMACRXCR_RBSZ_Pos 1 #define ETH_DMACRXCR_RBSZ_Msk (0x3FFFUL << ETH_DMACRXCR_RBSZ_Pos) + +/* RBSZ ignores bits 1:0, so RX buffers are a multiple of 4 bytes, and the + * TX buffer 1 length field is 14 bits */ +#define ETH_RX_BUF_ALIGN_Msk 0x3UL +#define ETH_RX_BUF_MAX 0x3FFCUL +#define ETH_TX_BUF_MAX 0x3FFFUL #define ETH_DMACRXCR_RXPBL_Pos 16 #define ETH_DMACRXCR_RXPBL_Msk (0x3FUL << ETH_DMACRXCR_RXPBL_Pos) @@ -180,6 +186,13 @@ whal_Error whal_Stm32h5_Eth_Init(whal_Eth *ethDev) (void)ethDev; + if (!cfg->txDescs || !cfg->txBufs || cfg->txDescCount == 0 || + !cfg->rxDescs || !cfg->rxBufs || cfg->rxDescCount == 0 || + cfg->txBufSize == 0 || cfg->txBufSize > ETH_TX_BUF_MAX || + cfg->rxBufSize == 0 || cfg->rxBufSize > ETH_RX_BUF_MAX || + (cfg->rxBufSize & ETH_RX_BUF_ALIGN_Msk)) + return WHAL_EINVAL; + /* DMA software reset */ whal_Reg_Update(base, ETH_DMAMR_REG, ETH_DMAMR_SWR_Msk, ETH_DMAMR_SWR_Msk); diff --git a/src/eth/stm32n6_eth.c b/src/eth/stm32n6_eth.c index 91c230e..80303c6 100644 --- a/src/eth/stm32n6_eth.c +++ b/src/eth/stm32n6_eth.c @@ -136,6 +136,12 @@ static struct { #define ETH_DMAC0RXCR_SR_Msk (1UL << ETH_DMAC0RXCR_SR_Pos) #define ETH_DMAC0RXCR_RBSZ_Pos 1 #define ETH_DMAC0RXCR_RBSZ_Msk (0x3FFFUL << ETH_DMAC0RXCR_RBSZ_Pos) + +/* RBSZ ignores bits 2:0, so RX buffers are a multiple of 8 bytes, and the + * TX buffer 1 length field is 14 bits */ +#define ETH_RX_BUF_ALIGN_Msk 0x7UL +#define ETH_RX_BUF_MAX 0x3FF8UL +#define ETH_TX_BUF_MAX 0x3FFFUL #define ETH_DMAC0RXCR_RXPBL_Pos 16 #define ETH_DMAC0RXCR_RXPBL_Msk (0x3FUL << ETH_DMAC0RXCR_RXPBL_Pos) @@ -193,7 +199,9 @@ whal_Error whal_Stm32n6_Eth_Init(whal_Eth *ethDev) if (!cfg->txDescs || !cfg->txBufs || cfg->txDescCount == 0 || !cfg->rxDescs || !cfg->rxBufs || cfg->rxDescCount == 0 || - cfg->txBufSize == 0 || cfg->rxBufSize == 0) + cfg->txBufSize == 0 || cfg->txBufSize > ETH_TX_BUF_MAX || + cfg->rxBufSize == 0 || cfg->rxBufSize > ETH_RX_BUF_MAX || + (cfg->rxBufSize & ETH_RX_BUF_ALIGN_Msk)) return WHAL_EINVAL; /* DMA software reset */ diff --git a/tests/eth/test_eth.c b/tests/eth/test_eth.c index f767ff8..ad80596 100644 --- a/tests/eth/test_eth.c +++ b/tests/eth/test_eth.c @@ -49,6 +49,16 @@ static void Test_Eth_Api(void) WHAL_ASSERT_EQ(whal_Eth_MdioRead(BOARD_ETH_DEV, 0, 0, NULL), WHAL_EINVAL); } +/* Zero-length and oversize frames are rejected before touching a descriptor */ +static void Test_Eth_SendBadLength(void) +{ + static uint8_t frame[BOARD_ETH_TX_BUF_SIZE + 1]; + + WHAL_ASSERT_EQ(whal_Eth_Send(BOARD_ETH_DEV, frame, 0), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Eth_Send(BOARD_ETH_DEV, frame, sizeof(frame)), + WHAL_EINVAL); +} + static void Test_Eth_MdioReadPhyId(void) { uint16_t id1 = 0; @@ -89,6 +99,7 @@ void whal_Test_Eth(void) { WHAL_TEST_SUITE_START("eth"); WHAL_TEST(Test_Eth_Api); + WHAL_TEST(Test_Eth_SendBadLength); WHAL_TEST(Test_Eth_MdioReadPhyId); WHAL_TEST(Test_Eth_PhyGetLinkState); WHAL_TEST_SUITE_END(); diff --git a/tests/eth/test_stm32h5_eth.c b/tests/eth/test_stm32h5_eth.c index f4ed575..60321b5 100644 --- a/tests/eth/test_stm32h5_eth.c +++ b/tests/eth/test_stm32h5_eth.c @@ -123,10 +123,44 @@ static void Test_Eth_UndersizedRecv(void) WHAL_SUCCESS); } +/* A frame of exactly the configured TX buffer size is accepted. It is longer + * than a standard Ethernet frame, so the looped-back copy may be flagged as a + * giant packet; the RX ring is only drained, not checked. */ +static void Test_Eth_SendMaxLength(void) +{ + static uint8_t frame[BOARD_ETH_TX_BUF_SIZE]; + uint8_t rxFrame[1536]; + size_t rxLen; + uint32_t start; + + for (size_t i = 0; i < sizeof(frame); i++) + frame[i] = (uint8_t)i; + for (size_t i = 0; i < 6; i++) + frame[i] = 0xFF; + + WHAL_ASSERT_EQ(whal_Stm32h5_Eth_Ext_EnableLoopback(BOARD_ETH_DEV, 1), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Eth_Start(BOARD_ETH_DEV, WHAL_ETH_SPEED_100, + WHAL_ETH_DUPLEX_FULL), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Eth_Send(BOARD_ETH_DEV, frame, sizeof(frame)), + WHAL_SUCCESS); + + start = g_tick; + while ((g_tick - start) < 100) { + rxLen = sizeof(rxFrame); + (void)whal_Eth_Recv(BOARD_ETH_DEV, rxFrame, &rxLen); + } + + WHAL_ASSERT_EQ(whal_Eth_Stop(BOARD_ETH_DEV), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Stm32h5_Eth_Ext_EnableLoopback(BOARD_ETH_DEV, 0), + WHAL_SUCCESS); +} + void whal_Test_Eth_Platform(void) { WHAL_TEST_SUITE_START("eth_platform"); WHAL_TEST(Test_Eth_Loopback); WHAL_TEST(Test_Eth_UndersizedRecv); + WHAL_TEST(Test_Eth_SendMaxLength); WHAL_TEST_SUITE_END(); } diff --git a/tests/eth/test_stm32n6_eth.c b/tests/eth/test_stm32n6_eth.c index 78f93d4..00ceeec 100644 --- a/tests/eth/test_stm32n6_eth.c +++ b/tests/eth/test_stm32n6_eth.c @@ -187,10 +187,44 @@ static void Test_Eth_UndersizedRecv(void) WHAL_SUCCESS); } +/* A frame of exactly the configured TX buffer size is accepted. It is longer + * than a standard Ethernet frame, so the looped-back copy may be flagged as a + * giant packet; the RX ring is only drained, not checked. */ +static void Test_Eth_SendMaxLength(void) +{ + static uint8_t frame[BOARD_ETH_TX_BUF_SIZE]; + uint8_t rxFrame[1536]; + size_t rxLen; + uint32_t start; + + for (size_t i = 0; i < sizeof(frame); i++) + frame[i] = (uint8_t)i; + for (size_t i = 0; i < 6; i++) + frame[i] = 0xFF; + + WHAL_ASSERT_EQ(whal_Stm32n6_Eth_Ext_EnableLoopback(BOARD_ETH_DEV, 1), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Eth_Start(BOARD_ETH_DEV, WHAL_ETH_SPEED_100, + WHAL_ETH_DUPLEX_FULL), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Eth_Send(BOARD_ETH_DEV, frame, sizeof(frame)), + WHAL_SUCCESS); + + start = g_tick; + while ((g_tick - start) < 100) { + rxLen = sizeof(rxFrame); + (void)whal_Eth_Recv(BOARD_ETH_DEV, rxFrame, &rxLen); + } + + WHAL_ASSERT_EQ(whal_Eth_Stop(BOARD_ETH_DEV), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Stm32n6_Eth_Ext_EnableLoopback(BOARD_ETH_DEV, 0), + WHAL_SUCCESS); +} + void whal_Test_Eth_Platform(void) { WHAL_TEST_SUITE_START("eth_platform"); WHAL_TEST(Test_Eth_Loopback); WHAL_TEST(Test_Eth_UndersizedRecv); + WHAL_TEST(Test_Eth_SendMaxLength); WHAL_TEST_SUITE_END(); } diff --git a/wolfHAL/eth/stm32h5_eth.h b/wolfHAL/eth/stm32h5_eth.h index 7b9c3e1..06d0b9c 100644 --- a/wolfHAL/eth/stm32h5_eth.h +++ b/wolfHAL/eth/stm32h5_eth.h @@ -56,11 +56,11 @@ typedef struct whal_Stm32h5_Eth_Cfg { whal_Stm32h5_Eth_TxDesc *txDescs; /* TX descriptor ring (pre-allocated) */ uint8_t *txBufs; /* TX frame buffers (pre-allocated) */ size_t txDescCount; /* Number of TX descriptors */ - size_t txBufSize; /* Size of each TX buffer in bytes */ + size_t txBufSize; /* TX buffer bytes, max 16383 */ whal_Stm32h5_Eth_RxDesc *rxDescs; /* RX descriptor ring (pre-allocated) */ uint8_t *rxBufs; /* RX frame buffers (pre-allocated) */ size_t rxDescCount; /* Number of RX descriptors */ - size_t rxBufSize; /* Size of each RX buffer in bytes */ + size_t rxBufSize; /* RX buffer bytes, 4n, max 16380 */ /* MDIO clock range select (CR field in MACMDIOAR). Picks the AHB->MDC * divider for the board's HCLK; MDC must stay <= 2.5 MHz (IEEE 802.3). * 0: 60-100 MHz (/42) 1: 100-150 MHz (/62) @@ -91,7 +91,8 @@ extern const whal_EthDriver whal_Stm32h5_Eth_Driver; * @param ethDev Ethernet device instance. * * @retval WHAL_SUCCESS Initialization completed. - * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_EINVAL Missing descriptor or buffer memory, a zero count, + * or a buffer size the DMA cannot represent. */ whal_Error whal_Stm32h5_Eth_Init(whal_Eth *ethDev); diff --git a/wolfHAL/eth/stm32n6_eth.h b/wolfHAL/eth/stm32n6_eth.h index a03d077..fd57598 100644 --- a/wolfHAL/eth/stm32n6_eth.h +++ b/wolfHAL/eth/stm32n6_eth.h @@ -41,6 +41,10 @@ * The driver uses descriptor rings in RAM for TX and RX DMA transfers. * Descriptor and buffer memory must be provided by the board configuration. * Descriptors must be 8-byte aligned. + * + * The driver does no data-cache maintenance, so the descriptor rings and + * buffers must be in memory the core does not cache (for example the + * non-cacheable AXISRAM1 alias used by the stm32n657a0_nucleo board). */ #include @@ -65,11 +69,11 @@ typedef struct whal_Stm32n6_Eth_Cfg { whal_Stm32n6_Eth_TxDesc *txDescs; /**< TX descriptor ring (8-byte aligned) */ uint8_t *txBufs; /**< TX frame buffers */ size_t txDescCount; /**< Number of TX descriptors */ - size_t txBufSize; /**< Size of each TX buffer in bytes */ + size_t txBufSize; /**< TX buffer bytes, max 16383 */ whal_Stm32n6_Eth_RxDesc *rxDescs; /**< RX descriptor ring (8-byte aligned) */ uint8_t *rxBufs; /**< RX frame buffers */ size_t rxDescCount; /**< Number of RX descriptors */ - size_t rxBufSize; /**< Size of each RX buffer in bytes */ + size_t rxBufSize; /**< RX buffer bytes, 8n, max 16376 */ /* MDIO clock range select (CR field in MACMDIOAR). Picks the AXI->MDC * divider for the board's HCLK; MDC must stay <= 2.5 MHz (IEEE 802.3). * 0: 60-100 MHz (/42) 1: 100-150 MHz (/62) @@ -100,7 +104,8 @@ extern const whal_EthDriver whal_Stm32n6_Eth_Driver; * @param ethDev Ethernet device instance. * * @retval WHAL_SUCCESS MAC initialized. - * @retval WHAL_EINVAL Null pointer or missing cfg. + * @retval WHAL_EINVAL Missing descriptor or buffer memory, a zero count, + * or a buffer size the DMA cannot represent. * @retval WHAL_ETIMEOUT DMA software-reset did not clear within the timeout. */ whal_Error whal_Stm32n6_Eth_Init(whal_Eth *ethDev); From d22459af1550e4502a9f391bccb0b5e6bc832e8c Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Wed, 14 Oct 2026 13:14:27 -0400 Subject: [PATCH 17/22] [timer, pwm, stm32wb, test] Stop SysTick on Deinit, reject undefined PWM polarity, add SysTick and full-duty tests - SysTick Deinit clears ENABLE and TICKINT and zeroes the counter - PWM Start (generic dispatch and LPTIM) rejects polarities other than NORMAL and INVERTED - Add a WB55 SysTick test that reads back RVR, CLKSOURCE and TICKINT, and checks Stop, Start and Deinit - Add dispatch tests for 100% duty, inverted polarity and an undefined polarity - Test that 100% duty reaches the PWM driver on hardware --- src/pwm/pwm.c | 4 +- src/pwm/stm32wb_lptim_pwm.c | 4 +- src/timer/systick.c | 6 ++ tests/core/test_dispatch.c | 14 +++ tests/pwm/test_pwm.c | 25 ++++++ tests/pwm/test_stm32wb_pwm.c | 5 ++ tests/timer/test_stm32wb_timer.c | 141 +++++++++++++++++++++++++++++++ wolfHAL/pwm/pwm.h | 3 +- wolfHAL/pwm/stm32wb_lptim_pwm.h | 4 +- wolfHAL/timer/systick.h | 2 + 10 files changed, 203 insertions(+), 5 deletions(-) create mode 100644 tests/timer/test_stm32wb_timer.c diff --git a/src/pwm/pwm.c b/src/pwm/pwm.c index b03221f..76d9f83 100644 --- a/src/pwm/pwm.c +++ b/src/pwm/pwm.c @@ -46,7 +46,9 @@ inline whal_Error whal_Pwm_Start(whal_Pwm *dev, uint8_t channel, const whal_Pwm_ChannelCfg *cfg) { if (!dev || !cfg || cfg->periodCycles == 0 || - cfg->pulseCycles > cfg->periodCycles) + cfg->pulseCycles > cfg->periodCycles || + (cfg->polarity != WHAL_PWM_POLARITY_NORMAL && + cfg->polarity != WHAL_PWM_POLARITY_INVERTED)) return WHAL_EINVAL; if (!dev->driver || !dev->driver->Start) return WHAL_ENOTSUP; diff --git a/src/pwm/stm32wb_lptim_pwm.c b/src/pwm/stm32wb_lptim_pwm.c index 2184cd7..3fa4a46 100644 --- a/src/pwm/stm32wb_lptim_pwm.c +++ b/src/pwm/stm32wb_lptim_pwm.c @@ -105,7 +105,9 @@ whal_Error whal_Stm32wb_Lptim_Pwm_Start(whal_Pwm *dev, uint8_t channel, if (!dev || !dev->cfg || !channelCfg || channelCfg->periodCycles == 0 || - channelCfg->pulseCycles > channelCfg->periodCycles) { + channelCfg->pulseCycles > channelCfg->periodCycles || + (channelCfg->polarity != WHAL_PWM_POLARITY_NORMAL && + channelCfg->polarity != WHAL_PWM_POLARITY_INVERTED)) { return WHAL_EINVAL; } if (channel != WHAL_STM32WB_LPTIM_PWM_CHANNEL) { diff --git a/src/timer/systick.c b/src/timer/systick.c index a432fe6..4581ee6 100644 --- a/src/timer/systick.c +++ b/src/timer/systick.c @@ -83,7 +83,13 @@ whal_Error whal_SysTick_Init(whal_Timer *timerDev) whal_Error whal_SysTick_Deinit(whal_Timer *timerDev) { + size_t base = whal_SysTick_Dev.base; (void)timerDev; + + whal_Reg_Update(base, SYSTICK_CSR_REG, + SYSTICK_CSR_ENABLE_Msk | SYSTICK_CSR_TICKINT_Msk, 0); + whal_Reg_Write(base, SYSTICK_CVR_REG, 0); + return WHAL_SUCCESS; } diff --git a/tests/core/test_dispatch.c b/tests/core/test_dispatch.c index a189d9b..6ff27c7 100644 --- a/tests/core/test_dispatch.c +++ b/tests/core/test_dispatch.c @@ -383,15 +383,29 @@ static void Test_Pwm_BadWave(void) WHAL_ASSERT_EQ(whal_Pwm_Start(&dev, 0, NULL), WHAL_EINVAL); WHAL_ASSERT_EQ(whal_Pwm_Start(&dev, 0, &cfg), WHAL_EINVAL); WHAL_ASSERT_EQ(whal_Pwm_Start(&dev, 0, &zero), WHAL_EINVAL); + cfg.pulseCycles = 50; + cfg.polarity = WHAL_PWM_POLARITY_INVERTED + 1; + WHAL_ASSERT_EQ(whal_Pwm_Start(&dev, 0, &cfg), WHAL_EINVAL); } static void Test_Pwm_ValidDispatch(void) { whal_Pwm dev = { .driver = &mockPwmDriver }; whal_Pwm_ChannelCfg cfg = { .periodCycles = 100, .pulseCycles = 50 }; + whal_Pwm_ChannelCfg full = { .periodCycles = 100, .pulseCycles = 100 }; + whal_Pwm_ChannelCfg inverted = { + .periodCycles = 100, + .pulseCycles = 50, + .polarity = WHAL_PWM_POLARITY_INVERTED, + }; WHAL_ASSERT_EQ(whal_Pwm_Init(&dev), WHAL_SUCCESS); WHAL_ASSERT_EQ(whal_Pwm_Start(&dev, 0, &cfg), WHAL_SUCCESS); WHAL_ASSERT_EQ(whal_Pwm_Stop(&dev, 0), WHAL_SUCCESS); + /* 100% duty (pulse == period) passes the dispatch check */ + WHAL_ASSERT_EQ(whal_Pwm_Start(&dev, 0, &full), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Pwm_Stop(&dev, 0), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Pwm_Start(&dev, 0, &inverted), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Pwm_Stop(&dev, 0), WHAL_SUCCESS); WHAL_ASSERT_EQ(whal_Pwm_Deinit(&dev), WHAL_SUCCESS); } diff --git a/tests/pwm/test_pwm.c b/tests/pwm/test_pwm.c index 05d448d..2b781c8 100644 --- a/tests/pwm/test_pwm.c +++ b/tests/pwm/test_pwm.c @@ -46,11 +46,36 @@ static void Test_Pwm_Api(void) WHAL_ASSERT_EQ(whal_Pwm_Start(NULL, 0, &wave), WHAL_EINVAL); WHAL_ASSERT_EQ(whal_Pwm_Start(BOARD_PWM_DEV, 0, NULL), WHAL_EINVAL); + + /* Only the two documented polarities are accepted. */ + wave.polarity = WHAL_PWM_POLARITY_INVERTED + 1; + WHAL_ASSERT_EQ(whal_Pwm_Start(BOARD_PWM_DEV, 0, &wave), WHAL_EINVAL); + wave.polarity = WHAL_PWM_POLARITY_NORMAL; +} + +/* 100% duty is structurally valid, so the dispatch passes it to the driver; + * a driver that cannot produce it reports WHAL_ENOTSUP, never WHAL_EINVAL. */ +static void Test_Pwm_FullDuty(void) +{ + whal_Pwm_ChannelCfg wave = { + .periodCycles = 1000, + .pulseCycles = 1000, + .pulseCount = WHAL_PWM_PULSE_COUNT_CONTINUOUS, + .polarity = WHAL_PWM_POLARITY_NORMAL, + }; + whal_Error err; + + err = whal_Pwm_Start(BOARD_PWM_DEV, 0, &wave); + if (err == WHAL_SUCCESS) + WHAL_ASSERT_EQ(whal_Pwm_Stop(BOARD_PWM_DEV, 0), WHAL_SUCCESS); + else + WHAL_ASSERT_EQ(err, WHAL_ENOTSUP); } void whal_Test_Pwm(void) { WHAL_TEST_SUITE_START("pwm"); WHAL_TEST(Test_Pwm_Api); + WHAL_TEST(Test_Pwm_FullDuty); WHAL_TEST_SUITE_END(); } diff --git a/tests/pwm/test_stm32wb_pwm.c b/tests/pwm/test_stm32wb_pwm.c index bb68ea4..c6496c6 100644 --- a/tests/pwm/test_stm32wb_pwm.c +++ b/tests/pwm/test_stm32wb_pwm.c @@ -42,6 +42,11 @@ static void Test_Stm32wb_Lptim_Pwm_Reject(void) WHAL_STM32WB_LPTIM_PWM_CHANNEL, &wave), WHAL_EINVAL); wave.pulseCycles = 500; + wave.polarity = WHAL_PWM_POLARITY_INVERTED + 1; + WHAL_ASSERT_EQ(whal_Stm32wb_Lptim_Pwm_Start(BOARD_PWM_DEV, + WHAL_STM32WB_LPTIM_PWM_CHANNEL, &wave), WHAL_EINVAL); + wave.polarity = WHAL_PWM_POLARITY_NORMAL; + wave.periodCycles = 0; WHAL_ASSERT_EQ(whal_Stm32wb_Lptim_Pwm_Start(BOARD_PWM_DEV, WHAL_STM32WB_LPTIM_PWM_CHANNEL, &wave), WHAL_EINVAL); diff --git a/tests/timer/test_stm32wb_timer.c b/tests/timer/test_stm32wb_timer.c new file mode 100644 index 0000000..806078a --- /dev/null +++ b/tests/timer/test_stm32wb_timer.c @@ -0,0 +1,141 @@ +/* test_stm32wb_timer.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHAL. + * + * wolfHAL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHAL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include "wolfHAL_board.h" +#include "test.h" + +#define SYSTICK_CSR 0x00 +#define SYSTICK_RVR 0x04 +#define SYSTICK_CVR 0x08 + +#define CSR_ENABLE (1UL << 0) +#define CSR_TICKINT (1UL << 1) +#define CSR_CLKSOURCE (1UL << 2) + +#define RVR_RELOAD_Msk 0x00FFFFFFUL + +/* Spin budget for one tick to elapse; far longer than a 1 ms tick */ +#define TICK_SPIN_MAX 10000000UL + +static size_t SysTickBase(void) +{ + return whal_SysTick_Dev.base; +} + +static const whal_SysTick_Cfg *SysTickCfg(void) +{ + return (const whal_SysTick_Cfg *)whal_SysTick_Dev.cfg; +} + +static int TickAdvances(void) +{ + uint32_t before = g_tick; + + for (volatile size_t i = 0; i < TICK_SPIN_MAX; i++) { + if (g_tick != before) + return 1; + } + + return 0; +} + +static void CheckConfig(void) +{ + const whal_SysTick_Cfg *cfg = SysTickCfg(); + size_t csr = whal_Reg_Read(SysTickBase(), SYSTICK_CSR); + + WHAL_ASSERT_EQ(whal_Reg_Read(SysTickBase(), SYSTICK_RVR) & RVR_RELOAD_Msk, + cfg->cyclesPerTick - 1); + WHAL_ASSERT_EQ((csr & CSR_CLKSOURCE) != 0, + cfg->clkSrc == WHAL_SYSTICK_CLKSRC_SYSCLK); + WHAL_ASSERT_EQ((csr & CSR_TICKINT) != 0, + cfg->tickInt == WHAL_SYSTICK_TICKINT_ENABLED); + WHAL_ASSERT_EQ(csr & CSR_ENABLE, CSR_ENABLE); +} + +/* Board_Init programmed the reload value, clock source and tick interrupt + * from the board's whal_SysTick_Cfg and started the counter. */ +static void Test_SysTick_ConfigReadback(void) +{ + CheckConfig(); +} + +static void Test_SysTick_StopStart(void) +{ + whal_Error stopErr; + size_t csr; + size_t cvr1, cvr2; + + stopErr = whal_Timer_Stop(WHAL_INTERNAL_DEV); + csr = whal_Reg_Read(SysTickBase(), SYSTICK_CSR); + + /* A stopped counter holds its value */ + cvr1 = whal_Reg_Read(SysTickBase(), SYSTICK_CVR); + for (volatile size_t i = 0; i < 1000; i++) {} + cvr2 = whal_Reg_Read(SysTickBase(), SYSTICK_CVR); + + /* Restart before asserting so later tests keep a time base */ + WHAL_ASSERT_EQ(whal_Timer_Start(WHAL_INTERNAL_DEV), WHAL_SUCCESS); + + WHAL_ASSERT_EQ(stopErr, WHAL_SUCCESS); + WHAL_ASSERT_EQ(csr & CSR_ENABLE, 0); + WHAL_ASSERT_EQ(cvr1, cvr2); + + WHAL_ASSERT_EQ(whal_Reg_Read(SysTickBase(), SYSTICK_CSR) & CSR_ENABLE, + CSR_ENABLE); + WHAL_ASSERT_EQ(TickAdvances(), 1); +} + +static void Test_SysTick_Deinit(void) +{ + whal_Error deinitErr; + size_t csr; + size_t cvr; + + deinitErr = whal_Timer_Deinit(WHAL_INTERNAL_DEV); + csr = whal_Reg_Read(SysTickBase(), SYSTICK_CSR); + cvr = whal_Reg_Read(SysTickBase(), SYSTICK_CVR); + + /* Restore the tick before asserting so later tests keep a time base */ + WHAL_ASSERT_EQ(whal_Timer_Init(WHAL_INTERNAL_DEV), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Timer_Start(WHAL_INTERNAL_DEV), WHAL_SUCCESS); + + WHAL_ASSERT_EQ(deinitErr, WHAL_SUCCESS); + WHAL_ASSERT_EQ(csr & (CSR_ENABLE | CSR_TICKINT), 0); + WHAL_ASSERT_EQ(cvr, 0); + + CheckConfig(); + if (g_whalTestCurFailed) + return; + + WHAL_ASSERT_EQ(TickAdvances(), 1); +} + +void whal_Test_Timer_Platform(void) +{ + WHAL_TEST_SUITE_START("timer_platform"); + WHAL_TEST(Test_SysTick_ConfigReadback); + WHAL_TEST(Test_SysTick_StopStart); + WHAL_TEST(Test_SysTick_Deinit); + WHAL_TEST_SUITE_END(); +} diff --git a/wolfHAL/pwm/pwm.h b/wolfHAL/pwm/pwm.h index c64301d..6c7302c 100644 --- a/wolfHAL/pwm/pwm.h +++ b/wolfHAL/pwm/pwm.h @@ -124,7 +124,8 @@ whal_Error whal_Pwm_Deinit(whal_Pwm *dev); * @param cfg Waveform (period, pulse, polarity) to apply. * * @retval WHAL_SUCCESS Output started. - * @retval WHAL_EINVAL Null pointer, zero period, or pulse exceeds period. + * @retval WHAL_EINVAL Null pointer, zero period, pulse exceeds period, or + * undefined polarity. * @retval WHAL_ENOTSUP No driver bound, or channel/waveform unsupported. */ whal_Error whal_Pwm_Start(whal_Pwm *dev, uint8_t channel, diff --git a/wolfHAL/pwm/stm32wb_lptim_pwm.h b/wolfHAL/pwm/stm32wb_lptim_pwm.h index 5186db7..f4574e4 100644 --- a/wolfHAL/pwm/stm32wb_lptim_pwm.h +++ b/wolfHAL/pwm/stm32wb_lptim_pwm.h @@ -120,8 +120,8 @@ whal_Error whal_Stm32wb_Lptim_Pwm_Deinit(whal_Pwm *dev); * @param cfg Waveform (period, pulse, polarity) to apply. * * @retval WHAL_SUCCESS Output started. - * @retval WHAL_EINVAL Null pointer, missing configuration, zero period, or - * pulse exceeding period. + * @retval WHAL_EINVAL Null pointer, missing configuration, zero period, + * pulse exceeding period, or undefined polarity. * @retval WHAL_ENOTSUP Unsupported channel, period out of the 16-bit range or * below 2, pulse of 0 or equal to the period, or a * non-continuous pulse count. diff --git a/wolfHAL/timer/systick.h b/wolfHAL/timer/systick.h index 1028317..80db3a8 100644 --- a/wolfHAL/timer/systick.h +++ b/wolfHAL/timer/systick.h @@ -81,6 +81,8 @@ whal_Error whal_SysTick_Init(whal_Timer *timerDev); /* * @brief Deinitialize the SysTick timer. * + * Stops the counter, disables the tick interrupt and clears the counter. + * * @param timerDev Timer device instance to deinitialize. * * @retval WHAL_SUCCESS Deinit completed. From eb0fd11923e0a20a5872f767bf382c01263096c3 Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Wed, 14 Oct 2026 13:24:00 -0400 Subject: [PATCH 18/22] [sensor, display, test] Report cleanup errors in BMI270 and Sharp LCD, add BMI270 decode and gravity tests - BMI270 Init, Deinit and Read return an I2C EndCom failure when the transfer succeeded - Sharp LCD Clear, Init, Deinit and Update run every cleanup step (SCS deassert, SPI EndCom, PWM Stop) and return the first error - Sharp LCD Init stops the VCOM PWM on any failure, including EndCom - Move the BMI270 parse into whal_Bmi270_Decode and test it against a fixed buffer - Replace the nonzero accel check with a gravity magnitude check --- src/display/sharp_memory_display.c | 37 ++++++++++++----- src/sensor/imu/bmi270_sensor.c | 35 +++++++++++----- tests/sensor/imu/test_bmi270_sensor.c | 58 ++++++++++++++++++++++++--- wolfHAL/sensor/imu/bmi270_sensor.h | 14 +++++++ 4 files changed, 118 insertions(+), 26 deletions(-) diff --git a/src/display/sharp_memory_display.c b/src/display/sharp_memory_display.c index 21d7773..91f5445 100644 --- a/src/display/sharp_memory_display.c +++ b/src/display/sharp_memory_display.c @@ -84,6 +84,7 @@ static whal_Error SharpMem_Clear(whal_SharpMemory_Display_Cfg *cfg) { uint8_t frame[2]; whal_Error err; + whal_Error stepErr; frame[0] = MODE_CLEAR; frame[1] = DUMMY; @@ -92,7 +93,9 @@ static whal_Error SharpMem_Clear(whal_SharpMemory_Display_Cfg *cfg) if (err) return err; err = whal_Spi_SendRecv(cfg->spiDev, frame, sizeof(frame), NULL, 0); - SharpMem_CsDeassert(cfg); + stepErr = SharpMem_CsDeassert(cfg); + if (stepErr && !err) + err = stepErr; return err; } @@ -100,6 +103,7 @@ whal_Error whal_SharpMemory_Display_Init(whal_Display *dev) { whal_SharpMemory_Display_Cfg *cfg; whal_Error err; + whal_Error stepErr; int pwmStarted = 0; #ifdef WHAL_CFG_SHARPMEMORY_DISPLAY_SINGLE_INSTANCE @@ -148,9 +152,11 @@ whal_Error whal_SharpMemory_Display_Init(whal_Display *dev) err = SharpMem_Clear(cfg); cleanup: + stepErr = whal_Spi_EndCom(cfg->spiDev); + if (stepErr && !err) + err = stepErr; if (err && pwmStarted) whal_Pwm_Stop(cfg->pwm, cfg->pwmChannel); - whal_Spi_EndCom(cfg->spiDev); return err; } @@ -158,6 +164,7 @@ whal_Error whal_SharpMemory_Display_Deinit(whal_Display *dev) { whal_SharpMemory_Display_Cfg *cfg; whal_Error err = WHAL_SUCCESS; + whal_Error stepErr; #ifdef WHAL_CFG_SHARPMEMORY_DISPLAY_SINGLE_INSTANCE cfg = (whal_SharpMemory_Display_Cfg *)whal_SharpMemory_Display_Dev.cfg; @@ -177,17 +184,22 @@ whal_Error whal_SharpMemory_Display_Deinit(whal_Display *dev) err = whal_Spi_StartCom(cfg->spiDev, cfg->spiComCfg); if (!err) { err = SharpMem_Clear(cfg); - whal_Spi_EndCom(cfg->spiDev); + stepErr = whal_Spi_EndCom(cfg->spiDev); + if (stepErr && !err) + err = stepErr; } } - if (cfg->vcomCfg) - whal_Pwm_Stop(cfg->pwm, cfg->pwmChannel); + if (cfg->vcomCfg) { + stepErr = whal_Pwm_Stop(cfg->pwm, cfg->pwmChannel); + if (stepErr && !err) + err = stepErr; + } - if (err == WHAL_SUCCESS) - return SharpMem_CsDeassert(cfg); + stepErr = SharpMem_CsDeassert(cfg); + if (stepErr && !err) + err = stepErr; - SharpMem_CsDeassert(cfg); return err; } @@ -202,6 +214,7 @@ whal_Error whal_SharpMemory_Display_Update(whal_Display *dev, uint16_t x, uint16 uint8_t trailer = DUMMY; uint16_t line; whal_Error err; + whal_Error stepErr; #ifdef WHAL_CFG_SHARPMEMORY_DISPLAY_SINGLE_INSTANCE cfg = (whal_SharpMemory_Display_Cfg *)whal_SharpMemory_Display_Dev.cfg; @@ -260,10 +273,14 @@ whal_Error whal_SharpMemory_Display_Update(whal_Display *dev, uint16_t x, uint16 if (!err) err = whal_Spi_SendRecv(cfg->spiDev, &trailer, 1, NULL, 0); - SharpMem_CsDeassert(cfg); + stepErr = SharpMem_CsDeassert(cfg); + if (stepErr && !err) + err = stepErr; cleanup: - whal_Spi_EndCom(cfg->spiDev); + stepErr = whal_Spi_EndCom(cfg->spiDev); + if (stepErr && !err) + err = stepErr; return err; } diff --git a/src/sensor/imu/bmi270_sensor.c b/src/sensor/imu/bmi270_sensor.c index b85a5b6..ec1959a 100644 --- a/src/sensor/imu/bmi270_sensor.c +++ b/src/sensor/imu/bmi270_sensor.c @@ -55,7 +55,7 @@ #define BMI270_GYR_RANGE_2000DPS 0x08 /* +/- 2000 deg/s */ /* Number of raw data bytes: accel (6) + gyro (6) */ -#define BMI270_DATA_LEN 12 +#define BMI270_DATA_LEN WHAL_BMI270_RAW_LEN #ifdef WHAL_CFG_BMI270_SENSOR_DIRECT_API_MAPPING #define whal_Bmi270_Init whal_Sensor_Init @@ -67,10 +67,21 @@ const whal_Sensor whal_Bmi270_Dev = WHAL_CFG_BMI270_DEV; #endif +void whal_Bmi270_Decode(const uint8_t *raw, whal_Bmi270_Data *out) +{ + out->accelX = (int16_t)(raw[0] | (raw[1] << 8)); + out->accelY = (int16_t)(raw[2] | (raw[3] << 8)); + out->accelZ = (int16_t)(raw[4] | (raw[5] << 8)); + out->gyroX = (int16_t)(raw[6] | (raw[7] << 8)); + out->gyroY = (int16_t)(raw[8] | (raw[9] << 8)); + out->gyroZ = (int16_t)(raw[10] | (raw[11] << 8)); +} + whal_Error whal_Bmi270_Init(whal_Sensor *dev) { whal_Bmi270_Cfg *cfg; whal_Error err; + whal_Error endErr; uint8_t val; #ifdef WHAL_CFG_BMI270_SINGLE_INSTANCE @@ -185,7 +196,9 @@ whal_Error whal_Bmi270_Init(whal_Sensor *dev) cfg->DelayMs(100); cleanup: - whal_I2c_EndCom(cfg->i2c); + endErr = whal_I2c_EndCom(cfg->i2c); + if (endErr && !err) + err = endErr; return err; } @@ -193,6 +206,7 @@ whal_Error whal_Bmi270_Deinit(whal_Sensor *dev) { whal_Bmi270_Cfg *cfg; whal_Error err; + whal_Error endErr; uint8_t val = BMI270_CMD_SOFT_RESET; #ifdef WHAL_CFG_BMI270_SINGLE_INSTANCE @@ -211,7 +225,9 @@ whal_Error whal_Bmi270_Deinit(whal_Sensor *dev) return err; err = whal_I2c_WriteReg(cfg->i2c, BMI270_REG_CMD, &val, 1); - whal_I2c_EndCom(cfg->i2c); + endErr = whal_I2c_EndCom(cfg->i2c); + if (endErr && !err) + err = endErr; return err; } @@ -222,6 +238,7 @@ whal_Error whal_Bmi270_Read(whal_Sensor *dev, void *data) whal_Bmi270_Data *out; uint8_t raw[BMI270_DATA_LEN]; whal_Error err; + whal_Error endErr; #ifdef WHAL_CFG_BMI270_SINGLE_INSTANCE cfg = (whal_Bmi270_Cfg *)whal_Bmi270_Dev.cfg; @@ -247,18 +264,14 @@ whal_Error whal_Bmi270_Read(whal_Sensor *dev, void *data) err = whal_I2c_ReadReg(cfg->i2c, BMI270_REG_DATA_ACC_X_LSB, raw, BMI270_DATA_LEN); - whal_I2c_EndCom(cfg->i2c); + endErr = whal_I2c_EndCom(cfg->i2c); + if (endErr && !err) + err = endErr; if (err) return err; - /* Parse little-endian 16-bit values */ - out->accelX = (int16_t)(raw[0] | (raw[1] << 8)); - out->accelY = (int16_t)(raw[2] | (raw[3] << 8)); - out->accelZ = (int16_t)(raw[4] | (raw[5] << 8)); - out->gyroX = (int16_t)(raw[6] | (raw[7] << 8)); - out->gyroY = (int16_t)(raw[8] | (raw[9] << 8)); - out->gyroZ = (int16_t)(raw[10] | (raw[11] << 8)); + whal_Bmi270_Decode(raw, out); return WHAL_SUCCESS; } diff --git a/tests/sensor/imu/test_bmi270_sensor.c b/tests/sensor/imu/test_bmi270_sensor.c index 44e541f..1af0324 100644 --- a/tests/sensor/imu/test_bmi270_sensor.c +++ b/tests/sensor/imu/test_bmi270_sensor.c @@ -33,17 +33,62 @@ * The board must provide g_whalBmi270 via the peripheral config. */ +/* The driver runs the accelerometer at +/-16g, 2048 LSB per g */ +#define ACCEL_LSB_PER_G 2048 + +/* At rest the accelerometer measures 1 g in any orientation; accept 0.8 g to + * 1.2 g, compared as squared magnitudes */ +#define GRAVITY_SQ_MIN ((uint32_t)(ACCEL_LSB_PER_G * 8 / 10) * \ + (uint32_t)(ACCEL_LSB_PER_G * 8 / 10)) +#define GRAVITY_SQ_MAX ((uint32_t)(ACCEL_LSB_PER_G * 12 / 10) * \ + (uint32_t)(ACCEL_LSB_PER_G * 12 / 10)) + +static uint32_t AccelMagnitudeSq(const whal_Bmi270_Data *d) +{ + int32_t x = d->accelX; + int32_t y = d->accelY; + int32_t z = d->accelZ; + + return (uint32_t)(x * x) + (uint32_t)(y * y) + (uint32_t)(z * z); +} + +/* Each field decodes from its own little-endian byte pair, including the + * sign bit; the bytes differ per field so a swapped index is caught. */ +static void Test_Bmi270_Decode(void) +{ + static const uint8_t raw[WHAL_BMI270_RAW_LEN] = { + 0x01, 0x02, /* accelX 0x0201 */ + 0x03, 0x84, /* accelY 0x8403 */ + 0xFF, 0x7F, /* accelZ 0x7FFF */ + 0x05, 0x06, /* gyroX 0x0605 */ + 0x07, 0xF8, /* gyroY 0xF807 */ + 0x00, 0x80, /* gyroZ 0x8000 */ + }; + whal_Bmi270_Data d = {0}; + + whal_Bmi270_Decode(raw, &d); + + WHAL_ASSERT_EQ(d.accelX, 0x0201); + WHAL_ASSERT_EQ(d.accelY, -31741); + WHAL_ASSERT_EQ(d.accelZ, 32767); + WHAL_ASSERT_EQ(d.gyroX, 0x0605); + WHAL_ASSERT_EQ(d.gyroY, -2041); + WHAL_ASSERT_EQ(d.gyroZ, -32768); +} + static void Test_Bmi270_Read(void) { whal_Bmi270_Data data = {0}; + uint32_t magSq; WHAL_ASSERT_EQ(whal_Sensor_Read( g_peripheralSensor[PERIPHERAL_SENSOR_BMI270].dev, &data), WHAL_SUCCESS); - /* At rest, at least one accel axis should be non-zero (gravity) */ - WHAL_ASSERT_NEQ(data.accelX | data.accelY | data.accelZ, 0); + magSq = AccelMagnitudeSq(&data); + WHAL_ASSERT_EQ(magSq >= GRAVITY_SQ_MIN, 1); + WHAL_ASSERT_EQ(magSq <= GRAVITY_SQ_MAX, 1); } static void Test_Bmi270_ReadMultiple(void) @@ -60,14 +105,17 @@ static void Test_Bmi270_ReadMultiple(void) &data2), WHAL_SUCCESS); - /* Both reads should have non-zero accel (gravity) */ - WHAL_ASSERT_NEQ(data1.accelX | data1.accelY | data1.accelZ, 0); - WHAL_ASSERT_NEQ(data2.accelX | data2.accelY | data2.accelZ, 0); + /* Both reads should measure gravity */ + WHAL_ASSERT_EQ(AccelMagnitudeSq(&data1) >= GRAVITY_SQ_MIN, 1); + WHAL_ASSERT_EQ(AccelMagnitudeSq(&data1) <= GRAVITY_SQ_MAX, 1); + WHAL_ASSERT_EQ(AccelMagnitudeSq(&data2) >= GRAVITY_SQ_MIN, 1); + WHAL_ASSERT_EQ(AccelMagnitudeSq(&data2) <= GRAVITY_SQ_MAX, 1); } void whal_Test_Bmi270_Sensor(void) { WHAL_TEST_SUITE_START("bmi270"); + WHAL_TEST(Test_Bmi270_Decode); WHAL_TEST(Test_Bmi270_Read); WHAL_TEST(Test_Bmi270_ReadMultiple); WHAL_TEST_SUITE_END(); diff --git a/wolfHAL/sensor/imu/bmi270_sensor.h b/wolfHAL/sensor/imu/bmi270_sensor.h index 6905c9a..7f1145d 100644 --- a/wolfHAL/sensor/imu/bmi270_sensor.h +++ b/wolfHAL/sensor/imu/bmi270_sensor.h @@ -44,6 +44,9 @@ /* Expected CHIP_ID value */ #define WHAL_BMI270_CHIP_ID 0x24 +/* Length of the accel + gyro burst read decoded by whal_Bmi270_Decode */ +#define WHAL_BMI270_RAW_LEN 12 + /* * @brief BMI270 sensor data returned by Read. */ @@ -75,6 +78,17 @@ typedef struct { extern const whal_Sensor whal_Bmi270_Dev; #endif +/* + * @brief Decode a raw accel + gyro burst read into axis values. + * + * raw holds DATA_8 through DATA_19: six little-endian signed 16-bit values + * in the order accel X, Y, Z, then gyro X, Y, Z. + * + * @param raw Burst-read buffer of WHAL_BMI270_RAW_LEN bytes. + * @param out Decoded axis values. + */ +void whal_Bmi270_Decode(const uint8_t *raw, whal_Bmi270_Data *out); + #ifndef WHAL_CFG_BMI270_SENSOR_DIRECT_API_MAPPING /* * @brief Driver instance for the BMI270. From d30179e86f31aea4cf57cf0e247b2b5556d72633 Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Wed, 14 Oct 2026 14:26:30 -0400 Subject: [PATCH 19/22] [uart, flash, dma, stm32, test] Clear stale UART line errors before DMA receive, add GPDMA reject and WB0 lock tests - WB and WBA UART-DMA RecvAsync clear latched line errors before arming, so blocking Recv reports only its own errors; document that async completions carry only the DMA result - Comment why a failed DMA stop wins in WBA UART-DMA Send and Recv - Document that the H5 flash config must describe the whole device - Rename the F0 flash pageSize field to pageSz and fix the stale 2 KB page docs - Skip the on-chip erase extent tests when the extent would reach the running image - Skip the cross-page write test when the page size does not fit its buffers - Add a WBA GPDMA test that Configure rejects a bad channel, direction and byte count - Add a WB0 flash test that a locked page cannot be erased or written --- boards/stm32f091rc_nucleo/wolfHAL_board.h | 2 +- boards/stm32f302r8_nucleo/wolfHAL_board.h | 2 +- src/flash/stm32f0_flash.c | 8 +- src/uart/stm32wb_uart_dma.c | 2 + src/uart/stm32wba_uart_dma.c | 4 + tests/dma/test_stm32wba_dma.c | 78 +++++++++++++++ tests/flash/test_flash.c | 26 +++++ tests/flash/test_stm32wb0_flash.c | 115 ++++++++++++++++++++++ wolfHAL/flash/stm32f0_flash.h | 8 +- wolfHAL/flash/stm32h5_flash.h | 8 +- wolfHAL/uart/stm32wb_uart_dma.h | 3 + wolfHAL/uart/stm32wba_uart_dma.h | 3 + 12 files changed, 247 insertions(+), 12 deletions(-) create mode 100644 tests/dma/test_stm32wba_dma.c create mode 100644 tests/flash/test_stm32wb0_flash.c diff --git a/boards/stm32f091rc_nucleo/wolfHAL_board.h b/boards/stm32f091rc_nucleo/wolfHAL_board.h index 9bf4a73..f4d37dc 100644 --- a/boards/stm32f091rc_nucleo/wolfHAL_board.h +++ b/boards/stm32f091rc_nucleo/wolfHAL_board.h @@ -92,7 +92,7 @@ enum { .cfg = (void *)&(const whal_Stm32f0_Flash_Cfg){ \ .startAddr = 0x08000000, \ .size = 0x40000, \ - .pageSize = 0x800, \ + .pageSz = 0x800, \ .timeout = &g_whalTimeout, \ }, \ } diff --git a/boards/stm32f302r8_nucleo/wolfHAL_board.h b/boards/stm32f302r8_nucleo/wolfHAL_board.h index b1dd663..232746c 100644 --- a/boards/stm32f302r8_nucleo/wolfHAL_board.h +++ b/boards/stm32f302r8_nucleo/wolfHAL_board.h @@ -94,7 +94,7 @@ enum { .cfg = (void *)&(const whal_Stm32f3_Flash_Cfg){ \ .startAddr = 0x08000000, \ .size = 0x10000, \ - .pageSize = 0x800, \ + .pageSz = 0x800, \ .timeout = &g_whalTimeout, \ }, \ } diff --git a/src/flash/stm32f0_flash.c b/src/flash/stm32f0_flash.c index 527a3d5..46e0f05 100644 --- a/src/flash/stm32f0_flash.c +++ b/src/flash/stm32f0_flash.c @@ -163,7 +163,7 @@ static whal_Error whal_Stm32f0_Flash_WriteOrErase(whal_Flash *flashDev, if (!write && dataSz == 0) return WHAL_SUCCESS; - if (!write && cfg->pageSize == 0) + if (!write && cfg->pageSz == 0) return WHAL_EINVAL; whal_Reg_Get(base, FLASH_SR_REG, @@ -201,9 +201,9 @@ static whal_Error whal_Stm32f0_Flash_WriteOrErase(whal_Flash *flashDev, } } } else { - size_t startPage = (addr - cfg->startAddr) / cfg->pageSize; + size_t startPage = (addr - cfg->startAddr) / cfg->pageSz; size_t endPage = ((addr - cfg->startAddr) + dataSz - 1) / - cfg->pageSize; + cfg->pageSz; for (size_t page = startPage; page <= endPage; ++page) { /* Enable page erase */ @@ -212,7 +212,7 @@ static whal_Error whal_Stm32f0_Flash_WriteOrErase(whal_Flash *flashDev, /* Write page start address to AR */ whal_Reg_Write(base, FLASH_AR_REG, - cfg->startAddr + page * cfg->pageSize); + cfg->startAddr + page * cfg->pageSz); /* Start erase */ whal_Reg_Update(base, FLASH_CR_REG, FLASH_CR_STRT_Msk, diff --git a/src/uart/stm32wb_uart_dma.c b/src/uart/stm32wb_uart_dma.c index 7675092..104d778 100644 --- a/src/uart/stm32wb_uart_dma.c +++ b/src/uart/stm32wb_uart_dma.c @@ -174,6 +174,8 @@ whal_Error whal_Stm32wb_UartDma_RecvAsync(whal_Uart *uartDev, void *data, return err; } + /* Drop line errors latched before this transfer */ + whal_Reg_Write(base, UART_ICR_REG, UART_ISR_ERR_Msk); whal_Reg_Update(base, UART_CR3_REG, UART_CR3_DMAR_Msk, UART_CR3_DMAR_Msk); diff --git a/src/uart/stm32wba_uart_dma.c b/src/uart/stm32wba_uart_dma.c index ec5dc44..ebf3da2 100644 --- a/src/uart/stm32wba_uart_dma.c +++ b/src/uart/stm32wba_uart_dma.c @@ -183,6 +183,8 @@ whal_Error whal_Stm32wba_UartDma_RecvAsync(whal_Uart *uartDev, void *data, return err; } + /* Drop line errors latched before this transfer */ + whal_Reg_Write(base, UART_ICR_REG, UART_ISR_ERR_Msk); whal_Reg_Update(base, UART_CR3_REG, UART_CR3_DMAR_Msk, UART_CR3_DMAR_Msk); @@ -236,6 +238,7 @@ whal_Error whal_Stm32wba_UartDma_Send(whal_Uart *uartDev, const void *data, cfg->base.timeout); cleanup: + /* A failed stop wins: the channel may still own the caller's buffer */ stopErr = whal_Dma_Stop(cfg->dma, cfg->txCh); if (stopErr) err = stopErr; @@ -284,6 +287,7 @@ whal_Error whal_Stm32wba_UartDma_Recv(whal_Uart *uartDev, void *data, } cleanup: + /* A failed stop wins: the channel may still own the caller's buffer */ stopErr = whal_Dma_Stop(cfg->dma, cfg->rxCh); if (stopErr) err = stopErr; diff --git a/tests/dma/test_stm32wba_dma.c b/tests/dma/test_stm32wba_dma.c new file mode 100644 index 0000000..8fd1a86 --- /dev/null +++ b/tests/dma/test_stm32wba_dma.c @@ -0,0 +1,78 @@ +/* test_stm32wba_dma.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHAL. + * + * wolfHAL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHAL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include "wolfHAL_board.h" +#include "test.h" + +#ifdef BOARD_DMA + +static uint8_t g_src[16]; +static uint8_t g_dst[16]; + +/* Every case is rejected before any channel register is written */ +static void Test_Gpdma_Reject(void) +{ + whal_Stm32wba_Gpdma_ChCfg cfg = { + .dir = WHAL_STM32WBA_GPDMA_DIR_MEM_TO_MEM, + .srcAddr = (size_t)g_src, + .dstAddr = (size_t)g_dst, + .nbytes = sizeof(g_src), + .srcWidth = WHAL_STM32WBA_GPDMA_WIDTH_8BIT, + .dstWidth = WHAL_STM32WBA_GPDMA_WIDTH_8BIT, + .srcInc = WHAL_STM32WBA_GPDMA_INC_ENABLE, + .dstInc = WHAL_STM32WBA_GPDMA_INC_ENABLE, + }; + size_t numCh = ((whal_Stm32wba_Gpdma_Cfg *)g_whalDma1.cfg)->numChannels; + + WHAL_ASSERT_EQ(whal_Dma_Configure(&g_whalDma1, numCh, &cfg), WHAL_EINVAL); + + cfg.dir = (whal_Stm32wba_Gpdma_Dir)(WHAL_STM32WBA_GPDMA_DIR_MEM_TO_MEM + 1); + WHAL_ASSERT_EQ(whal_Dma_Configure(&g_whalDma1, 0, &cfg), WHAL_EINVAL); + cfg.dir = WHAL_STM32WBA_GPDMA_DIR_MEM_TO_MEM; + + cfg.nbytes = 0; + WHAL_ASSERT_EQ(whal_Dma_Configure(&g_whalDma1, 0, &cfg), WHAL_EINVAL); + + cfg.nbytes = 0x10000; + WHAL_ASSERT_EQ(whal_Dma_Configure(&g_whalDma1, 0, &cfg), WHAL_EINVAL); +} + +#else + +static void Test_Gpdma_NoBoardDma(void) +{ + WHAL_SKIP(); +} + +#endif /* BOARD_DMA */ + +void whal_Test_Dma_Platform(void) +{ + WHAL_TEST_SUITE_START("dma_platform"); +#ifdef BOARD_DMA + WHAL_TEST(Test_Gpdma_Reject); +#else + WHAL_TEST(Test_Gpdma_NoBoardDma); +#endif + WHAL_TEST_SUITE_END(); +} diff --git a/tests/flash/test_flash.c b/tests/flash/test_flash.c index 2957812..8eae690 100644 --- a/tests/flash/test_flash.c +++ b/tests/flash/test_flash.c @@ -30,6 +30,12 @@ static size_t g_testFlashStart; static size_t g_testFlashAddr; static size_t g_testFlashSectorSz; static size_t g_testFlashPageSz; +static int g_testFlashOnChip; + +/* Linker symbols: the .data load image follows the code in flash */ +extern uint8_t _sidata[]; +extern uint8_t _sdata[]; +extern uint8_t _edata[]; static const uint8_t g_extentPattern[32] = { 0x5A, 0xA5, 0x3C, 0xC3, 0x69, 0x96, 0x0F, 0xF0, @@ -124,11 +130,22 @@ static size_t ExtentBase(void) return g_testFlashAddr; } +/* On-chip flash below the end of the running image must not be erased */ +static int ExtentOverlapsImage(size_t base) +{ + size_t imageEnd = (size_t)_sidata + (size_t)(_edata - _sdata); + + return g_testFlashOnChip && base < imageEnd; +} + static void Test_Flash_EraseExtent(void) { const size_t sz = g_testFlashSectorSz; const size_t base = ExtentBase(); + if (ExtentOverlapsImage(base)) + WHAL_SKIP(); + WHAL_ASSERT_EQ(whal_Flash_Unlock(g_testFlashDev, base, 3 * sz), WHAL_SUCCESS); WHAL_ASSERT_EQ(whal_Flash_Erase(g_testFlashDev, base, 3 * sz), @@ -163,6 +180,9 @@ static void Test_Flash_EraseOneBytePastSector(void) const size_t sz = g_testFlashSectorSz; const size_t base = ExtentBase(); + if (ExtentOverlapsImage(base)) + WHAL_SKIP(); + WHAL_ASSERT_EQ(whal_Flash_Unlock(g_testFlashDev, base, 3 * sz), WHAL_SUCCESS); WHAL_ASSERT_EQ(whal_Flash_Erase(g_testFlashDev, base, 3 * sz), @@ -200,6 +220,11 @@ static void Test_Flash_CrossPageWrite(void) size_t j; whal_Error err; + /* The cases need a page that fits the buffers and four pages per sector */ + if (page == 0 || page + 8 > sizeof(pattern) || + 4 * page > g_testFlashSectorSz) + WHAL_SKIP(); + for (i = 0; i < sizeof(pattern); i++) pattern[i] = (uint8_t)(i * 7 + 3); @@ -272,6 +297,7 @@ static void Test_Flash_OutOfBounds(void) static void run_flash_tests(const char *name, int onChip) { + g_testFlashOnChip = onChip; WHAL_TEST_SUITE_START("flash"); if (name) whal_Test_Printf(" device: %s\n", name); diff --git a/tests/flash/test_stm32wb0_flash.c b/tests/flash/test_stm32wb0_flash.c new file mode 100644 index 0000000..40020b0 --- /dev/null +++ b/tests/flash/test_stm32wb0_flash.c @@ -0,0 +1,115 @@ +/* test_stm32wb0_flash.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHAL. + * + * wolfHAL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHAL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include "wolfHAL_board.h" +#include "test.h" + +/* RM0529 does not say whether a command on a PAGEPROT-protected page raises + * CMDERR or is ignored, so the test checks the flash contents rather than the + * return code. */ + +static const uint8_t g_pattern[16] = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xF0, 0x0F, +}; + +static whal_Error WritePattern(size_t addr) +{ + whal_Error err; + + do { + err = whal_Flash_Write(BOARD_FLASH_DEV, addr, g_pattern, + sizeof(g_pattern)); + } while (err == WHAL_ENOTREADY); + + return err; +} + +static int IsErased(size_t addr, size_t len) +{ + uint8_t buf[16]; + + if (len > sizeof(buf) || + whal_Flash_Read(BOARD_FLASH_DEV, addr, buf, len) != WHAL_SUCCESS) + return 0; + + for (size_t i = 0; i < len; i++) { + if (buf[i] != 0xFF) + return 0; + } + return 1; +} + +static int HasPattern(size_t addr) +{ + uint8_t buf[sizeof(g_pattern)]; + + if (whal_Flash_Read(BOARD_FLASH_DEV, addr, buf, sizeof(buf)) != + WHAL_SUCCESS) + return 0; + + for (size_t i = 0; i < sizeof(buf); i++) { + if (buf[i] != g_pattern[i]) + return 0; + } + return 1; +} + +static void Test_Stm32wb0_Flash_LockBlocksWrite(void) +{ + const size_t addr = BOARD_FLASH_TEST_ADDR; + const size_t sz = BOARD_FLASH_SECTOR_SZ; + const size_t blank = addr + 64; + whal_Error err; + int patternKept; + int blankKept; + + WHAL_ASSERT_EQ(whal_Flash_Unlock(BOARD_FLASH_DEV, addr, sz), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Erase(BOARD_FLASH_DEV, addr, sz), WHAL_SUCCESS); + WHAL_ASSERT_EQ(WritePattern(addr), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Lock(BOARD_FLASH_DEV, addr, sz), WHAL_SUCCESS); + + /* Neither an erase nor a write may change a locked page */ + err = whal_Flash_Erase(BOARD_FLASH_DEV, addr, sz); + WHAL_ASSERT_EQ(err == WHAL_SUCCESS || err == WHAL_EHARDWARE, 1); + err = WritePattern(blank); + WHAL_ASSERT_EQ(err == WHAL_SUCCESS || err == WHAL_EHARDWARE, 1); + + patternKept = HasPattern(addr); + blankKept = IsErased(blank, sizeof(g_pattern)); + + /* Leave the test page erased and locked */ + WHAL_ASSERT_EQ(whal_Flash_Unlock(BOARD_FLASH_DEV, addr, sz), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Erase(BOARD_FLASH_DEV, addr, sz), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Lock(BOARD_FLASH_DEV, addr, sz), WHAL_SUCCESS); + + WHAL_ASSERT_EQ(patternKept, 1); + WHAL_ASSERT_EQ(blankKept, 1); +} + +void whal_Test_Flash_Platform(void) +{ + WHAL_TEST_SUITE_START("flash_platform"); + WHAL_TEST(Test_Stm32wb0_Flash_LockBlocksWrite); + WHAL_TEST_SUITE_END(); +} diff --git a/wolfHAL/flash/stm32f0_flash.h b/wolfHAL/flash/stm32f0_flash.h index 6518d24..94c940a 100644 --- a/wolfHAL/flash/stm32f0_flash.h +++ b/wolfHAL/flash/stm32f0_flash.h @@ -30,7 +30,7 @@ * @brief STM32F0 flash driver configuration. * * The STM32F0 embedded flash provides: - * - Up to 256 KB organized in 2 KB pages + * - Up to 256 KB organized in 1 KB or 2 KB pages, set by pageSz * - Half-word (16-bit) programming * - Page erase via FLASH_AR address register * - Configurable wait states based on CPU frequency @@ -42,7 +42,7 @@ typedef struct whal_Stm32f0_Flash_Cfg { size_t startAddr; /* Flash region start address */ size_t size; /* Flash region size in bytes */ - size_t pageSize; /* Erase page size in bytes (1 KB or 2 KB) */ + size_t pageSz; /* Erase page size in bytes (1 KB or 2 KB) */ whal_Timeout *timeout; /* Optional timeout for poll loops */ } whal_Stm32f0_Flash_Cfg; @@ -143,14 +143,14 @@ whal_Error whal_Stm32f0_Flash_Write(whal_Flash *flashDev, size_t addr, const void *data, size_t dataSz); /* - * @brief Erase one or more 2 KB pages covering [addr, addr+dataSz). + * @brief Erase one or more pageSz pages covering [addr, addr+dataSz). * * @param flashDev Flash device instance. * @param addr First page address. * @param dataSz Number of bytes to erase (rounded up to whole pages). * * @retval WHAL_SUCCESS Erase completed. - * @retval WHAL_EINVAL Null pointer or address out of region. + * @retval WHAL_EINVAL Null pointer, address out of region, or pageSz is 0. * @retval WHAL_ETIMEOUT BSY did not clear within the configured timeout. * @retval WHAL_EHARDWARE Erase error reported in FLASH_SR. */ diff --git a/wolfHAL/flash/stm32h5_flash.h b/wolfHAL/flash/stm32h5_flash.h index a08d6c6..770ba42 100644 --- a/wolfHAL/flash/stm32h5_flash.h +++ b/wolfHAL/flash/stm32h5_flash.h @@ -38,10 +38,14 @@ /* * @brief Flash device configuration. + * + * Erase derives the bank boundary from this configuration, so it must + * describe the whole device: startAddr is the start of bank 1 and size + * covers both banks. Bank swap (SWAP_BANK) is not supported. */ typedef struct whal_Stm32h5_Flash_Cfg { - size_t startAddr; /* Flash base address (typically 0x08000000) */ - size_t size; /* Flash size in bytes */ + size_t startAddr; /* Start of bank 1 (0x08000000) */ + size_t size; /* Total flash size in bytes, both banks */ whal_Timeout *timeout; } whal_Stm32h5_Flash_Cfg; diff --git a/wolfHAL/uart/stm32wb_uart_dma.h b/wolfHAL/uart/stm32wb_uart_dma.h index f2e0b6e..aa09bbf 100644 --- a/wolfHAL/uart/stm32wb_uart_dma.h +++ b/wolfHAL/uart/stm32wb_uart_dma.h @@ -136,6 +136,9 @@ whal_Error whal_Stm32wb_UartDma_SendAsync(whal_Uart *uartDev, const void *data, * Returns immediately after starting the DMA transfer. The buffer must * remain valid until cfg->rxResult is no longer WHAL_ENOTREADY. * + * Line errors latched before the transfer are cleared. cfg->rxResult carries + * only the DMA result, not parity, framing, noise or overrun errors. + * * @param uartDev UART device instance. * @param data Receive buffer. Must remain valid until completion. * @param dataSz Number of bytes to receive. diff --git a/wolfHAL/uart/stm32wba_uart_dma.h b/wolfHAL/uart/stm32wba_uart_dma.h index 1c4c993..c026b8a 100644 --- a/wolfHAL/uart/stm32wba_uart_dma.h +++ b/wolfHAL/uart/stm32wba_uart_dma.h @@ -144,6 +144,9 @@ whal_Error whal_Stm32wba_UartDma_SendAsync(whal_Uart *uartDev, const void *data, * @brief Start a non-blocking GPDMA RX transfer. Returns once the transfer * has been queued; completion is reported via the RX callback. * + * Line errors latched before the transfer are cleared. The RX callback + * reports only the DMA result, not parity, framing, noise or overrun errors. + * * @param uartDev UART device instance. * @param data Destination buffer (must be DMA-accessible until completion). * @param dataSz Number of bytes to receive. From b6a2ea8543dd9fea9b7a3d1176bc9e72438e07f4 Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Wed, 14 Oct 2026 16:27:11 -0400 Subject: [PATCH 20/22] [dma, stm32] Skip GPDMA Deinit reset when the channel abort times out - Deinit no longer writes RESET to a channel whose suspend/idle wait timed out, since a reset of a non-idle channel may not apply and the write would clear SUSP - Remaining channels are still torn down and the first error is returned --- src/dma/stm32wba_gpdma.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/dma/stm32wba_gpdma.c b/src/dma/stm32wba_gpdma.c index 5ccad64..9ff38e1 100644 --- a/src/dma/stm32wba_gpdma.c +++ b/src/dma/stm32wba_gpdma.c @@ -195,8 +195,12 @@ whal_Error whal_Stm32wba_Gpdma_Deinit(whal_Dma *dmaDev) for (uint8_t ch = 0; ch < cfg->numChannels; ch++) { abortErr = Gpdma_AbortChannel(base, ch, cfg->timeout); - if (abortErr && !err) - err = abortErr; + if (abortErr) { + /* Leave SUSP set; a reset of a non-idle channel may not apply */ + if (!err) + err = abortErr; + continue; + } whal_Reg_Write(base, GPDMA_CxCR(ch), GPDMA_CxCR_RESET_Msk); } From 8e7e66c8a0f0029946e2e8f04493ba927834da09 Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Fri, 16 Oct 2026 11:30:25 -0400 Subject: [PATCH 21/22] [crypto, stm32wba, test] Return ENOTSUP for empty HMAC messages, fix WBA HASH context test - HMAC Oneshot returns WHAL_ENOTSUP for an empty message, since the HASH core ignores DCAL in the HMAC message phase when no data was written - HMAC Finalize resets the core and returns WHAL_ENOTSUP when no message data was processed - Document the empty-message limitation on the HMAC Oneshot and Finalize functions - HMAC residue tests accept WHAL_ENOTSUP for empty messages, provided streaming and Oneshot agree - WBA HASH context test polls for DINIS after Start and compares all 54 context registers, since the HMAC state lives in CSR0 to CSR37 --- src/crypto/stm32wba_hash.c | 32 ++++++++++++++++++++++++ tests/crypto/test_hmac_sha1.c | 15 ++++++++--- tests/crypto/test_hmac_sha224.c | 15 ++++++++--- tests/crypto/test_hmac_sha256.c | 15 ++++++++--- tests/crypto/test_stm32wba_hmac_sha256.c | 17 ++++++------- wolfHAL/crypto/stm32wba_hash.h | 12 +++++++++ 6 files changed, 88 insertions(+), 18 deletions(-) diff --git a/src/crypto/stm32wba_hash.c b/src/crypto/stm32wba_hash.c index abe7d5a..7436adf 100644 --- a/src/crypto/stm32wba_hash.c +++ b/src/crypto/stm32wba_hash.c @@ -98,6 +98,7 @@ static whal_Stm32wba_HmacSha256_State g_hmacSha256State; * genuine last partial word. */ static uint8_t g_streamBuf[4]; static size_t g_streamBufBytes; +static uint8_t g_streamHasData; static uint32_t AlgoBits(size_t algo) { @@ -120,6 +121,7 @@ static void ResetHash(size_t base) static void StreamReset(void) { g_streamBufBytes = 0; + g_streamHasData = 0; } /* Accumulate Process input. Tops up the staging buffer to a full word if @@ -127,6 +129,9 @@ static void StreamReset(void) * trailing 0-3 bytes back into the staging buffer for the next call. */ static void StreamWrite(size_t base, const uint8_t *in, size_t inSz) { + if (inSz > 0) + g_streamHasData = 1; + if (g_streamBufBytes > 0) { while (g_streamBufBytes < 4 && inSz > 0) { g_streamBuf[g_streamBufBytes++] = *in++; @@ -571,6 +576,10 @@ whal_Error whal_Stm32wba_HmacSha1_Oneshot(whal_HmacSha1 *dev, (inSz > 0 && !in)) return WHAL_EINVAL; + /* The HMAC message phase cannot be completed without message data */ + if (inSz == 0) + return WHAL_ENOTSUP; + lkey = (keySz > 64) ? 1 : 0; whal_Reg_Update(base, HASH_STR_REG, HASH_STR_NBLW_Msk, 0); @@ -697,6 +706,11 @@ whal_Error whal_Stm32wba_HmacSha1_Finalize(whal_HmacSha1 *dev, if (!digest || digestSz != 20) return WHAL_EINVAL; + if (!g_streamHasData) { + ResetHash(base); + return WHAL_ENOTSUP; + } + /* Drain any buffered tail bytes, then DCAL to close message phase. */ whal_Reg_Update(base, HASH_STR_REG, HASH_STR_NBLW_Msk, 0); StreamDrain(base); @@ -753,6 +767,10 @@ whal_Error whal_Stm32wba_HmacSha224_Oneshot(whal_HmacSha224 *dev, (inSz > 0 && !in)) return WHAL_EINVAL; + /* The HMAC message phase cannot be completed without message data */ + if (inSz == 0) + return WHAL_ENOTSUP; + lkey = (keySz > 64) ? 1 : 0; whal_Reg_Update(base, HASH_STR_REG, HASH_STR_NBLW_Msk, 0); @@ -879,6 +897,11 @@ whal_Error whal_Stm32wba_HmacSha224_Finalize(whal_HmacSha224 *dev, if (!digest || digestSz != 28) return WHAL_EINVAL; + if (!g_streamHasData) { + ResetHash(base); + return WHAL_ENOTSUP; + } + /* Drain any buffered tail bytes, then DCAL to close message phase. */ whal_Reg_Update(base, HASH_STR_REG, HASH_STR_NBLW_Msk, 0); StreamDrain(base); @@ -935,6 +958,10 @@ whal_Error whal_Stm32wba_HmacSha256_Oneshot(whal_HmacSha256 *dev, (inSz > 0 && !in)) return WHAL_EINVAL; + /* The HMAC message phase cannot be completed without message data */ + if (inSz == 0) + return WHAL_ENOTSUP; + lkey = (keySz > 64) ? 1 : 0; whal_Reg_Update(base, HASH_STR_REG, HASH_STR_NBLW_Msk, 0); @@ -1061,6 +1088,11 @@ whal_Error whal_Stm32wba_HmacSha256_Finalize(whal_HmacSha256 *dev, if (!digest || digestSz != 32) return WHAL_EINVAL; + if (!g_streamHasData) { + ResetHash(base); + return WHAL_ENOTSUP; + } + /* Drain any buffered tail bytes, then DCAL to close message phase. */ whal_Reg_Update(base, HASH_STR_REG, HASH_STR_NBLW_Msk, 0); StreamDrain(base); diff --git a/tests/crypto/test_hmac_sha1.c b/tests/crypto/test_hmac_sha1.c index 3164b68..d5aafbb 100644 --- a/tests/crypto/test_hmac_sha1.c +++ b/tests/crypto/test_hmac_sha1.c @@ -267,6 +267,7 @@ static void Test_HmacSha1_StreamingResidue(void) uint8_t msg[101]; size_t i; size_t j; + whal_Error err; ResidueMsg(msg, sizeof(msg)); @@ -287,9 +288,17 @@ static void Test_HmacSha1_StreamingResidue(void) WHAL_SUCCESS); off += chunk; } - WHAL_ASSERT_EQ(whal_HmacSha1_Finalize(BOARD_HMAC_SHA1_DEV, digest, - sizeof(digest)), - WHAL_SUCCESS); + err = whal_HmacSha1_Finalize(BOARD_HMAC_SHA1_DEV, digest, + sizeof(digest)); + if (err == WHAL_ENOTSUP && hmacSha1ResidueCases[i].msgSz == 0) { + WHAL_ASSERT_EQ(whal_HmacSha1_Oneshot(BOARD_HMAC_SHA1_DEV, + hmacKey, sizeof(hmacKey), + msg, 0, oneshot, + sizeof(oneshot)), + WHAL_ENOTSUP); + continue; + } + WHAL_ASSERT_EQ(err, WHAL_SUCCESS); WHAL_ASSERT_MEM_EQ(digest, hmacSha1ResidueCases[i].digest, sizeof(digest)); diff --git a/tests/crypto/test_hmac_sha224.c b/tests/crypto/test_hmac_sha224.c index ae146ef..7cd4ca7 100644 --- a/tests/crypto/test_hmac_sha224.c +++ b/tests/crypto/test_hmac_sha224.c @@ -278,6 +278,7 @@ static void Test_HmacSha224_StreamingResidue(void) uint8_t msg[101]; size_t i; size_t j; + whal_Error err; ResidueMsg(msg, sizeof(msg)); @@ -298,9 +299,17 @@ static void Test_HmacSha224_StreamingResidue(void) WHAL_SUCCESS); off += chunk; } - WHAL_ASSERT_EQ(whal_HmacSha224_Finalize(BOARD_HMAC_SHA224_DEV, digest, - sizeof(digest)), - WHAL_SUCCESS); + err = whal_HmacSha224_Finalize(BOARD_HMAC_SHA224_DEV, digest, + sizeof(digest)); + if (err == WHAL_ENOTSUP && hmacSha224ResidueCases[i].msgSz == 0) { + WHAL_ASSERT_EQ(whal_HmacSha224_Oneshot(BOARD_HMAC_SHA224_DEV, + hmacKey, sizeof(hmacKey), + msg, 0, oneshot, + sizeof(oneshot)), + WHAL_ENOTSUP); + continue; + } + WHAL_ASSERT_EQ(err, WHAL_SUCCESS); WHAL_ASSERT_MEM_EQ(digest, hmacSha224ResidueCases[i].digest, sizeof(digest)); diff --git a/tests/crypto/test_hmac_sha256.c b/tests/crypto/test_hmac_sha256.c index 9671ea9..4b0d097 100644 --- a/tests/crypto/test_hmac_sha256.c +++ b/tests/crypto/test_hmac_sha256.c @@ -278,6 +278,7 @@ static void Test_HmacSha256_StreamingResidue(void) uint8_t msg[101]; size_t i; size_t j; + whal_Error err; ResidueMsg(msg, sizeof(msg)); @@ -298,9 +299,17 @@ static void Test_HmacSha256_StreamingResidue(void) WHAL_SUCCESS); off += chunk; } - WHAL_ASSERT_EQ(whal_HmacSha256_Finalize(BOARD_HMAC_SHA256_DEV, digest, - sizeof(digest)), - WHAL_SUCCESS); + err = whal_HmacSha256_Finalize(BOARD_HMAC_SHA256_DEV, digest, + sizeof(digest)); + if (err == WHAL_ENOTSUP && hmacSha256ResidueCases[i].msgSz == 0) { + WHAL_ASSERT_EQ(whal_HmacSha256_Oneshot(BOARD_HMAC_SHA256_DEV, + hmacKey, sizeof(hmacKey), + msg, 0, oneshot, + sizeof(oneshot)), + WHAL_ENOTSUP); + continue; + } + WHAL_ASSERT_EQ(err, WHAL_SUCCESS); WHAL_ASSERT_MEM_EQ(digest, hmacSha256ResidueCases[i].digest, sizeof(digest)); diff --git a/tests/crypto/test_stm32wba_hmac_sha256.c b/tests/crypto/test_stm32wba_hmac_sha256.c index a43f0a0..0f3e79a 100644 --- a/tests/crypto/test_stm32wba_hmac_sha256.c +++ b/tests/crypto/test_stm32wba_hmac_sha256.c @@ -32,8 +32,6 @@ #define HASH_HR_REG(x) (0x310 + (x) * 4) #define HASH_CSR_REG(x) (0xF8 + (x) * 4) -/* HASH_CSR38..53 hold the extra context saved only for HMAC (RM0493 27.4.8) */ -#define HASH_CSR_HMAC_FIRST 38 #define HASH_CSR_COUNT 54 static const uint8_t hmacKey[20] = { @@ -64,24 +62,25 @@ static int AnyNonZero(const uint32_t *words, size_t count) static void Test_Hash_InitChangesHmacContext(void) { size_t base = whal_Stm32wba_Hash_Dev.base; - uint32_t before[HASH_CSR_COUNT - HASH_CSR_HMAC_FIRST]; - uint32_t after[HASH_CSR_COUNT - HASH_CSR_HMAC_FIRST]; + uint32_t before[HASH_CSR_COUNT]; + uint32_t after[HASH_CSR_COUNT]; WHAL_ASSERT_EQ(whal_HmacSha256_Start(BOARD_HMAC_SHA256_DEV, hmacKey, sizeof(hmacKey)), WHAL_SUCCESS); - WHAL_ASSERT_EQ(whal_Reg_Read(base, HASH_SR_REG) & HASH_SR_DINIS_Msk, - HASH_SR_DINIS_Msk); - ReadCsrs(base, before, HASH_CSR_HMAC_FIRST, HASH_CSR_COUNT); - WHAL_ASSERT_EQ(AnyNonZero(before, sizeof(before) / sizeof(before[0])), 1); + WHAL_ASSERT_EQ(whal_Reg_ReadPoll(base, HASH_SR_REG, HASH_SR_DINIS_Msk, + HASH_SR_DINIS_Msk, &g_whalTimeout), + WHAL_SUCCESS); + ReadCsrs(base, before, 0, HASH_CSR_COUNT); + WHAL_ASSERT_EQ(AnyNonZero(before, HASH_CSR_COUNT), 1); whal_Reg_Update(base, HASH_CR_REG, HASH_CR_INIT_Msk, HASH_CR_INIT_Msk); if ((whal_Reg_Read(base, HASH_SR_REG) & HASH_SR_DINIS_Msk) == 0) WHAL_SKIP(); - ReadCsrs(base, after, HASH_CSR_HMAC_FIRST, HASH_CSR_COUNT); + ReadCsrs(base, after, 0, HASH_CSR_COUNT); WHAL_ASSERT_MEM_NEQ(after, before, sizeof(before)); } diff --git a/wolfHAL/crypto/stm32wba_hash.h b/wolfHAL/crypto/stm32wba_hash.h index 953a318..7121f6b 100644 --- a/wolfHAL/crypto/stm32wba_hash.h +++ b/wolfHAL/crypto/stm32wba_hash.h @@ -250,6 +250,8 @@ whal_Error whal_Stm32wba_Sha256_Finalize(whal_Sha256 *dev, /** * @brief HMAC-SHA-1 one-shot. * + * An empty message is not supported and returns WHAL_ENOTSUP. + * * @param dev HMAC-SHA-1 device instance. * @param key Key buffer. * @param keySz Key size in bytes. @@ -286,6 +288,8 @@ whal_Error whal_Stm32wba_HmacSha1_Process(whal_HmacSha1 *dev, /** * @brief Finalize an HMAC-SHA-1 session and produce the digest. * + * Returns WHAL_ENOTSUP if no message data was processed. + * * @param dev HMAC-SHA-1 device instance. * @param digest Output digest buffer. * @param digestSz Digest size in bytes (must be 20). @@ -298,6 +302,8 @@ whal_Error whal_Stm32wba_HmacSha1_Finalize(whal_HmacSha1 *dev, /** * @brief HMAC-SHA-224 one-shot. * + * An empty message is not supported and returns WHAL_ENOTSUP. + * * @param dev HMAC-SHA-224 device instance. * @param key Key buffer. * @param keySz Key size in bytes. @@ -334,6 +340,8 @@ whal_Error whal_Stm32wba_HmacSha224_Process(whal_HmacSha224 *dev, /** * @brief Finalize an HMAC-SHA-224 session and produce the digest. * + * Returns WHAL_ENOTSUP if no message data was processed. + * * @param dev HMAC-SHA-224 device instance. * @param digest Output digest buffer. * @param digestSz Digest size in bytes (must be 28). @@ -346,6 +354,8 @@ whal_Error whal_Stm32wba_HmacSha224_Finalize(whal_HmacSha224 *dev, /** * @brief HMAC-SHA-256 one-shot. * + * An empty message is not supported and returns WHAL_ENOTSUP. + * * @param dev HMAC-SHA-256 device instance. * @param key Key buffer. * @param keySz Key size in bytes. @@ -382,6 +392,8 @@ whal_Error whal_Stm32wba_HmacSha256_Process(whal_HmacSha256 *dev, /** * @brief Finalize an HMAC-SHA-256 session and produce the digest. * + * Returns WHAL_ENOTSUP if no message data was processed. + * * @param dev HMAC-SHA-256 device instance. * @param digest Output digest buffer. * @param digestSz Digest size in bytes (must be 32). From 4eac50ed139803235603a9876d0e4c10820b815b Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Fri, 16 Oct 2026 13:37:30 -0400 Subject: [PATCH 22/22] [block, flash] Accept WHAL_INTERNAL_DEV device handles in SD and SPI-NOR Init - SD (SPI) and SPI-NOR Init no longer reject a NULL spiDev or gpioDev, since single-instance boards pass WHAL_INTERNAL_DEV (NULL) for them - Init still rejects a missing spiComCfg --- src/block/sdhc_spi_block.c | 2 +- src/flash/spi_nor_flash.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/block/sdhc_spi_block.c b/src/block/sdhc_spi_block.c index b30274e..6382eea 100644 --- a/src/block/sdhc_spi_block.c +++ b/src/block/sdhc_spi_block.c @@ -188,7 +188,7 @@ whal_Error whal_SdhcSpi_Init(whal_Block *blockDev) cfg = (whal_SdhcSpi_Cfg *)blockDev->cfg; #endif - if (!cfg->spiDev || !cfg->spiComCfg || !cfg->gpioDev) + if (!cfg->spiComCfg) return WHAL_EINVAL; slow = *cfg->spiComCfg; diff --git a/src/flash/spi_nor_flash.c b/src/flash/spi_nor_flash.c index 0715fde..b060432 100644 --- a/src/flash/spi_nor_flash.c +++ b/src/flash/spi_nor_flash.c @@ -188,7 +188,7 @@ whal_Error whal_SpiNor_Init(whal_Flash *flashDev) cfg = (whal_SpiNor_Cfg *)flashDev->cfg; #endif - if (!cfg->spiDev || !cfg->spiComCfg || !cfg->gpioDev) + if (!cfg->spiComCfg) return WHAL_EINVAL; /* Writes split pages with a mask, so pageSz must be a power of two */ if (cfg->pageSz == 0 || (cfg->pageSz & (cfg->pageSz - 1)) != 0 ||