/* 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
// The simulator code uses the canonical form whenever possible in order to make
// the code in Part 3 more accessible. The canonical data formats are simple and
// not well suited for complex big number computations. When operating on big
// numbers, the data format is changed for easier manipulation. The format is native
// words in little-endian format. As the magnitude of the number decreases, the
// length of the array containing the number decreases but the starting address
// doesn't change.
//
// The functions in this file perform simple operations on these big numbers. Only
// the more complex operations are passed to the underlying support library.
// Although the support library would have most of these functions, the interface
// code to convert the format for the values is greater than the size of the
// code to implement the functions here. So, rather than incur the overhead of
// conversion, they are done here.
//
// If an implementer would prefer, the underlying library can be used simply by
// making code substitutions here.
//
// NOTE: There is an intention to continue to augment these functions so that there
// would be no need to use an external big number library.
//
// Many of these functions have no error returns and will always return TRUE. This
// is to allow them to be used in "guarded" sequences. That is:
//    OK = OK || BnSomething(s);
// where the BnSomething function should not be called if OK isn't true.

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

// A constant value of zero as a stand in for NULL bigNum values
const bignum_t   BnConstZero = {1, 0, {0}};

//** Functions

//*** AddSame()
// Adds two values that are the same size. This function allows 'result' to be
// the same as either of the addends. This is a nice function to put into assembly
// because handling the carry for multi-precision stuff is not as easy in C
// (unless there is a REALLY smart compiler). It would be nice if there were idioms
// in a language that a compiler could recognize what is going on and optimize
// loops like this.
//  Return Type: int
//      0           no carry out
//      1           carry out
static BOOL
AddSame(
    crypt_uword_t           *result,
    const crypt_uword_t     *op1,
    const crypt_uword_t     *op2,
    int                      count
    )
{
    int         carry = 0;
    int         i;

    for(i = 0; i < count; i++)
    {
        crypt_uword_t        a = op1[i];
        crypt_uword_t        sum = a + op2[i];
        result[i] = sum + carry;
        // generate a carry if the sum is less than either of the inputs
        // propagate a carry if there was a carry and the sum + carry is zero
        // do this using bit operations rather than logical operations so that
        // the time is about the same.
        //             propagate term      | generate term
        carry = ((result[i] == 0) & carry) | (sum < a);
    }
    return carry;
}

//*** CarryProp()
// Propagate a carry
static int
CarryProp(
    crypt_uword_t           *result,
    const crypt_uword_t     *op,
    int                      count,
    int                      carry
    )
{
    for(; count; count--)
        carry = ((*result++ = *op++ + carry) == 0) & carry;
    return carry;
}

static void
CarryResolve(
    bigNum          result,
    int             stop,
    int             carry
    )
{
    if(carry)
    {
        pAssert((unsigned)stop < result->allocated);
        result->d[stop++] = 1;
    }
    BnSetTop(result, stop);
}

//*** BnAdd()
// This function adds two bigNum values. This function always returns TRUE. 
LIB_EXPORT BOOL
BnAdd(
    bigNum           result,
    bigConst         op1,
    bigConst         op2
    )
{
    crypt_uword_t    stop;
    int              carry;
    const bignum_t   *n1 = op1;
    const bignum_t   *n2 = op2;

//
    if(n2->size > n1->size)
    {
        n1 = op2;
        n2 = op1;
    }
    pAssert(result->allocated >= n1->size);
    stop = MIN(n1->size, n2->allocated);
    carry = (int)AddSame(result->d, n1->d, n2->d, (int)stop);
    if(n1->size > stop)
        carry = CarryProp(&result->d[stop], &n1->d[stop], (int)(n1->size - stop), carry);
    CarryResolve(result, (int)n1->size, carry);
    return TRUE;
}

//*** BnAddWord()
// This function adds a word value to a bigNum. This function always returns TRUE.
LIB_EXPORT BOOL
BnAddWord(
    bigNum           result,
    bigConst         op,
    crypt_uword_t    word
    )
{
    int              carry;
//
    carry = (result->d[0] = op->d[0] + word) < word;
    carry = CarryProp(&result->d[1], &op->d[1], (int)(op->size - 1), carry);
    CarryResolve(result, (int)op->size, carry);
    return TRUE;
}

//*** SubSame()
// This function subtracts two values that have the same size.
static int
SubSame(
    crypt_uword_t           *result,
    const crypt_uword_t     *op1,
    const crypt_uword_t     *op2,
    int                      count
    )
{
    int                  borrow = 0;
    int                  i;
    for(i = 0; i < count; i++)
    {
        crypt_uword_t    a = op1[i];
        crypt_uword_t    diff = a - op2[i];
        result[i] = diff - borrow;
        //       generate   |      propagate
        borrow = (diff > a) | ((diff == 0) & borrow);
    }
    return borrow;
}

//*** BorrowProp()
// This propagates a borrow. If borrow is true when the end
// of the array is reached, then it means that op2 was larger than
// op1 and we don't handle that case so an assert is generated.
// This design choice was made because our only bigNum computations
// are on large positive numbers (primes) or on fields.
// Propagate a borrow.
static int
BorrowProp(
    crypt_uword_t           *result,
    const crypt_uword_t     *op,
    int                      size,
    int                      borrow
    )
{
    for(; size > 0; size--)
        borrow = ((*result++ = *op++ - borrow) == MAX_CRYPT_UWORD) && borrow;
    return borrow;
}

//*** BnSub()
// This function does subtraction of two bigNum values and returns result = op1 - op2 
// when op1 is greater than op2. If op2 is greater than op1, then a fault is 
// generated. This function always returns TRUE.
LIB_EXPORT BOOL
BnSub(
    bigNum           result,
    bigConst         op1,
    bigConst         op2
    )
{
    int             borrow;
    int             stop = (int)MIN(op1->size, op2->allocated);
//
    // Make sure that op2 is not obviously larger than op1
    pAssert(op1->size >= op2->size);
    borrow = SubSame(result->d, op1->d, op2->d, stop);
    if(op1->size > (crypt_uword_t)stop)
        borrow = BorrowProp(&result->d[stop], &op1->d[stop], (int)(op1->size - stop),
                            borrow);
    pAssert(!borrow);
    BnSetTop(result, op1->size);
    return TRUE;
}

//*** BnSubWord()
// This function subtracts a word value from a bigNum. This function always 
// returns TRUE.
LIB_EXPORT BOOL
BnSubWord(
    bigNum           result,
    bigConst         op,
    crypt_uword_t    word
    )
{
    int             borrow;
//
    pAssert(op->size > 1 || word <= op->d[0]);
    borrow = word > op->d[0];
    result->d[0] = op->d[0] - word;
    borrow = BorrowProp(&result->d[1], &op->d[1], (int)(op->size - 1), borrow);
    pAssert(!borrow);
    BnSetTop(result, op->size);
    return TRUE;
}

//*** BnUnsignedCmp()
// This function performs a comparison of op1 to op2. The compare is approximately
// constant time if the size of the values used in the compare is consistent
// across calls (from the same line in the calling code).
//  Return Type: int
//      < 0             op1 is less than op2
//      0               op1 is equal to op2
//      > 0             op1 is greater than op2
LIB_EXPORT int
BnUnsignedCmp(
    bigConst               op1,
    bigConst               op2
    )
{
    int             retVal;
    int             diff;
    int             i;
//
    pAssert((op1 != NULL) && (op2 != NULL));
    retVal = (int)(op1->size - op2->size);
    if(retVal == 0)
    {
        for(i = (int)(op1->size - 1); i >= 0; i--)
        {
            diff = (op1->d[i] < op2->d[i]) ? -1 : (op1->d[i] != op2->d[i]);
            retVal = retVal == 0 ? diff : retVal;
        }
    }
    else
        retVal = (retVal < 0) ? -1 : 1;
    return retVal;
}

//*** BnUnsignedCmpWord()
// Compare a bigNum to a crypt_uword_t.
//  Return Type: int
//      -1              op1 is less that word
//      0               op1 is equal to word
//      1               op1 is greater than word
LIB_EXPORT int
BnUnsignedCmpWord(
    bigConst             op1,
    crypt_uword_t        word
    )
{
    if(op1->size > 1)
        return 1;
    else if(op1->size == 1)
        return (op1->d[0] < word) ? -1 : (op1->d[0] > word);
    else // op1 is zero
        // equal if word is zero
        return (word == 0) ? 0 : -1;
}

//*** BnModWord()
// This function does modular division of a big number when the modulus is a
// word value.
LIB_EXPORT crypt_word_t
BnModWord(
    bigConst         numerator,
    crypt_word_t     modulus
    )
{
    BN_MAX(remainder);
    BN_VAR(mod, RADIX_BITS);
//
    mod->d[0] = modulus;
    mod->size = (modulus != 0);
    BnDiv(NULL, remainder, numerator, mod);
    return remainder->d[0];
}

//*** Msb()
// This function returns the bit number of the most significant bit of a 
// crypt_uword_t. The number for the least significant bit of any bigNum value is 0.
// The maximum return value is RADIX_BITS - 1,
//  Return Type: int
//      -1              the word was zero
//      n               the bit number of the most significant bit in the word
LIB_EXPORT int
Msb(
    crypt_uword_t           word
    )
{
    int             retVal = -1;
//
#if RADIX_BITS == 64
    if(word & 0xffffffff00000000) { retVal += 32; word >>= 32; }
#endif
    if(word & 0xffff0000) { retVal += 16; word >>= 16; }
    if(word & 0x0000ff00) { retVal += 8; word >>= 8; }
    if(word & 0x000000f0) { retVal += 4; word >>= 4; }
    if(word & 0x0000000c) { retVal += 2; word >>= 2; }
    if(word & 0x00000002) { retVal += 1; word >>= 1; }
    return retVal + (int)word;
}

//*** BnMsb()
// This function returns the number of the MSb of a bigNum value. 
//  Return Type: int
//      -1              the word was zero or 'bn' was NULL
//      n               the bit number of the most significant bit in the word
LIB_EXPORT int
BnMsb(
    bigConst            bn
    )
{
    // If the value is NULL, or the size is zero then treat as zero and return -1
    if(bn != NULL && bn->size > 0)
    {
        int         retVal = Msb(bn->d[bn->size - 1]);
        retVal += (int)(bn->size - 1) * RADIX_BITS;
        return retVal;
    }
    else
        return -1;
}

//*** BnSizeInBits()
// This function returns the number of bits required to hold a number. It is one 
// greater than the Msb.
//
LIB_EXPORT unsigned
BnSizeInBits(
    bigConst                 n
    )
{
    int     bits = BnMsb(n) + 1;
//
    return bits < 0? 0 : (unsigned)bits;
}

//*** BnSetWord()
// Change the value of a bignum_t to a word value.
LIB_EXPORT bigNum
BnSetWord(
    bigNum               n,
    crypt_uword_t        w
    )
{
    if(n != NULL)
    {
        pAssert(n->allocated > 1);
        n->d[0] = w;
        BnSetTop(n, (w != 0) ? 1 : 0);
    }
    return n;
}

//*** BnSetBit()
// This function will SET a bit in a bigNum. Bit 0 is the least-significant bit in 
// the 0th digit_t. The function always return TRUE
LIB_EXPORT BOOL
BnSetBit(
    bigNum           bn,        // IN/OUT: big number to modify
    unsigned int     bitNum     // IN: Bit number to SET
    )
{
    crypt_uword_t            offset = bitNum / RADIX_BITS;
    pAssert(bn->allocated * RADIX_BITS >= bitNum);
    // Grow the number if necessary to set the bit.
    while(bn->size <= offset)
        bn->d[bn->size++] = 0;
    bn->d[offset] |= ((crypt_uword_t)1 << RADIX_MOD(bitNum));
    return TRUE;
}

//*** BnTestBit()
// This function is used to check to see if a bit is SET in a bignum_t. The 0th bit 
// is the LSb of d[0].
//  Return Type: BOOL
//      TRUE(1)         the bit is set
//      FALSE(0)        the bit is not set or the number is out of range
LIB_EXPORT BOOL
BnTestBit(
    bigNum               bn,        // IN: number to check
    unsigned int         bitNum     // IN: bit to test
    )
{
    crypt_uword_t         offset = RADIX_DIV(bitNum);
//
    if(bn->size > offset)
        return ((bn->d[offset] & (((crypt_uword_t)1) << RADIX_MOD(bitNum))) != 0);
    else
        return FALSE;
}

//***BnMaskBits()
// This function is used to mask off high order bits of a big number.
// The returned value will have no more than 'maskBit' bits
// set.
// Note: There is a requirement that unused words of a bignum_t are set to zero.
//  Return Type: BOOL
//      TRUE(1)         result masked
//      FALSE(0)        the input was not as large as the mask
LIB_EXPORT BOOL
BnMaskBits(
    bigNum           bn,        // IN/OUT: number to mask
    crypt_uword_t    maskBit    // IN: the bit number for the mask.
    )
{
    crypt_uword_t    finalSize;
    BOOL             retVal;

    finalSize = BITS_TO_CRYPT_WORDS(maskBit);
    retVal = (finalSize <= bn->allocated);
    if(retVal && (finalSize > 0))
    {
        crypt_uword_t   mask;
        mask = ~((crypt_uword_t)0) >> RADIX_MOD(maskBit);
        bn->d[finalSize - 1] &= mask;
    }
    BnSetTop(bn, finalSize);
    return retVal;
}

//*** BnShiftRight()
// This function will shift a bigNum to the right by the shiftAmount. 
// This function always returns TRUE.
LIB_EXPORT BOOL
BnShiftRight(
    bigNum           result,
    bigConst         toShift,
    uint32_t         shiftAmount
    )
{
    uint32_t         offset = (shiftAmount >> RADIX_LOG2);
    uint32_t         i;
    uint32_t         shiftIn;
    crypt_uword_t    finalSize;
//
    shiftAmount = shiftAmount & RADIX_MASK;
    shiftIn = RADIX_BITS - shiftAmount;

    // The end size is toShift->size - offset less one additional
    // word if the shiftAmount would make the upper word == 0
    if(toShift->size > offset)
    {
        finalSize = toShift->size - offset;
        finalSize -= (toShift->d[toShift->size - 1] >> shiftAmount) == 0 ? 1 : 0;
    }
    else
        finalSize = 0;

    pAssert(finalSize <= result->allocated);
    if(finalSize != 0)
    {
        for(i = 0; i < finalSize; i++)
        {
            result->d[i] = (toShift->d[i + offset] >> shiftAmount)
                | (toShift->d[i + offset + 1] << shiftIn);
        }
        if(offset == 0)
            result->d[i] = toShift->d[i] >> shiftAmount;
    }
    BnSetTop(result, finalSize);
    return TRUE;
}

//*** BnGetRandomBits()
// This function gets random bits for use in various places. To make sure that the
// number is generated in a portable format, it is created as a TPM2B and then
// converted to the internal format.
//
// One consequence of the generation scheme is that, if the number of bits requested
// is not a multiple of 8, then the high-order bits are set to zero. This would come
// into play when generating a 521-bit ECC key. A 66-byte (528-bit) value is 
// generated an the high order 7 bits are masked off (CLEAR).
//  Return Type: BOOL
//      TRUE(1)         success
//      FALSE(0)        failure
LIB_EXPORT BOOL
BnGetRandomBits(
    bigNum           n,
    size_t           bits,
    RAND_STATE      *rand
)
{
    // Since this could be used for ECC key generation using the extra bits method,
    // make sure that the value is large enough
    TPM2B_TYPE(LARGEST, LARGEST_NUMBER + 8);
    TPM2B_LARGEST    large;
//
    large.b.size = (UINT16)BITS_TO_BYTES(bits);
    if(DRBG_Generate(rand, large.t.buffer, large.t.size) == large.t.size)
    {
        if(BnFrom2B(n, &large.b) != NULL)
        {
            if(BnMaskBits(n, bits))
                return TRUE;
        }
    }
    return FALSE;
}

//*** BnGenerateRandomInRange()
// This function is used to generate a random number r in the range 1 <= r < limit. 
// The function gets a random number of bits that is the size of limit. There is some
// some probability that the returned number is going to be greater than or equal
// to the limit. If it is, try again. There is no more than 50% chance that the
// next number is also greater, so try again. We keep trying until we get a
// value that meets the criteria. Since limit is very often a number with a LOT of
// high order ones, this rarely would need a second try.
//  Return Type: BOOL
//      TRUE(1)         success
//      FALSE(0)        failure ('limit' is too small)
LIB_EXPORT BOOL
BnGenerateRandomInRange(
    bigNum           dest,
    bigConst         limit,
    RAND_STATE      *rand
    )
{
    size_t   bits = BnSizeInBits(limit);
//
    if(bits < 2)
    {
        BnSetWord(dest, 0);
        return FALSE;
    }
    else
    {
        while(BnGetRandomBits(dest, bits, rand)
              && (BnEqualZero(dest) || (BnUnsignedCmp(dest, limit) >= 0)));
    }
    return !g_inFailureMode;
}