
#include <stdarg.h>
#include <string.h>

#include "tz_log.h"
#include "sse.h"
#include "sse_private.h"
#include "sse_entry_manage_util.h"
#include "tz_debug.h"


//#define TEST_FEATURE // If ISO works, remove or change this to 0

#ifndef TEST_FEATURE
#define RPDU_DATA_MAX_SIZE  256
#else
#define RPDU_DATA_MAX_SIZE  2
#endif

#define CPDU_ENTRY_DATA_MAX_SIZE_PER_FRAGMENT    223

#define TID_HASHED_LENGTH                                       16

//#define OTP_KEY_LENGTH                                              40 // encrypted key length is 40. (cf.original opt key length is 32.)
//#define VALID_SSE_VERSION_CODE                          ((uint16_t)0x0100)

static uint32_t g_sessionId = 0x00000000;
static uint8_t g_channelId = 0x00;
static uint8_t g_eseEntryTID[TID_HASHED_LENGTH] = {0x00, };
static uint8_t g_sseVer[3] = {0x00,};

//static const uint8_t gc_semTID[16] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B };

SSESTATUS getEntryData(get_data_param_t* getParam) {
    SSPSTATUS ssp_ret = 0;

    secEse_7816_cpdu_t c_getEntryData;
    secEse_7816_rpdu_t r_getEntryData;

    uint8_t r_entry_whole_data[MAX_ENTRY_SIZE] = {0,};
    uint8_t r_data[RPDU_DATA_MAX_SIZE] = {0,};
    uint8_t r_sw1 = 0;
    uint8_t r_sw2 = 0;
    uint32_t r_len = 0;
    uint8_t c_first_data[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint16_t c_first_data_size = 0;

    uint32_t current_r_entry_whole_data_len = 0;
    int32_t fragment_index = 0;

    uint8_t e0[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint16_t e0size = 0;
    uint8_t e2[6] = {0,};
    uint8_t e2size = 0;
    uint8_t e7[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t e7size = 0;
    uint8_t e9[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t e9size = 0;
    uint8_t ea[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t easize = 0;
    uint8_t ee[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t eesize = 0;
    uint8_t ef[34] = {0,};
    uint8_t efsize = 0;

    LOGD("= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =");
    LOGD("AUTH TYPE : 0x%x", getParam->auth_type);
    hex_print_tag_debug("SEM", getParam->password, getParam->passwordLen);
    hex_print_tag_debug("SEM", getParam->templateId, getParam->templateIdLen);

    if (getParam->entryId == NULL) {
        return SSESTATUS_INVALID_PARAMETER;
    }

    if (getParam->auth_type== GED_AUTH_TYPE_PASSWD ) {
        if (getParam->password == NULL || (getParam->passwordLen > 0x10 || getParam->passwordLen < 0x06) ) {
            LOGE("Auth type is passwd but password is not given or password length is incorrect");
            return SSESTATUS_INVALID_PARAMETER;
        }
    }

    if (getParam->auth_type== GED_AUTH_TYPE_BIO) {
        if (getParam->templateId== NULL || getParam->templateIdLen != 0x20) {
            LOGE("Auth type is bio but templateId is not given or templateId length is incorrect");
            return SSESTATUS_INVALID_PARAMETER;
        }
    }

    /* construct Fiirst Data-field */
    /// 1. E2
    BUF_FILL_TLV(TAG_E2_ENTRY_ID,4, getParam->entryId, e2, e2size);
    if (getParam->auth_type != GED_AUTH_TYPE_NONE) {
        /// 2. E9-EA or EE-EF
        if (getParam->auth_type == GED_AUTH_TYPE_PASSWD) {
            BUF_FILL_TLV(TAG_EA_PASSWORD, getParam->passwordLen, getParam->password, ea, easize);
            e9[0] = TAG_E9_AUTH_TYPE_PASSWRD;
            e9[1] = 0;
            e9size = 1+1;
            BUF_APPEND(e9, e9size, ea, easize);
            e9[1] = e9size -1-1;
        } else if (getParam->auth_type == GED_AUTH_TYPE_BIO) {
            BUF_FILL_TLV(TAG_EF_TEMPLATE_ID, getParam->templateIdLen, getParam->templateId, ef, efsize);
            ee[0] = TAG_EE_AUTH_TYPE_BIO;
            ee[1] = 0;
            eesize = 1+1;
            BUF_APPEND(ee, eesize, ef, efsize);
            ee[1] = eesize-1-1;
        }

        /// 3. E7-E9 or E7-EE
        e7[0] = TAG_E7_AUTH_TYPE_INFO;
        e7[1] = 0;
        e7size = 1+1;
        if (getParam->auth_type == GED_AUTH_TYPE_PASSWD) {
            BUF_APPEND(e7, e7size, e9, e9size);
        } else if (getParam->auth_type == GED_AUTH_TYPE_BIO) {
            BUF_APPEND(e7, e7size, ee, eesize);
        }
        e7[1] = e7size-1-1;
    }

    /// 4. E0
    e0[0] = TAG_E0_DATA_PROPERTIES;
    e0[1] = 0;
    e0size = 1+1;
    BUF_APPEND(e0, e0size, e2, e2size);
    if (getParam->auth_type != GED_AUTH_TYPE_NONE) {
        BUF_APPEND(e0, e0size, e7, e7size);
    }
    e0[1] = e0size-1-1;

    memcpy(c_first_data, e0, e0size);
    c_first_data_size = e0size;

    r_len =  getParam->outDataLen;
    if (r_len > MAX_ENTRY_SIZE) {
        r_len = MAX_ENTRY_SIZE;
    } else if(r_len == 0) {
        LOGE("--SSEM-- getEntryData() outDataLen param is ZERO..");
        return SSESTATUS_RESPONSE_DATA_LENGTH_IS_ZERO;
    }

    LOGD("getEntryData E0 build done.");
    hex_print_tag_debug("SEM", e0, e0size);

    // Here, send first cpdu, next , next and next cpdu ......
    while(1) {
        if (fragment_index == 0) { // first cpdu to send 'entryId'
            c_getEntryData.cla = CLA_GED;
            c_getEntryData.ins = INS_GED;
            c_getEntryData.p1 = P1_GED_FIRST;
            c_getEntryData.p2 = getParam->auth_type | getParam->data_type;
            c_getEntryData.lc = c_first_data_size;
            c_getEntryData.le_type = LE_TYPE_GED;
            c_getEntryData.le = LE_GED;
            c_getEntryData.cpdu_type = 0;
            c_getEntryData.pdata = c_first_data;
        } else {
            c_getEntryData.cla = CLA_GED;
            c_getEntryData.ins = INS_GED;
            c_getEntryData.p1 = P1_GED_NEXT;
            c_getEntryData.p2 = getParam->auth_type | getParam->data_type;
            c_getEntryData.lc = 0;
            c_getEntryData.le_type = LE_TYPE_GED;
            c_getEntryData.le = LE_GED;
            c_getEntryData.cpdu_type = 0;
            c_getEntryData.pdata = NULL;
        }

        memset(r_data, 0x00, RPDU_DATA_MAX_SIZE);
        r_getEntryData.pdata = r_data;

        LOGD(" SSEM (getEntryData) => SSPM :: CLA %x , INS %x ,P1 %x ,P2 %x ,LC %x ,LE_TYPE %x ,LE %x ,CPDU_TYPE %x ",c_getEntryData.cla, c_getEntryData.ins, c_getEntryData.p1, c_getEntryData.p2,
            c_getEntryData.lc, c_getEntryData.le_type, c_getEntryData.le, c_getEntryData.cpdu_type);
        hex_print_tag_debug("PDATA ==>", c_getEntryData.pdata, c_getEntryData.lc);

        ssp_ret = ssp_transaction(g_sessionId, &c_getEntryData, &r_getEntryData);
        if (SSPSTATUS_SUCCESS != ssp_ret) {
            return getSseStBySspSt(ssp_ret);
        }

        r_sw1 = r_getEntryData.sw1;
        r_sw2 = r_getEntryData.sw2;
        if (!(r_sw1 == 0x90 && r_sw2 == 0x00) && !(r_sw1 == 0x63 && r_sw2 == 0x10)) {
            if(r_sw1 == RES_VERIFY_FAIL_SW1) {
                *(getParam->outRemainRetryCnt) = r_sw2;
            }
            return getSseStBySspRapdu(r_sw1, r_sw2);
        }

        memcpy(r_entry_whole_data + current_r_entry_whole_data_len, r_getEntryData.pdata, r_getEntryData.len);
        current_r_entry_whole_data_len += r_getEntryData.len;

        if (getParam->outDataLen < current_r_entry_whole_data_len) {
            LOGE("--SSEM-- getEntryData() outDataLen param is less then whole data..");
            return SSESTATUS_DATA_SIZE_TOO_BIG;
        }

        if (r_sw1 == 0x90 && r_sw2 == 0x00) { // if it's last fragment, stop the loop
            break;
        }

        fragment_index++;
    }

    LOGD("--SSEM-- getEntryData() Finally : current_r_entry_whole_data_len : %d", current_r_entry_whole_data_len);
    memcpy(getParam->outData, r_entry_whole_data, current_r_entry_whole_data_len);
    getParam->outDataLen = current_r_entry_whole_data_len;

    return SSESTATUS_OK;
}

SSESTATUS putEntryData(put_data_param_t* putParam) {
    SSPSTATUS ssp_ret = 0;

    secEse_7816_cpdu_t c_put_data;
    secEse_7816_rpdu_t r_put_data;

    uint8_t entryDataLen[3] = {0,};

    uint8_t c_whole_data[MAX_ENTRY_SIZE] = {0x00,}; // c-apdu whole data including header data
    uint32_t c_whole_data_len = 0;
    uint32_t c_whole_data_offset = 0;

    uint8_t r_data[RPDU_DATA_MAX_SIZE] = {0x00,};
    uint8_t r_sw1;
    uint8_t r_sw2;

    uint32_t fragment_index = 0;
    uint32_t fragment_size = 0;
    uint16_t last_fragment_length = 0;

    uint8_t e0[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint16_t e0size = 0;
    uint8_t e1[5] = {0,};
    uint8_t e1size = 0;
    uint8_t e2[6] = {0,};
    uint8_t e2size = 0;
    uint8_t e3[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t e3size = 0;
    uint8_t e4[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t e4size = 0;
    uint8_t e5[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t e5size = 0;
    uint8_t e6[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t e6size = 0;
    uint8_t e7[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t e7size = 0;
    uint8_t e8[3] = {0,};
    uint8_t e8size = 0;
    uint8_t e9[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t e9size = 0;
    uint8_t ea[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t easize = 0;
    uint8_t eb[3] = {0,};
    uint8_t ebsize = 0;
    uint8_t ec[3] = {0,};
    uint8_t ecsize = 0;
    uint8_t ed[3] = {0,};
    uint8_t edsize = 0;
    uint8_t ee[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t eesize = 0;
    uint8_t ef[34] = {0,};
    uint8_t efsize = 0;

    if(putParam->entryId == NULL) {
        return SSESTATUS_INVALID_PARAMETER;
    }

    if(putParam->inDataLen > MAX_ENTRY_DATA_SIZE) {
        LOGE("inDataLen is too big! : 0x%x",putParam->inDataLen);
        return SSESTATUS_DATA_SIZE_TOO_BIG;
    }

    if(putParam->inDataLen > 0) {
        entryDataLen[0] = (uint8_t)(((putParam->inDataLen & 0x00ff0000) >> 16) & 0xff);
        entryDataLen[1] = (uint8_t)(((putParam->inDataLen & 0x0000ff00) >> 8) & 0xff);
        entryDataLen[2] = (uint8_t)((putParam->inDataLen & 0x000000ff) & 0xff);
    } else {
        entryDataLen[0] = 0;
        entryDataLen[1] = 0;
        entryDataLen[2] = 0;
    }

    hex_print_tag_debug("putEntryData :: entryDataLen", entryDataLen, 3);

    if(putParam->scope== PED_SCOPE_PUBLIC) {
        if(putParam->accessableTAID == NULL || putParam->accessableTAIDLen <= 0) {
            LOGE("Data Scope is PUBLIC, but TAID is not given in parameter\n");
            return SSESTATUS_INVALID_PARAMETER;
        }
    }

    if(putParam->auth_type == PED_AUTH_TYPE_PASSWD) {
        if(putParam->password == NULL || (putParam->passwordLen > 0x10 || putParam->passwordLen < 0x06) ) {
            LOGE("AUTH TYPE is PASSWORD, but password is not given in parameter\n");
            return SSESTATUS_INVALID_PARAMETER;
        }
    }

    if(putParam->auth_type == PED_AUTH_TYPE_BIO) {
        if(putParam->templateId1 == NULL) {
            LOGE("AUTH TYPE is BIO, but fp templateId is not given in parameter\n");
            return SSESTATUS_INVALID_PARAMETER;
        }
    }

    if(putParam->scope != PED_SCOPE_PUBLIC && putParam->scope != PED_SCOPE_PRIVATE) {
        LOGE("SCOPE value is invalid : 0x%x\n",putParam->scope);
        return SSESTATUS_INVALID_PARAMETER;
    }

    if(putParam->auth_type != PED_AUTH_TYPE_NONE && putParam->auth_type != PED_AUTH_TYPE_BIO && putParam->auth_type != PED_AUTH_TYPE_PASSWD) {
        LOGE("AUTH TYPE value is invalid : 0x%x\n",putParam->auth_type );
        return SSESTATUS_INVALID_PARAMETER;
    }

    if(putParam->data_type != PED_DATA_TYPE_NORMAL && putParam->data_type != PED_DATA_TYPE_PBED) {
        LOGE("DATA TYPE value is invalid : 0x%x\n",putParam->data_type);
        return SSESTATUS_INVALID_PARAMETER;
    }

    if(putParam->delete_option!= PED_DELETE_OPTION_DELETABLE && putParam->delete_option != PED_DELETE_OPTION_RESTRICTED
        && putParam->delete_option != PED_DELETE_OPTION_PERMANENT) {
        LOGE("DELETE OPTION value is invalid : 0x%x\n",putParam->delete_option);
        return SSESTATUS_INVALID_PARAMETER;
    }

    if(putParam->update_command != PED_UPDATE_DATA_N_ACCESS_RULE && putParam->update_command != PED_UPDATE_ACCESS_RULE) {
        LOGE("UPDATE COMMAND value is invalid : 0x%x\n",putParam->update_command);
        return SSESTATUS_INVALID_PARAMETER;
    }

    if(putParam->data_type == PED_DATA_TYPE_PBED &&
        (putParam->lenDEK != PED_PBED_DEK_LEN_128 && putParam->lenDEK != PED_PBED_DEK_LEN_256
         && putParam->lenDEK != PED_PBED_DEK_LEN_384 && putParam->lenDEK != PED_PBED_DEK_LEN_512) ) {
        LOGE("DEK LEN value is invalid : 0x%x\n", putParam->lenDEK);
        return SSESTATUS_INVALID_PARAMETER;
    }

    if(putParam->data_type != PED_AUTH_TYPE_NONE && putParam->retryCounter == 0) {
        LOGE("retryCounter value is invalid : 0x%x\n", putParam->retryCounter);
        return SSESTATUS_INVALID_PARAMETER;
    }

    memset(&c_put_data, 0, sizeof(secEse_7816_cpdu_t));
    memset(&r_put_data, 0, sizeof(secEse_7816_rpdu_t));
    r_put_data.pdata = r_data;

    //// construct [PUT-FIRST] header part : E0 [
    /// 1. E1
    BUF_FILL_TLV(TAG_E1_DATA_ENTRY_LENGTH, 0x03, entryDataLen, e1, e1size);
    /// 2. E2
    BUF_FILL_TLV(TAG_E2_ENTRY_ID, 0x04, putParam->entryId, e2, e2size);
    /// 3. E3-(E4-E5-E4-E5-E4-E5-E4-E5-E4-E5)-E6
    e3[0] = TAG_E3_ACCESS_RULE;
    e3[1] = 0;
    e3size = 1+1;

    if(putParam->scope== PED_SCOPE_PUBLIC) {
        e4[0] = TAG_E4_APPLET;
        e4[1] = 0;
        e4size = 1+1;

        if(putParam->accessableApppletAid1 != NULL && putParam->accessableAppletAid1Len > 0) {
            BUF_FILL_TLV(TAG_E5_AID, putParam->accessableAppletAid1Len, putParam->accessableApppletAid1, e5, e5size);
            BUF_APPEND(e4, e4size, e5, e5size);
            MEMSET_BUF_BUFSIZE(e5, e5size);
        }
        if(putParam->accessableApppletAid2 != NULL && putParam->accessableAppletAid2Len > 0) {
            BUF_FILL_TLV(TAG_E5_AID, putParam->accessableAppletAid2Len, putParam->accessableApppletAid2, e5, e5size);
            BUF_APPEND(e4, e4size, e5, e5size);
            MEMSET_BUF_BUFSIZE(e5, e5size);
        }
        if(putParam->accessableApppletAid3 != NULL && putParam->accessableAppletAid3Len > 0) {
            BUF_FILL_TLV(TAG_E5_AID, putParam->accessableAppletAid3Len, putParam->accessableApppletAid3, e5, e5size);
            BUF_APPEND(e4, e4size, e5, e5size);
            MEMSET_BUF_BUFSIZE(e5, e5size);
        }
        if(putParam->accessableApppletAid4 != NULL && putParam->accessableAppletAid4Len > 0) {
            BUF_FILL_TLV(TAG_E5_AID, putParam->accessableAppletAid4Len, putParam->accessableApppletAid4, e5, e5size);
            BUF_APPEND(e4, e4size, e5, e5size);
            MEMSET_BUF_BUFSIZE(e5, e5size);
        }
        if(putParam->accessableApppletAid5 != NULL && putParam->accessableAppletAid5Len > 0) {
            BUF_FILL_TLV(TAG_E5_AID, putParam->accessableAppletAid5Len, putParam->accessableApppletAid5, e5, e5size);
            BUF_APPEND(e4, e4size, e5, e5size);
            MEMSET_BUF_BUFSIZE(e5, e5size);
        }
        e4[1] = e4size -1 -1;

        if( (putParam->accessableApppletAid1 != NULL && putParam->accessableAppletAid1Len > 0)
            || (putParam->accessableApppletAid2 != NULL && putParam->accessableAppletAid2Len > 0)
            || (putParam->accessableApppletAid3 != NULL && putParam->accessableAppletAid3Len > 0)
            || (putParam->accessableApppletAid4 != NULL && putParam->accessableAppletAid4Len > 0)
            || (putParam->accessableApppletAid5 != NULL && putParam->accessableAppletAid5Len > 0) ) {
            BUF_APPEND(e3, e3size, e4, e4size); // e5 is mandatory. no e5 values, no e4 tag.
            e3[1] = e3size -1 -1;
        }
    }

    if(putParam->accessableTAID != NULL && putParam->accessableTAIDLen > 0) {
        BUF_FILL_TLV(TAG_E6_TA_ID, putParam->accessableTAIDLen, putParam->accessableTAID, e6, e6size);
    } else {
        e6[0] = TAG_E6_TA_ID;
        e6[1] = 0;
        e6size = 1+1;
    }
    BUF_APPEND(e3, e3size, e6, e6size);
    e3[1] = e3size -1 -1;

    LOGE("putEntryData E3, putParam->auth_type : %x",putParam->auth_type);

    if(putParam->auth_type != PED_AUTH_TYPE_NONE) {
        /// 4. E7-E8-E9 or E7-E8-EE
        e7[0] = TAG_E7_AUTH_TYPE_INFO;
        e7[1] = 0;
        e7size = 1+1;

        // E8
        BUF_FILL_TLV(TAG_E8_RETRY_COUNT, 1, &putParam->retryCounter, e8, e8size);
        BUF_APPEND(e7, e7size, e8, e8size);

        e7[1] = e7size -1 -1;

        if(putParam->auth_type == PED_AUTH_TYPE_PASSWD) {
            // E9-EA and E9-EB and E9-EC and E9-ED
            e9[0] = TAG_E9_AUTH_TYPE_PASSWRD;
            e9[1] = 0;
            e9size = 1+1;

            BUF_FILL_TLV(TAG_EA_PASSWORD, putParam->passwordLen, putParam->password, ea, easize);
            if(putParam->iterationCounter1 != 0) {
                BUF_FILL_TLV(TAG_EB_ITERATION_COUNT1, 0x01, &putParam->iterationCounter1, eb, ebsize);
            }
            if(putParam->iterationCounter2 != 0) {
                BUF_FILL_TLV(TAG_EC_ITERATION_COUNT2, 0x01, &putParam->iterationCounter2, ec, ecsize);
            }
            BUF_FILL_TLV(TAG_ED_LENGTH_OF_DEK, 0x01, &putParam->lenDEK, ed, edsize);

            BUF_APPEND(e9, e9size, ea, easize);
            if(putParam->iterationCounter1 != 0) {
                BUF_APPEND(e9, e9size, eb, ebsize);
            }
            if(putParam->iterationCounter2 != 0) {
                BUF_APPEND(e9, e9size, ec, ecsize);
            }
            BUF_APPEND(e9, e9size, ed, edsize);
            e9[1] = e9size -1 -1;

            BUF_APPEND(e7, e7size, e9, e9size);
            e7[1] = e7size -1 -1;
        }
        else if(putParam->auth_type == PED_AUTH_TYPE_BIO) {
            // EE-EF (and EE-EF and EE-EF)
            ee[0] = TAG_EE_AUTH_TYPE_BIO;
            ee[1] = 0;
            eesize = 1+1;
            BUF_FILL_TLV(TAG_EF_TEMPLATE_ID, 0x20, putParam->templateId1, ef, efsize);
            BUF_APPEND(ee, eesize, ef, efsize);
            MEMSET_BUF_BUFSIZE(ef, efsize);
            if(putParam->templateId2 != NULL) {
                BUF_FILL_TLV(TAG_EF_TEMPLATE_ID, 0x20, putParam->templateId2, ef, efsize);
                BUF_APPEND(ee, eesize, ef, efsize);
                MEMSET_BUF_BUFSIZE(ef, efsize);

                if(putParam->templateId3 != NULL) {
                    BUF_FILL_TLV(TAG_EF_TEMPLATE_ID, 0x20, putParam->templateId3, ef, efsize);
                    BUF_APPEND(ee, eesize, ef, efsize);
                }
            }
            ee[1] = eesize -1 -1;

            BUF_APPEND(e7, e7size, ee, eesize);
            e7[1] = e7size -1 -1;

        }
    }

    // Fianlly, top tag E0
    e0[0] = TAG_E0_DATA_PROPERTIES;
    e0size = e1size + e2size + e3size;
    if(putParam->auth_type != PED_AUTH_TYPE_NONE) {
        e0size += e7size;
    }

    if(e0size > 0x7f) {
        e0[1] = 0x81;
        e0[2] = (uint8_t)(e0size & 0xff);
        e0size = 1+2;
    } else {
        e0[1] = (uint8_t)(e0size & 0xff);
        e0size = 1+1;
    }
    BUF_APPEND(e0, e0size, e1, e1size);
    BUF_APPEND(e0, e0size, e2, e2size);
    BUF_APPEND(e0, e0size, e3, e3size);
    if(putParam->auth_type != PED_AUTH_TYPE_NONE) {
        BUF_APPEND(e0, e0size, e7, e7size);
    }

    if(e0size > 0x7f) {
        e0[2] = (uint8_t)(e0size & 0xff) -1 -2;
    } else {
        e0[1] = (uint8_t)(e0size & 0xff) -1 -1;
    }
    //// construct [PUT-FIRST] data part : E0 ]

    //hex_print_tag_debug("SEM", e0, e0size);
    MEMCPY_BUF_BUFSIZE(c_whole_data, e0, c_whole_data_len, e0size);
    if(putParam->inDataLen > 0 && putParam->inData != NULL) {
        BUF_APPEND(c_whole_data, c_whole_data_len, putParam->inData, putParam->inDataLen);
    }

    LOGD("TRACK ++++++++++++++++++++++++++++++++++++++ :: [%d]",c_whole_data_len);
    hex_print_tag_debug("SEM", c_whole_data, c_whole_data_len);

    fragment_size = c_whole_data_len / CPDU_ENTRY_DATA_MAX_SIZE_PER_FRAGMENT;
    last_fragment_length = c_whole_data_len % CPDU_ENTRY_DATA_MAX_SIZE_PER_FRAGMENT;
    if(last_fragment_length > 0) {
        fragment_size += 1;
    }

    LOGD("fragment_index : %d, fragment_size : %d, last_fragment_length : %d, c_whole_data_len : %d\n",fragment_index,fragment_size,last_fragment_length,c_whole_data_len);
    for (fragment_index = 0 ; fragment_index < fragment_size ; fragment_index++) {
        LOGD("in loop. fragment_index : %d, fragment_size : %d\n",fragment_index,fragment_size);
        c_put_data.cla = CLA_PED;
        c_put_data.ins = INS_PED;
        c_put_data.p1 = putParam->entryCreation | putParam->data_type | putParam->delete_option | putParam->auth_type;
        c_put_data.p2 = putParam->update_command | (putParam->scope == PED_SCOPE_PUBLIC ? (putParam->scope | putParam->scope_public_read_count) : putParam->scope);

        if(fragment_size == 1) {
            c_put_data.lc = last_fragment_length;
        } else {
            if(fragment_index == fragment_size -1)
                c_put_data.lc = last_fragment_length;
            else
                c_put_data.lc = CPDU_ENTRY_DATA_MAX_SIZE_PER_FRAGMENT;
        }
        c_put_data.cpdu_type = CPDU_TYPE_PED;
        c_put_data.pdata = c_whole_data + c_whole_data_offset;
        c_put_data.le = LE_PED;
        c_put_data.le_type = LE_TYPE_PED;

        if(fragment_index > 0)
            c_put_data.p1 = PED_NEXT;

        c_whole_data_offset += c_put_data.lc;

        LOGD("cla : %x",c_put_data.cla);
        LOGD("ins : %x",c_put_data.ins);
        LOGD("p1 : %x",c_put_data.p1);
        LOGD("p2 : %x",c_put_data.p2);
        LOGD("lc : %x",c_put_data.lc);
        //hex_print_tag_debug("SEM", c_put_data.pdata, c_put_data.lc);


        ssp_ret = ssp_transaction(g_sessionId, &c_put_data, &r_put_data);
        if(ssp_ret != SSPSTATUS_SUCCESS) {
            return getSseStBySspSt(ssp_ret);
        }

        r_sw1 = r_put_data.sw1;
        r_sw2 = r_put_data.sw2;
        if(!(r_sw1 == 0x90 && r_sw2 == 0x00)) {
            if(r_sw1 == RES_VERIFY_FAIL_SW1) {
                *(putParam->outRemainRetryCnt) = r_sw2;
            }
            return getSseStBySspRapdu(r_sw1, r_sw2);
        }
    }

    return SSESTATUS_OK;
}

SSESTATUS updateAuth(update_auth_param_t* uaParam) {
    SSPSTATUS ssp_ret = 0;

    secEse_7816_cpdu_t c_ua;
    secEse_7816_rpdu_t r_ua;

    uint8_t r_data[RPDU_DATA_MAX_SIZE] = {0x00,};
    uint8_t r_sw1;
    uint8_t r_sw2;

    uint8_t e0[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint16_t e0size = 0;
    uint8_t e2[6] = {0,};
    uint8_t e2size = 0;

    uint8_t e7Old[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t e7Oldsize = 0;
    uint8_t e9Old[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t e9Oldsize = 0;
    uint8_t eaOld[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t eaOldsize = 0;
    uint8_t eeOld[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t eeOldsize = 0;
    uint8_t efOld[34] = {0,};
    uint8_t efOldsize = 0;

    uint8_t f2New[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t f2Newsize = 0;
    uint8_t e8New[3] = {0,};
    uint8_t e8Newsize = 0;
    uint8_t e9New[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t e9Newsize = 0;
    uint8_t eaNew[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t eaNewsize = 0;
    uint8_t ebNew[3] = {0,};
    uint8_t ebNewsize = 0;
    uint8_t ecNew[3] = {0,};
    uint8_t ecNewsize = 0;
    uint8_t edNew[3] = {0,};
    uint8_t edNewsize = 0;
    uint8_t eeNew[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t eeNewsize = 0;
    uint8_t efNew[34] = {0,};
    uint8_t efNewsize = 0;

    memset(&c_ua, 0, sizeof(secEse_7816_cpdu_t));
    memset(&r_ua, 0, sizeof(secEse_7816_rpdu_t));
    r_ua.pdata = r_data;

    if(uaParam->entryId == NULL) {
        return SSESTATUS_INVALID_PARAMETER;
    }

    if(uaParam->old_auth_type == UA_AUTH_TYPE_NONE && uaParam->new_auth_type == UA_AUTH_TYPE_NONE) {
        LOGE("Old auth type and new auth type both are none\n");
        return SSESTATUS_INVALID_PARAMETER;
    }

    if(uaParam->new_data_type != PED_AUTH_TYPE_NONE && uaParam->new_retryCounter == 0) {
        LOGE("new_retryCounter value is invalid : 0x%x\n", uaParam->new_retryCounter);
        return SSESTATUS_INVALID_PARAMETER;
    }

    // construct data field
    /// 1. E2
    BUF_FILL_TLV(TAG_E2_ENTRY_ID, 4, uaParam->entryId, e2, e2size);

    if(uaParam->old_auth_type != UA_AUTH_TYPE_NONE) {
        /// 2. E7(old auth)-E9-EA or E7-EE-EF
        e7Old[0] = TAG_E7_AUTH_TYPE_INFO;
        e7Old[1] = 0;
        e7Oldsize = 1+1;
        if(uaParam->old_auth_type == UA_AUTH_TYPE_PASSWD) {
            e9Old[0] = TAG_E9_AUTH_TYPE_PASSWRD;
            e9Old[1] = 0;
            e9Oldsize = 1+1;
            BUF_FILL_TLV(TAG_EA_PASSWORD, uaParam->old_passwordLen, uaParam->old_password, eaOld, eaOldsize);
            BUF_APPEND(e9Old, e9Oldsize, eaOld, eaOldsize);
            e9Old[1] = e9Oldsize -1 -1;
            BUF_APPEND(e7Old, e7Oldsize, e9Old, e9Oldsize);
            e7Old[1] = e7Oldsize -1-1;
        } else if(uaParam->old_auth_type == UA_AUTH_TYPE_BIO) {
            eeOld[0] = TAG_EE_AUTH_TYPE_BIO;
            eeOld[1] = 0;
            eeOldsize = 1+1;
            BUF_FILL_TLV(TAG_EF_TEMPLATE_ID, 0x20, uaParam->old_templateId, efOld, efOldsize);
            BUF_APPEND(eeOld, eeOldsize, efOld, efOldsize);
            eeOld[1] = eeOldsize -1 -1;
            BUF_APPEND(e7Old, e7Oldsize, eeOld, eeOldsize);
            e7Old[1] = e7Oldsize -1-1;
        }
    }

    if(uaParam->new_auth_type != UA_AUTH_TYPE_NONE) {
        /// 3. F2-E8 and F2-E9 and F2-EE
        //// 4. E8
        BUF_FILL_TLV(TAG_E8_RETRY_COUNT, 1, &uaParam->new_retryCounter, e8New, e8Newsize);
        if(uaParam->new_auth_type == UA_AUTH_TYPE_PASSWD) {
            //// 5. E9
            e9New[0] = TAG_E9_AUTH_TYPE_PASSWRD;
            e9New[1] = 0;
            e9Newsize = 1+1;
            BUF_FILL_TLV(TAG_EA_PASSWORD, uaParam->new_passwordLen, uaParam->new_password, eaNew, eaNewsize);
            if(uaParam->new_iterationCounter1 != 0) {
                BUF_FILL_TLV(TAG_EB_ITERATION_COUNT1, 1,&uaParam->new_iterationCounter1, ebNew, ebNewsize);
            }
            if(uaParam->new_iterationCounter2 != 0) {
                BUF_FILL_TLV(TAG_EC_ITERATION_COUNT2, 1, &uaParam->new_iterationCounter2, ecNew, ecNewsize);
            }
            if(uaParam->new_data_type == PED_DATA_TYPE_PBED) {
                BUF_FILL_TLV(TAG_ED_LENGTH_OF_DEK, 1, &uaParam->new_lenDEK, edNew, edNewsize);
            }
            BUF_APPEND(e9New, e9Newsize, eaNew, eaNewsize);

            if(uaParam->new_iterationCounter1 != 0) {
                BUF_APPEND(e9New, e9Newsize, ebNew, ebNewsize);
            }
            if(uaParam->new_iterationCounter2 != 0) {
                BUF_APPEND(e9New, e9Newsize, ecNew, ecNewsize);
            }
            if(uaParam->new_data_type == PED_DATA_TYPE_PBED) {
                BUF_APPEND(e9New, e9Newsize, edNew, edNewsize);
            }
            e9New[1] = e9Newsize -1-1;
        } else if(uaParam->new_auth_type == UA_AUTH_TYPE_BIO) {
            //// 6. EE
            eeNew[0] = TAG_EE_AUTH_TYPE_BIO;
            eeNew[1] = 0;
            eeNewsize = 1+1;
            BUF_FILL_TLV(TAG_EF_TEMPLATE_ID, 0x20, uaParam->new_templateId, efNew, efNewsize);
            BUF_APPEND(eeNew, eeNewsize, efNew, efNewsize);
            eeNew[1] = eeNewsize -1-1;
        }

        f2New[0] = TAG_F2_NEW_AUTH_TYPE_INFO;
        if(uaParam->new_auth_type == UA_AUTH_TYPE_PASSWD) {
            f2Newsize = e8Newsize+e9Newsize;
        } else if(uaParam->new_auth_type == UA_AUTH_TYPE_BIO) {
            f2Newsize = e8Newsize+eeNewsize;
        }
        if(f2Newsize > 0x7f) {
            f2New[1] = 0x81;
            f2New[2] = f2Newsize;
            f2Newsize = 1+2;
        } else {
            f2New[1] = f2Newsize;
            f2Newsize = 1+1;
        }
        BUF_APPEND(f2New, f2Newsize, e8New, e8Newsize);
        if(uaParam->new_auth_type == UA_AUTH_TYPE_PASSWD) {
            BUF_APPEND(f2New, f2Newsize, e9New, e9Newsize);
        } else if(uaParam->new_auth_type == UA_AUTH_TYPE_BIO) {
            BUF_APPEND(f2New, f2Newsize, eeNew, eeNewsize);
        }
        if(f2Newsize > 0x7f) {
            f2New[2] = f2Newsize -1-2;
        } else {
            f2New[1] = f2Newsize -1-1;
        }
    }

    // 7. fianally, E0
    e0[0] = TAG_E0_DATA_PROPERTIES;

    e0size += e2size;
    if(uaParam->old_auth_type != UA_AUTH_TYPE_NONE) {
        e0size += e7Oldsize;
    }
    if(uaParam->new_auth_type != UA_AUTH_TYPE_NONE) {
        e0size += f2Newsize;
    }

    if(e0size > 0x7f) {
        e0[1] = 0x81;
        e0[2] = e0size;
        e0size = 1+2;
    } else {
        e0[1] = e0size;
        e0size = 1+1;
    }

    BUF_APPEND(e0, e0size, e2, e2size);

    if(uaParam->old_auth_type != UA_AUTH_TYPE_NONE) {
        BUF_APPEND(e0, e0size, e7Old, e7Oldsize);
    }
    if(uaParam->new_auth_type != UA_AUTH_TYPE_NONE) {
        BUF_APPEND(e0, e0size, f2New, f2Newsize);
    }

    LOGD("updateAuth e0 build done :: e0size - 0x%x",e0size);
    hex_print_tag_debug("SEM", e0, e0size);

    // TODO make APDU and send
    c_ua.cla = CLA_UA;
    c_ua.ins = INS_UA;
    c_ua.p1 = uaParam->ua_command;
    c_ua.p2 = uaParam->old_auth_type | uaParam->old_data_type;
    c_ua.lc = e0size;
    c_ua.cpdu_type = CPDU_TYPE_UA;
    c_ua.pdata = e0;
    c_ua.le = LE_UA;
    c_ua.le_type = LE_TYPE_UA;

    ssp_ret = ssp_transaction(g_sessionId, &c_ua, &r_ua);
    if(SSPSTATUS_SUCCESS != ssp_ret) {
        return getSseStBySspSt(ssp_ret);
    }

    r_sw1 = r_ua.sw1;
    r_sw2 = r_ua.sw2;
    if(!(r_sw1 == 0x90 && r_sw2 == 0x00)) {
        if(r_sw1 == RES_VERIFY_FAIL_SW1) {
            *(uaParam->outRemainRetryCnt) = r_sw2;
        }
        return getSseStBySspRapdu(r_sw1, r_sw2);
    }

    return SSESTATUS_OK;
}


SSESTATUS deleteEntryData(delete_data_param_t* deleteParam) {
    SSPSTATUS ssp_ret;

    secEse_7816_cpdu_t c_delete_data;
    secEse_7816_rpdu_t r_delete_data;
    uint8_t r_data[RPDU_DATA_MAX_SIZE] = {0x00,};
    uint8_t r_sw1;
    uint8_t r_sw2;

    uint8_t delete_data_part[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
//    uint16_t delete_data_part_size = 0;

    // contruct c-apdu data-field [[
    uint8_t e0[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint16_t e0size = 0;
    uint8_t e2[6] = {0,};
    uint8_t e2size = 0;
    uint8_t e7[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t e7size = 0;
    uint8_t e9[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t e9size = 0;
    uint8_t ea[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t easize = 0;
    uint8_t ee[MAX_ENTRY_COMAND_DATAFIELD_SIZE] = {0,};
    uint8_t eesize = 0;
    uint8_t ef[34] = {0,};
    uint8_t efsize = 0;
    // contruct c-apdu data-field ]]

    memset(&c_delete_data, 0, sizeof(secEse_7816_cpdu_t));
    memset(&r_delete_data, 0, sizeof(secEse_7816_rpdu_t));
    r_delete_data.pdata = r_data;

    // construct delete entry data part
    if(deleteParam->entryId == NULL) {
        return SSESTATUS_INVALID_PARAMETER;
    }

    if(deleteParam->auth_type== GED_AUTH_TYPE_PASSWD ) {
        if(deleteParam->password == NULL || (deleteParam->passwordLen > 0x10 || deleteParam->passwordLen < 0x06) ) {
            LOGE("Auth type is passwd but password is not given or password length is incorrect");
            return SSESTATUS_INVALID_PARAMETER;
        }
    }

    if(deleteParam->auth_type== GED_AUTH_TYPE_BIO) {
        if(deleteParam->templateId== NULL || deleteParam->templateIdLen != 0x20) {
            LOGE("Auth type is bio but templateId is not given or templateId length is incorrect");
            return SSESTATUS_INVALID_PARAMETER;
        }
    }

    /* construct Fiirst Data-field */
    /// 1. E2
    BUF_FILL_TLV(TAG_E2_ENTRY_ID,4, deleteParam->entryId, e2, e2size);

    if(deleteParam->auth_type != GED_AUTH_TYPE_NONE) {
        /// 2. E9-EA or EE-EF
        if( deleteParam->auth_type == GED_AUTH_TYPE_PASSWD ) {
            BUF_FILL_TLV(TAG_EA_PASSWORD, deleteParam->passwordLen, deleteParam->password, ea, easize);
            e9[0] = TAG_E9_AUTH_TYPE_PASSWRD;
            e9[1] = 0;
            e9size = 1+1;
            BUF_APPEND(e9, e9size, ea, easize);
            e9[1] = e9size -1-1;
        } else if( deleteParam->auth_type == GED_AUTH_TYPE_BIO ) {
            BUF_FILL_TLV(TAG_EF_TEMPLATE_ID, deleteParam->templateIdLen, deleteParam->templateId, ef, efsize);
            ee[0] = TAG_EE_AUTH_TYPE_BIO;
            ee[1] = 0;
            eesize = 1+1;
            BUF_APPEND(ee, eesize, ef, efsize);
            ee[1] = eesize-1-1;
        }

        /// 3. E7-E9 or E7-EE
        e7[0] = TAG_E7_AUTH_TYPE_INFO;
        e7[1] = 0;
        e7size = 1+1;
        if( deleteParam->auth_type == GED_AUTH_TYPE_PASSWD ) {
            BUF_APPEND(e7, e7size, e9, e9size);
        } else if( deleteParam->auth_type == GED_AUTH_TYPE_BIO ) {
            BUF_APPEND(e7, e7size, ee, eesize);
        }
        e7[1] = e7size-1-1;
    }

    /// 4. E0
#ifdef OT
    if( g_sseVer[0] == 0x02 && g_sseVer[1] == 0x01 && g_sseVer[2] == 0x01 ) { // OT SSE 2.01.1 workaround
        e0[0] = 0xF1;
    } else {
        e0[0] = TAG_E0_DATA_PROPERTIES;
    }
#else
    e0[0] = TAG_E0_DATA_PROPERTIES;
#endif

    e0[1] = 0;
    e0size = 1+1;

    BUF_APPEND(e0, e0size, e2, e2size);

    if(deleteParam->auth_type != GED_AUTH_TYPE_NONE) {
        BUF_APPEND(e0, e0size, e7, e7size);
    }
    e0[1] = e0size-1-1;

    LOGD("deleteEntryData E0 build done.");
    hex_print_tag_debug("SEM", e0, e0size);

    memcpy(delete_data_part, e0, e0size);

    c_delete_data.cla = CLA_DED;
    c_delete_data.ins = INS_DED;
    c_delete_data.p1 = deleteParam->auth_type;
    c_delete_data.p2 = P2_DED;
    c_delete_data.lc = e0size;//LC_DED;
    c_delete_data.cpdu_type = CPDU_TYPE_DED;
    c_delete_data.pdata = delete_data_part;
    c_delete_data.le = LE_DED;
    c_delete_data.le_type = LE_TYPE_DED;

    ssp_ret = ssp_transaction(g_sessionId, &c_delete_data, &r_delete_data);
    if(ssp_ret != SSPSTATUS_SUCCESS) {
        return getSseStBySspSt(ssp_ret);
    }

    r_sw1 = r_delete_data.sw1;
    r_sw2 = r_delete_data.sw2;
    if(!(r_sw1 == 0x90 && r_sw2 == 0x00)) {
        if(r_sw1 == RES_VERIFY_FAIL_SW1) {
            *(deleteParam->outRemainRetryCnt) = r_sw2;
        }
        return getSseStBySspRapdu(r_sw1, r_sw2);
    }

    return SSESTATUS_OK;
}

SSESTATUS deleteAllEntry() {
    SSPSTATUS ssp_ret;

    secEse_7816_cpdu_t c_delete_all;
    secEse_7816_rpdu_t r_delete_all;

    uint8_t r_data[RPDU_DATA_MAX_SIZE] = {0,};
    uint8_t r_sw1;
    uint8_t r_sw2;

    memset(&c_delete_all, 0, sizeof(secEse_7816_cpdu_t));
    memset(&r_delete_all, 0, sizeof(secEse_7816_rpdu_t));
    r_delete_all.pdata = r_data;

    c_delete_all.cla = CLA_DAE;
    c_delete_all.ins = INS_DAE;
    c_delete_all.p1 = P1_DAE;
    c_delete_all.p2 = P2_DAE;
    c_delete_all.lc = LC_DAE;
    c_delete_all.cpdu_type = CPDU_TYPE_DAE;
    c_delete_all.le_type = LE_TYPE_DAE;
    c_delete_all.le = LE_DAE;

    ssp_ret = ssp_transaction(g_sessionId, &c_delete_all, &r_delete_all);
    if(ssp_ret != SSPSTATUS_SUCCESS) {
        return getSseStBySspSt(ssp_ret);
    }

    r_sw1 = r_delete_all.sw1;
    r_sw2 = r_delete_all.sw2;
    if(!(r_sw1 == 0x90 && r_sw2 == 0x00)) {
        return getSseStBySspRapdu(r_sw1, r_sw2);
    }

    return SSESTATUS_OK;
}


SSESTATUS startService(const uint8_t *otp_key_blob, uint32_t otp_key_blob_size, const uint8_t tid[SSP_TID_SIZE]) {
    uint8_t channelId[1];
    ESESTATUS ese_status;
    SSPSTATUS ssp_session_status;
    secEse_7816_rpdu_t response;
    uint8_t r_data[RPDU_DATA_MAX_SIZE] = {0,};

    // Step 1. ISO channel open call. get g_channelId from ISO, and use this value to call openSession()
    ese_status = secEseOpen(channelId);
    if (ESESTATUS_SUCCESS != ese_status) {
        LOGE( "--SSEM-- ESESTATUS = 0x%08X", ese_status );
        return getSseStByIsoSt(ese_status);
    }
    g_channelId = channelId[0];

    // Step 2. SELECT SSE via ISO
    // TODO HERE :
    response.pdata = r_data;
    ese_status = secEseSelect(g_channelId, (uint8_t*)SSE_AID, 0, 0x10, &response);

    getSSEVersion(response.pdata[0], response.pdata[1], g_sseVer, g_sseVer+1, g_sseVer+2);

    if( ESESTATUS_SUCCESS != ese_status ) {
        LOGE( "--SSEM-- ESESTATUS = 0x%08X", ese_status );
        return getSseStByIsoSt(ese_status);
    }

    if( response.sw1 != 0x90 || response.sw2 != 0x00 ) {
        LOGE( "--SSEM-- SSE SW = %02X %02x", (uint32_t)response.sw1, (uint32_t)response.sw2 );
        return getSseStByIsoSt(ese_status);
    }

    LOGD("--SSEM-- SSE VERSION : %x.%x.%x", g_sseVer[0], g_sseVer[1], g_sseVer[2]);
    if(g_sseVer[0] < 0x02) {
        LOGE( "--SSEM-- SSE version is not supporting Low version = : %x.%x.%x", g_sseVer[0], g_sseVer[1], g_sseVer[2]);
        return SSESTATUS_UNSUPPORTED_ESE_VERSION;
    }
    // Step 3. SSPM openSession() : Start Secure Session via SSPM
    ssp_session_status = ssp_openSession(&g_sessionId, g_channelId, otp_key_blob, otp_key_blob_size, tid, SSE_AID, 0x10);

    if(ssp_session_status == SSPSTATUS_SUCCESS) {
        memcpy(g_eseEntryTID, tid, TID_HASHED_LENGTH);
        return SSESTATUS_OK;
    } else
        return getSseStBySspSt(ssp_session_status);
}

SSESTATUS stopService() {
    SSPSTATUS ssp_session_status;
    ESESTATUS ese_channel_status;

    if(g_sseVer[0] < 0x02)
        return SSESTATUS_UNSUPPORTED_ESE_VERSION;

    ssp_session_status = ssp_closeSession(g_sessionId);

    g_sessionId = 0x00000000;

    ese_channel_status = secEseClose(g_channelId);
    if (ese_channel_status != ESESTATUS_SUCCESS) {
        LOGE("--SSEM-- stopService() ese_channel_status = 0x%04x", ese_channel_status);
    }
    g_channelId = 0x00;

    memset(g_eseEntryTID, 0x00, TID_HASHED_LENGTH);

    return getSseStBySspSt(ssp_session_status);
}

void getSSEVer(uint8_t sseVer[3]) {
    sseVer[0] = g_sseVer[0];
    sseVer[1] = g_sseVer[1];
    sseVer[2] = g_sseVer[2];
}

