Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/cryptography/hazmat/bindings/openssl/binding.py: 73%

73 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-08 07:26 +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. 

4 

5from __future__ import annotations 

6 

7import os 

8import sys 

9import threading 

10import types 

11import typing 

12import warnings 

13 

14import cryptography 

15from cryptography.exceptions import InternalError 

16from cryptography.hazmat.bindings._rust import _openssl, openssl 

17from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES 

18 

19 

20def _openssl_assert( 

21 lib, 

22 ok: bool, 

23 errors: list[openssl.OpenSSLError] | None = None, 

24) -> None: 

25 if not ok: 

26 if errors is None: 

27 errors = openssl.capture_error_stack() 

28 

29 raise InternalError( 

30 "Unknown OpenSSL error. This error is commonly encountered when " 

31 "another library is not cleaning up the OpenSSL error stack. If " 

32 "you are using cryptography with another library that uses " 

33 "OpenSSL try disabling it before reporting a bug. Otherwise " 

34 "please file an issue at https://github.com/pyca/cryptography/" 

35 "issues with information on how to reproduce " 

36 f"this. ({errors!r})", 

37 errors, 

38 ) 

39 

40 

41def _legacy_provider_error(loaded: bool) -> None: 

42 if not loaded: 

43 raise RuntimeError( 

44 "OpenSSL 3.0's legacy provider failed to load. This is a fatal " 

45 "error by default, but cryptography supports running without " 

46 "legacy algorithms by setting the environment variable " 

47 "CRYPTOGRAPHY_OPENSSL_NO_LEGACY. If you did not expect this error," 

48 " you have likely made a mistake with your OpenSSL configuration." 

49 ) 

50 

51 

52def build_conditional_library( 

53 lib: typing.Any, 

54 conditional_names: dict[str, typing.Callable[[], list[str]]], 

55) -> typing.Any: 

56 conditional_lib = types.ModuleType("lib") 

57 conditional_lib._original_lib = lib # type: ignore[attr-defined] 

58 excluded_names = set() 

59 for condition, names_cb in conditional_names.items(): 

60 if not getattr(lib, condition): 

61 excluded_names.update(names_cb()) 

62 

63 for attr in dir(lib): 

64 if attr not in excluded_names: 

65 setattr(conditional_lib, attr, getattr(lib, attr)) 

66 

67 return conditional_lib 

68 

69 

70class Binding: 

71 """ 

72 OpenSSL API wrapper. 

73 """ 

74 

75 lib: typing.ClassVar = None 

76 ffi = _openssl.ffi 

77 _lib_loaded = False 

78 _init_lock = threading.Lock() 

79 _legacy_provider: typing.Any = ffi.NULL 

80 _legacy_provider_loaded = False 

81 _default_provider: typing.Any = ffi.NULL 

82 

83 def __init__(self) -> None: 

84 self._ensure_ffi_initialized() 

85 

86 def _enable_fips(self) -> None: 

87 # This function enables FIPS mode for OpenSSL 3.0.0 on installs that 

88 # have the FIPS provider installed properly. 

89 _openssl_assert(self.lib, self.lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER) 

90 self._base_provider = self.lib.OSSL_PROVIDER_load( 

91 self.ffi.NULL, b"base" 

92 ) 

93 _openssl_assert(self.lib, self._base_provider != self.ffi.NULL) 

94 self.lib._fips_provider = self.lib.OSSL_PROVIDER_load( 

95 self.ffi.NULL, b"fips" 

96 ) 

97 _openssl_assert(self.lib, self.lib._fips_provider != self.ffi.NULL) 

98 

99 res = self.lib.EVP_default_properties_enable_fips(self.ffi.NULL, 1) 

100 _openssl_assert(self.lib, res == 1) 

101 

102 @classmethod 

103 def _ensure_ffi_initialized(cls) -> None: 

104 with cls._init_lock: 

105 if not cls._lib_loaded: 

106 cls.lib = build_conditional_library( 

107 _openssl.lib, CONDITIONAL_NAMES 

108 ) 

109 cls._lib_loaded = True 

110 # As of OpenSSL 3.0.0 we must register a legacy cipher provider 

111 # to get RC2 (needed for junk asymmetric private key 

112 # serialization), RC4, Blowfish, IDEA, SEED, etc. These things 

113 # are ugly legacy, but we aren't going to get rid of them 

114 # any time soon. 

115 if cls.lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER: 

116 if not os.environ.get("CRYPTOGRAPHY_OPENSSL_NO_LEGACY"): 

117 cls._legacy_provider = cls.lib.OSSL_PROVIDER_load( 

118 cls.ffi.NULL, b"legacy" 

119 ) 

120 cls._legacy_provider_loaded = ( 

121 cls._legacy_provider != cls.ffi.NULL 

122 ) 

123 _legacy_provider_error(cls._legacy_provider_loaded) 

124 

125 cls._default_provider = cls.lib.OSSL_PROVIDER_load( 

126 cls.ffi.NULL, b"default" 

127 ) 

128 _openssl_assert( 

129 cls.lib, cls._default_provider != cls.ffi.NULL 

130 ) 

131 

132 @classmethod 

133 def init_static_locks(cls) -> None: 

134 cls._ensure_ffi_initialized() 

135 

136 

137def _verify_package_version(version: str) -> None: 

138 # Occasionally we run into situations where the version of the Python 

139 # package does not match the version of the shared object that is loaded. 

140 # This may occur in environments where multiple versions of cryptography 

141 # are installed and available in the python path. To avoid errors cropping 

142 # up later this code checks that the currently imported package and the 

143 # shared object that were loaded have the same version and raise an 

144 # ImportError if they do not 

145 so_package_version = _openssl.ffi.string( 

146 _openssl.lib.CRYPTOGRAPHY_PACKAGE_VERSION 

147 ) 

148 if version.encode("ascii") != so_package_version: 

149 raise ImportError( 

150 "The version of cryptography does not match the loaded " 

151 "shared object. This can happen if you have multiple copies of " 

152 "cryptography installed in your Python path. Please try creating " 

153 "a new virtual environment to resolve this issue. " 

154 "Loaded python version: {}, shared object version: {}".format( 

155 version, so_package_version 

156 ) 

157 ) 

158 

159 _openssl_assert( 

160 _openssl.lib, 

161 _openssl.lib.OpenSSL_version_num() == openssl.openssl_version(), 

162 ) 

163 

164 

165_verify_package_version(cryptography.__version__) 

166 

167Binding.init_static_locks() 

168 

169if ( 

170 sys.platform == "win32" 

171 and os.environ.get("PROCESSOR_ARCHITEW6432") is not None 

172): 

173 warnings.warn( 

174 "You are using cryptography on a 32-bit Python on a 64-bit Windows " 

175 "Operating System. Cryptography will be significantly faster if you " 

176 "switch to using a 64-bit Python.", 

177 UserWarning, 

178 stacklevel=2, 

179 )