From 362f144f13934bc511167e35c6dc8c07b5bce277 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 23 Jun 2026 11:22:55 +0000 Subject: [PATCH 01/22] AES: reject mode operations when no key has been set (F-6151) wc_AesInit zeroes the Aes struct, leaving keylen and rounds at 0. The CTR/CBC/GCM/ECB/CFB/OFB entry points never verified that a key had been installed with wc_AesSetKey, so calling them right after wc_AesInit ran the software cipher against an all-zero key schedule and returned success instead of an error. For CTR this exposes a reconstructable keystream; for GCM the hash subkey H is also zero, making the tag forgeable. Add the keylen == 0 guard already used by wc_AesXtsEncrypt to the software mode paths. The check is placed after the crypto callback fall-through so device-managed keys are unaffected. Hardware paths that call wc_AesGetKeySize already reject this case via rounds == 0. Add aes_no_key_set_test(), run from aes_test(), which inits an Aes context and confirms every mode rejects use before a key is set. --- wolfcrypt/src/aes.c | 64 ++++++++++++++++++++ wolfcrypt/test/test.c | 133 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index e96e4b1842..a2656ebe4f 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -7208,6 +7208,13 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) /* fall-through when unavailable */ } #endif + + /* Software/HW key schedule required from here on. */ + if (aes->keylen == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } + #if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_AES) /* if async and byte count above threshold */ if (aes->asyncDev.marker == WOLFSSL_ASYNC_MARKER_AES && @@ -7445,6 +7452,13 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) /* fall-through when unavailable */ } #endif + + /* Software/HW key schedule required from here on. */ + if (aes->keylen == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } + #if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_AES) /* if async and byte count above threshold */ if (aes->asyncDev.marker == WOLFSSL_ASYNC_MARKER_AES && @@ -7944,6 +7958,12 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) } #endif + /* Software/HW key schedule required from here on. */ + if (aes->keylen == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } + /* consume any unused bytes left in aes->tmp */ processed = min(aes->left, sz); xorbufout(out, in, (byte*)aes->tmp + WC_AES_BLOCK_SIZE - aes->left, @@ -11374,6 +11394,12 @@ int wc_AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz, } #endif + /* Software/HW key schedule (and hash subkey H) required from here on. */ + if (aes->keylen == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } + #if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_AES) /* if async and byte count above threshold */ /* only 12-byte IV is supported in HW */ @@ -12229,6 +12255,12 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, } #endif + /* Software/HW key schedule (and hash subkey H) required from here on. */ + if (aes->keylen == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } + #if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_AES) /* if async and byte count above threshold */ /* only 12-byte IV is supported in HW */ @@ -16366,6 +16398,12 @@ static WARN_UNUSED_RESULT int _AesEcbEncrypt( return DCPAesEcbEncrypt(aes, out, in, sz); #endif + /* Software key schedule required from here on. */ + if (aes->keylen == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } + VECTOR_REGISTERS_PUSH; #if defined(WOLFSSL_RISCV_ASM) @@ -16476,6 +16514,12 @@ static WARN_UNUSED_RESULT int _AesEcbDecrypt( return DCPAesEcbDecrypt(aes, out, in, sz); #endif + /* Software key schedule required from here on. */ + if (aes->keylen == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } + VECTOR_REGISTERS_PUSH; #if defined(WOLFSSL_RISCV_ASM) @@ -16631,6 +16675,10 @@ static WARN_UNUSED_RESULT int AesCfbEncrypt_C(Aes* aes, byte* out, if (sz == 0) { return 0; } + if (aes->keylen == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } if (aes->left > 0) { /* consume any unused bytes left in aes->tmp */ @@ -16714,6 +16762,10 @@ static WARN_UNUSED_RESULT int AesCfbDecrypt_C(Aes* aes, byte* out, if (sz == 0) { return 0; } + if (aes->keylen == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } if (aes->left > 0) { /* consume any unused bytes left in aes->tmp */ @@ -16942,6 +16994,10 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB8( if (sz == 0) { return 0; } + if (aes->keylen == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } VECTOR_REGISTERS_PUSH; @@ -17002,6 +17058,10 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB1( if (sz == 0) { return 0; } + if (aes->keylen == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } VECTOR_REGISTERS_PUSH; @@ -17160,6 +17220,10 @@ static WARN_UNUSED_RESULT int AesOfbCrypt_C(Aes* aes, byte* out, const byte* in, if (sz == 0) { return 0; } + if (aes->keylen == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } if (aes->left > 0) { /* consume any unused bytes left in aes->tmp */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 0556fd5351..614508e64d 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -16758,12 +16758,145 @@ static wc_test_ret_t aes_ecb_direct_test(void) } #endif /* HAVE_AES_ECB || WOLFSSL_AES_DIRECT */ +#if defined(HAVE_AES_CBC) || defined(WOLFSSL_AES_COUNTER) || \ + defined(HAVE_AESGCM) || defined(HAVE_AES_ECB) || \ + defined(WOLFSSL_AES_CFB) || defined(WOLFSSL_AES_OFB) +#define WC_TEST_HAVE_AES_NO_KEY_SET +/* Ensure AES mode APIs fail when used before wc_AesSetKey installs a key, + * instead of running with the all-zero key schedule left by wc_AesInit. */ +static wc_test_ret_t aes_no_key_set_test(void) +{ + wc_test_ret_t ret = 0; +#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) + Aes *aes = NULL; +#else + Aes aes[1]; +#endif + byte plain[WC_AES_BLOCK_SIZE]; + byte cipher[WC_AES_BLOCK_SIZE]; +#ifdef HAVE_AESGCM + byte iv[WC_AES_BLOCK_SIZE]; + byte tag[WC_AES_BLOCK_SIZE]; +#endif + + XMEMSET(plain, 0, sizeof(plain)); + XMEMSET(cipher, 0, sizeof(cipher)); +#ifdef HAVE_AESGCM + XMEMSET(iv, 0, sizeof(iv)); + XMEMSET(tag, 0, sizeof(tag)); +#endif + +#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) + aes = wc_AesNew(HEAP_HINT, devId, &ret); + if (aes == NULL) + return WC_TEST_RET_ENC_EC(ret); +#else + ret = wc_AesInit(aes, HEAP_HINT, devId); + if (ret != 0) + return WC_TEST_RET_ENC_EC(ret); +#endif + + /* No wc_AesSetKey: aes->keylen is 0, so every mode must reject the call. */ +#ifdef HAVE_AES_CBC + if (wc_AesCbcEncrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#ifdef HAVE_AES_DECRYPT + if (wc_AesCbcDecrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif +#endif /* HAVE_AES_CBC */ + +#ifdef WOLFSSL_AES_COUNTER + if (wc_AesCtrEncrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif + +#ifdef HAVE_AESGCM + if (wc_AesGcmEncrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE, iv, sizeof(iv), + tag, sizeof(tag), NULL, 0) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_AesGcmDecrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE, iv, sizeof(iv), + tag, sizeof(tag), NULL, 0) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif + +#ifdef HAVE_AES_ECB + if (wc_AesEcbEncrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#ifdef HAVE_AES_DECRYPT + if (wc_AesEcbDecrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif +#endif /* HAVE_AES_ECB */ + +#ifdef WOLFSSL_AES_CFB + if (wc_AesCfbEncrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#ifdef HAVE_AES_DECRYPT + if (wc_AesCfbDecrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif +#if !defined(WOLFSSL_NO_AES_CFB_1_8) + if (wc_AesCfb1Encrypt(aes, cipher, plain, 8) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_AesCfb8Encrypt(aes, cipher, plain, 1) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#ifdef HAVE_AES_DECRYPT + if (wc_AesCfb1Decrypt(aes, cipher, plain, 8) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_AesCfb8Decrypt(aes, cipher, plain, 1) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif +#endif /* !WOLFSSL_NO_AES_CFB_1_8 */ +#endif /* WOLFSSL_AES_CFB */ + +#ifdef WOLFSSL_AES_OFB + if (wc_AesOfbEncrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#ifdef HAVE_AES_DECRYPT + if (wc_AesOfbDecrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif +#endif /* WOLFSSL_AES_OFB */ + + ret = 0; /* success */ + out: + +#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) + wc_AesDelete(aes, &aes); +#else + wc_AesFree(aes); +#endif + + return ret; +} +#endif /* any AES mode for aes_no_key_set_test */ + WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_test(void) { wc_test_ret_t ret = 0; WOLFSSL_ENTER("aes_test"); +#ifdef WC_TEST_HAVE_AES_NO_KEY_SET + ret = aes_no_key_set_test(); + if (ret != 0) + return ret; +#endif + #ifndef HAVE_RENESAS_SYNC ret = aes_key_size_test(); if (ret != 0) From 521a217534c85d81124502a5fe5045e1bbf5a957 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 23 Jun 2026 11:26:39 +0000 Subject: [PATCH 02/22] RSA-PSS: test signature-to-message hash binding (F-6157) wc_RsaPSS_CheckPadding_ex2 binds a PSS signature to the message with a single comparison of the recomputed hash against the encoded hash H. Every positive PSS test passes the correct digest and the negative tests use a wrong salt length that fails before that comparison is reached, so deleting the comparison left all default tests passing even though any well-formed PSS structure would then verify against any message. After each successful sign/verify round-trip in rsa_pss_test, flip one bit of the message digest, keep the correct salt length, and require BAD_PADDING_E. This makes the hash comparison the deciding check and catches its removal. --- wolfcrypt/test/test.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 614508e64d..ca4cd75e19 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -30041,6 +30041,9 @@ done: static wc_test_ret_t rsa_pss_test(WC_RNG* rng, RsaKey* key) { byte digest[WC_MAX_DIGEST_SIZE]; +#ifndef WOLFSSL_MICROCHIP_TA100 + byte tamperedDigest[WC_MAX_DIGEST_SIZE]; +#endif wc_test_ret_t ret = 0; const char inStr[] = TEST_STRING; word32 inLen = (word32)TEST_STRING_SZ; @@ -30168,6 +30171,27 @@ static wc_test_ret_t rsa_pss_test(WC_RNG* rng, RsaKey* key) #endif if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), exit_rsa_pss); + + /* A well-formed PSS structure must not verify against a different + * message hash. Flip one digest bit, keep the correct salt length, + * and confirm the signature-to-message binding rejects it. */ + XMEMCPY(tamperedDigest, digest, digestSz); + tamperedDigest[0] ^= 0x01; +#if defined(HAVE_SELFTEST) && \ + (!defined(HAVE_SELFTEST_VERSION) || (HAVE_SELFTEST_VERSION < 2)) + ret = wc_RsaPSS_CheckPadding_ex(tamperedDigest, digestSz, plain, + plainSz, hash[j], -1); +#elif defined(HAVE_SELFTEST) && (HAVE_SELFTEST_VERSION == 2) + ret = wc_RsaPSS_CheckPadding_ex(tamperedDigest, digestSz, plain, + plainSz, hash[j], -1, 0); +#else + ret = wc_RsaPSS_CheckPadding_ex2(tamperedDigest, digestSz, plain, + plainSz, hash[j], -1, wc_RsaEncryptSize(key)*8, + HEAP_HINT); +#endif + if (ret != WC_NO_ERR_TRACE(BAD_PADDING_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), exit_rsa_pss); + ret = 0; #endif /* WOLFSSL_MICROCHIP_TA100 */ #ifdef RSA_PSS_TEST_WRONG_PARAMS From 4d18cc981edfd6f7999733c1c65587cc09db0cb0 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 23 Jun 2026 13:44:39 +0000 Subject: [PATCH 03/22] AES: track key-set state with a dedicated flag (F-6151) Replace the keylen == 0 guards with a new Aes.keySet bitfield, set by the key-installation paths and checked in the mode APIs. keylen alone is not a reliable "key installed" signal across backends: some ports map a zero key length to a default (e.g. PSOC6 maps keylen 0 to AES-128), so a keylen-based check placed only in the software paths left those builds running with the all-zero key schedule and would fail aes_no_key_set_test. keySet is set in wc_AesSetKeyLocal (the funnel for the software SetKey/SetKeyDirect/GcmSetKey paths) and in the PSOC6 wc_AesSetKey wrapper, and checked in the public CBC/CTR/GCM/ECB/CFB/OFB entry points, including the PSOC6 port variants. --- wolfcrypt/src/aes.c | 56 +++++++++++++++++++++++++++++++---------- wolfssl/wolfcrypt/aes.h | 5 ++++ 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index a2656ebe4f..fb3f1327a7 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -5297,7 +5297,10 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(Aes* aes, const byte* inBlock, int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen, const byte* iv, int dir) { - return wc_Psoc6_Aes_SetKey(aes, userKey, keylen, iv, dir); + int ret = wc_Psoc6_Aes_SetKey(aes, userKey, keylen, iv, dir); + if (ret == 0 && aes != NULL) + aes->keySet = 1; + return ret; } #if defined(WOLFSSL_AES_DIRECT) @@ -5620,6 +5623,7 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir) if (ret == 0) { /* Callback succeeded - SE owns the key */ aes->keylen = (int)keylen; + aes->keySet = 1; if (iv != NULL) XMEMCPY(aes->reg, iv, WC_AES_BLOCK_SIZE); else @@ -5736,6 +5740,7 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir) * reads it as the source of truth for the configured key size. */ aes->keylen = (int)keylen; aes->rounds = (keylen / 4) + 6; + aes->keySet = 1; #if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \ defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) || \ defined(WOLFSSL_AES_CTS) @@ -5765,6 +5770,7 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir) aes->keylen = (int)keylen; aes->rounds = (keylen/4) + 6; + aes->keySet = 1; ret = wc_AesSetIV(aes, iv); if (ret != 0) return ret; @@ -7139,12 +7145,20 @@ int wc_AesSetIV(Aes* aes, const byte* iv) int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { + if (aes == NULL || aes->keySet == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } return wc_Psoc6_Aes_CbcEncrypt(aes, out, in, sz); } #if defined(HAVE_AES_DECRYPT) int wc_AesCbcDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) { + if (aes == NULL || aes->keySet == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } return wc_Psoc6_Aes_CbcDecrypt(aes, out, in, sz); } #endif /* HAVE_AES_DECRYPT */ @@ -7210,7 +7224,7 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) #endif /* Software/HW key schedule required from here on. */ - if (aes->keylen == 0) { + if (aes->keySet == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -7454,7 +7468,7 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) #endif /* Software/HW key schedule required from here on. */ - if (aes->keylen == 0) { + if (aes->keySet == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -7959,7 +7973,7 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) #endif /* Software/HW key schedule required from here on. */ - if (aes->keylen == 0) { + if (aes->keySet == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -11395,7 +11409,7 @@ int wc_AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz, #endif /* Software/HW key schedule (and hash subkey H) required from here on. */ - if (aes->keylen == 0) { + if (aes->keySet == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -12256,7 +12270,7 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, #endif /* Software/HW key schedule (and hash subkey H) required from here on. */ - if (aes->keylen == 0) { + if (aes->keySet == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -16350,6 +16364,10 @@ int wc_AesEcbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { if ((in == NULL) || (out == NULL) || (aes == NULL)) return BAD_FUNC_ARG; + if (aes->keySet == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } return wc_Psoc6_Aes_EcbEncrypt(aes, out, in, sz); } @@ -16361,6 +16379,10 @@ int wc_AesEcbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) { if ((in == NULL) || (out == NULL) || (aes == NULL)) return BAD_FUNC_ARG; + if (aes->keySet == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } return wc_Psoc6_Aes_EcbDecrypt(aes, out, in, sz); } @@ -16399,7 +16421,7 @@ static WARN_UNUSED_RESULT int _AesEcbEncrypt( #endif /* Software key schedule required from here on. */ - if (aes->keylen == 0) { + if (aes->keySet == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -16515,7 +16537,7 @@ static WARN_UNUSED_RESULT int _AesEcbDecrypt( #endif /* Software key schedule required from here on. */ - if (aes->keylen == 0) { + if (aes->keySet == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -16637,12 +16659,20 @@ int wc_AesEcbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) int wc_AesCfbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { + if (aes == NULL || aes->keySet == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } return wc_Psoc6_Aes_CfbEncrypt(aes, out, in, sz); } #ifdef HAVE_AES_DECRYPT int wc_AesCfbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) { + if (aes == NULL || aes->keySet == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } return wc_Psoc6_Aes_CfbDecrypt(aes, out, in, sz); } #endif /* HAVE_AES_DECRYPT */ @@ -16675,7 +16705,7 @@ static WARN_UNUSED_RESULT int AesCfbEncrypt_C(Aes* aes, byte* out, if (sz == 0) { return 0; } - if (aes->keylen == 0) { + if (aes->keySet == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -16762,7 +16792,7 @@ static WARN_UNUSED_RESULT int AesCfbDecrypt_C(Aes* aes, byte* out, if (sz == 0) { return 0; } - if (aes->keylen == 0) { + if (aes->keySet == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -16994,7 +17024,7 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB8( if (sz == 0) { return 0; } - if (aes->keylen == 0) { + if (aes->keySet == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -17058,7 +17088,7 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB1( if (sz == 0) { return 0; } - if (aes->keylen == 0) { + if (aes->keySet == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -17220,7 +17250,7 @@ static WARN_UNUSED_RESULT int AesOfbCrypt_C(Aes* aes, byte* out, const byte* in, if (sz == 0) { return 0; } - if (aes->keylen == 0) { + if (aes->keySet == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } diff --git a/wolfssl/wolfcrypt/aes.h b/wolfssl/wolfcrypt/aes.h index 1eccc39cae..cb1287d19f 100644 --- a/wolfssl/wolfcrypt/aes.h +++ b/wolfssl/wolfcrypt/aes.h @@ -316,6 +316,11 @@ struct Aes { #endif int keylen; + /* Set to 1 once a key has been installed (wc_AesSetKey/SetKeyDirect/ + * GcmSetKey). Checked by the mode APIs so they fail instead of running + * with the all-zero key schedule left by wc_AesInit. */ + WC_BITFIELD keySet:1; + ALIGN16 word32 reg[WC_AES_BLOCK_SIZE / sizeof(word32)]; /* for CBC mode */ ALIGN16 word32 tmp[WC_AES_BLOCK_SIZE / sizeof(word32)]; /* same */ From 5ae42c4cddce334b24c58fe34c1d4d7f4b449aea Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 23 Jun 2026 17:00:57 +0000 Subject: [PATCH 04/22] AES: rename key-set flag and set it on every key install (F-6151) Address review of the keySet bitfield: - Rename keySet to keyInstalled. struct Aes already has a Cavium-only keySet member (HAVE_CAVIUM_OCTEON_SYNC), so an unconditional keySet bitfield was a duplicate that would not compile on that build. - Set keyInstalled on every key install, not just the software funnel. The ARM, PPC64 and other hardware wc_AesSetKey/SetKeyDirect variants populate keylen/rounds directly; without also setting the flag they would have made the shared mode APIs reject a validly-keyed context. The flag is now set at every keylen-assignment point in aes.c and in the PSOC6 SetKey wrapper. --- wolfcrypt/src/aes.c | 53 ++++++++++++++++++++++++----------------- wolfssl/wolfcrypt/aes.h | 5 ++-- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index fb3f1327a7..837a85f097 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -4632,6 +4632,7 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(Aes* aes, const byte* inBlock, rk = aes->key; aes->keylen = keylen; + aes->keyInstalled = 1; aes->rounds = keylen/4 + 6; XMEMCPY(rk, userKey, keylen); #ifdef WOLF_CRYPTO_CB @@ -4721,6 +4722,7 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(Aes* aes, const byte* inBlock, #endif aes->keylen = keylen; + aes->keyInstalled = 1; aes->rounds = keylen/4 + 6; XMEMCPY(aes->key, userKey, keylen); @@ -4805,6 +4807,7 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(Aes* aes, const byte* inBlock, #endif aes->keylen = keylen; + aes->keyInstalled = 1; aes->rounds = keylen/4 + 6; XMEMCPY(aes->key, userKey, keylen); ret = nrf51_aes_set_key(userKey); @@ -4864,6 +4867,7 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(Aes* aes, const byte* inBlock, #endif aes->keylen = keylen; + aes->keyInstalled = 1; aes->rounds = keylen/4 + 6; XMEMCPY(aes->key, userKey, keylen); @@ -4926,6 +4930,7 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(Aes* aes, const byte* inBlock, } aes->keylen = keylen; + aes->keyInstalled = 1; aes->rounds = keylen/4 + 6; XMEMCPY(aes->key, userKey, keylen); @@ -4991,6 +4996,7 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(Aes* aes, const byte* inBlock, aes->keylen = (int)keylen; aes->rounds = (keylen/4) + 6; + aes->keyInstalled = 1; #ifndef WOLFSSL_ARMASM_NO_HW_CRYPTO #ifdef WOLFSSL_ARM32_AES_DISPATCH @@ -5054,6 +5060,7 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(Aes* aes, const byte* inBlock, if (ret == 0) { /* Callback succeeded - SE owns the key */ aes->keylen = (int)keylen; + aes->keyInstalled = 1; if (iv != NULL) XMEMCPY(aes->reg, iv, WC_AES_BLOCK_SIZE); else @@ -5114,6 +5121,7 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(Aes* aes, const byte* inBlock, aes->keylen = (int)keylen; aes->rounds = (keylen/4) + 6; + aes->keyInstalled = 1; /* Determine base vs vector-crypto before the (dispatched) key setup so * the schedule matches the mode functions that later consume it. */ @@ -5162,6 +5170,7 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(Aes* aes, const byte* inBlock, if (ret == 0) { /* Callback succeeded - SE owns the key */ aes->keylen = (int)keylen; + aes->keyInstalled = 1; if (iv != NULL) XMEMCPY(aes->reg, iv, WC_AES_BLOCK_SIZE); else @@ -5299,7 +5308,7 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(Aes* aes, const byte* inBlock, { int ret = wc_Psoc6_Aes_SetKey(aes, userKey, keylen, iv, dir); if (ret == 0 && aes != NULL) - aes->keySet = 1; + aes->keyInstalled = 1; return ret; } @@ -5623,7 +5632,7 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir) if (ret == 0) { /* Callback succeeded - SE owns the key */ aes->keylen = (int)keylen; - aes->keySet = 1; + aes->keyInstalled = 1; if (iv != NULL) XMEMCPY(aes->reg, iv, WC_AES_BLOCK_SIZE); else @@ -5740,7 +5749,7 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir) * reads it as the source of truth for the configured key size. */ aes->keylen = (int)keylen; aes->rounds = (keylen / 4) + 6; - aes->keySet = 1; + aes->keyInstalled = 1; #if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \ defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) || \ defined(WOLFSSL_AES_CTS) @@ -5770,7 +5779,7 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir) aes->keylen = (int)keylen; aes->rounds = (keylen/4) + 6; - aes->keySet = 1; + aes->keyInstalled = 1; ret = wc_AesSetIV(aes, iv); if (ret != 0) return ret; @@ -7145,7 +7154,7 @@ int wc_AesSetIV(Aes* aes, const byte* iv) int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { - if (aes == NULL || aes->keySet == 0) { + if (aes == NULL || aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -7155,7 +7164,7 @@ int wc_AesSetIV(Aes* aes, const byte* iv) #if defined(HAVE_AES_DECRYPT) int wc_AesCbcDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) { - if (aes == NULL || aes->keySet == 0) { + if (aes == NULL || aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -7224,7 +7233,7 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) #endif /* Software/HW key schedule required from here on. */ - if (aes->keySet == 0) { + if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -7468,7 +7477,7 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) #endif /* Software/HW key schedule required from here on. */ - if (aes->keySet == 0) { + if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -7973,7 +7982,7 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) #endif /* Software/HW key schedule required from here on. */ - if (aes->keySet == 0) { + if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -11409,7 +11418,7 @@ int wc_AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz, #endif /* Software/HW key schedule (and hash subkey H) required from here on. */ - if (aes->keySet == 0) { + if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -12270,7 +12279,7 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, #endif /* Software/HW key schedule (and hash subkey H) required from here on. */ - if (aes->keySet == 0) { + if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -16364,7 +16373,7 @@ int wc_AesEcbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { if ((in == NULL) || (out == NULL) || (aes == NULL)) return BAD_FUNC_ARG; - if (aes->keySet == 0) { + if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -16379,7 +16388,7 @@ int wc_AesEcbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) { if ((in == NULL) || (out == NULL) || (aes == NULL)) return BAD_FUNC_ARG; - if (aes->keySet == 0) { + if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -16421,7 +16430,7 @@ static WARN_UNUSED_RESULT int _AesEcbEncrypt( #endif /* Software key schedule required from here on. */ - if (aes->keySet == 0) { + if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -16537,7 +16546,7 @@ static WARN_UNUSED_RESULT int _AesEcbDecrypt( #endif /* Software key schedule required from here on. */ - if (aes->keySet == 0) { + if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -16659,7 +16668,7 @@ int wc_AesEcbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) int wc_AesCfbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { - if (aes == NULL || aes->keySet == 0) { + if (aes == NULL || aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -16669,7 +16678,7 @@ int wc_AesCfbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) #ifdef HAVE_AES_DECRYPT int wc_AesCfbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) { - if (aes == NULL || aes->keySet == 0) { + if (aes == NULL || aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -16705,7 +16714,7 @@ static WARN_UNUSED_RESULT int AesCfbEncrypt_C(Aes* aes, byte* out, if (sz == 0) { return 0; } - if (aes->keySet == 0) { + if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -16792,7 +16801,7 @@ static WARN_UNUSED_RESULT int AesCfbDecrypt_C(Aes* aes, byte* out, if (sz == 0) { return 0; } - if (aes->keySet == 0) { + if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -17024,7 +17033,7 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB8( if (sz == 0) { return 0; } - if (aes->keySet == 0) { + if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -17088,7 +17097,7 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB1( if (sz == 0) { return 0; } - if (aes->keySet == 0) { + if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } @@ -17250,7 +17259,7 @@ static WARN_UNUSED_RESULT int AesOfbCrypt_C(Aes* aes, byte* out, const byte* in, if (sz == 0) { return 0; } - if (aes->keySet == 0) { + if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } diff --git a/wolfssl/wolfcrypt/aes.h b/wolfssl/wolfcrypt/aes.h index cb1287d19f..8bfd2b20fe 100644 --- a/wolfssl/wolfcrypt/aes.h +++ b/wolfssl/wolfcrypt/aes.h @@ -318,8 +318,9 @@ struct Aes { /* Set to 1 once a key has been installed (wc_AesSetKey/SetKeyDirect/ * GcmSetKey). Checked by the mode APIs so they fail instead of running - * with the all-zero key schedule left by wc_AesInit. */ - WC_BITFIELD keySet:1; + * with the all-zero key schedule left by wc_AesInit. Distinct from the + * Cavium-only keySet field below. */ + WC_BITFIELD keyInstalled:1; ALIGN16 word32 reg[WC_AES_BLOCK_SIZE / sizeof(word32)]; /* for CBC mode */ ALIGN16 word32 tmp[WC_AES_BLOCK_SIZE / sizeof(word32)]; /* same */ From 2b0e415821efc67946a689c110232f66c23bfd88 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 24 Jun 2026 11:43:42 +0000 Subject: [PATCH 05/22] AES: reject no-key use on PIC32MZ and RISC-V backends (F-6151) aes_no_key_set_test runs on every CI backend and exposed two ports whose own mode functions ran with an unset key. Other hardware backends (STM32, the secure-element sims) already reject this via wc_AesGetKeySize. - PIC32MZ: guard wc_AesCbcEncrypt/Decrypt. keyInstalled is already set through wc_AesSetKeyLocal, the generic SetKey funnel these use. - RISC-V assembly: set keyInstalled in all three wc_AesSetKey variants and guard CBC/ECB/GCM encrypt and decrypt. CTR already rejects via its rounds switch. --- wolfcrypt/src/aes.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 837a85f097..b926da9ae0 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -7054,6 +7054,11 @@ int wc_AesSetIV(Aes* aes, const byte* iv) { int ret; + if (aes == NULL || aes->keyInstalled == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } + if (sz == 0) return 0; @@ -7084,6 +7089,11 @@ int wc_AesSetIV(Aes* aes, const byte* iv) int ret; byte scratch[WC_AES_BLOCK_SIZE]; + if (aes == NULL || aes->keyInstalled == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } + if (sz == 0) return 0; From 181113f96b23f7ac4bb4540fd9d93efd8a07c851 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 24 Jun 2026 12:46:55 +0000 Subject: [PATCH 06/22] test: skip AES no-key test under FIPS and selftest (F-6151) Under FIPS/selftest the AES functions come from the validated module, which does not reject use before a key is installed, so aes_no_key_set_test failed on the CAVP selftest and FIPS customer-config CI jobs. The keyInstalled guard is a non-FIPS hardening, so gate the test on !HAVE_FIPS && !HAVE_SELFTEST. The FIPS CASTs (wc_RunAllCast_fips) pass, confirming the change does not affect validated AES. --- wolfcrypt/test/test.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index ca4cd75e19..9375942068 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -16758,9 +16758,13 @@ static wc_test_ret_t aes_ecb_direct_test(void) } #endif /* HAVE_AES_ECB || WOLFSSL_AES_DIRECT */ -#if defined(HAVE_AES_CBC) || defined(WOLFSSL_AES_COUNTER) || \ +/* The keyInstalled guard is a non-FIPS hardening; under FIPS/selftest the AES + * functions come from the validated module and don't reject a missing key, so + * this test does not apply there. */ +#if (defined(HAVE_AES_CBC) || defined(WOLFSSL_AES_COUNTER) || \ defined(HAVE_AESGCM) || defined(HAVE_AES_ECB) || \ - defined(WOLFSSL_AES_CFB) || defined(WOLFSSL_AES_OFB) + defined(WOLFSSL_AES_CFB) || defined(WOLFSSL_AES_OFB)) && \ + !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) #define WC_TEST_HAVE_AES_NO_KEY_SET /* Ensure AES mode APIs fail when used before wc_AesSetKey installs a key, * instead of running with the all-zero key schedule left by wc_AesInit. */ From 71d4b354cea1f5e5d622f38674f5bb6c2be128e9 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 24 Jun 2026 18:17:03 +0000 Subject: [PATCH 07/22] RSA-PSS test: use ret-independent failure code for tamper check (F-6157) Address review feedback on the negative tampered-digest assertion, which encoded ret via WC_TEST_RET_ENC_EC(ret). That is in fact non-zero even for ret == 0, but switch to WC_TEST_RET_ENC_NC so the failure code is unambiguously non-zero and independent of ret. Verified by mutation: deleting the signature-to-message hash compare still fails the test. --- wolfcrypt/test/test.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 9375942068..f4bb02def0 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -30193,8 +30193,12 @@ static wc_test_ret_t rsa_pss_test(WC_RNG* rng, RsaKey* key) plainSz, hash[j], -1, wc_RsaEncryptSize(key)*8, HEAP_HINT); #endif + /* Negative test: any result other than BAD_PADDING_E is a + * failure. In particular a 0 return (tampered digest wrongly + * accepted) must fail the test, so use a ret-independent, + * non-zero code rather than encoding ret. */ if (ret != WC_NO_ERR_TRACE(BAD_PADDING_E)) - ERROR_OUT(WC_TEST_RET_ENC_EC(ret), exit_rsa_pss); + ERROR_OUT(WC_TEST_RET_ENC_NC, exit_rsa_pss); ret = 0; #endif /* WOLFSSL_MICROCHIP_TA100 */ From e603c07897121960bc7de73f6f7cc2dd3c0cdeb6 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 14 Jul 2026 12:35:17 +0000 Subject: [PATCH 08/22] AES-CCM: reject use before a key is set (F-6151) CCM has the same zero-key exposure as GCM: CBC-MAC and CTR run against the all-zero key schedule left by wc_AesInit. Add the keyInstalled guard to the software CCM encrypt/decrypt paths, mirroring GCM, and to the RISC-V port so it returns BAD_FUNC_ARG instead of KEYUSAGE_E. Extend aes_no_key_set_test with CCM cases. --- wolfcrypt/src/aes.c | 12 ++++++++++++ wolfcrypt/test/test.c | 17 ++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index b926da9ae0..89400227c8 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -15592,6 +15592,12 @@ int wc_AesCcmEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz, } #endif + /* Software/HW key schedule required from here on. */ + if (aes->keyInstalled == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } + XMEMSET(A, 0, sizeof(A)); XMEMCPY(B+1, nonce, nonceSz); @@ -15756,6 +15762,12 @@ int wc_AesCcmDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz, } #endif + /* Software/HW key schedule required from here on. */ + if (aes->keyInstalled == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } + o = out; oSz = inSz; XMEMSET(A, 0, sizeof A); diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index f4bb02def0..8dd3577484 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -16762,7 +16762,7 @@ static wc_test_ret_t aes_ecb_direct_test(void) * functions come from the validated module and don't reject a missing key, so * this test does not apply there. */ #if (defined(HAVE_AES_CBC) || defined(WOLFSSL_AES_COUNTER) || \ - defined(HAVE_AESGCM) || defined(HAVE_AES_ECB) || \ + defined(HAVE_AESGCM) || defined(HAVE_AESCCM) || defined(HAVE_AES_ECB) || \ defined(WOLFSSL_AES_CFB) || defined(WOLFSSL_AES_OFB)) && \ !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) #define WC_TEST_HAVE_AES_NO_KEY_SET @@ -16778,14 +16778,14 @@ static wc_test_ret_t aes_no_key_set_test(void) #endif byte plain[WC_AES_BLOCK_SIZE]; byte cipher[WC_AES_BLOCK_SIZE]; -#ifdef HAVE_AESGCM +#if defined(HAVE_AESGCM) || defined(HAVE_AESCCM) byte iv[WC_AES_BLOCK_SIZE]; byte tag[WC_AES_BLOCK_SIZE]; #endif XMEMSET(plain, 0, sizeof(plain)); XMEMSET(cipher, 0, sizeof(cipher)); -#ifdef HAVE_AESGCM +#if defined(HAVE_AESGCM) || defined(HAVE_AESCCM) XMEMSET(iv, 0, sizeof(iv)); XMEMSET(tag, 0, sizeof(tag)); #endif @@ -16827,6 +16827,17 @@ static wc_test_ret_t aes_no_key_set_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #endif +#ifdef HAVE_AESCCM + if (wc_AesCcmEncrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE, iv, 13, + tag, sizeof(tag), NULL, 0) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#ifdef HAVE_AES_DECRYPT + if (wc_AesCcmDecrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE, iv, 13, + tag, sizeof(tag), NULL, 0) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif +#endif /* HAVE_AESCCM */ + #ifdef HAVE_AES_ECB if (wc_AesEcbEncrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) From 797cf23fd856acca3fe88560074ae8717f0939f3 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 14 Jul 2026 12:35:39 +0000 Subject: [PATCH 09/22] AES: check for a key before the sz == 0 early return (F-6151) In the software CFB/CFB1/CFB8/OFB helpers the sz == 0 early return came before the keyInstalled check, so a no-key call succeeded with sz == 0 but failed with sz > 0. Check the key first; crypto callbacks already ran in the callers. CBC keeps its sz == 0 return first because the DCP dispatch below it cannot handle sz == 0 (reads out + sz - 16); comment the ordering there. --- wolfcrypt/src/aes.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 89400227c8..2b641af2da 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -7203,6 +7203,8 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) } if (sz == 0) { + /* Keep above the DCP/crypto-cb dispatches: they must not see + * sz == 0. A missing key is only reported when there is work. */ return 0; } @@ -7431,6 +7433,8 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) } if (sz == 0) { + /* Keep above the DCP/crypto-cb dispatches: they must not see + * sz == 0. A missing key is only reported when there is work. */ return 0; } @@ -16733,13 +16737,13 @@ static WARN_UNUSED_RESULT int AesCfbEncrypt_C(Aes* aes, byte* out, if ((aes == NULL) || (out == NULL) || (in == NULL)) { return BAD_FUNC_ARG; } - if (sz == 0) { - return 0; - } if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } + if (sz == 0) { + return 0; + } if (aes->left > 0) { /* consume any unused bytes left in aes->tmp */ @@ -16820,13 +16824,13 @@ static WARN_UNUSED_RESULT int AesCfbDecrypt_C(Aes* aes, byte* out, if ((aes == NULL) || (out == NULL) || (in == NULL)) { return BAD_FUNC_ARG; } - if (sz == 0) { - return 0; - } if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } + if (sz == 0) { + return 0; + } if (aes->left > 0) { /* consume any unused bytes left in aes->tmp */ @@ -17052,13 +17056,13 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB8( return BAD_FUNC_ARG; } - if (sz == 0) { - return 0; - } if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } + if (sz == 0) { + return 0; + } VECTOR_REGISTERS_PUSH; @@ -17116,13 +17120,13 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB1( return BAD_FUNC_ARG; } - if (sz == 0) { - return 0; - } if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } + if (sz == 0) { + return 0; + } VECTOR_REGISTERS_PUSH; @@ -17278,13 +17282,13 @@ static WARN_UNUSED_RESULT int AesOfbCrypt_C(Aes* aes, byte* out, const byte* in, if ((aes == NULL) || (out == NULL) || (in == NULL)) { return BAD_FUNC_ARG; } - if (sz == 0) { - return 0; - } if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); return BAD_FUNC_ARG; } + if (sz == 0) { + return 0; + } if (aes->left > 0) { /* consume any unused bytes left in aes->tmp */ From dab0b9299270189d60dcbc9ecbfaafe47bca4341 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 15 Jul 2026 02:31:39 +0000 Subject: [PATCH 10/22] Address review: set keyInstalled in HW port key setup (F-6151) The keyInstalled guard added to the shared aes.c mode functions is only set by aes.c's own key schedule and the RISC-V port. Ports that ship their own compile-time wc_AesSetKey/wc_AesGcmSetKey left keyInstalled at 0, so any AES mode that falls through to a guarded aes.c path returned BAD_FUNC_ARG after a correct key setup. Set keyInstalled at the key-install point of the affected ports so the shared mode guards accept a validly-keyed context: - silabs, af_alg, devcrypto, ti, caam: wc_AesSetKey - af_alg, kcapi: wc_AesGcmSetKey --- wolfcrypt/src/port/af_alg/afalg_aes.c | 4 ++++ wolfcrypt/src/port/caam/caam_aes.c | 2 ++ wolfcrypt/src/port/devcrypto/devcrypto_aes.c | 2 ++ wolfcrypt/src/port/kcapi/kcapi_aes.c | 3 +++ wolfcrypt/src/port/silabs/silabs_aes.c | 3 +++ wolfcrypt/src/port/ti/ti-aes.c | 2 ++ 6 files changed, 16 insertions(+) diff --git a/wolfcrypt/src/port/af_alg/afalg_aes.c b/wolfcrypt/src/port/af_alg/afalg_aes.c index efbf635859..5ee66a3e98 100644 --- a/wolfcrypt/src/port/af_alg/afalg_aes.c +++ b/wolfcrypt/src/port/af_alg/afalg_aes.c @@ -146,6 +146,8 @@ int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen, #endif aes->keylen = keylen; aes->rounds = keylen/4 + 6; + /* Mark key installed so the shared aes.c mode guards accept this context. */ + aes->keyInstalled = 1; #ifdef WOLFSSL_AES_COUNTER aes->left = 0; @@ -556,6 +558,8 @@ int wc_AesGcmSetKey(Aes* aes, const byte* key, word32 len) aes->keylen = len; aes->rounds = len/4 + 6; aes->dir = AES_ENCRYPTION; + /* Mark key installed so the shared aes.c mode guards accept this context. */ + aes->keyInstalled = 1; if (aes->rdFd > WC_SOCK_NOTSET) { (void)close(aes->rdFd); diff --git a/wolfcrypt/src/port/caam/caam_aes.c b/wolfcrypt/src/port/caam/caam_aes.c index 4ab3ee7f94..79e6a8056a 100644 --- a/wolfcrypt/src/port/caam/caam_aes.c +++ b/wolfcrypt/src/port/caam/caam_aes.c @@ -88,6 +88,8 @@ int wc_AesSetKey(Aes* aes, const byte* key, word32 len, default: return BAD_FUNC_ARG; } + /* Mark key installed so the shared aes.c mode guards accept this context. */ + aes->keyInstalled = 1; if ((ret = wc_AesSetIV(aes, iv)) != 0) { return ret; diff --git a/wolfcrypt/src/port/devcrypto/devcrypto_aes.c b/wolfcrypt/src/port/devcrypto/devcrypto_aes.c index fe53360d58..916797bcf1 100644 --- a/wolfcrypt/src/port/devcrypto/devcrypto_aes.c +++ b/wolfcrypt/src/port/devcrypto/devcrypto_aes.c @@ -123,6 +123,8 @@ int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen, #endif aes->keylen = keylen; aes->rounds = keylen/4 + 6; + /* Mark key installed so the shared aes.c mode guards accept this context. */ + aes->keyInstalled = 1; #if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \ defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) diff --git a/wolfcrypt/src/port/kcapi/kcapi_aes.c b/wolfcrypt/src/port/kcapi/kcapi_aes.c index 0aab365a3c..d2cd8be371 100644 --- a/wolfcrypt/src/port/kcapi/kcapi_aes.c +++ b/wolfcrypt/src/port/kcapi/kcapi_aes.c @@ -208,6 +208,9 @@ int wc_AesGcmSetKey(Aes* aes, const byte* key, word32 len) if (ret == 0) { aes->keylen = len; aes->rounds = len/4 + 6; + /* Mark key installed so the shared aes.c mode guards accept this + * context. */ + aes->keyInstalled = 1; /* save key until type is known i.e. CBC, ECB, ... */ XMEMCPY((byte*)(aes->devKey), key, len); diff --git a/wolfcrypt/src/port/silabs/silabs_aes.c b/wolfcrypt/src/port/silabs/silabs_aes.c index 498acea21c..10b6995514 100644 --- a/wolfcrypt/src/port/silabs/silabs_aes.c +++ b/wolfcrypt/src/port/silabs/silabs_aes.c @@ -87,6 +87,9 @@ int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen, aes->ctx.key.storage.location.buffer.pointer = (void*)aes->key; aes->ctx.key.storage.location.buffer.size = keylen; aes->ctx.key.size = keylen; + /* Mark key installed so the shared aes.c mode guards accept this + * context. */ + aes->keyInstalled = 1; } return ret; diff --git a/wolfcrypt/src/port/ti/ti-aes.c b/wolfcrypt/src/port/ti/ti-aes.c index 4718d97ab2..16385a1962 100644 --- a/wolfcrypt/src/port/ti/ti-aes.c +++ b/wolfcrypt/src/port/ti/ti-aes.c @@ -103,6 +103,8 @@ int wc_AesSetKey(Aes* aes, const byte* key, word32 len, const byte* iv, int dir) } aes->keylen = len; aes->rounds = len / 4 + 6; + /* Mark key installed so the shared aes.c mode guards accept this context. */ + aes->keyInstalled = 1; XMEMCPY(aes->key, key, len); #if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \ From 8b5662ee9bea1efbe7cee122d4c42a36e6eda09f Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 16 Jul 2026 02:22:10 +0000 Subject: [PATCH 11/22] Address review: set keyInstalled in SECO CAAM key setup (F-6151) --- wolfcrypt/src/aes.c | 1 + 1 file changed, 1 insertion(+) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 2b641af2da..1dbf29066c 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -5721,6 +5721,7 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir) return WC_HW_E; } aes->blackKey = keyIdOut; + aes->keyInstalled = 1; return 0; } #endif From 44965314ae608ab10ac7cf0b990dc724ebe10f7f Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 16 Jul 2026 02:22:10 +0000 Subject: [PATCH 12/22] Address review: use INVALID_DEVID in aes_no_key_set_test (F-6151) --- wolfcrypt/test/test.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 8dd3577484..a1ff0fa15c 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -16790,12 +16790,16 @@ static wc_test_ret_t aes_no_key_set_test(void) XMEMSET(tag, 0, sizeof(tag)); #endif + /* The keyInstalled guard is pure-software hardening placed after the + * crypto-cb dispatch, so use INVALID_DEVID here: a registered device would + * route these calls to the callback (against a zero key) and never reach + * the guard. */ #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) - aes = wc_AesNew(HEAP_HINT, devId, &ret); + aes = wc_AesNew(HEAP_HINT, INVALID_DEVID, &ret); if (aes == NULL) return WC_TEST_RET_ENC_EC(ret); #else - ret = wc_AesInit(aes, HEAP_HINT, devId); + ret = wc_AesInit(aes, HEAP_HINT, INVALID_DEVID); if (ret != 0) return WC_TEST_RET_ENC_EC(ret); #endif From aaf9c7e2cba636df183a2547fef1e6b1d9edcd70 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 20 Jul 2026 02:21:54 +0000 Subject: [PATCH 13/22] Fix CI: reject no-key AES-CBC on RISC-V backend (F-6151) The RISC-V assembly CBC encrypt/decrypt paths return before the generic keyInstalled guard, so wc_AesCbcEncrypt/Decrypt accepted a call with no key set. aes_no_key_set_test caught this on the riscv64-o0 config. Add the guard to the RISC-V dispatch, matching the other backends. --- wolfcrypt/src/aes.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 1dbf29066c..9f3611baae 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -7220,6 +7220,10 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) #endif #if defined(WOLFSSL_RISCV_ASM) + if (aes->keyInstalled == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } AES_CBC_encrypt_RISCV64(in, out, sz, (byte*)aes->reg, (byte*)aes->key, (int)aes->rounds); (void)blocks; @@ -7466,6 +7470,10 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) } #if defined(WOLFSSL_RISCV_ASM) + if (aes->keyInstalled == 0) { + WOLFSSL_MSG("AES key not set"); + return BAD_FUNC_ARG; + } AES_CBC_decrypt_RISCV64(in, out, sz, (byte*)aes->reg, (byte*)aes->key, (int)aes->rounds); (void)blocks; From 67c11acddfd0c39c70e825c53f59e1f45f99fa79 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 21 Jul 2026 15:59:31 +0000 Subject: [PATCH 14/22] Address review: set keyInstalled in FREESCALE_LTC/MMCAU key setup (F-6151) The keyInstalled guard added to the shared aes.c mode functions is set by every in-aes.c key-schedule branch except FREESCALE_LTC and FREESCALE_MMCAU. Their wc_AesSetKeyLocal installs the key but left keyInstalled at 0, so any guarded AES mode that falls through to a shared aes.c path returned an error after a valid key setup on those ports. Set keyInstalled at the key-install point in both branches, matching the other ports fixed earlier in this PR. --- wolfcrypt/src/aes.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 9f3611baae..a14b1e4213 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -4760,6 +4760,7 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(Aes* aes, const byte* inBlock, aes->rounds = keylen/4 + 6; XMEMCPY(aes->key, userKey, keylen); + aes->keyInstalled = 1; #if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \ defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) || \ @@ -5279,6 +5280,8 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(Aes* aes, const byte* inBlock, #endif wolfSSL_CryptHwMutexUnLock(); + aes->keyInstalled = 1; + ret = wc_AesSetIV(aes, iv); } From 6b8634c89ab1aab06849e2be2bb4547a1a65f34b Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 24 Jul 2026 03:05:20 +0000 Subject: [PATCH 15/22] Address review: reject no-key AES on AF_ALG backend (F-6151) The AF_ALG AES mode overrides route through wc_AesSetup, which called setsockopt(ALG_SET_KEY, key, 0) with keylen 0 and returned WC_AFALG_SOCK_E instead of BAD_FUNC_ARG when no key was installed, so aes_no_key_set_test failed on WOLFSSL_AFALG. Reject setup when keyInstalled is 0, matching the software backend's no-key guard. Covers CBC, CTR, ECB and GCM. --- wolfcrypt/src/port/af_alg/afalg_aes.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wolfcrypt/src/port/af_alg/afalg_aes.c b/wolfcrypt/src/port/af_alg/afalg_aes.c index 5ee66a3e98..8fefe25ff4 100644 --- a/wolfcrypt/src/port/af_alg/afalg_aes.c +++ b/wolfcrypt/src/port/af_alg/afalg_aes.c @@ -60,6 +60,10 @@ static int wc_AesSetup(Aes* aes, const char* type, const char* name, int ivSz, i byte* key = (byte*)aes->key; #endif + if (aes->keyInstalled == 0) { + return BAD_FUNC_ARG; + } + if (aes->alFd == WC_SOCK_NOTSET) { aes->alFd = wc_Afalg_Socket(); if (aes->alFd < 0) { From bf99dfd5ecf6bc3cbb51e3b1b9d02d68f7f1bbf9 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 5 Aug 2026 10:31:34 +0000 Subject: [PATCH 16/22] Address review: AES no-key guard refinements - Move Aes.keyInstalled to the end of struct Aes so existing member offsets are unchanged (avoids an ABI break) and it is available in all configs that set it. - Report a missing key with MISSING_KEY instead of BAD_FUNC_ARG in the AES mode guards (aes.c and the AF_ALG backend), splitting the combined NULL/no-key checks so a NULL argument still returns BAD_FUNC_ARG. Update the no-key self test accordingly. - Make sz==0 handling consistent: return 0 before the no-key guard in the CFB/OFB/CFB1/CFB8 and PIC32MZ CBC paths, and add an sz==0 early return to wc_AesCtrEncrypt. - Place the RISC-V AES-CBC path after the DCP and crypto callback dispatches so a single key guard sits after all offload dispatches. --- wolfcrypt/src/aes.c | 154 ++++++++++++++------------ wolfcrypt/src/port/af_alg/afalg_aes.c | 2 +- wolfcrypt/test/test.c | 34 +++--- wolfssl/wolfcrypt/aes.h | 13 ++- 4 files changed, 108 insertions(+), 95 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index a14b1e4213..c3eda5bed8 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -7058,14 +7058,17 @@ int wc_AesSetIV(Aes* aes, const byte* iv) { int ret; - if (aes == NULL || aes->keyInstalled == 0) { - WOLFSSL_MSG("AES key not set"); + if (aes == NULL) return BAD_FUNC_ARG; - } if (sz == 0) return 0; + if (aes->keyInstalled == 0) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } + /* hardware fails on input that is not a multiple of AES block size */ if (sz % WC_AES_BLOCK_SIZE != 0) { #ifdef WOLFSSL_AES_CBC_LENGTH_CHECKS @@ -7093,14 +7096,17 @@ int wc_AesSetIV(Aes* aes, const byte* iv) int ret; byte scratch[WC_AES_BLOCK_SIZE]; - if (aes == NULL || aes->keyInstalled == 0) { - WOLFSSL_MSG("AES key not set"); + if (aes == NULL) return BAD_FUNC_ARG; - } if (sz == 0) return 0; + if (aes->keyInstalled == 0) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } + /* hardware fails on input that is not a multiple of AES block size */ if (sz % WC_AES_BLOCK_SIZE != 0) { #ifdef WOLFSSL_AES_CBC_LENGTH_CHECKS @@ -7168,9 +7174,11 @@ int wc_AesSetIV(Aes* aes, const byte* iv) int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { - if (aes == NULL || aes->keyInstalled == 0) { - WOLFSSL_MSG("AES key not set"); + if (aes == NULL) return BAD_FUNC_ARG; + if (aes->keyInstalled == 0) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; } return wc_Psoc6_Aes_CbcEncrypt(aes, out, in, sz); } @@ -7178,9 +7186,11 @@ int wc_AesSetIV(Aes* aes, const byte* iv) #if defined(HAVE_AES_DECRYPT) int wc_AesCbcDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) { - if (aes == NULL || aes->keyInstalled == 0) { - WOLFSSL_MSG("AES key not set"); + if (aes == NULL) return BAD_FUNC_ARG; + if (aes->keyInstalled == 0) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; } return wc_Psoc6_Aes_CbcDecrypt(aes, out, in, sz); } @@ -7222,18 +7232,6 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) } #endif -#if defined(WOLFSSL_RISCV_ASM) - if (aes->keyInstalled == 0) { - WOLFSSL_MSG("AES key not set"); - return BAD_FUNC_ARG; - } - AES_CBC_encrypt_RISCV64(in, out, sz, (byte*)aes->reg, (byte*)aes->key, - (int)aes->rounds); - (void)blocks; - (void)ret; - return 0; -#endif - #ifdef WOLFSSL_IMXRT_DCP /* Implemented in wolfcrypt/src/port/nxp/dcp_port.c */ if (aes->keylen == 16) @@ -7252,12 +7250,20 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) } #endif - /* Software/HW key schedule required from here on. */ + /* Single key guard after all offload dispatches. */ if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); - return BAD_FUNC_ARG; + return MISSING_KEY; } +#if defined(WOLFSSL_RISCV_ASM) + AES_CBC_encrypt_RISCV64(in, out, sz, (byte*)aes->reg, (byte*)aes->key, + (int)aes->rounds); + (void)blocks; + (void)ret; + return 0; +#endif + #if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_AES) /* if async and byte count above threshold */ if (aes->asyncDev.marker == WOLFSSL_ASYNC_MARKER_AES && @@ -7472,18 +7478,6 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) #endif } -#if defined(WOLFSSL_RISCV_ASM) - if (aes->keyInstalled == 0) { - WOLFSSL_MSG("AES key not set"); - return BAD_FUNC_ARG; - } - AES_CBC_decrypt_RISCV64(in, out, sz, (byte*)aes->reg, (byte*)aes->key, - (int)aes->rounds); - (void)blocks; - (void)ret; - return 0; -#endif - #ifdef WOLFSSL_IMXRT_DCP /* Implemented in wolfcrypt/src/port/nxp/dcp_port.c */ if (aes->keylen == 16) @@ -7502,12 +7496,20 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) } #endif - /* Software/HW key schedule required from here on. */ + /* Single key guard after all offload dispatches. */ if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); - return BAD_FUNC_ARG; + return MISSING_KEY; } +#if defined(WOLFSSL_RISCV_ASM) + AES_CBC_decrypt_RISCV64(in, out, sz, (byte*)aes->reg, (byte*)aes->key, + (int)aes->rounds); + (void)blocks; + (void)ret; + return 0; +#endif + #if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_AES) /* if async and byte count above threshold */ if (aes->asyncDev.marker == WOLFSSL_ASYNC_MARKER_AES && @@ -7995,6 +7997,12 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) return BAD_FUNC_ARG; } + if (sz == 0) { + /* Keep above the crypto-cb dispatch: it must not see sz == 0. + * A missing key is only reported when there is work. */ + return 0; + } + #ifdef WOLF_CRYPTO_CB #ifndef WOLF_CRYPTO_CB_FIND if (aes->devId != INVALID_DEVID) @@ -8010,7 +8018,7 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) /* Software/HW key schedule required from here on. */ if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); - return BAD_FUNC_ARG; + return MISSING_KEY; } /* consume any unused bytes left in aes->tmp */ @@ -11446,7 +11454,7 @@ int wc_AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz, /* Software/HW key schedule (and hash subkey H) required from here on. */ if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); - return BAD_FUNC_ARG; + return MISSING_KEY; } #if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_AES) @@ -12307,7 +12315,7 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, /* Software/HW key schedule (and hash subkey H) required from here on. */ if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); - return BAD_FUNC_ARG; + return MISSING_KEY; } #if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_AES) @@ -15611,7 +15619,7 @@ int wc_AesCcmEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz, /* Software/HW key schedule required from here on. */ if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); - return BAD_FUNC_ARG; + return MISSING_KEY; } XMEMSET(A, 0, sizeof(A)); @@ -15781,7 +15789,7 @@ int wc_AesCcmDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz, /* Software/HW key schedule required from here on. */ if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); - return BAD_FUNC_ARG; + return MISSING_KEY; } o = out; @@ -16413,7 +16421,7 @@ int wc_AesEcbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) return BAD_FUNC_ARG; if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); - return BAD_FUNC_ARG; + return MISSING_KEY; } return wc_Psoc6_Aes_EcbEncrypt(aes, out, in, sz); @@ -16428,7 +16436,7 @@ int wc_AesEcbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) return BAD_FUNC_ARG; if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); - return BAD_FUNC_ARG; + return MISSING_KEY; } return wc_Psoc6_Aes_EcbDecrypt(aes, out, in, sz); @@ -16470,7 +16478,7 @@ static WARN_UNUSED_RESULT int _AesEcbEncrypt( /* Software key schedule required from here on. */ if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); - return BAD_FUNC_ARG; + return MISSING_KEY; } VECTOR_REGISTERS_PUSH; @@ -16586,7 +16594,7 @@ static WARN_UNUSED_RESULT int _AesEcbDecrypt( /* Software key schedule required from here on. */ if (aes->keyInstalled == 0) { WOLFSSL_MSG("AES key not set"); - return BAD_FUNC_ARG; + return MISSING_KEY; } VECTOR_REGISTERS_PUSH; @@ -16706,9 +16714,11 @@ int wc_AesEcbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) int wc_AesCfbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { - if (aes == NULL || aes->keyInstalled == 0) { - WOLFSSL_MSG("AES key not set"); + if (aes == NULL) return BAD_FUNC_ARG; + if (aes->keyInstalled == 0) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; } return wc_Psoc6_Aes_CfbEncrypt(aes, out, in, sz); } @@ -16716,9 +16726,11 @@ int wc_AesCfbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) #ifdef HAVE_AES_DECRYPT int wc_AesCfbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) { - if (aes == NULL || aes->keyInstalled == 0) { - WOLFSSL_MSG("AES key not set"); + if (aes == NULL) return BAD_FUNC_ARG; + if (aes->keyInstalled == 0) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; } return wc_Psoc6_Aes_CfbDecrypt(aes, out, in, sz); } @@ -16749,13 +16761,13 @@ static WARN_UNUSED_RESULT int AesCfbEncrypt_C(Aes* aes, byte* out, if ((aes == NULL) || (out == NULL) || (in == NULL)) { return BAD_FUNC_ARG; } - if (aes->keyInstalled == 0) { - WOLFSSL_MSG("AES key not set"); - return BAD_FUNC_ARG; - } if (sz == 0) { return 0; } + if (aes->keyInstalled == 0) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } if (aes->left > 0) { /* consume any unused bytes left in aes->tmp */ @@ -16836,13 +16848,13 @@ static WARN_UNUSED_RESULT int AesCfbDecrypt_C(Aes* aes, byte* out, if ((aes == NULL) || (out == NULL) || (in == NULL)) { return BAD_FUNC_ARG; } - if (aes->keyInstalled == 0) { - WOLFSSL_MSG("AES key not set"); - return BAD_FUNC_ARG; - } if (sz == 0) { return 0; } + if (aes->keyInstalled == 0) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } if (aes->left > 0) { /* consume any unused bytes left in aes->tmp */ @@ -17068,13 +17080,13 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB8( return BAD_FUNC_ARG; } - if (aes->keyInstalled == 0) { - WOLFSSL_MSG("AES key not set"); - return BAD_FUNC_ARG; - } if (sz == 0) { return 0; } + if (aes->keyInstalled == 0) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } VECTOR_REGISTERS_PUSH; @@ -17132,13 +17144,13 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB1( return BAD_FUNC_ARG; } - if (aes->keyInstalled == 0) { - WOLFSSL_MSG("AES key not set"); - return BAD_FUNC_ARG; - } if (sz == 0) { return 0; } + if (aes->keyInstalled == 0) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } VECTOR_REGISTERS_PUSH; @@ -17294,13 +17306,13 @@ static WARN_UNUSED_RESULT int AesOfbCrypt_C(Aes* aes, byte* out, const byte* in, if ((aes == NULL) || (out == NULL) || (in == NULL)) { return BAD_FUNC_ARG; } - if (aes->keyInstalled == 0) { - WOLFSSL_MSG("AES key not set"); - return BAD_FUNC_ARG; - } if (sz == 0) { return 0; } + if (aes->keyInstalled == 0) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } if (aes->left > 0) { /* consume any unused bytes left in aes->tmp */ diff --git a/wolfcrypt/src/port/af_alg/afalg_aes.c b/wolfcrypt/src/port/af_alg/afalg_aes.c index 8fefe25ff4..764b43e18f 100644 --- a/wolfcrypt/src/port/af_alg/afalg_aes.c +++ b/wolfcrypt/src/port/af_alg/afalg_aes.c @@ -61,7 +61,7 @@ static int wc_AesSetup(Aes* aes, const char* type, const char* name, int ivSz, i #endif if (aes->keyInstalled == 0) { - return BAD_FUNC_ARG; + return MISSING_KEY; } if (aes->alFd == WC_SOCK_NOTSET) { diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index a1ff0fa15c..cf3fb21969 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -16807,74 +16807,74 @@ static wc_test_ret_t aes_no_key_set_test(void) /* No wc_AesSetKey: aes->keylen is 0, so every mode must reject the call. */ #ifdef HAVE_AES_CBC if (wc_AesCbcEncrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + WC_NO_ERR_TRACE(MISSING_KEY)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #ifdef HAVE_AES_DECRYPT if (wc_AesCbcDecrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + WC_NO_ERR_TRACE(MISSING_KEY)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #endif #endif /* HAVE_AES_CBC */ #ifdef WOLFSSL_AES_COUNTER if (wc_AesCtrEncrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + WC_NO_ERR_TRACE(MISSING_KEY)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #endif #ifdef HAVE_AESGCM if (wc_AesGcmEncrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE, iv, sizeof(iv), - tag, sizeof(tag), NULL, 0) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + tag, sizeof(tag), NULL, 0) != WC_NO_ERR_TRACE(MISSING_KEY)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); if (wc_AesGcmDecrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE, iv, sizeof(iv), - tag, sizeof(tag), NULL, 0) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + tag, sizeof(tag), NULL, 0) != WC_NO_ERR_TRACE(MISSING_KEY)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #endif #ifdef HAVE_AESCCM if (wc_AesCcmEncrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE, iv, 13, - tag, sizeof(tag), NULL, 0) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + tag, sizeof(tag), NULL, 0) != WC_NO_ERR_TRACE(MISSING_KEY)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #ifdef HAVE_AES_DECRYPT if (wc_AesCcmDecrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE, iv, 13, - tag, sizeof(tag), NULL, 0) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + tag, sizeof(tag), NULL, 0) != WC_NO_ERR_TRACE(MISSING_KEY)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #endif #endif /* HAVE_AESCCM */ #ifdef HAVE_AES_ECB if (wc_AesEcbEncrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + WC_NO_ERR_TRACE(MISSING_KEY)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #ifdef HAVE_AES_DECRYPT if (wc_AesEcbDecrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + WC_NO_ERR_TRACE(MISSING_KEY)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #endif #endif /* HAVE_AES_ECB */ #ifdef WOLFSSL_AES_CFB if (wc_AesCfbEncrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + WC_NO_ERR_TRACE(MISSING_KEY)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #ifdef HAVE_AES_DECRYPT if (wc_AesCfbDecrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + WC_NO_ERR_TRACE(MISSING_KEY)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #endif #if !defined(WOLFSSL_NO_AES_CFB_1_8) if (wc_AesCfb1Encrypt(aes, cipher, plain, 8) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + WC_NO_ERR_TRACE(MISSING_KEY)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); if (wc_AesCfb8Encrypt(aes, cipher, plain, 1) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + WC_NO_ERR_TRACE(MISSING_KEY)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #ifdef HAVE_AES_DECRYPT if (wc_AesCfb1Decrypt(aes, cipher, plain, 8) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + WC_NO_ERR_TRACE(MISSING_KEY)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); if (wc_AesCfb8Decrypt(aes, cipher, plain, 1) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + WC_NO_ERR_TRACE(MISSING_KEY)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #endif #endif /* !WOLFSSL_NO_AES_CFB_1_8 */ @@ -16882,11 +16882,11 @@ static wc_test_ret_t aes_no_key_set_test(void) #ifdef WOLFSSL_AES_OFB if (wc_AesOfbEncrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + WC_NO_ERR_TRACE(MISSING_KEY)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #ifdef HAVE_AES_DECRYPT if (wc_AesOfbDecrypt(aes, cipher, plain, WC_AES_BLOCK_SIZE) != - WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + WC_NO_ERR_TRACE(MISSING_KEY)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #endif #endif /* WOLFSSL_AES_OFB */ diff --git a/wolfssl/wolfcrypt/aes.h b/wolfssl/wolfcrypt/aes.h index 8bfd2b20fe..3410831f11 100644 --- a/wolfssl/wolfcrypt/aes.h +++ b/wolfssl/wolfcrypt/aes.h @@ -316,12 +316,6 @@ struct Aes { #endif int keylen; - /* Set to 1 once a key has been installed (wc_AesSetKey/SetKeyDirect/ - * GcmSetKey). Checked by the mode APIs so they fail instead of running - * with the all-zero key schedule left by wc_AesInit. Distinct from the - * Cavium-only keySet field below. */ - WC_BITFIELD keyInstalled:1; - ALIGN16 word32 reg[WC_AES_BLOCK_SIZE / sizeof(word32)]; /* for CBC mode */ ALIGN16 word32 tmp[WC_AES_BLOCK_SIZE / sizeof(word32)]; /* same */ @@ -488,6 +482,13 @@ struct Aes { cy_stc_crypto_aes_gcm_state_t aes_gcm_state; #endif #endif /* WOLFSSL_PSOC6_CRYPTO */ + + /* Set to 1 once a key has been installed (wc_AesSetKey/SetKeyDirect/ + * GcmSetKey). Checked by the mode APIs so they fail instead of running + * with the all-zero key schedule left by wc_AesInit. Distinct from the + * Cavium-only keySet field. Appended at the end of the struct so existing + * member offsets are unchanged. */ + WC_BITFIELD keyInstalled:1; }; #ifndef WC_AES_TYPE_DEFINED From 3154e3c2570111e09334c5e0c160a88211245f31 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 6 Aug 2026 02:41:11 +0000 Subject: [PATCH 17/22] Fix CI: skip aes_no_key_set_test under WOLF_CRYPTO_CB_FIND The swdev harness (--enable-swdev) defines WOLF_CRYPTO_CB_FIND, which compiles out the 'devId != INVALID_DEVID' guard in the AES mode entry points. Every AES call is then dispatched to the crypto callback regardless of devId, so the software keyInstalled guard is unreachable and the callback rejects a missing key with its own error rather than MISSING_KEY. aes_no_key_set_test asserts MISSING_KEY exactly, so it aborted every swdev/cryptocb-only config. Skip the test when WOLF_CRYPTO_CB_FIND is defined. --- wolfcrypt/test/test.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index cf3fb21969..a2b79eb0e2 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -16760,11 +16760,14 @@ static wc_test_ret_t aes_ecb_direct_test(void) /* The keyInstalled guard is a non-FIPS hardening; under FIPS/selftest the AES * functions come from the validated module and don't reject a missing key, so - * this test does not apply there. */ + * this test does not apply there. WOLF_CRYPTO_CB_FIND routes every call to the + * callback regardless of devId, so the software guard is unreachable and the + * callback rejects with its own error rather than MISSING_KEY. */ #if (defined(HAVE_AES_CBC) || defined(WOLFSSL_AES_COUNTER) || \ defined(HAVE_AESGCM) || defined(HAVE_AESCCM) || defined(HAVE_AES_ECB) || \ defined(WOLFSSL_AES_CFB) || defined(WOLFSSL_AES_OFB)) && \ - !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) + !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) && \ + !defined(WOLF_CRYPTO_CB_FIND) #define WC_TEST_HAVE_AES_NO_KEY_SET /* Ensure AES mode APIs fail when used before wc_AesSetKey installs a key, * instead of running with the all-zero key schedule left by wc_AesInit. */ From 4e7737abde364db69441fb8ff5200b8197d240c5 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 6 Aug 2026 09:08:13 +0000 Subject: [PATCH 18/22] Add opt-out for the AES key-set check and fix three backend gaps The keyInstalled guard is only reachable on the software mode paths. Backends that replace an entry point with a hardware arm in aes.c or a port file never reach it, so wolfcrypt_test's aes_no_key_set_test fails there (reported on STM32). Gate the guard and the test on WOLFSSL_AES_REQUIRE_KEY_SET, on by default and off automatically for those backends. WOLFSSL_NO_AES_KEY_SET_CHECK forces it off; defining WOLFSSL_AES_REQUIRE_KEY_SET forces it on. The struct member is always maintained so the layout does not depend on the option. Also fix three cases where the flag itself was wrong: - Renesas FSPSM CRYPTONLY wc_AesSetKey never set it, so wc_AesCfb1/8, which have no crypto-cb dispatch, rejected a correctly keyed context. - TI wc_AesInit did not zero the struct, leaving the flag indeterminate. - wc_AesFree returned early on the crypto-cb path without reaching the ForceZero, leaving the flag set on a freed context. --- .wolfssl_known_macro_extras | 1 + wolfcrypt/src/aes.c | 54 +++++++++++-------- .../src/port/Renesas/renesas_fspsm_aes.c | 1 + wolfcrypt/src/port/af_alg/afalg_aes.c | 2 +- wolfcrypt/src/port/ti/ti-aes.c | 1 + wolfcrypt/test/test.c | 5 +- wolfssl/wolfcrypt/aes.h | 42 ++++++++++++++- 7 files changed, 81 insertions(+), 25 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 5edebd83ef..7b7cf55e79 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -955,6 +955,7 @@ WOLFSSL_NEW_PRIME_CHECK WOLFSSL_NONBLOCK_OCSP WOLFSSL_NOSHA3_384 WOLFSSL_NOT_WINDOWS_API +WOLFSSL_NO_AES_KEY_SET_CHECK WOLFSSL_NO_BIO_ADDR_IN WOLFSSL_NO_CHACHA20_POLY1305_FUSED WOLFSSL_NO_CHACHA20_POLY1305_FUSED_IFMA diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index c3eda5bed8..9630705763 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -94,6 +94,13 @@ block cipher mechanism that uses n-bit binary string parameter key with 128-bits * WC_AES_BITSLICED: Use bitsliced AES implementation default: off * AES_GCM_GMULT_NCT: GCM GMULT non-constant-time default: off * NO_WOLFSSL_ALLOC_ALIGN: Disable aligned memory allocation default: off + * WOLFSSL_AES_REQUIRE_KEY_SET: + * Reject mode calls made before a key default: on, + * is installed. Off automatically on see aes.h + * backends that replace the mode + * entry points. + * WOLFSSL_NO_AES_KEY_SET_CHECK: + * Force the above check off default: off * * Hardware Acceleration (AES-specific): * WC_ASYNC_ENABLE_AES: Enable async AES operations default: off @@ -7064,7 +7071,7 @@ int wc_AesSetIV(Aes* aes, const byte* iv) if (sz == 0) return 0; - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -7102,7 +7109,7 @@ int wc_AesSetIV(Aes* aes, const byte* iv) if (sz == 0) return 0; - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -7176,7 +7183,7 @@ int wc_AesSetIV(Aes* aes, const byte* iv) { if (aes == NULL) return BAD_FUNC_ARG; - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -7188,7 +7195,7 @@ int wc_AesSetIV(Aes* aes, const byte* iv) { if (aes == NULL) return BAD_FUNC_ARG; - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -7251,7 +7258,7 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) #endif /* Single key guard after all offload dispatches. */ - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -7497,7 +7504,7 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) #endif /* Single key guard after all offload dispatches. */ - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -8016,7 +8023,7 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) #endif /* Software/HW key schedule required from here on. */ - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -11452,7 +11459,7 @@ int wc_AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz, #endif /* Software/HW key schedule (and hash subkey H) required from here on. */ - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -12313,7 +12320,7 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, #endif /* Software/HW key schedule (and hash subkey H) required from here on. */ - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -15617,7 +15624,7 @@ int wc_AesCcmEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz, #endif /* Software/HW key schedule required from here on. */ - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -15787,7 +15794,7 @@ int wc_AesCcmDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz, #endif /* Software/HW key schedule required from here on. */ - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -16196,6 +16203,9 @@ void wc_AesFree(Aes* aes) #ifdef WOLF_CRYPTO_CB_AES_SETKEY aes->devCtx = NULL; /* Clear device context handle */ #endif + /* This path skips the ForceZero below, so clear the flag here or a + * reused context passes the key-set guard with a freed key. */ + aes->keyInstalled = 0; /* If callback wants standard free, it can set devId to INVALID_DEVID. * Otherwise assume the callback handled cleanup. */ if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) @@ -16419,7 +16429,7 @@ int wc_AesEcbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { if ((in == NULL) || (out == NULL) || (aes == NULL)) return BAD_FUNC_ARG; - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -16434,7 +16444,7 @@ int wc_AesEcbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) { if ((in == NULL) || (out == NULL) || (aes == NULL)) return BAD_FUNC_ARG; - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -16476,7 +16486,7 @@ static WARN_UNUSED_RESULT int _AesEcbEncrypt( #endif /* Software key schedule required from here on. */ - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -16592,7 +16602,7 @@ static WARN_UNUSED_RESULT int _AesEcbDecrypt( #endif /* Software key schedule required from here on. */ - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -16716,7 +16726,7 @@ int wc_AesCfbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { if (aes == NULL) return BAD_FUNC_ARG; - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -16728,7 +16738,7 @@ int wc_AesCfbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) { if (aes == NULL) return BAD_FUNC_ARG; - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -16764,7 +16774,7 @@ static WARN_UNUSED_RESULT int AesCfbEncrypt_C(Aes* aes, byte* out, if (sz == 0) { return 0; } - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -16851,7 +16861,7 @@ static WARN_UNUSED_RESULT int AesCfbDecrypt_C(Aes* aes, byte* out, if (sz == 0) { return 0; } - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -17083,7 +17093,7 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB8( if (sz == 0) { return 0; } - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -17147,7 +17157,7 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB1( if (sz == 0) { return 0; } - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } @@ -17309,7 +17319,7 @@ static WARN_UNUSED_RESULT int AesOfbCrypt_C(Aes* aes, byte* out, const byte* in, if (sz == 0) { return 0; } - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } diff --git a/wolfcrypt/src/port/Renesas/renesas_fspsm_aes.c b/wolfcrypt/src/port/Renesas/renesas_fspsm_aes.c index c9d9dae0b9..f702f6fd7e 100644 --- a/wolfcrypt/src/port/Renesas/renesas_fspsm_aes.c +++ b/wolfcrypt/src/port/Renesas/renesas_fspsm_aes.c @@ -958,6 +958,7 @@ int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen, aes->ctx.wrapped_key = (FSPSM_AES_PWKEY)userKey; aes->keylen = (int)keylen; aes->ctx.keySize = keylen; + aes->keyInstalled = 1; return wc_AesSetIV(aes, iv); } diff --git a/wolfcrypt/src/port/af_alg/afalg_aes.c b/wolfcrypt/src/port/af_alg/afalg_aes.c index 764b43e18f..10cffa824f 100644 --- a/wolfcrypt/src/port/af_alg/afalg_aes.c +++ b/wolfcrypt/src/port/af_alg/afalg_aes.c @@ -60,7 +60,7 @@ static int wc_AesSetup(Aes* aes, const char* type, const char* name, int ivSz, i byte* key = (byte*)aes->key; #endif - if (aes->keyInstalled == 0) { + if (!WC_AES_KEY_IS_SET(aes)) { return MISSING_KEY; } diff --git a/wolfcrypt/src/port/ti/ti-aes.c b/wolfcrypt/src/port/ti/ti-aes.c index 16385a1962..1ee5ea7561 100644 --- a/wolfcrypt/src/port/ti/ti-aes.c +++ b/wolfcrypt/src/port/ti/ti-aes.c @@ -1057,6 +1057,7 @@ int wc_AesInit(Aes* aes, void* heap, int devId) if (aes == NULL) return BAD_FUNC_ARG; + XMEMSET(aes, 0, sizeof(Aes)); aes->heap = heap; (void)devId; diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index a2b79eb0e2..60da9f11c2 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -16762,10 +16762,13 @@ static wc_test_ret_t aes_ecb_direct_test(void) * functions come from the validated module and don't reject a missing key, so * this test does not apply there. WOLF_CRYPTO_CB_FIND routes every call to the * callback regardless of devId, so the software guard is unreachable and the - * callback rejects with its own error rather than MISSING_KEY. */ + * callback rejects with its own error rather than MISSING_KEY. + * WOLFSSL_AES_REQUIRE_KEY_SET additionally excludes the backends that replace + * the mode entry points. */ #if (defined(HAVE_AES_CBC) || defined(WOLFSSL_AES_COUNTER) || \ defined(HAVE_AESGCM) || defined(HAVE_AESCCM) || defined(HAVE_AES_ECB) || \ defined(WOLFSSL_AES_CFB) || defined(WOLFSSL_AES_OFB)) && \ + defined(WOLFSSL_AES_REQUIRE_KEY_SET) && \ !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) && \ !defined(WOLF_CRYPTO_CB_FIND) #define WC_TEST_HAVE_AES_NO_KEY_SET diff --git a/wolfssl/wolfcrypt/aes.h b/wolfssl/wolfcrypt/aes.h index 3410831f11..bf183eadf0 100644 --- a/wolfssl/wolfcrypt/aes.h +++ b/wolfssl/wolfcrypt/aes.h @@ -189,6 +189,45 @@ WOLFSSL_LOCAL void WC_ARG_NOT_NULL(1) GHASH(Gcm* gcm, const byte* a, #include "cy_crypto_common.h" #endif /* WOLFSSL_PSOC6_CRYPTO */ +/* Backends that replace one or more AES mode entry points, either with a + * hardware arm in aes.c or with a port file. Those entry points do not carry + * the key-set guard, so the check is not applied on these builds. */ +#if defined(STM32_CRYPTO) || \ + defined(HAVE_COLDFIRE_SEC) || \ + defined(FREESCALE_LTC) || \ + defined(FREESCALE_MMCAU) || \ + defined(MAX3266X_AES) || \ + (defined(WOLFSSL_CRYPTOCELL) && defined(WOLFSSL_CRYPTOCELL_AES)) || \ + (defined(WOLFSSL_SCE) && !defined(WOLFSSL_SCE_NO_AES)) || \ + defined(WOLFSSL_SILABS_SE_ACCEL) || \ + defined(WOLFSSL_TI_CRYPT) || \ + (defined(WOLFSSL_IMX6_CAAM) && !defined(NO_IMX6_CAAM_AES) && \ + !defined(WOLFSSL_QNX_CAAM)) || \ + defined(WOLFSSL_KCAPI_AES) || \ + defined(WOLFSSL_DEVCRYPTO_AES) || defined(WOLFSSL_DEVCRYPTO_CBC) || \ + defined(WOLFSSL_NXP_HASHCRYPT_AES) || \ + (defined(WOLFSSL_HAVE_PSA) && !defined(WOLFSSL_PSA_NO_AES)) || \ + defined(WOLFSSL_XILINX_CRYPT) || \ + defined(WOLF_CRYPTO_CB_ONLY_AES) + #define WC_AES_KEY_SET_CHECK_UNSUPPORTED +#endif + +/* Make the AES mode APIs return MISSING_KEY when called before a key is + * installed, instead of running with the all-zero key schedule left by + * wc_AesInit. Define WOLFSSL_AES_REQUIRE_KEY_SET to force the check on, or + * WOLFSSL_NO_AES_KEY_SET_CHECK to force it off. */ +#if !defined(WOLFSSL_AES_REQUIRE_KEY_SET) && \ + !defined(WOLFSSL_NO_AES_KEY_SET_CHECK) && \ + !defined(WC_AES_KEY_SET_CHECK_UNSUPPORTED) + #define WOLFSSL_AES_REQUIRE_KEY_SET +#endif + +#ifdef WOLFSSL_AES_REQUIRE_KEY_SET + #define WC_AES_KEY_IS_SET(aes) ((aes)->keyInstalled != 0) +#else + #define WC_AES_KEY_IS_SET(aes) (1) +#endif + #ifdef __cplusplus extern "C" { #endif @@ -487,7 +526,8 @@ struct Aes { * GcmSetKey). Checked by the mode APIs so they fail instead of running * with the all-zero key schedule left by wc_AesInit. Distinct from the * Cavium-only keySet field. Appended at the end of the struct so existing - * member offsets are unchanged. */ + * member offsets are unchanged. Always maintained, so the layout does not + * depend on WOLFSSL_AES_REQUIRE_KEY_SET. */ WC_BITFIELD keyInstalled:1; }; From d433852e9ae6e6901220946020dec120af980a36 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 6 Aug 2026 11:24:27 +0000 Subject: [PATCH 19/22] Guard wc_AesEncryptDirect and wc_AesKeyWrap_ex against a missing key wc_Aes{En,De}cryptDirect ran the software schedule with no key check. wc_AesKeyWrap_ex/UnWrap_ex validate only pointers and sizes before looping on the wc_AesEncryptDirect macro, which expands to wc_AesEncrypt and so bypasses a guard on the public function; they need their own. Without a key wc_AesEncrypt returned KEYUSAGE_E from its rounds check, so these now report MISSING_KEY like the mode APIs instead. Add WOLFSSL_AES_DIRECT and HAVE_AES_KEYWRAP to aes_no_key_set_test's outer condition, which also makes the test compile on DIRECT-only or keywrap-only builds, and cover the four APIs there. --- wolfcrypt/src/aes.c | 22 ++++++++++++++++++++++ wolfcrypt/test/test.c | 31 ++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 9630705763..9095325e51 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -6270,6 +6270,10 @@ int wc_AesSetIV(Aes* aes, const byte* iv) if (aes == NULL || out == NULL || in == NULL) return BAD_FUNC_ARG; + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } VECTOR_REGISTERS_PUSH; ret = wc_AesEncrypt(aes, in, out); VECTOR_REGISTERS_POP; @@ -6291,6 +6295,10 @@ int wc_AesSetIV(Aes* aes, const byte* iv) if (aes == NULL) return BAD_FUNC_ARG; + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } VECTOR_REGISTERS_PUSH; ret = wc_AesDecrypt(aes, in, out); VECTOR_REGISTERS_POP; @@ -17485,6 +17493,13 @@ static int AesKeyWrapRaw(Aes* aes, word32 inSz, byte* out, const byte* aiv) return BAD_FUNC_ARG; } + /* The block loop below uses the wc_AesEncryptDirect macro, which bypasses + * the public function's guard. */ + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } + r = out + KEYWRAP_BLOCK_SIZE; XMEMSET(t, 0, sizeof(t)); @@ -17640,6 +17655,13 @@ static int AesKeyUnWrapRaw(Aes* aes, const byte* in, word32 inSz, byte* out, return BAD_FUNC_ARG; } + /* The block loop below uses the wc_AesDecryptDirect macro, which bypasses + * the public function's guard. */ + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } + /* A = C[0], R[i] = C[i]; XMEMMOVE so in-place unwrap (in == out) is safe */ XMEMCPY(tmp, in, KEYWRAP_BLOCK_SIZE); XMEMMOVE(out, in + KEYWRAP_BLOCK_SIZE, inSz - KEYWRAP_BLOCK_SIZE); diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 60da9f11c2..4e00e49613 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -16767,7 +16767,8 @@ static wc_test_ret_t aes_ecb_direct_test(void) * the mode entry points. */ #if (defined(HAVE_AES_CBC) || defined(WOLFSSL_AES_COUNTER) || \ defined(HAVE_AESGCM) || defined(HAVE_AESCCM) || defined(HAVE_AES_ECB) || \ - defined(WOLFSSL_AES_CFB) || defined(WOLFSSL_AES_OFB)) && \ + defined(WOLFSSL_AES_CFB) || defined(WOLFSSL_AES_OFB) || \ + defined(WOLFSSL_AES_DIRECT) || defined(HAVE_AES_KEYWRAP)) && \ defined(WOLFSSL_AES_REQUIRE_KEY_SET) && \ !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) && \ !defined(WOLF_CRYPTO_CB_FIND) @@ -16788,6 +16789,9 @@ static wc_test_ret_t aes_no_key_set_test(void) byte iv[WC_AES_BLOCK_SIZE]; byte tag[WC_AES_BLOCK_SIZE]; #endif +#ifdef HAVE_AES_KEYWRAP + byte wrapped[WC_AES_BLOCK_SIZE + KEYWRAP_BLOCK_SIZE]; +#endif XMEMSET(plain, 0, sizeof(plain)); XMEMSET(cipher, 0, sizeof(cipher)); @@ -16795,6 +16799,9 @@ static wc_test_ret_t aes_no_key_set_test(void) XMEMSET(iv, 0, sizeof(iv)); XMEMSET(tag, 0, sizeof(tag)); #endif +#ifdef HAVE_AES_KEYWRAP + XMEMSET(wrapped, 0, sizeof(wrapped)); +#endif /* The keyInstalled guard is pure-software hardening placed after the * crypto-cb dispatch, so use INVALID_DEVID here: a registered device would @@ -16897,6 +16904,28 @@ static wc_test_ret_t aes_no_key_set_test(void) #endif #endif /* WOLFSSL_AES_OFB */ +#ifdef WOLFSSL_AES_DIRECT + if (wc_AesEncryptDirect(aes, cipher, plain) != + WC_NO_ERR_TRACE(MISSING_KEY)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#ifdef HAVE_AES_DECRYPT + if (wc_AesDecryptDirect(aes, plain, cipher) != + WC_NO_ERR_TRACE(MISSING_KEY)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif +#endif /* WOLFSSL_AES_DIRECT */ + +#ifdef HAVE_AES_KEYWRAP + if (wc_AesKeyWrap_ex(aes, plain, sizeof(plain), wrapped, sizeof(wrapped), + NULL) != WC_NO_ERR_TRACE(MISSING_KEY)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#ifdef HAVE_AES_DECRYPT + if (wc_AesKeyUnWrap_ex(aes, wrapped, sizeof(wrapped), cipher, + sizeof(cipher), NULL) != WC_NO_ERR_TRACE(MISSING_KEY)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif +#endif /* HAVE_AES_KEYWRAP */ + ret = 0; /* success */ out: From 0a02a24e3627f6812f302498235f29aa50234735 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 14 Aug 2026 12:18:48 +0000 Subject: [PATCH 20/22] Address review: mark the key installed on the WOLF_CRYPTO_CB_SETKEY path wc_CryptoCb_SetKey() success returned straight out of wc_AesSetKey without recording keylen or keyInstalled, unlike the WOLF_CRYPTO_CB_AES_SETKEY path just above it, so a device-managed key set that way was then rejected by the new guards. Also document why wc_AesInit_Id()/wc_AesInit_Label() deliberately leave keyInstalled clear: those contexts name a device-held key and have no software schedule, so falling through to a software path is exactly the case the guard exists to catch. --- wolfcrypt/src/aes.c | 21 +++++++++++++++++++-- wolfssl/wolfcrypt/aes.h | 18 +++++++++++++----- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 9095325e51..94d6a6fe81 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -5086,8 +5086,15 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(Aes* aes, const byte* inBlock, WC_SETKEY_AES, aes, (void*)userKey, keylen, (void*)iv, (iv != NULL) ? WC_AES_BLOCK_SIZE : 0, dir); - if (cbRet != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + if (cbRet != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + if (cbRet == 0) { + /* Callback succeeded - the device owns the key, so mark it + * installed like the AES_SETKEY path above. */ + aes->keylen = (int)keylen; + aes->keyInstalled = 1; + } return cbRet; + } /* CRYPTOCB_UNAVAILABLE: fall through to software setup */ #endif /* WOLF_CRYPTO_CB_SETKEY */ /* Standard CryptoCB path - copy key to devKey for encrypt/decrypt offload */ @@ -5660,8 +5667,15 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir) WC_SETKEY_AES, aes, (void*)userKey, keylen, (void*)iv, (iv != NULL) ? WC_AES_BLOCK_SIZE : 0, dir); - if (cbRet != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + if (cbRet != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + if (cbRet == 0) { + /* Callback succeeded - the device owns the key, so mark it + * installed like the AES_SETKEY path above. */ + aes->keylen = (int)keylen; + aes->keyInstalled = 1; + } return cbRet; + } /* CRYPTOCB_UNAVAILABLE: fall through to software setup */ #endif /* WOLF_CRYPTO_CB_SETKEY */ /* Standard CryptoCB path - copy key to devKey */ @@ -16164,6 +16178,8 @@ int wc_AesInit_Id(Aes* aes, unsigned char* id, int len, void* heap, int devId) XMEMCPY(aes->id, id, (size_t)len); aes->idLen = len; aes->labelLen = 0; + /* keyInstalled stays 0: the key lives on the device, not in the + * software schedule. See the field comment in aes.h. */ } return ret; @@ -16188,6 +16204,7 @@ int wc_AesInit_Label(Aes* aes, const char* label, void* heap, int devId) XMEMCPY(aes->label, label, labelLen); aes->labelLen = (int)labelLen; aes->idLen = 0; + /* keyInstalled stays 0: see wc_AesInit_Id() above. */ } return ret; diff --git a/wolfssl/wolfcrypt/aes.h b/wolfssl/wolfcrypt/aes.h index bf183eadf0..0968daa778 100644 --- a/wolfssl/wolfcrypt/aes.h +++ b/wolfssl/wolfcrypt/aes.h @@ -523,11 +523,19 @@ struct Aes { #endif /* WOLFSSL_PSOC6_CRYPTO */ /* Set to 1 once a key has been installed (wc_AesSetKey/SetKeyDirect/ - * GcmSetKey). Checked by the mode APIs so they fail instead of running - * with the all-zero key schedule left by wc_AesInit. Distinct from the - * Cavium-only keySet field. Appended at the end of the struct so existing - * member offsets are unchanged. Always maintained, so the layout does not - * depend on WOLFSSL_AES_REQUIRE_KEY_SET. */ + * GcmSetKey), including when a crypto callback takes ownership of it. + * Checked by the mode APIs so they fail instead of running with the + * all-zero key schedule left by wc_AesInit. Distinct from the Cavium-only + * keySet field. Appended at the end of the struct so existing member + * offsets are unchanged. Always maintained, so the layout does not depend + * on WOLFSSL_AES_REQUIRE_KEY_SET. + * + * Deliberately NOT set by wc_AesInit_Id()/wc_AesInit_Label(): those name a + * key held by the device and leave the software key schedule empty. The + * guard sits after every offload dispatch, so such a context still reaches + * its crypto callback; it only fails if it falls through to a software + * path, which is exactly the case that would otherwise encrypt with an + * all-zero key. */ WC_BITFIELD keyInstalled:1; }; From 311161c995101cf7d62d218e2515cd6597f3d548 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 19 Aug 2026 03:02:29 +0000 Subject: [PATCH 21/22] Address review: only mark the AES key installed once setup succeeded wc_AesSetKeyLocal set keyInstalled before the key schedule was built, so the failure returns after that point (AES-NI SAVE_VECTOR_REGISTERS2/BAD_ALIGN_E, the hardware key installs) left a context that passed WC_AES_KEY_IS_SET with an all-zero schedule. Split the body out and derive keyInstalled from its return value, so only a completed setup marks the context keyed. The WOLF_CRYPTO_CB_SETKEY path sets keylen for a device-held key but has no software schedule, which took AES-XTS past its keylen == 0 guard and into an encrypt with rounds == 0. Reject a zero rounds in the XTS entry points too; XTS has no crypto callback dispatch, so a device-held key is unusable there. AES-KW/KWP guarded ahead of its per-block loop, but with HAVE_AES_ECB that loop calls wc_AesEcbEncrypt, whose own guard sits after the crypto callback dispatch. Device-held keys were rejected where they previously ran on the device; keep the guard only for the direct-block path. The CFB/CFB1/CFB8/OFB helpers and the PIC32MZ CBC entry points returned 0 for a zero-length call on an unkeyed context while any other length returned MISSING_KEY. Check the key first. AES-CBC keeps its zero-length return above the offload dispatches, which must not see sz == 0. The PSoC6 wc_AesSetKey tested aes != NULL after already passing it to wc_Psoc6_Aes_SetKey; validate it up front instead. --- wolfcrypt/src/aes.c | 117 ++++++++++++++++++++++++++++++-------------- 1 file changed, 80 insertions(+), 37 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 94d6a6fe81..cb01f2e5f3 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -5323,8 +5323,13 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(Aes* aes, const byte* inBlock, int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen, const byte* iv, int dir) { - int ret = wc_Psoc6_Aes_SetKey(aes, userKey, keylen, iv, dir); - if (ret == 0 && aes != NULL) + int ret; + + if (aes == NULL) + return BAD_FUNC_ARG; + + ret = wc_Psoc6_Aes_SetKey(aes, userKey, keylen, iv, dir); + if (ret == 0) aes->keyInstalled = 1; return ret; } @@ -5599,12 +5604,38 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir) #endif /* NEED_AES_TABLES */ - /* AES - SetKey (block schedule via generated asm on RISC-V) */ + static WARN_UNUSED_RESULT int AesSetKeyLocal_body( + Aes* aes, const byte* userKey, word32 keylen, const byte* iv, int dir, + int checkKeyLen); + + /* AES - SetKey (block schedule via generated asm on RISC-V) + * + * keyInstalled is derived from the return value here rather than set + * inside the body. The body has failure returns after the point where the + * key material is accepted (AES-NI SAVE_VECTOR_REGISTERS2/BAD_ALIGN_E, the + * hardware key installs), and marking the context keyed on those paths + * would let it pass WC_AES_KEY_IS_SET with an all-zero key schedule. */ static WARN_UNUSED_RESULT int wc_AesSetKeyLocal( Aes* aes, const byte* userKey, word32 keylen, const byte* iv, int dir, int checkKeyLen) { int ret; + + if (aes == NULL) + return BAD_FUNC_ARG; + + aes->keyInstalled = 0; + ret = AesSetKeyLocal_body(aes, userKey, keylen, iv, dir, checkKeyLen); + aes->keyInstalled = (ret == 0) ? 1 : 0; + + return ret; + } + + static WARN_UNUSED_RESULT int AesSetKeyLocal_body( + Aes* aes, const byte* userKey, word32 keylen, const byte* iv, int dir, + int checkKeyLen) + { + int ret; #if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_SETKEY) int cbRet; #endif @@ -5649,7 +5680,6 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir) if (ret == 0) { /* Callback succeeded - SE owns the key */ aes->keylen = (int)keylen; - aes->keyInstalled = 1; if (iv != NULL) XMEMCPY(aes->reg, iv, WC_AES_BLOCK_SIZE); else @@ -5669,10 +5699,10 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir) (iv != NULL) ? WC_AES_BLOCK_SIZE : 0, dir); if (cbRet != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { if (cbRet == 0) { - /* Callback succeeded - the device owns the key, so mark it - * installed like the AES_SETKEY path above. */ + /* Callback succeeded - the device owns the key. rounds is + * left at 0: there is no software key schedule, and the + * XTS entry points use that to reject the context. */ aes->keylen = (int)keylen; - aes->keyInstalled = 1; } return cbRet; } @@ -5745,7 +5775,6 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir) return WC_HW_E; } aes->blackKey = keyIdOut; - aes->keyInstalled = 1; return 0; } #endif @@ -5774,7 +5803,6 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir) * reads it as the source of truth for the configured key size. */ aes->keylen = (int)keylen; aes->rounds = (keylen / 4) + 6; - aes->keyInstalled = 1; #if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \ defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) || \ defined(WOLFSSL_AES_CTS) @@ -5804,7 +5832,6 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir) aes->keylen = (int)keylen; aes->rounds = (keylen/4) + 6; - aes->keyInstalled = 1; ret = wc_AesSetIV(aes, iv); if (ret != 0) return ret; @@ -7090,14 +7117,14 @@ int wc_AesSetIV(Aes* aes, const byte* iv) if (aes == NULL) return BAD_FUNC_ARG; - if (sz == 0) - return 0; - if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } + if (sz == 0) + return 0; + /* hardware fails on input that is not a multiple of AES block size */ if (sz % WC_AES_BLOCK_SIZE != 0) { #ifdef WOLFSSL_AES_CBC_LENGTH_CHECKS @@ -7128,14 +7155,14 @@ int wc_AesSetIV(Aes* aes, const byte* iv) if (aes == NULL) return BAD_FUNC_ARG; - if (sz == 0) - return 0; - if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } + if (sz == 0) + return 0; + /* hardware fails on input that is not a multiple of AES block size */ if (sz % WC_AES_BLOCK_SIZE != 0) { #ifdef WOLFSSL_AES_CBC_LENGTH_CHECKS @@ -16796,13 +16823,13 @@ static WARN_UNUSED_RESULT int AesCfbEncrypt_C(Aes* aes, byte* out, if ((aes == NULL) || (out == NULL) || (in == NULL)) { return BAD_FUNC_ARG; } - if (sz == 0) { - return 0; - } if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } + if (sz == 0) { + return 0; + } if (aes->left > 0) { /* consume any unused bytes left in aes->tmp */ @@ -16883,13 +16910,13 @@ static WARN_UNUSED_RESULT int AesCfbDecrypt_C(Aes* aes, byte* out, if ((aes == NULL) || (out == NULL) || (in == NULL)) { return BAD_FUNC_ARG; } - if (sz == 0) { - return 0; - } if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } + if (sz == 0) { + return 0; + } if (aes->left > 0) { /* consume any unused bytes left in aes->tmp */ @@ -17115,13 +17142,13 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB8( return BAD_FUNC_ARG; } - if (sz == 0) { - return 0; - } if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } + if (sz == 0) { + return 0; + } VECTOR_REGISTERS_PUSH; @@ -17179,13 +17206,13 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB1( return BAD_FUNC_ARG; } - if (sz == 0) { - return 0; - } if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } + if (sz == 0) { + return 0; + } VECTOR_REGISTERS_PUSH; @@ -17341,13 +17368,13 @@ static WARN_UNUSED_RESULT int AesOfbCrypt_C(Aes* aes, byte* out, const byte* in, if ((aes == NULL) || (out == NULL) || (in == NULL)) { return BAD_FUNC_ARG; } - if (sz == 0) { - return 0; - } if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } + if (sz == 0) { + return 0; + } if (aes->left > 0) { /* consume any unused bytes left in aes->tmp */ @@ -17510,12 +17537,16 @@ static int AesKeyWrapRaw(Aes* aes, word32 inSz, byte* out, const byte* aiv) return BAD_FUNC_ARG; } +#ifndef HAVE_AES_ECB /* The block loop below uses the wc_AesEncryptDirect macro, which bypasses - * the public function's guard. */ + * the public function's guard. With HAVE_AES_ECB the loop calls + * wc_AesEcbEncrypt instead, whose own guard sits after the crypto + * callback dispatch, so a device-held key still reaches the device. */ if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } +#endif r = out + KEYWRAP_BLOCK_SIZE; XMEMSET(t, 0, sizeof(t)); @@ -17672,12 +17703,16 @@ static int AesKeyUnWrapRaw(Aes* aes, const byte* in, word32 inSz, byte* out, return BAD_FUNC_ARG; } +#ifndef HAVE_AES_ECB /* The block loop below uses the wc_AesDecryptDirect macro, which bypasses - * the public function's guard. */ + * the public function's guard. With HAVE_AES_ECB the loop calls + * wc_AesEcbDecrypt instead, whose own guard sits after the crypto + * callback dispatch, so a device-held key still reaches the device. */ if (!WC_AES_KEY_IS_SET(aes)) { WOLFSSL_MSG("AES key not set"); return MISSING_KEY; } +#endif /* A = C[0], R[i] = C[i]; XMEMMOVE so in-place unwrap (in == out) is safe */ XMEMCPY(tmp, in, KEYWRAP_BLOCK_SIZE); @@ -18735,7 +18770,9 @@ int wc_AesXtsEncrypt(XtsAes* xaes, byte* out, const byte* in, word32 sz, aes = &xaes->aes; - if (aes->keylen == 0) { + /* rounds == 0 means no software key schedule: XTS has no crypto + * callback dispatch, so a device-owned key is unusable here. */ + if ((aes->keylen == 0) || (aes->rounds == 0)) { WOLFSSL_MSG("wc_AesXtsEncrypt called with unset encryption key."); return BAD_FUNC_ARG; } @@ -18885,7 +18922,9 @@ int wc_AesXtsEncryptInit(XtsAes* xaes, const byte* i, word32 iSz, aes = &xaes->aes; - if (aes->keylen == 0) { + /* rounds == 0 means no software key schedule: XTS has no crypto + * callback dispatch, so a device-owned key is unusable here. */ + if ((aes->keylen == 0) || (aes->rounds == 0)) { WOLFSSL_MSG("wc_AesXtsEncrypt called with unset encryption key."); return BAD_FUNC_ARG; } @@ -19296,7 +19335,9 @@ int wc_AesXtsDecrypt(XtsAes* xaes, byte* out, const byte* in, word32 sz, * not a sequence of complete blocks. */ - if (aes->keylen == 0) { + /* rounds == 0 means no software key schedule: XTS has no crypto + * callback dispatch, so a device-owned key is unusable here. */ + if ((aes->keylen == 0) || (aes->rounds == 0)) { WOLFSSL_MSG("wc_AesXtsDecrypt called with unset decryption key."); return BAD_FUNC_ARG; } @@ -19448,7 +19489,9 @@ int wc_AesXtsDecryptInit(XtsAes* xaes, const byte* i, word32 iSz, aes = &xaes->aes; #endif - if (aes->keylen == 0) { + /* rounds == 0 means no software key schedule: XTS has no crypto + * callback dispatch, so a device-owned key is unusable here. */ + if ((aes->keylen == 0) || (aes->rounds == 0)) { WOLFSSL_MSG("wc_AesXtsDecrypt called with unset decryption key."); return BAD_FUNC_ARG; } From 47efe878af58a8b39050920fddfd17fea39101b4 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 19 Aug 2026 03:02:29 +0000 Subject: [PATCH 22/22] Address review: fix AES key-set guard gaps on AF_ALG and MAX3266X_CB AF_ALG's decrypt entry points return KEYUSAGE_E from their aes->dir check, and wc_AesGcmEncrypt returns BAD_FUNC_ARG on the unset socket, both before wc_AesSetup's key check. aes_no_key_set_test expects MISSING_KEY and failed on --enable-afalg. Guard each mode entry point ahead of those checks. AF_ALG also set keyInstalled before copying the key, so a key setup that failed on the socket left a keyless context marked as keyed. Move both assignments after the copy. aes.c undefines MAX3266X_AES for MAX3266X_CB builds, after aes.h has been processed, so those builds took the software and callback paths with the check compiled out. Keep the check enabled for them. --- wolfcrypt/src/port/af_alg/afalg_aes.c | 49 ++++++++++++++++++++++++--- wolfssl/wolfcrypt/aes.h | 2 +- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/wolfcrypt/src/port/af_alg/afalg_aes.c b/wolfcrypt/src/port/af_alg/afalg_aes.c index 10cffa824f..50b64f80a0 100644 --- a/wolfcrypt/src/port/af_alg/afalg_aes.c +++ b/wolfcrypt/src/port/af_alg/afalg_aes.c @@ -150,8 +150,6 @@ int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen, #endif aes->keylen = keylen; aes->rounds = keylen/4 + 6; - /* Mark key installed so the shared aes.c mode guards accept this context. */ - aes->keyInstalled = 1; #ifdef WOLFSSL_AES_COUNTER aes->left = 0; @@ -173,6 +171,10 @@ int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen, /* save key until type is known i.e. CBC, ECB, ... */ XMEMCPY((byte*)(aes->key), userKey, keylen); aes->dir = dir; + /* Mark key installed so the shared aes.c mode guards accept this + * context. Only after the key is copied, so a failed setup above does + * not leave a keyless context marked as keyed. */ + aes->keyInstalled = 1; return wc_AesSetIV(aes, iv); } @@ -198,6 +200,10 @@ int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen, } #endif + if (!WC_AES_KEY_IS_SET(aes)) { + return MISSING_KEY; + } + if (aes->dir != AES_ENCRYPTION) { return KEYUSAGE_E; } @@ -267,6 +273,10 @@ int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen, #endif } + if (!WC_AES_KEY_IS_SET(aes)) { + return MISSING_KEY; + } + if (aes->dir != AES_DECRYPTION) { return KEYUSAGE_E; } @@ -368,6 +378,10 @@ static int wc_Afalg_AesDirect(Aes* aes, byte* out, const byte* in, word32 sz) #if defined(WOLFSSL_AES_DIRECT) && defined(WOLFSSL_AFALG) int wc_AesEncryptDirect(Aes* aes, byte* out, const byte* in) { + if ((aes != NULL) && !WC_AES_KEY_IS_SET(aes)) { + return MISSING_KEY; + } + if (aes && (aes->dir != AES_ENCRYPTION)) { return KEYUSAGE_E; } @@ -378,6 +392,10 @@ int wc_AesEncryptDirect(Aes* aes, byte* out, const byte* in) int wc_AesDecryptDirect(Aes* aes, byte* out, const byte* in) { + if ((aes != NULL) && !WC_AES_KEY_IS_SET(aes)) { + return MISSING_KEY; + } + if (aes && (aes->dir != AES_DECRYPTION)) { return KEYUSAGE_E; } @@ -420,6 +438,10 @@ int wc_AesSetKeyDirect(Aes* aes, const byte* userKey, word32 keylen, return BAD_FUNC_ARG; } + if (!WC_AES_KEY_IS_SET(aes)) { + return MISSING_KEY; + } + if (aes->dir != AES_ENCRYPTION) { return KEYUSAGE_E; } @@ -562,8 +584,6 @@ int wc_AesGcmSetKey(Aes* aes, const byte* key, word32 len) aes->keylen = len; aes->rounds = len/4 + 6; aes->dir = AES_ENCRYPTION; - /* Mark key installed so the shared aes.c mode guards accept this context. */ - aes->keyInstalled = 1; if (aes->rdFd > WC_SOCK_NOTSET) { (void)close(aes->rdFd); @@ -586,6 +606,11 @@ int wc_AesGcmSetKey(Aes* aes, const byte* key, word32 len) XMEMCPY((byte*)(aes->key), key, len); #endif + /* Mark key installed so the shared aes.c mode guards accept this + * context. Only after the key is copied, so a failed setup above does + * not leave a keyless context marked as keyed. */ + aes->keyInstalled = 1; + return 0; } @@ -630,6 +655,10 @@ int wc_AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz, if (ret != 0) return ret; + if (!WC_AES_KEY_IS_SET(aes)) { + return MISSING_KEY; + } + if (aes->alFd == WC_SOCK_NOTSET) { WOLFSSL_MSG("AF_ALG GcmEncrypt called with alFd unset"); return BAD_FUNC_ARG; @@ -826,6 +855,10 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, return BAD_FUNC_ARG; } + if (!WC_AES_KEY_IS_SET(aes)) { + return MISSING_KEY; + } + if (ivSz > WC_SYSTEM_AESGCM_IV) ivSz = WC_SYSTEM_AESGCM_IV; @@ -999,6 +1032,10 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, #ifdef HAVE_AES_ECB int wc_AesEcbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { + if ((aes != NULL) && !WC_AES_KEY_IS_SET(aes)) { + return MISSING_KEY; + } + if (aes && (aes->dir != AES_ENCRYPTION)) { return KEYUSAGE_E; } @@ -1009,6 +1046,10 @@ int wc_AesEcbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) int wc_AesEcbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) { + if ((aes != NULL) && !WC_AES_KEY_IS_SET(aes)) { + return MISSING_KEY; + } + if (aes && (aes->dir != AES_DECRYPTION)) { return KEYUSAGE_E; } diff --git a/wolfssl/wolfcrypt/aes.h b/wolfssl/wolfcrypt/aes.h index 0968daa778..455f30088b 100644 --- a/wolfssl/wolfcrypt/aes.h +++ b/wolfssl/wolfcrypt/aes.h @@ -196,7 +196,7 @@ WOLFSSL_LOCAL void WC_ARG_NOT_NULL(1) GHASH(Gcm* gcm, const byte* a, defined(HAVE_COLDFIRE_SEC) || \ defined(FREESCALE_LTC) || \ defined(FREESCALE_MMCAU) || \ - defined(MAX3266X_AES) || \ + (defined(MAX3266X_AES) && !defined(MAX3266X_CB)) || \ (defined(WOLFSSL_CRYPTOCELL) && defined(WOLFSSL_CRYPTOCELL_AES)) || \ (defined(WOLFSSL_SCE) && !defined(WOLFSSL_SCE_NO_AES)) || \ defined(WOLFSSL_SILABS_SE_ACCEL) || \