# Secure Storage

## Overview

The TEE Secure Storage service provides persistent storage for securely storing sensitive data, such as cryptographic keys, cloud credentials, or other general-purpose information. It uses a dedicated flash partition of type `data` and subtype `nvs`. The TEE ensures both confidentiality and integrity of the stored data.

TEE Secure Storage adopts the Non-Volatile Storage Library partition format and uses the HMAC peripheral-based XTS-AES encryption scheme, as detailed here. The AES encryption keys are derived from an HMAC key programmed in eFuse with the purpose `esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP`. Please note that the TEE Secure storage does not support the NVS Flash Encryption-based scheme.

Important

- One eFuse block is required to store the HMAC key used for deriving the NVS encryption keys. This key is exclusive to the TEE and CANNOT be used by the REE for any purpose.

- The required key must be programmed into eFuse before firmware execution, as TEE Secure Storage does not support generating it on-device. If no valid key with the required purpose is found in the configured eFuse block, an error will be raised at runtime.

Additionally, the secure storage provides interfaces for performing the following cryptographic services from the TEE using securely stored key material:

1. Authenticated encryption and decryption using the `aes256_gcm` algorithm

2. Message signing and public key retrieval using ECDSA with the following curves

   - `ecdsa_secp256r1`

Note

As per the current implementation, the TEE Secure Storage partition must have the label `secure_storage`.

TEE secure storage also supports ECDSA signing with keys derived via PBKDF2 (Password-Based Key Derivation Function 2), using an HMAC key programmed in eFuse along with a user-provided salt. This mechanism enables ECDSA signing on the P-256 curve without requiring storage of the actual private keys. The eFuse HMAC key ID for the PBKDF2 operations is specified via the CONFIG_SECURE_TEE_PBKDF2_EFUSE_HMAC_KEY_ID option.

Important

- The eFuse HMAC key ID used for PBKDF2-based signing CANNOT be the same as the one used for deriving TEE secure storage encryption keys (i.e., CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID).

- This eFuse ID is also exclusive to the TEE and CANNOT be used by the REE for any purpose.

## Internals

Each data object consisting of the type, associated metadata flags (e.g., `WRITE_ONCE`), and the actual payload is encapsulated in a structured format and stored as a variable-length NVS blob in the secure storage partition.

Note

As per the current implementation, all data objects in the TEE Secure Storage are to be stored in the `tee_sec_stg_ns` namespace.

Currently, TEE secure storage supports storing the following cryptographic keys:

1. `aes256` keys, including the key and initialization vector (IV)

2. ECDSA key-pairs for the following curves, including both public and private components

   - `ecdsa_secp256r1`

All assets related to TEE secure storage are protected by the APM peripheral and are inaccessible to the REE application. Any direct access attempts will result in a system fault. Future updates are planned to add support for additional key types and general-purpose data storage.

The TEE Secure Storage feature supports two modes for determining how the NVS encryption keys are derived (see CONFIG_SECURE_TEE_SEC_STG_MODE):

> - Development Mode: Encryption keys are embedded (constant for all instances) in the ESP-TEE firmware.

> - Release Mode: Encryption keys are derived via the HMAC peripheral (or software-based HMAC implementation) using a key stored in eFuse, specified by CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID.

Note

- The valid range for CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID is from `0` (`hmac_key_id_t::HMAC_KEY0`) to `5` (`hmac_key_id_t::HMAC_KEY5`). By default, this config is set to `-1` and must be configured before building the TEE application.

- The following commands can be used to generate and program the HMAC key into the required eFuse block:

      # Generate a random 32-byte HMAC key
      openssl rand -out hmac_key_file.bin 32

      # Program the HMAC key into the eFuse block
      espefuse -p PORT burn-key <BLOCK_KEY0-5> hmac_key_file.bin HMAC_UP

## Tools

The NVS Partition Generator Utility tool can be used to generate binary images compatible with the NVS format for use with TEE Secure Storage. Since TEE Secure Storage stores data objects using a custom structured format, an additional step is required to convert input data into this format prior to image generation and encryption.

To support this process, the esp_tee_sec_stg_keygen.py script is provided for generating secure key blobs corresponding to the various supported cryptographic algorithms. These key blobs are then referenced in the input CSV file (format described here) and passed to the NVS Partition Generator utility to produce an encrypted images suitable for TEE Secure Storage.

Refer the detailed steps given here on generating key blobs and encrypted NVS partition images for TEE Secure Storage.

## Application Example

The tee_secure_storage example demonstrates how to generate ECDSA key pairs and AES-256 keys in the TEE secure storage and use them for signing messages and encrypting/decrypting data.

## API Reference

Note

To use the TEE Secure Storage APIs in your project, ensure that the tee_sec_storage component is listed as a local dependency in the component manager manifest file idf_component.yml. Refer to the tee_secure_storage example for guidance.

### Header File

- components/esp_tee/subproject/components/tee_sec_storage/include/esp_tee_sec_storage.h

### Functions

esp_err_t esp_tee_sec_storage_clear_key(const char *key_id)

  Clear a key from secure storage.

  Parameters:

    key_id -- Key identifier string

  Returns:

    esp_err_t ESP_OK on success, appropriate error code otherwise.

esp_err_t esp_tee_sec_storage_gen_key(const esp_tee_sec_storage_key_cfg_t *cfg)

  Generate a unique key and store it in the secure storage.

  Parameters:

    cfg -- Pointer to the key configuration

  Returns:

    esp_err_t ESP_OK on success, appropriate error code otherwise.

esp_err_t esp_tee_sec_storage_ecdsa_sign(const esp_tee_sec_storage_key_cfg_t *cfg, const uint8_t *hash, size_t hlen, esp_tee_sec_storage_ecdsa_sign_t *out_sign)

  Generate and return the signature for the specified message digest using the key pair located in the secure storage.

  Parameters:

    - cfg -- [in] Pointer to the key configuration

    - hash -- [in] Message digest

    - hlen -- [in] Digest length

    - out_sign -- [out] Output context holding the signature

  Returns:

    esp_err_t ESP_OK on success, appropriate error code otherwise.

esp_err_t esp_tee_sec_storage_ecdsa_get_pubkey(const esp_tee_sec_storage_key_cfg_t *cfg, esp_tee_sec_storage_ecdsa_pubkey_t *out_pubkey)

  Return the public key from secure storage.

  Parameters:

    - cfg -- [in] Pointer to the key configuration

    - out_pubkey -- [out] Output context holding the public key

  Returns:

    esp_err_t ESP_OK on success, appropriate error code otherwise.

esp_err_t esp_tee_sec_storage_aead_encrypt(const esp_tee_sec_storage_aead_ctx_t *ctx, uint8_t *iv, size_t iv_len, uint8_t *tag, size_t tag_len, uint8_t *output)

  Perform encryption using AES256-GCM with the key from secure storage.

  Note

  Non-standard `iv_len` / `tag_len` values are rejected with ESP_ERR_INVALID_SIZE.

  Parameters:

    - ctx -- [in] Pointer to the AEAD operation context

    - iv -- [out] Pointer to the output buffer for the generated initialization vector

    - iv_len -- [in] Length of the initialization vector buffer; must be exactly 12 bytes (96-bit IV, per NIST SP 800-38D)

    - tag -- [out] Pointer to the authentication tag buffer

    - tag_len -- [in] Length of the authentication tag; must be 12 to 16 bytes (96- to 128-bit tag, per NIST SP 800-38D)

    - output -- [out] Pointer to the output data buffer

  Returns:

    esp_err_t ESP_OK on success, appropriate error code otherwise.

esp_err_t esp_tee_sec_storage_aead_decrypt(const esp_tee_sec_storage_aead_ctx_t *ctx, const uint8_t *iv, size_t iv_len, const uint8_t *tag, size_t tag_len, uint8_t *output)

  Perform decryption using AES256-GCM with the key from secure storage.

  Note

  Non-standard `iv_len` / `tag_len` values are rejected with ESP_ERR_INVALID_SIZE.

  Parameters:

    - ctx -- [in] Pointer to the AEAD operation context

    - iv -- [in] Pointer to the initialization vector used during encryption

    - iv_len -- [in] Length of the initialization vector; must be exactly 12 bytes (96-bit IV, per NIST SP 800-38D)

    - tag -- [in] Pointer to the authentication tag buffer

    - tag_len -- [in] Length of the authentication tag; must be 12 to 16 bytes (96- to 128-bit tag, per NIST SP 800-38D)

    - output -- [out] Pointer to the output data buffer

  Returns:

    esp_err_t ESP_OK on success, appropriate error code otherwise.

esp_err_t esp_tee_sec_storage_ecdsa_sign_pbkdf2(const esp_tee_sec_storage_pbkdf2_ctx_t *ctx, const uint8_t *hash, size_t hlen, esp_tee_sec_storage_ecdsa_sign_t *out_sign, esp_tee_sec_storage_ecdsa_pubkey_t *out_pubkey)

  Generate and return the signature for the specified message digest using the key pair derived via PBKDF2-HMAC-SHA256 using the HMAC peripheral with the given salt and the configured HMAC eFuse key ID (`CONFIG_SECURE_TEE_PBKDF2_EFUSE_HMAC_KEY_ID`).

  Parameters:

    - ctx -- [in] Pointer to the PBKDF2 context

    - hash -- [in] Message digest

    - hlen -- [in] Digest length

    - out_sign -- [out] Output context holding the signature

    - out_pubkey -- [out] Output context holding the public key

  Returns:

    esp_err_t ESP_OK on success, appropriate error code otherwise.

### Structures

struct esp_tee_sec_storage_key_cfg_t

  Configuration structure for key generation/import.

  Public Members

  const char *id

    Unique identifier for the key

  esp_tee_sec_storage_type_t type

    Type of key (AES256, ECDSA_SECP256R1, etc.)

  uint32_t flags

    Key flags (e.g. WRITE_ONCE)

struct esp_tee_sec_storage_aead_ctx_t

  Context structure for AES-GCM AEAD encryption/decryption operations.

  Public Members

  const char *key_id

    Identifier of the key to use

  const uint8_t *aad

    Additional authenticated data

  size_t aad_len

    Length of additional authenticated data

  const uint8_t *input

    Input data buffer

  size_t input_len

    Length of input data

struct esp_tee_sec_storage_pbkdf2_ctx_t

  Context structure for ECDSA signing with PBKDF2 (HMAC) derived key.

  Public Members

  const uint8_t *salt

    Salt for PBKDF2

  size_t salt_len

    Length of the salt

  esp_tee_sec_storage_type_t key_type

    Key type to be generated and used

struct esp_tee_sec_storage_ecdsa_pubkey_t

  Structure holding the X and Y components of the ECDSA public key.

  Public Members

  uint8_t pub_x[MAX_ECDSA_SUPPORTED_KEY_LEN]

    X component

  uint8_t pub_y[MAX_ECDSA_SUPPORTED_KEY_LEN]

    Y component

struct esp_tee_sec_storage_ecdsa_sign_t

  Structure holding the R and S components of the ECDSA signature.

  Public Members

  uint8_t signature[MAX_ECDSA_SUPPORTED_KEY_LEN * 2]

    Signature

### Macros

MAX_ECDSA_SUPPORTED_KEY_LEN

  Maximum supported size for the ECDSA key (SECP256R1)

MAX_AES_SUPPORTED_KEY_LEN

  Maximum supported size for the AES key

SEC_STORAGE_FLAG_NONE

  No flags

SEC_STORAGE_FLAG_WRITE_ONCE

  Data can only be written once

SEC_STORAGE_FLAG_TEE_ONLY

  Key is owned exclusively by the TEE

### Enumerations

enum esp_tee_sec_storage_type_t

  Enum to represent the type of key stored in the secure storage.

  Values:

  enumerator ESP_SEC_STG_KEY_AES256
  enumerator ESP_SEC_STG_KEY_ECDSA_SECP256R1
  enumerator ESP_SEC_STG_TYPE_MAX

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.