From 6e7d9194d1e8d605d0462260836f723ac7d6ef1a Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Tue, 24 Nov 2009 02:32:29 +0100 Subject: [PATCH] Add HMAC and PBKDF2 --- conf/common.rmk | 5 ++ import_gcry.py | 27 +++++++- include/grub/cipher_wrap.h | 2 +- include/grub/crypto.h | 38 +++++++++- lib/crypto.c | 137 ++++++++++++++++++++++++++++++++++++- lib/pbkdf2.c | 101 +++++++++++++++++++++++++++ 6 files changed, 302 insertions(+), 8 deletions(-) create mode 100644 lib/pbkdf2.c 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; +}