*/
 #include "postgres.h"
 
+#include "common/cryptohash.h"
 #include "common/hashfn.h"
 #include "jit/jit.h"
 #include "storage/bufmgr.h"
    ResourceArray filearr;      /* open temporary files */
    ResourceArray dsmarr;       /* dynamic shmem segments */
    ResourceArray jitarr;       /* JIT contexts */
+   ResourceArray cryptohasharr;    /* cryptohash contexts */
 
    /* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
    int         nlocks;         /* number of owned locks */
 static void PrintSnapshotLeakWarning(Snapshot snapshot);
 static void PrintFileLeakWarning(File file);
 static void PrintDSMLeakWarning(dsm_segment *seg);
+static void PrintCryptoHashLeakWarning(Datum handle);
 
 
 /*****************************************************************************
    ResourceArrayInit(&(owner->filearr), FileGetDatum(-1));
    ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL));
    ResourceArrayInit(&(owner->jitarr), PointerGetDatum(NULL));
+   ResourceArrayInit(&(owner->cryptohasharr), PointerGetDatum(NULL));
 
    return owner;
 }
 
            jit_release_context(context);
        }
+
+       /* Ditto for cryptohash contexts */
+       while (ResourceArrayGetAny(&(owner->cryptohasharr), &foundres))
+       {
+           pg_cryptohash_ctx *context =
+           (pg_cryptohash_ctx *) PointerGetDatum(foundres);
+
+           if (isCommit)
+               PrintCryptoHashLeakWarning(foundres);
+           pg_cryptohash_free(context);
+       }
    }
    else if (phase == RESOURCE_RELEASE_LOCKS)
    {
    Assert(owner->filearr.nitems == 0);
    Assert(owner->dsmarr.nitems == 0);
    Assert(owner->jitarr.nitems == 0);
+   Assert(owner->cryptohasharr.nitems == 0);
    Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
 
    /*
    ResourceArrayFree(&(owner->filearr));
    ResourceArrayFree(&(owner->dsmarr));
    ResourceArrayFree(&(owner->jitarr));
+   ResourceArrayFree(&(owner->cryptohasharr));
 
    pfree(owner);
 }
        elog(ERROR, "JIT context %p is not owned by resource owner %s",
             DatumGetPointer(handle), owner->name);
 }
+
+/*
+ * Make sure there is room for at least one more entry in a ResourceOwner's
+ * cryptohash context reference array.
+ *
+ * This is separate from actually inserting an entry because if we run out of
+ * memory, it's critical to do so *before* acquiring the resource.
+ */
+void
+ResourceOwnerEnlargeCryptoHash(ResourceOwner owner)
+{
+   ResourceArrayEnlarge(&(owner->cryptohasharr));
+}
+
+/*
+ * Remember that a cryptohash context is owned by a ResourceOwner
+ *
+ * Caller must have previously done ResourceOwnerEnlargeCryptoHash()
+ */
+void
+ResourceOwnerRememberCryptoHash(ResourceOwner owner, Datum handle)
+{
+   ResourceArrayAdd(&(owner->cryptohasharr), handle);
+}
+
+/*
+ * Forget that a cryptohash context is owned by a ResourceOwner
+ */
+void
+ResourceOwnerForgetCryptoHash(ResourceOwner owner, Datum handle)
+{
+   if (!ResourceArrayRemove(&(owner->cryptohasharr), handle))
+       elog(ERROR, "cryptohash context %p is not owned by resource owner %s",
+            DatumGetPointer(handle), owner->name);
+}
+
+/*
+ * Debugging subroutine
+ */
+static void
+PrintCryptoHashLeakWarning(Datum handle)
+{
+   elog(WARNING, "cryptohash context reference leak: context %p still referenced",
+        DatumGetPointer(handle));
+}
 
 #include "postgres_fe.h"
 #endif
 
-#include <openssl/sha.h>
+#include <openssl/evp.h>
 
 #include "common/cryptohash.h"
+#ifndef FRONTEND
+#include "utils/memutils.h"
+#include "utils/resowner.h"
+#include "utils/resowner_private.h"
+#endif
 
 /*
  * In backend, use palloc/pfree to ease the error handling.  In frontend,
 #define FREE(ptr) free(ptr)
 #endif
 
+/*
+ * Internal structure for pg_cryptohash_ctx->data.
+ *
+ * This tracks the resource owner associated to each EVP context data
+ * for the backend.
+ */
+typedef struct pg_cryptohash_state
+{
+   EVP_MD_CTX *evpctx;
+
+#ifndef FRONTEND
+   ResourceOwner resowner;
+#endif
+} pg_cryptohash_state;
+
 /*
  * pg_cryptohash_create
  *
 pg_cryptohash_create(pg_cryptohash_type type)
 {
    pg_cryptohash_ctx *ctx;
+   pg_cryptohash_state *state;
 
    ctx = ALLOC(sizeof(pg_cryptohash_ctx));
    if (ctx == NULL)
        return NULL;
 
-   ctx->type = type;
-
-   switch (type)
+   state = ALLOC(sizeof(pg_cryptohash_state));
+   if (state == NULL)
    {
-       case PG_SHA224:
-       case PG_SHA256:
-           ctx->data = ALLOC(sizeof(SHA256_CTX));
-           break;
-       case PG_SHA384:
-       case PG_SHA512:
-           ctx->data = ALLOC(sizeof(SHA512_CTX));
-           break;
+       explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+       FREE(ctx);
+       return NULL;
    }
 
-   if (ctx->data == NULL)
+   ctx->data = state;
+   ctx->type = type;
+
+#ifndef FRONTEND
+   ResourceOwnerEnlargeCryptoHash(CurrentResourceOwner);
+#endif
+
+   /*
+    * Initialization takes care of assigning the correct type for OpenSSL.
+    */
+   state->evpctx = EVP_MD_CTX_create();
+
+   if (state->evpctx == NULL)
    {
+       explicit_bzero(state, sizeof(pg_cryptohash_state));
        explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+#ifndef FRONTEND
+       ereport(ERROR,
+               (errcode(ERRCODE_OUT_OF_MEMORY),
+                errmsg("out of memory")));
+#else
+       FREE(state);
        FREE(ctx);
        return NULL;
+#endif
    }
 
+#ifndef FRONTEND
+   state->resowner = CurrentResourceOwner;
+   ResourceOwnerRememberCryptoHash(CurrentResourceOwner,
+                                   PointerGetDatum(ctx));
+#endif
+
    return ctx;
 }
 
 pg_cryptohash_init(pg_cryptohash_ctx *ctx)
 {
    int         status = 0;
+   pg_cryptohash_state *state;
 
    if (ctx == NULL)
        return 0;
 
+   state = (pg_cryptohash_state *) ctx->data;
+
    switch (ctx->type)
    {
        case PG_SHA224:
-           status = SHA224_Init((SHA256_CTX *) ctx->data);
+           status = EVP_DigestInit_ex(state->evpctx, EVP_sha224(), NULL);
            break;
        case PG_SHA256:
-           status = SHA256_Init((SHA256_CTX *) ctx->data);
+           status = EVP_DigestInit_ex(state->evpctx, EVP_sha256(), NULL);
            break;
        case PG_SHA384:
-           status = SHA384_Init((SHA512_CTX *) ctx->data);
+           status = EVP_DigestInit_ex(state->evpctx, EVP_sha384(), NULL);
            break;
        case PG_SHA512:
-           status = SHA512_Init((SHA512_CTX *) ctx->data);
+           status = EVP_DigestInit_ex(state->evpctx, EVP_sha512(), NULL);
            break;
    }
 
 pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
 {
    int         status = 0;
+   pg_cryptohash_state *state;
 
    if (ctx == NULL)
        return 0;
 
-   switch (ctx->type)
-   {
-       case PG_SHA224:
-           status = SHA224_Update((SHA256_CTX *) ctx->data, data, len);
-           break;
-       case PG_SHA256:
-           status = SHA256_Update((SHA256_CTX *) ctx->data, data, len);
-           break;
-       case PG_SHA384:
-           status = SHA384_Update((SHA512_CTX *) ctx->data, data, len);
-           break;
-       case PG_SHA512:
-           status = SHA512_Update((SHA512_CTX *) ctx->data, data, len);
-           break;
-   }
+   state = (pg_cryptohash_state *) ctx->data;
+   status = EVP_DigestUpdate(state->evpctx, data, len);
 
    /* OpenSSL internals return 1 on success, 0 on failure */
    if (status <= 0)
 pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest)
 {
    int         status = 0;
+   pg_cryptohash_state *state;
 
    if (ctx == NULL)
        return 0;
 
-   switch (ctx->type)
-   {
-       case PG_SHA224:
-           status = SHA224_Final(dest, (SHA256_CTX *) ctx->data);
-           break;
-       case PG_SHA256:
-           status = SHA256_Final(dest, (SHA256_CTX *) ctx->data);
-           break;
-       case PG_SHA384:
-           status = SHA384_Final(dest, (SHA512_CTX *) ctx->data);
-           break;
-       case PG_SHA512:
-           status = SHA512_Final(dest, (SHA512_CTX *) ctx->data);
-           break;
-   }
+   state = (pg_cryptohash_state *) ctx->data;
+   status = EVP_DigestFinal_ex(state->evpctx, dest, 0);
 
    /* OpenSSL internals return 1 on success, 0 on failure */
    if (status <= 0)
 void
 pg_cryptohash_free(pg_cryptohash_ctx *ctx)
 {
+   pg_cryptohash_state *state;
+
    if (ctx == NULL)
        return;
-   FREE(ctx->data);
+
+   state = (pg_cryptohash_state *) ctx->data;
+   EVP_MD_CTX_destroy(state->evpctx);
+
+#ifndef FRONTEND
+   ResourceOwnerForgetCryptoHash(state->resowner,
+                                 PointerGetDatum(ctx));
+#endif
+
+   explicit_bzero(state, sizeof(pg_cryptohash_state));
    explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+   FREE(state);
    FREE(ctx);
 }