diff options
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | cipher/Makefile.am | 7 | ||||
-rw-r--r-- | cipher/kdf-internal.h | 40 | ||||
-rw-r--r-- | cipher/kdf.c | 48 | ||||
-rw-r--r-- | cipher/memxor.c | 326 | ||||
-rw-r--r-- | cipher/memxor.h | 21 | ||||
-rw-r--r-- | cipher/scrypt.c | 276 | ||||
-rw-r--r-- | cipher/scrypt.h | 66 | ||||
-rw-r--r-- | configure.ac | 43 | ||||
-rw-r--r-- | doc/gcrypt.texi | 2 | ||||
-rw-r--r-- | src/gcrypt.h.in | 2 | ||||
-rw-r--r-- | tests/t-kdf.c | 103 |
12 files changed, 393 insertions, 544 deletions
@@ -14,6 +14,8 @@ Noteworthy changes in version 1.6.0 (unreleased) * Added a random number generator to directly use the system's RNG. Also added an interface to prefer the use of a specified RNG. + * Added support for the SCRYPT algorithm. + * Interface changes relative to the 1.5.0 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gcry_ac_* REMOVED. @@ -58,6 +60,7 @@ Noteworthy changes in version 1.6.0 (unreleased) GCRYMPI_FLAG_IMMUTABLE NEW. GCRYMPI_FLAG_CONST NEW. GCRYPT_VERSION_NUMBER NEW. + GCRY_KDF_SCRYPT NEW. Noteworthy changes in version 1.5.0 (2011-06-29) diff --git a/cipher/Makefile.am b/cipher/Makefile.am index 5b016f05..51897949 100644 --- a/cipher/Makefile.am +++ b/cipher/Makefile.am @@ -31,7 +31,8 @@ AM_CCASFLAGS = $(NOEXECSTACK_FLAGS) noinst_LTLIBRARIES = libcipher.la -GCRYPT_MODULES = @GCRYPT_CIPHERS@ @GCRYPT_PUBKEY_CIPHERS@ @GCRYPT_DIGESTS@ +GCRYPT_MODULES = @GCRYPT_CIPHERS@ @GCRYPT_PUBKEY_CIPHERS@ \ + @GCRYPT_DIGESTS@ @GCRYPT_KDFS@ libcipher_la_DEPENDENCIES = $(GCRYPT_MODULES) libcipher_la_LIBADD = $(GCRYPT_MODULES) @@ -39,7 +40,8 @@ libcipher_la_LIBADD = $(GCRYPT_MODULES) libcipher_la_SOURCES = \ cipher.c cipher-internal.h \ cipher-cbc.c cipher-cfb.c cipher-ofb.c cipher-ctr.c cipher-aeswrap.c \ -pubkey.c md.c kdf.c scrypt.c memxor.c \ +pubkey.c md.c \ +kdf.c kdf-internal.h \ hmac-tests.c \ bithelp.h \ bufhelp.h \ @@ -62,6 +64,7 @@ md5.c \ rijndael.c rijndael-tables.h \ rmd160.c \ rsa.c \ +scrypt.c \ seed.c \ serpent.c \ sha1.c \ diff --git a/cipher/kdf-internal.h b/cipher/kdf-internal.h new file mode 100644 index 00000000..7079860e --- /dev/null +++ b/cipher/kdf-internal.h @@ -0,0 +1,40 @@ +/* kdf-internal.h - Internal defs for kdf.c + * Copyright (C) 2013 g10 Code GmbH + * + * This file is part of Libgcrypt. + * + * Libgcrypt is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser general Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * Libgcrypt 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef GCRY_KDF_INTERNAL_H +#define GCRY_KDF_INTERNAL_H + +/*-- kdf.c --*/ +gpg_err_code_t +_gcry_kdf_pkdf2 (const void *passphrase, size_t passphraselen, + int hashalgo, + const void *salt, size_t saltlen, + unsigned long iterations, + size_t keysize, void *keybuffer); + +/*-- scrypt.c --*/ +gcry_err_code_t +_gcry_kdf_scrypt (const unsigned char *passwd, size_t passwdlen, + int algo, int subalgo, + const unsigned char *salt, size_t saltlen, + unsigned long iterations, + size_t dklen, unsigned char *dk); + + +#endif /*GCRY_KDF_INTERNAL_H*/ diff --git a/cipher/kdf.c b/cipher/kdf.c index 4ea0fb29..da63574f 100644 --- a/cipher/kdf.c +++ b/cipher/kdf.c @@ -1,5 +1,6 @@ /* kdf.c - Key Derivation Functions * Copyright (C) 1998, 2011 Free Software Foundation, Inc. + * Copyright (C) 2013 g10 Code GmbH * * This file is part of Libgcrypt. * @@ -26,7 +27,7 @@ #include "g10lib.h" #include "cipher.h" #include "ath.h" -#include "scrypt.h" +#include "kdf-internal.h" /* Transform a passphrase into a suitable key of length KEYSIZE and @@ -34,7 +35,7 @@ must provide an HASHALGO, a valid ALGO and depending on that algo a SALT of 8 bytes and the number of ITERATIONS. Code taken from gnupg/agent/protect.c:hash_passphrase. */ -gpg_err_code_t +static gpg_err_code_t openpgp_s2k (const void *passphrase, size_t passphraselen, int algo, int hashalgo, const void *salt, size_t saltlen, @@ -117,11 +118,11 @@ openpgp_s2k (const void *passphrase, size_t passphraselen, used in HMAC mode. SALT is a salt of length SALTLEN and ITERATIONS gives the number of iterations. */ gpg_err_code_t -pkdf2 (const void *passphrase, size_t passphraselen, - int hashalgo, - const void *salt, size_t saltlen, - unsigned long iterations, - size_t keysize, void *keybuffer) +_gcry_kdf_pkdf2 (const void *passphrase, size_t passphraselen, + int hashalgo, + const void *salt, size_t saltlen, + unsigned long iterations, + size_t keysize, void *keybuffer) { gpg_err_code_t ec; gcry_md_hd_t md; @@ -139,7 +140,10 @@ pkdf2 (const void *passphrase, size_t passphraselen, unsigned long iter; /* Current iteration number. */ unsigned int i; - if (!salt || !saltlen || !iterations || !dklen) + /* NWe allow for a saltlen of 0 here to support scrypt. It is not + clear whether rfc2898 allows for this this, thus we do a test on + saltlen > 0 only in gcry_kdf_derive. */ + if (!salt || !iterations || !dklen) return GPG_ERR_INV_VALUE; hlen = gcry_md_get_algo_dlen (hashalgo); @@ -239,11 +243,12 @@ gcry_kdf_derive (const void *passphrase, size_t passphraselen, { gpg_err_code_t ec; - if (!passphrase || (!passphraselen && algo != GCRY_KDF_PBKDF2)) + if (!passphrase) { ec = GPG_ERR_INV_DATA; goto leave; } + if (!keybuffer || !keysize) { ec = GPG_ERR_INV_VALUE; @@ -256,8 +261,11 @@ gcry_kdf_derive (const void *passphrase, size_t passphraselen, case GCRY_KDF_SIMPLE_S2K: case GCRY_KDF_SALTED_S2K: case GCRY_KDF_ITERSALTED_S2K: - ec = openpgp_s2k (passphrase, passphraselen, algo, subalgo, - salt, saltlen, iterations, keysize, keybuffer); + if (!passphraselen) + ec = GPG_ERR_INV_DATA; + else + ec = openpgp_s2k (passphrase, passphraselen, algo, subalgo, + salt, saltlen, iterations, keysize, keybuffer); break; case GCRY_KDF_PBKDF1: @@ -265,12 +273,22 @@ gcry_kdf_derive (const void *passphrase, size_t passphraselen, break; case GCRY_KDF_PBKDF2: - ec = pkdf2 (passphrase, passphraselen, subalgo, - salt, saltlen, iterations, keysize, keybuffer); + if (!saltlen) + ec = GPG_ERR_INV_VALUE; + else + ec = _gcry_kdf_pkdf2 (passphrase, passphraselen, subalgo, + salt, saltlen, iterations, keysize, keybuffer); break; + + case 41: case GCRY_KDF_SCRYPT: - ec = scrypt (passphrase, passphraselen, subalgo, - salt, saltlen, iterations, keysize, keybuffer); +#if USE_SCRYPT + ec = _gcry_kdf_scrypt (passphrase, passphraselen, algo, subalgo, + salt, saltlen, iterations, keysize, keybuffer); +#else + ec = GPG_ERR_UNSUPPORTED_ALGORITHM; +#endif /*USE_SCRYPT*/ + break; default: ec = GPG_ERR_UNKNOWN_ALGORITHM; diff --git a/cipher/memxor.c b/cipher/memxor.c deleted file mode 100644 index 74307f0c..00000000 --- a/cipher/memxor.c +++ /dev/null @@ -1,326 +0,0 @@ -/* memxor.c - * - * - * This file is part of Libgcrypt. - * Adapted from the nettle, low-level cryptographics library for - * libgcrypt by Christian Grothoff; original license: - */ - -/* - * Copyright (C) 1991, 1993, 1995 Free Software Foundation, Inc. - * Copyright (C) 2010 Niels Möller - * - * The nettle library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The nettle library 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 Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the nettle library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02111-1301, USA. - */ - -/* Implementation inspired by memcmp in glibc, contributed to the FSF - by Torbjorn Granlund. - */ - -#include <config.h> -#include <limits.h> -#include <stdint.h> -#include "memxor.h" - -typedef unsigned long int word_t; - -// FIXME: need configure test for this hack... -#define SIZEOF_LONG 8 - -#if SIZEOF_LONG & (SIZEOF_LONG - 1) -#error Word size must be a power of two -#endif - -#define ALIGN_OFFSET(p) ((uintptr_t) (p) % sizeof(word_t)) - -#ifndef WORDS_BIGENDIAN -#define MERGE(w0, sh_1, w1, sh_2) (((w0) >> (sh_1)) | ((w1) << (sh_2))) -#else -#define MERGE(w0, sh_1, w1, sh_2) (((w0) << (sh_1)) | ((w1) >> (sh_2))) -#endif - -#define WORD_T_THRESH 16 - -/* XOR word-aligned areas. n is the number of words, not bytes. */ -static void -memxor_common_alignment (word_t *dst, const word_t *src, size_t n) -{ - /* FIXME: Require n > 0? */ - /* FIXME: Unroll four times, like memcmp? Probably not worth the - effort. */ - - if (n & 1) - { - *dst++ ^= *src++; - n--; - } - for (; n >= 2; dst += 2, src += 2, n -= 2) - { - dst[0] ^= src[0]; - dst[1] ^= src[1]; - } -} - -/* XOR *un-aligned* src-area onto aligned dst area. n is number of - words, not bytes. Assumes we can read complete words at the start - and end of the src operand. */ -static void -memxor_different_alignment (word_t *dst, const uint8_t *src, size_t n) -{ - size_t i; - int shl, shr; - const word_t *src_word; - unsigned offset = ALIGN_OFFSET (src); - word_t s0, s1; - - shl = CHAR_BIT * offset; - shr = CHAR_BIT * (sizeof(word_t) - offset); - - src_word = (const word_t *) ((uintptr_t) src & -SIZEOF_LONG); - - /* FIXME: Unroll four times, like memcmp? */ - i = n & 1; - s0 = src_word[i]; - if (i) - { - s1 = src_word[0]; - dst[0] ^= MERGE (s1, shl, s0, shr); - } - - for (; i < n; i += 2) - { - s1 = src_word[i+1]; - dst[i] ^= MERGE(s0, shl, s1, shr); - s0 = src_word[i+2]; - dst[i+1] ^= MERGE(s1, shl, s0, shr); - } -} - -/* Performance, Intel SU1400 (x86_64): 0.25 cycles/byte aligned, 0.45 - cycles/byte unaligned. */ - -/* XOR LEN bytes starting at SRCADDR onto DESTADDR. Result undefined - if the source overlaps with the destination. Return DESTADDR. */ -uint8_t * -memxor(uint8_t *dst, const uint8_t *src, size_t n) -{ - uint8_t *orig_dst = dst; - - if (n >= WORD_T_THRESH) - { - /* There are at least some bytes to compare. No need to test - for N == 0 in this alignment loop. */ - while (ALIGN_OFFSET (dst)) - { - *dst++ ^= *src++; - n--; - } - if (ALIGN_OFFSET (src)) - memxor_different_alignment ((word_t *) dst, src, n / sizeof(word_t)); - else - memxor_common_alignment ((word_t *) dst, (const word_t *) src, n / sizeof(word_t)); - - dst += n & -SIZEOF_LONG; - src += n & -SIZEOF_LONG; - n = n & (SIZEOF_LONG - 1); - } - for (; n > 0; n--) - *dst++ ^= *src++; - - return orig_dst; -} - - -/* XOR word-aligned areas. n is the number of words, not bytes. */ -static void -memxor3_common_alignment (word_t *dst, - const word_t *a, const word_t *b, size_t n) -{ - /* FIXME: Require n > 0? */ - while (n-- > 0) - dst[n] = a[n] ^ b[n]; -} - -static void -memxor3_different_alignment_b (word_t *dst, - const word_t *a, const uint8_t *b, unsigned offset, size_t n) -{ - int shl, shr; - const word_t *b_word; - - word_t s0, s1; - - shl = CHAR_BIT * offset; - shr = CHAR_BIT * (sizeof(word_t) - offset); - - b_word = (const word_t *) ((uintptr_t) b & -SIZEOF_LONG); - - if (n & 1) - { - n--; - s1 = b_word[n]; - s0 = b_word[n+1]; - dst[n] = a[n] ^ MERGE (s1, shl, s0, shr); - } - else - s1 = b_word[n]; - - while (n > 0) - { - n -= 2; - s0 = b_word[n+1]; - dst[n+1] = a[n+1] ^ MERGE(s0, shl, s1, shr); - s1 = b_word[n]; - dst[n] = a[n] ^ MERGE(s1, shl, s0, shr); - } -} - -static void -memxor3_different_alignment_ab (word_t *dst, - const uint8_t *a, const uint8_t *b, - unsigned offset, size_t n) -{ - int shl, shr; - const word_t *a_word; - const word_t *b_word; - - word_t s0, s1; - - shl = CHAR_BIT * offset; - shr = CHAR_BIT * (sizeof(word_t) - offset); - - a_word = (const word_t *) ((uintptr_t) a & -SIZEOF_LONG); - b_word = (const word_t *) ((uintptr_t) b & -SIZEOF_LONG); - - if (n & 1) - { - n--; - s1 = a_word[n] ^ b_word[n]; - s0 = a_word[n+1] ^ b_word[n+1]; - dst[n] = MERGE (s1, shl, s0, shr); - } - else - s1 = a_word[n] ^ b_word[n]; - - while (n > 0) - { - n -= 2; - s0 = a_word[n+1] ^ b_word[n+1]; - dst[n+1] = MERGE(s0, shl, s1, shr); - s1 = a_word[n] ^ b_word[n]; - dst[n] = MERGE(s1, shl, s0, shr); - } -} - -static void -memxor3_different_alignment_all (word_t *dst, - const uint8_t *a, const uint8_t *b, - unsigned a_offset, unsigned b_offset, - size_t n) -{ - int al, ar, bl, br; - const word_t *a_word; - const word_t *b_word; - - word_t a0, a1, b0, b1; - - al = CHAR_BIT * a_offset; - ar = CHAR_BIT * (sizeof(word_t) - a_offset); - bl = CHAR_BIT * b_offset; - br = CHAR_BIT * (sizeof(word_t) - b_offset); - - a_word = (const word_t *) ((uintptr_t) a & -SIZEOF_LONG); - b_word = (const word_t *) ((uintptr_t) b & -SIZEOF_LONG); - - if (n & 1) - { - n--; - a1 = a_word[n]; a0 = a_word[n+1]; - b1 = b_word[n]; b0 = b_word[n+1]; - - dst[n] = MERGE (a1, al, a0, ar) ^ MERGE (b1, bl, b0, br); - } - else - { - a1 = a_word[n]; - b1 = b_word[n]; - } - - while (n > 0) - { - n -= 2; - a0 = a_word[n+1]; b0 = b_word[n+1]; - dst[n+1] = MERGE(a0, al, a1, ar) ^ MERGE(b0, bl, b1, br); - a1 = a_word[n]; b1 = b_word[n]; - dst[n] = MERGE(a1, al, a0, ar) ^ MERGE(b1, bl, b0, br); - } -} - -/* Current implementation processes data in descending order, to - support overlapping operation with one of the sources overlapping - the start of the destination area. This feature is used only - internally by cbc decrypt, and it is not advertised or documented - to nettle users. */ -uint8_t * -memxor3(uint8_t *dst, const uint8_t *a, const uint8_t *b, size_t n) -{ - if (n >= WORD_T_THRESH) - { - unsigned i; - unsigned a_offset; - unsigned b_offset; - size_t nwords; - - for (i = ALIGN_OFFSET(dst + n); i > 0; i--) - { - n--; - dst[n] = a[n] ^ b[n]; - } - - a_offset = ALIGN_OFFSET(a + n); - b_offset = ALIGN_OFFSET(b + n); - - nwords = n / sizeof (word_t); - n %= sizeof (word_t); - - if (a_offset == b_offset) - { - if (!a_offset) - memxor3_common_alignment((word_t *) (dst + n), - (const word_t *) (a + n), - (const word_t *) (b + n), nwords); - else - memxor3_different_alignment_ab((word_t *) (dst + n), - a + n, b + n, a_offset, - nwords); - } - else if (!a_offset) - memxor3_different_alignment_b((word_t *) (dst + n), - (const word_t *) (a + n), b + n, - b_offset, nwords); - else if (!b_offset) - memxor3_different_alignment_b((word_t *) (dst + n), - (const word_t *) (b + n), a + n, - a_offset, nwords); - else - memxor3_different_alignment_all((word_t *) (dst + n), a + n, b + n, - a_offset, b_offset, nwords); - } - while (n-- > 0) - dst[n] = a[n] ^ b[n]; - - return dst; -} diff --git a/cipher/memxor.h b/cipher/memxor.h deleted file mode 100644 index f308155f..00000000 --- a/cipher/memxor.h +++ /dev/null @@ -1,21 +0,0 @@ -/* memxor.h - * - */ - -#ifndef MEMXOR_H_INCLUDED -#define MEMXOR_H_INCLUDED - -#include <stdlib.h> - -#ifdef __cplusplus -extern "C" { -#endif - -uint8_t *memxor(uint8_t *dst, const uint8_t *src, size_t n); -uint8_t *memxor3(uint8_t *dst, const uint8_t *a, const uint8_t *b, size_t n); - -#ifdef __cplusplus -} -#endif - -#endif /* MEMXOR_H_INCLUDED */ diff --git a/cipher/scrypt.c b/cipher/scrypt.c index 45ab4b31..06196d62 100644 --- a/cipher/scrypt.c +++ b/cipher/scrypt.c @@ -1,6 +1,22 @@ /* scrypt.c - Scrypt password-based key derivation function. + * Copyright (C) 2012 Simon Josefsson + * Copyright (C) 2013 Christian Grothoff + * Copyright (C) 2013 g10 Code GmbH * * This file is part of Libgcrypt. + * + * Libgcrypt is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser general Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * Libgcrypt 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* Adapted from the nettle, low-level cryptographics library for @@ -30,39 +46,40 @@ #include <string.h> #include "g10lib.h" -#include "scrypt.h" -#include "memxor.h" - +#include "kdf-internal.h" +#include "bufhelp.h" +/* We really need a 64 bit type for this code. */ +#ifdef HAVE_U64_TYPEDEF -#define _SALSA20_INPUT_LENGTH 16 +#define SALSA20_INPUT_LENGTH 16 #define ROTL32(n,x) (((x)<<(n)) | ((x)>>(32-(n)))) /* Reads a 64-bit integer, in network, big-endian, byte order */ #define READ_UINT64(p) \ -( (((uint64_t) (p)[0]) << 56) \ - | (((uint64_t) (p)[1]) << 48) \ - | (((uint64_t) (p)[2]) << 40) \ - | (((uint64_t) (p)[3]) << 32) \ - | (((uint64_t) (p)[4]) << 24) \ - | (((uint64_t) (p)[5]) << 16) \ - | (((uint64_t) (p)[6]) << 8) \ - | ((uint64_t) (p)[7])) +( (((u64) (p)[0]) << 56) \ + | (((u64) (p)[1]) << 48) \ + | (((u64) (p)[2]) << 40) \ + | (((u64) (p)[3]) << 32) \ + | (((u64) (p)[4]) << 24) \ + | (((u64) (p)[5]) << 16) \ + | (((u64) (p)[6]) << 8) \ + | ((u64) (p)[7])) /* And the other, little-endian, byteorder */ #define LE_READ_UINT64(p) \ -( (((uint64_t) (p)[7]) << 56) \ - | (((uint64_t) (p)[6]) << 48) \ - | (((uint64_t) (p)[5]) << 40) \ - | (((uint64_t) (p)[4]) << 32) \ - | (((uint64_t) (p)[3]) << 24) \ - | (((uint64_t) (p)[2]) << 16) \ - | (((uint64_t) (p)[1]) << 8) \ - | ((uint64_t) (p)[0])) +( (((u64) (p)[7]) << 56) \ + | (((u64) (p)[6]) << 48) \ + | (((u64) (p)[5]) << 40) \ + | (((u64) (p)[4]) << 32) \ + | (((u64) (p)[3]) << 24) \ + | (((u64) (p)[2]) << 16) \ + | (((u64) (p)[1]) << 8) \ + | ((u64) (p)[0])) @@ -83,9 +100,9 @@ static void -_salsa20_core(uint32_t *dst, const uint32_t *src, unsigned rounds) +_salsa20_core(u32 *dst, const u32 *src, unsigned rounds) { - uint32_t x[_SALSA20_INPUT_LENGTH]; + u32 x[SALSA20_INPUT_LENGTH]; unsigned i; assert ( (rounds & 1) == 0); @@ -104,33 +121,36 @@ _salsa20_core(uint32_t *dst, const uint32_t *src, unsigned rounds) QROUND(x[15], x[12], x[13], x[14]); } - for (i = 0; i < _SALSA20_INPUT_LENGTH; i++) + for (i = 0; i < SALSA20_INPUT_LENGTH; i++) { - uint32_t t = x[i] + src[i]; + u32 t = x[i] + src[i]; dst[i] = LE_SWAP32 (t); } } static void -_scryptBlockMix (uint32_t r, uint8_t *B, uint8_t *tmp2) +_scryptBlockMix (u32 r, unsigned char *B, unsigned char *tmp2) { - uint64_t i; - uint8_t *X = tmp2; - uint8_t *Y = tmp2 + 64; + u64 i; + unsigned char *X = tmp2; + unsigned char *Y = tmp2 + 64; #if 0 - for (i = 0; i < 2 * r; i++) + if (r == 1) { - size_t j; - printf ("B[%d]: ", i); - for (j = 0; j < 64; j++) - { - if (j % 4 == 0) - printf (" "); - printf ("%02x", B[i * 64 + j]); - } - printf ("\n"); + for (i = 0; i < 2 * r; i++) + { + size_t j; + printf ("B[%d] = ", (int)i); + for (j = 0; j < 64; j++) + { + if (j && !(j % 16)) + printf ("\n "); + printf (" %02x", B[i * 64 + j]); + } + putchar ('\n'); + } } #endif @@ -141,10 +161,10 @@ _scryptBlockMix (uint32_t r, uint8_t *B, uint8_t *tmp2) for (i = 0; i <= 2 * r - 1; i++) { /* T = X xor B[i] */ - memxor(X, &B[i * 64], 64); + buf_xor(X, X, &B[i * 64], 64); /* X = Salsa (T) */ - _salsa20_core (X, X, 8); + _salsa20_core ((u32*)X, (u32*)X, 8); /* Y[i] = X */ memcpy (&Y[i * 64], X, 64); @@ -157,38 +177,43 @@ _scryptBlockMix (uint32_t r, uint8_t *B, uint8_t *tmp2) } #if 0 - for (i = 0; i < 2 * r; i++) + if (r==1) { - size_t j; - printf ("B'[%d]: ", i); - for (j = 0; j < 64; j++) - { - if (j % 4 == 0) - printf (" "); - printf ("%02x", B[i * 64 + j]); - } - printf ("\n"); + for (i = 0; i < 2 * r; i++) + { + size_t j; + printf ("B'[%d] =", (int)i); + for (j = 0; j < 64; j++) + { + if (j && !(j % 16)) + printf ("\n "); + printf (" %02x", B[i * 64 + j]); + } + putchar ('\n'); + } } #endif } static void -_scryptROMix (uint32_t r, uint8_t *B, uint64_t N, - uint8_t *tmp1, uint8_t *tmp2) +_scryptROMix (u32 r, unsigned char *B, u64 N, + unsigned char *tmp1, unsigned char *tmp2) { - uint8_t *X = B, *T = B; - uint64_t i; + unsigned char *X = B, *T = B; + u64 i; #if 0 - printf ("B: "); - for (i = 0; i < 128 * r; i++) + if (r == 1) { - size_t j; - if (i % 4 == 0) - printf (" "); - printf ("%02x", B[i]); + printf ("B = "); + for (i = 0; i < 128 * r; i++) + { + if (i && !(i % 16)) + printf ("\n "); + printf (" %02x", B[i]); + } + putchar ('\n'); } - printf ("\n"); #endif /* for i = 0 to N - 1 do */ @@ -204,81 +229,118 @@ _scryptROMix (uint32_t r, uint8_t *B, uint64_t N, /* for i = 0 to N - 1 do */ for (i = 0; i <= N - 1; i++) { - uint64_t j; + u64 j; /* j = Integerify (X) mod N */ j = LE_READ_UINT64 (&X[128 * r - 64]) % N; /* T = X xor V[j] */ - memxor (T, &tmp1[j * 128 * r], 128 * r); + buf_xor (T, T, &tmp1[j * 128 * r], 128 * r); /* X = scryptBlockMix (T) */ _scryptBlockMix (r, T, tmp2); } #if 0 - printf ("B': "); - for (i = 0; i < 128 * r; i++) + if (r == 1) { - size_t j; - if (i % 4 == 0) - printf (" "); - printf ("%02x", B[i]); + printf ("B' ="); + for (i = 0; i < 128 * r; i++) + { + if (i && !(i % 16)) + printf ("\n "); + printf (" %02x", B[i]); + } + putchar ('\n'); } - printf ("\n"); #endif } /** */ gcry_err_code_t -scrypt (const uint8_t * passwd, size_t passwdlen, - int subalgo, - const uint8_t * salt, size_t saltlen, - unsigned long iterations, - size_t dkLen, uint8_t * DK) +_gcry_kdf_scrypt (const unsigned char *passwd, size_t passwdlen, + int algo, int subalgo, + const unsigned char *salt, size_t saltlen, + unsigned long iterations, + size_t dkLen, unsigned char *DK) { - /* XXX sanity-check parameters */ - uint64_t N = subalgo; /* CPU/memory cost paramter */ - uint32_t r = 8; /* block size, should be sane enough */ - uint32_t p = iterations; /* parallelization parameter */ - - uint32_t i; - uint8_t *B; - uint8_t *tmp1; - uint8_t *tmp2; - + u64 N = subalgo; /* CPU/memory cost paramter. */ + u32 r; /* Block size. */ + u32 p = iterations; /* Parallelization parameter. */ + + gpg_err_code_t ec; + u32 i; + unsigned char *B = NULL; + unsigned char *tmp1 = NULL; + unsigned char *tmp2 = NULL; + size_t r128; + size_t nbytes; + + if (subalgo < 1 || !iterations) + return GPG_ERR_INV_VALUE; + + if (algo == GCRY_KDF_SCRYPT) + r = 8; + else if (algo == 41) /* Hack to allow the use of all test vectors. */ + r = 1; + else + return GPG_ERR_UNKNOWN_ALGORITHM; + + r128 = r * 128; + if (r128 / 128 != r) + return GPG_ERR_ENOMEM; - B = malloc (p * 128 * r); - if (B == NULL) + nbytes = p * r128; + if (r128 && nbytes / r128 != p) return GPG_ERR_ENOMEM; - tmp1 = malloc (N * 128 * r); - if (tmp1 == NULL) - { - free (B); + nbytes = N * r128; + if (r128 && nbytes / r128 != N) return GPG_ERR_ENOMEM; - } - tmp2 = malloc (64 + 128 * r); - if (tmp2 == NULL) - { - free (B); - free (tmp1); + nbytes = 64 + r128; + if (nbytes < r128) return GPG_ERR_ENOMEM; - } - pkdf2 (passwd, passwdlen, GCRY_MD_SHA256, salt, saltlen, 1 /* iterations */, p * 128 * r, B); + B = gcry_malloc (p * r128); + if (!B) + { + ec = gpg_err_code_from_syserror (); + goto leave; + } - for (i = 0; i < p; i++) - _scryptROMix (r, &B[i * 128 * r], N, tmp1, tmp2); + tmp1 = gcry_malloc (N * r128); + if (!tmp1) + { + ec = gpg_err_code_from_syserror (); + goto leave; + } + + tmp2 = gcry_malloc (64 + r128); + if (!tmp2) + { + ec = gpg_err_code_from_syserror (); + goto leave; + } - for (i = 0; i < p; i++) - pkdf2 (passwd, passwdlen, GCRY_MD_SHA256, B, p * 128 * r, 1 /* iterations */, dkLen, DK); + ec = _gcry_kdf_pkdf2 (passwd, passwdlen, GCRY_MD_SHA256, salt, saltlen, + 1 /* iterations */, p * r128, B); - free (tmp2); - free (tmp1); - free (B); + for (i = 0; !ec && i < p; i++) + _scryptROMix (r, &B[i * r128], N, tmp1, tmp2); - return 0; + for (i = 0; !ec && i < p; i++) + ec = _gcry_kdf_pkdf2 (passwd, passwdlen, GCRY_MD_SHA256, B, p * r128, + 1 /* iterations */, dkLen, DK); + + leave: + gcry_free (tmp2); + gcry_free (tmp1); + gcry_free (B); + + return ec; } + + +#endif /* HAVE_U64_TYPEDEF */ diff --git a/cipher/scrypt.h b/cipher/scrypt.h deleted file mode 100644 index e0c8df92..00000000 --- a/cipher/scrypt.h +++ /dev/null @@ -1,66 +0,0 @@ -/* scrypt.h - Scrypt password-based key derivation function. - * - * This file is part of Libgcrypt. - */ - -/* Adapted from the nettle, low-level cryptographics library for - * libgcrypt by Christian Grothoff; original license: - * - * Copyright (C) 2012 Simon Josefsson - * - * The nettle library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The nettle library 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 Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the nettle library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02111-1301, USA. - */ - -#ifndef SCRYPT_H_INCLUDED -#define SCRYPT_H_INCLUDED - -#ifdef __cplusplus -extern "C" -{ -#endif - -#include <stdint.h> -#include "g10lib.h" -#include "cipher.h" - - -/* Transform a passphrase into a suitable key of length KEYSIZE and - store this key in the caller provided buffer KEYBUFFER. The caller - must provide PRFALGO which indicates the pseudorandom function to - use: This shall be the algorithms id of a hash algorithm; it is - used in HMAC mode. SALT is a salt of length SALTLEN and ITERATIONS - gives the number of iterations; implemented in 'kdf.c', used by - scrypt.c */ -gpg_err_code_t -pkdf2 (const void *passphrase, size_t passphraselen, - int hashalgo, - const void *salt, size_t saltlen, - unsigned long iterations, - size_t keysize, void *keybuffer); - - -gcry_err_code_t -scrypt (const uint8_t * passwd, size_t passwdlen, - int subalgo, - const uint8_t * salt, size_t saltlen, - unsigned long iterations, - size_t dkLen, uint8_t * DK); - -#ifdef __cplusplus -} -#endif - -#endif /* SCRYPT_H_INCLUDED */ diff --git a/configure.ac b/configure.ac index cebf4b9e..c5973896 100644 --- a/configure.ac +++ b/configure.ac @@ -199,6 +199,11 @@ available_digests="crc md4 md5 rmd160 sha1 sha256" available_digests_64="sha512 tiger whirlpool" enabled_digests="" +# Definitions for kdfs (optional ones) +available_kdfs="s2k pkdf2" +available_kdfs_64="scrypt" +enabled_kdfs="" + # Definitions for random modules. available_random_modules="linux egd unix" auto_random_modules="$available_random_modules" @@ -351,9 +356,11 @@ if test "$ac_cv_sizeof_unsigned_int" != "8" \ && test "$ac_cv_sizeof_unsigned_long" != "8" \ && test "$ac_cv_sizeof_unsigned_long_long" != "8" \ && test "$ac_cv_sizeof_uint64_t" != "8"; then - AC_MSG_WARN([No 64-bit types. Disabling TIGER/192, SHA-384, and SHA-512]) + AC_MSG_WARN([No 64-bit types. Disabling TIGER/192, SCRYPT, SHA-384, \ + and SHA-512]) else available_digests="$available_digests $available_digests_64" + available_kdfs="$available_kdfs $available_kdfs_64" fi # If not specified otherwise, all available algorithms will be @@ -361,6 +368,7 @@ fi default_ciphers="$available_ciphers" default_pubkey_ciphers="$available_pubkey_ciphers" default_digests="$available_digests" +default_kdfs="$available_kdfs" # Substitutions to set generated files in a Emacs buffer to read-only. AC_SUBST(emacs_local_vars_begin, ['Local Variables:']) @@ -431,6 +439,26 @@ for digest in $enabled_digests; do done AC_MSG_RESULT([$enabled_digests]) +# Implementation of the --enable-kdfs switch. +AC_ARG_ENABLE(kdfs, + AC_HELP_STRING([--enable-kfds=kdfs], + [select the KDFs to include]), + [enabled_kdfs=`echo $enableval | tr ',:' ' ' | tr '[A-Z]' '[a-z]'`], + [enabled_kdfs=""]) +if test "x$enabled_kdfs" = "x" \ + -o "$enabled_kdfs" = "yes" \ + -o "$enabled_kdfs" = "no"; then + enabled_kdfs=$default_kdfs +fi +AC_MSG_CHECKING([which key derivation functions to include]) +for kdf in $enabled_kdfs; do + LIST_MEMBER($kdf, $available_kdfs) + if test "$found" = "0"; then + AC_MSG_ERROR([unsupported key derivation function specified]) + fi +done +AC_MSG_RESULT([$enabled_kdfs]) + # Implementation of the --enable-random switch. AC_ARG_ENABLE(random, AC_HELP_STRING([--enable-random=name], @@ -1145,7 +1173,7 @@ fi # Define conditional sources and config.h symbols depending on the -# selected ciphers, pubkey-ciphers, digests and random modules. +# selected ciphers, pubkey-ciphers, digests, kdfs, and random modules. LIST_MEMBER(arcfour, $enabled_ciphers) if test "$found" = "1"; then @@ -1291,6 +1319,12 @@ GCRYPT_DIGESTS="$GCRYPT_DIGESTS rmd160.lo sha1.lo" AC_DEFINE(USE_RMD160, 1, [Defined if this module should be included]) AC_DEFINE(USE_SHA1, 1, [Defined if this module should be included]) +LIST_MEMBER(scrypt, $enabled_kdfs) +if test "$found" = "1" ; then + GCRYPT_KDFS="$GCRYPT_KDFS scrypt.lo" + AC_DEFINE(USE_SCRYPT, 1, [Defined if this module should be included]) +fi + LIST_MEMBER(linux, $random_modules) if test "$found" = "1" ; then GCRYPT_RANDOM="$GCRYPT_RANDOM rndlinux.lo" @@ -1327,6 +1361,7 @@ fi AC_SUBST([GCRYPT_CIPHERS]) AC_SUBST([GCRYPT_PUBKEY_CIPHERS]) AC_SUBST([GCRYPT_DIGESTS]) +AC_SUBST([GCRYPT_KDFS]) AC_SUBST([GCRYPT_RANDOM]) AC_SUBST(LIBGCRYPT_CIPHERS, $enabled_ciphers) @@ -1344,6 +1379,9 @@ AC_DEFINE_UNQUOTED(LIBGCRYPT_PUBKEY_CIPHERS, "$tmp", tmp=`echo "$enabled_digests" | tr ' ' : ` AC_DEFINE_UNQUOTED(LIBGCRYPT_DIGESTS, "$tmp", [List of available digest algorithms]) +tmp=`echo "$enabled_kdfs" | tr ' ' : ` +AC_DEFINE_UNQUOTED(LIBGCRYPT_KDFS, "$tmp", + [List of available KDF algorithms]) # @@ -1428,6 +1466,7 @@ GCRY_MSG_SHOW([Platform: ],[$PRINTABLE_OS_NAME ($host)]) GCRY_MSG_SHOW([Hardware detection module:],[$detection_module]) GCRY_MSG_WRAP([Enabled cipher algorithms:],[$enabled_ciphers]) GCRY_MSG_WRAP([Enabled digest algorithms:],[$enabled_digests]) +GCRY_MSG_WRAP([Enabled kdf algorithms: ],[$enabled_kdfs]) GCRY_MSG_WRAP([Enabled pubkey algorithms:],[$enabled_pubkey_ciphers]) GCRY_MSG_SHOW([Random number generator: ],[$random]) GCRY_MSG_SHOW([Using linux capabilities: ],[$use_capabilities]) diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi index fe040f02..8f277c2a 100644 --- a/doc/gcrypt.texi +++ b/doc/gcrypt.texi @@ -3174,7 +3174,7 @@ The PKCS#5 Passphrase Based Key Derivation Function number 2. @item GCRY_KDF_SCRYPT The SCRYPT Key Derivation Function. The subalgorithm is used to specify -the CPU/memory cost paramter N, and the number of iterations +the CPU/memory cost parameter N, and the number of iterations is used for the parallelization parameter p. The block size is fixed at 8 in the current implementation. diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in index dff0e0b1..3b37e19c 100644 --- a/src/gcrypt.h.in +++ b/src/gcrypt.h.in @@ -1194,7 +1194,7 @@ enum gcry_kdf_algos GCRY_KDF_ITERSALTED_S2K = 19, GCRY_KDF_PBKDF1 = 33, GCRY_KDF_PBKDF2 = 34, - GCRY_KDF_SCRYPT = 35 + GCRY_KDF_SCRYPT = 48 }; /* Derive a key from a passphrase. */ diff --git a/tests/t-kdf.c b/tests/t-kdf.c index 06c00263..50deba08 100644 --- a/tests/t-kdf.c +++ b/tests/t-kdf.c @@ -35,6 +35,7 @@ /* Program option flags. */ static int verbose; +static int debug; static int error_count; static void @@ -925,7 +926,7 @@ check_pbkdf2 (void) 20, "\x13\x3a\x4c\xe8\x37\xb4\xd2\x52\x1e\xe2" "\xbf\x03\xe1\x1c\x71\xca\x79\x4e\x07\x97" - }, + } }; int tvidx; gpg_error_t err; @@ -957,11 +958,106 @@ check_pbkdf2 (void) } +static void +check_scrypt (void) +{ + /* Test vectors are from draft-josefsson-scrypt-kdf-01. */ + static struct { + const char *p; /* Passphrase. */ + size_t plen; /* Length of P. */ + const char *salt; + size_t saltlen; + int parm_n; /* CPU/memory cost. */ + int parm_r; /* blocksize */ + unsigned long parm_p; /* parallelization. */ + int dklen; /* Requested key length. */ + const char *dk; /* Derived key. */ + int disabled; + } tv[] = { + { + "", 0, + "", 0, + 16, + 1, + 1, + 64, + "\x77\xd6\x57\x62\x38\x65\x7b\x20\x3b\x19\xca\x42\xc1\x8a\x04\x97" + "\xf1\x6b\x48\x44\xe3\x07\x4a\xe8\xdf\xdf\xfa\x3f\xed\xe2\x14\x42" + "\xfc\xd0\x06\x9d\xed\x09\x48\xf8\x32\x6a\x75\x3a\x0f\xc8\x1f\x17" + "\xe8\xd3\xe0\xfb\x2e\x0d\x36\x28\xcf\x35\xe2\x0c\x38\xd1\x89\x06" + }, + { + "password", 8, + "NaCl", 4, + 1024, + 8, + 16, + 64, + "\xfd\xba\xbe\x1c\x9d\x34\x72\x00\x78\x56\xe7\x19\x0d\x01\xe9\xfe" + "\x7c\x6a\xd7\xcb\xc8\x23\x78\x30\xe7\x73\x76\x63\x4b\x37\x31\x62" + "\x2e\xaf\x30\xd9\x2e\x22\xa3\x88\x6f\xf1\x09\x27\x9d\x98\x30\xda" + "\xc7\x27\xaf\xb9\x4a\x83\xee\x6d\x83\x60\xcb\xdf\xa2\xcc\x06\x40" + }, + { + "pleaseletmein", 13, + "SodiumChloride", 14, + 16384, + 8, + 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" + }, + { + "pleaseletmein", 13, + "SodiumChloride", 14, + 1048576, + 8, + 1, + 64, + "\x21\x01\xcb\x9b\x6a\x51\x1a\xae\xad\xdb\xbe\x09\xcf\x70\xf8\x81" + "\xec\x56\x8d\x57\x4a\x2f\xfd\x4d\xab\xe5\xee\x98\x20\xad\xaa\x47" + "\x8e\x56\xfd\x8f\x4b\xa5\xd0\x9f\xfa\x1c\x6d\x92\x7c\x40\xf4\xc3" + "\x37\x30\x40\x49\xe8\xa9\x52\xfb\xcb\xf4\x5c\x6f\xa7\x7a\x41\xa4", + 2 /* Only in debug mode. */ + } + }; + int tvidx; + gpg_error_t err; + unsigned char outbuf[64]; + int i; + + for (tvidx=0; tvidx < DIM(tv); tvidx++) + { + if (tv[tvidx].disabled && !(tv[tvidx].disabled == 2 && debug)) + continue; + if (verbose) + fprintf (stderr, "checking SCRYPT test vector %d\n", tvidx); + assert (tv[tvidx].dklen <= sizeof outbuf); + err = gcry_kdf_derive (tv[tvidx].p, tv[tvidx].plen, + tv[tvidx].parm_r == 1 ? 41 : GCRY_KDF_SCRYPT, + tv[tvidx].parm_n, + tv[tvidx].salt, tv[tvidx].saltlen, + tv[tvidx].parm_p, tv[tvidx].dklen, outbuf); + if (err) + fail ("scrypt test %d failed: %s\n", tvidx, gpg_strerror (err)); + else if (memcmp (outbuf, tv[tvidx].dk, tv[tvidx].dklen)) + { + fail ("scrypt 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) { - int debug = 0; - if (argc > 1 && !strcmp (argv[1], "--verbose")) verbose = 1; else if (argc > 1 && !strcmp (argv[1], "--debug")) @@ -977,6 +1073,7 @@ main (int argc, char **argv) check_openpgp (); check_pbkdf2 (); + check_scrypt (); return error_count ? 1 : 0; } |