#ifdef SCP11_ENABLE
#include "SCP11.h"
#include "SCP11_public.h"
#include "tz_debug.h"

#include "SCP03_tools.h"
#include "SCP03_rspParser.h"
#include "SCP03_cmdGenerator.h"

static uint8_t security_level;
static uint8_t security_level_status[4] = {0,};
secure_session_key_t secure_session_key;

SCP11STATUS scp11a_openSession(uint8_t scp11_aid[], uint32_t scp11_aid_len, uint8_t channelId, uint8_t securityLevel){
    uint8_t data[MAX_RAPDU_DATA_SIZE] = {0,};
    skpm_cert_key_t skpm_cert_key;
#ifdef CHAIN_OCE_CERT
    skpm_cert_key_t skpm_ka_kloc_cert_key;
#endif
    get_data_t get_data_key;
    perform_security_operation_t perform_security_operation_key;
    secEse_7816_rpdu_t rpdu;
    SCP11STATUS ret = SCP11_SUCCESS;

    security_level = securityLevel;
    security_level_status[0] = securityLevel & 0x01; // C-MAC
    security_level_status[1] = securityLevel & 0x02; // C-DECRYPTION
    security_level_status[2] = securityLevel & 0x10; // R-MAC
    security_level_status[3] = securityLevel & 0x20; // R-ENCRYPTION

    hex_print_tag_debug("scp11_aid",scp11_aid,scp11_aid_len);

#ifdef CHAIN_OCE_CERT
    ret=scp11_skpm_cert(&skpm_ka_kloc_cert_key, &skpm_cert_key);
    if(ret != SCP11_SUCCESS){
        LOGE("scp11a_skpm_cert Failed");
        goto error;
    }
#else
    ret=scp11_skpm_cert(&skpm_cert_key);
    if(ret != SCP11_SUCCESS){
        LOGE("scp11a_skpm_cert Failed");
        goto error;
    }
#endif

    memset(&rpdu, 0, sizeof(secEse_7816_rpdu_t));
    rpdu.pdata = data;
    if (channelId < 1 || channelId > 3 || ESESTATUS_SUCCESS != secEseSelect(channelId, scp11_aid, 0, scp11_aid_len, &rpdu) ) {
        LOGE("scp11a failed to SELECT AID");
	return SCP11_SELECT_AID_FAIL;
    }

    ret=scp11_get_data(channelId,&get_data_key);
    if(ret != SCP11_SUCCESS){
        LOGE("scp11a_get_data Failed");
        goto error;
    }
#ifdef CHAIN_OCE_CERT
    ret=scp11_perform_security_operation(channelId, &skpm_ka_kloc_cert_key, &skpm_cert_key, &perform_security_operation_key);
#else
    ret=scp11_perform_security_operation(channelId, &skpm_cert_key, &perform_security_operation_key);
#endif

    if(ret !=  SCP11_SUCCESS){
        LOGE("scp11a_perform_security_operation Failed");
        goto error;
    }

    ret=scp11_mutual_authenticate(channelId, security_level, &skpm_cert_key, &get_data_key, &perform_security_operation_key, &secure_session_key);
    if(ret != SCP11_SUCCESS){
        LOGE("scp11a_mutual_authenticate Failed");
        goto error;
    }

error:
    return ret;
}

SCP11STATUS scp11b_openSession(uint8_t scp11_aid[], uint32_t scp11_aid_len, uint8_t channelId, uint8_t securityLevel){
    uint8_t data[MAX_RAPDU_DATA_SIZE] = {0,};
    get_data_t get_data_key;
    perform_security_operation_t perform_security_operation_key;
    secEse_7816_rpdu_t rpdu;
    SCP11STATUS ret = SCP11_SUCCESS;

    security_level = securityLevel;
    security_level_status[0] = securityLevel & 0x01; // C-MAC
    security_level_status[1] = securityLevel & 0x02; // C-DECRYPTION
    security_level_status[2] = securityLevel & 0x10; // R-MAC
    security_level_status[3] = securityLevel & 0x20; // R-ENCRYPTION

    hex_print_tag_debug("scp11_aid",scp11_aid,scp11_aid_len);

    memset(&rpdu, 0, sizeof(secEse_7816_rpdu_t));
    rpdu.pdata = data;
    if (channelId < 1 || channelId > 3 || ESESTATUS_SUCCESS != secEseSelect(channelId, scp11_aid, 0, scp11_aid_len, &rpdu) ) {
        LOGE("scp11b failed to SELECT AID");
	return SCP11_SELECT_AID_FAIL;
    }

    ret=scp11_get_data(channelId,&get_data_key);
    if(ret != SCP11_SUCCESS){
        LOGE("scp11b_get_data Failed");
        goto error;
    }

    ret=scp11_internal_authenticate(channelId, security_level, &get_data_key, &perform_security_operation_key, &secure_session_key);
    if(ret != SCP11_SUCCESS){
        LOGE("scp11b_internal_authenticate Failed");
        goto error;
    }

error:
    return ret;
}

SCP11STATUS scp11_apduTransceive(uint8_t channelId, p_secEse_7816_cpdu_t pCpdu, p_secEse_7816_rpdu_t pRpdu) {
    ESESTATUS result = ESESTATUS_SUCCESS;
    SCPSTATUS verifyResult = 0;
    uint8_t ICV[16] = {0,};
    uint8_t encrypted_data[MAX_CIPHERED_TEXT_SIZE] = {0,};
    int encryptionCounter = 0;

    hex_print_tag_debug("s_enc",secure_session_key.s_enc,16);
    hex_print_tag_debug("s_mac",secure_session_key.s_mac,16);
    hex_print_tag_debug("s_rmac",secure_session_key.s_rmac,16);
    hex_print_tag_debug("Receipt",secure_session_key.Receipt,16);

    LOGI("scp11_apduTransceive() start");

    if (!(security_level_status[0] == 0 &&
        security_level_status[1] == 0 &&
        security_level_status[2] == 0 &&
        security_level_status[3] == 0)) {
        // Global platform CLA for Secure messaging
        pCpdu->cla = pCpdu->cla | 0x04;
    }

    if ((security_level_status[1] != 0) && (pCpdu->lc != 0)) { // C-DECRYPTION security level enabled and data is exist
        encryptionCounter = encryptionCounter+1;
        // generate ICV for data encryption
        genICV(ICV, secure_session_key.s_enc, encryptionCounter, ICVCMD);
    }
    // generate SCP03 command (encryption and MACing)
    getSCP03Cmd(pCpdu, encrypted_data, secure_session_key.s_enc, secure_session_key.s_mac, ICV, secure_session_key.Receipt, security_level_status);
    // transceive to ISO
    result = secEseTransmit(channelId, pCpdu, pRpdu);
    if ((pRpdu->sw1 == 0x90 && pRpdu->sw2 == 0x00) || pRpdu->sw1 == 0x62 || pRpdu->sw1 == 0x63) { // status byte is 9000 or 62xx or 63xx
        if ((security_level_status[2] != 0) && (pRpdu->len > 8)) { // R-MAC security level enabled
            // verify R-MAC
            verifyResult = verifyRMAC(secure_session_key.Receipt, secure_session_key.s_rmac, pRpdu);
            if (verifyResult != SCP_SUCCESS) {
                LOGE("scp11_verifyResult FAIL");
                return verifyResult;
            }
        }
        if ((security_level_status[3] != 0) && (pRpdu->len != 0)) { // R-ENCRYPTION security level enabled and response data is exist
            decryptRspDataField(secure_session_key.s_enc, encryptionCounter, pRpdu); // decrypt Data field
        }
    }

    if (ESESTATUS_SUCCESS != result) {
        LOGE("scp11_apduTransceive() failed to send APDU");
        return SCP11_FAIL;
    }

    hex_print_tag_debug("scp11_apduTransceive() data : ",pRpdu->pdata, pRpdu->len);

    if (pRpdu->sw1 == 0x90 && pRpdu->sw2 == 00) {
        LOGI("scp11_apduTransceive() Pass. sw1-sw2 : 0x%x%x", pRpdu->sw1, pRpdu->sw2);
        return SCP11_SUCCESS;
    } else {
        LOGE("scp11_apduTransceive() Failed. sw1-sw2 : 0x%x%x",pRpdu->sw1, pRpdu->sw2);
        return SCP11_FAIL;
    }
}
#endif
