Add HMAC and PBKDF2

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2009-11-24 02:32:29 +01:00
parent 37f7911f01
commit 6e7d9194d1
6 changed files with 302 additions and 8 deletions

View file

@ -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

View file

@ -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 <http://www.gnu.org/licenses/>.
#*
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,

View file

@ -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);
}

View file

@ -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

View file

@ -21,6 +21,13 @@
#include <grub/misc.h>
#include <grub/mm.h>
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)
{

101
lib/pbkdf2.c Normal file
View file

@ -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 <grub/crypto.h>
#include <grub/mm.h>
#include <grub/misc.h>
/* 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;
}