Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/cryptography/hazmat/bindings/openssl/binding.py: 80%
124 statements
« prev ^ index » next coverage.py v7.2.1, created at 2023-03-14 06:36 +0000
« prev ^ index » next coverage.py v7.2.1, created at 2023-03-14 06:36 +0000
1# This file is dual licensed under the terms of the Apache License, Version
2# 2.0, and the BSD License. See the LICENSE file in the root of this repository
3# for complete details.
5import os
6import sys
7import threading
8import types
9import typing
10import warnings
12import cryptography
13from cryptography import utils
14from cryptography.exceptions import InternalError
15from cryptography.hazmat.bindings._openssl import ffi, lib
16from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES
19class _OpenSSLErrorWithText(typing.NamedTuple):
20 code: int
21 lib: int
22 reason: int
23 reason_text: bytes
26class _OpenSSLError:
27 def __init__(self, code: int, lib: int, reason: int):
28 self._code = code
29 self._lib = lib
30 self._reason = reason
32 def _lib_reason_match(self, lib: int, reason: int) -> bool:
33 return lib == self.lib and reason == self.reason
35 @property
36 def code(self) -> int:
37 return self._code
39 @property
40 def lib(self) -> int:
41 return self._lib
43 @property
44 def reason(self) -> int:
45 return self._reason
48def _consume_errors(lib) -> typing.List[_OpenSSLError]:
49 errors = []
50 while True:
51 code: int = lib.ERR_get_error()
52 if code == 0:
53 break
55 err_lib: int = lib.ERR_GET_LIB(code)
56 err_reason: int = lib.ERR_GET_REASON(code)
58 errors.append(_OpenSSLError(code, err_lib, err_reason))
60 return errors
63def _errors_with_text(
64 errors: typing.List[_OpenSSLError],
65) -> typing.List[_OpenSSLErrorWithText]:
66 errors_with_text = []
67 for err in errors:
68 buf = ffi.new("char[]", 256)
69 lib.ERR_error_string_n(err.code, buf, len(buf))
70 err_text_reason: bytes = ffi.string(buf)
72 errors_with_text.append(
73 _OpenSSLErrorWithText(
74 err.code, err.lib, err.reason, err_text_reason
75 )
76 )
78 return errors_with_text
81def _consume_errors_with_text(lib):
82 return _errors_with_text(_consume_errors(lib))
85def _openssl_assert(
86 lib, ok: bool, errors: typing.Optional[typing.List[_OpenSSLError]] = None
87) -> None:
88 if not ok:
89 if errors is None:
90 errors = _consume_errors(lib)
91 errors_with_text = _errors_with_text(errors)
93 raise InternalError(
94 "Unknown OpenSSL error. This error is commonly encountered when "
95 "another library is not cleaning up the OpenSSL error stack. If "
96 "you are using cryptography with another library that uses "
97 "OpenSSL try disabling it before reporting a bug. Otherwise "
98 "please file an issue at https://github.com/pyca/cryptography/"
99 "issues with information on how to reproduce "
100 "this. ({!r})".format(errors_with_text),
101 errors_with_text,
102 )
105def _legacy_provider_error(loaded: bool) -> None:
106 if not loaded:
107 raise RuntimeError(
108 "OpenSSL 3.0's legacy provider failed to load. This is a fatal "
109 "error by default, but cryptography supports running without "
110 "legacy algorithms by setting the environment variable "
111 "CRYPTOGRAPHY_OPENSSL_NO_LEGACY. If you did not expect this error,"
112 " you have likely made a mistake with your OpenSSL configuration."
113 )
116def build_conditional_library(
117 lib: typing.Any,
118 conditional_names: typing.Dict[str, typing.Callable[[], typing.List[str]]],
119) -> typing.Any:
120 conditional_lib = types.ModuleType("lib")
121 conditional_lib._original_lib = lib # type: ignore[attr-defined]
122 excluded_names = set()
123 for condition, names_cb in conditional_names.items():
124 if not getattr(lib, condition):
125 excluded_names.update(names_cb())
127 for attr in dir(lib):
128 if attr not in excluded_names:
129 setattr(conditional_lib, attr, getattr(lib, attr))
131 return conditional_lib
134class Binding:
135 """
136 OpenSSL API wrapper.
137 """
139 lib: typing.ClassVar = None
140 ffi = ffi
141 _lib_loaded = False
142 _init_lock = threading.Lock()
143 _legacy_provider: typing.Any = ffi.NULL
144 _legacy_provider_loaded = False
145 _default_provider: typing.Any = ffi.NULL
147 def __init__(self) -> None:
148 self._ensure_ffi_initialized()
150 def _enable_fips(self) -> None:
151 # This function enables FIPS mode for OpenSSL 3.0.0 on installs that
152 # have the FIPS provider installed properly.
153 _openssl_assert(self.lib, self.lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER)
154 self._base_provider = self.lib.OSSL_PROVIDER_load(
155 self.ffi.NULL, b"base"
156 )
157 _openssl_assert(self.lib, self._base_provider != self.ffi.NULL)
158 self.lib._fips_provider = self.lib.OSSL_PROVIDER_load(
159 self.ffi.NULL, b"fips"
160 )
161 _openssl_assert(self.lib, self.lib._fips_provider != self.ffi.NULL)
163 res = self.lib.EVP_default_properties_enable_fips(self.ffi.NULL, 1)
164 _openssl_assert(self.lib, res == 1)
166 @classmethod
167 def _register_osrandom_engine(cls) -> None:
168 # Clear any errors extant in the queue before we start. In many
169 # scenarios other things may be interacting with OpenSSL in the same
170 # process space and it has proven untenable to assume that they will
171 # reliably clear the error queue. Once we clear it here we will
172 # error on any subsequent unexpected item in the stack.
173 cls.lib.ERR_clear_error()
174 if cls.lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE:
175 result = cls.lib.Cryptography_add_osrandom_engine()
176 _openssl_assert(cls.lib, result in (1, 2))
178 @classmethod
179 def _ensure_ffi_initialized(cls) -> None:
180 with cls._init_lock:
181 if not cls._lib_loaded:
182 cls.lib = build_conditional_library(lib, CONDITIONAL_NAMES)
183 cls._lib_loaded = True
184 cls._register_osrandom_engine()
185 # As of OpenSSL 3.0.0 we must register a legacy cipher provider
186 # to get RC2 (needed for junk asymmetric private key
187 # serialization), RC4, Blowfish, IDEA, SEED, etc. These things
188 # are ugly legacy, but we aren't going to get rid of them
189 # any time soon.
190 if cls.lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER:
191 if not os.environ.get("CRYPTOGRAPHY_OPENSSL_NO_LEGACY"):
192 cls._legacy_provider = cls.lib.OSSL_PROVIDER_load(
193 cls.ffi.NULL, b"legacy"
194 )
195 cls._legacy_provider_loaded = (
196 cls._legacy_provider != cls.ffi.NULL
197 )
198 _legacy_provider_error(cls._legacy_provider_loaded)
200 cls._default_provider = cls.lib.OSSL_PROVIDER_load(
201 cls.ffi.NULL, b"default"
202 )
203 _openssl_assert(
204 cls.lib, cls._default_provider != cls.ffi.NULL
205 )
207 @classmethod
208 def init_static_locks(cls) -> None:
209 cls._ensure_ffi_initialized()
212def _verify_package_version(version: str) -> None:
213 # Occasionally we run into situations where the version of the Python
214 # package does not match the version of the shared object that is loaded.
215 # This may occur in environments where multiple versions of cryptography
216 # are installed and available in the python path. To avoid errors cropping
217 # up later this code checks that the currently imported package and the
218 # shared object that were loaded have the same version and raise an
219 # ImportError if they do not
220 so_package_version = ffi.string(lib.CRYPTOGRAPHY_PACKAGE_VERSION)
221 if version.encode("ascii") != so_package_version:
222 raise ImportError(
223 "The version of cryptography does not match the loaded "
224 "shared object. This can happen if you have multiple copies of "
225 "cryptography installed in your Python path. Please try creating "
226 "a new virtual environment to resolve this issue. "
227 "Loaded python version: {}, shared object version: {}".format(
228 version, so_package_version
229 )
230 )
233_verify_package_version(cryptography.__version__)
235Binding.init_static_locks()
237if (
238 sys.platform == "win32"
239 and os.environ.get("PROCESSOR_ARCHITEW6432") is not None
240):
241 warnings.warn(
242 "You are using cryptography on a 32-bit Python on a 64-bit Windows "
243 "Operating System. Cryptography will be significantly faster if you "
244 "switch to using a 64-bit Python.",
245 UserWarning,
246 stacklevel=2,
247 )
250def _verify_openssl_version(lib):
251 if (
252 not lib.CRYPTOGRAPHY_OPENSSL_111D_OR_GREATER
253 and not lib.CRYPTOGRAPHY_IS_LIBRESSL
254 and not lib.CRYPTOGRAPHY_IS_BORINGSSL
255 ):
256 warnings.warn(
257 "Support for OpenSSL less than version 1.1.1d is deprecated and "
258 "the next release of cryptography will drop support. Please "
259 "upgrade your OpenSSL to version 1.1.1d or newer.",
260 utils.DeprecatedIn40,
261 )
264_verify_openssl_version(Binding.lib)