binding.py 4.5 KB

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