SM2 Digital Signatures (SM2)

[中文]

SM2 is an elliptic curve public-key algorithm defined in the Chinese national standard GB/T 32918. The ESP32-S31 ECC accelerator supports the SM2 recommended curve (GB/T 32918.5). The esp_sm2 API uses this hardware to derive public keys, to generate signatures, and to verify signatures.

All operations run on the ECC accelerator. The API does not use a software big-number library. The point multiplication that touches the private key or the nonce runs in constant-time mode.

Every buffer in this API is 32 bytes long (ESP_SM2_PARAM_LEN) and big-endian.

Digest Input

The API signs and verifies a 32-byte digest, not a message. GB/T 32918.2 defines the digest as e = SM3(ZA || M), where M is the message and ZA is the SM3 hash of the signer identity and the curve parameters. Use the esp_sm3 API to compute both hashes. The caller must compute the digest before it calls this API.

Generate a Key Pair

A private key is a random 32-byte value d in the range [1, n-2], where n is the order of the curve. Draw random candidates until the API accepts one:

#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);

The caller owns the private key. Store it in a secure location, and wipe every copy that is no longer necessary.

Sign a Digest

uint8_t digest[ESP_SM2_PARAM_LEN];   /* e = SM3(ZA || M), computed by the caller */
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() returns ESP_ERR_INVALID_ARG for a malformed input, and ESP_FAIL when it uses all of its retry attempts. Only a failing random number generator can exhaust the retries.

Warning

The signature nonce comes from esp_fill_random(). Make sure that a true entropy source is active before you sign. A biased nonce lets an attacker calculate the private key from signatures. See Random Number Generation for the conditions under which the random number generator produces true random numbers.

Verify a Signature

esp_err_t res = esp_sm2_verify(digest, pub_x, pub_y, sig_r, sig_s);
if (res == ESP_OK) {
    /* The signature is valid. */
}

esp_sm2_verify() validates the public key on the curve in hardware before it uses the key. It returns ESP_OK only for a valid signature. It returns ESP_ERR_INVALID_ARG for a malformed input, and ESP_FAIL for an invalid signature or an invalid public key.

Availability

The DIS_SM_CRYPT eFuse permanently disables the SM2 and SM3 hardware. When this eFuse is burned, the functions in this API return ESP_ERR_NOT_SUPPORTED. Invalid arguments are still reported as ESP_ERR_INVALID_ARG.

Application Example

The test application esp_security/test_apps/crypto_drivers/main/test_esp_sm2.c shows the complete flow: key derivation against a known-answer vector, signing, tamper detection, and verification with generated vectors.

API Reference

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.

Parameters:
  • 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.

Returns:

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.

Note

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.

Parameters:
  • 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.

Returns:

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.

Parameters:
  • 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.

Returns:

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).


Was this page helpful?