/**
 * Copyright(c) 2011 Trusted Logic.   All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name Trusted Logic nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "pkcs11_internal.h"

/* ------------------------------------------------------------------------
Internal Functions
------------------------------------------------------------------------- */
/**
* Checks the following pre-conditions:
* - cryptoki is initialized,
* - hSession is valid (primary and/or secondary),
* - the user is logged in.
*
* And updates handle values:
*  IN/OUT : phSession
*           IN  = Cryptoki external handle
*           OUT = TFAPI handle = primary cryptoki session handle
*  OUT    : phSecSession16Msb
*           OUT = 0 for a primary session or
*                 the secondary cryptoki session handle in the 16 MSB bits
*/
static CK_RV static_checkPreConditionsAndUpdateHandles(
         CK_SESSION_HANDLE*   phSession,
         uint32_t*            phCommandIDAndSession,
         PPKCS11_PRIMARY_SESSION_CONTEXT* ppSession)
{
   bool  bIsPrimarySession;

   /* Check Cryptoki is initialized */
   if (!g_bCryptokiInitialized)
   {
      return CKR_CRYPTOKI_NOT_INITIALIZED;
   }

   if (phSession == NULL)
   {
      return CKR_SESSION_HANDLE_INVALID;
   }

   /* Check that the session is valid */
   if (!ckInternalSessionIsOpenedEx(*phSession, &bIsPrimarySession))
   {
      return CKR_SESSION_HANDLE_INVALID;
   }
   /* previous check is fine, then update session handles */
   if (bIsPrimarySession)
   {
      PPKCS11_PRIMARY_SESSION_CONTEXT pSession =
         (PPKCS11_PRIMARY_SESSION_CONTEXT)(*phSession);

      *phSession = pSession->hCryptoSession;
      *phCommandIDAndSession = (pSession->hCryptoSession<<16)|(*phCommandIDAndSession&0x00007FFF);
      *ppSession = pSession;
   }
   else
   {
      PPKCS11_SECONDARY_SESSION_CONTEXT pSecSession =
         (PPKCS11_SECONDARY_SESSION_CONTEXT)(*phSession);

      *phSession = pSecSession->pPrimarySession->hCryptoSession;
      *phCommandIDAndSession = (pSecSession->hSecondaryCryptoSession<<16)|(1<<15)|(*phCommandIDAndSession&0x00007FFF);
      *ppSession = pSecSession->pPrimarySession;
   }

   return CKR_OK;
}

/******************************************/
/* The buffer must be freed by the caller */
/******************************************/
static CK_RV static_encodeTwoTemplates(
   uint8_t**         ppBuffer,
   uint32_t *        pBufferSize,
   const uint32_t    nParamIndex,
   CK_ATTRIBUTE*     pTemplate1,
   CK_ULONG          ulCount1,
   CK_ATTRIBUTE*     pTemplate2,
   CK_ULONG          ulCount2)
{
   INPUT_TEMPLATE_ITEM  sItem;

   uint32_t i;
   uint32_t nDataOffset    = 0;
   uint32_t nBufferIndex   = 0;
   uint32_t nBufferSize    = 0;
   uint8_t* pBuffer = NULL;
   CK_RV    nErrorCode = CKR_OK;

   if (ulCount1 == 0)
   {
      /* Nothing to do */
      return CKR_OK;
   }
   if (pTemplate1 == NULL)
   {
      /* Nothing to do */
      return CKR_OK;
   }

   /* First compute the total required buffer size that
    * will contain the full templates (for the template 1 AND 2)
    */
   nBufferSize =  4 +                                    /* Nb Attributes */
                  sizeof(INPUT_TEMPLATE_ITEM)*ulCount1;  /* The attributes items */
   if (pTemplate2 != NULL)
   {
      nBufferSize += 4 +                                    /* Nb Attributes */
                     sizeof(INPUT_TEMPLATE_ITEM)*ulCount2;  /* The attributes items */
   }

   /* First data (attribute values) on either template 1 or 2 will just be after the last item */
   nDataOffset = nBufferSize;

   for (i = 0; i < ulCount1; i++)
   {
      /* Each value will be aligned on 4 bytes.
         This computation includes the spare bytes. */
      nBufferSize += PKCS11_GET_SIZE_WITH_ALIGNMENT(pTemplate1[i].ulValueLen);
   }
   if (pTemplate2 != NULL)
   {
      for (i = 0; i < ulCount2; i++)
      {
         /* Each value will be aligned on 4 bytes.
            This computation includes the spare bytes. */
         nBufferSize += PKCS11_GET_SIZE_WITH_ALIGNMENT(pTemplate2[i].ulValueLen);
      }
   }

   pBuffer = (uint8_t*)malloc(nBufferSize);
   if (pBuffer == NULL)
   {
      /* Not enough memory */
      return CKR_DEVICE_MEMORY;
   }

   memset(pBuffer, 0, nBufferSize);

   /*
    * First template
    */
   *(uint32_t*)(pBuffer + nBufferIndex) = ulCount1;
   nBufferIndex += 4;
   for (i = 0; i < ulCount1; i++)
   {
      sItem.attributeType     = (uint32_t)pTemplate1[i].type;
      /* dataOffset = 0 means NULL buffer */
      sItem.dataOffset        = ((pTemplate1[i].pValue == NULL) ? 0 : nDataOffset);
      sItem.dataParamIndex    = nParamIndex; /* The parameter where we store the data (0 to 3) */
      sItem.dataValueLen      = (uint32_t)pTemplate1[i].ulValueLen;
      /* Copy the item */
      memcpy(pBuffer + nBufferIndex, &sItem, sizeof(INPUT_TEMPLATE_ITEM));
      nBufferIndex += sizeof(INPUT_TEMPLATE_ITEM);
      if (pTemplate1[i].pValue != NULL)
      {
         /* Copy the data */
         memcpy(pBuffer + nDataOffset, (uint8_t*)pTemplate1[i].pValue, (uint32_t)pTemplate1[i].ulValueLen);
         /* Next data will be stored just after the previous one but aligned on 4 bytes */
         nDataOffset += PKCS11_GET_SIZE_WITH_ALIGNMENT(pTemplate1[i].ulValueLen);
         if ((nDataOffset & 0xC0000000) != 0)
         {
            /* We whould never go in this case, that means the dataOffset will not be able to store the offset correctly */
            nErrorCode = CKR_DEVICE_ERROR;
            goto error;
         }
      }
   }

   /*
    * Second template
    */
   if (pTemplate2 != NULL)
   {
      *(uint32_t*)(pBuffer + nBufferIndex) = ulCount2;
      nBufferIndex += 4;
      for (i = 0; i < ulCount2; i++)
      {
         sItem.attributeType     = (uint32_t)pTemplate2[i].type;
         /* dataOffset = 0 means NULL buffer */
         sItem.dataOffset        = ((pTemplate2[i].pValue == NULL) ? 0 : nDataOffset);
         sItem.dataParamIndex    = nParamIndex; /* The parameter where we store the data (0..3) */
         sItem.dataValueLen      = (uint32_t)pTemplate2[i].ulValueLen;
         /* Copy the item */
         memcpy(pBuffer + nBufferIndex, &sItem, sizeof(INPUT_TEMPLATE_ITEM));
         nBufferIndex += sizeof(INPUT_TEMPLATE_ITEM);
         if (pTemplate2[i].pValue != NULL)
         {
            /* Copy the data */
            memcpy(pBuffer + nDataOffset, (uint8_t*)pTemplate2[i].pValue, (uint32_t)pTemplate2[i].ulValueLen);
            /* Next data will be stored just after the previous one but aligned on 4 bytes */
            nDataOffset += PKCS11_GET_SIZE_WITH_ALIGNMENT(pTemplate2[i].ulValueLen);
            if ((nDataOffset & 0xC0000000) != 0)
            {
               /* We whould never go in this case, that means the dataOffset will not be able to store the offset correctly */
               nErrorCode = CKR_DEVICE_ERROR;
               goto error;
            }
         }
      }
   }

   *ppBuffer      = pBuffer;
   *pBufferSize   = nBufferSize;

   return CKR_OK;

error:
   free(pBuffer);
   return nErrorCode;
}

/******************************************/
/* The buffer must be freed by the caller */
/******************************************/
static CK_RV static_encodeTemplate(
   uint8_t**         ppBuffer,
   uint32_t*         pBufferSize,
   const uint32_t    nParamIndex,
   CK_ATTRIBUTE*     pTemplate,
   CK_ULONG          ulCount)
{
   return static_encodeTwoTemplates(ppBuffer, pBufferSize, nParamIndex, pTemplate, ulCount, NULL, 0);
}
/* ----------------------------------------------------------------------- */

static CK_RV static_C_CallInit(
   uint32_t            nCommandID,
   CK_SESSION_HANDLE   hSession,
   const CK_MECHANISM* pMechanism,
   CK_OBJECT_HANDLE    hKey)
{
   TEEC_Result    teeErr;
   uint32_t       nErrorOrigin;
   TEEC_Operation sOperation;
   CK_RV          nErrorCode = CKR_OK;
   uint32_t       nCommandIDAndSession = nCommandID;
   uint32_t       nParamType2 = TEEC_NONE;
   PPKCS11_PRIMARY_SESSION_CONTEXT pSession;

   nErrorCode = static_checkPreConditionsAndUpdateHandles(&hSession, &nCommandIDAndSession, &pSession);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }
   if (pMechanism == NULL)
   {
      return CKR_ARGUMENTS_BAD;
   }

   memset(&sOperation, 0, sizeof(TEEC_Operation));
   sOperation.params[0].value.a = (uint32_t)pMechanism->mechanism;
   if (nCommandID != SERVICE_SYSTEM_PKCS11_C_DIGESTINIT_COMMAND_ID)
   {
      sOperation.params[0].value.b = (uint32_t)hKey;

   }
   sOperation.params[1].tmpref.buffer = (uint8_t*)pMechanism->pParameter;
   sOperation.params[1].tmpref.size   = (uint32_t)pMechanism->ulParameterLen;

   /* Specific case of RSA OAEP */
   if (((nCommandID == SERVICE_SYSTEM_PKCS11_C_ENCRYPTINIT_COMMAND_ID)
      ||(nCommandID == SERVICE_SYSTEM_PKCS11_C_DECRYPTINIT_COMMAND_ID))
      && (pMechanism->mechanism == CKM_RSA_PKCS_OAEP)
      && (pMechanism->pParameter != NULL))
   {
      /* Add the source buffer of the RSA OAEP mechanism parameters */
      nParamType2 = TEEC_MEMREF_TEMP_INPUT;
      sOperation.params[2].tmpref.buffer = (uint8_t*)((CK_RSA_PKCS_OAEP_PARAMS_PTR)(pMechanism->pParameter))->pSourceData;
      sOperation.params[2].tmpref.size   = (uint32_t) ((CK_RSA_PKCS_OAEP_PARAMS_PTR)(pMechanism->pParameter))->ulSourceDataLen;
   }
   sOperation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_MEMREF_TEMP_INPUT, nParamType2, TEEC_NONE);
   teeErr = TEEC_InvokeCommand(   &pSession->sSession,
                                  nCommandIDAndSession,        /* commandID */
                                  &sOperation,                 /* IN OUT operation */
                                  &nErrorOrigin                /* OUT returnOrigin, optional */
                                 );
   nErrorCode = (nErrorOrigin == TEEC_ORIGIN_TRUSTED_APP ?
                  teeErr :
                  ckInternalTeeErrorToCKError(teeErr));

   return nErrorCode;
}

/* ----------------------------------------------------------------------- */
/**
* If bSend, the pData buffer is sent to the service.
* If bResult, a buffer is received, the convention described in
* PKCS11 Section 11.2 applies for pResult and pulResultLen.
* Specific function used for single operation
*/
static CK_RV static_C_CallForSingle(
   uint32_t             nCommandID,
   CK_SESSION_HANDLE    hSession,
   const CK_BYTE*       pData,
   CK_ULONG             ulDataLen,
   CK_BYTE*             pResult,
   CK_ULONG*            pulResultLen,
   bool                 bSend,
   bool                 bReceive)
{
   TEEC_Result       teeErr;
   uint32_t          nErrorOrigin;
   TEEC_Operation    sOperation;
   CK_RV             nErrorCode = CKR_OK;
   uint32_t          nCommandIDAndSession = nCommandID;
   uint32_t          nParamType0 = TEEC_NONE;
   uint32_t          nParamType1 = TEEC_NONE;
   PPKCS11_PRIMARY_SESSION_CONTEXT pSession;

   nErrorCode = static_checkPreConditionsAndUpdateHandles(&hSession, &nCommandIDAndSession, &pSession);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   memset(&sOperation, 0, sizeof(TEEC_Operation));

   if (bSend)
   {
      nParamType0 = TEEC_MEMREF_TEMP_INPUT;
      sOperation.params[0].tmpref.buffer = (uint8_t*)pData;
      sOperation.params[0].tmpref.size   = (uint32_t)ulDataLen;
   }

   if (bReceive)
   {
      if (pulResultLen == NULL)
      {
         /* The P11 API Spec states that, in this case, the operation must be
            aborted and the error code CKR_ARGUMENTS_BAD must be returned. We
            achieve this result by sending an invalid parameter type */
         nParamType1 = TEEC_NONE;
      }
      else if (pResult == NULL)
      {
         /* If pResult is NULL, the caller only wants the buffer length.
            Send a NULL output memref */
         nParamType1 = TEEC_MEMREF_TEMP_OUTPUT;
         sOperation.params[1].tmpref.buffer = (uint8_t*)NULL;
      }
      else
      {
         /* send the result buffer information */
         nParamType1 = TEEC_MEMREF_TEMP_OUTPUT;
         sOperation.params[1].tmpref.buffer = (uint8_t*)pResult;
         sOperation.params[1].tmpref.size   = (uint32_t)*pulResultLen;
      }
   }

   sOperation.paramTypes = TEEC_PARAM_TYPES(nParamType0, nParamType1, TEEC_NONE, TEEC_NONE);
   teeErr = TEEC_InvokeCommand(&pSession->sSession,
                               nCommandIDAndSession,     /* commandID */
                               &sOperation,              /* IN OUT operation */
                               &nErrorOrigin             /* OUT returnOrigin, optional */
                              );
   if (teeErr != TEEC_SUCCESS)
   {
      nErrorCode = (nErrorOrigin == TEEC_ORIGIN_TRUSTED_APP ?
                     teeErr :
                     ckInternalTeeErrorToCKError(teeErr));
      goto end;
   }

   /* Success */
   nErrorCode = CKR_OK;

 end:
   if (bReceive)
   {
      if ((nErrorCode == CKR_OK) || (nErrorCode == CKR_BUFFER_TOO_SMALL))
      {
         /* The service has returned the actual result */
         /* The data is already in pResult, we get the returned length */
         *pulResultLen = sOperation.params[1].tmpref.size;
      }
   }

   return nErrorCode;
}

/* ----------------------------------------------------------------------- */
/**
* If bSend, the pData buffer is sent to the service.
* If bResult, a buffer is received, the convention described in
* PKCS11 Section 11.2 applies for pResult and pulResultLen.
* Specific function only used for update operations
*/
static CK_RV static_C_CallUpdate(
   uint32_t             nCommandID,
   CK_SESSION_HANDLE    hSession,
   const CK_BYTE*       pData,
   CK_ULONG             ulDataLen,
   CK_BYTE*             pResult,
   CK_ULONG*            pulResultLen,
   bool                 bSend,
   bool                 bReceive)
{
   TEEC_Result       teeErr;
   uint32_t          nErrorOrigin;
   TEEC_Operation    sOperation;
   CK_RV             nErrorCode = CKR_OK;
   uint32_t          nResultLen = 0;
   uint32_t          nCommandIDAndSession = nCommandID;
   uint32_t          nParamType0 = TEEC_NONE;
   uint32_t          nParamType1 = TEEC_NONE;
   PPKCS11_PRIMARY_SESSION_CONTEXT pSession;

   nErrorCode = static_checkPreConditionsAndUpdateHandles(&hSession, &nCommandIDAndSession, &pSession);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   if (pulResultLen != NULL)
   {
      nResultLen = *pulResultLen;
   }

   memset(&sOperation, 0, sizeof(TEEC_Operation));

   if (bSend)
   {
      nParamType0 = TEEC_MEMREF_TEMP_INPUT;
      sOperation.params[0].tmpref.buffer = (void*)pData;
      sOperation.params[0].tmpref.size   = ulDataLen;
   }

   if (bReceive)
   {
      if (pulResultLen == NULL)
      {
         /* The P11 API Spec states that, in this case, the operation must be
            aborted and the error code CKR_ARGUMENTS_BAD must be returned. We
            achieve this result by setting an invalid parameter type */
         nParamType1 = TEEC_NONE;
      }
      else if (pResult == NULL)
      {
         /* If pResult is NULL, the caller only wants the output buffer length.
            Pass a NULL output ref */
         nParamType1 = TEEC_MEMREF_TEMP_OUTPUT;
         sOperation.params[1].tmpref.buffer = NULL;
      }
      else
      {
         /* send the result buffer information */
         nParamType1 = TEEC_MEMREF_TEMP_OUTPUT;
         sOperation.params[1].tmpref.buffer = pResult;
         sOperation.params[1].tmpref.size   = (uint32_t)*pulResultLen;
      }
   }

   sOperation.paramTypes = TEEC_PARAM_TYPES(nParamType0, nParamType1, TEEC_NONE, TEEC_NONE);
   teeErr = TEEC_InvokeCommand(   &pSession->sSession,
                                  nCommandIDAndSession,        /* commandID */
                                  &sOperation,                 /* IN OUT operation */
                                  &nErrorOrigin                /* OUT returnOrigin, optional */
                                 );
   if (teeErr != TEEC_SUCCESS)
   {
      nErrorCode = (nErrorOrigin == TEEC_ORIGIN_TRUSTED_APP ?
                     teeErr :
                     ckInternalTeeErrorToCKError(teeErr));
      goto end;
   }

   /* Success */
   nErrorCode = CKR_OK;

 end:
   if (bReceive)
   {
      if ((nErrorCode == CKR_OK) || (nErrorCode == CKR_BUFFER_TOO_SMALL))
      {
         /* The service has returned the actual result */
         /* The data is already in pResult, we get the returned length */
         *pulResultLen = sOperation.params[1].tmpref.size;
      }
   }

   return nErrorCode;
}

/* Splits the buffer pData in chunks of nChunkSize size
 * and calls static_C_CallUpdate for each chunk
 */
static CK_RV static_C_CallSplitUpdate(
                           uint32_t           nCommandID,
                           CK_SESSION_HANDLE  hSession,
                           const CK_BYTE*     pData,
                           CK_ULONG           ulDataLen,
                           CK_BYTE*           pResult,
                           CK_ULONG*          pulResultLen,
                           bool               bSend,
                           bool               bReceive,
                           uint32_t           nChunkSize)
{
   CK_RV nErrorCode;
   CK_ULONG nPartDataLen;
   CK_ULONG nPartResultLen = 0;
   CK_ULONG ulResultLen = 0;
   bool bIsSymOperation = false;

   if (pulResultLen != NULL)
   {
      ulResultLen = *pulResultLen;
      /* Check wether the operation is a symetrical or asymetrical */
      if (*pulResultLen == ulDataLen)
      {
         bIsSymOperation = true;
      }
      *pulResultLen = 0;
   }

   while (ulDataLen > 0)
   {
      nPartDataLen = (ulDataLen <= nChunkSize ?
                        ulDataLen : nChunkSize);
      if (bIsSymOperation)
      {
         /* update the result only if it is a symetric operation */
         nPartResultLen = (ulResultLen <= nChunkSize ?
                               ulResultLen : nChunkSize);
      }
      else
      {
         nPartResultLen = ulResultLen;
      }

      nErrorCode =  static_C_CallUpdate(
                                 nCommandID,
                                 hSession,
                                 pData,
                                 nPartDataLen,
                                 pResult,
                                 &nPartResultLen,
                                 bSend,
                                 bReceive);
      if (nErrorCode != CKR_OK)
      {
         return nErrorCode;
      }

      ulDataLen -= nPartDataLen;
      pData += nPartDataLen;

      if (pResult != NULL)
      {
         ulResultLen -= nPartResultLen;
         pResult += nPartResultLen;
      }

      if ((pulResultLen != NULL) && (bIsSymOperation))
      {
         *pulResultLen += nPartResultLen;
      }
   }
   return CKR_OK;
}

/* Decides whether to split or not the inout/output buffer into chunks
*/
static CK_RV static_C_Call_CallForUpdate(
                           uint32_t           nCommandID,
                           CK_SESSION_HANDLE  hSession,
                           const CK_BYTE*     pData,
                           CK_ULONG           ulDataLen,
                           CK_BYTE*           pResult,
                           CK_ULONG*          pulResultLen,
                           bool               bSend,
                           bool               bReceive)
{
   CK_RV                   nErrorCode;
   uint32_t                nChunkSize;

   TEEC_ImplementationLimits  limits;

   if (!g_bCryptokiInitialized)
   {
      return CKR_CRYPTOKI_NOT_INITIALIZED;
   }

   TEEC_GetImplementationLimits(&limits);

   /* We can split the buffer in chunks of fixed size.
      No matter of the start address of the buffer,
      a safe size would be TotalNumberOfPages - 1
   */
   nChunkSize = limits.tmprefMaxSize - limits.pageSize;

   if (ulDataLen > nChunkSize)
   {
      /* inoutMaxSize = 0  means unlimited size */
       nErrorCode = static_C_CallSplitUpdate(nCommandID,
                                 hSession,
                                 pData,
                                 ulDataLen,
                                 pResult,
                                 pulResultLen,
                                 bSend,
                                 bReceive,
                                 nChunkSize);
   }
   else
   {
      nErrorCode = static_C_CallUpdate(nCommandID,
                                 hSession,
                                 pData,
                                 ulDataLen,
                                 pResult,
                                 pulResultLen,
                                 bSend,
                                 bReceive);
   }
   return nErrorCode;

}

/* ------------------------------------------------------------------------
Public Functions
------------------------------------------------------------------------- */

CK_RV PKCS11_EXPORT C_CreateObject(
   CK_SESSION_HANDLE    hSession,    /* the session's handle */
   const CK_ATTRIBUTE*  pTemplate,   /* the object's template */
   CK_ULONG             ulCount,     /* attributes in template */
   CK_OBJECT_HANDLE*    phObject)    /* receives new object's handle. */
{
   TEEC_Result          teeErr;
   uint32_t             nErrorOrigin;
   TEEC_Operation       sOperation;
   CK_RV                nErrorCode = CKR_OK;
   PPKCS11_PRIMARY_SESSION_CONTEXT pSession;
   uint32_t             nCommandIDAndSession = SERVICE_SYSTEM_PKCS11_C_CREATEOBJECT_COMMAND_ID;
   uint8_t*             pBuffer = NULL;
   uint32_t             nBufferSize = 0;

   if ( pTemplate == NULL || phObject == NULL )
   {
      return CKR_ARGUMENTS_BAD;
   }

   nErrorCode = static_checkPreConditionsAndUpdateHandles(&hSession, &nCommandIDAndSession, &pSession);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   nErrorCode = static_encodeTemplate(&pBuffer, &nBufferSize, 0, (CK_ATTRIBUTE*)pTemplate, ulCount); /* Sets the template on the param 0 */
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   memset(&sOperation, 0, sizeof(TEEC_Operation));
   sOperation.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT, TEEC_VALUE_OUTPUT, TEEC_NONE, TEEC_NONE);
   sOperation.params[0].tmpref.buffer = pBuffer;
   sOperation.params[0].tmpref.size   = nBufferSize;
   teeErr = TEEC_InvokeCommand(   &pSession->sSession,
                                  nCommandIDAndSession,        /* commandID */
                                  &sOperation,                 /* IN OUT operation */
                                  &nErrorOrigin                /* OUT returnOrigin, optional */
                                 );
   free(pBuffer);

   if (teeErr != TEEC_SUCCESS)
   {
      nErrorCode = (nErrorOrigin == TEEC_ORIGIN_TRUSTED_APP ?
                     teeErr :
                     ckInternalTeeErrorToCKError(teeErr));
      goto end;
   }

   *phObject = sOperation.params[1].value.a;

   /* Success */
   nErrorCode = CKR_OK;

end:
   return nErrorCode;
}

CK_RV PKCS11_EXPORT C_DestroyObject(
   CK_SESSION_HANDLE hSession,  /* the session's handle */
   CK_OBJECT_HANDLE  hObject)   /* the object's handle */
{
   TEEC_Result    teeErr;
   uint32_t       nErrorOrigin;
   TEEC_Operation sOperation;
   CK_RV       nErrorCode = CKR_OK;
   uint32_t    nCommandIDAndSession = SERVICE_SYSTEM_PKCS11_C_DESTROYOBJECT_COMMAND_ID;
   PPKCS11_PRIMARY_SESSION_CONTEXT pSession;

   nErrorCode = static_checkPreConditionsAndUpdateHandles(&hSession, &nCommandIDAndSession, &pSession);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   memset(&sOperation, 0, sizeof(TEEC_Operation));
   sOperation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_NONE, TEEC_NONE, TEEC_NONE);
   sOperation.params[0].value.a = (uint32_t)hObject;
   teeErr = TEEC_InvokeCommand(   &pSession->sSession,
                                  nCommandIDAndSession,        /* commandID */
                                  &sOperation,                 /* IN OUT operation */
                                  &nErrorOrigin                /* OUT returnOrigin, optional */
                                 );
   nErrorCode = (nErrorOrigin == TEEC_ORIGIN_TRUSTED_APP ?
                  teeErr :
                  ckInternalTeeErrorToCKError(teeErr));
   return nErrorCode;
}

CK_RV PKCS11_EXPORT C_GetAttributeValue(
   CK_SESSION_HANDLE hSession,   /* the session's handle */
   CK_OBJECT_HANDLE  hObject,    /* the object's handle */
   CK_ATTRIBUTE*     pTemplate,  /* specifies attributes, gets values */
   CK_ULONG          ulCount)    /* attributes in template */
{
   TEEC_Result       teeErr;
   uint32_t          nErrorOrigin;
   TEEC_Operation    sOperation;
   CK_RV             nErrorCode = CKR_OK;
   CK_RV             nFinalErrorCode = CKR_OK;
   uint32_t          i = 0;
   uint32_t          nCommandIDAndSession = SERVICE_SYSTEM_PKCS11_C_GETATTRIBUTEVALUE_COMMAND_ID;
   PPKCS11_PRIMARY_SESSION_CONTEXT pSession;

   nErrorCode = static_checkPreConditionsAndUpdateHandles(&hSession, &nCommandIDAndSession, &pSession);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   if (pTemplate == NULL)
   {
      return CKR_ARGUMENTS_BAD;
   }

   if (ulCount == 0)
   {
      return CKR_OK;
   }

   for (i = 0; i < ulCount; i++)
   {
      memset(&sOperation, 0, sizeof(TEEC_Operation));
      sOperation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_MEMREF_TEMP_OUTPUT, TEEC_NONE, TEEC_NONE);
      sOperation.params[0].value.a = (uint32_t)hObject;
      sOperation.params[0].value.b = (uint32_t)pTemplate[i].type;
      sOperation.params[1].tmpref.buffer = pTemplate[i].pValue;
      sOperation.params[1].tmpref.size   = pTemplate[i].ulValueLen;
      teeErr = TEEC_InvokeCommand(   &pSession->sSession,
                                     nCommandIDAndSession,        /* commandID */
                                     &sOperation,                 /* IN OUT operation */
                                     &nErrorOrigin                /* OUT returnOrigin, optional */
                                    );
      nErrorCode = (nErrorOrigin == TEEC_ORIGIN_TRUSTED_APP ?
                     teeErr :
                     ckInternalTeeErrorToCKError(teeErr));
      if (nErrorCode != CKR_OK)
      {
         if (  (nErrorCode == CKR_ATTRIBUTE_SENSITIVE) ||
               (nErrorCode == CKR_ATTRIBUTE_TYPE_INVALID) ||
               (nErrorCode == CKR_BUFFER_TOO_SMALL))
         {
            nFinalErrorCode = nErrorCode;
         }
         else
         {
            /* Not some of the special error codes: this is fatal */
            return nErrorCode;
         }
      }

      pTemplate[i].ulValueLen = sOperation.params[1].tmpref.size;
   }

   return nFinalErrorCode;
}

CK_RV PKCS11_EXPORT C_FindObjectsInit(
   CK_SESSION_HANDLE    hSession,   /* the session's handle */
   const CK_ATTRIBUTE*  pTemplate,  /* attribute values to match */
   CK_ULONG             ulCount)    /* attributes in search template */
{
   TEEC_Result    teeErr;
   uint32_t       nErrorOrigin;
   TEEC_Operation sOperation;
   CK_RV       nErrorCode = CKR_OK;
   PPKCS11_PRIMARY_SESSION_CONTEXT pSession;
   uint32_t    nCommandIDAndSession = SERVICE_SYSTEM_PKCS11_C_FINDOBJECTSINIT_COMMAND_ID;
   uint8_t*    pBuffer     = NULL;
   uint32_t    nBufferSize = 0;

   nErrorCode = static_checkPreConditionsAndUpdateHandles(&hSession, &nCommandIDAndSession, &pSession);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   nErrorCode = static_encodeTemplate(&pBuffer, &nBufferSize, 0, (CK_ATTRIBUTE*)pTemplate, ulCount); /* Sets the template on the param 0 */
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   memset(&sOperation, 0, sizeof(TEEC_Operation));
   sOperation.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT, TEEC_NONE, TEEC_NONE, TEEC_NONE);
   sOperation.params[0].tmpref.buffer = pBuffer;
   sOperation.params[0].tmpref.size   = nBufferSize;
   teeErr = TEEC_InvokeCommand(   &pSession->sSession,
                                  nCommandIDAndSession,        /* commandID */
                                  &sOperation,                 /* IN OUT operation */
                                  &nErrorOrigin                /* OUT returnOrigin, optional */
                                 );
   free(pBuffer);

   nErrorCode = (nErrorOrigin == TEEC_ORIGIN_TRUSTED_APP ?
                  teeErr :
                  ckInternalTeeErrorToCKError(teeErr));
   return nErrorCode;
}


CK_RV PKCS11_EXPORT C_FindObjects(
   CK_SESSION_HANDLE hSession,          /* the session's handle */
   CK_OBJECT_HANDLE* phObject,          /* receives object handle array */
   CK_ULONG          ulMaxObjectCount,  /* max handles to be returned */
   CK_ULONG*         pulObjectCount)    /* actual number returned */
{
   TEEC_Result       teeErr;
   uint32_t          nErrorOrigin;
   TEEC_Operation    sOperation;
   CK_RV             nErrorCode = CKR_OK;
   PPKCS11_PRIMARY_SESSION_CONTEXT pSession;
   uint32_t          nCommandIDAndSession = SERVICE_SYSTEM_PKCS11_C_FINDOBJECTS_COMMAND_ID;

   if ( (phObject == NULL) || (pulObjectCount == NULL))
   {
      return CKR_ARGUMENTS_BAD;
   }

   *pulObjectCount = 0;

   nErrorCode = static_checkPreConditionsAndUpdateHandles(&hSession, &nCommandIDAndSession, &pSession);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   memset(&sOperation, 0, sizeof(TEEC_Operation));
   sOperation.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_OUTPUT, TEEC_NONE, TEEC_NONE, TEEC_NONE);
   sOperation.params[0].tmpref.buffer = (uint8_t*)phObject;
   sOperation.params[0].tmpref.size   = (uint32_t)ulMaxObjectCount * sizeof(uint32_t);

   teeErr = TEEC_InvokeCommand(   &pSession->sSession,
                                  nCommandIDAndSession,        /* commandID */
                                  &sOperation,                 /* IN OUT operation */
                                  &nErrorOrigin                /* OUT returnOrigin, optional */
                                 );

   if (teeErr != TEEC_SUCCESS)
   {
      nErrorCode = (nErrorOrigin == TEEC_ORIGIN_TRUSTED_APP ?
                     teeErr :
                     ckInternalTeeErrorToCKError(teeErr));
      return nErrorCode;
   }

   *pulObjectCount = sOperation.params[0].tmpref.size / sizeof(uint32_t);

   return CKR_OK;
}

CK_RV PKCS11_EXPORT C_FindObjectsFinal(CK_SESSION_HANDLE hSession) /* the session's handle */
{
   TEEC_Result    teeErr;
   uint32_t       nErrorOrigin;
   TEEC_Operation sOperation;
   CK_RV          nErrorCode = CKR_OK;
   uint32_t       nCommandIDAndSession = SERVICE_SYSTEM_PKCS11_C_FINDOBJECTSFINAL_COMMAND_ID;
   PPKCS11_PRIMARY_SESSION_CONTEXT pSession;

   nErrorCode = static_checkPreConditionsAndUpdateHandles(&hSession, &nCommandIDAndSession, &pSession);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   memset(&sOperation, 0, sizeof(TEEC_Operation));
   teeErr = TEEC_InvokeCommand(   &pSession->sSession,
                                  nCommandIDAndSession,        /* commandID */
                                  &sOperation,                 /* IN OUT operation */
                                  &nErrorOrigin                /* OUT returnOrigin, optional */
                                 );

   nErrorCode = (nErrorOrigin == TEEC_ORIGIN_TRUSTED_APP ?
                  teeErr :
                  ckInternalTeeErrorToCKError(teeErr));
   return nErrorCode;
}


CK_RV PKCS11_EXPORT C_DigestInit(
   CK_SESSION_HANDLE   hSession,   /* the session's handle */
   const CK_MECHANISM* pMechanism) /* the digesting mechanism */
{
   return static_C_CallInit(
      SERVICE_SYSTEM_PKCS11_C_DIGESTINIT_COMMAND_ID,
      hSession,
      pMechanism,
      CK_INVALID_HANDLE);
}

CK_RV PKCS11_EXPORT C_Digest(
   CK_SESSION_HANDLE hSession,     /* the session's handle */
   const CK_BYTE*    pData,        /* data to be digested */
   CK_ULONG          ulDataLen,    /* bytes of data to be digested */
   CK_BYTE*          pDigest,      /* receives the message digest */
   CK_ULONG*         pulDigestLen) /* receives byte length of digest */
{
   return static_C_CallForSingle(
      SERVICE_SYSTEM_PKCS11_C_DIGEST_COMMAND_ID,
      hSession,
      pData,
      ulDataLen,
      pDigest,
      pulDigestLen,
      TRUE,
      TRUE);
}

CK_RV PKCS11_EXPORT C_DigestUpdate(
   CK_SESSION_HANDLE hSession,  /* the session's handle */
   const CK_BYTE*    pPart,     /* data to be digested */
   CK_ULONG          ulPartLen) /* bytes of data to be digested */
{
   return static_C_Call_CallForUpdate(
      SERVICE_SYSTEM_PKCS11_C_DIGESTUPDATE_COMMAND_ID,
      hSession,
      pPart,
      ulPartLen,
      NULL,
      NULL,
      TRUE,
      FALSE);
}

CK_RV PKCS11_EXPORT C_DigestFinal(
   CK_SESSION_HANDLE hSession,  /* the session's handle */
   CK_BYTE*       pDigest,      /* receives the message digest */
   CK_ULONG*      pulDigestLen) /* receives byte count of digest */
{
   return static_C_Call_CallForUpdate(
      SERVICE_SYSTEM_PKCS11_C_DIGESTFINAL_COMMAND_ID,
      hSession,
      NULL,
      0,
      pDigest,
      pulDigestLen,
      FALSE,
      TRUE);
}


CK_RV PKCS11_EXPORT C_SignInit(
   CK_SESSION_HANDLE    hSession,    /* the session's handle */
   const CK_MECHANISM*  pMechanism,  /* the signature mechanism */
   CK_OBJECT_HANDLE     hKey)        /* handle of the signature key */
{
   return static_C_CallInit(
      SERVICE_SYSTEM_PKCS11_C_SIGNINIT_COMMAND_ID,
      hSession,
      pMechanism,
      hKey);
}

CK_RV PKCS11_EXPORT C_Sign(
   CK_SESSION_HANDLE hSession,        /* the session's handle */
   const CK_BYTE*    pData,           /* the data (digest) to be signed */
   CK_ULONG          ulDataLen,       /* count of bytes to be signed */
   CK_BYTE*          pSignature,      /* receives the signature */
   CK_ULONG*         pulSignatureLen) /* receives byte count of signature */
{
   return static_C_CallForSingle(
      SERVICE_SYSTEM_PKCS11_C_SIGN_COMMAND_ID,
      hSession,
      pData,
      ulDataLen,
      pSignature,
      pulSignatureLen,
      TRUE,
      TRUE);
}

CK_RV PKCS11_EXPORT C_SignUpdate(
   CK_SESSION_HANDLE hSession,  /* the session's handle */
   const CK_BYTE*    pPart,     /* the data (digest) to be signed */
   CK_ULONG          ulPartLen) /* count of bytes to be signed */
{
   return static_C_Call_CallForUpdate(
      SERVICE_SYSTEM_PKCS11_C_SIGNUPDATE_COMMAND_ID,
      hSession,
      pPart,
      ulPartLen,
      NULL,
      NULL,
      TRUE,
      FALSE);
}

CK_RV PKCS11_EXPORT C_SignFinal(
   CK_SESSION_HANDLE hSession,     /* the session's handle */
   CK_BYTE*       pSignature,      /* receives the signature */
   CK_ULONG*      pulSignatureLen) /* receives byte count of signature */
{
   return static_C_Call_CallForUpdate(
      SERVICE_SYSTEM_PKCS11_C_SIGNFINAL_COMMAND_ID,
      hSession,
      NULL,
      0,
      pSignature,
      pulSignatureLen,
      FALSE,
      TRUE);
}

CK_RV PKCS11_EXPORT C_EncryptInit(
   CK_SESSION_HANDLE   hSession,    /* the session's handle */
   const CK_MECHANISM* pMechanism,  /* the encryption mechanism */
   CK_OBJECT_HANDLE    hKey)        /* handle of encryption key */
{
   return static_C_CallInit(
      SERVICE_SYSTEM_PKCS11_C_ENCRYPTINIT_COMMAND_ID,
      hSession,
      pMechanism,
      hKey);
}

CK_RV PKCS11_EXPORT C_Encrypt(
   CK_SESSION_HANDLE hSession,            /* the session's handle */
   const CK_BYTE*    pData,               /* the plaintext data */
   CK_ULONG          ulDataLen,           /* bytes of plaintext data */
   CK_BYTE*          pEncryptedData,      /* receives encrypted data */
   CK_ULONG*         pulEncryptedDataLen) /* receives encrypted byte count */
{

   return static_C_CallForSingle(
      SERVICE_SYSTEM_PKCS11_C_ENCRYPT_COMMAND_ID,
      hSession,
      pData,
      ulDataLen,
      pEncryptedData,
      pulEncryptedDataLen,
      TRUE,
      TRUE);
}



CK_RV PKCS11_EXPORT C_EncryptUpdate(
   CK_SESSION_HANDLE hSession,           /* the session's handle */
   const CK_BYTE*    pPart,              /* the plaintext data */
   CK_ULONG          ulPartLen,          /* bytes of plaintext data */
   CK_BYTE*          pEncryptedPart,     /* receives encrypted data */
   CK_ULONG*         pulEncryptedPartLen)/* receives encrypted byte count */
{
   return static_C_Call_CallForUpdate(
      SERVICE_SYSTEM_PKCS11_C_ENCRYPTUPDATE_COMMAND_ID,
      hSession,
      pPart,
      ulPartLen,
      pEncryptedPart,
      pulEncryptedPartLen,
      TRUE,
      TRUE);
}

CK_RV PKCS11_EXPORT C_EncryptFinal(
   CK_SESSION_HANDLE hSession,             /* the session's handle */
   CK_BYTE*       pLastEncryptedPart,      /* receives encrypted last part */
   CK_ULONG*      pulLastEncryptedPartLen) /* receives byte count */
{
   return static_C_Call_CallForUpdate(
      SERVICE_SYSTEM_PKCS11_C_ENCRYPTFINAL_COMMAND_ID,
      hSession,
      NULL,
      0,
      pLastEncryptedPart,
      pulLastEncryptedPartLen,
      FALSE,
      TRUE);
}

CK_RV PKCS11_EXPORT C_DecryptInit(
   CK_SESSION_HANDLE   hSession,    /* the session's handle */
   const CK_MECHANISM* pMechanism,  /* the decryption mechanism */
   CK_OBJECT_HANDLE    hKey)        /* handle of the decryption key */
{
   return static_C_CallInit(
      SERVICE_SYSTEM_PKCS11_C_DECRYPTINIT_COMMAND_ID,
      hSession,
      pMechanism,
      hKey);
}

CK_RV PKCS11_EXPORT C_Decrypt(
   CK_SESSION_HANDLE hSession,           /* the session's handle */
   const CK_BYTE*    pEncryptedData,     /* input encrypted data */
   CK_ULONG          ulEncryptedDataLen, /* count of bytes of input */
   CK_BYTE*          pData,              /* receives decrypted output */
   CK_ULONG*         pulDataLen)         /* receives decrypted byte count */
{

   return static_C_CallForSingle(
      SERVICE_SYSTEM_PKCS11_C_DECRYPT_COMMAND_ID,
      hSession,
      pEncryptedData,
      ulEncryptedDataLen,
      pData,
      pulDataLen,
      TRUE,
      TRUE);
}

CK_RV PKCS11_EXPORT C_DecryptUpdate(
   CK_SESSION_HANDLE hSession,            /* the session's handle */
   const CK_BYTE*    pEncryptedPart,      /* input encrypted data */
   CK_ULONG          ulEncryptedPartLen,  /* count of bytes of input */
   CK_BYTE*          pPart,               /* receives decrypted output */
   CK_ULONG*         pulPartLen)          /* receives decrypted byte count */
{
   return static_C_Call_CallForUpdate(
      SERVICE_SYSTEM_PKCS11_C_DECRYPTUPDATE_COMMAND_ID,
      hSession,
      pEncryptedPart,
      ulEncryptedPartLen,
      pPart,
      pulPartLen,
      TRUE,
      TRUE);
}

CK_RV PKCS11_EXPORT C_DecryptFinal(
   CK_SESSION_HANDLE hSession,    /* the session's handle */
   CK_BYTE*       pLastPart,      /* receives decrypted output */
   CK_ULONG*      pulLastPartLen) /* receives decrypted byte count */
{
   return static_C_Call_CallForUpdate(
      SERVICE_SYSTEM_PKCS11_C_DECRYPTFINAL_COMMAND_ID,
      hSession,
      NULL,
      0,
      pLastPart,
      pulLastPartLen,
      FALSE,
      TRUE);
}


CK_RV PKCS11_EXPORT C_GenerateKey(
   CK_SESSION_HANDLE    hSession,    /* the session's handle */
   const CK_MECHANISM*  pMechanism,  /* the key generation mechanism */
   const CK_ATTRIBUTE*  pTemplate,   /* template for the new key */
   CK_ULONG             ulCount,     /* number of attributes in template */
   CK_OBJECT_HANDLE*    phKey)       /* receives handle of new key */
{
   TEEC_Result    teeErr;
   uint32_t       nErrorOrigin;
   TEEC_Operation sOperation;
   CK_RV       nErrorCode = CKR_OK;
   uint32_t    nCommandIDAndSession = SERVICE_SYSTEM_PKCS11_C_GENERATEKEY_COMMAND_ID;
   uint8_t*    pBuffer     = NULL;
   uint32_t    nBufferSize = 0;
   PPKCS11_PRIMARY_SESSION_CONTEXT pSession;

   if ((pMechanism == NULL) || (phKey == NULL) || (pTemplate == NULL))
   {
      return CKR_ARGUMENTS_BAD;
   }

   nErrorCode = static_checkPreConditionsAndUpdateHandles(&hSession, &nCommandIDAndSession, &pSession);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   nErrorCode = static_encodeTemplate(&pBuffer, &nBufferSize, 2, (CK_ATTRIBUTE*)pTemplate, ulCount);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   memset(&sOperation, 0, sizeof(TEEC_Operation));
   sOperation.params[0].value.a = (uint32_t)pMechanism->mechanism;
   sOperation.params[0].value.b = 0;
   sOperation.params[1].tmpref.buffer = pMechanism->pParameter;
   sOperation.params[1].tmpref.size = (uint32_t)pMechanism->ulParameterLen;
   sOperation.params[2].tmpref.buffer = pBuffer;
   sOperation.params[2].tmpref.size = nBufferSize;
   sOperation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT, TEEC_MEMREF_TEMP_INPUT, TEEC_MEMREF_TEMP_INPUT, TEEC_NONE);
   teeErr = TEEC_InvokeCommand(   &pSession->sSession,
                                  nCommandIDAndSession,        /* commandID */
                                  &sOperation,                 /* IN OUT operation */
                                  &nErrorOrigin                /* OUT returnOrigin, optional */
                                 );
   free(pBuffer);

   if (teeErr != TEEC_SUCCESS)
   {
      nErrorCode = (nErrorOrigin == TEEC_ORIGIN_TRUSTED_APP ?
                     teeErr :
                     ckInternalTeeErrorToCKError(teeErr));
      return nErrorCode;
   }

   *phKey = sOperation.params[0].value.a;

   return CKR_OK;
}

CK_RV PKCS11_EXPORT C_GenerateKeyPair(
   CK_SESSION_HANDLE    hSession,                    /* the session's handle */
   const CK_MECHANISM*  pMechanism,                  /* the key gen. mech. */
   const CK_ATTRIBUTE*  pPublicKeyTemplate,          /* pub. attr. template */
   CK_ULONG             ulPublicKeyAttributeCount,   /* # of pub. attrs. */
   const CK_ATTRIBUTE*  pPrivateKeyTemplate,         /* priv. attr. template */
   CK_ULONG             ulPrivateKeyAttributeCount,  /* # of priv. attrs. */
   CK_OBJECT_HANDLE*    phPublicKey,                 /* gets pub. key handle */
   CK_OBJECT_HANDLE*    phPrivateKey)                /* gets priv. key handle */
{
   TEEC_Result    teeErr;
   uint32_t       nErrorOrigin;
   TEEC_Operation sOperation;
   CK_RV       nErrorCode = CKR_OK;
   uint32_t    nCommandIDAndSession = SERVICE_SYSTEM_PKCS11_C_GENERATEKEYPAIR_COMMAND_ID;
   uint8_t*    pBuffer     = NULL;
   uint32_t    nBufferSize = 0;
   PPKCS11_PRIMARY_SESSION_CONTEXT pSession;

   if (  (pMechanism == NULL) ||
         (pPublicKeyTemplate == NULL) || (pPrivateKeyTemplate == NULL) ||
         (phPublicKey== NULL) || (phPrivateKey== NULL))
   {
      return CKR_ARGUMENTS_BAD;
   }

   nErrorCode = static_checkPreConditionsAndUpdateHandles(&hSession, &nCommandIDAndSession, &pSession);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   nErrorCode = static_encodeTwoTemplates(&pBuffer, &nBufferSize, 2, (CK_ATTRIBUTE*)pPublicKeyTemplate, ulPublicKeyAttributeCount, (CK_ATTRIBUTE*)pPrivateKeyTemplate, ulPrivateKeyAttributeCount);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   memset(&sOperation, 0, sizeof(TEEC_Operation));
   sOperation.params[0].value.a = (uint32_t)pMechanism->mechanism;
   sOperation.params[0].value.b = 0;
   sOperation.params[1].tmpref.buffer = (uint8_t*)pMechanism->pParameter;
   sOperation.params[1].tmpref.size = (uint32_t)pMechanism->ulParameterLen;
   sOperation.params[2].tmpref.buffer = pBuffer;
   sOperation.params[2].tmpref.size = nBufferSize;
   sOperation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT, TEEC_MEMREF_TEMP_INPUT, TEEC_MEMREF_TEMP_INPUT, TEEC_NONE);
   teeErr = TEEC_InvokeCommand(   &pSession->sSession,
                                  nCommandIDAndSession,        /* commandID */
                                  &sOperation,                 /* IN OUT operation */
                                  &nErrorOrigin                /* OUT returnOrigin, optional */
                                 );
   free(pBuffer);

   if (teeErr != TEEC_SUCCESS)
   {
      nErrorCode = (nErrorOrigin == TEEC_ORIGIN_TRUSTED_APP ?
                     teeErr :
                     ckInternalTeeErrorToCKError(teeErr));
      return nErrorCode;
   }

   *phPublicKey  = sOperation.params[0].value.a;
   *phPrivateKey = sOperation.params[0].value.b;

   return CKR_OK;
}

CK_RV PKCS11_EXPORT C_DeriveKey(
   CK_SESSION_HANDLE    hSession,          /* session's handle */
   const CK_MECHANISM*  pMechanism,        /* key deriv. mech. */
   CK_OBJECT_HANDLE     hBaseKey,          /* base key */
   const CK_ATTRIBUTE*  pTemplate,         /* new key template */
   CK_ULONG             ulAttributeCount,  /* template length */
   CK_OBJECT_HANDLE*    phKey)             /* gets new handle */
{
   TEEC_Result    teeErr;
   uint32_t       nErrorOrigin;
   TEEC_Operation sOperation;
   CK_RV       nErrorCode = CKR_OK;
   uint32_t    nCommandIDAndSession = SERVICE_SYSTEM_PKCS11_C_DERIVEKEY_COMMAND_ID;
   uint8_t*    pBuffer     = NULL;
   uint32_t    nBufferSize = 0;
   PPKCS11_PRIMARY_SESSION_CONTEXT pSession;

   if ((pMechanism == NULL) || (pTemplate == NULL) || (phKey == NULL))
   {
      return CKR_ARGUMENTS_BAD;
   }

   nErrorCode = static_checkPreConditionsAndUpdateHandles(&hSession, &nCommandIDAndSession, &pSession);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   nErrorCode = static_encodeTemplate(&pBuffer, &nBufferSize, 2, (CK_ATTRIBUTE*)pTemplate, ulAttributeCount);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   memset(&sOperation, 0, sizeof(TEEC_Operation));
   sOperation.params[0].value.a = (uint32_t)pMechanism->mechanism;
   sOperation.params[0].value.b = (uint32_t)hBaseKey;
   sOperation.params[1].tmpref.buffer = (uint8_t*)pMechanism->pParameter;
   sOperation.params[1].tmpref.size = (uint32_t)pMechanism->ulParameterLen;
   sOperation.params[2].tmpref.buffer = pBuffer;
   sOperation.params[2].tmpref.size = nBufferSize;
   sOperation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT, TEEC_MEMREF_TEMP_INPUT, TEEC_MEMREF_TEMP_INPUT, TEEC_NONE);
   teeErr = TEEC_InvokeCommand(   &pSession->sSession,
                                  nCommandIDAndSession,        /* commandID */
                                  &sOperation,                 /* IN OUT operation */
                                  &nErrorOrigin                /* OUT returnOrigin, optional */
                                 );
   free(pBuffer);

   if (teeErr != TEEC_SUCCESS)
   {
      nErrorCode = (nErrorOrigin == TEEC_ORIGIN_TRUSTED_APP ?
                     teeErr :
                     ckInternalTeeErrorToCKError(teeErr));
      return nErrorCode;
   }

   *phKey = sOperation.params[0].value.a;

   return CKR_OK;
}

CK_RV PKCS11_EXPORT C_SeedRandom(
   CK_SESSION_HANDLE hSession,  /* the session's handle */
   const CK_BYTE*    pSeed,     /* the seed material */
   CK_ULONG          ulSeedLen) /* count of bytes of seed material */
{
   TEEC_Result    teeErr;
   uint32_t       nErrorOrigin;
   TEEC_Operation sOperation;
   CK_RV       nErrorCode = CKR_OK;
   uint32_t    nCommandIDAndSession = SERVICE_SYSTEM_PKCS11_C_SEEDRANDOM_COMMAND_ID;
   PPKCS11_PRIMARY_SESSION_CONTEXT pSession;

   nErrorCode = static_checkPreConditionsAndUpdateHandles(&hSession, &nCommandIDAndSession, &pSession);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }
   memset(&sOperation, 0, sizeof(TEEC_Operation));
   sOperation.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT, TEEC_NONE, TEEC_NONE, TEEC_NONE);
   sOperation.params[0].tmpref.buffer = (uint8_t*)pSeed;
   sOperation.params[0].tmpref.size   = (uint32_t)ulSeedLen;
   teeErr = TEEC_InvokeCommand(   &pSession->sSession,
                                  nCommandIDAndSession,        /* commandID */
                                  &sOperation,                 /* IN OUT operation */
                                  &nErrorOrigin                /* OUT returnOrigin, optional */
                                 );

   nErrorCode = (nErrorOrigin == TEEC_ORIGIN_TRUSTED_APP ?
                  teeErr :
                  ckInternalTeeErrorToCKError(teeErr));
   return nErrorCode;
}

CK_RV PKCS11_EXPORT C_GenerateRandom(
   CK_SESSION_HANDLE hSession,    /* the session's handle */
   CK_BYTE*          pRandomData,  /* receives the random data */
   CK_ULONG          ulRandomLen) /* number of bytes to be generated */
{
   TEEC_Result    teeErr;
   uint32_t       nErrorOrigin;
   TEEC_Operation sOperation;
   CK_RV       nErrorCode = CKR_OK;
   uint32_t    nCommandIDAndSession = SERVICE_SYSTEM_PKCS11_C_GENERATERANDOM_COMMAND_ID;
   PPKCS11_PRIMARY_SESSION_CONTEXT pSession;

   nErrorCode = static_checkPreConditionsAndUpdateHandles(&hSession, &nCommandIDAndSession, &pSession);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   do
   {
      CK_ULONG nArrayLength;
      nArrayLength = 1024;
      if (ulRandomLen < nArrayLength)
      {
         nArrayLength = ulRandomLen;
      }
      memset(&sOperation, 0, sizeof(TEEC_Operation));
      sOperation.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_OUTPUT, TEEC_NONE, TEEC_NONE, TEEC_NONE);
      sOperation.params[0].tmpref.buffer = (uint8_t*)pRandomData;
      sOperation.params[0].tmpref.size   = (uint32_t)nArrayLength;
      teeErr = TEEC_InvokeCommand(   &pSession->sSession,
                                     nCommandIDAndSession,        /* commandID */
                                     &sOperation,                 /* IN OUT operation */
                                     &nErrorOrigin                /* OUT returnOrigin, optional */
                                    );
      if (teeErr != TEEC_SUCCESS)
      {
         nErrorCode = (nErrorOrigin == TEEC_ORIGIN_TRUSTED_APP ?
                        teeErr :
                        ckInternalTeeErrorToCKError(teeErr));
         return nErrorCode;
      }

      ulRandomLen -= nArrayLength;
      pRandomData += nArrayLength;
      if (ulRandomLen == 0)
      {
         break;
      }
   }
   while(1);

   return CKR_OK;
}

CK_RV PKCS11_EXPORT C_VerifyInit(
   CK_SESSION_HANDLE   hSession,    /* the session's handle */
   const CK_MECHANISM* pMechanism,  /* the verification mechanism */
   CK_OBJECT_HANDLE    hKey)        /* handle of the verification key */
{
   return static_C_CallInit(
      SERVICE_SYSTEM_PKCS11_C_VERIFYINIT_COMMAND_ID,
      hSession,
      pMechanism,
      hKey);
}

CK_RV PKCS11_EXPORT C_Verify(
   CK_SESSION_HANDLE hSession,       /* the session's handle */
   const CK_BYTE*    pData,          /* plaintext data (digest) to compare */
   CK_ULONG          ulDataLen,      /* length of data (digest) in bytes */
   CK_BYTE*          pSignature,     /* the signature to be verified */
   CK_ULONG          ulSignatureLen) /* count of bytes of signature */
{
   TEEC_Result    teeErr;
   uint32_t       nErrorOrigin;
   TEEC_Operation sOperation;
   CK_RV       nErrorCode = CKR_OK;
   uint32_t    nCommandIDAndSession = SERVICE_SYSTEM_PKCS11_C_VERIFY_COMMAND_ID;
   PPKCS11_PRIMARY_SESSION_CONTEXT pSession;

   nErrorCode = static_checkPreConditionsAndUpdateHandles(&hSession, &nCommandIDAndSession, &pSession);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   memset(&sOperation, 0, sizeof(TEEC_Operation));
   sOperation.params[0].tmpref.buffer = (uint8_t*)pData;
   sOperation.params[0].tmpref.size   = (uint32_t)ulDataLen;
   sOperation.params[1].tmpref.buffer = (uint8_t*)pSignature;
   sOperation.params[1].tmpref.size   = (uint32_t)ulSignatureLen;
   sOperation.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT, TEEC_MEMREF_TEMP_INPUT, TEEC_NONE, TEEC_NONE);
   teeErr = TEEC_InvokeCommand(   &pSession->sSession,
                                  nCommandIDAndSession,        /* commandID */
                                  &sOperation,                 /* IN OUT operation */
                                  &nErrorOrigin                /* OUT returnOrigin, optional */
                                 );
   nErrorCode = (nErrorOrigin == TEEC_ORIGIN_TRUSTED_APP ?
                  teeErr :
                  ckInternalTeeErrorToCKError(teeErr));
   return nErrorCode;
}

CK_RV PKCS11_EXPORT C_VerifyUpdate(
   CK_SESSION_HANDLE hSession,  /* the session's handle */
   const CK_BYTE*    pPart,     /* plaintext data (digest) to compare */
   CK_ULONG          ulPartLen) /* length of data (digest) in bytes */
{
   return static_C_Call_CallForUpdate(
      SERVICE_SYSTEM_PKCS11_C_VERIFYUPDATE_COMMAND_ID,
      hSession,
      pPart,
      ulPartLen,
      NULL,
      NULL,
      TRUE,
      FALSE);
}

CK_RV PKCS11_EXPORT C_VerifyFinal(
   CK_SESSION_HANDLE hSession,       /* the session's handle */
   const CK_BYTE*    pSignature,     /* the signature to be verified */
   CK_ULONG          ulSignatureLen) /* count of bytes of signature */
{
   return static_C_Call_CallForUpdate(
      SERVICE_SYSTEM_PKCS11_C_VERIFYFINAL_COMMAND_ID,
      hSession,
      pSignature,
      ulSignatureLen,
      NULL,
      NULL,
      TRUE,
      FALSE);
}

CK_RV PKCS11_EXPORT C_CloseObjectHandle(
   CK_SESSION_HANDLE hSession,  /* the session's handle */
   CK_OBJECT_HANDLE  hObject)   /* the object's handle */
{
   TEEC_Result    teeErr;
   uint32_t       nErrorOrigin;
   TEEC_Operation sOperation;
   CK_RV       nErrorCode = CKR_OK;
   uint32_t    nCommandIDAndSession = SERVICE_SYSTEM_PKCS11_C_CLOSEOBJECTHANDLE_COMMAND_ID;
   PPKCS11_PRIMARY_SESSION_CONTEXT pSession;

   nErrorCode = static_checkPreConditionsAndUpdateHandles(&hSession, &nCommandIDAndSession, &pSession);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }
   memset(&sOperation, 0, sizeof(TEEC_Operation));
   sOperation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_NONE, TEEC_NONE, TEEC_NONE);
   sOperation.params[0].value.a = (uint32_t)hObject;
   sOperation.params[0].value.b = 0;
   teeErr = TEEC_InvokeCommand(   &pSession->sSession,
                                  nCommandIDAndSession,        /* commandID */
                                  &sOperation,                 /* IN OUT operation */
                                  &nErrorOrigin                /* OUT returnOrigin, optional */
                                 );
   nErrorCode = (nErrorOrigin == TEEC_ORIGIN_TRUSTED_APP ?
                  teeErr :
                  ckInternalTeeErrorToCKError(teeErr));
   return nErrorCode;
}

CK_RV PKCS11_EXPORT C_CopyObject(
         CK_SESSION_HANDLE    hSession,    /* the session's handle */
         CK_OBJECT_HANDLE     hObject,     /* the source object's handle */
   const CK_ATTRIBUTE*        pTemplate,   /* the template of the copied object */
         CK_ULONG             ulCount,     /* the number of attributes of the template*/
         CK_OBJECT_HANDLE*    phNewObject) /* the copied object's handle */
{
   TEEC_Result    teeErr;
   uint32_t       nErrorOrigin;
   TEEC_Operation sOperation;
   CK_RV       nErrorCode = CKR_OK;
   uint32_t    nCommandIDAndSession = SERVICE_SYSTEM_PKCS11_C_COPYOBJECT_COMMAND_ID;
   uint8_t*    pBuffer     = NULL;
   uint32_t    nBufferSize = 0;
   PPKCS11_PRIMARY_SESSION_CONTEXT pSession;

   if ((pTemplate == NULL) || (phNewObject == NULL))
   {
      return CKR_ARGUMENTS_BAD;
   }

   nErrorCode = static_checkPreConditionsAndUpdateHandles(&hSession, &nCommandIDAndSession, &pSession);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   nErrorCode = static_encodeTemplate(&pBuffer, &nBufferSize, 1, (CK_ATTRIBUTE*)pTemplate, ulCount);
   if (nErrorCode != CKR_OK)
   {
      return nErrorCode;
   }

   memset(&sOperation, 0, sizeof(TEEC_Operation));
   sOperation.params[0].value.a = (uint32_t)hObject;
   sOperation.params[0].value.b = 0;
   sOperation.params[1].tmpref.buffer = pBuffer;
   sOperation.params[1].tmpref.size   = nBufferSize;
   sOperation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT, TEEC_MEMREF_TEMP_INPUT, TEEC_NONE, TEEC_NONE);
   teeErr = TEEC_InvokeCommand(   &pSession->sSession,
                                  nCommandIDAndSession,        /* commandID */
                                  &sOperation,                 /* IN OUT operation */
                                  &nErrorOrigin                /* OUT returnOrigin, optional */
                                 );
   free(pBuffer);

   if (teeErr != TEEC_SUCCESS)
   {
      nErrorCode = (nErrorOrigin == TEEC_ORIGIN_TRUSTED_APP ?
                     teeErr :
                     ckInternalTeeErrorToCKError(teeErr));
      return nErrorCode;
   }

   *phNewObject = sOperation.params[0].value.a;

   return CKR_OK;
}
