/*!
* @file     Crypto_Impl.c
*
* @version  1.0.0
*
* @brief    AUTOSAR Crypto Driver implementations
 *           
* @attention
*
* Project Name      : AUTOSAR MCAL
*
* Platform          : Arm
*
* Autosar Standard  : Classic Platform 4.4.0
*
* Compiled Version  : G32A10xx_MCAL_1_0_0_15-Nov-25
*
* Copyright (C) 2026 Geehy Semiconductor
*
* You may not use this file except in compliance with the
* GEEHY COPYRIGHT NOTICE (Geehy Semiconductor Software License Agreement).
*
* The program is only for reference, which is distributed in the hope
* that it will be usefull and instructional for customers to develop
* their software. Unless required by applicable law or agreed to in
* writing, the program is distributed on an "AS IS" BASIS, WITHOUT
* ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
* See the Geehy Semiconductor Software License Agreement for the governing permissions
* and limitations under the License.
*/

/*
* MISRA-C:2012 compliance checks
*
*          
*     Crypto_Impl_c_MISRA_REF1
*          Breaks the required Rule 10.5 of MISRA 2012 guidelines, The value of an expression 
*          should not be cast to an inappropriate essential type.
*          
*     Crypto_Impl_c_MISRA_REF3
*          Breaks the required Rule 13.5 of MISRA 2012 guidelines, The right hand operand of a logical && or || 
*          operator shall not contain persistent side effects
*          
*     Crypto_Impl_c_MISRA_REF4
*          Breaks the required Rule 18.4 of MISRA 2012 guidelines,The +, -, += and -= operators should not be 
*          applied to an expression of pointer type
*          
*     Crypto_Impl_c_MISRA_REF5
*          Breaks the required Rule 14.2 of MISRA 2012 guidelines,A for loop shall be well-formed
*          
*     Crypto_Impl_c_MISRA_REF6
*          Breaks the required Rule 2.2 of MISRA 2012 guidelines,A project shall not contain dead code
*
*/

#ifdef __cplusplus
extern "C"{
#endif

#include "Crypto.h"
#include "Crypto_Impl.h"
#include "Crypto_Cse.h"
#include "Crypto_Cfg.h"
#include "Crypto_Types.h"
#include "Csm_Types.h"
#include "CryIf_Cbk.h"
#include "Crypto_Aes256.h"
#include "Crypto_Sha256.h"
#include "Crypto_ShaReg.h"
#include "Crypto_Rng.h"
#include "Crypto_RngReg.h"
/** @addtogroup G32A10xx_StdPeriphDriver
  @{
*/

/** @defgroup CRYPTO_Driver
  * @brief CRYPTO driver modules
  @{
*/

/** @defgroup CRYPTO_Macros
  @{
*/
/*******************************************************************************
                    VENDOR ID & AUTOSAR VERSION INFO
*******************************************************************************/
/** Vendor ID (vendorId) of the dedicated implementation of this module according to the AUTOSAR vendor list. */
#define CRYPTO_IMPL_VENDOR_ID                           0x00C1U
/** Major version number of AUTOSAR release on which the appropriate implementation is based on. */
#define CRYPTO_IMPL_AR_RELEASE_MAJOR_VERSION            0x04U
/** Minor version number of AUTOSAR release on which the appropriate implementation is based on. */
#define CRYPTO_IMPL_AR_RELEASE_MINOR_VERSION            0x04U
/** Revision version number of AUTOSAR release on which the appropriate implementation is based on. */
#define CRYPTO_IMPL_AR_RELEASE_REVISION_VERSION         0x00U
/** Major version number of the vendor specific implementation of the module. The numbering is vendor specific. */
#define CRYPTO_IMPL_SW_MAJOR_VERSION                    0x01U
/** Minor version number of the vendor specific implementation of the module. The numbering is vendor specific. */
#define CRYPTO_IMPL_SW_MINOR_VERSION                    0x00U
/** Patch level version number of the vendor specific implementation of the module. The numbering is vendor specific. */
#define CRYPTO_IMPL_SW_PATCH_VERSION                    0x00U

/*******************************************************************************
                             VERSION CHECK
*******************************************************************************/
/* Make sure the vendor id is the same between Crypto_Impl.c and Crypto.h */
#if (CRYPTO_IMPL_VENDOR_ID != CRYPTO_H_VENDOR_ID)
    #error "Vendor id mismatch between Crypto_Impl.c and Crypto.h"
#endif
/* Make sure the AUTOSAR major version number is the same between Crypto_Impl.c and Crypto.h */
#if (CRYPTO_IMPL_AR_RELEASE_MAJOR_VERSION != CRYPTO_H_AR_RELEASE_MAJOR_VERSION)
    #error "AUTOSAR major version number mismatch between Crypto_Impl.c and Crypto.h"
#endif
/* Make sure the AUTOSAR minor version number is the same between Crypto_Impl.c and Crypto.h */
#if (CRYPTO_IMPL_AR_RELEASE_MINOR_VERSION != CRYPTO_H_AR_RELEASE_MINOR_VERSION)
    #error "AUTOSAR minor version number mismatch between Crypto_Impl.c and Crypto.h"
#endif
/* Make sure the AUTOSAR revision version number is the same between Crypto_Impl.c and Crypto.h */
#if (CRYPTO_IMPL_AR_RELEASE_REVISION_VERSION != CRYPTO_H_AR_RELEASE_REVISION_VERSION)
    #error "AUTOSAR revision version number mismatch between Crypto_Impl.c and Crypto.h"
#endif
/* Make sure the software major version number is the same between Crypto_Impl.c and Crypto.h */
#if (CRYPTO_IMPL_SW_MAJOR_VERSION != CRYPTO_H_SW_MAJOR_VERSION)
    #error "Software major version number mismatch between Crypto_Impl.c and Crypto.h"
#endif
/* Make sure the software minor version number is the same between Crypto_Impl.c and Crypto.h */
#if (CRYPTO_IMPL_SW_MINOR_VERSION != CRYPTO_H_SW_MINOR_VERSION)
    #error "Software minor version number mismatch between Crypto_Impl.c and Crypto.h"
#endif
/* Make sure the software patch version number is the same between Crypto_Impl.c and Crypto.h */
#if (CRYPTO_IMPL_SW_PATCH_VERSION != CRYPTO_H_SW_PATCH_VERSION)
    #error "Software patch version number mismatch between Crypto_Impl.c and Crypto.h"
#endif
/* Make sure the vendor id is the same between Crypto_Impl.c and Crypto_Types.h */
#if (CRYPTO_IMPL_VENDOR_ID != CRYPTO_TYPES_H_VENDOR_ID)
    #error "Vendor id mismatch between Crypto_Impl.c and Crypto_Types.h"
#endif
/* Make sure the AUTOSAR major version number is the same between Crypto_Impl.c and Crypto_Types.h */
#if (CRYPTO_IMPL_AR_RELEASE_MAJOR_VERSION != CRYPTO_TYPES_H_AR_RELEASE_MAJOR_VERSION)
    #error "AUTOSAR major version number mismatch between Crypto_Impl.c and Crypto_Types.h"
#endif
/* Make sure the AUTOSAR minor version number is the same between Crypto_Impl.c and Crypto_Types.h */
#if (CRYPTO_IMPL_AR_RELEASE_MINOR_VERSION != CRYPTO_TYPES_H_AR_RELEASE_MINOR_VERSION)
    #error "AUTOSAR minor version number mismatch between Crypto_Impl.c and Crypto_Types.h"
#endif
/* Make sure the AUTOSAR revision version number is the same between Crypto_Impl.c and Crypto_Types.h */
#if (CRYPTO_IMPL_AR_RELEASE_REVISION_VERSION != CRYPTO_TYPES_H_AR_RELEASE_REVISION_VERSION)
    #error "AUTOSAR revision version number mismatch between Crypto_Impl.c and Crypto_Types.h"
#endif
/* Make sure the software major version number is the same between Crypto_Impl.c and Crypto_Types.h */
#if (CRYPTO_IMPL_SW_MAJOR_VERSION != CRYPTO_TYPES_H_SW_MAJOR_VERSION)
    #error "Software major version number mismatch between Crypto_Impl.c and Crypto_Types.h"
#endif
/* Make sure the software minor version number is the same between Crypto_Impl.c and Crypto_Types.h */
#if (CRYPTO_IMPL_SW_MINOR_VERSION != CRYPTO_TYPES_H_SW_MINOR_VERSION)
    #error "Software minor version number mismatch between Crypto_Impl.c and Crypto_Types.h"
#endif
/* Make sure the software patch version number is the same between Crypto_Impl.c and Crypto_Types.h */
#if (CRYPTO_IMPL_SW_PATCH_VERSION != CRYPTO_TYPES_H_SW_PATCH_VERSION)
    #error "Software patch version number mismatch between Crypto_Impl.c and Crypto_Types.h"
#endif
/* Make sure the vendor id is the same between Crypto_Impl.c and Crypto_Cse.h */
#if (CRYPTO_IMPL_VENDOR_ID != CRYPTO_CSE_H_VENDOR_ID)
    #error "Vendor id mismatch between Crypto_Impl.c and Crypto_Cse.h"
#endif
/* Make sure the AUTOSAR major version number is the same between Crypto_Impl.c and Crypto_Cse.h */
#if (CRYPTO_IMPL_AR_RELEASE_MAJOR_VERSION != CRYPTO_CSE_H_AR_RELEASE_MAJOR_VERSION)
    #error "AUTOSAR major version number mismatch between Crypto_Impl.c and Crypto_Cse.h"
#endif
/* Make sure the AUTOSAR minor version number is the same between Crypto_Impl.c and Crypto_Cse.h */
#if (CRYPTO_IMPL_AR_RELEASE_MINOR_VERSION != CRYPTO_CSE_H_AR_RELEASE_MINOR_VERSION)
    #error "AUTOSAR minor version number mismatch between Crypto_Impl.c and Crypto_Cse.h"
#endif
/* Make sure the AUTOSAR revision version number is the same between Crypto_Impl.c and Crypto_Cse.h */
#if (CRYPTO_IMPL_AR_RELEASE_REVISION_VERSION != CRYPTO_CSE_H_AR_RELEASE_REVISION_VERSION)
    #error "AUTOSAR revision version number mismatch between Crypto_Impl.c and Crypto_Cse.h"
#endif
/* Make sure the software major version number is the same between Crypto_Impl.c and Crypto_Cse.h */
#if (CRYPTO_IMPL_SW_MAJOR_VERSION != CRYPTO_CSE_H_SW_MAJOR_VERSION)
    #error "Software major version number mismatch between Crypto_Impl.c and Crypto_Cse.h"
#endif
/* Make sure the software minor version number is the same between Crypto_Impl.c and Crypto_Cse.h */
#if (CRYPTO_IMPL_SW_MINOR_VERSION != CRYPTO_CSE_H_SW_MINOR_VERSION)
    #error "Software minor version number mismatch between Crypto_Impl.c and Crypto_Cse.h"
#endif
/* Make sure the software patch version number is the same between Crypto_Impl.c and Crypto_Cse.h */
#if (CRYPTO_IMPL_SW_PATCH_VERSION != CRYPTO_CSE_H_SW_PATCH_VERSION)
    #error "Software patch version number mismatch between Crypto_Impl.c and Crypto_Cse.h"
#endif
/* Make sure the vendor id is the same between Crypto_Impl.c and Crypto_Impl.h */
#if (CRYPTO_IMPL_VENDOR_ID != CRYPTO_IMPL_H_VENDOR_ID)
    #error "Vendor id mismatch between Crypto_Impl.c and Crypto_Impl.h"
#endif
/* Make sure the AUTOSAR major version number is the same between Crypto_Impl.c and Crypto_Impl.h */
#if (CRYPTO_IMPL_AR_RELEASE_MAJOR_VERSION != CRYPTO_IMPL_H_AR_RELEASE_MAJOR_VERSION)
    #error "AUTOSAR major version number mismatch between Crypto_Impl.c and Crypto_Impl.h"
#endif
/* Make sure the AUTOSAR minor version number is the same between Crypto_Impl.c and Crypto_Impl.h */
#if (CRYPTO_IMPL_AR_RELEASE_MINOR_VERSION != CRYPTO_IMPL_H_AR_RELEASE_MINOR_VERSION)
    #error "AUTOSAR minor version number mismatch between Crypto_Impl.c and Crypto_Impl.h"
#endif
/* Make sure the AUTOSAR revision version number is the same between Crypto_Impl.c and Crypto_Impl.h */
#if (CRYPTO_IMPL_AR_RELEASE_REVISION_VERSION != CRYPTO_IMPL_H_AR_RELEASE_REVISION_VERSION)
    #error "AUTOSAR revision version number mismatch between Crypto_Impl.c and Crypto_Impl.h"
#endif
/* Make sure the software major version number is the same between Crypto_Impl.c and Crypto_Impl.h */
#if (CRYPTO_IMPL_SW_MAJOR_VERSION != CRYPTO_IMPL_H_SW_MAJOR_VERSION)
    #error "Software major version number mismatch between Crypto_Impl.c and Crypto_Impl.h"
#endif
/* Make sure the software minor version number is the same between Crypto_Impl.c and Crypto_Impl.h */
#if (CRYPTO_IMPL_SW_MINOR_VERSION != CRYPTO_IMPL_H_SW_MINOR_VERSION)
    #error "Software minor version number mismatch between Crypto_Impl.c and Crypto_Impl.h"
#endif
/* Make sure the software patch version number is the same between Crypto_Impl.c and Crypto_Impl.h */
#if (CRYPTO_IMPL_SW_PATCH_VERSION != CRYPTO_IMPL_H_SW_PATCH_VERSION)
    #error "Software patch version number mismatch between Crypto_Impl.c and Crypto_Impl.h"
#endif
/* Make sure the vendor id is the same between Crypto_Impl.c and Crypto_Cfg.h */
#if (CRYPTO_IMPL_VENDOR_ID != CRYPTO_CFG_H_VENDOR_ID)
    #error "Vendor id mismatch between Crypto_Impl.c and Crypto_Cfg.h"
#endif
/* Make sure the AUTOSAR major version number is the same between Crypto_Impl.c and Crypto_Cfg.h */
#if (CRYPTO_IMPL_AR_RELEASE_MAJOR_VERSION != CRYPTO_CFG_H_AR_RELEASE_MAJOR_VERSION)
    #error "AUTOSAR major version number mismatch between Crypto_Impl.c and Crypto_Cfg.h"
#endif
/* Make sure the AUTOSAR minor version number is the same between Crypto_Impl.c and Crypto_Cfg.h */
#if (CRYPTO_IMPL_AR_RELEASE_MINOR_VERSION != CRYPTO_CFG_H_AR_RELEASE_MINOR_VERSION)
    #error "AUTOSAR minor version number mismatch between Crypto_Impl.c and Crypto_Cfg.h"
#endif
/* Make sure the AUTOSAR revision version number is the same between Crypto_Impl.c and Crypto_Cfg.h */
#if (CRYPTO_IMPL_AR_RELEASE_REVISION_VERSION != CRYPTO_CFG_H_AR_RELEASE_REVISION_VERSION)
    #error "AUTOSAR revision version number mismatch between Crypto_Impl.c and Crypto_Cfg.h"
#endif
/* Make sure the software major version number is the same between Crypto_Impl.c and Crypto_Cfg.h */
#if (CRYPTO_IMPL_SW_MAJOR_VERSION != CRYPTO_CFG_H_SW_MAJOR_VERSION)
    #error "Software major version number mismatch between Crypto_Impl.c and Crypto_Cfg.h"
#endif
/* Make sure the software minor version number is the same between Crypto_Impl.c and Crypto_Cfg.h */
#if (CRYPTO_IMPL_SW_MINOR_VERSION != CRYPTO_CFG_H_SW_MINOR_VERSION)
    #error "Software minor version number mismatch between Crypto_Impl.c and Crypto_Cfg.h"
#endif
/* Make sure the software patch version number is the same between Crypto_Impl.c and Crypto_Cfg.h */
#if (CRYPTO_IMPL_SW_PATCH_VERSION != CRYPTO_CFG_H_SW_PATCH_VERSION)
    #error "Software patch version number mismatch between Crypto_Impl.c and Crypto_Cfg.h"
#endif

#ifdef ENABLE_INTERMODULE_MCAL_CHECK
    /* Make sure the AUTOSAR major version number is the same between Crypto_Impl.c and Csm_Types.h */
    #if (CRYPTO_IMPL_AR_RELEASE_MAJOR_VERSION != CSM_TYPES_H_AR_RELEASE_MAJOR_VERSION)
        #error "AUTOSAR major version number mismatch between Crypto_Impl.c and Csm_Types.h"
    #endif
    /* Make sure the AUTOSAR minor version number is the same between Crypto_Impl.c and Csm_Types.h */
    #if (CRYPTO_IMPL_AR_RELEASE_MINOR_VERSION != CSM_TYPES_H_AR_RELEASE_MINOR_VERSION)
        #error "AUTOSAR minor version number mismatch between Crypto_Impl.c and Csm_Types.h"
    #endif
    /* Make sure the AUTOSAR major version number is the same between Crypto_Impl.C and CryIf_Cbk.h */
    #if (CRYPTO_IMPL_AR_RELEASE_MAJOR_VERSION != CRYIF_CBK_H_AR_RELEASE_MAJOR_VERSION)
        #error "AUTOSAR major version number mismatch between Crypto_Impl.c and CryIf_Cbk.h"
    #endif
    /* Make sure the AUTOSAR minor version number is the same between Crypto_Impl.C and CryIf_Cbk.h */
    #if (CRYPTO_IMPL_AR_RELEASE_MINOR_VERSION != CRYIF_CBK_H_AR_RELEASE_MINOR_VERSION)
        #error "AUTOSAR minor version number mismatch between Crypto_Impl.c and CryIf_Cbk.h"
    #endif
#endif

/*******************************************************************************
                            LOCAL DEFINES AND MACROS
*******************************************************************************/

/* Mark the end of the job queue */
#define CRYPTO_JOB_QUEUE_NULL_ITEM   ((uint32)0xFFFFFFFFU)
/**@} end of group CRYPTO_Macros */
/*******************************************************************************
                            LOCAL VARIABLES
*******************************************************************************/
/** @defgroup CRYPTO_Variable
  @{
*/
#define CRYPTO_START_SEC_VAR_NO_INIT_UNSPECIFIED
#include "Crypto_MemMap.h"

/** Pointer to the current job */
static P2CONST(Crypto_JobType, AUTOMATIC, CRYPTO_APPL_DATA) g_cryptoCurrentJobPtr;

/** State of the current job */
static VAR(Crypto_JobStateType, CRYPTO_VAR) g_cryptoCurrentJobState;

/**
* Indicates if the CSE hardware module is initialized or not.
* CSE_HW_UNINIT: The CSE hardware module was not initialized.
* CSE_HW_READY:  The CSE hardware module was initialized.
*/
VAR(Crypto_Cse_HardwareStateType, CRYPTO_VAR) g_cseHardwareState;

#define CRYPTO_STOP_SEC_VAR_NO_INIT_UNSPECIFIED
#include "Crypto_MemMap.h"
/**@} end of group CRYPTO_Variable */
/*******************************************************************************
                        LOCAL FUNCTION DECLARATIONS
*******************************************************************************/
/** @defgroup CRYPTO_Fuctions
  @{
*/
#define CRYPTO_START_SEC_CODE
#include "Crypto_MemMap.h"

LOCAL_INLINE FUNC(Std_ReturnType, CRYPTO_CODE) Crypto_Impl_VerifyCseError(VAR(Crypto_Cse_ErrorCodeType, AUTOMATIC) ErrorCode);

LOCAL_INLINE FUNC(Std_ReturnType, CRYPTO_CODE) Crypto_Impl_ProcessOperation(P2CONST(Crypto_JobType, AUTOMATIC, CRYPTO_APPL_DATA) JobPtr);

LOCAL_INLINE FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Impl_MacGenerate(P2CONST(Crypto_JobType, AUTOMATIC, CRYPTO_APPL_CONST) JobPtr);

LOCAL_INLINE FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Impl_MacVerify(P2CONST(Crypto_JobType, AUTOMATIC, CRYPTO_APPL_CONST) JobPtr);

LOCAL_INLINE FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Impl_Encrypt(P2CONST(Crypto_JobType, AUTOMATIC, CRYPTO_APPL_CONST) JobPtr);

LOCAL_INLINE FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Impl_Decrypt(P2CONST(Crypto_JobType, AUTOMATIC, CRYPTO_APPL_CONST) JobPtr);

LOCAL_INLINE FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Impl_Generate(P2CONST(Crypto_JobType, AUTOMATIC, CRYPTO_APPL_CONST) JobPtr);

LOCAL_INLINE FUNC(boolean, CRYPTO_CODE) Crypto_Impl_QueueJob(VAR(uint32, AUTOMATIC) ObjectIndex,
    P2VAR(Crypto_JobType, AUTOMATIC,  CRYPTO_APPL_DATA) JobPtr);

LOCAL_INLINE FUNC(void, CRYPTO_CODE) Crypto_Impl_DequeueHeadJob(VAR(uint32, AUTOMATIC) ObjectIndex);

LOCAL_INLINE FUNC(Std_ReturnType, CRYPTO_CODE) Crypto_Impl_DequeueJob(VAR(uint32, AUTOMATIC) ObjectIndex,
    P2CONST(Crypto_JobInfoType, AUTOMATIC, CRYPTO_APPL_CONST) JobInfoPtr);

/*******************************************************************************
                            GLOBAL FUNCTIONS
*******************************************************************************/

/*!
* @brief    Initialize the Crypto Driver
*/
FUNC(Std_ReturnType, CRYPTO_CODE) Crypto_Impl_Init(void)
{
    VAR(Std_ReturnType, AUTOMATIC) result = (Std_ReturnType)E_NOT_OK;
    VAR(uint32, AUTOMATIC) clkStatus = 0x00U;
    VAR(uint32, AUTOMATIC) object = 0U;
    VAR(uint32, AUTOMATIC) element = 0U;
    /* Check if the hardware was properly configured */
    clkStatus = REG_READ32(RCM_AHBCLKEN_ADDR);
    if (RCM_AHBRNGSHAAES_CONFIGURED == (clkStatus & RCM_AHBRNGSHAAES_CONFIGURED))
    {
        result = (Std_ReturnType)E_OK;
        g_cseHardwareState = CSE_HW_READY;
    }
    else
    {
        /* nothing */
    }
    if ((Std_ReturnType)E_OK == result)
    {
        /* Initialize the internal job queues */
        while (object < (uint32)CRYPTO_DRIVER_OBJECT_NUMBER)
        {
            /* No job is queued currently, initialize the head of queued jobs to null. */
            Crypto_ListObject[object]->headOfQueuedJobs = CRYPTO_JOB_QUEUE_NULL_ITEM;

            if (0U != Crypto_ListObject[object]->queueSize)
            {
                /* Chain all elements into the queue of free jobs */
                Crypto_ListObject[object]->headOfFreeJobs = 0U;

                for (element = 0U; element < (Crypto_ListObject[object]->queueSize - 1U); element++)
                {
                    Crypto_ListObject[object]->queuedJobs[element].next = element + 1U;
                }

                /* The last element in the queue points to no other element */
                Crypto_ListObject[object]->queuedJobs[element].next = CRYPTO_JOB_QUEUE_NULL_ITEM;
            }
            else
            {
                /* If queue size is zero, mark the queue of free jobs as empty. */
                Crypto_ListObject[object]->headOfFreeJobs = CRYPTO_JOB_QUEUE_NULL_ITEM;
            }
            object++;
        }

        /* Crypto Driver was successfully initialized, change the driver state to idle. */
        g_cryptoCurrentJobState = CRYPTO_JOBSTATE_IDLE;
    }
    else
    {
        /* nothing */
    }
    return result;
}

/*!
* @brief        Processes a job
*
* @param[in]    ObjectIndex Index of the Crypto Driver Object
* @param[in]    JobPtr Pointer to the job to be processed
*
* @return       Std_ReturnType  Standard return type
*/
FUNC(Std_ReturnType, CRYPTO_CODE) Crypto_Impl_ProcessJob(
    VAR(uint32, AUTOMATIC) ObjectIndex,
    P2VAR(Crypto_JobType, AUTOMATIC, CRYPTO_APPL_DATA) JobPtr)
{
    VAR(Std_ReturnType, AUTOMATIC) result = (Std_ReturnType)E_OK;

    if (CRYPTO_OPERATIONMODE_SINGLECALL != JobPtr->jobPrimitiveInputOutput.mode)
    {
        result = (Std_ReturnType)E_NOT_OK;
    }
    else
    {
        /* If the job queue is not empty, which means there is already a job in progress.
        * The new asynchronous job shall be added to the queue.
        */
        if ((CRYPTO_PROCESSING_ASYNC == JobPtr->jobPrimitiveInfo->processingType) &&
            (CRYPTO_JOB_QUEUE_NULL_ITEM != Crypto_ListObject[ObjectIndex]->headOfQueuedJobs))
        {
            /* Try to queue the asynchronous job */
            result = ((boolean)TRUE == Crypto_Impl_QueueJob(ObjectIndex, JobPtr)) ?
                     (Std_ReturnType)E_OK : (Std_ReturnType)CRYPTO_E_QUEUE_FULL;
        }
        else
        {
            /* Synchronous jobs cannot be queued, try to process the job immediately.
            * Asynchronous jobs can also be processed immediately if the queue is empty.
            */
            result = Crypto_Impl_ProcessOperation(JobPtr);
            if ((Std_ReturnType)CRYPTO_E_BUSY == result)
            {
                /* Hardware is busy, try to queue the asynchronous job. */
                if ((CRYPTO_PROCESSING_ASYNC == JobPtr->jobPrimitiveInfo->processingType) &&
                    (0U != Crypto_ListObject[ObjectIndex]->queueSize))
                {
                    if (JobPtr->jobInfo->jobId != g_cryptoCurrentJobPtr->jobInfo->jobId)
                    {
                        /* Try to queue the asynchronous job */
                        result = ((boolean)TRUE == Crypto_Impl_QueueJob(ObjectIndex, JobPtr)) ?
                                 (Std_ReturnType)E_OK : (Std_ReturnType)CRYPTO_E_QUEUE_FULL;
                    }
                    else
                    {
                        /* If a job with the same job id is currently being processed,
                        * bypass it from queuing.
                        */
                        result = (Std_ReturnType)E_NOT_OK;
                    }
                }
                else
                {
                    /* nothing */
                }
            }
            else
            {
                /* nothing */
            }
        }
    }
    return result;
}

/*!
* @brief     Cancel a job in progress or in the queue
*
* @param[in] ObjectIndex Index of the Crypto Driver Object
* @param[in] JobPtr      Pointer to the job to be canceled
*
* @return    Std_ReturnType  Standard return type
*/
FUNC(Std_ReturnType, CRYPTO_CODE) Crypto_Impl_CancelJob(
    VAR(uint32, AUTOMATIC) ObjectIndex,
    P2CONST(Crypto_JobInfoType, AUTOMATIC, CRYPTO_APPL_CONST) JobPtr)
{
    VAR(Std_ReturnType, AUTOMATIC) result = (Std_ReturnType)E_NOT_OK;

    if ((CRYPTO_JOBSTATE_ACTIVE == g_cryptoCurrentJobState) &&
        (JobPtr->jobId == g_cryptoCurrentJobPtr->jobInfo->jobId))
    {
        /* The job is currently in progress, cancel it now. */
        result = ((Std_ReturnType)E_OK == Crypto_Cse_Cancel()) ?
                 (Std_ReturnType)CRYPTO_E_JOB_CANCELED : (Std_ReturnType)E_NOT_OK;
    }
    else
    {
        /* Try to remove the job from the queue */
        result = Crypto_Impl_DequeueJob(ObjectIndex, JobPtr);
    }
    return result;
}

/*!
* @brief    This function checks if there is asynchronous job in the queue.
*           If yes, start processing the job.
*/
FUNC(void, CRYPTO_CODE) Crypto_Impl_MainFunction(void)
{
    VAR(uint32, AUTOMATIC) firstJob = 0U;
    P2CONST(Crypto_JobType, AUTOMATIC, CRYPTO_APPL_DATA) jobPtr = NULL_PTR;
    VAR(uint32, AUTOMATIC) object = 0U;

    /* Check if there is asynchronous job in the queue */
    while (object < CRYPTO_DRIVER_OBJECT_NUMBER)
    {
        if (CRYPTO_JOB_QUEUE_NULL_ITEM != Crypto_ListObject[object]->headOfQueuedJobs)
        {
            break;
        }
        else
        {
            /* nothing */
        }
        ++object;
    }

    if (CRYPTO_DRIVER_OBJECT_NUMBER != object)
    {
        /* The queue is already sorted by priority, so the first job in the queue
        * has the highest priority. Now take the first job in the queue.
        */
        firstJob = Crypto_ListObject[object]->headOfQueuedJobs;
        jobPtr = Crypto_ListObject[object]->queuedJobs[firstJob].jobPtr;

        /* Process the job */
        if ((Std_ReturnType)CRYPTO_E_BUSY != Crypto_Impl_ProcessOperation(jobPtr))
        {
            /* The job is being processed, remove it from the queue */
            Crypto_Impl_DequeueHeadJob(object);
        }
        else
        {
            /* nothing */
        }
    }
    else
    {
        /* nothing */
    }
}

/*!
* @brief    Callback function called by the ISR
*/
FUNC(void, CRYPTO_CODE) Crypto_Impl_CallBackNotification(
    VAR(Crypto_Cse_ErrorCodeType, AUTOMATIC) ErrorCode)
{
    VAR(Std_ReturnType, AUTOMATIC) result = (Std_ReturnType)E_OK;

    result = Crypto_Impl_VerifyCseError(ErrorCode);
    CryIf_CallbackNotification(g_cryptoCurrentJobPtr, result);
    g_cryptoCurrentJobState = CRYPTO_JOBSTATE_IDLE;
}

#if (CRYPTO_KEY_GEN_CONFIGURED == STD_ON)
/*!
* @brief     Generate a random key and load it in RAM_KEY
* @details   This function can only generate a random number and load it into RAM_KEY.
*            For other keys the request will be ignored.
*
* @param[in] KeyId  Key ID
*
* @return    Std_ReturnType  Standard return type
*/
FUNC(Std_ReturnType, CRYPTO_CODE) Crypto_Impl_GenerateRandomKey(
    VAR(uint32, AUTOMATIC) KeyId)
{
    VAR(Std_ReturnType, AUTOMATIC) result = (Std_ReturnType)E_NOT_OK;
    VAR(Crypto_Cse_ErrorCodeType, AUTOMATIC) error = (Crypto_Cse_ErrorCodeType)CSE_ERC_GENERAL_ERROR;
    VAR(uint32, AUTOMATIC) keySize = 0;
    VAR(Crypto_KeyStatusType, AUTOMATIC) keyStatus = {(Std_ReturnType)E_OK,0U};
    VAR(uint32, AUTOMATIC) element = 0U;
    VAR(uint32, AUTOMATIC) i = 0U;
    VAR(uint8, AUTOMATIC) srcBuf[32] = {0};
    
    if ((boolean)TRUE == Crypto_Cse_IsBusy())
    {
        error = (Crypto_Cse_ErrorCodeType)CSE_ERC_BUSY;
    }
    else
    {
        /* Check if the parameter CryptoKeyId is out of range */
        keyStatus = Crypto_Impl_CheckKeyId(KeyId);

        /* Initialize random number generator */
        Crypto_Rng_Init();
        error = Crypto_Rng_WaitReady(CRYPTO_TIMEOUT_VALUE);
        if (CSE_ERC_OK == error)
        {
            while (element < Crypto_KeyList[keyStatus.index]->elementList->count)
            {
                keySize = Crypto_KeyElementList[Crypto_KeyList[keyStatus.index]->elementList->elements[element]]->actualSize;
                error = Crypto_Rng_RandomGenerate(keySize,srcBuf);
                if (CSE_ERC_OK == error)
                {
                    for (i = 0U; i < keySize; i++)
                    {
                        Crypto_KeyElementList[Crypto_KeyList[keyStatus.index]->elementList->elements[element]]->data[i] = srcBuf[i];
                    }
                    element++;
                }
                else
                {
                    break;
                }
            }
        }
        else
        {
            /* nothing */
        }
    }
    result = Crypto_Impl_VerifyCseError(error);
    return result;
}
#endif

/*!
* @brief        Verify if the key ID matches the configuration
*
* @param[in]    KeyId The key ID to be verified
*
* @return       Verification result
*/
FUNC(Crypto_KeyStatusType, CRYPTO_CODE) Crypto_Impl_CheckKeyId(VAR(uint32, AUTOMATIC) KeyId)
{
    VAR(Crypto_KeyStatusType, AUTOMATIC) keyStatus = {(Std_ReturnType)E_NOT_OK, 0U};
    VAR(uint32, AUTOMATIC) keyIndex = 0U;

    while (keyIndex < CRYPTO_KEYS_NUMBER)
    {
        if (KeyId == Crypto_KeyList[keyIndex]->keyId)
        {
            keyStatus.index = keyIndex;
            keyStatus.status = (Std_ReturnType)E_OK;
            break;
        }
        else
        {
            /* nothing */
        }
        keyIndex++;
    }
    return keyStatus;
}

/*!
* @brief        Verify if the key element ID matches the configuration
*
* @param[in]    KeyIndex     Index of the key
* @param[in]    KeyElementId The key element ID to be verified
*
* @return       Verification result
*/
FUNC(Crypto_ElementStatusType, CRYPTO_CODE) Crypto_Impl_CheckKeyElementId(
    VAR(uint32, AUTOMATIC) KeyIndex,
    VAR(uint32, AUTOMATIC) KeyElementId)
{
    VAR(Crypto_ElementStatusType, AUTOMATIC) keyElementStatus = {(Std_ReturnType)E_NOT_OK, 0U};
    VAR(uint32, AUTOMATIC) element = 0U;

    while (element < Crypto_KeyList[KeyIndex]->elementList->count)
    {
        if (KeyElementId ==
            Crypto_KeyElementList[Crypto_KeyList[KeyIndex]->elementList->elements[element]]->elementId)
        {
            keyElementStatus.index = Crypto_KeyList[KeyIndex]->elementList->elements[element];
            keyElementStatus.status = (Std_ReturnType)E_OK;
            break;
        }
        else
        {
            /* nothing */
        }
        element++;
    }
    return keyElementStatus;
}

/*******************************************************************************
                        LOCAL FUNCTIONS
*******************************************************************************/

/**
* @brief        Verify the error code returned by hardware and covert it to standard return code
*
* @param[in]    ErrorCode   Error code returned by hardware
*
* @return       Std_ReturnType  Standard return type
*/
LOCAL_INLINE FUNC(Std_ReturnType, CRYPTO_CODE) Crypto_Impl_VerifyCseError(
    VAR(Crypto_Cse_ErrorCodeType, AUTOMATIC) ErrorCode)
{
    VAR(Std_ReturnType, AUTOMATIC) result = (Std_ReturnType)E_OK;

    if (CSE_ERC_OK == ErrorCode)
    {
        result = (Std_ReturnType)E_OK;
    }
    else if (CSE_ERC_KEY_INVALID == ErrorCode)
    {
        result = (Std_ReturnType)CRYPTO_E_KEY_NOT_VALID;
    }
    else if (CSE_ERC_KEY_NOT_AVAILABLE == ErrorCode)
    {
        result = (Std_ReturnType)CRYPTO_E_KEY_NOT_AVAILABLE;
    }
    else if (CSE_ERC_BUSY == ErrorCode)
    {
        result = (Std_ReturnType)CRYPTO_E_BUSY;
    }
    else if (CSE_ERC_SMALL_BUFFER == ErrorCode)
    {
        result = (Std_ReturnType)CRYPTO_E_SMALL_BUFFER;
    }
    else if (CSE_ERC_CANCELED == ErrorCode)
    {
        result = (Std_ReturnType)CRYPTO_E_JOB_CANCELED;
    }
    else if (CSE_ERC_KEY_WRITE_PROTECTED == ErrorCode)
    {
        result = (Std_ReturnType)CRYPTO_E_KEY_WRITE_FAIL;
    }
    else
    {
        result = (Std_ReturnType)E_NOT_OK;
    }
    return result;
}

/*!
* @brief        Process an operation
*
* @param[in]    JobPtr Pointer to the job
*
* @return       Std_ReturnType  Standard return type
*/
LOCAL_INLINE FUNC(Std_ReturnType, CRYPTO_CODE) Crypto_Impl_ProcessOperation(
    P2CONST(Crypto_JobType, AUTOMATIC, CRYPTO_APPL_DATA) JobPtr)
{
    VAR(Std_ReturnType, AUTOMATIC) result = (Std_ReturnType)E_NOT_OK;
    VAR(Crypto_Cse_ErrorCodeType, AUTOMATIC) error = CSE_ERC_GENERAL_ERROR;
    VAR(Crypto_ServiceInfoType, AUTOMATIC) service = JobPtr->jobPrimitiveInfo->primitiveInfo->service;
    VAR(boolean, AUTOMATIC) isBusy = Crypto_Cse_IsBusy();
    if ((CRYPTO_JOBSTATE_IDLE == g_cryptoCurrentJobState) && ((boolean)FALSE == isBusy))
    {
        /* Store the job pointer, it will be used for asynchronous jobs. */
        g_cryptoCurrentJobPtr = JobPtr;

        /* Change the job state to active */
        g_cryptoCurrentJobState = CRYPTO_JOBSTATE_ACTIVE;

        if (service == CRYPTO_ENCRYPT)
        {
            error = Crypto_Impl_Encrypt(JobPtr);
        }
        else if (service == CRYPTO_DECRYPT)
        {
            error = Crypto_Impl_Decrypt(JobPtr);
        }
        else if (service == CRYPTO_MACGENERATE)
        {
            error = Crypto_Impl_MacGenerate(JobPtr);
        }
        else if (service == CRYPTO_MACVERIFY)
        {
            error = Crypto_Impl_MacVerify(JobPtr);
        }
        else if ((service == CRYPTO_MPCMGENERATE) || (service == CRYPTO_HASH) || (service == CRYPTO_RANDOMGENERATE))
        {
            error = Crypto_Impl_Generate(JobPtr);
        }
        else
        {
            /* nothing */
        }
        result = Crypto_Impl_VerifyCseError(error);

        /* The job is done for synchronous jobs, change job state to idle. */
        if (CRYPTO_PROCESSING_SYNC == JobPtr->jobPrimitiveInfo->processingType)
        {
            g_cryptoCurrentJobState = CRYPTO_JOBSTATE_IDLE;
        }
        else
        {
            /* nothing */
        }
    }
    else
    {
        result = (Std_ReturnType)CRYPTO_E_BUSY;
    }
    return result;
}

/*!
* @brief        Generate random number / SHA256 / MPC
*
* @param[in]    JobPtr Pointer to the job
*
* @return       Error code returned from hardware
*/
LOCAL_INLINE FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Impl_Generate(P2CONST(Crypto_JobType, AUTOMATIC, CRYPTO_APPL_CONST) JobPtr)
{
    VAR(Crypto_KeyStatusType, AUTOMATIC) keyStatus = {(Std_ReturnType)E_NOT_OK, 0U};
    VAR(Crypto_Cse_ErrorCodeType, AUTOMATIC) error = (Crypto_Cse_ErrorCodeType)CSE_ERC_GENERAL_ERROR;
    if (*JobPtr->jobPrimitiveInputOutput.outputLengthPtr < JobPtr->jobPrimitiveInfo->primitiveInfo->resultLength)
    {
        error = CSE_ERC_SMALL_BUFFER;
        g_cryptoCurrentJobState = CRYPTO_JOBSTATE_IDLE;
    }
    else
    {
        /* The output length is the same with the size of the MPC */
        *JobPtr->jobPrimitiveInputOutput.outputLengthPtr = JobPtr->jobPrimitiveInfo->primitiveInfo->resultLength;

        keyStatus = Crypto_Impl_CheckKeyId(JobPtr->cryptoKeyId);
        if ((Std_ReturnType)E_OK != keyStatus.status)
        {
            g_cryptoCurrentJobState = CRYPTO_JOBSTATE_IDLE;
        }
        else
        {
            if(CRYPTO_ALGOMODE_TRNG == JobPtr->jobPrimitiveInfo->primitiveInfo->algorithm.mode)
            {
                /* Generate random number */
                error = Crypto_Cse_GenerateRnd(JobPtr->jobPrimitiveInputOutput.outputPtr,JobPtr->jobPrimitiveInfo->processingType);
            }
            else if(CRYPTO_ALGOMODE_SHA256 == JobPtr->jobPrimitiveInfo->primitiveInfo->algorithm.mode)
            {
                /* Generate SHA256 data */
                error = Crypto_Cse_GenerateHash(JobPtr->jobPrimitiveInputOutput.inputPtr, JobPtr->jobPrimitiveInputOutput.inputLength, 
                    JobPtr->jobPrimitiveInputOutput.outputPtr,JobPtr->jobPrimitiveInfo->processingType);
            }
            else if(CRYPTO_ALGOMODE_MPC == JobPtr->jobPrimitiveInfo->primitiveInfo->algorithm.mode)
            {
                /* Generate MPC data */
                error = Crypto_Cse_AesGenerateMpc((JobPtr->jobPrimitiveInputOutput.inputPtr), JobPtr->jobPrimitiveInputOutput.inputLength, 
                       JobPtr->jobPrimitiveInputOutput.outputPtr,JobPtr->jobPrimitiveInfo->processingType);
            }
            else
            {
                /* do nothing*/
            }
        }
    }
    return error;
}

/*!
* @brief        Process MAC generation job
*
* @param[in]    JobPtr Pointer to the job
*
* @return       Error code returned from hardware
*/
LOCAL_INLINE FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Impl_MacGenerate(
    P2CONST(Crypto_JobType, AUTOMATIC, CRYPTO_APPL_CONST) JobPtr)
{
    VAR(Crypto_KeyStatusType, AUTOMATIC) keyStatus = {(Std_ReturnType)E_NOT_OK, 0U};
    VAR(Crypto_ElementStatusType, AUTOMATIC) keyElementStatusAes = {(Std_ReturnType)E_NOT_OK, 0U};
    VAR(Crypto_ElementStatusType, AUTOMATIC) keyElementStatusIV = {(Std_ReturnType)E_NOT_OK, 0U};
    VAR(Crypto_Cse_ErrorCodeType, AUTOMATIC) error = (Crypto_Cse_ErrorCodeType)CSE_ERC_GENERAL_ERROR;
    VAR(Crypto_Aes256_KeySizeType, AUTOMATIC) keySize = AES256_KEYSIZE_256B;

    if (*JobPtr->jobPrimitiveInputOutput.outputLengthPtr <
         JobPtr->jobPrimitiveInfo->primitiveInfo->resultLength)
    {
        error = CSE_ERC_SMALL_BUFFER;
        g_cryptoCurrentJobState = CRYPTO_JOBSTATE_IDLE;
    }
    else
    {
        /* The output length is the same with the size of the MAC */
        *JobPtr->jobPrimitiveInputOutput.outputLengthPtr =
                JobPtr->jobPrimitiveInfo->primitiveInfo->resultLength;

        keyStatus = Crypto_Impl_CheckKeyId(JobPtr->cryptoKeyId);
        if ((Std_ReturnType)E_OK != keyStatus.status)
        {
            g_cryptoCurrentJobState = CRYPTO_JOBSTATE_IDLE;
        }
        else
        {
            keyElementStatusAes = Crypto_Impl_CheckKeyElementId(keyStatus.index, JobPtr->KeyElementId);
            switch(Crypto_KeyElementList[keyElementStatusAes.index]->actualSize)
            {
                case 32:
                    keySize = AES256_KEYSIZE_256B;
                    break;
                case 24:
                    keySize = AES256_KEYSIZE_192B;
                    break;
                case 16:
                    keySize = AES256_KEYSIZE_128B;
                    break;
                default:
                    /* Nothing */
                    break;
            }
            
            /* Check key element CRYPTO_KEYELEMENT_IV (5), which is required by CBC mode */
            keyElementStatusIV = Crypto_Impl_CheckKeyElementId(keyStatus.index, CRYPTO_KEYELEMENT_IV);

            if ((Std_ReturnType)E_OK != keyElementStatusIV.status)
            {
                g_cryptoCurrentJobState = CRYPTO_JOBSTATE_IDLE;
            }
            else
            {
                error = Crypto_Cse_AesGenerateMac(Crypto_KeyElementList[keyElementStatusAes.index]->data, keySize,
                                Crypto_KeyElementList[keyElementStatusIV.index]->data, (JobPtr->jobPrimitiveInputOutput.inputPtr), 
                           JobPtr->jobPrimitiveInputOutput.inputLength, JobPtr->jobPrimitiveInputOutput.outputPtr,JobPtr->jobPrimitiveInfo->processingType);
            }
        }
    }
    return error;
}

/*!
* @brief        Process MAC verification job
*
* @param[in]    JobPtr Pointer to the job
*
* @return       Error code returned from hardware
*/
LOCAL_INLINE FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Impl_MacVerify(
    P2CONST(Crypto_JobType, AUTOMATIC, CRYPTO_APPL_CONST) JobPtr)
{
    
    VAR(Crypto_Cse_ErrorCodeType, AUTOMATIC) error = (Crypto_Cse_ErrorCodeType)CSE_ERC_GENERAL_ERROR;
    VAR(Crypto_Aes256_KeySizeType, AUTOMATIC) keySize = AES256_KEYSIZE_256B;
    VAR(Crypto_KeyStatusType, AUTOMATIC) keyStatus = {(Std_ReturnType)E_NOT_OK, 0U};
    VAR(Crypto_ElementStatusType, AUTOMATIC) keyElementStatusAes = {(Std_ReturnType)E_NOT_OK, 0U};
    VAR(Crypto_ElementStatusType, AUTOMATIC) keyElementStatusIV = {(Std_ReturnType)E_NOT_OK, 0U};
    VAR(uint8, AUTOMATIC) verificationStatus = 0;
    VAR(uint8, AUTOMATIC) cmpDataNum = 0;
    keyStatus = Crypto_Impl_CheckKeyId(JobPtr->cryptoKeyId);
    if ((Std_ReturnType)E_OK != keyStatus.status)
    {
        g_cryptoCurrentJobState = CRYPTO_JOBSTATE_IDLE;
    }
    else
    {
        keyElementStatusAes = Crypto_Impl_CheckKeyElementId(keyStatus.index, JobPtr->KeyElementId);
        switch(Crypto_KeyElementList[keyElementStatusAes.index]->actualSize)
        {
            case 32:
                keySize = AES256_KEYSIZE_256B;
                break;
            case 24:
                keySize = AES256_KEYSIZE_192B;
                break;
            case 16:
                keySize = AES256_KEYSIZE_128B;
                break;
            default:
                /* Nothing */
                break;
        }

        /* Check key element CRYPTO_KEYELEMENT_IV (5), which is required by CBC mode */
        keyElementStatusIV = Crypto_Impl_CheckKeyElementId(keyStatus.index, CRYPTO_KEYELEMENT_IV);

        if ((Std_ReturnType)E_OK != keyElementStatusIV.status)
        {
            g_cryptoCurrentJobState = CRYPTO_JOBSTATE_IDLE;
        }
        else
        {
            if (CRYPTO_PROCESSING_SYNC == JobPtr->jobPrimitiveInfo->processingType)
            {
                error = Crypto_Cse_AesGenerateMac(Crypto_KeyElementList[keyElementStatusAes.index]->data, keySize,
                                 Crypto_KeyElementList[keyElementStatusIV.index]->data, (JobPtr->jobPrimitiveInputOutput.inputPtr), 
                                 JobPtr->jobPrimitiveInputOutput.inputLength, JobPtr->jobPrimitiveInputOutput.outputPtr,JobPtr->jobPrimitiveInfo->processingType);
                                 
                if(error == CSE_ERC_OK)
                {
                    for (cmpDataNum = 0; cmpDataNum < JobPtr->jobPrimitiveInputOutput.secondaryInputLength; cmpDataNum++) 
                    {
                        verificationStatus |= JobPtr->jobPrimitiveInputOutput.secondaryInputPtr[cmpDataNum] ^ JobPtr->jobPrimitiveInputOutput.outputPtr[cmpDataNum];
                    }
                    *(JobPtr->jobPrimitiveInputOutput.verifyPtr) = (CSE_MAC_VERIFY_CORRECT == verificationStatus) ? CRYPTO_E_VER_OK : CRYPTO_E_VER_NOT_OK;
                    if(verificationStatus == (uint8)0)
                    {
                        error = (Crypto_Cse_ErrorCodeType)CSE_ERC_OK;
                    }
                    else
                    {
                        error = (Crypto_Cse_ErrorCodeType)CSE_ERC_ERROR;
                    }    
                } 
                else
                {
                    /* Nothing */
                }
            }
            else
            {
                error = Crypto_Cse_MacVerify(Crypto_KeyElementList[keyElementStatusAes.index]->data, keySize, Crypto_KeyElementList[keyElementStatusIV.index]->data, JobPtr);
            }
        }
    }
    return error;
}

/*!
* @brief        Process AES encryption job
*
* @param[in]    JobPtr Pointer to the job
*
* @return       Error code returned from hardware
*/
LOCAL_INLINE FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Impl_Encrypt(
    P2CONST(Crypto_JobType, AUTOMATIC, CRYPTO_APPL_CONST) JobPtr)
{
    VAR(Crypto_KeyStatusType, AUTOMATIC) keyStatus = {(Std_ReturnType)E_NOT_OK, 0U};
    VAR(Crypto_ElementStatusType, AUTOMATIC) keyElementStatusIV = {(Std_ReturnType)E_NOT_OK, 0U};
    VAR(Crypto_ElementStatusType, AUTOMATIC) keyElementStatusAes = {(Std_ReturnType)E_NOT_OK, 0U};

    VAR(Crypto_Cse_ErrorCodeType, AUTOMATIC) error = (Crypto_Cse_ErrorCodeType)CSE_ERC_GENERAL_ERROR;
    VAR(Crypto_Aes256_KeySizeType, AUTOMATIC) keySize = AES256_KEYSIZE_256B;
    if (JobPtr->jobPrimitiveInputOutput.inputLength > *JobPtr->jobPrimitiveInputOutput.outputLengthPtr)
    {
        error = CSE_ERC_SMALL_BUFFER;
        g_cryptoCurrentJobState = CRYPTO_JOBSTATE_IDLE;
    }
    else
    {
        *JobPtr->jobPrimitiveInputOutput.outputLengthPtr = JobPtr->jobPrimitiveInputOutput.inputLength;
        keyStatus = Crypto_Impl_CheckKeyId(JobPtr->cryptoKeyId);
        keyElementStatusAes = Crypto_Impl_CheckKeyElementId(keyStatus.index, JobPtr->KeyElementId);
        switch(Crypto_KeyElementList[keyElementStatusAes.index]->actualSize)
        {
            case 32:
                keySize = AES256_KEYSIZE_256B;
                break;
            case 24:
                keySize = AES256_KEYSIZE_192B;
                break;
            case 16:
                keySize = AES256_KEYSIZE_128B;
                break;
            default:
                /* Nothing */
                break;
        }
        if ((CRYPTO_ALGOMODE_CBC == JobPtr->jobPrimitiveInfo->primitiveInfo->algorithm.mode) ||
            (CRYPTO_ALGOMODE_CTR == JobPtr->jobPrimitiveInfo->primitiveInfo->algorithm.mode))
        {
            
            if ((Std_ReturnType)E_OK == keyStatus.status)
            {
                /* Check key element CRYPTO_KEYELEMENT_IV (5), which is required by CBC mode */
                keyElementStatusIV = Crypto_Impl_CheckKeyElementId(keyStatus.index, CRYPTO_KEYELEMENT_IV);
            }
            else
            {
                /* nothing */
            }

            if ((Std_ReturnType)E_OK != keyElementStatusIV.status)
            {
                g_cryptoCurrentJobState = CRYPTO_JOBSTATE_IDLE;
            }
            else
            {
                if (CRYPTO_ALGOMODE_CBC == JobPtr->jobPrimitiveInfo->primitiveInfo->algorithm.mode)
                {
                    error = Crypto_Cse_AesCbcEncryptDecrypt(AES256_ALGODIR_ENCRYPT, Crypto_KeyElementList[keyElementStatusAes.index]->data, keySize,
                           Crypto_KeyElementList[keyElementStatusIV.index]->data, (JobPtr->jobPrimitiveInputOutput.inputPtr), 
                           JobPtr->jobPrimitiveInputOutput.inputLength, JobPtr->jobPrimitiveInputOutput.outputPtr,JobPtr->jobPrimitiveInfo->processingType);
                }
                else
                {
                    error = Crypto_Cse_AesCtrEncryptDecrypt(AES256_ALGODIR_ENCRYPT, Crypto_KeyElementList[keyElementStatusAes.index]->data, keySize,
                           Crypto_KeyElementList[keyElementStatusIV.index]->data, (JobPtr->jobPrimitiveInputOutput.inputPtr), 
                           JobPtr->jobPrimitiveInputOutput.inputLength, JobPtr->jobPrimitiveInputOutput.outputPtr,JobPtr->jobPrimitiveInfo->processingType);
                }
            }
        }
        else if (CRYPTO_ALGOMODE_ECB == JobPtr->jobPrimitiveInfo->primitiveInfo->algorithm.mode)
        {
            error = Crypto_Cse_AesEcbEncryptDecrypt(AES256_ALGODIR_ENCRYPT, Crypto_KeyElementList[keyElementStatusAes.index]->data, keySize,
                           JobPtr->jobPrimitiveInputOutput.inputPtr, JobPtr->jobPrimitiveInputOutput.inputLength, JobPtr->jobPrimitiveInputOutput.outputPtr,
                           JobPtr->jobPrimitiveInfo->processingType);
        }
        else
        {
            /* nothing */
        }
    }
    return error;
}

/*!
* @brief        Process AES decryption job
*
* @param[in]    JobPtr Pointer to the job
*
* @return       Error code returned from hardware
*/
LOCAL_INLINE FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Impl_Decrypt(
    P2CONST(Crypto_JobType, AUTOMATIC, CRYPTO_APPL_CONST) JobPtr)
{
    VAR(Crypto_KeyStatusType, AUTOMATIC) keyStatus = {(Std_ReturnType)E_NOT_OK, 0U};
    VAR(Crypto_ElementStatusType, AUTOMATIC) keyElementStatusAes = {(Std_ReturnType)E_NOT_OK, 0U};
    VAR(Crypto_ElementStatusType, AUTOMATIC) keyElementStatusIV = {(Std_ReturnType)E_NOT_OK, 0U};
    VAR(Crypto_Cse_ErrorCodeType, AUTOMATIC) error = (Crypto_Cse_ErrorCodeType) CSE_ERC_GENERAL_ERROR;
    VAR(Crypto_Aes256_KeySizeType, AUTOMATIC) keySize = AES256_KEYSIZE_256B;

    if (JobPtr->jobPrimitiveInputOutput.inputLength >
       *JobPtr->jobPrimitiveInputOutput.outputLengthPtr)
    {
        error = CSE_ERC_SMALL_BUFFER;
        g_cryptoCurrentJobState = CRYPTO_JOBSTATE_IDLE;
    }
    else
    {
        *JobPtr->jobPrimitiveInputOutput.outputLengthPtr = JobPtr->jobPrimitiveInputOutput.inputLength;
        keyStatus = Crypto_Impl_CheckKeyId(JobPtr->cryptoKeyId);
        keyElementStatusAes = Crypto_Impl_CheckKeyElementId(keyStatus.index, JobPtr->KeyElementId);
        switch(Crypto_KeyElementList[keyElementStatusAes.index]->actualSize)
        {
            case 32:
                keySize = AES256_KEYSIZE_256B;
                break;
            case 24:
                keySize = AES256_KEYSIZE_192B;
                break;
            case 16:
                keySize = AES256_KEYSIZE_128B;
                break;
            default:
                /* Nothing */
                break;
        }
        if ((CRYPTO_ALGOMODE_CBC == JobPtr->jobPrimitiveInfo->primitiveInfo->algorithm.mode) ||
            (CRYPTO_ALGOMODE_CTR == JobPtr->jobPrimitiveInfo->primitiveInfo->algorithm.mode))
        {
            if ((Std_ReturnType)E_OK == keyStatus.status)
            {
                /* Check key element CRYPTO_KEYELEMENT_IV (5), which is required by CBC mode */
                keyElementStatusIV = Crypto_Impl_CheckKeyElementId(keyStatus.index, CRYPTO_KEYELEMENT_IV);
            }
            else
            {
                /* nothing */
            }

            if ((Std_ReturnType)E_OK != keyElementStatusIV.status)
            {
                g_cryptoCurrentJobState = CRYPTO_JOBSTATE_IDLE;
            }
            else
            {
                if (CRYPTO_ALGOMODE_CBC == JobPtr->jobPrimitiveInfo->primitiveInfo->algorithm.mode)
                {
                    error = Crypto_Cse_AesCbcEncryptDecrypt(AES256_ALGODIR_DECRYPT, Crypto_KeyElementList[keyElementStatusAes.index]->data, keySize,
                           Crypto_KeyElementList[keyElementStatusIV.index]->data, (JobPtr->jobPrimitiveInputOutput.inputPtr), 
                           JobPtr->jobPrimitiveInputOutput.inputLength, JobPtr->jobPrimitiveInputOutput.outputPtr,JobPtr->jobPrimitiveInfo->processingType);
                }
                else
                {
                    error = Crypto_Cse_AesCtrEncryptDecrypt(AES256_ALGODIR_DECRYPT, Crypto_KeyElementList[keyElementStatusAes.index]->data, keySize,
                           Crypto_KeyElementList[keyElementStatusIV.index]->data, (JobPtr->jobPrimitiveInputOutput.inputPtr), 
                           JobPtr->jobPrimitiveInputOutput.inputLength, JobPtr->jobPrimitiveInputOutput.outputPtr,JobPtr->jobPrimitiveInfo->processingType);
                }
            }
        }
        else if (CRYPTO_ALGOMODE_ECB == JobPtr->jobPrimitiveInfo->primitiveInfo->algorithm.mode)
        {
            error = Crypto_Cse_AesEcbEncryptDecrypt(AES256_ALGODIR_DECRYPT, Crypto_KeyElementList[keyElementStatusAes.index]->data, keySize,
               JobPtr->jobPrimitiveInputOutput.inputPtr, JobPtr->jobPrimitiveInputOutput.inputLength, JobPtr->jobPrimitiveInputOutput.outputPtr,
               JobPtr->jobPrimitiveInfo->processingType);
        }
        else
        {
            /* nothing */
        }
    }
    return error;
}

/*!
* @brief    Add a job into the queue
* @details  Add a job into the queue if at least one element is found in the queue
*           of free jobs. The job is inserted in the queue based on its priority.
*
* @param[in] ObjectIndex Index of the Crypto Driver Object
* @param[in] JobPtr      Pointer to the job to be queued
*
* @retval   TRUE:   Job is queued successfully
* @retval   FALSE:  Job cannot be queued as the queue was full
*/
LOCAL_INLINE FUNC(boolean, CRYPTO_CODE) Crypto_Impl_QueueJob(
    VAR(uint32, AUTOMATIC) ObjectIndex,
    P2VAR(Crypto_JobType, AUTOMATIC,  CRYPTO_APPL_DATA) JobPtr)
{
    VAR(uint32, AUTOMATIC) insertElement = 0U;
    VAR(uint32, AUTOMATIC) tempElement = 0U;
    P2CONST(Crypto_JobType, AUTOMATIC, CRYPTO_APPL_CONST) duplicatedJobPtr = NULL_PTR;
    P2VAR(uint32, AUTOMATIC, CRYPTO_APPL_DATA) queueElementPtr = NULL_PTR;
    VAR(boolean, AUTOMATIC) jobQueued = (boolean)FALSE;

    /* If there is no element in the free jobs queue, means the queue is full. */
    if (CRYPTO_JOB_QUEUE_NULL_ITEM == Crypto_ListObject[ObjectIndex]->headOfFreeJobs)
    {
        jobQueued = (boolean)FALSE;
    }
    else
    {
        /* Enter critical section */
        SchM_Enter_Crypto_CRYPTO_CRITICAL_SECTION0();

        /* Check if there is already a job has the same id in the queue. If yes, dequeues this job. */
        for (queueElementPtr = &(Crypto_ListObject[ObjectIndex]->headOfQueuedJobs);
            *queueElementPtr != CRYPTO_JOB_QUEUE_NULL_ITEM;)
        {
            if (JobPtr->jobInfo->jobId ==
                Crypto_ListObject[ObjectIndex]->queuedJobs[*queueElementPtr].jobPtr->jobInfo->jobId)
            {
                duplicatedJobPtr = Crypto_ListObject[ObjectIndex]->queuedJobs[*queueElementPtr].jobPtr;
                (void)Crypto_Impl_DequeueJob(ObjectIndex, duplicatedJobPtr->jobInfo);
                break;
            }
            else
            {
                /* nothing */
            }
            /* Go to the next element in the queue */
            queueElementPtr = &(Crypto_ListObject[ObjectIndex]->queuedJobs[*queueElementPtr].next);
        }

        /* Get the first free element from the free jobs queue. We will insert it
        * into the queued jobs.
        */
        insertElement = Crypto_ListObject[ObjectIndex]->headOfFreeJobs;

        /* The first free element is taken, now the head should point to the next free element. */
        Crypto_ListObject[ObjectIndex]->headOfFreeJobs =
                Crypto_ListObject[ObjectIndex]->queuedJobs[insertElement].next;

        /* Put the free element taken from the free jobs queue into the job queue.
        * In the right position based on priority.
        */
        queueElementPtr = &(Crypto_ListObject[ObjectIndex]->headOfQueuedJobs);
        while(*queueElementPtr != CRYPTO_JOB_QUEUE_NULL_ITEM)
        {
            if (JobPtr->jobInfo->jobPriority >
                Crypto_ListObject[ObjectIndex]->queuedJobs[*queueElementPtr].jobPtr->jobInfo->jobPriority)
            {
                /* The new element should be inserted here, store the old element first. */
                tempElement = *queueElementPtr;

                /* Insert the new element here */
                *queueElementPtr = insertElement;

                /* Now the old element should be the next one */
                Crypto_ListObject[ObjectIndex]->queuedJobs[insertElement].next = tempElement;
                jobQueued = (boolean)TRUE;
                break;
            }
            else
            {
                /* nothing */
            }
            /* Go to the next element in the queue */
            queueElementPtr = &(Crypto_ListObject[ObjectIndex]->queuedJobs[*queueElementPtr].next);
        }
        
        /* If we looped through all elements containing queued jobs and did not
        * find a place where to put the new element, add it at the end.
        */
        if ((boolean)FALSE == jobQueued)
        {
            *queueElementPtr = insertElement;
            Crypto_ListObject[ObjectIndex]->queuedJobs[insertElement].next = CRYPTO_JOB_QUEUE_NULL_ITEM;
        }
        else
        {
            /* nothing */
        }

        /* Exit critical section */
        SchM_Exit_Crypto_CRYPTO_CRITICAL_SECTION0();

        /* Copy the job into the inserted queue element */
        Crypto_ListObject[ObjectIndex]->queuedJobs[insertElement].jobPtr = JobPtr;
        jobQueued = (boolean)TRUE;
    }
    return jobQueued;
}

/*!
* @brief        Dequeue the first job in the queue
*
* @param[in]    ObjectIndex Index of the Crypto Driver Object
*/
LOCAL_INLINE FUNC(void, CRYPTO_CODE) Crypto_Impl_DequeueHeadJob(
    VAR(uint32, AUTOMATIC) ObjectIndex)
{
    /* Save current head of the queues */
    VAR(uint32, AUTOMATIC) tFree = Crypto_ListObject[ObjectIndex]->headOfFreeJobs;
    VAR(uint32, AUTOMATIC) tQueued = Crypto_ListObject[ObjectIndex]->headOfQueuedJobs;

    SchM_Enter_Crypto_CRYPTO_CRITICAL_SECTION1();

    /* Now the head of queued jobs should be the next element in the queue */
    Crypto_ListObject[ObjectIndex]->headOfQueuedJobs =
        Crypto_ListObject[ObjectIndex]->queuedJobs[tQueued].next;

    /* Add the removed head of queued jobs to the head of free jobs */
    Crypto_ListObject[ObjectIndex]->headOfFreeJobs = tQueued;
    Crypto_ListObject[ObjectIndex]->queuedJobs[tQueued].next = tFree;
    SchM_Exit_Crypto_CRYPTO_CRITICAL_SECTION1();
}

/*!
* @brief    Dequeue a job with the same job id from the queue
*
* @param[in] ObjectIndex Index of the Crypto Driver Object
* @param[in] JobInfoPtr  Pointer to jobInfo structure containing the jobId
*
* @retval   E_OK        Job was dequeued successfully
* @retval   E_NOT_OK    Job cannot be dequeued as it was not in the queue
*/
LOCAL_INLINE FUNC(Std_ReturnType, CRYPTO_CODE) Crypto_Impl_DequeueJob(
    VAR(uint32, AUTOMATIC) ObjectIndex,
    P2CONST(Crypto_JobInfoType, AUTOMATIC, CRYPTO_APPL_CONST) JobInfoPtr)
{
    VAR(uint32, AUTOMATIC) headOfFreeJobs = 0U;
    VAR(uint32, AUTOMATIC) elementToRemove = 0U;
    P2VAR(uint32, AUTOMATIC, CRYPTO_APPL_DATA) queueElementPtr = NULL_PTR;
    VAR(Std_ReturnType, AUTOMATIC) result = (Std_ReturnType)E_NOT_OK;

    /* Check if there is any element in the queued jobs */
    if (CRYPTO_JOB_QUEUE_NULL_ITEM == Crypto_ListObject[ObjectIndex]->headOfQueuedJobs)
    {
        result = (Std_ReturnType)E_NOT_OK;
    }
    else
    {
        /* Loop through the list of queued jobs, search for the job to dequeue. */
        for (queueElementPtr = &(Crypto_ListObject[ObjectIndex]->headOfQueuedJobs);
            *queueElementPtr != CRYPTO_JOB_QUEUE_NULL_ITEM;)
        {
            /* Found the job with the same job id */
            if (JobInfoPtr->jobId ==
                Crypto_ListObject[ObjectIndex]->queuedJobs[*queueElementPtr].jobPtr->jobInfo->jobId)
            {
                SchM_Enter_Crypto_CRYPTO_CRITICAL_SECTION2();

                /* Store the element to be removed from the queued jobs */
                elementToRemove = *queueElementPtr;

                /* The next element should be placed in the current position in the queue */
                *queueElementPtr = Crypto_ListObject[ObjectIndex]->queuedJobs[*queueElementPtr].next;

                /* Add the removed element as the head of free jobs */
                headOfFreeJobs = Crypto_ListObject[ObjectIndex]->headOfFreeJobs;
                Crypto_ListObject[ObjectIndex]->headOfFreeJobs = elementToRemove;
                Crypto_ListObject[ObjectIndex]->queuedJobs[elementToRemove].next = headOfFreeJobs;

                SchM_Exit_Crypto_CRYPTO_CRITICAL_SECTION2();
                result = (Std_ReturnType)E_OK;
                break;
            }
            else
            {
                /* nothing */
            }

            /* Go to the next element */
            queueElementPtr = &(Crypto_ListObject[ObjectIndex]->queuedJobs[*queueElementPtr].next);
        }
    }
    return result;
}

#define CRYPTO_STOP_SEC_CODE
#include "Crypto_MemMap.h"
/**@} end of group CRYPTO_Fuctions */

#ifdef __cplusplus
}
#endif
/**@} end of group CRYPTO_Driver */
/**@} end of group G32A10xx_StdPeriphDriver */
