// ******************************************************************************
//
//
//  Version/Date    Comment
//   2026.05.28     - First release
// ******************************************************************************


#include "tm4c129xnczad.h"
#include "mbedtls_config.h"
#include "sha256_alt.h"

#include "utils/ustdlib.h"
#include "utils/uartstdio.h"

#include <stdbool.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>

/* Include your TI Tiva hardware library structures */
#include "inc/hw_memmap.h"
#include "inc/hw_nvic.h"
#include "inc/hw_shamd5.h"
#include "inc/hw_types.h"
#include "driverlib/aes.h"
#include "driverlib/rom_map.h"
#include "driverlib/shamd5.h"
#include "driverlib/sysctl.h"

#include "mbedtls/aes.h"
#include "mbedtls/sha256.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/platform.h"



#ifdef MBEDTLS_SHA256_ALT


// Process an intermediate individual block frame
static void process_hardware_block(mbedtls_sha256_context *ACtx, const uint8_t *ABuffer, uint32_t ALength, bool AIsFinal)
{
  uint32_t LLoop;

  if (ALength == 0)
    return;

  // Enable peripheral clock gating if it was dropped or went idle
  if (ACtx->buffer_first)
    if (!SysCtlPeripheralReady(SYSCTL_PERIPH_CCM0))
    {
      SysCtlPeripheralEnable(SYSCTL_PERIPH_CCM0);
      while(!SysCtlPeripheralReady(SYSCTL_PERIPH_CCM0));
    }

  // Establish basic execution algorithm configurations
  uint32_t LModeRegister = SHAMD5_MODE_ALGO_SHA256;

  // Set initial or previous hash values
  if (ACtx->buffer_first)
    // First block: Tell engine to automatically load standard NIST starting constants
    LModeRegister |= SHAMD5_MODE_ALGO_CONSTANT;
  else
  {
    // Restore previous hash values
    for (LLoop = 0; LLoop < 8; LLoop++)
      HWREG(SHAMD5_BASE + SHAMD5_O_IDIGEST_A + (LLoop * 4)) = ACtx->state[LLoop];
  }
  // We always want a hash to be computed
  if (AIsFinal)
    LModeRegister |= SHAMD5_MODE_CLOSE_HASH;
  HWREG(SHAMD5_BASE + SHAMD5_O_MODE  ) = LModeRegister;
  // Set length (also the trigger to start processing)
  HWREG(SHAMD5_BASE + SHAMD5_O_LENGTH) = ALength;
  // Wait for the hardware engine to be ready to accepts data
  while ((HWREG(SHAMD5_BASE + SHAMD5_O_IRQSTATUS) & SHAMD5_INT_INPUT_READY) == 0) { } ;

  // Feed the data as 'words'
  LLoop = 0;
  do {
    uint32_t LWord;
    memcpy(&LWord, &ABuffer[LLoop], 4);
    HWREG(SHAMD5_BASE + SHAMD5_O_DATA_0_IN + LLoop) = LWord;
    LLoop += 4;
  } while (LLoop < ALength);

  // Wait for hardware engine processing to finish execution loop
  while ((HWREG(SHAMD5_BASE + SHAMD5_O_IRQSTATUS) & SHAMD5_INT_OUTPUT_READY) == 0) { } ;

  // Preserve the hash values
  for (LLoop = 0; LLoop < 8; LLoop++)
    ACtx->state[LLoop] = HWREG(SHAMD5_BASE + SHAMD5_O_IDIGEST_A + (LLoop * 4));
  ACtx->buffer_first = false;
}


/* ================= MBEDTLS API IMPLEMENTATION LAYER ================= */

/**
 * \brief          This function initializes a SHA-256 context.
 *
 * \param ctx      The SHA-256 context to initialize. This must not be \c NULL.
 */
void mbedtls_sha256_init(mbedtls_sha256_context *ctx)
{
    memset(ctx, 0, sizeof(mbedtls_sha256_context));
}



/**
 * \brief          This function clears a SHA-256 context.
 *
 * \param ctx      The SHA-256 context to clear. This may be \c NULL, in which
 *                 case this function returns immediately. If it is not \c NULL,
 *                 it must point to an initialized SHA-256 context.
 */
void mbedtls_sha256_free(mbedtls_sha256_context *ctx)
{
    if (ctx == NULL) {
        return;
    }

    mbedtls_platform_zeroize(ctx, sizeof(mbedtls_sha256_context));
}


/**
 * \brief          This function starts a SHA-224 or SHA-256 checksum
 *                 calculation.
 *
 * \param ctx      The context to use. This must be initialized.
 * \param is224    This determines which function to use. This must be
 *                 either \c 0 for SHA-256, or \c 1 for SHA-224.
 *
 * \note           is224 must be defined accordingly to the enabled
 *                 MBEDTLS_SHA224_C/MBEDTLS_SHA256_C symbols otherwise the
 *                 function will return #MBEDTLS_ERR_SHA512_BAD_INPUT_DATA.
 *
 * \return         \c 0 on success.
 * \return         A negative error code on failure.
 */
int mbedtls_sha256_starts(mbedtls_sha256_context *ctx, int is224)
{
  ctx->buffer_first = true;
  ctx->buffer_index = 0;
  return 0;
}


/**
 * \brief          This function feeds an input buffer into an ongoing
 *                 SHA-256 checksum calculation.
 *
 * \param ctx      The SHA-256 context. This must be initialized
 *                 and have a hash operation started.
 * \param input    The buffer holding the data. This must be a readable
 *                 buffer of length \p ilen Bytes.
 * \param ilen     The length of the input data in Bytes.
 *
 * \return         \c 0 on success.
 * \return         A negative error code on failure.
 */
int mbedtls_sha256_update(mbedtls_sha256_context *ctx,
                          const unsigned char *input,
                          size_t ilen)
{
  if (ilen == 0)
    return 0;

  // Check if we have something in our intermediate buffer
  if (ctx->buffer_index != 0)
  {
    // Calculate how many bytes we can still put in our buffer
    size_t LWriteMaxBufferBytes = SHA_BLOCK_SIZE - ctx->buffer_index;
    // If we can fill -more- than a complete buffer (because of deferred processing)
    if (ilen > LWriteMaxBufferBytes)
    {
      // Copy the part so that our buffer is filled
      memcpy(ctx->buffer + ctx->buffer_index, input, LWriteMaxBufferBytes);
      // Write the whole buffer
      process_hardware_block(ctx, ctx->buffer, SHA_BLOCK_SIZE, false);
      ctx->buffer_index = 0;
      input += LWriteMaxBufferBytes;
      ilen  -= LWriteMaxBufferBytes;
    }
  }

  // Process complete 64-byte structural intervals directly out of user source pointers
  // Note: We use '>' and not '>=' because we need to defer processing until we
  //       know we have all data (_finish is called)
  while (ilen > SHA_BLOCK_SIZE)
  {
    process_hardware_block(ctx, input, SHA_BLOCK_SIZE, false);
    input += SHA_BLOCK_SIZE;
    ilen  -= SHA_BLOCK_SIZE;
  }

  // Copy remaining data into our buffer
  if (ilen > 0)
  {
    memcpy(ctx->buffer + ctx->buffer_index, input, ilen);
    ctx->buffer_index += ilen;
  }

  return 0;
}


/*
 * \brief          This function finishes the SHA-256 operation, and writes
 *                 the result to the output buffer.
 *
 * \param ctx      The SHA-256 context. This must be initialized
 *                 and have a hash operation started.
 * \param output   The SHA-224 or SHA-256 checksum result.
 *                 This must be a writable buffer of length \c 32 bytes
 *                 for SHA-256, \c 28 bytes for SHA-224.
 *
 * \return         \c 0 on success.
 * \return         A negative error code on failure.
 */
int mbedtls_sha256_finish(mbedtls_sha256_context *ctx,
                          unsigned char *output)
{
  // Process final trailing data residue
  process_hardware_block(ctx, ctx->buffer, ctx->buffer_index, true);

  // Return result
  memcpy(output, ctx->state, 32);
  // Clean structural variables
  mbedtls_sha256_free(ctx);
  return 0;
}
#endif






#ifdef MBEDTLS_ENTROPY_HARDWARE_ALT
/**
 * @brief Jenkins One-at-a-Time Hash to diffuse simple predictable counters
 */
static uint32_t jenkins_one_at_a_time(uint32_t val) {
  uint32_t hash = 0;
  uint8_t *bytes = (uint8_t *)&val;
  for (int i = 0; i < 4; i++) {
    hash += bytes[i];
    hash += (hash << 10);
    hash ^= (hash >> 6);
  }
  hash += (hash << 3);
  hash ^= (hash >> 11);
  hash += (hash << 15);
  return hash;
}
#endif



#ifdef MBEDTLS_ENTROPY_HARDWARE_ALT
/**
 * @brief Custom SysTick Entropy poll function for MbedTLS
 * Handles continuous down-counter jitter tracking.
 */
// Use the name MBedTls expects
int mbedtls_hardware_poll(void *data,
                          unsigned char *output, size_t len, size_t *olen)
{
  // Prevent compiler warnings for unused parameter data context
  (void)data;

  size_t bytes_written = 0;

  while (bytes_written < len) {
    // Read the absolute raw hardware value of the 24-bit SysTick counter
    uint32_t raw_tick = HWREG(NVIC_ST_CURRENT);

    // Diffuse the value through the mixing block to avoid sequence matching
    uint32_t mixed_entropy = jenkins_one_at_a_time(raw_tick);

    // Chunk out the bytes directly into the requested target MbedTLS buffer
    size_t chunk = (len - bytes_written > 4) ? 4 : (len - bytes_written);
    uint8_t *p_entropy = (uint8_t *)&mixed_entropy;

    for (size_t i = 0; i < chunk; i++) {
      output[bytes_written++] = p_entropy[i];
    }
  }

  // Explicitly notify MbedTLS exactly how many bytes were generated
  *olen = bytes_written;

  return 0; // Absolute success status
}
#endif



#ifdef MBEDTLS_AES_ALT
void mbedtls_aes_init(mbedtls_aes_context *ctx) {
  memset(ctx, 0, sizeof(mbedtls_aes_context));

  // Enable peripheral clock gating if it was dropped or went idle
  if (!SysCtlPeripheralReady(SYSCTL_PERIPH_CCM0))
  {
    SysCtlPeripheralEnable(SYSCTL_PERIPH_CCM0);
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_CCM0));
  }
}
#endif


#ifdef MBEDTLS_AES_ALT
void mbedtls_aes_free(mbedtls_aes_context *ctx)
{
  if (ctx == NULL) {
    return;
  }
  mbedtls_platform_zeroize(ctx, sizeof(mbedtls_aes_context));
}
#endif


// #ifdef MBEDTLS_SHA256_ALT
// /**
//  * \brief          This function initializes a SHA-256 context.
//  *
//  * \param ctx      The SHA-256 context to initialize. This must not be \c NULL.
//  */
//  void mbedtls_sha256_init(mbedtls_sha256_context *ctx)
// {
//   if (ctx == NULL) return;
//   memset(ctx, 0, sizeof(mbedtls_sha256_context));

//   MAP_SysCtlPeripheralEnable (SYSCTL_PERIPH_CCM0);
//   // Wait for the peipheral to be ready
//   while(!MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_CCM0)) { };
//   // Reset the peripheral to ensure we are starting from a known condition.
//   MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_CCM0);
//   // Wait for the peripheral to be ready again.
//   while (!ROM_SysCtlPeripheralReady(SYSCTL_PERIPH_CCM0)) { };
// }
// #endif


// #ifdef MBEDTLS_SHA256_ALT
// /*
//  * The Context Setup / Starts Routine
//  * Resets the Tiva internal engine to prepare for processing a fresh chunk of incoming data.
//  * In Mbed TLS 3.x, this must return an integer status code (0 for absolute success).
//  */
// /**
//  * \brief          This function starts a SHA-224 or SHA-256 checksum
//  *                 calculation.
//  *
//  * \param ctx      The context to use. This must be initialized.
//  * \param is224    This determines which function to use. This must be
//  *                 either \c 0 for SHA-256, or \c 1 for SHA-224.
//  *
//  * \note           is224 must be defined accordingly to the enabled
//  *                 MBEDTLS_SHA224_C/MBEDTLS_SHA256_C symbols otherwise the
//  *                 function will return #MBEDTLS_ERR_SHA512_BAD_INPUT_DATA.
//  *
//  * \return         \c 0 on success.
//  * \return         A negative error code on failure.
//  */
// int mbedtls_sha256_starts(mbedtls_sha256_context *ctx, int is224)
// {
//   ctx->is224 = is224;

//   /* Fix: Clear the individual array elements of total */
//   ctx->total[0] = 0;
//   ctx->total[1] = 0;

//   MAP_SHAMD5Reset(SHAMD5_BASE);

//   // Configure the SHA/MD5 module algorithm
//   if (is224) then
//     MAP_SHAMD5ConfigSet(SHAMD5_BASE, SHAMD5_ALGO_HMAC_SHA224);
//   else
//     MAP_SHAMD5ConfigSet(SHAMD5_BASE, SHAMD5_ALGO_SHA256);

//   return 0;
// }
// #endif



// #ifdef MBEDTLS_SHA256_ALT
// // Copy from shamd5.c
// static void
// _SHAMD5DataWriteMultiple(uint32_t ui32Base, uint32_t *pui32DataSrc,
//                          uint32_t ui32DataLength)
// {
//     uint32_t ui32Idx, ui32Count;

//     // Calculate the number of blocks of data.
//     ui32Count = ui32DataLength / 64;

//     // Loop through all the blocks and write them into the data registers
//     // making sure to block additional operations until we can write the
//     // next 16 words.
//     for(ui32Idx = 0; ui32Idx < ui32Count; ui32Idx++)
//     {
//         // Write the block of data.
//         MAP_SHAMD5DataWrite(ui32Base, pui32DataSrc);
//         // Increment the pointer to next block of data.
//         pui32DataSrc += 16;
//     }

//     // Calculate the remaining bytes of data that don't make up a full block.
//     ui32Count = ui32DataLength % 64;
//     // If there are bytes that do not make up a whole block, then
//     // write them separately.
//     if(ui32Count)
//     {
//         // Wait until the engine has finished processing the previous block.
//         while((HWREG(ui32Base + SHAMD5_O_IRQSTATUS) &
//                SHAMD5_INT_INPUT_READY) == 0)
//         {
//         }
//         // Loop through the remaining words.
//         for(ui32Idx = 0; ui32Idx < ui32Count; ui32Idx += 4)
//         {
//             // Write the word into the data register.
//             HWREG(ui32Base + SHAMD5_O_DATA_0_IN + ui32Idx) = *pui32DataSrc++;
//         }
//     }
// }
// #endif


// #ifdef MBEDTLS_SHA256_ALT
// /*
//  * Feeds an incoming block fragment array into the hardware engine.
//  */
// /**
//  * \brief          This function feeds an input buffer into an ongoing
//  *                 SHA-256 checksum calculation.
//  *
//  * \param ctx      The SHA-256 context. This must be initialized
//  *                 and have a hash operation started.
//  * \param input    The buffer holding the data. This must be a readable
//  *                 buffer of length \p ilen Bytes.
//  * \param ilen     The length of the input data in Bytes.
//  *
//  * \return         \c 0 on success.
//  * \return         A negative error code on failure.
//  */
// int mbedtls_sha256_update(mbedtls_sha256_context *ctx, const unsigned char *input, size_t ilen)
// {
//   if (ilen == 0) return 0;
//   // set the lenght
//   HWREG(SHAMD5_BASE + SHAMD5_O_LENGTH) = ilen;
//   _SHAMD5DataWriteMultiple(SHAMD5_BASE, (uint32_t *)input, ilen);
//   return 0;
// }
// #endif


// #ifdef MBEDTLS_SHA256_ALT
// /**
//  * \brief          This function finishes the SHA-256 operation, and writes
//  *                 the result to the output buffer.
//  *
//  * \param ctx      The SHA-256 context. This must be initialized
//  *                 and have a hash operation started.
//  * \param output   The SHA-224 or SHA-256 checksum result.
//  *                 This must be a writable buffer of length \c 32 bytes
//  *                 for SHA-256, \c 28 bytes for SHA-224.
//  *
//  * \return         \c 0 on success.
//  * \return         A negative error code on failure.
//  */
//  int mbedtls_sha256_finish(mbedtls_sha256_context *ctx, unsigned char *output)
// {
//   while((HWREG(SHAMD5_BASE + SHAMD5_O_IRQSTATUS) & SHAMD5_INT_OUTPUT_READY) == 0) { };
//   MAP_SHAMD5ResultRead(SHAMD5_BASE, (uint32_t *)output);
//   return 0;
// }
// #endif


// #ifdef MBEDTLS_SHA256_ALT
// /*
//  * This function clears and safely deallocates a hardware SHA-256 context.
//  */
//  /**
//  * \brief          This function clears a SHA-256 context.
//  *
//  * \param ctx      The SHA-256 context to clear. This may be \c NULL, in which
//  *                 case this function returns immediately. If it is not \c NULL,
//  *                 it must point to an initialized SHA-256 context.
//  */
// void mbedtls_sha256_free(mbedtls_sha256_context *ctx)
// {
//   if (ctx == NULL) {
//     return;
//   }
//   mbedtls_platform_zeroize(ctx, sizeof(mbedtls_sha256_context));
// }
// #endif


#ifdef MBEDTLS_AES_ALT
/*
 * Hardware-Accelerated AES-ECB Single-Block processing function.
 */
/**
 * \brief          This function performs an AES single-block encryption or
 *                 decryption operation.
 *
 *                 It performs the operation defined in the \p mode parameter
 *                 (encrypt or decrypt), on the input data buffer defined in
 *                 the \p input parameter.
 *
 *                 mbedtls_aes_init(), and either mbedtls_aes_setkey_enc() or
 *                 mbedtls_aes_setkey_dec() must be called before the first
 *                 call to this API with the same context.
 *
 * \param ctx      The AES context to use for encryption or decryption.
 *                 It must be initialized and bound to a key.
 * \param mode     The AES operation: #MBEDTLS_AES_ENCRYPT or
 *                 #MBEDTLS_AES_DECRYPT.
 * \param input    The buffer holding the input data.
 *                 It must be readable and at least \c 16 Bytes long.
 * \param output   The buffer where the output data will be written.
 *                 It must be writeable and at least \c 16 Bytes long.

 * \return         \c 0 on success.
 */
int mbedtls_aes_crypt_ecb(mbedtls_aes_context *ctx,
                          int mode,
                          const unsigned char input[16],
                          unsigned char output[16])
{
  // Basic Boundary Checks
  if (ctx == NULL || input == NULL || output == NULL) {
    return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
  }

  uint32_t config_flags = 0;

  // Map Operational Direction
  if (mode == MBEDTLS_AES_ENCRYPT) {
    config_flags |= AES_CFG_DIR_ENCRYPT;
  } else if (mode == MBEDTLS_AES_DECRYPT) {
    config_flags |= AES_CFG_DIR_DECRYPT;
  } else {
    return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
  }

  // Configure the Tiva Core for raw Electronic Codebook (ECB) Mode
  config_flags |= AES_CFG_MODE_ECB;

  // Set Hardware Key Size Constraints
  if (ctx->keybits == 128) {
    config_flags |= AES_CFG_KEY_SIZE_128BIT;
  } else if (ctx->keybits == 256) {
    config_flags |= AES_CFG_KEY_SIZE_256BIT;
  } else {
    return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH;
  }

  // Write the Configuration Register Layout to Hardware
  AESConfigSet(AES_BASE, config_flags);

  // Seed the Primary Key 1 Channel via the DriverLib API
  AESKey1Set(AES_BASE, ctx->key, config_flags & (AES_CFG_KEY_SIZE_128BIT | AES_CFG_KEY_SIZE_256BIT));

  // Process the isolated 16-byte data block natively in Tiva silicon
  // Since ECB does not chain outputs, we do not call or read back any IV registers
  AESDataProcess(AES_BASE, (uint32_t *)input, (uint32_t *)output, 16);

  return 0; // Return success
}
#endif


#ifdef MBEDTLS_AES_SETKEY_ENC_ALT
/**
 * \brief          This function sets the encryption key.
 *
 * \param ctx      The AES context to which the key should be bound.
 *                 It must be initialized.
 * \param key      The encryption key.
 *                 This must be a readable buffer of size \p keybits bits.
 * \param keybits  The size of data passed in bits. Valid options are:
 *                 <ul><li>128 bits</li>
 *                 <li>192 bits</li>
 *                 <li>256 bits</li></ul>
 *
 * \return         \c 0 on success.
 * \return         #MBEDTLS_ERR_AES_INVALID_KEY_LENGTH on failure.
 */
 int mbedtls_aes_setkey_enc(mbedtls_aes_context *ctx, const unsigned char *key, unsigned int keybits)
{
  ctx->keybits = keybits;
  memcpy(ctx->key, key, keybits / 8);

  uint32_t keySizeConfig = 0;
  if(keybits == 128)      keySizeConfig = AES_CFG_KEY_SIZE_128BIT;
  else if(keybits == 256) keySizeConfig = AES_CFG_KEY_SIZE_256BIT;

  /* FIX: Pass the module base, key data array, and explicit size layout macro */
  AESKey1Set(AES_BASE, ctx->key, keySizeConfig);
  return 0;
}
#endif


#ifdef MBEDTLS_AES_SETKEY_DEC_ALT
/**
 * \brief          This function sets the decryption key.
 *
 * \param ctx      The AES context to which the key should be bound.
 *                 It must be initialized.
 * \param key      The decryption key.
 *                 This must be a readable buffer of size \p keybits bits.
 * \param keybits  The size of data passed. Valid options are:
 *                 <ul><li>128 bits</li>
 *                 <li>192 bits</li>
 *                 <li>256 bits</li></ul>
 *
 * \return         \c 0 on success.
 * \return         #MBEDTLS_ERR_AES_INVALID_KEY_LENGTH on failure.
 */
 int mbedtls_aes_setkey_dec(mbedtls_aes_context *ctx, const unsigned char *key, unsigned int keybits)
{
  ctx->keybits = keybits;
  memcpy(ctx->key, key, keybits / 8);

  uint32_t keySizeConfig = 0;
  if(keybits == 128)      keySizeConfig = AES_CFG_KEY_SIZE_128BIT;
  else if(keybits == 256) keySizeConfig = AES_CFG_KEY_SIZE_256BIT;

  /* FIX: Pass the module base, key data array, and explicit size layout macro */
  AESKey1Set(AES_BASE, ctx->key, keySizeConfig);
  return 0;
}
#endif


#ifdef MBEDTLS_AES_ALT
/*
 * Hardware-Accelerated AES-CBC Block Cipher processing function.
 * Connects Mbed TLS v3.x directly to the Tiva Corporate Cryptographic Module (CCM).
 */
/**
 * \brief  This function performs an AES-CBC encryption or decryption operation
 *         on full blocks.
 *
 *         It performs the operation defined in the \p mode
 *         parameter (encrypt/decrypt), on the input data buffer defined in
 *         the \p input parameter.
 *
 *         It can be called as many times as needed, until all the input
 *         data is processed. mbedtls_aes_init(), and either
 *         mbedtls_aes_setkey_enc() or mbedtls_aes_setkey_dec() must be called
 *         before the first call to this API with the same context.
 *
 * \note   This function operates on full blocks, that is, the input size
 *         must be a multiple of the AES block size of \c 16 Bytes.
 *
 * \note   Upon exit, the content of the IV is updated so that you can
 *         call the same function again on the next
 *         block(s) of data and get the same result as if it was
 *         encrypted in one call. This allows a "streaming" usage.
 *         If you need to retain the contents of the IV, you should
 *         either save it manually or use the cipher module instead.
 *
 *
 * \param ctx      The AES context to use for encryption or decryption.
 *                 It must be initialized and bound to a key.
 * \param mode     The AES operation: #MBEDTLS_AES_ENCRYPT or
 *                 #MBEDTLS_AES_DECRYPT.
 * \param length   The length of the input data in Bytes. This must be a
 *                 multiple of the block size (\c 16 Bytes).
 * \param iv       Initialization vector (updated after use).
 *                 It must be a readable and writeable buffer of \c 16 Bytes.
 * \param input    The buffer holding the input data.
 *                 It must be readable and of size \p length Bytes.
 * \param output   The buffer holding the output data.
 *                 It must be writeable and of size \p length Bytes.
 *
 * \return         \c 0 on success.
 * \return         #MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH
 *                 on failure.
 */
int mbedtls_aes_crypt_cbc(mbedtls_aes_context *ctx,
                          int mode,
                          size_t length,
                          unsigned char iv[16],
                          const unsigned char *input,
                          unsigned char *output)
{
  // Alignment and Length Validation Constraints
  if (ctx == NULL || iv == NULL || input == NULL || output == NULL) {
    return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
  }
  if (length == 0) {
    return 0; // Nothing to process
  }
  if (length % 16 != 0) {
    return MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH; // CBC mode requires full 16-byte blocks
  }

  uint32_t config_flags = 0;

  config_flags |= AES_CTRL_SAVE_CONTEXT;

  // Map Operational Direction
  if (mode == MBEDTLS_AES_ENCRYPT) {
    config_flags |= AES_CFG_DIR_ENCRYPT;
  } else if (mode == MBEDTLS_AES_DECRYPT) {
    config_flags |= AES_CFG_DIR_DECRYPT;
  } else {
    return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
  }

  // Set Tiva Core Configuration for Cipher Block Chaining (CBC)
  config_flags |= AES_CFG_MODE_CBC;

  // Set Hardware Key Size Constraints
  if (ctx->keybits == 128) {
    config_flags |= AES_CFG_KEY_SIZE_128BIT;
  } else if (ctx->keybits == 256) {
    config_flags |= AES_CFG_KEY_SIZE_256BIT;
  } else {
    return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH;
  }

  // Write Core Configuration Register Layout to Hardware
  AESConfigSet(AES_BASE, config_flags);

  // Seed Key 1 Channel via the DriverLib API
  AESKey1Set(AES_BASE, ctx->key, config_flags & (AES_CFG_KEY_SIZE_128BIT | AES_CFG_KEY_SIZE_256BIT));

  // Pass the 16-byte Initialization Vector (IV) to the Hardware Engine Registers
  AESIVSet(AES_BASE, (uint32_t *)iv);

  // Process Data Stream Natively via Tiva Core Physical Execution Pipelines
  // Note: Tiva DriverLib handles data streaming blocks via 32-bit aligned pointers
  AESDataProcess(AES_BASE, (uint32_t *)input, (uint32_t *)output, (uint32_t)length);

  // CRITICAL FOR STREAMING: Extract Updated Initialization Vector
  // Passes the module base and typecasts the Mbed TLS unsigned char iv array
  // to match TivaWare's expected 32-bit unsigned integer data pointer type.
  AESIVRead(AES_BASE, (uint32_t *)iv);

  return 0; // Success
}
#endif
