# 数字签名 (DS)

[English]

数字签名 (DS) 模块提供基于 RSA 的消息签名硬件加速，并使用预加密参数来计算签名。这些参数通过 HMAC 作为密钥派生函数进行加密，而 HMAC 则以 eFuse 作为输入密钥。

以上过程均在硬件中完成，因此在计算签名时，软件无法获取 RSA 参数的解密密钥，也无法获取 HMAC 密钥派生函数的输入密钥。

签名计算所涉及的硬件信息以及所用寄存器的有关信息，请参阅 ESP32-C3 技术参考手册 > 数字签名 (DS) [PDF]。

## 私钥参数

RSA 签名的私钥参数存储在 flash 中。为防止发生未经授权的访问，这些参数都进行了 AES 加密。此时，HMAC 模块被作为密钥派生函数，用于计算 AES 加密了的私钥参数。同时，HMAC 模块本身使用的来自 eFuse 密钥块的密钥也具有读取保护，可以防止发生未经授权的访问。

调用签名计算时，软件只需指定使用的 eFuse 密钥、相应的 eFuse 密钥用途、加密的 RSA 参数位置以及待签名的数据或信息。

## 密钥生成

在使用 DS 外设前，需首先创建并存储 HMAC 密钥和 RSA 私钥，此步骤可在 ESP32-C3 上通过软件完成，也可在主机上进行。在 ESP-IDF 中，可以使用 `esp_efuse_write_block()` 设置 HMAC 密钥，并使用 `esp_hmac_calculate()` 对 RSA 私钥参数进行加密。

计算并组装私钥参数的详细信息，请参阅 ESP32-C3 技术参考手册 > 数字签名 (DS) [PDF]。

## 在 ESP-IDF 中进行数字签名计算

在 ESP-IDF 中进行数字签名计算的工作流程，以及所用寄存器的有关信息，请参阅 ESP32-C3 技术参考手册 > 数字签名 (DS) [PDF]。

要进行数字签名计算，需要准备以下三个参数：

1. 用作 HMAC 密钥的 eFuse 密钥块 ID

2. 加密私钥参数的位置

3. 待签名的数据或信息

底层 API（原始 RSA）

签名计算需要一些时间，因此 ESP-IDF 提供了两种可用的 API：第一种是 `esp_ds_sign()`，调用此 API 后，程序会在计算完成前保持阻塞状态。如果在计算过程中，软件需要执行其他操作，则可以调用 `esp_ds_start_sign()`，用另一种方式启动签名计算，然后周期性地调用 `esp_ds_is_busy()`，检查计算是否已完成。一旦计算完成，即可调用 `esp_ds_finish_sign()` 来获取签名结果。

API 函数 `esp_ds_sign()` 和 `esp_ds_start_sign()` 在 DS 外设的帮助下计算原始 RSA 签名。该签名必须转换为合适的格式（例如 PKCS#1 v1.5 或 PSS），以用于 TLS 或其他协议。

备注

上述为 DS 的基本构件，其消息长度是固定的。为了对任意消息生成签名，通常会将实际消息的哈希值作为输入，并将其填充到所需长度。

PSA 密码学驱动程序

DS 外设也通过 PSA Crypto RSA DS 驱动程序 对外提供访问接口，因此可以使用标准 PSA API 执行签名（PKCS#1 v1.5 或 PSS）和 RSA 解密（PKCS#1 v1.5 或 OAEP）。在 `Component config` > `mbedTLS` 中启用 `CONFIG_MBEDTLS_HARDWARE_RSA_DS_PERIPHERAL`。要在 ESP-TLS 中配合使用 DS 外设（例如 TLS 客户端认证），请参阅 ESP-TLS 文档中的 ESP-TLS 的数字签名。

## TLS 连接所需的 DS 外设配置

在 ESP32-C3 芯片上使用 TLS 连接之前，必须先配置 DS 外设，具体步骤如下：

1. 随机生成一个 256 位的值，作为 `初始化向量` (IV)。

2. 随机生成一个 256 位的值，作为 `HMAC_KEY`。

3. 使用客户端私钥 (RAS) 和上述步骤生成的参数，计算加密的私钥参数。

4. 将 256 位的 `HMAC_KEY` 烧录到 eFuse 中，只支持 DS 外设读取。

更多细节，请参阅 ESP32-C3 技术参考手册 > 数字签名 (DS) [PDF]。

如果要以开发为目的配置 DS 外设，可以使用 esp-secure-cert-tool。

在完成 DS 外设配置后获得的加密私钥参数应保存在 flash 中。应用需要从 flash 读取 DS 数据（例如，通过 esp_secure_cert_mgr 组件提供的 API。详情请参阅 component/README）。关于在 ESP-TLS 中使用 DS 外设的方法，请参阅 ESP-TLS 的数字签名。

## 通过 PSA Crypto 使用 DS 外设

要在应用代码（不使用 ESP-TLS）中将 DS 外设用于签名或解密，请启用 `CONFIG_MBEDTLS_HARDWARE_RSA_DS_PERIPHERAL`。使用加密的密钥数据 (`esp_ds_data_t`)、eFuse 密钥块 ID 以及以 bit 为单位的 RSA 密钥长度填充 `esp_ds_data_ctx_t`。确保在创建密钥时（例如通过 `esp_ds_encrypt_params()` 或 DS 配置工具）设置 `esp_ds_data_t` 的 `rsa_length` 字段，然后将该上下文封装到 `esp_rsa_ds_opaque_key_t` 中，使用 `PSA_KEY_LIFETIME_ESP_RSA_DS_VOLATILE` 将其作为 PSA 不透明密钥导入，并调用 `psa_sign_hash()` 或 `psa_asymmetric_decrypt()`：

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

    // ds_ctx 指向 esp_ds_data_ctx_t（例如来自安全证书或 NVS）
    esp_ds_data_ctx_t *ds_ctx = ...;
    esp_rsa_ds_opaque_key_t rsa_ds_opaque_key = {
        .ds_data_ctx = ds_ctx,
    };

    psa_key_attributes_t attrs = PSA_KEY_ATTRIBUTES_INIT;
    psa_set_key_type(&attrs, PSA_KEY_TYPE_RSA_KEY_PAIR);
    psa_set_key_bits(&attrs, ds_ctx->rsa_length_bits);
    psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_HASH);
    psa_set_key_algorithm(&attrs, PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256));
    psa_set_key_lifetime(&attrs, PSA_KEY_LIFETIME_ESP_RSA_DS_VOLATILE);

    psa_key_id_t key_id;
    psa_status_t status = psa_import_key(&attrs,
                                         (const uint8_t *)&rsa_ds_opaque_key,
                                         sizeof(rsa_ds_opaque_key),
                                         &key_id);
    psa_reset_key_attributes(&attrs);
    if (status != PSA_SUCCESS) {
        // 处理错误
    }

    // 对哈希值进行签名（例如消息的 SHA-256）
    uint8_t hash[32] = { ... };
    uint8_t signature[256];
    size_t sig_len;
    status = psa_sign_hash(key_id, PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256),
                           hash, sizeof(hash), signature, sizeof(signature), &sig_len);

    psa_destroy_key(key_id);

### 持久化与易失性 RSA_DS PSA 密钥

驱动支持两种 PSA 密钥生命周期：

- `PSA_KEY_LIFETIME_ESP_RSA_DS_VOLATILE` （上述示例中使用）只在 PSA 密钥 槽中保存指向调用方提供的 `esp_ds_data_ctx_t` 以及密钥管理器恢复信息的 指针。被引用的缓冲区必须保持有效，直到调用 `psa_destroy_key()` 为止。这样可避免对大块数据（例如 `esp_ds_data_t`，约 1200–1600 字节，因芯片而异）进行深拷贝；当这些数据已经通过 `esp_secure_cert_mgr` 从 flash 中以 mmap 形式可用时，这一点尤其有用。

- `PSA_KEY_LIFETIME_ESP_RSA_DS` （持久化）在调用 `psa_import_key()` 时将加密的密钥数据深拷贝到 PSA 密钥槽中，并 由 PSA 与其他密钥属性一同持久化到 NVS。导入返回后，调用方即可释放原始 缓冲区；后续的 `psa_sign_hash()` 和 `psa_asymmetric_decrypt()` 调用会自动从 NVS 取回所需数据。 当应用希望密钥在重启后依然可用，且无需在每次启动时重新从外部存储 载入 `esp_ds_data_ctx_t` 时，应使用此生命周期。

## 使用 RSA_DS 外设进行 SSL 双向认证

此前位于 `examples/protocols/mqtt/ssl_ds` 目录下的 SSL 双向认证示例现已随独立的 espressif/mqtt 组件一同提供。请参照该组件文档获取 SSL DS 示例，并与 ESP-MQTT 一同构建。该示例仍使用 `mqtt_client` （由 ESP-MQTT 实现），通过双向认证 TLS 连接至 `test.mosquitto.org`，其中 TLS 通信层仍由 ESP-TLS 实现。

## API 参考

### Header File

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

- This header file can be included with:

  >     #include "psa_crypto_driver_esp_rsa_ds_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

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。