Skip to content

Commit

Permalink
Merge pull request #518 from lukpueh/enable-windows-ci
Browse files Browse the repository at this point in the history
Fix compatibility issues in tests and enable Windows CI
  • Loading branch information
lukpueh authored Feb 22, 2023
2 parents ab1061e + 1e41964 commit 265115e
Show file tree
Hide file tree
Showing 12 changed files with 97 additions and 127 deletions.
10 changes: 4 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion requirements-pinned.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Binary file not shown.
Binary file not shown.
55 changes: 19 additions & 36 deletions tests/test_gpg.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

import os
import shutil
import subprocess # nosec
import tempfile
import unittest

Expand All @@ -44,15 +43,13 @@
parse_signature_packet,
)
from securesystemslib.gpg.constants import (
GPG_TIMEOUT,
PACKET_TYPE_PRIMARY_KEY,
PACKET_TYPE_SUB_KEY,
PACKET_TYPE_USER_ATTR,
PACKET_TYPE_USER_ID,
SHA1,
SHA256,
SHA512,
gpg_export_pubkey_command,
have_gpg,
)
from securesystemslib.gpg.dsa import create_pubkey as dsa_create_pubkey
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions tests/test_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
[
(
Expand Down
63 changes: 22 additions & 41 deletions tests/test_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"""

import copy
import os
import unittest

import securesystemslib.ecdsa_keys
Expand All @@ -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
Expand Down Expand Up @@ -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(
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
)
Expand Down Expand Up @@ -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"
Expand Down
10 changes: 7 additions & 3 deletions tests/test_signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -384,8 +386,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
Expand Down
Loading

0 comments on commit 265115e

Please sign in to comment.