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

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 

5import os 

6import sys 

7import threading 

8import types 

9import typing 

10import warnings 

11 

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 

17 

18 

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() 

27 

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 ) 

38 

39 

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 ) 

49 

50 

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()) 

61 

62 for attr in dir(lib): 

63 if attr not in excluded_names: 

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

65 

66 return conditional_lib 

67 

68 

69class Binding: 

70 """ 

71 OpenSSL API wrapper. 

72 """ 

73 

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 

81 

82 def __init__(self) -> None: 

83 self._ensure_ffi_initialized() 

84 

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) 

97 

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

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

100 

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)) 

112 

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) 

136 

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 ) 

143 

144 @classmethod 

145 def init_static_locks(cls) -> None: 

146 cls._ensure_ffi_initialized() 

147 

148 

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 ) 

170 

171 _openssl_assert( 

172 _openssl.lib, 

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

174 ) 

175 

176 

177_verify_package_version(cryptography.__version__) 

178 

179Binding.init_static_locks() 

180 

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 ) 

192 

193 

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 ) 

206 

207 

208_verify_openssl_version(Binding.lib)