Add test case for SCRYPT and rework the code.
authorWerner Koch <wk@gnupg.org>
Fri, 5 Apr 2013 10:23:41 +0000 (12:23 +0200)
committerWerner Koch <wk@gnupg.org>
Fri, 5 Apr 2013 10:24:06 +0000 (12:24 +0200)
* tests/t-kdf.c (check_scrypt): New.
(main): Call new test.

* configure.ac: Support disabling of the scrypt algorithm.  Make KDF
enabling similar to the other algorithm classes.  Disable scrypt if we
don't have a 64 bit type.
* cipher/memxor.c, cipher/memxor.h: Remove.
* cipher/scrypt.h: Remove.
* cipher/kdf-internal.h: New.
* cipher/Makefile.am: Remove files.  Add new file.  Move scrypt.c to
EXTRA_libcipher_la_SOURCES.
(GCRYPT_MODULES): Add GCRYPT_KDFS.
* src/gcrypt.h.in (GCRY_KDF_SCRYPT): Change value.
* cipher/kdf.c (pkdf2): Rename to _gcry_kdf_pkdf2.
(_gcry_kdf_pkdf2): Don't bail out for SALTLEN==0.
(gcry_kdf_derive): Allow for a passwordlen of zero for scrypt.  Check
for SALTLEN > 0 for GCRY_KDF_PBKDF2.  Pass algo to _gcry_kdf_scrypt.
(gcry_kdf_derive) [!USE_SCRYPT]: Return an error.
* cipher/scrypt.c: Replace memxor.h by bufhelp.h.  Replace scrypt.h by
kdf-internal.h.  Enable code only if HAVE_U64_TYPEDEF is defined.
Replace C99 types uint64_t, uint32_t, and uint8_t by libgcrypt types.
(_SALSA20_INPUT_LENGTH): Remove underscore from identifier.
(_scryptBlockMix): Replace memxor by buf_xor.
(_gcry_kdf_scrypt): Use gcry_malloc and gcry_free.  Check for integer
overflow.  Add hack to support blocksize of 1 for tests.  Return
errors from calls to _gcry_kdf_pkdf2.

* cipher/kdf.c (openpgp_s2k): Make static.
--

This patch prepares the addition of more KDF functions, brings the
code into Libgcrypt shape, adds a test case and makes the code more
robust.  For example, scrypt would have fail silently if Libgcrypt was
not build with SHA256 support.  Also fixed symbol naming for systems
without a visibility support.

Signed-off-by: Werner Koch <wk@gnupg.org>
12 files changed:
NEWS
cipher/Makefile.am
cipher/kdf-internal.h [new file with mode: 0644]
cipher/kdf.c
cipher/memxor.c [deleted file]
cipher/memxor.h [deleted file]
cipher/scrypt.c
cipher/scrypt.h [deleted file]
configure.ac
doc/gcrypt.texi
src/gcrypt.h.in
tests/t-kdf.c

diff --git a/NEWS b/NEWS
index 0d75680..926e531 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -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)
index 5b016f0..5189794 100644 (file)
@@ -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 (file)
index 0000000..7079860
--- /dev/null
@@ -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*/
index 4ea0fb2..da63574 100644 (file)
@@ -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 (file)
index 74307f0..0000000
+++ /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 (file)
index f308155..0000000
+++ /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 */
index 45ab4b3..06196d6 100644 (file)
@@ -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
 #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]))
 
 
 
 
 
 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 (file)
index e0c8df9..0000000
+++ /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 */
index cebf4b9..c597389 100644 (file)
@@ -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])
index fe040f0..8f277c2 100644 (file)
@@ -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.
 
index dff0e0b..3b37e19 100644 (file)
@@ -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.  */
index 06c0026..50deba0 100644 (file)
@@ -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;
 }