diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index ce9603d5db841f..694dbc51a9e64e 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 @@ -64,11 +71,19 @@ #define PY_EVP_MD_fetch(algorithm, properties) EVP_MD_fetch(NULL, algorithm, properties) #define PY_EVP_MD_up_ref(md) EVP_MD_up_ref(md) #define PY_EVP_MD_free(md) EVP_MD_free(md) + +#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) #define PY_EVP_MD_up_ref(md) do {} while(0) #define PY_EVP_MD_free(md) do {} while(0) + +#define Py_HMAC_CTX_TYPE HMAC_CTX +#define PY_HMAC_CTX_free HMAC_CTX_free +#define PY_HMAC_update HMAC_Update #endif /* hash alias map and fast lookup @@ -267,6 +282,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* @@ -289,10 +307,15 @@ typedef struct { typedef struct { PyObject_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 // Prevents undefined behavior via multiple threads entering the C API. bool use_mutex; - PyMutex mutex; /* HMAC context lock */ + PyMutex mutex; /* HMAC context lock */ } HMACobject; #define HMACobject_CAST(op) ((HMACobject *)(op)) @@ -499,6 +522,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) @@ -630,6 +667,39 @@ get_openssl_evp_md(PyObject *module, PyObject *digestmod, Py_hash_type py_ht) return get_openssl_evp_md_by_utf8name(module, name, py_ht); } +/* + * 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. + */ +#ifdef Py_HAS_OPENSSL3_SUPPORT +static const char * +get_openssl_digest_name(PyObject *module, PyObject *digestmod, + Py_hash_type py_ht, int *evp_md_nid) +{ + if (evp_md_nid != NULL) { + *evp_md_nid = NID_undef; + } + PY_EVP_MD *md = get_openssl_evp_md(module, digestmod, py_ht); + if (md == NULL) { + return NULL; + } + int nid = EVP_MD_nid(md); + if (evp_md_nid != NULL) { + *evp_md_nid = nid; + } + const char *name = get_openssl_utf8name_by_nid(nid); + PY_EVP_MD_free(md); + if (name == NULL) { + raise_unsupported_digestmod_error(module, digestmod); + } + return name; +} +#endif + // --- OpenSSL HASH wrappers -------------------------------------------------- /* Thin wrapper around EVP_MD_CTX_new() which sets an exception on failure. */ @@ -1633,9 +1703,27 @@ _hashlib_scrypt_impl(PyObject *module, Py_buffer *password, Py_buffer *salt, } #endif /* PY_OPENSSL_HAS_SCRYPT */ -/* 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 @@ -1651,29 +1739,48 @@ _hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key, Py_buffer *msg, PyObject *digest) /*[clinic end generated code: output=82f19965d12706ac input=0a0790cc3db45c2e]*/ { + const void *r; 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; + PY_EVP_MD *evp = NULL; +#endif 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(module, digest, Py_ht_mac, NULL); + if (digest_name == NULL) { + return NULL; + } + Py_BEGIN_ALLOW_THREADS + r = 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 + assert(md_len < (size_t)PY_SSIZE_T_MAX); +#else evp = get_openssl_evp_md(module, digest, Py_ht_mac); if (evp == NULL) { return NULL; } Py_BEGIN_ALLOW_THREADS - result = HMAC( + r = HMAC( evp, (const void *)key->buf, (int)key->len, (const unsigned char *)msg->buf, (size_t)msg->len, @@ -1681,17 +1788,21 @@ _hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key, ); Py_END_ALLOW_THREADS PY_EVP_MD_free(evp); - - if (result == NULL) { +#endif + if (r == NULL) { +#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) @@ -1704,17 +1815,105 @@ 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); + if (!self->use_mutex && view.len >= HASHLIB_GIL_MINSIZE) { + // TODO(picnixz): see https://github.com/python/cpython/issues/135239. + self->use_mutex = true; + } + if (self->use_mutex) { + Py_BEGIN_ALLOW_THREADS + PyMutex_Lock(&self->mutex); + r = hashlib_openssl_HMAC_update_once(self->ctx, &view); + PyMutex_Unlock(&self->mutex); + Py_END_ALLOW_THREADS + } + else { + 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 + ENTER_HASHLIB(self); + ctx = EVP_MAC_CTX_dup(self->ctx); + LEAVE_HASHLIB(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; + } + ENTER_HASHLIB(self); + r = HMAC_CTX_copy(ctx, self->ctx); + LEAVE_HASHLIB(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; +} /*[clinic input] _hashlib.hmac_new @@ -1731,9 +1930,12 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj, PyObject *digestmod) /*[clinic end generated code: output=c20d9e4d9ed6d219 input=5f4071dcc7f34362]*/ { - PY_EVP_MD *digest; - HMAC_CTX *ctx = NULL; + _hashlibstate *state = get_hashlib_state(module); HMACobject *self = NULL; + Py_HMAC_CTX_TYPE *ctx = NULL; +#ifdef Py_HAS_OPENSSL3_SUPPORT + int evp_md_nid = NID_undef; +#endif int r; if (key->len > INT_MAX) { @@ -1748,11 +1950,54 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj, return NULL; } - digest = get_openssl_evp_md(module, digestmod, Py_ht_mac); +#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(). + */ + const char *digest = get_openssl_digest_name( + module, digestmod, Py_ht_mac, &evp_md_nid + ); if (digest == NULL) { return NULL; } + assert(evp_md_nid != NID_undef); + /* + * 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 + PY_EVP_MD *digest = get_openssl_evp_md(module, digestmod, Py_ht_mac); + if (digest == NULL) { + return NULL; + } ctx = py_openssl_wrapper_HMAC_CTX_new(); if (ctx == NULL) { PY_EVP_MD_free(digest); @@ -1761,12 +2006,16 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj, r = HMAC_Init_ex(ctx, key->buf, (int)key->len, digest, NULL /* impl */); PY_EVP_MD_free(digest); +#endif if (r == 0) { +#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 goto error; } - _hashlibstate *state = get_hashlib_state(module); self = PyObject_New(HMACobject, state->HMAC_type); if (self == NULL) { goto error; @@ -1774,86 +2023,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(evp_md_nid != NID_undef); + self->evp_md_nid = evp_md_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; - ENTER_HASHLIB(self); - result = HMAC_CTX_copy(new_ctx_p, self->ctx); - LEAVE_HASHLIB(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); - - if (!self->use_mutex && view.len >= HASHLIB_GIL_MINSIZE) { - self->use_mutex = true; - } - if (self->use_mutex) { - Py_BEGIN_ALLOW_THREADS - PyMutex_Lock(&self->mutex); - r = HMAC_Update(self->ctx, - (const unsigned char *)view.buf, - (size_t)view.len); - PyMutex_Unlock(&self->mutex); - Py_END_ALLOW_THREADS - } else { - 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 @@ -1865,34 +2054,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); @@ -1900,12 +2082,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; @@ -1924,30 +2104,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] @@ -1959,16 +2190,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] @@ -1984,43 +2208,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_get_digestsize(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_get_blocksize(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_get_name(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; @@ -2036,15 +2254,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_get_digestsize, NULL, NULL, NULL}, + {"block_size", _hashlib_HMAC_get_blocksize, NULL, NULL, NULL}, + {"name", _hashlib_HMAC_get_name, 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\ @@ -2059,20 +2277,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 }; @@ -2347,6 +2569,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; } @@ -2414,13 +2642,21 @@ 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) { + raise_ssl_error(PyExc_RuntimeError, "cannot initialize EVP_MAC HMAC"); + return -1; + } +#endif + return 0; }