/***************************************************************************//**
* @file
* @brief Advanced Encryption Standard (AES) accelerator peripheral API.
*******************************************************************************
* # License
* Copyright 2018 Silicon Laboratories Inc. www.silabs.com
*******************************************************************************
*
* SPDX-License-Identifier: Zlib
*
* The licensor of this software is Silicon Laboratories Inc.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
******************************************************************************/
#include "em_aes.h"
#if defined(AES_COUNT) && (AES_COUNT > 0)
#include "sl_assert.h"
#include
/***************************************************************************//**
* @addtogroup aes
* @details
* This module contains functions to control the AES peripheral of Silicon
* Labs 32-bit MCUs and SoCs.
* @{
******************************************************************************/
/*******************************************************************************
******************************* DEFINES ***********************************
******************************************************************************/
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
#define AES_BLOCKSIZE 16
/* Convenience macro to fetch a 32-bit unsigned integer from a potentially
* unaligned byte array with native endianness.
* Optimising compilers should be able to recognize that this operation is a
* direct access on architectures which support unaligned access. */
#define __UINT_FROM_BYTEARRAY(ptr_to_array) \
( ((uint32_t)((ptr_to_array)[3])) << 24 \
| ((uint32_t)((ptr_to_array)[2])) << 16 \
| ((uint32_t)((ptr_to_array)[1])) << 8 \
| ((uint32_t)((ptr_to_array)[0])) << 0)
/* Convenience macro to put a 32-bit unsigned integer into a potentially
* unaligned byte array with native endianness.
* Optimising compilers should be able to recognize that this operation is a
* direct write on architectures which support unaligned access. */
#define __UINT_TO_BYTEARRAY(ptr_to_array, value) \
do { \
uint32_t valbuf = value; \
(ptr_to_array)[3] = (valbuf >> 24) & 0xFFu; \
(ptr_to_array)[2] = (valbuf >> 16) & 0xFFu; \
(ptr_to_array)[1] = (valbuf >> 8) & 0xFFu; \
(ptr_to_array)[0] = (valbuf >> 0) & 0xFFu; \
} while (0)
/* Convenience macro to fetch a byte-reversed 32-bit unsigned integer from a
* potentially unaligned byte array. */
#define __REV_FROM_BYTEARRAY(ptr_to_array) \
__REV(__UINT_FROM_BYTEARRAY(ptr_to_array))
/* Convenience macro to put a byte-reversed 32-bit unsigned integer into a
* potentially unaligned byte array. */
#define __REV_TO_BYTEARRAY(ptr_to_array, value) \
__UINT_TO_BYTEARRAY(ptr_to_array, __REV(value))
/** @endcond */
/*******************************************************************************
************************** GLOBAL FUNCTIONS *******************************
******************************************************************************/
/***************************************************************************//**
* @brief
* Cipher-block chaining (CBC) cipher mode encryption/decryption, 128 bit key.
*
* @details
* Encryption:
* @verbatim
* Plaintext Plaintext
* | |
* V V
* InitVector ->XOR +-------------->XOR
* | | |
* V | V
* +--------------+ | +--------------+
* Key ->| Block cipher | | Key ->| Block cipher |
* | encryption | | | encryption |
* +--------------+ | +--------------+
* |---------+ |
* V V
* Ciphertext Ciphertext
* @endverbatim
* Decryption:
* @verbatim
* Ciphertext Ciphertext
* |----------+ |
* V | V
* +--------------+ | +--------------+
* Key ->| Block cipher | | Key ->| Block cipher |
* | decryption | | | decryption |
* +--------------+ | +--------------+
* | | |
* V | V
* InitVector ->XOR +-------------->XOR
* | |
* V V
* Plaintext Plaintext
* @endverbatim
* See general comments on layout and byte ordering of parameters.
*
* @param[out] out
* A buffer to place encrypted/decrypted data. Must be at least @p len long. It
* may be set equal to @p in, in which case the input buffer is overwritten.
*
* @param[in] in
* A buffer holding data to encrypt/decrypt. Must be at least @p len long.
*
* @param[in] len
* A number of bytes to encrypt/decrypt. Must be a multiple of 16.
*
* @param[in] key
* When encrypting, this is the 128 bit encryption key. When
* decrypting, this is the 128 bit decryption key. The decryption key may
* be generated from the encryption key with AES_DecryptKey128().
* On devices supporting key buffering, this argument can be null. If so, the
* key will not be loaded as it is assumed the key has been loaded
* into KEYHA previously.
*
* @param[in] iv
* 128 bit initialization vector.
*
* @param[in] encrypt
* Set to true to encrypt, false to decrypt.
******************************************************************************/
void AES_CBC128(uint8_t *out,
const uint8_t *in,
unsigned int len,
const uint8_t *key,
const uint8_t *iv,
bool encrypt)
{
int i;
/* Need to buffer one block when decrypting in case 'out' replaces 'in'. */
uint32_t prev[AES_BLOCKSIZE / sizeof(uint32_t)];
EFM_ASSERT(!(len % AES_BLOCKSIZE));
/* A number of blocks to process. */
len /= AES_BLOCKSIZE;
#if defined(AES_CTRL_KEYBUFEN)
/* Load the key into a high key for a key buffer usage. */
for (i = 3; i >= 0; i--) {
AES->KEYHA = __REV_FROM_BYTEARRAY(&key[i * 4]);
}
#endif
if (encrypt) {
/* Enable encryption with auto start using XOR. */
#if defined(AES_CTRL_KEYBUFEN)
AES->CTRL = AES_CTRL_KEYBUFEN | AES_CTRL_XORSTART;
#else
AES->CTRL = AES_CTRL_XORSTART;
#endif
/* Load an initialization vector. Since writing to DATA, it will */
/* not trigger encryption. */
for (i = 3; i >= 0; i--) {
AES->DATA = __REV_FROM_BYTEARRAY(&iv[i * 4]);
}
/* Encrypt data. */
while (len--) {
#if !defined(AES_CTRL_KEYBUFEN)
/* Load key. */
for (i = 3; i >= 0; i--) {
AES->KEYLA = __REV_FROM_BYTEARRAY(&key[i * 4]);
}
#endif
/* Load data and trigger encryption. */
for (i = 3; i >= 0; i--) {
AES->XORDATA = __REV_FROM_BYTEARRAY(&in[i * 4]);
}
in += AES_BLOCKSIZE;
/* Wait for completion. */
while (AES->STATUS & AES_STATUS_RUNNING)
;
/* Save encrypted data. */
for (i = 3; i >= 0; i--) {
__REV_TO_BYTEARRAY(&out[i * 4], AES->DATA);
}
out += AES_BLOCKSIZE;
}
} else {
/* Select decryption mode. */
#if defined(AES_CTRL_KEYBUFEN)
AES->CTRL = AES_CTRL_DECRYPT | AES_CTRL_KEYBUFEN | AES_CTRL_DATASTART;
#else
AES->CTRL = AES_CTRL_DECRYPT | AES_CTRL_DATASTART;
#endif
/* Copy initialization vector to the previous buffer to avoid special handling. */
memcpy(prev, iv, AES_BLOCKSIZE);
/* Decrypt data. */
while (len--) {
#if !defined(AES_CTRL_KEYBUFEN)
/* Load key. */
for (i = 3; i >= 0; i--) {
AES->KEYLA = __REV_FROM_BYTEARRAY(&key[i * 4]);
}
#endif
/* Load data and trigger decryption. */
for (i = 3; i >= 0; i--) {
AES->DATA = __REV_FROM_BYTEARRAY(&in[i * 4]);
}
/* Wait for completion. */
while (AES->STATUS & AES_STATUS_RUNNING)
;
/* To avoid an additional buffer, hardware is used directly for XOR and buffer. */
/* (Writing to XORDATA will not trigger encoding, triggering enabled on DATA.) */
for (i = 3; i >= 0; i--) {
AES->XORDATA = __REV(prev[i]);
}
memcpy(prev, in, AES_BLOCKSIZE);
in += AES_BLOCKSIZE;
/* Fetch decrypted data in a separate loop */
/* due to internal auto-shifting of words. */
for (i = 3; i >= 0; i--) {
__REV_TO_BYTEARRAY(&out[i * 4], AES->DATA);
}
out += AES_BLOCKSIZE;
}
}
}
#if defined(AES_CTRL_AES256)
/***************************************************************************//**
* @brief
* Cipher-block chaining (CBC) cipher mode encryption/decryption, 256 bit key.
*
* @details
* See AES_CBC128() for the CBC figure.
*
* See general comments on layout and byte ordering of parameters.
*
* @param[out] out
* A buffer to place encrypted/decrypted data. Must be at least @p len long. It
* may be set equal to @p in, in which case the input buffer is overwritten.
*
* @param[in] in
* A buffer holding data to encrypt/decrypt. Must be at least @p len long.
*
* @param[in] len
* A number of bytes to encrypt/decrypt. Must be a multiple of 16.
*
* @param[in] key
* When encrypting, this is the 256 bit encryption key. When
* decrypting, this is the 256 bit decryption key. The decryption key may
* be generated from the encryption key with AES_DecryptKey256().
*
* @param[in] iv
* 128 bit initialization vector to use.
*
* @param[in] encrypt
* Set to true to encrypt, false to decrypt.
******************************************************************************/
void AES_CBC256(uint8_t *out,
const uint8_t *in,
unsigned int len,
const uint8_t *key,
const uint8_t *iv,
bool encrypt)
{
int i;
int j;
/* Buffer one block when decrypting in case output replaces input. */
uint32_t prev[AES_BLOCKSIZE / sizeof(uint32_t)];
EFM_ASSERT(!(len % AES_BLOCKSIZE));
/* A number of blocks to process. */
len /= AES_BLOCKSIZE;
if (encrypt) {
/* Enable encryption with auto start using XOR. */
AES->CTRL = AES_CTRL_AES256 | AES_CTRL_XORSTART;
/* Load an initialization vector. Since writing to DATA, it will */
/* not trigger encryption. */
for (i = 3; i >= 0; i--) {
AES->DATA = __REV_FROM_BYTEARRAY(&iv[i * 4]);
}
/* Encrypt data. */
while (len--) {
/* Load the key and data and trigger encryption. */
for (i = 3, j = 7; i >= 0; i--, j--) {
AES->KEYLA = __REV_FROM_BYTEARRAY(&key[j * 4]);
AES->KEYHA = __REV_FROM_BYTEARRAY(&key[i * 4]);
/* Write data last, since will trigger encryption on the last iteration. */
AES->XORDATA = __REV_FROM_BYTEARRAY(&in[i * 4]);
}
in += AES_BLOCKSIZE;
/* Wait for completion. */
while (AES->STATUS & AES_STATUS_RUNNING)
;
/* Save encrypted data. */
for (i = 3; i >= 0; i--) {
__REV_TO_BYTEARRAY(&out[i * 4], AES->DATA);
}
out += AES_BLOCKSIZE;
}
} else {
/* Select decryption mode. */
AES->CTRL = AES_CTRL_AES256 | AES_CTRL_DECRYPT | AES_CTRL_DATASTART;
/* Copy the initialization vector to the previous buffer to avoid special handling. */
memcpy(prev, iv, AES_BLOCKSIZE);
/* Decrypt data. */
while (len--) {
/* Load the key and data and trigger decryption. */
for (i = 3, j = 7; i >= 0; i--, j--) {
AES->KEYLA = __REV_FROM_BYTEARRAY(&key[j * 4]);
AES->KEYHA = __REV_FROM_BYTEARRAY(&key[i * 4]);
/* Write data last, since will trigger encryption on the last iteration. */
AES->DATA = __REV_FROM_BYTEARRAY(&in[i * 4]);
}
/* Wait for completion. */
while (AES->STATUS & AES_STATUS_RUNNING)
;
/* To avoid an additional buffer, hardware is used directly for XOR and buffer. */
for (i = 3; i >= 0; i--) {
AES->XORDATA = __REV(prev[i]);
}
memcpy(prev, in, AES_BLOCKSIZE);
in += AES_BLOCKSIZE;
/* Fetch decrypted data in a separate loop */
/* due to internal auto-shifting of words. */
for (i = 3; i >= 0; i--) {
__REV_TO_BYTEARRAY(&out[i * 4], AES->DATA);
}
out += AES_BLOCKSIZE;
}
}
}
#endif
/***************************************************************************//**
* @brief
* Cipher feedback (CFB) cipher mode encryption/decryption, 128 bit key.
*
* @details
* Encryption:
* @verbatim
* InitVector +----------------+
* | | |
* V | V
* +--------------+ | +--------------+
* Key ->| Block cipher | | Key ->| Block cipher |
* | encryption | | | encryption |
* +--------------+ | +--------------+
* | | |
* V | V
* Plaintext ->XOR | Plaintext ->XOR
* |---------+ |
* V V
* Ciphertext Ciphertext
* @endverbatim
* Decryption:
* @verbatim
* InitVector +----------------+
* | | |
* V | V
* +--------------+ | +--------------+
* Key ->| Block cipher | | Key ->| Block cipher |
* | encryption | | | encryption |
* +--------------+ | +--------------+
* | | |
* V | V
* XOR<- Ciphertext XOR<- Ciphertext
* | |
* V V
* Plaintext Plaintext
* @endverbatim
* See general comments on layout and byte ordering of parameters.
*
* @param[out] out
* A buffer to place encrypted/decrypted data. Must be at least @p len long. It
* may be set equal to @p in, in which case the input buffer is overwritten.
*
* @param[in] in
* A buffer holding data to encrypt/decrypt. Must be at least @p len long.
*
* @param[in] len
* A number of bytes to encrypt/decrypt. Must be a multiple of 16.
*
* @param[in] key
* 128 bit encryption key is used for both encryption and decryption modes.
*
* @param[in] iv
* 128 bit initialization vector to use.
*
* @param[in] encrypt
* Set to true to encrypt, false to decrypt.
******************************************************************************/
void AES_CFB128(uint8_t *out,
const uint8_t *in,
unsigned int len,
const uint8_t *key,
const uint8_t *iv,
bool encrypt)
{
int i;
const uint8_t *data;
uint8_t tmp[AES_BLOCKSIZE];
EFM_ASSERT(!(len % AES_BLOCKSIZE));
#if defined(AES_CTRL_KEYBUFEN)
AES->CTRL = AES_CTRL_KEYBUFEN | AES_CTRL_DATASTART;
#else
AES->CTRL = AES_CTRL_DATASTART;
#endif
#if defined(AES_CTRL_KEYBUFEN)
/* Load the key into high key for key buffer usage. */
for (i = 3; i >= 0; i--) {
AES->KEYHA = __REV_FROM_BYTEARRAY(&key[i * 4]);
}
#endif
/* Encrypt/decrypt data. */
data = iv;
len /= AES_BLOCKSIZE;
while (len--) {
#if !defined(AES_CTRL_KEYBUFEN)
/* Load the key. */
for (i = 3; i >= 0; i--) {
AES->KEYLA = __REV_FROM_BYTEARRAY(&key[i * 4]);
}
#endif
/* Load data and trigger encryption. */
for (i = 3; i >= 0; i--) {
AES->DATA = __REV_FROM_BYTEARRAY(&data[i * 4]);
}
/* Do some required processing before waiting for completion. */
if (encrypt) {
data = out;
} else {
/* Copy the current ciphertext block since it may be overwritten. */
memcpy(tmp, in, AES_BLOCKSIZE);
data = tmp;
}
/* Wait for completion. */
while (AES->STATUS & AES_STATUS_RUNNING)
;
/* Save encrypted/decrypted data. */
for (i = 3; i >= 0; i--) {
__UINT_TO_BYTEARRAY(&out[i * 4], __REV(AES->DATA) ^ __UINT_FROM_BYTEARRAY(&in[i * 4]));
}
out += AES_BLOCKSIZE;
in += AES_BLOCKSIZE;
}
}
#if defined(AES_CTRL_AES256)
/***************************************************************************//**
* @brief
* Cipher feedback (CFB) cipher mode encryption/decryption, 256 bit key.
*
* @details
* See AES_CFB128() for the CFB figure.
*
* See general comments on layout and byte ordering of parameters.
*
* @param[out] out
* A buffer to place encrypted/decrypted data. Must be at least @p len long. It
* may be set equal to @p in, in which case the input buffer is overwritten.
*
* @param[in] in
* A buffer holding data to encrypt/decrypt. Must be at least @p len long.
*
* @param[in] len
* A number of bytes to encrypt/decrypt. Must be a multiple of 16.
*
* @param[in] key
* 256 bit encryption key is used for both encryption and decryption modes.
*
* @param[in] iv
* 128 bit initialization vector to use.
*
* @param[in] encrypt
* Set to true to encrypt, false to decrypt.
******************************************************************************/
void AES_CFB256(uint8_t *out,
const uint8_t *in,
unsigned int len,
const uint8_t *key,
const uint8_t *iv,
bool encrypt)
{
int i;
int j;
const uint8_t *data;
uint8_t tmp[AES_BLOCKSIZE];
EFM_ASSERT(!(len % AES_BLOCKSIZE));
/* Select encryption mode. */
AES->CTRL = AES_CTRL_AES256 | AES_CTRL_DATASTART;
/* Encrypt/decrypt data. */
data = iv;
len /= AES_BLOCKSIZE;
while (len--) {
/* Load the key and block to be encrypted/decrypted. */
for (i = 3, j = 7; i >= 0; i--, j--) {
AES->KEYLA = __REV_FROM_BYTEARRAY(&key[j * 4]);
AES->KEYHA = __REV_FROM_BYTEARRAY(&key[i * 4]);
/* Write data last, since will trigger encryption on last iteration. */
AES->DATA = __REV_FROM_BYTEARRAY(&data[i * 4]);
}
/* Do some required processing before waiting for completion. */
if (encrypt) {
data = out;
} else {
/* Copy the current ciphertext block since it may be overwritten. */
memcpy(tmp, in, AES_BLOCKSIZE);
data = tmp;
}
while (AES->STATUS & AES_STATUS_RUNNING)
;
/* Save encrypted/decrypted data. */
for (i = 3; i >= 0; i--) {
__UINT_TO_BYTEARRAY(&out[i * 4], __REV(AES->DATA) ^ __UINT_FROM_BYTEARRAY(&in[i * 4]));
}
out += AES_BLOCKSIZE;
in += AES_BLOCKSIZE;
}
}
#endif
/***************************************************************************//**
* @brief
* Counter (CTR) cipher mode encryption/decryption, 128 bit key.
*
* @details
* Encryption:
* @verbatim
* Counter Counter
* | |
* V V
* +--------------+ +--------------+
* Key ->| Block cipher | Key ->| Block cipher |
* | encryption | | encryption |
* +--------------+ +--------------+
* | |
* Plaintext ->XOR Plaintext ->XOR
* | |
* V V
* Ciphertext Ciphertext
* @endverbatim
* Decryption:
* @verbatim
* Counter Counter
* | |
* V V
* +--------------+ +--------------+
* Key ->| Block cipher | Key ->| Block cipher |
* | encryption | | encryption |
* +--------------+ +--------------+
* | |
* Ciphertext ->XOR Ciphertext ->XOR
* | |
* V V
* Plaintext Plaintext
* @endverbatim
* See general comments on layout and byte ordering of parameters.
*
* @param[out] out
* A buffer to place encrypted/decrypted data. Must be at least @p len long. It
* may be set equal to @p in, in which case the input buffer is overwritten.
*
* @param[in] in
* A buffer holding data to encrypt/decrypt. Must be at least @p len long.
*
* @param[in] len
* A number of bytes to encrypt/decrypt. Must be a multiple of 16.
*
* @param[in] key
* 128 bit encryption key.
* On devices supporting key buffering this argument can be null. If so, the
* key will not be loaded, as it is assumed the key has been loaded
* into KEYHA previously.
*
* @param[in,out] ctr
* 128 bit initial counter value. The counter is updated after each AES
* block encoding through use of @p ctrFunc.
*
* @param[in] ctrFunc
* A function used to update the counter value.
******************************************************************************/
void AES_CTR128(uint8_t *out,
const uint8_t *in,
unsigned int len,
const uint8_t *key,
uint8_t *ctr,
AES_CtrFuncPtr_TypeDef ctrFunc)
{
int i;
EFM_ASSERT(!(len % AES_BLOCKSIZE));
EFM_ASSERT(ctrFunc);
#if defined(AES_CTRL_KEYBUFEN)
AES->CTRL = AES_CTRL_KEYBUFEN | AES_CTRL_DATASTART;
#else
AES->CTRL = AES_CTRL_DATASTART;
#endif
#if defined(AES_CTRL_KEYBUFEN)
if (key) {
/* Load the key into high key for key buffer usage. */
for (i = 3; i >= 0; i--) {
AES->KEYHA = __REV_FROM_BYTEARRAY(&key[i * 4]);
}
}
#endif
/* Encrypt/decrypt data. */
len /= AES_BLOCKSIZE;
while (len--) {
#if !defined(AES_CTRL_KEYBUFEN)
/* Load the key. */
for (i = 3; i >= 0; i--) {
AES->KEYLA = __REV_FROM_BYTEARRAY(&key[i * 4]);
}
#endif
/* Load ctr to be encrypted/decrypted. */
for (i = 3; i >= 0; i--) {
AES->DATA = __REV_FROM_BYTEARRAY(&ctr[i * 4]);
}
/* Increment ctr for the next use. */
ctrFunc(ctr);
/* Wait for completion. */
while (AES->STATUS & AES_STATUS_RUNNING)
;
/* Save encrypted/decrypted data. */
for (i = 3; i >= 0; i--) {
__UINT_TO_BYTEARRAY(&out[i * 4], __REV(AES->DATA) ^ __UINT_FROM_BYTEARRAY(&in[i * 4]));
}
out += AES_BLOCKSIZE;
in += AES_BLOCKSIZE;
}
}
#if defined(AES_CTRL_AES256)
/***************************************************************************//**
* @brief
* Counter (CTR) cipher mode encryption/decryption, 256 bit key.
*
* @details
* See AES_CTR128() for CTR figure.
*
* See general comments on layout and byte ordering of parameters.
*
* @param[out] out
* A buffer to place encrypted/decrypted data. Must be at least @p len long. It
* may be set equal to @p in, in which case the input buffer is overwritten.
*
* @param[in] in
* A buffer holding data to encrypt/decrypt. Must be at least @p len long.
*
* @param[in] len
* A number of bytes to encrypt/decrypt. Must be a multiple of 16.
*
* @param[in] key
* 256 bit encryption key.
*
* @param[in,out] ctr
* 128 bit initial counter value. The counter is updated after each AES
* block encoding through use of @p ctrFunc.
*
* @param[in] ctrFunc
* Function used to update counter value.
******************************************************************************/
void AES_CTR256(uint8_t *out,
const uint8_t *in,
unsigned int len,
const uint8_t *key,
uint8_t *ctr,
AES_CtrFuncPtr_TypeDef ctrFunc)
{
int i;
int j;
EFM_ASSERT(!(len % AES_BLOCKSIZE));
EFM_ASSERT(ctrFunc);
/* Select encryption mode with auto trigger. */
AES->CTRL = AES_CTRL_AES256 | AES_CTRL_DATASTART;
/* Encrypt/decrypt data. */
len /= AES_BLOCKSIZE;
while (len--) {
/* Load the key and block to be encrypted/decrypted. */
for (i = 3, j = 7; i >= 0; i--, j--) {
AES->KEYLA = __REV_FROM_BYTEARRAY(&key[j * 4]);
AES->KEYHA = __REV_FROM_BYTEARRAY(&key[i * 4]);
/* Write data last, since will trigger encryption on last iteration. */
AES->DATA = __REV_FROM_BYTEARRAY(&ctr[i * 4]);
}
/* Increment ctr for the next use. */
ctrFunc(ctr);
/* Wait for completion. */
while (AES->STATUS & AES_STATUS_RUNNING)
;
/* Save encrypted/decrypted data. */
for (i = 3; i >= 0; i--) {
__UINT_TO_BYTEARRAY(&out[i * 4], __REV(AES->DATA) ^ __UINT_FROM_BYTEARRAY(&in[i * 4]));
}
out += AES_BLOCKSIZE;
in += AES_BLOCKSIZE;
}
}
#endif
/***************************************************************************//**
* @brief
* Update last 32 bits of 128 bit counter by incrementing with 1.
*
* @details
* Notice that no special consideration is given to possible wrap around. If
* 32 least significant bits are 0xFFFFFFFF, they will be updated to 0x00000000,
* ignoring overflow.
*
* See general comments on layout and byte ordering of parameters.
*
* @param[in,out] ctr
* A buffer holding 128 bit counter to be updated.
******************************************************************************/
void AES_CTRUpdate32Bit(uint8_t *ctr)
{
__REV_TO_BYTEARRAY(&ctr[12], __REV_FROM_BYTEARRAY(&ctr[12]) + 1);
}
/***************************************************************************//**
* @brief
* Generate a 128 bit decryption key from the 128 bit encryption key. The decryption
* key is used for some cipher modes when decrypting.
*
* @details
* See general comments on layout and byte ordering of parameters.
*
* @param[out] out
* A buffer to place 128 bit decryption key. Must be at least 16 bytes long. It
* may be set equal to @p in, in which case the input buffer is overwritten.
*
* @param[in] in
* A buffer holding 128 bit encryption key. Must be at least 16 bytes long.
******************************************************************************/
void AES_DecryptKey128(uint8_t *out, const uint8_t *in)
{
int i;
/* Load key */
for (i = 3; i >= 0; i--) {
AES->KEYLA = __REV_FROM_BYTEARRAY(&in[i * 4]);
}
/* Do dummy encryption to generate decrypt key */
AES->CTRL = 0;
AES_IntClear(AES_IF_DONE);
AES->CMD = AES_CMD_START;
/* Wait for completion */
while (AES->STATUS & AES_STATUS_RUNNING)
;
/* Save decryption key */
for (i = 3; i >= 0; i--) {
__REV_TO_BYTEARRAY(&out[i * 4], AES->KEYLA);
}
}
#if defined(AES_CTRL_AES256)
/***************************************************************************//**
* @brief
* Generate a 256 bit decryption key from the 256 bit encryption key. The decryption
* key is used for some cipher modes when decrypting.
*
* @details
* See general comments on layout and byte ordering of parameters.
*
* @param[out] out
* A buffer to place 256 bit decryption key. Must be at least 32 bytes long. It
* may be set equal to @p in, in which case the input buffer is overwritten.
*
* @param[in] in
* A buffer holding 256 bit encryption key. Must be at least 32 bytes long.
******************************************************************************/
void AES_DecryptKey256(uint8_t *out, const uint8_t *in)
{
int i;
int j;
/* Load key */
for (i = 3, j = 7; i >= 0; i--, j--) {
AES->KEYLA = __REV_FROM_BYTEARRAY(&in[j * 4]);
AES->KEYHA = __REV_FROM_BYTEARRAY(&in[i * 4]);
}
/* Do dummy encryption to generate decrypt key */
AES->CTRL = AES_CTRL_AES256;
AES->CMD = AES_CMD_START;
/* Wait for completion */
while (AES->STATUS & AES_STATUS_RUNNING)
;
/* Save decryption key */
for (i = 3, j = 7; i >= 0; i--, j--) {
__REV_TO_BYTEARRAY(&out[j * 4], AES->KEYLA);
__REV_TO_BYTEARRAY(&out[i * 4], AES->KEYHA);
}
}
#endif
/***************************************************************************//**
* @brief
* Electronic Codebook (ECB) cipher mode encryption/decryption, 128 bit key.
*
* @details
* Encryption:
* @verbatim
* Plaintext Plaintext
* | |
* V V
* +--------------+ +--------------+
* Key ->| Block cipher | Key ->| Block cipher |
* | encryption | | encryption |
* +--------------+ +--------------+
* | |
* V V
* Ciphertext Ciphertext
* @endverbatim
* Decryption:
* @verbatim
* Ciphertext Ciphertext
* | |
* V V
* +--------------+ +--------------+
* Key ->| Block cipher | Key ->| Block cipher |
* | decryption | | decryption |
* +--------------+ +--------------+
* | |
* V V
* Plaintext Plaintext
* @endverbatim
* See general comments on layout and byte ordering of parameters.
*
* @param[out] out
* A buffer to place encrypted/decrypted data. Must be at least @p len long. It
* may be set equal to @p in, in which case the input buffer is overwritten.
*
* @param[in] in
* A buffer holding data to encrypt/decrypt. Must be at least @p len long.
*
* @param[in] len
* A number of bytes to encrypt/decrypt. Must be a multiple of 16.
*
* @param[in] key
* When encrypting, this is the 128 bit encryption key. When
* decrypting, this is the 128 bit decryption key. The decryption key may
* be generated from the encryption key with AES_DecryptKey128().
*
* @param[in] encrypt
* Set to true to encrypt, false to decrypt.
******************************************************************************/
void AES_ECB128(uint8_t *out,
const uint8_t *in,
unsigned int len,
const uint8_t *key,
bool encrypt)
{
int i;
EFM_ASSERT(!(len % AES_BLOCKSIZE));
#if defined(AES_CTRL_KEYBUFEN)
/* Load the key into high key for key buffer usage. */
for (i = 3; i >= 0; i--) {
AES->KEYHA = __REV_FROM_BYTEARRAY(&key[i * 4]);
}
#endif
if (encrypt) {
/* Select encryption mode. */
#if defined(AES_CTRL_KEYBUFEN)
AES->CTRL = AES_CTRL_KEYBUFEN | AES_CTRL_DATASTART;
#else
AES->CTRL = AES_CTRL_DATASTART;
#endif
} else {
/* Select decryption mode. */
#if defined(AES_CTRL_KEYBUFEN)
AES->CTRL = AES_CTRL_DECRYPT | AES_CTRL_KEYBUFEN | AES_CTRL_DATASTART;
#else
AES->CTRL = AES_CTRL_DECRYPT | AES_CTRL_DATASTART;
#endif
}
/* Encrypt/decrypt data. */
len /= AES_BLOCKSIZE;
while (len--) {
#if !defined(AES_CTRL_KEYBUFEN)
/* Load the key. */
for (i = 3; i >= 0; i--) {
AES->KEYLA = __REV_FROM_BYTEARRAY(&key[i * 4]);
}
#endif
/* Load a block to be encrypted/decrypted. */
for (i = 3; i >= 0; i--) {
AES->DATA = __REV_FROM_BYTEARRAY(&in[i * 4]);
}
in += AES_BLOCKSIZE;
/* Wait for completion. */
while (AES->STATUS & AES_STATUS_RUNNING)
;
/* Save encrypted/decrypted data. */
for (i = 3; i >= 0; i--) {
__REV_TO_BYTEARRAY(&out[i * 4], AES->DATA);
}
out += AES_BLOCKSIZE;
}
}
#if defined(AES_CTRL_AES256)
/***************************************************************************//**
* @brief
* Electronic Codebook (ECB) cipher mode encryption/decryption, 256 bit key.
*
* @details
* See AES_ECB128() for the ECB figure.
*
* See general comments on layout and byte ordering of parameters.
*
* @param[out] out
* A buffer to place encrypted/decrypted data. Must be at least @p len long. It
* may be set equal to @p in, in which case the input buffer is overwritten.
*
* @param[in] in
* A buffer holding data to encrypt/decrypt. Must be at least @p len long.
*
* @param[in] len
* A number of bytes to encrypt/decrypt. Must be a multiple of 16.
*
* @param[in] key
* When encrypting, this is the 256 bit encryption key. When
* decrypting, this is the 256 bit decryption key. The decryption key may
* be generated from the encryption key with AES_DecryptKey256().
*
* @param[in] encrypt
* Set to true to encrypt, false to decrypt.
******************************************************************************/
void AES_ECB256(uint8_t *out,
const uint8_t *in,
unsigned int len,
const uint8_t *key,
bool encrypt)
{
int i;
int j;
EFM_ASSERT(!(len % AES_BLOCKSIZE));
if (encrypt) {
/* Select encryption mode. */
AES->CTRL = AES_CTRL_AES256 | AES_CTRL_DATASTART;
} else {
/* Select decryption mode. */
AES->CTRL = AES_CTRL_DECRYPT | AES_CTRL_AES256 | AES_CTRL_DATASTART;
}
/* Encrypt/decrypt data. */
len /= AES_BLOCKSIZE;
while (len--) {
/* Load the key and block to be encrypted/decrypted. */
for (i = 3, j = 7; i >= 0; i--, j--) {
AES->KEYLA = __REV_FROM_BYTEARRAY(&key[j * 4]);
AES->KEYHA = __REV_FROM_BYTEARRAY(&key[i * 4]);
/* Write data last, since will trigger encryption on last iteration. */
AES->DATA = __REV_FROM_BYTEARRAY(&in[i * 4]);
}
in += AES_BLOCKSIZE;
/* Wait for completion. */
while (AES->STATUS & AES_STATUS_RUNNING)
;
/* Save encrypted/decrypted data. */
for (i = 3; i >= 0; i--) {
__REV_TO_BYTEARRAY(&out[i * 4], AES->DATA);
}
out += AES_BLOCKSIZE;
}
}
#endif
/***************************************************************************//**
* @brief
* Output feedback (OFB) cipher mode encryption/decryption, 128 bit key.
*
* @details
* Encryption:
* @verbatim
* InitVector +----------------+
* | | |
* V | V
* +--------------+ | +--------------+
* Key ->| Block cipher | | Key ->| Block cipher |
* | encryption | | | encryption |
* +--------------+ | +--------------+
* | | |
* |---------+ |
* V V
* Plaintext ->XOR Plaintext ->XOR
* | |
* V V
* Ciphertext Ciphertext
* @endverbatim
* Decryption:
* @verbatim
* InitVector +----------------+
* | | |
* V | V
* +--------------+ | +--------------+
* Key ->| Block cipher | | Key ->| Block cipher |
* | encryption | | | encryption |
* +--------------+ | +--------------+
* | | |
* |---------+ |
* V V
* Ciphertext ->XOR Ciphertext ->XOR
* | |
* V V
* Plaintext Plaintext
* @endverbatim
* See general comments on layout and byte ordering of parameters.
*
* @param[out] out
* A buffer to place encrypted/decrypted data. Must be at least @p len long. It
* may be set equal to @p in, in which case the input buffer is overwritten.
*
* @param[in] in
* A buffer holding data to encrypt/decrypt. Must be at least @p len long.
*
* @param[in] len
* A number of bytes to encrypt/decrypt. Must be a multiple of 16.
*
* @param[in] key
* 128 bit encryption key.
*
* @param[in] iv
* 128 bit initialization vector to use.
******************************************************************************/
void AES_OFB128(uint8_t *out,
const uint8_t *in,
unsigned int len,
const uint8_t *key,
const uint8_t *iv)
{
int i;
EFM_ASSERT(!(len % AES_BLOCKSIZE));
/* Select encryption mode, trigger explicitly by command. */
#if defined(AES_CTRL_KEYBUFEN)
AES->CTRL = AES_CTRL_KEYBUFEN;
#else
AES->CTRL = 0;
#endif
/* Load the key into high key for key buffer usage. */
/* Load the initialization vector. */
for (i = 3; i >= 0; i--) {
#if defined(AES_CTRL_KEYBUFEN)
AES->KEYHA = __REV_FROM_BYTEARRAY(&key[i * 4]);
#endif
AES->DATA = __REV_FROM_BYTEARRAY(&iv[i * 4]);
}
/* Encrypt/decrypt data. */
len /= AES_BLOCKSIZE;
while (len--) {
#if !defined(AES_CTRL_KEYBUFEN)
/* Load the key. */
for (i = 3; i >= 0; i--) {
AES->KEYLA = __REV_FROM_BYTEARRAY(&key[i * 4]);
}
#endif
AES->CMD = AES_CMD_START;
/* Wait for completion. */
while (AES->STATUS & AES_STATUS_RUNNING)
;
/* Save encrypted/decrypted data. */
for (i = 3; i >= 0; i--) {
__UINT_TO_BYTEARRAY(&out[i * 4], __REV(AES->DATA) ^ __UINT_FROM_BYTEARRAY(&in[i * 4]));
}
out += AES_BLOCKSIZE;
in += AES_BLOCKSIZE;
}
}
#if defined(AES_CTRL_AES256)
/***************************************************************************//**
* @brief
* Output feedback (OFB) cipher mode encryption/decryption, 256 bit key.
*
* @details
* See AES_OFB128() for OFB figure.
*
* See general comments on layout and byte ordering of parameters.
*
* @param[out] out
* A buffer to place encrypted/decrypted data. Must be at least @p len long. It
* may be set equal to @p in, in which case the input buffer is overwritten.
*
* @param[in] in
* A buffer holding data to encrypt/decrypt. Must be at least @p len long.
*
* @param[in] len
* A number of bytes to encrypt/decrypt. Must be a multiple of 16.
*
* @param[in] key
* 256 bit encryption key.
*
* @param[in] iv
* 128 bit initialization vector to use.
******************************************************************************/
void AES_OFB256(uint8_t *out,
const uint8_t *in,
unsigned int len,
const uint8_t *key,
const uint8_t *iv)
{
int i;
int j;
EFM_ASSERT(!(len % AES_BLOCKSIZE));
/* Select encryption mode, trigger explicitly by command. */
AES->CTRL = AES_CTRL_AES256;
/* Load the initialization vector. */
for (i = 3; i >= 0; i--) {
AES->DATA = __REV_FROM_BYTEARRAY(&iv[i * 4]);
}
/* Encrypt/decrypt data. */
len /= AES_BLOCKSIZE;
while (len--) {
/* Load the key. */
for (i = 3, j = 7; i >= 0; i--, j--) {
AES->KEYLA = __REV_FROM_BYTEARRAY(&key[j * 4]);
AES->KEYHA = __REV_FROM_BYTEARRAY(&key[i * 4]);
}
AES->CMD = AES_CMD_START;
/* Wait for completion. */
while (AES->STATUS & AES_STATUS_RUNNING)
;
/* Save encrypted/decrypted data. */
for (i = 3; i >= 0; i--) {
__UINT_TO_BYTEARRAY(&out[i * 4], __REV(AES->DATA) ^ __UINT_FROM_BYTEARRAY(&in[i * 4]));
}
out += AES_BLOCKSIZE;
in += AES_BLOCKSIZE;
}
}
#endif
/** @} (end addtogroup aes) */
#endif /* defined(AES_COUNT) && (AES_COUNT > 0) */