1# coding: utf-8
2from __future__ import unicode_literals, division, absolute_import, print_function
3
4import os
5import platform
6import sys
7import threading
8
9from ._types import str_cls, type_name
10from .errors import LibraryNotFoundError
11from .version import __version__, __version_info__
12
13
14__all__ = [
15 '__version__',
16 '__version_info__',
17 'backend',
18 'ffi',
19 'load_order',
20 'use_ctypes',
21 'use_openssl',
22 'use_winlegacy',
23]
24
25
26_backend_lock = threading.Lock()
27_module_values = {
28 'backend': None,
29 'backend_config': None,
30 'ffi': None
31}
32
33
34def backend():
35 """
36 :return:
37 A unicode string of the backend being used: "openssl", "mac", "win",
38 "winlegacy"
39 """
40
41 if _module_values['backend'] is not None:
42 return _module_values['backend']
43
44 with _backend_lock:
45 if _module_values['backend'] is not None:
46 return _module_values['backend']
47
48 if sys.platform == 'win32':
49 # Windows XP was major version 5, Vista was 6
50 if sys.getwindowsversion()[0] < 6:
51 _module_values['backend'] = 'winlegacy'
52 else:
53 _module_values['backend'] = 'win'
54 elif sys.platform == 'darwin':
55 _module_values['backend'] = 'mac'
56 else:
57 _module_values['backend'] = 'openssl'
58
59 return _module_values['backend']
60
61
62def _backend_config():
63 """
64 :return:
65 A dict of config info for the backend. Only currently used by "openssl",
66 it may contains zero or more of the following keys:
67 - "libcrypto_path"
68 - "libssl_path"
69 """
70
71 if backend() != 'openssl':
72 return {}
73
74 if _module_values['backend_config'] is not None:
75 return _module_values['backend_config']
76
77 with _backend_lock:
78 if _module_values['backend_config'] is not None:
79 return _module_values['backend_config']
80
81 _module_values['backend_config'] = {}
82 return _module_values['backend_config']
83
84
85def use_openssl(libcrypto_path, libssl_path, trust_list_path=None):
86 """
87 Forces using OpenSSL dynamic libraries on OS X (.dylib) or Windows (.dll),
88 or using a specific dynamic library on Linux/BSD (.so).
89
90 This can also be used to configure oscrypto to use LibreSSL dynamic
91 libraries.
92
93 This method must be called before any oscrypto submodules are imported.
94
95 :param libcrypto_path:
96 A unicode string of the file path to the OpenSSL/LibreSSL libcrypto
97 dynamic library.
98
99 :param libssl_path:
100 A unicode string of the file path to the OpenSSL/LibreSSL libssl
101 dynamic library.
102
103 :param trust_list_path:
104 An optional unicode string of the path to a file containing
105 OpenSSL-compatible CA certificates in PEM format. If this is not
106 provided and the platform is OS X or Windows, the system trust roots
107 will be exported from the OS and used for all TLS connections.
108
109 :raises:
110 ValueError - when one of the paths is not a unicode string
111 OSError - when the trust_list_path does not exist on the filesystem
112 oscrypto.errors.LibraryNotFoundError - when one of the path does not exist on the filesystem
113 RuntimeError - when this function is called after another part of oscrypto has been imported
114 """
115
116 if not isinstance(libcrypto_path, str_cls):
117 raise ValueError('libcrypto_path must be a unicode string, not %s' % type_name(libcrypto_path))
118
119 if not isinstance(libssl_path, str_cls):
120 raise ValueError('libssl_path must be a unicode string, not %s' % type_name(libssl_path))
121
122 do_path_checks = True
123 if sys.platform == 'darwin':
124 mac_version_info = tuple(map(int, platform.mac_ver()[0].split('.')[:2]))
125 do_path_checks = mac_version_info < (10, 16)
126
127 if do_path_checks:
128 if not os.path.exists(libcrypto_path):
129 raise LibraryNotFoundError('libcrypto does not exist at %s' % libcrypto_path)
130
131 if not os.path.exists(libssl_path):
132 raise LibraryNotFoundError('libssl does not exist at %s' % libssl_path)
133
134 if trust_list_path is not None:
135 if not isinstance(trust_list_path, str_cls):
136 raise ValueError('trust_list_path must be a unicode string, not %s' % type_name(trust_list_path))
137 if not os.path.exists(trust_list_path):
138 raise OSError('trust_list_path does not exist at %s' % trust_list_path)
139
140 with _backend_lock:
141 new_config = {
142 'libcrypto_path': libcrypto_path,
143 'libssl_path': libssl_path,
144 'trust_list_path': trust_list_path,
145 }
146
147 if _module_values['backend'] == 'openssl' and _module_values['backend_config'] == new_config:
148 return
149
150 if _module_values['backend'] is not None:
151 raise RuntimeError('Another part of oscrypto has already been imported, unable to force use of OpenSSL')
152
153 _module_values['backend'] = 'openssl'
154 _module_values['backend_config'] = new_config
155
156
157def use_winlegacy():
158 """
159 Forces use of the legacy Windows CryptoAPI. This should only be used on
160 Windows XP or for testing. It is less full-featured than the Cryptography
161 Next Generation (CNG) API, and as a result the elliptic curve and PSS
162 padding features are implemented in pure Python. This isn't ideal, but it
163 a shim for end-user client code. No one is going to run a server on Windows
164 XP anyway, right?!
165
166 :raises:
167 EnvironmentError - when this function is called on an operating system other than Windows
168 RuntimeError - when this function is called after another part of oscrypto has been imported
169 """
170
171 if sys.platform != 'win32':
172 plat = platform.system() or sys.platform
173 if plat == 'Darwin':
174 plat = 'OS X'
175 raise EnvironmentError('The winlegacy backend can only be used on Windows, not %s' % plat)
176
177 with _backend_lock:
178 if _module_values['backend'] == 'winlegacy':
179 return
180
181 if _module_values['backend'] is not None:
182 raise RuntimeError(
183 'Another part of oscrypto has already been imported, unable to force use of Windows legacy CryptoAPI'
184 )
185
186 _module_values['backend'] = 'winlegacy'
187
188
189def use_ctypes():
190 """
191 Forces use of ctypes instead of cffi for the FFI layer
192
193 :raises:
194 RuntimeError - when this function is called after another part of oscrypto has been imported
195 """
196
197 with _backend_lock:
198 if _module_values['ffi'] == 'ctypes':
199 return
200
201 if _module_values['backend'] is not None:
202 raise RuntimeError(
203 'Another part of oscrypto has already been imported, unable to force use of ctypes'
204 )
205
206 _module_values['ffi'] = 'ctypes'
207
208
209def ffi():
210 """
211 Returns the FFI module being used
212
213 :return:
214 A unicode string of "cffi" or "ctypes"
215 """
216
217 if _module_values['ffi'] is not None:
218 return _module_values['ffi']
219
220 with _backend_lock:
221 try:
222 import cffi # noqa: F401
223 _module_values['ffi'] = 'cffi'
224 except (ImportError):
225 _module_values['ffi'] = 'ctypes'
226
227 return _module_values['ffi']
228
229
230def load_order():
231 """
232 Returns a list of the module and sub-module names for oscrypto in
233 dependency load order, for the sake of live reloading code
234
235 :return:
236 A list of unicode strings of module names, as they would appear in
237 sys.modules, ordered by which module should be reloaded first
238 """
239
240 return [
241 'oscrypto._asn1',
242 'oscrypto._cipher_suites',
243 'oscrypto._errors',
244 'oscrypto._int',
245 'oscrypto._types',
246 'oscrypto.errors',
247 'oscrypto.version',
248 'oscrypto',
249 'oscrypto._ffi',
250 'oscrypto._pkcs12',
251 'oscrypto._pkcs5',
252 'oscrypto._rand',
253 'oscrypto._tls',
254 'oscrypto._linux_bsd.trust_list',
255 'oscrypto._mac._common_crypto_cffi',
256 'oscrypto._mac._common_crypto_ctypes',
257 'oscrypto._mac._common_crypto',
258 'oscrypto._mac._core_foundation_cffi',
259 'oscrypto._mac._core_foundation_ctypes',
260 'oscrypto._mac._core_foundation',
261 'oscrypto._mac._security_cffi',
262 'oscrypto._mac._security_ctypes',
263 'oscrypto._mac._security',
264 'oscrypto._mac.trust_list',
265 'oscrypto._mac.util',
266 'oscrypto._openssl._libcrypto_cffi',
267 'oscrypto._openssl._libcrypto_ctypes',
268 'oscrypto._openssl._libcrypto',
269 'oscrypto._openssl._libssl_cffi',
270 'oscrypto._openssl._libssl_ctypes',
271 'oscrypto._openssl._libssl',
272 'oscrypto._openssl.util',
273 'oscrypto._win._cng_cffi',
274 'oscrypto._win._cng_ctypes',
275 'oscrypto._win._cng',
276 'oscrypto._win._decode',
277 'oscrypto._win._advapi32_cffi',
278 'oscrypto._win._advapi32_ctypes',
279 'oscrypto._win._advapi32',
280 'oscrypto._win._kernel32_cffi',
281 'oscrypto._win._kernel32_ctypes',
282 'oscrypto._win._kernel32',
283 'oscrypto._win._secur32_cffi',
284 'oscrypto._win._secur32_ctypes',
285 'oscrypto._win._secur32',
286 'oscrypto._win._crypt32_cffi',
287 'oscrypto._win._crypt32_ctypes',
288 'oscrypto._win._crypt32',
289 'oscrypto._win.trust_list',
290 'oscrypto._win.util',
291 'oscrypto.trust_list',
292 'oscrypto.util',
293 'oscrypto.kdf',
294 'oscrypto._mac.symmetric',
295 'oscrypto._openssl.symmetric',
296 'oscrypto._win.symmetric',
297 'oscrypto.symmetric',
298 'oscrypto._asymmetric',
299 'oscrypto._ecdsa',
300 'oscrypto._pkcs1',
301 'oscrypto._mac.asymmetric',
302 'oscrypto._openssl.asymmetric',
303 'oscrypto._win.asymmetric',
304 'oscrypto.asymmetric',
305 'oscrypto.keys',
306 'oscrypto._mac.tls',
307 'oscrypto._openssl.tls',
308 'oscrypto._win.tls',
309 'oscrypto.tls',
310 ]