/*--------------------------------------------------------------------------*/
/* Copyright 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  mcuxClMac_cmac_oneshot_example.c
 * @brief Example CMAC computation using functions of the mcuxClKey and mcuxClMac component
 *
 * @example mcuxClMac_cmac_oneshot_example.c
 * @brief   Example CMAC computation using functions of the mcuxClKey and mcuxClMac component
 */

#include <stdbool.h>  // bool type for the example's return code

#include <mcuxClCss.h> // Interface to the entire mcuxClCss component
#include <mcuxClKey.h>
#include <mcuxClSession.h>
#include <mcuxCsslFlowProtection.h> // Code flow protection

#include <mcuxClMac.h>
#include <internal/mcuxClMac_internal.h>


/** Performs a CMAC computation using functions of the mcuxClKey component.
 * @retval true  The example code completed successfully
 * @retval false The example code failed */
bool mcuxClMac_cmac_oneshot_example(void)
{
    /* Example AES-128 key. */
    static uint8_t aes128_key[MCUXCLKEY_SIZE_128] = {
                                        0x7c, 0x0b, 0x7d, 0xb9,
                                        0x81, 0x1f, 0x10, 0xd0,
                                        0x0e, 0x47, 0x6c, 0x7a,
                                        0x0d, 0x92, 0xf6, 0xe0
    };

    /* Example input size. */
    size_t cmac_input_size_16 = 32u;

    /* Example input to the CMAC function. */
    static uint8_t cmac_input16_in[] = {
                                        0x1e, 0xe0, 0xec, 0x46,
                                        0x6d, 0x46, 0xfd, 0x84,
                                        0x9b, 0x40, 0xc0, 0x66,
                                        0xb4, 0xfb, 0xbd, 0x22,
                                        0xa2, 0x0a, 0x4d, 0x80,
                                        0xa0, 0x08, 0xac, 0x9a,
                                        0xf1, 0x7e, 0x4f, 0xdf,
                                        0xd1, 0x06, 0x78, 0x5e
    };

    /* Example reference CMAC. */
    static uint8_t cmac_output_reference16[MCUXCLCSS_CMAC_OUT_SIZE] = {
                                        0xba, 0xec, 0xdc, 0x91,
                                        0xe9, 0xa1, 0xfc, 0x35,
                                        0x72, 0xad, 0xf1, 0xe4,
                                        0x23, 0x2a, 0xe2, 0x85
    };
    /**************************************************************************/
    /* Preparation                                                            */
    /**************************************************************************/

    /* Enable Css */
    MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(result, token, mcuxClCss_Enable_Async()); // Enable the CSSv2.
    // mcuxClCss_Enable_Async is a flow-protected function: Check the protection token and the return value
    if((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClCss_Enable_Async) != token) || (MCUXCLCSS_STATUS_OK_WAIT != result))
        return false;
    MCUX_CSSL_FP_FUNCTION_CALL_END();

    MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(result, token, mcuxClCss_WaitForOperation(MCUXCLCSS_ERROR_FLAGS_CLEAR)); // Wait for the mcuxClCss_Enable_Async operation to complete.
    // mcuxClCss_WaitForOperation is a flow-protected function: Check the protection token and the return value
    if((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClCss_WaitForOperation) != token) || (MCUXCLCSS_STATUS_OK != result))
        return false;
    MCUX_CSSL_FP_FUNCTION_CALL_END();


    /* Key buffer for the key in memory. */
    uint32_t key_buffer[MCUXCLKEY_SIZE_128 / sizeof(uint32_t)];

    /* Output buffer for the computed MAC. */
    static uint8_t result_buffer[MCUXCLCSS_CMAC_OUT_SIZE];

    /* Workarea. */
    uint32_t pWa[MCUXCLMAC_WA_SIZE_MAX / sizeof(uint32_t)];

    mcuxClSession_Descriptor_t session;

    MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(result, token, mcuxClSession_init(&session,
                                                                     pWa,
                                                                     MCUXCLMAC_WA_SIZE_MAX,
                                                                     NULL,
                                                                     NULL));

    if((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClSession_init) != token) || (MCUXCLSESSION_STATUS_OK != result))
        return false;
    MCUX_CSSL_FP_FUNCTION_CALL_END();


    /**************************************************************************/
    /* Key setup                                                              */
    /**************************************************************************/

    /* Create and initialize mcuxClKey_Descriptor_t structure. */
    mcuxClKey_Descriptor_t key = {0};

    MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(result, token, mcuxClKey_init(&session,
                                                                 (mcuxClKey_Handle_t) &key,
                                                                 &mcuxKey_keyType_Aes128,
                                                                 &mcuxClKey_protection_none,
                                                                 aes128_key,
                                                                 NULL, /* no srcAuxData */
                                                                 0u));

    if((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClKey_init) != token) || (MCUXCLKEY_STATUS_OK != result))
        return false;
    MCUX_CSSL_FP_FUNCTION_CALL_END();


    /* Set key properties. */
    mcuxClCss_KeyProp_t cmac_key_properties;

    cmac_key_properties.word.value = 0u;
    cmac_key_properties.bits.ucmac = MCUXCLCSS_KEYPROPERTY_CMAC_TRUE;
    cmac_key_properties.bits.ksize = MCUXCLCSS_KEYPROPERTY_KEY_SIZE_128;
    cmac_key_properties.bits.kactv = MCUXCLCSS_KEYPROPERTY_ACTIVE_TRUE;

    MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(result, token, mcuxClKey_setKeyproperties((mcuxClKey_Handle_t) &key,
                                                                             cmac_key_properties));

    if((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClKey_setKeyproperties) != token) || (MCUXCLKEY_STATUS_OK != result))
        return false;
    MCUX_CSSL_FP_FUNCTION_CALL_END();


    /* Load key to memory. */
    MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(result, token, mcuxClKey_loadMemory(&session,
                                                                       (mcuxClKey_Handle_t) &key,
                                                                       key_buffer));

    if((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClKey_loadMemory) != token) || (MCUXCLKEY_STATUS_OK != result))
        return false;
    MCUX_CSSL_FP_FUNCTION_CALL_END();

    /**************************************************************************/
    /* MAC computation                                                        */
    /**************************************************************************/

    /* Call the mcuxClMac_compute function to compute a CMAC in one shot. */
    MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(result, token, mcuxClMac_compute(&session,
                                                                    (mcuxClKey_Handle_t)&key,
                                                                    mcuxClMac_Mode_CMAC,
                                                                    (uint8_t*)cmac_input16_in,
                                                                    cmac_input_size_16,
                                                                    result_buffer));

    if((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClMac_compute) != token) || (MCUXCLMAC_ERRORCODE_OK != result))
        return false;
    MCUX_CSSL_FP_FUNCTION_CALL_END();

    /**************************************************************************/
    /* Verification                                                           */
    /**************************************************************************/

    /* Compare the result to the reference value. */
    for (size_t i = 0U; i < sizeof(result_buffer); i++)
    {
        // Expect that the resulting MAC matches our expected output
        if (result_buffer[i] != cmac_output_reference16[i]) return false;
    }

    /**************************************************************************/
    /* Cleanup                                                                */
    /**************************************************************************/

    /* Flush the key. */
    MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(result, token, mcuxClKey_flush(&session,
                                                                 (mcuxClKey_Handle_t) &key));

    if((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClKey_flush) != token) || (MCUXCLKEY_STATUS_OK != result))
        return false;
    MCUX_CSSL_FP_FUNCTION_CALL_END();


    MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(cleanup_result, cleanup_token, mcuxClSession_cleanup(
                /* mcuxClSession_Handle_t           pSession: */           &session));

    if(MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClSession_cleanup) != cleanup_token || MCUXCLSESSION_STATUS_OK != cleanup_result)
    {
        return false;
    }

    MCUX_CSSL_FP_FUNCTION_CALL_END();

    MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(destroy_result, destroy_token, mcuxClSession_destroy(
                /* mcuxClSession_Handle_t           pSession: */           &session));

    if(MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClSession_destroy) != destroy_token || MCUXCLSESSION_STATUS_OK != destroy_result)
    {
        return false;
    }

    MCUX_CSSL_FP_FUNCTION_CALL_END();


    MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(result, token, mcuxClCss_Disable()); // Disable the CSSv2.
    // mcuxClCss_Disable is a flow-protected function: Check the protection token and the return value
    if((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClCss_Disable) != token) || (MCUXCLCSS_STATUS_OK != result))
        return false;
    MCUX_CSSL_FP_FUNCTION_CALL_END();


    return true;
}
