aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Kehrer <paul.l.kehrer@gmail.com>2019-01-12 21:18:21 -0800
committerAlex Gaynor <alex.gaynor@gmail.com>2019-01-13 00:18:21 -0500
commitdbcbffa06c9930a687010ca816596ca3f5cc78e9 (patch)
tree27f88222ed222e45784f4c1e6ea0b8d6b9f9d07b
parent9b198104db8b53178212b5849919b6a61ca794ab (diff)
downloadplatform_external_python_cryptography-dbcbffa06c9930a687010ca816596ca3f5cc78e9.tar.gz
platform_external_python_cryptography-dbcbffa06c9930a687010ca816596ca3f5cc78e9.tar.bz2
platform_external_python_cryptography-dbcbffa06c9930a687010ca816596ca3f5cc78e9.zip
support x448 public/private serialization both raw and pkcs8 (#4653)
* support x448 public/private serialization both raw and pkcs8 * add tests for all other asym key types to prevent Raw * more tests * better tests * fix a test * funny story, I'm actually illiterate. * pep8 * require PrivateFormat.Raw or PublicFormat.Raw with Encoding.Raw * missing docs * parametrize * docs fixes * remove dupe line * assert something
-rw-r--r--docs/hazmat/primitives/asymmetric/serialization.rst33
-rw-r--r--docs/hazmat/primitives/asymmetric/x448.rst82
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py28
-rw-r--r--src/cryptography/hazmat/backends/openssl/x448.py73
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/x448.py10
-rw-r--r--src/cryptography/hazmat/primitives/serialization/base.py3
-rw-r--r--tests/hazmat/primitives/test_dh.py31
-rw-r--r--tests/hazmat/primitives/test_dsa.py29
-rw-r--r--tests/hazmat/primitives/test_ec.py28
-rw-r--r--tests/hazmat/primitives/test_rsa.py26
-rw-r--r--tests/hazmat/primitives/test_serialization.py68
-rw-r--r--tests/hazmat/primitives/test_x448.py107
12 files changed, 497 insertions, 21 deletions
diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst
index 7b3fb1d6..04bc705a 100644
--- a/docs/hazmat/primitives/asymmetric/serialization.rst
+++ b/docs/hazmat/primitives/asymmetric/serialization.rst
@@ -473,6 +473,13 @@ Serialization Formats
...
-----END PRIVATE KEY-----
+ .. attribute:: Raw
+
+ .. versionadded:: 2.5
+
+ A raw format used by :doc:`/hazmat/primitives/asymmetric/x448`. It is a
+ binary format and is invalid for other key types.
+
.. class:: PublicFormat
.. versionadded:: 0.8
@@ -516,6 +523,13 @@ Serialization Formats
The public key format used by OpenSSH (e.g. as found in
``~/.ssh/id_rsa.pub`` or ``~/.ssh/authorized_keys``).
+ .. attribute:: Raw
+
+ .. versionadded:: 2.5
+
+ A raw format used by :doc:`/hazmat/primitives/asymmetric/x448`. It is a
+ binary format and is invalid for other key types.
+
.. class:: ParameterFormat
.. versionadded:: 2.0
@@ -538,14 +552,16 @@ Serialization Encodings
:class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization`
,
:class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKeyWithSerialization`
- , :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKeyWithSerialization`
+ , :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKeyWithSerialization`,
+ :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKeyWithSerialization`,
and
- :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKeyWithSerialization`
+ :class:`~cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey`
as well as ``public_bytes`` on
- :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKeyWithSerialization`,
- :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicKeyWithSerialization`
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`,
+ :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicKey`,
+ :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey`,
and
- :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKeyWithSerialization`.
+ :class:`~cryptography.hazmat.primitives.asymmetric.x448.X448PublicKey`.
.. attribute:: PEM
@@ -565,6 +581,13 @@ Serialization Encodings
The format used by OpenSSH public keys. This is a text format.
+ .. attribute:: Raw
+
+ .. versionadded:: 2.5
+
+ A raw format used by :doc:`/hazmat/primitives/asymmetric/x448`. It is a
+ binary format and is invalid for other key types.
+
Serialization Encryption Types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/hazmat/primitives/asymmetric/x448.rst b/docs/hazmat/primitives/asymmetric/x448.rst
index 057b7b50..9b00c6af 100644
--- a/docs/hazmat/primitives/asymmetric/x448.rst
+++ b/docs/hazmat/primitives/asymmetric/x448.rst
@@ -66,6 +66,24 @@ Key interfaces
:returns: :class:`X448PrivateKey`
+ .. classmethod:: from_private_bytes(data)
+
+ :param bytes data: 56 byte private key.
+
+ :returns: :class:`X448PrivateKey`
+
+ .. doctest::
+
+ >>> from cryptography.hazmat.primitives import serialization
+ >>> from cryptography.hazmat.primitives.asymmetric import x448
+ >>> private_key = x448.X448PrivateKey.generate()
+ >>> private_bytes = private_key.private_bytes(
+ ... encoding=serialization.Encoding.Raw,
+ ... format=serialization.PrivateFormat.Raw,
+ ... encryption_algorithm=serialization.NoEncryption()
+ ... )
+ >>> loaded_private_key = x448.X448PrivateKey.from_private_bytes(private_bytes)
+
.. method:: public_key()
:returns: :class:`X448PublicKey`
@@ -77,6 +95,36 @@ Key interfaces
:returns bytes: A shared key.
+ .. method:: private_bytes(encoding, format, encryption_algorithm)
+
+ 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:: X448PublicKey
.. versionadded:: 2.5
@@ -89,15 +137,41 @@ Key interfaces
.. doctest::
+ >>> from cryptography.hazmat.primitives import serialization
>>> from cryptography.hazmat.primitives.asymmetric import x448
>>> private_key = x448.X448PrivateKey.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 = x448.X448PublicKey.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 cfe146f2..ecebe7b8 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -508,6 +508,9 @@ class Backend(object):
self.openssl_assert(dh_cdata != self._ffi.NULL)
dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
return _DHPrivateKey(self, dh_cdata, evp_pkey)
+ 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)
else:
raise UnsupportedAlgorithm("Unsupported key type.")
@@ -539,6 +542,9 @@ class Backend(object):
self.openssl_assert(dh_cdata != self._ffi.NULL)
dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
return _DHPublicKey(self, dh_cdata, evp_pkey)
+ 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)
else:
raise UnsupportedAlgorithm("Unsupported key type.")
@@ -1678,6 +1684,16 @@ class Backend(object):
"format must be an item from the PrivateFormat enum"
)
+ # Raw format and encoding are only valid for X25519, Ed25519, X448, and
+ # Ed448 keys. We capture those cases before this method is called so if
+ # we see those enum values here it means the caller has passed them to
+ # a key that doesn't support raw type
+ if format is serialization.PrivateFormat.Raw:
+ raise ValueError("raw format is invalid with this key or encoding")
+
+ if encoding is serialization.Encoding.Raw:
+ raise ValueError("raw encoding is invalid with this key or format")
+
if not isinstance(encryption_algorithm,
serialization.KeySerializationEncryption):
raise TypeError(
@@ -1737,7 +1753,7 @@ class Backend(object):
write_bio = self._lib.i2d_PKCS8PrivateKey_bio
key = evp_pkey
else:
- raise TypeError("encoding must be an item from the Encoding enum")
+ raise TypeError("encoding must be Encoding.PEM or Encoding.DER")
bio = self._create_mem_bio_gc()
res = write_bio(
@@ -1770,6 +1786,16 @@ class Backend(object):
if not isinstance(encoding, serialization.Encoding):
raise TypeError("encoding must be an item from the Encoding enum")
+ # Raw format and encoding are only valid for X25519, Ed25519, X448, and
+ # Ed448 keys. We capture those cases before this method is called so if
+ # we see those enum values here it means the caller has passed them to
+ # a key that doesn't support raw type
+ if format is serialization.PublicFormat.Raw:
+ raise ValueError("raw format is invalid with this key or encoding")
+
+ if encoding is serialization.Encoding.Raw:
+ raise ValueError("raw encoding is invalid with this key or format")
+
if (
format is serialization.PublicFormat.OpenSSH or
encoding is serialization.Encoding.OpenSSH
diff --git a/src/cryptography/hazmat/backends/openssl/x448.py b/src/cryptography/hazmat/backends/openssl/x448.py
index a10aa821..3792fd79 100644
--- a/src/cryptography/hazmat/backends/openssl/x448.py
+++ b/src/cryptography/hazmat/backends/openssl/x448.py
@@ -6,11 +6,15 @@ from __future__ import absolute_import, division, print_function
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.x448 import (
X448PrivateKey, X448PublicKey
)
_X448_KEY_SIZE = 56
+_PEM_DER = (
+ serialization.Encoding.PEM, serialization.Encoding.DER
+)
@utils.register_interface(X448PublicKey)
@@ -19,7 +23,35 @@ class _X448PublicKey(object):
self._backend = backend
self._evp_pkey = evp_pkey
- def public_bytes(self):
+ def public_bytes(self, encoding, format):
+ 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 _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):
buf = self._backend._ffi.new("unsigned char []", _X448_KEY_SIZE)
buflen = self._backend._ffi.new("size_t *", _X448_KEY_SIZE)
res = self._backend._lib.EVP_PKEY_get_raw_public_key(
@@ -53,3 +85,42 @@ class _X448PrivateKey(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 _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):
+ buf = self._backend._ffi.new("unsigned char []", _X448_KEY_SIZE)
+ buflen = self._backend._ffi.new("size_t *", _X448_KEY_SIZE)
+ res = self._backend._lib.EVP_PKEY_get_raw_private_key(
+ self._evp_pkey, buf, buflen
+ )
+ self._backend.openssl_assert(res == 1)
+ self._backend.openssl_assert(buflen[0] == _X448_KEY_SIZE)
+ return self._backend._ffi.buffer(buf, _X448_KEY_SIZE)[:]
diff --git a/src/cryptography/hazmat/primitives/asymmetric/x448.py b/src/cryptography/hazmat/primitives/asymmetric/x448.py
index 69bfa408..992ec0fd 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/x448.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/x448.py
@@ -25,7 +25,7 @@ class X448PublicKey(object):
return backend.x448_load_public_bytes(data)
@abc.abstractmethod
- def public_bytes(self):
+ def public_bytes(self, encoding, format):
"""
The serialized bytes of the public key.
"""
@@ -44,7 +44,7 @@ class X448PrivateKey(object):
return backend.x448_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.x448_load_private_bytes(data)
@@ -55,6 +55,12 @@ class X448PrivateKey(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/base.py b/src/cryptography/hazmat/primitives/serialization/base.py
index 5dd0c639..1670fd18 100644
--- a/src/cryptography/hazmat/primitives/serialization/base.py
+++ b/src/cryptography/hazmat/primitives/serialization/base.py
@@ -40,17 +40,20 @@ class Encoding(Enum):
PEM = "PEM"
DER = "DER"
OpenSSH = "OpenSSH"
+ Raw = "Raw"
class PrivateFormat(Enum):
PKCS8 = "PKCS8"
TraditionalOpenSSL = "TraditionalOpenSSL"
+ Raw = "Raw"
class PublicFormat(Enum):
SubjectPublicKeyInfo = "X.509 subjectPublicKeyInfo with PKCS#1"
PKCS1 = "Raw PKCS#1"
OpenSSH = "OpenSSH"
+ Raw = "Raw"
class ParameterFormat(Enum):
diff --git a/tests/hazmat/primitives/test_dh.py b/tests/hazmat/primitives/test_dh.py
index a70ae745..c63e520f 100644
--- a/tests/hazmat/primitives/test_dh.py
+++ b/tests/hazmat/primitives/test_dh.py
@@ -425,6 +425,20 @@ class TestDHPrivateKeySerialization(object):
assert loaded_priv_num == priv_num
@pytest.mark.parametrize(
+ ("encoding", "fmt"),
+ [
+ (serialization.Encoding.Raw, serialization.PrivateFormat.PKCS8),
+ (serialization.Encoding.DER, serialization.PrivateFormat.Raw),
+ (serialization.Encoding.Raw, serialization.PrivateFormat.Raw),
+ ]
+ )
+ def test_private_bytes_rejects_raw(self, encoding, fmt, backend):
+ parameters = dh.generate_parameters(2, 512, backend)
+ key = parameters.generate_private_key()
+ with pytest.raises(ValueError):
+ key.private_bytes(encoding, fmt, serialization.NoEncryption())
+
+ @pytest.mark.parametrize(
("key_path", "loader_func", "encoding", "is_dhx"),
[
(
@@ -806,6 +820,23 @@ class TestDHParameterSerialization(object):
else:
assert parameter_numbers.q is None
+ @pytest.mark.parametrize(
+ ("encoding", "fmt"),
+ [
+ (serialization.Encoding.Raw, serialization.PublicFormat.Raw),
+ (serialization.Encoding.PEM, serialization.PublicFormat.Raw),
+ (
+ serialization.Encoding.Raw,
+ serialization.PublicFormat.SubjectPublicKeyInfo
+ ),
+ ]
+ )
+ def test_public_bytes_rejects_raw(self, encoding, fmt, backend):
+ parameters = dh.generate_parameters(2, 512, backend)
+ key = parameters.generate_private_key().public_key()
+ with pytest.raises(ValueError):
+ key.public_bytes(encoding, fmt)
+
def test_parameter_bytes_invalid_encoding(self, backend):
parameters = dh.generate_parameters(2, 512, backend)
with pytest.raises(TypeError):
diff --git a/tests/hazmat/primitives/test_dsa.py b/tests/hazmat/primitives/test_dsa.py
index fb415732..5d2f1bd8 100644
--- a/tests/hazmat/primitives/test_dsa.py
+++ b/tests/hazmat/primitives/test_dsa.py
@@ -714,6 +714,19 @@ class TestDSASerialization(object):
assert loaded_priv_num == priv_num
@pytest.mark.parametrize(
+ ("encoding", "fmt"),
+ [
+ (serialization.Encoding.Raw, serialization.PrivateFormat.PKCS8),
+ (serialization.Encoding.DER, serialization.PrivateFormat.Raw),
+ (serialization.Encoding.Raw, serialization.PrivateFormat.Raw),
+ ]
+ )
+ def test_private_bytes_rejects_raw(self, encoding, fmt, backend):
+ key = DSA_KEY_1024.private_key(backend)
+ with pytest.raises(ValueError):
+ key.private_bytes(encoding, fmt, serialization.NoEncryption())
+
+ @pytest.mark.parametrize(
("fmt", "password"),
[
[serialization.PrivateFormat.PKCS8, b"s"],
@@ -951,3 +964,19 @@ class TestDSAPEMPublicKeySerialization(object):
key.public_bytes(
serialization.Encoding.PEM, serialization.PublicFormat.PKCS1
)
+
+ @pytest.mark.parametrize(
+ ("encoding", "fmt"),
+ [
+ (serialization.Encoding.Raw, serialization.PublicFormat.Raw),
+ (serialization.Encoding.PEM, serialization.PublicFormat.Raw),
+ (
+ serialization.Encoding.Raw,
+ serialization.PublicFormat.SubjectPublicKeyInfo
+ ),
+ ]
+ )
+ def test_public_bytes_rejects_raw(self, encoding, fmt, backend):
+ key = DSA_KEY_2048.private_key(backend).public_key()
+ with pytest.raises(ValueError):
+ key.public_bytes(encoding, fmt)
diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py
index f883d065..830d89a0 100644
--- a/tests/hazmat/primitives/test_ec.py
+++ b/tests/hazmat/primitives/test_ec.py
@@ -706,6 +706,20 @@ class TestECSerialization(object):
assert loaded_priv_num == priv_num
@pytest.mark.parametrize(
+ ("encoding", "fmt"),
+ [
+ (serialization.Encoding.Raw, serialization.PrivateFormat.PKCS8),
+ (serialization.Encoding.DER, serialization.PrivateFormat.Raw),
+ (serialization.Encoding.Raw, serialization.PrivateFormat.Raw),
+ ]
+ )
+ def test_private_bytes_rejects_raw(self, encoding, fmt, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ key = ec.generate_private_key(ec.SECP256R1(), backend)
+ with pytest.raises(ValueError):
+ key.private_bytes(encoding, fmt, serialization.NoEncryption())
+
+ @pytest.mark.parametrize(
("fmt", "password"),
[
[serialization.PrivateFormat.PKCS8, b"s"],
@@ -985,6 +999,20 @@ class TestEllipticCurvePEMPublicKeySerialization(object):
serialization.PublicFormat.SubjectPublicKeyInfo
)
+ @pytest.mark.parametrize(
+ ("encoding", "fmt"),
+ [
+ (serialization.Encoding.Raw, serialization.PublicFormat.Raw),
+ (serialization.Encoding.PEM, serialization.PublicFormat.Raw),
+ (serialization.Encoding.Raw, serialization.PublicFormat.PKCS1),
+ ]
+ )
+ def test_public_bytes_rejects_raw(self, encoding, fmt, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ key = ec.generate_private_key(ec.SECP256R1(), backend).public_key()
+ with pytest.raises(ValueError):
+ key.public_bytes(encoding, fmt)
+
def test_public_bytes_invalid_format(self, backend):
_skip_curve_unsupported(backend, ec.SECP256R1())
key = load_vectors_from_file(
diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py
index 268ee9d9..0c25bdbb 100644
--- a/tests/hazmat/primitives/test_rsa.py
+++ b/tests/hazmat/primitives/test_rsa.py
@@ -2062,6 +2062,19 @@ class TestRSAPrivateKeySerialization(object):
assert loaded_priv_num == priv_num
@pytest.mark.parametrize(
+ ("encoding", "fmt"),
+ [
+ (serialization.Encoding.Raw, serialization.PrivateFormat.PKCS8),
+ (serialization.Encoding.DER, serialization.PrivateFormat.Raw),
+ (serialization.Encoding.Raw, serialization.PrivateFormat.Raw),
+ ]
+ )
+ def test_private_bytes_rejects_raw(self, encoding, fmt, backend):
+ key = RSA_KEY_2048.private_key(backend)
+ with pytest.raises(ValueError):
+ key.private_bytes(encoding, fmt, serialization.NoEncryption())
+
+ @pytest.mark.parametrize(
("fmt", "password"),
[
[serialization.PrivateFormat.PKCS8, b"s"],
@@ -2286,3 +2299,16 @@ class TestRSAPEMPublicKeySerialization(object):
key = RSA_KEY_2048.private_key(backend).public_key()
with pytest.raises(TypeError):
key.public_bytes(serialization.Encoding.PEM, "invalidformat")
+
+ @pytest.mark.parametrize(
+ ("encoding", "fmt"),
+ [
+ (serialization.Encoding.Raw, serialization.PublicFormat.Raw),
+ (serialization.Encoding.PEM, serialization.PublicFormat.Raw),
+ (serialization.Encoding.Raw, serialization.PublicFormat.PKCS1),
+ ]
+ )
+ def test_public_bytes_rejects_raw(self, encoding, fmt, backend):
+ key = RSA_KEY_2048.private_key(backend).public_key()
+ with pytest.raises(ValueError):
+ key.public_bytes(encoding, fmt)
diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py
index a7355221..81d372fc 100644
--- a/tests/hazmat/primitives/test_serialization.py
+++ b/tests/hazmat/primitives/test_serialization.py
@@ -18,7 +18,9 @@ from cryptography.hazmat.backends.interfaces import (
)
from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
from cryptography.hazmat.primitives.serialization import (
- BestAvailableEncryption, load_der_parameters, load_der_private_key,
+ BestAvailableEncryption, Encoding, NoEncryption,
+ PrivateFormat, PublicFormat,
+ load_der_parameters, load_der_private_key,
load_der_public_key, load_pem_parameters, load_pem_private_key,
load_pem_public_key, load_ssh_public_key
)
@@ -1231,3 +1233,67 @@ class TestKeySerializationEncryptionTypes(object):
def test_encryption_with_zero_length_password(self):
with pytest.raises(ValueError):
BestAvailableEncryption(b"")
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.x448_supported(),
+ skip_message="Requires OpenSSL with X448 support"
+)
+class TestX448Serialization(object):
+ def test_load_der_private_key(self, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", "X448", "x448-pkcs8-enc.der"),
+ lambda derfile: derfile.read(),
+ mode="rb"
+ )
+ unencrypted = load_vectors_from_file(
+ os.path.join("asymmetric", "X448", "x448-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", "X448", "x448-pkcs8-enc.pem"),
+ lambda pemfile: pemfile.read(),
+ mode="rb"
+ )
+ unencrypted = load_vectors_from_file(
+ os.path.join("asymmetric", "X448", "x448-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"),
+ [
+ (
+ ["X448", "x448-pub.pem"],
+ Encoding.PEM,
+ load_pem_public_key
+ ),
+ (
+ ["X448", "x448-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_x448.py b/tests/hazmat/primitives/test_x448.py
index 71b25341..1833b03d 100644
--- a/tests/hazmat/primitives/test_x448.py
+++ b/tests/hazmat/primitives/test_x448.py
@@ -11,6 +11,7 @@ import pytest
from cryptography.exceptions import _Reasons
from cryptography.hazmat.backends.interfaces import DHBackend
+from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.x448 import (
X448PrivateKey, X448PublicKey
)
@@ -50,7 +51,7 @@ class TestX448Exchange(object):
private = binascii.unhexlify(vector["input_scalar"])
public = binascii.unhexlify(vector["input_u"])
shared_key = binascii.unhexlify(vector["output_u"])
- private_key = X448PrivateKey._from_private_bytes(private)
+ private_key = X448PrivateKey.from_private_bytes(private)
public_key = X448PublicKey.from_public_bytes(public)
computed_shared_key = private_key.exchange(public_key)
assert computed_shared_key == shared_key
@@ -64,11 +65,11 @@ class TestX448Exchange(object):
b"aa3b4749d55b9daf1e5b00288826c467274ce3ebbdd5c17b975e09d4"
b"af6c67cf10d087202db88286e2b79fceea3ec353ef54faa26e219f38"
)
- private_key = X448PrivateKey._from_private_bytes(private)
+ private_key = X448PrivateKey.from_private_bytes(private)
public_key = X448PublicKey.from_public_bytes(public)
for _ in range(1000):
computed_shared_key = private_key.exchange(public_key)
- private_key = X448PrivateKey._from_private_bytes(
+ private_key = X448PrivateKey.from_private_bytes(
computed_shared_key
)
public_key = X448PublicKey.from_public_bytes(old_private)
@@ -103,11 +104,60 @@ class TestX448Exchange(object):
)
]
)
- def test_public_bytes(self, private_bytes, public_bytes, backend):
- private_key = X448PrivateKey._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 = X448PrivateKey.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 = X448PublicKey.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
+
+ @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 = X448PrivateKey.generate()
+ serialized = key.private_bytes(encoding, fmt, encryption)
+ loaded_key = load_func(serialized, passwd, backend)
+ assert isinstance(loaded_key, X448PrivateKey)
def test_generate(self, backend):
key = X448PrivateKey.generate()
@@ -125,3 +175,46 @@ class TestX448Exchange(object):
with pytest.raises(ValueError):
X448PublicKey.from_public_bytes(b"a" * 57)
+
+ def test_invalid_private_bytes(self, backend):
+ key = X448PrivateKey.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 = X448PrivateKey.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
+ )