From 61463cd6d8c37ce24689ae06a736de373977cb18 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Thu, 1 Oct 2026 15:32:51 -0700 Subject: [PATCH 1/2] fix: return errors for refused requests GnuTLS treats a provider return >= 0 as success. Under HAVE_FIPS the wrapper returned GNUTLS_FIPS140_OP_NOT_APPROVED, a positive enum value, when wolfCrypt refused a request: - wc_HmacSetKey (FIPS refuses keys shorter than 14 bytes): gnutls_hmac_fast() returned 0 without writing the MAC, and gnutls_hmac_init() succeeded with an HMAC whose output does not depend on the key: SHA256(0^64 || SHA256(0^64 || msg)) for SHA-256. Anyone can compute it, so a forged tag verifies for every short-key HMAC user on a FIPS build; - wc_HKDF_Extract, and wc_PBKDF2_ex with HMAC_MIN_KEYLEN_E: gnutls_hkdf_extract()/gnutls_pbkdf2() returned 2 with the output unwritten; - RSA key generation failure and the 1024-bit refusal: the key was left without parameters. Return the negative error the non-FIPS path returns, and GNUTLS_E_INVALID_REQUEST for 1024-bit RSA. The Ed25519/Ed448/X25519/ X448 key generation curve checks returned GNUTLS_ECC_CURVE_INVALID, which is 0; return GNUTLS_E_ECC_UNSUPPORTED_CURVE. wolfssl_hmac_fast() now frees its context when the key is refused. New test test_fips_refusal_errors: HMAC-SHA256 with a 4-byte key (RFC 4231 test case 2) must fail with an error or give the RFC value, and two different short keys must not give the same MAC. --- wolfssl-gnutls-wrapper/src/kdf.c | 9 -- wolfssl-gnutls-wrapper/src/mac.c | 5 +- wolfssl-gnutls-wrapper/src/pk.c | 28 +++--- wolfssl-gnutls-wrapper/tests/Makefile | 2 +- .../tests/test_fips_refusal_errors.c | 96 +++++++++++++++++++ 5 files changed, 110 insertions(+), 30 deletions(-) create mode 100644 wolfssl-gnutls-wrapper/tests/test_fips_refusal_errors.c diff --git a/wolfssl-gnutls-wrapper/src/kdf.c b/wolfssl-gnutls-wrapper/src/kdf.c index 0ee716a..0acee3b 100644 --- a/wolfssl-gnutls-wrapper/src/kdf.c +++ b/wolfssl-gnutls-wrapper/src/kdf.c @@ -47,11 +47,7 @@ static int wolfssl_hkdf_extract(gnutls_mac_algorithm_t mac, const void *key, if (ret != 0) { WGW_WOLFSSL_ERROR("wc_HKDF_Extract", ret); -#if defined(HAVE_FIPS) - return GNUTLS_FIPS140_OP_NOT_APPROVED; -#else return GNUTLS_E_INTERNAL_ERROR; -#endif } return 0; @@ -147,11 +143,6 @@ static int wolfssl_pbkdf2(gnutls_mac_algorithm_t mac, const void *key, if (ret != 0) { WGW_WOLFSSL_ERROR("wc_PBKDF2_ex", ret); -#if defined(HAVE_FIPS) - if (ret == HMAC_MIN_KEYLEN_E) { - return GNUTLS_FIPS140_OP_NOT_APPROVED; - } -#endif return GNUTLS_E_INTERNAL_ERROR; } diff --git a/wolfssl-gnutls-wrapper/src/mac.c b/wolfssl-gnutls-wrapper/src/mac.c index c5ecc2e..791ee90 100644 --- a/wolfssl-gnutls-wrapper/src/mac.c +++ b/wolfssl-gnutls-wrapper/src/mac.c @@ -197,11 +197,7 @@ static int wolfssl_hmac_setkey(void *_ctx, const void *key, size_t keysize) (word32)keysize); if (ret != 0) { WGW_WOLFSSL_ERROR("wc_HmacSetKey", ret); -#if defined(HAVE_FIPS) - return GNUTLS_FIPS140_OP_NOT_APPROVED; -#else return GNUTLS_E_HASH_FAILED; -#endif } WGW_LOG("hmac key set successfully"); @@ -362,6 +358,7 @@ static int wolfssl_hmac_fast(gnutls_mac_algorithm_t algorithm, /* Set key into HMAC context. */ ret = wolfssl_hmac_setkey(ctx, key, keysize); if (ret != 0) { + wolfssl_hmac_deinit(ctx); return ret; } diff --git a/wolfssl-gnutls-wrapper/src/pk.c b/wolfssl-gnutls-wrapper/src/pk.c index bb0090b..9873221 100644 --- a/wolfssl-gnutls-wrapper/src/pk.c +++ b/wolfssl-gnutls-wrapper/src/pk.c @@ -2461,7 +2461,7 @@ static int wolfssl_pk_generate_params(gnutls_pk_algorithm_t algo, * @param [in] bits Number of bits in key. * @param [in, out] params GnuTLS PK parameters. * @return 0 on success. - * @return GNUTLS_FIPS140_OP_NOT_APPROVED when FIPS140 build + * @return GNUTLS_E_INVALID_REQUEST when FIPS140 build and 1024 bits. * @return GNUTLS_E_RANDOM_FAILED when random initialization fails. * @return GNUTLS_E_CRYPTO_INIT_FAILED when initializing RSA key fails. * @return GnuTLS error on other failure. @@ -2483,7 +2483,7 @@ static int wolfssl_pk_generate_keys_rsa(unsigned int bits, /* missing check for 1024, 1024 is not allowed */ if (bits == 1024) { WGW_ERROR("Bits size not valid"); - return GNUTLS_FIPS140_OP_NOT_APPROVED; + return GNUTLS_E_INVALID_REQUEST; } #endif @@ -2509,11 +2509,7 @@ static int wolfssl_pk_generate_keys_rsa(unsigned int bits, if (ret != 0) { WGW_ERROR("RSA key generation failed with code %d", ret); wc_FreeRsaKey(&rsa); -#if defined(HAVE_FIPS) - return GNUTLS_FIPS140_OP_NOT_APPROVED; -#else return GNUTLS_E_PK_GENERATION_ERROR; -#endif } /* Store RSA fields in the GnuTLS parameters. */ @@ -2715,7 +2711,7 @@ static int wolfssl_pk_generate_keys_ecc(unsigned int curve, * @param [in] curve GnuTLS curve. * @param [in, out] params GnuTLS PK parameters. * @return 0 on success. - * @return GNUTLS_ECC_CURVE_INVALID when curve does not match algorithm. + * @return GNUTLS_E_ECC_UNSUPPORTED_CURVE when curve does not match algorithm. * @return GNUTLS_E_RANDOM_FAILED when random initialization fails. * @return GNUTLS_E_CRYPTO_INIT_FAILED when initializing key fails. * @return GnuTLS error on other failure. @@ -2732,7 +2728,7 @@ static int wolfssl_pk_generate_keys_ed25519(unsigned int curve, /* Check curve matches algorithm. */ if (curve != GNUTLS_ECC_CURVE_ED25519) { - return GNUTLS_ECC_CURVE_INVALID; + return GNUTLS_E_ECC_UNSUPPORTED_CURVE; } /* Initialize a new random for blinding. */ @@ -2817,7 +2813,7 @@ static int wolfssl_pk_generate_keys_ed25519(unsigned int curve, * @param [in] curve GnuTLS curve. * @param [in, out] params GnuTLS PK parameters. * @return 0 on success. - * @return GNUTLS_ECC_CURVE_INVALID when curve does not match algorithm. + * @return GNUTLS_E_ECC_UNSUPPORTED_CURVE when curve does not match algorithm. * @return GNUTLS_E_RANDOM_FAILED when random initialization fails. * @return GNUTLS_E_CRYPTO_INIT_FAILED when initializing key fails. * @return GnuTLS error on other failure. @@ -2834,7 +2830,7 @@ static int wolfssl_pk_generate_keys_ed448(unsigned int curve, /* Check curve matches algorithm. */ if (curve != GNUTLS_ECC_CURVE_ED448) { - return GNUTLS_ECC_CURVE_INVALID; + return GNUTLS_E_ECC_UNSUPPORTED_CURVE; } /* Initialize a new random for blinding. */ @@ -2919,7 +2915,7 @@ static int wolfssl_pk_generate_keys_ed448(unsigned int curve, * @param [in] curve GnuTLS curve. * @param [in, out] params GnuTLS PK parameters. * @return 0 on success. - * @return GNUTLS_ECC_CURVE_INVALID when curve does not match algorithm. + * @return GNUTLS_E_ECC_UNSUPPORTED_CURVE when curve does not match algorithm. * @return GNUTLS_E_RANDOM_FAILED when random initialization fails. * @return GNUTLS_E_CRYPTO_INIT_FAILED when initializing key fails. * @return GnuTLS error on other failure. @@ -2936,7 +2932,7 @@ static int wolfssl_pk_generate_keys_x25519(unsigned int curve, /* Check curve matches algorithm. */ if (curve != GNUTLS_ECC_CURVE_X25519) { - return GNUTLS_ECC_CURVE_INVALID; + return GNUTLS_E_ECC_UNSUPPORTED_CURVE; } /* Initialize a new random for generation. */ @@ -3021,7 +3017,7 @@ static int wolfssl_pk_generate_keys_x25519(unsigned int curve, * @param [in] curve GnuTLS curve. * @param [in, out] params GnuTLS PK parameters. * @return 0 on success. - * @return GNUTLS_ECC_CURVE_INVALID when curve does not match algorithm. + * @return GNUTLS_E_ECC_UNSUPPORTED_CURVE when curve does not match algorithm. * @return GNUTLS_E_RANDOM_FAILED when random initialization fails. * @return GNUTLS_E_CRYPTO_INIT_FAILED when initializing key fails. * @return GnuTLS error on other failure. @@ -3038,7 +3034,7 @@ static int wolfssl_pk_generate_keys_x448(unsigned int curve, /* Check curve matches algorithm. */ if (curve != GNUTLS_ECC_CURVE_X448) { - return GNUTLS_ECC_CURVE_INVALID; + return GNUTLS_E_ECC_UNSUPPORTED_CURVE; } /* Initialize a new random for blinding. */ @@ -3125,8 +3121,8 @@ static int wolfssl_pk_generate_keys_x448(unsigned int curve, * @param [in] ephemeral Key pair is ephemenral - random can be weaker. * Ignored. * @return 0 on success. - * @return GNUTLS_FIPS140_OP_NOT_APPROVED when FIPS140 build - * @return GNUTLS_ECC_CURVE_INVALID when curve does not match algorithm. + * @return GNUTLS_E_INVALID_REQUEST when FIPS140 build and 1024 bits. + * @return GNUTLS_E_ECC_UNSUPPORTED_CURVE when curve does not match algorithm. * @return GNUTLS_E_RANDOM_FAILED when random initialization fails. * @return GNUTLS_E_CRYPTO_INIT_FAILED when initializing key fails. * @return GnuTLS error on other failure. diff --git a/wolfssl-gnutls-wrapper/tests/Makefile b/wolfssl-gnutls-wrapper/tests/Makefile index f7fe39d..0bf463b 100644 --- a/wolfssl-gnutls-wrapper/tests/Makefile +++ b/wolfssl-gnutls-wrapper/tests/Makefile @@ -1,4 +1,4 @@ -TESTS = test_hash test_shake test_aescbc test_aescfb8 test_aesgcm test_aesccm test_aesxts test_hmac test_cmac test_gmac test_rnd test_rnd_fork test_tls_prf test_hkdf test_pbkdf2 test_ecdsa_sign_and_verify test_ecdh_encrypt_and_decrypt test_eddsa_sign_and_verify test_rsa_sign_and_verify test_rsa_encrypt_and_decrypt test_dh_encrypt_and_decrypt test_pk_import_export test_long_hash test_fips test_aessiv +TESTS = test_hash test_shake test_aescbc test_aescfb8 test_aesgcm test_aesccm test_aesxts test_hmac test_cmac test_gmac test_rnd test_rnd_fork test_tls_prf test_hkdf test_pbkdf2 test_ecdsa_sign_and_verify test_ecdh_encrypt_and_decrypt test_eddsa_sign_and_verify test_rsa_sign_and_verify test_rsa_encrypt_and_decrypt test_dh_encrypt_and_decrypt test_pk_import_export test_long_hash test_fips test_aessiv test_fips_refusal_errors PKGCONF ?= pkg-config UNAME_S := $(shell uname -s) diff --git a/wolfssl-gnutls-wrapper/tests/test_fips_refusal_errors.c b/wolfssl-gnutls-wrapper/tests/test_fips_refusal_errors.c new file mode 100644 index 0000000..24ddf7b --- /dev/null +++ b/wolfssl-gnutls-wrapper/tests/test_fips_refusal_errors.c @@ -0,0 +1,96 @@ +/* A request the provider refuses must fail with a negative error. + * + * GnuTLS treats any provider return >= 0 as success. A FIPS wolfCrypt refuses + * an HMAC key shorter than 14 bytes; if that refusal is not returned as a + * negative error, gnutls_hmac_fast() reports success without writing the MAC, + * and gnutls_hmac_init() succeeds with an HMAC that does not depend on the + * key, so a forged tag verifies. + * + * HMAC-SHA256 with the 4-byte key "Jefe" (RFC 4231 test case 2), one-shot and + * streaming: each must either fail with a negative error (FIPS) or give the + * RFC value. Two different short keys must not give the same MAC. + */ +#include +#include +#include +#include +#include "test_util.h" + +static const char *msg = "what do ya want for nothing?"; + +/* RFC 4231, section 4.3. */ +static const unsigned char tc2_mac[32] = { + 0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, 0x6a, 0x04, 0x24, 0x26, + 0x08, 0x95, 0x75, 0xc7, 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, + 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43 +}; + +static int hmac_stream(const char *key, unsigned char *mac) +{ + gnutls_hmac_hd_t hd; + int ret; + + ret = gnutls_hmac_init(&hd, GNUTLS_MAC_SHA256, key, strlen(key)); + if (ret < 0) { + return ret; + } + ret = gnutls_hmac(hd, msg, strlen(msg)); + gnutls_hmac_deinit(hd, mac); + return ret; +} + +/* 0 when ret is a negative error, or success with the RFC 4231 value. */ +static int check(const char *op, int ret, const unsigned char *mac) +{ + if (ret < 0) { + printf("%s refused: %s\n", op, gnutls_strerror(ret)); + return 0; + } + if (ret > 0) { + printf("FAILURE - %s returned %d, not an error code\n", op, ret); + return 1; + } + return compare(op, mac, tc2_mac, sizeof(tc2_mac)); +} + +int main(void) +{ + unsigned char mac[32], mac2[32]; + int ret, ret2; + + printf("Testing that refused HMAC keys fail with an error...\n"); + + ret = gnutls_global_init(); + if (ret != 0) { + print_gnutls_error("initializing GnuTLS", ret); + return 1; + } + + memset(mac, 0, sizeof(mac)); + ret = gnutls_hmac_fast(GNUTLS_MAC_SHA256, "Jefe", 4, msg, strlen(msg), mac); + if (check("HMAC-SHA256 one-shot, 4-byte key", ret, mac) != 0) { + gnutls_global_deinit(); + return 1; + } + + memset(mac, 0, sizeof(mac)); + ret = hmac_stream("Jefe", mac); + if (check("HMAC-SHA256 streaming, 4-byte key", ret, mac) != 0) { + gnutls_global_deinit(); + return 1; + } + + memset(mac, 0, sizeof(mac)); + memset(mac2, 0, sizeof(mac2)); + ret = hmac_stream("Jefe", mac); + ret2 = hmac_stream("Jeff", mac2); + if (ret == 0 && ret2 == 0 && memcmp(mac, mac2, sizeof(mac)) == 0) { + printf("FAILURE - two different keys gave the same MAC\n"); + gnutls_global_deinit(); + return 1; + } + + gnutls_global_deinit(); + printf("\nAll refusal tests completed successfully!\n"); + return 0; +} From d05d0473a2f87147a86b40fc15ed1bcc754f9326 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Thu, 1 Oct 2026 17:23:00 -0700 Subject: [PATCH 2/2] fix(mac): do not free the context on CMAC/GMAC errors When wc_InitCmac (in the CMAC setkey, hash and output callbacks) or wc_GmacSetKey failed, the wrapper gnutls_free()d its context. GnuTLS owns that context and calls deinit on it afterwards, so it was used after free and freed twice. A failed CAST makes these calls fail on a FIPS build. Leave the context to deinit, and have the CMAC/GMAC fast functions free it when setkey fails (they leaked it on a wrong-size key). With wc_InitCmac/wc_GmacSetKey forced to fail, gnutls_hmac_init( AES-CMAC-128 / AES-GMAC-128) showed invalid reads/writes and a double free under valgrind; it now returns an error and runs clean. --- wolfssl-gnutls-wrapper/src/mac.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/wolfssl-gnutls-wrapper/src/mac.c b/wolfssl-gnutls-wrapper/src/mac.c index 791ee90..3a26e9d 100644 --- a/wolfssl-gnutls-wrapper/src/mac.c +++ b/wolfssl-gnutls-wrapper/src/mac.c @@ -504,7 +504,6 @@ static int wolfssl_cmac_setkey(void *_ctx, const void *key, size_t keysize) WC_CMAC_AES, NULL); if (ret != 0) { WGW_WOLFSSL_ERROR("wc_InitCmac", ret); - gnutls_free(ctx); return GNUTLS_E_HASH_FAILED; } @@ -545,7 +544,6 @@ static int wolfssl_cmac_hash(void *_ctx, const void *text, size_t textsize) (word32)ctx->key_size, WC_CMAC_AES, NULL); if (ret != 0) { WGW_WOLFSSL_ERROR("wc_InitCmac", ret); - gnutls_free(ctx); return GNUTLS_E_HASH_FAILED; } ctx->set_key = 0; @@ -614,7 +612,6 @@ static int wolfssl_cmac_output(void *_ctx, void *digest, size_t digestsize) (word32)ctx->key_size, WC_CMAC_AES, NULL); if (ret != 0) { WGW_WOLFSSL_ERROR("wc_InitCmac", ret); - gnutls_free(ctx); return GNUTLS_E_HASH_FAILED; } ctx->set_key = 0; @@ -687,6 +684,7 @@ static int wolfssl_cmac_fast(gnutls_mac_algorithm_t algorithm, /* Set key into CMAC context. */ ret = wolfssl_cmac_setkey(ctx, key, keysize); if (ret != 0) { + wolfssl_cmac_deinit(ctx); return ret; } @@ -862,7 +860,6 @@ static int wolfssl_gmac_setkey(void *_ctx, const void *key, size_t keysize) ret = wc_GmacSetKey(&ctx->gmac_ctx, (const byte*)key, (word32)keysize); if (ret != 0) { WGW_WOLFSSL_ERROR("wc_GmacSetKey", ret); - gnutls_free(ctx); return GNUTLS_E_HASH_FAILED; } @@ -1048,6 +1045,7 @@ static int wolfssl_gmac_fast(gnutls_mac_algorithm_t algorithm, /* Set key into GMAC context. */ ret = wolfssl_gmac_setkey(ctx, key, keysize); if (ret != 0) { + wolfssl_gmac_deinit(ctx); return ret; }