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.
4
5from __future__ import annotations
6
7import os
8import sys
9import threading
10import types
11import typing
12import warnings
13from collections.abc import Callable
14
15import cryptography
16from cryptography.exceptions import InternalError
17from cryptography.hazmat.bindings._rust import _openssl, openssl
18from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES
19from cryptography.utils import CryptographyDeprecationWarning
20
21
22def _openssl_assert(ok: bool) -> None:
23 if not ok:
24 errors = openssl.capture_error_stack()
25
26 raise InternalError(
27 "Unknown OpenSSL error. This error is commonly encountered when "
28 "another library is not cleaning up the OpenSSL error stack. If "
29 "you are using cryptography with another library that uses "
30 "OpenSSL try disabling it before reporting a bug. Otherwise "
31 "please file an issue at https://github.com/pyca/cryptography/"
32 "issues with information on how to reproduce "
33 f"this. ({errors!r})",
34 errors,
35 )
36
37
38def build_conditional_library(
39 lib: typing.Any,
40 conditional_names: dict[str, Callable[[], list[str]]],
41) -> typing.Any:
42 conditional_lib = types.ModuleType("lib")
43 conditional_lib._original_lib = lib # type: ignore[attr-defined]
44 excluded_names = set()
45 for condition, names_cb in conditional_names.items():
46 if not getattr(lib, condition):
47 excluded_names.update(names_cb())
48
49 for attr in dir(lib):
50 if attr not in excluded_names:
51 setattr(conditional_lib, attr, getattr(lib, attr))
52
53 return conditional_lib
54
55
56class Binding:
57 """
58 OpenSSL API wrapper.
59 """
60
61 lib: typing.ClassVar = None
62 ffi = _openssl.ffi
63 _lib_loaded = False
64 _init_lock = threading.Lock()
65
66 def __init__(self) -> None:
67 self._ensure_ffi_initialized()
68
69 @classmethod
70 def _ensure_ffi_initialized(cls) -> None:
71 with cls._init_lock:
72 if not cls._lib_loaded:
73 cls.lib = build_conditional_library(
74 _openssl.lib, CONDITIONAL_NAMES
75 )
76 cls._lib_loaded = True
77
78 @classmethod
79 def init_static_locks(cls) -> None:
80 cls._ensure_ffi_initialized()
81
82
83def _verify_package_version(version: str) -> None:
84 # Occasionally we run into situations where the version of the Python
85 # package does not match the version of the shared object that is loaded.
86 # This may occur in environments where multiple versions of cryptography
87 # are installed and available in the python path. To avoid errors cropping
88 # up later this code checks that the currently imported package and the
89 # shared object that were loaded have the same version and raise an
90 # ImportError if they do not
91 so_package_version = _openssl.ffi.string(
92 _openssl.lib.CRYPTOGRAPHY_PACKAGE_VERSION
93 )
94 if version.encode("ascii") != so_package_version:
95 raise ImportError(
96 "The version of cryptography does not match the loaded "
97 "shared object. This can happen if you have multiple copies of "
98 "cryptography installed in your Python path. Please try creating "
99 "a new virtual environment to resolve this issue. "
100 f"Loaded python version: {version}, "
101 f"shared object version: {so_package_version}"
102 )
103
104 _openssl_assert(
105 _openssl.lib.OpenSSL_version_num() == openssl.openssl_version(),
106 )
107
108
109_verify_package_version(cryptography.__version__)
110
111Binding.init_static_locks()
112
113if (
114 sys.platform == "win32"
115 and os.environ.get("PROCESSOR_ARCHITEW6432") is not None
116):
117 warnings.warn(
118 "You are using cryptography on a 32-bit Python on a 64-bit Windows "
119 "Operating System. Cryptography will be significantly faster if you "
120 "switch to using a 64-bit Python.",
121 UserWarning,
122 stacklevel=2,
123 )
124
125if (
126 not openssl.CRYPTOGRAPHY_IS_LIBRESSL
127 and not openssl.CRYPTOGRAPHY_IS_BORINGSSL
128 and not openssl.CRYPTOGRAPHY_IS_AWSLC
129 and not openssl.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER
130):
131 warnings.warn(
132 "You are using OpenSSL < 3.0. Support for OpenSSL < 3.0 is deprecated "
133 "and will be removed in the next release. Please upgrade to OpenSSL "
134 "3.0 or later.",
135 CryptographyDeprecationWarning,
136 stacklevel=2,
137 )