Hi NIIBE,

thank you for your proposal and your opinions on the matter.

I mostly share the same opinions as Stephan.

Could we ask you to reserve this decision, at this point?  I can imagine
the need for a thread local variable for the service indicator, but I
think that overloading ERRNO might be difficult to handle and/or
confusing for applications (library users).
I wouldn't say that it is difficult to handle but its definitely a little bit confusing as one would normally expect an errno to be set in conjunction with a error code and not success, i.e., by following this path one has to properly document the behavior. I also wouldn't say that errno is overloaded because at the point errno is set for FIPS, other errno values related to errors should have already been handled internally.

Well, may I ask for your patch also including a change of tests/t-kdf.c?
By doing so, it will be more clear to see how the change of libgcrypt
asks application changes.
Sure, I've attached a diff.

Best regards

David

On 24.10.24 11:08, Stephan Mueller wrote:
Am Donnerstag, 24. Oktober 2024, 03:34:31 Mitteleuropäische Sommerzeit schrieb
NIIBE Yutaka via Gcrypt-devel:

Hi NIIBE,

Hello,

Thank you for your proposal with a patch.

David Sugar wrote:
Libgcrypt implements a FIPS 140 service indicator to comply with the rules
stipulated by FIPS 140-3. However, based on recent clarifications provided
by NIST around the topic, the current implementation falls short of
meeting these clarifications.

The current service indicator "statically" returns the indicator whether
an
algorithm operates in a FIPS-compliant manner or not. This includes the
indicator whether the cryptographic algorithm is an "approved" algorithm
or
not.

The issue with this approach is that it is "static" in the sense that a
caller asks libgcrypt whether algorithm X is approved or not. NIST
clarified that such static behavior is not sufficient. NIST requests a
"dynamic" indicator in the sense that during the processing of the actual
request to perform a cryptographic operation, the indicator must be
"generated" and "returned" to the caller. I.e. when the caller performs,
say an RSA 1024 operation, that very API call is required to generate the
indicator that the request was not FIPS compliant. Conversely, when the
caller performs an RSA 2048 operation, that very API call must generate
the indicator that it was an approved mechanism.
I see the situation.

Let us improve.  (Well, I don't have a skill for the better
wording/expression in English.  I use "us", meaning: gpg-team + you +
all, including its users.)

Our solution we offer here is the use of the errno to convey the FIPS
status indicator.
Could we ask you to reserve this decision, at this point?  I can imagine
the need for a thread local variable for the service indicator, but I
think that overloading ERRNO might be difficult to handle and/or
confusing for applications (library users).

For this, firstly, I propose the API of following:

        void _gcry_thread_context_set_fsi (unsigned long fsi);
        unsigned long _gcry_thread_context_get_fsi (void);
        unsigned long gcry_thread_context_get_fsi (void);

(Function name change/suggestion is welcome, here, fsi stands for FIPS
service indicator.)

Two functions are internal use for libgcrypt.  The last function is
exposed to libgcrypt users.

Implementation may be the one with ERRNO actually later, but for now,
let us hide and defer the implementation detail.

It's true that libgcrypt code-base doesn't yet have thread specific
context things; That's long-standing issue to be done.  Since libgcrypt
assumes C90+something (only) for build environment and tries to support
various OS and compilers, it would be tricky to implement.  For this
particular problem, I created a separate ticket (and a branch
gniibe/t7340):

        https://dev.gnupg.org/T7340

# So far, I found that use of pre-C99 non-standard __thread specifier
# seems work well.  Any feedback is welcome for this.
Thanks very much for sharing this suggestion. I see no issue with it from an
API / FIPS point of view.

But since I am also maintaining a crypto lib (leancrypto), I am intrigued to
learn how such thread-local variables can be maintained. I think I have to
learn about the __thread specifier :-)
The provided patch adjusts one cryptographic service to show the chosen
approach. If we all agree, we will proceed in developing the patch series
to cover the other services appropriately.
Thanks a lot for this constructive approach.

- if the API return code is 0 -> errno contains the FIPS service indicator

- if the API return code is != 0 -> errno contains the "regular" error
indicator (i.e. the patch will not touch the errno)
Currently, I don't know if this is good.  My concern here is that this
will be a fundamental API change for libgcrypt users; I'm afraid it
requires changes for all/most applications.
Ok, but I do not see it this way, because the API (disregarding the errno in
the successful conclusion of the API) is unchanged. Only if the API concludes
successfully *and* a caller is interested in FIPS, it may want to consult th
errno. In a success case, usually no caller would consult the errno.
In my opinion, two points are important.

* A function may be finished earlier, not completing the operation.
   In this case, the API return code should not be 0 (keeping the API).
That case should not be affected by the suggestion, because the API returns
with a GPG error indicator.
* Introducing use of gcry_thread_context_get_fsi (after a function call)
   makes sense for FIPS certified applications to make sure if the
   function call of libgcrypt does not violate FIPS (new API).
Ok sure, as said above, no issue from our side.
                        *       *       *

Well, may I ask for your patch also including a change of tests/t-kdf.c?
By doing so, it will be more clear to see how the change of libgcrypt
asks application changes.
I would like to ask my colleague, David, for that.


Ciao
Stephan


--
atsec information security GmbH, Ismaninger Str. 19, 81675 München, Germany
Phone: +49-89-44249840  /  Web: atsec.com
HRB: 129439 (Amtsgericht München)
Geschaeftsfuehrer: Staffan Persson, Dr. Michael Vogel

--- libgcrypt/tests/t-kdf.c     2024-10-10 11:13:04.000000000 +0200
+++ libgcrypt-fips2/tests/t-kdf.c       2024-10-14 13:11:57.305374486 +0200
@@ -26,6 +26,7 @@
 #include <string.h>
 #include <stdarg.h>
 #include <assert.h>
+#include <errno.h>
 
 #include "stopwatch.h"
 #define PGM "t-kdf"
@@ -1102,6 +1103,7 @@
   gpg_error_t err;
   unsigned char outbuf[100];
   int i;
+  int my_errno;
 
   for (tvidx=0; tvidx < DIM(tv); tvidx++)
     {
@@ -1117,14 +1119,15 @@
                              GCRY_KDF_PBKDF2, tv[tvidx].hashalgo,
                              tv[tvidx].salt, tv[tvidx].saltlen,
                              tv[tvidx].c, tv[tvidx].dklen, outbuf);
+      my_errno = errno;
       if (in_fips_mode && tvidx > 7)
         {
-          if (!err)
+          if (my_errno != EOPNOTSUPP)
             fail ("pbkdf2 test %d unexpectedly passed in FIPS mode: %s\n",
                   tvidx, gpg_strerror (err));
           continue;
         }
-      if (err)
+      if (err || my_errno == EOPNOTSUPP)
         {
           if (in_fips_mode && (tv[tvidx].plen < 14 || tv[tvidx].dklen < 14))
             {
@@ -1926,6 +1929,145 @@
     }
 }
 
+static void
+check_fips_gcry_kdf_derive(void)
+{
+  static struct {
+    const char *p;   /* Passphrase.  */
+    size_t plen;     /* Length of P. */
+    int algo;
+    int subalgo;
+    const char *salt;
+    size_t saltlen;
+    unsigned long iterations;
+    int dklen;       /* Requested key length.  */
+    const char *dk;  /* Derived key.  */
+    int expect_error;
+  } tv[] = {
+    {
+      "passwordPASSWORDpassword", 24,
+      GCRY_KDF_PBKDF2, GCRY_MD_SHA1,
+      "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36,
+      4096,
+      25,
+      "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8"
+      "\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96"
+      "\x4c\xf2\xf0\x70\x38",
+      0
+    },
+    {
+      "pleaseletmein", 13,
+      GCRY_KDF_SCRYPT, 8,
+      "SodiumChloride", 14,
+      1,
+      64,
+      "\x70\x23\xbd\xcb\x3a\xfd\x73\x48\x46\x1c\x06\xcd\x81\xfd\x38\xeb"
+      "\xfd\xa8\xfb\xba\x90\x4f\x8e\x3e\xa9\xb5\x43\xf6\x54\x5d\xa1\xf2"
+      "\xd5\x43\x29\x55\x61\x3f\x0f\xcf\x62\xd4\x97\x05\x24\x2a\x9a\xf9"
+      "\xe6\x1e\x85\xdc\x0d\x65\x1e\x40\xdf\xcf\x01\x7b\x45\x57\x58\x87",
+      1 /* forbidden because unallowed algo */
+    },
+    {
+      "passwor", 7,
+      GCRY_KDF_PBKDF2, GCRY_MD_SHA1,
+      "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36,
+      4096,
+      25,
+      "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8"
+      "\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96"
+      "\x4c\xf2\xf0\x70\x38", /* this is wrong but we don't care because
+       it should fail anyway */
+      1 /* forbidden because passphrase len is too small */
+    },
+    {
+      "passwordPASSWORDpassword", 24,
+      GCRY_KDF_PBKDF2, GCRY_MD_SHA1,
+      "saltSALTsaltSAL", 15,
+      4096,
+      25,
+      "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8"
+      "\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96"
+      "\x4c\xf2\xf0\x70\x38", /* this is wrong but we don't care because
+       it should fail anyway */
+      1 /* forbidden because salt len is too small */
+    },
+    {
+      "passwordPASSWORDpassword", 24,
+      GCRY_KDF_PBKDF2, GCRY_MD_SHA1,
+      "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36,
+      999,
+      25,
+      "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8"
+      "\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96"
+      "\x4c\xf2\xf0\x70\x38", /* this is wrong but we don't care because
+       it should fail anyway */
+      1 /* forbidden because too few iterations */
+    },
+    {
+      "passwordPASSWORDpassword", 24,
+      GCRY_KDF_PBKDF2, GCRY_MD_SHA1,
+      "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36,
+      4096,
+      13,
+      "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8"
+      "\xd8\x36\x62", /* this is wrong but we don't care because
+       it should fail anyway */
+      1 /* forbidden because key size too small */
+    },
+  };
+
+  int tvidx;
+  gpg_error_t err;
+  unsigned char outbuf[100];
+  int i;
+  int my_errno;
+
+  for (tvidx=0; tvidx < DIM(tv); tvidx++)
+    {
+      if (verbose)
+        fprintf (stderr, "checking gcry_kdf_derive test vector %d algo %d for 
fips\n",
+            tvidx, tv[tvidx].algo);
+      assert (tv[tvidx].dklen <= sizeof outbuf);
+      err = gcry_kdf_derive (tv[tvidx].p, tv[tvidx].plen,
+                             tv[tvidx].algo, tv[tvidx].subalgo,
+                             tv[tvidx].salt, tv[tvidx].saltlen,
+                             tv[tvidx].iterations, tv[tvidx].dklen, outbuf);
+      // Errno has to be fetched directly after the call
+      my_errno = errno;
+
+      if (err)
+        {
+          fail ("gcry_kdf_derive test %d unexpectedly returned an error in 
FIPS mode: %s\n",
+              tvidx, gpg_strerror (err));
+          continue;
+        }
+      else
+        {
+          if (my_errno == EOPNOTSUPP && tv[tvidx].expect_error == 0)
+            {
+
+              fail ("gcry_kdf_derive test %d unexpectedly set errno '%s' in 
FIPS mode.\n",
+                  tvidx, strerror(my_errno), strerror(EOPNOTSUPP));
+              continue;
+            }
+          else if (my_errno != EOPNOTSUPP && tv[tvidx].expect_error)
+            {
+              fail ("gcry_kdf_derive test %d expected EOPNOTSUPP in FIPS 
mode\n",
+                  tvidx);
+              continue;
+            }
+
+          if (my_errno == 0 && memcmp (outbuf, tv[tvidx].dk, tv[tvidx].dklen))
+            {
+              fail ("gcry_kdf_derive test %d failed: mismatch\n", tvidx);
+              fputs ("got:", stderr);
+              for (i=0; i < tv[tvidx].dklen; i++)
+                fprintf (stderr, " %02x", outbuf[i]);
+              putc ('\n', stderr);
+            }
+        }
+    }
+}
 
 int
 main (int argc, char **argv)
@@ -2009,6 +2151,8 @@
       check_hkdf ();
       if (in_fips_mode)
         check_fips_indicators();
+      if (in_fips_mode)
+        check_fips_gcry_kdf_derive();
     }
 
   return error_count ? 1 : 0;

Attachment: OpenPGP_signature.asc
Description: OpenPGP digital signature

_______________________________________________
Gcrypt-devel mailing list
Gcrypt-devel@gnupg.org
https://lists.gnupg.org/mailman/listinfo/gcrypt-devel

Reply via email to