File gnutls-Add-missing-FIPS-service-indicator-transitions.patch of Package gnutls.26615

From e6c7e55953abd52b731cf4581e0d89065eb1e20a Mon Sep 17 00:00:00 2001
From: Zoltan Fridrich <zfridric@redhat.com>
Date: Tue, 5 Apr 2022 16:28:41 +0200
Subject: [PATCH] Add missing FIPS service indicator transitions

Signed-off-by: Zoltan Fridrich <zfridric@redhat.com>
---
 lib/ext/session_ticket.c       |   5 +
 lib/nettle/rnd-fips.c          |  33 ++++--
 lib/nettle/rnd.c               |  11 +-
 lib/x509/privkey_pkcs8_pbes1.c |   5 +-
 tests/fips-test.c              | 193 ++++++++++++++++++++++++++++-----
 5 files changed, 209 insertions(+), 38 deletions(-)

diff --git a/lib/ext/session_ticket.c b/lib/ext/session_ticket.c
index 5877f8fa12..cecb370cd4 100644
--- a/lib/ext/session_ticket.c
+++ b/lib/ext/session_ticket.c
@@ -22,6 +22,7 @@
 
 #include "gnutls_int.h"
 #include "errors.h"
+#include <fips.h>
 #include <datum.h>
 #include <algorithms.h>
 #include <handshake.h>
@@ -238,9 +239,11 @@ _gnutls_decrypt_session_ticket(gnutls_session_t session,
 				cipher_to_entry(TICKET_CIPHER),
 				&stek_cipher_key, &IV, 0);
 	if (ret < 0) {
+		_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
 		gnutls_assert();
 		goto cleanup;
 	}
+	_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
 
 	ret = _gnutls_cipher_decrypt(&cipher_hd, ticket.encrypted_state,
 				     ticket.encrypted_state_len);
@@ -315,9 +318,11 @@ _gnutls_encrypt_session_ticket(gnutls_session_t session,
 				cipher_to_entry(TICKET_CIPHER),
 				&stek_cipher_key, &IV, 1);
 	if (ret < 0) {
+		_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
 		gnutls_assert();
 		goto cleanup;
 	}
+	_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
 
 	ret = _gnutls_cipher_encrypt(&cipher_hd, encrypted_state.data,
 				     encrypted_state.size);
diff --git a/lib/nettle/rnd-fips.c b/lib/nettle/rnd-fips.c
index ccb92d25a2..35ad618d64 100644
--- a/lib/nettle/rnd-fips.c
+++ b/lib/nettle/rnd-fips.c
@@ -57,20 +57,27 @@ static int get_random(struct drbg_aes_ctx *ctx, struct fips_ctx *fctx,
 
 	if ( _gnutls_detect_fork(fctx->forkid) != 0) {
 		ret = _rngfips_ctx_reinit(fctx);
-		if (ret < 0)
+		if (ret < 0) {
+			_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
 			return gnutls_assert_val(ret);
+		}
 	}
 
 	if (ctx->reseed_counter > DRBG_AES_RESEED_TIME) {
 		ret = drbg_reseed(fctx, ctx);
-		if (ret < 0)
+		if (ret < 0) {
+			_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
 			return gnutls_assert_val(ret);
+		}
 	}
 
 	ret = drbg_aes_random(ctx, length, buffer);
-	if (ret == 0)
+	if (ret == 0) {
+		_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
 		return gnutls_assert_val(GNUTLS_E_RANDOM_FAILED);
+	}
 
+	_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
 	return 0;
 }
 
@@ -99,6 +106,7 @@ static int get_entropy(struct fips_ctx *fctx, uint8_t *buffer, size_t length)
 		sha256_digest(&ctx, sizeof(hash), hash);
 
 		if (memcmp(hash, fctx->entropy_hash, sizeof(hash)) == 0) {
+			_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
 			_gnutls_switch_lib_state(LIB_STATE_ERROR);
 			return gnutls_assert_val(GNUTLS_E_RANDOM_FAILED);
 		}
@@ -110,6 +118,7 @@ static int get_entropy(struct fips_ctx *fctx, uint8_t *buffer, size_t length)
 	}
 	zeroize_key(block, sizeof(block));
 
+	_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
 	return 0;
 }
 
@@ -121,15 +130,20 @@ static int drbg_init(struct fips_ctx *fctx, struct drbg_aes_ctx *ctx)
 	int ret;
 
 	ret = get_entropy(fctx, buffer, sizeof(buffer));
-	if (ret < 0)
+	if (ret < 0) {
+		_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
 		return gnutls_assert_val(ret);
+	}
 
 	ret = drbg_aes_init(ctx, sizeof(buffer), buffer,
 			    PSTRING_SIZE, (void*)PSTRING);
 	zeroize_key(buffer, sizeof(buffer));
-	if (ret == 0)
+	if (ret == 0) {
+		_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
 		return gnutls_assert_val(GNUTLS_E_RANDOM_FAILED);
+	}
 
+	_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
 	return GNUTLS_E_SUCCESS;
 }
 
@@ -140,14 +154,19 @@ static int drbg_reseed(struct fips_ctx *fctx, struct drbg_aes_ctx *ctx)
 	int ret;
 
 	ret = get_entropy(fctx, buffer, sizeof(buffer));
-	if (ret < 0)
+	if (ret < 0) {
+		_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
 		return gnutls_assert_val(ret);
+	}
 
 	ret = drbg_aes_reseed(ctx, sizeof(buffer), buffer, 0, NULL);
 	zeroize_key(buffer, sizeof(buffer));
-	if (ret == 0)
+	if (ret == 0) {
+		_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
 		return gnutls_assert_val(GNUTLS_E_RANDOM_FAILED);
+	}
 
+	_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
 	return GNUTLS_E_SUCCESS;
 }
 
diff --git a/lib/nettle/rnd.c b/lib/nettle/rnd.c
index 0512af1cd1..cddf1f72ff 100644
--- a/lib/nettle/rnd.c
+++ b/lib/nettle/rnd.c
@@ -174,8 +174,10 @@ wrap_nettle_rnd(void *_ctx, int level, void *data, size_t datasize)
 		prng_ctx = &ctx->normal;
 	else if (level == GNUTLS_RND_NONCE)
 		prng_ctx = &ctx->nonce;
-	else
+	else {
+		_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
 		return gnutls_assert_val(GNUTLS_E_RANDOM_FAILED);
+	}
 
 	/* Two reasons for this memset():
 	 *  1. avoid getting filled with valgrind warnings
@@ -207,12 +209,14 @@ wrap_nettle_rnd(void *_ctx, int level, void *data, size_t datasize)
 
 		if (ret < 0) {
 			gnutls_assert();
+			_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
 			goto cleanup;
 		}
 
 		ret = single_prng_init(prng_ctx, new_key, sizeof(new_key), 0);
 		if (ret < 0) {
 			gnutls_assert();
+			_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
 			goto cleanup;
 		}
 
@@ -227,17 +231,20 @@ wrap_nettle_rnd(void *_ctx, int level, void *data, size_t datasize)
 		ret = wrap_nettle_rnd(_ctx, GNUTLS_RND_RANDOM, new_key, sizeof(new_key));
 		if (ret < 0) {
 			gnutls_assert();
+			_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
 			goto cleanup;
 		}
 
 		ret = single_prng_init(prng_ctx, new_key, sizeof(new_key), 0);
 		if (ret < 0) {
 			gnutls_assert();
+			_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
 			goto cleanup;
 		}
 	}
 
 	ret = 0;
+	_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
 
 cleanup:
 	return ret;
@@ -254,8 +261,6 @@ static void wrap_nettle_rnd_refresh(void *_ctx)
 
 	wrap_nettle_rnd(_ctx, GNUTLS_RND_NONCE, &tmp, 1);
 	wrap_nettle_rnd(_ctx, GNUTLS_RND_RANDOM, &tmp, 1);
-
-	return;
 }
 
 int crypto_rnd_prio = INT_MAX;
diff --git a/lib/x509/privkey_pkcs8_pbes1.c b/lib/x509/privkey_pkcs8_pbes1.c
index 70217dac47..c296807974 100644
--- a/lib/x509/privkey_pkcs8_pbes1.c
+++ b/lib/x509/privkey_pkcs8_pbes1.c
@@ -161,8 +161,11 @@ _gnutls_decrypt_pbes1_des_md5_data(const char *password,
 	result =
 	    _gnutls_cipher_init(&ch, cipher_to_entry(GNUTLS_CIPHER_DES_CBC),
 				&dkey, &d_iv, 0);
-	if (result < 0)
+	if (result < 0) {
+		_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
 		return gnutls_assert_val(result);
+	}
+	_gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
 
 	result = _gnutls_cipher_decrypt(&ch, encrypted_data->data, encrypted_data->size);
 	if (result < 0) {
diff --git a/tests/fips-test.c b/tests/fips-test.c
index d72b5d2bce..32b224260a 100644
--- a/tests/fips-test.c
+++ b/tests/fips-test.c
@@ -9,11 +9,30 @@
 #include <gnutls/abstract.h>
 #include <gnutls/x509.h>
 
-void _gnutls_lib_simulate_error(void);
-
 /* This does check the FIPS140 support.
  */
 
+#define FIPS_PUSH_CONTEXT() do {				\
+	ret = gnutls_fips140_push_context(fips_context);	\
+	if (ret < 0) {						\
+		fail("gnutls_fips140_push_context failed\n");	\
+	}							\
+} while (0)
+
+#define FIPS_POP_CONTEXT(state) do {					\
+	ret = gnutls_fips140_pop_context();				\
+	if (ret < 0) {							\
+		fail("gnutls_fips140_context_pop failed\n");		\
+	}								\
+	fips_state = gnutls_fips140_get_operation_state(fips_context);	\
+	if (fips_state != GNUTLS_FIPS140_OP_ ## state) {		\
+		fail("operation state is not " # state " (%d)\n",	\
+		     fips_state);					\
+	}								\
+} while (0)
+
+void _gnutls_lib_simulate_error(void);
+
 static void tls_log_func(int level, const char *str)
 {
 	fprintf(stderr, "<%d>| %s", level, str);
@@ -21,6 +40,10 @@ static void tls_log_func(int level, const char *str)
 
 static uint8_t key16[16];
 static uint8_t iv16[16];
+uint8_t key_data[64];
+uint8_t iv_data[16];
+gnutls_fips140_context_t fips_context;
+gnutls_fips140_operation_state_t fips_state;
 
 static const gnutls_datum_t data = { .data = (unsigned char *)"foo", 3 };
 static const uint8_t rsa2342_sha1_sig_data[] = {
@@ -109,6 +132,131 @@ rsa_import_keypair(gnutls_privkey_t *privkey, gnutls_pubkey_t *pubkey,
 
 }
 
+static void
+test_aead_cipher_approved(gnutls_cipher_algorithm_t cipher,
+			  unsigned key_size)
+{
+	int ret;
+	gnutls_aead_cipher_hd_t h;
+	gnutls_datum_t key = { key_data, key_size };
+	gnutls_memset(key_data, 0, key_size);
+
+	FIPS_PUSH_CONTEXT();
+	ret = gnutls_aead_cipher_init(&h, cipher, &key);
+	if (ret < 0) {
+		fail("gnutls_aead_cipher_init failed %s for %s\n",
+		     gnutls_strerror(ret),
+		     gnutls_cipher_get_name(cipher));
+	}
+	gnutls_aead_cipher_deinit(h);
+	FIPS_POP_CONTEXT(APPROVED);
+}
+
+static void
+test_cipher_approved(gnutls_cipher_algorithm_t cipher,
+		     unsigned key_size, unsigned iv_size)
+{
+	int ret;
+	gnutls_cipher_hd_t h;
+	gnutls_datum_t key = { key_data, key_size };
+	gnutls_datum_t iv = { iv_data, iv_size };
+	gnutls_memset(key_data, 0, key_size);
+	gnutls_memset(iv_data, 0, iv_size);
+
+	FIPS_PUSH_CONTEXT();
+	ret = gnutls_cipher_init(&h, cipher, &key, &iv);
+	if (ret < 0) {
+		fail("gnutls_cipher_init failed for %s\n",
+		     gnutls_cipher_get_name(cipher));
+	}
+	gnutls_cipher_deinit(h);
+	FIPS_POP_CONTEXT(APPROVED);
+}
+
+static void
+test_cipher_allowed(gnutls_cipher_algorithm_t cipher,
+		    unsigned key_size, unsigned iv_size)
+{
+	int ret;
+	gnutls_cipher_hd_t h;
+	gnutls_datum_t key = { key_data, key_size };
+	gnutls_datum_t iv = { iv_data, iv_size };
+	gnutls_memset(key_data, 0, key_size);
+	gnutls_memset(iv_data, 0, iv_size);
+
+	FIPS_PUSH_CONTEXT();
+	ret = gnutls_cipher_init(&h, cipher, &key, &iv);
+	if (ret < 0) {
+		fail("gnutls_cipher_init failed for %s\n",
+		     gnutls_cipher_get_name(cipher));
+	}
+	gnutls_cipher_deinit(h);
+	FIPS_POP_CONTEXT(NOT_APPROVED);
+}
+
+static void
+test_cipher_disallowed(gnutls_cipher_algorithm_t cipher,
+		       unsigned key_size, unsigned iv_size)
+{
+	int ret;
+	gnutls_cipher_hd_t h;
+	gnutls_datum_t key = { key_data, key_size };
+	gnutls_datum_t iv = { iv_data, iv_size };
+	gnutls_memset(key_data, 0, key_size);
+	gnutls_memset(iv_data, 0, iv_size);
+
+	FIPS_PUSH_CONTEXT();
+	ret = gnutls_cipher_init(&h, cipher, &key, &iv);
+	if (ret != GNUTLS_E_UNWANTED_ALGORITHM) {
+		if (ret == 0)
+			gnutls_cipher_deinit(h);
+		fail("gnutls_cipher_init should have failed with"
+		     "GNUTLS_E_UNWANTED_ALGORITHM for %s\n",
+		     gnutls_cipher_get_name(cipher));
+	}
+	FIPS_POP_CONTEXT(ERROR);
+}
+
+static inline void
+test_ciphers(void)
+{
+	test_cipher_approved(GNUTLS_CIPHER_AES_128_CBC, 16, 16);
+	test_cipher_approved(GNUTLS_CIPHER_AES_192_CBC, 24, 16);
+	test_cipher_approved(GNUTLS_CIPHER_AES_256_CBC, 32, 16);
+	test_aead_cipher_approved(GNUTLS_CIPHER_AES_128_CCM, 16);
+	test_aead_cipher_approved(GNUTLS_CIPHER_AES_256_CCM, 32);
+	test_aead_cipher_approved(GNUTLS_CIPHER_AES_128_CCM_8, 16);
+	test_aead_cipher_approved(GNUTLS_CIPHER_AES_256_CCM_8, 32);
+	test_cipher_approved(GNUTLS_CIPHER_AES_128_CFB8, 16, 16);
+	test_cipher_approved(GNUTLS_CIPHER_AES_192_CFB8, 24, 16);
+	test_cipher_approved(GNUTLS_CIPHER_AES_256_CFB8, 32, 16);
+	test_cipher_allowed(GNUTLS_CIPHER_AES_128_GCM, 16, 12);
+	test_cipher_allowed(GNUTLS_CIPHER_AES_192_GCM, 24, 12);
+	test_cipher_allowed(GNUTLS_CIPHER_AES_256_GCM, 32, 12);
+	test_cipher_disallowed(GNUTLS_CIPHER_ARCFOUR_128, 16, 0);
+	test_cipher_disallowed(GNUTLS_CIPHER_ESTREAM_SALSA20_256, 32, 0);
+	test_cipher_disallowed(GNUTLS_CIPHER_SALSA20_256, 32, 8);
+	test_cipher_disallowed(GNUTLS_CIPHER_CHACHA20_32, 32, 16);
+	test_cipher_disallowed(GNUTLS_CIPHER_CHACHA20_64, 32, 16);
+	test_cipher_disallowed(GNUTLS_CIPHER_CAMELLIA_192_CBC, 24, 16);
+	test_cipher_disallowed(GNUTLS_CIPHER_CAMELLIA_128_CBC, 16, 16);
+	test_cipher_disallowed(GNUTLS_CIPHER_CHACHA20_POLY1305, 32, 12);
+	test_cipher_disallowed(GNUTLS_CIPHER_CAMELLIA_128_GCM, 16, 0);
+	test_cipher_disallowed(GNUTLS_CIPHER_CAMELLIA_256_GCM, 32, 12);
+	test_cipher_disallowed(GNUTLS_CIPHER_GOST28147_CPA_CFB, 32, 8);
+	test_cipher_disallowed(GNUTLS_CIPHER_GOST28147_CPB_CFB, 32, 8);
+	test_cipher_disallowed(GNUTLS_CIPHER_GOST28147_CPC_CFB, 32, 8);
+	test_cipher_disallowed(GNUTLS_CIPHER_AES_128_SIV, 32, 16);
+	test_cipher_disallowed(GNUTLS_CIPHER_AES_256_SIV, 64, 16);
+	test_cipher_disallowed(GNUTLS_CIPHER_GOST28147_TC26Z_CNT, 32, 8);
+	test_cipher_disallowed(GNUTLS_CIPHER_MAGMA_CTR_ACPKM, 32, 8);
+	test_cipher_disallowed(GNUTLS_CIPHER_KUZNYECHIK_CTR_ACPKM, 32, 16);
+	test_cipher_disallowed(GNUTLS_CIPHER_3DES_CBC, 24, 8);
+	test_cipher_disallowed(GNUTLS_CIPHER_DES_CBC, 8, 8);
+	test_cipher_disallowed(GNUTLS_CIPHER_ARCFOUR_40, 5, 0);
+	test_cipher_disallowed(GNUTLS_CIPHER_RC2_40_CBC, 5, 8);
+}
+
 void doit(void)
 {
 	int ret;
@@ -121,8 +269,6 @@ void doit(void)
 	gnutls_privkey_t privkey;
 	gnutls_datum_t key = { key16, sizeof(key16) };
 	gnutls_datum_t iv = { iv16, sizeof(iv16) };
-	gnutls_fips140_context_t fips_context;
-	gnutls_fips140_operation_state_t fips_state;
 	gnutls_datum_t signature;
 	unsigned int bits;
 	uint8_t hmac[64];
@@ -158,38 +304,23 @@ void doit(void)
 		fail("gnutls_fips140_pop_context succeeded while not pushed\n");
 	}
 
-#define FIPS_PUSH_CONTEXT() do {				\
-	ret = gnutls_fips140_push_context(fips_context);	\
-	if (ret < 0) {						\
-		fail("gnutls_fips140_push_context failed\n");	\
-	}							\
-} while (0)
-
-#define FIPS_POP_CONTEXT(state) do {					\
-	ret = gnutls_fips140_pop_context();				\
-	if (ret < 0) {							\
-		fail("gnutls_fips140_context_pop failed\n");		\
-	}								\
-	fips_state = gnutls_fips140_get_operation_state(fips_context);	\
-	if (fips_state != GNUTLS_FIPS140_OP_ ## state) {		\
-		fail("operation state is not " # state " (%d)\n",	\
-		     fips_state);					\
-	}								\
-} while (0)
-
 	/* Try crypto.h functionality */
-	ret =
-	    gnutls_cipher_init(&ch, GNUTLS_CIPHER_AES_128_CBC, &key, &iv);
+	test_ciphers();
+
+	FIPS_PUSH_CONTEXT();
+	ret = gnutls_cipher_init(&ch, GNUTLS_CIPHER_AES_128_CBC, &key, &iv);
 	if (ret < 0) {
 		fail("gnutls_cipher_init failed\n");
 	}
 	gnutls_cipher_deinit(ch);
+	FIPS_POP_CONTEXT(APPROVED);
 
-	ret =
-	    gnutls_cipher_init(&ch, GNUTLS_CIPHER_ARCFOUR_128, &key, &iv);
+	FIPS_PUSH_CONTEXT();
+	ret = gnutls_cipher_init(&ch, GNUTLS_CIPHER_ARCFOUR_128, &key, &iv);
 	if (ret != GNUTLS_E_UNWANTED_ALGORITHM) {
 		fail("gnutls_cipher_init succeeded for arcfour\n");
 	}
+	FIPS_POP_CONTEXT(ERROR);
 
 	ret = gnutls_hmac_init(&mh, GNUTLS_MAC_SHA1, key.data, key.size);
 	if (ret < 0) {
@@ -342,6 +473,14 @@ void doit(void)
 	gnutls_pubkey_deinit(pubkey);
 	gnutls_privkey_deinit(privkey);
 
+        /* Test RND functions */
+	FIPS_PUSH_CONTEXT();
+	ret = gnutls_rnd(GNUTLS_RND_RANDOM, key16, sizeof(key16));
+	if (ret < 0) {
+		fail("gnutls_rnd failed\n");
+	}
+	FIPS_POP_CONTEXT(APPROVED);
+
 	/* Test when FIPS140 is set to error state */
 	_gnutls_lib_simulate_error();
 
-- 
GitLab

openSUSE Build Service is sponsored by