summaryrefslogtreecommitdiffstats
path: root/grub-core/lib/crypto.c
diff options
context:
space:
mode:
Diffstat (limited to 'grub-core/lib/crypto.c')
-rw-r--r--grub-core/lib/crypto.c493
1 files changed, 493 insertions, 0 deletions
diff --git a/grub-core/lib/crypto.c b/grub-core/lib/crypto.c
new file mode 100644
index 0000000..ca334d5
--- /dev/null
+++ b/grub-core/lib/crypto.c
@@ -0,0 +1,493 @@
+/*
+ * 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 == GRUB_TERM_ESC)
+ {
+ 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 != GRUB_TERM_ESC);
+}
+#endif
+