/**
 *
 * @file bl_boot_verify.c
 *
 * @ingroup generic_bootloader_8bit
 *
 * @brief This source file provides the options verification schemes which can be used by the bootloader to verify application image.
 *
 * @version BOOTLOADER Driver Version 3.0.0
 */

${disclaimer}

#include <stdint.h>
#include <stdbool.h>
#include "../bl_bootload.h"


<#if (VERIFY == "CRC16_VERIFICATION" || VERIFY== "CRC32_VERIFICATION" || VERIFY== "CHECKSUM_VERIFICATION")>
<#if (VERIFY == "CRC16_VERIFICATION" || VERIFY == "CRC32_VERIFICATION")>
#define CRC_POLYNOMIAL    (${CRC_POLYNOMIAL_U}U)
#define CRC_SEED          (${CRC_SEED_U}U)
</#if>
<#if (VERIFY == "CRC32_VERIFICATION")>
#define CRC_XorOut        (${CRC_XOR_U}U)
</#if>

typedef enum
{
    OK,
    FAIL,
    ERROR
} validation_status_t;

<#if (VERIFY == "CRC16_VERIFICATION")>
static void BL_CalculateCRC16_CCITT(flash_address_t startAddress, uint32_t length, uint16_t *crcSeed);
static validation_status_t BL_ValidateCRC16_CCITT(flash_address_t startAddress, uint32_t length, flash_address_t crcAddress);
</#if>
<#if (VERIFY == "CRC32_VERIFICATION")>
static void BL_CalculateCRC32(flash_address_t startAddress, uint32_t length, uint32_t *crcSeed);
static validation_status_t BL_ValidateCRC32(flash_address_t startAddress, uint32_t length, flash_address_t crcAddress);
</#if>
<#if (VERIFY == "CHECKSUM_VERIFICATION")>
static void BL_CalculateChecksum(flash_address_t startAddress, uint32_t length, uint16_t *checkSum);
static validation_status_t BL_ValidateChecksum(flash_address_t startAddress, uint32_t length, flash_address_t checkAddress);
</#if>
</#if>

<#if (VERIFY == "ECDSA_SHA256_VERIFICATION")>
#include "../sha256.h"
#include "../../CryptoAuthenticationLibrary/basic/atca_basic.h"
#include "../../CryptoAuthenticationLibrary/atca_iface.h"
#include "../../CryptoAuthenticationLibrary/atca_status.h"

#define BITS_IN_A_BYTE                      (8)
#define SIZE_OF_ADDRESS                     (4)
#define SIZE_OF_COMBINED_APP_CODE_ADDRESS   (8)
#define EMPTY_ADDRESS                       (0xffffffff)

static uint32_t BL_Read4BytesFromFlash(uint32_t readAddress);
static struct BOOT_VERIFY_APPLICATION_FOOTER BL_applicationFooterRead();

static uint32_t applicationFooterAddress = BL_APPLICATION_FOOTER_START_ADDRESS_CRC16;

static const uint8_t publicKey[64] = {
    0xb7,0x35,0x87,0x9a,0xc1,0x59,0x6f,0xd3,
    0xca,0x5f,0x45,0x29,0xf6,0x1f,0x11,0xc6,
    0x7b,0xcf,0x8e,0xa2,0x04,0xdf,0x19,0x79,
    0x87,0x33,0xdb,0x0a,0xc4,0x52,0x3d,0xe5,
    0x91,0x5f,0xf3,0x5a,0x1f,0x7a,0x19,0x9f,
    0xf3,0xbe,0x05,0x32,0xd1,0x85,0xbe,0xe0,
    0x9f,0x96,0xf0,0x0f,0xee,0xc1,0x98,0x23,
    0x5f,0x2e,0x9d,0x44,0xf8,0x31,0x3f,0x92
};

static uint32_t BL_Read4BytesFromFlash(uint32_t readAddress)
{
    uint8_t byteReadFromFlash; 
    uint32_t valueReadFromAddress; 
    uint8_t bytesToRead = 0;  
    
    valueReadFromAddress = 0;
    byteReadFromFlash = 0;
        
    for(bytesToRead =0; bytesToRead<4; bytesToRead++)
    {
<#if DEVICE_TYPE == "PIC">        
        byteReadFromFlash = FLASH_Read((uint32_t)readAddress++); 
<#elseif DEVICE_TYPE == "AVR">   
        byteReadFromFlash = FLASH_Read((uint32_t)readAddress++);
</#if>      
           
        valueReadFromAddress = ((valueReadFromAddress) | ((uint32_t)byteReadFromFlash<<(bytesToRead*BITS_IN_A_BYTE)));                    
    } 
    return valueReadFromAddress;
}


static struct BOOT_VERIFY_APPLICATION_FOOTER BL_applicationFooterRead()
{
    struct BOOT_VERIFY_APPLICATION_FOOTER readApplicationFooter;
    uint8_t countBytesToRead;
    uint32_t applicationFooterHeadLocation;

    applicationFooterHeadLocation = applicationFooterAddress;            
    readApplicationFooter.startAddress = 0;
    readApplicationFooter.endAddress = 0;     
    
    readApplicationFooter.startAddress = BL_Read4BytesFromFlash(applicationFooterHeadLocation + ECDSA_SIGNATURE_LENGTH );
    readApplicationFooter.endAddress = BL_Read4BytesFromFlash(applicationFooterHeadLocation + ECDSA_SIGNATURE_LENGTH + SIZE_OF_ADDRESS);    

    for(countBytesToRead=0; countBytesToRead<ECDSA_SIGNATURE_LENGTH; countBytesToRead++)
    {
<#if DEVICE_TYPE == "PIC">  
        readApplicationFooter.signature[countBytesToRead] = FLASH_Read((uint32_t)(applicationFooterHeadLocation++));
<#elseif DEVICE_TYPE == "AVR">
        readApplicationFooter.signature[countBytesToRead] = FLASH_Read((uint32_t)(applicationFooterHeadLocation++));
</#if> 
    
    
                 
    }

    return readApplicationFooter;
}
</#if>

<#if VERIFY == "CRC16_VERIFICATION">
/**
 * @brief This is the main mathematical function that performs the CRC algorithm 
 *      with the configured data types and polynomial depending on the type of CRC we want 
 * @param [in] startAddress - starting address of FLASH memory region to be considered for CRC
 * @param [in] length - number of bytes from startAddress to be considered for CRC
 * @param [in,out] crcSeed - pointer to initial and final CRC
 */
static void BL_CalculateCRC16_CCITT(flash_address_t startAddress, uint32_t length, uint16_t *crcSeed)
{
<#if (CHIP_TYPE == "PIC18" || DEVICE_TYPE == "AVR")>
    uint32_t byteIndex;
    uint8_t readByte;

    flash_address_t startOfPageAddress = FLASH_PageAddressGet(startAddress);

    for (byteIndex = 0U; byteIndex < length; byteIndex++)
    {
        readByte = FLASH_Read(startOfPageAddress + byteIndex);
        *crcSeed ^= ((uint16_t) readByte << 8U);
</#if>
<#if (CHIP_TYPE == "PIC16")>
    uint32_t wordIndex;
    uint8_t byteIndex;
    uint16_t readWord;
    uint8_t byteArr[2];

    for (wordIndex = 0U; wordIndex < length; wordIndex++)
    {
        readWord = FLASH_Read(startAddress + wordIndex);
        byteArr[1] = (uint8_t) (readWord >> 8U);
        byteArr[0] = (uint8_t) (readWord);
        /* Bring the next byte into the checksum. */
        for (byteIndex = 0U; byteIndex < 2U; byteIndex++)
        {
            *crcSeed ^= (byteArr[byteIndex] << 8U);
</#if>
            for (uint8_t bit = 8U; bit > 0U; --bit)
            {
                if ((*crcSeed & ((uint16_t) 1U << 15U)) != 0U)
                {
                    *crcSeed = (*crcSeed << 1U) ^ CRC_POLYNOMIAL;
                }
                else
                {
                    *crcSeed <<= 1U;
                }
            }
        }
<#if (CHIP_TYPE == "PIC16")>
    }
</#if>
}

/**
 * @brief This function both calculated a new CRC on the APP section and compares it 
 *       to the CRC that is calculated and stored by the postBuild script in the 
 *       end application project
 * @param [in] startAddress - starting address of FLASH memory region to be considered for CRC
 * @param [in] length - number of bytes from startAddress to be considered for CRC
 * @param [in] crcAddress - pointer to initial and final CRC
 * @retval validation_status_t - OK if CRC passes; 
 *                          FAIL if validation did not pass;
 *                          ERROR if memory reference issue occured;
 */
static validation_status_t BL_ValidateCRC16_CCITT(flash_address_t startAddress, uint32_t length, flash_address_t crcAddress)
{
    validation_status_t testStatus;
    uint16_t crc = CRC_SEED;
    uint16_t refCRC;
    bool refAddrInsideEvaluatedArea = (((crcAddress + 1U) >= startAddress) && (crcAddress < (startAddress + length)));
    bool refAddrOutsideFlash = ((crcAddress + 1U) >= PROGMEM_SIZE);

    if ((length == 0U) || ((startAddress + length) > PROGMEM_SIZE))
    {
        testStatus = ERROR;
    }
    else if (refAddrInsideEvaluatedArea || refAddrOutsideFlash)
    {
        testStatus = ERROR;
    }
    else
    {
        BL_CalculateCRC16_CCITT(startAddress, length, &crc);
<#if (CHIP_TYPE == "PIC16")>
        refCRC = (uint16_t) (
                        ((uint8_t) FLASH_Read(crcAddress)) |
                        ((uint8_t) FLASH_Read(crcAddress + 1U) << 8U)
                        );
<#else>
        refCRC = (uint16_t) (
                ((uint16_t) FLASH_Read(crcAddress)) |
                ((uint16_t) FLASH_Read(crcAddress + 1U) << 8U)
                );
</#if>
        if (refCRC != crc)
        {
            testStatus = FAIL;
        }
        else
        {
            testStatus = OK;
        }
    }

    return testStatus;
}
</#if>
<#if (VERIFY == "CRC32_VERIFICATION")>
/**
 * @brief This function calculates a crc32 on the range of addresses and updates the value in crcSeed
 * @param [in] startAddress - starting address of FLASH memory region to be considered for CRC
 * @param [in] length - number of bytes from startAddress to be considered for CRC
 * @param [in,out] crcSeed - pointer to initial and final CRC
 */
static void BL_CalculateCRC32(flash_address_t startAddress, uint32_t length, uint32_t *crcSeed)
{
<#if (CHIP_TYPE == "PIC18" || DEVICE_TYPE == "AVR")>
    uint32_t byteIndex;
    uint8_t readByte;

    for (byteIndex = 0U; byteIndex < length; byteIndex++)
    {
        readByte = FLASH_Read(startAddress + byteIndex);
        
        *crcSeed ^= (uint32_t) readByte;
<#else>
    uint16_t wordIndex;
    uint16_t byteIndex;
    uint16_t readWord;
    uint8_t byteArr[2];

    for (wordIndex = 0U; wordIndex < length; wordIndex++)
    {
        readWord = FLASH_Read(startAddress + wordIndex);
        byteArr[1] = (uint8_t) (readWord >> 8U);
        byteArr[0] = (uint8_t) readWord;
        /* Bring next byte into the checksum. */
        for (byteIndex = 0U; byteIndex < 2U; byteIndex++)
        {
            *crcSeed ^= byteArr[byteIndex];
</#if>
        for (uint8_t bit = 8U; bit > 0U; --bit)
        {
            if ((*crcSeed & 0x01U) != 0U)
            {
                *crcSeed = (*crcSeed >> 1U) ^ CRC_POLYNOMIAL;
            }
            else
            {
                *crcSeed >>= 1U;
            }
        }
    }
<#if CHIP_TYPE == "PIC16">
    }
</#if>
    *crcSeed ^= CRC_XorOut;
}

/**
 * @brief This function both calculated a new CRC on the APP section and compares it 
 *      to the CRC that is calculated and stored by the postBuild script in the 
 *      end application project
 * @param [in] startAddress - starting address of FLASH memory region to be considered for CRC
 * @param [in] length - number of bytes from startAddress to be considered for CRC
 * @param [in] refAddress - pointer to initial and final CRC
 * @retval validation_status_t - OK if CRC passes; 
 *                          FAIL if validation did not pass;
 *                          ERROR if memory reference issue occured;
 */
static validation_status_t BL_ValidateCRC32(flash_address_t startAddress, uint32_t length, flash_address_t refAddress)
{
    validation_status_t testStatus;
    uint32_t crc = CRC_SEED;
    volatile uint32_t refCRC;
    bool refAddrInsideEvaluatedArea = (((refAddress + 3U) >= startAddress) && (refAddress < (startAddress + length)));
    bool refAddrOutsideFlash = ((refAddress + 3U) >= PROGMEM_SIZE);

    if ((length == 0U) || ((startAddress + length) > PROGMEM_SIZE))
    {
        testStatus = ERROR;
    }
    else if (refAddrInsideEvaluatedArea || refAddrOutsideFlash)
    {
        testStatus = ERROR;
    }
    else
    {
        BL_CalculateCRC32(startAddress, length, &crc);
<#if (CHIP_TYPE == "PIC16")>
        refCRC = (uint32_t) (
                (((uint32_t) FLASH_Read(refAddress) & 0x00FFU)) |
                (((uint32_t) FLASH_Read(refAddress + 1U) & 0x00FFU) << 8U) |
                (((uint32_t) FLASH_Read(refAddress + 2U) & 0x00FFU) << 16U) |
                (((uint32_t) FLASH_Read(refAddress + 3U) & 0x00FFU) << 24U)
                );
<#else>
        refCRC = (uint32_t) (
                (((uint32_t) FLASH_Read(refAddress))) |
                (((uint32_t) FLASH_Read(refAddress + 1U)) << 8U) |
                (((uint32_t) FLASH_Read(refAddress + 2U)) << 16U) |
                (((uint32_t) FLASH_Read(refAddress + 3U)) << 24U)
                );
</#if>
        if (refCRC != crc)
        {
            testStatus = FAIL;
        }
        else
        {
            testStatus = OK;
        }
    }
    return testStatus;
}
</#if>

<#if VERIFY == "CHECKSUM_VERIFICATION">
// Checksum validation/calculation functions
static void BL_CalculateChecksum(flash_address_t startAddress, uint32_t length, uint16_t *checkSum)
{
<#if CHIP_TYPE == "PIC18" || DEVICE_TYPE == "AVR">
    for (uint32_t i = 0; i < length; i += 2U)
    {
        *checkSum += (uint16_t)FLASH_Read(startAddress);
        startAddress++;
        *checkSum += ((uint16_t)FLASH_Read(startAddress)) << 8;
        startAddress++;
    }
</#if>
<#if CHIP_TYPE == "PIC16">
    for (uint32_t i = 0; i < length; i += 1U)
    {
        *checkSum += (uint16_t)FLASH_Read(startAddress);
        startAddress++;
    }
</#if>
}

/**
 * @brief This function validates the checksum on the APP section and compares it 
 *      to the checksum that is calculated and stored by the postBuild script in the 
 *      end application project
 * @param [in] startAddress - starting address of FLASH memory region to be considered for Checksum
 * @param [in] length - number of bytes from startAddress to be considered for Checksum
 * @param [in] checkAddress - pointer to initial and final Checkum
 * @retval validation_status_t - OK if passes; 
 *                          FAIL if validation did not pass;
 *                          ERROR if memory reference issue occured;
 */
static validation_status_t BL_ValidateChecksum(flash_address_t startAddress, uint32_t length, flash_address_t checkAddress)
{

    validation_status_t status = ERROR;
    uint16_t refChecksum = 0;
    uint16_t check_sum = 0;

    bool refAddrInsideEvaluatedArea = (((checkAddress + 1U) >= startAddress) && (checkAddress < (startAddress + length)));
    bool refAddrOutsideFlash = ((checkAddress + 1U) >= PROGMEM_SIZE);

    if ((length == 0U) || ((startAddress + length) > PROGMEM_SIZE))
    {
        status = ERROR;
    }
    else if (refAddrInsideEvaluatedArea || refAddrOutsideFlash)
    {
        status = ERROR;
    }
    else
    {
        BL_CalculateChecksum(startAddress, length, &check_sum);
<#if CHIP_TYPE == "PIC18" || DEVICE_TYPE == "AVR">
        refChecksum = (uint16_t)(FLASH_Read(checkAddress+1U) << 8U | FLASH_Read(checkAddress));
</#if>
<#if CHIP_TYPE == "PIC16">
        refChecksum = (uint16_t)(
                (FLASH_Read(checkAddress+1U) & 0x00FFU) << 8U 
                | (FLASH_Read(checkAddress) & 0x00FFU)
                );
</#if>
        if (refChecksum != check_sum)
        {
            status = FAIL;
        }
        else
        {
            status = OK;
        }
    }

    return status;
}
</#if>

bool BL_bootVerify(void)
{
    bool retVal;
<#if VERIFY == "ECDSA_SHA256_VERIFICATION">
    struct BOOT_VERIFY_APPLICATION_FOOTER applicationFooter;
    uint8_t sha256Result[32];
    ATCA_STATUS status;
    bool isVerified;
    
    isVerified =  false;
    
    applicationFooter = BL_applicationFooterRead();   
    
    if(applicationFooter.startAddress != EMPTY_ADDRESS || applicationFooter.endAddress != EMPTY_ADDRESS)
    { 
        SHA256(applicationFooter.startAddress, applicationFooter.endAddress, sha256Result);                     
        status = atcab_verify_extern(sha256Result, applicationFooter.signature, publicKey, &isVerified);        
        if((status == ATCA_SUCCESS) && (isVerified == true))
        {       
            retVal = true;
        }
        else
        {
            retVal = false;
        }
    }
    else
    {
        retVal = false;
    }  
</#if>
<#if VERIFY == "RESET_VECTOR_VERIFICATION">
<#if DEVICE_TYPE == "AVR">
// ******************************************************************
//  Check the start of the application space to see if there is a valid application present
// ******************************************************************
    if (FLASH_Read(START_OF_APP) != NO_APPLICATION)
    {    // Valid Application Found. Come out of the Bootloader
        retVal = true;
    }
    else
    {
        retVal = false;
    }
</#if>
<#if DEVICE_TYPE == "PIC">
// ******************************************************************
//  Check the reset vector to see if there is a valid application present
// ******************************************************************
    flash_address_t address = NEW_RESET_VECTOR;
   
    if (FLASH_Read(address) != NO_APPLICATION)
    {
        retVal = true;
    }
    else
    {
        retVal = false;
    }
</#if>
</#if>
<#if VERIFY == "STATUS_BYTE_VERIFICATION">
<#if DEVICE_TYPE == "AVR">
    // ******************************************************************
    // Check the 'Status Byte Flag' location for a special value
    // Update APPLICATION_VALID defined above for custom checked value 
    // By default; the value (0x42) is used
    // if ((byte) IS NOT APPLICATION_VALID value) Bootloader will Run.
    // ******************************************************************
    if (FLASH_Read(STATUS_ADDRESS) == APPLICATION_VALID)
    {
        retVal = true;
    }
    else
    {
        retVal = false;
    }
</#if>
<#if DEVICE_TYPE == "PIC">
<#if CHIP_TYPE == "PIC16">
    flash_data_t wordRead;

    wordRead = FLASH_Read(STATUS_ADDRESS);

    if ((wordRead & (flash_data_t) 0x00FF) == (flash_data_t) APPLICATION_VALID)
    {
        retVal = true;
    }
    else
    {
        retVal = false;
    }
</#if>
<#if CHIP_TYPE == "PIC18">
// ******************************************************************
//  Check a variable in flash to initiate bootloader
// ******************************************************************
    // This section reads the last location in
    // memory to see if the location is 0x55.
    // if it's 0x55, it runs the application.
    // Any other value runs the bootloader.
    if (FLASH_Read(STATUS_ADDRESS) == APPLICATION_VALID)
    {
        retVal = true;
    }
    else
    {
        retVal = false;
    }
</#if>
</#if>
</#if>
<#if VERIFY == "CRC32_VERIFICATION">
    // ******************************************************************
    // Run a software based CRC on the application space
    // if (true) CRC Passed, Application will start
    // if (false) APPLICATION SPACE is corrupted, Bootloader will run.
    // ******************************************************************

    validation_status_t CRCstatus = BL_ValidateCRC32((flash_address_t)START_OF_APP, CRC_LENGTH, CRC_STORE_ADDRESS);

    if(CRCstatus == OK)
    {
        retVal = true;
    }
    else
    {
        retVal = false;
    }
</#if>
<#if VERIFY == "CRC16_VERIFICATION">
    // ******************************************************************
    // Run a software based CRC on the application space
    // if (true) CRC Passed, Application will start
    // if (false) APPLICATION SPACE is corrupted, Bootloader will run.
    // ******************************************************************

    validation_status_t CRCstatus = BL_ValidateCRC16_CCITT((flash_address_t)START_OF_APP, CRC_LENGTH, CRC_STORE_ADDRESS);

    if(CRCstatus == OK)
    {
        retVal = true;
    }
    else
    {
        retVal = false;
    }

</#if>
<#if VERIFY == "CHECKSUM_VERIFICATION">
// **************************************************************************************
//  Calculate a checksum over the application area and compare to pre-calculated checksum from application image
// **************************************************************************************
    validation_status_t checksumPassed = BL_ValidateChecksum(START_OF_APP, CHECKSUM_LENGTH, CHECKSUM_ADDRESS);

    if (checksumPassed == OK)
    {
        retVal = true;
    }
    else
    {
        retVal = false;
    }
</#if>

    return retVal;
}

