/****************************************************************************//**
  \file phy.c

  \brief Specific HW Phy access APIs, for decryption. Currently, the code
         is implemented for decryption, for the sake of code space. It can
         easily be extended later

  \author
    Atmel Corporation: http://www.atmel.com \n
    Support email: avr@atmel.com

  Copyright (c) 2015, Atmel Corporation. All rights reserved.
  Licensed under Atmel's Limited License Agreement (BitCloudTM).

  \internal
  History:
    29/05/14 Srinivasan G - Created
*******************************************************************************/

#if (USE_IMAGE_SECURITY == 1)

/******************************************************************************
                   Includes section
******************************************************************************/
#include <types.h>
#include <extMemReader.h>
#include <phy.h>

/******************************************************************************
                   Static variables section
******************************************************************************/
/* currently hard coded to CBC */
static aesModeType_t keyMode;

/* Direction will be decryption for payload, encryption for MAC */
static aesDirectionType_t direction;

/* Encryption mode with flags. */
static uint8_t modeByte;

/* for dummy encrypt at the start */
static uint8_t first = 1;

/* decrypt key */
static uint8_t key[KEY_SIZE];

/* initialization vector */
static uint8_t iv[KEY_SIZE];

/* XOR feedback buffer. holds first cipher block for the first time */
static uint8_t dataMinusTwo[AES_BLOCKSIZE];

/******************************************************************************
                   global variables section
******************************************************************************/
/* feedback buffers. Holds IV for the first time */
uint8_t dataMinusOne[AES_BLOCKSIZE];

/******************************************************************************
                   Implementation section
******************************************************************************/

/************************ helper functions ***********************************/

/**************************************************************************//**
 \brief Setup AES unit

  This function performs the following tasks as part of the setup of the
  AES unit: key initialization, set encryption direction and encryption mode.

 \return  False if some parameter was illegal or operation failed, true else
**************************************************************************/
bool aesSetup(void)
{
  uint8_t i;

  // fill in key
  for(i = 0; i < AES_BLOCKSIZE; ++i)
  {
    trx_reg_write(RG_AES_KEY, key[i]);
  }

  if (AES_MODE_CBC == keyMode)
  {
    memcpy(dataMinusTwo, iv, AES_BLOCKSIZE);
  }

  // configuring for ECB encryption is fine for both enc & dec directions
  // set mode and direction - for the first decrypt
  //   -> Top nibble - 0x40 - CBC, 0x00 - ECB
  //   -> bottom nibble - 0x00 - Encrypt, 0x08 - Decrypt
  //   -> set always ECB. chaining will be taken care in the SW
  modeByte = SR_MASK(SR_AES_MODE, AES_MODE_ECB) |
                SR_MASK(SR_AES_DIR, AES_DIR_ENCRYPT);

  if (AES_DIR_DECRYPT == direction)
  {
    // dummy ECB encryption does the following:
    //   1. with the right key plumbed, do a ECB encryption with 'some' input
    //   2. read & write the key
    //   3. then on, configure for CBC decryption and move on
    trx_reg_write(RG_AES_CTRL, modeByte);

    for(i = 0; i < AES_BLOCKSIZE; i++)
    {
      trx_reg_write(RG_AES_STATE, dataMinusOne[i]);
    }

    trx_reg_write(RG_AES_CTRL, modeByte | SR_MASK(SR_AES_REQUEST, AES_START));

    while(!trx_bit_read(SR_AES_RY));

    for(i = 0; i < AES_BLOCKSIZE; ++i)
    {
      dataMinusOne[i] = trx_reg_read(RG_AES_KEY);
    }

    for(i = 0; i < AES_BLOCKSIZE; ++i)
    {
      trx_reg_write(RG_AES_KEY, dataMinusOne[i]);
    }

    modeByte = (SR_MASK(SR_AES_MODE, AES_MODE_ECB) | SR_MASK(SR_AES_DIR, AES_DIR_DECRYPT));
  }

  return true;
}

/**************************************************************************//**
 \brief Reads the result of previous AES en/decryption
        This function returns the result of the previous AES operation

 \param[out] data - result of previous operation
**************************************************************************/
void aesRead(uint8_t *data)
{
  uint8_t i;

  for(i = 0; i < AES_BLOCKSIZE; ++i)
  {
    *data++ = trx_reg_read(RG_AES_STATE);
  }
}

/**************************************************************************//**
\brief En/decrypt one AES block.
       The function returns after the AES operation is finished.

       AES CBC encryption flow :

   iv  text1  key                  text2  key
    |    |     |                     |     |       
    |    |     |                     |     |
    ----XOR    |      --------------XOR    |        ----------
         |     |      |              |     |        |
       ---------      |             ---------       |
       |encrypt|      |             |encrypt|       |
       ---------      |             ---------       |
           |          |                 |           |
           |          |                 |           |
        cipher 1 -----               cipher 2 ------    

 =============================================================================

       AES CBC decryption flow :

       key cipher1----              key cipher2----
        |     |       |              |     |       
       ---------      |             ---------      
       |decrypt|      |             |decrypt|
       ---------      |             ---------
           |          |                 |               .......
  iv ---- XOR         ---------------- XOR
           |                            |
      Plain text 1                  plain text 2

\param[in/out]  data - AES block to be en/decrypted
******************************************************************************/
void aesExec(uint8_t *data)
{
  uint8_t i;

  if ((AES_DIR_ENCRYPT == direction) && 
      (AES_MODE_CBC == keyMode))
  {
    // XOR text and IV - feed it into AES
    // do encrypt
    // copy cipher(output) as IV for next run
    for(i = 0; i < AES_BLOCKSIZE; i++)
    {
      data[i] ^= dataMinusTwo[i];
    }
  }
  
  for(i = 0; i < AES_BLOCKSIZE; i++)
  {
    trx_reg_write(RG_AES_STATE, data[i]);
  }

  trx_reg_write(RG_AES_CTRL, modeByte | SR_MASK(SR_AES_REQUEST, AES_START));

  // poll RG_AES_RY to know the end of operation - max 24us
  while(!trx_bit_read(SR_AES_RY));

  if (AES_DIR_DECRYPT == direction)
  {
    // feed cipher and key to AES and do decryption
    // xor the text(output) with the IV
    // save current cipher and as IV for next run
    if (AES_MODE_CBC == keyMode)
    {
      // copy to feedback buffer for subsequent calls
      memcpy(dataMinusOne, data, AES_BLOCKSIZE);
    }

    aesRead(data);

    if (AES_MODE_CBC == keyMode)
    {
      for(i = 0; i < AES_BLOCKSIZE; i++)
      {
        data[i] ^= dataMinusTwo[i];
      }
      memcpy(dataMinusTwo, dataMinusOne, AES_BLOCKSIZE);
    }
  }
  else if (AES_DIR_ENCRYPT == direction)
  {
    if (AES_MODE_CBC == keyMode)
    {
      aesRead(data);
      memcpy(dataMinusTwo, data, AES_BLOCKSIZE);
    }
  }
}

/************************ helper functions ***********************************/


/************************ API functions **************************************/

/**************************************************************************//**
\brief Reads the last or previous 128-bit data processed

\param[out] 128-bit data read from AES state register

\return
  true - succesfull read operation
  false - failed to read
***************************************************************************/
bool phyReadLastEncData(uint8_t *data)
{
  aesRead(data);
  return true;
}

/**************************************************************************//**
\brief call to clear any carry-forward data/feedback

\return
  true - succesfull operation
  false - failed
******************************************************************************/
bool phyClearHistory(void)
{
  memset(dataMinusOne, 0, AES_BLOCKSIZE);
  memset(dataMinusTwo, 0, AES_BLOCKSIZE);
  memset(key, 0, AES_BLOCKSIZE);
  memset(iv, 0, AES_BLOCKSIZE);
  keyMode   = AES_MODE_UNKNOWN;
  direction = AES_DIR_UNKNOWN;
  first     = 1;

  // dummy return for now
  return true;
}

/**************************************************************************//**
\brief Set up or initialize the required keys

\param
  [in] 128-bit key
  [in] 128-bit IV

\return
  true - succesfully set
  false - failed to set
******************************************************************************/
bool phySetKeyAndIv(uint8_t *inKey, uint8_t *inIv)
{
  memcpy(key, inKey, AES_BLOCKSIZE);
  memcpy(iv, inIv, AES_BLOCKSIZE);

  // sanity check for input buffer not done as it is a known input
  return true;
}

/**************************************************************************//**
\brief sets the AES mode to either ECB or CBC. Direction to either encrypt or
        decrypt

\param[in]
  mode - CBC/ECB
  direction - encrypt/decrypt

\return
  true - succesfully set
  false - failed to set
******************************************************************************/
bool phySetModeAndDir(aesModeType_t mode, aesDirectionType_t dir)
{
  if ((AES_MODE_UNKNOWN == mode) || (AES_DIR_UNKNOWN == dir))
  {
    return false;
  }
  else
  {
    keyMode   = mode;
    direction = dir;
  }

  return true;
}

/**************************************************************************//**
\brief AES block decrypt API call
       can be called to decrypt data in blocks of 16 byte multiples
\param
  Implicitly passed - EnDecryptReq_t type

\return
  true - succesful en/decryption
  false - failed operation
******************************************************************************/
bool phyEnDecrypt(void)
{
  uint8_t i;

  if ((AES_MODE_CBC != keyMode) ||
      !req.blockCount)
    return false;

  if (first)
  {
    // write the key and mode first
    aesSetup();
    first = 0;
  }

  aesExec(req.text);
  for (i = 1; i < req.blockCount; i++)
  {
    aesExec(req.text + i * AES_BLOCKSIZE);
  }

  return true;
}

/************************ API functions **************************************/

#endif /* #if (USE_IMAGE_SECURITY == 1) */

//eof phy.c