/*
* Copyright (C) 2016 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in
*     the documentation and/or other materials provided with the
*     distribution.
*   * Neither the name of Intel Corporation nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

#if !defined(_CP_MONTGOMETRY_H)
#define _CP_MONTGOMETRY_H

/*
// Montgomery spec structure
*/
struct _cpMontgomery
{
   IppCtxId       idCtx;      /* Montgomery spec identifier             */
   cpSize         maxLen;     /* maximum length of modulus being stored */
   cpSize         modLen;     /* length of modulus (and R = b^modLen)   */
   BNU_CHUNK_T    m0;         /* low word of (1/modulus) mod R          */
   BNU_CHUNK_T*   pModulus;   /* modulus (of modLen BNU_CHUNK_T size)   */
   BNU_CHUNK_T*   pIdentity;  /* mont_enc(1)                            */
   BNU_CHUNK_T*   pSquare;    /* mont_enc(R^2)                          */
   BNU_CHUNK_T*   pCube;      /* mont_enc(R^3)                          */
   BNU_CHUNK_T*   pTBuffer;   /* internal buffer  modLen BNU_CHUNK_T    */
   BNU_CHUNK_T*   pSBuffer;   /* internal buffer  modLen BNU_CHUNK_T    */
   BNU_CHUNK_T*   pProduct;   /* internal product (2*modLen BNU_CHUNK_T)*/
   BNU_CHUNK_T*   pKBuffer;   /* mul/sqr buffer (Karatsuba method used) */
};

/* accessory macros */
#define MNT_ID(eng)       ((eng)->idCtx)
#define MNT_ROOM(eng)     ((eng)->maxLen)
#define MNT_SIZE(eng)     ((eng)->modLen)
#define MNT_HELPER(eng)   ((eng)->m0)
#define MNT_MODULUS(eng)  ((eng)->pModulus)
#define MNT_1(eng)        ((eng)->pIdentity)
#define MNT_IDENT_R(eng)  (MNT_1((eng)))
#define MNT_SQUARE_R(eng) ((eng)->pSquare)
#define MNT_CUBE_R(eng)   ((eng)->pCube)
#define MNT_TBUFFER(eng)  ((eng)->pTBuffer)
#define MNT_SBUFFER(eng)  ((eng)->pSBuffer)
#define MNT_PRODUCT(eng)  ((eng)->pProduct)
#define MNT_KBUFFER(eng)  ((eng)->pKBuffer)

#define MNT_VALID_ID(eng) (MNT_ID((eng))==idCtxMontgomery)

/* default methos */
#define EXPONENT_METHOD    (ippBinaryMethod)

/* alignment */
#define MONT_ALIGNMENT  ((int)(sizeof(void*)))


/*
// Pacp/unpack Montgomery context
*/
void cpPackMontCtx(const IppsMontState* pCtx, Ipp8u* pBuffer);
void cpUnpackMontCtx(const Ipp8u* pBuffer, IppsMontState* pCtx);


/*
// Montgomery reduction, multiplication and squaring
*/
void cpMontRedAdc_BNU(BNU_CHUNK_T* pR,
                      BNU_CHUNK_T* pProduct,
                const BNU_CHUNK_T* pModulus, cpSize nsM, BNU_CHUNK_T m0);

__INLINE void cpMontRed_BNU(BNU_CHUNK_T* pR,
                            BNU_CHUNK_T* pProduct,
                      const BNU_CHUNK_T* pModulus, cpSize nsM, BNU_CHUNK_T m0)
{
   cpMontRedAdc_BNU(pR, pProduct, pModulus, nsM, m0);
}

__INLINE void cpMontMul_BNU(BNU_CHUNK_T* pR,
                      const BNU_CHUNK_T* pX, cpSize nsX,
                      const BNU_CHUNK_T* pY, cpSize nsY,
                      const BNU_CHUNK_T* pModulus, cpSize nsM, BNU_CHUNK_T m0,
                            BNU_CHUNK_T* pProduct, BNU_CHUNK_T* pKBuffer)
{
   cpMul_BNU(pProduct, pX,nsX, pY,nsY, pKBuffer);
   ZEXPAND_BNU(pProduct,nsX+nsY, 2*nsM);
   cpMontRed_BNU(pR, pProduct, pModulus, nsM, m0);
}

__INLINE void cpMontSqr_BNU(BNU_CHUNK_T* pR,
                      const BNU_CHUNK_T* pX, cpSize nsX,
                      const BNU_CHUNK_T* pModulus, cpSize nsM, BNU_CHUNK_T m0,
                            BNU_CHUNK_T* pProduct, BNU_CHUNK_T* pKBuffer)
{
   cpSqr_BNU(pProduct, pX,nsX, pKBuffer);
   ZEXPAND_BNU(pProduct, 2*nsX, 2*nsM);
   cpMontRed_BNU(pR, pProduct, pModulus, nsM, m0);
}

/*
// Montgomery encoding/decoding
*/
__INLINE cpSize cpMontEnc_BNU(BNU_CHUNK_T* pR,
                        const BNU_CHUNK_T* pXreg, cpSize nsX,
                              IppsMontState* pMont)
{
   cpSize nsM = MNT_SIZE(pMont);
   cpMontMul_BNU(pR,
                 pXreg, nsX, MNT_SQUARE_R(pMont), nsM,
                 MNT_MODULUS(pMont), nsM, MNT_HELPER(pMont),
                 MNT_PRODUCT(pMont), MNT_KBUFFER(pMont));

   FIX_BNU(pR, nsM);
   return nsM;
}

__INLINE cpSize cpMontDec_BNU(BNU_CHUNK_T* pR,
                        const BNU_CHUNK_T* pXmont, cpSize nsX,
                              IppsMontState* pMont)
{
   cpSize nsM = MNT_SIZE(pMont);
   ZEXPAND_COPY_BNU(MNT_PRODUCT(pMont), 2*nsM, pXmont, nsX);

   cpMontRed_BNU(pR, MNT_PRODUCT(pMont), MNT_MODULUS(pMont), nsM, MNT_HELPER(pMont));

   FIX_BNU(pR, nsM);
   return nsM;
}

__INLINE void cpMontEnc_BN(IppsBigNumState* pRbn,
                     const IppsBigNumState* pXbn,
                           IppsMontState* pMont)
{
   BNU_CHUNK_T* pR = BN_NUMBER(pRbn);
   cpSize nsM = MNT_SIZE(pMont);
   cpMontMul_BNU(pR,
                 BN_NUMBER(pXbn), BN_SIZE(pXbn),
                 MNT_SQUARE_R(pMont), nsM,
                 MNT_MODULUS(pMont), nsM, MNT_HELPER(pMont),
                 MNT_PRODUCT(pMont), MNT_KBUFFER(pMont));

   FIX_BNU(pR, nsM);
   BN_SIZE(pRbn) = nsM;
   BN_SIGN(pRbn) = ippBigNumPOS;
}

__INLINE void cpMontDec_BN(IppsBigNumState* pRbn,
                     const IppsBigNumState* pXbn,
                           IppsMontState* pMont)
{
   BNU_CHUNK_T* pR = BN_NUMBER(pRbn);
   cpSize nsM = MNT_SIZE(pMont);
   ZEXPAND_COPY_BNU(MNT_PRODUCT(pMont), 2*nsM, BN_NUMBER(pXbn), BN_SIZE(pXbn));

   cpMontRed_BNU(pR, MNT_PRODUCT(pMont), MNT_MODULUS(pMont), nsM, MNT_HELPER(pMont));

   FIX_BNU(pR, nsM);
   BN_SIZE(pRbn) = nsM;
   BN_SIGN(pRbn) = ippBigNumPOS;
}

/*
// Montgomery exponentiation (binary)
*/
cpSize cpMontExpBin_BNU(BNU_CHUNK_T* pY,
                  const BNU_CHUNK_T* pX, cpSize nsX,
                  const BNU_CHUNK_T* pE, cpSize nsE,
                        IppsMontState* pMont);

#endif /* _CP_MONTGOMETRY_H */
