/*--------------------------------------------------------------------------*/
/* Copyright 2020-2021 NXP                                                  */
/*                                                                          */
/* NXP Confidential. This software is owned or controlled by NXP and may    */
/* only be used strictly in accordance with the applicable license terms.   */
/* By expressly accepting such terms or by downloading, installing,         */
/* activating and/or otherwise using the software, you are agreeing that    */
/* you have read, and that you agree to comply with and are bound by, such  */
/* license terms. If you do not agree to be bound by the applicable license */
/* terms, then you may not retain, install, activate or otherwise use the   */
/* software.                                                                */
/*--------------------------------------------------------------------------*/

/** @file  mcuxClRsa_PrivateCrt.c
 *  @brief mcuxClRsa: implementation of RSA private CRT key operation
 */

#include <stdint.h>
#include <mcuxCsslFlowProtection.h>
#include <mcuxCsslParamIntegrity.h>
#include <mcuxCsslMemory_Compare.h>
#include <mcuxClMemory.h>
#include <mcuxClPkc.h>
#include <mcuxClMath.h>
#include <mcuxClCss_Rng.h>
#include <internal/mcuxClPkc_Operations.h>
#include <internal/mcuxClPkc_ImportExport.h>

#include <mcuxClRsa.h>

#include <internal/mcuxClRsa_Internal_PkcDefs.h>
#include <internal/mcuxClRsa_Internal_Functions.h>
#include <internal/mcuxClRsa_Internal_Types.h>
#include <internal/mcuxClRsa_Internal_Macros.h>
#include <internal/mcuxClRsa_Internal_MemoryConsumption.h>

MCUX_CSSL_FP_FUNCTION_DEF(mcuxClRsa_privateCRT)
mcuxClRsa_Status_Protected_t mcuxClRsa_privateCRT(
  mcuxClSession_Handle_t      pSession,
  const mcuxClRsa_Key * const pKey,
  const uint8_t * const      pInput,
  uint8_t * const            pOutput)
{
  MCUX_CSSL_FP_FUNCTION_ENTRY(mcuxClRsa_privateCRT);

  if((MCUXCLRSA_KEY_PRIVATECRT != pKey->keytype) && (MCUXCLRSA_KEY_PRIVATECRT_DFA != pKey->keytype))
  {
    MCUX_CSSL_FP_FUNCTION_EXIT(mcuxClRsa_privateCRT, MCUXCLRSA_STATUS_INVALID_INPUT);
  }

  if(NULL == pKey->pMod1->pKeyEntryData)
  {
    MCUX_CSSL_FP_FUNCTION_EXIT(mcuxClRsa_privateCRT, MCUXCLRSA_STATUS_INVALID_INPUT);
  }

  if(NULL == pKey->pMod2->pKeyEntryData)
  {
    MCUX_CSSL_FP_FUNCTION_EXIT(mcuxClRsa_privateCRT, MCUXCLRSA_STATUS_INVALID_INPUT);
  }

  if(NULL == pKey->pQInv->pKeyEntryData)
  {
    MCUX_CSSL_FP_FUNCTION_EXIT(mcuxClRsa_privateCRT, MCUXCLRSA_STATUS_INVALID_INPUT);
  }

  if(NULL == pKey->pExp1->pKeyEntryData)
  {
    MCUX_CSSL_FP_FUNCTION_EXIT(mcuxClRsa_privateCRT, MCUXCLRSA_STATUS_INVALID_INPUT);
  }

  if(NULL == pKey->pExp2->pKeyEntryData)
  {
    MCUX_CSSL_FP_FUNCTION_EXIT(mcuxClRsa_privateCRT, MCUXCLRSA_STATUS_INVALID_INPUT);
  }

  if((MCUXCLRSA_KEY_PRIVATECRT_DFA == pKey->keytype) && (NULL == pKey->pExp3->pKeyEntryData))
  {
    MCUX_CSSL_FP_FUNCTION_EXIT(mcuxClRsa_privateCRT, MCUXCLRSA_STATUS_INVALID_INPUT);
  }

  uint32_t keyBitLength;
  MCUXCLRSA_CALC_MODLEN_FROM_CRTKEY(pKey, keyBitLength);
  const uint32_t exactModByteLength = keyBitLength / 8u;

  if((exactModByteLength < 64U) || (exactModByteLength > 512U) )
  {
    MCUX_CSSL_FP_FUNCTION_EXIT(mcuxClRsa_privateCRT, MCUXCLRSA_STATUS_INVALID_INPUT);
  }

  const uint32_t byteLenPQ = pKey->pMod1->keyEntryLength;
  const uint32_t byteLenQInv = pKey->pQInv->keyEntryLength;
  const uint32_t byteLenCeilN = 2u * byteLenPQ;
  const uint32_t blindLen = MCUXCLRSA_PRIVATECRT_BLINDING_SIZE;
  const uint32_t blindAlignLen = MCUXCLPKC_ROUNDUP_SIZE(blindLen);
  const uint32_t blindSquaredAlignLen = MCUXCLPKC_ROUNDUP_SIZE(blindLen * 2u);
  const uint32_t primeAlignLen = MCUXCLPKC_ROUNDUP_SIZE(byteLenPQ);
  const uint32_t modAlignLen = MCUXCLPKC_ROUNDUP_SIZE(byteLenCeilN);
  const uint32_t qInvAlignLen = MCUXCLPKC_ROUNDUP_SIZE(byteLenQInv);
  const uint32_t blindedPrimeAlignLen = primeAlignLen + blindAlignLen;
  const uint32_t blindedMessageAlignLen = modAlignLen + blindAlignLen;
  const uint32_t blindedModAlignLen = 2u * blindedPrimeAlignLen;

  const uint16_t bufferSizeInput = (uint16_t)modAlignLen;
  const uint16_t bufferSizePrimeRand = (uint16_t)blindAlignLen;
  const uint16_t bufferSizePrimePQb = (uint16_t)blindedPrimeAlignLen + MCUXCLPKC_WORDSIZE;
  const uint16_t bufferSizePrimeT0 = (uint16_t)blindedPrimeAlignLen + MCUXCLPKC_WORDSIZE;
  const uint16_t bufferSizePrimeT1 = (uint16_t)blindedPrimeAlignLen + MCUXCLPKC_WORDSIZE;
  const uint16_t bufferSizePrimeT2 = (uint16_t)blindedPrimeAlignLen + MCUXCLPKC_WORDSIZE;
  const uint16_t bufferSizePrimeT3 = (uint16_t)blindedPrimeAlignLen + MCUXCLPKC_WORDSIZE;
  const uint16_t bufferSizePrimeT4 = (uint16_t)blindedPrimeAlignLen + MCUXCLPKC_WORDSIZE;
  const uint16_t bufferSizePrimeTE = (uint16_t)5u*MCUXCLPKC_WORDSIZE;
  const uint16_t bufferSizePrimeR = (uint16_t)blindedPrimeAlignLen + MCUXCLPKC_WORDSIZE;
  const uint16_t bufferSizePrimeT5 =  (uint16_t)blindedPrimeAlignLen;
  const uint16_t bufferSizeModM = (uint16_t)modAlignLen;
  const uint16_t bufferSizeModT1 = (uint16_t)modAlignLen + MCUXCLPKC_WORDSIZE;
  const uint16_t bufferSizeModT2 = (uint16_t)modAlignLen + MCUXCLPKC_WORDSIZE;
  const uint16_t bufferSizeModT3 = (uint16_t)blindedMessageAlignLen;
  const uint16_t bufferSizeModT4 = (uint16_t)blindedModAlignLen;
  const uint16_t bufferSizeModN = (uint16_t)blindedModAlignLen + MCUXCLPKC_WORDSIZE;

  uint8_t *pPkcWorkarea = (uint8_t *) (& pSession->pkcWa.buffer[pSession->pkcWa.used]);
  const uint16_t bufferSizeTotal = bufferSizeInput + bufferSizePrimeRand +
          MCUXCLRSA_MAX(bufferSizePrimePQb + bufferSizePrimeT0 + bufferSizePrimeT1 + bufferSizePrimeT2 + bufferSizePrimeT3 + bufferSizePrimeT4 + bufferSizePrimeTE + bufferSizePrimeR + bufferSizePrimeT5,
                       bufferSizeModM + bufferSizeModT1 + bufferSizeModT2 + bufferSizeModT3 + bufferSizeModT4 + bufferSizeModN);

  uint8_t *pC = pPkcWorkarea;
  uint8_t *pBlind32 = pC + bufferSizeInput;

  uint8_t *pPQ_b = pBlind32 + bufferSizePrimeRand + MCUXCLPKC_WORDSIZE;
  uint8_t *pPrimeT0 = pPQ_b + bufferSizePrimePQb - MCUXCLPKC_WORDSIZE;
  uint8_t *pPrimeT1 = pPrimeT0 + bufferSizePrimeT0;
  uint8_t *pPrimeT2 = pPrimeT1 + bufferSizePrimeT1;
  uint8_t *pPrimeT3 = pPrimeT2 + bufferSizePrimeT2;
  uint8_t *pPrimeT4 = pPrimeT3 + bufferSizePrimeT3;
  uint8_t *pPrimeTE = pPrimeT4 + bufferSizePrimeT4;
  uint8_t *pPrimeR = pPrimeTE + bufferSizePrimeTE;
  uint8_t *pPrimeT5 = pPrimeR + bufferSizePrimeR;

  uint8_t *pM = pPQ_b - MCUXCLPKC_WORDSIZE;
  uint8_t *pModT1 = pM + bufferSizeModM;
  uint8_t *pModT2 = pModT1 + bufferSizeModT1;
  uint8_t *pModT3 = pModT2 + bufferSizeModT2;
  uint8_t *pModT4 = pModT3 + bufferSizeModT3;
  uint8_t *pN = pModT4 + bufferSizeModT4 + MCUXCLPKC_WORDSIZE;

  uint16_t * pOperands = (uint16_t *)&pSession->cpuWa.buffer[pSession->cpuWa.used];
  pOperands[MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_INPUT]   = MCUXCLPKC_PTR2OFFSET(pPkcWorkarea);
  pOperands[MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_RAND]    = MCUXCLPKC_PTR2OFFSET(pBlind32);
  pOperands[MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B]    = MCUXCLPKC_PTR2OFFSET(pPQ_b);
  pOperands[MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0] = MCUXCLPKC_PTR2OFFSET(pPrimeT0);
  pOperands[MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET1] = MCUXCLPKC_PTR2OFFSET(pPrimeT1);
  pOperands[MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET2] = MCUXCLPKC_PTR2OFFSET(pPrimeT2);
  pOperands[MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET3] = MCUXCLPKC_PTR2OFFSET(pPrimeT3);
  pOperands[MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET4] = MCUXCLPKC_PTR2OFFSET(pPrimeT4);
  pOperands[MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_TE]      = MCUXCLPKC_PTR2OFFSET(pPrimeTE);
  pOperands[MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_R]       = MCUXCLPKC_PTR2OFFSET(pPrimeR);
  pOperands[MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET5] = MCUXCLPKC_PTR2OFFSET(pPrimeT5);
  pOperands[MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_M]       = MCUXCLPKC_PTR2OFFSET(pM);
  pOperands[MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT1]   = MCUXCLPKC_PTR2OFFSET(pModT1);
  pOperands[MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT2]   = MCUXCLPKC_PTR2OFFSET(pModT2);
  pOperands[MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT3]   = MCUXCLPKC_PTR2OFFSET(pModT3);
  pOperands[MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT4]   = MCUXCLPKC_PTR2OFFSET(pModT4);
  pOperands[MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N]       = MCUXCLPKC_PTR2OFFSET(pN);

  MCUXCLPKC_SETUPTRT(pOperands);

  MCUX_CSSL_FP_FUNCTION_CALL(memset_result, mcuxClMemory_set(pC + exactModByteLength, 0x00U, bufferSizeTotal - exactModByteLength, bufferSizeTotal - exactModByteLength));
  if(0u != memset_result)
  {
    MCUX_CSSL_FP_FUNCTION_EXIT(mcuxClRsa_privateCRT, MCUXCLRSA_STATUS_ERROR);
  }

  uint32_t * pExpTemp = (uint32_t *)pOperands + MCUXCLRSA_ROUND_UP_TO_CPU_WORDSIZE((MCUXCLRSA_INTERNAL_PRIVCRT_UPTRT_SIZE * sizeof(uint16_t)))/sizeof(uint32_t);

  MCUXCLPKC_PS1_SETLENGTH(0u, primeAlignLen);
  MCUXCLPKC_FP_SECUREIMPORTBIGENDIANTOPKC(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0, pKey->pMod2->pKeyEntryData, MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET4 , byteLenPQ);

  MCUX_CSSL_FP_FUNCTION_CALL(ret_PRNG_GetRandom1, mcuxClCss_Prng_GetRandom(pBlind32, blindLen));
  if (MCUXCLCSS_STATUS_OK != ret_PRNG_GetRandom1)
  {
      MCUX_CSSL_FP_FUNCTION_EXIT(mcuxClRsa_privateCRT, MCUXCLRSA_STATUS_ERROR);
  }
  pBlind32[0] |= 0x1u;

  MCUXCLPKC_FP_CALC_OP1_MUL(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_RAND , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0 );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLMATH_FP_NDASH(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET4 );

  MCUXCLPKC_PS1_SETLENGTH(modAlignLen, blindedPrimeAlignLen);
  MCUXCLPKC_FP_CALC_MC1_MR(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET2 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_INPUT , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_PS1_SETLENGTH(blindedPrimeAlignLen, blindedPrimeAlignLen);
  MCUXCLMATH_FP_SHIFTMODULUS(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET5 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B );
  MCUXCLMATH_FP_QDASH(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_R , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET5 ,
      MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET4 , (uint16_t)(modAlignLen + blindedPrimeAlignLen));

  MCUXCLPKC_FP_CALC_MC1_MM(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET2 ,
      MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_R , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B );

  MCUXCLMATH_FP_SECMODEXP(pKey->pExp2->pKeyEntryData,
                         pExpTemp,
                         pKey->pExp2->keyEntryLength,
                         MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_R,
                         MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0,
                         MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B,
                         MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_TE,
                         MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET4,
                         MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET2,
                         MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET5,
                         MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET1);

  MCUXCLPKC_FP_CALC_MC1_MR(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET3 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_R ,
      MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_PS1_SETLENGTH(0u, (uint32_t) bufferSizePrimeT0);
  MCUXCLPKC_FP_CALC_OP1_CONST(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0, 0u);
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_PS1_SETLENGTH(0u, primeAlignLen);
  MCUXCLPKC_FP_SECUREIMPORTBIGENDIANTOPKC(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0, pKey->pMod1->pKeyEntryData, MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET4 , byteLenPQ);

  MCUX_CSSL_FP_FUNCTION_CALL(ret_PRNG_GetRandom2, mcuxClCss_Prng_GetRandom(pBlind32, blindLen));
  if (MCUXCLCSS_STATUS_OK != ret_PRNG_GetRandom2)
  {
      MCUX_CSSL_FP_FUNCTION_EXIT(mcuxClRsa_privateCRT, MCUXCLRSA_STATUS_ERROR);
  }
  pBlind32[0] |= 0x1u;

  MCUXCLPKC_FP_CALC_OP1_MUL(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_RAND, MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0);
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLMATH_FP_NDASH(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET4 );

  MCUXCLPKC_PS1_SETLENGTH(modAlignLen, blindedPrimeAlignLen);
  MCUXCLPKC_FP_CALC_MC1_MR(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET2 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_INPUT , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_PS1_SETLENGTH(blindedPrimeAlignLen, blindedPrimeAlignLen);
  MCUXCLMATH_FP_SHIFTMODULUS(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET5 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B );
  MCUXCLMATH_FP_QDASH(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_R , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET5 ,
      MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET4 , (uint16_t)(modAlignLen + blindedPrimeAlignLen));

  MCUXCLPKC_FP_CALC_MC1_MM(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET2 ,
      MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_R , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLMATH_FP_SECMODEXP(pKey->pExp1->pKeyEntryData,
                         pExpTemp,
                         pKey->pExp1->keyEntryLength,
                         MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_R,
                         MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0,
                         MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B,
                         MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_TE,
                         MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET4,
                         MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET2,
                         MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET5,
                         MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET1);
  MCUXCLPKC_WAITFORFINISH();

  const uint32_t qDashAlignLen = MCUXCLRSA_MAX(blindedPrimeAlignLen, qInvAlignLen + MCUXCLPKC_WORDSIZE);
  MCUXCLMATH_FP_SHIFTMODULUS(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET5 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B );
  MCUXCLMATH_FP_QDASH(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET4 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET5 ,
      MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET2 , (uint16_t)qDashAlignLen);

  MCUXCLPKC_PS1_SETLENGTH(qDashAlignLen, blindedPrimeAlignLen);
  MCUXCLPKC_FP_CALC_MC1_MM(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET4 ,
      MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET3 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_PS1_SETLENGTH(blindedPrimeAlignLen, blindedPrimeAlignLen);
  MCUXCLPKC_FP_CALC_MC1_MS(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET5 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_R ,
      MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_PS1_SETLENGTH(0u, (uint32_t) bufferSizePrimeT0 + bufferSizePrimeT1 + bufferSizePrimeT2);
  MCUXCLPKC_FP_CALC_OP1_CONST(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0, 0u);

  MCUXCLPKC_PS1_SETLENGTH(0u, qInvAlignLen);
  MCUXCLPKC_FP_SECUREIMPORTBIGENDIANTOPKC(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0, pKey->pQInv->pKeyEntryData, MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET2 , byteLenQInv);

  MCUX_CSSL_FP_FUNCTION_CALL(ret_PRNG_GetRandom3, mcuxClCss_Prng_GetRandom(pPrimeT1, qInvAlignLen));
  if (MCUXCLCSS_STATUS_OK != ret_PRNG_GetRandom3)
  {
      MCUX_CSSL_FP_FUNCTION_EXIT(mcuxClRsa_privateCRT, MCUXCLRSA_STATUS_ERROR);
  }

  MCUXCLPKC_PS1_SETLENGTH(0u, qInvAlignLen + MCUXCLPKC_WORDSIZE );
  MCUXCLPKC_FP_CALC_OP1_ADD(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET2 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET1 );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_PS1_SETLENGTH(qDashAlignLen, blindedPrimeAlignLen);
  MCUXCLPKC_FP_CALC_MC1_MM(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET4 ,
      MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET2 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_FP_CALC_MC1_MM(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET2 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET4 ,
      MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET1 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_PS1_SETLENGTH(blindedPrimeAlignLen, blindedPrimeAlignLen);
  MCUXCLPKC_FP_CALC_MC1_MS(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET1 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0 ,
      MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET2 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_FP_CALC_MC1_MM(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET5 ,
      MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET1 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_FP_CALC_MC1_MR(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET1 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0  , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_PS1_SETLENGTH(0u, (uint32_t) bufferSizePrimeT0);
  MCUXCLPKC_FP_CALC_OP1_CONST(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0, 0u);
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_PS1_SETLENGTH(0u, primeAlignLen);
  MCUXCLPKC_FP_SECUREIMPORTBIGENDIANTOPKC(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0, pKey->pMod2->pKeyEntryData, MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET4 , byteLenPQ);

  MCUXCLPKC_PS1_SETLENGTH(primeAlignLen, blindedPrimeAlignLen);
  MCUXCLPKC_FP_CALC_MC1_PM(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT4 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET1 );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_PS1_SETLENGTH(0u, blindedMessageAlignLen);
  MCUXCLPKC_FP_CALC_OP1_CONST(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N, 0u);
  MCUXCLPKC_WAITFORFINISH();
  MCUXCLPKC_PS1_SETLENGTH(0u, blindedPrimeAlignLen);
  MCUXCLPKC_FP_CALC_OP1_OR_CONST(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET3  , 0u);

  MCUXCLPKC_PS1_SETLENGTH(0u, blindedMessageAlignLen);
  MCUXCLPKC_FP_CALC_OP1_ADD(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT3 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT4 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_PS1_SETLENGTH(0u, primeAlignLen);
  MCUXCLPKC_FP_CALC_OP1_MUL(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET1 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_RAND , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0 );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_PS1_SETLENGTH(blindedPrimeAlignLen, blindedPrimeAlignLen);
  MCUXCLPKC_FP_CALC_MC1_PM(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT4 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PQ_B , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET1 );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_PS1_SETLENGTH(0u, blindAlignLen);
  MCUXCLPKC_FP_CALC_OP1_MUL(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_RAND , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_RAND );

  MCUXCLMATH_FP_EXACTDIVIDEODD(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N ,
                              MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT4 ,
                              MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET0 ,
                              MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_PRIMET1 ,
                              blindedModAlignLen,
                              blindSquaredAlignLen );

  if(0U == (pN[0u] & 0x01U))
  {
      MCUX_CSSL_FP_FUNCTION_EXIT(mcuxClRsa_privateCRT, MCUXCLRSA_STATUS_INVALID_INPUT,
          MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMemory_set),
          4u * MCUXCLPKC_FP_CALLED_CALC_OP1_CONST,
          4u * MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClPkc_SecureImportBigEndianToPkc),
          3u * MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClCss_Prng_GetRandom),
          4u * MCUXCLPKC_FP_CALLED_CALC_OP1_MUL,
          2u * MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMath_NDash),
          4u * MCUXCLPKC_FP_CALLED_CALC_MC1_MR,
          3u * MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMath_ShiftModulus),
          3u * MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMath_QDash),
          6u * MCUXCLPKC_FP_CALLED_CALC_MC1_MM,
          2u * MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMath_SecModExp),
          2u * MCUXCLPKC_FP_CALLED_CALC_MC1_MS,
          2u * MCUXCLPKC_FP_CALLED_CALC_OP1_ADD,
          2u * MCUXCLPKC_FP_CALLED_CALC_MC1_PM,
          MCUXCLPKC_FP_CALLED_CALC_OP1_OR_CONST,
          MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMath_ExactDivideOdd));
  }

  MCUXCLPKC_PS1_SETLENGTH(0u, modAlignLen);
  MCUXCLPKC_FP_CALC_OP1_CMP(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_INPUT , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N );
  MCUXCLPKC_WAITFORFINISH();

  uint32_t carryFlag = MCUXCLPKC_GETCARRY();

  if(MCUXCLPKC_FLAG_CARRY != carryFlag)
  {

    MCUXCLPKC_PS1_SETLENGTH(0u, bufferSizeTotal);
    MCUXCLPKC_FP_CALC_OP1_CONST(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_INPUT, 0u);

    MCUX_CSSL_FP_FUNCTION_EXIT(mcuxClRsa_privateCRT, MCUXCLRSA_STATUS_INVALID_INPUT,
        MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMemory_set),
        5u * MCUXCLPKC_FP_CALLED_CALC_OP1_CONST,
        4u * MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClPkc_SecureImportBigEndianToPkc),
        3u * MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClCss_Prng_GetRandom),
        4u * MCUXCLPKC_FP_CALLED_CALC_OP1_MUL,
        2u * MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMath_NDash),
        4u * MCUXCLPKC_FP_CALLED_CALC_MC1_MR,
        3u * MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMath_ShiftModulus),
        3u * MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMath_QDash),
        6u * MCUXCLPKC_FP_CALLED_CALC_MC1_MM,
        2u * MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMath_SecModExp),
        2u * MCUXCLPKC_FP_CALLED_CALC_MC1_MS,
        2u * MCUXCLPKC_FP_CALLED_CALC_OP1_ADD,
        2u * MCUXCLPKC_FP_CALLED_CALC_MC1_PM,
        MCUXCLPKC_FP_CALLED_CALC_OP1_OR_CONST,
        MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMath_ExactDivideOdd),
        MCUXCLPKC_FP_CALLED_CALC_OP1_CMP);

  }

  MCUXCLMATH_FP_NDASH(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT2 );

  MCUXCLPKC_PS1_SETLENGTH(modAlignLen, modAlignLen);
  MCUXCLMATH_FP_SHIFTMODULUS(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT2 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N );
  MCUXCLMATH_FP_QDASH(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT1  , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT2 ,
      MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT4 , (uint16_t)blindedMessageAlignLen );

  MCUXCLPKC_PS1_SETLENGTH(blindedMessageAlignLen , modAlignLen);
  MCUXCLPKC_FP_CALC_MC1_MR(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT4 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT3 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_PS1_SETLENGTH(modAlignLen, modAlignLen);
  MCUXCLPKC_FP_CALC_MC1_MM(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT3 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT1 ,
      MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT4 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N );
  MCUXCLPKC_WAITFORFINISH();

  MCUXCLPKC_FP_CALC_MC1_MS(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_M , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT3 ,
      MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N );
  MCUXCLPKC_WAITFORFINISH();

  if(MCUXCLRSA_KEY_PRIVATECRT != pKey->keytype)
  {

      MCUXCLMATH_FP_QSQUARED(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT1 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT2 ,
          MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT4 );

      MCUXCLPKC_FP_CALC_MC1_MM(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT2 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_M ,
          MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT1 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N );

      MCUXCLMATH_FP_MODEXP_SQRMULTL2R(pKey->pExp3->pKeyEntryData, pKey->pExp3->keyEntryLength, MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT1 ,
          MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT2 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT4 );

      MCUXCLPKC_FP_CALC_MC1_MR(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT4, MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT1,
          MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N );

      MCUXCLPKC_FP_CALC_MC1_MS(MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT2 , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_MODT4,
          MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N , MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_N );
      MCUXCLPKC_WAITFORFINISH();

      MCUX_CSSL_FP_FUNCTION_CALL(compare_result, mcuxCsslMemory_Compare(mcuxCsslParamIntegrity_Protect(3u, pC, pModT2, exactModByteLength),
                                                                      pC ,
                                                                      pModT2 ,
                                                                      exactModByteLength));

      if(MCUX_CSSL_FP_RESULT(compare_result) != MCUXCSSLMEMORY_COMPARE_EQUAL)
      {
          MCUX_CSSL_FP_FUNCTION_EXIT(mcuxClRsa_privateCRT, MCUXCLRSA_STATUS_ERROR);
      }

  }

  MCUXCLPKC_FP_EXPORTBIGENDIANFROMPKC(pOutput, MCUXCLRSA_INTERNAL_UPTRTINDEX_PRIVCRT_M , exactModByteLength);

  MCUX_CSSL_FP_FUNCTION_EXIT(mcuxClRsa_privateCRT, MCUXCLRSA_INTERNAL_STATUS_KEYOP_OK,
          MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMemory_set),
          4u * MCUXCLPKC_FP_CALLED_CALC_OP1_CONST,
          4u * MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClPkc_SecureImportBigEndianToPkc),
          3u * MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClCss_Prng_GetRandom),
          4u * MCUXCLPKC_FP_CALLED_CALC_OP1_MUL,
          3u * MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMath_NDash),
          5u * MCUXCLPKC_FP_CALLED_CALC_MC1_MR,
          4u * MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMath_ShiftModulus),
          4u * MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMath_QDash),
          7u * MCUXCLPKC_FP_CALLED_CALC_MC1_MM,
          2u * MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMath_SecModExp),
          3u * MCUXCLPKC_FP_CALLED_CALC_MC1_MS,
          2u * MCUXCLPKC_FP_CALLED_CALC_OP1_ADD,
          2u * MCUXCLPKC_FP_CALLED_CALC_MC1_PM,
          MCUXCLPKC_FP_CALLED_CALC_OP1_OR_CONST,
          MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMath_ExactDivideOdd),
          MCUXCLPKC_FP_CALLED_CALC_OP1_CMP,
          MCUX_CSSL_FP_CONDITIONAL((MCUXCLRSA_KEY_PRIVATECRT != pKey->keytype),
                MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMath_QSquared),
                MCUXCLPKC_FP_CALLED_CALC_MC1_MM,
                MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMath_ModExp_SqrMultL2R),
                MCUXCLPKC_FP_CALLED_CALC_MC1_MR,
                MCUXCLPKC_FP_CALLED_CALC_MC1_MS,
                MCUX_CSSL_FP_FUNCTION_CALLED(mcuxCsslMemory_Compare)),
        MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClPkc_ExportBigEndianFromPkc));
}
