mirror of
				https://git.proxmox.com/git/grub2
				synced 2025-10-31 10:26:53 +00:00 
			
		
		
		
	 bdd89d239c
			
		
	
	
		bdd89d239c
		
	
	
	
	
		
			
			* Also use hex value for GRUB_TERM_ESC as '\e' is not in the C standard and is not understood by some compilers
		
			
				
	
	
		
			494 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			494 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *  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
 | |
| 
 |