doc PG 17 relnotes: add FETCH_COUNT item
[pgsql.git] / src / common / hmac.c
blob508b2fcd9326d27f74a072f2c8d31d4c0dfff6c3
1 /*-------------------------------------------------------------------------
3 * hmac.c
4 * Implements Keyed-Hashing for Message Authentication (HMAC)
6 * Fallback implementation of HMAC, as specified in RFC 2104.
8 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
9 * Portions Copyright (c) 1994, Regents of the University of California
11 * IDENTIFICATION
12 * src/common/hmac.c
14 *-------------------------------------------------------------------------
17 #ifndef FRONTEND
18 #include "postgres.h"
19 #else
20 #include "postgres_fe.h"
21 #endif
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"
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.
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
41 /* Set of error states */
42 typedef enum pg_hmac_errno
44 PG_HMAC_ERROR_NONE = 0,
45 PG_HMAC_ERROR_OOM,
46 PG_HMAC_ERROR_INTERNAL,
47 } pg_hmac_errno;
49 /* Internal pg_hmac_ctx structure */
50 struct pg_hmac_ctx
52 pg_cryptohash_ctx *hash;
53 pg_cryptohash_type type;
54 pg_hmac_errno error;
55 const char *errreason;
56 int block_size;
57 int digest_size;
60 * Use the largest block size among supported options. This wastes some
61 * memory but simplifies the allocation logic.
63 uint8 k_ipad[PG_SHA512_BLOCK_LENGTH];
64 uint8 k_opad[PG_SHA512_BLOCK_LENGTH];
67 #define HMAC_IPAD 0x36
68 #define HMAC_OPAD 0x5C
71 * pg_hmac_create
73 * Allocate a hash context. Returns NULL on failure for an OOM. The
74 * backend issues an error, without returning.
76 pg_hmac_ctx *
77 pg_hmac_create(pg_cryptohash_type type)
79 pg_hmac_ctx *ctx;
81 ctx = ALLOC(sizeof(pg_hmac_ctx));
82 if (ctx == NULL)
83 return NULL;
84 memset(ctx, 0, sizeof(pg_hmac_ctx));
85 ctx->type = type;
86 ctx->error = PG_HMAC_ERROR_NONE;
87 ctx->errreason = NULL;
90 * Initialize the context data. This requires to know the digest and
91 * block lengths, that depend on the type of hash used.
93 switch (type)
95 case PG_MD5:
96 ctx->digest_size = MD5_DIGEST_LENGTH;
97 ctx->block_size = MD5_BLOCK_SIZE;
98 break;
99 case PG_SHA1:
100 ctx->digest_size = SHA1_DIGEST_LENGTH;
101 ctx->block_size = SHA1_BLOCK_SIZE;
102 break;
103 case PG_SHA224:
104 ctx->digest_size = PG_SHA224_DIGEST_LENGTH;
105 ctx->block_size = PG_SHA224_BLOCK_LENGTH;
106 break;
107 case PG_SHA256:
108 ctx->digest_size = PG_SHA256_DIGEST_LENGTH;
109 ctx->block_size = PG_SHA256_BLOCK_LENGTH;
110 break;
111 case PG_SHA384:
112 ctx->digest_size = PG_SHA384_DIGEST_LENGTH;
113 ctx->block_size = PG_SHA384_BLOCK_LENGTH;
114 break;
115 case PG_SHA512:
116 ctx->digest_size = PG_SHA512_DIGEST_LENGTH;
117 ctx->block_size = PG_SHA512_BLOCK_LENGTH;
118 break;
121 ctx->hash = pg_cryptohash_create(type);
122 if (ctx->hash == NULL)
124 explicit_bzero(ctx, sizeof(pg_hmac_ctx));
125 FREE(ctx);
126 return NULL;
129 return ctx;
133 * pg_hmac_init
135 * Initialize a HMAC context. Returns 0 on success, -1 on failure.
138 pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len)
140 int i;
141 int digest_size;
142 int block_size;
143 uint8 *shrinkbuf = NULL;
145 if (ctx == NULL)
146 return -1;
148 digest_size = ctx->digest_size;
149 block_size = ctx->block_size;
151 memset(ctx->k_opad, HMAC_OPAD, ctx->block_size);
152 memset(ctx->k_ipad, HMAC_IPAD, ctx->block_size);
155 * If the key is longer than the block size, pass it through the hash once
156 * to shrink it down.
158 if (len > block_size)
160 pg_cryptohash_ctx *hash_ctx;
162 /* temporary buffer for one-time shrink */
163 shrinkbuf = ALLOC(digest_size);
164 if (shrinkbuf == NULL)
166 ctx->error = PG_HMAC_ERROR_OOM;
167 return -1;
169 memset(shrinkbuf, 0, digest_size);
171 hash_ctx = pg_cryptohash_create(ctx->type);
172 if (hash_ctx == NULL)
174 ctx->error = PG_HMAC_ERROR_OOM;
175 FREE(shrinkbuf);
176 return -1;
179 if (pg_cryptohash_init(hash_ctx) < 0 ||
180 pg_cryptohash_update(hash_ctx, key, len) < 0 ||
181 pg_cryptohash_final(hash_ctx, shrinkbuf, digest_size) < 0)
183 ctx->error = PG_HMAC_ERROR_INTERNAL;
184 ctx->errreason = pg_cryptohash_error(hash_ctx);
185 pg_cryptohash_free(hash_ctx);
186 FREE(shrinkbuf);
187 return -1;
190 key = shrinkbuf;
191 len = digest_size;
192 pg_cryptohash_free(hash_ctx);
195 for (i = 0; i < len; i++)
197 ctx->k_ipad[i] ^= key[i];
198 ctx->k_opad[i] ^= key[i];
201 /* tmp = H(K XOR ipad, text) */
202 if (pg_cryptohash_init(ctx->hash) < 0 ||
203 pg_cryptohash_update(ctx->hash, ctx->k_ipad, ctx->block_size) < 0)
205 ctx->error = PG_HMAC_ERROR_INTERNAL;
206 ctx->errreason = pg_cryptohash_error(ctx->hash);
207 if (shrinkbuf)
208 FREE(shrinkbuf);
209 return -1;
212 if (shrinkbuf)
213 FREE(shrinkbuf);
214 return 0;
218 * pg_hmac_update
220 * Update a HMAC context. Returns 0 on success, -1 on failure.
223 pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len)
225 if (ctx == NULL)
226 return -1;
228 if (pg_cryptohash_update(ctx->hash, data, len) < 0)
230 ctx->error = PG_HMAC_ERROR_INTERNAL;
231 ctx->errreason = pg_cryptohash_error(ctx->hash);
232 return -1;
235 return 0;
239 * pg_hmac_final
241 * Finalize a HMAC context. Returns 0 on success, -1 on failure.
244 pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len)
246 uint8 *h;
248 if (ctx == NULL)
249 return -1;
251 h = ALLOC(ctx->digest_size);
252 if (h == NULL)
254 ctx->error = PG_HMAC_ERROR_OOM;
255 return -1;
257 memset(h, 0, ctx->digest_size);
259 if (pg_cryptohash_final(ctx->hash, h, ctx->digest_size) < 0)
261 ctx->error = PG_HMAC_ERROR_INTERNAL;
262 ctx->errreason = pg_cryptohash_error(ctx->hash);
263 FREE(h);
264 return -1;
267 /* H(K XOR opad, tmp) */
268 if (pg_cryptohash_init(ctx->hash) < 0 ||
269 pg_cryptohash_update(ctx->hash, ctx->k_opad, ctx->block_size) < 0 ||
270 pg_cryptohash_update(ctx->hash, h, ctx->digest_size) < 0 ||
271 pg_cryptohash_final(ctx->hash, dest, len) < 0)
273 ctx->error = PG_HMAC_ERROR_INTERNAL;
274 ctx->errreason = pg_cryptohash_error(ctx->hash);
275 FREE(h);
276 return -1;
279 FREE(h);
280 return 0;
284 * pg_hmac_free
286 * Free a HMAC context.
288 void
289 pg_hmac_free(pg_hmac_ctx *ctx)
291 if (ctx == NULL)
292 return;
294 pg_cryptohash_free(ctx->hash);
295 explicit_bzero(ctx, sizeof(pg_hmac_ctx));
296 FREE(ctx);
300 * pg_hmac_error
302 * Returns a static string providing details about an error that happened
303 * during a HMAC computation.
305 const char *
306 pg_hmac_error(pg_hmac_ctx *ctx)
308 if (ctx == NULL)
309 return _("out of memory");
312 * If a reason is provided, rely on it, else fallback to any error code
313 * set.
315 if (ctx->errreason)
316 return ctx->errreason;
318 switch (ctx->error)
320 case PG_HMAC_ERROR_NONE:
321 return _("success");
322 case PG_HMAC_ERROR_INTERNAL:
323 return _("internal error");
324 case PG_HMAC_ERROR_OOM:
325 return _("out of memory");
328 Assert(false); /* cannot be reached */
329 return _("success");