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 + + 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 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 #include -/* 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 . */ + +/* Written by Simon Josefsson. */ + +#include + +#include "gc.h" + +#include +#include + +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 . */ + +/* Written by Simon Josefsson. */ + +#include + +#include "gc.h" + +#include +#include + +/* 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