Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/Crypto/Util/_raw_api.py: 42%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

167 statements  

1# =================================================================== 

2# 

3# Copyright (c) 2014, Legrandin <helderijs@gmail.com> 

4# All rights reserved. 

5# 

6# Redistribution and use in source and binary forms, with or without 

7# modification, are permitted provided that the following conditions 

8# are met: 

9# 

10# 1. Redistributions of source code must retain the above copyright 

11# notice, this list of conditions and the following disclaimer. 

12# 2. Redistributions in binary form must reproduce the above copyright 

13# notice, this list of conditions and the following disclaimer in 

14# the documentation and/or other materials provided with the 

15# distribution. 

16# 

17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 

18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 

19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 

20# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 

21# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 

22# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 

23# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 

24# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 

25# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 

26# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 

27# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 

28# POSSIBILITY OF SUCH DAMAGE. 

29# =================================================================== 

30 

31import os 

32import abc 

33import sys 

34from Crypto.Util.py3compat import byte_string 

35from Crypto.Util._file_system import pycryptodome_filename 

36 

37# 

38# List of file suffixes for Python extensions 

39# 

40if sys.version_info[0] < 3: 

41 

42 import imp 

43 extension_suffixes = [] 

44 for ext, mod, typ in imp.get_suffixes(): 

45 if typ == imp.C_EXTENSION: 

46 extension_suffixes.append(ext) 

47 

48else: 

49 

50 from importlib import machinery 

51 extension_suffixes = machinery.EXTENSION_SUFFIXES 

52 

53# Which types with buffer interface we support (apart from byte strings) 

54_buffer_type = (bytearray, memoryview) 

55 

56 

57class _VoidPointer(object): 

58 @abc.abstractmethod 

59 def get(self): 

60 """Return the memory location we point to""" 

61 return 

62 

63 @abc.abstractmethod 

64 def address_of(self): 

65 """Return a raw pointer to this pointer""" 

66 return 

67 

68 

69try: 

70 # Starting from v2.18, pycparser (used by cffi for in-line ABI mode) 

71 # stops working correctly when PYOPTIMIZE==2 or the parameter -OO is 

72 # passed. In that case, we fall back to ctypes. 

73 # Note that PyPy ships with an old version of pycparser so we can keep 

74 # using cffi there. 

75 # See https://github.com/Legrandin/pycryptodome/issues/228 

76 if '__pypy__' not in sys.builtin_module_names and sys.flags.optimize == 2: 

77 raise ImportError("CFFI with optimize=2 fails due to pycparser bug.") 

78 

79 # cffi still uses PyUnicode_GetSize, which was removed in Python 3.12 

80 # thus leading to a crash on cffi.dlopen() 

81 # See https://groups.google.com/u/1/g/python-cffi/c/oZkOIZ_zi5k 

82 if sys.version_info >= (3, 12) and os.name == "nt": 

83 raise ImportError("CFFI is not compatible with Python 3.12 on Windows") 

84 

85 from cffi import FFI 

86 

87 ffi = FFI() 

88 null_pointer = ffi.NULL 

89 uint8_t_type = ffi.typeof(ffi.new("const uint8_t*")) 

90 

91 _Array = ffi.new("uint8_t[1]").__class__.__bases__ 

92 

93 def load_lib(name, cdecl): 

94 """Load a shared library and return a handle to it. 

95 

96 @name, either an absolute path or the name of a library 

97 in the system search path. 

98 

99 @cdecl, the C function declarations. 

100 """ 

101 

102 if hasattr(ffi, "RTLD_DEEPBIND") and not os.getenv('PYCRYPTODOME_DISABLE_DEEPBIND'): 

103 lib = ffi.dlopen(name, ffi.RTLD_DEEPBIND) 

104 else: 

105 lib = ffi.dlopen(name) 

106 ffi.cdef(cdecl) 

107 return lib 

108 

109 def c_ulong(x): 

110 """Convert a Python integer to unsigned long""" 

111 return x 

112 

113 c_ulonglong = c_ulong 

114 c_uint = c_ulong 

115 c_ubyte = c_ulong 

116 

117 def c_size_t(x): 

118 """Convert a Python integer to size_t""" 

119 return x 

120 

121 def create_string_buffer(init_or_size, size=None): 

122 """Allocate the given amount of bytes (initially set to 0)""" 

123 

124 if isinstance(init_or_size, bytes): 

125 size = max(len(init_or_size) + 1, size) 

126 result = ffi.new("uint8_t[]", size) 

127 result[:] = init_or_size 

128 else: 

129 if size: 

130 raise ValueError("Size must be specified once only") 

131 result = ffi.new("uint8_t[]", init_or_size) 

132 return result 

133 

134 def get_c_string(c_string): 

135 """Convert a C string into a Python byte sequence""" 

136 return ffi.string(c_string) 

137 

138 def get_raw_buffer(buf): 

139 """Convert a C buffer into a Python byte sequence""" 

140 return ffi.buffer(buf)[:] 

141 

142 def c_uint8_ptr(data): 

143 if isinstance(data, _buffer_type): 

144 # This only works for cffi >= 1.7 

145 return ffi.cast(uint8_t_type, ffi.from_buffer(data)) 

146 elif byte_string(data) or isinstance(data, _Array): 

147 return data 

148 else: 

149 raise TypeError("Object type %s cannot be passed to C code" % type(data)) 

150 

151 class VoidPointer_cffi(_VoidPointer): 

152 """Model a newly allocated pointer to void""" 

153 

154 def __init__(self): 

155 self._pp = ffi.new("void *[1]") 

156 

157 def get(self): 

158 return self._pp[0] 

159 

160 def address_of(self): 

161 return self._pp 

162 

163 def VoidPointer(): 

164 return VoidPointer_cffi() 

165 

166 backend = "cffi" 

167 

168except ImportError: 

169 

170 import ctypes 

171 from ctypes import (CDLL, c_void_p, byref, c_ulong, c_ulonglong, c_size_t, 

172 create_string_buffer, c_ubyte, c_uint) 

173 from ctypes.util import find_library 

174 from ctypes import Array as _Array 

175 

176 null_pointer = None 

177 cached_architecture = [] 

178 

179 def c_ubyte(c): 

180 if not (0 <= c < 256): 

181 raise OverflowError() 

182 return ctypes.c_ubyte(c) 

183 

184 def load_lib(name, cdecl): 

185 if not cached_architecture: 

186 # platform.architecture() creates a subprocess, so caching the 

187 # result makes successive imports faster. 

188 import platform 

189 cached_architecture[:] = platform.architecture() 

190 bits, linkage = cached_architecture 

191 if "." not in name and not linkage.startswith("Win"): 

192 full_name = find_library(name) 

193 if full_name is None: 

194 raise OSError("Cannot load library '%s'" % name) 

195 name = full_name 

196 return CDLL(name) 

197 

198 def get_c_string(c_string): 

199 return c_string.value 

200 

201 def get_raw_buffer(buf): 

202 return buf.raw 

203 

204 # ---- Get raw pointer --- 

205 

206 _c_ssize_t = ctypes.c_ssize_t 

207 

208 _PyBUF_SIMPLE = 0 

209 _PyObject_GetBuffer = ctypes.pythonapi.PyObject_GetBuffer 

210 _PyBuffer_Release = ctypes.pythonapi.PyBuffer_Release 

211 _py_object = ctypes.py_object 

212 _c_ssize_p = ctypes.POINTER(_c_ssize_t) 

213 

214 # See Include/object.h for CPython 

215 # and https://github.com/pallets/click/blob/master/src/click/_winconsole.py 

216 class _Py_buffer(ctypes.Structure): 

217 _fields_ = [ 

218 ('buf', c_void_p), 

219 ('obj', ctypes.py_object), 

220 ('len', _c_ssize_t), 

221 ('itemsize', _c_ssize_t), 

222 ('readonly', ctypes.c_int), 

223 ('ndim', ctypes.c_int), 

224 ('format', ctypes.c_char_p), 

225 ('shape', _c_ssize_p), 

226 ('strides', _c_ssize_p), 

227 ('suboffsets', _c_ssize_p), 

228 ('internal', c_void_p) 

229 ] 

230 

231 # Extra field for CPython 2.6/2.7 

232 if sys.version_info[0] == 2: 

233 _fields_.insert(-1, ('smalltable', _c_ssize_t * 2)) 

234 

235 def c_uint8_ptr(data): 

236 if byte_string(data) or isinstance(data, _Array): 

237 return data 

238 elif isinstance(data, _buffer_type): 

239 obj = _py_object(data) 

240 buf = _Py_buffer() 

241 _PyObject_GetBuffer(obj, byref(buf), _PyBUF_SIMPLE) 

242 try: 

243 buffer_type = ctypes.c_ubyte * buf.len 

244 return buffer_type.from_address(buf.buf) 

245 finally: 

246 _PyBuffer_Release(byref(buf)) 

247 else: 

248 raise TypeError("Object type %s cannot be passed to C code" % type(data)) 

249 

250 # --- 

251 

252 class VoidPointer_ctypes(_VoidPointer): 

253 """Model a newly allocated pointer to void""" 

254 

255 def __init__(self): 

256 self._p = c_void_p() 

257 

258 def get(self): 

259 return self._p 

260 

261 def address_of(self): 

262 return byref(self._p) 

263 

264 def VoidPointer(): 

265 return VoidPointer_ctypes() 

266 

267 backend = "ctypes" 

268 

269 

270class SmartPointer(object): 

271 """Class to hold a non-managed piece of memory""" 

272 

273 def __init__(self, raw_pointer, destructor): 

274 self._raw_pointer = raw_pointer 

275 self._destructor = destructor 

276 

277 def get(self): 

278 return self._raw_pointer 

279 

280 def release(self): 

281 rp, self._raw_pointer = self._raw_pointer, None 

282 return rp 

283 

284 def __del__(self): 

285 try: 

286 if self._raw_pointer is not None: 

287 self._destructor(self._raw_pointer) 

288 self._raw_pointer = None 

289 except AttributeError: 

290 pass 

291 

292 

293def load_pycryptodome_raw_lib(name, cdecl): 

294 """Load a shared library and return a handle to it. 

295 

296 @name, the name of the library expressed as a PyCryptodome module, 

297 for instance Crypto.Cipher._raw_cbc. 

298 

299 @cdecl, the C function declarations. 

300 """ 

301 

302 split = name.split(".") 

303 dir_comps, basename = split[:-1], split[-1] 

304 attempts = [] 

305 for ext in extension_suffixes: 

306 try: 

307 filename = basename + ext 

308 full_name = pycryptodome_filename(dir_comps, filename) 

309 if not os.path.isfile(full_name): 

310 attempts.append("Not found '%s'" % filename) 

311 continue 

312 return load_lib(full_name, cdecl) 

313 except OSError as exp: 

314 attempts.append("Cannot load '%s': %s" % (filename, str(exp))) 

315 raise OSError("Cannot load native module '%s': %s" % (name, ", ".join(attempts))) 

316 

317 

318def is_buffer(x): 

319 """Return True if object x supports the buffer interface""" 

320 return isinstance(x, (bytes, bytearray, memoryview)) 

321 

322 

323def is_writeable_buffer(x): 

324 return (isinstance(x, bytearray) or 

325 (isinstance(x, memoryview) and not x.readonly))