/*
* Creation date : Tues Mar 03 09:00:00 2007
* Last modified : %modify_time%
*/
/** @file
* \brief This file contains implementation of 
* export/import ECC key functions, which worked with 
* LibTomCrypt. 
*
* \version LLF_ECPKI_BUILD.c#1:csrc:1
* \author Yermalayeu Ihar
* \remarks Copyright (C) 2007 by Discretix Technologies Ltd.
* All Rights reserved
*/

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

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

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

/**
****************************************************************
* Function Name: 
*  LLF_ECPKI_IsPoint
*
* Inputs:
* @param key [in] - pointer to LibTomCrypt key.
*
* Outputs:
* @returns \b CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - not CE2_OK
*
* \brief \b 
* Description:
*  Verify, if key is a point on the curve.
*
* \b 
* Algorithm:
*  -# Initialize LibTomCrypt primitives;
*  -# Compute t1 = y^2 - x^3 + ax;
*  -# Verify if b == t1;
***************************************************************/
CE2Error_t LLF_ECPKI_IsPoint(ecc_key *key)
{
  CE2Error_t result = CE2_OK;
  void *prime, *b, *t1, *t2, *a;
  int error_code;

  /*************************************/
  /* Initialize LibTomCrypt primitives */
  /*************************************/
  error_code = ltc_init_multi(&prime, &b, &t1, &t2, &a, NULL);
  if (error_code != CRYPT_OK) {
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }
  /* load a, prime, and b */
  error_code = ltc_mp.read_radix(a, ltc_ecc_sets[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(prime, 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(b, ltc_ecc_sets[key->idx].B, 16);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case; 
  }

  /**************************/
  /* Compute y^2 - x^3 + ax */
  /**************************/
  /* compute y^2 */
  error_code = ltc_mp.sqr(key->pubkey.y, t1);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case; 
  }
  /* compute x^3 */
  error_code = ltc_mp.sqr(key->pubkey.x, t2);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case; 
  }
  error_code = ltc_mp.mpdiv(t2, prime, NULL, t2);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case; 
  }
  /* T2 = X * T2  -> x * (x^2) = x^3*/
  error_code = ltc_mp.mul(key->pubkey.x, t2, t2);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case; 
  }
  /* compute y^2 - x^3 -> T1 = y^2 - x^3 */
  error_code = ltc_mp.sub(t1, t2, t1);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case; 
  }
   /* a*x */
  error_code = ltc_mp.mul(a, key->pubkey.x, t2);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case; 
  }
  /* mod (ax) */
  error_code = ltc_mp.mpdiv(t2, prime, NULL, t2);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case; 
  }
  /* T1 = T1 + T2  -> y^2 - x^3 - (ax) */
  error_code = ltc_mp.sub(t1,t2, t1);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case; 
  }
  /* mod T1 */
  error_code = ltc_mp.mpdiv(t1, prime, NULL, t1);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case; 
  }

  while (ltc_mp.compare_d(t1, 0) == LTC_MP_LT) {
    error_code = ltc_mp.add(t1, prime, t1);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case; 
    }
  }
  while (ltc_mp.compare(t1, prime) != LTC_MP_LT) {
    error_code = ltc_mp.sub(t1, prime, t1);
    if (error_code != CRYPT_OK) {
      result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
      goto error_case; 
    }
  }

  /*******************/
  /* compare t1 to b */
  /*******************/
  if (ltc_mp.compare(t1, b) != LTC_MP_EQ)
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;

error_case:
  ltc_deinit_multi(prime, b, t1, t2, a, NULL);
  return result;
}

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

/**
****************************************************************
* Function Name: 
*  LLF_ECPKI_BuildPrivKey
*
* Inputs:
* @param DomainID [in] - The enumerator variable defines current EC domain.
* @param PrivKeyIn_ptr [in] - Pointer to private key data.
* @param PrivKeySizeInBytes [in] - Size of private key data in bytes.
* @param UserPrivKey_ptr [out] - Pointer to the private key structure. 
*                    This structure is used as input to the ECPKI 
*                    cryptographic primitives.
*                    Size of buffer must be not less than:
*                    Order size of the appropriative domain in bytes
*
* Outputs:
* @returns \b CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*
* \brief \b 
* Description:
*  Builds (imports) the user private key structure 
*  from an existing private key so that this structure 
*  can be used by other EC primitives.
*
* \b 
* Algorithm:
*  -# Initialize LibTomCrypt primitives;
*  -# Read ECC private scalar value;
*  -# Make the public key;
*  -# Export LibTomCrypt ECC key to CE2 key format.
***************************************************************/
CE2Error_t LLF_ECPKI_BuildPrivKey(
			  CE2_ECPKI_DomainID_t      DomainID,	      /*in */   
				DxUint8_t			      *PrivKeyIn_ptr,     /*in*/
				DxUint32_t                 PrivKeySizeInBytes,/*in*/
				CE2_ECPKI_UserPrivKey_t  *UserPrivKey_ptr    /*out*/ )
{
  CE2Error_t result = CE2_OK, error;
  ecc_key  key;
  ecc_point *base;
  void *prime, *a, *order;
  int error_code, idx, size;

  /* Initialize ltc_mp structure */
  ltc_mp = ltm_desc;

  UserPrivKey_ptr->DomainID = DomainID;
  UserPrivKey_ptr->valid_tag = ECPKI_USER_KEY_MAX_BUFFER_SIZE;

  /* Initialize LibTomCrypt primitives */
  for (idx = 0; ltc_ecc_sets[idx].DomainId != (DomainID_t)DomainID; ++idx);
  size = ltc_ecc_sets[idx].size;
  if (size > ECC_MAXSIZE || ltc_ecc_sets[idx].size == 0) {
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }
  key.idx = idx;
  key.type = PK_PRIVATE;

  base = NULL;
  error_code = ltc_init_multi(&key.pubkey.x, &key.pubkey.y, 
    &key.pubkey.z, &key.k, &prime, &a, &order, NULL);
  if (error_code != CRYPT_OK) {
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  error_code = 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;
  }
  base = ltc_ecc_new_point();
  if (base == NULL) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  error_code = 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 = mp_read_radix(base->x, (char *)ltc_ecc_sets[key.idx].Gx, 16);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  error_code = mp_read_radix(base->y, (char *)ltc_ecc_sets[key.idx].Gy, 16);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }
  error_code = 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;
  }
  mp_set(base->z, 1);

  /* Read ECC private scalar value */
  error_code = mp_read_unsigned_bin(key.k, 
    PrivKeyIn_ptr, PrivKeySizeInBytes);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* Make the public key */
  error_code = ltc_mp.ecc_kptmul(key.k, base, &key.pubkey, prime, 1,a);
  if (error_code != CRYPT_OK) {
    result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    goto error_case;
  }

  /* Verify is a point on the curve? */
  error = LLF_ECPKI_IsPoint(&key);
  if (error != CE2_OK) {
    ecc_free(&key);
    return error;
  }


  /* Export LibTomCrypt ECC key to CE2 key format */
  error_code = ecc_export(UserPrivKey_ptr->PrivKeyDbBuff, 
    &(UserPrivKey_ptr->valid_tag), PK_PRIVATE, &key);
  if(error_code != CRYPT_OK) {
    ecc_free(&key);
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

error_case:
  ecc_free(&key);
  ltc_ecc_del_point(base);
  mp_clear_multi(prime, order, a, NULL);
  return result;
} /* End of LLF_ECPKI_BuildPrivKey */

/**
****************************************************************
* Function Name: 
*  LLF_ECPKI_BuildPublKey
*
* Inputs:
* @param DomainID [in] - The enumerator variable defines current EC domain.
* @param PublKeyIn_ptr [in] - Pointer to public key data.
* @param PublKeySizeInBytes [in] - Size of public key data in bytes.
* @param UserPublKey_ptr [out] - Pointer to the public key structure. 
*                    This structure is used as input to the ECPKI 
*                    cryptographic primitives.
*                    Size of buffer must be not less than:
*					 2*ModSizeInBytes - for WM DRM,
*					 2*ModSizeInBytes+1 - for other domains.  
*
* Outputs:
* @returns \b CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*
* \brief \b 
* Description:
*  Builds (imports) the user public key structure 
*  from an existing public key so that this structure 
*  can be used by other EC primitives.
*
* \b 
* Algorithm:
*  -# Determinate key size, initialize key structures;
*  -# Read ECC public key point;
*  -# Set other LibTomCrypt ECC key parameters;
*  -# Export LibTomCrypt ECC key to CE2 key format.
***************************************************************/
CE2Error_t LLF_ECPKI_BuildPublKey(
			  CE2_ECPKI_DomainID_t      DomainID,	        /*in*/				
				DxUint8_t			      *PublKeyIn_ptr,       /*in*/
				DxUint32_t                 PublKeySizeInBytes,  /*in*/
				CE2_ECPKI_UserPublKey_t  *UserPublKey_ptr      /*out*/ )
{
  ecc_key  key;
  int error_code;
  CE2Error_t error;
  DxUint_t modulusSize;
  DxUint32_t pos = 0;

  /* Initialize ltc_mp structure */
  ltc_mp = ltm_desc;

  /* Determinate modulus size, initialize key structures */
  modulusSize = LLF_ECPKI_DomainIdToModulusSize(DomainID);

  UserPublKey_ptr->DomainID = DomainID;
  UserPublKey_ptr->valid_tag = ECPKI_USER_KEY_MAX_BUFFER_SIZE;

  if (UserPublKey_ptr->DomainID != CE2_ECPKI_DomainID_WMDRM10) {
    if((PublKeyIn_ptr[pos]&0x02) != 0)
      return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
    pos++;
  } 

  if (modulusSize*2 + pos != PublKeySizeInBytes)
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;

  error_code = ltc_init_multi(&key.pubkey.x, &key.pubkey.y, 
    &key.pubkey.z, &key.k, NULL);
  if (error_code != CRYPT_OK) {
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  /* Read ECC public key point */
  error_code = mp_read_unsigned_bin(key.pubkey.x, 
    PublKeyIn_ptr + pos, modulusSize);
  if (error_code != CRYPT_OK) {
    ecc_free(&key);
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }
  pos += modulusSize;

  error_code = mp_read_unsigned_bin(key.pubkey.y, 
    PublKeyIn_ptr + pos, modulusSize);
  if (error_code != CRYPT_OK) {
    ecc_free(&key);
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  /* Set other LibTomCrypt ECC key parameters */
  key.type = PK_PUBLIC;

  for (key.idx = 0; 
    ltc_ecc_sets[key.idx].DomainId != (DomainID_t) DomainID; ++key.idx);

  if (ltc_ecc_sets[key.idx].size == 0) {
    ecc_free(&key);
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  mp_set(key.pubkey.z, 1);

  /* Verify is a point on the curve? */
  error = LLF_ECPKI_IsPoint(&key);
  if (error != CE2_OK) {
    ecc_free(&key);
    return error;
  }

  /* Export LibTomCrypt ECC key to CE2 key format */
  error_code = ecc_export(UserPublKey_ptr->PublKeyDbBuff, 
    &(UserPublKey_ptr->valid_tag), PK_PUBLIC, &key);
  if(error_code != CRYPT_OK) {
    ecc_free(&key);
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  ecc_free(&key);
  return CE2_OK;
} /* End of LLF_ECPKI_BuildPublKey */

/**
****************************************************************
* Function Name: 
*  LLF_ECPKI_ExportPublKey
*
* Inputs:
* @param UserPublKey_ptr [in] - A pointer to the public key structure 
*                    (in little-endian form). 
* @param Compression [in] - An enumerator parameter: defines point compression.
* @param ExternPublKey_ptr [out] - A pointer to the exported public key structure 
*                      in big-endian and point compression as defined 
*                      by input parameter. 
*                      Size of buffer must be not less than:
*					   2*ModSizeInBytes - for WM DRM,
*					   2*ModSizeInBytes+1 - for other domains.   
* @param PublKeySizeInBytes [in/out] - A pointer to a variable for input size of 
*                       user passed buffer for public key and output 
*                       the size of converted public key in bytes.
*
* Outputs:
* @returns \b CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*
* \brief \b 
* Description:
*  Converts an existing public key in little-endian form to  
*  big-endian export form and initializes an appropriate structure 
*  of type CE2_ECPKI_PublKeyExtern_t.
*
* \b 
* Algorithm:
*  -# Import CE2 user public key to LibTomCrypt key structure;
*  -# Write ECC public key point to temporary buffer 
*    (in big-endian format);
*  -# Make export public key (compress Y coordinate if need).
***************************************************************/
CE2Error_t LLF_ECPKI_ExportPublKey(
   			CE2_ECPKI_UserPublKey_t *UserPublKey_ptr,     /*in*/
				CE2_ECPKI_PointCompression_t  Compression,         /*in*/
				DxUint8_t	*ExternPublKey_ptr,   /*in*/
				DxUint32_t *PublKeySizeInBytes   /*in/out*/ )
{ 
  ecc_key  key;
  int error_code;
  DxUint32_t xLen = 256, yLen = 256, pos = 0, bufferSize, modulusSize, frontZero;
  DxUint8_t xBuf[256], yBuf[256];

  /* Initialize ltc_mp structure */
  ltc_mp = ltm_desc;

  modulusSize = LLF_ECPKI_DomainIdToModulusSize(UserPublKey_ptr->DomainID);
  
  /* Import CE2 user public key to LibTomCrypt key structure */
  error_code =  ecc_import(UserPublKey_ptr->PublKeyDbBuff, 
    UserPublKey_ptr->valid_tag, (DomainID_t)UserPublKey_ptr->DomainID, &key);
  if (error_code != CRYPT_OK) {
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  /* Write ECC public key point to temporary buffer (in big-endian format) */
  error_code = mp_to_unsigned_bin_n(key.pubkey.x, xBuf, &xLen);
  if(error_code != CRYPT_OK) {
    ecc_free(&key);
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  error_code = mp_to_unsigned_bin_n(key.pubkey.y, yBuf, &yLen);
  if(error_code != CRYPT_OK) {
    ecc_free(&key);
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  bufferSize = modulusSize*2;
  if (UserPublKey_ptr->DomainID != CE2_ECPKI_DomainID_WMDRM10)
    bufferSize++;
  if(bufferSize > *PublKeySizeInBytes) {
    *PublKeySizeInBytes = bufferSize;
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }
  *PublKeySizeInBytes = bufferSize;

  /* Make export public key (compress Y coordinate if need) */
  if (UserPublKey_ptr->DomainID != CE2_ECPKI_DomainID_WMDRM10) {
    if (Compression == CE2_EC_PointUncompressed) {
      ExternPublKey_ptr[pos] = 0x04;
    } else {
      ExternPublKey_ptr[pos] = 0x01&(yBuf[yLen - 1]);//There is LSbit of Y.
      if (Compression == CE2_EC_PointHybrid)
        ExternPublKey_ptr[pos] |= 0x06;
      else 
        ExternPublKey_ptr[pos] |= 0x04;
    }
    pos++;
    
    for (frontZero = 0; xLen + frontZero < modulusSize; frontZero++) {
      ExternPublKey_ptr[pos] = 0x00;
      pos++;
    }
    memcpy(ExternPublKey_ptr + pos, xBuf, xLen);
    pos += xLen;
    if (Compression != CE2_EC_PointCompressed) {
      for (frontZero = 0; yLen + frontZero < modulusSize; frontZero++) {
        ExternPublKey_ptr[pos] = 0x00;
        pos++;
      }
      memcpy(ExternPublKey_ptr + pos, yBuf, yLen);
    }
  } else {
    for (frontZero = 0; xLen + frontZero < modulusSize; frontZero++) {
      ExternPublKey_ptr[pos] = 0x00;
      pos++;
    }
    memcpy(ExternPublKey_ptr + pos, xBuf, xLen);
    pos += xLen;
    for (frontZero = 0; yLen + frontZero < modulusSize; frontZero++) {
      ExternPublKey_ptr[pos] = 0x00;
      pos++;
    }
    memcpy(ExternPublKey_ptr + pos, yBuf, yLen);
  }

  ecc_free(&key);
  return CE2_OK;
} /* LLF_ECPKI_ExportPublKey */

/**
****************************************************************
* Function Name: 
*  LLF_ECPKI_ExportPrivKey
*
* Inputs:
* @param UserPrivKey_ptr [in] - A pointer to the private key structure 
*                    (in little-endian form). 
* @param ExternPrivKey_ptr [out] - A pointer to the exported private key 
*                      in big-endian. 
*						Size of buffer must be not less than:
*						Order size of the appropriative domain in bytes
* @param ExternPrivKeySize_ptr [in\out]- A pointer to a variable for 
*                       input size of user passed buffer for private key 
*                       and output the size of converted private key in bytes.
*
* Outputs:
* @returns \b CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*   - CE2_ECPKI_EXPORT_PRIVATE_KEY_INVALID_USER_PRIV_KEY_PTR_ERROR
*   - CE2_ECPKI_EXPORT_PRIVATE_KEY_INVALID_EXTERN_PRIVATE_KEY_PTR_ERROR
*   - CE2_ECPKI_EXPORT_PRIVATE_KEY_INVALID_EXTERN_PRIVATE_KEY_SIZE_PTR_ERROR
*
* \brief \b 
* Description:
*  Converts an existing private key in little-endian form to  
*  big-endian export form.
*
* \b 
* Algorithm:
*  -# Import CE2 user private key to LibTomCrypt key structure;
*  -# Write ECC private scalar value to output buffer 
*    (in big-endian format);
***************************************************************/
CE2Error_t LLF_ECPKI_ExportPrivKey(
            CE2_ECPKI_UserPrivKey_t       *UserPrivKey_ptr,     /*in*/
            DxUint8_t			                *ExternPrivKey_ptr,   /*out*/
            DxUint32_t                    *ExternPrivKeySize_ptr   /*in/out*/ )
{
  ecc_key  key;
  int error_code;
  DxUint32_t kLen = 256, pos = 0;
  DxUint8_t kBuf[256];

  /* Initialize ltc_mp structure */
  ltc_mp = ltm_desc;

  /* Import CE2 user private key to LibTomCrypt key structure */
  error_code =  ecc_import(UserPrivKey_ptr->PrivKeyDbBuff, 
    UserPrivKey_ptr->valid_tag, (DomainID_t)UserPrivKey_ptr->DomainID, &key);
  if (error_code != CRYPT_OK) {
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  /* Write ECC private scalar value temporary buffer (in big-endian format) */
  error_code = mp_to_unsigned_bin_n(key.k, kBuf, &kLen);
  if(error_code != CRYPT_OK) {
    ecc_free(&key);
    return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
  }

  if(kLen > *ExternPrivKeySize_ptr) {
    ecc_free(&key);
    *ExternPrivKeySize_ptr = kLen;
    return LLF_ECPKI_EXPORT_PRIVATE_KEY_INVALID_EXTERN_PRIVATE_KEY_SIZE_ERROR;
  }
  *ExternPrivKeySize_ptr = kLen;
  memcpy(ExternPrivKey_ptr + pos, kBuf, kLen);

  ecc_free(&key);
  return CE2_OK;
} /* End of LLF_ECPKI_ExportPrivateKey*/
