
#include <qsee_sfs.h>
#include "ta_logger.h"

#include "tzWrappers/TzwMemory.h"

/*#include "tee_internal_api.h"*/

#include "ifaa_ta_common.h"
#include "ifaa_tee_common.h"
/*#include "ifaa_log_utils.h"*/
#include "ifaa_mem_utils.h"
#include "gp_oem.h"
#include "memory.h"
#include "ifaa_km.h"

#define __LEN_RSA2048           256

IFAA_Result IFAA_GetFpLastIdentifiedResult(uint8_t *buf_id, uint32_t *id_len) {

    int32_t lst_id = 0;
    if (TEE_GetFpLastIdentifiedResult(&lst_id) != TEE_SUCCESS) {
        LOG_E("fail to get last identified id.");
        return IFAA_ERR_GET_LAST_IDENTIFIED_RESULT;
    }

    *id_len = sizeof(lst_id);
    write32(buf_id, lst_id);

    return IFAA_ERR_SUCCESS;
}

IFAA_Result IFAA_GetFpAuthenticatorVersion(uint32_t *version) {
    *version = TEE_GetAuthenticatorVersion();
    return IFAA_ERR_SUCCESS;
}

IFAA_Result IFAA_GetFpEnrolledIdList(uint8_t* buf_id_list, uint32_t* id_list_len)
{
    if (buf_id_list == NULL || id_list_len == NULL) {
		LOG_E("get enrolled id list: bad params!");
		return IFAA_ERR_BAD_PARAM;
    }
    if(TEE_GetFpList(buf_id_list, id_list_len) == TEE_SUCCESS) {
		LOG_D("get fp list len = %d", *id_list_len);
#ifdef __DEV_DEBUG__
		logByteArrayHex(buf_id_list, *id_list_len, "enrolled bio id list");
#endif
		return IFAA_ERR_SUCCESS;
    } else {
		return IFAA_ERR_GET_FILE_LIST_FAILED;
    }
}

IFAABoolean IFAA_FpIdCompare(const uint8_t *buf_l, const uint8_t *buf_r, uint32_t buf_len) {
    return memcmp(buf_l, buf_r, buf_len) == 0 ? IFAA_YES : IFAA_NO;
}


static void *IFAA_funaddress_t[8][4];

IFAA_Result IFAA_TaAddEntry(IFAA_BioType bioType, IFAA_TaEntry entry, void *func) {
    IFAA_Result result = IFAA_ERR_SUCCESS;
    if (func == NULL || (bioType <= 0 || bioType >= 255) || (entry <= 0 || entry >= 5)) {
        LOG_E("Invalid parameters");
        return IFAA_ERR_BAD_PARAM;
    }

    switch (bioType) {
        case IFAA_BIO_FINGERPRINT:
            IFAA_funaddress_t[0][entry - 1] = func;
            break;
        case IFAA_BIO_IRIS:
            IFAA_funaddress_t[1][entry - 1] = func;
            break;
        case IFAA_BIO_FACE:
            IFAA_funaddress_t[2][entry - 1] = func;
            break;
        default:
            result = IFAA_ERR_UNKNOWN_CMD;
            break;
    }

    return result;
}

IFAA_Result IFAA_TaGetEntry(IFAA_BioType bioType, IFAA_TaEntry entry, void **func) {
    if ((bioType <= 0) || (bioType >= 255)
        || (entry <= 0) || (entry >= 5)
        || (IFAA_funaddress_t[bioType - 1][entry - 1] == 0)) {
        LOG_E("Invalid parameters");
        return IFAA_ERR_BAD_PARAM;
    }

    *func = IFAA_funaddress_t[bioType - 1][entry - 1];

    return IFAA_ERR_SUCCESS;
}


#ifdef TZ_MODEL_QCOM

IFAA_Result
IFAA_Sha256(const uint8_t *msg, uint32_t msg_len, uint8_t *digest, uint32_t *digest_len) {

    *digest_len = 32;
    int ret = qsee_hash(QSEE_HASH_SHA256, msg, msg_len, digest, *digest_len);
    if (ret < 0) {
        LOG_E("qsee_hash failed with ret = %d", ret);
        return IFAA_ERR_HASH;
    }

    return IFAA_ERR_SUCCESS;
}

#else
IFAA_Result IFAA_Sha256(const uint8_t *msg, uint32_t msg_len, uint8_t *digest, uint32_t *digest_len)
{

    TEE_OperationHandle digestop = NULL;
    TEE_Result ret = TEE_AllocateOperation(&digestop, TEE_ALG_SHA256, TEE_MODE_DIGEST, 0);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_AllocateOperation failed: %08x", ret);
        return IFAA_ERR_HASH;
    }

    TEE_DigestUpdate(digestop, msg, msg_len);
    ret = TEE_DigestDoFinal(digestop, NULL, 0, digest, digest_len);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_DigestDoFinal failed: %08x", ret);
        ret = IFAA_ERR_HASH;
    }

    tzwFreeOperation(digestop);
    if (TEE_SUCCESS == ret) ret = IFAA_ERR_SUCCESS;

    return ret;
}
#endif


#ifdef TZ_MODEL_QCOM

/* Endianness converter Macro */
#define htonl(x)                            \
    (((((uint32_t)(x)&0x000000FFU) << 24) | \
      (((uint32_t)(x)&0x0000FF00U) << 8) |  \
      (((uint32_t)(x)&0x00FF0000U) >> 8) |  \
      (((uint32_t)(x)&0xFF000000U) >> 24)))

int copy_S_BIGINT(uint8_t *dst, uint32_t dst_len, QSEE_S_BIGINT *src, uint32_t max_len) {
    uint32_t i = 0;
    uint32_t array_length = 0;
    if (dst_len > max_len) {
        return -1;
    }

    array_length = dst_len / 4;
    for (i = 0; i < array_length; i++) {
        *((uint32_t *) dst + i) = htonl(src->bi.a[array_length - 1 - i]);
    }

    return 0;
}

static bool init_key(QSEE_S_BIGINT **key, uint8_t *buf, uint32_t size) {
    *key = (QSEE_S_BIGINT *) tzwMalloc(sizeof(QSEE_S_BIGINT));
    if (*key != NULL) {
        qsee_BIGINT_read_unsigned_bin(&(*key)->bi, buf, size);
        return true;
    }
    return false;
}

IFAA_Result IFAA_RsaSignDigest(const IFAA_RsaKey *key,
                               const uint8_t *digest, uint32_t digest_len,
                               uint8_t *sig, uint32_t *sig_len) {

    IFAA_Result ret = IFAA_ERR_SUCCESS;
    QSEE_RSA_KEY rsa_key = {0};
    
    if(init_key(&(rsa_key.N), key->n.buf, key->n.len)
            && init_key(&(rsa_key.d), key->d.buf, key->d.len)
            && init_key(&(rsa_key.e), key->e.buf, key->e.len)) {
        
        rsa_key.type = QSEE_RSA_KEY_PRIVATE;
        int status = qsee_rsa_sign_hash(&rsa_key, QSEE_RSA_PAD_PKCS1_V1_5_SIG, NULL,
                                        QSEE_HASH_IDX_SHA256, digest, digest_len,
                                        sig, (int *) sig_len);
        if (status != 0) {
            LOG_E("qsee_rsa_sign_hash failed, status = %d", status);
            ret = IFAA_ERR_SIGN;
        }
    } else {
        LOG_E("qsee_rsa_sign_hash failed"); 
        ret = IFAA_ERR_SIGN;
    }

    if (rsa_key.N != NULL) tzwFree(rsa_key.N);
    if (rsa_key.d != NULL) tzwFree(rsa_key.d);
    if (rsa_key.e != NULL) tzwFree(rsa_key.e);

    return ret;
}

#else

#define RSA2048_LEN 256
IFAA_Result IFAA_RsaSignDigest(const IFAA_RsaKey* key,
                               const uint8_t* digest, uint32_t digest_len,
                               uint8_t* sig, uint32_t* sig_len)
{
    
    TEE_Attribute attr[3];
    TEE_ObjectHandle keyobj = NULL;
    TEE_OperationHandle signop = NULL;
    TEE_Result ret = TEE_AllocateTransientObject(TEE_TYPE_RSA_KEYPAIR, 8 * RSA2048_LEN, &keyobj);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_AllocateTransientObject failed: %08x", ret);
        return IFAA_ERR_OUT_OF_MEM;
    }
    
    TEE_InitRefAttribute(&attr[0], TEE_ATTR_RSA_MODULUS, key->n.buf,
                         key->n.len);
    TEE_InitRefAttribute(&attr[1], TEE_ATTR_RSA_PUBLIC_EXPONENT, key->e.buf,
                         key->e.len);
    TEE_InitRefAttribute(&attr[2], TEE_ATTR_RSA_PRIVATE_EXPONENT, key->d.buf,
                         key->d.len);
    ret = TEE_PopulateTransientObject(keyobj, attr, 3);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_PopulateTransientObject failed: %08x", ret);
        ret = IFAA_ERR_UNKNOWN;
        goto bye;
    }
 
    ret = TEE_AllocateOperation(&signop, TEE_ALG_RSASSA_PKCS1_V1_5_SHA256,
                                TEE_MODE_SIGN, 8 * RSA2048_LEN);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_AllocateOperation failed: %08x", ret);
        ret = IFAA_ERR_UNKNOWN;
        goto bye;
    }

    TEE_SetOperationKey(signop, keyobj);
    ret = TEE_AsymmetricSignDigest(signop, NULL, 0, digest, digest_len, sig, sig_len);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_AsymmetricSignDigest failed: %08x", ret);
        ret = IFAA_ERR_UNKNOWN;
        goto bye;
    }

bye:
    if (keyobj != NULL) {
        tzwFreeTransientObject(keyobj);
        keyobj = NULL;
    }

    if (signop != NULL) {
        tzwFreeOperation(signop);
        signop = NULL;
    }

    if (TEE_SUCCESS == ret) ret = IFAA_ERR_SUCCESS;
    return ret;
}
#endif

#ifdef TZ_MODEL_QCOM

IFAA_Result IFAA_RsaVerifyDigest(const IFAA_RsaKey *key,
                                 const uint8_t *digest, uint32_t digest_len,
                                 const uint8_t *sig, uint32_t sig_len) {

    IFAA_Result ret = IFAA_ERR_SUCCESS;
    QSEE_RSA_KEY rsa_key = {0};
    
    if(init_key(&(rsa_key.N), key->n.buf, key->n.len)
            && init_key(&(rsa_key.d), key->d.buf, key->d.len)
            && init_key(&(rsa_key.e), key->e.buf, key->e.len)) {
        

        rsa_key.type = QSEE_RSA_KEY_PUBLIC;
        int status = qsee_rsa_verify_signature(&rsa_key, QSEE_RSA_PAD_PKCS1_V1_5_SIG, NULL,
                                               QSEE_HASH_IDX_SHA256,
                                               (uint8_t *) digest, digest_len, (uint8_t *) sig,
                                               sig_len);
        if (status != 0) {
            LOG_E("qsee_rsa_verify_signature failed, status = %d", status);
            ret = IFAA_ERR_VERIFY;
        }
    }
    else {
        LOG_E("verify digist: init_key failed.");
        ret = IFAA_ERR_VERIFY;
    }
    if (rsa_key.N != NULL) tzwFree(rsa_key.N);
    if (rsa_key.d != NULL) tzwFree(rsa_key.d);
    if (rsa_key.e != NULL) tzwFree(rsa_key.e);

    return ret;
}

#else
IFAA_Result IFAA_RsaVerifyDigest(const IFAA_RsaKey* key,
                                 const uint8_t* digest, uint32_t digest_len,
                                 const uint8_t* sig, uint32_t sig_len)
{
    TEE_Attribute       attr[2];
    TEE_ObjectHandle    keyobj = NULL;
    TEE_OperationHandle verifyop = NULL;
    TEE_Result          ret = TEE_AllocateTransientObject(TEE_TYPE_RSA_PUBLIC_KEY, 8 * RSA2048_LEN, &keyobj);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_AllocateTransientObject failed: %08x", ret);
        return IFAA_ERR_OUT_OF_MEM;
    }

    TEE_InitRefAttribute(&attr[0], TEE_ATTR_RSA_MODULUS, key->n.buf,
                         key->n.len);
    TEE_InitRefAttribute(&attr[1], TEE_ATTR_RSA_PUBLIC_EXPONENT, key->e.buf,
                         key->e.len);

    ret = TEE_PopulateTransientObject(keyobj, attr, 2);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_PopulateTransientObject failed: %08x", ret);
        ret = IFAA_ERR_UNKNOWN;
        goto bye;
    }

    ret = TEE_AllocateOperation(&verifyop, TEE_ALG_RSASSA_PKCS1_V1_5_SHA256, TEE_MODE_VERIFY, 8 * RSA2048_LEN);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_AllocateOperation failed: %08x", ret);
        ret = IFAA_ERR_UNKNOWN;
        goto bye;
    }

    TEE_SetOperationKey(verifyop, keyobj);
    ret = TEE_AsymmetricVerifyDigest(verifyop, NULL, 0, digest, digest_len, sig, sig_len);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_AsymmetricVerifyDigest failed: %08x", ret);
        ret = IFAA_ERR_UNKNOWN;
        goto bye;
    }
 
bye:
    if (keyobj != NULL) {
        tzwFreeTransientObject(keyobj);
        keyobj = NULL;
    }
    if (verifyop != NULL) {
        tzwFreeOperation(verifyop);
        verifyop = NULL;
    }

    if (TEE_SUCCESS == ret) ret = IFAA_ERR_SUCCESS;
    return ret;
}
#endif

#ifdef TZ_MODEL_QCOM

IFAA_Result IFAA_RsaKeyGenerate(IFAA_RsaBit bits, IFAA_RsaKey *key) {

    IFAA_Result ret = IFAA_ERR_SUCCESS;
    QSEE_RSA_KEY rsa_key = {0};
    uint8_t pub_exp[] = {0x00, 0x00, 0x01, 0x00, 0x01};

    do {
        if (qsee_util_init_s_bigint(&(rsa_key.e)) ||
            qsee_util_init_s_bigint(&(rsa_key.d)) ||
            qsee_util_init_s_bigint(&(rsa_key.N)) ||
            qsee_util_init_s_bigint(&(rsa_key.p)) ||
            qsee_util_init_s_bigint(&(rsa_key.q))) {
            LOG_E("qsee_util_init_s_bigint failed.\n");
            ret = IFAA_ERR_KEY_GEN;
            break;
        }

        rsa_key.type = QSEE_RSA_KEY_PRIVATE_PUBLIC;

        switch (bits) {
            case RSA_BITS_2048:
                bits = 2048;
                break;
            case RSA_BITS_4096:
                bits = 4096;
                break;
        }

        int status =
                qsee_rsa_key_gen(&rsa_key, bits / 8, pub_exp, sizeof(pub_exp));
        if (status != 0) {
            LOG_E("qsee_rsa_key_gen failed.\n");
            ret = IFAA_ERR_KEY_GEN;
            break;
        }

        if (copy_S_BIGINT(key->n.buf, QSEE_RSA_KEY_SIZE(rsa_key.N), rsa_key.N,
                          key->n.len) ||
            copy_S_BIGINT(key->d.buf, QSEE_RSA_KEY_SIZE(rsa_key.d), rsa_key.d,
                          key->d.len) ||
            copy_S_BIGINT(key->e.buf, QSEE_RSA_KEY_SIZE(rsa_key.e), rsa_key.e,
                          key->e.len)) {
            LOG_E("copy_S_BIGINT failed.\n");
            ret = IFAA_ERR_KEY_GEN;
            break;
        }

        key->n.len = QSEE_RSA_KEY_SIZE(rsa_key.N);
        key->d.len = QSEE_RSA_KEY_SIZE(rsa_key.d);
        key->e.len = QSEE_RSA_KEY_SIZE(rsa_key.e);
    } while (false);

    if (rsa_key.d != NULL) qsee_util_free_s_bigint(rsa_key.d);
    if (rsa_key.N != NULL) qsee_util_free_s_bigint(rsa_key.N);
    if (rsa_key.p != NULL) qsee_util_free_s_bigint(rsa_key.p);
    if (rsa_key.q != NULL) qsee_util_free_s_bigint(rsa_key.q);
    if (rsa_key.e != NULL) qsee_util_free_s_bigint(rsa_key.e);

    return ret;
}

#else
IFAA_Result IFAA_RsaKeyGenerate(IFAA_RsaBit bits, IFAA_RsaKey *key)
{

    TEE_ObjectHandle    keyobj = NULL;
    TEE_Result          ret;
    uint32_t            rsa_key_bits = 0;

    switch(bits)
    {
        case RSA_BITS_2048:
            rsa_key_bits = 2048;
            break;
        case RSA_BITS_4096:
            rsa_key_bits = 4096;
            break;
        default:
            return IFAA_ERR_BAD_PARAM;
    }

    ret = TEE_AllocateTransientObject(TEE_TYPE_RSA_KEYPAIR, rsa_key_bits, &keyobj);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_AllocateTransientObject failed: %08x", ret);
        return IFAA_ERR_UNKNOWN;
    }

    ret = TEE_GenerateKey(keyobj, rsa_key_bits, NULL, 0);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_GenerateKey failed: %08x", ret);
        ret = IFAA_ERR_UNKNOWN;
        goto bye;
    }

    ret = TEE_GetObjectBufferAttribute(keyobj, TEE_ATTR_RSA_MODULUS, key->n.buf, &key->n.len);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_GetObjectBufferAttribute for MODULUS failed: %08x", ret);
        ret = IFAA_ERR_UNKNOWN;
        goto bye;
    }

    ret = TEE_GetObjectBufferAttribute(keyobj, TEE_ATTR_RSA_PUBLIC_EXPONENT, key->e.buf, &key->e.len);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_GetObjectBufferAttribute for PUBLIC EXPONENT failed: %08x", ret);
        ret = IFAA_ERR_UNKNOWN;
        goto bye;
    }

    ret = TEE_GetObjectBufferAttribute(keyobj, TEE_ATTR_RSA_PRIVATE_EXPONENT, key->d.buf, &key->d.len);
    if (ret != IFAA_ERR_SUCCESS) {
        LOG_E("TEE_GetObjectBufferAttribute for PRIVATE EXPONENT failed: %08x", ret);
        ret = IFAA_ERR_UNKNOWN;
        goto bye;
    }
    
bye:
    if (keyobj != NULL) {
        tzwFreeTransientObject(keyobj);
        keyobj = NULL;
    }
    
    if (TEE_SUCCESS == ret) ret = IFAA_ERR_SUCCESS;
    return ret;
}
#endif

#ifdef TZ_MODEL_QCOM
//Domain parameters for prime256v1
char m[] = "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff";

char a[] = "ffffffff00000001000000000000000000000000fffffffffffffffffffffffc";

char b[] = "5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b";

char G_x[] = "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296";

char G_y[] = "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5";

char n[] = "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551";

IFAA_Result IFAA_EccKeyGenerate(IFAA_EccKey* key)
{


    IFAA_Result                     ret = IFAA_ERR_SUCCESS;
    QSEE_qrlbn_ecc_domain_t         domain;
    QSEE_qrlbn_ecc_affine_point_t   publicKey;
    QSEE_qrlbn_ecc_bigval_t         privateKey;
    uint8_t                         privateKeyBin[32];
    uint8_t                         publicKeyXBin[32];
    uint8_t                         publicKeyYBin[32];

    do {

        /* Initialise the domain parameters for the Brainpool prime256v1 curve */
        ret = qsee_SW_GENERIC_ECC_init(&domain, m, a, b, G_x, G_y, n, 1);
        if (ret) {
            LOG_E("qsee_SW_GENERIC_ECC_init API failed.\n");
            ret = IFAA_ERR_KEY_GEN;
            break;
        }

        /* Generate a public/private key pair */
        ret = qsee_SW_GENERIC_ECC_keypair_generate(&privateKey, &publicKey, &domain);
        if (ret) {
            LOG_E("qsee_SW_GENERIC_ECC_keypair_generate API failed to generate pair.\n");
            ret = IFAA_ERR_KEY_GEN;
            break;
        }

        ret = qsee_SW_GENERIC_ECC_bigval_to_binary(&privateKeyBin[0],
                                               sizeof(privateKeyBin),
                                               &privateKey,
                                               &domain,
                                               QSEE_qrlbn_tag_privkey);
        if (ret) {
            LOG_E("qsee_SW_GENERIC_ECC_bigval_to_binary API failed on key p");
            ret = IFAA_ERR_KEY_GEN;
            break;
        }

        /* Convert the public key X coordinate to a binary form */
        ret = qsee_SW_GENERIC_ECC_bigval_to_binary(&publicKeyXBin[0],
                                           sizeof(publicKeyXBin),
                                           &publicKey.x,
                                           &domain,
                                           QSEE_qrlbn_tag_X);
        if (ret) {
            LOG_E("qsee_SW_GENERIC_ECC_bigval_to_binary API failed on key x");
            ret = IFAA_ERR_KEY_GEN;
            break;
        }

        /* Convert the public key Y coordinate to a binary form */
        ret = qsee_SW_GENERIC_ECC_bigval_to_binary(&publicKeyYBin[0],
                                           sizeof(publicKeyYBin),
                                           &publicKey.y,
                                           &domain,
                                           QSEE_qrlbn_tag_Y);
        if (ret) {
            LOG_E("qsee_SW_GENERIC_ECC_bigval_to_binary API failed on key y");
            ret = IFAA_ERR_KEY_GEN;
            break;
        }

        /* copy binary to IFAA_EccKey */
        memcpy(key->p.buf, (char*)privateKeyBin, 32);
        key->p.len = 32;
        memcpy(key->x.buf, (char*)publicKeyXBin, 32);
        key->x.len = 32;
        memcpy(key->y.buf, (char*)publicKeyYBin, 32);
        key->y.len = 32;
    } while (false);

    return ret;
}
#else
IFAA_Result IFAA_EccKeyGenerate(IFAA_EccKey* key)
{
	TEE_ObjectHandle    keyobj = NULL;
	uint32_t            keySize = 256; //Between 160 and 521 bits.
	uint32_t            attributeId = 0;
	TEE_Attribute       attr[1];

    TEE_Result ret = TEE_AllocateTransientObject(TEE_TYPE_ECDSA_KEYPAIR, keySize, &keyobj);
    if(ret != TEE_SUCCESS) {
	    LOG_E("TEE_AllocateTransientObject failed: %08x", ret);
	    return IFAA_ERR_UNKNOWN;
    }

    switch(keySize) {
	  case 256:
		  attributeId = TEE_ECC_CURVE_NIST_P256;
		  break;
	  default:
		  break;
    }
    TEE_InitValueAttribute(attr, TEE_ATTR_ECC_CURVE, attributeId, 0);

    ret = TEE_GenerateKey(keyobj, keySize, attr, 1);
    if (ret != TEE_SUCCESS) {
	    LOG_E("TEE_GenerateKey failed: %08x", ret);
	    ret = IFAA_ERR_UNKNOWN;
	    goto bye;
    }

    ret = TEE_GetObjectBufferAttribute(keyobj, TEE_ATTR_ECC_PUBLIC_VALUE_X, key->x.buf, &key->x.len);
    if (ret != TEE_SUCCESS) {
	    LOG_E("TEE_GetObjectBufferAttribute for PUBLIC X failed: %08x", ret);
	    ret = IFAA_ERR_UNKNOWN;
	    goto bye;
    }
    ret = TEE_GetObjectBufferAttribute(keyobj, TEE_ATTR_ECC_PUBLIC_VALUE_Y, key->y.buf, &key->y.len);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_GetObjectBufferAttribute for PUBLIC Y failed: %08x", ret);
        ret = IFAA_ERR_UNKNOWN;
        goto bye;
    }

    ret = TEE_GetObjectBufferAttribute(keyobj, TEE_ATTR_ECC_PRIVATE_VALUE, key->p.buf, &key->p.len);
    if (ret != IFAA_ERR_SUCCESS) {
        LOG_E("TEE_GetObjectBufferAttribute for ECC_PRIVATE  failed: %08x", ret);
        ret = IFAA_ERR_UNKNOWN;
        goto bye;
    }

  bye:
    if (keyobj != NULL) {
        TEE_FreeTransientObject(keyobj);
        keyobj = NULL;
    }

    if (TEE_SUCCESS == ret) ret = IFAA_ERR_SUCCESS;
    LOG_D("ECCKey generate result = 0x%04x", ret);
    return ret;
}
#endif

#define SHA1_HASH_LEN   20

#ifdef TZ_MODEL_QCOM
IFAA_Result IFAA_EccSignDigest(const IFAA_EccKey* key,
                               const uint8_t* digest, uint32_t digest_len,
                               uint8_t* sig, uint32_t* sig_len)
{
    ////////////////////////////////////////////////
    ////ECC秘钥签名在高通的参考实现
    ////////////////////////////////////////////////

    IFAA_Result              ret = IFAA_ERR_SUCCESS;
    QSEE_qrlbn_ecc_domain_t  domain;
    QSEE_qrlbn_ecc_bigval_t  private_key = {0};
    QSEE_qrlbn_ECDSA_sig_t   signature_data = {0};
    uint8_t                  sig_r_bin[32];
    uint8_t                  sig_s_bin[32];
    uint8_t*                 buf = sig;

    /* Initialise the domain parameters for the Brainpool 256r1 curve */
    ret = qsee_SW_GENERIC_ECC_init(&domain, m, a, b, G_x, G_y, n, 1);
    if (ret) {
        LOG_E("qsee_SW_GENERIC_ECC_init API failed.");
        ret = IFAA_ERR_SIGN;
        goto ret_handle;
    }

    //Convert private_key binary data to private.key.data Bigval
    ret = qsee_SW_GENERIC_ECC_binary_to_bigval(&private_key, key->p.buf, key->p.len,
                                                &domain, QSEE_qrlbn_tag_privkey);
    if (ret != 0) {
        LOG_E("qsee_SW_GENERIC_ECC_binary_to_bigval failed");
        ret = IFAA_ERR_SIGN;
        goto ret_handle;
    }

    // Sign
    ret = qsee_SW_GENERIC_ECDSA_sign((uint8_t *)digest, digest_len, &private_key, &signature_data, &domain);
    if (ret != 0) {
        LOG_E("qsee_SW_GENERIC_ECDSA_sign failed");
        ret = IFAA_ERR_SIGN;
        goto ret_handle;
    }

    //Convert signature_data.r Bigval to signature r binary data
    ret = qsee_SW_GENERIC_ECC_bigval_to_binary(sig_r_bin, 32, &signature_data.r, &domain, QSEE_qrlbn_tag_r);
    if (ret != 0) {
        LOG_E("qsee_SW_GENERIC_ECC_bigval_to_binary failed");
        ret = IFAA_ERR_SIGN;
        goto ret_handle;
    }

    //Convert signature_data.s Bigval to signature s binary data
    ret = qsee_SW_GENERIC_ECC_bigval_to_binary(sig_s_bin, 32, &signature_data.s, &domain, QSEE_qrlbn_tag_s);
    if (ret != 0) {
        LOG_E("qsee_SW_GENERIC_ECC_bigval_to_binary failed");
        ret = IFAA_ERR_SIGN;
        goto ret_handle;
    }

    memcpy(buf, sig_r_bin, 32);
    buf += 32;
    memcpy(buf, sig_s_bin, 32);
    *sig_len = 64;

ret_handle:

	return ret;
}
#else
IFAA_Result IFAA_EccSignDigest(const IFAA_EccKey* key,
                               const uint8_t* digest, uint32_t digest_len,
                               uint8_t* sig, uint32_t* sig_len)
{
	TEE_Attribute attr[4];
	TEE_ObjectHandle keyobj = NULL;
	TEE_OperationHandle signop = NULL;
	uint32_t keySize = 256;

	TEE_Result ret = TEE_AllocateTransientObject(TEE_TYPE_ECDSA_KEYPAIR, keySize, &keyobj);
	if (ret != TEE_SUCCESS) {
	  LOG_E("ecc sign digest TEE_AllocateTransientObject failed: %08x", ret);
	  return IFAA_ERR_OUT_OF_MEM;
	}

	TEE_InitRefAttribute(&attr[0], TEE_ATTR_ECC_PUBLIC_VALUE_X, key->x.buf,key->x.len);
	TEE_InitRefAttribute(&attr[1], TEE_ATTR_ECC_PUBLIC_VALUE_Y, key->y.buf,key->y.len);
	TEE_InitRefAttribute(&attr[2], TEE_ATTR_ECC_PRIVATE_VALUE, key->p.buf,key->p.len);
	TEE_InitValueAttribute(&attr[3], TEE_ATTR_ECC_CURVE, TEE_ECC_CURVE_NIST_P256, 0);

	LOG_D("EccSignDigest allocate transientObject ret: %08x", ret);
	ret = TEE_PopulateTransientObject(keyobj, attr, 4);
	if (ret != TEE_SUCCESS) {
	  LOG_E("TEE_PopulateTransientObject failed: %08x", ret);
	  ret = IFAA_ERR_UNKNOWN;
	  goto bye;
	}

	ret = TEE_AllocateOperation(&signop, TEE_ALG_ECDSA_P256, TEE_MODE_SIGN, keySize);
	if (ret != TEE_SUCCESS) {
	  LOG_E("TEE_AllocateOperation failed: %08x", ret);
	  ret = IFAA_ERR_UNKNOWN;
	  goto bye;
	}

	TEE_SetOperationKey(signop, keyobj);
	ret = TEE_AsymmetricSignDigest(signop, NULL, 0, digest, digest_len, sig, sig_len);
	if (ret != TEE_SUCCESS) {
	  LOG_E("TEE_AsymmetricSignDigest failed: %08x", ret);
	  ret = IFAA_ERR_UNKNOWN;
	  goto bye;
	}
	bye:
	if (keyobj != NULL) {
	  TEE_FreeTransientObject(keyobj);
	  keyobj = NULL;
	}

	if (signop != NULL) {
	  TEE_FreeOperation(signop);
	  signop = NULL;
	}

	if (TEE_SUCCESS == ret) ret = IFAA_ERR_SUCCESS;
	return ret;
}
#endif

#ifdef TZ_MODEL_QCOM
IFAA_Result IFAA_EccVerifyDigest(const IFAA_EccKey* key,
                                 const uint8_t* digest, uint32_t digest_len,
                                 const uint8_t* sig, uint32_t sig_len)
{

    IFAA_Result                    ret = IFAA_ERR_SUCCESS;
    QSEE_qrlbn_ECDSA_sig_t         signature_data;
    QSEE_qrlbn_ecc_affine_point_t  public_key = {0};
    QSEE_qrlbn_ecc_domain_t        domain;
    uint8_t                        *sig_r_bin = NULL;
    uint8_t                        *sig_s_bin = NULL;

    memset(&public_key, 0, sizeof(QSEE_qrlbn_ecc_affine_point_t));
    memset(&signature_data, 0, sizeof(QSEE_qrlbn_ECDSA_sig_t));

    /* Initialise the domain parameters for the Brainpool 256r1 curve */
    ret = qsee_SW_GENERIC_ECC_init(&domain, m, a, b, G_x, G_y, n, 1);
    if (ret) {
        LOG_E("qsee_SW_GENERIC_ECC_init API failed.\n");
        ret = IFAA_ERR_VERIFY;
        goto ret_handle;
    }

    //Convert x binary data to public_key.x Bigval
    ret = qsee_SW_GENERIC_ECC_binary_to_bigval(&public_key.x, key->x.buf, key->x.len,
                                                &domain, QSEE_qrlbn_tag_X);
    if (ret != 0) {
        LOG_E("qsee_SW_GENERIC_ECC_binary_to_bigval failed");
        ret = IFAA_ERR_VERIFY;
        goto ret_handle;
    }

    //Convert y binary data to public_key.y Bigval
    ret = qsee_SW_GENERIC_ECC_binary_to_bigval(&public_key.y, key->y.buf, key->y.len,
                                                &domain, QSEE_qrlbn_tag_Y);
    if (ret != 0) {
        LOG_E("qsee_SW_GENERIC_ECC_binary_to_bigval failed");
        ret = IFAA_ERR_VERIFY;
        goto ret_handle;
    }

    sig_r_bin = (uint8_t *)sig;
    sig_s_bin = (uint8_t *)sig + 32;

    //Convert r binary data to signature_data.r Bigval
    ret = qsee_SW_GENERIC_ECC_binary_to_bigval(&signature_data.r, sig_r_bin, 32, &domain, QSEE_qrlbn_tag_r);
    if (ret != 0) {
        LOG_E("qsee_SW_GENERIC_ECC_binary_to_bigval failed");
        ret = IFAA_ERR_VERIFY;
        goto ret_handle;
    }

    //Convert s binary data to signature_data.s Bigval
    ret = qsee_SW_GENERIC_ECC_binary_to_bigval(&signature_data.s, sig_s_bin, 32, &domain, QSEE_qrlbn_tag_s);
    if (ret != 0) {
        LOG_E("qsee_SW_GENERIC_ECC_binary_to_bigval failed");
        ret = IFAA_ERR_VERIFY;
        goto ret_handle;
    }

    ret = qsee_SW_GENERIC_ECDSA_verify((uint8_t *)digest, digest_len, &public_key, &signature_data, &domain);
    if (ret != 0) {
        LOG_E("qsee_SW_GENERIC_ECDSA_verify failed");
        ret = IFAA_ERR_VERIFY;
        goto ret_handle;
    }

ret_handle:

	return ret;
}

#else
IFAA_Result IFAA_EccVerifyDigest(const IFAA_EccKey* key,
                                 const uint8_t* digest, uint32_t digest_len,
                                 const uint8_t* sig, uint32_t sig_len)
{
	TEE_Attribute       attr[0];
	TEE_ObjectHandle    keyobj = TEE_HANDLE_NULL;
	TEE_OperationHandle verifyop = TEE_HANDLE_NULL;
	uint32_t            keySize = 256;

	TEE_Result  ret = TEE_AllocateTransientObject(TEE_TYPE_ECDSA_PUBLIC_KEY, keySize, &keyobj);
	if (ret != TEE_SUCCESS) {
	  LOG_E("TEE_AllocateTransientObject failed: %08x", ret);
	  return IFAA_ERR_OUT_OF_MEM;
	}

	TEE_InitRefAttribute(&attr[0], TEE_ATTR_ECC_PUBLIC_VALUE_X, key->x.buf,key->x.len);
	TEE_InitRefAttribute(&attr[1], TEE_ATTR_ECC_PUBLIC_VALUE_Y, key->y.buf,key->y.len);
	TEE_InitValueAttribute(&attr[2], TEE_ATTR_ECC_CURVE, TEE_ECC_CURVE_NIST_P256, 0);

	ret = TEE_PopulateTransientObject(keyobj, attr, 3);
	if (ret != TEE_SUCCESS) {
	  LOG_E("TEE_PopulateTransientObject failed: %08x", ret);
	  ret = IFAA_ERR_UNKNOWN;
	  goto bye;
	}

	ret = TEE_AllocateOperation(&verifyop, TEE_ALG_ECDSA_P256, TEE_MODE_VERIFY, keySize);
	if (ret != TEE_SUCCESS) {
	  LOG_E("TEE_AllocateOperation failed: %08x", ret);
	  ret = IFAA_ERR_UNKNOWN;
	  goto bye;
	}

	TEE_SetOperationKey(verifyop, keyobj);

	ret = TEE_AsymmetricVerifyDigest(verifyop, NULL, 0, digest, digest_len, sig, sig_len);
	if (ret != TEE_SUCCESS) {
	  LOG_E("TEE_AsymmetricVerifyDigest failed: %08x", ret);
	  ret = IFAA_ERR_UNKNOWN;
	  goto bye;
	}
bye:
	if (keyobj != NULL) {
	  TEE_FreeTransientObject(keyobj);
	  keyobj = NULL;
	}
	if (verifyop != NULL) {
	  TEE_FreeOperation(verifyop);
	  verifyop = NULL;
	}
	if (TEE_SUCCESS == ret) ret = IFAA_ERR_SUCCESS;
	return ret;

}
#endif

#ifdef TZ_MODEL_QCOM
IFAA_Result IFAA_HmacSha1(const uint8_t *msg, uint32_t msg_len,
                          const uint8_t *key, uint32_t key_len,
                          uint8_t *mac) {
    int ret = qsee_hmac(QSEE_HMAC_SHA1, msg, msg_len, key, key_len, mac);
    if (ret < 0) {
        LOG_E("qsee_hmac failed");
        return IFAA_ERR_HASH;
    }

    return IFAA_ERR_SUCCESS;
}

#else
IFAA_Result IFAA_HmacSha1(const uint8_t *msg, uint32_t msg_len,
                          const uint8_t *key, uint32_t key_len,
                          uint8_t *mac)
{
#define SHA1_HASH_LEN   20
#define B 64
    TEE_OperationHandle op = NULL;
    
    uint8_t k_ipad[B + 1] = {0}; /* inner padding - key XORd with ipad */
    uint8_t k_opad[B + 1] = {0}; /* outer padding - key XORd with opad */
    
    /* if key is longer than 64 bytes reset it to key=SHA1(key) */
    if (key_len > B) {
        TEE_AllocateOperation(&op, TEE_ALG_SHA1, TEE_MODE_DIGEST, 0);
        TEE_DigestUpdate(op, key, key_len);
        TEE_DigestDoFinal(op, NULL, 0, key, &key_len);
        tzwFreeOperation(op);
    }
    
    memset(k_ipad, 0, sizeof(k_ipad));
    memset(k_opad, 0, sizeof(k_opad));
    memcpy(k_ipad, key, key_len);
    memcpy(k_opad, key, key_len);
    for (int i = 0; i < B; ++i) {
        k_ipad[i] ^= 0x36;
        k_opad[i] ^= 0x5c;
    }
    uint32_t mac_len = SHA1_HASH_LEN;
    
    // perform inner SHA1
    TEE_AllocateOperation(&op, TEE_ALG_SHA1, TEE_MODE_DIGEST, 0);
    TEE_DigestUpdate(op, k_ipad, B);
    TEE_DigestDoFinal(op, msg, msg_len, mac, &mac_len);
    tzwFreeOperation(op);
    
    // perform outer SHA1
    TEE_AllocateOperation(&op, TEE_ALG_SHA1, TEE_MODE_DIGEST, 0);
    TEE_DigestUpdate(op, k_opad, B);
    TEE_DigestDoFinal(op, mac, SHA1_HASH_LEN, mac, &mac_len);
    tzwFreeOperation(op);

    return IFAA_ERR_SUCCESS;
}
#endif


IFAA_Result HOTPex(uint8_t *data, size_t data_len, uint8_t *hotp, uint32_t *hotp_len) {
#define MAX_CODE_LENGTH 8

    if (*hotp_len < 4) {
        return IFAA_ERR_BAD_PARAM;
    }

    if (*hotp_len > MAX_CODE_LENGTH) {
        *hotp_len = MAX_CODE_LENGTH;
    }

    uint8_t hash1[SHA1_HASH_LEN] = {0};
    uint8_t hash2[SHA1_HASH_LEN] = {0};
    uint8_t KEY1[] = {0x8D, 0x30, 0x16, 0x15, 0xFE, 0x43, 0xE6, 0x25,
                      0xAB, 0xEC, 0x6F, 0xE2, 0xA5, 0xF3, 0x81, 0x3D,
                      0x34, 0xC5, 0x99, 0x5C, 0x2F, 0xF8, 0x6C, 0x94,
                      0x38, 0x1F, 0xAA, 0xBC, 0x7B, 0x88, 0xC3, 0x74,
                      0x8A, 0x3C, 0x2B, 0x4E, 0x13, 0x04, 0xF8, 0x6B};
    uint8_t KEY2[] = {0xE3, 0x42, 0xA9, 0xFA, 0x17, 0x2A, 0xA6, 0x4E,
                      0xC2, 0x33, 0x9B, 0x95, 0xCB, 0x55, 0x5A, 0xBF,
                      0x07, 0xA7, 0x04, 0x60, 0xE0, 0xA9, 0xDE, 0x3B,
                      0xD6, 0x84, 0x55, 0x81, 0xC8, 0xC0, 0xE3, 0x9B,
                      0x0F, 0xDB, 0xBF, 0x9E, 0xE9, 0x6F, 0x1F, 0xA4};

    CHECK_FUNC_RET(IFAA_HmacSha1(data, data_len, KEY1, sizeof(KEY1), hash1));
    CHECK_FUNC_RET(IFAA_HmacSha1(data, data_len, KEY2, sizeof(KEY2), hash2));

    // zip and fold
    uint8_t code[MAX_CODE_LENGTH] = {0};
    int offset = hash1[SHA1_HASH_LEN - 1] & 0x0f;
    code[3] = hash1[offset + 0] & 0x7f;
    code[2] = hash1[offset + 1] & 0xff;
    code[1] = hash1[offset + 2] & 0xff;
    code[0] = hash1[offset + 3] & 0xff;

    offset = hash2[SHA1_HASH_LEN - 1] & 0x0f;
    code[4] = hash2[offset + 0] & 0x7f;
    code[5] = hash2[offset + 1] & 0xff;
    code[6] = hash2[offset + 2] & 0xff;
    code[7] = hash2[offset + 3] & 0xff;

    memcpy(hotp, code, *hotp_len);

    return IFAA_ERR_SUCCESS;
}

IFAA_Result IFAA_GetDeviceId(uint8_t *buf, uint32_t *len) {

#define HOTP_LEN        8
#define DEVICE_ID_LEN   40
#define PREFIX_LEN      8
#define SHA256_HASH_LEN 32

    if (buf == NULL || len == NULL || *len < DEVICE_ID_LEN) {
        LOG_E("get_device_id_impl - invalid parameter");
        return IFAA_ERR_BAD_PARAM;
    }

#define DEVICE_VENDOR_ID    0x01
#define CHIP_VENDOR_ID      0X02
#define TEE_VENDOR_ID       0x03
#define SENSOR_VENDOR_ID    0x04

    uint8_t *p = buf;
    write16(p, DEVICE_VENDOR_ID);
    p += 2;
    write16(p, CHIP_VENDOR_ID);
    p += 2;
    write16(p, TEE_VENDOR_ID);
    p += 2;
    write16(p, SENSOR_VENDOR_ID);
    p += 2;

    uint8_t data[DEVICE_ID_LEN] = {0};
    uint32_t data_len = sizeof(data);
    TEE_GetDeviceID(data, &data_len);

    uint8_t digest[SHA256_HASH_LEN];
    uint32_t digest_len = SHA256_HASH_LEN;
    IFAA_Result ret = IFAA_Sha256(data, data_len, digest, &digest_len);
    if (ret != IFAA_ERR_SUCCESS) {
        LOG_E("sha256 failed: 0x%08x", ret);
        return ret;
    }

    uint8_t hotp[HOTP_LEN] = {0};
    uint32_t hotp_len = sizeof(hotp);
    HOTPex(digest, digest_len, hotp, &hotp_len);

    int otp_index[HOTP_LEN] = {17, 3, 36, 9, 22, 38, 10, 25};
    for (int i = 0, k = 0; i < SHA256_HASH_LEN; ++i) {
        for (int j = 0; j < HOTP_LEN; ++j) {
            if (k == otp_index[j]) {
                p[k] = hotp[j];
                k++;
            }
        }
        p[k] = digest[i];
        k++;
    }
    *len = DEVICE_ID_LEN + PREFIX_LEN;
    // LOG_D("Finished deviceID : %s , deviceID size: %d",buf,*len);
    return IFAA_ERR_SUCCESS;
}

IFAA_Result IFAA_GetProtocolVersion(uint8_t *version, uint32_t *version_len) {

    int32_t i_version = TEE_GetAuthenticatorVersion();
    LOG_D("Authenticator version: %d", i_version);
    write32(version, i_version);
    *version_len = sizeof(i_version);

    return IFAA_ERR_SUCCESS;
}

IFAA_Result IFAA_GetOptionalCertEncodeAlg(uint8_t *algs, uint32_t *algs_len) {

    //write32(algs, CERT_ENCODE_ALG_IFAA | CERT_ENCODE_ALG_X509);
    write32(algs, CERT_ENCODE_ALG_IFAA);
    *algs_len = 4;
    return IFAA_ERR_SUCCESS;
}

IFAA_Result IFAA_CheckCert(const IFAA_Certificate *cert, IFAA_Certificate *rootcert) {
    IFAA_Result ret;
    uint8_t digest[64] = {0};
    uint32_t digest_len = sizeof(digest);
    uint32_t msg_len = cert->body.ifaa_cert.pub_key.n.len +
                       cert->body.ifaa_cert.pub_key.e.len +
                       sizeof(uint32_t) * 2;
    uint8_t *msg = (uint8_t *) tzwMalloc(msg_len);
    if (NULL == msg) {
        LOG_E("malloc failed.");
        return IFAA_ERR_OUT_OF_MEM;
    }

    IFAA_RsaKey rsakey;
    if (!cert || !rootcert) {
        LOG_E("Invalid parameters");
        tzwFree(msg);
        return IFAA_ERR_BAD_PARAM;
    }

    write32(msg, cert->body.ifaa_cert.pub_key.n.len);
    memcpy(msg + 4, cert->body.ifaa_cert.pub_key.n.buf, cert->body.ifaa_cert.pub_key.n.len);
    write32(msg + cert->body.ifaa_cert.pub_key.n.len + 4, cert->body.ifaa_cert.pub_key.e.len);
    memcpy(msg + cert->body.ifaa_cert.pub_key.n.len + 8, cert->body.ifaa_cert.pub_key.e.buf,
           cert->body.ifaa_cert.pub_key.e.len);

    rsakey.n = rootcert->body.ifaa_cert.pub_key.n;
    rsakey.e = rootcert->body.ifaa_cert.pub_key.e;

    ret = IFAA_Sha256(msg, msg_len, digest, &digest_len);
    if (ret != IFAA_ERR_SUCCESS) {
        LOG_E("DigestCal err!");
        tzwFree(msg);
        msg = NULL;
        return IFAA_ERR_BAD_PARAM;
    }

    ret = IFAA_RsaVerifyDigest(&rsakey, digest, digest_len,
                               cert->body.ifaa_cert.sig.buf,
                               cert->body.ifaa_cert.sig.len);
    if (ret != IFAA_ERR_SUCCESS) {
        LOG_E("IFAA_RsaVerifyDigest err!");
        tzwFree(msg);
        msg = NULL;
        return IFAA_ERR_BAD_PARAM;
    }
    if (msg)
        tzwFree(msg);
    msg = NULL;

    return ret;
}

#define ROOT_CERT_N (uint8_t[]){ \
    0x89,0x91,0xc3,0x0f,0x87,0x19,0xef,0xbf,0xec,0x56,0xb6,0x14,0x33,0x6f,0xd9,0x6b,0xbd,0xc8,0x06,0x09, \
    0x3a,0x42,0xa4,0x76,0xcd,0x01,0x26,0x06,0x44,0xfe,0x88,0x5c,0x59,0xc9,0xef,0x12,0xea,0xdd,0x2d,0xf3, \
    0x77,0xaa,0x0d,0xf2,0x95,0x60,0x08,0x2a,0xd7,0x75,0x3c,0x96,0x9f,0xd6,0x62,0xd8,0xe4,0xa8,0xa2,0x12, \
    0xb6,0x01,0x78,0x2a,0x55,0x82,0x4f,0x76,0x4d,0x91,0x00,0xa2,0x42,0xb6,0x8e,0x30,0xf0,0xb6,0x5c,0x5a, \
    0x5c,0x3d,0xb3,0xb0,0x93,0xb9,0xcb,0x06,0xaa,0xc0,0x29,0x5f,0x0e,0xc2,0x4a,0x23,0xca,0xbe,0x15,0xf1, \
    0x64,0x98,0xad,0xfa,0xd5,0x87,0xed,0x72,0x5b,0x03,0xec,0xb4,0xb6,0x44,0xfe,0x1c,0x35,0x0f,0x89,0xaa, \
    0xa5,0xf0,0x31,0x2a,0xb0,0x24,0xcc,0x42,0xa5,0x41,0x8e,0x9d,0xeb,0x91,0xb2,0x0e,0xf3,0xdb,0x8e,0x25, \
    0x1b,0xf5,0x72,0x35,0xa9,0x41,0xa2,0x91,0x13,0xa1,0x4d,0x74,0xe4,0xa3,0x4a,0x6c,0xa6,0x83,0xef,0xc9, \
    0x71,0x26,0x8b,0xa9,0x81,0x58,0x73,0xfe,0xf1,0x21,0x20,0x35,0x62,0x80,0x2d,0x7f,0xb3,0x50,0xbf,0x6a, \
    0x85,0x0b,0x38,0x0c,0xa9,0xa4,0xd5,0x97,0x71,0x24,0x3c,0xfd,0x6e,0x77,0x01,0x03,0xdb,0x1a,0x9d,0x5b, \
    0xa3,0xfa,0x15,0xad,0x5a,0x78,0xcd,0x4d,0x53,0xad,0xed,0x81,0x20,0x4a,0x8d,0x22,0x3d,0xfd,0xb8,0x8e, \
    0x6a,0x13,0x41,0xdb,0xe9,0xe2,0xa8,0x28,0x78,0x5c,0xf1,0x6a,0x5e,0x83,0x19,0xb4,0x0d,0xf5,0xf8,0xfe, \
    0x27,0x90,0xaf,0xbd,0x25,0xa6,0x89,0x84,0xb1,0xb9,0xdc,0xe5,0x9e,0x00,0x38,0x27 }

#define ROOT_CERT_E (uint8_t[]){ 0x1,0x0,0x1 }

#define ROOT_CERT_SIG (uint8_t[]){ \
    0x63,0x66,0xcb,0x3,0xff,0x14,0x59,0x52,0x93,0x3a,0xa9,0x24,0x9,0x21,0x8a,0xdd,0xb6,0x72,0xa2,0xbd,  \
    0x34,0x95,0x6d,0x2f,0x2e,0xf1,0x28,0x38,0xe0,0x99,0x5a,0x73,0xb6,0x71,0x97,0x80,0xe3,0x9,0x60,0xea,  \
    0xcc,0x56,0x19,0x34,0x7c,0xc8,0x2a,0xbc,0xb3,0x88,0x1b,0x14,0x8e,0xb0,0x9b,0xa7,0xcf,0xaf,0xb8,0x38, \
    0x76,0xab,0xc0,0x63,0x22,0x1c,0x5f,0x71,0x5f,0x9,0x5f,0x19,0x5,0xb7,0x61,0x5f,0xe4,0xfe,0x97,0xad,  \
    0x91,0x52,0x7e,0xbc,0x87,0x93,0x2e,0x60,0x47,0x7b,0x24,0x8d,0x25,0x40,0xce,0xf1,0x26,0xa8,0x57,0xab,    \
    0xba,0x77,0xf,0x2,0xf1,0xaf,0x6d,0xed,0xd4,0x4,0xe1,0xcb,0xb7,0x6e,0x78,0x4a,0x65,0x1,0x21,0x3e,    \
    0x54,0xe3,0x8e,0x8e,0xd1,0xb3,0x60,0xf4,0xd4,0x61,0xfa,0xa8,0x63,0x62,0x20,0xf9,0xfa,0x85,0x2d,0x2f, \
    0x1,0x33,0xa1,0x23,0xf2,0x7d,0x54,0x18,0x3e,0x52,0xd8,0x88,0x4,0x23,0xd,0x3d,0xaa,0x77,0xa0,0x55,   \
    0x37,0xa5,0xc4,0x1,0xe9,0x22,0x2d,0xef,0x36,0x98,0xa9,0xf6,0xbe,0xf3,0x7,0x89,0x40,0x88,0x16,0xb3,  \
    0x8c,0x7,0x6a,0x3e,0xb6,0xe3,0x59,0xb1,0x7a,0x84,0x17,0xa2,0x2e,0x37,0x3a,0xae,0xdf,0xa0,0xd9,0x52, \
    0xbb,0xc1,0xe5,0x82,0x4d,0x40,0x50,0xe2,0x5d,0x2,0xe0,0x3,0x44,0x68,0x18,0xcd,0xc8,0xba,0xd,0xc0,   \
    0x60,0x44,0x9e,0x72,0xad,0xc5,0x12,0x50,0x54,0x73,0xd8,0x5f,0x28,0xbb,0xae,0x1b,0xe7,0x72,0x3a,0xa8, \
    0x59,0x3d,0x8b,0xdf,0xc0,0x3a,0xb7,0x1c,0x8a,0x8f,0x6f,0xa3,0x68,0x1f,0xc0,0x2f }
//original key
#define PRI_NN (unsigned char[]) { \
    0xc3,0x49,0x30,0xb7,0xb5,0xb2,0xdb,0xcb,0xae,0x33,0x29,0x19,0xbc,0xa7,0x51,0x1,0x7c,0xab,0xb5,0x8d, \
    0xfe,0x71,0x7a,0xd3,0x2e,0xa1,0x57,0xcb,0xf0,0x15,0x1a,0x4c,0xb1,0xc6,0xba,0x6b,0xa,0xb3,0x8c,0x56, \
    0x49,0x51,0x48,0xd,0x62,0x6d,0x1d,0x51,0xae,0xfb,0x53,0x47,0x46,0x45,0xcd,0x8e,0x58,0xb7,0x4f,0x39, \
    0xef,0x7,0x1f,0xf7,0xdb,0x4b,0xe5,0x4a,0xbd,0xdf,0xcb,0xf3,0x38,0xca,0xa8,0x6e,0xcb,0x21,0x10,0x8a, \
    0xfb,0x88,0x15,0x56,0xcc,0xe6,0x3c,0x99,0x81,0xd8,0xc7,0xe9,0xf4,0x5f,0xa1,0xd8,0xd9,0xa9,0x49,0x35, \
    0xe5,0x5b,0xae,0x2,0x76,0xee,0x8f,0x6,0x9b,0x1f,0x96,0x9a,0x83,0x29,0x44,0x6b,0xf4,0x82,0x64,0xd8, \
    0xf8,0x97,0x90,0x69,0xae,0xe5,0xd1,0x3,0xd9,0x14,0x9e,0x81,0xa7,0xc7,0xc3,0x6d,0x46,0xcb,0xb1,0xcf, \
    0x9b,0x55,0xbe,0x43,0xc3,0x5e,0xdb,0xbe,0xf6,0xcf,0xdc,0x3b,0xbb,0xb3,0xf4,0x4f,0xda,0xad,0x3e,0xc3, \
    0x55,0x3d,0x1e,0x5f,0xdf,0xb5,0xa9,0x31,0x6f,0x1d,0xc3,0xc4,0x5,0xfe,0x63,0xaf,0x86,0xe2,0x31,0xe8, \
    0xe6,0x8,0x12,0x25,0xf1,0xc5,0x47,0x3,0xd2,0xb,0x46,0x30,0x42,0x8,0xa6,0xe3,0x8c,0xba,0x13,0x35, \
    0x7,0xec,0x29,0x73,0xa5,0xf3,0xdc,0x50,0xa8,0xd4,0xf6,0x58,0x4b,0x54,0xfd,0x9,0x79,0x4f,0xaf,0x34, \
    0xe1,0xa,0xa2,0xb0,0x20,0x80,0xe7,0x48,0xee,0xfe,0x47,0x3f,0x84,0x6a,0x20,0x4e,0x7e,0x8d,0x46,0xf1, \
    0xee,0x63,0x4a,0xe7,0xd2,0x2a,0xeb,0xf7,0xb7,0x6d,0xec,0xb3,0x7c,0x6d,0xe6,0x7b }
//key of Galaxy S8+
#define PRI_N (unsigned char[]) { \
    0x00,0xc1,0x8f,0xb1,0xe3,0xe4,0x6e,0xdf,0x6f,0x73,0x15,0xf0,0x56,0x34,0x31, \
    0xe6,0x04,0xfd,0x89,0xf9,0x6a,0x02,0x66,0x5a,0x8a,0x8f,0x52,0x2f,0x2b,0xcd, \
    0xf0,0xd4,0x91,0xdb,0xc1,0x29,0x53,0x6c,0xdc,0x83,0x2c,0xd5,0xa8,0x9b,0xf5, \
    0x9f,0xe9,0xf5,0xdc,0x3c,0x87,0xb8,0x0f,0x56,0xb9,0xc3,0xba,0xd1,0xbc,0x01, \
    0x56,0x71,0xfb,0xa4,0xdd,0x1f,0x29,0xbf,0x41,0x38,0x59,0x6b,0xba,0x86,0x8a, \
    0x99,0x74,0xb3,0xaf,0x6e,0xa1,0x28,0x21,0x4e,0x20,0xf2,0xc6,0xbe,0x9b,0xfe, \
    0x8f,0xc3,0xad,0xb0,0x68,0xb5,0xf0,0x2f,0x0f,0xb8,0xe9,0x44,0xe2,0x5c,0x2d, \
    0xdc,0xee,0x87,0x6d,0xab,0x19,0xe3,0x07,0x0b,0xfd,0x87,0x82,0x63,0xee,0xef, \
    0x33,0xf2,0xd6,0xe5,0x28,0x50,0x9a,0xd5,0xa1,0xd7,0x7c,0x00,0x76,0x6a,0xe9, \
    0xf5,0x77,0xbd,0x32,0x0a,0xfb,0x3d,0x51,0x4b,0x62,0xe7,0x06,0x7b,0xd8,0x4d, \
    0x65,0x04,0xf4,0x38,0x9b,0xb3,0x4d,0xd8,0x44,0x4c,0xcf,0x5d,0x85,0x6d,0x89, \
    0xd1,0x41,0xcb,0xcf,0x2a,0x70,0xcf,0x2a,0xb4,0xac,0xc9,0xa5,0x90,0x6e,0x86, \
    0x2a,0xdb,0x51,0xb2,0xab,0x0c,0x54,0x5d,0xe4,0x3f,0xae,0x1f,0xce,0xa0,0x92, \
    0x59,0xe8,0x4b,0xdd,0xee,0xaa,0x3f,0x0e,0x94,0x4d,0xd1,0x1c,0x00,0xc6,0x25, \
    0x59,0x27,0x1f,0xc4,0x42,0xbc,0x79,0x29,0xdd,0xd5,0x40,0x55,0x48,0x7f,0x7c, \
    0x50,0x7d,0x4b,0x3d,0xe0,0x76,0x8f,0x61,0xf1,0xe9,0xa0,0xbf,0x4a,0xe8,0x83, \
    0xe5,0x03,0xc5,0x6b,0xf4,0xe1,0x80,0xa3,0x12,0x4f,0x34,0x30,0xec,0xb8,0xf8, \
    0x9a,0x9b}

#define PRI_E (unsigned char[]) { 0x1,0x0,0x1 }
//original key
#define PRI_DD (unsigned char[]) { \
    0xb6,0x2f,0xd1,0x0,0x82,0xc5,0xf3,0x62,0x49,0x9,0x37,0xbd,0xe8,0xf,0x9c,0x76,0x2b,0xae,0x31,0xf9, \
    0xdf,0xb8,0x54,0xe3,0x32,0x2c,0x99,0xb1,0xc,0x31,0x53,0xd0,0xdb,0x45,0xd0,0x62,0xce,0xa0,0x5,0x3b, \
    0xf6,0xb8,0x9a,0xe4,0xc9,0xbf,0x8c,0x4b,0xc9,0x58,0x75,0x30,0x18,0x72,0x44,0xbc,0x19,0x2e,0x22,0xfe, \
    0xa4,0x6d,0xdc,0x38,0x2a,0xe1,0xda,0x6f,0x69,0x46,0xa1,0x8a,0x2e,0xa0,0x7a,0x94,0xaa,0x73,0x15,0x70, \
    0xe4,0xa,0xe8,0x5,0x8b,0xb0,0xfc,0x36,0x26,0x3a,0x6c,0xff,0x69,0xd9,0xd2,0x2f,0x71,0x99,0xa,0x50, \
    0xfa,0xc2,0x16,0x4b,0xfc,0x52,0xc1,0x22,0x63,0x94,0xb,0xb7,0xe8,0xc7,0x96,0x2a,0xb4,0xf8,0xf5,0x46, \
    0xaa,0x5d,0x14,0x0,0x54,0x87,0xd8,0xff,0x0,0x5e,0x86,0xf2,0x35,0x45,0x52,0x1f,0xe8,0xa2,0xb2,0x9b, \
    0xba,0xe6,0xc0,0x63,0x78,0xa5,0x60,0x6d,0x6d,0x8a,0x31,0x15,0xaf,0x28,0xf6,0xa3,0x63,0xe6,0x2,0xd5, \
    0xe6,0xbd,0x38,0xee,0x16,0x1c,0xef,0xcd,0x9e,0xc2,0x7,0x61,0x2e,0x3a,0x5f,0x76,0x6e,0x8c,0xe0,0x14, \
    0x7,0x12,0x28,0xb0,0x36,0x6e,0x9b,0x36,0xb9,0xec,0x81,0xe0,0xce,0x36,0x88,0xa,0x9,0x28,0xcd,0x3c, \
    0x38,0x4d,0xe9,0x9,0xec,0xc8,0x4c,0x8,0x24,0x27,0xaa,0x35,0x49,0xe,0x5b,0x29,0xd9,0xd0,0x83,0x52, \
    0x6,0x24,0xa8,0x74,0xae,0xcd,0x3c,0x5b,0xfe,0x53,0x86,0x88,0x10,0x42,0x4,0x4c,0x28,0x53,0x51,0xb7, \
    0x82,0xf3,0xe8,0x19,0xf,0x96,0xd6,0x48,0x13,0xf1,0x39,0x60,0x5b,0xe,0x2f,0x99 }
//key of Galaxy S8+
#define PRI_D (unsigned char[]) { \
    0x3b,0xe7,0xb8,0x1b,0x9a,0xe8,0x41,0x98,0xa1,0x9a,0xa9,0x9e,0x54,0x5b,0x19, \
    0x20,0x74,0x43,0x8e,0x80,0xa3,0xab,0x7f,0xdc,0x20,0x4d,0x44,0x32,0x1f,0x73, \
    0xa8,0xa6,0x57,0xc7,0xe9,0x7d,0x5b,0x54,0xc7,0xf8,0x49,0x4b,0xfc,0xb0,0xc1, \
    0x44,0x4c,0x86,0x53,0xe9,0x81,0x2a,0xa6,0x21,0xaa,0x15,0x18,0xb2,0x42,0x0b, \
    0xba,0x0d,0x32,0xd2,0x08,0xc2,0x07,0xe2,0x2c,0x89,0x68,0x50,0xfb,0x30,0xcc, \
    0x1b,0x95,0x3c,0xf7,0x55,0x43,0x31,0x43,0x97,0xe8,0xeb,0x5f,0xb4,0xb1,0xcd, \
    0x56,0xb7,0x5b,0xc0,0x9c,0x86,0x70,0x5a,0x42,0x55,0x57,0xaf,0x62,0xee,0x02, \
    0xd1,0x83,0xf2,0x8a,0x20,0xe1,0xe5,0x9d,0x6d,0x62,0xd2,0x15,0x11,0xae,0xd0, \
    0x7a,0x18,0xce,0x25,0x73,0x04,0xe8,0x96,0xbb,0x31,0x34,0xaa,0x37,0x8e,0xe3, \
    0x9b,0x2c,0xf9,0x6d,0xc7,0xac,0x4a,0x42,0x60,0x4f,0x6d,0x7a,0x0b,0xef,0xeb, \
    0x1d,0xef,0x6a,0x24,0xdf,0x8f,0x22,0x75,0xef,0x5b,0x2a,0xb1,0x55,0x7e,0x32, \
    0x2e,0x6a,0x95,0xae,0x5a,0x90,0x68,0xae,0x31,0x68,0x6d,0xdf,0x27,0x7b,0xe8, \
    0xec,0x34,0x65,0x28,0x05,0xc5,0xfd,0x06,0x69,0x9e,0xc0,0xcc,0x0a,0xa0,0x90, \
    0x40,0xbb,0xdc,0x9f,0xe5,0xeb,0x7e,0x5b,0xcc,0x01,0x38,0xb8,0xfb,0x74,0x9d, \
    0xd2,0xd0,0xc6,0x2a,0x74,0x48,0x30,0x9d,0x68,0xc6,0x4c,0x1e,0xc2,0xd5,0x88, \
    0xde,0x24,0xe9,0xaa,0xc3,0xc0,0x0c,0x50,0xda,0x38,0xb5,0xfc,0x8d,0x61,0x68, \
    0xb5,0xe9,0x8f,0x7d,0x10,0x13,0x87,0x78,0x82,0xe7,0x86,0x9c,0xc9,0x48,0xd3, \
    0x99}
#define SKPM_PRI_N (unsigned char[]) { \
    0xbc, 0xa7, 0xd5, 0x97, 0x93, 0xb6, 0x21, 0x7f, 0x64, 0x59, 0x1c, 0x96, 0xd1, 0x89, 0x69, \
    0xf9, 0x21, 0xd2, 0xaa, 0x67, 0xa9, 0xd6, 0x9f, 0xb3, 0xf7, 0x89, 0x35, 0x71, 0x6c, 0xa7, \
    0xee, 0xcd, 0x25, 0x5d, 0x6e, 0x5b, 0x40, 0x7e, 0x6a, 0x03, 0x15, 0x79, 0xf2, 0xc8, 0x6c, \
    0x8a, 0x64, 0x81, 0xe2, 0x8b, 0xd4, 0x2e, 0xc6, 0x23, 0x8e, 0x77, 0x96, 0xf0, 0x0d, 0xb9, \
    0x93, 0xb5, 0x0d, 0xc0, 0x3e, 0xd2, 0x3f, 0x72, 0xdf, 0x2e, 0x3a, 0x4f, 0x0a, 0xc2, 0x4a, \
    0x7c, 0x03, 0xf6, 0xb5, 0xb9, 0x25, 0xc6, 0x41, 0x2d, 0xbe, 0xec, 0x8d, 0xf4, 0x1b, 0x6e, \
    0xf6, 0xd9, 0x93, 0xed, 0xed, 0xbf, 0x0c, 0x4f, 0xaa, 0xa7, 0xb8, 0x48, 0x59, 0x0c, 0x62, \
    0x42, 0x9e, 0x9f, 0xf2, 0x8d, 0xf1, 0x6d, 0x3c, 0x27, 0x1e, 0x3d, 0xcb, 0x5a, 0xe3, 0x53, \
    0x36, 0x4c, 0x3c, 0x52, 0xf7, 0x69, 0x48, 0xa1, 0x6e, 0x77, 0xeb, 0xcf, 0xbb, 0x43, 0x9c, \
    0x8a, 0xbd, 0x55, 0xe9, 0x02, 0x05, 0x09, 0x62, 0xac, 0x66, 0xc8, 0x35, 0x0b, 0x7a, 0x70, \
    0x04, 0x6e, 0xbb, 0x79, 0xae, 0x65, 0x62, 0x0b, 0x81, 0xef, 0xd2, 0xc7, 0xa7, 0x28, 0xa5, \
    0x58, 0x14, 0x74, 0x8f, 0x34, 0x78, 0x1b, 0x2b, 0x38, 0x91, 0x91, 0x34, 0x2d, 0xed, 0x56, \
    0x77, 0xf2, 0x58, 0xb3, 0x73, 0xc4, 0x29, 0x0f, 0x55, 0xc0, 0x55, 0x2a, 0x1f, 0x27, 0xbd, \
    0xc9, 0x8d, 0x71, 0x48, 0x56, 0xe1, 0x5e, 0xe8, 0xd0, 0x31, 0x1e, 0x6c, 0x78, 0x81, 0x23, \
    0xf3, 0x3e, 0xcf, 0xec, 0x2c, 0x85, 0x91, 0x9d, 0x70, 0xe8, 0x56, 0xb3, 0xd9, 0x83, 0x61, \
    0xcb, 0xe6, 0x7a, 0x61, 0x95, 0xee, 0x1e, 0xa1, 0xe1, 0x9a, 0x80, 0x5b, 0x99, 0x81, 0xf0, \
    0x30, 0xd1, 0xc6, 0x7d, 0xe7, 0x2e, 0x97, 0xc0, 0xd5, 0xdb, 0x30, 0x1f, 0x12, 0xe9, 0x86, \
    0x21}

#define SKPM_PRI_D (unsigned char[]) { \
    0x1c,0x20,0x7d,0xc4,0xe2,0x9d,0xf5,0xf2,0x93,0x58,0x81,0xdd,0xae,0x91,0xe9, \
    0x3d,0xde,0xa0,0x15,0x0d,0xed,0x91,0x75,0x30,0x14,0xc8,0xdd,0x31,0xf2,0xfc, \
    0x3a,0xab,0x55,0x55,0xcf,0xbd,0x45,0x29,0x53,0xce,0xef,0xe5,0xc5,0x9f,0xc2, \
    0x27,0x04,0x03,0x25,0x6e,0xa9,0x8c,0xeb,0xb7,0xa3,0x67,0x0b,0x16,0xf1,0x44, \
    0xe8,0xd3,0xc6,0xa0,0x11,0xfd,0xb0,0xbe,0x04,0xae,0x54,0xbd,0x32,0x22,0x73, \
    0x8e,0x77,0xdc,0xd1,0x8d,0x30,0x27,0x95,0xd2,0x27,0x34,0x07,0x9a,0x0c,0x79, \
    0x07,0xce,0xba,0xb2,0x6f,0x8e,0xdb,0x5c,0x09,0x5a,0x86,0x2c,0x2b,0x32,0xa0, \
    0x77,0xc4,0x44,0x9e,0x9a,0x31,0x13,0x93,0x33,0x55,0xdb,0xea,0xb4,0xbb,0x4d, \
    0xcd,0xbb,0xa2,0x06,0xd0,0xa2,0x5e,0xd4,0x89,0x1d,0xd8,0xb8,0x90,0x47,0xd5, \
    0x3b,0x25,0x95,0xd8,0x40,0x81,0xa8,0x28,0xfa,0x69,0xd0,0x0e,0xf3,0x07,0x1b, \
    0x9c,0xb8,0x71,0x28,0x2b,0xcd,0x87,0x16,0x32,0x34,0xc7,0x8e,0x4e,0x23,0x6e, \
    0x07,0x35,0xe6,0xfc,0x06,0x23,0x7b,0x0e,0x9a,0x86,0x4a,0x71,0x8e,0x43,0x78, \
    0x2d,0x5c,0x10,0x69,0x91,0xe7,0x94,0xed,0xff,0x34,0x49,0x02,0xa7,0xb8,0xc2, \
    0x75,0xc8,0x90,0x35,0x4b,0x6f,0x09,0xa6,0x62,0x11,0x64,0x93,0x6f,0x22,0x2d, \
    0x8f,0x3f,0x5a,0x78,0x5a,0xcb,0xca,0xed,0x0a,0xde,0x3a,0x52,0xa6,0xdb,0x3b, \
    0xee,0xe5,0x53,0x61,0xd3,0xaf,0x24,0xdd,0xda,0x67,0x3f,0x15,0x6c,0x0e,0x29, \
    0xd5,0x07,0xa3,0x9a,0xf1,0xa0,0x6a,0x88,0xe8,0xb4,0x33,0x4a,0xcd,0x2b,0x34, \
    0x5d}

IFAA_Result IFAA_AuthenticatorVerifyDigest(const uint8_t *digest, uint32_t digest_len,
                                           const uint8_t *signature, uint32_t sig_len,
                                           const IFAA_Certificate *cert, uint32_t certlen) {

    IFAA_Result ret = IFAA_ERR_SUCCESS;
    IFAA_RsaKey rsakey;
    IFAA_Certificate root_cert;
    root_cert.body.ifaa_cert.pub_key.n.buf = ROOT_CERT_N;
    root_cert.body.ifaa_cert.pub_key.n.len = sizeof(ROOT_CERT_N);
    root_cert.body.ifaa_cert.pub_key.e.buf = ROOT_CERT_E;
    root_cert.body.ifaa_cert.pub_key.e.len = sizeof(ROOT_CERT_E);
    root_cert.body.ifaa_cert.sig.buf = ROOT_CERT_SIG;
    root_cert.body.ifaa_cert.sig.len = sizeof(ROOT_CERT_SIG);

    if (!digest || !signature || !cert || digest_len <= 0 || sig_len <= 0 || certlen <= 0) {
        LOG_E("Invalid parameters");
        return IFAA_ERR_BAD_PARAM;
    }

    switch (cert->cert_enc_alg) {
        case CERT_ENCODE_ALG_IFAA: {
            ret = IFAA_CheckCert(cert, &root_cert);
            if (ret != IFAA_ERR_SUCCESS) {
                LOG_E("VerifyCer err!");
                return IFAA_ERR_BAD_PARAM;
            }
            break;
        }

        default:
            return IFAA_ERR_BAD_PARAM;
    }

    rsakey.n = cert->body.ifaa_cert.pub_key.n;
    rsakey.e = cert->body.ifaa_cert.pub_key.e;
    ret = IFAA_RsaVerifyDigest(&rsakey, digest, digest_len, signature, sig_len);
    if (ret != IFAA_ERR_SUCCESS) {
        LOG_E("VerifyDigest err!");
        return IFAA_ERR_BAD_PARAM;
    }

    return ret;
}

//__attribute__((weak))
IFAA_Result IFAA_AuthenticatorSignDigest(const uint8_t *digest, uint32_t digest_len,
                                         uint8_t *signature, uint32_t *sig_len) {
    IFAA_Result ret = IFAA_ERR_SUCCESS;
    if (!digest || !signature || digest_len <= 0) {
        LOG_E("IFAA_AuthenticatorSignDigest,Invalid parameters");
        return IFAA_ERR_BAD_PARAM;
    }
    LOG_D("before sign, sig_len = %d, digestLen = %d", *sig_len, digest_len);
    ifaa_km_result_t result = ifaa_km_sign(digest, digest_len, signature, sig_len);
    if (result != IFAA_KM_SUCCESS) {
        LOG_E("IFAA_AuthenticatorSignDigest,Ifaa km sign failed : %d", result);
        return IFAA_ERR_SKPM_SIGN_FAILED;
    }
    LOG_D("after sign, sig_len = %d, digestLen = %d", *sig_len, digest_len);

//    IFAA_Result ret = IFAA_ERR_SUCCESS;
//    IFAA_RsaKey rsaprikey;
//    if (!digest || !signature || digest_len <= 0) {
//        LOG_E("Invalid parameters");
//        return IFAA_ERR_BAD_PARAM;
//    }
//
//    rsaprikey.n.buf = SKPM_PRI_N;
//    rsaprikey.n.len = sizeof(SKPM_PRI_N);
////    rsaprikey.e.buf = PRI_E;
////    rsaprikey.e.len = sizeof(PRI_E);
//    rsaprikey.d.buf = SKPM_PRI_D;
//    rsaprikey.d.len = sizeof(SKPM_PRI_D);
//
//
//    if(ret==IFAA_ERR_SUCCESS) {
//
//        ret = IFAA_RsaSignDigest(&rsaprikey, digest, digest_len, signature, sig_len);
//        if (ret != IFAA_ERR_SUCCESS) {
//            LOG_E("AuthenticatorSignDigest err!");
//            return IFAA_ERR_BAD_PARAM;
//        }
//    }
    return ret;
}

#define MAX_PATH_LEN 256

#ifdef TZ_MODEL_QCOM

IFAA_Result IFAA_WriteFile(const char *path, uint32_t path_len, const uint8_t *data, uint32_t len) {

    IFAA_Result ret = IFAA_ERR_SUCCESS;

    char buf_path[MAX_PATH_LEN] = {0};
    if (path_len > MAX_PATH_LEN) {
        LOG_E("IFAA_WriteFile ,can't use memcpy operation because of overflow.");
        return IFAA_ERR_BUF_TOO_SHORT;
    }
    memcpy(buf_path, path, path_len);
    LOG_D("write_file: %s", buf_path);

    int fd = qsee_sfs_open(buf_path, O_CREAT | O_WRONLY);
    if (fd == 0) {
        LOG_E("qsee_sfs_open failed: 0x%08X", qsee_sfs_error(fd));
        return IFAA_ERR_WRITE;
    }

    if (data != NULL && len > 0) {
        uint32_t count = qsee_sfs_write(fd, (const char *) data, len);
        if (count != len) {
            LOG_E("qsee_sfs_write failed: 0x%08X", qsee_sfs_error(fd));
            ret = IFAA_ERR_WRITE;
        }
    }

    int status = qsee_sfs_close(fd);
    if (status != 0) {
        LOG_E("qsee_sfs_close failed: 0x%08X", qsee_sfs_error(fd));
        ret = IFAA_ERR_WRITE;
    }

    return ret;
}

#else
IFAA_Result IFAA_WriteFile(const char *path, uint32_t path_len, const uint8_t *data, uint32_t len)
{
    
    CHECK_BOOL_RET_VAL(!path && path_len > 0
                       && !data && len > 0, IFAA_ERR_BAD_PARAM);

    TEE_ObjectHandle handle = NULL;
    TEE_Result ret;
    LOG_D("write_file: %s", path);
    LOG_D("strlen(path): %d", path_len);
    
    ret  = TEE_CreatePersistentObject(TEE_STORAGE_PRIVATE, path, path_len,
                                      /*TEE_DATA_FLAG_CREATE |*/ TEE_DATA_FLAG_ACCESS_WRITE,
                                      NULL, NULL, 0, &handle);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_CreatePersistentObject failed: 0x%08x", ret);
        return IFAA_ERR_WRITE;
    }
    
    ret = TEE_WriteObjectData(handle, data, len);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_WriteObjectData failed: 0x%08x", ret);
        ret = IFAA_ERR_WRITE;
    }
    
    TEE_CloseObject(handle);
    
    if (TEE_SUCCESS == ret) ret = IFAA_ERR_SUCCESS;
    return ret;
}
#endif

#ifdef TZ_MODEL_QCOM

IFAA_Result IFAA_ReadFile(const char *path, uint32_t path_len, uint8_t *data, uint32_t *len) {

    IFAA_Result ret = IFAA_ERR_SUCCESS;

    char buf_path[MAX_PATH_LEN] = {0};
    if (path_len > MAX_PATH_LEN) {
        LOG_E("IFAA_ReadFile ,can't use memcpy operation because of overflow.");
        return IFAA_ERR_BUF_TOO_SHORT;
    }
    memcpy(buf_path, path, path_len);
    LOG_D("read_file: %s", buf_path);

    int fd = qsee_sfs_open(buf_path, O_RDONLY);
    if (fd == 0) {
        int err = qsee_sfs_error(fd);
        LOG_E("qsee_sfs_open failed: 0x%08X", err);
        if (SFS_NO_FILE == err)
            return IFAA_ERR_NO_FILE;
        return IFAA_ERR_READ;
    }

    if (data != NULL && *len > 0) {
        int count = qsee_sfs_read(fd, (char *) data, (int) *len);
        if (count <= 0) {
            LOG_E("qsee_sfs_read failed: 0x%08X\n", qsee_sfs_error(fd));
            ret = IFAA_ERR_READ;
        } else {
            *len = count;
        }
    }

    int status = qsee_sfs_close(fd);
    if (status != 0) {
        LOG_E("qsee_sfs_close failed: 0x%08X", qsee_sfs_error(fd));
        ret = IFAA_ERR_READ;
    }

    return ret;
}

#else
IFAA_Result IFAA_ReadFile(const char *path, uint32_t path_len, uint8_t *data, uint32_t *len)
{
    
    CHECK_BOOL_RET_VAL(!path && path_len > 0
                       && !data && *len > 0, IFAA_ERR_BAD_PARAM);

    TEE_ObjectHandle handle = NULL;
    TEE_Result ret;
    LOG_D("read_file: %s", path);
    LOG_D("strlen(path): %d", path_len);
    
    ret = TEE_OpenPersistentObject(TEE_STORAGE_PRIVATE, path, path_len,
                                   TEE_DATA_FLAG_ACCESS_READ, &handle);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_OpenPersistentObject failed: 0x%08x", ret);
        return IFAA_ERR_READ;
    }
    
    ret = TEE_ReadObjectData(handle, data, *len, (uint32_t*)len);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_ReadObjectData failed: 0x%08x", ret);
        ret = IFAA_ERR_READ;
    }
    
    LOG_D("TEE_ReadObjectData len is %d", *len);
    TEE_CloseObject(handle);
    
    if (TEE_SUCCESS == ret) ret = IFAA_ERR_SUCCESS;    
    return ret;
}
#endif

#ifdef TZ_MODEL_QCOM

IFAA_Result IFAA_DeleteFile(const char *path, uint32_t path_len) {

    char buf_path[MAX_PATH_LEN] = {0};
    if (path_len > MAX_PATH_LEN) {
        LOG_E("IFAA_DeleteFile ,can't use memcpy operation because of overflow.");
        return IFAA_ERR_BUF_TOO_SHORT;
    }
    memcpy(buf_path, path, path_len);
    LOG_D("delete_file: %s", buf_path);

    int ret = qsee_sfs_rm(buf_path);
    if (ret != 0) {
        LOG_E("qsee_sfs_rm failed: 0x%08x", ret);
        return IFAA_ERR_ERASE;
    }

    return IFAA_ERR_SUCCESS;
}

#else
IFAA_Result IFAA_DeleteFile(const char *path, uint32_t path_len)
{

    CHECK_BOOL_RET_VAL(!path && path_len > 0, IFAA_ERR_BAD_PARAM);
    
    TEE_ObjectHandle    handle = NULL;
    TEE_Result          ret;
    LOG_D("delete_file: %s", path);
    
    ret = TEE_OpenPersistentObject(TEE_STORAGE_PRIVATE, path, path_len,
                                   TEE_DATA_FLAG_ACCESS_WRITE_META, &handle);
    if (ret != TEE_SUCCESS) {
        LOG_E("TEE_OpenPersistentObject failed: 0x%08x", ret);
        return IFAA_ERR_ERASE;
    }
    
    TEE_CloseAndDeletePersistentObject(handle);

    return IFAA_ERR_SUCCESS;
}
#endif

#ifdef TZ_MODEL_QCOM

IFAA_Result IFAA_ClearFiles() {
    uint32_t file_size = 0;
    sfs_file_entry *file_list = NULL;
    int ret = 0;
    size_t i = 0;

    if (0 != (ret = qsee_sfs_get_file_list(&file_list, &file_size))) {
        LOG_E("Getting File List Failed %d status", ret);
        return IFAA_ERR_GET_FILE_LIST_FAILED;
    }
    LOG_D("Cleanup of old files %u", file_size);

    for(i=0; i <file_size; i++)
    {
        LOG_D("FileName %s", file_list[i].file_name);
        qsee_sfs_rm(file_list[i].file_name);
    }
    qsee_sfs_clean_file_list(file_list);

    //qsee_sfs_rm(FILE_NAME);
    return IFAA_ERR_SUCCESS;

}

#else
IFAA_Result IFAA_ClearFiles()
{

}
#endif

#define CMD_PAIR(id) \
    {id, #id}

static ifaa_ta_cmd_pair_t IFAA_ta_cmd_pairs[] = {
    CMD_PAIR(IFAA_TA_CMD_GET_DEVICE_ID),
    CMD_PAIR(IFAA_TA_CMD_REGISTER),
    CMD_PAIR(IFAA_TA_CMD_AUTHENTICATE),
    CMD_PAIR(IFAA_TA_CMD_DEREGISTER),
    CMD_PAIR(IFAA_TA_CMD_QUERY_STATUS),
    CMD_PAIR(IFAA_TA_CMD_GEN_ASYMMETRIC_KEY),
    CMD_PAIR(IFAA_TA_CMD_GET_PROTOCOL_VERSION),

#ifndef IFBIO_RELEASE
    CMD_PAIR(IFAA_TA_CMD_GET_CERT_ALG_ENCODE),
    CMD_PAIR(IFAA_TA_CMD_READ_FILE),
    CMD_PAIR(IFAA_TA_CMD_WRITE_FILE),
    CMD_PAIR(IFAA_TA_CMD_DELETE_FILE),

    CMD_PAIR(IFAA_INTERNAL_GET_LAST_IDENTIFIED_RESULT),
    CMD_PAIR(IFAA_INTERNAL_GET_AUTHENTICATOR_VERSION),
    CMD_PAIR(IFAA_INTERNAL_GET_ID_LIST),
    CMD_PAIR(IFAA_INTERNAL_BIO_ID_COMPARE),

    CMD_PAIR(IFAA_INTERNAL_SHA256),
    CMD_PAIR(IFAA_INTERNAL_SIGN),
    CMD_PAIR(IFAA_INTERNAL_VERIFY),
    CMD_PAIR(IFAA_INTERNAL_KEY_GENERATE),
    CMD_PAIR(IFAA_INTERNAL_HMAC_SHA1),
    CMD_PAIR(IFAA_INTERNAL_AUTHENTICATOR_SIGN),
    CMD_PAIR(IFAA_INTERNAL_AUTHENTICATOR_VERIFY),

    CMD_PAIR(IFAA_TA_CMD_GET_SEC_DEVICE_ID),
    CMD_PAIR(IFAA_TA_CMD_CLEAR_SFS_FILE_LIST),
#endif

    CMD_PAIR(IFAA_TA_CMD_GET_SKPM_INJECT_STATE),
    CMD_PAIR(IFAA_TA_CMD_PROVISIONING_KEY_HANDLE),
    CMD_PAIR(IFAA_TA_CMD_GET_ID_LIST),
    CMD_PAIR(IFAA_TA_CMD_SET_ID_LIST),
};

const char *IFAA_getCmdString(ifaa_ta_cmd cmd){
    uint8_t len = sizeof(IFAA_ta_cmd_pairs)/sizeof(ifaa_ta_cmd_pair_t);
    const char *cmdString = NULL;
    for( uint8_t i = 0; i < len; i++){
        if(cmd == IFAA_ta_cmd_pairs[i].cmd_id){
            cmdString = IFAA_ta_cmd_pairs[i].cmd_string;
            break;
        }
    }

    return cmdString;
}
