aboutsummaryrefslogblamecommitdiff
path: root/py11/__init__.py
blob: d4c07a105757386337c051aa4172393f2541db8f (plain) (tree)




















































































































































































                                                                                                                       















                                                                                       











                                                                                          
 



                                               
# 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
    t = (CK_ATTRIBUTE * len(attributes))()
    for i in xrange(len(attributes)):
      t[i].type = attributes[i]
      t[i].pValue = None
      t[i].ulValueLen = 0
    self.so.C_GetAttributeValue(session_handle, object_handle, byref(t), len(t))
    for a in t:
      a.pValue = create_string_buffer(a.ulValueLen)
    self.so.C_GetAttributeValue(session_handle, object_handle, byref(t), len(t))
    return dict((a.type, a.pValue.raw) for a in t)

  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, byref(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_"))