/*************************************************************************
* Copyright (C) 2013 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.
//     Modular Exponentiation (windowed GPR version)
*/

#include "owncp.h"

#if defined(_USE_WINDOW_EXP_)

#include "pcpngmontexpstuff.h"
#include "gsscramble.h"

IPP_OWN_DEFN(cpSize, gsMontExpWinBuffer, (int modulusBits))
{
    cpSize w   = gsMontExp_WinSize(modulusBits);
    cpSize nsM = BITS_BNU_CHUNK(modulusBits);

    cpSize bufferNum = CACHE_LINE_SIZE / ((Ipp32s)sizeof(BNU_CHUNK_T)) +
                       gsGetScrambleBufferSize(nsM, w) /* pre-computed table */
                       + nsM                           /* tmp unscrambled table entry */
                       + nsM; /* zero expanded exponent | "masked" multiplier (X|1) */
    return bufferNum;
}

/*
// "fast" fixed-size window montgomery exponentiation
//
// - input/output are in Montgomery Domain
// - possible inplace mode
//
// scratch buffer structure:
//    precomputed table of multipliers[(1<<w)*nsM]
//    RR[nsM]     tmp result if inplace operation
//    EE[nsM+1]   power expasin
*/
/* clang-format off */
IPP_OWN_DEFN(cpSize, gsMontExpWin_BNU, (BNU_CHUNK_T* dataY,
                                        const BNU_CHUNK_T* dataX,
                                        cpSize nsX,
                                        const BNU_CHUNK_T* dataExp,
                                        cpSize bitsizeE,
                                        gsModEngine* pMont,
                                        BNU_CHUNK_T* pBuffer))
/* clang-format on */
{
    cpSize nsM = MOD_LEN(pMont);
    cpSize nsE = BITS_BNU_CHUNK(bitsizeE);

    /*
   // test for special cases:
   //    x^0 = 1
   //    0^e = 0
   */
    if (cpEqu_BNU_CHUNK(dataExp, nsE, 0)) {
        COPY_BNU(dataY, MOD_MNT_R(pMont), nsM);
    } else if (cpEqu_BNU_CHUNK(dataX, nsX, 0)) {
        ZEXPAND_BNU(dataY, 0, nsM);
    }

    /* general case */
    else {
        cpSize winSize    = gsMontExp_WinSize(bitsizeE);
        cpSize nPrecomute = 1 << winSize;
        BNU_CHUNK_T mask  = (BNU_CHUNK_T)(nPrecomute - 1);
        int n;

        BNU_CHUNK_T* pTable = pBuffer;
        BNU_CHUNK_T* dataTT = pTable + gsGetScrambleBufferSize(nsM, winSize);
        BNU_CHUNK_T* dataEE = dataTT;

        /* copy and expand base to the modulus length */
        ZEXPAND_COPY_BNU(dataTT, nsM, dataX, nsX);

        /* initialize resource */
        COPY_BNU(pTable, MOD_MNT_R(pMont), nsM);
        COPY_BNU(pTable + nsM, dataTT, nsM);
        for (n = 2; n < nPrecomute; n++) {
            MOD_METHOD(pMont)->mul(pTable + n * nsM, pTable + (n - 1) * nsM, dataTT, pMont);
        }

        /* expand exponent*/
        ZEXPAND_COPY_BNU(dataEE, nsE + 1, dataExp, nsE);
        bitsizeE = ((bitsizeE + winSize - 1) / winSize) * winSize;

        /* exponentiation */
        {
            /* position of the 1-st (left) window */
            int eBit = bitsizeE - winSize;

            /* Note: Static analysis can generate error/warning on the expression below.

         The value of "bitSizeE" is limited, ((modulusBitSize > bitSizeE > 0),
         it is checked in initialization phase by (ippsRSA_GetSizePublickey() and ippsRSA_InitPublicKey).
         Buffer "dataEE" assigned for copy of dataExp, is 1 (64-bit) chunk longer than size of RSA modulus,
         therefore the access "*((Ipp32u*)((Ipp16u*)dataEE+ eBit/BITSIZE(Ipp16u)))" is always inside the boundary.
         */
            /* extract 1-st window value */
            Ipp32u eChunk = *((Ipp32u*)((Ipp16u*)dataEE + eBit / BITSIZE(Ipp16u)));
            int shift     = eBit & 0xF;
            Ipp32u winVal = (eChunk >> shift) & mask;

            /* initialize result */
            COPY_BNU(dataY, pTable + winVal * (Ipp32u)nsM, nsM);

            for (eBit -= winSize; eBit >= 0; eBit -= winSize) {
                /* do square window times */
                for (n = 0, winVal = 0; n < winSize; n++) {
                    MOD_METHOD(pMont)->sqr(dataY, dataY, pMont);
                }

                /* extract next window value */
                eChunk = *((Ipp32u*)((Ipp16u*)dataEE + eBit / BITSIZE(Ipp16u)));
                shift  = eBit & 0xF;
                winVal = (eChunk >> shift) & mask;

                /* multiply precomputed value  */
                MOD_METHOD(pMont)->mul(dataY, dataY, pTable + winVal * (Ipp32u)nsM, pMont);
            }
        }
    }

    return nsM;
}

/*
// "fast" fixed-size window montgomery exponentiation
// - input/output are in Regular Domain
// - possible inplace mode
*/
/* clang-format off */
IPP_OWN_DEFN(cpSize, gsModExpWin_BNU, (BNU_CHUNK_T* dataY,
                                       const BNU_CHUNK_T* dataX,
                                       cpSize nsX,
                                       const BNU_CHUNK_T* dataExp,
                                       cpSize bitsizeE,
                                       gsModEngine* pMont,
                                       BNU_CHUNK_T* pBuffer))
/* clang-format on */
{
    cpSize nsM = MOD_LEN(pMont);

    /* copy and expand base to the modulus length */
    ZEXPAND_COPY_BNU(dataY, nsM, dataX, nsX);

    /* convert base to Montgomery domain */
    MOD_METHOD(pMont)->encode(dataY, dataY, pMont);

    /* exponentiation */
    gsMontExpWin_BNU(dataY, dataY, nsM, dataExp, bitsizeE, pMont, pBuffer);

    /* convert result back to regular domain */
    MOD_METHOD(pMont)->decode(dataY, dataY, pMont);

    return nsM;
}

#endif /* _USE_WINDOW_EXP_ */
