#include "em_common.h"

static em_ess_ctx * ess_ctx;

void em_ess_update_req_msg(uint8_t *msg, uint32_t *msg_index, const uint8_t *input, const uint32_t input_size,
			   const uint8_t *delem, const uint32_t max_size, const uint32_t input_max_size)
{
	int ret = EM_SUCCESS;

	EM_CHECK_NULL(__func__, 0, msg, msg_index, input);

	if (input_size > input_max_size) {
		LOGE("input buffer isn't normal(%u/%u)\n", input_size, input_max_size);
		ret = EM_ERR_EM_ESS_UPDATE_INVALID_INPUT;
		goto out;
	}

	if (*msg_index + input_size + strlen(EM_MAGIC_ESS_TOKENIZER) > max_size) {
		LOGE("%s/%s\n", msg, input);
		ret = EM_ERR_EM_ESS_UPDATE_INVALID_INPUT;
		goto out;
	}

	memcpy(msg + *msg_index, input, input_size);
	*msg_index += input_size;

	if (delem != NULL) {
		memcpy(msg + *msg_index, delem, 1);
		*msg_index += strlen((const char *)delem);
	}

out:
	if (ret != EM_SUCCESS)
		LOGE("Failed to update msg (%08x)\n", ret);

	return;
}

static int em_ess_init_ctx(const char *protocol_version)
{
	if (!ess_ctx) {
		ess_ctx = em_calloc(sizeof(em_ess_ctx), 1);
		if (ess_ctx == NULL)
			return EM_ERR_EM_ESS_INIT_CTX;
	}

	memset(ess_ctx, 0, sizeof(em_ess_ctx));
	memcpy(ess_ctx->protocol_version, protocol_version, EM_LEN_ESS_VERSION);

	return EM_SUCCESS;
}

static int em_ess_encrypt_message(uint8_t *out, uint32_t *len_out, em_keeping_item *keep, const uint8_t *certificate,
				  const uint32_t len_certificate)
{
	int ret;

	uint8_t iv_tmp[EM_LEN_SESSION_IV] = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_ENCRYPT_MESSAGE, out, len_out, keep, certificate);

	ret = em_get_random(ess_ctx->ss_data.key, EM_LEN_SESSION_KEY);
	if (ret != EM_LEN_SESSION_KEY) {
		LOGE("Failed to make session key(0x%08x)\n", ret);
		ret = EM_ERR_EM_ENCRYPT_MESSAGE_SESSION_KEY;
		goto out;
	}
	memcpy(keep->session_key, ess_ctx->ss_data.key, EM_LEN_SESSION_KEY);

	ret = em_get_random(ess_ctx->ss_data.iv, EM_LEN_SESSION_IV);
	if (ret != EM_LEN_SESSION_IV) {
		LOGE("Failed to make session iv(0x%08x)\n", ret);
		ret = EM_ERR_EM_ENCRYPT_MESSAGE_SESSION_IV;
		goto out;
	}
	memcpy(keep->session_iv, ess_ctx->ss_data.iv, EM_LEN_SESSION_IV);

	memcpy(iv_tmp, ess_ctx->ss_data.iv, EM_LEN_SESSION_IV);
	ret = em_crypto_aes_256_ctr_encrypt(ess_ctx->req_msg.data, ess_ctx->req_msg.len, ess_ctx->ss_data.key, iv_tmp,
					    ess_ctx->erm.data, &(ess_ctx->erm.len));
	if (ret != EM_SUCCESS) {
		LOGE("Failed to encrypt req msg(0x%08x)\n", ret);
		goto out;
	}

	ret = em_get_random(ess_ctx->unenc_esk.wb_iv, EM_LEN_IV);
	if (ret != EM_LEN_IV) {
		LOGE("Failed to get wb iv(0x%08x)\n", ret);
		ret = EM_ERR_EM_ENCRYPT_MESSAGE_SESSION_WB_IV;
		goto out;
	}

	ret = em_wb_aes_ctr_encrypt((uint8_t *)&(ess_ctx->ss_data), sizeof(ess_ctx->ss_data),
				    ess_ctx->unenc_esk.wb_enc_ss_data,
				    (unsigned int *)&(ess_ctx->unenc_esk.wb_enc_ss_data_len), ess_ctx->unenc_esk.wb_iv, EMWB_TAG_ETP);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to encrypt(wb) SS data (0x%08x)\n", ret);
		ret = EM_ERR_EM_ENCRYPT_MESSAGE_SESSION_WB_ENCRYPT;
		goto out;
	}

	ret = em_crypto_rsa_encrypt(certificate, len_certificate, (uint8_t *)&(ess_ctx->unenc_esk),
				    EM_LEN_IV + (ess_ctx->unenc_esk.wb_enc_ss_data_len), ess_ctx->esk.data,
				    &(ess_ctx->esk.len));
	if (ret != EM_SUCCESS) {
		LOGE("Failed to rsa encrypt(0x%08x)\n", ret);
		goto out;
	}

	if (ess_ctx->esk.len > EM_LEN_ESK) {
		LOGE("esk length isn't normal(%d)\n", ess_ctx->esk.len);
		ret = EM_ERR_EM_ENCRYPT_MESSAGE_ESK_LENGTH;
		goto out;
	}
	memcpy(ess_ctx->hash.ori, ess_ctx->esk.data, ess_ctx->esk.len);
	ess_ctx->hash.ori_len += ess_ctx->esk.len;

	if (ess_ctx->erm.len > EM_LEN_ERM) {
		LOGE("erm length isn't normal(%d)\n", ess_ctx->erm.len);
		ret = EM_ERR_EM_ENCRYPT_MESSAGE_ERM_LENGTH;
		goto out;
	}
	memcpy(ess_ctx->hash.ori + ess_ctx->hash.ori_len, ess_ctx->erm.data, ess_ctx->erm.len);
	ess_ctx->hash.ori_len += ess_ctx->erm.len;

	ess_ctx->hash.len = EM_LEN_SHA256;
	ret = em_crypto_sha256(ess_ctx->hash.ori, ess_ctx->hash.ori_len, ess_ctx->hash.data);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to make digest of esk_erm (0x%08x)\n", ret);
		goto out;
	}

	em_base64_encode((char *)ess_ctx->hash.data, (char *)ess_ctx->hash.data_b64, ess_ctx->hash.len);
	em_base64_encode((char *)ess_ctx->esk.data, (char *)ess_ctx->esk.data_b64, ess_ctx->esk.len);
	em_base64_encode((char *)ess_ctx->erm.data, (char *)ess_ctx->erm.data_b64, ess_ctx->erm.len);

	em_snprintf((char *)ess_ctx->esk.str_len, em_get_digit_count(ess_ctx->esk.len) + 1, "%d", ess_ctx->esk.len);
	em_snprintf((char *)ess_ctx->erm.str_len, em_get_digit_count(ess_ctx->erm.len) + 1, "%d", ess_ctx->erm.len);
	em_snprintf((char *)ess_ctx->esk.str_b64_len,
		    em_get_digit_count(strlen((const char *)ess_ctx->esk.data_b64)) + 1, "%d",
		    (int)strlen((const char *)ess_ctx->esk.data_b64));
	em_snprintf((char *)ess_ctx->erm.str_b64_len,
		    em_get_digit_count(strlen((const char *)ess_ctx->erm.data_b64)) + 1, "%d",
		    (int)strlen((const char *)ess_ctx->erm.data_b64));

	em_ess_update_req_msg(out, len_out, ess_ctx->protocol_version, EM_LEN_ESS_VERSION,
			      (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE, EM_LEN_ESS_VERSION);
	em_ess_update_req_msg(out, len_out, ess_ctx->esk.str_len, strlen((const char *)ess_ctx->esk.str_len),
			      (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE, EM_LEN_ESS_STR);
	em_ess_update_req_msg(out, len_out, ess_ctx->esk.str_b64_len, strlen((const char *)ess_ctx->esk.str_b64_len),
			      (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE, EM_LEN_ESS_STR);
	em_ess_update_req_msg(out, len_out, ess_ctx->esk.data_b64, strlen((const char *)ess_ctx->esk.data_b64),
			      (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE, EM_LEN_ESK_B64);
	em_ess_update_req_msg(out, len_out, ess_ctx->erm.str_len, strlen((const char *)ess_ctx->erm.str_len),
			      (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE, EM_LEN_ESS_STR);
	em_ess_update_req_msg(out, len_out, ess_ctx->erm.str_b64_len, strlen((const char *)ess_ctx->erm.str_b64_len),
			      (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE, EM_LEN_ESS_STR);
	em_ess_update_req_msg(out, len_out, ess_ctx->erm.data_b64, strlen((const char *)ess_ctx->erm.data_b64),
			      (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE, EM_LEN_MESSAGE);
	em_ess_update_req_msg(out, len_out, ess_ctx->hash.data_b64, strlen((const char *)ess_ctx->hash.data_b64),
			      (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE, EM_LEN_MESSAGE);

	ret = EM_SUCCESS;
out:
	memset(iv_tmp, 0, EM_LEN_SESSION_IV);

	if (ess_ctx) {
		memset(ess_ctx, 0, sizeof(em_ess_ctx));
		em_free(ess_ctx);
		ess_ctx = NULL;
	}

	return ret;
}

static int em_ess_make_token_message(em_context *ctx)
{
	int ret;
	uint32_t len_message_local_var;	//for exynos bootloader
	uint8_t tss[EM_LEN_TSS] = {0,};
	uint8_t activated[EM_LEN_ACTIVATED] = {0,};
	char unique_number[EM_LEN_UNIQUE] = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESS_MAKE_TOKEN_MESSAGE, ctx);

	ret = em_ess_init_ctx(EM_MAGIC_ESS_PROTOCOL_V1);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to init context(0x%08x)\n", ret);
		goto out;
	}

	ret = em_get_random(ctx->keep.nonce, EM_LEN_NONCE);
	if (ret != EM_LEN_NONCE) {
		LOGE("Failed to make nonce(0x%08x)\n", ret);
		ret = EM_ERR_EM_ESS_MAKE_TOKEN_MESSAGE_NONCE;
		goto out;
	}

	ret = em_get_random(ctx->keep.iin, EM_LEN_IIN);
	if (ret != EM_LEN_IIN) {
		LOGE("Failed to make iin(0x%08x)\n", ret);
		ret = EM_ERR_EM_ESS_MAKE_TOKEN_MESSAGE_IIN;
		goto out;
	}

	memcpy(ctx->keep.did, ctx->did, EM_LEN_DID);

	em_base64_encode((char *)ctx->keep.iin, (char *)ess_ctx->iin_b64, EM_LEN_IIN);
	em_base64_encode((char *)ctx->keep.nonce, (char *)ess_ctx->nonce_b64, EM_LEN_NONCE);

	em_snprintf((char *)tss, em_get_digit_count(ctx->tss) + 1, "%d", ctx->tss);
	em_snprintf((char *)activated, em_get_digit_count(ctx->activated) + 1, "%d", ctx->activated);

	len_message_local_var = ess_ctx->req_msg.len;
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, ctx->ess_prefixbody,
			      strlen((const char *)ctx->ess_prefixbody), (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE,
			      EM_LEN_ESS_PREFIX_BODY);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, ctx->keep.did, EM_LEN_DID,
			      (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE, EM_LEN_DID);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, ctx->keep.imei, EM_LEN_IMEI,
			      (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE, EM_LEN_IMEI);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, ctx->keep.model_name,
			      strlen((const char *)ctx->keep.model_name), (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE,
			      EM_LEN_MODEL_NAME);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, ess_ctx->nonce_b64,
			      strlen((const char *)ess_ctx->nonce_b64), (uint8_t *)EM_MAGIC_ESS_TOKENIZER,
			      EM_LEN_MESSAGE, EM_LEN_NONCE_B64);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, ess_ctx->iin_b64,
			      strlen((const char *)ess_ctx->iin_b64), (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE,
			      EM_LEN_IIN_B64);

	if (ctx->flags[0] & EM_FLAGS_0_EXIST_BOOTLOADER) {
		em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var,
				      (uint8_t *)EM_MAGIC_ESS_BL_SIDE, strlen(EM_MAGIC_ESS_BL_SIDE),
				      (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE, EM_LEN_ESS_AP_SIDE_OR_BP_SIDE);
	} else {
		em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var,
				      (uint8_t *)EM_MAGIC_ESS_AP_SIDE, strlen(EM_MAGIC_ESS_AP_SIDE),
				      (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE, EM_LEN_ESS_AP_SIDE_OR_BP_SIDE);
	}

	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, (uint8_t *)EM_MAGIC_ESS_OS_TYPE,
			      EM_LEN_ESS_OS_TYPE, (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE,
			      EM_LEN_ESS_OS_TYPE);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, ctx->osversion,
			      EM_LEN_OSVERSION, (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE,
			      EM_LEN_OSVERSION);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, ctx->swversion,
			      strlen((const char *)ctx->swversion), (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE,
			      EM_LEN_SWVERSION);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, ctx->blversion,
			      strlen((const char *)ctx->blversion), (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE,
			      EM_LEN_BLVERSION);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, ctx->fuse,
			      strlen((const char *)ctx->fuse), (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE,
			      EM_LEN_FUSE);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, ctx->smsn,
			      strlen((const char *)ctx->smsn), (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE,
			      EM_LEN_SMSN);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, ctx->openday,
			      EM_LEN_OPENDAY, (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE,
			      EM_LEN_OPENDAY);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, tss,
			      strlen((const char *)tss), (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE,
			      EM_LEN_TSS);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, activated,
			      strlen((const char *)activated), (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE,
			      EM_LEN_ACTIVATED);
	remove_spaces((const char *)ctx->serial, unique_number);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, (uint8_t *)unique_number,
			      strlen((const char *)unique_number), (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE,
			      EM_LEN_UNIQUE);

	ess_ctx->req_msg.len = len_message_local_var;
	ret = EM_SUCCESS;
out:
	return ret;
}

static int em_ess_make_delete_message(uint64_t *flags, em_keeping_item *keep, const uint8_t *did, const uint8_t *iin,
				      const uint8_t *token_id, const uint8_t *prefixbody,
				      const uint8_t is_rpmb_provision)
{
	int ret;
	uint32_t len_message_local_var;	//for exynos bootloader

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESS_MAKE_DELETE_MESSAGE, flags, keep, did, iin, token_id, prefixbody);

	memcpy(keep->did, did, EM_LEN_DID);
	memcpy(keep->iin, iin, EM_LEN_IIN);

	em_base64_encode((char *)keep->iin, (char *)ess_ctx->iin_b64, EM_LEN_IIN);

	len_message_local_var = ess_ctx->req_msg.len;
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, prefixbody,
			      strlen((const char *)prefixbody), (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE,
			      EM_LEN_ESS_PREFIX_BODY);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, token_id, EM_LEN_TOKEN_ID,
			      (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE, EM_LEN_TOKEN_ID);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, keep->did, EM_LEN_DID,
			      (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE, EM_LEN_DID);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, keep->imei, EM_LEN_IMEI,
			      (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE, EM_LEN_IMEI);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, keep->model_name,
			      strlen((const char *)keep->model_name), (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE,
			      EM_LEN_MODEL_NAME);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, ess_ctx->iin_b64,
			      strlen((const char *)ess_ctx->iin_b64), (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE,
			      EM_LEN_IIN_B64);

	ess_ctx->req_msg.len = len_message_local_var;
	ret = EM_SUCCESS;
out:

	return ret;
}

static int em_ess_make_recovery_message(uint64_t *flags, em_keeping_item *keep, const uint8_t *did,
					const uint8_t *prefixbody)
{
	int ret;
	uint32_t len_message_local_var;	//for exynos bootloader

	EM_CHECK_NULL(__func__, EM_ERR_EM_ESS_MAKE_RECOVERY_MESSAGE, flags, keep, did, prefixbody);

	ret = em_ess_init_ctx(EM_MAGIC_ESS_PROTOCOL_V1);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to init context(0x%08x)\n", ret);
		goto out;
	}

	ret = em_get_random(keep->nonce, EM_LEN_NONCE);
	if (ret != EM_LEN_NONCE) {
		LOGE("Failed to make nonce(0x%08x)\n", ret);
		ret = EM_ERR_EM_ESS_MAKE_RECOVERY_MESSAGE_NONCE;
		goto out;
	}

	em_base64_encode((char *)keep->nonce, (char *)ess_ctx->nonce_b64, EM_LEN_NONCE);

	len_message_local_var = ess_ctx->req_msg.len;
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, prefixbody,
			      strlen((const char *)prefixbody), (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE,
			      EM_LEN_ESS_PREFIX_BODY);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, did, EM_LEN_DID,
			      (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE, EM_LEN_DID);
	em_ess_update_req_msg(ess_ctx->req_msg.data, (uint32_t *)&len_message_local_var, ess_ctx->nonce_b64,
			      strlen((const char *)ess_ctx->nonce_b64), (uint8_t *)EM_MAGIC_ESS_TOKENIZER,
			      EM_LEN_MESSAGE, EM_LEN_NONCE_B64);

	ess_ctx->req_msg.len = len_message_local_var;
	ret = EM_SUCCESS;
out:
	return ret;
}

int em_ess_make_token_request(em_context *ctx)
{
	int ret;

	uint8_t *response_message = NULL;
	uint32_t len_response_message = 0;

	uint32_t len_message_local_var;	//for exynos bootloader

	response_message = (uint8_t *)em_calloc(EM_LEN_MESSAGE, 1);
	EM_CHECK_NULL(__func__, EM_ERR_EM_ESS_MAKE_TOKEN_REQUEST, ctx, response_message);

	if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_CERT)) {
		LOGE("Certificate doesn't exist\n");
		ret = EM_ERR_EM_ESS_MAKE_TOKEN_REQUEST_CERTIFICATE;
		goto out;
	}

	if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_ESS_PREFIXBODY)) {
		LOGE("ess prefixbody doesn't exist\n");
		ret = EM_ERR_EM_ESS_MAKE_TOKEN_REQUEST_PREFIXBODY;
		goto out;
	}

	if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_IMEI)) {
		LOGE("imei doesn't exist\n");
		ret = EM_ERR_EM_ESS_MAKE_TOKEN_REQUEST_IMEI;
		goto out;
	} else {
		memcpy(ctx->keep.imei, ctx->imei, EM_LEN_IMEI);
	}

	if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_MODEL_NAME)) {
		LOGE("model name doesn't exist\n");
		ret = EM_ERR_EM_ESS_MAKE_TOKEN_REQUEST_MODEL_NAME;
		goto out;
	} else {
		memcpy(ctx->keep.model_name, ctx->model_name, strlen((const char *)ctx->model_name));
	}

	ret = em_ess_make_token_message(ctx);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to make token message(0x%08x)\n", ret);
		goto out;
	}

	ret = em_ess_encrypt_message(response_message, &len_response_message, &ctx->keep, ctx->cert, ctx->len_cert);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to encrypt message(0x%08x)\n", ret);
		goto out;
	}

	ctx->len_message = 0;
	len_message_local_var = ctx->len_message;
	em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_PREFIX_RETURN_COMMAND,
			      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND), NULL, EM_LEN_MESSAGE,
			      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND));
	em_ess_update_req_msg(ctx->message, &len_message_local_var, ctx->ess_command_type, 1,
			      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM, EM_LEN_MESSAGE, 1);
	em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_OK,
			      strlen(EM_MAGIC_ESS_AT_COMMAND_OK), (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM,
			      EM_LEN_MESSAGE, strlen(EM_MAGIC_ESS_AT_COMMAND_OK));

	if (len_message_local_var + len_response_message > EM_LEN_MESSAGE) {
		LOGE("message buf len isn't normal(%u)\n", len_message_local_var + len_response_message);
		ctx->len_message = len_message_local_var;
		ret = EM_ERR_EM_ESS_MAKE_TOKEN_REQUEST_BUF_LEN;
		goto out;
	}

	em_ess_update_req_msg(ctx->message, &len_message_local_var, response_message,
			      strlen((const char *)response_message), NULL, EM_LEN_MESSAGE, EM_LEN_MESSAGE);
	ctx->message[len_message_local_var] = 0;
	ctx->len_message = len_message_local_var;

	ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_MESSAGE;
	ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_KEEPING_ITEM;

	ret = EM_SUCCESS;
out:
	if (response_message)
		em_free(response_message);

	return ret;
}

int em_ess_make_delete_request(em_context *ctx)
{
	int ret;

	uint8_t iin[EM_LEN_IIN] = {0,};
	uint8_t *response_message = NULL;
	uint32_t len_response_message = 0;

	em_core_v20 *core = NULL;

	uint32_t len_message_local_var;	//for exynos bootloader

	response_message = (uint8_t *)em_calloc(EM_LEN_MESSAGE, 1);
	EM_CHECK_NULL(__func__, EM_ERR_EM_ESS_MAKE_DELETE_REQUEST, ctx, response_message);
	core = (em_core_v20 *)&ctx->core_v20;
	(void)core;

	if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_CERT)) {
		LOGE("certificate doesn't exist(0x%016"PRIx64")\n", ctx->flags[0]);
		ret = EM_ERR_EM_ESS_MAKE_DELETE_REQUEST_CERT;
		goto out;
	}

	if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_ESS_PREFIXBODY)) {
		LOGE("prefixbody doesn't exist(0x%016"PRIx64")\n", ctx->flags[0]);
		ret = EM_ERR_EM_ESS_MAKE_DELETE_REQUEST_PREFIXBODY;
		goto out;
	}

	if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_IMEI)) {
		LOGE("imei doesn't exist\n");
		ret = EM_ERR_EM_ESS_MAKE_DELETE_REQUEST_IMEI;
		goto out;
	} else {
		memcpy(ctx->keep.imei, ctx->imei, EM_LEN_IMEI);
	}

	if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_MODEL_NAME)) {
		LOGE("model name doesn't exist\n");
		ret = EM_ERR_EM_ESS_MAKE_DELETE_REQUEST_MODEL_NAME;
		goto out;
	} else {
		memcpy(ctx->keep.model_name, ctx->model_name, strlen((const char *)ctx->model_name));
	}

	ret = em_ess_init_ctx(EM_MAGIC_ESS_PROTOCOL_V1);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to init context(0x%08x)\n", ret);
		goto out;
	}

#ifndef EMLITE
	if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_ESI)) {
		LOGE("esi doesn't exist(0x%016"PRIx64")\n", ctx->flags[0]);
		ret = EM_ERR_EM_ESS_MAKE_DELETE_REQUEST_ESI;
		goto out;
	}

	ret = em_esi_check_meta(ctx->esi);
	if (ret != EM_SUCCESS) {
		LOGE("Invalid ESI meta\n");
		goto out;
	}

	if (memcmp(core->magic, EM_MAGIC_EM_CORE, strlen(EM_MAGIC_EM_CORE)) == 0) {
		memcpy(iin, core->iin, EM_LEN_IIN);
	} else {
		ret = em_esi_get_item(ctx->esi, EM_TYPE_ESI_ITEM_IIN, iin, EM_LEN_IIN);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to get iin from esi(0x%08x)\n", ret);
			goto out;
		}
	}

	ret = em_esi_get_item(ctx->esi, EM_TYPE_ESI_ITEM_TOKEN_ID, ctx->token_id, EM_LEN_TOKEN_ID);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get token id from esi(0x%08x)\n", ret);
		goto out;
	}
#else
	if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_IIN)) {
		LOGE("Please check iin in payload\n");
		ret = EM_ERR_EM_ESS_MAKE_DELETE_MESSAGE_IIN;
		goto out;
	}
	memcpy(iin, ctx->iin, EM_LEN_IIN);

	ret = em_token_get_token_id(ctx);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get token id(0x%08x)\n", ret);
		goto out;
	}
#endif

	ret = em_ess_make_delete_message(ctx->flags, &ctx->keep, ctx->did, iin, ctx->token_id, ctx->ess_prefixbody,
					 ctx->is_provision);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to make delete message(0x%08x)\n", ret);
		goto out;
	}

	ret = em_ess_encrypt_message(response_message, &len_response_message, &ctx->keep, ctx->cert, ctx->len_cert);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to encrypt message(0x%08x)\n", ret);
		goto out;
	}

	if (len_response_message > EM_LEN_MESSAGE) {
		LOGE("message buf len isn't normal(%u)\n", len_response_message);
		ret = EM_ERR_EM_ESS_MAKE_DELETE_REQUEST_BUF_LEN;
		goto out;
	}

	ctx->len_message = 0;
	len_message_local_var = ctx->len_message;
	em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_PREFIX_RETURN_COMMAND,
			      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND), NULL, EM_LEN_MESSAGE,
			      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND));
	em_ess_update_req_msg(ctx->message, &len_message_local_var, ctx->ess_command_type, 1,
			      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM, EM_LEN_MESSAGE, 1);
	em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_OK,
			      strlen(EM_MAGIC_ESS_AT_COMMAND_OK), (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM,
			      EM_LEN_MESSAGE, strlen(EM_MAGIC_ESS_AT_COMMAND_OK));

	if (len_message_local_var + len_response_message > EM_LEN_MESSAGE) {
		LOGE("message buf len isn't normal(%u)\n", len_message_local_var + len_response_message);
		ctx->len_message = len_message_local_var;
		ret = EM_ERR_EM_ESS_MAKE_DELETE_REQUEST_BUF_LEN;
		goto out;
	}
	em_ess_update_req_msg(ctx->message, &len_message_local_var, response_message,
			      strlen((const char *)response_message), NULL, EM_LEN_MESSAGE, EM_LEN_MESSAGE);
	ctx->message[len_message_local_var] = 0;
	ctx->len_message = len_message_local_var;

#ifndef EMLITE
	ret = em_esi_remove_item(ctx->em_version, ctx->esi, EM_TYPE_ESI_ITEM_TOKEN_ID, ctx->key, EM_LEN_KEY_CORE_V20);
	if (ret != EM_SUCCESS) {
		LOGE("%s : Failed to remove item(0x%08x)\n", __func__, ret);
		goto out;
	}
#endif

	ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_ESI;
	ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_MESSAGE;

	ret = EM_SUCCESS;
out:
	memset(iin, 0, EM_LEN_IIN);
	core = NULL;

	if (response_message)
		em_free(response_message);

	return ret;
}

int em_ess_make_recovery_request(em_context *ctx)
{
	int ret;
	uint8_t *response_message = NULL;
	uint32_t len_response_message = 0;

	uint32_t len_message_local_var;	//for exynos bootloader

	response_message = (uint8_t *)em_calloc(EM_LEN_MESSAGE, 1);
	EM_CHECK_NULL(__func__, EM_ERR_EM_ESS_MAKE_RECOVERY_REQUEST, ctx, response_message);

	if (em_is_all_zero(ctx->did, EM_LEN_DID) == EM_SUCCESS || !(ctx->flags[0] & EM_FLAGS_0_EXIST_DID)) {
		LOGE("Please check did & flags\n");
		ret = EM_ERR_EM_ESS_MAKE_RECOVERY_REQUEST_DID;
		goto out;
	}

	ret = em_ess_make_recovery_message(ctx->flags, &ctx->keep, ctx->did, ctx->ess_prefixbody);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to recovery message(0x%08x)\n", ret);
		goto out;
	}

	ret = em_ess_encrypt_message(response_message, &len_response_message, &ctx->keep, ctx->cert, ctx->len_cert);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to encrypt message(0x%08x)\n", ret);
		goto out;
	}

	ctx->len_message = 0;
	len_message_local_var = ctx->len_message;
	em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_PREFIX_RETURN_COMMAND,
			      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND), NULL, EM_LEN_MESSAGE,
			      strlen(EM_MAGIC_ESS_PREFIX_COMMAND));
	em_ess_update_req_msg(ctx->message, &len_message_local_var, ctx->ess_command_type, 1,
			      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM, EM_LEN_MESSAGE, 1);
	em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_OK,
			      strlen(EM_MAGIC_ESS_AT_COMMAND_OK), (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM,
			      EM_LEN_MESSAGE, strlen(EM_MAGIC_ESS_AT_COMMAND_OK));

	if (len_message_local_var + len_response_message > EM_LEN_MESSAGE) {
		LOGE("message buf len isn't normal(%u)\n", len_message_local_var + len_response_message);
		ctx->len_message = len_message_local_var;
		ret = EM_ERR_EM_ESS_MAKE_RECOVERY_REQUEST_BUF_LEN;
		goto out;
	}

	em_ess_update_req_msg(ctx->message, &len_message_local_var, response_message,
			      strlen((const char *)response_message), NULL, EM_LEN_MESSAGE, EM_LEN_MESSAGE);
	ctx->message[len_message_local_var] = 0;
	ctx->len_message = len_message_local_var;

	ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_MESSAGE;
	ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_KEEPING_ITEM;

	ret = EM_SUCCESS;
out:
	if (response_message)
		em_free(response_message);

	return ret;
}

int em_ess_install_token_v1(em_context *ctx)
{
	int ret, len_token = 0;
	uint8_t is_all_zero = 0;

	em_core_v20 *core = NULL;
	uint32_t len_message_local_var;	//for exynos bootloader
	(void)core;
	(void)is_all_zero;

	ctx->parsed_token = (em_parsed_token *)em_calloc(sizeof(em_parsed_token), 1);
	EM_CHECK_NULL(__func__, EM_ERR_EM_ESS_INSTALL_TOKEN_V1, ctx, ctx->parsed_token);

	if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_KEEPING_ITEM)) {
		LOGE("keeping item doesn't exist\n");
		ret = EM_ERR_EM_ESS_INSTALL_TOKEN_V1_KEEP;
		goto out;
	}

	if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_RXIV_B64)) {
		LOGE("rx iv doesn't exist\n");
		ret = EM_ERR_EM_ESS_INSTALL_TOKEN_V1_RXIV_B64;
		goto out;
	}

	if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_ETOKEN_B64)) {
		LOGE("etoken b64 doesn't exist\n");
		ret = EM_ERR_EM_ESS_INSTALL_TOKEN_V1_ETOKEN_B64;
		goto out;
	}

	ret = em_ess_init_ctx(EM_MAGIC_ESS_PROTOCOL_V1);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to init context(0x%08x)\n", ret);
		goto out;
	}

	em_base64_decode((const char *)ctx->ess_etoken_b64, (char *)ess_ctx->etoken.data, &(ess_ctx->etoken.len));
	em_base64_decode((const char *)ctx->ess_iv_b64, (char *)ess_ctx->rx_ss_iv.data, &(ess_ctx->rx_ss_iv.len));

	ret = em_crypto_aes_256_ctr_decrypt(ess_ctx->etoken.data, ess_ctx->etoken.len, ctx->keep.session_key,
					    ess_ctx->rx_ss_iv.data, ctx->token, (int *)&len_token);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to decrypt e-token (0x%08x)\n", ret);
		goto out;
	}

	ret = em_token_verify_token(ctx, ctx->parsed_token, &ctx->token_ptr);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to verify token from ess(0x%08x)\n", ret);
		goto out;
	}

	ret = em_token_install(ctx);
	if (ret != EM_SUCCESS) {
		LOGE("token install is failed(0x%08x)\n", ret);
		goto out;
	}

#ifndef EMLITE
	core = (em_core_v20 *)&ctx->core_v20;
	if (ctx->is_provision) {
		if (em_is_all_zero((uint8_t *)core, sizeof(em_core_v20)) == EM_SUCCESS) {
			LOGI("core is all zero\n");
			is_all_zero = 1;
		}

		if (is_all_zero == 0) {
			if (memcmp(core->magic, EM_MAGIC_EM_CORE, strlen(EM_MAGIC_EM_CORE)) != 0) {
				LOGE("core isn't normal\n");
				ret = EM_ERR_EM_ESS_INSTALL_TOKEN_V1_CORE;
				goto out;
			}

			memcpy(core->iin, ctx->keep.iin, EM_LEN_IIN);
			ctx->flags[1] |= EM_FLAGS_1_WRITE_CORE;
		}
	}

	if (is_all_zero == 1 || !(ctx->is_provision)) {
		ret = em_esi_update_item(ctx->em_version, ctx->esi, EM_TYPE_ESI_ITEM_IIN, ctx->keep.iin, ctx->key,
					 EM_LEN_KEY_CORE_V20);
		if (ret != EM_SUCCESS) {
			LOGE("Failed to update iin to esi(0x%08x)\n", ret);
			goto out;
		}
	}
#endif

	ctx->len_message = 0;
	len_message_local_var = ctx->len_message;
	em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_PREFIX_RETURN_COMMAND,
			      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND), NULL, EM_LEN_MESSAGE,
			      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND));
	em_ess_update_req_msg(ctx->message, &len_message_local_var, ctx->ess_command_type, 1,
			      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM, EM_LEN_MESSAGE, 1);
	em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_OK,
			      strlen(EM_MAGIC_ESS_AT_COMMAND_OK), (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM,
			      EM_LEN_MESSAGE, strlen(EM_MAGIC_ESS_AT_COMMAND_OK));

	if (len_message_local_var + EM_LEN_DID + EM_LEN_TOKEN_ID > EM_LEN_MESSAGE) {
		LOGE("message buf len isn't normal(%u)\n", len_message_local_var + EM_LEN_DID + EM_LEN_TOKEN_ID);
		ctx->len_message = len_message_local_var;
		ret = EM_ERR_EM_ESS_INSTALL_TOKEN_V1_BUF_LEN;
		goto out;
	}

	em_ess_update_req_msg(ctx->message, &len_message_local_var, ctx->token_id, EM_LEN_TOKEN_ID,
			      (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE, EM_LEN_TOKEN_ID);
	em_ess_update_req_msg(ctx->message, &len_message_local_var, ctx->did, EM_LEN_DID,
			      (uint8_t *)EM_MAGIC_ESS_TOKENIZER, EM_LEN_MESSAGE, EM_LEN_DID);
	ctx->message[len_message_local_var] = 0;
	ctx->len_message = len_message_local_var;

	ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_TOKEN | EM_FLAGS_1_EXIST_RETURN_ESI | EM_FLAGS_1_EXIST_RETURN_MESSAGE |
					 EM_FLAGS_1_LTS_INSTALL_TOKEN;

	ret = EM_SUCCESS;
out:
	core = NULL;
	memset(ctx->keep.iin, 0, EM_LEN_IIN);

	if (ess_ctx) {
		em_free(ess_ctx);
		ess_ctx = NULL;
	}

	return ret;
}

int em_ess_recovery_esi_v1(em_context *ctx)
{
#ifndef EMLITE
	int ret;
	uint8_t *decrypted_recovery_data = NULL;
	int len_decrypted_recovery_data = 0, len_decrypted_aes_recovery_data = 0;

	uint32_t len_message_local_var; //for exynos bootloader

	decrypted_recovery_data = (uint8_t *)em_calloc(EM_LEN_RECOVERY_DATA, 1);
	EM_CHECK_NULL(__func__, EM_ERR_EM_ESS_RECOVERY_ESI_V1, ctx, decrypted_recovery_data);

	if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_ERSD_B64)) {
		LOGE("encrypted rds b64 doesn't exist\n");
		ret = EM_ERR_EM_ESS_RECOVERY_ESI_V1_ERSD;
		goto out;
	}

	if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_RXIV_B64)) {
		LOGE("rx iv b64 doesn't exist\n");
		ret = EM_ERR_EM_ESS_RECOVERY_ESI_V1_RXIV;
		goto out;
	}

	if (!(ctx->flags[0] & EM_FLAGS_0_EXIST_KEEPING_ITEM)) {
		LOGE("keeping item doesn't exist\n");
		ret = EM_ERR_EM_ESS_RECOVERY_ESI_V1_KEEPING_ITEM;
		goto out;
	}

	ret = em_ess_init_ctx(EM_MAGIC_ESS_PROTOCOL_V1);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to init context(0x%08x)\n", ret);
		goto out;
	}

	em_base64_decode((const char *)ctx->recovery_data, (char *)decrypted_recovery_data,
			 &len_decrypted_recovery_data);
	em_base64_decode((const char *)ctx->ess_iv_b64, (char *)ess_ctx->rx_ss_iv.data, &(ess_ctx->rx_ss_iv.len));

	ret = em_crypto_aes_256_ctr_decrypt((unsigned char *)decrypted_recovery_data, len_decrypted_recovery_data,
					    ctx->keep.session_key, ess_ctx->rx_ss_iv.data, ctx->recovery_data,
					    &len_decrypted_aes_recovery_data);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to decrypt e-token (0x%08x)\n", ret);
		goto out;
	}

	ret = em_esi_recovery(ctx);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to recovery esi(0x%08x)\n", ret);
		goto out;
	}

	ctx->len_message = 0;
	len_message_local_var = ctx->len_message;
	em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_PREFIX_RETURN_COMMAND,
			      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND), NULL, EM_LEN_MESSAGE,
			      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND));
	em_ess_update_req_msg(ctx->message, &len_message_local_var, ctx->ess_command_type, 1,
			      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM, EM_LEN_MESSAGE, 1);
	em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_OK,
			      strlen(EM_MAGIC_ESS_AT_COMMAND_OK), NULL, EM_LEN_MESSAGE,
			      strlen(EM_MAGIC_ESS_AT_COMMAND_OK));
	ctx->message[len_message_local_var] = 0;
	ctx->len_message = len_message_local_var;

	ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_MESSAGE;

	ret = EM_SUCCESS;
out:
	if (ess_ctx) {
		em_free(ess_ctx);
		ess_ctx = NULL;
	}

	if (decrypted_recovery_data)
		em_free(decrypted_recovery_data);

	return ret;
#else
	return EM_ERR_EM_ESS_RECOVERY_ESI_V1_NOT_SUPPORT;
#endif
}

int em_ess_delete_token_offline(em_context *ctx)
{
	int ret;
	uint8_t *response_message = NULL;
	uint32_t len_response_message = 0;

	uint32_t len_message_local_var;	//for exynos bootloader

	response_message = (uint8_t *)em_calloc(EM_LEN_MESSAGE, 1);
	EM_CHECK_NULL(__func__, EM_ERR_EM_ESS_DELETE_TOKEN_OFFLINE, ctx, response_message);

	ctx->len_message = 0;
	len_message_local_var = ctx->len_message;
	em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_PREFIX_RETURN_COMMAND,
			      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND), NULL, EM_LEN_MESSAGE,
			      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND));
	em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)"0", 1,
			      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM, EM_LEN_MESSAGE, 1);
	em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_OK,
			      strlen(EM_MAGIC_ESS_AT_COMMAND_OK), NULL, EM_LEN_MESSAGE,
			      strlen(EM_MAGIC_ESS_AT_COMMAND_OK));

	if (len_message_local_var + len_response_message > EM_LEN_MESSAGE) {
		LOGE("message buf len isn't normal(%u)\n", len_message_local_var + len_response_message);
		ctx->len_message = len_message_local_var;
		ret = EM_ERR_EM_ESS_DELETE_TOKEN_OFFLINE_BUF_LEN;
		goto out;
	}

	em_ess_update_req_msg(ctx->message, &len_message_local_var, response_message,
			      strlen((const char *)response_message), NULL, EM_LEN_MESSAGE, EM_LEN_MESSAGE);
	ctx->message[len_message_local_var] = 0;
	ctx->len_message = len_message_local_var;

	ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_MESSAGE;

	ret = EM_SUCCESS;
out:
	if (response_message)
		em_free(response_message);

	return ret;
}

int em_ess_get_info(em_context *ctx)
{
	int ret;
	uint8_t *response_message = NULL;
	uint32_t len_response_message = 0;

	uint32_t len_message_local_var;	//for exynos bootloader

	response_message = (uint8_t *)em_calloc(EM_LEN_MESSAGE, 1);
	EM_CHECK_NULL(__func__, EM_ERR_EM_ESS_DELETE_TOKEN_OFFLINE, ctx, response_message);

	ret = em_token_get_info(ctx);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to create info message(%08x)\n", ret);
		goto out;
	}

	memcpy(response_message, ctx->message, ctx->len_message);
	len_response_message = ctx->len_message;

	memset(ctx->message, 0, ctx->len_message);
	ctx->len_message = 0;
	len_message_local_var = ctx->len_message;
	em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_PREFIX_RETURN_COMMAND,
			      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND), NULL, EM_LEN_MESSAGE,
			      strlen(EM_MAGIC_ESS_PREFIX_RETURN_COMMAND));
	em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)"9", 1,
			      (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM, EM_LEN_MESSAGE, 1);
	em_ess_update_req_msg(ctx->message, &len_message_local_var, (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_OK,
			      strlen(EM_MAGIC_ESS_AT_COMMAND_OK), (uint8_t *)EM_MAGIC_ESS_AT_COMMAND_DELIM,
			      EM_LEN_MESSAGE, strlen(EM_MAGIC_ESS_AT_COMMAND_OK));

	if (len_message_local_var + len_response_message > EM_LEN_MESSAGE) {
		LOGE("message buf len isn't normal(%u)\n", len_message_local_var + len_response_message);
		ctx->len_message = len_message_local_var;
		ret = EM_ERR_EM_ESS_GET_INFO_BUF_LEN;
		goto out;
	}

	em_ess_update_req_msg(ctx->message, &len_message_local_var, response_message,
			      len_response_message, NULL, EM_LEN_MESSAGE, EM_LEN_MESSAGE);
	ctx->message[len_message_local_var] = 0;
	ctx->len_message = len_message_local_var;

	ctx->flags[1] |= EM_FLAGS_1_EXIST_RETURN_MESSAGE;

	ret = EM_SUCCESS;
out:
	if (response_message)
		em_free(response_message);

	return ret;
}
