//###########################################################################
//
// FILE:   aes.c
//
// TITLE:  Driver for the AES module.
//
//###########################################################################
// $Copyright:
// Copyright (C) 2022 Texas Instruments Incorporated - http://www.ti.com
//
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions 
// are met:
// 
//   Redistributions of source code must retain the above copyright 
//   notice, this list of conditions and the following disclaimer.
// 
//   Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the 
//   documentation and/or other materials provided with the   
//   distribution.
// 
//   Neither the name of Texas Instruments Incorporated nor the names of
//   its contributors may be used to endorse or promote products derived
//   from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// $
//###########################################################################

#include "aes.h"

//*****************************************************************************
//
// AES_configureModule
//
//*****************************************************************************
void
AES_configureModule(uint32_t base, const AES_ConfigParams *config)
{
    //
    // Check the arguments.
    //
    ASSERT(AES_isBaseValid(base));

    //
    // Write the ctrl register with new value and backup the save context field
    //
    HWREG_BP(base + AES_O_CTRL) = ((HWREG_BP(base + AES_O_CTRL) &
                                    ~AES_CONFIG_M)  |
                                   ((uint32_t)config->direction  |
                                    (uint32_t)config->keySize  |
                                    (uint32_t)config-> opMode     |
                                    (uint32_t)config->ctrWidth |
                                    (uint32_t)config->ccmLenWidth |
                                    (uint32_t)config->ccmAuthLenWidth));
}

//*****************************************************************************
//
// AES_setKey1
//
//*****************************************************************************
void
AES_setKey1(uint32_t base, const uint32_t key[], AES_KeySize keySize)
{
    //
    // Check the arguments.
    //
    ASSERT(AES_isBaseValid(base));

    //
    // With all key sizes, the first 4 words are written.
    //
    HWREG_BP(base + AES_O_KEY1_0) = key[0U];
    HWREG_BP(base + AES_O_KEY1_1) = key[1U];
    HWREG_BP(base + AES_O_KEY1_2) = key[2U];
    HWREG_BP(base + AES_O_KEY1_3) = key[3U];

    //
    // The key is 192 or 256 bits.  Write the next 2 words.
    //
    if(keySize != AES_KEY_SIZE_128BIT)
    {
        HWREG_BP(base + AES_O_KEY1_4) = key[4U];
        HWREG_BP(base + AES_O_KEY1_5) = key[5U];
    }

    //
    // The key is 256 bits.  Write the last 2 words.
    //
    if(keySize == AES_KEY_SIZE_256BIT)
    {
        HWREG_BP(base + AES_O_KEY1_6) = key[6U];
        HWREG_BP(base + AES_O_KEY1_7) = key[7U];
    }
}

//*****************************************************************************
//
// AES_setKey2
//
//*****************************************************************************
void
AES_setKey2(uint32_t base, const uint32_t key[], AES_KeySize keySize)
{
    //
    // Check the arguments.
    //
    ASSERT(AES_isBaseValid(base));

    //
    // With all key sizes, the first 4 words are written.
    //
    HWREG_BP(base + AES_O_KEY2_0) = key[0U];
    HWREG_BP(base + AES_O_KEY2_1) = key[1U];
    HWREG_BP(base + AES_O_KEY2_2) = key[2U];
    HWREG_BP(base + AES_O_KEY2_3) = key[3U];

    //
    // The key is 192 or 256 bits.  Write the next 2 words.
    //
    if(keySize != AES_KEY_SIZE_128BIT)
    {
        HWREG_BP(base + AES_O_KEY2_4) = key[4U];
        HWREG_BP(base + AES_O_KEY2_5) = key[5U];
    }

    //
    // The key is 256 bits.  Write the last 2 words.
    //
    if(keySize == AES_KEY_SIZE_256BIT)
    {
        HWREG_BP(base + AES_O_KEY2_6) = key[6U];
        HWREG_BP(base + AES_O_KEY2_7) = key[7U];
    }
}

//*****************************************************************************
//
// AES_setKey3
//
//*****************************************************************************
void
AES_setKey3(uint32_t base, const uint32_t key[])
{
    //
    // Check the arguments.
    //
    ASSERT(AES_isBaseValid(base));

    //
    // With all key sizes, the first 4 words are written.
    //
    HWREG_BP(base + AES_O_KEY2_4) = key[0U];
    HWREG_BP(base + AES_O_KEY2_5) = key[1U];
    HWREG_BP(base + AES_O_KEY2_6) = key[2U];
    HWREG_BP(base + AES_O_KEY2_7) = key[3U];
}

//*****************************************************************************
//
// AES_setInitializationVector
//
//*****************************************************************************
void
AES_setInitializationVector(uint32_t base, const uint32_t iniVector[])
{
    //
    // Check the arguments.
    //
    ASSERT(AES_isBaseValid(base));

    //
    // Set Initialization vector.
    //
    HWREG_BP(base + AES_O_IV_IN_OUT_0) = iniVector[0U];
    HWREG_BP(base + AES_O_IV_IN_OUT_1) = iniVector[1U];
    HWREG_BP(base + AES_O_IV_IN_OUT_2) = iniVector[2U];
    HWREG_BP(base + AES_O_IV_IN_OUT_3) = iniVector[3U];
}

//*****************************************************************************
//
// AES_readInitializationVector
//
//*****************************************************************************
void
AES_readInitializationVector(uint32_t base, uint32_t iniVector[])
{
    //
    // Check the arguments.
    //
    ASSERT(AES_isBaseValid(base));

    //
    // Read the initialization vector
    //
    iniVector[0U] = HWREG_BP(base + AES_O_IV_IN_OUT_0);
    iniVector[1U] = HWREG_BP(base + AES_O_IV_IN_OUT_1);
    iniVector[2U] = HWREG_BP(base + AES_O_IV_IN_OUT_2);
    iniVector[3U] = HWREG_BP(base + AES_O_IV_IN_OUT_3);

}

//*****************************************************************************
//
// AES_readTag
//
//*****************************************************************************
void
AES_readTag(uint32_t base, uint32_t tagData[])
{
    //
    // Check the arguments.
    //
    ASSERT(AES_isBaseValid(base));

    //
    // Wait for the output context to be ready.
    //
    while((AES_CTRL_SVCTXTRDY & (HWREG_BP(base + AES_O_CTRL))) !=
          AES_CTRL_SVCTXTRDY)
    {
    }

    //
    // Read the tag data.
    //
    tagData[0U] = HWREG_BP(base + AES_O_TAG_OUT_0);
    tagData[1U] = HWREG_BP(base + AES_O_TAG_OUT_1);
    tagData[2U] = HWREG_BP(base + AES_O_TAG_OUT_2);
    tagData[3U] = HWREG_BP(base + AES_O_TAG_OUT_3);
}

//*****************************************************************************
//
// AES_readDataNonBlocking
//
//*****************************************************************************
bool
AES_readDataNonBlocking(uint32_t base, uint32_t destArray[])
{
    bool temp;

    //
    // Check the arguments.
    //
    ASSERT(AES_isBaseValid(base));

    //
    // Check if the output is ready before reading the data.  If it is not
    // ready, return false.
    //
    if((AES_CTRL_OUTPUT_READY & (HWREG_BP(base + AES_O_CTRL))) !=
        AES_CTRL_OUTPUT_READY)
    {
        temp = false;
    }
    else
    {
        //
        // Read a block of data from the data registers
        //
        destArray[0U] = HWREG_BP(base + AES_O_DATA_IN_OUT_3);
        destArray[1U] = HWREG_BP(base + AES_O_DATA_IN_OUT_2);
        destArray[2U] = HWREG_BP(base + AES_O_DATA_IN_OUT_1);
        destArray[3U] = HWREG_BP(base + AES_O_DATA_IN_OUT_0);

        //
        // Read successful, return true.
        //
        temp = true;
    }
    return(temp);
}

//*****************************************************************************
//
// AES_readDataBlocking
//
//*****************************************************************************
void
AES_readDataBlocking(uint32_t base, uint32_t destArray[])
{
    //
    // Check the arguments.
    //
    ASSERT(AES_isBaseValid(base));

    //
    // Wait for the output to be ready before reading the data.
    //
    while((AES_CTRL_OUTPUT_READY & (HWREG_BP(base + AES_O_CTRL))) !=
           AES_CTRL_OUTPUT_READY)
    {
    }

    //
    // Read a block of data from the data registers
    //
    destArray[0U] = HWREG_BP(base + AES_O_DATA_IN_OUT_3);
    destArray[1U] = HWREG_BP(base + AES_O_DATA_IN_OUT_2);
    destArray[2U] = HWREG_BP(base + AES_O_DATA_IN_OUT_1);
    destArray[3U] = HWREG_BP(base + AES_O_DATA_IN_OUT_0);
}

//*****************************************************************************
//
// AES_writeDataNonBlocking
//
//*****************************************************************************
bool
AES_writeDataNonBlocking(uint32_t base, const uint32_t srcArray[])
{
    bool temp;

    //
    // Check the arguments.
    //
    ASSERT(AES_isBaseValid(base));

    //
    // Check if the input is ready.  If not, then return false.
    //
    if((AES_CTRL_INPUT_READY & (HWREG_BP(base + AES_O_CTRL))) !=
        AES_CTRL_INPUT_READY)
    {
        temp = false;
    }
    else
    {
        //
        // Write a block of data into the data registers.
        //
        HWREG_BP(base + AES_O_DATA_IN_OUT_3) = srcArray[0U];
        HWREG_BP(base + AES_O_DATA_IN_OUT_2) = srcArray[1U];
        HWREG_BP(base + AES_O_DATA_IN_OUT_1) = srcArray[2U];
        HWREG_BP(base + AES_O_DATA_IN_OUT_0) = srcArray[3U];

        //
        // Write successful, return true.
        //
        temp = true;
    }
    return(temp);
}

//*****************************************************************************
//
// AES_writeDataBlocking
//
//*****************************************************************************
void
AES_writeDataBlocking(uint32_t base, const uint32_t srcArray[])
{
    //
    // Check the arguments.
    //
    ASSERT(AES_isBaseValid(base));

    //
    // Wait for input ready.
    //
    while((AES_CTRL_INPUT_READY & (HWREG_BP(base + AES_O_CTRL))) !=
          AES_CTRL_INPUT_READY)
    {
    }

    //
    // Write a block of data into the data registers.
    //
    HWREG_BP(base + AES_O_DATA_IN_OUT_3) = srcArray[0U];
    HWREG_BP(base + AES_O_DATA_IN_OUT_2) = srcArray[1U];
    HWREG_BP(base + AES_O_DATA_IN_OUT_1) = srcArray[2U];
    HWREG_BP(base + AES_O_DATA_IN_OUT_0) = srcArray[3U];
}

//*****************************************************************************
//
// AES_processData
//
//*****************************************************************************
bool
AES_processData(uint32_t base, const uint32_t srcArray[], uint32_t destArray[],
                uint64_t dataLength)
{
    uint64_t count;

    //
    // Check the arguments.
    //
    ASSERT(AES_isBaseValid(base));

    //
    // Write the length register first, which triggers the engine to start
    // using this context.
    //
    AES_setDataLength(base, dataLength);

    //
    // Now loop until the blocks are written.
    //
    for(count = 0U; count < ((dataLength + 15U) / 16U); count++)
    {
        //
        // Write the data registers.
        //
        AES_writeDataBlocking(base, srcArray + (count * 4U));

        //
        // Read the data registers.
        //
        AES_readDataBlocking(base, destArray + (count * 4U));
    }

    //
    // Return true to indicate successful completion of the function.
    //
    return((bool)true);
}

//*****************************************************************************
//
// AES_authenticateData
//
//*****************************************************************************
bool
AES_authenticateData(uint32_t base, const uint32_t srcArray[],
                     uint64_t dataLength, uint32_t tagArray[])
{
    uint32_t count;

    //
    // Check the arguments.
    //
    ASSERT(AES_isBaseValid(base));

    //
    // Write the length register first, which triggers the engine to start
    // using this context.
    //
    AES_setDataLength(base, dataLength);

    //
    // Now loop until the blocks are written.
    //
    for(count = 0U; count < ((dataLength + 15U) / 16U); count++)
    {
        //
        // Write the data registers.
        //
        AES_writeDataBlocking(base, srcArray + (count * 4U));
    }

    //
    // Read the hash tag value.
    //
    AES_readTag(base, tagArray);

    //
    // Return true to indicate successful completion of the function.
    //
    return((bool)true);
}

//*****************************************************************************
//
// AES_processDatainAuthMode
//
//*****************************************************************************
bool
AES_processDatainAuthMode(uint32_t base, const uint32_t srcArray[],
                          uint32_t destArray[], uint64_t dataLength,
                          const uint32_t authDataArray[],
                          uint32_t authDataLength, uint32_t tagArray[])
{
    uint32_t count;

    //
    // Check the arguments.
    //
    ASSERT(AES_isBaseValid(base));

    //
    // Set the data length.
    //
    AES_setDataLength(base, dataLength);

    //
    // Set the additional authentication data length.
    //
    AES_setAuthDataLength(base, authDataLength);

    //
    // Now loop until the authentication data blocks are written.
    //
    for(count = 0U; count < ((authDataLength  + 15U) / 16U); count++)
    {
        //
        // Write the data registers.
        //
        AES_writeDataBlocking(base, authDataArray + (count * 4U));
    }

    //
    // Now loop until the data blocks are written.
    //
    for(count = 0U; count < ((dataLength + 15U) / 16U); count++)
    {
        //
        // Write the data registers.
        //
        AES_writeDataBlocking(base, srcArray + (count * 4U));

        //
        //
        // Read the data registers.
        //
        AES_readDataBlocking(base, destArray + (count * 4U));
    }

    //
    // Read the hash tag value.
    //
    AES_readTag(base, tagArray);

    //
    // Return true to indicate successful completion of the function.
    //
    return((bool)true);
}

//*****************************************************************************
//
// AES_getInterruptStatus
//
//*****************************************************************************
uint32_t
AES_getInterruptStatus(uint32_t base, bool intMask)
{
    uint32_t intStatus, intEnable, temp;

    //
    // Check the arguments.
    //
    ASSERT(AES_isBaseValid(base));

    //
    // Read the IRQ status register and return the value.
    //
    intStatus = HWREG_BP(base + AES_O_IRQSTATUS);
    if(intMask)
    {
        intEnable = HWREG_BP(base + AES_O_IRQENABLE);
        temp = intStatus & intEnable;
    }
    else
    {
        temp = intStatus;
    }
    return(temp);
}

