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 /src | |
parent | aeb3acbe9abffba68da3cc8b6bc0f3c2acb9bd9d (diff) | |
download | cryptography-c6c25c21496858271fbc4c89fb102074fd3d5f60.tar.gz cryptography-c6c25c21496858271fbc4c89fb102074fd3d5f60.tar.bz2 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
Diffstat (limited to 'src')
5 files changed, 111 insertions, 8 deletions
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", |