view lib/gc-gnulib.c @ 16366:bb182ee4a09d

maint: replace FSF snail-mail addresses with URLs * config/argz.mk, lib/accept4.c, lib/alignof.h, lib/alloca.in.h: * lib/alphasort.c, lib/arcfour.c, lib/arcfour.h, lib/arctwo.c: * lib/arctwo.h, lib/argz.c, lib/arpa_inet.in.h, lib/asnprintf.c: * lib/asprintf.c, lib/assert.in.h, lib/base32.c, lib/base32.h: * lib/base64.c, lib/base64.h, lib/c-ctype.c, lib/c-ctype.h: * lib/c-strcase.h, lib/c-strcasecmp.c, lib/c-strncasecmp.c: * lib/check-version.c, lib/check-version.h, lib/config.charset: * lib/ctype.in.h, lib/des.c, lib/des.h, lib/dup3.c, lib/errno.in.h: * lib/float+.h, lib/fnmatch.c, lib/fnmatch.in.h, lib/fnmatch_loop.c: * lib/fseeko.c, lib/gai_strerror.c, lib/gc-gnulib.c: * lib/gc-libgcrypt.c, lib/gc-pbkdf2-sha1.c, lib/gc.h: * lib/getaddrinfo.c, lib/getdelim.c, lib/getfilecon.c, lib/getline.c: * lib/getlogin_r.c, lib/getpass.c, lib/getpass.h, lib/gettext.h: * lib/gettimeofday.c, lib/glob.in.h, lib/glthread/cond.c: * lib/glthread/cond.h, lib/glthread/lock.c, lib/glthread/lock.h: * lib/glthread/thread.c, lib/glthread/thread.h: * lib/glthread/threadlib.c, lib/glthread/yield.h, lib/hmac-md5.c: * lib/hmac-sha1.c, lib/hmac.h, lib/iconv.c, lib/iconv.in.h: * lib/iconv_close.c, lib/iconv_open.c, lib/inet_ntop.c, lib/isfinite.c: * lib/isinf.c, lib/iswblank.c, lib/langinfo.in.h, lib/link.c: * lib/localcharset.c, lib/localcharset.h, lib/lseek.c, lib/malloc.c: * lib/malloca.c, lib/malloca.h, lib/md2.c, lib/md2.h, lib/md4.c: * lib/md4.h, lib/md5.c, lib/md5.h, lib/memmem.c, lib/mempcpy.c: * lib/memset.c, lib/memxor.c, lib/memxor.h, lib/minmax.h, lib/mktime.c: * lib/msvc-inval.c, lib/msvc-inval.h, lib/msvc-nothrow.c: * lib/msvc-nothrow.h, lib/netdb.in.h, lib/netinet_in.in.h, lib/nproc.c: * lib/nproc.h, lib/obstack_printf.c, lib/pathmax.h, lib/pipe.c: * lib/pipe2.c, lib/poll.c, lib/poll.in.h, lib/printf-args.c: * lib/printf-args.h, lib/printf-parse.c, lib/printf-parse.h: * lib/pselect.c, lib/pthread.in.h, lib/pty-private.h, lib/pty.in.h: * lib/read-file.c, lib/read-file.h, lib/ref-add.sin, lib/ref-del.sin: * lib/regcomp.c, lib/regex.c, lib/regex.h, lib/regex_internal.c: * lib/regex_internal.h, lib/regexec.c, lib/rijndael-alg-fst.c: * lib/rijndael-alg-fst.h, lib/rijndael-api-fst.c: * lib/rijndael-api-fst.h, lib/rint.c, lib/rintf.c, lib/rintl.c: * lib/round.c, lib/roundf.c, lib/roundl.c, lib/scandir.c, lib/select.c: * lib/sha1.c, lib/sha1.h, lib/size_max.h, lib/snprintf.c: * lib/stdalign.in.h, lib/stdarg.in.h, lib/stdbool.in.h: * lib/stddef.in.h, lib/stdint.in.h, lib/stdio.in.h, lib/str-kmp.h: * lib/str-two-way.h, lib/strcasecmp.c, lib/strcasestr.c, lib/strdup.c: * lib/striconv.c, lib/striconv.h, lib/string.in.h, lib/strings.in.h: * lib/strncasecmp.c, lib/strndup.c, lib/strnlen.c, lib/strpbrk.c: * lib/strptime.c, lib/strsep.c, lib/strstr.c, lib/strverscmp.c: * lib/sys_file.in.h, lib/sys_ioctl.in.h, lib/sys_select.in.h: * lib/sys_socket.in.h, lib/sys_stat.in.h, lib/sys_time.in.h: * lib/sys_times.in.h, lib/sys_types.in.h, lib/sys_uio.in.h: * lib/sys_utsname.in.h, lib/sys_wait.in.h, lib/tcgetsid.c: * lib/termios.in.h, lib/time.in.h, lib/time_r.c, lib/timegm.c: * lib/times.c, lib/unictype/3level.h, lib/unictype/3levelbit.h: * lib/unistd.in.h, lib/vasnprintf.c, lib/vasnprintf.h, lib/vasprintf.c: * lib/vsnprintf.c, lib/waitpid.c, lib/wchar.in.h, lib/wctype.in.h: * lib/xsize.h, tests/test-closein.c, tests/test-des.c: * tests/test-fclose.c, tests/test-fgetc.c, tests/test-filevercmp.c: * tests/test-fputc.c, tests/test-fread.c, tests/test-fwrite.c: * tests/test-gc-arcfour.c, tests/test-gc-arctwo.c, tests/test-gc-des.c: * tests/test-gc-hmac-md5.c, tests/test-gc-hmac-sha1.c: * tests/test-gc-md2.c, tests/test-gc-md4.c, tests/test-gc-md5.c: * tests/test-gc-pbkdf2-sha1.c, tests/test-gc-rijndael.c: * tests/test-gc-sha1.c, tests/test-gc.c, tests/test-getdelim.c: * tests/test-getline.c, tests/test-getndelim2.c, tests/test-md2.c: * tests/test-md4.c, tests/test-parse-datetime.c, tests/test-perror.c: * tests/test-perror2.c, tests/test-pipe.c, tests/test-pipe2.c: * tests/test-poll.c, tests/test-quotearg-simple.c: * tests/test-quotearg.c, tests/test-quotearg.h: * tests/test-round-ieee.c, tests/test-round1.c: * tests/test-roundf-ieee.c, tests/test-roundf1.c: * tests/test-roundl-ieee.c, tests/test-roundl.c: * tests/test-safe-alloc.c, tests/test-sigpipe.c: * tests/test-spawn-pipe-child.c, tests/test-spawn-pipe-main.c: * tests/test-strerror.c, tests/test-strerror_r.c: * tests/test-strsignal.c, tests/test-strverscmp.c: * tests/test-xmemdup0.c: Replace FSF snail mail addresses with URLs, as per GNU coding standards. See glibc bug <http://sourceware.org/bugzilla/show_bug.cgi?id=13673>.
author Paul Eggert <eggert@cs.ucla.edu>
date Thu, 09 Feb 2012 21:39:05 -0800 (2012-02-10)
parents 8250f2777afc
children e542fd46ad6f
line wrap: on
line source
/* gc-gnulib.c --- Common gnulib internal crypto interface functions
 * Copyright (C) 2002-2012 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 <http://www.gnu.org/licenses/>.
 *
 */

/* Note: This file is only built if GC uses internal functions. */

#include <config.h>

/* Get prototype. */
#include "gc.h"

#include <stdlib.h>
#include <string.h>

/* For randomize. */
#ifdef GNULIB_GC_RANDOM
# include <unistd.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <errno.h>
#endif

/* Hashes. */
#ifdef GNULIB_GC_MD2
# include "md2.h"
#endif
#ifdef GNULIB_GC_MD4
# include "md4.h"
#endif
#ifdef GNULIB_GC_MD5
# include "md5.h"
#endif
#ifdef GNULIB_GC_SHA1
# include "sha1.h"
#endif
#if defined(GNULIB_GC_HMAC_MD5) || defined(GNULIB_GC_HMAC_SHA1)
# include "hmac.h"
#endif

/* Ciphers. */
#ifdef GNULIB_GC_ARCFOUR
# include "arcfour.h"
#endif
#ifdef GNULIB_GC_ARCTWO
# include "arctwo.h"
#endif
#ifdef GNULIB_GC_DES
# include "des.h"
#endif
#ifdef GNULIB_GC_RIJNDAEL
# include "rijndael-api-fst.h"
#endif

/* The results of open() in this file are not used with fchdir,
   therefore save some unnecessary work in fchdir.c.  */
#undef open
#undef close

#ifdef GNULIB_GC_RANDOM
# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
#  include <windows.h>
#  include <wincrypt.h>
HCRYPTPROV g_hProv = 0;
#  ifndef PROV_INTEL_SEC
#   define PROV_INTEL_SEC 22
#  endif
#  ifndef CRYPT_VERIFY_CONTEXT
#   define CRYPT_VERIFY_CONTEXT 0xF0000000
#  endif
# endif
#endif

Gc_rc
gc_init (void)
{
#ifdef GNULIB_GC_RANDOM
# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
  if (g_hProv)
    CryptReleaseContext (g_hProv, 0);

  /* There is no need to create a container for just random data, so
     we can use CRYPT_VERIFY_CONTEXT (one call) see:
     http://blogs.msdn.com/dangriff/archive/2003/11/19/51709.aspx */

  /* We first try to use the Intel PIII RNG if drivers are present */
  if (!CryptAcquireContext (&g_hProv, NULL, NULL,
                            PROV_INTEL_SEC, CRYPT_VERIFY_CONTEXT))
    {
      /* not a PIII or no drivers available, use default RSA CSP */
      if (!CryptAcquireContext (&g_hProv, NULL, NULL,
                                PROV_RSA_FULL, CRYPT_VERIFY_CONTEXT))
        return GC_RANDOM_ERROR;
    }
# endif
#endif

  return GC_OK;
}

void
gc_done (void)
{
#ifdef GNULIB_GC_RANDOM
# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
  if (g_hProv)
    {
      CryptReleaseContext (g_hProv, 0);
      g_hProv = 0;
    }
# endif
#endif

  return;
}

#ifdef GNULIB_GC_RANDOM

/* Randomness. */

static Gc_rc
randomize (int level, char *data, size_t datalen)
{
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
  if (!g_hProv)
    return GC_RANDOM_ERROR;
  CryptGenRandom (g_hProv, (DWORD) datalen, data);
#else
  int fd;
  const char *device;
  size_t len = 0;
  int rc;

  switch (level)
    {
    case 0:
      device = NAME_OF_NONCE_DEVICE;
      break;

    case 1:
      device = NAME_OF_PSEUDO_RANDOM_DEVICE;
      break;

    default:
      device = NAME_OF_RANDOM_DEVICE;
      break;
    }

  if (strcmp (device, "no") == 0)
    return GC_RANDOM_ERROR;

  fd = open (device, O_RDONLY);
  if (fd < 0)
    return GC_RANDOM_ERROR;

  do
    {
      ssize_t tmp;

      tmp = read (fd, data, datalen);

      if (tmp < 0)
        {
          int save_errno = errno;
          close (fd);
          errno = save_errno;
          return GC_RANDOM_ERROR;
        }

      len += tmp;
    }
  while (len < datalen);

  rc = close (fd);
  if (rc < 0)
    return GC_RANDOM_ERROR;
#endif

  return GC_OK;
}

Gc_rc
gc_nonce (char *data, size_t datalen)
{
  return randomize (0, data, datalen);
}

Gc_rc
gc_pseudo_random (char *data, size_t datalen)
{
  return randomize (1, data, datalen);
}

Gc_rc
gc_random (char *data, size_t datalen)
{
  return randomize (2, data, datalen);
}

#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)
{
  return;
}

/* Ciphers. */

typedef struct _gc_cipher_ctx
{
  Gc_cipher alg;
  Gc_cipher_mode mode;
#ifdef GNULIB_GC_ARCTWO
  arctwo_context arctwoContext;
  char arctwoIV[ARCTWO_BLOCK_SIZE];
#endif
#ifdef GNULIB_GC_ARCFOUR
  arcfour_context arcfourContext;
#endif
#ifdef GNULIB_GC_DES
  gl_des_ctx desContext;
#endif
#ifdef GNULIB_GC_RIJNDAEL
  rijndaelKeyInstance aesEncKey;
  rijndaelKeyInstance aesDecKey;
  rijndaelCipherInstance aesContext;
#endif
} _gc_cipher_ctx;

Gc_rc
gc_cipher_open (Gc_cipher alg, Gc_cipher_mode mode,
                gc_cipher_handle * outhandle)
{
  _gc_cipher_ctx *ctx;
  Gc_rc rc = GC_OK;

  ctx = calloc (sizeof (*ctx), 1);
  if (!ctx)
    return GC_MALLOC_ERROR;

  ctx->alg = alg;
  ctx->mode = mode;

  switch (alg)
    {
#ifdef GNULIB_GC_ARCTWO
    case GC_ARCTWO40:
      switch (mode)
        {
        case GC_ECB:
        case GC_CBC:
          break;

        default:
          rc = GC_INVALID_CIPHER;
        }
      break;
#endif

#ifdef GNULIB_GC_ARCFOUR
    case GC_ARCFOUR128:
    case GC_ARCFOUR40:
      switch (mode)
        {
        case GC_STREAM:
          break;

        default:
          rc = GC_INVALID_CIPHER;
        }
      break;
#endif

#ifdef GNULIB_GC_DES
    case GC_DES:
      switch (mode)
        {
        case GC_ECB:
          break;

        default:
          rc = GC_INVALID_CIPHER;
        }
      break;
#endif

#ifdef GNULIB_GC_RIJNDAEL
    case GC_AES128:
    case GC_AES192:
    case GC_AES256:
      switch (mode)
        {
        case GC_ECB:
        case GC_CBC:
          break;

        default:
          rc = GC_INVALID_CIPHER;
        }
      break;
#endif

    default:
      rc = GC_INVALID_CIPHER;
    }

  if (rc == GC_OK)
    *outhandle = ctx;
  else
    free (ctx);

  return rc;
}

Gc_rc
gc_cipher_setkey (gc_cipher_handle handle, size_t keylen, const char *key)
{
  _gc_cipher_ctx *ctx = handle;

  switch (ctx->alg)
    {
#ifdef GNULIB_GC_ARCTWO
    case GC_ARCTWO40:
      arctwo_setkey (&ctx->arctwoContext, keylen, key);
      break;
#endif

#ifdef GNULIB_GC_ARCFOUR
    case GC_ARCFOUR128:
    case GC_ARCFOUR40:
      arcfour_setkey (&ctx->arcfourContext, key, keylen);
      break;
#endif

#ifdef GNULIB_GC_DES
    case GC_DES:
      if (keylen != 8)
        return GC_INVALID_CIPHER;
      gl_des_setkey (&ctx->desContext, key);
      break;
#endif

#ifdef GNULIB_GC_RIJNDAEL
    case GC_AES128:
    case GC_AES192:
    case GC_AES256:
      {
        rijndael_rc rc;
        size_t i;
        char keyMaterial[RIJNDAEL_MAX_KEY_SIZE + 1];

        for (i = 0; i < keylen; i++)
          sprintf (&keyMaterial[2 * i], "%02x", key[i] & 0xFF);

        rc = rijndaelMakeKey (&ctx->aesEncKey, RIJNDAEL_DIR_ENCRYPT,
                              keylen * 8, keyMaterial);
        if (rc < 0)
          return GC_INVALID_CIPHER;

        rc = rijndaelMakeKey (&ctx->aesDecKey, RIJNDAEL_DIR_DECRYPT,
                              keylen * 8, keyMaterial);
        if (rc < 0)
          return GC_INVALID_CIPHER;

        rc = rijndaelCipherInit (&ctx->aesContext, RIJNDAEL_MODE_ECB, NULL);
        if (rc < 0)
          return GC_INVALID_CIPHER;
      }
      break;
#endif

    default:
      return GC_INVALID_CIPHER;
    }

  return GC_OK;
}

Gc_rc
gc_cipher_setiv (gc_cipher_handle handle, size_t ivlen, const char *iv)
{
  _gc_cipher_ctx *ctx = handle;

  switch (ctx->alg)
    {
#ifdef GNULIB_GC_ARCTWO
    case GC_ARCTWO40:
      if (ivlen != ARCTWO_BLOCK_SIZE)
        return GC_INVALID_CIPHER;
      memcpy (ctx->arctwoIV, iv, ivlen);
      break;
#endif

#ifdef GNULIB_GC_RIJNDAEL
    case GC_AES128:
    case GC_AES192:
    case GC_AES256:
      switch (ctx->mode)
        {
        case GC_ECB:
          /* Doesn't use IV. */
          break;

        case GC_CBC:
          {
            rijndael_rc rc;
            size_t i;
            char ivMaterial[2 * RIJNDAEL_MAX_IV_SIZE + 1];

            for (i = 0; i < ivlen; i++)
              sprintf (&ivMaterial[2 * i], "%02x", iv[i] & 0xFF);

            rc = rijndaelCipherInit (&ctx->aesContext, RIJNDAEL_MODE_CBC,
                                     ivMaterial);
            if (rc < 0)
              return GC_INVALID_CIPHER;
          }
          break;

        default:
          return GC_INVALID_CIPHER;
        }
      break;
#endif

    default:
      return GC_INVALID_CIPHER;
    }

  return GC_OK;
}

Gc_rc
gc_cipher_encrypt_inline (gc_cipher_handle handle, size_t len, char *data)
{
  _gc_cipher_ctx *ctx = handle;

  switch (ctx->alg)
    {
#ifdef GNULIB_GC_ARCTWO
    case GC_ARCTWO40:
      switch (ctx->mode)
        {
        case GC_ECB:
          arctwo_encrypt (&ctx->arctwoContext, data, data, len);
          break;

        case GC_CBC:
          for (; len >= ARCTWO_BLOCK_SIZE; len -= ARCTWO_BLOCK_SIZE,
               data += ARCTWO_BLOCK_SIZE)
            {
              size_t i;
              for (i = 0; i < ARCTWO_BLOCK_SIZE; i++)
                data[i] ^= ctx->arctwoIV[i];
              arctwo_encrypt (&ctx->arctwoContext, data, data,
                              ARCTWO_BLOCK_SIZE);
              memcpy (ctx->arctwoIV, data, ARCTWO_BLOCK_SIZE);
            }
          break;

        default:
          return GC_INVALID_CIPHER;
        }
      break;
#endif

#ifdef GNULIB_GC_ARCFOUR
    case GC_ARCFOUR128:
    case GC_ARCFOUR40:
      arcfour_stream (&ctx->arcfourContext, data, data, len);
      break;
#endif

#ifdef GNULIB_GC_DES
    case GC_DES:
      for (; len >= 8; len -= 8, data += 8)
        gl_des_ecb_encrypt (&ctx->desContext, data, data);
      break;
#endif

#ifdef GNULIB_GC_RIJNDAEL
    case GC_AES128:
    case GC_AES192:
    case GC_AES256:
      {
        int nblocks;

        nblocks = rijndaelBlockEncrypt (&ctx->aesContext, &ctx->aesEncKey,
                                        data, 8 * len, data);
        if (nblocks < 0)
          return GC_INVALID_CIPHER;
      }
      break;
#endif

    default:
      return GC_INVALID_CIPHER;
    }

  return GC_OK;
}

Gc_rc
gc_cipher_decrypt_inline (gc_cipher_handle handle, size_t len, char *data)
{
  _gc_cipher_ctx *ctx = handle;

  switch (ctx->alg)
    {
#ifdef GNULIB_GC_ARCTWO
    case GC_ARCTWO40:
      switch (ctx->mode)
        {
        case GC_ECB:
          arctwo_decrypt (&ctx->arctwoContext, data, data, len);
          break;

        case GC_CBC:
          for (; len >= ARCTWO_BLOCK_SIZE; len -= ARCTWO_BLOCK_SIZE,
               data += ARCTWO_BLOCK_SIZE)
            {
              char tmpIV[ARCTWO_BLOCK_SIZE];
              size_t i;
              memcpy (tmpIV, data, ARCTWO_BLOCK_SIZE);
              arctwo_decrypt (&ctx->arctwoContext, data, data,
                              ARCTWO_BLOCK_SIZE);
              for (i = 0; i < ARCTWO_BLOCK_SIZE; i++)
                data[i] ^= ctx->arctwoIV[i];
              memcpy (ctx->arctwoIV, tmpIV, ARCTWO_BLOCK_SIZE);
            }
          break;

        default:
          return GC_INVALID_CIPHER;
        }
      break;
#endif

#ifdef GNULIB_GC_ARCFOUR
    case GC_ARCFOUR128:
    case GC_ARCFOUR40:
      arcfour_stream (&ctx->arcfourContext, data, data, len);
      break;
#endif

#ifdef GNULIB_GC_DES
    case GC_DES:
      for (; len >= 8; len -= 8, data += 8)
        gl_des_ecb_decrypt (&ctx->desContext, data, data);
      break;
#endif

#ifdef GNULIB_GC_RIJNDAEL
    case GC_AES128:
    case GC_AES192:
    case GC_AES256:
      {
        int nblocks;

        nblocks = rijndaelBlockDecrypt (&ctx->aesContext, &ctx->aesDecKey,
                                        data, 8 * len, data);
        if (nblocks < 0)
          return GC_INVALID_CIPHER;
      }
      break;
#endif

    default:
      return GC_INVALID_CIPHER;
    }

  return GC_OK;
}

Gc_rc
gc_cipher_close (gc_cipher_handle handle)
{
  _gc_cipher_ctx *ctx = handle;

  free (ctx);

  return GC_OK;
}

/* Hashes. */

#define MAX_DIGEST_SIZE 20

typedef struct _gc_hash_ctx
{
  Gc_hash alg;
  Gc_hash_mode mode;
  char hash[MAX_DIGEST_SIZE];
#ifdef GNULIB_GC_MD2
  struct md2_ctx md2Context;
#endif
#ifdef GNULIB_GC_MD4
  struct md4_ctx md4Context;
#endif
#ifdef GNULIB_GC_MD5
  struct md5_ctx md5Context;
#endif
#ifdef GNULIB_GC_SHA1
  struct sha1_ctx sha1Context;
#endif
} _gc_hash_ctx;

Gc_rc
gc_hash_open (Gc_hash hash, Gc_hash_mode mode, gc_hash_handle * outhandle)
{
  _gc_hash_ctx *ctx;
  Gc_rc rc = GC_OK;

  ctx = calloc (sizeof (*ctx), 1);
  if (!ctx)
    return GC_MALLOC_ERROR;

  ctx->alg = hash;
  ctx->mode = mode;

  switch (hash)
    {
#ifdef GNULIB_GC_MD2
    case GC_MD2:
      md2_init_ctx (&ctx->md2Context);
      break;
#endif

#ifdef GNULIB_GC_MD4
    case GC_MD4:
      md4_init_ctx (&ctx->md4Context);
      break;
#endif

#ifdef GNULIB_GC_MD5
    case GC_MD5:
      md5_init_ctx (&ctx->md5Context);
      break;
#endif

#ifdef GNULIB_GC_SHA1
    case GC_SHA1:
      sha1_init_ctx (&ctx->sha1Context);
      break;
#endif

    default:
      rc = GC_INVALID_HASH;
      break;
    }

  switch (mode)
    {
    case 0:
      break;

    default:
      rc = GC_INVALID_HASH;
      break;
    }

  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;

  *outhandle = out = calloc (sizeof (*out), 1);
  if (!out)
    return GC_MALLOC_ERROR;

  memcpy (out, in, sizeof (*out));

  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;

    default:
      return 0;
    }

  return len;
}

void
gc_hash_write (gc_hash_handle handle, size_t len, const char *data)
{
  _gc_hash_ctx *ctx = handle;

  switch (ctx->alg)
    {
#ifdef GNULIB_GC_MD2
    case GC_MD2:
      md2_process_bytes (data, len, &ctx->md2Context);
      break;
#endif

#ifdef GNULIB_GC_MD4
    case GC_MD4:
      md4_process_bytes (data, len, &ctx->md4Context);
      break;
#endif

#ifdef GNULIB_GC_MD5
    case GC_MD5:
      md5_process_bytes (data, len, &ctx->md5Context);
      break;
#endif

#ifdef GNULIB_GC_SHA1
    case GC_SHA1:
      sha1_process_bytes (data, len, &ctx->sha1Context);
      break;
#endif

    default:
      break;
    }
}

const char *
gc_hash_read (gc_hash_handle handle)
{
  _gc_hash_ctx *ctx = handle;
  const char *ret = NULL;

  switch (ctx->alg)
    {
#ifdef GNULIB_GC_MD2
    case GC_MD2:
      md2_finish_ctx (&ctx->md2Context, ctx->hash);
      ret = ctx->hash;
      break;
#endif

#ifdef GNULIB_GC_MD4
    case GC_MD4:
      md4_finish_ctx (&ctx->md4Context, ctx->hash);
      ret = ctx->hash;
      break;
#endif

#ifdef GNULIB_GC_MD5
    case GC_MD5:
      md5_finish_ctx (&ctx->md5Context, ctx->hash);
      ret = ctx->hash;
      break;
#endif

#ifdef GNULIB_GC_SHA1
    case GC_SHA1:
      sha1_finish_ctx (&ctx->sha1Context, ctx->hash);
      ret = ctx->hash;
      break;
#endif

    default:
      return NULL;
    }

  return ret;
}

void
gc_hash_close (gc_hash_handle handle)
{
  _gc_hash_ctx *ctx = handle;

  free (ctx);
}

Gc_rc
gc_hash_buffer (Gc_hash hash, const void *in, size_t inlen, char *resbuf)
{
  switch (hash)
    {
#ifdef GNULIB_GC_MD2
    case GC_MD2:
      md2_buffer (in, inlen, resbuf);
      break;
#endif

#ifdef GNULIB_GC_MD4
    case GC_MD4:
      md4_buffer (in, inlen, resbuf);
      break;
#endif

#ifdef GNULIB_GC_MD5
    case GC_MD5:
      md5_buffer (in, inlen, resbuf);
      break;
#endif

#ifdef GNULIB_GC_SHA1
    case GC_SHA1:
      sha1_buffer (in, inlen, resbuf);
      break;
#endif

    default:
      return GC_INVALID_HASH;
    }

  return GC_OK;
}

#ifdef GNULIB_GC_MD2
Gc_rc
gc_md2 (const void *in, size_t inlen, void *resbuf)
{
  md2_buffer (in, inlen, resbuf);
  return GC_OK;
}
#endif

#ifdef GNULIB_GC_MD4
Gc_rc
gc_md4 (const void *in, size_t inlen, void *resbuf)
{
  md4_buffer (in, inlen, resbuf);
  return GC_OK;
}
#endif

#ifdef GNULIB_GC_MD5
Gc_rc
gc_md5 (const void *in, size_t inlen, void *resbuf)
{
  md5_buffer (in, inlen, resbuf);
  return GC_OK;
}
#endif

#ifdef GNULIB_GC_SHA1
Gc_rc
gc_sha1 (const void *in, size_t inlen, void *resbuf)
{
  sha1_buffer (in, inlen, resbuf);
  return GC_OK;
}
#endif

#ifdef GNULIB_GC_HMAC_MD5
Gc_rc
gc_hmac_md5 (const void *key, size_t keylen,
             const void *in, size_t inlen, char *resbuf)
{
  hmac_md5 (key, keylen, in, inlen, resbuf);
  return GC_OK;
}
#endif

#ifdef GNULIB_GC_HMAC_SHA1
Gc_rc
gc_hmac_sha1 (const void *key, size_t keylen,
              const void *in, size_t inlen, char *resbuf)
{
  hmac_sha1 (key, keylen, in, inlen, resbuf);
  return GC_OK;
}
#endif