summaryrefslogtreecommitdiffstats
path: root/Lib/test/support/hashlib_helper.py
blob: bed3d696cb384dd02c7cf09f59f1245773868e47 (plain)
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
import functools
import hashlib
import unittest
from test.support.import_helper import import_module

try:
    import _hashlib
except ImportError:
    _hashlib = None


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


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