/**
 * 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.
 *
 */

#include <string.h>

#include "tz_hdcp2_crypto.h"
#include "tz_hdcp2.h"

#define NULL_ERR(x,y)	if(!x)return(y)

int TZ_HDCP2_WRAPKEY(const uint8_t command, uint8_t *request,
			const u32 req_size, uint8_t *response, uint32_t *res_size)
{
	NULL_ERR(request, HDCP2_TZ_NULL_REQUEST);
	NULL_ERR(response, HDCP2_TZ_NULL_RESPONSE);
	NULL_ERR(res_size, HDCP2_ERR);

	if (req_size != PROVISIONING_KEY_SIZE || *res_size != WRAPPED_KEY_SIZE) {
		LOGE("Error, Wrap key size\n");
		return HDCP2_ERR;
	}

	TEE_Result TEE_ret = TEE_SUCCESS;
	uint32_t wrap_sz = *res_size;
	uint8_t *wrap = NULL;

	SO_AccessControlInfoType ac_info;
	memset(&ac_info, 0, sizeof ac_info);
	ac_info.access_flags = TA_ID_AC;

	wrap = TEE_Malloc(wrap_sz, 0);
	if (wrap == NULL) {
		LOGE("TZ Wrap Key: Error, TEE malloc failed\n");
		return HDCP2_ERR;
	}

	TEE_ret = TEES_WrapSecureObject((const u8*)request, req_size, wrap, &wrap_sz, &ac_info);

	if (TEE_ret != TEE_SUCCESS) {
		LOGE("TZ Wrap Key: Error, TSL Wrap key failed [TEE_ret: %x]\n", TEE_ret);
		goto err;
	}

	//copy wrapped key to response
	TEE_MemMove(response, wrap, wrap_sz);
	LOGI("TZ Wrap Key: Success\n");

err:
	if (wrap != NULL)
		TEE_Free(wrap);

	return TEE_ret;
}

int TZ_HDCP2_LOADKEY(uint8_t *request, uint32_t req_size, HDCP2_KEY *hdcp2_key)
{
	NULL_ERR(request, HDCP2_TZ_NULL_REQUEST);
	TEE_Result TEE_ret = TEE_SUCCESS;
	uint32_t ret = 0;
	uint32_t unwrap_sz = req_size;
	uint8_t *unwrap = NULL;
	u8 kh[HDCP2_MESSAGE_DIGEST_SIZE] = {0};
	u32 reqsize = req_size;

#ifdef TEEGRIS_V4
	SO_AccessControlInfoType ac_info;
	memset(&ac_info, 0, sizeof ac_info);
	ac_info.access_flags = TA_ID_AC;

	TEEC_UUID uuid = (TEEC_UUID)TA_PROP_UUID;
	memcpy(&ac_info.ta_id, &uuid, sizeof(TEEC_UUID));

	TEE_ret = TEES_CheckSecureObjectCreator((const u8*)request, reqsize, &ac_info);
	if (TEE_ret != TEE_SUCCESS) {
		LOGE("TZ Load Key: Check SecureObject Creator failed with [TEE_ret: %x]\n", TEE_ret);
		ret = HDCP2_TZ_CRYPTO_ERROR;
		goto err;
	}
#endif

	unwrap = TEE_Malloc(req_size, 0);
	if (unwrap == NULL) {
		LOGE("TZ Load Key: Error, TEE malloc failed\n");
		return HDCP2_ERR;
	}

	TEE_ret = TEES_UnwrapSecureObject((const u8*)request, reqsize, unwrap, (size_t *)&unwrap_sz);
	if (TEE_ret != TEE_SUCCESS) {
		LOGE("TZ Load Key: Error, TSL Unwrap key failed with [TEE_ret: %x]\n", TEE_ret);
		ret = HDCP2_TZ_CRYPTO_ERROR;
		goto err;
	}

	if(unwrap_sz != PROVISIONING_KEY_SIZE) {
		LOGE("TZ Load Key: Error, unwrap_sz is invalid %d \n", unwrap_sz);
		ret = HDCP2_ERR;
		goto err;
	}
	//copy unwrapped key to hdcp2_key
	TEE_MemMove((uint8_t*)hdcp2_key, unwrap, unwrap_sz);

	ret = TZ_Decode_Key((u8 *)hdcp2_key, (u8 *)hdcp2_key);
	if (ret != HDCP2_OK) {
		LOGE("TZ_Decode_Key() failed. [ret : %x]\n", ret);
		goto reset;
	}

	ret = TZ_SHA256(kh, (u8*)hdcp2_key->lc128, 16);
	if (ret != HDCP2_MESSAGE_DIGEST_SIZE) {
		LOGE("TZ_SHA256() failed. [ret : %x]\n", ret);
		goto reset;
	}

	ret = TZ_Verify_Key(kh, HDCP2_MESSAGE_DIGEST_SIZE);
	if (ret != HDCP2_OK) {
		LOGE("TZ_Verify_Key() failed. [ret : %d]\n", ret);
		goto reset;
	}

	TZ_HDCP2_Set_Status_UnwrapKey(1);
	LOGI("TZ Load Key: Success\n");
reset:
	if ((ret != HDCP2_OK) && (hdcp2_key != NULL))
		TEE_MemFill(hdcp2_key, 0, sizeof(HDCP2_KEY));
err:
	if (unwrap != NULL)
		TEE_Free(unwrap);

	return ret;
}
