/* 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.
 */
//** Includes and Defines
#include "Tpm.h"
#include "CryptEccSignature_fp.h"

#if ALG_ECC

//** Utility Functions

//*** EcdsaDigest()
// Function to adjust the digest so that it is no larger than the order of the
// curve. This is used for ECDSA sign and verification.
static bigNum EcdsaDigest(bigNum              bnD,     // OUT: the adjusted digest
                          const TPM2B_DIGEST* digest,  // IN: digest to adjust
                          bigConst max  // IN: value that indicates the maximum
                                        //     number of bits in the results
)
{
    int bitsInMax = BnSizeInBits(max);
    int shift;
    //
    if(digest == NULL)
        BnSetWord(bnD, 0);
    else
    {
        BnFromBytes(bnD,
                    digest->t.buffer,
                    (NUMBYTES)MIN(digest->t.size, BITS_TO_BYTES(bitsInMax)));
        shift = BnSizeInBits(bnD) - bitsInMax;
        if(shift > 0)
            BnShiftRight(bnD, bnD, shift);
    }
    return bnD;
}

//*** BnSchnorrSign()
// This contains the Schnorr signature computation. It is used by both ECDSA and
// Schnorr signing. The result is computed as: ['s' = 'k' + 'r' * 'd' (mod 'n')]
// where
// 1) 's' is the signature
// 2) 'k' is a random value
// 3) 'r' is the value to sign
// 4) 'd' is the private EC key
// 5) 'n' is the order of the curve
//  Return Type: TPM_RC
//      TPM_RC_NO_RESULT        the result of the operation was zero or 'r' (mod 'n')
//                              is zero
static TPM_RC BnSchnorrSign(bigNum   bnS,  // OUT: 's' component of the signature
                            bigConst bnK,  // IN: a random value
                            bigNum   bnR,  // IN: the signature 'r' value
                            bigConst bnD,  // IN: the private key
                            bigConst bnN   // IN: the order of the curve
)
{
    // Need a local temp value to store the intermediate computation because product
    // size can be larger than will fit in bnS.
    BN_VAR(bnT1, MAX_ECC_PARAMETER_BYTES * 2 * 8);
    //
    // Reduce bnR without changing the input value
    BnDiv(NULL, bnT1, bnR, bnN);
    if(BnEqualZero(bnT1))
        return TPM_RC_NO_RESULT;
    // compute s = (k + r * d)(mod n)
    // r * d
    BnMult(bnT1, bnT1, bnD);
    // k * r * d
    BnAdd(bnT1, bnT1, bnK);
    // k + r * d (mod n)
    BnDiv(NULL, bnS, bnT1, bnN);
    return (BnEqualZero(bnS)) ? TPM_RC_NO_RESULT : TPM_RC_SUCCESS;
}

//** Signing Functions

//*** BnSignEcdsa()
// This function implements the ECDSA signing algorithm. The method is described
// in the comments below.
TPM_RC
BnSignEcdsa(bigNum   bnR,                // OUT: 'r' component of the signature
            bigNum   bnS,                // OUT: 's' component of the signature
            bigCurve E,                  // IN: the curve used in the signature
                                         //     process
            bigNum              bnD,     // IN: private signing key
            const TPM2B_DIGEST* digest,  // IN: the digest to sign
            RAND_STATE*         rand     // IN: used in debug of signing
)
{
    ECC_NUM(bnK);
    ECC_NUM(bnIk);
    BN_VAR(bnE, MAX(MAX_ECC_KEY_BYTES, MAX_DIGEST_SIZE) * 8);
    POINT(ecR);
    bigConst order  = CurveGetOrder(AccessCurveData(E));
    TPM_RC   retVal = TPM_RC_SUCCESS;
    INT32    tries  = 10;
    BOOL     OK     = FALSE;
    //
    pAssert(digest != NULL);
    // The algorithm as described in "Suite B Implementer's Guide to FIPS
    // 186-3(ECDSA)"
    // 1. Use one of the routines in Appendix A.2 to generate (k, k^-1), a
    //    per-message secret number and its inverse modulo n. Since n is prime,
    //    the output will be invalid only if there is a failure in the RBG.
    // 2. Compute the elliptic curve point R = [k]G = (xR, yR) using EC scalar
    //    multiplication (see [Routines]), where G is the base point included in
    //    the set of domain parameters.
    // 3. Compute r = xR mod n. If r = 0, then return to Step 1. 1.
    // 4. Use the selected hash function to compute H = Hash(M).
    // 5. Convert the bit string H to an integer e as described in Appendix B.2.
    // 6. Compute s = (k^-1 *  (e + d *  r)) mod q. If s = 0, return to Step 1.2.
    // 7. Return (r, s).
    // In the code below, q is n (that it, the order of the curve is p)

    do  // This implements the loop at step 6. If s is zero, start over.
    {
        for(; tries > 0; tries--)
        {
            // Step 1 and 2 -- generate an ephemeral key and the modular inverse
            // of the private key.
            if(!BnEccGenerateKeyPair(bnK, ecR, E, rand))
                continue;
            // x coordinate is mod p.  Make it mod q
            BnMod(ecR->x, order);
            // Make sure that it is not zero;
            if(BnEqualZero(ecR->x))
                continue;
            // write the modular reduced version of r as part of the signature
            BnCopy(bnR, ecR->x);
            // Make sure that a modular inverse exists and try again if not
            OK = (BnModInverse(bnIk, bnK, order));
            if(OK)
                break;
        }
        if(!OK)
            goto Exit;

        EcdsaDigest(bnE, digest, order);

        // now have inverse of K (bnIk), e (bnE), r (bnR),  d (bnD) and
        // CurveGetOrder(E)
        // Compute s = k^-1 (e + r*d)(mod q)
        //  first do s = r*d mod q
        BnModMult(bnS, bnR, bnD, order);
        // s = e + s = e + r * d
        BnAdd(bnS, bnE, bnS);
        // s = k^(-1)s (mod n) = k^(-1)(e + r * d)(mod n)
        BnModMult(bnS, bnIk, bnS, order);

        // If S is zero, try again
    } while(BnEqualZero(bnS));
Exit:
    return retVal;
}

#  if ALG_ECDAA

//*** BnSignEcdaa()
//
// This function performs 's' = 'r' + 'T' * 'd' mod 'q' where
// 1) 'r' is a random, or pseudo-random value created in the commit phase
// 2) 'nonceK' is a TPM-generated, random value 0 < 'nonceK' < 'n'
// 3) 'T' is mod 'q' of "Hash"('nonceK' || 'digest'), and
// 4) 'd' is a private key.
//
// The signature is the tuple ('nonceK', 's')
//
// Regrettably, the parameters in this function kind of collide with the parameter
// names used in ECSCHNORR making for a lot of confusion.
//  Return Type: TPM_RC
//      TPM_RC_SCHEME       unsupported hash algorithm
//      TPM_RC_NO_RESULT    cannot get values from random number generator
static TPM_RC BnSignEcdaa(
    TPM2B_ECC_PARAMETER* nonceK,  // OUT: 'nonce' component of the signature
    bigNum               bnS,     // OUT: 's' component of the signature
    bigCurve             E,       // IN: the curve used in signing
    bigNum               bnD,     // IN: the private key
    const TPM2B_DIGEST*  digest,  // IN: the value to sign (mod 'q')
    TPMT_ECC_SCHEME*     scheme,  // IN: signing scheme (contains the
                                  //      commit count value).
    OBJECT*     eccKey,           // IN: The signing key
    RAND_STATE* rand              // IN: a random number state
)
{
    TPM_RC              retVal;
    TPM2B_ECC_PARAMETER r;
    HASH_STATE          state;
    TPM2B_DIGEST        T;
    BN_MAX(bnT);
    //
    NOT_REFERENCED(rand);
    if(!CryptGenerateR(&r,
                       &scheme->details.ecdaa.count,
                       eccKey->publicArea.parameters.eccDetail.curveID,
                       &eccKey->name))
        retVal = TPM_RC_VALUE;
    else
    {
        // This allocation is here because 'r' doesn't have a value until
        // CrypGenerateR() is done.
        ECC_INITIALIZED(bnR, &r);
        do
        {
            // generate nonceK such that 0 < nonceK < n
            // use bnT as a temp.
            if(!BnEccGetPrivate(bnT, AccessCurveData(E), rand))
            {
                retVal = TPM_RC_NO_RESULT;
                break;
            }
            BnTo2B(bnT, &nonceK->b, 0);

            T.t.size = CryptHashStart(&state, scheme->details.ecdaa.hashAlg);
            if(T.t.size == 0)
            {
                retVal = TPM_RC_SCHEME;
            }
            else
            {
                CryptDigestUpdate2B(&state, &nonceK->b);
                CryptDigestUpdate2B(&state, &digest->b);
                CryptHashEnd2B(&state, &T.b);
                BnFrom2B(bnT, &T.b);
                // Watch out for the name collisions in this call!!
                retVal = BnSchnorrSign(bnS, bnR, bnT, bnD, AccessCurveData(E)->order);
            }
        } while(retVal == TPM_RC_NO_RESULT);
        // Because the rule is that internal state is not modified if the command
        // fails, only end the commit if the command succeeds.
        // NOTE that if the result of the Schnorr computation was zero
        // it will probably not be worthwhile to run the same command again because
        // the result will still be zero. This means that the Commit command will
        // need to be run again to get a new commit value for the signature.
        if(retVal == TPM_RC_SUCCESS)
            CryptEndCommit(scheme->details.ecdaa.count);
    }
    return retVal;
}
#  endif  // ALG_ECDAA

#  if ALG_ECSCHNORR

//*** SchnorrReduce()
// Function to reduce a hash result if it's magnitude is too large. The size of
// 'number' is set so that it has no more bytes of significance than 'reference'
// value. If the resulting number can have more bits of significance than
// 'reference'.
static void SchnorrReduce(TPM2B*   number,    // IN/OUT: Value to reduce
                          bigConst reference  // IN: the reference value
)
{
    UINT16 maxBytes = (UINT16)BITS_TO_BYTES(BnSizeInBits(reference));
    if(number->size > maxBytes)
        number->size = maxBytes;
}

//*** SchnorrEcc()
// This function is used to perform a modified Schnorr signature.
//
// This function will generate a random value 'k' and compute
// a) ('xR', 'yR') = ['k']'G'
// b) 'r' = "Hash"('xR' || 'P')(mod 'q')
// c) 'rT' = truncated 'r'
// d) 's'= 'k' + 'rT' * 'ds' (mod 'q')
// e) return the tuple 'rT', 's'
//
//  Return Type: TPM_RC
//      TPM_RC_NO_RESULT        failure in the Schnorr sign process
//      TPM_RC_SCHEME           hashAlg can't produce zero-length digest
static TPM_RC BnSignEcSchnorr(
    bigNum              bnR,      // OUT: 'r' component of the signature
    bigNum              bnS,      // OUT: 's' component of the signature
    bigCurve            E,        // IN: the curve used in signing
    bigNum              bnD,      // IN: the signing key
    const TPM2B_DIGEST* digest,   // IN: the digest to sign
    TPM_ALG_ID          hashAlg,  // IN: signing scheme (contains a hash)
    RAND_STATE*         rand      // IN: non-NULL when testing
)
{
    HASH_STATE hashState;
    UINT16     digestSize = CryptHashGetDigestSize(hashAlg);
    TPM2B_TYPE(T, MAX(MAX_DIGEST_SIZE, MAX_ECC_KEY_BYTES));
    TPM2B_T               T2b;
    TPM2B*                e      = &T2b.b;
    TPM_RC                retVal = TPM_RC_NO_RESULT;
    const ECC_CURVE_DATA* C;
    bigConst              order;
    bigConst              prime;
    ECC_NUM(bnK);
    POINT(ecR);
    //
    // Parameter checks
    if(E == NULL)
        ERROR_RETURN(TPM_RC_VALUE);
    C     = AccessCurveData(E);
    order = CurveGetOrder(C);
    prime = CurveGetOrder(C);

    // If the digest does not produce a hash, then null the signature and return
    // a failure.
    if(digestSize == 0)
    {
        BnSetWord(bnR, 0);
        BnSetWord(bnS, 0);
        ERROR_RETURN(TPM_RC_SCHEME);
    }
    do
    {
        // Generate a random key pair
        if(!BnEccGenerateKeyPair(bnK, ecR, E, rand))
            break;
        // Convert R.x to a string
        BnTo2B(ecR->x, e, (NUMBYTES)BITS_TO_BYTES(BnSizeInBits(prime)));

        // f) compute r = Hash(e || P) (mod n)
        CryptHashStart(&hashState, hashAlg);
        CryptDigestUpdate2B(&hashState, e);
        CryptDigestUpdate2B(&hashState, &digest->b);
        e->size = CryptHashEnd(&hashState, digestSize, e->buffer);
        // Reduce the hash size if it is larger than the curve order
        SchnorrReduce(e, order);
        // Convert hash to number
        BnFrom2B(bnR, e);
        // Do the Schnorr computation
        retVal = BnSchnorrSign(bnS, bnK, bnR, bnD, CurveGetOrder(C));
    } while(retVal == TPM_RC_NO_RESULT);
Exit:
    return retVal;
}

#  endif  // ALG_ECSCHNORR

#  if ALG_SM2
#    ifdef _SM2_SIGN_DEBUG

//*** BnHexEqual()
// This function compares a bignum value to a hex string.
//  Return Type: BOOL
//      TRUE(1)         values equal
//      FALSE(0)        values not equal
static BOOL BnHexEqual(bigNum      bn,  //IN: big number value
                       const char* c    //IN: character string number
)
{
    ECC_NUM(bnC);
    BnFromHex(bnC, c);
    return (BnUnsignedCmp(bn, bnC) == 0);
}
#    endif  // _SM2_SIGN_DEBUG

//*** BnSignEcSm2()
// This function signs a digest using the method defined in SM2 Part 2. The method
// in the standard will add a header to the message to be signed that is a hash of
// the values that define the key. This then hashed with the message to produce a
// digest ('e'). This function signs 'e'.
//  Return Type: TPM_RC
//      TPM_RC_VALUE         bad curve
static TPM_RC BnSignEcSm2(bigNum   bnR,  // OUT: 'r' component of the signature
                          bigNum   bnS,  // OUT: 's' component of the signature
                          bigCurve E,    // IN: the curve used in signing
                          bigNum   bnD,  // IN: the private key
                          const TPM2B_DIGEST* digest,  // IN: the digest to sign
                          RAND_STATE* rand  // IN: random number generator (mostly for
                                            //     debug)
)
{
    BN_MAX_INITIALIZED(bnE, digest);  // Don't know how big digest might be
    ECC_NUM(bnN);
    ECC_NUM(bnK);
    ECC_NUM(bnT);  // temp
    POINT(Q1);
    bigConst order = (E != NULL) ? CurveGetOrder(AccessCurveData(E)) : NULL;
//
#    ifdef _SM2_SIGN_DEBUG
    BnFromHex(bnE,
              "B524F552CD82B8B028476E005C377FB1"
              "9A87E6FC682D48BB5D42E3D9B9EFFE76");
    BnFromHex(bnD,
              "128B2FA8BD433C6C068C8D803DFF7979"
              "2A519A55171B1B650C23661D15897263");
#    endif
    // A3: Use random number generator to generate random number 1 <= k <= n-1;
    // NOTE: Ax: numbers are from the SM2 standard
loop:
{
    // Get a random number 0 < k < n
    BnGenerateRandomInRange(bnK, order, rand);
#    ifdef _SM2_SIGN_DEBUG
    BnFromHex(bnK,
              "6CB28D99385C175C94F94E934817663F"
              "C176D925DD72B727260DBAAE1FB2F96F");
#    endif
    // A4: Figure out the point of elliptic curve (x1, y1)=[k]G, and according
    // to details specified in 4.2.7 in Part 1 of this document, transform the
    // data type of x1 into an integer;
    if(!BnEccModMult(Q1, NULL, bnK, E))
        goto loop;
    // A5: Figure out 'r' = ('e' + 'x1') mod 'n',
    BnAdd(bnR, bnE, Q1->x);
    BnMod(bnR, order);
#    ifdef _SM2_SIGN_DEBUG
    pAssert(BnHexEqual(bnR,
                       "40F1EC59F793D9F49E09DCEF49130D41"
                       "94F79FB1EED2CAA55BACDB49C4E755D1"));
#    endif
    // if r=0 or r+k=n, return to A3;
    if(BnEqualZero(bnR))
        goto loop;
    BnAdd(bnT, bnK, bnR);
    if(BnUnsignedCmp(bnT, bnN) == 0)
        goto loop;
    // A6: Figure out s = ((1 + dA)^-1  (k - r  dA)) mod n,
    // if s=0, return to A3;
    // compute t = (1+dA)^-1
    BnAddWord(bnT, bnD, 1);
    BnModInverse(bnT, bnT, order);
#    ifdef _SM2_SIGN_DEBUG
    pAssert(BnHexEqual(bnT,
                       "79BFCF3052C80DA7B939E0C6914A18CB"
                       "B2D96D8555256E83122743A7D4F5F956"));
#    endif
    // compute s = t * (k - r * dA) mod n
    BnModMult(bnS, bnR, bnD, order);
    // k - r * dA mod n = k + n - ((r * dA) mod n)
    BnSub(bnS, order, bnS);
    BnAdd(bnS, bnK, bnS);
    BnModMult(bnS, bnS, bnT, order);
#    ifdef _SM2_SIGN_DEBUG
    pAssert(BnHexEqual(bnS,
                       "6FC6DAC32C5D5CF10C77DFB20F7C2EB6"
                       "67A457872FB09EC56327A67EC7DEEBE7"));
#    endif
    if(BnEqualZero(bnS))
        goto loop;
}
// A7: According to details specified in 4.2.1 in Part 1 of this document,
// transform the data type of r, s into bit strings, signature of message M
// is (r, s).
// This is handled by the common return code
#    ifdef _SM2_SIGN_DEBUG
    pAssert(BnHexEqual(bnR,
                       "40F1EC59F793D9F49E09DCEF49130D41"
                       "94F79FB1EED2CAA55BACDB49C4E755D1"));
    pAssert(BnHexEqual(bnS,
                       "6FC6DAC32C5D5CF10C77DFB20F7C2EB6"
                       "67A457872FB09EC56327A67EC7DEEBE7"));
#    endif
    return TPM_RC_SUCCESS;
}
#  endif  // ALG_SM2

//*** CryptEccSign()
// This function is the dispatch function for the various ECC-based
// signing schemes.
// There is a bit of ugliness to the parameter passing. In order to test this,
// we sometime would like to use a deterministic RNG so that we can get the same
// signatures during testing. The easiest way to do this for most schemes is to
// pass in a deterministic RNG and let it return canned values during testing.
// There is a competing need for a canned parameter to use in ECDAA. To accommodate
// both needs with minimal fuss, a special type of RAND_STATE is defined to carry
// the address of the commit value. The setup and handling of this is not very
// different for the caller than what was in previous versions of the code.
//  Return Type: TPM_RC
//      TPM_RC_SCHEME            'scheme' is not supported
LIB_EXPORT TPM_RC CryptEccSign(TPMT_SIGNATURE* signature,  // OUT: signature
                               OBJECT* signKey,  // IN: ECC key to sign the hash
                               const TPM2B_DIGEST* digest,  // IN: digest to sign
                               TPMT_ECC_SCHEME*    scheme,  // IN: signing scheme
                               RAND_STATE*         rand)
{
    CURVE_INITIALIZED(E, signKey->publicArea.parameters.eccDetail.curveID);
    ECC_INITIALIZED(bnD, &signKey->sensitive.sensitive.ecc.b);
    ECC_NUM(bnR);
    ECC_NUM(bnS);
    const ECC_CURVE_DATA* C;
    TPM_RC                retVal = TPM_RC_SCHEME;
    //
    NOT_REFERENCED(scheme);
    if(E == NULL)
        ERROR_RETURN(TPM_RC_VALUE);
    C = AccessCurveData(E);
    signature->signature.ecdaa.signatureR.t.size =
        sizeof(signature->signature.ecdaa.signatureR.t.buffer);
    signature->signature.ecdaa.signatureS.t.size =
        sizeof(signature->signature.ecdaa.signatureS.t.buffer);
    TEST(signature->sigAlg);
    switch(signature->sigAlg)
    {
        case TPM_ALG_ECDSA:
            retVal = BnSignEcdsa(bnR, bnS, E, bnD, digest, rand);
            break;
#  if ALG_ECDAA
        case TPM_ALG_ECDAA:
            retVal = BnSignEcdaa(&signature->signature.ecdaa.signatureR,
                                 bnS,
                                 E,
                                 bnD,
                                 digest,
                                 scheme,
                                 signKey,
                                 rand);
            bnR    = NULL;
            break;
#  endif
#  if ALG_ECSCHNORR
        case TPM_ALG_ECSCHNORR:
            retVal = BnSignEcSchnorr(
                bnR, bnS, E, bnD, digest, signature->signature.ecschnorr.hash, rand);
            break;
#  endif
#  if ALG_SM2
        case TPM_ALG_SM2:
            retVal = BnSignEcSm2(bnR, bnS, E, bnD, digest, rand);
            break;
#  endif
        default:
            break;
    }
    // If signature generation worked, convert the results.
    if(retVal == TPM_RC_SUCCESS)
    {
        NUMBYTES orderBytes = (NUMBYTES)BITS_TO_BYTES(BnSizeInBits(CurveGetOrder(C)));
        if(bnR != NULL)
            BnTo2B(bnR, &signature->signature.ecdaa.signatureR.b, orderBytes);
        if(bnS != NULL)
            BnTo2B(bnS, &signature->signature.ecdaa.signatureS.b, orderBytes);
    }
Exit:
    CURVE_FREE(E);
    return retVal;
}

//********************* Signature Validation   ********************

#  if ALG_ECDSA

//*** BnValidateSignatureEcdsa()
// This function validates an ECDSA signature. rIn and sIn should have been checked
// to make sure that they are in the range 0 < 'v' < 'n'
//  Return Type: TPM_RC
//      TPM_RC_SIGNATURE           signature not valid
TPM_RC
BnValidateSignatureEcdsa(bigNum   bnR,  // IN: 'r' component of the signature
                         bigNum   bnS,  // IN: 's' component of the signature
                         bigCurve E,    // IN: the curve used in the signature
                                        //     process
                         bn_point_t*         ecQ,  // IN: the public point of the key
                         const TPM2B_DIGEST* digest  // IN: the digest that was signed
)
{
    // Make sure that the allocation for the digest is big enough for a maximum
    // digest
    BN_VAR(bnE, MAX(MAX_ECC_KEY_BYTES, MAX_DIGEST_SIZE) * 8);
    POINT(ecR);
    ECC_NUM(bnU1);
    ECC_NUM(bnU2);
    ECC_NUM(bnW);
    bigConst order  = CurveGetOrder(AccessCurveData(E));
    TPM_RC   retVal = TPM_RC_SIGNATURE;
    //
    // Get adjusted digest
    EcdsaDigest(bnE, digest, order);
    // 1. If r and s are not both integers in the interval [1, n - 1], output
    //    INVALID.
    //  bnR  and bnS were validated by the caller
    // 2. Use the selected hash function to compute H0 = Hash(M0).
    // This is an input parameter
    // 3. Convert the bit string H0 to an integer e as described in Appendix B.2.
    // Done at entry
    // 4. Compute w = (s')^-1 mod n, using the routine in Appendix B.1.
    if(!BnModInverse(bnW, bnS, order))
        goto Exit;
    // 5. Compute u1 = (e' *   w) mod n, and compute u2 = (r' *  w) mod n.
    BnModMult(bnU1, bnE, bnW, order);
    BnModMult(bnU2, bnR, bnW, order);
    // 6. Compute the elliptic curve point R = (xR, yR) = u1G+u2Q, using EC
    //    scalar multiplication and EC addition (see [Routines]). If R is equal to
    //    the point at infinity O, output INVALID.
    if(BnPointMult(ecR, CurveGetG(AccessCurveData(E)), bnU1, ecQ, bnU2, E)
       != TPM_RC_SUCCESS)
        goto Exit;
    // 7. Compute v = Rx mod n.
    BnMod(ecR->x, order);
    // 8. Compare v and r0. If v = r0, output VALID; otherwise, output INVALID
    if(BnUnsignedCmp(ecR->x, bnR) != 0)
        goto Exit;

    retVal = TPM_RC_SUCCESS;
Exit:
    return retVal;
}

#  endif  // ALG_ECDSA

#  if ALG_SM2

//*** BnValidateSignatureEcSm2()
// This function is used to validate an SM2 signature.
//  Return Type: TPM_RC
//      TPM_RC_SIGNATURE            signature not valid
static TPM_RC BnValidateSignatureEcSm2(
    bigNum   bnR,               // IN: 'r' component of the signature
    bigNum   bnS,               // IN: 's' component of the signature
    bigCurve E,                 // IN: the curve used in the signature
                                //     process
    bigPoint            ecQ,    // IN: the public point of the key
    const TPM2B_DIGEST* digest  // IN: the digest that was signed
)
{
    POINT(P);
    ECC_NUM(bnRp);
    ECC_NUM(bnT);
    BN_MAX_INITIALIZED(bnE, digest);
    BOOL     OK;
    bigConst order = CurveGetOrder(AccessCurveData(E));

#    ifdef _SM2_SIGN_DEBUG
    // Make sure that the input signature is the test signature
    pAssert(BnHexEqual(bnR,
                       "40F1EC59F793D9F49E09DCEF49130D41"
                       "94F79FB1EED2CAA55BACDB49C4E755D1"));
    pAssert(BnHexEqual(bnS,
                       "6FC6DAC32C5D5CF10C77DFB20F7C2EB6"
                       "67A457872FB09EC56327A67EC7DEEBE7"));
#    endif
    // b)   compute t  := (r + s) mod n
    BnAdd(bnT, bnR, bnS);
    BnMod(bnT, order);
#    ifdef _SM2_SIGN_DEBUG
    pAssert(BnHexEqual(bnT,
                       "2B75F07ED7ECE7CCC1C8986B991F441A"
                       "D324D6D619FE06DD63ED32E0C997C801"));
#    endif
    // c)   verify that t > 0
    OK = !BnEqualZero(bnT);
    if(!OK)
        // set T to a value that should allow rest of the computations to run
        // without trouble
        BnCopy(bnT, bnS);
    // d)   compute (x, y) := [s]G + [t]Q
    OK = BnEccModMult2(P, NULL, bnS, ecQ, bnT, E);
#    ifdef _SM2_SIGN_DEBUG
    pAssert(OK
            && BnHexEqual(P->x,
                          "110FCDA57615705D5E7B9324AC4B856D"
                          "23E6D9188B2AE47759514657CE25D112"));
#    endif
    // e)   compute r' := (e + x) mod n (the x coordinate is in bnT)
    OK = OK && BnAdd(bnRp, bnE, P->x);
    OK = OK && BnMod(bnRp, order);

    // f)   verify that r' = r
    OK = OK && (BnUnsignedCmp(bnR, bnRp) == 0);

    if(!OK)
        return TPM_RC_SIGNATURE;
    else
        return TPM_RC_SUCCESS;
}

#  endif  // ALG_SM2

#  if ALG_ECSCHNORR

//*** BnValidateSignatureEcSchnorr()
// This function is used to validate an EC Schnorr signature.
//  Return Type: TPM_RC
//      TPM_RC_SIGNATURE        signature not valid
static TPM_RC BnValidateSignatureEcSchnorr(
    bigNum     bnR,             // IN: 'r' component of the signature
    bigNum     bnS,             // IN: 's' component of the signature
    TPM_ALG_ID hashAlg,         // IN: hash algorithm of the signature
    bigCurve   E,               // IN: the curve used in the signature
                                //     process
    bigPoint            ecQ,    // IN: the public point of the key
    const TPM2B_DIGEST* digest  // IN: the digest that was signed
)
{
    BN_MAX(bnRn);
    POINT(ecE);
    BN_MAX(bnEx);
    const ECC_CURVE_DATA* C          = AccessCurveData(E);
    bigConst              order      = CurveGetOrder(C);
    UINT16                digestSize = CryptHashGetDigestSize(hashAlg);
    HASH_STATE            hashState;
    TPM2B_TYPE(BUFFER, MAX(MAX_ECC_PARAMETER_BYTES, MAX_DIGEST_SIZE));
    TPM2B_BUFFER Ex2 = {{sizeof(Ex2.t.buffer), {0}}};
    BOOL         OK;
    //
    // E = [s]G - [r]Q
    BnMod(bnR, order);
    // Make -r = n - r
    BnSub(bnRn, order, bnR);
    // E = [s]G + [-r]Q
    OK = BnPointMult(ecE, CurveGetG(C), bnS, ecQ, bnRn, E) == TPM_RC_SUCCESS;
    //   // reduce the x portion of E mod q
    //    OK = OK && BnMod(ecE->x, order);
    // Convert to byte string
    OK = OK && BnTo2B(ecE->x, &Ex2.b, (NUMBYTES)(BITS_TO_BYTES(BnSizeInBits(order))));
    if(OK)
    {
        // Ex = h(pE.x || digest)
        CryptHashStart(&hashState, hashAlg);
        CryptDigestUpdate(&hashState, Ex2.t.size, Ex2.t.buffer);
        CryptDigestUpdate(&hashState, digest->t.size, digest->t.buffer);
        Ex2.t.size = CryptHashEnd(&hashState, digestSize, Ex2.t.buffer);
        SchnorrReduce(&Ex2.b, order);
        BnFrom2B(bnEx, &Ex2.b);
        // see if Ex matches R
        OK = BnUnsignedCmp(bnEx, bnR) == 0;
    }
    return (OK) ? TPM_RC_SUCCESS : TPM_RC_SIGNATURE;
}
#  endif  // ALG_ECSCHNORR

//*** CryptEccValidateSignature()
// This function validates an EcDsa or EcSchnorr signature.
// The point 'Qin' needs to have been validated to be on the curve of 'curveId'.
//  Return Type: TPM_RC
//      TPM_RC_SIGNATURE            not a valid signature
LIB_EXPORT TPM_RC CryptEccValidateSignature(
    TPMT_SIGNATURE*     signature,  // IN: signature to be verified
    OBJECT*             signKey,    // IN: ECC key signed the hash
    const TPM2B_DIGEST* digest      // IN: digest that was signed
)
{
    CURVE_INITIALIZED(E, signKey->publicArea.parameters.eccDetail.curveID);
    ECC_NUM(bnR);
    ECC_NUM(bnS);
    POINT_INITIALIZED(ecQ, &signKey->publicArea.unique.ecc);
    bigConst order;
    TPM_RC   retVal;

    if(E == NULL)
        ERROR_RETURN(TPM_RC_VALUE);

    order = CurveGetOrder(AccessCurveData(E));

    //    // Make sure that the scheme is valid
    switch(signature->sigAlg)
    {
        case TPM_ALG_ECDSA:
#  if ALG_ECSCHNORR
        case TPM_ALG_ECSCHNORR:
#  endif
#  if ALG_SM2
        case TPM_ALG_SM2:
#  endif
            break;
        default:
            ERROR_RETURN(TPM_RC_SCHEME);
            break;
    }
    // Can convert r and s after determining that the scheme is an ECC scheme. If
    // this conversion doesn't work, it means that the unmarshaling code for
    // an ECC signature is broken.
    BnFrom2B(bnR, &signature->signature.ecdsa.signatureR.b);
    BnFrom2B(bnS, &signature->signature.ecdsa.signatureS.b);

    // r and s have to be greater than 0 but less than the curve order
    if(BnEqualZero(bnR) || BnEqualZero(bnS))
        ERROR_RETURN(TPM_RC_SIGNATURE);
    if((BnUnsignedCmp(bnS, order) >= 0) || (BnUnsignedCmp(bnR, order) >= 0))
        ERROR_RETURN(TPM_RC_SIGNATURE);

    switch(signature->sigAlg)
    {
        case TPM_ALG_ECDSA:
            retVal = BnValidateSignatureEcdsa(bnR, bnS, E, ecQ, digest);
            break;

#  if ALG_ECSCHNORR
        case TPM_ALG_ECSCHNORR:
            retVal = BnValidateSignatureEcSchnorr(
                bnR, bnS, signature->signature.any.hashAlg, E, ecQ, digest);
            break;
#  endif
#  if ALG_SM2
        case TPM_ALG_SM2:
            retVal = BnValidateSignatureEcSm2(bnR, bnS, E, ecQ, digest);
            break;
#  endif
        default:
            FAIL(FATAL_ERROR_INTERNAL);
    }
Exit:
    CURVE_FREE(E);
    return retVal;
}

//***CryptEccCommitCompute()
// This function performs the point multiply operations required by TPM2_Commit.
//
// If 'B' or 'M' is provided, they must be on the curve defined by 'curveId'. This
// routine does not check that they are on the curve and results are unpredictable
// if they are not.
//
// It is a fatal error if 'r' is NULL. If 'B' is not NULL, then it is a
// fatal error if 'd' is NULL or if 'K' and 'L' are both NULL.
// If 'M' is not NULL, then it is a fatal error if 'E' is NULL.
//
//  Return Type: TPM_RC
//      TPM_RC_NO_RESULT        if 'K', 'L' or 'E' was computed to be the point
//                              at infinity
//      TPM_RC_CANCELED         a cancel indication was asserted during this
//                              function
LIB_EXPORT TPM_RC CryptEccCommitCompute(
    TPMS_ECC_POINT*      K,        // OUT: [d]B or [r]Q
    TPMS_ECC_POINT*      L,        // OUT: [r]B
    TPMS_ECC_POINT*      E,        // OUT: [r]M
    TPM_ECC_CURVE        curveId,  // IN: the curve for the computations
    TPMS_ECC_POINT*      M,        // IN: M (optional)
    TPMS_ECC_POINT*      B,        // IN: B (optional)
    TPM2B_ECC_PARAMETER* d,        // IN: d (optional)
    TPM2B_ECC_PARAMETER* r         // IN: the computed r value (required)
)
{
    CURVE_INITIALIZED(curve, curveId);  // Normally initialize E as the curve, but
                                        // E means something else in this function
    ECC_INITIALIZED(bnR, r);
    TPM_RC retVal = TPM_RC_SUCCESS;
    //
    // Validate that the required parameters are provided.
    // Note: E has to be provided if computing E := [r]Q or E := [r]M. Will do
    // E := [r]Q if both M and B are NULL.
    pAssert(r != NULL && E != NULL);

    // Initialize the output points in case they are not computed
    ClearPoint2B(K);
    ClearPoint2B(L);
    ClearPoint2B(E);

    // Sizes of the r parameter may not be zero
    pAssert(r->t.size > 0);

    // If B is provided, compute K=[d]B and L=[r]B
    if(B != NULL)
    {
        ECC_INITIALIZED(bnD, d);
        POINT_INITIALIZED(pB, B);
        POINT(pK);
        POINT(pL);
        //
        pAssert(d != NULL && K != NULL && L != NULL);

        if(!BnIsOnCurve(pB, AccessCurveData(curve)))
            ERROR_RETURN(TPM_RC_VALUE);
        // do the math for K = [d]B
        if((retVal = BnPointMult(pK, pB, bnD, NULL, NULL, curve)) != TPM_RC_SUCCESS)
            goto Exit;
        // Convert BN K to TPM2B K
        BnPointTo2B(K, pK, curve);
        //  compute L= [r]B after checking for cancel
        if(_plat__IsCanceled())
            ERROR_RETURN(TPM_RC_CANCELED);
        // compute L = [r]B
        if(!BnIsValidPrivateEcc(bnR, curve))
            ERROR_RETURN(TPM_RC_VALUE);
        if((retVal = BnPointMult(pL, pB, bnR, NULL, NULL, curve)) != TPM_RC_SUCCESS)
            goto Exit;
        // Convert BN L to TPM2B L
        BnPointTo2B(L, pL, curve);
    }
    if((M != NULL) || (B == NULL))
    {
        POINT_INITIALIZED(pM, M);
        POINT(pE);
        //
        // Make sure that a place was provided for the result
        pAssert(E != NULL);

        // if this is the third point multiply, check for cancel first
        if((B != NULL) && _plat__IsCanceled())
            ERROR_RETURN(TPM_RC_CANCELED);

        // If M provided, then pM will not be NULL and will compute E = [r]M.
        // However, if M was not provided, then pM will be NULL and E = [r]G
        // will be computed
        if((retVal = BnPointMult(pE, pM, bnR, NULL, NULL, curve)) != TPM_RC_SUCCESS)
            goto Exit;
        // Convert E to 2B format
        BnPointTo2B(E, pE, curve);
    }
Exit:
    CURVE_FREE(curve);
    return retVal;
}

#endif  // ALG_ECC