#include "em_common.h"

static int em_token_get_meta(const uint8_t *src, em_default_meta *meta, uint32_t *offset, const uint32_t len_max)
{
	int ret;

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_GET_META, src, meta, offset);

	if (*offset + sizeof(em_default_meta) > len_max) {
		LOGE("offset isn't normal(%u/%u)\n", (uint32_t)(*offset + sizeof(em_default_meta)),
		     len_max);
		ret = EM_ERR_EM_TOKEN_GET_META_OFFSET;
		goto out;
	}

	memcpy(meta, src + *offset, sizeof(em_default_meta));
	*offset += sizeof(em_default_meta);

	ret = EM_SUCCESS;
out:
	return ret;
}

static int em_token_get_element(const uint8_t *src, em_default_element *element, uint32_t *offset,
				const uint32_t len_max)
{
	int ret;

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_GET_ELEMENT, src, element, offset);

	if (*offset + sizeof(em_default_element) > len_max) {
		LOGE("offset isn't normal(%u/%u)\n", (uint32_t)(*offset + sizeof(em_default_element)),
		     len_max);
		ret = EM_ERR_EM_TOKEN_GET_ELEMENT_OFFSET;
		goto out;
	}

	memcpy(element, src + *offset, sizeof(em_default_element));
	*offset += sizeof(em_default_element);

	ret = EM_SUCCESS;
out:
	return ret;

}

static int em_token_verify(const uint8_t *data, const uint32_t len_data, const uint8_t *cert, const uint32_t len_cert,
			   const uint8_t *signature, const uint32_t len_signature, const uint32_t len_device,
			   const em_device_info *infos, const uint8_t *did_esi, uint64_t *flags)
{
	int ret;

	uint32_t loop = 0;
	uint8_t did_token[EM_LEN_DID] = {0,};
	uint8_t flag_token = 0;

	uint32_t len_compare_did = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_VERIFY, data, cert, signature, infos, did_esi, flags);

#ifdef EMAS
	len_compare_did = EM_LEN_DID;
#else
	len_compare_did = EM_LEN_DID - 2;
#endif

	for (loop = 0; loop < len_device; loop++) {
		memcpy(did_token, infos[loop].did, EM_LEN_DID);

		if (em_strncasecmp((const char *)did_esi, (const char *)did_token, len_compare_did) == 0) {
			flag_token = 1;
			break;
		}
	}

	if (flag_token == 0) {
		LOGE("Installed token isn't for this device\n");
		ret = EM_ERR_EM_TOKEN_VERIFY_DID;
		goto out;
	}

	ret = em_crypto_verify_rsa_signature(cert, len_cert, signature, len_signature, data, len_data);
	if (ret != EM_SUCCESS && (uint32_t)ret != EM_DEV_OK) {
		LOGE("Failed to verify signature(0x%08x)\n", ret);
		goto out;
	}

	if ((uint32_t)ret == EM_DEV_OK)
		flags[1] |= EM_FLAGS_0_DEV_CERT;

	ret = EM_SUCCESS;
out:
	return ret;
}

static int em_token_check_expiration(const char *token_id, const char *date_token_org, const char *date_server_org,
				     const char *priority_data)
{
	int ret;

	char date_token[EM_LEN_DATE + 1] = {0,};
	char date_server[EM_LEN_DATE + 1] = {0,};
	char date_priority[EM_LEN_DATE + 1] = {0,};

	uint32_t date_int_token = 0, date_int_server = 0, date_int_priority = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_CHECK_EXPIRATION, token_id, date_token_org, date_server_org);

	memcpy(date_token, date_token_org, EM_LEN_DATE);
	memcpy(date_server, date_server_org, EM_LEN_DATE);

	date_int_token = em_atoi(date_token);
	date_int_server = em_atoi(date_server);

	if (date_int_token == 0 || date_int_server == 0) {
		LOGE("em_atoi isn't working(%s/%u, %s/%u)\n", date_token, date_int_token, date_server,
		     date_int_server);
		ret = EM_ERR_EM_TOKEN_CHECK_EXPIRATION_ATOI;
		goto out;
	}

	if (date_int_token < date_int_server) {
		LOGE("token is expired(%s/%s, %u/%u)\n", date_token, date_server, date_int_token, date_int_server);
		ret = EM_ERR_EM_TOKEN_CHECK_EXPIRATION_EXPIRED;
		goto out;
	}

	if (priority_data) {
		memcpy(date_priority, priority_data, EM_LEN_DATE);

		if (memcmp(token_id, priority_data + EM_LEN_DATE, EM_LEN_TOKEN_ID) != 0) {
			LOGI("token_id isn't matched(%s/%s)\n", token_id, priority_data);
			ret = EM_SUCCESS;
			goto out;
		}

		date_int_priority = em_atoi(date_priority);
		if (date_int_priority == 0) {
			LOGE("em_atoi isn't working(%s/%u)\n", date_priority, date_int_priority);
			ret = EM_ERR_EM_TOKEN_CHECK_EXPIRATION_ATOI_P;
			goto out;
		}

		if (date_int_priority < date_int_server) {
			LOGE("token is expired[p](%s/%s)\n", date_priority, date_server);
			ret = EM_ERR_EM_TOKEN_CHECK_EXPIRATION_EXPIRED_P;
			goto out;
		}
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

static int em_token_parse_token_info(em_token_info *tokeninfo, em_token_ptr *token_ptr)
{
	int ret;
	uint32_t i;
	uint32_t offset = 0, len_max = 0;

	em_default_meta meta = {0,};
	em_default_element elem = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_PARSE_TOKEN_INFO, tokeninfo, token_ptr);

	len_max = EM_LEN_TOKEN - sizeof(em_header_info);

	ret = em_token_get_meta(token_ptr->token_info, &meta, &offset, len_max);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get meta(0x%08x)\n", ret);
		goto out;
	}

	if (memcmp(meta.magic, EM_MAGIC_TOKEN, strlen(EM_MAGIC_TOKEN))) {
		LOGE("token info magic error(%s)\n", meta.magic);
		ret = EM_ERR_EM_TOKEN_PARSE_TOKEN_INFO_MAGIC;
		goto out;
	}

	for (i = 0; i < meta.num_of_data; i++) {
		if (offset + sizeof(em_default_element) > len_max) {
			LOGE("offset isn't normal(%u/%u)\n",
			     (uint32_t)(offset + sizeof(em_default_element)), len_max);
			ret = EM_ERR_EM_TOKEN_PARSE_TOKEN_INFO_OFFSET;
			goto out;
		}

		ret = em_token_get_element(token_ptr->token_info, &elem, &offset, len_max);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to get element(0x%08x)\n", ret);
			goto out;
		}

		if (offset + elem.len > len_max) {
			LOGE("offset isn't normal(0x%08x/%u/%u)\n", elem.type,
			     (uint32_t)(offset + sizeof(em_default_element)), len_max);
			ret = EM_ERR_EM_TOKEN_PARSE_TOKEN_INFO_OFFSET_2;
			goto out;
		}

		switch (elem.type) {
		case EM_TYPE_INFO_TOKE_ID:
			if (elem.len != EM_LEN_TOKEN_ID) {
				LOGE("Unexpected token id size(%u)\n", elem.len);
				ret = EM_ERR_EM_TOKEN_PARSE_TOKEN_INFO_LEN_TOKE_ID;
				goto out;
			}

			memcpy(tokeninfo->id, token_ptr->token_info + offset, elem.len);
			break;

		case EM_TYPE_INFO_TOKE_UNIQUE_INFO:
			if (elem.len != sizeof(uint16_t)) {
				LOGE("Unexpected device info size(%u)\n", elem.len);
				ret = EM_ERR_EM_TOKEN_PARSE_TOKEN_INFO_LEN_UNIQUE;
				goto out;
			}

			memcpy(&tokeninfo->device_unique_info, token_ptr->token_info + offset, elem.len);
			break;

		case EM_TYPE_INFO_TOKE_NUM_DEVICES:
			if (elem.len != sizeof(uint16_t)) {
				LOGE("Unexpected num of device size(%u)\n", elem.len);
				ret = EM_ERR_EM_TOKEN_PARSE_TOKEN_INFO_LEN_DEVICE;
				goto out;
			}

			memcpy(&tokeninfo->num_of_devices, token_ptr->token_info + offset, elem.len);
			if (tokeninfo->num_of_devices > EM_LEN_MAX_DEVICE) {
				LOGE("Invalid num of devices(%u)\n", tokeninfo->num_of_devices);
				ret = EM_ERR_EM_TOKEN_PARSE_TOKEN_INFO_MAX_DEVICE;
				goto out;
			}
			break;

		default:
			ret = EM_ERR_EM_TOKEN_PARSE_TOKEN_INFO_UNKN_TYPE;
			LOGE("unknown type(0x%04x)\n", elem.type);
		}
		offset += elem.len;
	}

	// TODO : Check meta info size, priority low.

	ret = EM_SUCCESS;
out:
	return ret;
}

static int em_token_parse_device_info(em_device_info *device_info, uint16_t num_of_devices, em_token_ptr *token_ptr)
{
	int ret;
	uint32_t offset = 0, len_max;
	uint32_t i = 0, j = 0;

	em_default_meta meta = {0,};
	em_default_element elem = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_PARSE_DEVICE_INFO, device_info, token_ptr);

	len_max = EM_LEN_TOKEN - (token_ptr->em_device_info[0] - token_ptr->token_info) - sizeof(em_header_info);

	if (num_of_devices > EM_LEN_MAX_DEVICE) {
		LOGE("number of devices is bigger than max(%u)\n", num_of_devices);
		ret = EM_ERR_EM_TOKEN_PARSE_DEVICE_INFO_MAX_DEVICE;
		goto out;
	}

	for (i = 0; i < num_of_devices; i++) {
		offset = 0;
		ret = em_token_get_meta(token_ptr->em_device_info[i], &meta, &offset, len_max);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to get meta(0x%08x)\n", ret);
			goto out;
		}

		if (memcmp(meta.magic, EM_MAGIC_TOKEN_DEVICE, strlen(EM_MAGIC_TOKEN_DEVICE))) {
			LOGE("device info magic error\n");
			ret = EM_ERR_EM_TOKEN_PARSE_DEVICE_INFO_MAGIC;
			goto out;
		}

		for (j = 0; j < meta.num_of_data; j++) {
			if (offset + sizeof(em_default_element) > len_max) {
				LOGE("offset isn't normal(%u/%u)\n",
				     (uint32_t)(offset + sizeof(em_default_element)), len_max);
				ret = EM_ERR_EM_TOKEN_PARSE_DEVICE_INFO_OFFSET;
				goto out;
			}

			ret = em_token_get_element(token_ptr->em_device_info[i], &elem, &offset, len_max);
			if (ret != EM_SUCCESS) {
				LOGE("Failed to get element(0x%08x)\n", ret);
				goto out;
			}

			if (offset + elem.len > len_max) {
				LOGE("offset isn't normal(0x%08x/%u/%u)\n", elem.type,
				     (uint32_t)(offset + sizeof(em_default_element)), len_max);
				ret = EM_ERR_EM_TOKEN_PARSE_DEVICE_INFO_OFFSET_2;
				goto out;
			}

			switch (elem.type) {
			case EM_TYPE_INFO_DEVI_MODEL_NAME:
				if (elem.len > EM_LEN_MODEL_NAME) {
					LOGE("Unexpected model name size(%u)\n", elem.len);
					ret = EM_ERR_EM_TOKEN_PARSE_DEVICE_INFO_MODEL_NAME;
					goto out;
				}

				memcpy(device_info[i].model_name, token_ptr->em_device_info[i] + offset, elem.len);
				break;

			case EM_TYPE_INFO_DEVI_DID:
				if (elem.len > EM_LEN_DID) {
					LOGE("Unexpected did size(%u)\n", elem.len);
					ret = EM_ERR_EM_TOKEN_PARSE_DEVICE_INFO_DEVICE_ID;
					goto out;
				}

				memcpy(device_info[i].did, token_ptr->em_device_info[i] + offset, elem.len);
				break;

			case EM_TYPE_INFO_DEVI_IMEI:
				if (elem.len > EM_LEN_IMEI) {
					LOGE("Unexpected imei size(%u)\n", elem.len);
					ret = EM_ERR_EM_TOKEN_PARSE_DEVICE_INFO_IMEI;
					goto out;
				}

				memcpy(device_info[i].imei, token_ptr->em_device_info[i] + offset, elem.len);
				break;

			default:
				LOGE("Unknown type(0x%04x)\n", elem.type);
			}
			offset += elem.len;
		}
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

static int em_token_parse_issuer_info(em_issuer_info *issuerinfo, em_token_ptr *token_ptr)
{
	int ret;
	uint32_t i;
	uint32_t offset = 0, len_max = 0;

	em_default_meta meta = {0,};
	em_default_element elem = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_PARSE_ISSUER_INFO, issuerinfo, token_ptr);

	len_max = EM_LEN_TOKEN - (token_ptr->issuer_info - token_ptr->token_info) - sizeof(em_header_info);

	ret = em_token_get_meta(token_ptr->issuer_info, &meta, &offset, len_max);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get meta(0x%08x)\n", ret);
		goto out;
	}

	if (memcmp(meta.magic, EM_MAGIC_TOKEN_ISSUER, strlen(EM_MAGIC_TOKEN_ISSUER))) {
		LOGE("issuer info magic error\n");
		ret = EM_ERR_EM_TOKEN_PARSE_ISSUER_INFO_MAGIC;
		goto out;
	}

	for (i = 0; i < meta.num_of_data; i++) {
		if (offset + sizeof(em_default_element) > len_max) {
			LOGE("offset isn't normal(%u/%u)\n",
			     (uint32_t)(offset + sizeof(em_default_element)), len_max);
			ret = EM_ERR_EM_TOKEN_PARSE_ISSUER_INFO_OFFSET;
			goto out;
		}

		ret = em_token_get_element(token_ptr->issuer_info, &elem, &offset, len_max);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to get element(0x%08x)\n", ret);
			goto out;
		}

		if (offset + elem.len > len_max) {
			LOGE("offset isn't normal(0x%08x/%u/%u)\n", elem.type,
			     (uint32_t)(offset + sizeof(em_default_element)), len_max);
			ret = EM_ERR_EM_TOKEN_PARSE_ISSUER_INFO_OFFSET_2;
			goto out;
		}

		switch (elem.type) {
		case EM_TYPE_INFO_ISSU_SINGLE_ID:
			if (elem.len > EM_LEN_SINGLE_ID) {
				LOGE("Unexpected single id size(%u)\n", elem.len);
				ret = EM_ERR_EM_TOKEN_PARSE_ISSUER_INFO_SINGLE_ID;
				goto out;
			}

			memcpy(issuerinfo->single_id, token_ptr->issuer_info + offset, elem.len);
			break;

		case EM_TYPE_INFO_ISSU_OTP:
			if (elem.len != EM_LEN_OTP) {
				LOGE("Unexpected otp size(%u)\n", elem.len);
				ret = EM_ERR_EM_TOKEN_PARSE_ISSUER_INFO_OTP;
				goto out;
			}

			memcpy(issuerinfo->otp, token_ptr->issuer_info + offset, elem.len);
			break;

		case EM_TYPE_INFO_ISSU_NONCE:
			if (elem.len != EM_LEN_NONCE) {
				LOGE("Unexpected nonce size(%u)\n", elem.len);
				ret = EM_ERR_EM_TOKEN_PARSE_ISSUER_INFO_NONCE;
				goto out;
			}

			memcpy(issuerinfo->nonce, token_ptr->issuer_info + offset, elem.len);
			break;

		case EM_TYPE_INFO_ISSU_SYSTEM_ID:
			if (elem.len > EM_LEN_SYSTEM_ID) {
				LOGE("Unexpected system id size(%u)\n", elem.len);
				ret = EM_ERR_EM_TOKEN_PARSE_ISSUER_INFO_SYSTEM_ID;
				goto out;
			}

			memcpy(issuerinfo->system_id, token_ptr->issuer_info + offset, elem.len);
			break;

		case EM_TYPE_INFO_ISSU_IP:
			if (elem.len > EM_LEN_IP_ADDR) {
				LOGE("Unexpected ip addr size(%u)\n", elem.len);
				ret = EM_ERR_EM_TOKEN_PARSE_ISSUER_INFO_IP;
				goto out;
			}

			memcpy(issuerinfo->ip_addr, token_ptr->issuer_info + offset, elem.len);
			break;

		case EM_TYPE_INFO_ISSU_MAC:
			if (elem.len > EM_LEN_MAC_ADDR) {
				LOGE("Unexpected mac addr size(%u)\n", elem.len);
				ret = EM_ERR_EM_TOKEN_PARSE_ISSUER_INFO_MAC;
				goto out;
			}

			memcpy(issuerinfo->mac_addr, token_ptr->issuer_info + offset, elem.len);
			break;

		default:
			LOGE("Unknown type(0x%04x)\n", elem.type);
		}
		offset += elem.len;
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

static int em_token_parse_mode_info(em_mode_info *modeinfo, em_token_ptr *token_ptr)
{
	int ret;
	uint32_t i;
	uint32_t offset = 0, len_max = 0;

	em_default_meta meta = {0,};
	em_default_element elem = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_PARSE_MODE_INFO, modeinfo, token_ptr);

	len_max = EM_LEN_TOKEN - (token_ptr->mode_db - token_ptr->token_info) - sizeof(em_header_info);

	ret = em_token_get_meta(token_ptr->mode_info, &meta, &offset, len_max);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get meta(0x%08x)\n", ret);
		goto out;
	}

	if (meta.num_of_data > EM_LEN_MAX_MODE) {
		LOGE("num of modes is bigger than max(%u)\n", meta.num_of_data);
		ret = EM_ERR_EM_TOKEN_PARSE_MODE_INFO_MAX_MODE;
		goto out;
	}
	modeinfo->num_of_modes = meta.num_of_data;

	for (i = 0; i < meta.num_of_data; i++) {
		if (offset + sizeof(em_default_element) > len_max) {
			LOGE("offset isn't normal(%u/%u)\n",
			     (uint32_t)(offset + sizeof(em_default_element)), len_max);
			ret = EM_ERR_EM_TOKEN_PARSE_MODE_INFO_OFFSET;
			goto out;
		}

		ret = em_token_get_element(token_ptr->mode_info, &elem, &offset, len_max);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to get element(0x%08x)\n", ret);
			goto out;
		}
		modeinfo->mode[i] = elem.type;
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

static int em_token_parse_validity_info(em_validity_info *valiinfo, em_token_ptr *token_ptr)
{
	int ret;
	uint32_t i;
	uint32_t offset = 0, len_max = 0;

	em_default_meta meta = {0,};
	em_default_element elem = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_PARSE_VALIDITY_INFO, valiinfo, token_ptr);

	len_max = EM_LEN_TOKEN - (token_ptr->validity_info - token_ptr->token_info - sizeof(em_header_info));

	ret = em_token_get_meta(token_ptr->validity_info, &meta, &offset, len_max);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get meta(0x%08x)\n", ret);
		goto out;
	}

	if (memcmp(meta.magic, EM_MAGIC_TOKEN_VALIDATE, strlen(EM_MAGIC_TOKEN_VALIDATE))) {
		LOGE("validity info magic error\n");
		ret = EM_ERR_EM_TOKEN_PARSE_VALIDITY_INFO_MAGIC;
		goto out;
	}

	for (i = 0; i < meta.num_of_data; i++) {
		if (offset + sizeof(em_default_element) > len_max) {
			LOGE("offset isn't normal(%u/%u)\n",
			     (uint32_t)(offset + sizeof(em_default_element)), len_max);
			ret = EM_ERR_EM_TOKEN_PARSE_VALIDITY_INFO_OFFSET;
			goto out;
		}

		ret = em_token_get_element(token_ptr->validity_info, &elem, &offset, len_max);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to get element(0x%08x)\n", ret);
			goto out;
		}

		if (offset + elem.len > len_max) {
			LOGE("offset isn't normal(%08x/%u/%u)\n", elem.type, offset + elem.len, len_max);
			ret = EM_ERR_EM_TOKEN_PARSE_VALIDITY_INFO_MAGIC;
			goto out;
		}

		switch (elem.type) {
		case EM_TYPE_INFO_VALI_ISSUED_DATE:
			if (elem.len != EM_LEN_DATE) {
				LOGE("Unexpected issued date size(%u)\n", elem.len);
				ret = EM_ERR_EM_TOKEN_PARSE_VALIDITY_INFO_DATE;
				goto out;
			}

			memcpy(valiinfo->issued_date, token_ptr->validity_info + offset, elem.len);
			break;

		case EM_TYPE_INFO_VALI_EXPIRY_DATE:
			if (elem.len != EM_LEN_DATE) {
				LOGE("Unexpected expiry size(%u)\n", elem.len);
				ret = EM_ERR_EM_TOKEN_PARSE_VALIDITY_INFO_DATE_2;
				goto out;
			}

			memcpy(valiinfo->expiry_date, token_ptr->validity_info + offset, elem.len);
			break;

		default:
			LOGE("Unknown type(0x%04x)\n", elem.type);
		}

		offset += elem.len;
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

static int em_token_parse_integrity_info(em_integrity_info *integinfo, em_token_ptr *token_ptr)
{
	int ret;
	uint32_t i;
	uint32_t offset = 0, len_max = 0;

	em_default_meta meta = {0,};
	em_default_element elem = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_PARSE_INTEGRITY_INFO, integinfo, token_ptr);

	len_max = EM_LEN_TOKEN - (token_ptr->integrity_info - token_ptr->token_info) - sizeof(em_header_info);

	ret = em_token_get_meta(token_ptr->integrity_info, &meta, &offset, len_max);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get meta(0x%08x)\n", ret);
		goto out;
	}

	if (memcmp(meta.magic, EM_MAGIC_TOKEN_INTEGRITY, strlen(EM_MAGIC_TOKEN_INTEGRITY))) {
		LOGE("integrity info magic error(%s)\n", meta.magic);
		ret = EM_ERR_EM_TOKEN_PARSE_INTEGRITY_INFO_MAGIC;
		goto out;
	}

	for (i = 0; i < meta.num_of_data; i++) {
		if (offset + sizeof(em_default_element) > len_max) {
			LOGE("offset isn't normal(%u/%u)\n",
			     (uint32_t)(offset + sizeof(em_default_element)), len_max);
			ret = EM_ERR_EM_TOKEN_PARSE_INTEGRITY_INFO_OFFSET;
			goto out;
		}

		ret = em_token_get_element(token_ptr->integrity_info, &elem, &offset, len_max);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to get element(0x%08x)\n", ret);
			goto out;
		}

		if (offset + elem.len > len_max) {
			LOGE("offset isn't normal(%08x/%u/%u)\n", elem.type, offset + elem.len, len_max);
			ret = EM_ERR_EM_TOKEN_PARSE_INTEGRITY_INFO_OFFSET2;
			goto out;
		}

		switch (elem.type) {
		case EM_TYPE_INFO_INTE_SIGNATURE:
			if (elem.len > EM_LEN_SIGNATURE) {
				LOGE("Unexpected sig size(%u)\n", elem.len);
				ret = EM_ERR_EM_TOKEN_PARSE_INTEGRITY_INFO_SIGN;
				goto out;
			}

			memcpy(integinfo->signature, token_ptr->integrity_info + offset, elem.len);
			integinfo->len_signature = elem.len;
			break;

		case EM_TYPE_INFO_INTE_SERVER_CERT:
			if (elem.len > EM_LEN_CERTIFICATE) {
				LOGE("Unexpected server cert size(%u)\n", elem.len);
				ret = EM_ERR_EM_TOKEN_PARSE_INTEGRITY_INFO_CERT;
				goto out;
			}

			memcpy(integinfo->cert, token_ptr->integrity_info + offset, elem.len);
			integinfo->len_cert = elem.len;
			break;

		default:
			LOGE("Unknown type(0x%04x)\n", elem.type);
		}

		offset += elem.len;
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

static int em_token_parse_mode_db(em_mode_db *modedb, em_token_ptr *token_ptr)
{
	int ret;
	uint32_t i, j;
	uint32_t offset = 0, len_max = 0;

	em_default_meta meta = {0,};
	em_default_element elem = {0,};

	uint16_t mode_index = 0;
	em_modb_attr *mode_attrbutes = NULL;

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_PARSE_MODE_DB_INFO, modedb, token_ptr);

	len_max = EM_LEN_TOKEN - (token_ptr->mode_db - token_ptr->token_info) - sizeof(em_header_info);

	ret = em_token_get_meta(token_ptr->mode_db, &meta, &offset, len_max);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get meta(0x%08x)\n", ret);
		goto out;
	}

	if (memcmp((char *)meta.magic, EM_MAGIC_TOKEN_MODB, strlen(EM_MAGIC_TOKEN_MODB))) {
		LOGE("mode db magic error(%02x/%02x/%02x/%02x)\n", meta.magic[0], meta.magic[1],
		     meta.magic[2], meta.magic[3]);
		ret = EM_ERR_EM_TOKEN_PARSE_MODE_DB_INFO_MAGIC;
		goto out;
	}

	if (meta.num_of_data > EM_LEN_MAX_MODE) {
		LOGE("Unexpected num of mode(%u)\n", meta.num_of_data);
		ret = EM_ERR_EM_TOKEN_PARSE_MODE_DB_INFO_NUM;
		goto out;
	}

	for (i = 0; i < meta.num_of_data; i++) {
		mode_index = 0;
		mode_attrbutes = NULL;

		if (offset + sizeof(em_modb_data) > len_max) {
			LOGE("offset isn't normal(%u/%u)\n", (uint32_t)(offset + sizeof(em_modb_data)),
			     len_max);
			ret = EM_ERR_EM_TOKEN_PARSE_MODE_DB_INFO_OFFSET_2;
			goto out;
		}

		memcpy(&mode_index, (token_ptr->mode_db)+offset, sizeof(uint16_t));

		memcpy(&modedb->data[mode_index], (token_ptr->mode_db)+offset, sizeof(em_modb_data) - sizeof(em_modb_attr));
		offset += (sizeof(em_modb_data) - sizeof(em_modb_attr));

#ifdef EMAS
		LOGD("index : 0x%04x\n", modedb->data[mode_index].index);
		LOGD("name  : %s\n", modedb->data[mode_index].name);
		LOGD("desc  : %s\n", modedb->data[mode_index].desc);
		LOGD("group_index : 0x%04x\n", modedb->data[mode_index].group_index);
		LOGD("attr_size  : %u\n", modedb->data[mode_index].len_attr);
		LOGD("num_of_attr  : %u\n", modedb->data[mode_index].num_of_attr);
#endif
		mode_attrbutes = &modedb->data[mode_index].attributes;

		for (j = 0; j < modedb->data[mode_index].num_of_attr; j++) {
			if (modedb->data[mode_index].len_attr > EM_LEN_MODB_ATTRIBUTE) {
				LOGE("Unexpected mode attr size(%u)\n",
				     modedb->data[mode_index].len_attr);
				ret = EM_ERR_EM_TOKEN_PARSE_MODE_DB_INFO_ATTR;
				goto out;
			}

			ret = em_token_get_element(token_ptr->mode_db, &elem, &offset, len_max);
			if (ret != EM_SUCCESS) {
				LOGE("Failed to get element(0x%08x)\n", ret);
				goto out;
			}

#ifdef EMAS
			LOGD("Get mode attribute - type(0x%04x)\n", elem.type);
#endif
			if (offset + elem.len > len_max) {
				LOGE("offset isn't normal(%08x/%u/%u)\n", elem.type, offset + elem.len,
				     len_max);
				ret = EM_ERR_EM_TOKEN_PARSE_MODE_DB_INFO_OFFSET;
				goto out;
			}

			switch (elem.type) {
			case EM_TYPE_INFO_MODB_DEVICE_INFO:
				if (elem.len > sizeof(mode_attrbutes->dui)) {
					LOGE("Unexpected dui size(%u)\n", elem.len);
					ret = EM_ERR_EM_TOKEN_PARSE_MODE_DB_INFO_DUI;
					goto out;
				}

				memcpy(&mode_attrbutes->dui, token_ptr->mode_db + offset, elem.len);
#ifdef EMAS
				LOGD("mode_attrbutes->dui : 0x%x\n", mode_attrbutes->dui);
#endif
				break;

			case EM_TYPE_INFO_MODB_HIDDEN:
				if (elem.len > sizeof(mode_attrbutes->hidden)) {
					LOGE("Unexpected hidden size(%u)\n", elem.len);
					ret = EM_ERR_EM_TOKEN_PARSE_MODE_DB_INFO_HIDDEN;
					goto out;
				}

				memcpy(&mode_attrbutes->hidden, token_ptr->mode_db + offset, elem.len);
#ifdef EMAS
				LOGD("mode_attrbutes->hidden : 0x%x\n", mode_attrbutes->hidden);
#endif
				break;

			case EM_TYPE_INFO_MODB_MTUC:
				if (elem.len > sizeof(mode_attrbutes->is_mtuc_enabled)) {
					LOGE("Unexpected mtuc size(%u)\n", elem.len);
					ret = EM_ERR_EM_TOKEN_PARSE_MODE_DB_INFO_MTUC;
					goto out;
				}

				memcpy(&mode_attrbutes->is_mtuc_enabled, token_ptr->mode_db + offset, elem.len);
#ifdef EMAS
				LOGD("mode_attrbutes->is_mtuc_enabled : 0x%x\n",
				     mode_attrbutes->is_mtuc_enabled);
#endif
				break;

			case EM_TYPE_INFO_MODB_MTUC_VALUE:
				if (elem.len > sizeof(mode_attrbutes->mtuc_value)) {
					LOGE("Unexpected mtuc_value size(%u)\n", elem.len);
					ret = EM_ERR_EM_TOKEN_PARSE_MODE_DB_INFO_MTUC_V;
					goto out;
				}

				memcpy(&mode_attrbutes->mtuc_value, token_ptr->mode_db + offset, elem.len);
#ifdef EMAS
				LOGD("mode_attrbutes->mtuc_value : %u\n", mode_attrbutes->mtuc_value);
#endif
				break;

			case EM_TYPE_INFO_MODB_EXCLUSIVE:
				if (elem.len > sizeof(mode_attrbutes->exclusive)) {
					LOGE("Unexpected exclusive size(%u)\n", elem.len);
					ret = EM_ERR_EM_TOKEN_PARSE_MODE_DB_INFO_EXCLUSIVE;
					goto out;
				}

				memcpy(&mode_attrbutes->exclusive, token_ptr->mode_db + offset, elem.len);
#ifdef EMAS
				LOGD("mode_attrbutes->exclusive : 0x%x\n", mode_attrbutes->exclusive);
#endif
				break;

			case EM_TYPE_INFO_MODB_USED_ONCE:
				if (elem.len > sizeof(mode_attrbutes->used_once)) {
					LOGE("Unexpected used_once size(%u)\n", elem.len);
					ret = EM_ERR_EM_TOKEN_PARSE_MODE_DB_INFO_ONCE;
					goto out;
				}

				memcpy(&mode_attrbutes->used_once, token_ptr->mode_db + offset, elem.len);
#ifdef EMAS
				LOGD("mode_attrbutes->used_once : 0x%x\n", mode_attrbutes->used_once);
#endif
				break;

			case EM_TYPE_INFO_MODB_EXTENSION:
				if (elem.len > EM_LEN_MODB_EXTENSEION) {
					LOGE("Unexpected ext size(%u)\n", elem.len);
					ret = EM_ERR_EM_TOKEN_PARSE_MODE_DB_INFO_EXTENSION;
					goto out;
				}

				memcpy(&mode_attrbutes->ext, token_ptr->mode_db + offset, elem.len);
#ifdef EMAS
				LOGD("mode_attrbutes->ext : %s\n", mode_attrbutes->ext);
#endif
				mode_attrbutes->len_ext = elem.len;
				break;

			default:
				LOGE("Unknown type(0x%04x)\n", elem.type);
			}

			offset += elem.len;
		}
	}
	ret = EM_SUCCESS;

out:
	return ret;
}

static int em_token_parse(const uint8_t *token, em_token_ptr *token_ptr, em_parsed_token *pt)
{
	int ret;

	uint32_t i = 0, offset = 0, address = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_PARSE, token, token_ptr, pt);

	/* Header */
	memcpy(&token_ptr->header, token, sizeof(em_header_info));
	if (memcmp(token_ptr->header.prefix, EM_MAGIC_HEADER_PREFIX, EM_LEN_HEADER_FIELD)) {
		LOGE("Token Prefix isn't matched(%02x/%02x/%02x)\n", token_ptr->header.prefix[0],
		     token_ptr->header.prefix[1], token_ptr->header.prefix[2]);
		ret = EM_ERR_EM_TOKEN_PARSE_HEDAER_MAGIC;
		goto out;
	}

	if (memcmp(token_ptr->header.type, "RMA", EM_LEN_HEADER_FIELD) != 0 &&
	    memcmp(token_ptr->header.type, "RSD", EM_LEN_HEADER_FIELD) != 0 &&
	    memcmp(token_ptr->header.type, "RWS", EM_LEN_HEADER_FIELD) != 0 &&
	    memcmp(token_ptr->header.type, "RSS", EM_LEN_HEADER_FIELD) != 0 &&
	    memcmp(token_ptr->header.type, "RJS", EM_LEN_HEADER_FIELD) != 0) {
		LOGE("This token type isn't support(%02x/%02x/%02x)\n", token_ptr->header.type[0],
		     token_ptr->header.type[1], token_ptr->header.type[2]);
		ret = EM_ERR_EM_TOKEN_PARSE_TOKEN_HEADER;
		goto out;
	}

	if (memcmp(token_ptr->header.version, EM_MAGIC_PACKET_VERSION, EM_LEN_HEADER_VERSION) != 0) {
		LOGE("Token version isn't matched(%02x/%02x/%02x/%02x)\n", token_ptr->header.version[0],
		     token_ptr->header.version[1], token_ptr->header.version[2], token_ptr->header.version[3]);
		ret = EM_ERR_EM_TOKEN_PARSE_TOKEN_VERSION;
		goto out;
	}

	offset += sizeof(em_header_info) + sizeof(uint32_t);

	memcpy(&address, token + offset, sizeof(uint32_t));
	token_ptr->token_info = (uint8_t *)token + address;
	offset += sizeof(uint32_t);

	/* TOKEN INFO */
	ret = em_token_parse_token_info(&pt->token, token_ptr);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to parse token info(0x%08x)\n", ret);
		goto out;
	}

	/* DEVICE INFO */
	for (i = 0; i < pt->token.num_of_devices; i++) {
		memcpy(&address, token + offset, sizeof(uint32_t));
		token_ptr->em_device_info[i] = (uint8_t *)(token + address);
		offset += sizeof(uint32_t);
	}

	ret = em_token_parse_device_info(pt->device, pt->token.num_of_devices, token_ptr);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to parse device info(0x%08x)\n", ret);
		goto out;
	}

	/* ISSUER INFO */
	memcpy(&address, token + offset, sizeof(uint32_t));
	token_ptr->issuer_info = (uint8_t *)(token + address);
	offset += sizeof(uint32_t);
	ret = em_token_parse_issuer_info(&pt->issuer, token_ptr);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to parse issuer info(0x%08x)\n", ret);
		goto out;
	}

	/* MODE INFO */
	memcpy(&address, token + offset, sizeof(uint32_t));
	token_ptr->mode_info = (uint8_t *)(token + address);
	offset += sizeof(uint32_t);
	ret = em_token_parse_mode_info(&pt->mode, token_ptr);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to parse mode info(0x%08x)\n", ret);
		goto out;
	}

	/* VALI INFO */
	memcpy(&address, token + offset, sizeof(uint32_t));
	token_ptr->validity_info = (uint8_t *)(token + address);
	offset += sizeof(uint32_t);
	ret = em_token_parse_validity_info(&pt->validity, token_ptr);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to parse validity info(0x%08x)\n", ret);
		goto out;
	}

	/* INTE INFO */
	memcpy(&address, token + offset, sizeof(uint32_t));
	token_ptr->integrity_info = (uint8_t *)(token + address);
	offset += sizeof(uint32_t);
	ret = em_token_parse_integrity_info(&pt->integrity, token_ptr);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to parse integrity info(0x%08x)\n", ret);
		goto out;
	}

	/* MODEDB INFO */
	memcpy(&address, token + offset, sizeof(uint32_t));
	token_ptr->mode_db = (uint8_t *)(token + address);
	offset += sizeof(uint32_t);
	ret = em_token_parse_mode_db(&pt->modedb, token_ptr);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to parse mode db(0x%08x)\n", ret);
		goto out;
	}

	/* GROUPDB INFO */
	memcpy(&address, token + offset, sizeof(uint32_t));
	token_ptr->group_db = (uint8_t *)(token + address);
	offset += sizeof(uint32_t);
	if (memcmp(token_ptr->group_db, "GRDB", 4) != 0) {
		LOGE("group_db_info parse error\n");
		ret = EM_ERR_EM_TOKEN_PARSE_TOKEN_GROUP_DB;
		goto out;
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

static int em_token_make_tuc_v20(tuc_table_v20 *table, em_token_ptr *token_ptr,
				 em_parsed_token *parsed_token)
{
	int ret, i = 0, j = 0;

	uint8_t *modb = NULL;
	uint8_t tucFlag = 0;

	tuc_v20 *temp_tuc = NULL;
	uint32_t size_of_attr = 0, num_of_attr = 0;
	uint16_t attr_type = 0, attr_len = 0;

	uint32_t offset = 0, len_max = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_MAKE_TUC_V20, table, token_ptr, parsed_token);

	len_max = EM_LEN_TOKEN - (token_ptr->mode_db - token_ptr->token_info) - sizeof(em_header_info);

	table->num_of_tuc = parsed_token->mode.num_of_modes;
	if (parsed_token->mode.num_of_modes > EM_LEN_MAX_MODE) {
		LOGE("num_of_modes isn't normal(%u/%u)\n", parsed_token->mode.num_of_modes,
		     EM_LEN_MAX_MODE);
		ret = EM_ERR_EM_TOKEN_MAKE_TUC_V20_MAX_MODE;
		goto out;
	}

	modb = token_ptr->mode_db;
	offset += 4 + 4 + 4;
	for (i = 0; i < parsed_token->mode.num_of_modes; i++) {
		temp_tuc = &table->tucs[i];

		if (offset + sizeof(em_modb_data) > len_max) {
			LOGE("offset isn't normal(%u/%u)\n", (uint32_t)(offset + sizeof(em_modb_data)),
			     len_max);
			ret = EM_ERR_EM_TOKEN_MAKE_TUC_V20_OFFSET;
			goto out;
		}

		memcpy(&temp_tuc->mode_index, modb + offset, sizeof(uint16_t));
		offset += 2;   /* index */
		offset += 32;  /* mode data name */
		offset += 128; /* mode data desc */
		offset += 2;   /* mode data group index */

		memcpy(&size_of_attr, modb + offset, sizeof(uint32_t));
		offset += sizeof(uint32_t);
		memcpy(&num_of_attr, modb + offset, sizeof(uint32_t));
		offset += sizeof(uint32_t);

		for (j = 0; (uint32_t)j < num_of_attr; j++) {
			memcpy(&attr_type, modb + offset, sizeof(uint16_t));
			offset += sizeof(uint16_t);
			memcpy(&attr_len, modb + offset, sizeof(uint16_t));
			offset += sizeof(uint16_t);

			if (attr_type == EM_TYPE_TUC_VALUE) {
				memcpy(&temp_tuc->count, modb + offset, sizeof(uint32_t));
				offset += sizeof(uint32_t);
			} else if (attr_type == EM_TYPE_TUC_USAGE_COUNT_TYPE) {
				memcpy(&tucFlag, modb + offset, sizeof(uint8_t));
				if (tucFlag == EM_TYPE_TUC_IS_ENABLE)
					temp_tuc->flags |= EM_TYPE_TUC_IS_ENABLE;
				else if (tucFlag == EM_TYPE_TUC_ONLY_ONCE)
					temp_tuc->flags |= EM_TYPE_TUC_ONLY_ONCE;

				offset += sizeof(uint8_t);
			} else {
				offset += attr_len;
			}
		}
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

static int em_token_make_tuc(tuc_table *table, em_token_ptr *token_ptr,
				 em_parsed_token *parsed_token)
{
	int ret, i = 0, j = 0;

	uint8_t *modb = NULL;
	uint8_t tucFlag = 0;

	tuc *temp_tuc = NULL;
	uint32_t size_of_attr = 0, num_of_attr = 0;
	uint16_t attr_type = 0, attr_len = 0;

	uint32_t offset = 0, len_max = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_MAKE_TUC, table, token_ptr, parsed_token);

	len_max = EM_LEN_TOKEN - (token_ptr->mode_db - token_ptr->token_info) - sizeof(em_header_info);

	table->num_of_tuc = parsed_token->mode.num_of_modes;
	if (parsed_token->mode.num_of_modes > EM_LEN_MAX_MODE) {
		LOGE("num_of_modes isn't normal(%u/%u)\n", parsed_token->mode.num_of_modes,
		     EM_LEN_MAX_MODE);
		ret = EM_ERR_EM_TOKEN_MAKE_TUC_MAX_MODE;
		goto out;
	}

	modb = token_ptr->mode_db;
	offset += 4 + 4 + 4;
	for (i = 0; i < parsed_token->mode.num_of_modes; i++) {
		temp_tuc = &table->tucs[i];

		if (offset + sizeof(em_modb_data) > len_max) {
			LOGE("offset isn't normal(%u/%u)\n", (uint32_t)(offset + sizeof(em_modb_data)),
			     len_max);
			ret = EM_ERR_EM_TOKEN_MAKE_TUC_OFFSET;
			goto out;
		}

		memcpy(&temp_tuc->mode_index, modb + offset, sizeof(uint16_t));
		offset += 2;   /* index */
		offset += 32;  /* mode data name */
		offset += 128; /* mode data desc */
		offset += 2;   /* mode data group index */

		memcpy(&size_of_attr, modb + offset, sizeof(uint32_t));
		offset += sizeof(uint32_t);
		memcpy(&num_of_attr, modb + offset, sizeof(uint32_t));
		offset += sizeof(uint32_t);

		for (j = 0; (uint32_t)j < num_of_attr; j++) {
			memcpy(&attr_type, modb + offset, sizeof(uint16_t));
			offset += sizeof(uint16_t);
			memcpy(&attr_len, modb + offset, sizeof(uint16_t));
			offset += sizeof(uint16_t);

			if (attr_type == EM_TYPE_TUC_VALUE) {
				memcpy(&temp_tuc->max_count, modb + offset, sizeof(uint32_t));
				temp_tuc->count = temp_tuc->max_count;
				offset += sizeof(uint32_t);
			} else if (attr_type == EM_TYPE_TUC_USAGE_COUNT_TYPE) {
				memcpy(&tucFlag, modb + offset, sizeof(uint8_t));
				if (tucFlag == EM_TYPE_TUC_IS_ENABLE)
					temp_tuc->flags |= EM_TYPE_TUC_IS_ENABLE;
				else if (tucFlag == EM_TYPE_TUC_ONLY_ONCE)
					temp_tuc->flags |= EM_TYPE_TUC_ONLY_ONCE;

				offset += sizeof(uint8_t);
			} else {
				offset += attr_len;
			}
		}
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

static int em_token_restore_tuc(uint8_t em_version, em_token_ptr *token_ptr, em_parsed_token *parsed_token,
				void *table_org)
{
	int ret;
	tuc_table *table = NULL;
	tuc_table_v20 *table_v20 = NULL, table_new = {0,};

	uint16_t i = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_RESTORE_TUC, token_ptr, parsed_token, table_org);

	if (em_version == 20) {
		ret = em_token_make_tuc_v20(&table_new, token_ptr, parsed_token);
		if (ret != EM_SUCCESS){
			LOGE("Failed to make tuc v20(0x%08x)\n", ret);
			goto out;
		}

		table_v20 = (tuc_table_v20 *)table_org;
		for (i = 0; i < table_v20->num_of_tuc; i++){
			if (table_v20->tucs[i].mode_index == table_new.tucs[i].mode_index) {
				if (!(table_v20->tucs[i].flags & EM_TYPE_TUC_ONLY_ONCE))
					continue;
				table_new.tucs[i].count = table_v20->tucs[i].count;
			} else {
				LOGE("Unknown tuc, please check it\n");
				ret = EM_ERR_EM_TOKEN_RESTORE_TUC_TABLE;
				goto out;
			}
		}
		memcpy(table_org, &table_new, sizeof(tuc_table_v20));
	} else {
		table = (tuc_table *)table_org;
		for (i = 0; i < table->num_of_tuc; i++) {
			if (table->tucs[i].flags & EM_TYPE_TUC_IS_ENABLE)
				table->tucs[i].count = table->tucs[i].max_count;
		}
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

int em_token_is_installed(const uint8_t *token, const uint8_t *esi, uint64_t *flags)
{
	int ret, i = 0;

	uint8_t em_version = 0;

	em_token_ptr token_ptr = {0,};
	em_parsed_token *parsed_token = NULL;

	uint8_t token_id_esi[EM_LEN_TOKEN_ID + 1] = {0,};
	uint8_t did_esi[EM_LEN_TOKEN_ID + 1] = {0,};
	uint32_t len_token = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_GET_STATUS, token, esi, flags);

	parsed_token = (em_parsed_token *)em_calloc(sizeof(em_parsed_token), 1);
	if (parsed_token == NULL) {
		LOGE("Failed to allocate token buffer\n");
		ret = EM_ERR_EM_TOKEN_GET_STATUS_ALLOC_PAR_TOKEN;
		goto out;
	}

	ret = em_esi_get_item(esi, EM_TYPE_ESI_ITEM_TOKEN_ID, token_id_esi, EM_LEN_TOKEN_ID);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get token_id from esi(0x%08x)\n", ret);
		goto out;
	}

	ret = em_esi_get_item(esi, EM_TYPE_ESI_ITEM_DID, did_esi, EM_LEN_DID);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get did from esi(0x%08x)\n", ret);
		goto out;
	}

	ret = em_token_parse(token, &token_ptr, parsed_token);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to parse token(0x%08x)\n", ret);
		flags[1] |= EM_FLAGS_1_EXIST_RETURN_TOKEN_REMOVE;
		goto out;
	}

	len_token = (token_ptr.integrity_info - token_ptr.token_info) + sizeof(em_header_info) +
		    (sizeof(uint32_t) * parsed_token->token.num_of_devices) + (sizeof(uint32_t) * 8);
	ret = em_token_verify(token, len_token, parsed_token->integrity.cert, parsed_token->integrity.len_cert,
			      parsed_token->integrity.signature, parsed_token->integrity.len_signature,
			      parsed_token->token.num_of_devices, parsed_token->device, did_esi, flags);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to verify token(0x%08x)\n", ret);
		flags[1] |= EM_FLAGS_1_EXIST_RETURN_TOKEN_REMOVE;
		goto out;
	}

	if (memcmp(token_id_esi, parsed_token->token.id, EM_LEN_TOKEN_ID) != 0) {
		LOGE("No matched token id\n");
		ret = EM_ERR_EM_TOKEN_GET_STATUS_TOKEN_ID;
		flags[1] |= EM_FLAGS_1_EXIST_RETURN_TOKEN_REMOVE;
		goto out;
	}

	ret = EM_SUCCESS;
out:
	if (parsed_token)
		em_free(parsed_token);

	return ret;
}

int em_token_get_status(em_context *ctx, const uint8_t *token, uint8_t *esi, const uint16_t mode,
			const uint8_t *server_date, uint64_t *flags, const uint8_t is_provision)
{
	int ret, i = 0;

	uint8_t em_version = 0;

	em_esi_meta meta = {0,};
	em_token_ptr token_ptr = {0,};
	em_parsed_token *parsed_token = NULL;

	em_core_v20 core = {0,};
	uint8_t *key = NULL;
	uint8_t not_completed = 0;

	tuc_table_v20 tuc_v20 = {0,};
	tuc_table em_tuc = {0,};

	void *p_tuc = NULL;
	uint32_t len_tuc = 0;

	uint8_t token_id_esi[EM_LEN_TOKEN_ID + 1] = {0,};
	uint8_t did_esi[EM_LEN_TOKEN_ID + 1] = {0,};
	uint8_t priority_date[EM_LEN_DATE + 1] = {0,};
	uint8_t *p_date = NULL;

	uint32_t len_token = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_GET_STATUS, ctx, token, esi, server_date, flags);

	parsed_token = (em_parsed_token *)em_calloc(sizeof(em_parsed_token), 1);
	if (parsed_token == NULL) {
		LOGE("Failed to allocate token buffer\n");
		ret = EM_ERR_EM_TOKEN_GET_STATUS_ALLOC_PAR_TOKEN;
		goto out;
	}

	ret = em_esi_get_item(esi, EM_TYPE_ESI_ITEM_TOKEN_ID, token_id_esi, EM_LEN_TOKEN_ID);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get token_id from esi(0x%08x)\n", ret);
		goto out;
	}

	ret = em_esi_get_item(esi, EM_TYPE_ESI_ITEM_DID, did_esi, EM_LEN_DID);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get did from esi(0x%08x)\n", ret);
		goto out;
	}

	if (memcmp(did_esi, "30", 2) != 0) {
		em_version = 20;

		if (is_provision) {
			ret = em_read_core((uint8_t *)&core, sizeof(em_core_v20));
			if (ret != EM_SUCCESS) {
				if ((uint32_t)ret == EM_ERR_EM_READ_CORE_ALL_ZERO) {
					LOGE("Core is all zero(0x%08x)\n", ret);
					not_completed = 1;
				} else {
					LOGE("Failed to read core(0x%08x)\n", ret);
					goto out;
				}
			}
			key = core.key;
		}

		if (not_completed == 1 || (!is_provision)) {
			ret = em_esi_get_item(esi, EM_TYPE_ESI_ITEM_SHARED_KEY, key, EM_LEN_KEY_CORE_V20);
			if (ret != EM_SUCCESS) {
				LOGE("Failed to get key from esi(0x%08x)\n", ret);
				goto out;
			}
		}
	} else {
		em_version = 30;
	}

	ret = em_token_parse(token, &token_ptr, parsed_token);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to parse token(0x%08x)\n", ret);
		flags[1] |= EM_FLAGS_1_EXIST_RETURN_TOKEN_REMOVE;
		goto out;
	}

	len_token = (token_ptr.integrity_info - token_ptr.token_info) + sizeof(em_header_info) +
		    (sizeof(uint32_t) * parsed_token->token.num_of_devices) + (sizeof(uint32_t) * 8);
	ret = em_token_verify(token, len_token, parsed_token->integrity.cert, parsed_token->integrity.len_cert,
			      parsed_token->integrity.signature, parsed_token->integrity.len_signature,
			      parsed_token->token.num_of_devices, parsed_token->device, did_esi, flags);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to verify token(0x%08x)\n", ret);
		flags[1] |= EM_FLAGS_1_EXIST_RETURN_TOKEN_REMOVE;
		goto out;
	}

	/*
	  TODO : Client Check
	 */
	if (!(flags[0] & EM_FLAGS_0_EXIST_IS_CALLER_CHECKED)) {
		ret = em_client_verify(ctx, parsed_token);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to verify client(0x%08x)\n", ret);
			goto out;
		}
	}

	if (memcmp(token_id_esi, parsed_token->token.id, EM_LEN_TOKEN_ID) != 0) {
		LOGE("No matched token id\n");
		ret = EM_ERR_EM_TOKEN_GET_STATUS_TOKEN_ID;
		flags[1] |= EM_FLAGS_1_EXIST_RETURN_TOKEN_REMOVE;
		goto out;
	}

	ret = EM_ERR_EM_TOKEN_GET_STATUS_NOT_ALLOWED_MODE;
	for (i = 0; i < parsed_token->mode.num_of_modes; i++) {
		if (mode == parsed_token->mode.mode[i]) {
			LOGI("Requested mode(0x%04x) is exists in token\n", mode);
			ret = EM_SUCCESS;
			break;
		}
	}

	if (ret != EM_SUCCESS){
		LOGE("Requested mode(0x%04x) isn't exists in token\n", mode);
		goto out;
	}

	if (em_version == 20) {
		p_tuc = (void *)&tuc_v20;
		len_tuc = sizeof(tuc_table_v20);
	} else {
		p_tuc = (void *)&em_tuc;
		len_tuc = sizeof(tuc_table);
	}

	ret = em_get_tuc_table(esi, p_tuc, len_tuc);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get tuc table(0x%08x)\n", ret);
		goto out;
	}

	if (flags[0] & EM_FLAGS_0_EXIST_DATE) {
		p_date = priority_date;
		ret = em_esi_get_item(esi, EM_TYPE_ESI_ITEM_PRIORITY_TIME, priority_date, EM_LEN_DATE);
		if (ret != EM_SUCCESS) {
			LOGI("Failed to get priority_date(0x%08x)\n", ret);
			p_date = NULL;
		}

		ret = em_token_check_expiration((const char *)token_id_esi,
						(const char *)parsed_token->validity.expiry_date,
						(const char *)server_date, (const char *)p_date);
		if (ret != EM_SUCCESS) {
			flags[1] |= EM_FLAGS_1_EXIST_RETURN_TOKEN_EXPIRED | EM_FLAGS_1_EXIST_RETURN_TOKEN_REMOVE;
			LOGE("Failed to check expiration(0x%08x)\n", ret);
			goto out;
		}

		ret = em_token_restore_tuc(em_version, &token_ptr, parsed_token, p_tuc);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to resotre tuc(0x%08x)\n", ret);
			goto out;
		}

		LOGI("tuc is recovered\n");
	} else {
		if (em_version == 20) {
			for (i = 0; i < tuc_v20.num_of_tuc; i++) {
				if (tuc_v20.tucs[i].mode_index != mode)
					continue;

				if (!(tuc_v20.tucs[i].flags & EM_TYPE_TUC_IS_ENABLE) &&
				    !(tuc_v20.tucs[i].flags & EM_TYPE_TUC_ONLY_ONCE)) {
					LOGI("Requested mode(0x%04x) isn't related usage count(0x%08x)\n", mode,
					     tuc_v20.tucs[i].flags);
					break;
				}

				if (tuc_v20.tucs[i].count == 0) {
					LOGE("mode(%04x) can't be used(%u)\n", mode, tuc_v20.tucs[i].count);
					ret = EM_ERR_EM_TOKEN_GET_STATUS_TUC_ZERO;
					goto out;
				}

				if (ctx->flags[0] & EM_FLAGS_0_EXIST_PLEASE_NO_COUNT)
					LOGI("No count");
				else
					tuc_v20.tucs[i].count = tuc_v20.tucs[i].count - 1;
			}
		} else {
			for (i = 0; i < em_tuc.num_of_tuc; i++) {
				if (em_tuc.tucs[i].mode_index != mode)
					continue;

				if (!(tuc_v20.tucs[i].flags & EM_TYPE_TUC_IS_ENABLE) &&
				    !(tuc_v20.tucs[i].flags & EM_TYPE_TUC_ONLY_ONCE)) {
					LOGI("Requested mode(0x%04x) isn't related usage count(0x%08x)\n", mode,
					     tuc_v20.tucs[i].flags);
					break;
				}

				if (em_tuc.tucs[i].count == 0) {
					LOGE("mode(%04x) can't be used(%u)\n", mode, em_tuc.tucs[i].count);
					ret = EM_ERR_EM_TOKEN_GET_STATUS_TUC_ZERO;
					goto out;
				}

				if (ctx->flags[0] & EM_FLAGS_0_EXIST_PLEASE_NO_COUNT)
					LOGI("No count");
				else
					em_tuc.tucs[i].count = em_tuc.tucs[i].count - 1;
			}
		}
	}

	ret = em_esi_update_item(em_version, esi,
				 (em_version == 20) ? EM_TYPE_ESI_ITEM_TUC_TABLE_LEGACY : EM_TYPE_ESI_ITEM_TUC_TABLES,
				 p_tuc, key, EM_LEN_KEY_CORE_V20);
	if (ret != EM_SUCCESS){
		LOGE("Failed to update tuc(0x%08x)\n", ret);
		goto out;
	}

	memcpy(&meta, esi + EM_LEN_ESI_DIGEST, sizeof(em_esi_meta));

	if (is_provision && not_completed == 0) {
		if (em_version == 20) {
			meta.write_counter += 1;
			core.esi_ctr = meta.write_counter;
			ret = em_write_core((uint8_t *)&core, sizeof(em_core_v20));
			if (ret != EM_SUCCESS) {
				LOGE("Failed to write core(0x%08x)\n", ret);
				goto out;
			}
		}
	}

	ret = em_esi_update(em_version, esi, &meta, NULL, key, EM_LEN_KEY_CORE_V20);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to update esi(0x%08x)\n", ret);
		goto out;
	}

	flags[1] |= EM_FLAGS_1_EXIST_RETURN_ESI;
	// TODO If version is 3.0, save esi to rpmb

	ret = EM_SUCCESS;
out:
	memset(&core, 0, sizeof(em_core_v20));

	if (parsed_token)
		em_free(parsed_token);

	return ret;
}

int em_token_install(uint8_t em_version, uint32_t cmd, uint64_t *flags, uint8_t *esi, const uint8_t *token,
		     const uint8_t *did, const uint8_t is_provision, const em_keeping_item *keep, const uint8_t *key)
{
	int ret;

	em_core_v20 *core = NULL;
	em_esi_meta meta = {0,};

	tuc_table_v20 table_v20 = {0,};

	em_token_ptr token_ptr = {0,};
	em_parsed_token *pt = NULL;

	uint8_t last_token_id_esi[EM_LEN_TOKEN_ID] = {0,};
	uint8_t last_token_id_temp[EM_LEN_TOKEN_ID] = {0,};
	uint8_t last_token_id_temp_2[EM_LEN_TOKEN_ID] = {0,};

	uint32_t lti_type = 0, len_token = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_INSTALL, esi, token, flags);

	pt = (em_parsed_token *)em_calloc(sizeof(em_parsed_token), 1);
	if (pt == NULL) {
		LOGE("Failed to allocate memory\n");
		ret = EM_ERR_EM_TOKEN_INSTALL_ALLOC_PARSED_TOKEN;
		goto out;
	}

	ret = em_token_parse(token, &token_ptr, pt);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to parse token(0x%08x)\n", ret);
		goto out;
	}

	if (cmd == EM_CMD_INSTALL_TOKEN) {
		if (!(flags[0] & EM_FLAGS_0_EXIST_BOOTLOADER)) {
			if (!(flags[0] & EM_FLAGS_0_EXIST_KEEPING_ITEM)) {
				LOGE("Keeping item isn't exists\n");
				ret = EM_ERR_EM_TOKEN_INSTALL_KEEPING_ITEM;
				goto out;
			}

			if (memcmp(keep->single_id, pt->issuer.single_id, EM_LEN_SINGLE_ID) != 0) {
				LOGE("Requested single id isn't matched\n");
				ret = EM_ERR_EM_TOKEN_INSTALL_SINGLE_ID;
				goto out;
			}

			if (memcmp(keep->model_name, pt->device[0].model_name, EM_LEN_MODEL_NAME) != 0){
				LOGE("Rueqested model name isn't matched\n");
				ret = EM_ERR_EM_TOKEN_INSTALL_MODEL_NAME;
				goto out;
			}

			if (memcmp(keep->nonce, pt->issuer.nonce, EM_LEN_NONCE) != 0) {
				LOGE("Requested nonce isn't matched\n");
				ret = EM_ERR_EM_TOKEN_INSTALL_NONCE;
				goto out;
			}

			if (strncmp((char *)token_ptr.header.type, "RMA", EM_LEN_HEADER_FIELD) &&
			    strncmp((char *)token_ptr.header.type, "RSD", EM_LEN_HEADER_FIELD)) {
				LOGE("Not support token type(%02x/%02x/%02x)\n", token_ptr.header.type[0],
				     token_ptr.header.type[1], token_ptr.header.type[2]);
				ret = EM_ERR_EM_TOKEN_INSTALL_NOT_SUPPORT_TYPE;
				goto out;
			}
		} else {
			if (strncmp((char *)token_ptr.header.type, "RWS", EM_LEN_HEADER_FIELD) &&
			    strncmp((char *)token_ptr.header.type, "RSS", EM_LEN_HEADER_FIELD)) {
				LOGE("Not support token type(%02x/%02x/%02x)\n", token_ptr.header.type[0],
				     token_ptr.header.type[1], token_ptr.header.type[2]);
				ret = EM_ERR_EM_TOKEN_INSTALL_NOT_SUPPORT_TYPE_2;
				goto out;
			}
		}
	} else if (cmd == EM_CMD_INSTALL_TOKEN_ESS_V1) {
		if (memcmp(keep->nonce, pt->issuer.nonce, EM_LEN_NONCE) != 0) {
			LOGE("Requested nonce isn't matched\n");
			ret = EM_ERR_EM_TOKEN_INSTALL_NONCE_ESS;
			goto out;
		}
	}

	if (em_strncasecmp((const char *)did, (const char *)pt->device[0].did, EM_LEN_DID) != 0) {
		LOGE("Did isn't matched\n");
		ret = EM_ERR_EM_TOKEN_INSTALL_DID;
		goto out;
	}

	len_token = (token_ptr.integrity_info - token_ptr.token_info) + sizeof(em_header_info) +
		    (sizeof(uint32_t) * pt->token.num_of_devices) + (sizeof(uint32_t) * 8);
	ret = em_token_verify(token, len_token, pt->integrity.cert, pt->integrity.len_cert, pt->integrity.signature,
			      pt->integrity.len_signature, pt->token.num_of_devices, pt->device, did, flags);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to verify token(0x%08x)\n", ret);
		goto out;
	}

	ret = em_token_is_from_fac(pt->integrity.cert, pt->integrity.len_cert);
	if (ret != EM_SUCCESS && (uint32_t)ret != EM_ERR_EM_TOKEN_IS_FROM_FAC_FROM_FAC) {
		LOGE("Failed to check location where issued(0x%08x)\n", ret);
		goto out;
	}

	lti_type = ((uint32_t)ret == EM_ERR_EM_TOKEN_IS_FROM_FAC_FROM_FAC) ? EM_TYPE_ESI_ITEM_LATEST_FAC_TOKEN_ID
									   : EM_TYPE_ESI_ITEM_LATEST_TOKEN_ID;

	ret = em_esi_get_item(esi, lti_type, last_token_id_esi, EM_LEN_TOKEN_ID);
	if (ret == EM_SUCCESS) {
		if (lti_type == EM_ERR_EM_TOKEN_IS_FROM_FAC_FROM_FAC) {
			memcpy(last_token_id_temp, last_token_id_esi, 5);
			memcpy(last_token_id_temp_2, pt->token.id, 5);
		} else {
			memcpy(last_token_id_temp, last_token_id_esi, 5);
			memcpy(last_token_id_temp + 5, last_token_id_esi + 10, 6);
			memcpy(last_token_id_temp_2, pt->token.id, 5);
			memcpy(last_token_id_temp_2 + 5, pt->token.id + 10, 6);
		}

		if (memcmp(last_token_id_temp, last_token_id_temp_2, EM_LEN_TOKEN_ID) >= 0 ) {
			LOGE("This token is already used\n");
			ret = EM_ERR_EM_TOKEN_INSTALL_ALREADY_USED;
			goto out;
		}
	}

	ret = em_esi_update_item(em_version, esi, lti_type, pt->token.id, key, EM_LEN_KEY_CORE_V20);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to update lti to esi(0x%08x)\n", ret);
		goto out;
	}

	ret = em_esi_update_item(em_version, esi, EM_TYPE_ESI_ITEM_TOKEN_ID, pt->token.id, key, EM_LEN_KEY_CORE_V20);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to update token id to esi(0x%08x)\n", ret);
		goto out;
	}

	if (em_version == 30) {
		// TODO MAKE3.0 TUC
	} else {
		ret = em_token_make_tuc_v20(&table_v20, &token_ptr, pt);
		if (ret != EM_SUCCESS){
			LOGE("Failed to make tuc v20(0x%08x)\n", ret);
			goto out;
		}

		ret = em_esi_update_item(em_version, esi, EM_TYPE_ESI_ITEM_TUC_TABLE_LEGACY, (uint8_t *)&table_v20, key,
					 EM_LEN_KEY_CORE_V20);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to add tuc table v20 to esi(0x%08x)\n", ret);
			goto out;
		}

		memcpy(&meta, esi + EM_LEN_ESI_DIGEST, sizeof(em_esi_meta));
		meta.write_counter += 1;

		ret = em_esi_update(em_version, esi, &meta, NULL, key, EM_LEN_KEY_CORE_V20);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to update esi(0x%08x)\n", ret);
			goto out;
		}

		if (is_provision) {
			core = (em_core_v20 *)em_calloc(sizeof(em_core_v20), 1);
			if (core == NULL) {
				LOGE("Failed to alloc core\n");
				ret = EM_ERR_EM_TOKEN_INSTALL_ALLOC_CORE;
				goto out;
			}

			ret = em_read_core((uint8_t *)core, sizeof(em_core_v20));
			if (ret != EM_SUCCESS) {
				if ((uint32_t)ret == EM_ERR_EM_READ_CORE_ALL_ZERO) {
					LOGE("Core is all zero\n");
				} else {
					LOGE("Failed to read core(0x%08x)\n", ret);
					goto out;
				}
			} else {
				core->esi_ctr = meta.write_counter;

				ret = em_write_core((uint8_t *)core, sizeof(em_core_v20));
				if (ret != EM_SUCCESS) {
					LOGE("Failed to write core(0x%08x)\n", ret);
					goto out;
				}
			}
		}
	}

	flags[1] |= EM_FLAGS_1_EXIST_RETURN_ESI;

	ret = EM_SUCCESS;
out:
	if (pt) {
		em_free(pt);
		pt = NULL;
	}

	if (core) {
		memset(core, 0, sizeof(em_core_v20));
		em_free(core);
		core = NULL;
	}

	return ret;
}

int em_token_is_from_fac(const uint8_t *cert, const int len_cert)
{
	int ret = EM_SUCCESS;

	char uid[EM_LEN_CERT_UID] = {0,};
	char *uid_attr[EM_LEN_CERT_UID_ATTR] = {0,};
	int index = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_IS_FROM_FAC, cert);

	ret = em_crypto_get_uid_from_cert(cert, len_cert, uid, sizeof(uid));
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get uid from cert(0x%08x)\n", ret);
		goto out;
	}

	uid_attr[index] = em_strtok(uid, ":");
	while (uid_attr[index] != NULL)
		uid_attr[++index] = em_strtok(NULL, ":");

	if (uid_attr[2] == NULL) {
		LOGE("No server type\n");
		goto out;
	}

	if (memcmp(uid_attr[2], "10", 2) == 0) {
		LOGI("This is cert from factory\n");
		ret = EM_ERR_EM_TOKEN_IS_FROM_FAC_FROM_FAC;
	}

out:
	return ret;
}

int em_token_get_usage_count(const uint8_t em_version, const uint8_t *esi, const uint32_t mode, uint32_t *out)
{
	int ret, i = 0;

	tuc_table *table = NULL;
	tuc_table_v20 *table_v20 = NULL;

	uint8_t *buf = NULL;

	buf = (uint8_t *)em_calloc(sizeof(tuc_table), 1); // sizeof tuc_table > tuc_table_v20
	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_GET_USAGE_COUNT, esi, out, buf);

	ret = em_esi_get_item(esi, (em_version == 20) ? EM_TYPE_ESI_ITEM_TUC_TABLE_LEGACY : EM_TYPE_ESI_ITEM_TUC_TABLES,
			      buf, (em_version == 20) ? sizeof(tuc_table_v20) : sizeof(tuc_table));
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get tuc table(0x%04x/0x%08x)\n",
		     (em_version == 20) ? EM_TYPE_ESI_ITEM_TUC_TABLE_LEGACY : EM_TYPE_ESI_ITEM_TUC_TABLES, ret);
		goto out;
	}

	if (em_version == 20) {
		table_v20 = (tuc_table_v20 *)buf;
		for (i = 0; i < table_v20->num_of_tuc; i++) {
			if (table_v20->tucs[i].mode_index == mode) {
				*out = table_v20->tucs[i].count;
				break;
			} else if (!(table_v20->tucs[i].flags & EM_TYPE_TUC_IS_ENABLE)){
				*out = 99999999;
				break;
			}
		}
	} else {
		table = (tuc_table *)buf;
		for (i = 0; i < table->num_of_tuc; i++) {
			if (table->tucs[i].mode_index == mode) {
				*out = table->tucs[i].count;
				break;
			}
		}
	}

	ret = EM_SUCCESS;
out:
	if (buf)
		em_free(buf);

	return ret;
}

static void em_token_set_no_token(uint8_t *buf, uint32_t buf_size, uint32_t *offset)
{
	em_add_string(buf, buf_size, offset, (uint8_t *)EM_MAGIC_NOK, (uint32_t)strlen(EM_MAGIC_NOK));
	em_add_string(buf, buf_size, offset, (uint8_t *)EM_MAGIC_GET_MODE_TOKENINZER,
		      (uint32_t)strlen(EM_MAGIC_GET_MODE_TOKENINZER));
	em_add_string(buf, buf_size, offset, (uint8_t *)EM_MAGIC_GET_MODE_NO_TOKEN,
		      (uint32_t)strlen(EM_MAGIC_GET_MODE_NO_TOKEN));
}

int em_token_get_only_mode(const uint8_t *token, const uint8_t *esi, uint64_t *flags, uint8_t *out, uint32_t *len_out)
{
	int ret;

	uint32_t index = 0, i = 0, len = 0;

	uint8_t did_esi[EM_LEN_DID] = {0,};
	uint8_t token_id_esi[EM_LEN_TOKEN_ID] = {0,};

	em_token_ptr token_ptr = {0,};
	em_parsed_token *pt = NULL;
	uint32_t len_token = 0;

	char str_tmp[EM_LEN_TOKEN_MODE_STRING] = {0,};

	pt = (em_parsed_token *)em_calloc(sizeof(em_parsed_token), 1);
	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_GET_ONLY_MODE_T_ID, token, esi, flags, len_out, out, pt);

	ret = em_esi_get_item(esi, EM_TYPE_ESI_ITEM_DID, did_esi, EM_LEN_DID);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get did from esi(0x%08x)\n", ret);
		goto out;
	}

	ret = em_token_parse(token, &token_ptr, pt);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to parse token(0x%08x)\n", ret);
		goto out;
	}

	len_token = (token_ptr.integrity_info - token_ptr.token_info) + sizeof(em_header_info) +
		    (sizeof(uint32_t) * pt->token.num_of_devices) + (sizeof(uint32_t) * 8);
	ret = em_token_verify(token, len_token, pt->integrity.cert, pt->integrity.len_cert, pt->integrity.signature,
			      pt->integrity.len_signature, pt->token.num_of_devices, pt->device, did_esi, flags);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to verify token(0x%08x)\n", ret);
		flags[1] |= EM_FLAGS_1_EXIST_RETURN_TOKEN_REMOVE;
		goto out;
	}

	ret = em_esi_get_item(esi, EM_TYPE_ESI_ITEM_TOKEN_ID, token_id_esi, EM_LEN_TOKEN_ID);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get token id from esi(0x%08x)\n", ret);
		goto out;
	}

	if (memcmp(token_id_esi, pt->token.id, EM_LEN_TOKEN_ID) != 0) {
		LOGE("Not matched token_id\n");
		ret = EM_ERR_EM_TOKEN_GET_ONLY_MODE_T_ID;
		goto out;
	}

	if (em_strncasecmp((const char *)did_esi, (const char *)pt->device[0].did, EM_LEN_DID) != 0) {
		LOGE("Not matched did\n");
		ret = EM_ERR_EM_TOKEN_GET_ONLY_MODE_DID;
		goto out;
	}

	if (pt->mode.num_of_modes > EM_LEN_MAX_MODE) {
		LOGE("num of modes(%u) is bigger than max(%u)\n", pt->mode.num_of_modes, EM_LEN_MAX_MODE);
		ret = EM_ERR_EM_TOKEN_GET_ONLY_MODE_MODE;
		goto out;
	}

	for (i = 0; i < pt->mode.num_of_modes; i++) {
		em_itoa_for_mode(pt->mode.mode[i], str_tmp);
		len = strlen(str_tmp);
		if (len > EM_LEN_TOKEN_MODE_STRING) {
			LOGE("Mode length isn't normal(%u)\n", len);
			ret = EM_ERR_EM_TOKEN_GET_ONLY_MODE_M_LEN;
			goto out;
		}

		if (index + len + 1 > EM_LEN_TOKEN_MODE_INFORMATION) {
			LOGE("buf isn't enough(%u)\n", len);
			LOGE("%s/%s\n", str_tmp, out);
			ret = EM_ERR_EM_TOKEN_GET_ONLY_MODE_BUF;
			goto out;
		}
		memcpy(out + index, str_tmp, len);
		if ((uint32_t)i != (uint32_t)(pt->mode.num_of_modes - 1)) {
			out[index + len] = '_';
			index += len + 1;
		}
	}
	*len_out = index + len;

	ret = EM_SUCCESS;
out:
	if (pt) {
		em_free(pt);
		pt = NULL;
	}
	return ret;
}

int em_token_get_mode_information(const uint8_t *token, const uint8_t *esi, uint64_t *flags, uint8_t *out,
				  uint32_t *len_out)
{
	int ret, i = 0;

	uint32_t index = 0, len = 0;

	uint8_t did_esi[EM_LEN_DID] = {0,};
	uint8_t is_dev_device = 1;

	uint8_t *modes = NULL;
	uint32_t len_modes;

	modes = (uint8_t *)em_calloc(EM_LEN_TOKEN_MODE_INFORMATION, 1);
	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_GET_MODE_INFORMATION, token, esi, flags, len_out, out, modes);

	ret = em_esi_get_item(esi, EM_TYPE_ESI_ITEM_DID, did_esi, EM_LEN_DID);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get did from esi(0x%08x)\n", ret);
		goto out;
	}

	if (memcmp(did_esi + 14, "11", 2) == 0)
		is_dev_device = 0;

	em_add_string(out, EM_LEN_TOKEN_MODE_INFORMATION, &index, (uint8_t *)EM_MAGIC_OK,
		      (uint32_t)strlen(EM_MAGIC_OK));
	em_add_string(out, EM_LEN_TOKEN_MODE_INFORMATION, &index, (uint8_t *)EM_MAGIC_GET_MODE_TOKENINZER,
		      (uint32_t)strlen(EM_MAGIC_GET_MODE_TOKENINZER));

	if (is_dev_device == 1)
		em_add_string(out, EM_LEN_TOKEN_MODE_INFORMATION, &index, (uint8_t *)EM_MAGIC_GET_MODE_FROM_DEV,
			      (uint32_t)strlen(EM_MAGIC_GET_MODE_FROM_DEV));
	else
		em_add_string(out, EM_LEN_TOKEN_MODE_INFORMATION, &index, (uint8_t *)EM_MAGIC_GET_MODE_FROM_TOKEN,
			      (uint32_t)strlen(EM_MAGIC_GET_MODE_FROM_TOKEN));

	ret = em_token_get_only_mode(token, esi, flags, modes, &len_modes);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get mode(0x%08x)\n", ret);

		if (is_dev_device == 0) {
			memset(out, 0, index);
			index = 0;
			em_token_set_no_token(out, EM_LEN_TOKEN_MODE_INFORMATION, &index);
		} else {
			LOGI("This is dev device and no token\n");
		}

		flags[1] |= EM_FLAGS_1_EXIST_RETURN_MESSAGE;
		*len_out = index;

		ret = EM_SUCCESS;
		goto out;
	}

	em_add_string(out, EM_LEN_TOKEN_MODE_INFORMATION, &index, (uint8_t *)EM_MAGIC_GET_MODE_TOKENINZER,
		      (uint32_t)strlen(EM_MAGIC_GET_MODE_TOKENINZER));
	em_add_string(out, EM_LEN_TOKEN_MODE_INFORMATION, &index, modes, len_modes);
	*len_out = index;

	flags[1] |= EM_FLAGS_1_EXIST_RETURN_MESSAGE;

	ret = EM_SUCCESS;
out:
	LOGI("Return message (%d)%s\n", *len_out, out);
	if (modes)
		em_free(modes);

	return ret;
}

int em_token_get_mode_information_for_bit(const uint8_t *token, const uint8_t *esi, uint64_t *flags,
					  uint64_t *mode_bits)
{
	int ret, i = 0;

	uint32_t index = 0, len = 0;

	uint8_t did_esi[EM_LEN_DID] = {0,};
	uint8_t token_id_esi[EM_LEN_TOKEN_ID] = {0,};

	em_token_ptr token_ptr = {0,};
	em_parsed_token *pt = NULL;
	uint32_t len_token = 0;

	pt = (em_parsed_token *)em_calloc(sizeof(em_parsed_token), 1);
	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_GET_MODE_BIT, token, esi, flags, mode_bits, pt);

	ret = em_esi_get_item(esi, EM_TYPE_ESI_ITEM_DID, did_esi, EM_LEN_DID);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get did from esi(0x%08x)\n", ret);
		goto out;
	}

	ret = em_token_parse(token, &token_ptr, pt);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to parse token(0x%08x)\n", ret);
		goto out;
	}

	len_token = (token_ptr.integrity_info - token_ptr.token_info) + sizeof(em_header_info) +
		    (sizeof(uint32_t) * pt->token.num_of_devices) + (sizeof(uint32_t) * 8);
	ret = em_token_verify(token, len_token, pt->integrity.cert, pt->integrity.len_cert, pt->integrity.signature,
			      pt->integrity.len_signature, pt->token.num_of_devices, pt->device, did_esi, flags);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to verify token(0x%08x)\n", ret);
		flags[1] |= EM_FLAGS_1_EXIST_RETURN_TOKEN_REMOVE;
		goto out;
	}

	ret = em_esi_get_item(esi, EM_TYPE_ESI_ITEM_TOKEN_ID, token_id_esi, EM_LEN_TOKEN_ID);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get token id from esi(0x%08x)\n", ret);
		goto out;
	}

	if (memcmp(token_id_esi, pt->token.id, EM_LEN_TOKEN_ID) != 0) {
		LOGE("Not matched token_id\n");
		ret = EM_ERR_EM_TOKEN_GET_MODE_BIT_TOKEN_ID;
		goto out;
	}

	if (em_strncasecmp((const char *)did_esi, (const char *)pt->device[0].did, EM_LEN_DID) != 0) {
		LOGE("Not matched token\n");
		ret = EM_ERR_EM_TOKEN_GET_MODE_BIT_DID;
		goto out;
	}

	if (pt->mode.num_of_modes > EM_LEN_MAX_MODE) {
		LOGE("num of modes(%u) is bigger than max(%u)\n", pt->mode.num_of_modes,
		     EM_LEN_MAX_MODE);
		ret = EM_ERR_EM_TOKEN_GET_MODE_BIT_MODE;
		goto out;
	}

	for (i = 0; i < pt->mode.num_of_modes; i++) {
		if (pt->mode.mode[i] / 64 >= EM_LEN_GET_MODES_BIT_BUFFER) {
			LOGE("Mode flag buffer isn't enough(0x%08x)\n", pt->mode.mode[i]);
			ret = EM_ERR_EM_TOKEN_GET_MODE_BIT_BUF;
			goto out;
		}

		mode_bits[(pt->mode.mode[i] + 1) / 64] |= (1 << ((pt->mode.mode[i]) % 64));
	}

	LOGI("%"PRIu64"%"PRIu64"%"PRIu64"%"PRIu64"\n", mode_bits[0], mode_bits[1], mode_bits[2], mode_bits[3]);
	flags[0] = flags[0] | EM_FLAGS_0_EXIST_MODE_BITS;
	ret = EM_SUCCESS;
out:
	if (pt) {
		em_free(pt);
		pt = NULL;
	}

	return ret;
}

int em_token_check_time_msg(uint8_t *message, uint32_t len_message, uint64_t *flags, em_keeping_item *keep)
{
	int ret;

	em_header_info header = {0,};
	em_token_ptr token_ptr = {0,};
	em_integrity_info *integ = NULL;

	char time[EM_LEN_DATE + 1] = {0,};
	uint8_t msg[EM_LEN_NONCE + EM_LEN_DATE] = {0,};

	integ = (em_integrity_info *)em_calloc(sizeof(em_integrity_info), 1);
	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_CHECK_TIME_MSG, message, flags, keep, integ);

	memcpy(&header, message, sizeof(em_header_info));
	if (memcmp(header.prefix, EM_MAGIC_PACKET_ENG, strlen(EM_MAGIC_PACKET_ENG)) != 0) {
		LOGE("Unknown header magic(%02x/%02x/%02x\n", header.prefix[0], header.prefix[1], header.prefix[2]);
		ret = EM_ERR_EM_TOKEN_CHECK_TIME_MSG_MAGIC;
		goto out;
	}

	if (memcmp(header.type, EM_MAGIC_TYPE_TIME_RESPONSE, strlen(EM_MAGIC_TYPE_TIME_RESPONSE)) != 0) {
		LOGE("Unknown type(%02x/%02x/%02x)\n", header.type[0], header.type[1], header.type[2]);
		ret = EM_ERR_EM_TOKEN_CHECK_TIME_MSG_TYPE;
		goto out;
	}

	if (memcmp(header.version, EM_MAGIC_PACKET_VERSION, strlen(EM_MAGIC_PACKET_VERSION)) != 0) {
		LOGE("Unknown version(%02x/%02x/%02x)\n", header.version[0], header.version[1], header.version[2]);
		ret = EM_ERR_EM_TOKEN_CHECK_TIME_MSG_VERSION;
		goto out;
	}

	token_ptr.integrity_info = message + sizeof(em_header_info) + EM_LEN_DATE;
	ret = em_token_parse_integrity_info(integ, &token_ptr);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to check integrity(0x%08x)\n", ret);
		goto out;
	}

	memcpy(msg, keep->nonce, EM_LEN_NONCE);
	memcpy(msg + EM_LEN_NONCE, message + sizeof(em_header_info), EM_LEN_DATE);

	ret = em_crypto_verify_rsa_signature(integ->cert, integ->len_cert, integ->signature, integ->len_signature, msg,
					     EM_LEN_NONCE + EM_LEN_DATE);
	if (ret != EM_SUCCESS && (uint32_t)ret != EM_DEV_OK) {
		LOGE("Failed to verify signature(0x%08x)\n", ret);
		goto out;
	}

	memcpy(keep->date, msg + EM_LEN_NONCE, EM_LEN_DATE);

	ret = EM_SUCCESS;
out:
	flags[1] |= EM_FLAGS_1_EXIST_RETURN_KEEPING_ITEM;

	memset(msg, 0, EM_LEN_NONCE + EM_LEN_DATE);
	memset(keep->nonce, 0, EM_LEN_NONCE);

	if (integ)
		em_free(integ);


	return ret;
}

int em_token_get_expiry_date(em_context *ctx, char *out_date)
{
	int ret;

	uint32_t len_token = 0;
	uint8_t token_id_esi[EM_LEN_TOKEN_ID] = {0,};
	uint8_t did_esi[EM_LEN_DID] = {0,};

	em_token_ptr token_ptr = {0,};
	em_parsed_token *pt = NULL;

	pt = (em_parsed_token *)em_calloc(sizeof(em_parsed_token), 1);
	EM_CHECK_NULL(__func__, EM_ERR_EM_TOKEN_GET_EXPIRY_DATE, ctx, out_date, pt);

	ret = em_token_parse(ctx->token, &token_ptr, pt);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to parse token(0x%08x)\n", ret);
		goto out;
	}

	ret = em_esi_get_item(ctx->esi, EM_TYPE_ESI_ITEM_DID, (uint8_t *)did_esi, EM_LEN_DID);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get did from esi(0x%08x)\n", ret);
		goto out;
	}

	len_token = (token_ptr.integrity_info - token_ptr.token_info) + sizeof(em_header_info) +
		    (sizeof(uint32_t) * pt->token.num_of_devices) + (sizeof(uint32_t) * 8);
	ret =
	    em_token_verify(ctx->token, len_token, pt->integrity.cert, pt->integrity.len_cert, pt->integrity.signature,
			    pt->integrity.len_signature, pt->token.num_of_devices, pt->device, did_esi, ctx->flags);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to verify token(0x%08x)\n", ret);
		ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_TOKEN_REMOVE;
		goto out;
	}

	ret = em_esi_get_item(ctx->esi, EM_TYPE_ESI_ITEM_TOKEN_ID, (uint8_t *)token_id_esi, EM_LEN_TOKEN_ID);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get token id from esi(0x%08x)\n", ret);
		goto out;
	}

	if (memcmp((const char *)token_id_esi, (const char *)pt->token.id, EM_LEN_TOKEN_ID) != 0) {
		LOGE("Not matched token id\n");
		ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_TOKEN_REMOVE;
		ret = EM_ERR_EM_TOKEN_GET_EXPIRY_DATE_TOKEN_ID;
		goto out;
	}

	memcpy(out_date, (const char *)pt->validity.expiry_date, EM_LEN_DATE);
	ret = EM_SUCCESS;
out:
	if (pt)
		em_free(pt);

	return ret;
}
