/**
 * 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.
 *
 */
 /* tz_hdcp_key_WRAPAPI.c
 *
 *  Holds all trustzone receiver functions for HDCP2
 *
 */

#include <stdlib.h>
#include "tz_hdcp2_common.h"
#include "tz_hdcp2_crypto.h"
#include "qsee_log.h"
#include "qsee_kdf.h"

#ifdef USE_QSEE_WRAP_WITH_WB
#include "wbAPI/hdcp_wb_aes_ctr.h"
#endif /* USE_QSEE_WRAP_WITH_WB */

/**
 * wrap key without SFS module
 *
 */
int TZ_HDCP2_WRAP_NO_SFS(uint8_t *request, uint32_t req_size, uint8_t *response, uint32_t *res_size)
{
	int res = 0;
	int ret = HDCP2_TZ_OK;
	uint8_t *encrypted_key = NULL;
	uint32_t encrypted_key_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);

	LOGI("TZ_HDCP2_WRAP_NO_SFS call");

	if (!request || !res_size) {
		LOGE("Store: parameter NULL");
		ret = HDCP2_TZ_NULL_REQUEST;
		goto Err;
	}

	if (req_size % HDCP2_AES_KEY_SIZE) {
		LOGE("Store: Data size(%d) not multiple of 16bytes. Plz have a padding", req_size);
		ret = HDCP2_ERR;
		goto Err;
	}

	encrypted_key = qsee_malloc(req_size);
	if (encrypted_key == NULL){
		LOGE("Store: Can't allocate for buffer.");
		ret = HDCP2_TZ_NULL_RESPONSE;
		goto Err;
	}
	encrypted_key_len = req_size;
	memset(encrypted_key, 0, encrypted_key_len);

	// Generate Key label, context via random
	ret = TZ_rand(cipher_key.label, HDCP2_AES_KEY_SIZE);
	if (ret != HDCP2_AES_KEY_SIZE) {
		LOGE("Store: Random Label_Key failed. ret = %d", ret);
		goto Err;
	}

	ret = TZ_rand(cipher_key.context, HDCP2_AES_KEY_SIZE);
	if (ret != HDCP2_AES_KEY_SIZE) {
		LOGE("Store: Random Context_Key failed. ret = %d", ret);
		goto Err;
	}

	// Generate AES_Key by KDF
	res = 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 (res != HDCP2_TZ_OK) {
		LOGE("Store: qsee_kdf failed. ret = %d", ret);
		ret = HDCP2_ERR_KDF;
		goto Err;
	}

	//Encrypt ra key using AES_ECB
	ret = TZ_AES_encrypt(cipher_key.key, HDCP2_AES_KEY_SIZE, request + 1, req_size, encrypted_key, &encrypted_key_len, NULL, QSEE_CIPHER_MODE_ECB);
	if(ret != HDCP2_OK) {
		LOGE("Store: TZ_AES_encrypt failed. ret = %d", ret);
		goto Err;
	}

	// Compact output
	memcpy(response + 1, cipher_key.context, HDCP2_AES_KEY_SIZE);
	memcpy(response + 1 + HDCP2_AES_KEY_SIZE, cipher_key.label, HDCP2_AES_KEY_SIZE);
	memcpy(response + 1 + 2*HDCP2_AES_KEY_SIZE, encrypted_key, encrypted_key_len);
	*res_size = (uint32_t)(2*HDCP2_AES_KEY_SIZE + encrypted_key_len + 1);

Err:
	if (encrypted_key != NULL)
		qsee_free(encrypted_key);

	return ret;
}

/**
 * unwrap key without SFS module
 *
 */
int TZ_HDCP2_LOADKEY_NO_SFS(uint8_t *request, uint32_t req_size, HDCP2_KEY *hdcp2_key, uint8_t enable_wb)
{
	int res = 0;
	int ret = HDCP2_TZ_OK;
	uint8_t *unwrap = NULL;
	uint32_t unwrap_sz = 0;
	uint8_t *decrypted_key = NULL;
	uint32_t decrypted_key_len = 0;
	uint8_t kh[HDCP2_MESSAGE_DIGEST_SIZE] = {0, };
	uint8_t lcHash[HDCP2_MESSAGE_DIGEST_SIZE] = {0x0b, 0xed, 0x21, 0x97, 0x2c, 0x16, 0xe8, 0x11, 0x6e, 0xc2, 0xf8,
												0xed, 0xff, 0x53, 0xe4, 0x5b, 0xec, 0xce, 0x39, 0x9b, 0x84, 0xc0,
												0x3e, 0x54, 0x5d, 0x72, 0xb5, 0x2c, 0x59, 0x73, 0x8f, 0x18};
	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};
	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);

	LOGI("TZ_HDCP2_LOADKEY_NO_SFS call : %d", enable_wb);

	memset(hdcp2_key, 0, sizeof(HDCP2_KEY));
	if (!request || !req_size) {
		LOGE("Load: parameter NULL");
		ret = HDCP2_TZ_NULL_REQUEST;
		goto Err;
	}

	decrypted_key = qsee_malloc(PROVISIONING_KEY_SIZE);
	if (decrypted_key == NULL ){
		LOGE("Load: Can't allocate for buffer 1.");
		ret = HDCP2_TZ_NULL_REQUEST;
		goto Err;
	}
	decrypted_key_len = PROVISIONING_KEY_SIZE;
	memset(decrypted_key, 0, PROVISIONING_KEY_SIZE);

	unwrap = qsee_malloc(PROVISIONING_KEY_SIZE);
	if (unwrap == NULL) {
		LOGE("Load: Can't allocate for buffer 2.");
		ret = HDCP2_TZ_NULL_REQUEST;
		goto Err;
	}
	unwrap_sz = PROVISIONING_KEY_SIZE;
	memset(unwrap, 0, PROVISIONING_KEY_SIZE);

	//take data from efs/cpk/h2k.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, PROVISIONING_KEY_SIZE);

	//Generate AES_Key by KDF functionTZ_Decode_Key
	res = 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 (res != HDCP2_TZ_OK) {
		LOGE("Load: qsee_kdf failed.");
		ret = HDCP2_ERR_KDF;
		goto Err;
	}

	//Decrypt HDCP2_KEY
	ret = TZ_AES_decrypt(cipher_key.key, HDCP2_AES_KEY_SIZE, unwrap, unwrap_sz, decrypted_key, &decrypted_key_len, NULL, QSEE_CIPHER_MODE_ECB);
	if (ret < HDCP2_TZ_OK) {
		LOGE("Load: Decrypt AES_ECB failed");
		goto Err;
	}
	memcpy(hdcp2_key, decrypted_key, PROVISIONING_KEY_SIZE);

	if (enable_wb == 1) {
#ifdef USE_QSEE_WRAP_WITH_WB
		unsigned char iv[16] = {0xa5, 0x25, 0x0c, 0xe3, 0x92, 0x7b, 0xa7, 0xa0, 0x7f, 0xfe, 0x1c, 0x60, 0x98, 0xea, 0xad, 0xf9};

		ret = hdcp_wb_aes_ctr_decrypt(hdcp2_key, LC_128_SIZE, hdcp2_key, &decrypted_key_len, iv);
		if (ret != HDCP2_OK) {
			LOGE("Load: hdcp_wb_aes_ctr_decrypt() FAILED! returned = %d", ret);
			goto Err;
		}
#else
		LOGE("Load: this project does not support WB!");
		ret = HDCP2_TZ_CRYPTO_ERROR;
		goto Err;
#endif /* USE_QSEE_WRAP_WITH_WB */
	} else {
		ret = TZ_Decode_Key((uint8_t *)hdcp2_key, (uint8_t *)hdcp2_key);
		if (ret != HDCP2_OK) {
			LOGE("Load: TZ_Decode_Key() FAILED! returned = %d", ret);
			goto Err;
		}
	}

	// Verify Key
	ret = TZ_SHA256(kh, (uint8_t*)hdcp2_key->lc128, LC_128_SIZE);
	if (ret != HDCP2_MESSAGE_DIGEST_SIZE) {
		LOGE("Load: TZ_SHA256() FAILED! returned = %d", ret);
		ret = KEY_VERIFICATION_FAILED;
		goto Err;
	}

	if (memcmp(kh, lcHash, HDCP2_MESSAGE_DIGEST_SIZE) && memcmp(kh, lcTestHash, HDCP2_MESSAGE_DIGEST_SIZE)) {
		LOGE(" Load: Verify Key Failed: different hash value");
		ret = KEY_VERIFICATION_FAILED;
		goto Err;
	}

	LOGW("TZ_HDCP2_LOADKEY_T sucessfull (%d)", enable_wb);

Err:
	if (decrypted_key != NULL) {
		memset(decrypted_key, 0, PROVISIONING_KEY_SIZE);
		qsee_free(decrypted_key);
		decrypted_key = NULL;
	}

	if (unwrap != NULL) {
		memset(unwrap, 0, PROVISIONING_KEY_SIZE);
		qsee_free(unwrap);
		unwrap = NULL;
	}

	return ret;
}

