forked from pool/python-asyncssh
Accepting request 713406 from devel:languages:python
- update to 1.17.0 - drop old_openssl.patch * Added support for “reverse direction” SSH connections, useful to support applications like NETCONF Call Home, described in RFC 8071. * Added support for the PyCA implementation of Chacha20-Poly1305, eliminating the dependency on libnacl/libsodium to provide this functionality, as long as OpenSSL 1.1.1b or later is installed. * Restored libnacl support for Curve25519/Ed25519 on systems which have an older version of OpenSSL that doesn’t have that support. This fallback also applies to Chacha20-Poly1305. * Disabled the use of RSA SHA-2 signatures when using the Pageant or Windows 10 OpenSSH agent on Windows, since neither of those support the signature flags options to request them. * Fixed a regression where a callable was no longer usable in the sftp_factory argument of create_server. OBS-URL: https://build.opensuse.org/request/show/713406 OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/python-asyncssh?expand=0&rev=7
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b9e4612daed054725d658726dda7dfdd9f9deb711102e874d197c326bc0a2a62
|
||||
size 323419
|
||||
3
asyncssh-1.17.0.tar.gz
Normal file
3
asyncssh-1.17.0.tar.gz
Normal file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ed34d4731c15453af6f0528bc8ad58db2dd3d632e69d671e36259d437d9b37e3
|
||||
size 326554
|
||||
@@ -1,379 +0,0 @@
|
||||
From 1dee113bb3e4a6888de562b0413e9abd6a0f0f04 Mon Sep 17 00:00:00 2001
|
||||
From: Ron Frederick <ronf@timeheart.net>
|
||||
Date: Fri, 19 Apr 2019 16:19:43 -0700
|
||||
Subject: [PATCH] Restore libnacl support for curve25519/ed25519 as a fallback
|
||||
for PyCA
|
||||
|
||||
This commit restores the ability to use libnacl/libsodium for
|
||||
curve25519/ed25519 when the version of OpenSSL available through
|
||||
the cryptography package does not have this support.
|
||||
|
||||
This commit also fixes a bug where ed25519 and ed448 were being
|
||||
registered as valid host key algorithms even when support for them was
|
||||
not available on the system.
|
||||
---
|
||||
asyncssh/crypto/ed.py | 273 +++++++++++++++++++++++++++++++-----------
|
||||
asyncssh/eddsa.py | 16 ++-
|
||||
docs/api.rst | 10 +-
|
||||
3 files changed, 219 insertions(+), 80 deletions(-)
|
||||
|
||||
Index: asyncssh-1.16.1/asyncssh/crypto/ed.py
|
||||
===================================================================
|
||||
--- asyncssh-1.16.1.orig/asyncssh/crypto/ed.py
|
||||
+++ asyncssh-1.16.1/asyncssh/crypto/ed.py
|
||||
@@ -18,7 +18,10 @@
|
||||
# Contributors:
|
||||
# Ron Frederick - initial implementation, API, and documentation
|
||||
|
||||
-"""A shim around PyCA for Edwards-curve keys and key exchange"""
|
||||
+"""A shim around PyCA and libnacl for Edwards-curve keys and key exchange"""
|
||||
+
|
||||
+import ctypes
|
||||
+import os
|
||||
|
||||
from cryptography.exceptions import InvalidSignature
|
||||
from cryptography.hazmat.backends.openssl import backend
|
||||
@@ -38,112 +41,240 @@ curve25519_available = backend.x25519_su
|
||||
curve448_available = backend.x448_supported()
|
||||
|
||||
|
||||
-class _EdKey(PyCAKey):
|
||||
- """Base class for shim around PyCA for Ed25519/Ed448 keys"""
|
||||
+if ed25519_available: # pragma: no branch
|
||||
+ class _EdKey(PyCAKey):
|
||||
+ """Base class for shim around PyCA for Ed25519/Ed448 keys"""
|
||||
|
||||
- def __init__(self, pyca_key, pub, priv=None):
|
||||
- super().__init__(pyca_key)
|
||||
+ def __init__(self, pyca_key, pub, priv=None):
|
||||
+ super().__init__(pyca_key)
|
||||
|
||||
- self._pub = pub
|
||||
- self._priv = priv
|
||||
+ self._pub = pub
|
||||
+ self._priv = priv
|
||||
|
||||
- @property
|
||||
- def public_value(self):
|
||||
- """Return the public value encoded as a byte string"""
|
||||
+ @property
|
||||
+ def public_value(self):
|
||||
+ """Return the public value encoded as a byte string"""
|
||||
|
||||
- return self._pub
|
||||
+ return self._pub
|
||||
|
||||
- @property
|
||||
- def private_value(self):
|
||||
- """Return the private value encoded as a byte string"""
|
||||
+ @property
|
||||
+ def private_value(self):
|
||||
+ """Return the private value encoded as a byte string"""
|
||||
|
||||
- return self._priv
|
||||
+ return self._priv
|
||||
|
||||
|
||||
-class EdDSAPrivateKey(_EdKey):
|
||||
- """A shim around PyCA for EdDSA private keys"""
|
||||
+ class EdDSAPrivateKey(_EdKey):
|
||||
+ """A shim around PyCA for EdDSA private keys"""
|
||||
|
||||
- _priv_classes = {b'ed25519': ed25519.Ed25519PrivateKey,
|
||||
- b'ed448': ed448.Ed448PrivateKey}
|
||||
+ _priv_classes = {b'ed25519': ed25519.Ed25519PrivateKey,
|
||||
+ b'ed448': ed448.Ed448PrivateKey}
|
||||
|
||||
- @classmethod
|
||||
- def construct(cls, curve_id, priv):
|
||||
- """Construct an EdDSA private key"""
|
||||
+ @classmethod
|
||||
+ def construct(cls, curve_id, priv):
|
||||
+ """Construct an EdDSA private key"""
|
||||
|
||||
- priv_cls = cls._priv_classes[curve_id]
|
||||
- priv_key = priv_cls.from_private_bytes(priv)
|
||||
- pub_key = priv_key.public_key()
|
||||
- pub = pub_key.public_bytes(Encoding.Raw, PublicFormat.Raw)
|
||||
+ priv_cls = cls._priv_classes[curve_id]
|
||||
+ priv_key = priv_cls.from_private_bytes(priv)
|
||||
+ pub_key = priv_key.public_key()
|
||||
+ pub = pub_key.public_bytes(Encoding.Raw, PublicFormat.Raw)
|
||||
|
||||
- return cls(priv_key, pub, priv)
|
||||
+ return cls(priv_key, pub, priv)
|
||||
|
||||
- @classmethod
|
||||
- def generate(cls, curve_id):
|
||||
- """Generate a new ECDSA private key"""
|
||||
+ @classmethod
|
||||
+ def generate(cls, curve_id):
|
||||
+ """Generate a new EdDSA private key"""
|
||||
|
||||
- priv_cls = cls._priv_classes[curve_id]
|
||||
- priv_key = priv_cls.generate()
|
||||
- priv = priv_key.private_bytes(Encoding.Raw, PrivateFormat.Raw,
|
||||
- NoEncryption())
|
||||
+ priv_cls = cls._priv_classes[curve_id]
|
||||
+ priv_key = priv_cls.generate()
|
||||
+ priv = priv_key.private_bytes(Encoding.Raw, PrivateFormat.Raw,
|
||||
+ NoEncryption())
|
||||
|
||||
- pub_key = priv_key.public_key()
|
||||
- pub = pub_key.public_bytes(Encoding.Raw, PublicFormat.Raw)
|
||||
+ pub_key = priv_key.public_key()
|
||||
+ pub = pub_key.public_bytes(Encoding.Raw, PublicFormat.Raw)
|
||||
|
||||
- return cls(priv_key, pub, priv)
|
||||
+ return cls(priv_key, pub, priv)
|
||||
|
||||
- def sign(self, data):
|
||||
- """Sign a block of data"""
|
||||
+ def sign(self, data):
|
||||
+ """Sign a block of data"""
|
||||
|
||||
- return self.pyca_key.sign(data)
|
||||
+ return self.pyca_key.sign(data)
|
||||
|
||||
|
||||
-class EdDSAPublicKey(_EdKey):
|
||||
- """A shim around PyCA for EdDSA public keys"""
|
||||
+ class EdDSAPublicKey(_EdKey):
|
||||
+ """A shim around PyCA for EdDSA public keys"""
|
||||
|
||||
- _pub_classes = {b'ed25519': ed25519.Ed25519PublicKey,
|
||||
- b'ed448': ed448.Ed448PublicKey}
|
||||
+ _pub_classes = {b'ed25519': ed25519.Ed25519PublicKey,
|
||||
+ b'ed448': ed448.Ed448PublicKey}
|
||||
|
||||
- @classmethod
|
||||
- def construct(cls, curve_id, pub):
|
||||
- """Construct an ECDSA public key"""
|
||||
+ @classmethod
|
||||
+ def construct(cls, curve_id, pub):
|
||||
+ """Construct an EdDSA public key"""
|
||||
|
||||
- pub_cls = cls._pub_classes[curve_id]
|
||||
- pub_key = pub_cls.from_public_bytes(pub)
|
||||
+ pub_cls = cls._pub_classes[curve_id]
|
||||
+ pub_key = pub_cls.from_public_bytes(pub)
|
||||
|
||||
- return cls(pub_key, pub)
|
||||
+ return cls(pub_key, pub)
|
||||
|
||||
- def verify(self, data, sig):
|
||||
- """Verify the signature on a block of data"""
|
||||
+ def verify(self, data, sig):
|
||||
+ """Verify the signature on a block of data"""
|
||||
|
||||
- try:
|
||||
- self.pyca_key.verify(sig, data)
|
||||
- return True
|
||||
- except InvalidSignature:
|
||||
- return False
|
||||
+ try:
|
||||
+ self.pyca_key.verify(sig, data)
|
||||
+ return True
|
||||
+ except InvalidSignature:
|
||||
+ return False
|
||||
+else: # pragma: no cover
|
||||
+ class _EdKey:
|
||||
+ """Base class for shim around libnacl for Ed25519 keys"""
|
||||
|
||||
+ def __init__(self, pub, priv=None):
|
||||
+ self._pub = pub
|
||||
+ self._priv = priv
|
||||
|
||||
-class Curve25519DH:
|
||||
- """Curve25519 Diffie Hellman implementation"""
|
||||
+ @property
|
||||
+ def public_value(self):
|
||||
+ """Return the public value encoded as a byte string"""
|
||||
|
||||
- def __init__(self):
|
||||
- self._priv_key = x25519.X25519PrivateKey.generate()
|
||||
+ return self._pub
|
||||
+
|
||||
+ @property
|
||||
+ def private_value(self):
|
||||
+ """Return the private value encoded as a byte string"""
|
||||
+
|
||||
+ return self._priv[:-len(self._pub)] if self._priv else None
|
||||
+
|
||||
+
|
||||
+ class EdDSAPrivateKey(_EdKey):
|
||||
+ """A shim around libnacl for Ed25519 private keys"""
|
||||
+
|
||||
+ @classmethod
|
||||
+ def construct(cls, curve_id, priv):
|
||||
+ """Construct an EdDSA private key"""
|
||||
+
|
||||
+ # pylint: disable=unused-argument
|
||||
+
|
||||
+ return cls(*_ed25519_construct_keypair(priv))
|
||||
+
|
||||
+ @classmethod
|
||||
+ def generate(cls, curve_id):
|
||||
+ """Generate a new EdDSA private key"""
|
||||
+
|
||||
+ # pylint: disable=unused-argument
|
||||
+
|
||||
+ return cls(*_ed25519_generate_keypair())
|
||||
+
|
||||
+ def sign(self, data):
|
||||
+ """Sign a block of data"""
|
||||
+
|
||||
+ return _ed25519_sign(data, self._priv)[:-len(data)]
|
||||
+
|
||||
+
|
||||
+ class EdDSAPublicKey(_EdKey):
|
||||
+ """A shim around libnacl for Ed25519 public keys"""
|
||||
+
|
||||
+ @classmethod
|
||||
+ def construct(cls, curve_id, pub):
|
||||
+ """Construct an EdDSA public key"""
|
||||
+
|
||||
+ # pylint: disable=unused-argument
|
||||
+
|
||||
+ if len(pub) != _ED25519_PUBLIC_BYTES:
|
||||
+ raise ValueError('Invalid Ed25519 public key')
|
||||
+
|
||||
+ return cls(pub)
|
||||
+
|
||||
+ def verify(self, data, sig):
|
||||
+ """Verify the signature on a block of data"""
|
||||
+
|
||||
+ try:
|
||||
+ return _ed25519_verify(sig + data, self._pub) == data
|
||||
+ except ValueError:
|
||||
+ return False
|
||||
+
|
||||
+ try:
|
||||
+ import libnacl
|
||||
+
|
||||
+ _ED25519_PUBLIC_BYTES = libnacl.crypto_sign_ed25519_PUBLICKEYBYTES
|
||||
+
|
||||
+ _ed25519_construct_keypair = libnacl.crypto_sign_seed_keypair
|
||||
+ _ed25519_generate_keypair = libnacl.crypto_sign_keypair
|
||||
+ _ed25519_sign = libnacl.crypto_sign
|
||||
+ _ed25519_verify = libnacl.crypto_sign_open
|
||||
+
|
||||
+ ed25519_available = True
|
||||
+ except (ImportError, OSError, AttributeError):
|
||||
+ pass
|
||||
+
|
||||
+
|
||||
+if curve25519_available: # pragma: no branch
|
||||
+ class Curve25519DH:
|
||||
+ """Curve25519 Diffie Hellman implementation based on PyCA"""
|
||||
+
|
||||
+ def __init__(self):
|
||||
+ self._priv_key = x25519.X25519PrivateKey.generate()
|
||||
+
|
||||
+ def get_public(self):
|
||||
+ """Return the public key to send in the handshake"""
|
||||
+
|
||||
+ return self._priv_key.public_key().public_bytes(Encoding.Raw,
|
||||
+ PublicFormat.Raw)
|
||||
+
|
||||
+ def get_shared(self, peer_public):
|
||||
+ """Return the shared key from the peer's public key"""
|
||||
+
|
||||
+ peer_key = x25519.X25519PublicKey.from_public_bytes(peer_public)
|
||||
+ shared = self._priv_key.exchange(peer_key)
|
||||
+ return int.from_bytes(shared, 'big')
|
||||
+else: # pragma: no cover
|
||||
+ class Curve25519DH:
|
||||
+ """Curve25519 Diffie Hellman implementation based on libnacl"""
|
||||
+
|
||||
+ def __init__(self):
|
||||
+ self._private = os.urandom(_CURVE25519_SCALARBYTES)
|
||||
+
|
||||
+ def get_public(self):
|
||||
+ """Return the public key to send in the handshake"""
|
||||
+
|
||||
+ public = ctypes.create_string_buffer(_CURVE25519_BYTES)
|
||||
+
|
||||
+ if _curve25519_base(public, self._private) != 0:
|
||||
+ # This error is never returned by libsodium
|
||||
+ raise ValueError('Curve25519 failed') # pragma: no cover
|
||||
+
|
||||
+ return public.raw
|
||||
+
|
||||
+ def get_shared(self, peer_public):
|
||||
+ """Return the shared key from the peer's public key"""
|
||||
+
|
||||
+ if len(peer_public) != _CURVE25519_BYTES:
|
||||
+ raise ValueError('Invalid curve25519 public key size')
|
||||
+
|
||||
+ shared = ctypes.create_string_buffer(_CURVE25519_BYTES)
|
||||
+
|
||||
+ if _curve25519(shared, self._private, peer_public) != 0:
|
||||
+ # This error is never returned by libsodium
|
||||
+ raise ValueError('Curve25519 failed') # pragma: no cover
|
||||
+
|
||||
+ return int.from_bytes(shared.raw, 'big')
|
||||
|
||||
- def get_public(self):
|
||||
- """Return the public key to send in the handshake"""
|
||||
+ try:
|
||||
+ from libnacl import nacl
|
||||
|
||||
- return self._priv_key.public_key().public_bytes(Encoding.Raw,
|
||||
- PublicFormat.Raw)
|
||||
+ _CURVE25519_BYTES = nacl.crypto_scalarmult_curve25519_bytes()
|
||||
+ _CURVE25519_SCALARBYTES = \
|
||||
+ nacl.crypto_scalarmult_curve25519_scalarbytes()
|
||||
|
||||
- def get_shared(self, peer_public):
|
||||
- """Return the shared key from the peer's public key"""
|
||||
+ _curve25519 = nacl.crypto_scalarmult_curve25519
|
||||
+ _curve25519_base = nacl.crypto_scalarmult_curve25519_base
|
||||
|
||||
- peer_key = x25519.X25519PublicKey.from_public_bytes(peer_public)
|
||||
- shared = self._priv_key.exchange(peer_key)
|
||||
- return int.from_bytes(shared, 'big')
|
||||
+ curve25519_available = True
|
||||
+ except (ImportError, OSError, AttributeError):
|
||||
+ pass
|
||||
|
||||
|
||||
class Curve448DH:
|
||||
- """Curve448 Diffie Hellman implementation"""
|
||||
+ """Curve448 Diffie Hellman implementation based on PyCA"""
|
||||
|
||||
def __init__(self):
|
||||
self._priv_key = x448.X448PrivateKey.generate()
|
||||
Index: asyncssh-1.16.1/asyncssh/eddsa.py
|
||||
===================================================================
|
||||
--- asyncssh-1.16.1.orig/asyncssh/eddsa.py
|
||||
+++ asyncssh-1.16.1/asyncssh/eddsa.py
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
from .asn1 import ASN1DecodeError, ObjectIdentifier, der_encode, der_decode
|
||||
from .crypto import EdDSAPrivateKey, EdDSAPublicKey
|
||||
+from .crypto import ed25519_available, ed448_available
|
||||
from .packet import String
|
||||
from .public_key import OMIT, SSHKey, SSHOpenSSHCertificateV01
|
||||
from .public_key import KeyImportError, KeyExportError
|
||||
@@ -186,10 +187,15 @@ class _Ed448Key(_EdKey):
|
||||
all_sig_algorithms = set(sig_algorithms)
|
||||
|
||||
|
||||
-for _cls in (_Ed25519Key, _Ed448Key):
|
||||
- _cert_algorithm = _cls.algorithm + b'-cert-v01@openssh.com'
|
||||
+if ed25519_available: # pragma: no branch
|
||||
+ register_public_key_alg(b'ssh-ed25519', _Ed25519Key)
|
||||
|
||||
- register_public_key_alg(_cls.algorithm, _cls)
|
||||
+ register_certificate_alg(1, b'ssh-ed25519',
|
||||
+ b'ssh-ed25519-cert-v01@openssh.com',
|
||||
+ _Ed25519Key, SSHOpenSSHCertificateV01)
|
||||
|
||||
- register_certificate_alg(1, _cls.algorithm, _cert_algorithm,
|
||||
- _cls, SSHOpenSSHCertificateV01)
|
||||
+if ed448_available: # pragma: no branch
|
||||
+ register_public_key_alg(b'ssh-ed448', _Ed448Key)
|
||||
+
|
||||
+ register_certificate_alg(1, b'ssh-ed448', b'ssh-ed448-cert-v01@openssh.com',
|
||||
+ _Ed448Key, SSHOpenSSHCertificateV01)
|
||||
@@ -1,3 +1,22 @@
|
||||
-------------------------------------------------------------------
|
||||
Tue Jun 4 13:07:40 UTC 2019 - Ondřej Súkup <mimi.vx@gmail.com>
|
||||
|
||||
- update to 1.17.0
|
||||
- drop old_openssl.patch
|
||||
* Added support for “reverse direction” SSH connections, useful to support
|
||||
applications like NETCONF Call Home, described in RFC 8071.
|
||||
* Added support for the PyCA implementation of Chacha20-Poly1305, eliminating
|
||||
the dependency on libnacl/libsodium to provide this functionality,
|
||||
as long as OpenSSL 1.1.1b or later is installed.
|
||||
* Restored libnacl support for Curve25519/Ed25519 on systems which have
|
||||
an older version of OpenSSL that doesn’t have that support.
|
||||
This fallback also applies to Chacha20-Poly1305.
|
||||
* Disabled the use of RSA SHA-2 signatures when using the Pageant or Windows 10
|
||||
OpenSSH agent on Windows, since neither of those support the signature
|
||||
flags options to request them.
|
||||
* Fixed a regression where a callable was no longer usable in the
|
||||
sftp_factory argument of create_server.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Tue Apr 23 08:29:31 UTC 2019 - Ondřej Súkup <mimi.vx@gmail.com>
|
||||
|
||||
|
||||
@@ -19,16 +19,15 @@
|
||||
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
|
||||
%define skip_python2 1
|
||||
Name: python-asyncssh
|
||||
Version: 1.16.1
|
||||
Version: 1.17.0
|
||||
Release: 0
|
||||
Summary: Asynchronous SSHv2 client and server library
|
||||
License: EPL-2.0 OR GPL-2.0-or-later
|
||||
Group: Development/Languages/Python
|
||||
Url: http://asyncssh.timeheart.net
|
||||
Source: https://files.pythonhosted.org/packages/source/a/asyncssh/asyncssh-%{version}.tar.gz
|
||||
Patch0: old_openssl.patch
|
||||
BuildRequires: %{python_module bcrypt >= 3.1.3}
|
||||
BuildRequires: %{python_module cryptography >= 2.6.1}
|
||||
BuildRequires: %{python_module cryptography >= 2.7}
|
||||
BuildRequires: %{python_module gssapi >= 1.2.0}
|
||||
BuildRequires: %{python_module pyOpenSSL >= 17.0.0}
|
||||
BuildRequires: %{python_module setuptools}
|
||||
@@ -37,7 +36,7 @@ BuildRequires: openssh
|
||||
BuildRequires: openssl
|
||||
BuildRequires: python-rpm-macros
|
||||
Requires: python-bcrypt >= 3.1.3
|
||||
Requires: python-cryptography >= 2.6.1
|
||||
Requires: python-cryptography >= 2.7
|
||||
Requires: python-gssapi >= 1.2.0
|
||||
Requires: python-libnacl >= 1.4.2
|
||||
Requires: python-pyOpenSSL >= 17.0.0
|
||||
@@ -53,7 +52,6 @@ asyncio framework.
|
||||
|
||||
%prep
|
||||
%setup -q -n asyncssh-%{version}
|
||||
%patch0 -p1
|
||||
|
||||
%build
|
||||
%python_build
|
||||
|
||||
Reference in New Issue
Block a user