1 /*-------------------------------------------------------------------------
4 * Implements Keyed-Hashing for Message Authentication (HMAC)
6 * Fallback implementation of HMAC, as specified in RFC 2104.
8 * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
9 * Portions Copyright (c) 1994, Regents of the University of California
14 *-------------------------------------------------------------------------
20 #include "postgres_fe.h"
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.
34 #define ALLOC(size) palloc(size)
35 #define FREE(ptr) pfree(ptr)
37 #define ALLOC(size) malloc(size)
38 #define FREE(ptr) free(ptr)
41 /* Set of error states */
42 typedef enum pg_hmac_errno
44 PG_HMAC_ERROR_NONE
= 0,
46 PG_HMAC_ERROR_INTERNAL
,
49 /* Internal pg_hmac_ctx structure */
52 pg_cryptohash_ctx
*hash
;
53 pg_cryptohash_type type
;
55 const char *errreason
;
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
73 * Allocate a hash context. Returns NULL on failure for an OOM. The
74 * backend issues an error, without returning.
77 pg_hmac_create(pg_cryptohash_type type
)
81 ctx
= ALLOC(sizeof(pg_hmac_ctx
));
84 memset(ctx
, 0, sizeof(pg_hmac_ctx
));
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.
96 ctx
->digest_size
= MD5_DIGEST_LENGTH
;
97 ctx
->block_size
= MD5_BLOCK_SIZE
;
100 ctx
->digest_size
= SHA1_DIGEST_LENGTH
;
101 ctx
->block_size
= SHA1_BLOCK_SIZE
;
104 ctx
->digest_size
= PG_SHA224_DIGEST_LENGTH
;
105 ctx
->block_size
= PG_SHA224_BLOCK_LENGTH
;
108 ctx
->digest_size
= PG_SHA256_DIGEST_LENGTH
;
109 ctx
->block_size
= PG_SHA256_BLOCK_LENGTH
;
112 ctx
->digest_size
= PG_SHA384_DIGEST_LENGTH
;
113 ctx
->block_size
= PG_SHA384_BLOCK_LENGTH
;
116 ctx
->digest_size
= PG_SHA512_DIGEST_LENGTH
;
117 ctx
->block_size
= PG_SHA512_BLOCK_LENGTH
;
121 ctx
->hash
= pg_cryptohash_create(type
);
122 if (ctx
->hash
== NULL
)
124 explicit_bzero(ctx
, sizeof(pg_hmac_ctx
));
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
)
143 uint8
*shrinkbuf
= NULL
;
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
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
;
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
;
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
);
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
);
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
)
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
);
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
)
251 h
= ALLOC(ctx
->digest_size
);
254 ctx
->error
= PG_HMAC_ERROR_OOM
;
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
);
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
);
286 * Free a HMAC context.
289 pg_hmac_free(pg_hmac_ctx
*ctx
)
294 pg_cryptohash_free(ctx
->hash
);
295 explicit_bzero(ctx
, sizeof(pg_hmac_ctx
));
302 * Returns a static string providing details about an error that happened
303 * during a HMAC computation.
306 pg_hmac_error(pg_hmac_ctx
*ctx
)
309 return _("out of memory");
312 * If a reason is provided, rely on it, else fallback to any error code
316 return ctx
->errreason
;
320 case PG_HMAC_ERROR_NONE
:
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 */