/*!
 * @file        bmp561_hmac.c
 *
 * @brief       This file provides all the HMAC firmware functions
 *
 * @version     V1.0.0
 *
 * @date        2025-01-01
 *
 * @attention
 *
 *  Copyright (C) 2025 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be useful 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 SOFTWARE PACKAGE LICENSE for the governing permissions
 *  and limitations under the License.
 */

#include "bmp561_hmac.h"
#include "bmp561_sysctrl.h"
#include "string.h"

/** @addtogroup Peripherals_Library Standard Peripheral Library
  @{
*/

/** @addtogroup HMAC_Driver HMAC Driver
  @{
*/

/** @addtogroup HMAC_Functions Functions
  @{
*/

/*!
 * @brief     Resets the HMAC peripheral registers to their default reset values.
 *
 * @param     None
 *
 * @retval    None
 *
 */
void HMAC_Reset(void)
{
    SYSCTRL_Unlock();
    SYSCTRL_EnableAPBPeriphReset(SYSCTRL_APB_HMAC);
    SYSCTRL_DisableAPBPeriphReset(SYSCTRL_APB_HMAC);
    SYSCTRL_Lock();
}

/*!
 * @brief     Configures HMAC work mode.
 *
 * @param     mode: specifies the HMAC work mode.
 *                  This parameter can be one of the following values:
 *                  @arg HMAC_WK_MODE_NORMAL : Normal mode
 *                  @arg HMAC_WK_MODE_SPECIAL: Special mode
 *
 * @retval    None
 *
 */
void HMAC_ConfigWorkMode(HMAC_WK_MODE_T mode)
{
    HMAC->CR_B.WKMD = mode;
}

/*!
 * @brief     Configures HMAC operation mode.
 *
 * @param     mode: specifies the HMAC operation mode.
 *                  This parameter can be one of the following values:
 *                  @arg HMAC_OP_MODE_HMAC: HMAC-SHA256
 *                  @arg HMAC_OP_MODE_SHA : 512bit input SHA-256
 *
 * @retval    None
 *
 */
void HMAC_ConfigOperationMode(HMAC_OP_MODE_T mode)
{
    HMAC->CR_B.OPMD = mode;
}

/*!
 * @brief     Configures HMAC message length.
 *
 * @param     length: specifies the HMAC message length.
 *                    This parameter can be configured from 1 to 0x100.
 *
 * @retval    None
 *
 * @note      HMAC_OP_HMAC mode, the length of message max value is 256bits.
 *
 */
void HMAC_ConfigMessageLength(uint16_t length)
{
    HMAC->CR_B.MSGLEN = length;
}

/*!
 * @brief     Enables HMAC.
 *
 * @param     None
 *
 * @retval    None
 *
 */
void HMAC_Enable(void)
{
		HMAC->CR_B.HMACE = BIT_SET;
}

/*!
 * @brief     Disables HMAC.
 *
 * @param     None
 *
 * @retval    None
 *
 */
void HMAC_Disable(void)
{
		HMAC->CR_B.HMACE = BIT_RESET;
}

/*!
 * @brief     Starts HMAC calculation.
 *
 * @param     None
 *
 * @retval    None
 *
 */
void HMAC_StartCalc(void)
{
		HMAC->CR_B.START = BIT_SET;
}

/*!
 * @brief     Enables HMAC interrupt.
 *
 * @param     None
 *
 * @retval    None
 *
 */
void HMAC_EnableInterrupt(void)
{
		HMAC->CR_B.HMACIE = BIT_SET;
}

/*!
 * @brief     Disables HMAC interrupt.
 *
 * @param     None
 *
 * @retval    None
 *
 */
void HMAC_DisableInterrupt(void)
{
		HMAC->CR_B.HMACIE = BIT_RESET;
}


/*!
 * @brief     Writes the HMAC 7 KEY registers.
 *
 * @param     pKey: key data pointer.
 *
 * @retval    None
 *
 * @note      HMAC_OP_SHA mode, 7 KEY registers store the source data bit511 - bit256, must be 32 byte.
 *            HMAC_OP_HMAC mode, 7 KEY registers store the key data bit255 - bit0, must be 32 byte.
 */
void HMAC_WriteKey(uint8_t* pKey)
{
    uint8_t i;

    for(i = 0; i < 8; i++)
    {
        HMAC->KEY[i] = (pKey[i * 4 + 0] << 24) | (pKey[i * 4 + 1] << 16) | (pKey[i * 4 + 2] << 8) | pKey[i * 4 + 3];
    }
}

/*!
 * @brief     Writes the HMAC 7 MSG registers.
 *
 * @param     pMsg: message data pointer.
 *
 * @retval    None
 *
 * @note      (1)HMAC_OP_SHA mode, 7 MSG registers store message bit255-bit0. And message length is fixed 256bit.
 *               HMAC_OP_HMAC mode, 7 MSG registers store message bit255-bit0. And message length is optional.
 *
 *            (2)This function will fix 0 to the last of message if message length is not the multiple of 4.
 *               Make sure input message length is the multiple of 4 if don't need fix 0 to the last of message.
 *
 */
void HMAC_WriteMessage(uint8_t* pMsg)
{
    uint8_t msgLen, regNum, residue;
    uint8_t i;

    /** Get message length in HMAC->CR register Bit[5:13]*/
    msgLen = (HMAC->CR >> 5) / 8;
    regNum = msgLen / 4;
    residue = msgLen % 4;

    /** write the message to HMAC->MSGR */
    if(residue)    //!< 1กข2กข3
    {
        HMAC->MSG[8 - regNum - 1] = 0;

        /** write the residue byte */
        for(i = 0; i< residue; i++)
        {
            HMAC->MSG[8 - regNum - 1] <<= 8;
            HMAC->MSG[8 - regNum - 1] |= pMsg[i];
        }

        /** write the full MSGBUFF */
        if(regNum >= 1)
        {
            for(i=0; i<regNum; i++)
            {
                HMAC->MSG[8 - regNum + i] = (pMsg[i * 4 + residue    ] << 24) | (pMsg[i * 4 + residue + 1] << 16) |\
                                            (pMsg[i * 4 + residue + 2] << 8 ) |  pMsg[i * 4 + residue + 3];
            }
        }
    }
    else
    {
        for(i = 0; i < regNum; i++)
        {
            HMAC->MSG[8 - regNum + i] = (pMsg[i * 4 + 0] << 24) | (pMsg[i * 4 + 1] << 16) |\
                                        (pMsg[i * 4 + 2] << 8 ) |  pMsg[i * 4 + 3];
        }
    }
}

/*!
 * @brief     Updates SHA256 group calculate initial value.
 *
 * @param     pRes: initial value data pointer.
 *
 * @retval    None
 *
 * @note      HMAC_OP_SHA mode, before next group calculate, RES7R ~ RES0R should be written to
 *            previous group result.
 *
 */
void HMAC_UpdateInitVal(uint8_t* pRes)
{
    uint8_t i;

    for(i = 0; i < 8; i++)
    {
        HMAC->RES[i] = (pRes[i * 4 + 0] << 24) | (pRes[i * 4 + 1] << 16) | (pRes[i * 4 + 2] << 8) | pRes[i * 4 + 3];
    }
}

/*!
 * @brief     Gets the HMAC calculate result.
 *
 * @param     pRes: calculate result data pointer.
 *
 * @retval    None
 *
 * @note      HMAC_OP_SHA mode, 7 RES registers store the result of one group(512bit).
 *            HMAC_OP_HMAC mode, 7 RES registers store the final result of HMAC.
 *
 */
void HMAC_GetCalcResult(uint8_t* pRes)
{
    uint8_t i,j;

    for(i = 0; i < 8; i++)
    {
        for(j = 0; j < 4; j++)
        {
            pRes[i * 4 + j] = (uint8_t)(HMAC->RES[i] >> (24 - j * 8));
        }
    }
}

/*!
 * @brief     Reads the specified HMAC flag.
 *
 * @param     flag: specifies the HMAC flag.
 *                  This parameter can be one of the following values:
 *                  @arg HMAC_FLAG_INTF : Operate completed interrupt flag
 *                  @arg HMAC_FLAG_DONE : Operate done flag
 *
 * @retval    The new state of flag(SET or RESET).
 *
 */
uint8_t HMAC_ReadFlag(HMAC_FLAG_T flag)
{
    uint8_t status = RESET;

    if((HMAC->SR & flag) != (uint32_t)RESET)
    {
        status = SET;
    }
    else
    {
        status = RESET;
    }
    return status;
}

/*!
 * @brief     Clear the HMAC interrupt flag.
 *
 * @param     None
 *
 * @retval    None
 *
 */
void HMAC_ClearFlag(void)
{
    HMAC->SCR_B.CINTF = BIT_SET;
}

/*!
 * @brief     Calculate HMAC in HMAC mode.
 *
 * @param     pKey: input key pointer.
 *
 * @param     pData: data pointer.
 *
 * @param     dataLen: input data length, unit is byte.
 *
 * @param     result: output result pointer.
 *
 * @retval    None
 *
 * @note      HMAC mode supports up to 32 bytes of data input, and the key length
 *            is fixed at 32 bytes.
 *
 */
void HMAC_CalcHMAC(uint8_t* pKey, uint8_t* pData, uint8_t dataLen, uint8_t* result)
{
    HMAC_Reset();
    HMAC_Enable();
    /** Configure mode and key length */
    HMAC_ConfigOperationMode(HMAC_OP_MODE_HMAC);
    HMAC_ConfigMessageLength(dataLen * 8);
    /** Write key and data */
    HMAC_WriteKey(pKey);
    HMAC_WriteMessage(pData);
    HMAC_EnableInterrupt();
    HMAC_StartCalc();
    /** wait calculate finished */
    while(HMAC_ReadFlag(HMAC_FLAG_DONE) == RESET);
    HMAC_GetCalcResult(result);
    HMAC_ClearFlag();
    HMAC_Disable();
}

/*!
 * @brief     Calculate HMAC in SHA mode.
 *
 * @param     pData: input data pointer.
 *
 * @param     dataLen: input data length, unit is byte.
 *
 * @param     result: output result pointer.
 *
 * @retval    None
 *
 * @note      This function is an example for SHA256.
 *
 */
void HMAC_CalcSHA256(uint8_t* pData, uint16_t dataLen, uint8_t* result)
{
    uint8_t  lastBlock[64 * 2];
    uint16_t i;

    uint8_t  remainder = dataLen % 64;
    uint16_t blockNum  = dataLen / 64;

    HMAC_Reset();
    HMAC_Enable();
    HMAC_ConfigOperationMode(HMAC_OP_MODE_SHA);

    /** Fix message */
    if(remainder < 56)
    {
        memcpy(lastBlock, (pData + 64 * blockNum), remainder);
        lastBlock[remainder] = 0x80;
        memset((lastBlock + remainder + 1), 0x00, (64 - remainder - 1 - 4));

        lastBlock[60] = (dataLen * 8) >> 24;
        lastBlock[61] = (dataLen * 8) >> 16;
        lastBlock[62] = (dataLen * 8) >> 8;
        lastBlock[63] = (dataLen * 8) & 0xFF;

        blockNum += 0x01;

        for(i = 0; i < (blockNum - 1); i++)
        {
            HMAC_WriteKey((pData + i * 64));
            HMAC_WriteMessage((pData + 32 + i * 64));
            HMAC_StartCalc();

            while(HMAC_ReadFlag(HMAC_FLAG_DONE) == RESET);
            HMAC_GetCalcResult(result);
            HMAC_ClearFlag();

            HMAC_UpdateInitVal(result);
        }

        HMAC_WriteKey(lastBlock);
        HMAC_WriteMessage((lastBlock + 32));
    }
    else
    {
        /** Fix block with 0 */
        memcpy(lastBlock,(pData + 64 * blockNum), remainder);
        lastBlock[remainder] = 0x80;
        memset((lastBlock + remainder + 1), 0x00, (64 - 1 - remainder));

        /** Fix another block */
        memset((lastBlock + 64), 0x00, 60);
        lastBlock[124] = (dataLen * 8) >> 24;
        lastBlock[125] = (dataLen * 8) >> 16;
        lastBlock[126] = (dataLen * 8) >> 8;
        lastBlock[127] = (dataLen * 8) & 0xFF;

        blockNum += 0x02;

        for(i = 0; i < (blockNum - 2); i++)
        {
            HMAC_WriteKey((pData + i * 64));
            HMAC_WriteMessage((pData + 32 + i * 64));
            HMAC_StartCalc();

            while(HMAC_ReadFlag(HMAC_FLAG_DONE) == RESET);
            HMAC_GetCalcResult(result);
            HMAC_ClearFlag();

            HMAC_UpdateInitVal(result);
        }

        HMAC_WriteKey(lastBlock);
        HMAC_WriteMessage((lastBlock + 32));

        HMAC_StartCalc();
        while(HMAC_ReadFlag(HMAC_FLAG_DONE) == RESET);
        HMAC_GetCalcResult(result);
        HMAC_ClearFlag();

        HMAC_UpdateInitVal(result);

        HMAC_WriteKey((lastBlock + 64));
        HMAC_WriteMessage((lastBlock + 64 + 32));

    }
    HMAC_StartCalc();
    while(HMAC_ReadFlag(HMAC_FLAG_DONE) == RESET);
    HMAC_GetCalcResult(result);
    HMAC_ClearFlag();

    HMAC_Disable();
}

/**@} end of group HMAC_Functions*/
/**@} end of group HMAC_Driver*/
/**@} end of group Peripherals_Library*/
