/*
 * src/arithmetics.c
 *
 * Copyright (C) 2013, Samsung Electronics Co., Ltd.
 *
 * TEE arithmetics
 */
#define THIRTY_TWO_BIT

#include <tee_internal_api.h>

#include <limits.h>

#include <string.h>
#include <stdarg.h>

#include <arithmetics_common.h>

#include <gpapi_log.h>
#ifdef USE_SCRYPTO_VER2_4
#include <openssl/scrypto_version.h>
#include <openssl/bn.h>
#else
#ifndef BN_MASK2
  #ifdef BORING_SSL
    #include <openssl/internal.h>
  #endif
#endif
#endif

#define COUNT_CHUNKS(UNIT, CHUNK_SIZE) (((UNIT) + (CHUNK_SIZE) - 1) / (CHUNK_SIZE))

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
#undef CHECK_MEMORY_ACCESS_RIGHTS
#endif

static bool cc_max_magnitude_init = false;
static uint32_t CRYPTOCORE_MAX_MAGNITUDE_SUPPORTED;

static void BigInt2BigInt(const TEE_BigInt* source, TEE_BigInt* destination) {
// for dest >= source
    MPI* gpmpi1 = (MPI*)source;
    MPI* gpmpi2 = (MPI*)destination;
    unsigned char* mpi_src = (unsigned char *)&gpmpi1->mpi;
    unsigned char* mpi_dst = (unsigned char *)&gpmpi2->mpi;
    size_t src_size = (size_t)MPIPureSize(mpi_src);

    if (MPIGetCapacityInBytes(gpmpi2->capacity) < src_size){
        MB_LOGE("Panic Reason: MPI capacity smaller than src size\n");
        TEE_Panic(0);
    }

    memcpy(mpi_dst, mpi_src, src_size + OPENSSL_METADATA_SIZE_IN_BYTES);
}

static unsigned int maxBigIntSize = 4 * 0xFFFF;

void TEE_BigIntInit(TEE_BigInt* value, const uint32_t length) {

    /* Initialise value from property - only done on first call */
    if (!cc_max_magnitude_init)
    {
        //TEE_GetPropertyAsU32(TEE_PROPSET_TEE_IMPLEMENTATION, "gpd.tee.arith.maxBigIntSize", &CRYPTOCORE_MAX_MAGNITUDE_SUPPORTED);
        CRYPTOCORE_MAX_MAGNITUDE_SUPPORTED = maxBigIntSize;
        cc_max_magnitude_init = true;
    }

    if (length < GP_API_METADATA_LENGTH_IN_U32) {
      MB_LOGE("Panic reason: invalid length (%u is less than minimum %u)",
                length, GP_API_METADATA_LENGTH_IN_U32);
      TEE_Panic(ID_TEE_BigIntInit);
    }

    if (!value ||
        (CRYPTOCORE_MAX_MAGNITUDE_SUPPORTED == 0) ||
        (CRYPTOCORE_MAX_MAGNITUDE_SUPPORTED < ((length - GP_API_METADATA_LENGTH_IN_U32)*32)))
    {
        MB_LOGE("Panic Reason: value(%p) == NULL or \n"
                  "CRYPTOCORE_MAX_MAGNITUDE_SUPPORTED(%u bits) == 0 or\n"
                  "BN size(%u bits) is greater than max allowed = %u bits\n",
                  value,
                  CRYPTOCORE_MAX_MAGNITUDE_SUPPORTED,
                  ((length - GP_API_METADATA_LENGTH_IN_U32)*32), /* convert to bits*/
                  CRYPTOCORE_MAX_MAGNITUDE_SUPPORTED);
        TEE_Panic(ID_TEE_BigIntInit);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, value, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_WRITE, value, length*4))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntInit);
    }
#endif /* CHECK_MEMORY_ACCESS_RIGHTS */

    MPI* gpmpi = (MPI*)value;
    BIGNUM* bn = NULL;

    MPISetCapacity(gpmpi->capacity, length - GP_API_METADATA_LENGTH_IN_U32);
//    unsigned char* mpi = (unsigned char*)gpmpi + GP_MPI_CAPACITY_SIZE_IN_BYTES;
    unsigned char* mpi = (unsigned char *)&gpmpi->mpi;

    bn = BN_new();
    BNCheckNULL(bn);

#ifdef USE_SCRYPTO_VER2_4
    int result = bn_expand(bn, (length - GP_API_METADATA_LENGTH_IN_U32) * BN_BITS2);
    IntCheckNULL(result);
#else
#ifndef BORING_SSL
    bn = bn_expand2(bn, length - GP_API_METADATA_LENGTH_IN_U32);
#else
    /* bn_wexpand seems to do the same in BoringSSL */
    bn = bn_wexpand(bn, length - GP_API_METADATA_LENGTH_IN_U32);
#endif
    BNCheckNULL(bn);
#endif

    size_t mpi_need_size;
    // compute required mpi size
    mpi_need_size = BN_bn2mpi(bn, NULL);

#ifdef PRINT_DEBUG
    MB_LOGD("%s[%s] - mpi_need_size(FULL,MAX)=%d(%d,%d) byte, length=%d byte\n", __FUNCTION__, __TIME__, (int)mpi_need_size, bn->dmax*sizeof(int)+OPENSSL_METADATA_SIZE_IN_BYTES+1,
            bn->dmax*sizeof(int)+GP_MPI_CAPACITY_SIZE_IN_BYTES+OPENSSL_METADATA_SIZE_IN_BYTES+1,(int)length * sizeof(uint32_t));
    size_t capacity = MPIGetCapacityInBytes(gpmpi->capacity);
    MB_LOGD("%s[%s] - capacity(inBytes)=%d\n", __FUNCTION__, __TIME__, capacity);
#endif

    if ((mpi_need_size + GP_MPI_CAPACITY_SIZE_IN_BYTES) > (size_t)length*4){
        MB_LOGE("Panic Reason: insufficient length\n");
        PRINT_OSSL_ERROR();
        BNFreePanic(bn);
    }

    size_t mpi_real_size; // with metadata
    mpi_real_size = BN_bn2mpi(bn, mpi);

    if (mpi_real_size != mpi_need_size){
        MB_LOGE("Panic Reason: openssl BN to internal representation "
                "conversion failed\n");
        PRINT_OSSL_ERROR();
        BNFreePanic(bn);
    }

    BN_clear_free(bn);
}



TEE_Result TEE_BigIntConvertFromOctetString(TEE_BigInt* dest, const uint8_t* buffer, const uint32_t sz_buffer, const int32_t sign) {
    if (!dest || !buffer) {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntConvertFromOctetString);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if ((TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, dest, MPI_size)) ||
        (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_WRITE, dest, (uint32_t) BIGNUMBER_MEMORY_SIZE(dest)))) {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights of dest)\n");
        TEE_Panic(ID_TEE_BigIntConvertFromOctetString);
    }

    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_ANY_OWNER, (void*)buffer, sz_buffer)) {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights of buffer)\n");
        TEE_Panic(ID_TEE_BigIntConvertFromOctetString);
    }
#endif

    TEE_Result result = TEE_SUCCESS;

    BIGNUM* bn = NULL;
    MPI* gpmpi = (MPI*)dest;
//    unsigned char* mpi = (unsigned char*)gpmpi + GP_MPI_CAPACITY_SIZE_IN_BYTES;
    unsigned char* mpi = (unsigned char *)&gpmpi->mpi;

//    unsigned char* mpi = (unsigned char*)dest;
    long len = 0; // long is the same as used in BN_mpi2bn
    size_t capacity = 0;

    // pure length (without metadata)
    len = MPIPureSize(mpi);
    MPICheckSize(len);

    capacity = MPIGetCapacityInBytes(gpmpi->capacity);

    if (capacity < sz_buffer){
        return TEE_ERROR_OVERFLOW;
    }

    ERR_clear_error();
    bn = BN_mpi2bn(mpi, len+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULLWithOpeSSLOutOfMemoryCheck(bn);

    bn = BN_bin2bn((unsigned char*)buffer, (int)sz_buffer, bn);
    BNCheckNULLWithOpeSSLOutOfMemoryCheck(bn);

    bn->neg = (sign < 0) ? 1 : 0;

    size_t mpi_real_size;
    mpi_real_size = BN_bn2mpi(bn, mpi);

    // '+1' - for sign size
    if (mpi_real_size > capacity+1+OPENSSL_METADATA_SIZE_IN_BYTES){
        MB_LOGE("Panic Reason: openssl BN to internal representation "
                "conversion failed\n");
        PRINT_OSSL_ERROR();
        BNFreePanic(bn);
    }

    BN_clear_free(bn);

    return result;
}



TEE_Result TEE_BigIntConvertToOctetString(void* buffer, uint32_t* sz_buffer_out, const TEE_BigInt* value) {
    if (!value  || !sz_buffer_out ||
       (buffer && !*sz_buffer_out) ||
       (!buffer && *sz_buffer_out))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntConvertToOctetString);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)value, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)value, (uint32_t) BIGNUMBER_MEMORY_SIZE(value)) ||
        (sz_buffer_out && (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_WRITE, sz_buffer_out, (uint32_t)sizeof(size_t)))) ||
        (buffer && sz_buffer_out && (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_WRITE | TEE_MEMORY_ACCESS_ANY_OWNER, buffer, *sz_buffer_out))))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntConvertToOctetString);
    }
#endif


    TEE_Result result = TEE_SUCCESS;

    BIGNUM* bn = NULL;
    MPI* gpmpi = (MPI*)value;
    unsigned char* mpi = (unsigned char *)&gpmpi->mpi;
    long len = 0; // long is the same as used in BN_mpi2bn
    size_t bin_len = 0;

    // pure length (without metadata)
    len = MPIPureSize(mpi);
    MPICheckSize(len);

    if (*sz_buffer_out == 0){
        *sz_buffer_out = (size_t)len;
        return (len == 0) ? TEE_SUCCESS : TEE_ERROR_SHORT_BUFFER;
    }

    ERR_clear_error();
    bn = BN_mpi2bn(mpi, len+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULLWithOpeSSLOutOfMemoryCheck(bn);

    bin_len = (size_t)BN_num_bytes(bn);
    if (bin_len > *sz_buffer_out){
        *sz_buffer_out = bin_len ; // size already without sign
        BN_clear_free(bn);
        return TEE_ERROR_SHORT_BUFFER;
    }

    /* ignore negative */
    bin_len = BN_bn2bin(bn, buffer);
    *sz_buffer_out = bin_len ; // size already without sign

    BN_clear_free(bn);

    return result;
}



void TEE_BigIntConvertFromS32(TEE_BigInt* result, const int32_t input) {
    if (!result){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntConvertFromS32);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, result, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_WRITE, result, (uint32_t) BIGNUMBER_MEMORY_SIZE(result)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntConvertFromS32);
    }
#endif

    BIGNUM* bn = NULL;
    MPI* gpmpi = (MPI*)result;
    unsigned char* mpi = (unsigned char *)&gpmpi->mpi;
    long len = 0; // long is the same as used in BN_mpi2bn

    // pure length (without metadata)
    len = MPIPureSize(mpi);
    MPICheckSize(len);

    bn = BN_mpi2bn(mpi, len+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);

    BNCheckNULL(bn);

    BN_ULONG word;
    word = (input < 0) ? -input : input;

    if (!BN_set_word(bn, word)){
        MB_LOGE("Panic reason: openssl BN_set_word() failed "
                 "with parameters bn(%p) and word(%d)\n", bn, (int)word);
        BNFreePanic(bn);
    }

    bn->neg = (input < 0) ? 1 : 0;

    size_t mpi_real_size;
    mpi_real_size = (size_t)BN_bn2mpi(bn, mpi);

    size_t capacity = 0;
    capacity = MPIGetCapacityInBytes(gpmpi->capacity);

    // '+1' - for sign size
    if (mpi_real_size > capacity+1+OPENSSL_METADATA_SIZE_IN_BYTES){
        MB_LOGE("Panic Reason: openssl BN to internal representation "
                "conversion failed\n");
        PRINT_OSSL_ERROR();
        BNFreePanic(bn);
    }

    BN_clear_free(bn);
}



TEE_Result TEE_BigIntConvertToS32(int32_t* value_result, const TEE_BigInt* input)
{
    if (!value_result || !input){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntConvertToS32);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)input, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)input, (uint32_t) BIGNUMBER_MEMORY_SIZE(input)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_WRITE, value_result, (uint32_t)sizeof(int32_t)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntConvertToS32);
    }
#endif

    TEE_Result result = TEE_SUCCESS;

    BIGNUM* bn = NULL;
    MPI* gpmpi = (MPI*)input;
    unsigned char* mpi = (unsigned char *)&gpmpi->mpi;
    long len = 0; // long is the same as used in BN_mpi2bn

    // pure length (without metadata)
    len = MPIPureSize(mpi);
    MPICheckSize(len);

    ERR_clear_error();
    bn = BN_mpi2bn(mpi, len+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULLWithOpeSSLOutOfMemoryCheck(bn);



    BN_ULONG word;
    word = BN_get_word(bn);

#ifdef USE_SCRYPTO_VER2_4
    if ((word == ULONG_MAX) ||
#else
    if ((word == BN_MASK2) ||
#endif
            ((word > INT32_MAX) && (bn->neg == 0)) ||
            ((word > (BN_ULONG)(INT32_MAX) + 1) && (bn->neg == 1)))
    {
//        if (bn->top > 1){ // check for case when BN value is equal 0xffffffffL
//            BN_clear_free(bn);
//            return TEE_ERROR_OVERFLOW;
//        }
        BN_clear_free(bn);
        return TEE_ERROR_OVERFLOW;
    }

    *value_result = (bn->neg == 1) ? -word : word;

    BN_clear_free(bn);

    return result;
}



int32_t TEE_BigIntCmp(const TEE_BigInt* op1, const TEE_BigInt* op2) {
    if (!op1 || !op2){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntCmp);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op1, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op1, (uint32_t) BIGNUMBER_MEMORY_SIZE(op1)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op2, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op2, (uint32_t) BIGNUMBER_MEMORY_SIZE(op2)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntCmp);
    }
#endif


    int32_t result = 0;
    BIGNUM* bn1 = NULL, *bn2 = NULL;
    MPI* gpmpi1 = (MPI*)op1;
    MPI* gpmpi2 = (MPI*)op2;
    unsigned char* mpi1 = (unsigned char *)&gpmpi1->mpi;
    unsigned char* mpi2 = (unsigned char *)&gpmpi2->mpi;
    long len1 = 0, len2 = 0; // long is the same as used in BN_mpi2bn

    // pure length (without metadata)
    len1 = MPIPureSize(mpi1);
    MPICheckSize(len1);
    len2 = MPIPureSize(mpi2);
    MPICheckSize(len2);

    bn1 = BN_mpi2bn(mpi1, len1+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn1);
    bn2 = BN_mpi2bn(mpi2, len2+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL1(bn2, bn1);

    result = BN_cmp(bn1,bn2);

    BN_clear_free(bn1);
    BN_clear_free(bn2);

    return result;
}



int32_t TEE_BigIntCmpS32(const TEE_BigInt* value1_raw, const int32_t value2) {
    if (!value1_raw){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntCmpS32);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)value1_raw, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)value1_raw, (uint32_t) BIGNUMBER_MEMORY_SIZE(value1_raw)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntCmpS32);
    }
#endif


    BIGNUM* bn = NULL;
    BIGNUM* bn32 = NULL;
    MPI* gpmpi = (MPI*)value1_raw;
    unsigned char* mpi = (unsigned char *)&gpmpi->mpi;
    long len = 0; // long is the same as used in BN_mpi2bn

    // pure length (without metadata)
    len = MPIPureSize(mpi);
    MPICheckSize(len);

    bn = BN_mpi2bn(mpi, len+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);

    BNCheckNULL(bn);

    bn32 = BN_new();
    BNCheckNULL1(bn32, bn)
            ;
    BN_ULONG word;
    word = (value2 < 0) ? -value2 : value2;

    if (!BN_set_word(bn32, word)){
        MB_LOGE("Panic reason: openssl BN_set_word() failed "
                 "with parameters bn(%p) and word(%d)\n", bn32, (int)word);
        BNFreePanic2(bn, bn32);
    }

    bn32->neg = (value2 < 0) ? 1 : 0;

    int32_t result = BN_cmp(bn,bn32);

    BN_clear_free(bn);
    BN_clear_free(bn32);

    return result;
}



void TEE_BigIntShiftRight(TEE_BigInt* destination_raw, const TEE_BigInt* source_raw, const uint32_t bits)
{
    if (!destination_raw || !source_raw){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntShiftRight);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)source_raw, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)source_raw, (uint32_t) BIGNUMBER_MEMORY_SIZE(source_raw)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, destination_raw, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_WRITE, destination_raw, (uint32_t) BIGNUMBER_MEMORY_SIZE(destination_raw)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntShiftRight);
    }
#endif


    BIGNUM* bn_dst = NULL, *bn_src = NULL;
    MPI* gpmpi1 = (MPI*)destination_raw;
    MPI* gpmpi2 = (MPI*)source_raw;
    unsigned char* mpi_dst = (unsigned char *)&gpmpi1->mpi;
    unsigned char* mpi_src = (unsigned char *)&gpmpi2->mpi;
    long len1 = 0, len2 = 0; // long is the same as used in BN_mpi2bn

    // pure length (without metadata)
    len1 = MPIPureSize(mpi_dst);
    MPICheckSize(len1);
    len2 = MPIPureSize(mpi_src);
    MPICheckSize(len2);

    bn_dst = BN_mpi2bn(mpi_dst, len1+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn_dst);
    bn_src = BN_mpi2bn(mpi_src, len2+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL1(bn_src, bn_dst);

    // bits is greater than the bit length of op
    if (bits > (size_t)BN_num_bits(bn_src)){
        BN_zero(bn_dst);
    }else{
        if (!BN_rshift(bn_dst, bn_src, bits)){
            MB_LOGE("Panic reason: openssl BN shift operation failed\n");
            PRINT_OSSL_ERROR();
            BNFreePanic2(bn_dst, bn_src);
        }
    }

    BN_bn2mpi(bn_dst, mpi_dst);

    BN_clear_free(bn_dst);
    BN_clear_free(bn_src);
}



bool TEE_BigIntGetBit(const TEE_BigInt* object_raw, const uint32_t index) {
    if (!object_raw){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntGetBit);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)object_raw, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)object_raw, (uint32_t) BIGNUMBER_MEMORY_SIZE(object_raw)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntGetBit);
    }
#endif

    BIGNUM* bn = NULL;
    MPI* gpmpi = (MPI*)object_raw;
    unsigned char* mpi = (unsigned char *)&gpmpi->mpi;
    long len = 0; // long is the same as used in BN_mpi2bn

    // pure length (without metadata)
    len = MPIPureSize(mpi);
    MPICheckSize(len);

    bn = BN_mpi2bn(mpi, len+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn);

    bool result = BN_is_bit_set(bn, index);

    BN_clear_free(bn);


    return result;
}



uint32_t TEE_BigIntGetBitCount(const TEE_BigInt* object_raw) {
    if (!object_raw){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntGetBitCount);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)object_raw, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)object_raw, (uint32_t) BIGNUMBER_MEMORY_SIZE(object_raw)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntGetBitCount);
    }
#endif

    uint32_t result;

    BIGNUM* bn = NULL;
    MPI* gpmpi = (MPI*)object_raw;
    unsigned char* mpi = (unsigned char *)&gpmpi->mpi;
    long len = 0; // long is the same as used in BN_mpi2bn

    // pure length (without metadata)
    len = MPIPureSize(mpi);
    MPICheckSize(len);

    if (len == 0){
        return 0;
    }

    bn = BN_mpi2bn(mpi, len+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn);
    bn->neg = 0;

    result = (uint32_t)BN_num_bits(bn);

    BN_clear_free(bn);



    return result;
}



void TEE_BigIntAdd(TEE_BigInt* dest, const TEE_BigInt* op1, const TEE_BigInt* op2) {
    if (!dest || !op1 || !op2){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntAdd);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, dest, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_WRITE, dest, (uint32_t) BIGNUMBER_MEMORY_SIZE(dest)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op1, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op1, (uint32_t) BIGNUMBER_MEMORY_SIZE(op1)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op2, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op2, (uint32_t) BIGNUMBER_MEMORY_SIZE(op2)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntAdd);
    }
#endif


    BIGNUM* bn_dst = NULL, *bn_op1 = NULL, *bn_op2 = NULL;
    MPI* gpmpi1 = (MPI*)dest;
    MPI* gpmpi2 = (MPI*)op1;
    MPI* gpmpi3 = (MPI*)op2;
    unsigned char* mpi_dst = (unsigned char *)&gpmpi1->mpi;
    unsigned char* mpi_op1 = (unsigned char *)&gpmpi2->mpi;
    unsigned char* mpi_op2 = (unsigned char *)&gpmpi3->mpi;
    long len1 = 0, len2 = 0, len3 = 0; // long is the same as used in BN_mpi2bn
    size_t dst_magnitude = 0;

    // pure length (without metadata)
    len1 = MPIPureSize(mpi_dst);
    MPICheckSize(len1);
    len2 = MPIPureSize(mpi_op1);
    MPICheckSize(len2);
    len3 = MPIPureSize(mpi_op2);
    MPICheckSize(len3);

    bn_dst = BN_mpi2bn(mpi_dst, len1+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn_dst);
    bn_op1 = BN_mpi2bn(mpi_op1, len2+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL1(bn_op1, bn_dst);
    bn_op2 = BN_mpi2bn(mpi_op2, len3+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL2(bn_op2, bn_dst, bn_op1);
    dst_magnitude = MPIGetCapacityInU32(gpmpi1->capacity);

    if (!BN_add(bn_dst, bn_op1, bn_op2)){
        MB_LOGE("Panic reason: openssl BN add operation failed\n");
        PRINT_OSSL_ERROR();
        BNFreePanic3(bn_dst, bn_op1, bn_op2);
    }


    // check result magnitude
#if defined(FIPS_SCRYPTO_MODULE_VERSION_NUM) && (FIPS_SCRYPTO_MODULE_VERSION_NUM >= 0x02052001)
    if (dst_magnitude < (size_t)bn_dst->width){
#else
    if (dst_magnitude < (size_t)bn_dst->top){
#endif
#ifdef PRINT_DEBUG
        MB_LOGD("---> file: '%s' \n---> func: '%s'\n---> line: %d\n---> time: [%s]\n=======> FAIL *** check result magnitude ***\n", __FILE__, __FUNCTION__, __LINE__, __TIME__);
#endif
        MB_LOGE("Panic reason: resulted size exceeds initial BN size\n");
        BNFreePanic3(bn_dst, bn_op1, bn_op2);
    }

    BN_bn2mpi(bn_dst, mpi_dst);

    BN_clear_free(bn_dst);
    BN_clear_free(bn_op1);
    BN_clear_free(bn_op2);
}



void TEE_BigIntSub(TEE_BigInt* dest, const TEE_BigInt* op1, const TEE_BigInt* op2) {
    if (!dest || !op1 || !op2){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntSub);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, dest, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_WRITE, dest, (uint32_t) BIGNUMBER_MEMORY_SIZE(dest)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op1, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op1, (uint32_t) BIGNUMBER_MEMORY_SIZE(op1)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op2, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op2, (uint32_t) BIGNUMBER_MEMORY_SIZE(op2)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntSub);
    }
#endif


    BIGNUM* bn_dst = NULL, *bn_op1 = NULL, *bn_op2 = NULL;
    MPI* gpmpi1 = (MPI*)dest;
    MPI* gpmpi2 = (MPI*)op1;
    MPI* gpmpi3 = (MPI*)op2;
    unsigned char* mpi_dst = (unsigned char *)&gpmpi1->mpi;
    unsigned char* mpi_op1 = (unsigned char *)&gpmpi2->mpi;
    unsigned char* mpi_op2 = (unsigned char *)&gpmpi3->mpi;
    long len1 = 0, len2 = 0, len3 = 0; // long is the same as used in BN_mpi2bn
    size_t dst_magnitude = 0;

    // pure length (without metadata)
    len1 = MPIPureSize(mpi_dst);
    MPICheckSize(len1);
    len2 = MPIPureSize(mpi_op1);
    MPICheckSize(len2);
    len3 = MPIPureSize(mpi_op2);
    MPICheckSize(len3);

    bn_dst = BN_mpi2bn(mpi_dst, len1+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn_dst);
    bn_op1 = BN_mpi2bn(mpi_op1, len2+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL1(bn_op1, bn_dst);
    bn_op2 = BN_mpi2bn(mpi_op2, len3+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL2(bn_op2, bn_dst, bn_op1);
    dst_magnitude = MPIGetCapacityInU32(gpmpi1->capacity);

    if (!BN_sub(bn_dst, bn_op1, bn_op2)){
        MB_LOGE("Panic reason: openssl BN substract operation failed\n");
        PRINT_OSSL_ERROR();
        BNFreePanic3(bn_dst, bn_op1, bn_op2);
    }

    // check result magnitude
#if defined(FIPS_SCRYPTO_MODULE_VERSION_NUM) && (FIPS_SCRYPTO_MODULE_VERSION_NUM >= 0x02052001)
    if (dst_magnitude < (size_t)bn_dst->width){
#else
    if (dst_magnitude < (size_t)bn_dst->top){
#endif
#ifdef PRINT_DEBUG
        MB_LOGD("---> file: '%s' \n---> func: '%s'\n---> line: %d\n---> time: [%s]\n=======> FAIL *** check result magnitude ***\n", __FILE__, __FUNCTION__, __LINE__, __TIME__);
#endif
        MB_LOGE("Panic reason: resulted size exceeds initial BN size\n");
        BNFreePanic3(bn_dst, bn_op1, bn_op2);
    }

    BN_bn2mpi(bn_dst, mpi_dst);

    BN_clear_free(bn_dst);
    BN_clear_free(bn_op1);
    BN_clear_free(bn_op2);
}



void TEE_BigIntNeg(TEE_BigInt* dest, const TEE_BigInt* op) {
    if (!dest || !op){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntNeg);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, dest, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_WRITE, dest, (uint32_t) BIGNUMBER_MEMORY_SIZE(dest)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op, (uint32_t) BIGNUMBER_MEMORY_SIZE(op)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntNeg);
    }
#endif

    BIGNUM* bn_dst = NULL, *bn_op = NULL;
    MPI* gpmpi1 = (MPI*)dest;
    MPI* gpmpi2 = (MPI*)op;
    unsigned char* mpi_dst = (unsigned char *)&gpmpi1->mpi;
    long len1 = 0, len2 = 0; // long is the same as used in BN_mpi2bn
    size_t dst_magnitude = 0;

    len1 = MPIPureSize(mpi_dst);
    MPICheckSize(len1);
    bn_dst = BN_mpi2bn(mpi_dst, len1+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn_dst);
    dst_magnitude = MPIGetCapacityInU32(gpmpi1->capacity);

    if (op != dest){
        unsigned char* mpi_op = (unsigned char *)&gpmpi2->mpi;
        len2 = MPIPureSize(mpi_op);
        MPICheckSize(len2);
        bn_op = BN_mpi2bn(mpi_op, len2+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
        BNCheckNULL1(bn_op, bn_dst);

        bn_dst = BN_copy(bn_dst, bn_op);

        BN_clear_free(bn_op);

        BNCheckNULL(bn_dst);
    }

    // check result magnitude
#if defined(FIPS_SCRYPTO_MODULE_VERSION_NUM) && (FIPS_SCRYPTO_MODULE_VERSION_NUM >= 0x02052001)
    if (dst_magnitude < (size_t)bn_dst->width){
#else
    if (dst_magnitude < (size_t)bn_dst->top){
#endif
#ifdef PRINT_DEBUG
        MB_LOGD("---> file: '%s' \n---> func: '%s'\n---> line: %d\n---> time: [%s]\n=======> FAIL *** check result magnitude ***\n", __FILE__, __FUNCTION__, __LINE__, __TIME__);
#endif
        MB_LOGE("Panic reason: resulted size exceeds initial BN size\n");
        BNFreePanic(bn_dst);
    }

    bn_dst->neg = (bn_dst->neg == 0) ? 1 : 0;
    BN_bn2mpi(bn_dst, mpi_dst);

    BN_clear_free(bn_dst);
}



void TEE_BigIntMul(TEE_BigInt* dest, const TEE_BigInt* op1, const TEE_BigInt* op2) {
    if (!dest || !op1 || !op2){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntMul);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, dest, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_WRITE, dest, (uint32_t) BIGNUMBER_MEMORY_SIZE(dest)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op1, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op1, (uint32_t) BIGNUMBER_MEMORY_SIZE(op1)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op2, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op2, (uint32_t) BIGNUMBER_MEMORY_SIZE(op2)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntMul);
    }
#endif

    BIGNUM* bn_dst = NULL, *bn_op1 = NULL, *bn_op2 = NULL;
    MPI* gpmpi1 = (MPI*)dest;
    MPI* gpmpi2 = (MPI*)op1;
    MPI* gpmpi3 = (MPI*)op2;
    unsigned char* mpi_dst = (unsigned char *)&gpmpi1->mpi;
    unsigned char* mpi_op1 = (unsigned char *)&gpmpi2->mpi;
    unsigned char* mpi_op2 = (unsigned char *)&gpmpi3->mpi;
    long len1 = 0, len2 = 0, len3 = 0; // long is the same as used in BN_mpi2bn
    size_t dst_magnitude = 0;

    // pure length (without metadata)
    len1 = MPIPureSize(mpi_dst);
    MPICheckSize(len1);
    len2 = MPIPureSize(mpi_op1);
    MPICheckSize(len2);
    len3 = MPIPureSize(mpi_op2);
    MPICheckSize(len3);

    bn_dst = BN_mpi2bn(mpi_dst, len1+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn_dst);
    bn_op1 = BN_mpi2bn(mpi_op1, len2+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL1(bn_op1, bn_dst);
    bn_op2 = BN_mpi2bn(mpi_op2, len3+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL2(bn_op2, bn_op1, bn_dst);
    dst_magnitude = MPIGetCapacityInU32(gpmpi1->capacity);


    BN_CTX *ctx = NULL;
    ctx = BN_CTX_new();
    if (ctx == NULL){
        MB_LOGE("Panic reason: ctx is NULL\n");
        BNFreePanic3(bn_dst, bn_op1, bn_op2);
    }

    if (!BN_mul(bn_dst, bn_op1, bn_op2, ctx)){
        BN_CTX_free(ctx);
        MB_LOGE("Panic reason: openssl BN multiply operation failed\n");
        PRINT_OSSL_ERROR();
        BNFreePanic3(bn_dst, bn_op1, bn_op2);
    }

    // check result magnitude
#if defined(FIPS_SCRYPTO_MODULE_VERSION_NUM) && (FIPS_SCRYPTO_MODULE_VERSION_NUM >= 0x02052001)
    if (dst_magnitude < (size_t)bn_dst->width){
#else
    if (dst_magnitude < (size_t)bn_dst->top){
#endif
#ifdef PRINT_DEBUG
        MB_LOGD("---> file: '%s' \n---> func: '%s'\n---> line: %d\n---> time: [%s]\n=======> FAIL *** check result magnitude ***\n", __FILE__, __FUNCTION__, __LINE__, __TIME__);
#endif
        BN_CTX_free(ctx);
        MB_LOGE("Panic reason: resulted size exceeds initial BN size\n");
        BNFreePanic3(bn_dst, bn_op1, bn_op2);
    }

    BN_bn2mpi(bn_dst, mpi_dst);

    BN_clear_free(bn_dst);
    BN_clear_free(bn_op1);
    BN_clear_free(bn_op2);
    BN_CTX_free(ctx);
}



void TEE_BigIntSquare(TEE_BigInt* dest, const TEE_BigInt* op) {
    if (!dest || !op){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntSquare);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, dest, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_WRITE, dest, (uint32_t) BIGNUMBER_MEMORY_SIZE(dest)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op, (uint32_t) BIGNUMBER_MEMORY_SIZE(op)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntSquare);
    }
#endif


    TEE_BigIntMul(dest, op, op);
}



void TEE_BigIntDiv(TEE_BigInt* dest_q, TEE_BigInt* dest_r, const TEE_BigInt* op1, const TEE_BigInt* op2) {
    if ((!dest_q && !dest_r) || !op1 || !op2){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntDiv);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if ((dest_q && (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, dest_q, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_WRITE, dest_q, (uint32_t) BIGNUMBER_MEMORY_SIZE(dest_q)))) ||
        (dest_r && (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, dest_r, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_WRITE, dest_r, (uint32_t) BIGNUMBER_MEMORY_SIZE(dest_r)))) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op1, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op1, (uint32_t) BIGNUMBER_MEMORY_SIZE(op1)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op2, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op2, (uint32_t) BIGNUMBER_MEMORY_SIZE(op2)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntDiv);
    }
#endif

    if (MEM_OVERLAP(dest_q, dest_r)){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (memory pointed to by dest_q[%p] and dest_r[%p] MUST NOT overlap)\n", dest_q, dest_r);
        TEE_Panic(ID_TEE_BigIntDiv);
    }

    BIGNUM* bn_dst_q = NULL, *bn_dst_r = NULL, *bn_op1 = NULL, *bn_op2 = NULL;
    MPI* gpmpi1 = (MPI*)dest_q;
    MPI* gpmpi2 = (MPI*)op1;
    MPI* gpmpi3 = (MPI*)op2;
    MPI* gpmpi4 = (MPI*)dest_r;
    unsigned char* mpi_op1 = (unsigned char *)&gpmpi2->mpi;
    unsigned char* mpi_op2 = (unsigned char *)&gpmpi3->mpi;
    unsigned char* mpi_dst_q = NULL;
    unsigned char* mpi_dst_r = NULL;
    long len1 = 0, len2 = 0, len3 = 0, len4 = 0; // long is the same as used in BN_mpi2bn
    size_t dst_q_magnitude = 0, dst_r_magnitude = 0;

    len3 = MPIPureSize(mpi_op2);
    MPICheckSize(len3);
    bn_op2 = BN_mpi2bn(mpi_op2, len3+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn_op2);
    if (BN_is_zero(bn_op2)){
        MB_LOGE("Panic reason: openssl BN (divisor) is zero\n");
        BNFreePanic(bn_op2);
    }

    len2 = MPIPureSize(mpi_op1);
    MPICheckSizeAndFree1(len2, bn_op2);
    bn_op1 = BN_mpi2bn(mpi_op1, len2+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL1(bn_op1, bn_op2);

    if (dest_q){
        mpi_dst_q = (unsigned char *)&gpmpi1->mpi;
        len1 = MPIPureSize(mpi_dst_q);
        MPICheckSizeAndFree2(len1, bn_op1, bn_op2);
        bn_dst_q = BN_mpi2bn(mpi_dst_q, len1+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
        BNCheckNULL2(bn_dst_q, bn_op1, bn_op2);
        dst_q_magnitude = MPIGetCapacityInU32(gpmpi1->capacity);
    }
    if (dest_r){
        mpi_dst_r = (unsigned char *)&gpmpi4->mpi;
        len4 = MPIPureSize(mpi_dst_r);
        MPICheckSizeAndFree3(len1, bn_dst_q, bn_op1, bn_op2);
        bn_dst_r = BN_mpi2bn(mpi_dst_r, len4+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
        BNCheckNULL3(bn_dst_r, bn_dst_q, bn_op1, bn_op2);
        dst_r_magnitude = MPIGetCapacityInU32(gpmpi4->capacity);
    }

    BN_CTX *ctx = NULL;
    ctx = BN_CTX_new();
    if (ctx == NULL){
        MB_LOGE("Panic reason: context is NULL\n");
        BNFreePanic4(bn_dst_q, bn_dst_r, bn_op1, bn_op2);
    }

    if (!BN_div(bn_dst_q, bn_dst_r, bn_op1, bn_op2, ctx)){
        BN_CTX_free(ctx);
        MB_LOGE("Panic reason: openssl BN division operation failed\n");
        PRINT_OSSL_ERROR();
        BNFreePanic4(bn_dst_q, bn_dst_r, bn_op1, bn_op2);
    }

    if (dest_q){
        // check dst_q magnitude
#if defined(FIPS_SCRYPTO_MODULE_VERSION_NUM) && (FIPS_SCRYPTO_MODULE_VERSION_NUM >= 0x02052001)
        if (dst_q_magnitude < (size_t)bn_dst_q->width){
#else
        if (dst_q_magnitude < (size_t)bn_dst_q->top){
#endif
#ifdef PRINT_DEBUG
            MB_LOGD("---> file: '%s' \n---> func: '%s'\n---> line: %d\n---> time: [%s]\n=======> FAIL *** check result magnitude ***\n", __FILE__, __FUNCTION__, __LINE__, __TIME__);
#endif
            BN_CTX_free(ctx);
            MB_LOGE("Panic reason: resulted size exceeds initial BN size\n");
            BNFreePanic4(bn_dst_q, bn_dst_r, bn_op1, bn_op2);
        }
        BN_bn2mpi(bn_dst_q, mpi_dst_q);
    }

    if (dest_r){
        // check dst_r magnitude
#if defined(FIPS_SCRYPTO_MODULE_VERSION_NUM) && (FIPS_SCRYPTO_MODULE_VERSION_NUM >= 0x02052001)
        if (dst_r_magnitude < (size_t)bn_dst_r->width){
#else
        if (dst_r_magnitude < (size_t)bn_dst_r->top){
#endif
#ifdef PRINT_DEBUG
            MB_LOGD("---> file: '%s' \n---> func: '%s'\n---> line: %d\n---> time: [%s]\n=======> FAIL *** check result magnitude ***\n", __FILE__, __FUNCTION__, __LINE__, __TIME__);
#endif
            BN_CTX_free(ctx);
            MB_LOGE("Panic reason: resulted size exceeds initial BN size\n");
            BNFreePanic4(bn_dst_q, bn_dst_r, bn_op1, bn_op2);
        }
        BN_bn2mpi(bn_dst_r, mpi_dst_r);
    }

    BN_clear_free(bn_dst_q);
    BN_clear_free(bn_dst_r);
    BN_clear_free(bn_op1);
    BN_clear_free(bn_op2);
    BN_CTX_free(ctx);
}



void TEE_BigIntMod(TEE_BigInt* dest, const TEE_BigInt* op, const TEE_BigInt* n) {
    if (!dest || !op || !n){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntMod);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, dest, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_WRITE, dest, (uint32_t) BIGNUMBER_MEMORY_SIZE(dest)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) op, (uint32_t) BIGNUMBER_MEMORY_SIZE(op)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) n, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*) n, (uint32_t) BIGNUMBER_MEMORY_SIZE(n)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntMod);
    }
#endif

    // dest and op MAY point to the same memory region but n MUST point to a unique memory region
    if (MEM_OVERLAP(dest, n) || MEM_OVERLAP(op, n)) {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (memory overlap)\n");
        TEE_Panic(ID_TEE_BigIntMod);
    }

    // n<2 panic
    if (TEE_BigIntCmpS32 (n,2) < 0){
        MB_LOGE("Panic reason: modulus is less than 2\n");
        TEE_Panic(ID_TEE_BigIntMod);
    }


    BIGNUM* bn_dst = NULL, *bn_op = NULL, *bn_n = NULL;
    MPI* gpmpi1 = (MPI*)dest;
    MPI* gpmpi2 = (MPI*)op;
    MPI* gpmpi3 = (MPI*)n;
    unsigned char* mpi_dst = (unsigned char *)&gpmpi1->mpi;
    unsigned char* mpi_op = (unsigned char *)&gpmpi2->mpi;
    unsigned char* mpi_n = (unsigned char *)&gpmpi3->mpi;
    long len1 = 0, len2 = 0, len3 = 0; // long is the same as used in BN_mpi2bn
    size_t dst_magnitude = 0;

    // pure length (without metadata)
    len1 = MPIPureSize(mpi_dst);
    MPICheckSize(len1);
    len2 = MPIPureSize(mpi_op);
    MPICheckSize(len2);
    len3 = MPIPureSize(mpi_n);
    MPICheckSize(len3);

    bn_dst = BN_mpi2bn(mpi_dst, len1+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn_dst);
    bn_op = BN_mpi2bn(mpi_op, len2+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL1(bn_op, bn_dst);
    bn_n = BN_mpi2bn(mpi_n, len3+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL2(bn_n, bn_op, bn_dst);
    dst_magnitude = MPIGetCapacityInU32(gpmpi1->capacity);


    BN_CTX *ctx = NULL;
    ctx = BN_CTX_new();
    if (ctx == NULL){
        MB_LOGE("Panic reason: context is NULL\n");
        PRINT_OSSL_ERROR();
        BNFreePanic3(bn_dst, bn_op, bn_n);
    }

    if (!BN_nnmod(bn_dst, bn_op, bn_n, ctx)){
        BN_CTX_free(ctx);
        MB_LOGE("Panic reason: openssl BN nmod operation failed\n");
        PRINT_OSSL_ERROR();
        BNFreePanic3(bn_dst, bn_op, bn_n);
    }

    // check result magnitude
#if defined(FIPS_SCRYPTO_MODULE_VERSION_NUM) && (FIPS_SCRYPTO_MODULE_VERSION_NUM >= 0x02052001)
    if (dst_magnitude < (size_t)bn_dst->width){
#else
    if (dst_magnitude < (size_t)bn_dst->top){
#endif
#ifdef PRINT_DEBUG
        MB_LOGD("---> file: '%s' \n---> func: '%s'\n---> line: %d\n---> time: [%s]\n=======> FAIL *** check result magnitude ***\n", __FILE__, __FUNCTION__, __LINE__, __TIME__);
#endif
        BN_CTX_free(ctx);
        MB_LOGE("Panic reason: resulted size exceeds initial BN size\n");
        BNFreePanic3(bn_dst, bn_op, bn_n);
    }


    BN_bn2mpi(bn_dst, mpi_dst);

    BN_clear_free(bn_dst);
    BN_clear_free(bn_op);
    BN_clear_free(bn_n);
    BN_CTX_free(ctx);
}



void TEE_BigIntAddMod(TEE_BigInt* dest, const TEE_BigInt* op1, const TEE_BigInt* op2, const TEE_BigInt* n)
{
    if (!dest || !op1 || !op2 || !n){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntAddMod);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, dest, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_WRITE, dest, (uint32_t) BIGNUMBER_MEMORY_SIZE(dest)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op1, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op1, (uint32_t) BIGNUMBER_MEMORY_SIZE(op1)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op2, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op2, (uint32_t) BIGNUMBER_MEMORY_SIZE(op2)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)n, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)n, (uint32_t) BIGNUMBER_MEMORY_SIZE(n)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntAddMod);
    }
#endif

    // All or some of dest, op1, and op2 MAY point to the same memory region but n MUST point to a unique memory region
    if (MEM_OVERLAP(dest, n) || MEM_OVERLAP(op1, n) || MEM_OVERLAP(op2, n)){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (memory overlap)\n");
        TEE_Panic(ID_TEE_BigIntMod);
    }

    if (TEE_BigIntCmpS32 (n,2) < 0){
        MB_LOGE("Panic reason: modulus less than 2\n");
        TEE_Panic(ID_TEE_BigIntAddMod);
    }

    BIGNUM* bn_dst = NULL, *bn_op1 = NULL, *bn_op2 = NULL, *bn_mod = NULL;
    MPI* gpmpi1 = (MPI*)dest;
    MPI* gpmpi2 = (MPI*)op1;
    MPI* gpmpi3 = (MPI*)op2;
    MPI* gpmpi4 = (MPI*)n;
    unsigned char* mpi_dst = (unsigned char *)&gpmpi1->mpi;
    unsigned char* mpi_op1 = (unsigned char *)&gpmpi2->mpi;
    unsigned char* mpi_op2 = (unsigned char *)&gpmpi3->mpi;
    unsigned char* mpi_mod = (unsigned char *)&gpmpi4->mpi;
    long len1 = 0, len2 = 0, len3 = 0, len4 = 0;// long is the same as used in BN_mpi2bn
    size_t dst_magnitude = 0;

    // pure length (without metadata)
    len1 = MPIPureSize(mpi_dst);
    MPICheckSize(len1);
    len2 = MPIPureSize(mpi_op1);
    MPICheckSize(len2);
    len3 = MPIPureSize(mpi_op2);
    MPICheckSize(len3);
    len4 = MPIPureSize(mpi_mod);
    MPICheckSize(len4);

    bn_dst = BN_mpi2bn(mpi_dst, len1+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn_dst);
    bn_op1 = BN_mpi2bn(mpi_op1, len2+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL1(bn_op1, bn_dst);
    bn_op2 = BN_mpi2bn(mpi_op2, len3+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL2(bn_op2, bn_op1, bn_dst);
    bn_mod = BN_mpi2bn(mpi_mod, len4+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL3(bn_mod, bn_op2, bn_op1, bn_dst);
    dst_magnitude = MPIGetCapacityInU32(gpmpi1->capacity);

    // [0,n-1] panic
    BIGNUM bn_zero;
    BN_init(&bn_zero); // init and set to zero
    if ((BN_cmp(bn_op1, &bn_zero) < 0) || (BN_cmp(bn_op1, bn_mod) >= 0)){
        MB_LOGE("Operand op1 MUST be in the interval [0,n-1].\n");
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_mod);
    }
    if ((BN_cmp(bn_op2, &bn_zero) < 0) || (BN_cmp(bn_op2, bn_mod) >= 0)){
        MB_LOGE("Operand op2 MUST be in the interval [0,n-1].\n");
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_mod);
    }

    BN_CTX *ctx = NULL;
    ctx = BN_CTX_new();
    if (ctx == NULL){
        MB_LOGE("Panic reason: context is NULL\n");
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_mod);
    }


    if (!BN_mod_add(bn_dst, bn_op1, bn_op2, bn_mod, ctx)){
        BN_CTX_free(ctx);
        MB_LOGE("Panic reason: openssl BN add operation failed\n");
        PRINT_OSSL_ERROR();
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_mod);
    }


    // check result magnitude
#if defined(FIPS_SCRYPTO_MODULE_VERSION_NUM) && (FIPS_SCRYPTO_MODULE_VERSION_NUM >= 0x02052001)
    if (dst_magnitude < (size_t)bn_dst->width){
#else
    if (dst_magnitude < (size_t)bn_dst->top){
#endif
#ifdef PRINT_DEBUG
        MB_LOGD("---> file: '%s' \n---> func: '%s'\n---> line: %d\n---> time: [%s]\n=======> FAIL *** check result magnitude ***\n", __FILE__, __FUNCTION__, __LINE__, __TIME__);
#endif
        BN_CTX_free(ctx);
        MB_LOGE("Panic reason: resulted size exceeds initial BN size\n");
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_mod);
    }

    BN_bn2mpi(bn_dst, mpi_dst);

    BN_clear_free(bn_dst);
    BN_clear_free(bn_op1);
    BN_clear_free(bn_op2);
    BN_clear_free(bn_mod);
    BN_CTX_free(ctx);
}

void TEE_BigIntSubMod(TEE_BigInt* dest, const TEE_BigInt* op1, const TEE_BigInt* op2, const TEE_BigInt* n) {
    if (!dest || !op1 || !op2 || !n){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntSubMod);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, dest, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_WRITE, dest, (uint32_t) BIGNUMBER_MEMORY_SIZE(dest)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op1, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op1, (uint32_t) BIGNUMBER_MEMORY_SIZE(op1)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op2, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op2, (uint32_t) BIGNUMBER_MEMORY_SIZE(op2)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)n, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)n, (uint32_t) BIGNUMBER_MEMORY_SIZE(n)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntSubMod);
    }
#endif

    // All or some of dest, op1, and op2 MAY point to the same memory region but n MUST point to a unique memory region
    if (MEM_OVERLAP(dest, n) || MEM_OVERLAP(op1, n) || MEM_OVERLAP(op2, n)){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (memory overlap)\n");
        TEE_Panic(ID_TEE_BigIntSubMod);
    }

    if (TEE_BigIntCmpS32 (n,2) < 0){
        MB_LOGE("Panic reason: modulus less than 2\n");
        TEE_Panic(ID_TEE_BigIntSubMod);
    }


    BIGNUM* bn_dst = NULL, *bn_op1 = NULL, *bn_op2 = NULL, *bn_mod = NULL;
    MPI* gpmpi1 = (MPI*)dest;
    MPI* gpmpi2 = (MPI*)op1;
    MPI* gpmpi3 = (MPI*)op2;
    MPI* gpmpi4 = (MPI*)n;
    unsigned char* mpi_dst = (unsigned char *)&gpmpi1->mpi;
    unsigned char* mpi_op1 = (unsigned char *)&gpmpi2->mpi;
    unsigned char* mpi_op2 = (unsigned char *)&gpmpi3->mpi;
    unsigned char* mpi_mod = (unsigned char *)&gpmpi4->mpi;
    long len1 = 0, len2 = 0, len3 = 0, len4 = 0;// long is the same as used in BN_mpi2bn
    size_t dst_magnitude = 0;

    // pure length (without metadata)
    len1 = MPIPureSize(mpi_dst);
    MPICheckSize(len1);
    len2 = MPIPureSize(mpi_op1);
    MPICheckSize(len2);
    len3 = MPIPureSize(mpi_op2);
    MPICheckSize(len3);
    len4 = MPIPureSize(mpi_mod);
    MPICheckSize(len4);

    bn_dst = BN_mpi2bn(mpi_dst, len1+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn_dst);
    bn_op1 = BN_mpi2bn(mpi_op1, len2+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL1(bn_op1, bn_dst);
    bn_op2 = BN_mpi2bn(mpi_op2, len3+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL2(bn_op2, bn_op1, bn_dst);
    bn_mod = BN_mpi2bn(mpi_mod, len4+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL3(bn_mod, bn_op2, bn_op1, bn_dst);
    dst_magnitude = MPIGetCapacityInU32(gpmpi1->capacity);

    // [0,n-1] panic
    BIGNUM bn_zero;
    BN_init(&bn_zero); // init and set to zero
    if ((BN_cmp(bn_op1, &bn_zero) < 0) || (BN_cmp(bn_op1, bn_mod) >= 0)){
        MB_LOGE("Operand op1 MUST be in the interval [0,n-1].\n");
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_mod);
    }
    if ((BN_cmp(bn_op2, &bn_zero) < 0) || (BN_cmp(bn_op2, bn_mod) >= 0)){
        MB_LOGE("Operand op2 MUST be in the interval [0,n-1].\n");
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_mod);
    }

    BN_CTX *ctx = NULL;
    ctx = BN_CTX_new();
    if (ctx == NULL){
        MB_LOGE("Panic reason: context is NULL\n");
        PRINT_OSSL_ERROR();
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_mod);
    }


    if (!BN_mod_sub(bn_dst, bn_op1, bn_op2, bn_mod, ctx)){
        BN_CTX_free(ctx);
        MB_LOGE("Panic reason: openssl BN mod sub operation failed\n");
        PRINT_OSSL_ERROR();
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_mod);
    }

    // check result magnitude
#if defined(FIPS_SCRYPTO_MODULE_VERSION_NUM) && (FIPS_SCRYPTO_MODULE_VERSION_NUM >= 0x02052001)
    if (dst_magnitude < (size_t)bn_dst->width){
#else
    if (dst_magnitude < (size_t)bn_dst->top){
#endif
#ifdef PRINT_DEBUG
        MB_LOGD("---> file: '%s' \n---> func: '%s'\n---> line: %d\n---> time: [%s]\n=======> FAIL *** check result magnitude ***\n", __FILE__, __FUNCTION__, __LINE__, __TIME__);
#endif
        BN_CTX_free(ctx);
        MB_LOGE("Panic reason: resulted size exceeds initial BN size\n");
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_mod);
    }

    BN_bn2mpi(bn_dst, mpi_dst);

    BN_clear_free(bn_dst);
    BN_clear_free(bn_op1);
    BN_clear_free(bn_op2);
    BN_clear_free(bn_mod);
    BN_CTX_free(ctx);
}



void TEE_BigIntMulMod(TEE_BigInt* dest, const TEE_BigInt* op1, const TEE_BigInt* op2, const TEE_BigInt* n) {
    if (!dest || !op1 || !op2 || !n){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntMulMod);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, dest, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_WRITE, dest, (uint32_t) BIGNUMBER_MEMORY_SIZE(dest)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op1, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op1, (uint32_t) BIGNUMBER_MEMORY_SIZE(op1)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op2, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op2, (uint32_t) BIGNUMBER_MEMORY_SIZE(op2)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)n, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)n, (uint32_t) BIGNUMBER_MEMORY_SIZE(n)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntMulMod);
    }
#endif

    // All or some of dest, op1, and op2 MAY point to the same memory region but n MUST point to a unique memory region
    if (MEM_OVERLAP(dest, n) || MEM_OVERLAP(op1, n) || MEM_OVERLAP(op2, n)){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (memory overlap)\n");
        TEE_Panic(ID_TEE_BigIntMulMod);
    }

    if (TEE_BigIntCmpS32 (n,2) < 0){
        MB_LOGE("Panic reason: modulus less than 2\n");
        TEE_Panic(ID_TEE_BigIntMulMod);
    }

    BIGNUM* bn_dst = NULL, *bn_op1 = NULL, *bn_op2 = NULL, *bn_mod = NULL;
    MPI* gpmpi1 = (MPI*)dest;
    MPI* gpmpi2 = (MPI*)op1;
    MPI* gpmpi3 = (MPI*)op2;
    MPI* gpmpi4 = (MPI*)n;
    unsigned char* mpi_dst = (unsigned char *)&gpmpi1->mpi;
    unsigned char* mpi_op1 = (unsigned char *)&gpmpi2->mpi;
    unsigned char* mpi_op2 = (unsigned char *)&gpmpi3->mpi;
    unsigned char* mpi_mod = (unsigned char *)&gpmpi4->mpi;
    long len1 = 0, len2 = 0, len3 = 0, len4 = 0;// long is the same as used in BN_mpi2bn
    size_t dst_magnitude = 0;

    // pure length (without metadata)
    len1 = MPIPureSize(mpi_dst);
    MPICheckSize(len1);
    len2 = MPIPureSize(mpi_op1);
    MPICheckSize(len2);
    len3 = MPIPureSize(mpi_op2);
    MPICheckSize(len3);
    len4 = MPIPureSize(mpi_mod);
    MPICheckSize(len4);

    bn_dst = BN_mpi2bn(mpi_dst, len1+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn_dst);
    bn_op1 = BN_mpi2bn(mpi_op1, len2+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL1(bn_op1, bn_dst);
    bn_op2 = BN_mpi2bn(mpi_op2, len3+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL2(bn_op2, bn_op1, bn_dst);
    bn_mod = BN_mpi2bn(mpi_mod, len4+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL3(bn_mod, bn_op2, bn_op1, bn_dst);
    dst_magnitude = MPIGetCapacityInU32(gpmpi1->capacity);

    // [0,n-1] panic
    BIGNUM bn_zero;
    BN_init(&bn_zero); // init and set to zero
    if ((BN_cmp(bn_op1, &bn_zero) < 0) || (BN_cmp(bn_op1, bn_mod) >= 0)){
        MB_LOGE("Operand op1 MUST be in the interval [0,n-1].\n");
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_mod);
    }
    if ((BN_cmp(bn_op2, &bn_zero) < 0) || (BN_cmp(bn_op2, bn_mod) >= 0) ){
        MB_LOGE("Operand op2 MUST be in the interval [0,n-1].\n");
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_mod);
    }

    BN_CTX *ctx = NULL;
    ctx = BN_CTX_new();
    if (ctx == NULL){
        MB_LOGE("Panic reason: context is NULL\n");
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_mod);
    }

    if (!BN_mod_mul(bn_dst, bn_op1, bn_op2, bn_mod, ctx)){
        BN_CTX_free(ctx);
        MB_LOGE("Panic reason: openssl BN modulus multiply "
                "operation failed\n");
        PRINT_OSSL_ERROR();
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_mod);
    }

    // check result magnitude
#if defined(FIPS_SCRYPTO_MODULE_VERSION_NUM) && (FIPS_SCRYPTO_MODULE_VERSION_NUM >= 0x02052001)
    if (dst_magnitude < (size_t)bn_dst->width){
#else
    if (dst_magnitude < (size_t)bn_dst->top){
#endif
#ifdef PRINT_DEBUG
        MB_LOGD("---> file: '%s' \n---> func: '%s'\n---> line: %d\n---> time: [%s]\n=======> FAIL *** check result magnitude ***\n", __FILE__, __FUNCTION__, __LINE__, __TIME__);
#endif
        BN_CTX_free(ctx);
        MB_LOGE("Panic reason: resulted size exceeds initial BN size\n");
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_mod);
    }

    BN_bn2mpi(bn_dst, mpi_dst);

    BN_clear_free(bn_dst);
    BN_clear_free(bn_op1);
    BN_clear_free(bn_op2);
    BN_clear_free(bn_mod);
    BN_CTX_free(ctx);
}



void TEE_BigIntSquareMod(TEE_BigInt* dest, const TEE_BigInt* op, const TEE_BigInt* n) {
    if (!dest || !op || !n){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntSquareMod);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, dest, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_WRITE, dest, (uint32_t) BIGNUMBER_MEMORY_SIZE(dest)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op, (uint32_t) BIGNUMBER_MEMORY_SIZE(op)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)n, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)n, (uint32_t) BIGNUMBER_MEMORY_SIZE(n)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntSquareMod);
    }
#endif

    // All or some of dest, op1, and op2 MAY point to the same memory region but n MUST point to a unique memory region
    if (MEM_OVERLAP(dest, n) || MEM_OVERLAP(op, n)){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (memory overlap)\n");
        TEE_Panic(ID_TEE_BigIntSquareMod);
    }

    if (TEE_BigIntCmpS32 (n,2) < 0){
        MB_LOGE("Panic reason: modulus less than 2\n");
        TEE_Panic(ID_TEE_BigIntSquareMod);
    }

    TEE_BigIntMulMod(dest, op, op, n);
}



void TEE_BigIntInvMod(TEE_BigInt* dest, const TEE_BigInt* op, const TEE_BigInt* n) {
    if (!dest || !op || !n){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntInvMod);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, dest, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_WRITE, dest, (uint32_t) BIGNUMBER_MEMORY_SIZE(dest)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op, (uint32_t) BIGNUMBER_MEMORY_SIZE(op)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)n, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)n, (uint32_t) BIGNUMBER_MEMORY_SIZE(n)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntInvMod);
    }
#endif

    // All or some of dest, op1, and op2 MAY point to the same memory region but n MUST point to a unique memory region
    if (MEM_OVERLAP(dest, n) || MEM_OVERLAP(op, n)){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (memory overlap)\n");
        TEE_Panic(ID_TEE_BigIntInvMod);
    }

    if (TEE_BigIntCmpS32 (n,2) < 0){
        MB_LOGE("Panic reason: modulus less than 2\n");
        TEE_Panic(ID_TEE_BigIntInvMod);
    }


    BIGNUM* bn_dst = NULL, *bn_op = NULL, *bn_n = NULL;
    MPI* gpmpi1 = (MPI*)dest;
    MPI* gpmpi2 = (MPI*)op;
    MPI* gpmpi3 = (MPI*)n;
    unsigned char* mpi_dst = (unsigned char *)&gpmpi1->mpi;
    unsigned char* mpi_op = (unsigned char *)&gpmpi2->mpi;
    unsigned char* mpi_n = (unsigned char *)&gpmpi3->mpi;
    long len2 = 0, len3 = 0; // long is the same as used in BN_mpi2bn
    size_t dst_magnitude = 0;

    // pure length (without metadata)
//    len1 = MPIPureSize(mpi_dst);
//    MPICheckSize(len1);
    len2 = MPIPureSize(mpi_op);
    MPICheckSize(len2);
    len3 = MPIPureSize(mpi_n);
    MPICheckSize(len3);

//    bn_dst = BN_mpi2bn(mpi_dst, len1+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
//    BNCheckNULL(bn_dst);
    bn_op = BN_mpi2bn(mpi_op, len2+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn_op);
    bn_n = BN_mpi2bn(mpi_n, len3+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL1(bn_n, bn_op);
    dst_magnitude = MPIGetCapacityInU32(gpmpi1->capacity);

    // [1,n-1] panic
    BIGNUM bn_one;
    BN_init(&bn_one);
    BN_one(&bn_one);
    if ((BN_cmp(bn_op, &bn_one) < 0) || (BN_cmp(bn_op, bn_n) >= 0)){
        MB_LOGE("Operand op MUST be in the interval [1,n-1].\n");
        BNFreePanic2(bn_op, bn_n);
    }

    BN_CTX *ctx = NULL;
    ctx = BN_CTX_new();
    if (ctx == NULL){
        MB_LOGE("Panic reason: context is NULL\n");
//        BNFreePanic3(bn_dst, bn_op, bn_n);
        BNFreePanic2(bn_op, bn_n);
    }

    bn_dst = BN_mod_inverse(NULL, bn_op, bn_n, ctx);
    if (!bn_dst){
        BN_CTX_free(ctx);
        MB_LOGE("Panic reason: failed BIGNUM inverse modulo operation "
                  "failed\n");
        PRINT_OSSL_ERROR();
        BNFreePanic2(bn_op, bn_n);
    }

    // check result magnitude
#if defined(FIPS_SCRYPTO_MODULE_VERSION_NUM) && (FIPS_SCRYPTO_MODULE_VERSION_NUM >= 0x02052001)
    if (dst_magnitude < (size_t)bn_dst->width){
#else
    if (dst_magnitude < (size_t)bn_dst->top){
#endif
#ifdef PRINT_DEBUG
        MB_LOGD("---> file: '%s' \n---> func: '%s'\n---> line: %d\n---> time: [%s]\n=======> FAIL *** check result magnitude ***\n", __FILE__, __FUNCTION__, __LINE__, __TIME__);
#endif
        BN_CTX_free(ctx);
        MB_LOGE("Panic reason: resulted size exceeds initial BN size\n");
        BNFreePanic3(bn_dst, bn_op, bn_n);
    }

    BN_bn2mpi(bn_dst, mpi_dst);

    BN_clear_free(bn_dst);
    BN_clear_free(bn_op);
    BN_clear_free(bn_n);
    BN_CTX_free(ctx);
}



bool TEE_BigIntRelativePrime(const TEE_BigInt* op1, const TEE_BigInt* op2) {
    if (!op1 || !op2){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntRelativePrime);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op1, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op1, (uint32_t) BIGNUMBER_MEMORY_SIZE(op1)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op2, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op2, (uint32_t) BIGNUMBER_MEMORY_SIZE(op2)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntRelativePrime);
    }
#endif


    BIGNUM* bn_op1 = NULL, *bn_op2 = NULL, *bn_gcd = NULL;
    MPI* gpmpi1 = (MPI*)op1;
    MPI* gpmpi2 = (MPI*)op2;
    unsigned char* mpi_op1 = (unsigned char *)&gpmpi1->mpi;
    unsigned char* mpi_op2 = (unsigned char *)&gpmpi2->mpi;
    long len1 = 0, len2 = 0; // long is the same as used in BN_mpi2bn

    // pure length (without metadata)
    len1 = MPIPureSize(mpi_op1);
    MPICheckSize(len1);
    len2 = MPIPureSize(mpi_op2);
    MPICheckSize(len2);

    bn_op1 = BN_mpi2bn(mpi_op1, len1+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn_op1);
    bn_op2 = BN_mpi2bn(mpi_op2, len2+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL1(bn_op2, bn_op1);


    BN_CTX *ctx = NULL;
    ctx = BN_CTX_new();
    if (ctx == NULL){
        MB_LOGE("Panic reason: context is NULL\n");
        BNFreePanic2(bn_op1, bn_op2);
    }

    bn_gcd = BN_new();
    BNCheckNULL2(bn_gcd, bn_op1, bn_op2);

    if (!BN_gcd(bn_gcd, bn_op1, bn_op2, ctx)){
        BN_CTX_free(ctx);
        MB_LOGE("Panic reason: openssl BN gcd operation failed\n");
        PRINT_OSSL_ERROR();
        BNFreePanic3(bn_op1, bn_op2, bn_gcd);
    }

    bool result = BN_is_one(bn_gcd);

    BN_clear_free(bn_op1);
    BN_clear_free(bn_op2);
    BN_clear_free(bn_gcd);
    BN_CTX_free(ctx);

    return result;
}



void TEE_BigIntComputeExtendedGcd(TEE_BigInt* gcd, TEE_BigInt* u, TEE_BigInt* v, const TEE_BigInt* op1, const TEE_BigInt* op2) {
    if (!gcd || !op1 || !op2){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntComputeExtendedGcd);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, gcd, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_WRITE, gcd, (uint32_t) BIGNUMBER_MEMORY_SIZE(gcd)) ||
        (u && (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, u, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_WRITE, u, (uint32_t) BIGNUMBER_MEMORY_SIZE(u)))) ||
        (v && (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, v, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_WRITE, v, (uint32_t) BIGNUMBER_MEMORY_SIZE(v)))) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op1, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op1, (uint32_t) BIGNUMBER_MEMORY_SIZE(op1)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op2, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op2, (uint32_t) BIGNUMBER_MEMORY_SIZE(op2)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntComputeExtendedGcd);
    }
#endif


    //onst uint32_t BIGNUMBER_MEMORY_SZ = BIGNUMBER_MEMORY_SIZE(gcd);
    //MB_LOGD("TEE_BigIntComputeExtendedGcd BIGNUMBER_MEMORY_SZ=%d\n", BIGNUMBER_MEMORY_SZ);



    //MB_LOGD("TEE_BigIntComputeExtendedGcd TEE_BigIntGetBitCount(op1)=%d\n", TEE_BigIntGetBitCount(op1)); // 14
    //MB_LOGD("TEE_BigIntComputeExtendedGcd TEE_BigIntGetBitCount(op2)=%d\n", TEE_BigIntGetBitCount(op2)); // 16


    uint32_t MAGNITUDE = TEE_BigIntGetBitCount(op1)  + TEE_BigIntGetBitCount(op2);
    uint32_t sz_integer = TEE_BigIntSizeInU32(MAGNITUDE);

    //MB_LOGD("TEE_BigIntComputeExtendedGcd MAGNITUDE=%d\n", MAGNITUDE);
    //MB_LOGD("TEE_BigIntComputeExtendedGcd sz_integer=%d\n", sz_integer);

    // define max buff
    //const uint32_t sz_integer_buf = TEE_BigIntSizeInU32(CRYPTOCORE_MAX_MAGNITUDE_SUPPORTED);

/*
 * ~TODO:
 *      we can use max magnitude from op1, op2
 *      and use mod operations
 * Also we can try modify euclid function from bn_gcd.c to compute u and v
 *
 */

    // A0
    uint32_t * placeholder_A0 = (uint32_t *)TEE_Malloc(sz_integer * sizeof(uint32_t), HINT_FILL_WITH_ZEROS);
    if (!placeholder_A0)
    {
        MB_LOGE("Panic reason: ERROR_OUT_OF_MEMORY\n");
        TEE_Panic(ID_TEE_BigIntComputeExtendedGcd);
    }

    TEE_BigInt* A0 = (TEE_BigInt*)placeholder_A0;
    TEE_BigIntInit(A0, sz_integer);
    TEE_BigIntConvertFromS32(A0, 1);

    // B0
    uint32_t * placeholder_B0 = (uint32_t *)TEE_Malloc(sz_integer * sizeof(uint32_t), HINT_FILL_WITH_ZEROS);
    if (!placeholder_B0)
    {
        MB_LOGE("Panic reason: ERROR_OUT_OF_MEMORY\n");
        TEE_Panic(ID_TEE_BigIntComputeExtendedGcd);
    }
    TEE_BigInt* B0 = (TEE_BigInt*)placeholder_B0;
    TEE_BigIntInit(B0, sz_integer);
    TEE_BigIntConvertFromS32(B0, 0);

    // r0
    uint32_t * placeholder_r0 = (uint32_t *)TEE_Malloc(sz_integer * sizeof(uint32_t), HINT_FILL_WITH_ZEROS);
    if (!placeholder_r0)
    {
        MB_LOGE("Panic reason: ERROR_OUT_OF_MEMORY\n");
        TEE_Panic(ID_TEE_BigIntComputeExtendedGcd);
    }
    TEE_BigInt* r0 = (TEE_BigInt*)placeholder_r0;
    TEE_BigIntInit(r0, sz_integer);
    BigInt2BigInt(op1, r0);

    // A1
    uint32_t * placeholder_A1 = (uint32_t *)TEE_Malloc(sz_integer * sizeof(uint32_t), HINT_FILL_WITH_ZEROS);
    if (!placeholder_A1)
    {
        MB_LOGE("Panic reason: ERROR_OUT_OF_MEMORY\n");
        TEE_Panic(ID_TEE_BigIntComputeExtendedGcd);
    }
    TEE_BigInt* A1 = (TEE_BigInt*)placeholder_A1;
    TEE_BigIntInit(A1, sz_integer);
    TEE_BigIntConvertFromS32(A1, 0);

    // B1
    uint32_t * placeholder_B1 = (uint32_t *)TEE_Malloc(sz_integer * sizeof(uint32_t), HINT_FILL_WITH_ZEROS);
    if (!placeholder_B1)
    {
        MB_LOGE("Panic reason: ERROR_OUT_OF_MEMORY\n");
        TEE_Panic(ID_TEE_BigIntComputeExtendedGcd);
    }
    TEE_BigInt* B1 = (TEE_BigInt*)placeholder_B1;
    TEE_BigIntInit(B1, sz_integer);
    TEE_BigIntConvertFromS32(B1, 1);

    // r1
    uint32_t * placeholder_r1 = (uint32_t *)TEE_Malloc(sz_integer * sizeof(uint32_t), HINT_FILL_WITH_ZEROS);
    if (!placeholder_r1)
    {
        MB_LOGE("Panic reason: ERROR_OUT_OF_MEMORY\n");
        TEE_Panic(ID_TEE_BigIntComputeExtendedGcd);
    }
    TEE_BigInt* r1 = (TEE_BigInt*)placeholder_r1;
    TEE_BigIntInit(r1, sz_integer);
    BigInt2BigInt(op2, r1);

    // q
    uint32_t * placeholder_q = (uint32_t *)TEE_Malloc(sz_integer * sizeof(uint32_t), HINT_FILL_WITH_ZEROS);
    if (!placeholder_q)
    {
        MB_LOGE("Panic reason: ERROR_OUT_OF_MEMORY\n");
        TEE_Panic(ID_TEE_BigIntComputeExtendedGcd);
    }
    TEE_BigInt* q = (TEE_BigInt*)placeholder_q;
    TEE_BigIntInit(q, sz_integer);

    // temp
    uint32_t * placeholder_temp = (uint32_t *)TEE_Malloc(sz_integer * sizeof(uint32_t), HINT_FILL_WITH_ZEROS);
    if (!placeholder_temp)
    {
        MB_LOGE("Panic reason: ERROR_OUT_OF_MEMORY\n");
        TEE_Panic(ID_TEE_BigIntComputeExtendedGcd);
    }
    TEE_BigInt* temp = (TEE_BigInt*)placeholder_temp;
    TEE_BigIntInit(temp, sz_integer);

    while(TEE_BigIntCmpS32(r1, 0)) {
        // q:=quot(r0,r1);
        TEE_BigIntDiv(q, NULL, r0, r1);

        // temp:=A0-A1*q, A0:=A1, A1:=temp;
        TEE_BigIntMul(temp, A1, q);
        TEE_BigIntSub(temp, A0, temp);
        BigInt2BigInt(A1, A0);
        BigInt2BigInt(temp, A1);

        // temp:=B0-B1*q, B0:=B1, B1:=temp;
        TEE_BigIntMul(temp, B1, q);
        TEE_BigIntSub(temp, B0, temp);
        BigInt2BigInt(B1, B0);
        BigInt2BigInt(temp, B1);

        // temp:=r0-r1*q, r0:=r1, r1:=temp;
        TEE_BigIntMul(temp, r1, q);
        TEE_BigIntSub(temp, r0, temp);
        BigInt2BigInt(r1, r0);
        BigInt2BigInt(temp, r1);
    }

    if(u) {
        BigInt2BigInt(A0, u);
    }
    if(v) {
        BigInt2BigInt(B0, v);
    }
    BigInt2BigInt(r0, gcd);


    TEE_Free(placeholder_A0);
    TEE_Free(placeholder_B0);
    TEE_Free(placeholder_r0);
    TEE_Free(placeholder_A1);
    TEE_Free(placeholder_B1);
    TEE_Free(placeholder_r1);
    TEE_Free(placeholder_q);
    TEE_Free(placeholder_temp);
}



uint32_t TEE_BigIntFMMSizeInU32(const uint32_t modulusSizeInBits) {
    return TEE_BigIntSizeInU32(modulusSizeInBits);
}



void TEE_BigIntInitFMM(TEE_BigIntFMM* object, const uint32_t len) {
    TEE_BigIntInit((TEE_BigInt*)object, len);
}



uint32_t TEE_BigIntFMMContextSizeInU32(const uint32_t modulusSizeInBits)
{
   BN_MONT_CTX *ctx = NULL;
   (void)ctx;
   const uint32_t modulo_size = TEE_BigIntSizeInU32(modulusSizeInBits);
   const uint32_t total_size = 3 * sizeof(ctx->RR.dmax) /* for size of bignums: ctx->RR, ctx->N, ctx->Ni */
#ifndef BORING_SSL
       + sizeof(ctx->ri)
       + sizeof(ctx->flags)
#endif
       + sizeof(ctx->n0);

   return COUNT_CHUNKS(total_size, sizeof(uint32_t)) + 3 * modulo_size;
}


static void serializeFMMContext(TEE_BigIntFMMContext *const context, const BN_MONT_CTX *const mont)
{
    int bigIntSize;
    unsigned char *ctx = (unsigned char *) context;
    unsigned char *mpi;

#ifndef BORING_SSL
    /* serializing integers */
    memcpy(ctx, &mont->flags, sizeof(mont->flags));
    ctx += sizeof(mont->flags);

    memcpy(ctx, &mont->ri, sizeof(mont->ri));
    ctx += sizeof(mont->ri);
#endif

    /* serializing BN_ULONG array */
    memcpy(ctx, &mont->n0[0], sizeof(mont->n0[0]));
    ctx += sizeof(mont->n0[0]);

    memcpy(ctx, &mont->n0[1], sizeof(mont->n0[1]));
    ctx += sizeof(mont->n0[1]);

    /* Serializing BIGNUMs from mont as MPIs and size of those MPIs.
     * Putting size first, then putting MPI. */
    mpi = ctx + sizeof(bigIntSize);
    bigIntSize = BN_bn2mpi(&mont->N, mpi);
    memcpy(ctx, &bigIntSize, sizeof(bigIntSize));
    ctx += sizeof(bigIntSize) + bigIntSize;

#ifndef BORING_SSL
    mpi = ctx + sizeof(bigIntSize);
    bigIntSize = BN_bn2mpi(&mont->Ni, mpi);
    memcpy(ctx, &bigIntSize, sizeof(bigIntSize));
    ctx += sizeof(bigIntSize) + bigIntSize;
#endif

    mpi = ctx + sizeof(bigIntSize);
    bigIntSize = BN_bn2mpi(&mont->RR, mpi);
    memcpy(ctx, &bigIntSize, sizeof(bigIntSize));
}

static void deserializeFMMContext(BN_MONT_CTX *const mont, const TEE_BigIntFMMContext *const context)
{
    int bigIntSize;
    unsigned char *ctx = (unsigned char *) context;
    unsigned char *mpi;

#ifndef BORING_SSL
    /* deserializing integers */
    memcpy(&mont->flags, ctx, sizeof(mont->flags));
    ctx += sizeof(mont->flags);
    memcpy(&mont->ri, ctx, sizeof(mont->ri));
    ctx += sizeof(mont->ri);
#endif

    /* serializing BN_ULONG array */
    memcpy(&mont->n0[0], ctx, sizeof(mont->n0[0]));
    ctx += sizeof(mont->n0[0]);
    memcpy(&mont->n0[1], ctx, sizeof(mont->n0[1]));
    ctx += sizeof(mont->n0[1]);

    /* Deserializing MPIs to BIGNUMs.
     * Getting size first, then getting MPI. */
    memcpy(&bigIntSize, ctx, sizeof(bigIntSize));
    ctx += sizeof(bigIntSize);
    mpi = ctx;
    BN_mpi2bn(mpi, bigIntSize, &mont->N);
    ctx += bigIntSize;

#ifndef BORING_SSL
    memcpy(&bigIntSize, ctx, sizeof(bigIntSize));
    ctx += sizeof(bigIntSize);
    mpi = ctx;
    BN_mpi2bn(mpi, bigIntSize, &mont->Ni);
    ctx += bigIntSize;
#endif

    memcpy(&bigIntSize, ctx, sizeof(bigIntSize));
    ctx += sizeof(bigIntSize);
    mpi = ctx;
    BN_mpi2bn(mpi, bigIntSize, &mont->RR);
}

void TEE_BigIntInitFMMContext(TEE_BigIntFMMContext* context, const uint32_t len, const TEE_BigInt* modulus) {
    if (!context || !modulus || !len){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntInitFMMContext);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, context, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_WRITE, context, len*sizeof(uint32_t)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)modulus, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)modulus, (uint32_t) BIGNUMBER_MEMORY_SIZE(modulus)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntInitFMMContext);
    }
#endif

    BIGNUM* bn = NULL;
    BIGNUM bn_two;
    MPI* gpmpi = (MPI*)modulus;
    unsigned char* mpi = (unsigned char *)&gpmpi->mpi;
    long len1 = 0; // long is the same as used in BN_mpi2bn

    // pure length (without metadata)
    len1 = MPIPureSize(mpi);
    MPICheckSize(len1);

    bn = BN_mpi2bn(mpi, len1+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn);

    BN_init(&bn_two);
    BN_set_word(&bn_two, 2);

    if ((BN_cmp(bn, &bn_two) != 1) || (!BN_is_odd(bn))){ // mod <= 2 or bn is not odd
        MB_LOGE("Panic reason: mod <= 2 or bn is not odd\n");
        BN_clear_free(bn);
        TEE_Panic(ID_TEE_BigIntInitFMMContext);
    }
#ifdef USE_SCRYPTO_VER2_4
    BN_CTX *ctx = NULL;
    ctx = BN_CTX_new();
    if (ctx == NULL) {
        PRINT_OSSL_ERROR();
        BN_clear_free(bn);
        TEE_Panic(ID_TEE_BigIntInitFMMContext);
    }

    BN_MONT_CTX* mont = NULL;
    mont = BN_MONT_CTX_new_for_modulus(bn, ctx);
    if (!mont) {
        PRINT_OSSL_ERROR();
        BN_clear_free(bn);
        BN_CTX_free(ctx);
        TEE_Panic(ID_TEE_BigIntInitFMMContext);
    }
#else

    BN_MONT_CTX* mont = NULL;
    mont = BN_MONT_CTX_new();
    if (!mont) {
        PRINT_OSSL_ERROR();
        BN_clear_free(bn);
        TEE_Panic(ID_TEE_BigIntInitFMMContext);
    }

    BN_CTX *ctx = NULL;
    ctx = BN_CTX_new();
    if (ctx == NULL) {
        PRINT_OSSL_ERROR();
        BN_clear_free(bn);
        BN_MONT_CTX_free(mont);
        TEE_Panic(ID_TEE_BigIntInitFMMContext);
    }

    if (!BN_MONT_CTX_set(mont, bn, ctx)) {
        PRINT_OSSL_ERROR();
        BN_clear_free(bn);
        BN_MONT_CTX_free(mont);
        BN_CTX_free(ctx);
        TEE_Panic(ID_TEE_BigIntInitFMMContext);
    }
#endif

    memset(context, 0, len*4);


    serializeFMMContext(context, mont);

    BN_MONT_CTX_free(mont);
    BN_CTX_free(ctx);
    BN_clear_free(bn);
}




void TEE_BigIntConvertToFMM(TEE_BigIntFMM* dest, const TEE_BigInt* src, const TEE_BigInt* n, const TEE_BigIntFMMContext* context) {
    if (!dest || !src || !n || !context){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntConvertToFMM);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, dest, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_WRITE, dest, (uint32_t) BIGNUMBER_MEMORY_SIZE(dest)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)src, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)src, (uint32_t) BIGNUMBER_MEMORY_SIZE(src)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)n, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)n, (uint32_t) BIGNUMBER_MEMORY_SIZE(n)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)context, (uint32_t)FMM_CONTEXT_MEMORY_SIZE(n)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntConvertToFMM);
    }
#endif

    BIGNUM* bn_src = NULL, *bn_modulus = NULL;
    BIGNUM bn_mod;
    MPI* gpmpi1 = (MPI*)dest;
    MPI* gpmpi2 = (MPI*)src;
    MPI* gpmpi3 = (MPI*)n;
    unsigned char* mpi_dst = (unsigned char *)&gpmpi1->mpi;
    unsigned char* mpi_src = (unsigned char *)&gpmpi2->mpi;
    unsigned char* mpi_modulus = (unsigned char *)&gpmpi3->mpi;
    long len2 = 0, len3 = 0; // long is the same as used in BN_mpi2bn
    size_t dst_magnitude = 0;

    // pure length (without metadata)
    len2 = MPIPureSize(mpi_src);
    MPICheckSize(len2);
    len3 = MPIPureSize(mpi_modulus);
    MPICheckSize(len3);

    // can't be empty at this point
    if (!len3 || !MPIGetCapacityInU32(gpmpi1->capacity) || !MPIGetCapacityInU32(gpmpi2->capacity)){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntConvertFromFMM);
    }

    bn_src = BN_mpi2bn(mpi_src, len2+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn_src);
    bn_modulus = BN_mpi2bn(mpi_modulus, len3+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL1(bn_modulus, bn_src);
    dst_magnitude = MPIGetCapacityInU32(gpmpi1->capacity);

    // [0,n-1] panic
    // operand must be non-negative and smaller than the modulus due to BN_to_montgomery() openssl description
    BIGNUM bn_zero;
    BN_init(&bn_zero); // init and set to zero
    if ((BN_cmp(bn_src, &bn_zero) < 0) || (BN_cmp(bn_src, bn_modulus) >= 0)){
        MB_LOGE("Operand src MUST be in the interval [0,n-1].\n");
        BNFreePanic2(bn_src, bn_modulus);
    }

#ifdef USE_SCRYPTO_VER2_4
    BN_CTX *ctx = NULL;
    ctx = BN_CTX_new();
    if (ctx == NULL){
        MB_LOGE("Panic reason: context is NULL\n");
        BNFreePanic2(bn_src, bn_modulus);
    }

    BN_MONT_CTX *mont = BN_MONT_CTX_new_for_modulus(bn_modulus, ctx);
    if (!mont) {
        BN_CTX_free(ctx);
        PRINT_OSSL_ERROR();
        BNFreePanic2(bn_src, bn_modulus);
    }

    deserializeFMMContext(mont, context);
    if (BN_cmp(bn_modulus, &mont->N) != 0){
        MB_LOGE("Panic reason: modulus n isn't equal to modulus of FMM"
                  " context\n");
        BN_MONT_CTX_free(mont);
        BNFreePanic2(bn_src, bn_modulus);
    }

    BN_init(&bn_mod);
#else
    BN_MONT_CTX *mont = BN_MONT_CTX_new();
    if (!mont) {
        PRINT_OSSL_ERROR();
        BNFreePanic2(bn_src, bn_modulus);
    }

    deserializeFMMContext(mont, context);
    if (BN_cmp(bn_modulus, &mont->N) != 0){
        MB_LOGE("Panic reason: modulus n isn't equal to modulus of FMM"
                  " context\n");
        BN_MONT_CTX_free(mont);
        BNFreePanic2(bn_src, bn_modulus);
    }

    BN_init(&bn_mod);

    BN_CTX *ctx = NULL;
    ctx = BN_CTX_new();
    if (ctx == NULL){
        MB_LOGE("Panic reason: context is NULL\n");
        BN_MONT_CTX_free(mont);
        BNFreePanic2(bn_src, bn_modulus);
    }
#endif

    // Openssl:
    // The inputs must be reduced modulo, otherwise the result will be outside the expected range
    if (!BN_mod(&bn_mod, bn_src, bn_modulus, ctx)){
        BN_CTX_free(ctx);
        BN_MONT_CTX_free(mont);
        MB_LOGE("Panic reason: openssl BN_mod() operation failed\n");
        PRINT_OSSL_ERROR();
        BNFreePanic2(bn_src, bn_modulus);
    }

    // check result magnitude
#if defined(FIPS_SCRYPTO_MODULE_VERSION_NUM) && (FIPS_SCRYPTO_MODULE_VERSION_NUM >= 0x02052001)
    if (dst_magnitude < (size_t)bn_mod.width){
#else
    if (dst_magnitude < (size_t)bn_mod.top){
#endif
#ifdef PRINT_DEBUG
        MB_LOGD("---> file: '%s' \n---> func: '%s'\n---> line: %d\n---> time: [%s]\n=======> FAIL *** check result magnitude ***\n", __FILE__, __FUNCTION__, __LINE__, __TIME__);
#endif
        BN_CTX_free(ctx);
        BN_MONT_CTX_free(mont);
        MB_LOGE("Panic reason: resulted size exceeds initial BN size\n");
        BNFreePanic2(bn_src, bn_modulus);
    }

    if (!BN_to_montgomery(&bn_mod, &bn_mod, mont, ctx)) {
        BN_CTX_free(ctx);
        BN_MONT_CTX_free(mont);
        MB_LOGE("Panic reason: openssl BN_to_montgomery() operation failed\n");
        PRINT_OSSL_ERROR();
        BNFreePanic2(bn_src, bn_modulus);
    }

    BN_bn2mpi(&bn_mod, mpi_dst);

    BN_clear_free(bn_src);
    BN_clear_free(bn_modulus);
    BN_CTX_free(ctx);
    BN_MONT_CTX_free(mont);
}



void TEE_BigIntConvertFromFMM(TEE_BigInt* dest, const TEE_BigIntFMM* src, const TEE_BigInt* n, const TEE_BigIntFMMContext* context) {
    if (!dest || !src || !n || !context){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntConvertFromFMM);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, dest, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_WRITE, dest, (uint32_t) BIGNUMBER_MEMORY_SIZE(dest)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)src, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)src, (uint32_t) BIGNUMBER_MEMORY_SIZE(src)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)n, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)n, (uint32_t) BIGNUMBER_MEMORY_SIZE(n)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)context, (uint32_t)FMM_CONTEXT_MEMORY_SIZE(n)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntConvertFromFMM);
    }
#endif

    BIGNUM* bn_dst = NULL, *bn_src = NULL,  *bn_modulus = NULL;
    MPI* gpmpi1 = (MPI*)dest;
    MPI* gpmpi2 = (MPI*)src;
    MPI* gpmpi4 = (MPI*)n;
    unsigned char* mpi_dst = (unsigned char *)&gpmpi1->mpi;
    unsigned char* mpi_src = (unsigned char *)&gpmpi2->mpi;
    unsigned char* mpi_modulus = (unsigned char *)&gpmpi4->mpi;
    long len1 = 0, len2 = 0, len4 = 0; // long is the same as used in BN_mpi2bn

    // pure length (without metadata)
    len1 = MPIPureSize(mpi_dst);
    MPICheckSize(len1);
    len2 = MPIPureSize(mpi_src);
    MPICheckSize(len2);
    len4 = MPIPureSize(mpi_modulus);
    MPICheckSize(len4);

    // can't be empty at this point
    if (!len4 || !MPIGetCapacityInU32(gpmpi1->capacity) || !MPIGetCapacityInU32(gpmpi2->capacity)){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntConvertFromFMM);
    }

    bn_dst = BN_mpi2bn(mpi_dst, len1+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn_dst);
    bn_src = BN_mpi2bn(mpi_src, len2+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL1(bn_src, bn_dst);
    bn_modulus = BN_mpi2bn(mpi_modulus, len4+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL2(bn_modulus, bn_src, bn_dst);

#ifdef USE_SCRYPTO_VER2_4
    BN_CTX *ctx = NULL;
    ctx = BN_CTX_new();
    if (ctx == NULL){
        MB_LOGE("Panic reason: context is NULL\n");
        BNFreePanic3(bn_dst, bn_src, bn_modulus);
    }

    BN_MONT_CTX *mont = BN_MONT_CTX_new_for_modulus(bn_modulus, ctx);
    if (!mont) {
        BN_CTX_free(ctx);
        PRINT_OSSL_ERROR();
        BNFreePanic3(bn_dst, bn_src, bn_modulus);
    }

    deserializeFMMContext(mont, context);
    if (BN_cmp(bn_modulus, &mont->N) != 0){
        MB_LOGE("Panic reason: modulus n isn't equal to modulus of FMM"
                  " context\n");
        BN_MONT_CTX_free(mont);
        BNFreePanic3(bn_dst, bn_src, bn_modulus);
    }
#else
    BN_MONT_CTX *mont = BN_MONT_CTX_new();
    if (!mont) {
        PRINT_OSSL_ERROR();
        BNFreePanic3(bn_dst, bn_src, bn_modulus);
    }

    deserializeFMMContext(mont, context);
    if (BN_cmp(bn_modulus, &mont->N) != 0){
        MB_LOGE("Panic reason: modulus n isn't equal to modulus of FMM"
                  " context\n");
        BN_MONT_CTX_free(mont);
        BNFreePanic3(bn_dst, bn_src, bn_modulus);
    }

    BN_CTX *ctx = NULL;
    ctx = BN_CTX_new();
    if (ctx == NULL){
        MB_LOGE("Panic reason: context is NULL\n");
        BN_MONT_CTX_free(mont);
        BNFreePanic3(bn_dst, bn_src, bn_modulus);
    }
#endif
    if (!BN_from_montgomery(bn_dst, bn_src, mont, ctx)) {
        BN_CTX_free(ctx);
        BN_MONT_CTX_free(mont);
        MB_LOGE("Panic reason: openssl BN_from_montgomery() operation failed\n");
        PRINT_OSSL_ERROR();
        BNFreePanic3(bn_dst, bn_src, bn_modulus);
    }

    BN_bn2mpi(bn_dst, mpi_dst);

    BN_clear_free(bn_dst);
    BN_clear_free(bn_src);
    BN_clear_free(bn_modulus);
    BN_CTX_free(ctx);
    BN_MONT_CTX_free(mont);
}



void TEE_BigIntComputeFMM(TEE_BigIntFMM* dest, const TEE_BigIntFMM* op1, const TEE_BigIntFMM* op2, const TEE_BigInt* n, const TEE_BigIntFMMContext* context) {
    if (!dest || !op1 || !op2 || !n || !context){
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntComputeFMM);
    }

#ifdef CHECK_MEMORY_ACCESS_RIGHTS
    if (TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, dest, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_WRITE, dest, (uint32_t) BIGNUMBER_MEMORY_SIZE(dest)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op1, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op1, (uint32_t) BIGNUMBER_MEMORY_SIZE(op1)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op2, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)op2, (uint32_t) BIGNUMBER_MEMORY_SIZE(op2)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)n, MPI_size) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)n, (uint32_t) BIGNUMBER_MEMORY_SIZE(n)) ||
        TEE_SUCCESS != TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_READ, (void*)context, (uint32_t)FMM_CONTEXT_MEMORY_SIZE(n)))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS (CheckMemoryAccessRights)\n");
        TEE_Panic(ID_TEE_BigIntComputeFMM);
    }
#endif /* #ifdef CHECK_MEMORY_ACCESS_RIGHTS */

    BIGNUM* bn_dst = NULL, *bn_op1 = NULL, *bn_op2 = NULL, *bn_modulus = NULL;
    MPI* gpmpi1 = (MPI*)dest;
    MPI* gpmpi2 = (MPI*)op1;
    MPI* gpmpi3 = (MPI*)op2;
    MPI* gpmpi4 = (MPI*)n;
    unsigned char* mpi_dst = (unsigned char *)&gpmpi1->mpi;
    unsigned char* mpi_op1 = (unsigned char *)&gpmpi2->mpi;
    unsigned char* mpi_op2 = (unsigned char *)&gpmpi3->mpi;
    unsigned char* mpi_modulus = (unsigned char *)&gpmpi4->mpi;
    long len1 = 0, len2 = 0, len3 = 0, len4 = 0; // long is the same as used in BN_mpi2bn

    // pure length (without metadata)
    len1 = MPIPureSize(mpi_dst);
    MPICheckSize(len1);
    len2 = MPIPureSize(mpi_op1);
    MPICheckSize(len2);
    len3 = MPIPureSize(mpi_op2);
    MPICheckSize(len3);
    len4 = MPIPureSize(mpi_modulus);
    MPICheckSize(len4);

    // can't be empty at this point
    if (!len4 || !MPIGetCapacityInU32(gpmpi1->capacity) ||
        !MPIGetCapacityInU32(gpmpi2->capacity) ||
        !MPIGetCapacityInU32(gpmpi3->capacity))
    {
        MB_LOGE("Panic reason: ERROR_BAD_PARAMETERS\n");
        TEE_Panic(ID_TEE_BigIntComputeFMM);
    }

    bn_dst = BN_mpi2bn(mpi_dst, len1+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL(bn_dst);
    bn_op1 = BN_mpi2bn(mpi_op1, len2+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL1(bn_op1, bn_dst);
    bn_op2 = BN_mpi2bn(mpi_op2, len3+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL2(bn_op2, bn_op1, bn_dst);
    bn_modulus = BN_mpi2bn(mpi_modulus, len4+OPENSSL_METADATA_SIZE_IN_BYTES, NULL);
    BNCheckNULL3(bn_modulus, bn_op2, bn_op1, bn_dst);

#ifdef USE_SCRYPTO_VER2_4
    BN_CTX *ctx = NULL;
    ctx = BN_CTX_new();
    if (ctx == NULL){
        MB_LOGE("Panic reason: context is NULL\n");
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_modulus);
    }

    BN_MONT_CTX *mont = BN_MONT_CTX_new_for_modulus(bn_modulus, ctx);
    if (!mont) {
        BN_CTX_free(ctx);
        PRINT_OSSL_ERROR();
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_modulus);
    }

    deserializeFMMContext(mont, context);
    if (BN_cmp(bn_modulus, &mont->N) != 0){
        MB_LOGE("Panic reason: modulus n isn't equal to modulus of FMM"
                  " context\n");
        BN_MONT_CTX_free(mont);
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_modulus);
    }
#else
    BN_MONT_CTX *mont = BN_MONT_CTX_new();
    if (!mont) {
        PRINT_OSSL_ERROR();
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_modulus);
    }

    deserializeFMMContext(mont, context);
    if (BN_cmp(bn_modulus, &mont->N) != 0){
        MB_LOGE("Panic reason: modulus n isn't equal to modulus of FMM"
                  " context\n");
        BN_MONT_CTX_free(mont);
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_modulus);
    }

    BN_CTX *ctx = NULL;
    ctx = BN_CTX_new();
    if (ctx == NULL){
        MB_LOGE("Panic reason: context is NULL\n");
        BN_MONT_CTX_free(mont);
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_modulus);
    }
#endif

    if (!BN_mod_mul_montgomery(bn_dst, bn_op1, bn_op2, mont, ctx)){
        BN_CTX_free(ctx);
        MB_LOGE("Panic reason: openssl BN Montgomery modulus multiply "
                "operation failed\n");
        PRINT_OSSL_ERROR();
        BN_MONT_CTX_free(mont);
        BNFreePanic4(bn_dst, bn_op1, bn_op2, bn_modulus);
    }

    BN_bn2mpi(bn_dst, mpi_dst);

    BN_clear_free(bn_dst);
    BN_clear_free(bn_op1);
    BN_clear_free(bn_op2);
    BN_clear_free(bn_modulus);
    BN_CTX_free(ctx);
    BN_MONT_CTX_free(mont);
}
