File 0004-dh-perform-SP800-56A-rev3-full-pubkey-validation-on-.patch of Package gnutls.18749

From 8b575625614fbe5a22b68dc8d1877efb1d44dd37 Mon Sep 17 00:00:00 2001
From: Daiki Ueno <ueno@gnu.org>
Date: Fri, 17 Jul 2020 17:47:06 +0200
Subject: [PATCH 4/5] dh: perform SP800-56A rev3 full pubkey validation on
 keygen

This implements full public key validation required in SP800-56A rev3,
section 5.6.2.3.1.

Signed-off-by: Daiki Ueno <ueno@gnu.org>
---
 lib/nettle/pk.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 90 insertions(+)

Index: gnutls-3.6.7/lib/nettle/pk.c
===================================================================
--- gnutls-3.6.7.orig/lib/nettle/pk.c	2020-09-03 14:20:20.205198306 +0200
+++ gnutls-3.6.7/lib/nettle/pk.c	2020-09-03 14:20:23.057216249 +0200
@@ -56,6 +56,7 @@
 #endif
 #include <gnettle.h>
 #include <fips.h>
+#include "dh.h"
 
 static inline const struct ecc_curve *get_supported_nist_curve(int curve);
 static inline const struct ecc_curve *get_supported_gost_curve(int curve);
@@ -1856,6 +1857,54 @@ cleanup:
 }
 #endif
 
+static inline int
+dh_find_q(const gnutls_pk_params_st *pk_params, mpz_t q)
+{
+	gnutls_datum_t prime = { NULL, 0 };
+	gnutls_datum_t generator = { NULL, 0 };
+	uint8_t *data_q;
+	size_t n_q;
+	bigint_t _q;
+	int ret = 0;
+
+	ret = _gnutls_mpi_dprint(pk_params->params[DSA_P], &prime);
+	if (ret < 0) {
+		gnutls_assert();
+		goto cleanup;
+	}
+
+	ret = _gnutls_mpi_dprint(pk_params->params[DSA_G], &generator);
+	if (ret < 0) {
+		gnutls_assert();
+		goto cleanup;
+	}
+
+	if (!_gnutls_dh_prime_match_fips_approved(prime.data,
+						  prime.size,
+						  generator.data,
+						  generator.size,
+						  &data_q,
+						  &n_q)) {
+		ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+		goto cleanup;
+	}
+
+	if (_gnutls_mpi_init_scan_nz(&_q, data_q, n_q) != 0) {
+		ret = gnutls_assert_val(GNUTLS_E_MPI_SCAN_FAILED);
+		goto cleanup;
+	}
+
+	mpz_set(q, TOMPZ(_q));
+	_gnutls_mpi_release(&_q);
+
+ cleanup:
+	gnutls_free(prime.data);
+	gnutls_free(generator.data);
+
+	return ret;
+}
+
+
 /* To generate a DH key either q must be set in the params or
  * level should be set to the number of required bits.
  */
@@ -1937,6 +1986,9 @@ wrap_nettle_pk_generate_keys(gnutls_pk_a
 			mpz_t x, y;
 			int max_tries;
 			unsigned have_q = 0;
+			mpz_t q;
+			mpz_t primesub1;
+			mpz_t ypowq;
 
 			if (algo != params->algo)
 				return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
@@ -1954,6 +2006,10 @@ wrap_nettle_pk_generate_keys(gnutls_pk_a
 			mpz_init(x);
 			mpz_init(y);
 
+			mpz_init(q);
+			mpz_init(primesub1);
+			mpz_init(ypowq);
+
 			max_tries = 3;
 			do {
 				if (have_q) {
@@ -1985,8 +2041,40 @@ wrap_nettle_pk_generate_keys(gnutls_pk_a
 					ret = GNUTLS_E_LIB_IN_ERROR_STATE;
 					goto dh_fail;
 				}
+
 			} while(mpz_cmp_ui(y, 1) == 0);
 
+#ifdef ENABLE_FIPS140
+			if (_gnutls_fips_mode_enabled()) {
+				/* Perform FFC full public key validation checks
+				 * according to SP800-56A (revision 3), 5.6.2.3.1.
+				 */
+
+				/* Step 1: 2 <= y <= p - 2 */
+				mpz_sub_ui(primesub1, pub.p, 1);
+
+				if (mpz_cmp_ui(y, 2) < 0 || mpz_cmp(y, primesub1) >= 0) {
+					ret = gnutls_assert_val(GNUTLS_E_RANDOM_FAILED);
+					goto dh_fail;
+				}
+
+				/* Step 2: 1 = y^q mod p */
+				if (have_q)
+					mpz_set(q, pub.q);
+				else {
+					ret = dh_find_q(params, q);
+					if (ret < 0)
+						goto dh_fail;
+				}
+
+				mpz_powm(ypowq, y, q, pub.p);
+				if (mpz_cmp_ui(ypowq, 1) != 0) {
+					ret = gnutls_assert_val(GNUTLS_E_RANDOM_FAILED);
+					goto dh_fail;
+				}
+			}
+#endif
+
 			ret = _gnutls_mpi_init_multi(&params->params[DSA_Y], &params->params[DSA_X], NULL);
 			if (ret < 0) {
 				gnutls_assert();
@@ -2003,6 +2091,9 @@ wrap_nettle_pk_generate_keys(gnutls_pk_a
 			mpz_clear(r);
 			mpz_clear(x);
 			mpz_clear(y);
+			mpz_clear(q);
+			mpz_clear(primesub1);
+			mpz_clear(ypowq);
 
 			if (ret < 0)
 				goto fail;
openSUSE Build Service is sponsored by