/*************************************************************************
* Copyright (C) 2015 Intel Corporation
*
* Licensed under the Apache License,  Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 	http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law  or agreed  to  in  writing,  software
* distributed under  the License  is  distributed  on  an  "AS IS"  BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the  specific  language  governing  permissions  and
* limitations under the License.
*************************************************************************/

/*
//
//  Purpose:
//     Cryptography Primitive.
//     Message Authentication Algorithm
//     Internal Definitions and Internal Functions Prototypes
//
//
*/

#if !defined(_CP_AESAUTH_GCM_H)
#define _CP_AESAUTH_GCM_H

#include "owncp.h"
#include "pcpaesm.h"

#if (_IPP32E < _IPP32E_K0)

#define BLOCK_SIZE (MBS_RIJ128)

/* GCM Hash prototype: GHash = GHash*HKey mod G() */
IPP_OWN_FUNPTR(void, MulGcm_, (Ipp8u * pGHash, const Ipp8u* pHKey, const void* pParam))

/* GCM Authentication prototype: GHash = (GHash^src[])*HKey mod G() */
/* clang-format off */
IPP_OWN_FUNPTR(void, Auth_, (Ipp8u* pHash,
                             const Ipp8u* pSrc,
                             int len,
                             const Ipp8u* pHKey,
                             const void* pParam))
/* clang-format on */

/* GCM Encrypt_Authentication prototype */
IPP_OWN_FUNPTR(void, Encrypt_, (Ipp8u * pDst, const Ipp8u* pSrc, int len, IppsAES_GCMState* pCtx))

/* GCM Authentication_Decrypt prototype */
IPP_OWN_FUNPTR(void, Decrypt_, (Ipp8u * pDst, const Ipp8u* pSrc, int len, IppsAES_GCMState* pCtx))

typedef enum { GcmInit, GcmIVprocessing, GcmAADprocessing, GcmTXTprocessing } GcmState;

struct _cpAES_GCM {

    Ipp32u idCtx;                  /* AES-GCM id                    */
    GcmState state;                /* GCM state: Init, IV|AAD|TXT processing */
    Ipp64u ivLen;                  /* IV length (bytes)             */
    Ipp64u aadLen;                 /* header length (bytes)         */
    Ipp64u txtLen;                 /* text length (bytes)           */

    int bufLen;                    /* stuff buffer length           */
    __ALIGN16                      /* aligned buffers               */
        Ipp8u counter[BLOCK_SIZE]; /* counter                       */
    Ipp8u ecounter0[BLOCK_SIZE];   /* encrypted initial counter     */
    Ipp8u ecounter[BLOCK_SIZE];    /* encrypted counter             */
    Ipp8u ghash[BLOCK_SIZE];       /* ghash accumulator             */

    MulGcm_ hashFun;               /* AES-GCM mul function          */
    Auth_ authFun;                 /* authentication function       */
    Encrypt_ encFun;               /* encryption & authentication   */
    Decrypt_ decFun;               /* authentication & decryption   */

    __ALIGN16                      /* aligned AES context           */
        IppsAESSpec cipher;

#if (_AES_PROB_NOISE == _FEATURE_ON_)
    __ALIGN16
    cpAESNoiseParams noiseParams;
#endif

    __ALIGN16                         /* aligned pre-computed data:    */
        Ipp8u multiplier[BLOCK_SIZE]; /* - (default) hKey                             */
                                      /* - (aes_ni)  hKey*t, (hKey*t)^2, (hKey*t)^4   */
                                      /* - (avx2_vaes) 16 vectors by 128-bit values
                                       hKey<<1,    hKey^2<<1,  hKey^3<<1,  hKey^4<<1,
                                       hKey^5<<1,  hKey^6<<1,  hKey^7<<1,  hKey^8<<1,
                                       hKey^9<<1,  hKey^10<<1, hKey^11<<1, hKey^12<<1,
                                       hKey^13<<1, hKey^14<<1, hKey^15<<1, hKey^16<<1,
                                    */
                                      /* - (vaes_ni) 8 reverted ordered vectors by 4 128-bit values.
                                      hKeys derivations in the multiplier[] array in order of appearance
                                      (zero-index starts from the left):
                                      hKey^4<<1, hKey^3<<1,   hKey^2<<1,  hKey<<1,
                                      hKey^8<<1, hKey^7<<1,   hKey^6<<1,  hKey^5<<1,
                                      hKey^12<<1, hKey^11<<1, hKey^10<<1, hKey^9<<1,
                                      hKey^16<<1, hKey^15<<1, hKey^14<<1, hKey^13<<1,
                                      ... <same 4 vectors for Karatsuba partial products> ...
                                     */
                                      /* - (safe) hKey*(t^i), i=0,...,127             */
};

#define CTR_POS 12

/* alignment */
#define AESGCM_ALIGNMENT (16)

#define PRECOMP_DATA_SIZE_AES_NI_AESGCM    (BLOCK_SIZE * 4)
#define PRECOMP_DATA_SIZE_AVX2_VAES_AESGCM (BLOCK_SIZE * 16)
#define PRECOMP_DATA_SIZE_VAES_NI_AESGCM   (BLOCK_SIZE * 16 * 2)
#define PRECOMP_DATA_SIZE_FAST2K           (BLOCK_SIZE * 128)

/*
// Useful macros
*/
#define AESGCM_SET_ID(context) \
    ((context)->idCtx = (Ipp32u)idCtxAESGCM ^ (Ipp32u)IPP_UINT_PTR(context))
#define AESGCM_STATE(context) ((context)->state)

#define AESGCM_IV_LEN(context)  ((context)->ivLen)
#define AESGCM_AAD_LEN(context) ((context)->aadLen)
#define AESGCM_TXT_LEN(context) ((context)->txtLen)

#define AESGCM_BUFLEN(context)    ((context)->bufLen)
#define AESGCM_COUNTER(context)   ((context)->counter)
#define AESGCM_ECOUNTER0(context) ((context)->ecounter0)
#define AESGCM_ECOUNTER(context)  ((context)->ecounter)
#define AESGCM_GHASH(context)     ((context)->ghash)

#define AESGCM_HASH(context) ((context)->hashFun)
#define AESGCM_AUTH(context) ((context)->authFun)
#define AESGCM_ENC(context)  ((context)->encFun)
#define AESGCM_DEC(context)  ((context)->decFun)

#define AESGCM_CIPHER(context) (IppsAESSpec*)(&((context)->cipher))

#if (_AES_PROB_NOISE == _FEATURE_ON_)
#define AESGCM_NOISE_PARAMS(context) ((context)->noiseParams)
#endif

#define AESGCM_HKEY(context)  ((context)->multiplier)
#define AESGCM_CPWR(context)  ((context)->multiplier)
#define AES_GCM_MTBL(context) ((context)->multiplier)

#define AESGCM_VALID_ID(context) \
    ((((context)->idCtx) ^ (Ipp32u)IPP_UINT_PTR((context))) == (Ipp32u)idCtxAESGCM)

#if 0
IPPCP_INLINE void IncrementCounter32(Ipp8u* pCtr)
{
    int i;
    for(i=BLOCK_SIZE-1; i>=CTR_POS && 0==(Ipp8u)(++pCtr[i]); i--) ;
}
#endif
IPPCP_INLINE void IncrementCounter32(Ipp8u* pCtr)
{
    Ipp32u* pCtr32 = (Ipp32u*)pCtr;
    Ipp32u ctrVal  = pCtr32[3];
    ctrVal         = ENDIANNESS32(ctrVal);
    ctrVal++;
    ctrVal    = ENDIANNESS32(ctrVal);
    pCtr32[3] = ctrVal;
}

#if (_IPP >= _IPP_P8) || (_IPP32E >= _IPP32E_Y8)
#define AesGcmPrecompute_avx2_vaes OWNAPI(AesGcmPrecompute_avx2_vaes)
IPP_OWN_DECL(void, AesGcmPrecompute_avx2_vaes, (Ipp8u * pPrecomputeData, const Ipp8u* pHKey))
#define AesGcmPrecompute_avx OWNAPI(AesGcmPrecompute_avx)
IPP_OWN_DECL(void, AesGcmPrecompute_avx, (Ipp8u * pPrecomputeData, const Ipp8u* pHKey))
#define AesGcmMulGcm_avx OWNAPI(AesGcmMulGcm_avx)
IPP_OWN_DECL(void, AesGcmMulGcm_avx, (Ipp8u * pGhash, const Ipp8u* pHkey, const void* pParam))
/* clang-format off */
#define AesGcmAuth_avx OWNAPI(AesGcmAuth_avx)
   IPP_OWN_DECL (void, AesGcmAuth_avx, (Ipp8u* pGhash, const Ipp8u* pSrc, int len, const Ipp8u* pHkey, const void* pParam))
#define wrpAesGcmEnc_avx OWNAPI(wrpAesGcmEnc_avx)
   IPP_OWN_DECL (void, wrpAesGcmEnc_avx, (Ipp8u* pDst, const Ipp8u* pSrc, int len, IppsAES_GCMState* pCtx))
#define wrpAesGcmDec_avx OWNAPI(wrpAesGcmDec_avx)
   IPP_OWN_DECL (void, wrpAesGcmDec_avx, (Ipp8u* pDst, const Ipp8u* pSrc, int len, IppsAES_GCMState* pCtx))
#define AesGcmEnc_avx OWNAPI(AesGcmEnc_avx)
   IPP_OWN_DECL (void, AesGcmEnc_avx, (Ipp8u* pDst, const Ipp8u* pSrc, int len, RijnCipher cipher, int nr, const Ipp8u* pKeys, Ipp8u* pGhash, Ipp8u* pCnt, Ipp8u* pECnt, const Ipp8u* pMuls))
#define AesGcmDec_avx OWNAPI(AesGcmDec_avx)
   IPP_OWN_DECL (void, AesGcmDec_avx, (Ipp8u* pDst, const Ipp8u* pSrc, int len, RijnCipher cipher, int nr, const Ipp8u* pKeys, Ipp8u* pGhash, Ipp8u* pCnt, Ipp8u* pECnt, const Ipp8u* pMuls))
#endif
/* clang-format on */


#if (_IPP32E >= _IPP32E_K0)
#define AesGcmPrecompute_vaes OWNAPI(AesGcmPrecompute_vaes)
IPP_OWN_DECL(void, AesGcmPrecompute_vaes, (Ipp8u* const pPrecomputeData, const Ipp8u* const pHKey))
#define AesGcmMulGcm_vaes OWNAPI(AesGcmMulGcm_vaes)
IPP_OWN_DECL(void, AesGcmMulGcm_vaes, (Ipp8u * pGhash, const Ipp8u* pHkey, const void* pParam))
/* clang-format off */
#define AesGcmAuth_vaes OWNAPI(AesGcmAuth_vaes)
IPP_OWN_DECL(void, AesGcmAuth_vaes, (Ipp8u* pGhash, const Ipp8u* pSrc, int len, const Ipp8u* pHkey, const void* pParam))
#define AesGcmEnc_vaes OWNAPI(AesGcmEnc_vaes)
IPP_OWN_DECL(void, AesGcmEnc_vaes, (Ipp8u* pDst, const Ipp8u* pSrc, int len, IppsAES_GCMState* pCtx))
#define AesGcmDec_vaes OWNAPI(AesGcmDec_vaes)
IPP_OWN_DECL(void, AesGcmDec_vaes, (Ipp8u* pDst, const Ipp8u* pSrc, int len, IppsAES_GCMState* pCtx))
#endif /* _IPP32E>=_IPP32E_K0 */
/* clang-format on */

#define AesGcmPrecompute_table2K OWNAPI(AesGcmPrecompute_table2K)
IPP_OWN_DECL(void, AesGcmPrecompute_table2K, (Ipp8u * pPrecomputeData, const Ipp8u* pHKey))

/* #define AesGcmMulGcm_table2K OWNAPI(AesGcmMulGcm_table2K) */
/*    IPP_OWN_DECL (void, AesGcmMulGcm_table2K, (Ipp8u* pGhash, const Ipp8u* pHkey, const void* pParam)) */
/* clang-format off */
#define AesGcmMulGcm_table2K_ct OWNAPI(AesGcmMulGcm_table2K_ct)
   IPP_OWN_DECL (void, AesGcmMulGcm_table2K_ct, (Ipp8u* pGhash, const Ipp8u* pHkey, const void* pParam))

/* #define AesGcmAuth_table2K OWNAPI(AesGcmAuth_table2K) */
/*    IPP_OWN_DECL (void, AesGcmAuth_table2K, (Ipp8u* pGhash, const Ipp8u* pSrc, int len, const Ipp8u* pHkey, const void* pParam)) */
#define AesGcmAuth_table2K_ct OWNAPI(AesGcmAuth_table2K_ct)
   IPP_OWN_DECL (void, AesGcmAuth_table2K_ct, (Ipp8u* pGhash, const Ipp8u* pSrc, int len, const Ipp8u* pHkey, const void* pParam))

#define wrpAesGcmEnc_table2K OWNAPI(wrpAesGcmEnc_table2K)
   IPP_OWN_DECL (void, wrpAesGcmEnc_table2K, (Ipp8u* pDst, const Ipp8u* pSrc, int len, IppsAES_GCMState* pCtx))
#define wrpAesGcmDec_table2K OWNAPI(wrpAesGcmDec_table2K)
   IPP_OWN_DECL (void, wrpAesGcmDec_table2K, (Ipp8u* pDst, const Ipp8u* pSrc, int len, IppsAES_GCMState* pCtx))

#if (_IPP == _IPP_H9) || (_IPP32E == _IPP32E_L9)
#define AesGcmEnc_vaes_avx2 OWNAPI(AesGcmEnc_vaes_avx2)
   IPP_OWN_DECL (void, AesGcmEnc_vaes_avx2, (Ipp8u* pDst, const Ipp8u* pSrc, int len, IppsAES_GCMState* pCtx))
#define AesGcmDec_vaes_avx2 OWNAPI(AesGcmDec_vaes_avx2)
   IPP_OWN_DECL (void, AesGcmDec_vaes_avx2, (Ipp8u* pDst, const Ipp8u* pSrc, int len, IppsAES_GCMState* pCtx))
#endif /* #if (_IPP==_IPP_H9) || (_IPP32E==_IPP32E_L9) */

/* clang-format on */

extern const Ipp16u AesGcmConst_table[256]; /* precomputed reduction table */

static int cpSizeofCtx_AESGCM(void)
{
    int precomp_size;

#if (_IPP >= _IPP_P8) || (_IPP32E >= _IPP32E_Y8)
    if (IsFeatureEnabled(ippCPUID_AVX2VAES | ippCPUID_AVX2VCLMUL))
        precomp_size = PRECOMP_DATA_SIZE_AVX2_VAES_AESGCM;
    else if (IsFeatureEnabled(ippCPUID_AES | ippCPUID_CLMUL))
        precomp_size = PRECOMP_DATA_SIZE_AES_NI_AESGCM;
    else
#endif
        precomp_size = PRECOMP_DATA_SIZE_FAST2K;

    /* decrease precomp_size as soon as BLOCK_SIZE bytes already reserved in context */
    precomp_size -= BLOCK_SIZE;

    return (Ipp32s)sizeof(IppsAES_GCMState) + precomp_size + AESGCM_ALIGNMENT - 1;
}

#endif // (_IPP32E < _IPP32E_K0)

#endif /* _CP_AESAUTH_GCM_H*/
