From c6a22d622aabb47f5a4153777271f2258a592c45 Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Wed, 5 Feb 2020 13:59:45 -0500 Subject: [PATCH v9 1/5] 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. (In my version, I decided to support all of the SHA variants for which we have code, added a function to translate values of the enum type back to strings so that users of this code don't need to do that, used different naming, and tried to be more careful amount formatting and comments.) --- 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 ce01df68b9..e199ed7acb 100644 --- a/src/common/Makefile +++ b/src/common/Makefile @@ -47,6 +47,7 @@ LIBS += $(PTHREAD_LIBS) OBJS_COMMON = \ 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 ec25042933..358e923a28 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -119,8 +119,8 @@ sub mkvcbuild } our @pgcommonallfiles = qw( - base64.c config_info.c controldata_utils.c d2s.c encnames.c exec.c - f2s.c file_perm.c hashfn.c ip.c jsonapi.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 saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c -- 2.17.2 (Apple Git-113)