aboutsummaryrefslogtreecommitdiffstats
path: root/src/cryptography
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 /src/cryptography
parent9b198104db8b53178212b5849919b6a61ca794ab (diff)
downloadcryptography-dbcbffa06c9930a687010ca816596ca3f5cc78e9.tar.gz
cryptography-dbcbffa06c9930a687010ca816596ca3f5cc78e9.tar.bz2
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
Diffstat (limited to 'src/cryptography')
-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
4 files changed, 110 insertions, 4 deletions
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):