From 8c60e8bbe740f35ad5398dc2a54aa8ca1155e8c4 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 23 Jun 2026 11:22:55 +0000 Subject: [PATCH 1/7] 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 6806acbc965..7fa255065c8 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -6760,6 +6760,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 && @@ -6970,6 +6977,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 && @@ -7425,6 +7439,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, @@ -10423,6 +10443,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 */ @@ -11173,6 +11199,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 */ @@ -14497,6 +14529,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(__aarch64__) && defined(WOLFSSL_ARMASM) @@ -14592,6 +14630,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(__aarch64__) && defined(WOLFSSL_ARMASM) @@ -14732,6 +14776,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 */ @@ -14807,6 +14855,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 */ @@ -14951,6 +15003,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; @@ -15011,6 +15067,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; @@ -15169,6 +15229,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 e95bba5ae5e..3333a1a3f35 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -16122,12 +16122,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 7f3fb69972093b8a8615a6325b41f0fee77c91fc Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 23 Jun 2026 11:26:39 +0000 Subject: [PATCH 2/7] 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 3333a1a3f35..6abf9276536 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -27375,6 +27375,9 @@ static wc_test_ret_t rsa_decode_test(RsaKey* keyPub) 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; @@ -27502,6 +27505,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 8f6f7d47af7be2be22af1b31df4c3a74b6fe01e6 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 23 Jun 2026 13:44:39 +0000 Subject: [PATCH 3/7] 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 7fa255065c8..728f52e07a1 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -4871,7 +4871,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) @@ -5188,6 +5191,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 @@ -5304,6 +5308,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) @@ -5333,6 +5338,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; @@ -6699,12 +6705,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 */ @@ -6762,7 +6776,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; } @@ -6979,7 +6993,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; } @@ -7440,7 +7454,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; } @@ -10444,7 +10458,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; } @@ -11200,7 +11214,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; } @@ -14481,6 +14495,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); } @@ -14492,6 +14510,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); } @@ -14530,7 +14552,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; } @@ -14631,7 +14653,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; } @@ -14738,12 +14760,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 */ @@ -14776,7 +14806,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; } @@ -14855,7 +14885,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; } @@ -15003,7 +15033,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; } @@ -15067,7 +15097,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; } @@ -15229,7 +15259,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 e3d7637470d..1ed4c73ef4e 100644 --- a/wolfssl/wolfcrypt/aes.h +++ b/wolfssl/wolfcrypt/aes.h @@ -292,6 +292,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 a45766c4b7ef1da78e098c8b553df37cd72f45f2 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 23 Jun 2026 17:00:57 +0000 Subject: [PATCH 4/7] 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 728f52e07a1..1166af3cf36 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -4228,6 +4228,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); #if !defined(WOLFSSL_STM32_CUBEMX) || defined(STM32_HAL_V2) @@ -4309,6 +4310,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); @@ -4393,6 +4395,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); @@ -4452,6 +4455,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); @@ -4514,6 +4518,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); @@ -4579,6 +4584,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 AES_set_key_AARCH32(userKey, keylen, (byte*)aes->key, dir); @@ -4631,6 +4637,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 @@ -4691,6 +4698,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; AES_set_encrypt_key(userKey, keylen * 8, (byte*)aes->key); @@ -4736,6 +4744,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 @@ -4873,7 +4882,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; } @@ -5191,7 +5200,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 @@ -5308,7 +5317,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) @@ -5338,7 +5347,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; @@ -6705,7 +6714,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; } @@ -6715,7 +6724,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; } @@ -6776,7 +6785,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; } @@ -6993,7 +7002,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; } @@ -7454,7 +7463,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; } @@ -10458,7 +10467,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; } @@ -11214,7 +11223,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; } @@ -14495,7 +14504,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; } @@ -14510,7 +14519,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; } @@ -14552,7 +14561,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; } @@ -14653,7 +14662,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; } @@ -14760,7 +14769,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; } @@ -14770,7 +14779,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; } @@ -14806,7 +14815,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; } @@ -14885,7 +14894,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; } @@ -15033,7 +15042,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; } @@ -15097,7 +15106,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; } @@ -15259,7 +15268,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 1ed4c73ef4e..108c121054b 100644 --- a/wolfssl/wolfcrypt/aes.h +++ b/wolfssl/wolfcrypt/aes.h @@ -294,8 +294,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 0dd8d77264fb3d85c008531d35c8eba21703ccdb Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 24 Jun 2026 11:43:42 +0000 Subject: [PATCH 5/7] 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 +++++++ wolfcrypt/src/port/riscv/riscv-64-aes.c | 39 +++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 1166af3cf36..fcb5529ad0f 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -6614,6 +6614,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; @@ -6644,6 +6649,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; diff --git a/wolfcrypt/src/port/riscv/riscv-64-aes.c b/wolfcrypt/src/port/riscv/riscv-64-aes.c index 8dee9a87d51..4ccdabbcb12 100644 --- a/wolfcrypt/src/port/riscv/riscv-64-aes.c +++ b/wolfcrypt/src/port/riscv/riscv-64-aes.c @@ -506,6 +506,7 @@ int wc_AesSetKey(Aes* aes, const byte* key, word32 keyLen, const byte* iv, if (ret == 0) { /* Finish setting the AES object. */ aes->keylen = keyLen; + aes->keyInstalled = 1; #if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \ defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) aes->left = 0; @@ -698,6 +699,10 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) if ((aes == NULL) || (out == NULL) || (in == NULL)) { ret = BAD_FUNC_ARG; } + /* A key must have been installed. */ + if ((ret == 0) && (aes->keyInstalled == 0)) { + ret = BAD_FUNC_ARG; + } #ifdef WOLFSSL_AES_CBC_LENGTH_CHECKS /* Ensure a multiple of blocks is to be encrypted. */ if ((ret == 0) && (sz % WC_AES_BLOCK_SIZE)) { @@ -910,6 +915,10 @@ int wc_AesCbcDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) if ((aes == NULL) || (out == NULL) || (in == NULL)) { ret = BAD_FUNC_ARG; } + /* A key must have been installed. */ + if ((ret == 0) && (aes->keyInstalled == 0)) { + ret = BAD_FUNC_ARG; + } /* Ensure a multiple of blocks is being decrypted. */ if ((ret == 0) && (sz % WC_AES_BLOCK_SIZE)) { #ifdef WOLFSSL_AES_CBC_LENGTH_CHECKS @@ -1778,6 +1787,7 @@ int wc_AesSetKey(Aes* aes, const byte* key, word32 keyLen, const byte* iv, if (ret == 0) { /* Finish setting the AES object. */ aes->keylen = keyLen; + aes->keyInstalled = 1; #if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \ defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) aes->left = 0; @@ -2988,6 +2998,7 @@ int wc_AesSetKey(Aes* aes, const byte* key, word32 keyLen, const byte* iv, #endif aes->keylen = (int)keyLen; aes->rounds = (keyLen / 4) + 6; + aes->keyInstalled = 1; /* Compute the key schedule. */ AesSetKey_C(aes, key, keyLen, dir); @@ -3870,6 +3881,10 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) if ((aes == NULL) || (out == NULL) || (in == NULL)) { ret = BAD_FUNC_ARG; } + /* A key must have been installed. */ + if ((ret == 0) && (aes->keyInstalled == 0)) { + ret = BAD_FUNC_ARG; + } #ifdef WOLFSSL_AES_CBC_LENGTH_CHECKS /* Ensure a multiple of blocks is to be encrypted. */ if ((ret == 0) && (sz % WC_AES_BLOCK_SIZE)) { @@ -3938,6 +3953,10 @@ int wc_AesCbcDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) if ((aes == NULL) || (out == NULL) || (in == NULL)) { ret = BAD_FUNC_ARG; } + /* A key must have been installed. */ + if ((ret == 0) && (aes->keyInstalled == 0)) { + ret = BAD_FUNC_ARG; + } /* Ensure a multiple of blocks is being decrypted. */ if ((ret == 0) && (sz % WC_AES_BLOCK_SIZE)) { #ifdef WOLFSSL_AES_CBC_LENGTH_CHECKS @@ -4011,6 +4030,10 @@ int wc_AesEcbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) if ((aes == NULL) || (out == NULL) || (in == NULL)) { ret = BAD_FUNC_ARG; } + /* A key must have been installed. */ + if ((ret == 0) && (aes->keyInstalled == 0)) { + ret = BAD_FUNC_ARG; + } /* Ensure a multiple of blocks is to be encrypted. */ if ((ret == 0) && ((sz % WC_AES_BLOCK_SIZE) != 0)) { ret = BAD_LENGTH_E; @@ -4048,6 +4071,10 @@ int wc_AesEcbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) if ((aes == NULL) || (out == NULL) || (in == NULL)) { ret = BAD_FUNC_ARG; } + /* A key must have been installed. */ + if ((ret == 0) && (aes->keyInstalled == 0)) { + ret = BAD_FUNC_ARG; + } /* Ensure a multiple of blocks is to be decrypted. */ if ((ret == 0) && ((sz % WC_AES_BLOCK_SIZE) != 0)) { ret = BAD_LENGTH_E; @@ -6937,6 +6964,10 @@ int wc_AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz, WOLFSSL_MSG("GcmEncrypt tagSz error"); ret = BAD_FUNC_ARG; } + /* A key must have been installed. */ + if ((ret == 0) && (aes->keyInstalled == 0)) { + ret = BAD_FUNC_ARG; + } if (ret == 0) { switch (aes->rounds) { @@ -8840,6 +8871,10 @@ int wc_AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz, WOLFSSL_MSG("GcmEncrypt tagSz error"); ret = BAD_FUNC_ARG; } + /* A key must have been installed. */ + if ((ret == 0) && (aes->keyInstalled == 0)) { + ret = BAD_FUNC_ARG; + } if (ret == 0) { @@ -8937,6 +8972,10 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, WOLFSSL_MSG("a NULL parameter passed in when size is larger than 0"); ret = BAD_FUNC_ARG; } + /* A key must have been installed. */ + if ((ret == 0) && (aes->keyInstalled == 0)) { + ret = BAD_FUNC_ARG; + } if (ret == 0) { if (nonceSz == GCM_NONCE_MID_SZ) { From ecd3a61a62eba7003ff5df80beb8735664513a2d Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 24 Jun 2026 12:46:55 +0000 Subject: [PATCH 6/7] 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 6abf9276536..79d37af3b11 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -16122,9 +16122,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 8460187df515758976a0e9920a2451047d9a7a49 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 24 Jun 2026 18:17:03 +0000 Subject: [PATCH 7/7] 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 79d37af3b11..69c287fc10e 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -27527,8 +27527,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 */