SM2 数字签名 (SM2)

[English]

SM2 是中国国家标准 GB/T 32918 定义的椭圆曲线公钥算法。ESP32-S31 的 ECC 加速器支持 SM2 推荐曲线(GB/T 32918.5)。esp_sm2 API 使用该硬件派生公钥、生成签名和验证签名。

所有运算均在 ECC 加速器上完成,API 不使用软件大数库。涉及私钥或随机数的点乘运算以恒定时间模式执行。

该 API 中的所有缓冲区均为 32 字节(ESP_SM2_PARAM_LEN),采用大端字节序。

摘要输入

该 API 签名和验证的对象是 32 字节摘要,而非消息本身。GB/T 32918.2 将摘要定义为 e = SM3(ZA || M),其中 M 为消息,ZA 为签名者身份标识与曲线参数的 SM3 杂凑值。这两个杂凑值均可使用 esp_sm3 API 计算。调用者必须先计算摘要,再调用该 API。

生成密钥对

私钥是范围在 [1, n-2] 内的 32 字节随机值 d,其中 n 为曲线的阶。反复生成随机候选值,直到 API 接受为止:

#include "esp_sm2.h"
#include "esp_random.h"

uint8_t priv_key[ESP_SM2_PARAM_LEN];
uint8_t pub_x[ESP_SM2_PARAM_LEN];
uint8_t pub_y[ESP_SM2_PARAM_LEN];
esp_err_t err;

do {
    esp_fill_random(priv_key, sizeof(priv_key));
    err = esp_sm2_get_pubkey(priv_key, pub_x, pub_y);
} while (err == ESP_ERR_INVALID_ARG);
ESP_ERROR_CHECK(err);

私钥由调用者负责保管。请将私钥存放在安全位置,并擦除所有不再需要的副本。

对摘要签名

uint8_t digest[ESP_SM2_PARAM_LEN];   /* e = SM3(ZA || M),由调用者计算 */
uint8_t sig_r[ESP_SM2_PARAM_LEN];
uint8_t sig_s[ESP_SM2_PARAM_LEN];

ESP_ERROR_CHECK(esp_sm2_sign(digest, priv_key, sig_r, sig_s));

esp_sm2_sign() 在输入格式错误时返回 ESP_ERR_INVALID_ARG,在用尽全部重试次数时返回 ESP_FAIL。只有随机数发生器故障才会耗尽重试次数。

警告

签名随机数来自 esp_fill_random()。签名前请确保真随机熵源处于激活状态。带偏差的随机数会使攻击者能够从签名中计算出私钥。关于随机数发生器产生真随机数的条件,请参阅 随机数发生器。

验证签名

esp_err_t res = esp_sm2_verify(digest, pub_x, pub_y, sig_r, sig_s);
if (res == ESP_OK) {
    /* 签名有效。 */
}

esp_sm2_verify() 在使用公钥前,先通过硬件验证该公钥位于曲线上。仅当签名有效时返回 ESP_OK;输入格式错误时返回 ESP_ERR_INVALID_ARG;签名或公钥无效时返回 ESP_FAIL。

可用性

DIS_SM_CRYPT eFuse 会永久禁用 SM2 和 SM3 硬件。烧写该 eFuse 后,该 API 中的函数返回 ESP_ERR_NOT_SUPPORTED。无效参数仍会返回 ESP_ERR_INVALID_ARG。

应用示例

测试应用 esp_security/test_apps/crypto_drivers/main/test_esp_sm2.c 演示了完整流程:基于已知答案向量的密钥派生、签名、篡改检测,以及基于生成向量的签名验证。

API 参考

Header File

  • components/esp_security/include/esp_sm2.h

  • This header file can be included with:

    #include "esp_sm2.h"
    
  • This header file is a part of the API provided by the esp_security component. To declare that your component depends on esp_security, add the following to your CMakeLists.txt:

    REQUIRES esp_security
    

    or

    PRIV_REQUIRES esp_security
    

Functions

esp_err_t esp_sm2_get_pubkey(const uint8_t priv_key[ESP_SM2_PARAM_LEN], uint8_t pub_x[ESP_SM2_PARAM_LEN], uint8_t pub_y[ESP_SM2_PARAM_LEN])

Derive the SM2 public key from a private key.

All buffers are big-endian and ESP_SM2_PARAM_LEN bytes long.

参数:
  • priv_key -- Private key d. Must satisfy 1 <= d <= n-2.

  • pub_x -- Output: public key X coordinate.

  • pub_y -- Output: public key Y coordinate.

返回:

ESP_OK on success. ESP_ERR_INVALID_ARG on a NULL pointer or an out-of-range key. ESP_ERR_NOT_SUPPORTED when the DIS_SM_CRYPT eFuse disables SM crypto. ESP_FAIL is reserved for a hardware error. The ECC peripheral cannot report one for this operation, so this function does not return ESP_FAIL today.

esp_err_t esp_sm2_sign(const uint8_t digest[ESP_SM2_PARAM_LEN], const uint8_t priv_key[ESP_SM2_PARAM_LEN], uint8_t sig_r[ESP_SM2_PARAM_LEN], uint8_t sig_s[ESP_SM2_PARAM_LEN])

Generate an SM2 signature (GB/T 32918.2) over a digest.

The caller computes e = SM3(ZA || M). All buffers are big-endian and ESP_SM2_PARAM_LEN bytes long.

备注

This function gets the signature nonce from esp_fill_random(). Make sure that a true entropy source is available before you sign. Refer to the ESP-IDF documentation about random number generation. A biased nonce lets an attacker calculate the private key.

参数:
  • digest -- Digest e to sign.

  • priv_key -- Private key d. Must satisfy 1 <= d <= n-2.

  • sig_r -- Output: signature component r.

  • sig_s -- Output: signature component s.

返回:

ESP_OK on success. ESP_ERR_INVALID_ARG on a NULL pointer or an out-of-range key. ESP_ERR_NOT_SUPPORTED when the DIS_SM_CRYPT eFuse disables SM crypto. ESP_FAIL when the function used all of its retries. Only a failed random number generator causes this.

esp_err_t esp_sm2_verify(const uint8_t digest[ESP_SM2_PARAM_LEN], const uint8_t pub_x[ESP_SM2_PARAM_LEN], const uint8_t pub_y[ESP_SM2_PARAM_LEN], const uint8_t sig_r[ESP_SM2_PARAM_LEN], const uint8_t sig_s[ESP_SM2_PARAM_LEN])

Verify an SM2 signature (GB/T 32918.2) over a digest.

The caller computes e = SM3(ZA || M). All buffers are big-endian and ESP_SM2_PARAM_LEN bytes long. The public key is validated on-curve in hardware before use.

参数:
  • digest -- Digest e that was signed.

  • pub_x -- Public key X coordinate.

  • pub_y -- Public key Y coordinate.

  • sig_r -- Signature component r. Must satisfy 1 <= r <= n-1.

  • sig_s -- Signature component s. Must satisfy 1 <= s <= n-1.

返回:

ESP_OK when the signature is valid. ESP_ERR_INVALID_ARG on a NULL pointer or an out-of-range r or s. ESP_ERR_NOT_SUPPORTED when the DIS_SM_CRYPT eFuse disables SM crypto. ESP_FAIL when the signature or the public key is invalid.

Macros

ESP_SM2_PARAM_LEN

Length in bytes of every SM2 parameter (coordinate, scalar, digest).


此文档对您有帮助吗?