#ifdef USE_GRDM
#include "tz_platform.h"
#include "grdm.h"
#include "tz_debug.h"
#include "sem.h"
#include "ssp_util.h"
#include "crypto_module.h"

#include "grdm_app.h"
#include "grdm_transport.h"
#include "SCP03_kdf.h"
#include "grdm_fw.h"
#include "tz_utils.h"

#ifdef GRDM_TEST
#include "grdm_bl.h"
#include "grdm_test.h"
#endif

int wrapExternalData(uint8_t domain_index,uint8_t ta_index, uint8_t* input, uint32_t inputLen, uint8_t* output, uint32_t* outputLen) {
#ifdef USE_MOBICORE
    const uint8_t whiteList[][GRDM_TA_LIST_SIZE][GRDM_TID_SIZE] = {
        {{0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B},}, /* domain index 0 */
        {{0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B},}, /* domain index 1 */
        {{0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B},}, /* domain index 2 */
        {{0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B},}, /* domain index 3 */
        {{0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D},}, /* domain index 4 */
        {{0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F},}, /* domain index 5 */
        {{0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C},}, /* domain index 6 */
        {{0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04},}, /* domain index 7 */};
#endif
#ifdef USE_QSEE
    const uint8_t whiteList[][GRDM_TA_LIST_SIZE][GRDM_TID_SIZE] = {
        {"sem",}, /* domain index 0x11 */
        {"sem",}, /* domain index 0x12 */
        {"sem",}, /* domain index 0x13 */
        {"skpm","abcde",}, /* domain index 0x14 */
        {"skpm",}, /* domain index 0x15 */};
#endif
#if defined(USE_BLOWFISH) || (USE_TRUSTY_UNISOC)
    const uint8_t whiteList[][GRDM_TA_LIST_SIZE][GRDM_TID_SIZE] = {
        {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x45, 0x4d, 0x65, 0x53, 0x45},}, /* domain index 0 */
        {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x45, 0x4d, 0x65, 0x53, 0x45},}, /* domain index 1 */
        {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x45, 0x4d, 0x65, 0x53, 0x45},}, /* domain index 2 */
        {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x45, 0x4d, 0x65, 0x53, 0x45},}, /* domain index 3 */
        {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x43, 0x43, 0x4d, 0x53, 0x45},}, /* domain index 4 */};
#endif

    int32_t ret = RET_SUCCESS;
    LOGD("wrapExternalData started");
    if(domain_index < 0x11){
        LOGE("invalid domain_index %02x", domain_index);
        return GRDM_INVALID_DOMAIN_INDEX_ERROR;
    }

    domain_index = domain_index - 0x11;

    LOGD("[ wrapExternalData ] taid :: %s", whiteList[domain_index][ta_index]);
    hex_print_tag_debug("[ wrapExternalData ] input :: ", input, inputLen);

    ret = ssp_wrap_secure_object(whiteList[domain_index][ta_index], input, inputLen, output, outputLen);
    if (ret != 0){
        LOGE("wrapExternalData fail ret = %d", ret);
        return RET_ERR_DATA_WRAPPING_FAIL;
    }

    hex_print_tag_debug("[ wrapExternalData ] output :: ", output, *outputLen);

    return ret;
}

int unwrapExternalData(uint8_t* input, uint32_t inputLen, uint8_t* output, uint32_t* outputLen) {
    int32_t ret = RET_SUCCESS;

#if defined(USE_BLOWFISH) || (USE_TRUSTY_UNISOC) || defined(USE_QSEE)
    ret = ssp_unwrap_secure_object(SEM_TID, input, inputLen, output, outputLen);
#endif
#ifdef USE_MOBICORE
    ret = ssp_unwrap_secure_object(input, inputLen, output, outputLen);
#endif
    if (ret != 0){
        LOGE("unwrapExternalData fail ret = %d", ret);
        return RET_ERR_DATA_UNWRAPPING_FAIL;
    }

    hex_print_tag_debug("[ unwrapExternalData ] output :: ", output, *outputLen);

    return ret;
}

void genDomainKey(uint8_t domain_index, uint8_t* admin_key, uint8_t* domain_key) {
    uint8_t label[12] = {0,};
    uint8_t context[16] = {0,};
    uint8_t L[2];
    uint8_t adminhead[16] = {0,};

    memcpy(adminhead, admin_key, 16);
    memcpy(context, admin_key + 16, 16);
    label[11] = domain_index;

    L[0] = 0x01;
    L[1] = 0x00;

    kdf(adminhead, label, context, 16, L, domain_key);
    crypto_clear_mem(context, sizeof(context));
    crypto_clear_mem(adminhead, sizeof(adminhead));
}

GRDM_RESULT grdmPutKey(uint8_t* data, uint32_t dataLen, uint8_t* domain_key, uint32_t* domain_key_len) {
    uint8_t domain_index;
    uint8_t ta_index;
    uint8_t adminKeyBlob[GRDM_ADMIN_KEYBLOB_BUFFER_SIZE];
    uint32_t adminKeyBlobLen = sizeof(adminKeyBlob);
    uint8_t unwrappedAdminKey[GRDM_ADMIN_KEYBLOB_BUFFER_SIZE];
    uint32_t unwrappedAdminKeyLen = sizeof(unwrappedAdminKey);
    uint8_t domain_authkey[GRDM_DOMAIN_KEY_SIZE];
    uint32_t domain_authkey_len = sizeof(domain_authkey);
    uint8_t wrapped_domain_authkey[GRDM_DOMAIN_KEYBLOB_BUFFER_SIZE] = {0,};
    uint32_t wrapped_domain_authkey_len = sizeof(wrapped_domain_authkey);
    uint8_t unwrapped_domain_authkey[GRDM_DOMAIN_KEYBLOB_BUFFER_SIZE] = {0,};
    uint32_t unwrapped_domain_authkey_len = sizeof(unwrapped_domain_authkey);
    uint8_t domain_key_status = 0;
    uint8_t dataBuf[MAX_CREDENTIAL_SIZE] = {0x00,};;
    uint32_t dataBufLen = sizeof(dataBuf);
    uint32_t credential_cur = 0;
    int calculated_subdomain_index = 0;
    int calculated_credential_index = 0;

    GRDM_RESULT result = GRDM_NO_ERROR;

#if defined(USE_TRUSTY_UNISOC)
    int32_t minAdminKeyBlobSize = 0;
#elif defined(USE_QSEE)
    int32_t minAdminKeyBlobSize = 128;
#else
    int32_t minAdminKeyBlobSize = 104;
#endif
    if(dataLen < 2 + minAdminKeyBlobSize || dataLen > 2 + GRDM_ADMIN_KEYBLOB_BUFFER_SIZE) {
        LOGE("[ grdmPutKey ] wrong cmd length %d", dataLen);
        return RET_ERR_WRONG_INPUT_FORMAT;
    }

    // parse domain index
    domain_index = data[0];
    LOGD("domain_index = %02x",domain_index);
    if (domain_index < 0x11){
        LOGE("domain_index error %d", result);
        return GRDM_INVALID_INDEX;
    }

    ta_index = data[1];
    LOGD("ta_index = %02x",ta_index);

    calculated_subdomain_index = domain_index & 0x0F;
    calculated_credential_index = 1 + (calculated_subdomain_index/8);

    credential_cur = calculated_subdomain_index * 32;
    LOGD("calculated_subdomain_index = %d",calculated_subdomain_index);
    LOGD("calculated_credential_index = %d",calculated_credential_index);
    LOGD("credential_cur = %d",credential_cur);

    adminKeyBlobLen = dataLen - 2;

    // parse admin key blob
    memcpy(adminKeyBlob, data + 2, adminKeyBlobLen);

    // unwrap admin key blob
#ifdef USE_TRUSTY_UNISOC
    result = unwrapInternalData("admin_authkey", unwrappedAdminKey, &unwrappedAdminKeyLen);
#else
    result = unwrapInternalData(adminKeyBlob, adminKeyBlobLen, unwrappedAdminKey, &unwrappedAdminKeyLen);
#endif
    if (result != GRDM_NO_ERROR){
        LOGE("unwrapInternalData error %d", result);
        return RET_ERR_DATA_UNWRAPPING_FAIL;
    }

    hex_print_tag_debug("[ grdmPutKey ] unwrappedAdminKey :: ", unwrappedAdminKey, unwrappedAdminKeyLen);

    // check domain key status
    result = grdm_domain_putkey_init(domain_index, &domain_key_status);
    if (result != GRDM_NO_ERROR){
        LOGE("grdm_putKey_init error %d", result);
        goto error;
    }

    if (domain_key_status == GRDM_DOMAIN_KEY_STATUS_NOT_EXIST) {
        // generate domain auth key
        //crypto_gen_random(domain_authkey, domain_authkey_len);
        genDomainKey(domain_index, unwrappedAdminKey, domain_authkey);
        domain_authkey_len = GRDM_DOMAIN_KEY_SIZE;
        hex_print_tag_debug("[ grdmPutKey ] domain_authkey :: ", domain_authkey, domain_authkey_len);

        // put domain auth key
        result = grdm_subdomain_putKey(unwrappedAdminKey, unwrappedAdminKeyLen, domain_index, domain_authkey, domain_authkey_len);
        if (result != GRDM_NO_ERROR){
            LOGE("grdmPutKey error %d", result);
            goto error;
        }

        // get admin domain credential 0
        result = grdm_getCredential(GRDM_ADMIN_DOMAIN_INDEX, unwrappedAdminKey, unwrappedAdminKeyLen, calculated_credential_index, dataBuf, &dataBufLen);
        if (result != GRDM_NO_ERROR && result != GRDM_DATA_NOT_FOUND){
            LOGE("grdm_getCredential error %d", result);
            goto error;
        }
        hex_print_tag_debug("[ grdmPutKey ] grdm_getCredential before:: ", dataBuf, dataBufLen);

        // update admin domain credential 0
        memcpy(dataBuf + credential_cur, domain_authkey, domain_authkey_len);

        // store admin domain credential 0
        result = grdm_storeCredential(GRDM_ADMIN_DOMAIN_INDEX, unwrappedAdminKey, unwrappedAdminKeyLen, calculated_credential_index, dataBuf, MAX_CREDENTIAL_SIZE);
        if (result != GRDM_NO_ERROR){
            LOGE("grdm_storeCredential error %d", result);
            goto error;
        }

        // check admin domain credential 0
        dataBufLen = MAX_CREDENTIAL_SIZE;
        result = grdm_getCredential(GRDM_ADMIN_DOMAIN_INDEX, unwrappedAdminKey, unwrappedAdminKeyLen, calculated_credential_index, dataBuf, &dataBufLen);
        if (result != GRDM_NO_ERROR){
            LOGE("grdm_getCredential error %d", result);
            goto error;
        }
        hex_print_tag_debug("[ grdmPutKey ] grdm_getCredential after:: ", dataBuf, dataBufLen);
    } else if(domain_key_status == GRDM_DOMAIN_KEY_STATUS_EXIST){
        // get admin domain credential 0
        result = grdm_getCredential(GRDM_ADMIN_DOMAIN_INDEX, unwrappedAdminKey, unwrappedAdminKeyLen, 1, dataBuf, &dataBufLen);
        if (result != GRDM_NO_ERROR){
            LOGE("grdm_getCredential error %d", result);
            goto error;
        }
        hex_print_tag_debug("[ grdmPutKey ] grdm_getCredential injected domain keys:: ", dataBuf, dataBufLen);

        // get domain key from the credential
        memcpy(domain_authkey, dataBuf + credential_cur, domain_authkey_len);
        hex_print_tag_debug("[ grdmPutKey ] grdm_getCredential injected domain key:: ", domain_authkey, domain_authkey_len);
    } else {
        LOGE("domain_key_status error %d", domain_key_status);
        result = GRDM_INVALID_DATA;
        goto error;
    }

    // wrap domain auth key
#ifdef DEBUG_LOW
    if (domain_index == 0x11 && ta_index == 1) {
#ifdef USE_TRUSTY_UNISOC
        result = wrapInternalData("domain_authkey", domain_authkey, domain_authkey_len);
#else
        result = wrapInternalData(domain_authkey, domain_authkey_len, wrapped_domain_authkey, &wrapped_domain_authkey_len);
#endif
    } else {
        result = wrapExternalData(domain_index, ta_index,domain_authkey, domain_authkey_len, wrapped_domain_authkey, &wrapped_domain_authkey_len);
    }
 #else
    result = wrapExternalData(domain_index, ta_index,domain_authkey, domain_authkey_len, wrapped_domain_authkey, &wrapped_domain_authkey_len);
 #endif

    if (result != 0){
        LOGE("grdm_getCredential error %d", result);
        result = RET_ERR_DATA_WRAPPING_FAIL;
        goto error;
    }
    hex_print_tag_debug("[ grdmPutKey ] wrapData wrapped_domain_authkey :: ", wrapped_domain_authkey, wrapped_domain_authkey_len);
    
    // return wrapped domain auth key
    if (*domain_key_len >= wrapped_domain_authkey_len){
        memcpy(domain_key,wrapped_domain_authkey,wrapped_domain_authkey_len);
        *domain_key_len = wrapped_domain_authkey_len;
        hex_print_tag_debug("[ grdmPutKey ] domain_key :: ", domain_key, *domain_key_len);
    } else {
        result = GRDM_BUFFER_NOT_ENOUGH;
        goto error;
    }

    // for the test unwrap domain auth key
 #ifdef DEBUG_LOW
    if (domain_index == 0x11 && ta_index == 1) {
#ifdef USE_TRUSTY_UNISOC
        result = unwrapInternalData("domain_authkey", unwrapped_domain_authkey, &unwrapped_domain_authkey_len);
#else
        result = unwrapInternalData(wrapped_domain_authkey, wrapped_domain_authkey_len, unwrapped_domain_authkey, &unwrapped_domain_authkey_len);
#endif
    }

    if (result != 0){
        LOGE("grdm_getCredential error %d", result);
        result = RET_ERR_DATA_UNWRAPPING_FAIL;
        goto error;
    }
    hex_print_tag_debug("[ grdmPutKey ] Internal unwrapped_domain_authkey :: ", unwrapped_domain_authkey, unwrapped_domain_authkey_len);
 #endif

error:
    crypto_clear_mem(unwrappedAdminKey, unwrappedAdminKeyLen);
    crypto_clear_mem(wrapped_domain_authkey, wrapped_domain_authkey_len);
    crypto_clear_mem(domain_authkey, domain_authkey_len);
    crypto_clear_mem(dataBuf, dataBufLen);
    return result;
}

GRDM_RESULT grdmInjectImei(uint8_t* data, uint32_t dataLen) {
    uint8_t adminKeyBlob[GRDM_ADMIN_KEYBLOB_BUFFER_SIZE];
    uint32_t adminKeyBlobLen = GRDM_ADMIN_KEYBLOB_BUFFER_SIZE;
    uint8_t unwrappedAdminKey[GRDM_ADMIN_KEY_SIZE];
    uint32_t unwrappedAdminKeyLen = GRDM_ADMIN_KEY_SIZE;
    uint8_t imei[GRDM_IMEI_DATA_SIZE];
    uint32_t imeiLen = GRDM_IMEI_DATA_SIZE;
    GRDM_RESULT result = GRDM_NO_ERROR;

#if defined(USE_TRUSTY_UNISOC)
    int32_t minAdminKeyBlobSize = 0;
#elif defined(USE_QSEE)
    int32_t minAdminKeyBlobSize = 128;
#else
    int32_t minAdminKeyBlobSize = 104;
#endif

    if (dataLen < GRDM_IMEI_DATA_SIZE + minAdminKeyBlobSize || dataLen > GRDM_IMEI_DATA_SIZE + GRDM_ADMIN_KEYBLOB_BUFFER_SIZE){
        LOGE("grdm_injectIMEI wrong cmd length %d", dataLen);
        return RET_ERR_WRONG_INPUT_FORMAT;
    }
    adminKeyBlobLen = dataLen - GRDM_IMEI_DATA_SIZE;

    //pull admin key blob
    memcpy(imei,data,GRDM_IMEI_DATA_SIZE);
    memcpy(adminKeyBlob,data+GRDM_IMEI_DATA_SIZE,adminKeyBlobLen);

    hex_print_tag_debug("[ grdmInjectImei ] imei :: ", imei, GRDM_IMEI_DATA_SIZE);
    hex_print_tag_debug("[ grdmInjectImei ] adminKeyBlob :: ", adminKeyBlob, adminKeyBlobLen);

#ifdef USE_TRUSTY_UNISOC
    result = unwrapInternalData("admin_authkey", unwrappedAdminKey, &unwrappedAdminKeyLen);
#else
    result = unwrapInternalData(adminKeyBlob, adminKeyBlobLen, unwrappedAdminKey, &unwrappedAdminKeyLen);
#endif
    if (result != 0){
        LOGE("grdmInjectImei unwrapInternalData error %d", result);
        return RET_ERR_DATA_UNWRAPPING_FAIL;
    }
    hex_print_tag_debug("[ grdmInjectImei ] unwrappedAdminKey :: ", unwrappedAdminKey, unwrappedAdminKeyLen);
    result = grdm_injectIMEI(unwrappedAdminKey,unwrappedAdminKeyLen,imei,imeiLen);
    if (result != GRDM_NO_ERROR)
        LOGE("grdm_injectIMEI error %d", result);

    crypto_clear_mem(unwrappedAdminKey, unwrappedAdminKeyLen);
    return result;
}

GRDM_RESULT grdmGetImei(uint8_t* data, uint32_t* dataLen) {
    GRDM_RESULT result = GRDM_NO_ERROR;
    result = grdm_getIMEI(data,dataLen);
    return result;
}

GRDM_RESULT grdmProvisionAdmin(uint8_t* adminKeyBlob, uint32_t* adminKeyBlobLen) {
    uint8_t apId [AP_ID_SIZE];
    GRDM_RESULT result = GRDM_NO_ERROR;
    uint16_t ret = 0;
    uint8_t admin_authkey[GRDM_ADMIN_KEY_SIZE];
    uint32_t admin_authkey_len = GRDM_ADMIN_KEY_SIZE;
    //uint8_t adminKey[32] = {0x8E,0xE2,0x06,0x00,0xD9,0x4A,0x08,0x2F,0xC6,0x61,
    //                        0x93,0x85,0xA1,0xED,0xF7,0x20,0x2F,0xE8,0xBF,0x2F,
    //                        0x1B,0xEE,0x18,0xEC,0x9C,0xCA,0xFB,0x75,0x36,0x37,
    //                        0xA6,0xB1};
    uint8_t wrappedKey[GRDM_ADMIN_KEYBLOB_BUFFER_SIZE] = {0,};
    uint32_t wrappedKeyLen = GRDM_ADMIN_KEYBLOB_BUFFER_SIZE;

    uint8_t unwrappedKey[GRDM_ADMIN_KEYBLOB_BUFFER_SIZE] = {0,};
    uint32_t unwrappedKeyLen = GRDM_ADMIN_KEYBLOB_BUFFER_SIZE;

    ret = crypto_get_apId(apId);
    if (ret == 0){
        hex_print_tag_debug("[ grdmProvisionAdmin ] apId :: ", apId, AP_ID_SIZE);
    } else {
        LOGE("crypto_get_apId fail !!");
        return GRDM_COMM_ERROR;
    }
    result = grdm_provisionAdmin(apId, AP_ID_SIZE, admin_authkey, &admin_authkey_len);
    if (result != 0){
        LOGE("grdm_provisionAdmin fail !!");
        return result;
    }
    LOGD ("admin_authkey_len = %d", admin_authkey_len);
    //[ grdmProvisionAdmin ] apId ::  data size : 16
    //0xD14237F9C9C20EAEED908C62EE62889D
    hex_print_tag_debug("[ grdmProvisionAdmin ] admin_authkey :: ", admin_authkey, admin_authkey_len);
    //[ grdmProvisionAdmin ] admin_authkey ::  data size : 32
    //0x8EE20600D94A082FC6619385A1EDF7202FE8BF2F1BEE18EC9CCAFB753637A6B1
#ifdef USE_TRUSTY_UNISOC
    result = wrapInternalData("admin_authkey", admin_authkey, admin_authkey_len);
#else
    result = wrapInternalData(admin_authkey, admin_authkey_len, wrappedKey, &wrappedKeyLen);
#endif
    if (result != 0){
        LOGE("grdm_provisionAdmin wrapInternalData fail !!");
        result = RET_ERR_DATA_WRAPPING_FAIL;
        goto error;
    }

#ifdef USE_TRUSTY_UNISOC
    result = unwrapInternalData("admin_authkey", unwrappedKey, &unwrappedKeyLen);
#else
    hex_print_tag_debug("[ grdmProvisionAdmin ] wrapInternalData :: ", wrappedKey, wrappedKeyLen);
    if (*adminKeyBlobLen >= wrappedKeyLen){
        memcpy(adminKeyBlob,wrappedKey,wrappedKeyLen);
        *adminKeyBlobLen = wrappedKeyLen;
    } else {
        result = GRDM_BUFFER_NOT_ENOUGH;
        goto error;
    }

    result = unwrapInternalData(wrappedKey, wrappedKeyLen, unwrappedKey, &unwrappedKeyLen);
#endif
    if (result != 0){
        LOGE("grdm_provisionAdmin unwrapInternalData fail !!");
        result = RET_ERR_DATA_UNWRAPPING_FAIL;
        goto error;
    }
    hex_print_tag_debug("[ grdmProvisionAdmin ] unwrapInternalData :: ", unwrappedKey, unwrappedKeyLen);

error:
    crypto_clear_mem(wrappedKey, wrappedKeyLen);
    crypto_clear_mem(admin_authkey, admin_authkey_len);
    crypto_clear_mem(unwrappedKey, unwrappedKeyLen);
    return GRDM_NO_ERROR;
}

GRDM_RESULT grdmResetAdmin(void) {
    GRDM_RESULT result = GRDM_NO_ERROR;
    uint8_t data[255] = {0,};
    secGrdm_7816_rpdu_t rsp = {0,};
    rsp.pdata = data;
    uint8_t resetApdu[5] = {0x80, 0x88, 0x02, 0x00, 0x01};
    result = secGrdmAPDUTransmit(resetApdu, 5, &rsp);
    if (result != 0)
        LOGE("grdmResetAdmin error %d", result);

    return result;
}

GRDM_RESULT grdmFWcheckImage(uint8_t* header, uint32_t headerLen, uint8_t* rspData, uint32_t* rspDataLen){
    GRDM_RESULT result = GRDM_NO_ERROR;
    unsigned int target_size = 0;
    unsigned int fw_start_addr = 0;

    result = grdm_FW_checkImage(header, headerLen, &fw_start_addr, &target_size);
    if (result != 0){
        LOGE("grdmFWcheckImage error %d", result);
        return result;
    }
    LOGD("fw_start_addr = %d", fw_start_addr);
    LOGD("target_size = %d", target_size);
    if (*rspDataLen >= 8){
        memcpy(rspData, &fw_start_addr,4) ;
        memcpy(rspData+4, &target_size, 4);
        *rspDataLen = 8;
    } else {
        return GRDM_BUFFER_NOT_ENOUGH;
    }

    return result;
}

GRDM_RESULT grdmFWstartUpgrade(uint8_t* fw, uint32_t fw_len){
    GRDM_RESULT result = GRDM_NO_ERROR;
    unsigned int target_size = 0;
    unsigned int fw_start_addr = 0;

    result = grdm_FW_startUpgrade(fw, fw_len);
    if (result != 0){
        LOGE("grdm_FW_startUpgrade error %d", result);
    }

    return result;
}

GRDM_RESULT grdmFWcheckHeader(uint8_t* header, uint32_t headerLen, uint8_t* nextLen){
    GRDM_RESULT result = GRDM_NO_ERROR;
    uint32_t tempNextLen;

    result = grdm_FW_checkHeader(header, headerLen, &tempNextLen);
    if (result != 0){
        LOGE("grdm_FW_checkHeader error %d", result);
        return result;
    }
    memcpy(nextLen,&tempNextLen,4);

    return result;
}

 GRDM_RESULT grdmFwReadVersion(uint8_t* version, uint32_t* version_len){
     GRDM_RESULT result = GRDM_NO_ERROR;
     unsigned char partition;
     *version_len = GRDM_IMAGE_HEADER_SIZE;
 
     result = grdm_FW_readVersion(version, version_len, &partition);
     if (result != 0){
         LOGE("grdmFwReadVersion error %d", result);
         return result;
     }
     hex_print_tag_debug("[ grdm_FW_readVersion ] version :: ", version, *version_len);
     LOGD("partition : %02X", partition);
 
     return result;
 }

GRDM_RESULT grdmFWupdateUpgrade(uint8_t* fw, uint32_t fw_len){
    GRDM_RESULT result = GRDM_NO_ERROR;

    result = grdm_FW_updateUpgrade(fw, fw_len);
    if (result != 0){
        LOGE("grdm_FW_updateUpgrade error %d", result);
        return result;
    }

    return result;
}

GRDM_RESULT grdmFWfinishUpgrade(uint8_t* version, uint32_t* version_len){
    GRDM_RESULT result = GRDM_NO_ERROR;

    result = grdm_FW_finishUpgrade(version, version_len);
    if (result != 0){
        LOGE("grdm_FW_updateUpgrade error %d", result);
        return result;
    }
    hex_print_tag_debug("[ grdmFWfinishUpgrade ] version :: ", version, *version_len);

    return result;
}

GRDM_RESULT grdmFDCCheckStatus(uint8_t* data, uint32_t* dataLen) {
    GRDM_RESULT result = GRDM_NO_ERROR;
    secGrdm_7816_rpdu_t rsp = {0,};
    rsp.pdata = data;
    uint8_t apdu[5] = {0x80, 0x88, 0x03, 0x00, 0x02};
    result = secGrdmAPDUTransmit(apdu, 5, &rsp);
    if (result != GRDM_NO_ERROR){
        LOGE("grdmFDCCheckStatus error %d", result);
 		return result;
    }
    if (*dataLen >= rsp.len){
    	memcpy(data, rsp.pdata, rsp.len);
    	*dataLen = rsp.len;
    	hex_print_tag_debug("grdmFDCCheckStatus :: ", data, *dataLen);
    } else {
        return GRDM_BUFFER_NOT_ENOUGH;
    }
 
    return result;
}
 
GRDM_RESULT grdmFDCCheckRestriction(uint8_t* data, uint32_t* dataLen) {
    GRDM_RESULT result = GRDM_NO_ERROR;
    secGrdm_7816_rpdu_t rsp = {0,};
    rsp.pdata = data;
    uint8_t apdu[5] = {0x80, 0x88, 0x04, 0x00, 0x02}; 
    result = secGrdmAPDUTransmit(apdu, 5, &rsp);
    if (result != GRDM_NO_ERROR){
        LOGE("grdmFDCCheckRestriction error %d", result);
 		return result;
    }
    if (*dataLen >= rsp.len){
		memcpy(data, rsp.pdata, rsp.len);
     	*dataLen = rsp.len;
     	hex_print_tag_debug("grdmFDCCheckRestriction :: ", data, *dataLen);
    } else {
        return GRDM_BUFFER_NOT_ENOUGH;
    }
 
    return result;
 }
 
GRDM_RESULT grdmFDCCheckLog(uint8_t* data, uint32_t* dataLen, uint8_t p1) {
    GRDM_RESULT result = GRDM_NO_ERROR;
    secGrdm_7816_rpdu_t rsp = {0,};
    rsp.pdata = data;
    uint8_t* apdu;
 	
    if (p1 != 0x00 && p1 != 0x01) {
        return GRDM_INVALID_INDEX;
    }
    apdu = tz_malloc(4);
    if (apdu == NULL) {
        return GRDM_UNKNOWN_ERROR;
    }
    apdu[0] = 0x80;
    apdu[1] = 0xF6;
    apdu[2] = p1;
    apdu[4] = 0x00;
 	
    result = secGrdmAPDUTransmit(apdu, 4, &rsp);
    if (result != GRDM_NO_ERROR){
        LOGE("grdmFDCCheckFDCLog error %d", result);
 		return result;
    }
    if (*dataLen >= rsp.len){
    	memcpy(data, rsp.pdata, rsp.len);
     	*dataLen = rsp.len;
     	hex_print_tag_debug("grdmFDCCheckLog :: ",  data, *dataLen);
    } else {
        return GRDM_BUFFER_NOT_ENOUGH;
    }
 
    return result;
}
 

#ifdef DEBUG_LOW
static uint8_t credential[4][MAX_CREDENTIAL_SIZE] = {
    {0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F}, 
    {0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x81}, 
    {0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x82}, 
    {0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x83}};

GRDM_RESULT grdmFDCAttack(uint8_t* data, uint32_t* dataLen, uint8_t condition) {
    GRDM_RESULT result = GRDM_NO_ERROR;
    secGrdm_7816_rpdu_t rsp = {0,};
    rsp.pdata = data;
    uint8_t apdu[4] = {0x80, 0xFD, 0x00, 0x00}; 
    if(condition == 0xFF){
        LOGE("grdmFDCAttack, goto restricted mode!");
    } else if(condition < 0 || condition > 4){
        LOGE("grdmFDCAttack, wrong condition: %d", condition);
        return GRDM_INVALID_INDEX;
    }
    apdu[3] = condition;
    /*0x00 : Voltage
    0x01 : Temperature
    0x02 : Glitch
    0x03 : Active Shield
    0x04 : Lazor Detection
    0xFF : goto restricted mode*/
    
    result = secGrdmAPDUTransmit(apdu, 4, &rsp);
    if (result != GRDM_NO_ERROR){
        LOGE("grdmFDCAttack error %d", result);
	    return result;
    }
    if (*dataLen >= rsp.len){
    	memcpy(data, rsp.pdata, rsp.len);
    	*dataLen = rsp.len;
    	hex_print_tag_debug("grdmFDCAttack :: ", data, *dataLen);
    } else {
        return GRDM_BUFFER_NOT_ENOUGH;
    }

    return result;
}

GRDM_RESULT grdmDeleteCredential(uint8_t* domainKey, uint32_t domainKeyLen) {
    GRDM_RESULT result = GRDM_NO_ERROR;
    uint8_t domain_index = 0x11;

    uint8_t unwrappedDomainKey[GRDM_DOMAIN_KEYBLOB_BUFFER_SIZE];
    uint32_t unwrappedDomainKeyLen = GRDM_DOMAIN_KEYBLOB_BUFFER_SIZE;

    int i = 0;

    // unwrap admin key blob

#ifdef USE_TRUSTY_UNISOC
    result = unwrapInternalData("domain_authkey", unwrappedDomainKey, &unwrappedDomainKeyLen);
#else
    result = unwrapInternalData(domainKey, domainKeyLen, unwrappedDomainKey, &unwrappedDomainKeyLen);
#endif
    if (result != 0){
        LOGE("grdmDeleteCredential unwrapInternalData fail !!");
        return result;
    }
    hex_print_tag_debug("[ grdmDeleteCredential ] unwrappedDomainKey :: ", unwrappedDomainKey, unwrappedDomainKeyLen);

    for(i = 0; i < 4; i++){
        result = grdm_deleteCredential(domain_index, unwrappedDomainKey, unwrappedDomainKeyLen, i);
        if (result != 0){
            LOGE("grdm_deleteCredential error %d", result);
            return result;
        }
    }
    return result;
}

GRDM_RESULT grdmStoreCredential(uint8_t* domainKey, uint32_t domainKeyLen) {
    GRDM_RESULT result = GRDM_NO_ERROR;
    uint8_t domain_index = 0x11;

    uint8_t unwrappedDomainKey[GRDM_DOMAIN_KEYBLOB_BUFFER_SIZE];
    uint32_t unwrappedDomainKeyLen = GRDM_DOMAIN_KEYBLOB_BUFFER_SIZE;

    int i = 0;

    // unwrap admin key blob
#ifdef USE_TRUSTY_UNISOC
    result = unwrapInternalData("domain_authkey", unwrappedDomainKey, &unwrappedDomainKeyLen);
#else
    result = unwrapInternalData(domainKey, domainKeyLen, unwrappedDomainKey, &unwrappedDomainKeyLen);
#endif
    if (result != 0){
        LOGE("grdmStoreCredential unwrapInternalData fail !!");
        return RET_ERR_DATA_UNWRAPPING_FAIL;
    }
    hex_print_tag_debug("[ grdmStoreCredential ] unwrappedDomainKey :: ", unwrappedDomainKey, unwrappedDomainKeyLen);
	
    for(i = 0; i < 4; i++){
        result = grdm_storeCredential(domain_index, unwrappedDomainKey, unwrappedDomainKeyLen, i, credential[i], MAX_CREDENTIAL_SIZE);
        if (result != 0){
            LOGE("grdmStoreCredential error %d", result);
            return result;
        }
    }

    return result;
}

GRDM_RESULT grdmGetCredential(uint8_t* domainKey, uint32_t domainKeyLen) {
    GRDM_RESULT result = GRDM_NO_ERROR;
    uint8_t domain_index = 0x11;
    int32_t verify[4] = {-1000, };

    uint8_t unwrappedDomainKey[GRDM_DOMAIN_KEYBLOB_BUFFER_SIZE];
    uint32_t unwrappedDomainKeyLen = GRDM_DOMAIN_KEYBLOB_BUFFER_SIZE;

    int i = 0;

    uint8_t dataBuf[MAX_CREDENTIAL_SIZE];
    uint32_t dataBufLen = MAX_CREDENTIAL_SIZE;

    // unwrap admin key blob
#ifdef USE_TRUSTY_UNISOC
    result = unwrapInternalData("domain_authkey", unwrappedDomainKey, &unwrappedDomainKeyLen);
#else
    result = unwrapInternalData(domainKey, domainKeyLen, unwrappedDomainKey, &unwrappedDomainKeyLen);
#endif
    if (result != 0){
        LOGE("grdmGetCredential unwrapInternalData fail !!");
        return RET_ERR_DATA_UNWRAPPING_FAIL;
    }
    hex_print_tag_debug("[ grdmGetCredential ] unwrappedDomainKey :: ", unwrappedDomainKey, unwrappedDomainKeyLen);

    for(i = 0; i < 4; i++){
        result = grdm_getCredential(domain_index, unwrappedDomainKey, unwrappedDomainKeyLen, i,dataBuf, &dataBufLen);
        if (result != 0){
            LOGE("grdmGetCredential credential%d error %d", i, result);
            return result;
        }
        LOGD("grdmGetCredential credential%d success", i);
        hex_print_tag_debug("[ grdmGetCredential ] credential :: ", dataBuf, dataBufLen);
        verify[i] = memcmp(credential[i], dataBuf, dataBufLen);
    }

    if(verify[0] != 0 || verify[1] != 0 || verify[2] != 0 || verify[3] != 0){
        LOGE("grdmGetCredential failed to credential verification :: %d, %d, %d, %d", verify[0], verify[1], verify[2], verify[3]);
    	return GRDM_INVALID_DATA;
    }
    return result;
}

#ifdef GRDM_TEST
GRDM_RESULT grdmBlPutkey() {
    GRDM_RESULT result = GRDM_NO_ERROR;
    uint8_t tempKey[32] = {0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
                           0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F};
    uint8_t tempKeyLen = 32;
    uint8_t tempHostId[16] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
                              0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
    uint8_t tempHostIdLen = 16;

    uint8_t rot[48] = {0x00,0x00,0x00,0x00,
                       0x00,0x00,0x00,0x00,
                       0x00,0x00,0x00,0x11,
                       0x00,0x00,0x00,0x22,
                       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33};
    uint8_t status[48] = {0x01,0x00,0x00,0x00,
                       0x00,0x00,0x00,0x00,
                       0x00,0x00,0x00,0x11,
                       0x00,0x00,0x00,0x22,
                       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33};
    uint8_t kg[48] = {0x02,0x00,0x00,0x00,
                       0x00,0x00,0x00,0x00,
                       0x00,0x00,0x00,0x11,
                       0x00,0x00,0x00,0x22,
                       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33};

    result = grdm_BL_putkey(tempKey,tempKeyLen,tempHostId,tempHostIdLen);
    if (result != 0)
        LOGE("grdm_BL_putkey error %d", result);

    result = grdm_BL_storeCredential(tempKey, tempKeyLen, 1, rot, 48);
    if (result != 0)
        LOGE("grdm_BL_storeCredential rot error %d", result);
	result = grdm_BL_storeCredential(tempKey, tempKeyLen, 0, status, 48);
    if (result != 0)
        LOGE("grdm_BL_storeCredential status error %d", result);
	result = grdm_BL_storeCredential(tempKey, tempKeyLen, 2, kg, 48);
    if (result != 0)
        LOGE("grdm_BL_storeCredential kg error %d", result);
    return result;
}
#endif

GRDM_RESULT grdmKMPutkey() {
    GRDM_RESULT result = GRDM_NO_ERROR;
    uint8_t tempKey[32] = {0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
                           0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x81};
    uint8_t tempKeyLen = 32;

    result = grdm_KM_putKey(tempKey,tempKeyLen);
    if (result != 0)
        LOGE("grdm_KM_putKey error %d", result);

    return result;
}

GRDM_RESULT grdmICCCPutkey() {
    GRDM_RESULT result = GRDM_NO_ERROR;
    uint8_t tempKey[32] = {0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
                           0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x82};
    uint8_t tempKeyLen = 32;

    result = grdm_ICCC_putKey(tempKey,tempKeyLen);
    if (result != 0)
        LOGE("grdmICCCPutkey error %d", result);

    return result;
}

GRDM_RESULT grdmGetBLCredential() {
    GRDM_RESULT result = GRDM_NO_ERROR;
    uint8_t KMKey[32] = {0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
                           0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x81};
    uint8_t ICCCKey[32] = {0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
                           0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x82};
    uint32_t keyLen = 32;
    uint8_t data[256] = {0,};
    uint32_t dataLen = 256;
	
    result = grdm_getBLCredential(ACCESSOR_KM, KMKey, keyLen, BL_CREDENTIAL_STATUS, data, &dataLen);
    if (result != 0)
        LOGE("grdm_getBLCredential ACCESSOR_KM BL_CREDENTIAL_STATUS error %d", result);
	hex_print_tag_debug("[ grdm_getBLCredential ] ACCESSOR_KM BL_CREDENTIAL_STATUS :: ", data, dataLen);
	memset(data,0,256);
	
	result = grdm_getBLCredential(ACCESSOR_KM, KMKey, keyLen, BL_CREDENTIAL_ROT, data, &dataLen);
    if (result != 0)
        LOGE("grdm_getBLCredential ACCESSOR_KM BL_CREDENTIAL_ROT error %d", result);
	hex_print_tag_debug("[ grdm_getBLCredential ] ACCESSOR_KM BL_CREDENTIAL_ROT :: ", data, dataLen);
	memset(data,0,256);
	
	result = grdm_getBLCredential(ACCESSOR_KM, KMKey, keyLen, BL_CREDENTIAL_KG, data, &dataLen);
    if (result != 0)
        LOGE("grdm_getBLCredential ACCESSOR_KM BL_CREDENTIAL_KG error %d", result);
	hex_print_tag_debug("[ grdm_getBLCredential ] ACCESSOR_KM BL_CREDENTIAL_KG :: ", data, dataLen);
	memset(data,0,256);
	
	result = grdm_getBLCredential(ACCESSOR_KM, KMKey, keyLen, BL_CREDENTIAL_EM_CORE, data, &dataLen);
    if (result != 0)
        LOGE("grdm_getBLCredential ACCESSOR_KM BL_CREDENTIAL_EM_CORE error %d", result);
	hex_print_tag_debug("[ grdm_getBLCredential ] ACCESSOR_KM BL_CREDENTIAL_EM_CORE :: ", data, dataLen);
	memset(data,0,256);


	result = grdm_getBLCredential(ACCESSOR_ICCC, ICCCKey, keyLen, BL_CREDENTIAL_STATUS, data, &dataLen);
    if (result != 0)
        LOGE("grdm_getBLCredential ACCESSOR_ICCC BL_CREDENTIAL_STATUS error %d", result);
	hex_print_tag_debug("[ grdm_getBLCredential ] ACCESSOR_ICCC BL_CREDENTIAL_STATUS :: ", data, dataLen);
	memset(data,0,256);

	result = grdm_getBLCredential(ACCESSOR_ICCC, ICCCKey, keyLen, BL_CREDENTIAL_ROT, data, &dataLen);
    if (result != 0)
        LOGE("grdm_getBLCredential ACCESSOR_ICCC BL_CREDENTIAL_ROT error %d", result);
	hex_print_tag_debug("[ grdm_getBLCredential ] ACCESSOR_ICCC BL_CREDENTIAL_ROT :: ", data, dataLen);
	memset(data,0,256);

	result = grdm_getBLCredential(ACCESSOR_ICCC, ICCCKey, keyLen, BL_CREDENTIAL_KG, data, &dataLen);
    if (result != 0)
        LOGE("grdm_getBLCredential ACCESSOR_ICCC BL_CREDENTIAL_KG error %d", result);
	hex_print_tag_debug("[ grdm_getBLCredential ] ACCESSOR_ICCC BL_CREDENTIAL_KG :: ", data, dataLen);
	memset(data,0,256);

	result = grdm_getBLCredential(ACCESSOR_ICCC, ICCCKey, keyLen, BL_CREDENTIAL_EM_CORE, data, &dataLen);
    if (result != 0)
        LOGE("grdm_getBLCredential ACCESSOR_ICCC BL_CREDENTIAL_EM_CORE error %d", result);
	hex_print_tag_debug("[ grdm_getBLCredential ] ACCESSOR_ICCC BL_CREDENTIAL_EM_CORE :: ", data, dataLen);
	memset(data,0,256);

    return result;
}

GRDM_RESULT grdmGetDeviceAttestation(uint8_t* inputData, uint32_t inputDataLen, uint8_t* signedData, uint32_t* signedDataLen){
    GRDM_RESULT result = GRDM_NO_ERROR;
    uint8_t attestation_type;
    uint8_t time[13];
    uint8_t* challenge;
    uint32_t challenge_len;
    uint8_t temp[3072] = {0,};
    uint32_t tempLen = 3072;

    challenge_len = inputDataLen - ATTESTATION_TYPE_SIZE - ATTESTATION_TIME_SIZE;
    challenge = tz_malloc(challenge_len);

    attestation_type = inputData[0];
    memcpy(time, inputData+ATTESTATION_TYPE_SIZE, ATTESTATION_TIME_SIZE);
    memcpy(challenge, inputData+ATTESTATION_TYPE_SIZE+ATTESTATION_TIME_SIZE, challenge_len);

    LOGD("attestation_type = %d", attestation_type);
    hex_print_tag_debug("[ grdmGetDeviceAttestation ] time :: ", time, ATTESTATION_TIME_SIZE);
    hex_print_tag_debug("[ grdmGetDeviceAttestation ] challenge :: ", challenge, challenge_len);

    result = grdm_getDeviceAttestation(attestation_type, challenge, challenge_len, time, ATTESTATION_TIME_SIZE, temp, &tempLen);
    if (result != 0){
        LOGE("grdm_getDeviceAttestation error %d", result);
        goto error;
    }
    if (*signedDataLen >= tempLen){
        hex_print_tag_debug("[ grdm_getDeviceAttestation ] signedData :: ", temp, tempLen);
        memcpy(signedData,temp,tempLen);
        *signedDataLen = tempLen;
    } else {
        return GRDM_BUFFER_NOT_ENOUGH;
    }

error :
    tz_free(challenge);
    return result;
}

GRDM_RESULT grdmFactoryReset(){
	GRDM_RESULT result = GRDM_NO_ERROR;
	uint8_t apdu[4] = {0x80,0x7F,0x00,0x00};
	uint8_t data[256] = {0,};
	secGrdm_7816_rpdu_t rsp = {0,};
	rsp.pdata = data;
	
	result = secGrdmAPDUTransmit(apdu, 4, &rsp);
	if (result != 0)
        LOGE("grdmFactoryReset error %d", result);
    return result;
}

GRDM_RESULT grdmAdminKeyReset(){
	GRDM_RESULT result = GRDM_NO_ERROR;
	uint8_t apdu[5] = {0x80,0x88,0x02,0x00,0x01};
	uint8_t data[256] = {0,};
	secGrdm_7816_rpdu_t rsp = {0,};
	rsp.pdata = data;
	
	result = secGrdmAPDUTransmit(apdu, 5, &rsp);
	if (result != 0)
        LOGE("grdmAdminKeyReset error %d", result);
    return result;
}

#ifdef GRDM_TEST
GRDM_RESULT grdmResetStatus(void) {
    return grdm_ResetStatus();
}
#endif

#endif /* DEBUG_LOW */
#endif /* USE_GRDM */
