/*
* Creation date : Tues Mar 03 09:00:00 2007
* Last modified : %modify_time%
*/
/** @file
* \brief This file contains implementation of 
* AES (Advanced Encryption Standard) low level functions. 
*
* \version LLF_AES.c#1:csrc:1
* \author Yermalayeu Ihar
* \remarks Copyright (C) 2007 by Discretix Technologies Ltd.
* All Rights reserved
*/

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

#include "LLF_AES.h"
#include "tomcrypt.h"

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

#define LLF_XTS_AES_GF_128_FDBK 0x87 
#define LLF_XTS_AES_BLK_BYTES 16 

/************************ Enums *******************************/
/************************ Typedefs ****************************/
/************************ Global Data *************************/

const DxUint64_t LLF_AES_WRAP_DEFAULT_IV = 0xA6A6A6A6A6A6A6A6;

const unsigned char LLF_AES_XCBC_MAC_K1[] = {
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
};

const unsigned char LLF_AES_XCBC_MAC_K2[] = {
  0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 
  0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02
};

const unsigned char LLF_AES_XCBC_MAC_K3[] = {
  0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
  0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03
};

const unsigned char LLF_AES_XCBC_MAC_PAD[] = {
  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

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

/***************************************************************
* Function Name: 
*  LLF_AES_Set_t
*
* @param t [in] - A input value;
* @param pt [out] - A 64-bit big-endian output value;
*
* @returns \b
*  -nothing
*
* \brief \b 
* Description:
*  Given an unsigned long t (in host byte order), store this value as a
*  64-bit big-endian value (MSB first) in *pt.
*
*  \b 
* Algorithm:
* -# Convert to big-endian value;
***************************************************************/
void LLF_AES_Set_t(unsigned char *pt, unsigned long t)
{
  int i;
  for(i = 7 ; i > 1 ; i--)
  {
    pt[i] = (unsigned char)t;
    t >>= 8;
  }
}

/***************************************************************
* Function Name: 
*  LLF_AES_ECB
*
*  @param Key_ptr [in] - A pointer to the user's key buffer.
*  @param CipherIndex[in] - Cipher LibTomCrypt index.
*  @param KeySize [in] - The size of the KEY.
*  @param EncryptDecryptFlag [in] - A flag specifying whether the AES should perform an 
*                       Encrypt operation (0) or a Decrypt operation (1). 
*  @param DataIn_ptr [in] - The pointer to the buffer of the input data to the AES. 
*  @param DataInSize [in] - The size of the input data.
*  @param DataOut_ptr [out] - The pointer to the buffer of the output data from the AES. 
*
* @returns \b
*  CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*   - CE2_LLF_AES_MODULE_ERROR_BASE
*
* \brief \b 
* Description:
*  This function is used to operate the AES machine in ECB mode. 
*
*  \b 
* Algorithm:
* -# Start LibTomCrypt ECB.
* -# Encrypt/decrypt with using LibTomCrypt ECB. 
***************************************************************/
CE2Error_t LLF_AES_ECB( CE2_AES_Key_t           Key_ptr,
											  DxInt_t                 CipherIndex,
                        DxInt_t                 KeyLen,            
                        CE2_AES_EncryptMode_t   EncryptDecryptFlag, 
                        DxUint8_t               *DataIn_ptr,        
                        DxUint32_t              DataInSize,         
                        DxUint8_t               *DataOut_ptr )
{
  symmetric_ECB ecb;
  int error_code = CRYPT_OK;
  CE2Error_t result = CE2_OK;

  /* Start LibTomCrypt ECB */
  error_code = ecb_start(CipherIndex, (unsigned char *)Key_ptr, 
    KeyLen, 0, &ecb);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_AES_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* Encrypt/decrypt with using LibTomCrypt ECB. */
  if (EncryptDecryptFlag == CE2_AES_Encrypt) {
    error_code = ecb_encrypt(DataIn_ptr, DataOut_ptr, DataInSize, &ecb);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_AES_MODULE_ERROR_BASE;
      goto error_case;
    }
  } else {
    error_code = ecb_decrypt(DataIn_ptr, DataOut_ptr, DataInSize, &ecb);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_AES_MODULE_ERROR_BASE;
      goto error_case;
    }
  }

error_case:
  ecb_done(&ecb);
  return result;
}

/***************************************************************
* Function Name: 
*  LLF_AES_CBC
*
*  @param IVCounter_ptr [in] - This parameter should contain the IV values.
*  @param Key_ptr [in] - A pointer to the user's key buffer.
*  @param CipherIndex[in] - Cipher LibTomCrypt index.
*  @param KeySize [in] - The size of the KEY.
*  @param EncryptDecryptFlag [in] - A flag specifying whether the AES should perform an 
*                       Encrypt operation (0) or a Decrypt operation (1). 
*  @param DataIn_ptr [in] - The pointer to the buffer of the input data to the AES. 
*  @param DataInSize [in] - The size of the input data.
*  @param DataOut_ptr [out] - The pointer to the buffer of the output data from the AES. 
*
* @returns \b
*  CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*   - CE2_LLF_AES_MODULE_ERROR_BASE
*
* \brief \b 
* Description:
*  This function is used to operate the AES machine in CBC mode. 
*
*  \b 
* Algorithm:
* -# Start LibTomCrypt CBC.
* -# Encrypt/decrypt with using LibTomCrypt CBC. 
***************************************************************/
CE2Error_t LLF_AES_CBC(CE2_AES_IvCounter_t  IVCounter_ptr,       
                    CE2_AES_Key_t           Key_ptr,                 
                    DxInt_t                 CipherIndex,
                    DxInt_t                 KeyLen,            
                    CE2_AES_EncryptMode_t   EncryptDecryptFlag, 
                    DxUint8_t               *DataIn_ptr,        
                    DxUint32_t              DataInSize,         
                    DxUint8_t               *DataOut_ptr )
{
  symmetric_CBC cbc;
  int error_code = CRYPT_OK;
  CE2Error_t result = CE2_OK;

  /* Start LibTomCrypt CBC */
  error_code = cbc_start(CipherIndex, (unsigned char *)IVCounter_ptr,
    (unsigned char *)Key_ptr, KeyLen, 0, &cbc);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_AES_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* Start LibTomCrypt CBC */
  if (EncryptDecryptFlag == CE2_AES_Encrypt) {
    error_code = cbc_encrypt(DataIn_ptr, DataOut_ptr, DataInSize, &cbc);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_AES_MODULE_ERROR_BASE;
      goto error_case;
    }
  } else {
    error_code = cbc_decrypt(DataIn_ptr, DataOut_ptr, DataInSize, &cbc);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_AES_MODULE_ERROR_BASE;
      goto error_case;
    }
  }

error_case:
  cbc_done(&cbc);
  return result;
}

/***************************************************************
* Function Name: 
*  LLF_AES_CTR
*
*  @param IVCounter_ptr [in] - This value should contain the init counter.
*  @param Key_ptr [in] - A pointer to the user's key buffer.
*  @param CipherIndex[in] - Cipher LibTomCrypt index.
*  @param KeySize [in] - The size of the KEY.
*  @param EncryptDecryptFlag [in] - A flag specifying whether the AES should perform an 
*                       Encrypt operation (0) or a Decrypt operation (1). 
*  @param DataIn_ptr [in] - The pointer to the buffer of the input data to the AES. 
*  @param DataInSize [in] - The size of the input data.
*  @param DataOut_ptr [out] - The pointer to the buffer of the output data from the AES. 
*
* @returns \b
*  CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*   - CE2_LLF_AES_MODULE_ERROR_BASE
*
* \brief \b 
* Description:
*  This function is used to operate the AES machine in CTR mode. 
*
*  \b 
* Algorithm:
* -# Start LibTomCrypt CTR.
* -# Encrypt/decrypt with using LibTomCrypt CTR. 
***************************************************************/
CE2Error_t LLF_AES_CTR(CE2_AES_IvCounter_t  IVCounter_ptr,       
                       CE2_AES_Key_t           Key_ptr,                 
                       DxInt_t                 CipherIndex,
                       DxInt_t                 KeyLen,            
                       CE2_AES_EncryptMode_t   EncryptDecryptFlag, 
                       DxUint8_t               *DataIn_ptr,        
                       DxUint32_t              DataInSize,         
                       DxUint8_t               *DataOut_ptr )
{
  symmetric_CTR ctr;
  int error_code = CRYPT_OK;
  CE2Error_t result = CE2_OK;

  /* Start LibTomCrypt CTR */
  error_code = ctr_start(CipherIndex, (unsigned char *)IVCounter_ptr,
    (unsigned char *)Key_ptr, KeyLen, 0, CTR_COUNTER_BIG_ENDIAN, &ctr);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_AES_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* Start LibTomCrypt CTR */
  if (EncryptDecryptFlag == CE2_AES_Encrypt) {
    error_code = ctr_encrypt(DataIn_ptr, DataOut_ptr, DataInSize, &ctr);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_AES_MODULE_ERROR_BASE;
      goto error_case;
    }
  } else {
    error_code = ctr_decrypt(DataIn_ptr, DataOut_ptr, DataInSize, &ctr);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_AES_MODULE_ERROR_BASE;
      goto error_case;
    }
  }

error_case:
  ctr_done(&ctr);
  return result;
}

/***************************************************************
* Function Name: 
*  LLF_AES_XCBC_MAC
*
*  @param Key_ptr [in] - A pointer to the user's key buffer.
*  @param KeySize [in] - The size of the KEY.
*  @param DataIn_ptr [in] - The pointer to the buffer of the input data to the AES. 
*  @param DataInSize [in] - The size of the input data.
*  @param DataOut_ptr [out] - The pointer to the buffer of the output data from the AES. 
*
* @returns \b
*  CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*   - CE2_LLF_AES_MODULE_ERROR_BASE
*
* \brief \b 
* Description:
*  This function is used to operate the AES machine in XCBC-MAC mode 
*
*  \b 
* Algorithm:
* -# Setup LibTomCrypt key K.
* -# Make MAC with using AES-XCBC-MAC 
***************************************************************/
CE2Error_t LLF_AES_XCBC_MAC(CE2_AES_Key_t           Key_ptr,                 
                            DxInt_t                 KeyLen,            
                            DxUint8_t               *DataIn_ptr,        
                            DxUint32_t              DataInSize,         
                            DxUint8_t               *DataOut_ptr)
{
  symmetric_key skey_K, skey_K1;
  int error_code = CRYPT_OK;
  CE2Error_t result = CE2_OK;
  DxUint8_t K1[CE2_AES_BLOCK_SIZE_IN_BYTES];
  DxUint32_t K2[CE2_AES_BLOCK_SIZE_IN_BYTES], K3[CE2_AES_BLOCK_SIZE_IN_BYTES],
    E[CE2_AES_BLOCK_SIZE_IN_WORDS], M[CE2_AES_BLOCK_SIZE_IN_WORDS];
  DxInt32_t nBlocks, remainderLen, i, i_word;
  
  /*****************************/
  /*  Setup LibTomCrypt key K  */
  /*****************************/
  error_code = aes_setup((unsigned char *)Key_ptr, KeyLen, 0, &skey_K);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_AES_MODULE_ERROR_BASE;
    goto error_case;
  }

  /*******************************************************************/
  /* Consider that input message DataIn_ptr consists of n blocks,    */
  /* M[1] ... M[n], in which the blocksize of blocks M[1] ... M[n-1] */ 
  /* is 128 bits and the blocksize of block M[n] is between 1 and    */
  /* 128 bits:                                                       */
  /*******************************************************************/
 
  nBlocks = DataInSize/CE2_AES_BLOCK_SIZE_IN_BYTES;
  remainderLen = DataInSize%CE2_AES_BLOCK_SIZE_IN_BYTES;
  if (remainderLen)
    nBlocks++;

  /**********************************************************************/
  /* (1)  Derive 3 128-bit keys (K1, K2 and K3) from the 128-bit secret */
  /*  key K, as follows:                                                */
  /*  K1 = 0x01010101010101010101010101010101 encrypted with Key K      */
  /*  K2 = 0x02020202020202020202020202020202 encrypted with Key K      */
  /*  K3 = 0x03030303030303030303030303030303 encrypted with Key K      */
  /**********************************************************************/

  /* K1 = 0x01010101010101010101010101010101 encrypted with Key K */
  error_code = aes_ecb_encrypt(LLF_AES_XCBC_MAC_K1, K1, &skey_K);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_AES_MODULE_ERROR_BASE;
    goto error_case;
  }
  /* Setup LibTomCrypt key K1 */
  error_code = aes_setup(K1, CE2_AES_BLOCK_SIZE_IN_BYTES, 0, &skey_K1);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_AES_MODULE_ERROR_BASE;
    goto error_case;
  }

  /*  K2 = 0x02020202020202020202020202020202 encrypted with Key K */
  error_code = aes_ecb_encrypt(LLF_AES_XCBC_MAC_K2, (unsigned char *)K2, 
    &skey_K);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_AES_MODULE_ERROR_BASE;
    goto error_case;
  }

  /*  K3 = 0x03030303030303030303030303030303 encrypted with Key K */
  error_code = aes_ecb_encrypt(LLF_AES_XCBC_MAC_K3, (unsigned char *)K3, 
    &skey_K);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_AES_MODULE_ERROR_BASE;
    goto error_case;
  }

  /*********************************************************/
  /* (2)  Define E[0] = 0x00000000000000000000000000000000 */
  /*********************************************************/
  memset(E, 0x00, CE2_AES_BLOCK_SIZE_IN_BYTES);

  /****************************************************************/
  /* (3)  For each block M[i], where i = 1 ... n-1:               */
  /*   XOR M[i] with E[i-1], then encrypt the result with Key K1, */
  /*   yielding E[i].                                             */
  /****************************************************************/

  /* For  i = 1 ... n-1 */ 
  for(i = 0; i < nBlocks - 1; i++) {
    /* Set M[i] */
    memcpy(M, DataIn_ptr + i*CE2_AES_BLOCK_SIZE_IN_BYTES, 
      CE2_AES_BLOCK_SIZE_IN_BYTES);

    /* XOR M[i] with E[i-1] */
    for(i_word = 0; i_word < CE2_AES_BLOCK_SIZE_IN_WORDS; i_word++)
      M[i_word] ^= E[i_word];

    /* Encrypt the result with Key K1, yielding E[i]. */
    error_code = aes_ecb_encrypt((unsigned char*)M, (unsigned char*)E, 
      &skey_K1);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_AES_MODULE_ERROR_BASE;
      goto error_case;
    }
  }

  if (remainderLen == 0 && nBlocks) {
    /****************************************************************/
    /* (4)  For block M[n]:                                         */
    /*    a)  If the blocksize of M[n] is 128 bits:                 */
    /*      XOR M[n] with E[n-1] and Key K2, then encrypt the       */
    /*      result with Key K1, yielding E[n].                      */
    /****************************************************************/

    /* Set M[n] */
    memcpy(M, DataIn_ptr + (nBlocks - 1)*CE2_AES_BLOCK_SIZE_IN_BYTES, 
      CE2_AES_BLOCK_SIZE_IN_BYTES);

    /* XOR M[n] with E[n-1] and Key K2*/
    for(i_word = 0; i_word < CE2_AES_BLOCK_SIZE_IN_WORDS; i_word++){
      M[i_word] ^= E[i_word];
      M[i_word] ^= K2[i_word];
    }

    /* Encrypt the result with Key K1, yielding E[n]. */
    error_code = aes_ecb_encrypt((unsigned char*)M, (unsigned char*)E, 
      &skey_K1);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_AES_MODULE_ERROR_BASE;
      goto error_case;
    }
  } else {
    /************************************************************/
    /* (4)  For block M[n]:                                     */
    /*   b)  If the blocksize of M[n] is less than 128 bits:    */
    /*     i)  Pad M[n] with a single "1" bit, followed by the  */ 
    /*       number of "0" bits (possibly none) required to     */
    /*       increase M[n]'s blocksize to 128 bits.             */
    /*     ii) XOR M[n] with E[n-1] and Key K3, then encrypt    */ 
    /*       the result with Key K1, yielding E[n].             */
    /************************************************************/
  
    /* Set remainder bits to block M[n] */
    memcpy(M, DataIn_ptr + (nBlocks - 1)*CE2_AES_BLOCK_SIZE_IN_BYTES,
      remainderLen);

    /* Pad M[n] with a single "1" bit, followed by the */ 
    /* number of "0" bits (possibly none) required to  */
    /* increase M[n]'s blocksize to 128 bits.          */
    memcpy(((unsigned char*)M) + remainderLen, LLF_AES_XCBC_MAC_PAD, 
      CE2_AES_BLOCK_SIZE_IN_BYTES - remainderLen);

    /* XOR M[n] with E[n-1] and Key K3 */
    for(i_word = 0; i_word < CE2_AES_BLOCK_SIZE_IN_WORDS; i_word++){
      M[i_word] ^= E[i_word];
      M[i_word] ^= K3[i_word];
    }

    /* Encrypt the result with Key K1, yielding E[n]. */
    error_code = aes_ecb_encrypt((unsigned char*)M, (unsigned char*)E, 
      &skey_K1);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_AES_MODULE_ERROR_BASE;
      goto error_case;
    }
  }

  /*********************************************************/
  /* For AES-XCBC-MAC-96:                                  */
  /* (5)  The authenticator value is the leftmost 96 bits  */ 
  /*   of the 128-bit E[n].                                */
  /* For AES-XCBC-MAC-PRF:                                 */
  /* (5)  The authenticator value is 128-bit E[n].         */
  /*********************************************************/

  memcpy(DataOut_ptr, E, CE2_AES_BLOCK_SIZE_IN_BYTES);

error_case:
  aes_done(&skey_K);
  aes_done(&skey_K1);
  return result;
}


/***************************************************************
* Function Name: 
*  LLF_AES_CMAC
*
*  @param Key_ptr [in] - A pointer to the user's key buffer.
*  @param CipherIndex[in] - Cipher LibTomCrypt index.
*  @param KeySize [in] - The size of the KEY.
*  @param DataIn_ptr [in] - The pointer to the buffer of the input data to the AES. 
*  @param DataInSize [in] - The size of the input data.
*  @param DataOut_ptr [out] - The pointer to the buffer of the output data from the AES. 
*
* @returns \b
*  CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*   - CE2_LLF_AES_MODULE_ERROR_BASE
*
* \brief \b 
* Description:
*  This function is used to operate the AES machine in CMAC (OMAC1) mode. 
*
*  \b 
* Algorithm:
* -# Start the OMAC.
* -# Process a few octets.
* -# Get result. 
***************************************************************/
CE2Error_t  LLF_AES_CMAC(CE2_AES_Key_t           Key_ptr,                 
                         DxInt_t                 CipherIndex,
                         DxInt_t                 KeyLen,            
                         DxUint8_t                *DataIn_ptr,        
                         DxUint32_t               DataInSize,         
                         DxUint8_t                *DataOut_ptr )
{
  int error_code;
  omac_state omac;
  unsigned long outLen = CE2_AES_BLOCK_SIZE_IN_BYTES;
  
  /* Start the OMAC */
  error_code = omac_init(&omac, CipherIndex, Key_ptr, KeyLen);
  if (error_code != CRYPT_OK) {
    return CE2_LLF_AES_MODULE_ERROR_BASE;
  }

  /* Process a few octets */
  error_code = omac_process(&omac, DataIn_ptr, DataInSize);
  if (error_code != CRYPT_OK) {
    return CE2_LLF_AES_MODULE_ERROR_BASE;
  }

  /* Get result */
  error_code = omac_done(&omac, DataOut_ptr, &outLen);
  if (error_code != CRYPT_OK) {
    return CE2_LLF_AES_MODULE_ERROR_BASE;
  }

  return CE2_OK;
}

/***************************************************************
* Function Name: 
*  LLF_AES_MAC
*
*  @param IVCounter_ptr [in] - This parameter is the buffer of the IV values.
*  @param CipherIndex[in] - Cipher LibTomCrypt index.
*  @param Key_ptr [in] - A pointer to the user's key buffer.
*  @param KeySize [in] - The size of the KEY.
*  @param DataIn_ptr [in] - The pointer to the buffer of the input data to the AES. 
*  @param DataInSize [in] - The size of the input data.
*  @param DataOut_ptr [out] - The pointer to the buffer of the output data from the AES. 
*
* @returns \b
*  CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*   - CE2_LLF_AES_MODULE_ERROR_BASE
*
* \brief \b 
* Description:
*   Make MAC (Message Authentication Code) on the base of CBC encryption.
*
*  \b 
* Algorithm:
*  -# Start LibTomCrypt CBC
*  -# Make CBC encryption
*  -# Copy last 16 bytes of the last encrypted block of a AES CBC operation 
*     to output buffer (DataOut_ptr);
***************************************************************/
CE2Error_t  LLF_AES_MAC(CE2_AES_IvCounter_t     IVCounter_ptr,
                        CE2_AES_Key_t           Key_ptr,                 
                        DxInt_t                 CipherIndex,
                        DxInt_t                 KeyLen,            
                        DxUint8_t               *DataIn_ptr,        
                        DxUint32_t              DataInSize,         
                        DxUint8_t               *DataOut_ptr )
{
  symmetric_CBC cbc;
  int error_code;
  CE2Error_t result = CE2_OK;
  DxUint8_t *pCBCOutBuffer = DX_NULL;

  pCBCOutBuffer = malloc(DataInSize);
  if (pCBCOutBuffer == DX_NULL) {
    result = CE2_LLF_AES_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* Start LibTomCrypt CBC */
  error_code = cbc_start(CipherIndex, (unsigned char *)IVCounter_ptr,
    (unsigned char *)Key_ptr, KeyLen, 0, &cbc);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_AES_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* Make CBC encryption */
  error_code = cbc_encrypt(DataIn_ptr, pCBCOutBuffer, DataInSize, &cbc);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_AES_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* Copy last 16 bytes of the last encrypted block of a */
  /* AES CBC operation to output buffer (DataOut_ptr)    */
  memcpy(DataOut_ptr, pCBCOutBuffer + DataInSize - CE2_AES_BLOCK_SIZE_IN_BYTES,
    CE2_AES_BLOCK_SIZE_IN_BYTES);

error_case:
  if (pCBCOutBuffer != DX_NULL)
    free(pCBCOutBuffer);
  cbc_done(&cbc);
  return result;
}

CE2Error_t LLF_XTS_AES_Encrypt(CE2_AES_XTS_Tweak_t Tweak_pt, // Tweak block	
                               symmetric_ECB *pKey,          // Key used for "ECB" encryption 
                               DxUint8_t  *DataIn_ptr,       // Plain text input data       
                               DxUint32_t DataInSize,        // Data size, in bytes const        
                               DxUint8_t  *DataOut_ptr)      // Cipher text sector output data
{ 
  DxUint32_t i, j; 
  DxUint8_t TweakValue[LLF_XTS_AES_BLK_BYTES];
  DxUint8_t WorkValue[LLF_XTS_AES_BLK_BYTES]; 
  DxUint8_t Cin, Cout; // "Carry" bits for LFSR shifting 
  CE2Error_t result = CE2_OK;
  int error_code = CRYPT_OK;

  memcpy( TweakValue, Tweak_pt, LLF_XTS_AES_BLK_BYTES );

  /* Now encrypt the sector data */
  for (i = 0; i + LLF_XTS_AES_BLK_BYTES <= DataInSize; i += LLF_XTS_AES_BLK_BYTES) { 
    /* Merge the tweak into the input block */
    for (j = 0; j < LLF_XTS_AES_BLK_BYTES; j++) 
      WorkValue[j] = DataIn_ptr[i + j] ^ TweakValue[j]; 

    /* Encrypt one block */
    error_code = ecb_encrypt(WorkValue, WorkValue, LLF_XTS_AES_BLK_BYTES, pKey);
    if (error_code != CRYPT_OK) 
      return CE2_LLF_AES_MODULE_ERROR_BASE;

    /* Merge the tweak into the output block */
    for (j = 0; j < LLF_XTS_AES_BLK_BYTES; j++) 
      DataOut_ptr[i + j] = WorkValue[j] ^ TweakValue[j];

    /* LFSR "shift" the tweak value for the next location */
    Cin = 0; 
    for (j = 0; j < LLF_XTS_AES_BLK_BYTES; j++) { 
      Cout = (TweakValue[j] >> 7) & 0x01; 
      TweakValue[j] = ((TweakValue[j] << 1) + Cin) & 0xFF; 
      Cin = Cout; 
    } 
    if (Cout) 
      TweakValue[0] ^= LLF_XTS_AES_GF_128_FDBK; 
  } 
  /* Handling of a final partial block */ 
  if (i < DataInSize) 
  { 
    for (j = 0; i + j < DataInSize; j++) { 
      /* Copy in the final plain text bytes */ 
      WorkValue[j] = DataIn_ptr[i + j] ^ TweakValue[j];

      /* Copy out the final cipher text bytes */
      DataOut_ptr[i + j] = DataOut_ptr[i + j - LLF_XTS_AES_BLK_BYTES]; 
    } 

    /* "Steal" cipher text to complete the block */
    for (; j < LLF_XTS_AES_BLK_BYTES; j++)  
      WorkValue[j] = DataOut_ptr[i + j - LLF_XTS_AES_BLK_BYTES] ^ TweakValue[j];

    /* Encrypt the final block*/ 
    error_code = ecb_encrypt(WorkValue, WorkValue, LLF_XTS_AES_BLK_BYTES, pKey);
    if (error_code != CRYPT_OK) 
      return CE2_LLF_AES_MODULE_ERROR_BASE;

    /* Merge the tweak into the output block */
    for (j = 0; j < LLF_XTS_AES_BLK_BYTES; j++) 
      DataOut_ptr[i + j - LLF_XTS_AES_BLK_BYTES] = WorkValue[j] ^ TweakValue[j]; 
  }

  return CE2_OK;
}

CE2Error_t LLF_XTS_AES_Decrypt(CE2_AES_XTS_Tweak_t Tweak_pt, // Key used for "ECB" encryption 
                               symmetric_ECB *pKey,		     // Key used for generating sector "tweak"
                               DxUint8_t  *DataIn_ptr,       // Cipher text input data       
                               DxUint32_t DataInSize,        // Data size, in bytes const        
                               DxUint8_t  *DataOut_ptr)      // Plain text sector output data
{
  DxUint32_t i, j; 
  DxUint8_t TweakValue1[LLF_XTS_AES_BLK_BYTES];
  DxUint8_t TweakValue2[LLF_XTS_AES_BLK_BYTES];
  DxUint8_t WorkValue[LLF_XTS_AES_BLK_BYTES]; 
  DxUint8_t Cin, Cout; // "Carry" bits for LFSR shifting 
  CE2Error_t result = CE2_OK;
  int error_code = CRYPT_OK;

  memcpy( TweakValue1, Tweak_pt, LLF_XTS_AES_BLK_BYTES );

  if (DataInSize%LLF_XTS_AES_BLK_BYTES == 0) {
    /* Now decrypt the sector data */
    for (i = 0; i + LLF_XTS_AES_BLK_BYTES <= DataInSize; i += LLF_XTS_AES_BLK_BYTES) { 
      /* Merge the tweak into the input block */
      for (j = 0; j < LLF_XTS_AES_BLK_BYTES; j++) 
        WorkValue[j] = DataIn_ptr[i + j] ^ TweakValue1[j]; 

      /* Decrypt one block */
      error_code = ecb_decrypt(WorkValue, WorkValue, LLF_XTS_AES_BLK_BYTES, pKey);
      if (error_code != CRYPT_OK) 
        return CE2_LLF_AES_MODULE_ERROR_BASE;

      /* Merge the tweak into the output block */
      for (j = 0; j < LLF_XTS_AES_BLK_BYTES; j++) 
        DataOut_ptr[i + j] = WorkValue[j] ^ TweakValue1[j];

      /* LFSR "shift" the tweak value for the next location */
      Cin = 0; 
      for (j = 0; j < LLF_XTS_AES_BLK_BYTES; j++) { 
        Cout = (TweakValue1[j] >> 7) & 0x01; 
        TweakValue1[j] = ((TweakValue1[j] << 1) + Cin) & 0xFF; 
        Cin = Cout; 
      } 
      if (Cout) 
        TweakValue1[0] ^= LLF_XTS_AES_GF_128_FDBK; 
    }
  } else {
    /* Now decrypt the sector data */
    for (i = 0; i + 2*LLF_XTS_AES_BLK_BYTES <= DataInSize; i += LLF_XTS_AES_BLK_BYTES) { 
      /* Merge the tweak into the input block */
      for (j = 0; j < LLF_XTS_AES_BLK_BYTES; j++) 
        WorkValue[j] = DataIn_ptr[i + j] ^ TweakValue1[j]; 

      /* Decrypt one block */
      error_code = ecb_decrypt(WorkValue, WorkValue, LLF_XTS_AES_BLK_BYTES, pKey);
      if (error_code != CRYPT_OK) 
        return CE2_LLF_AES_MODULE_ERROR_BASE;

      /* Merge the tweak into the output block */
      for (j = 0; j < LLF_XTS_AES_BLK_BYTES; j++) 
        DataOut_ptr[i + j] = WorkValue[j] ^ TweakValue1[j];

      /* LFSR "shift" the tweak value for the next location */
      Cin = 0; 
      for (j = 0; j < LLF_XTS_AES_BLK_BYTES; j++) { 
        Cout = (TweakValue1[j] >> 7) & 0x01; 
        TweakValue1[j] = ((TweakValue1[j] << 1) + Cin) & 0xFF; 
        Cin = Cout; 
      } 
      if (Cout) 
        TweakValue1[0] ^= LLF_XTS_AES_GF_128_FDBK; 
    }

    /* Handling of a penultimate block and final partial block */ 
    memcpy(TweakValue2, TweakValue1, LLF_XTS_AES_BLK_BYTES);
    Cin = 0; 
    for (j = 0; j < LLF_XTS_AES_BLK_BYTES; j++) { 
      Cout = (TweakValue2[j] >> 7) & 0x01; 
      TweakValue2[j] = ((TweakValue2[j] << 1) + Cin) & 0xFF; 
      Cin = Cout; 
    } 
    if (Cout) 
      TweakValue2[0] ^= LLF_XTS_AES_GF_128_FDBK; 

    /* Merge the tweak into the penultimate input block */
    for (j = 0; j < LLF_XTS_AES_BLK_BYTES; j++) 
      WorkValue[j] = DataIn_ptr[i + j] ^ TweakValue2[j]; 

    /* Decrypt penultimate block */
    error_code = ecb_decrypt(WorkValue, WorkValue, LLF_XTS_AES_BLK_BYTES, pKey);
    if (error_code != CRYPT_OK) 
      return CE2_LLF_AES_MODULE_ERROR_BASE;

    /* Merge the tweak into the penultimate output block */
    for (j = 0; j < LLF_XTS_AES_BLK_BYTES; j++) 
      DataOut_ptr[i + j] = WorkValue[j] ^ TweakValue2[j];

    for (j = 0; i + j < DataInSize - LLF_XTS_AES_BLK_BYTES; j++) { 
      /* Copy in the final plain text bytes */ 
      WorkValue[j] = DataIn_ptr[i + LLF_XTS_AES_BLK_BYTES + j] ^ TweakValue1[j];

      /* Copy out the final cipher text bytes */
      DataOut_ptr[i + LLF_XTS_AES_BLK_BYTES + j] = DataOut_ptr[i + j]; 
    } 

    /* "Steal" cipher text to complete the block */
    for (; j < LLF_XTS_AES_BLK_BYTES; j++)  
      WorkValue[j] = DataOut_ptr[i + j] ^ TweakValue1[j];

    /* Decrypt the final block */ 
    error_code = ecb_decrypt(WorkValue, WorkValue, LLF_XTS_AES_BLK_BYTES, pKey);
    if (error_code != CRYPT_OK) 
      return CE2_LLF_AES_MODULE_ERROR_BASE;

    /* Merge the tweak into the final output block */
    for (j = 0; j < LLF_XTS_AES_BLK_BYTES; j++) 
      DataOut_ptr[i + j] = WorkValue[j] ^ TweakValue1[j]; 
  }

  return CE2_OK;
}


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

/***************************************************************
* Function Name: 
*  LLF_AES
*
* @param IVCounter_ptr [in] - This parameter is the buffer of the IV or 
*                  counters on mode CTR.
*                  In ECB, XCBC, CMAC mode this parameter is not used.
*                  In CBC and MAC modes this parameter should contain the IV values.
*                  In CTR mode this value should contain the init counter.
*                  In XCBC and CMAC modes it may be NULL
*  @param Key_ptr [in] - A pointer to the user's key buffer.
*  @param KeySize [in] - The size of the KEY used by the AES: 128, 192 or 256 bits, 
*            as defined in the enum.
*  @param EncryptDecryptFlag [in] - A flag specifying whether the AES should perform an 
*                       Encrypt operation (0) or a Decrypt operation (1). 
*                       In XCBC and CMAC modes it must be 0.
*  @param OperationMode [in] - The operation mode: ECB, CBC, MAC, CTR, XCBC (PRF and 96), CMAC.
*  @param DataIn_ptr [in] - The pointer to the buffer of the input data to the AES. 
*               The pointer's value does not need to be word-aligned.
*  @param DataInSize [in] - The size of the input data (must be not 0 and must be multiple 
*               of 16 bytes, besides XCBC and CMAC).
*  @param DataOut_ptr [out] - The pointer to the buffer of the output data from the AES. 
*                The pointer does not need to be aligned to 32 bits. 
*                On XCBC and CMAC modes it may be NULL. 
*                The pointer's value does not need to be word-aligned.  
*
* @returns \b
*  CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*   - CE2_LLF_AES_MODULE_ERROR_BASE
*
* \brief \b 
* Description:
*  This function is used to operate the AES machine in one 
*  integrated operation.
*
*  \b 
* Algorithm:
* -# Initialize LibTomCrypt etc.
* -# Choose cipher mode 
* -# Make encryption/decryption corresponding selected mode.
***************************************************************/
CE2Error_t  LLF_AES(CE2_AES_IvCounter_t     IVCounter_ptr,       
                             CE2_AES_Key_t           Key_ptr,                 
                             CE2_AES_KeySize_t       KeySize,            
                             CE2_AES_EncryptMode_t   EncryptDecryptFlag, 
                             CE2_AES_OperationMode_t OperationMode ,           
                             DxUint8_t                *DataIn_ptr,        
                             DxUint32_t               DataInSize,         
                             DxUint8_t                *DataOut_ptr )
{
  int keyLen, cipherIndex, error_code;

  /*****************************/
  /*  Find LibTomCrypt cipher  */
  /*****************************/
  error_code = register_cipher(&aes_desc);
  if (error_code == -1 ) 
    return CE2_LLF_AES_MODULE_ERROR_BASE;

  cipherIndex = find_cipher("aes");
  if (cipherIndex == -1)
    return CE2_LLF_AES_MODULE_ERROR_BASE;

  /*****************************/
  /*     Choose key length     */
  /*****************************/
  switch (KeySize) {
    case CE2_AES_Key128BitSize:
      keyLen = 16;
      break;
    case CE2_AES_Key192BitSize:
      keyLen = 24;
      break;
    case CE2_AES_Key256BitSize:
      keyLen = 32;
      break;
    default:
      return CE2_LLF_AES_MODULE_ERROR_BASE;
  }

  /**********************************/
  /* Choose LibTomCrypt cipher mode */
  /**********************************/
  switch (OperationMode) {
    case CE2_AES_ECB_mode:
      return LLF_AES_ECB(Key_ptr, cipherIndex, keyLen, EncryptDecryptFlag, 
        DataIn_ptr, DataInSize, DataOut_ptr);
    case CE2_AES_CBC_mode:
      return LLF_AES_CBC(IVCounter_ptr, Key_ptr, cipherIndex, keyLen,            
        EncryptDecryptFlag, DataIn_ptr, DataInSize, DataOut_ptr);
    case CE2_AES_MAC_mode:
      return LLF_AES_MAC(IVCounter_ptr, Key_ptr, cipherIndex, keyLen,            
        DataIn_ptr, DataInSize, DataOut_ptr);
    case CE2_AES_CTR_mode:
      return LLF_AES_CTR(IVCounter_ptr, Key_ptr, cipherIndex, keyLen,            
        EncryptDecryptFlag, DataIn_ptr, DataInSize, DataOut_ptr);
    case CE2_AES_XCBC_MAC_mode:
      return LLF_AES_XCBC_MAC(Key_ptr, keyLen, DataIn_ptr, 
        DataInSize, DataOut_ptr);
    case CE2_AES_CMAC_mode: 
      return LLF_AES_CMAC(Key_ptr, cipherIndex, keyLen,            
        DataIn_ptr, DataInSize, DataOut_ptr);
    default:
      return CE2_LLF_AES_MODULE_ERROR_BASE;
  }

  //return CE2_OK;
}

/***************************************************************
* Function Name: 
*  LLF_AES_Wrap
*
* @param DataIn_ptr [in] - A pointer to plain text data to be wrapped
*               NOTE: Overlapping between the data input and data output buffer
*               is not allowed, except the inplace case that is legal . 			
* @param DataInLen [in]  - Length of data in bytes. DataLen must be multiple of 
*               8 bytes and  must be in range [16,  2^28].
* @param KeyData [in] - A pointer to  key data (key encryption key - KEK). 
* @param KeySize [in] - Enumerator variable, defines length of key.
* @param WrapDataOut_ptr [out] - A pointer to buffer for output of wrapped data.
* @param WrapDataLen_ptr [in/out] - A pointer to a buffer for input of size of 
*                    user passed buffer and for output actual 
*                    size of unwrapped data in bytes. Buffer size must 
*                    be not less than DataLen+CE2_AES_WRAP_BLOCK_SIZE_IN_BYTES.                         
*
* @returns \b
*  CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*   - CE2_LLF_AES_MODULE_ERROR_BASE
*
* \brief \b 
* Description:
*   The LLF_AES_Wrap function implements the following algorithm 
*   (rfc3394, Sept. 2002)
*  Inputs:  Plaintext DataIn, n 64-bit values {P1, P2, ..., Pn}, 
*   KeyData, K (the KEK).                
*  Outputs: Ciphertext, WrapDataOut (n+1) 64-bit values {C0, C1, ..., Cn}.
*
*  \b 
* Algorithm:
* -# Initialize LibTomCrypt etc.
* -# Calculate algorithm (rfc3394, Sept. 2002) 
* -# Output the results 
***************************************************************/
CE2Error_t LLF_AES_Wrap (
                                    DxUint8_t            *DataIn_ptr,      /*in*/   
                                    DxUint32_t            DataInLen,       /*in*/
                                    CE2_AES_Key_t        KeyData,         /*in*/
                                    CE2_AES_KeySize_t    KeySize,         /*in*/
                                    DxUint8_t            *WrapDataOut_ptr, /*out*/
                                    DxUint32_t           *WrapDataLen_ptr  /*in/out*/ )
{
  CE2Error_t result = CE2_OK;
  int keyLen, cipherIndex, error_code;
  unsigned int i, j, nBlocks = DataInLen/CE2_AES_WRAP_BLOCK_SIZE_IN_BYTES;
  DxUint64_t *R = DX_NULL;
  DxUint64_t t;
  DxUint64_t B[2];
  symmetric_key skey;

#define A B[0]

  /*****************************/
  /*  Find LibTomCrypt cipher  */
  /*****************************/
  error_code = register_cipher(&aes_desc);
  if (error_code == -1 ) 
    return CE2_LLF_AES_MODULE_ERROR_BASE;

  cipherIndex = find_cipher("aes");
  if (cipherIndex == -1)
    return CE2_LLF_AES_MODULE_ERROR_BASE;

  /*****************************/
  /*     Choose key length     */
  /*****************************/
  switch (KeySize) {
    case CE2_AES_Key128BitSize:
      keyLen = 16;
      break;
    case CE2_AES_Key192BitSize:
      keyLen = 24;
      break;
    case CE2_AES_Key256BitSize:
      keyLen = 32;
      break;
    default:
      return CE2_LLF_AES_MODULE_ERROR_BASE;
  }

  /*****************************/
  /*  Setup LibTomCrypt key    */
  /*****************************/
  error_code = aes_setup((unsigned char *)KeyData, keyLen, 0, &skey);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_AES_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* Allocate R */ 
  R = malloc(sizeof(DxUint64_t)*(nBlocks + 1));
  if (R == DX_NULL) {
    result = CE2_LLF_AES_MODULE_ERROR_BASE;
    goto error_case;
  }

  /***********************************************/ 
  /* 1) Initialize variables                     */
  /*   Set A = IV, an initial value              */ 
  /*   For i = 1 to n                            */
  /*       R[i] = P[i]                           */
  /***********************************************/ 
  /* Set A = IV, an initial value */
  A = LLF_AES_WRAP_DEFAULT_IV;

  /* For i = 1 to n */
  /*   R[i] = P[i]  */
  memcpy(&(R[1]), DataIn_ptr, DataInLen);

  /* t = 0 */
  memset(&t, 0, sizeof(t));

  /**********************************************/ 
  /* 2) Calculate intermediate values           */
  /*   For j = 0 to 5                           */
  /*     For i = 1 to n                         */
  /*       B = AES(K, A | R[i])                 */
  /*       A = MSB(64, B) ^ t where t = (n*j)+i */
  /*       R[i] = LSB(64, B)                    */
  /**********************************************/ 

  /* For j = 0 to 5 */
  for (j = 0; j < 6; ++j) {          
    /* For i = 1 to n */
    for (i = 1; i <= nBlocks; ++i) {    
      /* A | R[i] */
      /* A = B[0] => B = B[0]B[1] = A|R[i] */
      B[1] = R[i]; 

      /* AES(K, A | R[i]) */
      error_code = aes_ecb_encrypt((unsigned char *)B, (unsigned char *)B, &skey);
      if (error_code != CRYPT_OK) {
        result = CE2_LLF_AES_MODULE_ERROR_BASE;
        goto error_case;
      }
      
      /* t = (nBlocks * j ) + i */
      LLF_AES_Set_t((unsigned char *)&t, (nBlocks*j) + i);

      /* A = MSB(64, B) ^ t where t = (n*j)+i */
      A ^= t; 

      /* R[i] = LSB(64, B) */
      R[i] = B[1];
    }
  } 

  /**************************/ 
  /* 3) Output the results. */
  /*   Set C[0] = A         */
  /*   For i = 1 to n       */
  /*     C[i] = R[i]        */
  /**************************/ 

  /* Set C[0] = A */
  R[0] =  A; 

  /* For i = 1 to n */
  /*   C[i] = R[i]  */
  memcpy(WrapDataOut_ptr, &R[0], *WrapDataLen_ptr);

error_case:
  if (R != DX_NULL)
    free(R);
  aes_done(&skey);
  return result;
} /* End of LLF_AES_Wrap */

/***************************************************************
* Function Name: 
*  LLF_AES_Unwrap
*
* @param WrapDataIn_ptr [in] - A pointer to wrapped data to be unwrapped 
*                   NOTE: Overlapping between the data input and 
*                   data output buffer is not allowed, except the 
*                   inplace case that is legal . 			
* @param WrapDataInLen [in] - Length of wrapped data in bytes. DataLen must be 
*                  multiple of 8 bytes and  must be in range [24, 2^29].
* @param KeyData [in] - A pointer to  key data (key encryption key - KEK). 
* @param KeySize [in] - Enumerator variable, defines length of key.
* @param DataOut_ptr [out] - A pointer to buffer for output of unwrapped data.
* @param DataOutLen_ptr [in/out] - A pointer to a buffer for input of size of user 
*                   passed buffer and for output of actual size of 
*                   unwrapped data in bytes. DataOutLen must be 
*                   multiple of 8 bytes and must be not less than 
*                   WrapDataInLen - CE2_AES_WRAP_BLOCK_SIZE_IN_BYTES.
*
* @returns \b
*  CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*   - CE2_LLF_AES_MODULE_ERROR_BASE
*
* \brief \b 
* Description:
*   The LLF_AES_Unwrap function performs inverse AES_Wrap transformation 
*   and implements the following algorithm (rfc3394, Sept. 2002): 
*  Inputs:  Ciphertext, (n+1) 64-bit values {C0, C1, ..., Cn}, and
*   K  - key (the KEK).
*  Outputs: Plaintext, n 64-bit values {P1, P2, ..., Pn}.
*
*  \b 
* Algorithm:
* -# Initialize LibTomCrypt etc.
* -# Calculate algorithm (rfc3394, Sept. 2002) 
* -# Output the results 
***************************************************************/
CE2Error_t LLF_AES_Unwrap(
                                     DxUint8_t            *WrapDataIn_ptr, /*in*/   
                                     DxUint32_t            WrapDataInLen,    /*in*/
                                     CE2_AES_Key_t        KeyData,        /*in*/
                                     CE2_AES_KeySize_t    KeySize,        /*in*/ 
                                     DxUint8_t            *DataOut_ptr,    /*out*/
                                     DxUint32_t           *DataOutLen_ptr     /*in/out*/ )
{
  CE2Error_t result = CE2_OK;
  int keyLen, cipherIndex, error_code, bad;
  unsigned int nBlocks = WrapDataInLen/CE2_AES_WRAP_BLOCK_SIZE_IN_BYTES - 1;
  int i, j;
  DxUint64_t *R = DX_NULL;
  DxUint64_t t;
  DxUint64_t B[2];
  symmetric_key skey;

#define A B[0]

  /*****************************/
  /*  Find LibTomCrypt cipher  */
  /*****************************/
  error_code = register_cipher(&aes_desc);
  if (error_code == -1 ) 
    return CE2_LLF_AES_MODULE_ERROR_BASE;

  cipherIndex = find_cipher("aes");
  if (cipherIndex == -1)
    return CE2_LLF_AES_MODULE_ERROR_BASE;

  /*****************************/
  /*     Choose key length     */
  /*****************************/
  switch (KeySize) {
    case CE2_AES_Key128BitSize:
      keyLen = 16;
      break;
    case CE2_AES_Key192BitSize:
      keyLen = 24;
      break;
    case CE2_AES_Key256BitSize:
      keyLen = 32;
      break;
    default:
      return CE2_LLF_AES_MODULE_ERROR_BASE;
  }

  /*****************************/
  /*  Setup LibTomCrypt key    */
  /*****************************/
  error_code = aes_setup((unsigned char *)KeyData, keyLen, 0, &skey);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_AES_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* Allocate R */ 
  R = malloc(sizeof(DxUint64_t)*(nBlocks + 1));
  if (R == DX_NULL) {
    result = CE2_LLF_AES_MODULE_ERROR_BASE;
    goto error_case;
  }

  /****************************/
  /* 1) Initialize variables  */
  /*   Set A = C[0]           */
  /*   For i = 1 to n         */
  /*     R[i] = C[i]          */
  /****************************/

  /* For i = 1 to n  */
  /*   R[i] = C[i]   */
  memcpy(&R[0], WrapDataIn_ptr, WrapDataInLen);

  /* Set A = C[0] */
  A = R[0];
 
  /* t = 0 */
  memset(&t, 0, sizeof(t));

  /********************************************************/
  /* 2) Calculate intermediate values.                    */
  /*   For j = 5 to 0                                     */
  /*     For i = n to 1                                   */
  /*       B = AES-1(K, (A ^ t) | R[i]) where t = n*j + i */
  /*       A = MSB(64, B)                                 */
  /*       R[i] = LSB(64, B)                              */
  /********************************************************/
  
  /* For j = 5 to 0 */
  for (j = 5; j >= 0  ; --j) {
    /* For i = n to 1 */
    for (i = nBlocks; i >= 1; --i) { 
      /* t = n*j + i */    
      LLF_AES_Set_t((unsigned char *)&t, (nBlocks*j) + i); 
      
      /* A ^ t */
      A ^= t; 

      /* (A ^ t) | R[i]) */
      B[1] = R[i]; 

      /* AES-1(K, (A ^ t) | R[i])   */ 
      /* A = B[0] => A = MSB(64, B) */
      error_code = aes_ecb_decrypt((unsigned char *)B, (unsigned char *)B, &skey);
      if (error_code != CRYPT_OK) {
        result = CE2_LLF_AES_MODULE_ERROR_BASE;
        goto error_case;
      }

      /* R[i] = LSB(64, B) */
      R[i] = B[1]; 
    }
  }

  /******************************************/
  /* 3) Output the results.                 */
  /*   If A is an appropriate initial value */ 
  /*   Then                                 */
  /*     For i = 1 to n                     */
  /*       P[i] = R[i]                      */
  /*   Else                                 */
  /*     Return an error                    */
  /******************************************/

  /* If A is an appropriate initial value */ 
  bad = memcmp(&A, &LLF_AES_WRAP_DEFAULT_IV, CE2_AES_WRAP_BLOCK_SIZE_IN_BYTES); 
  if (bad == 0) {
    /* For i = 1 to n */
    /*   P[i] = R[i]  */
    memcpy(DataOut_ptr, &(R[1]), *DataOutLen_ptr);
  } else {
    /* Return an error */
    result = CE2_LLF_AES_MODULE_ERROR_BASE;
    goto error_case;
  }

error_case:
  if (R != DX_NULL)
    free(R);
  aes_done(&skey);
  return result;
} /* End of LLF_AES_Unwrap */

/**
****************************************************************
* Function Name: 
*  LLF_AES_GenerateTweakValue
*
* @param Tweak_dst [out] - This parameter is a return value from generator Tweak Value.
* @param Key_ptr [in] - This parameter is a key used for generator.
* @param KeySize [in] - Size of key.
* @param SectorNumber [in] - 64-bit sector number.
*
* @returns \b
*  CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*   - CE2_XTS_AES_ILLEGAL_TWEAK_PTR_ERROR
*   - CE2_XTS_AES_ILLEGAL_KEY_PTR_ERROR
*
* \brief \b 
* Description:
*  Used for generating sector "tweak"
*
**  \b 
* Algorithm:
*  -# Verify input parameters;
*  -# Call low level function LLF_XTS_AES().
***************************************************************/
CE2Error_t LLF_AES_GenerateTweakValue(CE2_AES_XTS_Tweak_t Tweak_dst,
												   CE2_AES_Key_t       Key_ptr,
												   CE2_AES_KeySize_t   KeySize,
												   CE2_AES_XTS_Tweak_t SectorNumber )
{
  symmetric_ECB ecb;
  CE2Error_t result = CE2_OK;
  int keyLen, cipherIndex, error_code;
  int j;

  /* Find LibTomCrypt cipher */
  error_code = register_cipher(&aes_desc);
  if (error_code == -1 ) 
	  return CE2_LLF_AES_MODULE_ERROR_BASE;

  cipherIndex = find_cipher("aes");
  if (cipherIndex == -1)
	  return CE2_LLF_AES_MODULE_ERROR_BASE;

  /* Choose key length */
  switch (KeySize) {
	case CE2_AES_Key128BitSize:
		keyLen = 16;
		break;
	case CE2_AES_Key192BitSize:
		keyLen = 24;
		break;
	case CE2_AES_Key256BitSize:
		keyLen = 32;
		break;
	default:
		return CE2_LLF_AES_MODULE_ERROR_BASE;
  }

  /* Start LibTomCrypt ECB */
  error_code = ecb_start(cipherIndex, (unsigned char *)Key_ptr, 
	  keyLen, 0, &ecb);
  if (error_code != CRYPT_OK) {
	  result = CE2_LLF_AES_MODULE_ERROR_BASE;
	  goto error_;
  }

  /* Convert sector number to tweak plain text    */
  /* Note that TweakValue[] is padded with zeroes */ 
  for ( j = 0; j < LLF_XTS_AES_BLK_BYTES; j++) { 
	  Tweak_dst[j] = SectorNumber[j];
  }
//   for ( j = 0; j < LLF_XTS_AES_BLK_BYTES; j++) { 
//     Tweak_dst[j] = (DxUint8_t) (SectorNumber & 0xFF); 
//     SectorNumber = SectorNumber >> 8;  
//   }

  /* Encrypt the tweak */
  error_code = ecb_encrypt(Tweak_dst, Tweak_dst, LLF_XTS_AES_BLK_BYTES, &ecb);
  if (error_code != CRYPT_OK) 
    return CE2_LLF_AES_MODULE_ERROR_BASE;

error_:
  ecb_done(&ecb);
  return result;
}
/**
****************************************************************
* Function Name: 
*  LLF_XTS_AES
*
* @param Tweak_ptr [in] - Pointer to buffer containing tweak value.
* @param Key_ptr [in] - Pointer to buffer containing AES key used for XTS-AES cipher.
* @param KeySize [in] - Size of AES key.
* @param EncryptDecryptFlag [in] - This flag specialize encrypt or decrypt operation.
* @param DataIn_ptr [in] - A pointer to input data.
* @param DataInSize [in] - Size of input data. It must be at least 16 bytes.
* @param DataOut_ptr [out] - A pointer to output data.
*
* @returns \b
*  CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*
* \brief \b 
* Description:
*  This function is used to make XTS-AES operations;
*
*  \b 
* Algorithm:
*  -# 
***************************************************************/
CE2Error_t LLF_XTS_AES(CE2_AES_XTS_Tweak_t     Tweak_ptr,
					   CE2_AES_Key_t           Key_ptr,
					   CE2_AES_KeySize_t       KeySize,
					   CE2_AES_EncryptMode_t   EncryptDecryptFlag, 
					   DxUint8_t               *DataIn_ptr,        
					   DxUint32_t              DataInSize,         
					   DxUint8_t               *DataOut_ptr )
{
  symmetric_ECB ecb;
  int keyLen, cipherIndex, error_code;
  CE2Error_t result = CE2_OK;

  /* Find LibTomCrypt cipher */
  error_code = register_cipher(&aes_desc);
  if (error_code == -1 ) 
    return CE2_LLF_AES_MODULE_ERROR_BASE;

  cipherIndex = find_cipher("aes");
  if (cipherIndex == -1)
    return CE2_LLF_AES_MODULE_ERROR_BASE;

  /* Choose key length */
  switch (KeySize) {
    case CE2_AES_Key128BitSize:
      keyLen = 16;
      break;
    case CE2_AES_Key192BitSize:
      keyLen = 24;
      break;
    case CE2_AES_Key256BitSize:
      keyLen = 32;
      break;
    default:
      return CE2_LLF_AES_MODULE_ERROR_BASE;
  }

  /* Start LibTomCrypt ECB */
  error_code = ecb_start(cipherIndex, (unsigned char *)Key_ptr, 
    keyLen, 0, &ecb);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_AES_MODULE_ERROR_BASE;
    goto error_case1;
  }

  /* Call encrypt or decrypt sub-method */
  if (EncryptDecryptFlag == CE2_AES_Encrypt) {
    result = LLF_XTS_AES_Encrypt(Tweak_ptr, &ecb,
      DataIn_ptr, DataInSize, DataOut_ptr);
  } else {
    result = LLF_XTS_AES_Decrypt(Tweak_ptr, &ecb,
      DataIn_ptr, DataInSize, DataOut_ptr);
  }

error_case1:
  ecb_done(&ecb);
  return result;
} /* End of LLF_XTS_AES */

/**
****************************************************************
* Function Name: 
*  LLF_AES_CCM
*
* @param EncrDecrMode [in] - Enumerator variable defining operation mode (0 - encrypt; 1 - decrypt).
* @param CCM_Key [in] - A buffer, containing the AESCCM key passed by user (predefined size 128 bits).
* @param QFieldSize [in] - Byte-size of formatted field for writing significant bytes of the TextSizeQ 
*                          value. Valid values according to our implementation: [2,3,4]. 
* @param N_ptr [in] - A pointer to Nonce - unique value assigned to all data passed into CCM.
*                     Bytes order - big endian form (MSB is the first).
* @param SizeOfN [in] - The size of the user passed Nonce (in bytes). 	Valid values: 
*                       7 <= SizeOfN <= (15-QFieldSize). 
* @param ADataIn_ptr [in] - A pointer to the additional data buffer. The pointer does 
*                           not need to be aligned. On CSI input mode the pointer must be equal to 
*                           value (0xFFFFFFFC | DataInAlignment). 
* @param ADataInSize [in] - The size of the additional data in bytes;  
* @param TextDataIn_ptr [in] - A pointer to the input text data buffer (plain or cipher according to
*                              encrypt-decrypt mode). The pointer does not need to be aligned.
*                              On CSI input mode the pointer must be equal to value (0xFFFFFFFC | DataInAlignment). 
* @param TextDataInSize [in] - The size of the input text data in bytes;  
* @param TextDataOut_ptr [out] - The output text data pointer (cipher or plain text data). 
* @param TextDataOutSize [in] - The size of the output text data buffer. The size of buffer must be enough 
*                               for output the data:
*                                   - on Encrypt mode: TextDataOutSize >= TextDataInSize + SizeOfT.
*                                   - on Decrypt mode: TextDataOutSize >= TextDataInSize - SizeOfT.
* @param SizeOfT [in] - Size of AES-CCM MAC output T in bytes. Valid values: [4,6,8,10,12,14,16].   
*
* @returns \b
*  CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*
* \brief \b 
* Description:
*  This function is used to perform the AES_CCM operation in one integrated process.
*  The function preforms CCM algorithm according to NIST 800-38C.
*
*  \b 
* Algorithm:
*  -# Verify input parameters;
*  -# Call low level function LLF_AES_CCM().
***************************************************************/
CE2CIMPORT_C CE2Error_t LLF_AES_CCM(CE2_AES_EncryptMode_t  EncrDecrMode,
                                    CE2_AESCCM_Key_t       CCM_Key,
                                    DxUint8_t              QFieldSize,
                                    DxUint8_t             *N_ptr,
                                    DxUint8_t              SizeOfN,
                                    DxUint8_t             *ADataIn_ptr,
                                    DxUint32_t             ADataInSize,
                                    DxUint8_t             *TextDataIn_ptr,
                                    DxUint32_t             TextDataInSize,
                                    DxUint8_t             *TextDataOut_ptr,
                                    DxUint32_t             TextDataOutSize,
                                    DxUint8_t              SizeOfT)
{
  int keyLen, cipherIndex, error_code;
  const unsigned char *key_ptr;
  unsigned char nonce[CE2_AESCCM_NONCE_MAX_SIZE_BYTES]=
  {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
  unsigned char tag[4];
  unsigned char *tag_ptr;
  unsigned long tagLen;

  /*****************************/
  /*  Find LibTomCrypt cipher  */
  /*****************************/
  error_code = register_cipher(&aes_desc);
  if (error_code == -1 ) 
    return CE2_LLF_AES_MODULE_ERROR_BASE;

  cipherIndex = find_cipher("aes");
  if (cipherIndex == -1)
    return CE2_LLF_AES_MODULE_ERROR_BASE;

  /*Initialize nonce*/
  memcpy(&nonce[CE2_AESCCM_NQ_BLOCK_SIZE_BYTES - QFieldSize - SizeOfN], N_ptr, SizeOfN);
  SizeOfN = CE2_AESCCM_NQ_BLOCK_SIZE_BYTES - QFieldSize;

  /*****************************/
  /*     Choose key length     */
  /*****************************/
  keyLen  = sizeof(CE2_AESCCM_Key_t);
  key_ptr = (const unsigned char *)CCM_Key;

  /*****************************/
  /*    Make CCM operation     */
  /*****************************/
  if (EncrDecrMode == CE2_AES_Encrypt) {
    tag_ptr = &TextDataOut_ptr[TextDataInSize];
    tagLen  = SizeOfT;

	error_code = ccm_memory(cipherIndex, key_ptr, keyLen, 0,
      nonce, SizeOfN, ADataIn_ptr, ADataInSize,
      TextDataIn_ptr, TextDataInSize, TextDataOut_ptr, tag_ptr, &tagLen, 0);
    if (error_code != CRYPT_OK)
      return CE2_LLF_AES_MODULE_ERROR_BASE;
  } else {
    tag_ptr = tag;
    tagLen  = sizeof(tag);

	error_code = ccm_memory(cipherIndex, key_ptr, keyLen, 0,
      nonce, SizeOfN, ADataIn_ptr, ADataInSize,
      TextDataOut_ptr, TextDataInSize, TextDataIn_ptr, tag_ptr, &tagLen, 1);
    if (error_code != CRYPT_OK)
      return CE2_LLF_AES_MODULE_ERROR_BASE;
  }

  return CE2_OK;
} /* End of LLF_AES_CCM */

/**
****************************************************************
* Function Name: 
*  LLF_AES_GCM
*
* @param Key_ptr [in] - This is a AES key used for CCM cipher.
* @param KeySize [in] - A size in bytes of the AES key.
* @param IV_ptr [in] - A pointer to buffer with IV (Initial Vector). 
*                      It maybe null if IVSize = 0.
* @param IVSize [in] - A size of IV buffer in bytes;
* @param AAD_ptr [in] - A pointer to AAD (Additional Authentication Data) buffer. 
*                       It maybe null if AADSize = 0.
* @param AADSize [in] - A size of AAD buffer in bytes.
* @param EncryptDecryptFlag [in] - This flag specialize encrypt or decrypt operation. 
* @param DataIn_ptr [in] - A pointer to input data.        
* @param DataInSize [in] - Size of input data. It must be multiple of 16 bytes.         
* @param DataOut_ptr [out] - A pointer to output data.
* @param Tag_ptr [out] - A pointer to output tag buffer. 
* @param TagSize_ptr [in/out] - A pointer to size of tag buffer.
*                               Note: maximal used size of tag is 16 bytes.
*
* @returns \b
*  CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*
* \brief \b 
* Description:
*  This function is used to make GCM operations;
*
*  \b 
* Algorithm:
*  -# 
***************************************************************/
CE2Error_t LLF_AES_GCM(CE2_AES_Key_t         Key_ptr,
                       CE2_AES_KeySize_t     KeySize,
                       DxUint8_t             *IV_ptr, 
                       DxUint32_t            IVSize,
                       DxUint8_t             *AAD_ptr, 
                       DxUint32_t            AADSize,
                       CE2_AES_EncryptMode_t EncryptDecryptFlag, 
                       DxUint8_t             *DataIn_ptr,        
                       DxUint32_t            DataInSize,         
                       DxUint8_t             *DataOut_ptr,
                       DxUint8_t             *Tag_ptr, 
                       DxUint32_t            *TagSize_ptr)
{
  int keyLen, cipherIndex, error_code;

  /*****************************/
  /*  Find LibTomCrypt cipher  */
  /*****************************/
  error_code = register_cipher(&aes_desc);
  if (error_code == -1 ) 
    return CE2_LLF_AES_MODULE_ERROR_BASE;

  cipherIndex = find_cipher("aes");
  if (cipherIndex == -1)
    return CE2_LLF_AES_MODULE_ERROR_BASE;

  /*****************************/
  /*     Choose key length     */
  /*****************************/
  switch (KeySize) {
    case CE2_AES_Key128BitSize:
      keyLen = 16;
      break;
    case CE2_AES_Key192BitSize:
      keyLen = 24;
      break;
    case CE2_AES_Key256BitSize:
      keyLen = 32;
      break;
    default:
      return CE2_LLF_AES_MODULE_ERROR_BASE;
  }

  /*****************************/
  /*    Make GCM operation     */
  /*****************************/
  if (EncryptDecryptFlag == CE2_AES_Encrypt) {
    error_code = gcm_memory(cipherIndex, Key_ptr, keyLen, IV_ptr, IVSize, 
      AAD_ptr, AADSize, DataIn_ptr, DataInSize, DataOut_ptr, 
      Tag_ptr, TagSize_ptr, 0);
    if (error_code != CRYPT_OK)
      return CE2_LLF_AES_MODULE_ERROR_BASE;
  } else {
    error_code = gcm_memory(cipherIndex, Key_ptr, keyLen, IV_ptr, IVSize, 
      AAD_ptr, AADSize, DataOut_ptr, DataInSize, DataIn_ptr, 
      Tag_ptr, TagSize_ptr, 1);
    if (error_code != CRYPT_OK)
      return CE2_LLF_AES_MODULE_ERROR_BASE;
  }

  return CE2_OK;
} /* End of LLF_AES_GCM */
