/*
* Copyright (C) 2016 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in
*     the documentation and/or other materials provided with the
*     distribution.
*   * Neither the name of Intel Corporation nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

#include "owndefs.h"
#include "owncp.h"
#include "pcpaesm.h"
#include "pcptool.h"

#include "pcprijtables.h"

/*F*
//    Name: ippsAESGetSize
//
// Purpose: Returns size of AES context (in bytes).
//
// Returns:                Reason:
//    ippStsNullPtrErr        pSzie == NULL
//    ippStsNoErr             no errors
//
// Parameters:
//    pSize       pointer to AES size of context(in bytes)
//
*F*/
IPPFUN(IppStatus, ippsAESGetSize,(int* pSize))
{
   /* test size's pointer */
   IPP_BAD_PTR1_RET(pSize);

   *pSize = cpSizeofCtx_AES();

   return ippStsNoErr;
}

/* number of rounds (use [NK] for access) */
static int rij128nRounds[3] = {NR128_128, NR128_192, NR128_256};

/*
// number of keys (estimation only!)  (use [NK] for access)
//
// accurate number of keys necassary for encrypt/decrypt are:
//    nKeys = NB * (NR+1)
//       where NB - data block size (32-bit words)
//             NR - number of rounds (depend on NB and keyLen)
//
// but the estimation
//    estnKeys = (NK*n) >= nKeys
// or
//    estnKeys = ( (NB*(NR+1) + (NK-1)) / NK) * NK
//       where NK - key length (words)
//             NB - data block size (word)
//             NR - number of rounds (depend on NB and keyLen)
//             nKeys - accurate numner of keys
// is more convinient when calculates key extension
*/
static int rij128nKeys[3] = {44,  54,  64 };

/*
// helper for nRounds[] and estnKeys[] access
// note: x is length in 32-bits words
*/
__INLINE int rij_index(int x)
{ return (x-NB(128))>>1; }

/*F*
//    Name: ippsAESInit
//
// Purpose: Init AES context for future usage
//          and setup secret key.
//
// Returns:                Reason:
//    ippStsNullPtrErr        pCtx == NULL
//    ippStsMemAllocErr       size of buffer is not match fro operation
//    ippStsLengthErr         keyLen != 16
//                            keyLen != 24
//                            keyLen != 32
//
// Parameters:
//    pKey        secret key
//    keyLen      length of the secret key (in bytes)
//    pCtx        pointer to buffer initialized as AES context
//    ctxSize     available size (in bytes) of buffer above
//
// Note:
//    if pKey==NULL, then AES initialized by zero value key
//
*F*/
IPPFUN(IppStatus, ippsAESInit,(const Ipp8u* pKey, int keyLen,
                               IppsAESSpec* pCtxRaw, int rawCtxSize))
{
   /* test context pointer */
   IPP_BAD_PTR1_RET(pCtxRaw);

   /* make sure in legal keyLen */
   IPP_BADARG_RET(keyLen!=16 && keyLen!=24 && keyLen!=32, ippStsLengthErr);

   {
      /* use aligned Rijndael context */
      IppsAESSpec* pCtx = (IppsAESSpec*)( IPP_ALIGNED_PTR(pCtxRaw, AES_ALIGNMENT) );

      /* test available size of context buffer */
      if(((Ipp8u*)pCtx+sizeof(IppsAESSpec)) > ((Ipp8u*)pCtxRaw+rawCtxSize))
         IPP_ERROR_RET(ippStsMemAllocErr);

      else {
         int keyWords = NK(keyLen*BITSIZE(Ipp8u));
         int nExpKeys = rij128nKeys  [ rij_index(keyWords) ];
         int nRounds  = rij128nRounds[ rij_index(keyWords) ];

         Ipp8u zeroKey[32] = {0};
         const Ipp8u* pActualKey = pKey? pKey : zeroKey;

         /* clear context */
         PaddBlock(0, pCtx, sizeof(IppsAESSpec));

         /* init spec */
         RIJ_ID(pCtx) = idCtxRijndael;
         RIJ_NB(pCtx) = NB(128);
         RIJ_NK(pCtx) = keyWords;
         RIJ_NR(pCtx) = nRounds;
         RIJ_SAFE_INIT(pCtx) = 1;

         /* set key expansion */
         ExpandRijndaelKey(pActualKey, keyWords, NB(128), nRounds, nExpKeys,
                           RIJ_EKEYS(pCtx),
                           RIJ_DKEYS(pCtx));
         {
            int nr;
            Ipp8u* pEnc_key = (Ipp8u*)(RIJ_EKEYS(pCtx));
            /* update key material: transpose inplace */
            for(nr=0; nr<(1+nRounds); nr++, pEnc_key+=16) {
               SWAP(pEnc_key[ 1], pEnc_key[ 4]);
               SWAP(pEnc_key[ 2], pEnc_key[ 8]);
               SWAP(pEnc_key[ 3], pEnc_key[12]);
               SWAP(pEnc_key[ 6], pEnc_key[ 9]);
               SWAP(pEnc_key[ 7], pEnc_key[13]);
               SWAP(pEnc_key[11], pEnc_key[14]);
            }
         }
         RIJ_ENCODER(pCtx) = Safe2Encrypt_RIJ128; /* safe encoder (compact Sbox)) */
         RIJ_DECODER(pCtx) = Safe2Decrypt_RIJ128; /* safe decoder (compact Sbox)) */

         return ippStsNoErr;
      }
   }
}
