From b4ec42146bfe8c9580c31d32a619e7712c519486 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Tue, 24 Nov 2020 19:36:13 +0900
Subject: [PATCH v5 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                     |  89 +-------
 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                           |   6 +-
 src/common/checksum_helper.c                  |  79 +++++--
 src/common/cryptohash.c                       | 189 +++++++++++++++++
 src/common/cryptohash_openssl.c               | 196 ++++++++++++++++++
 src/common/scram-common.c                     | 165 ++++++++++-----
 src/common/sha2.c                             |  23 +-
 .../common/sha2.h => common/sha2_int.h}       |  38 +---
 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                   |   3 +-
 src/tools/pgindent/typedefs.list              |   1 +
 23 files changed, 928 insertions(+), 573 deletions(-)
 create mode 100644 src/include/common/cryptohash.h
 create mode 100644 src/common/cryptohash.c
 create mode 100644 src/common/cryptohash_openssl.c
 copy src/{include/common/sha2.h => common/sha2_int.h} (73%)
 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..0e4a6631a3
--- /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..c8b9096043 100644
--- a/src/include/common/sha2.h
+++ b/src/include/common/sha2.h
@@ -1,9 +1,10 @@
 /*-------------------------------------------------------------------------
  *
  * sha2.h
- *	  Generic headers for SHA224, 256, 384 AND 512 functions of PostgreSQL.
+ *	  Constants related to SHA224, 256, 384 AND 512.
  *
- * 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
@@ -11,49 +12,9 @@
  *-------------------------------------------------------------------------
  */
 
-/* $OpenBSD: sha2.h,v 1.2 2004/04/28 23:11:57 millert Exp $ */
-
-/*
- * FILE:	sha2.h
- * AUTHOR:	Aaron D. Gifford <me@aarongifford.com>
- *
- * Copyright (c) 2000-2001, Aaron D. Gifford
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *	  notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *	  notice, this list of conditions and the following disclaimer in the
- *	  documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holder nor the names of contributors
- *	  may be used to endorse or promote products derived from this software
- *	  without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $From: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
- */
-
 #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 +29,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..b8f5187282 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -82,9 +82,11 @@ 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 \
+	sha2.o
 endif
 
 # A few files are currently only built for frontend, not server
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..f548497538
--- /dev/null
+++ b/src/common/cryptohash.c
@@ -0,0 +1,189 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 "sha2_int.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:
+			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..1a462accc5 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,9 +57,19 @@
 #include "postgres_fe.h"
 #endif
 
-#include <sys/param.h>
+#include "sha2_int.h"
 
-#include "common/sha2.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
 
 /*
  * UNROLLED TRANSFORM LOOP NOTE:
diff --git a/src/include/common/sha2.h b/src/common/sha2_int.h
similarity index 73%
copy from src/include/common/sha2.h
copy to src/common/sha2_int.h
index 9c4abf777d..96db773f96 100644
--- a/src/include/common/sha2.h
+++ b/src/common/sha2_int.h
@@ -1,12 +1,12 @@
 /*-------------------------------------------------------------------------
  *
- * sha2.h
- *	  Generic headers for SHA224, 256, 384 AND 512 functions of PostgreSQL.
+ * sha2_int.h
+ *	  Internal headers for fallback implementation of SHA{224,256,384,512}
  *
  * Portions Copyright (c) 2016-2020, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *		  src/include/common/sha2.h
+ *		  src/common/sha2_int.h
  *
  *-------------------------------------------------------------------------
  */
@@ -47,34 +47,11 @@
  * $From: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
  */
 
-#ifndef _PG_SHA2_H_
-#define _PG_SHA2_H_
+#ifndef PG_SHA2_INT_H
+#define PG_SHA2_INT_H
 
-#ifdef USE_OPENSSL
-#include <openssl/sha.h>
-#endif
+#include "common/sha2.h"
 
-/*** SHA224/256/384/512 Various Length Definitions ***********************/
-#define PG_SHA224_BLOCK_LENGTH			64
-#define PG_SHA224_DIGEST_LENGTH			28
-#define PG_SHA224_DIGEST_STRING_LENGTH	(PG_SHA224_DIGEST_LENGTH * 2 + 1)
-#define PG_SHA256_BLOCK_LENGTH			64
-#define PG_SHA256_DIGEST_LENGTH			32
-#define PG_SHA256_DIGEST_STRING_LENGTH	(PG_SHA256_DIGEST_LENGTH * 2 + 1)
-#define PG_SHA384_BLOCK_LENGTH			128
-#define PG_SHA384_DIGEST_LENGTH			48
-#define PG_SHA384_DIGEST_STRING_LENGTH	(PG_SHA384_DIGEST_LENGTH * 2 + 1)
-#define PG_SHA512_BLOCK_LENGTH			128
-#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];
@@ -89,7 +66,6 @@ typedef struct pg_sha512_ctx
 } 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);
@@ -112,4 +88,4 @@ 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_ */
+#endif							/* PG_SHA2_INT_H */
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..720b55142b 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -129,11 +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, 'cryptohash.c');
 		push(@pgcommonallfiles, 'sha2.c');
 	}
 
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index fde701bfd4..9c959e126d 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3180,6 +3180,7 @@ pg_conn_host_type
 pg_conv_map
 pg_crc32
 pg_crc32c
+pg_cryptohash_ctx
 pg_ctype_cache
 pg_enc
 pg_enc2gettext
-- 
2.29.2

