/**
  * @file arithmetics_common.h
  * @brief  arithmetics_common
  * @author Georgiy Levchenko (g.levchenko@samsung.com)
  * @version 0.1
  * @date Created 28-08-15
  * @par In Samsung Ukraine R&D Center (SRK) under a contract between
  * @par LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) and "Samsung Elecrtronics Co", Ltd (Seoul, Republic of Korea)
  * @par Copyright: (c) Samsung Electronics Co, Ltd 2015. All rights reserved.
  **/
#ifndef _ARITHMETICS_COMMON_H
#define	_ARITHMETICS_COMMON_H

#ifdef	__cplusplus
extern "C" {
#endif

#include <tee_internal_api.h>

#ifndef BORING_SSL
#include <openssl/opensslconf.h>
#endif

#include <openssl/bn.h>
#include <openssl/bn_prime.h>
#include <openssl/err.h>
#include <openssl/crypto.h> /* needed for FIPS symbols declaration */

#define GP_API_METADATA_LENGTH_IN_U32 2
#define OPENSSL_METADATA_SIZE_IN_BYTES 4
#define GP_MINIMAL_CONFIDENCE_LEVEL 80

/* pure length (without metadata) */
#define MPIPureSize(mpi) (((long)mpi[0]<<24)|((long)mpi[1]<<16)|((int)mpi[2]<<8)|(int)mpi[3])
#define MPICheckSize(mpi_pure_size) \
if (mpi_pure_size < 0) { \
    MB_LOGE("Panic Reason: MPI pure size less than 0\n"); \
    TEE_Panic(0); \
}

#define MPICheckSizeAndFree1(mpi_pure_size, bn) \
if (mpi_pure_size < 0) { \
    MB_LOGE("Panic Reason: MPI pure size less than 0\n"); \
    BN_clear_free(bn); \
    TEE_Panic(0); \
}
#define MPICheckSizeAndFree2(mpi_pure_size, bn, bn1) \
if (mpi_pure_size < 0) { \
    MB_LOGE("Panic Reason: MPI pure size less than 0\n"); \
    BN_clear_free(bn); \
    BN_clear_free(bn1); \
    TEE_Panic(0); \
}
#define MPICheckSizeAndFree3(mpi_pure_size, bn, bn1, bn2) \
if (mpi_pure_size < 0) { \
    MB_LOGE("Panic Reason: MPI pure size less than 0\n"); \
    BN_clear_free(bn); \
    BN_clear_free(bn1); \
    BN_clear_free(bn2); \
    TEE_Panic(0); \
}
#define MPISetCapacity(cap, length) cap[0] = (unsigned char)(((length)>> 8)&0xff); \
                                    cap[1] = (unsigned char)(((length)    )&0xff);

#define MPIGetCapacityInU32(cap) (((size_t)cap[0]<<8)|(size_t)cap[1])
#define MPIGetCapacityInBytes(cap) ((((size_t)cap[0]<<8)|(size_t)cap[1])*4)

/* Full BN memory size (capacity+ metadata) in bytes */
#define BIGNUMBER_MEMORY_SIZE(BN) \
    ((size_t)(MPIGetCapacityInBytes(((MPI*)BN)->capacity) + GP_API_METADATA_LENGTH_IN_U32*4))

/* Montgomery CTX size in bytes */
#define FMM_CONTEXT_MEMORY_SIZE(modulus) \
    (TEE_BigIntFMMContextSizeInU32(TEE_BigIntGetBitCount(modulus))*4)

#define IN_BN_MEM_RANGE(BN, addr) \
( ((char*)addr >= (char*)BN) && ((char*)addr < ((char*)BN + BIGNUMBER_MEMORY_SIZE(BN))) )

#define MEM_OVERLAP(mem1, mem2) \
( (mem1 && mem2) && \
( (mem1 == mem2) || \
IN_BN_MEM_RANGE(mem1, mem2) || \
IN_BN_MEM_RANGE(mem1, (char*)mem2+BIGNUMBER_MEMORY_SIZE(mem2)-1) ) )

#define IntCheckNULL(result) \
if (result == 0) { \
    MB_LOGE("Panic Reason: Result is NULL\n"); \
    PRINT_OSSL_ERROR(); \
    TEE_Panic(0); \
}
#define BNCheckNULL(bn) \
if (bn == NULL) { \
    MB_LOGE("Panic Reason: BN is NULL\n"); \
    PRINT_OSSL_ERROR(); \
    TEE_Panic(0); \
}
#define BNCheckNULL1(bn, bn1) \
if (bn == NULL) { \
    MB_LOGE("Panic Reason: BN is NULL\n"); \
    PRINT_OSSL_ERROR(); \
    BN_clear_free(bn1); \
    TEE_Panic(0); \
}
#define BNCheckNULL2(bn, bn1, bn2) \
if (bn == NULL) { \
    MB_LOGE("Panic Reason: BN is NULL\n"); \
    PRINT_OSSL_ERROR(); \
    BN_clear_free(bn1); \
    BN_clear_free(bn2); \
    TEE_Panic(0); \
}
#define BNCheckNULL3(bn, bn1, bn2, bn3) \
if (bn == NULL) { \
    MB_LOGE("Panic Reason: BN is NULL\n"); \
    PRINT_OSSL_ERROR(); \
    BN_clear_free(bn1); \
    BN_clear_free(bn2); \
    BN_clear_free(bn3); \
    TEE_Panic(0); \
}

#define BNCheckNULLWithOpeSSLOutOfMemoryCheck(bn) \
if ((bn) == NULL) { \
    if (ERR_R_MALLOC_FAILURE == ERR_GET_REASON(ERR_peek_error())) { \
        return TEE_ERROR_OUT_OF_MEMORY; \
    } \
    MB_LOGE("Panic Reason: BN is NULL\n"); \
    PRINT_OSSL_ERROR(); \
    TEE_Panic(0); \
}

#define BNFreePanic(bn) BN_clear_free(bn); \
                        TEE_Panic(0);
#define BNFreePanic2(bn, bn1) BN_clear_free(bn); \
                        BN_clear_free(bn1); TEE_Panic(0);
#define BNFreePanic3(bn, bn1, bn2) BN_clear_free(bn); \
                        BN_clear_free(bn1); BN_clear_free(bn2); TEE_Panic(0);
#define BNFreePanic4(bn, bn1, bn2, bn3) BN_clear_free(bn); \
                        BN_clear_free(bn1); BN_clear_free(bn2); BN_clear_free(bn3); TEE_Panic(0);

#ifndef INT32_MAX
#define INT32_MAX         ((int32_t)(~0U>>1))
#endif
#ifndef INT32_MIN
#define INT32_MIN         (-INT_MAX - 1)
#endif

#ifndef MAX
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#endif

#ifndef MIN
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#endif

#define PRINT_OSSL_ERROR() \
    do {\
        if (0 != ERR_peek_error()) {\
            MB_LOGE("Internal OPENSSL error: \n");\
            MB_LOGE("   OSSL ERR_GET_LIB - %u\n", ERR_GET_LIB(ERR_peek_error()));\
            MB_LOGE("   OSSL ERR_GET_FUNC - %u\n", ERR_GET_FUNC(ERR_peek_error()));\
            MB_LOGE("   OSSL ERR_GET_REASON - %u\n", ERR_GET_REASON(ERR_peek_error()));\
        }\
    } while(0)


/* mpi for GlobalPlatform */
#define GP_MPI_CAPACITY_SIZE_IN_BYTES 2
typedef struct GP_MPI_st MPI;
struct GP_MPI_st{
    unsigned char capacity[GP_MPI_CAPACITY_SIZE_IN_BYTES];  /* Number max size in U32 */
    unsigned char  mpi[1]; /* for preventing padding */
};
static const uint32_t MPI_size = (uint32_t) sizeof(MPI);
static const uint32_t MONT_CTX_size = (uint32_t) sizeof(BN_MONT_CTX);


#ifdef	__cplusplus
}
#endif

#endif	/* _ARITHMETICS_COMMON_H */

