#include "factory.h"

#include "tz_debug.h"
#include "sec_apdu.h"

#ifdef NXP
extern uint8_t chipType;

void getRestrictedModeNxp(p_rsp_t rsp) {
    secEse_7816_rpdu_t rpdu;
    secEse_7816_cpdu_t cpdu;
    uint8_t r_data[MAX_RAPDU_DATA_SIZE];
    uint8_t isd_aid[] = {0xA0, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x00};
    //uint8_t nxp_cmd_get_memory[] = {0x80, 0xca, 0xff, 0x21, 0x00};

    memset(&cpdu, 0, sizeof(secEse_7816_cpdu_t));
    memset(&rpdu, 0, sizeof(secEse_7816_rpdu_t));
    memset(r_data, 0, MAX_RAPDU_DATA_SIZE);
    rpdu.pdata = r_data;

    memset(&rpdu, 0, sizeof(secEse_7816_rpdu_t));
    rpdu.pdata = r_data;
    if (ESESTATUS_SUCCESS != secEseSelect(0x00, isd_aid, 0, 0x08, &rpdu) ) {
        LOGE("getRestrictedMode() failed to SELECT ISD");
        rsp->ret = RET_ERR_GET_RESTRICTED_MODE;
        goto error;
    }

    cpdu.cla = 0x80;
    cpdu.ins = 0xCA;
    cpdu.p1 = 0xFF;
    cpdu.p2 = 0x21;
    cpdu.le_type = 0x00;

    if (ESESTATUS_SUCCESS != secEseTransmit(0x00, &cpdu, &rpdu)) {
        LOGE("getRestrictedMode failed to send GET_MEMORY(NXP) apdu");
        rsp->ret = RET_ERR_GET_RESTRICTED_MODE;
        goto error;
    }

    rsp->data[0] = rpdu.sw1;
    rsp->data[1] = rpdu.sw2;
    rsp->dataLen = 2;
    rsp->ret = RET_SUCCESS;
    LOGD("getRestrictedMode : 0x%02x %02x", rpdu.sw1, rpdu.sw2);
error:
    LOGD("getRestrictedMode returned: %d", rsp->ret);
}

void getACDumpNxp(p_rsp_t rsp) {
    secEse_7816_rpdu_t rpdu;
    secEse_7816_cpdu_t cpdu;
    uint8_t r_data[512];
    ESESTATUS ese_status = 0;
    uint32_t offset = 0;
    uint32_t loop_count = 0;
    uint8_t select_aid[8] = { 0xA0, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x00 };
    uint32_t select_aid_size = 0x8;
    uint8_t apdu_nxp_get_acdump_data[2] = {0xDF, 0x26};
    uint8_t apdu_nxp_get_compressed_acdump_data[2] = {0xDF, 0x52};

    memset(&cpdu, 0, sizeof(secEse_7816_cpdu_t));
    memset(&rpdu, 0, sizeof(secEse_7816_rpdu_t));
    memset(r_data, 0, 512);
    rpdu.pdata = r_data;
    rpdu.len = 512;

    if (chipType == PN80T) {
        LOGI("PN80T chipset");
        cpdu.cla = 0x80;
        cpdu.ins = 0xCA;
        cpdu.p1 = 0x00;
        cpdu.p2 = 0xFE;
        cpdu.lc = 0x02;
        cpdu.pdata = apdu_nxp_get_acdump_data;
        cpdu.le = 0x00;
        loop_count = 1;
    } else if (chipType == SN100X) {
        LOGI("SN100X chipset");
        cpdu.cla = 0x00;
        cpdu.ins = 0xCA;
        cpdu.p1 = 0x00;
        cpdu.p2 = 0xFE;
        cpdu.lc = 0x02;
        cpdu.cpdu_type=0x01;
        cpdu.pdata = apdu_nxp_get_compressed_acdump_data;
        cpdu.le = 0x00;
        loop_count = 1;
    } else {
        LOGE("This type is not supported!");
    }

    ese_status = secEseSelect(0x00, select_aid, 0, select_aid_size, &rpdu);
    if (ESESTATUS_SUCCESS != ese_status) {
        LOGE( "getACDump() failed to SELECT Applet - ESESTATUS = 0x%08X", ese_status );
        rsp->ret = RET_ERR_GET_ACDUMP;
        goto error_getacdump;
    }

    memset(&rpdu, 0, sizeof(secEse_7816_rpdu_t));
    rpdu.pdata = r_data;
    rpdu.len = 512;
    rsp->dataLen = 0;
    for(;loop_count>0;loop_count--){
        if (ESESTATUS_SUCCESS != secEseTransmit(0x00, &cpdu, &rpdu)) {
            LOGE("getACDump() failed to send GET_ACDUMP apdu");
            rsp->ret = RET_ERR_GET_ACDUMP;
            goto error_getacdump;
        }

        if ( MAX_DATA_SIZE < (offset + rpdu.len) )
        {
            LOGE("getACDump() invalid data length");
            rsp->ret = RET_ERR_BUFFER_OVERFLOW;
            goto error_getacdump;
        }

        memcpy(rsp->data + offset, rpdu.pdata, rpdu.len);
        rsp->dataLen += rpdu.len;
        if(rsp->dataLen >= (sizeof(rsp->data)-1) ) {
            rsp->ret = RET_ERR_GET_ACDUMP;
            goto error_getacdump;
        }

        rsp->data[rsp->dataLen] = rpdu.sw1;
        rsp->data[rsp->dataLen+1] = rpdu.sw2;
        rsp->dataLen+=2;
        rsp->ret = RET_SUCCESS;
        offset = rsp->dataLen;
    }

    hex_print_tag_debug("getACDump :: ", rsp->data, rsp->dataLen);

    LOGD("getACDump : 0x%02x %02x", rpdu.sw1, rpdu.sw2);
error_getacdump:
    LOGD("getACDump returned: %d", rsp->ret);
}

void getAttackedNxp(p_rsp_t rsp) {
    // NXP : ISD SELECT -> GET APDU_CHECK_ESE_STATE response[response.length - 3] == (byte) 0x5A means attacked
    uint8_t cn = 0x00;
    secEse_7816_rpdu_t rpdu;
    secEse_7816_cpdu_t cpdu;
    uint8_t r_data[MAX_RAPDU_DATA_SIZE];
    ESESTATUS ese_status;
    //uint8_t apdu_nxp_get_attacked_state[8] = {0x80, 0xCA, 0x00, 0xFE, 0x02, 0xDF, 0x3C, 0x00};
    uint8_t isd_aid[] = {0xA0, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x00};
    uint8_t data_apdu_nxp_get_attacked_state[2] = {0xDF, 0x3C};

    LOGD("getAttacked started");

    memset(&cpdu, 0, sizeof(secEse_7816_cpdu_t));
    memset(&rpdu, 0, sizeof(secEse_7816_rpdu_t));
    memset(r_data, 0, MAX_RAPDU_DATA_SIZE);
    rpdu.pdata = r_data;

    ese_status = secEseSelect(cn, isd_aid, 0, 0x08, &rpdu);
    LOGD("eSE_Status : 0x%02x %02x", rpdu.sw1, rpdu.sw2);
    if (ESESTATUS_SUCCESS != ese_status) {
        LOGE( "getAttacked() NXP failed to SELECT ISD - ESESTATUS = 0x%08X", ese_status );
        rsp->ret = RET_ERR_GET_ATTACKED;
        goto error_getattacked;
    }

    cpdu.cla = 0x80;
    cpdu.ins = 0xCA;
    cpdu.p1 = 0x00;
    cpdu.p2 = 0xFE;
    cpdu.lc = 0x02;
    cpdu.pdata = data_apdu_nxp_get_attacked_state;
    cpdu.le = 0x00;

    if (ESESTATUS_SUCCESS != secEseTransmit(0x00, &cpdu, &rpdu)) {
        LOGE("getAttacked failed to send GET_ATTACKED(NXP) apdu");
        rsp->ret = RET_ERR_GET_ATTACKED;
        goto error_getattacked;
    }

    /* Note:  rpdu.len is uint16 anc can't be > MAX_DATA_SIZE */
    memcpy(rsp->data, rpdu.pdata, rpdu.len);
    rsp->dataLen = rpdu.len;
    if(rpdu.len+1 > (signed)sizeof(rsp->data)) {
        LOGE("getAttacked data Overflow");
        rsp->ret = RET_ERR_GET_ATTACKED;
        goto error_getattacked;
    }
    rsp->data[rpdu.len] = rpdu.sw1;
    rsp->data[rpdu.len+1] = rpdu.sw2;
    rsp->dataLen+=2;
    rsp->ret = RET_SUCCESS;

    hex_print_tag_debug("getAttacked :: ", rsp->data, rsp->dataLen);

    LOGD("getAttacked : 0x%02x %02x", rpdu.sw1, rpdu.sw2);
error_getattacked:
    LOGD("getAttacked returned: %d", rsp->ret);
}

void getOkAttackedNxp(p_rsp_t rsp) {
    // NXP : ISD SELECT -> GET APDU_CHECK_ESE_STATE response[response.length - 3] == (byte) 0x5A means NG_attacked
    uint8_t cn = 0x00;
    secEse_7816_rpdu_t rpdu;
    secEse_7816_cpdu_t cpdu;
    uint8_t r_data[MAX_RAPDU_DATA_SIZE];
    ESESTATUS ese_status;
    //uint8_t apdu_nxp_get_attacked_state[8] = {0x80, 0xCA, 0x00, 0xFE, 0x02, 0xDF, 0x3C, 0x00};
    uint8_t isd_aid[] = {0xA0, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x00};
    uint8_t data_apdu_nxp_get_okattacked_state[2] = {0xDF, 0x5B};

    LOGD("getOkAttacked started");

    memset(&cpdu, 0, sizeof(secEse_7816_cpdu_t));
    memset(&rpdu, 0, sizeof(secEse_7816_rpdu_t));
    memset(r_data, 0, MAX_RAPDU_DATA_SIZE);
    rpdu.pdata = r_data;

    ese_status = secEseSelect(cn, isd_aid, 0, 0x08, &rpdu);
    LOGD("eSE_Status : 0x%02x %02x", rpdu.sw1, rpdu.sw2);
    if (ESESTATUS_SUCCESS != ese_status) {
        LOGE( "getOkAttacked() NXP failed to SELECT ISD - ESESTATUS = 0x%08X", ese_status );
        rsp->ret = RET_ERR_GET_ATTACKED;
        goto error_getattacked;
    }

    cpdu.cla = 0x80;
    cpdu.ins = 0xCA;
    cpdu.p1 = 0x00;
    cpdu.p2 = 0xFE;
    cpdu.lc = 0x02;
    cpdu.pdata = data_apdu_nxp_get_okattacked_state;
    cpdu.le = 0x00;

    if (ESESTATUS_SUCCESS != secEseTransmit(0x00, &cpdu, &rpdu)) {
        LOGE("getOkAttacked failed to send GET_OK_ATTACKED(NXP) apdu");
        rsp->ret = RET_ERR_GET_ATTACKED;
        goto error_getattacked;
    }

    /* Note:  rpdu.len is uint16 anc can't be > MAX_DATA_SIZE */
    memcpy(rsp->data, rpdu.pdata, rpdu.len);
    rsp->dataLen = rpdu.len;
    if(rpdu.len+1 > (signed)sizeof(rsp->data)) {
        LOGE("getOkAttacked data Overflow");
        rsp->ret = RET_ERR_GET_ATTACKED;
        goto error_getattacked;
    }
    rsp->data[rpdu.len] = rpdu.sw1;
    rsp->data[rpdu.len+1] = rpdu.sw2;
    rsp->dataLen+=2;
    rsp->ret = RET_SUCCESS;

    hex_print_tag_debug("getOkAttacked :: ", rsp->data, rsp->dataLen);

    LOGD("getOkAttacked : 0x%02x %02x", rpdu.sw1, rpdu.sw2);
error_getattacked:
    LOGD("getOkAttacked returned: %d", rsp->ret);
}
#endif

#ifdef GEM
void getRestrictedModeThales(p_rsp_t rsp) {
    ESESTATUS ese_status;
    secEse_7816_rpdu_t rpdu;
    secEse_7816_cpdu_t cpdu;
    uint8_t r_data[MAX_RAPDU_DATA_SIZE];
    uint8_t isd_aid[] = {0xA0, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x00};

    memset(&cpdu, 0, sizeof(secEse_7816_cpdu_t));
    memset(&rpdu, 0, sizeof(secEse_7816_rpdu_t));
    memset(r_data, 0, MAX_RAPDU_DATA_SIZE);
    rpdu.pdata = r_data;

    ese_status = secEseSelect(0x00, isd_aid, 0, 0x08, &rpdu);
    if (ESESTATUS_SUCCESS != ese_status) {
        LOGE( "ESESTATUS = 0x%08X", ese_status );
        rsp->ret = RET_ERR_GET_RESTRICTED_MODE;
        goto error;
    }

    rsp->data[0] = rpdu.sw1;
    rsp->data[1] = rpdu.sw2;
    rsp->dataLen = 2;
    rsp->ret = RET_SUCCESS;
    LOGD("getRestrictedMode : 0x%02x %02x", rpdu.sw1, rpdu.sw2);
error:
    LOGD("getRestrictedMode returned: %d", rsp->ret);
}

void getACDumpThales(p_rsp_t rsp) {
    secEse_7816_rpdu_t rpdu;
    secEse_7816_cpdu_t cpdu;
    uint8_t r_data[MAX_RAPDU_DATA_SIZE];
    ESESTATUS ese_status = 0;
    uint32_t offset = 0;
    uint32_t loop_count = 0;
    uint8_t select_aid[13] = { 0xA0, 0x00, 0x00, 0x00, 0x18, 0x02, 0x46, 0x44, 0x46, 0x41, 0x50, 0x50, 0x01 };
    uint32_t select_aid_size = 0x0D;

    memset(&cpdu, 0, sizeof(secEse_7816_cpdu_t));
    memset(&rpdu, 0, sizeof(secEse_7816_rpdu_t));
    memset(r_data, 0, MAX_RAPDU_DATA_SIZE);
    rpdu.pdata = r_data;
    cpdu.cla = 0x80;
    cpdu.ins = 0x07;
    cpdu.p1 = 0x00;
    cpdu.p2 = 0x14; // 1st array
    cpdu.le = 0x00;
    loop_count = 2;


    ese_status = secEseSelect(0x00, select_aid, 0, select_aid_size, &rpdu);
    if (ESESTATUS_SUCCESS != ese_status) {
        LOGE( "getACDump() failed to SELECT Applet - ESESTATUS = 0x%08X", ese_status );
        rsp->ret = RET_ERR_GET_ACDUMP;
        goto error_getacdump;
    }

    rsp->dataLen = 0;
    for(;loop_count>0;loop_count--){
        if (ESESTATUS_SUCCESS != secEseTransmit(0x00, &cpdu, &rpdu)) {
            LOGE("getACDump() failed to send GET_ACDUMP apdu");
            rsp->ret = RET_ERR_GET_ACDUMP;
            goto error_getacdump;
        }

        if ( MAX_DATA_SIZE < (offset + rpdu.len) )
        {
            LOGE("getACDump() invalid data length");
            rsp->ret = RET_ERR_BUFFER_OVERFLOW;
            goto error_getacdump;
        }

        memcpy(rsp->data + offset, rpdu.pdata, rpdu.len);
        rsp->dataLen += rpdu.len;
        if(rsp->dataLen >= (sizeof(rsp->data)-1) ) {
            rsp->ret = RET_ERR_GET_ACDUMP;
            goto error_getacdump;
        }

        rsp->data[rsp->dataLen] = rpdu.sw1;
        rsp->data[rsp->dataLen+1] = rpdu.sw2;
        rsp->dataLen+=2;
        rsp->ret = RET_SUCCESS;
        offset = rsp->dataLen;
        cpdu.p2--;
    }

    hex_print_tag_debug("getACDump :: ", rsp->data, rsp->dataLen);

    LOGD("getACDump : 0x%02x %02x", rpdu.sw1, rpdu.sw2);
error_getacdump:
    LOGD("getACDump returned: %d", rsp->ret);
}

void getAttackedThales(p_rsp_t rsp) {
    // Gemalto : FDC SELECT -> APDU_CHECK_ESE_STATE , response[response.length - 3] == (byte) 0x64 means attacked
    uint8_t cn = 0x00;
    secEse_7816_rpdu_t rpdu;
    secEse_7816_cpdu_t cpdu;
    uint8_t r_data[MAX_RAPDU_DATA_SIZE];
    ESESTATUS ese_status;
    //uint8_t apdu_gemalto_get_check_ese_state[5] = {0x80, 0x03, 0x00, 0x00, 0x01};
    uint8_t fdc_aid[] = {0xA0, 0x00, 0x00, 0x00, 0x18, 0x02, 0x46, 0x44, 0x46, 0x41, 0x50, 0x50, 0x01};

    LOGD("getAttacked started");

    memset(&cpdu, 0, sizeof(secEse_7816_cpdu_t));
    memset(&rpdu, 0, sizeof(secEse_7816_rpdu_t));
    memset(r_data, 0, MAX_RAPDU_DATA_SIZE);
    rpdu.pdata = r_data;

    ese_status = secEseSelect(cn, fdc_aid, 0, 0x0D, &rpdu);
    if (ESESTATUS_SUCCESS != ese_status) {
        LOGE( "getAttacked() GEMALTO failed to SELECT FDC - ESESTATUS = 0x%08X", ese_status );
        rsp->ret = RET_ERR_GET_ATTACKED;
        goto error_getattacked;
    }

    cpdu.cla = 0x80;
    cpdu.ins = 0x03;
    cpdu.p1 = 0x00;
    cpdu.p2 = 0x00;
    cpdu.le = 0x01;
    cpdu.pdata = NULL;
    cpdu.cpdu_type = 0x00;
    cpdu.lc = 0x00;
    cpdu.le_type = 0x01;

    if (ESESTATUS_SUCCESS != secEseTransmit(0x00, &cpdu, &rpdu)) {
        LOGE("getAttacked failed to send GET_ATTACKED(GEMALTO) apdu");
        rsp->ret = RET_ERR_GET_ATTACKED;
        goto error_getattacked;
    }
    /* Note:  rpdu.len is uint16 anc can't be > MAX_DATA_SIZE */
    memcpy(rsp->data, rpdu.pdata, rpdu.len);
    rsp->dataLen = rpdu.len;
    if(rpdu.len+1 > (signed)sizeof(rsp->data)) {
        LOGE("getAttacked data Overflow");
        rsp->ret = RET_ERR_GET_ATTACKED;
        goto error_getattacked;
    }
    rsp->data[rpdu.len] = rpdu.sw1;
    rsp->data[rpdu.len+1] = rpdu.sw2;
    rsp->dataLen+=2;
    rsp->ret = RET_SUCCESS;

    hex_print_tag_debug("getAttacked :: ", rsp->data, rsp->dataLen);

    LOGD("getAttacked : 0x%02x %02x", rpdu.sw1, rpdu.sw2);
error_getattacked:
    LOGD("getAttacked returned: %d", rsp->ret);
}
#endif

void getRestrictedMode(p_rsp_t rsp) {
#ifdef GEM
    getRestrictedModeThales(rsp);
#elif defined NXP
    getRestrictedModeNxp(rsp);
#else
    LOGE("This vendor is not supported!");
#endif
}

void getACDump(p_rsp_t rsp) {
#ifdef GEM
    getACDumpThales(rsp);
#elif defined NXP
    getACDumpNxp(rsp);
#else
    LOGE("This vendor is not supported!");
#endif
}

void getAttacked(p_rsp_t rsp) {
#ifdef GEM
    getAttackedThales(rsp);
#elif defined NXP
    getAttackedNxp(rsp);
#else
    LOGE("This vendor is not supported!");
#endif
}

void getOkAttacked(p_rsp_t rsp) {
#ifdef NXP
    getOkAttackedNxp(rsp);
#else
    LOGE("This vendor is not supported!");
#endif
}
