summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Josefsson <simon@josefsson.org>2020-01-15 23:10:58 +0100
committerSimon Josefsson <simon@josefsson.org>2020-01-15 23:26:56 +0100
commitdf2bd909b3ec63c2aa8d60f47c8fda3a7c9044d8 (patch)
tree4d9dbe321142d5ff296082ff78b05f0519e73034
parent611869be9f1083e53305446d90a2909fc89914ef (diff)
downloadgnulib-df2bd909b3ec63c2aa8d60f47c8fda3a7c9044d8.tar.gz
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.
-rw-r--r--ChangeLog12
-rwxr-xr-xMODULES.html.sh1
-rw-r--r--NEWS2
-rw-r--r--lib/gc-pbkdf2-sha1.c71
-rw-r--r--lib/gc-pbkdf2.c138
-rw-r--r--lib/gc.h23
-rw-r--r--modules/crypto/gc-pbkdf222
-rw-r--r--modules/crypto/gc-pbkdf2-sha110
-rw-r--r--modules/crypto/gc-pbkdf2-tests11
-rw-r--r--tests/test-gc-pbkdf2.c131
10 files changed, 342 insertions, 79 deletions
diff --git a/ChangeLog b/ChangeLog
index 245f2a7c1e..fc48c3960a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2020-01-15 Simon Josefsson <simon@josefsson.org>
+
+ 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 <bruno@clisp.org>
c32stombs: Add tests.
diff --git a/MODULES.html.sh b/MODULES.html.sh
index e3e2415ad9..7eab5e547a 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 b3a1fd6304..dc5cc71f96 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 9d625766f7..7c17c80fb7 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 0000000000..297b348093
--- /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 e608ec7dcd..05fb8a3d25 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 0000000000..47b61015e0
--- /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 b0cec61458..576078de66 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 0000000000..0b90547c78
--- /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 0000000000..005ac0df10
--- /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;
+}