1# Copyright 2020 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Utilites for mutual TLS."""
16
17from os import getenv
18
19from google.auth import exceptions
20from google.auth.transport import _mtls_helper
21
22
23def has_default_client_cert_source(include_context_aware=True):
24 """Check if default client SSL credentials exists on the device.
25
26 Args:
27 include_context_aware (bool): include_context_aware indicates if context_aware
28 path location will be checked or should it be skipped.
29
30 Returns:
31 bool: indicating if the default client cert source exists.
32 """
33 if (
34 include_context_aware
35 and _mtls_helper._check_config_path(_mtls_helper.CONTEXT_AWARE_METADATA_PATH)
36 is not None
37 ):
38 return True
39 if (
40 _mtls_helper._check_config_path(
41 _mtls_helper.CERTIFICATE_CONFIGURATION_DEFAULT_PATH
42 )
43 is not None
44 ):
45 return True
46 cert_config_path = getenv("GOOGLE_API_CERTIFICATE_CONFIG")
47 if (
48 cert_config_path
49 and _mtls_helper._check_config_path(cert_config_path) is not None
50 ):
51 return True
52 return False
53
54
55def default_client_cert_source():
56 """Get a callback which returns the default client SSL credentials.
57
58 Returns:
59 Callable[[], [bytes, bytes]]: A callback which returns the default
60 client certificate bytes and private key bytes, both in PEM format.
61
62 Raises:
63 google.auth.exceptions.DefaultClientCertSourceError: If the default
64 client SSL credentials don't exist or are malformed.
65 """
66 if not has_default_client_cert_source(include_context_aware=True):
67 raise exceptions.MutualTLSChannelError(
68 "Default client cert source doesn't exist"
69 )
70
71 def callback():
72 try:
73 _, cert_bytes, key_bytes = _mtls_helper.get_client_cert_and_key()
74 except (OSError, RuntimeError, ValueError) as caught_exc:
75 new_exc = exceptions.MutualTLSChannelError(caught_exc)
76 raise new_exc from caught_exc
77
78 return cert_bytes, key_bytes
79
80 return callback
81
82
83def default_client_encrypted_cert_source(cert_path, key_path):
84 """Get a callback which returns the default encrpyted client SSL credentials.
85
86 Args:
87 cert_path (str): The cert file path. The default client certificate will
88 be written to this file when the returned callback is called.
89 key_path (str): The key file path. The default encrypted client key will
90 be written to this file when the returned callback is called.
91
92 Returns:
93 Callable[[], [str, str, bytes]]: A callback which generates the default
94 client certificate, encrpyted private key and passphrase. It writes
95 the certificate and private key into the cert_path and key_path, and
96 returns the cert_path, key_path and passphrase bytes.
97
98 Raises:
99 google.auth.exceptions.DefaultClientCertSourceError: If any problem
100 occurs when loading or saving the client certificate and key.
101 """
102 if not has_default_client_cert_source(include_context_aware=True):
103 raise exceptions.MutualTLSChannelError(
104 "Default client encrypted cert source doesn't exist"
105 )
106
107 def callback():
108 try:
109 (
110 _,
111 cert_bytes,
112 key_bytes,
113 passphrase_bytes,
114 ) = _mtls_helper.get_client_ssl_credentials(generate_encrypted_key=True)
115 with open(cert_path, "wb") as cert_file:
116 cert_file.write(cert_bytes)
117 with open(key_path, "wb") as key_file:
118 key_file.write(key_bytes)
119 except (exceptions.ClientCertError, OSError) as caught_exc:
120 new_exc = exceptions.MutualTLSChannelError(caught_exc)
121 raise new_exc from caught_exc
122
123 return cert_path, key_path, passphrase_bytes
124
125 return callback
126
127
128def should_use_client_cert():
129 """Returns boolean for whether the client certificate should be used for mTLS.
130
131 This is a wrapper around _mtls_helper.check_use_client_cert().
132 If GOOGLE_API_USE_CLIENT_CERTIFICATE is set to true or false, a corresponding
133 bool value will be returned
134 If GOOGLE_API_USE_CLIENT_CERTIFICATE is unset, the value will be inferred by
135 reading a file pointed at by GOOGLE_API_CERTIFICATE_CONFIG, and verifying it
136 contains a "workload" section. If so, the function will return True,
137 otherwise False.
138
139 Returns:
140 bool: indicating whether the client certificate should be used for mTLS.
141 """
142 return _mtls_helper.check_use_client_cert()