/*************************************************************************
* Copyright (C) 2013 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.
//     Internal Definitions and
//     Internal SMS4 Function Prototypes
//
//
*/

#if !defined(_PCP_SMS4_H)
#define _PCP_SMS4_H

#include "owncp.h"
#include "pcpmask_ct.h"

/* SMS4 round keys number */
#define SMS4_ROUND_KEYS_NUM (32)

#if SMS4_ROUND_KEYS_NUM != 32
#error SMS4_ROUND_KEYS_NUM must be equal 32
#endif

struct _cpSMS4 {
    Ipp32u idCtx;                          /* SMS4 spec identifier */
    Ipp32u enc_rkeys[SMS4_ROUND_KEYS_NUM]; /* enc round keys       */
    Ipp32u dec_rkeys[SMS4_ROUND_KEYS_NUM]; /* dec round keys       */
};

/*
// access macros
*/
#define SMS4_SET_ID(ctx) ((ctx)->idCtx = (Ipp32u)idCtxSMS4 ^ (Ipp32u)IPP_UINT_PTR(ctx))
#define SMS4_RK(ctx)     ((ctx)->enc_rkeys)
#define SMS4_ERK(ctx)    ((ctx)->enc_rkeys)
#define SMS4_DRK(ctx)    ((ctx)->dec_rkeys)

/* SMS4 data block size (bytes) */
#define MBS_SMS4 (16)

#if MBS_SMS4 != 16
#error MBS_SMS4 must be equal 16
#endif

/* valid SMS4 context ID */
#define VALID_SMS4_ID(ctx) ((((ctx)->idCtx) ^ (Ipp32u)IPP_UINT_PTR((ctx))) == (Ipp32u)idCtxSMS4)

/* alignment of AES context */
#define SMS4_ALIGNMENT (4)

/* size of SMS4 context */
IPPCP_INLINE int cpSizeofCtx_SMS4(void) { return sizeof(IppsSMS4Spec); }

/* SMS4 constants */
extern const __ALIGN64 Ipp8u SMS4_Sbox[16 * 16];
extern const Ipp32u SMS4_FK[4];
extern const Ipp32u SMS4_CK[32];

//////////////////////////////////////////////////////////////////////////////////////////////////////
/* S-box substitution (endian dependent!) */

#include "pcpbnuimpl.h"
#define SELECTION_BITS ((sizeof(BNU_CHUNK_T) / sizeof(Ipp8u)) - 1)

IPPCP_INLINE Ipp8u getSboxValue(Ipp8u x)
{
    BNU_CHUNK_T selection  = 0;
    const Ipp8u* SboxEntry = SMS4_Sbox;

    Ipp32u i;
    for (i = 0; i < sizeof(SMS4_Sbox); i++) {
        BNU_CHUNK_T mask = cpIsEqu_ct(x, i);
        selection |= SboxEntry[i] & mask;
    }
    return (Ipp8u)(selection & 0xFF);
}

IPPCP_INLINE Ipp32u cpSboxT_SMS4(Ipp32u x)
{
    Ipp32u y = getSboxValue(x & 0xFF);
    y |= (Ipp32u)(getSboxValue((x >> 8) & 0xFF) << 8);
    y |= (Ipp32u)(getSboxValue((x >> 16) & 0xFF) << 16);
    y |= (Ipp32u)(getSboxValue((x >> 24) & 0xFF) << 24);
    return y;
}

/* key expansion transformation:
   - linear Linear
   - mixer Mix (permutation T in the SMS4 standard phraseology)
*/
IPPCP_INLINE Ipp32u cpExpKeyLinear_SMS4(Ipp32u x) { return x ^ ROL32(x, 13) ^ ROL32(x, 23); }

IPPCP_INLINE Ipp32u cpExpKeyMix_SMS4(Ipp32u x) { return cpExpKeyLinear_SMS4(cpSboxT_SMS4(x)); }

/* cipher transformations:
   - linear Linear
   - mixer Mix (permutation T in the SMS4 standard phraseology)
*/
IPPCP_INLINE Ipp32u cpCipherLinear_SMS4(Ipp32u x)
{
    return x ^ ROL32(x, 2) ^ ROL32(x, 10) ^ ROL32(x, 18) ^ ROL32(x, 24);
}

IPPCP_INLINE Ipp32u cpCipherMix_SMS4(Ipp32u x) { return cpCipherLinear_SMS4(cpSboxT_SMS4(x)); }
//////////////////////////////////////////////////////////////////////////////////////////////


#define cpSMS4_Cipher OWNAPI(cpSMS4_Cipher)
IPP_OWN_DECL(void, cpSMS4_Cipher, (Ipp8u * otxt, const Ipp8u* itxt, const Ipp32u* pRoundKeys))

#if (_IPP >= _IPP_P8) || (_IPP32E >= _IPP32E_Y8)
#define cpSMS4_SetRoundKeys_aesni OWNAPI(cpSMS4_SetRoundKeys_aesni)
IPP_OWN_DECL(void, cpSMS4_SetRoundKeys_aesni, (Ipp32u * pRounKey, const Ipp8u* pSecretKey))

#define cpSMS4_ECB_aesni_x1 OWNAPI(cpSMS4_ECB_aesni_x1)
IPP_OWN_DECL(void, cpSMS4_ECB_aesni_x1, (Ipp8u * pOut, const Ipp8u* pInp, const Ipp32u* pRKey))
#define cpSMS4_ECB_aesni OWNAPI(cpSMS4_ECB_aesni)
/* clang-format off */
IPP_OWN_DECL(int, cpSMS4_ECB_aesni, (Ipp8u* pDst,
                                     const Ipp8u* pSrc,
                                     int nLen,
                                     const Ipp32u* pRKey))
/* clang-format on */
#define cpSMS4_CBC_dec_aesni OWNAPI(cpSMS4_CBC_dec_aesni)
/* clang-format off */
IPP_OWN_DECL(int, cpSMS4_CBC_dec_aesni, (Ipp8u* pDst,
                                         const Ipp8u* pSrc,
                                         int nLen,
                                         const Ipp32u* pRKey,
                                         Ipp8u* pIV))
/* clang-format on */
#define cpSMS4_CTR_aesni OWNAPI(cpSMS4_CTR_aesni)
/* clang-format off */
IPP_OWN_DECL(int, cpSMS4_CTR_aesni, (Ipp8u* pOut,
                                     const Ipp8u* pInp,
                                     int len,
                                     const Ipp32u* pRKey,
                                     const Ipp8u* pCtrMask,
                                     Ipp8u* pCtr))
/* clang-format on */
#if (_IPP32E >= _IPP32E_L9)
// This function is based on vsm4key4 instruction
#define cpSMS4_SetRoundKeys_ni OWNAPI(cpSMS4_SetRoundKeys_ni)
IPP_OWN_DECL(void, cpSMS4_SetRoundKeys_ni, (Ipp32u * pRoundKey, const Ipp8u* pSecretKey))

// This function processes 128-bit block of data with vsm4rnds4 instruction (xmm registers)
#define cpSMS4_ECB_ni OWNAPI(cpSMS4_ECB_ni)
IPP_OWN_DECL(void, cpSMS4_ECB_ni, (Ipp8u * otxt, const Ipp8u* itxt, const Ipp32u* pRoundKeys))
// This function processes 256-bit block of data with vsm4rnds4 instruction (ymm registers)
#define cpSMS4_ECB_ni_256 OWNAPI(cpSMS4_ECB_ni_256)
IPP_OWN_DECL(void, cpSMS4_ECB_ni_256, (Ipp8u * otxt, const Ipp8u* itxt, const Ipp32u* pRoundKeys))

#endif /* #if (_IPP32E>=_IPP32E_L9) */

#if (_IPP >= _IPP_H9) || (_IPP32E >= _IPP32E_L9)
#define cpSMS4_ECB_aesni_x12 OWNAPI(cpSMS4_ECB_aesni_x12)
/* clang-format off */
IPP_OWN_DECL(int, cpSMS4_ECB_aesni_x12, (Ipp8u* pOut,
                                         const Ipp8u* pInp,
                                         int len,
                                         const Ipp32u* pRKey))
/* clang-format on */
#define cpSMS4_CBC_dec_aesni_x12 OWNAPI(cpSMS4_CBC_dec_aesni_x12)
/* clang-format off */
IPP_OWN_DECL(int, cpSMS4_CBC_dec_aesni_x12, (Ipp8u * pOut,
                                             const Ipp8u* pInp,
                                             int len,
                                             const Ipp32u* pRKey,
                                             Ipp8u* pIV))
/* clang-format on */
#define cpSMS4_CTR_aesni_x4 OWNAPI(cpSMS4_CTR_aesni_x4)
/* clang-format off */
IPP_OWN_DECL(int, cpSMS4_CTR_aesni_x4, (Ipp8u* pOut,
                                        const Ipp8u* pInp,
                                        int len,
                                        const Ipp32u* pRKey,
                                        const Ipp8u* pCtrMask,
                                        Ipp8u* pCtr))
/* clang-format on */
#if (_IPP32E >= _IPP32E_K1)
#if defined(__INTEL_COMPILER) || defined(__INTEL_LLVM_COMPILER) || !defined(_MSC_VER) || \
    (_MSC_VER >= 1920)

#define cpSMS4_ECB_gfni_x1 OWNAPI(cpSMS4_ECB_gfni_x1)
IPP_OWN_DECL(void, cpSMS4_ECB_gfni_x1, (Ipp8u * pOut, const Ipp8u* pInp, const Ipp32u* pRKey))
#define cpSMS4_ECB_gfni512 OWNAPI(cpSMS4_ECB_gfni512)
/* clang-format off */
IPP_OWN_DECL(int, cpSMS4_ECB_gfni512, (Ipp8u* pOut,
                                       const Ipp8u* pInp,
                                       int len,
                                       const Ipp32u* pRKey))
/* clang-format on */
#define cpSMS4_CBC_dec_gfni512 OWNAPI(cpSMS4_CBC_dec_gfni512)
/* clang-format off */
IPP_OWN_DECL(int, cpSMS4_CBC_dec_gfni512, (Ipp8u* pOut,
                                           const Ipp8u* pInp,
                                           int len,
                                           const Ipp32u* pRKey,
                                           Ipp8u* pIV))
/* clang-format on */
#define cpSMS4_CTR_gfni512 OWNAPI(cpSMS4_CTR_gfni512)
/* clang-format off */
IPP_OWN_DECL(int, cpSMS4_CTR_gfni512, (Ipp8u* pOut,
                                       const Ipp8u* pInp,
                                       int len,
                                       const Ipp32u* pRKey,
                                       const Ipp8u* pCtrMask,
                                       Ipp8u* pCtr))
/* clang-format on */
#define cpSMS4_CFB_dec_gfni512 OWNAPI(cpSMS4_CFB_dec_gfni512)
/* clang-format off */
IPP_OWN_DECL(void, cpSMS4_CFB_dec_gfni512, (Ipp8u* pOut,
                                            const Ipp8u* pInp,
                                            int len,
                                            int cfbBlkSize,
                                            const Ipp32u* pRKey,
                                            Ipp8u* pIV))
/* clang-format on */

#endif /* #if defined (__INTEL_COMPILER) || defined (__INTEL_LLVM_COMPILER) || !defined (_MSC_VER) || (_MSC_VER >= 1920) */
#endif /* (_IPP32E>=_IPP32E_K1) */

#endif /* (_IPP>=_IPP_H9) || (_IPP32E>=_IPP32E_L9) */

#endif

#define cpProcessSMS4_ctr OWNAPI(cpProcessSMS4_ctr)
/* clang-format off */
IPP_OWN_DECL(IppStatus, cpProcessSMS4_ctr, (const Ipp8u* pSrc,
                                            Ipp8u* pDst,
                                            int dataLen,
                                            const IppsSMS4Spec* pCtx,
                                            Ipp8u* pCtrValue,
                                            int ctrNumBitSize))
/* clang-format on */
#endif /* _PCP_SMS4_H */
