/*************************************************************************
* Copyright (C) 2019 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.
//     AES encryption (VAES-512 kernels)
//
//  Contents:
//      cpAESEncrypt1_VAES_NI
//      cpAESEncrypt2_VAES_NI
//      cpAESEncrypt3_VAES_NI
//      cpAESEncrypt4_VAES_NI
//
*/

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

#if(_IPP32E>=_IPP32E_K1)

#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
#pragma warning(disable: 4305) // zmmintrin.h bug: conversion from int to _mmask8
#endif

#if !defined(_PCP_AES_ENCRYPT_VAES512_H_)
#define _PCP_AES_ENCRYPT_VAES512_H_

////////////////////////////////////////////////////////////////////////////////

static void cpAESEncrypt1_VAES_NI(__m512i* blk0,
                                  const __m128i* pRkey,
                                  int   cipherRounds)
{
   int nr;

   __m512i rKey0 = _mm512_broadcast_i64x2(pRkey[0]);
   __m512i rKey1 = _mm512_broadcast_i64x2(pRkey[1]);

   __m512i b0 = _mm512_xor_si512(*blk0, rKey0);
   rKey0      = _mm512_broadcast_i64x2(pRkey[2]);

   for (nr = 1, pRkey++; nr < cipherRounds; nr += 2, pRkey += 2) {
      b0    = _mm512_aesenc_epi128(b0, rKey1);
      rKey1 = _mm512_broadcast_i64x2(pRkey[2]);
      b0    = _mm512_aesenc_epi128(b0, rKey0);
      rKey0 = _mm512_broadcast_i64x2(pRkey[3]);
   }

   b0 = _mm512_aesenc_epi128(b0, rKey1);
   *blk0 = _mm512_aesenclast_epi128(b0, rKey0);

   rKey0 = _mm512_setzero_si512();
   rKey1 = _mm512_setzero_si512();
}

static void cpAESEncrypt2_VAES_NI(__m512i* blk0,
                                 __m512i* blk1,
                                 const __m128i* pRkey,
                                 int   cipherRounds)
{
   int nr;

   __m512i rKey0 = _mm512_broadcast_i64x2(pRkey[0]);
   __m512i rKey1 = _mm512_broadcast_i64x2(pRkey[1]);

   __m512i b0 = _mm512_xor_si512(*blk0, rKey0);
   __m512i b1 = _mm512_xor_si512(*blk1, rKey0);
   rKey0      = _mm512_broadcast_i64x2(pRkey[2]);

   for (nr = 1, pRkey++; nr < cipherRounds; nr += 2, pRkey += 2) {
      b0    = _mm512_aesenc_epi128(b0, rKey1);
      b1    = _mm512_aesenc_epi128(b1, rKey1);
      rKey1 = _mm512_broadcast_i64x2(pRkey[2]);

      b0    = _mm512_aesenc_epi128(b0, rKey0);
      b1    = _mm512_aesenc_epi128(b1, rKey0);
      rKey0 = _mm512_broadcast_i64x2(pRkey[3]);
   }

   b0 = _mm512_aesenc_epi128(b0, rKey1);
   b1 = _mm512_aesenc_epi128(b1, rKey1);

   *blk0 = _mm512_aesenclast_epi128(b0, rKey0);
   *blk1 = _mm512_aesenclast_epi128(b1, rKey0);

   rKey0 = _mm512_setzero_si512();
   rKey1 = _mm512_setzero_si512();
}

static void cpAESEncrypt3_VAES_NI(__m512i* blk0,
                                  __m512i* blk1,
                                  __m512i* blk2,
                                  const __m128i* pRkey,
                                  int   cipherRounds)
{
   int nr;

   __m512i rKey0 = _mm512_broadcast_i64x2(pRkey[0]);
   __m512i rKey1 = _mm512_broadcast_i64x2(pRkey[1]);

   __m512i b0 = _mm512_xor_si512(*blk0, rKey0);
   __m512i b1 = _mm512_xor_si512(*blk1, rKey0);
   __m512i b2 = _mm512_xor_si512(*blk2, rKey0);
   rKey0      = _mm512_broadcast_i64x2(pRkey[2]);

   for (nr = 1, pRkey++; nr < cipherRounds; nr += 2, pRkey += 2) {
      b0    = _mm512_aesenc_epi128(b0, rKey1);
      b1    = _mm512_aesenc_epi128(b1, rKey1);
      b2    = _mm512_aesenc_epi128(b2, rKey1);
      rKey1 = _mm512_broadcast_i64x2(pRkey[2]);

      b0    = _mm512_aesenc_epi128(b0, rKey0);
      b1    = _mm512_aesenc_epi128(b1, rKey0);
      b2    = _mm512_aesenc_epi128(b2, rKey0);
      rKey0 = _mm512_broadcast_i64x2(pRkey[3]);
   }

   b0 = _mm512_aesenc_epi128(b0, rKey1);
   b1 = _mm512_aesenc_epi128(b1, rKey1);
   b2 = _mm512_aesenc_epi128(b2, rKey1);

   *blk0 = _mm512_aesenclast_epi128(b0, rKey0);
   *blk1 = _mm512_aesenclast_epi128(b1, rKey0);
   *blk2 = _mm512_aesenclast_epi128(b2, rKey0);

   rKey0 = _mm512_setzero_si512();
   rKey1 = _mm512_setzero_si512();
}

static void cpAESEncrypt4_VAES_NI(__m512i* blk0,
                                  __m512i* blk1,
                                  __m512i* blk2,
                                  __m512i* blk3,
                                  const __m128i* pRkey,
                                  int   cipherRounds)
{
   int nr;

   __m512i rKey0 = _mm512_broadcast_i64x2(pRkey[0]);
   __m512i rKey1 = _mm512_broadcast_i64x2(pRkey[1]);

   __m512i b0 = _mm512_xor_si512(*blk0, rKey0);
   __m512i b1 = _mm512_xor_si512(*blk1, rKey0);
   __m512i b2 = _mm512_xor_si512(*blk2, rKey0);
   __m512i b3 = _mm512_xor_si512(*blk3, rKey0);
   rKey0      = _mm512_broadcast_i64x2(pRkey[2]);

   for (nr = 1, pRkey++; nr < cipherRounds; nr += 2, pRkey += 2) {
      b0    = _mm512_aesenc_epi128(b0, rKey1);
      b1    = _mm512_aesenc_epi128(b1, rKey1);
      b2    = _mm512_aesenc_epi128(b2, rKey1);
      b3    = _mm512_aesenc_epi128(b3, rKey1);
      rKey1 = _mm512_broadcast_i64x2(pRkey[2]);

      b0    = _mm512_aesenc_epi128(b0, rKey0);
      b1    = _mm512_aesenc_epi128(b1, rKey0);
      b2    = _mm512_aesenc_epi128(b2, rKey0);
      b3    = _mm512_aesenc_epi128(b3, rKey0);
      rKey0 = _mm512_broadcast_i64x2(pRkey[3]);
   }
   b0 = _mm512_aesenc_epi128(b0, rKey1);
   b1 = _mm512_aesenc_epi128(b1, rKey1);
   b2 = _mm512_aesenc_epi128(b2, rKey1);
   b3 = _mm512_aesenc_epi128(b3, rKey1);

   *blk0 = _mm512_aesenclast_epi128(b0, rKey0);
   *blk1 = _mm512_aesenclast_epi128(b1, rKey0);
   *blk2 = _mm512_aesenclast_epi128(b2, rKey0);
   *blk3 = _mm512_aesenclast_epi128(b3, rKey0);

   rKey0 = _mm512_setzero_si512();
   rKey1 = _mm512_setzero_si512();
}

#endif /* _PCP_AES_ENCRYPT_VAES512_H_ */

#endif /* _IPP32E>=_IPP32E_K1 */
