@@ -368,41 +368,83 @@ notify_ssl_error_occurred(void)
368
368
}
369
369
/* LCOV_EXCL_STOP */
370
370
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
+ */
378
385
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 ++ ) {
380
391
if (h -> ossl_nid == nid ) {
381
- name = h -> py_name ;
382
- break ;
392
+ return h ;
383
393
}
384
394
}
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 );
385
407
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
+ }
394
419
}
395
420
return name ;
396
421
}
397
422
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 )
400
438
{
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 ) );
403
441
}
404
442
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
+ */
406
448
static PY_EVP_MD *
407
449
get_openssl_evp_md_by_utf8name (PyObject * module , const char * name ,
408
450
Py_hash_type py_ht )
@@ -471,42 +513,46 @@ get_openssl_evp_md_by_utf8name(PyObject *module, const char *name,
471
513
return digest ;
472
514
}
473
515
474
- /* Get digest EVP_MD from object
516
+ /*
517
+ * Raise an exception indicating that 'digestmod' is not supported.
518
+ */
519
+ static void
520
+ raise_unsupported_digestmod_error (PyObject * module , PyObject * digestmod )
521
+ {
522
+ _hashlibstate * state = get_hashlib_state (module );
523
+ PyErr_Format (state -> unsupported_digestmod_error ,
524
+ "Unsupported digestmod %R" , digestmod );
525
+ }
526
+
527
+ /*
528
+ * Get a new reference to an EVP_MD described by 'digestmod' and purpose.
529
+ *
530
+ * On error, set an exception and return NULL.
475
531
*
476
- * * string
477
- * * _hashopenssl builtin function
532
+ * Parameters
478
533
*
479
- * on error returns NULL with exception set.
534
+ * digestmod A digest name or a _hashopenssl builtin function
535
+ * py_ht The message digest purpose.
480
536
*/
481
537
static PY_EVP_MD *
482
- get_openssl_evp_md (PyObject * module , PyObject * digestmod ,
483
- Py_hash_type py_ht )
538
+ get_openssl_evp_md (PyObject * module , PyObject * digestmod , Py_hash_type py_ht )
484
539
{
485
- PyObject * name_obj = NULL ;
486
540
const char * name ;
487
-
488
541
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 );
542
+ name = PyUnicode_AsUTF8 (digestmod );
494
543
}
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 ;
544
+ else {
545
+ PyObject * dict = get_hashlib_state (module )-> constructs ;
546
+ assert (dict != NULL );
547
+ PyObject * borrowed_ref = PyDict_GetItemWithError (dict , digestmod );
548
+ name = borrowed_ref == NULL ? NULL : PyUnicode_AsUTF8 (borrowed_ref );
503
549
}
504
-
505
- name = PyUnicode_AsUTF8 (name_obj );
506
550
if (name == NULL ) {
551
+ if (!PyErr_Occurred ()) {
552
+ raise_unsupported_digestmod_error (module , digestmod );
553
+ }
507
554
return NULL ;
508
555
}
509
-
510
556
return get_openssl_evp_md_by_utf8name (module , name , py_ht );
511
557
}
512
558
@@ -745,7 +791,9 @@ _hashlib_HASH_get_name(PyObject *op, void *Py_UNUSED(closure))
745
791
notify_ssl_error_occurred ();
746
792
return NULL ;
747
793
}
748
- return get_openssl_evp_md_name (md );
794
+ const char * name = get_hashlib_utf8name_by_evp_md (md );
795
+ assert (name != NULL || PyErr_Occurred ());
796
+ return name == NULL ? NULL : PyUnicode_FromString (name );
749
797
}
750
798
751
799
static PyGetSetDef HASH_getsets [] = {
@@ -1775,20 +1823,15 @@ _hmac_dealloc(PyObject *op)
1775
1823
static PyObject *
1776
1824
_hmac_repr (PyObject * op )
1777
1825
{
1826
+ const char * digest_name ;
1778
1827
HMACobject * self = HMACobject_CAST (op );
1779
1828
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 );
1829
+ digest_name = md == NULL ? NULL : get_hashlib_utf8name_by_evp_md (md );
1784
1830
if (digest_name == NULL ) {
1831
+ assert (PyErr_Occurred ());
1785
1832
return NULL ;
1786
1833
}
1787
- PyObject * repr = PyUnicode_FromFormat (
1788
- "<%U HMAC object @ %p>" , digest_name , self
1789
- );
1790
- Py_DECREF (digest_name );
1791
- return repr ;
1834
+ return PyUnicode_FromFormat ("<%s HMAC object @ %p>" , digest_name , self );
1792
1835
}
1793
1836
1794
1837
/*[clinic input]
@@ -1900,13 +1943,12 @@ _hashlib_hmac_get_name(PyObject *op, void *Py_UNUSED(closure))
1900
1943
if (md == NULL ) {
1901
1944
return NULL ;
1902
1945
}
1903
- PyObject * digest_name = get_openssl_evp_md_name (md );
1946
+ const char * digest_name = get_hashlib_utf8name_by_evp_md (md );
1904
1947
if (digest_name == NULL ) {
1948
+ assert (PyErr_Occurred ());
1905
1949
return NULL ;
1906
1950
}
1907
- PyObject * name = PyUnicode_FromFormat ("hmac-%U" , digest_name );
1908
- Py_DECREF (digest_name );
1909
- return name ;
1951
+ return PyUnicode_FromFormat ("hmac-%s" , digest_name );
1910
1952
}
1911
1953
1912
1954
static PyMethodDef HMAC_methods [] = {
@@ -1982,7 +2024,9 @@ _openssl_hash_name_mapper(const EVP_MD *md, const char *from,
1982
2024
return ;
1983
2025
}
1984
2026
1985
- py_name = get_openssl_evp_md_name (md );
2027
+ const char * name = get_hashlib_utf8name_by_evp_md (md );
2028
+ assert (name != NULL || PyErr_Occurred ());
2029
+ py_name = name == NULL ? NULL : PyUnicode_FromString (name );
1986
2030
if (py_name == NULL ) {
1987
2031
state -> error = 1 ;
1988
2032
} else {
0 commit comments