aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/test/support/hashlib_helper.py
blob: 5043f08dd93de65fbbfb115534c064938e684bce (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import functools
import hashlib
import importlib
import unittest
from test.support.import_helper import import_module

try:
    import _hashlib
except ImportError:
    _hashlib = None

try:
    import _hmac
except ImportError:
    _hmac = None


def requires_hashlib():
    return unittest.skipIf(_hashlib is None, "requires _hashlib")


def requires_builtin_hmac():
    return unittest.skipIf(_hmac is None, "requires _hmac")


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.

    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 (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):
        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:
                _hashlib.new(digestname, usedforsecurity=usedforsecurity)
            except ValueError:
                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


def find_gil_minsize(modules_names, default=2048):
    """Get the largest GIL_MINSIZE value for the given cryptographic modules.

    The valid module names are the following:

    - _hashlib
    - _md5, _sha1, _sha2, _sha3, _blake2
    - _hmac
    """
    sizes = []
    for module_name in modules_names:
        try:
            module = importlib.import_module(module_name)
        except ImportError:
            continue
        sizes.append(getattr(module, '_GIL_MINSIZE', default))
    return max(sizes, default=default)