/**
 * Copyright (C) 2011 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Mobile Communication Division,
 * Digital Media & Communications Business, Samsung Electronics Co., Ltd.
 *
 * This software and its documentation are confidential and proprietary
 * information of Samsung Electronics Co., Ltd.  No part of the software and
 * documents may be copied, reproduced, transmitted, translated, or reduced to
 * any electronic medium or machine-readable form without the prior written
 * consent of Samsung Electronics.
 *
 * Samsung Electronics makes no representations with respect to the contents,
 * and assumes no responsibility for any errors that might appear in the
 * software and documents. This publication and the contents hereof are subject
 * to change without notice.
 *
 */

/**
 * @file tz_hdcp2_transmitter.c
 * @author
 * @date
 * @brief This file contains definition of all the functions used in transmitter in SWD side
 */

#include <string.h>
#include <unistd.h>

#include "tz_hdcp2_crypto.h"
#include "tz_hdcp2.h"

#ifdef USE_WFD_TS_MUX_HW
#include "tees_ssapi.h"
#endif /* USE_WFD_TS_MUX_HW */

#ifdef USE_MTK
#include "drSecMemApi.h"
#endif /* USE_MTK */

/**
 * @def NULL_ERR(x,y)
 * This macro is assigned a value of
 * if(!x)return(y);
 * to be maintained everywhere.
 */
#define NULL_ERR(x,y)	if(!x) return(y);

static TZ_HDCP2_CTX hdcp2_ctx;

uint8_t pairing_info_dir[] = "/teesst/sshdcpapp";

#ifdef TEEGRIS_V4
#define CRYPTO_DEV_NAME "/dev/crypto"
#define PHYS_DEV_NAME "/dev/phys"
#else
#define CRYPTO_DEV_NAME "dev://crypto"
#define PHYS_DEV_NAME	"phys://"
#endif

int g_secdrv_fd;
int g_crypt_dev_fd = 0;

#define HDCP2_MESSAGE_DIGEST_SIZE	32
#define HDCP2_MASTER_KEY_SIZE	16

// This is published DCP-LLC Transmitter public key .
// refer spec HDCP 2.1 specification section 2.1 ( Table 2.1)
char test_certid_r1[5] = {0x74, 0x5B, 0xB8, 0xBD, 0x04};
char test_certid_r2[5] = {0x8B, 0xA4, 0x47, 0x42, 0xFB};

// This is Test Key published by DCP-LLC Lab .
char mod_buf_test[384] = {
	0xA2,0xC7,0x55,0x57,0x54,0xCB,0xAA,0xA7,0x7A,
	0x27,0x92,0xC3,0x1A,0x6D,0xC2,0x31,0xCF,0x12,
	0xC2,0x24,0xBF,0x89,0x72,0x46,0xA4,0x8D,0x20,
	0x83,0xB2,0xDD,0x04,0xDA,0x7E,0x01,0xA9,0x19,
	0xEF,0x7E,0x8C,0x47,0x54,0xC8,0x59,0x72,0x5C,
	0x89,0x60,0x62,0x9F,0x39,0xD0,0xE4,0x80,0xCA,
	0xA8,0xD4,0x1E,0x91,0xE3,0x0E,0x2C,0x77,0x55,
	0x6D,0x58,0xA8,0x9E,0x3E,0xF2,0xDA,0x78,0x3E,
	0xBA,0xD1,0x05,0x37,0x07,0xF2,0x88,0x74,0x0C,
	0xBC,0xFB,0x68,0xA4,0x7A,0x27,0xAD,0x63,0xA5,
	0x1F,0x67,0xF1,0x45,0x85,0x16,0x49,0x8A,0xE6,
	0x34,0x1C,0x6E,0x80,0xF5,0xFF,0x13,0x72,0x85,
	0x5D,0xC1,0xDE,0x5F,0x01,0x86,0x55,0x86,0x71,
	0xE8,0x10,0x33,0x14,0x70,0x2A,0x5F,0x15,0x7B,
	0x5C,0x65,0x3C,0x46,0x3A,0x17,0x79,0xED,0x54,
	0x6A,0xA6,0xC9,0xDF,0xEB,0x2A,0x81,0x2A,0x80,
	0x2A,0x46,0xA2,0x06,0xDB,0xFD,0xD5,0xF3,0xCF,
	0x74,0xBB,0x66,0x56,0x48,0xD7,0x7C,0x6A,0x03,
	0x14,0x1E,0x55,0x56,0xE4,0xB6,0xFA,0x38,0x2B,
	0x5D,0xFB,0x87,0x9F,0x9E,0x78,0x21,0x87,0xC0,
	0x0C,0x63,0x3E,0x8D,0x0F,0xE2,0xA7,0x19,0x10,
	0x9B,0x15,0xE1,0x11,0x87,0x49,0x33,0x49,0xB8,
	0x66,0x32,0x28,0x7C,0x87,0xF5,0xD2,0x2E,0xC5,
	0xF3,0x66,0x2F,0x79,0xEF,0x40,0x5A,0xD4,0x14,
	0x85,0x74,0x5F,0x06,0x43,0x50,0xCD,0xDE,0x84,
	0xE7,0x3C,0x7D,0x8E,0x8A,0x49,0xCC,0x5A,0xCF,
	0x73,0xA1,0x8A,0x13,0xFF,0x37,0x13,0x3D,0xAD,
	0x57,0xD8,0x51,0x22,0xD6,0x32,0x1F,0xC0,0x68,
	0x4C,0xA0,0x5B,0xDD,0x5F,0x78,0xC8,0x9F,0x2D,
	0x3A,0xA2,0xB8,0x1E,0x4A,0xE4,0x08,0x55,0x64,
	0x05,0xE6,0x94,0xFB,0xEB,0x03,0x6A,0x0A,0xBE,
	0x83,0x18,0x94,0xD4,0xB6,0xC3,0xF2,0x58,0x9C,
	0x7A,0x24,0xDD,0xD1,0x3A,0xB7,0x3A,0xB0,0xBB,
	0xE5,0xD1,0x28,0xAB,0xAD,0x24,0x54,0x72,0x0E,
	0x76,0xD2,0x89,0x32,0xEA,0x46,0xD3,0x78,0xD0,
	0xA9,0x67,0x78,0xC1,0x2D,0x18,0xB0,0x33,0xDE,
	0xDB,0x27,0xCC,0xB0,0x7C,0xC9,0xA4,0xBD,0xDF,
	0x2B,0x64,0x10,0x32,0x44,0x06,0x81,0x21,0xB3,
	0xBA,0xCF,0x33,0x85,0x49,0x1E,0x86,0x4C,0xBD,
	0xF2,0x3D,0x34,0xEF,0xD6,0x23,0x7A,0x9F,0x2C,
	0xDA,0x84,0xF0,0x83,0x83,0x71,0x7D,0xDA,0x6E,
	0x44,0x96,0xCD,0x1D,0x05,0xDE,0x30,0xF6,0x1E,
	0x2F,0x9C,0x99,0x9C,0x60,0x07
};

// This is published DCP-LLC Transmitter public key .
// refer spec HDCP 2.1 specification section 2.1 ( Table 2.1)
char mod_buf[384] = {
	0xB0,0xE9,0xAA,0x45,0xF1,0x29,0xBA,0x0A,0x1C,0xBE,0x17,0x57,0x28,0xEB,0x2B,0x4E,
	0x8F,0xD0,0xC0,0x6A,0xAD,0x79,0x98,0x0F,0x8D,0x43,0x8D,0x47,0x04,0xB8,0x2B,0xF4,
	0x15,0x21,0x56,0x19,0x01,0x40,0x01,0x3B,0xD0,0x91,0x90,0x62,0x9E,0x89,0xC2,0x27,
	0x8E,0xCF,0xB6,0xDB,0xCE,0x3F,0x72,0x10,0x50,0x93,0x8C,0x23,0x29,0x83,0x7B,0x80,
	0x64,0xA7,0x59,0xE8,0x61,0x67,0x4C,0xBC,0xD8,0x58,0xB8,0xF1,0xD4,0xF8,0x2C,0x37,
	0x98,0x16,0x26,0x0E,0x4E,0xF9,0x4E,0xEE,0x24,0xDE,0xCC,0xD1,0x4B,0x4B,0xC5,0x06,
	0x7A,0xFB,0x49,0x65,0xE6,0xC0,0x00,0x83,0x48,0x1E,0x8E,0x42,0x2A,0x53,0xA0,0xF5,
	0x37,0x29,0x2B,0x5A,0xF9,0x73,0xC5,0x9A,0xA1,0xB5,0xB5,0x74,0x7C,0x06,0xDC,0x7B,
	0x7C,0xDC,0x6C,0x6E,0x82,0x6B,0x49,0x88,0xD4,0x1B,0x25,0xE0,0xEE,0xD1,0x79,0xBD,
	0x39,0x85,0xFA,0x4F,0x25,0xEC,0x70,0x19,0x23,0xC1,0xB9,0xA6,0xD9,0x7E,0x3E,0xDA,
	0x48,0xA9,0x58,0xE3,0x18,0x14,0x1E,0x9F,0x30,0x7F,0x4C,0xA8,0xAE,0x53,0x22,0x66,
	0x2B,0xBE,0x24,0xCB,0x47,0x66,0xFC,0x83,0xCF,0x5C,0x2D,0x1E,0x3A,0xAB,0xAB,0x06,
	0xBE,0x05,0xAA,0x1A,0x9B,0x2D,0xB7,0xA6,0x54,0xF3,0x63,0x2B,0x97,0xBF,0x93,0xBE,
	0xC1,0xAF,0x21,0x39,0x49,0x0C,0xE9,0x31,0x90,0xCC,0xC2,0xBB,0x3C,0x02,0xC4,0xE2,
	0xBD,0xBD,0x2F,0x84,0x63,0x9B,0xD2,0xDD,0x78,0x3E,0x90,0xC6,0xC5,0xAC,0x16,0x77,
	0x2E,0x69,0x6C,0x77,0xFD,0xED,0x8A,0x4D,0x6A,0x8C,0xA3,0xA9,0x25,0x6C,0x21,0xFD,
	0xB2,0x94,0x0C,0x84,0xAA,0x07,0x29,0x26,0x46,0xF7,0x9B,0x3A,0x19,0x87,0xE0,0x9F,
	0xEB,0x30,0xA8,0xF5,0x64,0xEB,0x07,0xF1,0xE9,0xDB,0xF9,0xAF,0x2C,0x8B,0x69,0x7E,
	0x2E,0x67,0x39,0x3F,0xF3,0xA6,0xE5,0xCD,0xDA,0x24,0x9B,0xA2,0x78,0x72,0xF0,0xA2,
	0x27,0xC3,0xE0,0x25,0xB4,0xA1,0x04,0x6A,0x59,0x80,0x27,0xB5,0xDA,0xB4,0xB4,0x53,
	0x97,0x3B,0x28,0x99,0xAC,0xF4,0x96,0x27,0x0F,0x7F,0x30,0x0C,0x4A,0xAF,0xCB,0x9E,
	0xD8,0x71,0x28,0x24,0x3E,0xBC,0x35,0x15,0xBE,0x13,0xEB,0xAF,0x43,0x01,0xBD,0x61,
	0x24,0x54,0x34,0x9F,0x73,0x3E,0xB5,0x10,0x9F,0xC9,0xFC,0x80,0xE8,0x4D,0xE3,0x32,
	0x96,0x8F,0x88,0x10,0x23,0x25,0xF3,0xD3,0x3E,0x6E,0x6D,0xBB,0xDC,0x29,0x66,0xEB
};

char exp_buf[] = {0x03, };

/**
 * @def SIZE_OF_DCPLLC_SIGNATURE
 * This macro is assigned a value of 384 to be maintained everywhere.
 */
#define SIZE_OF_DCPLLC_SIGNATURE	384

/**
 * @def SIZE_OF_PUBLIC_KEY_CERTIFICATE_OF_HDCP_RECEIVER
 * This macro is assigned a value of 522 to be maintained everywhere.
 */
#define SIZE_OF_PUBLIC_KEY_CERTIFICATE_OF_HDCP_RECEIVER	522

/**
 * @def SIZE_OF_DCP_PUBLICKEY_MODULUS
 * This macro is assigned a value of 384 to be maintained everywhere.
 */
#define SIZE_OF_DCP_PUBLICKEY_MODULUS	384

/**
 * @def SIZE_OF_DCP_PUBLICKEY_EXPONENT
 * This macro is assigned a value of 1 to be maintained everywhere.
 */
#define SIZE_OF_DCP_PUBLICKEY_EXPONENT	1

#ifdef USE_SET_DRM_FLAG
struct tci_set_hdcp_flag_msg_t {
	uint32_t id;
	uint32_t return_code;
	uint32_t sid;
	uint32_t hdcp_flag;
};

#define TA_WV_DRM_UUID	{0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x57, 0x56, 0x44, 0x52, 0x4d}}
#define DRM_SET_HDCP_INFO	0x00004000

int TZ_Set_HDCP2Flag(bool openflag)
{
	TEE_UUID pTargetUUID = TA_WV_DRM_UUID;
	TEE_TASessionHandle drm_session = TEE_HANDLE_NULL;

	TEE_Param targetParams[4];
	TEE_Result TEE_ret;

	uint32_t paramTypes;
	uint32_t ret_val;
	struct tci_set_hdcp_flag_msg_t hdcp_flag_msg;

	LOGI("Open DRM session\n");
	paramTypes = TEE_PARAM_TYPES(TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE);
	TEE_ret = TEE_OpenTASession(&pTargetUUID, TEE_TIMEOUT_INFINITE, paramTypes, targetParams, &drm_session, &ret_val);
	if (TEE_ret != TEE_SUCCESS) {
		LOGE("TEE_OpenTASession error [TEE_ret: %x]\n", TEE_ret);
		return HDCP2_ERR;
	}

	if (drm_session == TEE_HANDLE_NULL) {
		LOGI("Session DRM TA is already closed\n");
		return HDCP2_ERR;
	}

	LOGI("Invoke DRM TA (%d)\n", openflag);
	if (openflag)
		hdcp_flag_msg.hdcp_flag = 0x4; // HDCP Open (HDCP Authenticte success)
	else
		hdcp_flag_msg.hdcp_flag = 0xF; // HDCP Close

	hdcp_flag_msg.id = DRM_SET_HDCP_INFO;

	targetParams[0].memref.buffer = (void *)&hdcp_flag_msg;
	targetParams[0].memref.size = sizeof(struct tci_set_hdcp_flag_msg_t);

	paramTypes = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INOUT, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE);
	TEE_ret = TEE_InvokeTACommand(drm_session, TEE_TIMEOUT_INFINITE, DRM_SET_HDCP_INFO, paramTypes, targetParams, &ret_val);
	if (TEE_ret != TEE_SUCCESS) {
		LOGE("TEE_OpenTASession error [TEE_ret: %x]\n", TEE_ret);
		return HDCP2_ERR;
	}

	LOGI("Close DRM session\n");
	TEE_CloseTASession(drm_session);

	return HDCP2_OK;
}
#endif /* USE_SET_DRM_FLAG */

/**
 * int TZ_HW_Get_Oem_Flag()
 * @brief This function performs the integrity check
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_HW_Get_Oem_Flag()
{
	u32 flag_id = 3;
	u32 value = 0;

	if (HDCP2_OK != TEES_GetIrsFlagValue(&flag_id, &value))
		return HDCP2_ERR_GET_OEM_FLAG;

#ifndef DEBUG
	// Integrity Check Failed State
	if (value)
		return HDCP2_ERR_INTEGRITY;
#endif /* DEBUG */

	return HDCP2_OK;
}

/**
 * @fn int TZ_HW_Init_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function opens the crypto dev used later during Encryption
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_HW_Init_T(const u8 command, u8 *request, const u32 req_size,
			u8 *response, u32 *res_size)
{
	if (TZ_HW_Get_Oem_Flag() != HDCP2_OK) {
		LOGE("Integrity check fail\n");
		return HDCP2_ERR_INTEGRITY;
	}

	g_crypt_dev_fd = open(CRYPTO_DEV_NAME, O_RDONLY, 0);
	if (g_crypt_dev_fd<=0) {
		LOGE("open CRYPTO DEV failed with errno : %d\n", errno);
		return HDCP2_ERR;
	}

#ifdef USE_SET_DRM_FLAG
	if (TZ_Set_HDCP2Flag(true) != HDCP2_OK) {
		LOGE("Fail setting HDCP2 flag\n");
		return HDCP2_ERR;
	}
#endif /* USE_SET_DRM_FLAG */

	return HDCP2_OK;
}

int TZ_HW_Close_T(const u8 command, u8 *request, const u32 req_size,
			u8 *response, u32 *res_size)
{
#ifdef USE_SET_DRM_FLAG
	if (TZ_Set_HDCP2Flag(false) != HDCP2_OK) {
		LOGE("Fail unsetting HDCP2 flag\n");
		return HDCP2_ERR;
	}
#endif /* USE_SET_DRM_FLAG */

	if (g_crypt_dev_fd > 0) {
		close(g_crypt_dev_fd);
	} else {
		return HDCP2_ERR;
	}
	g_crypt_dev_fd = 0;

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_Reset_T()
 * @brief This function resets the HDCP context
 * @return int - HDCP2_OK
 */
int TZ_AKE_Reset_T()
{
	TEE_MemFill(&hdcp2_ctx, 0, sizeof(TZ_HDCP2_CTX));
	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_SET_PAIRING_INFO_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size)
 * @brief This function receives the pairing info from receiver
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing the HDCP2_PAIRING_INFO message
 * @param req_size - size of request
 * @param response- pointer to response
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_SET_PAIRING_INFO_T(const u8 command, u8 *request, const u32 req_size,
			u8 *response, u32 *res_size)
{
	TEE_Result TEE_ret;
	HDCP2_PAIRING_INFO *pair_info = (HDCP2_PAIRING_INFO *) request;
	uint32_t length = sizeof(HDCP2_PAIRING_INFO) - sizeof(pair_info->padding);
	uint8_t *unwrap = NULL;
	NULL_ERR(pair_info, HDCP2_TZ_NULL_REQUEST);

#ifdef TEEGRIS_V4
	SO_AccessControlInfoType ac_info;
	memset(&ac_info, 0, sizeof ac_info);
	ac_info.access_flags = TA_ID_AC;

	TEEC_UUID uuid = (TEEC_UUID)TA_PROP_UUID;
	memcpy(&ac_info.ta_id, &uuid, sizeof(TEEC_UUID));

	TEE_ret = TEES_CheckSecureObjectCreator((const u8*)pair_info, sizeof(HDCP2_PAIRING_INFO), &ac_info);
	if (TEE_ret != TEE_SUCCESS) {
		LOGE("TZ_AKE_SET_PAIRING_INFO_T: Check SecureObject Creator failed with [TEE_ret: %x]\n", TEE_ret);
		goto err;
	}
#endif

	unwrap = TEE_Malloc(length, 0);
	if (unwrap == NULL) {
		LOGE("TZ_AKE_Send_Pairing_Info_T: Error, TEE malloc failed\n");
		return HDCP2_ERR;
	}

	// decrypt pairing info
	TEE_ret = TEES_UnwrapSecureObject((const u8 *) pair_info, sizeof(HDCP2_PAIRING_INFO), unwrap, &length);
	if (TEE_ret != TEE_SUCCESS) {
		LOGE("TZ_AKE_SET_PAIRING_INFO_T: Error, readObjectDataFromTeeStore/TEES_UnwrapSecureObject result = %x\n", TEE_ret);
		goto err;
	}

	TEE_MemMove((u8 *) &hdcp2_ctx.pairing_info, unwrap, length);
	hdcp2_ctx.is_paired = 1;

	TZ_LOG_HEX("Pairing Info", (u8 *) &hdcp2_ctx.pairing_info, length);

err:
	if (unwrap != NULL)
		TEE_Free(unwrap);

	return TEE_ret;
}

/**
 * @fn int TZ_HDCP2_LOADKEY_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function loads the key provisioning key
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_HDCP2_LOADKEY_T(const uint8_t command, uint8_t *request,
			const u32 req_size, uint8_t *response, uint32_t *res_size)
{
	return TZ_HDCP2_LOADKEY(request, req_size, &hdcp2_key);
}

/**
 * @fn int TZ_SET_HDCP_VERSION_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function sets the hdcp version.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_SET_HDCP_VERSION_T(const u8 command, u8 *request, u32 req_size,
			u8 *response, u32 *res_size)
{
	NULL_ERR(request, HDCP2_TZ_NULL_REQUEST);
	if (req_size != 2 /* the buffer size for hdcp_version */) {
		LOGE("TZ_SET_HDCP_VERSION_T: Error, Invalid Req size (%d)\n", req_size);
		return HDCP2_ERR_INVALID_INPUT;
	}

	hdcp2_ctx.version = request[1];
	LOGD("TZ_SET_HDCP_VERSION_T : HDCP %d.%d version is setuped\n",
			hdcp2_ctx.version / 10, hdcp2_ctx.version % 10);

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_Init_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function initiates the authentication from transmitter by sending the initiation message which contains 64 bit pseudo random value
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing AKE_INIT message
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_Init_T(const u8 command, u8 *request, const u32 req_size,
			u8 *response, u32 *res_size)
{
	if (*res_size != sizeof(AKE_INIT)) {
		LOGE("Error,AKE_INIT data size (%d)\n", *res_size);
		return HDCP2_ERR;
	}
	AKE_INIT *tx_payload = (AKE_INIT *) response;

	NULL_ERR(tx_payload, HDCP2_TZ_NULL_RESPONSE);

	if (TZ_HW_Get_Oem_Flag() != HDCP2_OK) {
		LOGE("Integrity check fail\n");
		return HDCP2_ERR_INTEGRITY;
	}

	TZ_AKE_Reset_T();
	tx_payload->msg_id = command;

	// generate a random
	if (TZ_rand(hdcp2_ctx.rtx, sizeof(hdcp2_ctx.rtx)) != sizeof(hdcp2_ctx.rtx))
		return HDCP2_ERR_CRYPTO;

	// make payload
	TEE_MemMove(tx_payload->rtx, hdcp2_ctx.rtx, sizeof(hdcp2_ctx.rtx));

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_Transmitter_Info_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function sends the transmitter info to the receiver
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing AKE_TRANSMITTER_INFO message
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_Transmitter_Info_T(const u8 command, u8 *request, const u32 req_size,
			u8 *response, u32 *res_size)
{
	if (*res_size != sizeof(AKE_TRANSMITTER_INFO)) {
		LOGE("Error,AKE_TRANSMITTER_INFO data size (%d)\n", *res_size);
		return HDCP2_ERR;
	}
	AKE_TRANSMITTER_INFO *tx_payload = (AKE_TRANSMITTER_INFO *) response;
	u8 disable_precompute = 0;

	NULL_ERR(request, HDCP2_TZ_NULL_REQUEST);

	if (req_size != 2) {	// request buffer size should be 'sizeof(u8) * 2'
		LOGE("Error, Precompute_check data size (%d)\n", req_size);
		return HDCP2_ERR;
	}

	disable_precompute = request[1];
	NULL_ERR(tx_payload, HDCP2_TZ_NULL_RESPONSE);

	tx_payload->msg_id = command;
	// length is always six bytes
	tx_payload->LENGTH[0] = 0x00;
	tx_payload->LENGTH[1] = 0x06;
	// transmitter's version is 0x01, pp.63
	tx_payload->VERSION = 0x01;
	tx_payload->TRANSMITTER_CAPABILITY_MASK[0] = 0;
	tx_payload->TRANSMITTER_CAPABILITY_MASK[1] = 0x00;

	/* TRANSMITTER_CAPABILITY_MASK[1] is 0x01 enabling precomputation of L
		  TRANSMITTER_LOCALITY_PRECOMPUTE_SUPPORT is bit0 set to value 1*/
	if (disable_precompute == 0) {
		tx_payload->TRANSMITTER_CAPABILITY_MASK[1] |= LOCALITY_PRECOMPUTE_SUPPORT;
	}

	if (hdcp2_ctx.version >= HDCP2_VERSION_2_2) {
		tx_payload->VERSION = hdcp2_ctx.version - HDCP2_VERSION_2_0;
		/* Set content stream bit only if precompute, as Rx may not handle precompute and content stream bit properly.
		 * This check is for a safer side */
		//For HDcp2.2 Tx a new bit TRANSMITTER_CONTENT_CATEGORY_SUPPORT is introduced at bit1 set to value 1
		tx_payload->TRANSMITTER_CAPABILITY_MASK[1] |= CONTENT_CATEGORY_SUPPORT;

		hdcp2_ctx.transmitter_info.TRANSMITTER_CAPABILITY_MASK[0] = tx_payload->TRANSMITTER_CAPABILITY_MASK[0];
		hdcp2_ctx.transmitter_info.TRANSMITTER_CAPABILITY_MASK[1] = tx_payload->TRANSMITTER_CAPABILITY_MASK[1];
	}

	//TRANSMITTER_LOCALITY_PRECOMPUTE_SUPPORT is bit0 of TRANSMITTER_CAPABILITY_MASK
	hdcp2_ctx.Tx_LC_Precompute = tx_payload->TRANSMITTER_CAPABILITY_MASK[1] & LOCALITY_PRECOMPUTE_SUPPORT;

	hdcp2_ctx.transmitter_info.VERSION = tx_payload->VERSION;
	LOGI("TZ_AKE_Transmitter_Info_T : transmitter_info.VERSION : 0x%02x\n", hdcp2_ctx.transmitter_info.VERSION);

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_Send_Cert_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief A certificate is  received from the receiver which indicates whether the connected receiver is an HDCP receiver and also extracts Receiver ID
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing AKE_SEND_CERT message
 * @param req_size - size of request
 * @param response- pointer to response
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_Send_Cert_T(const u8 command, u8 *request, const u32 req_size,
			u8 *response, u32 *res_size)
{
	if (req_size != sizeof(AKE_SEND_CERT)) {
		LOGE("Error, AKE CERT data size (%d)\n", req_size);
		return HDCP2_ERR;
	}

 	if(!TZ_HDCP2_Get_Status_UnwrapKey()) {
		LOGE("TZ_AKE_Send_Cert_T: TZ_HDCP2_Get_Status_UnwrapKey return failed");
		return HDCP2_ERR_UNWRAPKEY;
	}
	AKE_SEND_CERT *cert = (AKE_SEND_CERT *) request;

	NULL_ERR(cert, HDCP2_TZ_NULL_REQUEST);
	TEE_MemMove(&hdcp2_key.cert, cert->certrx, sizeof(HDCP2_RECEIVER_CERTIFICATE));
	hdcp2_ctx.REPEATER = cert->REPEATER;

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_Receiver_Info_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function gets the information regarding compliance of receiver with various hdcp versions
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing AKE_RECEIVER_INFO message
 * @param req_size - size of request
 * @param response- pointer to response
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_Receiver_Info_T(const u8 command, u8 *request, const u32 req_size,
			u8 *response, u32 *res_size)
{
	if (req_size != sizeof(AKE_RECEIVER_INFO)) {
		LOGE("Error, AKE_RECEIVER_INFO data size (%d)\n", req_size);
		return HDCP2_ERR;
	}
	AKE_RECEIVER_INFO *receiver_info = (AKE_RECEIVER_INFO *) request;

	NULL_ERR(receiver_info, HDCP2_TZ_NULL_REQUEST);

	//updating the hdcp transmitter version as per the receiver Info version.
	if (HDCP2_VERSION_2_0 + receiver_info->VERSION < hdcp2_ctx.version) {
		hdcp2_ctx.version = HDCP2_VERSION_2_0 + receiver_info->VERSION;
		LOGI("TZ_AKE_Receiver_Info_T : HDCP version is changed to %d.%d\n",
				(hdcp2_ctx.version / 10), (hdcp2_ctx.version % 10));
	}
	TEE_MemMove(&hdcp2_ctx.receiver_info, receiver_info, sizeof(AKE_RECEIVER_INFO));

	hdcp2_ctx.Rx_LC_Precompute = receiver_info->RECEIVER_CAPABILITY_MASK[1] & LOCALITY_PRECOMPUTE_SUPPORT;

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_No_Store_km_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function is called when transmitter does not have 128 bit master key stored corresponding to the receiver ID.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing AKE_NO_STORED_KM message
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_No_Store_km_T(const u8 command, u8 *request, const u32 req_size,
			u8 *response, u32 *res_size)
{
	TEE_Attribute attr_salt;
	uint8_t szoutdataHASH[HDCP2_MESSAGE_DIGEST_SIZE] = {0, };
	uint8_t dcp_llc_sign[SIZE_OF_DCPLLC_SIGNATURE] ;

	uint8_t recv_Id[SIZE_OF_PUBLIC_KEY_CERTIFICATE_OF_HDCP_RECEIVER - SIZE_OF_DCPLLC_SIGNATURE] ;
	int ret_val=0;
	TEE_Result result;
	TEE_OperationHandle operSigVerify = NULL;
	TEE_ObjectHandle objDcpPubKey = NULL;
	TEE_Attribute attrsDcpPubKey[2];

	if (*res_size != sizeof(AKE_NO_STORED_KM)) {
		LOGE("Error, AKE_NO_STORED_KM data size (%d)\n", *res_size);
		return HDCP2_ERR;
	}

 	if(!TZ_HDCP2_Get_Status_UnwrapKey()) {
		LOGE("TZ_AKE_No_Store_km_T: TZ_HDCP2_Get_Status_UnwrapKey return failed");
		return HDCP2_ERR_UNWRAPKEY;
	}
	AKE_NO_STORED_KM *no_stored_km = (AKE_NO_STORED_KM *) response;
	TEE_MemFill(dcp_llc_sign,0,SIZE_OF_DCPLLC_SIGNATURE);
	TEE_MemFill(recv_Id,0,(SIZE_OF_PUBLIC_KEY_CERTIFICATE_OF_HDCP_RECEIVER - SIZE_OF_DCPLLC_SIGNATURE));

	LOGD("TZ_AKE_No_Store_km_T: Info, Cert Verification using Test Key\n");

	if (TEE_MemCompare(hdcp2_key.cert.receiver_id, test_certid_r1, 5) == 0
		|| TEE_MemCompare(hdcp2_key.cert.receiver_id, test_certid_r2, 5) == 0) {
		LOGE("TZ_AKE_No_Store_km_T: Info, Cert Verification using Test Key\n");
		TEE_MemMove(mod_buf, mod_buf_test, SIZE_OF_DCPLLC_SIGNATURE);
	} else {
		LOGE("TZ_AKE_No_Store_km_T: Info, Cert Verification using  Production key......\n");
	}

	*res_size = sizeof(AKE_NO_STORED_KM);
	NULL_ERR(no_stored_km, HDCP2_TZ_NULL_RESPONSE);

	TEE_MemMove(dcp_llc_sign, hdcp2_key.cert.dcp_llc_sign, SIZE_OF_DCPLLC_SIGNATURE);
	TEE_MemMove(recv_Id, (const uint8_t *)&hdcp2_key.cert, SIZE_OF_PUBLIC_KEY_CERTIFICATE_OF_HDCP_RECEIVER - SIZE_OF_DCPLLC_SIGNATURE);

	ret_val = TZ_SHA256(szoutdataHASH, recv_Id, sizeof(recv_Id));
	if (ret_val != HDCP2_MESSAGE_DIGEST_SIZE) {
		LOGE("TZ_AKE_No_Store_km_T: Error, DCP:TZ_SHA256 failed: = %d\n", ret_val);
		ret_val = HDCP2_ERR_DCP_VERIFICATION;
		goto cleanup;
	} else {
		ret_val = HDCP2_OK;
	}
	result = TEE_AllocateOperation(&operSigVerify, TEE_ALG_RSASSA_PKCS1_V1_5_SHA256, TEE_MODE_VERIFY, 3072/*SIZE_OF_DCP_PUBLICKEY_MODULUS+SIZE_OF_DCP_PUBLICKEY_EXPONENT*/);
	if ( result != TEE_SUCCESS ) {
		LOGE("TZ_AKE_No_Store_km_T: Error, TEE_AllocateOperation<SigVerify>, result: %u\n", result);
		goto cleanup;
	}

	// Allocate Transient Object, create object attributes with KPub<dcp> key Of LLC.
	result = TEE_AllocateTransientObject(TEE_TYPE_RSA_PUBLIC_KEY,3072 /* (SIZE_OF_DCP_PUBLICKEY_MODULUS + SIZE_OF_DCP_PUBLICKEY_EXPONENT)*/, &objDcpPubKey);
	if ( result != TEE_SUCCESS ) {
		LOGE("TZ_AKE_No_Store_km_T: Error, TEE_AllocateTransientObject<objDcpPubKey>, result: %u\n", result);
		goto cleanup;
	}

	// Initialize all those Attributes required to populate Transient Object
	TEE_InitRefAttribute(&attrsDcpPubKey[0], TEE_ATTR_RSA_MODULUS, mod_buf, SIZE_OF_DCP_PUBLICKEY_MODULUS);
	TEE_InitRefAttribute(&attrsDcpPubKey[1], TEE_ATTR_RSA_PUBLIC_EXPONENT, exp_buf, SIZE_OF_DCP_PUBLICKEY_EXPONENT);

	result = TEE_PopulateTransientObject(objDcpPubKey, attrsDcpPubKey,2);
	if (result != TEE_SUCCESS) {
		LOGE("TZ_AKE_No_Store_km_T: Error, Failed to Populate Transient Object - objDcpPubKey, result: %u\n", result);
		goto cleanup;
	}

	// Set Operation Key with KPub<dcp> key Of LLC
	result = TEE_SetOperationKey(operSigVerify, objDcpPubKey);
	if (result != TEE_SUCCESS) {
		LOGE("TZ_AKE_No_Store_km_T: Error, Failed to Set Operation Key, result: %u\n", result);
		goto cleanup;
	}

	result = TEE_AsymmetricVerifyDigest(operSigVerify, &attr_salt, 0, szoutdataHASH, HDCP2_MESSAGE_DIGEST_SIZE, dcp_llc_sign, SIZE_OF_DCPLLC_SIGNATURE);
	if (result != TEE_SUCCESS) {
		LOGE("TZ_AKE_No_Store_km_T: Error, Failed to Verify DCP LLC Signature..., result: %x\n", result);
		ret_val = HDCP2_ERR_DCP_VERIFICATION;
		goto cleanup;
	}

#ifndef DEBUG
	LOGI("TZ_AKE_No_Store_km_T: Success, TEE_AsymmetricVerifyDigest<DCPSignVerify>: %u\n", result);
#endif /* DEBUG */

cleanup:
	TEE_FreeOperation(operSigVerify);
	TEE_CloseObject(objDcpPubKey);

	if (ret_val != HDCP2_OK)
		return ret_val;

	// 1. randomly choose hdcp2_ctx->km
	TZ_rand((u8 *) hdcp2_ctx.pairing_info.km, sizeof(hdcp2_ctx.pairing_info.km));
	TZ_LOG_HEX("km", hdcp2_ctx.pairing_info.km, sizeof(hdcp2_ctx.pairing_info.km));

	// 2. Encrypt hdcp2_ctx->km using receiver's public key
	no_stored_km->msg_id = command;

	if (TZ_RSA_OAEP_encrypt(&hdcp2_ctx, &hdcp2_key, hdcp2_ctx.pairing_info.km,
			no_stored_km->Ekpub_km) != sizeof(no_stored_km->Ekpub_km))
		return HDCP2_ERR_OAEP_ENCRYPTION;

	TZ_LOG_HEX("Ekm", no_stored_km->Ekpub_km, sizeof(no_stored_km->Ekpub_km));

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_Store_km_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function is called when transmitter has 128 bit master key corresponding to the receiver ID.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing AKE_STORED_KM message
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_Store_km_T(const u8 command, u8 *request, const u32 req_size,
			u8 *response, u32 *res_size)
{
	if (*res_size != sizeof(AKE_STORED_KM)) {
		LOGE("Error, AKE_STORED_KM data size (%d)\n", *res_size);
		return HDCP2_ERR;
	}
	AKE_STORED_KM *stored_km = (AKE_STORED_KM *) response;

	NULL_ERR(stored_km, HDCP2_TZ_NULL_RESPONSE);

	if (!hdcp2_ctx.is_paired)
		return HDCP2_ERR_STORED_KM_NOT_PAIRED;

	stored_km->msg_id = command;
	TEE_MemMove(stored_km->m, hdcp2_ctx.pairing_info.m, sizeof(stored_km->m));
	TEE_MemMove(stored_km->Ekh_km, hdcp2_ctx.pairing_info.Ekm, sizeof(stored_km->Ekh_km));

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_Send_rrx_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function receives AKE_Send_rrx message from receiver containing the 64 bit pseudo random value
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing AKE_SEND_RRX message
 * @param req_size - size of request
 * @param response- pointer to response
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_Send_rrx_T(const u8 command, u8 *request, const u32 req_size,
			u8 *response, u32 *res_size)
{
	if (req_size != sizeof(AKE_SEND_RRX)) {
		LOGE("Error, AKE_SEND_RRX data size (%d)\n", req_size);
		return HDCP2_ERR;
	}
	AKE_SEND_RRX *rrx = (AKE_SEND_RRX *) request;

	NULL_ERR(rrx, HDCP2_TZ_NULL_REQUEST);
	TEE_MemMove(hdcp2_ctx.rrx, rrx->rrx, sizeof(rrx->rrx));

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_Send_h_prime_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function receives AKE_SEND_H_PRIME message from the receiver which contains 256-bit H-prime. This message has to be received within 200ms in case of stored_km and within 1s in case of no_stored_km.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing AKE_SEND_H_PRIME message
 * @param req_size - size of request
 * @param response- pointer to response
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_Send_h_prime_T(const u8 command, u8 *request, const u32 req_size,
			u8 *response, u32 *res_size)
{
	u8 H[32] = {0, };
	u8 input[8] = {0, };

	if (req_size != sizeof(AKE_SEND_H_PRIME)) {
		LOGE("Error, AKE_SEND_H_PRIME data size (%d)\n", req_size);
		return HDCP2_ERR;
	}

 	if(!TZ_HDCP2_Get_Status_UnwrapKey()) {
		LOGE("TZ_AKE_Send_h_prime_T: TZ_HDCP2_Get_Status_UnwrapKey return failed");
		return HDCP2_ERR_UNWRAPKEY;
	}
	AKE_SEND_H_PRIME *h_prime = (AKE_SEND_H_PRIME *) request;

	NULL_ERR(h_prime, HDCP2_TZ_NULL_REQUEST);

	// Derivate kd = dkey0 || dkey1
	TZ_Derivate_dkey(&hdcp2_ctx);
	TEE_MemMove(hdcp2_ctx.kd, hdcp2_ctx.dkey, 16);

	TZ_Derivate_dkey(&hdcp2_ctx);
	TEE_MemMove(hdcp2_ctx.kd + 16, hdcp2_ctx.dkey, 16);

	if (hdcp2_key.cert.Protocol_Descriptor == 0x01 && hdcp2_ctx.version >= HDCP2_VERSION_2_2) {
		u8 temp_input[14];
		TEE_MemFill(temp_input, 0, sizeof(temp_input));
		TEE_MemMove(temp_input, hdcp2_ctx.rtx, sizeof(hdcp2_ctx.rtx));
		temp_input[7] ^= hdcp2_ctx.REPEATER;
		temp_input[8] = hdcp2_ctx.receiver_info.VERSION;
		TEE_MemMove(temp_input + 9, hdcp2_ctx.receiver_info.RECEIVER_CAPABILITY_MASK,
				sizeof(hdcp2_ctx.receiver_info.RECEIVER_CAPABILITY_MASK));
		temp_input[11] = hdcp2_ctx.transmitter_info.VERSION;
		TEE_MemMove(temp_input + 12, hdcp2_ctx.transmitter_info.TRANSMITTER_CAPABILITY_MASK,
				sizeof(hdcp2_ctx.transmitter_info.TRANSMITTER_CAPABILITY_MASK));

		//Compute H
		// HMAC-SHA256(rtx XOR REPEATER || receiver_info.VERSION || receiver_info.RECEIVER_CAPABILITY_MASK ||
		// transmitter_info.VERSION || transmitter_info.TRANSMITTER_CAPABILITY_MASK, kd)
		TZ_HMAC_SHA256(H, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd), temp_input, sizeof(temp_input));
		TZ_LOG_HEX("H", H, 32);
		TZ_LOG_HEX("h_prime", h_prime->H, 32);
	} else {
		TEE_MemMove(input, hdcp2_ctx.rtx, sizeof(hdcp2_ctx.rtx));

		input[7] ^= hdcp2_ctx.REPEATER;
		TZ_LOG_HEX("input", input, 8);
		TZ_LOG_HEX("rn", hdcp2_ctx.rn, sizeof(hdcp2_ctx.rn));
		TZ_LOG_HEX("km", hdcp2_ctx.pairing_info.km, sizeof(hdcp2_ctx.pairing_info.km));
		TZ_LOG_HEX("kd", hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd));

		// HMAC-SHA256(rtx XOR REPEATER, kd)
		TZ_HMAC_SHA256(H, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd), input, sizeof(input));

		TZ_LOG_HEX("H", H, 32);
		TZ_LOG_HEX("h_prime", h_prime->H, 32);
	}

	if (TEE_MemCompare(h_prime->H, H, sizeof(H)))
		return HDCP2_ERR_INVALID_H;

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_Send_Pairing_Info_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function receives AKE_SEND_PAIRING_INFO message from receiver which contains 128 bit Ekh(km).
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing AKE_SEND_PAIRING_INFO message
 * @param req_size - size of request
 * @param response- pointer to response containing HDCP2_PAIRING_INFO message
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_Send_Pairing_Info_T(const u8 command, u8 *request,
			const u32 req_size, u8 *response, u32 *res_size)
{
	NULL_ERR(request, HDCP2_TZ_NULL_REQUEST);
	NULL_ERR(response, HDCP2_TZ_NULL_RESPONSE);
	NULL_ERR(res_size, HDCP2_ERR);

	if (*res_size != sizeof(HDCP2_PAIRING_INFO) || req_size != sizeof(AKE_SEND_PAIRING_INFO)) {
		LOGE("Error, AKE pairing data size (%d), (%d)\n", req_size, *res_size);
		return HDCP2_ERR;
	}
 	if(!TZ_HDCP2_Get_Status_UnwrapKey()) {
		LOGE("TZ_AKE_Send_Pairing_Info_T: TZ_HDCP2_Get_Status_UnwrapKey return failed");
		return HDCP2_ERR_UNWRAPKEY;
	}
	
	AKE_SEND_PAIRING_INFO *pairing_info = (AKE_SEND_PAIRING_INFO *) request;
	HDCP2_PAIRING_INFO *p;
	uint8_t *wrap = NULL;
	TEE_Result TEE_ret;

	SO_AccessControlInfoType ac_info;
	TEE_MemFill(&ac_info, 0, sizeof(ac_info));

#ifdef TEEGRIS_V4
	ac_info.access_flags = TA_ID_AC;
#else
	ac_info.access_flags = TA_ID_AC | AUTH_ID_AC;
#endif
	p = TEE_Malloc(*res_size, 0);

	// copy pairing info
	TEE_MemMove(p->Ekm, pairing_info->Ekh_Km, sizeof(pairing_info->Ekh_Km));
	TEE_MemMove(p->km, hdcp2_ctx.pairing_info.km, sizeof(hdcp2_ctx.pairing_info.km));
	if (hdcp2_ctx.version >= HDCP2_VERSION_2_2) {
		//Spec HDCP2.2 page:20
		//TBD:From the spec, if the receiver is 2.0 complaint(i.e.,receiverInfo version=0x01
		//Transmitter must not store pairing info m, km, ekm and receiver ID corresponding to the receiver.
		if (hdcp2_ctx.transmitter_info.VERSION != 0x01) {
			TEE_MemMove(p->m, hdcp2_ctx.rtx, sizeof(hdcp2_ctx.rtx));
			TEE_MemMove(p->m + sizeof(hdcp2_ctx.rtx), hdcp2_ctx.rrx, sizeof(hdcp2_ctx.rrx));
			TZ_LOG_HEX("Master Key", p->m, sizeof(p->m));
		}
	} else {
		TEE_MemMove(p->m, hdcp2_ctx.rtx, sizeof(hdcp2_ctx.rtx)); //as spec 2.1
	}

	TEE_MemMove(p->receiver_id, hdcp2_key.cert.receiver_id, sizeof(hdcp2_key.cert.receiver_id));

	// encrypt pairing info
	TZ_LOG_HEX("Pairing Info", response, *res_size);

	wrap = TEE_Malloc(*res_size, 0);
	if (wrap == NULL) {
		LOGE("TZ_AKE_Send_Pairing_Info_T: Error, TEE malloc failed\n");
		return HDCP2_ERR;
	}

	TEE_ret = TEES_WrapSecureObject(p, *res_size - sizeof(p->padding), wrap, res_size, &ac_info);
	if (TEE_ret != TEE_SUCCESS) {
		LOGE("TZ_AKE_Send_Pairing_Info_T: Error, TEE_pWrapSO result = %u\n", TEE_ret);
		goto err;
	}

	TEE_MemMove(response, wrap, *res_size);
	TZ_LOG_HEX("Encrypted Pairing Info", response, *res_size);
	hdcp2_ctx.is_paired = 1;

err:
	if (wrap != NULL)
		TEE_Free(wrap);

	return TEE_ret;
}

/**
 * @fn int TZ_LC_Init_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function initiates locality check by sending a message containing 64 bit pseudo random number to hdcp receiver
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing LC_INIT message
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_LC_Init_T(const u8 command, u8 *request, const u32 req_size,
			u8 *response, u32 *res_size)
{
	int i=0;
	u8 key[32] = {0, };
	u8 L[32] = {0, };

	if (*res_size != sizeof(LC_INIT)) {
		LOGE("Error, LC_INIT data size (%d)\n", *res_size);
		return HDCP2_ERR;
	}
	LC_INIT *lc_init = (LC_INIT *) response;

	NULL_ERR(lc_init, HDCP2_TZ_NULL_RESPONSE);

	// 1. randomly choose hdcp2_ctx->rn
	TZ_rand(hdcp2_ctx.rn, sizeof(hdcp2_ctx.rn));

	// 2. Copy to payload
	lc_init->msg_id = command;
	TEE_MemMove(lc_init->rn, hdcp2_ctx.rn, sizeof(hdcp2_ctx.rn));

	if (hdcp2_ctx.Tx_LC_Precompute && hdcp2_ctx.Rx_LC_Precompute) {
		// key = kd ^ rrx
		TEE_MemMove(key, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd));
		for (i = 24; i < 32; i++)
			key[i] ^= hdcp2_ctx.rrx[i - 24];

		if (hdcp2_ctx.version >= HDCP2_VERSION_2_2 && hdcp2_ctx.transmitter_info.VERSION != 0x01) {
			u8 temp_rn[16] = {0, };
			TEE_MemMove(temp_rn, hdcp2_ctx.rn, sizeof(hdcp2_ctx.rn));
			TEE_MemMove(temp_rn+sizeof(hdcp2_ctx.rn), hdcp2_ctx.rn, sizeof(hdcp2_ctx.rn));
			//HMAC-SHA256(rn || rn, kd XOR rrx)
			TZ_HMAC_SHA256(L, key, sizeof(key), temp_rn, sizeof(temp_rn));
		} else {
			TZ_HMAC_SHA256(L, key, sizeof(key), hdcp2_ctx.rn, sizeof(hdcp2_ctx.rn));
		}

		TEE_MemMove(hdcp2_ctx.L_msb, L, 16);
		TEE_MemMove(hdcp2_ctx.L_lsb, L + 16, 16);
		TZ_LOG_HEX("hdcp2_ctx.L_msb Tx", hdcp2_ctx.L_msb, sizeof(hdcp2_ctx.L_msb));
		TZ_LOG_HEX("hdcp2_ctx.L_lsb Tx", hdcp2_ctx.L_lsb, sizeof(hdcp2_ctx.L_lsb));
	}

	return HDCP2_OK;
}

/**
 * @fn int TZ_RTT_Challenge_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function sends the message containing the least significant 128 bits of L to the receiver
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing RTT_CHALLENGE message
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_RTT_Challenge_T(const uint8_t command, uint8_t *request,
			const u32 req_size, uint8_t *response, uint32_t *res_size)
{
	if (*res_size != sizeof(RTT_CHALLENGE)) {
		LOGE("Error, RTT_CHALLENGE data size (%d)\n", *res_size);
		return HDCP2_ERR;
	}
	RTT_CHALLENGE *rtt = (RTT_CHALLENGE *) response;

	NULL_ERR(rtt, HDCP2_TZ_NULL_RESPONSE);

	rtt->msg_id = command;
	TEE_MemMove(rtt->L_lsb, hdcp2_ctx.L_lsb, sizeof(hdcp2_ctx.L_lsb));

	return HDCP2_OK;
}

/**
 * @fn int TZ_LC_Send_L_prime_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function receives a message containing most significant bits of L- prime from receiver. This is compared with L. The time elapsed between sending RTT_Challenge_T and receiving L-prime must be less than 7ms.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing LC_SEND_L_PRIME_PC message
 * @param req_size - size of request
 * @param response- pointer to response
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_LC_Send_L_prime_T(const u8 command, u8 *request, const u32 req_size,
			u8 *response, u32 *res_size)
{
	u8 key[32] = {0, };
	u8 L[32] = {0, };

	if (hdcp2_ctx.Tx_LC_Precompute && hdcp2_ctx.Rx_LC_Precompute) {
		if (req_size != sizeof(LC_SEND_L_PRIME_PC)) {
			LOGE("Error, LC_SEND_L_PRIME_PC data size (%d)\n", req_size);
			return HDCP2_ERR;
		}
		LC_SEND_L_PRIME_PC *l_prime = (LC_SEND_L_PRIME_PC *) request;
		NULL_ERR(request, HDCP2_TZ_NULL_REQUEST);

		if (TEE_MemCompare(l_prime->L_msb, hdcp2_ctx.L_msb, sizeof(hdcp2_ctx.L_msb)))
			return HDCP2_ERR_INVALID_L;
	} else {
		if (req_size != sizeof(LC_SEND_L_PRIME)) {
			LOGE("Error, LC_SEND_L_PRIME data size (%d)\n", req_size);
			return HDCP2_ERR;
		}
		int i = 0;
		LC_SEND_L_PRIME *l_prime = (LC_SEND_L_PRIME *) request;

		NULL_ERR(request, HDCP2_TZ_NULL_REQUEST);

		// key = kd ^ rrx
		TEE_MemMove(key, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd));
		for (i = 24; i < 32; i++)
			key[i] ^= hdcp2_ctx.rrx[i - 24];

		TZ_HMAC_SHA256(L, key, sizeof(key), hdcp2_ctx.rn, sizeof(hdcp2_ctx.rn));

		if (TEE_MemCompare(l_prime->L, L, sizeof(L)))
			return HDCP2_ERR_INVALID_L;
	}

	return HDCP2_OK;
}

/**
 * @fn int TZ_SKE_Send_Eks_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function sends a message containing Edkey(ks) and a 64 bit pseudo random number to the hdcp receiver
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing SKE_SEND_EKS message
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_SKE_Send_Eks_T(const u8 command, u8 *request, const u32 req_size,
			u8 *response, u32 *res_size)
{
	int ret = 0;
	int i = 0;

#ifdef USE_WFD_TS_MUX_HW
	uint32_t sessionKey[8] = {0, };
	uint32_t riv[2] = {0, };

	struct secHDCPKeyInfo_t secHDCPKey;
	TEE_MemFill(&secHDCPKey, 0, sizeof(secHDCPKey));
	//memset(&secHDCPKey, 0, sizeof(secHDCPKeyInfo_t));
#endif /* USE_WFD_TS_MUX_HW */

	NULL_ERR(response, HDCP2_TZ_NULL_RESPONSE);
 	if(!TZ_HDCP2_Get_Status_UnwrapKey()) {
		LOGE("TZ_SKE_Send_Eks_T: TZ_HDCP2_Get_Status_UnwrapKey return failed");
		return HDCP2_ERR_UNWRAPKEY;
	}

	// 1. Generate random ks and riv
	TZ_rand(hdcp2_ctx.ks, sizeof(hdcp2_ctx.ks));
	TZ_rand(hdcp2_ctx.riv, sizeof(hdcp2_ctx.riv));

	if (hdcp2_ctx.version == HDCP2_VERSION_2_3) {
		if (*res_size != sizeof(SKE_SEND_EKS_VER23)) {
			LOGE("Error, SKE_SEND_EKS_VER23[%d] != res_size[%d]\n", sizeof(SKE_SEND_EKS_VER23), *res_size);
			return HDCP2_ERR;
		}
		SKE_SEND_EKS_VER23 *eks = (SKE_SEND_EKS_VER23 *) response;

		// HDCP v2.3: Calculate HMAC of riv
		TZ_HMAC_SHA256(eks->H, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd), hdcp2_ctx.riv,
				sizeof(hdcp2_ctx.riv));
		TZ_LOG_HEX("eks->H", eks->H, sizeof(eks->H));

		// 2. Key Derivation (dkey2 <= ctr=2)
		TZ_Derivate_dkey(&hdcp2_ctx);

		// 3. Eks = ks ^ dkey2 ^ rrx[8-15]
		for (i = 0; i < 16; i++) {
			eks->Eks[i] = (i < 8) ? hdcp2_ctx.ks[i] ^ hdcp2_ctx.dkey[i] :
							hdcp2_ctx.ks[i] ^ hdcp2_ctx.dkey[i] ^ hdcp2_ctx.rrx[i - 8];
		}

		TZ_LOG_HEX("eks->Eks", eks->Eks, sizeof(eks->Eks));

		// 4. Make payload
		eks->msg_id = command;
		TEE_MemMove(eks->riv, hdcp2_ctx.riv, sizeof(hdcp2_ctx.riv));
	} else {
		if (*res_size != sizeof(SKE_SEND_EKS)) {
			LOGE("Error, SKE_SEND_EKS[%d] != res_size[%d]\n", sizeof(SKE_SEND_EKS), *res_size);
			return HDCP2_ERR;
		}
		SKE_SEND_EKS *eks = (SKE_SEND_EKS *) response;

		// 2. Key Derivation (dkey2 <= ctr=2)
		TZ_Derivate_dkey(&hdcp2_ctx);

		// 3. Eks = ks ^ dkey2 ^ rrx[8-15]
		for (i = 0; i < 16; i++) {
			eks->Eks[i] = (i < 8) ? hdcp2_ctx.ks[i] ^ hdcp2_ctx.dkey[i] :
							hdcp2_ctx.ks[i] ^ hdcp2_ctx.dkey[i] ^ hdcp2_ctx.rrx[i - 8];
		}

		TZ_LOG_HEX("eks->Eks", eks->Eks, sizeof(eks->Eks));

		// 4. Make payload
		eks->msg_id = command;
		TEE_MemMove(eks->riv, hdcp2_ctx.riv, sizeof(hdcp2_ctx.riv));
	}

	TZ_LOG_HEX("ks", hdcp2_ctx.ks, sizeof(hdcp2_ctx.ks));
#ifdef USE_WFD_TS_MUX_HW
	memcpy((void *)sessionKey, (void *)hdcp2_ctx.ks, 16);
	memcpy((void *)riv, (void *)hdcp2_ctx.riv, 8);

	secHDCPKey.session_key = sessionKey;
	secHDCPKey.riv = riv;

	TZ_LOG_HEX("secHDCPKey.session_key", (uint8_t *)secHDCPKey.session_key, 16);
	TZ_LOG_HEX("secHDCPKey.riv", (uint8_t *)secHDCPKey.riv, 8);

	unsigned int result = 0x0;
	ret = TEES_HDCP_SetKeyInfo(&secHDCPKey, &result);
	if (ret != TEE_SUCCESS) {
		LOGE("ERROR : TEES_SECCAM_IsProtected = %#x (0x%x)\n", ret, result);
		return ret;
	}
#endif /* USE_WFD_TS_MUX_HW */

	// 5. Set content key
	if ((ret = TZ_Get_ContentKey(hdcp2_ctx.ks, hdcp2_key.lc128, hdcp2_key.ckey)) < 0) {
		return ret;
	}

#ifdef USE_SET_DRM_FLAG
	if (TZ_Set_HDCP2Flag(true) != HDCP2_OK) {
		LOGE("Fail setting HDCP2 flag\n");
		return HDCP2_ERR;
	}
#endif /* USE_SET_DRM_FLAG */

	return HDCP2_OK;
}

/**
 * @fn int TZ_RepeaterAuth_Send_ReceiverId_List_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function gets the message containing the information regarding the receivers connected to the repeater
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_RepeaterAuth_Send_ReceiverId_List_T(const u8 command, u8 *request,
			const u32 req_size, u8 *response, u32 *res_size)
{
	int ret=0;

	// added verify for 2.2 ver also
	if (hdcp2_ctx.version >= HDCP2_VERSION_2_1) {
		if (req_size < HDCP_FIXED_SIZE_REPEATERAUTH_SEND_RECEIVERID_LIST21) {
			LOGE("Error, TZ_RepeaterAuth_Send_ReceiverId_List_T req_size (%d)\n", req_size);
			return HDCP2_ERR;
		}

		REPEATERAUTH_SEND_RECEIVER_ID_LIST21 *cert = (REPEATERAUTH_SEND_RECEIVER_ID_LIST21 *) request;
		if (req_size == (u32)(((cert->DEVICE_COUNT)*RECEIVER_ID_SIZE) + HDCP_FIXED_SIZE_REPEATERAUTH_SEND_RECEIVERID_LIST21)) {
			ret = TZ_RepeaterAuth_Send_ReceiverId_List21_T(request);
		} else {
			return HDCP2_ERR_INVALID_V;
		}
	} else {
		if (req_size < HDCP_FIXED_SIZE_REPEATERAUTH_SEND_RECEIVERID_LIST20) {
			LOGE("Error, TZ_RepeaterAuth_Send_ReceiverId_List_T req_size (%d)\n", req_size);
			return HDCP2_ERR;
		}

		REPEATERAUTH_SEND_RECEIVER_ID_LIST20 *cert = (REPEATERAUTH_SEND_RECEIVER_ID_LIST20 *) request;
		if (req_size == (u32)(((cert->DEVICE_COUNT)*RECEIVER_ID_SIZE) + HDCP_FIXED_SIZE_REPEATERAUTH_SEND_RECEIVERID_LIST20)) {
			ret = TZ_RepeaterAuth_Send_ReceiverId_List20_T(request);
		} else {
			return HDCP2_ERR_INVALID_V;
		}
	}

	return ret;
}

/**
 * @fn int TZ_RepeaterAuth_Send_ReceiverId_List20_T(uint8_t *request);
 * @brief This function gets the message containing the information regarding the hdcp2.0 compliant receivers connected to the repeater
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing REPEATERAUTH_SEND_RECEIVER_ID_LIST20 message
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_RepeaterAuth_Send_ReceiverId_List20_T(u8 *request)
{
	u8 V[32] = {0, };
	u8 input[159];
	u8 totalSize = 0;
	u8 offset = 0;

	TEE_MemFill(input,0,159);

	REPEATERAUTH_SEND_RECEIVER_ID_LIST20 *recidinfo = (REPEATERAUTH_SEND_RECEIVER_ID_LIST20 *) request;

	NULL_ERR(request, HDCP2_ERR_NULL_REQUEST);

	if(recidinfo->DEVICE_COUNT > MAX_RECEIVER_DEVICE)
		return HDCP2_ERR_TOO_MANY_DEVICES;

	TEE_MemMove(input, recidinfo->RECEIVER_IDs[0].RECEIVER_IDj, (recidinfo->DEVICE_COUNT)*RECEIVER_ID_SIZE);
	offset = (recidinfo->DEVICE_COUNT)*RECEIVER_ID_SIZE;

	TEE_MemMove(input + offset, &(recidinfo->DEPTH), sizeof(recidinfo->DEPTH));
	TEE_MemMove(input + offset+ sizeof(recidinfo->DEPTH),
			&(recidinfo->DEVICE_COUNT), sizeof(recidinfo->DEVICE_COUNT));
	TEE_MemMove(input + offset + sizeof(recidinfo->DEPTH) + sizeof(recidinfo->DEVICE_COUNT),
			&(recidinfo->MAX_DEVS_EXCEEDED), sizeof(recidinfo->MAX_DEVS_EXCEEDED));
	TEE_MemMove(input  + offset + sizeof(recidinfo->DEPTH) + sizeof(recidinfo->DEVICE_COUNT)
					+ sizeof(recidinfo->MAX_DEVS_EXCEEDED),
			&(recidinfo->MAX_CASCADE_EXCEEDED), sizeof(recidinfo->MAX_CASCADE_EXCEEDED));

	totalSize = offset + sizeof(recidinfo->DEPTH) + sizeof(recidinfo->DEVICE_COUNT)
					+ sizeof(recidinfo->MAX_DEVS_EXCEEDED) + sizeof(recidinfo->MAX_CASCADE_EXCEEDED);

	TZ_HMAC_SHA256(V, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd), input, totalSize);

	if (TEE_MemCompare(recidinfo->V_PRIME, V, sizeof(V)))
		return HDCP2_ERR_INVALID_V;

	return HDCP2_OK;
}

/**
 * @fn int TZ_RepeaterAuth_Send_ReceiverId_List21_T(uint8_t *request);
 * @brief This function gets the message containing the information regarding the hdcp2.1 compliant receivers connected to the repeater
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing REPEATERAUTH_SEND_RECEIVER_ID_LIST21 message
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_RepeaterAuth_Send_ReceiverId_List21_T(u8 *request)
{
	u8 V[32] ;
	u8 input[164];
	u8 totalSize = 0;
	u8 offset = 0;

	TEE_MemFill(V, 0, 32);
	TEE_MemFill(input, 0, 164);

	REPEATERAUTH_SEND_RECEIVER_ID_LIST21 *recidinfo = (REPEATERAUTH_SEND_RECEIVER_ID_LIST21 *) request;

	NULL_ERR(request, HDCP2_ERR_NULL_REQUEST);

	if(recidinfo->DEVICE_COUNT > MAX_RECEIVER_DEVICE)
		return HDCP2_ERR_TOO_MANY_DEVICES;

	TEE_MemMove(input, recidinfo->RECEIVER_IDs[0].RECEIVER_IDj, (recidinfo->DEVICE_COUNT)*RECEIVER_ID_SIZE);
	offset = (recidinfo->DEVICE_COUNT)*RECEIVER_ID_SIZE;

	TEE_MemMove(input + offset, &(recidinfo->DEPTH), sizeof(recidinfo->DEPTH));
	TEE_MemMove(input + offset + sizeof(recidinfo->DEPTH),
			&(recidinfo->DEVICE_COUNT), sizeof(recidinfo->DEVICE_COUNT));
	TEE_MemMove(input + offset + sizeof(recidinfo->DEPTH) + sizeof(recidinfo->DEVICE_COUNT),
			&(recidinfo->MAX_DEVS_EXCEEDED), sizeof(recidinfo->MAX_DEVS_EXCEEDED));
	TEE_MemMove(input + offset + sizeof(recidinfo->DEPTH) + sizeof(recidinfo->DEVICE_COUNT)
					+ sizeof(recidinfo->MAX_DEVS_EXCEEDED),
			&(recidinfo->MAX_CASCADE_EXCEEDED), sizeof(recidinfo->MAX_CASCADE_EXCEEDED));
	TEE_MemMove(input + offset + sizeof(recidinfo->DEPTH) + sizeof(recidinfo->DEVICE_COUNT)
					+ sizeof(recidinfo->MAX_DEVS_EXCEEDED) + sizeof(recidinfo->MAX_CASCADE_EXCEEDED),
			&(recidinfo->HDCP2_LEGACY_DEVICE_DOWNSTREAM), sizeof(recidinfo->HDCP2_LEGACY_DEVICE_DOWNSTREAM));
	TEE_MemMove(input + offset + sizeof(recidinfo->DEPTH) + sizeof(recidinfo->DEVICE_COUNT)
					+ sizeof(recidinfo->MAX_DEVS_EXCEEDED) + sizeof(recidinfo->MAX_CASCADE_EXCEEDED)
					+ sizeof(recidinfo->HDCP2_LEGACY_DEVICE_DOWNSTREAM),
			&(recidinfo->HDCP1_DEVICE_DOWNSTREAM), sizeof(recidinfo->HDCP1_DEVICE_DOWNSTREAM));
	TEE_MemMove(input + offset + sizeof(recidinfo->DEPTH) + sizeof(recidinfo->DEVICE_COUNT)
					+ sizeof(recidinfo->MAX_DEVS_EXCEEDED) + sizeof(recidinfo->MAX_CASCADE_EXCEEDED)
					+ sizeof(recidinfo->HDCP2_LEGACY_DEVICE_DOWNSTREAM)
					+ sizeof(recidinfo->HDCP1_DEVICE_DOWNSTREAM),
			recidinfo->seq_num_V, sizeof(recidinfo->seq_num_V));

	totalSize = offset + sizeof(recidinfo->DEPTH) + sizeof(recidinfo->DEVICE_COUNT)
					+ sizeof(recidinfo->MAX_DEVS_EXCEEDED)+ sizeof(recidinfo->MAX_CASCADE_EXCEEDED)
					+ sizeof(recidinfo->HDCP2_LEGACY_DEVICE_DOWNSTREAM)
					+ sizeof(recidinfo->HDCP1_DEVICE_DOWNSTREAM) + sizeof(recidinfo->seq_num_V);

	TZ_HMAC_SHA256(V, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd), input, totalSize);

	//TZ_LOG_HEX("V", V, sizeof(V));
	//TZ_LOG_HEX("V' MSB from Dongle", recidinfo->V_PRIME,
	//		sizeof(recidinfo->V_PRIME));

	TEE_MemMove(hdcp2_ctx.repeater_ack, V + 16, 16); //V lsb
	if (TEE_MemCompare(recidinfo->V_PRIME, V, 16) != 0) //V msb
		return HDCP2_ERR_INVALID_V;

	return HDCP2_OK;
}

/**
 * @fn int TZ_RepeaterAuth_Send_Ack_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function sends an acknowledgment to the receiver
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response  containing REPEATERAUTH_SEND_ACK message
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_RepeaterAuth_Send_Ack_T(const u8 command, u8 *request,
			const u32 req_size, u8 *response, u32 *res_size)
{
	if (*res_size != sizeof(REPEATERAUTH_SEND_ACK)) {
		LOGE("Error, REPEATERAUTH_SEND_ACK data size (%d)\n", *res_size);
		return HDCP2_ERR;
	}

	REPEATERAUTH_SEND_ACK *send_ack = (REPEATERAUTH_SEND_ACK *) response;
	NULL_ERR(send_ack, HDCP2_ERR_NULL_RESPONSE);

	send_ack->msg_id = command;
	TEE_MemMove(send_ack->V, hdcp2_ctx.repeater_ack, sizeof(hdcp2_ctx.repeater_ack));

	return HDCP2_OK;
}

/**
 * @fn int TZ_Receiver_AuthStatus_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function lets the transmitter know about the authentication status of receiver with the repeater
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing REPEATERAUTH_SEND_ACK message
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_Receiver_AuthStatus_T(const u8 command, u8 *request, const u32 req_size,
			u8 *response, u32 *res_size)
{
	if (req_size != sizeof(RECEIVER_AUTHSTATUS)) {
		LOGE("Error, RECEIVER_AUTHSTATUS data size (%d)\n", req_size);
		return HDCP2_ERR;
	}

	RECEIVER_AUTHSTATUS *status = (RECEIVER_AUTHSTATUS *) request;
	NULL_ERR(status, HDCP2_ERR_NULL_REQUEST);

	if (status->REAUTH_Req == 1)
		return HDCP2_ERR_REAUTH_REQ; //request for reauthentication
	else
		return HDCP2_OK;
}

/**
 * @fn int TZ_RepeaterAuth_Stream_Manage_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function propagates content stream management information, which includes Type values assigned to the content streams
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing REPEATERAUTH_STREAM_MANAGE message
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_RepeaterAuth_Stream_Manage_T(const u8 command, u8 *request,
			const u32 req_size, u8 *response, u32 *res_size)
{
	if (*res_size != sizeof(REPEATERAUTH_STREAM_MANAGE) || req_size != sizeof(REPEATERAUTH_STREAM_INFO)) {
		LOGE("Error, RepeaterAuth data size (%d), (%d)\n", req_size, *res_size);
		return HDCP2_ERR;
	}
	int i = 0;
	*res_size = sizeof(REPEATERAUTH_STREAM_MANAGE);

	REPEATERAUTH_STREAM_MANAGE *send_msg =
			(REPEATERAUTH_STREAM_MANAGE *) response;
	NULL_ERR(send_msg, HDCP2_ERR_NULL_RESPONSE);

	REPEATERAUTH_STREAM_INFO *send_info = (REPEATERAUTH_STREAM_INFO *) request;
	NULL_ERR(send_info, HDCP2_ERR_NULL_RESPONSE);

	TEE_MemMove(hdcp2_ctx.no_of_streams, send_info->no_of_streams,
			sizeof(hdcp2_ctx.no_of_streams));

	for (i = 0; i < hdcp2_ctx.no_of_streams[1] && hdcp2_ctx.no_of_streams[1] <= 16; i++) {
		TEE_MemMove(hdcp2_ctx.multi_stream[i].streamCtrj,
				send_info->multi_streaminfo[i].streamCtrj,
				sizeof(hdcp2_ctx.multi_stream[i].streamCtrj));
		TEE_MemMove(hdcp2_ctx.multi_stream[i].ContentStreamIDj,
				send_info->multi_streaminfo[i].ContentStreamIDj,
				sizeof(hdcp2_ctx.multi_stream[i].ContentStreamIDj));

		hdcp2_ctx.multi_stream[i].Type = send_info->multi_streaminfo[i].Type;
	}

	send_msg->msg_id = command;
	TEE_MemMove(send_msg->seq_num_M, hdcp2_ctx.seq_num_M,
			sizeof(hdcp2_ctx.seq_num_M));
	TEE_MemMove(send_msg->k, hdcp2_ctx.no_of_streams, sizeof(send_msg->k));

	//saving the multistream data to calculate RepeaterAuth_Stream_Ready message
	for (i = 0; i < hdcp2_ctx.no_of_streams[1] && hdcp2_ctx.no_of_streams[1] <= 16; i++) {
		TEE_MemMove(send_msg->max_k_info[i].streamCtrj,
				hdcp2_ctx.multi_stream[i].streamCtrj,
				sizeof(send_msg->max_k_info[i].streamCtrj));
		TEE_MemMove(send_msg->max_k_info[i].ContentStreamIDj,
				hdcp2_ctx.multi_stream[i].ContentStreamIDj,
				sizeof(send_msg->max_k_info[i].ContentStreamIDj));

		send_msg->max_k_info[i].Type = hdcp2_ctx.multi_stream[i].Type;
	}

	return HDCP2_OK;
}

/**
 * @fn int TZ_RepeaterAuth_Stream_Ready_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function obtains M- prime as sent by repeater
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing REPEATERAUTH_STREAM_READY message
 * @param req_size - size of request
 * @param response- pointer to response
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_RepeaterAuth_Stream_Ready_T(const u8 command, u8 *request,
			const u32 req_size, u8 *response, u32 *res_size)
{
	REPEATERAUTH_STREAM_READY *status = (REPEATERAUTH_STREAM_READY *) request;
	NULL_ERR(status, HDCP2_ERR_NULL_REQUEST);
	u8 kd[32] = {0, };
	u8 M[32] = {0, };
	// size = hdcp2_ctx.no_of_streams*7 + 3;
	u8 input[115];
	u8* temp_ptr = input;
	int i = 0;

	if (req_size != sizeof(REPEATERAUTH_STREAM_READY)) {
		LOGE("Error, REPEATERAUTH data size (%d)\n", req_size);
		return HDCP2_ERR_INVALID_M;
	}

	TEE_MemFill(input,0,115);

	//calculate input
	for (i = 0; i < hdcp2_ctx.no_of_streams[1] && hdcp2_ctx.no_of_streams[1] <= 16; i++) {
		TEE_MemMove(temp_ptr, hdcp2_ctx.multi_stream[i].streamCtrj,
				sizeof(hdcp2_ctx.multi_stream[i].streamCtrj));
		TEE_MemMove(temp_ptr + sizeof(hdcp2_ctx.multi_stream[i].streamCtrj),
				hdcp2_ctx.multi_stream[i].ContentStreamIDj,
				sizeof(hdcp2_ctx.multi_stream[i].ContentStreamIDj));
		TEE_MemMove(temp_ptr + sizeof(hdcp2_ctx.multi_stream[i].streamCtrj)
						+ sizeof(hdcp2_ctx.multi_stream[i].ContentStreamIDj),
				&(hdcp2_ctx.multi_stream[i].Type),
				sizeof(hdcp2_ctx.multi_stream[i].Type));
		temp_ptr = temp_ptr + sizeof(hdcp2_ctx.multi_stream[i].streamCtrj)
				+ sizeof(hdcp2_ctx.multi_stream[i].ContentStreamIDj)
				+ sizeof(hdcp2_ctx.multi_stream[i].Type);
	}

	TEE_MemMove(temp_ptr, hdcp2_ctx.seq_num_M, sizeof(hdcp2_ctx.seq_num_M));

	TZ_SHA256(kd, (uint8_t*) &hdcp2_ctx.kd, 32);
	TZ_HMAC_SHA256(M, kd, sizeof(kd), input, (hdcp2_ctx.no_of_streams[1] * 7 + 3));

	if (TEE_MemCompare(status->M_PRIME, M, 32) != 0)
		return HDCP2_ERR_INVALID_M;

	hdcp2_ctx.seq_num_M[2] += 1;

	return HDCP2_OK;
}

/**
 * @fn int TZ_ENC_Data(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function encrypts the plain text sent to it.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing the data stream to be encrypted
 * @param req_size - size of request
 * @param response- pointer to response
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_ENC_Data(u8 command, u8 *request, const u32 req_size, u8 *response,
			u32 *res_size)
{
	int ret = 0;
	unsigned char p[16];
	u8 *output = response;
	u32 ct_len  = 0;
	u8 *input_data = NULL;
	u8 *enc_data = NULL;
	int ion_fd = 0;
	uint64_t phys_fd = 0;
	unsigned char pKey[16] = {0, };
	int length = 0;

	if (req_size < sizeof(CIP_DATA_INFO) - (ADJUST_POINTER_SIZE * 2)) {
		LOGE("TZ_ENC_Data: Request size, wrong : %d\n", req_size);
		return HDCP2_ERR_INVALID_INPUT;
	}

 	if(!TZ_HDCP2_Get_Status_UnwrapKey()) {
		LOGE("TZ_ENC_Data: TZ_HDCP2_Get_Status_UnwrapKey return failed");
		return HDCP2_ERR_UNWRAPKEY;
	}
	CIP_DATA_INFO *data = (CIP_DATA_INFO *) request;
#ifdef CONFIG_HDCP_64BIT
	CIP_DATA_INFO tmp_data;
	TEE_MemMove(&tmp_data, request, req_size);
	TEE_MemFill(request, 0, req_size);
#endif /* CONFIG_HDCP_64BIT */

#ifdef USE_MTK
	tlApimem_t meminfo;
	uint64_t input_pa;
#endif /* USE_MTK */

//	TZ_LOG_HEX("lc128....", hdcp2_key.lc128, 16);
//	TZ_LOG_HEX("hdcp ks", hdcp2_ctx.ks, 16);

	if (TZ_Get_ContentKey(hdcp2_ctx.ks, hdcp2_key.lc128, pKey) != 0)
		return HDCP2_ERR_CRYPTO;

	if (!data) {
		LOGE("TZ_ENC_Data: Error, Invalid Input (data == null)\n");
		return HDCP2_ERR_INVALID_INPUT;
	}

#ifdef CONFIG_HDCP_64BIT
	data->dummy = tmp_data.dummy;
	data->input_data = tmp_data.input_data;
	data->output_data = tmp_data.output_data;
	data->str_ctr = tmp_data.str_ctr;
	data->inp_ctr = tmp_data.inp_ctr;
	data->length = tmp_data.length;
	data->split_ctr = *(&(tmp_data.length) + 4);
	data->offset = *(&(tmp_data.length) + 3);
	data->output = *(&(tmp_data.length) + 2);
	data->input = *(&(tmp_data.length) + 1);

//	TZ_HDCP_DEBUG("data->length: %d\n", data->length);
	LOGD("data->input: 0x%llx\n", data->input);
	LOGD("data->output: 0x%llx\n", data->output);
//	TZ_HDCP_DEBUG("data->offset: %d\n", (int)data->offset);
#endif /* CONFIG_HDCP_64BIT */

	if (!data->input || !(&(data->length)) || !data->output || !(&(data->offset))) {
		LOGE("TZ_ENC_Data: Error, Invalid Input parameters\n");
		return HDCP2_ERR_INVALID_INPUT;
	}

	if (data->length > MAX_ENCRYPT_BUFFER) {
		LOGE("Data size : %d\n", data->length);
		LOGE("Error, Data max size\n");
		return HDCP2_ERR_INVALID_INPUT;
	}

	if(data->length != *res_size) {
		LOGE("Data size : %d, Response size : %d\n", data->length, *res_size);
		LOGE("Error, data size is not the same as reponse size\n");
		return HDCP2_ERR_INVALID_INPUT;
	}

	if (req_size != (sizeof(CIP_DATA_INFO) - (ADJUST_POINTER_SIZE * 2)) && (int)data->offset != -1) {
		LOGE("TZ_ENC_Data: Error, Invalid Req size (%d)\n", req_size);
		return HDCP2_ERR_INVALID_INPUT;
	} else {
		if (req_size != (sizeof(CIP_DATA_INFO) - (ADJUST_POINTER_SIZE * 2) + data->length) && (int)data->offset == -1) {
			LOGE("TZ_DEC_Data: Error, Invalid Req size (%d)\n", req_size);
			return HDCP2_ERR_INVALID_INPUT;
		}
	}

	LOGD("TZ_ENC_Data: input : 0x%x, output : 0x%x, in_ctr : %lld\n", 
					(unsigned int)data->input, (unsigned int)data->output, data->inp_ctr);
	LOGD("TZ_ENC_Data: split_ctr : %d\n", data->split_ctr);
	LOGD("TZ_ENC_Data: data_len : %d, res_size : %d\n", data->length, *res_size);

	if ((int)data->offset == -1) {
		LOGD("virtual data\n");
		enc_data = request + sizeof(CIP_DATA_INFO) - (ADJUST_POINTER_SIZE * 2);
	} else {
		LOGD("ion data\n");
		ion_fd = open(PHYS_DEV_NAME, O_RDWR);
		if (ion_fd < 0) {
			LOGE("failed to open %s : %d\n",PHYS_DEV_NAME, errno);
			return HDCP2_ERR;
		}

		phys_fd = (uint64_t)(data->input);

		length = data->length + data->split_ctr * MAX_ENCRYPT_BUFFER;

#ifdef USE_MTK
		if (drMemPAQueryByType(phys_fd, &input_pa, MEM_HAPP_SVP) != 0) {
			LOGE("TZ_ENC_Data: Error, mapping error\n");
			close(ion_fd);
			return HDCP2_ERR_INVALID_INPUT;
		}
		if (input_pa == 0) {
			LOGE("TZ_ENC_Data: Error, input address error (%llx)\n", input_pa);
			close(ion_fd);
			return HDCP2_ERR_INVALID_INPUT;
		}
		LOGD("phys_fd: %llx, input_pa : %llx\n", phys_fd, input_pa);
		input_data = (u8 *)mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_PHYS_NON_CACHED, ion_fd, input_pa);
#else
		input_data = (u8 *)mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_PHYS_NON_CACHED, ion_fd, phys_fd*PAGE_SIZE);
#endif /* USE_MTK */

		if (input_data == MAP_FAILED) {
			LOGE("input mmap failed : %d\n", errno);
			close(ion_fd);
			return HDCP2_ERR;
		} else {
			LOGD("[before] input_data : 0x%x, MAX_ENCRYPT_BUFFER : %d, split_ctr: %d\n", 
							(unsigned int)input_data, MAX_ENCRYPT_BUFFER, data->split_ctr);
			enc_data = input_data + (data->split_ctr * MAX_ENCRYPT_BUFFER);
			LOGD("[after] enc_data : 0x%x\n", (unsigned int)enc_data);
		}

		phys_fd = (uint64_t)(data->output);
		output = (u8 *)mmap(NULL, data->length, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_PHYS_NON_SECURE, ion_fd, phys_fd*PAGE_SIZE);
		if (output == MAP_FAILED) {
			LOGE("output mmap failed : %d\n", errno );
			munmap(input_data, length);
			close(ion_fd);
			return HDCP2_ERR;
		}
	}

//	TZ_LOG_HEX("[Input Data]", input_data, 32);

	*res_size = data->length;
	p[0] = hdcp2_ctx.riv[0];
	p[1] = hdcp2_ctx.riv[1];
	p[2] = hdcp2_ctx.riv[2];
	p[3] = hdcp2_ctx.riv[3];
	p[4] = hdcp2_ctx.riv[4] ^ (u8) ((data->str_ctr >> 24) & 0xff);
	p[5] = hdcp2_ctx.riv[5] ^ (u8) ((data->str_ctr >> 16) & 0xff);
	p[6] = hdcp2_ctx.riv[6] ^ (u8) ((data->str_ctr >> 8) & 0xff);
	p[7] = hdcp2_ctx.riv[7] ^ (u8) (data->str_ctr & 0xff);
	p[8] = (u8) ((data->inp_ctr >> 56) & 0xff);
	p[9] = (u8) ((data->inp_ctr >> 48) & 0xff);
	p[10] = (u8) ((data->inp_ctr >> 40) & 0xff);
	p[11] = (u8) ((data->inp_ctr >> 32) & 0xff);
	p[12] = (u8) ((data->inp_ctr >> 24) & 0xff);
	p[13] = (u8) ((data->inp_ctr >> 16) & 0xff);
	p[14] = (u8) ((data->inp_ctr >> 8) & 0xff);
	p[15] = (u8) (data->inp_ctr & 0xff);

//	TZ_LOG_HEX("pkey===", pKey, 16);

	ct_len = data->length;

#ifdef USE_DUMP_RAW_DATA
	for (int i = 0; i < data->length; i++)
		*(output+i) = *(enc_data+i);
#else
	ret = TZ_Cipher_AES_CTR_Encrypt_HW(enc_data, data->length, output, &ct_len, pKey, p);
#endif /* USE_DUMP_RAW_DATA */

//	TZ_LOG_HEX("[Output Data]", output, 32);

	if (ret != HDCP2_OK)
		LOGE("data encryption error\n");

	if ((int)data->offset != -1) {
		munmap(input_data, length);
		munmap(output, data->length);
		close(ion_fd);
	}

	return ret;
}
