Switch PBKDF2 from internal SHA1 to libgcrypt.
authorMilan Broz <gmazyland@gmail.com>
Thu, 30 Jul 2009 14:56:59 +0000 (14:56 +0000)
committerMilan Broz <gmazyland@gmail.com>
Thu, 30 Jul 2009 14:56:59 +0000 (14:56 +0000)
Also make hash algorithm not fixed here.

git-svn-id: https://cryptsetup.googlecode.com/svn/trunk@79 36d66b0a-2a48-0410-832c-cd162a569da5

ChangeLog
luks/af.c
luks/af.h
luks/keymanage.c
luks/pbkdf.c
luks/pbkdf.h

index 5b44845..ce82339 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,6 +3,7 @@
        * Print error when getline fails.
        * Remove po/cryptsetup-luks.pot, it's autogenerated.
        * Return ENOENT for empty keyslots, EINVAL will be used later for other type of error.
+       * Switch PBKDF2 from internal SHA1 to libgcrypt, make hash algorithm not hardcoded to SHA1 here.
 
 2009-07-28  Milan Broz  <mbroz@redhat.com>
        * Pad luks header to 512 sector size.
index faa77f2..31a0197 100644 (file)
--- a/luks/af.c
+++ b/luks/af.c
@@ -1,6 +1,7 @@
 /*
  * AFsplitter - Anti forensic information splitter
  * Copyright 2004, Clemens Fruhwirth <clemens@endorphin.org>
+ * Copyright (C) 2009 Red Hat, Inc. All rights reserved.
  *
  * AFsplitter diffuses information over a large stripe of data, 
  * therefor supporting secure data destruction.
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
  
-#include <stdio.h>
+#include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
 #include <netinet/in.h>
 #include <errno.h>
+#include <gcrypt.h>
 #include "sha1.h"
 #include "XORblock.h"
 #include "random.h"
 
+static int hash_buf(char *src, char *dst, uint32_t iv, int len, int hash_id)
+{
+       gcry_md_hd_t hd;
+       unsigned char *digest;
+
+       iv = htonl(iv);
+       if (gcry_md_open(&hd, hash_id, 0))
+               return 1;
+       gcry_md_write(hd, (unsigned char *)&iv, sizeof(iv));
+       gcry_md_write(hd, src, len);
+       digest = gcry_md_read(hd, hash_id);
+       memcpy(dst, digest, len);
+       gcry_md_close(hd);
+       return 0;
+}
+
 /* diffuse: Information spreading over the whole dataset with
- * the help of sha512. 
+ * the help of hash function.
  */
 
-static void diffuse(unsigned char *src, unsigned char *dst, size_t size)
+static int diffuse(char *src, char *dst, size_t size, int hash_id)
 {
-       sha1_ctx ctx;
-       uint32_t i;
-       uint32_t IV;    /* host byte order independend hash IV */
-       
-       unsigned int fullblocks = size / SHA1_DIGEST_SIZE;
-       unsigned int padding = size % SHA1_DIGEST_SIZE;
-       unsigned char final[SHA1_DIGEST_SIZE];
-
-       /* hash block the whole data set with different IVs to produce
-        * more than just a single data block
-        */
-       for (i=0; i < fullblocks; i++) {
-               sha1_begin(&ctx);
-               IV = htonl(i);
-               sha1_hash((const unsigned char *) &IV, sizeof(IV), &ctx);
-               sha1_hash(src + SHA1_DIGEST_SIZE * i, SHA1_DIGEST_SIZE, &ctx);
-               sha1_end(dst + SHA1_DIGEST_SIZE * i, &ctx);
-       }
+       unsigned int digest_size = gcry_md_get_algo_dlen(hash_id);
+       unsigned int i, blocks, padding;
 
-       if(padding) {
-               sha1_begin(&ctx);
-               IV = htonl(i);
-               sha1_hash((const unsigned char *) &IV, sizeof(IV), &ctx);
-               sha1_hash(src + SHA1_DIGEST_SIZE * i, padding, &ctx);
-               sha1_end(final, &ctx);
-               memcpy(dst + SHA1_DIGEST_SIZE * i, final, padding);
-       }
+       blocks = size / digest_size;
+       padding = size % digest_size;
+
+       for (i = 0; i < blocks; i++)
+               if(hash_buf(src + digest_size * i,
+                           dst + digest_size * i,
+                           i, digest_size, hash_id))
+                       return 1;
+
+       if(padding)
+               if(hash_buf(src + digest_size * i,
+                           dst + digest_size * i,
+                           i, padding, hash_id))
+                       return 1;
+
+       return 0;
 }
 
 /*
@@ -73,11 +79,15 @@ static void diffuse(unsigned char *src, unsigned char *dst, size_t size)
  * must be supplied to AF_merge to recover information.
  */
 
-int AF_split(char *src, char *dst, size_t blocksize, unsigned int blocknumbers)
+int AF_split(char *src, char *dst, size_t blocksize, unsigned int blocknumbers, const char *hash)
 {
        unsigned int i;
        char *bufblock;
        int r = -EINVAL;
+       int hash_id;
+
+       if (!(hash_id = gcry_md_map_name(hash)))
+               return -EINVAL;
 
        if((bufblock = calloc(blocksize, 1)) == NULL) return -ENOMEM;
 
@@ -87,7 +97,8 @@ int AF_split(char *src, char *dst, size_t blocksize, unsigned int blocknumbers)
                if(r < 0) goto out;
 
                XORblock(dst+(blocksize*i),bufblock,bufblock,blocksize);
-               diffuse((unsigned char *) bufblock, (unsigned char *) bufblock, blocksize);
+               if(diffuse(bufblock, bufblock, blocksize, hash_id))
+                       goto out;
        }
        /* the last block is computed */
        XORblock(src,bufblock,dst+(i*blocksize),blocksize);
@@ -97,20 +108,27 @@ out:
        return r;
 }
 
-int AF_merge(char *src, char *dst, size_t blocksize, unsigned int blocknumbers)
+int AF_merge(char *src, char *dst, size_t blocksize, unsigned int blocknumbers, const char *hash)
 {
        unsigned int i;
        char *bufblock;
+       int r = -EINVAL;
+       int hash_id;
+
+       if (!(hash_id = gcry_md_map_name(hash)))
+               return -EINVAL;
 
        if((bufblock = calloc(blocksize, 1)) == NULL) return -ENOMEM;
 
        memset(bufblock,0,blocksize);
        for(i=0; i<blocknumbers-1; i++) {
                XORblock(src+(blocksize*i),bufblock,bufblock,blocksize);
-               diffuse((unsigned char *) bufblock, (unsigned char *) bufblock, blocksize);
+               if(diffuse(bufblock, bufblock, blocksize, hash_id))
+                       goto out;
        }
        XORblock(src + blocksize * i, bufblock, dst, blocksize);
-
-       free(bufblock); 
+       r = 0;
+out:
+       free(bufblock);
        return 0;
 }
index ae4feb6..78f978c 100644 (file)
--- a/luks/af.h
+++ b/luks/af.h
@@ -7,19 +7,19 @@
  */
 
 /*
- * AF_split operates on src and produces information splitted data in 
+ * AF_split operates on src and produces information splitted data in
  * dst. src is assumed to be of the length blocksize. The data stripe
- * dst points to must be captable of storing blocksize*blocknumbers. 
+ * dst points to must be captable of storing blocksize*blocknumbers.
  * blocknumbers is the data multiplication factor.
  *
- * AF_merge does just the opposite: reproduces the information stored in 
- * src of the length blocksize*blocknumbers into dst of the length 
+ * AF_merge does just the opposite: reproduces the information stored in
+ * src of the length blocksize*blocknumbers into dst of the length
  * blocksize.
  *
  * On error, both functions return -1, 0 otherwise.
- */ 
+ */
 
-int AF_split(char *src, char *dst, size_t blocksize, unsigned int blocknumbers);
-int AF_merge(char *src, char *dst, size_t blocksize, unsigned int blocknumbers);
+int AF_split(char *src, char *dst, size_t blocksize, unsigned int blocknumbers, const char *hash);
+int AF_merge(char *src, char *dst, size_t blocksize, unsigned int blocknumbers, const char *hash);
 
 #endif
index 4a502dc..6011b90 100644 (file)
@@ -191,14 +191,21 @@ int LUKS_generate_phdr(struct luks_phdr *header,
        header->keyBytes=mk->keyLength;
 
        r = getRandom(header->mkDigestSalt,LUKS_SALTSIZE);
-       if(r < 0) return r;
+       if(r < 0) {
+               set_error( _("Cannot create LUKS header: reading random salt failed."));
+               return r;
+       }
 
        /* Compute master key digest */
        header->mkDigestIterations = LUKS_MKD_ITER;
-       PBKDF2_HMAC_SHA1(mk->key,mk->keyLength,
-                        header->mkDigestSalt,LUKS_SALTSIZE,
-                        header->mkDigestIterations,
-                        header->mkDigest,LUKS_DIGESTSIZE);
+       r = PBKDF2_HMAC(header->hashSpec,mk->key,mk->keyLength,
+                       header->mkDigestSalt,LUKS_SALTSIZE,
+                       header->mkDigestIterations,
+                       header->mkDigest,LUKS_DIGESTSIZE);
+       if(r < 0) {
+               set_error( _("Cannot create LUKS header: header digest failed (using hash %s)."), header->hashSpec);
+               return r;
+       }
 
        currentSector = round_up_modulo(LUKS_PHDR_SIZE, alignSectors);
        for(i = 0; i < LUKS_NUMKEYS; ++i) {
@@ -241,18 +248,20 @@ int LUKS_set_key(const char *device, unsigned int keyIndex,
 
 //     assert((mk->keyLength % TWOFISH_BLOCKSIZE) == 0); FIXME
 
-       PBKDF2_HMAC_SHA1(password,passwordLen,
-                        hdr->keyblock[keyIndex].passwordSalt,LUKS_SALTSIZE,
-                        hdr->keyblock[keyIndex].passwordIterations,
-                        derivedKey, hdr->keyBytes);
+       r = PBKDF2_HMAC(hdr->hashSpec, password,passwordLen,
+                       hdr->keyblock[keyIndex].passwordSalt,LUKS_SALTSIZE,
+                       hdr->keyblock[keyIndex].passwordIterations,
+                       derivedKey, hdr->keyBytes);
+       if(r < 0) return r;
+
        /*
         * AF splitting, the masterkey stored in mk->key is splitted to AfMK
         */
        AFEKSize = hdr->keyblock[keyIndex].stripes*mk->keyLength;
        AfKey = (char *)malloc(AFEKSize);
        if(AfKey == NULL) return -ENOMEM;
-       
-       r = AF_split(mk->key,AfKey,mk->keyLength,hdr->keyblock[keyIndex].stripes);
+
+       r = AF_split(mk->key,AfKey,mk->keyLength,hdr->keyblock[keyIndex].stripes,hdr->hashSpec);
        if(r < 0) goto out;
 
        /* Encryption via dm */
@@ -305,11 +314,12 @@ int LUKS_open_key(const char *device,
        AFEKSize = hdr->keyblock[keyIndex].stripes*mk->keyLength;
        AfKey = (char *)malloc(AFEKSize);
        if(AfKey == NULL) return -ENOMEM;
-       
-       PBKDF2_HMAC_SHA1(password,passwordLen,
-                        hdr->keyblock[keyIndex].passwordSalt,LUKS_SALTSIZE,
-                        hdr->keyblock[keyIndex].passwordIterations,
-                        derivedKey, hdr->keyBytes);
+
+       r = PBKDF2_HMAC(hdr->hashSpec, password,passwordLen,
+                       hdr->keyblock[keyIndex].passwordSalt,LUKS_SALTSIZE,
+                       hdr->keyblock[keyIndex].passwordIterations,
+                       derivedKey, hdr->keyBytes);
+       if(r < 0) goto out;
 
        r = LUKS_decrypt_from_storage(AfKey,
                                      AFEKSize,
@@ -319,23 +329,24 @@ int LUKS_open_key(const char *device,
                                      device,
                                      hdr->keyblock[keyIndex].keyMaterialOffset,
                                      backend);
-       if(r < 0) {
-               if(!get_error())
-                       set_error("Failed to read from key storage");
-               goto out;
-       }
+       if(r < 0) goto out;
+
+       r = AF_merge(AfKey,mk->key,mk->keyLength,hdr->keyblock[keyIndex].stripes,hdr->hashSpec);
+       if(r < 0) goto out;
 
-       r = AF_merge(AfKey,mk->key,mk->keyLength,hdr->keyblock[keyIndex].stripes);
+       r = PBKDF2_HMAC(hdr->hashSpec,mk->key,mk->keyLength,
+                       hdr->mkDigestSalt,LUKS_SALTSIZE,
+                       hdr->mkDigestIterations,
+                       checkHashBuf,LUKS_DIGESTSIZE);
        if(r < 0) goto out;
-       
-       PBKDF2_HMAC_SHA1(mk->key,mk->keyLength,
-                        hdr->mkDigestSalt,LUKS_SALTSIZE,
-                        hdr->mkDigestIterations,
-                        checkHashBuf,LUKS_DIGESTSIZE);
 
        r = (memcmp(checkHashBuf,hdr->mkDigest, LUKS_DIGESTSIZE) == 0)?0:-EPERM;
 out:
        free(AfKey);
+
+       if( r < 0 && !get_error())
+               set_error("Failed to read from key storage.");
+
        return r;
 }
 
@@ -489,7 +500,15 @@ int LUKS_is_last_keyslot(const char *device, unsigned int keyIndex)
 
 int LUKS_benchmarkt_iterations()
 {
-       return PBKDF2_performance_check()/2;
+       unsigned int count;
+       char *hash = "sha1";
+
+       if (PBKDF2_performance_check(hash, &count) < 0) {
+               set_error(_("Not compatible options (using hash algorithm %s)."), hash);
+               return -EINVAL;
+       }
+
+       return count/2;
 }
 
 int LUKS_device_ready(const char *device, int mode)
index 5be2b26..5633cd1 100644 (file)
-/*
- * Copyright 2004 Clemens Fruhwirth <clemens@endorphin.org>
- * Implementation of PBKDF2-HMAC-SHA1 according to RFC 2898.
+/* Implementation of Password-Based Cryptography as per PKCS#5
+ * Copyright (C) 2002,2003 Simon Josefsson
+ * Copyright (C) 2004 Free Software Foundation
+ *
+ * LUKS code
+ * Copyright (C) 2004 Clemens Fruhwirth <clemens@endorphin.org>
+ * Copyright (C) 2009 Red Hat, Inc. All rights reserved.
  *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
+ * This file 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.
  *
- * This program is distributed in the hope that it will be useful,
+ * This file 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 Library General Public License for more details.
+ * 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 file; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
+
 #include <netinet/in.h>
 #include <errno.h>
 #include <signal.h>
+#include <alloca.h>
 #include <sys/time.h>
+#include <gcrypt.h>
 
-#include "hmac_sha1.h"
-#include "XORblock.h"
-#include <assert.h>
+static volatile unsigned int __PBKDF2_global_j = 0;
+static volatile unsigned int __PBKDF2_performance = 0;
 
-static unsigned int *__PBKDF2_global_j;
-static unsigned int __PBKDF2_performance=0;
+static int init_crypto(void)
+{
+       if (!gcry_control (GCRYCTL_INITIALIZATION_FINISHED_P)) {
+               if (!gcry_check_version (GCRYPT_VERSION))
+                       return -ENOSYS;
+               gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
+               gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
+               gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
+               gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+       }
 
-void PBKDF2_HMAC_SHA1(const char *password, size_t passwordLen, 
-                     const char *salt, size_t saltLen, unsigned int iterations, 
-                     char *dKey, size_t dKeyLen)
+       return 0;
+}
+
+/*
+ * 5.2 PBKDF2
+ *
+ *  PBKDF2 applies a pseudorandom function (see Appendix B.1 for an
+ *  example) to derive keys. The length of the derived key is essentially
+ *  unbounded. (However, the maximum effective search space for the
+ *  derived key may be limited by the structure of the underlying
+ *  pseudorandom function. See Appendix B.1 for further discussion.)
+ *  PBKDF2 is recommended for new applications.
+ *
+ *  PBKDF2 (P, S, c, dkLen)
+ *
+ *  Options:        PRF        underlying pseudorandom function (hLen
+ *                             denotes the length in octets of the
+ *                             pseudorandom function output)
+ *
+ *  Input:          P          password, an octet string (ASCII or UTF-8)
+ *                  S          salt, an octet string
+ *                  c          iteration count, a positive integer
+ *                  dkLen      intended length in octets of the derived
+ *                             key, a positive integer, at most
+ *                             (2^32 - 1) * hLen
+ *
+ *  Output:         DK         derived key, a dkLen-octet string
+ */
+
+#define MAX_PRF_BLOCK_LEN 80
+
+static int pkcs5_pbkdf2(const char *hash,
+                       const char *P, size_t Plen,
+                       const char *S, size_t Slen,
+                       unsigned int c, unsigned int dkLen,
+                       char *DK, int perfcheck)
 {
-       uint32_t i=1;
-       unsigned int j;
-       /* U_n is the buffer for U_n values */
-       unsigned char U_n[SHA1_DIGEST_SIZE];
-       /* F_buf is the XOR buffer for F function */
-       char F_buf[SHA1_DIGEST_SIZE];
-       hmac_ctx templateCtx;
-
-       /* We need a global pointer for signal handlers */
-       __PBKDF2_global_j = &j;
-
-       /* Make a template context initialized with password as key */
-       hmac_sha_begin(&templateCtx);
-       hmac_sha_key((unsigned char *) password,passwordLen,&templateCtx);
-       
-#define HMAC_REINIT(__ctx)             memcpy(&__ctx,&templateCtx,sizeof(__ctx))
-       
-       /* The first hash iteration is done different, therefor 
-               we reduce iterations to conveniently use it as a loop 
-               counter */
-       assert(iterations != 0);
-       iterations--; 
-
-       while(dKeyLen > 0) {
-               hmac_ctx ctx;
-               uint32_t iNetworkOrdered;
-               unsigned int blocksize = dKeyLen<SHA1_DIGEST_SIZE?dKeyLen:SHA1_DIGEST_SIZE;
-
-               j=iterations;
-               HMAC_REINIT(ctx);
-               // U_1 hashing 
-               hmac_sha_data((unsigned char *) salt,saltLen,&ctx);
-               iNetworkOrdered = htonl(i);
-               hmac_sha_data((unsigned char *)&iNetworkOrdered, sizeof(uint32_t), &ctx);
-               hmac_sha_end(U_n, SHA1_DIGEST_SIZE, &ctx);
-               memcpy(F_buf, U_n, SHA1_DIGEST_SIZE);
-
-               // U_n hashing
-               while(j--) {
-                       HMAC_REINIT(ctx);
-                       hmac_sha_data(U_n,SHA1_DIGEST_SIZE, &ctx);
-                       hmac_sha_end(U_n,SHA1_DIGEST_SIZE, &ctx);
-                       XORblock(F_buf,(char*) U_n,F_buf,SHA1_DIGEST_SIZE);
+       gcry_md_hd_t prf;
+       gcry_error_t err;
+       char U[MAX_PRF_BLOCK_LEN];
+       char T[MAX_PRF_BLOCK_LEN];
+       int PRF;
+       unsigned int u;
+       unsigned int hLen;
+       unsigned int l;
+       unsigned int r;
+       int rc;
+       unsigned char *p;
+       int i;
+       int k;
+
+       if (init_crypto())
+               return -ENOSYS;
+
+       PRF = gcry_md_map_name(hash);
+       if (PRF == 0)
+               return -EINVAL;
+
+       hLen = gcry_md_get_algo_dlen(PRF);
+       if (hLen == 0 || hLen > MAX_PRF_BLOCK_LEN)
+               return -EINVAL;
+
+       if (c == 0)
+               return -EINVAL;
+
+       if (dkLen == 0)
+               return -EINVAL;
+
+       /*
+        *
+        *  Steps:
+        *
+        *     1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and
+        *        stop.
+        */
+
+       if (dkLen > 4294967295U)
+               return -EINVAL;
+
+       /*
+        *     2. Let l be the number of hLen-octet blocks in the derived key,
+        *        rounding up, and let r be the number of octets in the last
+        *        block:
+        *
+        *                  l = CEIL (dkLen / hLen) ,
+        *                  r = dkLen - (l - 1) * hLen .
+        *
+        *        Here, CEIL (x) is the "ceiling" function, i.e. the smallest
+        *        integer greater than, or equal to, x.
+        */
+
+       l = dkLen / hLen;
+       if (dkLen % hLen)
+               l++;
+       r = dkLen - (l - 1) * hLen;
+
+       /*
+        *     3. For each block of the derived key apply the function F defined
+        *        below to the password P, the salt S, the iteration count c, and
+        *        the block index to compute the block:
+        *
+        *                  T_1 = F (P, S, c, 1) ,
+        *                  T_2 = F (P, S, c, 2) ,
+        *                  ...
+        *                  T_l = F (P, S, c, l) ,
+        *
+        *        where the function F is defined as the exclusive-or sum of the
+        *        first c iterates of the underlying pseudorandom function PRF
+        *        applied to the password P and the concatenation of the salt S
+        *        and the block index i:
+        *
+        *                  F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c
+        *
+        *        where
+        *
+        *                  U_1 = PRF (P, S || INT (i)) ,
+        *                  U_2 = PRF (P, U_1) ,
+        *                  ...
+        *                  U_c = PRF (P, U_{c-1}) .
+        *
+        *        Here, INT (i) is a four-octet encoding of the integer i, most
+        *        significant octet first.
+        *
+        *     4. Concatenate the blocks and extract the first dkLen octets to
+        *        produce a derived key DK:
+        *
+        *                  DK = T_1 || T_2 ||  ...  || T_l<0..r-1>
+        *
+        *     5. Output the derived key DK.
+        *
+        *  Note. The construction of the function F follows a "belt-and-
+        *  suspenders" approach. The iterates U_i are computed recursively to
+        *  remove a degree of parallelism from an opponent; they are exclusive-
+        *  ored together to reduce concerns about the recursion degenerating
+        *  into a small set of values.
+        *
+        */
+
+       err = gcry_md_open(&prf, PRF, GCRY_MD_FLAG_HMAC);
+       if (err)
+               return -EINVAL;
+
+       for (i = 1; (uint) i <= l; i++) {
+               memset(T, 0, hLen);
+
+               for (u = 1; u <= c ; u++) {
+                       gcry_md_reset(prf);
+
+                       rc = gcry_md_setkey(prf, P, Plen);
+                       if (rc)
+                               return -EINVAL;
+
+                       if (u == 1) {
+                               char *tmp;
+                               size_t tmplen = Slen + 4;
+
+                               tmp = alloca(tmplen);
+                               if (tmp == NULL)
+                                       return -ENOMEM;
+
+                               memcpy(tmp, S, Slen);
+                               tmp[Slen + 0] = (i & 0xff000000) >> 24;
+                               tmp[Slen + 1] = (i & 0x00ff0000) >> 16;
+                               tmp[Slen + 2] = (i & 0x0000ff00) >> 8;
+                               tmp[Slen + 3] = (i & 0x000000ff) >> 0;
+
+                               gcry_md_write(prf, tmp, tmplen);
+                       } else {
+                               gcry_md_write(prf, U, hLen);
+                       }
+
+                       p = gcry_md_read(prf, PRF);
+                       if (p == NULL)
+                               return -EINVAL;
+
+                       memcpy(U, p, hLen);
+
+                       for (k = 0; (uint) k < hLen; k++)
+                               T[k] ^= U[k];
+
+                       if (perfcheck && __PBKDF2_performance)
+                               goto out;
+
+                       if (perfcheck)
+                               __PBKDF2_global_j--;
                }
-               memcpy(dKey,F_buf,blocksize);
-               dKey+=blocksize; dKeyLen-=blocksize; i++;
+
+               memcpy(DK + (i - 1) * hLen, T, (uint) i == l ? r : hLen);
        }
-#undef HMAC_REINIT
+out:
+       gcry_md_close(prf);
+
+       return 0;
+}
+
+int PBKDF2_HMAC(const char *hash,
+               const char *password, size_t passwordLen,
+               const char *salt, size_t saltLen, unsigned int iterations,
+               char *dKey, size_t dKeyLen)
+{
+       return pkcs5_pbkdf2(hash, password, passwordLen, salt, saltLen,
+                           iterations, (unsigned int)dKeyLen, dKey, 0);
+}
+
+int PBKDF2_HMAC_ready(const char *hash)
+{
+       int hash_id = gcry_md_map_name(hash);
+
+       if (!hash_id)
+               return -EINVAL;
+
+       /* Used hash must have at least 160 bits */
+       if (gcry_md_get_algo_dlen(hash_id) < 20)
+               return -EINVAL;
+
+       return 1;
 }
 
 static void sigvtalarm(int foo)
 {
-       __PBKDF2_performance = ~(0U) - *__PBKDF2_global_j;
-       *__PBKDF2_global_j = 0;
+       __PBKDF2_performance = ~(0U) - __PBKDF2_global_j;
+       __PBKDF2_global_j = 0;
 }
 
-unsigned int PBKDF2_performance_check() 
+/* This code benchmarks PBKDF2 and returns iterations/second using wth specified hash */
+int PBKDF2_performance_check(const char *hash, unsigned int *iter)
 {
-       /* This code benchmarks PBKDF2 and returns 
-       iterations/second per SHA1_DIGEST_SIZE */
-       
+       int r;
        char buf;
        struct itimerval it;
 
-       if(__PBKDF2_performance != 0) return __PBKDF2_performance;
+       if(__PBKDF2_performance != 0) {
+               *iter = __PBKDF2_performance;
+               return 0;
+       }
+
+       if (!PBKDF2_HMAC_ready(hash))
+               return -EINVAL;
 
        signal(SIGVTALRM,sigvtalarm);
        it.it_interval.tv_usec = 0;
@@ -104,11 +288,12 @@ unsigned int PBKDF2_performance_check()
        it.it_value.tv_usec = 0;
        it.it_value.tv_sec =  1;
        if (setitimer (ITIMER_VIRTUAL, &it, NULL) < 0)
-       return 0;
+               return -EINVAL;
+
+       r = pkcs5_pbkdf2(hash, "foo", 3, "bar", 3, ~(0U), 1, &buf, 1);
+
+       __PBKDF2_global_j = 0;
 
-       PBKDF2_HMAC_SHA1("foo", 3,
-                        "bar", 3, ~(0U),
-                        &buf, 1);
-       
-       return __PBKDF2_performance;
+       *iter = __PBKDF2_performance;
+       return r;
 }
index 49f9eb8..1488dec 100644 (file)
@@ -5,10 +5,13 @@
 
 /* */
 
-void PBKDF2_HMAC_SHA1(const char *password, size_t passwordLen, 
-                     const char *salt, size_t saltLen, unsigned int iterations, 
-                     char *dKey, size_t dKeyLen);
+int PBKDF2_HMAC(const char *hash,
+               const char *password, size_t passwordLen,
+               const char *salt, size_t saltLen, unsigned int iterations,
+               char *dKey, size_t dKeyLen);
 
-unsigned int PBKDF2_performance_check();
+
+int PBKDF2_performance_check(const char *hash, unsigned int *iter);
+int PBKDF2_HMAC_ready(const char *hash);
 
 #endif