/**
  ******************************************************************************
  * @file    stm32f4xx_cryp_aes.c
  * @author  MCD Application Team
  * @version V1.8.0
  * @date    04-November-2016
  * @brief   This file provides high level functions to encrypt and decrypt an 
  *          input message using AES in ECB/CBC/CTR/GCM/CCM modes.
  *          It uses the stm32f4xx_cryp.c/.h drivers to access the STM32F4xx CRYP
  *          peripheral.
  *          AES-ECB/CBC/CTR/GCM/CCM modes are available on STM32F437x Devices.
  *          For STM32F41xx Devices, only AES-ECB/CBC/CTR modes are available.
  *
@verbatim
 ===================================================================
                  ##### How to use this driver #####
 ===================================================================
 [..]
   (#) Enable The CRYP controller clock using 
      RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_CRYP, ENABLE); function.
  
   (#) Encrypt and decrypt using AES in ECB Mode using CRYP_AES_ECB() function.
  
   (#) Encrypt and decrypt using AES in CBC Mode using CRYP_AES_CBC() function.
  
   (#) Encrypt and decrypt using AES in CTR Mode using CRYP_AES_CTR() function.

   (#) Encrypt and decrypt using AES in GCM Mode using CRYP_AES_GCM() function.
   
   (#) Encrypt and decrypt using AES in CCM Mode using CRYP_AES_CCM() function.
     
@endverbatim
  *
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2016 STMicroelectronics</center></h2>
  *
  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        http://www.st.com/software_license_agreement_liberty_v2
  *
  * Unless required by applicable law or agreed to in writing, software 
  * distributed under the License is distributed on an "AS IS" BASIS, 
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************  
  */

/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_cryp.h"

/** @addtogroup STM32F4xx_StdPeriph_Driver
  * @{
  */

/** @defgroup CRYP 
  * @brief CRYP driver modules
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define AESBUSY_TIMEOUT    ((uint32_t) 0x00010000)

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/** @defgroup CRYP_Private_Functions
  * @{
  */ 

/** @defgroup CRYP_Group6 High Level AES functions
 *  @brief   High Level AES functions 
 *
@verbatim   
 ===============================================================================
                       ##### High Level AES functions #####
 ===============================================================================

@endverbatim
  * @{
  */

/**
  * @brief  Encrypt and decrypt using AES in ECB Mode
  * @param  Mode: encryption or decryption Mode.
  *          This parameter can be one of the following values:
  *            @arg MODE_ENCRYPT: Encryption
  *            @arg MODE_DECRYPT: Decryption
  * @param  Key: Key used for AES algorithm.
  * @param  Keysize: length of the Key, must be a 128, 192 or 256.
  * @param  Input: pointer to the Input buffer.
  * @param  Ilength: length of the Input buffer, must be a multiple of 16.
  * @param  Output: pointer to the returned buffer.
  * @retval An ErrorStatus enumeration value:
  *          - SUCCESS: Operation done
  *          - ERROR: Operation failed
  */
ErrorStatus CRYP_AES_ECB(uint8_t Mode, uint8_t* Key, uint16_t Keysize,
                         uint8_t* Input, uint32_t Ilength, uint8_t* Output)
{
  CRYP_InitTypeDef AES_CRYP_InitStructure;
  CRYP_KeyInitTypeDef AES_CRYP_KeyInitStructure;
  __IO uint32_t counter = 0;
  uint32_t busystatus = 0;
  ErrorStatus status = SUCCESS;
  uint32_t keyaddr    = (uint32_t)Key;
  uint32_t inputaddr  = (uint32_t)Input;
  uint32_t outputaddr = (uint32_t)Output;
  uint32_t i = 0;

  /* Crypto structures initialisation*/
  CRYP_KeyStructInit(&AES_CRYP_KeyInitStructure);

  switch(Keysize)
  {
    case 128:
    AES_CRYP_InitStructure.CRYP_KeySize = CRYP_KeySize_128b;
    AES_CRYP_KeyInitStructure.CRYP_Key2Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Right= __REV(*(uint32_t*)(keyaddr));
    break;
    case 192:
    AES_CRYP_InitStructure.CRYP_KeySize  = CRYP_KeySize_192b;
    AES_CRYP_KeyInitStructure.CRYP_Key1Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key1Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Right= __REV(*(uint32_t*)(keyaddr));
    break;
    case 256:
    AES_CRYP_InitStructure.CRYP_KeySize  = CRYP_KeySize_256b;
    AES_CRYP_KeyInitStructure.CRYP_Key0Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key0Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key1Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key1Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Right= __REV(*(uint32_t*)(keyaddr));
    break;
    default:
    break;
  }

  /*------------------ AES Decryption ------------------*/
  if(Mode == MODE_DECRYPT) /* AES decryption */
  {
    /* Flush IN/OUT FIFOs */
    CRYP_FIFOFlush();

    /* Crypto Init for Key preparation for decryption process */
    AES_CRYP_InitStructure.CRYP_AlgoDir = CRYP_AlgoDir_Decrypt;
    AES_CRYP_InitStructure.CRYP_AlgoMode = CRYP_AlgoMode_AES_Key;
    AES_CRYP_InitStructure.CRYP_DataType = CRYP_DataType_32b;
    CRYP_Init(&AES_CRYP_InitStructure);

    /* Key Initialisation */
    CRYP_KeyInit(&AES_CRYP_KeyInitStructure);

    /* Enable Crypto processor */
    CRYP_Cmd(ENABLE);

    /* wait until the Busy flag is RESET */
    do
    {
      busystatus = CRYP_GetFlagStatus(CRYP_FLAG_BUSY);
      counter++;
    }while ((counter != AESBUSY_TIMEOUT) && (busystatus != RESET));

    if (busystatus != RESET)
   {
       status = ERROR;
    }
    else
    {
      /* Crypto Init for decryption process */  
      AES_CRYP_InitStructure.CRYP_AlgoDir = CRYP_AlgoDir_Decrypt;
    }
  }
  /*------------------ AES Encryption ------------------*/
  else /* AES encryption */
  {

    CRYP_KeyInit(&AES_CRYP_KeyInitStructure);

    /* Crypto Init for Encryption process */
    AES_CRYP_InitStructure.CRYP_AlgoDir  = CRYP_AlgoDir_Encrypt;
  }

  AES_CRYP_InitStructure.CRYP_AlgoMode = CRYP_AlgoMode_AES_ECB;
  AES_CRYP_InitStructure.CRYP_DataType = CRYP_DataType_8b;
  CRYP_Init(&AES_CRYP_InitStructure);

  /* Flush IN/OUT FIFOs */
  CRYP_FIFOFlush();

  /* Enable Crypto processor */
  CRYP_Cmd(ENABLE);

  if(CRYP_GetCmdStatus() == DISABLE)
  {
    /* The CRYP peripheral clock is not enabled or the device doesn't embed 
       the CRYP peripheral (please check the device sales type. */
    return(ERROR);
  }
  
  for(i=0; ((i<Ilength) && (status != ERROR)); i+=16)
  {

    /* Write the Input block in the IN FIFO */
    CRYP_DataIn(*(uint32_t*)(inputaddr));
    inputaddr+=4;
    CRYP_DataIn(*(uint32_t*)(inputaddr));
    inputaddr+=4;
    CRYP_DataIn(*(uint32_t*)(inputaddr));
    inputaddr+=4;
    CRYP_DataIn(*(uint32_t*)(inputaddr));
    inputaddr+=4;

    /* Wait until the complete message has been processed */
    counter = 0;
    do
    {
      busystatus = CRYP_GetFlagStatus(CRYP_FLAG_BUSY);
      counter++;
    }while ((counter != AESBUSY_TIMEOUT) && (busystatus != RESET));

    if (busystatus != RESET)
   {
       status = ERROR;
    }
    else
    {

      /* Read the Output block from the Output FIFO */
      *(uint32_t*)(outputaddr) = CRYP_DataOut();
      outputaddr+=4;
      *(uint32_t*)(outputaddr) = CRYP_DataOut();
      outputaddr+=4;
      *(uint32_t*)(outputaddr) = CRYP_DataOut();
      outputaddr+=4;
      *(uint32_t*)(outputaddr) = CRYP_DataOut(); 
      outputaddr+=4;
    }
  }

  /* Disable Crypto */
  CRYP_Cmd(DISABLE);

  return status; 
}

/**
  * @brief  Encrypt and decrypt using AES in CBC Mode
  * @param  Mode: encryption or decryption Mode.
  *          This parameter can be one of the following values:
  *            @arg MODE_ENCRYPT: Encryption
  *            @arg MODE_DECRYPT: Decryption
  * @param  InitVectors: Initialisation Vectors used for AES algorithm.
  * @param  Key: Key used for AES algorithm.
  * @param  Keysize: length of the Key, must be a 128, 192 or 256.
  * @param  Input: pointer to the Input buffer.
  * @param  Ilength: length of the Input buffer, must be a multiple of 16.
  * @param  Output: pointer to the returned buffer.
  * @retval An ErrorStatus enumeration value:
  *          - SUCCESS: Operation done
  *          - ERROR: Operation failed
  */
ErrorStatus CRYP_AES_CBC(uint8_t Mode, uint8_t InitVectors[16], uint8_t *Key,
                         uint16_t Keysize, uint8_t *Input, uint32_t Ilength,
                         uint8_t *Output)
{
  CRYP_InitTypeDef AES_CRYP_InitStructure;
  CRYP_KeyInitTypeDef AES_CRYP_KeyInitStructure;
  CRYP_IVInitTypeDef AES_CRYP_IVInitStructure;
  __IO uint32_t counter = 0;
  uint32_t busystatus = 0;
  ErrorStatus status = SUCCESS;
  uint32_t keyaddr    = (uint32_t)Key;
  uint32_t inputaddr  = (uint32_t)Input;
  uint32_t outputaddr = (uint32_t)Output;
  uint32_t ivaddr = (uint32_t)InitVectors;
  uint32_t i = 0;

  /* Crypto structures initialisation*/
  CRYP_KeyStructInit(&AES_CRYP_KeyInitStructure);

  switch(Keysize)
  {
    case 128:
    AES_CRYP_InitStructure.CRYP_KeySize = CRYP_KeySize_128b;
    AES_CRYP_KeyInitStructure.CRYP_Key2Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Right= __REV(*(uint32_t*)(keyaddr));
    break;
    case 192:
    AES_CRYP_InitStructure.CRYP_KeySize  = CRYP_KeySize_192b;
    AES_CRYP_KeyInitStructure.CRYP_Key1Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key1Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Right= __REV(*(uint32_t*)(keyaddr));
    break;
    case 256:
    AES_CRYP_InitStructure.CRYP_KeySize  = CRYP_KeySize_256b;
    AES_CRYP_KeyInitStructure.CRYP_Key0Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key0Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key1Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key1Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Right= __REV(*(uint32_t*)(keyaddr));
    break;
    default:
    break;
  }

  /* CRYP Initialization Vectors */
  AES_CRYP_IVInitStructure.CRYP_IV0Left = __REV(*(uint32_t*)(ivaddr));
  ivaddr+=4;
  AES_CRYP_IVInitStructure.CRYP_IV0Right= __REV(*(uint32_t*)(ivaddr));
  ivaddr+=4;
  AES_CRYP_IVInitStructure.CRYP_IV1Left = __REV(*(uint32_t*)(ivaddr));
  ivaddr+=4;
  AES_CRYP_IVInitStructure.CRYP_IV1Right= __REV(*(uint32_t*)(ivaddr));


  /*------------------ AES Decryption ------------------*/
  if(Mode == MODE_DECRYPT) /* AES decryption */
  {
    /* Flush IN/OUT FIFOs */
    CRYP_FIFOFlush();

    /* Crypto Init for Key preparation for decryption process */
    AES_CRYP_InitStructure.CRYP_AlgoDir = CRYP_AlgoDir_Decrypt;
    AES_CRYP_InitStructure.CRYP_AlgoMode = CRYP_AlgoMode_AES_Key;
    AES_CRYP_InitStructure.CRYP_DataType = CRYP_DataType_32b;

    CRYP_Init(&AES_CRYP_InitStructure);

    /* Key Initialisation */
    CRYP_KeyInit(&AES_CRYP_KeyInitStructure);

    /* Enable Crypto processor */
    CRYP_Cmd(ENABLE);

    /* wait until the Busy flag is RESET */
    do
    {
      busystatus = CRYP_GetFlagStatus(CRYP_FLAG_BUSY);
      counter++;
    }while ((counter != AESBUSY_TIMEOUT) && (busystatus != RESET));

    if (busystatus != RESET)
   {
       status = ERROR;
    }
    else
    {
      /* Crypto Init for decryption process */  
      AES_CRYP_InitStructure.CRYP_AlgoDir = CRYP_AlgoDir_Decrypt;
    }
  }
  /*------------------ AES Encryption ------------------*/
  else /* AES encryption */
  {
    CRYP_KeyInit(&AES_CRYP_KeyInitStructure);

    /* Crypto Init for Encryption process */
    AES_CRYP_InitStructure.CRYP_AlgoDir  = CRYP_AlgoDir_Encrypt;
  }
  AES_CRYP_InitStructure.CRYP_AlgoMode = CRYP_AlgoMode_AES_CBC;
  AES_CRYP_InitStructure.CRYP_DataType = CRYP_DataType_8b;
  CRYP_Init(&AES_CRYP_InitStructure);

  /* CRYP Initialization Vectors */
  CRYP_IVInit(&AES_CRYP_IVInitStructure);

  /* Flush IN/OUT FIFOs */
  CRYP_FIFOFlush();

  /* Enable Crypto processor */
  CRYP_Cmd(ENABLE);

  if(CRYP_GetCmdStatus() == DISABLE)
  {
    /* The CRYP peripheral clock is not enabled or the device doesn't embed 
       the CRYP peripheral (please check the device sales type. */
    return(ERROR);
  }
  
  for(i=0; ((i<Ilength) && (status != ERROR)); i+=16)
  {

    /* Write the Input block in the IN FIFO */
    CRYP_DataIn(*(uint32_t*)(inputaddr));
    inputaddr+=4;
    CRYP_DataIn(*(uint32_t*)(inputaddr));
    inputaddr+=4;
    CRYP_DataIn(*(uint32_t*)(inputaddr));
    inputaddr+=4;
    CRYP_DataIn(*(uint32_t*)(inputaddr));
    inputaddr+=4;
    /* Wait until the complete message has been processed */
    counter = 0;
    do
    {
      busystatus = CRYP_GetFlagStatus(CRYP_FLAG_BUSY);
      counter++;
    }while ((counter != AESBUSY_TIMEOUT) && (busystatus != RESET));

    if (busystatus != RESET)
   {
       status = ERROR;
    }
    else
    {

      /* Read the Output block from the Output FIFO */
      *(uint32_t*)(outputaddr) = CRYP_DataOut();
      outputaddr+=4;
      *(uint32_t*)(outputaddr) = CRYP_DataOut();
      outputaddr+=4;
      *(uint32_t*)(outputaddr) = CRYP_DataOut();
      outputaddr+=4;
      *(uint32_t*)(outputaddr) = CRYP_DataOut();
      outputaddr+=4;
    }
  }

  /* Disable Crypto */
  CRYP_Cmd(DISABLE);

  return status;
}

/**
  * @brief  Encrypt and decrypt using AES in CTR Mode
  * @param  Mode: encryption or decryption Mode.
  *           This parameter can be one of the following values:
  *            @arg MODE_ENCRYPT: Encryption
  *            @arg MODE_DECRYPT: Decryption
  * @param  InitVectors: Initialisation Vectors used for AES algorithm.
  * @param  Key: Key used for AES algorithm.
  * @param  Keysize: length of the Key, must be a 128, 192 or 256.
  * @param  Input: pointer to the Input buffer.
  * @param  Ilength: length of the Input buffer, must be a multiple of 16.
  * @param  Output: pointer to the returned buffer.
  * @retval An ErrorStatus enumeration value:
  *          - SUCCESS: Operation done
  *          - ERROR: Operation failed
  */
ErrorStatus CRYP_AES_CTR(uint8_t Mode, uint8_t InitVectors[16], uint8_t *Key, 
                         uint16_t Keysize, uint8_t *Input, uint32_t Ilength,
                         uint8_t *Output)
{
  CRYP_InitTypeDef AES_CRYP_InitStructure;
  CRYP_KeyInitTypeDef AES_CRYP_KeyInitStructure;
  CRYP_IVInitTypeDef AES_CRYP_IVInitStructure;
  __IO uint32_t counter = 0;
  uint32_t busystatus = 0;
  ErrorStatus status = SUCCESS;
  uint32_t keyaddr    = (uint32_t)Key;
  uint32_t inputaddr  = (uint32_t)Input;
  uint32_t outputaddr = (uint32_t)Output;
  uint32_t ivaddr     = (uint32_t)InitVectors;
  uint32_t i = 0;

  /* Crypto structures initialisation*/
  CRYP_KeyStructInit(&AES_CRYP_KeyInitStructure);

  switch(Keysize)
  {
    case 128:
    AES_CRYP_InitStructure.CRYP_KeySize = CRYP_KeySize_128b;
    AES_CRYP_KeyInitStructure.CRYP_Key2Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Right= __REV(*(uint32_t*)(keyaddr));
    break;
    case 192:
    AES_CRYP_InitStructure.CRYP_KeySize  = CRYP_KeySize_192b;
    AES_CRYP_KeyInitStructure.CRYP_Key1Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key1Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Right= __REV(*(uint32_t*)(keyaddr));
    break;
    case 256:
    AES_CRYP_InitStructure.CRYP_KeySize  = CRYP_KeySize_256b;
    AES_CRYP_KeyInitStructure.CRYP_Key0Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key0Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key1Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key1Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Right= __REV(*(uint32_t*)(keyaddr));
    break;
    default:
    break;
  }
  /* CRYP Initialization Vectors */
  AES_CRYP_IVInitStructure.CRYP_IV0Left = __REV(*(uint32_t*)(ivaddr));
  ivaddr+=4;
  AES_CRYP_IVInitStructure.CRYP_IV0Right= __REV(*(uint32_t*)(ivaddr));
  ivaddr+=4;
  AES_CRYP_IVInitStructure.CRYP_IV1Left = __REV(*(uint32_t*)(ivaddr));
  ivaddr+=4;
  AES_CRYP_IVInitStructure.CRYP_IV1Right= __REV(*(uint32_t*)(ivaddr));

  /* Key Initialisation */
  CRYP_KeyInit(&AES_CRYP_KeyInitStructure);

  /*------------------ AES Decryption ------------------*/
  if(Mode == MODE_DECRYPT) /* AES decryption */
  {
    /* Crypto Init for decryption process */
    AES_CRYP_InitStructure.CRYP_AlgoDir = CRYP_AlgoDir_Decrypt;
  }
  /*------------------ AES Encryption ------------------*/
  else /* AES encryption */
  {
    /* Crypto Init for Encryption process */
    AES_CRYP_InitStructure.CRYP_AlgoDir = CRYP_AlgoDir_Encrypt;
  }
  AES_CRYP_InitStructure.CRYP_AlgoMode = CRYP_AlgoMode_AES_CTR;
  AES_CRYP_InitStructure.CRYP_DataType = CRYP_DataType_8b;
  CRYP_Init(&AES_CRYP_InitStructure);

  /* CRYP Initialization Vectors */
  CRYP_IVInit(&AES_CRYP_IVInitStructure);

  /* Flush IN/OUT FIFOs */
  CRYP_FIFOFlush();

  /* Enable Crypto processor */
  CRYP_Cmd(ENABLE);

  if(CRYP_GetCmdStatus() == DISABLE)
  {
    /* The CRYP peripheral clock is not enabled or the device doesn't embed 
       the CRYP peripheral (please check the device sales type. */
    return(ERROR);
  }
  
  for(i=0; ((i<Ilength) && (status != ERROR)); i+=16)
  {

    /* Write the Input block in the IN FIFO */
    CRYP_DataIn(*(uint32_t*)(inputaddr));
    inputaddr+=4;
    CRYP_DataIn(*(uint32_t*)(inputaddr));
    inputaddr+=4;
    CRYP_DataIn(*(uint32_t*)(inputaddr));
    inputaddr+=4;
    CRYP_DataIn(*(uint32_t*)(inputaddr));
    inputaddr+=4;
    /* Wait until the complete message has been processed */
    counter = 0;
    do
    {
      busystatus = CRYP_GetFlagStatus(CRYP_FLAG_BUSY);
      counter++;
    }while ((counter != AESBUSY_TIMEOUT) && (busystatus != RESET));

    if (busystatus != RESET)
   {
       status = ERROR;
    }
    else
    {

      /* Read the Output block from the Output FIFO */
      *(uint32_t*)(outputaddr) = CRYP_DataOut();
      outputaddr+=4;
      *(uint32_t*)(outputaddr) = CRYP_DataOut();
      outputaddr+=4;
      *(uint32_t*)(outputaddr) = CRYP_DataOut();
      outputaddr+=4;
      *(uint32_t*)(outputaddr) = CRYP_DataOut();
      outputaddr+=4;
    }
  }
  /* Disable Crypto */
  CRYP_Cmd(DISABLE);

  return status;
}

/**
  * @brief  Encrypt and decrypt using AES in GCM Mode. The GCM and CCM modes
  *         are available only on STM32F437x Devices.
  * @param  Mode: encryption or decryption Mode.
  *          This parameter can be one of the following values:
  *            @arg MODE_ENCRYPT: Encryption
  *            @arg MODE_DECRYPT: Decryption
  * @param  InitVectors: Initialisation Vectors used for AES algorithm.
  * @param  Key: Key used for AES algorithm.
  * @param  Keysize: length of the Key, must be a 128, 192 or 256.
  * @param  Input: pointer to the Input buffer.
  * @param  Ilength: length of the Input buffer in bytes, must be a multiple of 16.
  * @param  Header: pointer to the header buffer.
  * @param  Hlength: length of the header buffer in bytes, must be a multiple of 16.  
  * @param  Output: pointer to the returned buffer.
  * @param  AuthTAG: pointer to the authentication TAG buffer.
  * @retval An ErrorStatus enumeration value:
  *          - SUCCESS: Operation done
  *          - ERROR: Operation failed
  */
ErrorStatus CRYP_AES_GCM(uint8_t Mode, uint8_t InitVectors[16],
                         uint8_t *Key, uint16_t Keysize,
                         uint8_t *Input, uint32_t ILength,
                         uint8_t *Header, uint32_t HLength,
                         uint8_t *Output, uint8_t *AuthTAG)
{
  CRYP_InitTypeDef AES_CRYP_InitStructure;
  CRYP_KeyInitTypeDef AES_CRYP_KeyInitStructure;
  CRYP_IVInitTypeDef AES_CRYP_IVInitStructure;
  __IO uint32_t counter = 0;
  uint32_t busystatus = 0;
  ErrorStatus status = SUCCESS;
  uint32_t keyaddr    = (uint32_t)Key;
  uint32_t inputaddr  = (uint32_t)Input;
  uint32_t outputaddr = (uint32_t)Output;
  uint32_t ivaddr     = (uint32_t)InitVectors;
  uint32_t headeraddr = (uint32_t)Header;
  uint32_t tagaddr = (uint32_t)AuthTAG;
  uint64_t headerlength = HLength * 8;/* header length in bits */
  uint64_t inputlength = ILength * 8;/* input length in bits */
  uint32_t loopcounter = 0;

  /* Crypto structures initialisation*/
  CRYP_KeyStructInit(&AES_CRYP_KeyInitStructure);

  switch(Keysize)
  {
    case 128:
    AES_CRYP_InitStructure.CRYP_KeySize = CRYP_KeySize_128b;
    AES_CRYP_KeyInitStructure.CRYP_Key2Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Right= __REV(*(uint32_t*)(keyaddr));
    break;
    case 192:
    AES_CRYP_InitStructure.CRYP_KeySize  = CRYP_KeySize_192b;
    AES_CRYP_KeyInitStructure.CRYP_Key1Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key1Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Right= __REV(*(uint32_t*)(keyaddr));
    break;
    case 256:
    AES_CRYP_InitStructure.CRYP_KeySize  = CRYP_KeySize_256b;
    AES_CRYP_KeyInitStructure.CRYP_Key0Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key0Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key1Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key1Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Right= __REV(*(uint32_t*)(keyaddr));
    break;
    default:
    break;
  }
  
  /* CRYP Initialization Vectors */
  AES_CRYP_IVInitStructure.CRYP_IV0Left = __REV(*(uint32_t*)(ivaddr));
  ivaddr+=4;
  AES_CRYP_IVInitStructure.CRYP_IV0Right= __REV(*(uint32_t*)(ivaddr));
  ivaddr+=4;
  AES_CRYP_IVInitStructure.CRYP_IV1Left = __REV(*(uint32_t*)(ivaddr));
  ivaddr+=4;
  AES_CRYP_IVInitStructure.CRYP_IV1Right= __REV(*(uint32_t*)(ivaddr));
  
  /*------------------ AES Encryption ------------------*/
  if(Mode == MODE_ENCRYPT) /* AES encryption */
  {
    /* Flush IN/OUT FIFOs */
    CRYP_FIFOFlush();
    
    /* Key Initialisation */
    CRYP_KeyInit(&AES_CRYP_KeyInitStructure);
    
    /* CRYP Initialization Vectors */
    CRYP_IVInit(&AES_CRYP_IVInitStructure);
    
    /* Crypto Init for Key preparation for decryption process */
    AES_CRYP_InitStructure.CRYP_AlgoDir = CRYP_AlgoDir_Encrypt;
    AES_CRYP_InitStructure.CRYP_AlgoMode = CRYP_AlgoMode_AES_GCM;
    AES_CRYP_InitStructure.CRYP_DataType = CRYP_DataType_8b;
    CRYP_Init(&AES_CRYP_InitStructure);
    
    /***************************** Init phase *********************************/
    /* Select init phase */
    CRYP_PhaseConfig(CRYP_Phase_Init);
    
    /* Enable Crypto processor */
    CRYP_Cmd(ENABLE);
    
    /* Wait for CRYPEN bit to be 0 */
    while(CRYP_GetCmdStatus() == ENABLE)
    {
    }
    
    /***************************** header phase *******************************/
    if(HLength != 0)
    {
      /* Select header phase */
      CRYP_PhaseConfig(CRYP_Phase_Header);
      
      /* Enable Crypto processor */
      CRYP_Cmd(ENABLE);
      
      if(CRYP_GetCmdStatus() == DISABLE)
      {
         /* The CRYP peripheral clock is not enabled or the device doesn't embed 
            the CRYP peripheral (please check the device sales type. */
         return(ERROR);
      }
      
      for(loopcounter = 0; (loopcounter < HLength); loopcounter+=16)
      {
        /* Wait until the IFEM flag is reset */
        while(CRYP_GetFlagStatus(CRYP_FLAG_IFEM) == RESET)
        {
        }
        
        /* Write the Input block in the IN FIFO */
        CRYP_DataIn(*(uint32_t*)(headeraddr));
        headeraddr+=4;
        CRYP_DataIn(*(uint32_t*)(headeraddr));
        headeraddr+=4;
        CRYP_DataIn(*(uint32_t*)(headeraddr));
        headeraddr+=4;
        CRYP_DataIn(*(uint32_t*)(headeraddr));
        headeraddr+=4;
      }
      
      /* Wait until the complete message has been processed */
      counter = 0;
      do
      {
        busystatus = CRYP_GetFlagStatus(CRYP_FLAG_BUSY);
        counter++;
      }while ((counter != AESBUSY_TIMEOUT) && (busystatus != RESET));

      if (busystatus != RESET)
      {
        status = ERROR;
      }
    }
    
    /**************************** payload phase *******************************/
    if(ILength != 0)
    {
      /* Select payload phase */
      CRYP_PhaseConfig(CRYP_Phase_Payload);
      
      /* Enable Crypto processor */
      CRYP_Cmd(ENABLE);
      
      if(CRYP_GetCmdStatus() == DISABLE)
      {
        /* The CRYP peripheral clock is not enabled or the device doesn't embed 
           the CRYP peripheral (please check the device sales type. */
        return(ERROR);
      }
      
      for(loopcounter = 0; ((loopcounter < ILength) && (status != ERROR)); loopcounter+=16)
      {
        /* Wait until the IFEM flag is reset */
        while(CRYP_GetFlagStatus(CRYP_FLAG_IFEM) == RESET)
        {
        }
        /* Write the Input block in the IN FIFO */
        CRYP_DataIn(*(uint32_t*)(inputaddr));
        inputaddr+=4;
        CRYP_DataIn(*(uint32_t*)(inputaddr));
        inputaddr+=4;
        CRYP_DataIn(*(uint32_t*)(inputaddr));
        inputaddr+=4;
        CRYP_DataIn(*(uint32_t*)(inputaddr));
        inputaddr+=4;
        
        /* Wait until the complete message has been processed */
        counter = 0;
        do
        {
          busystatus = CRYP_GetFlagStatus(CRYP_FLAG_BUSY);
          counter++;
        }while ((counter != AESBUSY_TIMEOUT) && (busystatus != RESET));

        if (busystatus != RESET)
        {
          status = ERROR;
        }
        else
        {
          /* Wait until the OFNE flag is reset */
          while(CRYP_GetFlagStatus(CRYP_FLAG_OFNE) == RESET)
          {
          }
          
          /* Read the Output block from the Output FIFO */
          *(uint32_t*)(outputaddr) = CRYP_DataOut();
          outputaddr+=4;
          *(uint32_t*)(outputaddr) = CRYP_DataOut();
          outputaddr+=4;
          *(uint32_t*)(outputaddr) = CRYP_DataOut();
          outputaddr+=4;
          *(uint32_t*)(outputaddr) = CRYP_DataOut();
          outputaddr+=4;
        }
      }
    }
    
    /***************************** final phase ********************************/
    /* Select final phase */
    CRYP_PhaseConfig(CRYP_Phase_Final);
    
    /* Enable Crypto processor */
    CRYP_Cmd(ENABLE);
    
    if(CRYP_GetCmdStatus() == DISABLE)
    {
      /* The CRYP peripheral clock is not enabled or the device doesn't embed 
         the CRYP peripheral (please check the device sales type. */
      return(ERROR);
    }
    
    /* Write number of bits concatenated with header in the IN FIFO */
    CRYP_DataIn(__REV(headerlength>>32));
    CRYP_DataIn(__REV(headerlength));
    CRYP_DataIn(__REV(inputlength>>32));
    CRYP_DataIn(__REV(inputlength));
    /* Wait until the OFNE flag is reset */
    while(CRYP_GetFlagStatus(CRYP_FLAG_OFNE) == RESET)
    {
    }
    
    tagaddr = (uint32_t)AuthTAG;
    /* Read the Auth TAG in the IN FIFO */
    *(uint32_t*)(tagaddr) = CRYP_DataOut();
    tagaddr+=4;
    *(uint32_t*)(tagaddr) = CRYP_DataOut();
    tagaddr+=4;
    *(uint32_t*)(tagaddr) = CRYP_DataOut();
    tagaddr+=4;
    *(uint32_t*)(tagaddr) = CRYP_DataOut();
    tagaddr+=4;
  }
  /*------------------ AES Decryption ------------------*/
  else /* AES decryption */
  {
    /* Flush IN/OUT FIFOs */
    CRYP_FIFOFlush();
    
    /* Key Initialisation */
    CRYP_KeyInit(&AES_CRYP_KeyInitStructure);
    
    /* CRYP Initialization Vectors */
    CRYP_IVInit(&AES_CRYP_IVInitStructure);
    
    /* Crypto Init for Key preparation for decryption process */
    AES_CRYP_InitStructure.CRYP_AlgoDir = CRYP_AlgoDir_Decrypt;
    AES_CRYP_InitStructure.CRYP_AlgoMode = CRYP_AlgoMode_AES_GCM;
    AES_CRYP_InitStructure.CRYP_DataType = CRYP_DataType_8b;
    CRYP_Init(&AES_CRYP_InitStructure);
    
    /***************************** Init phase *********************************/
    /* Select init phase */
    CRYP_PhaseConfig(CRYP_Phase_Init);
    
    /* Enable Crypto processor */
    CRYP_Cmd(ENABLE);
    
    /* Wait for CRYPEN bit to be 0 */
    while(CRYP_GetCmdStatus() == ENABLE)
    {
    }
    
    /***************************** header phase *******************************/
    if(HLength != 0)
    {
      /* Select header phase */
      CRYP_PhaseConfig(CRYP_Phase_Header);
      
      /* Enable Crypto processor */
      CRYP_Cmd(ENABLE);
      
      if(CRYP_GetCmdStatus() == DISABLE)
      {
        /* The CRYP peripheral clock is not enabled or the device doesn't embed 
           the CRYP peripheral (please check the device sales type. */
        return(ERROR);
      }
      
      for(loopcounter = 0; (loopcounter < HLength); loopcounter+=16)
      {
        /* Wait until the IFEM flag is reset */
        while(CRYP_GetFlagStatus(CRYP_FLAG_IFEM) == RESET)
        {
        }
        
        /* Write the Input block in the IN FIFO */
        CRYP_DataIn(*(uint32_t*)(headeraddr));
        headeraddr+=4;
        CRYP_DataIn(*(uint32_t*)(headeraddr));
        headeraddr+=4;
        CRYP_DataIn(*(uint32_t*)(headeraddr));
        headeraddr+=4;
        CRYP_DataIn(*(uint32_t*)(headeraddr));
        headeraddr+=4;
      }
      
      /* Wait until the complete message has been processed */
      counter = 0;
      do
      {
        busystatus = CRYP_GetFlagStatus(CRYP_FLAG_BUSY);
        counter++;
      }while ((counter != AESBUSY_TIMEOUT) && (busystatus != RESET));

      if (busystatus != RESET)
      {
        status = ERROR;
      }
    }
    
    /**************************** payload phase *******************************/
    if(ILength != 0)
    {
      /* Select payload phase */
      CRYP_PhaseConfig(CRYP_Phase_Payload);

      /* Enable Crypto processor */
      CRYP_Cmd(ENABLE);
      
      if(CRYP_GetCmdStatus() == DISABLE)
      {
        /* The CRYP peripheral clock is not enabled or the device doesn't embed 
           the CRYP peripheral (please check the device sales type. */
        return(ERROR);
      }
      
      for(loopcounter = 0; ((loopcounter < ILength) && (status != ERROR)); loopcounter+=16)
      {
        /* Wait until the IFEM flag is reset */
        while(CRYP_GetFlagStatus(CRYP_FLAG_IFEM) == RESET)
        {
        }
        /* Write the Input block in the IN FIFO */
        CRYP_DataIn(*(uint32_t*)(inputaddr));
        inputaddr+=4;
        CRYP_DataIn(*(uint32_t*)(inputaddr));
        inputaddr+=4;
        CRYP_DataIn(*(uint32_t*)(inputaddr));
        inputaddr+=4;
        CRYP_DataIn(*(uint32_t*)(inputaddr));
        inputaddr+=4;
        
        /* Wait until the complete message has been processed */
        counter = 0;
        do
        {
          busystatus = CRYP_GetFlagStatus(CRYP_FLAG_BUSY);
          counter++;
        }while ((counter != AESBUSY_TIMEOUT) && (busystatus != RESET));

        if (busystatus != RESET)
        {
          status = ERROR;
        }
        else
        {
          /* Wait until the OFNE flag is reset */
          while(CRYP_GetFlagStatus(CRYP_FLAG_OFNE) == RESET)
          {
          }
          
          /* Read the Output block from the Output FIFO */
          *(uint32_t*)(outputaddr) = CRYP_DataOut();
          outputaddr+=4;
          *(uint32_t*)(outputaddr) = CRYP_DataOut();
          outputaddr+=4;
          *(uint32_t*)(outputaddr) = CRYP_DataOut();
          outputaddr+=4;
          *(uint32_t*)(outputaddr) = CRYP_DataOut();
          outputaddr+=4;
        }
      }
    }
    
    /***************************** final phase ********************************/
    /* Select final phase */
    CRYP_PhaseConfig(CRYP_Phase_Final);

    /* Enable Crypto processor */
    CRYP_Cmd(ENABLE);
    
    if(CRYP_GetCmdStatus() == DISABLE)
    {
      /* The CRYP peripheral clock is not enabled or the device doesn't embed 
         the CRYP peripheral (please check the device sales type. */
      return(ERROR);
    }
    
    /* Write number of bits concatenated with header in the IN FIFO */
    CRYP_DataIn(__REV(headerlength>>32));
    CRYP_DataIn(__REV(headerlength));
    CRYP_DataIn(__REV(inputlength>>32));
    CRYP_DataIn(__REV(inputlength));
    /* Wait until the OFNE flag is reset */
    while(CRYP_GetFlagStatus(CRYP_FLAG_OFNE) == RESET)
    {
    }
    
    tagaddr = (uint32_t)AuthTAG;
    /* Read the Auth TAG in the IN FIFO */
    *(uint32_t*)(tagaddr) = CRYP_DataOut();
    tagaddr+=4;
    *(uint32_t*)(tagaddr) = CRYP_DataOut();
    tagaddr+=4;
    *(uint32_t*)(tagaddr) = CRYP_DataOut();
    tagaddr+=4;
    *(uint32_t*)(tagaddr) = CRYP_DataOut();
    tagaddr+=4;
  }
  /* Disable Crypto */
  CRYP_Cmd(DISABLE);

  return status;
}

/**
  * @brief  Encrypt and decrypt using AES in CCM Mode. The GCM and CCM modes
  *         are available only on STM32F437x Devices.
  * @param  Mode: encryption or decryption Mode.
  *          This parameter can be one of the following values:
  *            @arg MODE_ENCRYPT: Encryption
  *            @arg MODE_DECRYPT: Decryption
  * @param  Nonce: the nonce used for AES algorithm. It shall be unique for each processing.
  * @param  Key: Key used for AES algorithm.
  * @param  Keysize: length of the Key, must be a 128, 192 or 256.
  * @param  Input: pointer to the Input buffer.
  * @param  Ilength: length of the Input buffer in bytes, must be a multiple of 16.
  * @param  Header: pointer to the header buffer.
  * @param  Hlength: length of the header buffer in bytes.
  * @param  HBuffer: pointer to temporary buffer used to append the header
  *         HBuffer size must be equal to Hlength + 21
  * @param  Output: pointer to the returned buffer.
  * @param  AuthTAG: pointer to the authentication TAG buffer.
  * @param  TAGSize: the size of the TAG (called also MAC).
  * @retval An ErrorStatus enumeration value:
  *          - SUCCESS: Operation done
  *          - ERROR: Operation failed
  */
ErrorStatus CRYP_AES_CCM(uint8_t Mode, 
                         uint8_t* Nonce, uint32_t NonceSize,
                         uint8_t *Key, uint16_t Keysize,
                         uint8_t *Input, uint32_t ILength,
                         uint8_t *Header, uint32_t HLength, uint8_t *HBuffer,
                         uint8_t *Output,
                         uint8_t *AuthTAG, uint32_t TAGSize)
{
  CRYP_InitTypeDef AES_CRYP_InitStructure;
  CRYP_KeyInitTypeDef AES_CRYP_KeyInitStructure;
  CRYP_IVInitTypeDef AES_CRYP_IVInitStructure;
  __IO uint32_t counter = 0;
  uint32_t busystatus = 0;
  ErrorStatus status = SUCCESS;
  uint32_t keyaddr    = (uint32_t)Key;
  uint32_t inputaddr  = (uint32_t)Input;
  uint32_t outputaddr = (uint32_t)Output;
  uint32_t headeraddr = (uint32_t)Header;
  uint32_t tagaddr = (uint32_t)AuthTAG;
  uint32_t headersize = HLength;
  uint32_t loopcounter = 0;
  uint32_t bufferidx = 0;
  uint8_t blockb0[16] = {0};/* Block B0 */
  uint8_t ctr[16] = {0}; /* Counter */
  uint32_t temptag[4] = {0}; /* temporary TAG (MAC) */
  uint32_t ctraddr = (uint32_t)ctr;
  uint32_t b0addr = (uint32_t)blockb0;
  
  /************************ Formatting the header block ***********************/
  if(headersize != 0)
  {
    /* Check that the associated data (or header) length is lower than 2^16 - 2^8 = 65536 - 256 = 65280 */
    if(headersize < 65280)
    {
      HBuffer[bufferidx++] = (uint8_t) ((headersize >> 8) & 0xFF);
      HBuffer[bufferidx++] = (uint8_t) ((headersize) & 0xFF);
      headersize += 2;
    }
    else
    {
      /* header is encoded as 0xff || 0xfe || [headersize]32, i.e., six octets */
      HBuffer[bufferidx++] = 0xFF;
      HBuffer[bufferidx++] = 0xFE;
      HBuffer[bufferidx++] = headersize & 0xff000000;
      HBuffer[bufferidx++] = headersize & 0x00ff0000;
      HBuffer[bufferidx++] = headersize & 0x0000ff00;
      HBuffer[bufferidx++] = headersize & 0x000000ff;
      headersize += 6;
    }
    /* Copy the header buffer in internal buffer "HBuffer" */
    for(loopcounter = 0; loopcounter < headersize; loopcounter++)
    {
      HBuffer[bufferidx++] = Header[loopcounter];
    }
    /* Check if the header size is modulo 16 */
    if ((headersize % 16) != 0)
    {
      /* Padd the header buffer with 0s till the HBuffer length is modulo 16 */
      for(loopcounter = headersize; loopcounter <= ((headersize/16) + 1) * 16; loopcounter++)
      {
        HBuffer[loopcounter] = 0;
      }
      /* Set the header size to modulo 16 */
      headersize = ((headersize/16) + 1) * 16;
    }
    /* set the pointer headeraddr to HBuffer */
    headeraddr = (uint32_t)HBuffer;
  }
  /************************* Formatting the block B0 **************************/
  if(headersize != 0)
  {
    blockb0[0] = 0x40;
  }
  /* Flags byte */
  blockb0[0] |= 0u | (((( (uint8_t) TAGSize - 2) / 2) & 0x07 ) << 3 ) | ( ( (uint8_t) (15 - NonceSize) - 1) & 0x07);
  
  for (loopcounter = 0; loopcounter < NonceSize; loopcounter++)
  {
    blockb0[loopcounter+1] = Nonce[loopcounter];
  }
  for ( ; loopcounter < 13; loopcounter++)
  {
    blockb0[loopcounter+1] = 0;
  }
  
  blockb0[14] = ((ILength >> 8) & 0xFF);
  blockb0[15] = (ILength & 0xFF);
  
  /************************* Formatting the initial counter *******************/
  /* Byte 0:
     Bits 7 and 6 are reserved and shall be set to 0
     Bits 3, 4, and 5 shall also be set to 0, to ensure that all the counter blocks
     are distinct from B0
     Bits 0, 1, and 2 contain the same encoding of q as in B0
  */
  ctr[0] = blockb0[0] & 0x07;
  /* byte 1 to NonceSize is the IV (Nonce) */
  for(loopcounter = 1; loopcounter < NonceSize + 1; loopcounter++)
  {
    ctr[loopcounter] = blockb0[loopcounter];
  }
  /* Set the LSB to 1 */
  ctr[15] |= 0x01;
  
  /* Crypto structures initialisation*/
  CRYP_KeyStructInit(&AES_CRYP_KeyInitStructure);
  
  switch(Keysize)
  {
    case 128:
    AES_CRYP_InitStructure.CRYP_KeySize = CRYP_KeySize_128b;
    AES_CRYP_KeyInitStructure.CRYP_Key2Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Right= __REV(*(uint32_t*)(keyaddr));
    break;
    case 192:
    AES_CRYP_InitStructure.CRYP_KeySize  = CRYP_KeySize_192b;
    AES_CRYP_KeyInitStructure.CRYP_Key1Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key1Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Right= __REV(*(uint32_t*)(keyaddr));
    break;
    case 256:
    AES_CRYP_InitStructure.CRYP_KeySize  = CRYP_KeySize_256b;
    AES_CRYP_KeyInitStructure.CRYP_Key0Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key0Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key1Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key1Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key2Right= __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Left = __REV(*(uint32_t*)(keyaddr));
    keyaddr+=4;
    AES_CRYP_KeyInitStructure.CRYP_Key3Right= __REV(*(uint32_t*)(keyaddr));
    break;
    default:
    break;
  }
  
  /* CRYP Initialization Vectors */
  AES_CRYP_IVInitStructure.CRYP_IV0Left = (__REV(*(uint32_t*)(ctraddr)));
  ctraddr+=4;
  AES_CRYP_IVInitStructure.CRYP_IV0Right= (__REV(*(uint32_t*)(ctraddr)));
  ctraddr+=4;
  AES_CRYP_IVInitStructure.CRYP_IV1Left = (__REV(*(uint32_t*)(ctraddr)));
  ctraddr+=4;
  AES_CRYP_IVInitStructure.CRYP_IV1Right= (__REV(*(uint32_t*)(ctraddr)));
  
  /*------------------ AES Encryption ------------------*/
  if(Mode == MODE_ENCRYPT) /* AES encryption */
  {
    /* Flush IN/OUT FIFOs */
    CRYP_FIFOFlush();
    
    /* Key Initialisation */
    CRYP_KeyInit(&AES_CRYP_KeyInitStructure);
    
    /* CRYP Initialization Vectors */
    CRYP_IVInit(&AES_CRYP_IVInitStructure);
    
    /* Crypto Init for Key preparation for decryption process */
    AES_CRYP_InitStructure.CRYP_AlgoDir = CRYP_AlgoDir_Encrypt;
    AES_CRYP_InitStructure.CRYP_AlgoMode = CRYP_AlgoMode_AES_CCM;
    AES_CRYP_InitStructure.CRYP_DataType = CRYP_DataType_8b;
    CRYP_Init(&AES_CRYP_InitStructure);
    
    /***************************** Init phase *********************************/
    /* Select init phase */
    CRYP_PhaseConfig(CRYP_Phase_Init);
    
    b0addr = (uint32_t)blockb0;
    /* Write the blockb0 block in the IN FIFO */
    CRYP_DataIn((*(uint32_t*)(b0addr)));
    b0addr+=4;
    CRYP_DataIn((*(uint32_t*)(b0addr)));
    b0addr+=4;
    CRYP_DataIn((*(uint32_t*)(b0addr)));
    b0addr+=4;
    CRYP_DataIn((*(uint32_t*)(b0addr)));
    
    /* Enable Crypto processor */
    CRYP_Cmd(ENABLE);
    
    /* Wait for CRYPEN bit to be 0 */
    while(CRYP_GetCmdStatus() == ENABLE)
    {
    }
    /***************************** header phase *******************************/
    if(headersize != 0)
    {
      /* Select header phase */
      CRYP_PhaseConfig(CRYP_Phase_Header);
      
      /* Enable Crypto processor */
      CRYP_Cmd(ENABLE);
      
      if(CRYP_GetCmdStatus() == DISABLE)
      {
         /* The CRYP peripheral clock is not enabled or the device doesn't embed 
            the CRYP peripheral (please check the device sales type. */
         return(ERROR);
      }
      
      for(loopcounter = 0; (loopcounter < headersize); loopcounter+=16)
      {
        /* Wait until the IFEM flag is reset */
        while(CRYP_GetFlagStatus(CRYP_FLAG_IFEM) == RESET)
        {
        }
        
        /* Write the Input block in the IN FIFO */
        CRYP_DataIn(*(uint32_t*)(headeraddr));
        headeraddr+=4;
        CRYP_DataIn(*(uint32_t*)(headeraddr));
        headeraddr+=4;
        CRYP_DataIn(*(uint32_t*)(headeraddr));
        headeraddr+=4;
        CRYP_DataIn(*(uint32_t*)(headeraddr));
        headeraddr+=4;
      }
      
      /* Wait until the complete message has been processed */
      counter = 0;
      do
      {
        busystatus = CRYP_GetFlagStatus(CRYP_FLAG_BUSY);
        counter++;
      }while ((counter != AESBUSY_TIMEOUT) && (busystatus != RESET));

      if (busystatus != RESET)
      {
        status = ERROR;
      }
    }
    
    /**************************** payload phase *******************************/
    if(ILength != 0)
    {
      /* Select payload phase */
      CRYP_PhaseConfig(CRYP_Phase_Payload);
      
      /* Enable Crypto processor */
      CRYP_Cmd(ENABLE);
      
      if(CRYP_GetCmdStatus() == DISABLE)
      {
        /* The CRYP peripheral clock is not enabled or the device doesn't embed 
           the CRYP peripheral (please check the device sales type. */
        return(ERROR);
      }
      
      for(loopcounter = 0; ((loopcounter < ILength) && (status != ERROR)); loopcounter+=16)
      {
        /* Wait until the IFEM flag is reset */
        while(CRYP_GetFlagStatus(CRYP_FLAG_IFEM) == RESET)
        {
        }
        
        /* Write the Input block in the IN FIFO */
        CRYP_DataIn(*(uint32_t*)(inputaddr));
        inputaddr+=4;
        CRYP_DataIn(*(uint32_t*)(inputaddr));
        inputaddr+=4;
        CRYP_DataIn(*(uint32_t*)(inputaddr));
        inputaddr+=4;
        CRYP_DataIn(*(uint32_t*)(inputaddr));
        inputaddr+=4;
        
        /* Wait until the complete message has been processed */
        counter = 0;
        do
        {
          busystatus = CRYP_GetFlagStatus(CRYP_FLAG_BUSY);
          counter++;
        }while ((counter != AESBUSY_TIMEOUT) && (busystatus != RESET));

        if (busystatus != RESET)
        {
          status = ERROR;
        }
        else
        {
          /* Wait until the OFNE flag is reset */
          while(CRYP_GetFlagStatus(CRYP_FLAG_OFNE) == RESET)
          {
          }
          
          /* Read the Output block from the Output FIFO */
          *(uint32_t*)(outputaddr) = CRYP_DataOut();
          outputaddr+=4;
          *(uint32_t*)(outputaddr) = CRYP_DataOut();
          outputaddr+=4;
          *(uint32_t*)(outputaddr) = CRYP_DataOut();
          outputaddr+=4;
          *(uint32_t*)(outputaddr) = CRYP_DataOut();
          outputaddr+=4;
        }
      }
    }
    
    /***************************** final phase ********************************/
    /* Select final phase */
    CRYP_PhaseConfig(CRYP_Phase_Final);
    
    /* Enable Crypto processor */
    CRYP_Cmd(ENABLE);
    
    if(CRYP_GetCmdStatus() == DISABLE)
    {
      /* The CRYP peripheral clock is not enabled or the device doesn't embed 
         the CRYP peripheral (please check the device sales type. */
      return(ERROR);
    }
    
    ctraddr = (uint32_t)ctr;
    /* Write the counter block in the IN FIFO */
    CRYP_DataIn(*(uint32_t*)(ctraddr));
    ctraddr+=4;
    CRYP_DataIn(*(uint32_t*)(ctraddr));
    ctraddr+=4;
    CRYP_DataIn(*(uint32_t*)(ctraddr));
    ctraddr+=4;
    /* Reset bit 0 (after 8-bit swap) is equivalent to reset bit 24 (before 8-bit swap) */
    CRYP_DataIn(*(uint32_t*)(ctraddr) & 0xfeffffff);
    
    /* Wait until the OFNE flag is reset */
    while(CRYP_GetFlagStatus(CRYP_FLAG_OFNE) == RESET)
    {
    }
    
    /* Read the Auth TAG in the IN FIFO */
    temptag[0] = CRYP_DataOut();
    temptag[1] = CRYP_DataOut();
    temptag[2] = CRYP_DataOut();
    temptag[3] = CRYP_DataOut();
  }
  /*------------------ AES Decryption ------------------*/
  else /* AES decryption */
  {
    /* Flush IN/OUT FIFOs */
    CRYP_FIFOFlush();
    
    /* Key Initialisation */
    CRYP_KeyInit(&AES_CRYP_KeyInitStructure);
    
    /* CRYP Initialization Vectors */
    CRYP_IVInit(&AES_CRYP_IVInitStructure);
    
    /* Crypto Init for Key preparation for decryption process */
    AES_CRYP_InitStructure.CRYP_AlgoDir = CRYP_AlgoDir_Decrypt;
    AES_CRYP_InitStructure.CRYP_AlgoMode = CRYP_AlgoMode_AES_CCM;
    AES_CRYP_InitStructure.CRYP_DataType = CRYP_DataType_8b;
    CRYP_Init(&AES_CRYP_InitStructure);
    
    /***************************** Init phase *********************************/
    /* Select init phase */
    CRYP_PhaseConfig(CRYP_Phase_Init);
    
    b0addr = (uint32_t)blockb0;
    /* Write the blockb0 block in the IN FIFO */
    CRYP_DataIn((*(uint32_t*)(b0addr)));
    b0addr+=4;
    CRYP_DataIn((*(uint32_t*)(b0addr)));
    b0addr+=4;
    CRYP_DataIn((*(uint32_t*)(b0addr)));
    b0addr+=4;
    CRYP_DataIn((*(uint32_t*)(b0addr)));
    
    /* Enable Crypto processor */
    CRYP_Cmd(ENABLE);
    
    /* Wait for CRYPEN bit to be 0 */
    while(CRYP_GetCmdStatus() == ENABLE)
    {
    }
    
    /***************************** header phase *******************************/
    if(headersize != 0)
    {
      /* Select header phase */
      CRYP_PhaseConfig(CRYP_Phase_Header);
      
      /* Enable Crypto processor */
      CRYP_Cmd(ENABLE);
      
      if(CRYP_GetCmdStatus() == DISABLE)
      {
        /* The CRYP peripheral clock is not enabled or the device doesn't embed 
           the CRYP peripheral (please check the device sales type. */
        return(ERROR);
      }
      
      for(loopcounter = 0; (loopcounter < headersize); loopcounter+=16)
      {
        /* Wait until the IFEM flag is reset */
        while(CRYP_GetFlagStatus(CRYP_FLAG_IFEM) == RESET)
        {
        }
        
        /* Write the Input block in the IN FIFO */
        CRYP_DataIn(*(uint32_t*)(headeraddr));
        headeraddr+=4;
        CRYP_DataIn(*(uint32_t*)(headeraddr));
        headeraddr+=4;
        CRYP_DataIn(*(uint32_t*)(headeraddr));
        headeraddr+=4;
        CRYP_DataIn(*(uint32_t*)(headeraddr));
        headeraddr+=4;
      }
      
      /* Wait until the complete message has been processed */
      counter = 0;
      do
      {
        busystatus = CRYP_GetFlagStatus(CRYP_FLAG_BUSY);
        counter++;
      }while ((counter != AESBUSY_TIMEOUT) && (busystatus != RESET));

      if (busystatus != RESET)
      {
        status = ERROR;
      }
    }
    
    /**************************** payload phase *******************************/
    if(ILength != 0)
    {
      /* Select payload phase */
      CRYP_PhaseConfig(CRYP_Phase_Payload);

      /* Enable Crypto processor */
      CRYP_Cmd(ENABLE);
      
      if(CRYP_GetCmdStatus() == DISABLE)
      {
        /* The CRYP peripheral clock is not enabled or the device doesn't embed 
           the CRYP peripheral (please check the device sales type. */
        return(ERROR);
      }
      
      for(loopcounter = 0; ((loopcounter < ILength) && (status != ERROR)); loopcounter+=16)
      {
        /* Wait until the IFEM flag is reset */
        while(CRYP_GetFlagStatus(CRYP_FLAG_IFEM) == RESET)
        {
        }
        
        /* Write the Input block in the IN FIFO */
        CRYP_DataIn(*(uint32_t*)(inputaddr));
        inputaddr+=4;
        CRYP_DataIn(*(uint32_t*)(inputaddr));
        inputaddr+=4;
        CRYP_DataIn(*(uint32_t*)(inputaddr));
        inputaddr+=4;
        CRYP_DataIn(*(uint32_t*)(inputaddr));
        inputaddr+=4;
        
        /* Wait until the complete message has been processed */
        counter = 0;
        do
        {
          busystatus = CRYP_GetFlagStatus(CRYP_FLAG_BUSY);
          counter++;
        }while ((counter != AESBUSY_TIMEOUT) && (busystatus != RESET));

        if (busystatus != RESET)
        {
          status = ERROR;
        }
        else
        {
          /* Wait until the OFNE flag is reset */
          while(CRYP_GetFlagStatus(CRYP_FLAG_OFNE) == RESET)
          {
          }
          
          /* Read the Output block from the Output FIFO */
          *(uint32_t*)(outputaddr) = CRYP_DataOut();
          outputaddr+=4;
          *(uint32_t*)(outputaddr) = CRYP_DataOut();
          outputaddr+=4;
          *(uint32_t*)(outputaddr) = CRYP_DataOut();
          outputaddr+=4;
          *(uint32_t*)(outputaddr) = CRYP_DataOut();
          outputaddr+=4;
        }
      }
    }
    
    /***************************** final phase ********************************/
    /* Select final phase */
    CRYP_PhaseConfig(CRYP_Phase_Final);
    
    /* Enable Crypto processor */
    CRYP_Cmd(ENABLE);
    
    if(CRYP_GetCmdStatus() == DISABLE)
    {
      /* The CRYP peripheral clock is not enabled or the device doesn't embed 
         the CRYP peripheral (please check the device sales type. */
      return(ERROR);
    }
    
    ctraddr = (uint32_t)ctr;
    /* Write the counter block in the IN FIFO */
    CRYP_DataIn(*(uint32_t*)(ctraddr));
    ctraddr+=4;
    CRYP_DataIn(*(uint32_t*)(ctraddr));
    ctraddr+=4;
    CRYP_DataIn(*(uint32_t*)(ctraddr));
    ctraddr+=4;
    /* Reset bit 0 (after 8-bit swap) is equivalent to reset bit 24 (before 8-bit swap) */
    CRYP_DataIn(*(uint32_t*)(ctraddr) & 0xfeffffff);
    
    /* Wait until the OFNE flag is reset */
    while(CRYP_GetFlagStatus(CRYP_FLAG_OFNE) == RESET)
    {
    }
    
    /* Read the Authentification TAG (MAC) in the IN FIFO */
    temptag[0] = CRYP_DataOut();
    temptag[1] = CRYP_DataOut();
    temptag[2] = CRYP_DataOut();
    temptag[3] = CRYP_DataOut();
  }
  
  /* Copy temporary authentication TAG in user TAG buffer */
  for(loopcounter = 0; (loopcounter < TAGSize); loopcounter++)
  {
    /* Set the authentication TAG buffer */
    *((uint8_t*)tagaddr+loopcounter) = *((uint8_t*)temptag+loopcounter);
  }
  
  /* Disable Crypto */
  CRYP_Cmd(DISABLE);

  return status;
}

/**
  * @}
  */ 

/**
  * @}
  */ 

/**
  * @}
  */ 

/**
  * @}
  */ 

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

