/*************************************************************************
* Copyright (C) 2022 Intel Corporation
*
* Licensed under the Apache License,  Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 	http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law  or agreed  to  in  writing,  software
* distributed under  the License  is  distributed  on  an  "AS IS"  BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the  specific  language  governing  permissions  and
* limitations under the License.
*************************************************************************/

#include "owncp.h"
#include "owndefs.h"
#include "pcpgfpecstuff.h"
#include "sm2/sm2_key_exchange_method.h"
#include "sm2/sm2_stuff.h"

#define CHECK_PUBLIC_KEY(KEY)                                                    \
   IPP_BAD_PTR1_RET((KEY))                                                       \
   IPP_BADARG_RET(!ECP_POINT_VALID_ID((KEY)), ippStsContextMatchErr)             \
   IPP_BADARG_RET(ECP_POINT_FELEN((KEY)) != GFP_FELEN(pME), ippStsOutOfRangeErr) \
   IPP_BADARG_RET(0 == gfec_IsPointOnCurve((KEY), pEC), ippStsInvalidPoint)


/**
 * @brief ippsGFpECKeyExchangeSM2_Setup
 * setup Key Exchange Context (add Za | Zb | Pa | Pb | Ra | Rb)
 * see standard:
 * [GBT.32918.3-2016] Public Key cryptographic algorithm SM2 based on elliptic curves
 * Part 3: Key exchange protocol
 * @param [in]  pZSelf            (self) user ID hash Z       (copy)
 * @param [in]  pZPeer            (peer) user ID hash Z       (copy)
 * @param [in]  pPublicKeySelf    (self) public key           (copy)
 * @param [in]  pPublicKeyPeer    (peer) public key           (copy)
 * @param [in]  pEphPublicKeySelf (self) ephemeral public key (copy)
 * @param [in]  pEphPublicKeyPeer (peer) ephemeral public key (copy)
 * @param [out] pKE               context
 * @return
 * ippStsNoErr               - successful
 * ippStsNullPtrErr          - if pEC | pKE is NULL
 * ippStsContextMatchErr     - if pEC no valid ID | pEC no exists SUBGROUP
 * ippStsNotSupportedModeErr - if pEC no supported (n|p) modulus arithmetic
 * ippStsRangeErr            - if BitSize(pEC) < IPP_SM3_DIGEST_BITSIZE
 * ippStsBadArgErr           - if role(pKE) no equal ippKESM2Requester|ippKESM2Responder
 * ippStsInvalidPoint        - if no in curve Pub Key (User A|B) | Eph Pub Key (User A|B)
 */
/* clang-format off */
IPPFUN(IppStatus, ippsGFpECKeyExchangeSM2_Setup, (const Ipp8u pZSelf[IPP_SM3_DIGEST_BYTESIZE],
                                                  const Ipp8u pZPeer[IPP_SM3_DIGEST_BYTESIZE],
                                                  const IppsGFpECPoint *pPublicKeySelf,
                                                  const IppsGFpECPoint *pPublicKeyPeer,
                                                  const IppsGFpECPoint *pEphPublicKeySelf,
                                                  const IppsGFpECPoint *pEphPublicKeyPeer,
                                                  IppsGFpECKeyExchangeSM2State *pKE))
/* clang-format on */
{
   /* check Key Exchange */
   IPP_BAD_PTR1_RET(pKE);
   /* check id */
   IPP_BADARG_RET(!EC_SM2_KEY_EXCH_VALID_ID(pKE), ippStsContextMatchErr);
   /* check User Role */
   const IppsKeyExchangeRoleSM2 role = EC_SM2_KEY_EXCH_ROLE(pKE);
   IPP_BADARG_RET(((ippKESM2Requester != role) && (ippKESM2Responder != role)), ippStsBadArgErr);

   /* check Za | Zb */
   IPP_BAD_PTR2_RET(pZSelf, pZPeer);

   /* check Elliptic Curve */
   IppsGFpECState *pEC = EC_SM2_KEY_EXCH_EC(pKE);

   IPP_BAD_PTR1_RET(pEC);
   IPP_BADARG_RET(!VALID_ECP_ID(pEC), ippStsContextMatchErr);
   IPP_BADARG_RET(!ECP_SUBGROUP(pEC), ippStsContextMatchErr);

   gsModEngine *pME = GFP_PMA(ECP_GFP(pEC)); /* base P */
   IPP_BADARG_RET(1 < GFP_EXTDEGREE(pME), ippStsNotSupportedModeErr);
   gsModEngine *nME = ECP_MONT_R(pEC); /* base N */
   IPP_BADARG_RET(1 < GFP_EXTDEGREE(nME), ippStsNotSupportedModeErr);
   /* check bitsize >= SM3_DIGSET */
   IPP_BADARG_RET(!(ECP_ORDBITSIZE(pEC) >= IPP_SM3_DIGEST_BITSIZE), ippStsRangeErr);

   /* check data User A */
   /* public and ephemeral public key */
   CHECK_PUBLIC_KEY(pPublicKeySelf)
   CHECK_PUBLIC_KEY(pEphPublicKeySelf)

   /* check data User B */
   /* public and ephemeral public key */
   CHECK_PUBLIC_KEY(pPublicKeyPeer)
   CHECK_PUBLIC_KEY(pEphPublicKeyPeer)

   {
      const int elemSize = GFP_FELEN(pME); /* size BNU_CHUNK */

      /* init pointer copy User A|B */
      Ipp8u *pCopyZSelf = (ippKESM2Requester == role) ? EC_SM2_KEY_EXCH_USER_ID_HASH_USER_A(pKE) : EC_SM2_KEY_EXCH_USER_ID_HASH_USER_B(pKE);
      Ipp8u *pCopyZPeer = (ippKESM2Requester == role) ? EC_SM2_KEY_EXCH_USER_ID_HASH_USER_B(pKE) : EC_SM2_KEY_EXCH_USER_ID_HASH_USER_A(pKE);
      /* copy Za and Zb */
      cpSM2_CopyBlock(pCopyZSelf, pZSelf, IPP_SM3_DIGEST_BYTESIZE);
      cpSM2_CopyBlock(pCopyZPeer, pZPeer, IPP_SM3_DIGEST_BYTESIZE);

      /* init ptr pointer */
      /* User A */
      const IppsGFpECPoint *loc_pub_key_user_a     = (ippKESM2Requester == role) ? pPublicKeySelf : pPublicKeyPeer;
      const IppsGFpECPoint *loc_eph_pub_key_user_a = (ippKESM2Requester == role) ? pEphPublicKeySelf : pEphPublicKeyPeer;
      /* User B */
      const IppsGFpECPoint *loc_pub_key_user_b     = (ippKESM2Requester == role) ? pPublicKeyPeer : pPublicKeySelf;
      const IppsGFpECPoint *loc_eph_pub_key_user_b = (ippKESM2Requester == role) ? pEphPublicKeyPeer : pEphPublicKeySelf;
      /* Copy Pointer User A|B */
      const Ipp32s size_struct_point = (Ipp32s)sizeof(IppsGFpECPoint);
      const Ipp32s size_data_point   = 3 * (Ipp32s)sizeof(BNU_CHUNK_T) * elemSize;

      Ipp8u *ptr = (Ipp8u *)(pKE) + sizeof(IppsGFpECKeyExchangeSM2State);

      /* Public key User A */
      EC_SM2_KEY_EXCH_PUB_KEY_USER_A(pKE) = (IppsGFpECPoint *)ptr;
      ptr += size_struct_point;
      cpSM2KE_CopyPointData(EC_SM2_KEY_EXCH_PUB_KEY_USER_A(pKE), (BNU_CHUNK_T *)ptr, loc_pub_key_user_a, pEC);
      ptr += size_data_point;
      /* Ephemeral Public Key User A */
      EC_SM2_KEY_EXCH_EPH_PUB_KEY_USER_A(pKE) = (IppsGFpECPoint *)ptr;
      ptr += size_struct_point;
      cpSM2KE_CopyPointData(EC_SM2_KEY_EXCH_EPH_PUB_KEY_USER_A(pKE), (BNU_CHUNK_T *)ptr, loc_eph_pub_key_user_a, pEC);
      ptr += size_data_point;
      /* Public key User B */
      EC_SM2_KEY_EXCH_PUB_KEY_USER_B(pKE) = (IppsGFpECPoint *)ptr;
      ptr += size_struct_point;
      cpSM2KE_CopyPointData(EC_SM2_KEY_EXCH_PUB_KEY_USER_B(pKE), (BNU_CHUNK_T *)ptr, loc_pub_key_user_b, pEC);
      ptr += size_data_point;
      /* Ephemeral Public Key User B */
      EC_SM2_KEY_EXCH_EPH_PUB_KEY_USER_B(pKE) = (IppsGFpECPoint *)ptr;
      ptr += size_struct_point;
      cpSM2KE_CopyPointData(EC_SM2_KEY_EXCH_EPH_PUB_KEY_USER_B(pKE), (BNU_CHUNK_T *)ptr, loc_eph_pub_key_user_b, pEC);

      return ippStsNoErr;
   }
}

#undef CHECK_PUBLIC_KEY
