2015-01-19 14:00:35 +01:00
|
|
|
# ===================================================================
|
|
|
|
|
#
|
|
|
|
|
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
|
|
|
|
|
# All rights reserved.
|
|
|
|
|
#
|
|
|
|
|
# Redistribution and use in source and binary forms, with or without
|
|
|
|
|
# modification, are permitted provided that the following conditions
|
|
|
|
|
# are met:
|
|
|
|
|
#
|
|
|
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
|
|
|
# notice, this list of conditions and the following disclaimer.
|
|
|
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
|
# notice, this list of conditions and the following disclaimer in
|
|
|
|
|
# the documentation and/or other materials provided with the
|
|
|
|
|
# distribution.
|
|
|
|
|
#
|
|
|
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
|
|
|
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
|
|
|
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
|
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
|
|
|
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
|
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
|
|
|
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
|
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
|
|
|
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
|
# POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
# ===================================================================
|
|
|
|
|
|
2017-05-21 22:52:53 +02:00
|
|
|
import sys
|
2015-02-10 22:29:40 +01:00
|
|
|
from Crypto.Util.py3compat import byte_string
|
2016-01-03 16:02:56 -05:00
|
|
|
from Crypto.Util._file_system import pycryptodome_filename
|
2015-02-10 22:29:40 +01:00
|
|
|
|
2017-05-21 22:52:53 +02:00
|
|
|
#
|
|
|
|
|
# List of file suffixes for Python extensions
|
|
|
|
|
#
|
|
|
|
|
if sys.version_info[0] <= 3 or \
|
|
|
|
|
(sys.version_info[0] == 3 and sys.version_info[1] <= 3):
|
|
|
|
|
|
|
|
|
|
import imp
|
|
|
|
|
extension_suffixes = []
|
|
|
|
|
for ext, mod, typ in imp.get_suffixes():
|
|
|
|
|
if typ == imp.C_EXTENSION:
|
|
|
|
|
extension_suffixes.append(ext)
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
|
|
from importlib import machinery
|
|
|
|
|
extension_suffixes = machinery.EXTENSION_SUFFIXES
|
|
|
|
|
|
2018-03-26 20:25:03 +02:00
|
|
|
# Which types with buffer interface we support (apart from byte strings)
|
|
|
|
|
if sys.version_info[0] == 2 and sys.version_info[1] < 7:
|
|
|
|
|
_buffer_type = (bytearray)
|
|
|
|
|
else:
|
|
|
|
|
_buffer_type = (bytearray, memoryview)
|
2017-05-21 22:52:53 +02:00
|
|
|
|
2015-01-19 14:00:35 +01:00
|
|
|
try:
|
|
|
|
|
from cffi import FFI
|
|
|
|
|
|
|
|
|
|
ffi = FFI()
|
|
|
|
|
null_pointer = ffi.NULL
|
2018-03-06 13:44:02 +01:00
|
|
|
uint8_t_type = ffi.typeof(ffi.new("const uint8_t*"))
|
2015-01-19 14:00:35 +01:00
|
|
|
|
2018-03-30 21:44:18 +02:00
|
|
|
_Array = ffi.new("uint8_t[1]").__class__.__bases__
|
|
|
|
|
|
2015-01-19 14:00:35 +01:00
|
|
|
def load_lib(name, cdecl):
|
|
|
|
|
"""Load a shared library and return a handle to it.
|
|
|
|
|
|
2015-02-01 21:18:05 +01:00
|
|
|
@name, either an absolute path or the name of a library
|
2015-01-19 14:00:35 +01:00
|
|
|
in the system search path.
|
|
|
|
|
|
|
|
|
|
@cdecl, the C function declarations.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
lib = ffi.dlopen(name)
|
|
|
|
|
ffi.cdef(cdecl)
|
|
|
|
|
return lib
|
|
|
|
|
|
|
|
|
|
def c_ulong(x):
|
|
|
|
|
"""Convert a Python integer to unsigned long"""
|
|
|
|
|
return x
|
|
|
|
|
|
2015-02-10 13:35:49 +01:00
|
|
|
c_ulonglong = c_ulong
|
|
|
|
|
|
2015-01-19 14:00:35 +01:00
|
|
|
def c_size_t(x):
|
|
|
|
|
"""Convert a Python integer to size_t"""
|
|
|
|
|
return x
|
|
|
|
|
|
|
|
|
|
def create_string_buffer(size):
|
2016-10-17 07:58:32 +02:00
|
|
|
"""Allocate the given amount of bytes (initially set to 0)"""
|
2016-10-16 22:48:13 +02:00
|
|
|
return ffi.new("uint8_t[]", size)
|
2015-01-19 14:00:35 +01:00
|
|
|
|
|
|
|
|
def get_c_string(c_string):
|
|
|
|
|
"""Convert a C string into a Python byte sequence"""
|
|
|
|
|
return ffi.string(c_string)
|
|
|
|
|
|
|
|
|
|
def get_raw_buffer(buf):
|
|
|
|
|
"""Convert a C buffer into a Python byte sequence"""
|
|
|
|
|
return ffi.buffer(buf)[:]
|
|
|
|
|
|
2018-03-06 13:48:00 +01:00
|
|
|
def c_uint8_ptr(data):
|
2018-03-26 20:25:03 +02:00
|
|
|
if isinstance(data, _buffer_type):
|
2018-03-05 08:10:25 +01:00
|
|
|
# This only works for cffi >= 1.7
|
2018-03-06 13:44:02 +01:00
|
|
|
return ffi.cast(uint8_t_type, ffi.from_buffer(data))
|
2018-03-30 21:44:18 +02:00
|
|
|
elif byte_string(data) or isinstance(data, _Array):
|
2018-03-06 13:44:02 +01:00
|
|
|
return data
|
2018-03-05 08:10:25 +01:00
|
|
|
else:
|
|
|
|
|
raise TypeError("Object type %s cannot be passed to C code" % type(data))
|
|
|
|
|
|
2015-01-19 14:00:35 +01:00
|
|
|
class VoidPointer(object):
|
|
|
|
|
"""Model a newly allocated pointer to void"""
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self._pp = ffi.new("void *[1]")
|
|
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
|
return self._pp[0]
|
|
|
|
|
|
|
|
|
|
def address_of(self):
|
|
|
|
|
return self._pp
|
|
|
|
|
|
2015-02-10 22:29:40 +01:00
|
|
|
backend = "cffi"
|
2015-01-19 14:00:35 +01:00
|
|
|
|
|
|
|
|
except ImportError:
|
2018-03-26 20:25:03 +02:00
|
|
|
|
|
|
|
|
import ctypes
|
2015-02-10 13:35:49 +01:00
|
|
|
from ctypes import (CDLL, c_void_p, byref, c_ulong, c_ulonglong, c_size_t,
|
2018-03-05 08:10:25 +01:00
|
|
|
create_string_buffer, c_ubyte)
|
2015-02-01 21:18:05 +01:00
|
|
|
from ctypes.util import find_library
|
2018-03-26 20:25:03 +02:00
|
|
|
from _ctypes import Array as _Array
|
2015-01-19 14:00:35 +01:00
|
|
|
|
|
|
|
|
null_pointer = None
|
|
|
|
|
|
|
|
|
|
def load_lib(name, cdecl):
|
2015-02-19 21:21:34 +01:00
|
|
|
import platform
|
|
|
|
|
bits, linkage = platform.architecture()
|
|
|
|
|
if "." not in name and not linkage.startswith("Win"):
|
2015-02-01 21:18:05 +01:00
|
|
|
full_name = find_library(name)
|
|
|
|
|
if full_name is None:
|
|
|
|
|
raise OSError("Cannot load library '%s'" % name)
|
|
|
|
|
name = full_name
|
2015-01-19 14:00:35 +01:00
|
|
|
return CDLL(name)
|
|
|
|
|
|
|
|
|
|
def get_c_string(c_string):
|
|
|
|
|
return c_string.value
|
|
|
|
|
|
|
|
|
|
def get_raw_buffer(buf):
|
|
|
|
|
return buf.raw
|
|
|
|
|
|
2018-03-26 20:25:03 +02:00
|
|
|
# ---- Get raw pointer ---
|
|
|
|
|
|
2018-03-30 21:39:13 +02:00
|
|
|
if sys.version_info[0] == 2 and sys.version_info[1] == 6:
|
|
|
|
|
# ctypes in 2.6 does not define c_ssize_t. Replacing it
|
|
|
|
|
# with c_size_t keeps the structure correctely laid out
|
|
|
|
|
_c_ssize_t = c_size_t
|
|
|
|
|
else:
|
|
|
|
|
_c_ssize_t = ctypes.c_ssize_t
|
|
|
|
|
|
2018-03-26 20:25:03 +02:00
|
|
|
_PyBUF_SIMPLE = 0
|
|
|
|
|
_PyObject_GetBuffer = ctypes.pythonapi.PyObject_GetBuffer
|
|
|
|
|
_py_object = ctypes.py_object
|
2018-03-30 21:39:13 +02:00
|
|
|
_c_ssize_p = ctypes.POINTER(_c_ssize_t)
|
2018-03-26 20:25:03 +02:00
|
|
|
|
|
|
|
|
# See Include/object.h for CPython
|
|
|
|
|
# and https://github.com/pallets/click/blob/master/click/_winconsole.py
|
|
|
|
|
class _Py_buffer(ctypes.Structure):
|
|
|
|
|
_fields_ = [
|
|
|
|
|
('buf', c_void_p),
|
|
|
|
|
('obj', ctypes.py_object),
|
2018-03-30 21:39:13 +02:00
|
|
|
('len', _c_ssize_t),
|
|
|
|
|
('itemsize', _c_ssize_t),
|
2018-03-26 20:25:03 +02:00
|
|
|
('readonly', ctypes.c_int),
|
|
|
|
|
('ndim', ctypes.c_int),
|
|
|
|
|
('format', ctypes.c_char_p),
|
|
|
|
|
('shape', _c_ssize_p),
|
|
|
|
|
('strides', _c_ssize_p),
|
|
|
|
|
('suboffsets', _c_ssize_p),
|
|
|
|
|
('internal', c_void_p)
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
# Extra field for CPython 2.6/2.7
|
2018-03-30 21:39:13 +02:00
|
|
|
if sys.version_info[0] == 2 or (sys.version_info[0] == 3 and sys.version_info[1] <= 2):
|
|
|
|
|
_fields_.insert(-1, ('smalltable', _c_ssize_t * 2))
|
2018-03-26 20:25:03 +02:00
|
|
|
|
2018-03-06 13:48:00 +01:00
|
|
|
def c_uint8_ptr(data):
|
2018-03-26 20:25:03 +02:00
|
|
|
if byte_string(data) or isinstance(data, _Array):
|
2018-03-05 08:10:25 +01:00
|
|
|
return data
|
2018-03-26 20:25:03 +02:00
|
|
|
elif isinstance(data, _buffer_type):
|
|
|
|
|
obj = _py_object(data)
|
|
|
|
|
buf = _Py_buffer()
|
|
|
|
|
_PyObject_GetBuffer(obj, byref(buf), _PyBUF_SIMPLE)
|
|
|
|
|
buffer_type = c_ubyte * buf.len
|
|
|
|
|
return buffer_type.from_address(buf.buf)
|
2018-03-05 08:10:25 +01:00
|
|
|
else:
|
|
|
|
|
raise TypeError("Object type %s cannot be passed to C code" % type(data))
|
|
|
|
|
|
2018-03-26 20:25:03 +02:00
|
|
|
# ---
|
|
|
|
|
|
2015-01-19 14:00:35 +01:00
|
|
|
class VoidPointer(object):
|
|
|
|
|
"""Model a newly allocated pointer to void"""
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self._p = c_void_p()
|
|
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
|
return self._p
|
|
|
|
|
|
|
|
|
|
def address_of(self):
|
|
|
|
|
return byref(self._p)
|
|
|
|
|
|
2015-02-10 22:29:40 +01:00
|
|
|
backend = "ctypes"
|
2018-03-26 20:25:03 +02:00
|
|
|
del ctypes
|
2015-01-19 14:00:35 +01:00
|
|
|
|
2017-05-21 22:52:53 +02:00
|
|
|
|
2015-01-19 14:00:35 +01:00
|
|
|
class SmartPointer(object):
|
|
|
|
|
"""Class to hold a non-managed piece of memory"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, raw_pointer, destructor):
|
|
|
|
|
self._raw_pointer = raw_pointer
|
|
|
|
|
self._destructor = destructor
|
|
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
|
return self._raw_pointer
|
|
|
|
|
|
|
|
|
|
def release(self):
|
|
|
|
|
rp, self._raw_pointer = self._raw_pointer, None
|
|
|
|
|
return rp
|
|
|
|
|
|
|
|
|
|
def __del__(self):
|
2016-11-02 22:30:39 +01:00
|
|
|
try:
|
|
|
|
|
if self._raw_pointer is not None:
|
|
|
|
|
self._destructor(self._raw_pointer)
|
|
|
|
|
self._raw_pointer = None
|
|
|
|
|
except AttributeError:
|
|
|
|
|
pass
|
2015-01-19 14:00:35 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def load_pycryptodome_raw_lib(name, cdecl):
|
|
|
|
|
"""Load a shared library and return a handle to it.
|
|
|
|
|
|
|
|
|
|
@name, the name of the library expressed as a PyCryptodome module,
|
|
|
|
|
for instance Crypto.Cipher._raw_cbc.
|
|
|
|
|
|
|
|
|
|
@cdecl, the C function declarations.
|
|
|
|
|
"""
|
|
|
|
|
|
2016-01-03 16:02:56 -05:00
|
|
|
split = name.split(".")
|
|
|
|
|
dir_comps, basename = split[:-1], split[-1]
|
2018-01-24 23:21:15 +01:00
|
|
|
attempts = []
|
2017-05-21 22:52:53 +02:00
|
|
|
for ext in extension_suffixes:
|
|
|
|
|
try:
|
2018-01-24 23:21:15 +01:00
|
|
|
filename = basename + ext
|
|
|
|
|
return load_lib(pycryptodome_filename(dir_comps, filename),
|
2017-05-21 22:52:53 +02:00
|
|
|
cdecl)
|
2018-01-24 23:21:15 +01:00
|
|
|
except OSError, exp:
|
|
|
|
|
attempts.append("Trying '%s': %s" % (filename, str(exp)))
|
|
|
|
|
raise OSError("Cannot load native module '%s': %s" % (name, ", ".join(attempts)))
|
2015-02-10 22:29:40 +01:00
|
|
|
|
|
|
|
|
def expect_byte_string(data):
|
2018-03-05 08:10:25 +01:00
|
|
|
raise NotImplementedError("To be removed")
|