aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.rst2
-rw-r--r--docs/hazmat/primitives/asymmetric/serialization.rst7
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py8
-rw-r--r--src/cryptography/hazmat/backends/openssl/ed25519.py5
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/ed25519.py4
-rw-r--r--src/cryptography/hazmat/primitives/serialization/ssh.py13
-rw-r--r--tests/hazmat/primitives/test_serialization.py38
7 files changed, 62 insertions, 15 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 3d3233ff..0670d75b 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -24,6 +24,8 @@ Changelog
OpenSSL 1.1.1.
* Added support for :doc:`/hazmat/primitives/asymmetric/ed25519` when using
OpenSSL 1.1.1.
+* :func:`~cryptography.hazmat.primitives.serialization.load_ssh_public_key` can
+ now load ``ed25519`` public keys.
* Add support for easily mapping an object identifier to its elliptic curve
class via
:func:`~cryptography.hazmat.primitives.asymmetric.ec.get_curve_for_oid`.
diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst
index dbb2bb63..84867efb 100644
--- a/docs/hazmat/primitives/asymmetric/serialization.rst
+++ b/docs/hazmat/primitives/asymmetric/serialization.rst
@@ -376,10 +376,6 @@ DSA keys look almost identical but begin with ``ssh-dss`` rather than
Deserialize a public key from OpenSSH (:rfc:`4253`) encoded data to an
instance of the public key type for the specified backend.
- .. note::
-
- Currently Ed25519 keys are not supported.
-
:param bytes data: The OpenSSH encoded key data.
:param backend: A backend which implements
@@ -391,8 +387,9 @@ DSA keys look almost identical but begin with ``ssh-dss`` rather than
:returns: One of
:class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`,
:class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey`,
- or
:class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey`
+ , or
+ :class:`~cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PublicKey`,
depending on the contents of ``data``.
:raises ValueError: If the OpenSSH data could not be properly decoded or
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index eab60778..b040b809 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -38,7 +38,7 @@ from cryptography.hazmat.backends.openssl.ec import (
_EllipticCurvePrivateKey, _EllipticCurvePublicKey
)
from cryptography.hazmat.backends.openssl.ed25519 import (
- _ED25519_KEY_SIZE, _Ed25519PrivateKey, _Ed25519PublicKey
+ _Ed25519PrivateKey, _Ed25519PublicKey
)
from cryptography.hazmat.backends.openssl.ed448 import (
_ED448_KEY_SIZE, _Ed448PrivateKey, _Ed448PublicKey
@@ -70,7 +70,7 @@ from cryptography.hazmat.backends.openssl.x509 import (
)
from cryptography.hazmat.bindings.openssl import binding
from cryptography.hazmat.primitives import hashes, serialization
-from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
+from cryptography.hazmat.primitives.asymmetric import dsa, ec, ed25519, rsa
from cryptography.hazmat.primitives.asymmetric.padding import (
MGF1, OAEP, PKCS1v15, PSS
)
@@ -2212,7 +2212,7 @@ class Backend(object):
def ed25519_load_public_bytes(self, data):
utils._check_bytes("data", data)
- if len(data) != _ED25519_KEY_SIZE:
+ if len(data) != ed25519._ED25519_KEY_SIZE:
raise ValueError("An Ed25519 public key is 32 bytes long")
evp_pkey = self._lib.EVP_PKEY_new_raw_public_key(
@@ -2224,7 +2224,7 @@ class Backend(object):
return _Ed25519PublicKey(self, evp_pkey)
def ed25519_load_private_bytes(self, data):
- if len(data) != _ED25519_KEY_SIZE:
+ if len(data) != ed25519._ED25519_KEY_SIZE:
raise ValueError("An Ed25519 private key is 32 bytes long")
utils._check_byteslike("data", data)
diff --git a/src/cryptography/hazmat/backends/openssl/ed25519.py b/src/cryptography/hazmat/backends/openssl/ed25519.py
index 15c1b1ec..f02f5622 100644
--- a/src/cryptography/hazmat/backends/openssl/ed25519.py
+++ b/src/cryptography/hazmat/backends/openssl/ed25519.py
@@ -7,12 +7,9 @@ from __future__ import absolute_import, division, print_function
from cryptography import exceptions, utils
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import (
- Ed25519PrivateKey, Ed25519PublicKey
+ Ed25519PrivateKey, Ed25519PublicKey, _ED25519_KEY_SIZE, _ED25519_SIG_SIZE
)
-_ED25519_KEY_SIZE = 32
-_ED25519_SIG_SIZE = 64
-
@utils.register_interface(Ed25519PublicKey)
class _Ed25519PublicKey(object):
diff --git a/src/cryptography/hazmat/primitives/asymmetric/ed25519.py b/src/cryptography/hazmat/primitives/asymmetric/ed25519.py
index 96be9c58..d89445fa 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/ed25519.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/ed25519.py
@@ -11,6 +11,10 @@ import six
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+_ED25519_KEY_SIZE = 32
+_ED25519_SIG_SIZE = 64
+
+
@six.add_metaclass(abc.ABCMeta)
class Ed25519PublicKey(object):
@classmethod
diff --git a/src/cryptography/hazmat/primitives/serialization/ssh.py b/src/cryptography/hazmat/primitives/serialization/ssh.py
index cb838927..a1d6c8c9 100644
--- a/src/cryptography/hazmat/primitives/serialization/ssh.py
+++ b/src/cryptography/hazmat/primitives/serialization/ssh.py
@@ -11,7 +11,7 @@ import six
from cryptography import utils
from cryptography.exceptions import UnsupportedAlgorithm
-from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
+from cryptography.hazmat.primitives.asymmetric import dsa, ec, ed25519, rsa
def load_ssh_public_key(data, backend):
@@ -31,6 +31,8 @@ def load_ssh_public_key(data, backend):
b'ecdsa-sha2-nistp256', b'ecdsa-sha2-nistp384', b'ecdsa-sha2-nistp521',
]:
loader = _load_ssh_ecdsa_public_key
+ elif key_type == b'ssh-ed25519':
+ loader = _load_ssh_ed25519_public_key
else:
raise UnsupportedAlgorithm('Key type is not supported.')
@@ -102,6 +104,15 @@ def _load_ssh_ecdsa_public_key(expected_key_type, decoded_data, backend):
return ec.EllipticCurvePublicKey.from_encoded_point(curve, data)
+def _load_ssh_ed25519_public_key(expected_key_type, decoded_data, backend):
+ data, rest = _ssh_read_next_string(decoded_data)
+
+ if rest:
+ raise ValueError('Key body contains extra bytes.')
+
+ return ed25519.Ed25519PublicKey.from_public_bytes(data)
+
+
def _ssh_read_next_string(data):
"""
Retrieves the next RFC 4251 string value from the data.
diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py
index ce3a4943..c5ce258c 100644
--- a/tests/hazmat/primitives/test_serialization.py
+++ b/tests/hazmat/primitives/test_serialization.py
@@ -16,7 +16,7 @@ from cryptography.hazmat.backends.interfaces import (
DERSerializationBackend, DSABackend, EllipticCurveBackend,
PEMSerializationBackend, RSABackend
)
-from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
+from cryptography.hazmat.primitives.asymmetric import dsa, ec, ed25519, rsa
from cryptography.hazmat.primitives.serialization import (
BestAvailableEncryption, Encoding, NoEncryption,
PrivateFormat, PublicFormat,
@@ -1274,6 +1274,42 @@ class TestECDSASSHSerialization(object):
load_ssh_public_key(ssh_key, backend)
+@pytest.mark.supported(
+ only_if=lambda backend: backend.ed25519_supported(),
+ skip_message="Requires OpenSSL with Ed25519 support"
+)
+class TestEd25519SSHSerialization(object):
+ def test_load_ssh_public_key(self, backend):
+ ssh_key = (
+ b"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG2fgpmpYO61qeAxGd0wgRaN/E4"
+ b"GR+xWvBmvxjxrB1vG user@chiron.local"
+ )
+ key = load_ssh_public_key(ssh_key, backend)
+ assert isinstance(key, ed25519.Ed25519PublicKey)
+ assert key.public_bytes(
+ Encoding.Raw, PublicFormat.Raw
+ ) == (
+ b"m\x9f\x82\x99\xa9`\xee\xb5\xa9\xe01\x19\xdd0\x81\x16\x8d\xfc"
+ b"N\x06G\xecV\xbc\x19\xaf\xc6<k\x07[\xc6"
+ )
+
+ def test_load_ssh_public_key_not_32_bytes(self, backend):
+ ssh_key = (
+ b"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI22fgpmpYO61qeAxGd0wgRaN/E4"
+ b"GR+xWvBmvxjxrB1vGaGVs user@chiron.local"
+ )
+ with pytest.raises(ValueError):
+ load_ssh_public_key(ssh_key, backend)
+
+ def test_load_ssh_public_key_trailing_data(self, backend):
+ ssh_key = (
+ b"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG2fgpmpYO61qeAxGd0wgRa"
+ b"N/E4GR+xWvBmvxjxrB1vGdHJhaWxpbmdkYXRh user@chiron.local"
+ )
+ with pytest.raises(ValueError):
+ load_ssh_public_key(ssh_key, backend)
+
+
class TestKeySerializationEncryptionTypes(object):
def test_non_bytes_password(self):
with pytest.raises(ValueError):