--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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