diff --git a/fido2-compat.patch b/fido2-compat.patch new file mode 100644 index 0000000..ff0f6af --- /dev/null +++ b/fido2-compat.patch @@ -0,0 +1,176 @@ +From b9e58a3914c7d1df7f2c096e8c1c0220799e247f Mon Sep 17 00:00:00 2001 +From: Ron Frederick +Date: Fri, 3 Oct 2025 17:44:39 -0700 +Subject: [PATCH] Update asycnssh to use version 2 of the fido2 package + +--- + asyncssh/sk.py | 33 ++++++++++++++++++++++----------- + pyproject.toml | 2 +- + tests/sk_stub.py | 26 +++++++++++++++++++++----- + 3 files changed, 44 insertions(+), 17 deletions(-) + +diff --git a/asyncssh/sk.py b/asyncssh/sk.py +index ca5aef7..bb02ed2 100644 +--- a/asyncssh/sk.py ++++ b/asyncssh/sk.py +@@ -128,7 +128,9 @@ def _ctap2_enroll(dev: 'CtapHidDevice', alg: int, application: str, + def _win_enroll(alg: int, application: str, user: str) -> Tuple[bytes, bytes]: + """Enroll a new security key using Windows WebAuthn API""" + +- client = WindowsClient(application, verify=_verify_rp_id) ++ data_collector = DefaultClientDataCollector(origin=application, ++ verify=_verify_rp_id) ++ client = WindowsClient(data_collector) + + rp = {'id': application, 'name': application} + user_cred = {'id': user.encode('utf-8'), 'name': user} +@@ -137,7 +139,8 @@ def _win_enroll(alg: int, application: str, user: str) -> Tuple[bytes, bytes]: + 'pubKeyCredParams': key_params} + + result = client.make_credential(options) +- cdata = result.attestation_object.auth_data.credential_data ++ response = result.response ++ cdata = response.attestation_object.auth_data.credential_data + + # pylint: disable=no-member + return _decode_public_key(alg, cdata.public_key), cdata.credential_id +@@ -188,17 +191,20 @@ def _win_sign(data: bytes, application: str, + key_handle: bytes) -> Tuple[int, int, bytes, bytes]: + """Sign a message with a security key using Windows WebAuthn API""" + +- client = WindowsClient(application, verify=_verify_rp_id) ++ data_collector = DefaultClientDataCollector(origin=application, ++ verify=_verify_rp_id) ++ client = WindowsClient(data_collector) + + creds = [{'type': 'public-key', 'id': key_handle}] + options = {'challenge': data, 'rpId': application, + 'allowCredentials': creds} + + result = client.get_assertion(options).get_response(0) +- auth_data = result.authenticator_data ++ response = result.response ++ auth_data = response.authenticator_data + + return auth_data.flags, auth_data.counter, \ +- result.signature, bytes(result.client_data) ++ response.signature, bytes(response.client_data) + + + def sk_webauthn_prefix(data: bytes, application: str) -> bytes: +@@ -327,7 +333,7 @@ def sk_get_resident(application: str, user: Optional[str], + + + try: +- from fido2.client import WindowsClient ++ from fido2.client import DefaultClientDataCollector + from fido2.ctap import CtapError + from fido2.ctap1 import Ctap1, APDU, ApduError + from fido2.ctap2 import Ctap2, ClientPin, PinProtocolV1 +@@ -335,13 +341,8 @@ def sk_get_resident(application: str, user: Optional[str], + from fido2.hid import CtapHidDevice + + sk_available = True +- +- sk_use_webauthn = WindowsClient.is_available() and \ +- hasattr(ctypes, 'windll') and \ +- not ctypes.windll.shell32.IsUserAnAdmin() + except (ImportError, OSError, AttributeError): # pragma: no cover + sk_available = False +- sk_use_webauthn = False + + def _sk_not_available(*args: object, **kwargs: object) -> NoReturn: + """Report that security key support is unavailable""" +@@ -351,3 +352,13 @@ def _sk_not_available(*args: object, **kwargs: object) -> NoReturn: + sk_enroll = _sk_not_available + sk_sign = _sk_not_available + sk_get_resident = _sk_not_available ++ ++try: ++ from fido2.client.windows import WindowsClient ++ ++ sk_use_webauthn = WindowsClient.is_available() and \ ++ hasattr(ctypes, 'windll') and \ ++ not ctypes.windll.shell32.IsUserAnAdmin() ++except ImportError: ++ WindowsClient = None ++ sk_use_webauthn = False +diff --git a/pyproject.toml b/pyproject.toml +index ea30886..2f4f113 100644 +--- a/pyproject.toml ++++ b/pyproject.toml +@@ -35,7 +35,7 @@ dynamic = ['version'] + + [project.optional-dependencies] + bcrypt = ['bcrypt >= 3.1.3'] +-fido2 = ['fido2 >= 0.9.2, < 2'] ++fido2 = ['fido2 >= 2'] + gssapi = ['gssapi >= 1.2.0'] + libnacl = ['libnacl >= 1.4.2'] + pkcs11 = ['python-pkcs11 >= 0.7.0'] +diff --git a/tests/sk_stub.py b/tests/sk_stub.py +index 0926e4e..090f150 100644 +--- a/tests/sk_stub.py ++++ b/tests/sk_stub.py +@@ -93,6 +93,13 @@ def __init__(self, attestation_object): + self.attestation_object = attestation_object + + ++class _RegistrationResponse: ++ """Security key registration response""" ++ ++ def __init__(self, attestation_response): ++ self.response = attestation_response ++ ++ + class _AuthenticatorData: + """Security key authenticator data in aseertion""" + +@@ -110,6 +117,13 @@ def __init__(self, client_data, auth_data, signature): + self.signature = signature + + ++class _AuthenticationResponse: ++ """Security key authentication response""" ++ ++ def __init__(self, response): ++ self.response = response ++ ++ + class _AssertionSelection: + """Security key assertion response list""" + +@@ -261,9 +275,9 @@ def get_assertions(self, application, message_hash, allow_creds, options): + class WindowsClient(_CtapStub): + """Stub for unit testing U2F security keys via Windows WebAuthn""" + +- def __init__(self, origin, verify): +- self._origin = origin +- self._verify = verify ++ def __init__(self, data_collector): ++ self._origin = data_collector._origin ++ self._verify = data_collector._verify + + def make_credential(self, options): + """Make a credential using Windows WebAuthN API""" +@@ -275,8 +289,9 @@ def make_credential(self, options): + public_key, key_handle = self._enroll(alg) + + cdata = _CredentialData(alg, public_key, key_handle) ++ attestation_object = _Credential(_CredentialAuthData(cdata)) + +- return _AttestationResponse(_Credential(_CredentialAuthData(cdata))) ++ return _RegistrationResponse(_AttestationResponse(attestation_object)) + + def get_assertion(self, options): + """Get assertion using Windows WebAuthN API""" +@@ -297,7 +312,8 @@ def get_assertion(self, options): + key_handle, flags) + + auth_data = _AuthenticatorData(flags, counter) +- assertion = _AssertionResponse(data, auth_data, sig) ++ response = _AssertionResponse(data, auth_data, sig) ++ assertion = _AuthenticationResponse(response) + + return _AssertionSelection([assertion]) + diff --git a/python-asyncssh.changes b/python-asyncssh.changes index 20f102e..fd1bdd1 100644 --- a/python-asyncssh.changes +++ b/python-asyncssh.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Fri Oct 10 11:51:00 UTC 2025 - Nico Krapp + +- Add fido2-compat.patch to restore compatibility with python-fido2 >= 2 +- Update requirements from pyproject.toml + ------------------------------------------------------------------- Thu Oct 9 12:26:08 UTC 2025 - John Paul Adrian Glaubitz diff --git a/python-asyncssh.spec b/python-asyncssh.spec index a6b2fae..6128281 100644 --- a/python-asyncssh.spec +++ b/python-asyncssh.spec @@ -1,7 +1,7 @@ # # spec file for package python-asyncssh # -# Copyright (c) 2025 SUSE LLC +# Copyright (c) 2025 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -26,31 +26,31 @@ Group: Development/Languages/Python URL: https://github.com/ronf/asyncssh Source: https://files.pythonhosted.org/packages/source/a/asyncssh/asyncssh-%{version}.tar.gz Patch0: gss_test.patch +# PATCH-FIX-UPSTREAM fido2-compat.patch +Patch1: fido2-compat.patch # SECTION test requirements BuildRequires: %{python_module bcrypt >= 3.1.3} -BuildRequires: %{python_module cryptography >= 2.8} -BuildRequires: %{python_module fido2 >= 0.8.1} +BuildRequires: %{python_module cryptography >= 39.0} +BuildRequires: %{python_module fido2 >= 2} BuildRequires: %{python_module gssapi >= 1.2.0} BuildRequires: %{python_module pip} BuildRequires: %{python_module pyOpenSSL >= 17.0.0} BuildRequires: %{python_module pytest} BuildRequires: %{python_module setuptools} -BuildRequires: %{python_module typing_extensions} -BuildRequires: %{python_module uvloop >= 0.9.1} +BuildRequires: %{python_module typing_extensions >= 4.0.0} BuildRequires: %{python_module wheel} BuildRequires: openssh BuildRequires: openssl -BuildRequires: (libnettle8 if python38-base) # /SECTION BuildRequires: fdupes BuildRequires: python-rpm-macros -Requires: python-bcrypt >= 3.1.3 -Requires: python-cryptography >= 2.8 -Requires: python-gssapi >= 1.2.0 -Requires: python-libnacl >= 1.4.2 -Requires: python-pyOpenSSL >= 17.0.0 -Recommends: libnettle8 -Recommends: python-fido2 >= 0.8.1 +Requires: python-cryptography >= 39.0 +Requires: python-typing_extensions >= 4.0.0 +Recommends: python-bcrypt >= 3.1.3 +Recommends: python-fido2 >= 2 +Recommends: python-gssapi >= 1.2.0 +Recommends: python-libnacl >= 1.4.2 +Recommends: python-pyOpenSSL >= 23.0.0 BuildArch: noarch %python_subpackages