@@ -368,41 +368,83 @@ notify_ssl_error_occurred(void)
368368}
369369/* LCOV_EXCL_STOP */ 
370370
371- static  const  char  * 
372- get_openssl_evp_md_utf8name (const  EVP_MD  * md )
373- {
374-     assert (md  !=  NULL );
375-     int  nid  =  EVP_MD_nid (md );
376-     const  char  * name  =  NULL ;
377-     const  py_hashentry_t  * h ;
371+ /* 
372+  * OpenSSL provides a way to go from NIDs to digest names for hash functions 
373+  * but lacks this granularity for MAC objects where it is not possible to get 
374+  * the underlying digest name (only the block size and digest size are allowed 
375+  * to be recovered). 
376+  * 
377+  * In addition, OpenSSL aliases pollute the list of known digest names 
378+  * as OpenSSL appears to have its own definition of alias. In particular, 
379+  * the resulting list still contains duplicate and alternate names for several 
380+  * algorithms. 
381+  * 
382+  * Therefore, digest names, whether they are used by hash functions or HMAC, 
383+  * are handled through EVP_MD objects or directly by using some NID. 
384+  */ 
378385
379-     for  (h  =  py_hashes ; h -> py_name  !=  NULL ; h ++ ) {
386+ /* Get a cached entry by OpenSSL NID. */ 
387+ static  const  py_hashentry_t  * 
388+ get_hashentry_by_nid (int  nid )
389+ {
390+     for  (const  py_hashentry_t  * h  =  py_hashes ; h -> py_name  !=  NULL ; h ++ ) {
380391        if  (h -> ossl_nid  ==  nid ) {
381-             name  =  h -> py_name ;
382-             break ;
392+             return  h ;
383393        }
384394    }
395+     return  NULL ;
396+ }
397+ 
398+ /* 
399+  * Convert the NID to a string via OBJ_nid2_*() functions. 
400+  * 
401+  * If 'nid' cannot be resolved, set an exception and return NULL. 
402+  */ 
403+ static  const  char  * 
404+ get_asn1_utf8name_by_nid (int  nid )
405+ {
406+     const  char  * name  =  OBJ_nid2ln (nid );
385407    if  (name  ==  NULL ) {
386-         /* Ignore aliased names and only use long, lowercase name. The aliases 
387-          * pollute the list and OpenSSL appears to have its own definition of 
388-          * alias as the resulting list still contains duplicate and alternate 
389-          * names for several algorithms. 
390-          */ 
391-         name  =  OBJ_nid2ln (nid );
392-         if  (name  ==  NULL )
393-             name  =  OBJ_nid2sn (nid );
408+         // In OpenSSL 3.0 and later, OBJ_nid*() are thread-safe and may raise. 
409+         assert (ERR_peek_last_error () !=  0 );
410+         if  (ERR_GET_REASON (ERR_peek_last_error ()) !=  OBJ_R_UNKNOWN_NID ) {
411+             notify_ssl_error_occurred ();
412+             return  NULL ;
413+         }
414+         // fallback to short name and unconditionally propagate errors 
415+         name  =  OBJ_nid2sn (nid );
416+         if  (name  ==  NULL ) {
417+             raise_ssl_error (PyExc_ValueError , "cannot resolve NID %d" , nid );
418+         }
394419    }
395420    return  name ;
396421}
397422
398- static  PyObject  * 
399- get_openssl_evp_md_name (const  EVP_MD  * md )
423+ /* 
424+  * Convert the NID to an OpenSSL digest name. 
425+  * 
426+  * On error, set an exception and return NULL. 
427+  */ 
428+ static  const  char  * 
429+ get_hashlib_utf8name_by_nid (int  nid )
430+ {
431+     const  py_hashentry_t  * e  =  get_hashentry_by_nid (nid );
432+     return  e  ? e -> py_name  : get_asn1_utf8name_by_nid (nid );
433+ }
434+ 
435+ /* Same as get_hashlib_utf8name_by_nid() but using an EVP_MD object. */ 
436+ static  const  char  * 
437+ get_hashlib_utf8name_by_evp_md (const  EVP_MD  * md )
400438{
401-     const   char   * name   =   get_openssl_evp_md_utf8name ( md );
402-     return  PyUnicode_FromString ( name );
439+     assert ( md   !=   NULL );
440+     return  get_hashlib_utf8name_by_nid ( EVP_MD_nid ( md ) );
403441}
404442
405- /* Get EVP_MD by HID and purpose */ 
443+ /* 
444+  * Get a new reference to an EVP_MD object described by name and purpose. 
445+  * 
446+  * If 'name' is an OpenSSL indexed name, the return value is cached. 
447+  */ 
406448static  PY_EVP_MD  * 
407449get_openssl_evp_md_by_utf8name (PyObject  * module , const  char  * name ,
408450                               Py_hash_type  py_ht )
@@ -464,49 +506,54 @@ get_openssl_evp_md_by_utf8name(PyObject *module, const char *name,
464506        }
465507    }
466508    if  (digest  ==  NULL ) {
509+         // NOTE(picnixz): report hash type value instead of name 
467510        raise_ssl_error (state -> unsupported_digestmod_error ,
468511                        "unsupported hash type %s" , name );
469512        return  NULL ;
470513    }
471514    return  digest ;
472515}
473516
474- /* Get digest EVP_MD from object 
517+ /* 
518+  * Raise an exception indicating that 'digestmod' is not supported. 
519+  */ 
520+ static  void 
521+ raise_unsupported_digestmod_error (PyObject  * module , PyObject  * digestmod )
522+ {
523+     _hashlibstate  * state  =  get_hashlib_state (module );
524+     PyErr_Format (state -> unsupported_digestmod_error ,
525+                  "Unsupported digestmod %R" , digestmod );
526+ }
527+ 
528+ /* 
529+  * Get a new reference to an EVP_MD described by 'digestmod' and purpose. 
530+  * 
531+  * On error, set an exception and return NULL. 
475532 * 
476-  * * string 
477-  * * _hashopenssl builtin function 
533+  * Parameters 
478534 * 
479-  * on error returns NULL with exception set. 
535+  *      digestmod   A digest name or a _hashlib.openssl_* function 
536+  *      py_ht       The message digest purpose. 
480537 */ 
481538static  PY_EVP_MD  * 
482- get_openssl_evp_md (PyObject  * module , PyObject  * digestmod ,
483-                    Py_hash_type  py_ht )
539+ get_openssl_evp_md (PyObject  * module , PyObject  * digestmod , Py_hash_type  py_ht )
484540{
485-     PyObject  * name_obj  =  NULL ;
486541    const  char  * name ;
487- 
488542    if  (PyUnicode_Check (digestmod )) {
489-         name_obj  =  digestmod ;
490-     } else  {
491-         _hashlibstate  * state  =  get_hashlib_state (module );
492-         // borrowed ref 
493-         name_obj  =  PyDict_GetItemWithError (state -> constructs , digestmod );
543+         name  =  PyUnicode_AsUTF8 (digestmod );
494544    }
495-     if  (name_obj  ==  NULL ) {
496-         if  (!PyErr_Occurred ()) {
497-             _hashlibstate  * state  =  get_hashlib_state (module );
498-             PyErr_Format (
499-                 state -> unsupported_digestmod_error ,
500-                 "Unsupported digestmod %R" , digestmod );
501-         }
502-         return  NULL ;
545+     else  {
546+         PyObject  * dict  =  get_hashlib_state (module )-> constructs ;
547+         assert (dict  !=  NULL );
548+         PyObject  * borrowed_ref  =  PyDict_GetItemWithError (dict , digestmod );
549+         name  =  borrowed_ref  ==  NULL  ? NULL  : PyUnicode_AsUTF8 (borrowed_ref );
503550    }
504- 
505-     name  =  PyUnicode_AsUTF8 (name_obj );
506551    if  (name  ==  NULL ) {
552+         if  (!PyErr_Occurred ()) {
553+             raise_unsupported_digestmod_error (module , digestmod );
554+         }
507555        return  NULL ;
508556    }
509- 
510557    return  get_openssl_evp_md_by_utf8name (module , name , py_ht );
511558}
512559
@@ -745,7 +792,9 @@ _hashlib_HASH_get_name(PyObject *op, void *Py_UNUSED(closure))
745792        notify_ssl_error_occurred ();
746793        return  NULL ;
747794    }
748-     return  get_openssl_evp_md_name (md );
795+     const  char  * name  =  get_hashlib_utf8name_by_evp_md (md );
796+     assert (name  !=  NULL  ||  PyErr_Occurred ());
797+     return  name  ==  NULL  ? NULL  : PyUnicode_FromString (name );
749798}
750799
751800static  PyGetSetDef  HASH_getsets [] =  {
@@ -1775,20 +1824,15 @@ _hmac_dealloc(PyObject *op)
17751824static  PyObject  * 
17761825_hmac_repr (PyObject  * op )
17771826{
1827+     const  char  * digest_name ;
17781828    HMACobject  * self  =  HMACobject_CAST (op );
17791829    const  EVP_MD  * md  =  _hashlib_hmac_get_md (self );
1780-     if  (md  ==  NULL ) {
1781-         return  NULL ;
1782-     }
1783-     PyObject  * digest_name  =  get_openssl_evp_md_name (md );
1830+     digest_name  =  md  ==  NULL  ? NULL  : get_hashlib_utf8name_by_evp_md (md );
17841831    if  (digest_name  ==  NULL ) {
1832+         assert (PyErr_Occurred ());
17851833        return  NULL ;
17861834    }
1787-     PyObject  * repr  =  PyUnicode_FromFormat (
1788-         "<%U HMAC object @ %p>" , digest_name , self 
1789-     );
1790-     Py_DECREF (digest_name );
1791-     return  repr ;
1835+     return  PyUnicode_FromFormat ("<%s HMAC object @ %p>" , digest_name , self );
17921836}
17931837
17941838/*[clinic input] 
@@ -1900,13 +1944,12 @@ _hashlib_hmac_get_name(PyObject *op, void *Py_UNUSED(closure))
19001944    if  (md  ==  NULL ) {
19011945        return  NULL ;
19021946    }
1903-     PyObject   * digest_name  =  get_openssl_evp_md_name (md );
1947+     const   char   * digest_name  =  get_hashlib_utf8name_by_evp_md (md );
19041948    if  (digest_name  ==  NULL ) {
1949+         assert (PyErr_Occurred ());
19051950        return  NULL ;
19061951    }
1907-     PyObject  * name  =  PyUnicode_FromFormat ("hmac-%U" , digest_name );
1908-     Py_DECREF (digest_name );
1909-     return  name ;
1952+     return  PyUnicode_FromFormat ("hmac-%s" , digest_name );
19101953}
19111954
19121955static  PyMethodDef  HMAC_methods [] =  {
@@ -1982,7 +2025,9 @@ _openssl_hash_name_mapper(const EVP_MD *md, const char *from,
19822025        return ;
19832026    }
19842027
1985-     py_name  =  get_openssl_evp_md_name (md );
2028+     const  char  * name  =  get_hashlib_utf8name_by_evp_md (md );
2029+     assert (name  !=  NULL  ||  PyErr_Occurred ());
2030+     py_name  =  name  ==  NULL  ? NULL  : PyUnicode_FromString (name );
19862031    if  (py_name  ==  NULL ) {
19872032        state -> error  =  1 ;
19882033    } else  {
0 commit comments