Files
esp-idf/examples/security/key_manager/main/key_mgr_sign_main.c
T

544 lines
19 KiB
C

/*
* Key Manager Signing Example
*
* Demonstrates ECDSA and RSA-DS signing operations using keys deployed
* via the Key Manager peripheral on Key Manager supported targets
*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "esp_log.h"
#include "esp_err.h"
#include "nvs_flash.h"
#include "soc/soc_caps.h"
#include "psa/crypto.h"
#if SOC_KEY_MANAGER_SUPPORTED
#include "esp_key_mgr.h"
#endif
#if SOC_ECDSA_SUPPORTED
#include "psa_crypto_driver_esp_ecdsa.h"
#endif
#if SOC_DIG_SIGN_SUPPORTED
#include "psa_crypto_driver_esp_rsa_ds.h"
#endif
#define HASH_LEN 32
#define ECDSA_COMPONENT_LEN 32
#define ECDSA_UNCOMPRESSED_POINT_FORMAT 0x04
/*
* Test data generated by generate_test_data.py (seed: 12345)
* See README.md for instructions on regenerating this data.
*/
#include "test_data.h"
#if CONFIG_EXAMPLE_KEY_MGR_RSA_DS_SIGN
/* Use target-specific RSA key size from SOC capabilities */
#define RSA_KEY_BITS SOC_RSA_MAX_BIT_LEN
#define RSA_KEY_BYTES (RSA_KEY_BITS / 8)
/* Convert RSA key bits to esp_digital_signature_length_t enum value */
#define RSA_DS_LENGTH ((RSA_KEY_BITS / 32) - 1)
#endif /* CONFIG_EXAMPLE_KEY_MGR_RSA_DS_SIGN */
static const char *TAG = "key_mgr_sign";
#define NVS_KEY_MGR_NAMESPACE "key_mgr"
#if SOC_KEY_MANAGER_SUPPORTED
/**
* @brief Get NVS key string for a given key type (for "key_type" -> "key_recovery_info" storage)
*/
static const char *key_type_to_nvs_key(esp_key_mgr_key_type_t key_type)
{
switch (key_type) {
case ESP_KEY_MGR_ECDSA_KEY:
return "ecdsa";
case ESP_KEY_MGR_DS_KEY:
return "ds";
default:
return "unknown";
}
}
/**
* @brief Load key recovery info from NVS if present
*
* @param key_type Key type (used as NVS key name)
* @param key_recovery_info Output buffer to fill (must be at least sizeof(esp_key_mgr_key_recovery_info_t))
* @return ESP_OK if loaded, ESP_ERR_NVS_NOT_FOUND if no key stored, or other error
*/
static esp_err_t load_key_recovery_info_from_nvs(esp_key_mgr_key_type_t key_type,
esp_key_mgr_key_recovery_info_t *key_recovery_info)
{
nvs_handle_t handle;
esp_err_t err = nvs_open(NVS_KEY_MGR_NAMESPACE, NVS_READONLY, &handle);
if (err != ESP_OK) {
/* ESP_ERR_NVS_NOT_FOUND = namespace "key_mgr" does not exist yet (e.g. after erase_flash) */
if (err != ESP_ERR_NVS_NOT_FOUND) {
ESP_LOGE(TAG, "Failed to open NVS namespace: 0x%x", err);
}
return err;
}
/* Get the blob size first */
size_t required_size = 0;
err = nvs_get_blob(handle, key_type_to_nvs_key(key_type), NULL, &required_size);
if (err == ESP_ERR_NVS_NOT_FOUND) {
nvs_close(handle);
return ESP_ERR_NVS_NOT_FOUND;
}
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get key_recovery_info size from NVS: 0x%x", err);
nvs_close(handle);
return err;
}
if (required_size != sizeof(esp_key_mgr_key_recovery_info_t)) {
ESP_LOGE(TAG, "Invalid key_recovery_info size in NVS (expected %zu, got %zu)",
sizeof(esp_key_mgr_key_recovery_info_t), required_size);
nvs_close(handle);
return ESP_ERR_INVALID_SIZE;
}
err = nvs_get_blob(handle, key_type_to_nvs_key(key_type), key_recovery_info, &required_size);
nvs_close(handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to read key_recovery_info from NVS: 0x%x", err);
return err;
}
return ESP_OK;
}
/**
* @brief Store key recovery info in NVS under key "key_type" -> blob
*
* @param key_type Key type (used as NVS key name)
* @param key_recovery_info Recovery info to store
* @return ESP_OK on success
*/
static esp_err_t save_key_recovery_info_to_nvs(esp_key_mgr_key_type_t key_type,
const esp_key_mgr_key_recovery_info_t *key_recovery_info)
{
nvs_handle_t handle;
esp_err_t err = nvs_open(NVS_KEY_MGR_NAMESPACE, NVS_READWRITE, &handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to open NVS namespace: 0x%x", err);
return err;
}
err = nvs_set_blob(handle, key_type_to_nvs_key(key_type), key_recovery_info,
sizeof(esp_key_mgr_key_recovery_info_t));
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to write key_recovery_info to NVS: 0x%x", err);
nvs_close(handle);
return err;
}
err = nvs_commit(handle);
nvs_close(handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to commit NVS: 0x%x", err);
return err;
}
ESP_LOGI(TAG, "Stored key_recovery_info in NVS for key_type \"%s\"", key_type_to_nvs_key(key_type));
return ESP_OK;
}
/**
* @brief Deploy a key using Key Manager in AES mode
*
* @param k1_encrypted Pointer to the encrypted K1 key material
* @param k1_encrypted_len Length of k1_encrypted (must be >= KEY_MGR_K1_ENCRYPTED_SIZE)
* @param key_type Type of key to deploy (ECDSA, DS, etc.)
* @param key_len Length of the key (for ECDSA keys)
* @param key_recovery_info Output: Key recovery information for later use
* @return esp_err_t ESP_OK on success
*/
static esp_err_t deploy_key_in_key_manager(const uint8_t *k1_encrypted,
size_t k1_encrypted_len,
esp_key_mgr_key_type_t key_type,
esp_key_mgr_key_len_t key_len,
esp_key_mgr_key_recovery_info_t *key_recovery_info)
{
esp_key_mgr_aes_key_config_t *key_config = NULL;
esp_err_t ret = ESP_FAIL;
/* Verify sizes before memcpy to avoid buffer overflows */
if (sizeof(k2_info) < KEY_MGR_K2_INFO_SIZE) {
ESP_LOGE(TAG, "k2_info size mismatch (need %d, have %u)", KEY_MGR_K2_INFO_SIZE, sizeof(k2_info));
return ESP_ERR_INVALID_SIZE;
}
if (k1_encrypted_len < KEY_MGR_K1_ENCRYPTED_SIZE) {
ESP_LOGE(TAG, "k1_encrypted size too small (need %d, have %u)", KEY_MGR_K1_ENCRYPTED_SIZE, k1_encrypted_len);
return ESP_ERR_INVALID_SIZE;
}
if (sizeof(init_key) < KEY_MGR_SW_INIT_KEY_SIZE) {
ESP_LOGE(TAG, "init_key size mismatch (need %d, have %u)", KEY_MGR_SW_INIT_KEY_SIZE, sizeof(init_key));
return ESP_ERR_INVALID_SIZE;
}
key_config = calloc(1, sizeof(esp_key_mgr_aes_key_config_t));
if (key_config == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory for key config");
return ESP_ERR_NO_MEM;
}
key_config->key_type = key_type;
key_config->key_len = key_len;
key_config->use_pre_generated_sw_init_key = 1;
memcpy(key_config->k2_info, (uint8_t *)k2_info, KEY_MGR_K2_INFO_SIZE);
memcpy(key_config->k1_encrypted[0], (uint8_t *)k1_encrypted, KEY_MGR_K1_ENCRYPTED_SIZE);
memcpy(key_config->sw_init_key, (uint8_t *)init_key, KEY_MGR_SW_INIT_KEY_SIZE);
ret = esp_key_mgr_deploy_key_in_aes_mode(key_config, key_recovery_info);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to deploy key in Key Manager: 0x%x", ret);
} else {
ESP_LOGI(TAG, "Key deployed successfully in Key Manager");
}
free(key_config);
return ret;
}
#if CONFIG_EXAMPLE_KEY_MGR_ECDSA_SIGN
/**
* @brief Demonstrate ECDSA signing using Key Manager deployed key
*
* This function:
* 1. Deploys an ECDSA key using Key Manager
* 2. Creates a PSA opaque key reference
* 3. Signs a hash using psa_sign_hash()
* 4. Verifies the signature using the known ECDSA public key
*/
static void example_ecdsa_sign_with_key_manager(void)
{
ESP_LOGI(TAG, "ECDSA Signing with Key Manager");
esp_err_t ret;
psa_status_t status;
/* Allocate key recovery info */
esp_key_mgr_key_recovery_info_t *key_recovery_info = NULL;
key_recovery_info = calloc(1, sizeof(esp_key_mgr_key_recovery_info_t));
if (key_recovery_info == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory for key recovery info");
return;
}
/* Step 1: Use existing key from NVS or deploy new ECDSA key and store in NVS */
ret = load_key_recovery_info_from_nvs(ESP_KEY_MGR_ECDSA_KEY, key_recovery_info);
if (ret == ESP_ERR_NVS_NOT_FOUND) {
ESP_LOGI(TAG, "Step 1: No ECDSA key in NVS, deploying new key via Key Manager...");
ret = deploy_key_in_key_manager(k1_ecdsa256_encrypt, sizeof(k1_ecdsa256_encrypt), ESP_KEY_MGR_ECDSA_KEY,
ESP_KEY_MGR_ECDSA_LEN_256, key_recovery_info);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to deploy ECDSA key");
goto cleanup;
}
ret = save_key_recovery_info_to_nvs(ESP_KEY_MGR_ECDSA_KEY, key_recovery_info);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to store key_recovery_info in NVS");
goto cleanup;
}
} else if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to load key_recovery_info from NVS: 0x%x", ret);
goto cleanup;
} else {
ESP_LOGI(TAG, "Step 1: Using existing ECDSA key from NVS");
}
/* Step 2: Create PSA opaque key reference */
ESP_LOGI(TAG, "Step 2: Creating PSA opaque key reference...");
esp_ecdsa_opaque_key_t opaque_key = {
.curve = ESP_ECDSA_CURVE_SECP256R1,
.key_recovery_info = key_recovery_info,
};
psa_key_id_t priv_key_id = 0;
psa_key_attributes_t priv_attr = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_type(&priv_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&priv_attr, 256);
psa_set_key_usage_flags(&priv_attr, PSA_KEY_USAGE_SIGN_HASH);
psa_set_key_algorithm(&priv_attr, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
psa_set_key_lifetime(&priv_attr, PSA_KEY_LIFETIME_ESP_ECDSA_VOLATILE);
status = psa_import_key(&priv_attr, (uint8_t *)&opaque_key, sizeof(opaque_key), &priv_key_id);
psa_reset_key_attributes(&priv_attr);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "Failed to import opaque key: %d", (int)status);
goto cleanup;
}
/* Step 3: Sign the hash */
ESP_LOGI(TAG, "Step 3: Signing hash using Key Manager key...");
uint8_t signature[2 * ECDSA_COMPONENT_LEN];
size_t signature_len = 0;
status = psa_sign_hash(priv_key_id,
PSA_ALG_ECDSA(PSA_ALG_SHA_256),
sha256_hash, HASH_LEN,
signature, sizeof(signature),
&signature_len);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "Failed to sign hash: %d", (int)status);
psa_destroy_key(priv_key_id);
goto cleanup;
}
ESP_LOGI(TAG, "Signature generated successfully (%" PRIu32 " bytes)", (uint32_t)signature_len);
ESP_LOG_BUFFER_HEX_LEVEL(TAG, signature, signature_len, ESP_LOG_DEBUG);
/* Cleanup opaque key */
psa_destroy_key(priv_key_id);
/* Step 4: Verify the signature using the known ECDSA public key */
ESP_LOGI(TAG, "Step 4: Verifying signature with known public key...");
psa_key_id_t pub_key_id = 0;
psa_key_attributes_t pub_key_attr = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_type(&pub_key_attr, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_usage_flags(&pub_key_attr, PSA_KEY_USAGE_VERIFY_HASH);
psa_set_key_algorithm(&pub_key_attr, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
psa_set_key_lifetime(&pub_key_attr, PSA_KEY_LIFETIME_VOLATILE);
status = psa_import_key(&pub_key_attr, ecdsa_public_key, sizeof(ecdsa_public_key), &pub_key_id);
psa_reset_key_attributes(&pub_key_attr);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "Failed to import ECDSA public key: %d", (int)status);
goto cleanup;
}
status = psa_verify_hash(pub_key_id, PSA_ALG_ECDSA(PSA_ALG_SHA_256),
sha256_hash, HASH_LEN, signature, signature_len);
psa_destroy_key(pub_key_id);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "ECDSA signature verification failed: %d", (int)status);
goto cleanup;
}
ESP_LOGI(TAG, "ECDSA signature verification PASSED!");
cleanup:
free(key_recovery_info);
ESP_LOGI(TAG, "ECDSA Signing Example Complete\n");
}
#endif /* CONFIG_EXAMPLE_KEY_MGR_ECDSA_SIGN */
#if CONFIG_EXAMPLE_KEY_MGR_RSA_DS_SIGN
/**
* @brief Demonstrate RSA-DS signing using Key Manager deployed key
*
* This function:
* 1. Deploys a DS (HMAC) key using Key Manager
* 2. Prepares DS data context
* 3. Creates a PSA opaque key reference for RSA-DS
* 4. Signs a hash using psa_sign_hash()
* 5. Verifies the signature using the known RSA public key
*/
static void example_rsa_ds_sign_with_key_manager(void)
{
ESP_LOGI(TAG, "RSA-DS Signing with Key Manager");
esp_err_t ret;
psa_status_t status;
/* Allocate key recovery info */
esp_key_mgr_key_recovery_info_t *key_recovery_info = NULL;
key_recovery_info = calloc(1, sizeof(esp_key_mgr_key_recovery_info_t));
if (key_recovery_info == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory for key recovery info");
return;
}
/* Step 1: Use existing key from NVS or deploy new DS key and store in NVS */
ret = load_key_recovery_info_from_nvs(ESP_KEY_MGR_DS_KEY, key_recovery_info);
if (ret == ESP_ERR_NVS_NOT_FOUND) {
ESP_LOGI(TAG, "Step 1: No DS key in NVS, deploying new key via Key Manager...");
ret = deploy_key_in_key_manager(k1_ds_aes_key_encrypt, sizeof(k1_ds_aes_key_encrypt), ESP_KEY_MGR_DS_KEY, 0, key_recovery_info);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to deploy DS key");
goto cleanup;
}
ret = save_key_recovery_info_to_nvs(ESP_KEY_MGR_DS_KEY, key_recovery_info);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to store key_recovery_info in NVS");
goto cleanup;
}
} else if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to load key_recovery_info from NVS: 0x%x", ret);
goto cleanup;
} else {
ESP_LOGI(TAG, "Step 1: Using existing DS key from NVS");
}
/* Step 2: Prepare DS data context */
ESP_LOGI(TAG, "Step 2: Preparing DS data context...");
esp_ds_data_t *ds_data = calloc(1, sizeof(esp_ds_data_t));
if (ds_data == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory for DS data");
goto cleanup;
}
ds_data->rsa_length = RSA_DS_LENGTH;
memcpy(ds_data->iv, ds_iv, ESP_DS_IV_LEN);
memcpy(ds_data->c, ds_c, ESP_DS_C_LEN);
esp_ds_data_ctx_t ds_data_ctx = {
.esp_ds_data = ds_data,
.rsa_length_bits = RSA_KEY_BITS,
};
esp_rsa_ds_opaque_key_t rsa_ds_opaque_key = {
.ds_data_ctx = &ds_data_ctx,
.key_recovery_info = key_recovery_info,
};
/* Step 3: Create PSA opaque key reference for RSA-DS */
ESP_LOGI(TAG, "Step 3: Creating PSA opaque key reference for RSA-DS...");
psa_key_id_t ds_key_id = 0;
psa_key_attributes_t ds_key_attr = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_type(&ds_key_attr, PSA_KEY_TYPE_RSA_KEY_PAIR);
psa_set_key_bits(&ds_key_attr, RSA_KEY_BITS);
psa_set_key_usage_flags(&ds_key_attr, PSA_KEY_USAGE_SIGN_HASH);
psa_set_key_algorithm(&ds_key_attr, PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256));
psa_set_key_lifetime(&ds_key_attr, PSA_KEY_LIFETIME_ESP_RSA_DS_VOLATILE);
status = psa_import_key(&ds_key_attr,
(const uint8_t *)&rsa_ds_opaque_key,
sizeof(rsa_ds_opaque_key),
&ds_key_id);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "Failed to import RSA-DS opaque key: %d", (int)status);
free(ds_data);
goto cleanup;
}
/* Step 4: Sign the hash using RSA-DS */
ESP_LOGI(TAG, "Step 4: Signing hash using RSA-DS with Key Manager key...");
uint8_t *rsa_signature = calloc(1, RSA_KEY_BYTES);
if (rsa_signature == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory for RSA signature");
psa_destroy_key(ds_key_id);
free(ds_data);
goto cleanup;
}
size_t rsa_signature_len = 0;
status = psa_sign_hash(ds_key_id,
PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256),
sha256_hash, HASH_LEN,
rsa_signature, RSA_KEY_BYTES,
&rsa_signature_len);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "Failed to sign hash with RSA-DS: %d", (int)status);
psa_destroy_key(ds_key_id);
psa_reset_key_attributes(&ds_key_attr);
free(rsa_signature);
free(ds_data);
goto cleanup;
}
ESP_LOGI(TAG, "RSA-DS signature generated successfully (%" PRIu32 " bytes)", (uint32_t)rsa_signature_len);
ESP_LOG_BUFFER_HEX_LEVEL(TAG, rsa_signature, rsa_signature_len, ESP_LOG_DEBUG);
/* Cleanup DS opaque key */
psa_destroy_key(ds_key_id);
psa_reset_key_attributes(&ds_key_attr);
free(ds_data);
/* Step 5: Verify the signature using the known RSA public key */
ESP_LOGI(TAG, "Step 5: Verifying RSA-DS signature with known public key...");
psa_key_id_t rsa_pub_key_id = 0;
psa_key_attributes_t rsa_pub_key_attr = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_type(&rsa_pub_key_attr, PSA_KEY_TYPE_RSA_PUBLIC_KEY);
psa_set_key_bits(&rsa_pub_key_attr, RSA_KEY_BITS);
psa_set_key_usage_flags(&rsa_pub_key_attr, PSA_KEY_USAGE_VERIFY_HASH);
psa_set_key_algorithm(&rsa_pub_key_attr, PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256));
psa_set_key_lifetime(&rsa_pub_key_attr, PSA_KEY_LIFETIME_VOLATILE);
status = psa_import_key(&rsa_pub_key_attr, rsa_public_key_der, sizeof(rsa_public_key_der), &rsa_pub_key_id);
psa_reset_key_attributes(&rsa_pub_key_attr);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "Failed to import RSA public key: %d", (int)status);
free(rsa_signature);
goto cleanup;
}
status = psa_verify_hash(rsa_pub_key_id, PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256),
sha256_hash, HASH_LEN, rsa_signature, rsa_signature_len);
psa_destroy_key(rsa_pub_key_id);
free(rsa_signature);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "RSA-DS signature verification failed: %d", (int)status);
goto cleanup;
}
ESP_LOGI(TAG, "RSA-DS signature verification PASSED!");
cleanup:
free(key_recovery_info);
ESP_LOGI(TAG, "RSA-DS Signing Example Complete\n");
}
#endif /* CONFIG_EXAMPLE_KEY_MGR_RSA_DS_SIGN */
#endif /* SOC_KEY_MANAGER_SUPPORTED */
void app_main(void)
{
ESP_LOGI(TAG, "Key Manager Signing Example");
#if !SOC_KEY_MANAGER_SUPPORTED
ESP_LOGE(TAG, "Key Manager is not supported on this chip!");
return;
#endif /* SOC_KEY_MANAGER_SUPPORTED */
/* Initialize NVS (required for storing key_recovery_info) */
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
#if CONFIG_EXAMPLE_KEY_MGR_ECDSA_SIGN
example_ecdsa_sign_with_key_manager();
#endif
#if CONFIG_EXAMPLE_KEY_MGR_RSA_DS_SIGN
example_rsa_ds_sign_with_key_manager();
#endif
ESP_LOGI(TAG, "Key Manager Signing Example finished.");
}