mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
Merge branch 'feat/key_manager_ecdh1_key_deployment_mode_v6.0' into 'release/v6.0'
Key Manager ECDH1 key deployment mode (v6.0) See merge request espressif/esp-idf!48344
This commit is contained in:
+107
-1
@@ -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
|
||||
import hashlib
|
||||
import hmac
|
||||
@@ -146,6 +146,39 @@ def generate_k1_G(k1_bytes: bytes) -> tuple:
|
||||
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:
|
||||
hmac_message = (
|
||||
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_result_3072: bytes,
|
||||
ds_iv: bytes,
|
||||
ecdh1_xts_test_data: list,
|
||||
ecdh1_hmac_result: bytes,
|
||||
ecdh1_p256_pubx: bytes,
|
||||
ecdh1_p256_puby: bytes,
|
||||
) -> None:
|
||||
with open('key_manager_test_cases.h', 'w', encoding='utf-8') as file:
|
||||
header_content = f"""#include <stdint.h>
|
||||
@@ -327,6 +364,21 @@ typedef struct test_data_ecdh0 {{
|
||||
uint8_t k1_G[2][64];
|
||||
}} 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
|
||||
test_data_aes_mode_t test_data_xts_aes_128 = {{
|
||||
.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)} }},
|
||||
}},
|
||||
}};
|
||||
"""
|
||||
|
||||
# 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)
|
||||
@@ -490,6 +580,18 @@ def generate_tests_cases() -> None:
|
||||
|
||||
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_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_result_3072,
|
||||
ds_iv,
|
||||
ecdh1_xts_test_data,
|
||||
ecdh1_hmac_result,
|
||||
ecdh1_p256_pubx,
|
||||
ecdh1_p256_puby,
|
||||
)
|
||||
|
||||
|
||||
|
||||
+54
-8
File diff suppressed because one or more lines are too long
@@ -600,6 +600,96 @@ static void key_mgr_test_ds_aes_mode(void)
|
||||
}
|
||||
#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
|
||||
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_SETUP(key_manager)
|
||||
@@ -629,6 +719,11 @@ TEST(key_manager, xts_key_128_ecdh0_deployment)
|
||||
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
|
||||
TEST(key_manager, xts_key_128_random_deployment)
|
||||
{
|
||||
@@ -691,6 +786,11 @@ TEST(key_manager, ecdsa_p256_key_ecdh0_deployment)
|
||||
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)
|
||||
{
|
||||
key_mgr_test_ecdsa_key_random_mode(ESP_KEY_MGR_ECDSA_LEN_256);
|
||||
@@ -725,6 +825,11 @@ TEST(key_manager, hmac_key_ecdh0_deployment)
|
||||
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)
|
||||
{
|
||||
key_mgr_test_hmac_random_mode();
|
||||
@@ -744,6 +849,7 @@ TEST_GROUP_RUNNER(key_manager)
|
||||
#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_key_128_ecdh0_deployment);
|
||||
RUN_TEST_CASE(key_manager, xts_key_128_ecdh1_deployment);
|
||||
#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
|
||||
RUN_TEST_CASE(key_manager, xts_key_128_random_deployment);
|
||||
@@ -766,6 +872,7 @@ TEST_GROUP_RUNNER(key_manager)
|
||||
|
||||
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_ecdh1_deployment);
|
||||
RUN_TEST_CASE(key_manager, ecdsa_p256_key_random_deployment);
|
||||
|
||||
#if SOC_ECDSA_SUPPORT_CURVE_P384
|
||||
@@ -778,6 +885,7 @@ TEST_GROUP_RUNNER(key_manager)
|
||||
#if SOC_KEY_MANAGER_HMAC_KEY_DEPLOY
|
||||
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_ecdh1_deployment);
|
||||
RUN_TEST_CASE(key_manager, hmac_key_random_deployment);
|
||||
#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 */
|
||||
} 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
|
||||
*/
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
* @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
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
// 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.
|
||||
@@ -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");
|
||||
|
||||
// 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_type = key_type;
|
||||
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_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
|
||||
// Primary purposes in multi-stage deployments skip validation after the first stage
|
||||
// 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");
|
||||
|
||||
// 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_type = key_type;
|
||||
config->key_info->key_len = key_len;
|
||||
@@ -789,6 +789,157 @@ cleanup:
|
||||
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;
|
||||
bool multi_stage_deployment;
|
||||
} 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 = config->multi_stage_deployment ? 1 : 0;
|
||||
|
||||
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 (!multi_stage_deployment_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 (multi_stage_deployment_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];
|
||||
ecdh1_deploy_config.multi_stage_deployment = true;
|
||||
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 {
|
||||
esp_key_mgr_key_purpose_t key_purpose;
|
||||
const esp_key_mgr_random_key_config_t *key_config;
|
||||
@@ -845,6 +996,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_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.
|
||||
@@ -856,10 +1011,6 @@ static esp_err_t key_mgr_deploy_key_random_mode(random_deploy_config_t *config)
|
||||
}
|
||||
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_type = key_type;
|
||||
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_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 */
|
||||
|
||||
#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_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 */
|
||||
|
||||
#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_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_SUPPORTED */
|
||||
|
||||
Reference in New Issue
Block a user