diff options
author | Paul Kehrer <paul.l.kehrer@gmail.com> | 2019-01-12 21:18:21 -0800 |
---|---|---|
committer | Alex Gaynor <alex.gaynor@gmail.com> | 2019-01-13 00:18:21 -0500 |
commit | dbcbffa06c9930a687010ca816596ca3f5cc78e9 (patch) | |
tree | 27f88222ed222e45784f4c1e6ea0b8d6b9f9d07b /src | |
parent | 9b198104db8b53178212b5849919b6a61ca794ab (diff) | |
download | cryptography-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')
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): |