Hello community,
here is the log from the commit of package python-asyncssh for openSUSE:Factory checked in at 2019-04-23 14:38:50
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-asyncssh (Old)
and /work/SRC/openSUSE:Factory/.python-asyncssh.new.5536 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-asyncssh"
Tue Apr 23 14:38:50 2019 rev:6 rq:697004 version:1.16.1
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-asyncssh/python-asyncssh.changes 2019-04-02 09:22:51.184731969 +0200
+++ /work/SRC/openSUSE:Factory/.python-asyncssh.new.5536/python-asyncssh.changes 2019-04-23 14:38:58.697576472 +0200
@@ -1,0 +2,6 @@
+Tue Apr 23 08:29:31 UTC 2019 - Ondřej Súkup
From 1dee113bb3e4a6888de562b0413e9abd6a0f0f04 Mon Sep 17 00:00:00 2001 From: Ron Frederick
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)