feat: consolidate mTLS channel errors (#480)

feat: consolidate mTLS channel errors
diff --git a/google/auth/exceptions.py b/google/auth/exceptions.py
index 4f66dc2..9501386 100644
--- a/google/auth/exceptions.py
+++ b/google/auth/exceptions.py
@@ -34,3 +34,7 @@
 
 class DefaultCredentialsError(GoogleAuthError):
     """Used to indicate that acquiring default credentials failed."""
+
+
+class MutualTLSChannelError(GoogleAuthError):
+    """Used to indicate that mutual TLS channel creation is failed."""
diff --git a/google/auth/transport/grpc.py b/google/auth/transport/grpc.py
index 32ffabc..d62c415 100644
--- a/google/auth/transport/grpc.py
+++ b/google/auth/transport/grpc.py
@@ -20,6 +20,7 @@
 
 import six
 
+from google.auth import exceptions
 from google.auth.transport import _mtls_helper
 
 try:
@@ -217,17 +218,8 @@
         grpc.Channel: The created gRPC channel.
 
     Raises:
-        OSError: If the cert provider command launch fails during the application
-            default SSL credentials loading process on devices with endpoint
-            verification support.
-        RuntimeError: If the cert provider command has a runtime error during the
-            application default SSL credentials loading process on devices with
-            endpoint verification support.
-        ValueError:
-            If the context aware metadata file is malformed or if the cert provider
-            command doesn't produce both client certificate and key during the
-            application default SSL credentials loading process on devices with
-            endpoint verification support.
+        google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel
+            creation failed for any reason.
     """
     # Create the metadata plugin for inserting the authorization header.
     metadata_plugin = AuthMetadataPlugin(credentials, request)
@@ -293,20 +285,21 @@
             grpc.ChannelCredentials: The created grpc channel credentials.
 
         Raises:
-            OSError: If the cert provider command launch fails.
-            RuntimeError: If the cert provider command has a runtime error.
-            ValueError:
-                If the context aware metadata file is malformed or if the cert provider
-                command doesn't produce both the client certificate and key.
+            google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel
+                creation failed for any reason.
         """
         if self._context_aware_metadata_path:
-            metadata = _mtls_helper._read_dca_metadata_file(
-                self._context_aware_metadata_path
-            )
-            cert, key = _mtls_helper.get_client_ssl_credentials(metadata)
-            self._ssl_credentials = grpc.ssl_channel_credentials(
-                certificate_chain=cert, private_key=key
-            )
+            try:
+                metadata = _mtls_helper._read_dca_metadata_file(
+                    self._context_aware_metadata_path
+                )
+                cert, key = _mtls_helper.get_client_ssl_credentials(metadata)
+                self._ssl_credentials = grpc.ssl_channel_credentials(
+                    certificate_chain=cert, private_key=key
+                )
+            except (OSError, RuntimeError, ValueError) as caught_exc:
+                new_exc = exceptions.MutualTLSChannelError(caught_exc)
+                six.raise_from(new_exc, caught_exc)
         else:
             self._ssl_credentials = grpc.ssl_channel_credentials()
 
diff --git a/google/auth/transport/requests.py b/google/auth/transport/requests.py
index 2d31d96..26096e2 100644
--- a/google/auth/transport/requests.py
+++ b/google/auth/transport/requests.py
@@ -355,23 +355,32 @@
                 will be used.
 
         Raises:
-            ImportError: If certifi or pyOpenSSL is not installed.
-            OpenSSL.crypto.Error: If client cert or key is invalid.
-            OSError: If the cert provider command launch fails during the
-                application default SSL credentials loading process.
-            RuntimeError: If the cert provider command has a runtime error during
-                the application default SSL credentials loading process.
-            ValueError: If the context aware metadata file is malformed or the
-                cert provider command doesn't produce both client certicate and
-                key during the application default SSL credentials loading process.
+            google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel
+                creation failed for any reason.
         """
-        self._is_mtls, cert, key = google.auth.transport._mtls_helper.get_client_cert_and_key(
-            client_cert_callback
-        )
+        try:
+            import OpenSSL
+        except ImportError as caught_exc:
+            new_exc = exceptions.MutualTLSChannelError(caught_exc)
+            six.raise_from(new_exc, caught_exc)
 
-        if self._is_mtls:
-            mtls_adapter = _MutualTlsAdapter(cert, key)
-            self.mount("https://", mtls_adapter)
+        try:
+            self._is_mtls, cert, key = google.auth.transport._mtls_helper.get_client_cert_and_key(
+                client_cert_callback
+            )
+
+            if self._is_mtls:
+                mtls_adapter = _MutualTlsAdapter(cert, key)
+                self.mount("https://", mtls_adapter)
+        except (
+            ImportError,
+            OpenSSL.crypto.Error,
+            OSError,
+            RuntimeError,
+            ValueError,
+        ) as caught_exc:
+            new_exc = exceptions.MutualTLSChannelError(caught_exc)
+            six.raise_from(new_exc, caught_exc)
 
     def request(
         self,
diff --git a/google/auth/transport/urllib3.py b/google/auth/transport/urllib3.py
index 3b2ba28..c359f35 100644
--- a/google/auth/transport/urllib3.py
+++ b/google/auth/transport/urllib3.py
@@ -297,24 +297,33 @@
             True if the channel is mutual TLS and False otherwise.
 
         Raises:
-            ImportError: If certifi or pyOpenSSL is not installed.
-            OpenSSL.crypto.Error: If client cert or key is invalid.
-            OSError: If the cert provider command launch fails during the
-                application default SSL credentials loading process.
-            RuntimeError: If the cert provider command has a runtime error during
-                the application default SSL credentials loading process.
-            ValueError: If the context aware metadata file is malformed or the
-                cert provider command doesn't produce both client certicate and
-                key during the application default SSL credentials loading process.
+            google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel
+                creation failed for any reason.
         """
-        found_cert_key, cert, key = transport._mtls_helper.get_client_cert_and_key(
-            client_cert_callabck
-        )
+        try:
+            import OpenSSL
+        except ImportError as caught_exc:
+            new_exc = exceptions.MutualTLSChannelError(caught_exc)
+            six.raise_from(new_exc, caught_exc)
 
-        if found_cert_key:
-            self.http = _make_mutual_tls_http(cert, key)
-        else:
-            self.http = _make_default_http()
+        try:
+            found_cert_key, cert, key = transport._mtls_helper.get_client_cert_and_key(
+                client_cert_callabck
+            )
+
+            if found_cert_key:
+                self.http = _make_mutual_tls_http(cert, key)
+            else:
+                self.http = _make_default_http()
+        except (
+            ImportError,
+            OpenSSL.crypto.Error,
+            OSError,
+            RuntimeError,
+            ValueError,
+        ) as caught_exc:
+            new_exc = exceptions.MutualTLSChannelError(caught_exc)
+            six.raise_from(new_exc, caught_exc)
 
         if self._has_user_provided_http:
             self._has_user_provided_http = False
diff --git a/tests/transport/test_grpc.py b/tests/transport/test_grpc.py
index 23e62a2..5c61f96 100644
--- a/tests/transport/test_grpc.py
+++ b/tests/transport/test_grpc.py
@@ -21,6 +21,7 @@
 
 from google.auth import _helpers
 from google.auth import credentials
+from google.auth import exceptions
 from google.auth import transport
 
 try:
@@ -315,7 +316,7 @@
         # Mock that client cert and key are not loaded and exception is raised.
         mock_get_client_ssl_credentials.side_effect = ValueError()
 
-        with pytest.raises(ValueError):
+        with pytest.raises(exceptions.MutualTLSChannelError):
             assert google.auth.transport.grpc.SslCredentials().ssl_credentials
 
     def test_get_client_ssl_credentials_success(
diff --git a/tests/transport/test_requests.py b/tests/transport/test_requests.py
index 3f3e14c..d6770de 100644
--- a/tests/transport/test_requests.py
+++ b/tests/transport/test_requests.py
@@ -14,6 +14,7 @@
 
 import datetime
 import functools
+import sys
 
 import freezegun
 import mock
@@ -23,6 +24,7 @@
 import requests.adapters
 from six.moves import http_client
 
+from google.auth import exceptions
 import google.auth.credentials
 import google.auth.transport._mtls_helper
 import google.auth.transport.requests
@@ -414,3 +416,21 @@
 
         # Assert _MutualTlsAdapter constructor is not called.
         mock_adapter_ctor.assert_not_called()
+
+    @mock.patch(
+        "google.auth.transport._mtls_helper.get_client_cert_and_key", autospec=True
+    )
+    def test_configure_mtls_channel_exceptions(self, mock_get_client_cert_and_key):
+        mock_get_client_cert_and_key.side_effect = ValueError()
+
+        auth_session = google.auth.transport.requests.AuthorizedSession(
+            credentials=mock.Mock()
+        )
+        with pytest.raises(exceptions.MutualTLSChannelError):
+            auth_session.configure_mtls_channel()
+
+        mock_get_client_cert_and_key.return_value = (False, None, None)
+        with mock.patch.dict("sys.modules"):
+            sys.modules["OpenSSL"] = None
+            with pytest.raises(exceptions.MutualTLSChannelError):
+                auth_session.configure_mtls_channel()
diff --git a/tests/transport/test_urllib3.py b/tests/transport/test_urllib3.py
index 0452e91..a25fcd7 100644
--- a/tests/transport/test_urllib3.py
+++ b/tests/transport/test_urllib3.py
@@ -12,12 +12,15 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import sys
+
 import mock
 import OpenSSL
 import pytest
 from six.moves import http_client
 import urllib3
 
+from google.auth import exceptions
 import google.auth.credentials
 import google.auth.transport._mtls_helper
 import google.auth.transport.urllib3
@@ -221,3 +224,21 @@
         assert not is_mtls
         mock_get_client_cert_and_key.assert_called_once()
         mock_make_mutual_tls_http.assert_not_called()
+
+    @mock.patch(
+        "google.auth.transport._mtls_helper.get_client_cert_and_key", autospec=True
+    )
+    def test_configure_mtls_channel_exceptions(self, mock_get_client_cert_and_key):
+        authed_http = google.auth.transport.urllib3.AuthorizedHttp(
+            credentials=mock.Mock()
+        )
+
+        mock_get_client_cert_and_key.side_effect = ValueError()
+        with pytest.raises(exceptions.MutualTLSChannelError):
+            authed_http.configure_mtls_channel()
+
+        mock_get_client_cert_and_key.return_value = (False, None, None)
+        with mock.patch.dict("sys.modules"):
+            sys.modules["OpenSSL"] = None
+            with pytest.raises(exceptions.MutualTLSChannelError):
+                authed_http.configure_mtls_channel()