1 import functools
2 import hashlib
3 import unittest
4
5 try:
6 import _hashlib
7 except ImportError:
8 _hashlib = None
9
10
11 def 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