
  /*
	*  Object %name	: %
	*  State			:  %state%
	*  Creation date	:  Tue Feb 01 17:15:04 2005
	*  Last modified	:  %modify_time%
	*/
  /** @file
	*  \brief A brief description of this module
	*
	*  \version CRYS_DH.c#1:csrc:8
	*  \author ohads
	*  \remarks Copyright (C) 2004 by Discretix Technologies Ltd.
	*			All Rights reserved
	*/



/************* Include Files ****************/
#include "DX_VOS_Mem.h"
#include "error.h"
#include "PLAT_SystemDep.h"
#include "SEPDriver.h" 
#include "CRYS_Defs.h"
#include "CRYS_KDF.h"
#include "CRYS_KDF_error.h"
#include "crys_host_op_code.h"


/************************ Defines *******************************/

/* canceling the lint warning:
	Use of goto is deprecated */
/*lint --e{801} */
#define CRYS_KDF_INTERNAL_MAX_SIZE_OTHER_INFO_ENTRY_IN_WORDS  64
/************************ Enums *********************************/

/************************ macros ********************************/

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

/****************************************************************/
/**
 * @brief _DX_KDF_KeyDerivFunc performs key derivation according to one of some modes defined in standards: 
 				ANSI X9.42-2001, ANSI X9.63, OMA_TS_DRM_DRM_V2_0-20050712-C.

				The present implementation of the function allows the following operation modes:
				- CRYS_KDF_ASN1_DerivMode - mode based on  ASN.1 DER encoding;
				- CRYS_KDF_ConcatDerivMode - mode based on concatenation;
					- CRYS_KDF_X963_DerivMode = CRYS_KDF_ConcatDerivMode;
 				- CRYS_KDF_OMA_DRM_DerivMode - specific mode for OMA DRM.

 			The purpose of this function is to derive a keying data from the shared secret value and some 
			other optional shared information (SharedInfo).
 			
	The actual APIs that will be used by the user are:
		- CRYS_KDF_ASN1_KeyDerivFunc ;			
		- CRYS_KDF_ConcatKeyDerivFunc ;
		- CRYS_KDF_OMADRM_DerivFunc .
 			
  \note The length in Bytes of the hash result buffer is denoted by "hashlen".
  \note All buffers arguments are represented in Big-Endian format.
 
  @param[in] ZZSecret_ptr 	- A pointer to shared secret value octet string. 
  @param[in] ZZSecretSize  	- The shared secret value Size, in bytes.
  @param[in] OtherInfo		- The pointer to structure, containing pointers and sizes of optional data shared  
										by two entities intended to share the secret value. This argument is optional 
								(if not needed - set it to NULL).
  @param[in] KDFhashMode	- The KDF identifier of hash function to be used. The hash function output must be at least 160 bits.
  @param[out] KeyingData_ptr - A pointer to the keying data derived from the secret key, of length KeyLenInBits
  @param[in] KeyingDataSizeBytes	- The size in bytes of the keying data to be generated. In our implementation - 
  								KeyLenInBytes <= 2^32-1 .
  @param[in] derivation_mode - Specifies one of above described derivation modes.

		NOTE: 
			1.  The size of shared secret value , and size of each of parties of OtherInfo must be less than 
				maximal size of input data for used HASH function.
  							
  @return CRYSError_t - On success the value CRYS_OK is returned, 
							and on failure an ERROR as defined in CRYS_KDF_error.h:
						CRYS_KDF_INVALID_ARGUMENT_POINTER_ERROR
						CRYS_KDF_INVALID_KEY_DERIVATION_MODE_ERROR
						CRYS_KDF_INVALID_SHARED_SECRET_VALUE_SIZE_ERROR
						CRYS_KDF_INVALID_SIZE_OF_DATA_TO_HASHING_ERROR
						CRYS_KDF_INVALID_ARGUMENT_HASH_MODE_ERROR
						CRYS_KDF_INVALID_OTHER_INFO_SIZE_ERROR
						CRYS_KDF_INVALID_KEYING_DATA_SIZE_ERROR
*/

CEXPORT_C CRYSError_t  _DX_KDF_KeyDerivFunc( 
								DxUint8_t				    *ZZSecret_ptr,
								DxUint32_t				    ZZSecretSize,
								CRYS_KDF_OtherInfo_t		*OtherInfo_ptr,
								CRYS_KDF_HASH_OpMode_t		KDFhashMode,
								CRYS_KDF_DerivFuncMode_t	derivation_mode,
								DxUint8_t				    *KeyingData_ptr,
								DxUint32_t				    KeyingDataSizeBytes )
{

	/* FUNCTION DECLARATIONS */

	/* The return error identifier */
	CRYSError_t		Error;

    /* offset into SRAM */
    DxUint32_t      sramOffset;

    /* read param */
    DxUint32_t      messageParam[5];
    
	/* Loop counters */
	DxUint32_t      i;
	
	/* The HASH input maximal block size */
	DxUint32_t		MaxHashInputSize;

	/* Other info Entry pointer */
	DxUint32_t		*OtherInfoEntry_ptr;

	/* Dummy var for storing zero */
	DxUint32_t		dummyZero = 0;
	
	/* Maximal message length */
	DxUint32_t maxLength = 0;

	/* FUNCTION LOGIC */
	
	/* ............... local initializations .............................. */
	/* -------------------------------------------------------------------- */
	
	/* initializing the Error to O.K */
	Error = CRYS_OK;

	#ifndef CRYS_NO_HASH_SUPPORT 
	#ifndef CRYS_NO_KDF_SUPPORT 
	
	
	/* ............... checking the parameters validity ................... */
	/* -------------------------------------------------------------------- */

	/* if an argument pointer is DX_NULL return an error */
	if( ZZSecret_ptr == DX_NULL || KeyingData_ptr == DX_NULL )
		return CRYS_KDF_INVALID_ARGUMENT_POINTER_ERROR;

	if( derivation_mode >= CRYS_KDF_DerivFunc_NumOfModes )
		return CRYS_KDF_INVALID_KEY_DERIVATION_MODE_ERROR;

	/* On ASN1 mode check the OtehrInfo pointer, on other modes it is optional */
	if( derivation_mode == CRYS_KDF_ASN1_DerivMode )
	{
		if(OtherInfo_ptr == DX_NULL) 
			return CRYS_KDF_INVALID_ARGUMENT_POINTER_ERROR;

		if( OtherInfo_ptr->AlgorithmID_ptr == DX_NULL )
				return CRYS_KDF_INVALID_ALGORITHM_ID_POINTER_ERROR;

		if( OtherInfo_ptr->SizeOfAlgorithmID == 0 )
			return CRYS_KDF_INVALID_ALGORITHM_ID_SIZE_ERROR;
	}
	/* Check sizes of the input data to be hashed (according to HASH conditions)*/
	MaxHashInputSize = 1 << 29;

	if(ZZSecretSize == 0 || ZZSecretSize > MaxHashInputSize )
			return CRYS_KDF_INVALID_SHARED_SECRET_VALUE_SIZE_ERROR;
		
		if( OtherInfo_ptr != DX_NULL )
	{
		if( OtherInfo_ptr->AlgorithmID_ptr != DX_NULL &&
			OtherInfo_ptr->SizeOfAlgorithmID > MaxHashInputSize )
				return CRYS_KDF_INVALID_OTHER_INFO_SIZE_ERROR;

		if( OtherInfo_ptr->PartyUInfo_ptr != DX_NULL &&
			OtherInfo_ptr->SizeOfPartyUInfo > MaxHashInputSize )
				return CRYS_KDF_INVALID_OTHER_INFO_SIZE_ERROR;

		if( OtherInfo_ptr->PartyVInfo_ptr != DX_NULL &&
			OtherInfo_ptr->SizeOfPartyVInfo > MaxHashInputSize )
				return CRYS_KDF_INVALID_OTHER_INFO_SIZE_ERROR;

		if( OtherInfo_ptr->SuppPrivInfo_ptr != DX_NULL &&
			OtherInfo_ptr->SizeOfSuppPrivInfo > MaxHashInputSize )
				return CRYS_KDF_INVALID_OTHER_INFO_SIZE_ERROR;

		if( OtherInfo_ptr->SuppPubInfo_ptr != DX_NULL &&
			OtherInfo_ptr->SizeOfSuppPubInfo > MaxHashInputSize )
				return CRYS_KDF_INVALID_OTHER_INFO_SIZE_ERROR;
	}
	
	/* Check the size of keying data. Note: because max size limited in our implementation by
		value 2^32-1, then we need to check the 0 value only */
	if(KeyingDataSizeBytes == 0)
		return CRYS_KDF_INVALID_KEYING_DATA_SIZE_ERROR;
    
	/* lock access to the SEP */
	Error = SEPDriver_Lock();
	
	if(Error != DX_OK)
	{
		goto end_function;
	}
	
	/*----------------------------
		start sending message to SEP 
	-----------------------------*/
	sramOffset = 0;
	
	/* start the message */
	SEPDriver_StartMessage(&sramOffset);
  
	/* prepare message */
	messageParam[0] = DX_SEP_HOST_SEP_PROTOCOL_HOST_KDF_KEY_DERIV_FUNC_OP_CODE;
	messageParam[1] = KDFhashMode;
	messageParam[2] = derivation_mode;
	messageParam[3] = KeyingDataSizeBytes;
	messageParam[4] = ZZSecretSize;
	
	/* send message */
	Error = SEPDriver_WriteParamater(	(DxUint32_t)messageParam, 
							sizeof(DxUint32_t) * 5,
							sizeof(DxUint32_t) * 5,
							&sramOffset,
							DX_FALSE);
	if(Error != DX_OK)
	{
		goto end_function_unlock;
	}
	
	/* write ZZSecret */
	Error = SEPDriver_WriteParamater(	(DxUint32_t)ZZSecret_ptr,
							ZZSecretSize,
							sizeof(DxUint32_t) * CRYS_KDF_INTERNAL_MAX_SIZE_OTHER_INFO_ENTRY_IN_WORDS,
							&sramOffset,
							DX_FALSE);
	if(Error != DX_OK)
	{
		goto end_function_unlock;
	}
	
	/* write OtherInfo struct */
	
	/* Set OtherInfoEntry_ptr to first entry pointer */
	OtherInfoEntry_ptr = (DxUint32_t*)OtherInfo_ptr;
	
	/* OtherInfo data concatenating and hashing loop */
	for( i = 0; i < CRYS_KDF_COUNT_OF_OTHER_INFO_ENTRIES; i++ )
	{
		if((OtherInfoEntry_ptr != DX_NULL) && (*OtherInfoEntry_ptr != DX_NULL) && (*(OtherInfoEntry_ptr + 1) != 0) )
		{
			/*check the internal size limit*/
			if((((DxUint32_t)*(OtherInfoEntry_ptr + 1))/sizeof(DxUint32_t)) > CRYS_KDF_INTERNAL_MAX_SIZE_OTHER_INFO_ENTRY_IN_WORDS)
			{
				Error = CRYS_KDF_INVALID_OTHER_INFO_SIZE_ERROR;
				goto end_function_unlock;
			}
			
			/* write the field size */
			Error = SEPDriver_WriteParamater(	(DxUint32_t)(OtherInfoEntry_ptr + 1),
									sizeof(DxUint32_t),
									sizeof(DxUint32_t),
									&sramOffset,
									DX_FALSE);
			if(Error != DX_OK)
			{
				goto end_function_unlock;
			}
			/* write the field data */
			Error = SEPDriver_WriteParamater(	(DxUint32_t)(*OtherInfoEntry_ptr),
									*(OtherInfoEntry_ptr + 1),
									sizeof(DxUint32_t) * CRYS_KDF_INTERNAL_MAX_SIZE_OTHER_INFO_ENTRY_IN_WORDS,
									&sramOffset,
									DX_FALSE);
			if(Error != DX_OK)
			{
				goto end_function_unlock;
			}
			
			/* Shift the pointer to the next entry */
			OtherInfoEntry_ptr += 2;
		}
		else
		{
			Error = SEPDriver_WriteParamater(	(DxUint32_t)(&dummyZero),
									sizeof(DxUint32_t),
									sizeof(DxUint32_t)*(CRYS_KDF_INTERNAL_MAX_SIZE_OTHER_INFO_ENTRY_IN_WORDS+1),
									&sramOffset,
									DX_FALSE);
			if(Error != DX_OK)
			{
				goto end_function_unlock;
			}
			
		}
		
	}
	
	SEPDriver_EndMessage(sramOffset);
            
    /* wait for the response */
    Error = SEPDriver_POLL_FOR_REPONSE();
    if(Error != DX_OK)
    {
        goto end_function_unlock;
    }

    /*-------------------
    start reading message from the SEP 
    ---------------------*/

    /* start the message */
    Error = SEPDriver_StartIncomingMessage(&sramOffset);
    if(Error != DX_OK)
    {
        goto end_function_unlock;
    }

    /* read opcode +  status  */
    Error = SEPDriver_ReadParamater((DxUint32_t)messageParam ,
                          sizeof(DxUint32_t) * 2,
                          sizeof(DxUint32_t) * 2, 
                          &sramOffset , 
                          DX_FALSE);
    if(Error != DX_OK)
    {
        goto end_function_unlock;
    }

    /* check the opcode */
    if(messageParam[0] != DX_SEP_HOST_SEP_PROTOCOL_HOST_KDF_KEY_DERIV_FUNC_OP_CODE)
    {
        Error = DX_WRONG_OPCODE_FROM_SEP_ERR;
        goto end_function_unlock;
    }

    if(messageParam[1] != CRYS_OK)
    {
        Error = messageParam[1];
        goto end_function_unlock;
    }

    /* align the message length */
    maxLength = ((KeyingDataSizeBytes+sizeof(DxUint32_t)-1)/sizeof(DxUint32_t))*sizeof(DxUint32_t);
    /* read the result */
    Error = SEPDriver_ReadParamater((DxUint32_t)KeyingData_ptr,
                           KeyingDataSizeBytes,
                           maxLength,
                           &sramOffset,
                           DX_FALSE);
    if(Error != DX_OK)
    {
        goto end_function_unlock;
    }
	
	end_function_unlock:	

	/* lock access to the SEP */
	SEPDriver_Unlock();

	end_function:

	return Error;

  #endif /*CRYS_NO_KDF_SUPPORT*/
  #endif /*CRYS_NO_HASH_SUPPORT*/
 

}/* END OF _DX_KDF_KeyDerivationFunc */	

