diff --git a/Misc/NEWS.d/next/Library/2025-06-23-13-02-08.gh-issue-134531.yUmj07.rst b/Misc/NEWS.d/next/Library/2025-06-23-13-02-08.gh-issue-134531.yUmj07.rst new file mode 100644 index 00000000000000..032cf12e06f785 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-06-23-13-02-08.gh-issue-134531.yUmj07.rst @@ -0,0 +1,3 @@ +:mod:`hmac`: use the :manpage:`EVP_MAC(3ssl)` interface for HMAC when Python +is built with OpenSSL 3.0 and later instead of the deprecated +:manpage:`HMAC_CTX(3ssl) ` interface. Patch by Bénédikt Tran. diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index d79e4b360e95c5..833750ad662bab 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -24,24 +24,31 @@ #include "Python.h" #include "pycore_hashtable.h" -#include "pycore_strhex.h" // _Py_strhex() -#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_PTR_RELAXED +#include "pycore_strhex.h" // _Py_strhex() +#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_PTR_RELAXED #include "hashlib.h" +#include +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +# define Py_HAS_OPENSSL3_SUPPORT +#endif + +#include /* EVP is the preferred interface to hashing in OpenSSL */ #include -#include -#include // FIPS_mode() +#include // FIPS_mode() /* We use the object interface to discover what hashes OpenSSL supports. */ #include -#include -#include - -#if OPENSSL_VERSION_NUMBER >= 0x30000000L -# define Py_HAS_OPENSSL3_SUPPORT +#ifdef Py_HAS_OPENSSL3_SUPPORT +# include // OSSL_MAC_PARAM_DIGEST +# include // OSSL_PARAM_*() +#else +# include // HMAC() #endif +#include + #ifndef OPENSSL_THREADS # error "OPENSSL_THREADS is not defined, Python requires thread-safe OpenSSL" #endif @@ -65,6 +72,10 @@ #define PY_EVP_MD_free(md) EVP_MD_free(md) #define PY_EVP_MD_CTX_md(CTX) EVP_MD_CTX_get0_md(CTX) + +#define Py_HMAC_CTX_TYPE EVP_MAC_CTX +#define PY_HMAC_CTX_free EVP_MAC_CTX_free +#define PY_HMAC_update EVP_MAC_update #else #define PY_EVP_MD const EVP_MD #define PY_EVP_MD_fetch(algorithm, properties) EVP_get_digestbyname(algorithm) @@ -72,6 +83,10 @@ #define PY_EVP_MD_free(md) do {} while(0) #define PY_EVP_MD_CTX_md(CTX) EVP_MD_CTX_md(CTX) + +#define Py_HMAC_CTX_TYPE HMAC_CTX +#define PY_HMAC_CTX_free HMAC_CTX_free +#define PY_HMAC_update HMAC_Update #endif /* @@ -283,6 +298,9 @@ typedef struct { PyObject *constructs; PyObject *unsupported_digestmod_error; _Py_hashtable_t *hashtable; +#ifdef Py_HAS_OPENSSL3_SUPPORT + EVP_MAC *evp_hmac; +#endif } _hashlibstate; static inline _hashlibstate* @@ -304,7 +322,12 @@ typedef struct { typedef struct { HASHLIB_OBJECT_HEAD - HMAC_CTX *ctx; /* OpenSSL hmac context */ +#ifdef Py_HAS_OPENSSL3_SUPPORT + EVP_MAC_CTX *ctx; /* OpenSSL HMAC EVP-based context */ + int evp_md_nid; /* needed to find the message digest name */ +#else + HMAC_CTX *ctx; /* OpenSSL HMAC plain context */ +#endif } HMACobject; #define HMACobject_CAST(op) ((HMACobject *)(op)) @@ -617,6 +640,20 @@ get_hashlib_utf8name_by_nid(int nid) return e ? e->py_name : get_asn1_utf8name_by_nid(nid); } +#ifdef Py_HAS_OPENSSL3_SUPPORT +/* + * Convert the NID to an OpenSSL "canonical" cached, SN_* or LN_* digest name. + * + * On error, set an exception and return NULL. + */ +static const char * +get_openssl_utf8name_by_nid(int nid) +{ + const py_hashentry_t *e = get_hashentry_by_nid(nid); + return e ? e->ossl_name : get_asn1_utf8name_by_nid(nid); +} +#endif + /* Same as get_hashlib_utf8name_by_nid() but using an EVP_MD object. */ static const char * get_hashlib_utf8name_by_evp_md(const EVP_MD *md) @@ -712,7 +749,8 @@ get_openssl_evp_md_by_utf8name(_hashlibstate *state, const char *name, * py_ht The message digest purpose. */ static PY_EVP_MD * -get_openssl_evp_md(_hashlibstate *state, PyObject *digestmod, Py_hash_type py_ht) +get_openssl_evp_md(_hashlibstate *state, + PyObject *digestmod, Py_hash_type py_ht) { const char *name; if (PyUnicode_Check(digestmod)) { @@ -733,6 +771,44 @@ get_openssl_evp_md(_hashlibstate *state, PyObject *digestmod, Py_hash_type py_ht return get_openssl_evp_md_by_utf8name(state, name, py_ht); } +#ifdef Py_HAS_OPENSSL3_SUPPORT +/* + * Get the "canonical" name of an EVP_MD described by 'digestmod' and purpose. + * + * On error, set an exception and return NULL. + * + * This function should not be used to construct the exposed Python name, + * but rather to invoke OpenSSL EVP_* functions. + */ +static const char * +get_openssl_digest_name(_hashlibstate *state, + PyObject *digestmod, Py_hash_type py_ht, + EVP_MD **evp_md) +{ + PY_EVP_MD *md = get_openssl_evp_md(state, digestmod, py_ht); + if (md == NULL) { + return NULL; + } + int nid = EVP_MD_nid(md); + const char *name = get_openssl_utf8name_by_nid(nid); + if (name == NULL) { + if (evp_md != NULL) { + *evp_md = NULL; + } + PY_EVP_MD_free(md); + raise_unsupported_algorithm_error(state, digestmod); + return NULL; + } + if (evp_md != NULL) { + *evp_md = md; + } + else { + PY_EVP_MD_free(md); + } + return name; +} +#endif + // --- OpenSSL HASH wrappers -------------------------------------------------- /* Thin wrapper around EVP_MD_CTX_new() which sets an exception on failure. */ @@ -1748,9 +1824,27 @@ _hashlib_scrypt_impl(PyObject *module, Py_buffer *password, Py_buffer *salt, #undef HASHLIB_SCRYPT_MAX_DKLEN #undef HASHLIB_SCRYPT_MAX_MAXMEM -/* Fast HMAC for hmac.digest() +// --- OpenSSL HMAC interface ------------------------------------------------- + +/* + * Functions prefixed by hashlib_openssl_HMAC_* are wrappers around OpenSSL + * and implement "atomic" operations (e.g., "free"). These functions are used + * by those prefixed by _hashlib_HMAC_* that are methods for HMAC objects, or + * other (local) helper functions prefixed by hashlib_HMAC_*. */ +#ifdef Py_HAS_OPENSSL3_SUPPORT +/* EVP_MAC_CTX array of parameters specifying the "digest" */ +#define HASHLIB_HMAC_OSSL_PARAMS(DIGEST) \ + (const OSSL_PARAM []) { \ + OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, \ + (char *)DIGEST, strlen(DIGEST)), \ + OSSL_PARAM_END \ + } +#endif + +// --- One-shot HMAC interface ------------------------------------------------ + /*[clinic input] _hashlib.hmac_digest as _hashlib_hmac_singleshot @@ -1767,30 +1861,54 @@ _hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key, /*[clinic end generated code: output=82f19965d12706ac input=0a0790cc3db45c2e]*/ { _hashlibstate *state = get_hashlib_state(module); + const void *result; unsigned char md[EVP_MAX_MD_SIZE] = {0}; +#ifdef Py_HAS_OPENSSL3_SUPPORT + size_t md_len = 0; + const char *digest_name = NULL; +#else unsigned int md_len = 0; - unsigned char *result; - PY_EVP_MD *evp; +#endif int is_xof; + PY_EVP_MD *evp = NULL; if (key->len > INT_MAX) { - PyErr_SetString(PyExc_OverflowError, - "key is too long."); + PyErr_SetString(PyExc_OverflowError, "key is too long."); return NULL; } if (msg->len > INT_MAX) { - PyErr_SetString(PyExc_OverflowError, - "msg is too long."); + PyErr_SetString(PyExc_OverflowError, "msg is too long."); + return NULL; + } + +#ifdef Py_HAS_OPENSSL3_SUPPORT + digest_name = get_openssl_digest_name(state, digest, Py_ht_mac, &evp); + if (digest_name == NULL) { + assert(evp == NULL); return NULL; } + assert(evp != NULL); + is_xof = PY_EVP_MD_xof(evp); + Py_BEGIN_ALLOW_THREADS + result = EVP_Q_mac( + NULL, OSSL_MAC_NAME_HMAC, NULL, NULL, + HASHLIB_HMAC_OSSL_PARAMS(digest_name), + (const void *)key->buf, (size_t)key->len, + (const unsigned char *)msg->buf, (size_t)msg->len, + md, sizeof(md), &md_len + ); + Py_END_ALLOW_THREADS + PY_EVP_MD_free(evp); + assert(md_len < (size_t)PY_SSIZE_T_MAX); +#else evp = get_openssl_evp_md(state, digest, Py_ht_mac); if (evp == NULL) { return NULL; } + is_xof = PY_EVP_MD_xof(evp); Py_BEGIN_ALLOW_THREADS - is_xof = PY_EVP_MD_xof(evp); result = HMAC( evp, (const void *)key->buf, (int)key->len, @@ -1799,23 +1917,27 @@ _hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key, ); Py_END_ALLOW_THREADS PY_EVP_MD_free(evp); - +#endif if (result == NULL) { if (is_xof) { /* use a better default error message if an XOF is used */ raise_unsupported_algorithm_error(state, digest); } else { +#ifdef Py_HAS_OPENSSL3_SUPPORT + notify_ssl_error_occurred_in(Py_STRINGIFY(EVP_Q_mac)); +#else notify_ssl_error_occurred_in(Py_STRINGIFY(HMAC)); +#endif } return NULL; } return PyBytes_FromStringAndSize((const char*)md, md_len); } -/* OpenSSL-based HMAC implementation - */ +// --- HMAC Object ------------------------------------------------------------ +#ifndef Py_HAS_OPENSSL3_SUPPORT /* Thin wrapper around HMAC_CTX_new() which sets an exception on failure. */ static HMAC_CTX * py_openssl_wrapper_HMAC_CTX_new(void) @@ -1828,17 +1950,187 @@ py_openssl_wrapper_HMAC_CTX_new(void) return ctx; } -static int _hmac_update(HMACobject*, PyObject*); - static const EVP_MD * -_hashlib_hmac_get_md(HMACobject *self) +hashlib_openssl_HMAC_evp_md_borrowed(HMACobject *self) { + assert(self->ctx != NULL); const EVP_MD *md = HMAC_CTX_get_md(self->ctx); if (md == NULL) { notify_ssl_error_occurred("missing EVP_MD for HMAC context"); } return md; } +#endif + +static const char * +hashlib_HMAC_get_hashlib_digest_name(HMACobject *self) +{ +#ifdef Py_HAS_OPENSSL3_SUPPORT + return get_hashlib_utf8name_by_nid(self->evp_md_nid); +#else + const EVP_MD *md = hashlib_openssl_HMAC_evp_md_borrowed(self); + return md == NULL ? NULL : get_hashlib_utf8name_by_evp_md(md); +#endif +} + +static int +hashlib_openssl_HMAC_update_once(Py_HMAC_CTX_TYPE *ctx, const Py_buffer *v) +{ + if (!PY_HMAC_update(ctx, (const unsigned char *)v->buf, (size_t)v->len)) { + notify_smart_ssl_error_occurred_in(Py_STRINGIFY(PY_HMAC_update)); + return -1; + } + return 0; +} + +/* Thin wrapper around PY_HMAC_CTX_free that allows to pass a NULL 'ctx'. */ +static inline void +hashlib_openssl_HMAC_CTX_free(Py_HMAC_CTX_TYPE *ctx) +{ + /* The NULL check was not present in every OpenSSL versions. */ + if (ctx) { + PY_HMAC_CTX_free(ctx); + } +} + +static int +hashlib_openssl_HMAC_update_with_lock(HMACobject *self, PyObject *data) +{ + int r; + Py_buffer view = {0}; + GET_BUFFER_VIEW_OR_ERROR(data, &view, return -1); + HASHLIB_EXTERNAL_INSTRUCTIONS_LOCKED( + self, view.len, + r = hashlib_openssl_HMAC_update_once(self->ctx, &view) + ); + PyBuffer_Release(&view); + return r; +} + +static Py_HMAC_CTX_TYPE * +hashlib_openssl_HMAC_ctx_copy_with_lock(HMACobject *self) +{ + Py_HMAC_CTX_TYPE *ctx = NULL; +#ifdef Py_HAS_OPENSSL3_SUPPORT + HASHLIB_ACQUIRE_LOCK(self); + ctx = EVP_MAC_CTX_dup(self->ctx); + HASHLIB_RELEASE_LOCK(self); + if (ctx == NULL) { + notify_smart_ssl_error_occurred_in(Py_STRINGIFY(EVP_MAC_CTX_dup)); + goto error; + } +#else + int r; + ctx = py_openssl_wrapper_HMAC_CTX_new(); + if (ctx == NULL) { + return NULL; + } + HASHLIB_ACQUIRE_LOCK(self); + r = HMAC_CTX_copy(ctx, self->ctx); + HASHLIB_RELEASE_LOCK(self); + if (r == 0) { + notify_smart_ssl_error_occurred_in(Py_STRINGIFY(HMAC_CTX_copy)); + goto error; + } +#endif + return ctx; + +error: + hashlib_openssl_HMAC_CTX_free(ctx); + return NULL; +} + +static Py_HMAC_CTX_TYPE * +hashlib_HMAC_CTX_new_from_digestmod(_hashlibstate *state, + Py_buffer *key, PyObject *digestmod, + int *nid) +{ + Py_HMAC_CTX_TYPE *ctx = NULL; + PY_EVP_MD *md = NULL; + int is_xof, r; +#ifdef Py_HAS_OPENSSL3_SUPPORT + const char *digest = NULL; +#endif + +#ifdef Py_HAS_OPENSSL3_SUPPORT + /* + * OpenSSL 3.0 does not provide a way to extract the NID from an EVP_MAC + * object and does not expose the underlying digest name. The reason is + * that OpenSSL 3.0 treats HMAC objects as being the "same", differing + * only by their *context* parameters. While it is *required* to set + * the digest name when constructing EVP_MAC_CTX objects, that name + * is unfortunately not recoverable through EVP_MAC_CTX_get_params(). + * + * On the other hand, the (deprecated) interface based on HMAC_CTX is + * based on EVP_MD, which allows to treat HMAC objects as if they were + * hash functions when querying the digest name. + * + * Since HMAC objects are constructed from DIGESTMOD values and since + * we have a way to map DIGESTMOD to EVP_MD objects, and then to NIDs, + * HMAC objects based on EVP_MAC will store the NID of the EVP_MD we + * used to deduce the digest name to pass to EVP_MAC_CTX_set_params(). + */ + assert(nid != NULL); + digest = get_openssl_digest_name(state, digestmod, Py_ht_mac, &md); + assert((digest == NULL && md == NULL) || (digest != NULL && md != NULL)); + if (digest == NULL) { + return NULL; + } + *nid = EVP_MD_nid(md); + is_xof = PY_EVP_MD_xof(md); + PY_EVP_MD_free(md); + + /* + * OpenSSL is responsible for managing the EVP_MAC object's ref. count + * by calling EVP_MAC_up_ref() and EVP_MAC_free() in EVP_MAC_CTX_new() + * and EVP_MAC_CTX_free() respectively. + */ + ctx = EVP_MAC_CTX_new(state->evp_hmac); + if (ctx == NULL) { + /* EVP_MAC_CTX_new() may also set an ERR_R_EVP_LIB error */ + notify_smart_ssl_error_occurred_in(Py_STRINGIFY(EVP_MAC_CTX_new)); + return NULL; + } + + r = EVP_MAC_init( + ctx, + (const unsigned char *)key->buf, + (size_t)key->len, + HASHLIB_HMAC_OSSL_PARAMS(digest) + ); +#else + assert(nid == NULL); + md = get_openssl_evp_md(state, digestmod, Py_ht_mac); + if (md == NULL) { + return NULL; + } + is_xof = PY_EVP_MD_xof(md); + + ctx = py_openssl_wrapper_HMAC_CTX_new(); + if (ctx == NULL) { + PY_EVP_MD_free(md); + return NULL; + } + + r = HMAC_Init_ex(ctx, key->buf, (int)key->len, md, NULL /* impl */); + PY_EVP_MD_free(md); +#endif + if (r == 0) { + if (is_xof) { + /* use a better default error message if an XOF is used */ + raise_unsupported_algorithm_error(state, digestmod); + } + else { +#ifdef Py_HAS_OPENSSL3_SUPPORT + notify_ssl_error_occurred_in(Py_STRINGIFY(EVP_MAC_init)); +#else + notify_ssl_error_occurred_in(Py_STRINGIFY(HMAC_Init_ex)); +#endif + } + return NULL; + } + return ctx; +} /*[clinic input] _hashlib.hmac_new @@ -1856,10 +2148,11 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj, /*[clinic end generated code: output=c20d9e4d9ed6d219 input=5f4071dcc7f34362]*/ { _hashlibstate *state = get_hashlib_state(module); - PY_EVP_MD *digest; - HMAC_CTX *ctx = NULL; HMACobject *self = NULL; - int is_xof, r; + Py_HMAC_CTX_TYPE *ctx = NULL; +#ifdef Py_HAS_OPENSSL3_SUPPORT + int nid; +#endif if (key->len > INT_MAX) { PyErr_SetString(PyExc_OverflowError, @@ -1873,29 +2166,15 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj, return NULL; } - digest = get_openssl_evp_md(state, digestmod, Py_ht_mac); - if (digest == NULL) { - return NULL; - } +#ifdef Py_HAS_OPENSSL3_SUPPORT + ctx = hashlib_HMAC_CTX_new_from_digestmod(state, key, digestmod, &nid); +#else + ctx = hashlib_HMAC_CTX_new_from_digestmod(state, key, digestmod, NULL); +#endif - ctx = py_openssl_wrapper_HMAC_CTX_new(); if (ctx == NULL) { - PY_EVP_MD_free(digest); - goto error; - } - - is_xof = PY_EVP_MD_xof(digest); - r = HMAC_Init_ex(ctx, key->buf, (int)key->len, digest, NULL /* impl */); - PY_EVP_MD_free(digest); - if (r == 0) { - if (is_xof) { - /* use a better default error message if an XOF is used */ - raise_unsupported_algorithm_error(state, digestmod); - } - else { - notify_ssl_error_occurred_in(Py_STRINGIFY(HMAC_Init_ex)); - } - goto error; + assert(PyErr_Occurred()); + return NULL; } self = PyObject_New(HMACobject, state->HMAC_type); @@ -1905,74 +2184,26 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj, self->ctx = ctx; ctx = NULL; // 'ctx' is now owned by 'self' +#ifdef Py_HAS_OPENSSL3_SUPPORT + assert(nid != NID_undef); + self->evp_md_nid = nid; +#endif HASHLIB_INIT_MUTEX(self); + /* feed initial data */ if ((msg_obj != NULL) && (msg_obj != Py_None)) { - if (!_hmac_update(self, msg_obj)) { + if (hashlib_openssl_HMAC_update_with_lock(self, msg_obj) < 0) { goto error; } } return (PyObject *)self; error: - if (ctx) HMAC_CTX_free(ctx); + hashlib_openssl_HMAC_CTX_free(ctx); Py_XDECREF(self); return NULL; } -/* helper functions */ -static int -locked_HMAC_CTX_copy(HMAC_CTX *new_ctx_p, HMACobject *self) -{ - int result; - HASHLIB_ACQUIRE_LOCK(self); - result = HMAC_CTX_copy(new_ctx_p, self->ctx); - HASHLIB_RELEASE_LOCK(self); - if (result == 0) { - notify_smart_ssl_error_occurred_in(Py_STRINGIFY(HMAC_CTX_copy)); - return -1; - } - return 0; -} - -/* returning 0 means that an error occurred and an exception is set */ -static unsigned int -_hashlib_hmac_digest_size(HMACobject *self) -{ - const EVP_MD *md = _hashlib_hmac_get_md(self); - if (md == NULL) { - return 0; - } - unsigned int digest_size = EVP_MD_size(md); - assert(digest_size <= EVP_MAX_MD_SIZE); - if (digest_size == 0) { - notify_ssl_error_occurred("invalid digest size"); - } - return digest_size; -} - -static int -_hmac_update(HMACobject *self, PyObject *obj) -{ - int r; - Py_buffer view = {0}; - - GET_BUFFER_VIEW_OR_ERROR(obj, &view, return 0); - HASHLIB_EXTERNAL_INSTRUCTIONS_LOCKED( - self, view.len, - r = HMAC_Update( - self->ctx, (const unsigned char *)view.buf, (size_t)view.len - ) - ); - PyBuffer_Release(&view); - - if (r == 0) { - notify_ssl_error_occurred_in(Py_STRINGIFY(HMAC_Update)); - return 0; - } - return 1; -} - /*[clinic input] _hashlib.HMAC.copy @@ -1984,34 +2215,27 @@ _hashlib_HMAC_copy_impl(HMACobject *self) /*[clinic end generated code: output=29aa28b452833127 input=e2fa6a05db61a4d6]*/ { HMACobject *retval; - - HMAC_CTX *ctx = py_openssl_wrapper_HMAC_CTX_new(); + Py_HMAC_CTX_TYPE *ctx = hashlib_openssl_HMAC_ctx_copy_with_lock(self); if (ctx == NULL) { return NULL; } - if (locked_HMAC_CTX_copy(ctx, self) < 0) { - HMAC_CTX_free(ctx); - return NULL; - } - retval = PyObject_New(HMACobject, Py_TYPE(self)); if (retval == NULL) { - HMAC_CTX_free(ctx); + PY_HMAC_CTX_free(ctx); return NULL; } retval->ctx = ctx; HASHLIB_INIT_MUTEX(retval); - return (PyObject *)retval; } static void -_hmac_dealloc(PyObject *op) +_hashlib_HMAC_dealloc(PyObject *op) { HMACobject *self = HMACobject_CAST(op); PyTypeObject *tp = Py_TYPE(self); if (self->ctx != NULL) { - HMAC_CTX_free(self->ctx); + PY_HMAC_CTX_free(self->ctx); self->ctx = NULL; } PyObject_Free(self); @@ -2019,12 +2243,10 @@ _hmac_dealloc(PyObject *op) } static PyObject * -_hmac_repr(PyObject *op) +_hashlib_HMAC_repr(PyObject *op) { - const char *digest_name; HMACobject *self = HMACobject_CAST(op); - const EVP_MD *md = _hashlib_hmac_get_md(self); - digest_name = md == NULL ? NULL : get_hashlib_utf8name_by_evp_md(md); + const char *digest_name = hashlib_HMAC_get_hashlib_digest_name(self); if (digest_name == NULL) { assert(PyErr_Occurred()); return NULL; @@ -2043,30 +2265,81 @@ static PyObject * _hashlib_HMAC_update_impl(HMACobject *self, PyObject *msg) /*[clinic end generated code: output=f31f0ace8c625b00 input=1829173bb3cfd4e6]*/ { - if (!_hmac_update(self, msg)) { + if (hashlib_openssl_HMAC_update_with_lock(self, msg) < 0) { return NULL; } Py_RETURN_NONE; } -static int -_hmac_digest(HMACobject *self, unsigned char *buf, unsigned int len) +#define BAD_DIGEST_SIZE 0 + +/* + * Return the digest size in bytes. + * + * On error, set an exception and return BAD_DIGEST_SIZE. + */ +static unsigned int +hashlib_openssl_HMAC_digest_size(HMACobject *self) { - HMAC_CTX *temp_ctx = py_openssl_wrapper_HMAC_CTX_new(); - if (temp_ctx == NULL) { - return 0; + assert(EVP_MAX_MD_SIZE < INT_MAX); +#ifdef Py_HAS_OPENSSL3_SUPPORT + assert(self->ctx != NULL); + size_t digest_size = EVP_MAC_CTX_get_mac_size(self->ctx); + assert(digest_size <= (size_t)EVP_MAX_MD_SIZE); +#else + const EVP_MD *md = hashlib_openssl_HMAC_evp_md_borrowed(self); + if (md == NULL) { + return BAD_DIGEST_SIZE; + } + int digest_size = EVP_MD_size(md); + /* digest_size < 0 iff EVP_MD context is NULL (which is impossible here) */ + assert(digest_size >= 0); + assert(digest_size <= (int)EVP_MAX_MD_SIZE); +#endif + /* digest_size == 0 means that the context is not entirely initialized */ + if (digest_size == 0) { + raise_ssl_error(PyExc_ValueError, "missing digest size"); + return BAD_DIGEST_SIZE; + } + return (unsigned int)digest_size; +} + +/* + * Extract the MAC value to 'buf' and return the digest size. + * + * The buffer 'buf' must have at least hashlib_openssl_HMAC_digest_size(self) + * bytes. Smaller buffers lead to undefined behaviors. + * + * On error, set an exception and return -1. + */ +static Py_ssize_t +hashlib_openssl_HMAC_digest_compute(HMACobject *self, unsigned char *buf) +{ + unsigned int digest_size = hashlib_openssl_HMAC_digest_size(self); + assert(digest_size <= EVP_MAX_MD_SIZE); + if (digest_size == BAD_DIGEST_SIZE) { + assert(PyErr_Occurred()); + return -1; } - if (locked_HMAC_CTX_copy(temp_ctx, self) < 0) { - HMAC_CTX_free(temp_ctx); - return 0; + Py_HMAC_CTX_TYPE *ctx = hashlib_openssl_HMAC_ctx_copy_with_lock(self); + if (ctx == NULL) { + return -1; } - int r = HMAC_Final(temp_ctx, buf, &len); - HMAC_CTX_free(temp_ctx); +#ifdef Py_HAS_OPENSSL3_SUPPORT + int r = EVP_MAC_final(ctx, buf, NULL, digest_size); +#else + int r = HMAC_Final(ctx, buf, NULL); +#endif + PY_HMAC_CTX_free(ctx); if (r == 0) { +#ifdef Py_HAS_OPENSSL3_SUPPORT + notify_ssl_error_occurred_in(Py_STRINGIFY(EVP_MAC_final)); +#else notify_ssl_error_occurred_in(Py_STRINGIFY(HMAC_Final)); - return 0; +#endif + return -1; } - return 1; + return digest_size; } /*[clinic input] @@ -2078,16 +2351,9 @@ static PyObject * _hashlib_HMAC_digest_impl(HMACobject *self) /*[clinic end generated code: output=1b1424355af7a41e input=bff07f74da318fb4]*/ { - unsigned char digest[EVP_MAX_MD_SIZE]; - unsigned int digest_size = _hashlib_hmac_digest_size(self); - if (digest_size == 0) { - return NULL; - } - int r = _hmac_digest(self, digest, digest_size); - if (r == 0) { - return NULL; - } - return PyBytes_FromStringAndSize((const char *)digest, digest_size); + unsigned char buf[EVP_MAX_MD_SIZE]; + Py_ssize_t n = hashlib_openssl_HMAC_digest_compute(self, buf); + return n < 0 ? NULL : PyBytes_FromStringAndSize((const char *)buf, n); } /*[clinic input] @@ -2103,43 +2369,37 @@ static PyObject * _hashlib_HMAC_hexdigest_impl(HMACobject *self) /*[clinic end generated code: output=80d825be1eaae6a7 input=5abc42702874ddcf]*/ { - unsigned char digest[EVP_MAX_MD_SIZE]; - unsigned int digest_size = _hashlib_hmac_digest_size(self); - if (digest_size == 0) { - return NULL; - } - int r = _hmac_digest(self, digest, digest_size); - if (r == 0) { - return NULL; - } - return _Py_strhex((const char *)digest, digest_size); + unsigned char buf[EVP_MAX_MD_SIZE]; + Py_ssize_t n = hashlib_openssl_HMAC_digest_compute(self, buf); + return n < 0 ? NULL : _Py_strhex((const char *)buf, n); } static PyObject * -_hashlib_hmac_get_digest_size(PyObject *op, void *Py_UNUSED(closure)) +_hashlib_HMAC_digest_size_getter(PyObject *op, void *Py_UNUSED(closure)) { HMACobject *self = HMACobject_CAST(op); - unsigned int digest_size = _hashlib_hmac_digest_size(self); - return digest_size == 0 ? NULL : PyLong_FromLong(digest_size); + unsigned int size = hashlib_openssl_HMAC_digest_size(self); + return size == BAD_DIGEST_SIZE ? NULL : PyLong_FromLong(size); } static PyObject * -_hashlib_hmac_get_block_size(PyObject *op, void *Py_UNUSED(closure)) +_hashlib_HMAC_block_size_getter(PyObject *op, void *Py_UNUSED(closure)) { HMACobject *self = HMACobject_CAST(op); - const EVP_MD *md = _hashlib_hmac_get_md(self); +#ifdef Py_HAS_OPENSSL3_SUPPORT + assert(self->ctx != NULL); + return PyLong_FromSize_t(EVP_MAC_CTX_get_block_size(self->ctx)); +#else + const EVP_MD *md = hashlib_openssl_HMAC_evp_md_borrowed(self); return md == NULL ? NULL : PyLong_FromLong(EVP_MD_block_size(md)); +#endif } static PyObject * -_hashlib_hmac_get_name(PyObject *op, void *Py_UNUSED(closure)) +_hashlib_HMAC_name_getter(PyObject *op, void *Py_UNUSED(closure)) { HMACobject *self = HMACobject_CAST(op); - const EVP_MD *md = _hashlib_hmac_get_md(self); - if (md == NULL) { - return NULL; - } - const char *digest_name = get_hashlib_utf8name_by_evp_md(md); + const char *digest_name = hashlib_HMAC_get_hashlib_digest_name(self); if (digest_name == NULL) { assert(PyErr_Occurred()); return NULL; @@ -2155,15 +2415,15 @@ static PyMethodDef HMAC_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyGetSetDef HMAC_getset[] = { - {"digest_size", _hashlib_hmac_get_digest_size, NULL, NULL, NULL}, - {"block_size", _hashlib_hmac_get_block_size, NULL, NULL, NULL}, - {"name", _hashlib_hmac_get_name, NULL, NULL, NULL}, +static PyGetSetDef HMAC_getsets[] = { + {"digest_size", _hashlib_HMAC_digest_size_getter, NULL, NULL, NULL}, + {"block_size", _hashlib_HMAC_block_size_getter, NULL, NULL, NULL}, + {"name", _hashlib_HMAC_name_getter, NULL, NULL, NULL}, {NULL} /* Sentinel */ }; -PyDoc_STRVAR(hmactype_doc, +PyDoc_STRVAR(HMACobject_type_doc, "The object used to calculate HMAC of a message.\n\ \n\ Methods:\n\ @@ -2178,20 +2438,24 @@ Attributes:\n\ name -- the name, including the hash algorithm used by this object\n\ digest_size -- number of bytes in digest() output\n"); -static PyType_Slot HMACtype_slots[] = { - {Py_tp_doc, (char *)hmactype_doc}, - {Py_tp_repr, _hmac_repr}, - {Py_tp_dealloc, _hmac_dealloc}, +static PyType_Slot HMACobject_type_slots[] = { + {Py_tp_doc, (char *)HMACobject_type_doc}, + {Py_tp_repr, _hashlib_HMAC_repr}, + {Py_tp_dealloc, _hashlib_HMAC_dealloc}, {Py_tp_methods, HMAC_methods}, - {Py_tp_getset, HMAC_getset}, + {Py_tp_getset, HMAC_getsets}, {0, NULL} }; -PyType_Spec HMACtype_spec = { - "_hashlib.HMAC", /* name */ - sizeof(HMACobject), /* basicsize */ - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE, - .slots = HMACtype_slots, +PyType_Spec HMACobject_type_spec = { + .name = "_hashlib.HMAC", + .basicsize = sizeof(HMACobject), + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_DISALLOW_INSTANTIATION + | Py_TPFLAGS_IMMUTABLETYPE + ), + .slots = HMACobject_type_slots }; @@ -2466,6 +2730,12 @@ hashlib_clear(PyObject *m) _Py_hashtable_destroy(state->hashtable); state->hashtable = NULL; } +#ifdef Py_HAS_OPENSSL3_SUPPORT + if (state->evp_hmac != NULL) { + EVP_MAC_free(state->evp_hmac); + state->evp_hmac = NULL; + } +#endif return 0; } @@ -2533,13 +2803,22 @@ hashlib_init_hmactype(PyObject *module) { _hashlibstate *state = get_hashlib_state(module); - state->HMAC_type = (PyTypeObject *)PyType_FromSpec(&HMACtype_spec); + state->HMAC_type = (PyTypeObject *)PyType_FromSpec(&HMACobject_type_spec); if (state->HMAC_type == NULL) { return -1; } if (PyModule_AddType(module, state->HMAC_type) < 0) { return -1; } +#ifdef Py_HAS_OPENSSL3_SUPPORT + state->evp_hmac = EVP_MAC_fetch(NULL, "HMAC", NULL); + if (state->evp_hmac == NULL) { + ERR_clear_error(); + PyErr_SetString(PyExc_ImportError, "cannot initialize EVP_MAC HMAC"); + return -1; + } +#endif + return 0; } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy