diff options
author | Paul Kehrer <paul.l.kehrer@gmail.com> | 2019-01-14 21:50:17 -0600 |
---|---|---|
committer | Alex Gaynor <alex.gaynor@gmail.com> | 2019-01-14 22:50:17 -0500 |
commit | c6c25c21496858271fbc4c89fb102074fd3d5f60 (patch) | |
tree | 009896d2b53e2d45f050b35320609bf348f0e31c | |
parent | aeb3acbe9abffba68da3cc8b6bc0f3c2acb9bd9d (diff) | |
download | platform_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.rst | 4 | ||||
-rw-r--r-- | docs/hazmat/primitives/asymmetric/x25519.rst | 88 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 6 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/x25519.py | 93 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/x448.py | 7 | ||||
-rw-r--r-- | src/cryptography/hazmat/primitives/asymmetric/x25519.py | 10 | ||||
-rw-r--r-- | src/cryptography/hazmat/primitives/serialization/__init__.py | 3 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_serialization.py | 64 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_x25519.py | 122 | ||||
-rw-r--r-- | tests/wycheproof/test_x25519.py | 2 |
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( |