/*************************************************************************
* 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.
//     Internal Safe Rijndael Encrypt, Decrypt
//     (It's the special free from Sbox/tables implementation)
//
*/

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

#if ((_IPP < _IPP_V8) && (_IPP32E < _IPP32E_U8)) /* no pshufb instruction */

#if (_ALG_AES_SAFE_ == _ALG_AES_SAFE_COMPOSITE_GF_)
#include "pcprij128safe.h"

#if defined _PCP_RIJ_SAFE_OLD
/*
// old version
*/

IPP_OWN_DEFN(Ipp8u, TransformByte, (Ipp8u x, const Ipp8u Transformation[]))
{
    Ipp32u y = 0;

    Ipp32u testBit = 0x01;
    int bit;
    for (bit = 0; bit < 8; bit++) {
        Ipp32u mask = (x & testBit) ? 0xFF : 0;
        y ^= Transformation[bit] & mask;
        testBit <<= 1;
    }

    return (Ipp8u)y;
}

static Ipp8u Native2CompositeTransformation[] = { 0x01, 0x2E, 0x49, 0x43, 0x35, 0xD0, 0x3D, 0xE9 };

IPP_OWN_DEFN(void, TransformNative2Composite, (Ipp8u out[], const Ipp8u inp[]))
{
    int n;
    for (n = 0; n < 16; n++)
        out[n] = TransformByte(inp[n], Native2CompositeTransformation);
}

static Ipp8u Composite2NativeTransformation[] = { 0x01, 0x5C, 0xE0, 0x50, 0x1F, 0xEE, 0x55, 0x6A };

IPP_OWN_DEFN(void, TransformComposite2Native, (Ipp8u out[], const Ipp8u inp[]))
{
    int n;
    for (n = 0; n < 16; n++)
        out[n] = TransformByte(inp[n], Composite2NativeTransformation);
}

IPP_OWN_DEFN(void, AddRoundKey, (Ipp8u out[], const Ipp8u inp[], const Ipp8u pKey[]))
{
    int n;
    for (n = 0; n < 16; n++)
        out[n] = (Ipp8u)(inp[n] ^ pKey[n]);
}

#define MASK_BIT(x, n) ((Ipp32s)((x) << (31 - n)) >> 31)
#define GF16mulX(x)    (((x) << 1) ^ (MASK_BIT(x, 3) & 0x13))

static Ipp8u GF16mul(Ipp8u a, Ipp8u b)
{
    Ipp32u a0 = a;
    Ipp32u a1 = GF16mulX(a0);
    Ipp32u a2 = GF16mulX(a1);
    Ipp32u a4 = GF16mulX(a2);

    Ipp32u r = (a0 & MASK_BIT(b, 0)) ^ (a1 & MASK_BIT(b, 1)) ^ (a2 & MASK_BIT(b, 2)) ^
               (a4 & MASK_BIT(b, 3));

    return (Ipp8u)r;
}

static Ipp8u GF16_sqr[]  = { 0x00, 0x01, 0x04, 0x05, 0x03, 0x02, 0x07, 0x06,
                             0x0C, 0x0D, 0x08, 0x09, 0x0F, 0x0E, 0x0B, 0x0A };
static Ipp8u GF16_sqr1[] = { 0x00, 0x09, 0x02, 0x0B, 0x08, 0x01, 0x0A, 0x03,
                             0x06, 0x0F, 0x04, 0x0D, 0x0E, 0x07, 0x0C, 0x05 };
static Ipp8u GF16_inv[]  = { 0x00, 0x01, 0x09, 0x0E, 0x0D, 0x0B, 0x07, 0x06,
                             0x0F, 0x02, 0x0C, 0x05, 0x0A, 0x04, 0x03, 0x08 };
IPP_OWN_DEFN(Ipp8u, InverseComposite, (Ipp8u x))
{
    /* split x = {bc} => b*t + c */
    int b = (x >> 4) & 0x0F;
    int c = x & 0x0F;

    int D = GF16mul((Ipp8u)b, (Ipp8u)c) ^ GF16_sqr[c] ^ GF16_sqr1[b];

    D = GF16_inv[D];

    c = GF16mul((Ipp8u)(b ^ c), (Ipp8u)D);
    b = GF16mul((Ipp8u)b, (Ipp8u)D);

    /* merge p*t + q => {pq} = x */
    x = (Ipp8u)((b << 4) + c);
    return x;
}
#endif /* _PCP_RIJ_SAFE_OLD */

#if !defined _PCP_RIJ_SAFE_OLD
/*
// new version
*/

/* GF(2^128) -> GF((2^4)^2) isomorphous transformation matrix Ipp8u Native2CompositeTransformation[] = {0x01,0x2E,0x49,0x43,0x35,0xD0,0x3D,0xE9};
   is defined in reference code, see doc for details */

/* defived from Native2CompositeTransformation[i], i=0,1,2,3 */
static Ipp8u Native2CompositeTransformationLO[] = {
    /* 0 */ 0x00,
    /* 1 */ 0x01,
    /* 2 */ 0x2E,
    /* 3 */ 0x2E ^ 0x01,
    /* 4 */ 0x49,
    /* 5 */ 0x49 ^ 0x01,
    /* 6 */ 0x49 ^ 0x2E,
    /* 7 */ 0x49 ^ 0x2E ^ 0x01,
    /* 8 */ 0x43,
    /* 9 */ 0x43 ^ 0x01,
    /* a */ 0x43 ^ 0x2E,
    /* b */ 0x43 ^ 0x2E ^ 0x01,
    /* c */ 0x43 ^ 0x49,
    /* d */ 0x43 ^ 0x49 ^ 0x01,
    /* e */ 0x43 ^ 0x49 ^ 0x2E,
    /* f */ 0x43 ^ 0x49 ^ 0x2E ^ 0x01
};
/* defived from Native2CompositeTransformation[i], i=4,5,6,7 */
static Ipp8u Native2CompositeTransformationHI[] = {
    /* 0 */ 0x00,
    /* 1 */ 0x35,
    /* 2 */ 0xD0,
    /* 3 */ 0xD0 ^ 0x35,
    /* 4 */ 0x3D,
    /* 5 */ 0x3D ^ 0x35,
    /* 6 */ 0x3D ^ 0xD0,
    /* 7 */ 0x3D ^ 0xD0 ^ 0x35,
    /* 8 */ 0xE9,
    /* 9 */ 0xE9 ^ 0x35,
    /* a */ 0xE9 ^ 0xD0,
    /* b */ 0xE9 ^ 0xD0 ^ 0x35,
    /* c */ 0xE9 ^ 0x3D,
    /* d */ 0xE9 ^ 0x3D ^ 0x35,
    /* e */ 0xE9 ^ 0x3D ^ 0xD0,
    /* f */ 0xE9 ^ 0x3D ^ 0xD0 ^ 0x35
};
IPP_OWN_DEFN(void, TransformNative2Composite, (Ipp8u out[16], const Ipp8u inp[16]))
{
    Ipp8u blk_lo[16], blk_hi[16];
    ((Ipp64u*)blk_lo)[0] = ((Ipp64u*)inp)[0] & 0x0F0F0F0F0F0F0F0F;
    ((Ipp64u*)blk_lo)[1] = ((Ipp64u*)inp)[1] & 0x0F0F0F0F0F0F0F0F;
    ((Ipp64u*)blk_hi)[0] = (((Ipp64u*)inp)[0] >> 4) & 0x0F0F0F0F0F0F0F0F;
    ((Ipp64u*)blk_hi)[1] = (((Ipp64u*)inp)[1] >> 4) & 0x0F0F0F0F0F0F0F0F;
    {
        int n;
        for (n = 0; n < 16; n++) {
            Ipp8u lo = Native2CompositeTransformationLO[blk_lo[n]];
            Ipp8u hi = Native2CompositeTransformationHI[blk_hi[n]];
            out[n]   = lo ^ hi;
        }
    }
}

/* GF((2^4)^2) -> GF(2^128) isomorphous transformation matrix Ipp8u Composite2NativeTransformation[] = {0x01,0x5C,0xE0,0x50,0x1F,0xEE,0x55,0x6A};
   is defined in reference code, see doc for details */
/* defived from Composite2NativeTransformation[i], i=0,1,2,3 */
static Ipp8u Composite2NativeTransformationLO[] = {
    /* 0 */ 0x00,
    /* 1 */ 0x01,
    /* 2 */ 0x5C,
    /* 3 */ 0x5C ^ 0x01,
    /* 4 */ 0xE0,
    /* 5 */ 0xE0 ^ 0x01,
    /* 6 */ 0xE0 ^ 0x5C,
    /* 7 */ 0xE0 ^ 0x5C ^ 0x01,
    /* 8 */ 0x50,
    /* 9 */ 0x50 ^ 0x01,
    /* a */ 0x50 ^ 0x5C,
    /* b */ 0x50 ^ 0x5C ^ 0x01,
    /* c */ 0x50 ^ 0xE0,
    /* d */ 0x50 ^ 0xE0 ^ 0x01,
    /* e */ 0x50 ^ 0xE0 ^ 0x5C,
    /* f */ 0x50 ^ 0xE0 ^ 0x5C ^ 0x01
};

/* defived from Composite2NativeTransformation[i], i=4,5,6,7 */
static Ipp8u Composite2NativeTransformationHI[] = {
    /* 0 */ 0x00,
    /* 1 */ 0x1F,
    /* 2 */ 0xEE,
    /* 3 */ 0xEE ^ 0x1F,
    /* 4 */ 0x55,
    /* 5 */ 0x55 ^ 0x1F,
    /* 6 */ 0x55 ^ 0xEE,
    /* 7 */ 0x55 ^ 0xEE ^ 0x1F,
    /* 8 */ 0x6A,
    /* 9 */ 0x6A ^ 0x1F,
    /* a */ 0x6A ^ 0xEE,
    /* b */ 0x6A ^ 0xEE ^ 0x1F,
    /* c */ 0x6A ^ 0x55,
    /* d */ 0x6A ^ 0x55 ^ 0x1F,
    /* e */ 0x6A ^ 0x55 ^ 0xEE,
    /* f */ 0x6A ^ 0x55 ^ 0xEE ^ 0x1F
};
IPP_OWN_DEFN(void, TransformComposite2Native, (Ipp8u out[16], const Ipp8u inp[16]))
{
    Ipp8u blk_lo[16], blk_hi[16];
    ((Ipp64u*)blk_lo)[0] = ((Ipp64u*)inp)[0] & 0x0F0F0F0F0F0F0F0F;
    ((Ipp64u*)blk_lo)[1] = ((Ipp64u*)inp)[1] & 0x0F0F0F0F0F0F0F0F;
    ((Ipp64u*)blk_hi)[0] = (((Ipp64u*)inp)[0] >> 4) & 0x0F0F0F0F0F0F0F0F;
    ((Ipp64u*)blk_hi)[1] = (((Ipp64u*)inp)[1] >> 4) & 0x0F0F0F0F0F0F0F0F;
    {
        int n;
        for (n = 0; n < 16; n++) {
            Ipp8u lo = Composite2NativeTransformationLO[blk_lo[n]];
            Ipp8u hi = Composite2NativeTransformationHI[blk_hi[n]];
            out[n]   = lo ^ hi;
        }
    }
}
#endif /* !_PCP_RIJ_SAFE_OLD */

#endif /* _ALG_AES_SAFE_COMPOSITE_GF_ */

#endif /* (_IPP <_IPP_V8) && (_IPP32E <_IPP32E_U8) */
