From: Milan Broz Date: Thu, 30 Jul 2009 14:56:59 +0000 (+0000) Subject: Switch PBKDF2 from internal SHA1 to libgcrypt. X-Git-Tag: upstream/1.6~748 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=7e7c9c176c0e59b979be7d22bab99debd643e0d8;p=platform%2Fupstream%2Fcryptsetup.git Switch PBKDF2 from internal SHA1 to libgcrypt. Also make hash algorithm not fixed here. git-svn-id: https://cryptsetup.googlecode.com/svn/trunk@79 36d66b0a-2a48-0410-832c-cd162a569da5 --- diff --git a/ChangeLog b/ChangeLog index 5b44845..ce82339 100644 --- 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 * Pad luks header to 512 sector size. diff --git a/luks/af.c b/luks/af.c index faa77f2..31a0197 100644 --- a/luks/af.c +++ b/luks/af.c @@ -1,6 +1,7 @@ /* * AFsplitter - Anti forensic information splitter * Copyright 2004, Clemens Fruhwirth + * Copyright (C) 2009 Red Hat, Inc. All rights reserved. * * AFsplitter diffuses information over a large stripe of data, * therefor supporting secure data destruction. @@ -19,52 +20,57 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include +#include #include #include -#include -#include -#include -#include #include #include +#include #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; ikeyBytes=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) diff --git a/luks/pbkdf.c b/luks/pbkdf.c index 5be2b26..5633cd1 100644 --- a/luks/pbkdf.c +++ b/luks/pbkdf.c @@ -1,102 +1,286 @@ -/* - * Copyright 2004 Clemens Fruhwirth - * 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 + * 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 #include #include +#include #include +#include -#include "hmac_sha1.h" -#include "XORblock.h" -#include +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 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; } diff --git a/luks/pbkdf.h b/luks/pbkdf.h index 49f9eb8..1488dec 100644 --- a/luks/pbkdf.h +++ b/luks/pbkdf.h @@ -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