vaseboot/VasEBoot-core/lib/crypto.c

762 lines
18 KiB
C

/*
* VAS_EBOOT -- GRand Unified Bootloader
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2006
* 2007, 2008, 2009 Free Software Foundation, Inc.
*
* VAS_EBOOT 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.
*
* VAS_EBOOT 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 VAS_EBOOT. If not, see <http://www.gnu.org/licenses/>.
*/
#include <VasEBoot/crypto.h>
#include <VasEBoot/misc.h>
#include <VasEBoot/mm.h>
#include <VasEBoot/term.h>
#include <VasEBoot/dl.h>
#include <VasEBoot/i18n.h>
#include <VasEBoot/env.h>
VAS_EBOOT_MOD_LICENSE ("GPLv3+");
struct VasEBoot_crypto_hmac_handle
{
const struct gcry_md_spec *md;
void *ctx;
void *ctx2;
void *ctx_cache;
void *ctx2_cache;
};
static gcry_cipher_spec_t *VasEBoot_ciphers = NULL;
static gcry_md_spec_t *VasEBoot_digests = NULL;
int _gcry_no_fips_mode_required = 1;
void (*VasEBoot_crypto_autoload_hook) (const char *name) = NULL;
/* Based on libgcrypt-1.4.4/src/misc.c. */
void
VasEBoot_burn_stack (VasEBoot_size_t size)
{
char buf[64];
VasEBoot_memset (buf, 0, sizeof (buf));
if (size > sizeof (buf))
VasEBoot_burn_stack (size - sizeof (buf));
}
void
_gcry_burn_stack (int size)
{
VasEBoot_burn_stack (size);
}
void
__gcry_burn_stack (unsigned int size)
{
VasEBoot_burn_stack (size);
}
void
__gcry_burn_stack_dummy (void)
{
}
void __attribute__ ((noreturn))
_gcry_assert_failed (const char *expr, const char *file, int line,
const char *func)
{
VasEBoot_fatal ("assertion %s at %s:%d (%s) failed\n", expr, file, line, func);
}
void
_gcry_bug( const char *file, int line, const char *func )
{
VasEBoot_fatal ("... this is a bug (%s:%d:%s)\n", file, line, func);
}
void _gcry_log_error (const char *fmt, ...)
{
va_list args;
const char *debug = VasEBoot_env_get ("debug");
if (! debug)
return;
if (VasEBoot_strword (debug, "all") || VasEBoot_strword (debug, "gcrypt"))
{
VasEBoot_printf ("gcrypt error: ");
va_start (args, fmt);
VasEBoot_vprintf (fmt, args);
va_end (args);
VasEBoot_refresh ();
}
}
void _gcry_log_info (const char *fmt, ...)
{
va_list args;
const char *debug = VasEBoot_env_get ("debug");
if (! debug)
return;
if (VasEBoot_strword (debug, "all") || VasEBoot_strword (debug, "gcrypt"))
{
VasEBoot_printf ("gcrypt info: ");
va_start (args, fmt);
VasEBoot_vprintf (fmt, args);
va_end (args);
VasEBoot_refresh ();
}
}
void
_gcry_fast_wipememory2 (void *ptr, int set, VasEBoot_size_t len)
{
VasEBoot_memset(ptr, set, len);
}
void
_gcry_fast_wipememory (void *ptr, VasEBoot_size_t len)
{
VasEBoot_memset(ptr, 0, len);
}
void
VasEBoot_cipher_register (gcry_cipher_spec_t *cipher)
{
cipher->next = VasEBoot_ciphers;
VasEBoot_ciphers = cipher;
}
void
VasEBoot_cipher_unregister (gcry_cipher_spec_t *cipher)
{
gcry_cipher_spec_t **ciph;
for (ciph = &VasEBoot_ciphers; *ciph; ciph = &((*ciph)->next))
if (*ciph == cipher)
{
*ciph = (*ciph)->next;
break;
}
}
void
VasEBoot_md_register (gcry_md_spec_t *digest)
{
digest->next = VasEBoot_digests;
VasEBoot_digests = digest;
}
void
VasEBoot_md_unregister (gcry_md_spec_t *cipher)
{
gcry_md_spec_t **ciph;
for (ciph = &VasEBoot_digests; *ciph; ciph = &((*ciph)->next))
if (*ciph == cipher)
{
*ciph = (*ciph)->next;
break;
}
}
struct gcry_pk_spec *VasEBoot_crypto_pk_dsa;
struct gcry_pk_spec *VasEBoot_crypto_pk_ecdsa;
struct gcry_pk_spec *VasEBoot_crypto_pk_rsa;
void
VasEBoot_crypto_hash (const gcry_md_spec_t *hash, void *out, const void *in,
VasEBoot_size_t inlen)
{
VAS_EBOOT_PROPERLY_ALIGNED_ARRAY (ctx, VAS_EBOOT_CRYPTO_MAX_MD_CONTEXT_SIZE);
if (hash->contextsize > sizeof (ctx))
VasEBoot_fatal ("Too large md context");
hash->init (&ctx, 0);
hash->write (&ctx, in, inlen);
hash->final (&ctx);
VasEBoot_memcpy (out, hash->read (&ctx), hash->mdlen);
}
const gcry_md_spec_t *
VasEBoot_crypto_lookup_md_by_name (const char *name)
{
const gcry_md_spec_t *md;
int first = 1;
while (1)
{
for (md = VasEBoot_digests; md; md = md->next)
if (VasEBoot_strcasecmp (name, md->name) == 0)
return md;
if (VasEBoot_crypto_autoload_hook && first)
VasEBoot_crypto_autoload_hook (name);
else
return NULL;
first = 0;
}
}
const gcry_md_spec_t *
VasEBoot_crypto_lookup_md_by_oid (const char *oid)
{
const gcry_md_spec_t *md;
if (!oid)
return NULL;
if (VasEBoot_strncmp (oid, "oid.", 4) == 0 || VasEBoot_strncmp (oid, "OID.", 4) == 0)
oid += 4;
for (md = VasEBoot_digests; md; md = md->next)
{
const gcry_md_oid_spec_t *oid_specs = md->oids;
if (oid_specs)
{
for (int j = 0; oid_specs[j].oidstring; j++)
if (VasEBoot_strcasecmp (oid, oid_specs[j].oidstring) == 0)
return md;
}
}
return NULL;
}
static const char *md_algos[] = {
[1] = "MD5",
[2] = "SHA1",
[3] = "RIPEMD160",
[6] = "TIGER",
[8] = "SHA256",
[9] = "SHA384",
[10] = "SHA512",
[11] = "SHA224",
[301] = "MD4",
[302] = "CRC32",
[303] = "CRC32RFC1510",
[304] = "CRC24RFC2440",
[305] = "WHIRLPOOL",
[306] = "TIGER1",
[307] = "TIGER2",
[308] = "GOSTR3411_94",
[309] = "STRIBOG256",
[310] = "STRIBOG512",
[311] = "GOSTR3411_CP",
[312] = "SHA3-224",
[313] = "SHA3-256",
[314] = "SHA3-384",
[315] = "SHA3-512",
[316] = "SHAKE128",
[317] = "SHAKE256",
[318] = "BLAKE2B_512",
[319] = "BLAKE2B_384",
[320] = "BLAKE2B_256",
[321] = "BLAKE2B_160",
[322] = "BLAKE2S_256",
[323] = "BLAKE2S_224",
[324] = "BLAKE2S_160",
[325] = "BLAKE2S_128",
[326] = "SM3",
[327] = "SHA512_256",
[328] = "SHA512_224",
};
const gcry_md_spec_t *
VasEBoot_crypto_lookup_md_by_algo (int algo)
{
const gcry_md_spec_t *md;
int first = 1;
while (1)
{
for (md = VasEBoot_digests; md; md = md->next)
if (algo == md->algo)
return md;
if (VasEBoot_crypto_autoload_hook && first && algo > 0 && algo < (int)ARRAY_SIZE(md_algos) && md_algos[algo]) {
VasEBoot_crypto_autoload_hook (md_algos[algo]);
} else
return NULL;
first = 0;
}
}
const gcry_cipher_spec_t *
VasEBoot_crypto_lookup_cipher_by_name (const char *name)
{
const gcry_cipher_spec_t *ciph;
int first = 1;
while (1)
{
for (ciph = VasEBoot_ciphers; ciph; ciph = ciph->next)
{
const char **alias;
if (VasEBoot_strcasecmp (name, ciph->name) == 0)
return ciph;
if (!ciph->aliases)
continue;
for (alias = ciph->aliases; *alias; alias++)
if (VasEBoot_strcasecmp (name, *alias) == 0)
return ciph;
}
if (VasEBoot_crypto_autoload_hook && first)
VasEBoot_crypto_autoload_hook (name);
else
return NULL;
first = 0;
}
}
VasEBoot_crypto_cipher_handle_t
VasEBoot_crypto_cipher_open (const struct gcry_cipher_spec *cipher)
{
VasEBoot_crypto_cipher_handle_t ret;
ret = VasEBoot_malloc (sizeof (*ret) + cipher->contextsize);
if (!ret)
return NULL;
ret->cipher = cipher;
return ret;
}
gcry_err_code_t
VasEBoot_crypto_cipher_set_key (VasEBoot_crypto_cipher_handle_t cipher,
const unsigned char *key,
unsigned keylen)
{
/* TODO: Fix this. It's ugly as hell. */
void *bulk_ops[100];
return cipher->cipher->setkey (cipher->ctx, key, keylen, (void *) bulk_ops);
}
gcry_err_code_t
VasEBoot_crypto_ecb_decrypt (VasEBoot_crypto_cipher_handle_t cipher,
void *out, const void *in, VasEBoot_size_t size)
{
const VasEBoot_uint8_t *inptr, *end;
VasEBoot_uint8_t *outptr;
VasEBoot_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 VasEBoot_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
VasEBoot_crypto_ecb_encrypt (VasEBoot_crypto_cipher_handle_t cipher,
void *out, const void *in, VasEBoot_size_t size)
{
const VasEBoot_uint8_t *inptr, *end;
VasEBoot_uint8_t *outptr;
VasEBoot_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 VasEBoot_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
VasEBoot_crypto_cbc_encrypt (VasEBoot_crypto_cipher_handle_t cipher,
void *out, const void *in, VasEBoot_size_t size,
void *iv_in)
{
VasEBoot_uint8_t *outptr;
const VasEBoot_uint8_t *inptr, *end;
void *iv;
VasEBoot_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 VasEBoot_uint8_t *) in + size;
iv = iv_in;
for (inptr = in, outptr = out; inptr < end;
inptr += blocksize, outptr += blocksize)
{
VasEBoot_crypto_xor (outptr, inptr, iv, blocksize);
cipher->cipher->encrypt (cipher->ctx, outptr, outptr);
iv = outptr;
}
VasEBoot_memcpy (iv_in, iv, blocksize);
return GPG_ERR_NO_ERROR;
}
gcry_err_code_t
VasEBoot_crypto_cbc_decrypt (VasEBoot_crypto_cipher_handle_t cipher,
void *out, const void *in, VasEBoot_size_t size,
void *iv)
{
const VasEBoot_uint8_t *inptr, *end;
VasEBoot_uint8_t *outptr;
VasEBoot_uint8_t ivt[VAS_EBOOT_CRYPTO_MAX_CIPHER_BLOCKSIZE];
VasEBoot_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 > VAS_EBOOT_CRYPTO_MAX_CIPHER_BLOCKSIZE)
return GPG_ERR_INV_ARG;
end = (const VasEBoot_uint8_t *) in + size;
for (inptr = in, outptr = out; inptr < end;
inptr += blocksize, outptr += blocksize)
{
VasEBoot_memcpy (ivt, inptr, blocksize);
cipher->cipher->decrypt (cipher->ctx, outptr, inptr);
VasEBoot_crypto_xor (outptr, outptr, iv, blocksize);
VasEBoot_memcpy (iv, ivt, blocksize);
}
return GPG_ERR_NO_ERROR;
}
/* Based on gcry/cipher/md.c. */
struct VasEBoot_crypto_hmac_handle *
VasEBoot_crypto_hmac_init (const struct gcry_md_spec *md,
const void *key, VasEBoot_size_t keylen)
{
VasEBoot_uint8_t *helpkey = NULL;
VasEBoot_uint8_t *ipad = NULL, *opad = NULL;
void *ctx = NULL, *ctx2 = NULL;
void *ctx_cache = NULL, *ctx2_cache = NULL;
struct VasEBoot_crypto_hmac_handle *ret = NULL;
unsigned i;
if (md->mdlen > md->blocksize)
return NULL;
ctx = VasEBoot_malloc (md->contextsize);
if (!ctx)
goto err;
ctx2 = VasEBoot_malloc (md->contextsize);
if (!ctx2)
goto err;
ctx_cache = VasEBoot_malloc (md->contextsize);
if (!ctx_cache)
goto err;
ctx2_cache = VasEBoot_malloc (md->contextsize);
if (!ctx2_cache)
goto err;
if ( keylen > md->blocksize )
{
helpkey = VasEBoot_malloc (md->mdlen);
if (!helpkey)
goto err;
VasEBoot_crypto_hash (md, helpkey, key, keylen);
key = helpkey;
keylen = md->mdlen;
}
ipad = VasEBoot_zalloc (md->blocksize);
if (!ipad)
goto err;
opad = VasEBoot_zalloc (md->blocksize);
if (!opad)
goto err;
VasEBoot_memcpy ( ipad, key, keylen );
VasEBoot_memcpy ( opad, key, keylen );
for (i=0; i < md->blocksize; i++ )
{
ipad[i] ^= 0x36;
opad[i] ^= 0x5c;
}
VasEBoot_free (helpkey);
helpkey = NULL;
/* inner pad */
md->init (ctx, 0);
md->write (ctx, ipad, md->blocksize);
VasEBoot_memcpy (ctx_cache, ctx, md->contextsize);
VasEBoot_memset (ipad, 0, md->blocksize);
VasEBoot_free (ipad);
ipad = NULL;
/* outer pad */
md->init (ctx2, 0);
md->write (ctx2, opad, md->blocksize);
VasEBoot_memcpy (ctx2_cache, ctx2, md->contextsize);
VasEBoot_memset (opad, 0, md->blocksize);
VasEBoot_free (opad);
opad = NULL;
ret = VasEBoot_malloc (sizeof (*ret));
if (!ret)
goto err;
ret->md = md;
ret->ctx = ctx;
ret->ctx2 = ctx2;
ret->ctx_cache = ctx_cache;
ret->ctx2_cache = ctx2_cache;
return ret;
err:
VasEBoot_free (helpkey);
VasEBoot_free (ctx);
VasEBoot_free (ctx2);
VasEBoot_free (ctx_cache);
VasEBoot_free (ctx2_cache);
VasEBoot_free (ipad);
VasEBoot_free (opad);
return NULL;
}
void
VasEBoot_crypto_hmac_write (struct VasEBoot_crypto_hmac_handle *hnd,
const void *data,
VasEBoot_size_t datalen)
{
hnd->md->write (hnd->ctx, data, datalen);
}
void
VasEBoot_crypto_hmac_fini (struct VasEBoot_crypto_hmac_handle *hnd, void *out)
{
VasEBoot_crypto_hmac_final (hnd, out);
VasEBoot_crypto_hmac_free (hnd);
}
void
VasEBoot_crypto_hmac_reset (struct VasEBoot_crypto_hmac_handle *hnd)
{
VasEBoot_memcpy (hnd->ctx, hnd->ctx_cache, hnd->md->contextsize);
VasEBoot_memcpy (hnd->ctx2, hnd->ctx2_cache, hnd->md->contextsize);
}
void
VasEBoot_crypto_hmac_final (struct VasEBoot_crypto_hmac_handle *hnd, void *out)
{
VasEBoot_uint8_t *p;
hnd->md->final (hnd->ctx);
hnd->md->read (hnd->ctx);
p = hnd->md->read (hnd->ctx);
hnd->md->write (hnd->ctx2, p, hnd->md->mdlen);
hnd->md->final (hnd->ctx2);
VasEBoot_memcpy (out, hnd->md->read (hnd->ctx2), hnd->md->mdlen);
}
void
VasEBoot_crypto_hmac_free (struct VasEBoot_crypto_hmac_handle *hnd)
{
VasEBoot_memset (hnd->ctx, 0, hnd->md->contextsize);
VasEBoot_free (hnd->ctx);
VasEBoot_memset (hnd->ctx2, 0, hnd->md->contextsize);
VasEBoot_free (hnd->ctx2);
VasEBoot_memset (hnd->ctx_cache, 0, hnd->md->contextsize);
VasEBoot_free (hnd->ctx_cache);
VasEBoot_memset (hnd->ctx2_cache, 0, hnd->md->contextsize);
VasEBoot_free (hnd->ctx2_cache);
VasEBoot_memset (hnd, 0, sizeof (*hnd));
VasEBoot_free (hnd);
}
gcry_err_code_t
VasEBoot_crypto_hmac_buffer (const struct gcry_md_spec *md,
const void *key, VasEBoot_size_t keylen,
const void *data, VasEBoot_size_t datalen, void *out)
{
struct VasEBoot_crypto_hmac_handle *hnd;
hnd = VasEBoot_crypto_hmac_init (md, key, keylen);
if (!hnd)
return GPG_ERR_OUT_OF_MEMORY;
VasEBoot_crypto_hmac_write (hnd, data, datalen);
VasEBoot_crypto_hmac_fini (hnd, out);
return GPG_ERR_NO_ERROR;
}
VasEBoot_err_t
VasEBoot_crypto_gcry_error (gcry_err_code_t in)
{
if (in == GPG_ERR_NO_ERROR)
return VAS_EBOOT_ERR_NONE;
return VAS_EBOOT_ACCESS_DENIED;
}
/*
* Compare byte arrays of length LEN, return 1 if it's not same,
* 0, otherwise.
*/
int
VasEBoot_crypto_memcmp (const void *b1, const void *b2, VasEBoot_size_t len)
{
const VasEBoot_uint8_t *a = b1;
const VasEBoot_uint8_t *b = b2;
int ab, ba;
VasEBoot_size_t i;
/* Constant-time compare. */
for (i = 0, ab = 0, ba = 0; i < len; i++)
{
/* If a[i] != b[i], either ab or ba will be negative. */
ab |= a[i] - b[i];
ba |= b[i] - a[i];
}
/* 'ab | ba' is negative when buffers are not equal, extract sign bit. */
return ((unsigned int)(ab | ba) >> (sizeof(unsigned int) * 8 - 1)) & 1;
}
void *
_gcry_malloc_secure (VasEBoot_size_t n)
{
return VasEBoot_malloc(n);
}
void *
_gcry_malloc (VasEBoot_size_t n)
{
return VasEBoot_malloc(n);
}
void *
_gcry_xmalloc(VasEBoot_size_t n)
{
void *ret = VasEBoot_malloc(n);
if (ret == NULL)
VasEBoot_fatal("_gcry_xmalloc failed");
return ret;
}
void *
_gcry_xmalloc_secure(VasEBoot_size_t n)
{
void *ret = VasEBoot_malloc(n);
if (ret == NULL)
VasEBoot_fatal("_gcry_xmalloc_secure failed");
return ret;
}
void _gcry_free (void *p)
{
VasEBoot_free (p);
}
void *
_gcry_xrealloc (void *a, VasEBoot_size_t n)
{
void *ret = VasEBoot_realloc(a, n);
if (ret == NULL)
VasEBoot_fatal("_gcry_xrealloc failed");
return ret;
}
void *
_gcry_xcalloc (VasEBoot_size_t n, VasEBoot_size_t m)
{
void *ret = VasEBoot_calloc(n, m);
if (ret == NULL)
VasEBoot_fatal("_gcry_xcalloc failed");
return ret;
}
void *
_gcry_xcalloc_secure (VasEBoot_size_t n, VasEBoot_size_t m)
{
void *ret = VasEBoot_calloc(n, m);
if (ret == NULL)
VasEBoot_fatal("_gcry_xcalloc_secure failed");
return ret;
}
int
_gcry_is_secure (const void *a)
{
(void) a;
return 0;
}
void _gcry_divide_by_zero (void)
{
VasEBoot_fatal("gcrypt division by zero");
}
#ifndef VAS_EBOOT_UTIL
int
VasEBoot_password_get (char buf[], unsigned buf_size)
{
unsigned cur_len = 0;
int key;
while (1)
{
key = VasEBoot_getkey ();
if (key == '\n' || key == '\r')
break;
if (key == VAS_EBOOT_TERM_ESC)
{
cur_len = 0;
break;
}
if (key == '\b')
{
if (cur_len)
cur_len--;
continue;
}
if (!VasEBoot_isprint (key))
continue;
if (cur_len + 2 < buf_size)
buf[cur_len++] = key;
}
VasEBoot_memset (buf + cur_len, 0, buf_size - cur_len);
VasEBoot_xputs ("\n");
VasEBoot_refresh ();
return (key != VAS_EBOOT_TERM_ESC);
}
#endif