summaryrefslogtreecommitdiffstats
path: root/grub-core/lib/libgcrypt/cipher/kdf.c
diff options
context:
space:
mode:
Diffstat (limited to 'grub-core/lib/libgcrypt/cipher/kdf.c')
-rw-r--r--grub-core/lib/libgcrypt/cipher/kdf.c278
1 files changed, 278 insertions, 0 deletions
diff --git a/grub-core/lib/libgcrypt/cipher/kdf.c b/grub-core/lib/libgcrypt/cipher/kdf.c
new file mode 100644
index 0000000..46e8550
--- /dev/null
+++ b/grub-core/lib/libgcrypt/cipher/kdf.c
@@ -0,0 +1,278 @@
+/* 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);
+}