#include "ssp.h"
#include "ssp_service.h"
#include "ssp_util.h"
#include "service_key.h"
#include "tz_utils.h"

#define SELECT_SSP_RESPONSE_SIZE                4

extern uint8_t sDrkCert[2048];
extern uint16_t sDrkCertLen;

extern uint8_t sServiceCert[2048];
extern uint16_t sServiceCertLen;

extern uint8_t sWrappedEccKey[300];
extern uint32_t sWrappedEccKeyLen;

int32_t getKVN30Keyset(uint8_t channelId, const uint8_t *otp_key_blob, uint32_t otp_key_blob_size, uint8_t* outKeySet, uint32_t* outKeySetLen);
void isSspValid(p_cmd_t cmd, p_rsp_t rsp) {
    uint8_t cn = 0;

    uint8_t otp_key_blob[SSP_MAX_WRAPPED_OTP_KEY_SIZE] = {0, };
    uint32_t otp_key_blob_size = 0;

    uint8_t kvn30keyset[AES_128_KEY_SIZE * 3] = {0,}; // enc 16,mac 16, dek 16
    uint32_t kvn30keysetLen = 0;

    otp_key_blob_size = cmd->dataLen;
    if (otp_key_blob_size == 0) {
        LOGE("otp_key_blob is not exist");
        rsp->ret = RET_ERR_INVALID_INPUT_PARAMS;
        goto error;
    }

    if (otp_key_blob_size > SSP_MAX_WRAPPED_OTP_KEY_SIZE) {
        LOGE("otp_key_blob length is over the buffer size, %u", otp_key_blob_size);
        rsp->ret = RET_ERR_BUFFER_OVERFLOW;
        goto error;
    }
    memcpy(otp_key_blob, cmd->data, otp_key_blob_size);
    hex_print_tag_debug("otp_key_blob", otp_key_blob, otp_key_blob_size);


    if (ESESTATUS_SUCCESS != secEseOpen(&cn)) {
        LOGE("isSspValid channel open fail");
        rsp->ret = RET_ERR_IS_SSP_VALID;
        goto error;
    }

    // ssp kvn30 session open -> opensession with cmd -> close session in getKVN30Keyset
    if (getKVN30Keyset(cn, otp_key_blob, otp_key_blob_size, kvn30keyset, &kvn30keysetLen) != RET_SUCCESS) {
        LOGE("Failed to get KVN30Keyset");
        rsp->ret = RET_ERR_IS_SSP_VALID;
        goto error;
    }

    rsp->ret = RET_SUCCESS;

error:
    if (cn != 0) {
        secEseClose(cn);
    }

    LOGD("isSspValid returned: %d", rsp->ret);
}

void getSspVerStatus(p_rsp_t rsp) {
    SSPSTATUS sspRet;
    ssp_applet_state_t sspState;

    LOGD("getSspVerStatus start");

    if (rsp == NULL) {
        LOGE("rsp is null");
        return;
    }

    sspRet = ssp_getAppletState(&sspState);
    rsp->data[0] = sspState.state;
    rsp->data[1] = sspState.version_major;
    rsp->data[2] = sspState.version_minor;
    rsp->data[3] = sspState.version_release;
    rsp->dataLen = SELECT_SSP_RESPONSE_SIZE;

    if (sspRet == SSPSTATUS_SUCCESS) {
        rsp->status = sspState.state;
        if (sspState.state == SSP_STATE_SELECTABLE
            || sspState.state == SSP_STATE_PERSONALIZED
            || sspState.state == SSP_STATE_RESET) {
            rsp->ret = RET_SUCCESS;
        } else if (sspState.state == SSP_STATE_BLOCKED
            || sspState.state == SSP_STATE_TERMINATED) {
            rsp->ret = RET_ERR_IS_SSP_VALID;
            goto error;
        }
    } else if(sspRet == SSPSTATUS_SSP_NOT_EXIST) {
        rsp->ret = RET_ERR_SSP_NOT_EXIST;
    } else {
        rsp->ret = RET_ERR_IS_SSP_VALID;
    }
error:
    LOGD("State : %x, Ver : %x.%x.%x", sspState.state, sspState.version_major, sspState.version_minor, sspState.version_release);
    LOGD("getSspVerStatus returned: %d", rsp->ret);
}

void provisionKotp(p_cmd_t cmd, p_rsp_t rsp, uint8_t type) {
    SSPSTATUS ret;
    uint8_t otp_key_blob[SSP_MAX_WRAPPED_OTP_KEY_SIZE];
    uint32_t otp_key_blob_size = SSP_MAX_WRAPPED_OTP_KEY_SIZE;

    uint8_t unwrappedEccKey[200] = {0, };
    uint32_t unwrappedEccKeyLen = sizeof(unwrappedEccKey);

    uint8_t eccKey[32] = {0, };
    uint8_t eccKeyLen = 0;

    uint32_t inputSize;

    LOGD("provisionKotp is called");
    inputSize = cmd->dataLen;
    if (inputSize > WRAPPED_CERT_SIZE) {
        LOGE("Input data is over the buffer.");
        rsp->ret = RET_ERR_BUFFER_OVERFLOW;
        return ;
    }

    if (type == 0) {
        unwrapSEMServiceKey(cmd, rsp);
    } else {
        unwrapRewrappedSEMServiceKey(cmd, rsp);
    }

    if (rsp->ret != RET_SUCCESS) {
        LOGE("UnwrapSEMServiceKey failed");
        rsp->ret = RET_ERR_PROVISION_OTP;
        return ;
    }

    hex_print_tag_debug("sWrappedEccKey", sWrappedEccKey, sWrappedEccKeyLen);

#ifdef USE_TRUSTY_UNISOC
    if (unwrapInternalData("service_key", unwrappedEccKey, &unwrappedEccKeyLen) != SSPSTATUS_SUCCESS) {
#else
    if (unwrapInternalData(sWrappedEccKey, sWrappedEccKeyLen, unwrappedEccKey, &unwrappedEccKeyLen) != SSPSTATUS_SUCCESS) {
#endif
        LOGE("Unwrap sWrappedEccKey failed");
        rsp->ret = RET_ERR_DATA_UNWRAPPING_FAIL;
        return ;
    }

    hex_print_tag_debug("unwrappedEccKey", unwrappedEccKey, unwrappedEccKeyLen);

    memcpy(&eccKeyLen, unwrappedEccKey + 6, 1);
    LOGD("eccKeyLen : %u", eccKeyLen);
    if (eccKeyLen == 32) {
        memcpy(eccKey, unwrappedEccKey + 7, eccKeyLen);
    } else if (eccKeyLen < 32) {
        memcpy(eccKey + (32 - eccKeyLen), unwrappedEccKey + 7, eccKeyLen);
        eccKeyLen = 32;
    } else {
        LOGE("eccKey is over the buffer");
        rsp->ret = RET_ERR_BUFFER_OVERFLOW;
        return ;
    }
    secure_memclear( unwrappedEccKey, sizeof ( unwrappedEccKeyLen ) );
    hex_print_tag_debug("eccKey", eccKey, eccKeyLen);

    ret = ssp_service_doProvisioning(sDrkCert, sDrkCertLen, sServiceCert, sServiceCertLen, eccKey, otp_key_blob, &otp_key_blob_size);
    secure_memclear( eccKey, sizeof( eccKey ) );

    if ( SSP_MAX_WRAPPED_OTP_KEY_SIZE < otp_key_blob_size ) {
            LOGE("otp_key is over the buffer");
            rsp->ret = RET_ERR_BUFFER_OVERFLOW;
            return;
    }

    if (ret == SSPSTATUS_SUCCESS) {
        rsp->ret = RET_SUCCESS;
        if ( MAX_DATA_SIZE < otp_key_blob_size ) {
            LOGE("otp_key_blob_size is over the buffer");
            rsp->ret = RET_ERR_BUFFER_OVERFLOW;
            return;
        }
        memcpy(rsp->data, otp_key_blob, otp_key_blob_size);
        rsp->dataLen = otp_key_blob_size;
    } else if (ret == SSPSTATUS_GET_CERT_VERIFICATION_FAIL) {
        LOGE("Get cert verification failed");
        rsp->ret = RET_ERR_GET_CERT_VERIFICATION;
    } else {
        rsp->ret = RET_ERR_PROVISION_OTP;
    }
}

void resetSspForFactory(p_rsp_t rsp) {
    SSPSTATUS ret;

    ret = ssp_service_doResetForFactory();
    rsp->status = ret;
    if (ret == SSPSTATUS_SUCCESS) {
        rsp->ret = RET_SUCCESS;
    } else {
        rsp->ret = RET_ERR_RESET_SSP;
    }
}

