/*
* Creation date : Tues Mar 28 09:00:00 2007
* Last modified : %modify_time%
*/
/** @file
* \brief This file contains implementation of C2. 
*
* \version LLF_C2.c#1:csrc:1
* \author Yermalayeu Ihar
* \remarks Copyright (C) 2007 by Discretix Technologies Ltd.
* All Rights reserved
*/

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

#include "LLF_C2.h"
#include <memory.h>
#include <stdio.h>

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

/* Logical left rotate macros: */ 
#define LLF_C2_lrot8(x, n) (((x)<<(n))|((x)>>(8-(n)))) 
#define LLF_C2_lrot32(x, n) (((DxUint32_t)(x)<<(n))|((DxUint32_t)(x)>>(32-(n))))

/* Convert big-endian numbers to little-endian and back */ 
#define LLF_C2_TurnRoundBytes(x) ((((x)>>24) & 0xffL) | (((x)>>8) & 0xff00L) | \
                                 (((x)<<8) & 0xff0000L) | (((x)<<24) & 0xff000000L))

/* The cipher has 10 rounds: */ 
#define LLF_C2_MAX_ROUND 10

/* The name of file which contains secret constant */ 
#define LLF_C2_SECRET_CONSTANT_FILE_NAME "CE2_C2_SecretConstant.ini"

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

#if 0
/* The secret constant. */ 
const DxUint8_t LLF_C2_SecretConstant[256] = {
  0xB6, 0xAA, 0xEB, 0xB3, 0x35, 0x5D, 0xEE, 0xB1, 0x72, 0x33, 0x05, 0x13, 0x6D, 0xC7, 0x6C, 0x27, 
  0x25, 0x54, 0xE9, 0x4C, 0xDE, 0xC3, 0x21, 0x39, 0xA9, 0xAB, 0xD6, 0xDF, 0xE8, 0x71, 0x94, 0xAE, 
  0x16, 0x44, 0x76, 0xCD, 0xB7, 0x78, 0x20, 0xF0, 0xC1, 0x9F, 0xCF, 0xAF, 0x0F, 0xCB, 0x59, 0x83, 
  0x3A, 0x5E, 0xB8, 0xB5, 0xF3, 0x47, 0x80, 0xC2, 0xF6, 0x14, 0xE6, 0x69, 0xFC, 0x17, 0xE0, 0xE5, 
  0x79, 0xF9, 0x12, 0xBF, 0x3C, 0xB4, 0x66, 0xAD, 0xF7, 0x65, 0x95, 0xF4, 0x4E, 0x02, 0xA0, 0x07, 
  0x4D, 0x2F, 0x0D, 0x7E, 0xE4, 0xEF, 0xA1, 0x8C, 0x6E, 0xD2, 0xFD, 0x19, 0x1C, 0x82, 0x42, 0xBB, 
  0x9A, 0x43, 0xC6, 0xE2, 0x1F, 0xF2, 0x75, 0x1A, 0x63, 0x45, 0xD1, 0x30, 0x81, 0x7F, 0x8E, 0x62, 
  0x3B, 0xA4, 0xFB, 0x1E, 0x5F, 0xBC, 0xB0, 0x40, 0x8B, 0x74, 0x38, 0x8A, 0xC4, 0x73, 0x9C, 0x09, 
  0xD4, 0xED, 0xD3, 0x5A, 0x60, 0x48, 0xC5, 0x9E, 0x01, 0xCC, 0x34, 0x1B, 0x58, 0x36, 0x23, 0x88, 
  0x7A, 0x90, 0x9B, 0x8F, 0xBD, 0x3F, 0xB9, 0x57, 0xA2, 0x3E, 0x04, 0xB2, 0x49, 0x37, 0x5C, 0x7D, 
  0x61, 0x4A, 0xA6, 0x67, 0xEC, 0x7C, 0x0E, 0x96, 0xDD, 0xE3, 0x2C, 0x56, 0x08, 0x0C, 0x8D, 0x2B, 
  0x6A, 0xFE, 0xEA, 0xA3, 0xCA, 0x3D, 0x91, 0xE7, 0xC9, 0xAC, 0x03, 0xD5, 0x89, 0x86, 0xDC, 0x10,
  0x55, 0x77, 0xC8, 0xD7, 0x97, 0x24, 0x46, 0x9D, 0x0A, 0x1D, 0x22, 0xD9, 0xFF, 0x5B, 0x52, 0xD8, 
  0x00, 0xFA, 0x53, 0x26, 0x29, 0x2E, 0x2A, 0x11, 0xC0, 0x6F, 0x4F, 0x7B, 0x28, 0x99, 0x41, 0x92, 
  0xDB, 0xF8, 0x50, 0xA8, 0x51, 0xA5, 0x4B, 0x93, 0x87, 0xDA, 0x06, 0x85, 0x2D, 0xBA, 0x0B, 0x98, 
  0x70, 0x6B, 0xBE, 0xF1, 0x18, 0xD0, 0x31, 0x68, 0x15, 0x84, 0x64, 0xE1, 0xCE, 0xA7, 0xF5, 0x32, 
};
#endif

DxUint8_t LLF_C2_SecretConstant[256];
EcDxBool_t LLF_C2_SecretConstantLoaded = FALSE;

/* A 64-bit fixed initial value 'h0'. */
// 01234567 89abcdef
const DxUint8_t LLF_C2_HASH_h0[CE2_C2_BLOCK_SIZE_IN_BYTES] = {
   0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef
	 //0x67, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab, 0x89
};

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

/**
****************************************************************
* Function Name: 
*  LLF_C2_LoadSecretConstant
*
*  @param fileName [in] - The name of file which contains secret constant.
*
* @returns \b
* @returns \b CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*   -# CE2_LLF_C2_LOAD_SECTET_CONSTANT_CANT_OPEN_FILE
*   -# CE2_LLF_C2_LOAD_SECTET_CONSTANT_CANT_READ_FILE
*
* \brief \b 
* Description:
*  This function loads secret constant from a file.
*
*  \b 
* Algorithm:
*  -# Open a file.
*  -# Read secret constant.
***************************************************************/
CE2Error_t LLF_C2_LoadSecretConstant(DxUint8_t* fileName)
{
  CE2Error_t result = CE2_OK;
  int error_code, i, n;
  FILE *file;

  file = fopen(fileName, "r");
  if (file <= 0) {
    return CE2_LLF_C2_LOAD_SECTET_CONSTANT_CANT_OPEN_FILE;
  }

  n = sizeof(LLF_C2_SecretConstant)/sizeof(DxUint8_t);
  for(i = 0; i < n; i++) {
#if 0
    error_code = fread_s(LLF_C2_SecretConstant + i*sizeof(DxUint8_t), 
      sizeof(DxUint8_t), sizeof(DxUint8_t), 1, file);
    if (error_code != 0) {
      result = CE2_LLF_C2_LOAD_SECTET_CONSTANT_CANT_READ_FILE;
      goto error_case;
    }
#endif 
#if 1
	//fscanf_s(file, "%X", LLF_C2_SecretConstant + i*sizeof(DxUint8_t));
    error_code = fscanf(file,"%X", LLF_C2_SecretConstant + i*sizeof(DxUint8_t));
    if (error_code != 1) {
      result = CE2_LLF_C2_LOAD_SECTET_CONSTANT_CANT_READ_FILE;
      goto error_case;
    }
#endif 
  }

  LLF_C2_SecretConstantLoaded = TRUE;

error_case:
  fclose(file);
  return result;
} /* End of LLF_C2_LoadSecretConstant */


/**
****************************************************************
* Function Name: 
*  LLF_C2_FeistelCipher
*
*  @param data [in] - plan input data.
*  @param key [in] - key.
*
* @returns \b
*    DxUint32_t crypted data
*
* \brief \b 
* Description:
*  This function encrypt data with Fiestel block cipher cipher.
*
*  \b 
* Algorithm:
*  -# Make Fiestel block cipher encryption.
***************************************************************/
DxUint32_t LLF_C2_FeistelCipher(DxUint32_t data, DxUint32_t key) 
{ 
  DxUint32_t t; 
  DxUint8_t v[4], u;

  /* Key Insertion */ 
  t = data + key; 
  
  /* Secret Constant */ 
  v[3] = (DxUint8_t)((t >> 24)&0xff); 
  v[2] = (DxUint8_t)((t >> 16)&0xff); 
  v[1] = (DxUint8_t)((t >> 8)&0xff); 
  v[0] = LLF_C2_SecretConstant[t&0xff]; 
  u = v[0]^0x65;
  v[1] ^= LLF_C2_lrot8(u, 1); 
  u = v[0]^0x2b; 
  v[2] ^= LLF_C2_lrot8(u, 5); 
  u = v[0]^0xc9; 
  v[3] ^= LLF_C2_lrot8(u, 2); 
  
  /* Rotate */ 
  t = ((DxUint32_t)v[3] << 24) | ((DxUint32_t)v[2] << 16) |
    ((DxUint32_t)v[1] << 8) | (DxUint32_t)v[0]; 
  t ^= LLF_C2_lrot32(t, 9) ^ LLF_C2_lrot32(t, 22); 

  return t;
} /* End of LLF_C2_FeistelCipher */

/**
****************************************************************
* Function Name: 
*  LLF_C2_ECB_Encrypt
*
*  @param key [in] - C2 cipher key.
*  @param inout [in/out] - Pointer to input/output 64-bits data block.
*
* @returns \b
*    nothing
*
* \brief \b 
* Description:
*  This function make C2 encryption in ECB (Electronic Code Book) mode.
*  Note: all values have big-endian format;
*
*  \b 
* Algorithm:
*  -# Encrypt in C2 ECB mode.
***************************************************************/
void LLF_C2_ECB_Encrypt(CE2_C2Cipher_Key_t key, DxUint32_t *inout) 
{ 
  DxUint32_t L, R, t; 
  DxUint32_t ktmpa, ktmpb, ktmpc, ktmpd; 
  DxUint32_t sk[LLF_C2_MAX_ROUND]; 
  int round; 
  
  /* Input Conversion */ 
  L = inout[0]; 
  R = inout[1]; 
  
  /* Key Schedule Generation */ 
  ktmpa = ((DxUint32_t)key[0]<<16)|((DxUint32_t)key[1]<<8)|
    (DxUint32_t)key[2]; 
  ktmpb = ((DxUint32_t)key[3]<<24)|((DxUint32_t)key[4]<<16)|
    ((DxUint32_t)key[5]<<8)|(DxUint32_t)key[6]; 

  for(round = 0; round < LLF_C2_MAX_ROUND; round++) { 
    ktmpa &= 0x00ffffff; 
    sk[round] = ktmpb + ((DxUint32_t)LLF_C2_SecretConstant[(ktmpa&0xff)^round]<<4); 
    /* This is a 56 bit rotate: */ 
    ktmpc = ktmpb >> (32 - 17); 
    ktmpd = ktmpa >> (24 - 17); 
    ktmpa = (ktmpa << 17)|ktmpc; 
    ktmpb = (ktmpb << 17)|ktmpd; 
  } 

  for(round = 0; round < LLF_C2_MAX_ROUND; round++) { 
    /* Fiestel net: */ 
    L += LLF_C2_FeistelCipher(R, sk[round]); 
    /* Swap */
    t = L; 
    L = R; 
    R = t; 
  } 
  /* Swap cancel */
  t = L; 
  L = R; 
  R = t;

  /* Output */ 
  *inout++ = L;
  *inout++ = R;
} /* End of LLF_C2_ECB_Encrypt */

/**
****************************************************************
* Function Name: 
*  LLF_C2_ECB_Decrypt
*
*  @param key [in] - C2 cipher key.
*  @param inout [in/out] - Pointer to input/output 64-bits data block.
*
* @returns \b
*    nothing
*
* \brief \b 
* Description:
*  This function make C2 decryption in ECB (Electronic Code Book) mode.
*  Note: all values have big-endian format;
*
*  \b 
* Algorithm:
*  -# Decrypt in C2 ECB mode.
***************************************************************/
void LLF_C2_ECB_Decrypt(CE2_C2Cipher_Key_t key, DxUint32_t *inout) 
{ 
  DxUint32_t L, R, t ; 
  DxUint32_t ktmpa, ktmpb, ktmpc, ktmpd; 
  DxUint32_t sk[LLF_C2_MAX_ROUND]; 
  int round; 
  
  /* Input Conversion */ 
  L = inout[0];
  R = inout[1];
  /* Key Generation */ 
  
  ktmpa = ((DxUint32_t)key[0]<<16)|((DxUint32_t)key[1]<<8)|
    (DxUint32_t)key[2]; 
  ktmpb = ((DxUint32_t)key[3]<<24)|((DxUint32_t)key[4]<<16)|
    ((DxUint32_t)key[5]<<8)|(DxUint32_t)key[6]; 
  
  for(round = 0; round < LLF_C2_MAX_ROUND; round++) { 
    ktmpa &= 0x00ffffff; 
    sk[round] = ktmpb + ((DxUint32_t)LLF_C2_SecretConstant[(ktmpa&0xff)^round]<<4); 
    /* 56 bit left rotate */ 
    ktmpc = ktmpb >> (32 - 17); 
    ktmpd = ktmpa >> (24 - 17); 
    ktmpa = (ktmpa << 17)|ktmpc; 
    ktmpb = (ktmpb << 17)|ktmpd; 
  } 
  
  for(round = LLF_C2_MAX_ROUND - 1; round >= 0; round--) { 
    /* Feistel net */ 
    L -= LLF_C2_FeistelCipher(R, sk[round]); 
    /* Swap */ 
    t = L; 
    L = R; 
    R = t; 
  } 
  /* Swap cancel */ 
  t = L; 
  L = R; 
  R = t; 
  
  /* Output */ 
  *inout++ = L; 
  *inout++ = R;
} /* End of LLF_C2_ECB_Decrypt */

/**
****************************************************************
* Function Name: 
*  LLF_C2_C_CCB_Encrypt
*
*  @param key [in] - C2 cipher key.
*  @param inout [in/out] - Pointer to input/output data.
*  @param length [in] - length of input message;
*
* @returns \b
*    nothing
*
* \brief \b 
* Description:
*  This function make C2 encryption in C-CBC 
*  (Converted Cipher Block Chaining) mode.
*  Note: all values have big-endian format;
*
*  \b 
* Algorithm:
*  -# Encrypt in C2 C-CBC mode.
***************************************************************/
void LLF_C2_C_CCB_Encrypt(const CE2_C2Cipher_Key_t key, 
                          DxUint32_t *inout, 
                          int length) 
{ 
  DxUint32_t L, R, t; 
  DxUint32_t ktmpa, ktmpb, ktmpc, ktmpd; 
  DxUint32_t sk[LLF_C2_MAX_ROUND]; 
  DxUint8_t inkey[7]; 
  int keyRound = LLF_C2_MAX_ROUND; /* first time through, then becomes 2 */
  int round, i; 
  
  memcpy(inkey, key, 7); 

  for (i = 0; i < length; i += 8) { 
    /* Input Conversion */ 
    L = inout[0]; 
    R = inout[1];

    /* Key Schedule Generation */ 
    ktmpa = ((DxUint32_t)inkey[0]<<16)|((DxUint32_t)inkey[1]<<8)|
      (DxUint32_t)inkey[2]; 
    ktmpb = ((DxUint32_t)inkey[3]<<24)|((DxUint32_t)inkey[4]<<16)|
      ((DxUint32_t)inkey[5]<<8)|(DxUint32_t)inkey[6]; 

    for(round = 0; round < keyRound; round++) { 
      ktmpa &= 0x00ffffff; 
      sk[round] = ktmpb + ((DxUint32_t)LLF_C2_SecretConstant[(ktmpa&0xff)^round]<<4); 
      /* This is a 56 bit rotate: */ 
      ktmpc = ktmpb >> (32 - 17); 
      ktmpd = ktmpa >> (24 - 17); 
      ktmpa = (ktmpa << 17)|ktmpc; 
      ktmpb = (ktmpb << 17)|ktmpd; 
    } 
    
    for(round = 0; round < LLF_C2_MAX_ROUND; round++) { 
      /* Feistel net: */ 
      L += LLF_C2_FeistelCipher(R, sk[round % keyRound]); 
      if (round == 4) { 
        /* key chaining */ 
        inkey[0] = key[0] ^ (DxUint8_t)(R >> 16); 
        inkey[1] = key[1] ^ (DxUint8_t)(R >> 8); 
        inkey[2] = key[2] ^ (DxUint8_t)R; 
        inkey[3] = key[3] ^ (DxUint8_t)(L >> 24); 
        inkey[4] = key[4] ^ (DxUint8_t)(L >> 16); 
        inkey[5] = key[5] ^ (DxUint8_t)(L >> 8); 
        inkey[6] = key[6] ^ (DxUint8_t)L; 
      } 
      /* Swap */ 
      t = L; 
      L = R; 
      R = t; 
    } 
    /* Swap cancel */ 
    t = L; 
    L = R; 
    R = t; 
    
    /* Output */ 
    *inout++ = L; 
    *inout++ = R; 

    /* Subsequent blocks after the first use a truncated key schedule: */ 
    keyRound = 2; 
  } 
} /* End of LLF_C2_C_CCB_Encrypt */

/**
****************************************************************
* Function Name: 
*  LLF_C2_C_CCB_Decrypt
*
*  @param key [in] - C2 cipher key.
*  @param inout [in/out] - Pointer to input/output data.
*  @param length [in] - length of input message;
*
* @returns \b
*    nothing
*
* \brief \b 
* Description:
*  This function make C2 decryption in C-CBC 
*  (Converted Cipher Block Chaining) mode.
*  Note: all values have big-endian format;
*
*  \b 
* Algorithm:
*  -# Decrypt in C2 C-CBC mode.
***************************************************************/
void LLF_C2_C_CCB_Decrypt(const CE2_C2Cipher_Key_t key, 
                          DxUint32_t *inout, 
                          int length) 
{ 
  DxUint32_t L, R, t; 
  DxUint32_t ktmpa, ktmpb, ktmpc, ktmpd; 
  DxUint32_t sk[LLF_C2_MAX_ROUND]; 
  DxUint8_t inkey[7]; 
  int keyRound = LLF_C2_MAX_ROUND; /* first time through, then becomes 2 */
  int i, round; 
  
  memcpy(inkey, key, 7); 
  
  for (i = 0; i < length; i += 8) { 
    /* Input Conversion */ 
    L = inout[0]; 
    R = inout[1]; 
    
    /* Key Generation */ 
    ktmpa = ((DxUint32_t)inkey[0]<<16)|((DxUint32_t)inkey[1]<<8)|
      (DxUint32_t)inkey[2]; 
    ktmpb = ((DxUint32_t)inkey[3]<<24)|((DxUint32_t)inkey[4]<<16)|
      ((DxUint32_t)inkey[5]<<8)|(DxUint32_t)inkey[6]; 
    
    for(round = 0; round < keyRound; round++) 
    { 
      ktmpa &= 0x00ffffff; 
      sk[round] = ktmpb + ((DxUint32_t)LLF_C2_SecretConstant[(ktmpa&0xff)^round]<<4); 
      /* 56 bit left rotate */ 
      ktmpc = ktmpb >> (32 - 17); 
      ktmpd = ktmpa >> (24 - 17); 
      ktmpa = (ktmpa << 17)|ktmpc; 
      ktmpb = (ktmpb << 17)|ktmpd; 
    } 
    
    for(round = LLF_C2_MAX_ROUND - 1; round >= 0; round--) 
    { 
      /* Feistel net */ 
      L -= LLF_C2_FeistelCipher(R, sk[round%keyRound]); 

      /* Swap */ 
      t = L; 
      L = R; 
      R = t; 

      if (round == 5) { 
        /* key chaining */ 
        inkey[0] = key[0] ^ (DxUint8_t)(R >> 16); 
        inkey[1] = key[1] ^ (DxUint8_t)(R >> 8); 
        inkey[2] = key[2] ^ (DxUint8_t)R; 
        inkey[3] = key[3] ^ (DxUint8_t)(L >> 24); 
        inkey[4] = key[4] ^ (DxUint8_t)(L >> 16); 
        inkey[5] = key[5] ^ (DxUint8_t)(L >> 8); 
        inkey[6] = key[6] ^ (DxUint8_t)L; 
      } 
    } 
    /* Swap cancel */ 
    t = L; 
    L = R; 
    R = t; 
    
    /* Output */ 
    *inout++ = L; 
    *inout++ = R;

    /* Subsequent blocks after the first use a truncated key schedule: */ 
    keyRound = 2; 
  } 
} /* End of LLF_C2_C_CCB_Decrypt */

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

/**
****************************************************************
* Function Name: 
*  LLF_C2
*
*  @param Key_ptr [in] - A pointer to a buffer of the key to 
*                        operate the C2Cipher operations.
*  @param EncryptDecryptFlag [in] - A flag that determines what 
*                                   operation is performed - 
*                                   encrypting the data or 
*                                   decrypting the data.
*  @param OperationMode [in] - The operation mode of the C2Cipher
*                              (ECB or C-CBC);
*  @param DataIn_ptr [in] - A pointer to a buffer of the input 
*                           data that the C2Cipher operation will 
*                           be executed on.
*  @param DataInSize [in] - The size of the input data block in bytes
*                           Must be a multiple of 8 bytes.
*  @param DataOut_ptr [out] - A pointer to the output buffer that 
*                             shall contain the results.
*
* @returns \b
*  CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*
* \brief \b 
* Description:
*  This function provides a C2 function for processing data.
*
*  \b 
* Algorithm:
*  -# Load the secret constant if need.
*  -# Choose sub-algorithm corresponding to given mode.
***************************************************************/
CE2Error_t LLF_C2(
      CE2_C2Cipher_Key_t           Key_ptr,            /*in*/     
      CE2_C2Cipher_EncryptMode_t   EncryptDecryptFlag, /*in*/
      CE2_C2Cipher_OperationMode_t OperationMode ,     /*in*/ 
      DxUint8_t                    *DataIn_ptr,        /*in*/ 
      DxUint32_t                   DataInSize,         /*in*/ 
      DxUint8_t                    *DataOut_ptr )      /*in/out*/
{
  DxUint32_t i;
  CE2Error_t error;

  /* Load the secret constant if need. */
  if (LLF_C2_SecretConstantLoaded == FALSE) {
    error = LLF_C2_LoadSecretConstant(LLF_C2_SECRET_CONSTANT_FILE_NAME);
    if (error != CE2_OK)
      return error;
  }

  memcpy(DataOut_ptr, DataIn_ptr, DataInSize);

  /* Choose sub-algorithm corresponding to given mode. */
  switch(OperationMode) {
    case CE2_C2Cipher_ECB_mode:
      switch(EncryptDecryptFlag) {
        case CE2_C2Cipher_Encrypt_mode:
          for (i = 0; i < DataInSize; i += CE2_C2_BLOCK_SIZE_IN_BYTES)
            LLF_C2_ECB_Encrypt(Key_ptr, (DxUint32_t*)(DataOut_ptr + i));
          break;
        case CE2_C2Cipher_Decrypt_mode:
          for (i = 0; i < DataInSize; i += CE2_C2_BLOCK_SIZE_IN_BYTES)
            LLF_C2_ECB_Decrypt(Key_ptr, (DxUint32_t*)(DataOut_ptr + i));
          break;
        default:
          return CE2_LLF_C2_MODULE_ERROR_BASE;
      }
      break;
    case CE2_C2Cipher_C_CBC_mode:
      switch(EncryptDecryptFlag) {
        case CE2_C2Cipher_Encrypt_mode:
          LLF_C2_C_CCB_Encrypt(Key_ptr, (DxUint32_t*)DataOut_ptr, DataInSize);
          break;
        case CE2_C2Cipher_Decrypt_mode:
          LLF_C2_C_CCB_Decrypt(Key_ptr, (DxUint32_t*)DataOut_ptr, DataInSize);
          break;
        default:
          return CE2_LLF_C2_MODULE_ERROR_BASE;
      }
      break;
    default:
      return CE2_LLF_C2_MODULE_ERROR_BASE;
  }

  return CE2_OK;
} /* End of LLF_C2 */

/**
****************************************************************
* Function Name: 
*  LLF_C2_HASH
*
*  @param[in] DataIn_ptr - A pointer to the buffer that stores the data 
*                          to be hashed .
*  @param[in] DataInSize - The size of the data to be hashed in bytes. 
*  @param[out] HashResultBuff - A pointer to the target buffer where the 
*                               C2 HASH result stored in the context is 
*                               loaded to.
*
* @returns \b
*  CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*
* \brief \b 
* Description:
*  This function performs all C2 HASH operations on one buffer of data.
*
*  \b 
* Algorithm:
*  -# Load the secret constant if need.
*  -# Pad input data by '10...0' to make the its total length a multiple of 64 bits.
*  -# Set 56 least significant bits of 64-bit value 'h0' as initial key value.
*  -# Process the main data blocks 
*     a. Executing the C2 ECB encrypt operation for i-th data block 
*     b. Performs XOR of encrypt output with i-th data block 
*     c. Set 56 least significant bits of 'HashResultBuff' as a next key value. 
*  -# Process (a-c) the last data block. Hash is result of this operation.
***************************************************************/
CE2Error_t LLF_C2_HASH(DxUint8_t              *DataIn_ptr,
                       DxUint32_t             DataSize,
                       CE2_C2HASH_Result_t    HashResultBuff)
{
  CE2Error_t error;
  DxUint32_t i, j, d;
  CE2_C2_BLOCK_BYTES_t lastPaddedBlock;
  CE2_C2Cipher_Key_t key;
  DxUint32_t *pCurrentBlock;

  /* Load the secret constant if need. */
  if (LLF_C2_SecretConstantLoaded == FALSE) {
    error = LLF_C2_LoadSecretConstant(LLF_C2_SECRET_CONSTANT_FILE_NAME);
    if (error != CE2_OK)
      return error;
  }

  d = DataSize/CE2_C2_BLOCK_SIZE_IN_BYTES;

  /* Pad input data by '10...0' to make the its total length a multiple of 64 bits. */
  i = DataSize%CE2_C2_BLOCK_SIZE_IN_BYTES;
  memcpy(lastPaddedBlock, DataIn_ptr + CE2_C2_BLOCK_SIZE_IN_BYTES*d, i);
  lastPaddedBlock[i++] = 0x80;
  memset(lastPaddedBlock + i, 0, CE2_C2_BLOCK_SIZE_IN_BYTES - i);
  /* Set 56 least significant bits of 64-bit value 'h0' as initial key value. */
  memcpy(key, LLF_C2_HASH_h0 + 1, sizeof(CE2_C2Cipher_Key_t));

  /* Process the main data blocks */
  pCurrentBlock = (DxUint32_t*)DataIn_ptr;
  for (i = 0; i < d; i++) {
    /* Executing the C2 ECB encrypt operation for i-th data block */
    memcpy(HashResultBuff, pCurrentBlock, CE2_C2_BLOCK_SIZE_IN_BYTES);
    LLF_C2_ECB_Encrypt(key, HashResultBuff);

    /* Performs XOR of encrypt output with i-th data block */
    for(j = 0; j < CE2_C2_BLOCK_SIZE_IN_WORDS; j++)
      HashResultBuff[j] ^= pCurrentBlock[j];

    /* Set 56 least significant bits of 'HashResultBuff' as a next key value */
		for(j = 0; j < CE2_C2_BLOCK_SIZE_IN_WORDS; j++)
			HashResultBuff[j] = LLF_C2_TurnRoundBytes(HashResultBuff[j]);
    memcpy(key, ((DxUint8_t*)HashResultBuff) + 1, sizeof(CE2_C2Cipher_Key_t));

    pCurrentBlock += CE2_C2_BLOCK_SIZE_IN_WORDS;
  }

  /* Process the last data block. Hash is result of this operation. */
  /* Executing the C2 ECB encrypt operation for last data block */
	pCurrentBlock = (DxUint32_t*)lastPaddedBlock;
	for(j = 0; j < CE2_C2_BLOCK_SIZE_IN_WORDS; j++)
		pCurrentBlock[j] = LLF_C2_TurnRoundBytes(pCurrentBlock[j]);
	memcpy(HashResultBuff, pCurrentBlock, CE2_C2_BLOCK_SIZE_IN_BYTES);
  LLF_C2_ECB_Encrypt(key, HashResultBuff);

  /* Performs XOR of encrypt output with last data block */
  for(i = 0; i < CE2_C2_BLOCK_SIZE_IN_WORDS; i++)
    HashResultBuff[i] ^= pCurrentBlock[i];

  return CE2_OK;
} /* End of LLF_HASH */

/**
****************************************************************
* Function Name: 
*  LLF_C2_OneWayFunc
*
*  @param[in] Data1_ptr - The pointer to the buffer of the input Data1 
*                         used as encrypting key. The pointer need to 
*                         be aligned to 32 bits.
*  @param[in] Data2 - The pointer to the 64-bits buffer of the input 
*                     Data2. The pointer need to be aligned to 32 bits.
*  @param[in/out] DataOut_ptr - The pointer to the 64-bits buffer for 
*                               output data. The pointer need to be 
*                               aligned to 32 bits. 
*
* @returns \b
*  CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*
* \brief \b 
* Description:
*  This function is used to perform C2 One Way operation on 64-bit block 
*  of data. The function executes the following major steps:
*  1. Executing the C2 ECB encrypt operation of Data2 with key = Data1 
*  by calling the low level function LLF_C2_ECB_Encrypt.
*  2. Performs XOR of encrypt output with Data2 and output this result.
*
*  \b 
* Algorithm:
*  -# Load the secret constant if need;
*  -# Executing the C2 ECB encrypt operation of Data2 with key = Data1;
*  -# Performs XOR of encrypt output with Data2 and output this result.
***************************************************************/
CE2Error_t  LLF_C2_OneWayFunc(CE2_C2Cipher_Key_t   Data1_ptr,     
                              CE2_C2_BLOCK_BYTES_t Data2_ptr,
                              CE2_C2_BLOCK_BYTES_t DataOut_ptr)
{
  CE2Error_t error;
  DxUint32_t i;

  /* Load the secret constant if need. */
  if (LLF_C2_SecretConstantLoaded == FALSE) {
    error = LLF_C2_LoadSecretConstant(LLF_C2_SECRET_CONSTANT_FILE_NAME);
    if (error != CE2_OK)
      return error;
  }

  /* Executing the C2 ECB encrypt operation of Data2 with key = Data1 */
  memcpy(DataOut_ptr, Data2_ptr, sizeof(CE2_C2_BLOCK_BYTES_t));
  LLF_C2_ECB_Encrypt(Data1_ptr, (DxUint32_t*)DataOut_ptr);

  /* Performs XOR of encrypt output with Data2 and output this result */
  for(i = 0; i < CE2_C2_BLOCK_SIZE_IN_WORDS; i++)
    ((DxUint32_t*)DataOut_ptr)[i] ^= ((DxUint32_t*)Data2_ptr)[i];

  return CE2_OK;
} /* End of OneWayFunc */
