aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Kehrer <paul.l.kehrer@gmail.com>2019-01-14 21:50:17 -0600
committerAlex Gaynor <alex.gaynor@gmail.com>2019-01-14 22:50:17 -0500
commitc6c25c21496858271fbc4c89fb102074fd3d5f60 (patch)
tree009896d2b53e2d45f050b35320609bf348f0e31c
parentaeb3acbe9abffba68da3cc8b6bc0f3c2acb9bd9d (diff)
downloadplatform_external_python_cryptography-c6c25c21496858271fbc4c89fb102074fd3d5f60.tar.gz
platform_external_python_cryptography-c6c25c21496858271fbc4c89fb102074fd3d5f60.tar.bz2
platform_external_python_cryptography-c6c25c21496858271fbc4c89fb102074fd3d5f60.zip
Serialization x25519 (#4688)
* modify x25519 serialization to match x448 supports raw and pkcs8 encoding on private_bytes supports raw and subjectpublickeyinfo on public_bytes deprecates zero argument call to public_bytes * add docs * this is public now * don't need that * review feedback
-rw-r--r--CHANGELOG.rst4
-rw-r--r--docs/hazmat/primitives/asymmetric/x25519.rst88
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py6
-rw-r--r--src/cryptography/hazmat/backends/openssl/x25519.py93
-rw-r--r--src/cryptography/hazmat/backends/openssl/x448.py7
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/x25519.py10
-rw-r--r--src/cryptography/hazmat/primitives/serialization/__init__.py3
-rw-r--r--tests/hazmat/primitives/test_serialization.py64
-rw-r--r--tests/hazmat/primitives/test_x25519.py122
-rw-r--r--tests/wycheproof/test_x25519.py2
10 files changed, 378 insertions, 21 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 42772af0..307f1551 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -37,6 +37,10 @@ Changelog
:meth:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicNumbers.from_encoded_point`.
* Added :attr:`~cryptography.x509.ocsp.OCSPResponse.signature_hash_algorithm`
to ``OCSPResponse``.
+* Updated :doc:`/hazmat/primitives/asymmetric/x25519` support to allow
+ additional serialization methods. Calling
+ :meth:`~cryptography.hazmat.primitives.asymmetric.x25519.X25519PublicKey.public_bytes`
+ with no arguments has been deprecated.
.. _v2-4-2:
diff --git a/docs/hazmat/primitives/asymmetric/x25519.rst b/docs/hazmat/primitives/asymmetric/x25519.rst
index 67ed2809..ea01fbaa 100644
--- a/docs/hazmat/primitives/asymmetric/x25519.rst
+++ b/docs/hazmat/primitives/asymmetric/x25519.rst
@@ -66,6 +66,29 @@ Key interfaces
:returns: :class:`X25519PrivateKey`
+ .. classmethod:: from_private_bytes(data)
+
+ .. versionadded:: 2.5
+
+ A class method for loading an X25519 key encoded as
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.Raw`.
+
+ :param bytes data: 32 byte private key.
+
+ :returns: :class:`X25519PrivateKey`
+
+ .. doctest::
+
+ >>> from cryptography.hazmat.primitives import serialization
+ >>> from cryptography.hazmat.primitives.asymmetric import x25519
+ >>> private_key = x25519.X25519PrivateKey.generate()
+ >>> private_bytes = private_key.private_bytes(
+ ... encoding=serialization.Encoding.Raw,
+ ... format=serialization.PrivateFormat.Raw,
+ ... encryption_algorithm=serialization.NoEncryption()
+ ... )
+ >>> loaded_private_key = x25519.X25519PrivateKey.from_private_bytes(private_bytes)
+
.. method:: public_key()
:returns: :class:`X25519PublicKey`
@@ -77,6 +100,38 @@ Key interfaces
:returns bytes: A shared key.
+ .. method:: private_bytes(encoding, format, encryption_algorithm)
+
+ .. versionadded:: 2.5
+
+ Allows serialization of the key to bytes. Encoding (
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM`,
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`, or
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.Raw`) and
+ format (
+ :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.PKCS8`
+ or
+ :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.Raw`
+ ) are chosen to define the exact serialization.
+
+ :param encoding: A value from the
+ :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum.
+
+ :param format: A value from the
+ :class:`~cryptography.hazmat.primitives.serialization.PrivateFormat`
+ enum. If the ``encoding`` is
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.Raw`
+ then ``format`` must be
+ :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.Raw`
+ , otherwise it must be
+ :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.PKCS8`.
+
+ :param encryption_algorithm: An instance of an object conforming to the
+ :class:`~cryptography.hazmat.primitives.serialization.KeySerializationEncryption`
+ interface.
+
+ :return bytes: Serialized key.
+
.. class:: X25519PublicKey
.. versionadded:: 2.0
@@ -92,12 +147,37 @@ Key interfaces
>>> from cryptography.hazmat.primitives.asymmetric import x25519
>>> private_key = x25519.X25519PrivateKey.generate()
>>> public_key = private_key.public_key()
- >>> public_bytes = public_key.public_bytes()
+ >>> public_bytes = public_key.public_bytes(
+ ... encoding=serialization.Encoding.Raw,
+ ... format=serialization.PublicFormat.Raw
+ ... )
>>> loaded_public_key = x25519.X25519PublicKey.from_public_bytes(public_bytes)
- .. method:: public_bytes()
-
- :returns bytes: The raw bytes of the public key.
+ .. method:: public_bytes(encoding, format)
+
+ Allows serialization of the key to bytes. Encoding (
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM`,
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`, or
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.Raw`) and
+ format (
+ :attr:`~cryptography.hazmat.primitives.serialization.PublicFormat.SubjectPublicKeyInfo`
+ or
+ :attr:`~cryptography.hazmat.primitives.serialization.PublicFormat.Raw`
+ ) are chosen to define the exact serialization.
+
+ :param encoding: A value from the
+ :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum.
+
+ :param format: A value from the
+ :class:`~cryptography.hazmat.primitives.serialization.PublicFormat`
+ enum. If the ``encoding`` is
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.Raw`
+ then ``format`` must be
+ :attr:`~cryptography.hazmat.primitives.serialization.PublicFormat.Raw`
+ , otherwise it must be
+ :attr:`~cryptography.hazmat.primitives.serialization.PublicFormat.SubjectPublicKeyInfo`.
+
+ :returns bytes: The public key bytes.
.. _`Diffie-Hellman key exchange`: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index ecebe7b8..9eca5f8a 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -511,6 +511,9 @@ class Backend(object):
elif key_type == getattr(self._lib, "EVP_PKEY_X448", None):
# EVP_PKEY_X448 is not present in OpenSSL < 1.1.1
return _X448PrivateKey(self, evp_pkey)
+ elif key_type == getattr(self._lib, "EVP_PKEY_X25519", None):
+ # EVP_PKEY_X25519 is not present in OpenSSL < 1.1.0
+ return _X25519PrivateKey(self, evp_pkey)
else:
raise UnsupportedAlgorithm("Unsupported key type.")
@@ -545,6 +548,9 @@ class Backend(object):
elif key_type == getattr(self._lib, "EVP_PKEY_X448", None):
# EVP_PKEY_X448 is not present in OpenSSL < 1.1.1
return _X448PublicKey(self, evp_pkey)
+ elif key_type == getattr(self._lib, "EVP_PKEY_X25519", None):
+ # EVP_PKEY_X25519 is not present in OpenSSL < 1.1.0
+ return _X25519PublicKey(self, evp_pkey)
else:
raise UnsupportedAlgorithm("Unsupported key type.")
diff --git a/src/cryptography/hazmat/backends/openssl/x25519.py b/src/cryptography/hazmat/backends/openssl/x25519.py
index 06f3985d..914f5941 100644
--- a/src/cryptography/hazmat/backends/openssl/x25519.py
+++ b/src/cryptography/hazmat/backends/openssl/x25519.py
@@ -4,20 +4,66 @@
from __future__ import absolute_import, division, print_function
+import warnings
+
from cryptography import utils
from cryptography.hazmat.backends.openssl.utils import _evp_pkey_derive
+from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.x25519 import (
X25519PrivateKey, X25519PublicKey
)
+_X25519_KEY_SIZE = 32
+
+
@utils.register_interface(X25519PublicKey)
class _X25519PublicKey(object):
def __init__(self, backend, evp_pkey):
self._backend = backend
self._evp_pkey = evp_pkey
- def public_bytes(self):
+ def public_bytes(self, encoding=None, format=None):
+ if encoding is None or format is None:
+ if encoding is not None or format is not None:
+ raise ValueError("Both encoding and format are required")
+ else:
+ warnings.warn(
+ "public_bytes now requires encoding and format arguments. "
+ "Support for calling without arguments will be removed in "
+ "cryptography 2.7",
+ utils.DeprecatedIn25,
+ )
+ encoding = serialization.Encoding.Raw
+ format = serialization.PublicFormat.Raw
+ if (
+ encoding is serialization.Encoding.Raw or
+ format is serialization.PublicFormat.Raw
+ ):
+ if (
+ encoding is not serialization.Encoding.Raw or
+ format is not serialization.PublicFormat.Raw
+ ):
+ raise ValueError(
+ "When using Raw both encoding and format must be Raw"
+ )
+
+ return self._raw_public_bytes()
+
+ if (
+ encoding in serialization._PEM_DER and
+ format is not serialization.PublicFormat.SubjectPublicKeyInfo
+ ):
+ raise ValueError(
+ "format must be SubjectPublicKeyInfo when encoding is PEM or "
+ "DER"
+ )
+
+ return self._backend._public_key_bytes(
+ encoding, format, self, self._evp_pkey, None
+ )
+
+ def _raw_public_bytes(self):
ucharpp = self._backend._ffi.new("unsigned char **")
res = self._backend._lib.EVP_PKEY_get1_tls_encodedpoint(
self._evp_pkey, ucharpp
@@ -56,3 +102,48 @@ class _X25519PrivateKey(object):
return _evp_pkey_derive(
self._backend, self._evp_pkey, peer_public_key
)
+
+ def private_bytes(self, encoding, format, encryption_algorithm):
+ if (
+ encoding is serialization.Encoding.Raw or
+ format is serialization.PublicFormat.Raw
+ ):
+ if (
+ format is not serialization.PrivateFormat.Raw or
+ encoding is not serialization.Encoding.Raw or not
+ isinstance(encryption_algorithm, serialization.NoEncryption)
+ ):
+ raise ValueError(
+ "When using Raw both encoding and format must be Raw "
+ "and encryption_algorithm must be NoEncryption"
+ )
+
+ return self._raw_private_bytes()
+
+ if (
+ encoding in serialization._PEM_DER and
+ format is not serialization.PrivateFormat.PKCS8
+ ):
+ raise ValueError(
+ "format must be PKCS8 when encoding is PEM or DER"
+ )
+
+ return self._backend._private_key_bytes(
+ encoding, format, encryption_algorithm, self._evp_pkey, None
+ )
+
+ def _raw_private_bytes(self):
+ # When we drop support for CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 we can
+ # switch this to EVP_PKEY_new_raw_private_key
+ # The trick we use here is serializing to a PKCS8 key and just
+ # using the last 32 bytes, which is the key itself.
+ bio = self._backend._create_mem_bio_gc()
+ res = self._backend._lib.i2d_PKCS8PrivateKey_bio(
+ bio, self._evp_pkey,
+ self._backend._ffi.NULL, self._backend._ffi.NULL,
+ 0, self._backend._ffi.NULL, self._backend._ffi.NULL
+ )
+ self._backend.openssl_assert(res == 1)
+ pkcs8 = self._backend._read_mem_bio(bio)
+ self._backend.openssl_assert(len(pkcs8) == 48)
+ return pkcs8[-_X25519_KEY_SIZE:]
diff --git a/src/cryptography/hazmat/backends/openssl/x448.py b/src/cryptography/hazmat/backends/openssl/x448.py
index 3792fd79..13e4ce15 100644
--- a/src/cryptography/hazmat/backends/openssl/x448.py
+++ b/src/cryptography/hazmat/backends/openssl/x448.py
@@ -12,9 +12,6 @@ from cryptography.hazmat.primitives.asymmetric.x448 import (
)
_X448_KEY_SIZE = 56
-_PEM_DER = (
- serialization.Encoding.PEM, serialization.Encoding.DER
-)
@utils.register_interface(X448PublicKey)
@@ -39,7 +36,7 @@ class _X448PublicKey(object):
return self._raw_public_bytes()
if (
- encoding in _PEM_DER and
+ encoding in serialization._PEM_DER and
format is not serialization.PublicFormat.SubjectPublicKeyInfo
):
raise ValueError(
@@ -104,7 +101,7 @@ class _X448PrivateKey(object):
return self._raw_private_bytes()
if (
- encoding in _PEM_DER and
+ encoding in serialization._PEM_DER and
format is not serialization.PrivateFormat.PKCS8
):
raise ValueError(
diff --git a/src/cryptography/hazmat/primitives/asymmetric/x25519.py b/src/cryptography/hazmat/primitives/asymmetric/x25519.py
index f4bdf3db..94602261 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/x25519.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/x25519.py
@@ -28,7 +28,7 @@ class X25519PublicKey(object):
return backend.x25519_load_public_bytes(data)
@abc.abstractmethod
- def public_bytes(self):
+ def public_bytes(self, encoding=None, format=None):
"""
The serialized bytes of the public key.
"""
@@ -47,7 +47,7 @@ class X25519PrivateKey(object):
return backend.x25519_generate_key()
@classmethod
- def _from_private_bytes(cls, data):
+ def from_private_bytes(cls, data):
from cryptography.hazmat.backends.openssl.backend import backend
return backend.x25519_load_private_bytes(data)
@@ -58,6 +58,12 @@ class X25519PrivateKey(object):
"""
@abc.abstractmethod
+ def private_bytes(self, encoding, format, encryption_algorithm):
+ """
+ The serialized bytes of the private key.
+ """
+
+ @abc.abstractmethod
def exchange(self, peer_public_key):
"""
Performs a key exchange operation using the provided peer's public key.
diff --git a/src/cryptography/hazmat/primitives/serialization/__init__.py b/src/cryptography/hazmat/primitives/serialization/__init__.py
index 3a34bf8f..f6d4ce99 100644
--- a/src/cryptography/hazmat/primitives/serialization/__init__.py
+++ b/src/cryptography/hazmat/primitives/serialization/__init__.py
@@ -14,6 +14,9 @@ from cryptography.hazmat.primitives.serialization.ssh import (
load_ssh_public_key
)
+
+_PEM_DER = (Encoding.PEM, Encoding.DER)
+
__all__ = [
"load_der_parameters", "load_der_private_key", "load_der_public_key",
"load_pem_parameters", "load_pem_private_key", "load_pem_public_key",
diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py
index 81d372fc..2bc49078 100644
--- a/tests/hazmat/primitives/test_serialization.py
+++ b/tests/hazmat/primitives/test_serialization.py
@@ -1297,3 +1297,67 @@ class TestX448Serialization(object):
assert public_key.public_bytes(
encoding, PublicFormat.SubjectPublicKeyInfo
) == data
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.x25519_supported(),
+ skip_message="Requires OpenSSL with X25519 support"
+)
+class TestX25519Serialization(object):
+ def test_load_der_private_key(self, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", "X25519", "x25519-pkcs8-enc.der"),
+ lambda derfile: derfile.read(),
+ mode="rb"
+ )
+ unencrypted = load_vectors_from_file(
+ os.path.join("asymmetric", "X25519", "x25519-pkcs8.der"),
+ lambda derfile: derfile.read(),
+ mode="rb"
+ )
+ key = load_der_private_key(data, b"password", backend)
+ assert key.private_bytes(
+ Encoding.DER, PrivateFormat.PKCS8, NoEncryption()
+ ) == unencrypted
+
+ def test_load_pem_private_key(self, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", "X25519", "x25519-pkcs8-enc.pem"),
+ lambda pemfile: pemfile.read(),
+ mode="rb"
+ )
+ unencrypted = load_vectors_from_file(
+ os.path.join("asymmetric", "X25519", "x25519-pkcs8.pem"),
+ lambda pemfile: pemfile.read(),
+ mode="rb"
+ )
+ key = load_pem_private_key(data, b"password", backend)
+ assert key.private_bytes(
+ Encoding.PEM, PrivateFormat.PKCS8, NoEncryption()
+ ) == unencrypted
+
+ @pytest.mark.parametrize(
+ ("key_path", "encoding", "loader"),
+ [
+ (
+ ["X25519", "x25519-pub.pem"],
+ Encoding.PEM,
+ load_pem_public_key
+ ),
+ (
+ ["X25519", "x25519-pub.der"],
+ Encoding.DER,
+ load_der_public_key
+ ),
+ ]
+ )
+ def test_load_public_key(self, key_path, encoding, loader, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", *key_path),
+ lambda pemfile: pemfile.read(),
+ mode="rb"
+ )
+ public_key = loader(data, backend)
+ assert public_key.public_bytes(
+ encoding, PublicFormat.SubjectPublicKeyInfo
+ ) == data
diff --git a/tests/hazmat/primitives/test_x25519.py b/tests/hazmat/primitives/test_x25519.py
index 0f83eb6e..682c3125 100644
--- a/tests/hazmat/primitives/test_x25519.py
+++ b/tests/hazmat/primitives/test_x25519.py
@@ -9,8 +9,10 @@ import os
import pytest
+from cryptography import utils
from cryptography.exceptions import _Reasons
from cryptography.hazmat.backends.interfaces import DHBackend
+from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.x25519 import (
X25519PrivateKey, X25519PublicKey
)
@@ -50,7 +52,7 @@ class TestX25519Exchange(object):
private = binascii.unhexlify(vector["input_scalar"])
public = binascii.unhexlify(vector["input_u"])
shared_key = binascii.unhexlify(vector["output_u"])
- private_key = X25519PrivateKey._from_private_bytes(private)
+ private_key = X25519PrivateKey.from_private_bytes(private)
public_key = X25519PublicKey.from_public_bytes(public)
computed_shared_key = private_key.exchange(public_key)
assert computed_shared_key == shared_key
@@ -64,11 +66,11 @@ class TestX25519Exchange(object):
b"684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d9953"
b"2c51"
)
- private_key = X25519PrivateKey._from_private_bytes(private)
+ private_key = X25519PrivateKey.from_private_bytes(private)
public_key = X25519PublicKey.from_public_bytes(public)
for _ in range(1000):
computed_shared_key = private_key.exchange(public_key)
- private_key = X25519PrivateKey._from_private_bytes(
+ private_key = X25519PrivateKey.from_private_bytes(
computed_shared_key
)
public_key = X25519PublicKey.from_public_bytes(old_private)
@@ -86,13 +88,25 @@ class TestX25519Exchange(object):
private = binascii.unhexlify(
"78f1e8edf14481b389448dac8f59c70b038e7cf92ef2c7eff57a72466e115296"
)
- private_key = X25519PrivateKey._from_private_bytes(
+ private_key = X25519PrivateKey.from_private_bytes(
private
)
public_key = X25519PublicKey.from_public_bytes(public)
with pytest.raises(ValueError):
private_key.exchange(public_key)
+ def test_deprecated_public_bytes(self, backend):
+ key = X25519PrivateKey.generate().public_key()
+ with pytest.warns(utils.DeprecatedIn25):
+ key.public_bytes()
+
+ def test_public_bytes_bad_args(self, backend):
+ key = X25519PrivateKey.generate().public_key()
+ with pytest.raises(ValueError):
+ key.public_bytes(None, serialization.PublicFormat.Raw)
+ with pytest.raises(ValueError):
+ key.public_bytes(serialization.Encoding.Raw)
+
# These vectors are also from RFC 7748
# https://tools.ietf.org/html/rfc7748#section-6.1
@pytest.mark.parametrize(
@@ -120,11 +134,20 @@ class TestX25519Exchange(object):
)
]
)
- def test_public_bytes(self, private_bytes, public_bytes, backend):
- private_key = X25519PrivateKey._from_private_bytes(private_bytes)
- assert private_key.public_key().public_bytes() == public_bytes
+ def test_pub_priv_bytes_raw(self, private_bytes, public_bytes, backend):
+ private_key = X25519PrivateKey.from_private_bytes(private_bytes)
+ assert private_key.private_bytes(
+ serialization.Encoding.Raw,
+ serialization.PrivateFormat.Raw,
+ serialization.NoEncryption()
+ ) == private_bytes
+ assert private_key.public_key().public_bytes(
+ serialization.Encoding.Raw, serialization.PublicFormat.Raw
+ ) == public_bytes
public_key = X25519PublicKey.from_public_bytes(public_bytes)
- assert public_key.public_bytes() == public_bytes
+ assert public_key.public_bytes(
+ serialization.Encoding.Raw, serialization.PublicFormat.Raw
+ ) == public_bytes
def test_generate(self, backend):
key = X25519PrivateKey.generate()
@@ -142,3 +165,86 @@ class TestX25519Exchange(object):
with pytest.raises(ValueError):
X25519PublicKey.from_public_bytes(b"a" * 33)
+
+ def test_invalid_private_bytes(self, backend):
+ key = X25519PrivateKey.generate()
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.Raw,
+ serialization.PrivateFormat.Raw,
+ None
+ )
+
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.Raw,
+ serialization.PrivateFormat.PKCS8,
+ None
+ )
+
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.Raw,
+ serialization.NoEncryption()
+ )
+
+ def test_invalid_public_bytes(self, backend):
+ key = X25519PrivateKey.generate().public_key()
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.Raw,
+ serialization.PublicFormat.SubjectPublicKeyInfo
+ )
+
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.PEM,
+ serialization.PublicFormat.PKCS1
+ )
+
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.PEM,
+ serialization.PublicFormat.Raw
+ )
+
+ @pytest.mark.parametrize(
+ ("encoding", "fmt", "encryption", "passwd", "load_func"),
+ [
+ (
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.PKCS8,
+ serialization.BestAvailableEncryption(b"password"),
+ b"password",
+ serialization.load_pem_private_key
+ ),
+ (
+ serialization.Encoding.DER,
+ serialization.PrivateFormat.PKCS8,
+ serialization.BestAvailableEncryption(b"password"),
+ b"password",
+ serialization.load_der_private_key
+ ),
+ (
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.PKCS8,
+ serialization.NoEncryption(),
+ None,
+ serialization.load_pem_private_key
+ ),
+ (
+ serialization.Encoding.DER,
+ serialization.PrivateFormat.PKCS8,
+ serialization.NoEncryption(),
+ None,
+ serialization.load_der_private_key
+ ),
+ ]
+ )
+ def test_round_trip_private_serialization(self, encoding, fmt, encryption,
+ passwd, load_func, backend):
+ key = X25519PrivateKey.generate()
+ serialized = key.private_bytes(encoding, fmt, encryption)
+ loaded_key = load_func(serialized, passwd, backend)
+ assert isinstance(loaded_key, X25519PrivateKey)
diff --git a/tests/wycheproof/test_x25519.py b/tests/wycheproof/test_x25519.py
index 5e6253ce..0727ec39 100644
--- a/tests/wycheproof/test_x25519.py
+++ b/tests/wycheproof/test_x25519.py
@@ -23,7 +23,7 @@ from cryptography.hazmat.primitives.asymmetric.x25519 import (
def test_x25519(backend, wycheproof):
assert list(wycheproof.testgroup.items()) == [("curve", "curve25519")]
- private_key = X25519PrivateKey._from_private_bytes(
+ private_key = X25519PrivateKey.from_private_bytes(
binascii.unhexlify(wycheproof.testcase["private"])
)
public_key = X25519PublicKey.from_public_bytes(