/*
 * Class:     org_opensc_pkcs11_spi_PKCS11CipherSpi
 * Method:    initDecryptNative
 * Signature: (JJJJI)V
 */
JNIEXPORT void JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_spi_PKCS11CipherSpi_initDecryptNative)
  (JNIEnv *env, jobject jciph, jlong mh, jlong shandle, jlong hsession, jlong hkey, jint alg)
{
  int rv;
  CK_MECHANISM mechanism;
  pkcs11_slot_t *slot;

  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return;

  memset(&mechanism, 0, sizeof(mechanism));
  mechanism.mechanism = alg;

  rv = mod->method->C_DecryptInit(hsession,&mechanism,hkey);

  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_DecryptInit failed for slot %d.",
                          (int)slot->id);
      return;
    }
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Session
 * Method:    encryptInitNative
 * Signature: (JJJJI[B)V
 */
JNIEXPORT void JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Session_encryptInitNative)
  (JNIEnv *env, jclass cls, jlong mh, jlong shandle, jlong hsession, jlong hkey, jint alg, jbyteArray param)
{
  int rv;
  CK_MECHANISM mechanism;
  pkcs11_slot_t *slot;

  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return;

  memset(&mechanism, 0, sizeof(mechanism));
  mechanism.mechanism = alg;

  if (param) {
      mechanism.ulParameterLen = (*env)->GetArrayLength(env, param);
      mechanism.pParameter = alloca(mechanism.ulParameterLen);
      (*env)->GetByteArrayRegion(env,param,0,mechanism.ulParameterLen,(jbyte*)mechanism.pParameter);
  }

  rv = mod->method->C_EncryptInit(hsession,&mechanism,hkey);

  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_EncryptInit failed for slot %d.",
                          (int)slot->id);
      return;
    }
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Session
 * Method:    openNative
 * Signature: (JJI)J
 */
JNIEXPORT jlong JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Session_openNative)
  (JNIEnv *env, jclass jsession, jlong mh, jlong shandle, jint rw)
{
  int rv;
  CK_SESSION_HANDLE hsession;
  pkcs11_slot_t *slot;
  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return 0;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return 0;


  rv = mod->method->C_OpenSession(slot->id,
                                  rw ? (CKF_SERIAL_SESSION | CKF_RW_SESSION) : (CKF_SERIAL_SESSION),
                                  NULL, NULL,
                                  &hsession);
  if (rv != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                         "C_OpenSession for PKCS11 slot %d failed.",
                         (int)slot->id);
      return 0;
    }

   return hsession;
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Session
 * Method:    loginNative
 * Signature: (JJJI[B)V
 */
JNIEXPORT void JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Session_loginNative)
  (JNIEnv *env, jobject jsession, jlong mh, jlong shandle, jlong hsession, jint type, jbyteArray jpin)
{
  int rv;
  CK_UTF8CHAR_PTR pin=0;
  CK_ULONG pin_len=0;
  pkcs11_slot_t *slot;
  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return;

  if (jpin)
    {
      allocaCArrayFromJByteArray(pin,pin_len,env,jpin);
    }

  rv = mod->method->C_Login(hsession,type,pin,pin_len);
  if (rv != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                         "C_Login for PKCS11 slot %d failed.",
                         (int)slot->id);
      return;
    }
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Slot
 * Method:    getTokenSerialNumberNative
 * Signature: (JJ)[B
 */
jbyteArray JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Slot_getTokenSerialNumberNative)
  (JNIEnv *env, jobject jslot, jlong mh, jlong handle)
{
  int l;
  jbyteArray ret;
  pkcs11_slot_t *slot;
  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return JNI_FALSE;

  slot = pkcs11_slot_from_jhandle(env,handle);
  if (!slot) return JNI_FALSE;

  if ((slot->ck_slot_info.flags & CKF_TOKEN_PRESENT) == 0)
    jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",CKR_TOKEN_NOT_PRESENT,
                        "No token present in slot %d.",
                        (int)slot->id);

  l = sizeof(slot->ck_token_info.serialNumber)/sizeof(CK_UTF8CHAR);

  while (l > 0 && slot->ck_token_info.serialNumber[l-1] == ' ')
    --l;

  ret = (*env)->NewByteArray(env,l);
  (*env)->SetByteArrayRegion(env,ret,0,l,(jbyte*)slot->ck_token_info.serialNumber);

  return ret;
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Object
 * Method:    getMechanismsNative
 * Signature: (JJJJ)[Lorg/opensc/pkcs11/wrap/PKCS11Mechanism;
 */
jobjectArray JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Object_getAllowedMechanismsNative)
  (JNIEnv *env, jclass jp11obj, jlong mh, jlong shandle, jlong hsession, jlong ohandle)
{
  int rv;
  CK_ATTRIBUTE templ;
  CK_MECHANISM_TYPE_PTR mechanisms;
  CK_ULONG n_mechanisms;
  pkcs11_slot_t *slot;

  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return 0;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return 0;

  templ.type = CKA_ALLOWED_MECHANISMS;
  templ.pValue = NULL;
  templ.ulValueLen = 0;
      
  rv = mod->method->C_GetAttributeValue(hsession,ohandle,&templ,1);
  
  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_GetAttributeValue failed for attribute CKA_ALLOWED_MECHANISMS.");
      return 0;
    }

  if (templ.ulValueLen == ~((CK_ULONG)0))
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",CKR_FUNCTION_FAILED,
                          "C_GetAttributeValue returned ulValueLen -1 for attribute CKA_ALLOWED_MECHANISMS but returned CKR_OK. The underlying PKCS#11 module seems to be broken.");
      return 0;
    }

  templ.pValue = alloca(templ.ulValueLen);

  rv = mod->method->C_GetAttributeValue(hsession,ohandle,&templ,1);
  
  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_GetAttributeValue failed for attribute CKA_ALLOWED_MECHANISMS.");
      return 0;
    }

  if (templ.ulValueLen == ~((CK_ULONG)0))
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",CKR_FUNCTION_FAILED,
                          "C_GetAttributeValue returned ulValueLen -1 for attribute CKA_ALLOWED_MECHANISMS but returned CKR_OK. The underlying PKCS#11 module seems to be broken.");
      return 0;
    }

  mechanisms = (CK_MECHANISM_TYPE_PTR)templ.pValue;
  n_mechanisms = templ.ulValueLen/sizeof(CK_MECHANISM_TYPE);

  return pkcs11_slot_make_jmechanisms(env,mod,slot,mechanisms,n_mechanisms);
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Object
 * Method:    createObjectNative
 * Signature: (JJJ[Lorg/opensc/pkcs11/wrap/PKCS11Attribute;)J
 */
JNIEXPORT jlong JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Object_createObjectNative)
  (JNIEnv *env, jclass jp11obj, jlong mh, jlong shandle, jlong hsession, jobjectArray attrs)
{
  int rv;
  CK_ULONG i;
  CK_ULONG ulAttributeCount;
  CK_ATTRIBUTE_PTR pAttributes;
  CK_OBJECT_HANDLE hObject;
  jclass clazz;
  jmethodID getKindID,getDataID;
  pkcs11_slot_t *slot;
  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return 0;
  
  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return 0;

  clazz = (*env)->FindClass(env,"org/opensc/pkcs11/wrap/PKCS11Attribute");

  if (!clazz) return 0;

  getKindID = (*env)->GetMethodID(env,clazz,"getKind","()I");

  if (!getKindID) return 0;

   getDataID = (*env)->GetMethodID(env,clazz,"getData","()[B");

  if (!getDataID) return 0;

  ulAttributeCount = (*env)->GetArrayLength(env,attrs);
  pAttributes = alloca(ulAttributeCount * sizeof(CK_ATTRIBUTE));

  for (i=0;i<ulAttributeCount;++i)
    {
      jbyteArray data;
      jobject jattr = (*env)->GetObjectArrayElement(env,attrs,i);
      if (!jattr) return 0;

      pAttributes[i].type = (*env)->CallIntMethod(env,jattr,getKindID);

      data = (jbyteArray)(*env)->CallObjectMethod(env,jattr,getDataID);

      allocaCArrayFromJByteArray(pAttributes[i].pValue,pAttributes[i].ulValueLen,env,data);
    }


  rv = mod->method->C_CreateObject(hsession, pAttributes, ulAttributeCount, &hObject);

  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_CreateObject failed.");
      return 0;
    }

  return hObject;
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Slot
 * Method:    isHardwareDeviceNative
 * Signature: (J)Z
 */
jboolean JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Slot_isHardwareDeviceNative)
  (JNIEnv *env, jobject jslot, jlong mh, jlong handle)
{
  pkcs11_slot_t *slot;
  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return JNI_FALSE;

  slot = pkcs11_slot_from_jhandle(env,handle);
  if (!slot) return JNI_FALSE;

  return (slot->ck_slot_info.flags & CKF_HW_SLOT) != 0;
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Slot
 * Method:    destroySlotNative
 * Signature: (J)
 */
void JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Slot_destroySlotNative)
  (JNIEnv *env, jobject jslot, jlong mh, jlong handle)
{
  pkcs11_slot_t *slot;
  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return;

  slot = pkcs11_slot_from_jhandle(env,handle);
  if (!slot) return;

  destroy_pkcs11_slot(env,mod,slot);
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Slot
 * Method:    initSlotNative
 * Signature: (J)J
 */
jlong JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Slot_initSlotNative)
  (JNIEnv *env, jobject jslot, jlong mh, jlong id)
{
  pkcs11_slot_t *slot;
  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return 0;
  
  slot = new_pkcs11_slot(env,mod,id);
  if (!slot) return 0;

  return pkcs11_slot_to_jhandle(env,slot);
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Slot
 * Method:    getFirmwareVersionNative
 * Signature: (JJ)D
 */
jdouble JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Slot_getFirmwareVersionNative)
  (JNIEnv *env, jobject jslot, jlong mh, jlong handle)
{
  pkcs11_slot_t *slot;
  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return JNI_FALSE;

  slot = pkcs11_slot_from_jhandle(env,handle);
  if (!slot) return JNI_FALSE;

  return (jdouble)slot->ck_slot_info.firmwareVersion.major + 
    0.01 * (jdouble)slot->ck_slot_info.firmwareVersion.minor;
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Session
 * Method:    encryptFinalNative
 * Signature: (JJJ[BII)[B
 */
JNIEXPORT jbyteArray JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Session_encryptFinalNative)
  (JNIEnv *env, jclass cls, jlong mh, jlong shandle, jlong hsession)
{
  int rv;
  CK_BYTE_PTR pOutputPart = 0;
  CK_ULONG ulOutputLen = 0;
  jbyteArray ret;
  pkcs11_slot_t *slot;

  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return 0;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return 0;

  rv = mod->method->C_EncryptFinal(hsession,pOutputPart, &ulOutputLen);

  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_EncryptFinal failed for slot %d.",
                          (int)slot->id);
      return 0;
    }

  pOutputPart=alloca(ulOutputLen);

  rv = mod->method->C_EncryptFinal(hsession,pOutputPart,&ulOutputLen);

  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_EncryptFinal failed for slot %d.",
                          (int)slot->id);
      return 0;
    }

  ret = 0;

  if (ulOutputLen > 0)
    { 
      ret = (*env)->NewByteArray(env,ulOutputLen);
      if (ret)
        {
          (*env)->SetByteArrayRegion(env,ret,0,ulOutputLen,(jbyte*)pOutputPart);
        }
    }

  return ret;
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Session
 * Method:    verifyUpdateNative
 * Signature: (JJJ[BII)V
 */
JNIEXPORT void JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Session_verifyUpdateNative)
  (JNIEnv *env, jclass cls, jlong mh, jlong shandle, jlong hsession, jbyteArray data, jint off, jint len)
{
  int rv;
  CK_BYTE_PTR pPart;
  pkcs11_slot_t *slot;
  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return;

  if (len < 0)
    {
      jnixThrowException(env,"org/opensc/pkcs11/wrap/PKCS11Exception",
                         "Invalid data length %d.",(int)len);
      return;
    }

  if (data == NULL)
    {
      jnixThrowException(env,"org/opensc/pkcs11/wrap/PKCS11Exception",
                         "NULL input data.");
      return;
    }

  if (off < 0 || off > len)
    {
      jnixThrowException(env,"org/opensc/pkcs11/wrap/PKCS11Exception",
                         "Invalid input offset %d.",(int)off);
      return;
    }


  allocaCArrayFromJByteArrayOffLen(pPart,env,data,off,len);

  rv = mod->method->C_VerifyUpdate(hsession,pPart,len);

  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_VerifyUpdate failed for slot %d.",
                          (int)slot->id);
      return;
    }
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Slot
 * Method:    hasTokenProtectedAuthPathNative
 * Signature: (JJ)Z
 */
jboolean JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Slot_hasTokenProtectedAuthPathNative)
  (JNIEnv *env, jobject jslot, jlong mh, jlong handle)
{
  pkcs11_slot_t *slot;
  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return JNI_FALSE;

  slot = pkcs11_slot_from_jhandle(env,handle);
  if (!slot) return JNI_FALSE;

  if ((slot->ck_slot_info.flags & CKF_TOKEN_PRESENT) == 0)
    jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",CKR_TOKEN_NOT_PRESENT,
                        "No token present in slot %d.",
                        (int)slot->id);

  return (slot->ck_token_info.flags & CKF_PROTECTED_AUTHENTICATION_PATH) != 0;
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Session
 * Method:    logoutNative
 * Signature: (JJJ)V
 */
JNIEXPORT void JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Session_logoutNative)
  (JNIEnv *env, jobject jsession, jlong mh, jlong shandle, jlong hsession)
{
  int rv;
  pkcs11_slot_t *slot;
  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return;

  rv = mod->method->C_Logout(hsession);
  if (rv != CKR_OK)
    {
      fprintf(stderr,"PKCS11Session.logoutNative: C_Logout for PKCS11 slot %d(" PKCS11_MOD_NAME_FMT ") failed (%s).",
              (int)slot->id,mod->name,pkcs11_strerror(rv));
    }
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Session
 * Method:    closeNative
 * Signature: (JJJ)V
 */
JNIEXPORT void JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Session_closeNative)
  (JNIEnv *env, jclass jsession, jlong mh, jlong shandle, jlong hsession)
{
  int rv;
  pkcs11_slot_t *slot;
  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return;

  rv = mod->method->C_CloseSession(hsession);
  if (rv != CKR_OK)
    {
      fprintf(stderr,"pkcs11_slot_close_session: C_CloseSession for PKCS11 slot %d(" PKCS11_MOD_NAME_FMT ") failed.",
              (int)slot->id,mod->name);
    }
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Slot
 * Method:    enumerateSlotsNative
 * Signature: (J)[J
 */
jlongArray JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Slot_enumerateSlotsNative)
  (JNIEnv *env, jclass clazz, jlong mh)
{
  CK_ULONG nslots=0;
  CK_ULONG i;
  CK_SLOT_ID *slot_ids;
  jlong *long_slot_ids;
  jlongArray ret;
  int rv;

  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return 0;

  rv = mod->method->C_GetSlotList(FALSE /* tokenPresent */,(CK_SLOT_ID_PTR)0,&nslots);

  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_GetSlotList failed for module " PKCS11_MOD_NAME_FMT ".",
                          mod->name);
      return 0;
    }

  slot_ids = (CK_SLOT_ID *)alloca(sizeof(CK_SLOT_ID)*nslots);

  rv = mod->method->C_GetSlotList(FALSE /* tokenPresent */,slot_ids,&nslots);

  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_GetSlotList failed for module " PKCS11_MOD_NAME_FMT ".",mod->name);
      return 0;
    }
  
  long_slot_ids = (jlong *)alloca(sizeof(jlong)*nslots);

  for (i=0;i<nslots;++i)
    long_slot_ids[i] = slot_ids[i];

  ret = (*env)->NewLongArray(env,nslots);
  (*env)->SetLongArrayRegion(env,ret,0,nslots,long_slot_ids);

  return ret;
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Slot
 * Method:    waitForSlotNative
 * Signature: (J)J
 */
JNIEXPORT jlong JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Slot_waitForSlotNative)
  (JNIEnv *env, jclass jslot, jlong mh)
{
  CK_ULONG slotId;
  int rv;

  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return 0;

  /* wait in blocking mode. */
  rv = mod->method->C_WaitForSlotEvent(0,&slotId,NULL);

  if (rv != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_WaitForSlotEvent failed.");
      return 0;
    }

  return slotId;
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Slot
 * Method:    getMechanismsNative
 * Signature: (JJ)[Lorg/opensc/pkcs11/wrap/PKCS11Mechanism;
 */
jobjectArray JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Slot_getMechanismsNative)
  (JNIEnv *env, jobject jslot, jlong mh, jlong handle)
{
  int rv;
  CK_ULONG n_mechanisms = 0;
  CK_MECHANISM_TYPE_PTR mechanisms;
  pkcs11_slot_t *slot;

  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return 0;

  slot = pkcs11_slot_from_jhandle(env,handle);
  if (!slot) return 0;

  rv = mod->method->C_GetMechanismList(slot->id,NULL,&n_mechanisms);

  if (rv != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_GetMechanismList for PKCS11 slot %d failed.",
                          (int)slot->id);
      return 0;
    }

  mechanisms =
    (CK_MECHANISM_TYPE_PTR)alloca(n_mechanisms*sizeof(CK_MECHANISM_TYPE));

  rv = mod->method->C_GetMechanismList(slot->id,mechanisms,&n_mechanisms);

  if (rv != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_GetMechanismList for PKCS11 slot %d failed.",
                          (int)slot->id);
      return 0;
    }

  return pkcs11_slot_make_jmechanisms(env,mod,slot,mechanisms,n_mechanisms);
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Object
 * Method:    getULongAttributeNative
 * Signature: (JJJJI)I
 */
jint JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Object_getULongAttributeNative)
  (JNIEnv *env, jclass jp11obj, jlong mh, jlong shandle, jlong hsession, jlong ohandle, jint att)
{
  int rv;
  CK_ATTRIBUTE templ;
  /* default return value */
  CK_ULONG ret=~((CK_ULONG)0);
  pkcs11_slot_t *slot;

  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return 0;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return 0;

  templ.type = att;
  templ.pValue = &ret;
  templ.ulValueLen = sizeof(CK_ULONG);
      
  rv = mod->method->C_GetAttributeValue(hsession,ohandle,&templ,1);
  
  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_GetAttributeValue failed for attribute %u.",
                          (unsigned)att);
      return 0;
    }

  if (templ.ulValueLen != sizeof(CK_ULONG))
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",CKR_FUNCTION_FAILED,
                          "C_GetAttributeValue returned ulValueLen==%lu, which is not equal to sizeof(CK_ULONG) for attribute %u of type CK_ULONG but returned CKR_OK. The underlying PKCS#11 module seems to be broken.",
                          (unsigned long)templ.ulValueLen,(unsigned)att);
      return 0;
    }

  return ret;
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Session
 * Method:    verifyUpdateByteNative
 * Signature: (JJJB)V
 */
JNIEXPORT void JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Session_verifyUpdateByteNative)
  (JNIEnv *env, jclass cls, jlong mh, jlong shandle, jlong hsession, jbyte b)
{ 
  int rv;
  CK_BYTE bb = (CK_BYTE)b;
  pkcs11_slot_t *slot;
  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return;

  rv = mod->method->C_VerifyUpdate(hsession,&bb,1);

  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_VerifyUpdate failed for slot %d.",
                          (int)slot->id);
      return;
    }
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Session
 * Method:    verifyFinalNative
 * Signature: (JJJ[B)Z
 */
JNIEXPORT jboolean JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Session_verifyFinalNative)
  (JNIEnv *env, jclass cls, jlong mh, jlong shandle, jlong hsession, jbyteArray data)
{
  int rv;
  CK_BYTE_PTR pSignature;
  CK_ULONG    ulSignatureLen;
  pkcs11_slot_t *slot;
  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return JNI_FALSE;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return JNI_FALSE;

  if (data == NULL)
    {
      jnixThrowException(env,"org/opensc/pkcs11/wrap/PKCS11Exception",
                         "NULL input data.");
      return JNI_FALSE;
    }

  allocaCArrayFromJByteArray(pSignature,ulSignatureLen,env,data);

  rv = mod->method->C_VerifyFinal(hsession,pSignature,ulSignatureLen);

  switch (rv)
    {
    case CKR_SIGNATURE_INVALID:
      return JNI_FALSE;

    case CKR_OK:
      return JNI_TRUE;

    default:
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_VerifyFinal failed for slot %d.",
                          (int)slot->id);
      return JNI_FALSE;
    }
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Slot
 * Method:    getDescriptionNative
 * Signature: (JJ)[B
 */
jbyteArray JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Slot_getDescriptionNative)
  (JNIEnv *env, jobject jslot, jlong mh, jlong handle)
{
  int l;
  jbyteArray ret;
  pkcs11_slot_t *slot;

  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return JNI_FALSE;

  slot = pkcs11_slot_from_jhandle(env,handle);
  if (!slot) return JNI_FALSE;

  l = 64;

  while (l > 0 && slot->ck_slot_info.slotDescription[l-1] == ' ')
    --l;

  ret = (*env)->NewByteArray(env,l);
  (*env)->SetByteArrayRegion(env,ret,0,l,(jbyte*)slot->ck_slot_info.slotDescription);

  return ret;
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Slot
 * Method:    getTokenMaxPinLenNative
 * Signature: (JJ)I
 */
jint JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Slot_getTokenMaxPinLenNative)
  (JNIEnv *env, jobject jslot, jlong mh, jlong handle)
{
  pkcs11_slot_t *slot;
  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return JNI_FALSE;

  slot = pkcs11_slot_from_jhandle(env,handle);
  if (!slot) return JNI_FALSE;

  if ((slot->ck_slot_info.flags & CKF_TOKEN_PRESENT) == 0)
    jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",CKR_TOKEN_NOT_PRESENT,
                        "No token present in slot %d.",
                        (int)slot->id);

  if (slot->ck_token_info.ulMaxPinLen > 0x7fffffff ||
      slot->ck_token_info.ulMinPinLen > slot->ck_token_info.ulMaxPinLen )
    jnixThrowException(env,"org/opensc/pkcs11/wrap/PKCS11Exception",
                       "Invalid value %u for ulMaxPinLen of token in slot %d.",
                       (unsigned)slot->ck_token_info.ulMaxPinLen,(int)slot->id);

  return slot->ck_token_info.ulMaxPinLen;
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Object
 * Method:    deleteObjectNative
 * Signature: (JJJJ)V
 */
JNIEXPORT void JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Object_deleteObjectNative)
  (JNIEnv *env, jclass jp11obj, jlong mh, jlong shandle, jlong hsession, jlong ohandle)
{
  int rv;
  CK_ATTRIBUTE templ;
  CK_MECHANISM_TYPE_PTR mechanisms;
  CK_ULONG n_mechanisms;
  pkcs11_slot_t *slot;

  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return;

  rv = mod->method->C_DestroyObject(hsession,ohandle);
  
  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_DestroyObject failed.");
      return;
    }
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Object
 * Method:    enumObjectsNative
 * Signature: (JJJ[Lorg/opensc/pkcs11/wrap/PKCS11Attribute;)[J
 */
JNIEXPORT jlongArray JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Object_enumObjectsNative)
  (JNIEnv *env, jclass jp11obj, jlong mh, jlong shandle, jlong hsession, jobjectArray attrs)
{
  jclass clazz;
  jmethodID getKindID,getDataID;
  CK_ULONG ulAttributeCount;
  CK_ATTRIBUTE_PTR pAttributes;
  CK_ULONG i,count;
  int nobjs,rv;
  CK_ULONG obj_ids[ENUM_HANDLES_BLOCK_SZ];
  size_t ret_obj_ids_sz;
  jlong *ret_obj_ids;
  jlongArray ret;
  pkcs11_slot_t *slot;

  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return 0;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return 0;

  clazz = (*env)->FindClass(env,"org/opensc/pkcs11/wrap/PKCS11Attribute");

  if (!clazz) return 0;

  getKindID = (*env)->GetMethodID(env,clazz,"getKind","()I");

  if (!getKindID) return 0;

  getDataID = (*env)->GetMethodID(env,clazz,"getData","()[B");

  if (!getDataID) return 0;

  ulAttributeCount = (*env)->GetArrayLength(env,attrs);
  pAttributes = alloca(ulAttributeCount * sizeof(CK_ATTRIBUTE));

  for (i=0;i<ulAttributeCount;++i)
    {
      jbyteArray data;
      jobject jattr = (*env)->GetObjectArrayElement(env,attrs,i);
      if (!jattr) return 0;

      pAttributes[i].type = (*env)->CallIntMethod(env,jattr,getKindID);

      data = (jbyteArray)(*env)->CallObjectMethod(env,jattr,getDataID);

      allocaCArrayFromJByteArray(pAttributes[i].pValue,pAttributes[i].ulValueLen,env,data);
    }

  ret_obj_ids_sz = ENUM_HANDLES_BLOCK_SZ;
  nobjs = 0;
  ret_obj_ids=(jlong*)malloc(ret_obj_ids_sz * sizeof(jlong));

  if (!ret_obj_ids)
    {
      jnixThrowException(env,"org/opensc/pkcs11/wrap/PKCS11Exception",
                         "Out of memory during object enumeration for slot number %d.",
                         (int)slot->id);
      goto failed;
    }

  rv = mod->method->C_FindObjectsInit(hsession,pAttributes,ulAttributeCount);

  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_FindObjectsInit failed for slot number %d.",
                          (int)slot->id);
      goto failed;
    }

  count = 0;

  rv = mod->method->C_FindObjects(hsession,obj_ids, ENUM_HANDLES_BLOCK_SZ, &count);

  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_FindObjects failed for slot number %d.",
                          (int)slot->id);
      goto failed;
    }

  for (i=0; i<count ;++i)
    {
      jlong id = (jlong)obj_ids[i];
      ret_obj_ids[nobjs] = id;
      ++nobjs;
    }

  while (count == ENUM_HANDLES_BLOCK_SZ)
    {
      jlong *new_obj_ids;
      ret_obj_ids_sz += ENUM_HANDLES_BLOCK_SZ;

      new_obj_ids=(jlong*)realloc(ret_obj_ids,ret_obj_ids_sz * sizeof(jlong));
     
      if (!new_obj_ids)
        {
          jnixThrowException(env,"org/opensc/pkcs11/wrap/PKCS11Exception",
                             "Out of memory during object enumeration for slot number %d.",
                             (int)slot->id);
          goto failed;
        }

      ret_obj_ids=new_obj_ids;

      rv = mod->method->C_FindObjects(hsession,obj_ids,ENUM_HANDLES_BLOCK_SZ, &count);
      
      if (rv  != CKR_OK)
        {
          jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                              "C_FindObjects failed for slot number %d.",
                              (int)slot->id);
          goto failed;
        }

      for (i=0; i<count ;++i)
        {
          jlong id = (jlong)obj_ids[i];
          ret_obj_ids[nobjs] = id;
          ++nobjs;
        }
    }

  rv = mod->method->C_FindObjectsFinal(hsession);
  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_FindObjectsFinal failed for slot number %d.",
                          (int)slot->id);
      goto failed;
    }

  /* OK, akc it into JAVA's realm. */
  ret = (*env)->NewLongArray(env,nobjs);
  (*env)->SetLongArrayRegion(env,ret,0,nobjs,ret_obj_ids);

  free(ret_obj_ids);
  return ret;

 failed:
  free(ret_obj_ids);
  return 0;
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Object
 * Method:    getAttributeNative
 * Signature: (JJJI)[B
 */
JNIEXPORT jbyteArray JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Object_getAttributeNative)
  (JNIEnv *env, jclass jp11obj, jlong mh, jlong shandle, jlong hsession, jlong ohandle, jint att)
{
  int rv;
  CK_ATTRIBUTE templ;
  jbyteArray ret;
  pkcs11_slot_t *slot;

  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return 0;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return 0;

  templ.type = att;
  templ.pValue = NULL;
  templ.ulValueLen = 0;
      
  rv = mod->method->C_GetAttributeValue(hsession,ohandle,&templ,1);
  
  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_GetAttributeValue failed for attribute %u.",
                          (unsigned)att);
      return 0;
    }

  if (templ.ulValueLen == ~((CK_ULONG)0))
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",CKR_FUNCTION_FAILED,
                          "C_GetAttributeValue returned ulValueLen -1 for attribute %u but returned CKR_OK. The underlying PKCS#11 module seems to be broken.",
                          (unsigned)att);
      return 0;
    }

  templ.pValue = alloca(templ.ulValueLen);

  rv = mod->method->C_GetAttributeValue(hsession,ohandle,&templ,1);
  
  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_GetAttributeValue failed for attribute %u.",
                          (unsigned)att);
      return 0;
    }
  
  if (templ.ulValueLen == ~((CK_ULONG)0))
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",CKR_FUNCTION_FAILED,
                          "C_GetAttributeValue returned ulValueLen -1 for attribute %u but returned CKR_OK. The underlying PKCS#11 module seems to be broken.",
                          (unsigned)att);
      return 0;
    }

  ret = (*env)->NewByteArray(env,templ.ulValueLen);
  (*env)->SetByteArrayRegion(env,ret,0,templ.ulValueLen,(jbyte*)templ.pValue);
  
  return ret;
}
/*
 * Class:     org_opensc_pkcs11_spi_PKCS11CipherSpi
 * Method:    updateDecryptNative
 * Signature: (JJJJ[BII)[B
 */
JNIEXPORT jbyteArray JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_spi_PKCS11CipherSpi_updateDecryptNative)
  (JNIEnv *env, jobject jciph, jlong mh, jlong shandle, jlong hsession, jlong hkey, jbyteArray input, jint off, jint len)
{
  int rv;
  CK_BYTE_PTR pInputPart;
  CK_BYTE_PTR pOutputPart = 0;
  CK_ULONG ulOutputLen = 0;
  jbyteArray ret;
  pkcs11_slot_t *slot;

  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return 0;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return 0;

  if (len < 0)
    {
      jnixThrowException(env,"org/opensc/pkcs11/wrap/PKCS11Exception",
                         "Invalid data length %d.",(int)len);
      return 0;
    }

  if (input == NULL)
    {
      jnixThrowException(env,"org/opensc/pkcs11/wrap/PKCS11Exception",
                         "NULL input data.");
      return 0;
    }

  if (off < 0 || off > len)
    {
      jnixThrowException(env,"org/opensc/pkcs11/wrap/PKCS11Exception",
                         "Invalid input offset %d.",(int)off);
      return 0;
    }

  allocaCArrayFromJByteArrayOffLen(pInputPart,env,input,off,len);

  rv = mod->method->C_DecryptUpdate(hsession,pInputPart,len,pOutputPart,&ulOutputLen);

  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_DecryptUpdate failed for slot %d.",
                          (int)slot->id);
      return 0;
    }

  pOutputPart=alloca(ulOutputLen);

  rv = mod->method->C_DecryptUpdate(hsession,pInputPart,len,pOutputPart,&ulOutputLen);

  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_DecryptUpdate failed for slot %d.",
                          (int)slot->id);
      return 0;
    }

  ret = (*env)->NewByteArray(env,ulOutputLen);
  if (ret)
    (*env)->SetByteArrayRegion(env,ret,0,ulOutputLen,(jbyte*)pOutputPart);
  
  return ret;
}
/*
 * Class:     org_opensc_pkcs11_spi_PKCS11CipherSpi
 * Method:    doFinalEncryptNativeOff
 * Signature: (JJJJ[BII[BI)I
 */
JNIEXPORT jint JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_spi_PKCS11CipherSpi_doFinalEncryptNativeOff)
  (JNIEnv *env, jobject jciph, jlong mh, jlong shandle, jlong hsession, jlong hkey, jbyteArray input, jint off, jint len, jbyteArray output, jint output_off)
{
  int rv;
  CK_BYTE_PTR pOutputPart;
  CK_ULONG ulOutputLen,ulOutputLen0,ulOutputLen1;
  CK_BYTE_PTR pInputPart;
  pkcs11_slot_t *slot;
  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);

  if (!mod) return 0;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return 0;

  if (len < 0)
    {
      jnixThrowException(env,"org/opensc/pkcs11/wrap/PKCS11Exception",
                         "Invalid data length %d.",(int)len);
      return 0;
    }

  if (input == NULL)
    {
      jnixThrowException(env,"org/opensc/pkcs11/wrap/PKCS11Exception",
                         "NULL input data.");
      return 0;
    }

  if (off < 0 || off > len)
    {
      jnixThrowException(env,"org/opensc/pkcs11/wrap/PKCS11Exception",
                         "Invalid input offset %d.",(int)off);
      return 0;
    }

  ulOutputLen = (*env)->GetArrayLength(env,output);
  
  if (output_off < 0 || output_off > ulOutputLen)
    {
      jnixThrowException(env,"org/opensc/pkcs11/wrap/PKCS11Exception",
                         "Invalid output offset %d.",(int)output_off);
      return 0;
    }
 
  ulOutputLen -= output_off;
  pOutputPart = alloca(ulOutputLen);
  ulOutputLen0 = ulOutputLen;

  allocaCArrayFromJByteArrayOffLen(pInputPart,env,input,off,len);

  rv = mod->method->C_EncryptUpdate(hsession,pInputPart,len,pOutputPart,&ulOutputLen0);

  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_EncryptUpdate failed for slot %d.",
                          (int)slot->id);
      return 0;
    }

  ulOutputLen1 = ulOutputLen - ulOutputLen0;

  rv = mod->method->C_EncryptFinal(hsession,pOutputPart+ulOutputLen0,&ulOutputLen1);

  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_EncryptFinal failed for slot %d.",
                          (int)slot->id);
      return 0;
    }

  ulOutputLen = ulOutputLen0+ulOutputLen1;

  (*env)->SetByteArrayRegion(env,output,output_off,ulOutputLen,(jbyte*)pOutputPart);
  
  return ulOutputLen;
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Session
 * Method:    signNative
 * Signature: (JJJ[BII)[B
 */
JNIEXPORT jbyteArray JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Session_signNative)
  (JNIEnv *env, jclass cls, jlong mh, jlong shandle, jlong hsession, jbyteArray ba, jint off, jint len)
{
  int rv;
  CK_BYTE_PTR pMessage = NULL;
  CK_BYTE_PTR pSignature = NULL;
  CK_ULONG    ulSignatureLen = 0;
  jbyteArray ret;
  pkcs11_slot_t *slot;
  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return 0;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return 0;

  if (len < 0)
    {
      jnixThrowException(env,"org/opensc/pkcs11/wrap/PKCS11Exception",
                         "Invalid data length %d.",(int)len);
      return NULL;
    }

  if (ba == NULL)
    {
      jnixThrowException(env,"org/opensc/pkcs11/wrap/PKCS11Exception",
                         "NULL input data.");
      return NULL;
    }

  if (off < 0)
    {
      jnixThrowException(env,"org/opensc/pkcs11/wrap/PKCS11Exception",
                         "Invalid input offset %d.",(int)off);
      return NULL;
    }

  allocaCArrayFromJByteArrayOffLen(pMessage,env,ba,off,len);

  rv = mod->method->C_Sign(hsession,pMessage,len,pSignature,&ulSignatureLen);

  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_Sign failed for slot %d.",
                          (int)slot->id);
      return NULL;
    }

  pSignature = (CK_BYTE_PTR)alloca(ulSignatureLen);

  rv = mod->method->C_Sign(hsession,pMessage,len,pSignature,&ulSignatureLen);

  if (rv  != CKR_OK)
    {
      jnixThrowExceptionI(env,"org/opensc/pkcs11/wrap/PKCS11Exception",rv,
                          "C_SignFinal failed for slot %d.",
                          (int)slot->id);
      return NULL;
    }

  ret = (*env)->NewByteArray(env,ulSignatureLen);
  if (ret)
    (*env)->SetByteArrayRegion(env,ret,0,ulSignatureLen,(jbyte*)pSignature);

  return ret;
}