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

  \brief HW access APIs(SAMR21) for AES-128 encryption/decryption

  \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:
    26/06/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;

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

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

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

// decrypt key
uint8_t key[KEY_SIZE];

// initialization vector
uint8_t iv[KEY_SIZE];

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

/******************************************************************************

AES_CTRL:

position:      7      6     5     4     3     2     1     0
            -------------------------------------------------
meaning:    | REQ |  M2 |  M1 |  M0 | DIR |  R  |  R  |  R  |
            -------------------------------------------------

M2 M1 M0
0  0  0  - ECB
0  0  1  - key operation
0  1  0  - CBC

DIR
0  - Encryption
1  - Decryption

REQ
0  - idle
1  - start operation

-------------------------------------------------------------------------------

AES_STATUS:

position:      7      6     5     4     3     2     1     0
            -------------------------------------------------
meaning:    | ERR |  R  |  R  |  R  |  R  |  R  |  R  | DON |
            -------------------------------------------------

ERR
0  - no error
1  - error

DON
0  - operation not completed
1  - operation completed

******************************************************************************/

/************************ helper functions - start ***************************/

/**************************************************************************//**
 \brief Initialize SPI interface used for AES access
**************************************************************************/
void aesInit(void)
{
  // reset SPI
  GPIO_PHY_RST_clr();
  HAL_Delay(HAL_RESET_DELAY);
  GPIO_PHY_RST_set();  
  
  // Configure PIOs for SPI
  GPIO_PHY_SLP_TR_make_out();
  GPIO_PHY_SLP_TR_clr();
  GPIO_PHY_RST_make_out();
  GPIO_PHY_IRQ_make_in();
  GPIO_PHY_CS_make_out();
  GPIO_PHY_MISO_make_in();
  GPIO_PHY_MOSI_make_out();
  GPIO_PHY_SCK_make_out();

  // pin configurations
  PORTB_PINCFG30 |= PORTA_PINCFG30_PMUXEN; //MOSI  B30
  PORTC_PINCFG19 |= PORTC_PINCFG19_PMUXEN; //MISO  C19
  PORTC_PINCFG18 |= PORTC_PINCFG18_PMUXEN; //SCK   C18 
  PORTB_PINCFG0  |= PORTB_PINCFG0_PMUXEN;  //IRQ   B0

  // Configure SPI in peripheral multiplexing
  PORTB_PMUX15 = PORTB_PMUX15_PMUXE(PORTB_PMUX15_PMUXE_VALUE);
  PORTC_PMUX9 = PORTC_PMUX9_PMUXE(PORTC_PMUX9_PMUXE_VALUE) | PORTC_PMUX9_PMUXO(PORTC_PMUX9_PMUXO_VALUE);

  // Enable the clock of SPI
  PM_APBCMASK |= PM_APBCMASK_SERCOM4;

  // Configure the generic clk settings
  GCLK_CLKCTRL_s.id    = CLKCTRL_SERCOM_ID;
  GCLK_CLKCTRL_s.gen   = CLKCTRL_GENERTR_VAL;
  GCLK_CLKCTRL_s.clken = CLKCTRL_GENERTR_CLKEN;
  
  // Reset
  SC4_SPI_CTRLA = SC4_SPI_CTRLA_SWRST;
  while(halSpiSwrstSyncing());

  SC4_SPI_CTRLB  = SC4_SPI_CTRLB_RXEN;
  while(halSpiCtrlBSyncing());

  // setup spi mode master, pad & enable it
  SC4_SPI_CTRLA |=  SC4_SPI_CTRLA_MODE(SERCOM4_SPI_CTRLA_MODE_VAL) | SC4_SPI_CTRLA_DOPO(SERCOM4_SPI_CTRLA_DOPO_VAL) | SC4_SPI_CTRLA_ENABLE;
  while(halSpiEnableSyncing());
}

/**************************************************************************//**
 \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)
{
  // fill in the right key
  WRITE_AES_KEY(key);

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

  if (AES_DIR_DECRYPT == direction)
  {
    DO_ECB_ZERO_ENCRYPT();
    WAIT_FOR_AES_OP_END();

    //read and write the round key
    READ_AES_KEY(dataMinusOne);
    WRITE_AES_KEY(dataMinusOne);
  }

  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)
{
  READ_AES_DATA(data);
}

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

       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];
    }
  }

  if (AES_DIR_DECRYPT == direction)
  {
    //ecb decrypt
    DO_ECB_DECRYPT(data);
    WAIT_FOR_AES_OP_END();

    if (AES_MODE_CBC == keyMode)
    {
      // copy to feedback buffer for subsequent calls
      memcpy(dataMinusOne, data, AES_BLOCKSIZE);
    }

    //read 128bit state from AES HW
    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)
  {
    DO_ECB_ENCRYPT(data);
    WAIT_FOR_AES_OP_END();
    
    if (AES_MODE_CBC == keyMode)
    {
      aesRead(data);
      memcpy(dataMinusTwo, data, AES_BLOCKSIZE);
    }
  }
}

/************************ helper functions - end *****************************/


/************************ API functions - start ******************************/

/**************************************************************************//**
\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)
{
  // initialize AES engine
  aesInit();
  
  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 encrypt/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 - end ********************************/

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

//eof phy.c