/*
* Creation date : Wed Mar 07 18:29:07 2007
* Last modified : %modify_time%
*/
/** @file
* \brief This file contains implementation of 
* LLF_RSA_PRIM functions.
*
* \version LLF_RSA_PRIM.c#1:csrc:1
* \author Pavel Sasunkevich
* \remarks Copyright (C) 2007 by Discretix Technologies Ltd.
* All Rights reserved
*/

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

#include "LLF_RSA_PRIM.h"
#include "LLF_RSA_Common.h"

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

/**
****************************************************************
* Function Name: 
*  LLF_RSA_PRIM_Encrypt
*
* Inputs:
*  @param UserPubKey_ptr [in] - A pointer to the public key data structure;
*  @param Data_ptr   [in] - A pointer to the data to encrypt;
*  @param DataSize   [in] - The size, in bytes, of the data to encrypt;
*  @param Output_ptr [out] - Pointer to the encrypted data.
*
* Outputs: @returns \b
*  CE2Error_t  
*  - CE2_OK - On success
*  - Otherwise - error code:
*      CE2_RSA_INVALID_PUB_KEY_STRUCT_POINTER_ERROR,
*      CE2_RSA_PUB_KEY_VALIDATION_TAG_ERROR,
*      CE2_RSA_PRIM_DATA_STRUCT_POINTER_INVALID,
*      CE2_RSA_DATA_POINTER_INVALID_ERROR,
*      CE2_RSA_INVALID_OUTPUT_POINTER_ERROR,
*      CE2_RSA_INVALID_MESSAGE_BUFFER_SIZE,
*      CE2_RSA_INVALID_MESSAGE_DATA_SIZE,
*      CE2_RSA_INVALID_MESSAGE_VAL.
*
* \brief \b
* Description:
*  LLF_RSA_PRIM_Encrypt implements the RSAEP algorithm as defined in
*  PKCS#1 v2.1 6.1.1
*
* \b
* Algorithm:
*  -# Register and initialize prng and hash;
*  -# Build RSA public key using values from UserPubKey_ptr;
*  -# Encrypt data using RSA public key.
***************************************************************/
CE2Error_t LLF_RSA_PRIM_Encrypt(
                        CE2_RSAUserPubKey_t *UserPubKey_ptr,/* in */
                        DxUint8_t           *Data_ptr,		/* in */
                        DxUint16_t           DataSize,		/* in */
                        DxUint8_t           *Output_ptr)	/* out */
{
	CE2RSAPubKey_t * pubKey = (CE2RSAPubKey_t *)UserPubKey_ptr->PublicKeyDbBuff;

	prng_state yarrow_prng;
	//unsigned long e = 0;
	unsigned long outlen = 0;
	int error_code;
	int hash_idx;
	int prng_idx;
	rsa_key key = { 0 };

	/* register prng */
	error_code = (register_prng(&yarrow_desc) == -1) ?
		(CE2_LLF_RSA_MODULE_ERROR_BASE) : (CE2_OK);
	if (error_code != CRYPT_OK)
	{
		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 */
	error_code = (register_hash(&sha1_desc) == -1) ?
		(CE2_LLF_RSA_MODULE_ERROR_BASE) : (CE2_OK);
	if (error_code != CRYPT_OK)
	{
		error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
		goto exit_label;
	}

	hash_idx = find_hash("sha1");
	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;
	}

	error_code = (hash_idx == -1 || prng_idx == -1) ?
		(CE2_LLF_RSA_MODULE_ERROR_BASE) : (CE2_OK);
	if (error_code != CRYPT_OK)
	{
		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);

	/* encrypt data */
	error_code = rsa_encrypt_key(
		Data_ptr, DataSize,
		Output_ptr, &outlen,
		0, 0,
		&yarrow_prng, prng_idx, hash_idx, &key);
	if (error_code != CRYPT_OK)
	{
		error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
		goto exit_label;
	}

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

/**
****************************************************************
* Function Name: 
*  LLF_RSA_PRIM_Decrypt
*
* Inputs:
*  @param UserPrivKey_ptr [in] - Pointer to the private key data structure;
*  @param Data_ptr   [in] - A pointer to the data to be decrypted;
*  @param DataSize   [in] - The size, in bytes, of the data to decrypt;
*  @param Output_ptr [out] - A pointer to the decrypted data.
*
* Outputs: @returns \b
*  CE2Error_t  
*  - CE2_OK - On success
*  - Otherwise - error code:
*      CE2_RSA_INVALID_PRIV_KEY_STRUCT_POINTER_ERROR,
*      CE2_RSA_PRIM_DATA_STRUCT_POINTER_INVALID,
*      CE2_RSA_PRIV_KEY_VALIDATION_TAG_ERROR,
*      CE2_RSA_DATA_POINTER_INVALID_ERROR,
*      CE2_RSA_INVALID_OUTPUT_POINTER_ERROR,
*      CE2_RSA_INVALID_MESSAGE_DATA_SIZE,
*      CE2_RSA_INVALID_MESSAGE_VAL.
*
* \brief \b
* Description:
*  LLF_RSA_PRIM_Decrypt implements the RSADP algorithm as defined in
*  PKCS#1 v2.1 6.1.2
*
* \b
* Algorithm:
*  -# Register and initialize prng and hash;
*  -# Build RSA private key using values from UserPrivKey_ptr;
*  -# If private key is non CRT, then convert it to CRT mode;
*  -# Decrypt data using RSA private key.
***************************************************************/
CE2Error_t LLF_RSA_PRIM_Decrypt(
                        CE2_RSAUserPrivKey_t *UserPrivKey_ptr, /* in */
                        DxUint8_t     *Data_ptr,	/* in */
                        DxUint16_t     DataSize,	/* in */
                        DxUint8_t     *Output_ptr)	/* out */
{
	CE2RSAPrivKey_t * privKey = (CE2RSAPrivKey_t *)UserPrivKey_ptr->PrivateKeyDbBuff;

	prng_state yarrow_prng;
	//unsigned long e = 0;
	unsigned long outlen = 0;
	unsigned long x;
	//int stat = 0;		  /* default to invalid */
	unsigned char *decoded = 0;
	int result = 0;
	int error_code;
	int hash_idx;
	int prng_idx;
	rsa_key key = { 0 };

	/* register prng */
	error_code = (register_prng(&yarrow_desc) == -1) ?
		(CE2_LLF_RSA_MODULE_ERROR_BASE) : (CE2_OK);
	if (error_code != CRYPT_OK)
	{
		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 */
	error_code = (register_hash(&sha1_desc) == -1) ?
		(CE2_LLF_RSA_MODULE_ERROR_BASE) : (CE2_OK);
	if (error_code != CRYPT_OK)
	{
		error_code = CE2_LLF_RSA_MODULE_ERROR_BASE;
		goto exit_label;
	}

	hash_idx = find_hash("sha1");
	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;
	}

	error_code = (hash_idx == -1 || prng_idx == -1) ?
		(CE2_LLF_RSA_MODULE_ERROR_BASE) : (CE2_OK);
	if (error_code != CRYPT_OK)
	{
		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);

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

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

	/* decrypt message using builded RSA key */
	error_code = pkcs_1_oaep_decode(
		decoded, x,
		0, 0,
		privKey->nSizeInBits, hash_idx, Output_ptr, &outlen, &result);
	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);
	return error_code;
} /* End of LLF_RSA_PRIM_Decrypt */
