
  /*
   *  Object %name    : %
   *  State           :  %state%
   *  Creation date   :  Thu Dec 02 15:35:52 2004
   *  Last modified   :  %modify_time%
   */
  /** @file
   *  \brief A brief description of this module
   *
   *  \version CRYS_HMAC.c#1:csrc:4
   *  \author adams
   *  \remarks Copyright (C) 2004 by Discretix Technologies Ltd.
   *           All Rights reserved
   */



/************* Include Files ****************/

#include "DX_VOS_Mem.h"
#include "DX_VOS_Memmap.h"
#include "CRYS_HMAC_error.h"
#include "CRYS_HASH.h"
#include "CRYS_HMAC.h"
#include "error.h"
#include "crys_host_op_code.h"
#include "SEPDriver.h"
#include "gen.h"

/************************ Defines ******************************/


/************************ macros ******************************/


/************************ Typedefs ******************************/


/* the MAC key IPAD and OPAD bytes */
#define MAC_KEY_IPAD_BYTE 0x36
#define MAC_KEY_OPAD_BYTE 0x5C

/************************ Global Data ******************************/

#ifndef CRYS_NO_EXT_IF_MODE_SUPPORT
extern CRYSError_t  CRYS_CALLBACK_Handler();
#else
#define CRYS_CALLBACK_Handler DX_NULL
#endif

/************* Private function prototype ****************/


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

/**
 * This function initializes the HMAC machine on the CRYS level.
 *
 * This function allocates and initializes the HMAC Context .
 * The function receives as input a pointer to store the context handle to HMAC Context. 
 *
 * If the key is larger then 64 bytes it performs on it a HASH operation.
 * then the function executed a HASH_init session and processes a HASH update 
 * on the Key XOR ipad and stores it on the context.
 *
 * the context. 
 * 
 * @param[in] ContextID_ptr - a pointer to the HMAC context buffer allocated by the user that
 *                       is used for the HMAC machine operation.
 *
 * @param[in] OperationMode - The operation mode : MD5 or SHA1.
 *
 * @param[in] key_ptr - The pointer to the users key buffer.
 *
 * @oaram[in] keySize - The size of the received key.
 *
 * @return CRYSError_t on success the function returns CRYS_OK else non ZERO error.
 *      
 */
CEXPORT_C CRYSError_t CRYS_HMAC_Init(CRYS_HMACUserContext_t*    ContextID_ptr,
                                     CRYS_HASH_OperationMode_t  OperationMode,
                                     DxUint8_t*                 key_ptr,
                                     DxUint16_t                 keySize )
{
  /* The return error identifier */
  CRYSError_t Error;
  
  /* offset */
  DxUint32_t  sramOffset;
  
  /* read param */
  DxUint32_t  messageParam[3];
  
  /* max length */
  DxUint32_t  maxLength;
  
  
  
  /*--------------------
      CODE
  ----------------------*/
  
  Error = CRYS_OK;
                       
  #ifndef CRYS_NO_HASH_SUPPORT                                      
      
  /* ............... checking the parameters validity ................... */
   
  /* if the users context ID pointer is DX_NULL return an error */
  if( ContextID_ptr == DX_NULL )
  {
    Error = CRYS_HMAC_INVALID_USER_CONTEXT_POINTER_ERROR;
    goto end_function;
  }
     
  /* check if the operation mode is legal */
  if( OperationMode >= CRYS_HASH_NumOfModes )
  {
    Error = CRYS_HMAC_ILLEGAL_OPERATION_MODE_ERROR;
    goto end_function;
  }
   
  /* check if the key pointer is valid */
  if( key_ptr == DX_NULL )
  {
    Error = CRYS_HMAC_INVALID_KEY_POINTER_ERROR;
    goto end_function;
  }
  
  /* check if the key size is valid */
  if( keySize == 0 )
  {
    Error = CRYS_HMAC_UNVALID_KEY_SIZE_ERROR;
    goto end_function;
  }
  
   /* lock access to the SEP */	  
   Error = SEPDriver_Lock();
   
   if(Error != DX_OK)
   {
       goto end_function;
   }
      
  /*----------------------------
    start sending message to SEP 
  -----------------------------*/
  sramOffset = 0;
   
  /* start the message */
  SEPDriver_StartMessage(&sramOffset);
  
  /* prepare params */
  messageParam[0] = DX_SEP_HOST_SEP_PROTOCOL_HOST_HMAC_INIT_OP_CODE;
  messageParam[1] = OperationMode;
  messageParam[2] = keySize;
  
  /* send params */
  Error = SEPDriver_WriteParamater((DxUint32_t)messageParam , 
                           sizeof(DxUint32_t) * 3,
                           sizeof(DxUint32_t) * 3,
                           &sramOffset , 
                           DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
   
  /* send key */
  maxLength = ((keySize + 3) / sizeof(DxUint32_t)) * sizeof(DxUint32_t);
  Error = SEPDriver_WriteParamater((DxUint32_t)key_ptr , 
                            keySize ,
                            maxLength, 
                            &sramOffset , 
                            DX_TRUE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
                            
  SEPDriver_EndMessage(sramOffset);
            
  /* wait for the response */
  Error = SEPDriver_POLL_FOR_REPONSE();
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /*-------------------
    start reading message from the SEP 
  ---------------------*/
   
  /* start the message */
  Error = SEPDriver_StartIncomingMessage(&sramOffset);
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /* read opcode + status  */
  Error = SEPDriver_ReadParamater((DxUint32_t)messageParam ,
                          sizeof(DxUint32_t) * 2,
                          sizeof(DxUint32_t) * 2,
                          &sramOffset , 
                          DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
   
  /* check the opcode */
  if(messageParam[0] != DX_SEP_HOST_SEP_PROTOCOL_HOST_HMAC_INIT_OP_CODE)
  {
    Error = DX_WRONG_OPCODE_FROM_SEP_ERR;
    goto end_function_unlock;
  }
   
  /* check the status */
  if(messageParam[1] != CRYS_OK)
  {
    Error = messageParam[1];
    goto end_function_unlock;
  }
   
  /* read the context */
  maxLength = ((sizeof(CRYS_HMACUserContext_t) + 3) / sizeof(DxUint32_t)) * sizeof(DxUint32_t);
  Error = SEPDriver_ReadParamater((DxUint32_t)ContextID_ptr,
                           sizeof(CRYS_HMACUserContext_t),
                           maxLength,
                           &sramOffset,
                           DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
  /* ...................... end of function ................................ */   

end_function_unlock:   

  /* lock access to the SEP */
  SEPDriver_Unlock();


end_function:   

  return Error;
  
  #endif /* !CRYS_NO_HASH_SUPPORT */
                              
}/* END OF CRYS_HMAC_Init */

/**
 * This function process a HMAC block of data via the HASH Hardware.
 * The function receives as input an handle to the  HMAC Context , and operated the HASH update on the data
 * below.
 * 
 * @param[in] ContextID_ptr - a pointer to the HMAC context buffer allocated by the user that
 *                       is used for the HMAC machine operation.
 *
 * @param DataIn_ptr a pointer to the buffer that stores the data to be 
 *                       hashed .
 * 
 * @param DataInSize  The size of the data to be hashed in bytes. 
 *
 * @return CRYSError_t on success the function returns CRYS_OK else non ZERO error.
 *
 */

CEXPORT_C CRYSError_t CRYS_HMAC_Update(CRYS_HMACUserContext_t*    ContextID_ptr,
                                       DxUint8_t*                 DataIn_ptr,
                                       DxUint32_t                 DataInSize )
{
  /* The return error identifier */
  CRYSError_t         Error;
   
  /* address of the first table in the input list */
  DxUint32_t          inputLLITableAddr;
   
  /* number of entries in each of the first tables */
  DxUint32_t          numEntriesInTable;
  
  /* first table data size */
  DxUint32_t          firstTableDataSize;
  
  /* hash user context pointer */
  CRYS_HASHUserContext_t* HashUserContext_ptr;
  
  /* number of into the message from the start of user buffer */
  DxUint32_t              numBytesFromStart;
  
  /* number of into the message from the start of user buffer */
  DxUint32_t              numBytesFromEnd;
  
  /* block size */
  DxUint32_t              blockSize;
  
  /* offset */
  DxUint32_t          	  sramOffset;
  
  /* read parameter */
  DxUint32_t          	  messageParam[4];
  
  /* max length */
  DxUint32_t          	maxLength;
  
  
   
  /*----------------------------------
      CODE
  -----------------------------------*/
                      
  #ifndef CRYS_NO_HASH_SUPPORT    
  
  Error = CRYS_OK;                                  

  /* ............... checking the parameters validity ................... */
   
  /* if the users context ID pointer is DX_NULL return an error */
  if( ContextID_ptr == DX_NULL )
  {
    Error = CRYS_HMAC_INVALID_USER_CONTEXT_POINTER_ERROR;
    goto end_function;
  }
   
  /* if the users Data In pointer is illegal and the size is not 0 return an error */
  if( DataIn_ptr == DX_NULL && DataInSize )
  {
    Error = CRYS_HMAC_DATA_IN_POINTER_INVALID_ERROR;
    goto end_function;
  }
  
  /* if the data size is zero no need to execute an update , return CRYS_OK */
  if( DataInSize == 0 )
  {
    Error = CRYS_OK;
    goto end_function;
  }
  
   /* lock access to the SEP */	  
   Error = SEPDriver_Lock();
   
   if(Error != DX_OK)
   {
       goto end_function;
   }
  
  
  HashUserContext_ptr = &((HMACContext_t *)ContextID_ptr->context_buff)->HashUserContext;
  
  /* get the block size */
  blockSize = ((HASHContext_t *)HashUserContext_ptr->context_buff)->OperationModeBlockSizeInBytes;
  
  /* get the number of bytes from the beginning of the user buffer into the message */
  numBytesFromStart = (blockSize - ((HASHContext_t*)(HashUserContext_ptr->context_buff))->NumOfBytesOnThePreviousUpdateBuff) % blockSize;
  if(DataInSize < numBytesFromStart)
  {
    numBytesFromStart = DataInSize;
  }

  /* get the number of bytes from the beginning of the user buffer into the message */
  numBytesFromEnd = (DataInSize - numBytesFromStart) % blockSize;

  /* prepare tables for the input only */
  Error = SEPDriver_PREPARE_INPUT_SYM_DMA_TABLE((DxUint32_t)(DataIn_ptr + numBytesFromStart) , 
                                                            DataInSize - (numBytesFromStart + numBytesFromEnd),
                                                            blockSize,
                                                            &inputLLITableAddr , 
                                                            &numEntriesInTable,
                                                            &firstTableDataSize ) ; 
   
  if(Error != DX_OK)
  {
    goto end_function_unlock;
  }
  
  /*----------------------------
      start sending message to SEP 
  -----------------------------*/
  sramOffset = 0;
   
  /* start the message */
  SEPDriver_StartMessage(&sramOffset);
  
  /*prepare params */
  messageParam[0] = DX_SEP_HOST_SEP_PROTOCOL_HOST_HMAC_UPDATE_OP_CODE;
  messageParam[1] = inputLLITableAddr;
  messageParam[2] = numEntriesInTable;
  messageParam[3] = firstTableDataSize;
  
  /* send opcode */
  Error = SEPDriver_WriteParamater((DxUint32_t)messageParam,
                           sizeof(DxUint32_t) * 4,
                           sizeof(DxUint32_t) * 4,
                           &sramOffset , 
                           DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
  
  /* send the size of the first part of the input buffer */
  Error = SEPDriver_WriteParamater((DxUint32_t)&numBytesFromStart,
                                   sizeof(DxUint32_t),
                                   sizeof(DxUint32_t),
                                   &sramOffset , 
                                   DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
  
  /* send the first part of the input buffer */
  Error = SEPDriver_WriteParamater((DxUint32_t)DataIn_ptr,
                                   numBytesFromStart,
                                   sizeof(DxUint32_t) * 32,
                                   &sramOffset , 
                                   DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
  
  /* send the size of the last part of the input buffer */
  Error = SEPDriver_WriteParamater((DxUint32_t)&numBytesFromEnd,
                                   sizeof(DxUint32_t),
                                   sizeof(DxUint32_t),
                                   &sramOffset , 
                                   DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
  
  /* send the last part of the input buffer */
  Error = SEPDriver_WriteParamater((DxUint32_t)(DataIn_ptr + DataInSize - numBytesFromEnd),
                                   numBytesFromEnd,
                                   sizeof(DxUint32_t) * 32,
                                   &sramOffset , 
                                   DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
  
  /* send context */
  maxLength = ((sizeof(CRYS_HMACUserContext_t) + 3) / sizeof(DxUint32_t)) * sizeof(DxUint32_t);
  Error = SEPDriver_WriteParamater((DxUint32_t)ContextID_ptr , 
                            sizeof(CRYS_HMACUserContext_t) ,
                            maxLength, 
                            &sramOffset , 
                            DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
   
  SEPDriver_EndMessage(sramOffset);
            
  /* wait for the response */
  Error = SEPDriver_WaitForResponse(SEP_DRIVER_POLLING , 0 , CRYS_CALLBACK_Handler , SEP_DRIVER_TIMEOUT_WAIT_FOR_INIFINITY);
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /*-------------------
    start reading message from the SEP 
  ---------------------*/
  
  /* free allocated tables */
  Error = SEPDriver_FreeDMATables();
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /* start the message */
  Error = SEPDriver_StartIncomingMessage(&sramOffset);
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /* read opcode + status */
  Error = SEPDriver_ReadParamater((DxUint32_t)messageParam,
                          sizeof(DxUint32_t) * 2,
                          sizeof(DxUint32_t) * 2,
                          &sramOffset , 
                          DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
   
  /* check the opcode */
  if(messageParam[0] != DX_SEP_HOST_SEP_PROTOCOL_HOST_HMAC_UPDATE_OP_CODE)
  {
    Error = DX_WRONG_OPCODE_FROM_SEP_ERR;
    goto end_function_unlock;
  }
   
  /* read the status */
  if(messageParam[1] != CRYS_OK)
  {
    Error = messageParam[1];
    goto end_function_unlock;
  }
   
  /* read the context */
  maxLength = ((sizeof(CRYS_HMACUserContext_t) + 3) / sizeof(DxUint32_t)) * sizeof(DxUint32_t);
  Error = SEPDriver_ReadParamater((DxUint32_t)ContextID_ptr,
                           sizeof(CRYS_HMACUserContext_t),
                           maxLength,
                           &sramOffset,
                           DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
                                           
end_function_unlock:

  /* unlock access to the SEP */
  SEPDriver_Unlock();                                          
                                                 
end_function:

  return Error;
   
  #endif /* !CRYS_NO_HASH_SUPPORT */
   
}/* END OF CRYS_HMAC_Update */ 

/**
 * This function finalize the HMAC process of data block.
 * The function receives as input an handle to the HMAC Context , that was initialized before
 * by an CRYS_HMAC_Init function or by CRYS_HMAC_Update function.
 * The function finishes the HASH operation on the ipad and text then 
 * executes a new hash operation with the key XOR opad and the previous HASH operation result.  
 *
 *  @param[in] ContextID_ptr - a pointer to the HMAC context buffer allocated by the user that
 *                       is used for the HMAC machine operation.
 *
 *  @retval HmacResultBuff a pointer to the target buffer where the 
 *                       HMAC result stored in the context is loaded to.
 *
 *  @return CRYSError_t on success the function returns CRYS_OK else non ZERO error.
 */

CEXPORT_C CRYSError_t CRYS_HMAC_Finish( CRYS_HMACUserContext_t*   ContextID_ptr ,
                                        CRYS_HASH_Result_t        HmacResultBuff )
{
  /* The return error identifier */
  CRYSError_t   Error;
  
  /* offset */
  DxUint32_t    sramOffset;
  
  /* read parameter */
  DxUint32_t    messageParam[2];
  
  /* max length */
  DxUint32_t    maxLength;
  
  
    
  /*------------------------------
      CODE
  --------------------------------*/
  
  #ifndef CRYS_NO_HASH_SUPPORT                                      
 
  Error = CRYS_OK;
 
  /* ............... checking the parameters validity ................... */
  
  /* if the users context ID pointer is DX_NULL return an error */
  if( ContextID_ptr == DX_NULL )
  {
    Error = CRYS_HMAC_INVALID_USER_CONTEXT_POINTER_ERROR;
    goto end_function;
  }
   
  /* if the result buffer DX_NULL return an error */
  if( HmacResultBuff == DX_NULL )
  {
    Error = CRYS_HMAC_INVALID_RESULT_BUFFER_POINTER_ERROR;
    goto end_function;
  }
  
   /* lock access to the SEP */	  
   Error = SEPDriver_Lock();
   
   if(Error != DX_OK)
   {
       goto end_function;
   }
  
  /*----------------------------
      start sending message to SEP 
  -----------------------------*/
  sramOffset = 0;
   
  /* start the message */
  SEPDriver_StartMessage(&sramOffset);
  
  /* prepare params */
  messageParam[0] = DX_SEP_HOST_SEP_PROTOCOL_HOST_HMAC_FINISH_OP_CODE;
  
  /* send params */
  Error = SEPDriver_WriteParamater((DxUint32_t)messageParam ,
                           sizeof(DxUint32_t),
                           sizeof(DxUint32_t),
                           &sramOffset , 
                           DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
  
  /* send context */
  maxLength = ((sizeof(CRYS_HMACUserContext_t) + 3) / sizeof(DxUint32_t)) * sizeof(DxUint32_t);
  Error = SEPDriver_WriteParamater((DxUint32_t)ContextID_ptr , 
                            sizeof(CRYS_HMACUserContext_t) , 
                            maxLength,
                            &sramOffset , 
                            DX_FALSE); 
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
  
  SEPDriver_EndMessage(sramOffset);
            
  /* wait for the response */
  Error = SEPDriver_POLL_FOR_REPONSE();
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /*-------------------
    start reading message from the SEP 
  ---------------------*/
   
  /* start the message */
  Error = SEPDriver_StartIncomingMessage(&sramOffset);
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /* read opcode + status  */
  Error = SEPDriver_ReadParamater((DxUint32_t)messageParam ,
                          sizeof(DxUint32_t) * 2,
                          sizeof(DxUint32_t) * 2,
                          &sramOffset , 
                          DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
   
  /* check the opcode  */
  if(messageParam[0] != DX_SEP_HOST_SEP_PROTOCOL_HOST_HMAC_FINISH_OP_CODE)
  {
    Error = DX_WRONG_OPCODE_FROM_SEP_ERR;
    goto end_function_unlock;
  }
   
  /* read the status */
  if(messageParam[1] != CRYS_OK)
  {
    Error = messageParam[1];
    goto end_function_unlock;
  }
   
  /* read the result */
  maxLength = ((sizeof(CRYS_HASH_Result_t) + 3) / sizeof(DxUint32_t)) * sizeof(DxUint32_t);
  Error = SEPDriver_ReadParamater((DxUint32_t)HmacResultBuff,
                           sizeof(CRYS_HASH_Result_t),
                           maxLength,
                           &sramOffset,
                           DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
                                           
  /* ...................... end of function ................................ */   

end_function_unlock:   

  /* lock access to the SEP */
  SEPDriver_Unlock();
  
                       
end_function:

  return Error;

  #endif /* !CRYS_NO_HASH_SUPPORT */

}/* END OF CRYS_HMAC_Finish */

/**
 * @brief This function is a service function that frees the context if the operation has
 *        failed. 
 *
 *        The function executes the following major steps:
 *
 *        1. Checks the validity of all of the inputs of the function. 
 *           If one of the received parameters is not valid it shall return an error.
 *
 *           The major checkers that are run over the received parameters:
 *           - verifying that the context pointer is not DX_NULL (*ContextID_ptr). 
 *        2. Clearing the users context.
 *        3. Exit the handler with the OK code.
 *      
 *
 * @param[in] ContextID_ptr - a pointer to the HMAC context buffer allocated by the user that
 *                       is used for the HMAC machine operation. this should be the same context that was
 *                       used on the previous call of this session.
 *
 *
 * @return CRYSError_t - On success CRYS_OK is returned, on failure a
 *                        value MODULE_* CRYS_HMAC_error.h
 */

CEXPORT_C CRYSError_t  CRYS_HMAC_Free(CRYS_HMACUserContext_t* ContextID_ptr )
{
  /* The return error identifier */
  CRYSError_t Error;

  /*--------------------
      CODE
  -----------------------*/
  Error = CRYS_OK;
  
  
                            
  #ifndef CRYS_NO_HASH_SUPPORT                                      
  
  /* ............... checking the parameters validity ................... */
   
  /* if the users context ID pointer is DX_NULL return an error */
  if( ContextID_ptr == DX_NULL )
  {
    Error = CRYS_HMAC_INVALID_USER_CONTEXT_POINTER_ERROR;
    goto end_function;
  }
   
  /* .............. clearing the users context .......................... */
  /* -------------------------------------------------------------------- */
   
  DX_VOS_MemSet( ContextID_ptr , 0 , sizeof(CRYS_HMACUserContext_t) );   

  /* ................. end of function ..................................... */
  /* ----------------------------------------------------------------------- */
 
end_function:
      
  return Error;   
 
  #endif /* !CRYS_NO_HASH_SUPPORT */

}/* END OF CRYS_HMAC_Free */

/**
 * This function provide HASH function to process one buffer of data.
 * The function allocates an internal HASH Context , it initializes the 
 * HASH Context with the cryptographic attributes that are needed for 
 * the HASH block operation ( initialize H's value for the HASH algorithm ).
 * Then the function loads the Hardware with the initializing values and after 
 * that process the data block using the hardware to do hash .
 * At the end the function return the message digest of the data buffer .
 *
 *
 * @param[in] OperationMode - The operation mode : MD5 or SHA1.
 *
 * @param[in] key_ptr - The pointer to the users key buffer.
 *
 * @oaram[in] keySize - The size of the received key.
 * 
 * @param DataIn_ptr a pointer to the buffer that stores the data to be 
 *                       hashed .
 * 
 * @param DataInSize  The size of the data to be hashed in bytes. 
 *
 * @retval HashResultBuff a pointer to the target buffer where the 
 *                      HMAC result stored in the context is loaded to.
 *
 * @return CRYSError_t on success the function returns CRYS_OK else non ZERO error.
 *
 */
CEXPORT_C CRYSError_t CRYS_HMAC  ( CRYS_HASH_OperationMode_t  OperationMode,
                                   DxUint8_t*                 key_ptr,
                                   DxUint16_t                 keySize,
                                   DxUint8_t*                 DataIn_ptr,
                                   DxUint32_t                 DataSize,
                                   CRYS_HASH_Result_t         HmacResultBuff )
{                         
  /* The return error identifier */
  CRYSError_t         Error;
   
  /* address of the first table in the input list */
  DxUint32_t          inputLLITableAddr;
   
  /* number of entries in each of the first tables */
  DxUint32_t          numEntriesInTable;
  
  /* first table data size */
  DxUint32_t          firstTableDataSize;
  
  /* number of into the message from the start of user buffer */
  DxUint32_t          numBytesFromStart;
  
  /* number of into the message from the start of user buffer */
  DxUint32_t          numBytesFromEnd;
  
  /* block size */
  DxUint32_t          blockSize;
  
  /* offset */
  DxUint32_t          sramOffset;
  
  /* read parameter */
  DxUint32_t          messageParam[6];
  
  /* max length */
  DxUint32_t          maxLength;
  
   
  /*-----------------------------
      CODE 
  -------------------------------*/
   
  /* ............... local initializations .............................. */
  Error = CRYS_OK;
   

  /* check if the operation mode is legal */
  if( OperationMode >= CRYS_HASH_NumOfModes )
  {
    Error = CRYS_HMAC_ILLEGAL_OPERATION_MODE_ERROR;
    goto end_function;
  }
  
  /* check if the key pointer is valid */
  if( key_ptr == DX_NULL )
  {
    Error = CRYS_HMAC_INVALID_KEY_POINTER_ERROR;
    goto end_function;
  }
  
  /* check if the key size is valid */
  if( keySize == 0 )
  {
    Error = CRYS_HMAC_UNVALID_KEY_SIZE_ERROR;
    goto end_function;
  }
  
  /* if the users Data In pointer is illegal and the size is not 0 return an error */
  if( DataIn_ptr == DX_NULL && DataSize )
  {
    Error = CRYS_HMAC_DATA_IN_POINTER_INVALID_ERROR;
    goto end_function;
  }
  
           
  /* larger then 2^29 (to prevant an overflow on the transition to bits ) 
     return error */
  if( DataSize >= (1 << 29) )
  {
    Error = CRYS_HMAC_DATA_SIZE_ILLEGAL;
    goto end_function;
  }
  
 
  /* if the result buffer DX_NULL return an error */
  if( HmacResultBuff == DX_NULL )
  {
    Error = CRYS_HMAC_INVALID_RESULT_BUFFER_POINTER_ERROR;
    goto end_function;
  } 
                         
  #ifndef CRYS_NO_HASH_SUPPORT  
  
   /* lock access to the SEP */	  
   Error = SEPDriver_Lock();
   
   if(Error != DX_OK)
   {
       goto end_function;
   }
  
  /* calculate block size */
  switch(OperationMode)
  {
  	case CRYS_HASH_SHA1_mode:
  	case CRYS_HASH_SHA224_mode:
  	case CRYS_HASH_SHA256_mode:
  	case CRYS_HASH_MD5_mode:
  		blockSize = CRYS_HASH_BLOCK_SIZE_IN_BYTES;
  		break;
  	case CRYS_HASH_SHA384_mode:
  	case CRYS_HASH_SHA512_mode:
  		blockSize = CRYS_HASH_SHA2_BLOCK_SIZE_IN_BYTES;
  		break;
  	default:
  		return CRYS_HMAC_ILLEGAL_OPERATION_MODE_ERROR;
  }
   
  /* get the number of bytes from the beginning of the user buffer into the message */
  numBytesFromStart = 0;
  
  /* get the number of bytes from the beginning of the user buffer into the message */
  numBytesFromEnd = DataSize % blockSize;
   
  /* prepare tables for the input only */
  Error = SEPDriver_PREPARE_INPUT_SYM_DMA_TABLE((DxUint32_t)(DataIn_ptr + numBytesFromStart)  , 
                                                            DataSize - (numBytesFromStart + numBytesFromEnd) ,
                                                            blockSize,
                                                            &inputLLITableAddr , 
                                                            &numEntriesInTable,
                                                            &firstTableDataSize ) ; 
     
  if(Error != DX_OK)
  {
    goto end_function_unlock;
  }
  
  /*----------------------------
      start sending message to SEP 
  -----------------------------*/
  sramOffset = 0;
   
  /* start the message */
  SEPDriver_StartMessage(&sramOffset);
  
  /* prepare params */
  messageParam[0] = DX_SEP_HOST_SEP_PROTOCOL_HOST_HMAC_OP_CODE;
  messageParam[1] = OperationMode;
  messageParam[2] = inputLLITableAddr;
  messageParam[3] = numEntriesInTable;
  messageParam[4] = firstTableDataSize;
  messageParam[5] = keySize;
  
  /* send opcode */
  Error = SEPDriver_WriteParamater((DxUint32_t)messageParam ,
                                   sizeof(DxUint32_t) * 6,
                                   sizeof(DxUint32_t) * 6, 
                                   &sramOffset , 
                                   DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
  
  /* send the size of the first part of the input buffer */
  Error = SEPDriver_WriteParamater((DxUint32_t)&numBytesFromStart,
                                   sizeof(DxUint32_t),
                                   sizeof(DxUint32_t),
                                   &sramOffset , 
                                   DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
  
  /* send the first part of the input buffer */
  Error = SEPDriver_WriteParamater((DxUint32_t)DataIn_ptr,
                                   numBytesFromStart,
                                   sizeof(DxUint32_t) * 32,
                                   &sramOffset , 
                                   DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
  
  /* send the size of the last part of the input buffer */
  Error = SEPDriver_WriteParamater((DxUint32_t)&numBytesFromEnd,
                                   sizeof(DxUint32_t),
                                   sizeof(DxUint32_t),
                                   &sramOffset , 
                                   DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
  
  /* send the last part of the input buffer */
  Error = SEPDriver_WriteParamater((DxUint32_t)(DataIn_ptr + DataSize - numBytesFromEnd),
                                   numBytesFromEnd,
                                   sizeof(DxUint32_t) * 32,
                                   &sramOffset , 
                                   DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
   
  /* send key */
  maxLength = ((keySize + 3) / sizeof(DxUint32_t)) * sizeof(DxUint32_t);
  Error = SEPDriver_WriteParamater((DxUint32_t)key_ptr, 
                            keySize ,
                            maxLength, 
                            &sramOffset , 
                            DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
  
  /* end message */ 
  SEPDriver_EndMessage(sramOffset);
            
  /* wait for the response */
  Error = SEPDriver_WaitForResponse(SEP_DRIVER_POLLING , 0 , CRYS_CALLBACK_Handler , SEP_DRIVER_TIMEOUT_WAIT_FOR_INIFINITY);
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /*-------------------
    start reading message from the SEP 
  ---------------------*/
  
  /* free allocated tables */
  Error = SEPDriver_FreeDMATables();
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /* start the message */
  Error = SEPDriver_StartIncomingMessage(&sramOffset);
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /* read opcode + status */
  Error = SEPDriver_ReadParamater((DxUint32_t)messageParam ,
                          sizeof(DxUint32_t) * 2,
                          sizeof(DxUint32_t) * 2, 
                          &sramOffset , 
                          DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
   
  /* check the opcode */
  if(messageParam[0] != DX_SEP_HOST_SEP_PROTOCOL_HOST_HMAC_OP_CODE)
  {
    Error = DX_WRONG_OPCODE_FROM_SEP_ERR;
    goto end_function_unlock;
  }
   
  /* check the status */
  if(messageParam[1] != CRYS_OK)
  {
    Error = messageParam[1];
    goto end_function_unlock;
  }
   
  /* read the result */
  maxLength = ((sizeof(CRYS_HASH_Result_t) + 3) / sizeof(DxUint32_t)) * sizeof(DxUint32_t);
  Error = SEPDriver_ReadParamater((DxUint32_t)HmacResultBuff,
                           sizeof(CRYS_HASH_Result_t),
                           maxLength,
                           &sramOffset,
                           DX_TRUE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
                                           
end_function_unlock:

  /* unlock access to the SEP */
  SEPDriver_Unlock();                                          
                                                 
end_function:

  return Error;

  #endif /* !CRYS_NO_HASH_SUPPORT */
   
}/* END OF CRYS_HMAC */   

