summaryrefslogtreecommitdiff
path: root/lib/gc-pbkdf2.c
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 /lib/gc-pbkdf2.c
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.
Diffstat (limited to 'lib/gc-pbkdf2.c')
-rw-r--r--lib/gc-pbkdf2.c138
1 files changed, 138 insertions, 0 deletions
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);
+}