/*
 * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
 *
 * PROPRIETARY/CONFIDENTIAL
 *
 * This software is the confidential and proprietary information of Samsung
 * Electronics Co., Ltd. ("Confidential Information"). You shall not disclose such
 * Confidential Information and shall use it only in accordance with the terms of
 * the license agreement you entered into with Samsung Electronics Co., Ltd. ("SAMSUNG")
 * SAMSUNG MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
 * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT
 * NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SAMSUNG SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
 * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */

#include <openssl/mem.h>
#include <string.h>
#include <tee_internal_api.h>
#include <tees_extension.h>

#include "dsms_ta.h"
#include "dsms_ta_internal.h"
#define LOG_TAG "[DSMS-TA]"
#include "log.h"

#define DSMS_TA_FEATURE_CODE_LEN 4
#define DSMS_TA_MAX_DETAIL_LEN 1024
#define DSMS_TA_MAX_VALUE_AS_STRING_LEN 22
#define DSMS_TA_MAX_ENTRY_LEN 1053

static const TEE_UUID TA_UUID_ALLOWLIST[] = {
	{0x10000000, 0, 0, {0, 0, 0, 0, 0, 0, 0, 1}}, // CLIENT_TA
};

static const TEE_UUID CA_UUID_ALLOWLIST[] = {
#ifdef TEEGRIS_V4
	{0x7595ad65, 0x9404, 0x537f, {0x96, 0xe6, 0x40, 0x33, 0xb4, 0x29, 0x16, 0xab}}, // /vendor/bin/dsmsca
#ifdef DEBUG
	{0x56c7630f, 0x7ca6, 0x5eca, {0xad, 0xb2, 0x7b, 0x74, 0x5e, 0x4c, 0x08, 0x26}}, // /vendor/tee/dsms_ca (test only)
#endif
#elif TEEGRIS_V3
	{0xfca8d98e, 0x3f6b, 0x5651, {0x89, 0x6d, 0x9a, 0x3e, 0xab, 0x44, 0xc7, 0x02}}, // /vendor/bin/dsmsca
#ifdef DEBUG
	{0x2bd3fa66, 0x06b1, 0x5346, {0x91, 0x16, 0x60, 0xa1, 0x3e, 0x82, 0x4e, 0x8f}}, // /vendor/tee/dsms_ca (test only)
#endif
#endif
};

static TEE_Result TA_IsReceiverAllowed() {
	uint32_t i, allowlist_len;
	TEE_Result res;
	TEE_Identity identity;

	res = TEE_GetPropertyAsIdentity(TEE_PROPSET_CURRENT_CLIENT,
			"gpd.client.identity", &identity);
	if (res != TEE_SUCCESS) {
		LOGD("Failed to get identity");
		return TEE_ERROR_ACCESS_DENIED;
	}

	LOGD_UUID(identity.uuid);

	if (identity.login != TEE_LOGIN_APPLICATION) {
		LOGD("Wrong login identity: %u", identity.login);
		return TEE_ERROR_ACCESS_DENIED;
	}

	allowlist_len = sizeof(CA_UUID_ALLOWLIST) / sizeof(*CA_UUID_ALLOWLIST);
	for (i = 0; i < allowlist_len; i++) {
		if (CRYPTO_memcmp(&(CA_UUID_ALLOWLIST[i]), &(identity.uuid),
				sizeof(TEE_UUID)) == 0) {
			return TEE_SUCCESS;
		}
	}

	return TEE_ERROR_ACCESS_DENIED;
}

static TEE_Result TA_IsSenderAllowed() {
	uint32_t i, allowlist_len;
	TEE_Result res;
	TEE_Identity identity;

	res = TEE_GetPropertyAsIdentity(TEE_PROPSET_CURRENT_CLIENT,
			"gpd.client.identity", &identity);
	if (res != TEE_SUCCESS) {
		LOGD("Failed to get identity");
		return TEE_ERROR_ACCESS_DENIED;
	}

	LOGD_UUID(identity.uuid);

	if (identity.login != TEE_LOGIN_TRUSTED_APP) {
		LOGD("Wrong login identity: %u", identity.login);
		return TEE_ERROR_ACCESS_DENIED;
	}

	allowlist_len = sizeof(TA_UUID_ALLOWLIST) / sizeof(*TA_UUID_ALLOWLIST);
	for (i = 0; i < allowlist_len; i++) {
		if (CRYPTO_memcmp(&(TA_UUID_ALLOWLIST[i]), &(identity.uuid),
				sizeof(TEE_UUID)) == 0) {
			return TEE_SUCCESS;
		}
	}

	return TEE_ERROR_ACCESS_DENIED;
}

static TEE_Result TA_StorageWrite(void *buffer, uint32_t len) {
	TEE_Result ret;
	TEE_ObjectHandle object = (TEE_ObjectHandle) NULL;
	void *local_buffer;
	uint8_t objectID[TEE_OBJECT_ID_MAX_LEN];
	uint32_t flags = TEE_DATA_FLAG_ACCESS_WRITE |
			 TEE_DATA_FLAG_SHARE_WRITE |
			 TEE_DATA_FLAG_OVERWRITE;

	if (!buffer)
		return TEE_ERROR_BAD_PARAMETERS;

	if (len < 1 || len > DSMS_TA_MAX_ENTRY_LEN)
		return TEE_ERROR_BAD_PARAMETERS;

	TEE_GenerateRandom(objectID, TEE_OBJECT_ID_MAX_LEN);

	local_buffer = TEE_Malloc(len, 0);
	if (!local_buffer)
		return TEE_ERROR_OUT_OF_MEMORY;
	memcpy(local_buffer, buffer, len);

	ret = TEE_CreatePersistentObject(TEE_STORAGE_PRIVATE, objectID,
			TEE_OBJECT_ID_MAX_LEN, flags, (TEE_ObjectHandle) NULL,
			NULL, 0, &object);
	if (ret != TEE_SUCCESS)
		goto out;

	ret = TEE_WriteObjectData(object, local_buffer, len);
	if (ret != TEE_SUCCESS)
		goto out;

out:
	if (object != (TEE_ObjectHandle) NULL)
		TEE_CloseObject(object);

	if (local_buffer)
		TEE_Free(local_buffer);

	return ret;
}

static TEE_Result TA_StorageRead(void *buffer, uint32_t len) {
	TEE_Result ret;
	TEE_ObjectHandle object = (TEE_ObjectHandle) NULL;
	TEE_ObjectEnumHandle objectEnumerator = (TEE_ObjectEnumHandle) NULL;
	TEE_ObjectInfo objectInfo;
	void *local_buffer;
	uint8_t objectID[TEE_OBJECT_ID_MAX_LEN];
	uint32_t objectIDLen;
	uint32_t flags = TEE_DATA_FLAG_ACCESS_READ |
			 TEE_DATA_FLAG_SHARE_READ |
			 TEE_DATA_FLAG_ACCESS_WRITE_META;
	uint32_t read_bytes = 0;

	if (!buffer)
		return TEE_ERROR_BAD_PARAMETERS;

	if (len < 1 || len > DSMS_TA_MAX_ENTRY_LEN)
		return TEE_ERROR_BAD_PARAMETERS;

	local_buffer = TEE_Malloc(len, 0);
	if (!local_buffer)
		return TEE_ERROR_OUT_OF_MEMORY;

	ret = TEE_AllocatePersistentObjectEnumerator(&objectEnumerator);
	if (ret != TEE_SUCCESS)
		goto out;

	ret = TEE_StartPersistentObjectEnumerator(objectEnumerator,
			TEE_STORAGE_PRIVATE);
	if (ret != TEE_SUCCESS)
		goto out;

	ret = TEE_GetNextPersistentObject(objectEnumerator, &objectInfo,
			objectID, &objectIDLen);
	if (ret != TEE_SUCCESS)
		goto out;

	ret = TEE_OpenPersistentObject(TEE_STORAGE_PRIVATE, objectID,
			objectIDLen, flags, &object);
	if (ret != TEE_SUCCESS)
		goto out;

	ret = TEE_ReadObjectData(object, local_buffer, len, &read_bytes);
	if (ret != TEE_SUCCESS)
		goto out;

	memcpy(buffer, local_buffer, len);

out:
	if (object != (TEE_ObjectHandle) NULL)
		TEE_CloseAndDeletePersistentObject1(object);

	if (objectEnumerator != (TEE_ObjectEnumHandle) NULL)
		TEE_FreePersistentObjectEnumerator(objectEnumerator);

	if (local_buffer)
		TEE_Free(local_buffer);

	return ret;
}

TEE_Result TA_CreateEntryPoint(void)
{
	return TEE_SUCCESS;
}

void TA_DestroyEntryPoint(void)
{
}

TEE_Result TA_OpenSessionEntryPoint(uint32_t paramTypes,
		TEE_Param params[4], void **sessionContext)
{
	(void) params;
	(void) sessionContext;

	if (paramTypes != TEE_PARAM_TYPES(TEE_PARAM_TYPE_NONE,
					  TEE_PARAM_TYPE_NONE,
					  TEE_PARAM_TYPE_NONE,
					  TEE_PARAM_TYPE_NONE)) {
		return TEE_ERROR_BAD_PARAMETERS;
	}

	return TEE_SUCCESS;
}

void TA_CloseSessionEntryPoint(void *sessionContext)
{
	(void) sessionContext;
}

TEE_Result TA_InvokeCommandEntryPoint(void *sessionContext,
		uint32_t commandID, uint32_t paramTypes, TEE_Param params[4])
{
	TEE_Result ret = TEE_SUCCESS;

	(void) sessionContext;

	switch (commandID) {
	case DSMS_TA_SEND_MSG:
		LOGD("TA_InvokeCommandEntryPoint: DSMS_TA_SEND_MSG");

		if (paramTypes != TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
						  TEE_PARAM_TYPE_NONE,
						  TEE_PARAM_TYPE_NONE,
						  TEE_PARAM_TYPE_NONE)) {
			return TEE_ERROR_BAD_PARAMETERS;
		}

		if (TA_IsSenderAllowed() != TEE_SUCCESS) {
			LOGE("Denied access to sender");
			return TEE_ERROR_ACCESS_DENIED;
		}

		LOGD("TA_StorageWrite: %s", (char *) params[0].memref.buffer);
		ret = TA_StorageWrite((uint8_t *) params[0].memref.buffer,
				params[0].memref.size);
		if (ret != TEE_SUCCESS)
			LOGE("Writing to storage failed");
		break;

	case DSMS_TA_RECV_MSG:
		LOGD("TA_InvokeCommandEntryPoint: DSMS_TA_RECV_MSG");

		if (paramTypes != TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_OUTPUT,
						  TEE_PARAM_TYPE_NONE,
						  TEE_PARAM_TYPE_NONE,
						  TEE_PARAM_TYPE_NONE)) {
			return TEE_ERROR_BAD_PARAMETERS;
		}

		if (TEES_IsREESharedMemory(TEE_MEMORY_ACCESS_WRITE,
				params[0].memref.buffer,
				params[0].memref.size) != TEE_SUCCESS) {
			LOGE("Memory access check error");
			return TEE_ERROR_ACCESS_DENIED;
		}

		if (TA_IsReceiverAllowed() != TEE_SUCCESS) {
			LOGE("Denied access to receiver");
			return TEE_ERROR_ACCESS_DENIED;
		}

		ret = TA_StorageRead((uint8_t *) params[0].memref.buffer,
				params[0].memref.size);
		switch (ret) {
		case TEE_SUCCESS:
			((char *) params[0].memref.buffer)[params[0].memref.size - 1] = '\0';
			LOGD("TA_StorageRead: %s",
			     (char *) params[0].memref.buffer);
			break;
		case TEE_ERROR_ITEM_NOT_FOUND:
			LOGD("Storage empty");
			break;
		default:
			LOGE("Reading from storage failed");
		}
		break;

	default:
		LOGE("Unknown command ID");
		return TEE_ERROR_BAD_PARAMETERS;
	}

	return ret;
}
