diff --git a/conf/common.rmk b/conf/common.rmk
index 35b4620f4..3356c556b 100644
--- a/conf/common.rmk
+++ b/conf/common.rmk
@@ -624,4 +624,9 @@ crypto_mod_SOURCES = lib/crypto.c
crypto_mod_CFLAGS = $(COMMON_CFLAGS)
crypto_mod_LDFLAGS = $(COMMON_LDFLAGS)
+pkglib_MODULES += pbkdf2.mod
+pbkdf2_mod_SOURCES = lib/pbkdf2.c
+pbkdf2_mod_CFLAGS = $(COMMON_CFLAGS)
+pbkdf2_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
include $(srcdir)/conf/gcry.mk
diff --git a/import_gcry.py b/import_gcry.py
index 39542d8c4..ae5b76778 100644
--- a/import_gcry.py
+++ b/import_gcry.py
@@ -1,3 +1,21 @@
+#*
+#* GRUB -- GRand Unified Bootloader
+#* Copyright (C) 2009 Free Software Foundation, Inc.
+#*
+#* GRUB is free software: you can redistribute it and/or modify
+#* it under the terms of the GNU General Public License as published by
+#* the Free Software Foundation, either version 3 of the License, or
+#* (at your option) any later version.
+#*
+#* GRUB 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 General Public License for more details.
+#*
+#* You should have received a copy of the GNU General Public License
+#* along with GRUB. If not, see .
+#*
+
import re
import sys
import os
@@ -26,9 +44,12 @@ conf = open (os.path.join (outdir, "conf", "gcry.rmk"), "w")
conf.write ("# -*- makefile -*-\n\n")
chlog = ""
-mdblocksizes = {"_gcry_digest_spec_crc32" : 1,
- "_gcry_digest_spec_crc32_rfc1510" : 1,
- "_gcry_digest_spec_crc24_rfc2440" : 1,
+# Strictly speaking CRC32/CRC24 work on bytes so this value should be 1
+# But libgcrypt uses 64. Let's keep the value for compatibility. Since
+# noone uses CRC24/CRC32 for HMAC this is no problem
+mdblocksizes = {"_gcry_digest_spec_crc32" : 64,
+ "_gcry_digest_spec_crc32_rfc1510" : 64,
+ "_gcry_digest_spec_crc24_rfc2440" : 64,
"_gcry_digest_spec_md4" : 64,
"_gcry_digest_spec_md5" : 64,
"_gcry_digest_spec_rmd160" : 64,
diff --git a/include/grub/cipher_wrap.h b/include/grub/cipher_wrap.h
index 9e6242b7f..60ab369f0 100644
--- a/include/grub/cipher_wrap.h
+++ b/include/grub/cipher_wrap.h
@@ -64,7 +64,7 @@ typedef union {
static inline void
grub_assert_real (const char *file, int line, int cond)
{
- if (cond)
+ if (!cond)
grub_fatal ("Assertion failed at %s:%d\n", file, line);
}
diff --git a/include/grub/crypto.h b/include/grub/crypto.h
index 34b1cb078..df15f9bb9 100644
--- a/include/grub/crypto.h
+++ b/include/grub/crypto.h
@@ -166,11 +166,15 @@ typedef struct gcry_md_spec
struct gcry_md_spec *next;
} gcry_md_spec_t;
-typedef struct grub_crypto_cipher_handle
+struct grub_crypto_cipher_handle
{
const struct gcry_cipher_spec *cipher;
char ctx[0];
-} *grub_crypto_cipher_handle_t;
+};
+
+typedef struct grub_crypto_cipher_handle *grub_crypto_cipher_handle_t;
+
+struct grub_crypto_hmac_handle;
const gcry_cipher_spec_t *
grub_crypto_lookup_cipher_by_name (const char *name);
@@ -213,7 +217,7 @@ grub_md_register (gcry_md_spec_t *digest);
void
grub_md_unregister (gcry_md_spec_t *cipher);
void
-grub_crypto_hash (const gcry_md_spec_t *hash, void *out, void *in,
+grub_crypto_hash (const gcry_md_spec_t *hash, void *out, const void *in,
grub_size_t inlen);
const gcry_md_spec_t *
grub_crypto_lookup_md_by_name (const char *name);
@@ -223,7 +227,35 @@ grub_crypto_gcry_error (gcry_err_code_t in);
void grub_burn_stack (grub_size_t size);
+struct grub_crypto_hmac_handle *
+grub_crypto_hmac_init (const struct gcry_md_spec *md,
+ const void *key, grub_size_t keylen);
+void
+grub_crypto_hmac_write (struct grub_crypto_hmac_handle *hnd, void *data,
+ grub_size_t datalen);
+gcry_err_code_t
+grub_crypto_hmac_fini (struct grub_crypto_hmac_handle *hnd, void *out);
+
+gcry_err_code_t
+grub_crypto_hmac_buffer (const struct gcry_md_spec *md,
+ const void *key, grub_size_t keylen,
+ void *data, grub_size_t datalen, void *out);
+
extern gcry_md_spec_t _gcry_digest_spec_md5;
#define GRUB_MD_MD5 ((const gcry_md_spec_t *) &_gcry_digest_spec_md5)
+#define GRUB_MD_SHA1 ((const gcry_md_spec_t *) &_gcry_digest_spec_sha1)
+
+/* Implement PKCS#5 PBKDF2 as per RFC 2898. The PRF to use is HMAC variant
+ of digest supplied by MD. Inputs are the password P of length PLEN,
+ the salt S of length SLEN, the iteration counter C (> 0), and the
+ desired derived output length DKLEN. Output buffer is DK which
+ must have room for at least DKLEN octets. The output buffer will
+ be filled with the derived data. */
+gcry_err_code_t
+grub_crypto_pbkdf2 (const struct gcry_md_spec *md,
+ const grub_uint8_t *P, grub_size_t Plen,
+ const grub_uint8_t *S, grub_size_t Slen,
+ unsigned int c,
+ grub_uint8_t *DK, grub_size_t dkLen);
#endif
diff --git a/lib/crypto.c b/lib/crypto.c
index 718d6e278..3cb66f942 100644
--- a/lib/crypto.c
+++ b/lib/crypto.c
@@ -21,6 +21,13 @@
#include
#include
+struct grub_crypto_hmac_handle
+{
+ const struct gcry_md_spec *md;
+ void *ctx;
+ void *opad;
+};
+
static gcry_cipher_spec_t *grub_ciphers = NULL;
static gcry_md_spec_t *grub_digests = NULL;
@@ -69,7 +76,7 @@ grub_md_unregister (gcry_md_spec_t *cipher)
}
void
-grub_crypto_hash (const gcry_md_spec_t *hash, void *out, void *in,
+grub_crypto_hash (const gcry_md_spec_t *hash, void *out, const void *in,
grub_size_t inlen)
{
grub_uint8_t ctx[hash->contextsize];
@@ -228,6 +235,134 @@ grub_crypto_cbc_decrypt (grub_crypto_cipher_handle_t cipher,
return GPG_ERR_NO_ERROR;
}
+/* Based on gcry/cipher/md.c. */
+struct grub_crypto_hmac_handle *
+grub_crypto_hmac_init (const struct gcry_md_spec *md,
+ const void *key, grub_size_t keylen)
+{
+ grub_uint8_t *helpkey = NULL;
+ grub_uint8_t *ipad = NULL, *opad = NULL;
+ void *ctx = NULL;
+ struct grub_crypto_hmac_handle *ret = NULL;
+ unsigned i;
+
+ if (md->mdlen > md->blocksize)
+ return NULL;
+
+ ctx = grub_malloc (md->contextsize);
+ if (!ctx)
+ goto err;
+
+ if ( keylen > md->blocksize )
+ {
+ helpkey = grub_malloc (md->mdlen);
+ if (!helpkey)
+ goto err;
+ grub_crypto_hash (md, helpkey, key, keylen);
+
+ key = helpkey;
+ keylen = md->mdlen;
+ }
+
+ ipad = grub_zalloc (md->blocksize);
+ if (!ipad)
+ goto err;
+
+ opad = grub_zalloc (md->blocksize);
+ if (!opad)
+ goto err;
+
+ grub_memcpy ( ipad, key, keylen );
+ grub_memcpy ( opad, key, keylen );
+ for (i=0; i < md->blocksize; i++ )
+ {
+ ipad[i] ^= 0x36;
+ opad[i] ^= 0x5c;
+ }
+ grub_free (helpkey);
+ helpkey = NULL;
+
+ md->init (ctx);
+
+ md->write (ctx, ipad, md->blocksize); /* inner pad */
+ grub_memset (ipad, 0, md->blocksize);
+ grub_free (ipad);
+ ipad = NULL;
+
+ ret = grub_malloc (sizeof (*ret));
+ if (!ret)
+ goto err;
+
+ ret->md = md;
+ ret->ctx = ctx;
+ ret->opad = opad;
+
+ return ret;
+
+ err:
+ grub_free (helpkey);
+ grub_free (ctx);
+ grub_free (ipad);
+ grub_free (opad);
+ return NULL;
+}
+
+void
+grub_crypto_hmac_write (struct grub_crypto_hmac_handle *hnd, void *data,
+ grub_size_t datalen)
+{
+ hnd->md->write (hnd->ctx, data, datalen);
+}
+
+gcry_err_code_t
+grub_crypto_hmac_fini (struct grub_crypto_hmac_handle *hnd, void *out)
+{
+ grub_uint8_t *p;
+ grub_uint8_t *ctx2;
+
+ ctx2 = grub_malloc (hnd->md->contextsize);
+ if (!ctx2)
+ return GPG_ERR_OUT_OF_MEMORY;
+
+ hnd->md->final (hnd->ctx);
+ hnd->md->read (hnd->ctx);
+ p = hnd->md->read (hnd->ctx);
+
+ hnd->md->init (ctx2);
+ hnd->md->write (ctx2, hnd->opad, hnd->md->blocksize);
+ hnd->md->write (ctx2, p, hnd->md->mdlen);
+ hnd->md->final (ctx2);
+ grub_memset (hnd->opad, 0, hnd->md->blocksize);
+ grub_free (hnd->opad);
+ grub_memset (hnd->ctx, 0, hnd->md->contextsize);
+ grub_free (hnd->ctx);
+
+ grub_memcpy (out, hnd->md->read (ctx2), hnd->md->mdlen);
+ grub_memset (ctx2, 0, hnd->md->contextsize);
+ grub_free (ctx2);
+
+ grub_memset (hnd, 0, sizeof (*hnd));
+ grub_free (hnd);
+
+ return GPG_ERR_NO_ERROR;
+}
+
+gcry_err_code_t
+grub_crypto_hmac_buffer (const struct gcry_md_spec *md,
+ const void *key, grub_size_t keylen,
+ void *data, grub_size_t datalen, void *out)
+{
+ struct grub_crypto_hmac_handle *hnd;
+
+ hnd = grub_crypto_hmac_init (md, key, keylen);
+ if (!hnd)
+ return GPG_ERR_OUT_OF_MEMORY;
+
+ grub_crypto_hmac_write (hnd, data, datalen);
+ return grub_crypto_hmac_fini (hnd, out);
+}
+
+
grub_err_t
grub_crypto_gcry_error (gcry_err_code_t in)
{
diff --git a/lib/pbkdf2.c b/lib/pbkdf2.c
new file mode 100644
index 000000000..f2d3ca97f
--- /dev/null
+++ b/lib/pbkdf2.c
@@ -0,0 +1,101 @@
+/* gc-pbkdf2-sha1.c --- Password-Based Key Derivation Function a'la PKCS#5
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2009 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program 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 General Public License for more details.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* Written by Simon Josefsson. */
+
+#include
+#include
+#include
+
+/* Implement PKCS#5 PBKDF2 as per RFC 2898. The PRF to use is HMAC variant
+ of digest supplied by MD. Inputs are the password P of length PLEN,
+ the salt S of length SLEN, the iteration counter C (> 0), and the
+ desired derived output length DKLEN. Output buffer is DK which
+ must have room for at least DKLEN octets. The output buffer will
+ be filled with the derived data. */
+gcry_err_code_t
+grub_crypto_pbkdf2 (const struct gcry_md_spec *md,
+ const grub_uint8_t *P, grub_size_t Plen,
+ const grub_uint8_t *S, grub_size_t Slen,
+ unsigned int c,
+ grub_uint8_t *DK, grub_size_t dkLen)
+{
+ unsigned int hLen = md->mdlen;
+ grub_uint8_t U[md->mdlen];
+ grub_uint8_t T[md->mdlen];
+ unsigned int u;
+ unsigned int l;
+ unsigned int r;
+ unsigned int i;
+ unsigned int k;
+ gcry_err_code_t rc;
+ grub_uint8_t *tmp;
+ grub_size_t tmplen = Slen + 4;
+
+ if (c == 0)
+ return GPG_ERR_INV_ARG;
+
+ if (dkLen == 0)
+ return GPG_ERR_INV_ARG;
+
+ if (dkLen > 4294967295U)
+ return GPG_ERR_INV_ARG;
+
+ l = ((dkLen - 1) / hLen) + 1;
+ r = dkLen - (l - 1) * hLen;
+
+ tmp = grub_malloc (tmplen);
+ if (tmp == NULL)
+ return GPG_ERR_OUT_OF_MEMORY;
+
+ grub_memcpy (tmp, S, Slen);
+
+ for (i = 1; i <= l; i++)
+ {
+ grub_memset (T, 0, hLen);
+
+ for (u = 1; u <= c; u++)
+ {
+ if (u == 1)
+ {
+ tmp[Slen + 0] = (i & 0xff000000) >> 24;
+ tmp[Slen + 1] = (i & 0x00ff0000) >> 16;
+ tmp[Slen + 2] = (i & 0x0000ff00) >> 8;
+ tmp[Slen + 3] = (i & 0x000000ff) >> 0;
+
+ rc = grub_crypto_hmac_buffer (md, P, Plen, tmp, tmplen, U);
+ }
+ else
+ rc = grub_crypto_hmac_buffer (md, P, Plen, U, hLen, U);
+
+ if (rc != GPG_ERR_NO_ERROR)
+ {
+ grub_free (tmp);
+ return rc;
+ }
+
+ for (k = 0; k < hLen; k++)
+ T[k] ^= U[k];
+ }
+
+ grub_memcpy (DK + (i - 1) * hLen, T, i == l ? r : hLen);
+ }
+
+ grub_free (tmp);
+
+ return GPG_ERR_NO_ERROR;
+}