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():
24 """Check if default client SSL credentials exists on the device.
25
26 Returns:
27 bool: indicating if the default client cert source exists.
28 """
29 if (
30 _mtls_helper._check_config_path(_mtls_helper.CONTEXT_AWARE_METADATA_PATH)
31 is not None
32 ):
33 return True
34 if (
35 _mtls_helper._check_config_path(
36 _mtls_helper.CERTIFICATE_CONFIGURATION_DEFAULT_PATH
37 )
38 is not None
39 ):
40 return True
41 cert_config_path = getenv("GOOGLE_API_CERTIFICATE_CONFIG")
42 if (
43 cert_config_path
44 and _mtls_helper._check_config_path(cert_config_path) is not None
45 ):
46 return True
47 return False
48
49
50def default_client_cert_source():
51 """Get a callback which returns the default client SSL credentials.
52
53 Returns:
54 Callable[[], [bytes, bytes]]: A callback which returns the default
55 client certificate bytes and private key bytes, both in PEM format.
56
57 Raises:
58 google.auth.exceptions.DefaultClientCertSourceError: If the default
59 client SSL credentials don't exist or are malformed.
60 """
61 if not has_default_client_cert_source():
62 raise exceptions.MutualTLSChannelError(
63 "Default client cert source doesn't exist"
64 )
65
66 def callback():
67 try:
68 _, cert_bytes, key_bytes = _mtls_helper.get_client_cert_and_key()
69 except (OSError, RuntimeError, ValueError) as caught_exc:
70 new_exc = exceptions.MutualTLSChannelError(caught_exc)
71 raise new_exc from caught_exc
72
73 return cert_bytes, key_bytes
74
75 return callback
76
77
78def default_client_encrypted_cert_source(cert_path, key_path):
79 """Get a callback which returns the default encrpyted client SSL credentials.
80
81 Args:
82 cert_path (str): The cert file path. The default client certificate will
83 be written to this file when the returned callback is called.
84 key_path (str): The key file path. The default encrypted client key will
85 be written to this file when the returned callback is called.
86
87 Returns:
88 Callable[[], [str, str, bytes]]: A callback which generates the default
89 client certificate, encrpyted private key and passphrase. It writes
90 the certificate and private key into the cert_path and key_path, and
91 returns the cert_path, key_path and passphrase bytes.
92
93 Raises:
94 google.auth.exceptions.DefaultClientCertSourceError: If any problem
95 occurs when loading or saving the client certificate and key.
96 """
97 if not has_default_client_cert_source():
98 raise exceptions.MutualTLSChannelError(
99 "Default client encrypted cert source doesn't exist"
100 )
101
102 def callback():
103 try:
104 (
105 _,
106 cert_bytes,
107 key_bytes,
108 passphrase_bytes,
109 ) = _mtls_helper.get_client_ssl_credentials(generate_encrypted_key=True)
110 with open(cert_path, "wb") as cert_file:
111 cert_file.write(cert_bytes)
112 with open(key_path, "wb") as key_file:
113 key_file.write(key_bytes)
114 except (exceptions.ClientCertError, OSError) as caught_exc:
115 new_exc = exceptions.MutualTLSChannelError(caught_exc)
116 raise new_exc from caught_exc
117
118 return cert_path, key_path, passphrase_bytes
119
120 return callback
121
122
123def should_use_client_cert():
124 """Returns boolean for whether the client certificate should be used for mTLS.
125
126 This is a wrapper around _mtls_helper.check_use_client_cert().
127 If GOOGLE_API_USE_CLIENT_CERTIFICATE is set to true or false, a corresponding
128 bool value will be returned
129 If GOOGLE_API_USE_CLIENT_CERTIFICATE is unset, the value will be inferred by
130 reading a file pointed at by GOOGLE_API_CERTIFICATE_CONFIG, and verifying it
131 contains a "workload" section. If so, the function will return True,
132 otherwise False.
133
134 Returns:
135 bool: indicating whether the client certificate should be used for mTLS.
136 """
137 return _mtls_helper.check_use_client_cert()