blob: f92cfbb75f6670985ce9501ccc65f18d3223b8be [file] [log] [blame]
/*
* Copyright (c) 2016, Freescale Semiconductor, Inc.
* Copyright 2016-2017 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "fsl_aes.h"
#include "string.h"
/*******************************************************************************
* Definitions
*******************************************************************************/
/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.aes"
#endif
/*!< Use standard C library memcpy */
#define aes_memcpy memcpy
/*! @brief Definitions used for writing to the AES module CFG register. */
typedef enum _aes_configuration
{
kAES_CfgEncryptEcb = 0x001u, /*!< OUTTEXT_SEL, HOLD_SEL and INBLK_SEL = Encrypt Ecb */
kAES_CfgDecryptEcb = 0x001u, /*!< OUTTEXT_SEL, HOLD_SEL and INBLK_SEL = Decrypt Ecb */
kAES_CfgEncryptCbc = 0x023u, /*!< OUTTEXT_SEL, HOLD_SEL and INBLK_SEL = Encrypt Cbc */
kAES_CfgDecryptCbc = 0x211u, /*!< OUTTEXT_SEL, HOLD_SEL and INBLK_SEL = Decrypt Cbc */
kAES_CfgEncryptCfb = 0x132u, /*!< OUTTEXT_SEL, HOLD_SEL and INBLK_SEL = Encrypt Cfb */
kAES_CfgDecryptCfb = 0x112u, /*!< OUTTEXT_SEL, HOLD_SEL and INBLK_SEL = Decrypt Cfb */
kAES_CfgCryptOfb = 0x122u, /*!< OUTTEXT_SEL, HOLD_SEL and INBLK_SEL = Ofb */
kAES_CfgCryptCtr = 0x102u, /*!< OUTTEXT_SEL, HOLD_SEL and INBLK_SEL = Ctr */
kAES_CfgCryptGcmTag = 0x102u, /*!< OUTTEXT_SEL, HOLD_SEL and INBLK_SEL = Gcm */
kAES_CfgSwap = 0x51u, /*!< INTEXT swap bytes, OUTTEXT swap bytes, PROC_EN = Encrypt/Decrypt */
kAES_CfgIntextSwap = 0x11u, /*!< INTEXT swap bytes, PROC_EN = Encrypt/Decrypt */
kAES_CfgSwapIntextHashIn = 0x12u, /*!< Swap INTEXT only, Hash INTEXT */
kAES_CfgSwapIntextHashOut = 0x16u, /*!< Swap INTEXT only, Hash OUTTEXT */
kAES_CfgSwapEnDecHashIn = 0x53u, /*!< Swap INTEXT and OUTTEXT, Encrypt/Decrypt and Hash, Hash INTEXT */
kAES_CfgSwapEnDecHashOut = 0x57u, /*!< Swap INTEXT and OUTTEXT, Encrypt/Decrypt and Hash, Hash OUTTEXT */
kAES_CfgSwapIntextEnDecHashIn = 0x13u, /*!< Swap INTEXT, Encrypt/Decrypt and Hash, Hash INTEXT */
} aes_configuration_t;
/*! @brief Actual operation with AES. */
typedef enum _aes_encryption_decryption_mode
{
kAES_ModeEncrypt = 0U, /*!< Encryption */
kAES_ModeDecrypt = 1U, /*!< Decryption */
} aes_encryption_decryption_mode_t;
/*******************************************************************************
* Prototypes
******************************************************************************/
static void aes_gcm_dec32(uint8_t *input);
/*******************************************************************************
* Code
******************************************************************************/
/*!
* @brief Reads an unaligned word.
*
* This function creates a 32-bit word from an input array of four bytes.
*
* @param src Input array of four bytes. The array can start at any address in memory.
* @return 32-bit unsigned int created from the input byte array.
*/
static inline uint32_t aes_get_word_from_unaligned(const uint8_t *srcAddr)
{
#if (!(defined(__CORTEX_M)) || (defined(__CORTEX_M) && (__CORTEX_M == 0)))
register const uint8_t *src = srcAddr;
/* Cortex M0 does not support misaligned loads */
if ((uint32_t)src & 0x3u)
{
union _align_bytes_t
{
uint32_t word;
uint8_t byte[sizeof(uint32_t)];
} my_bytes;
my_bytes.byte[0] = *src;
my_bytes.byte[1] = *(src + 1);
my_bytes.byte[2] = *(src + 2);
my_bytes.byte[3] = *(src + 3);
return my_bytes.word;
}
else
{
/* addr aligned to 0-modulo-4 so it is safe to type cast */
return *((const uint32_t *)src);
}
#elif defined(__CC_ARM) || defined(__ARMCC_VERSION)
/* -O3 optimization in Keil uses LDM instruction here (LDM r4!, {r0})
* which is wrong, because srcAddr might be unaligned.
* LDM on unaligned address causes hard-fault. so use memcpy() */
uint32_t ret;
memcpy(&ret, srcAddr, sizeof(uint32_t));
return ret;
#else
return *((const uint32_t *)srcAddr);
#endif
}
/*!
* @brief Converts a 32-bit word into a byte array.
*
* This function creates an output array of four bytes from an input 32-bit word.
*
* @param srcWord Input 32-bit unsigned integer.
* @param[out] dst Output array of four bytes. The array can start at any address in memory.
*/
static inline void aes_set_unaligned_from_word(uint32_t srcWord, uint8_t *dstAddr)
{
#if (!(defined(__CORTEX_M)) || (defined(__CORTEX_M) && (__CORTEX_M == 0)))
register uint8_t *dst = dstAddr;
/* Cortex M0 does not support misaligned stores */
if ((uint32_t)dst & 0x3u)
{
*dst++ = (srcWord & 0x000000FFU);
*dst++ = (srcWord & 0x0000FF00U) >> 8;
*dst++ = (srcWord & 0x00FF0000U) >> 16;
*dst++ = (srcWord & 0xFF000000U) >> 24;
}
else
{
*((uint32_t *)dstAddr) = srcWord; /* addr aligned to 0-modulo-4 so it is safe to type cast */
}
#elif defined(__CC_ARM) || defined(__ARMCC_VERSION)
uint32_t ret;
ret = srcWord;
memcpy(dstAddr, &ret, sizeof(uint32_t));
return;
#else
*((uint32_t *)dstAddr) = srcWord;
#endif
}
/*!
* @brief Swap bytes withing 32-bit word.
*
* This function changes endianess of a 32-bit word.
*
* @param in 32-bit unsigned integer
* @return 32-bit unsigned integer with different endianess (big endian to little endian and vice versa).
*/
static uint32_t swap_bytes(uint32_t in)
{
return (((in & 0x000000ffu) << 24) | ((in & 0x0000ff00u) << 8) | ((in & 0x00ff0000u) >> 8) |
((in & 0xff000000u) >> 24));
}
/*!
* @brief Loads key into key registers.
*
* This function loads the key into the AES key registers.
*
* @param base AES peripheral base address.
* @param key Input key in big endian format.
* @param keySize Size of the input key in bytes. Must be 16, 24 or 32.
* @return Status of the load key operation.
*/
static status_t aes_load_key(AES_Type *base, const uint8_t *key, size_t keySize)
{
uint32_t aesCfgKeyCfg;
uint32_t aesCfg;
int index;
int keyWords;
switch (keySize)
{
case 16u:
aesCfgKeyCfg = 0u;
keyWords = 4;
break;
case 24u:
aesCfgKeyCfg = 1u;
keyWords = 6;
break;
case 32u:
aesCfgKeyCfg = 2u;
keyWords = 8;
break;
default:
keyWords = 0;
break;
}
if (!keyWords)
{
/* invalidate a possibly valid key. user attempted to set a key but gives incorrect size. */
base->CMD = AES_CMD_WIPE(1);
/* wait for Idle */
while (!(base->STAT & AES_STAT_IDLE_MASK))
{
}
base->CMD = AES_CMD_WIPE(0u);
return kStatus_InvalidArgument;
}
aesCfg = base->CFG;
aesCfg &= ~AES_CFG_KEY_CFG_MASK;
aesCfg |= AES_CFG_KEY_CFG(aesCfgKeyCfg);
base->CFG = aesCfg;
for (index = 0; index < keyWords; index++)
{
base->KEY[index] = swap_bytes(aes_get_word_from_unaligned(key));
key += sizeof(uint32_t);
}
return kStatus_Success;
}
/*!
* @brief AES process one 16-byte block.
*
* This function pushes 16 bytes of data to INTEXT and pops 16 bytes of data from OUTTEXT.
*
* @param base AES peripheral base address.
* @param[out] output 16-byte block read from the OUTTEXT registers.
* @param input 16-byte block written to the INTEXT registers.
*/
static void aes_one_block(AES_Type *base, uint8_t *output, const uint8_t *input)
{
int index;
uint32_t aesStat;
/* If the IN_READY bit in STAT register is 1, write the input text in the INTEXT [3:0]
registers. */
index = 0;
while (index < 4)
{
aesStat = base->STAT;
if (aesStat & AES_STAT_IN_READY_MASK)
{
base->INTEXT[index] = aes_get_word_from_unaligned(input);
input += sizeof(uint32_t);
index++;
}
}
index = 0;
while (index < 4)
{
aesStat = base->STAT;
if (aesStat & AES_STAT_OUT_READY_MASK)
{
aes_set_unaligned_from_word(base->OUTTEXT[index], output);
output += sizeof(uint32_t);
index++;
}
}
}
/*!
* @brief AES switch to forward mode.
*
* This function sets the AES to forward mode if it is in reverse mode.
*
* @param base AES peripheral base address.
*/
static void aes_set_forward(AES_Type *base)
{
if (AES_STAT_REVERSE_MASK == (base->STAT & AES_STAT_REVERSE_MASK))
{
/* currently in reverse, switch to forward */
base->CMD = AES_CMD_SWITCH_MODE(1u);
/* wait for Idle */
while (!(base->STAT & AES_STAT_IDLE_MASK))
{
}
base->CMD = AES_CMD_SWITCH_MODE(0u);
}
}
/*!
* @brief AES switch to reverse mode.
*
* This function sets the AES to reverse mode if it is in forward mode.
*
* @param base AES peripheral base address.
*/
static void aes_set_reverse(AES_Type *base)
{
if (!(base->STAT & AES_STAT_REVERSE_MASK))
{
/* currently in forward, switch to reverse */
base->CMD = AES_CMD_SWITCH_MODE(1u);
/* wait for Idle */
while (!(base->STAT & AES_STAT_IDLE_MASK))
{
}
base->CMD = AES_CMD_SWITCH_MODE(0u);
}
}
/*!
* @brief AES write HOLDING registers.
*
* This function writes input 16-byte data to AES HOLDING registers.
*
* @param base AES peripheral base address.
* @param input 16-byte input data.
*/
static void aes_set_holding(AES_Type *base, const uint8_t *input)
{
int index;
for (index = 0; index < 4; index++)
{
base->HOLDING[index] = swap_bytes(aes_get_word_from_unaligned(input));
input += sizeof(uint32_t);
}
}
/*!
* @brief AES check KEY_VALID.
*
* This function check the KEY_VALID bit in AES status register.
*
* @param base AES peripheral base address.
* @return kStatus_Success if the key is valid.
* @return kStatus_InvalidArgument if the key is invalid.
*/
static status_t aes_check_key_valid(AES_Type *base)
{
status_t status;
/* Check the key is valid */
status =
(AES_STAT_KEY_VALID_MASK == (base->STAT & AES_STAT_KEY_VALID_MASK)) ? kStatus_Success : kStatus_InvalidArgument;
return status;
}
/*!
* brief Sets AES key.
*
* Sets AES key.
*
* param base AES peripheral base address
* param key Input key to use for encryption or decryption
* param keySize Size of the input key, in bytes. Must be 16, 24, or 32.
* return Status from Set Key operation
*/
status_t AES_SetKey(AES_Type *base, const uint8_t *key, size_t keySize)
{
return aes_load_key(base, key, keySize);
}
/*!
* brief Encrypts AES using the ECB block mode.
*
* Encrypts AES using the ECB block mode.
*
* param base AES peripheral base address
* param plaintext Input plain text to encrypt
* param[out] ciphertext Output cipher text
* param size Size of input and output data in bytes. Must be multiple of 16 bytes.
* return Status from encrypt operation
*/
status_t AES_EncryptEcb(AES_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, size_t size)
{
status_t status;
/* ECB mode, size must be 16-byte multiple */
if (size % 16u)
{
return kStatus_InvalidArgument;
}
/*5. Select the required crypto operation (encryption/decryption/hash) and AES mode in the CFG register.*/
*(volatile uint8_t *)((uint32_t)base) = (uint8_t)kAES_CfgSwap;
*(volatile uint16_t *)(((uint32_t)base + 2u)) = (uint16_t)kAES_CfgEncryptEcb;
status = aes_check_key_valid(base);
if (status != kStatus_Success)
{
return status;
}
/* make sure AES is set to forward mode */
aes_set_forward(base);
while (size)
{
aes_one_block(base, ciphertext, plaintext);
ciphertext += 16;
plaintext += 16;
size -= 16u;
}
return status;
}
/*!
* brief Decrypts AES using the ECB block mode.
*
* Decrypts AES using the ECB block mode.
*
* param base AES peripheral base address
* param ciphertext Input ciphertext to decrypt
* param[out] plaintext Output plain text
* param size Size of input and output data in bytes. Must be multiple of 16 bytes.
* return Status from decrypt operation
*/
status_t AES_DecryptEcb(AES_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, size_t size)
{
status_t status;
/* ECB mode, size must be 16-byte multiple */
if (size % 16u)
{
return kStatus_InvalidArgument;
}
status = aes_check_key_valid(base);
if (status != kStatus_Success)
{
return status;
}
/*5. Select the required crypto operation (encryption/decryption/hash) and AES mode in the CFG register.*/
*(volatile uint8_t *)((uint32_t)base) = (uint8_t)kAES_CfgSwap;
*(volatile uint16_t *)(((uint32_t)base + 2u)) = (uint16_t)kAES_CfgDecryptEcb;
/* make sure AES is set to reverse mode */
aes_set_reverse(base);
while (size)
{
aes_one_block(base, plaintext, ciphertext);
ciphertext += 16;
plaintext += 16;
size -= 16u;
}
return status;
}
/*!
* @brief Main function for CBC, CFB and OFB modes.
*
* @param base AES peripheral base address
* @param input Input plaintext for encryption, input ciphertext for decryption.
* @param[out] output Output ciphertext for encryption, output plaintext for decryption.
* @param size Size of input and output data in bytes. For CBC and CFB it must be multiple of 16-bytes.
* @param iv Input initial vector to combine with the first input block.
* @return Status from the operation.
*/
static status_t aes_block_mode(AES_Type *base,
const uint8_t *input,
uint8_t *output,
size_t size,
const uint8_t iv[AES_IV_SIZE],
aes_configuration_t configuration)
{
status_t status;
/* CBC and CFB128 mode, size must be 16-byte multiple */
switch (configuration)
{
case kAES_CfgEncryptCfb:
case kAES_CfgDecryptCfb:
case kAES_CfgEncryptCbc:
case kAES_CfgDecryptCbc:
if (size % 16u)
{
status = kStatus_InvalidArgument;
}
else
{
status = kStatus_Success;
}
break;
case kAES_CfgCryptOfb:
status = kStatus_Success;
break;
default:
status = kStatus_InvalidArgument;
break;
}
if (status != kStatus_Success)
{
return status;
}
/* Check the key is valid */
status = aes_check_key_valid(base);
if (status != kStatus_Success)
{
return status;
}
/*5. Select the required crypto operation (encryption/decryption/hash) and AES mode in the CFG register.*/
*(volatile uint8_t *)((uint32_t)base) = (uint8_t)kAES_CfgSwap;
*(volatile uint16_t *)(((uint32_t)base + 2u)) = (uint16_t)configuration;
/* CBC decrypt use reverse AES cipher. */
switch (configuration)
{
case kAES_CfgDecryptCbc:
aes_set_reverse(base);
break;
default:
aes_set_forward(base);
break;
}
/* For CBC, CFB and OFB modes write the Initialization Vector (IV) in the HOLDING
[3:0] registers. For CTR mode write the initial counter value in the HOLDING [3:0]
registers. */
aes_set_holding(base, iv);
while (size >= 16u)
{
aes_one_block(base, output, input);
size -= 16u;
output += 16;
input += 16;
}
/* OFB can have non-block multiple size. CBC and CFB128 have size zero at this moment. */
if (size)
{
uint8_t blkTemp[16] = {0};
aes_memcpy(blkTemp, input, size);
aes_one_block(base, blkTemp, blkTemp);
aes_memcpy(output, blkTemp, size);
}
return status;
}
/*!
* brief Encrypts AES using CBC block mode.
*
* param base AES peripheral base address
* param plaintext Input plain text to encrypt
* param[out] ciphertext Output cipher text
* param size Size of input and output data in bytes. Must be multiple of 16 bytes.
* param iv Input initial vector to combine with the first input block.
* return Status from encrypt operation
*/
status_t AES_EncryptCbc(
AES_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, size_t size, const uint8_t iv[AES_IV_SIZE])
{
return aes_block_mode(base, plaintext, ciphertext, size, iv, kAES_CfgEncryptCbc);
}
/*!
* brief Decrypts AES using CBC block mode.
*
* param base AES peripheral base address
* param ciphertext Input cipher text to decrypt
* param[out] plaintext Output plain text
* param size Size of input and output data in bytes. Must be multiple of 16 bytes.
* param iv Input initial vector to combine with the first input block.
* return Status from decrypt operation
*/
status_t AES_DecryptCbc(
AES_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, size_t size, const uint8_t iv[AES_IV_SIZE])
{
return aes_block_mode(base, ciphertext, plaintext, size, iv, kAES_CfgDecryptCbc);
}
/*!
* brief Encrypts AES using CFB block mode.
*
* param base AES peripheral base address
* param plaintext Input plain text to encrypt
* param[out] ciphertext Output cipher text
* param size Size of input and output data in bytes. Must be multiple of 16 bytes.
* param iv Input Initial vector to be used as the first input block.
* return Status from encrypt operation
*/
status_t AES_EncryptCfb(
AES_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, size_t size, const uint8_t iv[AES_IV_SIZE])
{
return aes_block_mode(base, plaintext, ciphertext, size, iv, kAES_CfgEncryptCfb);
}
/*!
* brief Decrypts AES using CFB block mode.
*
* param base AES peripheral base address
* param ciphertext Input cipher text to decrypt
* param[out] plaintext Output plain text
* param size Size of input and output data in bytes. Must be multiple of 16 bytes.
* param iv Input Initial vector to be used as the first input block.
* return Status from decrypt operation
*/
status_t AES_DecryptCfb(
AES_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, size_t size, const uint8_t iv[AES_IV_SIZE])
{
return aes_block_mode(base, ciphertext, plaintext, size, iv, kAES_CfgDecryptCfb);
}
/*!
* brief Encrypts AES using OFB block mode.
*
* param base AES peripheral base address
* param plaintext Input plain text to encrypt
* param[out] ciphertext Output cipher text
* param size Size of input and output data in bytes.
* param iv Input Initial vector to be used as the first input block.
* return Status from encrypt operation
*/
status_t AES_EncryptOfb(
AES_Type *base, const uint8_t *plaintext, uint8_t *ciphertext, size_t size, const uint8_t iv[AES_IV_SIZE])
{
return aes_block_mode(base, plaintext, ciphertext, size, iv, kAES_CfgCryptOfb);
}
/*!
* brief Decrypts AES using OFB block mode.
*
* param base AES peripheral base address
* param ciphertext Input cipher text to decrypt
* param[out] plaintext Output plain text
* param size Size of input and output data in bytes.
* param iv Input Initial vector to be used as the first input block.
* return Status from decrypt operation
*/
status_t AES_DecryptOfb(
AES_Type *base, const uint8_t *ciphertext, uint8_t *plaintext, size_t size, const uint8_t iv[AES_IV_SIZE])
{
return aes_block_mode(base, ciphertext, plaintext, size, iv, kAES_CfgCryptOfb);
}
/*!
* brief Encrypts or decrypts AES using CTR block mode.
*
* Encrypts or decrypts AES using CTR block mode.
* AES CTR mode uses only forward AES cipher and same algorithm for encryption and decryption.
* The only difference between encryption and decryption is that, for encryption, the input argument
* is plain text and the output argument is cipher text. For decryption, the input argument is cipher text
* and the output argument is plain text.
*
* param base AES peripheral base address
* param input Input data for CTR block mode
* param[out] output Output data for CTR block mode
* param size Size of input and output data in bytes
* param[in,out] counter Input counter (updates on return)
* param[out] counterlast Output cipher of last counter, for chained CTR calls. NULL can be passed if chained calls are
* not used.
* param[out] szLeft Output number of bytes in left unused in counterlast block. NULL can be passed if chained calls
* are not used.
* return Status from crypt operation
*/
status_t AES_CryptCtr(AES_Type *base,
const uint8_t *input,
uint8_t *output,
size_t size,
uint8_t counter[AES_BLOCK_SIZE],
uint8_t counterlast[AES_BLOCK_SIZE],
size_t *szLeft)
{
status_t status;
uint32_t lastSize;
uint8_t lastBlock[AES_BLOCK_SIZE] = {0};
uint8_t *lastEncryptedCounter;
/* Check the key is valid */
status = aes_check_key_valid(base);
if (status != kStatus_Success)
{
return status;
}
/*5. Select the required crypto operation (encryption/decryption/hash) and AES mode in the CFG register.*/
*(volatile uint8_t *)((uint32_t)base) = (uint8_t)kAES_CfgSwap;
*(volatile uint16_t *)(((uint32_t)base + 2u)) = (uint16_t)kAES_CfgCryptCtr;
aes_set_forward(base);
/* For CBC, CFB and OFB modes write the Initialization Vector (IV) in the HOLDING
[3:0] registers. For CTR mode write the initial counter value in the HOLDING [3:0]
registers. */
aes_set_holding(base, counter);
/* set counter increment by one */
base->CTR_INCR = 0x1U;
/* Split the size into full 16-byte chunks and last incomplete block */
lastSize = 0U;
if (size <= 16U)
{
lastSize = size;
size = 0U;
}
else
{
/* Process all 16-byte data chunks. */
lastSize = size % 16U;
if (lastSize == 0U)
{
lastSize = 16U;
size -= 16U;
}
else
{
size -= lastSize; /* size will be rounded down to 16 byte boundary. remaining bytes in lastSize */
}
}
/* full 16-byte blocks */
while (size)
{
aes_one_block(base, output, input);
size -= 16u;
input += 16;
output += 16;
}
/* last block */
if (counterlast)
{
lastEncryptedCounter = counterlast;
}
else
{
lastEncryptedCounter = lastBlock;
}
aes_one_block(base, lastEncryptedCounter, lastBlock); /* lastBlock is all zeroes, so I get directly ECB of the last
counter, as the XOR with zeroes doesn't change */
/* remain output = input XOR counterlast */
for (uint32_t i = 0; i < lastSize; i++)
{
output[i] = input[i] ^ lastEncryptedCounter[i];
}
/* read counter value after last encryption */
aes_set_unaligned_from_word(swap_bytes(base->HOLDING[3]), &counter[12]);
aes_gcm_dec32(&counter[0]); /* TODO HW problem? (first time it increments HOLDING3 twice) */
if (szLeft)
{
*szLeft = 16U - lastSize;
}
return status;
}
/*!
* @brief AES write input to INTEXT registers.
*
* This function writes input 16-byte data to AES INTEXT registers
* and wait for the engine Idle.
*
* @param base AES peripheral base address.
* @param input 16-byte input data.
*/
static void aes_gcm_one_block_input_only(AES_Type *base, const uint8_t *input)
{
int index;
uint32_t aesStat;
/* If the IN_READY bit in STAT register is 1, write the input text in the INTEXT [3:0]
registers. */
index = 0;
while (index < 4)
{
aesStat = base->STAT;
if (aesStat & AES_STAT_IN_READY_MASK)
{
base->INTEXT[index] = aes_get_word_from_unaligned(input);
input += sizeof(uint32_t);
index++;
}
}
while (0 == (base->STAT & AES_STAT_IDLE_MASK))
{
}
}
/*!
* @brief AES execute command.
*
* This function issues command to AES CMD register.
*
* @param base AES peripheral base address.
* @param cmdMask Bit mask for the command to be issued.
*/
static void aes_command(AES_Type *base, uint32_t cmdMask)
{
base->CMD = cmdMask;
/* wait for Idle */
while (!(base->STAT & AES_STAT_IDLE_MASK))
{
}
base->CMD = 0;
}
/*!
* @brief AES read GCM_TAG.
*
* This function reads data from GCM_TAG registers.
*
* @param base AES peripheral base address.
* @param output 16 byte memory to write the GCM_TAG to.
*/
static void aes_gcm_get_tag(AES_Type *base, uint8_t *output)
{
int index;
for (index = 0; index < 4; index++)
{
aes_set_unaligned_from_word(swap_bytes(base->GCM_TAG[index]), output);
output += sizeof(uint32_t);
}
}
/*!
* @brief AES GCM check validity of input arguments.
*
* This function checks the validity of input arguments.
*
* @param base AES peripheral base address.
* @param src Source address (plaintext or ciphertext).
* @param iv Initialization vector address.
* @param aad Additional authenticated data address.
* @param dst Destination address (plaintext or ciphertext).
* @param inputSize Size of input (same size as output) data in bytes.
* @param ivSize Size of Initialization vector in bytes.
* @param aadSize Size of additional data in bytes.
* @param tagSize Size of the GCM tag in bytes.
*/
static status_t aes_gcm_check_input_args(AES_Type *base,
const uint8_t *src,
const uint8_t *iv,
const uint8_t *aad,
uint8_t *dst,
size_t inputSize,
size_t ivSize,
size_t aadSize,
size_t tagSize)
{
if (!base)
{
return kStatus_InvalidArgument;
}
/* tag can be NULL to skip tag processing */
if ((ivSize && (!iv)) || (aadSize && (!aad)) || (inputSize && ((!src) || (!dst))))
{
return kStatus_InvalidArgument;
}
/* octet length of tag (tagSize) must be element of 4,8,12,13,14,15,16 */
if (((tagSize > 16u) || (tagSize < 12u)) && (tagSize != 4u) && (tagSize != 8u))
{
return kStatus_InvalidArgument;
}
/* no IV AAD DATA makes no sense */
if (0 == (inputSize + ivSize + aadSize))
{
return kStatus_InvalidArgument;
}
/* check length of input strings. This is more strict than the GCM specificaiton due to 32-bit architecture.
* The API interface would work on 64-bit architecture as well, but as it has not been tested, let it limit to
* 32-bits.
*/
if (!((ivSize >= 1u) && (sizeof(size_t) <= 4u)))
{
return kStatus_InvalidArgument;
}
return kStatus_Success;
}
/*!
* @brief Increment a 16 byte integer.
*
* This function increments by one a 16 byte integer.
*
* @param input Pointer to a 16 byte integer to be incremented by one.
*/
static void aes_gcm_incr32(uint8_t *input)
{
int i = 15;
while (input[i] == (uint8_t)0xFFu)
{
input[i] = (uint8_t)0x00u;
i--;
if (i < 0)
{
return;
}
}
if (i >= 0)
{
input[i] += (uint8_t)1u;
}
}
/*!
* @brief Decrement a 16 byte integer.
*
* This function decrements by one a 16 byte integer.
*
* @param input Pointer to a 16 byte integer to be decremented by one.
*/
static void aes_gcm_dec32(uint8_t *input)
{
int i = 15;
while (input[i] == (uint8_t)0x00u)
{
input[i] = (uint8_t)0xFFu;
i--;
if (i < 0)
{
return;
}
}
if (i >= 0)
{
input[i] -= (uint8_t)1u;
}
}
/*!
* @brief AES read GF128_Z registers.
*
* This function reads AES module GF128_Z registers.
*
* @param base AES peripheral base address.
* @param input Pointer to a 16 byte integer to write the GF128_Z value to.
*/
static void aes_get_gf128(AES_Type *base, uint8_t *output)
{
int i;
for (i = 0; i < 4; i++)
{
aes_set_unaligned_from_word(swap_bytes(base->GF128_Z[i]), output);
output += sizeof(uint32_t);
}
}
/*!
* @brief GCM process.
*
* This function is the main function for AES GCM encryption/decryption and tag generation/comparison.
*
* @param base AES peripheral base address.
* @param encryptMode Whether the actual operation is an encryption or a decryption.
* @param src Input plaintext for encryption or ciphertext for decryption.
* @param inputSize Size of input and output in bytes.
* @param iv Input initial vector.
* @param ivSize Size of initial vector in bytes.
* @param aad Additional authenticated data.
* @param aadSize Size of additional authenticated data in bytes.
* @param dst Output ciphertext for encryption or plaintext for decryption.
* @param tag For encryption, GCM tag output. For decryption, GCM tag input for comparison.
* @param tagSize Size of the GCM tag in bytes.
*/
static status_t aes_gcm_process(AES_Type *base,
aes_encryption_decryption_mode_t encryptMode,
const uint8_t *src,
size_t inputSize,
const uint8_t *iv,
size_t ivSize,
const uint8_t *aad,
size_t aadSize,
uint8_t *dst,
uint8_t *tag,
size_t tagSize)
{
status_t status;
uint8_t blkZero[16] = {0};
uint8_t blkJ0[16] = {0};
uint8_t blkTag[16] = {0};
uint32_t saveSize;
uint32_t saveAadSize;
uint32_t saveIvSize;
uint8_t lastBlock[AES_BLOCK_SIZE] = {0};
status = aes_gcm_check_input_args(base, src, iv, aad, dst, inputSize, ivSize, aadSize, tagSize);
if (status != kStatus_Success)
{
return status;
}
status = aes_check_key_valid(base);
if (status != kStatus_Success)
{
return status;
}
saveSize = inputSize;
saveAadSize = aadSize;
saveIvSize = ivSize;
/* 1. Let H = CIPHK(0_128). */
*(volatile uint8_t *)((uint32_t)base) = (uint8_t)
kAES_CfgIntextSwap; /* do not swap OUTTEXT, as OUTTEXT is copied for further use into GF128_Y register */
*(volatile uint16_t *)(((uint32_t)base + 2u)) = (uint16_t)kAES_CfgEncryptEcb;
/* make sure AES is set to forward mode */
aes_set_forward(base);
/* move 128-bit zeroes to INTEXT */
aes_gcm_one_block_input_only(base, blkZero);
/* set COPY_TO_Y command */
aes_command(base, AES_CMD_COPY_TO_Y(1));
if (ivSize != 12u)
{
/* if ivSize is not 12 bytes, we have to compute GF128 hash to get the initial counter J0 */
uint8_t ivBlkZero[16] = {0};
*(volatile uint8_t *)((uint32_t)base) = (uint8_t)kAES_CfgSwapIntextHashIn;
while (ivSize >= 16u)
{
aes_gcm_one_block_input_only(base, iv);
iv += 16;
ivSize -= 16u;
}
if (ivSize)
{
aes_memcpy(ivBlkZero, iv, ivSize);
aes_gcm_one_block_input_only(base, ivBlkZero);
}
aes_memcpy(ivBlkZero, blkZero, 16);
aes_set_unaligned_from_word(swap_bytes(8 * saveIvSize), &ivBlkZero[12]);
aes_gcm_one_block_input_only(base, ivBlkZero);
aes_get_gf128(base, blkJ0);
aes_gcm_incr32(blkJ0);
/* put back the hash sub-key. this will abort running GF128, because we have to start new GF128 again for AAD
* and C */
*(volatile uint8_t *)((uint32_t)base) = (uint8_t)
kAES_CfgIntextSwap; /* do not swap OUTTEXT, as OUTTEXT is copied for further use into GF128_Y register */
*(volatile uint16_t *)(((uint32_t)base + 2u)) = (uint16_t)kAES_CfgEncryptEcb;
aes_gcm_one_block_input_only(base, blkZero);
aes_command(base, AES_CMD_COPY_TO_Y(1));
}
else
{
aes_memcpy(blkJ0, iv, 12);
blkJ0[15] = 0x02U; /* add one to Counter for the first encryption with plaintext - see GCM specification */
}
/* process all AAD as GF128 of INTEXT, pad to full 16-bytes block with zeroes */
if (aadSize)
{
*(volatile uint8_t *)((uint32_t)base) = (uint8_t)kAES_CfgSwapIntextHashIn;
while (aadSize >= 16u)
{
aes_gcm_one_block_input_only(base, aad);
aad += 16;
aadSize -= 16u;
}
if (aadSize)
{
uint8_t aadBlkZero[16] = {0};
aes_memcpy(aadBlkZero, aad, aadSize);
aes_gcm_one_block_input_only(base, aadBlkZero);
}
}
/* switch to EnryptDecryptHash, Hash of OUTTEXT, EncryptDecrypt in GCM mode */
/* Process all plaintext, pad to full 16-bytes block with zeroes */
if (encryptMode == kAES_ModeEncrypt)
{
*(volatile uint8_t *)((uint32_t)base) =
(uint8_t)kAES_CfgSwapEnDecHashOut; /* Encrypt operation adds output to hash */
}
else
{
*(volatile uint8_t *)((uint32_t)base) =
(uint8_t)kAES_CfgSwapEnDecHashIn; /* Decrypt operation adds input to hash */
}
*(volatile uint16_t *)(((uint32_t)base + 2u)) = (uint16_t) /*kAES_CfgCryptGcmTag*/ kAES_CfgCryptCtr;
aes_set_holding(base, blkJ0);
/* set counter increment by one */
base->CTR_INCR = 0x1U;
if (inputSize)
{
/* full 16-byte blocks */
while (inputSize >= 16u)
{
aes_one_block(base, dst, src);
inputSize -= 16u;
src += 16;
dst += 16;
}
/* last incomplete block. output shall be padded. */
if (inputSize)
{
if (encryptMode == kAES_ModeEncrypt)
{
uint8_t outputBlkZero[16] = {0};
/* I need to pad ciphertext, so I turn off the hash, and after I have ciphertext, I pad it with zeroes
* and hash manually */
*(volatile uint8_t *)((uint32_t)base) = (uint8_t)kAES_CfgSwap;
aes_memcpy(lastBlock, src, inputSize);
aes_one_block(base, lastBlock, lastBlock);
aes_memcpy(dst, lastBlock, inputSize);
/* pad the last output block with zeroes and add it to GF128 hash */
aes_memcpy(outputBlkZero, lastBlock, inputSize);
*(volatile uint8_t *)((uint32_t)base) = (uint8_t)kAES_CfgSwapIntextHashIn;
aes_gcm_one_block_input_only(base, outputBlkZero);
}
else
{
aes_memcpy(lastBlock, src, inputSize);
aes_one_block(base, lastBlock, lastBlock);
aes_memcpy(dst, lastBlock, inputSize);
}
}
}
/* Process AAD len plus Ciphertext len */
*(volatile uint8_t *)((uint32_t)base) = (uint8_t)kAES_CfgIntextSwap;
*(volatile uint16_t *)(((uint32_t)base + 2u)) = (uint16_t)kAES_CfgEncryptEcb;
aes_gcm_dec32(blkJ0);
aes_gcm_one_block_input_only(base, blkJ0);
*(volatile uint8_t *)((uint32_t)base) = (uint8_t)kAES_CfgSwapIntextHashIn;
if (saveSize)
{
aes_set_unaligned_from_word(swap_bytes(saveSize * 8u), &blkZero[12]);
}
if (saveAadSize)
{
aes_set_unaligned_from_word(swap_bytes(saveAadSize * 8u), &blkZero[4]);
}
aes_gcm_one_block_input_only(base, blkZero); /* len(A) || len(C) */
aes_gcm_get_tag(base, blkTag);
if (tag && tagSize)
{
if (encryptMode == kAES_ModeEncrypt)
{
aes_memcpy(tag, blkTag, tagSize);
}
else
{
size_t i = 0;
uint32_t equal = 0;
uint32_t chXor;
while (i < tagSize)
{
chXor = tag[i] ^ blkTag[i];
equal |= chXor;
i++;
}
if (equal != 0)
{
status = kStatus_Fail;
}
}
}
return status;
}
/*!
* brief Encrypts AES and tags using GCM block mode.
*
* Encrypts AES and optionally tags using GCM block mode. If plaintext is NULL, only the GHASH is calculated and output
* in the 'tag' field.
*
* param base AES peripheral base address
* param plaintext Input plain text to encrypt
* param[out] ciphertext Output cipher text.
* param size Size of input and output data in bytes
* param iv Input initial vector
* param ivSize Size of the IV
* param aad Input additional authentication data
* param aadSize Input size in bytes of AAD
* param[out] tag Output hash tag. Set to NULL to skip tag processing.
* param tagSize Input size of the tag to generate, in bytes. Must be 4,8,12,13,14,15 or 16.
* return Status from encrypt operation
*/
status_t AES_EncryptTagGcm(AES_Type *base,
const uint8_t *plaintext,
uint8_t *ciphertext,
size_t size,
const uint8_t *iv,
size_t ivSize,
const uint8_t *aad,
size_t aadSize,
uint8_t *tag,
size_t tagSize)
{
status_t status;
status =
aes_gcm_process(base, kAES_ModeEncrypt, plaintext, size, iv, ivSize, aad, aadSize, ciphertext, tag, tagSize);
return status;
}
/*!
* brief Decrypts AES and authenticates using GCM block mode.
*
* Decrypts AES and optionally authenticates using GCM block mode. If ciphertext is NULL, only the GHASH is calculated
* and compared with the received GHASH in 'tag' field.
*
* param base AES peripheral base address
* param ciphertext Input cipher text to decrypt
* param[out] plaintext Output plain text.
* param size Size of input and output data in bytes
* param iv Input initial vector
* param ivSize Size of the IV
* param aad Input additional authentication data
* param aadSize Input size in bytes of AAD
* param tag Input hash tag to compare. Set to NULL to skip tag processing.
* param tagSize Input size of the tag, in bytes. Must be 4, 8, 12, 13, 14, 15, or 16.
* return Status from decrypt operation
*/
status_t AES_DecryptTagGcm(AES_Type *base,
const uint8_t *ciphertext,
uint8_t *plaintext,
size_t size,
const uint8_t *iv,
size_t ivSize,
const uint8_t *aad,
size_t aadSize,
const uint8_t *tag,
size_t tagSize)
{
uint8_t temp_tag[16] = {0}; /* max. octet length of MAC (tag) is 16 */
uint8_t *tag_ptr;
status_t status;
tag_ptr = NULL;
if (tag)
{
aes_memcpy(temp_tag, tag, tagSize);
tag_ptr = &temp_tag[0];
}
status = aes_gcm_process(base, kAES_ModeDecrypt, ciphertext, size, iv, ivSize, aad, aadSize, plaintext, tag_ptr,
tagSize);
return status;
}
void AES_Init(AES_Type *base)
{
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
/* ungate clock */
CLOCK_EnableClock(kCLOCK_Aes);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
}
void AES_Deinit(AES_Type *base)
{
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
/* gate clock */
CLOCK_DisableClock(kCLOCK_Aes);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
}