From 824e4f4a783c5e49468fc88b2607e8fa6e236612 Mon Sep 17 00:00:00 2001 From: Lukas Puehringer Date: Mon, 20 Feb 2023 11:12:08 +0100 Subject: [PATCH 1/6] Misc storage-related test compatibility fixes * test_interface Case-handle expected permissions for newly created private key files (world-readable on Windows, user-readable elsewhere). * test_storage - Refactor exception tests for better readability. - Create test directory and explicitly set permissions, to test permission errors more reliably. - Skip permission tests on Windows, which does not fully support `mode`. * test_util Remove permission-error test in ensure_parent_dir method to avoid platform case handling. This is only an internally used thin wrapper around create_dirs, which is already tested extensively in test_storage. Signed-off-by: Lukas Puehringer --- tests/test_interface.py | 11 +++++++ tests/test_storage.py | 63 ++++++++++++++++++++--------------------- tests/test_util.py | 4 --- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/tests/test_interface.py b/tests/test_interface.py index d0500f89..00e4f911 100755 --- a/tests/test_interface.py +++ b/tests/test_interface.py @@ -850,6 +850,17 @@ def test_generate_keypair_wrappers(self): """ key_pw = "pw" expected_priv_mode = stat.S_IFREG | stat.S_IRUSR | stat.S_IWUSR + if os.name == "nt": + expected_priv_mode = ( + stat.S_IFREG + | stat.S_IWUSR + | stat.S_IRUSR + | stat.S_IWGRP + | stat.S_IRGRP + | stat.S_IWOTH + | stat.S_IROTH + ) + for idx, (gen, gen_prompt, gen_plain, import_priv, schema) in enumerate( [ ( diff --git a/tests/test_storage.py b/tests/test_storage.py index c0b24d81..772690e9 100644 --- a/tests/test_storage.py +++ b/tests/test_storage.py @@ -17,11 +17,13 @@ import os import shutil +import stat import tempfile import unittest +from pathlib import Path -import securesystemslib.exceptions import securesystemslib.storage +from securesystemslib.exceptions import StorageError class TestStorage(unittest.TestCase): # pylint: disable=missing-class-docstring @@ -40,42 +42,33 @@ def tearDown(self): shutil.rmtree(self.temp_dir) def test_exceptions(self): - try: - with self.storage_backend.get("/none/existent/path") as file_object: - file_object.read() - except Exception as exc: # pylint: disable=broad-except - self.assertIsInstance(exc, securesystemslib.exceptions.StorageError) - - self.assertRaises( - securesystemslib.exceptions.StorageError, - self.storage_backend.put, - self.fileobj, - "/none/existent/path", - ) + invalid_path = "" + non_existent_path = Path(self.temp_dir) / "not_existent" + self.assertFalse(non_existent_path.exists()) - self.assertRaises( - securesystemslib.exceptions.StorageError, - self.storage_backend.getsize, - "/none/existent/path", - ) + with self.assertRaises(StorageError): + with self.storage_backend.get(non_existent_path) as _: + pass - self.assertRaises( - securesystemslib.exceptions.StorageError, - self.storage_backend.create_folder, - "/none/existent/path", - ) + with self.assertRaises(StorageError): + self.storage_backend.getsize(non_existent_path) - self.assertRaises( - securesystemslib.exceptions.StorageError, - self.storage_backend.create_folder, - "", - ) + with self.assertRaises(StorageError): + self.storage_backend.list_folder(non_existent_path) - self.assertRaises( - securesystemslib.exceptions.StorageError, - self.storage_backend.list_folder, - "/none/existent/path", - ) + with self.assertRaises(StorageError): + self.storage_backend.create_folder(invalid_path) + + @unittest.skipIf(os.name == "nt", "n/a on Windows") + def test_permission_exceptions(self): + non_writable_path = Path(self.temp_dir) / "not_writable" + os.mkdir(non_writable_path, mode=stat.S_IRUSR) + + with self.assertRaises(StorageError): + self.storage_backend.put(self.fileobj, non_writable_path / "new") + + with self.assertRaises(StorageError): + self.storage_backend.create_folder(non_writable_path / "new") def test_files(self): with self.storage_backend.get(self.filepath) as get_fileobj: @@ -117,3 +110,7 @@ def test_singleton(self): self.assertEqual(id(fb1), id(fb2)) self.assertEqual(id(self.storage_backend), id(fb1)) self.assertEqual(id(fb2), id(self.storage_backend)) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_util.py b/tests/test_util.py index f6c4472c..3ce37223 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -185,10 +185,6 @@ def test_B4_ensure_parent_dir(self): parent_dir, ) - # Check that when a folder cannot be created a StorageError is thrown - with self.assertRaises(securesystemslib.exceptions.StorageError): - securesystemslib.util.ensure_parent_dir("/a/file.txt") - # When we call ensure_parent_dir with filepath arg like "a.txt", # then the directory of that filepath will be an empty string. # We want to make sure that securesyslib.storage.create_folder() From 4fa46764d4267ae027ed8eeab506f267827a900d Mon Sep 17 00:00:00 2001 From: Lukas Puehringer Date: Tue, 21 Feb 2023 10:32:56 +0100 Subject: [PATCH 2/6] Fix failing gpg tests on Windows Prior to this change gpg tests failed because securesystemslib.gpg interface functions cannot handle absoulte paths as homedir on Windows. This patch works around the issue to fix tests only by not using absolute paths: * Use relative path to homedir for gpg tests, or * Store and use fixtures of raw pubkey data for gpg parsing tests. These test don't actually need to run the gpg command. The fixtures were created like so: ``` keyids=(F557D0FF451DEF45372591429EA70BD13D883381 \ E8AC80C924116DABB51D4B987CB07D6D2C199C7C) dir=tests/gpg_keyrings/rsa for keyid in ${keyids[@]}; do gpg --homedir ${dir} --export ${keyid} > ${dir}/${keyid}.raw; done ``` Signed-off-by: Lukas Puehringer --- ...AC80C924116DABB51D4B987CB07D6D2C199C7C.raw | Bin 0 -> 2768 bytes ...57D0FF451DEF45372591429EA70BD13D883381.raw | Bin 0 -> 6792 bytes tests/test_gpg.py | 55 ++++++------------ tests/test_signer.py | 6 +- 4 files changed, 23 insertions(+), 38 deletions(-) create mode 100644 tests/gpg_keyrings/rsa/E8AC80C924116DABB51D4B987CB07D6D2C199C7C.raw create mode 100644 tests/gpg_keyrings/rsa/F557D0FF451DEF45372591429EA70BD13D883381.raw diff --git a/tests/gpg_keyrings/rsa/E8AC80C924116DABB51D4B987CB07D6D2C199C7C.raw b/tests/gpg_keyrings/rsa/E8AC80C924116DABB51D4B987CB07D6D2C199C7C.raw new file mode 100644 index 0000000000000000000000000000000000000000..fe3cdec4c186520df8f1c901b323d4adfd84e5db GIT binary patch literal 2768 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 Fe*+~)%wPZj literal 0 HcmV?d00001 diff --git a/tests/test_gpg.py b/tests/test_gpg.py index 075f0a5a..5e443036 100644 --- a/tests/test_gpg.py +++ b/tests/test_gpg.py @@ -21,7 +21,6 @@ import os import shutil -import subprocess # nosec import tempfile import unittest @@ -44,7 +43,6 @@ parse_signature_packet, ) from securesystemslib.gpg.constants import ( - GPG_TIMEOUT, PACKET_TYPE_PRIMARY_KEY, PACKET_TYPE_SUB_KEY, PACKET_TYPE_USER_ATTR, @@ -52,7 +50,6 @@ SHA1, SHA256, SHA512, - gpg_export_pubkey_command, have_gpg, ) from securesystemslib.gpg.dsa import create_pubkey as dsa_create_pubkey @@ -209,35 +206,19 @@ def setUpClass(self): # pylint: disable=bad-classmethod-argument gpg_keyring_path = os.path.join( os.path.dirname(os.path.realpath(__file__)), "gpg_keyrings", "rsa" ) - homearg = ( - "--homedir {}".format( # pylint: disable=consider-using-f-string - gpg_keyring_path - ).replace("\\", "/") - ) - # Load test raw public key bundle from rsa keyring, used to construct - # erroneous gpg data in tests below. - keyid = "F557D0FF451DEF45372591429EA70BD13D883381" - cmd = gpg_export_pubkey_command(keyid=keyid, homearg=homearg) - proc = subprocess.run( - cmd, - capture_output=True, - timeout=GPG_TIMEOUT, - check=True, - ) - self.raw_key_data = proc.stdout - self.raw_key_bundle = parse_pubkey_bundle(self.raw_key_data) + # Load raw public key bundle data from fixtures + key = "F557D0FF451DEF45372591429EA70BD13D883381" + key_expired = "E8AC80C924116DABB51D4B987CB07D6D2C199C7C" + data = {} + for keyid in [key, key_expired]: + path = os.path.join(gpg_keyring_path, f"{keyid}.raw") + with open(path, "rb") as f: + data[keyid] = f.read() - # Export pubkey bundle with expired key for key expiration tests - keyid = "E8AC80C924116DABB51D4B987CB07D6D2C199C7C" - cmd = gpg_export_pubkey_command(keyid=keyid, homearg=homearg) - proc = subprocess.run( - cmd, - capture_output=True, - timeout=GPG_TIMEOUT, - check=True, - ) - self.raw_expired_key_bundle = parse_pubkey_bundle(proc.stdout) + self.raw_key_data = data[key] + self.raw_key_bundle = parse_pubkey_bundle(data[key]) + self.raw_expired_key_bundle = parse_pubkey_bundle(data[key_expired]) def test_parse_pubkey_payload_errors(self): """Test parse_pubkey_payload errors with manually crafted data.""" @@ -604,8 +585,10 @@ def setUpClass(self): # pylint: disable=bad-classmethod-argument ) self.test_dir = os.path.realpath(tempfile.mkdtemp()) - self.gnupg_home = os.path.join(self.test_dir, "rsa") - shutil.copytree(gpg_keyring_path, self.gnupg_home) + self.gnupg_home = "rsa" + shutil.copytree( + gpg_keyring_path, os.path.join(self.test_dir, self.gnupg_home) + ) os.chdir(self.test_dir) @classmethod @@ -779,14 +762,14 @@ def setUpClass(self): # pylint: disable=bad-classmethod-argument # Create directory to run the tests without having everything blow up self.working_dir = os.getcwd() self.test_dir = os.path.realpath(tempfile.mkdtemp()) - self.gnupg_home = os.path.join(self.test_dir, "dsa") + self.gnupg_home = "dsa" # Find keyrings keyrings = os.path.join( os.path.dirname(os.path.realpath(__file__)), "gpg_keyrings", "dsa" ) - shutil.copytree(keyrings, self.gnupg_home) + shutil.copytree(keyrings, os.path.join(self.test_dir, self.gnupg_home)) os.chdir(self.test_dir) @classmethod @@ -878,14 +861,14 @@ def setUpClass(self): # pylint: disable=bad-classmethod-argument # Create directory to run the tests without having everything blow up self.working_dir = os.getcwd() self.test_dir = os.path.realpath(tempfile.mkdtemp()) - self.gnupg_home = os.path.join(self.test_dir, "dsa") + self.gnupg_home = "dsa" # Find keyrings keyrings = os.path.join( os.path.dirname(os.path.realpath(__file__)), "gpg_keyrings", "eddsa" ) - shutil.copytree(keyrings, self.gnupg_home) + shutil.copytree(keyrings, os.path.join(self.test_dir, self.gnupg_home)) os.chdir(self.test_dir) @classmethod diff --git a/tests/test_signer.py b/tests/test_signer.py index c296f495..03610782 100644 --- a/tests/test_signer.py +++ b/tests/test_signer.py @@ -384,8 +384,10 @@ def setUpClass(cls): ) cls.test_dir = os.path.realpath(tempfile.mkdtemp()) - cls.gnupg_home = os.path.join(cls.test_dir, "rsa") - shutil.copytree(gpg_keyring_path, cls.gnupg_home) + cls.gnupg_home = "rsa" + shutil.copytree( + gpg_keyring_path, os.path.join(cls.test_dir, cls.gnupg_home) + ) os.chdir(cls.test_dir) @classmethod From 99247d23d390dccac2690f19ec5ae9c13257b46c Mon Sep 17 00:00:00 2001 From: Lukas Puehringer Date: Tue, 21 Feb 2023 11:22:14 +0100 Subject: [PATCH 3/6] Fix HSM env var assertion in tox.ini The used trick to assert that HSM tests are not silently skipped due to an unset environment variable does not seem to work on Windows. This patch provides a simple alternative that does not require advanced escape character usage. Signed-off-by: Lukas Puehringer --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 512d205a..5e1df6dc 100644 --- a/tox.ini +++ b/tox.ini @@ -20,7 +20,7 @@ deps = commands = python -m tests.check_gpg_available - python -c '"{env:PYKCS11LIB}"' # Required for 'test_hsm_signer' + python -c 'import os; os.environ["PYKCS11LIB"]' # assert HSM tests aren't skipped coverage run tests/aggregate_tests.py coverage report -m --fail-under 95 From 6097b6fdd3f05ec3a5380746a7c4bf96d6c8bcee Mon Sep 17 00:00:00 2001 From: Lukas Puehringer Date: Tue, 21 Feb 2023 12:20:49 +0100 Subject: [PATCH 4/6] Do not run sphincs tests on Windows The required PySPX library is not available on Windows. This patch configures requirements files to not install PySPX on Windows, and skips relevant tests on Windows. Details: * in test_keys the relevant tests are factored out into a separate test class to avoid inline platform case handling in multiple places. The refactor also simplifies the tests and removes one graceful failure check, which is redundant with check_public_interfaces. * in test_signer the keys need for testing are simply not populated on windows. Signed-off-by: Lukas Puehringer --- requirements-pinned.txt | 2 +- requirements.txt | 2 +- tests/test_keys.py | 63 ++++++++++++++--------------------------- tests/test_signer.py | 4 ++- 4 files changed, 27 insertions(+), 44 deletions(-) diff --git a/requirements-pinned.txt b/requirements-pinned.txt index ac515e11..10f78e95 100644 --- a/requirements-pinned.txt +++ b/requirements-pinned.txt @@ -19,5 +19,5 @@ pykcs11==1.5.11 # via -r requirements.txt pynacl==1.5.0 # via -r requirements.txt -pyspx==0.5.0 +pyspx==0.5.0 ; platform_system != "Windows" # via -r requirements.txt diff --git a/requirements.txt b/requirements.txt index 5e35f52f..7bf2c867 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,6 @@ # triggers CI/CD builds to automatically test against updated dependencies. cryptography >= 37.0.0 pynacl -PySPX +PySPX; platform_system != 'Windows' PyKCS11 asn1crypto diff --git a/tests/test_keys.py b/tests/test_keys.py index 0fd2568e..03dccbc7 100755 --- a/tests/test_keys.py +++ b/tests/test_keys.py @@ -18,6 +18,7 @@ """ import copy +import os import unittest import securesystemslib.ecdsa_keys @@ -34,13 +35,33 @@ DATA = securesystemslib.formats.encode_canonical(DATA_STR).encode("utf-8") +@unittest.skipIf(os.name == "nt", "PySPX n/a on Windows") +class TestSphincsKeys(unittest.TestCase): + """Test create keys, sign and verify for sphincs keys.""" + + def test_sphincs_keys(self): + key = KEYS.generate_sphincs_key() + sig = KEYS.create_signature(key, b"data") + self.assertTrue(securesystemslib.formats.SIGNATURE_SCHEMA.matches(sig)) + + # Assert valid/invalid signature + self.assertTrue(KEYS.verify_signature(key, sig, b"data")) + self.assertFalse(KEYS.verify_signature(key, sig, b"not data")) + + # Assert verificaiton failure for unsupported signing scheme + key["scheme"] = "invalid_scheme" + with self.assertRaises( + securesystemslib.exceptions.UnsupportedAlgorithmError + ): + KEYS.verify_signature(key, sig, b"data") + + class TestKeys(unittest.TestCase): # pylint: disable=missing-class-docstring @classmethod def setUpClass(cls): cls.rsakey_dict = KEYS.generate_rsa_key() cls.ed25519key_dict = KEYS.generate_ed25519_key() cls.ecdsakey_dict = KEYS.generate_ecdsa_key() - cls.sphincskey_dict = KEYS.generate_sphincs_key() def test_generate_rsa_key(self): _rsakey_dict = KEYS.generate_rsa_key() # pylint: disable=invalid-name @@ -267,7 +288,6 @@ def test_create_signature(self): # Creating a signature for 'DATA'. rsa_signature = KEYS.create_signature(self.rsakey_dict, DATA) ed25519_signature = KEYS.create_signature(self.ed25519key_dict, DATA) - sphincs_signature = KEYS.create_signature(self.sphincskey_dict, DATA) # Check format of output. self.assertEqual( @@ -284,13 +304,6 @@ def test_create_signature(self): ), FORMAT_ERROR_MSG, ) - self.assertEqual( - None, - securesystemslib.formats.SIGNATURE_SCHEMA.check_match( - sphincs_signature - ), - FORMAT_ERROR_MSG, - ) # Test for invalid signature scheme. args = (self.rsakey_dict, DATA) @@ -344,7 +357,6 @@ def test_verify_signature(self): # pylint: disable=too-many-statements rsa_signature = KEYS.create_signature(self.rsakey_dict, DATA) ed25519_signature = KEYS.create_signature(self.ed25519key_dict, DATA) ecdsa_signature = KEYS.create_signature(self.ecdsakey_dict, DATA) - sphincs_signature = KEYS.create_signature(self.sphincskey_dict, DATA) # Verifying the 'signature' of 'DATA'. verified = KEYS.verify_signature(self.rsakey_dict, rsa_signature, DATA) @@ -368,24 +380,6 @@ def test_verify_signature(self): # pylint: disable=too-many-statements ) self.ed25519key_dict["scheme"] = valid_scheme - # Verifying the 'sphincs_signature' of 'DATA'. - verified = KEYS.verify_signature( - self.sphincskey_dict, sphincs_signature, DATA - ) - self.assertTrue(verified, "Incorrect signature.") - - # Verify that an invalid sphincs signature scheme is rejected. - valid_scheme = self.sphincskey_dict["scheme"] - self.sphincskey_dict["scheme"] = "invalid_scheme" - self.assertRaises( - securesystemslib.exceptions.UnsupportedAlgorithmError, - KEYS.verify_signature, - self.sphincskey_dict, - sphincs_signature, - DATA, - ) - self.sphincskey_dict["scheme"] = valid_scheme - # Verifying the 'ecdsa_signature' of 'DATA'. verified = KEYS.verify_signature( self.ecdsakey_dict, ecdsa_signature, DATA @@ -431,11 +425,6 @@ def test_verify_signature(self): # pylint: disable=too-many-statements ) self.assertFalse(verified, "Returned 'True' on an incorrect signature.") - verified = KEYS.verify_signature( - self.sphincskey_dict, sphincs_signature, _DATA - ) - self.assertFalse(verified, "Returned 'True' on an incorrect signature.") - verified = KEYS.verify_signature( self.ecdsakey_dict, ecdsa_signature, _DATA ) @@ -484,14 +473,6 @@ def test_verify_signature(self): # pylint: disable=too-many-statements ) self.assertTrue(verified, "Incorrect signature.") - # Verify that sphincs fails if PySPX is not installed - KEYS.sphincs_keys.SPX_AVAIL = False # Monkey patch availability - with self.assertRaises( - securesystemslib.exceptions.UnsupportedLibraryError - ): - KEYS.verify_signature(self.sphincskey_dict, sphincs_signature, DATA) - KEYS.sphincs_keys.SPX_AVAIL = True - # Verify ecdsa key with HEX encoded keyval instead of PEM encoded keyval ecdsa_key = KEYS.generate_ecdsa_key() ecdsa_key["keyval"]["public"] = "abcd" diff --git a/tests/test_signer.py b/tests/test_signer.py index 03610782..db114fd0 100644 --- a/tests/test_signer.py +++ b/tests/test_signer.py @@ -168,8 +168,10 @@ def setUpClass(cls): KEYS.generate_rsa_key(), KEYS.generate_ed25519_key(), KEYS.generate_ecdsa_key(), - KEYS.generate_sphincs_key(), ] + if os.name != "nt": + cls.keys.append(KEYS.generate_sphincs_key()) + cls.DATA = b"DATA" # pylint: disable=consider-using-with From 8c1d9edeede1a20bee659c85e140baf7ae26fe6a Mon Sep 17 00:00:00 2001 From: Lukas Puehringer Date: Fri, 10 Feb 2023 11:42:31 +0100 Subject: [PATCH 5/6] Enable windows-latest builds in CI Signed-off-by: Lukas Puehringer --- .github/workflows/ci.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8449694c..6eec22f7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,8 +16,7 @@ jobs: # Run tests on each OS/Python combination matrix: python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] - # TODO: Add windows-latest when gpg issues are solved - os: [ubuntu-latest, macos-latest] + os: [ubuntu-latest, macos-latest, windows-latest] toxenv: [py] include: @@ -63,10 +62,9 @@ jobs: brew install softhsm echo "PYKCS11LIB=$(brew --prefix softhsm)/lib/softhsm/libsofthsm2.so" >> $GITHUB_ENV - # TODO: Uncomment when testing on Windows - # elif [ "$RUNNER_OS" == "Windows" ]; then - # choco install softhsm.install - # echo "PYKCS11LIB=C:\SoftHSM2\lib\softhsm2-x64.dll" >> $GITHUB_ENV + elif [ "$RUNNER_OS" == "Windows" ]; then + choco install softhsm.install + echo "PYKCS11LIB=C:\SoftHSM2\lib\softhsm2-x64.dll" >> $GITHUB_ENV else echo "$RUNNER_OS not supported" From 1e41964b51979b6503a365289224acf8dfa55e3b Mon Sep 17 00:00:00 2001 From: Lukas Puehringer Date: Tue, 21 Feb 2023 12:56:03 +0100 Subject: [PATCH 6/6] Lower min test coverage in tox.ini to 90% Tests on Windows fail because they don't have 95% coverage (only ~93 %). This commit lowers the requirement to a value that seems reasonable for all platforms. Coverage measurement may be fine tuned with #208 and changed back to a higher value if desired. Signed-off-by: Lukas Puehringer --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 5e1df6dc..7d01da48 100644 --- a/tox.ini +++ b/tox.ini @@ -22,7 +22,7 @@ commands = python -m tests.check_gpg_available python -c 'import os; os.environ["PYKCS11LIB"]' # assert HSM tests aren't skipped coverage run tests/aggregate_tests.py - coverage report -m --fail-under 95 + coverage report -m --fail-under 90 [testenv:purepy311] deps =