1 /*-------------------------------------------------------------------------
4 * Set of wrapper routines on top of OpenSSL to support cryptographic
7 * This should only be used if code is compiled with OpenSSL support.
9 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
10 * Portions Copyright (c) 1994, Regents of the University of California
13 * src/common/cryptohash_openssl.c
15 *-------------------------------------------------------------------------
21 #include "postgres_fe.h"
24 #include <openssl/err.h>
25 #include <openssl/evp.h>
27 #include "common/cryptohash.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 the backend, use an allocation in TopMemoryContext to count for
38 * resowner cleanup handling. In the frontend, use malloc to be able
39 * to return a failure status back to the caller.
42 #define ALLOC(size) MemoryContextAlloc(TopMemoryContext, size)
43 #define FREE(ptr) pfree(ptr)
45 #define ALLOC(size) malloc(size)
46 #define FREE(ptr) free(ptr)
49 /* Set of error states */
50 typedef enum pg_cryptohash_errno
52 PG_CRYPTOHASH_ERROR_NONE
= 0,
53 PG_CRYPTOHASH_ERROR_DEST_LEN
,
54 PG_CRYPTOHASH_ERROR_OPENSSL
,
55 } pg_cryptohash_errno
;
58 * Internal pg_cryptohash_ctx structure.
60 * This tracks the resource owner associated to each EVP context data
63 struct pg_cryptohash_ctx
65 pg_cryptohash_type type
;
66 pg_cryptohash_errno error
;
67 const char *errreason
;
72 ResourceOwner resowner
;
76 /* ResourceOwner callbacks to hold cryptohash contexts */
78 static void ResOwnerReleaseCryptoHash(Datum res
);
80 static const ResourceOwnerDesc cryptohash_resowner_desc
=
82 .name
= "OpenSSL cryptohash context",
83 .release_phase
= RESOURCE_RELEASE_BEFORE_LOCKS
,
84 .release_priority
= RELEASE_PRIO_CRYPTOHASH_CONTEXTS
,
85 .ReleaseResource
= ResOwnerReleaseCryptoHash
,
86 .DebugPrint
= NULL
/* the default message is fine */
89 /* Convenience wrappers over ResourceOwnerRemember/Forget */
91 ResourceOwnerRememberCryptoHash(ResourceOwner owner
, pg_cryptohash_ctx
*ctx
)
93 ResourceOwnerRemember(owner
, PointerGetDatum(ctx
), &cryptohash_resowner_desc
);
96 ResourceOwnerForgetCryptoHash(ResourceOwner owner
, pg_cryptohash_ctx
*ctx
)
98 ResourceOwnerForget(owner
, PointerGetDatum(ctx
), &cryptohash_resowner_desc
);
103 SSLerrmessage(unsigned long ecode
)
109 * This may return NULL, but we would fall back to a default error path if
110 * that were the case.
112 return ERR_reason_error_string(ecode
);
116 * pg_cryptohash_create
118 * Allocate a hash context. Returns NULL on failure for an OOM. The
119 * backend issues an error, without returning.
122 pg_cryptohash_create(pg_cryptohash_type type
)
124 pg_cryptohash_ctx
*ctx
;
127 * Make sure that the resource owner has space to remember this reference.
128 * This can error out with "out of memory", so do this before any other
129 * allocation to avoid leaking.
132 ResourceOwnerEnlarge(CurrentResourceOwner
);
135 ctx
= ALLOC(sizeof(pg_cryptohash_ctx
));
138 memset(ctx
, 0, sizeof(pg_cryptohash_ctx
));
140 ctx
->error
= PG_CRYPTOHASH_ERROR_NONE
;
141 ctx
->errreason
= NULL
;
144 * Initialization takes care of assigning the correct type for OpenSSL.
145 * Also ensure that there aren't any unconsumed errors in the queue from
149 ctx
->evpctx
= EVP_MD_CTX_create();
151 if (ctx
->evpctx
== NULL
)
153 explicit_bzero(ctx
, sizeof(pg_cryptohash_ctx
));
157 (errcode(ERRCODE_OUT_OF_MEMORY
),
158 errmsg("out of memory")));
165 ctx
->resowner
= CurrentResourceOwner
;
166 ResourceOwnerRememberCryptoHash(CurrentResourceOwner
, ctx
);
175 * Initialize a hash context. Returns 0 on success, and -1 on failure.
178 pg_cryptohash_init(pg_cryptohash_ctx
*ctx
)
188 status
= EVP_DigestInit_ex(ctx
->evpctx
, EVP_md5(), NULL
);
191 status
= EVP_DigestInit_ex(ctx
->evpctx
, EVP_sha1(), NULL
);
194 status
= EVP_DigestInit_ex(ctx
->evpctx
, EVP_sha224(), NULL
);
197 status
= EVP_DigestInit_ex(ctx
->evpctx
, EVP_sha256(), NULL
);
200 status
= EVP_DigestInit_ex(ctx
->evpctx
, EVP_sha384(), NULL
);
203 status
= EVP_DigestInit_ex(ctx
->evpctx
, EVP_sha512(), NULL
);
207 /* OpenSSL internals return 1 on success, 0 on failure */
210 ctx
->errreason
= SSLerrmessage(ERR_get_error());
211 ctx
->error
= PG_CRYPTOHASH_ERROR_OPENSSL
;
214 * The OpenSSL error queue should normally be empty since we've
215 * consumed an error, but cipher initialization can in FIPS-enabled
216 * OpenSSL builds generate two errors so clear the queue here as well.
225 * pg_cryptohash_update
227 * Update a hash context. Returns 0 on success, and -1 on failure.
230 pg_cryptohash_update(pg_cryptohash_ctx
*ctx
, const uint8
*data
, size_t len
)
237 status
= EVP_DigestUpdate(ctx
->evpctx
, data
, len
);
239 /* OpenSSL internals return 1 on success, 0 on failure */
242 ctx
->errreason
= SSLerrmessage(ERR_get_error());
243 ctx
->error
= PG_CRYPTOHASH_ERROR_OPENSSL
;
250 * pg_cryptohash_final
252 * Finalize a hash context. Returns 0 on success, and -1 on failure.
255 pg_cryptohash_final(pg_cryptohash_ctx
*ctx
, uint8
*dest
, size_t len
)
265 if (len
< MD5_DIGEST_LENGTH
)
267 ctx
->error
= PG_CRYPTOHASH_ERROR_DEST_LEN
;
272 if (len
< SHA1_DIGEST_LENGTH
)
274 ctx
->error
= PG_CRYPTOHASH_ERROR_DEST_LEN
;
279 if (len
< PG_SHA224_DIGEST_LENGTH
)
281 ctx
->error
= PG_CRYPTOHASH_ERROR_DEST_LEN
;
286 if (len
< PG_SHA256_DIGEST_LENGTH
)
288 ctx
->error
= PG_CRYPTOHASH_ERROR_DEST_LEN
;
293 if (len
< PG_SHA384_DIGEST_LENGTH
)
295 ctx
->error
= PG_CRYPTOHASH_ERROR_DEST_LEN
;
300 if (len
< PG_SHA512_DIGEST_LENGTH
)
302 ctx
->error
= PG_CRYPTOHASH_ERROR_DEST_LEN
;
308 status
= EVP_DigestFinal_ex(ctx
->evpctx
, dest
, 0);
310 /* OpenSSL internals return 1 on success, 0 on failure */
313 ctx
->errreason
= SSLerrmessage(ERR_get_error());
314 ctx
->error
= PG_CRYPTOHASH_ERROR_OPENSSL
;
323 * Free a hash context.
326 pg_cryptohash_free(pg_cryptohash_ctx
*ctx
)
331 EVP_MD_CTX_destroy(ctx
->evpctx
);
335 ResourceOwnerForgetCryptoHash(ctx
->resowner
, ctx
);
338 explicit_bzero(ctx
, sizeof(pg_cryptohash_ctx
));
343 * pg_cryptohash_error
345 * Returns a static string providing details about an error that
346 * happened during a computation.
349 pg_cryptohash_error(pg_cryptohash_ctx
*ctx
)
352 * This implementation would never fail because of an out-of-memory error,
353 * except when creating the context.
356 return _("out of memory");
359 * If a reason is provided, rely on it, else fallback to any error code
363 return ctx
->errreason
;
367 case PG_CRYPTOHASH_ERROR_NONE
:
369 case PG_CRYPTOHASH_ERROR_DEST_LEN
:
370 return _("destination buffer too small");
371 case PG_CRYPTOHASH_ERROR_OPENSSL
:
372 return _("OpenSSL failure");
375 Assert(false); /* cannot be reached */
379 /* ResourceOwner callbacks */
383 ResOwnerReleaseCryptoHash(Datum res
)
385 pg_cryptohash_ctx
*ctx
= (pg_cryptohash_ctx
*) DatumGetPointer(res
);
387 ctx
->resowner
= NULL
;
388 pg_cryptohash_free(ctx
);