Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Skip tests requiring sha1 signing if the backend doesn't support that #2011

Merged
merged 4 commits into from Mar 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 23 additions & 1 deletion tests/test_client.py
Expand Up @@ -41,13 +41,17 @@
from paramiko.pkey import PublicBlob
from paramiko.ssh_exception import SSHException, AuthenticationException

from .util import _support, slow
from .util import _support, sha1_signing_unsupported, slow


requires_gss_auth = unittest.skipUnless(
paramiko.GSS_AUTH_AVAILABLE, "GSS auth not available"
)

requires_sha1_signing = unittest.skipIf(
sha1_signing_unsupported(), "SHA-1 signing not supported"
)

FINGERPRINTS = {
"ssh-dss": b"\x44\x78\xf0\xb9\xa2\x3c\xc5\x18\x20\x09\xff\x75\x5b\xc1\xd2\x6c", # noqa
"ssh-rsa": b"\x60\x73\x38\x44\xcb\x51\x86\x65\x7f\xde\xda\xa2\x2b\x5a\x57\xd5", # noqa
Expand Down Expand Up @@ -235,33 +239,39 @@ def _test_connection(self, **kwargs):


class SSHClientTest(ClientTest):
@requires_sha1_signing
def test_client(self):
"""
verify that the SSHClient stuff works too.
"""
self._test_connection(password="pygmalion")

@requires_sha1_signing
def test_client_dsa(self):
"""
verify that SSHClient works with a DSA key.
"""
self._test_connection(key_filename=_support("test_dss.key"))

@requires_sha1_signing
def test_client_rsa(self):
"""
verify that SSHClient works with an RSA key.
"""
self._test_connection(key_filename=_support("test_rsa.key"))

@requires_sha1_signing
def test_client_ecdsa(self):
"""
verify that SSHClient works with an ECDSA key.
"""
self._test_connection(key_filename=_support("test_ecdsa_256.key"))

@requires_sha1_signing
def test_client_ed25519(self):
self._test_connection(key_filename=_support("test_ed25519.key"))

@requires_sha1_signing
def test_multiple_key_files(self):
"""
verify that SSHClient accepts and tries multiple key files.
Expand Down Expand Up @@ -293,6 +303,7 @@ def test_multiple_key_files(self):
self.tearDown()
self.setUp()

@requires_sha1_signing
def test_multiple_key_files_failure(self):
"""
Expect failure when multiple keys in play and none are accepted
Expand All @@ -306,6 +317,7 @@ def test_multiple_key_files_failure(self):
allowed_keys=["ecdsa-sha2-nistp256"],
)

@requires_sha1_signing
def test_certs_allowed_as_key_filename_values(self):
# NOTE: giving cert path here, not key path. (Key path test is below.
# They're similar except for which path is given; the expected auth and
Expand All @@ -319,6 +331,7 @@ def test_certs_allowed_as_key_filename_values(self):
public_blob=PublicBlob.from_file(cert_path),
)

@requires_sha1_signing
def test_certs_implicitly_loaded_alongside_key_filename_keys(self):
# NOTE: a regular test_connection() w/ test_rsa.key would incidentally
# test this (because test_xxx.key-cert.pub exists) but incidental tests
Expand Down Expand Up @@ -469,6 +482,7 @@ def test_banner_timeout(self):
kwargs = dict(self.connect_kwargs, banner_timeout=0.5)
self.assertRaises(paramiko.SSHException, self.tc.connect, **kwargs)

@requires_sha1_signing
def test_auth_trickledown(self):
"""
Failed key auth doesn't prevent subsequent pw auth from succeeding
Expand All @@ -489,6 +503,7 @@ def test_auth_trickledown(self):
)
self._test_connection(**kwargs)

@requires_sha1_signing
@slow
def test_auth_timeout(self):
"""
Expand Down Expand Up @@ -591,13 +606,15 @@ def test_host_key_negotiation_1(self):
host_key = paramiko.ECDSAKey.generate()
self._client_host_key_bad(host_key)

@requires_sha1_signing
def test_host_key_negotiation_2(self):
host_key = paramiko.RSAKey.generate(2048)
self._client_host_key_bad(host_key)

def test_host_key_negotiation_3(self):
self._client_host_key_good(paramiko.ECDSAKey, "test_ecdsa_256.key")

@requires_sha1_signing
def test_host_key_negotiation_4(self):
self._client_host_key_good(paramiko.RSAKey, "test_rsa.key")

Expand Down Expand Up @@ -681,24 +698,28 @@ class PasswordPassphraseTests(ClientTest):
# instead of suffering a real connection cycle.
# TODO: in that case, move the below to be part of an integration suite?

@requires_sha1_signing
def test_password_kwarg_works_for_password_auth(self):
# Straightforward / duplicate of earlier basic password test.
self._test_connection(password="pygmalion")

# TODO: more granular exception pending #387; should be signaling "no auth
# methods available" because no key and no password
@raises(SSHException)
@requires_sha1_signing
def test_passphrase_kwarg_not_used_for_password_auth(self):
# Using the "right" password in the "wrong" field shouldn't work.
self._test_connection(passphrase="pygmalion")

@requires_sha1_signing
def test_passphrase_kwarg_used_for_key_passphrase(self):
# Straightforward again, with new passphrase kwarg.
self._test_connection(
key_filename=_support("test_rsa_password.key"),
passphrase="television",
)

@requires_sha1_signing
def test_password_kwarg_used_for_passphrase_when_no_passphrase_kwarg_given(
self
): # noqa
Expand All @@ -709,6 +730,7 @@ def test_password_kwarg_used_for_passphrase_when_no_passphrase_kwarg_given(
)

@raises(AuthenticationException) # TODO: more granular
@requires_sha1_signing
def test_password_kwarg_not_used_for_passphrase_when_passphrase_kwarg_given( # noqa
self
):
Expand Down
8 changes: 7 additions & 1 deletion tests/test_pkey.py
Expand Up @@ -44,9 +44,13 @@
from mock import patch, Mock
import pytest

from .util import _support, is_low_entropy
from .util import _support, is_low_entropy, sha1_signing_unsupported


requires_sha1_signing = unittest.skipIf(
sha1_signing_unsupported(), "SHA-1 signing not supported"
)

# from openssh's ssh-keygen
PUB_RSA = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA049W6geFpmsljTwfvI1UmKWWJPNFI74+vNKTk4dmzkQY2yAMs6FhlvhlI8ysU4oj71ZsRYMecHbBbxdN79+JRFVYTKaLqjwGENeTd+yv4q+V2PvZv3fLnzApI3l7EJCqhWwJUHJ1jAkZzqDx0tyOL4uoZpww3nmE0kb3y21tH4c=" # noqa
PUB_DSS = "ssh-dss AAAAB3NzaC1kc3MAAACBAOeBpgNnfRzr/twmAQRu2XwWAp3CFtrVnug6s6fgwj/oLjYbVtjAy6pl/h0EKCWx2rf1IetyNsTxWrniA9I6HeDj65X1FyDkg6g8tvCnaNB8Xp/UUhuzHuGsMIipRxBxw9LF608EqZcj1E3ytktoW5B5OcjrkEoz3xG7C+rpIjYvAAAAFQDwz4UnmsGiSNu5iqjn3uTzwUpshwAAAIEAkxfFeY8P2wZpDjX0MimZl5wkoFQDL25cPzGBuB4OnB8NoUk/yjAHIIpEShw8V+LzouMK5CTJQo5+Ngw3qIch/WgRmMHy4kBq1SsXMjQCte1So6HBMvBPIW5SiMTmjCfZZiw4AYHK+B/JaOwaG9yRg2Ejg4Ok10+XFDxlqZo8Y+wAAACARmR7CCPjodxASvRbIyzaVpZoJ/Z6x7dAumV+ysrV1BVYd0lYukmnjO1kKBWApqpH1ve9XDQYN8zgxM4b16L21kpoWQnZtXrY3GZ4/it9kUgyB7+NwacIBlXa8cMDL7Q/69o0d54U0X/NeX5QxuYR6OMJlrkQB7oiW/P/1mwjQgE=" # noqa
Expand Down Expand Up @@ -256,6 +260,7 @@ def _sign_and_verify_rsa(self, algorithm, saved_sig):
pub = RSAKey(data=key.asbytes())
self.assertTrue(pub.verify_ssh_sig(b"ice weasels", msg))

@requires_sha1_signing
def test_sign_and_verify_ssh_rsa(self):
self._sign_and_verify_rsa("ssh-rsa", SIGNED_RSA)

Expand All @@ -280,6 +285,7 @@ def test_sign_dss(self):
pub = DSSKey(data=key.asbytes())
self.assertTrue(pub.verify_ssh_sig(b"ice weasels", msg))

@requires_sha1_signing
def test_generate_rsa(self):
key = RSAKey.generate(1024)
msg = key.sign_ssh_data(b"jerri blank")
Expand Down
8 changes: 7 additions & 1 deletion tests/test_transport.py
Expand Up @@ -61,7 +61,7 @@
from paramiko.py3compat import bytes, byte_chr
from paramiko.message import Message

from .util import needs_builtin, _support, slow
from .util import needs_builtin, _support, sha1_signing_unsupported, slow
from .loop import LoopSocket


Expand All @@ -77,6 +77,10 @@
Maybe.
"""

requires_sha1_signing = unittest.skipIf(
sha1_signing_unsupported(), "SHA-1 signing not supported"
)


class NullServer(ServerInterface):
paranoid_did_password = False
Expand Down Expand Up @@ -1283,6 +1287,7 @@ class TestSHA2SignatureKeyExchange(unittest.TestCase):
# are new tests in test_pkey.py which use known signature blobs to prove
# the SHA2 family was in fact used!

@requires_sha1_signing
def test_base_case_ssh_rsa_still_used_as_fallback(self):
# Prove that ssh-rsa is used if either, or both, participants have SHA2
# algorithms disabled
Expand Down Expand Up @@ -1405,6 +1410,7 @@ def test_client_sha1_disabled_server_sha2_disabled_no_match(self):
) as (tc, ts, err):
assert isinstance(err, AuthenticationException)

@requires_sha1_signing
def test_ssh_rsa_still_used_when_sha2_disabled(self):
privkey = RSAKey.from_private_key_file(_support("test_rsa.key"))
# NOTE: this works because key obj comparison uses public bytes
Expand Down
27 changes: 27 additions & 0 deletions tests/util.py
Expand Up @@ -9,6 +9,10 @@
from paramiko.py3compat import builtins, PY2
from paramiko.ssh_gss import GSS_AUTH_AVAILABLE

from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding, rsa

tests_dir = dirname(realpath(__file__))

Expand Down Expand Up @@ -144,3 +148,26 @@ def is_low_entropy():
# I don't see a way to tell internally if the hash seed was set this
# way, but env should be plenty sufficient, this is only for testing.
return is_32bit and os.environ.get("PYTHONHASHSEED", None) == "0"


def sha1_signing_unsupported():
"""
This is used to skip tests in environments where SHA-1 signing is
not supported by the backend.
"""
private_key = rsa.generate_private_key(
public_exponent=65537, key_size=2048, backend=default_backend()
)
message = b"Some dummy text"
try:
private_key.sign(
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA1()),
salt_length=padding.PSS.MAX_LENGTH,
),
hashes.SHA1(),
)
return False
except UnsupportedAlgorithm as e:
return e._reason is _Reasons.UNSUPPORTED_HASH