/*************************************************************************
* Copyright (C) 2015 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.
//     Decrypt 128-bit data block according to Rijndael
//     (compact S-box based implementation)
//
//  Contents:
//     Safe2Decrypt_RIJ128()
//
//
*/

#include "owncp.h"

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

#if (_ALG_AES_SAFE_ == _ALG_AES_SAFE_COMPACT_SBOX_)

#include "pcprij128safe2.h"
#include "pcprijtables.h"


#include "pcpbnuimpl.h"
#define SELECTION_BITS ((sizeof(BNU_CHUNK_T) / sizeof(Ipp8u)) - 1)

#if defined(__INTEL_COMPILER)
IPPCP_INLINE Ipp8u getInvSboxValue(Ipp8u x)
{
    BNU_CHUNK_T selection        = 0;
    const BNU_CHUNK_T* SboxEntry = (BNU_CHUNK_T*)RijDecSbox;

    BNU_CHUNK_T i_sel = x / sizeof(BNU_CHUNK_T); /* selection index */
    BNU_CHUNK_T i;
    for (i = 0; i < sizeof(RijEncSbox) / sizeof(BNU_CHUNK_T); i++) {

        /* build specific to avoid jump instruction here */
        BNU_CHUNK_T mask = (i == i_sel) ? (BNU_CHUNK_T)(-1) : 0;
        selection |= SboxEntry[i] & mask;
    }
    selection >>= (x & SELECTION_BITS) * 8;
    return (Ipp8u)(selection & 0xFF);
}

#else
#include "pcpmask_ct.h"
IPPCP_INLINE Ipp8u getInvSboxValue(Ipp8u x)
{
    BNU_CHUNK_T selection        = 0;
    const BNU_CHUNK_T* SboxEntry = (BNU_CHUNK_T*)RijDecSbox;

    Ipp32u _x = x / sizeof(BNU_CHUNK_T);
    Ipp32u i;
    for (i = 0; i < sizeof(RijEncSbox) / (sizeof(BNU_CHUNK_T)); i++) {
        // volatile is needed to prevent the compiler from optimizing to keep the execution time constant
        volatile BNS_CHUNK_T mask = (BNS_CHUNK_T)cpIsEqu_ct(_x, i);
        selection |= SboxEntry[i] & (BNU_CHUNK_T)mask;
    }
    selection >>= (x & SELECTION_BITS) * 8;
    return (Ipp8u)(selection & 0xFF);
}
#endif

IPPCP_INLINE void invSubBytes(Ipp8u state[])
{
    int i;
    for (i = 0; i < 16; i++)
        state[i] = getInvSboxValue(state[i]);
}

IPPCP_INLINE void invShiftRows(Ipp32u* state)
{
    state[1] = ROR32(state[1], 24);
    state[2] = ROR32(state[2], 16);
    state[3] = ROR32(state[3], 8);
}

IPPCP_INLINE void invMixColumns(Ipp32u* state)
{
    Ipp32u y0 = state[1] ^ state[2] ^ state[3];
    Ipp32u y1 = state[0] ^ state[2] ^ state[3];
    Ipp32u y2 = state[0] ^ state[1] ^ state[3];
    Ipp32u y3 = state[0] ^ state[1] ^ state[2];
    Ipp32u t02, t13, t0123;

    state[0] = xtime4(state[0]);
    state[1] = xtime4(state[1]);
    state[2] = xtime4(state[2]);
    state[3] = xtime4(state[3]);

    y0 ^= state[0] ^ state[1];
    y1 ^= state[1] ^ state[2];
    y2 ^= state[2] ^ state[3];
    y3 ^= state[3] ^ state[0];

    t02 = state[0] ^ state[2];
    t13 = state[1] ^ state[3];
    t02 = xtime4(t02);
    t13 = xtime4(t13);

    t0123 = t02 ^ t13;
    t0123 = xtime4(t0123);

    state[0] = y0 ^ t02 ^ t0123;
    state[1] = y1 ^ t13 ^ t0123;
    state[2] = y2 ^ t02 ^ t0123;
    state[3] = y3 ^ t13 ^ t0123;
}

/* clang-format off */
IPP_OWN_DEFN(void, Safe2Decrypt_RIJ128, (const Ipp8u* in,
                                         Ipp8u* out,
                                         int Nr,
                                         const Ipp8u* RoundKey,
                                         const void* sbox))
/* clang-format on */
{
    Ipp32u state[4];

    int round = 0;

    IPP_UNREFERENCED_PARAMETER(sbox);

    // copy input to the state array
    TRANSPOSE((Ipp8u*)state, in);

    // add the round key to the state before starting the rounds.
    XorRoundKey((Ipp32u*)state, (Ipp32u*)(RoundKey + Nr * 16));

    // there will be Nr rounds
    for (round = Nr - 1; round > 0; round--) {
        invShiftRows(state);
        invSubBytes((Ipp8u*)state);
        XorRoundKey(state, (Ipp32u*)(RoundKey + round * 16));
        invMixColumns(state);
    }

    // last round
    invShiftRows(state);
    invSubBytes((Ipp8u*)state);
    XorRoundKey(state, (Ipp32u*)(RoundKey + 0 * 16));

    // copy from the state to output
    TRANSPOSE(out, (Ipp8u*)state);
}
#endif /* _ALG_AES_SAFE_COMPACT_SBOX_ */

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