_raw_api.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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. import sys
  31. from Crypto.Util.py3compat import byte_string
  32. from Crypto.Util._file_system import pycryptodome_filename
  33. #
  34. # List of file suffixes for Python extensions
  35. #
  36. if sys.version_info[0] <= 3 or \
  37. (sys.version_info[0] == 3 and sys.version_info[1] <= 3):
  38. import imp
  39. extension_suffixes = []
  40. for ext, mod, typ in imp.get_suffixes():
  41. if typ == imp.C_EXTENSION:
  42. extension_suffixes.append(ext)
  43. else:
  44. from importlib import machinery
  45. extension_suffixes = machinery.EXTENSION_SUFFIXES
  46. try:
  47. from cffi import FFI
  48. ffi = FFI()
  49. null_pointer = ffi.NULL
  50. def load_lib(name, cdecl):
  51. """Load a shared library and return a handle to it.
  52. @name, either an absolute path or the name of a library
  53. in the system search path.
  54. @cdecl, the C function declarations.
  55. """
  56. lib = ffi.dlopen(name)
  57. ffi.cdef(cdecl)
  58. return lib
  59. def c_ulong(x):
  60. """Convert a Python integer to unsigned long"""
  61. return x
  62. c_ulonglong = c_ulong
  63. def c_size_t(x):
  64. """Convert a Python integer to size_t"""
  65. return x
  66. def create_string_buffer(size):
  67. """Allocate the given amount of bytes (initially set to 0)"""
  68. return ffi.new("uint8_t[]", size)
  69. def get_c_string(c_string):
  70. """Convert a C string into a Python byte sequence"""
  71. return ffi.string(c_string)
  72. def get_raw_buffer(buf):
  73. """Convert a C buffer into a Python byte sequence"""
  74. return ffi.buffer(buf)[:]
  75. class VoidPointer(object):
  76. """Model a newly allocated pointer to void"""
  77. def __init__(self):
  78. self._pp = ffi.new("void *[1]")
  79. def get(self):
  80. return self._pp[0]
  81. def address_of(self):
  82. return self._pp
  83. Array = ffi.new("uint8_t[1]").__class__.__bases__
  84. backend = "cffi"
  85. except ImportError:
  86. from ctypes import (CDLL, c_void_p, byref, c_ulong, c_ulonglong, c_size_t,
  87. create_string_buffer)
  88. from ctypes.util import find_library
  89. from _ctypes import Array
  90. null_pointer = None
  91. def load_lib(name, cdecl):
  92. import platform
  93. bits, linkage = platform.architecture()
  94. if "." not in name and not linkage.startswith("Win"):
  95. full_name = find_library(name)
  96. if full_name is None:
  97. raise OSError("Cannot load library '%s'" % name)
  98. name = full_name
  99. return CDLL(name)
  100. def get_c_string(c_string):
  101. return c_string.value
  102. def get_raw_buffer(buf):
  103. return buf.raw
  104. class VoidPointer(object):
  105. """Model a newly allocated pointer to void"""
  106. def __init__(self):
  107. self._p = c_void_p()
  108. def get(self):
  109. return self._p
  110. def address_of(self):
  111. return byref(self._p)
  112. backend = "ctypes"
  113. class SmartPointer(object):
  114. """Class to hold a non-managed piece of memory"""
  115. def __init__(self, raw_pointer, destructor):
  116. self._raw_pointer = raw_pointer
  117. self._destructor = destructor
  118. def get(self):
  119. return self._raw_pointer
  120. def release(self):
  121. rp, self._raw_pointer = self._raw_pointer, None
  122. return rp
  123. def __del__(self):
  124. try:
  125. if self._raw_pointer is not None:
  126. self._destructor(self._raw_pointer)
  127. self._raw_pointer = None
  128. except AttributeError:
  129. pass
  130. def load_pycryptodome_raw_lib(name, cdecl):
  131. """Load a shared library and return a handle to it.
  132. @name, the name of the library expressed as a PyCryptodome module,
  133. for instance Crypto.Cipher._raw_cbc.
  134. @cdecl, the C function declarations.
  135. """
  136. split = name.split(".")
  137. dir_comps, basename = split[:-1], split[-1]
  138. for ext in extension_suffixes:
  139. try:
  140. return load_lib(pycryptodome_filename(dir_comps, basename + ext),
  141. cdecl)
  142. except OSError:
  143. pass
  144. raise OSError("Cannot load native module '%s'" % name)
  145. def expect_byte_string(data):
  146. if not byte_string(data) and not isinstance(data, Array):
  147. raise TypeError("Only byte strings can be passed to C code")