/*
* Creation date : Tues Mar 03 09:00:00 2007
* Last modified : %modify_time%
*/
/** @file
* \brief This file contains implementation of low level
* function for ElGamal encryption/decryption, which worked with
* LibTomCrypt. 
*
* \version LLF_ECPKI_ELGAMAL.c#1:csrc:1
* \author Yermalayeu Ihar
* \remarks Copyright (C) 2007 by Discretix Technologies Ltd.
* All Rights reserved
*/

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

#include "LLF_ECPKI_ELGAMAL.h"
#include "LLF_ECPKI_Common.h"
#include "LLF_ECPKI_BUILD.h"
#include "tommath.h"

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

#define LLF_ECPKI_ELGAMAL_BUFFER_SIZE 128
#define LLF_ECPKI_ELGAMAL_SQRT_TRY_NUMBER 100
#define LLF_ECPKI_ELGAMAL_POINT_TRY_NUMBER 256

/************************ Enums *******************************/
/************************ Typedefs ****************************/
/************************ Global Data *************************/
/************************ Private function prototype **********/
/************************ Private Functions *******************/

#if 0
void BigNumDebugPrint(void *num, const char* comment)
{
  FILE *out;
  DxUint8_t buffer[255];
  DxUint32_t i_byte, size = 255;

  fopen_s(&out, "int_log.txt", "a+");
  if (num != NULL) {
    size = ltc_mp.unsigned_size(num);
    ltc_mp.unsigned_write(num, buffer);
    for (i_byte = 0; i_byte < size; i_byte++) {
      fprintf(out, "%X", (buffer[i_byte]&0xf0)/0x10);
      fprintf(out, "%X ", buffer[i_byte]&0x0f);
    }
  }
  if (comment != NULL)
    fprintf(out, " %s", comment);
  fprintf(out, "\n");

  fclose(out);
}
#endif

/**
****************************************************************
* Function Name: 
*  LLF_ECPKI_Add_Points
*
* Inputs:
* @param A [in] - A pointer to input point A. 
* @param B [in] - A pointer to input point B.
* @param C [out] - The pointer to output point C = A + B (or C = A - B if sub != 0).
* @param p [in] - The pointer to modulus (in LibTomCrypt format).
* @param a [in] - The pointer to a-parameter of a curve (in LibTomCrypt format).
* @param sub[in] - If sub != 0 then C = A - B.
*
* Outputs:
* @returns \b CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - nonzero value.
*
* \brief \b 
* Description:
*  Add (or subtract) A and B and store result in C.
*
* \b 
* Algorithm:
*  -# Initialize LibTomCrypt variables.
*  -# If sub != 0 then B = -B.
*  -# Make montgomery normalization for A and B.
*  -# C = A + B.
*  -# Map C to affine.
***************************************************************/
CE2Error_t LLF_ECPKI_Add_Points(ecc_point *A, ecc_point *B, ecc_point *C, 
                                void *p, void *a, int sub)
{
  void *mp = NULL, *mu = NULL;
  int error_code;
  CE2Error_t result = CE2_OK;
  ecc_point *A2, *B2;

  /* Initialize LibTomCrypt variables. */
  error_code = ltc_mp.montgomery_setup(p, &mp);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  error_code = ltc_mp.init(&mu);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  A2 = ltc_ecc_new_point();
  B2 = ltc_ecc_new_point();
  if (A2 == NULL || B2 == NULL) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* If sub != 0 then B = -B */
  if(sub != 0) {
    error_code = ltc_mp.sub(p, B->y, B2->y);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case;
    }
  } else {
    error_code = ltc_mp.copy(B->y, B2->y);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case;
    }
  }

  /* Make montgomery normalization for A and B. */
  error_code = ltc_mp.montgomery_normalization(mu, p);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  error_code = ltc_mp.mulmod(A->x, mu, p, A2->x);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  error_code = ltc_mp.mulmod(A->y, mu, p, A2->y);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  error_code = ltc_mp.mulmod(A->z, mu, p, A2->z);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  error_code = ltc_mp.mulmod(B->x, mu, p, B2->x);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  error_code = ltc_mp.mulmod(B2->y, mu, p, B2->y);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  error_code = ltc_mp.mulmod(B->z, mu, p, B2->z);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* Add A and B */
  error_code = ltc_mp.ecc_kptadd(A2, B2, C, p, mp, a);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* Map C to affine */
  error_code = ltc_mp.ecc_map(C, p, mp);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

error_case:
  if (mp != NULL)
    ltc_mp.montgomery_deinit(mp);
  if (mu != NULL)
    ltc_mp.deinit(mu);
  if (A2 != NULL)
    ltc_ecc_del_point(A2);
  if (B2 != NULL)
    ltc_ecc_del_point(B2);
  return result;
} /* End of LLF_ECPKI_Add_Points */

/**
****************************************************************
* Function Name: 
*  LLF_ECPKI_Div_2_Mod
*
* Inputs:
* @param in [in] - A pointer to input big number (in LibTomCrypt format). 
* @param pModulus [in] - A pointer to modulus big number (in LibTomCrypt format).
* @param out [out] - The pointer to output big number (in LibTomCrypt format).
*
* Outputs:
* @returns \b CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - nonzero value.
*
* \brief \b 
* Description:
*  This function makes modular division by 2.
*
* \b 
* Algorithm:
*  -# Verify if input value is odd number; then add modulus to input number.
*  -# Divide input by 2.
***************************************************************/
CE2Error_t LLF_ECPKI_Div_2_Mod(void *in, void *pModulus, void *out)
{
  int error_code;
  unsigned long remainder;

  /* Verify if input value is odd number */
  error_code = ltc_mp.modi(in, 2, &remainder);
  if (error_code != CRYPT_OK)
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  if (remainder == 1) {
    /* Add modulus to input number */
    error_code = ltc_mp.add(in, pModulus, out);
    if (error_code != CRYPT_OK)
      return CE2_LLF_ECPKI_MODULE_ERROR_BASE;

    /* Divide input by 2 */
    error_code = ltc_mp.div_2(out, out);
    if (error_code != CRYPT_OK)
      return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  } else {
    /* Divide input by 2 */
    error_code = ltc_mp.div_2(in, out);
    if (error_code != CRYPT_OK)
      return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  return CE2_OK;
} /* End of LLF_ECPKI_Div_2_Mod */

/**
****************************************************************
* Function Name: 
*  LLF_ECPKI_LucasSequences
*
* Inputs:
* @param Q [in] - A pointer to input argument big number (in LibTomCrypt format). 
* @param P [in] - A pointer to input argument big number (in LibTomCrypt format). 
* @param k [in] - A pointer to input argument big number (in LibTomCrypt format).
* @param pModulus [in] - A pointer to modulus big number (in LibTomCrypt format).
* @param U [out] - The pointer to output big number (in LibTomCrypt format).
* @param V [out] - The pointer to output big number (in LibTomCrypt format).
*
* Outputs:
* @returns \b CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - nonzero value.
*
* \brief \b 
* Description:
*  This function implements Lucas sequences algorithm. It need for LLF_ECPKI_Sqrt.
*  Let P and Q be nonzero integers. The Lucas sequences U(k) and V(k) for P, Q 
*  are defined by:
*  U(0) = 0, U(1) = 1, and U(k) = P*U(k – 1) – Q*U(k – 2) for k => 2.
*  V(0) = 2, V(1) = P, and V(k) = P*V(k – 1) – Q*V(k – 2) for k => 2.
*  Note: for large k an another algorithm is used.
*
* \b 
* Algorithm:
*  -# Initialize LibTomCrypt variables.
*  -# Compute D = P^2 – 4Q.
*  -# Let k = k(r) k(r–1) ... k(1) k(0) be the binary representation of k, 
*     where the most significant bit k(r) of k is 1.
*  -# Set U = 1, V = P.
*  -# For i from r – 1 down to 0 do.
*       1)  Set (U, V) = (U*V mod p, (V^2 + D*U^2)/2 mod p).
*       2)  If k(i) = 1, then set (U, V) = ((P*U + V)/2 mod p, (P*V + D*U)/2 mod p).
*  -# Output U and V.
***************************************************************/
CE2Error_t LLF_ECPKI_LucasSequences(void *Q, void *P, void *k, void* pModulus, 
                                    void *U, void *V)
{
  CE2Error_t result = CE2_OK, error;
  void *D, *t0, *t1, *t2;
  int error_code;
  DxUint32_t k_size_in_bytes, i_byte;
  DxUint8_t k_buf[LLF_ECPKI_ELGAMAL_BUFFER_SIZE], bit;

  /* Initialize LibTomCrypt variables */
  error_code = ltc_init_multi(&D, &t0, &t1, &t2, NULL);
  if (error_code != CRYPT_OK) {
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  /* Compute D = P^2 – 4Q. */
  /* t0 = P^2 */
  error_code = ltc_mp.mul(P, P, t0);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  /* t1 = 4Q */
  error_code = ltc_mp.muli(Q, 4, t1);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  /* t1 = P^2 – 4Q. */
  error_code = ltc_mp.sub(t0, t1, t1);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  /* D = (P^2 – 4Q) mod p. */
  error_code = ltc_mp.mpdiv(t1, pModulus, NULL, D);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* Let k = k(r) k(r–1) ... k(1) k(0) be the binary representation of k, */
  /* where the most significant bit k(r) of k is 1.                       */
  k_size_in_bytes = ltc_mp.unsigned_size(k);
  error_code = ltc_mp.unsigned_write(k, k_buf);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  /* Note: k_buf has big-endian k representation. */
  /* find most significant bit k(r) of k is 1 */
  for(bit = 0x80; (bit&k_buf[0]) == 0 ; bit >>= 1);

  /* Set U = 1, V = P. */
  /* U = 1 */
  error_code = ltc_mp.set_int(U, 1);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  /* V = P */
  error_code = ltc_mp.copy(P, V);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* For i from r – 1 down to 0 do */
  /* i = r - 1 */
  bit >>= 1;
  /* The bytes loop */
  for(i_byte = 0; i_byte < k_size_in_bytes; i_byte++) {
    /* The bits loop */
    for(;bit != 0; bit >>= 1) {
      /* Set (U, V) = (U*V mod p, (V^2 + D*U^2)/2 mod p) */
      /* t0 = U*V */
      error_code = ltc_mp.mul(U, V, t0);
      if (error_code != CRYPT_OK) {
        result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
        goto error_case;
      }
      /* t1 = V^2 */
      error_code = ltc_mp.sqr(V, t1);
      if (error_code != CRYPT_OK) {
        result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
        goto error_case;
      }
      /* t2 = U^2 */
      error_code = ltc_mp.sqr(U, t2);
      if (error_code != CRYPT_OK) {
        result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
        goto error_case;
      }
      /* t2 = D*U^2 */
      error_code = ltc_mp.mul(D, t2, t2);
      if (error_code != CRYPT_OK) {
        result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
        goto error_case;
      }
      /* t1 = V^2 + D*U^2 */
      error_code = ltc_mp.add(t1, t2, t1);
      if (error_code != CRYPT_OK) {
        result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
        goto error_case;
      }
      /* t1 = (V^2 + D*U^2)/2 */
      error = LLF_ECPKI_Div_2_Mod(t1, pModulus, t1);
      if (error != CE2_OK) {
        result = error;
        goto error_case;
      }
      /* V = (V^2 + D*U^2)/2 mod p */
      error_code = ltc_mp.mpdiv(t1, pModulus, NULL, V);
      if (error_code != CRYPT_OK) {
        result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
        goto error_case;
      }
      /* U = U*V mod p */
      error_code = ltc_mp.mpdiv(t0, pModulus, NULL, U);
      if (error_code != CRYPT_OK) {
        result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
        goto error_case;
      }

      /* If k(i) = 1 */
      if((bit&k_buf[i_byte]) != 0) {
        /* set (U, V) = ((P*U + V)/2 mod p, (P*V + D*U)/2 mod p) */
        /* t0 = P*U */
        error_code = ltc_mp.mul(P, U, t0);
        if (error_code != CRYPT_OK) {
          result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
          goto error_case;
        }
        /* t1 = P*U + V */
        error_code = ltc_mp.add(t0, V, t1);
        if (error_code != CRYPT_OK) {
          result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
          goto error_case;
        }
        /* t1 = (P*U + V)/2 */
        error = LLF_ECPKI_Div_2_Mod(t1, pModulus, t1);
        if (error != CE2_OK) {
          result = error;
          goto error_case;
        }
        /* t0 = P*V */
        error_code = ltc_mp.mul(P, V, t0);
        if (error_code != CRYPT_OK) {
          result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
          goto error_case;
        }
        /* t2 = D*U */
        error_code = ltc_mp.mul(D, U, t2);
        if (error_code != CRYPT_OK) {
          result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
          goto error_case;
        }
        /* t2 = P*V + D*U */
        error_code = ltc_mp.add(t0, t2, t2);
        if (error_code != CRYPT_OK) {
          result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
          goto error_case;
        }
        /* t2 = (P*V + D*U)/2 */
        error = LLF_ECPKI_Div_2_Mod(t2, pModulus, t2);
        if (error != CE2_OK) {
          result = error;
          goto error_case;
        }
        /* U = (P*U + V)/2 mod p */
        error_code = ltc_mp.mpdiv(t1, pModulus, NULL, U);
        if (error_code != CRYPT_OK) {
          result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
          goto error_case;
        }
        /* V = (P*V + D*U)/2 mod p */
        error_code = ltc_mp.mpdiv(t2, pModulus, NULL, V);
        if (error_code != CRYPT_OK) {
          result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
          goto error_case;
        }
      }
    }
    bit = 0x80;
  }

error_case:
  ltc_deinit_multi(D, t0, t1, t2, NULL);
  return result;
} /* End of LLF_ECPKI_LucasSequences */

/**
****************************************************************
* Function Name: 
*  LLF_ECPKI_Sqrt_1
*
* Inputs:
* @param pArgument [in] - A pointer to argument big number (in LibTomCrypt format). 
* @param pModulus [in] - A pointer to modulus big number (in LibTomCrypt format).
* @param u [in] - A pointer to big number (pModulus = 4*u + 3) (in LibTomCrypt format).
* @param pResult [out] - The pointer to output big number (in LibTomCrypt format).
*
* Outputs:
* @returns \b CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - nonzero value.
*
* \brief \b 
* Description:
*  This function implements first algorithm of determination of square roots.
*  It is called by main LLF_ECPKI_Sqrt function when pModulus = 4*u + 3.
*
* \b 
* Algorithm:
*  -# Initialize LibTomCrypt variables.
*  -# Compute x = pArgument^(u+1) mod pModulus.
*  -# Compute z = x^2 mod pModulus.
*  -# If z = pArgument, then output x; otherwise, output error code.
***************************************************************/
CE2Error_t LLF_ECPKI_Sqrt_1(void *pArgument, void *pModulus, void *u, void* pResult)
{
  CE2Error_t result = CE2_OK;
  void *z, *t0;
  int error_code, cmp_result;

  /* Initialize LibTomCrypt variables */
  error_code = ltc_init_multi(&z, &t0, NULL);
  if (error_code != CRYPT_OK) {
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  /* Compute pResult = pArgument^(u+1) mod pModulus */
  /* t0 = u + 1 */
  error_code = ltc_mp.addi(u, 1, t0);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  /* pResult = pArgument^(u+1) mod pModulus */
  error_code = ltc_mp.exptmod(pArgument, t0, pModulus, pResult);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* Compute z = pResult^2 mod pModulus */
  error_code = ltc_mp.sqrmod(pResult, pModulus, z);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* If z = a, then output x; otherwise, output error code */
  cmp_result = ltc_mp.compare(z, pArgument);
  if (cmp_result != LTC_MP_EQ) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

error_case:
  ltc_deinit_multi(z, t0, NULL);
  return result;
} /* End of LLF_ECPKI_Sqrt_1 */

/**
****************************************************************
* Function Name: 
*  LLF_ECPKI_Sqrt_2
*
* Inputs:
* @param pArgument [in] - A pointer to argument big number (in LibTomCrypt format). 
* @param pModulus [in] - A pointer to modulus big number (in LibTomCrypt format).
* @param u [in] - A pointer to big number (pModulus = 8*u + 5) (in LibTomCrypt format).
* @param pResult [out] - The pointer to output big number (in LibTomCrypt format).
*
* Outputs:
* @returns \b CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - nonzero value.
*
* \brief \b 
* Description:
*  This function implements second algorithm of determination of square roots.
*  It is called by main LLF_ECPKI_Sqrt function when pModulus = 8*u + 5.
*
* \b 
* Algorithm:
*  -# Ininialize LibTomCrypt variables.
*  -# Compute v = (2*pArgument)^u mod pModulus
*  -# Compute i = 2*pArgument*v^2 mod pModulus.
*  -# Compute x = pArgument*v*(i - 1) mod pModulus.
*  -# Compute z = x^2 mod pModulus.
*  -# If z = pArgument, then output x; otherwise, output error code.
***************************************************************/
CE2Error_t LLF_ECPKI_Sqrt_2(void *pArgument, void *pModulus, void *u, void* pResult)
{
  CE2Error_t result = CE2_OK;
  void *z, *v, *i, *t0, *t1;
  int error_code, cmp_result;

  /* Initialize LibTomCrypt variables */
  error_code = ltc_init_multi(&z, &v, &i, &t0, &t1, NULL);
  if (error_code != CRYPT_OK) {
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  /* Compute v = (2*pArgument)^u mod pModulus */
  /* t0 = 2*pArgument */
  error_code = ltc_mp.muli(pArgument, 2, t0);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  /* v = (2*pArgument)^u mod pModulus */
  error_code = ltc_mp.exptmod(t0, u, pModulus, v);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* Compute i = 2*pArgument*v^2 mod pModulus */
  /* t0 = v^2 */
  error_code = ltc_mp.sqrmod(v, pModulus, t0);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  /* t1 = 2*pArgument */
  error_code = ltc_mp.muli(pArgument, 2, t1);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  /* i = 2*pArgument*v^2 mod pModulus */
  error_code = ltc_mp.mulmod(t1, t0, pModulus, i);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* Compute x = pArgument*v*(i - 1) mod pModulus */
  /* t0 = i - 1 */
  error_code = ltc_mp.subi(i, 1, t0);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  /* t1 = v*(i - 1) */
  error_code = ltc_mp.mul(v, t0, t1);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  /* pResult = pArgument*v*(i - 1) mod pModulus */
  error_code = ltc_mp.mulmod(pArgument, t1, pModulus, pResult);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* Compute z = pResult^2 mod pModulus */
  error_code = ltc_mp.sqrmod(pResult, pModulus, z);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* If z = a, then output x; otherwise, output error code */
  cmp_result = ltc_mp.compare(z, pArgument);
  if (cmp_result != LTC_MP_EQ) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

error_case:
  ltc_deinit_multi(z, v, i, t0, t1, NULL);
  return result;
} /* End of LLF_ECPKI_Sqrt_2 */

/**
****************************************************************
* Function Name: 
*  LLF_ECPKI_Sqrt_3
*
* Inputs:
* @param pArgument [in] - A pointer to argument big number (in LibTomCrypt format). 
* @param pModulus [in] - A pointer to modulus big number (in LibTomCrypt format).
* @param u [in] - A pointer to big number (pModulus = 4*u + 1) (in LibTomCrypt format).
* @param pResult [out] - The pointer to output big number (in LibTomCrypt format).
*
* Outputs:
* @returns \b CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - nonzero value.
*
* \brief \b 
* Description:
*  This function implements third algorithm of determination of square roots.
*  It is called by main LLF_ECPKI_Sqrt function when pModulus = 4*u + 1.
*
* \b 
* Algorithm:
*  -# Ininialize LibTomCrypt variables.
*  -# Set Q = pArgument.
*  -# Generate an arbitrary (e.g. random or incremental) P with 0 =< P < pModulus.
*  -# Compute the Lucas sequence elements U = U(2u+1) mod p, V = V(2u+1) mod p.
*  -# If V^2 == 4Q (mod p), then output x = V/2 mod p and stop.
*  -# If U != +-1 (mod p), then exit with error.
*  -# Go to Step 3.
***************************************************************/
CE2Error_t LLF_ECPKI_Sqrt_3(void *pArgument, void *pModulus, void *u, void* pResult)
{
  CE2Error_t result = CE2_OK, error;
  int error_code, i_try, cmp_result;
  void *Q, *U, *k, *P, *V, *t0, *t1;

  /* Initialize LibTomCrypt variables */
  ltc_mp = ltm_desc;
  error_code = ltc_init_multi(&Q, &U, &k, &P, &V, &t0, &t1, NULL);
  if (error_code != CRYPT_OK) {
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  /* Set Q = pArgument */
  error_code = ltc_mp.copy(pArgument, Q);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  for (i_try = 0; i_try < LLF_ECPKI_ELGAMAL_SQRT_TRY_NUMBER; i_try++) {
    /* Generate an arbitrary (e.g. random or incremental) P with 0 =< P < pModulus. */
    /* if i_try < pModulus than set P = i_try; */
    cmp_result = ltc_mp.compare_d(pModulus, i_try);
    if (cmp_result == LTC_MP_GT) {
      /* i_try < pModulus; set P = i_try */
      error_code = ltc_mp.set_int(P, i_try);
      if (error_code != CRYPT_OK) {
        result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
        goto error_case;
      }
    } else {
      /* i_try >= pModulus; error case*/
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case;
    }

    /* Compute the Lucas sequence elements U = U(2u+1) mod p, V = V(2u+1) mod p */
    /* k = 2u */
    error_code = ltc_mp.muli(u, 2, k);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case;
    }
    /* k = 2u + 1 */
    error_code = ltc_mp.addi(k, 1, k);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case;
    }
    /* Compute the Lucas sequence */
    error = LLF_ECPKI_LucasSequences(Q, P, k, pModulus, U, V);
    if (error != CE2_OK) {
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case;
    }

    /* If V^2 == 4Q mod p, then output x = V/2 mod p and stop */
    /* t0 = V^2 mod p */
    error_code = ltc_mp.sqrmod(V, pModulus, t0);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case;
    }
    /* t1 = 4Q */
    error_code = ltc_mp.muli(Q, 4, t1);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case;
    }
    /* t1 = 4Q mod p */
    error_code = ltc_mp.mpdiv(t1, pModulus, NULL, t1);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case;
    }
    /* Verify if V^2 == 4Q mod p */
    cmp_result = ltc_mp.compare(t0, t1);
    if (cmp_result ==  LTC_MP_EQ) {
      /* pResult = V/2 mod p */
      error_code = ltc_mp.div_2(V, pResult);
      if (error_code != CRYPT_OK) {
        result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
        goto error_case;
      }
      /* Exit with positive result */
      goto function_end;
    }
    /* If U != +-1 (mod p), then exit with error */
    /* Verify if U != 1 */
    cmp_result = ltc_mp.compare_d(U, 1);
    if (cmp_result !=  LTC_MP_EQ) {
      /* Verify if U != -1 */
      /* Set t0 = 1 */
      error_code = ltc_mp.set_int(t0, 1);
      if (error_code != CRYPT_OK) {
        result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
        goto error_case;
      }
      /* t0 = -t0 = -1 */
      error_code = ltc_mp.neg(t0, t0);
      if (error_code != CRYPT_OK) {
        result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
        goto error_case;
      }
      /* Verify if U != -1 */
      cmp_result = ltc_mp.compare(U, t0);
      if (cmp_result !=  LTC_MP_EQ) {
        /* exit with error (square root is not exist) */
        result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
        goto error_case;
      }
    }
  }

function_end:
error_case:
  ltc_deinit_multi(Q, U, k, P, V, t0, t1, NULL);
  return result;
} /* End of LLF_ECPKI_Sqrt_3 */

/**
****************************************************************
* Function Name: 
*  LLF_ECPKI_Sqrt
*
* Inputs:
* @param pArgument [in] - A pointer to argument big number (in LibTomCrypt format). 
* @param pModulus [in] - A pointer to modulus big number (in LibTomCrypt format).
* @param pResult [out] - The pointer to output big number (in LibTomCrypt format).
*
* Outputs:
* @returns \b CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - nonzero value.
*
* \brief \b 
* Description:
*  Let p be an odd prime, and let a be an integer with 0 <= a < p. A square root (mod p)
*  of a is an integer x with 0 <= x < p and x^2 = a (mod p).  
*  If a = 0, then there is one square root (mod p), namely x = 0. If g != 0, then a has 
*  either 0 or 2 square roots (mod p). If x is one square root, then the other is p – x. 
*  This function determines whether a has square roots (mod p) and, if so, computes one.
*
* \b 
* Algorithm:
*  -# Ininialize LibTomCrypt variables.
*  -# Verify if pArgument is a zero? There is trivial solution (zero) in this case.
*  -# Algorithm section. Call coresponding function.
***************************************************************/
CE2Error_t LLF_ECPKI_Sqrt(void *pArgument, void *pModulus, void* pResult)
{
  CE2Error_t result = CE2_OK;
  int error_code;
  unsigned int rem_uint;
  void *u, *t0, *remainder;

  /* Initialize ltc_mp structure and other LibTomCrypt variables */
  ltc_mp = ltm_desc;
  error_code = ltc_init_multi(&u, &t0, &remainder, NULL);
  if (error_code != CRYPT_OK) {
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  /* Verify if pArgument == 0 */
  if (ltc_mp.compare_d(pArgument, 0) == LTC_MP_EQ) {
    /* In this case square root is 0 */
    error_code = ltc_mp.set_int(pResult, 0);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case;
    }
    goto function_end;
  }

  /* There is 3 algorithm of square root searching. This is selection one. */
  error_code = ltc_mp.set_int(t0, 8);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  /* pModulus = 8*u + remainder */
  error_code = ltc_mp.mpdiv(pModulus, t0, u, remainder);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  rem_uint = ltc_mp.get_int(remainder);
  switch(rem_uint) {
    /* This case is equivalent to pModulus = 4*u + 1 => using 3-rd algorithm. */
    case 1:
      /* Now pModulus = 8*u + 1; but must be pModulus = 4*u + 1 => u = 2u. */
      error_code = ltc_mp.muli(u, 2, u);
      if (error_code != CRYPT_OK) {
        result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
        goto error_case;
      }
      result = LLF_ECPKI_Sqrt_3(pArgument, pModulus, u, pResult);
      break;
    /* This case is equivalent to pModulus = 4*u + 3 => using 1-st algorithm. */
    case 3:
      /* Now pModulus = 8*u + 3; but must be pModulus = 4*u + 3 => u = 2u. */
      error_code = ltc_mp.muli(u, 2, u);
      if (error_code != CRYPT_OK) {
        result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
        goto error_case;
      }
      result = LLF_ECPKI_Sqrt_1(pArgument, pModulus, u, pResult);
      break;
    /* This case is equivalent to pModulus = 8*u + 5 => using 2-nd algorithm. */
    case 5:
      result = LLF_ECPKI_Sqrt_2(pArgument, pModulus, u, pResult);
      break;
    /* This case is equivalent to pModulus = 4*u + 3 => using 1-st algorithm. */
    case 7:
      /* Now pModulus = 8*u + 7; but must be pModulus = 4*u + 3 => u = 2u + 1. */
      /* u = 2u */
      error_code = ltc_mp.muli(u, 2, u);
      if (error_code != CRYPT_OK) {
        result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
        goto error_case;
      }
      /* u = 2u + 4 */
      error_code = ltc_mp.addi(u, 1, u);
      if (error_code != CRYPT_OK) {
        result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
        goto error_case;
      }
      result = LLF_ECPKI_Sqrt_1(pArgument, pModulus, u, pResult);
      break;
    default:
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

function_end:
error_case:
  ltc_deinit_multi(u, t0, remainder, NULL);
  return result;
} /* End of LLF_ECPKI_Sqrt */

/**
****************************************************************
* Function Name: 
*  LLF_ECPKI_GetOffset
*
* Inputs:
* @param modulus [in] - modulus (in LibTomCrypt format). 
* @param pResult [out] - A Pointer to the output offset value.
*
* Outputs:
* @returns \b CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - nonzero value.
*
* \brief \b 
* Description:
*  This function get value of offset for given modulus.
*
* \b 
* Algorithm:
*  -# Receive MSB from modulus.
*  -# If MSB less than 100 return 1 else 0.
***************************************************************/
CE2Error_t LLF_ECPKI_GetOffset(void *modulus, int *pOffset)
{
  DxUint8_t buffer[LLF_ECPKI_ELGAMAL_BUFFER_SIZE];
  CE2Error_t result = CE2_OK;
  int error_code;

  /* Convert modulus to big-endian format; MSB will be first. */
  error_code = ltc_mp.unsigned_write(modulus, buffer);
  if(error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* If MSB less than 100 return 1 else 0. */
  if (buffer[0] >= 100)
    *pOffset = 0;
  else
    *pOffset = 1;

error_case:
  return result;
} /* LLF_ECPKI_GetOffset */

#if 0
void LLF_ECPKI_LucasSequences_Test(void)
{
  CE2Error_t result = CE2_OK;
  long Ui, Vi, Pi, Qi, ki, pi, U0, U1, U2, V0, V1, V2;
  void *U, *V, *Q, *P, *k, *p;

  ltc_init_multi(&U, &V, &Q, &P, &k, &p, NULL);

  /* Test 1  LS(3, 2, 0..40, 41, [], []) */
  pi = 41; Qi = 3; Pi = 2;
  ltc_mp.set_int(p, pi);
  ltc_mp.set_int(Q, Qi);
  ltc_mp.set_int(P, Pi);

  U1 = 1; U2 = 0;
  V1 = Pi; V2 = 2;
  for (ki = 2; ki < 300; ki++) {
    /* Find Lucas Sequences with using LLF_ECPKI_LucasSequences() function */
    ltc_mp.set_int(k, ki);
    LLF_ECPKI_LucasSequences(Q, P, k, p, U, V);
    Ui = ltc_mp.get_int(U);
    if (ltc_mp.compare_d(U, 0) == LTC_MP_LT)
      Ui = pi - Ui;
    Vi = ltc_mp.get_int(V);
    if (ltc_mp.compare_d(V, 0) == LTC_MP_LT)
      Vi = pi - Vi;

    /* Find Lucas Sequences by ordinary path */
    U0 = (Pi*U1 - Qi*U2); 
    V0 = (Pi*V1 - Qi*V2);

    if (U0 >= pi) U0 %= pi;
    if (V0 >= pi) V0 %= pi;
    if (U0 < 0) U0 = pi - ((-U0)%pi);
    if (V0 < 0) V0 = pi - ((-V0)%pi);
//    printf("%d\t %d\t\t %d\t%d\n", U0, V0, Ui, Vi);

    if (Ui != U0 || Vi != V0)
      printf("Wrong LucasSequences with k = %d!\n", ki);

    U2 = U1; U1 = U0;
    V2 = V1; V1 = V0;
  }

  ltc_deinit_multi(U, V, Q, P, k, p, NULL);
}

void LLF_ECPKI_Sqrt_Test(void)
{
  CE2Error_t result = CE2_OK;
  unsigned long x_uint;
  int res;
  void *a, *x, *p;

  ltc_mp = ltm_desc;
  ltc_init_multi(&a, &x, &p, NULL);


  /* Test 1 (positive)  sqrt 23 mod 43 = 25 or 18 (p = 4u + 3) */
  ltc_mp.set_int(a, 23);
  ltc_mp.set_int(p, 43);
  result = LLF_ECPKI_Sqrt(a, p, x);
  if (result != CE2_OK)
    printf("Test 1: Return error instead of CE2_OK!\n");
  x_uint = ltc_mp.get_int(x);
  if ((x_uint != 25) && (x_uint != 18))
    printf("Test 1: Return wrong sqrt value!\n");

  /* Test 2 (positive)  sqrt 30 mod 37 = 17 or 20 (p = 8u + 5) */
  ltc_mp.set_int(a, 30);
  ltc_mp.set_int(p, 37);
  result = LLF_ECPKI_Sqrt(a, p, x);
  if (result != CE2_OK)
    printf("Test 2: Return error instead of CE2_OK!\n");
  x_uint = ltc_mp.get_int(x);
  if ((x_uint != 17) && (x_uint != 20))
    printf("Test 2: Return wrong sqrt value!\n");

  /* Test 3 (positive)  sqrt 10 mod 41 = 16 or 25 (p = 4u + 1 != 8u + 5) */
  ltc_mp.set_int(a, 10);
  ltc_mp.set_int(p, 41);
  result = LLF_ECPKI_Sqrt(a, p, x);
  if (result != CE2_OK)
    printf("Test 3: Return error instead of CE2_OK!\n");
  x_uint = ltc_mp.get_int(x);
  if ((x_uint != 16) && (x_uint != 25))
    printf("Test 3: Return wrong sqrt value!\n");

  /* Test 4 (negative)  sqrt 2 mod 11 - root is not exist (p = 4u + 3) */
  ltc_mp.set_int(a, 2);
  ltc_mp.set_int(p, 11);
  result = LLF_ECPKI_Sqrt(a, p, x);
  if (result == CE2_OK)
    printf("Test 1: Return CE2_OK instead of error!\n");

  /* Test 5 (negative)  sqrt 2 mod 37 - root is not exist (p = 8u + 5) */
  ltc_mp.set_int(a, 2);
  ltc_mp.set_int(p, 37);
  result = LLF_ECPKI_Sqrt(a, p, x);
  if (result == CE2_OK)
    printf("Test 5: Return error instead of CE2_OK!\n");

  /* Test 6 (negative)  sqrt 3 mod 41 - root is not exist (p = 4u + 1 != 8u + 5) */
  ltc_mp.set_int(a, 3);
  ltc_mp.set_int(p, 41);
  result = LLF_ECPKI_Sqrt(a, p, x);
  if (result == CE2_OK)
    printf("Test 3: Return CE2_OK instead of error!\n");

  /* Test 7 (positive)  sqrt 81 mod prime = 9 */
  ltc_mp.set_int(a, 81);
  ltc_mp.read_radix(p, (char*)ltc_ecc_sets[8].prime, 16);
  ltc_mp.isprime(p, &res);
  result = LLF_ECPKI_Sqrt(a, p, x);
  if (result != CE2_OK)
    printf("Test 7: Return error instead of CE2_OK!\n");

  ltc_deinit_multi(a, x, p, NULL);
}
#endif

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

/**
****************************************************************
* Function Name: 
*  LLF_ECPKI_ELGAMAL_Encrypt
*
* Inputs:
* @param ReceiverUserPublKey_ptr [in] - A pointer to receiver public key 
*                           (key in little endian)
* @param MessageIn_ptr [in] - A pointer to message to be encrypted (message in big endian).
* @param MessageInSizeBytes [in] - A size of incoming message in bytes. The size must be
*                       MessageInSizeBytes =< ModulusSizeInBytes -  1.
*                       Note: in case CE2_ECPKI_DomainID_secp521r1 
*                       MessageInSizeBytes =< ModulusSizeInBytes -  2.
* @param EncrMessageOut_ptr [out] - A pointer to buffer for encrypted message.
* @param IsEphemerKeyInternal [in] - A parameter defining whether ephemeral 
*                         key internal or external
* @param EphemerPrivKeyIn_ptr [in] - A pointer to ephemeral private key.
* @param EphemerPrivKeySizeBytes [in] - A size (in bytes) of sender's ephemeral 
*                            private key data.
*
* Outputs:
* @returns \b CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*
* \brief \b 
* Description:
*  This function make Elgamal encryption.
*
* \b 
* Algorithm:
*  -# Create internal or import external ephemeral key
*  -# Initialization of LibTomCrypt primitives 
*  -# Convert input message to a point at the elliptical curve M
*  -# Encrypt message B 
*  -# Build output message (V, B); V - public ephemeral key
***************************************************************/
CE2Error_t  LLF_ECPKI_ELGAMAL_Encrypt (
					CE2_ECPKI_UserPublKey_t       *ReceiverUserPublKey_ptr, /*in*/
					DxUint8_t                      *MessageIn_ptr,           /*in*/
					DxUint32_t                      MessageInSizeBytes,      /*in*/
					DxUint8_t			           *EncrMessageOut_ptr,      /*out*/
					DxInt8_t                        IsEphemerKeyInternal,    /*in*/
		            DxUint8_t                      *EphemerPrivKeyIn_ptr,    /*in*/ 
					DxUint32_t                      EphemerPrivKeySizeBytes /*in*/)
{
  CE2_ECPKI_UserPrivKey_t EphemerPrivKey;
  CE2_ECPKI_UserPublKey_t EphemerPublKey;
  CE2Error_t result = CE2_OK, error;
  DxUint32_t size, modulusSize, pos, frontZero;
  DxUint8_t buffer[LLF_ECPKI_ELGAMAL_BUFFER_SIZE];
  int i, cmp_result, error_code = CRYPT_OK;
  ecc_key ephemer_key, receiver_key;
  ecc_point B, M;
  void *prime, *a, *order, *b, *t0, *t1;
  DxUint8_t *pChangeByte_ptr;
  int offset = 0;
  int k = 0;

  /* Initialize ltc_mp structure */
  ltc_mp = ltm_desc;

  modulusSize = LLF_ECPKI_DomainIdToModulusSize(
    ReceiverUserPublKey_ptr->DomainID); 

  /* Create internal or import external ephemeral key */
  if (IsEphemerKeyInternal == TRUE) {
    error_code = CE2_ECPKI_GenKeyPair(ReceiverUserPublKey_ptr->DomainID, 
      &EphemerPrivKey, &EphemerPublKey);
    if (error_code != CE2_OK) {
      return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    }
  } else {
    error_code = LLF_ECPKI_BuildPrivKey(ReceiverUserPublKey_ptr->DomainID,
      EphemerPrivKeyIn_ptr, EphemerPrivKeySizeBytes, &EphemerPrivKey); 
    if (error_code != CRYPT_OK) {
      return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    }
  } 
  error_code =  ecc_import(EphemerPrivKey.PrivKeyDbBuff, 
    EphemerPrivKey.valid_tag, (DomainID_t)EphemerPrivKey.DomainID, &ephemer_key);
  if (error_code != CRYPT_OK) {
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  /* Import receiver key */
  error_code =  ecc_import(ReceiverUserPublKey_ptr->PublKeyDbBuff, 
    ReceiverUserPublKey_ptr->valid_tag, (DomainID_t)ReceiverUserPublKey_ptr->DomainID, 
    &receiver_key);
  if (error_code != CRYPT_OK) {
    ecc_free(&ephemer_key);
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  /* Initialization of LibTomCrypt primitives */
  error_code = ltc_init_multi(&B.x, &B.y, &B.z, &M.x, &M.y, &M.z, &prime, &a, 
    &order, &b, &t0, &t1, NULL);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  error_code = ltc_mp.read_radix(prime, 
    (char*)ltc_ecc_sets[receiver_key.idx].prime, 16);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  error_code = ltc_mp.read_radix(order, 
    (char*)ltc_ecc_sets[receiver_key.idx].order, 16);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  error_code = ltc_mp.read_radix(a, (char *)ltc_ecc_sets[receiver_key.idx].A, 16);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  error_code = ltc_mp.read_radix(b, (char *)ltc_ecc_sets[receiver_key.idx].B, 16);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /*------------------------------------------------------------*/
  /* Convert input message to a point at the elliptical curve M */ 
  /*------------------------------------------------------------*/
  /* Set 1 to z-coordinate */
  error_code = ltc_mp.set_int(M.z, 1);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  /* If value of MSByte of modulus > 100, than set pointer to changing byte to
  MSB byte of message buffer, else to antecedent byte and set 0 to these bytes */
  /* Get offset (0 or 1) for given EC modulus */
  error = LLF_ECPKI_GetOffset(prime, &offset);
  if(error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  pChangeByte_ptr = (DxUint8_t*)buffer + offset;
  *pChangeByte_ptr = 0;

  /* Copy input message to the temporary buffer.                         */ 
  /* Note: first byte is most significant -> it not used can be changed. */
  memset(buffer, 0, modulusSize);
  memcpy(buffer + 1 + offset, MessageIn_ptr, MessageInSizeBytes);
  for(i = 0; i < LLF_ECPKI_ELGAMAL_POINT_TRY_NUMBER; i++) {
    /* Change most significant byte while y^2 != x^3 + a*x + b mod prime */
    *pChangeByte_ptr = (DxUint8_t)i;
    /* Set x-coordinate */
    error_code = ltc_mp.unsigned_read(M.x, buffer, MessageInSizeBytes + 1 + offset);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case;
    }
    /* M.x must be less than modulus */
    cmp_result = ltc_mp.compare(M.x, prime);
    if (cmp_result != LTC_MP_LT) {
      i = LLF_ECPKI_ELGAMAL_POINT_TRY_NUMBER;
      break;
    }

    /* t0 = x^2 */
    error_code = ltc_mp.sqr(M.x, t0);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case;
    }
    /* t0 = x^3 */
    error_code = ltc_mp.mul(t0, M.x, t0);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case;
    }
    /* t1 = a*x */
    error_code = ltc_mp.mul(a, M.x, t1);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case;
    }
    /* t1 = a*x + b */
    error_code = ltc_mp.add(b, t1, t1);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case;
    }
    /* t1 = x^3 + a*x + b */
    error_code = ltc_mp.add(t0, t1, t1);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case;
    }
    /* t1 = x^3 + a*x + b mod prime */
    error_code = ltc_mp.mpdiv(t1, prime, NULL, t1);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case;
    }

    /* Set y = sqrt(x^3 + a*x + b) mod prime */
    error = LLF_ECPKI_Sqrt(t1, prime, M.y);
    if (error == CE2_OK) {
      /* Exit from the loop */
      break;
    }
  }
  /* Verify if the message is converted to a point */
  if (i == LLF_ECPKI_ELGAMAL_POINT_TRY_NUMBER) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /*-----------------*/
  /* Encrypt message */
  /*-----------------*/
  /* B = ephemer_key.k*receiver_key.pubkey */ 
  error_code = ltc_mp.ecc_kptmul(ephemer_key.k, &receiver_key.pubkey, 
    &B, prime, 1, a);
  if (error_code != CRYPT_OK){
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  /* B = ephemer_key.k*receiver_key.pubkey + M */ 
  error = LLF_ECPKI_Add_Points(&M, &B, &B, prime, a, 0);
  if (error != CE2_OK){
    result = error;
    goto error_case;
  }

  /*-----------------------------*/
  /* Build output message (V, B) */ 
  /*-----------------------------*/
  /* Store V->x; Note V - public ephemeral key */
  pos = 0;
  size = modulusSize;
  error_code = mp_to_unsigned_bin_n(ephemer_key.pubkey.x, buffer, &size);
  if(error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  for (frontZero = 0; size + frontZero < modulusSize; frontZero++) {
    EncrMessageOut_ptr[pos] = 0x00;
    pos++;
  }
  memcpy(EncrMessageOut_ptr + pos, buffer, size);
  pos += size;
  /* Store V->y */
  size = modulusSize;
  error_code = mp_to_unsigned_bin_n(ephemer_key.pubkey.y, buffer, &size);
  if(error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  for (frontZero = 0; size + frontZero < modulusSize; frontZero++) {
    EncrMessageOut_ptr[pos] = 0x00;
    pos++;
  }
  memcpy(EncrMessageOut_ptr + pos, buffer, size);
  pos += size;
  /* Store B->x */
  size = modulusSize;
  error_code = mp_to_unsigned_bin_n(B.x, buffer, &size);
  if(error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  for (frontZero = 0; size + frontZero < modulusSize; frontZero++) {
    EncrMessageOut_ptr[pos] = 0x00;
    pos++;
  }
  memcpy(EncrMessageOut_ptr + pos, buffer, size);
  pos += size;
 /* Store B->y */
  size = modulusSize;
  error_code = mp_to_unsigned_bin_n(B.y, buffer, &size);
  if(error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  for (frontZero = 0; size + frontZero < modulusSize; frontZero++) {
    EncrMessageOut_ptr[pos] = 0x00;
    pos++;
  }
  memcpy(EncrMessageOut_ptr + pos, buffer, size);
  pos += size;

error_case:
  ecc_free(&ephemer_key);
  ecc_free(&receiver_key);
  ltc_deinit_multi(M.x, M.y, M.z, B.x, B.y, B.z, prime, a, order, t0, t1, 
    b, NULL);
  return result;
}

/**
****************************************************************
* Function Name: 
*  LLF_ECPKI_ELGAMAL_Decrypt
*
* Inputs:
* @param ReceiverUserPrivKey_ptr [in] - A pointer to a receiver private key 
*                            structure (in affine coordinates).    				
* @param EncrMessageIn_ptr [in] - The user passed pointer to the encrypted 
*                      message buffer.
* @param DecrMessageOut_ptr [out] - The user passed pointer to buffer for output of  
*                       decrypted message.
* @param DecrMessageOutSize_ptr [in] - A pointer to size of uzer passed buffer 
*                          for decrypted message (in) and actual size 
*                          of decrypted message (out).
*                          Input value must be *DecrMessageOutSize_ptr <= 
*                         (EC modulus size in bytes) - 1. 
*                         Note: in case CE2_ECPKI_DomainID_secp521r1 input value must be 
*                         *DecrMessageOutSize_ptr <= (EC modulus size in bytes) - 2.
*
* Outputs:
* @returns \b CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*
* \brief \b 
* Description:
*  This function make Elgamal decryption.
*
* \b 
* Algorithm:
*  -# Initialization of LibTomCrypt primitives
*  -# Read input message (V, B)
*  -# Decrypt message point M 
*  -# Build from M output message
***************************************************************/
CE2Error_t  LLF_ECPKI_ELGAMAL_Decrypt (			      
				CE2_ECPKI_UserPrivKey_t    *ReceiverUserPrivKey_ptr,  /*in*/	
				DxUint8_t   		       *EncrMessageIn_ptr,        /*in*/
				DxUint8_t		           *DecrMessageOut_ptr,       /*out*/
				DxUint32_t                 *DecrMessageOutSize_ptr   /*in*/)
{
  CE2Error_t result = CE2_OK, error;
  int error_code = CE2_OK;
  void *prime, *a, *order, *t0;
  DxUint32_t size, modulusSize, pos;
  DxUint8_t buffer[LLF_ECPKI_ELGAMAL_BUFFER_SIZE];
  DxUint8_t decrBuffer[LLF_ECPKI_ELGAMAL_BUFFER_SIZE];
  ecc_point B, V, M;
  ecc_key key;
  int offset = 0;
  int i = 0;

  /* Initialize ltc_mp structure */
  ltc_mp = ltm_desc;

  modulusSize = LLF_ECPKI_DomainIdToModulusSize(
    ReceiverUserPrivKey_ptr->DomainID); 

  error_code =  ecc_import(ReceiverUserPrivKey_ptr->PrivKeyDbBuff, 
    ReceiverUserPrivKey_ptr->valid_tag, 
    (DomainID_t)ReceiverUserPrivKey_ptr->DomainID, &key);
  if (error_code != CRYPT_OK) {
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  /* Initialization of LibTomCrypt primitives */
  error_code = ltc_init_multi(&V.x, &V.y, &V.z, &B.x, &B.y, &B.z, &M.x, &M.y, &M.z, 
    &prime, &a, &order, &t0, NULL);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  error_code = ltc_mp.read_radix(prime, 
    (char*)ltc_ecc_sets[key.idx].prime, 16);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  error_code = ltc_mp.read_radix(order, 
    (char*)ltc_ecc_sets[key.idx].order, 16);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  error_code = ltc_mp.read_radix(a, (char *)ltc_ecc_sets[key.idx].A, 16);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /*--------------------*/
  /* Read input message */
  /*--------------------*/
  pos = 0;
  /* Read V.x */
  error_code = ltc_mp.unsigned_read(V.x, EncrMessageIn_ptr + pos, 
    modulusSize);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  pos += modulusSize;
  /* Read V.y */
  error_code = ltc_mp.unsigned_read(V.y, EncrMessageIn_ptr + pos, 
    modulusSize);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  pos += modulusSize;
  /* Set 1 to V.z */
  error_code = ltc_mp.set_int(V.z, 1);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  /* Read B.x */
  error_code = ltc_mp.unsigned_read(B.x, EncrMessageIn_ptr + pos, 
    modulusSize);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  pos += modulusSize;
  /* Read B.y */
  error_code = ltc_mp.unsigned_read(B.y, EncrMessageIn_ptr + pos, 
    modulusSize);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  pos += modulusSize;
  /* Set 1 to B.z */
  error_code = ltc_mp.set_int(B.z, 1);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /*---------------------------------------*/
  /* Decrypt message point M = B - key.k*V */
  /*---------------------------------------*/
  /* M = key.k*V */ 
  error_code = ltc_mp.ecc_kptmul(key.k, &V, &M, prime, 1, a); 
  if (error_code != CRYPT_OK){
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  /* M = B - key.k*V */
  error = LLF_ECPKI_Add_Points(&B, &M, &M, prime, a, 1);
  if (error != CE2_OK){
    result = error;
    goto error_case;
  }

  /* Build decrypted output message */ 
  size = modulusSize;
  error_code = mp_to_unsigned_bin_n(M.x, buffer, &size);
  if(error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* Set actual size of decrypted message ( bytes which not may be changed during encryption )
  message size = (EC modulus size minus 1 or 2 according to EC domain) */

  /* Get offset (0 or 1) for given EC modulus */
  error = LLF_ECPKI_GetOffset(prime, &offset);
  if(error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  offset++;

  /* Most significant byte is not used */
  memset(decrBuffer, 0, modulusSize);

  // decrypted message contains all decrypted message (including first bytes for change and zeros at the beginning):
  // restore the zeros which were deleted by function mp_to_unsigned_bin_n:
  memcpy(decrBuffer + (modulusSize - size), buffer, size);

  // decrBuffer only contains the decrypted message in big endian format, so copy number of bytes according
  // to the given decrypted message size:
  memcpy(DecrMessageOut_ptr, decrBuffer + (modulusSize - *DecrMessageOutSize_ptr), *DecrMessageOutSize_ptr);

error_case:
  ltc_deinit_multi(V.x, V.y, V.z, B.x, B.y, B.z, M.x, M.y, M.z, 
    prime, a, order, t0, NULL);
  ecc_free(&key);
  return result;
}
