mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
gh-134531: use EVP_MAC API for _hashlib.HMAC when possible (#135235)
Use `EVP_MAC` API for HMAC on builds with OpenSSL 3.0 or later.
This commit is contained in:
parent
d6d054853d
commit
b9c50b4988
2 changed files with 362 additions and 88 deletions
|
|
@ -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) <hmac(3)>` interface. Patch by Bénédikt Tran.
|
||||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
/* EVP is the preferred interface to hashing in OpenSSL */
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/crypto.h> // FIPS_mode()
|
||||
/* We use the object interface to discover what hashes OpenSSL supports. */
|
||||
#include <openssl/objects.h>
|
||||
|
|
@ -40,6 +39,10 @@
|
|||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
# define Py_HAS_OPENSSL3_SUPPORT
|
||||
# include <openssl/core_names.h> // OSSL_MAC_PARAM_DIGEST
|
||||
# include <openssl/params.h> // OSSL_PARAM_*()
|
||||
#else
|
||||
# include <openssl/hmac.h> // HMAC()
|
||||
#endif
|
||||
|
||||
#ifndef OPENSSL_THREADS
|
||||
|
|
@ -65,6 +68,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 +79,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 +294,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 +318,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 +636,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)
|
||||
|
|
@ -733,6 +766,47 @@ 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) {
|
||||
if (evp_md != NULL) {
|
||||
*evp_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. */
|
||||
|
|
@ -1742,9 +1816,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
|
||||
|
||||
|
|
@ -1762,9 +1854,14 @@ _hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key,
|
|||
{
|
||||
_hashlibstate *state = get_hashlib_state(module);
|
||||
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
|
||||
unsigned char *result = NULL;
|
||||
PY_EVP_MD *evp = NULL;
|
||||
int is_xof;
|
||||
|
||||
if (key->len > INT_MAX) {
|
||||
|
|
@ -1778,13 +1875,34 @@ _hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key,
|
|||
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,
|
||||
|
|
@ -1793,23 +1911,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)
|
||||
|
|
@ -1821,18 +1943,179 @@ py_openssl_wrapper_HMAC_CTX_new(void)
|
|||
}
|
||||
return ctx;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int _hmac_update(HMACobject*, PyObject*);
|
||||
|
||||
#ifndef Py_HAS_OPENSSL3_SUPPORT
|
||||
static const EVP_MD *
|
||||
_hashlib_hmac_get_md(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_hmac_get_md(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 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) {
|
||||
*nid = NID_undef;
|
||||
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
|
||||
|
|
@ -1850,10 +2133,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;
|
||||
PY_HMAC_CTX_TYPE *ctx = NULL;
|
||||
HMACobject *self = NULL;
|
||||
int is_xof, r;
|
||||
#ifdef Py_HAS_OPENSSL3_SUPPORT
|
||||
int nid;
|
||||
#endif
|
||||
|
||||
if (key->len > INT_MAX) {
|
||||
PyErr_SetString(PyExc_OverflowError,
|
||||
|
|
@ -1867,29 +2151,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);
|
||||
|
|
@ -1899,36 +2169,27 @@ _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 (_hmac_update(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;
|
||||
}
|
||||
|
||||
#define BAD_DIGEST_SIZE 0
|
||||
|
||||
/*
|
||||
|
|
@ -1939,6 +2200,12 @@ locked_HMAC_CTX_copy(HMAC_CTX *new_ctx_p, HMACobject *self)
|
|||
static unsigned int
|
||||
_hashlib_hmac_digest_size(HMACobject *self)
|
||||
{
|
||||
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_hmac_get_md(self);
|
||||
if (md == NULL) {
|
||||
return BAD_DIGEST_SIZE;
|
||||
|
|
@ -1947,6 +2214,7 @@ _hashlib_hmac_digest_size(HMACobject *self)
|
|||
/* 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");
|
||||
|
|
@ -1960,21 +2228,13 @@ _hmac_update(HMACobject *self, PyObject *obj)
|
|||
{
|
||||
int r;
|
||||
Py_buffer view = {0};
|
||||
|
||||
GET_BUFFER_VIEW_OR_ERROR(obj, &view, return 0);
|
||||
GET_BUFFER_VIEW_OR_ERROR(obj, &view, return -1);
|
||||
HASHLIB_EXTERNAL_INSTRUCTIONS_LOCKED(
|
||||
self, view.len,
|
||||
r = HMAC_Update(
|
||||
self->ctx, (const unsigned char *)view.buf, (size_t)view.len
|
||||
)
|
||||
r = hashlib_openssl_HMAC_update_once(self->ctx, &view)
|
||||
);
|
||||
PyBuffer_Release(&view);
|
||||
|
||||
if (r == 0) {
|
||||
notify_ssl_error_occurred_in(Py_STRINGIFY(HMAC_Update));
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
|
|
@ -1988,24 +2248,17 @@ _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;
|
||||
}
|
||||
|
||||
|
|
@ -2015,7 +2268,7 @@ _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);
|
||||
|
|
@ -2025,10 +2278,8 @@ _hmac_dealloc(PyObject *op)
|
|||
static PyObject *
|
||||
_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;
|
||||
|
|
@ -2047,7 +2298,7 @@ static PyObject *
|
|||
_hashlib_HMAC_update_impl(HMACobject *self, PyObject *msg)
|
||||
/*[clinic end generated code: output=f31f0ace8c625b00 input=1829173bb3cfd4e6]*/
|
||||
{
|
||||
if (!_hmac_update(self, msg)) {
|
||||
if (_hmac_update(self, msg) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
|
|
@ -2056,7 +2307,7 @@ _hashlib_HMAC_update_impl(HMACobject *self, PyObject *msg)
|
|||
/*
|
||||
* Extract the MAC value to 'buf' and return the digest size.
|
||||
*
|
||||
* The buffer 'buf' must have at least hashlib_openssl_HMAC_digest_size(self)
|
||||
* The buffer 'buf' must have at least _hashlib_hmac_digest_size(self)
|
||||
* bytes. Smaller buffers lead to undefined behaviors.
|
||||
*
|
||||
* On error, set an exception and return -1.
|
||||
|
|
@ -2070,18 +2321,22 @@ _hmac_digest(HMACobject *self, unsigned char *buf)
|
|||
assert(PyErr_Occurred());
|
||||
return -1;
|
||||
}
|
||||
HMAC_CTX *temp_ctx = py_openssl_wrapper_HMAC_CTX_new();
|
||||
if (temp_ctx == NULL) {
|
||||
PY_HMAC_CTX_TYPE *ctx = hashlib_openssl_HMAC_ctx_copy_with_lock(self);
|
||||
if (ctx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (locked_HMAC_CTX_copy(temp_ctx, self) < 0) {
|
||||
HMAC_CTX_free(temp_ctx);
|
||||
return -1;
|
||||
}
|
||||
int r = HMAC_Final(temp_ctx, buf, NULL);
|
||||
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));
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
return digest_size;
|
||||
|
|
@ -2133,19 +2388,20 @@ static PyObject *
|
|||
_hashlib_hmac_get_block_size(PyObject *op, void *Py_UNUSED(closure))
|
||||
{
|
||||
HMACobject *self = HMACobject_CAST(op);
|
||||
#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_hmac_get_md(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))
|
||||
{
|
||||
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;
|
||||
|
|
@ -2472,6 +2728,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;
|
||||
}
|
||||
|
|
@ -2546,6 +2808,15 @@ hashlib_init_hmactype(PyObject *module)
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue