1 /*-------------------------------------------------------------------------
2  *
3  * hmac.c
4  *	  Implements Keyed-Hashing for Message Authentication (HMAC)
5  *
6  * Fallback implementation of HMAC, as specified in RFC 2104.
7  *
8  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
9  * Portions Copyright (c) 1994, Regents of the University of California
10  *
11  * IDENTIFICATION
12  *	  src/common/hmac.c
13  *
14  *-------------------------------------------------------------------------
15  */
16 
17 #ifndef FRONTEND
18 #include "postgres.h"
19 #else
20 #include "postgres_fe.h"
21 #endif
22 
23 #include "common/cryptohash.h"
24 #include "common/hmac.h"
25 #include "common/md5.h"
26 #include "common/sha1.h"
27 #include "common/sha2.h"
28 
29 /*
30  * In backend, use palloc/pfree to ease the error handling.  In frontend,
31  * use malloc to be able to return a failure status back to the caller.
32  */
33 #ifndef FRONTEND
34 #define ALLOC(size) palloc(size)
35 #define FREE(ptr) pfree(ptr)
36 #else
37 #define ALLOC(size) malloc(size)
38 #define FREE(ptr) free(ptr)
39 #endif
40 
41 /*
42  * Internal structure for pg_hmac_ctx->data with this implementation.
43  */
44 struct pg_hmac_ctx
45 {
46 	pg_cryptohash_ctx *hash;
47 	pg_cryptohash_type type;
48 	int			block_size;
49 	int			digest_size;
50 
51 	/*
52 	 * Use the largest block size among supported options.  This wastes some
53 	 * memory but simplifies the allocation logic.
54 	 */
55 	uint8		k_ipad[PG_SHA512_BLOCK_LENGTH];
56 	uint8		k_opad[PG_SHA512_BLOCK_LENGTH];
57 };
58 
59 #define HMAC_IPAD 0x36
60 #define HMAC_OPAD 0x5C
61 
62 /*
63  * pg_hmac_create
64  *
65  * Allocate a hash context.  Returns NULL on failure for an OOM.  The
66  * backend issues an error, without returning.
67  */
68 pg_hmac_ctx *
pg_hmac_create(pg_cryptohash_type type)69 pg_hmac_create(pg_cryptohash_type type)
70 {
71 	pg_hmac_ctx *ctx;
72 
73 	ctx = ALLOC(sizeof(pg_hmac_ctx));
74 	if (ctx == NULL)
75 		return NULL;
76 	memset(ctx, 0, sizeof(pg_hmac_ctx));
77 	ctx->type = type;
78 
79 	/*
80 	 * Initialize the context data.  This requires to know the digest and
81 	 * block lengths, that depend on the type of hash used.
82 	 */
83 	switch (type)
84 	{
85 		case PG_MD5:
86 			ctx->digest_size = MD5_DIGEST_LENGTH;
87 			ctx->block_size = MD5_BLOCK_SIZE;
88 			break;
89 		case PG_SHA1:
90 			ctx->digest_size = SHA1_DIGEST_LENGTH;
91 			ctx->block_size = SHA1_BLOCK_SIZE;
92 			break;
93 		case PG_SHA224:
94 			ctx->digest_size = PG_SHA224_DIGEST_LENGTH;
95 			ctx->block_size = PG_SHA224_BLOCK_LENGTH;
96 			break;
97 		case PG_SHA256:
98 			ctx->digest_size = PG_SHA256_DIGEST_LENGTH;
99 			ctx->block_size = PG_SHA256_BLOCK_LENGTH;
100 			break;
101 		case PG_SHA384:
102 			ctx->digest_size = PG_SHA384_DIGEST_LENGTH;
103 			ctx->block_size = PG_SHA384_BLOCK_LENGTH;
104 			break;
105 		case PG_SHA512:
106 			ctx->digest_size = PG_SHA512_DIGEST_LENGTH;
107 			ctx->block_size = PG_SHA512_BLOCK_LENGTH;
108 			break;
109 	}
110 
111 	ctx->hash = pg_cryptohash_create(type);
112 	if (ctx->hash == NULL)
113 	{
114 		explicit_bzero(ctx, sizeof(pg_hmac_ctx));
115 		FREE(ctx);
116 		return NULL;
117 	}
118 
119 	return ctx;
120 }
121 
122 /*
123  * pg_hmac_init
124  *
125  * Initialize a HMAC context.  Returns 0 on success, -1 on failure.
126  */
127 int
pg_hmac_init(pg_hmac_ctx * ctx,const uint8 * key,size_t len)128 pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len)
129 {
130 	int			i;
131 	int			digest_size;
132 	int			block_size;
133 	uint8	   *shrinkbuf = NULL;
134 
135 	if (ctx == NULL)
136 		return -1;
137 
138 	digest_size = ctx->digest_size;
139 	block_size = ctx->block_size;
140 
141 	memset(ctx->k_opad, HMAC_OPAD, ctx->block_size);
142 	memset(ctx->k_ipad, HMAC_IPAD, ctx->block_size);
143 
144 	/*
145 	 * If the key is longer than the block size, pass it through the hash once
146 	 * to shrink it down.
147 	 */
148 	if (len > block_size)
149 	{
150 		pg_cryptohash_ctx *hash_ctx;
151 
152 		/* temporary buffer for one-time shrink */
153 		shrinkbuf = ALLOC(digest_size);
154 		if (shrinkbuf == NULL)
155 			return -1;
156 		memset(shrinkbuf, 0, digest_size);
157 
158 		hash_ctx = pg_cryptohash_create(ctx->type);
159 		if (hash_ctx == NULL)
160 		{
161 			FREE(shrinkbuf);
162 			return -1;
163 		}
164 
165 		if (pg_cryptohash_init(hash_ctx) < 0 ||
166 			pg_cryptohash_update(hash_ctx, key, len) < 0 ||
167 			pg_cryptohash_final(hash_ctx, shrinkbuf, digest_size) < 0)
168 		{
169 			pg_cryptohash_free(hash_ctx);
170 			FREE(shrinkbuf);
171 			return -1;
172 		}
173 
174 		key = shrinkbuf;
175 		len = digest_size;
176 		pg_cryptohash_free(hash_ctx);
177 	}
178 
179 	for (i = 0; i < len; i++)
180 	{
181 		ctx->k_ipad[i] ^= key[i];
182 		ctx->k_opad[i] ^= key[i];
183 	}
184 
185 	/* tmp = H(K XOR ipad, text) */
186 	if (pg_cryptohash_init(ctx->hash) < 0 ||
187 		pg_cryptohash_update(ctx->hash, ctx->k_ipad, ctx->block_size) < 0)
188 	{
189 		if (shrinkbuf)
190 			FREE(shrinkbuf);
191 		return -1;
192 	}
193 
194 	if (shrinkbuf)
195 		FREE(shrinkbuf);
196 	return 0;
197 }
198 
199 /*
200  * pg_hmac_update
201  *
202  * Update a HMAC context.  Returns 0 on success, -1 on failure.
203  */
204 int
pg_hmac_update(pg_hmac_ctx * ctx,const uint8 * data,size_t len)205 pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len)
206 {
207 	if (ctx == NULL)
208 		return -1;
209 
210 	if (pg_cryptohash_update(ctx->hash, data, len) < 0)
211 		return -1;
212 
213 	return 0;
214 }
215 
216 /*
217  * pg_hmac_final
218  *
219  * Finalize a HMAC context.  Returns 0 on success, -1 on failure.
220  */
221 int
pg_hmac_final(pg_hmac_ctx * ctx,uint8 * dest,size_t len)222 pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len)
223 {
224 	uint8	   *h;
225 
226 	if (ctx == NULL)
227 		return -1;
228 
229 	h = ALLOC(ctx->digest_size);
230 	if (h == NULL)
231 		return -1;
232 	memset(h, 0, ctx->digest_size);
233 
234 	if (pg_cryptohash_final(ctx->hash, h, ctx->digest_size) < 0)
235 	{
236 		FREE(h);
237 		return -1;
238 	}
239 
240 	/* H(K XOR opad, tmp) */
241 	if (pg_cryptohash_init(ctx->hash) < 0 ||
242 		pg_cryptohash_update(ctx->hash, ctx->k_opad, ctx->block_size) < 0 ||
243 		pg_cryptohash_update(ctx->hash, h, ctx->digest_size) < 0 ||
244 		pg_cryptohash_final(ctx->hash, dest, len) < 0)
245 	{
246 		FREE(h);
247 		return -1;
248 	}
249 
250 	FREE(h);
251 	return 0;
252 }
253 
254 /*
255  * pg_hmac_free
256  *
257  * Free a HMAC context.
258  */
259 void
pg_hmac_free(pg_hmac_ctx * ctx)260 pg_hmac_free(pg_hmac_ctx *ctx)
261 {
262 	if (ctx == NULL)
263 		return;
264 
265 	pg_cryptohash_free(ctx->hash);
266 	explicit_bzero(ctx, sizeof(pg_hmac_ctx));
267 	FREE(ctx);
268 }
269