#include <stdio.h>
#include <string.h>
#include <openssl/nid.h>
#include <openssl/ec.h>
#include <openssl/ec_key.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/digest.h>

#include "knoxai_io_datatypes.h"
#include "knoxai_logger.h"

#include <tee_internal_api.h>

#define IV_BLOCK_SIZE            16
#define ECIES_AUTH_ALGO_LENGTH   48 //SHA384
#define PUBKEY_SIZE              (KNOXAI_FAC_KEY_LEN*2+1)

//uint32_t wb_get(const uint8_t *id, uint32_t id_len, uint8_t *outData, uint32_t out_len);

#ifdef HARD_CODED
uint32_t getPublicFromPrivate(uint8_t *pri, uint32_t key_len, uint8_t *out_pubx, uint8_t *out_puby){
    uint8_t   pubx[] = { 
        (uint8_t)0xA6, (uint8_t)0xDF, (uint8_t)0x50, (uint8_t)0xA7, (uint8_t)0x03, (uint8_t)0x5F, (uint8_t)0x74, (uint8_t)0x01, (uint8_t)0xED, (uint8_t)0x18, (uint8_t)0x2D, (uint8_t)0x60, (uint8_t)0x32, (uint8_t)0xBA, (uint8_t)0xCC, (uint8_t)0xFC, 
        (uint8_t)0x9A, (uint8_t)0xF4, (uint8_t)0x5D, (uint8_t)0x33, (uint8_t)0x58, (uint8_t)0xE8, (uint8_t)0x80, (uint8_t)0x97, (uint8_t)0xCF, (uint8_t)0x1A, (uint8_t)0x12, (uint8_t)0x6C, (uint8_t)0xCF, (uint8_t)0x45, (uint8_t)0x36, (uint8_t)0xB6};
    uint8_t   puby[] = {
        (uint8_t)0x98, (uint8_t)0xD5, (uint8_t)0x31, (uint8_t)0xD0, (uint8_t)0xF1, (uint8_t)0xBB, (uint8_t)0x46, (uint8_t)0x2A, (uint8_t)0xB8, (uint8_t)0x64, (uint8_t)0xB6, (uint8_t)0xE1, (uint8_t)0x01, (uint8_t)0x15, (uint8_t)0x19, (uint8_t)0x15, 
        (uint8_t)0x7F, (uint8_t)0x16, (uint8_t)0x04, (uint8_t)0x12, (uint8_t)0x5E, (uint8_t)0xD1, (uint8_t)0xE1, (uint8_t)0xAC, (uint8_t)0xDC, (uint8_t)0x97, (uint8_t)0x79, (uint8_t)0x7E, (uint8_t)0x79, (uint8_t)0xD2, (uint8_t)0xE2, (uint8_t)0x9A};
    
    TEE_MemMove(out_pubx, pubx, key_len);
    TEE_MemMove(out_puby, puby, key_len);
    return 0;
}
#else
uint32_t getPublicFromPrivate(uint8_t *pri, uint32_t key_len, uint8_t *out_x, uint8_t *out_y) {  
    TEE_Result          rc;
    uint8_t             buffer[PUBKEY_SIZE]; // key_len * 2 + 1
    BIGNUM              bigPrivate;
    BIGNUM              *resPrivate = NULL;
    const EC_GROUP      *group = NULL;
    EC_POINT            *pub_key = NULL;
    EC_KEY              *key = NULL;
    uint32_t            ret_size=0;

    if ( key_len * 2 + 1 > PUBKEY_SIZE ) {
        return 1;
    }
    BN_init(&bigPrivate);
    resPrivate = BN_bin2bn(pri, key_len, &bigPrivate);
    if ( resPrivate == NULL ) {
        KNOXAI_LOG("BN_bin2bn output is wrong");
        rc = 2;  goto Exit;
    }
    if(NULL == (key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1))) {
       KNOXAI_LOG("EC_KEY_new_by_curve_name curve is err");
        rc = 3;  goto Exit;
    }
    rc = EC_KEY_set_private_key(key, resPrivate);
    if ( !rc ) {
        KNOXAI_LOG("TEE_BigIntConvertToOctetString output is wrong[%d]", rc);
        goto Exit;
    }
    group = EC_KEY_get0_group(key);
    if ( group == NULL ) {
        rc = 5;  goto Exit;
    }
    pub_key = EC_POINT_new(group);
    if ( pub_key == NULL ) {
        rc = 6;  goto Exit;
    }
    rc = EC_POINT_mul(group, pub_key, resPrivate, NULL, NULL, NULL);
    if ( !rc ) {
        KNOXAI_LOG("EC_POINT_point2oct mul is err[%d]", rc);
        goto Exit;
    }
    EC_KEY_set_public_key(key, pub_key);
    ret_size = EC_POINT_point2oct(group, pub_key, POINT_CONVERSION_UNCOMPRESSED, buffer, PUBKEY_SIZE, NULL);
    //KNOXAI_DBG_DUMP("public key", buffer, ret_size);
    if ( ret_size != PUBKEY_SIZE || buffer[0] != 0x04 ) {        
        KNOXAI_LOG("EC_POINT_point2oct ret size error[%d]", ret_size);
        rc = 10;  goto ExitInit;
    }
    TEE_MemMove(out_x, buffer+1, key_len);
    TEE_MemMove(out_y, buffer+1+key_len, key_len);
    rc = 0;
ExitInit:
    TEE_MemFill(buffer, 0, PUBKEY_SIZE);
Exit:
    if ( key != NULL ) EC_KEY_free(key);
    if ( pub_key != NULL ) EC_POINT_free(pub_key);
    if ( resPrivate ) BN_clear(resPrivate);
    return rc;
}
#endif
int isEC_Point(uint8_t *pubx, uint8_t *puby, uint32_t in_len) {
    TEE_Result          rc = TEE_ERROR_GENERIC;
    const EC_GROUP      *group = NULL;
    EC_POINT            *pub_key = NULL;
    EC_KEY              *key = NULL;
    uint8_t             buffer[KNOXAI_FAC_KEY_LEN*2+1];

    if ( KNOXAI_FAC_KEY_LEN < in_len ) {
        rc = 1; goto Exit;
    }
    if(NULL == (key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1))) {
       KNOXAI_LOG("EC_KEY_new_by_curve_name curve is err");
        rc = 3;  goto Exit;
    }
    group = EC_KEY_get0_group(key);
    if ( group == NULL ) {
        rc = 5;  goto Exit;
    }
    pub_key = EC_POINT_new(group);
    if ( pub_key == NULL ) {
        rc = 6;  goto Exit;
    }
    buffer[0] = 0x04;
    TEE_MemMove(buffer+1, pubx, KNOXAI_FAC_KEY_LEN);
    TEE_MemMove(buffer+1+KNOXAI_FAC_KEY_LEN, puby, KNOXAI_FAC_KEY_LEN);
    rc = EC_POINT_oct2point(group, pub_key, buffer, KNOXAI_FAC_KEY_LEN*2+1, NULL);
    if ( !rc ) {
        KNOXAI_LOG("EC_POINT_oct2point is err[%d]", rc);
        goto Exit;
    }
    // EC_POINT_is_on_curve returns one if |point| is an element of |group|
    rc = EC_POINT_is_on_curve(group, pub_key, NULL);
    if ( rc == 1) return 1;
Exit:
    if ( key != NULL ) EC_KEY_free(key);
    if ( pub_key != NULL ) EC_POINT_free(pub_key);
    KNOXAI_LOG("isPoint is err[%d]", rc);
    return 0;
}
uint32_t getSharedSecret(uint8_t *pri, uint8_t *pubx, uint8_t *puby, uint32_t in_len, uint8_t *output, uint32_t *out_len) {
    TEE_Result          rc;
    TEE_Attribute       attrs[4];
    TEE_OperationHandle hndl = NULL;
    TEE_ObjectHandle    obj_hndl_arr[2] = {0};
    const uint32_t      MAX_KEY_BIT = KNOXAI_FAC_KEY_LEN * 8;
    uint8_t             pubPairX[KNOXAI_FAC_KEY_LEN] = {0};
    uint8_t             pubPairY[KNOXAI_FAC_KEY_LEN] = {0};  
    
    if ( in_len != KNOXAI_FAC_KEY_LEN || pri == NULL || pubx == NULL || puby == NULL) {
        KNOXAI_LOG("getSharedSecret input is wrong");
        return KNOXAI_FAILURE;
    }
    if ( output == NULL || out_len == NULL ) {
        KNOXAI_LOG("getSharedSecret output is wrong");
        return KNOXAI_FAILURE;
    }
#if defined(ECC_IMPLEMENTATION) || defined(TEE_INCLUDE_DEPRECATED_ECC_IDENTIFIERS) // (depre TEE_ALG_ECDH_PXX in GP 1.1.1)
    rc = TEE_AllocateOperation(&hndl, TEE_ALG_ECDH_P256, TEE_MODE_DERIVE, MAX_KEY_BIT);
#else
    rc = TEE_AllocateOperation(&hndl, TEE_ALG_ECDH_DERIVE_SHARED_SECRET, TEE_MODE_DERIVE, MAX_KEY_BIT);
#endif
    if (rc) {
        KNOXAI_LOG("getSharedSecret_AllocateOperation is failed%d", rc);
        goto Exit;
    }
    // device key pair
    rc = TEE_AllocateTransientObject(TEE_TYPE_ECDH_KEYPAIR, MAX_KEY_BIT, &obj_hndl_arr[0]);
    if (rc) {
        KNOXAI_LOG("getSharedSecret_AllocateTransientObject is failed%d", rc);
        goto Exit;
    }
    rc = getPublicFromPrivate(pri, in_len, pubPairX, pubPairY);
    if (rc) {
        KNOXAI_LOG("cannot make(convert) public key from private[%d]", rc);
        goto Exit;
    }
    //KNOXAI_DBG_DUMP("public x", pubPairX, in_len);
    //KNOXAI_DBG_DUMP("public y", pubPairY, in_len);
    TEE_InitRefAttribute(&attrs[0], TEE_ATTR_ECC_PUBLIC_VALUE_X, pubPairX, in_len);
    TEE_InitRefAttribute(&attrs[1], TEE_ATTR_ECC_PUBLIC_VALUE_Y, pubPairY, in_len);
    TEE_InitRefAttribute(&attrs[2], TEE_ATTR_ECC_PRIVATE_VALUE, pri, in_len);
    TEE_InitValueAttribute(&attrs[3], TEE_ATTR_ECC_CURVE, TEE_ECC_CURVE_NIST_P256, 0);
    rc = TEE_PopulateTransientObject(obj_hndl_arr[0], attrs, 4);
    if (rc) {
        KNOXAI_LOG("getSharedSecret_GenerateKey is failed[%d]", rc);
        goto Exit;
    }
    rc = TEE_SetOperationKey(hndl, obj_hndl_arr[0]);
    if (rc) {
        KNOXAI_LOG("getSharedSecret_SetOperationKey is failed[%d]", rc);
        goto Exit;
    }
    rc = TEE_AllocateTransientObject(TEE_TYPE_GENERIC_SECRET, MAX_KEY_BIT, &obj_hndl_arr[1]);
    TEE_InitRefAttribute(&attrs[0], TEE_ATTR_ECC_PUBLIC_VALUE_X, pubx, in_len);
    TEE_InitRefAttribute(&attrs[1], TEE_ATTR_ECC_PUBLIC_VALUE_Y, puby, in_len);
    TEE_DeriveKey( hndl, attrs, 2, obj_hndl_arr[1]);
    size_t t_out_len = *out_len;
    rc = TEE_GetObjectBufferAttribute(obj_hndl_arr[1], TEE_ATTR_SECRET_VALUE, output, &t_out_len); 
    *out_len = (uint32_t)t_out_len;
    if (rc) {
        KNOXAI_LOG("getSharedSecret_GetObjectBufferAttribute is failed[%d]", rc);
        goto Exit;
    }
Exit:
    if (hndl) TEE_FreeOperation(hndl);
    for ( int i=0 ; i<2 ; i++ ) {
        if (obj_hndl_arr[i]) TEE_FreeTransientObject(obj_hndl_arr[i]);
    }
    return rc;
}
uint32_t getMacEncKey(uint8_t *pwd, uint32_t in_len, uint8_t *outMac, uint8_t *outEnc, uint32_t *out_len) {
    TEE_Result          rc;
    TEE_OperationHandle hndl = NULL;
    size_t              buffer_len = 64;
    uint8_t             buffer[64];
    uint8_t             C[4] = {0x0, 0x0, 0x0, 0x1};// ISO 18033 kdf1 => C = 0, kdf2 => C = 1

    if ( pwd == NULL || in_len != KNOXAI_FAC_KEY_LEN ) {
        KNOXAI_LOG("getMacEncKey input is wrong");
        return KNOXAI_FAILURE;
    }
    if ( outMac == NULL || outEnc == NULL || out_len == NULL ) {
        KNOXAI_LOG("getMacEncKey output is wrong");
        return KNOXAI_FAILURE;
    }
    rc = TEE_AllocateOperation(&hndl, TEE_ALG_SHA256, TEE_MODE_DIGEST, 0); // 256 bits
    if (rc) {
        KNOXAI_LOG("kdf_AllocateOperation is failed[%d] ", rc);
        goto Exit;
    }
    TEE_DigestUpdate(hndl, pwd, in_len);
    rc = TEE_DigestDoFinal(hndl, C, 4, buffer, &buffer_len);
    if (rc) {
        KNOXAI_LOG("kdf_DigestDoFinal1 is failed[%d]", rc);
        goto Exit;
    }
    if ( buffer_len == KNOXAI_FAC_KEY_LEN )
        TEE_MemMove(outEnc, buffer, buffer_len);
    //round2 mackey
    C[3]++;
    TEE_DigestUpdate(hndl, pwd, in_len);
    rc = TEE_DigestDoFinal(hndl, C, 4, buffer, &buffer_len);
    if (rc) {
        KNOXAI_LOG("kdf_DigestDoFinal2 is failed[%d]", rc);
        TEE_MemFill(outEnc, 0, KNOXAI_FAC_KEY_LEN);
        goto Exit;
    }
    if ( buffer_len == KNOXAI_FAC_KEY_LEN )
        TEE_MemMove(outMac, buffer, buffer_len);
    *out_len = buffer_len;

Exit:
    if (hndl) TEE_FreeOperation(hndl);
    return rc;
}
uint32_t knoxai_removepad(uint8_t *decMessage, uint32_t decMessage_len) {
    uint32_t new_len = decMessage_len;
    uint32_t block_size = IV_BLOCK_SIZE; //16 or 8
    uint8_t  nlast = 0;

    if ( decMessage_len % block_size != 0 ) {
        return decMessage_len;
    }
    nlast = decMessage[decMessage_len - 1];
    if (nlast <= block_size && nlast < decMessage_len && nlast > 0) {
        if (nlast == 0x01) {
            new_len--;
        } else if (decMessage[decMessage_len-nlast] == nlast) {
            int bcheck = false;
            for (int i = 0; i < nlast; i++) {
                if (decMessage[decMessage_len-nlast+i] == nlast) continue;
                bcheck = true;
                break;
            }
            if (bcheck) {
                KNOXAI_LOG("no pad or error"); // no pad or error
            }
            else {
                new_len -= nlast;
            }
        }
    }
    return new_len;
}
uint32_t compareAuthnDecrypt(uint8_t *encMessage, uint32_t encMessage_len,
        uint8_t *deckey, uint32_t decmac_len,
        uint8_t *IV, uint32_t iv_len,
        uint8_t *auth_tag, uint32_t auth_len, uint8_t *mackey,
        uint8_t *decMessage, uint32_t *decMessage_len) {
    TEE_Result          rc;
    TEE_Attribute       attrs[1];
    TEE_OperationHandle hndl = NULL, hndAES = NULL;
    TEE_ObjectHandle    obj_hndl = NULL, obj_hndAES = NULL;
    const uint32_t      MAX_KEY_BIT = KNOXAI_FAC_KEY_LEN * 8;

    if ( encMessage == NULL || encMessage_len < 1 || deckey == NULL ||
          decmac_len != KNOXAI_FAC_KEY_LEN || IV == NULL || iv_len < 1 || auth_tag == NULL || 
          auth_len != ECIES_AUTH_ALGO_LENGTH || mackey == NULL ) {
        KNOXAI_LOG("getMacEncKey input is wrong");
        return KNOXAI_FAILURE;
    }
    if ( decMessage == NULL || decMessage_len == NULL ) {
        KNOXAI_LOG("getMacEncKey output is wrong");
        return KNOXAI_FAILURE;
    }
    TEE_InitRefAttribute(&attrs[0], TEE_ATTR_SECRET_VALUE, mackey, decmac_len); // key length 32B = 256 bits
    rc = TEE_AllocateTransientObject(TEE_TYPE_HMAC_SHA384, MAX_KEY_BIT, &obj_hndl);
    if (rc) {
        KNOXAI_LOG("mac_AllocateTransientObject is failed[0x%x]", rc);
        goto Exit;
    }
    rc = TEE_PopulateTransientObject(obj_hndl, attrs, 1);
    if (rc) {
        KNOXAI_LOG("mac_PopulateTransientObject is failed[0x%x]", rc);
        goto Exit;
    }
    rc = TEE_AllocateOperation(&hndl, TEE_ALG_HMAC_SHA384, TEE_MODE_MAC, MAX_KEY_BIT); // key's bits 256
    if (rc) {
        KNOXAI_LOG("mac_AllocateOperation is failed[0x%x]", rc);
        goto Exit;
    }
    rc = TEE_SetOperationKey(hndl, obj_hndl);
    if (rc) {
        KNOXAI_LOG("mac_SetOperationKey is failed[0x%x]", rc);
        goto Exit;
    }
    TEE_MACInit(hndl, NULL, 0);// IV, iv_len);
    rc = TEE_MACCompareFinal(hndl, encMessage, encMessage_len, auth_tag, auth_len);
    if (rc) {
        KNOXAI_LOG("mac_MACCompareFinal is failed[0x%x]", rc);
        goto Exit;
    }
    // MAC ok -> DEC
    // TEE_ALG_AES_CBC_PKCS5(not GP) => NOPAD
    rc = TEE_AllocateOperation(&hndAES, TEE_ALG_AES_CBC_NOPAD, TEE_MODE_DECRYPT, MAX_KEY_BIT);
    if (rc) {
        KNOXAI_LOG("dec_AllocateOperation is failed[0x%x]", rc);
        goto Exit;
    };
    rc = TEE_AllocateTransientObject(TEE_TYPE_AES, MAX_KEY_BIT, &obj_hndAES);
    if (rc) {
        KNOXAI_LOG("dec_AllocateTransientObject is failed[0x%x]", rc);
        goto Exit;
    };
    TEE_InitRefAttribute(&attrs[0], TEE_ATTR_SECRET_VALUE, deckey, decmac_len);
    TEE_PopulateTransientObject(obj_hndAES, attrs, 1);
    rc = TEE_SetOperationKey(hndAES, obj_hndAES);
    if (rc) {
        KNOXAI_LOG("dec_SetOperationKey is failed[0x%x]", rc);
        goto Exit;
    };
    TEE_CipherInit(hndAES, IV, iv_len);
    
    size_t t_decMessage_len = *decMessage_len;
    rc = TEE_CipherDoFinal(hndAES, encMessage, encMessage_len, decMessage, &t_decMessage_len);
    *decMessage_len = (uint32_t)t_decMessage_len;
    
    if (rc) {
        KNOXAI_LOG("dec_CipherDoFinal is failed[0x%x]", rc);
        goto Exit;
    };
    // Remove padding
    *decMessage_len = knoxai_removepad(decMessage, *decMessage_len);
Exit:
    if (hndAES) TEE_FreeOperation(hndAES);
    if (obj_hndAES) TEE_FreeTransientObject(obj_hndAES);
    if (hndl) TEE_FreeOperation(hndl);
    if (obj_hndl) TEE_FreeTransientObject(obj_hndl);
    return rc;
}

uint32_t knoxai_ecies(uint8_t *kek, uint32_t kek_length, uint8_t *wrappedData, uint32_t wrappedDataLen, uint8_t *plainData, uint32_t* plainDataLen)
{
    uint32_t            ret = KNOXAI_FAILURE;
    // Internal val
    uint8_t             sharedSecret[KNOXAI_FAC_KEY_LEN];
    uint32_t            secret_len = KNOXAI_FAC_KEY_LEN;
    uint8_t             decKey[KNOXAI_FAC_KEY_LEN];
    uint8_t             macKey[KNOXAI_FAC_KEY_LEN];
    uint32_t            decmacKey_len = KNOXAI_FAC_KEY_LEN;
    
    // parse data
    uint8_t             decMsg[KNOXAI_FAC_KEY_LEN*2] = {0};
    uint32_t            decMsg_len = KNOXAI_FAC_KEY_LEN*2;
    uint8_t             encMsg[KNOXAI_FAC_KEY_LEN*2] = {0};
    uint32_t            encMsg_len = KNOXAI_FAC_KEY_LEN;
    uint8_t             authTag[ECIES_AUTH_ALGO_LENGTH] = {0};
    uint32_t            auth_len = ECIES_AUTH_ALGO_LENGTH;
    uint8_t             pubx[KNOXAI_FAC_KEY_LEN] = {0};
    uint8_t             puby[KNOXAI_FAC_KEY_LEN] = {0};
    uint8_t             IV[IV_BLOCK_SIZE] = {0};
    uint32_t            iv_len = IV_BLOCK_SIZE;
    uint32_t            ref_next =0;
    uint8_t             Nbuffer[4] = {0};
    uint32_t            Nindex = 0;
    
    if ( wrappedData == NULL || plainData == NULL || plainDataLen== NULL ) {
        ret = KNOXAI_FAILURE;
        KNOXAI_LOG("knoxai_ecies input data wrong 1");
        goto Exit;
    }
    if ( *plainDataLen < decMsg_len || wrappedDataLen < (32+32+2+2+16+48+16) ) {
        ret = KNOXAI_FAILURE;
        KNOXAI_LOG("knoxai_ecies data length of in(%d)/out(%d) are wrong", wrappedDataLen, *plainDataLen);
        goto Exit;
    }
    if ( kek == NULL || kek_length != KNOXAI_FAC_KEY_LEN ) {
        KNOXAI_LOG("knoxai_ecies input data wrong 4");
        ret = KNOXAI_FAILURE;
        goto Exit;
    }
    // from sendmsg - kek(pri), pubx, puby, N, encMsg(C), authTag, IV
    /*
    uint8_t   uid[] = "*KNOX System TA*";
    uint32_t  uid_length = strnlen(uid, 16);
    uint8_t   kek[] = "000000000900000000080000000007654";
    uint32_t  kek_length = KNOXAI_FAC_KEY_LEN;
    ret = wb_get(uid, uid_length, kek, kek_length);
    if (ret != 0) {        
        ret = KNOXAI_FAC_WB_ERR;
        goto Exit;
    }
    //hexdump(kek, "pri", kek_length);
    uint8_t   wrappedData[] = {
                (uint8_t)0x7e, (uint8_t)0x56, (uint8_t)0xdd, (uint8_t)0x7e, (uint8_t)0x50, (uint8_t)0x18, (uint8_t)0x90, (uint8_t)0x59, (uint8_t)0x6a, (uint8_t)0x8f, (uint8_t)0x72, (uint8_t)0xb0, (uint8_t)0x52, (uint8_t)0x3f, (uint8_t)0xe6, (uint8_t)0xce,
                (uint8_t)0x77, (uint8_t)0x18, (uint8_t)0xf7, (uint8_t)0xfd, (uint8_t)0xad, (uint8_t)0x25, (uint8_t)0xca, (uint8_t)0x0c, (uint8_t)0x5a, (uint8_t)0xf8, (uint8_t)0xd0, (uint8_t)0xe7, (uint8_t)0x89, (uint8_t)0x92, (uint8_t)0x7c, (uint8_t)0xe4,
                (uint8_t)0x75, (uint8_t)0x35, (uint8_t)0x9e, (uint8_t)0xf2, (uint8_t)0xa2, (uint8_t)0xe4, (uint8_t)0x2c, (uint8_t)0xf0, (uint8_t)0x07, (uint8_t)0xde, (uint8_t)0x29, (uint8_t)0x46, (uint8_t)0xcb, (uint8_t)0xb5, (uint8_t)0xa2, (uint8_t)0x20,
                (uint8_t)0x38, (uint8_t)0x03, (uint8_t)0x01, (uint8_t)0x0a, (uint8_t)0x0f, (uint8_t)0x10, (uint8_t)0x88, (uint8_t)0xb1, (uint8_t)0x9d, (uint8_t)0x9e, (uint8_t)0xef, (uint8_t)0xc2, (uint8_t)0x6d, (uint8_t)0x05, (uint8_t)0xf8, (uint8_t)0x93,
                (uint8_t)0x00, (uint8_t)0x01, (uint8_t)0x00, (uint8_t)0x20,
                (uint8_t)0xeb, (uint8_t)0x8f, (uint8_t)0x41, (uint8_t)0x52, (uint8_t)0xcf, (uint8_t)0xc6, (uint8_t)0x79, (uint8_t)0xb3, (uint8_t)0x5a, (uint8_t)0x97, (uint8_t)0x87, (uint8_t)0x32, (uint8_t)0x7e, (uint8_t)0x59, (uint8_t)0x18, (uint8_t)0x6d,
                (uint8_t)0xeb, (uint8_t)0x8f, (uint8_t)0x41, (uint8_t)0x52, (uint8_t)0xcf, (uint8_t)0xc6, (uint8_t)0x79, (uint8_t)0xb3, (uint8_t)0x5a, (uint8_t)0x97, (uint8_t)0x87, (uint8_t)0x32, (uint8_t)0x7e, (uint8_t)0x59, (uint8_t)0x18, (uint8_t)0x6d,
                (uint8_t)0xe6, (uint8_t)0x7f, (uint8_t)0xc6, (uint8_t)0x17, (uint8_t)0x87, (uint8_t)0x58, (uint8_t)0x69, (uint8_t)0xfc, (uint8_t)0xa6, (uint8_t)0x03, (uint8_t)0xfb, (uint8_t)0xe4, (uint8_t)0x0f, (uint8_t)0x92, (uint8_t)0x9d, (uint8_t)0x69,
                (uint8_t)0x95, (uint8_t)0xf0, (uint8_t)0x11, (uint8_t)0xfe, (uint8_t)0xcf, (uint8_t)0xd1, (uint8_t)0xc2, (uint8_t)0x11, (uint8_t)0x3f, (uint8_t)0xb5, (uint8_t)0xd9, (uint8_t)0x8f, (uint8_t)0xf6, (uint8_t)0x6d, (uint8_t)0x1c, (uint8_t)0x75,
                (uint8_t)0xa0, (uint8_t)0x72, (uint8_t)0xb4, (uint8_t)0x27, (uint8_t)0xb5, (uint8_t)0xbc, (uint8_t)0xdb, (uint8_t)0x10, (uint8_t)0x9c, (uint8_t)0x51, (uint8_t)0x31, (uint8_t)0x62, (uint8_t)0x07, (uint8_t)0xbe, (uint8_t)0x69, (uint8_t)0xa6,
                (uint8_t)0xdc, (uint8_t)0x68, (uint8_t)0x31, (uint8_t)0x0c, (uint8_t)0xa9, (uint8_t)0xaa, (uint8_t)0x46, (uint8_t)0x36, (uint8_t)0x39, (uint8_t)0x92, (uint8_t)0x77, (uint8_t)0xae, (uint8_t)0x5f, (uint8_t)0xd2, (uint8_t)0xb9, (uint8_t)0x05};
    */
// ********** data parse
// **** pubx    | puby   | N      | cipher len |cipher text       | auth data | IV 
// **** 32bytes |32bytes | 2bytes | 2 bytes    | Mbytes(min 16bytes) |48bytes    |16bytes = 132+M
    KNOXAI_DBG_DUMP("wrappedData", wrappedData, wrappedDataLen);
    ref_next = 0;
    TEE_MemMove(pubx   , wrappedData + ref_next, KNOXAI_FAC_KEY_LEN);
    ref_next += KNOXAI_FAC_KEY_LEN;
    TEE_MemMove(puby   , wrappedData + ref_next, KNOXAI_FAC_KEY_LEN);
    ref_next += KNOXAI_FAC_KEY_LEN;
    TEE_MemMove(Nbuffer , wrappedData + ref_next, 4);
    Nindex = (uint32_t)(Nbuffer[0]<<8) + (uint32_t)Nbuffer[1];
    encMsg_len = (uint32_t)(Nbuffer[2]<<8) + (uint32_t)Nbuffer[3];
    KNOXAI_LOG("ECIES, Nindex=%d, [0x%x,0x%x], [0x%x,0x%x]", Nindex, Nbuffer[0], Nbuffer[1], Nbuffer[2], Nbuffer[3]);
    ref_next += 4;
    if ( encMsg_len > KNOXAI_FAC_KEY_LEN*2 || encMsg_len < IV_BLOCK_SIZE ) {
        ret = KNOXAI_FAILURE;
        goto Exit;
    }
    TEE_MemMove(encMsg , wrappedData + ref_next, encMsg_len);
    ref_next += encMsg_len;
    TEE_MemMove(authTag, wrappedData + ref_next, ECIES_AUTH_ALGO_LENGTH);
    ref_next += ECIES_AUTH_ALGO_LENGTH;
    TEE_MemMove(IV     , wrappedData + ref_next, IV_BLOCK_SIZE);

    KNOXAI_DBG_DUMP("enc", encMsg, encMsg_len);
    KNOXAI_DBG_DUMP("IV", IV, IV_BLOCK_SIZE);
// ********** pub key check
    if ( !isEC_Point(pubx, puby, kek_length ) ) {
        KNOXAI_LOG("knoxai_ecies input data wrong 7");
        ret = KNOXAI_FAILURE;
        goto Exit;
    }
// ********** generate pri/pub index
    if ( Nindex > 0 ) {
        uint8_t   key[KNOXAI_FAC_KEY_LEN] = {0};
        uint32_t  keyLength = kek_length;
        uint8_t   uid[] = "*KNOX System TA*";
        uint32_t  uid_length = strnlen((const char*)uid, 16);
        if(!PKCS5_PBKDF2_HMAC((const char *)kek, kek_length, (const unsigned char *)uid, uid_length,
                                Nindex, EVP_sha256(),
                                keyLength, key)) {
            KNOXAI_LOG("PKCS5_PBKDF2_HMAC(pwd,salt, out) failed");
            ret = KNOXAI_FAILURE;
            goto Exit;
        }
        if ( keyLength != KNOXAI_FAC_KEY_LEN) {
            KNOXAI_LOG("PKCS5_PBKDF2_HMAC(key length wrong) failed");
            ret = KNOXAI_FAILURE;
            goto Exit;
        }
        TEE_MemMove(kek, key, KNOXAI_FAC_KEY_LEN);
    }
// ********** ECDH
    if (getSharedSecret(kek, pubx, puby, kek_length, sharedSecret, &secret_len) != 0) {
        ret = KNOXAI_FAILURE;
        goto Exit;
    }
    KNOXAI_DBG_DUMP("ECDH", sharedSecret, secret_len);
// ********** KDF
    if (getMacEncKey(sharedSecret, secret_len, macKey, decKey, &decmacKey_len) != 0) {
        ret = KNOXAI_FAILURE;
        goto Exit;
    }
    KNOXAI_DBG_DUMP("DECKey", decKey, decmacKey_len);
    KNOXAI_DBG_DUMP("MACKey", macKey, decmacKey_len);
// ********** Check Auth tag (MAC) &  decrypt - AES CBC
    if (compareAuthnDecrypt(encMsg, encMsg_len, decKey, decmacKey_len, IV, iv_len, 
          authTag, auth_len, macKey, decMsg, &decMsg_len) != 0) {
        ret = KNOXAI_FAILURE;
        goto Exit;
    }
    KNOXAI_DBG_DUMP("decryptedMessage", decMsg, decMsg_len);
// **** results
    ret = KNOXAI_SUCCESS;
    TEE_MemMove(plainData, decMsg, decMsg_len);
    *plainDataLen = decMsg_len;
    KNOXAI_LOG("ECIES success, dec len=%d", decMsg_len);
Exit:
    TEE_MemFill(sharedSecret, 0x00, sizeof(sharedSecret));
    TEE_MemFill(decKey, 0x00, sizeof(decKey));
    TEE_MemFill(macKey, 0x00, sizeof(macKey));
    TEE_MemFill(kek, 0x00, sizeof(kek));
    //parse data
    TEE_MemFill(decMsg, 0x00, sizeof(decMsg));        
    TEE_MemFill(encMsg, 0x00, sizeof(encMsg));
    TEE_MemFill(authTag, 0x00, sizeof(authTag));
    TEE_MemFill(pubx, 0x00, sizeof(pubx));
    TEE_MemFill(puby, 0x00, sizeof(puby));
    TEE_MemFill(IV, 0x00, sizeof(IV));
    return ret;
}
