/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2006 * 2007, 2008, 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/>. */ #include <grub/crypto.h> #include <grub/misc.h> #include <grub/mm.h> #include <grub/term.h> #include <grub/dl.h> #include <grub/i18n.h> #include <grub/env.h> GRUB_MOD_LICENSE ("GPLv3+"); 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; void (*grub_crypto_autoload_hook) (const char *name) = NULL; /* Based on libgcrypt-1.4.4/src/misc.c. */ void grub_burn_stack (grub_size_t size) { char buf[64]; grub_memset (buf, 0, sizeof (buf)); if (size > sizeof (buf)) grub_burn_stack (size - sizeof (buf)); } void _gcry_burn_stack (int size) { grub_burn_stack (size); } void __attribute__ ((noreturn)) _gcry_assert_failed (const char *expr, const char *file, int line, const char *func) { grub_fatal ("assertion %s at %s:%d (%s) failed\n", expr, file, line, func); } void _gcry_log_error (const char *fmt, ...) { va_list args; const char *debug = grub_env_get ("debug"); if (! debug) return; if (grub_strword (debug, "all") || grub_strword (debug, "gcrypt")) { grub_printf ("gcrypt error: "); va_start (args, fmt); grub_vprintf (fmt, args); va_end (args); grub_refresh (); } } void grub_cipher_register (gcry_cipher_spec_t *cipher) { cipher->next = grub_ciphers; grub_ciphers = cipher; } void grub_cipher_unregister (gcry_cipher_spec_t *cipher) { gcry_cipher_spec_t **ciph; for (ciph = &grub_ciphers; *ciph; ciph = &((*ciph)->next)) if (*ciph == cipher) { *ciph = (*ciph)->next; break; } } void grub_md_register (gcry_md_spec_t *digest) { digest->next = grub_digests; grub_digests = digest; } void grub_md_unregister (gcry_md_spec_t *cipher) { gcry_md_spec_t **ciph; for (ciph = &grub_digests; *ciph; ciph = &((*ciph)->next)) if (*ciph == cipher) { *ciph = (*ciph)->next; break; } } void grub_crypto_hash (const gcry_md_spec_t *hash, void *out, const void *in, grub_size_t inlen) { GRUB_PROPERLY_ALIGNED_ARRAY (ctx, GRUB_CRYPTO_MAX_MD_CONTEXT_SIZE); if (hash->contextsize > sizeof (ctx)) grub_fatal ("Too large md context"); hash->init (&ctx); hash->write (&ctx, in, inlen); hash->final (&ctx); grub_memcpy (out, hash->read (&ctx), hash->mdlen); } const gcry_md_spec_t * grub_crypto_lookup_md_by_name (const char *name) { const gcry_md_spec_t *md; int first = 1; while (1) { for (md = grub_digests; md; md = md->next) if (grub_strcasecmp (name, md->name) == 0) return md; if (grub_crypto_autoload_hook && first) grub_crypto_autoload_hook (name); else return NULL; first = 0; } } const gcry_cipher_spec_t * grub_crypto_lookup_cipher_by_name (const char *name) { const gcry_cipher_spec_t *ciph; int first = 1; while (1) { for (ciph = grub_ciphers; ciph; ciph = ciph->next) { const char **alias; if (grub_strcasecmp (name, ciph->name) == 0) return ciph; if (!ciph->aliases) continue; for (alias = ciph->aliases; *alias; alias++) if (grub_strcasecmp (name, *alias) == 0) return ciph; } if (grub_crypto_autoload_hook && first) grub_crypto_autoload_hook (name); else return NULL; first = 0; } } grub_crypto_cipher_handle_t grub_crypto_cipher_open (const struct gcry_cipher_spec *cipher) { grub_crypto_cipher_handle_t ret; ret = grub_malloc (sizeof (*ret) + cipher->contextsize); if (!ret) return NULL; ret->cipher = cipher; return ret; } gcry_err_code_t grub_crypto_cipher_set_key (grub_crypto_cipher_handle_t cipher, const unsigned char *key, unsigned keylen) { return cipher->cipher->setkey (cipher->ctx, key, keylen); } gcry_err_code_t grub_crypto_ecb_decrypt (grub_crypto_cipher_handle_t cipher, void *out, const void *in, grub_size_t size) { const grub_uint8_t *inptr, *end; grub_uint8_t *outptr; grub_size_t blocksize; if (!cipher->cipher->decrypt) return GPG_ERR_NOT_SUPPORTED; blocksize = cipher->cipher->blocksize; if (blocksize == 0 || (((blocksize - 1) & blocksize) != 0) || ((size & (blocksize - 1)) != 0)) return GPG_ERR_INV_ARG; end = (const grub_uint8_t *) in + size; for (inptr = in, outptr = out; inptr < end; inptr += blocksize, outptr += blocksize) cipher->cipher->decrypt (cipher->ctx, outptr, inptr); return GPG_ERR_NO_ERROR; } gcry_err_code_t grub_crypto_ecb_encrypt (grub_crypto_cipher_handle_t cipher, void *out, const void *in, grub_size_t size) { const grub_uint8_t *inptr, *end; grub_uint8_t *outptr; grub_size_t blocksize; if (!cipher->cipher->encrypt) return GPG_ERR_NOT_SUPPORTED; blocksize = cipher->cipher->blocksize; if (blocksize == 0 || (((blocksize - 1) & blocksize) != 0) || ((size & (blocksize - 1)) != 0)) return GPG_ERR_INV_ARG; end = (const grub_uint8_t *) in + size; for (inptr = in, outptr = out; inptr < end; inptr += blocksize, outptr += blocksize) cipher->cipher->encrypt (cipher->ctx, outptr, inptr); return GPG_ERR_NO_ERROR; } gcry_err_code_t grub_crypto_cbc_encrypt (grub_crypto_cipher_handle_t cipher, void *out, const void *in, grub_size_t size, void *iv_in) { grub_uint8_t *outptr; const grub_uint8_t *inptr, *end; void *iv; grub_size_t blocksize; if (!cipher->cipher->encrypt) return GPG_ERR_NOT_SUPPORTED; blocksize = cipher->cipher->blocksize; if (blocksize == 0 || (((blocksize - 1) & blocksize) != 0) || ((size & (blocksize - 1)) != 0)) return GPG_ERR_INV_ARG; end = (const grub_uint8_t *) in + size; iv = iv_in; for (inptr = in, outptr = out; inptr < end; inptr += blocksize, outptr += blocksize) { grub_crypto_xor (outptr, inptr, iv, blocksize); cipher->cipher->encrypt (cipher->ctx, outptr, outptr); iv = outptr; } grub_memcpy (iv_in, iv, blocksize); return GPG_ERR_NO_ERROR; } gcry_err_code_t grub_crypto_cbc_decrypt (grub_crypto_cipher_handle_t cipher, void *out, const void *in, grub_size_t size, void *iv) { const grub_uint8_t *inptr, *end; grub_uint8_t *outptr; grub_uint8_t ivt[GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE]; grub_size_t blocksize; if (!cipher->cipher->decrypt) return GPG_ERR_NOT_SUPPORTED; blocksize = cipher->cipher->blocksize; if (blocksize == 0 || (((blocksize - 1) & blocksize) != 0) || ((size & (blocksize - 1)) != 0)) return GPG_ERR_INV_ARG; if (blocksize > GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE) return GPG_ERR_INV_ARG; end = (const grub_uint8_t *) in + size; for (inptr = in, outptr = out; inptr < end; inptr += blocksize, outptr += blocksize) { grub_memcpy (ivt, inptr, blocksize); cipher->cipher->decrypt (cipher->ctx, outptr, inptr); grub_crypto_xor (outptr, outptr, iv, blocksize); grub_memcpy (iv, ivt, blocksize); } 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, const 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, const 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) { if (in == GPG_ERR_NO_ERROR) return GRUB_ERR_NONE; return GRUB_ACCESS_DENIED; } int grub_crypto_memcmp (const void *a, const void *b, grub_size_t n) { register grub_size_t counter = 0; const grub_uint8_t *pa, *pb; for (pa = a, pb = b; n; pa++, pb++, n--) { if (*pa != *pb) counter++; } return !!counter; } #ifndef GRUB_UTIL int grub_password_get (char buf[], unsigned buf_size) { unsigned cur_len = 0; int key; while (1) { key = grub_getkey (); if (key == '\n' || key == '\r') break; if (key == '\e') { cur_len = 0; break; } if (key == '\b') { if (cur_len) cur_len--; continue; } if (!grub_isprint (key)) continue; if (cur_len + 2 < buf_size) buf[cur_len++] = key; } grub_memset (buf + cur_len, 0, buf_size - cur_len); grub_xputs ("\n"); grub_refresh (); return (key != '\e'); } #endif