/*
* Creation date : Fri Mar 09 17:24:34 2007
* Last modified : %modify_time%
*/
/** @file
* \brief This file contains implementation of 
* LLF_RSA_SCHEMES functions.
*
* \version LLF_RSA_SCHEMES.c#1:csrc:1
* \author Pavel Sasunkevich
* \remarks Copyright (C) 2007 by Discretix Technologies Ltd.
* All Rights reserved
*/

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

#include "LLF_RSA_SCHEMES.h"
#include "LLF_RSA_Common.h"

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

/* The function changes the hash_descriptor values of the TomCrypt */
/* hashFunc - hash mode                                            */
/* pHashIndex - return the hash index in the hash_descriptor       */
/* Function returns error code									   */	
CE2Error_t RegisterOldHashDx(int hashFunc, int *pHashIndex)							 
{
  int error_code = CE2_OK;

  /* Register hash */
  switch (hashFunc)
  {
  case CE2_RSA_After_HASH_NOT_KNOWN_mode: /* For PKCS1 v1.5 */
    /* If PKCS1_ver is CE2_PKCS1_VER15, then
    the value CE2_RSA_After_HASH_NOT_KNOWN_mode will allow
    the signature data to determine the hash function to be used.*/
    /* this operations are a little lower */
    break;
  case CE2_RSA_HASH_SHA1_mode:
  case CE2_RSA_After_SHA1_mode: /* For PKCS1 v1.5 */
    if (register_hash(&sha1_descDx) == -1)
    {
      error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
      goto exit_label;
    }
    *pHashIndex = find_hash("sha1Dx");
    break;
  case CE2_RSA_HASH_SHA224_mode:
  case CE2_RSA_After_SHA224_mode: /* For PKCS1 v1.5 */
    if (register_hash(&sha224_descDx) == -1)
    {
      error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
      goto exit_label;
    }
    *pHashIndex = find_hash("sha224Dx");
    break;
  case CE2_RSA_HASH_SHA256_mode:
  case CE2_RSA_After_SHA256_mode: /* For PKCS1 v1.5 */
    if (register_hash(&sha256_descDx) == -1)
    {
      error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
      goto exit_label;
    }
    *pHashIndex = find_hash("sha256Dx");
    break;
  case CE2_RSA_HASH_SHA384_mode:
  case CE2_RSA_After_SHA384_mode: /* For PKCS1 v1.5 */
    if (register_hash(&sha384_descDx) == -1)
    {
      error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
      goto exit_label;
    }
    *pHashIndex = find_hash("sha384Dx");
    break;
  case CE2_RSA_HASH_SHA512_mode:
  case CE2_RSA_After_SHA512_mode: /* For PKCS1 v1.5 */
    if (register_hash(&sha512_descDx) == -1)
    {
      error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
      goto exit_label;
    }
    *pHashIndex = find_hash("sha512Dx");
    break;
  case CE2_RSA_HASH_MD5_mode:
  case CE2_RSA_After_MD5_mode:
    /*if (PKCS1_ver == CE2_PKCS1_VER21)
    {
    error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
    break;
    }*/
    if (register_hash(&md5_descDx) == -1)
    {
        error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
        goto exit_label;
    }
    *pHashIndex = find_hash("md5Dx");
    break;
    /*	for v2.1 MD5 is not supported, since
    according to the PKCS#1 ver2.1 it is not recommended*/
  default:
    error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
  }

exit_label:
  return error_code;
}

CE2Error_t LLF_RSA_HASH(CE2_RSA_HASH_OpMode_t HashMode,
                        DxUint8_t             *DataIn_ptr,
                        DxUint32_t            DataInSize,
                        DxUint8_t             *DataOut_ptr,
                        DxUint32_t            *DataOutSize_ptr)
{
  int hash_index = -1, make_hash = 1;
  int error_code = CRYPT_OK;
  DxUint8_t empty_buffer;

  switch(HashMode)
  {
  case CE2_RSA_HASH_MD5_mode:
  case CE2_RSA_HASH_SHA1_mode:
  case CE2_RSA_HASH_SHA224_mode:
  case CE2_RSA_HASH_SHA256_mode:
  case CE2_RSA_HASH_SHA384_mode:
  case CE2_RSA_HASH_SHA512_mode:
    make_hash = 1;
    break;
  case CE2_RSA_After_MD5_mode:
    *DataOutSize_ptr = CE2_HASH_MD5_DIGEST_SIZE_IN_BYTES;
    make_hash = 0;
    break;
  case CE2_RSA_After_SHA1_mode:
    *DataOutSize_ptr = CE2_HASH_SHA1_DIGEST_SIZE_IN_BYTES;
    make_hash = 0;
    break;
  case CE2_RSA_After_SHA224_mode:
    *DataOutSize_ptr = CE2_HASH_SHA224_DIGEST_SIZE_IN_BYTES;
    make_hash = 0;
    break;
  case CE2_RSA_After_SHA256_mode:
    *DataOutSize_ptr = CE2_HASH_SHA256_DIGEST_SIZE_IN_BYTES;
    make_hash = 0;
    break;
  case CE2_RSA_After_SHA384_mode:
    *DataOutSize_ptr = CE2_HASH_SHA384_DIGEST_SIZE_IN_BYTES;
    make_hash = 0;
    break;
  case CE2_RSA_After_SHA512_mode:
    *DataOutSize_ptr = CE2_HASH_SHA512_DIGEST_SIZE_IN_BYTES;
    make_hash = 0;
    break;
  default:
    return CE2_LLF_HASH_MODULE_ERROR_BASE;
  }

  if (make_hash) {
    RegisterOldHashDx(HashMode, &hash_index);	
    if (hash_index == -1)
      return CE2_LLF_HASH_MODULE_ERROR_BASE;

    /* Case of input message is absent */
    if (DataIn_ptr == NULL) {
      DataInSize = 0;
      DataIn_ptr = &empty_buffer;
    }

    /* Perform hash operation. */
    error_code = hash_memory(hash_index, DataIn_ptr, DataInSize,
      DataOut_ptr, DataOutSize_ptr);
    if (error_code != CRYPT_OK)
      return CE2_LLF_HASH_MODULE_ERROR_BASE;
  } else {
    if (DataIn_ptr != NULL) {
      memset(DataOut_ptr, 0, *DataOutSize_ptr);
      memcpy(DataOut_ptr, DataIn_ptr, min(*DataOutSize_ptr, DataInSize));
    } else {
      *DataOutSize_ptr = 0;
    }
  }

  return CE2_OK;
} /* End of LLF_RSA_HASH */

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

/**
****************************************************************
* Function Name: 
*  LLF_DX_RSA_SCHEMES_Encrypt
*
* Inputs:
*  @param UserPubKey_ptr [in] - A pointer to the public key data structure of the User;
*  @param hashFunc       [in] - The hash function to be used;
*  @param L              [in] - The label input;
*  @param Llen           [in] - The label length;
*  @param MGF            [in] - The mask generation function. PKCS#1 v2.1 defines MGF1, 
*         so the only value allowed here is CE2_PKCS1_MGF1;
*  @param DataIn_ptr     [in] - A pointer to the data to encrypt;
*  @param DataInSize       [in] - The size, in bytes, of the data to encrypt;
*  @param Output_ptr     [out] - A pointer to the encrypted data;
*  @param PKCS1_ver      [in] - Ver 1.5 or 2.1, according to the functionality required.
*
* Outputs: @returns \b
*  CE2Error_t  
*  - CE2_OK - On success
*  - Otherwise - error code:
*      CE2_BAD_PARAM,
*      CE2_OUT_OF_RANGE.
*
* \brief \b
* Description:
*  This function implements the Encrypt algorithm as defined in
*  PKCS#1 v2.1 and PKCS#1 v1.5.
*  The actual macro that will be used by the user is:
*   CE2_RSA_OAEP_Encrypt 		- for v2.1;
*   CE2_RSA_PKCS1v15_Encrypt	- for v1.5.
*
* \b
* Algorithm:
*  -# Register prng and hash;
*  -# Encode packet depending on PKCS ver;
*  -# Compute an RSA modular exponentiation depending on PKCS ver.
***************************************************************/
CE2Error_t LLF_DX_RSA_SCHEMES_Encrypt(
                        CE2_RSAUserPubKey_t *UserPubKey_ptr,/* in */
                        CE2_RSA_HASH_OpMode_t hashFunc,	/* in */
                        DxUint8_t      *L,				/* in */
                        DxUint16_t      Llen,			/* in */
                        CE2_PKCS1_MGF_t MGF,			/* in */
                        DxUint8_t       *DataIn_ptr,	/* in */
                        DxUint16_t      DataInSize,		/* in */
                        DxUint8_t       *Output_ptr,	/* out */
                        CE2_PKCS1_version PKCS1_ver)	/* in */
{
	CE2RSAPubKey_t * pubKey = (CE2RSAPubKey_t *)UserPubKey_ptr->PublicKeyDbBuff;
	prng_state yarrow_prng;
	//unsigned long e = 0;
	unsigned long outlen = 0;
	unsigned long x;
	int error_code = -1;
	int hash_idx = 0;
	int prng_idx = 0;
	rsa_key key = { 0 };
	CE2Error_t error;

	/* register prng */
	if (register_prng(&yarrow_desc) == -1)
	{
		error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
		goto exit_label;
	}

	/* register a math library (in this case TomFastMath) */
	ltc_mp = ltm_desc;

	/* Register hash: only SHA1 supported */
	/* The other hash functions recommended 
	by PKCS#1 v2.1 are SHA-256/284/512. */
	switch (hashFunc)
	{
	case CE2_RSA_HASH_NO_HASH_mode:
		break;
	case CE2_RSA_HASH_MD5_mode:
		if (register_hash(&md5_desc) == -1)
		{
			error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
			goto exit_label;
		}
		hash_idx = find_hash("md5");
		break;
	case CE2_RSA_HASH_SHA1_mode:
		if (register_hash(&sha1_desc) == -1)
		{
			error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
			goto exit_label;
		}
		hash_idx = find_hash("sha1");
		break;
	case CE2_RSA_HASH_SHA224_mode:
		if (register_hash(&sha224_desc) == -1)
		{
			error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
			goto exit_label;
		}
		hash_idx = find_hash("sha224");
		break;
  case CE2_RSA_HASH_SHA256_mode:
		if (register_hash(&sha256_desc) == -1)
		{
			error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
			goto exit_label;
		}
		hash_idx = find_hash("sha256");
		break;
 case CE2_RSA_HASH_SHA384_mode:
		if (register_hash(&sha384_desc) == -1)
		{
			error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
			goto exit_label;
		}
		hash_idx = find_hash("sha384");
		break;
  case CE2_RSA_HASH_SHA512_mode:
		if (register_hash(&sha512_desc) == -1)
		{
			error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
			goto exit_label;
		}
		hash_idx = find_hash("sha512");
		break;
	default:
		error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
		goto exit_label;
	}

	if (PKCS1_ver == CE2_PKCS1_VER21)
	{
		if (MGF != CE2_PKCS1_MGF1)
		{
			error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
			goto exit_label;
		}

		/* DataInSize Must be <= (modulus_size - 2*Hash_output_length - 2). */
		error_code = (DataInSize >
			(BITS2BYTES(pubKey->nSizeInBits) - 2*hash_descriptor[hash_idx].hashsize-2));

		if (error_code != CRYPT_OK)
		{
			error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
			goto exit_label;
		}
	}

	prng_idx = find_prng("yarrow");

	error_code = rng_make_prng(128, prng_idx, &yarrow_prng, 0);
	if (error_code != CRYPT_OK)
	{
		error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
		goto exit_label;
	}

	if (hash_idx == -1 || prng_idx == -1)
	{
		error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
		goto exit_label;
	}

	error_code = LLF_RSA_UserPubKey_TO_rsakey(UserPubKey_ptr, &key);
	if (error_code != CRYPT_OK)
	{
		error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
		goto exit_label;
	}

	/* take outlen like modulus size */
	outlen = BITS2BYTES(pubKey->nSizeInBits);

	/* master encrypt code starts */

	x = outlen;
	
	if (PKCS1_ver == CE2_PKCS1_VER15)
	{
		/* pad it */
		error = LLF_RSA_PKCS1_V15_Encode(DataIn_ptr, DataInSize,
			pubKey->nSizeInBits, Output_ptr, &x); 
		if (error != CE2_OK) {
			error_code = error;
			goto exit_label;
		}
		
		//error_code = pkcs_1_v15_es_encode(
		//	DataIn_ptr, DataInSize, pubKey->nSizeInBits,
		//	&yarrow_prng, prng_idx, Output_ptr, &x);
		//if (error_code != CRYPT_OK)
		//{
		//	error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
		//	goto exit_label;
		//}

		/* encrypt it */
		error_code = rsa_exptmod(Output_ptr, x, Output_ptr, &outlen, PK_PUBLIC, &key);
		if (error_code != CRYPT_OK)
		{
			error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
			goto exit_label;
		}
	}
	else
	if (PKCS1_ver == CE2_PKCS1_VER21)
	{
		/* OAEP pad the key */
		// need to recode: MGF inside
		/* if MGF1 is single call pkcs_1_oaep_encode, othervise recode */
		/*error_code = pkcs_1_oaep_encode(
			DataIn_ptr, DataInSize, L, 
			Llen, pubKey->nSizeInBits, &yarrow_prng, prng_idx, hash_idx,
			Output_ptr, &x);*/
		error_code = LLF_RSA_pkcs_1_oaep_encode(
			DataIn_ptr, DataInSize, L, 
			Llen, pubKey->nSizeInBits, &yarrow_prng, prng_idx, hash_idx,
			Output_ptr, &x);
		if (error_code != CRYPT_OK)
		{
			error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
			goto exit_label;
		}

		/* rsa exptmod the OAEP pad */
		error_code = ltc_mp.rsa_me(Output_ptr, x, Output_ptr, &outlen, PK_PUBLIC, &key);
		if (error_code != CRYPT_OK)
		{
			error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
			goto exit_label;
		}
	}

	/* end of master code  */

exit_label:
	rsa_free(&key);
	return error_code;
} /* End of LLF_DX_RSA_SCHEMES_Encrypt */

/**
****************************************************************
* Function Name: 
*  LLF_DX_RSA_SCHEMES_Decrypt
*
* Inputs:
*  @param UserPrivKey_ptr [in] - A pointer to the private key data structure;
*  @param hashFunc        [in] - The hash function to be used;
*  @param L               [in] - The label input;
*  @param Llen            [in] - The label length;
*  @param MGF             [in] - The mask generation function. PKCS#1 v2.1 defines MGF1, 
          so the only value allowed here is CE2_PKCS1_MGF1.
*  @param Data_ptr        [in] - A pointer to the data to decrypt;
*  @param DataSize        [in] - The size, in bytes, of the data to decrypt;
*  @param Output_ptr      [out] - Pointer to the decrypted data;
*  @param OutputSize_ptr  [in/out] - The size of the Output_buffer ptr  [bytes];
*  @param PKCS1_ver       [in] - Ver 1.5 or 2.1, according to the functionality required.
*
* Outputs: @returns \b
*  CE2Error_t  
*  - CE2_OK - On success
*  - Otherwise - error code.
*      CE2_BAD_PARAM,
*      CE2_OUT_OF_RANGE.
*
* \brief \b
* Description:
*  This function implements the Decrypt algorithm as defined
*  in PKCS#1 v2.1 and PKCS#1 v1.5.
*   The actual macro that will be used by the user is:
*   CE2_RSA_OAEP_Decrypt		- for v2.1;
*   CE2_RSA_PKCS1v15_Decrypt	- for v1.5.
*
* \b
* Algorithm:
*  -# Register prng and hash;
*  -# Compute an RSA modular exponentiation depending on PKCS ver;
*  -# Decode packet depending on PKCS ver.
***************************************************************/
CE2Error_t LLF_DX_RSA_SCHEMES_Decrypt(
                        CE2_RSAUserPrivKey_t *UserPrivKey_ptr,/* in */
                        CE2_RSA_HASH_OpMode_t hashFunc,	/* in */
                        DxUint8_t       *L,				/* in */
                        DxUint16_t      Llen,			/* in */
                        CE2_PKCS1_MGF_t MGF,			/* in */
                        DxUint8_t       *Data_ptr,		/* in */
                        DxUint16_t      DataSize,		/* in */
                        DxUint8_t       *Output_ptr,	/* out */
                        DxUint16_t      *OutputSize_ptr,/* in, out */
                        CE2_PKCS1_version PKCS1_ver)	/* in */
{
	CE2RSAPrivKey_t * privKey = (CE2RSAPrivKey_t *)UserPrivKey_ptr->PrivateKeyDbBuff;

	//unsigned long e = 0;
	unsigned long outlen = *OutputSize_ptr;
	unsigned long x;
	int stat = 0;		  /* default to invalid */
	unsigned char *decoded = 0;
	//unsigned int ModulusSizeinBytes = BITS2BYTES(privKey->nSizeInBits);
	//int result = 0;
	int error_code;
	int hash_idx = 0;
	rsa_key key = { 0 };
	void * data = 0;
	CE2Error_t error;


	/* register a math library (in this case TomFastMath) */
	ltc_mp = ltm_desc;

	/* Register hash: only SHA1 supported */
	/* The other hash functions recommended 
	by PKCS#1 v2.1 are SHA-256/284/512. */
	if (PKCS1_ver == CE2_PKCS1_VER21)
	{
		switch (hashFunc)
		{
		case CE2_RSA_HASH_NO_HASH_mode:
			break;
		case CE2_RSA_HASH_MD5_mode:
			if (register_hash(&md5_desc) == -1)
			{
				error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
				goto exit_label;
			}
			hash_idx = find_hash("md5");
			break;
		case CE2_RSA_HASH_SHA1_mode:
			if (register_hash(&sha1_desc) == -1)
			{
				error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
				goto exit_label;
			}
			hash_idx = find_hash("sha1");
			break;
		case CE2_RSA_HASH_SHA224_mode:
			if (register_hash(&sha224_desc) == -1)
			{
				error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
				goto exit_label;
			}
			hash_idx = find_hash("sha224");
			break;
		case CE2_RSA_HASH_SHA256_mode:
			if (register_hash(&sha256_desc) == -1)
			{
				error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
				goto exit_label;
			}
			hash_idx = find_hash("sha256");
			break;
		case CE2_RSA_HASH_SHA384_mode:
			if (register_hash(&sha384_desc) == -1)
			{
				error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
				goto exit_label;
			}
			hash_idx = find_hash("sha384");
			break;
		case CE2_RSA_HASH_SHA512_mode:
			if (register_hash(&sha512_desc) == -1)
			{
				error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
				goto exit_label;
			}
			hash_idx = find_hash("sha512");
			break;
		default:
			error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
			goto exit_label;
		}

		if (MGF != CE2_PKCS1_MGF1)
		{
			error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
			goto exit_label;
		}

		if (hash_idx == -1)
		{
			error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
			goto exit_label;
		}
	}

	error_code = LLF_RSA_UserPrivKey_TO_rsakey(UserPrivKey_ptr, &key);
	if (error_code != CRYPT_OK)
	{
		error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
		goto exit_label;
	}

	/* take outlen like modulus size */
	outlen = *OutputSize_ptr;

	/* master decrypt code starts */

	/* allocate ram for decoded packet */
	decoded = XMALLOC(DataSize);
	if (decoded == NULL)
	{
		error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
		goto exit_label;
	}

  x = DataSize;
	error_code = LLF_RSA_EXPTMOD(Data_ptr, DataSize, decoded, &x, &key, PKCS1_ver);
	if (error_code != CRYPT_OK)
	{
		error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
		goto exit_label;
	}

	/* rsa decode the packet */
	if (PKCS1_ver == CE2_PKCS1_VER15)
	{
		/* PKCS #1 v1.5 depad */
		error = LLF_RSA_PKCS1_V15_Decode(decoded, x, privKey->nSizeInBits,
			Output_ptr, &outlen);
		if (error != CE2_OK) {
			error_code = error;
			goto exit_label;
		}

		//error_code = pkcs_1_v15_es_decode(
		//	decoded, x, privKey->nSizeInBits,
		//	Output_ptr, outlen, &stat);
		//if (error_code != CRYPT_OK)
		//{
		//	error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
		//	goto exit_label;
		//}
		/* end test */
	}
	else
	if (PKCS1_ver == CE2_PKCS1_VER21)
	{
		/* now OAEP decode the packet */
		/* if MGF1 is single call pkcs_1_oaep_decode, othervise recode */
		error_code = pkcs_1_oaep_decode(
			decoded, x, L, (unsigned long)Llen,
			privKey->nSizeInBits, hash_idx, 
			Output_ptr, &outlen, &stat);
		if (error_code != CRYPT_OK)
		{
			error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
			goto exit_label;
		}
	}
	*OutputSize_ptr = (DxUint16_t)outlen;

	/* Check the result of our decryption. */
	/*
	error_code = (stat == 1) ? (CE2_OK) : (CE2_ERROR_BASE);
	if (error_code != CRYPT_OK)
	{
		error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
		goto exit_label;
	}
	*/
	/* end of master decrypt code */

exit_label:
	XFREE(decoded);
	rsa_free(&key);
	ltc_init_multi(data, 0);
	return error_code;
} /* End of LLF_DX_RSA_SCHEMES_Decrypt */

/**
****************************************************************
* Function Name: 
*  LLF_DX_RSA_Sign
*
* Inputs:
*  @param UserPrivKey_ptr [in] - A pointer to the private key data
*         structure of the user;
*  @param hashFunc        [in] - The hash function to be used;
*  @param MGF             [in] - The mask generation function;
*  @param SaltLen         [in] - The Length of the Salt buffer;
*  @param DataIn_ptr      [in] - A pointer to the data to sign;
*  @param DataInSize      [in] - The size, in bytes, of the data to sign;
*  @param Output_ptr      [out] - A pointer to the signature;
*  @param OutputSize_ptr  [in/out] - A pointer to the Signature Size value;
*  @param PKCS1_ver       [in] - Ver 1.5 or 2.1, according to the functionality required.
*
* Outputs: @returns \b
*  CE2Error_t  
*  - CE2_OK - On success
*  - Otherwise - error code.
*      CE2_RSA_INVALID_PRIV_KEY_STRUCT_POINTER_ERROR,
*      CE2_RSA_PRIV_KEY_VALIDATION_TAG_ERROR,
*      CE2_RSA_HASH_ILLEGAL_OPERATION_MODE_ERROR,
*      CE2_RSA_MGF_ILLEGAL_ARG_ERROR,
*      CE2_RSA_PKCS1_VER_ARG_ERROR,
*      CE2_RSA_INVALID_MESSAGE_DATA_SIZE,
*      CE2_RSA_INVALID_OUTPUT_POINTER_ERROR,
*      CE2_RSA_INVALID_SIGNATURE_BUFFER_SIZE.
*
* \brief \b
* Description:
*  RSA_Sign implements the Signing algorithm as defined
*  in PKCS#1 v1.5 or v2.1, using a single function
*  The actual macros that will be used by the user are:
*   CE2_RSA_PKCS1v15_Sign		- for v1.5;
*   CE2_RSA_PSS_Sign			- for v2.1.
*								- for v2.1 MD5 is not supported, since 
*                 according to the PKCS#1 ver2.1 it is not recommended.
*
* \b
* Algorithm:
*  -# Register prng and hash;
*  -# Calculate message hash;
*  -# Encode packet depending on PKCS ver - get sign of msg hash.
*  -# Compute an RSA modular exponentiation depending on PKCS ver;
***************************************************************/
CE2Error_t LLF_DX_RSA_Sign(
                        CE2_RSAUserPrivKey_t *UserPrivKey_ptr,/* in */
                        CE2_RSA_HASH_OpMode_t hashFunc,	/* in */
                        CE2_PKCS1_MGF_t MGF,			/* in */
                        DxUint16_t    SaltLen,			/* in */
                        DxUint8_t     *DataIn_ptr,		/* in */
                        DxUint32_t     DataInSize,		/* in */
                        DxUint8_t     *Output_ptr,		/* out */
                        DxUint16_t    *OutputSize_ptr,	/* in, out */
                        CE2_PKCS1_version PKCS1_ver)	/* in */
{
  CE2RSAPrivKey_t * privKey = (CE2RSAPrivKey_t *)UserPrivKey_ptr->PrivateKeyDbBuff;

  prng_state yarrow_prng;
  //unsigned long e = 0;
  unsigned long outlen = 0;
  unsigned long x;
  int error_code;
  int hash_idx = 0;
  int prng_idx = 0;
  hash_state hs = { 0 };
  DxUint8_t hash[MAXBLOCKSIZE] = { 0 };
  DxUint32_t hashSize = MAXBLOCKSIZE;
  rsa_key key = { 0 };

  /* register prng */
  if (register_prng(&yarrow_desc) == -1)
  {
    error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
    goto exit_label;
  }

  /* register a math library (in this case TomFastMath) */
  ltc_mp = ltm_desc;

  /* Anna 6/8/2007 fix bug of TomCrypt that has wrong DER encoding T */
  /* of the DigestInfo values */
  /* for back word computability */
  /* The function changes the hash_descriptor values of the TomCrypt */
  error_code = RegisterOldHashDx(hashFunc, &hash_idx);
  if (error_code != CE2_OK)
    goto exit_label;

  if (PKCS1_ver == CE2_PKCS1_VER21)
  {
    if (MGF != CE2_PKCS1_MGF1)
    {
      error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
      goto exit_label;
    }
  }

  prng_idx = find_prng("yarrow");

  error_code = rng_make_prng(128, prng_idx, &yarrow_prng, 0);
  if (error_code != CRYPT_OK)
  {
    error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
    goto exit_label;
  }

  if (hash_idx == -1 || prng_idx == -1)
  {
    error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
    goto exit_label;
  }

  error_code = LLF_RSA_UserPrivKey_TO_rsakey(UserPrivKey_ptr, &key);
  if (error_code != CRYPT_OK)
  {
    error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
    goto exit_label;
  }

  /* take outlen like modulus size */
  outlen = BITS2BYTES(privKey->nSizeInBits);

  /* master sign code starts */

  /* OAEP pad the key */
  x = outlen;
  error_code = LLF_RSA_HASH(hashFunc, DataIn_ptr, DataInSize,
    hash, &hashSize);
  if (error_code != CRYPT_OK) {
    error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
    goto exit_label;
  }

  if (PKCS1_ver == CE2_PKCS1_VER15)
  {
    /* pad it */
    error_code = pkcs_1_v15_sa_encode(
      hash, hash_descriptor[hash_idx].hashsize, hash_idx,
      privKey->nSizeInBits, Output_ptr, &x);
    if (error_code != CRYPT_OK)
    {
      error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
      goto exit_label;
    }
  } else if (PKCS1_ver == CE2_PKCS1_VER21) {
    // need to recode: MGF inside
    /* if MGF1 is single call pkcs_1_pss_encode, othervise recode */
    /* PSS pad the key */
    error_code = pkcs_1_pss_encode(
      hash, hash_descriptor[hash_idx].hashsize,
      SaltLen, &yarrow_prng, prng_idx, hash_idx,
      privKey->nSizeInBits, Output_ptr, &x);
    if (error_code != CRYPT_OK)
    {
      error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
      goto exit_label;
    }
  }

  /* RSA encode it */
  error_code = LLF_RSA_EXPTMOD(Output_ptr, x, Output_ptr, &outlen, &key, PKCS1_ver);
  if (error_code != CRYPT_OK)
  {
    error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
    goto exit_label;
  }

  *OutputSize_ptr = (DxUint16_t)(outlen);
  /* end of master code  */

exit_label:
    rsa_free(&key);
    return error_code;
} /* End of LLF_DX_RSA_Sign */

/**
****************************************************************
* Function Name: 
*  LLF_DX_RSA_Verify
*
* Inputs:
*  @param UserPubKey_ptr [in] - A pointer to the public key data structure of the user;
*  @param hashFunc       [in] - The hash function to be used. 
*  @param MGF            [in] - The mask generation function;
*  @param SaltLen        [in] - The Length of the Salt buffer;
*  @param DataIn_ptr     [in] - A pointer to the data whose signature is to be verified;
*  @param DataInSize     [in] - The size, in bytes, of the data whose signature is to be verified;
*  @param Sig_ptr        [in] - A pointer to the signature to be verified;
*  @param PKCS1_ver      [in] - Ver 1.5 or 2.1, according to the functionality required;
*
* Outputs: @returns \b
*  CE2Error_t  
*  - CE2_OK - On success
*  - Otherwise - error code:
*      CE2_INVALID_SIGNATURE,
*      CE2_BAD_PARAM,
*      CE2_OUT_OF_RANGE.
*
* \brief \b
* Description:
*  RSA_Verify implements the RSASSA-PKCS1v15 algorithm
   in a single function, as defined in PKCS#1 v1.5 and v2.1
*  The actual macro that will be used by the users is:
*   CE2_RSA_PKCS1v15_Verify			- for v1.5;
*   CE2_RSA_PSS_Verify				- for v2.1;
*									- for v2.1 MD5 is not supported, since
*					according to the PKCS#1 ver2.1 it is not recommended.
*
* \b
* Algorithm:
*  -# Register prng and hash;
*  -# Calculate message hash;
*  -# Compute an RSA modular exponentiation depending on PKCS ver;
*  -# Decode packet depending on PKCS ver - get verify msg hash.
***************************************************************/
CE2Error_t LLF_DX_RSA_Verify(
                        CE2_RSAUserPubKey_t *UserPubKey_ptr,/* in */
                        CE2_RSA_HASH_OpMode_t hashFunc,	/* in */
                        CE2_PKCS1_MGF_t MGF,		/* in */
                        DxUint16_t      SaltLen,	/* in */
                        DxUint8_t       *DataIn_ptr,/* in */
                        DxUint32_t      DataInSize,	/* in */
                        DxUint8_t       *Sig_ptr,	/* in */
                        CE2_PKCS1_version PKCS1_ver)/* in */
{
  CE2RSAPubKey_t * pubKey = (CE2RSAPubKey_t *)UserPubKey_ptr->PublicKeyDbBuff;

  prng_state yarrow_prng;
  unsigned long outlen = 0;
  unsigned long siglen;
  int stat = 0;		  /* default to invalid */
  unsigned char *decoded = 0;
  unsigned long decoded_len;
  int error_code;
  int hash_idx = 0;
  int prng_idx = 0;
  hash_state hs = { 0 };
  unsigned char hash[MAXBLOCKSIZE] = { 0 };
  DxUint32_t hashSize = MAXBLOCKSIZE;
  DxUint8_t empty_buffer;
  rsa_key key = { 0 };

  /* take outlen like modulus size */
  outlen = BITS2BYTES(pubKey->nSizeInBits);
  siglen = BITS2BYTES(pubKey->nSizeInBits);
  decoded_len = outlen;

  /* register prng */
  if (register_prng(&yarrow_desc) == -1)
  {
    error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
    goto exit_label;
  }

  /* register a math library (in this case TomFastMath) */
  ltc_mp = ltm_desc;

  /* Anna 6/8/2007 fix bug of TomCrypt that has wrong DER encoding T */
  /* of the DigestInfo values */
  /* for back word computability */
  /* The function changes the hash_descriptor values of the TomCrypt */
  error_code = RegisterOldHashDx(hashFunc, &hash_idx);
  if (error_code != CE2_OK)
    goto exit_label;

  if (PKCS1_ver == CE2_PKCS1_VER21)
  {
    if (MGF != CE2_PKCS1_MGF1)
    {
      error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
      goto exit_label;
    }
  }

  prng_idx = find_prng("yarrow");

  error_code = rng_make_prng(128, prng_idx, &yarrow_prng, 0);
  if (error_code != CRYPT_OK)
  {
    error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
    goto exit_label;
  }

  if (hash_idx == -1 || prng_idx == -1)
  {
    error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
    goto exit_label;
  }

  error_code = LLF_RSA_UserPubKey_TO_rsakey(UserPubKey_ptr, &key);
  if (error_code != CRYPT_OK)
  {
    error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
    goto exit_label;
  }

  /* master verify code starts */

  /* allocate ram for decoded packet */
  decoded = XMALLOC(siglen);
  if (!decoded)
  {
    error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
    goto exit_label;
  }

  /* before we start */
  if (PKCS1_ver == CE2_PKCS1_VER15)
  {
    /* first decode the packet */
    /* rsa decode the packet */
    error_code = rsa_exptmod(Sig_ptr, siglen, decoded, &decoded_len, PK_PUBLIC, &key);
    if (error_code != CRYPT_OK)
    {
      error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
      goto exit_label;
    }

    /*
    if hash mode is unknown, than all operations to decode
    the packet was performed, othervise need to determine hash_idx
    */
    if (hashFunc == CE2_RSA_After_HASH_NOT_KNOWN_mode)
    {
      /* determine the hash function */
      error_code = LLF_RSA_pkcs_v15_sa_get_sig_hash_mode(
        decoded, decoded_len, BITS2BYTES(pubKey->nSizeInBits), &hash_idx);
      if (error_code != CRYPT_OK)
      {
        error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
        goto exit_label;
      }
    }
  }

#if 0
  /* get msg hash */
  if ((PKCS1_ver == CE2_PKCS1_VER15) && 
    (hashFunc == CE2_RSA_After_HASH_NOT_KNOWN_mode)) {
    if (DataIn_ptr == NULL) {
      DataInSize = 0;
      DataIn_ptr = &empty_buffer;
    }

    error_code = hash_descriptor[hash_idx].init(&hs);
    if (error_code != CRYPT_OK)
    {
      error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
      goto exit_label;
    }

    error_code = hash_descriptor[hash_idx].process(&hs, DataIn_ptr, DataInSize);
    if (error_code != CRYPT_OK)
    {
      error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
      goto exit_label;
    }

    error_code = hash_descriptor[hash_idx].done(&hs, hash);
    if (error_code != CRYPT_OK)
    {
      error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
      goto exit_label;
    }
  } else {
    error_code = LLF_RSA_HASH(hashFunc, DataIn_ptr, DataInSize,
      hash, &hashSize);
    if (error_code != CRYPT_OK) {
      error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
      goto exit_label;
    }
  }
#else
  /* get msg hash */
  if (PKCS1_ver == CE2_PKCS1_VER15) {
    if (DataIn_ptr == NULL) {
      DataInSize = 0;
      DataIn_ptr = &empty_buffer;
    }
  }

  // if (hashFunc = CE2_RSA_After_HASH_NOT_KNOWN_mode) execute hash
  // after hash is known:
  if ((PKCS1_ver == CE2_PKCS1_VER15)&&
    (hashFunc == CE2_RSA_After_HASH_NOT_KNOWN_mode))
  {


    // signatue was done with some ...AFTERHASH mode, so hash should contain
    // original message
    if (DataIn_ptr != NULL) {
      memset(hash, 0, hash_descriptor[hash_idx].hashsize );
      memcpy(hash, DataIn_ptr, min(hash_descriptor[hash_idx].hashsize, DataInSize));
    }
  }
  else {
    error_code = LLF_RSA_HASH(hashFunc, DataIn_ptr, DataInSize,
      hash, &hashSize);
    if (error_code != CRYPT_OK) {
      error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
      goto exit_label;
    }
  }
#endif


  if (PKCS1_ver == CE2_PKCS1_VER15)
  {
    /* PKCS #1 v1.5 depad */
    error_code = LLF_RSA_pkcs_1_v15_sa_decode(
      hash, hash_descriptor[hash_idx].hashsize, decoded, decoded_len,
      hash_idx, pubKey->nSizeInBits, &stat);
    if (error_code != CRYPT_OK)
    {
      error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
      goto exit_label;
    }
  }
  else
    if (PKCS1_ver == CE2_PKCS1_VER21)
    {
      /* rsa exptmod the OAEP pad */
      error_code = ltc_mp.rsa_me(Sig_ptr, siglen, decoded, &decoded_len, PK_PUBLIC, &key);
      if (error_code != CRYPT_OK)
      {
        error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
        goto exit_label;
      }

      // retrieve SaltLen if it unknown
      if (SaltLen == CE2_RSA_VERIFY_SALT_LENGTH_UNKNOWN)
      {
        error_code =
          LLF_RSA_pss_get_sig_salt_len(decoded, decoded_len,
            pubKey->nSizeInBits, hash_idx, &SaltLen);
        if (error_code != CRYPT_OK)
        {
          error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
          goto exit_label;
        }
      }

      // need to recode: MGF inside
      /* if MGF1 is single call pkcs_1_oaep_encode, othervise recode */
      /* PSS decode it */
      error_code = pkcs_1_pss_decode(
        hash, hash_descriptor[hash_idx].hashsize,
        decoded, decoded_len, SaltLen, hash_idx, pubKey->nSizeInBits, &stat);
      if (error_code != CRYPT_OK)
      {
        error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
        goto exit_label;
      }
    }

    /* Check the result of our verification. */
    if (stat != 1)
    {
      error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
      goto exit_label;
    }

    /* end of master code  */

exit_label:
    XFREE(decoded);
    rsa_free(&key);
    return error_code;
} /* End of LLF_DX_RSA_Verify */

/**
****************************************************************
* Function Name: 
* LLF_DX_RSA_GetDEREncodingLength
*
*  @param hashFunc [in] - RSA hash function
*  @param pTLength [out] - Returns the DER encoding length (tLen), 
*                          as explained in the EMSA-PKCS1-v1_5
*
* @returns \b
*  CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*   - CE2_UTILS_INVALID_POINTER_ERROR
*
* \brief \b 
* Description:
*  Returns the DER encoding length (tLen), as explained in the EMSA-PKCS1-v1_5
*
*  \b 
* Algorithm:
***************************************************************/
CE2Error_t LLF_DX_RSA_GetDEREncodingLength(CE2_RSA_HASH_OpMode_t hashFunc,
                                           DxUint32_t			       *pTLength)
{
  CE2Error_t result = CE2_OK;
  DxUint32_t tLen = 0;

  /* Register hash */
  switch (hashFunc)
  {
  case CE2_RSA_After_HASH_NOT_KNOWN_mode: /* For PKCS1 v1.5 */
    /* If PKCS1_ver is CE2_PKCS1_VER15, then
    the value CE2_RSA_After_HASH_NOT_KNOWN_mode will allow
    the signature data to determine the hash function to be used.*/
    /* this operations are a little lower */
    break;
  case CE2_RSA_HASH_SHA1_mode:
  case CE2_RSA_After_SHA1_mode: /* For PKCS1 v1.5 */
    tLen = sha1_descDx.hashsize + sha1_descDx.OIDlen;
    break;
  case CE2_RSA_HASH_SHA224_mode:
  case CE2_RSA_After_SHA224_mode: /* For PKCS1 v1.5 */
    tLen = sha224_descDx.hashsize + sha224_descDx.OIDlen;
    break;
  case CE2_RSA_HASH_SHA256_mode:
  case CE2_RSA_After_SHA256_mode: /* For PKCS1 v1.5 */
    tLen = sha256_descDx.hashsize + sha256_descDx.OIDlen;
    break;
  case CE2_RSA_HASH_SHA384_mode:
  case CE2_RSA_After_SHA384_mode: /* For PKCS1 v1.5 */
    tLen = sha384_descDx.hashsize + sha384_descDx.OIDlen;
    break;
  case CE2_RSA_HASH_SHA512_mode:
  case CE2_RSA_After_SHA512_mode: /* For PKCS1 v1.5 */
    tLen = sha512_descDx.hashsize + sha512_descDx.OIDlen;
    break;
    /*	for v2.1 MD5 is not supported, since
    according to the PKCS#1 ver2.1 it is not recommended*/
    case CE2_RSA_HASH_MD5_mode:
    case CE2_RSA_After_MD5_mode:
		tLen = md5_descDx.hashsize + md5_descDx.OIDlen;
    break;
  default:
    result = CE2_LLF_RSA_MODULE_ERROR_BASE;
  }

  *pTLength = tLen;
  return result;
} /* End of LLF_DX_RSA_GetDEREncodingLength */
