
cryptlibConverter version 1                         December 11, 2002              
=====================================================================

cryptlibConverter.py is a python script which parses cryptlib.h and generates code that makes cryptlib accessible within java and python:

cryptlibConverter.py cryptlib.h <outputDir> [java|python]

TestCryptlib.[py|java] is a test program that demonstrates using cryptlib within these languages.  Run it with no arguments for instructions.

In the example directory is a sample output of running the converter on the .h file from cryptlib 3.1, beta 2.

This package is in the public domain.


Java
-----
The java version assumes the commands 'javac', 'jar', and 'javah' are on the path, and creates:
 ./cryptlib/crypt.java, .class
 ./cryptlib/CryptException.java, .class
 ./cryptlib.jar (containing above .class files)
 ./cryptlibjni.h, .c

cryptlibjni.c should be included in the cryptlib project and compiled into its shared library.  This will require adding the JDK include directories to your compiler's settings.

Once this is done, you can place cryptlib.jar on your classpath, the shared library on your path, and use System.loadLibrary() to load the shared library.

crypt.java preserves much of the formatting from cryptlib.h, so it can serve as documentation of what functions are available, what their signatures are, etc..

Everything is placed in the 'crypt' class, so all constants and functions are prefixed with 'crypt.', i.e.:

crypt.SetAttribute(k, crypt.CTXINFO_KEYSIZE, 128);

Instead of:
cryptSetAttribute(k, CRYPT_CTXINFO_KEYSIZE, 128);

A plethora of types are supported in place of pointers:
 - Java Strings can be used wherever the C interface requires an input char* or null-terminated void*.
 - Java byte arrays can be used wherever the C interface requires an input or output non-null-terminated void*.
 - As of JDK 1.4, there is an nio.ByteBuffer class which can be "direct", in which case they are arrays of bytes that are at a fixed memory location, so they can be passed to native code with zero copying.  These can be passed to functions just like byte arrays, and will be more efficient.
 - Whenever passing a byte array or ByteBuffer as input, it can be followed by offset, length integers, or these can be omitted and will default to (0, length).  Whenever passing a byte array or ByteBuffer for receiving output, it can be followed by an offset, which can be omitted and will default to 0.
 
In C, cryptlib returns length values and handles through pass-by-address integers, and always has a status code as the return value.  The java version instead throws CryptException for any status code besides CRYPT_OK, and migrates returned integer values to the return value.  This means that function signatures are different than they are in C, so you may need to consult crypt.java to figure out what to pass to particular functions.  

There is a special version of cryptGetAttributeString() which returns Strings, for convenience:

String label = crypt.GetAttributeString(k, crypt.CTXINFO_LABEL);


Python
-------
Creates:
 ./cryptlibpy.c
 
This should be included in the cryptlib project and built into the shared library.  This will require adding the Python include directories to your compiler's settings.  Then, with the shared library in your path, you should be able to do "from cl32 import *" and then start using cryptlib.  This has only been tested on Windows, and probably needs tweaking to work elsewhere.

The python code accesses data through the "buffer" API, which supports Python's strings and arrays, and also supports "Numeric" arrays which are a popular add-on.  All these types are made availabe to the C code with zero copying.  

The python code does not have optional length and offset parameters, like the Java code.  Python uses slicing syntax to select subsequences of a sequence, i.e. seq[startIndex:endIndex].  Slicing strings and arrays causes a copy, but "Numeric" arrays use view slicing and support the buffer API, so they could be used for zero-copy operations on subsequences if you need that.  Thus there's no need to provide explicit offset, length parameters.

In C, cryptlib returns length values and handles through pass-by-address integers, and always has a status code as the return value.  The python version instead throws CryptException for any status code besides CRYPT_OK, and migrates returned integer values to the return value.  This means that function signatures are different than they are in C, so you may need to consult cryptlibpy.c and decode its call to 'PyArg_ParseTuples()' to figure out what to pass to particular functions.  Or just fiddle around with it.

When returning handles, the python code returns objects of type 'CryptHandle', which inherits from integer, but adds overloads to support a "shortcut syntax" for getting/setting attributes with less typing:

keyPair = cryptCreateContext(CRYPT_UNUSED, CRYPT_ALGO_RSA)

# Verbose attribute syntax
cryptSetAttributeString(keyPair, CRYPT_CTXINFO_LABEL, "blabla")

# Shortcut attribute syntax
keyPair.CTXINFO_KEYSIZE = 128
print keyPair.CTXINFO_NAME_ALGO


General Limitations
--------------------
Can't handle cryptInitEx() or any other function that's declared but not defined, so these need to be removed from cryptlib.h manually, before cryptlibConverter is run, or the resulting C files won't compile.

Ignores everything inside conditional directives (#if, #ifdef), so if you want e.g. SHA2, you have to remove the conditionals manually.

Can't handle cryptUIGenerateKey() or cryptUIDisplayCert(), since they're the only functions that take output char* and HWND parameters, and I haven't added support for these types.

Doesn't support structs (i.e. CRYPT_QUERY_INFO, CRYPT_OBJECT_INFO, CRYPT_PKCSINFO_RSA, CRYPT_PKCINFO_DLP), or any of the functions using structs.

Python and Java don't have enumerated types.  So all enums are just turned into integers.  The one exception to this is in Python, where handles are returned as instances of a type 'CryptHandle' which is defined within the module, and which is a child of integer.  This class has overloads so that you can use a "shortcut" syntax to get/set the attributes of the cryptlib object.

Python and Java don't have pass-by-reference primitive types.  So in functions that accept such an integer, typically to return a length value or new object handle, it's migrated to the return value.  Instead of returning status values, they're converted to exceptions and thrown.

Since it migrates any primitive pass-by-reference value it finds to the return value, it has trouble with cryptGetCertExtension(), which is the only function that takes two pass-by-reference ints.  It resolves this by discarding the first such parameter (the criticalFlag in this case) when it finds two of them.

The cryptlib error messages are returned directly, even though they sometimes refer to the position of C parameters, which might be different from the position of the Java or Python parameters because of the above-mentioned migration of return values.  So be wary of error messages that complain about the position of an argument.


Trevor Perrin
tperrin@sigaba.com

