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

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

#include "LLF_HASH.h"
#include "tomcrypt.h"

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

/**
****************************************************************
* Function Name: 
*  LLF_HASH_HashStateInit
*
* Inputs:
* @param OperationMode [in] - The operation mode:
*                      CE2_HASH_SHA1_mode, CE2_HASH_SHA224_mode, CE2_HASH_SHA256_mode,
*                      CE2_HASH_SHA384_mode, CE2_HASH_SHA512_mode, CE2_HASH_MD5_mode.
* @param HashState_ptr [in/out] - internal state to be initialized.
* @param HashIndex_ptr [in/out] - [optional] hash index to be initialized.
*
* Outputs: @returns \b
*  CE2Error_t  
*  - CE2_OK - On success
*  - Otherwise - error code:
*   - CE2_HASH_ILLEGAL_HASH_OP_MODE_ERROR
*
* \brief \b
* Description:
*  Initializes hash state according to operation mode.
*
* \b
* Algorithm:
*  -# Initialize HASH state according to operation mode.
***************************************************************/
CE2Error_t LLF_HASH_HashStateInit(CE2_HASH_OperationMode_t	OperationMode,
								  hash_state				*HashState_ptr,
								  int						*HashIndex_ptr)
{
	int	hash_index;
	int	error_code = CRYPT_OK;

	/* Which mode to use:
	SHA1, SHA224, SHA256, SHA384, SHA512, MD5. */
	switch(OperationMode)
	{
	case CE2_HASH_SHA1_mode:
		if (register_hash(&sha1_desc) == -1) 
			return CE2_LLF_HASH_MODULE_ERROR_BASE;
		hash_index = find_hash("sha1");
		break;
    case CE2_HASH_SHA224_mode:
		if (register_hash(&sha224_desc) == -1) 
			return CE2_LLF_HASH_MODULE_ERROR_BASE;
		hash_index = find_hash("sha224");
		break;
    case CE2_HASH_SHA256_mode:
		if (register_hash(&sha256_desc) == -1) 
			return CE2_LLF_HASH_MODULE_ERROR_BASE;
		hash_index = find_hash("sha256");
		break;
    case CE2_HASH_SHA384_mode:
		if (register_hash(&sha384_desc) == -1) 
			return CE2_LLF_HASH_MODULE_ERROR_BASE;
		hash_index = find_hash("sha384");
		break;
    case CE2_HASH_SHA512_mode:
		if (register_hash(&sha512_desc) == -1) 
			return CE2_LLF_HASH_MODULE_ERROR_BASE;
		hash_index = find_hash("sha512");
		break;
    case CE2_HASH_MD5_mode:
		if (register_hash(&md5_desc) == -1) 
			return CE2_LLF_HASH_MODULE_ERROR_BASE;
		hash_index = find_hash("md5");
		break;
	default:
		return CE2_LLF_HASH_MODULE_ERROR_BASE;
	}

	if (hash_index == -1)
		return CE2_LLF_HASH_MODULE_ERROR_BASE;

	/* Initialize HASH machine. */
	error_code = hash_descriptor[hash_index].init(HashState_ptr);
	if (error_code != CRYPT_OK)
		return CE2_LLF_HASH_MODULE_ERROR_BASE;

	if (HashIndex_ptr != DX_NULL)
		*HashIndex_ptr = hash_index;

	return CE2_OK;
} /* End of LLF_HASH_HashStateInit */

/**
****************************************************************
* Function Name: 
*  LLF_HASH_HashStateToContext
*
* Inputs:
* @param OperationMode [in] - The operation mode:
*                      CE2_HASH_SHA1_mode, CE2_HASH_SHA224_mode, CE2_HASH_SHA256_mode,
*                      CE2_HASH_SHA384_mode, CE2_HASH_SHA512_mode, CE2_HASH_MD5_mode.
* @param HashState_ptr [in] - internal state to be used for initialization.
* @param ContextID_ptr [in] - A pointer to the HASH context buffer allocated by the user 
*                      that is used for the HASH machine operation.
*
* Outputs: @returns \b
*  None.
*
* \brief \b
* Description:
*  Initializes hash state of HASH context with using entire HASH state.
*
* \b
* Algorithm:
*  -# Initialize hash state of HASH context with using entire HASH state.
***************************************************************/
void LLF_HASH_HashStateToContext(CE2_HASH_OperationMode_t	OperationMode,
								 hash_state					*HashState_ptr,
								 CE2_HASHContext_t			*HASHContext_ptr)
{
	DxUint8_t	*buff_ptr;
	DxUint32_t	buff_size;
	DxUint64_t	*length;
	DxUint32_t	*state_ptr;
	DxUint32_t	state_size;
	DxUint32_t	*curlen;

	/* Which mode to use:
	SHA1, SHA224, SHA256, SHA384, SHA512, MD5. */
	switch(OperationMode)
	{
	case CE2_HASH_SHA1_mode:
		buff_ptr	= HashState_ptr->sha1.buf;
		buff_size	= sizeof(HashState_ptr->sha1.buf);
		length		= &HashState_ptr->sha1.length;
		state_ptr	= HashState_ptr->sha1.state;
		state_size	= sizeof(HashState_ptr->sha1.state);
		curlen		= &HashState_ptr->sha1.curlen;
		break;
	case CE2_HASH_SHA224_mode:
	case CE2_HASH_SHA256_mode:
		buff_ptr	= HashState_ptr->sha256.buf;
		buff_size	= sizeof(HashState_ptr->sha256.buf);
		length		= &HashState_ptr->sha256.length;
		state_ptr	= HashState_ptr->sha256.state;
		state_size	= sizeof(HashState_ptr->sha256.state);
	case CE2_HASH_SHA384_mode:
	case CE2_HASH_SHA512_mode:
		buff_ptr	= HashState_ptr->sha512.buf;
		buff_size	= sizeof(HashState_ptr->sha512.buf);
		length		= &HashState_ptr->sha512.length;
		state_ptr	= (DxUint32_t *)HashState_ptr->sha512.state;
		state_size	= sizeof(HashState_ptr->sha512.state);
		curlen		= &HashState_ptr->sha512.curlen;
		break;
	case CE2_HASH_MD5_mode:
		buff_ptr	= HashState_ptr->md5.buf;
		buff_size	= sizeof(HashState_ptr->md5.buf);
		length		= &HashState_ptr->md5.length;
		state_ptr	= HashState_ptr->md5.state;
		state_size	= sizeof(HashState_ptr->md5.state);
		curlen		= &HashState_ptr->md5.curlen;
		break;
	default:
		return;
	}

	HASHContext_ptr->OperationMode = OperationMode;

	memcpy(HASHContext_ptr->DataFromThePreviousUpdateBuff, buff_ptr, buff_size);
	memcpy(HASHContext_ptr->HASH_Result, state_ptr, state_size);

	HASHContext_ptr->Length = *length;
	HASHContext_ptr->CurLength = *curlen;
} /* End of LLF_HASH_HashStateToContext */

/**
****************************************************************
* Function Name: 
*  LLF_HASH_HashStateFromContext
*
* Inputs:
* @param ContextID_ptr [in] - A pointer to the HASH context buffer allocated by the user 
*                      that is used for the HASH machine operation.
* @param HashState_ptr [in] - internal state to be initialized.
*
* Outputs: @returns \b
*  None.
*
* \brief \b
* Description:
*  Initializes internal hash state with using entire HASH context.
*
* \b
* Algorithm:
*  -# Initialize internal hash state with using entire HASH context.
***************************************************************/
void LLF_HASH_HashStateFromContext(CE2_HASHContext_t	*HASHContext_ptr,
								   hash_state			*HashState_ptr)
{
	DxUint8_t	*buff_ptr;
	DxUint32_t	buff_size;
	DxUint64_t	*length;
	DxUint32_t	*state_ptr;
	DxUint32_t	state_size;
	DxUint32_t	*curlen;

	/* Which mode to use:
	SHA1, SHA224, SHA256, SHA384, SHA512, MD5. */
	switch(HASHContext_ptr->OperationMode)
	{
	case CE2_HASH_SHA1_mode:
		buff_ptr	= HashState_ptr->sha1.buf;
		buff_size	= sizeof(HashState_ptr->sha1.buf);
		length		= &HashState_ptr->sha1.length;
		state_ptr	= HashState_ptr->sha1.state;
		state_size	= sizeof(HashState_ptr->sha1.state);
		curlen		= &HashState_ptr->sha1.curlen;
		break;
	case CE2_HASH_SHA224_mode:
	case CE2_HASH_SHA256_mode:
		buff_ptr	= HashState_ptr->sha256.buf;
		buff_size	= sizeof(HashState_ptr->sha256.buf);
		length		= &HashState_ptr->sha256.length;
		state_ptr	= HashState_ptr->sha256.state;
		state_size	= sizeof(HashState_ptr->sha256.state);
	case CE2_HASH_SHA384_mode:
	case CE2_HASH_SHA512_mode:
		buff_ptr	= HashState_ptr->sha512.buf;
		buff_size	= sizeof(HashState_ptr->sha512.buf);
		length		= &HashState_ptr->sha512.length;
		state_ptr	= (DxUint32_t *)HashState_ptr->sha512.state;
		state_size	= sizeof(HashState_ptr->sha512.state);
		curlen		= &HashState_ptr->sha512.curlen;
		break;
	case CE2_HASH_MD5_mode:
		buff_ptr	= HashState_ptr->md5.buf;
		buff_size	= sizeof(HashState_ptr->md5.buf);
		length		= &HashState_ptr->md5.length;
		state_ptr	= HashState_ptr->md5.state;
		state_size	= sizeof(HashState_ptr->md5.state);
		curlen		= &HashState_ptr->md5.curlen;
		break;
	default:
		return;
	}

	memcpy(buff_ptr, HASHContext_ptr->DataFromThePreviousUpdateBuff, buff_size);
	memcpy(state_ptr, HASHContext_ptr->HASH_Result, state_size);

	*length = HASHContext_ptr->Length;
	*curlen = HASHContext_ptr->CurLength;
} /* End of LLF_HASH_HashStateFromContext */

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

/**
****************************************************************
* Function Name: 
*  LLF_HASH_Init
*
* Inputs:
* @param ContextID_ptr [in] - A pointer to the HASH context buffer allocated by the user 
*                      that is used for the HASH machine operation.
* @param OperationMode [in] - The operation mode:
*                      CE2_HASH_SHA1_mode, CE2_HASH_SHA224_mode, CE2_HASH_SHA256_mode,
*                      CE2_HASH_SHA384_mode, CE2_HASH_SHA512_mode, CE2_HASH_MD5_mode.
*
* Outputs: @returns \b
*  CE2Error_t  
*  - CE2_OK - On success
*  - Otherwise - error code:
*   - CE2_HASH_INVALID_USER_CONTEXT_POINTER_ERROR
*   - CE2_HASH_ILLEGAL_HASH_OP_MODE_ERROR
*
* \brief \b
* Description:
*  This function initializes the HASH machine on the CE2 level.
* This function allocates and initializes the HASH Context.
* The function receives as input a pointer to store the context handle to the HASH Context,
* and it initializes the HASH Context with the cryptographic attributes that are needed for 
* the HASH block operation (initializes H's value for the HASH algorithm).
*
* \b
* Algorithm:
*  -# Initializes HASH context with using inputted data.
***************************************************************/
CE2Error_t LLF_HASH_Init(CE2_HASHUserContext_t		*ContextID_ptr,
						 CE2_HASH_OperationMode_t	OperationMode)
{
	int					error_code = CRYPT_OK;
	hash_state			hstate;
	CE2_HASHContext_t	*HASHContext;

	/* Initialize hash sate. */
	error_code = LLF_HASH_HashStateInit(OperationMode, &hstate, DX_NULL);
	if (error_code != CE2_OK)
		return CE2_LLF_HASH_MODULE_ERROR_BASE;

	HASHContext = (CE2_HASHContext_t *)ContextID_ptr->context_buff;

	LLF_HASH_HashStateToContext(OperationMode, &hstate, HASHContext);

	/* Set context validation tag */
	ContextID_ptr->valid_tag = sizeof(CE2_HASHUserContext_t);

	return CE2_OK;
} /* End of LLF_HASH_Init */

/**
****************************************************************
* Function Name: 
*  LLF_HASH_Update
*
* Inputs:
* @param ContextID_ptr [in] - A pointer to the HASH context buffer allocated by the user 
*                      that is used for the HASH machine operation.
* @param DataIn_ptr [in] - A pointer to the buffer that stores the data to be hashed;
* @param DataSize [in] - The size of the data to be hashed in bytes;
*
* Outputs: @returns \b
*  CE2Error_t  
*  - CE2_OK - On success
*  - Otherwise - error code:
*   - CE2_HASH_INVALID_USER_CONTEXT_POINTER_ERROR
*   - CE2_HASH_INVALID_DATA_IN_PTR_ERROR
*
* \brief \b
* Description:
*  This function process a block of data via the HASH Hardware/Software.
* The function receives as input an handle to the HASH Context that was previously 
* initialized by a CE2_HASH_Init function or by another call to a CE2_HASH_Update function. \n
* The function sets the last H's value that was stored in 
* the CE2 HASH context, and then processes the data block. 
* At the end of the process, the function stores in the HASH context the H's value, 
* together with the cryptographic attributes that are needed for 
* the HASH block operation (initialize H's value for the HASH algorithm).
* This function is used in cases where the data is not all arranged in one 
* continuous buffer. \n
*
* \b
* Algorithm:
*  -# Update HASH using inputted data with using LibTomCrypt.
***************************************************************/
CE2Error_t LLF_HASH_Update(CE2_HASHUserContext_t	*ContextID_ptr,
						   DxUint8_t				*DataIn_ptr,
						   DxUint32_t				DataInSize)
{
	int					error_code = CRYPT_OK;
	hash_state			hstate;
	int					hash_index;
	CE2_HASHContext_t	*HASHContext = (CE2_HASHContext_t *)ContextID_ptr->context_buff;

	/* Initialize hash sate. */
	error_code = LLF_HASH_HashStateInit(HASHContext->OperationMode, &hstate, &hash_index);
	if (error_code != CE2_OK)
		return CE2_LLF_HASH_MODULE_ERROR_BASE;

	LLF_HASH_HashStateFromContext(HASHContext, &hstate);

	/* Get HASH result to our output buffer. */
	error_code = hash_descriptor[hash_index].process(&hstate, DataIn_ptr, DataInSize);
	if (error_code != CRYPT_OK)
		return CE2_LLF_HASH_MODULE_ERROR_BASE;

	LLF_HASH_HashStateToContext(HASHContext->OperationMode, &hstate, HASHContext);

	return CE2_OK;
} /* End of LLF_HASH_Update */

/**
****************************************************************
* Function Name: 
*  LLF_HASH_Finish
*
* Inputs:
* @param ContextID_ptr [in] - A pointer to the HASH context buffer allocated by the user 
*                      that is used for the HASH machine operation.
* @param HashResultBuff [out] - A pointer to the target buffer where the 
*                  HASH result stored in the context is loaded to.
*
* Outputs: @returns \b
*  CE2Error_t  
*  - CE2_OK - On success
*  - Otherwise - error code:
*   - CE2_HASH_INVALID_USER_CONTEXT_POINTER_ERROR
*   - CE2_HASH_INVALID_HASH_RESULT_BUFF_PTR_ERROR
*
* \brief \b
* Description:
*   This function finalizes the hashing process of data block.
* The function receives as input an handle to the HASH Context , that was initialized before
* by an CE2_HASH_Init function or by CE2_HASH_Update function.
* The function "adds" an header to the data block as the specific hash standard 
* specifics, then it loads the engine and reads the final message digest.
*
* \b
* Algorithm:
*  -# Finish HASH calculation and copy it into HASH result buffer
*     with using LibTomCrypt.
***************************************************************/
CE2Error_t LLF_HASH_Finish(CE2_HASHUserContext_t	*ContextID_ptr,
						   CE2_HASH_Result_t		HashResultBuff)
{
	int					error_code = CRYPT_OK;
	hash_state			hstate;
	int					hash_index;
	CE2_HASHContext_t	*HASHContext = (CE2_HASHContext_t *)ContextID_ptr->context_buff;

	/* Initialize hash sate. */
	error_code = LLF_HASH_HashStateInit(HASHContext->OperationMode, &hstate, &hash_index);
	if (error_code != CE2_OK)
		return CE2_LLF_HASH_MODULE_ERROR_BASE;

	LLF_HASH_HashStateFromContext(HASHContext, &hstate);

	/* Get HASH result to our output buffer. */
	error_code = hash_descriptor[hash_index].done(&hstate, (unsigned char *)HashResultBuff);
	if (error_code != CRYPT_OK)
		return CE2_LLF_HASH_MODULE_ERROR_BASE;

	LLF_HASH_HashStateToContext(HASHContext->OperationMode, &hstate, HASHContext);

	return CE2_OK;
} /* End of LLF_HASH_Finish */

/**
****************************************************************
* Function Name: 
*  LLF_HASH_Free
*
* Inputs:
* @param ContextID_ptr [in] - A pointer to the HASH context buffer allocated by the user 
*                      that is used for the HASH machine operation.
*
* Outputs: @returns \b
*  CE2Error_t  
*  - CE2_OK - On success
*  - Otherwise - error code:
*   - CE2_HASH_INVALID_USER_CONTEXT_POINTER_ERROR
*
* \brief \b
* Description:
*  This function is a utility function that frees the context if the operation has failed.
*
*        The function executes the following major steps:
*
*        1. Checks the validity of all of the inputs of the function. 
*           If one of the received parameters is not valid it shall return an error.
*           The main checks that are run over the received parameters:
*           - verifying that the context pointer is not DX_NULL (*ContextID_ptr). 
*        2. Clears the user's context.
*        3. Exits the handler with the OK code.
*
* \b
* Algorithm:
*  -# Clear HASH context.
***************************************************************/
CE2Error_t LLF_HASH_Free(CE2_HASHUserContext_t *ContextID_ptr)
{
	ContextID_ptr->valid_tag = 0;
	((CE2_HASHContext_t *)ContextID_ptr->context_buff)->OperationMode = 0xffff;

	return CE2_OK;
} /* End of LLF_HASH_Free */

/**
****************************************************************
* Function Name: 
*  LLF_HASH
*
* Inputs:
* @param OperationMode [in] - The operation mode :
*                  SHA1, SHA224, SHA256, SHA384, SHA512, MD5;
* @param DataIn_ptr [in] - A pointer to the buffer that stores the data to be hashed;
* @param DataSize [in] - The size of the data to be hashed in bytes;
* @param HashResultBuff [out] - A pointer to the target buffer where the 
*                  HASH result stored in the context is loaded to.
*
* Outputs: @returns \b
*  CE2Error_t  
*  - CE2_OK - On success
*  - Otherwise - error code.
*
* \brief \b
* Description:
*   The function allocates an internal HASH Context, and initializes the 
* HASH Context with the cryptographic attributes that are needed for 
* the HASH block operation (initialize H's value for the HASH algorithm).
* Next the function loads the engine with the initializing values, 
* and then processes the data block, calculating the hash.
* Finally, the function return the message digest of the data buffer .
*
* \b
* Algorithm:
*  -# Initialize HASH machine;
*  -# Perform hash operation.
***************************************************************/
CE2Error_t LLF_HASH  ( CE2_HASH_OperationMode_t  OperationMode,	/* in */
                       DxUint8_t                 *DataIn_ptr,	/* in */
                       DxUint32_t                DataSize,		/* in */
                       CE2_HASH_Result_t         HashResultBuff )/* out */
{
	int hash_index;
	int error_code = CRYPT_OK;
	unsigned long hashBufferSize = sizeof(CE2_HASH_Result_t);

	/* Which mode to use:
	SHA1, SHA224, SHA256, SHA384, SHA512, MD5. */
	switch(OperationMode)
	{
	case CE2_HASH_SHA1_mode:
		if (register_hash(&sha1_desc) == -1) 
			return CE2_LLF_HASH_MODULE_ERROR_BASE;
		hash_index = find_hash("sha1");
		break;
    case CE2_HASH_SHA224_mode:
		if (register_hash(&sha224_desc) == -1) 
			return CE2_LLF_HASH_MODULE_ERROR_BASE;
		hash_index = find_hash("sha224");
		break;
    case CE2_HASH_SHA256_mode:
		if (register_hash(&sha256_desc) == -1) 
			return CE2_LLF_HASH_MODULE_ERROR_BASE;
		hash_index = find_hash("sha256");
		break;
    case CE2_HASH_SHA384_mode:
		if (register_hash(&sha384_desc) == -1) 
			return CE2_LLF_HASH_MODULE_ERROR_BASE;
		hash_index = find_hash("sha384");
		break;
    case CE2_HASH_SHA512_mode:
		if (register_hash(&sha512_desc) == -1) 
			return CE2_LLF_HASH_MODULE_ERROR_BASE;
		hash_index = find_hash("sha512");
		break;
    case CE2_HASH_MD5_mode:
		if (register_hash(&md5_desc) == -1) 
			return CE2_LLF_HASH_MODULE_ERROR_BASE;
		hash_index = find_hash("md5");
		break;
	default:
		return CE2_LLF_HASH_MODULE_ERROR_BASE;
	}

	if (hash_index == -1)
		return CE2_LLF_HASH_MODULE_ERROR_BASE;

	/* Perform hash operation. */
	if (DataIn_ptr == DX_NULL)
	{
		hash_state hstate;

		/* Initialize HASH machine. */
		error_code = hash_descriptor[hash_index].init(&hstate);
		if (error_code != CRYPT_OK)
			return CE2_LLF_HASH_MODULE_ERROR_BASE;

		/* Get HASH result to our output buffer. */
		error_code = hash_descriptor[hash_index].done(&hstate, (unsigned char *)HashResultBuff);
	}
	else
		error_code = hash_memory(hash_index, DataIn_ptr, DataSize,
			(unsigned char *)HashResultBuff, &hashBufferSize);

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