Coverage Report

Created: 2026-08-13 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/common/hmac.c
Line
Count
Source
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-2026, 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
0
#define ALLOC(size) palloc(size)
35
0
#define FREE(ptr) pfree(ptr)
36
#else
37
#define ALLOC(size) malloc(size)
38
#define FREE(ptr) free(ptr)
39
#endif
40
41
/* Set of error states */
42
typedef enum pg_hmac_errno
43
{
44
  PG_HMAC_ERROR_NONE = 0,
45
  PG_HMAC_ERROR_OOM,
46
  PG_HMAC_ERROR_INTERNAL,
47
} pg_hmac_errno;
48
49
/* Internal pg_hmac_ctx structure */
50
struct pg_hmac_ctx
51
{
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;
58
59
  /*
60
   * Use the largest block size among supported options.  This wastes some
61
   * memory but simplifies the allocation logic.
62
   */
63
  uint8   k_ipad[PG_SHA512_BLOCK_LENGTH];
64
  uint8   k_opad[PG_SHA512_BLOCK_LENGTH];
65
};
66
67
0
#define HMAC_IPAD 0x36
68
0
#define HMAC_OPAD 0x5C
69
70
/*
71
 * pg_hmac_create
72
 *
73
 * Allocate a hash context.  Returns NULL on failure for an OOM.  The
74
 * backend issues an error, without returning.
75
 */
76
pg_hmac_ctx *
77
pg_hmac_create(pg_cryptohash_type type)
78
0
{
79
0
  pg_hmac_ctx *ctx;
80
81
0
  ctx = ALLOC(sizeof(pg_hmac_ctx));
82
0
  if (ctx == NULL)
83
0
    return NULL;
84
0
  memset(ctx, 0, sizeof(pg_hmac_ctx));
85
0
  ctx->type = type;
86
0
  ctx->error = PG_HMAC_ERROR_NONE;
87
0
  ctx->errreason = NULL;
88
89
  /*
90
   * Initialize the context data.  This requires to know the digest and
91
   * block lengths, that depend on the type of hash used.
92
   */
93
0
  switch (type)
94
0
  {
95
0
    case PG_MD5:
96
0
      ctx->digest_size = MD5_DIGEST_LENGTH;
97
0
      ctx->block_size = MD5_BLOCK_SIZE;
98
0
      break;
99
0
    case PG_SHA1:
100
0
      ctx->digest_size = SHA1_DIGEST_LENGTH;
101
0
      ctx->block_size = SHA1_BLOCK_SIZE;
102
0
      break;
103
0
    case PG_SHA224:
104
0
      ctx->digest_size = PG_SHA224_DIGEST_LENGTH;
105
0
      ctx->block_size = PG_SHA224_BLOCK_LENGTH;
106
0
      break;
107
0
    case PG_SHA256:
108
0
      ctx->digest_size = PG_SHA256_DIGEST_LENGTH;
109
0
      ctx->block_size = PG_SHA256_BLOCK_LENGTH;
110
0
      break;
111
0
    case PG_SHA384:
112
0
      ctx->digest_size = PG_SHA384_DIGEST_LENGTH;
113
0
      ctx->block_size = PG_SHA384_BLOCK_LENGTH;
114
0
      break;
115
0
    case PG_SHA512:
116
0
      ctx->digest_size = PG_SHA512_DIGEST_LENGTH;
117
0
      ctx->block_size = PG_SHA512_BLOCK_LENGTH;
118
0
      break;
119
0
  }
120
121
0
  ctx->hash = pg_cryptohash_create(type);
122
0
  if (ctx->hash == NULL)
123
0
  {
124
0
    explicit_bzero(ctx, sizeof(pg_hmac_ctx));
125
0
    FREE(ctx);
126
0
    return NULL;
127
0
  }
128
129
0
  return ctx;
130
0
}
131
132
/*
133
 * pg_hmac_init
134
 *
135
 * Initialize a HMAC context.  Returns 0 on success, -1 on failure.
136
 */
137
int
138
pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len)
139
0
{
140
0
  int     digest_size;
141
0
  int     block_size;
142
0
  uint8    *shrinkbuf = NULL;
143
144
0
  if (ctx == NULL)
145
0
    return -1;
146
147
0
  digest_size = ctx->digest_size;
148
0
  block_size = ctx->block_size;
149
150
0
  memset(ctx->k_opad, HMAC_OPAD, ctx->block_size);
151
0
  memset(ctx->k_ipad, HMAC_IPAD, ctx->block_size);
152
153
  /*
154
   * If the key is longer than the block size, pass it through the hash once
155
   * to shrink it down.
156
   */
157
0
  if (len > block_size)
158
0
  {
159
0
    pg_cryptohash_ctx *hash_ctx;
160
161
    /* temporary buffer for one-time shrink */
162
0
    shrinkbuf = ALLOC(digest_size);
163
0
    if (shrinkbuf == NULL)
164
0
    {
165
0
      ctx->error = PG_HMAC_ERROR_OOM;
166
0
      return -1;
167
0
    }
168
0
    memset(shrinkbuf, 0, digest_size);
169
170
0
    hash_ctx = pg_cryptohash_create(ctx->type);
171
0
    if (hash_ctx == NULL)
172
0
    {
173
0
      ctx->error = PG_HMAC_ERROR_OOM;
174
0
      FREE(shrinkbuf);
175
0
      return -1;
176
0
    }
177
178
0
    if (pg_cryptohash_init(hash_ctx) < 0 ||
179
0
      pg_cryptohash_update(hash_ctx, key, len) < 0 ||
180
0
      pg_cryptohash_final(hash_ctx, shrinkbuf, digest_size) < 0)
181
0
    {
182
0
      ctx->error = PG_HMAC_ERROR_INTERNAL;
183
0
      ctx->errreason = pg_cryptohash_error(hash_ctx);
184
0
      pg_cryptohash_free(hash_ctx);
185
0
      FREE(shrinkbuf);
186
0
      return -1;
187
0
    }
188
189
0
    key = shrinkbuf;
190
0
    len = digest_size;
191
0
    pg_cryptohash_free(hash_ctx);
192
0
  }
193
194
0
  for (size_t i = 0; i < len; i++)
195
0
  {
196
0
    ctx->k_ipad[i] ^= key[i];
197
0
    ctx->k_opad[i] ^= key[i];
198
0
  }
199
200
  /* tmp = H(K XOR ipad, text) */
201
0
  if (pg_cryptohash_init(ctx->hash) < 0 ||
202
0
    pg_cryptohash_update(ctx->hash, ctx->k_ipad, ctx->block_size) < 0)
203
0
  {
204
0
    ctx->error = PG_HMAC_ERROR_INTERNAL;
205
0
    ctx->errreason = pg_cryptohash_error(ctx->hash);
206
0
    if (shrinkbuf)
207
0
      FREE(shrinkbuf);
208
0
    return -1;
209
0
  }
210
211
0
  if (shrinkbuf)
212
0
    FREE(shrinkbuf);
213
0
  return 0;
214
0
}
215
216
/*
217
 * pg_hmac_update
218
 *
219
 * Update a HMAC context.  Returns 0 on success, -1 on failure.
220
 */
221
int
222
pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len)
223
0
{
224
0
  if (ctx == NULL)
225
0
    return -1;
226
227
0
  if (pg_cryptohash_update(ctx->hash, data, len) < 0)
228
0
  {
229
0
    ctx->error = PG_HMAC_ERROR_INTERNAL;
230
0
    ctx->errreason = pg_cryptohash_error(ctx->hash);
231
0
    return -1;
232
0
  }
233
234
0
  return 0;
235
0
}
236
237
/*
238
 * pg_hmac_final
239
 *
240
 * Finalize a HMAC context.  Returns 0 on success, -1 on failure.
241
 */
242
int
243
pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len)
244
0
{
245
0
  uint8    *h;
246
247
0
  if (ctx == NULL)
248
0
    return -1;
249
250
0
  h = ALLOC(ctx->digest_size);
251
0
  if (h == NULL)
252
0
  {
253
0
    ctx->error = PG_HMAC_ERROR_OOM;
254
0
    return -1;
255
0
  }
256
0
  memset(h, 0, ctx->digest_size);
257
258
0
  if (pg_cryptohash_final(ctx->hash, h, ctx->digest_size) < 0)
259
0
  {
260
0
    ctx->error = PG_HMAC_ERROR_INTERNAL;
261
0
    ctx->errreason = pg_cryptohash_error(ctx->hash);
262
0
    FREE(h);
263
0
    return -1;
264
0
  }
265
266
  /* H(K XOR opad, tmp) */
267
0
  if (pg_cryptohash_init(ctx->hash) < 0 ||
268
0
    pg_cryptohash_update(ctx->hash, ctx->k_opad, ctx->block_size) < 0 ||
269
0
    pg_cryptohash_update(ctx->hash, h, ctx->digest_size) < 0 ||
270
0
    pg_cryptohash_final(ctx->hash, dest, len) < 0)
271
0
  {
272
0
    ctx->error = PG_HMAC_ERROR_INTERNAL;
273
0
    ctx->errreason = pg_cryptohash_error(ctx->hash);
274
0
    FREE(h);
275
0
    return -1;
276
0
  }
277
278
0
  FREE(h);
279
0
  return 0;
280
0
}
281
282
/*
283
 * pg_hmac_free
284
 *
285
 * Free a HMAC context.
286
 */
287
void
288
pg_hmac_free(pg_hmac_ctx *ctx)
289
0
{
290
0
  if (ctx == NULL)
291
0
    return;
292
293
0
  pg_cryptohash_free(ctx->hash);
294
0
  explicit_bzero(ctx, sizeof(pg_hmac_ctx));
295
0
  FREE(ctx);
296
0
}
297
298
/*
299
 * pg_hmac_error
300
 *
301
 * Returns a static string providing details about an error that happened
302
 * during a HMAC computation.
303
 */
304
const char *
305
pg_hmac_error(pg_hmac_ctx *ctx)
306
0
{
307
0
  if (ctx == NULL)
308
0
    return _("out of memory");
309
310
  /*
311
   * If a reason is provided, rely on it, else fallback to any error code
312
   * set.
313
   */
314
0
  if (ctx->errreason)
315
0
    return ctx->errreason;
316
317
0
  switch (ctx->error)
318
0
  {
319
0
    case PG_HMAC_ERROR_NONE:
320
0
      return _("success");
321
0
    case PG_HMAC_ERROR_INTERNAL:
322
0
      return _("internal error");
323
0
    case PG_HMAC_ERROR_OOM:
324
0
      return _("out of memory");
325
0
  }
326
327
0
  Assert(false);        /* cannot be reached */
328
0
  return _("success");
329
0
}