Skip to content

Commit

Permalink
Fix parsing of priv keys via pub key APIs to error properly in ossl3 (#…
Browse files Browse the repository at this point in the history
…7135)

In OpenSSL 3.0.x the PEM_read_bio_PUBKEY function will invoke
the default password callback if you pass an encrypted private
key. This is very, very, very bad as the default callback can
trigger an interactive console prompt, which will hang the
Python process. We therefore provide our own callback to
catch this and error out properly.
  • Loading branch information
reaperhulk committed Apr 27, 2022
1 parent 82d9339 commit 2d4e0b2
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 2 deletions.
21 changes: 19 additions & 2 deletions src/cryptography/hazmat/backends/openssl/backend.py
Expand Up @@ -906,8 +906,20 @@ def load_pem_private_key(

def load_pem_public_key(self, data: bytes) -> PUBLIC_KEY_TYPES:
mem_bio = self._bytes_to_bio(data)
# In OpenSSL 3.0.x the PEM_read_bio_PUBKEY function will invoke
# the default password callback if you pass an encrypted private
# key. This is very, very, very bad as the default callback can
# trigger an interactive console prompt, which will hang the
# Python process. We therefore provide our own callback to
# catch this and error out properly.
userdata = self._ffi.new("CRYPTOGRAPHY_PASSWORD_DATA *")
evp_pkey = self._lib.PEM_read_bio_PUBKEY(
mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL
mem_bio.bio,
self._ffi.NULL,
self._ffi.addressof(
self._lib._original_lib, "Cryptography_pem_password_cb"
),
userdata,
)
if evp_pkey != self._ffi.NULL:
evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
Expand All @@ -920,7 +932,12 @@ def load_pem_public_key(self, data: bytes) -> PUBLIC_KEY_TYPES:
res = self._lib.BIO_reset(mem_bio.bio)
self.openssl_assert(res == 1)
rsa_cdata = self._lib.PEM_read_bio_RSAPublicKey(
mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL
mem_bio.bio,
self._ffi.NULL,
self._ffi.addressof(
self._lib._original_lib, "Cryptography_pem_password_cb"
),
userdata,
)
if rsa_cdata != self._ffi.NULL:
rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
Expand Down
15 changes: 15 additions & 0 deletions tests/hazmat/primitives/test_serialization.py
Expand Up @@ -39,6 +39,7 @@
)


from .fixtures_rsa import RSA_KEY_2048
from .test_ec import _skip_curve_unsupported
from .utils import (
_check_dsa_private_numbers,
Expand Down Expand Up @@ -513,6 +514,20 @@ def test_load_pem_rsa_public_key(self, key_file, backend):
numbers = key.public_numbers()
assert numbers.e == 65537

def test_load_priv_key_with_public_key_api_fails(self, backend):
# In OpenSSL 3.0.x the PEM_read_bio_PUBKEY function will invoke
# the default password callback if you pass an encrypted private
# key. This is very, very, very bad as the default callback can
# trigger an interactive console prompt, which will hang the
# Python process. This test makes sure we don't do that.
priv_key_serialized = RSA_KEY_2048.private_key().private_bytes(
Encoding.PEM,
PrivateFormat.PKCS8,
BestAvailableEncryption(b"password"),
)
with pytest.raises(ValueError):
load_pem_public_key(priv_key_serialized)

@pytest.mark.supported(
only_if=lambda backend: backend.dsa_supported(),
skip_message="Does not support DSA.",
Expand Down

0 comments on commit 2d4e0b2

Please sign in to comment.