/**
 * 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_crypto.c
 * @author
 * @date
 * @brief This file contains all the encryption-decryption function definitions used in HDCP authentication protocol.
 */

#include "tz_hdcp2.h"
#include "tz_hdcp2_crypto.h"
#include "tz_hdcp2_common.h"
#include "qsee_rsa.h"
#include "qsee_hash.h"
#include "qsee_log.h"

#define _POLARSSL_

//CRYPTO IS UNDER TESTING,SO IT MAY HAVE SOME STUBS.CODE CLEANUP WILL BE DONE ONCE TESTING IS COMPLETED

/**
 * @fn int TZ_RSA_PKCS1_OAEP_SHA256(unsigned char *to, int tlen,const unsigned char *from, int flen, unsigned char *param, int plen);
 * @brief - This function is used for RSAES-OAEP encryption.
 * @param to - pointer to the encrypted output data.
 * @param tlen - length of output.
 * @param from - pointer to input data to be encrypted.
 * @param flen - length of input.
 * @param param - pointer to the parameters, if any.
 * @param plen - length of parameter.
 * @return int returns HDCP2_OK in case of success, else error code corresponding to the error
*/
int TZ_RSA_PKCS1_OAEP_SHA256(unsigned char *to, int tlen,
			const unsigned char *from, int flen, unsigned char *param, int plen);

/**
 * @fn int TZ_RSA_VERIFY_PKCS1_OAEP_SHA256(unsigned char *to, int tlen, const unsigned char *from, int flen, int num, const unsigned char *param, int plen)
 * @brief This function is used to unhash the decrypted data. It is called by TZ_RSA_OAEP_decrypt.
 * @param to - the pointer to the decrypted and unhashed data.
 * @param tlen - length of the data.
 * @param from - the pointer to the decrypted but hashed data
 * @param flen - length of from data.
 * @param num - length of HDCP2_SIZE_RECEIVER_PUBKEY.
 * @param param - pointer to parameter, if any.
 * @param plen - length of parameter.
 * @return int returns HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_RSA_VERIFY_PKCS1_OAEP_SHA256(unsigned char *to, int tlen,
			const unsigned char *from, int flen, int num,
			const unsigned char *param, int plen);

/**
 * @fn int TZ_AES_encrypt(uint8_t *key, uint32_t key_len, uint8_t *pt, uint32_t pt_len, uint8_t *ct, uint32_t *ct_len)
 * @brief This function is used for AES ecryption while pairing HDCP Transmitter and HDCP Receiver.
 * @param key - pointer to the key used in AES encryption
 * @param key_len - length of the key
 * @param pt - pointer to the data/information to be encrypted
 * @param pt_len - length of the data
 * @param ct - pointer to the encrypted output
 * @param ct_len - length of the encrypted output
 * @param pP - pointer to the initialization vector
 * @param mode - The cipher mode selected.
 * @return int returns HDCP2_ERR_CRYPTO in case of failure else returns TLAPI_OK.
 */
int TZ_AES_encrypt(uint8_t *key, uint32_t key_len, uint8_t *pt,uint32_t pt_len, uint8_t *ct, uint32_t *ct_len,
			uint8_t *pP, QSEE_CIPHER_MODE_ET mode)
{
	qsee_cipher_ctx *ctx = 0;
	int status = HDCP2_OK;

	LOGD("TZ_AES_ECB_encrypt called");

	if (key == NULL || key_len != DECRYPT_BLK_SIZE) {
		LOGE("Invalid key length");
		return HDCP2_ERR_CRYPTO;
	}

	if (pt == NULL || pt_len == 0) {
		LOGE("Invalid input data length");
		return HDCP2_ERR_CRYPTO;
	}

	if (ct == NULL) {
		LOGE("Invalid output data length");
		return HDCP2_ERR_CRYPTO;
	}

	LOGD("TZ_AES_ECB_encrypt1 called");

	if (qsee_cipher_init(QSEE_CIPHER_ALGO_AES_128, &ctx) < 0) {
		LOGE("TZ_AES_ECB_encrypt::qsee_cipher_init failed");
		status = HDCP2_ERR;
		return status;
	}

	LOGD("TZ_AES_ECB_encrypt2 called");

	if (qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_KEY, key, QSEE_AES128_KEY_SIZE) < 0) {
		LOGE(" TZ_AES_encrypt::qsee_cipher_set_param failed for QSEE_CIPHER_PARAM_KEY");
		status = HDCP2_ERR;
		goto clean;
	}

	LOGD("TZ_AES_ECB_encrypt3 called");

	if (qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_MODE, &mode, sizeof(mode)) < 0) {
		LOGE(" TZ_AES_encrypt::qsee_cipher_set_param failed for QSEE_CIPHER_PARAM_MODE ");
		status = HDCP2_ERR;
		goto clean;
	}

	LOGD("TZ_AES_ECB_encrypt4 called");

	if (pP != NULL && qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_IV, pP, QSEE_AES128_IV_SIZE) < 0) {
		LOGE(" TZ_AES_encrypt::qsee_cipher_set_param failed for QSEE_CIPHER_PARAM_IV");
		status = HDCP2_ERR;
		goto clean;
	}

	LOGD("TZ_AES_ECB_encrypt5 called");

	/*-----------------------------------------------------------------------
	Now encrypt the data
	-------------------------------------------------------------------------*/
	LOGD("Start tz_app_crypto_test packet pt_len = %d", pt_len);

	if ((status = qsee_cipher_encrypt(ctx, pt, pt_len, ct, ct_len)) < 0) {
		//LOGE("pt: %x, pt_len:%d, ct: %x, ct_len: %d", pt, pt_len, ct, ct_len);
		LOGE(" TZ_AES_encrypt::qsee_cipher_encrypt  failed  %d", status);
		status = HDCP2_ERR;
		goto clean;
	}
	LOGD("TZ_AES_ECB_encrypt6 called");

clean:
	if (ctx) {
		LOGD(" TZ_AES_encrypt::qsee_cipher_free_ctx  calling   ");
		qsee_cipher_free_ctx(ctx);
		ctx = 0;
	}

	LOGD(" TZ_AES_encrypt Left   ");

	return status;
}

#ifdef SW_CRYPTO
/**
 * @fn int TZ_AES_encrypt_CTR(uint8_t *key, uint32_t key_len, uint8_t *pt,uint32_t pt_len, uint8_t *ct, uint32_t *ct_len, uint8_t *pP, SW_CipherModeType mode)
 * @brief This function is used for AES ecryption while pairing HDCP Transmitter and HDCP Receiver.
 * @param key - pointer to the key used in AES encryption
 * @param key_len - length of the key
 * @param pt - pointer to the data/information to be encrypted
 * @param pt_len - length of the data
 * @param ct - pointer to the encrypted output
 * @param ct_len - length of the encrypted output
 * @param pP - pointer to the initialization vector
 * @param mode - The cipher mode selected.
 * @return int returns HDCP2_ERR_CRYPTO in case of failure else returns TLAPI_OK.
 */
int TZ_AES_encrypt_CTR(uint8_t *key, uint32_t key_len, uint8_t *pt,uint32_t pt_len, uint8_t *ct, uint32_t *ct_len,
			uint8_t *pP, SW_CipherModeType mode)
{
	CryptoCntxHandle *handle = 0;

	int status = HDCP2_OK;
	IovecListType	ioVecIn;
	IovecListType	ioVecOut;
	IovecType		IovecIn;
	IovecType		IovecOut;
	SW_CipherEncryptDir dir = SW_CIPHER_ENCRYPT;

	ioVecIn.iov = &IovecIn;
	ioVecIn.size = 1;
	ioVecOut.iov = &IovecOut;
	ioVecOut.size = 1;
	ioVecIn.iov[0].dwLen = pt_len;
	ioVecIn.iov[0].pvBase = pt;
	ioVecOut.iov[0].dwLen = pt_len;
	ioVecOut.iov[0].pvBase = ct;

	if (qsee_SW_Cipher_Init(&handle, SW_CIPHER_ALG_AES128) != 0) {
		LOGE("TZ_AES_ECB_encrypt::qsee_cipher_init failed");
		status = HDCP2_ERR;
		return status;
	}

	if (qsee_SW_Cipher_SetParam(handle, SW_CIPHER_PARAM_KEY, key, QSEE_AES128_KEY_SIZE)!=0) {
		LOGE(" TZ_AES_encrypt::qsee_cipher_set_param failed for QSEE_CIPHER_PARAM_KEY");
		status = HDCP2_ERR;
		goto clean;
	}

	if (qsee_SW_Cipher_SetParam(handle, SW_CIPHER_PARAM_DIRECTION, &dir, sizeof(SW_CipherEncryptDir)) != 0) {
		LOGE("qsee_SW_Cipher_SetParam API failed");
		status = HDCP2_ERR;
	}

	if (qsee_SW_Cipher_SetParam(handle, SW_CIPHER_PARAM_MODE, &mode, sizeof(mode))!=0) {
		LOGE(" TZ_AES_encrypt::qsee_cipher_set_param failed for QSEE_CIPHER_PARAM_MODE ");
		status = HDCP2_ERR;
		goto clean;
	}

	if (qsee_SW_Cipher_SetParam(handle, SW_CIPHER_PARAM_IV, pP, SW_AES_IV_SIZE)!=0) {
		LOGE(" TZ_AES_encrypt::qsee_cipher_set_param failed for QSEE_CIPHER_PARAM_IV");
		status = HDCP2_ERR;
		goto clean;
	}

	if ((status = qsee_SW_CipherData(handle, ioVecIn, &ioVecOut)) !=0 ) {
		LOGE(" TZ_AES_encrypt::qsee_cipher_encrypt  failed  %d", status);
		status = HDCP2_ERR;
		goto clean;
	}

clean:
	if (handle) {
		LOGD(" TZ_AES_encrypt::qsee_cipher_free_ctx  calling   ");
		qsee_SW_Cipher_DeInit(&handle, SW_CIPHER_ALG_AES128);
		handle = 0;
	}

	return status;
}
#endif /* SW_CRYPTO */

/**
 * @fn int TZ_AES_decrypt(uint8_t *key, uint32_t key_len, uint8_t *ct, uint32_t ct_len, uint8_t *dt, uint32_t *dt_len, uint8_t *pP, QSEE_CIPHER_MODE_ET mode)
 * @brief This function is used for AES deryption.
 * @param key - pointer to the key used in AES decryption
 * @param key_len - length of the key
 * @param ct - pointer to the encrypted data
 * @param ct_len - length of the data
 * @param dt - pointer to the decrypted output
 * @param dt_len - length of the decrypted output
 * @param pP - pointer to the initialization vector
 * @param mode - Cipher mode selected.
 * @return int returns TLAPI_OK in case of success else returns corresponding error code.
 */
int TZ_AES_decrypt(uint8_t *key, uint32_t key_len, uint8_t *ct, uint32_t ct_len, uint8_t *dt, uint32_t *dt_len,
			uint8_t *pP, QSEE_CIPHER_MODE_ET mode)
{
	qsee_cipher_ctx *ctx = 0;
	int status = 0;
	*dt_len = ct_len;

	LOGD("TZ_AES_decrypt called ct_len = %d",ct_len);

	if (key == NULL || key_len != 16) {
		LOGE("Invalid key length");
		return HDCP2_ERR;
	}

	if (ct == NULL || ct_len == 0) {
		LOGE("Invalid input data length");
		return HDCP2_ERR;
	}

	if (dt == NULL) {
		LOGE("Invalid output data length");
		return HDCP2_ERR;
	}

	if (qsee_cipher_init(QSEE_CIPHER_ALGO_AES_128, &ctx) < 0) {
		LOGE("TZ_AES_decrypt::qsee_cipher_init  failed ");
		status = HDCP2_ERR;
		goto clean;
	}
	if (qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_KEY, key, QSEE_AES128_KEY_SIZE) < 0) {
		LOGE("TZ_AES_decrypt::qsee_cipher_set_param QSEE_CIPHER_PARAM_KEY failed ");
		status = HDCP2_ERR;
		goto clean;
	}
	if (qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_MODE, &mode, sizeof(mode)) < 0) {
		LOGE("TZ_AES_decrypt::qsee_cipher_set_param  failed  QSEE_CIPHER_PARAM_MODE");
		status = HDCP2_ERR;
		goto clean;
	}
	if (pP != NULL && qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_IV, pP, QSEE_AES128_IV_SIZE) < 0) {
		LOGE(" TZ_AES_decrypt::qsee_cipher_set_param failed for QSEE_CIPHER_PARAM_IV");
		status = HDCP2_ERR;
		goto clean;
	}

	/*-----------------------------------------------------------------------
	Now decrypt  the data
	-------------------------------------------------------------------------*/
	if ((status = qsee_cipher_decrypt(ctx, ct, ct_len, dt, dt_len)) < 0) {
		LOGE("TZ_AES_decrypt::qsee_cipher_decrypt  failed %d", status);
		status = HDCP2_ERR;
		goto  clean;
	}
	LOGD("Start tz_app_crypto_test after decrypt  dt_len = 0x%x", *dt_len);

clean:
	if (ctx) {
		qsee_cipher_free_ctx(ctx);
		ctx = 0;
	}

	LOGD("TZ_AES_decrypt Left ");

	return status;
}

/**
 * @fn int TZ_Get_ContentKey(uint8_t *ks, uint8_t *lc128, uint8_t* pKey)
 * @brief This function is used to compute the Key used in AES module.
 * @param ks - session key.
 * @param lc128 - constant global key.
 * @param pKey - resultant output key
 * @return int returns HDCP2_OK in case of success.
 */
int TZ_Get_ContentKey(uint8_t *ks, uint8_t *lc128, uint8_t* pKey)
{
	int ret = HDCP2_OK;
	int i = 0;

	if (pKey == NULL || ks == NULL || lc128 == NULL) {
		ret = HDCP2_ERR_INVALID_INPUT;
		goto err;
	}

	for (i = 0; i < 16; i++)
		pKey[i] = ks[i] ^ lc128[i];

err:
	return ret;
}

/**
 * @fn int TZ_rand(uint8_t *data, const int length)
 * @brief This function generates the pseudo - random number of length given as parameter.
 * @param data - type of pseudo random to be generated.
 * @param length - length of pseudo random to be generated.
 * @return int - returns length of generated pseudo-random number if successful else returns HDCP2_ERR_CRYPTO.
 */
int TZ_rand(uint8_t *data, const int length)
{
	int k = 0;

	LOGD("TZ_rand called");
	k = qsee_prng_getdata(data, length);
	if(k < length)
		return HDCP2_ERR;

	return length;
}

/**
 * @fn void TZ_IncreaseCtr(TZ_HDCP2_CTX *hdcp_ctx)
 * @brief This function is used to increase the counter in AES counter module.
 * @param hdcp_ctx - pointer to the HDCP context.
 * @return void
 */
void TZ_IncreaseCtr(TZ_HDCP2_CTX *hdcp_ctx)
{
	int i = 0;

	for (i = 7; i >= 0; i--) {
		hdcp_ctx->ctr[i] = hdcp_ctx->ctr[i] == MAX_INCREASE_COUNTER ? 0 : hdcp_ctx->ctr[i] + 1;
		if (hdcp_ctx->ctr[i] != 0)
			break;
	}
}

/**
 * @fn int TZ_Derivate_dkey(TZ_HDCP2_CTX *hdcp_ctx)
 * @brief This function is used for Key derivation.
 * @param hdcp_ctx - pointer to the HDCP context.
 * @return int returns HDCP2_OK in case of success else returns (HDCP2_ERR_TRUSTZONE_BASE - 61).
 */
int TZ_Derivate_dkey(TZ_HDCP2_CTX *hdcp_ctx)
{
	int i = 0;
	uint8_t key[16];
	uint8_t input[16] = {0, };
	uint32_t ct_len = 16;
	int ret = 0;

	if (hdcp_ctx->version >= HDCP2_VERSION_2_2 && hdcp_ctx->transmitter_info.VERSION >= 0x02 && hdcp_ctx->receiver_info.VERSION >= 0x02) {
		//IV = rtx || (rrx XOR ctr)
		u8 temp[8] = {0, };
		for(i = 7; i >= 0; i--)
			temp[i] = hdcp_ctx->rrx[i] ^ hdcp_ctx->ctr[i];

		memcpy(input, hdcp_ctx->rtx, sizeof(hdcp_ctx->rtx));
		memcpy(input + sizeof(hdcp_ctx->rtx), temp, sizeof(temp));
	} else {
		// input = rx || ctr
		memcpy(input, hdcp_ctx->rtx, sizeof(hdcp_ctx->rtx));
		memcpy(input + 8, hdcp_ctx->ctr, sizeof(hdcp_ctx->ctr));
	}

	// key = km XOR rn
	memcpy(key, hdcp_ctx->pairing_info.km, sizeof(key));
	for (i = 8; i < 16; i++)
		key[i] ^= hdcp_ctx->rn[i - 8];

	ret = TZ_AES_encrypt(key, 16, input, 16, hdcp_ctx->dkey, &ct_len, NULL, QSEE_CIPHER_MODE_ECB);
	if (ret < HDCP2_OK)
		return ret;

	if (ct_len != 16)
		return HDCP2_ERR_TRUSTZONE_BASE-61;

	TZ_IncreaseCtr(hdcp_ctx);

	return HDCP2_OK;
}

/**
 * @fn int TZ_SHA256(uint8_t *md, uint8_t *in, const int inlen)
 * @brief This is the hashing function used in cryptographic signature calculated on receiver certificate.
 * @param md - pointer to the key used in hash function.
 * @param in - pointer to the data to be hashed.
 * @param inlen - length of the data
 * @return int - returns 32 if success else returns HDCP2_ERR_CRYPTO.
 */
int TZ_SHA256(uint8_t *md, uint8_t *in, const int inlen)
{
	int ret = HDCP2_ERR_CRYPTO;
	qsee_hash_ctx *hash_ctx;
	size_t outlen = HDCP2_MESSAGE_DIGEST_SIZE;
	unsigned char emptyDigest[] = {0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C,
							0x14, 0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24, 0x27, 0xAE,
							0x41, 0xE4, 0x64, 0x9B, 0x93, 0x4C, 0xA4, 0x95, 0x99, 0x1B, 0x78,
							0x52, 0xB8, 0x55};

	if (md == NULL) {
		return ret;
	}

	if (in == NULL || inlen == 0) {
		memcpy(md, emptyDigest, sizeof(emptyDigest));
		return outlen;
	}

	ret = qsee_hash_init(QSEE_HASH_SHA256, &hash_ctx); // 3 == QSEE_HASH_ALGO_ET.QSEE_HASH_SHA256
	if ( ret < 0) {
		LOGE("qsee_hash_init  Failed");
		return ret;
	}

	ret = qsee_hash(QSEE_HASH_SHA256, in, inlen, md, sizeof(emptyDigest));
	if ( ret < 0) {
		LOGE("qsee_hash  Failed");
		qsee_hash_free_ctx(hash_ctx);
		return ret;
	}
	qsee_hash_free_ctx(hash_ctx);

	return outlen;
}

/**
 * @fn int TZ_HMAC_SHA256(uint8_t *md, uint8_t *key, const int keylen, uint8_t *in, const int inlen)
 * @brief This is hashing function used to calculate the hash value of the data given, *in, using hashing key *md.
 * @param md - output pointer
 * @param key - pointer to the key used in hash function.
 * @param keylen - length of the key
 * @param in - data to be hashed
 * @param inlen - length of the data
 * @return int returns HDCP2_ERR_CRYPTO
 */
int TZ_HMAC_SHA256(uint8_t *md, uint8_t *key, const int keylen, uint8_t *in,
			const int inlen)
{
	int ret = 0;

	ret =  qsee_hmac(QSEE_HMAC_SHA256,in,inlen,key,keylen,md);
	if ( ret < 0) {
		LOGE("TZ_HMAC_SHA256  failed = %d ",ret);
	}

	return ret;
}

/**
 * @fn int TZ_RSA_OAEP_encrypt(TZ_HDCP2_CTX *hdcp_ctx, HDCP2_KEY *hdcp2_key, uint8_t *in, uint8_t *out)
 * @brief This function is used for the encryption of data using RSAES-OAEP encryption scheme.
 * @param hdcp_ctx - pointer to the HDCP context.
 * @param hdcp2_key - pointer to the key used in the data encryption.
 * @param in - pointer to the data to be encrypted.
 * @param out - pointer to the output encrypted data.
 * @return int - returns length of the encrypted output.
 */

int TZ_RSA_OAEP_encrypt(TZ_HDCP2_CTX *hdcp_ctx, HDCP2_KEY *hdcp2_key, uint8_t *in, uint8_t *out)
{
	int ret = HDCP2_OK;
	int length = HDCP2_SIZE_RECEIVER_PUBKEY;
	QSEE_RSA_KEY *key = NULL;
	QSEE_S_BIGINT rsa_mod,rsa_pbk_exp;
	QSEE_RSA_OAEP_PAD_INFO oaep_pad_info = {QSEE_HASH_IDX_SHA256, NULL, 0};

	memset(out, 0, HDCP2_SIZE_RECEIVER_PUBKEY);
	LOGD("TZ_RSA_OAEP_encrypt called ");

	key = (QSEE_RSA_KEY*)qsee_malloc(sizeof(QSEE_RSA_KEY));
	if (NULL == key) {
		return -2000;//-E_FAILURE;
	}

	memset(key, 0, sizeof(QSEE_RSA_KEY));

	/*load public key */
	if (qsee_BIGINT_read_unsigned_bin(&(rsa_mod.bi), hdcp2_key->cert.publickey_n, sizeof(hdcp2_key->cert.publickey_n)) ||
		qsee_BIGINT_read_unsigned_bin(&(rsa_pbk_exp.bi), hdcp2_key->cert.publickey_e, sizeof(hdcp2_key->cert.publickey_e))) {
		LOGE("read_unsigned_bin() failed" );
	} else {
		key->e = &rsa_pbk_exp;
		key->N = &rsa_mod;
	}

	key->type = QSEE_RSA_KEY_PUBLIC;
	key->bitLength = 1024;
	ret = qsee_rsa_encrypt(key, QSEE_RSA_PAD_PKCS1_OAEP, &oaep_pad_info,
					in, 16, out, (int*)&length);
	if (CE_SUCCESS != ret) {
		LOGE("qsee_rsa_encrypt failed ret :%d",ret);
	}

	qsee_free(key);

	if( ret != 0 )
		return ret;

	LOGD("TZ_RSA_OAEP_encrypt Left");

	return length;
}

/**
 * @fn int TZ_RSA_OAEP_decrypt(TZ_HDCP2_CTX *hdcp_ctx, HDCP2_KEY *hdcp2_key, uint8_t *in, uint8_t *out)
 * @brief This function is used for the decryption of data using RSAES-OAEP scheme.
 * @param hdcp_ctx - pointer to the HDCP context.
 * @param hdcp2_key - pointer to the key used in the data decryption.
 * @param in - pointer to the data to be decrypted.
 * @param out - pointer to the decrypted data.
 * @return int returns HDCP2_OK in success else returns corresponding error code.
 */
int TZ_RSA_OAEP_decrypt(TZ_HDCP2_CTX *hdcp_ctx, HDCP2_KEY *hdcp2_key,
			uint8_t *in, uint8_t *out)
{
	int ret = HDCP2_OK;
	int length = HDCP2_SIZE_RECEIVER_PUBKEY;
	QSEE_RSA_KEY *key = NULL;
	QSEE_S_BIGINT rsa_mod,rsa_pbk_exp,rsa_prime1,rsa_prime2,rsa_exp1,rsa_exp2,rsa_coef;
	QSEE_RSA_OAEP_PAD_INFO  oaep_pad_info = {QSEE_HASH_IDX_SHA256, NULL, 0};

	memset(out, 0, HDCP2_SIZE_RECEIVER_PUBKEY);
	LOGD("TZ_POLARSSL_RSA_OAEP_decrypt called \n");

	key = (QSEE_RSA_KEY*)qsee_malloc(sizeof(QSEE_RSA_KEY));
	if (NULL == key) {
		return -2000; //-E_FAILURE;
	}

	memset(key, 0, sizeof(QSEE_RSA_KEY));

	/*load public key */
	if (qsee_BIGINT_read_unsigned_bin(&(rsa_mod.bi), hdcp2_key->cert.publickey_n, sizeof(hdcp2_key->cert.publickey_n)) ||
		qsee_BIGINT_read_unsigned_bin(&(rsa_pbk_exp.bi), hdcp2_key->cert.publickey_e, sizeof(hdcp2_key->cert.publickey_e)) ||
		qsee_BIGINT_read_unsigned_bin(&(rsa_prime1.bi), hdcp2_key->private_key.p, sizeof(hdcp2_key->private_key.p)) || 
		qsee_BIGINT_read_unsigned_bin(&(rsa_prime2.bi), hdcp2_key->private_key.q, sizeof(hdcp2_key->private_key.q)) || 
		qsee_BIGINT_read_unsigned_bin(&(rsa_exp1.bi), hdcp2_key->private_key.dmp1, sizeof(hdcp2_key->private_key.dmp1)) ||
		qsee_BIGINT_read_unsigned_bin(&(rsa_exp2.bi), hdcp2_key->private_key.dmq1, sizeof(hdcp2_key->private_key.dmq1)) || 
		qsee_BIGINT_read_unsigned_bin(&(rsa_coef.bi), hdcp2_key->private_key.iqmp, sizeof(hdcp2_key->private_key.iqmp)) ) {
		LOGE("read_unsigned_bin() failed" );
	} else {
		key->e = &rsa_pbk_exp;
		key->N = &rsa_mod;
		key->p = &rsa_prime1;
		key->q = &rsa_prime2;
		key->qP = &rsa_coef;
		key->dP = &rsa_exp1;
		key->dQ = &rsa_exp2;
	}

	key->type = QSEE_RSA_KEY_PRIVATE_CRT;
	//key->bitLength = 1024;

	LOGI("%d, %d",strlen((const char *)in),strlen((const char *)out) );

	ret = qsee_rsa_decrypt(key, QSEE_RSA_PAD_PKCS1_OAEP, &oaep_pad_info,
							   in, 128, out, (int*)&length);
	LOGI("%d, %d",strlen((const char *)in),strlen((const char *)out) );
	if (CE_SUCCESS != ret) {
		LOGE("qsee_rsa_decrypt failed ret :%d",ret);
	}

	qsee_free(key);

	if( ret != 0 )
		return -1;

	return length;
}

/**
 * @fn int TZ_PKCS1_MGF1(unsigned char *mask, long len, const unsigned char *seed, long seedlen)
 * @brief This function is used for mask generation which uses TZ_SHA256 internally.
 * @param mask - pointer to the bits used for masking the data.
 * @param len - length of the mask.
 * @param seed - pointer to the extra bits used for seeding the input data.
 * @param seedlen - length of the seed number.
 * @return int returns length of the output
 */
int TZ_PKCS1_MGF1(unsigned char *mask, long len, const unsigned char *seed,
			long seedlen)
{
	long i, outlen = 0;
	unsigned char md[32];
	unsigned char buffer[128] = {0, };
	int mdlen = 32;

	for (i = 0; outlen < len; i++) {
		memcpy(buffer, seed, seedlen);
		buffer[seedlen] = (unsigned char) ((i >> 24) & 255);
		buffer[seedlen + 1] = (unsigned char) ((i >> 16) & 255);
		buffer[seedlen + 2] = (unsigned char) ((i >> 8)) & 255;
		buffer[seedlen + 3] = (unsigned char) (i & 255);

		if (outlen + mdlen <= len) {
			if (TZ_SHA256(mask + outlen, buffer, seedlen + 4) < 0) {
				return HDCP2_ERR_CRYPTO;
			}
			outlen += mdlen;
		} else {
			if (TZ_SHA256(md, buffer, seedlen + 4) < 0) {
				return HDCP2_ERR_CRYPTO;
			}
			memcpy(mask + outlen, md, len - outlen);
			outlen = len;
		}
	}

	return len;
}

/**
 * @fn int TZ_RSA_PKCS1_OAEP_SHA256(unsigned char *to, int tlen,const unsigned char *from, int flen, unsigned char *param, int plen);
 * @brief - This function is used for RSAES-OAEP encryption.
 * @param to - pointer to the encrypted output data.
 * @param tlen - length of output.
 * @param from - pointer to input data to be encrypted.
 * @param flen - length of input.
 * @param param - pointer to the parameters, if any.
 * @param plen - length of parameter.
 * @return int returns HDCP2_OK in case of success, else error code corresponding to the error
*/
int TZ_RSA_PKCS1_OAEP_SHA256(unsigned char *to, int tlen, const unsigned char *from, int flen, unsigned char *param, int plen)
{
	int i, digestlen = 32, emlen = tlen - 1;
	unsigned char *db, *seed;
	unsigned char dbmask[256] = {0, }, seedmask[32] = {0, };
	LOGD("TZ_RSA_PKCS1_OAEP_SHA256 called ");

	if (flen > emlen - 2 * digestlen - 1) {
		LOGE("TZ_RSA_PKCS1_OAEP_SHA256  :: HDCP2_ERR_CRYPTO called ");
		return HDCP2_ERR_CRYPTO;
	}

	if (emlen < 2 * digestlen + 1) {
		LOGE("TZ_RSA_PKCS1_OAEP_SHA256  :: HDCP2_ERR_CRYPTO called  pt 1");
		return HDCP2_ERR_CRYPTO;
	}

	to[0] = 0;
	seed = to + 1;
	db = to + digestlen + 1;

	if (TZ_SHA256(db, param, plen) < 0) {
		LOGE("TZ_RSA_PKCS1_OAEP_SHA256  :: HDCP2_ERR_CRYPTO called  pt 2");
		return HDCP2_ERR_CRYPTO;
	}

	memset(db + digestlen, 0, emlen - flen - 2 * digestlen - 1);
	db[emlen - flen - digestlen - 1] = 0x01;
	memcpy(db + emlen - flen - digestlen, from, (unsigned int) flen);

	if (TZ_rand(seed, digestlen) <= 0) {
		LOGE("TZ_RSA_PKCS1_OAEP_SHA256  :: HDCP2_ERR_CRYPTO called  pt 3 TZ_rand");
		return HDCP2_ERR_CRYPTO;
	}

	if (TZ_PKCS1_MGF1(dbmask, emlen - digestlen, seed, digestlen) < 0) {
		LOGE("TZ_RSA_PKCS1_OAEP_SHA256  :: HDCP2_ERR_CRYPTO called  pt 3 TZ_PKCS1_MGF1");
		return HDCP2_ERR_CRYPTO;
	}

	for (i = 0; i < emlen - digestlen; i++)
	db[i] ^= dbmask[i];

	if (TZ_PKCS1_MGF1(seedmask, digestlen, db, emlen - digestlen) < 0) {
		LOGE("TZ_RSA_PKCS1_OAEP_SHA256  :: HDCP2_ERR_CRYPTO called  pt 3 TZ_PKCS1_MGF1 2nd call");
		return HDCP2_ERR_CRYPTO;
	}

	for (i = 0; i < digestlen; i++)
		seed[i] ^= seedmask[i];

	LOGD("TZ_RSA_PKCS1_OAEP_SHA256  Left = %d\n",tlen);

	return tlen;
}

/**
 * @fn int TZ_RSA_VERIFY_PKCS1_OAEP_SHA256(unsigned char *to, int tlen, const unsigned char *from, int flen, int num, const unsigned char *param, int plen)
 * @brief This function is used to unhash the decrypted data. It is called by TZ_RSA_OAEP_decrypt.
 * @param to - the pointer to the decrypted and unhashed data.
 * @param tlen - length of the data.
 * @param from - the pointer to the decrypted but hashed data
 * @param flen - length of from data.
 * @param num - length of HDCP2_SIZE_RECEIVER_PUBKEY.
 * @param param - pointer to parameter, if any.
 * @param plen - length of parameter.
 * @return int returns HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_RSA_VERIFY_PKCS1_OAEP_SHA256(unsigned char *to, int tlen,
			const unsigned char *from, int flen, int num,
			const unsigned char *param, int plen)
{
	int i = -1, digestlen = 32, dblen = -1, mlen = -1;
	const unsigned char *maskeddb;
	int lzero = -1;
	unsigned char db[256] = {0, }, seed[32], phash[32];
	unsigned char *padded_from;
	int bad = 0;

	if (--num < 2 * digestlen + 1)
		return HDCP2_ERR_TRUSTZONE_BASE-1;

	lzero = num - flen;
	if (lzero < 0) {
		lzero = 0;
		flen = num;
	}

	dblen = num - digestlen;
	if (dblen > (MAX_HASH_BUFFER - digestlen) / 2) {
		return HDCP2_ERR_TRUSTZONE_BASE-1;
	}
	padded_from = db + dblen;
	if (lzero > MAX_HASH_BUFFER - dblen || flen > MAX_HASH_BUFFER - dblen) {
		return HDCP2_ERR_TRUSTZONE_BASE-1;
	}
	memset(padded_from, 0, lzero);

	bad = 0;
	memcpy(padded_from, from + 1, flen);

	maskeddb = padded_from + digestlen;
	if (TZ_PKCS1_MGF1(seed, digestlen, maskeddb, dblen) < 0)
		return HDCP2_ERR_TRUSTZONE_BASE-2;

	for (i = 0; i < digestlen; i++)
		seed[i] ^= padded_from[i];

	if (TZ_PKCS1_MGF1(db, dblen, seed, digestlen) < 0)
		return HDCP2_ERR_TRUSTZONE_BASE-3;

	for (i = 0; i < dblen; i++)
		db[i] ^= maskeddb[i];

	if (TZ_SHA256(phash, (u8 *)param, plen) < 0)
		return HDCP2_ERR_TRUSTZONE_BASE-4;

	if (memcmp(db, phash, digestlen) != 0 || bad) {
		return HDCP2_ERR_TRUSTZONE_BASE - 5;
	} else {
		for (i = digestlen; i < dblen; i++)
			if (db[i] != 0x00)
				break;
		if (i == dblen || db[i] != 0x01)
			return HDCP2_ERR_TRUSTZONE_BASE - 6;
		else {
			//everything looks OK
			mlen = dblen - ++i;
			if (tlen < mlen || mlen > HDCP2_SIZE_RECEIVER_PUBKEY) {
				return HDCP2_ERR_TRUSTZONE_BASE - 7;
			} else {
				memcpy(to, db + i, mlen);
			}
		}
	}

	return HDCP2_OK;
}
