| import functools |
| import hashlib |
| import unittest |
| |
| try: |
| import _hashlib |
| except ImportError: |
| _hashlib = None |
| |
| |
| def requires_hashdigest(digestname, openssl=None, usedforsecurity=True): |
| """Decorator raising SkipTest if a hashing algorithm is not available |
| |
| The hashing algorithm could be missing or blocked by a strict crypto |
| policy. |
| |
| 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. |
| |
| ValueError: [digital envelope routines: EVP_DigestInit_ex] disabled for FIPS |
| ValueError: unsupported hash type md4 |
| """ |
| 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) |
| def wrapper(*args, **kwargs): |
| try: |
| if openssl and _hashlib is not None: |
| _hashlib.new(digestname, usedforsecurity=usedforsecurity) |
| else: |
| hashlib.new(digestname, usedforsecurity=usedforsecurity) |
| except ValueError: |
| raise unittest.SkipTest( |
| f"hash digest '{digestname}' is not available." |
| ) |
| return func_or_class(*args, **kwargs) |
| return wrapper |
| return decorator |