diff --git a/requirements/tests.txt b/requirements/tests.txt index 75c4fac..364709e 100644 --- a/requirements/tests.txt +++ b/requirements/tests.txt @@ -2,3 +2,4 @@ docker pytest-subtests pyfakefs==4.4.0; python_version == '3.5' pyfakefs; python_version > '3.5' +cryptography diff --git a/src/pytestskipmarkers/utils/platform.py b/src/pytestskipmarkers/utils/platform.py index e337d41..bd32bf1 100644 --- a/src/pytestskipmarkers/utils/platform.py +++ b/src/pytestskipmarkers/utils/platform.py @@ -7,6 +7,8 @@ .. PYTEST_DONT_REWRITE """ +import contextlib +import hashlib import multiprocessing import os import pathlib @@ -210,23 +212,32 @@ def is_fips_enabled() -> bool: ): return True sysctl_path = shutil.which("sysctl") - if not sysctl_path: - return False - ret = subprocess.run( - [sysctl_path, "crypto.fips_enabled"], - check=False, - shell=False, - stdout=subprocess.PIPE, - text=True, - ) - if ret.returncode == 0: - stripped_output = ret.stdout.strip() - if not stripped_output: - # No output? - return False - if "=" not in stripped_output: - # Don't know how to parse this - return False - if stripped_output.split("=")[-1].strip() == "1": + if sysctl_path: + ret = subprocess.run( + [sysctl_path, "crypto.fips_enabled"], + check=False, + shell=False, + stdout=subprocess.PIPE, + text=True, + ) + if ret.returncode == 0: + stripped_output = ret.stdout.strip() + if ( + stripped_output + and "=" in stripped_output + and stripped_output.split("=")[-1].strip() == "1" + ): + return True + + with contextlib.suppress(ImportError): + import cryptography.hazmat.backends.openssl.backend + + if cryptography.hazmat.backends.openssl.backend._fips_enabled: + return True + + try: + hashlib.md5() # nosec + except ValueError as exc: + if str(exc) == "[digital envelope routines] unsupported": return True return False diff --git a/tests/unit/utils/test_platform.py b/tests/unit/utils/test_platform.py index 692837f..037025d 100644 --- a/tests/unit/utils/test_platform.py +++ b/tests/unit/utils/test_platform.py @@ -12,6 +12,14 @@ import pytestskipmarkers.utils.platform +try: + import cryptography.hazmat.backends.openssl.backend as backend + + cryptography_import_error = "" +except ImportError as exc: + backend = None + cryptography_import_error = f"Failed to import cryptography: {exc}" + log = logging.getLogger(__name__) @@ -206,6 +214,26 @@ def test_is_fips_enabled_sysctl(output, expected): assert pytestskipmarkers.utils.platform.is_fips_enabled() is expected +@pytest.mark.skipif(backend is None, reason=cryptography_import_error) +@pytest.mark.parametrize("fips_enabled", [True, False], ids=lambda x: f"fips_enabled={x}") +def test_is_fips_enabled_cryptography(fs, fips_enabled): + fs.create_file("/proc/sys/crypto/fips_enabled", contents="0") + with mock.patch("shutil.which", return_value=None): + with mock.patch.object(backend, "_fips_enabled", fips_enabled): + assert pytestskipmarkers.utils.platform.is_fips_enabled() is fips_enabled + + +def test_is_fips_by_catch_exception(fs): + fs.create_file("/proc/sys/crypto/fips_enabled", contents="0") + with mock.patch("shutil.which", return_value=None): + with mock.patch("hashlib.md5", return_value="a random md5 hash"): + assert pytestskipmarkers.utils.platform.is_fips_enabled() is False + with mock.patch( + "hashlib.md5", side_effect=ValueError("[digital envelope routines] unsupported") + ): + assert pytestskipmarkers.utils.platform.is_fips_enabled() is True + + def test_is_spawning_platform(): with mock.patch("multiprocessing.get_start_method", return_value="spawn"): assert pytestskipmarkers.utils.platform.is_spawning_platform() is True