/*
* Creation date : Fri Mar 02 15:33:10 2007
* Last modified : %modify_time%
*/
/** @file
* \brief This file contains implementation of 
* LLF_DES() function. 
*
* \version LLF_DES.c#1:csrc:1
* \author Pavel Sasunkevich
* \remarks Copyright (C) 2007 by Discretix Technologies Ltd.
* All Rights reserved
*/

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

#include "LLF_DES.h"
#include "tomcrypt.h"

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

/**
****************************************************************
* Function Name: 
*  LLF_DES
*
* Inputs:
* @param IV_ptr [in\out] - This parameter is the buffer of the IV;
* @param Key_ptr [in] - A pointer to the user's key buffer;
* @param NumOfKeys [in] - The number of keys used: 1, 2, or 3 (defined in the enum);
* @param EncryptDecryptFlag [in] - A flag that determines if the DES should perform;
* @param OperationMode [in] - The operation mode: ECB or CBC;
* @param DataIn_ptr [in] - The pointer to the buffer of the input data to the DES;
* @param DataInSize [in] - The size of the input data.
* @param DataOut_ptr [in\out] - The pointer to the buffer of the output data from the DES;
*
* Outputs: @returns \b
*  CE2Error_t  
*  - CE2_OK - On success
*  - Otherwise - error code.
*
* \brief \b
* Description:
*   This function is used to operate the DES machine in one integrated
* operation: an Encrypt or a Decrypt operation, using operation
* mode: ECB or CBC.
*
* \b
* Algorithm:
*  -# Initialize DES machine;
*  -# Initialize operation mode: ECB or CBC;
*  -# Perform operation: an Encrypt operation or a Decrypt operation.
***************************************************************/
CE2Error_t LLF_DES  ( CE2_DES_Iv_t            IV_ptr,			/* in, out */
                      CE2_DES_Key_t           *Key_ptr,			/* in */
                      CE2_DES_NumOfKeys_t     NumOfKeys,		/* in */
                      CE2_DES_EncryptMode_t   EncryptDecryptFlag,/* in */
                      CE2_DES_OperationMode_t OperationMode,	/* in */
                      DxUint8_t              *DataIn_ptr,		/* in */
                      DxUint32_t              DataInSize,		/* in */
                      DxUint8_t              *DataOut_ptr )	/* in, out */
{
	struct setup_cipher
	{
		const struct ltc_cipher_descriptor * cipher;
		char * name;
		int key_length;
	};
	struct setup_cipher sc[4] =
	{
		{ 0, "", 0 },
		{ &des_desc, "des", 8 },
		{ &des3_desc, "3des", 24 },
		{ &des3_desc, "3des", 24 }
	};

	struct setup_cipher * psc = &sc[NumOfKeys];
	int des_index;
	int error_code;
	symmetric_CBC scbc;
	symmetric_ECB secb;

	/* Register cipher and get his index.
	It needs to perform DES operation, used 'des' or '3des'. */
	error_code = register_cipher(psc->cipher);
	if (error_code == -1)
		return CE2_LLF_DES_MODULE_ERROR_BASE;
	des_index = find_cipher(psc->name);
	
	if (des_index == -1)
		return CE2_LLF_DES_MODULE_ERROR_BASE;

	/* To perform operation with two keys in '3des' we need to
	create third key, which is taken (copied) from first. */
	if (NumOfKeys == CE2_DES_2_KeysInUse)
		memcpy(&(Key_ptr->key3), &(Key_ptr->key1), sizeof(Key_ptr->key3));

	/* Which mode to use: ECB or CBC. */
	if (OperationMode == CE2_DES_ECB_mode)
	{
		/* Initialize ECB mode with inputted key. */
		error_code = ecb_start(des_index, (const unsigned char *)Key_ptr, psc->key_length, 0, &secb);

		if (error_code == CRYPT_OK)
		{
			/* Perform encryption or decryption. */
			switch(EncryptDecryptFlag)
			{
			case CE2_DES_Encrypt:
				error_code = ecb_encrypt(DataIn_ptr, DataOut_ptr, DataInSize, &secb);
				break;
			case CE2_DES_Decrypt:
				error_code = ecb_decrypt(DataIn_ptr, DataOut_ptr, DataInSize, &secb);
				break;
			default:
				error_code = CE2_LLF_DES_MODULE_ERROR_BASE;
			}
			ecb_done(&secb);
		}
	} else
	if (OperationMode == CE2_DES_CBC_mode)
	{
		/* Initialize CBC mode with inputted key and IV value. */
		error_code = cbc_start(des_index, IV_ptr, (const unsigned char *)Key_ptr, psc->key_length, 0, &scbc);

		if (error_code == CRYPT_OK)
		{
			/* Perform encryption or decryption. */
			switch(EncryptDecryptFlag)
			{
			case CE2_DES_Encrypt:
				error_code = cbc_encrypt(DataIn_ptr, DataOut_ptr, DataInSize, &scbc);
				break;
			case CE2_DES_Decrypt:
				error_code = cbc_decrypt(DataIn_ptr, DataOut_ptr, DataInSize, &scbc);
				break;
			default:
				error_code = CE2_LLF_DES_MODULE_ERROR_BASE;
			}
			cbc_done(&scbc);
		}
	}
	else
		error_code = CE2_LLF_DES_MODULE_ERROR_BASE;

	return (error_code != CRYPT_OK) ? (CE2_LLF_HASH_MODULE_ERROR_BASE) : (CE2_OK);
} /* End of LLF_DES */
