1 /*-------------------------------------------------------------------------
4 * Implementation of HMAC with OpenSSL.
6 * This should only be used if code is compiled with OpenSSL support.
8 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
9 * Portions Copyright (c) 1994, Regents of the University of California
12 * src/common/hmac_openssl.c
14 *-------------------------------------------------------------------------
20 #include "postgres_fe.h"
24 #include <openssl/err.h>
25 #include <openssl/hmac.h>
27 #include "common/hmac.h"
28 #include "common/md5.h"
29 #include "common/sha1.h"
30 #include "common/sha2.h"
32 #include "utils/memutils.h"
33 #include "utils/resowner.h"
37 * In backend, use an allocation in TopMemoryContext to count for resowner
38 * cleanup handling if necessary. In frontend, use malloc to be able to return
39 * a failure status back to the caller.
42 #define USE_RESOWNER_FOR_HMAC
43 #define ALLOC(size) MemoryContextAlloc(TopMemoryContext, size)
44 #define FREE(ptr) pfree(ptr)
46 #define ALLOC(size) malloc(size)
47 #define FREE(ptr) free(ptr)
50 /* Set of error states */
51 typedef enum pg_hmac_errno
53 PG_HMAC_ERROR_NONE
= 0,
54 PG_HMAC_ERROR_DEST_LEN
,
55 PG_HMAC_ERROR_OPENSSL
,
58 /* Internal pg_hmac_ctx structure */
62 pg_cryptohash_type type
;
64 const char *errreason
;
66 #ifdef USE_RESOWNER_FOR_HMAC
67 ResourceOwner resowner
;
71 /* ResourceOwner callbacks to hold HMAC contexts */
72 #ifdef USE_RESOWNER_FOR_HMAC
73 static void ResOwnerReleaseHMAC(Datum res
);
75 static const ResourceOwnerDesc hmac_resowner_desc
=
77 .name
= "OpenSSL HMAC context",
78 .release_phase
= RESOURCE_RELEASE_BEFORE_LOCKS
,
79 .release_priority
= RELEASE_PRIO_HMAC_CONTEXTS
,
80 .ReleaseResource
= ResOwnerReleaseHMAC
,
81 .DebugPrint
= NULL
/* the default message is fine */
84 /* Convenience wrappers over ResourceOwnerRemember/Forget */
86 ResourceOwnerRememberHMAC(ResourceOwner owner
, pg_hmac_ctx
*ctx
)
88 ResourceOwnerRemember(owner
, PointerGetDatum(ctx
), &hmac_resowner_desc
);
91 ResourceOwnerForgetHMAC(ResourceOwner owner
, pg_hmac_ctx
*ctx
)
93 ResourceOwnerForget(owner
, PointerGetDatum(ctx
), &hmac_resowner_desc
);
98 SSLerrmessage(unsigned long ecode
)
104 * This may return NULL, but we would fall back to a default error path if
105 * that were the case.
107 return ERR_reason_error_string(ecode
);
113 * Allocate a hash context. Returns NULL on failure for an OOM. The
114 * backend issues an error, without returning.
117 pg_hmac_create(pg_cryptohash_type type
)
121 ctx
= ALLOC(sizeof(pg_hmac_ctx
));
124 memset(ctx
, 0, sizeof(pg_hmac_ctx
));
127 ctx
->error
= PG_HMAC_ERROR_NONE
;
128 ctx
->errreason
= NULL
;
132 * Initialization takes care of assigning the correct type for OpenSSL.
133 * Also ensure that there aren't any unconsumed errors in the queue from
138 #ifdef USE_RESOWNER_FOR_HMAC
139 ResourceOwnerEnlarge(CurrentResourceOwner
);
142 ctx
->hmacctx
= HMAC_CTX_new();
144 if (ctx
->hmacctx
== NULL
)
146 explicit_bzero(ctx
, sizeof(pg_hmac_ctx
));
150 (errcode(ERRCODE_OUT_OF_MEMORY
),
151 errmsg("out of memory")));
157 #ifdef USE_RESOWNER_FOR_HMAC
158 ctx
->resowner
= CurrentResourceOwner
;
159 ResourceOwnerRememberHMAC(CurrentResourceOwner
, ctx
);
168 * Initialize a HMAC context. Returns 0 on success, -1 on failure.
171 pg_hmac_init(pg_hmac_ctx
*ctx
, const uint8
*key
, size_t len
)
181 status
= HMAC_Init_ex(ctx
->hmacctx
, key
, len
, EVP_md5(), NULL
);
184 status
= HMAC_Init_ex(ctx
->hmacctx
, key
, len
, EVP_sha1(), NULL
);
187 status
= HMAC_Init_ex(ctx
->hmacctx
, key
, len
, EVP_sha224(), NULL
);
190 status
= HMAC_Init_ex(ctx
->hmacctx
, key
, len
, EVP_sha256(), NULL
);
193 status
= HMAC_Init_ex(ctx
->hmacctx
, key
, len
, EVP_sha384(), NULL
);
196 status
= HMAC_Init_ex(ctx
->hmacctx
, key
, len
, EVP_sha512(), NULL
);
200 /* OpenSSL internals return 1 on success, 0 on failure */
203 ctx
->errreason
= SSLerrmessage(ERR_get_error());
204 ctx
->error
= PG_HMAC_ERROR_OPENSSL
;
214 * Update a HMAC context. Returns 0 on success, -1 on failure.
217 pg_hmac_update(pg_hmac_ctx
*ctx
, const uint8
*data
, size_t len
)
224 status
= HMAC_Update(ctx
->hmacctx
, data
, len
);
226 /* OpenSSL internals return 1 on success, 0 on failure */
229 ctx
->errreason
= SSLerrmessage(ERR_get_error());
230 ctx
->error
= PG_HMAC_ERROR_OPENSSL
;
239 * Finalize a HMAC context. Returns 0 on success, -1 on failure.
242 pg_hmac_final(pg_hmac_ctx
*ctx
, uint8
*dest
, size_t len
)
253 if (len
< MD5_DIGEST_LENGTH
)
255 ctx
->error
= PG_HMAC_ERROR_DEST_LEN
;
260 if (len
< SHA1_DIGEST_LENGTH
)
262 ctx
->error
= PG_HMAC_ERROR_DEST_LEN
;
267 if (len
< PG_SHA224_DIGEST_LENGTH
)
269 ctx
->error
= PG_HMAC_ERROR_DEST_LEN
;
274 if (len
< PG_SHA256_DIGEST_LENGTH
)
276 ctx
->error
= PG_HMAC_ERROR_DEST_LEN
;
281 if (len
< PG_SHA384_DIGEST_LENGTH
)
283 ctx
->error
= PG_HMAC_ERROR_DEST_LEN
;
288 if (len
< PG_SHA512_DIGEST_LENGTH
)
290 ctx
->error
= PG_HMAC_ERROR_DEST_LEN
;
296 status
= HMAC_Final(ctx
->hmacctx
, dest
, &outlen
);
298 /* OpenSSL internals return 1 on success, 0 on failure */
301 ctx
->errreason
= SSLerrmessage(ERR_get_error());
302 ctx
->error
= PG_HMAC_ERROR_OPENSSL
;
311 * Free a HMAC context.
314 pg_hmac_free(pg_hmac_ctx
*ctx
)
319 HMAC_CTX_free(ctx
->hmacctx
);
320 #ifdef USE_RESOWNER_FOR_HMAC
322 ResourceOwnerForgetHMAC(ctx
->resowner
, ctx
);
325 explicit_bzero(ctx
, sizeof(pg_hmac_ctx
));
332 * Returns a static string providing details about an error that happened
333 * during a HMAC computation.
336 pg_hmac_error(pg_hmac_ctx
*ctx
)
339 return _("out of memory");
342 * If a reason is provided, rely on it, else fallback to any error code
346 return ctx
->errreason
;
350 case PG_HMAC_ERROR_NONE
:
352 case PG_HMAC_ERROR_DEST_LEN
:
353 return _("destination buffer too small");
354 case PG_HMAC_ERROR_OPENSSL
:
355 return _("OpenSSL failure");
358 Assert(false); /* cannot be reached */
362 /* ResourceOwner callbacks */
364 #ifdef USE_RESOWNER_FOR_HMAC
366 ResOwnerReleaseHMAC(Datum res
)
368 pg_hmac_ctx
*ctx
= (pg_hmac_ctx
*) DatumGetPointer(res
);
370 ctx
->resowner
= NULL
;