# An attempt at a Python interface to PKCS 11 using the scary ctypes # module from the Python standard library. from struct import pack, unpack from ctypes import * from .exceptions import * from .types import * from .constants import * # Prototypes for the PKCS #11 public functions. # # We don't bother implementing C_GetFunctionList(), because it would # be extremely tedious and it's not all that useful to us in Python. # Instead, we emulate its behavior in the PKCS11 class. PKCS11_Prototypes = ( ("C_Initialize", [CK_VOID_PTR]), ("C_Finalize", [CK_VOID_PTR]), ("C_GetInfo", [CK_INFO_PTR]), # ("C_GetFunctionList", [CK_FUNCTION_LIST_PTR_PTR]), ("C_GetSlotList", [CK_BBOOL, CK_SLOT_ID_PTR, CK_ULONG_PTR]), ("C_GetSlotInfo", [CK_SLOT_ID, CK_SLOT_INFO_PTR]), ("C_GetTokenInfo", [CK_SLOT_ID, CK_TOKEN_INFO_PTR]), ("C_GetMechanismList", [CK_SLOT_ID, CK_MECHANISM_TYPE_PTR, CK_ULONG_PTR]), ("C_GetMechanismInfo", [CK_SLOT_ID, CK_MECHANISM_TYPE, CK_MECHANISM_INFO_PTR]), ("C_InitToken", [CK_SLOT_ID, CK_UTF8CHAR_PTR, CK_ULONG, CK_UTF8CHAR_PTR]), ("C_InitPIN", [CK_SESSION_HANDLE, CK_UTF8CHAR_PTR, CK_ULONG]), ("C_SetPIN", [CK_SESSION_HANDLE, CK_UTF8CHAR_PTR, CK_ULONG, CK_UTF8CHAR_PTR, CK_ULONG]), ("C_OpenSession", [CK_SLOT_ID, CK_FLAGS, CK_VOID_PTR, CK_NOTIFY, CK_SESSION_HANDLE_PTR]), ("C_CloseSession", [CK_SESSION_HANDLE]), ("C_CloseAllSessions", [CK_SLOT_ID]), ("C_GetSessionInfo", [CK_SESSION_HANDLE, CK_SESSION_INFO_PTR]), ("C_GetOperationState", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR]), ("C_SetOperationState", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_OBJECT_HANDLE, CK_OBJECT_HANDLE]), ("C_Login", [CK_SESSION_HANDLE, CK_USER_TYPE, CK_UTF8CHAR_PTR, CK_ULONG]), ("C_Logout", [CK_SESSION_HANDLE]), ("C_CreateObject", [CK_SESSION_HANDLE, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR]), ("C_CopyObject", [CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR]), ("C_DestroyObject", [CK_SESSION_HANDLE, CK_OBJECT_HANDLE]), ("C_GetObjectSize", [CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_ULONG_PTR]), ("C_GetAttributeValue", [CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_ATTRIBUTE_PTR, CK_ULONG]), ("C_SetAttributeValue", [CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_ATTRIBUTE_PTR, CK_ULONG]), ("C_FindObjectsInit", [CK_SESSION_HANDLE, CK_ATTRIBUTE_PTR, CK_ULONG]), ("C_FindObjects", [CK_SESSION_HANDLE, CK_OBJECT_HANDLE_PTR, CK_ULONG, CK_ULONG_PTR]), ("C_FindObjectsFinal", [CK_SESSION_HANDLE]), ("C_EncryptInit", [CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE]), ("C_Encrypt", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR]), ("C_EncryptUpdate", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR]), ("C_EncryptFinal", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR]), ("C_DecryptInit", [CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE]), ("C_Decrypt", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR]), ("C_DecryptUpdate", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR]), ("C_DecryptFinal", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR]), ("C_DigestInit", [CK_SESSION_HANDLE, CK_MECHANISM_PTR]), ("C_Digest", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR]), ("C_DigestUpdate", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG]), ("C_DigestKey", [CK_SESSION_HANDLE, CK_OBJECT_HANDLE]), ("C_DigestFinal", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR]), ("C_SignInit", [CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE]), ("C_Sign", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR]), ("C_SignUpdate", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG]), ("C_SignFinal", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR]), ("C_SignRecoverInit", [CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE]), ("C_SignRecover", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR]), ("C_VerifyInit", [CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE]), ("C_Verify", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG]), ("C_VerifyUpdate", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG]), ("C_VerifyFinal", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG]), ("C_VerifyRecoverInit", [CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE]), ("C_VerifyRecover", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR]), ("C_DigestEncryptUpdate", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR]), ("C_DecryptDigestUpdate", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR]), ("C_SignEncryptUpdate", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR]), ("C_DecryptVerifyUpdate", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR]), ("C_GenerateKey", [CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR]), ("C_GenerateKeyPair", [CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_ATTRIBUTE_PTR, CK_ULONG, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR, CK_OBJECT_HANDLE_PTR]), ("C_WrapKey", [CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE, CK_OBJECT_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR]), ("C_UnwrapKey", [CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR]), ("C_DeriveKey", [CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR]), ("C_SeedRandom", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG]), ("C_GenerateRandom", [CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG]), ("C_GetFunctionStatus", [CK_SESSION_HANDLE]), ("C_CancelFunction", [CK_SESSION_HANDLE]), ("C_WaitForSlotEvent", [CK_FLAGS, CK_SLOT_ID_PTR, CK_VOID_PTR])) # Need to convert attributes between a sane Pythonic representation # and something ctypes can handle. Pythoninc representation would be # an iteraable of two element sequences, or perhaps a dict. def attributes_to_ctypes(dict_or_iterable): if isinstance(dict_or_iterable, dict): dict_or_iterable = dict_or_iterable.iteritems() items = tuple(dict_or_iterable) a = (CK_ATTRIBUTE * len(items))() for i, kv in enumerate(items): k, v = kv if isinstance(v, bool): v = pack("B", int(v)) elif isinstance(v, (int, long)): v = pack("L", v) a[i].type = k a[i].pValue = create_string_buffer(v) a[i].ulValueLen = len(v) return a class PKCS11 (object): def __init__(self, so_name = "libpkcs11.so"): self.so_name = so_name self.so = CDLL(so_name) def raise_on_failure(rv): if rv != CKR_OK: raise CKR_Exception.ckr_map[rv] for name, args in PKCS11_Prototypes: func = getattr(self.so, name) func.restype = raise_on_failure func.argtypes = args def __getattr__(self, name): return getattr(self.so, name) def C_GetFunctionList(self): return self @property def version(self): info = CK_INFO() self.so.C_GetInfo(byref(info)) return info.cryptokiVersion def C_Initialize(self): self.so.C_Initialize(None) def C_Finalize(self): self.so.C_Finalize(None) def C_GetSlotList(self): slots = (CK_SLOT_ID * 10)() count = CK_ULONG(len(slots)) self.so.C_GetSlotList(CK_TRUE, slots, byref(count)) return [slots[i] for i in xrange(count.value)] def C_GetTokenInfo(self, slot_id): token_info = CK_TOKEN_INFO() self.so.C_GetTokenInfo(slot_id, byref(token_info)) return token_info def C_OpenSession(self, slot, flags = CKF_SERIAL_SESSION | CKF_RW_SESSION, application = None, notify = CK_NOTIFY()): handle = CK_SESSION_HANDLE() self.so.C_OpenSession(slot, flags, application, notify, byref(handle)) return handle.value def C_GenerateRandom(self, session, n): buffer = create_string_buffer(n) self.so.C_GenerateRandom(session, buffer, sizeof(buffer)) return buffer.raw def C_Login(self, session, user, pin): self.so.C_Login(session, user, pin, len(pin)) def C_GetAttributeValue(self, session_handle, object_handle, attributes): if not isinstance(attributes, (list, tuple)): attributes = tuple(attributes) if not all(isinstance(a, (int, long)) for a in attributes): raise TypeError template = (CK_ATTRIBUTE * len(attributes))() for i in xrange(len(attributes)): template[i].type = attributes[i] template[i].pValue = None template[i].ulValueLen = 0 self.so.C_GetAttributeValue(session_handle, object_handle, template, len(template)) for t in template: t.pValue = create_string_buffer(t.ulValueLen) self.so.C_GetAttributeValue(session_handle, object_handle, template, len(template)) return dict((t.type, t.pValue[:t.ulValueLen]) for t in template) def C_FindObjectsInit(self, session, template): if template: self.so.C_FindObjectsInit(session, attributes_to_ctypes(template), len(template)) else: self.so.C_FindObjectsInit(session, None, 0) def C_FindObjects(self, session, chunk_size = 10): objects = (CK_OBJECT_HANDLE * chunk_size)() count = CK_ULONG() while True: self.so.C_FindObjects(session, objects, len(objects), byref(count)) for i in xrange(count.value): yield objects[i] if count.value == 0: break def C_GenerateKeyPair(self, session, mechanism_type, public_template, private_template): mechanism = CK_MECHANISM(mechanism_type, None, 0) public_template = attributes_to_ctypes(public_template) private_template = attributes_to_ctypes(private_template) public_handle = CK_OBJECT_HANDLE() private_handle = CK_OBJECT_HANDLE() self.so.C_GenerateKeyPair(session, byref(mechanism), public_template, len(public_template), private_template, len(private_template), byref(public_handle), byref(private_handle)) return public_handle.value, private_handle.value __all__ = ["PKCS11"] __all__.extend(name for name in globals() if name.startswith("CK") or name.startswith("CRYPTOKI_"))