cryptlib/cryptlibConverter.py
2017-08-02 20:22:28 +02:00

2452 lines
89 KiB
Python

import sys
import os
import stat
import re
#Helper Functions
#==========================================================================
def parseEnumContents(enumContents, nameSpace, debugString):
enumPattern = re.compile(r"(CRYPT_[\w_]*)\s*(=.*?)?(\Z|,|(?=/))", re.DOTALL) # %1 [= %2][,]
commentPattern = re.compile(r"\s*/\*(.*)\*/") # /* %1 */
counter = 0 #Keep track of the implicit enum value
enumTuples = [] #Build a list of (key, val, comment) tuples, one for each enum value
#Find the next "enum value"
enumMatch = enumPattern.search(enumContents)
while enumMatch:
#Extract the key and RHS value
rawKey, rawVal = enumMatch.groups()[:-1]
key = rawKey.rstrip()
if not key.startswith("CRYPT_"):
raise "enum'd value doesn't start with CRYPT_ "+key
key = key.replace("CRYPT_", "")
#Evaluate the RHS (if it exists) to get the value
if rawVal:
rhs = rawVal[1:].strip().replace("CRYPT_", "")
val = eval(rhs, nameSpace)
#Otherwise the value is the implicit counter
else:
val = counter
#Extract the comment
commentMatch = commentPattern.match(enumContents, enumMatch.end())
if commentMatch:
rawComment = commentMatch.group(1)
comment = rawComment.strip()
else:
comment = ""
#Collect all the parsed values into a tuple
enumTuple = (key, str(val), comment)
#if comment:
# print debugString+ ":" + " Parsed Element %s = %s /* %s */" % enumTuple
#else:
# print debugString+ ":" + " Parsed Element %s = %s" % enumTuple[:-1]
#raw_input()
nameSpace[key] = val #Add new enum value into namespace
counter = val + 1 #Increment implicit enum value
#Accumulate the parsed (key, val, comment) tuples
enumTuples.append(enumTuple)
#Get next enum value
enumMatch = enumPattern.search(enumContents, enumMatch.end())
return enumTuples
#Parses a string of function parameters into a list of ParamStruct
class ParamStruct:
def __init__(self):
self.type = ""
self.isPtr = 0
self.isOut = 0
self.category = ""
self.name = ""
self.rawIndex = 0 # The index of this param in the initial, raw cryptlib header
def __str__(self):
return str( (self.type, self.isPtr, self.category, self.name, self.rawIndex) )
def parseFunctionParams(functionParams):
paramStructs = []
functionParams = functionParams.replace("\n", "") #Make one big line
functionParamsList = [e.strip() for e in functionParams.split(",")] #Break into a list of params
index = 0
for e in functionParamsList:
pieces = e.split(" ") #Separate each param into component pieces, ie ["C_IN", "CRYPT_ALGO_TYPE", "cryptAlgo"]
p = ParamStruct()
if len(pieces)==1 and pieces[0]=="void":
continue
elif len(pieces)==3:
if pieces[0] == "C_OUT":
p.isOut = 1
if pieces[0] == "C_OUT_OPT":
p.isOut = 1
if pieces[1] == "C_STR":
p.type = "char"
p.name = pieces[2]
p.isPtr = 1
else:
p.type = pieces[1]
p.name = pieces[2]
p.isPtr = 0
elif len(pieces)==4: #Ie ["C_OUT", "CRYPT_CONTEXT", "C_PTR", "cryptContext"]
if pieces[0] == "C_OUT":
p.isOut = 1
if pieces[0] == "C_OUT_OPT":
p.isOut = 1
p.type = pieces[1]
if pieces[2] == "C_PTR":
p.isPtr = 1
else:
raise "Expecting C_PTR in parseFunctionParams"
p.name = pieces[3]
else:
raise "parsing error in parseFunctionParams"
if p.type in enumTypes:
p.category = "enumType"
elif p.type in intTypes:
p.category = "intType"
elif p.type in structTypes:
p.category = "structType"
elif p.type in rawTypes:
p.category = "rawType"
else:
raise "unknown type error is parseFunctionParams"
p.rawIndex = index
index += 1
paramStructs.append(p)
#for p in paramStructs:
# print p.__dict__
#raw_input()
return paramStructs
#Takes a string containing a JNI function parameters prototype list, and a list of ParamStructs
#And injects names into the string and returns it
def expandFunctionPrototype(functionPrototype, newParamStructs):
functionPrototype = functionPrototype.replace("\n", "") #Make one big line
paramPrototypesList = [e.strip() for e in functionPrototype.split(",")] #Break into a list of params
paramNamesList = ["env", "cryptClass"] + [e.name for e in newParamStructs]
newFunctionParams = ""
for (p1, p2) in zip(paramPrototypesList, paramNamesList):
newFunctionParams += p1 + " " + p2 + ", "
newFunctionParams = newFunctionParams[:-2]
return newFunctionParams
#Main
#=========================================================================
#Execution starts here...
#=========================================================================
if len(sys.argv) != 4:
print "cryptlibConverter.py <inFile> <outDir> <language>"
sys.exit()
inFile = sys.argv[1]
outDir = sys.argv[2]
language = sys.argv[3]
if not os.path.exists(outDir):
print "Making output directory..."
os.mkdir(outDir)
if not language in ("java", "python", "net"):
print "only java, python, and net are supported!"
sys.exit()
if language == "java":
#ENUM IDIOM
#typedefEnumTemplate = "public static class %(typedefName)s\n{public int getValue(){return m_value;}private %(typedefName)s(int value){m_value = value;}int m_value;}"
#typedefEnumElementTemplate = "public static final %(typedefName)s %(name)-NPADs = new %(typedefName)s(%(value)-VPADs);"
#ENUMs as ints
typedefEnumTemplate = "// %(typedefName)s"
typedefEnumElementTemplate = "public static final int %(name)-NPADs = %(value)-VPADs;"
typedefEnumElementTemplateComment = typedefEnumElementTemplate + " // %(comment)s"
simpleEnumElementTemplate = "public static final int %(name)-NPADs = %(value)-VPADs;"
simpleEnumElementTemplateComment = simpleEnumElementTemplate + " // %(comment)s"
defineNPad = "40"
defineVPad = "4"
defineTemplate = simpleEnumElementTemplate
defineTemplateComment = simpleEnumElementTemplateComment
exceptionPrefix = """
package cryptlib;
public class CryptException extends Exception
{
private int m_status;
private String m_message;
public CryptException(int status)
{
m_status = status;
String prefix = Integer.toString(status) + ": ";
"""
exceptionPostfix = """\
m_message = prefix + "Unknown Exception ?!?!";
}
public int getStatus()
{
return m_status;
}
public String getMessage()
{
return m_message;
}
};"""
exceptionTemplate = """\
if (m_status == crypt.%(name)s) {
m_message = prefix + "%(comment)s";
return; }
"""
cryptQueryInfoString = """
package cryptlib;
public class CRYPT_QUERY_INFO
{
public String algoName;
public int blockSize;
public int minKeySize;
public int keySize;
public int maxKeySize;
public CRYPT_QUERY_INFO(String newAlgoName, int newBlockSize, int newMinKeySize, int newKeySize, int newMaxKeySize)
{
algoName = newAlgoName;
blockSize = newBlockSize;
minKeySize = newMinKeySize;
keySize = newKeySize;
maxKeySize = newMaxKeySize;
}
};"""
cryptObjectInfoString = """
package cryptlib;
public class CRYPT_OBJECT_INFO
{
public int objectType;
public int cryptAlgo;
public int cryptMode;
public int hashAlgo;
public byte[] salt;
public CRYPT_OBJECT_INFO(int newObjectType, int newCryptAlgo, int newCryptMode, int newHashAlgo, byte[] newSalt)
{
objectType = newObjectType;
cryptAlgo = newCryptAlgo;
cryptMode = newCryptMode;
hashAlgo = newHashAlgo;
salt = newSalt;
}
};"""
addFunctionWrappers = 1
wholeFunctionDeclaration = None
functionDeclaration = "public static native "
returnIntDeclaration = "int "
returnVoidDeclaration = "void "
paramsPrefix = "("
paramsPostfix = ") throws CryptException;"
paramWhiteSpace = "\t\t\t\t\t\t"
paramVoidPtrTemplate = "java.nio.ByteBuffer %(name)s,\n"
addFunctionAlternate = 1
paramVoidPtrAlternate = ("java.nio.ByteBuffer", "byte[]")
paramCharPtrTemplate = "String %(name)s,\n"
paramIntTemplate = "int %(name)s,\n"
paramIntTypeTemplate = "int %(name)s, // %(type)s\n"
#wrapperLengthTemplate = "%s.capacity(), "
#wrapperStringLengthTemplate = "%s.length(), "
wrapperLengthTemplate = "%(1)s == null ? 0 : %(1)s.capacity(), "
wrapperStringLengthTemplate = "%(1)s == null ? 0 : %(1)s.getBytes().length, "
wrapperStringReplace = ("java.nio.ByteBuffer", "String")
wrapperStringTemplate = '%(1)s == null ? null : %(1)s.getBytes()'
#ENUM IDIOM
#paramEnumTypeTemplate = "%(type)s %(name)s,\n"
#ENUMs as ints
paramEnumTypeTemplate = "int %(name)s, // %(type)s\n"
commentPrefix = "//"
classPrefix = "package cryptlib;\n\nimport java.nio.*;\n\npublic class crypt\n{\n"
classPostfix = "\n};"
sFuncs = None
sInts = None
elif language == "python":
typedefEnumTemplate = ""
#typedefEnumElementTemplate = "%(name)-NPADs = %(value)-VPADs"
typedefEnumElementTemplate = """\
v = Py_BuildValue("i", CRYPT_%(name)s);
PyDict_SetItemString(moduleDict, "CRYPT_%(name)s", v);
Py_DECREF(v);"""
typedefEnumElementTemplateComment = typedefEnumElementTemplate + " /* %(comment)s */"
simpleEnumElementTemplate = typedefEnumElementTemplate
simpleEnumElementTemplateComment = typedefEnumElementTemplateComment
defineNPad = "40"
defineVPad = "4"
defineTemplate = typedefEnumElementTemplate
defineTemplateComment = typedefEnumElementTemplateComment
exceptionPrefix = ""
exceptionPostfix = ""
exceptionTemplate = """\
else if (status == CRYPT_%(name)s)
o = Py_BuildValue("(is)", CRYPT_%(name)s, "%(comment)s");
"""
addFunctionWrappers = 0
wholeFunctionDeclaration = """\
static PyObject* python_crypt%s(PyObject* self, PyObject* args)
{
}
"""
moduleFunctionEntry = "\t{ \"crypt%s\", python_crypt%s, METH_VARARGS }, "
pyBeforeExceptions = r"""
#include <Python.h>
#include "../cryptlib.h"
static PyObject* cryptHandleClass;
static PyObject* cryptQueryInfoClass;
static PyObject* cryptObjectInfoClass;
static PyObject *CryptException;
static int getPointerWrite(PyObject* objPtr, unsigned char** bytesPtrPtr, int* lengthPtr)
{
if (objPtr == Py_None)
{
*bytesPtrPtr = NULL;
*lengthPtr = 0;
return 1;
}
Py_ssize_t size = 0;
/*See if it's an array object*/
if (PyObject_AsWriteBuffer(objPtr, (void **)bytesPtrPtr, &size) == -1)
return 0;
*lengthPtr = size;
return 1;
}
static int getPointerRead(PyObject* objPtr, unsigned char** bytesPtrPtr, int* lengthPtr)
{
if (objPtr == Py_None)
{
*bytesPtrPtr = NULL;
*lengthPtr = 0;
return 1;
}
Py_ssize_t size = 0;
/*See if it's an array object*/
if (PyObject_AsWriteBuffer(objPtr, (void **)bytesPtrPtr, &size) == -1)
{
PyErr_Clear();
/*See if it's a string object*/
/*This returns the length excluding the NULL if it's a string,
which is what we want*/
if (PyObject_AsCharBuffer(objPtr, (const char **)bytesPtrPtr, &size) == -1)
return 0;
}
*lengthPtr = size;
return 1;
}
static int getPointerReadNoLength(PyObject* objPtr, unsigned char** bytesPtrPtr)
{
int length;
return getPointerRead(objPtr, bytesPtrPtr, &length);
}
static int getPointerWriteCheckIndices(PyObject* objPtr, unsigned char** bytesPtrPtr, int* lengthPtr)
{
int checkLength = *lengthPtr;
if (getPointerWrite(objPtr, bytesPtrPtr, lengthPtr) == 0)
return 0;
//If sequence is non-NULL and too short...
if (*bytesPtrPtr && (*lengthPtr < checkLength))
{
PyErr_SetString(PyExc_IndexError, "A sequence passed to cryptlib was too small");
return 0;
}
return 1;
}
static int getPointerReadString(PyObject* objPtr, char** charPtrPtr)
{
Py_ssize_t length = 0;
char* newPtr = NULL;
if (objPtr == Py_None)
{
*charPtrPtr = NULL;
return 1;
}
/*See if it's a string or a buffer object*/
if (PyObject_AsCharBuffer(objPtr, charPtrPtr, &length) == -1)
{
/*See if it's an array*/
PyErr_Clear();
if (PyObject_AsWriteBuffer(objPtr, charPtrPtr, &length) == -1)
return 0;
}
/*This code isn't necessary for a string, but it is for arrays and buffers,
so we do it always anyway, since the PyObject_AsCharBuffer apparently doesn't
guarantee us null-terminated data, and this way releasePointerString() doesn't
have to differentiate */
newPtr = malloc(length+1);
if (newPtr == NULL)
{
PyErr_NoMemory();
return 0;
}
memcpy(newPtr, *charPtrPtr, length);
newPtr[length] = 0;
*charPtrPtr = newPtr;
return 1;
}
static void releasePointer(PyObject* objPtr, unsigned char* bytesPtr)
{
}
static void releasePointerString(PyObject* objPtr, char* charPtr)
{
free(charPtr);
}
static PyObject* processStatus(int status)
{
PyObject* o = NULL;
/* If an error has already occurred, ignore the status and just fall through */
if (PyErr_Occurred())
return(NULL);
if (status >= CRYPT_OK)
return(Py_BuildValue(""));
"""
pyBeforeFuncs = """\
PyErr_SetObject(CryptException, o);
Py_DECREF(o);
return(NULL);
}
static int processStatusBool(int status)
{
PyObject* o = processStatus(status);
if (o == NULL)
return(0);
else
{
Py_DECREF(o);
return(1);
}
}
static PyObject* processStatusReturnInt(int status, int returnValue)
{
PyObject* o = processStatus(status);
if (o == NULL)
return(0);
else
{
Py_DECREF(o);
o = Py_BuildValue("i", returnValue);
return(o);
}
}
static PyObject* processStatusReturnCryptHandle(int status, int returnValue)
{
PyObject* o2;
PyObject* o = processStatus(status);
if (o == NULL)
return(0);
else
{
Py_DECREF(o);
o2 = Py_BuildValue("(i)", returnValue);
o = PyObject_CallObject(cryptHandleClass, o2);
Py_DECREF(o2);
return(o);
}
}
static PyObject* processStatusReturnCryptQueryInfo(int status, CRYPT_QUERY_INFO returnValue)
{
PyObject* o2;
PyObject* o = processStatus(status);
if (o == NULL)
return(0);
else
{
Py_DECREF(o);
o2 = Py_BuildValue("(siiii)", returnValue.algoName, returnValue.blockSize, returnValue.minKeySize, returnValue.keySize, returnValue.maxKeySize);
o = PyObject_CallObject(cryptQueryInfoClass, o2);
Py_DECREF(o2);
return(o);
}
}
static PyObject* processStatusReturnCryptObjectInfo(int status, CRYPT_OBJECT_INFO returnValue)
{
PyObject* o2;
PyObject* o = processStatus(status);
if (o == NULL)
return(0);
else
{
Py_DECREF(o);
o2 = Py_BuildValue("(iiiis#)", returnValue.objectType, returnValue.cryptAlgo, returnValue.cryptMode, returnValue.hashAlgo, returnValue.salt, returnValue.saltSize);
o = PyObject_CallObject(cryptObjectInfoClass, o2);
Py_DECREF(o2);
return(o);
}
}
"""
pyBeforeModuleFunctions = """
static PyMethodDef module_functions[] =
{
"""
pyBeforeInts = r""" {0, 0}
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"cryptlib_py",
NULL,
-1,
module_functions,
NULL,
NULL,
NULL,
NULL
};
#endif
PyMODINIT_FUNC
#if PY_MAJOR_VERSION >= 3
PyInit_cryptlib_py(void)
#else
initcryptlib_py(void)
#endif
{
PyObject* module;
#if PY_MAJOR_VERSION >= 3
module = PyModule_Create(&moduledef);
if (module == NULL){
return NULL;
}
#else
module = Py_InitModule("cryptlib_py", module_functions);
#endif
PyObject* moduleDict;
PyObject* v = NULL;
PyObject *globalsDict;
moduleDict = PyModule_GetDict(module);
CryptException = PyErr_NewException("cryptlib_py.CryptException", NULL, NULL);
PyDict_SetItemString(moduleDict, "CryptException", CryptException);
globalsDict = PyEval_GetGlobals();
PyRun_String(
"from array import *\n\
import types\n\
class CryptHandle:\n\
def __init__(self, value):\n\
self.__dict__['value'] = value\n\
def __int__(self):\n\
return self.value\n\
def __str__(self):\n\
return str(self.value)\n\
def __repr__(self):\n\
return str(self.value)\n\
def __getattr__(self, name):\n\
name = name.upper()\n\
if not name.startswith('CRYPT_'):\n\
name = 'CRYPT_' + name\n\
nameVal = globals()[name]\n\
try:\n\
return cryptGetAttribute(self, nameVal)\n\
except CryptException:\n\
length = cryptGetAttributeString(self, nameVal, None)\n\
buf = array('c', '0' * length)\n\
length = cryptGetAttributeString(self, nameVal, buf)\n\
return ''.join(buf[:length])\n\
def __setattr__(self, name, value):\n\
name = name.upper()\n\
if not name.startswith('CRYPT_'):\n\
name = 'CRYPT_' + name\n\
nameVal = globals()[name]\n\
if isinstance(value, types.IntType) or isinstance(value, CryptHandle):\n\
cryptSetAttribute(self, nameVal, value)\n\
else:\n\
cryptSetAttributeString(self, nameVal, value)\n",
Py_file_input, globalsDict, moduleDict);
PyRun_String(
"class CRYPT_QUERY_INFO:\n\
def __init__(self, algoName, blockSize, minKeySize, keySize, maxKeySize):\n\
self.algoName = algoName\n\
self.blockSize = blockSize\n\
self.minKeySize = minKeySize\n\
self.keySize = keySize\n\
self.maxKeySize = maxKeySize\n",
Py_file_input, globalsDict, moduleDict);
PyRun_String(
"class CRYPT_OBJECT_INFO:\n\
def __init__(self, objectType, cryptAlgo, cryptMode, hashAlgo, salt):\n\
self.objectType = objectType\n\
self.cryptAlgo = cryptAlgo\n\
self.cryptMode = cryptMode\n\
self.hashAlgo = hashAlgo\n\
self.salt = salt\n",
Py_file_input, globalsDict, moduleDict);
cryptHandleClass = PyMapping_GetItemString(moduleDict, "CryptHandle");
cryptQueryInfoClass = PyMapping_GetItemString(moduleDict, "CRYPT_QUERY_INFO");
cryptObjectInfoClass = PyMapping_GetItemString(moduleDict, "CRYPT_OBJECT_INFO");
Py_DECREF(globalsDict);
"""
pyAfterInts = """
#if PY_MAJOR_VERSION >= 3
return module;
#endif
}"""
paramCharPtrTemplate = "%(name)s, # string\n"
paramIntTemplate = "%(name)s, # integer\n"
paramIntTypeTemplate = "%(name)s, # %(type)s\n"
paramEnumTypeTemplate = "%(name)s, # %(type)s\n"
commentPrefix = "# " #pad to 2-characters for formatting consistency
classPrefix = "class crypt:\n"
classPostfix= ""
outFile = os.path.join(outDir, "cryptlib.py")
sFuncs = "\n"
sInts = "\n"
sModFuncs = ""
setupPy = r"""#!/usr/bin/env python
from distutils.core import setup, Extension
import sys
if sys.platform == "win32":
ext = Extension("cryptlib_py",
sources=["python.c"],
library_dirs=['../binaries'],
libraries=['cl32'])
else:
ext = Extension("cryptlib_py",
sources=["python.c"],
library_dirs=['..'],
libraries=['cl'])
setup(name="cryptlib_py", ext_modules=[ext])
"""
elif language == "net":
typedefEnumTemplate = "// %(typedefName)s"
typedefEnumElementTemplate = "public const int %(name)-NPADs = %(value)-VPADs;"
typedefEnumElementTemplateComment = typedefEnumElementTemplate + " // %(comment)s"
simpleEnumElementTemplate = "public const int %(name)-NPADs = %(value)-VPADs;"
simpleEnumElementTemplateComment = simpleEnumElementTemplate + " // %(comment)s"
defineNPad = "40"
defineVPad = "4"
defineTemplate = simpleEnumElementTemplate
defineTemplateComment = simpleEnumElementTemplateComment
exceptionPrefix = """
[StructLayout(LayoutKind.Sequential, Pack=0, CharSet=CharSet.Ansi)]
public class CRYPT_QUERY_INFO
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=64)]public String algoName;
public int blockSize;
public int minKeySize;
public int keySize;
public int maxKeySize;
public CRYPT_QUERY_INFO(){}
public CRYPT_QUERY_INFO(String newAlgoName, int newBlockSize, int newMinKeySize, int newKeySize, int newMaxKeySize)
{
algoName = newAlgoName;
blockSize = newBlockSize;
minKeySize = newMinKeySize;
keySize = newKeySize;
maxKeySize = newMaxKeySize;
}
}
[StructLayout(LayoutKind.Sequential, Pack=0, CharSet=CharSet.Ansi)]
public class CRYPT_OBJECT_INFO
{
public int objectType;
public int cryptAlgo;
public int cryptMode;
public int hashAlgo;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=32)]public byte[] salt;
public int saltSize;
public CRYPT_OBJECT_INFO()
{
salt = new byte[64];
saltSize = 64;
}
public CRYPT_OBJECT_INFO(int newObjectType, int newCryptAlgo, int newCryptMode, int newHashAlgo, byte[] newSalt)
{
objectType = newObjectType;
cryptAlgo = newCryptAlgo;
cryptMode = newCryptMode;
hashAlgo = newHashAlgo;
}
}
public class CryptException : ApplicationException
{
public int Status { get { return (int)Data["Status"]; } }
public int ExtraInfo { get { return (int)Data["ExtraInfo"]; } }
public CryptException(int status)
: base(convertMessage(status))
{
Data.Add("Status", status);
}
public CryptException(int status, int extra)
: base(convertMessage(status))
{
Data.Add("Status", status);
Data.Add("ExtraInfo", extra);
}
private static string convertMessage(int status)
{
String prefix = Convert.ToString(status) + ": ";
switch (status)
{
"""
exceptionPostfix = """\
default: return prefix + "Unknown Exception ?!?!";
}
}
}"""
exceptionTemplate = """\
case crypt.%(name)s: return prefix + "%(comment)s";
"""
addFunctionWrappers = 1
wholeFunctionDeclaration = None
functionDeclaration = "public static "
returnIntDeclaration = "int "
returnVoidDeclaration = "void "
paramsPrefix = "("
paramsPostfix = ");"
paramWhiteSpace = "\t\t\t\t\t\t"
paramVoidPtrTemplate = "byte[] %(name)s,\n"
addFunctionAlternate = 0
paramCharPtrTemplate = "String %(name)s,\n"
paramIntTemplate = "int %(name)s,\n"
paramIntTypeTemplate = "int %(name)s, // %(type)s\n"
wrapperLengthTemplate = "%(1)s == null ? 0 : %(1)s.Length, "
wrapperStringLengthTemplate = "%(1)s == null ? 0 : new UTF8Encoding().GetByteCount(%(1)s), "
wrapperStringReplace = ("byte[]", "String")
wrapperStringTemplate = "%(1)s == null ? null : new UTF8Encoding().GetBytes(%(1)s)"
#ENUM IDIOM
#paramEnumTypeTemplate = "%(type)s %(name)s,\n"
#ENUMs as ints
paramEnumTypeTemplate = "int %(name)s, // %(type)s\n"
commentPrefix = "//"
classPrefix = """using System;
using System.Runtime.InteropServices;
using System.Text;
namespace cryptlib
{
public class crypt
{
"""
classPostfix = """
/* Helper Functions */
private static void processStatus(int status)
{
if (status < crypt.OK)
throw new CryptException(status);
}
private static void processStatus(int status, int extraInfo)
{
if (status < crypt.OK)
throw new CryptException(status, extraInfo);
}
private static void checkIndices(byte[] array, int sequenceOffset, int sequenceLength)
{
if (array == null)
{
if (sequenceOffset == 0)
return;
else
throw new IndexOutOfRangeException();
}
int arrayLength = array.Length;
if (sequenceOffset < 0 ||
sequenceOffset >= arrayLength ||
sequenceOffset + sequenceLength > arrayLength)
throw new IndexOutOfRangeException();
}
private static void getPointer(byte[] buffer, int bufferOffset, ref GCHandle bufferHandle, ref IntPtr bufferPtr)
{
if (buffer == null)
return;
bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
bufferPtr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, bufferOffset);
}
private static void releasePointer(GCHandle bufferHandle)
{
if (bufferHandle.IsAllocated)
bufferHandle.Free();
}
}
"""
sFuncs = None
sInts = None
s = open(inFile).read()
inFileTabSize = 4
#Global variables
#These accumulate information about the types in the input file
#----------------------------------------------------------------
nameSpace = {} #Accumulate enum'd values here and use them to eval() the RHS of new ones
enumTypes = [] #Accumulate typedef'd enum types here
intTypes = [] #Accumulate typedef'd int types here
structTypes = []#Accumulate typedef'd struct types here
rawTypes = ["char", "int", "void"]
functionNames = [] #Accumulate function names here
rawParamStructsDict = {} #Accumulate function name -> list of ParamStructs (of original c code)
newParamStructsDict = {} #Accumulate function name -> list of ParamStructs (of new java/python code)
newReturnStructsDict = {} #Accumulate function name -> ParamStruct of return parameter (of new java/python code)
newDiscardedStructsDict = {} #Accumulate function name -> ParamStruct of discarded parameter
lengthIndicesDict = {} #Accumulate function name -> indices in newParamStructs of lengths (used for python)
offsetIndicesDict= {} #Accumulate function name -> indices in newParamStructs of offsets (used for python)
errors = {} #Dictionary mapping return values to exception objects
print "Parsing input file and generating %s files..." % language
#Removing enclosing include guard
#---------------------------------
s = re.search(r"#ifndef _CRYPTLIB_DEFINED\n(.*)\n#endif", s, re.DOTALL).group(1)
#Ignore anything before "#define C_INOUT..."
#--------------------------------------------
s = s[re.search(r"#define C_INOUT.*?\n", s, re.DOTALL).end() : ]
#Remove all conditionals
#------------------------
while 1:
endifMatch = re.search(r"#endif.*?\n", s, re.DOTALL)
if endifMatch == None:
break
ifdefIndex = s.rfind("#if", 0, endifMatch.start())
s = s[ : ifdefIndex] + s[endifMatch.end() : ]
s = re.sub(r'\n\s*/\*.*\*/\s*\n', lambda match: '' if match.group(0).find('CRYPT_') >= 0 else match.group(0), s)
#Delete lines used for extended function and function-parameter checking
#like C_CHECK_RETVAL C_NONNULL_ARG( ( 3 ) ) \
#--------------------------------------------
functionParamterPattern = re.compile(r"((C_CHECK_RETVAL|C_NONNULL_ARG\s*\(\s*\([ \t0-9,]+\s*\)\s*\))\s+(\\\n)?)", re.DOTALL)
while 1:
deleteExtended = functionParamterPattern.search(s)
if deleteExtended == None:
break
s = s[ : deleteExtended.start(1) ] + s[ deleteExtended.end(1) : ]
#Replace typedef enums
#----------------------
typedefEnumPattern = re.compile(r"typedef[ \t]+enum[ \t]+{([^}]*)}\s*(\w*);", re.DOTALL) # typedef enum { %1 } %2;
#Find the next typedef enum
typedefEnumMatch = typedefEnumPattern.search(s)
while typedefEnumMatch:
#Extract its contents and type name
enumContents, typedefName = typedefEnumMatch.groups()
enumTypes.append(typedefName)
#Parse its contents
enumTuples = parseEnumContents(enumContents, nameSpace, "typedef")
#Determine the length to pad names to
namePad = str( max( [len(e[0]) for e in enumTuples] ) )
valuePad = str( max( [len(e[1]) for e in enumTuples] ) )
#Construct its output equivalent from language-specific string templates
newTypedefEnum = typedefEnumTemplate % vars()
for enumTuple in enumTuples:
name, value, comment = enumTuple
if not comment:
paddedTemplate = typedefEnumElementTemplate.replace("NPAD", namePad).replace("VPAD", valuePad)
newEnum = paddedTemplate % vars()
else:
paddedTemplate = typedefEnumElementTemplateComment.replace("NPAD", namePad).replace("VPAD", valuePad)
newEnum = paddedTemplate % vars()
if newTypedefEnum:
newTypedefEnum += "\n" #Don't always add this or we'll get extraneous newlines in python
newTypedefEnum += newEnum
#print "Output Equivalent of Typedef Enum====\n", newTypedefEnum
#raw_input()
if sInts:
sInts += newTypedefEnum + "\n"
#Substitute the output equivalent for the input
s = s[ : typedefEnumMatch.start()] + newTypedefEnum + s[typedefEnumMatch.end() : ]
#Get next typedef
typedefEnumMatch = typedefEnumPattern.search(s, typedefEnumMatch.start() + len(newTypedefEnum))
#print "ENUMTYPES:\n",enumTypes
#Replace simple enums
#---------------------
#This works on the theory that we've already replaced all typedef enums with
#something that doesn't use the word "enum", so any remaining occurrences of
#enum will belong to simple enums.
simpleEnumPattern = re.compile(r"enum[ \t]+{([^}]*)};", re.DOTALL) # enum { %1 };
#Find the next simple enum
simpleEnumMatch = simpleEnumPattern.search(s)
while simpleEnumMatch:
#Extract its contents
enumContents = simpleEnumMatch.group(1)
#Parse its contents
enumTuples = parseEnumContents(enumContents, nameSpace, "simple")
#Determine the length to pad names to
namePad = str( max( [len(e[0]) for e in enumTuples] ) )
valuePad = str( max( [len(e[1]) for e in enumTuples] ) )
#Construct its output equivalent from language-specific string templates
newSimpleEnum = ""
for enumTuple in enumTuples:
name, value, comment = enumTuple
if not comment:
paddedTemplate = simpleEnumElementTemplate.replace("NPAD", namePad).replace("VPAD", valuePad)
newEnum = paddedTemplate % vars()
else:
paddedTemplate = simpleEnumElementTemplateComment.replace("NPAD", namePad).replace("VPAD", valuePad)
newEnum = paddedTemplate % vars()
if newSimpleEnum:
newSimpleEnum += "\n" #Don't always add this or we'll get extraneous newlines in python
newSimpleEnum += newEnum
#print "Output Equivalent of Simple Enum====\n", newSimpleEnum
#raw_input()
if sInts:
sInts += newSimpleEnum + "\n"
#Substitute the output equivalent for the input
s = s[ : simpleEnumMatch.start()] + newSimpleEnum + s[simpleEnumMatch.end() : ]
#Get next typedef
simpleEnumMatch = simpleEnumPattern.search(s, simpleEnumMatch.start() + len(newSimpleEnum))
#Replace #define'd constants
#----------------------------
definePattern = re.compile(r"#define[ \t]+(\w+)[ \t]+([-\w]+)[ \t]*(/\*.*\*/*)?") # #define %1 %2 [/*%3*/]
exceptionString = exceptionPrefix
#Find the next #define
defineMatch = definePattern.search(s)
while defineMatch:
#Parse its contents
name, value, comment = defineMatch.groups()
if not name.startswith("CRYPT_"):
raise "name doesn't start with CRYPT_"+name
name = name.replace("CRYPT_", "")
#Construct its output equivalent from language-specific string templates
if not comment:
paddedTemplate = defineTemplate.replace("NPAD", defineNPad).replace("VPAD", defineVPad)
newDefine = paddedTemplate % vars()
else:
comment = comment[2:-2].strip()
paddedTemplate = defineTemplateComment.replace("NPAD", defineNPad).replace("VPAD", defineVPad)
newDefine = paddedTemplate % vars()
#print "define: " + newDefine
#raw_input()
if sInts:
sInts += newDefine + "\n"
#Substitute the output equivalent for the input
s = s[ : defineMatch.start()] + newDefine + s[defineMatch.end() : ]
#Append to exception string if error
if name.startswith("ERROR_") or name.startswith("ENVELOPE_RESOURCE"):
exceptionString += exceptionTemplate % vars()
#Get next #define
defineMatch = definePattern.search(s, defineMatch.start() + len(newDefine))
exceptionString += exceptionPostfix
#Replace #define'd constants with parenthesis
#--------------------------------------------
definePattern = re.compile(r"#define[ \t]+(\w+)[ \t]+\([ \t]*([-0-9]+)[ \)]*\)[ \t]*(/\*.*\*/*)?") # #define %1 ( %2 ) [/*%3*/]
exceptionString = exceptionPrefix
#Find the next #define
defineMatch = definePattern.search(s)
while defineMatch:
#Parse its contents
name, value, comment = defineMatch.groups()
if not name.startswith("CRYPT_"):
raise "name doesn't start with CRYPT_"+name
name = name.replace("CRYPT_", "")
#Construct its output equivalent from language-specific string templates
if not comment:
paddedTemplate = defineTemplate.replace("NPAD", defineNPad).replace("VPAD", defineVPad)
newDefine = paddedTemplate % vars()
else:
comment = comment[2:-2].strip()
paddedTemplate = defineTemplateComment.replace("NPAD", defineNPad).replace("VPAD", defineVPad)
newDefine = paddedTemplate % vars()
#print "define: " + newDefine
#raw_input()
if sInts:
sInts += newDefine + "\n"
#Substitute the output equivalent for the input
s = s[ : defineMatch.start()] + newDefine + s[defineMatch.end() : ]
#Append to exception string if error
if name.startswith("ERROR_") or name.startswith("ENVELOPE_RESOURCE"):
exceptionString += exceptionTemplate % vars()
#Get next #define
defineMatch = definePattern.search(s, defineMatch.start() + len(newDefine))
exceptionString += exceptionPostfix
#Comment out #define'd macros
#-----------------------------
definePattern = re.compile(r"#define[ \t]+[^(]+\([^\)]*\)([^\n]*\\\n)*[^\n]*")
defineMatch = definePattern.search(s)
while defineMatch:
#print "defined macros:", defineMatch.group()
#raw_input()
define = defineMatch.group()
newDefine = commentPrefix + "CRYPTLIBCONVERTER - NOT SUPPORTED:\n" + define
newDefine = newDefine.replace("\n", "\n"+commentPrefix)
s = s[ : defineMatch.start()] + newDefine + s[defineMatch.end() : ]
defineMatch = definePattern.search(s, defineMatch.start() + len(newDefine))
#Comment out typedef integer types
#----------------------------------
typedefIntPattern = re.compile(r"typedef[ \t]+int[ \t]+([^ \t]*)[ \t]*;[ \t]*\n")
typedefIntMatch = typedefIntPattern.search(s)
while typedefIntMatch:
typedefInt = typedefIntMatch.group()
typedefIntName = typedefIntMatch.group(1)
intTypes.append(typedefIntName)
#print "typedef int:", typedefInt
#raw_input()
newTypedefInt = commentPrefix + "CRYPTLIBCONVERTER - NOT NEEDED: " + typedefInt
s = s[ : typedefIntMatch.start()] + newTypedefInt + s[typedefIntMatch.end() : ]
typedefIntMatch = typedefIntPattern.search(s, typedefIntMatch.start() + len(newTypedefInt))
#print "INTTYPES:\n",intTypes
#Comment out typedef structs
#----------------------------
typedefStructPattern = re.compile(r"typedef[ \t]+struct[ \t]\{[^}]*}[ \t]*([^;]+);")
typedefStructMatch = typedefStructPattern.search(s)
while typedefStructMatch:
typedefStruct = typedefStructMatch.group()
typedefStructName = typedefStructMatch.group(1)
structTypes.append(typedefStructName)
#print "typedef struct:", typedefStructName
#raw_input()
newTypedefStruct = commentPrefix + "CRYPTLIBCONVERTER - NOT SUPPORTED:\n" + typedefStruct
newTypedefStruct = newTypedefStruct.replace("\n", "\n"+commentPrefix)
s = s[ : typedefStructMatch.start()] + newTypedefStruct + s[typedefStructMatch.end() : ]
typedefStructMatch = typedefStructPattern.search(s, typedefStructMatch.start() + len(newTypedefStruct))
#print "STRUCTTYPES:\n",structTypes
#raw_input()
#Translate functions
#--------------------
functionPattern = re.compile(r"C_RET[ \t]+([^ \t]+)[ \t]*\(([^\)]*)\);", re.DOTALL)
functionMatch = functionPattern.search(s)
while functionMatch:
function = functionMatch.group()
functionName, functionParams = functionMatch.groups()
if not functionName.startswith("crypt"):
raise "name doesn't start with crypt"+functionName
functionName = functionName[len("crypt") : ]
functionNames.append(functionName)
#print "function:", functionName, functionParams
#raw_input()
#Parse its parameters
paramStructs = parseFunctionParams(functionParams)
#Add raw list of ParamStructs to dictionary
#This will be used later, when generating the .c glue code
rawParamStructsDict[functionName] = paramStructs
# Since java and python don't have pass-by-reference, what we do is migrate the
# output int parameter in certain functions into the return value. If the function
# creates or returns values such as integers or a (void*, int*) pair, we record
# the indexes of the parameters to migrate. We do this in functions like:
# cryptCreate*() # return handle of new object
# cryptGetAttribute() # return integer value of attribute
# cryptGetAttributeString() # convert (void*, int*) to python string or java byte array
# cryptExportKey() # convert (void*, int*) "
# cryptCreateSignature() # convert (void*, int*)
# cryptKeysetOpen() # return handle
# cryptGetPublicKey() # return handle
# cryptGetPrivateKey() # return handle
# cryptGetCertExtension() # convert (void*, int) but DISCARD criticalFlag (the int* before)!
# cryptImportCert() # return handle
# cryptExportCert() # convert (void*, int)
# cryptCAGetItem() # return handle
# cryptCACertManagement() # return handle
# cryptPushData() # return integer (# of bytes copied)
# cryptPopData() # convert (void*, int*) even though they're not adjacent
discardIntIndex = -1
returnIntIndex = -1
returnVoidPtrIndex = -1
discardInLengthIndex1 = -1 # To discard input lengths, since python don't need these
discardInLengthIndex2 = -1
#Scan through looking for a void pointer preceded by "keyIDtype"
#Convert it into a char*
index = 1
for p in paramStructs[1:]:
p2 = paramStructs[index-1]
if p.isPtr and p.type=="void" and p2.type=="CRYPT_KEYID_TYPE":
p.type = "char"
index += 1
#Scan through looking for output int pointers (or structs)?!
#If there's two, we will migrate the second occurrence to the return value
#and discard the first so as to handle cryptGetCertExtension()
index = 0
for p in paramStructs:
if p.isOut and p.isPtr and (p.category=="intType" or p.type=="int" or p.category=="structType"):
if returnIntIndex != -1:
if discardIntIndex != -1:
raise "Found two returned ints to discard?!"
discardIntIndex = returnIntIndex
returnIntIndex = index
index += 1
#Record the migrated return value's ParamStruct
if returnIntIndex != -1:
newReturnStructsDict[functionName] = paramStructs[returnIntIndex]
#Return the discarded value's ParamStruct
if discardIntIndex != -1:
newDiscardedStructsDict[functionName] = paramStructs[discardIntIndex]
#Copy the parsed parameters and remove those we're going to migrate or discard
newParamStructs = [paramStructs[count]
for count in range(len(paramStructs))
if count not in (discardIntIndex, returnIntIndex)]
#Indices of input offsets and lengths
offsetIndices = []
lengthIndices = []
#Scan through looking for void pointer, add an int offset
index = 0
while 1:
if index >= len(newParamStructs):
break
p = newParamStructs[index]
if p.isPtr and p.type=="void":
newp = ParamStruct()
newp.type = "int"
newp.isPtr = 0
newp.isOut = 0
newp.category = "rawType"
newp.name = p.name+"Offset"
newParamStructs = newParamStructs[:index+1] + [newp] + newParamStructs[index+1:]
offsetIndices.append(index+1)
if not p.isOut and len(newParamStructs)> index+2 and newParamStructs[index+2].type == "int":
lengthIndices.append(index+2)
index += 1
#Add processed list of ParamStructs to dictionary
#This will be used later, when generating the .c glue code
newParamStructsDict[functionName] = newParamStructs
lengthIndicesDict[functionName] = lengthIndices
offsetIndicesDict[functionName] = offsetIndices
#Used for Python
if wholeFunctionDeclaration:
newFunction = wholeFunctionDeclaration % functionName
if sFuncs:
sFuncs += newFunction + "\n"
else: assert(0)
sModFuncs += (moduleFunctionEntry % (functionName, functionName)) + "\n"
else: #Java
#Construct new function declaration from language-specific templates
newFunction = functionDeclaration
if returnIntIndex != -1:
if newReturnStructsDict.get(functionName).category == "structType":
newFunction += newReturnStructsDict.get(functionName).type + " "
else:
newFunction += returnIntDeclaration
else:
newFunction += returnVoidDeclaration
newFunction += functionName + paramsPrefix
if len(newParamStructs):
newFunction += "\n" + paramWhiteSpace
if offsetIndices or lengthIndices:
newFunctionWrapper = newFunction
newFunctionWrapperArgs = ""
else:
newFunctionWrapper = None
#At this point I'm just getting lazy. Basically, we automatically generate
#String wrappers for function that take void* input parameters. Encrypt and
#Decrypt take void* in/out parameters. We should be smart enough to detect
#these and not generate wrappers for them, but instead, since we don't otherwise
#differentiate between C_IN and C_INOUT, we just hardcodedly exclude these
#from having String wrappers
if offsetIndices and lengthIndices and functionName not in ("Encrypt", "Decrypt"):
newFunctionStringWrapper = newFunction
newFunctionStringWrapperArgs = ""
else:
newFunctionStringWrapper = None
newFunctionStringWrapperArgs = ""
index = 0
for p in newParamStructs:
if p.category == "rawType" and p.type == "void" and p.isPtr:
template = paramVoidPtrTemplate
previousBufferName = p.name
elif p.category == "rawType" and p.type == "char" and p.isPtr:
template = paramCharPtrTemplate
elif p.category == "rawType" and p.type == "int" and not p.isPtr:
template = paramIntTemplate
elif p.category == "intType" and not p.isPtr:
template = paramIntTypeTemplate
elif p.category == "enumType" and not p.isPtr:
template = paramEnumTypeTemplate
#elif p.category == "structType"
else:
raise "Unrecognized parameter type! " + str(p)
#Strip out commas from the last param template, kind of a hack..
if index == len(newParamStructs)-1:
template = template[:].replace(",", "")
#Update the main function with this new param
newFunction += template % vars(p)
newFunction += paramWhiteSpace
#Update the wrapper function with this new param
#If this is not a special param...
if addFunctionWrappers and newFunctionWrapper and \
index not in offsetIndices and index not in lengthIndices:
#Determine if this is the last param in the wrapper function
anyMoreParams = [e for e in range(index+1, len(newParamStructs))
if e not in offsetIndices and e not in lengthIndices]
#Update the wrapper function's param list
if not anyMoreParams:
template = template[:].replace(",", "")
newFunctionWrapper += template % vars(p)
newFunctionWrapper += paramWhiteSpace
#Update the wrapper function's args that it uses to call the main function
newFunctionWrapperArgs += p.name
newFunctionWrapperArgs += ", "
#Do the same things for the string wrapper
if newFunctionStringWrapper:
newFunctionStringWrapper += template % vars(p)
newFunctionStringWrapper += paramWhiteSpace
if p.type=="void" and p.isPtr and not p.isOut:
newFunctionStringWrapperArgs += wrapperStringTemplate % {"1":p.name};
else:
newFunctionStringWrapperArgs += p.name
newFunctionStringWrapperArgs += ", "
else:
#If this is a special param (an offset or length)...
if index in offsetIndices:
newFunctionWrapperArgs += "0, "
newFunctionStringWrapperArgs += "0, "
elif index in lengthIndices:
newFunctionWrapperArgs += wrapperLengthTemplate % {"1":previousBufferName}
newFunctionStringWrapperArgs += wrapperStringLengthTemplate % {"1":previousBufferName}
index += 1
#Add final postfix of ); to the main and wrapper param lists
newFunction += paramsPostfix
if newFunctionWrapper:
newFunctionWrapper += paramsPostfix
if newFunctionStringWrapper:
newFunctionStringWrapper += paramsPostfix
#If this function takes a void* parameter, and we want to convert this
#into two different versions, duplicate and modify the version here
if addFunctionAlternate:
if newFunction.find(paramVoidPtrAlternate[0]) != -1:
newFunction += "\n" + newFunction.replace(*paramVoidPtrAlternate)
#If we've prepared a wrapper function to eliminate need for offset[, length]:
if addFunctionWrappers and newFunctionWrapper:
newFunctionWrapper = newFunctionWrapper.replace("native ", "")
newFunctionWrapper = newFunctionWrapper[:-1] + " { return %s(%s); }" % (functionName, newFunctionWrapperArgs[:-2])
if returnIntIndex == -1:
newFunctionWrapper = newFunctionWrapper.replace("return ", "")
#Duplicate and modify the wrapper function
if addFunctionAlternate:
newFunctionWrapper2 = newFunctionWrapper.replace(*paramVoidPtrAlternate)
newFunctionWrapper2 = newFunctionWrapper2.replace("capacity()", "length")
newFunctionWrapper += "\n" + newFunctionWrapper2
newFunction += "\n" + newFunctionWrapper
if addFunctionWrappers and newFunctionStringWrapper:
newFunctionStringWrapper = newFunctionStringWrapper.replace("native ", "")
newFunctionStringWrapper = newFunctionStringWrapper[:-1] + " { return %s(%s); }" % (functionName, newFunctionStringWrapperArgs[:-2])
newFunctionStringWrapper = newFunctionStringWrapper.replace(*wrapperStringReplace)
if returnIntIndex == -1:
newFunctionStringWrapper = newFunctionStringWrapper.replace("return ", "")
newFunction += "\n" + newFunctionStringWrapper
#Add a special-case string accessor for GetAttributeString() to Java and .NET
if functionName == "GetAttributeString" and language == "java":
newFunction += """
public static String GetAttributeString(
int cryptHandle, // CRYPT_HANDLE
int attributeType // CRYPT_ATTRIBUTE_TYPE
) throws CryptException
{
int length = GetAttributeString(cryptHandle, attributeType, (byte[])null);
byte[] bytes = new byte[length];
length = GetAttributeString(cryptHandle, attributeType, bytes);
return new String(bytes, 0, length);
}
"""
elif functionName == "GetAttributeString" and language == "net":
newFunction += """
public static String GetAttributeString(
int cryptHandle, // CRYPT_HANDLE
int attributeType // CRYPT_ATTRIBUTE_TYPE
)
{
int length = GetAttributeString(cryptHandle, attributeType, null);
byte[] bytes = new byte[length];
length = GetAttributeString(cryptHandle, attributeType, bytes);
return new UTF8Encoding().GetString(bytes, 0, length);
}
"""
#Add special-case functions for cryptAddRandom(), since it allows NULL as a
#first parameter, and a pollType constant in place of the length
elif functionName == "AddRandom":
if language == "java":
newFunction += """
public static native void AddRandom(
int pollType
) throws CryptException;
"""
elif language == "net":
newFunction += """
public static void AddRandom(
int pollType
);
"""
# Add an extra linebreak between functions
# This isn't in cryptlib.h, but it makes the converted files more readable
if s[functionMatch.end()+1] != "\n":
newFunction += "\n"
s = s[ : functionMatch.start()] + newFunction + s[functionMatch.end() : ]
functionMatch = functionPattern.search(s, functionMatch.start() + len(newFunction))
#Translate comments
#-------------------
if language != "java" and language != "net":
while 1:
match = re.search(r"/\*(.*?)\*/", s, re.DOTALL)
if match == None:
break
#print match.group()
#raw_input()
commentStrings = (commentPrefix + match.group(1) + " ").split("\n")
newComment = commentStrings[0]
for commentString in commentStrings[1:]:
if commentString.startswith("\t"):
newComment += "\n" + commentPrefix + (" " * (inFileTabSize-2)) + commentString[1:]
elif commentString.startswith(" "):
newComment += "\n" + commentPrefix + commentString[1:]
else:
newComment += "\n" + commentPrefix + commentString
#print "building up:\n", newComment
#raw_input()
idx = commentString.find("\n")
s = s[ : match.start()] + newComment + s[match.end() : ]
#Indent each line by one tab
#---------------------------
s = s.replace("\n", "\n\t")
#Write out file(s)
#Then generate the .h/.c files necessary for language integration
#At this point it's easier to just hardcode constants and have
#separate python and java codepaths
#---------------
if language=="java":
#Add enclosing class syntax
#---------------------------
s = classPrefix + s + classPostfix
os.chdir(outDir)
if not os.path.exists("cryptlib"):
os.mkdir("./cryptlib")
os.chdir("./cryptlib")
print "Writing java files..."
f = open("crypt.java", "w")
f.write(s)
f.close()
f = open("CryptException.java", "w")
f.write(exceptionString)
f.close()
f = open("CRYPT_QUERY_INFO.java", "w")
f.write(cryptQueryInfoString)
f.close()
f = open("CRYPT_OBJECT_INFO.java", "w")
f.write(cryptObjectInfoString)
f.close()
print "Compiling java files..."
print os.popen4("javac -classpath .. crypt.java")[1].read() #Compile java file
print os.popen4("javac -classpath .. CryptException.java")[1].read() #Compile java file
print os.popen4("javac -classpath .. CRYPT_QUERY_INFO.java")[1].read() #Compile java file
print os.popen4("javac -classpath .. CRYPT_OBJECT_INFO.java")[1].read() #Compile java file
os.chdir("..")
print "Generating jar file..."
print os.popen4("jar cf cryptlib.jar cryptlib/crypt.class cryptlib/CryptException.class cryptlib/CRYPT_QUERY_INFO.class cryptlib/CRYPT_OBJECT_INFO.class cryptlib")[1].read()
print "Generating JNI header file..."
print os.popen4("javah -classpath . -o cryptlib_jni.h -jni cryptlib.crypt")[1].read() #Generate C header
s = open("cryptlib_jni.h").read()
os.remove("cryptlib_jni.h")
print "Generating JNI source file..."
#Now we take cryptlib_jni.h and derive the .c file from it
#Strip off headers and footers
#startIndex = s.find("/*\n * Class")
#endIndex = s.find("#ifdef", startIndex)
#s = s[startIndex : endIndex]
startIndex = s.find("#undef")
endIndex = s.find("/*\n * Class", startIndex)
sold = s
s = s[startIndex : endIndex]
startIndex = endIndex
endIndex = sold.find("#ifdef", startIndex)
s2 = sold[startIndex : endIndex]
#Add includes and helper functions
#Note that we assume "cryptlib.h" is one directory behind us
s = r"""
#include "../crypt.h"
#ifdef USE_JAVA
#include <jni.h>
#include <stdio.h> //printf
#include <stdlib.h> //malloc, free
%s
/* Helper Functions */
int processStatus(JNIEnv *env, jint status)
{
jclass exClass;
jmethodID exConstructor;
jthrowable obj;
if (status >= cryptlib_crypt_OK)
return 1;
exClass = (*env)->FindClass(env, "cryptlib/CryptException");
if (exClass == 0)
{
printf("java_jni.c:processStatus - no class?!\n");
return 0;
}
exConstructor = (*env)->GetMethodID(env, exClass, "<init>", "(I)V");
if (exConstructor == 0)
{
printf("java_jni.c:processStatus - no constructor?!\n");
return 0;
}
obj = (*env)->NewObject(env, exClass, exConstructor, status);
if (obj == 0)
{
printf("java_jni.c:processStatus - no object?!\n");
return 0;
}
if ((*env)->Throw(env, obj) < 0)
{
printf("java_jni.c:processStatus - failed to throw?!\n");
return 0;
}
return 0;
}
jobject processStatusReturnCryptQueryInfo(JNIEnv *env, int status, CRYPT_QUERY_INFO returnValue)
{
jclass exClass;
jmethodID exConstructor;
jstring algoName;
jobject obj;
if (status < cryptlib_crypt_OK)
return NULL;
exClass = (*env)->FindClass(env, "cryptlib/CRYPT_QUERY_INFO");
if (exClass == 0)
{
printf("java_jni.c:processStatusReturnCryptQueryInfo - no class?!\n");
return NULL;
}
exConstructor = (*env)->GetMethodID(env, exClass, "<init>", "(Ljava/lang/String;IIII)V");
if (exConstructor == 0)
{
printf("java_jni.c:processStatusReturnCryptQueryInfo - no constructor?!\n");
return NULL;
}
algoName = (*env)->NewStringUTF(env, returnValue.algoName);
obj = (*env)->NewObject(env, exClass, exConstructor, algoName, returnValue.blockSize, returnValue.minKeySize, returnValue.keySize, returnValue.maxKeySize);
if (obj == 0)
{
printf("java_jni.c:processStatusReturnCryptQueryInfo - no object?!\n");
return NULL;
}
return obj;
}
jobject processStatusReturnCryptObjectInfo(JNIEnv *env, int status, CRYPT_OBJECT_INFO returnValue)
{
jclass exClass;
jmethodID exConstructor;
jbyteArray salt;
jobject obj;
if (status < cryptlib_crypt_OK)
return NULL;
exClass = (*env)->FindClass(env, "cryptlib/CRYPT_OBJECT_INFO");
if (exClass == 0)
{
printf("java_jni.c:processStatusReturnCryptObjectInfo - no class?!\n");
return NULL;
}
exConstructor = (*env)->GetMethodID(env, exClass, "<init>", "(IIII[B)V");
if (exConstructor == 0)
{
printf("java_jni.c:processStatusReturnCryptObjectInfo - no constructor?!\n");
return NULL;
}
salt = (*env)->NewByteArray(env, returnValue.saltSize);
(*env)->SetByteArrayRegion(env, salt, 0, returnValue.saltSize, returnValue.salt);
obj = (*env)->NewObject(env, exClass, exConstructor, returnValue.objectType, returnValue.cryptAlgo, returnValue.cryptMode, returnValue.hashAlgo, salt);
if (obj == 0)
{
printf("java_jni.c:processStatusReturnCryptObjectInfo - no object?!\n");
return NULL;
}
return obj;
}
int checkIndicesArray(JNIEnv *env, jbyteArray array, int sequenceOffset, int sequenceLength)
{
jsize arrayLength;
jclass exClass;
if (array == NULL)
{
if (sequenceOffset == 0)
return 1;
else
{
exClass = (*env)->FindClass(env, "java/lang/ArrayIndexOutOfBoundsException");
if (exClass == 0)
printf("java_jni.c:checkIndicesArray - no class?!\n");
else
if ((*env)->ThrowNew(env, exClass, "") < 0)
printf("java_jni.c:checkIndicesArray - failed to throw?!\n");
return 0;
}
}
arrayLength = (*env)->GetArrayLength(env, array);
if (sequenceOffset < 0 ||
sequenceOffset >= arrayLength ||
sequenceOffset + sequenceLength > arrayLength)
{
exClass = (*env)->FindClass(env, "java/lang/ArrayIndexOutOfBoundsException");
if (exClass == 0)
printf("java_jni.c:checkIndicesArray - no class?!\n");
else
if ((*env)->ThrowNew(env, exClass, "") < 0)
printf("java_jni.c:checkIndicesArray - failed to throw?!\n");
return 0;
}
return 1;
}
int getPointerArray(JNIEnv* env, jbyteArray array, jbyte** bytesPtrPtr)
{
jboolean isCopy;
if (array == NULL)
{
(*bytesPtrPtr) = NULL;
return 1;
}
(*bytesPtrPtr) = (*env)->GetByteArrayElements(env, array, &isCopy);
if (*bytesPtrPtr == NULL)
{
printf("java_jni.c:getPointer - failed to get elements of array?!\n");
return 0;
}
return 1;
}
void releasePointerArray(JNIEnv* env,jbyteArray array, jbyte* bytesPtr)
{
if (bytesPtr == NULL)
return;
(*env)->ReleaseByteArrayElements(env, array, bytesPtr, 0);
}
int checkIndicesNIO(JNIEnv *env, jobject byteBuffer, int sequenceOffset, int sequenceLength)
{
jlong byteBufferLength;
jclass exClass;
if (byteBuffer == NULL)
{
if (sequenceOffset == 0)
return 1;
else
{
exClass = (*env)->FindClass(env, "java/lang/ArrayIndexOutOfBoundsException");
if (exClass == 0)
printf("java_jni.c:checkIndicesNIO - no class?!\n");
else
if ((*env)->ThrowNew(env, exClass, "") < 0)
printf("java_jni.c:checkIndicesNIO - failed to throw?!\n");
return 0;
}
}
byteBufferLength = (*env)->GetDirectBufferCapacity(env, byteBuffer);
if (byteBufferLength == -1)
{
exClass = (*env)->FindClass(env, "java/lang/UnsupportedOperationException");
if (exClass == 0)
printf("java_jni.c:checkIndicesNIO - no class?!\n");
else
if ((*env)->ThrowNew(env, exClass,
"Either a non-direct ByteBuffer was passed or your JVM doesn't support JNI access to direct ByteBuffers") < 0)
printf("java_jni.c:checkIndicesNIO - failed to throw?!\n");
return 0;
}
if (sequenceOffset < 0 ||
sequenceOffset >= byteBufferLength ||
sequenceOffset + sequenceLength > byteBufferLength)
{
exClass = (*env)->FindClass(env, "java/lang/ArrayIndexOutOfBoundsException");
if (exClass == 0)
printf("java_jni.c:checkIndicesNIO - no class?!\n");
else
if ((*env)->ThrowNew(env, exClass, "") < 0)
printf("java_jni.c:checkIndicesNIO - failed to throw?!\n");
return 0;
}
return 1;
}
int getPointerNIO(JNIEnv* env, jobject byteBuffer, jbyte** bytesPtrPtr)
{
jclass exClass;
if (byteBuffer == NULL)
{
(*bytesPtrPtr) = NULL;
return 1;
}
(*bytesPtrPtr) = (*env)->GetDirectBufferAddress(env, byteBuffer);
if (*bytesPtrPtr == NULL)
{
exClass = (*env)->FindClass(env, "java/lang/UnsupportedOperationException");
if (exClass == 0)
printf("java_jni.c:getPointerNIO - no class?!\n");
else
if ((*env)->ThrowNew(env, exClass, "Your JVM doesn't support JNI access to direct ByteBuffers") < 0)
printf("java_jni.c:getPointerNIO - failed to throw?!\n");
return 0;
}
return 1;
}
void releasePointerNIO(JNIEnv* env,jbyteArray array, jbyte* bytesPtr)
{
}
int getPointerString(JNIEnv* env, jstring str, jbyte** bytesPtrPtr)
{
jboolean isCopy;
jsize strLength;
const jbyte* rawBytesPtr;
jclass exClass;
#ifdef __WINCE__
int status;
#endif // __WINCE__
if (str == NULL)
{
(*bytesPtrPtr) = NULL;
return 1;
}
rawBytesPtr = (*env)->GetStringUTFChars(env, str, &isCopy);
if (rawBytesPtr == NULL)
{
printf("java_jni.c:getPointerString - failed to get elements of String?!\n");
return 0;
}
strLength = (*env)->GetStringUTFLength(env, str);
#ifdef __WINCE__
(*bytesPtrPtr) = (jbyte*)malloc(strLength*2+2); // this is unicode, therefore \0 is two bytes long
#else
(*bytesPtrPtr) = (jbyte*)malloc(strLength+1);
#endif // __WINCE__
if (*bytesPtrPtr == NULL)
{
exClass = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
if (exClass == 0)
printf("java_jni.c:getPointerString - no class?!\n");
else
if ((*env)->ThrowNew(env, exClass, "") < 0)
printf("java_jni.c:getPointerString - failed to throw?!\n");
(*env)->ReleaseStringUTFChars(env, str, rawBytesPtr);
return 0;
}
#ifdef __WINCE__
status = asciiToUnicode (*bytesPtrPtr, strLength*2+2, rawBytesPtr, strLength+1);
if (status == CRYPT_ERROR_BADDATA) {
(*env)->ReleaseStringUTFChars(env, str, rawBytesPtr);
return 0;
}
#else
memcpy(*bytesPtrPtr, rawBytesPtr, strLength);
(*bytesPtrPtr)[strLength] = 0;
#endif // __WINCE__
(*env)->ReleaseStringUTFChars(env, str, rawBytesPtr);
return 1;
}
void releasePointerString(JNIEnv* env, jstring str, jbyte* bytesPtr)
{
if (bytesPtr == NULL)
return;
free(bytesPtr);
}
%s
#endif /* USE_JAVA */
""" % (s, s2)
functionPattern = re.compile(r"JNIEXPORT ([^ \t]+) JNICALL Java_cryptlib_crypt_([^ \t\n]+)\n[ \t]*\(([^\)]*)\);")
functionMatch = functionPattern.search(s)
while functionMatch:
#print functionMatch.groups()
#Extract the returnType, name, and prototype for this function
function = functionMatch.group()
functionReturnType, functionName, functionPrototype = functionMatch.groups()
#Handle special-case AddRandom(pollType) function
if functionName == "AddRandom__I":
p = ParamStruct()
p.type = "int"
p.isPtr = 0
p.isOut = 0
p.category = "intType"
p.name = "NULL"
p.rawIndex = 0
p2 = ParamStruct()
p2.type = "int"
p2.isPtr = 0
p2.isOut = 0
p2.category = "intType"
p2.name = "pollType"
p2.rawIndex = 1
rawParamStructs = [p,p2]
newParamStructs = [p2,p2]
voidTag = "Array"
functionName = functionName.split("__")[0]
returnName = None
discardName = None
else:
#Strip JNI decoration off function name
#But record which variety of tagged helper functions to use
if functionName.find("__") != -1:
if functionName.find("ByteBuffer") != -1:
voidTag = "NIO"
else:
voidTag = "Array"
functionName = functionName.split("__")[0]
#Look up the parameters we've previously determined for this function
rawParamStructs = rawParamStructsDict[functionName]
newParamStructs = newParamStructsDict[functionName]
if newReturnStructsDict.get(functionName):
returnName = newReturnStructsDict.get(functionName).name
returnType = newReturnStructsDict.get(functionName).type
returnCategory = newReturnStructsDict.get(functionName).category
else:
returnName = None
if newDiscardedStructsDict.get(functionName):
discardName = newDiscardedStructsDict.get(functionName).name
else:
discardName = None
#for p in newParamStructs: print " "+str(p)
#Substitute parameter names into the function prototype
newFunctionParams = expandFunctionPrototype(functionPrototype, newParamStructs)
startIndex = functionMatch.start(3) - functionMatch.start()
endIndex = functionMatch.end(3) - functionMatch.start()
function = function[ : startIndex] + newFunctionParams + function[ endIndex : ]
newFunctionBody = "\nint status = 0;\n"
arguments = ""
for p in rawParamStructs:
if p.name == returnName or p.name == discardName:
arguments += "&"
if p.isPtr and p.type=="void":
arguments += "%sPtr + %sOffset, " % (p.name, p.name)
elif p.isPtr and p.type=="char":
arguments += "%sPtr, " % p.name
else:
arguments += p.name + ", "
arguments = arguments[:-2]
if returnName:
if returnCategory == "intType" or returnType=="int":
newFunctionBody += "jint %s = 0;\n" % returnName
else:
newFunctionBody += returnType +" %s;\n" % returnName
if discardName:
newFunctionBody += "jint %s = 0;\n" % discardName
#If we have arrays, add the code to handle them
arrayNames = [p.name for p in newParamStructs if p.isPtr]
charArrayNames = [p.name for p in newParamStructs if p.isPtr and p.type=="char"]
voidArrayNames = [p.name for p in newParamStructs if p.isPtr and p.type=="void"]
outVoidArrayNames = [p.name for p in newParamStructs if p.isPtr and p.type=="void" and p.isOut]
if arrayNames:
#Declare C pointers to retrieve array contents
for n in arrayNames:
newFunctionBody += "jbyte* " + n + "Ptr = 0;\n"
newFunctionBody += "\n"
#Retrieve the contents for strings
#We need to do this first cause in one case this char* is needed as an argument
if charArrayNames:
for n in charArrayNames:
newFunctionBody += "if (!getPointerString(env, %(n)s, &%(n)sPtr))\n\tgoto finish;\n" % vars()
newFunctionBody += "\n"
#Query the length of output data
#CryptPopData is the only exception that produces output data
#but doesn't require this cause an explicit length is passed in
if outVoidArrayNames and functionName.find("PopData") == -1:
for n in outVoidArrayNames:
argumentsWithNull = arguments.replace("%sPtr + %sOffset" % (n,n), "NULL")
newFunctionBody += "if (!processStatus(env, crypt%s(%s)))\n\tgoto finish;\n" % (functionName, argumentsWithNull)
newFunctionBody += "\n"
elif functionName.find("PopData") != -1:
newFunctionBody += "//CryptPopData is a special case that doesn't have the length querying call\n\n"
if voidArrayNames:
for n in voidArrayNames:
index = [p.name for p in newParamStructs].index(n)
if n in outVoidArrayNames:
lengthName = returnName
else:
if len(newParamStructs)<=index+2 or newParamStructs[index+2].category != "rawType" or newParamStructs[index+2].type != "int":
lengthName = "1" #CheckSignature and ImportKey don't have a length, so we can't do a good check!
else:
lengthName = newParamStructs[index+2].name
newFunctionBody += "if (!checkIndices%(voidTag)s(env, %(n)s, %(n)sOffset, %(lengthName)s))\n\tgoto finish;\n" % vars()
newFunctionBody += "\n"
for n in voidArrayNames:
newFunctionBody += "if (!getPointer%(voidTag)s(env, %(n)s, &%(n)sPtr))\n\tgoto finish;\n" % vars()
if newFunctionBody[-2] != "\n":
newFunctionBody += "\n"
newFunctionBody += "status = crypt%s(%s);\n\n" % (functionName, arguments)
if arrayNames:
newFunctionBody += "finish:\n"
if arrayNames:
for n in voidArrayNames:
newFunctionBody += "releasePointer%(voidTag)s(env, %(n)s, %(n)sPtr);\n" % vars()
for n in charArrayNames:
newFunctionBody += "releasePointerString(env, %(n)s, %(n)sPtr);\n" % vars()
#newFunctionBody += "processStatus(env, status);\n"
#if returnName:
# newFunctionBody += "return(%s);\n" % returnName
if returnName:
if returnCategory == "intType" or returnType == "int":
newFunctionBody += "processStatus(env, status);\n"
newFunctionBody += "return(%s);\n" % returnName
elif returnType == "CRYPT_QUERY_INFO":
newFunctionBody += "return(processStatusReturnCryptQueryInfo(env, status, %s));\n" % returnName
elif returnType == "CRYPT_OBJECT_INFO":
newFunctionBody += "return(processStatusReturnCryptObjectInfo(env, status, %s));\n" % returnName
else:
newFunctionBody += "processStatus(env, status);\n"
newFunctionBody = newFunctionBody[:-1] #Strip off last \n
newFunctionBody = newFunctionBody.replace("\n", "\n\t");
newFunction = function[:-1]+"\n{" + newFunctionBody + "\n}"
#Substitute the output equivalent for the input
s = s[ : functionMatch.start()] + newFunction + s[functionMatch.end() : ]
#Get next function
functionMatch = functionPattern.search(s, functionMatch.start() + len(newFunction))
f = open("java_jni.c", "w")
f.write(s)
f.close()
elif language == "python":
os.chdir(outDir)
#print sInts
#raw_input()
#print sFuncs
#raw_input()
moduleFunctions = ""
s = pyBeforeExceptions + exceptionString + pyBeforeFuncs + sFuncs + \
pyBeforeModuleFunctions + moduleFunctions + sModFuncs + \
pyBeforeInts + sInts + \
pyAfterInts
#print s
#raw_input()
functionPattern = re.compile(r"static PyObject\* python_crypt([^\(]+)[^{]+{([^}]*)}", re.DOTALL)
functionMatch = functionPattern.search(s)
while functionMatch:
#print functionMatch.group()
#print functionMatch.groups()
#raw_input()
#Most of the code in this loop is copied-then-modified from the java code above, ugly..
voidTag = ""
#Extract the function name and body
functionName, functionBody = functionMatch.groups()
#Look up the parameters we've previously determined for this function
rawParamStructs = rawParamStructsDict[functionName]
newParamStructs = newParamStructsDict[functionName]
lengthIndices = lengthIndicesDict[functionName]
offsetIndices = offsetIndicesDict[functionName]
#print functionName, lengthIndices, offsetIndices
#raw_input()
if newReturnStructsDict.get(functionName):
returnName = newReturnStructsDict.get(functionName).name
returnType = newReturnStructsDict.get(functionName).type
returnCategory = newReturnStructsDict.get(functionName).category
else:
returnName = None
if newDiscardedStructsDict.get(functionName):
discardName = newDiscardedStructsDict.get(functionName).name
else:
discardName = None
newFunctionBody = "\nint status = 0;\n"
#Arguments to pass to the C cryptlib function
arguments = ""
for p in rawParamStructs:
if p.name == returnName or p.name == discardName:
arguments += "&"
if p.isPtr and p.type=="void":
arguments += "%sPtr, " % (p.name)
elif p.isPtr and p.type=="char":
arguments += "%sPtr, " % p.name
else:
arguments += p.name + ", "
arguments = arguments[:-2]
if returnName:
if returnCategory == "intType" or returnType=="int":
newFunctionBody += "int %s = 0;\n" % returnName
else:
newFunctionBody += returnType +" %s;\n" % returnName
if discardName:
newFunctionBody += "int %s = 0;\n" % discardName
#Declare variables to parse args tuple into
index = 0
for p in newParamStructs:
if index not in offsetIndices: #Python doesn't use the offsets
if p.isPtr:
newFunctionBody += "PyObject* %s = NULL;\n" % p.name
else:
newFunctionBody += "int %s = 0;\n" % p.name
index += 1
#If we have arrays, add the code to handle them
arrayNames = [p.name for p in newParamStructs if p.isPtr]
charArrayNames = [p.name for p in newParamStructs if p.isPtr and p.type=="char"]
voidArrayNames = [p.name for p in newParamStructs if p.isPtr and p.type=="void"]
outVoidArrayNames = [p.name for p in newParamStructs if p.isPtr and p.type=="void" and p.isOut]
if arrayNames:
#Declare C pointers to retrieve array contents
for n in arrayNames:
newFunctionBody += "unsigned char* " + n + "Ptr = 0;\n"
newFunctionBody += "\n"
#Retrieve the contents for strings
#We need to do this first cause in one case this char* is needed as an argument
#Parse the input arguments from the python user
#Arguments to parse from the python args tuple
if newParamStructs:
parseFormatString = ""
parseArgsList = []
index = 0
for p in newParamStructs:
if index not in lengthIndices and index not in offsetIndices:
if p.isPtr and p.type=="char":
parseFormatString += "O"
parseArgsList.append("&" + p.name)
elif p.isPtr and p.type=="void":
parseFormatString += "O"
parseArgsList.append("&" + p.name)
else:
parseFormatString += "i"
parseArgsList.append("&" + p.name)
index += 1
if newFunctionBody[-2] != "\n":
newFunctionBody += "\n"
if functionName == "AddRandom":
newFunctionBody += """\
//Special case to handle SLOWPOLL / FASTPOLL values
if (PyArg_ParseTuple(args, "i", &randomDataLength))
return processStatus(cryptAddRandom(NULL, randomDataLength));\n\n"""
newFunctionBody += """\
if (!PyArg_ParseTuple(args, "%s", %s))
return(NULL);\n\n""" % (parseFormatString, ", ".join(parseArgsList))
#for p in newParamStructs:
# print p.name,
if arrayNames:
if charArrayNames:
for n in charArrayNames:
newFunctionBody += "if (!getPointerReadString(%(n)s, &%(n)sPtr))\n\tgoto finish;\n" % vars()
newFunctionBody += "\n"
#Query the length of output data
#CryptPopData is the only exception that produces output data
#but doesn't require this cause an explicit length is passed in
if outVoidArrayNames and functionName.find("PopData") == -1:
for n in outVoidArrayNames:
argumentsWithNull = arguments.replace("%sPtr" % (n), "NULL")
newFunctionBody += "if (!processStatusBool(crypt%s(%s)))\n\tgoto finish;\n" % (functionName, argumentsWithNull)
newFunctionBody += "\n"
elif functionName.find("PopData") != -1:
newFunctionBody += "//CryptPopData is a special case that doesn't have the length querying call\n\n"
#Go through and get the pointers for translated void*
if voidArrayNames:
for n in voidArrayNames:
index = [p.name for p in newParamStructs].index(n)
#Determine the name of its length parameter
if n in outVoidArrayNames:
lengthName = returnName
else:
if len(newParamStructs)<=index+2 or newParamStructs[index+2].category != "rawType" or newParamStructs[index+2].type != "int":
lengthName = "1" #QueryObject, CheckSignature and ImportKey don't have a length, so we can't do a good check!
else:
lengthName = newParamStructs[index+2].name
if n in outVoidArrayNames and functionName.find("PopData") == -1:
newFunctionBody += "if (!getPointerWriteCheckIndices%(voidTag)s(%(n)s, &%(n)sPtr, &%(lengthName)s))\n\tgoto finish;\n" % vars()
else:
if lengthName == "1": #Handle the #CheckSignature/ImportKey case
newFunctionBody += "if (!getPointerReadNoLength%(voidTag)s(%(n)s, &%(n)sPtr))\n\tgoto finish;\n" % vars()
else:
#If the pointer is C_IN and not C_INOUT
#(we check against Encrypt/Decrypt directory for this latter check)
#We only need it for reading, not writing
if n not in outVoidArrayNames and functionName not in ("Encrypt", "Decrypt"):
newFunctionBody += "if (!getPointerRead(%(n)s, &%(n)sPtr, &%(lengthName)s))\n\tgoto finish;\n" % vars()
else:
newFunctionBody += "if (!getPointerWrite(%(n)s, &%(n)sPtr, &%(lengthName)s))\n\tgoto finish;\n" % vars()
if newFunctionBody[-2] != "\n":
newFunctionBody += "\n"
newFunctionBody += "status = crypt%s(%s);\n\n" % (functionName, arguments)
if arrayNames:
newFunctionBody += "finish:\n"
if arrayNames:
for n in voidArrayNames:
newFunctionBody += "releasePointer(%(n)s, %(n)sPtr);\n" % vars()
for n in charArrayNames:
newFunctionBody += "releasePointerString(%(n)s, %(n)sPtr);\n" % vars()
if returnName:
if returnCategory == "intType":
newFunctionBody += "return(processStatusReturnCryptHandle(status, %s));" % returnName
elif returnType == "CRYPT_QUERY_INFO":
newFunctionBody += "return(processStatusReturnCryptQueryInfo(status, %s));" % returnName
elif returnType == "CRYPT_OBJECT_INFO":
newFunctionBody += "return(processStatusReturnCryptObjectInfo(status, %s));" % returnName
elif returnType == "int":
newFunctionBody += "return(processStatusReturnInt(status, %s));" % returnName
else:
newFunctionBody += "return(processStatus(status));"
newFunctionBody = newFunctionBody.replace("\n", "\n\t")
#Substitute the output equivalent for the input
s = s[ : functionMatch.start(2)] + newFunctionBody + "\n" + s[functionMatch.end(2) : ]
#Get next function
functionMatch = functionPattern.search(s, functionMatch.start() + len(newFunction))
f = open("python.c", "w")
f.write(s)
f.close()
f = open("setup.py", "w")
f.write(setupPy)
f.close()
elif language == "net":
flagForAddRandomHack = 0
#functionPattern = re.compile(r"public static ([^ \t]+) ([^ \t\n]+)\([ \t\n]*\(([^\)]*)\) throws CryptException;")
functionPattern = re.compile(r"public static ([^ \t]+) ([^ \t\n]+)\([ \t\n]*([^\)]*)\);")
functionMatch = functionPattern.search(s)
while functionMatch:
#print functionMatch.groups()
#Extract the returnType, name, and prototype for this function
function = functionMatch.group()
functionReturnType, functionName, functionPrototype = functionMatch.groups()
#Handle special-case AddRandom(pollType) function
if functionName == "AddRandom":
flagForAddRandomHack += 1
if flagForAddRandomHack == 2:
p = ParamStruct()
p.type = "int"
p.isPtr = 0
p.isOut = 0
p.category = "intType"
p.name = "IntPtr.Zero"
p.rawIndex = 0
p2 = ParamStruct()
p2.type = "int"
p2.isPtr = 0
p2.isOut = 0
p2.category = "intType"
p2.name = "pollType"
p2.rawIndex = 1
rawParamStructs = [p,p2]
newParamStructs = [p2,p2]
voidTag = "Array"
functionName = functionName.split("__")[0]
returnName = None
discardName = None
flagForAddRandomHack = None
else:
#Look up the parameters we've previously determined for this function
rawParamStructs = rawParamStructsDict[functionName]
newParamStructs = newParamStructsDict[functionName]
if newReturnStructsDict.get(functionName):
returnName = newReturnStructsDict.get(functionName).name
returnType = newReturnStructsDict.get(functionName).type
returnCategory = newReturnStructsDict.get(functionName).category
else:
returnName = None
if newDiscardedStructsDict.get(functionName):
discardName = newDiscardedStructsDict.get(functionName).name
else:
discardName = None
#for p in newParamStructs: print " "+str(p)
#Substitute parameter names into the function prototype
newFunctionParams = functionPrototype #expandFunctionPrototype(functionPrototype, newParamStructs)
startIndex = functionMatch.start(3) - functionMatch.start()
endIndex = functionMatch.end(3) - functionMatch.start()
function = function[ : startIndex] + newFunctionParams + function[ endIndex : ]
newFunctionBody = "\n"
arguments = ""
for p in rawParamStructs:
if p.name == returnName or p.name == discardName:
arguments += "%sPtr, " % p.name
elif p.isPtr and p.type=="void":
arguments += "%sPtr, " % p.name
elif p.isPtr and p.type=="char":
arguments += "%sPtr, " % p.name
else:
arguments += p.name + ", "
arguments = arguments[:-2]
if returnName:
if returnCategory == "structType":
newFunctionBody += "IntPtr %sPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(%s)));\n" % (returnName, returnType)
newFunctionBody += "%s %s = new %s();\n" % (returnType, returnName, returnType)
else:
newFunctionBody += "IntPtr %sPtr = Marshal.AllocHGlobal(4);\n" % returnName
if discardName:
newFunctionBody += "IntPtr %sPtr = Marshal.AllocHGlobal(4);\n" % discardName
#If we have arrays, add the code to handle them
arrayNames = [p.name for p in newParamStructs if p.isPtr]
charArrayNames = [p.name for p in newParamStructs if p.isPtr and p.type=="char"]
voidArrayNames = [p.name for p in newParamStructs if p.isPtr and p.type=="void"]
outVoidArrayNames = [p.name for p in newParamStructs if p.isPtr and p.type=="void" and p.isOut]
if arrayNames:
#Declare C pointers to retrieve array contents
for n in arrayNames:
#newFunctionBody += "jbyte* " + n + "Ptr = 0;\n"
newFunctionBody += "GCHandle %sHandle = new GCHandle();\nIntPtr %sPtr = IntPtr.Zero;\n" % (n,n)
if n in charArrayNames:
newFunctionBody += "byte[] %sArray = new UTF8Encoding().GetBytes(%s);\n" % (n,n)
newFunctionBody += "try\n{\n"
#Retrieve the contents for strings
#We need to do this first cause in one case this char* is needed as an argument
if charArrayNames:
for n in charArrayNames:
newFunctionBody += "getPointer(%(n)sArray, 0, ref %(n)sHandle, ref %(n)sPtr);\n" % vars()
#Query the length of output data
#CryptPopData is the only exception that produces output data
#but doesn't require this cause an explicit length is passed in
if outVoidArrayNames and functionName.find("PopData") == -1:
for n in outVoidArrayNames:
argumentsWithNull = arguments.replace("%sPtr + %sOffset" % (n,n), "NULL")
newFunctionBody += "processStatus(wrapped_%s(%s));\n" % (functionName, argumentsWithNull)
newFunctionBody += "int %s = Marshal.ReadInt32(%sPtr);\n" % (returnName, returnName)
elif functionName.find("PopData") != -1 or functionName.find("PushData") != -1:
newFunctionBody += "int %s = 0;\n" % returnName
newFunctionBody += "int status;\n"
if voidArrayNames:
for n in voidArrayNames:
index = [p.name for p in newParamStructs].index(n)
if n in outVoidArrayNames:
lengthName = returnName
else:
if len(newParamStructs)<=index+2 or newParamStructs[index+2].category != "rawType" or newParamStructs[index+2].type != "int":
lengthName = "1" #CheckSignature and ImportKey don't have a length, so we can't do a good check!
else:
lengthName = newParamStructs[index+2].name
newFunctionBody += "checkIndices(%(n)s, %(n)sOffset, %(lengthName)s);\n" % vars()
for n in voidArrayNames:
newFunctionBody += "getPointer(%(n)s, %(n)sOffset, ref %(n)sHandle, ref %(n)sPtr);\n" % vars()
elif returnName:
newFunctionBody += "try\n{\n"
if functionName.find("PopData") == -1 and functionName.find("PushData") == -1:
newFunctionBody += "processStatus(wrapped_%s(%s));\n" % (functionName, arguments)
else:
newFunctionBody += "status = wrapped_%s(%s);\n" % (functionName, arguments)
newFunctionBody += "%s = Marshal.ReadInt32(%sPtr);\n" % (returnName, returnName)
newFunctionBody += "processStatus(status, %s);\n" %returnName
#if newFunctionBody[-2] != "\n":
# newFunctionBody += "\n"
if returnName:
if returnCategory == "structType":
newFunctionBody += "Marshal.PtrToStructure(%sPtr, %s);\n" % (returnName, returnName)
newFunctionBody += "return %s;\n" % returnName
else:
if functionName.find("PopData") == -1 and functionName.find("PushData") == -1:
newFunctionBody += "return Marshal.ReadInt32(%sPtr);\n" % returnName
else:
newFunctionBody += "return %s;\n" %returnName
if arrayNames or returnName:
newFunctionBody += "}\nfinally\n{\n"
if returnName:
newFunctionBody += "Marshal.FreeHGlobal(%sPtr);\n" % returnName
if arrayNames:
for n in voidArrayNames:
newFunctionBody += "releasePointer(%(n)sHandle);\n" % vars()
for n in charArrayNames:
newFunctionBody += "releasePointer(%(n)sHandle);\n" % vars()
newFunctionBody += "}\n"
#Add tabs to lines inside brackets:
lines = newFunctionBody.split("\n")
brackets = 0
for count in range(len(lines)):
line = lines[count]
if line.startswith("}"):
brackets -= 1
lines[count] = ("\t" * brackets) + line
if line.startswith("{"):
brackets += 1
newFunctionBody = "\n".join(lines)
newFunctionBody = newFunctionBody[:-1] #Strip off last \n
newFunctionBody = newFunctionBody.replace("\n", "\n\t");
newFunction = function[:-1]+"\n{" + newFunctionBody + "\n}"
newFunction = newFunction.replace("\n", "\n\t");
#Substitute the output equivalent for the input
s = s[ : functionMatch.start()] + newFunction + s[functionMatch.end() : ]
#Get next function
functionMatch = functionPattern.search(s, functionMatch.start() + len(newFunction))
#Add enclosing class syntax
#---------------------------
s = classPrefix + s
for functionName in functionNames:
rawParamStructs = rawParamStructsDict[functionName]
parameters = ""
for p in rawParamStructs:
if p.isPtr:
parameters += "IntPtr %s, " % p.name
else:
parameters += "int %s, " % p.name
parameters = parameters[:-2]
s += '\t[DllImport("cl32.dll", EntryPoint="crypt%s")]\n\tprivate static extern int wrapped_%s(%s);\n\n' % (functionName, functionName, parameters)
functionNames = [] #Accumulate function names here
#rawParamStructsDict = {} #Accumulate function name -> list of ParamStructs (of original c code)
#newParamStructsDict = {} #Accumulate function name -> list of ParamStructs (of new java/python code)
s += classPostfix + exceptionString + "\n\n}"
os.chdir(outDir)
print "Writing .NET file..."
f = open("cryptlib.cs", "w")
f.write(s)
f.close()