/**
 * 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 <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include "tz_hdcp2_crypto.h"
#include "tz_hdcp2_common.h"
#include "qsee_log.h"
#include "qsee_rsa.h"
#include "qsee_hash.h"
#include "qsee_fuse.h"
#include "qsee_kdf.h"
#include "qsee_services.h"
#ifndef CONFIG_MSM8953
#include "qsee_drm.h"
#endif

/**
 * @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;
static HDCP2_KEY hdcp2_key;

uint8_t pairing_info_dir[] = "/data/misc/sshdcpapp/sfs";

u8 s_Tx_pKey[DECRYPT_BLK_SIZE];

#define QSEECOM_ALIGN_SIZE	0x40
#define QSEECOM_ALIGN_MASK	(QSEECOM_ALIGN_SIZE - 1)
#define QSEECOM_ALIGN(x)	\
	((x + QSEECOM_ALIGN_SIZE) & (~QSEECOM_ALIGN_MASK))

#define HDCP2_TZ_FAIL 9999

// 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

#ifndef HDCP2_NO_TIME_LIMIT
/**
 * @fn static long EstimateTime(timestamp_t *start, timestamp_t *end);
 * @brief This function is used to estimate the time taken between 2 events.
 * @param start - Pointer to start event.
 * @param end- Pointer to end event.
 * @return long - duration between the events.
 */
static long EstimateTime(timestamp_t *start, timestamp_t *end)
{
	return end - start; //(end->tv_sec - start->tv_sec)*1000 + (end->tv_usec - start->tv_usec)/1000;
}
#endif /* HDCP2_NO_TIME_LIMIT */

/**
 * @fn void TZ_TX_LOG_HEX(const char *title, unsigned char *data, int length);
 * @brief This function is used to print the Tz logs.
 * @param title - Title of the prints.
 * @param data- Pointer to the data to be printed
 * @param length - size of the data.
 * @return void
 */
void TZ_TX_LOG_HEX(const char *title, const unsigned char *data, int length)
{
#ifdef DEBUG
	int i;
	char buffer[128] = {0, };
	char dest_buffer[128] = {0, };

	// print title
	LOGD("%s[%d] = ", title, length);

	// print binary data
	for (i = 0; i < length; i++) {
		if (i % 16 == 0) {
			LOGD("%s", buffer);
			buffer[0] = 0;
		}
		sprintf(dest_buffer, "%s%.2x ", buffer, data[i]);
		sprintf(buffer,"%s",dest_buffer);
	}

	sprintf(dest_buffer,"%s",buffer);
	sprintf(buffer, "%s", dest_buffer);
	LOGD("%s\n", buffer);
#endif /* DEBUG */
}

/**
 * @fn int TZ_AKE_Reset_T()
 * @brief This function resets the HDCP context
 * @return int - HDCP2_OK
 */
static int TZ_AKE_Reset_T()
{
	memset(&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 res_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)
{
	int ret = HDCP2_OK;
	*res_size = sizeof(u8);
	uint8_t *unwrap = NULL;
	uint32_t unwrap_sz = 0;
	uint8_t *decrypted_pairing_info = NULL;
	uint32_t decrypted_pairing_info_len = 0;
	CIPHER_KEY cipher_key;

	memset(cipher_key.label, 0, HDCP2_AES_KEY_SIZE);
	memset(cipher_key.context, 0, HDCP2_AES_KEY_SIZE);
	memset(cipher_key.key, 0, HDCP2_AES_KEY_SIZE);

	decrypted_pairing_info = qsee_malloc(sizeof(HDCP2_PAIRING_INFO));
	if (decrypted_pairing_info == NULL) {
		LOGE("Load Pairing Info: Can't allocate for buffer 1.");
		ret = HDCP2_TZ_NULL_RESPONSE;
		goto Err;
	}
	decrypted_pairing_info_len = sizeof(HDCP2_PAIRING_INFO);
	memset(decrypted_pairing_info, 0, decrypted_pairing_info_len);

	unwrap = qsee_malloc(sizeof(HDCP2_PAIRING_INFO));
	if (unwrap == NULL) {
		LOGE("Load Pairing Info: Can't allocate for buffer 1.");
		ret = HDCP2_TZ_NULL_RESPONSE;
		goto Err;
	}
	unwrap_sz = sizeof(HDCP2_PAIRING_INFO);
	memset(unwrap, 0, unwrap_sz);

	//take pairing info data is sent from NWD
	memcpy(cipher_key.context, request + 1, HDCP2_AES_KEY_SIZE);
	memcpy(cipher_key.label, request + 1 + HDCP2_AES_KEY_SIZE, HDCP2_AES_KEY_SIZE);
	memcpy(unwrap, request + 1 + HDCP2_AES_KEY_SIZE + HDCP2_AES_KEY_SIZE, sizeof(HDCP2_PAIRING_INFO));	

	//Generate AES_Key by KDF function
	ret = qsee_kdf(NULL, 0, (void*)cipher_key.label, HDCP2_AES_KEY_SIZE, 
					(void*)cipher_key.context, HDCP2_AES_KEY_SIZE, cipher_key.key, HDCP2_AES_KEY_SIZE);
	if (ret != HDCP2_TZ_OK) {
		LOGE("Load Pairing Info: qsee_kdf failed.");
		ret = HDCP2_ERR_KDF;
		goto Err;
	}

	//Decrypt Pairing Info
	ret = TZ_AES_decrypt(cipher_key.key, HDCP2_AES_KEY_SIZE, unwrap, unwrap_sz,
					decrypted_pairing_info, &decrypted_pairing_info_len, NULL, QSEE_CIPHER_MODE_ECB);
	if (ret < HDCP2_OK) {
		LOGE("Load Pairing Info: Decrypt AES_ECB failed");
		goto Err;
	}
	memcpy((uint8_t *)&hdcp2_ctx.pairing_info, decrypted_pairing_info, sizeof(HDCP2_PAIRING_INFO));

	TZ_TX_LOG_HEX("hdcp2_ctx.pairing_info", (uint8_t *)&hdcp2_ctx.pairing_info, sizeof(HDCP2_PAIRING_INFO));
	hdcp2_ctx.is_paired = 1;

Err:
	if (decrypted_pairing_info != NULL)
		qsee_free(decrypted_pairing_info);
	if (unwrap != NULL)
		qsee_free(unwrap);

	return 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 res_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 u8 command, u8 *request,
			const u32 req_size, u8 *response, u32 *res_size)
{
	int ret = 0;
	*res_size = sizeof(u8);

	LOGI("Load key without SFS");
	ret = TZ_HDCP2_LOADKEY_NO_SFS(request, req_size, &hdcp2_key, (command == CMD_HDCP2_LOADKEY_ODM)?1:0);

	return ret;
}

/**
 * @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 on transmitter side.
 * @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);
	hdcp2_ctx.version = request[1];
	LOGW("TZ_SET_HDCP_VERSION_T : HDCP %d.%d version is setuped", 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 res_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)
{
	AKE_INIT *tx_payload = (AKE_INIT *) response;
	int HDCPflag = 0;
	boolean isblown = 0;

	LOGD("TZ_AKE_Init_T called = %d", command);
	*res_size = sizeof(AKE_INIT);
	NULL_ERR(tx_payload, HDCP2_TZ_NULL_RESPONSE);

#ifndef DEBUG
	if (qsee_is_sw_fuse_blown(OEMFLAG_TZ_DRM, &isblown, sizeof(boolean)))
		return HDCP2_ERR_INTEGRITY;

	if (isblown == 1)
		return HDCP2_ERR_INTEGRITY;
#endif /* DEBUG */

	TZ_AKE_Reset_T();

#ifndef HDCP2_NO_TIME_LIMIT
	GetTimeStamp(&hdcp2_ctx.time_ake_init);
#endif /* HDCP2_NO_TIME_LIMIT */

	// Set OPL: 0x09 ~ HDCP 2.2
	qsee_set_global_flag(0x09);

	HDCPflag = qsee_get_global_flag();
	if (HDCPflag != 0x09) {
		LOGE("not able to set OPL");
		return HDCP2_ERR;
	}

	LOGD("OPL value is set");

	tx_payload->msg_id = command;

	// generate a random
	if (TZ_rand(hdcp2_ctx.rtx, sizeof(hdcp2_ctx.rtx)) != sizeof(hdcp2_ctx.rtx)) {
		LOGE("TZ_AKE_Init_T returning with error HDCP2_ERR_CRYPTO");
		response = NULL;
		return HDCP2_ERR_CRYPTO;
	}

	// make payload ....
	memcpy(tx_payload->rtx, hdcp2_ctx.rtx, sizeof(hdcp2_ctx.rtx));

	TZ_TX_LOG_HEX("TZ_AKE_Init_T rtx", tx_payload->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 res_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)
{
	AKE_TRANSMITTER_INFO *tx_payload = (AKE_TRANSMITTER_INFO *) response;
	u8 disable_precompute = 0;
	LOGD("TZ_AKE_Transmitter_Info_T called = %d", command);

	NULL_ERR(request, HDCP2_TZ_NULL_REQUEST);
	disable_precompute = request[1];

	*res_size = sizeof(AKE_TRANSMITTER_INFO);
	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;
	LOGW("TZ_AKE_Transmitter_Info_T : transmitter_info.VERSION : 0x%02x", hdcp2_ctx.transmitter_info.VERSION);

#ifndef HDCP2_NO_TIME_LIMIT
	GetTimeStamp(&hdcp2_ctx.ts);
#endif /* !HDCP2_NO_TIME_LIMIT */

	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 res_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)
{
	AKE_SEND_CERT *cert = (AKE_SEND_CERT *) request;
	*res_size = sizeof(u8);
	LOGD("TZ_AKE_Send_Cert_T called = %d", command);
	NULL_ERR(cert, HDCP2_TZ_NULL_REQUEST);

	memcpy(&hdcp2_key.cert, cert->certrx, sizeof(HDCP2_RECEIVER_CERTIFICATE));

	TZ_TX_LOG_HEX("TZ_AKE_Send_Cert_T", 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 res_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)
{
#ifndef HDCP2_NO_TIME_LIMIT
	long duration = 0;
	timestamp_t ts;
#endif /* !HDCP2_NO_TIME_LIMIT */

	AKE_RECEIVER_INFO *receiver_info = (AKE_RECEIVER_INFO *) request;
	*res_size = sizeof(u8);
	LOGD("TZ_AKE_Receiver_Info_T called = %d", command);

	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;
		LOGW("TZ_AKE_Receiver_Info_T : HDCP version is changed to %d.%d", hdcp2_ctx.version / 10, hdcp2_ctx.version % 10);
	}

	memcpy(&hdcp2_ctx.receiver_info, receiver_info, sizeof(AKE_RECEIVER_INFO));

	hdcp2_ctx.Rx_LC_Precompute = receiver_info->RECEIVER_CAPABILITY_MASK[1] & LOCALITY_PRECOMPUTE_SUPPORT;

#ifndef HDCP2_NO_TIME_LIMIT
	GetTimeStamp(&ts);
	duration = EstimateTime(&hdcp2_ctx.ts, &ts);
	if (duration > HDCP2_VALID_TIME_100MS)
		return HDCP2_ERR_TIME_AKE_RECEIVER_INFO;
#endif /* !HDCP2_NO_TIME_LIMIT */

	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 res_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)
{
	int ret = 0;
	uint8 dcp_llc_sign[SIZE_OF_DCPLLC_SIGNATURE] = {0, };
	uint8 recv_Id[SIZE_OF_PUBLIC_KEY_CERTIFICATE_OF_HDCP_RECEIVER - SIZE_OF_DCPLLC_SIGNATURE] = {0, };

	QSEE_RSA_KEY *key = NULL;
	uint8 hash[QSEE_SHA256_HASH_SZ] = {0, };
	uint32 hash_len = QSEE_SHA256_HASH_SZ;
	QSEE_HASH_IDX hashidx = QSEE_HASH_IDX_SHA256;
	QSEE_S_BIGINT rsa_mod,rsa_pbk_exp;

	AKE_NO_STORED_KM *no_stored_km = (AKE_NO_STORED_KM *) response;

	if (memcmp(hdcp2_key.cert.receiver_id, test_certid_r1, 5) == 0 || memcmp(hdcp2_key.cert.receiver_id, test_certid_r2, 5) == 0) {
		LOGI("[HDCP2] Cert Verification using Test Key");
		memcpy(mod_buf, mod_buf_test, SIZE_OF_DCPLLC_SIGNATURE);
	} else {
		LOGI("[HDCP2] Cert Verification using  Production key");
	}

	*res_size = sizeof(AKE_NO_STORED_KM);
	NULL_ERR(no_stored_km, HDCP2_TZ_NULL_RESPONSE);

	LOGD("TZ_AKE_No_Store_km_T called = %d", command);

	key = (QSEE_RSA_KEY*)qsee_malloc(sizeof(QSEE_RSA_KEY));
	if (NULL == key) {
		return -2000; //-E_FAILURE;
	}

	memset(key, 0, sizeof(QSEE_RSA_KEY));

	if (qsee_BIGINT_read_unsigned_bin(&(rsa_mod.bi), (const uint8_t *)mod_buf, sizeof(mod_buf)) ||
		qsee_BIGINT_read_unsigned_bin(&(rsa_pbk_exp.bi), (const uint8_t *)exp_buf, sizeof(exp_buf)) ) {
		LOGE("read_unsigned_bin() failed" );
	} else {
		key->e = &rsa_pbk_exp;
		key->N = &rsa_mod;
	}

	memcpy(dcp_llc_sign, hdcp2_key.cert.dcp_llc_sign, SIZE_OF_DCPLLC_SIGNATURE);
	memcpy(recv_Id, (const uint8 *)&hdcp2_key.cert, SIZE_OF_PUBLIC_KEY_CERTIFICATE_OF_HDCP_RECEIVER - SIZE_OF_DCPLLC_SIGNATURE);

	do {
		if (hashidx == QSEE_HASH_IDX_SHA256) {
			hash_len = QSEE_SHA256_HASH_SZ;
		}

		ret = qsee_hash((QSEE_HASH_ALGO_ET)(hashidx), recv_Id, SIZE_OF_PUBLIC_KEY_CERTIFICATE_OF_HDCP_RECEIVER - SIZE_OF_DCPLLC_SIGNATURE, hash, hash_len);
		if (CE_SUCCESS != ret) {
			LOGE("tzbsp_hash in rsa_sign_verify failed ret :%d",ret);
			break;
		}

		key->type = QSEE_RSA_KEY_PUBLIC;
		key->bitLength = 3072;
		ret = qsee_rsa_verify_signature(key, QSEE_RSA_PAD_PKCS1_V1_5_SIG, NULL, hashidx,
						hash, hash_len, dcp_llc_sign, SIZE_OF_DCPLLC_SIGNATURE);
		if (CE_SUCCESS != ret) {
			LOGE("verify signature failed ret :%d",ret);
			break;
		}
	} while(0);

	qsee_free(key);

	// 1. randomly choose hdcp2_ctx->km
	if(TZ_rand((u8 *)hdcp2_ctx.pairing_info.km, sizeof(hdcp2_ctx.pairing_info.km)) != sizeof(hdcp2_ctx.pairing_info.km)) {
		LOGE("TZ_rand failed");
		return HDCP2_ERR;
	}
	// 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)) {
		LOGE("TZ_RSA_OAEP_encrypt failed");
		return HDCP2_ERR_OAEP_ENCRYPTION;
	}

	// Set new timestamp
#ifndef HDCP2_NO_TIME_LIMIT
	GetTimeStamp(&hdcp2_ctx.ts);
#endif /* !HDCP2_NO_TIME_LIMIT */

	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 res_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)
{
	AKE_STORED_KM *stored_km = (AKE_STORED_KM *) response;

	*res_size = sizeof(AKE_STORED_KM);
	NULL_ERR(stored_km, HDCP2_TZ_NULL_RESPONSE);

	LOGD("TZ_AKE_Store_km_T called = %d", command);
	if (!hdcp2_ctx.is_paired)
		return HDCP2_ERR_STORED_KM_NOT_PAIRED;

	stored_km->msg_id = command;
	memcpy(stored_km->m, hdcp2_ctx.pairing_info.m, sizeof(stored_km->m));
	memcpy(stored_km->Ekh_km, hdcp2_ctx.pairing_info.Ekm, sizeof(stored_km->Ekh_km));

	// Set new timestamp
#ifndef HDCP2_NO_TIME_LIMIT
	GetTimeStamp(&hdcp2_ctx.ts);
#endif /* !HDCP2_NO_TIME_LIMIT */

	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 res_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)
{
	AKE_SEND_RRX *rrx = (AKE_SEND_RRX *) request;
	*res_size = sizeof(u8);
	LOGD("TZ_AKE_Send_rrx_T called = %d", command);
	NULL_ERR(rrx, HDCP2_TZ_NULL_REQUEST);
	memcpy(hdcp2_ctx.rrx, rrx->rrx, sizeof(rrx->rrx));

	TZ_TX_LOG_HEX("TZ_AKE_Send_rrx_T", 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 res_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[HDCP2_MESSAGE_DIGEST_SIZE] = {0, };
	u8 input[8] = {0, };
	int ret = HDCP2_OK;

#ifndef HDCP2_NO_TIME_LIMIT
	timestamp_t ts = {0, };
	long duration = 0;
#endif /* !HDCP2_NO_TIME_LIMIT */

	AKE_SEND_H_PRIME *h_prime = (AKE_SEND_H_PRIME *) request;
	char pair_info_path[HDCP2_PAIR_INFO_PATH_LEN]= {0, };
	*res_size = sizeof(u8);
	LOGD("TZ_AKE_Send_h_prime_T called = %d", command);
	NULL_ERR(h_prime, HDCP2_TZ_NULL_REQUEST);

	// verify time
#ifndef HDCP2_NO_TIME_LIMIT
	GetTimeStamp(&ts);
	duration = EstimateTime(&hdcp2_ctx.ts, &ts);

	if (hdcp2_ctx.is_paired) {
		if (duration > HDCP2_VALID_TIME_200MS)
			return HDCP2_ERR_TIME_AKE_SEND_H_PRIME;
	} else {
		if (duration > HDCP2_VALID_TIME_1SEC)
			return HDCP2_ERR_TIME_AKE_SEND_H_PRIME;
	}
#endif /* !HDCP2_NO_TIME_LIMIT */

	// Derivate kd = dkey0 || dkey1
	if ((ret = TZ_Derivate_dkey(&hdcp2_ctx)) < 0) {
		LOGE("TZ_Derivate_dkey failed with ret:%d", ret);
		return ret;
	}
	memcpy(hdcp2_ctx.kd, hdcp2_ctx.dkey, 16);

	if ((ret = TZ_Derivate_dkey(&hdcp2_ctx)) < 0) {
		LOGE("TZ_Derivate_dkey failed with ret:%d", ret);
		return ret;
	}
	memcpy(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];
		memset(temp_input, 0, sizeof(temp_input));
		memcpy(temp_input, hdcp2_ctx.rtx, sizeof(hdcp2_ctx.rtx));
		temp_input[7] ^= hdcp2_ctx.REPEATER;
		temp_input[8] = hdcp2_ctx.receiver_info.VERSION;
		memcpy(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;
		memcpy(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)
		if ((ret = TZ_HMAC_SHA256(H, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd), temp_input, sizeof(temp_input)))<0) {
			LOGE("TZ_HMAC_SHA256 failed with ret:%d", ret);
			return ret;
		}
		TZ_LOG_HEX("H", H, 32);
		TZ_LOG_HEX("h_prime", h_prime->H, 32);
	} else {
		// input
		memcpy(input, hdcp2_ctx.rtx, sizeof(hdcp2_ctx.rtx));
		input[7] ^= hdcp2_ctx.REPEATER;

		if((ret = TZ_HMAC_SHA256(H, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd), input, sizeof(input))) < 0) {
			LOGE("TZ_HMAC_SHA256 failed with ret:%d", ret);
			return ret;
		}
	}

	if (memcmp(h_prime->H, H, sizeof(H))) {
		snprintf(pair_info_path, HDCP2_PAIR_INFO_PATH_LEN, "%s/s%02x%02x%02x%02x%02x.hdcp2",
			pairing_info_dir, hdcp2_key.cert.receiver_id[0], hdcp2_key.cert.receiver_id[1],
			hdcp2_key.cert.receiver_id[2], hdcp2_key.cert.receiver_id[3], hdcp2_key.cert.receiver_id[4]);

		ret = qsee_sfs_rm(pair_info_path);
		if (ret != 0) {
			LOGE("   sfs_rm() FAILED! returned = %d", ret);
		} else {
			LOGD("   sfs_rm() PASSED");
		}
		hdcp2_ctx.is_paired = 0;
		return HDCP2_ERR_INVALID_H;
	}

	// Set new timestamp
#ifndef HDCP2_NO_TIME_LIMIT
	GetTimeStamp(&hdcp2_ctx.ts);
#endif /* !HDCP2_NO_TIME_LIMIT */

	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 res_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)
{
#ifndef HDCP2_NO_TIME_LIMIT
	long duration=0;
	timestamp_t ts= {0, };
#endif /* !HDCP2_NO_TIME_LIMIT */

	HDCP2_PAIRING_INFO_BOX pairing_info_box;

	int ret = HDCP2_OK;

	AKE_SEND_PAIRING_INFO *pairing_info = (AKE_SEND_PAIRING_INFO *) request;
	HDCP2_PAIRING_INFO *p = (HDCP2_PAIRING_INFO *) response;
	LOGD("TZ_AKE_Send_Pairing_Info_T called = %d", command);

	*res_size = sizeof(HDCP2_PAIRING_INFO);
	NULL_ERR(request, HDCP2_TZ_NULL_REQUEST);
	NULL_ERR(response, HDCP2_TZ_NULL_RESPONSE);

	// verify time
#ifndef HDCP2_NO_TIME_LIMIT
	GetTimeStamp(&ts);
	duration = EstimateTime(&hdcp2_ctx.ts, &ts);
	if (duration > HDCP2_VALID_TIME_200MS) {
		ret = HDCP2_ERR_TIME_AKE_SEND_PAIRING_INFO;
		goto Err;
	}
#endif /* !HDCP2_NO_TIME_LIMIT */

	// clear pairing info
	memset(p, 0, sizeof(HDCP2_PAIRING_INFO));

	// copy pairing info
	memcpy(p->Ekm, pairing_info->Ekh_Km, sizeof(pairing_info->Ekh_Km));
	memcpy(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) {
			memcpy(p->m, hdcp2_ctx.rtx, sizeof(hdcp2_ctx.rtx));
			memcpy(p->m + sizeof(hdcp2_ctx.rtx), hdcp2_ctx.rrx, sizeof(hdcp2_ctx.rrx));
		}
	} else {
		memcpy(p->m, hdcp2_ctx.rtx, sizeof(hdcp2_ctx.rtx));
	}

	memcpy(p->receiver_id, hdcp2_key.cert.receiver_id,sizeof(hdcp2_key.cert.receiver_id));

	pairing_info_box.pairing_info = *p;
	TZ_HDCP2_WRAP_NO_SFS((uint8_t *)(&pairing_info_box), sizeof(HDCP2_PAIRING_INFO), response, res_size);

	TZ_TX_LOG_HEX("Encrypted Pairing Info", response, sizeof(HDCP2_PAIRING_INFO));

	hdcp2_ctx.is_paired = 1;

Err:
	return 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 res_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, };

	LC_INIT *lc_init = (LC_INIT *)response;
	*res_size = sizeof(LC_INIT);
	NULL_ERR(lc_init, HDCP2_TZ_NULL_RESPONSE);

	LOGD("TZ_LC_Init_T called = %d", command);

	// 1. randomly choose hdcp2_ctx->rn
	if (TZ_rand(hdcp2_ctx.rn, sizeof(hdcp2_ctx.rn)) != sizeof(hdcp2_ctx.rn)) {
		LOGE("TZ_rand failed");
		return HDCP2_ERR;
	}

	// 2. Copy to payload
	lc_init->msg_id = command;
	memcpy(lc_init->rn, hdcp2_ctx.rn, sizeof(hdcp2_ctx.rn));

	if (hdcp2_ctx.Tx_LC_Precompute && hdcp2_ctx.Rx_LC_Precompute) {
		// key = kd ^ rrx
		memcpy(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, };
			memcpy(temp_rn, hdcp2_ctx.rn, sizeof(hdcp2_ctx.rn));
			memcpy(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));
		}
		memcpy(hdcp2_ctx.L_msb, L, 16);
		memcpy(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 u8 command, u8 *request,
			const u32 req_size, u8 *response, u32 *res_size)
{
	RTT_CHALLENGE *rtt = (RTT_CHALLENGE *) response;
	*res_size = sizeof(RTT_CHALLENGE);
	NULL_ERR(rtt, HDCP2_TZ_NULL_RESPONSE);

	LOGD("TZ_RTT_Challenge_T called = %d", command);

	rtt->msg_id = command;
	memcpy(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 res_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)
{
	int i = 0;
	u8 key[32] = {0, };
	u8 L[32] = {0, };
	int ret = HDCP2_OK;

	LOGD("TZ_LC_Send_L_prime_T called = %d", command);

	if (hdcp2_ctx.Tx_LC_Precompute && hdcp2_ctx.Rx_LC_Precompute) { //req_size == sizeof(LC_SEND_L_PRIME_PC)) //precomputed
		LC_SEND_L_PRIME_PC *l_prime = (LC_SEND_L_PRIME_PC *) request;
		NULL_ERR(request, HDCP2_TZ_NULL_REQUEST);

		if (memcmp(l_prime->L_msb, hdcp2_ctx.L_msb, sizeof(hdcp2_ctx.L_msb)))
			return HDCP2_ERR_INVALID_L;
	} else {
		LC_SEND_L_PRIME *l_prime = (LC_SEND_L_PRIME *) request;
		*res_size = sizeof(u8);
		NULL_ERR(request, HDCP2_TZ_NULL_REQUEST);

		// key = kd ^ rrx
		memcpy(key, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd));
		for (i = 24; i < 32; i++)
			key[i] ^= hdcp2_ctx.rrx[i - 24];

		l_prime->msg_id = command;

		if ((ret = TZ_HMAC_SHA256(L, key, sizeof(key), hdcp2_ctx.rn, sizeof(hdcp2_ctx.rn))) < 0) {
			LOGE("TZ_HMAC_SHA256 failed with ret:%d", ret);
			return ret;
		}

		if (memcmp(l_prime->L, L, sizeof(L)))
			return HDCP2_ERR_INVALID_L;
	}

	LOGD("TZ_LC_Send_L_prime_T left");

	return ret;
}

/**
 * @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 res_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 = HDCP2_OK;
	int i = 0;

	NULL_ERR(response, HDCP2_TZ_NULL_RESPONSE);
	LOGD("TZ_SKE_Send_Eks_T called = %d", command);

	// 1. Generate random ks and riv
	if (TZ_rand(hdcp2_ctx.ks, sizeof(hdcp2_ctx.ks)) != sizeof(hdcp2_ctx.ks)) {
		LOGE("TZ_rand failed");
		return HDCP2_ERR;
	}
	if (TZ_rand(hdcp2_ctx.riv, sizeof(hdcp2_ctx.riv))!=sizeof(hdcp2_ctx.riv)) {
	   LOGE("TZ_rand failed");
	   return HDCP2_ERR;
	}

	if (hdcp2_ctx.version == HDCP2_VERSION_2_3) {
		SKE_SEND_EKS_VER23 *eks = (SKE_SEND_EKS_VER23 *) response;
		*res_size = sizeof(SKE_SEND_EKS_VER23);

		// 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));

		// 2. Key Derivation (dkey2 <= ctr=2)
		if ((ret = TZ_Derivate_dkey(&hdcp2_ctx)) < 0) {
			LOGE("TZ_Derivate_dkey failed with ret:%d", ret);
			return ret;
		}

		// 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];
		}

		// 4. Make payload
		eks->msg_id = command;
		memcpy(eks->riv, hdcp2_ctx.riv, sizeof(hdcp2_ctx.riv));
	} else {
		SKE_SEND_EKS *eks = (SKE_SEND_EKS *) response;
		*res_size = sizeof(SKE_SEND_EKS);

		// 2. Key Derivation (dkey2 <= ctr=2)
		if ((ret = TZ_Derivate_dkey(&hdcp2_ctx)) < 0) {
			LOGE("TZ_Derivate_dkey failed with ret:%d", ret);
			return ret;
		}

		// 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];
		}

		// 4. Make payload
		eks->msg_id = command;
		memcpy(eks->riv, hdcp2_ctx.riv, sizeof(hdcp2_ctx.riv));
	}

	// 5. Set content key
	if ((ret = TZ_Get_ContentKey(hdcp2_ctx.ks, hdcp2_key.lc128, hdcp2_key.ckey)) < 0) {
		LOGE("TZ_Get_ContentKey failed with ret:%d", ret);
		return ret;
	}

	if (TZ_Get_ContentKey(hdcp2_ctx.ks, hdcp2_key.lc128, s_Tx_pKey) != 0) {
		LOGE("TZ_Get_ContentKey failed");
		return HDCP2_ERR_CRYPTO;
	}

#ifndef HDCP2_NO_TIME_LIMIT
	GetTimeStamp(&hdcp2_ctx.time_ske_done);
#endif /* !HDCP2_NO_TIME_LIMIT */

	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 res_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;

	*res_size = sizeof(u8);
	LOGD("TZ_RepeaterAuth_Send_ReceiverId_List_T called: %d", command);
	if (hdcp2_ctx.version >= HDCP2_VERSION_2_1)
		ret = TZ_RepeaterAuth_Send_ReceiverId_List21_T(request);
	else
		ret = TZ_RepeaterAuth_Send_ReceiverId_List20_T(request);

	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 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] = {0, };
	u8 offset=0;
	u8 totalSize = 0;
	int ret = HDCP2_OK;

#ifndef HDCP2_NO_TIME_LIMIT
	timestamp_t ts = {0, };
	long duration = 0;
#endif /* !HDCP2_NO_TIME_LIMIT */

	REPEATERAUTH_SEND_RECEIVER_ID_LIST20 *recidinfo = (REPEATERAUTH_SEND_RECEIVER_ID_LIST20 *)request;
	NULL_ERR(recidinfo, HDCP2_ERR_NULL_REQUEST);
	LOGD("TZ_RepeaterAuth_Send_ReceiverId_List20_T called:");

#ifndef HDCP2_NO_TIME_LIMIT
	 // to verify that this is received within 3 sec
	GetTimeStamp(&ts);
	duration = EstimateTime(&hdcp2_ctx.time_ske_done, &ts);
	if (duration >=  HDCP_REPEATER_RECEIVER_ID_DURATION)
		return HDCP2_ERR_TIME_REPEATER_RECEIVER_ID;
#endif /* !HDCP2_NO_TIME_LIMIT */

	if (recidinfo->DEVICE_COUNT > MAX_RECEIVER_DEVICE)
		return HDCP2_ERR_TOO_MANY_DEVICES;

	for ( int i=0; i< (recidinfo->DEVICE_COUNT); i++ ) {
		memcpy(input + i*5, recidinfo->RECEIVER_IDs[i].RECEIVER_IDj, 5);
	}
	offset = (recidinfo->DEVICE_COUNT)*RECEIVER_ID_SIZE;

	memcpy(input + offset, &(recidinfo->DEPTH), sizeof(recidinfo->DEPTH));
	memcpy(input + offset + sizeof(recidinfo->DEPTH), &(recidinfo->DEVICE_COUNT), sizeof(recidinfo->DEVICE_COUNT));
	memcpy(input + offset + sizeof(recidinfo->DEPTH)+ sizeof(recidinfo->DEVICE_COUNT), &(recidinfo->MAX_DEVS_EXCEEDED), sizeof(recidinfo->MAX_DEVS_EXCEEDED));
	memcpy(input + offset + sizeof(recidinfo->DEPTH)+ sizeof(recidinfo->DEVICE_COUNT)+ sizeof(recidinfo->MAX_DEVS_EXCEEDED), &(recidinfo->MAX_CASCADE_EXCEEDED), sizeof(recidinfo->MAX_CASCADE_EXCEEDED));

	TZ_TX_LOG_HEX("input--", input, sizeof(input));

	totalSize = offset + sizeof(recidinfo->DEPTH)+ sizeof(recidinfo->DEVICE_COUNT)+ sizeof(recidinfo->MAX_DEVS_EXCEEDED) + sizeof(recidinfo->MAX_CASCADE_EXCEEDED);

	if ((ret = TZ_HMAC_SHA256(V, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd), input, totalSize)) < 0) {
		LOGE("TZ_HMAC_SHA256 failed with ret:%d", ret);
		return ret;
	}

	TZ_TX_LOG_HEX("V", V, sizeof(V));
	TZ_TX_LOG_HEX("V_PRIME", recidinfo->V_PRIME, sizeof(recidinfo->V_PRIME));

	if (memcmp(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 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] = {0, };
	u8 input[164] = {0, };
	u8 offset=0;
	u8 totalSize = 0;

	int ret = HDCP2_OK;
#ifndef HDCP2_NO_TIME_LIMIT
	long duration = 0;
	timestamp_t ts = {0, };
#endif /* !HDCP2_NO_TIME_LIMIT */

	REPEATERAUTH_SEND_RECEIVER_ID_LIST21 *recidinfo = (REPEATERAUTH_SEND_RECEIVER_ID_LIST21 *)request;
	NULL_ERR(recidinfo, HDCP2_ERR_NULL_REQUEST);
	LOGD("TZ_RepeaterAuth_Send_ReceiverId_List21_T called:");

#ifndef HDCP2_NO_TIME_LIMIT
	// to verify that this is received within 3 sec
	GetTimeStamp(&ts);
	duration = EstimateTime(&hdcp2_ctx.time_ske_done, &ts);
	if (duration >=  HDCP_REPEATER_RECEIVER_ID_DURATION)
		return HDCP2_ERR_TIME_REPEATER_RECEIVER_ID;
#endif /* !HDCP2_NO_TIME_LIMIT */

	if(recidinfo->DEVICE_COUNT > MAX_RECEIVER_DEVICE)
		return HDCP2_ERR_TOO_MANY_DEVICES;

	for ( int i=0; i< (recidinfo->DEVICE_COUNT); i++ ) {
		memcpy(input + i*5, recidinfo->RECEIVER_IDs[i].RECEIVER_IDj, 5);
	}
	offset = (recidinfo->DEVICE_COUNT)*RECEIVER_ID_SIZE;

	memcpy(input + offset, &(recidinfo->DEPTH), sizeof(recidinfo->DEPTH));
	memcpy(input + offset + sizeof(recidinfo->DEPTH), &(recidinfo->DEVICE_COUNT), sizeof(recidinfo->DEVICE_COUNT));
	memcpy(input + offset + sizeof(recidinfo->DEPTH)+ sizeof(recidinfo->DEVICE_COUNT), &(recidinfo->MAX_DEVS_EXCEEDED), sizeof(recidinfo->MAX_DEVS_EXCEEDED));
	memcpy(input + offset + sizeof(recidinfo->DEPTH)+ sizeof(recidinfo->DEVICE_COUNT)+ sizeof(recidinfo->MAX_DEVS_EXCEEDED), &(recidinfo->MAX_CASCADE_EXCEEDED), sizeof(recidinfo->MAX_CASCADE_EXCEEDED));
	memcpy(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));
	memcpy(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));
	memcpy(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_TX_LOG_HEX("input--", input, sizeof(input));

	if((ret = TZ_HMAC_SHA256(V, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd), input, totalSize)) < 0) {
		LOGE("TZ_HMAC_SHA256 failed with ret:%d", ret);
		return ret;
	}
	memcpy(hdcp2_ctx.repeater_ack, V+16, 16);   //V lsb

	TZ_TX_LOG_HEX("V", V, sizeof(V));
	TZ_TX_LOG_HEX("V_PRIME", recidinfo->V_PRIME, sizeof(recidinfo->V_PRIME));
	if (memcmp(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 res_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)
{
	REPEATERAUTH_SEND_ACK *send_ack = (REPEATERAUTH_SEND_ACK *)response;
	*res_size = sizeof(REPEATERAUTH_SEND_ACK);

	LOGD("TZ_RepeaterAuth_Send_Ack_T Called:%d", command);

	NULL_ERR(send_ack, HDCP2_ERR_NULL_RESPONSE);

	send_ack->msg_id = command;
	memcpy(send_ack->V, hdcp2_ctx.repeater_ack, sizeof(hdcp2_ctx.repeater_ack));

#ifndef HDCP2_NO_TIME_LIMIT
	// Set new timestamp_nwd
	GetTimeStamp(&hdcp2_ctx.ts);
#endif /* !HDCP2_NO_TIME_LIMIT */

	LOGD("TZ_RepeaterAuth_Send_Ack_T Exit");
	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 res_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)
{
	RECEIVER_AUTHSTATUS *status = (RECEIVER_AUTHSTATUS *)request;
	*res_size = sizeof(u8);
	NULL_ERR(status, HDCP2_ERR_NULL_REQUEST);
	LOGD("TZ_Receiver_AuthStatus_T Called:%d", command);
	if (status->REAUTH_Req == 1) {
		//request for reauthentication
		return HDCP2_ERR_REAUTH_REQ;
	}

	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 res_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)
{
	int i = 0;

	REPEATERAUTH_STREAM_MANAGE *send_msg = (REPEATERAUTH_STREAM_MANAGE *)response;
	REPEATERAUTH_STREAM_INFO *send_info = (REPEATERAUTH_STREAM_INFO *)request;
	*res_size = sizeof(REPEATERAUTH_STREAM_MANAGE);

	LOGD("TZ_RepeaterAuth_Stream_Manage_T Called:%d", command);

	NULL_ERR(send_msg, HDCP2_ERR_NULL_RESPONSE);
	NULL_ERR(send_info, HDCP2_ERR_NULL_RESPONSE);

	if (req_size != QSEECOM_ALIGN(sizeof(REPEATERAUTH_STREAM_INFO))) {
		LOGE("TZ_RepeaterAuth_Stream_Manage_T Error, wrong req_size = %d\n", req_size);
		*res_size = 0;
		return HDCP2_ERR_INVALID_INPUT;
	}

	TZ_TX_LOG_HEX("send_info->no_of_streams", send_info->no_of_streams, 2);
	TZ_TX_LOG_HEX("hdcp2_ctx.no_of_streams", hdcp2_ctx.no_of_streams, 2);

	memcpy(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++) {
		memcpy(hdcp2_ctx.multi_stream[i].streamCtrj,
				send_info->multi_streaminfo[i].streamCtrj,
				sizeof(hdcp2_ctx.multi_stream[i].streamCtrj));
		memcpy(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;
	memcpy(send_msg->seq_num_M, hdcp2_ctx.seq_num_M, sizeof(hdcp2_ctx.seq_num_M));
	memcpy(send_msg->k, hdcp2_ctx.no_of_streams, sizeof(send_msg->k));

	if (sizeof(send_msg->max_k_info)/sizeof(Downstream_Params) < hdcp2_ctx.no_of_streams[1]) {
		LOGE("Error, wrong no_of_streams = %d\n", hdcp2_ctx.no_of_streams[1]);
		*res_size = 0;
		return HDCP2_ERR_INVALID_INPUT;
	}

	for (i = 0; i < hdcp2_ctx.no_of_streams[1]; i++) {
		memcpy(send_msg->max_k_info[i].streamCtrj,
				hdcp2_ctx.multi_stream[i].streamCtrj,
				sizeof(send_msg->max_k_info[i].streamCtrj));
		memcpy(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;
	}

#ifndef HDCP2_NO_TIME_LIMIT
	GetTimeStamp(&hdcp2_ctx.time_rep_stream_manage_done);
#endif /* !HDCP2_NO_TIME_LIMIT */

	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 res_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)
{
#ifndef HDCP2_NO_TIME_LIMIT
	long duration;
	timestamp_t ts;
#endif /* !HDCP2_NO_TIME_LIMIT */

	u8 kd[32] = {0, };
	u8 M[32] = {0, };
	u8 input[115] = {0, };
	u8* temp_ptr = input;
	int i = 0;

	int ret = 0;
	REPEATERAUTH_STREAM_READY *status = (REPEATERAUTH_STREAM_READY *) request;
	LOGD("TZ_RepeaterAuth_Stream_Ready_T Called:%d", command);
	*res_size = sizeof(u8);
	NULL_ERR(status, HDCP2_ERR_NULL_REQUEST);

#ifndef HDCP2_NO_TIME_LIMIT
	// to verify that this is received within 3 sec
	GetTimeStamp(&ts);
	duration = EstimateTime(&hdcp2_ctx.time_rep_stream_manage_done, &ts);
	if (duration >=  HDCP2_VALID_TIME_100MS)
		return HDCP2_ERR_TIMEDOUT_M;
#endif /* !HDCP2_NO_TIME_LIMIT */

	//calculate input
	for (i = 0; i < hdcp2_ctx.no_of_streams[1] && hdcp2_ctx.no_of_streams[1] <= 16; i++) {
		memcpy(temp_ptr, hdcp2_ctx.multi_stream[i].streamCtrj,
				sizeof(hdcp2_ctx.multi_stream[i].streamCtrj));
		memcpy(temp_ptr + sizeof(hdcp2_ctx.multi_stream[i].streamCtrj),
				hdcp2_ctx.multi_stream[i].ContentStreamIDj,
				sizeof(hdcp2_ctx.multi_stream[i].ContentStreamIDj));
		memcpy(
				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);
	}

	memcpy(temp_ptr, hdcp2_ctx.seq_num_M, sizeof(hdcp2_ctx.seq_num_M));

	if((ret = TZ_SHA256(kd, (u8*) &hdcp2_ctx.kd, 32)) != 32) {
		LOGE("TZ_SHA256 failed with ret:%d", ret);
		return ret;
	}

	if((ret = TZ_HMAC_SHA256(M, kd, sizeof(kd), input, (hdcp2_ctx.no_of_streams[1] * 7 + 3))) < 0) {
		LOGE("TZ_HMAC_SHA256 failed with ret:%d", ret);
		return ret;
	}

	 if (memcmp(status->M_PRIME, M, 32) != 0) { //V msb
		return HDCP2_ERR_INVALID_M;
	}

	hdcp2_ctx.seq_num_M[2] += 1; //handles for 1 stream now
	LOGD("TZ_RepeaterAuth_Stream_Ready_T Exit");
	return HDCP2_OK;
}

/**
 * @fn uint32 calculateTotalSegNum(tz_buf_array_s_t* memArray);
 * @brief This function is to find the total number of segments for a particular memory array
 * @param memArray - pointer to the memory array.
 * @return uint32 - total number of segments.
 */
uint32 calculateTotalSegNum(tz_buf_array_s_t* memArray)
{
	uint32 index = 0;

	while ((index < TZ_OUT_BUF_MAX) && (SegAddress(MemSeg(memArray, index)) != 0)) {
		index++;
	}

	LOGI("exit. segNum = %u", index);
	return index;
}

/**
 * @fn uint32 calculateTotalSize(tz_buf_array_s_t* memArray);
 * @brief This function to calculate the total size of the memory array.
 * @param memArray - pointer to the memory array.
 * @return uint32 - total size.
 */
uint32 calculateTotalSize(tz_buf_array_s_t* memArray)
{
	uint32 index = 0;
	uint32 totalSize = 0;

	for (; index < MemSegNum(memArray); index++) {
		totalSize += SegSize(MemSeg(memArray, index));
	}

	LOGI("exit. total mem array size = %u", totalSize);
	return totalSize;
}

#define SCATTERED_SIZE	(1024*1024)
#define MAX_INP_CTR	(SCATTERED_SIZE / 16)

/**
 * @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 res_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;
	u8 *input = NULL;
	u8 *output = NULL;
	int enc_ret = HDCP2_ERR;
	u8 p[DECRYPT_BLK_SIZE] = {0, };
	u64 input_ctr = 0;
	int tmp_size = 0;
	uint32 index = 0;
	tz_buf_array_s_t *in_addrs = NULL;
	tz_buf_array_s_t *out_addrs = NULL;
	u8 *in_addr = NULL;
	int in_size = -1;
	u8 *out_addr = NULL;
	int out_size = -1;
	int max_count = 0;

#ifdef TEST_ONLY
	u8 kh[HDCP2_MESSAGE_DIGEST_SIZE] = {0, };
	unsigned char lcTestHash[32] = {0xa6, 0xd8, 0x93, 0x41, 0x33, 0x9b, 0x76,
									0xa5, 0x83, 0x16, 0xe1, 0x24, 0x5c, 0x2e, 0xa2, 0x82, 0x5b,
									0x85, 0x4e, 0xc1, 0x20, 0x92, 0x6e, 0xfc, 0xda, 0x9b, 0x27,
									0x1a, 0x9e, 0x61, 0x74, 0x36};
#endif /* TEST_ONLY */

	CIP_DATA_INFO_TX *data = (CIP_DATA_INFO_TX *)(((u8*)request));

	if (!data || !data->input || !data->length) {
		LOGE("TZ_ENC_Data: Error, Invalid Input\n");
		*res_size = 0; //For error return
		return HDCP2_ERR_INVALID_INPUT;
	}

	if (data->length > MAX_ENCRYPT_BUFFER) {
		LOGE("Error, Data max size\n");
		*res_size = 0; //For error return
		return HDCP2_ERR_INVALID_INPUT;
	}

	if (req_size != ((TZ_OUT_BUF_MAX == 512) ? (sizeof(CIP_DATA_INFO_TX)-8) : sizeof(CIP_DATA_INFO_TX))) {
		LOGE("TZ_ENC_Data: Error, Invalid Req size (%d)\n", req_size);
		*res_size = 0; //For error return
		return HDCP2_ERR_INVALID_INPUT;
	}

	max_count = (int)((data->length + SCATTERED_SIZE - 1) >> 20); //(int)(data->length) / SCATTERED_SIZE;

	in_addrs = (tz_buf_array_s_t *)(&(data->input_data));
	out_addrs = (tz_buf_array_s_t *)(&(data->output_data));

	for (index = 0 ; index < max_count ; index++) {
		/* you can get each phys addr and size info from the following codes. */
		/* But you need to modify these for your own purpose */
		if(in_size != 0) { // 0 means this is continous memory
			in_addr = (uint8*)SegAddress(MemSeg(in_addrs, index));
			in_size = SegSize(MemSeg(in_addrs, index));
		} else {
			in_addr = in_addr + SCATTERED_SIZE;
		}
		LOGD("[%d]in_addr %x, in_size %d length %d", index, in_addr, in_size, data->length);

		if (out_size != 0) {
			out_addr = (uint8*)SegAddress(MemSeg(out_addrs, index));
			out_size = SegSize(MemSeg(out_addrs, index));
		} else {
			out_addr = out_addr + SCATTERED_SIZE;
		}

		LOGD("[%d]in_addr %x, in_size %d, out_addr %x, out_size %d length %d input_ctr %lld str_ctr %u",
				index, in_addr, in_size, out_addr, out_size, data->length, data->inp_ctr, data->str_ctr);

		*res_size = data->length > SCATTERED_SIZE * (index + 1) ? SCATTERED_SIZE : data->length;
		if(index > 0)
			*res_size = data->length - SCATTERED_SIZE * index;

		*res_size = QSEECOM_ALIGN(*res_size);
		tmp_size = *res_size;
		//input_ctr = data->inp_ctr + (index * MAX_INP_CTR);
		input_ctr = data->inp_ctr;

		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)((input_ctr>> 56) & 0xff);
		p[9] = (u8)((input_ctr >> 48) & 0xff);
		p[10] = (u8)((input_ctr>> 40) & 0xff);
		p[11] = (u8)((input_ctr >> 32) & 0xff);
		p[12] = (u8)((input_ctr >> 24) & 0xff);
		p[13] = (u8)((input_ctr>> 16) & 0xff);
		p[14] = (u8)((input_ctr>> 8) & 0xff);
		p[15] = (u8)(input_ctr& 0xff);

		//Input buffer
		input = in_addr;

		if (qsee_register_shared_buffer(input, tmp_size) < 0) {
			*res_size = 0; //For error return
			LOGE("TZ_ENC_Data input qsee_register_shared_buffer failed");
			LOGI("qsee_register_shared_buffer input=0x%.8x", input);
			LOGI("qsee_register_shared_buffer tmp_size=%d", tmp_size);
			LOGI("qsee_register_shared_buffer *res_size=%d", *res_size);
			return HDCP2_ERR_BUFFER_PROTECTION -1001;	//-2305
		}
		if(qsee_prepare_shared_buf_for_secure_read(input, tmp_size) < 0) {
			*res_size = 0; //For error return
			LOGE("TZ_ENC_Data input qsee_prepare_shared_buf_for_secure_read failed");
			qsee_deregister_shared_buffer((void *)input);
			return HDCP2_ERR_BUFFER_PROTECTION -1002;	//-2306
		}

		//Output buffer
		output = out_addr;

		if(qsee_register_shared_buffer(output, tmp_size) < 0) {
			*res_size = 0; //For error return
			LOGE("TZ_ENC_Data output qsee_register_shared_buffer failed");
			qsee_prepare_shared_buf_for_nosecure_read((void *)input, tmp_size);
			qsee_deregister_shared_buffer((void *)input);
			return HDCP2_ERR_BUFFER_PROTECTION - 1003;	//-2307
		}
		if(qsee_prepare_shared_buf_for_secure_read(output, tmp_size) < 0) {
			*res_size = 0; //For error return
			LOGE("TZ_DEC_Data_ION output qsee_prepare_shared_buf_for_secure_read failed");
			qsee_prepare_shared_buf_for_nosecure_read((void *)input, tmp_size);
			qsee_deregister_shared_buffer((void *)input);
			qsee_deregister_shared_buffer((void *)output);
			return HDCP2_ERR_BUFFER_PROTECTION - 1004;	//-2308
		}

		//CRYPTO
		// Verify Key
#ifdef TEST_ONLY
		ret = TZ_SHA256(kh, (u8*)hdcp2_key.lc128, 16);
		if (ret != HDCP2_MESSAGE_DIGEST_SIZE) {
			*res_size = 0; //For error return
			LOGE("TZ_SHA256() FAILED! returned = %ld", ret);
			return KEY_VERIFICATION_FAILED;
		}
		if (!memcmp(kh, lcTestHash, HDCP2_MESSAGE_DIGEST_SIZE)) {
			TZ_TX_LOG_HEX("TZ_ENC_Data key", s_Tx_pKey, DECRYPT_BLK_SIZE);
			TZ_TX_LOG_HEX("hdcp2_ctx.riv", hdcp2_ctx.riv, sizeof(hdcp2_ctx.riv));
		}
#endif /* TEST_ONLY */

#ifdef SW_CRYPTO
		enc_ret = TZ_AES_encrypt_CTR(s_Tx_pKey, DECRYPT_BLK_SIZE, input, tmp_size, output, res_size, p, QSEE_CIPHER_MODE_CTR);
#else
		enc_ret = TZ_AES_encrypt(s_Tx_pKey, DECRYPT_BLK_SIZE, input, tmp_size, output, res_size, p, QSEE_CIPHER_MODE_CTR);
#endif /* SW_CRYPTO */

		*res_size = 0; //For error return

		//Input buffer
		if (qsee_prepare_shared_buf_for_nosecure_read((void *)input, tmp_size) < 0) {
			LOGE("TZ_ENC_Data input qsee_prepare_shared_buf_for_nosecure_read failed");
			ret = HDCP2_ERR_BUFFER_PROTECTION - 1005;	//-2309
		}
		if (qsee_deregister_shared_buffer((void *)input) < 0) {
			LOGE("TZ_ENC_Data input qsee_deregister_shared_buffer failed");
			ret = HDCP2_ERR_BUFFER_PROTECTION - 1006;	//-2310
		}

		//Output buffer
		if (qsee_prepare_shared_buf_for_nosecure_read((void *)output, tmp_size) < 0) {
			LOGE("TZ_ENC_Data output qsee_prepare_shared_buf_for_nosecure_read failed");
			ret = HDCP2_ERR_BUFFER_PROTECTION - 1007;	//-2311
		}
		if (qsee_deregister_shared_buffer((void *)output) < 0) {
			LOGE("TZ_ENC_Data output qsee_deregister_shared_buffer failed");
			ret = HDCP2_ERR_BUFFER_PROTECTION - 1008;	//-2312
		}
	}

	if(enc_ret != HDCP2_OK) {
		LOGE("TZ Encrypt failed");
		return HDCP2_ERR_CRYPTO;
	} else {
		return ret;
	}
}

/**
 * @fn void TZ_Free_Resource_T()
 * @brief This function free hdcp2_ctx and hdcp2_key
 */
void TZ_Free_Resource_T()
{
	memset(&hdcp2_ctx, 0, sizeof(hdcp2_ctx));
	memset(&hdcp2_key, 0, sizeof(hdcp2_key));
}

