On Thu, Nov 05, 2020 at 03:41:23PM +0900, Michael Paquier wrote:
> This conflicted on HEAD with pgcrypto.  Please find attached a rebased
> set.

I got to think more about this stuff and attached is a new patch set
that redesigns the generic interface used for the crypto hash
functions, in order to use the same entry point at the end for SHA2,
SHA1, MD5 or even HMAC.  This is part of 0001:
- Introduction of a single file called cryptohash[_openssl].c, which
includes five functions to create, initialize, update, finalize and
free a crypto hash context.  The attached does the work for SHA2.
- The fallback implementations are in their own file in src/common/,
and get included in cryptohash.c.  cryptohash_openssl.c is much more
simple as it needs to use EVP for everything.
- Adding a new crypto function in the set is simple once this is done,
as a type needs to be added with the correct options plugged in.

0002 and 0003 don't have any changes.  I think that we could also
rename the existing cryptohashes.c to crypohashfuncs.c to be more
consistent, but I have left that out for now.
--
Michael
From 7175bb4610114c4d04c43c35e176d4c67241ebdd Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Fri, 13 Nov 2020 11:47:25 +0900
Subject: [PATCH v4 1/3] Rework SHA2 and crypto hash APIs

This will make easier a switch to EVP for the OpenSSL SHA2 layer.  Note
that the layer introduced here is generalized for the purpose of a
future integration with HMAC, MD5, and even more.
---
 src/include/common/checksum_helper.h      |  13 +-
 src/include/common/cryptohash.h           |  40 +++++
 src/include/common/scram-common.h         |  17 +-
 src/include/common/sha2.h                 |  51 +-----
 src/include/replication/backup_manifest.h |   3 +-
 src/backend/libpq/auth-scram.c            |  94 +++++++----
 src/backend/replication/backup_manifest.c |  25 ++-
 src/backend/replication/basebackup.c      |  24 ++-
 src/backend/utils/adt/cryptohashes.c      |  53 ++++--
 src/common/Makefile                       |   7 +-
 src/common/checksum_helper.c              |  79 +++++++--
 src/common/cryptohash.c                   | 191 +++++++++++++++++++++
 src/common/cryptohash_openssl.c           | 196 ++++++++++++++++++++++
 src/common/scram-common.c                 | 165 ++++++++++++------
 src/common/sha2.c                         |  63 +++++--
 src/common/sha2_openssl.c                 | 102 -----------
 src/bin/pg_verifybackup/parse_manifest.c  |  15 +-
 src/bin/pg_verifybackup/pg_verifybackup.c |  24 ++-
 src/interfaces/libpq/fe-auth-scram.c      | 114 +++++++------
 contrib/pgcrypto/internal-sha2.c          | 188 +++++----------------
 src/tools/msvc/Mkvcbuild.pm               |   4 +-
 src/tools/pgindent/typedefs.list          |   1 +
 22 files changed, 951 insertions(+), 518 deletions(-)
 create mode 100644 src/include/common/cryptohash.h
 create mode 100644 src/common/cryptohash.c
 create mode 100644 src/common/cryptohash_openssl.c
 delete mode 100644 src/common/sha2_openssl.c

diff --git a/src/include/common/checksum_helper.h b/src/include/common/checksum_helper.h
index 48b0745dad..b07a34e7e4 100644
--- a/src/include/common/checksum_helper.h
+++ b/src/include/common/checksum_helper.h
@@ -14,6 +14,7 @@
 #ifndef CHECKSUM_HELPER_H
 #define CHECKSUM_HELPER_H
 
+#include "common/cryptohash.h"
 #include "common/sha2.h"
 #include "port/pg_crc32c.h"
 
@@ -41,10 +42,10 @@ typedef enum pg_checksum_type
 typedef union pg_checksum_raw_context
 {
 	pg_crc32c	c_crc32c;
-	pg_sha224_ctx c_sha224;
-	pg_sha256_ctx c_sha256;
-	pg_sha384_ctx c_sha384;
-	pg_sha512_ctx c_sha512;
+	pg_cryptohash_ctx *c_sha224;
+	pg_cryptohash_ctx *c_sha256;
+	pg_cryptohash_ctx *c_sha384;
+	pg_cryptohash_ctx *c_sha512;
 } pg_checksum_raw_context;
 
 /*
@@ -66,8 +67,8 @@ typedef struct pg_checksum_context
 extern bool pg_checksum_parse_type(char *name, pg_checksum_type *);
 extern char *pg_checksum_type_name(pg_checksum_type);
 
-extern void pg_checksum_init(pg_checksum_context *, pg_checksum_type);
-extern void pg_checksum_update(pg_checksum_context *, const uint8 *input,
+extern int	pg_checksum_init(pg_checksum_context *, pg_checksum_type);
+extern int	pg_checksum_update(pg_checksum_context *, const uint8 *input,
 							   size_t len);
 extern int	pg_checksum_final(pg_checksum_context *, uint8 *output);
 
diff --git a/src/include/common/cryptohash.h b/src/include/common/cryptohash.h
new file mode 100644
index 0000000000..775c975451
--- /dev/null
+++ b/src/include/common/cryptohash.h
@@ -0,0 +1,40 @@
+/*-------------------------------------------------------------------------
+ *
+ * cryptohash.h
+ *	  Generic headers for cryptographic hash functions.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *		  src/include/common/cryptohash.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef _PG_CRYPTOHASH_H_
+#define _PG_CRYPTOHASH_H_
+
+/* Context Structures for each hash function */
+typedef enum
+{
+	PG_SHA224 = 0,
+	PG_SHA256,
+	PG_SHA384,
+	PG_SHA512
+} pg_cryptohash_type;
+
+typedef struct pg_cryptohash_ctx
+{
+	pg_cryptohash_type type;
+	/* private area used by each hash implementation */
+	void	   *data;
+} pg_cryptohash_ctx;
+
+extern pg_cryptohash_ctx *pg_cryptohash_create(pg_cryptohash_type type);
+extern int	pg_cryptohash_init(pg_cryptohash_ctx *ctx);
+extern int	pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len);
+extern int	pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest);
+extern void pg_cryptohash_free(pg_cryptohash_ctx *ctx);
+
+#endif							/* _PG_CRYPTOHASH_H_ */
diff --git a/src/include/common/scram-common.h b/src/include/common/scram-common.h
index 2edae2dd3c..f4a7c60725 100644
--- a/src/include/common/scram-common.h
+++ b/src/include/common/scram-common.h
@@ -13,6 +13,7 @@
 #ifndef SCRAM_COMMON_H
 #define SCRAM_COMMON_H
 
+#include "common/cryptohash.h"
 #include "common/sha2.h"
 
 /* Name of SCRAM mechanisms per IANA */
@@ -50,19 +51,19 @@
  */
 typedef struct
 {
-	pg_sha256_ctx sha256ctx;
+	pg_cryptohash_ctx *sha256ctx;
 	uint8		k_opad[SHA256_HMAC_B];
 } scram_HMAC_ctx;
 
-extern void scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen);
-extern void scram_HMAC_update(scram_HMAC_ctx *ctx, const char *str, int slen);
-extern void scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx);
+extern int	scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen);
+extern int	scram_HMAC_update(scram_HMAC_ctx *ctx, const char *str, int slen);
+extern int	scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx);
 
-extern void scram_SaltedPassword(const char *password, const char *salt,
+extern int	scram_SaltedPassword(const char *password, const char *salt,
 								 int saltlen, int iterations, uint8 *result);
-extern void scram_H(const uint8 *str, int len, uint8 *result);
-extern void scram_ClientKey(const uint8 *salted_password, uint8 *result);
-extern void scram_ServerKey(const uint8 *salted_password, uint8 *result);
+extern int	scram_H(const uint8 *str, int len, uint8 *result);
+extern int	scram_ClientKey(const uint8 *salted_password, uint8 *result);
+extern int	scram_ServerKey(const uint8 *salted_password, uint8 *result);
 
 extern char *scram_build_secret(const char *salt, int saltlen, int iterations,
 								const char *password);
diff --git a/src/include/common/sha2.h b/src/include/common/sha2.h
index 9c4abf777d..e36e22ac76 100644
--- a/src/include/common/sha2.h
+++ b/src/include/common/sha2.h
@@ -3,7 +3,8 @@
  * sha2.h
  *	  Generic headers for SHA224, 256, 384 AND 512 functions of PostgreSQL.
  *
- * Portions Copyright (c) 2016-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
  *		  src/include/common/sha2.h
@@ -50,10 +51,6 @@
 #ifndef _PG_SHA2_H_
 #define _PG_SHA2_H_
 
-#ifdef USE_OPENSSL
-#include <openssl/sha.h>
-#endif
-
 /*** SHA224/256/384/512 Various Length Definitions ***********************/
 #define PG_SHA224_BLOCK_LENGTH			64
 #define PG_SHA224_DIGEST_LENGTH			28
@@ -68,48 +65,4 @@
 #define PG_SHA512_DIGEST_LENGTH			64
 #define PG_SHA512_DIGEST_STRING_LENGTH	(PG_SHA512_DIGEST_LENGTH * 2 + 1)
 
-/* Context Structures for SHA224/256/384/512 */
-#ifdef USE_OPENSSL
-typedef SHA256_CTX pg_sha256_ctx;
-typedef SHA512_CTX pg_sha512_ctx;
-typedef SHA256_CTX pg_sha224_ctx;
-typedef SHA512_CTX pg_sha384_ctx;
-#else
-typedef struct pg_sha256_ctx
-{
-	uint32		state[8];
-	uint64		bitcount;
-	uint8		buffer[PG_SHA256_BLOCK_LENGTH];
-} pg_sha256_ctx;
-typedef struct pg_sha512_ctx
-{
-	uint64		state[8];
-	uint64		bitcount[2];
-	uint8		buffer[PG_SHA512_BLOCK_LENGTH];
-} pg_sha512_ctx;
-typedef struct pg_sha256_ctx pg_sha224_ctx;
-typedef struct pg_sha512_ctx pg_sha384_ctx;
-#endif							/* USE_OPENSSL */
-
-/* Interface routines for SHA224/256/384/512 */
-extern void pg_sha224_init(pg_sha224_ctx *ctx);
-extern void pg_sha224_update(pg_sha224_ctx *ctx, const uint8 *input0,
-							 size_t len);
-extern void pg_sha224_final(pg_sha224_ctx *ctx, uint8 *dest);
-
-extern void pg_sha256_init(pg_sha256_ctx *ctx);
-extern void pg_sha256_update(pg_sha256_ctx *ctx, const uint8 *input0,
-							 size_t len);
-extern void pg_sha256_final(pg_sha256_ctx *ctx, uint8 *dest);
-
-extern void pg_sha384_init(pg_sha384_ctx *ctx);
-extern void pg_sha384_update(pg_sha384_ctx *ctx,
-							 const uint8 *, size_t len);
-extern void pg_sha384_final(pg_sha384_ctx *ctx, uint8 *dest);
-
-extern void pg_sha512_init(pg_sha512_ctx *ctx);
-extern void pg_sha512_update(pg_sha512_ctx *ctx, const uint8 *input0,
-							 size_t len);
-extern void pg_sha512_final(pg_sha512_ctx *ctx, uint8 *dest);
-
 #endif							/* _PG_SHA2_H_ */
diff --git a/src/include/replication/backup_manifest.h b/src/include/replication/backup_manifest.h
index fb1291cbe4..e7c4047497 100644
--- a/src/include/replication/backup_manifest.h
+++ b/src/include/replication/backup_manifest.h
@@ -28,7 +28,7 @@ typedef struct backup_manifest_info
 {
 	BufFile    *buffile;
 	pg_checksum_type checksum_type;
-	pg_sha256_ctx manifest_ctx;
+	pg_cryptohash_ctx *manifest_ctx;
 	uint64		manifest_size;
 	bool		force_encode;
 	bool		first_file;
@@ -48,5 +48,6 @@ extern void AddWALInfoToBackupManifest(backup_manifest_info *manifest,
 									   TimeLineID starttli, XLogRecPtr endptr,
 									   TimeLineID endtli);
 extern void SendBackupManifest(backup_manifest_info *manifest);
+extern void FreeBackupManifest(backup_manifest_info *manifest);
 
 #endif
diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c
index 0f79b28bb5..5a54bdd353 100644
--- a/src/backend/libpq/auth-scram.c
+++ b/src/backend/libpq/auth-scram.c
@@ -527,8 +527,10 @@ scram_verify_plain_password(const char *username, const char *password,
 		password = prep_password;
 
 	/* Compute Server Key based on the user-supplied plaintext password */
-	scram_SaltedPassword(password, salt, saltlen, iterations, salted_password);
-	scram_ServerKey(salted_password, computed_key);
+	if (scram_SaltedPassword(password, salt, saltlen, iterations,
+							 salted_password) < 0 ||
+		scram_ServerKey(salted_password, computed_key) < 0)
+		elog(ERROR, "could not compute server key");
 
 	if (prep_password)
 		pfree(prep_password);
@@ -653,6 +655,8 @@ mock_scram_secret(const char *username, int *iterations, char **salt,
 
 	/* Generate deterministic salt */
 	raw_salt = scram_mock_salt(username);
+	if (raw_salt == NULL)
+		elog(ERROR, "could not encode salt");	/* same error as follows */
 
 	encoded_len = pg_b64_enc_len(SCRAM_DEFAULT_SALT_LEN);
 	/* don't forget the zero-terminator */
@@ -1084,7 +1088,8 @@ verify_final_nonce(scram_state *state)
 
 /*
  * Verify the client proof contained in the last message received from
- * client in an exchange.
+ * client in an exchange.  Returns true if the verification is a success,
+ * or false for a failure.
  */
 static bool
 verify_client_proof(scram_state *state)
@@ -1095,27 +1100,33 @@ verify_client_proof(scram_state *state)
 	scram_HMAC_ctx ctx;
 	int			i;
 
-	/* calculate ClientSignature */
-	scram_HMAC_init(&ctx, state->StoredKey, SCRAM_KEY_LEN);
-	scram_HMAC_update(&ctx,
-					  state->client_first_message_bare,
-					  strlen(state->client_first_message_bare));
-	scram_HMAC_update(&ctx, ",", 1);
-	scram_HMAC_update(&ctx,
-					  state->server_first_message,
-					  strlen(state->server_first_message));
-	scram_HMAC_update(&ctx, ",", 1);
-	scram_HMAC_update(&ctx,
-					  state->client_final_message_without_proof,
-					  strlen(state->client_final_message_without_proof));
-	scram_HMAC_final(ClientSignature, &ctx);
+	/*
+	 * Calculate ClientSignature.  Note that we don't log directly a failure
+	 * here even when processing the calculations as this could involve a mock
+	 * authentication.
+	 */
+	if (scram_HMAC_init(&ctx, state->StoredKey, SCRAM_KEY_LEN) < 0 ||
+		scram_HMAC_update(&ctx,
+						  state->client_first_message_bare,
+						  strlen(state->client_first_message_bare)) < 0 ||
+		scram_HMAC_update(&ctx, ",", 1) < 0 ||
+		scram_HMAC_update(&ctx,
+						  state->server_first_message,
+						  strlen(state->server_first_message)) < 0 ||
+		scram_HMAC_update(&ctx, ",", 1) < 0 ||
+		scram_HMAC_update(&ctx,
+						  state->client_final_message_without_proof,
+						  strlen(state->client_final_message_without_proof)) < 0 ||
+		scram_HMAC_final(ClientSignature, &ctx) < 0)
+		elog(ERROR, "could not calculate client signature");
 
 	/* Extract the ClientKey that the client calculated from the proof */
 	for (i = 0; i < SCRAM_KEY_LEN; i++)
 		ClientKey[i] = state->ClientProof[i] ^ ClientSignature[i];
 
 	/* Hash it one more time, and compare with StoredKey */
-	scram_H(ClientKey, SCRAM_KEY_LEN, client_StoredKey);
+	if (scram_H(ClientKey, SCRAM_KEY_LEN, client_StoredKey) < 0)
+		elog(ERROR, "could not hash stored key");
 
 	if (memcmp(client_StoredKey, state->StoredKey, SCRAM_KEY_LEN) != 0)
 		return false;
@@ -1346,19 +1357,22 @@ build_server_final_message(scram_state *state)
 	scram_HMAC_ctx ctx;
 
 	/* calculate ServerSignature */
-	scram_HMAC_init(&ctx, state->ServerKey, SCRAM_KEY_LEN);
-	scram_HMAC_update(&ctx,
-					  state->client_first_message_bare,
-					  strlen(state->client_first_message_bare));
-	scram_HMAC_update(&ctx, ",", 1);
-	scram_HMAC_update(&ctx,
-					  state->server_first_message,
-					  strlen(state->server_first_message));
-	scram_HMAC_update(&ctx, ",", 1);
-	scram_HMAC_update(&ctx,
-					  state->client_final_message_without_proof,
-					  strlen(state->client_final_message_without_proof));
-	scram_HMAC_final(ServerSignature, &ctx);
+	if (scram_HMAC_init(&ctx, state->ServerKey, SCRAM_KEY_LEN) < 0 ||
+		scram_HMAC_update(&ctx,
+						  state->client_first_message_bare,
+						  strlen(state->client_first_message_bare)) < 0 ||
+		scram_HMAC_update(&ctx, ",", 1) < 0 ||
+		scram_HMAC_update(&ctx,
+						  state->server_first_message,
+						  strlen(state->server_first_message)) < 0 ||
+		scram_HMAC_update(&ctx, ",", 1) < 0 ||
+		scram_HMAC_update(&ctx,
+						  state->client_final_message_without_proof,
+						  strlen(state->client_final_message_without_proof)) < 0 ||
+		scram_HMAC_final(ServerSignature, &ctx) < 0)
+	{
+		elog(ERROR, "could not calculate server signature");
+	}
 
 	siglen = pg_b64_enc_len(SCRAM_KEY_LEN);
 	/* don't forget the zero-terminator */
@@ -1388,12 +1402,12 @@ build_server_final_message(scram_state *state)
 /*
  * Deterministically generate salt for mock authentication, using a SHA256
  * hash based on the username and a cluster-level secret key.  Returns a
- * pointer to a static buffer of size SCRAM_DEFAULT_SALT_LEN.
+ * pointer to a static buffer of size SCRAM_DEFAULT_SALT_LEN, or NULL.
  */
 static char *
 scram_mock_salt(const char *username)
 {
-	pg_sha256_ctx ctx;
+	pg_cryptohash_ctx *ctx;
 	static uint8 sha_digest[PG_SHA256_DIGEST_LENGTH];
 	char	   *mock_auth_nonce = GetMockAuthenticationNonce();
 
@@ -1406,10 +1420,16 @@ scram_mock_salt(const char *username)
 	StaticAssertStmt(PG_SHA256_DIGEST_LENGTH >= SCRAM_DEFAULT_SALT_LEN,
 					 "salt length greater than SHA256 digest length");
 
-	pg_sha256_init(&ctx);
-	pg_sha256_update(&ctx, (uint8 *) username, strlen(username));
-	pg_sha256_update(&ctx, (uint8 *) mock_auth_nonce, MOCK_AUTH_NONCE_LEN);
-	pg_sha256_final(&ctx, sha_digest);
+	ctx = pg_cryptohash_create(PG_SHA256);
+	if (pg_cryptohash_init(ctx) < 0 ||
+		pg_cryptohash_update(ctx, (uint8 *) username, strlen(username)) < 0 ||
+		pg_cryptohash_update(ctx, (uint8 *) mock_auth_nonce, MOCK_AUTH_NONCE_LEN) < 0 ||
+		pg_cryptohash_final(ctx, sha_digest) < 0)
+	{
+		pg_cryptohash_free(ctx);
+		return NULL;
+	}
+	pg_cryptohash_free(ctx);
 
 	return (char *) sha_digest;
 }
diff --git a/src/backend/replication/backup_manifest.c b/src/backend/replication/backup_manifest.c
index bab5e2f53b..c3f339c556 100644
--- a/src/backend/replication/backup_manifest.c
+++ b/src/backend/replication/backup_manifest.c
@@ -65,7 +65,9 @@ InitializeBackupManifest(backup_manifest_info *manifest,
 	else
 	{
 		manifest->buffile = BufFileCreateTemp(false);
-		pg_sha256_init(&manifest->manifest_ctx);
+		manifest->manifest_ctx = pg_cryptohash_create(PG_SHA256);
+		if (pg_cryptohash_init(manifest->manifest_ctx) < 0)
+			elog(ERROR, "failed to initialize checksum of backup manifest");
 	}
 
 	manifest->manifest_size = UINT64CONST(0);
@@ -79,6 +81,16 @@ InitializeBackupManifest(backup_manifest_info *manifest,
 						 "\"Files\": [");
 }
 
+/*
+ * Free resources assigned to a backup manifest constructed.
+ */
+void
+FreeBackupManifest(backup_manifest_info *manifest)
+{
+	pg_cryptohash_free(manifest->manifest_ctx);
+	manifest->manifest_ctx = NULL;
+}
+
 /*
  * Add an entry to the backup manifest for a file.
  */
@@ -166,6 +178,9 @@ AddFileToBackupManifest(backup_manifest_info *manifest, const char *spcoid,
 		int			checksumlen;
 
 		checksumlen = pg_checksum_final(checksum_ctx, checksumbuf);
+		if (checksumlen < 0)
+			elog(ERROR, "could not finalize checksum of file \"%s\"",
+				 pathname);
 
 		appendStringInfo(&buf,
 						 ", \"Checksum-Algorithm\": \"%s\", \"Checksum\": \"",
@@ -310,7 +325,8 @@ SendBackupManifest(backup_manifest_info *manifest)
 	 * twice.
 	 */
 	manifest->still_checksumming = false;
-	pg_sha256_final(&manifest->manifest_ctx, checksumbuf);
+	if (pg_cryptohash_final(manifest->manifest_ctx, checksumbuf) < 0)
+		elog(ERROR, "failed to finalize checksum of backup manifest");
 	AppendStringToManifest(manifest, "\"Manifest-Checksum\": \"");
 	hex_encode((char *) checksumbuf, sizeof checksumbuf, checksumstringbuf);
 	checksumstringbuf[PG_SHA256_DIGEST_STRING_LENGTH - 1] = '\0';
@@ -373,7 +389,10 @@ AppendStringToManifest(backup_manifest_info *manifest, char *s)
 
 	Assert(manifest != NULL);
 	if (manifest->still_checksumming)
-		pg_sha256_update(&manifest->manifest_ctx, (uint8 *) s, len);
+	{
+		if (pg_cryptohash_update(manifest->manifest_ctx, (uint8 *) s, len) < 0)
+			elog(ERROR, "failed to update checksum of backup manifest");
+	}
 	BufFileWrite(manifest->buffile, s, len);
 	manifest->manifest_size += len;
 }
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index b89df01fa7..0969036c7d 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -730,6 +730,7 @@ perform_base_backup(basebackup_options *opt)
 	}
 
 	/* clean up the resource owner we created */
+	FreeBackupManifest(&manifest);
 	WalSndResourceCleanup(true);
 
 	pgstat_progress_end_command();
@@ -1094,7 +1095,9 @@ sendFileWithContent(const char *filename, const char *content,
 				len;
 	pg_checksum_context checksum_ctx;
 
-	pg_checksum_init(&checksum_ctx, manifest->checksum_type);
+	if (pg_checksum_init(&checksum_ctx, manifest->checksum_type) < 0)
+		elog(ERROR, "could not initialize checksum of file \"%s\"",
+			 filename);
 
 	len = strlen(content);
 
@@ -1130,7 +1133,10 @@ sendFileWithContent(const char *filename, const char *content,
 		update_basebackup_progress(pad);
 	}
 
-	pg_checksum_update(&checksum_ctx, (uint8 *) content, len);
+	if (pg_checksum_update(&checksum_ctx, (uint8 *) content, len) < 0)
+		elog(ERROR, "could not update checksum of file \"%s\"",
+			 filename);
+
 	AddFileToBackupManifest(manifest, NULL, filename, len,
 							(pg_time_t) statbuf.st_mtime, &checksum_ctx);
 }
@@ -1584,7 +1590,9 @@ sendFile(const char *readfilename, const char *tarfilename,
 	bool		verify_checksum = false;
 	pg_checksum_context checksum_ctx;
 
-	pg_checksum_init(&checksum_ctx, manifest->checksum_type);
+	if (pg_checksum_init(&checksum_ctx, manifest->checksum_type) < 0)
+		elog(ERROR, "could not initialize checksum of file \"%s\"",
+			 readfilename);
 
 	fd = OpenTransientFile(readfilename, O_RDONLY | PG_BINARY);
 	if (fd < 0)
@@ -1758,7 +1766,8 @@ sendFile(const char *readfilename, const char *tarfilename,
 		update_basebackup_progress(cnt);
 
 		/* Also feed it to the checksum machinery. */
-		pg_checksum_update(&checksum_ctx, (uint8 *) buf, cnt);
+		if (pg_checksum_update(&checksum_ctx, (uint8 *) buf, cnt) < 0)
+			elog(ERROR, "could not update checksum of base backup");
 
 		len += cnt;
 		throttle(cnt);
@@ -1772,7 +1781,8 @@ sendFile(const char *readfilename, const char *tarfilename,
 		{
 			cnt = Min(sizeof(buf), statbuf->st_size - len);
 			pq_putmessage('d', buf, cnt);
-			pg_checksum_update(&checksum_ctx, (uint8 *) buf, cnt);
+			if (pg_checksum_update(&checksum_ctx, (uint8 *) buf, cnt) < 0)
+				elog(ERROR, "could not update checksum of base backup");
 			update_basebackup_progress(cnt);
 			len += cnt;
 			throttle(cnt);
@@ -1780,8 +1790,8 @@ sendFile(const char *readfilename, const char *tarfilename,
 	}
 
 	/*
-	 * Pad to a block boundary, per tar format requirements. (This small
-	 * piece of data is probably not worth throttling, and is not checksummed
+	 * Pad to a block boundary, per tar format requirements. (This small piece
+	 * of data is probably not worth throttling, and is not checksummed
 	 * because it's not actually part of the file.)
 	 */
 	pad = tarPaddingBytesRequired(len);
diff --git a/src/backend/utils/adt/cryptohashes.c b/src/backend/utils/adt/cryptohashes.c
index e897660927..5de294a7fd 100644
--- a/src/backend/utils/adt/cryptohashes.c
+++ b/src/backend/utils/adt/cryptohashes.c
@@ -13,6 +13,7 @@
  */
 #include "postgres.h"
 
+#include "common/cryptohash.h"
 #include "common/md5.h"
 #include "common/sha2.h"
 #include "utils/builtins.h"
@@ -78,16 +79,21 @@ sha224_bytea(PG_FUNCTION_ARGS)
 	bytea	   *in = PG_GETARG_BYTEA_PP(0);
 	const uint8 *data;
 	size_t		len;
-	pg_sha224_ctx ctx;
+	pg_cryptohash_ctx *ctx;
 	unsigned char buf[PG_SHA224_DIGEST_LENGTH];
 	bytea	   *result;
 
 	len = VARSIZE_ANY_EXHDR(in);
 	data = (unsigned char *) VARDATA_ANY(in);
 
-	pg_sha224_init(&ctx);
-	pg_sha224_update(&ctx, data, len);
-	pg_sha224_final(&ctx, buf);
+	ctx = pg_cryptohash_create(PG_SHA224);
+	if (pg_cryptohash_init(ctx) < 0)
+		elog(ERROR, "could not initialize %s context", "SHA224");
+	if (pg_cryptohash_update(ctx, data, len) < 0)
+		elog(ERROR, "could not update %s context", "SHA224");
+	if (pg_cryptohash_final(ctx, buf) < 0)
+		elog(ERROR, "could not finalize %s context", "SHA224");
+	pg_cryptohash_free(ctx);
 
 	result = palloc(sizeof(buf) + VARHDRSZ);
 	SET_VARSIZE(result, sizeof(buf) + VARHDRSZ);
@@ -102,16 +108,21 @@ sha256_bytea(PG_FUNCTION_ARGS)
 	bytea	   *in = PG_GETARG_BYTEA_PP(0);
 	const uint8 *data;
 	size_t		len;
-	pg_sha256_ctx ctx;
+	pg_cryptohash_ctx *ctx;
 	unsigned char buf[PG_SHA256_DIGEST_LENGTH];
 	bytea	   *result;
 
 	len = VARSIZE_ANY_EXHDR(in);
 	data = (unsigned char *) VARDATA_ANY(in);
 
-	pg_sha256_init(&ctx);
-	pg_sha256_update(&ctx, data, len);
-	pg_sha256_final(&ctx, buf);
+	ctx = pg_cryptohash_create(PG_SHA256);
+	if (pg_cryptohash_init(ctx) < 0)
+		elog(ERROR, "could not initialize %s context", "SHA256");
+	if (pg_cryptohash_update(ctx, data, len) < 0)
+		elog(ERROR, "could not update %s context", "SHA256");
+	if (pg_cryptohash_final(ctx, buf) < 0)
+		elog(ERROR, "could not finalize %s context", "SHA256");
+	pg_cryptohash_free(ctx);
 
 	result = palloc(sizeof(buf) + VARHDRSZ);
 	SET_VARSIZE(result, sizeof(buf) + VARHDRSZ);
@@ -126,16 +137,21 @@ sha384_bytea(PG_FUNCTION_ARGS)
 	bytea	   *in = PG_GETARG_BYTEA_PP(0);
 	const uint8 *data;
 	size_t		len;
-	pg_sha384_ctx ctx;
+	pg_cryptohash_ctx *ctx;
 	unsigned char buf[PG_SHA384_DIGEST_LENGTH];
 	bytea	   *result;
 
 	len = VARSIZE_ANY_EXHDR(in);
 	data = (unsigned char *) VARDATA_ANY(in);
 
-	pg_sha384_init(&ctx);
-	pg_sha384_update(&ctx, data, len);
-	pg_sha384_final(&ctx, buf);
+	ctx = pg_cryptohash_create(PG_SHA384);
+	if (pg_cryptohash_init(ctx) < 0)
+		elog(ERROR, "could not initialize %s context", "SHA384");
+	if (pg_cryptohash_update(ctx, data, len) < 0)
+		elog(ERROR, "could not update %s context", "SHA384");
+	if (pg_cryptohash_final(ctx, buf) < 0)
+		elog(ERROR, "could not finalize %s context", "SHA384");
+	pg_cryptohash_free(ctx);
 
 	result = palloc(sizeof(buf) + VARHDRSZ);
 	SET_VARSIZE(result, sizeof(buf) + VARHDRSZ);
@@ -150,16 +166,21 @@ sha512_bytea(PG_FUNCTION_ARGS)
 	bytea	   *in = PG_GETARG_BYTEA_PP(0);
 	const uint8 *data;
 	size_t		len;
-	pg_sha512_ctx ctx;
+	pg_cryptohash_ctx *ctx;
 	unsigned char buf[PG_SHA512_DIGEST_LENGTH];
 	bytea	   *result;
 
 	len = VARSIZE_ANY_EXHDR(in);
 	data = (unsigned char *) VARDATA_ANY(in);
 
-	pg_sha512_init(&ctx);
-	pg_sha512_update(&ctx, data, len);
-	pg_sha512_final(&ctx, buf);
+	ctx = pg_cryptohash_create(PG_SHA512);
+	if (pg_cryptohash_init(ctx) < 0)
+		elog(ERROR, "could not initialize %s context", "SHA512");
+	if (pg_cryptohash_update(ctx, data, len) < 0)
+		elog(ERROR, "could not update %s context", "SHA512");
+	if (pg_cryptohash_final(ctx, buf) < 0)
+		elog(ERROR, "could not finalize %s context", "SHA512");
+	pg_cryptohash_free(ctx);
 
 	result = palloc(sizeof(buf) + VARHDRSZ);
 	SET_VARSIZE(result, sizeof(buf) + VARHDRSZ);
diff --git a/src/common/Makefile b/src/common/Makefile
index 25c55bd642..e5492933d4 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -82,9 +82,9 @@ OBJS_COMMON = \
 ifeq ($(with_openssl),yes)
 OBJS_COMMON += \
 	protocol_openssl.o \
-	sha2_openssl.o
+	cryptohash_openssl.o
 else
-OBJS_COMMON += sha2.o
+OBJS_COMMON += cryptohash.o
 endif
 
 # A few files are currently only built for frontend, not server
@@ -172,6 +172,9 @@ kwlist_d.h: $(top_srcdir)/src/include/parser/kwlist.h $(GEN_KEYWORDLIST_DEPS)
 # that you don't get broken parsing code, even in a non-enable-depend build.
 keywords.o keywords_shlib.o keywords_srv.o: kwlist_d.h
 
+# Fallback cryptographic implementations need the those files.
+cryptohash.o: cryptohash.c sha2.c
+
 # The code imported from Ryu gets a pass on declaration-after-statement,
 # in order to keep it more closely aligned with its upstream.
 RYU_FILES = d2s.o f2s.o
diff --git a/src/common/checksum_helper.c b/src/common/checksum_helper.c
index 79a9a7447b..8e06524cd3 100644
--- a/src/common/checksum_helper.c
+++ b/src/common/checksum_helper.c
@@ -77,8 +77,9 @@ pg_checksum_type_name(pg_checksum_type type)
 
 /*
  * Initialize a checksum context for checksums of the given type.
+ * Returns 0 for a success, -1 for a failure.
  */
-void
+int
 pg_checksum_init(pg_checksum_context *context, pg_checksum_type type)
 {
 	context->type = type;
@@ -92,24 +93,55 @@ pg_checksum_init(pg_checksum_context *context, pg_checksum_type type)
 			INIT_CRC32C(context->raw_context.c_crc32c);
 			break;
 		case CHECKSUM_TYPE_SHA224:
-			pg_sha224_init(&context->raw_context.c_sha224);
+			context->raw_context.c_sha224 = pg_cryptohash_create(PG_SHA224);
+			if (context->raw_context.c_sha224 == NULL)
+				return -1;
+			if (pg_cryptohash_init(context->raw_context.c_sha224) < 0)
+			{
+				pg_cryptohash_free(context->raw_context.c_sha224);
+				return -1;
+			}
 			break;
 		case CHECKSUM_TYPE_SHA256:
-			pg_sha256_init(&context->raw_context.c_sha256);
+			context->raw_context.c_sha256 = pg_cryptohash_create(PG_SHA256);
+			if (context->raw_context.c_sha256 == NULL)
+				return -1;
+			if (pg_cryptohash_init(context->raw_context.c_sha256) < 0)
+			{
+				pg_cryptohash_free(context->raw_context.c_sha256);
+				return -1;
+			}
 			break;
 		case CHECKSUM_TYPE_SHA384:
-			pg_sha384_init(&context->raw_context.c_sha384);
+			context->raw_context.c_sha384 = pg_cryptohash_create(PG_SHA384);
+			if (context->raw_context.c_sha384 == NULL)
+				return -1;
+			if (pg_cryptohash_init(context->raw_context.c_sha384) < 0)
+			{
+				pg_cryptohash_free(context->raw_context.c_sha384);
+				return -1;
+			}
 			break;
 		case CHECKSUM_TYPE_SHA512:
-			pg_sha512_init(&context->raw_context.c_sha512);
+			context->raw_context.c_sha512 = pg_cryptohash_create(PG_SHA512);
+			if (context->raw_context.c_sha512 == NULL)
+				return -1;
+			if (pg_cryptohash_init(context->raw_context.c_sha512) < 0)
+			{
+				pg_cryptohash_free(context->raw_context.c_sha512);
+				return -1;
+			}
 			break;
 	}
+
+	return 0;
 }
 
 /*
  * Update a checksum context with new data.
+ * Returns 0 for a success, -1 for a failure.
  */
-void
+int
 pg_checksum_update(pg_checksum_context *context, const uint8 *input,
 				   size_t len)
 {
@@ -122,25 +154,32 @@ pg_checksum_update(pg_checksum_context *context, const uint8 *input,
 			COMP_CRC32C(context->raw_context.c_crc32c, input, len);
 			break;
 		case CHECKSUM_TYPE_SHA224:
-			pg_sha224_update(&context->raw_context.c_sha224, input, len);
+			if (pg_cryptohash_update(context->raw_context.c_sha224, input, len) < 0)
+				return -1;
 			break;
 		case CHECKSUM_TYPE_SHA256:
-			pg_sha256_update(&context->raw_context.c_sha256, input, len);
+			if (pg_cryptohash_update(context->raw_context.c_sha256, input, len) < 0)
+				return -1;
 			break;
 		case CHECKSUM_TYPE_SHA384:
-			pg_sha384_update(&context->raw_context.c_sha384, input, len);
+			if (pg_cryptohash_update(context->raw_context.c_sha384, input, len) < 0)
+				return -1;
 			break;
 		case CHECKSUM_TYPE_SHA512:
-			pg_sha512_update(&context->raw_context.c_sha512, input, len);
+			if (pg_cryptohash_update(context->raw_context.c_sha512, input, len) < 0)
+				return -1;
 			break;
 	}
+
+	return 0;
 }
 
 /*
  * Finalize a checksum computation and write the result to an output buffer.
  *
  * The caller must ensure that the buffer is at least PG_CHECKSUM_MAX_LENGTH
- * bytes in length. The return value is the number of bytes actually written.
+ * bytes in length. The return value is the number of bytes actually written,
+ * or -1 for a failure.
  */
 int
 pg_checksum_final(pg_checksum_context *context, uint8 *output)
@@ -168,19 +207,27 @@ pg_checksum_final(pg_checksum_context *context, uint8 *output)
 			memcpy(output, &context->raw_context.c_crc32c, retval);
 			break;
 		case CHECKSUM_TYPE_SHA224:
-			pg_sha224_final(&context->raw_context.c_sha224, output);
+			if (pg_cryptohash_final(context->raw_context.c_sha224, output) < 0)
+				return -1;
+			pg_cryptohash_free(context->raw_context.c_sha224);
 			retval = PG_SHA224_DIGEST_LENGTH;
 			break;
 		case CHECKSUM_TYPE_SHA256:
-			pg_sha256_final(&context->raw_context.c_sha256, output);
-			retval = PG_SHA256_DIGEST_LENGTH;
+			if (pg_cryptohash_final(context->raw_context.c_sha256, output) < 0)
+				return -1;
+			pg_cryptohash_free(context->raw_context.c_sha256);
+			retval = PG_SHA224_DIGEST_LENGTH;
 			break;
 		case CHECKSUM_TYPE_SHA384:
-			pg_sha384_final(&context->raw_context.c_sha384, output);
+			if (pg_cryptohash_final(context->raw_context.c_sha384, output) < 0)
+				return -1;
+			pg_cryptohash_free(context->raw_context.c_sha384);
 			retval = PG_SHA384_DIGEST_LENGTH;
 			break;
 		case CHECKSUM_TYPE_SHA512:
-			pg_sha512_final(&context->raw_context.c_sha512, output);
+			if (pg_cryptohash_final(context->raw_context.c_sha512, output) < 0)
+				return -1;
+			pg_cryptohash_free(context->raw_context.c_sha512);
 			retval = PG_SHA512_DIGEST_LENGTH;
 			break;
 	}
diff --git a/src/common/cryptohash.c b/src/common/cryptohash.c
new file mode 100644
index 0000000000..e693a1e290
--- /dev/null
+++ b/src/common/cryptohash.c
@@ -0,0 +1,191 @@
+/*-------------------------------------------------------------------------
+ *
+ * cryptohash.c
+ *	  Fallback implementations for cryptographic hash functions.
+ *
+ * This is the set of in-core functions used when there are no other
+ * alternative options like OpenSSL.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/common/cryptohash.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include <sys/param.h>
+
+#include "common/cryptohash.h"
+
+/* Include internals of each implementation here */
+#include "sha2.c"
+
+/*
+ * In backend, use palloc/pfree to ease the error handling.  In frontend,
+ * use malloc to be able to return a failure status back to the caller.
+ */
+#ifndef FRONTEND
+#define ALLOC(size) palloc(size)
+#define FREE(ptr) pfree(ptr)
+#else
+#define ALLOC(size) malloc(size)
+#define FREE(ptr) free(ptr)
+#endif
+
+/*
+ * pg_cryptohash_create
+ *
+ * Allocate a hash context.  Returns NULL on failure.
+ */
+pg_cryptohash_ctx *
+pg_cryptohash_create(pg_cryptohash_type type)
+{
+	pg_cryptohash_ctx *ctx;
+
+	ctx = ALLOC(sizeof(pg_cryptohash_ctx));
+	if (ctx == NULL)
+		return NULL;
+
+	ctx->type = type;
+
+	switch (type)
+	{
+		case PG_SHA224:
+			ctx->data = ALLOC(sizeof(pg_sha224_ctx));
+			break;
+		case PG_SHA256:
+			ctx->data = ALLOC(sizeof(pg_sha256_ctx));
+			break;
+		case PG_SHA384:
+			ctx->data = ALLOC(sizeof(pg_sha384_ctx));
+			break;
+		case PG_SHA512:
+			ctx->data = ALLOC(sizeof(pg_sha512_ctx));
+			break;
+	}
+
+	if (ctx->data == NULL)
+	{
+		explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+		FREE(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+/*
+ * pg_cryptohash_init
+ *
+ * Initialize a hash context.  Note that this implementation is designed
+ * to never fail, so this always returns 0.
+ */
+int
+pg_cryptohash_init(pg_cryptohash_ctx *ctx)
+{
+	if (ctx == NULL)
+		return 0;
+
+	switch (ctx->type)
+	{
+		case PG_SHA224:
+			pg_sha224_init((pg_sha224_ctx *) ctx->data);
+			break;
+		case PG_SHA256:
+			pg_sha256_init((pg_sha256_ctx *) ctx->data);
+			break;
+		case PG_SHA384:
+			pg_sha384_init((pg_sha384_ctx *) ctx->data);
+			break;
+		case PG_SHA512:
+			pg_sha512_init((pg_sha512_ctx *) ctx->data);
+			break;
+	}
+
+	return 0;
+}
+
+/*
+ * pg_cryptohash_update
+ *
+ * Update a hash context.  Note that this implementation is designed
+ * to never fail, so this always returns 0.
+ */
+int
+pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
+{
+	if (ctx == NULL)
+		return 0;
+
+	switch (ctx->type)
+	{
+		case PG_SHA224:
+			pg_sha224_update((pg_sha224_ctx *) ctx->data, data, len);
+			break;
+		case PG_SHA256:
+			pg_sha256_update((pg_sha256_ctx *) ctx->data, data, len);
+			break;
+		case PG_SHA384:
+			pg_sha384_update((pg_sha384_ctx *) ctx->data, data, len);
+			break;
+		case PG_SHA512:
+			pg_sha512_update((pg_sha512_ctx *) ctx->data, data, len);
+			break;
+	}
+
+	return 0;
+}
+
+/*
+ * pg_cryptohash_final
+ *
+ * Finalize a hash context.  Note that this implementation is designed
+ * to never fail, so this always returns 0.
+ */
+int
+pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest)
+{
+	if (ctx == NULL)
+		return 0;
+
+	switch (ctx->type)
+	{
+		case PG_SHA224:
+			pg_sha224_final((pg_sha224_ctx *) ctx->data, dest);
+			break;
+		case PG_SHA256:
+			pg_sha256_final((pg_sha256_ctx *) ctx->data, dest);
+			break;
+		case PG_SHA384:
+			pg_sha384_final((pg_sha384_ctx *) ctx->data, dest);
+			break;
+		case PG_SHA512:
+			pg_sha512_final((pg_sha512_ctx *) ctx->data, dest);
+			break;
+	}
+
+	return 0;
+}
+
+/*
+ * pg_cryptohash_free
+ *
+ * Free a hash context.
+ */
+void
+pg_cryptohash_free(pg_cryptohash_ctx *ctx)
+{
+	if (ctx == NULL)
+		return;
+	FREE(ctx->data);
+	explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+	FREE(ctx);
+}
diff --git a/src/common/cryptohash_openssl.c b/src/common/cryptohash_openssl.c
new file mode 100644
index 0000000000..406abf6163
--- /dev/null
+++ b/src/common/cryptohash_openssl.c
@@ -0,0 +1,196 @@
+/*-------------------------------------------------------------------------
+ *
+ * cryptohash_openssl.c
+ *	  Set of wrapper routines on top of OpenSSL to support cryptographic
+ *	  hash functions.
+ *
+ * This should only be used if code is compiled with OpenSSL support.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *		  src/common/cryptohash_openssl.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include <openssl/sha.h>
+
+#include "common/cryptohash.h"
+
+/*
+ * In backend, use palloc/pfree to ease the error handling.  In frontend,
+ * use malloc to be able to return a failure status back to the caller.
+ */
+#ifndef FRONTEND
+#define ALLOC(size) palloc(size)
+#define FREE(ptr) pfree(ptr)
+#else
+#define ALLOC(size) malloc(size)
+#define FREE(ptr) free(ptr)
+#endif
+
+/*
+ * pg_cryptohash_create
+ *
+ * Allocate a hash context.  Returns NULL on failure.
+ */
+pg_cryptohash_ctx *
+pg_cryptohash_create(pg_cryptohash_type type)
+{
+	pg_cryptohash_ctx *ctx;
+
+	ctx = ALLOC(sizeof(pg_cryptohash_ctx));
+	if (ctx == NULL)
+		return NULL;
+
+	ctx->type = type;
+
+	switch (type)
+	{
+		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;
+	}
+
+	if (ctx->data == NULL)
+	{
+		explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+		FREE(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+/*
+ * pg_cryptohash_init
+ *
+ * Initialize a hash context.  Returns 0 on success, and -1 on failure.
+ */
+int
+pg_cryptohash_init(pg_cryptohash_ctx *ctx)
+{
+	int			status = 0;
+
+	if (ctx == NULL)
+		return 0;
+
+	switch (ctx->type)
+	{
+		case PG_SHA224:
+			status = SHA224_Init((SHA256_CTX *) ctx->data);
+			break;
+		case PG_SHA256:
+			status = SHA256_Init((SHA256_CTX *) ctx->data);
+			break;
+		case PG_SHA384:
+			status = SHA384_Init((SHA512_CTX *) ctx->data);
+			break;
+		case PG_SHA512:
+			status = SHA512_Init((SHA512_CTX *) ctx->data);
+			break;
+	}
+
+	/* OpenSSL internals return 1 on success, 0 on failure */
+	if (status <= 0)
+		return -1;
+	return 0;
+}
+
+/*
+ * pg_cryptohash_update
+ *
+ * Update a hash context.  Returns 0 on success, and -1 on failure.
+ */
+int
+pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
+{
+	int			status;
+
+	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;
+	}
+
+	/* OpenSSL internals return 1 on success, 0 on failure */
+	if (status <= 0)
+		return -1;
+	return 0;
+}
+
+/*
+ * pg_cryptohash_final
+ *
+ * Finalize a hash context.  Returns 0 on success, and -1 on failure.
+ */
+int
+pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest)
+{
+	int			status;
+
+	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;
+	}
+
+	/* OpenSSL internals return 1 on success, 0 on failure */
+	if (status <= 0)
+		return -1;
+	return 0;
+}
+
+/*
+ * pg_cryptohash_free
+ *
+ * Free a hash context.
+ */
+void
+pg_cryptohash_free(pg_cryptohash_ctx *ctx)
+{
+	if (ctx == NULL)
+		return;
+	FREE(ctx->data);
+	explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+	FREE(ctx);
+}
diff --git a/src/common/scram-common.c b/src/common/scram-common.c
index 4971134b22..04082414e5 100644
--- a/src/common/scram-common.c
+++ b/src/common/scram-common.c
@@ -29,9 +29,9 @@
 /*
  * Calculate HMAC per RFC2104.
  *
- * The hash function used is SHA-256.
+ * The hash function used is SHA-256.  Returns 0 on success, -1 on failure.
  */
-void
+int
 scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen)
 {
 	uint8		k_ipad[SHA256_HMAC_B];
@@ -44,13 +44,21 @@ scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen)
 	 */
 	if (keylen > SHA256_HMAC_B)
 	{
-		pg_sha256_ctx sha256_ctx;
+		pg_cryptohash_ctx *sha256_ctx;
 
-		pg_sha256_init(&sha256_ctx);
-		pg_sha256_update(&sha256_ctx, key, keylen);
-		pg_sha256_final(&sha256_ctx, keybuf);
+		sha256_ctx = pg_cryptohash_create(PG_SHA256);
+		if (sha256_ctx == NULL)
+			return -1;
+		if (pg_cryptohash_init(sha256_ctx) < 0 ||
+			pg_cryptohash_update(sha256_ctx, key, keylen) < 0 ||
+			pg_cryptohash_final(sha256_ctx, keybuf) < 0)
+		{
+			pg_cryptohash_free(sha256_ctx);
+			return -1;
+		}
 		key = keybuf;
 		keylen = SCRAM_KEY_LEN;
+		pg_cryptohash_free(sha256_ctx);
 	}
 
 	memset(k_ipad, HMAC_IPAD, SHA256_HMAC_B);
@@ -62,45 +70,75 @@ scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen)
 		ctx->k_opad[i] ^= key[i];
 	}
 
+	ctx->sha256ctx = pg_cryptohash_create(PG_SHA256);
+	if (ctx->sha256ctx == NULL)
+		return -1;
+
 	/* tmp = H(K XOR ipad, text) */
-	pg_sha256_init(&ctx->sha256ctx);
-	pg_sha256_update(&ctx->sha256ctx, k_ipad, SHA256_HMAC_B);
+	if (pg_cryptohash_init(ctx->sha256ctx) < 0 ||
+		pg_cryptohash_update(ctx->sha256ctx, k_ipad, SHA256_HMAC_B) < 0)
+	{
+		pg_cryptohash_free(ctx->sha256ctx);
+		return -1;
+	}
+
+	return 0;
 }
 
 /*
  * Update HMAC calculation
- * The hash function used is SHA-256.
+ * The hash function used is SHA-256.  Returns 0 on success, -1 on failure.
  */
-void
+int
 scram_HMAC_update(scram_HMAC_ctx *ctx, const char *str, int slen)
 {
-	pg_sha256_update(&ctx->sha256ctx, (const uint8 *) str, slen);
+	Assert(ctx->sha256ctx != NULL);
+	if (pg_cryptohash_update(ctx->sha256ctx, (const uint8 *) str, slen) < 0)
+	{
+		pg_cryptohash_free(ctx->sha256ctx);
+		return -1;
+	}
+	return 0;
 }
 
 /*
  * Finalize HMAC calculation.
- * The hash function used is SHA-256.
+ * The hash function used is SHA-256.  Returns 0 on success, -1 on failure.
  */
-void
+int
 scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx)
 {
 	uint8		h[SCRAM_KEY_LEN];
 
-	pg_sha256_final(&ctx->sha256ctx, h);
+	Assert(ctx->sha256ctx != NULL);
+
+	if (pg_cryptohash_final(ctx->sha256ctx, h) < 0)
+	{
+		pg_cryptohash_free(ctx->sha256ctx);
+		return -1;
+	}
 
 	/* H(K XOR opad, tmp) */
-	pg_sha256_init(&ctx->sha256ctx);
-	pg_sha256_update(&ctx->sha256ctx, ctx->k_opad, SHA256_HMAC_B);
-	pg_sha256_update(&ctx->sha256ctx, h, SCRAM_KEY_LEN);
-	pg_sha256_final(&ctx->sha256ctx, result);
+	if (pg_cryptohash_init(ctx->sha256ctx) < 0 ||
+		pg_cryptohash_update(ctx->sha256ctx, ctx->k_opad, SHA256_HMAC_B) < 0 ||
+		pg_cryptohash_update(ctx->sha256ctx, h, SCRAM_KEY_LEN) < 0 ||
+		pg_cryptohash_final(ctx->sha256ctx, result) < 0)
+	{
+		pg_cryptohash_free(ctx->sha256ctx);
+		return -1;
+	}
+
+	pg_cryptohash_free(ctx->sha256ctx);
+	return 0;
 }
 
 /*
  * Calculate SaltedPassword.
  *
- * The password should already be normalized by SASLprep.
+ * The password should already be normalized by SASLprep.  Returns 0 on
+ * success, -1 on failure.
  */
-void
+int
 scram_SaltedPassword(const char *password,
 					 const char *salt, int saltlen, int iterations,
 					 uint8 *result)
@@ -120,63 +158,86 @@ scram_SaltedPassword(const char *password,
 	 */
 
 	/* First iteration */
-	scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len);
-	scram_HMAC_update(&hmac_ctx, salt, saltlen);
-	scram_HMAC_update(&hmac_ctx, (char *) &one, sizeof(uint32));
-	scram_HMAC_final(Ui_prev, &hmac_ctx);
+	if (scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len) < 0 ||
+		scram_HMAC_update(&hmac_ctx, salt, saltlen) < 0 ||
+		scram_HMAC_update(&hmac_ctx, (char *) &one, sizeof(uint32)) < 0 ||
+		scram_HMAC_final(Ui_prev, &hmac_ctx) < 0)
+		return -1;
+
 	memcpy(result, Ui_prev, SCRAM_KEY_LEN);
 
 	/* Subsequent iterations */
 	for (i = 2; i <= iterations; i++)
 	{
-		scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len);
-		scram_HMAC_update(&hmac_ctx, (const char *) Ui_prev, SCRAM_KEY_LEN);
-		scram_HMAC_final(Ui, &hmac_ctx);
+		if (scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len) < 0 ||
+			scram_HMAC_update(&hmac_ctx, (const char *) Ui_prev, SCRAM_KEY_LEN) < 0 ||
+			scram_HMAC_final(Ui, &hmac_ctx) < 0)
+			return -1;
+
 		for (j = 0; j < SCRAM_KEY_LEN; j++)
 			result[j] ^= Ui[j];
 		memcpy(Ui_prev, Ui, SCRAM_KEY_LEN);
 	}
+
+	return 0;
 }
 
 
 /*
  * Calculate SHA-256 hash for a NULL-terminated string. (The NULL terminator is
- * not included in the hash).
+ * not included in the hash).  Returns 0 on success, -1 on failure.
  */
-void
+int
 scram_H(const uint8 *input, int len, uint8 *result)
 {
-	pg_sha256_ctx ctx;
+	pg_cryptohash_ctx *ctx;
 
-	pg_sha256_init(&ctx);
-	pg_sha256_update(&ctx, input, len);
-	pg_sha256_final(&ctx, result);
+	ctx = pg_cryptohash_create(PG_SHA256);
+	if (ctx == NULL)
+		return -1;
+
+	if (pg_cryptohash_init(ctx) < 0 ||
+		pg_cryptohash_update(ctx, input, len) < 0 ||
+		pg_cryptohash_final(ctx, result) < 0)
+	{
+		pg_cryptohash_free(ctx);
+		return -1;
+	}
+
+	pg_cryptohash_free(ctx);
+	return 0;
 }
 
 /*
- * Calculate ClientKey.
+ * Calculate ClientKey.  Returns 0 on success, -1 on failure.
  */
-void
+int
 scram_ClientKey(const uint8 *salted_password, uint8 *result)
 {
 	scram_HMAC_ctx ctx;
 
-	scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN);
-	scram_HMAC_update(&ctx, "Client Key", strlen("Client Key"));
-	scram_HMAC_final(result, &ctx);
+	if (scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN) < 0 ||
+		scram_HMAC_update(&ctx, "Client Key", strlen("Client Key")) < 0 ||
+		scram_HMAC_final(result, &ctx) < 0)
+		return -1;
+
+	return 0;
 }
 
 /*
- * Calculate ServerKey.
+ * Calculate ServerKey.  Returns 0 on success, -1 on failure.
  */
-void
+int
 scram_ServerKey(const uint8 *salted_password, uint8 *result)
 {
 	scram_HMAC_ctx ctx;
 
-	scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN);
-	scram_HMAC_update(&ctx, "Server Key", strlen("Server Key"));
-	scram_HMAC_final(result, &ctx);
+	if (scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN) < 0 ||
+		scram_HMAC_update(&ctx, "Server Key", strlen("Server Key")) < 0 ||
+		scram_HMAC_final(result, &ctx) < 0)
+		return -1;
+
+	return 0;
 }
 
 
@@ -207,12 +268,18 @@ scram_build_secret(const char *salt, int saltlen, int iterations,
 		iterations = SCRAM_DEFAULT_ITERATIONS;
 
 	/* Calculate StoredKey and ServerKey */
-	scram_SaltedPassword(password, salt, saltlen, iterations,
-						 salted_password);
-	scram_ClientKey(salted_password, stored_key);
-	scram_H(stored_key, SCRAM_KEY_LEN, stored_key);
-
-	scram_ServerKey(salted_password, server_key);
+	if (scram_SaltedPassword(password, salt, saltlen, iterations,
+							 salted_password) < 0 ||
+		scram_ClientKey(salted_password, stored_key) < 0 ||
+		scram_H(stored_key, SCRAM_KEY_LEN, stored_key) < 0 ||
+		scram_ServerKey(salted_password, server_key) < 0)
+	{
+#ifdef FRONTEND
+		return NULL;
+#else
+		elog(ERROR, "could not calculate stored key and server key");
+#endif
+	}
 
 	/*----------
 	 * The format is:
diff --git a/src/common/sha2.c b/src/common/sha2.c
index 0d329bb238..15911a3414 100644
--- a/src/common/sha2.c
+++ b/src/common/sha2.c
@@ -1,12 +1,13 @@
 /*-------------------------------------------------------------------------
  *
  * sha2.c
- *	  Set of SHA functions for SHA-224, SHA-256, SHA-384 and SHA-512.
+ *	   SHA functions for SHA-224, SHA-256, SHA-384 and SHA-512.
  *
- * This is the set of in-core functions used when there are no other
- * alternative options like OpenSSL.
+ * This includes the fallback implementation for SHA2 cryptographic
+ * hashes.
  *
- * Portions Copyright (c) 2016-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
  *	  src/common/sha2.c
@@ -56,10 +57,36 @@
 #include "postgres_fe.h"
 #endif
 
-#include <sys/param.h>
-
 #include "common/sha2.h"
 
+/* Internal structures used for the private area of pg_cryptohash_ctx->data */
+typedef struct pg_sha256_ctx
+{
+	uint32		state[8];
+	uint64		bitcount;
+	uint8		buffer[PG_SHA256_BLOCK_LENGTH];
+} pg_sha256_ctx;
+typedef struct pg_sha512_ctx
+{
+	uint64		state[8];
+	uint64		bitcount[2];
+	uint8		buffer[PG_SHA512_BLOCK_LENGTH];
+} pg_sha512_ctx;
+typedef struct pg_sha256_ctx pg_sha224_ctx;
+typedef struct pg_sha512_ctx pg_sha384_ctx;
+
+/*
+ * In backend, use palloc/pfree to ease the error handling.  In frontend,
+ * use malloc to be able to return a failure status back to the caller.
+ */
+#ifndef FRONTEND
+#define ALLOC(size) palloc(size)
+#define FREE(ptr) pfree(ptr)
+#else
+#define ALLOC(size) malloc(size)
+#define FREE(ptr) free(ptr)
+#endif
+
 /*
  * UNROLLED TRANSFORM LOOP NOTE:
  * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform
@@ -264,7 +291,7 @@ static const uint64 sha512_initial_hash_value[8] = {
 
 
 /*** SHA-256: *********************************************************/
-void
+static void
 pg_sha256_init(pg_sha256_ctx *context)
 {
 	if (context == NULL)
@@ -461,7 +488,7 @@ SHA256_Transform(pg_sha256_ctx *context, const uint8 *data)
 }
 #endif							/* SHA2_UNROLL_TRANSFORM */
 
-void
+static void
 pg_sha256_update(pg_sha256_ctx *context, const uint8 *data, size_t len)
 {
 	size_t		freespace,
@@ -562,7 +589,7 @@ SHA256_Last(pg_sha256_ctx *context)
 	SHA256_Transform(context, context->buffer);
 }
 
-void
+static void
 pg_sha256_final(pg_sha256_ctx *context, uint8 *digest)
 {
 	/* If no digest buffer is passed, we don't bother doing this: */
@@ -590,7 +617,7 @@ pg_sha256_final(pg_sha256_ctx *context, uint8 *digest)
 
 
 /*** SHA-512: *********************************************************/
-void
+static void
 pg_sha512_init(pg_sha512_ctx *context)
 {
 	if (context == NULL)
@@ -787,7 +814,7 @@ SHA512_Transform(pg_sha512_ctx *context, const uint8 *data)
 }
 #endif							/* SHA2_UNROLL_TRANSFORM */
 
-void
+static void
 pg_sha512_update(pg_sha512_ctx *context, const uint8 *data, size_t len)
 {
 	size_t		freespace,
@@ -890,7 +917,7 @@ SHA512_Last(pg_sha512_ctx *context)
 	SHA512_Transform(context, context->buffer);
 }
 
-void
+static void
 pg_sha512_final(pg_sha512_ctx *context, uint8 *digest)
 {
 	/* If no digest buffer is passed, we don't bother doing this: */
@@ -919,7 +946,7 @@ pg_sha512_final(pg_sha512_ctx *context, uint8 *digest)
 
 
 /*** SHA-384: *********************************************************/
-void
+static void
 pg_sha384_init(pg_sha384_ctx *context)
 {
 	if (context == NULL)
@@ -929,13 +956,13 @@ pg_sha384_init(pg_sha384_ctx *context)
 	context->bitcount[0] = context->bitcount[1] = 0;
 }
 
-void
+static void
 pg_sha384_update(pg_sha384_ctx *context, const uint8 *data, size_t len)
 {
 	pg_sha512_update((pg_sha512_ctx *) context, data, len);
 }
 
-void
+static void
 pg_sha384_final(pg_sha384_ctx *context, uint8 *digest)
 {
 	/* If no digest buffer is passed, we don't bother doing this: */
@@ -963,7 +990,7 @@ pg_sha384_final(pg_sha384_ctx *context, uint8 *digest)
 }
 
 /*** SHA-224: *********************************************************/
-void
+static void
 pg_sha224_init(pg_sha224_ctx *context)
 {
 	if (context == NULL)
@@ -973,13 +1000,13 @@ pg_sha224_init(pg_sha224_ctx *context)
 	context->bitcount = 0;
 }
 
-void
+static void
 pg_sha224_update(pg_sha224_ctx *context, const uint8 *data, size_t len)
 {
 	pg_sha256_update((pg_sha256_ctx *) context, data, len);
 }
 
-void
+static void
 pg_sha224_final(pg_sha224_ctx *context, uint8 *digest)
 {
 	/* If no digest buffer is passed, we don't bother doing this: */
diff --git a/src/common/sha2_openssl.c b/src/common/sha2_openssl.c
deleted file mode 100644
index 41673b3a88..0000000000
--- a/src/common/sha2_openssl.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * sha2_openssl.c
- *	  Set of wrapper routines on top of OpenSSL to support SHA-224
- *	  SHA-256, SHA-384 and SHA-512 functions.
- *
- * This should only be used if code is compiled with OpenSSL support.
- *
- * Portions Copyright (c) 2016-2020, PostgreSQL Global Development Group
- *
- * IDENTIFICATION
- *		  src/common/sha2_openssl.c
- *
- *-------------------------------------------------------------------------
- */
-
-#ifndef FRONTEND
-#include "postgres.h"
-#else
-#include "postgres_fe.h"
-#endif
-
-#include <openssl/sha.h>
-
-#include "common/sha2.h"
-
-
-/* Interface routines for SHA-256 */
-void
-pg_sha256_init(pg_sha256_ctx *ctx)
-{
-	SHA256_Init((SHA256_CTX *) ctx);
-}
-
-void
-pg_sha256_update(pg_sha256_ctx *ctx, const uint8 *data, size_t len)
-{
-	SHA256_Update((SHA256_CTX *) ctx, data, len);
-}
-
-void
-pg_sha256_final(pg_sha256_ctx *ctx, uint8 *dest)
-{
-	SHA256_Final(dest, (SHA256_CTX *) ctx);
-}
-
-/* Interface routines for SHA-512 */
-void
-pg_sha512_init(pg_sha512_ctx *ctx)
-{
-	SHA512_Init((SHA512_CTX *) ctx);
-}
-
-void
-pg_sha512_update(pg_sha512_ctx *ctx, const uint8 *data, size_t len)
-{
-	SHA512_Update((SHA512_CTX *) ctx, data, len);
-}
-
-void
-pg_sha512_final(pg_sha512_ctx *ctx, uint8 *dest)
-{
-	SHA512_Final(dest, (SHA512_CTX *) ctx);
-}
-
-/* Interface routines for SHA-384 */
-void
-pg_sha384_init(pg_sha384_ctx *ctx)
-{
-	SHA384_Init((SHA512_CTX *) ctx);
-}
-
-void
-pg_sha384_update(pg_sha384_ctx *ctx, const uint8 *data, size_t len)
-{
-	SHA384_Update((SHA512_CTX *) ctx, data, len);
-}
-
-void
-pg_sha384_final(pg_sha384_ctx *ctx, uint8 *dest)
-{
-	SHA384_Final(dest, (SHA512_CTX *) ctx);
-}
-
-/* Interface routines for SHA-224 */
-void
-pg_sha224_init(pg_sha224_ctx *ctx)
-{
-	SHA224_Init((SHA256_CTX *) ctx);
-}
-
-void
-pg_sha224_update(pg_sha224_ctx *ctx, const uint8 *data, size_t len)
-{
-	SHA224_Update((SHA256_CTX *) ctx, data, len);
-}
-
-void
-pg_sha224_final(pg_sha224_ctx *ctx, uint8 *dest)
-{
-	SHA224_Final(dest, (SHA256_CTX *) ctx);
-}
diff --git a/src/bin/pg_verifybackup/parse_manifest.c b/src/bin/pg_verifybackup/parse_manifest.c
index 608e23538b..5b4ce28837 100644
--- a/src/bin/pg_verifybackup/parse_manifest.c
+++ b/src/bin/pg_verifybackup/parse_manifest.c
@@ -624,7 +624,7 @@ verify_manifest_checksum(JsonManifestParseState *parse, char *buffer,
 	size_t		number_of_newlines = 0;
 	size_t		ultimate_newline = 0;
 	size_t		penultimate_newline = 0;
-	pg_sha256_ctx manifest_ctx;
+	pg_cryptohash_ctx *manifest_ctx;
 	uint8		manifest_checksum_actual[PG_SHA256_DIGEST_LENGTH];
 	uint8		manifest_checksum_expected[PG_SHA256_DIGEST_LENGTH];
 
@@ -652,9 +652,15 @@ verify_manifest_checksum(JsonManifestParseState *parse, char *buffer,
 									"last line not newline-terminated");
 
 	/* Checksum the rest. */
-	pg_sha256_init(&manifest_ctx);
-	pg_sha256_update(&manifest_ctx, (uint8 *) buffer, penultimate_newline + 1);
-	pg_sha256_final(&manifest_ctx, manifest_checksum_actual);
+	manifest_ctx = pg_cryptohash_create(PG_SHA256);
+	if (manifest_ctx == NULL)
+		context->error_cb(context, "out of memory");
+	if (pg_cryptohash_init(manifest_ctx) < 0)
+		context->error_cb(context, "could not initialize checksum of manifest");
+	if (pg_cryptohash_update(manifest_ctx, (uint8 *) buffer, penultimate_newline + 1) < 0)
+		context->error_cb(context, "could not update checksum of manifest");
+	if (pg_cryptohash_final(manifest_ctx, manifest_checksum_actual) < 0)
+		context->error_cb(context, "could not finalize checksum of manifest");
 
 	/* Now verify it. */
 	if (parse->manifest_checksum == NULL)
@@ -667,6 +673,7 @@ verify_manifest_checksum(JsonManifestParseState *parse, char *buffer,
 	if (memcmp(manifest_checksum_actual, manifest_checksum_expected,
 			   PG_SHA256_DIGEST_LENGTH) != 0)
 		context->error_cb(context, "manifest checksum mismatch");
+	pg_cryptohash_free(manifest_ctx);
 }
 
 /*
diff --git a/src/bin/pg_verifybackup/pg_verifybackup.c b/src/bin/pg_verifybackup/pg_verifybackup.c
index bb3733b57e..07320d3699 100644
--- a/src/bin/pg_verifybackup/pg_verifybackup.c
+++ b/src/bin/pg_verifybackup/pg_verifybackup.c
@@ -726,13 +726,26 @@ verify_file_checksum(verifier_context *context, manifest_file *m,
 	}
 
 	/* Initialize checksum context. */
-	pg_checksum_init(&checksum_ctx, m->checksum_type);
+	if (pg_checksum_init(&checksum_ctx, m->checksum_type) < 0)
+	{
+		report_backup_error(context, "could not initialize checksum of file \"%s\"",
+							relpath);
+		return;
+	}
 
 	/* Read the file chunk by chunk, updating the checksum as we go. */
 	while ((rc = read(fd, buffer, READ_CHUNK_SIZE)) > 0)
 	{
 		bytes_read += rc;
-		pg_checksum_update(&checksum_ctx, buffer, rc);
+		if (pg_checksum_update(&checksum_ctx, buffer, rc) < 0)
+		{
+			report_backup_error(context, "could not update checksum of file \"%s\"",
+								relpath);
+			close(fd);
+			return;
+		}
+
+
 	}
 	if (rc < 0)
 		report_backup_error(context, "could not read file \"%s\": %m",
@@ -767,6 +780,13 @@ verify_file_checksum(verifier_context *context, manifest_file *m,
 
 	/* Get the final checksum. */
 	checksumlen = pg_checksum_final(&checksum_ctx, checksumbuf);
+	if (checksumlen < 0)
+	{
+		report_backup_error(context,
+							"could not finalize checksum of file \"%s\"",
+							relpath);
+		return;
+	}
 
 	/* And check it against the manifest. */
 	if (checksumlen != m->checksum_length)
diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c
index 6d266e9796..0a216cbe84 100644
--- a/src/interfaces/libpq/fe-auth-scram.c
+++ b/src/interfaces/libpq/fe-auth-scram.c
@@ -63,8 +63,8 @@ static bool read_server_first_message(fe_scram_state *state, char *input);
 static bool read_server_final_message(fe_scram_state *state, char *input);
 static char *build_client_first_message(fe_scram_state *state);
 static char *build_client_final_message(fe_scram_state *state);
-static bool verify_server_signature(fe_scram_state *state);
-static void calculate_client_proof(fe_scram_state *state,
+static bool verify_server_signature(fe_scram_state *state, bool *match);
+static bool calculate_client_proof(fe_scram_state *state,
 								   const char *client_final_message_without_proof,
 								   uint8 *result);
 
@@ -256,11 +256,15 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
 			 * Verify server signature, to make sure we're talking to the
 			 * genuine server.
 			 */
-			if (verify_server_signature(state))
-				*success = true;
-			else
+			if (!verify_server_signature(state, success))
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("could not verify server signature\n"));
+				goto error;
+			}
+
+			if (!*success)
 			{
-				*success = false;
 				printfPQExpBuffer(&conn->errorMessage,
 								  libpq_gettext("incorrect server signature\n"));
 			}
@@ -544,9 +548,15 @@ build_client_final_message(fe_scram_state *state)
 		goto oom_error;
 
 	/* Append proof to it, to form client-final-message. */
-	calculate_client_proof(state,
-						   state->client_final_message_without_proof,
-						   client_proof);
+	if (!calculate_client_proof(state,
+								state->client_final_message_without_proof,
+								client_proof))
+	{
+		termPQExpBuffer(&buf);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not calculate client proof\n"));
+		return NULL;
+	}
 
 	appendPQExpBufferStr(&buf, ",p=");
 	encoded_len = pg_b64_enc_len(SCRAM_KEY_LEN);
@@ -745,9 +755,9 @@ read_server_final_message(fe_scram_state *state, char *input)
 
 /*
  * Calculate the client proof, part of the final exchange message sent
- * by the client.
+ * by the client.  Returns true on success, false on failure.
  */
-static void
+static bool
 calculate_client_proof(fe_scram_state *state,
 					   const char *client_final_message_without_proof,
 					   uint8 *result)
@@ -762,61 +772,67 @@ calculate_client_proof(fe_scram_state *state,
 	 * Calculate SaltedPassword, and store it in 'state' so that we can reuse
 	 * it later in verify_server_signature.
 	 */
-	scram_SaltedPassword(state->password, state->salt, state->saltlen,
-						 state->iterations, state->SaltedPassword);
-
-	scram_ClientKey(state->SaltedPassword, ClientKey);
-	scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey);
-
-	scram_HMAC_init(&ctx, StoredKey, SCRAM_KEY_LEN);
-	scram_HMAC_update(&ctx,
-					  state->client_first_message_bare,
-					  strlen(state->client_first_message_bare));
-	scram_HMAC_update(&ctx, ",", 1);
-	scram_HMAC_update(&ctx,
-					  state->server_first_message,
-					  strlen(state->server_first_message));
-	scram_HMAC_update(&ctx, ",", 1);
-	scram_HMAC_update(&ctx,
-					  client_final_message_without_proof,
-					  strlen(client_final_message_without_proof));
-	scram_HMAC_final(ClientSignature, &ctx);
+	if (scram_SaltedPassword(state->password, state->salt, state->saltlen,
+							 state->iterations, state->SaltedPassword) < 0 ||
+		scram_ClientKey(state->SaltedPassword, ClientKey) < 0 ||
+		scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey) < 0 ||
+		scram_HMAC_init(&ctx, StoredKey, SCRAM_KEY_LEN) < 0 ||
+		scram_HMAC_update(&ctx,
+						  state->client_first_message_bare,
+						  strlen(state->client_first_message_bare)) < 0 ||
+		scram_HMAC_update(&ctx, ",", 1) < 0 ||
+		scram_HMAC_update(&ctx,
+						  state->server_first_message,
+						  strlen(state->server_first_message)) < 0 ||
+		scram_HMAC_update(&ctx, ",", 1) < 0 ||
+		scram_HMAC_update(&ctx,
+						  client_final_message_without_proof,
+						  strlen(client_final_message_without_proof)) < 0 ||
+		scram_HMAC_final(ClientSignature, &ctx) < 0)
+		return false;
 
 	for (i = 0; i < SCRAM_KEY_LEN; i++)
 		result[i] = ClientKey[i] ^ ClientSignature[i];
+
+	return true;
 }
 
 /*
  * Validate the server signature, received as part of the final exchange
- * message received from the server.
+ * message received from the server.  *match tracks if the server signature
+ * matched or not. Returns true if the server signature got verified, and
+ * false for a processing error.
  */
 static bool
-verify_server_signature(fe_scram_state *state)
+verify_server_signature(fe_scram_state *state, bool *match)
 {
 	uint8		expected_ServerSignature[SCRAM_KEY_LEN];
 	uint8		ServerKey[SCRAM_KEY_LEN];
 	scram_HMAC_ctx ctx;
 
-	scram_ServerKey(state->SaltedPassword, ServerKey);
-
+	if (scram_ServerKey(state->SaltedPassword, ServerKey) < 0 ||
 	/* calculate ServerSignature */
-	scram_HMAC_init(&ctx, ServerKey, SCRAM_KEY_LEN);
-	scram_HMAC_update(&ctx,
-					  state->client_first_message_bare,
-					  strlen(state->client_first_message_bare));
-	scram_HMAC_update(&ctx, ",", 1);
-	scram_HMAC_update(&ctx,
-					  state->server_first_message,
-					  strlen(state->server_first_message));
-	scram_HMAC_update(&ctx, ",", 1);
-	scram_HMAC_update(&ctx,
-					  state->client_final_message_without_proof,
-					  strlen(state->client_final_message_without_proof));
-	scram_HMAC_final(expected_ServerSignature, &ctx);
-
-	if (memcmp(expected_ServerSignature, state->ServerSignature, SCRAM_KEY_LEN) != 0)
+		scram_HMAC_init(&ctx, ServerKey, SCRAM_KEY_LEN) < 0 ||
+		scram_HMAC_update(&ctx,
+						  state->client_first_message_bare,
+						  strlen(state->client_first_message_bare)) < 0 ||
+		scram_HMAC_update(&ctx, ",", 1) < 0 ||
+		scram_HMAC_update(&ctx,
+						  state->server_first_message,
+						  strlen(state->server_first_message)) < 0 ||
+		scram_HMAC_update(&ctx, ",", 1) < 0 ||
+		scram_HMAC_update(&ctx,
+						  state->client_final_message_without_proof,
+						  strlen(state->client_final_message_without_proof)) < 0 ||
+		scram_HMAC_final(expected_ServerSignature, &ctx) < 0)
 		return false;
 
+	/* signature processed, so now check after it */
+	if (memcmp(expected_ServerSignature, state->ServerSignature, SCRAM_KEY_LEN) != 0)
+		*match = false;
+	else
+		*match = true;
+
 	return true;
 }
 
diff --git a/contrib/pgcrypto/internal-sha2.c b/contrib/pgcrypto/internal-sha2.c
index 9fa940b5bb..0fe53e15af 100644
--- a/contrib/pgcrypto/internal-sha2.c
+++ b/contrib/pgcrypto/internal-sha2.c
@@ -33,6 +33,7 @@
 
 #include <time.h>
 
+#include "common/cryptohash.h"
 #include "common/sha2.h"
 #include "px.h"
 
@@ -42,7 +43,6 @@ void		init_sha384(PX_MD *h);
 void		init_sha512(PX_MD *h);
 
 /* SHA224 */
-
 static unsigned
 int_sha224_len(PX_MD *h)
 {
@@ -55,42 +55,7 @@ int_sha224_block_len(PX_MD *h)
 	return PG_SHA224_BLOCK_LENGTH;
 }
 
-static void
-int_sha224_update(PX_MD *h, const uint8 *data, unsigned dlen)
-{
-	pg_sha224_ctx *ctx = (pg_sha224_ctx *) h->p.ptr;
-
-	pg_sha224_update(ctx, data, dlen);
-}
-
-static void
-int_sha224_reset(PX_MD *h)
-{
-	pg_sha224_ctx *ctx = (pg_sha224_ctx *) h->p.ptr;
-
-	pg_sha224_init(ctx);
-}
-
-static void
-int_sha224_finish(PX_MD *h, uint8 *dst)
-{
-	pg_sha224_ctx *ctx = (pg_sha224_ctx *) h->p.ptr;
-
-	pg_sha224_final(ctx, dst);
-}
-
-static void
-int_sha224_free(PX_MD *h)
-{
-	pg_sha224_ctx *ctx = (pg_sha224_ctx *) h->p.ptr;
-
-	px_memset(ctx, 0, sizeof(*ctx));
-	pfree(ctx);
-	pfree(h);
-}
-
 /* SHA256 */
-
 static unsigned
 int_sha256_len(PX_MD *h)
 {
@@ -103,42 +68,7 @@ int_sha256_block_len(PX_MD *h)
 	return PG_SHA256_BLOCK_LENGTH;
 }
 
-static void
-int_sha256_update(PX_MD *h, const uint8 *data, unsigned dlen)
-{
-	pg_sha256_ctx *ctx = (pg_sha256_ctx *) h->p.ptr;
-
-	pg_sha256_update(ctx, data, dlen);
-}
-
-static void
-int_sha256_reset(PX_MD *h)
-{
-	pg_sha256_ctx *ctx = (pg_sha256_ctx *) h->p.ptr;
-
-	pg_sha256_init(ctx);
-}
-
-static void
-int_sha256_finish(PX_MD *h, uint8 *dst)
-{
-	pg_sha256_ctx *ctx = (pg_sha256_ctx *) h->p.ptr;
-
-	pg_sha256_final(ctx, dst);
-}
-
-static void
-int_sha256_free(PX_MD *h)
-{
-	pg_sha256_ctx *ctx = (pg_sha256_ctx *) h->p.ptr;
-
-	px_memset(ctx, 0, sizeof(*ctx));
-	pfree(ctx);
-	pfree(h);
-}
-
 /* SHA384 */
-
 static unsigned
 int_sha384_len(PX_MD *h)
 {
@@ -151,42 +81,7 @@ int_sha384_block_len(PX_MD *h)
 	return PG_SHA384_BLOCK_LENGTH;
 }
 
-static void
-int_sha384_update(PX_MD *h, const uint8 *data, unsigned dlen)
-{
-	pg_sha384_ctx *ctx = (pg_sha384_ctx *) h->p.ptr;
-
-	pg_sha384_update(ctx, data, dlen);
-}
-
-static void
-int_sha384_reset(PX_MD *h)
-{
-	pg_sha384_ctx *ctx = (pg_sha384_ctx *) h->p.ptr;
-
-	pg_sha384_init(ctx);
-}
-
-static void
-int_sha384_finish(PX_MD *h, uint8 *dst)
-{
-	pg_sha384_ctx *ctx = (pg_sha384_ctx *) h->p.ptr;
-
-	pg_sha384_final(ctx, dst);
-}
-
-static void
-int_sha384_free(PX_MD *h)
-{
-	pg_sha384_ctx *ctx = (pg_sha384_ctx *) h->p.ptr;
-
-	px_memset(ctx, 0, sizeof(*ctx));
-	pfree(ctx);
-	pfree(h);
-}
-
 /* SHA512 */
-
 static unsigned
 int_sha512_len(PX_MD *h)
 {
@@ -199,37 +94,40 @@ int_sha512_block_len(PX_MD *h)
 	return PG_SHA512_BLOCK_LENGTH;
 }
 
+/* Generic interface for all SHA2 methods */
 static void
-int_sha512_update(PX_MD *h, const uint8 *data, unsigned dlen)
+int_sha2_update(PX_MD *h, const uint8 *data, unsigned dlen)
 {
-	pg_sha512_ctx *ctx = (pg_sha512_ctx *) h->p.ptr;
+	pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
 
-	pg_sha512_update(ctx, data, dlen);
+	if (pg_cryptohash_update(ctx, data, dlen) < 0)
+		elog(ERROR, "could not update %s context", "SHA2");
 }
 
 static void
-int_sha512_reset(PX_MD *h)
+int_sha2_reset(PX_MD *h)
 {
-	pg_sha512_ctx *ctx = (pg_sha512_ctx *) h->p.ptr;
+	pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
 
-	pg_sha512_init(ctx);
+	if (pg_cryptohash_init(ctx) < 0)
+		elog(ERROR, "could not initialize %s context", "SHA2");
 }
 
 static void
-int_sha512_finish(PX_MD *h, uint8 *dst)
+int_sha2_finish(PX_MD *h, uint8 *dst)
 {
-	pg_sha512_ctx *ctx = (pg_sha512_ctx *) h->p.ptr;
+	pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
 
-	pg_sha512_final(ctx, dst);
+	if (pg_cryptohash_final(ctx, dst) < 0)
+		elog(ERROR, "could not finalize %s context", "SHA2");
 }
 
 static void
-int_sha512_free(PX_MD *h)
+int_sha2_free(PX_MD *h)
 {
-	pg_sha512_ctx *ctx = (pg_sha512_ctx *) h->p.ptr;
+	pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
 
-	px_memset(ctx, 0, sizeof(*ctx));
-	pfree(ctx);
+	pg_cryptohash_free(ctx);
 	pfree(h);
 }
 
@@ -238,18 +136,17 @@ int_sha512_free(PX_MD *h)
 void
 init_sha224(PX_MD *md)
 {
-	pg_sha224_ctx *ctx;
-
-	ctx = palloc0(sizeof(*ctx));
+	pg_cryptohash_ctx *ctx;
 
+	ctx = pg_cryptohash_create(PG_SHA224);
 	md->p.ptr = ctx;
 
 	md->result_size = int_sha224_len;
 	md->block_size = int_sha224_block_len;
-	md->reset = int_sha224_reset;
-	md->update = int_sha224_update;
-	md->finish = int_sha224_finish;
-	md->free = int_sha224_free;
+	md->reset = int_sha2_reset;
+	md->update = int_sha2_update;
+	md->finish = int_sha2_finish;
+	md->free = int_sha2_free;
 
 	md->reset(md);
 }
@@ -257,18 +154,17 @@ init_sha224(PX_MD *md)
 void
 init_sha256(PX_MD *md)
 {
-	pg_sha256_ctx *ctx;
-
-	ctx = palloc0(sizeof(*ctx));
+	pg_cryptohash_ctx *ctx;
 
+	ctx = pg_cryptohash_create(PG_SHA256);
 	md->p.ptr = ctx;
 
 	md->result_size = int_sha256_len;
 	md->block_size = int_sha256_block_len;
-	md->reset = int_sha256_reset;
-	md->update = int_sha256_update;
-	md->finish = int_sha256_finish;
-	md->free = int_sha256_free;
+	md->reset = int_sha2_reset;
+	md->update = int_sha2_update;
+	md->finish = int_sha2_finish;
+	md->free = int_sha2_free;
 
 	md->reset(md);
 }
@@ -276,18 +172,17 @@ init_sha256(PX_MD *md)
 void
 init_sha384(PX_MD *md)
 {
-	pg_sha384_ctx *ctx;
-
-	ctx = palloc0(sizeof(*ctx));
+	pg_cryptohash_ctx *ctx;
 
+	ctx = pg_cryptohash_create(PG_SHA384);
 	md->p.ptr = ctx;
 
 	md->result_size = int_sha384_len;
 	md->block_size = int_sha384_block_len;
-	md->reset = int_sha384_reset;
-	md->update = int_sha384_update;
-	md->finish = int_sha384_finish;
-	md->free = int_sha384_free;
+	md->reset = int_sha2_reset;
+	md->update = int_sha2_update;
+	md->finish = int_sha2_finish;
+	md->free = int_sha2_free;
 
 	md->reset(md);
 }
@@ -295,18 +190,17 @@ init_sha384(PX_MD *md)
 void
 init_sha512(PX_MD *md)
 {
-	pg_sha512_ctx *ctx;
-
-	ctx = palloc0(sizeof(*ctx));
+	pg_cryptohash_ctx *ctx;
 
+	ctx = pg_cryptohash_create(PG_SHA512);
 	md->p.ptr = ctx;
 
 	md->result_size = int_sha512_len;
 	md->block_size = int_sha512_block_len;
-	md->reset = int_sha512_reset;
-	md->update = int_sha512_update;
-	md->finish = int_sha512_finish;
-	md->free = int_sha512_free;
+	md->reset = int_sha2_reset;
+	md->update = int_sha2_update;
+	md->finish = int_sha2_finish;
+	md->free = int_sha2_free;
 
 	md->reset(md);
 }
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 90594bd41b..af15d3db6c 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -129,12 +129,12 @@ sub mkvcbuild
 
 	if ($solution->{options}->{openssl})
 	{
-		push(@pgcommonallfiles, 'sha2_openssl.c');
+		push(@pgcommonallfiles, 'cryptohash_openssl.c');
 		push(@pgcommonallfiles, 'protocol_openssl.c');
 	}
 	else
 	{
-		push(@pgcommonallfiles, 'sha2.c');
+		push(@pgcommonallfiles, 'cryptohash.c');
 	}
 
 	our @pgcommonfrontendfiles = (
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 0e4bb4f07f..0217f76add 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3191,6 +3191,7 @@ pg_mb_radix_tree
 pg_on_exit_callback
 pg_re_flags
 pg_saslprep_rc
+pg_cryptohash_ctx
 pg_sha224_ctx
 pg_sha256_ctx
 pg_sha384_ctx
-- 
2.29.2

From a92306674f07c82a920250486c93671648dcdc4d Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Fri, 13 Nov 2020 11:50:21 +0900
Subject: [PATCH v4 2/3] Switch cryptohash_openssl.c to use EVP

Postgres is two decades late for this switch.
---
 src/include/utils/resowner_private.h  |  7 +++
 src/backend/utils/resowner/resowner.c | 65 ++++++++++++++++++++
 src/common/cryptohash_openssl.c       | 88 +++++++++++++--------------
 3 files changed, 113 insertions(+), 47 deletions(-)

diff --git a/src/include/utils/resowner_private.h b/src/include/utils/resowner_private.h
index a781a7a2aa..5ce6fcf882 100644
--- a/src/include/utils/resowner_private.h
+++ b/src/include/utils/resowner_private.h
@@ -95,4 +95,11 @@ extern void ResourceOwnerRememberJIT(ResourceOwner owner,
 extern void ResourceOwnerForgetJIT(ResourceOwner owner,
 								   Datum handle);
 
+/* support for EVP context management */
+extern void ResourceOwnerEnlargeEVP(ResourceOwner owner);
+extern void ResourceOwnerRememberEVP(ResourceOwner owner,
+									 Datum handle);
+extern void ResourceOwnerForgetEVP(ResourceOwner owner,
+								   Datum handle);
+
 #endif							/* RESOWNER_PRIVATE_H */
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 8bc2c4e9ea..1efb5e98b4 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -20,6 +20,10 @@
  */
 #include "postgres.h"
 
+#ifdef USE_OPENSSL
+#include <openssl/evp.h>
+#endif
+
 #include "common/hashfn.h"
 #include "jit/jit.h"
 #include "storage/bufmgr.h"
@@ -128,6 +132,7 @@ typedef struct ResourceOwnerData
 	ResourceArray filearr;		/* open temporary files */
 	ResourceArray dsmarr;		/* dynamic shmem segments */
 	ResourceArray jitarr;		/* JIT contexts */
+	ResourceArray evparr;		/* EVP contexts */
 
 	/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
 	int			nlocks;			/* number of owned locks */
@@ -175,6 +180,7 @@ static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
 static void PrintSnapshotLeakWarning(Snapshot snapshot);
 static void PrintFileLeakWarning(File file);
 static void PrintDSMLeakWarning(dsm_segment *seg);
+static void PrintEVPLeakWarning(Datum handle);
 
 
 /*****************************************************************************
@@ -444,6 +450,7 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
 	ResourceArrayInit(&(owner->filearr), FileGetDatum(-1));
 	ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL));
 	ResourceArrayInit(&(owner->jitarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->evparr), PointerGetDatum(NULL));
 
 	return owner;
 }
@@ -553,6 +560,17 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 
 			jit_release_context(context);
 		}
+
+		/* Ditto for EVP contexts */
+		while (ResourceArrayGetAny(&(owner->evparr), &foundres))
+		{
+			if (isCommit)
+				PrintEVPLeakWarning(foundres);
+#ifdef USE_OPENSSL
+			EVP_MD_CTX_destroy((EVP_MD_CTX *) DatumGetPointer(foundres));
+#endif
+			ResourceOwnerForgetEVP(owner, foundres);
+		}
 	}
 	else if (phase == RESOURCE_RELEASE_LOCKS)
 	{
@@ -725,6 +743,7 @@ ResourceOwnerDelete(ResourceOwner owner)
 	Assert(owner->filearr.nitems == 0);
 	Assert(owner->dsmarr.nitems == 0);
 	Assert(owner->jitarr.nitems == 0);
+	Assert(owner->evparr.nitems == 0);
 	Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
 
 	/*
@@ -752,6 +771,7 @@ ResourceOwnerDelete(ResourceOwner owner)
 	ResourceArrayFree(&(owner->filearr));
 	ResourceArrayFree(&(owner->dsmarr));
 	ResourceArrayFree(&(owner->jitarr));
+	ResourceArrayFree(&(owner->evparr));
 
 	pfree(owner);
 }
@@ -1370,3 +1390,48 @@ ResourceOwnerForgetJIT(ResourceOwner owner, Datum handle)
 		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
+ * EVP 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
+ResourceOwnerEnlargeEVP(ResourceOwner owner)
+{
+	ResourceArrayEnlarge(&(owner->evparr));
+}
+
+/*
+ * Remember that an EVP context is owned by a ResourceOwner
+ *
+ * Caller must have previously done ResourceOwnerEnlargeEVP()
+ */
+void
+ResourceOwnerRememberEVP(ResourceOwner owner, Datum handle)
+{
+	ResourceArrayAdd(&(owner->evparr), handle);
+}
+
+/*
+ * Forget that an EVP context is owned by a ResourceOwner
+ */
+void
+ResourceOwnerForgetEVP(ResourceOwner owner, Datum handle)
+{
+	if (!ResourceArrayRemove(&(owner->evparr), handle))
+		elog(ERROR, "EVP context %p is not owned by resource owner %s",
+			 DatumGetPointer(handle), owner->name);
+}
+
+/*
+ * Debugging subroutine
+ */
+static void
+PrintEVPLeakWarning(Datum handle)
+{
+	elog(WARNING, "EVP context reference leak: context %p still referenced",
+		 DatumGetPointer(handle));
+}
diff --git a/src/common/cryptohash_openssl.c b/src/common/cryptohash_openssl.c
index 406abf6163..cde3c9d597 100644
--- a/src/common/cryptohash_openssl.c
+++ b/src/common/cryptohash_openssl.c
@@ -21,9 +21,14 @@
 #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,
@@ -53,25 +58,31 @@ pg_cryptohash_create(pg_cryptohash_type type)
 
 	ctx->type = type;
 
-	switch (type)
-	{
-		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;
-	}
+#ifndef FRONTEND
+	ResourceOwnerEnlargeEVP(CurrentResourceOwner);
+#endif
+
+	/*
+	 * Initialization takes care of assigning the correct type for OpenSSL.
+	 */
+	ctx->data = EVP_MD_CTX_create();
 
 	if (ctx->data == NULL)
 	{
 		explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
+#ifndef FRONTEND
+		elog(ERROR, "out of memory");
+#else
 		FREE(ctx);
 		return NULL;
+#endif
 	}
 
+#ifndef FRONTEND
+	ResourceOwnerRememberEVP(CurrentResourceOwner,
+							 PointerGetDatum(ctx->data));
+#endif
+
 	return ctx;
 }
 
@@ -91,16 +102,20 @@ pg_cryptohash_init(pg_cryptohash_ctx *ctx)
 	switch (ctx->type)
 	{
 		case PG_SHA224:
-			status = SHA224_Init((SHA256_CTX *) ctx->data);
+			status = EVP_DigestInit_ex((EVP_MD_CTX *) ctx->data,
+									   EVP_sha224(), NULL);
 			break;
 		case PG_SHA256:
-			status = SHA256_Init((SHA256_CTX *) ctx->data);
+			status = EVP_DigestInit_ex((EVP_MD_CTX *) ctx->data,
+									   EVP_sha256(), NULL);
 			break;
 		case PG_SHA384:
-			status = SHA384_Init((SHA512_CTX *) ctx->data);
+			status = EVP_DigestInit_ex((EVP_MD_CTX *) ctx->data,
+									   EVP_sha384(), NULL);
 			break;
 		case PG_SHA512:
-			status = SHA512_Init((SHA512_CTX *) ctx->data);
+			status = EVP_DigestInit_ex((EVP_MD_CTX *) ctx->data,
+									   EVP_sha512(), NULL);
 			break;
 	}
 
@@ -123,21 +138,7 @@ pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
 	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;
-	}
+	status = EVP_DigestUpdate((EVP_MD_CTX *) ctx->data, data, len);
 
 	/* OpenSSL internals return 1 on success, 0 on failure */
 	if (status <= 0)
@@ -158,21 +159,7 @@ pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest)
 	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;
-	}
+	status = EVP_DigestFinal_ex((EVP_MD_CTX *) ctx->data, dest, 0);
 
 	/* OpenSSL internals return 1 on success, 0 on failure */
 	if (status <= 0)
@@ -190,7 +177,14 @@ pg_cryptohash_free(pg_cryptohash_ctx *ctx)
 {
 	if (ctx == NULL)
 		return;
-	FREE(ctx->data);
+
+	EVP_MD_CTX_destroy((EVP_MD_CTX *) ctx->data);
+
+#ifndef FRONTEND
+	ResourceOwnerForgetEVP(CurrentResourceOwner,
+						   PointerGetDatum(ctx->data));
+#endif
+
 	explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
 	FREE(ctx);
 }
-- 
2.29.2

From 499530b170dcd4b5ec2b519284f835171a9cee1f Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Thu, 5 Nov 2020 15:34:55 +0900
Subject: [PATCH v4 3/3] Move pgcrypto to use in-core resowner facility for EVP

This simplifies some code in pgcrypto as it does not need anymore to
rely on its own internal logic for the EVP registration and automatic
cleanup.
---
 contrib/pgcrypto/openssl.c | 87 +++++---------------------------------
 1 file changed, 10 insertions(+), 77 deletions(-)

diff --git a/contrib/pgcrypto/openssl.c b/contrib/pgcrypto/openssl.c
index 5ebe213406..d8e1269a40 100644
--- a/contrib/pgcrypto/openssl.c
+++ b/contrib/pgcrypto/openssl.c
@@ -38,6 +38,7 @@
 #include "px.h"
 #include "utils/memutils.h"
 #include "utils/resowner.h"
+#include "utils/resowner_private.h"
 
 /*
  * Max lengths we might want to handle.
@@ -49,67 +50,12 @@
  * Hashes
  */
 
-/*
- * To make sure we don't leak OpenSSL handles on abort, we keep OSSLDigest
- * objects in a linked list, allocated in TopMemoryContext. We use the
- * ResourceOwner mechanism to free them on abort.
- */
 typedef struct OSSLDigest
 {
 	const EVP_MD *algo;
 	EVP_MD_CTX *ctx;
-
-	ResourceOwner owner;
-	struct OSSLDigest *next;
-	struct OSSLDigest *prev;
 } OSSLDigest;
 
-static OSSLDigest *open_digests = NULL;
-static bool digest_resowner_callback_registered = false;
-
-static void
-free_openssl_digest(OSSLDigest *digest)
-{
-	EVP_MD_CTX_destroy(digest->ctx);
-	if (digest->prev)
-		digest->prev->next = digest->next;
-	else
-		open_digests = digest->next;
-	if (digest->next)
-		digest->next->prev = digest->prev;
-	pfree(digest);
-}
-
-/*
- * Close any open OpenSSL handles on abort.
- */
-static void
-digest_free_callback(ResourceReleasePhase phase,
-					 bool isCommit,
-					 bool isTopLevel,
-					 void *arg)
-{
-	OSSLDigest *curr;
-	OSSLDigest *next;
-
-	if (phase != RESOURCE_RELEASE_AFTER_LOCKS)
-		return;
-
-	next = open_digests;
-	while (next)
-	{
-		curr = next;
-		next = curr->next;
-
-		if (curr->owner == CurrentResourceOwner)
-		{
-			if (isCommit)
-				elog(WARNING, "pgcrypto digest reference leak: digest %p still referenced", curr);
-			free_openssl_digest(curr);
-		}
-	}
-}
-
 static unsigned
 digest_result_size(PX_MD *h)
 {
@@ -155,7 +101,10 @@ digest_free(PX_MD *h)
 {
 	OSSLDigest *digest = (OSSLDigest *) h->p.ptr;
 
-	free_openssl_digest(digest);
+	EVP_MD_CTX_destroy(digest->ctx);
+	ResourceOwnerForgetEVP(CurrentResourceOwner,
+						   PointerGetDatum(digest->ctx));
+	pfree(digest);
 	pfree(h);
 }
 
@@ -177,42 +126,26 @@ px_find_digest(const char *name, PX_MD **res)
 		OpenSSL_add_all_algorithms();
 	}
 
-	if (!digest_resowner_callback_registered)
-	{
-		RegisterResourceReleaseCallback(digest_free_callback, NULL);
-		digest_resowner_callback_registered = true;
-	}
-
 	md = EVP_get_digestbyname(name);
 	if (md == NULL)
 		return PXE_NO_HASH;
 
-	/*
-	 * Create an OSSLDigest object, an OpenSSL MD object, and a PX_MD object.
-	 * The order is crucial, to make sure we don't leak anything on
-	 * out-of-memory or other error.
-	 */
-	digest = MemoryContextAlloc(TopMemoryContext, sizeof(*digest));
-
 	ctx = EVP_MD_CTX_create();
 	if (!ctx)
-	{
-		pfree(digest);
 		return -1;
-	}
+
 	if (EVP_DigestInit_ex(ctx, md, NULL) == 0)
 	{
 		EVP_MD_CTX_destroy(ctx);
-		pfree(digest);
 		return -1;
 	}
 
+	ResourceOwnerEnlargeEVP(CurrentResourceOwner);
+	ResourceOwnerRememberEVP(CurrentResourceOwner, PointerGetDatum(ctx));
+
+	digest = palloc(sizeof(*digest));
 	digest->algo = md;
 	digest->ctx = ctx;
-	digest->owner = CurrentResourceOwner;
-	digest->next = open_digests;
-	digest->prev = NULL;
-	open_digests = digest;
 
 	/* The PX_MD object is allocated in the current memory context. */
 	h = palloc(sizeof(*h));
-- 
2.29.2

Attachment: signature.asc
Description: PGP signature

Reply via email to