1import functools 2import hashlib 3import unittest 4 5try: 6 import _hashlib 7except ImportError: 8 _hashlib = None 9 10 11def requires_hashdigest(digestname, openssl=None, usedforsecurity=True): 12 """Decorator raising SkipTest if a hashing algorithm is not available 13 14 The hashing algorithm could be missing or blocked by a strict crypto 15 policy. 16 17 If 'openssl' is True, then the decorator checks that OpenSSL provides 18 the algorithm. Otherwise the check falls back to built-in 19 implementations. The usedforsecurity flag is passed to the constructor. 20 21 ValueError: [digital envelope routines: EVP_DigestInit_ex] disabled for FIPS 22 ValueError: unsupported hash type md4 23 """ 24 def decorator(func_or_class): 25 if isinstance(func_or_class, type): 26 setUpClass = func_or_class.__dict__.get('setUpClass') 27 if setUpClass is None: 28 def setUpClass(cls): 29 super(func_or_class, cls).setUpClass() 30 setUpClass.__qualname__ = func_or_class.__qualname__ + '.setUpClass' 31 setUpClass.__module__ = func_or_class.__module__ 32 else: 33 setUpClass = setUpClass.__func__ 34 setUpClass = classmethod(decorator(setUpClass)) 35 func_or_class.setUpClass = setUpClass 36 return func_or_class 37 38 @functools.wraps(func_or_class) 39 def wrapper(*args, **kwargs): 40 try: 41 if openssl and _hashlib is not None: 42 _hashlib.new(digestname, usedforsecurity=usedforsecurity) 43 else: 44 hashlib.new(digestname, usedforsecurity=usedforsecurity) 45 except ValueError: 46 raise unittest.SkipTest( 47 f"hash digest '{digestname}' is not available." 48 ) 49 return func_or_class(*args, **kwargs) 50 return wrapper 51 return decorator 52