#include "em_common.h"

void em_esi_read_meta(em_esi_meta *meta, uint8_t *esi)
{
	uint32_t offset = EM_LEN_ESI_DIGEST;

	em_get_data_from_raw(meta->magic, EM_LEN_ESI_MAGIC, esi, EM_LEN_ESI, &offset, EM_LEN_ESI_MAGIC);
	em_get_data_from_raw((uint8_t *)&(meta->version), (uint32_t)sizeof(meta->version), esi, EM_LEN_ESI, &offset,
			     (uint32_t)sizeof(meta->version));
	em_get_data_from_raw((uint8_t *)&(meta->writer), (uint32_t)sizeof(meta->writer), esi, EM_LEN_ESI, &offset,
			     (uint32_t)sizeof(meta->writer));
	em_get_data_from_raw((uint8_t *)&(meta->write_counter), (uint32_t)sizeof(meta->write_counter), esi, EM_LEN_ESI,
			     &offset, (uint32_t)sizeof(meta->write_counter));
	em_get_data_from_raw((uint8_t *)&(meta->size_of_items), (uint32_t)sizeof(meta->size_of_items), esi, EM_LEN_ESI,
			     &offset, (uint32_t)sizeof(meta->size_of_items));
	em_get_data_from_raw((uint8_t *)&(meta->num_of_items), (uint32_t)sizeof(meta->num_of_items), esi, EM_LEN_ESI,
			     &offset, (uint32_t)sizeof(meta->num_of_items));

#ifdef DEBUG_LOG
	LOGD("meta->magic : %02x/%02x/%02x\n", meta->magic[0], meta->magic[1], meta->magic[2]);
	LOGD("meta->version : %x\n", meta->version);
	LOGD("meta->writer : %x\n", meta->writer);
	LOGD("meta->write_counter : %u\n", meta->write_counter);
	LOGD("meta->size_of_items : %u\n", meta->size_of_items);
	LOGD("meta->num_of_items : %u\n", meta->num_of_items);
#endif
}

void em_esi_write_meta(em_esi_meta *meta, uint8_t *esi)
{
	uint32_t offset = EM_LEN_ESI_DIGEST;

	em_set_data_to_raw(meta->magic, EM_LEN_ESI_MAGIC, esi, EM_LEN_ESI, &offset, EM_LEN_ESI_MAGIC);
	em_set_data_to_raw((uint8_t *)&(meta->version), (uint32_t)sizeof(meta->version), esi, EM_LEN_ESI, &offset,
			   (uint32_t)sizeof(meta->version));
	em_set_data_to_raw((uint8_t *)&(meta->writer), (uint32_t)sizeof(meta->writer), esi, EM_LEN_ESI, &offset,
			   (uint32_t)sizeof(meta->writer));
	em_set_data_to_raw((uint8_t *)&(meta->write_counter), (uint32_t)sizeof(meta->write_counter), esi, EM_LEN_ESI,
			   &offset, (uint32_t)sizeof(meta->write_counter));
	em_set_data_to_raw((uint8_t *)&(meta->size_of_items), (uint32_t)sizeof(meta->size_of_items), esi, EM_LEN_ESI,
			   &offset, (uint32_t)sizeof(meta->size_of_items));
	em_set_data_to_raw((uint8_t *)&(meta->num_of_items), (uint32_t)sizeof(meta->num_of_items), esi, EM_LEN_ESI,
			   &offset, (uint32_t)sizeof(meta->num_of_items));
#ifdef DEBUG_LOG
	LOGD("meta->magic : %02x/%02x/%02x\n", meta->magic[0], meta->magic[1], meta->magic[2]);
	LOGD("meta->version : %x\n", meta->version);
	LOGD("meta->writer : %x\n", meta->writer);
	LOGD("meta->write_counter : %u\n", meta->write_counter);
	LOGD("meta->size_of_items : %u\n", meta->size_of_items);
	LOGD("meta->num_of_items : %u\n", meta->num_of_items);
#endif
}

void em_esi_read_tuc_table_v20(tuc_table_v20 *table, uint8_t *raw_data)
{
	int i;
	uint32_t offset = 0;

	em_get_data_from_raw((uint8_t *)&(table->num_of_tuc), (uint32_t)sizeof(table->num_of_tuc), raw_data,
			     EM_STRUCT_ESI_TUC_TABLE_V20_SIZE, &offset, (uint32_t)sizeof(table->num_of_tuc));

	if (table->num_of_tuc > EM_LEN_MAX_MODE_TUC_V20) {
		LOGE("Invaild num of tuc%u\n", table->num_of_tuc);
		return;
	}

	for (i = 0; i < table->num_of_tuc; i++) {
		uint16_t *mode_index = &(table->tucs[i].mode_index);
		uint8_t *flags = &(table->tucs[i].flags);
		uint32_t *count = &(table->tucs[i].count);
		em_get_data_from_raw((uint8_t *)mode_index, (uint32_t)sizeof(*mode_index), raw_data,
				     EM_STRUCT_ESI_TUC_TABLE_V20_SIZE, &offset, (uint32_t)sizeof(*mode_index));
		em_get_data_from_raw((uint8_t *)flags, (uint32_t)sizeof(*flags), raw_data,
				     EM_STRUCT_ESI_TUC_TABLE_V20_SIZE, &offset, (uint32_t)sizeof(*flags));
		em_get_data_from_raw((uint8_t *)count, (uint32_t)sizeof(*count), raw_data,
				     EM_STRUCT_ESI_TUC_TABLE_V20_SIZE, &offset, (uint32_t)sizeof(*count));
#ifdef DEBUG_LOG
		LOGD("tucs[%u]:mode_index : %04x\n", i, table->tucs[i].mode_index);
		LOGD("tucs[%u]:flags : %02x\n", i, table->tucs[i].flags);
		LOGD("tucs[%u]:count : %u\n", i, table->tucs[i].count);
#endif
	}
}

void em_esi_read_tuc_table(tuc_table *table, uint8_t *raw_data)
{
	int i;
	uint32_t offset = 0;

	em_get_data_from_raw((uint8_t *)&(table->num_of_tuc), (uint32_t)sizeof(table->num_of_tuc), raw_data,
			     EM_STRUCT_ESI_TUC_TABLE_SIZE, &offset, (uint32_t)sizeof(table->num_of_tuc));

	if (table->num_of_tuc > EM_LEN_MAX_MODE_TUC_V20) {
		LOGE("Invaild num of tuc%u\n", table->num_of_tuc);
		return;
	}

	for (i = 0; i < table->num_of_tuc; i++) {
		uint16_t *mode_index = &(table->tucs[i].mode_index);
		uint8_t *flags = &(table->tucs[i].flags);
		uint32_t *count = &(table->tucs[i].count);
		uint32_t *max_count = &(table->tucs[i].max_count);
		em_get_data_from_raw((uint8_t *)mode_index, (uint32_t)sizeof(*mode_index), raw_data,
				     EM_STRUCT_ESI_TUC_TABLE_SIZE, &offset, (uint32_t)sizeof(*mode_index));
		em_get_data_from_raw((uint8_t *)flags, (uint32_t)sizeof(*flags), raw_data, EM_STRUCT_ESI_TUC_TABLE_SIZE,
				     &offset, (uint32_t)sizeof(*flags));
		em_get_data_from_raw((uint8_t *)count, (uint32_t)sizeof(*count), raw_data, EM_STRUCT_ESI_TUC_TABLE_SIZE,
				     &offset, (uint32_t)sizeof(*count));
		em_get_data_from_raw((uint8_t *)max_count, (uint32_t)sizeof(*max_count), raw_data,
				     EM_STRUCT_ESI_TUC_TABLE_SIZE, &offset, (uint32_t)sizeof(*max_count));
#ifdef DEBUG_LOG
		LOGD("tucs[%u]:mode_index : %04x\n", i, table->tucs[i].mode_index);
		LOGD("tucs[%u]:flags : %02x\n", i, table->tucs[i].flags);
		LOGD("tucs[%u]:count : %u\n", i, table->tucs[i].count);
		LOGD("tucs[%u]:max_count : %u\n", i, table->tucs[i].max_count);
#endif
	}
}

static void em_esi_item_free(em_esi_item *items, int num_of_items)
{
	int i = 0;
	em_esi_item *p = NULL;

	if (items == NULL)
		return;

	p = items;

	if (p) {
		for (i = 0; i < num_of_items; i++) {
			if (p[i].data) {
				em_free(p[i].data);
				p[i].data = NULL;
			}
		}
		em_free(p);
		p = NULL;
	}
}

int em_esi_check_init_state(const uint8_t *init_state)
{
	int ret = EM_ERR_EM_ESI_CHECK_INIT_STATE_UNKNOWN;

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_CHECK_INIT_STATE, init_state);

	if (memcmp(init_state, EM_MAGIC_ESI_V20_SHARING, strlen(EM_MAGIC_ESI_V20_SHARING)) == 0) {
		ret = EM_TYPE_INIT_STATE_SHARING;
	} else if (memcmp(init_state, EM_MAGIC_ESI_V20_SHARED, strlen(EM_MAGIC_ESI_V20_SHARED)) == 0) {
		ret = EM_TYPE_INIT_STATE_SHARED;
	} else if (memcmp(init_state, EM_MAGIC_ESI_V20_PRESHARED, strlen(EM_MAGIC_ESI_V20_PRESHARED)) == 0) {
		ret = EM_TYPE_INIT_STATE_PRESHARED;
	} else if (memcmp(init_state, EM_MAGIC_ESI_V20_INTERMEDIATE, strlen(EM_MAGIC_ESI_V20_INTERMEDIATE)) == 0) {
		ret = EM_TYPE_INIT_STATE_INTERMEDIATE;
	} else if (memcmp(init_state, EM_MAGIC_ESI_V20_COMPLETED, strlen(EM_MAGIC_ESI_V20_COMPLETED)) == 0) {
		ret = EM_TYPE_INIT_STATE_COMPLETED;
	} else {
		LOGE("%02x%02x%02x%02x%02x%02x%02x%02x\n", init_state[0], init_state[1], init_state[2], init_state[3],
		     init_state[4], init_state[5], init_state[6], init_state[7]);
		LOGE("%02x%02x%02x%02x%02x%02x%02x%02x\n", init_state[8], init_state[9], init_state[10], init_state[11],
		     init_state[12], init_state[13], init_state[14], init_state[15]);
	}
out:
	return ret;
}

int em_esi_get_all_item(const uint8_t *esi, em_esi_item *buf, uint32_t buf_size)
{
	int ret, i = 0;
	em_esi_meta meta = {0,};
	uint32_t offset = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_GET_ALL_ITEM, esi, buf);

	offset += EM_LEN_ESI_DIGEST;
	em_esi_read_meta(&meta, (uint8_t *)esi);
	offset += (uint32_t)EM_STRUCT_ESI_META_SIZE;

	if (buf_size < meta.num_of_items) {
		LOGE("buf size isn't enough(%u/%u)\n", buf_size, meta.num_of_items);
		ret = EM_ERR_EM_ESI_GET_ALL_ITEM_BUF_SIZE;
		goto out;
	}

	for (i = 0; i < meta.num_of_items; i++) {
		if (offset >= EM_LEN_ESI) {
			LOGE("meta data isn't normal(%u/%u)\n", offset, EM_LEN_ESI);
			ret = EM_ERR_EM_ESI_GET_ALL_ITEM_META_DATA;
			goto out;
		}

		memcpy(&(buf[i].elem), esi + offset, sizeof(em_default_element));
		offset += (uint32_t)sizeof(em_default_element);

		buf[i].data = (uint8_t *)em_calloc(buf[i].elem.len, 1);
		if (buf[i].data == NULL) {
			if (buf[i].elem.len == 0)
				continue;

			LOGE("Failed to allocate items[%u/%u].data memory\n", i, buf[i].elem.type);
			ret = EM_ERR_EM_ESI_GET_ALL_ITEM_ALLOC_ITEM_BUF;
			goto out;
		}
		memcpy(buf[i].data, esi + offset, buf[i].elem.len);
		offset += buf[i].elem.len;
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

static int em_esi_update_digest(const uint32_t version, uint8_t *esi, const uint8_t *key)
{
	int ret;
	uint8_t digest[EM_LEN_ESI_DIGEST] = {0,};
	em_esi_meta meta = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_UPDATE_DIGEST, esi);

	em_esi_read_meta(&meta, esi);

	if (version == 20) {
		if (key == NULL) {
			LOGI("Key isn't exists\n");
			ret = EM_SUCCESS;
			goto out;
		}
		ret = em_crypto_hmac(digest, esi + EM_LEN_ESI_DIGEST, EM_STRUCT_ESI_META_SIZE + meta.size_of_items, key,
				     EM_LEN_KEY_CORE_V20);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to make hmac(0x%08x)\n", ret);
			goto out;
		}
	} else if (version == 30) {
		ret = em_crypto_sha256(esi + EM_LEN_ESI_DIGEST, EM_STRUCT_ESI_META_SIZE + meta.size_of_items, digest);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to make hash(0x%08x)\n", ret);
			goto out;
		}
	} else {
		ret = EM_ERR_EM_ESI_UPDATE_DIGEST_UNKNOWN_VERSION;
		goto out;
	}

	memcpy(esi, digest, EM_LEN_ESI_DIGEST);
	ret = EM_SUCCESS;
out:
	return ret;
}

int em_esi_initialize(uint32_t version, uint8_t *buf, uint8_t *key, uint32_t write_counter)
{
	int ret;
	em_esi_meta meta = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_INIT, buf);

	memcpy(meta.magic, EM_MAGIC_ESI, EM_LEN_ESI_MAGIC);

#ifdef EMAS
	meta.writer = 'E';
#elif EMBS
	meta.writer = 'B';
#endif
	meta.write_counter = write_counter;
	meta.size_of_items = 0;
	meta.num_of_items = 0;
	meta.version = version;

	em_esi_write_meta(&meta, buf);

	ret = em_esi_update_digest(version, buf, key);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to update digest(0x%08x)\n", ret);
		goto out;
	}

	ret = EM_SUCCESS;
out:

	return ret;
}

int em_esi_update(const uint32_t version, uint8_t *esi_buf, em_esi_meta *meta, const em_esi_item *item,
		  const uint8_t *key, const uint32_t key_len)
{
	int ret, i;
	uint32_t offset = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_UPDATE, esi_buf, meta);

#ifdef EMAS
	meta->writer = 'E';
#elif EMBS
	meta->writer = 'B';
#endif

	offset += EM_LEN_ESI_DIGEST;
	em_esi_write_meta(meta, esi_buf);
	offset += (uint32_t)EM_STRUCT_ESI_META_SIZE;

	if (item != NULL) {
		for (i = 0; i < meta->num_of_items; i++) {
			memcpy(esi_buf + offset, &item[i].elem, sizeof(em_default_element));
			offset += (uint32_t)sizeof(em_default_element);

			if (item[i].elem.type > EM_TYPE_ESI_ITEM_MAX)
				LOGI("Unknown item type(%u/%u)\n", item[i].elem.type, item[i].elem.len);

			if (offset + item[i].elem.len >= EM_LEN_ESI) {
				LOGE("Buffer size isn't enough(%u/%u)\n", offset + item[i].elem.len, EM_LEN_ESI);
				ret = EM_ERR_EM_ESI_UPDATE_BUFFER_SIZE;
				goto out;
			}
			memcpy(esi_buf + offset, item[i].data, item[i].elem.len);
			offset += item[i].elem.len;
		}
	}

	ret = em_esi_update_digest(version, esi_buf, key);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to update digest(0x%08x)\n", ret);
		goto out;
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

int em_esi_update_item(const uint32_t version, uint8_t *esi, const uint32_t type, const uint8_t *data,
		       const uint8_t *key, const uint32_t key_len)
{
	int ret, i = 0, exist_flags = 0, index = 0;
	uint32_t offset = 0, item_size = 0, item_alloc_size = 0;
	em_esi_meta meta = {0,};
	em_esi_item *items = NULL, *p = NULL, *temp = NULL;

	tuc_table_v20 *p_tuc_table_v20 = NULL;
	tuc_table *p_tuc_table = NULL;

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_UPDATE_ITEM, esi, data);

	em_esi_read_meta(&meta, esi);

	if (meta.num_of_items > 0) {
		items = (em_esi_item *)em_calloc(sizeof(em_esi_item), meta.num_of_items);
		if (items == NULL) {
			LOGE("Failed to allocate memory\n");
			ret = EM_ERR_EM_ESI_UPDATE_ITEM_ALLOC_ITEMS;
			goto out;
		}
	}
	item_alloc_size = meta.num_of_items;

	if (items != NULL) {
		ret = em_esi_get_all_item(esi, items, meta.num_of_items);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to get item(0x%08x)\n", ret);
			goto out;
		}
	}

	index = -1;
	for (i = 0; i < meta.num_of_items; i++) {
		if (type == items[i].elem.type) {
			exist_flags = 1;
			index = i;
			break;
		}
	}

	item_size = meta.size_of_items;

	if (exist_flags == 1) {
		temp = &items[index];
		switch (type) {
		case EM_TYPE_ESI_ITEM_INIT_STATE:
		case EM_TYPE_ESI_ITEM_TOKEN_STATE:
			if (temp->data)
				em_free(temp->data);
			item_size -= temp->elem.len;
			temp->elem.len = strlen((char *)data);
			temp->data = (uint8_t *)em_calloc(temp->elem.len, 1);
			if (temp->data == NULL) {
				LOGE("Failed to allocate buffer(%u)\n", type);
				ret = EM_ERR_EM_ESI_UPDATE_ITEM_ALLOC_STATE_BUFFER;
				goto out;
			}
			break;
		case EM_TYPE_ESI_ITEM_TUC_TABLE_LEGACY:
			if (temp->data)
				em_free(temp->data);
			item_size -= temp->elem.len;

			p_tuc_table_v20 = (tuc_table_v20 *)data;
			temp->elem.len = (p_tuc_table_v20->num_of_tuc * EM_STRUCT_ESI_TUC_V20_SIZE) + 2;

			temp->data = (uint8_t *)em_calloc(temp->elem.len, 1);
			if (temp->data == NULL) {
				LOGE("failed to allocate memory tuc buffer\n");
				ret = EM_ERR_EM_ESI_UPDATE_ITEM_ALLOC_TUC_BUFFER;
				goto out;
			}
			item_size += temp->elem.len;
			memcpy(temp->data, &(p_tuc_table_v20->num_of_tuc), sizeof(uint16_t));
			offset = sizeof(uint16_t);
			for (i = 0; i < p_tuc_table_v20->num_of_tuc; i++) {
				memcpy(temp->data + offset, &(p_tuc_table_v20->tucs[i].mode_index), sizeof(uint16_t));
				offset += (uint32_t)sizeof(uint16_t);
				memcpy(temp->data + offset, &(p_tuc_table_v20->tucs[i].flags), sizeof(uint8_t));
				offset += (uint32_t)sizeof(uint8_t);
				memcpy(temp->data + offset, &(p_tuc_table_v20->tucs[i].count), sizeof(uint32_t));
				offset += (uint32_t)sizeof(uint32_t);
			}

			break;
		case EM_TYPE_ESI_ITEM_TUC_TABLES:
			if (temp->data)
				em_free(temp->data);
			item_size -= temp->elem.len;

			p_tuc_table = (tuc_table *)data;
			temp->elem.len = (p_tuc_table->num_of_tuc * EM_STRUCT_ESI_TUC_SIZE) + 2;

			temp->data = (uint8_t *)em_calloc(temp->elem.len, 1);
			if (temp->data == NULL) {
				LOGE("failed to allocate memory tuc buffer\n");
				ret = EM_ERR_EM_ESI_UPDATE_ITEM_ALLOC_TUC_BUFFER2;
				goto out;
			}
			item_size += temp->elem.len;

			memcpy(temp->data, &(p_tuc_table->num_of_tuc), sizeof(uint16_t));
			offset = (uint32_t)sizeof(uint16_t);
			for (i = 0; i < p_tuc_table->num_of_tuc; i++) {
				memcpy(temp->data + offset, &(p_tuc_table->tucs[i].mode_index), sizeof(uint16_t));
				offset += (uint32_t)sizeof(uint16_t);
				memcpy(temp->data + offset, &(p_tuc_table->tucs[i].flags), sizeof(uint8_t));
				offset += (uint32_t)sizeof(uint8_t);
				memcpy(temp->data + offset, &(p_tuc_table->tucs[i].count), sizeof(uint32_t));
				offset += (uint32_t)sizeof(uint32_t);
				memcpy(temp->data + offset, &(p_tuc_table->tucs[i].max_count), sizeof(uint32_t));
				offset += (uint32_t)sizeof(uint32_t);
			}
			break;
		case EM_TYPE_ESI_ITEM_SHARED_KEY:
		case EM_TYPE_ESI_ITEM_SHARED_ESI_COUNTER:
		case EM_TYPE_ESI_ITEM_TOKEN_ID:
		case EM_TYPE_ESI_ITEM_LATEST_TOKEN_ID:
		case EM_TYPE_ESI_ITEM_LATEST_FAC_TOKEN_ID:
		case EM_TYPE_ESI_ITEM_DID:
		case EM_TYPE_ESI_ITEM_RECOVERY_COUNTER:
		case EM_TYPE_ESI_ITEM_RECOVERY_COUNTER_BL:
		case EM_TYPE_ESI_ITEM_IIN:
		case EM_TYPE_ESI_ITEM_PRIORITY_TIME:
			break;
		default:
			LOGE("Unknown Item type(0x%02x)\n", type);
			ret = EM_ERR_EM_ESI_UPDATE_ITEM_UNKNOWN_TYPE2;
			goto out;
		}

		if (type != EM_TYPE_ESI_ITEM_TUC_TABLE_LEGACY && type != EM_TYPE_ESI_ITEM_TUC_TABLES)
			memcpy(temp->data, data, temp->elem.len);

		meta.size_of_items = item_size;
		ret = em_esi_update(version, esi, &meta, items, key, key_len);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to update ESI(0x%08x)\n", ret);
			goto out;
		}
	} else {
		p = (em_esi_item *)em_calloc((meta.num_of_items + 1) * sizeof(em_esi_item), 1);
		if (p == NULL) {
			LOGE("Failed to allocate memory\n");
			ret = EM_ERR_EM_ESI_UPDATE_ITEM_ALLOC_P;
			goto out;
		}

		for (i = 0; i < meta.num_of_items; i++) {
			p[i].elem.type = items[i].elem.type;
			p[i].elem.len = items[i].elem.len;

			p[i].data = (uint8_t *)em_calloc(p[i].elem.len, 1);
			if (p[i].data == NULL) {
				if (p[i].elem.len == 0)
					continue;
				LOGE("Failed to allocate memory\n");
				ret = EM_ERR_EM_ESI_UPDATE_ITEM_ALLOC_DATA_BUFFER;
				goto out;
			}
			memcpy(p[i].data, items[i].data, p[i].elem.len);
		}
		p[meta.num_of_items].elem.type = type;

		switch (type) {
		case EM_TYPE_ESI_ITEM_INIT_STATE:
		case EM_TYPE_ESI_ITEM_TOKEN_STATE:
			p[meta.num_of_items].elem.len = strlen((char *)data);
			break;
		case EM_TYPE_ESI_ITEM_TUC_TABLE_LEGACY:
			p_tuc_table_v20 = (tuc_table_v20 *)data;
			p[meta.num_of_items].elem.len = (p_tuc_table_v20->num_of_tuc * EM_STRUCT_ESI_TUC_V20_SIZE) + 2;
			break;
		case EM_TYPE_ESI_ITEM_TUC_TABLES:
			p_tuc_table = (tuc_table *)data;
			p[meta.num_of_items].elem.len = (p_tuc_table->num_of_tuc * EM_STRUCT_ESI_TUC_SIZE) + 2;
			break;
		case EM_TYPE_ESI_ITEM_SHARED_KEY:
			p[meta.num_of_items].elem.len = 32;
			break;
		case EM_TYPE_ESI_ITEM_SHARED_ESI_COUNTER:
			/* case ESI_ITEM_SHARED_ITL_COUNTER: */
			p[meta.num_of_items].elem.len = sizeof(uint32_t);
			break;
		case EM_TYPE_ESI_ITEM_TOKEN_ID:
		case EM_TYPE_ESI_ITEM_LATEST_TOKEN_ID:
		case EM_TYPE_ESI_ITEM_LATEST_FAC_TOKEN_ID:
			p[meta.num_of_items].elem.len = EM_LEN_TOKEN_ID;
			break;
		case EM_TYPE_ESI_ITEM_DID:
			p[meta.num_of_items].elem.len = EM_LEN_DID;
			break;
		case EM_TYPE_ESI_ITEM_RECOVERY_COUNTER:
			p[meta.num_of_items].elem.len = sizeof(uint32_t);
			break;
		case EM_TYPE_ESI_ITEM_RECOVERY_COUNTER_BL:
			p[meta.num_of_items].elem.len = sizeof(uint32_t);
			break;
		case EM_TYPE_ESI_ITEM_IIN:
			p[meta.num_of_items].elem.len = EM_LEN_IIN;
			break;
		case EM_TYPE_ESI_ITEM_PRIORITY_TIME:
			p[meta.num_of_items].elem.len = EM_LEN_PRIORITY_TIME;
			break;
		default:
			LOGE("Unknown Item type(%02x)\n", type);
			ret = EM_ERR_EM_ESI_UPDATE_ITEM_UNKNOWN_TYPE2;
			goto out;
		}

		p[meta.num_of_items].data = (uint8_t *)em_calloc(p[meta.num_of_items].elem.len, 1);
		if (p[meta.num_of_items].data == NULL) {
			LOGE("Failed to allocate memory\n");
			ret = EM_ERR_EM_ESI_UPDATE_ITEM_ALLOC_DATA_BUF_2;
			goto out;
		}

		if (type == EM_TYPE_ESI_ITEM_TUC_TABLE_LEGACY) {
			memcpy(p[meta.num_of_items].data, &(p_tuc_table_v20->num_of_tuc), sizeof(uint16_t));
			offset = (uint32_t)sizeof(uint16_t);
			for (i = 0; i < p_tuc_table_v20->num_of_tuc; i++) {
				memcpy(p[meta.num_of_items].data + offset, &p_tuc_table_v20->tucs[i].mode_index,
				       sizeof(uint16_t));
				offset += (uint32_t)sizeof(uint16_t);
				memcpy(p[meta.num_of_items].data + offset, &p_tuc_table_v20->tucs[i].flags,
				       sizeof(uint8_t));
				offset += (uint32_t)sizeof(uint8_t);
				memcpy(p[meta.num_of_items].data + offset, &p_tuc_table_v20->tucs[i].count,
				       sizeof(uint32_t));
				offset += (uint32_t)sizeof(uint32_t);
			}
		} else if (type == EM_TYPE_ESI_ITEM_TUC_TABLES) {
			memcpy(p[meta.num_of_items].data + offset, &(p_tuc_table->num_of_tuc), sizeof(uint16_t));
			offset = (uint32_t)sizeof(uint16_t);
			for (i = 0; i < p_tuc_table->num_of_tuc; i++) {
				memcpy(p[meta.num_of_items].data + offset, &p_tuc_table->tucs[i].mode_index,
				       sizeof(uint16_t));
				offset += (uint32_t)sizeof(uint16_t);
				memcpy(p[meta.num_of_items].data + offset, &p_tuc_table->tucs[i].flags,
				       sizeof(uint8_t));
				offset += (uint32_t)sizeof(uint8_t);
				memcpy(p[meta.num_of_items].data + offset, &p_tuc_table->tucs[i].count,
				       sizeof(uint32_t));
				offset += (uint32_t)sizeof(uint32_t);
				memcpy(p[meta.num_of_items].data + offset, &p_tuc_table->tucs[i].max_count,
				       sizeof(uint32_t));
				offset += (uint32_t)sizeof(uint32_t);
			}
		} else {
			memcpy(p[meta.num_of_items].data, data, p[meta.num_of_items].elem.len);
		}

		item_size += (uint32_t)sizeof(em_default_element) + p[meta.num_of_items].elem.len;
		meta.size_of_items = item_size;
		meta.num_of_items += 1;
		ret = em_esi_update(version, esi, &meta, p, key, key_len);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to update ESI(%08x)\n", ret);
			goto out;
		}
	}
	ret = EM_SUCCESS;
out:
	em_esi_item_free(items, item_alloc_size);
	em_esi_item_free(p, item_alloc_size + 1);

	return ret;
}

int em_esi_remove_item(const uint32_t version, uint8_t *esi, const uint32_t type, const uint8_t *key,
		       const uint32_t key_len)
{
	int ret, i = 0;

	em_esi_meta meta = {0,};
	em_esi_item *items = NULL, *new_items = NULL;

	uint32_t item_size = 0, origin_num_of_items = 0, offset = 0;
	uint32_t exist_flags = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_REMOVE_ITEM, esi);

	em_esi_read_meta(&meta, esi);

	origin_num_of_items = meta.num_of_items;

	if (meta.num_of_items != 0) {
		items = (em_esi_item *)em_calloc(sizeof(em_esi_item), meta.num_of_items);
		if (items == NULL) {
			LOGE("Failed to allocate item memory\n");
			ret = EM_ERR_EM_ESI_REMOVE_ITEM_ALLOC_ITEM_BUFFER;
			goto out;
		}

		ret = em_esi_get_all_item(esi, items, meta.num_of_items);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to get item(0x%08x)\n", ret);
			goto out;
		}

		for (i = 0; i < meta.num_of_items; i++) {
			if (type == items[i].elem.type) {
				exist_flags = 1;
				break;
			}
		}

		if (exist_flags == 0) {
			LOGE("Not exists requested item type(0x%04x)\n", type);
			ret = EM_ERR_EM_ESI_REMOVE_ITEM_NOT_EXISTS_TYPE;
			goto out;
		}
	} else {
		LOGE("No item in ESI(%u)\n", meta.num_of_items);
		ret = EM_ERR_EM_ESI_REMOVE_ITEM_NO_ITEM;
		goto out;
	}

	if (meta.num_of_items > 1) {
		new_items = (em_esi_item *)em_calloc(sizeof(em_esi_item), meta.num_of_items - 1);
		if (new_items == NULL) {
			LOGE("Failed to allocate new items buffer\n");
			ret = EM_ERR_EM_ESI_REMOVE_ITEM_ALLOC_NEW_BUFFER;
			goto out;
		}

		for (i = 0; i < meta.num_of_items; i++) {
			if (items[i].elem.type == type) // Ignore requsted type for remove
				continue;

			new_items[offset].elem.type = items[i].elem.type;
			new_items[offset].elem.len = items[i].elem.len;
			new_items[offset].data = (uint8_t *)em_calloc(items[i].elem.len, 1);
			if (new_items[offset].data == NULL && items[i].elem.len != 0) {
				LOGE("Failed to allocate memory data\n");
				ret = EM_ERR_EM_ESI_REMOVE_ITEM_ALLOC_DATA_BUFFER;
				goto out;
			}

			if (items[i].elem.len != 0)
				memcpy(new_items[offset].data, items[i].data, items[i].elem.len);

			item_size += items[i].elem.len + (uint32_t)sizeof(em_default_element);
			offset += 1;
		}
	}

	meta.size_of_items = item_size;
	meta.num_of_items -= 1;

	memset(esi, 0, EM_LEN_ESI);
	ret = em_esi_update(version, esi, &meta, new_items, key, EM_LEN_KEY_CORE_V20);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to update esi(0x%08x)\n", ret);
		goto out;
	}

	ret = EM_SUCCESS;
out:
	em_esi_item_free(items, origin_num_of_items);

	em_esi_item_free(new_items, offset);

	return ret;
}

int em_esi_get_item(const uint8_t *esi, const uint32_t type, uint8_t *buf, const uint32_t len_buf)
{
	int ret, i = 0;

	em_esi_meta meta = {0,};
	em_esi_item *items = NULL;

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_GET_ITEM, esi, buf);

	em_esi_read_meta(&meta, (uint8_t *)esi);

	if (meta.num_of_items == 0) {
		LOGE("Type(%u) isn't exists in ESI\n", type);
		ret = EM_ERR_EM_ESI_GET_ITEM_ITEM_IS_NOT_EXISTS;
		goto out;
	}

	items = (em_esi_item *)em_calloc(meta.num_of_items, sizeof(em_esi_item));
	if (items == NULL) {
		LOGE("Failed to allocate item memory\n");
		ret = EM_ERR_EM_ESI_GET_ITEM_ALLOC_ITEM_BUFFER;
		goto out;
	}

	ret = em_esi_get_all_item(esi, items, meta.num_of_items);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get items(0x%08x)\n", ret);
		goto out;
	}

	for (i = 0; i < meta.num_of_items; i++) {
		if (items[i].elem.type == type) {
			if (items[i].elem.len > len_buf) {
				LOGE("buf size isn't enough(%u/%u)\n", items[i].elem.len, len_buf);
				ret = EM_ERR_EM_ESI_GET_ITEM_BUFFER_NOT_ENOUGH;
				goto out;
			}
			memcpy(buf, items[i].data, items[i].elem.len);
			ret = EM_SUCCESS;
			goto out;
		}
	}

	ret = EM_ERR_EM_ESI_GET_ITEM_ITEM_IS_NOT_EXISTS;
out:
	em_esi_item_free(items, meta.num_of_items);
	return ret;
}

int em_get_tuc_table(uint8_t *esi, void *buf, uint32_t len_buf)
{
	int ret;
	em_esi_meta meta = {0,};
	uint32_t type = 0;
	uint32_t len_table_max = 0;
	void *temp = NULL;
	uint32_t len_temp = 0;

	em_esi_read_meta(&meta, esi);

	if (meta.version == 20) {
		type = EM_TYPE_ESI_ITEM_TUC_TABLE_LEGACY;
		len_table_max = sizeof(tuc_table_v20);
		len_temp = EM_STRUCT_ESI_TUC_TABLE_V20_SIZE;
	} else if (meta.version == 30) {
		type = EM_TYPE_ESI_ITEM_TUC_TABLES;
		len_table_max = sizeof(tuc_table);
		len_temp = EM_STRUCT_ESI_TUC_TABLE_SIZE;
	} else {
		LOGE("Unknown version(%u)\n", meta.version);
		ret = EM_ERR_EM_ESI_GET_TUC_TABLE_UNKNOWN_VERSION;
		goto out;
	}

	if (len_buf < len_table_max) {
		LOGE("buffer isn't enough(%u/%u)\n", len_buf, len_table_max);
		ret = EM_ERR_EM_ESI_GET_TUC_TABLE_NOT_ENOUGH;
		goto out;
	}

	temp = (em_esi_item *)em_calloc(1, len_temp);
	if (temp == NULL) {
		LOGE("Failed to allocate buffer\n");
		ret = EM_ERR_EM_ESI_GET_TUC_ALLOC_BUFFER;
		goto out;
	}

	ret = em_esi_get_item(esi, type, temp, len_temp);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get item(0x%08x)\n", ret);
		goto out;
	}

	if (meta.version == 20) {
		em_esi_read_tuc_table_v20((tuc_table_v20 *)buf, temp);
	} else if (meta.version == 30) {
		em_esi_read_tuc_table((tuc_table *)buf, temp);
	} else {
		LOGE("Unknown version(%u)\n", meta.version);
		ret = EM_ERR_EM_ESI_GET_TUC_TABLE_UNKNOWN_VERSION;
		goto out;
	}

	ret = EM_SUCCESS;
out:
	if (temp)
		em_free(temp);

	return ret;
}

int em_esi_check_validation(uint8_t *local_esi)
{
	int ret;
	// TODO ESI 3.0 Validation

	if (local_esi == NULL) {
		// READ ESI on RPMB
	}

	// HASH CHECK

	ret = EM_SUCCESS;

	return ret;
}

int em_esi_check_meta(const uint8_t *esi)
{
	int ret;
	uint32_t size = 0;

	em_esi_meta meta = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_CHECK_META, esi);

	em_esi_read_meta(&meta, (uint8_t *)esi);
	if (memcmp(meta.magic, EM_MAGIC_ESI, strlen(EM_MAGIC_ESI)) != 0) {
		LOGE("esi meta isn't normal(%02x/%02x/%02x)\n", meta.magic[0], meta.magic[1], meta.magic[2]);
		ret = EM_ERR_EM_ESI_CHECK_META_ESI_MAGIC;
		goto out;
	}

	size = EM_LEN_ESI_DIGEST + EM_STRUCT_ESI_META_SIZE + meta.size_of_items;
	if (size > EM_LEN_ESI) {
		LOGE("meta data isn't normal(%u/%u)\n", size, EM_LEN_ESI);
		ret = EM_ERR_EM_ESI_CHECK_META_INVALID;
		goto out;
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

int em_esi_check_validation_v20(em_context *ctx)
{
	int ret;

	em_core_v20 *core = NULL;
	em_esi_meta meta = {0,};

	uint8_t hmac[EM_LEN_ESI_DIGEST] = {0,};
	uint8_t key[EM_LEN_KEY_CORE_V20] = {0,};
	uint8_t init_state[EM_LEN_ESI_INIT_STATE] = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_CHECK_VALIDATION_V20, ctx);
	core = (em_core_v20 *)&ctx->core_v20;

	ret = em_esi_check_meta(ctx->esi);
	if (ret != EM_SUCCESS) {
		LOGE("Invalid ESI meta\n");
		goto out;
	}

	em_esi_read_meta(&meta, ctx->esi);

	ret = em_esi_get_item(ctx->esi, EM_TYPE_ESI_ITEM_INIT_STATE, init_state, EM_LEN_ESI_INIT_STATE);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get init state from esi(0x%08x)\n", ret);
		goto out;
	}

	ret = em_esi_check_init_state(init_state);
	if ((uint32_t)ret == EM_ERR_EM_ESI_CHECK_INIT_STATE_UNKNOWN) {
		LOGE("init state is unknown\n");
		goto out;
	}

	if (ctx->is_provision && ret == EM_TYPE_INIT_STATE_COMPLETED) {
		if (memcmp(core->magic, EM_MAGIC_EM_CORE, strlen(EM_MAGIC_EM_CORE)) != 0) {
			LOGE("core mg isn't normal(%02x/%02x/%02x/%02x)\n", core->magic[0], core->magic[1],
			     core->magic[2], core->magic[3]);
			ret = EM_ERR_EM_ESI_CHECK_VALIDATION_V20_CORE_MG;
			goto out;
		}

		memcpy(key, core->key, EM_LEN_KEY_CORE_V20);
	} else {
		ret = em_esi_get_item(ctx->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;
		}
	}

	ret = em_crypto_hmac(hmac, ctx->esi + EM_LEN_ESI_DIGEST, EM_STRUCT_ESI_META_SIZE + meta.size_of_items, key,
			     EM_LEN_KEY_CORE_V20);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to make hamc(0x%08x)\n", ret);
		goto out;
	}

	if (memcmp(ctx->esi, hmac, EM_LEN_ESI_DIGEST) != 0) {
		LOGE("Failed to validate ESI\n");
		ret = EM_ERR_EM_ESI_CHECK_VALIDATION_V20_HMAC;
		goto out;
	}

	if (core->esi_ctr > meta.write_counter) {
		int recovery_counter = 0;
		uint32_t recovery_cnt_get_type;

		LOGE("Failed to check count(%u/%u)\n", core->esi_ctr, meta.write_counter);

		if (ctx->flags[0] & EM_FLAGS_0_EXIST_BOOTLOADER)
			recovery_cnt_get_type = EM_TYPE_ESI_ITEM_RECOVERY_COUNTER;
		else
			recovery_cnt_get_type = EM_TYPE_ESI_ITEM_RECOVERY_COUNTER_BL;

		LOGI("Recovery counter type : %08x\n", recovery_cnt_get_type);

		ret = em_esi_get_item(ctx->esi, (const uint32_t)recovery_cnt_get_type, (uint8_t *)&recovery_counter,
				      EM_LEN_RECOVERY_COUNTER);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to get recovery counter from esi(0x%08x)\n", ret);
			goto out;
		}

		if (memcmp(core->magic, EM_MAGIC_EM_CORE, strlen(EM_MAGIC_EM_CORE)) == 0) {
			if (core->recovery_ctr <= (uint32_t)recovery_counter) {
				LOGI("Recovery counter is valid\n");
				core->recovery_ctr += 1;
			} else {
				LOGE("Recovery counter is invalid(core rcounter %d, esi rcounter %d)\n",
				     core->recovery_ctr, recovery_counter);
				ret = EM_ERR_EM_ESI_CHECK_VALIDATION_V20_COUNTER;
				goto out;
			}

			ret = em_esi_remove_item(20, ctx->esi, (const uint32_t)recovery_cnt_get_type, key,
						 EM_LEN_KEY_CORE_V20);
			if (ret != EM_SUCCESS) {
				LOGE("Failed to remove rcounter(%08x)\n", ret);
				goto out;
			}

			core->esi_ctr = meta.write_counter;
			ctx->flags[1] |= EM_FLAGS_1_WRITE_CORE;
		} else {
			ret = EM_ERR_EM_ESI_CHECK_VALIDATION_V20_COUNTER;
			goto out;
		}
		ret = EM_SUCCESS_RECOVERY_COUNTER;
		goto out;
	}

	ret = EM_SUCCESS;
out:
	core = NULL;
	memset(key, 0, EM_LEN_KEY_CORE_V20);

	return ret;
}

int em_esi_recovery(em_context *ctx)
{
	int ret, i = 0;

	uint8_t *digest = NULL, *item_pointer = NULL, need_init_flag = 0;
	uint16_t version_itl = 0, len_internal_itl = 0, lti_type = 0;
	uint32_t esi_counter = 0, recovery_counter = 0, server_type = 0;
	uint32_t offset_digest = 0, offset_packet = 0, offset_itl = 0, offset_item = 0;

	em_core_v20 *core = NULL;
	em_esi_meta meta = {0,};

	uint8_t token_id[EM_LEN_TOKEN_ID] = {0,}, token_id2[EM_LEN_TOKEN_ID] = {0,};
	uint8_t latest_token_id[EM_LEN_TOKEN_ID] = {0,};
	uint8_t init_state_v20[20] = {0,};

	uint8_t *itl = NULL;

	digest = (uint8_t *)em_calloc(10256, sizeof(uint8_t));
	EM_CHECK_NULL(__func__, EM_ERR_EM_ESI_RECOVERY, ctx, digest);
	core = (em_core_v20 *)&ctx->core_v20;

	if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_DID) || em_is_all_zero(ctx->did, EM_LEN_DID) == EM_SUCCESS) {
		LOGE("Please check flags and did\n");
		ret = EM_ERR_EM_ESI_RECOVERY_DID;
		goto out;
	}

	itl = ctx->recovery_data;

	memcpy(digest + offset_digest, ctx->did, EM_LEN_DID);
	offset_digest += EM_LEN_DID;

	memcpy(digest + offset_digest, ctx->keep.nonce, EM_LEN_NONCE);
	offset_digest += EM_LEN_NONCE;

	if (ctx->is_provision) {
		if (memcmp(core->magic, EM_MAGIC_EM_CORE, strlen(EM_MAGIC_EM_CORE)) != 0)
			need_init_flag = 1;
		else
			esi_counter = core->esi_ctr;
	} else {
		LOGI("Not Provisioned\n");
		need_init_flag = 1;
	}

	ret = em_esi_initialize(ctx->em_version, ctx->esi, NULL, esi_counter);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to initialize esi(0x%08x)\n", ret);
		goto out;
	}

	em_esi_read_meta(&meta, ctx->esi);

	if (memcmp(itl, EM_MAGIC_PACKET_ENG, strlen(EM_MAGIC_PACKET_ENG)) != 0) {
		LOGE("Unknown packet matic(%02x/%02x/%02x)\n", itl[0], itl[1], itl[2]);
		ret = EM_ERR_EM_ESI_RECOVERY_ITL_PACKET_MAGIC;
		goto out;
	}
	offset_packet += strlen(EM_MAGIC_PACKET_ENG);

	// IRS Chkeck
	if (memcmp(itl + offset_packet, EM_MAGIC_IRS, strlen(EM_MAGIC_IRS)) != 0) {
		LOGE("Unknown packet type(%02x/%02x/%02x)\n", itl[offset_packet], itl[offset_packet + 1],
		     itl[offset_packet + 2]);
		ret = EM_ERR_EM_ESI_RECOVERY_ITL_PACKET_TYPE;
		goto out;
	}
	offset_packet += strlen(EM_MAGIC_IRS);

	if (memcmp(itl + offset_packet, EM_MAGIC_PACKET_VERSION2, strlen(EM_MAGIC_PACKET_VERSION2)) != 0) {
		LOGE("Unknown packet version(%02x/%02x/%02x/%02x)\n", itl[offset_packet], itl[offset_packet + 1],
		     itl[offset_packet + 2], itl[offset_packet + 3]);
		ret = EM_ERR_EM_ESI_RECOVERY_ITL_PACKET_VERSION;
		goto out;
	}
	offset_packet += strlen(EM_MAGIC_PACKET_VERSION2);

	offset_itl = offset_packet;
	if (memcmp(itl + offset_packet, EM_MAGIC_ITL, strlen(EM_MAGIC_ITL)) != 0) {
		LOGE("Unknown ITL magic(%02x/%02x/%02x)\n", itl[offset_packet], itl[offset_packet + 1],
		     itl[offset_packet + 1]);
		ret = EM_ERR_EM_ESI_RECOVERY_ITL_MAGIC;
		goto out;
	}
	offset_packet += strlen(EM_MAGIC_ITL);

	memcpy(&version_itl, itl + offset_packet, sizeof(uint16_t));
	if (version_itl != EM_VERSION_ITL) {
		LOGE("Unknown ITL version(0x%04x)\n", version_itl);
		ret = EM_ERR_EM_ESI_RECOVERY_ITL_VERSION;
		goto out;
	}
	offset_packet += (uint32_t)sizeof(uint16_t);

	memcpy(&len_internal_itl, itl + offset_packet, sizeof(uint16_t));
	offset_packet += (uint32_t)sizeof(uint16_t) + 9; /* 9 byte is reserved */
	item_pointer = itl + offset_packet;

	if (ctx->cmd == EM_CMD_RECOVERY_ESS_V1 && len_internal_itl == 0) /* FOR ESS ITL SIZE */
		len_internal_itl++;

	offset_packet += len_internal_itl * EM_LEN_TOKEN_ID;

	meta.num_of_items = len_internal_itl;
	meta.size_of_items = len_internal_itl * EM_LEN_TOKEN_ID;

	if (meta.size_of_items > EM_LEN_RECOVERY_DATA) {
		LOGE("itl item size isn't normal(%u/%u)\n", meta.size_of_items, (uint32_t)EM_LEN_RECOVERY_DATA);
		ret = EM_ERR_EM_ESI_RECOVERY_ITL_MAX_SIZE;
		goto out;
	}

	if (meta.num_of_items > EM_LEN_RECOVERY_NUM_OF_TOKEN_ID) {
		LOGE("itl num data isn't normal(%u/%u)\n", meta.num_of_items,
		     (uint32_t)EM_LEN_RECOVERY_NUM_OF_TOKEN_ID);
		ret = EM_ERR_EM_ESI_RECOVERY_ITL_MAX_NUM;
		goto out;
	}

	if (EM_LEN_RECOVERY_DATA - offset_itl < EM_LEN_TOKEN_ID + meta.size_of_items) {
		LOGE("itl item size isn't normal(%u/%d)\n", meta.size_of_items, offset_itl);
		ret = EM_ERR_EM_ESI_RECOVERY_ITL_MAX_NUM2;
		goto out;
	}
	memcpy(digest + offset_digest, itl + offset_itl, EM_LEN_TOKEN_ID + meta.size_of_items);
	offset_digest += EM_LEN_TOKEN_ID + meta.size_of_items;

	ret = em_crypto_verify_recovery_data(itl, offset_packet, digest, offset_digest, &server_type);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to verify recovery data(0x%08x)\n", ret);
		goto out;
	}

	offset_item = 0;
	if ((EM_LEN_TOKEN_ID * meta.num_of_items) >= (EM_LEN_RECOVERY_DATA - offset_packet + meta.size_of_items)) {
		LOGE("offset isn't normal(%u/%u)\n", EM_LEN_TOKEN_ID * meta.num_of_items,
		     (uint32_t)(EM_LEN_RECOVERY_DATA - offset_packet + meta.size_of_items));
		ret = EM_ERR_EM_ESI_RECOVERY_ITL_MAX_SIZE_2;
		goto out;
	}

	for (i = 0; i < meta.num_of_items; i++) {
		memcpy(token_id, item_pointer + offset_item, 5);
		memcpy(token_id + 5, item_pointer + offset_item + 10, 6);

		if (memcmp(token_id, token_id2, 11) > 0) {
			memcpy(token_id2, token_id, 11);
			memcpy(latest_token_id, item_pointer + offset_item, EM_LEN_TOKEN_ID);
		}
		offset_item += EM_LEN_TOKEN_ID;
	}

	lti_type = (server_type == EM_FLAG_EM_TOKEN_FROM_FAC) ? EM_TYPE_ESI_ITEM_LATEST_FAC_TOKEN_ID
							      : EM_TYPE_ESI_ITEM_LATEST_TOKEN_ID;
	ret = em_esi_update_item(ctx->em_version, ctx->esi, lti_type, latest_token_id, NULL, EM_LEN_KEY_CORE_V20);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to update LTI(%04x) to ESI without key(%08x)\n", lti_type, ret);
		goto out;
	}

	if (need_init_flag) {
		// Sharing
		uint32_t shared_esi_counter = 0;

		if (em_get_random(core->key, EM_LEN_KEY_CORE_V20) != EM_LEN_KEY_CORE_V20) {
			LOGE("Failed to make key\n");
			ret = EM_ERR_EM_ESI_RECOVERY_MAKE_KEY;
			goto out;
		}

		ret = em_esi_update_item(ctx->em_version, ctx->esi, EM_TYPE_ESI_ITEM_SHARED_KEY, core->key, core->key,
					 EM_LEN_KEY_CORE_V20);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to update item(0x%08x)\n", ret);
			goto out;
		}

		ret = em_esi_update_item(ctx->em_version, ctx->esi, EM_TYPE_ESI_ITEM_SHARED_ESI_COUNTER,
					 (uint8_t *)&shared_esi_counter, core->key, EM_LEN_KEY_CORE_V20);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to add shared esi counter to esi(0x%08x)\n", ret);
			goto out;
		}

		memcpy(init_state_v20, EM_MAGIC_ESI_V20_SHARING, strlen(EM_MAGIC_ESI_V20_SHARING));
	} else {
		uint32_t recovery_cnt_set_type;

		if (ctx->flags[0] & EM_FLAGS_0_EXIST_BOOTLOADER)
			recovery_cnt_set_type = EM_TYPE_ESI_ITEM_RECOVERY_COUNTER_BL;
		else
			recovery_cnt_set_type = EM_TYPE_ESI_ITEM_RECOVERY_COUNTER;

		memcpy(init_state_v20, EM_MAGIC_ESI_V20_COMPLETED, strlen(EM_MAGIC_ESI_V20_COMPLETED));

		recovery_counter = core->recovery_ctr;
		core->recovery_ctr += 1;

		ctx->flags[1] |= EM_FLAGS_1_WRITE_CORE;
		ret = em_esi_update_item(ctx->em_version, ctx->esi, (const uint32_t)recovery_cnt_set_type,
					 (uint8_t *)&recovery_counter, core->key, EM_LEN_KEY_CORE_V20);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to update recovery counter(0x%08x)\n", ret);
			goto out;
		}
	}

	ret = em_esi_update_item(ctx->em_version, ctx->esi, EM_TYPE_ESI_ITEM_INIT_STATE, init_state_v20, core->key,
				 EM_LEN_KEY_CORE_V20);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to add init_state(%08x)\n", ret);
		goto out;
	}

	ret = em_esi_update_item(ctx->em_version, ctx->esi, EM_TYPE_ESI_ITEM_DID, ctx->did, core->key,
				 EM_LEN_KEY_CORE_V20);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to add DID(%08x)\n", ret);
		goto out;
	}

	ctx->flags[1] |=
	    EM_FLAGS_1_EXIST_RETURN_ESI | EM_FLAGS_1_EXIST_RETURN_TOKEN_REMOVE | EM_FLAGS_1_LTS_REMOVE_TOKEN;

	ret = EM_SUCCESS;
out:
	core = NULL;

	if (digest)
		em_free(digest);

	return ret;
}
