File libgcrypt-FIPS-SLI-Implement-FIPS-service-indicator-for-gcry_pk_hash_API.patch of Package libgcrypt.38414

From edb43bc290046bd22548bf69ae2fbeb453112e44 Mon Sep 17 00:00:00 2001
From: NIIBE Yutaka <gniibe@fsij.org>
Date: Wed, 18 Dec 2024 14:18:26 +0900
Subject: [PATCH 21/24] fips,cipher: Implement FIPS service indicator for
 gcry_pk_hash_ API.

* src/visibility.c (gcry_pk_hash_sign): Initialize the indicator.
(gcry_pk_hash_verify): Likewise.
* tests/t-fips-service-ind.c (check_pk_hash_sign_verify): New.
(main): Call check_pk_hash_sign_verify.
* cipher/ecc-curves.c (_gcry_ecc_fill_in_curve): Don't reject, but
mark non-compliance.
* cipher/pubkey.c (prepare_datasexp_to_be_signed): Likewise.
(_gcry_pk_sign_md, _gcry_pk_verify_md): Likewise.

--

GnuPG-bug-id: 7338
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
Signed-off-by: Lucas Mulling <lucas.mulling@suse.com>
---
 cipher/ecc-curves.c        |   2 +-
 cipher/pubkey.c            |  20 ++--
 src/visibility.c           |   2 +
 tests/t-fips-service-ind.c | 209 +++++++++++++++++++++++++++++++++++++
 4 files changed, 222 insertions(+), 11 deletions(-)

diff --git a/cipher/ecc-curves.c b/cipher/ecc-curves.c
index 17fa5505..ddf9cbe1 100644
--- a/cipher/ecc-curves.c
+++ b/cipher/ecc-curves.c
@@ -645,7 +645,7 @@ _gcry_ecc_fill_in_curve (unsigned int nbits, const char *name,
      possible to bypass this check by specifying the curve parameters
      directly.  */
   if (fips_mode () && !domain_parms[idx].fips )
-    return GPG_ERR_NOT_SUPPORTED;
+    fips_service_indicator_mark_non_compliant ();
 
   switch (domain_parms[idx].model)
     {
diff --git a/cipher/pubkey.c b/cipher/pubkey.c
index 214bd611..e2e54199 100644
--- a/cipher/pubkey.c
+++ b/cipher/pubkey.c
@@ -510,10 +510,7 @@ prepare_datasexp_to_be_signed (const char *tmpl, gcry_md_hd_t hd,
       algo = _gcry_md_get_algo (hd);
 
       if (fips_mode () && algo == GCRY_MD_SHA1)
-        {
-          _gcry_md_close (hd);
-          return GPG_ERR_DIGEST_ALGO;
-        }
+        fips_service_indicator_mark_non_compliant ();
 
       digest_name = _gcry_md_algo_name (algo);
       digest_size = (int)_gcry_md_get_algo_dlen (algo);
@@ -535,12 +532,13 @@ prepare_datasexp_to_be_signed (const char *tmpl, gcry_md_hd_t hd,
 
       algo = _gcry_md_map_name (digest_name_supplied);
       xfree (digest_name_supplied);
-      if (algo == 0
-          || (fips_mode () && algo == GCRY_MD_SHA1))
+      if (algo == 0)
 	{
 	  _gcry_md_close (hd);
 	  return GPG_ERR_DIGEST_ALGO;
 	}
+      else if (fips_mode () && algo == GCRY_MD_SHA1)
+        fips_service_indicator_mark_non_compliant ();
 
       digest_size = (int)_gcry_md_get_algo_dlen (algo);
       digest = _gcry_md_read (hd, algo);
@@ -613,10 +611,11 @@ _gcry_pk_sign_md (gcry_sexp_t *r_sig, const char *tmpl, gcry_md_hd_t hd_orig,
   if (rc)
     goto leave;
 
+  if (!spec->flags.fips && fips_mode ())
+    fips_service_indicator_mark_non_compliant ();
+
   if (spec->flags.disabled)
     rc = GPG_ERR_PUBKEY_ALGO;
-  else if (!spec->flags.fips && fips_mode ())
-    rc = GPG_ERR_PUBKEY_ALGO;
   else if (spec->sign)
     rc = spec->sign (r_sig, s_data, keyparms);
   else
@@ -690,10 +689,11 @@ _gcry_pk_verify_md (gcry_sexp_t s_sig, const char *tmpl, gcry_md_hd_t hd_orig,
   if (rc)
     goto leave;
 
+  if (!spec->flags.fips && fips_mode ())
+    fips_service_indicator_mark_non_compliant ();
+
   if (spec->flags.disabled)
     rc = GPG_ERR_PUBKEY_ALGO;
-  else if (!spec->flags.fips && fips_mode ())
-    rc = GPG_ERR_PUBKEY_ALGO;
   else if (spec->verify)
     rc = spec->verify (s_sig, s_data, keyparms);
   else
diff --git a/src/visibility.c b/src/visibility.c
index c9d07f0b..d22c8b59 100644
--- a/src/visibility.c
+++ b/src/visibility.c
@@ -1056,6 +1056,7 @@ gcry_pk_hash_sign (gcry_sexp_t *result, const char *data_tmpl, gcry_sexp_t skey,
       *result = NULL;
       return gpg_error (fips_not_operational ());
     }
+  fips_service_indicator_init ();
   return gpg_error (_gcry_pk_sign_md (result, data_tmpl, hd, skey, ctx));
 }
 
@@ -1073,6 +1074,7 @@ gcry_pk_hash_verify (gcry_sexp_t sigval, const char *data_tmpl, gcry_sexp_t pkey
 {
   if (!fips_is_operational ())
     return gpg_error (fips_not_operational ());
+  fips_service_indicator_init ();
   return gpg_error (_gcry_pk_verify_md (sigval, data_tmpl, hd, pkey, ctx));
 }
 
diff --git a/tests/t-fips-service-ind.c b/tests/t-fips-service-ind.c
index 4b13436f..9a22d696 100644
--- a/tests/t-fips-service-ind.c
+++ b/tests/t-fips-service-ind.c
@@ -29,6 +29,7 @@
 
 #define PGM "t-fips-service-ind"
 
+#define NEED_HEX2BUFFER
 #include "t-common.h"
 static int in_fips_mode;
 #define MAX_DATA_LEN 1040
@@ -39,6 +40,213 @@ static int in_fips_mode;
 # include <windows.h>
 #endif
 
+/* Check gcry_pk_hash_sign, gcry_pk_hash_verify API.  */
+static void
+check_pk_hash_sign_verify (void)
+{
+  static struct {
+    int md_algo;
+    const char *prvkey;
+    const char *pubkey;
+    const char *data_tmpl;
+    const char *k;
+    int expect_failure;
+    int expect_failure_hash;
+  } tv[] = {
+    {                           /* non-compliant hash */
+      GCRY_MD_BLAKE2B_512,
+      "(private-key (ecc (curve nistp256)"
+      " (d #519b423d715f8b581f4fa8ee59f4771a5b44c8130b4e3eacca54a56dda72b464#)))",
+      "(public-key (ecc (curve nistp256)"
+      " (q #041ccbe91c075fc7f4f033bfa248db8fccd3565de94bbfb12f3c59ff46c271bf83"
+      "ce4014c68811f9a21a1fdb2c0e6113e06db7ca93b7404e78dc7ccd5ca89a4ca9#)))",
+      "(data(flags raw)(hash %s %b)(label %b))",
+      "94a1bbb14b906a61a280f245f9e93c7f3b4a6247824f5d33b9670787642a68de",
+      1, 1
+    },
+    {                           /* non-compliant curve */
+      GCRY_MD_SHA256,
+      "(private-key (ecc (curve secp256k1)"
+      " (d #c2cdf0a8b0a83b35ace53f097b5e6e6a0a1f2d40535eff1cf434f52a43d59d8f#)))",
+
+      "(public-key (ecc (curve secp256k1)"
+      " (q #046fcc37ea5e9e09fec6c83e5fbd7a745e3eee81d16ebd861c9e66f55518c19798"
+      "4e9f113c07f875691df8afc1029496fc4cb9509b39dcd38f251a83359cc8b4f7#)))",
+      "(data(flags raw)(hash %s %b)(label %b))",
+      "94a1bbb14b906a61a280f245f9e93c7f3b4a6247824f5d33b9670787642a68de",
+      1, 0
+    },
+    {
+      GCRY_MD_SHA256,
+      "(private-key (ecc (curve nistp256)"
+      " (d #519b423d715f8b581f4fa8ee59f4771a5b44c8130b4e3eacca54a56dda72b464#)))",
+      "(public-key (ecc (curve nistp256)"
+      " (q #041ccbe91c075fc7f4f033bfa248db8fccd3565de94bbfb12f3c59ff46c271bf83"
+      "ce4014c68811f9a21a1fdb2c0e6113e06db7ca93b7404e78dc7ccd5ca89a4ca9#)))",
+      "(data(flags raw)(hash %s %b)(label %b))",
+      "94a1bbb14b906a61a280f245f9e93c7f3b4a6247824f5d33b9670787642a68de",
+      0, 0
+    }
+  };
+  int tvidx;
+  gpg_error_t err;
+  gpg_err_code_t ec;
+  const char *msg = "Takerufuji Mikiya, who won the championship in March 2024";
+  int msglen;
+
+  msglen = strlen (msg);
+  for (tvidx=0; tvidx < DIM(tv); tvidx++)
+    {
+      gcry_md_hd_t hd = NULL;
+      gcry_sexp_t s_sk = NULL;
+      gcry_sexp_t s_pk = NULL;
+      void *buffer = NULL;
+      size_t buflen;
+      gcry_ctx_t ctx = NULL;
+      gcry_sexp_t s_sig= NULL;
+
+      if (verbose)
+        info ("checking gcry_pk_hash_ test %d\n", tvidx);
+
+      err = gcry_md_open (&hd, tv[tvidx].md_algo, 0);
+      if (err)
+        {
+          fail ("algo %d, gcry_md_open failed: %s\n", tv[tvidx].md_algo,
+                gpg_strerror (err));
+          goto next;
+        }
+
+      ec = gcry_get_fips_service_indicator ();
+      if (ec == GPG_ERR_INV_OP)
+        {
+          /* libgcrypt is old, no support of the FIPS service indicator.  */
+          fail ("gcry_pk_hash test %d unexpectedly failed to check the FIPS service indicator.\n",
+                tvidx);
+          goto next;
+        }
+
+      if (in_fips_mode && !tv[tvidx].expect_failure_hash && ec)
+        {
+          /* Success with the FIPS service indicator == 0 expected, but != 0.  */
+          fail ("gcry_pk_hash test %d unexpectedly set the indicator in FIPS mode.\n",
+                tvidx);
+          goto next;
+        }
+      else if (in_fips_mode && tv[tvidx].expect_failure_hash && !ec)
+        {
+          /* Success with the FIPS service indicator != 0 expected, but == 0.  */
+          fail ("gcry_pk_hash test %d unexpectedly cleared the indicator in FIPS mode.\n",
+                tvidx);
+          goto next;
+        }
+
+      err = gcry_sexp_build (&s_sk, NULL, tv[tvidx].prvkey);
+      if (err)
+        {
+          fail ("error building SEXP for test, %s: %s",
+                "sk", gpg_strerror (err));
+          goto next;
+        }
+
+      err = gcry_sexp_build (&s_pk, NULL, tv[tvidx].pubkey);
+      if (err)
+        {
+          fail ("error building SEXP for test, %s: %s",
+                "pk", gpg_strerror (err));
+          goto next;
+        }
+
+      if (!(buffer = hex2buffer (tv[tvidx].k, &buflen)))
+        {
+          fail ("error parsing for test, %s: %s",
+                "msg", "invalid hex string");
+          goto next;
+        }
+
+      err = gcry_pk_random_override_new (&ctx, buffer, buflen);
+      if (err)
+        {
+          fail ("error setting 'k' for test: %s",
+                gpg_strerror (err));
+          goto next;
+        }
+
+      gcry_md_write (hd, msg, msglen);
+
+      err = gcry_pk_hash_sign (&s_sig, tv[tvidx].data_tmpl, s_sk, hd, ctx);
+      if (err)
+        {
+          fail ("gcry_pk_hash_sign failed: %s", gpg_strerror (err));
+          goto next;
+        }
+
+      ec = gcry_get_fips_service_indicator ();
+      if (ec == GPG_ERR_INV_OP)
+        {
+          /* libgcrypt is old, no support of the FIPS service indicator.  */
+          fail ("gcry_pk_hash test %d unexpectedly failed to check the FIPS service indicator.\n",
+                tvidx);
+          goto next;
+        }
+
+      if (in_fips_mode && !tv[tvidx].expect_failure && ec)
+        {
+          /* Success with the FIPS service indicator == 0 expected, but != 0.  */
+          fail ("gcry_pk_hash test %d unexpectedly set the indicator in FIPS mode.\n",
+                tvidx);
+          goto next;
+        }
+      else if (in_fips_mode && tv[tvidx].expect_failure && !ec)
+        {
+          /* Success with the FIPS service indicator != 0 expected, but == 0.  */
+          fail ("gcry_pk_hash_sign test %d unexpectedly cleared the indicator in FIPS mode.\n",
+                tvidx);
+          goto next;
+        }
+
+      err = gcry_pk_hash_verify (s_sig, tv[tvidx].data_tmpl, s_pk, hd, ctx);
+      if (err)
+        {
+          fail ("gcry_pk_hash_verify failed for test: %s",
+                gpg_strerror (err));
+          goto next;
+        }
+
+      ec = gcry_get_fips_service_indicator ();
+      if (ec == GPG_ERR_INV_OP)
+        {
+          /* libgcrypt is old, no support of the FIPS service indicator.  */
+          fail ("gcry_pk_hash test %d unexpectedly failed to check the FIPS service indicator.\n",
+                tvidx);
+          goto next;
+        }
+
+      if (in_fips_mode && !tv[tvidx].expect_failure && ec)
+        {
+          /* Success with the FIPS service indicator == 0 expected, but != 0.  */
+          fail ("gcry_pk_hash test %d unexpectedly set the indicator in FIPS mode.\n",
+                tvidx);
+          goto next;
+        }
+      else if (in_fips_mode && tv[tvidx].expect_failure && !ec)
+        {
+          /* Success with the FIPS service indicator != 0 expected, but == 0.  */
+          fail ("gcry_pk_hash_verify test %d unexpectedly cleared the indicator in FIPS mode.\n",
+                tvidx);
+          goto next;
+        }
+
+    next:
+      gcry_sexp_release (s_sig);
+      xfree (buffer);
+      gcry_ctx_release (ctx);
+      gcry_sexp_release (s_pk);
+      gcry_sexp_release (s_sk);
+      if (hd)
+        gcry_md_close (hd);
+    }
+}
+
 /* Check gcry_cipher_open, gcry_cipher_setkey, gcry_cipher_encrypt,
    gcry_cipher_decrypt, gcry_cipher_close API.  */
 static void
@@ -936,6 +1144,7 @@ main (int argc, char **argv)
   check_md_o_w_r_c ();
   check_mac_o_w_r_c ();
   check_cipher_o_s_e_d_c ();
+  check_pk_hash_sign_verify ();
 
   return !!error_count;
 }
-- 
2.47.1

openSUSE Build Service is sponsored by