File 0004-dh-perform-SP800-56A-rev3-full-pubkey-validation-on-.patch of Package gnutls.31638
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(¶ms->params[DSA_Y], ¶ms->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;