Hardware Accelerators in ESP32-S3: Enhancing Cryptographic Efficiency

This article focuses on the SHA, AES, RSA, and HMAC hardware accelerators in the ESP32-S3, explaining how dedicated circuits enhance encryption efficiency. By comparing the algorithm characteristics and application scenarios of different accelerators, along with program examples involving data hashing and encryption/decryption, it demonstrates their collaborative role in ensuring data security and efficiency.

Hardware Accelerators in ESP32-S3: Enhancing Cryptographic Efficiency

01

Introduction to Accelerators

The SHA, AES, RSA, and HMAC accelerators integrated into the ESP32-S3 are hardware modules specifically designed for cryptographic operations. The essence of “acceleration” is to replace general-purpose CPUs with dedicated circuits to execute specific algorithms, resulting in speed improvements of tens to hundreds of times while reducing power consumption and CPU usage. For example, hardware AES encryption can achieve speeds over 10 times that of software implementations, which is crucial for real-time communication or battery-powered devices.

1. Evolution and Historical Significance:

  • SHA Accelerator:

    From SHA-0 to the SHA-2 series (SHA-256, SHA-512, and SHA-512/t), hardware acceleration has driven the application of hash algorithms in blockchain, digital signatures, and other fields, ensuring data integrity.

  • AES Accelerator:

    Technologies like AES-NI have popularized symmetric encryption in high-performance computing, supporting data privacy protection for IoT devices and addressing the security vulnerabilities of DES.

  • RSA Accelerator:

    From the early 512 bits to the current 2048/3072 bits, hardware acceleration has made secure communication (such as SSL/TLS) possible, supporting the explosive growth of e-commerce and online services.

  • HMAC Accelerator:

    As a core component of security protocols, hardware acceleration enhances the security and efficiency of scenarios like API authentication and VPNs, addressing the security flaws of traditional message authentication codes.

2. Why Design Multiple Accelerators in ESP32-S3?

  • Differences in Algorithm Principles

    The mathematical foundations of different accelerators are entirely different: AES is based on substitution and shifting operations, RSA relies on the difficulty of large number factorization, SHA is a one-way hash function, and HMAC is a combination of hashing and keys. Dedicated hardware can optimize circuits for the characteristics of each algorithm, while general-purpose accelerators cannot accommodate all principles.

  • Performance and Security Balance

    A single accelerator cannot meet diverse needs: AES is suitable for encrypting large amounts of data but has difficulties with key distribution, RSA is suitable for key exchange but is inefficient for encrypting large data, SHA can only verify integrity, while HMAC needs to ensure both integrity and authenticity. They need to work together (e.g., RSA encrypts the AES key, AES encrypts data, and HMAC verifies transmission).

  • Historical Evolution Needs

    Cryptographic algorithms continuously iterate with security threats: DES has been replaced by AES, and SHA-1 has been replaced by SHA-256. Each new algorithm requires hardware adaptation. Dedicated accelerators can specifically support new algorithms while being compatible with older versions, ensuring device compatibility during security upgrades.

3. Comparison of Four Accelerators

Accelerator

Data Before Acceleration

Data After Acceleration

Practical Significance

Remarks

SHA

Any data (e.g., files)

Fixed-length hash value (e.g., 32 bytes)

Verifies that data has not been tampered with, such as software download verification

Supports SHA-1/224/256/384/512/SHA-512/t, does not support SHA-3

AES

Plaintext (e.g., “Temperature 25℃”)

Ciphertext (e.g., random byte sequence)

Protects data privacy, such as smart home sensor data

Supports ECB/CBC/CTR/OFB/CFB8/CFB128; GCM requires software implementation (no hardware acceleration)

RSA

Symmetric key (e.g., AES key)

Encrypted ciphertext

Securely distributes keys, such as negotiating keys in TLS handshake

Hardware supports up to 3072 bits; >3072 bits degrades to software implementation

HMAC

Message (e.g., “Transfer 100 yuan”) + key

HMAC value (e.g., 32 bytes)

Verifies message origin and integrity, such as bank transfer requests

Keys > 128 bytes require preprocessing (SHA-256 compression), increasing time by about 0.02ms

02

Working Principles

1.SHA:

The SHA accelerator executes SHA series hash algorithms through hardware circuits. The core process converts input data of any length into a fixed-length hash value through “padding → chunking → iterative hashing”. Its working principle involves performing a series of logical operations (such as shifting, XOR, addition) on data blocks and iteratively processing all data blocks to ultimately generate an irreversible hash value. The hardware significantly enhances efficiency by parallel processing hash steps through pipelined design and dedicated computation units.

Comparison of data before and after acceleration:

Before acceleration: Raw data of any length (e.g., an 8KB firmware file, binary “00 01 02 …”).

After acceleration: Fixed-length hash value (e.g., 32 bytes SHA-256 result “5d 2b 0f … 4a”).

2.AES :

The AES accelerator executes symmetric encryption algorithms through hardware circuits, primarily based on a “substitution – shifting” network for data encryption and decryption. Its working principle involves dividing data into 16-byte blocks and performing multiple rounds of “byte substitution (SubBytes) → row shifting (ShiftRows) → column mixing (MixColumns) → round key addition (AddRoundKey)” operations, combining the key to generate round keys for encryption; decryption is achieved through inverse operations. The hardware significantly speeds up the process by parallel processing round operations through dedicated circuits.

Comparison of data before and after acceleration:

Before acceleration: Plaintext data (e.g., binary “54 3d 32 …”).

After acceleration: Encrypted ciphertext (e.g., “3a d7 7b … 6e”, appearing as random binary).

After decryption: Restored to original plaintext data.

3. RSA Accelerator:

The RSA accelerator executes asymmetric encryption through hardware circuits, primarily based on large number modular operations to achieve “public key encryption – private key decryption” or “private key signing – public key verification”. Its working principle utilizes the “large number factorization problem”: during encryption, it calculates C=Me mod N (C is the ciphertext, M is the plaintext, e is the public key exponent, N is the modulus); during decryption, it calculates M=Cd mod N (d is the private key exponent). The hardware accelerates core operations through dedicated large number computation units (such as modular multipliers, modular exponentiators), reducing time complexity.

Comparison of data before and after acceleration:

Before acceleration: Symmetric key to be encrypted (e.g., 16-byte AES key “2b 7e 15 … 3c”).

After acceleration: Encrypted ciphertext (2048-bit RSA generates 256-byte ciphertext, appearing as random binary).

After decryption: Restored to original symmetric key.

4. HMAC Accelerator:

The HMAC accelerator executes hash-based message authentication code algorithms through hardware circuits, primarily combining hash functions and shared keys to achieve message authentication. Its working principle is: first, preprocess the message with the key (XOR the key with a fixed value), then perform hash operations in two steps (first hash the combination of the key and message, then hash the intermediate result with another combination of the key), ultimately generating the HMAC value. The hardware achieves end-to-end acceleration by integrating key preprocessing units and hash acceleration circuits.

Comparison of data before and after acceleration:

Before acceleration: Original message (e.g., “Transfer 100 yuan”) + shared key (32 bytes “00 01 … 1f”).

After acceleration: 32-byte HMAC value (e.g., “a3 b7 2d … 8f”).

Verification result: The receiver calculates the HMAC using the same message and key, comparing it with the received HMAC value; if they match, verification is successful.

03

Program Examples and Results

1. SHA Accelerator:

Function:

Generate a segment of 8 KB test data on the ESP32, use hardware-accelerated SHA-256 to compute its digest (hash), and print the first 32 bytes of data and the 32-byte hash result via serial port, finally releasing memory.

Code:

/* Include header files */#include "freertos/FreeRTOS.h"      // Provides FreeRTOS basic definitions (not directly used in this example)#include "esp_log.h"                // ESP-IDF logging system: ESP_LOGx, ESP_LOG_BUFFER_HEX#include "mbedtls/sha256.h"         // mbedTLS 3.x SHA-256 single API#include <stdlib.h>                 // malloc / free#include <stdint.h>                 // uint8_t, size_t, etc. fixed-width types
/* Log TAG: All log lines will display [SHA_EXAMPLE] */static const char *TAG = "SHA_EXAMPLE";
/* Program entry point, automatically called once by ESP-IDF after system startup */void app_main(void){    /* 1. Prepare 8 KB test data length constant */    const size_t LEN = 8192;
    /* 2. Dynamically allocate 8 KB memory on the heap for storing test data */    uint8_t *data = malloc(LEN);    if (!data) {                           // Memory allocation failure protection        ESP_LOGE(TAG, "malloc failed");    // Print error log        return;                            // Directly return to avoid null pointer    }
    /* 3. Fill the entire 8 KB data with 0x00, 0x01,..., 0xFF in a loop */    for (size_t i = 0; i < LEN; ++i) {        data[i] = (uint8_t)(i & 0xFF);     // &0xFF ensures value is between 0~255    }
    /* 4. Print the first 32 bytes of data for visual verification */    ESP_LOG_BUFFER_HEX(TAG, data, 32);     // 16 bytes per line, automatically wraps
    /* 5. Define a 32-byte buffer to store the 256-bit SHA-256 digest */    uint8_t sha_out[32];
    /* 6. Call mbedTLS's hardware-accelerated SHA-256 computation       Parameters: input pointer, input length, output buffer, algorithm choice (0=SHA-256, 1=SHA-224) */    mbedtls_sha256(data, LEN, sha_out, 0);
    /* 7. Print the digest result */    ESP_LOGI(TAG, "SHA-256 result:");        // Normal info log    ESP_LOG_BUFFER_HEX(TAG, sha_out, 32);    // Hexadecimal print of 32-byte digest
    /* 8. Free the allocated heap memory to prevent memory leaks */    free(data);}

Result:

Serial print result: I (848) main_task: Calling app_main()I (851) SHA_EXAMPLE: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f I (857) SHA_EXAMPLE: 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f I (865) SHA_EXAMPLE: SHA-256 result:I (869) SHA_EXAMPLE: dc 40 4a 61 3f ed ae b5 40 34 51 4b c6 50 5f 56 I (876) SHA_EXAMPLE: b9 33 ca a5 25 02 99 ba 7d 09 43 77 a5 1c aa 46 I (883) main_task: Returned from app_main()

Explanation:

The following two lines are the raw data.

I (851) SHA_EXAMPLE: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f I (857) SHA_EXAMPLE: 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 

The following two lines are the unique fingerprint obtained after SHA-256 computation.

I (869) SHA_EXAMPLE: dc 40 4a 61 3f ed ae b5 40 34 51 4b c6 50 5f 56 I (876) SHA_EXAMPLE: b9 33 ca a5 25 02 99 ba 7d 09 43 77 a5 1c aa 46 

2. AES Accelerator:

Function:

AES-128-ECB encrypts the temperature and humidity string “T=23.5,H=60.2” and then decrypts it to verify the correctness of the encryption and decryption loop.

Code:

/** *  AES-128-ECB Encryption/Decryption Example (ESP-IDF) *  Description: Encrypts the 16-byte plaintext "T=23.5,H=60.2" with a fixed key, *        then immediately decrypts it to verify the correctness of the result. */#include &lt;stdio.h&gt;#include &lt;string.h&gt;#include "esp_log.h"#include "mbedtls/aes.h"
static const char *TAG = "AES_EXAMPLE";
void app_main(void){    /* 1. Prepare data ---------------------------------------------------------- */    uint8_t plain[16]  = "T=23.5,H=60.2";   //  AES processes the original plaintext data. Exactly 16B    uint8_t key[16]    = {0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,  // 16 bytes (128 bits) AES symmetric encryption key for AES-128-ECB encryption and decryption                          0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c};
    uint8_t cipher[16];  // Store encryption result    uint8_t verify[16];  // Store decryption result
    /* 2. Initialize mbedTLS AES context --------------------------------------- */    mbedtls_aes_context ctx;    mbedtls_aes_init(&amp;ctx);
    /* 3. Encrypt -------------------------------------------------------------- */    mbedtls_aes_setkey_enc(&amp;ctx, key, 128);                 // Set encryption key    mbedtls_aes_crypt_ecb(&amp;ctx, MBEDTLS_AES_ENCRYPT,        // ECB mode encryption                          plain, cipher);
    ESP_LOGI(TAG, "Cipher text (hex):");    ESP_LOG_BUFFER_HEX(TAG, cipher, sizeof(cipher));        // Print ciphertext
    /* 4. Decrypt -------------------------------------------------------------- */    mbedtls_aes_setkey_dec(&amp;ctx, key, 128);                 // Set decryption key    mbedtls_aes_crypt_ecb(&amp;ctx, MBEDTLS_AES_DECRYPT,        // ECB mode decryption                          cipher, verify);
    ESP_LOGI(TAG, "Decrypt back (hex):");    ESP_LOG_BUFFER_HEX(TAG, verify, sizeof(verify));        // Print decryption result (HEX)
    /* 5. Print the decryption result directly as a string (note that there may be padding 0x00 at the end) -------- */    char tmp[17] = {0};                     // Reserve terminator    memcpy(tmp, verify, 16);    ESP_LOGI(TAG, "Plain text (ASCII): \"%s\"", tmp);
    /* 6. Free resources ---------------------------------------------------------- */    mbedtls_aes_free(&amp;ctx);}

Result:

Log print information: I (849) main_task: Calling app_main()I (851) AES_EXAMPLE: Cipher text:I (855) AES_EXAMPLE: e4 fa 5f 91 99 4a d4 ce cf 87 8d 40 9c 60 7e d5 I (862) AES_EXAMPLE: Decrypt back:I (866) AES_EXAMPLE: 54 3d 32 33 2e 35 2c 48 3d 36 30 2e 32 00 00 00 I (873) main_task: Returned from app_main()

Explanation:

e4 fa 5f 91… is the 16-byte ciphertext;

54 3d 32 33… is the restored 16-byte plaintext, corresponding to the ASCII string “T=23.5,H=60.2\0\0\0”.

3. RSA Accelerator:

Function:

This example generates a 2048-bit RSA key on the ESP32-C3/C6/S3 with an RSA hardware accelerator, completing the full process of “SHA-256 → RSA signing → signature verification”, measuring and printing the signing time, and then automatically rebooting.

Code:

/*  RSA Accelerator Demonstration: 2048-bit Private Key Signing */#include &lt;stdio.h&gt;#include &lt;string.h&gt;
/* FreeRTOS related */#include "freertos/FreeRTOS.h"#include "freertos/task.h"
/* ESP logging, reboot, high-precision timer */#include "esp_log.h"#include "esp_system.h"#include "esp_timer.h"
/* mbedTLS algorithm library */#include "mbedtls/rsa.h"#include "mbedtls/entropy.h"#include "mbedtls/ctr_drbg.h"#include "mbedtls/sha256.h"
static const char *TAG = "RSA_ACCEL";
void app_main(void){    /* ---------- 1. Initialize mbedTLS context ---------- */    mbedtls_rsa_context         rsa;        /* RSA key context */    mbedtls_entropy_context     entropy;    /* Entropy pool */    mbedtls_ctr_drbg_context    ctr_drbg;   /* Random number generator */
    mbedtls_entropy_init(&amp;entropy);    mbedtls_ctr_drbg_init(&amp;ctr_drbg);    mbedtls_rsa_init(&amp;rsa);
    /* Random seed personalization string */    const char *pers = "rsa_accel_demo";    mbedtls_ctr_drbg_seed(&amp;ctr_drbg, mbedtls_entropy_func, &amp;entropy,                          (const unsigned char *)pers, strlen(pers));
    /* ---------- 2. Generate 2048-bit RSA key pair ---------- */    ESP_LOGI(TAG, "Generating 2048-bit RSA key ...");    /* Note: The generation process only uses software algorithms, which takes a long time; the accelerator does not participate. */    mbedtls_rsa_gen_key(&amp;rsa, mbedtls_ctr_drbg_random, &amp;ctr_drbg,                        2048,                /* Key bit length */                        65537);              /* Public key exponent */
    /* ---------- 3. Compute message hash ---------- */    const char *msg = "hello esp32 rsa";    unsigned char hash[32];                 /* SHA-256 output 32 bytes */    mbedtls_sha256((const unsigned char *)msg, strlen(msg), hash, 0);
    /* ---------- 4. Sign the message with the private key ---------- */    unsigned char sig[256];                 /* 2048-bit => 256 bytes */    int64_t t0 = esp_timer_get_time();      /* High-precision timing starts */    int ret = mbedtls_rsa_pkcs1_sign(                  &amp;rsa,                     /* RSA context (containing private key) */                  mbedtls_ctr_drbg_random,  /* Random function */                  &amp;ctr_drbg,                /* Random context */                  MBEDTLS_MD_SHA256,        /* Hash algorithm identifier */                  32,                       /* Hash length */                  hash,                     /* Input hash value */                  sig);                     /* Output signature buffer */    int64_t t1 = esp_timer_get_time();      /* Timing ends */
    if (ret == 0) {        ESP_LOGI(TAG, "RSA sign OK, time = %lld ms", (t1 - t0) / 1000);    } else {        ESP_LOGE(TAG, "mbedtls_rsa_pkcs1_sign returned -0x%04X", -ret);    }
    /* ---------- 5. Verify the signature with the public key ---------- */    ret = mbedtls_rsa_pkcs1_verify(              &amp;rsa,                         /* RSA context (containing public key) */              MBEDTLS_MD_SHA256,            /* Hash algorithm identifier */              32,                           /* Hash length */              hash,                         /* Original hash value */              sig);                         /* Signature to verify */    ESP_LOGI(TAG, "Verify %s", ret ? "FAIL" : "PASS");
    /* ---------- 6. Free resources ---------- */    mbedtls_rsa_free(&amp;rsa);    mbedtls_ctr_drbg_free(&amp;ctr_drbg);    mbedtls_entropy_free(&amp;entropy);
    /* ---------- 7. Delay reboot ---------- */    ESP_LOGI(TAG, "Demo end, reboot in 5 s");    vTaskDelay(pdMS_TO_TICKS(5000));   /* Block for 5 seconds */    esp_restart();                     /* Soft reboot */}

Result:

Output log information: I (835) main_task: Started on CPU0I (856) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocationsI (856) main_task: Calling app_main()I (860) RSA_ACCEL: Generating 2048-bit RSA key ...I (11904) RSA_ACCEL: RSA sign OK, time = 461 msI (11905) RSA_ACCEL: Verify PASSI (11905) RSA_ACCEL: Demo end, reboot in 5 s

Explanation:

I (835): After the chip is powered on, the ESP-IDF’s “main task” has started on CPU0, preparing to schedule user programs.

I (856): When the chip has PSRAM, the system reserves a pool of 32 KB of memory for DMA/internal allocations, which is not directly related to the RSA example and is part of the startup information.

I (856): The framework officially calls the user-written app_main() entry.

I (860): The program begins generating a 2048-bit RSA key pair using software algorithms; the ESP32’s RSA accelerator does not participate in the generation phase, so it takes a long time.

I (11904): The private key has been generated; then the mbedTLS’s mbedtls_rsa_pkcs1_sign is called to sign the message with SHA-256 + RSA-2048, taking 461 ms (the hardware accelerator has intervened, making it several times faster than pure software).

I (11905): The same RSA public key is used to verify the signature just made, and the result passes.

I (11905): The demonstration is complete, and the system will soft reboot after 5 seconds.

4. HMAC Accelerator:

Function:

  • Construct a control command string (open_light:1722200000);

  • Use a 32-byte shared symmetric key;

  • Calculate HMAC-SHA256 signature using the mbedTLS software library;

  • Print the signature for message integrity verification or identity authentication.

Code:

/* main.c  – HMAC-SHA256 Example (ESP-IDF v5.3 compatible) */#include &lt;stdio.h&gt;#include &lt;string.h&gt;#include "esp_log.h"#include "mbedtls/md.h"
static const char *TAG = "HMAC_EXAMPLE";
void app_main(void){    /* 1. Original message: command + timestamp */    char msg[] = "open_light:1722200000";   // Total 21 bytes    ESP_LOGI(TAG, "Message: %s", msg);    ESP_LOG_BUFFER_HEX(TAG, msg, strlen(msg));
    /* 2. Shared key (for demonstration: 0x00 ~ 0x1F) */    uint8_t key[32] = {0};    for (int i = 0; i < 32; i++) {        key[i] = (uint8_t)i;    }
    /* 3. Calculate HMAC-SHA256     *    Using mbedTLS's general message digest interface     */    uint8_t mac[32] = {0};               // Result buffer    const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);    mbedtls_md_hmac(md_info,            // Algorithm: SHA256                    key, sizeof(key),   // Key                    (const unsigned char *)msg, strlen(msg), // Message                    mac);               // Output 32-byte MAC
    /* 4. Print MAC for verification or network transmission */    ESP_LOGI(TAG, "HMAC:");    ESP_LOG_BUFFER_HEX(TAG, mac, sizeof(mac));}

Result:

Log information: I (849) main_task: Calling app_main()I (851) HMAC_EXAMPLE: Message: open_light:1722200000I (857) HMAC_EXAMPLE: 6f 70 65 6e 5f 6c 69 67 68 74 3a 31 37 32 32 32 I (864) HMAC_EXAMPLE: 30 30 30 30 30 I (869) HMAC_EXAMPLE: HMAC:I (872) HMAC_EXAMPLE: 20 19 eb cb 00 c4 2c 1d 54 d9 95 d0 25 18 0d 72 I (879) HMAC_EXAMPLE: 58 69 53 6c 7e 12 59 be b0 22 71 7d d9 9f 2a fe I (892) main_task: Returned from app_main()

Explanation:

The following is the ASCII representation of open_light:17222:

I (857) HMAC_EXAMPLE: 6f 70 65 6e 5f 6c 69 67 68 74 3a 31 37 32 32 32 

The following is the 32-byte MAC value, shown in two lines of hexadecimal:

I (872) HMAC_EXAMPLE: 20 19 eb cb 00 c4 2c 1d 54 d9 95 d0 25 18 0d 72 I (879) HMAC_EXAMPLE: 58 69 53 6c 7e 12 59 be b0 22 71 7d d9 9f 2a fe 

This article’s software and hardware case description:

1. SOC Model: ESP32-S3-N16R8;

2. Software Development Environment: ESP-IDF 5.3.1, VSCode IDE (VSCodeUserSetup-x64-1.102.1 version);

3. ESP32 Project Version: IDF version (FreeRTOS);

4. Program Download: Type C USB (USB to serial) interface;

5. This software and hardware case is for personal learning only and not for commercial use.

References for this article:

“ESP32-S3 Technical Reference Manual Version 1.7”;

“ESP32-S3 Series Chip Technical Specification Version 2.0”.

Leave a Comment