blob: 6a1f662633545ab6890f2cb716220bc205e735bc [file] [log] [blame]
/* gc-libgcrypt.c --- Crypto wrappers around Libgcrypt for GC.
* Copyright (C) 2002-2020 Free Software Foundation, Inc.
*
* This file 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 file 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 file; if not, see <https://www.gnu.org/licenses/>.
*
*/
/* Note: This file is only built if GC uses Libgcrypt. */
#include <config.h>
/* Get prototype. */
#include "gc.h"
#include <stdlib.h>
#include <string.h>
/* Get libgcrypt API. */
#include <gcrypt.h>
#if GNULIB_GC_MD2
/* libgcrypt declares GCRY_MD_MD2 but does not actually implement the
MD2 algorithm. Therefore take the implementation from gnulib. */
# include "md2.h"
#endif
#if GNULIB_GC_SM3 && !LIBGCRYPT_HAS_MD_SM3
# include "sm3.h"
#endif
#include <assert.h>
#ifndef MIN_GCRYPT_VERSION
# define MIN_GCRYPT_VERSION "1.4.4"
#endif
/* Initialization. */
Gc_rc
gc_init (void)
{
gcry_error_t err;
err = gcry_control (GCRYCTL_ANY_INITIALIZATION_P);
if (err == GPG_ERR_NO_ERROR)
{
if (gcry_control (GCRYCTL_DISABLE_SECMEM, NULL, 0))
return GC_INIT_ERROR;
if (gcry_check_version (MIN_GCRYPT_VERSION) == NULL)
return GC_INIT_ERROR;
err = gcry_control (GCRYCTL_INITIALIZATION_FINISHED, NULL, 0);
if (err != GPG_ERR_NO_ERROR)
return GC_INIT_ERROR;
}
return GC_OK;
}
void
gc_done (void)
{
return;
}
#if GNULIB_GC_RANDOM
/* Randomness. */
Gc_rc
gc_nonce (char *data, size_t datalen)
{
gcry_create_nonce ((unsigned char *) data, datalen);
return GC_OK;
}
Gc_rc
gc_pseudo_random (char *data, size_t datalen)
{
gcry_randomize ((unsigned char *) data, datalen, GCRY_STRONG_RANDOM);
return GC_OK;
}
Gc_rc
gc_random (char *data, size_t datalen)
{
gcry_randomize ((unsigned char *) data, datalen, GCRY_VERY_STRONG_RANDOM);
return GC_OK;
}
#endif
/* Memory allocation. */
void
gc_set_allocators (gc_malloc_t func_malloc,
gc_malloc_t secure_malloc,
gc_secure_check_t secure_check,
gc_realloc_t func_realloc, gc_free_t func_free)
{
gcry_set_allocation_handler (func_malloc, secure_malloc, secure_check,
func_realloc, func_free);
}
/* Ciphers. */
Gc_rc
gc_cipher_open (Gc_cipher alg, Gc_cipher_mode mode,
gc_cipher_handle * outhandle)
{
int gcryalg, gcrymode;
gcry_error_t err;
switch (alg)
{
case GC_AES128:
gcryalg = GCRY_CIPHER_RIJNDAEL;
break;
case GC_AES192:
gcryalg = GCRY_CIPHER_RIJNDAEL;
break;
case GC_AES256:
gcryalg = GCRY_CIPHER_RIJNDAEL256;
break;
case GC_3DES:
gcryalg = GCRY_CIPHER_3DES;
break;
case GC_DES:
gcryalg = GCRY_CIPHER_DES;
break;
case GC_ARCFOUR128:
case GC_ARCFOUR40:
gcryalg = GCRY_CIPHER_ARCFOUR;
break;
case GC_ARCTWO40:
gcryalg = GCRY_CIPHER_RFC2268_40;
break;
#ifdef HAVE_CAMELLIA
case GC_CAMELLIA128:
gcryalg = GCRY_CIPHER_CAMELLIA128;
break;
case GC_CAMELLIA256:
gcryalg = GCRY_CIPHER_CAMELLIA256;
break;
#endif
default:
return GC_INVALID_CIPHER;
}
switch (mode)
{
case GC_ECB:
gcrymode = GCRY_CIPHER_MODE_ECB;
break;
case GC_CBC:
gcrymode = GCRY_CIPHER_MODE_CBC;
break;
case GC_STREAM:
gcrymode = GCRY_CIPHER_MODE_STREAM;
break;
default:
return GC_INVALID_CIPHER;
}
err = gcry_cipher_open ((gcry_cipher_hd_t *) outhandle,
gcryalg, gcrymode, 0);
if (gcry_err_code (err))
return GC_INVALID_CIPHER;
return GC_OK;
}
Gc_rc
gc_cipher_setkey (gc_cipher_handle handle, size_t keylen, const char *key)
{
gcry_error_t err;
err = gcry_cipher_setkey ((gcry_cipher_hd_t) handle, key, keylen);
if (gcry_err_code (err))
return GC_INVALID_CIPHER;
return GC_OK;
}
Gc_rc
gc_cipher_setiv (gc_cipher_handle handle, size_t ivlen, const char *iv)
{
gcry_error_t err;
err = gcry_cipher_setiv ((gcry_cipher_hd_t) handle, iv, ivlen);
if (gcry_err_code (err))
return GC_INVALID_CIPHER;
return GC_OK;
}
Gc_rc
gc_cipher_encrypt_inline (gc_cipher_handle handle, size_t len, char *data)
{
if (gcry_cipher_encrypt ((gcry_cipher_hd_t) handle,
data, len, NULL, len) != 0)
return GC_INVALID_CIPHER;
return GC_OK;
}
Gc_rc
gc_cipher_decrypt_inline (gc_cipher_handle handle, size_t len, char *data)
{
if (gcry_cipher_decrypt ((gcry_cipher_hd_t) handle,
data, len, NULL, len) != 0)
return GC_INVALID_CIPHER;
return GC_OK;
}
Gc_rc
gc_cipher_close (gc_cipher_handle handle)
{
gcry_cipher_close (handle);
return GC_OK;
}
/* Hashes. */
/* Maximum of GC_MD2_DIGEST_SIZE and GC_SM3_DIGEST_SIZE. */
#define MAX_DIGEST_SIZE 32
typedef struct _gc_hash_ctx {
Gc_hash alg;
Gc_hash_mode mode;
gcry_md_hd_t gch;
#if GNULIB_GC_MD2 || (GNULIB_GC_SM3 && !LIBGCRYPT_HAS_MD_SM3)
char hash[MAX_DIGEST_SIZE];
#endif
#if GNULIB_GC_MD2
struct md2_ctx md2Context;
#endif
#if GNULIB_GC_SM3 && !LIBGCRYPT_HAS_MD_SM3
struct sm3_ctx sm3Context;
#endif
} _gc_hash_ctx;
Gc_rc
gc_hash_open (Gc_hash hash, Gc_hash_mode mode, gc_hash_handle * outhandle)
{
_gc_hash_ctx *ctx;
int gcryalg = 0, gcrymode = 0;
gcry_error_t err;
Gc_rc rc = GC_OK;
ctx = calloc (sizeof (*ctx), 1);
if (!ctx)
return GC_MALLOC_ERROR;
ctx->alg = hash;
ctx->mode = mode;
switch (hash)
{
#if GNULIB_GC_MD2
case GC_MD2:
/* Not needed, because ctx is already zero-initialized. */
/*md2_init_ctx (&ctx->md2Context);*/
gcryalg = GCRY_MD_NONE;
break;
#endif
case GC_MD4:
gcryalg = GCRY_MD_MD4;
break;
case GC_MD5:
gcryalg = GCRY_MD_MD5;
break;
case GC_SHA1:
gcryalg = GCRY_MD_SHA1;
break;
case GC_SHA256:
gcryalg = GCRY_MD_SHA256;
break;
case GC_SHA384:
gcryalg = GCRY_MD_SHA384;
break;
case GC_SHA512:
gcryalg = GCRY_MD_SHA512;
break;
case GC_SHA224:
gcryalg = GCRY_MD_SHA224;
break;
case GC_RMD160:
gcryalg = GCRY_MD_RMD160;
break;
#if GNULIB_GC_SM3
case GC_SM3:
# if LIBGCRYPT_HAS_MD_SM3
gcryalg = GCRY_MD_SM3;
# else
sm3_init_ctx (&ctx->sm3Context);
gcryalg = GCRY_MD_NONE;
# endif
break;
#endif
default:
rc = GC_INVALID_HASH;
}
switch (mode)
{
case GC_NULL:
gcrymode = 0;
break;
case GC_HMAC:
gcrymode = GCRY_MD_FLAG_HMAC;
break;
default:
rc = GC_INVALID_HASH;
}
if (rc == GC_OK && gcryalg != GCRY_MD_NONE)
{
err = gcry_md_open (&ctx->gch, gcryalg, gcrymode);
if (gcry_err_code (err))
rc = GC_INVALID_HASH;
}
if (rc == GC_OK)
*outhandle = ctx;
else
free (ctx);
return rc;
}
Gc_rc
gc_hash_clone (gc_hash_handle handle, gc_hash_handle * outhandle)
{
_gc_hash_ctx *in = handle;
_gc_hash_ctx *out;
int err;
*outhandle = out = calloc (sizeof (*out), 1);
if (!out)
return GC_MALLOC_ERROR;
memcpy (out, in, sizeof (*out));
err = gcry_md_copy (&out->gch, in->gch);
if (err)
{
free (out);
return GC_INVALID_HASH;
}
return GC_OK;
}
size_t
gc_hash_digest_length (Gc_hash hash)
{
size_t len;
switch (hash)
{
case GC_MD2:
len = GC_MD2_DIGEST_SIZE;
break;
case GC_MD4:
len = GC_MD4_DIGEST_SIZE;
break;
case GC_MD5:
len = GC_MD5_DIGEST_SIZE;
break;
case GC_RMD160:
len = GC_RMD160_DIGEST_SIZE;
break;
case GC_SHA1:
len = GC_SHA1_DIGEST_SIZE;
break;
case GC_SHA256:
len = GC_SHA256_DIGEST_SIZE;
break;
case GC_SHA384:
len = GC_SHA384_DIGEST_SIZE;
break;
case GC_SHA512:
len = GC_SHA512_DIGEST_SIZE;
break;
case GC_SHA224:
len = GC_SHA224_DIGEST_SIZE;
break;
case GC_SM3:
len = GC_SM3_DIGEST_SIZE;
break;
default:
return 0;
}
return len;
}
void
gc_hash_hmac_setkey (gc_hash_handle handle, size_t len, const char *key)
{
_gc_hash_ctx *ctx = handle;
#if GNULIB_GC_MD2
if (ctx->alg != GC_MD2)
#endif
#if GNULIB_GC_SM3 && !LIBGCRYPT_HAS_MD_SM3
if (ctx->alg != GC_SM3)
#endif
gcry_md_setkey (ctx->gch, key, len);
}
void
gc_hash_write (gc_hash_handle handle, size_t len, const char *data)
{
_gc_hash_ctx *ctx = handle;
#if GNULIB_GC_MD2
if (ctx->alg == GC_MD2)
md2_process_bytes (data, len, &ctx->md2Context);
else
#endif
#if GNULIB_GC_SM3 && !LIBGCRYPT_HAS_MD_SM3
if (ctx->alg == GC_SM3)
sm3_process_bytes (data, len, &ctx->sm3Context);
else
#endif
gcry_md_write (ctx->gch, data, len);
}
const char *
gc_hash_read (gc_hash_handle handle)
{
_gc_hash_ctx *ctx = handle;
const char *digest;
#if GNULIB_GC_MD2
if (ctx->alg == GC_MD2)
{
md2_finish_ctx (&ctx->md2Context, ctx->hash);
digest = ctx->hash;
}
else
#endif
#if GNULIB_GC_SM3 && !LIBGCRYPT_HAS_MD_SM3
if (ctx->alg == GC_SM3)
{
sm3_finish_ctx (&ctx->sm3Context, ctx->hash);
digest = ctx->hash;
}
else
#endif
{
gcry_md_final (ctx->gch);
digest = (const char *) gcry_md_read (ctx->gch, 0);
}
return digest;
}
void
gc_hash_close (gc_hash_handle handle)
{
_gc_hash_ctx *ctx = handle;
#if GNULIB_GC_MD2
if (ctx->alg != GC_MD2)
#endif
#if GNULIB_GC_SM3 && !LIBGCRYPT_HAS_MD_SM3
if (ctx->alg != GC_SM3)
#endif
gcry_md_close (ctx->gch);
free (ctx);
}
Gc_rc
gc_hash_buffer (Gc_hash hash, const void *in, size_t inlen, char *resbuf)
{
int gcryalg;
switch (hash)
{
#if GNULIB_GC_MD2
case GC_MD2:
md2_buffer (in, inlen, resbuf);
return GC_OK;
#endif
#if GNULIB_GC_MD4
case GC_MD4:
gcryalg = GCRY_MD_MD4;
break;
#endif
#if GNULIB_GC_MD5
case GC_MD5:
gcryalg = GCRY_MD_MD5;
break;
#endif
#if GNULIB_GC_SHA1
case GC_SHA1:
gcryalg = GCRY_MD_SHA1;
break;
#endif
#if GNULIB_GC_SHA256
case GC_SHA256:
gcryalg = GCRY_MD_SHA256;
break;
#endif
#if GNULIB_GC_SHA384
case GC_SHA384:
gcryalg = GCRY_MD_SHA384;
break;
#endif
#if GNULIB_GC_SHA512
case GC_SHA512:
gcryalg = GCRY_MD_SHA512;
break;
#endif
#if GNULIB_GC_SHA224
case GC_SHA224:
gcryalg = GCRY_MD_SHA224;
break;
#endif
#if GNULIB_GC_RMD160
case GC_RMD160:
gcryalg = GCRY_MD_RMD160;
break;
#endif
#if GNULIB_GC_SM3
case GC_SM3:
# if !LIBGCRYPT_HAS_MD_SM3
sm3_buffer (in, inlen, resbuf);
return GC_OK;
# else
gcryalg = GCRY_MD_SM3;
break;
# endif
#endif
default:
return GC_INVALID_HASH;
}
gcry_md_hash_buffer (gcryalg, resbuf, in, inlen);
return GC_OK;
}
/* One-call interface. */
#if GNULIB_GC_MD2
Gc_rc
gc_md2 (const void *in, size_t inlen, void *resbuf)
{
md2_buffer (in, inlen, resbuf);
return GC_OK;
}
#endif
#if GNULIB_GC_MD4
Gc_rc
gc_md4 (const void *in, size_t inlen, void *resbuf)
{
size_t outlen = gcry_md_get_algo_dlen (GCRY_MD_MD4);
gcry_md_hd_t hd;
gpg_error_t err;
unsigned char *p;
assert (outlen == GC_MD4_DIGEST_SIZE);
err = gcry_md_open (&hd, GCRY_MD_MD4, 0);
if (err != GPG_ERR_NO_ERROR)
return GC_INVALID_HASH;
gcry_md_write (hd, in, inlen);
p = gcry_md_read (hd, GCRY_MD_MD4);
if (p == NULL)
{
gcry_md_close (hd);
return GC_INVALID_HASH;
}
memcpy (resbuf, p, outlen);
gcry_md_close (hd);
return GC_OK;
}
#endif
#if GNULIB_GC_MD5
Gc_rc
gc_md5 (const void *in, size_t inlen, void *resbuf)
{
size_t outlen = gcry_md_get_algo_dlen (GCRY_MD_MD5);
gcry_md_hd_t hd;
gpg_error_t err;
unsigned char *p;
assert (outlen == GC_MD5_DIGEST_SIZE);
err = gcry_md_open (&hd, GCRY_MD_MD5, 0);
if (err != GPG_ERR_NO_ERROR)
return GC_INVALID_HASH;
gcry_md_write (hd, in, inlen);
p = gcry_md_read (hd, GCRY_MD_MD5);
if (p == NULL)
{
gcry_md_close (hd);
return GC_INVALID_HASH;
}
memcpy (resbuf, p, outlen);
gcry_md_close (hd);
return GC_OK;
}
#endif
#if GNULIB_GC_SHA1
Gc_rc
gc_sha1 (const void *in, size_t inlen, void *resbuf)
{
size_t outlen = gcry_md_get_algo_dlen (GCRY_MD_SHA1);
gcry_md_hd_t hd;
gpg_error_t err;
unsigned char *p;
assert (outlen == GC_SHA1_DIGEST_SIZE);
err = gcry_md_open (&hd, GCRY_MD_SHA1, 0);
if (err != GPG_ERR_NO_ERROR)
return GC_INVALID_HASH;
gcry_md_write (hd, in, inlen);
p = gcry_md_read (hd, GCRY_MD_SHA1);
if (p == NULL)
{
gcry_md_close (hd);
return GC_INVALID_HASH;
}
memcpy (resbuf, p, outlen);
gcry_md_close (hd);
return GC_OK;
}
#endif
#if GNULIB_GC_SHA256
Gc_rc
gc_sha256 (const void *in, size_t inlen, void *resbuf)
{
size_t outlen = gcry_md_get_algo_dlen (GCRY_MD_SHA256);
gcry_md_hd_t hd;
gpg_error_t err;
unsigned char *p;
assert (outlen == GC_SHA256_DIGEST_SIZE);
err = gcry_md_open (&hd, GCRY_MD_SHA256, 0);
if (err != GPG_ERR_NO_ERROR)
return GC_INVALID_HASH;
gcry_md_write (hd, in, inlen);
p = gcry_md_read (hd, GCRY_MD_SHA256);
if (p == NULL)
{
gcry_md_close (hd);
return GC_INVALID_HASH;
}
memcpy (resbuf, p, outlen);
gcry_md_close (hd);
return GC_OK;
}
#endif
#if GNULIB_GC_SHA512
Gc_rc
gc_sha512 (const void *in, size_t inlen, void *resbuf)
{
size_t outlen = gcry_md_get_algo_dlen (GCRY_MD_SHA512);
gcry_md_hd_t hd;
gpg_error_t err;
unsigned char *p;
assert (outlen == GC_SHA512_DIGEST_SIZE);
err = gcry_md_open (&hd, GCRY_MD_SHA512, 0);
if (err != GPG_ERR_NO_ERROR)
return GC_INVALID_HASH;
gcry_md_write (hd, in, inlen);
p = gcry_md_read (hd, GCRY_MD_SHA512);
if (p == NULL)
{
gcry_md_close (hd);
return GC_INVALID_HASH;
}
memcpy (resbuf, p, outlen);
gcry_md_close (hd);
return GC_OK;
}
#endif
#if GNULIB_GC_SM3
Gc_rc
gc_sm3 (const void *in, size_t inlen, void *resbuf)
{
# if !LIBGCRYPT_HAS_MD_SM3
sm3_buffer (in, inlen, resbuf);
return GC_OK;
# else
size_t outlen = gcry_md_get_algo_dlen (GCRY_MD_SM3);
gcry_md_hd_t hd;
gpg_error_t err;
unsigned char *p;
assert (outlen == GC_SM3_DIGEST_SIZE);
err = gcry_md_open (&hd, GCRY_MD_SM3, 0);
if (err != GPG_ERR_NO_ERROR)
return GC_INVALID_HASH;
gcry_md_write (hd, in, inlen);
p = gcry_md_read (hd, GCRY_MD_SM3);
if (p == NULL)
{
gcry_md_close (hd);
return GC_INVALID_HASH;
}
memcpy (resbuf, p, outlen);
gcry_md_close (hd);
return GC_OK;
# endif
}
#endif
#if GNULIB_GC_HMAC_MD5
Gc_rc
gc_hmac_md5 (const void *key, size_t keylen,
const void *in, size_t inlen, char *resbuf)
{
size_t hlen = gcry_md_get_algo_dlen (GCRY_MD_MD5);
gcry_md_hd_t mdh;
unsigned char *hash;
gpg_error_t err;
assert (hlen == GC_MD5_DIGEST_SIZE);
err = gcry_md_open (&mdh, GCRY_MD_MD5, GCRY_MD_FLAG_HMAC);
if (err != GPG_ERR_NO_ERROR)
return GC_INVALID_HASH;
err = gcry_md_setkey (mdh, key, keylen);
if (err != GPG_ERR_NO_ERROR)
{
gcry_md_close (mdh);
return GC_INVALID_HASH;
}
gcry_md_write (mdh, in, inlen);
hash = gcry_md_read (mdh, GCRY_MD_MD5);
if (hash == NULL)
{
gcry_md_close (mdh);
return GC_INVALID_HASH;
}
memcpy (resbuf, hash, hlen);
gcry_md_close (mdh);
return GC_OK;
}
#endif
#if GNULIB_GC_HMAC_SHA1
Gc_rc
gc_hmac_sha1 (const void *key, size_t keylen,
const void *in, size_t inlen, char *resbuf)
{
size_t hlen = gcry_md_get_algo_dlen (GCRY_MD_SHA1);
gcry_md_hd_t mdh;
unsigned char *hash;
gpg_error_t err;
assert (hlen == GC_SHA1_DIGEST_SIZE);
err = gcry_md_open (&mdh, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC);
if (err != GPG_ERR_NO_ERROR)
return GC_INVALID_HASH;
err = gcry_md_setkey (mdh, key, keylen);
if (err != GPG_ERR_NO_ERROR)
{
gcry_md_close (mdh);
return GC_INVALID_HASH;
}
gcry_md_write (mdh, in, inlen);
hash = gcry_md_read (mdh, GCRY_MD_SHA1);
if (hash == NULL)
{
gcry_md_close (mdh);
return GC_INVALID_HASH;
}
memcpy (resbuf, hash, hlen);
gcry_md_close (mdh);
return GC_OK;
}
#endif
#if GNULIB_GC_HMAC_SHA256
Gc_rc
gc_hmac_sha256 (const void *key, size_t keylen,
const void *in, size_t inlen, char *resbuf)
{
size_t hlen = gcry_md_get_algo_dlen (GCRY_MD_SHA256);
gcry_md_hd_t mdh;
unsigned char *hash;
gpg_error_t err;
assert (hlen == GC_SHA256_DIGEST_SIZE);
err = gcry_md_open (&mdh, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
if (err != GPG_ERR_NO_ERROR)
return GC_INVALID_HASH;
err = gcry_md_setkey (mdh, key, keylen);
if (err != GPG_ERR_NO_ERROR)
{
gcry_md_close (mdh);
return GC_INVALID_HASH;
}
gcry_md_write (mdh, in, inlen);
hash = gcry_md_read (mdh, GCRY_MD_SHA256);
if (hash == NULL)
{
gcry_md_close (mdh);
return GC_INVALID_HASH;
}
memcpy (resbuf, hash, hlen);
gcry_md_close (mdh);
return GC_OK;
}
#endif
#if GNULIB_GC_HMAC_SHA512
Gc_rc
gc_hmac_sha512 (const void *key, size_t keylen,
const void *in, size_t inlen, char *resbuf)
{
size_t hlen = gcry_md_get_algo_dlen (GCRY_MD_SHA512);
gcry_md_hd_t mdh;
unsigned char *hash;
gpg_error_t err;
assert (hlen == GC_SHA512_DIGEST_SIZE);
err = gcry_md_open (&mdh, GCRY_MD_SHA512, GCRY_MD_FLAG_HMAC);
if (err != GPG_ERR_NO_ERROR)
return GC_INVALID_HASH;
err = gcry_md_setkey (mdh, key, keylen);
if (err != GPG_ERR_NO_ERROR)
{
gcry_md_close (mdh);
return GC_INVALID_HASH;
}
gcry_md_write (mdh, in, inlen);
hash = gcry_md_read (mdh, GCRY_MD_SHA512);
if (hash == NULL)
{
gcry_md_close (mdh);
return GC_INVALID_HASH;
}
memcpy (resbuf, hash, hlen);
gcry_md_close (mdh);
return GC_OK;
}
#endif