bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] crypto/gc-pbkdf2: New module.


From: Simon Josefsson
Subject: [PATCH] crypto/gc-pbkdf2: New module.
Date: Wed, 15 Jan 2020 23:37:48 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/26.1 (gnu/linux)

The gc-pbkdf2-sha1 module is hard-coded to SHA1, this patch deprecates
it in favor of the gc-pbkdf2 module that provide an interface that
accepts a gc_hash parameter instead.

/Simon

* MODULES.html.sh (func_all_modules): Add gc-pbkdf2.
* NEWS: Deprecated gc-pbkdf2-sha1 in favor of gc-pbkdf2.
* lib/gc-pbkdf2.c: New file.
* lib/gc-pbkdf2-sha1.c: Use new interface.
* lib/gc.h (GC_MAX_DIGEST_SIZE, gc_pbkdf2_hmac): Add.
* modules/crypto/gc-pbkdf2: New file.
* modules/crypto/gc-pbkdf2-tests: New file.
* tests/test-gc-pbkdf2.c: New file.
---
 ChangeLog                      |  12 +++
 MODULES.html.sh                |   1 +
 NEWS                           |   2 +
 lib/gc-pbkdf2-sha1.c           |  71 +----------------
 lib/gc-pbkdf2.c                | 138 +++++++++++++++++++++++++++++++++
 lib/gc.h                       |  23 ++++--
 modules/crypto/gc-pbkdf2       |  22 ++++++
 modules/crypto/gc-pbkdf2-sha1  |  10 ++-
 modules/crypto/gc-pbkdf2-tests |  11 +++
 tests/test-gc-pbkdf2.c         | 131 +++++++++++++++++++++++++++++++
 10 files changed, 342 insertions(+), 79 deletions(-)
 create mode 100644 lib/gc-pbkdf2.c
 create mode 100644 modules/crypto/gc-pbkdf2
 create mode 100644 modules/crypto/gc-pbkdf2-tests
 create mode 100644 tests/test-gc-pbkdf2.c

diff --git a/ChangeLog b/ChangeLog
index 245f2a7c1..fc48c3960 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2020-01-15  Simon Josefsson  <address@hidden>
+
+       crypto/gc-pbkdf2: New module.
+       * MODULES.html.sh (func_all_modules): Add gc-pbkdf2.
+       * NEWS: Deprecated gc-pbkdf2-sha1 in favor of gc-pbkdf2.
+       * lib/gc-pbkdf2.c: New file.
+       * lib/gc-pbkdf2-sha1.c: Use new interface.
+       * lib/gc.h (GC_MAX_DIGEST_SIZE, gc_pbkdf2_hmac): Add.
+       * modules/crypto/gc-pbkdf2: New file.
+       * modules/crypto/gc-pbkdf2-tests: New file.
+       * tests/test-gc-pbkdf2.c: New file.
+
 2020-01-12  Bruno Haible  <address@hidden>
 
        c32stombs: Add tests.
diff --git a/MODULES.html.sh b/MODULES.html.sh
index e3e2415ad..7eab5e547 100755
--- a/MODULES.html.sh
+++ b/MODULES.html.sh
@@ -1985,6 +1985,7 @@ func_all_modules ()
   func_module crypto/gc-md2
   func_module crypto/gc-md4
   func_module crypto/gc-md5
+  func_module crypto/gc-pbkdf2
   func_module crypto/gc-pbkdf2-sha1
   func_module crypto/gc-random
   func_module crypto/gc-rijndael
diff --git a/NEWS b/NEWS
index b3a1fd630..dc5cc71f9 100644
--- a/NEWS
+++ b/NEWS
@@ -58,6 +58,8 @@ User visible incompatible changes
 
 Date        Modules         Changes
 
+2020-01-15  gc-pbkdf2-sha1  This module is deprecated.  Use gc-pbkdf2 instead.
+
 2019-12-12  dfa             Its API now uses ptrdiff_t instead of size_t.
 
 2019-12-11  dfa             To call dfamust, one must now call dfaparse
diff --git a/lib/gc-pbkdf2-sha1.c b/lib/gc-pbkdf2-sha1.c
index 9d625766f..7c17c80fb 100644
--- a/lib/gc-pbkdf2-sha1.c
+++ b/lib/gc-pbkdf2-sha1.c
@@ -23,80 +23,11 @@
 #include <stdlib.h>
 #include <string.h>
 
-/* Implement PKCS#5 PBKDF2 as per RFC 2898.  The PRF to use is hard
-   coded to be HMAC-SHA1.  Inputs are the password P of length PLEN,
-   the salt S of length SLEN, the iteration counter C (> 0), and the
-   desired derived output length DKLEN.  Output buffer is DK which
-   must have room for at least DKLEN octets.  The output buffer will
-   be filled with the derived data.  */
 Gc_rc
 gc_pbkdf2_sha1 (const char *P, size_t Plen,
                 const char *S, size_t Slen,
                 unsigned int c,
                 char *DK, size_t dkLen)
 {
-  unsigned int hLen = 20;
-  char U[20];
-  char T[20];
-  unsigned int u;
-  unsigned int l;
-  unsigned int r;
-  unsigned int i;
-  unsigned int k;
-  int rc;
-  char *tmp;
-  size_t tmplen = Slen + 4;
-
-  if (c == 0)
-    return GC_PKCS5_INVALID_ITERATION_COUNT;
-
-  if (dkLen == 0)
-    return GC_PKCS5_INVALID_DERIVED_KEY_LENGTH;
-
-  if (dkLen > 4294967295U)
-    return GC_PKCS5_DERIVED_KEY_TOO_LONG;
-
-  l = ((dkLen - 1) / hLen) + 1;
-  r = dkLen - (l - 1) * hLen;
-
-  tmp = malloc (tmplen);
-  if (tmp == NULL)
-    return GC_MALLOC_ERROR;
-
-  memcpy (tmp, S, Slen);
-
-  for (i = 1; i <= l; i++)
-    {
-      memset (T, 0, hLen);
-
-      for (u = 1; u <= c; u++)
-        {
-          if (u == 1)
-            {
-              tmp[Slen + 0] = (i & 0xff000000) >> 24;
-              tmp[Slen + 1] = (i & 0x00ff0000) >> 16;
-              tmp[Slen + 2] = (i & 0x0000ff00) >> 8;
-              tmp[Slen + 3] = (i & 0x000000ff) >> 0;
-
-              rc = gc_hmac_sha1 (P, Plen, tmp, tmplen, U);
-            }
-          else
-            rc = gc_hmac_sha1 (P, Plen, U, hLen, U);
-
-          if (rc != GC_OK)
-            {
-              free (tmp);
-              return rc;
-            }
-
-          for (k = 0; k < hLen; k++)
-            T[k] ^= U[k];
-        }
-
-      memcpy (DK + (i - 1) * hLen, T, i == l ? r : hLen);
-    }
-
-  free (tmp);
-
-  return GC_OK;
+  return gc_pbkdf2_hmac (GC_SHA1, P, Plen, S, Slen, c, DK, dkLen);
 }
diff --git a/lib/gc-pbkdf2.c b/lib/gc-pbkdf2.c
new file mode 100644
index 000000000..297b34809
--- /dev/null
+++ b/lib/gc-pbkdf2.c
@@ -0,0 +1,138 @@
+/* gc-pbkdf2.c --- Password-Based Key Derivation Function a'la PKCS#5
+   Copyright (C) 2002-2006, 2009-2020 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Simon Josefsson. */
+
+#include <config.h>
+
+#include "gc.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+typedef Gc_rc (*gc_prf_func) (const void *key, size_t keylen,
+                              const void *in, size_t inlen, char *resbuf);
+
+static Gc_rc
+gc_pbkdf2_prf (gc_prf_func prf, size_t hLen,
+               const char *P, size_t Plen,
+               const char *S, size_t Slen,
+               unsigned int c,
+               char *DK, size_t dkLen)
+{
+  char U[GC_MAX_DIGEST_SIZE];
+  char T[GC_MAX_DIGEST_SIZE];
+  unsigned int u;
+  unsigned int l;
+  unsigned int r;
+  unsigned int i;
+  unsigned int k;
+  int rc;
+  char *tmp;
+  size_t tmplen = Slen + 4;
+
+  if (c == 0)
+    return GC_PKCS5_INVALID_ITERATION_COUNT;
+
+  if (dkLen == 0)
+    return GC_PKCS5_INVALID_DERIVED_KEY_LENGTH;
+
+  if (dkLen > 4294967295U)
+    return GC_PKCS5_DERIVED_KEY_TOO_LONG;
+
+  l = ((dkLen - 1) / hLen) + 1;
+  r = dkLen - (l - 1) * hLen;
+
+  tmp = malloc (tmplen);
+  if (tmp == NULL)
+    return GC_MALLOC_ERROR;
+
+  memcpy (tmp, S, Slen);
+
+  for (i = 1; i <= l; i++)
+    {
+      memset (T, 0, hLen);
+
+      for (u = 1; u <= c; u++)
+        {
+          if (u == 1)
+            {
+              tmp[Slen + 0] = (i & 0xff000000) >> 24;
+              tmp[Slen + 1] = (i & 0x00ff0000) >> 16;
+              tmp[Slen + 2] = (i & 0x0000ff00) >> 8;
+              tmp[Slen + 3] = (i & 0x000000ff) >> 0;
+
+              rc = prf (P, Plen, tmp, tmplen, U);
+            }
+          else
+            rc = prf (P, Plen, U, hLen, U);
+
+          if (rc != GC_OK)
+            {
+              free (tmp);
+              return rc;
+            }
+
+          for (k = 0; k < hLen; k++)
+            T[k] ^= U[k];
+        }
+
+      memcpy (DK + (i - 1) * hLen, T, i == l ? r : hLen);
+    }
+
+  free (tmp);
+
+  return GC_OK;
+}
+
+Gc_rc
+gc_pbkdf2_hmac (Gc_hash hash,
+                const char *P, size_t Plen,
+                const char *S, size_t Slen,
+                unsigned int c, char *DK, size_t dkLen)
+{
+  gc_prf_func prf;
+  size_t hLen;
+
+  switch (hash)
+    {
+#if GNULIB_GC_HMAC_SHA1
+    case GC_SHA1:
+      prf = gc_hmac_sha1;
+      hLen = GC_SHA1_DIGEST_SIZE;
+      break;
+#endif
+
+#if GNULIB_GC_HMAC_SHA256
+    case GC_SHA256:
+      prf = gc_hmac_sha256;
+      hLen = GC_SHA256_DIGEST_SIZE;
+      break;
+#endif
+
+#if GNULIB_GC_HMAC_SHA512
+    case GC_SHA512:
+      prf = gc_hmac_sha512;
+      hLen = GC_SHA512_DIGEST_SIZE;
+      break;
+#endif
+
+    default:
+      return GC_INVALID_HASH;
+    }
+
+  return gc_pbkdf2_prf (prf, hLen, P, Plen, S, Slen, c, DK, dkLen);
+}
diff --git a/lib/gc.h b/lib/gc.h
index e608ec7dc..05fb8a3d2 100644
--- a/lib/gc.h
+++ b/lib/gc.h
@@ -72,6 +72,8 @@ typedef void *gc_hash_handle;
 #define GC_SHA224_DIGEST_SIZE 24
 #define GC_SM3_DIGEST_SIZE 32
 
+#define GC_MAX_DIGEST_SIZE 64
+
 /* Cipher types. */
 enum Gc_cipher
 {
@@ -171,13 +173,20 @@ extern Gc_rc gc_hmac_sha256 (const void *key, size_t 
keylen,
 extern Gc_rc gc_hmac_sha512 (const void *key, size_t keylen,
                              const void *in, size_t inlen, char *resbuf);
 
-/* Derive cryptographic keys from a password P of length PLEN, with
-   salt S of length SLEN, placing the result in pre-allocated buffer
-   DK of length DKLEN.  An iteration count is specified in C, where a
-   larger value means this function take more time (typical iteration
-   counts are 1000-20000).  This function "stretches" the key to be
-   exactly dkLen bytes long.  GC_OK is returned on success, otherwise
-   a Gc_rc error code is returned.  */
+/* Derive cryptographic keys using PKCS#5 PBKDF2 (RFC 2898) from a
+   password P of length PLEN, with salt S of length SLEN, placing the
+   result in pre-allocated buffer DK of length DKLEN.  The PRF is hard
+   coded to be HMAC with HASH.  An iteration count is specified in C
+   (> 0), where a larger value means this function take more time
+   (typical iteration counts are 1000-20000).  This function
+   "stretches" the key to be exactly dkLen bytes long.  GC_OK is
+   returned on success, otherwise a Gc_rc error code is returned.  */
+extern Gc_rc
+gc_pbkdf2_hmac (Gc_hash hash,
+                const char *P, size_t Plen,
+                const char *S, size_t Slen,
+                unsigned int c, char *DK, size_t dkLen);
+
 extern Gc_rc
 gc_pbkdf2_sha1 (const char *P, size_t Plen,
                 const char *S, size_t Slen,
diff --git a/modules/crypto/gc-pbkdf2 b/modules/crypto/gc-pbkdf2
new file mode 100644
index 000000000..47b61015e
--- /dev/null
+++ b/modules/crypto/gc-pbkdf2
@@ -0,0 +1,22 @@
+Description:
+Password-Based Key Derivation Function according to PKCS#5/RFC2898
+
+Files:
+lib/gc-pbkdf2.c
+
+Depends-on:
+crypto/gc
+
+configure.ac:
+
+Makefile.am:
+lib_SOURCES += gc-pbkdf2.c
+
+Include:
+"gc.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+Simon Josefsson
diff --git a/modules/crypto/gc-pbkdf2-sha1 b/modules/crypto/gc-pbkdf2-sha1
index b0cec6145..576078de6 100644
--- a/modules/crypto/gc-pbkdf2-sha1
+++ b/modules/crypto/gc-pbkdf2-sha1
@@ -1,11 +1,17 @@
 Description:
-Password-Based Key Derivation Function according to PKCS#5/RFC2898
+Password-Based Key Derivation Function according to PKCS#5/RFC2898 with 
HMAC-SHA1
+
+Status:
+deprecated
+
+Notice:
+This module is deprecated. Use the module 'gc-pbkdf2' instead.
 
 Files:
 lib/gc-pbkdf2-sha1.c
 
 Depends-on:
-crypto/gc
+crypto/gc-pbkdf2
 crypto/gc-hmac-sha1
 
 configure.ac:
diff --git a/modules/crypto/gc-pbkdf2-tests b/modules/crypto/gc-pbkdf2-tests
new file mode 100644
index 000000000..0b90547c7
--- /dev/null
+++ b/modules/crypto/gc-pbkdf2-tests
@@ -0,0 +1,11 @@
+Files:
+tests/test-gc-pbkdf2.c
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-gc-pbkdf2
+check_PROGRAMS += test-gc-pbkdf2
+test_gc_pbkdf2_LDADD = $(LDADD) @LIB_CRYPTO@
diff --git a/tests/test-gc-pbkdf2.c b/tests/test-gc-pbkdf2.c
new file mode 100644
index 000000000..005ac0df1
--- /dev/null
+++ b/tests/test-gc-pbkdf2.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2002-2005, 2007, 2010-2020 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Simon Josefsson.  */
+
+#include <config.h>
+
+#include "gc.h"
+
+#include <stdio.h>
+#include <string.h>
+
+/* Test vectors from RFC 3962. */
+
+#define G_CLEF "\xF0\x9D\x84\x9E"
+
+struct pkcs5
+{
+  Gc_hash hash;
+  int iterations;
+  const char *password;
+  const char *salt;
+  int dklen;
+  const char *expected;
+};
+const struct pkcs5 pkcs5[] = {
+#if GNULIB_GC_HMAC_SHA1
+  {GC_SHA1, 1, "password", "ATHENA.MIT.EDUraeburn", 16,
+   "\xCD\xED\xB5\x28\x1B\xB2\xF8\x01\x56\x5A\x11\x22\xB2\x56\x35\x15"},
+  {GC_SHA1, 2, "password", "ATHENA.MIT.EDUraeburn", 16,
+   "\x01\xdb\xee\x7f\x4a\x9e\x24\x3e\x98\x8b\x62\xc7\x3c\xda\x93\x5d"},
+  {GC_SHA1, 2, "password", "ATHENA.MIT.EDUraeburn", 32,
+   "\x01\xdb\xee\x7f\x4a\x9e\x24\x3e\x98\x8b\x62\xc7\x3c\xda\x93\x5d"
+   "\xa0\x53\x78\xb9\x32\x44\xec\x8f\x48\xa9\x9e\x61\xad\x79\x9d\x86"},
+  {GC_SHA1, 1200, "password", "ATHENA.MIT.EDUraeburn", 16,
+   "\x5c\x08\xeb\x61\xfd\xf7\x1e\x4e\x4e\xc3\xcf\x6b\xa1\xf5\x51\x2b"},
+  {GC_SHA1, 1200, "password", "ATHENA.MIT.EDUraeburn", 32,
+   "\x5c\x08\xeb\x61\xfd\xf7\x1e\x4e\x4e\xc3\xcf\x6b\xa1\xf5\x51\x2b"
+   "\xa7\xe5\x2d\xdb\xc5\xe5\x14\x2f\x70\x8a\x31\xe2\xe6\x2b\x1e\x13"},
+  {GC_SHA1, 5, "password", "\x12\x34\x56\x78\x78\x56\x34\x12\x00", 16,
+   "\xd1\xda\xa7\x86\x15\xf2\x87\xe6\xa1\xc8\xb1\x20\xd7\x06\x2a\x49"},
+  {GC_SHA1, 5, "password", "\x12\x34\x56\x78\x78\x56\x34\x12\x00", 32,
+   "\xd1\xda\xa7\x86\x15\xf2\x87\xe6\xa1\xc8\xb1\x20\xd7\x06\x2a\x49"
+   "\x3f\x98\xd2\x03\xe6\xbe\x49\xa6\xad\xf4\xfa\x57\x4b\x6e\x64\xee"},
+  {GC_SHA1, 1200,
+   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+   "pass phrase equals block size", 16,
+   "\x13\x9c\x30\xc0\x96\x6b\xc3\x2b\xa5\x5f\xdb\xf2\x12\x53\x0a\xc9"},
+  {GC_SHA1, 1200,
+   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+   "pass phrase equals block size", 32,
+   "\x13\x9c\x30\xc0\x96\x6b\xc3\x2b\xa5\x5f\xdb\xf2\x12\x53\x0a\xc9"
+   "\xc5\xec\x59\xf1\xa4\x52\xf5\xcc\x9a\xd9\x40\xfe\xa0\x59\x8e\xd1"},
+  {GC_SHA1, 1200,
+   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+   "pass phrase exceeds block size", 16,
+   "\x9c\xca\xd6\xd4\x68\x77\x0c\xd5\x1b\x10\xe6\xa6\x87\x21\xbe\x61"},
+  {GC_SHA1, 1200,
+   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+   "pass phrase exceeds block size", 32,
+   "\x9c\xca\xd6\xd4\x68\x77\x0c\xd5\x1b\x10\xe6\xa6\x87\x21\xbe\x61"
+   "\x1a\x8b\x4d\x28\x26\x01\xdb\x3b\x36\xbe\x92\x46\x91\x5e\xc8\x2a"},
+  {GC_SHA1, 50, G_CLEF "\x00", "EXAMPLE.COMpianist", 16,
+   "\x6b\x9c\xf2\x6d\x45\x45\x5a\x43\xa5\xb8\xbb\x27\x6a\x40\x3b\x39"},
+  {GC_SHA1, 50, G_CLEF "\x00", "EXAMPLE.COMpianist", 32,
+   "\x6b\x9c\xf2\x6d\x45\x45\x5a\x43\xa5\xb8\xbb\x27\x6a\x40\x3b\x39"
+   "\xe7\xfe\x37\xa0\xc4\x1e\x02\xc2\x81\xff\x30\x69\xe1\xe9\x4f\x52"},
+  {GC_SHA1, 500,
+   "All n-entities must communicate with other n-entities via n-1 "
+   "entiteeheehees", "\x12\x34\x56\x78\x78\x56\x34\x12\x00", 16,
+   "\x6A\x89\x70\xBF\x68\xC9\x2C\xAE\xA8\x4A\x8D\xF2\x85\x10\x85\x86"},
+#endif
+#if GNULIB_GC_HMAC_SHA256
+  {GC_SHA256, 4096, "pencil",
+   "\x5b\x6d\x99\x68\x9d\x12\x35\x8e\xec\xa0\x4b\x14\x12\x36\xfa\x81",
+   32,
+   "\xc4\xa4\x95\x10\x32\x3a\xb4\xf9\x52\xca\xc1\xfa\x99\x44\x19\x39"
+   "\xe7\x8e\xa7\x4d\x6b\xe8\x1d\xdf\x70\x96\xe8\x75\x13\xdc\x61\x5d"}
+#endif
+};
+
+int
+main (int argc, char *argv[])
+{
+  size_t i;
+  int rc;
+  char out[BUFSIZ];
+
+  rc = gc_pbkdf2_hmac (GC_MD2, "p", 1, "s", 1, 42, out, 17);
+
+  if (rc != GC_INVALID_HASH)
+    {
+      printf ("PBKDF2 accepts invalid MD2 hash %d\n", rc);
+      return 1;
+    }
+
+  for (i = 0; i < sizeof (pkcs5) / sizeof (pkcs5[0]); i++)
+    {
+      rc = gc_pbkdf2_hmac (pkcs5[i].hash,
+                           pkcs5[i].password, strlen (pkcs5[i].password),
+                           pkcs5[i].salt, strlen (pkcs5[i].salt),
+                           pkcs5[i].iterations, out, pkcs5[i].dklen);
+      if (rc != GC_OK)
+        {
+          printf ("PKCS5 entry %ld failed fatally: %d\n",
+                  (unsigned long) i, rc);
+          return 1;
+        }
+
+      if (memcmp (pkcs5[i].expected, out, pkcs5[i].dklen) != 0)
+        {
+          printf ("PKCS5 entry %ld failed\n", (unsigned long) i);
+          return 1;
+        }
+    }
+
+  return 0;
+}
-- 
2.20.1

Attachment: signature.asc
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]