1 /*-------------------------------------------------------------------------
2  *
3  * checksum_helper.c
4  *	  Compute a checksum of any of various types using common routines
5  *
6  * Portions Copyright (c) 2016-2020, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  *		  src/common/checksum_helper.c
10  *
11  *-------------------------------------------------------------------------
12  */
13 
14 #ifndef FRONTEND
15 #include "postgres.h"
16 #else
17 #include "postgres_fe.h"
18 #endif
19 
20 #include "common/checksum_helper.h"
21 
22 /*
23  * If 'name' is a recognized checksum type, set *type to the corresponding
24  * constant and return true. Otherwise, set *type to CHECKSUM_TYPE_NONE and
25  * return false.
26  */
27 bool
pg_checksum_parse_type(char * name,pg_checksum_type * type)28 pg_checksum_parse_type(char *name, pg_checksum_type *type)
29 {
30 	pg_checksum_type result_type = CHECKSUM_TYPE_NONE;
31 	bool		result = true;
32 
33 	if (pg_strcasecmp(name, "none") == 0)
34 		result_type = CHECKSUM_TYPE_NONE;
35 	else if (pg_strcasecmp(name, "crc32c") == 0)
36 		result_type = CHECKSUM_TYPE_CRC32C;
37 	else if (pg_strcasecmp(name, "sha224") == 0)
38 		result_type = CHECKSUM_TYPE_SHA224;
39 	else if (pg_strcasecmp(name, "sha256") == 0)
40 		result_type = CHECKSUM_TYPE_SHA256;
41 	else if (pg_strcasecmp(name, "sha384") == 0)
42 		result_type = CHECKSUM_TYPE_SHA384;
43 	else if (pg_strcasecmp(name, "sha512") == 0)
44 		result_type = CHECKSUM_TYPE_SHA512;
45 	else
46 		result = false;
47 
48 	*type = result_type;
49 	return result;
50 }
51 
52 /*
53  * Get the canonical human-readable name corresponding to a checksum type.
54  */
55 char *
pg_checksum_type_name(pg_checksum_type type)56 pg_checksum_type_name(pg_checksum_type type)
57 {
58 	switch (type)
59 	{
60 		case CHECKSUM_TYPE_NONE:
61 			return "NONE";
62 		case CHECKSUM_TYPE_CRC32C:
63 			return "CRC32C";
64 		case CHECKSUM_TYPE_SHA224:
65 			return "SHA224";
66 		case CHECKSUM_TYPE_SHA256:
67 			return "SHA256";
68 		case CHECKSUM_TYPE_SHA384:
69 			return "SHA384";
70 		case CHECKSUM_TYPE_SHA512:
71 			return "SHA512";
72 	}
73 
74 	Assert(false);
75 	return "???";
76 }
77 
78 /*
79  * Initialize a checksum context for checksums of the given type.
80  */
81 void
pg_checksum_init(pg_checksum_context * context,pg_checksum_type type)82 pg_checksum_init(pg_checksum_context *context, pg_checksum_type type)
83 {
84 	context->type = type;
85 
86 	switch (type)
87 	{
88 		case CHECKSUM_TYPE_NONE:
89 			/* do nothing */
90 			break;
91 		case CHECKSUM_TYPE_CRC32C:
92 			INIT_CRC32C(context->raw_context.c_crc32c);
93 			break;
94 		case CHECKSUM_TYPE_SHA224:
95 			pg_sha224_init(&context->raw_context.c_sha224);
96 			break;
97 		case CHECKSUM_TYPE_SHA256:
98 			pg_sha256_init(&context->raw_context.c_sha256);
99 			break;
100 		case CHECKSUM_TYPE_SHA384:
101 			pg_sha384_init(&context->raw_context.c_sha384);
102 			break;
103 		case CHECKSUM_TYPE_SHA512:
104 			pg_sha512_init(&context->raw_context.c_sha512);
105 			break;
106 	}
107 }
108 
109 /*
110  * Update a checksum context with new data.
111  */
112 void
pg_checksum_update(pg_checksum_context * context,const uint8 * input,size_t len)113 pg_checksum_update(pg_checksum_context *context, const uint8 *input,
114 				   size_t len)
115 {
116 	switch (context->type)
117 	{
118 		case CHECKSUM_TYPE_NONE:
119 			/* do nothing */
120 			break;
121 		case CHECKSUM_TYPE_CRC32C:
122 			COMP_CRC32C(context->raw_context.c_crc32c, input, len);
123 			break;
124 		case CHECKSUM_TYPE_SHA224:
125 			pg_sha224_update(&context->raw_context.c_sha224, input, len);
126 			break;
127 		case CHECKSUM_TYPE_SHA256:
128 			pg_sha256_update(&context->raw_context.c_sha256, input, len);
129 			break;
130 		case CHECKSUM_TYPE_SHA384:
131 			pg_sha384_update(&context->raw_context.c_sha384, input, len);
132 			break;
133 		case CHECKSUM_TYPE_SHA512:
134 			pg_sha512_update(&context->raw_context.c_sha512, input, len);
135 			break;
136 	}
137 }
138 
139 /*
140  * Finalize a checksum computation and write the result to an output buffer.
141  *
142  * The caller must ensure that the buffer is at least PG_CHECKSUM_MAX_LENGTH
143  * bytes in length. The return value is the number of bytes actually written.
144  */
145 int
pg_checksum_final(pg_checksum_context * context,uint8 * output)146 pg_checksum_final(pg_checksum_context *context, uint8 *output)
147 {
148 	int			retval = 0;
149 
150 	StaticAssertStmt(sizeof(pg_crc32c) <= PG_CHECKSUM_MAX_LENGTH,
151 					 "CRC-32C digest too big for PG_CHECKSUM_MAX_LENGTH");
152 	StaticAssertStmt(PG_SHA224_DIGEST_LENGTH <= PG_CHECKSUM_MAX_LENGTH,
153 					 "SHA224 digest too for PG_CHECKSUM_MAX_LENGTH");
154 	StaticAssertStmt(PG_SHA256_DIGEST_LENGTH <= PG_CHECKSUM_MAX_LENGTH,
155 					 "SHA256 digest too for PG_CHECKSUM_MAX_LENGTH");
156 	StaticAssertStmt(PG_SHA384_DIGEST_LENGTH <= PG_CHECKSUM_MAX_LENGTH,
157 					 "SHA384 digest too for PG_CHECKSUM_MAX_LENGTH");
158 	StaticAssertStmt(PG_SHA512_DIGEST_LENGTH <= PG_CHECKSUM_MAX_LENGTH,
159 					 "SHA512 digest too for PG_CHECKSUM_MAX_LENGTH");
160 
161 	switch (context->type)
162 	{
163 		case CHECKSUM_TYPE_NONE:
164 			break;
165 		case CHECKSUM_TYPE_CRC32C:
166 			FIN_CRC32C(context->raw_context.c_crc32c);
167 			retval = sizeof(pg_crc32c);
168 			memcpy(output, &context->raw_context.c_crc32c, retval);
169 			break;
170 		case CHECKSUM_TYPE_SHA224:
171 			pg_sha224_final(&context->raw_context.c_sha224, output);
172 			retval = PG_SHA224_DIGEST_LENGTH;
173 			break;
174 		case CHECKSUM_TYPE_SHA256:
175 			pg_sha256_final(&context->raw_context.c_sha256, output);
176 			retval = PG_SHA256_DIGEST_LENGTH;
177 			break;
178 		case CHECKSUM_TYPE_SHA384:
179 			pg_sha384_final(&context->raw_context.c_sha384, output);
180 			retval = PG_SHA384_DIGEST_LENGTH;
181 			break;
182 		case CHECKSUM_TYPE_SHA512:
183 			pg_sha512_final(&context->raw_context.c_sha512, output);
184 			retval = PG_SHA512_DIGEST_LENGTH;
185 			break;
186 	}
187 
188 	Assert(retval <= PG_CHECKSUM_MAX_LENGTH);
189 	return retval;
190 }
191