Make use of gcry_kdf_derive.
authorWerner Koch <wk@gnupg.org>
Thu, 10 Mar 2011 17:39:34 +0000 (18:39 +0100)
committerWerner Koch <wk@gnupg.org>
Thu, 10 Mar 2011 17:39:34 +0000 (18:39 +0100)
Factoring common code out is always a Good Thing.  Also added a
configure test to print an error if gcry_kdf_derive is missing in
Libgcrypt.

agent/ChangeLog
agent/protect.c
configure.ac
g10/ChangeLog
g10/passphrase.c

index b636c50..5f14306 100644 (file)
@@ -1,3 +1,7 @@
+2011-03-10  Werner Koch  <wk@g10code.com>
+
+       * protect.c (hash_passphrase): Use the new gcry_kdf_derive.
+
 2011-03-08  Werner Koch  <wk@g10code.com>
 
        * cvt-openpgp.c (GCRY_PK_ECDH) [!HAVE_GCRY_PK_ECDH]: Remove.
index 94de893..0b8c9b4 100644 (file)
@@ -1023,70 +1023,13 @@ hash_passphrase (const char *passphrase, int hashalgo,
                  unsigned long s2kcount,
                  unsigned char *key, size_t keylen)
 {
-  int rc;
-  gcry_md_hd_t md;
-  int pass, i;
-  int used = 0;
-  int pwlen = strlen (passphrase);
-
-  if ( (s2kmode != 0 && s2kmode != 1 && s2kmode != 3)
-      || !hashalgo || !keylen || !key || !passphrase)
-    return gpg_error (GPG_ERR_INV_VALUE);
-  if ((s2kmode == 1 ||s2kmode == 3) && !s2ksalt)
-    return gpg_error (GPG_ERR_INV_VALUE);
-
-  rc = gcry_md_open (&md, hashalgo, GCRY_MD_FLAG_SECURE);
-  if (rc)
-    return rc;
-
-  for (pass=0; used < keylen; pass++)
-    {
-      if (pass)
-        {
-          gcry_md_reset (md);
-          for (i=0; i < pass; i++) /* preset the hash context */
-            gcry_md_putc (md, 0);
-       }
-
-      if (s2kmode == 1 || s2kmode == 3)
-        {
-          int len2 = pwlen + 8;
-          unsigned long count = len2;
-
-          if (s2kmode == 3)
-            {
-              count = s2kcount;
-              if (count < len2)
-                count = len2;
-            }
 
-          while (count > len2)
-            {
-              gcry_md_write (md, s2ksalt, 8);
-              gcry_md_write (md, passphrase, pwlen);
-              count -= len2;
-            }
-          if (count < 8)
-            gcry_md_write (md, s2ksalt, count);
-          else
-            {
-              gcry_md_write (md, s2ksalt, 8);
-              count -= 8;
-              gcry_md_write (md, passphrase, count);
-            }
-        }
-      else
-        gcry_md_write (md, passphrase, pwlen);
-
-      gcry_md_final (md);
-      i = gcry_md_get_algo_dlen (hashalgo);
-      if (i > keylen - used)
-        i = keylen - used;
-      memcpy  (key+used, gcry_md_read (md, hashalgo), i);
-      used += i;
-    }
-  gcry_md_close(md);
-  return 0;
+  return gcry_kdf_derive (passphrase, strlen (passphrase),
+                          s2kmode == 3? GCRY_KDF_ITERSALTED_S2K :
+                          s2kmode == 1? GCRY_KDF_SALTED_S2K :
+                          s2kmode == 0? GCRY_KDF_SIMPLE_S2K : GCRY_KDF_NONE,
+                          hashalgo, s2ksalt, 8, s2kcount,
+                          keylen, key);
 }
 
 
index f265dc3..ab40c6f 100644 (file)
@@ -742,6 +742,22 @@ AM_PATH_GPG_ERROR("$NEED_GPG_ERROR_VERSION",
 AM_PATH_LIBGCRYPT("$NEED_LIBGCRYPT_API:$NEED_LIBGCRYPT_VERSION",
         have_libgcrypt=yes,have_libgcrypt=no)
 
+# FIxme: Remove this test after libgcrypt 1.5.0 has been released.
+AC_CACHE_CHECK([whether Libgcrypt has gcry_kdf_derive],
+                gnupg_cv_gcry_kdf_derive,
+               [ _gnupg_gcry_save_cflags=$CFLAGS
+                 _gnupg_gcry_save_libs=$LIBS
+                 CFLAGS="$CFLAGS $LIBGCRYPT_CFLAGS"
+                 LIBS="$LIBS $LIBGCRYPT_LIBS"
+                 AC_TRY_LINK(
+                   [#include <gcrypt.h>],
+                   [ return gcry_kdf_derive (NULL,0,0,0,NULL,0,0,0,NULL); ],
+                   gnupg_cv_gcry_kdf_derive=yes,
+                   gnupg_cv_gcry_kdf_derive=no)
+                 LIBS=$_gnupg_gcry_save_libs
+                 CFLAGS=$_gnupg_gcry_save_cflags])
+
+
 #
 # libassuan is used for IPC
 #
@@ -1605,6 +1621,15 @@ if test "$have_libgcrypt" = "no"; then
 ***   ftp://ftp.gnupg.org/gcrypt/alpha/libgcrypt/
 *** (at least version $NEED_LIBGCRYPT_VERSION using API $NEED_LIBGCRYPT_API is required.)
 ***]])
+elif test "$gnupg_cv_gcry_kdf_derive" = no; then
+   die=yes
+   AC_MSG_NOTICE([[
+***
+*** Libgcrypt 1.5.0 has not yet been released and thus the API
+*** is a bit in a flux.  Your version misses the function
+***        gcry_kdf_derive
+*** You need to install a newer Libgcrypt version.
+***]])
 fi
 if test "$have_libassuan" = "no"; then
    die=yes
index 9026077..f9edf57 100644 (file)
@@ -1,3 +1,8 @@
+2011-03-10  Werner Koch  <wk@g10code.com>
+
+       * passphrase.c (hash_passphrase): Remove.
+       (passphrase_to_dek_ext): Use gcry_kdf_derive.
+
 2011-03-03  Werner Koch  <wk@g10code.com>
 
        * keylist.c (print_card_key_info): Re-implement using the agent.
index 8065810..481d29e 100644 (file)
@@ -1,6 +1,6 @@
 /* passphrase.c -  Get a passphrase
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
- *               2005, 2006, 2007, 2009 Free Software Foundation, Inc.
+ *               2005, 2006, 2007, 2009, 2011 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -101,81 +101,6 @@ encode_s2k_iterations (int iterations)
 }
 
 
-
-/* Hash a passphrase using the supplied s2k.
-   Always needs: dek->algo, s2k->mode, s2k->hash_algo.  */
-static void
-hash_passphrase ( DEK *dek, char *pw, STRING2KEY *s2k)
-{
-  gcry_md_hd_t md;
-  int pass, i;
-  int used = 0;
-  int pwlen = strlen(pw);
-
-  assert ( s2k->hash_algo );
-  dek->keylen = openpgp_cipher_get_algo_keylen (dek->algo);
-  if ( !(dek->keylen > 0 && dek->keylen <= DIM(dek->key)) )
-    BUG();
-
-  if (gcry_md_open (&md, s2k->hash_algo, 1))
-    BUG ();
-  for (pass=0; used < dek->keylen ; pass++ )
-    {
-      if ( pass )
-        {
-          gcry_md_reset (md);
-          for (i=0; i < pass; i++ ) /* Preset the hash context.  */
-            gcry_md_putc (md, 0 );
-       }
-
-      if ( s2k->mode == 1 || s2k->mode == 3 )
-        {
-          int len2 = pwlen + 8;
-          ulong count = len2;
-
-          if ( s2k->mode == 3 )
-            {
-              count = S2K_DECODE_COUNT(s2k->count);
-              if ( count < len2 )
-                count = len2;
-           }
-
-          /* Fixme: To avoid DoS attacks by sending an sym-encrypted
-             packet with a very high S2K count, we should either cap
-             the iteration count or CPU seconds based timeout.  */
-
-          /* A little bit complicated because we need a ulong for count. */
-          while ( count > len2 )  /* maybe iterated+salted */
-            {
-              gcry_md_write ( md, s2k->salt, 8 );
-              gcry_md_write ( md, pw, pwlen );
-              count -= len2;
-           }
-          if ( count < 8 )
-            gcry_md_write ( md, s2k->salt, count );
-          else
-            {
-              gcry_md_write ( md, s2k->salt, 8 );
-              count -= 8;
-              gcry_md_write ( md, pw, count );
-           }
-       }
-      else
-        gcry_md_write ( md, pw, pwlen );
-      gcry_md_final( md );
-
-      i = gcry_md_get_algo_dlen ( s2k->hash_algo );
-      if ( i > dek->keylen - used )
-        i = dek->keylen - used;
-
-      memcpy (dek->key+used, gcry_md_read (md, s2k->hash_algo), i);
-      used += i;
-    }
-  gcry_md_close(md);
-}
-
-
-
 int
 have_static_passphrase()
 {
@@ -655,7 +580,24 @@ passphrase_to_dek_ext (u32 *keyid, int pubkey_algo,
   if ( (!pw || !*pw) && (mode == 2 || mode == 4))
     dek->keylen = 0;
   else
-    hash_passphrase (dek, pw, s2k);
+    {
+      dek->keylen = openpgp_cipher_get_algo_keylen (dek->algo);
+      if (!(dek->keylen > 0 && dek->keylen <= DIM(dek->key)))
+        BUG ();
+      if (gcry_kdf_derive (pw, strlen (pw),
+                           s2k->mode == 3? GCRY_KDF_ITERSALTED_S2K :
+                           s2k->mode == 1? GCRY_KDF_SALTED_S2K :
+                           /* */           GCRY_KDF_SIMPLE_S2K,
+                           s2k->hash_algo, s2k->salt, 8,
+                           S2K_DECODE_COUNT(s2k->count),
+                           dek->keylen, dek->key))
+        {
+          xfree (pw);
+          xfree (dek);
+         write_status( STATUS_MISSING_PASSPHRASE );
+          return NULL;
+        }
+    }
   if (s2k_cacheid)
     memcpy (dek->s2k_cacheid, s2k_cacheid, sizeof dek->s2k_cacheid);
   xfree(last_pw);