/*************************************************************************
* Copyright (C) 2002 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.
//     DES Cipher function (MemJam mitigation included)
//
//  Contents:
//     initial permutation: ip()
//     final permutation: fp()
//     round function: rndm()
//     DES block encrypt/decrypt: Chipher_DES()
//
//
*/


#include "owndefs.h"
#include "owncp.h"
#include "pcpdes.h"
#include "pcpmask_ct.h"

/*
// Following implementation of ip(), fp() and rndm()
// assumes input and output of DES as sequence of bytes.
*/
#define ip(x,y) {                \
    Ipp32u t; \
    (x) = ROR32((x), 4);           \
    (t) = ((x)^(y)) & 0x0f0f0f0fL; \
    (y)^= (t);                     \
    (x) = ROR32((x)^(t), 12);      \
    (t) = ((y)^(x)) & 0xffff0000L; \
    (y)^= (t);                     \
    (x) = ROR32((x)^(t), 18);      \
    (t) = ((x)^(y)) & 0x33333333L; \
    (y)^= (t);                     \
    (x) = ROR32((x)^(t), 22);      \
    (t) = ((y)^(x)) & 0xff00ff00L; \
    (y)^= (t);                     \
    (x) = ROR32((x)^(t),  9);      \
    (t) = ((x)^(y)) & 0x55555555L; \
    (x) = ROL32((x)^(t), 2);       \
    (y) = ROL32((y)^(t), 1); \
}

#define fp(x,y) {                \
    Ipp32u t; \
    (y) = ROR32((y), 1);           \
    (x) = ROR32((x), 2);           \
    (t) = ((x)^(y)) & 0x55555555L; \
    (y)^= (t);                     \
    (x) = ROL32((x)^(t),  9);      \
    (t) = ((y)^(x)) & 0xff00ff00L; \
    (y)^= (t);                     \
    (x) = ROL32((x)^(t), 22);      \
    (t) = ((x)^(y)) & 0x33333333L; \
    (y)^= (t);                     \
    (x) = ROL32((x)^(t), 18);      \
    (t) = ((y)^(x)) & 0xffff0000L; \
    (y)^= (t);                     \
    (x) = ROL32((x)^(t), 12);      \
    (t) = ((x)^(y)) & 0x0f0f0f0fL; \
    (y)^= (t);                     \
    (x) = ROL32((x)^(t), 4); \
}


static BNU_CHUNK_T getSbox(const BNU_CHUNK_T sbox[], int idx)
{
   unsigned int i;
   BNU_CHUNK_T res = 0;
   for(i=0; i<(64/sizeof(BNU_CHUNK_T)); i++) {
      BNU_CHUNK_T mask = cpIsEqu_ct(i, (BNU_CHUNK_T)idx);
      res |= sbox[i] & mask;
   }
   return res;
}
static Ipp8u getSbox8u(const Ipp8u sbox[], int idx)
{
   int shift = idx % (Ipp32s)((sizeof(BNU_CHUNK_T))/sizeof(Ipp8u));
   idx /= ((sizeof(BNU_CHUNK_T))/sizeof(Ipp8u));
   return (Ipp8u)( getSbox((BNU_CHUNK_T*)sbox, idx) >>(shift*8) );
}
static Ipp32u getSbox32u(const Ipp8u sbox[], int idx)
{
   int shift = idx % (Ipp32s)((sizeof(BNU_CHUNK_T))/sizeof(Ipp32u));
   idx /= ((sizeof(BNU_CHUNK_T))/sizeof(Ipp32u));
   return (Ipp32u)( getSbox((BNU_CHUNK_T*)sbox, idx)>>shift*32 );
}

static Ipp32u rndm(Ipp32u x0, Ipp32u x1, Ipp32u* key, const Ipp8u* sbox)
{
   Ipp32u
   tt = key[0] ^ (x1 &0x3F3F3F3F);
   x0 ^= getSbox32u(sbox+512+64*0, getSbox8u(sbox+64*0, (Ipp8u)tt));
   tt >>= 8;
   x0 ^= getSbox32u(sbox+512+64*2, getSbox8u(sbox+64*2, (Ipp8u)tt));
   tt >>= 8;
   x0 ^= getSbox32u(sbox+512+64*4, getSbox8u(sbox+64*4, (Ipp8u)tt));
   tt >>= 8;
   x0 ^= getSbox32u(sbox+512+64*6, getSbox8u(sbox+64*6, (Ipp8u)tt));

   tt = (key)[1] ^ (ROR32((x1 &0xF3F3F3F3),4));
   x0 ^= getSbox32u(sbox+512+64*1, getSbox8u(sbox+64*1, (Ipp8u)tt));
   tt >>= 8;
   x0 ^= getSbox32u(sbox+512+64*3, getSbox8u(sbox+64*3, (Ipp8u)tt));
   tt >>= 8;
   x0 ^= getSbox32u(sbox+512+64*5, getSbox8u(sbox+64*5, (Ipp8u)tt));
   tt >>= 8;
   x0 ^= getSbox32u(sbox+512+64*7, getSbox8u(sbox+64*7, (Ipp8u)tt));

   return x0;
}

IPP_OWN_DEFN (Ipp64u, Cipher_DES, (Ipp64u inpBlk, const RoundKeyDES* pRKey, const Ipp32u sbox[]))
{
   const Ipp8u* sbox8 = (const Ipp8u*)sbox;

   #if (IPP_ENDIAN == IPP_BIG_ENDIAN)
   Ipp32u q0 = IPP_LODWORD(inpBlk);
   Ipp32u q1 = IPP_HIDWORD(inpBlk);
   q0 = ENDIANNESS(q0);
   q1 = ENDIANNESS(q1);
   #else
   Ipp32u q0 = IPP_HIDWORD(inpBlk);
   Ipp32u q1 = IPP_LODWORD(inpBlk);
   #endif

   /* apply inverse permutation IP */
   ip(q0,q1);

   /* 16 magic encrypt iterations */
   q0 = rndm(q0,q1, ((HalfRoundKeyDES*)(pRKey)),    sbox8);
   q1 = rndm(q1,q0, ((HalfRoundKeyDES*)(pRKey+1 )), sbox8);
   q0 = rndm(q0,q1, ((HalfRoundKeyDES*)(pRKey+2 )), sbox8);
   q1 = rndm(q1,q0, ((HalfRoundKeyDES*)(pRKey+3 )), sbox8);
   q0 = rndm(q0,q1, ((HalfRoundKeyDES*)(pRKey+4 )), sbox8);
   q1 = rndm(q1,q0, ((HalfRoundKeyDES*)(pRKey+5 )), sbox8);
   q0 = rndm(q0,q1, ((HalfRoundKeyDES*)(pRKey+6 )), sbox8);
   q1 = rndm(q1,q0, ((HalfRoundKeyDES*)(pRKey+7 )), sbox8);
   q0 = rndm(q0,q1, ((HalfRoundKeyDES*)(pRKey+8 )), sbox8);
   q1 = rndm(q1,q0, ((HalfRoundKeyDES*)(pRKey+9 )), sbox8);
   q0 = rndm(q0,q1, ((HalfRoundKeyDES*)(pRKey+10)), sbox8);
   q1 = rndm(q1,q0, ((HalfRoundKeyDES*)(pRKey+11)), sbox8);
   q0 = rndm(q0,q1, ((HalfRoundKeyDES*)(pRKey+12)), sbox8);
   q1 = rndm(q1,q0, ((HalfRoundKeyDES*)(pRKey+13)), sbox8);
   q0 = rndm(q0,q1, ((HalfRoundKeyDES*)(pRKey+14)), sbox8);
   q1 = rndm(q1,q0, ((HalfRoundKeyDES*)(pRKey+15)), sbox8);

   /* apply forward permutation FP */
   fp(q1,q0);

   #if (IPP_ENDIAN == IPP_BIG_ENDIAN)
   q0 = ENDIANNESS(q0);
   q1 = ENDIANNESS(q1);
   return IPP_MAKEDWORD(q1,q0);
   #else
   return IPP_MAKEDWORD(q0,q1);
   #endif
}
