/*************************************************************************
* 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.
//     Set Round Keys for DES/TDES
// 
//  Contents:
//     SetKey_DES()
//     ippcSetKey_DES()
// 
// 
*/

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


/*
// bit 0 is left-most in byte (reference FIPS 46-3)
*/
static int bytebit[] = {
    0x80,0x40,0x20,0x10,0x8,0x4,0x2,0x1
};

/*
// Key schedule-related tables (reference FIPS 46-3)
*/

/* PC-1 permuted table (for the user key) */
static Ipp8u pc1[] = {
    57, 49, 41, 33, 25, 17,  9,
     1, 58, 50, 42, 34, 26, 18,
    10,  2, 59, 51, 43, 35, 27,
    19, 11,  3, 60, 52, 44, 36,

    63, 55, 47, 39, 31, 23, 15,
     7, 62, 54, 46, 38, 30, 22,
    14,  6, 61, 53, 45, 37, 29,
    21, 13,  5, 28, 20, 12,  4
};

/* number left rotations of PC-1 key */
static int rotations[] = {
    1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28
};

/* PC-2 table (for round key construction) */
static Ipp8u pc2[] = {
    14, 17, 11, 24,  1,  5,
     3, 28, 15,  6, 21, 10,
    23, 19, 12,  4, 26,  8,
    16,  7, 27, 20, 13,  2,
    41, 52, 31, 37, 47, 55,
    30, 40, 51, 45, 33, 48,
    44, 49, 39, 56, 34, 53,
    46, 42, 50, 36, 29, 32
};

#define SWAP8U(a,b) { Ipp8u x=(a); (a)=(b); (b)=x; }
/*
// Generate key schedule for encryption and decryption
*/
IPP_OWN_DEFN (void, SetKey_DES, (const Ipp8u* pKey, IppsDESSpec* pCtx))
{
   RoundKeyDES* pEncRoundKey = DES_EKEYS(pCtx);
   RoundKeyDES* pDecRoundKey = DES_DKEYS(pCtx);

   __ALIGN64 Ipp8u pc1key[56];        /* PC-1 permuted user key bits */
   __ALIGN64 Ipp8u rndkey[56];        /* rounded key bits */

   int nkey;
   int i;

   /*
   // apply permutation PC-1
   */
   for(i=0; i<56; i++) {
      /* location into the user key (bit number) */
      int nBit = pc1[i]-1;
      /* location into the user key (byte and offset in the byte) */
      int nByte  = nBit>>3;
      int offset = nBit & 0x07;

      /* test bit in the user key and set into the permuted */
      pc1key[i] = (Ipp8u)( (pKey[nByte] & bytebit[offset] ) >>(7-offset));
   }

   /*
   // key schedule for encryption
   */
   for(nkey=0; nkey<16; nkey++) {
      Ipp64u tmp = 0;
      Ipp8u* rkeyNibble = (Ipp8u*)(&tmp);

      int pc1keyBit, pc1keyBit_m28;
      Ipp32u mask;

      /* rotate right pc1key */
      for (i=0; i<28; i++) {
         pc1keyBit = i+rotations[nkey];
         pc1keyBit_m28 = pc1keyBit-28;
         mask = (Ipp32u)(pc1keyBit_m28>>(BITSIZE(Ipp32u)-1));
         pc1keyBit = (Ipp32s)((Ipp32u)pc1keyBit & mask) | (Ipp32s)((Ipp32u)pc1keyBit_m28 &~mask);
         rndkey[i] = pc1key[pc1keyBit];
      }
      for (; i<56; i++) {
         pc1keyBit = i+rotations[nkey];
         pc1keyBit_m28 = pc1keyBit-28;
         mask = (Ipp32u)((pc1keyBit-56)>>(BITSIZE(Ipp32u)-1));
         pc1keyBit = (Ipp32s)((Ipp32u)pc1keyBit & mask) | (Ipp32s)((Ipp32u)pc1keyBit_m28 &~mask);
         rndkey[i] = pc1key[pc1keyBit];
      }

      /*
      // construct eight 6-bit nibbles of round key
      // applying PC-2 permutation to rndkey[]
      */
      for(i=0; i<48; i++) {
         int offset = i%6;
         rkeyNibble[i/6] |= (rndkey[pc2[i]-1]<<offset);
      }

      /* re-order rkey for Cipher_DES() implementation matching */
      SWAP8U(rkeyNibble[1], rkeyNibble[2]);
      SWAP8U(rkeyNibble[5], rkeyNibble[6]);
      SWAP8U(rkeyNibble[5], rkeyNibble[3]);
      SWAP8U(rkeyNibble[4], rkeyNibble[2]);

      /* remember tmp and rkeyNibble are aliases */
      #if (IPP_ENDIAN == IPP_BIG_ENDIAN)
      tmp = IPP_MAKEDWORD(ENDIANNESS(IPP_LODWORD(tmp)), ENDIANNESS(IPP_HIDWORD(tmp)));
      #endif
      pEncRoundKey[nkey] = tmp;
   }

   /*
   // key schedule for decryption (just copy in reverse order)
   */
   for(nkey=0; nkey<16; nkey++)
      pDecRoundKey[nkey] = pEncRoundKey[16-nkey-1];
}
