diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 523eada81b..4f88a806c0 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -959,6 +959,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 e96e4b1842..cb01f2e5f3 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 @@ -4632,6 +4639,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 +4729,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); @@ -4758,6 +4767,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) || \ @@ -4805,6 +4815,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 +4875,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 +4938,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 +5004,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 +5068,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 @@ -5071,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 */ @@ -5114,6 +5136,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 +5185,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 @@ -5270,6 +5294,8 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt(Aes* aes, const byte* inBlock, #endif wolfSSL_CryptHwMutexUnLock(); + aes->keyInstalled = 1; + ret = wc_AesSetIV(aes, iv); } @@ -5297,7 +5323,15 @@ 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; + + 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; } #if defined(WOLFSSL_AES_DIRECT) @@ -5570,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 @@ -5637,8 +5697,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. 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; + } return cbRet; + } /* CRYPTOCB_UNAVAILABLE: fall through to software setup */ #endif /* WOLF_CRYPTO_CB_SETKEY */ /* Standard CryptoCB path - copy key to devKey */ @@ -6244,6 +6311,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; @@ -6265,6 +6336,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; @@ -7039,6 +7114,14 @@ int wc_AesSetIV(Aes* aes, const byte* iv) { int ret; + if (aes == NULL) + return BAD_FUNC_ARG; + + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } + if (sz == 0) return 0; @@ -7069,6 +7152,14 @@ int wc_AesSetIV(Aes* aes, const byte* iv) int ret; byte scratch[WC_AES_BLOCK_SIZE]; + if (aes == NULL) + return BAD_FUNC_ARG; + + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } + if (sz == 0) return 0; @@ -7139,12 +7230,24 @@ int wc_AesSetIV(Aes* aes, const byte* iv) int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { + if (aes == NULL) + return BAD_FUNC_ARG; + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } 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) + return BAD_FUNC_ARG; + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } return wc_Psoc6_Aes_CbcDecrypt(aes, out, in, sz); } #endif /* HAVE_AES_DECRYPT */ @@ -7170,6 +7273,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; } @@ -7183,14 +7288,6 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) } #endif -#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 - #ifdef WOLFSSL_IMXRT_DCP /* Implemented in wolfcrypt/src/port/nxp/dcp_port.c */ if (aes->keylen == 16) @@ -7208,6 +7305,21 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) /* fall-through when unavailable */ } #endif + + /* Single key guard after all offload dispatches. */ + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + 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 && @@ -7391,6 +7503,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; } @@ -7420,14 +7534,6 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) #endif } -#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 - #ifdef WOLFSSL_IMXRT_DCP /* Implemented in wolfcrypt/src/port/nxp/dcp_port.c */ if (aes->keylen == 16) @@ -7445,6 +7551,21 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) /* fall-through when unavailable */ } #endif + + /* Single key guard after all offload dispatches. */ + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + 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 && @@ -7932,6 +8053,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) @@ -7944,6 +8071,12 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) } #endif + /* Software/HW key schedule required from here on. */ + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } + /* 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 +11507,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 (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } + #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 +12368,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 (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } + #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 */ @@ -15527,6 +15672,12 @@ int wc_AesCcmEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz, } #endif + /* Software/HW key schedule required from here on. */ + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } + XMEMSET(A, 0, sizeof(A)); XMEMCPY(B+1, nonce, nonceSz); @@ -15691,6 +15842,12 @@ int wc_AesCcmDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz, } #endif + /* Software/HW key schedule required from here on. */ + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } + o = out; oSz = inSz; XMEMSET(A, 0, sizeof A); @@ -16048,6 +16205,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; @@ -16072,6 +16231,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; @@ -16095,6 +16255,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)) @@ -16318,6 +16481,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 (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } return wc_Psoc6_Aes_EcbEncrypt(aes, out, in, sz); } @@ -16329,6 +16496,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 (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } return wc_Psoc6_Aes_EcbDecrypt(aes, out, in, sz); } @@ -16366,6 +16537,12 @@ static WARN_UNUSED_RESULT int _AesEcbEncrypt( return DCPAesEcbEncrypt(aes, out, in, sz); #endif + /* Software key schedule required from here on. */ + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } + VECTOR_REGISTERS_PUSH; #if defined(WOLFSSL_RISCV_ASM) @@ -16476,6 +16653,12 @@ static WARN_UNUSED_RESULT int _AesEcbDecrypt( return DCPAesEcbDecrypt(aes, out, in, sz); #endif + /* Software key schedule required from here on. */ + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } + VECTOR_REGISTERS_PUSH; #if defined(WOLFSSL_RISCV_ASM) @@ -16593,12 +16776,24 @@ 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) + return BAD_FUNC_ARG; + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } 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) + return BAD_FUNC_ARG; + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } return wc_Psoc6_Aes_CfbDecrypt(aes, out, in, sz); } #endif /* HAVE_AES_DECRYPT */ @@ -16628,6 +16823,10 @@ static WARN_UNUSED_RESULT int AesCfbEncrypt_C(Aes* aes, byte* out, 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; + } if (sz == 0) { return 0; } @@ -16711,6 +16910,10 @@ static WARN_UNUSED_RESULT int AesCfbDecrypt_C(Aes* aes, byte* out, 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; + } if (sz == 0) { return 0; } @@ -16939,6 +17142,10 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB8( return BAD_FUNC_ARG; } + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } if (sz == 0) { return 0; } @@ -16999,6 +17206,10 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB1( return BAD_FUNC_ARG; } + if (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } if (sz == 0) { return 0; } @@ -17157,6 +17368,10 @@ 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 (!WC_AES_KEY_IS_SET(aes)) { + WOLFSSL_MSG("AES key not set"); + return MISSING_KEY; + } if (sz == 0) { return 0; } @@ -17322,6 +17537,17 @@ 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. 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)); @@ -17477,6 +17703,17 @@ 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. 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); XMEMMOVE(out, in + KEYWRAP_BLOCK_SIZE, inSz - KEYWRAP_BLOCK_SIZE); @@ -18533,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; } @@ -18683,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; } @@ -19094,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; } @@ -19246,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; } 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 efbf635859..50b64f80a0 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 (!WC_AES_KEY_IS_SET(aes)) { + return MISSING_KEY; + } + if (aes->alFd == WC_SOCK_NOTSET) { aes->alFd = wc_Afalg_Socket(); if (aes->alFd < 0) { @@ -167,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); } @@ -192,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; } @@ -261,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; } @@ -362,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; } @@ -372,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; } @@ -414,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; } @@ -578,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; } @@ -622,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; @@ -818,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; @@ -991,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; } @@ -1001,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/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..1ee5ea7561 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) || \ @@ -1055,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 0556fd5351..4e00e49613 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -16758,12 +16758,199 @@ static wc_test_ret_t aes_ecb_direct_test(void) } #endif /* HAVE_AES_ECB || WOLFSSL_AES_DIRECT */ +/* 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. 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. + * 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_DIRECT) || defined(HAVE_AES_KEYWRAP)) && \ + 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 +/* 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]; +#if defined(HAVE_AESGCM) || defined(HAVE_AESCCM) + 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)); +#if defined(HAVE_AESGCM) || defined(HAVE_AESCCM) + 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 + * 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, INVALID_DEVID, &ret); + if (aes == NULL) + return WC_TEST_RET_ENC_EC(ret); +#else + ret = wc_AesInit(aes, HEAP_HINT, INVALID_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(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(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(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(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(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(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(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(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(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(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(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(MISSING_KEY)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_AesCfb8Encrypt(aes, cipher, plain, 1) != + 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(MISSING_KEY)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (wc_AesCfb8Decrypt(aes, cipher, plain, 1) != + WC_NO_ERR_TRACE(MISSING_KEY)) + 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(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(MISSING_KEY)) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#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: + +#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) @@ -29908,6 +30095,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; @@ -30035,6 +30225,31 @@ 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 + /* 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_NC, exit_rsa_pss); + ret = 0; #endif /* WOLFSSL_MICROCHIP_TA100 */ #ifdef RSA_PSS_TEST_WRONG_PARAMS diff --git a/wolfssl/wolfcrypt/aes.h b/wolfssl/wolfcrypt/aes.h index 1eccc39cae..455f30088b 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(MAX3266X_CB)) || \ + (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 @@ -482,6 +521,22 @@ 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), 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; }; #ifndef WC_AES_TYPE_DEFINED