Skip to content

gh-130149: cleanup refactorization of test_hmac.py #131318

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Mar 17, 2025
1 change: 1 addition & 0 deletions Lib/hmac.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def __init__(self, key, msg=None, digestmod=''):

def _init_hmac(self, key, msg, digestmod):
self._hmac = _hashopenssl.hmac_new(key, msg, digestmod=digestmod)
self._inner = self._outer = None # because the slots are defined
self.digest_size = self._hmac.digest_size
self.block_size = self._hmac.block_size

Expand Down
92 changes: 65 additions & 27 deletions Lib/test/support/hashlib_helper.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import functools
import hashlib
import unittest
from test.support.import_helper import import_module

try:
import _hashlib
Expand All @@ -12,44 +13,81 @@ def requires_hashlib():
return unittest.skipIf(_hashlib is None, "requires _hashlib")


def _decorate_func_or_class(func_or_class, decorator_func):
if not isinstance(func_or_class, type):
return decorator_func(func_or_class)

decorated_class = func_or_class
setUpClass = decorated_class.__dict__.get('setUpClass')
if setUpClass is None:
def setUpClass(cls):
super(decorated_class, cls).setUpClass()
setUpClass.__qualname__ = decorated_class.__qualname__ + '.setUpClass'
setUpClass.__module__ = decorated_class.__module__
else:
setUpClass = setUpClass.__func__
setUpClass = classmethod(decorator_func(setUpClass))
decorated_class.setUpClass = setUpClass
return decorated_class


def requires_hashdigest(digestname, openssl=None, usedforsecurity=True):
"""Decorator raising SkipTest if a hashing algorithm is not available
"""Decorator raising SkipTest if a hashing algorithm is not available.

The hashing algorithm could be missing or blocked by a strict crypto
policy.
The hashing algorithm may be missing, blocked by a strict crypto policy,
or Python may be configured with `--with-builtin-hashlib-hashes=no`.

If 'openssl' is True, then the decorator checks that OpenSSL provides
the algorithm. Otherwise the check falls back to built-in
implementations. The usedforsecurity flag is passed to the constructor.
the algorithm. Otherwise the check falls back to (optional) built-in
HACL* implementations.

The usedforsecurity flag is passed to the constructor but has no effect
on HACL* implementations.

Examples of exceptions being suppressed:
ValueError: [digital envelope routines: EVP_DigestInit_ex] disabled for FIPS
ValueError: unsupported hash type md4
"""
if openssl and _hashlib is not None:
def test_availability():
_hashlib.new(digestname, usedforsecurity=usedforsecurity)
else:
def test_availability():
hashlib.new(digestname, usedforsecurity=usedforsecurity)

def decorator_func(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
test_availability()
except ValueError as exc:
msg = f"missing hash algorithm: {digestname!r}"
raise unittest.SkipTest(msg) from exc
return func(*args, **kwargs)
return wrapper

def decorator(func_or_class):
if isinstance(func_or_class, type):
setUpClass = func_or_class.__dict__.get('setUpClass')
if setUpClass is None:
def setUpClass(cls):
super(func_or_class, cls).setUpClass()
setUpClass.__qualname__ = func_or_class.__qualname__ + '.setUpClass'
setUpClass.__module__ = func_or_class.__module__
else:
setUpClass = setUpClass.__func__
setUpClass = classmethod(decorator(setUpClass))
func_or_class.setUpClass = setUpClass
return func_or_class

@functools.wraps(func_or_class)
return _decorate_func_or_class(func_or_class, decorator_func)
return decorator


def requires_openssl_hashdigest(digestname, *, usedforsecurity=True):
"""Decorator raising SkipTest if an OpenSSL hashing algorithm is missing.

The hashing algorithm may be missing or blocked by a strict crypto policy.
"""
def decorator_func(func):
@requires_hashlib()
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
if openssl and _hashlib is not None:
_hashlib.new(digestname, usedforsecurity=usedforsecurity)
else:
hashlib.new(digestname, usedforsecurity=usedforsecurity)
_hashlib.new(digestname, usedforsecurity=usedforsecurity)
except ValueError:
raise unittest.SkipTest(
f"hash digest {digestname!r} is not available."
)
return func_or_class(*args, **kwargs)
msg = f"missing OpenSSL hash algorithm: {digestname!r}"
raise unittest.SkipTest(msg)
return func(*args, **kwargs)
return wrapper

def decorator(func_or_class):
return _decorate_func_or_class(func_or_class, decorator_func)
return decorator
Loading
Loading