diff --git a/packages/google-auth/google/auth/transport/_custom_tls_signer.py b/packages/google-auth/google/auth/transport/_custom_tls_signer.py index 90143101ab07..6f4c5d2d959c 100644 --- a/packages/google-auth/google/auth/transport/_custom_tls_signer.py +++ b/packages/google-auth/google/auth/transport/_custom_tls_signer.py @@ -45,6 +45,18 @@ ) +# Cast SSL_CTX* to void* +def _cast_ssl_ctx_to_void_p_pyopenssl(ssl_ctx): + try: + import cffi + except ImportError as caught_exc: + raise exceptions.MutualTLSChannelError( + "cffi is required for pyOpenSSL ECP support." + ) from caught_exc + + return ctypes.cast(int(cffi.FFI().cast("intptr_t", ssl_ctx)), ctypes.c_void_p) + + # Cast SSL_CTX* to void* def _cast_ssl_ctx_to_void_p_stdlib(context): if not issubclass(type(context), ssl.SSLContext): @@ -281,7 +293,7 @@ def attach_to_ssl_context(self, ctx): if not self._offload_lib.ConfigureSslContext( self._sign_callback, ctypes.c_char_p(self._cert), - _cast_ssl_ctx_to_void_p_stdlib(ctx), + _cast_ssl_ctx_to_void_p_pyopenssl(ctx._ctx._context), ): raise exceptions.MutualTLSChannelError( "failed to configure ECP Offload SSL context" diff --git a/packages/google-auth/google/auth/transport/requests.py b/packages/google-auth/google/auth/transport/requests.py index 822cf687f5d0..0cb7342105b5 100644 --- a/packages/google-auth/google/auth/transport/requests.py +++ b/packages/google-auth/google/auth/transport/requests.py @@ -278,7 +278,7 @@ class _MutualTlsOffloadAdapter(requests.adapters.HTTPAdapter): } Raises: - ImportError: if certifi is not installed + ImportError: if certifi or pyOpenSSL is not installed google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel creation failed for any reason. """ @@ -290,6 +290,11 @@ def __init__(self, enterprise_cert_file_path): self.signer = _custom_tls_signer.CustomTlsSigner(enterprise_cert_file_path) self.signer.load_libraries() + if not self.signer.should_use_provider(): + import urllib3.contrib.pyopenssl + + urllib3.contrib.pyopenssl.inject_into_urllib3() + poolmanager = create_urllib3_context() poolmanager.load_verify_locations(cafile=certifi.where()) self.signer.attach_to_ssl_context(poolmanager) diff --git a/packages/google-auth/setup.py b/packages/google-auth/setup.py index cff8a7bdbda9..29ab4b9590b6 100644 --- a/packages/google-auth/setup.py +++ b/packages/google-auth/setup.py @@ -39,7 +39,7 @@ reauth_extra_require = ["pyu2f>=0.1.5"] -enterprise_cert_extra_require = cryptography_base_require +enterprise_cert_extra_require = ["pyopenssl>=20.0.0", "cffi>=1.0.0"] urllib3_extra_require = [ "urllib3 >= 1.26.15, < 3.0.0", @@ -65,6 +65,7 @@ *reauth_extra_require, "responses", *urllib3_extra_require, + *enterprise_cert_extra_require, # Async Dependencies *aiohttp_extra_require, "aioresponses", diff --git a/packages/google-auth/tests/transport/test__custom_tls_signer.py b/packages/google-auth/tests/transport/test__custom_tls_signer.py index fa210ee0b8d7..395dbfbb2245 100644 --- a/packages/google-auth/tests/transport/test__custom_tls_signer.py +++ b/packages/google-auth/tests/transport/test__custom_tls_signer.py @@ -168,6 +168,9 @@ def test_get_cert(): def test_custom_tls_signer(): + urllib3_pyopenssl = pytest.importorskip("urllib3.contrib.pyopenssl") + urllib3_pyopenssl.inject_into_urllib3() + offload_lib = mock.MagicMock() signer_lib = mock.MagicMock() @@ -238,7 +241,9 @@ def test_custom_tls_signer_failed_to_attach(): signer_object._sign_callback = mock.MagicMock() signer_object._cert = b"mock cert" signer_object._offload_lib.ConfigureSslContext.return_value = False - signer_object.attach_to_ssl_context(ssl.SSLContext()) + ctx = mock.Mock() + ctx._ctx._context = 123456 + signer_object.attach_to_ssl_context(ctx) assert excinfo.match("failed to configure ECP Offload SSL context") @@ -366,3 +371,12 @@ def test_cast_ssl_ctx_to_void_p_stdlib_mock_error(): TypeError, match="context must be an instance of ssl.SSLContext, not a mock" ): _custom_tls_signer._cast_ssl_ctx_to_void_p_stdlib(context) + + +def test_cast_ssl_ctx_to_void_p_pyopenssl(): + urllib3_pyopenssl = pytest.importorskip("urllib3.contrib.pyopenssl") + urllib3_pyopenssl.inject_into_urllib3() + + context = create_urllib3_context() + res = _custom_tls_signer._cast_ssl_ctx_to_void_p_pyopenssl(context._ctx._context) + assert isinstance(res, ctypes.c_void_p) diff --git a/packages/google-auth/tests/transport/test_requests.py b/packages/google-auth/tests/transport/test_requests.py index 2ca1922494ef..70eca665c16e 100644 --- a/packages/google-auth/tests/transport/test_requests.py +++ b/packages/google-auth/tests/transport/test_requests.py @@ -1061,6 +1061,7 @@ def test_success( mock_proxy_manager_for, mock_init_poolmanager, ): + pytest.importorskip("urllib3.contrib.pyopenssl") enterprise_cert_file_path = "/path/to/enterprise/cert/json" adapter = google.auth.transport.requests._MutualTlsOffloadAdapter( enterprise_cert_file_path