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
This header file can be included with:
#include "esp_sm2.h"
This header file is a part of the API provided by the
esp_securitycomponent. To declare that your component depends onesp_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).