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