From 28d803546857280f42d082acf251f3c145db0199 Mon Sep 17 00:00:00 2001 From: Dimitri John Ledkov Date: Tue, 26 Nov 2024 15:14:31 +0000 Subject: [PATCH 01/12] 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 | 47 ++++++++++----- 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, 116 insertions(+), 16 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 1b2c30cc32f564..879693f8ba64b9 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): cache = __builtin_constructor_cache constructor = cache.get(name) @@ -87,32 +104,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. @@ -163,7 +180,7 @@ def __hash_new(name, data=b'', **kwargs): # hash, try using our builtin implementations. # This allows for SHA224/256 and SHA384/512 support even though # the OpenSSL library prior to 0.9.8 doesn't provide them. - return __get_builtin_constructor(name)(data) + return __get_builtin_constructor(name)(data, **kwargs) try: 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 8d94ba361fd934..908717f17916f2 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2447,6 +2447,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 c6375c1c7e51681386f5b46dbee96501319797d8 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Tue, 26 Nov 2024 22:54:49 -0800 Subject: [PATCH 02/12] use != 0 rather than == 1 and update a comment. --- Lib/hashlib.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/hashlib.py b/Lib/hashlib.py index 879693f8ba64b9..3bbf7b8f7ff5f0 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 @@ -178,8 +178,7 @@ def __hash_new(name, data=b'', **kwargs): except ValueError: # If the _hashlib module (OpenSSL) doesn't support the named # hash, try using our builtin implementations. - # This allows for SHA224/256 and SHA384/512 support even though - # the OpenSSL library prior to 0.9.8 doesn't provide them. + # OpenSSL may not have been compiled to support everything. return __get_builtin_constructor(name)(data, **kwargs) From b3aa6cb4fcd29ce5a790713c4cf3b36adca8720d Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Tue, 26 Nov 2024 23:05:12 -0800 Subject: [PATCH 03/12] 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 8ec61b59cb9ef2aaf9be5dd2b516a13a614c7ca7 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/12] 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 56f0f7ad8cb18692aaf8982aef93617c8b84c50f 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/12] 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 3bbf7b8f7ff5f0..b71fe5eb90eb0c 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 @@ -188,10 +188,15 @@ def __hash_new(name, data=b'', **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 f8c51331c98efd582483e5d32098f718cdf42064 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Wed, 27 Nov 2024 00:02:42 -0800 Subject: [PATCH 06/12] 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 0dbef0d667a0693227a41edd67e642f54f0d6092 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/12] 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 6e2aa8508ec84146e2a732ee002e43722ad86ed1 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/12] 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 575b2cd0da7056..07af804efaf639 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -17,6 +17,7 @@ import unittest import warnings from test import support +from test.support import os_helper, script_helper from test.support import _4G, bigmemtest from test.support.import_helper import import_fresh_module from test.support import os_helper @@ -1196,6 +1197,18 @@ def test_file_digest(self): with open(os_helper.TESTFN, "wb") as f: hashlib.file_digest(f, "sha256") + 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 dc964b9a50bcfa1891e42954a6ad3ed860e9595a 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/12] 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 b5a846e2715cb46ca7b54c5a4c4885d8603d3886 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/12] 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 07af804efaf639..f1ff7c33004b28 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -17,11 +17,11 @@ import unittest import warnings from test import support -from test.support import os_helper, script_helper from test.support import _4G, bigmemtest from test.support.import_helper import import_fresh_module from test.support import os_helper from test.support import requires_resource +from test.support import script_helper from test.support import threading_helper from http.client import HTTPException From c22d643e92073ad3e18a8cb7b72f2c27da1b8d39 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/12] 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 905ba7f06c12f7ef9985ccaa2bf24229f759a2e1 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/12] 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()