mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
gh-134531: refactor _hashlib
logic for handling NIDs and EVP_MDs (#135254)
This commit is contained in:
parent
158e5162bf
commit
aee45fd03f
1 changed files with 106 additions and 62 deletions
|
@ -368,41 +368,83 @@ notify_ssl_error_occurred(void)
|
|||
}
|
||||
/* LCOV_EXCL_STOP */
|
||||
|
||||
static const char *
|
||||
get_openssl_evp_md_utf8name(const EVP_MD *md)
|
||||
{
|
||||
assert(md != NULL);
|
||||
int nid = EVP_MD_nid(md);
|
||||
const char *name = NULL;
|
||||
const py_hashentry_t *h;
|
||||
/*
|
||||
* OpenSSL provides a way to go from NIDs to digest names for hash functions
|
||||
* but lacks this granularity for MAC objects where it is not possible to get
|
||||
* the underlying digest name (only the block size and digest size are allowed
|
||||
* to be recovered).
|
||||
*
|
||||
* In addition, OpenSSL aliases pollute the list of known digest names
|
||||
* as OpenSSL appears to have its own definition of alias. In particular,
|
||||
* the resulting list still contains duplicate and alternate names for several
|
||||
* algorithms.
|
||||
*
|
||||
* Therefore, digest names, whether they are used by hash functions or HMAC,
|
||||
* are handled through EVP_MD objects or directly by using some NID.
|
||||
*/
|
||||
|
||||
for (h = py_hashes; h->py_name != NULL; h++) {
|
||||
/* Get a cached entry by OpenSSL NID. */
|
||||
static const py_hashentry_t *
|
||||
get_hashentry_by_nid(int nid)
|
||||
{
|
||||
for (const py_hashentry_t *h = py_hashes; h->py_name != NULL; h++) {
|
||||
if (h->ossl_nid == nid) {
|
||||
name = h->py_name;
|
||||
break;
|
||||
return h;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the NID to a string via OBJ_nid2*() functions.
|
||||
*
|
||||
* If 'nid' cannot be resolved, set an exception and return NULL.
|
||||
*/
|
||||
static const char *
|
||||
get_asn1_utf8name_by_nid(int nid)
|
||||
{
|
||||
const char *name = OBJ_nid2ln(nid);
|
||||
if (name == NULL) {
|
||||
/* Ignore aliased names and only use long, lowercase name. The aliases
|
||||
* pollute the list and OpenSSL appears to have its own definition of
|
||||
* alias as the resulting list still contains duplicate and alternate
|
||||
* names for several algorithms.
|
||||
*/
|
||||
name = OBJ_nid2ln(nid);
|
||||
if (name == NULL)
|
||||
name = OBJ_nid2sn(nid);
|
||||
// In OpenSSL 3.0 and later, OBJ_nid*() are thread-safe and may raise.
|
||||
assert(ERR_peek_last_error() != 0);
|
||||
if (ERR_GET_REASON(ERR_peek_last_error()) != OBJ_R_UNKNOWN_NID) {
|
||||
notify_ssl_error_occurred();
|
||||
return NULL;
|
||||
}
|
||||
// fallback to short name and unconditionally propagate errors
|
||||
name = OBJ_nid2sn(nid);
|
||||
if (name == NULL) {
|
||||
raise_ssl_error(PyExc_ValueError, "cannot resolve NID %d", nid);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
get_openssl_evp_md_name(const EVP_MD *md)
|
||||
/*
|
||||
* Convert the NID to an OpenSSL digest name.
|
||||
*
|
||||
* On error, set an exception and return NULL.
|
||||
*/
|
||||
static const char *
|
||||
get_hashlib_utf8name_by_nid(int nid)
|
||||
{
|
||||
const char *name = get_openssl_evp_md_utf8name(md);
|
||||
return PyUnicode_FromString(name);
|
||||
const py_hashentry_t *e = get_hashentry_by_nid(nid);
|
||||
return e ? e->py_name : get_asn1_utf8name_by_nid(nid);
|
||||
}
|
||||
|
||||
/* Get EVP_MD by HID and purpose */
|
||||
/* 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)
|
||||
{
|
||||
assert(md != NULL);
|
||||
return get_hashlib_utf8name_by_nid(EVP_MD_nid(md));
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a new reference to an EVP_MD object described by name and purpose.
|
||||
*
|
||||
* If 'name' is an OpenSSL indexed name, the return value is cached.
|
||||
*/
|
||||
static PY_EVP_MD *
|
||||
get_openssl_evp_md_by_utf8name(PyObject *module, const char *name,
|
||||
Py_hash_type py_ht)
|
||||
|
@ -471,42 +513,46 @@ get_openssl_evp_md_by_utf8name(PyObject *module, const char *name,
|
|||
return digest;
|
||||
}
|
||||
|
||||
/* Get digest EVP_MD from object
|
||||
/*
|
||||
* Raise an exception indicating that 'digestmod' is not supported.
|
||||
*/
|
||||
static void
|
||||
raise_unsupported_digestmod_error(PyObject *module, PyObject *digestmod)
|
||||
{
|
||||
_hashlibstate *state = get_hashlib_state(module);
|
||||
PyErr_Format(state->unsupported_digestmod_error,
|
||||
"Unsupported digestmod %R", digestmod);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a new reference to an EVP_MD described by 'digestmod' and purpose.
|
||||
*
|
||||
* * string
|
||||
* * _hashopenssl builtin function
|
||||
* On error, set an exception and return NULL.
|
||||
*
|
||||
* on error returns NULL with exception set.
|
||||
* Parameters
|
||||
*
|
||||
* digestmod A digest name or a _hashopenssl builtin function
|
||||
* py_ht The message digest purpose.
|
||||
*/
|
||||
static PY_EVP_MD *
|
||||
get_openssl_evp_md(PyObject *module, PyObject *digestmod,
|
||||
Py_hash_type py_ht)
|
||||
get_openssl_evp_md(PyObject *module, PyObject *digestmod, Py_hash_type py_ht)
|
||||
{
|
||||
PyObject *name_obj = NULL;
|
||||
const char *name;
|
||||
|
||||
if (PyUnicode_Check(digestmod)) {
|
||||
name_obj = digestmod;
|
||||
} else {
|
||||
_hashlibstate *state = get_hashlib_state(module);
|
||||
// borrowed ref
|
||||
name_obj = PyDict_GetItemWithError(state->constructs, digestmod);
|
||||
name = PyUnicode_AsUTF8(digestmod);
|
||||
}
|
||||
if (name_obj == NULL) {
|
||||
else {
|
||||
PyObject *dict = get_hashlib_state(module)->constructs;
|
||||
assert(dict != NULL);
|
||||
PyObject *borrowed_ref = PyDict_GetItemWithError(dict, digestmod);
|
||||
name = borrowed_ref == NULL ? NULL : PyUnicode_AsUTF8(borrowed_ref);
|
||||
}
|
||||
if (name == NULL) {
|
||||
if (!PyErr_Occurred()) {
|
||||
_hashlibstate *state = get_hashlib_state(module);
|
||||
PyErr_Format(
|
||||
state->unsupported_digestmod_error,
|
||||
"Unsupported digestmod %R", digestmod);
|
||||
raise_unsupported_digestmod_error(module, digestmod);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
name = PyUnicode_AsUTF8(name_obj);
|
||||
if (name == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return get_openssl_evp_md_by_utf8name(module, name, py_ht);
|
||||
}
|
||||
|
||||
|
@ -745,7 +791,9 @@ _hashlib_HASH_get_name(PyObject *op, void *Py_UNUSED(closure))
|
|||
notify_ssl_error_occurred();
|
||||
return NULL;
|
||||
}
|
||||
return get_openssl_evp_md_name(md);
|
||||
const char *name = get_hashlib_utf8name_by_evp_md(md);
|
||||
assert(name != NULL || PyErr_Occurred());
|
||||
return name == NULL ? NULL : PyUnicode_FromString(name);
|
||||
}
|
||||
|
||||
static PyGetSetDef HASH_getsets[] = {
|
||||
|
@ -1775,20 +1823,15 @@ _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);
|
||||
if (md == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
PyObject *digest_name = get_openssl_evp_md_name(md);
|
||||
digest_name = md == NULL ? NULL : get_hashlib_utf8name_by_evp_md(md);
|
||||
if (digest_name == NULL) {
|
||||
assert(PyErr_Occurred());
|
||||
return NULL;
|
||||
}
|
||||
PyObject *repr = PyUnicode_FromFormat(
|
||||
"<%U HMAC object @ %p>", digest_name, self
|
||||
);
|
||||
Py_DECREF(digest_name);
|
||||
return repr;
|
||||
return PyUnicode_FromFormat("<%s HMAC object @ %p>", digest_name, self);
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
|
@ -1900,13 +1943,12 @@ _hashlib_hmac_get_name(PyObject *op, void *Py_UNUSED(closure))
|
|||
if (md == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
PyObject *digest_name = get_openssl_evp_md_name(md);
|
||||
const char *digest_name = get_hashlib_utf8name_by_evp_md(md);
|
||||
if (digest_name == NULL) {
|
||||
assert(PyErr_Occurred());
|
||||
return NULL;
|
||||
}
|
||||
PyObject *name = PyUnicode_FromFormat("hmac-%U", digest_name);
|
||||
Py_DECREF(digest_name);
|
||||
return name;
|
||||
return PyUnicode_FromFormat("hmac-%s", digest_name);
|
||||
}
|
||||
|
||||
static PyMethodDef HMAC_methods[] = {
|
||||
|
@ -1982,7 +2024,9 @@ _openssl_hash_name_mapper(const EVP_MD *md, const char *from,
|
|||
return;
|
||||
}
|
||||
|
||||
py_name = get_openssl_evp_md_name(md);
|
||||
const char *name = get_hashlib_utf8name_by_evp_md(md);
|
||||
assert(name != NULL || PyErr_Occurred());
|
||||
py_name = name == NULL ? NULL : PyUnicode_FromString(name);
|
||||
if (py_name == NULL) {
|
||||
state->error = 1;
|
||||
} else {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue