# Hash-Based Message Authentication Code (HMAC)

[中文]

Hash-based Message Authentication Code (HMAC) is a secure authentication technique that verifies the authenticity and integrity of a message with a pre-shared key. This module provides hardware acceleration for SHA256-HMAC generation using a key burned into an eFuse block.

For more detailed information on the application workflow and the HMAC calculation process, see ESP32-C5 Technical Reference Manual > HMAC Accelerator (HMAC) [PDF].

## Generalized Application Scheme

Let there be two parties, A and B. They want to verify the authenticity and integrity of messages sent between each other. Before they can start sending messages, they need to exchange the secret key via a secure channel.

To verify A's messages, B can do the following:

- A calculates the HMAC of the message it wants to send.

- A sends the message and the HMAC to B.

- B calculates the HMAC of the received message itself.

- B checks whether the received and calculated HMACs match.

If they do match, the message is authentic.

However, the HMAC itself is not bound to this use case. It can also be used for challenge-response protocols supporting HMAC or as a key input for further security modules (see below), etc.

## HMAC on ESP32-C5

On ESP32-C5, the HMAC module works with a secret key burnt into the eFuses.

On ESP32-C5, the HMAC module also supports storing a secret key in the Key Manager. Refer to Key Manager for more details.

This key can be made completely inaccessible for any resources outside the cryptographic modules, thus avoiding key leakage.

Furthermore, ESP32-C5 has three different application scenarios for its HMAC module:

1. HMAC is generated for software use

2. HMAC is used as a key for the RSA Digital Signature Peripheral (RSA_DS)

3. HMAC is used for enabling the soft-disabled JTAG interface

The first mode is called Upstream mode, while the last two modes are called Downstream modes.

### eFuse Keys for HMAC

Six physical eFuse blocks can be used as keys for the HMAC module: block 4 ~ block 9. The enum `hmac_key_id_t` in the API maps them to `HMAC_KEY0` ~ `HMAC_KEY5`.

Each key has a corresponding eFuse parameter key purpose determining for which of the HMAC application scenarios (see below) the key may be used:

Key Purpose | Application Scenario
--- | ---
8 | HMAC generated for software use
7 | HMAC used as a key for the RSA Digital Signature Peripheral (RSA_DS)
6 | HMAC used for enabling the soft-disabled JTAG interface
5 | HMAC both as a key for the RSA_DS module and for enabling JTAG

This is to prevent the usage of a key for a different function than originally intended.

To calculate an HMAC, the software has to provide the ID of the key block containing the secret key as well as the key purpose (see ESP32-C5 Technical Reference Manual > eFuse Controller (eFuse) [PDF]).

Before the HMAC key calculation, the HMAC module looks up the purpose of the provided key block. The calculation only proceeds if the purpose of the provided key block matches the purpose stored in the eFuses of the key block provided by the ID.

### HMAC Generation for Software

Key purpose value: 8

In this case, the HMAC is given out to the software, e.g., to authenticate a message.

The API to calculate the HMAC is `psa_mac_compute()`, which takes an opaque PSA key referencing an eFuse key block that contains the secret and has its purpose set to Upstream mode.

### HMAC for RSA Digital Signature

Key purpose values: 7, 5

The HMAC can be used as a key derivation function to decrypt private key parameters which are used by the RSA Digital Signature module. A standard message is used by the hardware in that case. You only need to provide the eFuse key block and purpose on the HMAC side, additional parameters are required for the RSA Digital Signature component in that case.

Neither the key nor the actual HMAC is ever exposed outside the HMAC module and RSA_DS component. The calculation of the HMAC and its handover to the RSA_DS component happen internally.

For more details, see ESP32-C5 Technical Reference Manual > RSA Digital Signature Peripheral (RSA_DS) [PDF].

### HMAC for Enabling JTAG

Key purpose values: 6, 5

The third application is using the HMAC as a key to enable JTAG if it was soft-disabled before.

Following is the procedure to re-enable the JTAG:

Stage 1: Setup

1. Generate a 256-bit HMAC secret key to use for JTAG re-enable.

2. Write the key to an eFuse block with key purpose HMAC_DOWN_ALL (5) or HMAC_DOWN_JTAG (6). This can be done using the `esp_efuse_write_key()` function in the firmware or using `idf.py efuse-burn-key` from the host.

3. Configure the eFuse key block to be read-protected using the `esp_efuse_set_read_protect()`, so that software cannot read back the value.

4. Burn the `soft JTAG disable` bit/bits on ESP32-C5. This will permanently disable JTAG unless the correct key value is provided by the software.

Note

The API esp_efuse_write_field_cnt(ESP_EFUSE_SOFT_DIS_JTAG, ESP_EFUSE_SOFT_DIS_JTAG[0]->bit_count) can be used to burn `soft JTAG disable` bits on ESP32-C5.

Note

If `DIS_PAD_JTAG` eFuse is set, then `SOFT_DIS_JTAG` functionality does not work because JTAG is permanently disabled.

JTAG enables

1. The key to re-enable JTAG is the output of the HMAC-SHA256 function using the secret key in eFuse and 32 `0x00` bytes as the message.

2. Pass this key value when calling the `esp_hmac_jtag_enable()` function from the firmware.

3. To re-disable JTAG in the firmware, reset the system or call `esp_hmac_jtag_disable()`.

For a complete workflow of soft-disabling and re-enabling JTAG, refer to security/hmac_soft_jtag. This example demonstrates how to use HMAC to re-enable a soft-disabled JTAG interface, covering steps like generating an HMAC key, burning it to eFuse, and creating token data from the key.

For more details, see ESP32-C5 Technical Reference Manual > HMAC Accelerator (HMAC) [PDF].

## Application Outline

The following code is an outline of how to set an eFuse key and then use it to calculate an HMAC for software usage.

Using eFuses to store the HMAC key:

We use `esp_efuse_write_key` to set physical key block 4 in the eFuse for the HMAC module together with its purpose. `ESP_EFUSE_KEY_PURPOSE_HMAC_UP` (8) means that this key can only be used for HMAC generation for software usage:

    #include "esp_efuse.h"

    const uint8_t key_data[32] = { ... };

    esp_err_t status = esp_efuse_write_key(EFUSE_BLK_KEY4,
                        ESP_EFUSE_KEY_PURPOSE_HMAC_UP,
                        key_data, sizeof(key_data));

    if (status == ESP_OK) {
        // written key
    } else {
        // writing key failed, maybe written already
    }

Now we can calculate an HMAC for software usage with the saved key through the PSA Crypto API.

Using an eFuse-based HMAC key:

    #include "psa/crypto.h"
    #include "psa_crypto_driver_esp_hmac_opaque.h"

    uint8_t hmac[32];
    size_t hmac_length = 0;

    const char *message = "Hello, HMAC!";
    const size_t msg_len = 12;

    // Setup key attributes for ESP-HMAC opaque driver
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE);
    psa_set_key_algorithm(&attributes, PSA_ALG_HMAC(PSA_ALG_SHA_256));
    psa_set_key_type(&attributes, PSA_KEY_TYPE_HMAC);
    psa_set_key_bits(&attributes, 256);
    psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_ESP_HMAC_VOLATILE);

    // Create opaque key reference for eFuse-based key
    esp_hmac_opaque_key_t opaque_key = {
        .efuse_key_id = HMAC_KEY4,
    };

    // Import the opaque key
    psa_key_id_t key_id = 0;
    psa_status_t status = psa_import_key(&attributes, (uint8_t *)&opaque_key,
                                         sizeof(opaque_key), &key_id);
    if (status != PSA_SUCCESS) {
        // Failed to import key
        psa_reset_key_attributes(&attributes);
        return;
    }

    // Compute HMAC
    status = psa_mac_compute(key_id, PSA_ALG_HMAC(PSA_ALG_SHA_256),
                             (uint8_t *)message, msg_len,
                             hmac, sizeof(hmac), &hmac_length);

    // Clean up
    psa_destroy_key(key_id);
    psa_reset_key_attributes(&attributes);

    if (status == PSA_SUCCESS) {
        // HMAC written to hmac now
    } else {
        // failure calculating HMAC
    }

Note

The ESP-HMAC opaque PSA driver is backed by the one-shot hardware HMAC peripheral, which computes the MAC over the whole message in a single operation and cannot save or restore intermediate state between calls. Multipart streaming is therefore not supported: supply the entire message in a single `psa_mac_compute()` call (as shown above) or in a single multipart update. A second non-empty update on the same operation returns `PSA_ERROR_BAD_STATE`; the operation fails closed and never produces a MAC computed over only part of the message.

## API Reference

### Header File

- components/mbedtls/port/psa_driver/include/psa_crypto_driver_esp_hmac_opaque_contexts.h

- This header file can be included with:

  >     #include "psa_crypto_driver_esp_hmac_opaque_contexts.h"

- This header file is a part of the API provided by the `mbedtls` component. To declare that your component depends on `mbedtls`, add the following to your CMakeLists.txt:

  >     REQUIRES mbedtls

  > or

  >     PRIV_REQUIRES mbedtls

Was this page helpful?

- Thank you! We received your feedback.
- If you have any comments, fill in Espressif Documentation Feedback Form.

- We value your feedback.
- Let us know how we can improve this page by filling in Espressif Documentation Feedback Form.