Hi Nordic Team,
in our project we want to write/read cryptographic keys to/from the KMU.
One of the algorithms we are going to use is ECDSA.
My naive approach was to just modify the existing nrf/samples/crypto/ecdsa code.
Another ticket in the developer zone (see https://devzone.nordicsemi.com/f/nordic-q-a/118434/kmu-psa-persistent-key-generation) reveals, that there's a bug in SDK v2.9.0.
Therefore, I switched ncs to the main branch (commit a95e127c906afef92e36e79120d2ec127c250b87).
My prj.conf equals:
# # Copyright (c) 2024 Nordic Semiconductor ASA # # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause # # The Zephyr CMSIS emulation assumes that ticks are ms, currently CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000 CONFIG_MAIN_STACK_SIZE=4096 CONFIG_HEAP_MEM_POOL_SIZE=4096 CONFIG_REBOOT=y # Enable logging CONFIG_CONSOLE=y CONFIG_LOG=y # Enable nordic security backend and PSA APIs CONFIG_NRF_SECURITY=y CONFIG_MBEDTLS_PSA_CRYPTO_C=y # Enable persistent storage APIs CONFIG_MBEDTLS_PSA_CRYPTO_STORAGE_C=y # Mbedtls configuration CONFIG_MBEDTLS_ENABLE_HEAP=y CONFIG_MBEDTLS_HEAP_SIZE=8192 # Using hardware crypto accelerator CONFIG_PSA_CRYPTO_DRIVER_OBERON=n CONFIG_PSA_CRYPTO_DRIVER_CRACEN=y CONFIG_PSA_WANT_GENERATE_RANDOM=y # ECDSA support CONFIG_PSA_WANT_ALG_ECDSA=y CONFIG_PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_GENERATE=y CONFIG_PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT=y CONFIG_PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_EXPORT=y CONFIG_PSA_WANT_ECC_SECP_R1_256=y CONFIG_PSA_WANT_ALG_SHA_256=y # Use TRUSTED_STORAGE because this is a non-TF-M board target. CONFIG_TRUSTED_STORAGE=y CONFIG_FLASH=y CONFIG_FLASH_PAGE_LAYOUT=y CONFIG_FLASH_MAP=y CONFIG_ZMS=y CONFIG_SETTINGS=y CONFIG_DEBUG=y
My modified generate_ecdsa_keypair() succeeds to generate a new keypair in the KMU resp. read an existing keypair from the KMU:
int generate_ecdsa_keypair(void)
{
psa_status_t status;
size_t olen;
LOG_INF("Generating/read random persistent ECDSA key pair...");
/* Configure the key attributes */
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_SIGN_MESSAGE);
psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDSA(PSA_ALG_ANY_HASH));
psa_set_key_type(&key_attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&key_attributes, 256);
psa_set_key_lifetime(
&key_attributes,
PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(
PSA_KEY_PERSISTENCE_DEFAULT,
PSA_KEY_LOCATION_CRACEN_KMU
)
);
psa_set_key_id(
&key_attributes,
PSA_KEY_HANDLE_FROM_CRACEN_KMU_SLOT(
CRACEN_KMU_KEY_USAGE_SCHEME_RAW,
8
)
);
status = psa_generate_key(&key_attributes, &ecdsa_keypair_id);
if(status == PSA_SUCCESS) {
// nothing to do
}
else if(status == PSA_ERROR_ALREADY_EXISTS) {
LOG_INF("ECDSA keypair already exists!");
ecdsa_keypair_id = PSA_KEY_HANDLE_FROM_CRACEN_KMU_SLOT(CRACEN_KMU_KEY_USAGE_SCHEME_RAW, 8);
}
else {
LOG_INF("psa_generate_key failed! (Error: %d)", status);
return APP_ERROR;
}
/* Export the public ECDSA key */
status = psa_export_public_key(ecdsa_keypair_id, m_ecdsa_pub_key, sizeof(m_ecdsa_pub_key), &olen);
if(status != PSA_SUCCESS) {
LOG_INF("failed to export ECDSA public key (Error: %d)", status);
return APP_ERROR;
}
PRINT_HEX("ECDSA pub key", m_ecdsa_pub_key, sizeof(m_ecdsa_pub_key));
/* After the key handle is acquired the attributes are not needed */
psa_reset_key_attributes(&key_attributes);
LOG_INF("ECDSA keypair generated/read successfully!");
return APP_SUCCESS;
}
But my modified import_ecdsa_pub_key() function fails:
int import_ecdsa_pub_key(void)
{
/* Configure the key attributes */
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_status_t status;
/* Configure the key attributes */
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_VERIFY_HASH | PSA_KEY_USAGE_VERIFY_MESSAGE);
psa_set_key_lifetime(
&key_attributes,
PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(
PSA_KEY_PERSISTENCE_DEFAULT,
PSA_KEY_LOCATION_CRACEN_KMU
)
);
psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDSA(PSA_ALG_ANY_HASH));
psa_set_key_type(&key_attributes, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&key_attributes, 256);
psa_set_key_id(
&key_attributes,
PSA_KEY_HANDLE_FROM_CRACEN_KMU_SLOT(
CRACEN_KMU_KEY_USAGE_SCHEME_RAW,
8
)
);
status = psa_import_key(&key_attributes, m_ecdsa_pub_key, sizeof(m_ecdsa_pub_key), &ecdsa_pub_key_id);
if (status != PSA_SUCCESS) {
LOG_INF("psa_import_key failed! (Error: %d)", status);
return APP_ERROR;
}
/* Reset key attributes and free any allocated resources. */
psa_reset_key_attributes(&key_attributes);
return APP_SUCCESS;
}
psa_import_key() returns PSA_ERROR_INVALID_ARGUMENT.
The error code comes from nrf/subsys/nrf_security/src/drivers/cracen/cracenpsa/src/kmu.c:837ff
case CRACEN_KMU_KEY_USAGE_SCHEME_RAW:
push_address = (uint8_t *)kmu_push_area;
if (key_buffer_size != 16 && key_buffer_size != 24 && key_buffer_size != 32) {
return PSA_ERROR_INVALID_ARGUMENT;
}
break;
The buffer size for the ECDSA public key (i.e. m_ecdsa_pub_key) is 65 bytes.
Am I doing something wrong?
Best regards,
Christian