From cafd5715ddd445bc843ebc19f2a802a87b8befe7 Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Fri, 20 Mar 2020 14:48:33 -0400
Subject: [PATCH v19 2/4] Add checksum helper functions.

Patch written from scratch by me, but it is similar to previous work
by Rushabh Lathia and Suraj Kharage. Suraj also reviewed this version
off-list. Advice on how not to break Windows from Davinder Singh.

Discussion: http://postgr.es/m/CA+TgmoZV8dw1H2bzZ9xkKwdrk8+XYa+DC9H=F7heO2zna5T6qg@mail.gmail.com
---
 src/common/Makefile                  |   1 +
 src/common/checksum_helper.c         | 190 +++++++++++++++++++++++++++
 src/include/common/checksum_helper.h |  74 +++++++++++
 src/tools/msvc/Mkvcbuild.pm          |   4 +-
 4 files changed, 267 insertions(+), 2 deletions(-)
 create mode 100644 src/common/checksum_helper.c
 create mode 100644 src/include/common/checksum_helper.h

diff --git a/src/common/Makefile b/src/common/Makefile
index a97c723fbd..d0be882cca 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -48,6 +48,7 @@ LIBS += $(PTHREAD_LIBS)
 OBJS_COMMON = \
 	archive.o \
 	base64.o \
+	checksum_helper.o \
 	config_info.o \
 	controldata_utils.o \
 	d2s.o \
diff --git a/src/common/checksum_helper.c b/src/common/checksum_helper.c
new file mode 100644
index 0000000000..79a9a7447b
--- /dev/null
+++ b/src/common/checksum_helper.c
@@ -0,0 +1,190 @@
+/*-------------------------------------------------------------------------
+ *
+ * checksum_helper.c
+ *	  Compute a checksum of any of various types using common routines
+ *
+ * Portions Copyright (c) 2016-2020, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *		  src/common/checksum_helper.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include "common/checksum_helper.h"
+
+/*
+ * If 'name' is a recognized checksum type, set *type to the corresponding
+ * constant and return true. Otherwise, set *type to CHECKSUM_TYPE_NONE and
+ * return false.
+ */
+bool
+pg_checksum_parse_type(char *name, pg_checksum_type *type)
+{
+	pg_checksum_type result_type = CHECKSUM_TYPE_NONE;
+	bool		result = true;
+
+	if (pg_strcasecmp(name, "none") == 0)
+		result_type = CHECKSUM_TYPE_NONE;
+	else if (pg_strcasecmp(name, "crc32c") == 0)
+		result_type = CHECKSUM_TYPE_CRC32C;
+	else if (pg_strcasecmp(name, "sha224") == 0)
+		result_type = CHECKSUM_TYPE_SHA224;
+	else if (pg_strcasecmp(name, "sha256") == 0)
+		result_type = CHECKSUM_TYPE_SHA256;
+	else if (pg_strcasecmp(name, "sha384") == 0)
+		result_type = CHECKSUM_TYPE_SHA384;
+	else if (pg_strcasecmp(name, "sha512") == 0)
+		result_type = CHECKSUM_TYPE_SHA512;
+	else
+		result = false;
+
+	*type = result_type;
+	return result;
+}
+
+/*
+ * Get the canonical human-readable name corresponding to a checksum type.
+ */
+char *
+pg_checksum_type_name(pg_checksum_type type)
+{
+	switch (type)
+	{
+		case CHECKSUM_TYPE_NONE:
+			return "NONE";
+		case CHECKSUM_TYPE_CRC32C:
+			return "CRC32C";
+		case CHECKSUM_TYPE_SHA224:
+			return "SHA224";
+		case CHECKSUM_TYPE_SHA256:
+			return "SHA256";
+		case CHECKSUM_TYPE_SHA384:
+			return "SHA384";
+		case CHECKSUM_TYPE_SHA512:
+			return "SHA512";
+	}
+
+	Assert(false);
+	return "???";
+}
+
+/*
+ * Initialize a checksum context for checksums of the given type.
+ */
+void
+pg_checksum_init(pg_checksum_context *context, pg_checksum_type type)
+{
+	context->type = type;
+
+	switch (type)
+	{
+		case CHECKSUM_TYPE_NONE:
+			/* do nothing */
+			break;
+		case CHECKSUM_TYPE_CRC32C:
+			INIT_CRC32C(context->raw_context.c_crc32c);
+			break;
+		case CHECKSUM_TYPE_SHA224:
+			pg_sha224_init(&context->raw_context.c_sha224);
+			break;
+		case CHECKSUM_TYPE_SHA256:
+			pg_sha256_init(&context->raw_context.c_sha256);
+			break;
+		case CHECKSUM_TYPE_SHA384:
+			pg_sha384_init(&context->raw_context.c_sha384);
+			break;
+		case CHECKSUM_TYPE_SHA512:
+			pg_sha512_init(&context->raw_context.c_sha512);
+			break;
+	}
+}
+
+/*
+ * Update a checksum context with new data.
+ */
+void
+pg_checksum_update(pg_checksum_context *context, const uint8 *input,
+				   size_t len)
+{
+	switch (context->type)
+	{
+		case CHECKSUM_TYPE_NONE:
+			/* do nothing */
+			break;
+		case CHECKSUM_TYPE_CRC32C:
+			COMP_CRC32C(context->raw_context.c_crc32c, input, len);
+			break;
+		case CHECKSUM_TYPE_SHA224:
+			pg_sha224_update(&context->raw_context.c_sha224, input, len);
+			break;
+		case CHECKSUM_TYPE_SHA256:
+			pg_sha256_update(&context->raw_context.c_sha256, input, len);
+			break;
+		case CHECKSUM_TYPE_SHA384:
+			pg_sha384_update(&context->raw_context.c_sha384, input, len);
+			break;
+		case CHECKSUM_TYPE_SHA512:
+			pg_sha512_update(&context->raw_context.c_sha512, input, len);
+			break;
+	}
+}
+
+/*
+ * 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.
+ */
+int
+pg_checksum_final(pg_checksum_context *context, uint8 *output)
+{
+	int			retval = 0;
+
+	StaticAssertStmt(sizeof(pg_crc32c) <= PG_CHECKSUM_MAX_LENGTH,
+					 "CRC-32C digest too big for PG_CHECKSUM_MAX_LENGTH");
+	StaticAssertStmt(PG_SHA224_DIGEST_LENGTH <= PG_CHECKSUM_MAX_LENGTH,
+					 "SHA224 digest too for PG_CHECKSUM_MAX_LENGTH");
+	StaticAssertStmt(PG_SHA256_DIGEST_LENGTH <= PG_CHECKSUM_MAX_LENGTH,
+					 "SHA256 digest too for PG_CHECKSUM_MAX_LENGTH");
+	StaticAssertStmt(PG_SHA384_DIGEST_LENGTH <= PG_CHECKSUM_MAX_LENGTH,
+					 "SHA384 digest too for PG_CHECKSUM_MAX_LENGTH");
+	StaticAssertStmt(PG_SHA512_DIGEST_LENGTH <= PG_CHECKSUM_MAX_LENGTH,
+					 "SHA512 digest too for PG_CHECKSUM_MAX_LENGTH");
+
+	switch (context->type)
+	{
+		case CHECKSUM_TYPE_NONE:
+			break;
+		case CHECKSUM_TYPE_CRC32C:
+			FIN_CRC32C(context->raw_context.c_crc32c);
+			retval = sizeof(pg_crc32c);
+			memcpy(output, &context->raw_context.c_crc32c, retval);
+			break;
+		case CHECKSUM_TYPE_SHA224:
+			pg_sha224_final(&context->raw_context.c_sha224, output);
+			retval = PG_SHA224_DIGEST_LENGTH;
+			break;
+		case CHECKSUM_TYPE_SHA256:
+			pg_sha256_final(&context->raw_context.c_sha256, output);
+			retval = PG_SHA256_DIGEST_LENGTH;
+			break;
+		case CHECKSUM_TYPE_SHA384:
+			pg_sha384_final(&context->raw_context.c_sha384, output);
+			retval = PG_SHA384_DIGEST_LENGTH;
+			break;
+		case CHECKSUM_TYPE_SHA512:
+			pg_sha512_final(&context->raw_context.c_sha512, output);
+			retval = PG_SHA512_DIGEST_LENGTH;
+			break;
+	}
+
+	Assert(retval <= PG_CHECKSUM_MAX_LENGTH);
+	return retval;
+}
diff --git a/src/include/common/checksum_helper.h b/src/include/common/checksum_helper.h
new file mode 100644
index 0000000000..48b0745dad
--- /dev/null
+++ b/src/include/common/checksum_helper.h
@@ -0,0 +1,74 @@
+/*-------------------------------------------------------------------------
+ *
+ * checksum_helper.h
+ *	  Compute a checksum of any of various types using common routines
+ *
+ * Portions Copyright (c) 2016-2020, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *		  src/include/common/checksum_helper.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef CHECKSUM_HELPER_H
+#define CHECKSUM_HELPER_H
+
+#include "common/sha2.h"
+#include "port/pg_crc32c.h"
+
+/*
+ * Supported checksum types. It's not necessarily the case that code using
+ * these functions needs a cryptographically strong checksum; it may only
+ * need to detect accidental modification. That's why we include CRC-32C: it's
+ * much faster than any of the other algorithms. On the other hand, we omit
+ * MD5 here because any new that does need a cryptographically strong checksum
+ * should use something better.
+ */
+typedef enum pg_checksum_type
+{
+	CHECKSUM_TYPE_NONE,
+	CHECKSUM_TYPE_CRC32C,
+	CHECKSUM_TYPE_SHA224,
+	CHECKSUM_TYPE_SHA256,
+	CHECKSUM_TYPE_SHA384,
+	CHECKSUM_TYPE_SHA512
+} pg_checksum_type;
+
+/*
+ * This is just a union of all applicable context types.
+ */
+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_checksum_raw_context;
+
+/*
+ * This structure provides a convenient way to pass the checksum type and the
+ * checksum context around together.
+ */
+typedef struct pg_checksum_context
+{
+	pg_checksum_type type;
+	pg_checksum_raw_context raw_context;
+} pg_checksum_context;
+
+/*
+ * This is the longest possible output for any checksum algorithm supported
+ * by this file.
+ */
+#define PG_CHECKSUM_MAX_LENGTH		PG_SHA512_DIGEST_LENGTH
+
+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,
+							   size_t len);
+extern int	pg_checksum_final(pg_checksum_context *, uint8 *output);
+
+#endif
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 5c88825f49..72a21dbd41 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -120,8 +120,8 @@ sub mkvcbuild
 	}
 
 	our @pgcommonallfiles = qw(
-	  archive.c
-	  base64.c config_info.c controldata_utils.c d2s.c encnames.c exec.c
+	  archive.c base64.c checksum_helper.c
+      config_info.c controldata_utils.c d2s.c encnames.c exec.c
 	  f2s.c file_perm.c hashfn.c ip.c jsonapi.c
 	  keywords.c kwlookup.c link-canary.c md5.c
 	  pg_lzcompress.c pgfnames.c psprintf.c relpath.c rmtree.c
-- 
2.17.2 (Apple Git-113)

