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[typing.Any] = 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    )