#include <string.h>

#include "basedef.h"
#include "dbg.h"
#include "util.h"
#include "tal_operation.h"
#include "cmd_crypto.h"
#include "parser_tlv.h"
#include "cmd_registry.h"
#include "tlv_parser.h"
#include "parser_certificate.h"
#include "cmd_common.h"


void _getMsg(uint8* pInput, TlvRsaDecryptedData* pTlvRsaDecryptedData, BlobData* pBlobMsgVersion, BlobData* pBlobMsgHM, BlobData* pBlobMsgHmacKey, BlobData* pBlobMsgAesKey)
{
    pBlobMsgVersion->data = pInput + pTlvRsaDecryptedData->msgVersion.offset;
    pBlobMsgVersion->dataSize = pTlvRsaDecryptedData->msgVersion.length;
    pBlobMsgVersion->length = pBlobMsgVersion->dataSize;

    pBlobMsgHM->data = pInput + pTlvRsaDecryptedData->msgHM.offset;
    pBlobMsgHM->dataSize = pTlvRsaDecryptedData->msgHM.length;
    pBlobMsgHM->length = pBlobMsgHM->dataSize;

    pBlobMsgHmacKey->data = pInput + pTlvRsaDecryptedData->msgHmacKey.offset;
    pBlobMsgHmacKey->dataSize = pTlvRsaDecryptedData->msgHmacKey.length;
    pBlobMsgHmacKey->length = pBlobMsgHmacKey->dataSize;

    pBlobMsgAesKey->data = pInput + pTlvRsaDecryptedData->msgAesKey.offset;
    pBlobMsgAesKey->dataSize = pTlvRsaDecryptedData->msgAesKey.length;
    pBlobMsgAesKey->length = pBlobMsgAesKey->dataSize;
}


boolean allocKey(const uint32 size, TAL_Key* pTalKey)
{
    uint32 expLen = sizeof(g_rsaExponent);

    printD("start");

    if (pTalKey == NULL || pTalKey->cryptoType == SIGNATURE_TYPE_NONE) {
        printE("key is NULL");
        return FALSE;
    }

    switch (pTalKey->cryptoType) {
        case SIGNATURE_TYPE_RSA_SHA256_PSS:
        case SIGNATURE_TYPE_RSA_SHA256_PKCS1:
        case SIGNATURE_TYPE_RSA_SHA384_PKCS1:
        case CIPHER_TYPE_RSA_SHA256_OAEP:
            pTalKey->key.rsa.exponent.value = gTal.malloc(expLen + (4 % expLen));  //4byte align
            if(pTalKey->key.rsa.exponent.value == NULL) {
                printE("exponent malloc failed");
                freeKey(pTalKey);
                return FALSE;
            }
            memset(pTalKey->key.rsa.exponent.value, 0, expLen + (4 % expLen));
            pTalKey->key.rsa.exponent.length = 0;

            pTalKey->key.rsa.modulus.value = gTal.malloc(size);
            if(pTalKey->key.rsa.modulus.value == NULL) {
                printE("modulus malloc failed");
                freeKey(pTalKey);
                return FALSE;
            }
            memset(pTalKey->key.rsa.modulus.value, 0, size);
            pTalKey->key.rsa.modulus.length = 0;

            pTalKey->key.rsa.privateExponent.value = gTal.malloc(size);
            if(pTalKey->key.rsa.privateExponent.value == NULL) {
                printE("privateExponent malloc failed");
                freeKey(pTalKey);
                return FALSE;
            }
            memset(pTalKey->key.rsa.privateExponent.value, 0, size);
            pTalKey->key.rsa.privateExponent.length = 0;
            break;
        case CIPHER_TYPE_AES_CBC_PKCS5:
            pTalKey->key.aes.key.value = gTal.malloc(size);
            if(pTalKey->key.aes.key.value == NULL) {
                printE("key malloc failed");
                freeKey(pTalKey);
                return FALSE;
            }
            memset(pTalKey->key.aes.key.value, 0, size);
            pTalKey->key.aes.key.length = 0;

            pTalKey->key.aes.iv.value = gTal.malloc(size);
            if(pTalKey->key.aes.iv.value == NULL) {
                printE("iv malloc failed");
                freeKey(pTalKey);
                return FALSE;
            }
            memset(pTalKey->key.aes.iv.value, 0, size);
            pTalKey->key.aes.iv.length = 0;
            break;
        default:
            printE("unknown key type");
            break;
    }

    printD("end");
    return TRUE;
}


void freeKey(TAL_Key* pTalKey)
{
    printD("start");

    if (pTalKey == NULL || pTalKey->cryptoType == SIGNATURE_TYPE_NONE) {
        printE("key is NULL");
        return;
    }

    switch (pTalKey->cryptoType) {
        case SIGNATURE_TYPE_RSA_SHA256_PSS:
        case SIGNATURE_TYPE_RSA_SHA256_PKCS1:
        case SIGNATURE_TYPE_RSA_SHA384_PKCS1:
        case CIPHER_TYPE_RSA_SHA256_OAEP:
            if(pTalKey->key.rsa.exponent.value != NULL) {
                gTal.free(pTalKey->key.rsa.exponent.value, pTalKey->key.rsa.exponent.length);
                pTalKey->key.rsa.exponent.value = NULL;
                pTalKey->key.rsa.exponent.length = 0;
            }
            if(pTalKey->key.rsa.modulus.value != NULL) {
                gTal.free(pTalKey->key.rsa.modulus.value, pTalKey->key.rsa.modulus.length);
                pTalKey->key.rsa.modulus.value = NULL;
                pTalKey->key.rsa.modulus.length = 0;
            }
            if(pTalKey->key.rsa.privateExponent.value != NULL) {
                gTal.free(pTalKey->key.rsa.privateExponent.value, pTalKey->key.rsa.privateExponent.length);
                pTalKey->key.rsa.privateExponent.value = NULL;
                pTalKey->key.rsa.privateExponent.length = 0;
            }
            break;
        case CIPHER_TYPE_AES_CBC_PKCS5:
            if(pTalKey->key.aes.key.value != NULL) {
                gTal.free(pTalKey->key.aes.key.value, pTalKey->key.aes.key.length);
                pTalKey->key.aes.key.value = NULL;
                pTalKey->key.aes.key.length = 0;
            }

            if(pTalKey->key.aes.iv.value != NULL) {
                gTal.free(pTalKey->key.aes.iv.value, pTalKey->key.aes.iv.length);
                pTalKey->key.aes.iv.value = NULL;
                pTalKey->key.aes.iv.length = 0;
            }
            break;
        default:
            printE("unknown key type");
            break;
    }

    printD("end");
}



AuthnrResult processDecryption(const BlobData* pBlobInput, BlobData* pBlobOutput)
{
    AuthnrResult ret = AUTHNR_SUCCESS;
    boolean retBool = TRUE;

    TlvDecryptionCommand tlvDecryptionCommand = {0};
    TlvRsaDecryptedData tlvRsaDecryptedData = {0};
    TlvEncodeData tlvOutput = {0};

    BlobData blobTmpIssuer = {0};
    BlobData blobTmpSubject = {0};
    TlvCertificate tlvIssuerCertificate = {0};
    TlvCertificate tlvSubjectCertificate = {0};

    BlobData blobWrappedKeyHandle = {0};
    BlobData blobKeyHandle = {0};

    BlobData blobTmpBuf = {0};

    TAL_Key rsaPrivKey = {.cryptoType = CIPHER_TYPE_RSA_SHA256_OAEP};
    TAL_Key aesKey = {.cryptoType = CIPHER_TYPE_AES_CBC_PKCS5};

    BlobData blobRsaEncData = {0, }, blobAesEncData = {0, };
    BlobData blobRsaPlainData = {0, }, blobAesPlainData = {0, };
    BlobData blobMsgVersion = {0, }, blobMsgHM = {0, }, blobMsgHmacKey = {0, }, blobMsgAesKey = {0, };

    uint32 rsaKeySize = 256;
    uint32 aesKeySize = 32;
    uint32 aesIvSize = 16;

    uint16 tempUint16 = 0;
    uint32 pos = 0;
    uint32 peLen = 0; //length of privateExponent

    printI("dc");

    CHECK_BLOB_POINTER(pBlobInput, AUTHNR_ERROR_PARAM);
    CHECK_BLOB_POINTER(pBlobOutput, AUTHNR_ERROR_PARAM);

    do {
        // Input validation
        if (pBlobInput->dataSize > MAX_GENERAL_SIGNAL_LENGTH ||
                pBlobOutput->dataSize > MAX_GENERAL_SIGNAL_LENGTH) {
            printE("input validation failed");
            ret = AUTHNR_ERROR_PARAM;
            break;
        }

        // Init output
        secure_zero_memory(pBlobOutput->data, pBlobOutput->dataSize);
        pBlobOutput->length = 0;

        // Parsing Input
        retBool = parseDecryptionCommand(pBlobInput, &tlvDecryptionCommand);
        if (retBool != TRUE || tlvDecryptionCommand.tag != TAG_TAD_DECRYPTION_COMMAND) {
            printE("failed to parse TAG_TAD_DECRYPTION_COMMAND");
            return AUTHNR_ERROR_PARSING;
        }
#ifdef USE_DUMP_DATA
        {
            printD("tlvDecryptionCommand - tag:%x, length:%d, offset:%d", tlvDecryptionCommand.tag, tlvDecryptionCommand.length, tlvDecryptionCommand.offset);
            printD("tlvDecryptionCommand.sub1Certificate - tag:%x, length:%d, offset:%d", tlvDecryptionCommand.sub1Certificate.tag, tlvDecryptionCommand.sub1Certificate.length, tlvDecryptionCommand.sub1Certificate.offset);
            printD("tlvDecryptionCommand.leafCertificate - tag:%x, length:%d, offset:%d", tlvDecryptionCommand.leafCertificate.tag, tlvDecryptionCommand.leafCertificate.length, tlvDecryptionCommand.leafCertificate.offset);
            printD("tlvDecryptionCommand.rsaPrivateKeyHandle - tag:%x, length:%d, offset:%d", tlvDecryptionCommand.rsaPrivateKeyHandle.tag, tlvDecryptionCommand.rsaPrivateKeyHandle.length, tlvDecryptionCommand.rsaPrivateKeyHandle.offset);
            printD("tlvDecryptionCommand.rsaEncryptedData - tag:%x, length:%d, offset:%d", tlvDecryptionCommand.rsaEncryptedData.tag, tlvDecryptionCommand.rsaEncryptedData.length, tlvDecryptionCommand.rsaEncryptedData.offset);
            printD("tlvDecryptionCommand.aesEncryptedData - tag:%x, length:%d, offset:%d", tlvDecryptionCommand.aesEncryptedData.tag, tlvDecryptionCommand.aesEncryptedData.length, tlvDecryptionCommand.aesEncryptedData.offset);
        }
#endif

        // 1. Get SKPM RSA PrivKey
        // 1-1. cert chain validation
        // RootCA Certificate self validation
        printD("RootCA Certificate self validation");
        blobTmpIssuer.data = (uint8*) g_rcac;
        blobTmpIssuer.dataSize = sizeof(g_rcac);
        blobTmpIssuer.length = blobTmpIssuer.dataSize;
        retBool = parseCertificate(&blobTmpIssuer, 0, &tlvIssuerCertificate);
        if (retBool != TRUE || tlvIssuerCertificate.tag != 0x30) {
            printE("tag is not correct. tlvIssuerCertificate");
            ret = AUTHNR_ERROR_PARSING;
            break;
        }

        blobTmpSubject.data = (uint8*) g_rcac;
        blobTmpSubject.dataSize = sizeof(g_rcac);
        blobTmpSubject.length = blobTmpSubject.dataSize;
        retBool = parseCertificate(&blobTmpSubject, 0, &tlvSubjectCertificate);
        if (retBool != TRUE || tlvSubjectCertificate.tag != 0x30) {
            printE("tag is not correct. tlvSubjectCertificate");
            ret = AUTHNR_ERROR_PARSING;
            break;
        }

        ret = verifyCertificatePath(&blobTmpIssuer, &tlvIssuerCertificate, &blobTmpSubject, &tlvSubjectCertificate);
        if (ret != AUTHNR_SUCCESS) {
            printD("rcvf");
            ret = AUTHNR_ERROR_CERTIFICATE_PATH;
            break;
        }
        printD("RootCA Certificate self validation success");

        // RootCA - Sub1 Certificate validation
        printD("RootCA - Sub1 Certificate validation");
        blobTmpIssuer.data = (uint8*) g_rcac;
        blobTmpIssuer.dataSize = sizeof(g_rcac);
        blobTmpIssuer.length = blobTmpIssuer.dataSize;
        retBool = parseCertificate(&blobTmpIssuer, 0, &tlvIssuerCertificate);
        if (retBool != TRUE || tlvIssuerCertificate.tag != 0x30) {
            printE("tag is not correct. tlvIssuerCertificate");
            ret = AUTHNR_ERROR_PARSING;
            break;
        }

        blobTmpSubject.data = pBlobInput->data + tlvDecryptionCommand.sub1Certificate.offset;
        blobTmpSubject.dataSize = tlvDecryptionCommand.sub1Certificate.length;
        blobTmpSubject.length = blobTmpSubject.dataSize;
        retBool = parseCertificate(&blobTmpSubject, 0, &tlvSubjectCertificate);
        if (retBool != TRUE || tlvSubjectCertificate.tag != 0x30) {
            printE("tag is not correct. tlvSubjectCertificate");
            ret = AUTHNR_ERROR_PARSING;
            break;
        }

        ret = verifyCertificatePath(&blobTmpIssuer, &tlvIssuerCertificate, &blobTmpSubject, &tlvSubjectCertificate);
        if (ret != AUTHNR_SUCCESS) {
            printE("s1vf");
            ret = AUTHNR_ERROR_CERTIFICATE_PATH;
            break;
        }
        printD("RootCA - Sub1 Certificate validation success");

        // Sub1 - Leaf Certificate validation
        printD("Sub1 - Leaf Certificate validation");
        blobTmpIssuer.data = pBlobInput->data + tlvDecryptionCommand.sub1Certificate.offset;
        blobTmpIssuer.dataSize = tlvDecryptionCommand.sub1Certificate.length;
        blobTmpIssuer.length = blobTmpIssuer.dataSize;
        retBool = parseCertificate(&blobTmpIssuer, 0, &tlvIssuerCertificate);
        if (retBool != TRUE || tlvIssuerCertificate.tag != 0x30) {
            printE("tag is not correct. tlvIssuerCertificate");
            ret = AUTHNR_ERROR_PARSING;
            break;
        }

        blobTmpSubject.data = pBlobInput->data + tlvDecryptionCommand.leafCertificate.offset;
        blobTmpSubject.dataSize = tlvDecryptionCommand.leafCertificate.length;
        blobTmpSubject.length = blobTmpSubject.dataSize;
        retBool = parseCertificate(&blobTmpSubject, 0, &tlvSubjectCertificate);
        if (retBool != TRUE || tlvSubjectCertificate.tag != 0x30) {
            printE("tag is not correct. tlvSubjectCertificate");
            ret = AUTHNR_ERROR_PARSING;
            break;
        }

        ret = verifyCertificatePath(&blobTmpIssuer, &tlvIssuerCertificate, &blobTmpSubject, &tlvSubjectCertificate);
        if (ret != AUTHNR_SUCCESS) {
            printE("lfvf");
            ret = AUTHNR_ERROR_CERTIFICATE_PATH;
            break;
        }
        printD("Sub1 - Leaf Certificate validation success");

        // 1-2. Key Handle validation
        blobWrappedKeyHandle.data = pBlobInput->data + tlvDecryptionCommand.rsaPrivateKeyHandle.offset;
        blobWrappedKeyHandle.dataSize = tlvDecryptionCommand.rsaPrivateKeyHandle.length;
        blobWrappedKeyHandle.length = blobWrappedKeyHandle.dataSize;

        blobKeyHandle.length = 0;
        blobKeyHandle.dataSize = blobWrappedKeyHandle.dataSize;
        blobKeyHandle.data = gTal.malloc(blobKeyHandle.dataSize);
        if (blobKeyHandle.data == NULL){
            printE("blobKeyHandle.data malloc fail");
            ret = AUTHNR_ERROR_MALLOC;
            break;
        }

        blobTmpBuf.dataSize = 1024;
        blobTmpBuf.data = gTal.malloc(blobTmpBuf.dataSize);
        if (blobTmpBuf.data == NULL){
            printE("blobTmpBuf.data malloc fail");
            ret = AUTHNR_ERROR_MALLOC;
            break;
        }
        blobTmpBuf.length = blobTmpBuf.dataSize;

        retBool = gTal.unwrap(&blobWrappedKeyHandle, &blobKeyHandle);
        if (retBool != TRUE) {
            printE("unwrap error. RSA Key Handle");
            ret = AUTHNR_ERROR_UNWRAP;
            break;
        }

        // KEY_HANDLE : | MAGIC_NUMBER | VERSION | ACCESS_TOKEN | PRIVATE_KEY |
        // ACCESS_TOKEN : hash(CERTIFICATE)
        // magic number validation
        retBool = array_to_uint16(&tempUint16, blobKeyHandle.data);
        if (retBool != TRUE) {
            printE("get value failed");
            ret = AUTHNR_ERROR_GENERAL;
            break;
        }
        if (tempUint16 != SKPM_KEY_HANDLE_MAGIC_NUMBER) {
            printE("magic number error");
            ret = AUTHNR_ERROR_ACCESS_DENIED;
            break;
        }
        pos = SKPM_KEY_HANDLE_MAGIC_NUMBER_LENGTH;

        // version validation
        retBool = array_to_uint16(&tempUint16, blobKeyHandle.data + pos);
        if (retBool != TRUE) {
            printE("get value failed");
            ret = AUTHNR_ERROR_GENERAL;
            break;
        }
        if (tempUint16 != SKPM_KEY_HANDLE_VERSION) {
            printE("version error");
            ret = AUTHNR_ERROR_ACCESS_DENIED;
            break;
        }
        pos += SKPM_KEY_HANDLE_VERSION_LENGTH;

        // access token validation
        retBool = gTal.hash(&blobTmpSubject, HASH_TYPE_SHA256, &blobTmpBuf); //blobTmpSubject <- leaf cert
        if (retBool != TRUE) {
            printE("hash error");
            ret = AUTHNR_ERROR_HASH;
            break;
        }

        if (memcmp(blobKeyHandle.data + pos, blobTmpBuf.data, SKPM_KEY_HANDLE_ACCESS_TKOEN_LENGTH) != 0) {
            printE("access token error");
            ret = AUTHNR_ERROR_ACCESS_DENIED;
            break;
        }
        pos += SKPM_KEY_HANDLE_ACCESS_TKOEN_LENGTH;


        // 1-3. get RSA Private Key
        retBool = allocKey(rsaKeySize, &rsaPrivKey);
        if(retBool != TRUE) {
            printE("gTal.rsa_allocKey failed");
            ret = AUTHNR_ERROR_ALLOC_KEY;
            break;
        }

        rsaPrivKey.key.rsa.exponent.length = sizeof(g_rsaExponent);
        memcpy(rsaPrivKey.key.rsa.exponent.value, g_rsaExponent, rsaPrivKey.key.rsa.exponent.length);

        rsaPrivKey.key.rsa.modulus.length = rsaKeySize;
        memcpy(rsaPrivKey.key.rsa.modulus.value, blobKeyHandle.data + pos, rsaPrivKey.key.rsa.modulus.length);
        pos += rsaPrivKey.key.rsa.modulus.length;

        retBool = array_to_uint16(&tempUint16, blobKeyHandle.data + pos);
        if (retBool != TRUE) {
            printE("get length of privateExponent failed");
            ret = AUTHNR_ERROR_GENERAL;
            break;
        }
        peLen = (uint32)tempUint16;
        pos += KEYHANDLE_KEY_LENGTH_LENGTH;

        rsaPrivKey.key.rsa.privateExponent.length = peLen;
        memcpy(rsaPrivKey.key.rsa.privateExponent.value, blobKeyHandle.data + pos, rsaPrivKey.key.rsa.privateExponent.length);


        // 2. RSA Decryption
        // input data
        blobRsaEncData.data = pBlobInput->data + tlvDecryptionCommand.rsaEncryptedData.offset;
        blobRsaEncData.dataSize = tlvDecryptionCommand.rsaEncryptedData.length;
        blobRsaEncData.length = blobRsaEncData.dataSize;

        blobAesEncData.data = pBlobInput->data + tlvDecryptionCommand.aesEncryptedData.offset;
        blobAesEncData.dataSize = tlvDecryptionCommand.aesEncryptedData.length;
        blobAesEncData.length = blobAesEncData.dataSize;

        // decryption
        blobRsaPlainData.dataSize = MAX_GENERAL_RSA_ENCRYPTED_DATA_LENGTH;
        blobRsaPlainData.data = gTal.malloc(blobRsaPlainData.dataSize);
        if (blobRsaPlainData.data == NULL){
            printE("blobRsaPlainData.data malloc fail");
            ret = AUTHNR_ERROR_MALLOC;
            break;
        }
        blobRsaPlainData.length = 0;
        secure_zero_memory(blobRsaPlainData.data, blobRsaPlainData.dataSize);

        retBool = gTal.rsa_decryption(&blobRsaEncData, &rsaPrivKey, &blobRsaPlainData);
        if(retBool != TRUE || blobRsaPlainData.length == 0) {
            printE("gTal.rsa_decryption failed");
            ret = AUTHNR_ERROR_DECRYPT;
            break;
        }
#ifdef USE_DUMP_DATA
        printBlob(blobRsaPlainData, "RSA_PLAIN_DATA");
#endif


        // 3. MSG Parsing
        retBool = parseRsaDecryptedData(&blobRsaPlainData, &tlvRsaDecryptedData);
        if (retBool != TRUE) {
            printE("failed to parse blobRsaPlainData");
            ret = AUTHNR_ERROR_PARSING;
            break;
        }
#ifdef USE_DUMP_DATA
        {
            printD("tlvGetMsgCommand.msgVersion - tag:%x, length:%d, offset:%d", tlvRsaDecryptedData.msgVersion.tag, tlvRsaDecryptedData.msgVersion.length, tlvRsaDecryptedData.msgVersion.offset);
            printD("tlvGetMsgCommand.msgHM - tag:%x, length:%d, offset:%d", tlvRsaDecryptedData.msgHM.tag, tlvRsaDecryptedData.msgHM.length, tlvRsaDecryptedData.msgHM.offset);
            printD("tlvGetMsgCommand.msgHmacKey - tag:%x, length:%d, offset:%d", tlvRsaDecryptedData.msgHmacKey.tag, tlvRsaDecryptedData.msgHmacKey.length, tlvRsaDecryptedData.msgHmacKey.offset);
            printD("tlvGetMsgCommand.msgAesKey - tag:%x, length:%d, offset:%d", tlvRsaDecryptedData.msgAesKey.tag, tlvRsaDecryptedData.msgAesKey.length, tlvRsaDecryptedData.msgAesKey.offset);
        }
#endif

        _getMsg(blobRsaPlainData.data, &tlvRsaDecryptedData, &blobMsgVersion, &blobMsgHM, &blobMsgHmacKey, &blobMsgAesKey);

        // MSG version validation
        if (memcmp(blobMsgVersion.data, &MSG_VERSION, blobMsgVersion.length)) {
            printE("MSG verification failed");
            ret = AUTHNR_ERROR_ACCESS_DENIED;
            break;
        }


        // 4. HMAC
        retBool = gTal.hmac(&blobAesEncData, &blobMsgHmacKey, HASH_TYPE_SHA256, &blobTmpBuf);
        if(retBool != TRUE) {
            printE("gTal.hmac failed");
            ret = AUTHNR_ERROR_HMAC;
            break;
        }

        if (memcmp(blobMsgHM.data, blobTmpBuf.data, blobMsgHM.length)) {
            printE("HMAC verification failed");
            ret = AUTHNR_ERROR_ACCESS_DENIED;
            break;
        }


        // 5. AES Decryption
        blobAesPlainData.dataSize = pBlobOutput->dataSize;
        blobAesPlainData.data = gTal.malloc(blobAesPlainData.dataSize);
        if (blobAesPlainData.data == NULL){
            printE("blobAesPlainData.data malloc fail");
            ret = AUTHNR_ERROR_MALLOC;
            break;
        }
        blobAesPlainData.length = 0;
        secure_zero_memory(blobAesPlainData.data, blobAesPlainData.dataSize);

        // get AES Key & IV
        retBool = allocKey(aesKeySize, &aesKey);
        if(retBool != TRUE) {
            printE("gTal.rsa_allocKey failed");
            ret = AUTHNR_ERROR_ALLOC_KEY;
            break;
        }

        aesKey.key.aes.key.length = aesKeySize;
        memcpy(aesKey.key.aes.key.value, blobMsgAesKey.data, aesKey.key.aes.key.length);

        aesKey.key.aes.iv.length = aesIvSize;
        memcpy(aesKey.key.aes.iv.value, blobMsgAesKey.data + aesKey.key.aes.key.length, aesKey.key.aes.iv.length);

        // decryption
        retBool = gTal.aes_decryption(&blobAesEncData, &aesKey, &blobAesPlainData);
        if(retBool != TRUE || blobAesPlainData.length == 0) {
            printE("gTal.aes_decryption failed");
            ret = AUTHNR_ERROR_DECRYPT;
            break;
        }

        // ### MAKE RESPONSE ###
        tlvOutput.tag = TAG_TAD_DECRYPTION_RESPONSE;
        tlvOutput.dataSize = blobAesPlainData.length;
        tlvOutput.length = tlvOutput.dataSize;
        tlvOutput.data = gTal.malloc(tlvOutput.dataSize);
        if (tlvOutput.data == NULL){
            printE("tlvOutput.data malloc fail");
            ret = AUTHNR_ERROR_MALLOC;
            break;
        }
        memcpy(tlvOutput.data, blobAesPlainData.data, blobAesPlainData.length);

        retBool = encodeTlv2Blob(tlvOutput, pBlobOutput);
        if (retBool != TRUE) {
            printE("encode error");
            ret = AUTHNR_ERROR_ENCODE;
            break;
        }
//#ifdef USE_DUMP_DATA
//        printBlob(*pBlobOutput, "TAG_TAD_DECRYPTION_RESPONSE");
//#endif

    } while(0);

    // Free buffers
    freeKey(&rsaPrivKey);
    freeKey(&aesKey);

    if (blobRsaPlainData.data != NULL) {
        gTal.free(blobRsaPlainData.data, blobRsaPlainData.dataSize);
        blobRsaPlainData.data = NULL;
    }

    if (blobAesPlainData.data != NULL) {
        gTal.free(blobAesPlainData.data, blobAesPlainData.dataSize);
        blobAesPlainData.data = NULL;
    }

    if (blobTmpBuf.data != NULL) {
        gTal.free(blobTmpBuf.data, blobTmpBuf.dataSize);
        blobTmpBuf.data = NULL;
    }

    if (tlvOutput.data != NULL) {
        gTal.free(tlvOutput.data, tlvOutput.dataSize);
        tlvOutput.data = NULL;
    }

    printD("end");
    return ret;
}

