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):
25        @functools.wraps(func)
26        def wrapper(*args, **kwargs):
27            try:
28                if openssl and _hashlib is not None:
29                    _hashlib.new(digestname, usedforsecurity=usedforsecurity)
30                else:
31                    hashlib.new(digestname, usedforsecurity=usedforsecurity)
32            except ValueError:
33                raise unittest.SkipTest(
34                    f"hash digest '{digestname}' is not available."
35                )
36            return func(*args, **kwargs)
37        return wrapper
38    return decorator
39