//------------------------------------------------------------------------------
// File:          aes_hardware.c
// Function:      Firmware encryption using hardware acceleration
// Supported chip(s):
//    - AT91SAM7XC128
//    - AT91SAM7XC256
// Supported toolchain(s):
//    - IAR Embedded Workbench
// Date created:  07 June 2006
// Created by:    JJo
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// Includes
//------------------------------------------------------------------------------

#include "aes_hardware.h"

#if defined(USE_ENCRYPTION) && defined(ENCRYPTION_AES_HARD)

//------------------------------------------------------------------------------
// Inline functions
//------------------------------------------------------------------------------

/**
 * Name:     ASCII2Hex
 * Purpose:  Converts an ASCII value to an hexadecimal one
 * Input(s):
 *  - ASCII string
 *  - Buffer to store integer value
 *  - Length of string
 */
inline void ASCII2Hex(const unsigned char * ascii, unsigned char * binary, unsigned int length) {
  
  for (unsigned int i=0; i < length; i++) {

    if (ascii[i*2] >= 'A') {

      binary[i] = ascii[i*2] - 'A' + 10;
    }
    else {

      binary[i] = ascii[i*2] - '0';
    }

    binary[i] <<= 4;
  
    if (ascii[i*2+1] >= 'A') {

      binary[i] += ascii[i*2+1] - 'A' + 10;
    }
    else {

      binary[i] += ascii[i*2+1] - '0';
    }
  }
}

//------------------------------------------------------------------------------
// Functions
//------------------------------------------------------------------------------

/**
 * Name:     aes_hard_init
 * Purpose:  Initializes the AES peripheral
 */
void aes_hard_init(void) {
  
  // Local variables
  unsigned char key[16];
  
#if defined(ENCRYPTION_CTR) || defined(ENCRYPTION_CBC)
  unsigned char IV[16];
#endif
  
  debug_printf("AES/HARD: Initializing ...\n");
  
  // Activate peripheral clock
  AT91F_AES_CfgPMC();
  
  // Load mode
#if defined(ENCRYPTION_ECB)
  AT91F_AES_CfgModeReg(AT91C_BASE_AES, AT91C_AES_SMOD_PDC | AT91C_AES_OPMOD_ECB);
#elif defined(ENCRYPTION_CBC)
  AT91F_AES_CfgModeReg(AT91C_BASE_AES, AT91C_AES_SMOD_PDC | AT91C_AES_OPMOD_CBC);
#elif defined(ENCRYPTION_CTR)
  AT91F_AES_CfgModeReg(AT91C_BASE_AES, AT91C_AES_SMOD_PDC | AT91C_AES_OPMOD_CTR);
#endif
  
  // Convert and load key
  ASCII2Hex(ENCRYPTION_KEY, key, ENCRYPTION_KEY_LENGTH);
  
  AT91C_BASE_AES->AES_KEYWxR[0] = ((int *) key)[0];
  AT91C_BASE_AES->AES_KEYWxR[1] = ((int *) key)[1];
  AT91C_BASE_AES->AES_KEYWxR[2] = ((int *) key)[2];
  AT91C_BASE_AES->AES_KEYWxR[3] = ((int *) key)[3];
  
  // Convert and load IV
#if defined(ENCRYPTION_CTR) || defined(ENCRYPTION_CBC)
  ASCII2Hex(ENCRYPTION_IV, IV, ENCRYPTION_BLOCK_LENGTH);
    
  AT91C_BASE_AES->AES_IVxR[0] = ((int *) IV)[0];
  AT91C_BASE_AES->AES_IVxR[1] = ((int *) IV)[1];
  AT91C_BASE_AES->AES_IVxR[2] = ((int *) IV)[2];
  AT91C_BASE_AES->AES_IVxR[3] = ((int *) IV)[3];
#endif

  debug_printf("AES/HARD: Initialization done.\n");
}

/**
 * Name:     aes_hard_cleanup
 * Purpose:  Cleans up the AES peripheral
 */
void aes_hard_cleanup(void) {
  
  debug_printf("AES/HARD: Cleaning up ...\n");
  
  AT91F_AES_CfgModeReg(AT91C_BASE_AES, 0);
  AT91F_PMC_DisablePeriphClock(AT91C_BASE_PMC, 1 << AT91C_ID_AES);
  
  // Close PDC
  AT91F_PDC_Close(AT91C_BASE_PDC_AES);

  debug_printf("AES/HARD: Cleanup done.\n");
}

/**
 * Name:     aes_hard_decrypt
 * Purpose:  Decrypts a variable-length cipher text
 * Input(s):
 *  - Cipher text to decrypt
 *  - Buffer to store plain text
 *  - Length of cipher text (in bytes)
 */
int aes_hard_decrypt(const unsigned char * cipherText,
                     unsigned char * plainText,
                     unsigned int length) {
  
  // Check parameters
  if ((cipherText == NULL) || (plainText == NULL)) {
    
    return ERROR;
  }
  
  // Set source and destination buffers in PDC
  AT91C_BASE_PDC_AES->PDC_TPR = (unsigned int) cipherText;
  AT91C_BASE_PDC_AES->PDC_RPR = (unsigned int) plainText;

  AT91C_BASE_PDC_AES->PDC_TCR = length >> 2;
  AT91C_BASE_PDC_AES->PDC_RCR = length >> 2;
  
  // Start decryption and wait
  AT91C_BASE_PDC_AES->PDC_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;
  while ((AT91F_AES_GetStatus(AT91C_BASE_AES) & AT91C_AES_ENDRX) == 0);
  
  return OK;
}

#endif // defined(USE_ENCRYPTION) && defined(ENCRYPTION_AES_HARD)
