+typedef struct _gc_hash_ctx {
+ Gc_hash alg;
+ Gc_hash_mode mode;
+ gcry_md_hd_t gch;
+#ifdef GNULIB_GC_MD2
+ char hash[GC_MD2_DIGEST_SIZE];
+ struct md2_ctx md2Context;
+#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)
+ {
+ case GC_MD2:
+ gcryalg = GCRY_MD_NONE;
+ break;
+
+ 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;
+
+ default:
+ rc = GC_INVALID_HASH;
+ }
+
+ switch (mode)
+ {
+ case 0:
+ 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;
+
+ 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;
+#ifdef GNULIB_GC_MD2
+ if (ctx->alg != GC_MD2)
+#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;
+
+#ifdef GNULIB_GC_MD2
+ if (ctx->alg == GC_MD2)
+ md2_process_bytes (data, len, &ctx->md2Context);
+ 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;
+
+#ifdef GNULIB_GC_MD2
+ if (ctx->alg == GC_MD2)
+ {
+ md2_finish_ctx (&ctx->md2Context, ctx->hash);
+ digest = ctx->hash;
+ }
+ else
+#endif
+ {
+ gcry_md_final (ctx->gch);
+ digest = gcry_md_read (ctx->gch, 0);
+ }
+
+ return digest;
+}
+
+void
+gc_hash_close (gc_hash_handle handle)
+{
+ _gc_hash_ctx *ctx = handle;
+
+#ifdef GNULIB_GC_MD2
+ if (ctx->alg != GC_MD2)
+#endif
+ gcry_md_close (ctx->gch);
+
+ free (ctx);
+}
+
+Gc_rc