diff --git a/src/_cffi_src/openssl/cryptography.py b/src/_cffi_src/openssl/cryptography.py index e12e36549528..84c0cf360727 100644 --- a/src/_cffi_src/openssl/cryptography.py +++ b/src/_cffi_src/openssl/cryptography.py @@ -86,6 +86,8 @@ static const int CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE; static const int CRYPTOGRAPHY_HAS_WORKING_ED25519; +static const int CRYPTOGRAPHY_LIBRESSL_LESS_THAN_370; + static const int CRYPTOGRAPHY_IS_LIBRESSL; static const int CRYPTOGRAPHY_IS_BORINGSSL; """ diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 6f6fb9021304..49e94b122fef 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -1838,54 +1838,29 @@ def dh_x942_serialization_supported(self) -> bool: return self._lib.Cryptography_HAS_EVP_PKEY_DHX == 1 def x25519_load_public_bytes(self, data: bytes) -> x25519.X25519PublicKey: - # If/when LibreSSL adds support for EVP_PKEY_new_raw_public_key we - # can switch to it (Cryptography_HAS_RAW_KEY) if len(data) != 32: raise ValueError("An X25519 public key is 32 bytes long") - evp_pkey = self._create_evp_pkey_gc() - res = self._lib.EVP_PKEY_set_type(evp_pkey, self._lib.NID_X25519) - self.openssl_assert(res == 1) - res = self._lib.EVP_PKEY_set1_tls_encodedpoint( - evp_pkey, data, len(data) + data_ptr = self._ffi.from_buffer(data) + evp_pkey = self._lib.EVP_PKEY_new_raw_public_key( + self._lib.NID_X25519, self._ffi.NULL, data_ptr, len(data) ) - self.openssl_assert(res == 1) + self.openssl_assert(evp_pkey != self._ffi.NULL) + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) return _X25519PublicKey(self, evp_pkey) def x25519_load_private_bytes( self, data: bytes ) -> x25519.X25519PrivateKey: - # If/when LibreSSL adds support for EVP_PKEY_new_raw_private_key we - # can switch to it (Cryptography_HAS_RAW_KEY) drop the - # zeroed_bytearray garbage. - # OpenSSL only has facilities for loading PKCS8 formatted private - # keys using the algorithm identifiers specified in - # https://tools.ietf.org/html/draft-ietf-curdle-pkix-09. - # This is the standard PKCS8 prefix for a 32 byte X25519 key. - # The form is: - # 0:d=0 hl=2 l= 46 cons: SEQUENCE - # 2:d=1 hl=2 l= 1 prim: INTEGER :00 - # 5:d=1 hl=2 l= 5 cons: SEQUENCE - # 7:d=2 hl=2 l= 3 prim: OBJECT :1.3.101.110 - # 12:d=1 hl=2 l= 34 prim: OCTET STRING (the key) - # Of course there's a bit more complexity. In reality OCTET STRING - # contains an OCTET STRING of length 32! So the last two bytes here - # are \x04\x20, which is an OCTET STRING of length 32. if len(data) != 32: raise ValueError("An X25519 private key is 32 bytes long") - pkcs8_prefix = b'0.\x02\x01\x000\x05\x06\x03+en\x04"\x04 ' - with self._zeroed_bytearray(48) as ba: - ba[0:16] = pkcs8_prefix - ba[16:] = data - bio = self._bytes_to_bio(ba) - evp_pkey = self._lib.d2i_PrivateKey_bio(bio.bio, self._ffi.NULL) - + data_ptr = self._ffi.from_buffer(data) + evp_pkey = self._lib.EVP_PKEY_new_raw_private_key( + self._lib.NID_X25519, self._ffi.NULL, data_ptr, len(data) + ) self.openssl_assert(evp_pkey != self._ffi.NULL) evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) - self.openssl_assert( - self._lib.EVP_PKEY_id(evp_pkey) == self._lib.EVP_PKEY_X25519 - ) return _X25519PrivateKey(self, evp_pkey) def _evp_pkey_keygen_gc(self, nid): @@ -1908,7 +1883,7 @@ def x25519_generate_key(self) -> x25519.X25519PrivateKey: def x25519_supported(self) -> bool: if self._fips_enabled: return False - return not self._lib.CRYPTOGRAPHY_IS_LIBRESSL + return not self._lib.CRYPTOGRAPHY_LIBRESSL_LESS_THAN_370 def x448_load_public_bytes(self, data: bytes) -> x448.X448PublicKey: if len(data) != 56: @@ -2074,19 +2049,6 @@ def aead_cipher_supported(self, cipher) -> bool: self._lib.EVP_get_cipherbyname(cipher_name) != self._ffi.NULL ) - @contextlib.contextmanager - def _zeroed_bytearray(self, length: int) -> typing.Iterator[bytearray]: - """ - This method creates a bytearray, which we copy data into (hopefully - also from a mutable buffer that can be dynamically erased!), and then - zero when we're done. - """ - ba = bytearray(length) - try: - yield ba - finally: - self._zero_data(ba, length) - def _zero_data(self, data, length: int) -> None: # We clear things this way because at the moment we're not # sure of a better way that can guarantee it overwrites the diff --git a/src/cryptography/hazmat/backends/openssl/x25519.py b/src/cryptography/hazmat/backends/openssl/x25519.py index e3b41eced1a5..b7f9406c7b2a 100644 --- a/src/cryptography/hazmat/backends/openssl/x25519.py +++ b/src/cryptography/hazmat/backends/openssl/x25519.py @@ -47,16 +47,14 @@ def public_bytes( ) def _raw_public_bytes(self) -> bytes: - ucharpp = self._backend._ffi.new("unsigned char **") - res = self._backend._lib.EVP_PKEY_get1_tls_encodedpoint( - self._evp_pkey, ucharpp + buf = self._backend._ffi.new("unsigned char []", _X25519_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _X25519_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_public_key( + self._evp_pkey, buf, buflen ) - self._backend.openssl_assert(res == 32) - self._backend.openssl_assert(ucharpp[0] != self._backend._ffi.NULL) - data = self._backend._ffi.gc( - ucharpp[0], self._backend._lib.OPENSSL_free - ) - return self._backend._ffi.buffer(data, res)[:] + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _X25519_KEY_SIZE) + return self._backend._ffi.buffer(buf, _X25519_KEY_SIZE)[:] class _X25519PrivateKey(X25519PrivateKey): @@ -112,21 +110,11 @@ def private_bytes( ) def _raw_private_bytes(self) -> bytes: - # If/when LibreSSL adds support for EVP_PKEY_get_raw_private_key we - # can switch to it (Cryptography_HAS_RAW_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, + buf = self._backend._ffi.new("unsigned char []", _X25519_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _X25519_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_private_key( + self._evp_pkey, buf, buflen ) 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:] + self._backend.openssl_assert(buflen[0] == _X25519_KEY_SIZE) + return self._backend._ffi.buffer(buf, _X25519_KEY_SIZE)[:]