/* Microsoft Reference Implementation for TPM 2.0
 *
 *  The copyright in this software is being made available under the BSD License,
 *  included below. This software may be subject to other third party and
 *  contributor rights, including patent rights, and no such rights are granted
 *  under this license.
 *
 *  Copyright (c) Microsoft Corporation
 *
 *  All rights reserved.
 *
 *  BSD License
 *
 *  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.
 *
 *  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 HOLDER 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.
 */

//** Introduction
//
// This file contains the math functions that are not implemented in the BnMath
// library (yet). These math functions will call the wolfcrypt library to execute
// the operations. There is a difference between the internal format and the
// wolfcrypt format. To call the wolfcrypt function, a mp_int structure is created
// for each passed variable. We define USE_FAST_MATH wolfcrypt option, which allocates
// mp_int on the stack. We must copy each word to the new structure, and set the used
// size.
//
// Not using USE_FAST_MATH would allow for a simple pointer swap for the big integer
// buffer 'd', however wolfcrypt expects to manage this memory, and will swap out
// the pointer to and from temporary variables and free the reference underneath us.
// Using USE_FAST_MATH also instructs wolfcrypt to use the stack for all these
// intermediate variables

//** Includes and Defines
#include "Tpm.h"

#ifdef MATH_LIB_WOLF
#  include "BnConvert_fp.h"
#  include "TpmToWolfMath_fp.h"

#  define WOLF_HALF_RADIX (RADIX_BITS == 64 && !defined(FP_64BIT))

//** Functions

//*** BnFromWolf()
// This function converts a wolfcrypt mp_int to a TPM bignum. In this implementation
// it is assumed that wolfcrypt used the same format for a big number as does the
// TPM -- an array of native-endian words in little-endian order.
void BnFromWolf(bigNum bn, mp_int* wolfBn)
{
    if(bn != NULL)
    {
        int i;
#  if WOLF_HALF_RADIX
        pAssert((unsigned)wolfBn->used <= 2 * BnGetAllocated(bn));
#  else
        pAssert((unsigned)wolfBn->used <= BnGetAllocated(bn));
#  endif
        for(i = 0; i < wolfBn->used; i++)
        {
#  if WOLF_HALF_RADIX
            if(i & 1)
                bn->d[i / 2] |= (crypt_uword_t)wolfBn->dp[i] << 32;
            else
                bn->d[i / 2] = wolfBn->dp[i];
#  else
            bn->d[i] = wolfBn->dp[i];
#  endif
        }

#  if WOLF_HALF_RADIX
        BnSetTop(bn, (wolfBn->used + 1) / 2);
#  else
        BnSetTop(bn, wolfBn->used);
#  endif
    }
}

//*** BnToWolf()
// This function converts a TPM bignum to a wolfcrypt mp_init, and has the same
// assumptions as made by BnFromWolf()
void BnToWolf(mp_int* toInit, bigConst initializer)
{
    uint32_t i;
    if(toInit != NULL && initializer != NULL)
    {
        for(i = 0; i < initializer->size; i++)
        {
#  if WOLF_HALF_RADIX
            toInit->dp[2 * i]     = (fp_digit)initializer->d[i];
            toInit->dp[2 * i + 1] = (fp_digit)(initializer->d[i] >> 32);
#  else
            toInit->dp[i] = initializer->d[i];
#  endif
        }

#  if WOLF_HALF_RADIX
        toInit->used = (int)initializer->size * 2;
        if(toInit->dp[toInit->used - 1] == 0 && toInit->dp[toInit->used - 2] != 0)
            --toInit->used;
#  else
        toInit->used = (int)initializer->size;
#  endif
        toInit->sign = 0;
    }
}

//*** MpInitialize()
// This function initializes an wolfcrypt mp_int.
mp_int* MpInitialize(mp_int* toInit)
{
    mp_init(toInit);
    return toInit;
}

#  if LIBRARY_COMPATIBILITY_CHECK
//** MathLibraryCompatibililtyCheck()
// This function is only used during development to make sure that the library
// that is being referenced is using the same size of data structures as the TPM.
BOOL MathLibraryCompatibilityCheck(void)
{
    BN_VAR(tpmTemp, 64 * 8);  // allocate some space for a test value
    crypt_uword_t i;
    TPM2B_TYPE(TEST, 16);
    TPM2B_TEST test = {{16,
                        {0x0F,
                         0x0E,
                         0x0D,
                         0x0C,
                         0x0B,
                         0x0A,
                         0x09,
                         0x08,
                         0x07,
                         0x06,
                         0x05,
                         0x04,
                         0x03,
                         0x02,
                         0x01,
                         0x00}}};
    // Convert the test TPM2B to a bigNum
    BnFrom2B(tpmTemp, &test.b);
    MP_INITIALIZED(wolfTemp, tpmTemp);
    (wolfTemp);  // compiler warning
    // Make sure the values are consistent
    VERIFY(wolfTemp->used * sizeof(fp_digit)
           == (int)tpmTemp->size * sizeof(crypt_uword_t));
    for(i = 0; i < tpmTemp->size; i++)
        VERIFY(((crypt_uword_t*)wolfTemp->dp)[i] == tpmTemp->d[i]);
    return 1;
Error:
    return 0;
}
#  endif

//*** BnModMult()
// Does multiply and divide returning the remainder of the divide.
LIB_EXPORT BOOL BnModMult(bigNum result, bigConst op1, bigConst op2, bigConst modulus)
{
    WOLF_ENTER();
    BOOL OK;
    MP_INITIALIZED(bnOp1, op1);
    MP_INITIALIZED(bnOp2, op2);
    MP_INITIALIZED(bnTemp, NULL);
    BN_VAR(temp, LARGEST_NUMBER_BITS * 2);

    pAssert(BnGetAllocated(result) >= BnGetSize(modulus));

    OK = (mp_mul(bnOp1, bnOp2, bnTemp) == MP_OKAY);
    if(OK)
    {
        BnFromWolf(temp, bnTemp);
        OK = BnDiv(NULL, result, temp, modulus);
    }

    WOLF_LEAVE();
    return OK;
}

//*** BnMult()
// Multiplies two numbers
LIB_EXPORT BOOL BnMult(bigNum result, bigConst multiplicand, bigConst multiplier)
{
    WOLF_ENTER();
    BOOL OK;
    MP_INITIALIZED(bnTemp, NULL);
    MP_INITIALIZED(bnA, multiplicand);
    MP_INITIALIZED(bnB, multiplier);

    pAssert(result->allocated >= (BITS_TO_CRYPT_WORDS(
                BnSizeInBits(multiplicand) + BnSizeInBits(multiplier))));

    OK = (mp_mul(bnA, bnB, bnTemp) == MP_OKAY);
    if(OK)
    {
        BnFromWolf(result, bnTemp);
    }

    WOLF_LEAVE();
    return OK;
}

//*** BnDiv()
// This function divides two bigNum values. The function returns FALSE if
// there is an error in the operation.
LIB_EXPORT BOOL BnDiv(
    bigNum quotient, bigNum remainder, bigConst dividend, bigConst divisor)
{
    WOLF_ENTER();
    BOOL OK;
    MP_INITIALIZED(bnQ, quotient);
    MP_INITIALIZED(bnR, remainder);
    MP_INITIALIZED(bnDend, dividend);
    MP_INITIALIZED(bnSor, divisor);
    pAssert(!BnEqualZero(divisor));
    if(BnGetSize(dividend) < BnGetSize(divisor))
    {
        if(quotient)
            BnSetWord(quotient, 0);
        if(remainder)
            BnCopy(remainder, dividend);
        OK = TRUE;
    }
    else
    {
        pAssert(
            (quotient == NULL)
            || (quotient->allocated >= (unsigned)(dividend->size - divisor->size)));
        pAssert((remainder == NULL) || (remainder->allocated >= divisor->size));
        OK = (mp_div(bnDend, bnSor, bnQ, bnR) == MP_OKAY);
        if(OK)
        {
            BnFromWolf(quotient, bnQ);
            BnFromWolf(remainder, bnR);
        }
    }

    WOLF_LEAVE();
    return OK;
}

#  if ALG_RSA
//*** BnGcd()
// Get the greatest common divisor of two numbers
LIB_EXPORT BOOL BnGcd(bigNum   gcd,      // OUT: the common divisor
                      bigConst number1,  // IN:
                      bigConst number2   // IN:
)
{
    WOLF_ENTER();
    BOOL OK;
    MP_INITIALIZED(bnGcd, gcd);
    MP_INITIALIZED(bn1, number1);
    MP_INITIALIZED(bn2, number2);
    pAssert(gcd != NULL);
    OK = (mp_gcd(bn1, bn2, bnGcd) == MP_OKAY);
    if(OK)
    {
        BnFromWolf(gcd, bnGcd);
    }
    WOLF_LEAVE();
    return OK;
}

//***BnModExp()
// Do modular exponentiation using bigNum values. The conversion from a mp_int to
// a bigNum is trivial as they are based on the same structure
LIB_EXPORT BOOL BnModExp(bigNum   result,    // OUT: the result
                         bigConst number,    // IN: number to exponentiate
                         bigConst exponent,  // IN:
                         bigConst modulus    // IN:
)
{
    WOLF_ENTER();
    BOOL OK;
    MP_INITIALIZED(bnResult, result);
    MP_INITIALIZED(bnN, number);
    MP_INITIALIZED(bnE, exponent);
    MP_INITIALIZED(bnM, modulus);
    OK = (mp_exptmod(bnN, bnE, bnM, bnResult) == MP_OKAY);
    if(OK)
    {
        BnFromWolf(result, bnResult);
    }

    WOLF_LEAVE();
    return OK;
}

//*** BnModInverse()
// Modular multiplicative inverse
LIB_EXPORT BOOL BnModInverse(bigNum result, bigConst number, bigConst modulus)
{
    WOLF_ENTER();
    BOOL OK;
    MP_INITIALIZED(bnResult, result);
    MP_INITIALIZED(bnN, number);
    MP_INITIALIZED(bnM, modulus);

    OK = (mp_invmod(bnN, bnM, bnResult) == MP_OKAY);
    if(OK)
    {
        BnFromWolf(result, bnResult);
    }

    WOLF_LEAVE();
    return OK;
}
#  endif  // TPM_ALG_RSA

#  if ALG_ECC

//*** PointFromWolf()
// Function to copy the point result from a wolf ecc_point to a bigNum
void PointFromWolf(bigPoint   pOut,  // OUT: resulting point
                   ecc_point* pIn    // IN: the point to return
)
{
    BnFromWolf(pOut->x, pIn->x);
    BnFromWolf(pOut->y, pIn->y);
    BnFromWolf(pOut->z, pIn->z);
}

//*** PointToWolf()
// Function to copy the point result from a bigNum to a wolf ecc_point
void PointToWolf(ecc_point* pOut,  // OUT: resulting point
                 pointConst pIn    // IN: the point to return
)
{
    BnToWolf(pOut->x, pIn->x);
    BnToWolf(pOut->y, pIn->y);
    BnToWolf(pOut->z, pIn->z);
}

//*** EcPointInitialized()
// Allocate and initialize a point.
static ecc_point* EcPointInitialized(pointConst initializer)
{
    ecc_point* P;

    P = wc_ecc_new_point();
    pAssert(P != NULL);
    // mp_int x,y,z are stack allocated.
    // initializer is not required
    if(P != NULL && initializer != NULL)
    {
        PointToWolf(P, initializer);
    }

    return P;
}

//*** BnEccModMult()
// This function does a point multiply of the form R = [d]S
// return type: BOOL
//  FALSE       failure in operation; treat as result being point at infinity
LIB_EXPORT BOOL BnEccModMult(bigPoint   R,  // OUT: computed point
                             pointConst S,  // IN: point to multiply by 'd' (optional)
                             bigConst   d,  // IN: scalar for [d]S
                             bigCurve   E)
{
    WOLF_ENTER();
    BOOL OK;
    MP_INITIALIZED(bnD, d);
    MP_INITIALIZED(bnPrime, CurveGetPrime(E));
    POINT_CREATE(pS, NULL);
    POINT_CREATE(pR, NULL);

    if(S == NULL)
        S = CurveGetG(AccessCurveData(E));

    PointToWolf(pS, S);

    OK = (wc_ecc_mulmod(bnD, pS, pR, NULL, bnPrime, 1) == MP_OKAY);
    if(OK)
    {
        PointFromWolf(R, pR);
    }

    POINT_DELETE(pR);
    POINT_DELETE(pS);

    WOLF_LEAVE();
    return !BnEqualZero(R->z);
}

//*** BnEccModMult2()
// This function does a point multiply of the form R = [d]G + [u]Q
// return type: BOOL
//  FALSE       failure in operation; treat as result being point at infinity
LIB_EXPORT BOOL BnEccModMult2(bigPoint   R,  // OUT: computed point
                              pointConst S,  // IN: optional point
                              bigConst   d,  // IN: scalar for [d]S or [d]G
                              pointConst Q,  // IN: second point
                              bigConst   u,  // IN: second scalar
                              bigCurve   E   // IN: curve
)
{
    WOLF_ENTER();
    BOOL OK;
    POINT_CREATE(pR, NULL);
    POINT_CREATE(pS, NULL);
    POINT_CREATE(pQ, Q);
    MP_INITIALIZED(bnD, d);
    MP_INITIALIZED(bnU, u);
    MP_INITIALIZED(bnPrime, CurveGetPrime(E));
    MP_INITIALIZED(bnA, CurveGet_a(E));

    if(S == NULL)
        S = CurveGetG(AccessCurveData(E));
    PointToWolf(pS, S);

    OK = (ecc_mul2add(pS, bnD, pQ, bnU, pR, bnA, bnPrime, NULL) == MP_OKAY);
    if(OK)
    {
        PointFromWolf(R, pR);
    }

    POINT_DELETE(pS);
    POINT_DELETE(pQ);
    POINT_DELETE(pR);

    WOLF_LEAVE();
    return !BnEqualZero(R->z);
}

//** BnEccAdd()
// This function does addition of two points.
// return type: BOOL
//  FALSE       failure in operation; treat as result being point at infinity
LIB_EXPORT BOOL BnEccAdd(bigPoint   R,  // OUT: computed point
                         pointConst S,  // IN: point to multiply by 'd'
                         pointConst Q,  // IN: second point
                         bigCurve   E   // IN: curve
)
{
    WOLF_ENTER();
    BOOL     OK;
    mp_digit mp;
    POINT_CREATE(pR, NULL);
    POINT_CREATE(pS, S);
    POINT_CREATE(pQ, Q);
    MP_INITIALIZED(bnA, CurveGet_a(E));
    MP_INITIALIZED(bnMod, CurveGetPrime(E));
    //
    OK = (mp_montgomery_setup(bnMod, &mp) == MP_OKAY);
    OK = OK && (ecc_projective_add_point(pS, pQ, pR, bnA, bnMod, mp) == MP_OKAY);
    if(OK)
    {
        PointFromWolf(R, pR);
    }

    POINT_DELETE(pS);
    POINT_DELETE(pQ);
    POINT_DELETE(pR);

    WOLF_LEAVE();
    return !BnEqualZero(R->z);
}

#  endif  // TPM_ALG_ECC

#endif  // MATH_LIB_WOLF