text: /* 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 functions that are used for the two-phase, ECC, 
  // key-exchange protocols
  
  
  #include "Tpm.h"
  
  #if CC_ZGen_2Phase == YES
  
  //** Functions
  
  #if ALG_ECMQV
  
  //*** avf1()
  // This function does the associated value computation required by MQV key
  // exchange.
  // Process:
  // 1. Convert 'xQ' to an integer 'xqi' using the convention specified in Appendix C.3.
  // 2. Calculate
  //        xqm = xqi mod 2^ceil(f/2) (where f = ceil(log2(n)).
  // 3. Calculate the associate value function
  //        avf(Q) = xqm + 2ceil(f / 2)
  // Always returns TRUE(1).
  static BOOL
  avf1(
      bigNum               bnX,           // IN/OUT: the reduced value
      bigNum               bnN            // IN: the order of the curve
      )
  {
  // compute f = 2^(ceil(ceil(log2(n)) / 2))
      int                      f = (BnSizeInBits(bnN) + 1) / 2;
  // x' = 2^f + (x mod 2^f)
      BnMaskBits(bnX, f);   // This is mod 2*2^f but it doesn't matter because
                              // the next operation will SET the extra bit anyway
      BnSetBit(bnX, f);
      return TRUE;
  }
  
  //*** C_2_2_MQV()
  // This function performs the key exchange defined in SP800-56A
  // 6.1.1.4 Full MQV, C(2, 2, ECC MQV).
  //
  // CAUTION: Implementation of this function may require use of essential claims in
  // patents not owned by TCG members.
  //
  // Points 'QsB' and 'QeB' are required to be on the curve of 'inQsA'. The function 
  // will fail, possibly catastrophically, if this is not the case.
  //  Return Type: TPM_RC
  //      TPM_RC_NO_RESULT        the value for dsA does not give a valid point on the
  //                              curve
  static TPM_RC
  C_2_2_MQV(
      TPMS_ECC_POINT          *outZ,         // OUT: the computed point
      TPM_ECC_CURVE            curveId,      // IN: the curve for the computations
      TPM2B_ECC_PARAMETER     *dsA,          // IN: static private TPM key
      TPM2B_ECC_PARAMETER     *deA,          // IN: ephemeral private TPM key
      TPMS_ECC_POINT          *QsB,          // IN: static public party B key
      TPMS_ECC_POINT          *QeB           // IN: ephemeral public party B key
      )
  {
      CURVE_INITIALIZED(E, curveId);
      const ECC_CURVE_DATA    *C;
      POINT(pQeA);
      POINT_INITIALIZED(pQeB, QeB);
      POINT_INITIALIZED(pQsB, QsB);
      ECC_NUM(bnTa);
      ECC_INITIALIZED(bnDeA, deA);
      ECC_INITIALIZED(bnDsA, dsA);
      ECC_NUM(bnN);
      ECC_NUM(bnXeB);
      TPM_RC                 retVal;
  //
      // Parameter checks
      if(E == NULL)
          ERROR_RETURN(TPM_RC_VALUE);
      pAssert(outZ != NULL && pQeB != NULL && pQsB != NULL && deA != NULL 
              && dsA != NULL);
      C = AccessCurveData(E);
  // Process:
  //  1. implicitsigA = (de,A + avf(Qe,A)ds,A ) mod n.
  //  2. P = h(implicitsigA)(Qe,B + avf(Qe,B)Qs,B).
  //  3. If P = O, output an error indicator.
  //  4. Z=xP, where xP is the x-coordinate of P.
  
      // Compute the public ephemeral key pQeA = [de,A]G
      if((retVal = BnPointMult(pQeA, CurveGetG(C), bnDeA, NULL, NULL, E))
         != TPM_RC_SUCCESS)
          goto Exit;
  
  //  1. implicitsigA = (de,A + avf(Qe,A)ds,A ) mod n.
  //  tA := (ds,A + de,A  avf(Xe,A)) mod n    (3)
  //  Compute 'tA' = ('deA' +  'dsA'  avf('XeA')) mod n
      // Ta = avf(XeA);
      BnCopy(bnTa, pQeA->x);
      avf1(bnTa, bnN);
      // do Ta = ds,A * Ta mod n = dsA * avf(XeA) mod n
      BnModMult(bnTa, bnDsA, bnTa, bnN);
      // now Ta = deA + Ta mod n =  deA + dsA * avf(XeA) mod n
      BnAdd(bnTa, bnTa, bnDeA);
      BnMod(bnTa, bnN);
  
  //  2. P = h(implicitsigA)(Qe,B + avf(Qe,B)Qs,B).
  // Put this in because almost every case of h is == 1 so skip the call when
      // not necessary.
      if(!BnEqualWord(CurveGetCofactor(C), 1))
          // Cofactor is not 1 so compute Ta := Ta * h mod n
          BnModMult(bnTa, bnTa, CurveGetCofactor(C), CurveGetOrder(C));
  
      // Now that 'tA' is (h * 'tA' mod n)
      // 'outZ' = (tA)(Qe,B + avf(Qe,B)Qs,B).
  
      // first, compute XeB = avf(XeB)
      avf1(bnXeB, bnN);
  
      // QsB := [XeB]QsB
      BnPointMult(pQsB, pQsB, bnXeB, NULL, NULL, E);
      BnEccAdd(pQeB, pQeB, pQsB, E);
  
      // QeB := [tA]QeB = [tA](QsB + [Xe,B]QeB) and check for at infinity
      // If the result is not the point at infinity, return QeB
      BnPointMult(pQeB, pQeB, bnTa, NULL, NULL, E);
      if(BnEqualZero(pQeB->z))
          ERROR_RETURN(TPM_RC_NO_RESULT);
      // Convert BIGNUM E to TPM2B E
      BnPointTo2B(outZ, pQeB, E);
  
  Exit:
      CURVE_FREE(E);
      return retVal;
  }
  
  #endif // ALG_ECMQV
  
  //*** C_2_2_ECDH()
  // This function performs the two phase key exchange defined in SP800-56A,
  // 6.1.1.2 Full Unified Model, C(2, 2, ECC CDH).
  //
  static TPM_RC
  C_2_2_ECDH(
      TPMS_ECC_POINT          *outZs,         // OUT: Zs
      TPMS_ECC_POINT          *outZe,         // OUT: Ze
      TPM_ECC_CURVE            curveId,       // IN: the curve for the computations
      TPM2B_ECC_PARAMETER     *dsA,           // IN: static private TPM key
      TPM2B_ECC_PARAMETER     *deA,           // IN: ephemeral private TPM key
      TPMS_ECC_POINT          *QsB,           // IN: static public party B key
      TPMS_ECC_POINT          *QeB            // IN: ephemeral public party B key
      )
  {
      CURVE_INITIALIZED(E, curveId);
      ECC_INITIALIZED(bnAs, dsA);
      ECC_INITIALIZED(bnAe, deA);
      POINT_INITIALIZED(ecBs, QsB);
      POINT_INITIALIZED(ecBe, QeB);
      POINT(ecZ);
      TPM_RC            retVal;
  //
      // Parameter checks
      if(E == NULL)
          ERROR_RETURN(TPM_RC_CURVE);
      pAssert(outZs != NULL && dsA != NULL && deA != NULL && QsB != NULL 
              && QeB != NULL);
  
      // Do the point multiply for the Zs value ([dsA]QsB)
      retVal = BnPointMult(ecZ, ecBs, bnAs, NULL, NULL, E);
      if(retVal == TPM_RC_SUCCESS)
      {
          // Convert the Zs value.
          BnPointTo2B(outZs, ecZ, E);
          // Do the point multiply for the Ze value ([deA]QeB)
          retVal = BnPointMult(ecZ, ecBe, bnAe, NULL, NULL, E);
          if(retVal == TPM_RC_SUCCESS)
              BnPointTo2B(outZe, ecZ, E);
      }
  Exit:
      CURVE_FREE(E);
      return retVal;
  }
  
  //*** CryptEcc2PhaseKeyExchange()
  // This function is the dispatch routine for the EC key exchange functions that use
  // two ephemeral and two static keys.
  //  Return Type: TPM_RC
  //      TPM_RC_SCHEME             scheme is not defined
  LIB_EXPORT TPM_RC
  CryptEcc2PhaseKeyExchange(
      TPMS_ECC_POINT          *outZ1,         // OUT: a computed point
      TPMS_ECC_POINT          *outZ2,         // OUT: and optional second point
      TPM_ECC_CURVE            curveId,       // IN: the curve for the computations
      TPM_ALG_ID               scheme,        // IN: the key exchange scheme
      TPM2B_ECC_PARAMETER     *dsA,           // IN: static private TPM key
      TPM2B_ECC_PARAMETER     *deA,           // IN: ephemeral private TPM key
      TPMS_ECC_POINT          *QsB,           // IN: static public party B key
      TPMS_ECC_POINT          *QeB            // IN: ephemeral public party B key
      )
  {
      pAssert(outZ1 != NULL
              && dsA != NULL && deA != NULL
              && QsB != NULL && QeB != NULL);
  
      // Initialize the output points so that they are empty until one of the
      // functions decides otherwise
      outZ1->x.b.size = 0;
      outZ1->y.b.size = 0;
      if(outZ2 != NULL)
      {
          outZ2->x.b.size = 0;
          outZ2->y.b.size = 0;
      }
      switch(scheme)
      {
          case ALG_ECDH_VALUE:
              return C_2_2_ECDH(outZ1, outZ2, curveId, dsA, deA, QsB, QeB);
              break;
  #if     ALG_ECMQV
          case ALG_ECMQV_VALUE:
              return C_2_2_MQV(outZ1, curveId, dsA, deA, QsB, QeB);
              break;
  #endif
  #if     ALG_SM2
          case ALG_SM2_VALUE:
              return SM2KeyExchange(outZ1, curveId, dsA, deA, QsB, QeB);
              break;
  #endif
          default:
              return TPM_RC_SCHEME;
      }
  }
  
  #if     ALG_SM2
  
  //*** ComputeWForSM2()
  // Compute the value for w used by SM2
  static UINT32
  ComputeWForSM2(
      bigCurve        E
      )
  {
      //  w := ceil(ceil(log2(n)) / 2) - 1
      return (BnMsb(CurveGetOrder(AccessCurveData(E))) / 2 - 1);
  }
  
  //*** avfSm2()
  // This function does the associated value computation required by SM2 key
  // exchange. This is different from the avf() in the international standards
  // because it returns a value that is half the size of the value returned by the
  // standard avf(). For example, if 'n' is 15, 'Ws' ('w' in the standard) is 2 but 
  // the 'W' here is 1. This means that an input value of 14 (1110b) would return a 
  // value of 110b with the standard but 10b with the scheme in SM2.
  static bigNum
  avfSm2(
      bigNum              bn,           // IN/OUT: the reduced value
      UINT32              w              // IN: the value of w
      )
  {
      // a)   set w := ceil(ceil(log2(n)) / 2) - 1
      // b)   set x' := 2^w + ( x & (2^w - 1))
      // This is just like the avf for MQV where x' = 2^w + (x mod 2^w)
  
      BnMaskBits(bn, w);   // as with avf1, this is too big by a factor of 2 but
                           // it doesn't matter because we SET the extra bit
                           // anyway
      BnSetBit(bn, w);
      return bn;
  }
  
  //*** SM2KeyExchange()
  // This function performs the key exchange defined in SM2.
  // The first step is to compute
  //  'tA' = ('dsA' + 'deA'  avf(Xe,A)) mod 'n'
  // Then, compute the 'Z' value from
  // 'outZ' = ('h'  'tA' mod 'n') ('QsA' + [avf('QeB.x')]('QeB')).
  // The function will compute the ephemeral public key from the ephemeral
  // private key.
  // All points are required to be on the curve of 'inQsA'. The function will fail
  // catastrophically if this is not the case
  //  Return Type: TPM_RC
  //      TPM_RC_NO_RESULT        the value for dsA does not give a valid point on the
  //                              curve
  LIB_EXPORT TPM_RC
  SM2KeyExchange(
      TPMS_ECC_POINT        *outZ,         // OUT: the computed point
      TPM_ECC_CURVE          curveId,      // IN: the curve for the computations
      TPM2B_ECC_PARAMETER   *dsAIn,        // IN: static private TPM key
      TPM2B_ECC_PARAMETER   *deAIn,        // IN: ephemeral private TPM key
      TPMS_ECC_POINT        *QsBIn,        // IN: static public party B key
      TPMS_ECC_POINT        *QeBIn         // IN: ephemeral public party B key
      )
  {
      CURVE_INITIALIZED(E, curveId);
      const ECC_CURVE_DATA      *C;
      ECC_INITIALIZED(dsA, dsAIn);
      ECC_INITIALIZED(deA, deAIn);
      POINT_INITIALIZED(QsB, QsBIn);
      POINT_INITIALIZED(QeB, QeBIn);
      BN_WORD_INITIALIZED(One, 1);
      POINT(QeA);
      ECC_NUM(XeB);
      POINT(Z);
      ECC_NUM(Ta);
      UINT32                   w;
      TPM_RC                 retVal = TPM_RC_NO_RESULT;
  //
      // Parameter checks
      if(E == NULL)
          ERROR_RETURN(TPM_RC_CURVE);
      C = AccessCurveData(E);
      pAssert(outZ != NULL && dsA != NULL && deA != NULL &&  QsB != NULL 
              && QeB != NULL);
  
      // Compute the value for w
      w = ComputeWForSM2(E);
  
      // Compute the public ephemeral key pQeA = [de,A]G
      if(!BnEccModMult(QeA, CurveGetG(C), deA, E))
          goto Exit;
  
      //  tA := (ds,A + de,A  avf(Xe,A)) mod n    (3)
      //  Compute 'tA' = ('dsA' +  'deA'  avf('XeA')) mod n
      // Ta = avf(XeA);
      // do Ta = de,A * Ta = deA * avf(XeA)
      BnMult(Ta, deA, avfSm2(QeA->x, w));
      // now Ta = dsA + Ta =  dsA + deA * avf(XeA)
      BnAdd(Ta, dsA, Ta);
      BnMod(Ta, CurveGetOrder(C));
  
      //  outZ = [h  tA mod n] (Qs,B + [avf(Xe,B)](Qe,B)) (4)
      // Put this in because almost every case of h is == 1 so skip the call when
      // not necessary.
      if(!BnEqualWord(CurveGetCofactor(C), 1))
          // Cofactor is not 1 so compute Ta := Ta * h mod n
          BnModMult(Ta, Ta, CurveGetCofactor(C), CurveGetOrder(C));
      // Now that 'tA' is (h * 'tA' mod n)
      // 'outZ' = ['tA'](QsB + [avf(QeB.x)](QeB)).
      BnCopy(XeB, QeB->x);
      if(!BnEccModMult2(Z, QsB, One, QeB, avfSm2(XeB, w), E))
          goto Exit;
      // QeB := [tA]QeB = [tA](QsB + [Xe,B]QeB) and check for at infinity
      if(!BnEccModMult(Z, Z, Ta, E))
          goto Exit;
      // Convert BIGNUM E to TPM2B E
      BnPointTo2B(outZ, Z, E);
      retVal = TPM_RC_SUCCESS;
  Exit:
      CURVE_FREE(E);
      return retVal;
  }
  #endif
  
  #endif // CC_ZGen_2Phase