/**
 * 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 "tz_hdcp2_crypto.h"

#define NULL_ERR(x,y)       if(!x)return(y);
#define SHA256_CALCULATION(x, y, z, reset, size){	\
	if(TZ_SHA256(x,y,z) < 0){						\
		if(reset != NULL) memset(reset,0,size);		\
		return(HDCP2_ERR_CRYPTO);}}


int TZ_Wrap(u8 *in, const int inlen, u8 *out, int *outlen)
{
	tlApiResult_t tlRet;

#ifdef USE_MC10_WRAPAPI
	tlRet = tlApiWrapObject(in, 0, inlen, out, (size_t *) outlen,
				MC_SO_CONTEXT_TLT, MC_SO_LIFETIME_PERMANENT, NULL);
#else
	tlRet = tlApiWrapObject(in, 0, inlen, out, (size_t *) outlen,
				MC_SO_CONTEXT_TLT, MC_SO_LIFETIME_PERMANENT, NULL, TLAPI_WRAP_DEFAULT);
#endif
	if (TLAPI_OK != tlRet) {
		LOGE("tlApiWrapObject failed with ret=0x%08X, exit\n", tlRet);
		return HDCP2_ERR_CRYPTO;
	}

	LOGD("Wrapped data length = %d \n", *outlen);
	return HDCP2_OK;
}

int TZ_Unwrap(u8 *in, const int inlen, u8 *out, int *outlen)
{
	tlApiResult_t tlRet;

#ifdef USE_MC10_WRAPAPI
	tlRet = tlApiUnwrapObject(in, out, (size_t *) outlen);
#else
	tlRet = tlApiUnwrapObject(in, inlen, out, (size_t *) outlen, TLAPI_UNWRAP_DEFAULT);
#endif

	if (TLAPI_OK != tlRet) {
		LOGE("tlApiUnwrapObject failed with ret=0x%08X, exit\n", tlRet);
		return HDCP2_ERR_CRYPTO;
	}

	LOGD("unwrapped data length = %d \n", *outlen);
	return HDCP2_OK;
}


int TZ_Decode_Key(u8 *encoded, u8 *key)
{
	int i = 0;
	u32 outlen = 16;
	u8 prk[16] = { 0 };

	for (i = 0; i < 16; i++)
		prk[i] = (u8) (0x20 + i);

	// 0 [16] lc key
	TZ_AES_ECB_decrypt(prk, 16, encoded, 16, key, &outlen);
	if (outlen != 16)
		return HDCP2_ERR_TRUSTZONE_DECODE_KEY;

	// 538 [320] private key
	outlen = 320;
	TZ_AES_ECB_decrypt(prk, 16, encoded + 538, 320, key + 538, &outlen);
	if (outlen != 320)
		return HDCP2_ERR_TRUSTZONE_DECODE_KEY;

	return HDCP2_OK;
}


int TZ_Seal_Key(u8 *input, u8 *sealedkey)
{
	int i = 0;
	u32 outlen = 16;
	unsigned char h1[32] = { 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 h2[32] = { 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 };

	memcpy(sealedkey, input, WRAPPED_KEY_SIZE);
	SHA256_CALCULATION (h2, (uint8_t*) (input + PROVISIONING_KEY_SIZE - 16), 16, NULL, 0);
	
	for (i=0; i<16; i++)
		h2[i] ^= h1[i];

	// 0 [16] lc key
	TZ_AES_ECB_encrypt(h2, 16, input, 16, sealedkey, &outlen);
	if (outlen != 16)
		return HDCP2_ERR_TRUSTZONE_ENCODE_KEY;

	// 538 [320] private key
	outlen = 320;
	TZ_AES_ECB_encrypt(h2, 16, input + 538, 320, sealedkey + 538, &outlen);
	if (outlen != 320)
		return HDCP2_ERR_TRUSTZONE_ENCODE_KEY;

	return HDCP2_OK;
}

int TZ_Unseal_Key(u8 *sealedkey, u8 *key)
{
	int i = 0;
	u32 outlen = 16;
	unsigned char h1[32] = { 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 h2[32] = { 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 };

	memcpy(key, sealedkey, WRAPPED_KEY_SIZE);
	SHA256_CALCULATION (h2, (uint8_t*) (sealedkey + PROVISIONING_KEY_SIZE - 16), 16, NULL, 0);
	
	for (i=0; i<16; i++)
		h2[i] ^= h1[i];

	// 0 [16] lc key
	TZ_AES_ECB_decrypt(h2, 16, sealedkey, 16, key, &outlen);
	if (outlen != 16)
		return HDCP2_ERR_TRUSTZONE_DECODE_KEY;

	// 538 [320] private key
	outlen = 320;
	TZ_AES_ECB_decrypt(h2, 16, sealedkey + 538, 320, key + 538, &outlen);
	if (outlen != 320)
		return HDCP2_ERR_TRUSTZONE_DECODE_KEY;

	return HDCP2_OK;
}


int TZ_HDCP2_WRAPKEY(const uint8_t command, uint8_t *request,
			const u32 req_size, uint8_t *response, uint32_t *res_size)
{
	int ret = HDCP2_OK;
	uint8_t sealedkey[WRAPPED_KEY_SIZE] = {0}; // 1008
	*res_size = MC_SO_SIZE(0, WRAPPED_KEY_SIZE);

	NULL_ERR(request, HDCP2_TZ_NULL_REQUEST);
	NULL_ERR(response, HDCP2_TZ_NULL_RESPONSE);

	memcpy(response, request, *res_size);

	if ((ret = TZ_Decode_Key(request, response)) < 0)
		return ret;

	if ((ret = TZ_Seal_Key(response, sealedkey)) < 0)
		return ret;

	// Wrap HDCP 2.x Key
	if (TZ_Wrap(sealedkey, sizeof(HDCP2_KEY), response, (int *) res_size) < 0)
		return HDCP2_ERR_WRAP_KEYBOX3;

	return HDCP2_OK;
}

int TZ_HDCP2_LOADKEY(uint8_t *request, HDCP2_KEY *hdcp2_key)
{
	int length = MC_SO_SIZE(0, WRAPPED_KEY_SIZE);
	HDCP2_KEY *p = (HDCP2_KEY *) request;
	unsigned char lcHash[32] = { 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 };
	unsigned char kh[32] = { 0 };
	uint8_t sealedkey[WRAPPED_KEY_SIZE] = {0}; // WRAPPED_KEY_SIZE = 1008

	memset(hdcp2_key, 0, sizeof(HDCP2_KEY));
	NULL_ERR(p, HDCP2_TZ_NULL_REQUEST);
	
	// Decrypt HDCP 2.x Key
	if (TZ_Unwrap((u8*) request, WRAPPED_KEY_SIZE, (u8*) hdcp2_key, &length) < 0) {
		memset(hdcp2_key, 0, sizeof(HDCP2_KEY));
		return HDCP2_ERR_CRYPTO;
	}

	// Verify Key
	SHA256_CALCULATION(kh, (uint8_t*) hdcp2_key->lc128, 16, hdcp2_key, sizeof(HDCP2_KEY));
	if (memcmp(kh, lcHash, 32) && memcmp(kh, lcTestHash, 32)) {
		memcpy(sealedkey, (u8*) hdcp2_key, length);
		if (TZ_Unseal_Key(sealedkey, (u8*) hdcp2_key) < 0) {
			memset(hdcp2_key, 0, sizeof(HDCP2_KEY));
			return HDCP2_ERR_CRYPTO;
		}

		SHA256_CALCULATION(kh, (uint8_t*) hdcp2_key->lc128, 16, hdcp2_key, sizeof(HDCP2_KEY));
		if (memcmp(kh, lcHash, 32) && memcmp(kh, lcTestHash, 32))
			return HDCP2_ERR_READ_WRAPPED_KEYBOX1;
		else
			LOGI("sealed tzh loaded\n");
	} else {
		LOGI("tzh loaded\n");
	}

	return HDCP2_OK;
}
