#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>
#include <errno.h>

#include "vk_utils.h"
#include "vk_error.h"
#include "vk_log.h"
#include "vk_constants.h"
#include "vk_table.h"
#include "vk_interface.h"
#include "vk_vault_manager.h"
#include "crypto/vk_crypto_aes.h"
#include "crypto/vk_crypto.h"

extern vk_device_info_t g_device_info;

#define MAX_HEX_LEN_FOR_LOGGING 1024
#define A_LOG_LEN 512 // limitted-length

// https://primes.utm.edu/lists/small/10000.txt
#define P0 43
#define P1 53353
#define P2 66169
#define P3 90617

uint32_t make_hash_str(const char* s)
{
	if (s == NULL) return 0;

	uint32_t h = P0;
	while (*s) {
		 h = (h * P1) ^ (s[0] * P2);
		 s++;
	}
	return h % P3;
}

static char* _strtok(char* strToken, const char* strDelimit1, const char* strDelimit2)
{
	static char* pCurrent;
	char* pDelimit1;
	char* pDelimit2;

	if (strToken != NULL)
		pCurrent = strToken;
	else
		strToken = pCurrent;

	if (*pCurrent == '\0')
		return NULL;

	while (*pCurrent) {
		pDelimit1 = (char*)strDelimit1;
		pDelimit2 = (char*)strDelimit2;
		while (*pDelimit1 || *pDelimit2) {
			if (*pCurrent == *pDelimit1 || *pCurrent == *pDelimit2) {
				*pCurrent = 0;
				++pCurrent;
				return strToken;
			}
			++pDelimit1;
			++pDelimit2;
		}
		++pCurrent;
	}

	return strToken;
}

bool isAllZero(const void* buf, int size)
{
	int i;
	char* src = (char*)buf;

	if (src == NULL) {
		LOGE("%s: Source is null\n", __func__);
		return false;
	}

	if (size == 0)
		return true;

	if (size < 0) {
		LOGE("%s: size is wrong\n", __func__);
		return false;
	}

	for (i = 0; i < size; i++) {
		if (src[i] != 0x00) {
			return false;
		}
	}

	return true;
}

char* getVaultNameByClientName(char* clientName)
{
	int i;

	if (clientName == NULL) {
		LOGE("%s: clientName is null\n", __func__);
		return NULL;
	}

	for (i = 0; i < NUM_OF_VAULTS; i++) {
		if (!strcmp(VTAB[i].client_name, clientName)) {
			return (char*)VTAB[i].vault_name;
		}
	}

	LOGE("%s: Invalid client name(%s)\n", __func__, clientName);
	return NULL;
}

int getVaultIndexByName(char* vaultName)
{
	int i;

	if (isAllZero(vaultName, MAX_VAULT_NAME_LEN)) {
		LOGE("%s: vaultName is null\n", __func__);
		return VK_ERR_INVALID_ARGUMENT;
	}

	for (i = 0; i < NUM_OF_VAULTS; i++) {
		if (!strcmp(VTAB[i].vault_name, vaultName)) {
			return i;
		}
	}

	LOGE("%s: Invalid vault name(%s)\n", __func__, vaultName);
	return VK_ERR_INVALID_ARGUMENT;
}

int get_vtab_index(char* vault_name, char* client_name)
{
	int i;

	if (isAllZero(vault_name, MAX_VAULT_NAME_LEN)) {
		LOGE("%s: vault_name is null\n", __func__);
		return VK_ERR_INVALID_ARGUMENT;
	}

	if (isAllZero(client_name, MAX_CLIENT_NAME_LEN)) {
		LOGE("%s: client_name is null\n", __func__);
		return VK_ERR_INVALID_ARGUMENT;
	}

	for (i = 0; i < NUM_OF_VAULTS; i++) {
		if (!strcmp(VTAB[i].vault_name, vault_name)
			&& !strcmp(VTAB[i].client_name, client_name)) {
			return i;
		}
	}

	LOGE("%s: There is no matched item(%s/%s)\n", __func__, vault_name, client_name);
	return VK_ERR_INVALID_ARGUMENT;
}

int swap_vault_open(cmd_req_t* source, cmd_req_t* target, char* vault_name)
{
	if (source == NULL) {
		LOGE("source is null\n");
		return VK_ERR_INVALID_ARGUMENT;
	}

	if (target == NULL) {
		LOGE("target is null\n");
		return VK_ERR_INVALID_ARGUMENT;
	}

	if (vault_name == NULL) {
		LOGE("vault name is null\n");
		return VK_ERR_INVALID_ARGUMENT;
	}

	memset(target, 0, sizeof(cmd_req_t));

	memcpy(target, source, sizeof(cmd_req_t));
	target->vtab_index = getVaultIndexByName(vault_name);
	if (target->vtab_index >= NUM_OF_VAULTS) {
		return VK_ERR_READ_VTAB;
	}

	target->entire_vault_size = VTAB[target->vtab_index].num_blocks * VAULT_SINGLE_BLOCK_LEN;
	target->vault_level = VTAB[target->vtab_index].vault_level;
	target->client_code = VTAB[target->vtab_index].client_code;

	memset(g_device_info.client_crypto_context, 0, MAX_CRYPTO_CONTEXT_LEN);
	memcpy(g_device_info.client_crypto_context, VTAB[target->vtab_index].crypto_context, MAX_CRYPTO_CONTEXT_LEN);

	return VK_SUCCESS;
}

void swap_vault_close(cmd_req_t* source)
{
	if (source != NULL && !isAllZero(g_device_info.client_crypto_context, MAX_CRYPTO_CONTEXT_LEN)) {
		memset(g_device_info.client_crypto_context, 0, MAX_CRYPTO_CONTEXT_LEN);
		memcpy(g_device_info.client_crypto_context, VTAB[source->vtab_index].crypto_context, MAX_CRYPTO_CONTEXT_LEN);
	}
}

void show_req_to_ta(req_to_ta_t* req_to_ta)
{
	LOGI("[req_to_ta - Start]\n");
	if (req_to_ta == NULL) {
		LOGI("req_to_ta is null\n");
		return;
	}
	LOGI("cmd_no : %d\n", req_to_ta->cmd_no);
	LOGI("cmd_id : 0x%08x\n", req_to_ta->cmd_id);
	LOGI("msg_size : 0x%08x\n", req_to_ta->msg_size);
	LOGI("msg : \n");
	
	print_hex_to_string(req_to_ta->msg, 
		(req_to_ta->msg_size) > MAX_HEX_LEN_FOR_LOGGING ? MAX_HEX_LEN_FOR_LOGGING : req_to_ta->msg_size);
	LOGI("\n");

	LOGI("preload_data_size : 0x%08x\n", req_to_ta->preload_steady.preload_size);
	if (req_to_ta->preload_steady.preload_size > 0) {
		LOGI("preload_data(unshleted) : \n");

		print_hex_to_string(req_to_ta->preload_steady.preload_data, 
			(req_to_ta->preload_steady.preload_size) > MAX_HEX_LEN_FOR_LOGGING ?
			MAX_HEX_LEN_FOR_LOGGING : req_to_ta->preload_steady.preload_size);
		LOGI("\n");
	}
	LOGI("[req_to_ta - End]\n");
}

void show_rsp_from_ta(rsp_from_ta_t* rsp_from_ta)
{
	LOGI("[rsp_from_ta - Start]\n");
	if (rsp_from_ta == NULL) {
		LOGI("cmd_req is null\n");
		return;
	}

	LOGI("cmd_no : %d\n", rsp_from_ta->cmd_no);
	LOGI("ret : 0x%08x\n", rsp_from_ta->ret);
	LOGI("cmd_id : 0x%08x\n", rsp_from_ta->cmd_id);
	LOGI("msg_size : 0x%08x\n", rsp_from_ta->msg_size);

	LOGI("msg : \n");
	print_hex_to_string(rsp_from_ta->msg, 
		(uint32_t)(rsp_from_ta->msg_size) > MAX_HEX_LEN_FOR_LOGGING ?
		MAX_HEX_LEN_FOR_LOGGING : rsp_from_ta->msg_size);
	LOGI("\n");

	LOGI("req_data_size : 0x%08x\n", rsp_from_ta->req_steady.req_data_size);
	if (rsp_from_ta->req_steady.req_data_size > 0) {
		LOGI("req_data : \n");
		print_hex_to_string(rsp_from_ta->req_steady.req_data, 
			(rsp_from_ta->req_steady.req_data_size) > MAX_HEX_LEN_FOR_LOGGING ?
			MAX_HEX_LEN_FOR_LOGGING : rsp_from_ta->req_steady.req_data_size);
		LOGI("\n");
	}
	LOGI("[rsp_from_ta - End]\n");
}

void show_cp_msg(cp_rw_msg_t* msg, int type)
{
	int32_t i;

	LOGD("[CP_DEBUG] : from cp msg start\n");
	LOGD("cmd_id : 0x%08x\n",  msg->cmd_id);
	LOGD("nonceVK : \n");
	for (i = 0; i < VAULT_NONCE_LEN; i++) {
		LOGD("%02x", *(msg->nonceCP +i));
	}
	LOGD("\n");
	LOGD("nonceCP : \n");
	for(i = 0; i < VAULT_NONCE_LEN; i++) {
		LOGD("%02x", *(msg->nonceVK +i));
	}
	LOGD("\n");
	LOGD("data_size : 0x%08x\n",  msg->data_size);
	LOGD("data_offset : 0x%08x\n",  msg->data_offset);
	if (type == 1) {
		LOGD("data : \n");
		for (i = 0; i < (int)(msg->data_size); i++) {
			LOGD("%02x", *(msg->data +i));
		}
		LOGD("\n");
	}
	LOGD("[CP_DEBUG] : from cp msg end\n");
}

void print_hex_to_string(void* byte, int len)
{
	char* src = (char*)byte;

	if (src == NULL) {
		LOGE("Source is NULL.\n");
		return;
	}

	if (len > 0) {
		int i, j;
		int cmpt_len;
		char logbuf[A_LOG_LEN * 2 + 1] = {0, };

		len > A_LOG_LEN ? (cmpt_len = A_LOG_LEN) : (cmpt_len = len);

		for (i = 0, j = 0; j < cmpt_len; i = i + 2, j++) {
			snprintf(logbuf + i,  (A_LOG_LEN * 2 + 1) - i, "%02x", src[j]);
		}
#ifdef VK_SWD_QSEE
		KLOGI("%s\n", logbuf);
#else
		LOGI("%s\n", logbuf);
#endif
		memset(logbuf, 0, A_LOG_LEN * 2 + 1);

	} else {
		LOGI("length is zero\n");
	}
}

int searchString(char* str, const char* find_str, const char* delim1, const char* delim2)
{
#define MAX_STR_LEN 512
	if (str == NULL) return -1;
	if (find_str == NULL) return -1;

	char tstr[MAX_STR_LEN] = {0,};
	if (strlen(str) <= MAX_STR_LEN) {
		memcpy(tstr, str, strlen(str));
	} else {
		return -1;
	}

	char* token = _strtok(tstr, delim1, delim2);

	while (token != NULL)
	{
		if (!strcmp(token, find_str)) {
			return 1;
		}
		token = _strtok(NULL, delim1, delim2);
	}

	return 0;
}

#define MAX_ITEM_DATA_LEN      MAX_CLIENT_MSG_LEN - 4 // Maxinum size of a item is 'MAX_CLIENT_MSG_LEN - sizeof(item_meta)'
#define MAX_ITEMS_IN_PAYLOAD   100

int vk_payload_push_item(uint8_t* payload, uint16_t type, uint8_t* data, uint32_t len)
{
	item_meta_t item = { 0, };
	uint32_t payload_len = 0;
	uint32_t offset = 0;
	uint32_t num_items = 0;
	uint32_t i = 0;

	int ret = VK_ERR_GENERAL;

	if (payload == NULL) {
		LOGE("vk_payload_push_item : payload is null\n");
		return VK_ERR_INVALID_ARGUMENT;
	}

	if (type < VK_PAYLOAD_TYPE_MIN || type > VK_PAYLOAD_TYPE_MAX) {
		LOGE("wrong type(0x%04x)\n", type);
		return VK_ERR_INVALID_ARGUMENT;
	}

	if (data == NULL) {
		LOGE("data is null\n");
		return VK_ERR_INVALID_ARGUMENT;
	}

	if (len <= 0 || len > MAX_ITEM_DATA_LEN) {
		LOGE("Invalid length(%d/%d)\n", len, MAX_ITEM_DATA_LEN);
		return VK_ERR_INVALID_ARGUMENT;
	}

	memcpy(&num_items, (char*)payload, sizeof(int));
	if (num_items > MAX_ITEMS_IN_PAYLOAD) {
		LOGE("unexpected number of items(%d)\n", num_items);
		return VK_ERR_INVALID_ARGUMENT;
	}

	// Calculate payload length at present
	offset = sizeof(uint32_t);
	payload_len = offset;
	for (i = 0; i < num_items; i++) {
		if (payload_len > MAX_ITEM_DATA_LEN) {
			LOGE("Payload overflow (%d/%d)\n", payload_len, MAX_ITEM_DATA_LEN);
			return VK_ERR_INVALID_ARGUMENT;
		}
		memcpy(&item, payload + offset, sizeof(item_meta_t));
		offset += (uint32_t)sizeof(item_meta_t);
		offset += (uint32_t)item.len;
		payload_len += (int)sizeof(item_meta_t) + (int)item.len;
	}

	if (payload_len + len > MAX_ITEM_DATA_LEN) {
		LOGE("Payload overflow (%d/%d)\n", payload_len + len, MAX_ITEM_DATA_LEN);
		return VK_ERR_INVALID_ARGUMENT;
	}

	// Put type and length
	item.type = type;
	item.len = (unsigned short)len;
	memcpy(payload + offset, &item, sizeof(item_meta_t));

	// Put data
	offset += (int)sizeof(item_meta_t);
	memcpy(payload + offset, data, len);

	// Update num_items
	num_items++;
	memcpy(payload, &num_items, sizeof(int));

	// To return total length of payload
	payload_len += (int)sizeof(item_meta_t) + len;
	ret = payload_len;

	return ret;
}

bool hasEmTokenToWrite(target_t* target, cmd_req_t* req)
{
	bool result = false;
	int32_t ret = VK_ERR_GENERAL;
	cmd_req_t* tReq = NULL;
	vault_t* emt_vault = NULL;
	uint8_t* read_ptr = NULL;
	uint8_t mode_index[5] = {0,};
	const uint8_t zero_block[VAULT_SINGLE_BLOCK_LEN] = {0,};
	int android_sdk_version = 0;
	int32_t i = 0;

	if (target == NULL) {
		LOGE("%s: target is null\n", __func__);
		return false;
	}

	if (req == NULL) {
		LOGE("%s: req is null\n", __func__);
		return false;
	}

	if (g_device_info.development_device == 'T') {
		KLOGI("%s: Devices for development don't check the EM token\n", __func__);
		return true;
	}

	// Check EM token table : vk_table.h
	for (i = 0; i != NUM_OF_EMT_ITEMS; i++) {
		if (!strcmp(VTAB[req->vtab_index].client_name, EM_TOKEN_TAB[i].client_name)
		    && !strcmp(VTAB[req->vtab_index].vault_name, EM_TOKEN_TAB[i].vault_name)) {
			memcpy((char*)mode_index, EM_TOKEN_TAB[i].mode_index, strlen(EM_TOKEN_TAB[i].mode_index));
			break;
		}
	}

	if (isAllZero(mode_index, sizeof(mode_index))) {
		LOGE("%s: There is no matched mode into table(%s)\n", __func__, VTAB[req->vtab_index].vault_name);
		return false;
	}

	android_sdk_version = getPropResult(PROP_TYPE_SDK_VERSION);
	if (android_sdk_version == PROP_RESULT_FALSE || android_sdk_version < 0) { // 'PROP_RESULT_FALSE' means getting failure of property value
		LOGE("%s: Failed to get prop result(%d/%d)\n", __func__, PROP_TYPE_SDK_VERSION, android_sdk_version);
		return false;
	}

	if (android_sdk_version >= ANDROID_OS_R) {
		read_ptr = req->em_token;
	} else {
		// Convert Vault information to access EMT vault.
		ret = target->platform.memory_alloc(&tReq, sizeof(cmd_req_t));
		if (ret != VK_SUCCESS || tReq == NULL) {
			LOGE("%s: Failed memory alloc(%d)\n", __func__, ret);
			goto out;
		}

		if ((ret = swap_vault_open(req, tReq, VAULT_NAME_EMT)) != VK_SUCCESS) {
			goto out;
		}

		ret = target->platform.memory_alloc(&emt_vault, tReq->entire_vault_size);
		if (ret != VK_SUCCESS || emt_vault == NULL) {
			LOGE("%s: Failed memory alloc(%d)\n", __func__, ret);
			goto out;
		}

		// Read EMT vault
		ret = target->vault.read_entire_vault(target, tReq, emt_vault);
		if (ret != VK_SUCCESS) {
			LOGE("%s: Failed read vault(%d)\n", __func__, ret);
			goto out;
		}

		read_ptr = emt_vault->sheltered.sheltered_data;
	}

	// Guide (d14.ko)
	// http://mobilerndhub.sec.samsung.net/wiki/display/CPSECURITY/Data+Payload - [EM Token Information]
	if (searchString((char*)read_ptr, "OK", ",", "_")) {
		if (searchString((char*)read_ptr, "TOK", ",", "_")) {
			if (searchString((char*)read_ptr, (const char*)mode_index, ",", "_")) {
				result = true;
				goto out;
			} else {
				LOGI("%s: Can't find EM token that is allow to write.(%s/%s)\n", __func__, VTAB[req->vtab_index].vault_name, mode_index);
				goto out;
			}
		}
	} else {
		LOGI("%s: Can't use em token. It's empty(%s)\n", __func__, read_ptr);
		result = false;
		goto out;
	}

out:
	swap_vault_close(req);

	if (tReq != NULL) {
		memset(tReq, 0, sizeof(cmd_req_t));
		target->platform.memory_free(tReq);
	}

	if (emt_vault != NULL) {
		target->platform.memory_free(emt_vault);
	}

	// Delete EM Token on P OS
	if (result == true) {
		if (android_sdk_version < ANDROID_OS_Q) {
			// clear EM Token vault
			for (i = 0; i < VTAB[getVaultIndexByName(VAULT_NAME_EMT)].num_blocks * 2; i++) {
				ret = target->platform.write(VTAB[getVaultIndexByName(VAULT_NAME_EMT)].rpmb_partition,
												VTAB[getVaultIndexByName(VAULT_NAME_EMT)].start_index_rpmb + i,
												zero_block, VAULT_RPMB_BLOCK_UNIT);
				if (ret != VK_SUCCESS) {
					LOGE("%s: EMT clear failed(%d)\n", __func__, ret);
					return false;
				}
			}
		}
	}

	return result;
}

int check_write_protection(target_t* target, cmd_req_t* req, bool* enabled)
{
	int ret = VK_ERR_GENERAL;
	uint32_t fuse_status = 0;

	if (target == NULL) {
		LOGE("%s: target is null\n", __func__);
		return VK_ERR_INVALID_ARGUMENT;
	}

	if (req == NULL) {
		LOGE("%s: req is null\n", __func__);
		return VK_ERR_INVALID_ARGUMENT;
	}

	if (req->vtab_index >= NUM_OF_VAULTS) {
		LOGE("%s: Invaild table index(%d)\n", __func__, req->vtab_index);
		return VK_ERR_READ_VTAB;
	}

	if (enabled == NULL) {
		LOGE("%s: Invaild parameters(enabled)\n", __func__);
		return VK_ERR_INVALID_ARGUMENT;
	}

	if (VTAB[req->vtab_index].one_time_writable) {

		*enabled = true; // initialize

		ret = target->vault.read_meta(target, req, META_VAULT_WRITTEN, &fuse_status);
		if (ret != VK_SUCCESS) {
			LOGE("%s: Failed to check if whether writable(%d/%d)\n", __func__, META_VAULT_WRITTEN, ret);
			return ret;
		}

		if (fuse_status != META_FUSE_BLOWN) {
			*enabled = false;
		}
	} else {
		*enabled = false;
	}

	return VK_SUCCESS;
}

int get_vk_info(target_t* target, cmd_req_t* req)
{
	int32_t ret = VK_ERR_GENERAL;

	uint32_t rpmb_key_prov = 0;
	cmd_req_t* tReq = NULL;
	unsvault_t* vk_vault = NULL;
	uint32_t custom_kernel = OEM_FLAG_CUSTOM_KERNEL;
	uint32_t fuse_status = 0;

	if (target == NULL) {
		LOGE("%s: target is null\n", __func__);
		return VK_ERR_INVALID_ARGUMENT;
	}

	if (req == NULL) {
		LOGE("%s: req is null\n", __func__);
		return VK_ERR_INVALID_ARGUMENT;
	}

	if (isAllZero(target->client.crypto_context, MAX_CRYPTO_CONTEXT_LEN)) {
		LOGE("%s: Invalid cryto context\n", __func__);
		return VK_ERR_INVALID_ARGUMENT;
	}

	memcpy(g_device_info.client_crypto_context, target->client.crypto_context, MAX_CRYPTO_CONTEXT_LEN);

	ret = target->platform.check_rpmb_key_provisioning(&rpmb_key_prov);
	if (ret != VK_SUCCESS) {
		LOGE("%s: Failed to check rpmb key prov(%d)\n", __func__, ret);
		goto out;
	}

	if (rpmb_key_prov == RPMB_NOT_PROVISIONED) {
		LOGI("%s: RPMB Key provisioning not yet. Skip.\n", __func__);

		g_device_info.rpmb_key_provisioning = 'F';

		ret = target->platform.check_custom_kernel(&custom_kernel);
		if (ret != VK_SUCCESS) {
			LOGE("%s: Failed to check oem_sw_fuse(%d)\n", __func__, ret);
			goto out;
		}

		if (custom_kernel == OEM_FLAG_CUSTOM_KERNEL) {
			g_device_info.custom_kernel = 'T';
		} else {
			g_device_info.custom_kernel = 'F';
		}

		memcpy((uint8_t*)&g_device_info, req->device_property, MAX_PROP_FIELD_LEN);

		ret = VK_SUCCESS;
	} else {
		g_device_info.rpmb_key_provisioning = 'T'; // <-- Do not chanage relative code position

		// Convert Vault information to access EMT vault.
		ret = target->platform.memory_alloc(&tReq, sizeof(cmd_req_t));
		if (ret != VK_SUCCESS || tReq == NULL) {
			LOGE("%s: Failed memory alloc(%d)\n", __func__, ret);
			goto out;
		}

		if ((ret = swap_vault_open(req, tReq, VAULT_NAME_VK)) != VK_SUCCESS) {
			goto out;
		}

		ret = target->platform.memory_alloc(&vk_vault, VAULT_SINGLE_BLOCK_LEN);
		if (ret != VK_SUCCESS || vk_vault == NULL) {
			LOGE("%s: Failed memory alloc(%d)\n", __func__, ret);
			goto out;
		}

		// Read VK vault
		ret = target->vault.read_unsheltered_vault(target, tReq, vk_vault);
		if (ret != VK_SUCCESS) {
			LOGE("%s: Failed read vault(%d)\n", __func__, ret);
			goto out;
		}

		memcpy((uint8_t*)&g_device_info, vk_vault->unsheltered_data, MAX_PROP_FIELD_LEN);
		memcpy(g_device_info.binary_ver_history, vk_vault->unsheltered_data + MAX_PROP_FIELD_LEN, MAX_BUILD_VER_FIELD_LEN);
		memcpy(g_device_info.vek_hash, vk_vault->reserved, SHA256_DIGEST_LEN);
		memcpy(g_device_info.ap_serial_no, vk_vault->reserved + SHA256_DIGEST_LEN, AP_SN_LEN);
		memcpy(g_device_info.vk_id, vk_vault->reserved + SHA256_DIGEST_LEN + AP_SN_LEN, VK_ID_LEN);
	}

#if defined(VK_SWD_QSEE) || defined(USE_VK_DRIVER)
	// Read & Overwrite the eFuse of Commercial device
	ret = target->platform.read_otp_fuse(OTP_TYPE_COMMERCIAL_DEVICE, &fuse_status);
	if (ret != VK_SUCCESS) {
		LOGE("%s: Failed to check OTP value(%d/%d)\n", __func__, OTP_TYPE_COMMERCIAL_DEVICE, ret);
		goto out;
	}

	if (fuse_status == META_FUSE_BLOWN) {
		g_device_info.development_device = 'F';
	} else {
		g_device_info.development_device = 'T';
	}
#endif

out:
	swap_vault_close(req);

	// Print info.
	KLOGI("======= Device information =======\n");
	if (!isAllZero(&g_device_info, sizeof(vk_device_info_t))) {
		print_hex_to_string(&g_device_info, 9 + MAX_BUILD_VER_FIELD_LEN);
#ifdef __DEBUG__
		LOGD("crypto context(%d)\n", strlen(g_device_info.client_crypto_context));
		print_hex_to_string(g_device_info.client_crypto_context, strlen(g_device_info.client_crypto_context));
#endif
	}
	KLOGI("==================================\n");

	if (tReq != NULL) {
		memset(tReq, 0, sizeof(cmd_req_t));
		target->platform.memory_free(tReq);
	}

	if (vk_vault != NULL) {
		target->platform.memory_free(vk_vault);
	}

	return ret;
}

int getPropResult(uint32_t prop_type)
{
	int ret = VK_ERR_GENERAL;
	int result;

	switch (prop_type) {
		case PROP_TYPE_FAC_BINARY :
			result = g_device_info.factory_binary;
			break;
		case PROP_TYPE_EM_STATUS :
			result = g_device_info.development_device;
			break;
		case PROP_TYPE_ENG_BINARY :
			result = g_device_info.binary_build_type;
			break;
		case PROP_TYPE_FIRST_API_LEVEL :
			result = g_device_info.first_api_level;
			break;
		case PROP_TYPE_SDK_VERSION :
			result = g_device_info.sdk_version;
			break;
		case PROP_TYPE_VEK_DIFF :
			result = g_device_info.vek_integrity;
			break;
		case PROP_TYPE_CUSTOM_KERNEL :
			result = g_device_info.custom_kernel;
			break;
		case PROP_TYPE_AP_SN :
			result = g_device_info.ap_serial_integrity;
			break;
		default  :
			LOGE("%s: Invalid property type(%08x)\n", __func__, prop_type);
			result = -1;
			break;
	}

	if (result == 'T')
		ret = PROP_RESULT_TRUE;
	else if (result == 'F')
		ret = PROP_RESULT_FALSE;
	else
		ret = result;

out:
	return ret;
}

int getId(target_t* target, uint8_t* vk_id)
{
	int ret = VK_ERR_GENERAL;

	cmd_req_t* tReq = NULL;
	uint8_t* stored_data = NULL;
	uint32_t rpmb_key_prov = 0;

	if (target == NULL) {
		LOGE("%s: Invalid arguments(target)\n", __func__);
		return VK_ERR_INVALID_ARGUMENT;
	}

	if (vk_id == NULL) {
		LOGE("%s: Invalid arguments(vk_id)\n", __func__);
		return VK_ERR_INVALID_ARGUMENT;
	}

	ret = target->platform.check_rpmb_key_provisioning(&rpmb_key_prov);
	if (ret != VK_SUCCESS) {
		LOGE("%s: Failed to check rpmb key provisioning (%d)\n", __func__, ret);
		goto out;
	}

	if (rpmb_key_prov == RPMB_NOT_PROVISIONED) {
		ret = VK_ERR_RPMB_KEY_PROV;
		goto out;
	}

	ret = target->platform.memory_alloc(&tReq, sizeof(cmd_req_t));
	if (ret != VK_SUCCESS || tReq == NULL) {
		LOGE("%s: Failed memory alloc(%d)\n", __func__, ret);
		goto out;
	}

	tReq->vtab_index = getVaultIndexByName(VAULT_NAME_VK);
	if (tReq->vtab_index >= NUM_OF_VAULTS) {
		ret = VK_ERR_READ_VTAB;
		goto out;
	}

	tReq->entire_vault_size = VTAB[tReq->vtab_index].num_blocks * VAULT_SINGLE_BLOCK_LEN;
	tReq->vault_level = VTAB[tReq->vtab_index].vault_level;

	ret = target->platform.memory_alloc(&stored_data, VAULT_SINGLE_BLOCK_LEN);
	if (ret != VK_SUCCESS || stored_data == NULL) {
		LOGE("%s: Failed memory alloc(%d)\n", __func__, ret);
		goto out;
	}

	ret = target->vault.read_unsheltered_vault(target, tReq, (unsvault_t*)stored_data);
	if (ret != VK_SUCCESS) {
		LOGE("%s: Failed read VK vault(%d)\n", __func__, ret);
		goto out;
	}

	memcpy(vk_id, stored_data + UNSHELTERED_RESERVED_OFFSET + VEK_LEN + AP_SN_LEN, VK_ID_LEN);

	if (isAllZero(vk_id, VK_ID_LEN)) {
		LOGE("%s: Critical error(No ID)\n", __func__);
		ret = VK_ERR_GENERAL;
		goto out;
	}

out:
	if (stored_data != NULL) {
		memset(stored_data, 0, VAULT_SINGLE_BLOCK_LEN);
		target->platform.memory_free(stored_data);
	}

	if (tReq != NULL) {
		memset(tReq, 0, sizeof(cmd_req_t));
		target->platform.memory_free(tReq);
	}

	return ret;
}

// 0 -> '0'  {0x48, 0x00, ... } result_len : 1
// 2 -> '10'  {0x49, 0x48, 0x00, ... } result_len : 2
// 13 -> '1101' {0x49, 0x49, 0x48, 0x49, 0x00, ... } result_len : 4
int decimal_to_binary_string(int num, char* result, int* result_len)
{
	int i,j;
	char buffer[32] = {0,};
	char inverse_buff[32] = {0,};

	if (result == NULL) {
		LOGE("%s: result is null\n", __func__);
		return VK_ERR_INVALID_ARGUMENT;
	}

	if (result_len == NULL) {
		LOGE("%s: result len is null\n", __func__);
		return VK_ERR_INVALID_ARGUMENT;
	}

	*result_len = 0;

	i = 0;
	while (num > 0) {
		buffer[i] = num % 2 + '0';
		num = num / 2;
		i++;
	}

	// inverse
	j = 0;
	while(i > 0) {
		inverse_buff[i - 1] = buffer[j++];
		(*result_len)++;
		i--;
	}

	if (*result_len == 0) {
		inverse_buff[0] = '0';
		*result_len = 1;
	}

	memset(result, 0, 32);
	memcpy(result, inverse_buff, 32);

	return VK_SUCCESS;
}

void vk_nanosleep(long int nsec)
{
#ifdef VK_SWD_TEEGRIS
	struct timespec t_req;
	struct timespec t_rem;
	t_req.tv_sec = 0;
	t_req.tv_nsec = nsec;
	if (nanosleep(&t_req, &t_rem) != VK_SUCCESS) {
		LOGE("%s: nanosleep() failed. errno : %d\n", __func__, errno);
	}
#else
	// Kinibi doesn't support sleep API on TA
	// ref. drApiThreadSleep()

	// In QSEE, error occured when using nanosleep()
	// QTI Case : 04272890
#endif
}
