Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 1 addition & 37 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import functools
import gc
import glob
import hashlib
import importlib
import importlib.util
import locale
Expand Down Expand Up @@ -59,11 +58,6 @@
except ImportError:
resource = None

try:
import _hashlib
except ImportError:
_hashlib = None

__all__ = [
# globals
"PIPE_MAX_SIZE", "verbose", "max_memuse", "use_resources", "failfast",
Expand All @@ -81,7 +75,7 @@
"create_empty_file", "can_symlink", "fs_is_case_insensitive",
# unittest
"is_resource_enabled", "requires", "requires_freebsd_version",
"requires_linux_version", "requires_mac_ver", "requires_hashdigest",
"requires_linux_version", "requires_mac_ver",
"check_syntax_error", "check_syntax_warning",
"TransientResource", "time_out", "socket_peer_reset", "ioerror_peer_reset",
"transient_internet", "BasicTestRunner", "run_unittest", "run_doctest",
Expand Down Expand Up @@ -685,36 +679,6 @@ def wrapper(*args, **kw):
return decorator


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):
@functools.wraps(func)
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(*args, **kwargs)
return wrapper
return decorator


def system_must_validate_cert(f):
"""Skip the test on TLS certificate validation failures."""
@functools.wraps(f)
Expand Down
38 changes: 38 additions & 0 deletions Lib/test/support/hashlib_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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):
@functools.wraps(func)
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(*args, **kwargs)
return wrapper
return decorator
1 change: 0 additions & 1 deletion Lib/test/test_hashlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import warnings
from test import support
from test.support import _4G, bigmemtest, import_fresh_module
from test.support import requires_hashdigest
from http.client import HTTPException

# Were we compiled --with-pydebug or with #define Py_DEBUG?
Expand Down
38 changes: 19 additions & 19 deletions Lib/test/test_hmac.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import unittest.mock
import warnings

from test.support import requires_hashdigest
from test.support import hashlib_helper


def ignore_warning(func):
Expand All @@ -21,7 +21,7 @@ def wrapper(*args, **kwargs):

class TestVectorsTestCase(unittest.TestCase):

@requires_hashdigest('md5', openssl=True)
@hashlib_helper.requires_hashdigest('md5', openssl=True)
def test_md5_vectors(self):
# Test the HMAC module against test vectors from the RFC.

Expand Down Expand Up @@ -79,7 +79,7 @@ def md5test(key, data, digest):
b"and Larger Than One Block-Size Data"),
"6f630fad67cda0ee1fb1f562db3aa53e")

@requires_hashdigest('sha1', openssl=True)
@hashlib_helper.requires_hashdigest('sha1', openssl=True)
def test_sha_vectors(self):
def shatest(key, data, digest):
h = hmac.HMAC(key, data, digestmod=hashlib.sha1)
Expand Down Expand Up @@ -272,23 +272,23 @@ def hmactest(key, data, hexdigests):
'134676fb6de0446065c97440fa8c6a58',
})

@requires_hashdigest('sha224', openssl=True)
@hashlib_helper.requires_hashdigest('sha224', openssl=True)
def test_sha224_rfc4231(self):
self._rfc4231_test_cases(hashlib.sha224, 'sha224', 28, 64)

@requires_hashdigest('sha256', openssl=True)
@hashlib_helper.requires_hashdigest('sha256', openssl=True)
def test_sha256_rfc4231(self):
self._rfc4231_test_cases(hashlib.sha256, 'sha256', 32, 64)

@requires_hashdigest('sha384', openssl=True)
@hashlib_helper.requires_hashdigest('sha384', openssl=True)
def test_sha384_rfc4231(self):
self._rfc4231_test_cases(hashlib.sha384, 'sha384', 48, 128)

@requires_hashdigest('sha512', openssl=True)
@hashlib_helper.requires_hashdigest('sha512', openssl=True)
def test_sha512_rfc4231(self):
self._rfc4231_test_cases(hashlib.sha512, 'sha512', 64, 128)

@requires_hashdigest('sha256')
@hashlib_helper.requires_hashdigest('sha256')
def test_legacy_block_size_warnings(self):
class MockCrazyHash(object):
"""Ain't no block_size attribute here."""
Expand Down Expand Up @@ -329,29 +329,29 @@ class ConstructorTestCase(unittest.TestCase):
"6c845b47f52b3b47f6590c502db7825aad757bf4fadc8fa972f7cd2e76a5bdeb"
)

@requires_hashdigest('sha256')
@hashlib_helper.requires_hashdigest('sha256')
def test_normal(self):
# Standard constructor call.
try:
hmac.HMAC(b"key", digestmod='sha256')
except Exception:
self.fail("Standard constructor call raised exception.")

@requires_hashdigest('sha256')
@hashlib_helper.requires_hashdigest('sha256')
def test_with_str_key(self):
# Pass a key of type str, which is an error, because it expects a key
# of type bytes
with self.assertRaises(TypeError):
h = hmac.HMAC("key", digestmod='sha256')

@requires_hashdigest('sha256')
@hashlib_helper.requires_hashdigest('sha256')
def test_dot_new_with_str_key(self):
# Pass a key of type str, which is an error, because it expects a key
# of type bytes
with self.assertRaises(TypeError):
h = hmac.new("key", digestmod='sha256')

@requires_hashdigest('sha256')
@hashlib_helper.requires_hashdigest('sha256')
def test_withtext(self):
# Constructor call with text.
try:
Expand All @@ -360,7 +360,7 @@ def test_withtext(self):
self.fail("Constructor call with text argument raised exception.")
self.assertEqual(h.hexdigest(), self.expected)

@requires_hashdigest('sha256')
@hashlib_helper.requires_hashdigest('sha256')
def test_with_bytearray(self):
try:
h = hmac.HMAC(bytearray(b"key"), bytearray(b"hash this!"),
Expand All @@ -369,15 +369,15 @@ def test_with_bytearray(self):
self.fail("Constructor call with bytearray arguments raised exception.")
self.assertEqual(h.hexdigest(), self.expected)

@requires_hashdigest('sha256')
@hashlib_helper.requires_hashdigest('sha256')
def test_with_memoryview_msg(self):
try:
h = hmac.HMAC(b"key", memoryview(b"hash this!"), digestmod="sha256")
except Exception:
self.fail("Constructor call with memoryview msg raised exception.")
self.assertEqual(h.hexdigest(), self.expected)

@requires_hashdigest('sha256')
@hashlib_helper.requires_hashdigest('sha256')
def test_withmodule(self):
# Constructor call with text and digest module.
try:
Expand All @@ -388,7 +388,7 @@ def test_withmodule(self):

class SanityTestCase(unittest.TestCase):

@requires_hashdigest('sha256')
@hashlib_helper.requires_hashdigest('sha256')
def test_exercise_all_methods(self):
# Exercising all methods once.
# This must not raise any exceptions
Expand All @@ -404,7 +404,7 @@ def test_exercise_all_methods(self):

class CopyTestCase(unittest.TestCase):

@requires_hashdigest('sha256')
@hashlib_helper.requires_hashdigest('sha256')
def test_attributes(self):
# Testing if attributes are of same type.
h1 = hmac.HMAC(b"key", digestmod="sha256")
Expand All @@ -416,7 +416,7 @@ def test_attributes(self):
self.assertEqual(type(h1.outer), type(h2.outer),
"Types of outer don't match.")

@requires_hashdigest('sha256')
@hashlib_helper.requires_hashdigest('sha256')
def test_realcopy(self):
# Testing if the copy method created a real copy.
h1 = hmac.HMAC(b"key", digestmod="sha256")
Expand All @@ -428,7 +428,7 @@ def test_realcopy(self):
self.assertTrue(id(h1.outer) != id(h2.outer),
"No real copy of the attribute 'outer'.")

@requires_hashdigest('sha256')
@hashlib_helper.requires_hashdigest('sha256')
def test_equality(self):
# Testing if the copy has the same digests.
h1 = hmac.HMAC(b"key", digestmod="sha256")
Expand Down
10 changes: 5 additions & 5 deletions Lib/test/test_imaplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
import socket

from test.support import (reap_threads, verbose, transient_internet,
run_with_tz, run_with_locale, cpython_only,
requires_hashdigest)
run_with_tz, run_with_locale, cpython_only)
from test.support import hashlib_helper
import unittest
from unittest import mock
from datetime import datetime, timezone, timedelta
Expand Down Expand Up @@ -372,7 +372,7 @@ def cmd_AUTHENTICATE(self, tag, args):
self.assertEqual(code, 'OK')
self.assertEqual(server.response, b'ZmFrZQ==\r\n') # b64 encoded 'fake'

@requires_hashdigest('md5')
@hashlib_helper.requires_hashdigest('md5')
def test_login_cram_md5_bytes(self):
class AuthHandler(SimpleIMAPHandler):
capabilities = 'LOGINDISABLED AUTH=CRAM-MD5'
Expand All @@ -390,7 +390,7 @@ def cmd_AUTHENTICATE(self, tag, args):
ret, _ = client.login_cram_md5("tim", b"tanstaaftanstaaf")
self.assertEqual(ret, "OK")

@requires_hashdigest('md5')
@hashlib_helper.requires_hashdigest('md5')
def test_login_cram_md5_plain_text(self):
class AuthHandler(SimpleIMAPHandler):
capabilities = 'LOGINDISABLED AUTH=CRAM-MD5'
Expand Down Expand Up @@ -824,7 +824,7 @@ def cmd_AUTHENTICATE(self, tag, args):
b'ZmFrZQ==\r\n') # b64 encoded 'fake'

@reap_threads
@requires_hashdigest('md5')
@hashlib_helper.requires_hashdigest('md5')
def test_login_cram_md5(self):

class AuthHandler(SimpleIMAPHandler):
Expand Down
5 changes: 3 additions & 2 deletions Lib/test/test_poplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from unittest import TestCase, skipUnless
from test import support as test_support
from test.support import hashlib_helper
from test.support import socket_helper

HOST = socket_helper.HOST
Expand Down Expand Up @@ -311,11 +312,11 @@ def test_noop(self):
def test_rpop(self):
self.assertOK(self.client.rpop('foo'))

@test_support.requires_hashdigest('md5')
@hashlib_helper.requires_hashdigest('md5')
def test_apop_normal(self):
self.assertOK(self.client.apop('foo', 'dummypassword'))

@test_support.requires_hashdigest('md5')
@hashlib_helper.requires_hashdigest('md5')
def test_apop_REDOS(self):
# Replace welcome with very long evil welcome.
# NB The upper bound on welcome length is currently 2048.
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_smtplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@

import unittest
from test import support, mock_socket
from test.support import hashlib_helper
from test.support import socket_helper
from test.support import threading_setup, threading_cleanup, join_thread
from test.support import requires_hashdigest
from unittest.mock import Mock

HOST = socket_helper.HOST
Expand Down Expand Up @@ -1058,7 +1058,7 @@ def testAUTH_LOGIN(self):
self.assertEqual(resp, (235, b'Authentication Succeeded'))
smtp.close()

@requires_hashdigest('md5')
@hashlib_helper.requires_hashdigest('md5')
def testAUTH_CRAM_MD5(self):
self.serv.add_feature("AUTH CRAM-MD5")
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost',
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_tarfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import tarfile

from test import support
from test.support import script_helper, requires_hashdigest
from test.support import script_helper

# Check for our compression modules.
try:
Expand Down
3 changes: 2 additions & 1 deletion Lib/test/test_urllib2_localnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import hashlib

from test import support
from test.support import hashlib_helper

try:
import ssl
Expand Down Expand Up @@ -322,7 +323,7 @@ class ProxyAuthTests(unittest.TestCase):
PASSWD = "test123"
REALM = "TestRealm"

@support.requires_hashdigest("md5")
@hashlib_helper.requires_hashdigest("md5")
def setUp(self):
super(ProxyAuthTests, self).setUp()
# Ignore proxy bypass settings in the environment.
Expand Down