From 21b30a1d6fa394dc81c2d70686c4acd6cb37934f Mon Sep 17 00:00:00 2001 From: Dimitri John Ledkov Date: Tue, 26 Nov 2024 15:14:31 +0000 Subject: [PATCH 01/13] gh-127298: When in FIPS mode ensure builtin hashes check for usedforsecurity=False When _hashlib/OpenSSL is available, and OpenSSL is in FIPS mode, ensure that builtin (fallback) hash implementations are wrapped with a check for usedforsecurity=False. It is likely that buitin implementations are FIPS unapproved (either algorithm disallowed; or the implementation not certified by NIST). This enables strict approved-only compliance when usedforsecurity=True on FIPS systems only. And yet it also enables fallback access with usedforsecurity=False for any missing (historical, disallowed or missing certified implementation) algorithms (i.e. blake2, md5, shake/sha3) depending on the runtime configuration of OpenSSL. --- Lib/hashlib.py | 45 +++++++++----- Lib/test/hashlibdata/openssl.cnf | 19 ++++++ Lib/test/ssltests.py | 2 +- Lib/test/test_hashlib_fips.py | 59 +++++++++++++++++++ Makefile.pre.in | 1 + ...-11-26-16-31-40.gh-issue-127298.jqYJvn.rst | 4 ++ 6 files changed, 115 insertions(+), 15 deletions(-) create mode 100644 Lib/test/hashlibdata/openssl.cnf create mode 100644 Lib/test/test_hashlib_fips.py create mode 100644 Misc/NEWS.d/next/Library/2024-11-26-16-31-40.gh-issue-127298.jqYJvn.rst diff --git a/Lib/hashlib.py b/Lib/hashlib.py index 6c73eb9f31f8e4..8b7cf8171f39fc 100644 --- a/Lib/hashlib.py +++ b/Lib/hashlib.py @@ -79,6 +79,23 @@ 'blake2b', 'blake2s', } +# Wrapper that only allows usage when usedforsecurity=False +# (effectively unapproved service indicator) +def __usedforsecurity_check(md, name, *args, **kwargs): + if kwargs.get("usedforsecurity", True): + raise ValueError(name + " is blocked when usedforsecurity=True") + return md(*args, **kwargs) + +# If _hashlib is in FIPS mode, use the above wrapper to ensure builtin +# implementation checks usedforsecurity kwarg. It means all builtin +# implementations are treated as an unapproved implementation, as they +# are unlikely to have been certified by NIST. +def __get_wrapped_builtin(md, name): + if _hashlib is not None and _hashlib.get_fips_mode() == 1: + from functools import partial + return partial(__usedforsecurity_check, md, name) + return md + def __get_builtin_constructor(name): if not isinstance(name, str): # Since this function is only used by new(), we use the same @@ -92,32 +109,32 @@ def __get_builtin_constructor(name): try: if name in {'SHA1', 'sha1'}: import _sha1 - cache['SHA1'] = cache['sha1'] = _sha1.sha1 + cache['SHA1'] = cache['sha1'] = __get_wrapped_builtin(_sha1.sha1, name) elif name in {'MD5', 'md5'}: import _md5 - cache['MD5'] = cache['md5'] = _md5.md5 + cache['MD5'] = cache['md5'] = __get_wrapped_builtin(_md5.md5, name) elif name in {'SHA256', 'sha256', 'SHA224', 'sha224'}: import _sha2 - cache['SHA224'] = cache['sha224'] = _sha2.sha224 - cache['SHA256'] = cache['sha256'] = _sha2.sha256 + cache['SHA224'] = cache['sha224'] = __get_wrapped_builtin(_sha2.sha224, name) + cache['SHA256'] = cache['sha256'] = __get_wrapped_builtin(_sha2.sha256, name) elif name in {'SHA512', 'sha512', 'SHA384', 'sha384'}: import _sha2 - cache['SHA384'] = cache['sha384'] = _sha2.sha384 - cache['SHA512'] = cache['sha512'] = _sha2.sha512 + cache['SHA384'] = cache['sha384'] = __get_wrapped_builtin(_sha2.sha384, name) + cache['SHA512'] = cache['sha512'] = __get_wrapped_builtin(_sha2.sha512, name) elif name in {'blake2b', 'blake2s'}: import _blake2 - cache['blake2b'] = _blake2.blake2b - cache['blake2s'] = _blake2.blake2s + cache['blake2b'] = __get_wrapped_builtin(_blake2.blake2b, name) + cache['blake2s'] = __get_wrapped_builtin(_blake2.blake2s, name) elif name in {'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512'}: import _sha3 - cache['sha3_224'] = _sha3.sha3_224 - cache['sha3_256'] = _sha3.sha3_256 - cache['sha3_384'] = _sha3.sha3_384 - cache['sha3_512'] = _sha3.sha3_512 + cache['sha3_224'] = __get_wrapped_builtin(_sha3.sha3_224, name) + cache['sha3_256'] = __get_wrapped_builtin(_sha3.sha3_256, name) + cache['sha3_384'] = __get_wrapped_builtin(_sha3.sha3_384, name) + cache['sha3_512'] = __get_wrapped_builtin(_sha3.sha3_512, name) elif name in {'shake_128', 'shake_256'}: import _sha3 - cache['shake_128'] = _sha3.shake_128 - cache['shake_256'] = _sha3.shake_256 + cache['shake_128'] = __get_wrapped_builtin(_sha3.shake_128, name) + cache['shake_256'] = __get_wrapped_builtin(_sha3.shake_256, name) except ImportError: pass # no extension module, this hash is unsupported. diff --git a/Lib/test/hashlibdata/openssl.cnf b/Lib/test/hashlibdata/openssl.cnf new file mode 100644 index 00000000000000..9a936ddc5effb8 --- /dev/null +++ b/Lib/test/hashlibdata/openssl.cnf @@ -0,0 +1,19 @@ +# Activate base provider only, with default properties fips=yes. It +# means that fips mode is on, and no digest implementations are +# available. Perfect for mock testing builtin FIPS wrappers. + +config_diagnostics = 1 +openssl_conf = openssl_init + +[openssl_init] +providers = provider_sect +alg_section = algorithm_sect + +[provider_sect] +base = base_sect + +[base_sect] +activate = 1 + +[algorithm_sect] +default_properties = fips=yes diff --git a/Lib/test/ssltests.py b/Lib/test/ssltests.py index ee03aed5cca532..0ecd82658601cb 100644 --- a/Lib/test/ssltests.py +++ b/Lib/test/ssltests.py @@ -7,7 +7,7 @@ TESTS = [ 'test_asyncio', 'test_ensurepip.py', 'test_ftplib', 'test_hashlib', - 'test_hmac', 'test_httplib', 'test_imaplib', + 'test_hashlib_fips', 'test_hmac', 'test_httplib', 'test_imaplib', 'test_poplib', 'test_ssl', 'test_smtplib', 'test_smtpnet', 'test_urllib2_localnet', 'test_venv', 'test_xmlrpc' ] diff --git a/Lib/test/test_hashlib_fips.py b/Lib/test/test_hashlib_fips.py new file mode 100644 index 00000000000000..8cf469df882cde --- /dev/null +++ b/Lib/test/test_hashlib_fips.py @@ -0,0 +1,59 @@ +# Test the hashlib module usedforsecurity wrappers under fips. +# +# Copyright (C) 2024 Dimitri John Ledkov (dimitri.ledkov@surgut.co.uk) +# Licensed to PSF under a Contributor Agreement. +# + +import os +import unittest + +OPENSSL_CONF_BACKUP = os.environ.get("OPENSSL_CONF") + +class HashLibFIPSTestCase(unittest.TestCase): + @classmethod + def setUpClass(cls): + # This openssl.cnf mocks FIPS mode without any digest + # loaded. It means all digests must raise ValueError when + # usedforsecurity=True via either openssl or builtin + # constructors + OPENSSL_CONF = os.path.join(os.path.dirname(__file__), "hashlibdata", "openssl.cnf") + os.environ["OPENSSL_CONF"] = OPENSSL_CONF + # Ensure hashlib is loading a fresh libcrypto with openssl + # context affected by the above config file. Check if this can + # be folded into test_hashlib.py, specifically if + # import_fresh_module() results in a fresh library context + import hashlib + + def setUp(self): + try: + from _hashlib import get_fips_mode + except ImportError: + self.skipTest('_hashlib not available') + + if get_fips_mode() != 1: + self.skipTest('mocking fips mode failed') + + @classmethod + def tearDownClass(cls): + if OPENSSL_CONF_BACKUP: + os.environ["OPENSSL_CONF"] = OPENSSL_CONF_BACKUP + else: + del(os.environ["OPENSSL_CONF"]) + + def test_algorithms_available(self): + import hashlib + self.assertTrue(set(hashlib.algorithms_guaranteed). + issubset(hashlib.algorithms_available)) + # all available algorithms must be loadable, bpo-47101 + self.assertNotIn("undefined", hashlib.algorithms_available) + for name in hashlib.algorithms_available: + digest = hashlib.new(name, usedforsecurity=False) + + def test_usedforsecurity_true(self): + import hashlib + for name in hashlib.algorithms_available: + with self.assertRaises(ValueError): + digest = hashlib.new(name, usedforsecurity=True) + +if __name__ == "__main__": + unittest.main() diff --git a/Makefile.pre.in b/Makefile.pre.in index edc6c5aa812e27..a14dcd06e3d62e 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2600,6 +2600,7 @@ TESTSUBDIRS= idlelib/idle_test \ test/decimaltestdata \ test/dtracedata \ test/encoded_modules \ + test/hashlibdata \ test/leakers \ test/libregrtest \ test/mathdata \ diff --git a/Misc/NEWS.d/next/Library/2024-11-26-16-31-40.gh-issue-127298.jqYJvn.rst b/Misc/NEWS.d/next/Library/2024-11-26-16-31-40.gh-issue-127298.jqYJvn.rst new file mode 100644 index 00000000000000..96c99bceee5860 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-26-16-31-40.gh-issue-127298.jqYJvn.rst @@ -0,0 +1,4 @@ +:mod:`hashlib`'s fallback builtin hash implementations now check +usedforsecurity=False, when hashlib is in FIPS mode. This ensures that +approved-only implementations are in use on FIPS systems by default. +The builtin implemenations are made available for unapproved use only. From 42b912a0477c721969261ce9c5e99ac4ab3988b9 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Tue, 26 Nov 2024 22:54:49 -0800 Subject: [PATCH 02/13] use != 0 rather than == 1 and update a comment. --- Lib/hashlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/hashlib.py b/Lib/hashlib.py index 8b7cf8171f39fc..24ef35d30548cd 100644 --- a/Lib/hashlib.py +++ b/Lib/hashlib.py @@ -91,7 +91,7 @@ def __usedforsecurity_check(md, name, *args, **kwargs): # implementations are treated as an unapproved implementation, as they # are unlikely to have been certified by NIST. def __get_wrapped_builtin(md, name): - if _hashlib is not None and _hashlib.get_fips_mode() == 1: + if _hashlib is not None and _hashlib.get_fips_mode() != 0: from functools import partial return partial(__usedforsecurity_check, md, name) return md From ac9dc6d18da3ecd35c6681ac2b9868cbf5519361 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Tue, 26 Nov 2024 23:05:12 -0800 Subject: [PATCH 03/13] Update the NEWS to reflect reality. FIPS mode is an OpenSSL feature and we don't require OpenSSL. So anyone wanting to rely on this will need to ensure their build includes Modules/_hashopenssl.c as `_hashlib` linked appropriately. --- .../2024-11-26-16-31-40.gh-issue-127298.jqYJvn.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2024-11-26-16-31-40.gh-issue-127298.jqYJvn.rst b/Misc/NEWS.d/next/Library/2024-11-26-16-31-40.gh-issue-127298.jqYJvn.rst index 96c99bceee5860..c209bca12253a4 100644 --- a/Misc/NEWS.d/next/Library/2024-11-26-16-31-40.gh-issue-127298.jqYJvn.rst +++ b/Misc/NEWS.d/next/Library/2024-11-26-16-31-40.gh-issue-127298.jqYJvn.rst @@ -1,4 +1,8 @@ -:mod:`hashlib`'s fallback builtin hash implementations now check -usedforsecurity=False, when hashlib is in FIPS mode. This ensures that -approved-only implementations are in use on FIPS systems by default. -The builtin implemenations are made available for unapproved use only. +:mod:`hashlib`'s builtin hash implementations now check ``usedforsecurity=False``, +when the OpenSSL library default provider is in OpenSSL 3's FIPS mode. This helps +ensure that only US FIPS approved implementations are in use by default on systems +configured as such. + +This is only active when :mod:`hashlib` has been built with OpenSSL implementation +support and said OpenSSL library includes the FIPS mode feature. Not all variants +do, and OpenSSL is not a *required* build time dependency of ``hashlib``. From 4da9f775aea3b9553e7a44e55c54183ad73f7f02 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith [Google LLC]" Date: Wed, 27 Nov 2024 07:57:55 +0000 Subject: [PATCH 04/13] address _some_ correctness details in the test. --- Lib/test/test_hashlib_fips.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_hashlib_fips.py b/Lib/test/test_hashlib_fips.py index 8cf469df882cde..13ed81a1c88adb 100644 --- a/Lib/test/test_hashlib_fips.py +++ b/Lib/test/test_hashlib_fips.py @@ -5,13 +5,17 @@ # import os +import sys import unittest OPENSSL_CONF_BACKUP = os.environ.get("OPENSSL_CONF") + class HashLibFIPSTestCase(unittest.TestCase): @classmethod def setUpClass(cls): + if sys.modules.get("_hashlib") or sys.modules.get("_ssl"): + raise AssertionError("_hashlib or _ssl already imported, too late to change OPENSSL_CONF.") # This openssl.cnf mocks FIPS mode without any digest # loaded. It means all digests must raise ValueError when # usedforsecurity=True via either openssl or builtin @@ -35,10 +39,10 @@ def setUp(self): @classmethod def tearDownClass(cls): - if OPENSSL_CONF_BACKUP: + if OPENSSL_CONF_BACKUP is not None: os.environ["OPENSSL_CONF"] = OPENSSL_CONF_BACKUP else: - del(os.environ["OPENSSL_CONF"]) + os.environ.pop("OPENSSL_CONF", None) def test_algorithms_available(self): import hashlib From 7a954ff0b5b87a65c19bab6c30bcc9c7544972d5 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith [Google LLC]" Date: Wed, 27 Nov 2024 07:58:29 +0000 Subject: [PATCH 05/13] avoid repeated calls, reword comment. --- Lib/hashlib.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Lib/hashlib.py b/Lib/hashlib.py index 24ef35d30548cd..82b552a42c6308 100644 --- a/Lib/hashlib.py +++ b/Lib/hashlib.py @@ -86,12 +86,12 @@ def __usedforsecurity_check(md, name, *args, **kwargs): raise ValueError(name + " is blocked when usedforsecurity=True") return md(*args, **kwargs) -# If _hashlib is in FIPS mode, use the above wrapper to ensure builtin -# implementation checks usedforsecurity kwarg. It means all builtin -# implementations are treated as an unapproved implementation, as they -# are unlikely to have been certified by NIST. +# If the _hashlib OpenSSL wrapper is in FIPS mode, wrap other implementations +# to check the usedforsecurity kwarg. All builtin implementations are treated +# as only available for useforsecurity=False purposes in the presence of such +# a configured and linked OpenSSL. def __get_wrapped_builtin(md, name): - if _hashlib is not None and _hashlib.get_fips_mode() != 0: + if __openssl_fips_mode != 0: from functools import partial return partial(__usedforsecurity_check, md, name) return md @@ -209,10 +209,15 @@ def __hash_new(name, *args, **kwargs): __get_hash = __get_openssl_constructor algorithms_available = algorithms_available.union( _hashlib.openssl_md_meth_names) + try: + __openssl_fips_mode = _hashlib.get_fips_mode() + except ValueError: + __openssl_fips_mode = 0 except ImportError: _hashlib = None new = __py_new __get_hash = __get_builtin_constructor + __openssl_fips_mode = 0 try: # OpenSSL's PKCS5_PBKDF2_HMAC requires OpenSSL 1.0+ with HMAC and SHA From 461920d149bf7d8919596c60c8e054f02611e07f Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Wed, 27 Nov 2024 00:02:42 -0800 Subject: [PATCH 06/13] Update 2024-11-26-16-31-40.gh-issue-127298.jqYJvn.rst --- .../next/Library/2024-11-26-16-31-40.gh-issue-127298.jqYJvn.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2024-11-26-16-31-40.gh-issue-127298.jqYJvn.rst b/Misc/NEWS.d/next/Library/2024-11-26-16-31-40.gh-issue-127298.jqYJvn.rst index c209bca12253a4..e555661a195073 100644 --- a/Misc/NEWS.d/next/Library/2024-11-26-16-31-40.gh-issue-127298.jqYJvn.rst +++ b/Misc/NEWS.d/next/Library/2024-11-26-16-31-40.gh-issue-127298.jqYJvn.rst @@ -1,5 +1,5 @@ :mod:`hashlib`'s builtin hash implementations now check ``usedforsecurity=False``, -when the OpenSSL library default provider is in OpenSSL 3's FIPS mode. This helps +when the OpenSSL library default provider is in OpenSSL's FIPS mode. This helps ensure that only US FIPS approved implementations are in use by default on systems configured as such. From dabed52d66bfea3a89fe37ae741c2027b142c20a Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith [Google LLC]" Date: Wed, 27 Nov 2024 08:44:29 +0000 Subject: [PATCH 07/13] avoid rerunning test in refleak hunting mode. --- Lib/test/test_hashlib_fips.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Lib/test/test_hashlib_fips.py b/Lib/test/test_hashlib_fips.py index 13ed81a1c88adb..bab27d6ba29de6 100644 --- a/Lib/test/test_hashlib_fips.py +++ b/Lib/test/test_hashlib_fips.py @@ -12,8 +12,12 @@ class HashLibFIPSTestCase(unittest.TestCase): + _executions = 0 # prevent re-running on in refleak hunting mode, etc. + @classmethod def setUpClass(cls): + if cls._executions > 0: + raise unittest.SkipTest("Cannot run this test within the same Python process.") if sys.modules.get("_hashlib") or sys.modules.get("_ssl"): raise AssertionError("_hashlib or _ssl already imported, too late to change OPENSSL_CONF.") # This openssl.cnf mocks FIPS mode without any digest @@ -43,6 +47,7 @@ def tearDownClass(cls): os.environ["OPENSSL_CONF"] = OPENSSL_CONF_BACKUP else: os.environ.pop("OPENSSL_CONF", None) + cls._executions += 1 def test_algorithms_available(self): import hashlib From 57241dbb2bd9bb74d36827f53e555e202cc42b4d Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith [Google LLC]" Date: Wed, 27 Nov 2024 09:55:07 +0000 Subject: [PATCH 08/13] Change to spawn _test_hashlib_fips from test_hashlib with an env. --- .../{test_hashlib_fips.py => _test_hashlib_fips.py} | 10 ++-------- Lib/test/support/script_helper.py | 11 +++++++++-- Lib/test/test_hashlib.py | 13 +++++++++++++ 3 files changed, 24 insertions(+), 10 deletions(-) rename Lib/test/{test_hashlib_fips.py => _test_hashlib_fips.py} (84%) diff --git a/Lib/test/test_hashlib_fips.py b/Lib/test/_test_hashlib_fips.py similarity index 84% rename from Lib/test/test_hashlib_fips.py rename to Lib/test/_test_hashlib_fips.py index bab27d6ba29de6..a4fb3f40dac471 100644 --- a/Lib/test/test_hashlib_fips.py +++ b/Lib/test/_test_hashlib_fips.py @@ -4,22 +4,17 @@ # Licensed to PSF under a Contributor Agreement. # +"""Primarily executed by test_hashlib.py. It can run stand alone by humans.""" + import os -import sys import unittest OPENSSL_CONF_BACKUP = os.environ.get("OPENSSL_CONF") class HashLibFIPSTestCase(unittest.TestCase): - _executions = 0 # prevent re-running on in refleak hunting mode, etc. - @classmethod def setUpClass(cls): - if cls._executions > 0: - raise unittest.SkipTest("Cannot run this test within the same Python process.") - if sys.modules.get("_hashlib") or sys.modules.get("_ssl"): - raise AssertionError("_hashlib or _ssl already imported, too late to change OPENSSL_CONF.") # This openssl.cnf mocks FIPS mode without any digest # loaded. It means all digests must raise ValueError when # usedforsecurity=True via either openssl or builtin @@ -47,7 +42,6 @@ def tearDownClass(cls): os.environ["OPENSSL_CONF"] = OPENSSL_CONF_BACKUP else: os.environ.pop("OPENSSL_CONF", None) - cls._executions += 1 def test_algorithms_available(self): import hashlib diff --git a/Lib/test/support/script_helper.py b/Lib/test/support/script_helper.py index 46ce950433ddf6..7b83c8e06326d3 100644 --- a/Lib/test/support/script_helper.py +++ b/Lib/test/support/script_helper.py @@ -303,7 +303,14 @@ def make_zip_pkg(zip_dir, zip_basename, pkg_name, script_basename, @support.requires_subprocess() -def run_test_script(script): +def run_test_script(script, **kwargs): + """Run the file *script* in a child interpreter. + + Keyword arguments are passed on to subprocess.run() within. + + Asserts if the child exits non-zero. Prints child output after + execution when run in verbose mode. + """ # use -u to try to get the full output if the test hangs or crash if support.verbose: def title(text): @@ -315,7 +322,7 @@ def title(text): # In verbose mode, the child process inherit stdout and stdout, # to see output in realtime and reduce the risk of losing output. args = [sys.executable, "-E", "-X", "faulthandler", "-u", script, "-v"] - proc = subprocess.run(args) + proc = subprocess.run(args, **kwargs) print(title(f"{name} completed: exit code {proc.returncode}"), flush=True) if proc.returncode: diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py index 33845d8a9e2651..4845cbedb2a4b1 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -19,6 +19,7 @@ import threading import unittest from test import support +from test.support import os_helper, script_helper from test.support import _4G, bigmemtest from test.support import hashlib_helper from test.support.import_helper import import_fresh_module @@ -1387,6 +1388,18 @@ def scrypt(password=b"password", /, **kwargs): MAX_DKLEN = ((1 << 32) - 1) * 32 # see RFC 7914 self.assertRaises(numeric_exc_types, scrypt, dklen=MAX_DKLEN + 1) + def test_builtins_in_openssl_fips_mode(self): + try: + from _hashlib import get_fips_mode + except ImportError: + self.skipTest('OpenSSL _hashlib not available') + from test import _test_hashlib_fips + child_test_path = _test_hashlib_fips.__file__ + env = os.environ.copy() + # A config to mock FIPS mode, see _test_hashlib_fips.py. + env["OPENSSL_CONF"] = os.path.join(os.path.dirname(__file__), "hashlibdata", "openssl.cnf") + script_helper.run_test_script(child_test_path, env=env) + if __name__ == "__main__": unittest.main() From 3910723fde6bd4edc161925b5943c146c77ff4c5 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith [Google LLC]" Date: Wed, 27 Nov 2024 10:02:31 +0000 Subject: [PATCH 09/13] Use subTest to id the algorithm. --- Lib/test/_test_hashlib_fips.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/_test_hashlib_fips.py b/Lib/test/_test_hashlib_fips.py index a4fb3f40dac471..af9eb98e2033dc 100644 --- a/Lib/test/_test_hashlib_fips.py +++ b/Lib/test/_test_hashlib_fips.py @@ -50,12 +50,13 @@ def test_algorithms_available(self): # all available algorithms must be loadable, bpo-47101 self.assertNotIn("undefined", hashlib.algorithms_available) for name in hashlib.algorithms_available: - digest = hashlib.new(name, usedforsecurity=False) + with self.subTest(name): + digest = hashlib.new(name, usedforsecurity=False) def test_usedforsecurity_true(self): import hashlib for name in hashlib.algorithms_available: - with self.assertRaises(ValueError): + with self.subTest(name), self.assertRaises(ValueError): digest = hashlib.new(name, usedforsecurity=True) if __name__ == "__main__": From e8ef3806238540dc8966061eb6bbe54d31d669e2 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith [Google LLC]" Date: Wed, 27 Nov 2024 10:05:09 +0000 Subject: [PATCH 10/13] lint --- Lib/test/test_hashlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py index 4845cbedb2a4b1..8551616fe1e1fc 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -19,11 +19,11 @@ import threading import unittest from test import support -from test.support import os_helper, script_helper from test.support import _4G, bigmemtest from test.support import hashlib_helper from test.support.import_helper import import_fresh_module from test.support import requires_resource +from test.support import script_helper from test.support import threading_helper from http.client import HTTPException From 3db1eb5ae6d2350f8d3bccdd19e216e1b92c1a61 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith [Google LLC]" Date: Wed, 27 Nov 2024 10:17:53 +0000 Subject: [PATCH 11/13] remove the no longer appropriate mention from ssltests. --- Lib/test/ssltests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/ssltests.py b/Lib/test/ssltests.py index 0ecd82658601cb..ee03aed5cca532 100644 --- a/Lib/test/ssltests.py +++ b/Lib/test/ssltests.py @@ -7,7 +7,7 @@ TESTS = [ 'test_asyncio', 'test_ensurepip.py', 'test_ftplib', 'test_hashlib', - 'test_hashlib_fips', 'test_hmac', 'test_httplib', 'test_imaplib', + 'test_hmac', 'test_httplib', 'test_imaplib', 'test_poplib', 'test_ssl', 'test_smtplib', 'test_smtpnet', 'test_urllib2_localnet', 'test_venv', 'test_xmlrpc' ] From 4e5dddb3c1d458d2422e7fd0d569ed36ed48ceac Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith [Google LLC]" Date: Wed, 27 Nov 2024 10:46:04 +0000 Subject: [PATCH 12/13] fix subTest context manager nesting. --- Lib/test/_test_hashlib_fips.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/_test_hashlib_fips.py b/Lib/test/_test_hashlib_fips.py index af9eb98e2033dc..9253724595446d 100644 --- a/Lib/test/_test_hashlib_fips.py +++ b/Lib/test/_test_hashlib_fips.py @@ -56,8 +56,9 @@ def test_algorithms_available(self): def test_usedforsecurity_true(self): import hashlib for name in hashlib.algorithms_available: - with self.subTest(name), self.assertRaises(ValueError): - digest = hashlib.new(name, usedforsecurity=True) + with self.subTest(name): + with self.assertRaises(ValueError): + digest = hashlib.new(name, usedforsecurity=True) if __name__ == "__main__": unittest.main() From fd308b4764902f2bbb21f0ee3096dbb477a639db Mon Sep 17 00:00:00 2001 From: Dimitri John Ledkov Date: Mon, 8 Sep 2025 01:34:11 +0100 Subject: [PATCH 13/13] Use null provider, which is guaranteed to have nothing --- Lib/test/hashlibdata/openssl.cnf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/hashlibdata/openssl.cnf b/Lib/test/hashlibdata/openssl.cnf index 9a936ddc5effb8..022f1771a46b1e 100644 --- a/Lib/test/hashlibdata/openssl.cnf +++ b/Lib/test/hashlibdata/openssl.cnf @@ -10,9 +10,9 @@ providers = provider_sect alg_section = algorithm_sect [provider_sect] -base = base_sect +null = null_sect -[base_sect] +[null_sect] activate = 1 [algorithm_sect]