feat(esp_security): add ECDH1 deployment mode to Key Manager driver

This commit is contained in:
harshal.patil
2026-05-11 09:38:00 +05:30
parent 3e6273c9c4
commit 98bbe26aea
8 changed files with 583 additions and 39 deletions
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD # SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0 # SPDX-License-Identifier: Unlicense OR CC0-1.0
import hashlib import hashlib
import hmac import hmac
@@ -146,6 +146,39 @@ def generate_k1_G(k1_bytes: bytes) -> tuple:
return k1_G, k1_G return k1_G, k1_G
def compute_ecdh1_x(k1_be: bytes, k2_le: bytes) -> int:
"""Compute x(k1*k2*G) on NIST P-256 as an integer.
k1 is interpreted big-endian (matches the user's k1*G computation in
generate_k1_G); k2 is interpreted little-endian (the KM's convention
for k2 recovered from k2_info). The deployed-key byte string is the
big-endian encoding of the returned integer.
"""
k1_int = int.from_bytes(k1_be, byteorder='big')
k2_int = int.from_bytes(k2_le, byteorder='little')
generator = NIST256p.generator.to_affine()
point = (k1_int * k2_int) * generator
return int(point.x())
def generate_ecdsa_pub_from_scalar(scalar_int: int, curve_size_bits: int) -> tuple:
"""Compute (scalar * G).x, .y for the given P-N curve and return them
as little-endian bytes (the ECDSA peripheral's exported-pubkey order)."""
if curve_size_bits == 192:
curve = ec.SECP192R1()
elif curve_size_bits == 256:
curve = ec.SECP256R1()
elif curve_size_bits == 384:
curve = ec.SECP384R1()
else:
raise ValueError(f'Unsupported curve size: {curve_size_bits}')
private_key = ec.derive_private_key(scalar_int, curve)
pub_numbers = private_key.public_key().public_numbers()
pubx = pub_numbers.x.to_bytes(curve_size_bits // 8, byteorder='little')
puby = pub_numbers.y.to_bytes(curve_size_bits // 8, byteorder='little')
return pubx, puby
def generate_hmac_test_data(key: bytes) -> tuple: def generate_hmac_test_data(key: bytes) -> tuple:
hmac_message = ( hmac_message = (
b'Deleniti voluptas explicabo et assumenda. Sed et aliquid minus quis. ' b'Deleniti voluptas explicabo et assumenda. Sed et aliquid minus quis. '
@@ -265,6 +298,10 @@ def write_to_c_header(
ds_encrypted_input_params_3072: bytes, ds_encrypted_input_params_3072: bytes,
ds_result_3072: bytes, ds_result_3072: bytes,
ds_iv: bytes, ds_iv: bytes,
ecdh1_xts_test_data: list,
ecdh1_hmac_result: bytes,
ecdh1_p256_pubx: bytes,
ecdh1_p256_puby: bytes,
) -> None: ) -> None:
with open('key_manager_test_cases.h', 'w', encoding='utf-8') as file: with open('key_manager_test_cases.h', 'w', encoding='utf-8') as file:
header_content = f"""#include <stdint.h> header_content = f"""#include <stdint.h>
@@ -327,6 +364,21 @@ typedef struct test_data_ecdh0 {{
uint8_t k1_G[2][64]; uint8_t k1_G[2][64];
}} test_data_ecdh0_mode_t; }} test_data_ecdh0_mode_t;
// ECDH1 takes the AES-mode-style (init_key, k2_info) inputs plus the ECDH
// k1*G public point (sourced from test_data_ecdh0.k1_G to share the same
// k1 across ECDH0 and ECDH1 tests). k1_encrypted is unused, so it isn't in
// this struct.
typedef struct test_data_ecdh1 {{
uint8_t init_key[32];
uint8_t k2_info[64];
uint8_t plaintext_data[128];
union {{
test_xts_data_t xts_test_data[TEST_COUNT];
test_ecdsa_data_t ecdsa_test_data;
test_hmac_data_t hmac_test_data;
}};
}} test_data_ecdh1_mode_t;
// For 32-byte k1 key // For 32-byte k1 key
test_data_aes_mode_t test_data_xts_aes_128 = {{ test_data_aes_mode_t test_data_xts_aes_128 = {{
.init_key = {{ {key_to_c_format(init_key)} }}, .init_key = {{ {key_to_c_format(init_key)} }},
@@ -431,6 +483,44 @@ test_data_aes_mode_t test_data_ds = {{
.ds_encrypted_input_params_iv = {{ {key_to_c_format(ds_iv)} }}, .ds_encrypted_input_params_iv = {{ {key_to_c_format(ds_iv)} }},
}}, }},
}}; }};
"""
# Per-key-type ECDH1 instances. Expected per-peripheral outputs
# (XTS ciphertext, HMAC result, ECDSA pubkey) are computed off-device
# using the deployed key x(k1*k2*G).
header_content += f"""
test_data_ecdh1_mode_t test_data_ecdh1_xts_aes_128 = {{
.init_key = {{ {key_to_c_format(init_key)} }},
.k2_info = {{ {key_to_c_format(k2_info)} }},
.plaintext_data = {{ {key_to_c_format(bytes(range(1, 129)))} }},
.xts_test_data = {{
"""
for data_size, flash_address, ciphertext in ecdh1_xts_test_data:
header_content += (
f'\t\t{{.data_size = {data_size}, '
f'.data_offset = 0x{flash_address:x}, '
f'.ciphertext = {{{key_to_c_format(ciphertext)}}}}},\n'
)
header_content += '\t}\n};\n'
header_content += f"""
test_data_ecdh1_mode_t test_data_ecdh1_hmac = {{
.init_key = {{ {key_to_c_format(init_key)} }},
.k2_info = {{ {key_to_c_format(k2_info)} }},
.hmac_test_data = {{
.message = {{ {key_to_c_format(hmac_message)} }},
.hmac_result = {{ {key_to_c_format(ecdh1_hmac_result)} }}
}}
}};
test_data_ecdh1_mode_t test_data_ecdh1_ecdsa = {{
.init_key = {{ {key_to_c_format(init_key)} }},
.k2_info = {{ {key_to_c_format(k2_info)} }},
.ecdsa_test_data = {{
.ecdsa_p256_pubx = {{ {key_to_c_format(ecdh1_p256_pubx)} }},
.ecdsa_p256_puby = {{ {key_to_c_format(ecdh1_p256_puby)} }},
}}
}};
""" """
file.write(header_content) file.write(header_content)
@@ -490,6 +580,18 @@ def generate_tests_cases() -> None:
hmac_message, hmac_result = generate_hmac_test_data(k1_32) hmac_message, hmac_result = generate_hmac_test_data(k1_32)
# ECDH1: deployed key bytes = big-endian x(k1*k2*G). Per-peripheral
# effective key:
# - XTS-AES-128: key = x_be (32 BE bytes)
# - HMAC: key = x_le (= x_be[::-1], the slot is read LE)
# - ECDSA-P256: scalar = x_int mod n_p256
ecdh1_x_int = compute_ecdh1_x(k1_32, k2)
ecdh1_x_be = ecdh1_x_int.to_bytes(32, byteorder='big')
ecdh1_x_le = ecdh1_x_be[::-1]
ecdh1_xts_test_data = generate_xts_test_data(ecdh1_x_be)
ecdh1_hmac_result = hmac.HMAC(ecdh1_x_le, hmac_message, hashlib.sha256).digest()
ecdh1_p256_pubx, ecdh1_p256_puby = generate_ecdsa_pub_from_scalar(ecdh1_x_int % NIST256p.order, 256)
ds_iv = os.urandom(16) ds_iv = os.urandom(16)
ds_message_4096, ds_encrypted_input_params_4096, ds_result_4096 = generate_ds_encrypted_input_params( ds_message_4096, ds_encrypted_input_params_4096, ds_result_4096 = generate_ds_encrypted_input_params(
@@ -531,6 +633,10 @@ def generate_tests_cases() -> None:
ds_encrypted_input_params_3072, ds_encrypted_input_params_3072,
ds_result_3072, ds_result_3072,
ds_iv, ds_iv,
ecdh1_xts_test_data,
ecdh1_hmac_result,
ecdh1_p256_pubx,
ecdh1_p256_puby,
) )
File diff suppressed because one or more lines are too long
@@ -600,6 +600,118 @@ static void key_mgr_test_ds_aes_mode(void)
} }
#endif /* SOC_KEY_MANAGER_DS_KEY_DEPLOY */ #endif /* SOC_KEY_MANAGER_DS_KEY_DEPLOY */
/* ---- ECDH1 deployment helpers ----
*
* Per-key-type test vectors (k2_info, init_key, expected XTS ciphertext /
* HMAC result / ECDSA-P256 pubkey) are precomputed off-device by
* gen_key_manager_test_cases.py against the same committed k1_64.bin /
* k2.bin / init_key.bin / rand_num.bin used by the AES-mode and ECDH0
* tests, and emitted as test_data_ecdh1_xts_aes_128 / test_data_ecdh1_hmac
* / test_data_ecdh1_ecdsa instances of test_data_aes_mode_t. That lets the
* AES-mode per-peripheral verifiers (test_xts_aes_key_aes_mode,
* key_mgr_test_hmac_key_aes_mode, test_ecdsa_key_aes_mode) compare HW
* output bitwise against the precomputed expected values without any
* on-device key-derivation. k1*G (the ECDH input the user supplies) is
* shared with the ECDH0 tests via test_data_ecdh0.k1_G[0]. */
#if SOC_KEY_MANAGER_HMAC_KEY_DEPLOY
static void key_mgr_test_hmac_ecdh1_mode(void)
{
static esp_key_mgr_ecdh1_key_config_t key_config;
memcpy(key_config.k2_info, (uint8_t*) test_data_ecdh1_hmac.k2_info, KEY_MGR_K2_INFO_SIZE);
memcpy(key_config.k1_G[0], (uint8_t*) test_data_ecdh0.k1_G[0], KEY_MGR_ECDH0_INFO_SIZE);
memcpy(key_config.sw_init_key, (uint8_t*) test_data_ecdh1_hmac.init_key, KEY_MGR_SW_INIT_KEY_SIZE);
key_config.use_pre_generated_sw_init_key = 1;
key_config.key_type = ESP_KEY_MGR_HMAC_KEY;
static esp_key_mgr_key_recovery_info_t key_recovery_info;
TEST_ASSERT_EQUAL(ESP_OK, esp_key_mgr_deploy_key_in_ecdh1_mode(&key_config, &key_recovery_info));
TEST_ASSERT_EQUAL(ESP_OK, esp_key_mgr_activate_key(&key_recovery_info));
uint8_t hw_mac[32] = { 0 };
TEST_ASSERT_EQUAL(ESP_OK, hmac_calculate(HMAC_KEY_KM,
test_data_ecdh1_hmac.hmac_test_data.message,
sizeof(test_data_ecdh1_hmac.hmac_test_data.message),
hw_mac));
TEST_ASSERT_EQUAL_HEX8_ARRAY(test_data_ecdh1_hmac.hmac_test_data.hmac_result,
hw_mac, sizeof(hw_mac));
TEST_ASSERT_EQUAL(ESP_OK, esp_key_mgr_deactivate_key(key_recovery_info.key_type));
}
#endif /* SOC_KEY_MANAGER_HMAC_KEY_DEPLOY */
#if SOC_KEY_MANAGER_FE_KEY_DEPLOY && SOC_KEY_MANAGER_FE_KEY_DEPLOY_XTS_AES_128
/* Common XTS-AES verify body. Used by both the AES-mode and ECDH1-mode XTS
* tests, which carry their own struct types but share the same plaintext +
* expected-ciphertext layout. */
static void verify_xts_aes_test_data(const uint8_t *plaintext_data,
const test_xts_data_t *xts_test_data)
{
const esp_partition_t *partition = get_test_storage_partition();
ESP_ERROR_CHECK(esp_partition_erase_range(partition, 0, partition->size));
uint8_t read_data[128];
for (int i = 0; i < TEST_COUNT; i++) {
memset(read_data, 0, sizeof(read_data));
uint32_t address = xts_test_data[i].data_offset;
uint32_t data_size = xts_test_data[i].data_size;
ESP_ERROR_CHECK(esp_flash_write_encrypted(NULL, address, plaintext_data, data_size));
ESP_ERROR_CHECK(esp_flash_read(NULL, read_data, address, data_size));
TEST_ASSERT_EQUAL_HEX8_ARRAY(xts_test_data[i].ciphertext, read_data, data_size);
}
}
static void key_mgr_test_xts_aes_128_ecdh1_mode(void)
{
static esp_key_mgr_ecdh1_key_config_t key_config;
memcpy(key_config.k2_info, (uint8_t*) test_data_ecdh1_xts_aes_128.k2_info, KEY_MGR_K2_INFO_SIZE);
memcpy(key_config.k1_G[0], (uint8_t*) test_data_ecdh0.k1_G[0], KEY_MGR_ECDH0_INFO_SIZE);
memcpy(key_config.sw_init_key, (uint8_t*) test_data_ecdh1_xts_aes_128.init_key, KEY_MGR_SW_INIT_KEY_SIZE);
key_config.use_pre_generated_sw_init_key = 1;
key_config.key_type = ESP_KEY_MGR_FLASH_XTS_AES_KEY;
key_config.key_len = ESP_KEY_MGR_XTS_AES_LEN_128;
static esp_key_mgr_key_recovery_info_t key_recovery_info;
TEST_ASSERT_EQUAL(ESP_OK, esp_key_mgr_deploy_key_in_ecdh1_mode(&key_config, &key_recovery_info));
TEST_ASSERT_EQUAL(ESP_OK, esp_key_mgr_activate_key(&key_recovery_info));
verify_xts_aes_test_data(test_data_ecdh1_xts_aes_128.plaintext_data,
test_data_ecdh1_xts_aes_128.xts_test_data);
TEST_ASSERT_EQUAL(ESP_OK, esp_key_mgr_deactivate_key(key_recovery_info.key_type));
}
#endif /* SOC_KEY_MANAGER_FE_KEY_DEPLOY && SOC_KEY_MANAGER_FE_KEY_DEPLOY_XTS_AES_128 */
#if SOC_KEY_MANAGER_ECDSA_KEY_DEPLOY
static void key_mgr_test_ecdsa_p256_ecdh1_mode(void)
{
static esp_key_mgr_ecdh1_key_config_t key_config;
memcpy(key_config.k2_info, (uint8_t*) test_data_ecdh1_ecdsa.k2_info, KEY_MGR_K2_INFO_SIZE);
memcpy(key_config.k1_G[0], (uint8_t*) test_data_ecdh0.k1_G[0], KEY_MGR_ECDH0_INFO_SIZE);
memcpy(key_config.sw_init_key, (uint8_t*) test_data_ecdh1_ecdsa.init_key, KEY_MGR_SW_INIT_KEY_SIZE);
key_config.use_pre_generated_sw_init_key = 1;
key_config.key_type = ESP_KEY_MGR_ECDSA_KEY;
key_config.key_len = ESP_KEY_MGR_ECDSA_LEN_256;
static esp_key_mgr_key_recovery_info_t key_recovery_info;
TEST_ASSERT_EQUAL(ESP_OK, esp_key_mgr_deploy_key_in_ecdh1_mode(&key_config, &key_recovery_info));
TEST_ASSERT_EQUAL(ESP_OK, esp_key_mgr_activate_key(&key_recovery_info));
#if SOC_ECDSA_SUPPORT_DETERMINISTIC_MODE
test_ecdsa_key_aes_mode(ECDSA_CURVE_SECP256R1, sha_digest,
test_data_ecdh1_ecdsa.ecdsa_test_data.ecdsa_p256_pubx,
test_data_ecdh1_ecdsa.ecdsa_test_data.ecdsa_p256_puby,
ECDSA_K_TYPE_DETERMINISITIC);
#endif
test_ecdsa_key_aes_mode(ECDSA_CURVE_SECP256R1, sha_digest,
test_data_ecdh1_ecdsa.ecdsa_test_data.ecdsa_p256_pubx,
test_data_ecdh1_ecdsa.ecdsa_test_data.ecdsa_p256_puby,
ECDSA_K_TYPE_TRNG);
TEST_ASSERT_EQUAL(ESP_OK, esp_key_mgr_deactivate_key(key_recovery_info.key_type));
}
#endif /* SOC_KEY_MANAGER_ECDSA_KEY_DEPLOY */
TEST_GROUP(key_manager); TEST_GROUP(key_manager);
TEST_SETUP(key_manager) TEST_SETUP(key_manager)
@@ -629,6 +741,11 @@ TEST(key_manager, xts_key_128_ecdh0_deployment)
key_mgr_test_xts_aes_128_ecdh0_mode(); key_mgr_test_xts_aes_128_ecdh0_mode();
} }
TEST(key_manager, xts_key_128_ecdh1_deployment)
{
key_mgr_test_xts_aes_128_ecdh1_mode();
}
#if CONFIG_CRYPTO_TEST_APP_ENABLE_FPGA_TESTS #if CONFIG_CRYPTO_TEST_APP_ENABLE_FPGA_TESTS
TEST(key_manager, xts_key_128_random_deployment) TEST(key_manager, xts_key_128_random_deployment)
{ {
@@ -691,6 +808,11 @@ TEST(key_manager, ecdsa_p256_key_ecdh0_deployment)
key_mgr_test_ecdsa_key_ecdh0_mode(ESP_KEY_MGR_ECDSA_LEN_256); key_mgr_test_ecdsa_key_ecdh0_mode(ESP_KEY_MGR_ECDSA_LEN_256);
} }
TEST(key_manager, ecdsa_p256_key_ecdh1_deployment)
{
key_mgr_test_ecdsa_p256_ecdh1_mode();
}
TEST(key_manager, ecdsa_p256_key_random_deployment) TEST(key_manager, ecdsa_p256_key_random_deployment)
{ {
key_mgr_test_ecdsa_key_random_mode(ESP_KEY_MGR_ECDSA_LEN_256); key_mgr_test_ecdsa_key_random_mode(ESP_KEY_MGR_ECDSA_LEN_256);
@@ -725,6 +847,11 @@ TEST(key_manager, hmac_key_ecdh0_deployment)
key_mgr_test_hmac_ecdh0_mode(); key_mgr_test_hmac_ecdh0_mode();
} }
TEST(key_manager, hmac_key_ecdh1_deployment)
{
key_mgr_test_hmac_ecdh1_mode();
}
TEST(key_manager, hmac_key_random_deployment) TEST(key_manager, hmac_key_random_deployment)
{ {
key_mgr_test_hmac_random_mode(); key_mgr_test_hmac_random_mode();
@@ -744,6 +871,7 @@ TEST_GROUP_RUNNER(key_manager)
#if SOC_KEY_MANAGER_FE_KEY_DEPLOY_XTS_AES_128 #if SOC_KEY_MANAGER_FE_KEY_DEPLOY_XTS_AES_128
RUN_TEST_CASE(key_manager, xts_aes_128_key_aes_deployment); RUN_TEST_CASE(key_manager, xts_aes_128_key_aes_deployment);
RUN_TEST_CASE(key_manager, xts_key_128_ecdh0_deployment); RUN_TEST_CASE(key_manager, xts_key_128_ecdh0_deployment);
RUN_TEST_CASE(key_manager, xts_key_128_ecdh1_deployment);
#if CONFIG_CRYPTO_TEST_APP_ENABLE_FPGA_TESTS #if CONFIG_CRYPTO_TEST_APP_ENABLE_FPGA_TESTS
// This tests expects Flash encryption to be enabled as the test compares the decrypted flash data with the plaintext data // This tests expects Flash encryption to be enabled as the test compares the decrypted flash data with the plaintext data
RUN_TEST_CASE(key_manager, xts_key_128_random_deployment); RUN_TEST_CASE(key_manager, xts_key_128_random_deployment);
@@ -766,6 +894,7 @@ TEST_GROUP_RUNNER(key_manager)
RUN_TEST_CASE(key_manager, ecdsa_p256_key_aes_deployment); RUN_TEST_CASE(key_manager, ecdsa_p256_key_aes_deployment);
RUN_TEST_CASE(key_manager, ecdsa_p256_key_ecdh0_deployment); RUN_TEST_CASE(key_manager, ecdsa_p256_key_ecdh0_deployment);
RUN_TEST_CASE(key_manager, ecdsa_p256_key_ecdh1_deployment);
RUN_TEST_CASE(key_manager, ecdsa_p256_key_random_deployment); RUN_TEST_CASE(key_manager, ecdsa_p256_key_random_deployment);
#if SOC_ECDSA_SUPPORT_CURVE_P384 #if SOC_ECDSA_SUPPORT_CURVE_P384
@@ -778,6 +907,7 @@ TEST_GROUP_RUNNER(key_manager)
#if SOC_KEY_MANAGER_HMAC_KEY_DEPLOY #if SOC_KEY_MANAGER_HMAC_KEY_DEPLOY
RUN_TEST_CASE(key_manager, hmac_key_aes_deployment); RUN_TEST_CASE(key_manager, hmac_key_aes_deployment);
RUN_TEST_CASE(key_manager, hmac_key_ecdh0_deployment); RUN_TEST_CASE(key_manager, hmac_key_ecdh0_deployment);
RUN_TEST_CASE(key_manager, hmac_key_ecdh1_deployment);
RUN_TEST_CASE(key_manager, hmac_key_random_deployment); RUN_TEST_CASE(key_manager, hmac_key_random_deployment);
#endif /* SOC_KEY_MANAGER_HMAC_KEY_DEPLOY */ #endif /* SOC_KEY_MANAGER_HMAC_KEY_DEPLOY */
@@ -58,6 +58,24 @@ typedef struct {
WORD_ALIGNED_ATTR uint8_t k1_G[2][KEY_MGR_ECDH0_INFO_SIZE]; /*!< K1*G points for ECDH0 deployment */ WORD_ALIGNED_ATTR uint8_t k1_G[2][KEY_MGR_ECDH0_INFO_SIZE]; /*!< K1*G points for ECDH0 deployment */
} esp_key_mgr_ecdh0_key_config_t; } esp_key_mgr_ecdh0_key_config_t;
/**
* @brief Configuration for deploying a key in ECDH1 mode
*
* ECDH1 requires the same init_key/k2_info inputs as AES mode plus the
* ECDH k1*G public point(s). Unlike ECDH0, k2*G is computed by the user
* locally and is not read back from the Key Manager.
*/
typedef struct {
esp_key_mgr_key_type_t key_type; /*!< Type of key to deploy */
esp_key_mgr_key_len_t key_len; /*!< Length of the key */
bool use_pre_generated_huk_info; /*!< Use pre-generated HUK info if true */
bool use_pre_generated_sw_init_key; /*!< Use pre-generated software init key if true */
WORD_ALIGNED_ATTR esp_key_mgr_huk_info_t huk_info; /*!< HUK recovery info */
WORD_ALIGNED_ATTR uint8_t sw_init_key[KEY_MGR_SW_INIT_KEY_SIZE]; /*!< Software init key */
WORD_ALIGNED_ATTR uint8_t k2_info[KEY_MGR_K2_INFO_SIZE]; /*!< K2 info for ECDH1 deployment */
WORD_ALIGNED_ATTR uint8_t k1_G[2][KEY_MGR_ECDH0_INFO_SIZE]; /*!< K1*G points for ECDH1 deployment */
} esp_key_mgr_ecdh1_key_config_t;
/** /**
* @brief Configuration for deploying a key in Random mode * @brief Configuration for deploying a key in Random mode
*/ */
@@ -110,6 +128,18 @@ esp_err_t esp_key_mgr_deploy_key_in_aes_mode(const esp_key_mgr_aes_key_config_t
*/ */
esp_err_t esp_key_mgr_deploy_key_in_ecdh0_mode(const esp_key_mgr_ecdh0_key_config_t *key_config, esp_key_mgr_key_recovery_info_t *key_info, esp_key_mgr_ecdh0_info_t *ecdh0_key_info); esp_err_t esp_key_mgr_deploy_key_in_ecdh0_mode(const esp_key_mgr_ecdh0_key_config_t *key_config, esp_key_mgr_key_recovery_info_t *key_info, esp_key_mgr_ecdh0_info_t *ecdh0_key_info);
/**
* @brief Deploy key in ECDH1 deployment mode
*
* @param[in] key_config ECDH1 key configuration
* @param[out] key_info A writable struct of esp_key_mgr_key_recovery_info_t type.
* The recovery key info for the deployed key shall be stored here.
* @return
* - ESP_OK on success
* - ESP_FAIL or relevant error code on failure
*/
esp_err_t esp_key_mgr_deploy_key_in_ecdh1_mode(const esp_key_mgr_ecdh1_key_config_t *key_config, esp_key_mgr_key_recovery_info_t *key_info);
/** /**
* @brief Deploy key in Random deployment mode * @brief Deploy key in Random deployment mode
* *
+161 -12
View File
@@ -423,6 +423,10 @@ static esp_err_t key_mgr_deploy_key_aes_mode(aes_deploy_config_t *config)
key_mgr_hal_read_public_info(key_recovery_info, KEY_MGR_KEY_RECOVERY_INFO_SIZE); key_mgr_hal_read_public_info(key_recovery_info, KEY_MGR_KEY_RECOVERY_INFO_SIZE);
// Wait till Key Manager deployment is complete
key_mgr_hal_continue();
key_mgr_wait_for_state(ESP_KEY_MGR_STATE_IDLE);
// Check if key deployment validation should be skipped for this purpose // Check if key deployment validation should be skipped for this purpose
// Primary purposes in multi-stage deployments skip validation after the first stage // Primary purposes in multi-stage deployments skip validation after the first stage
// because the key is not yet completely deployed. // because the key is not yet completely deployed.
@@ -434,10 +438,6 @@ static esp_err_t key_mgr_deploy_key_aes_mode(aes_deploy_config_t *config)
} }
ESP_LOGD(TAG, "Key deployment valid"); ESP_LOGD(TAG, "Key deployment valid");
// Wait till Key Manager deployment is complete
key_mgr_hal_continue();
key_mgr_wait_for_state(ESP_KEY_MGR_STATE_IDLE);
config->key_info->key_info[key_recovery_info_index].crc = esp_rom_crc32_le(0, key_recovery_info, KEY_MGR_KEY_RECOVERY_INFO_SIZE); config->key_info->key_info[key_recovery_info_index].crc = esp_rom_crc32_le(0, key_recovery_info, KEY_MGR_KEY_RECOVERY_INFO_SIZE);
config->key_info->key_type = key_type; config->key_info->key_type = key_type;
config->key_info->key_len = key_len; config->key_info->key_len = key_len;
@@ -709,6 +709,10 @@ static esp_err_t key_mgr_deploy_key_ecdh0_mode(ecdh0_deploy_config_t *config)
key_mgr_hal_read_public_info(key_recovery_info, KEY_MGR_KEY_RECOVERY_INFO_SIZE); key_mgr_hal_read_public_info(key_recovery_info, KEY_MGR_KEY_RECOVERY_INFO_SIZE);
key_mgr_hal_read_assist_info(config->ecdh0_key_info); key_mgr_hal_read_assist_info(config->ecdh0_key_info);
// Wait till Key Manager deployment is complete
key_mgr_hal_continue();
key_mgr_wait_for_state(ESP_KEY_MGR_STATE_IDLE);
// Check if key deployment validation should be skipped for this purpose // Check if key deployment validation should be skipped for this purpose
// Primary purposes in multi-stage deployments skip validation after the first stage // Primary purposes in multi-stage deployments skip validation after the first stage
// because the key is not yet completely deployed. // because the key is not yet completely deployed.
@@ -720,10 +724,6 @@ static esp_err_t key_mgr_deploy_key_ecdh0_mode(ecdh0_deploy_config_t *config)
} }
ESP_LOGD(TAG, "Key deployment valid"); ESP_LOGD(TAG, "Key deployment valid");
// Wait till Key Manager deployment is complete
key_mgr_hal_continue();
key_mgr_wait_for_state(ESP_KEY_MGR_STATE_IDLE);
config->key_info->key_info[key_recovery_info_index].crc = esp_rom_crc32_le(0, key_recovery_info, KEY_MGR_KEY_RECOVERY_INFO_SIZE); config->key_info->key_info[key_recovery_info_index].crc = esp_rom_crc32_le(0, key_recovery_info, KEY_MGR_KEY_RECOVERY_INFO_SIZE);
config->key_info->key_type = key_type; config->key_info->key_type = key_type;
config->key_info->key_len = key_len; config->key_info->key_len = key_len;
@@ -789,6 +789,155 @@ cleanup:
return esp_ret; return esp_ret;
} }
typedef struct ecdh1_deploy {
esp_key_mgr_key_purpose_t key_purpose;
const uint8_t *k1_G;
const esp_key_mgr_ecdh1_key_config_t *key_config;
esp_key_mgr_key_recovery_info_t *key_info;
bool huk_deployed;
} ecdh1_deploy_config_t;
static esp_err_t key_mgr_deploy_key_ecdh1_mode(ecdh1_deploy_config_t *config)
{
esp_err_t esp_ret = ESP_FAIL;
key_mgr_wait_for_state(ESP_KEY_MGR_STATE_IDLE);
if ((!key_mgr_hal_is_huk_valid()) || (!config->huk_deployed)) {
huk_deploy_config_t huk_deploy_config = {
.use_pre_generated_huk_info = config->key_config->use_pre_generated_huk_info,
.pre_generated_huk_info = &config->key_config->huk_info,
.huk_recovery_info = &config->key_info->huk_info,
};
esp_ret = deploy_huk(&huk_deploy_config);
if (esp_ret != ESP_OK) {
return esp_ret;
}
ESP_LOGD(TAG, "HUK deployed successfully");
}
uint8_t key_recovery_info_index = is_multi_stage_key_purpose(config->key_purpose) ? 0 : 1;
uint8_t *key_recovery_info = config->key_info->key_info[key_recovery_info_index].info;
// Step 1: Initialization
key_mgr_hal_set_key_generator_mode(ESP_KEY_MGR_KEYGEN_MODE_ECDH1);
key_mgr_hal_set_key_purpose(config->key_purpose);
esp_key_mgr_key_type_t key_type = config->key_config->key_type;
esp_key_mgr_key_len_t key_len = config->key_config->key_len;
if (key_type == ESP_KEY_MGR_FLASH_XTS_AES_KEY || key_type == ESP_KEY_MGR_PSRAM_XTS_AES_KEY) {
key_mgr_hal_set_xts_aes_key_len(key_type, key_len);
}
if (config->key_config->use_pre_generated_sw_init_key) {
key_mgr_hal_use_sw_init_key();
} else if (!esp_efuse_find_purpose(ESP_EFUSE_KEY_PURPOSE_KM_INIT_KEY, NULL)) {
ESP_LOGE(TAG, "Could not find key with purpose KM_INIT_KEY");
return ESP_FAIL;
}
key_mgr_hal_start();
// Step 2: Load phase
key_mgr_wait_for_state(ESP_KEY_MGR_STATE_LOAD);
if (config->key_config->use_pre_generated_sw_init_key) {
key_mgr_hal_write_sw_init_key(config->key_config->sw_init_key, KEY_MGR_SW_INIT_KEY_SIZE);
}
ESP_LOGD(TAG, "Writing Information into Key Manager Registers");
key_mgr_hal_write_assist_info(config->key_config->k2_info, KEY_MGR_K2_INFO_SIZE);
key_mgr_hal_write_public_info(config->k1_G, KEY_MGR_ECDH0_INFO_SIZE);
key_mgr_hal_continue();
// Step 3: Gain phase
key_mgr_wait_for_state(ESP_KEY_MGR_STATE_GAIN);
key_mgr_hal_read_public_info(key_recovery_info, KEY_MGR_KEY_RECOVERY_INFO_SIZE);
// Wait till Key Manager deployment is complete
key_mgr_hal_continue();
key_mgr_wait_for_state(ESP_KEY_MGR_STATE_IDLE);
// Check if key deployment validation should be skipped for this purpose
// Primary purposes in multi-stage deployments skip validation after the first stage
// because the key is not yet completely deployed.
if (!is_multi_stage_key_purpose(config->key_purpose)) {
if (!key_mgr_hal_is_key_deployment_valid(key_type, key_len)) {
ESP_LOGE(TAG, "Key deployment is not valid");
return ESP_FAIL;
}
}
ESP_LOGD(TAG, "Key deployment valid");
config->key_info->key_info[key_recovery_info_index].crc = esp_rom_crc32_le(0, key_recovery_info, KEY_MGR_KEY_RECOVERY_INFO_SIZE);
config->key_info->key_type = key_type;
config->key_info->key_len = key_len;
config->key_info->key_deployment_mode = ESP_KEY_MGR_KEYGEN_MODE_ECDH1;
config->key_info->magic = KEY_HUK_SECTOR_MAGIC;
return ESP_OK;
}
esp_err_t esp_key_mgr_deploy_key_in_ecdh1_mode(const esp_key_mgr_ecdh1_key_config_t *key_config,
esp_key_mgr_key_recovery_info_t *key_info)
{
if (!key_mgr_ll_is_supported()) {
return ESP_ERR_NOT_SUPPORTED;
}
if (key_config == NULL || key_info == NULL) {
return ESP_ERR_INVALID_ARG;
}
ESP_LOGD(TAG, "Key Deployment in ECDH1 mode");
ecdh1_deploy_config_t ecdh1_deploy_config = {
.key_config = key_config,
.key_info = key_info,
.k1_G = key_config->k1_G[0],
};
ecdh1_deploy_config.key_purpose = get_key_purpose(key_config->key_type, key_config->key_len);
if (ecdh1_deploy_config.key_purpose == ESP_KEY_MGR_KEY_PURPOSE_INVALID) {
ESP_LOGE(TAG, "Invalid key type");
return ESP_ERR_INVALID_ARG;
}
esp_key_mgr_acquire_hardware(true);
esp_err_t esp_ret = key_mgr_deploy_key_ecdh1_mode(&ecdh1_deploy_config);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Key deployment in ECDH1 mode failed");
goto cleanup;
}
ecdh1_deploy_config.huk_deployed = true;
if (is_multi_stage_key_purpose(ecdh1_deploy_config.key_purpose)) {
ecdh1_deploy_config.key_purpose = get_secondary_key_purpose(ecdh1_deploy_config.key_purpose);
ecdh1_deploy_config.k1_G = key_config->k1_G[1];
esp_ret = key_mgr_deploy_key_ecdh1_mode(&ecdh1_deploy_config);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Key deployment in ECDH1 mode failed");
goto cleanup;
}
}
// Set the Key Manager Static Register to use own key for the respective key type
key_mgr_hal_set_key_usage(key_config->key_type, ESP_KEY_MGR_USE_OWN_KEY);
cleanup:
esp_key_mgr_release_hardware(true);
return esp_ret;
}
typedef struct random_deploy { typedef struct random_deploy {
esp_key_mgr_key_purpose_t key_purpose; esp_key_mgr_key_purpose_t key_purpose;
const esp_key_mgr_random_key_config_t *key_config; const esp_key_mgr_random_key_config_t *key_config;
@@ -845,6 +994,10 @@ static esp_err_t key_mgr_deploy_key_random_mode(random_deploy_config_t *config)
key_mgr_wait_for_state(ESP_KEY_MGR_STATE_GAIN); key_mgr_wait_for_state(ESP_KEY_MGR_STATE_GAIN);
key_mgr_hal_read_public_info(key_recovery_info, KEY_MGR_KEY_RECOVERY_INFO_SIZE); key_mgr_hal_read_public_info(key_recovery_info, KEY_MGR_KEY_RECOVERY_INFO_SIZE);
// Wait till Key Manager deployment is complete
key_mgr_hal_continue();
key_mgr_wait_for_state(ESP_KEY_MGR_STATE_IDLE);
// Check if key deployment validation should be skipped for this purpose // Check if key deployment validation should be skipped for this purpose
// Primary purposes in multi-stage deployments skip validation after the first stage // Primary purposes in multi-stage deployments skip validation after the first stage
// because the key is not yet completely deployed. // because the key is not yet completely deployed.
@@ -856,10 +1009,6 @@ static esp_err_t key_mgr_deploy_key_random_mode(random_deploy_config_t *config)
} }
ESP_LOGD(TAG, "Key deployment valid"); ESP_LOGD(TAG, "Key deployment valid");
// Wait till Key Manager deployment is complete
key_mgr_hal_continue();
key_mgr_wait_for_state(ESP_KEY_MGR_STATE_IDLE);
config->key_info->key_info[key_recovery_info_index].crc = esp_rom_crc32_le(0, key_recovery_info, KEY_MGR_KEY_RECOVERY_INFO_SIZE); config->key_info->key_info[key_recovery_info_index].crc = esp_rom_crc32_le(0, key_recovery_info, KEY_MGR_KEY_RECOVERY_INFO_SIZE);
config->key_info->key_type = key_type; config->key_info->key_type = key_type;
config->key_info->key_len = key_len; config->key_info->key_len = key_len;
@@ -226,6 +226,36 @@ TEST_CASE("Key Manager Random mode: XTS-AES-128 key deployment", "[hw_crypto] [k
free(key_config); free(key_config);
free(key_recovery_info); free(key_recovery_info);
} }
TEST_CASE("Key Manager ECDH1 mode: XTS-AES-128 key deployment", "[hw_crypto] [key_mgr]")
{
SKIP_IF_KEY_MGR_NOT_SUPPORTED();
esp_key_mgr_ecdh1_key_config_t *key_config = calloc(1, sizeof(esp_key_mgr_ecdh1_key_config_t));
TEST_ASSERT_NOT_NULL(key_config);
memcpy(key_config->k2_info, (uint8_t*) k2_info, KEY_MGR_K2_INFO_SIZE);
memcpy(key_config->k1_G[0], (uint8_t*) k1_G, KEY_MGR_ECDH0_INFO_SIZE);
memcpy(key_config->sw_init_key, (uint8_t*) init_key, KEY_MGR_SW_INIT_KEY_SIZE);
key_config->use_pre_generated_sw_init_key = 1;
key_config->key_type = ESP_KEY_MGR_FLASH_XTS_AES_KEY;
key_config->key_len = ESP_KEY_MGR_XTS_AES_LEN_128;
esp_key_mgr_key_recovery_info_t *key_recovery_info = calloc(1, sizeof(esp_key_mgr_key_recovery_info_t));
TEST_ASSERT_NOT_NULL(key_recovery_info);
TEST_ASSERT_EQUAL(ESP_OK, esp_key_mgr_deploy_key_in_ecdh1_mode(key_config, key_recovery_info));
TEST_ASSERT_EQUAL(ESP_OK, esp_key_mgr_activate_key(key_recovery_info));
/* verify=false: same convention as the ECDH0 case above. The expected
* ciphertext IS derivable (k2 = AES_DEC(k2_info, init_key); deployed key
* = x(k1*k2*G)) but this app doesn't carry the off-device ECC code.
* Bitwise verification lives in the HAL crypto test app. */
TEST_ASSERT_EQUAL(ESP_OK, test_xts_aes_key(false));
TEST_ASSERT_EQUAL(ESP_OK, esp_key_mgr_deactivate_key(key_recovery_info->key_type));
free(key_config);
free(key_recovery_info);
}
#endif /* SOC_KEY_MANAGER_FE_KEY_DEPLOY */ #endif /* SOC_KEY_MANAGER_FE_KEY_DEPLOY */
#if SOC_KEY_MANAGER_ECDSA_KEY_DEPLOY #if SOC_KEY_MANAGER_ECDSA_KEY_DEPLOY
@@ -333,6 +363,31 @@ TEST_CASE("Key Manager random mode: HMAC key deployment", "[hw_crypto] [key_mgr]
free(key_config); free(key_config);
free(key_recovery_info); free(key_recovery_info);
} }
TEST_CASE("Key Manager ECDH1 mode: HMAC key deployment", "[hw_crypto] [key_mgr]")
{
SKIP_IF_KEY_MGR_NOT_SUPPORTED();
esp_key_mgr_ecdh1_key_config_t *key_config = calloc(1, sizeof(esp_key_mgr_ecdh1_key_config_t));
TEST_ASSERT_NOT_NULL(key_config);
memcpy(key_config->k2_info, (uint8_t*) k2_info, KEY_MGR_K2_INFO_SIZE);
memcpy(key_config->k1_G[0], (uint8_t*) k1_G, KEY_MGR_ECDH0_INFO_SIZE);
memcpy(key_config->sw_init_key, (uint8_t*) init_key, KEY_MGR_SW_INIT_KEY_SIZE);
key_config->use_pre_generated_sw_init_key = 1;
key_config->key_type = ESP_KEY_MGR_HMAC_KEY;
esp_key_mgr_key_recovery_info_t *key_recovery_info = calloc(1, sizeof(esp_key_mgr_key_recovery_info_t));
TEST_ASSERT_NOT_NULL(key_recovery_info);
TEST_ASSERT_EQUAL(ESP_OK, esp_key_mgr_deploy_key_in_ecdh1_mode(key_config, key_recovery_info));
TEST_ASSERT_EQUAL(ESP_OK, esp_key_mgr_activate_key(key_recovery_info));
TEST_ASSERT_EQUAL(ESP_OK, test_hmac_key(false));
TEST_ASSERT_EQUAL(ESP_OK, esp_key_mgr_deactivate_key(key_recovery_info->key_type));
free(key_config);
free(key_recovery_info);
}
#endif /* SOC_KEY_MANAGER_HMAC_KEY_DEPLOY */ #endif /* SOC_KEY_MANAGER_HMAC_KEY_DEPLOY */
#if SOC_KEY_MANAGER_DS_KEY_DEPLOY #if SOC_KEY_MANAGER_DS_KEY_DEPLOY
@@ -434,5 +489,33 @@ TEST_CASE("Key Manager random mode: DS key deployment", "[hw_crypto] [key_mgr]")
free(key_config); free(key_config);
free(key_recovery_info); free(key_recovery_info);
} }
TEST_CASE("Key Manager ECDH1 mode: DS key deployment", "[hw_crypto] [key_mgr]")
{
SKIP_IF_KEY_MGR_NOT_SUPPORTED();
esp_key_mgr_ecdh1_key_config_t *key_config = calloc(1, sizeof(esp_key_mgr_ecdh1_key_config_t));
TEST_ASSERT_NOT_NULL(key_config);
memcpy(key_config->k2_info, (uint8_t*) k2_info, KEY_MGR_K2_INFO_SIZE);
memcpy(key_config->k1_G[0], (uint8_t*) k1_G, KEY_MGR_ECDH0_INFO_SIZE);
memcpy(key_config->sw_init_key, (uint8_t*) init_key, KEY_MGR_SW_INIT_KEY_SIZE);
key_config->use_pre_generated_sw_init_key = 1;
key_config->key_type = ESP_KEY_MGR_DS_KEY;
esp_key_mgr_key_recovery_info_t *key_recovery_info = calloc(1, sizeof(esp_key_mgr_key_recovery_info_t));
TEST_ASSERT_NOT_NULL(key_recovery_info);
TEST_ASSERT_EQUAL(ESP_OK, esp_key_mgr_deploy_key_in_ecdh1_mode(key_config, key_recovery_info));
TEST_ASSERT_EQUAL(ESP_OK, esp_key_mgr_activate_key(key_recovery_info));
// Deploy/activate path coverage only. The deployed AES-CBC key for DS
// is x(k1*k2*G); pre-generating the encrypted DS input params would
// require running that key derivation plus the DS-blob AES-CBC step
// off-device, neither of which this app carries.
TEST_ASSERT_EQUAL(ESP_OK, esp_key_mgr_deactivate_key(key_recovery_info->key_type));
free(key_config);
free(key_recovery_info);
}
#endif /* SOC_KEY_MANAGER_DS_KEY_DEPLOY */ #endif /* SOC_KEY_MANAGER_DS_KEY_DEPLOY */
#endif /* SOC_KEY_MANAGER_SUPPORTED */ #endif /* SOC_KEY_MANAGER_SUPPORTED */
@@ -108,16 +108,16 @@ In this mode, a private key is negotiated using Elliptic Curve Diffie-Hellman (E
- The deployment process can occur over an untrusted channel. - The deployment process can occur over an untrusted channel.
- Intended for high-security provisioning environments. - Intended for high-security provisioning environments.
For detailed information various deployment modes, see **{IDF_TARGET_NAME} Technical Reference Manual** > **Chapter Key Manager** [`PDF <{IDF_TARGET_TRM_EN_URL}>`__] > **Section Key Manager**. ECDH1 Deploy Mode
^^^^^^^^^^^^^^^^^
.. ECDH1 Deploy Mode This mode is similar to ECDH0 Deploy Mode, with additional flexibility for manufacturing workflows.
.. ~~~~~~~~~~~~~~~~~
.. - Supports negotiated key deployment using auxiliary recovery data
.. This mode is similar to ECDH0 Deploy Mode, with additional flexibility for manufacturing workflows. - Allows updating deployed keys by replacing auxiliary information
.. - Intended for large-scale manufacturing with controlled trust assumptions
.. - Supports negotiated key deployment using auxiliary recovery data
.. - Allows updating deployed keys by replacing auxiliary information For detailed information various deployment modes, see **{IDF_TARGET_NAME} Technical Reference Manual** > **Chapter Key Manager** [`PDF <{IDF_TARGET_TRM_EN_URL}>`__] > **Section Key Manager**.
.. - Intended for large-scale manufacturing with controlled trust assumptions
Typical Workflows Typical Workflows
----------------- -----------------
@@ -108,16 +108,16 @@ ECDH0 部署模式
- 部署过程可以通过不受信任的信道进行。 - 部署过程可以通过不受信任的信道进行。
- 适用于高安全性的配置环境。 - 适用于高安全性的配置环境。
有关各种部署模式的详细信息,请参阅 **《{IDF_TARGET_NAME} 技术参考手册》** > **密钥管理器章节** [`PDF <{IDF_TARGET_TRM_EN_URL}>`__] > **密钥管理器小节**。 ECDH1 部署模式
^^^^^^^^^^^^^^
.. ECDH1 Deploy Mode 此模式类似于 ECDH0 部署模式,但为生产配置流程提供了额外的灵活性。
.. ~~~~~~~~~~~~~~~~~
.. - 支持使用辅助恢复数据进行协商的密钥部署。
.. This mode is similar to ECDH0 Deploy Mode, with additional flexibility for manufacturing workflows. - 允许通过替换辅助信息来更新已部署的密钥。
.. - 适用于具有受控信任假设的大规模生产场景。
.. - Supports negotiated key deployment using auxiliary recovery data
.. - Allows updating deployed keys by replacing auxiliary information 有关各种部署模式的详细信息,请参阅 **《{IDF_TARGET_NAME} 技术参考手册》** > **密钥管理器章节** [`PDF <{IDF_TARGET_TRM_EN_URL}>`__] > **密钥管理器小节**。
.. - Intended for large-scale manufacturing with controlled trust assumptions
典型工作流程 典型工作流程
------------ ------------