278 lines
8.5 KiB
C
278 lines
8.5 KiB
C
/* kdf.c - Key Derivation Functions
|
|
* Copyright (C) 1998, 2011 Free Software Foundation, Inc.
|
|
*
|
|
* 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/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "g10lib.h"
|
|
#include "cipher.h"
|
|
#include "ath.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 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
|
|
openpgp_s2k (const void *passphrase, size_t passphraselen,
|
|
int algo, 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;
|
|
char *key = keybuffer;
|
|
int pass, i;
|
|
int used = 0;
|
|
int secmode;
|
|
|
|
if ((algo == GCRY_KDF_SALTED_S2K || algo == GCRY_KDF_ITERSALTED_S2K)
|
|
&& (!salt || saltlen != 8))
|
|
return GPG_ERR_INV_VALUE;
|
|
|
|
secmode = gcry_is_secure (passphrase) || gcry_is_secure (keybuffer);
|
|
|
|
ec = gpg_err_code (gcry_md_open (&md, hashalgo,
|
|
secmode? GCRY_MD_FLAG_SECURE : 0));
|
|
if (ec)
|
|
return ec;
|
|
|
|
for (pass=0; used < keysize; pass++)
|
|
{
|
|
if (pass)
|
|
{
|
|
gcry_md_reset (md);
|
|
for (i=0; i < pass; i++) /* Preset the hash context. */
|
|
gcry_md_putc (md, 0);
|
|
}
|
|
|
|
if (algo == GCRY_KDF_SALTED_S2K || algo == GCRY_KDF_ITERSALTED_S2K)
|
|
{
|
|
int len2 = passphraselen + 8;
|
|
unsigned long count = len2;
|
|
|
|
if (algo == GCRY_KDF_ITERSALTED_S2K)
|
|
{
|
|
count = iterations;
|
|
if (count < len2)
|
|
count = len2;
|
|
}
|
|
|
|
while (count > len2)
|
|
{
|
|
gcry_md_write (md, salt, saltlen);
|
|
gcry_md_write (md, passphrase, passphraselen);
|
|
count -= len2;
|
|
}
|
|
if (count < saltlen)
|
|
gcry_md_write (md, salt, count);
|
|
else
|
|
{
|
|
gcry_md_write (md, salt, saltlen);
|
|
count -= saltlen;
|
|
gcry_md_write (md, passphrase, count);
|
|
}
|
|
}
|
|
else
|
|
gcry_md_write (md, passphrase, passphraselen);
|
|
|
|
gcry_md_final (md);
|
|
i = gcry_md_get_algo_dlen (hashalgo);
|
|
if (i > keysize - used)
|
|
i = keysize - used;
|
|
memcpy (key+used, gcry_md_read (md, hashalgo), i);
|
|
used += i;
|
|
}
|
|
gcry_md_close (md);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* 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. */
|
|
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)
|
|
{
|
|
gpg_err_code_t ec;
|
|
gcry_md_hd_t md;
|
|
int secmode;
|
|
unsigned int dklen = keysize;
|
|
char *dk = keybuffer;
|
|
unsigned int hlen; /* Output length of the digest function. */
|
|
unsigned int l; /* Rounded up number of blocks. */
|
|
unsigned int r; /* Number of octets in the last block. */
|
|
char *sbuf; /* Malloced buffer to concatenate salt and iter
|
|
as well as space to hold TBUF and UBUF. */
|
|
char *tbuf; /* Buffer for T; ptr into SBUF, size is HLEN. */
|
|
char *ubuf; /* Buffer for U; ptr into SBUF, size is HLEN. */
|
|
unsigned int lidx; /* Current block number. */
|
|
unsigned long iter; /* Current iteration number. */
|
|
unsigned int i;
|
|
|
|
if (!salt || !saltlen || !iterations || !dklen)
|
|
return GPG_ERR_INV_VALUE;
|
|
|
|
hlen = gcry_md_get_algo_dlen (hashalgo);
|
|
if (!hlen)
|
|
return GPG_ERR_DIGEST_ALGO;
|
|
|
|
secmode = gcry_is_secure (passphrase) || gcry_is_secure (keybuffer);
|
|
|
|
/* We ignore step 1 from pksc5v2.1 which demands a check that dklen
|
|
is not larger that 0xffffffff * hlen. */
|
|
|
|
/* Step 2 */
|
|
l = ((dklen - 1)/ hlen) + 1;
|
|
r = dklen - (l - 1) * hlen;
|
|
|
|
/* Setup buffers and prepare a hash context. */
|
|
sbuf = (secmode
|
|
? gcry_malloc_secure (saltlen + 4 + hlen + hlen)
|
|
: gcry_malloc (saltlen + 4 + hlen + hlen));
|
|
if (!sbuf)
|
|
return gpg_err_code_from_syserror ();
|
|
tbuf = sbuf + saltlen + 4;
|
|
ubuf = tbuf + hlen;
|
|
|
|
ec = gpg_err_code (gcry_md_open (&md, hashalgo,
|
|
(GCRY_MD_FLAG_HMAC
|
|
| (secmode?GCRY_MD_FLAG_SECURE:0))));
|
|
if (ec)
|
|
{
|
|
gcry_free (sbuf);
|
|
return ec;
|
|
}
|
|
|
|
/* Step 3 and 4. */
|
|
memcpy (sbuf, salt, saltlen);
|
|
for (lidx = 1; lidx <= l; lidx++)
|
|
{
|
|
for (iter = 0; iter < iterations; iter++)
|
|
{
|
|
ec = gpg_err_code (gcry_md_setkey (md, passphrase, passphraselen));
|
|
if (ec)
|
|
{
|
|
gcry_md_close (md);
|
|
gcry_free (sbuf);
|
|
return ec;
|
|
}
|
|
if (!iter) /* Compute U_1: */
|
|
{
|
|
sbuf[saltlen] = (lidx >> 24);
|
|
sbuf[saltlen + 1] = (lidx >> 16);
|
|
sbuf[saltlen + 2] = (lidx >> 8);
|
|
sbuf[saltlen + 3] = lidx;
|
|
gcry_md_write (md, sbuf, saltlen + 4);
|
|
memcpy (ubuf, gcry_md_read (md, 0), hlen);
|
|
memcpy (tbuf, ubuf, hlen);
|
|
}
|
|
else /* Compute U_(2..c): */
|
|
{
|
|
gcry_md_write (md, ubuf, hlen);
|
|
memcpy (ubuf, gcry_md_read (md, 0), hlen);
|
|
for (i=0; i < hlen; i++)
|
|
tbuf[i] ^= ubuf[i];
|
|
}
|
|
}
|
|
if (lidx == l) /* Last block. */
|
|
memcpy (dk, tbuf, r);
|
|
else
|
|
{
|
|
memcpy (dk, tbuf, hlen);
|
|
dk += hlen;
|
|
}
|
|
}
|
|
|
|
gcry_md_close (md);
|
|
gcry_free (sbuf);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Derive a key from a passphrase. KEYSIZE gives the requested size
|
|
of the keys in octets. KEYBUFFER is a caller provided buffer
|
|
filled on success with the derived key. The input passphrase is
|
|
taken from (PASSPHRASE,PASSPHRASELEN) which is an arbitrary memory
|
|
buffer. ALGO specifies the KDF algorithm to use; these are the
|
|
constants GCRY_KDF_*. SUBALGO specifies an algorithm used
|
|
internally by the KDF algorithms; this is usually a hash algorithm
|
|
but certain KDF algorithm may use it differently. {SALT,SALTLEN}
|
|
is a salt as needed by most KDF algorithms. ITERATIONS is a
|
|
positive integer parameter to most KDFs. 0 is returned on success,
|
|
or an error code on failure. */
|
|
gpg_error_t
|
|
gcry_kdf_derive (const void *passphrase, size_t passphraselen,
|
|
int algo, int subalgo,
|
|
const void *salt, size_t saltlen,
|
|
unsigned long iterations,
|
|
size_t keysize, void *keybuffer)
|
|
{
|
|
gpg_err_code_t ec;
|
|
|
|
if (!passphrase || (!passphraselen && algo != GCRY_KDF_PBKDF2))
|
|
{
|
|
ec = GPG_ERR_INV_DATA;
|
|
goto leave;
|
|
}
|
|
if (!keybuffer || !keysize)
|
|
{
|
|
ec = GPG_ERR_INV_VALUE;
|
|
goto leave;
|
|
}
|
|
|
|
|
|
switch (algo)
|
|
{
|
|
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);
|
|
break;
|
|
|
|
case GCRY_KDF_PBKDF1:
|
|
ec = GPG_ERR_UNSUPPORTED_ALGORITHM;
|
|
break;
|
|
|
|
case GCRY_KDF_PBKDF2:
|
|
ec = pkdf2 (passphrase, passphraselen, subalgo,
|
|
salt, saltlen, iterations, keysize, keybuffer);
|
|
break;
|
|
|
|
default:
|
|
ec = GPG_ERR_UNKNOWN_ALGORITHM;
|
|
break;
|
|
}
|
|
|
|
leave:
|
|
return gpg_error (ec);
|
|
}
|