Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/cryptography/hazmat/bindings/openssl/binding.py: 81%
84 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 06:36 +0000
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 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._rust import _openssl, openssl
16from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES
19def _openssl_assert(
20 lib,
21 ok: bool,
22 errors: typing.Optional[typing.List[openssl.OpenSSLError]] = None,
23) -> None:
24 if not ok:
25 if errors is None:
26 errors = openssl.capture_error_stack()
28 raise InternalError(
29 "Unknown OpenSSL error. This error is commonly encountered when "
30 "another library is not cleaning up the OpenSSL error stack. If "
31 "you are using cryptography with another library that uses "
32 "OpenSSL try disabling it before reporting a bug. Otherwise "
33 "please file an issue at https://github.com/pyca/cryptography/"
34 "issues with information on how to reproduce "
35 "this. ({!r})".format(errors),
36 errors,
37 )
40def _legacy_provider_error(loaded: bool) -> None:
41 if not loaded:
42 raise RuntimeError(
43 "OpenSSL 3.0's legacy provider failed to load. This is a fatal "
44 "error by default, but cryptography supports running without "
45 "legacy algorithms by setting the environment variable "
46 "CRYPTOGRAPHY_OPENSSL_NO_LEGACY. If you did not expect this error,"
47 " you have likely made a mistake with your OpenSSL configuration."
48 )
51def build_conditional_library(
52 lib: typing.Any,
53 conditional_names: typing.Dict[str, typing.Callable[[], typing.List[str]]],
54) -> typing.Any:
55 conditional_lib = types.ModuleType("lib")
56 conditional_lib._original_lib = lib # type: ignore[attr-defined]
57 excluded_names = set()
58 for condition, names_cb in conditional_names.items():
59 if not getattr(lib, condition):
60 excluded_names.update(names_cb())
62 for attr in dir(lib):
63 if attr not in excluded_names:
64 setattr(conditional_lib, attr, getattr(lib, attr))
66 return conditional_lib
69class Binding:
70 """
71 OpenSSL API wrapper.
72 """
74 lib: typing.ClassVar = None
75 ffi = _openssl.ffi
76 _lib_loaded = False
77 _init_lock = threading.Lock()
78 _legacy_provider: typing.Any = ffi.NULL
79 _legacy_provider_loaded = False
80 _default_provider: typing.Any = ffi.NULL
82 def __init__(self) -> None:
83 self._ensure_ffi_initialized()
85 def _enable_fips(self) -> None:
86 # This function enables FIPS mode for OpenSSL 3.0.0 on installs that
87 # have the FIPS provider installed properly.
88 _openssl_assert(self.lib, self.lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER)
89 self._base_provider = self.lib.OSSL_PROVIDER_load(
90 self.ffi.NULL, b"base"
91 )
92 _openssl_assert(self.lib, self._base_provider != self.ffi.NULL)
93 self.lib._fips_provider = self.lib.OSSL_PROVIDER_load(
94 self.ffi.NULL, b"fips"
95 )
96 _openssl_assert(self.lib, self.lib._fips_provider != self.ffi.NULL)
98 res = self.lib.EVP_default_properties_enable_fips(self.ffi.NULL, 1)
99 _openssl_assert(self.lib, res == 1)
101 @classmethod
102 def _register_osrandom_engine(cls) -> None:
103 # Clear any errors extant in the queue before we start. In many
104 # scenarios other things may be interacting with OpenSSL in the same
105 # process space and it has proven untenable to assume that they will
106 # reliably clear the error queue. Once we clear it here we will
107 # error on any subsequent unexpected item in the stack.
108 cls.lib.ERR_clear_error()
109 if cls.lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE:
110 result = cls.lib.Cryptography_add_osrandom_engine()
111 _openssl_assert(cls.lib, result in (1, 2))
113 @classmethod
114 def _ensure_ffi_initialized(cls) -> None:
115 with cls._init_lock:
116 if not cls._lib_loaded:
117 cls.lib = build_conditional_library(
118 _openssl.lib, CONDITIONAL_NAMES
119 )
120 cls._lib_loaded = True
121 cls._register_osrandom_engine()
122 # As of OpenSSL 3.0.0 we must register a legacy cipher provider
123 # to get RC2 (needed for junk asymmetric private key
124 # serialization), RC4, Blowfish, IDEA, SEED, etc. These things
125 # are ugly legacy, but we aren't going to get rid of them
126 # any time soon.
127 if cls.lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER:
128 if not os.environ.get("CRYPTOGRAPHY_OPENSSL_NO_LEGACY"):
129 cls._legacy_provider = cls.lib.OSSL_PROVIDER_load(
130 cls.ffi.NULL, b"legacy"
131 )
132 cls._legacy_provider_loaded = (
133 cls._legacy_provider != cls.ffi.NULL
134 )
135 _legacy_provider_error(cls._legacy_provider_loaded)
137 cls._default_provider = cls.lib.OSSL_PROVIDER_load(
138 cls.ffi.NULL, b"default"
139 )
140 _openssl_assert(
141 cls.lib, cls._default_provider != cls.ffi.NULL
142 )
144 @classmethod
145 def init_static_locks(cls) -> None:
146 cls._ensure_ffi_initialized()
149def _verify_package_version(version: str) -> None:
150 # Occasionally we run into situations where the version of the Python
151 # package does not match the version of the shared object that is loaded.
152 # This may occur in environments where multiple versions of cryptography
153 # are installed and available in the python path. To avoid errors cropping
154 # up later this code checks that the currently imported package and the
155 # shared object that were loaded have the same version and raise an
156 # ImportError if they do not
157 so_package_version = _openssl.ffi.string(
158 _openssl.lib.CRYPTOGRAPHY_PACKAGE_VERSION
159 )
160 if version.encode("ascii") != so_package_version:
161 raise ImportError(
162 "The version of cryptography does not match the loaded "
163 "shared object. This can happen if you have multiple copies of "
164 "cryptography installed in your Python path. Please try creating "
165 "a new virtual environment to resolve this issue. "
166 "Loaded python version: {}, shared object version: {}".format(
167 version, so_package_version
168 )
169 )
171 _openssl_assert(
172 _openssl.lib,
173 _openssl.lib.OpenSSL_version_num() == openssl.openssl_version(),
174 )
177_verify_package_version(cryptography.__version__)
179Binding.init_static_locks()
181if (
182 sys.platform == "win32"
183 and os.environ.get("PROCESSOR_ARCHITEW6432") is not None
184):
185 warnings.warn(
186 "You are using cryptography on a 32-bit Python on a 64-bit Windows "
187 "Operating System. Cryptography will be significantly faster if you "
188 "switch to using a 64-bit Python.",
189 UserWarning,
190 stacklevel=2,
191 )
194def _verify_openssl_version(lib):
195 if (
196 not lib.CRYPTOGRAPHY_OPENSSL_111D_OR_GREATER
197 and not lib.CRYPTOGRAPHY_IS_LIBRESSL
198 and not lib.CRYPTOGRAPHY_IS_BORINGSSL
199 ):
200 warnings.warn(
201 "Support for OpenSSL less than version 1.1.1d is deprecated and "
202 "the next release of cryptography will drop support. Please "
203 "upgrade your OpenSSL to version 1.1.1d or newer.",
204 utils.DeprecatedIn40,
205 )
208_verify_openssl_version(Binding.lib)