diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 583422bea4..5e30c64b3b 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -651,6 +651,7 @@ WC_ASYNC_NO_SHA384 WC_ASYNC_NO_SHA512 WC_ASYNC_NO_X25519 WC_ASYNC_THREAD_BIND +WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS WC_CACHE_RESISTANT_BASE64_TABLE WC_DILITHIUM_FIXED_ARRAY WC_DISABLE_RADIX_ZERO_PAD diff --git a/doc/dox_comments/header_files/cmac.h b/doc/dox_comments/header_files/cmac.h index 4de7810a8a..b2bac936a4 100644 --- a/doc/dox_comments/header_files/cmac.h +++ b/doc/dox_comments/header_files/cmac.h @@ -174,8 +174,11 @@ int wc_AesCmacGenerate(byte* out, word32* outSz, \ingroup CMAC \brief Single shot function for validating a CMAC \return 0 on success - \param check CMAC value to verify - \param checkSz size of check buffer + \return BAD_FUNC_ARG if parameters are invalid + \return MAC_CMP_FAILED_E if the supplied tag does not match + \param check Expected MAC value to verify + \param checkSz size of expected MAC value; must be in + [\c WC_CMAC_TAG_MIN_SZ, \c WC_AES_BLOCK_SIZE] \param in input data to process \param inSz size of input data \param key key pointer @@ -211,10 +214,8 @@ int wc_CMAC_Grow(Cmac* cmac, const byte* in, int inSz); \ingroup CMAC \brief Single shot AES-CMAC generation with extended parameters including heap and device ID. - \return 0 on success \return BAD_FUNC_ARG if parameters are invalid - \param cmac Pointer to Cmac structure (can be NULL for one-shot) \param out Buffer to store MAC output \param outSz Pointer to output size (in/out) @@ -249,14 +250,13 @@ int wc_AesCmacGenerate_ex(Cmac *cmac, byte* out, word32* outSz, \ingroup CMAC \brief Single shot AES-CMAC verification with extended parameters including heap and device ID. - \return 0 on success \return BAD_FUNC_ARG if parameters are invalid \return MAC_CMP_FAILED_E if MAC verification fails - - \param cmac Pointer to Cmac structure (can be NULL for one-shot) + \param cmac Pointer to Cmac structure \param check Expected MAC value to verify - \param checkSz Size of expected MAC + \param checkSz Size of expected MAC; must be in + [\c WC_CMAC_TAG_MIN_SZ, \c WC_AES_BLOCK_SIZE] \param in Input data to authenticate \param inSz Length of input data \param key AES key @@ -267,17 +267,24 @@ int wc_AesCmacGenerate_ex(Cmac *cmac, byte* out, word32* outSz, _Example_ \code - byte mac[AES_BLOCK_SIZE]; + Cmac cmac; + byte mac[WC_AES_BLOCK_SIZE]; byte key[16], msg[64]; + int ret; - int ret = wc_AesCmacVerify_ex(NULL, mac, sizeof(mac), msg, + ret = wc_InitCmac_ex(&cmac, key, sizeof(key), WC_CMAC_AES, NULL, + NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_AesCmacVerify_ex(&cmac, mac, sizeof(mac), msg, sizeof(msg), key, sizeof(key), NULL, INVALID_DEVID); + } if (ret == MAC_CMP_FAILED_E) { // MAC verification failed } \endcode + \sa wc_InitCmac_ex \sa wc_AesCmacVerify \sa wc_AesCmacGenerate_ex */ diff --git a/src/tls13.c b/src/tls13.c index 794e3e068d..e2d69c9f60 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -6139,7 +6139,10 @@ static int DoTls13CertificateRequest(WOLFSSL* ssl, const byte* input, ssl->options.sendVerify = SEND_BLANK_CERT; #else WOLFSSL_MSG("Certificate required but none set on client"); - SendAlert(ssl, alert_fatal, illegal_parameter); + /* RFC 8446 Section 4.4.2.4: send certificate_required when a + * peer (here, the client) cannot provide a certificate that the + * other peer required. */ + SendAlert(ssl, alert_fatal, certificate_required); WOLFSSL_ERROR_VERBOSE(NO_CERT_ERROR); return NO_CERT_ERROR; #endif diff --git a/tests/api/test_cmac.c b/tests/api/test_cmac.c index e30eeefb2e..b3af844e97 100644 --- a/tests/api/test_cmac.c +++ b/tests/api/test_cmac.c @@ -244,6 +244,47 @@ int test_wc_AesCmacGenerate(void) WC_NO_ERR_TRACE(BAD_FUNC_ARG)); ExpectIntEQ(wc_AesCmacVerify(mac, macSz, NULL, msgSz, key, keySz), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + ExpectIntEQ(wc_AesCmacVerify(mac, 1, msg, msgSz, key, keySz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_AesCmacVerify(mac, WC_CMAC_TAG_MIN_SZ - 1, msg, msgSz, + key, keySz), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_AesCmacVerify(mac, WC_AES_BLOCK_SIZE + 1, msg, msgSz, + key, keySz), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* Truncated tags within the supported range must verify correctly when + * the generator was asked to produce the same length */ + { + byte truncMac[WC_AES_BLOCK_SIZE]; + word32 truncSz; + word32 lengths[] = { WC_CMAC_TAG_MIN_SZ, 8, WC_AES_BLOCK_SIZE - 1 }; + word32 lengthsSz = (word32)(sizeof(lengths)/sizeof(lengths[0])); + word32 li; + for (li = 0; li < lengthsSz; li++) { + XMEMSET(truncMac, 0, sizeof(truncMac)); + truncSz = lengths[li]; + ExpectIntEQ(wc_AesCmacGenerate(truncMac, &truncSz, msg, msgSz, + key, keySz), 0); + ExpectIntEQ(truncSz, lengths[li]); + ExpectIntEQ(wc_AesCmacVerify(truncMac, truncSz, msg, msgSz, + key, keySz), 0); + /* Flipping a bit in the truncated tag must yield + * MAC_CMP_FAILED_E, not silent success from comparing a too + * short prefix. */ + truncMac[0] ^= 0x01; + ExpectIntEQ(wc_AesCmacVerify(truncMac, truncSz, msg, msgSz, + key, keySz), WC_NO_ERR_TRACE(MAC_CMP_FAILED_E)); + } + } + + /* A full-length tag that does not match must return MAC_CMP_FAILED_E. */ + { + byte badMac[WC_AES_BLOCK_SIZE]; + XMEMCPY(badMac, mac, WC_AES_BLOCK_SIZE); + badMac[0] ^= 0x01; + ExpectIntEQ(wc_AesCmacVerify(badMac, WC_AES_BLOCK_SIZE, msg, msgSz, + key, keySz), WC_NO_ERR_TRACE(MAC_CMP_FAILED_E)); + } #endif return EXPECT_RESULT(); diff --git a/wolfcrypt/src/cmac.c b/wolfcrypt/src/cmac.c index b5e23f85b1..0e8a9f7e42 100644 --- a/wolfcrypt/src/cmac.c +++ b/wolfcrypt/src/cmac.c @@ -574,24 +574,32 @@ int wc_AesCmacVerify_ex(Cmac* cmac, { int ret = 0; byte a[WC_AES_BLOCK_SIZE]; - word32 aSz = sizeof(a); + word32 aSz; int compareRet; - if (cmac == NULL || check == NULL || checkSz == 0 || - (in == NULL && inSz != 0)) { + if (cmac == NULL || check == NULL || checkSz < WC_CMAC_TAG_MIN_SZ || + checkSz > WC_AES_BLOCK_SIZE || (in == NULL && inSz != 0)) { return BAD_FUNC_ARG; } - XMEMSET(a, 0, aSz); + aSz = checkSz; + XMEMSET(a, 0, sizeof(a)); ret = wc_AesCmacGenerate_ex(cmac, a, &aSz, in, inSz, key, keySz, heap, devId); + /* aSz is passed by reference to wc_AesCmacGenerate_ex, which on the + * WOLF_CRYPTO_CB path forwards it to a user-supplied callback that may + * write back any value. Reject anything that does not match the user + * provided length. */ + if (ret == 0 && aSz != checkSz) { + ret = BAD_STATE_E; + } if (ret == 0) { - compareRet = ConstantCompare(check, a, (int)min(checkSz, aSz)); - ret = compareRet ? 1 : 0; + compareRet = ConstantCompare(check, a, (int)aSz); + ret = compareRet ? MAC_CMP_FAILED_E : 0; } return ret; @@ -605,7 +613,8 @@ int wc_AesCmacVerify(const byte* check, word32 checkSz, int ret = 0; WC_DECLARE_VAR(cmac, Cmac, 1, 0); - if (check == NULL || checkSz == 0 || (in == NULL && inSz > 0) || + if (check == NULL || checkSz < WC_CMAC_TAG_MIN_SZ || + checkSz > WC_AES_BLOCK_SIZE || (in == NULL && inSz > 0) || key == NULL || keySz == 0) { return BAD_FUNC_ARG; } diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 12f25cc534..77d2caa1f7 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -4202,7 +4202,8 @@ extern void uITRON4_free(void *p) ; #if defined(HAVE_CURVE25519) && !defined(CURVE25519_SMALL) && \ !defined(FREESCALE_LTC_ECC) && !defined(WOLFSSL_ARMASM) && \ (!defined(USE_INTEL_SPEEDUP) || defined(NO_CURVED25519_X64)) && \ - !defined(WOLFSSL_CURVE25519_BLINDING) && !defined(NO_CURVE25519_BLINDING) + !defined(WOLFSSL_CURVE25519_BLINDING) && !defined(NO_CURVE25519_BLINDING) \ + && !defined(WC_NO_RNG) #define WOLFSSL_CURVE25519_BLINDING #endif @@ -4212,7 +4213,8 @@ extern void uITRON4_free(void *p) ; #if (defined(USE_FAST_MATH) && !defined(TFM_TIMING_RESISTANT)) || \ (defined(HAVE_ECC) && !defined(ECC_TIMING_RESISTANT)) || \ (!defined(NO_RSA) && !defined(WC_RSA_BLINDING) && !defined(HAVE_FIPS) && \ - !defined(WC_NO_RNG)) + !defined(WC_NO_RNG) && !defined(WOLFSSL_RSA_PUBLIC_ONLY) && \ + !defined(WOLFSSL_RSA_VERIFY_ONLY)) #if !defined(_MSC_VER) && !defined(__TASKING__) #warning "For timing resistance / side-channel attack prevention consider using harden options" @@ -4222,6 +4224,20 @@ extern void uITRON4_free(void *p) ; #endif #endif +/* WC_NO_RNG silently removes RSA blinding, as blinding depends on the RNG. + * Refuse to build until the conflict is resolved or the loss of hardening is + * explicitly acknowledged via WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS. */ +#if defined(WC_NO_RNG) && ((defined(WC_RSA_BLINDING) && !defined(NO_RSA) && \ + !defined(WOLFSSL_RSA_PUBLIC_ONLY) && !defined(WOLFSSL_RSA_VERIFY_ONLY)) || \ + (defined(HAVE_CURVE25519) && defined(WOLFSSL_CURVE25519_BLINDING)) || \ + (defined(HAVE_ECC) && defined(WOLFSSL_ECC_BLIND_K))) && \ + !defined(WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS) + #error "Blinding is enabled but the RNG is disabled. Either remove \ +WC_NO_RNG to enable the RNG, disable blinding by removing WC_RSA_BLINDING/\ +WOLFSSL_CURVE25519_BLINDING/WOLFSSL_ECC_BLIND_K, or acknowledge the loss of \ +blinding by defining WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS." +#endif + #ifdef OPENSSL_COEXIST /* make sure old names are disabled */ #ifndef NO_OLD_SSL_NAMES