Merge branch 'feat/support_aes_block_and_dma_modes_during_runtime' into 'master'

Support AES block and DMA modes during runtime

Closes IDFGH-15251 and IDF-2594

See merge request espressif/esp-idf!40917
This commit is contained in:
Harshal Patil
2025-09-23 19:46:49 +05:30
24 changed files with 989 additions and 1495 deletions
+3 -11
View File
@@ -210,14 +210,6 @@ if(CONFIG_SOC_SHA_SUPPORTED)
endif()
endif()
if(CONFIG_SOC_AES_SUPPORTED)
if(CONFIG_SOC_AES_SUPPORT_DMA)
set(AES_PERIPHERAL_TYPE "dma")
else()
set(AES_PERIPHERAL_TYPE "block")
endif()
endif()
if(SHA_PERIPHERAL_TYPE STREQUAL "core")
target_include_directories(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/sha/core/include")
@@ -229,7 +221,7 @@ if(SHA_PERIPHERAL_TYPE STREQUAL "core")
target_sources(mbedcrypto PRIVATE "${SHA_CORE_SRCS}")
endif()
if(AES_PERIPHERAL_TYPE STREQUAL "dma")
if(CONFIG_SOC_AES_SUPPORT_DMA)
if(NOT CONFIG_SOC_AES_GDMA)
set(AES_DMA_SRCS "${COMPONENT_DIR}/port/aes/dma/esp_aes_crypto_dma_impl.c")
else()
@@ -242,7 +234,7 @@ if(AES_PERIPHERAL_TYPE STREQUAL "dma")
target_sources(mbedcrypto PRIVATE "${AES_DMA_SRCS}")
endif()
if((SHA_PERIPHERAL_TYPE STREQUAL "core" AND CONFIG_SOC_SHA_SUPPORT_DMA) OR AES_PERIPHERAL_TYPE STREQUAL "dma")
if((SHA_PERIPHERAL_TYPE STREQUAL "core" AND CONFIG_SOC_SHA_SUPPORT_DMA) OR CONFIG_SOC_AES_SUPPORT_DMA)
target_link_libraries(mbedcrypto PRIVATE idf::esp_mm)
if(CONFIG_SOC_SHA_GDMA OR CONFIG_SOC_AES_GDMA)
if(CONFIG_SOC_AXI_DMA_EXT_MEM_ENC_ALIGNMENT)
@@ -263,7 +255,7 @@ if(CONFIG_SOC_AES_SUPPORTED)
target_include_directories(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/aes/include")
target_sources(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/aes/esp_aes_xts.c"
"${COMPONENT_DIR}/port/aes/esp_aes_common.c"
"${COMPONENT_DIR}/port/aes/${AES_PERIPHERAL_TYPE}/esp_aes.c"
"${COMPONENT_DIR}/port/aes/esp_aes.c"
)
endif()
+51 -36
View File
@@ -1071,42 +1071,6 @@ menu "mbedTLS"
This reduces code size, but disables support for 192-bit and
256-bit AES keys.
config MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC
bool "Enable AES hardware's pseudo round function"
default n
depends on SOC_AES_SUPPORT_PSEUDO_ROUND_FUNCTION
help
Enables the pseudo round function of the AES peripheral.
Enabling this would impact the performance of the AES operations.
For more info regarding the performance impact, please checkout
the pseudo round function section of the security guide.
choice MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH
prompt "Strength of the pseudo rounds function"
depends on MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC
default MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH_LOW
help
The strength of the pseudo rounds functions can be configured to low, medium and high.
You can configure the strength of the pseudo rounds functions according to your use cases,
for example, increasing the strength would provide higher security but would slow down the
hardware AES encryption/decryption operations.
config MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH_LOW
bool "Low"
config MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH_MEDIUM
bool "Medium"
config MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH_HIGH
bool "High"
endchoice
config MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH
int
default 1 if MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH_LOW
default 2 if MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH_MEDIUM
default 3 if MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH_HIGH
config MBEDTLS_CMAC_C
bool "Enable CMAC mode for block ciphers"
default y
@@ -1647,6 +1611,57 @@ menu "mbedTLS"
priority level and any level from 1 to 3 can be selected (based on the availability).
Note: Higher value indicates high interrupt priority.
config MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC
bool "Enable AES hardware's pseudo round function"
default n
depends on SOC_AES_SUPPORT_PSEUDO_ROUND_FUNCTION && MBEDTLS_HARDWARE_AES
help
Enables the pseudo round function of the AES peripheral.
Enabling this would impact the performance of the AES operations.
For more info regarding the performance impact, please checkout
the pseudo round function section of the security guide.
choice MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH
prompt "Strength of the pseudo rounds function"
depends on MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC
default MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH_LOW
help
The strength of the pseudo rounds functions can be configured to low, medium and high.
You can configure the strength of the pseudo rounds functions according to your use cases,
for example, increasing the strength would provide higher security but would slow down the
hardware AES encryption/decryption operations.
config MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH_LOW
bool "Low"
config MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH_MEDIUM
bool "Medium"
config MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH_HIGH
bool "High"
endchoice
config MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH
int
default 1 if MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH_LOW
default 2 if MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH_MEDIUM
default 3 if MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH_HIGH
config MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
bool "Enable performance optimisation for the small data length hardware AES operations"
depends on MBEDTLS_HARDWARE_AES && SOC_AES_SUPPORT_DMA
default y
help
This option enables dynamically switching between the hardware
AES peripheral's block and DMA modes based on the length of the input data,
thus, significantly speeding up the AES operations with shorter data lengths.
For example, NVS encryption/decryption operations, TLS communication, etc.
with smaller data lengths.
It is enabled by default due to the significant performance impact but note that
it also increases the binary size by ~1.2 KB as it pulls in the peripheral's block
mode code as well.
config MBEDTLS_PK_RSA_ALT_SUPPORT
bool "Enable RSA alt support"
default y
@@ -48,7 +48,7 @@ target_include_directories(mbedcrypto PRIVATE ${crypto_port_inc_dirs})
target_sources(mbedcrypto PRIVATE "${COMPONENT_DIR}/esp_tee/esp_tee_crypto_shared_gdma.c")
# AES implementation
target_sources(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/aes/dma/esp_aes.c"
target_sources(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/aes/esp_aes.c"
"${COMPONENT_DIR}/port/aes/dma/esp_aes_dma_core.c")
target_sources(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/aes/esp_aes_common.c"
-579
View File
@@ -1,579 +0,0 @@
/**
* \brief AES block cipher, ESP block hardware accelerated version
* Based on mbedTLS FIPS-197 compliant version.
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* Additions Copyright (C) 2016-2017, Espressif Systems (Shanghai) PTE Ltd
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* 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.
*
*/
/*
* The AES block cipher was designed by Vincent Rijmen and Joan Daemen.
*
* http://csrc.nist.gov/encryption/aes/rijndael/Rijndael.pdf
* http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
*/
#include <string.h>
#include "mbedtls/aes.h"
#include "mbedtls/platform_util.h"
#include "esp_log.h"
#include "aes/esp_aes.h"
#include "soc/hwcrypto_periph.h"
#include "soc/soc_caps.h"
#include <sys/lock.h>
#include "hal/aes_hal.h"
#include "hal/aes_ll.h"
#include "esp_aes_internal.h"
#include <freertos/FreeRTOS.h>
#include <stdio.h>
#include "esp_private/esp_crypto_lock_internal.h"
static const char *TAG = "esp-aes";
/* AES uses a spinlock mux not a lock as the underlying block operation
only takes 208 cycles (to write key & compute block), +600 cycles
for DPORT protection but +3400 cycles again if you use a full sized lock.
For CBC, CFB, etc. this may mean that interrupts are disabled for a longer
period of time for bigger lengths. However at the moment this has to happen
anyway due to DPORT protection...
*/
static portMUX_TYPE aes_spinlock = portMUX_INITIALIZER_UNLOCKED;
void esp_aes_acquire_hardware( void )
{
portENTER_CRITICAL(&aes_spinlock);
/* Enable AES hardware */
AES_RCC_ATOMIC() {
aes_ll_enable_bus_clock(true);
aes_ll_reset_register();
}
}
void esp_aes_release_hardware( void )
{
/* Disable AES hardware */
AES_RCC_ATOMIC() {
aes_ll_enable_bus_clock(false);
}
portEXIT_CRITICAL(&aes_spinlock);
}
/* Run a single 16 byte block of AES, using the hardware engine.
*
* Call only while holding esp_aes_acquire_hardware().
*
* The function esp_aes_block zeroises the output buffer in the case of following conditions:
* 1. If key is not written in the hardware
* 2. If the fault injection check failed
*/
static int esp_aes_block(esp_aes_context *ctx, const void *input, void *output)
{
uint32_t i0, i1, i2, i3;
const uint32_t *input_words = (uint32_t *)input;
uint32_t *output_words = (uint32_t *)output;
/* If no key is written to hardware yet, either the user hasn't called
mbedtls_aes_setkey_enc/mbedtls_aes_setkey_dec - meaning we also don't
know which mode to use - or a fault skipped the
key write to hardware. Treat this as a fatal error and zero the output block.
*/
if (ctx->key_in_hardware != ctx->key_bytes) {
mbedtls_platform_zeroize(output, 16);
return MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH;
}
i0 = input_words[0];
i1 = input_words[1];
i2 = input_words[2];
i3 = input_words[3];
#ifdef CONFIG_MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC
esp_aes_enable_pseudo_rounds(CONFIG_MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH);
#endif /* CONFIG_MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC */
aes_hal_transform_block(input, output);
/* Physical security check: Verify the AES accelerator actually ran, and wasn't
skipped due to external fault injection while starting the peripheral.
Note that i0,i1,i2,i3 are copied from input buffer in case input==output.
Bypassing this check requires at least one additional fault.
*/
if (i0 == output_words[0] && i1 == output_words[1] && i2 == output_words[2] && i3 == output_words[3]) {
// calling zeroing functions to narrow the
// window for a double-fault of the abort step, here
memset(output, 0, 16);
mbedtls_platform_zeroize(output, 16);
abort();
}
return 0;
}
static int esp_aes_validate_input(esp_aes_context *ctx, const unsigned char *input,
const unsigned char *output )
{
if (!ctx) {
ESP_LOGD(TAG, "No AES context supplied");
return -1;
}
if (!input) {
ESP_LOGD(TAG, "No input supplied");
return -1;
}
if (!output) {
ESP_LOGD(TAG, "No output supplied");
return -1;
}
return 0;
}
/*
* AES-ECB block encryption
*/
int esp_internal_aes_encrypt(esp_aes_context *ctx,
const unsigned char input[16],
unsigned char output[16] )
{
int r = -1;
if (esp_aes_validate_input(ctx, input, output)) {
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!valid_key_length(ctx)) {
return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH;
}
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, ESP_AES_ENCRYPT);
r = esp_aes_block(ctx, input, output);
esp_aes_release_hardware();
return r;
}
/*
* AES-ECB block decryption
*/
int esp_internal_aes_decrypt(esp_aes_context *ctx,
const unsigned char input[16],
unsigned char output[16] )
{
int r = -1;
if (esp_aes_validate_input(ctx, input, output)) {
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!valid_key_length(ctx)) {
return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH;
}
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, ESP_AES_DECRYPT);
r = esp_aes_block(ctx, input, output);
esp_aes_release_hardware();
return r;
}
/*
* AES-ECB block encryption/decryption
*/
int esp_aes_crypt_ecb(esp_aes_context *ctx,
int mode,
const unsigned char input[16],
unsigned char output[16] )
{
int r = -1;
if (esp_aes_validate_input(ctx, input, output)) {
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!valid_key_length(ctx)) {
return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH;
}
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, mode);
r = esp_aes_block(ctx, input, output);
esp_aes_release_hardware();
return r;
}
/*
* AES-CBC buffer encryption/decryption
*/
int esp_aes_crypt_cbc(esp_aes_context *ctx,
int mode,
size_t length,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output )
{
int ret = -1;
if (esp_aes_validate_input(ctx, input, output)) {
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!iv) {
ESP_LOGD(TAG, "No IV supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
uint32_t *output_words = (uint32_t *)output;
const uint32_t *input_words = (const uint32_t *)input;
uint32_t *iv_words = (uint32_t *)iv;
unsigned char temp[16];
if ( length % 16 ) {
return ( ERR_ESP_AES_INVALID_INPUT_LENGTH );
}
if (!valid_key_length(ctx)) {
return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH;
}
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, mode);
if ( mode == ESP_AES_DECRYPT ) {
while ( length > 0 ) {
memcpy(temp, input_words, 16);
ret = esp_aes_block(ctx, input_words, output_words);
if (ret != 0) {
goto cleanup;
}
output_words[0] = output_words[0] ^ iv_words[0];
output_words[1] = output_words[1] ^ iv_words[1];
output_words[2] = output_words[2] ^ iv_words[2];
output_words[3] = output_words[3] ^ iv_words[3];
memcpy( iv_words, temp, 16 );
input_words += 4;
output_words += 4;
length -= 16;
}
} else { // ESP_AES_ENCRYPT
while ( length > 0 ) {
output_words[0] = input_words[0] ^ iv_words[0];
output_words[1] = input_words[1] ^ iv_words[1];
output_words[2] = input_words[2] ^ iv_words[2];
output_words[3] = input_words[3] ^ iv_words[3];
ret = esp_aes_block(ctx, output_words, output_words);
if (ret != 0) {
goto cleanup;
}
memcpy( iv_words, output_words, 16 );
input_words += 4;
output_words += 4;
length -= 16;
}
}
ret = 0;
cleanup:
esp_aes_release_hardware();
return ret;
}
/*
* AES-CFB128 buffer encryption/decryption
*/
int esp_aes_crypt_cfb128(esp_aes_context *ctx,
int mode,
size_t length,
size_t *iv_off,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output )
{
int ret = -1;
if (esp_aes_validate_input(ctx, input, output)) {
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!iv) {
ESP_LOGE(TAG, "No IV supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!iv_off) {
ESP_LOGE(TAG, "No IV offset supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!valid_key_length(ctx)) {
return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH;
}
int c;
size_t n = *iv_off;
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, ESP_AES_ENCRYPT);
if ( mode == ESP_AES_DECRYPT ) {
while ( length-- ) {
if ( n == 0 ) {
ret = esp_aes_block(ctx, iv, iv);
if (ret != 0) {
goto cleanup;
}
}
c = *input++;
*output++ = (unsigned char)( c ^ iv[n] );
iv[n] = (unsigned char) c;
n = ( n + 1 ) & 0x0F;
}
} else {
while ( length-- ) {
if ( n == 0 ) {
ret = esp_aes_block(ctx, iv, iv);
if (ret != 0) {
goto cleanup;
}
}
iv[n] = *output++ = (unsigned char)( iv[n] ^ *input++ );
n = ( n + 1 ) & 0x0F;
}
}
*iv_off = n;
ret = 0;
cleanup:
esp_aes_release_hardware();
return ret;
}
/*
* AES-CFB8 buffer encryption/decryption
*/
int esp_aes_crypt_cfb8(esp_aes_context *ctx,
int mode,
size_t length,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output )
{
int ret = -1;
unsigned char c;
unsigned char ov[17];
if (esp_aes_validate_input(ctx, input, output)) {
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!iv) {
ESP_LOGE(TAG, "No IV supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!valid_key_length(ctx)) {
return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH;
}
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, ESP_AES_ENCRYPT);
while ( length-- ) {
memcpy( ov, iv, 16 );
ret = esp_aes_block(ctx, iv, iv);
if (ret != 0) {
goto cleanup;
}
if ( mode == ESP_AES_DECRYPT ) {
ov[16] = *input;
}
c = *output++ = (unsigned char)( iv[0] ^ *input++ );
if ( mode == ESP_AES_ENCRYPT ) {
ov[16] = c;
}
memcpy( iv, ov + 1, 16 );
}
ret = 0;
cleanup:
esp_aes_release_hardware();
return ret;
}
/*
* AES-CTR buffer encryption/decryption
*/
int esp_aes_crypt_ctr(esp_aes_context *ctx,
size_t length,
size_t *nc_off,
unsigned char nonce_counter[16],
unsigned char stream_block[16],
const unsigned char *input,
unsigned char *output )
{
int c, i, ret = -1;
if (esp_aes_validate_input(ctx, input, output)) {
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!stream_block) {
ESP_LOGE(TAG, "No stream supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!nonce_counter) {
ESP_LOGE(TAG, "No nonce supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!nc_off) {
ESP_LOGE(TAG, "No nonce offset supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
size_t n = *nc_off;
if (!valid_key_length(ctx)) {
return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH;
}
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, ESP_AES_ENCRYPT);
while ( length-- ) {
if ( n == 0 ) {
ret = esp_aes_block(ctx, nonce_counter, stream_block);
if (ret != 0) {
goto cleanup;
}
for ( i = 16; i > 0; i-- ) {
if ( ++nonce_counter[i - 1] != 0 ) {
break;
}
}
}
c = *input++;
*output++ = (unsigned char)( c ^ stream_block[n] );
n = ( n + 1 ) & 0x0F;
}
*nc_off = n;
ret = 0;
cleanup:
esp_aes_release_hardware();
return ret;
}
/*
* AES-OFB (Output Feedback Mode) buffer encryption/decryption
*/
int esp_aes_crypt_ofb(esp_aes_context *ctx,
size_t length,
size_t *iv_off,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output )
{
int ret = -1;
size_t n;
if (esp_aes_validate_input(ctx, input, output)) {
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!iv) {
ESP_LOGE(TAG, "No IV supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!iv_off) {
ESP_LOGE(TAG, "No IV offset supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
n = *iv_off;
if (n > 15) {
return (MBEDTLS_ERR_AES_BAD_INPUT_DATA);
}
if (!valid_key_length(ctx)) {
return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH;
}
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, ESP_AES_ENCRYPT);
while (length--) {
if ( n == 0 ) {
ret = esp_aes_block(ctx, iv, iv);
if (ret != 0) {
goto cleanup;
}
}
*output++ = *input++ ^ iv[n];
n = ( n + 1 ) & 0x0F;
}
*iv_off = n;
ret = 0;
cleanup:
esp_aes_release_hardware();
return ( ret );
}
-521
View File
@@ -1,521 +0,0 @@
/**
* \brief AES block cipher, ESP DMA hardware accelerated version
* Based on mbedTLS FIPS-197 compliant version.
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* Additions Copyright (C) 2016-2020, Espressif Systems (Shanghai) PTE Ltd
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* 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.
*
*/
/*
* The AES block cipher was designed by Vincent Rijmen and Joan Daemen.
*
* http://csrc.nist.gov/encryption/aes/rijndael/Rijndael.pdf
* http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
*/
#include <string.h>
#include "mbedtls/aes.h"
#include "esp_log.h"
#include "esp_crypto_lock.h"
#include "hal/aes_hal.h"
#include "esp_aes_internal.h"
#include "esp_crypto_periph_clk.h"
#if SOC_AES_GDMA
#define AES_LOCK() esp_crypto_sha_aes_lock_acquire()
#define AES_RELEASE() esp_crypto_sha_aes_lock_release()
#elif SOC_AES_CRYPTO_DMA
#define AES_LOCK() esp_crypto_dma_lock_acquire()
#define AES_RELEASE() esp_crypto_dma_lock_release()
#include "hal/crypto_dma_ll.h"
#endif
static const char *TAG = "esp-aes";
void esp_aes_acquire_hardware( void )
{
/* Released by esp_aes_release_hardware()*/
AES_LOCK();
esp_crypto_aes_enable_periph_clk(true);
}
/* Function to disable AES and Crypto DMA clocks and release locks */
void esp_aes_release_hardware( void )
{
esp_crypto_aes_enable_periph_clk(false);
AES_RELEASE();
}
static int esp_aes_validate_input(esp_aes_context *ctx, const unsigned char *input,
unsigned char *output )
{
if (!ctx) {
ESP_LOGE(TAG, "No AES context supplied");
return -1;
}
if (!input) {
ESP_LOGE(TAG, "No input supplied");
return -1;
}
if (!output) {
ESP_LOGE(TAG, "No output supplied");
return -1;
}
return 0;
}
/*
* AES-ECB single block encryption
*/
int esp_internal_aes_encrypt(esp_aes_context *ctx,
const unsigned char input[16],
unsigned char output[16] )
{
int r = -1;
if (esp_aes_validate_input(ctx, input, output)) {
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!valid_key_length(ctx)) {
return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH;
}
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, ESP_AES_ENCRYPT);
aes_hal_mode_init(ESP_AES_BLOCK_MODE_ECB);
r = esp_aes_process_dma(ctx, input, output, AES_BLOCK_BYTES, NULL);
esp_aes_release_hardware();
return r;
}
/*
* AES-ECB single block decryption
*/
int esp_internal_aes_decrypt(esp_aes_context *ctx,
const unsigned char input[16],
unsigned char output[16] )
{
int r = -1;
if (esp_aes_validate_input(ctx, input, output)) {
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!valid_key_length(ctx)) {
return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH;
}
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, ESP_AES_DECRYPT);
aes_hal_mode_init(ESP_AES_BLOCK_MODE_ECB);
r = esp_aes_process_dma(ctx, input, output, AES_BLOCK_BYTES, NULL);
esp_aes_release_hardware();
return r;
}
/*
* AES-ECB block encryption/decryption
*/
int esp_aes_crypt_ecb(esp_aes_context *ctx,
int mode,
const unsigned char input[16],
unsigned char output[16] )
{
int r = -1;
if (esp_aes_validate_input(ctx, input, output)) {
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!valid_key_length(ctx)) {
return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH;
}
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, mode);
aes_hal_mode_init(ESP_AES_BLOCK_MODE_ECB);
r = esp_aes_process_dma(ctx, input, output, AES_BLOCK_BYTES, NULL);
esp_aes_release_hardware();
return r;
}
/*
* AES-CBC buffer encryption/decryption
*/
int esp_aes_crypt_cbc(esp_aes_context *ctx,
int mode,
size_t length,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output )
{
int r = -1;
if (esp_aes_validate_input(ctx, input, output)) {
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!iv) {
ESP_LOGE(TAG, "No IV supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
/* For CBC input length should be multiple of
* AES BLOCK BYTES
* */
if ( (length % AES_BLOCK_BYTES) || (length == 0) ) {
return ERR_ESP_AES_INVALID_INPUT_LENGTH;
}
if (!valid_key_length(ctx)) {
return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH;
}
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, mode);
aes_hal_mode_init(ESP_AES_BLOCK_MODE_CBC);
aes_hal_set_iv(iv);
r = esp_aes_process_dma(ctx, input, output, length, NULL);
if (r != 0) {
goto cleanup;
}
aes_hal_read_iv(iv);
cleanup:
esp_aes_release_hardware();
return r;
}
/*
* AES-CFB8 buffer encryption/decryption
*/
int esp_aes_crypt_cfb8(esp_aes_context *ctx,
int mode,
size_t length,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output )
{
int r = -1;
unsigned char c;
unsigned char ov[17];
size_t block_bytes = length - (length % AES_BLOCK_BYTES);
if (esp_aes_validate_input(ctx, input, output)) {
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!iv) {
ESP_LOGE(TAG, "No IV supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!valid_key_length(ctx)) {
return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH;
}
/* The DMA engine will only output correct IV if it runs
full blocks of input in CFB8 mode
*/
esp_aes_acquire_hardware();
if (block_bytes > 0) {
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, mode);
aes_hal_mode_init(ESP_AES_BLOCK_MODE_CFB8);
aes_hal_set_iv(iv);
r = esp_aes_process_dma(ctx, input, output, block_bytes, NULL);
if (r != 0) {
goto cleanup;
}
aes_hal_read_iv(iv);
length -= block_bytes;
input += block_bytes;
output += block_bytes;
}
// Process remaining bytes block-at-a-time in ECB mode
if (length > 0) {
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, MBEDTLS_AES_ENCRYPT);
aes_hal_mode_init(ESP_AES_BLOCK_MODE_ECB);
while ( length-- ) {
memcpy( ov, iv, 16 );
r = esp_aes_process_dma(ctx, iv, iv, AES_BLOCK_BYTES, NULL);
if (r != 0) {
goto cleanup;
}
if ( mode == MBEDTLS_AES_DECRYPT ) {
ov[16] = *input;
}
c = *output++ = ( iv[0] ^ *input++ );
if ( mode == MBEDTLS_AES_ENCRYPT ) {
ov[16] = c;
}
memcpy( iv, ov + 1, 16 );
}
}
r = 0;
cleanup:
esp_aes_release_hardware();
return r;
}
/*
* AES-CFB128 buffer encryption/decryption
*/
int esp_aes_crypt_cfb128(esp_aes_context *ctx,
int mode,
size_t length,
size_t *iv_off,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output )
{
uint8_t c;
size_t stream_bytes = 0;
size_t n;
if (esp_aes_validate_input(ctx, input, output)) {
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!iv) {
ESP_LOGE(TAG, "No IV supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!iv_off) {
ESP_LOGE(TAG, "No IV offset supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!valid_key_length(ctx)) {
return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH;
}
n = *iv_off;
/* First process the *iv_off bytes
* which are pending from the previous call to this API
*/
while (n > 0 && length > 0) {
if (mode == MBEDTLS_AES_ENCRYPT) {
iv[n] = *output++ = *input++ ^ iv[n];
} else {
c = *input++;
*output++ = c ^ iv[n];
iv[n] = c;
}
n = (n + 1) % AES_BLOCK_BYTES;
length--;
}
if (length > 0) {
stream_bytes = length % AES_BLOCK_BYTES;
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, mode);
aes_hal_mode_init(ESP_AES_BLOCK_MODE_CFB128);
aes_hal_set_iv(iv);
int r = esp_aes_process_dma(ctx, input, output, length, iv);
if (r != 0) {
esp_aes_release_hardware();
return r;
}
if (stream_bytes == 0) {
// if we didn't need the partial 'stream block' then the new IV is in the IV register
aes_hal_read_iv(iv);
} else {
// if we did process a final partial block the new IV is already processed via DMA (and has some bytes of output in it),
// In decrypt mode any partial bytes are output plaintext (iv ^ c) and need to be swapped back to ciphertext (as the next
// block uses ciphertext as its IV input)
//
// Note: It may be more efficient to not process the partial block via DMA in this case.
if (mode == MBEDTLS_AES_DECRYPT) {
memcpy(iv, input + length - stream_bytes, stream_bytes);
}
}
esp_aes_release_hardware();
}
*iv_off = n + stream_bytes;
return 0;
}
/*
* AES-OFB (Output Feedback Mode) buffer encryption/decryption
*/
int esp_aes_crypt_ofb(esp_aes_context *ctx,
size_t length,
size_t *iv_off,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output )
{
size_t n;
size_t stream_bytes = 0;
if (esp_aes_validate_input(ctx, input, output)) {
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!iv) {
ESP_LOGE(TAG, "No IV supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!iv_off) {
ESP_LOGE(TAG, "No IV offset supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
n = *iv_off;
/* If there is an offset then use the output of the previous AES block
(the updated IV) to calculate the new output */
while (n > 0 && length > 0) {
*output++ = (*input++ ^ iv[n]);
n = (n + 1) & 0xF;
length--;
}
if (length > 0) {
stream_bytes = (length % AES_BLOCK_BYTES);
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, ESP_AES_DECRYPT);
aes_hal_mode_init(ESP_AES_BLOCK_MODE_OFB);
aes_hal_set_iv(iv);
int r = esp_aes_process_dma(ctx, input, output, length, iv);
if (r != 0) {
esp_aes_release_hardware();
return r;
}
aes_hal_read_iv(iv);
esp_aes_release_hardware();
}
*iv_off = n + stream_bytes;
return 0;
}
#ifdef CONFIG_MBEDTLS_CIPHER_MODE_CTR
/*
* AES-CTR buffer encryption/decryption
*/
int esp_aes_crypt_ctr(esp_aes_context *ctx,
size_t length,
size_t *nc_off,
unsigned char nonce_counter[16],
unsigned char stream_block[16],
const unsigned char *input,
unsigned char *output )
{
size_t n;
if (esp_aes_validate_input(ctx, input, output)) {
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!stream_block) {
ESP_LOGE(TAG, "No stream supplied");
return -1;
}
if (!nonce_counter) {
ESP_LOGE(TAG, "No nonce supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!nc_off) {
ESP_LOGE(TAG, "No nonce offset supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
n = *nc_off;
if (!valid_key_length(ctx)) {
return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH;
}
/* Process any unprocessed bytes left in stream block from
last operation */
while (n > 0 && length > 0) {
*output++ = (unsigned char)(*input++ ^ stream_block[n]);
n = (n + 1) & 0xF;
length--;
}
if (length > 0) {
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, ESP_AES_DECRYPT);
aes_hal_mode_init(ESP_AES_BLOCK_MODE_CTR);
aes_hal_set_iv(nonce_counter);
int r = esp_aes_process_dma(ctx, input, output, length, stream_block);
if (r != 0) {
esp_aes_release_hardware();
return r;
}
aes_hal_read_iv(nonce_counter);
esp_aes_release_hardware();
}
*nc_off = n + (length % AES_BLOCK_BYTES);
return 0;
}
#endif /* CONFIG_MBEDTLS_CIPHER_MODE_CTR */
@@ -15,6 +15,7 @@
extern "C" {
#endif
#if SOC_AES_SUPPORT_DMA
/**
* @brief Start the DMA engine
*
@@ -41,6 +42,7 @@ bool esp_aes_dma_done(const crypto_dma_desc_t *output);
*/
void esp_aes_intr_alloc(void);
#endif /* SOC_AES_SUPPORT_DMA */
#ifdef __cplusplus
}
#endif
+749
View File
@@ -0,0 +1,749 @@
/*
* \brief AES block and DMA hardware accelerated version
* Based on mbedTLS FIPS-197 compliant version.
*
* SPDX-FileCopyrightText: The Mbed TLS Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD
*/
/*
* The AES block cipher was designed by Vincent Rijmen and Joan Daemen.
*
* http://csrc.nist.gov/encryption/aes/rijndael/Rijndael.pdf
* http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
*/
#include <string.h>
#include "mbedtls/aes.h"
#include "esp_log.h"
#include "esp_crypto_lock.h"
#include "hal/aes_hal.h"
#include "esp_aes_internal.h"
#include "esp_crypto_periph_clk.h"
#include "soc/soc_caps.h"
#include "sdkconfig.h"
#if SOC_AES_GDMA
#define AES_LOCK() esp_crypto_sha_aes_lock_acquire()
#define AES_RELEASE() esp_crypto_sha_aes_lock_release()
#elif SOC_AES_CRYPTO_DMA
#define AES_LOCK() esp_crypto_dma_lock_acquire()
#define AES_RELEASE() esp_crypto_dma_lock_release()
#include "hal/crypto_dma_ll.h"
#endif
static const char *TAG = "esp-aes";
#if CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
#if CONFIG_IDF_TARGET_ESP32P4
#define AES_DMA_MODE_THRESHOLD 512
#else
#define AES_DMA_MODE_THRESHOLD 128
#endif
#endif /* CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM */
#if !SOC_AES_SUPPORT_DMA
#include <freertos/FreeRTOS.h>
/* AES uses a spinlock mux not a lock as the underlying block operation
only takes 208 cycles (to write key & compute block), +600 cycles
for DPORT protection but +3400 cycles again if you use a full sized lock.
For CBC, CFB, etc. this may mean that interrupts are disabled for a longer
period of time for bigger lengths. However at the moment this has to happen
anyway due to DPORT protection...
*/
static portMUX_TYPE aes_spinlock = portMUX_INITIALIZER_UNLOCKED;
#endif
void esp_aes_acquire_hardware( void )
{
/* Released by esp_aes_release_hardware()*/
#if SOC_AES_SUPPORT_DMA
AES_LOCK();
#else
portENTER_CRITICAL(&aes_spinlock);
#endif
esp_crypto_aes_enable_periph_clk(true);
}
/* Function to disable AES and Crypto DMA clocks and release locks */
void esp_aes_release_hardware( void )
{
esp_crypto_aes_enable_periph_clk(false);
#if SOC_AES_SUPPORT_DMA
AES_RELEASE();
#else
portEXIT_CRITICAL(&aes_spinlock);
#endif
}
static int esp_aes_validate_input(esp_aes_context *ctx, const unsigned char *input,
unsigned char *output, unsigned char *iv, bool validate_iv)
{
if (!ctx) {
ESP_LOGE(TAG, "No AES context supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!input) {
ESP_LOGE(TAG, "No input supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!output) {
ESP_LOGE(TAG, "No output supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!valid_key_length(ctx)) {
return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH;
}
if (validate_iv && !iv) {
ESP_LOGE(TAG, "No IV supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
return 0;
}
#if !SOC_AES_SUPPORT_DMA || CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
/* Run a single 16 byte block of AES, using the hardware engine.
*
* Call only while holding esp_aes_acquire_hardware().
*
* The function esp_aes_block zeroises the output buffer in the case of following conditions:
* 1. If key is not written in the hardware
* 2. If the fault injection check failed
*/
static int esp_aes_block(esp_aes_context *ctx, const void *input, void *output)
{
uint32_t i0, i1, i2, i3;
const uint32_t *input_words = (uint32_t *)input;
uint32_t *output_words = (uint32_t *)output;
/* If no key is written to hardware yet, either the user hasn't called
mbedtls_aes_setkey_enc/mbedtls_aes_setkey_dec - meaning we also don't
know which mode to use - or a fault skipped the
key write to hardware. Treat this as a fatal error and zero the output block.
*/
if (ctx->key_in_hardware != ctx->key_bytes) {
mbedtls_platform_zeroize(output, 16);
return MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH;
}
i0 = input_words[0];
i1 = input_words[1];
i2 = input_words[2];
i3 = input_words[3];
#ifdef CONFIG_MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC
esp_aes_enable_pseudo_rounds(CONFIG_MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC_STRENGTH);
#endif /* CONFIG_MBEDTLS_AES_USE_PSEUDO_ROUND_FUNC */
aes_hal_transform_block(input, output);
/* Physical security check: Verify the AES accelerator actually ran, and wasn't
skipped due to external fault injection while starting the peripheral.
Note that i0,i1,i2,i3 are copied from input buffer in case input==output.
Bypassing this check requires at least one additional fault.
*/
if (i0 == output_words[0] && i1 == output_words[1] && i2 == output_words[2] && i3 == output_words[3]) {
// calling zeroing functions to narrow the
// window for a double-fault of the abort step, here
memset(output, 0, 16);
mbedtls_platform_zeroize(output, 16);
abort();
}
return 0;
}
#endif /* !SOC_AES_SUPPORT_DMA || CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM */
/*
* AES-ECB single block encryption
*/
int esp_internal_aes_encrypt(esp_aes_context *ctx,
const unsigned char input[16],
unsigned char output[16] )
{
return esp_aes_crypt_ecb(ctx, ESP_AES_ENCRYPT, input, output);
}
/*
* AES-ECB single block decryption
*/
int esp_internal_aes_decrypt(esp_aes_context *ctx,
const unsigned char input[16],
unsigned char output[16] )
{
return esp_aes_crypt_ecb(ctx, ESP_AES_DECRYPT, input, output);
}
/*
* AES-ECB block encryption/decryption
*/
int esp_aes_crypt_ecb(esp_aes_context *ctx,
int mode,
const unsigned char input[16],
unsigned char output[16] )
{
// Use helper for validation
int ret = esp_aes_validate_input(ctx, input, output, NULL, false);
if (ret != 0) {
return ret;
}
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, mode);
#if SOC_AES_SUPPORT_DMA && !CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
aes_hal_mode_init(ESP_AES_BLOCK_MODE_ECB);
ret = esp_aes_process_dma(ctx, input, output, AES_BLOCK_BYTES, NULL);
#else
ret = esp_aes_block(ctx, input, output);
#endif
esp_aes_release_hardware();
return ret;
}
/*
* AES-CBC buffer encryption/decryption
*/
int esp_aes_crypt_cbc(esp_aes_context *ctx,
int mode,
size_t length,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output )
{
// Use helpers for validation
int ret = esp_aes_validate_input(ctx, input, output, iv, true);
if (ret != 0) {
return ret;
}
/* For CBC input length should be multiple of AES BLOCK BYTES */
if ( (length % AES_BLOCK_BYTES) || (length == 0) ) {
return ERR_ESP_AES_INVALID_INPUT_LENGTH;
}
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
#if SOC_AES_SUPPORT_DMA
#if CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
if (length > AES_DMA_MODE_THRESHOLD) {
#endif /* CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM */
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, mode);
aes_hal_mode_init(ESP_AES_BLOCK_MODE_CBC);
aes_hal_set_iv(iv);
ret = esp_aes_process_dma(ctx, input, output, length, NULL);
if (ret != 0) {
goto cleanup;
}
aes_hal_read_iv(iv);
#if CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
} else
#endif /* CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM */
#endif /* SOC_AES_SUPPORT_DMA */
#if !SOC_AES_SUPPORT_DMA || CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
{
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, mode);
uint32_t *output_words = (uint32_t *)output;
const uint32_t *input_words = (const uint32_t *)input;
uint32_t *iv_words = (uint32_t *)iv;
unsigned char temp[16];
if (mode == ESP_AES_DECRYPT) {
while (length > 0) {
memcpy(temp, input_words, 16);
ret = esp_aes_block(ctx, input_words, output_words);
if (ret != 0) {
goto cleanup;
}
output_words[0] = output_words[0] ^ iv_words[0];
output_words[1] = output_words[1] ^ iv_words[1];
output_words[2] = output_words[2] ^ iv_words[2];
output_words[3] = output_words[3] ^ iv_words[3];
memcpy( iv_words, temp, 16 );
input_words += 4;
output_words += 4;
length -= 16;
}
} else { // ESP_AES_ENCRYPT
while (length > 0) {
output_words[0] = input_words[0] ^ iv_words[0];
output_words[1] = input_words[1] ^ iv_words[1];
output_words[2] = input_words[2] ^ iv_words[2];
output_words[3] = input_words[3] ^ iv_words[3];
ret = esp_aes_block(ctx, output_words, output_words);
if (ret != 0) {
goto cleanup;
}
memcpy(iv_words, output_words, 16);
input_words += 4;
output_words += 4;
length -= 16;
}
}
ret = 0;
}
#endif /* !SOC_AES_SUPPORT_DMA || CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM */
cleanup:
esp_aes_release_hardware();
return ret;
}
/*
* AES-CFB8 buffer encryption/decryption
*/
int esp_aes_crypt_cfb8(esp_aes_context *ctx,
int mode,
size_t length,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output )
{
unsigned char c;
unsigned char ov[17];
// Use helpers for validation
int ret = esp_aes_validate_input(ctx, input, output, iv, true);
if (ret != 0) {
return ret;
}
esp_aes_acquire_hardware();
#if SOC_AES_SUPPORT_DMA
#if CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
if (length > AES_DMA_MODE_THRESHOLD) {
#endif /* CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM */
size_t block_bytes = length - (length % AES_BLOCK_BYTES);
/* The DMA engine will only output correct IV if it runs
full blocks of input in CFB8 mode
*/
if (block_bytes > 0) {
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, mode);
aes_hal_mode_init(ESP_AES_BLOCK_MODE_CFB8);
aes_hal_set_iv(iv);
ret = esp_aes_process_dma(ctx, input, output, block_bytes, NULL);
if (ret != 0) {
goto cleanup;
}
aes_hal_read_iv(iv);
length -= block_bytes;
input += block_bytes;
output += block_bytes;
}
// Process remaining bytes block-at-a-time in ECB mode
if (length > 0) {
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, ESP_AES_ENCRYPT);
aes_hal_mode_init(ESP_AES_BLOCK_MODE_ECB);
while ( length-- ) {
memcpy( ov, iv, 16 );
ret = esp_aes_process_dma(ctx, iv, iv, AES_BLOCK_BYTES, NULL);
if (ret != 0) {
goto cleanup;
}
if ( mode == ESP_AES_DECRYPT ) {
ov[16] = *input;
}
c = *output++ = ( iv[0] ^ *input++ );
if ( mode == ESP_AES_ENCRYPT ) {
ov[16] = c;
}
memcpy( iv, ov + 1, 16 );
}
}
ret = 0;
#if CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
} else
#endif /* CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM */
#endif /* SOC_AES_SUPPORT_DMA */
#if !SOC_AES_SUPPORT_DMA || CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
{
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, ESP_AES_ENCRYPT);
while (length--) {
memcpy( ov, iv, 16 );
ret = esp_aes_block(ctx, iv, iv);
if (ret != 0) {
goto cleanup;
}
if (mode == ESP_AES_DECRYPT) {
ov[16] = *input;
}
c = *output++ = (unsigned char)(iv[0] ^ *input++);
if (mode == ESP_AES_ENCRYPT) {
ov[16] = c;
}
memcpy( iv, ov + 1, 16 );
}
ret = 0;
}
#endif /* !SOC_AES_SUPPORT_DMA || CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM */
cleanup:
esp_aes_release_hardware();
return ret;
}
/*
* AES-CFB128 buffer encryption/decryption
*/
int esp_aes_crypt_cfb128(esp_aes_context *ctx,
int mode,
size_t length,
size_t *iv_off,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output )
{
uint8_t c;
size_t n;
// Use helpers for validation
int ret = esp_aes_validate_input(ctx, input, output, iv, true);
if (ret != 0) {
return ret;
}
if (!iv_off) {
ESP_LOGE(TAG, "No IV offset supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
n = *iv_off;
#if SOC_AES_SUPPORT_DMA
#if CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
if (length > AES_DMA_MODE_THRESHOLD) {
#endif /* CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM */
size_t stream_bytes = 0;
/* First process the *iv_off bytes
* which are pending from the previous call to this API
*/
while (n > 0 && length > 0) {
if (mode == ESP_AES_ENCRYPT) {
iv[n] = *output++ = *input++ ^ iv[n];
} else {
c = *input++;
*output++ = c ^ iv[n];
iv[n] = c;
}
n = (n + 1) % AES_BLOCK_BYTES;
length--;
}
if (length > 0) {
stream_bytes = length % AES_BLOCK_BYTES;
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, mode);
aes_hal_mode_init(ESP_AES_BLOCK_MODE_CFB128);
aes_hal_set_iv(iv);
ret = esp_aes_process_dma(ctx, input, output, length, iv);
if (ret != 0) {
esp_aes_release_hardware();
return ret;
}
if (stream_bytes == 0) {
// if we didn't need the partial 'stream block' then the new IV is in the IV register
aes_hal_read_iv(iv);
} else {
// if we did process a final partial block the new IV is already processed via DMA (and has some bytes of output in it),
// In decrypt mode any partial bytes are output plaintext (iv ^ c) and need to be swapped back to ciphertext (as the next
// block uses ciphertext as its IV input)
//
// Note: It may be more efficient to not process the partial block via DMA in this case.
if (mode == ESP_AES_DECRYPT) {
memcpy(iv, input + length - stream_bytes, stream_bytes);
}
}
esp_aes_release_hardware();
}
*iv_off = n + stream_bytes;
#if CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
} else
#endif /* CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM */
#endif /* SOC_AES_SUPPORT_DMA */
#if !SOC_AES_SUPPORT_DMA || CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
{
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, ESP_AES_ENCRYPT);
if (mode == ESP_AES_DECRYPT) {
while (length--) {
if (n == 0) {
ret = esp_aes_block(ctx, iv, iv);
if (ret != 0) {
esp_aes_release_hardware();
return ret;
}
}
c = *input++;
*output++ = (unsigned char)( c ^ iv[n] );
iv[n] = (unsigned char) c;
n = (n + 1) & 0x0F;
}
} else {
while (length--) {
if ( n == 0 ) {
ret = esp_aes_block(ctx, iv, iv);
if (ret != 0) {
esp_aes_release_hardware();
return ret;
}
}
iv[n] = *output++ = (unsigned char)(iv[n] ^ *input++);
n = ( n + 1 ) & 0x0F;
}
}
esp_aes_release_hardware();
*iv_off = n;
}
#endif /* !SOC_AES_SUPPORT_DMA || CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM */
return 0;
}
/*
* AES-OFB (Output Feedback Mode) buffer encryption/decryption
*/
int esp_aes_crypt_ofb(esp_aes_context *ctx,
size_t length,
size_t *iv_off,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output )
{
size_t n;
// Use helpers for validation
int ret = esp_aes_validate_input(ctx, input, output, iv, true);
if (ret != 0) {
return ret;
}
if (!iv_off) {
ESP_LOGE(TAG, "No IV offset supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
n = *iv_off;
#if SOC_AES_SUPPORT_DMA
#if CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
if (length > AES_DMA_MODE_THRESHOLD) {
#endif /* CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM */
size_t stream_bytes = 0;
/* If there is an offset then use the output of the previous AES block
(the updated IV) to calculate the new output */
while (n > 0 && length > 0) {
*output++ = (*input++ ^ iv[n]);
n = (n + 1) & 0xF;
length--;
}
if (length > 0) {
stream_bytes = (length % AES_BLOCK_BYTES);
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, ESP_AES_DECRYPT);
aes_hal_mode_init(ESP_AES_BLOCK_MODE_OFB);
aes_hal_set_iv(iv);
ret = esp_aes_process_dma(ctx, input, output, length, iv);
if (ret != 0) {
esp_aes_release_hardware();
return ret;
}
aes_hal_read_iv(iv);
esp_aes_release_hardware();
}
*iv_off = n + stream_bytes;
#if CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
} else
#endif /* CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM */
#endif /* SOC_AES_SUPPORT_DMA */
#if !SOC_AES_SUPPORT_DMA || CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
{
// TODO: Check if this is necessary as the DMA version does not have this check
if (n > 15) {
return (MBEDTLS_ERR_AES_BAD_INPUT_DATA);
}
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, ESP_AES_ENCRYPT);
while (length--) {
if (n == 0) {
ret = esp_aes_block(ctx, iv, iv);
if (ret != 0) {
esp_aes_release_hardware();
return ret;
}
}
*output++ = *input++ ^ iv[n];
n = (n + 1) & 0x0F;
}
esp_aes_release_hardware();
*iv_off = n;
}
#endif /* !SOC_AES_SUPPORT_DMA || CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM */
return 0;
}
/*
* AES-CTR buffer encryption/decryption
*/
int esp_aes_crypt_ctr(esp_aes_context *ctx,
size_t length,
size_t *nc_off,
unsigned char nonce_counter[16],
unsigned char stream_block[16],
const unsigned char *input,
unsigned char *output )
{
size_t n;
// Use helper for validation
int ret = esp_aes_validate_input(ctx, input, output, NULL, false);
if (ret != 0) {
return ret;
}
if (!stream_block) {
ESP_LOGE(TAG, "No stream supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!nonce_counter) {
ESP_LOGE(TAG, "No nonce supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
if (!nc_off) {
ESP_LOGE(TAG, "No nonce offset supplied");
return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
}
n = *nc_off;
#if SOC_AES_SUPPORT_DMA
#if CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
if (length > AES_DMA_MODE_THRESHOLD) {
#endif /* CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM */
/* Process any unprocessed bytes left in stream block from
last operation */
while (n > 0 && length > 0) {
*output++ = (unsigned char)(*input++ ^ stream_block[n]);
n = (n + 1) & 0xF;
length--;
}
if (length > 0) {
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, ESP_AES_DECRYPT);
aes_hal_mode_init(ESP_AES_BLOCK_MODE_CTR);
aes_hal_set_iv(nonce_counter);
ret = esp_aes_process_dma(ctx, input, output, length, stream_block);
if (ret != 0) {
esp_aes_release_hardware();
return ret;
}
aes_hal_read_iv(nonce_counter);
esp_aes_release_hardware();
}
*nc_off = n + (length % AES_BLOCK_BYTES);
#if CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
} else
#endif /* CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM */
#endif /* SOC_AES_SUPPORT_DMA */
#if !SOC_AES_SUPPORT_DMA || CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM
{
esp_aes_acquire_hardware();
ctx->key_in_hardware = 0;
ctx->key_in_hardware = aes_hal_setkey(ctx->key, ctx->key_bytes, ESP_AES_ENCRYPT);
int c, i;
while (length--) {
if (n == 0) {
ret = esp_aes_block(ctx, nonce_counter, stream_block);
if (ret != 0) {
esp_aes_release_hardware();
return ret;
}
for (i = 16; i > 0; i--) {
if (++nonce_counter[i - 1] != 0) {
break;
}
}
}
c = *input++;
*output++ = (unsigned char)(c ^ stream_block[n]);
n = (n + 1) & 0x0F;
}
esp_aes_release_hardware();
*nc_off = n;
}
#endif /* !SOC_AES_SUPPORT_DMA || CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM */
return 0;
}
@@ -345,7 +345,6 @@ int esp_internal_aes_decrypt( esp_aes_context *ctx, const unsigned char input[16
/** XTS-AES buffer encryption/decryption */
int esp_aes_crypt_xts( esp_aes_xts_context *ctx, int mode, size_t length, const unsigned char data_unit[16], const unsigned char *input, unsigned char *output );
#ifdef __cplusplus
}
#endif
@@ -10,12 +10,13 @@
#include "esp_private/gdma.h"
#include "esp_err.h"
#include "soc/lldesc.h"
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {
#endif
#if (SOC_AES_GDMA) || (SOC_SHA_GDMA)
/**
* @brief Start a GDMA transfer on the shared crypto DMA channel
* Supports AXI-DMA and AHB-DMA.
@@ -52,6 +53,7 @@ bool esp_crypto_shared_gdma_done(void);
* and need the DMA channel for other peripherals. An example would be doing some processing after disconnecting WiFi
*/
void esp_crypto_shared_gdma_free(void);
#endif /* (SOC_AES_GDMA) || (SOC_SHA_GDMA) */
#ifdef __cplusplus
}
+3 -1
View File
@@ -34,6 +34,7 @@
#include "esp_sha_internal.h"
#include "sha/sha_core.h"
#include "esp_compiler.h"
/* Implementation that should never be optimized out by the compiler */
static void mbedtls_zeroize(void *v, size_t n)
@@ -119,7 +120,8 @@ int mbedtls_internal_sha1_process(mbedtls_sha1_context *ctx, const unsigned char
esp_internal_sha_update_state(ctx);
#if SOC_SHA_SUPPORT_DMA
if (sha_operation_mode(64) == SHA_DMA_MODE) {
// Unlikely to use DMA because data size is 64 bytes which is smaller than the DMA threshold
if (unlikely(sha_operation_mode(64) == SHA_DMA_MODE)) {
int ret = esp_sha_dma(SHA1, data, 64, NULL, 0, ctx->first_block);
if (ret != 0) {
esp_sha_release_hardware();
@@ -34,6 +34,7 @@
#include "esp_sha_internal.h"
#include "sha/sha_core.h"
#include "esp_compiler.h"
/* Implementation that should never be optimized out by the compiler */
static void mbedtls_zeroize(void *v, size_t n)
@@ -132,7 +133,8 @@ int mbedtls_internal_sha256_process(mbedtls_sha256_context *ctx, const unsigned
esp_internal_sha_update_state(ctx);
#if SOC_SHA_SUPPORT_DMA
if (sha_operation_mode(64) == SHA_DMA_MODE) {
// Unlikely to use DMA because data size is 64 bytes which is smaller than the DMA threshold
if (unlikely(sha_operation_mode(64) == SHA_DMA_MODE)) {
int ret = esp_sha_dma(ctx->mode, data, 64, NULL, 0, ctx->first_block);
if (ret != 0) {
esp_sha_release_hardware();
@@ -40,6 +40,7 @@
#include "esp_sha_internal.h"
#include "sha/sha_core.h"
#include "esp_compiler.h"
/* Implementation that should never be optimized out by the compiler */
static void mbedtls_zeroize(void *v, size_t n)
@@ -169,7 +170,8 @@ int mbedtls_internal_sha512_process(mbedtls_sha512_context *ctx, const unsigned
}
#if SOC_SHA_SUPPORT_DMA
if (sha_operation_mode(128) == SHA_DMA_MODE) {
// Likely to use DMA because data size is 128 bytes which is larger or equal to the DMA threshold
if (likely(sha_operation_mode(128) == SHA_DMA_MODE)) {
ret = esp_sha_dma(ctx->mode, data, 128, NULL, 0, ctx->first_block);
if (ret != 0) {
esp_sha_release_hardware();