KMU import ECDSA public key

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

Parents
  • This was obviously my fault.

    Of course, I don't have to import the public into the KMU. It's already there.

    So PSA_KEY_LIFETIME_VOLATILE is the right option.

    psa_status_t status;
    
    /* Configure the key attributes */
    psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
    
    psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_VERIFY_HASH | PSA_KEY_USAGE_VERIFY_MESSAGE);
    psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
    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_lifetime(&key_attributes, PSA_KEY_LIFETIME_VOLATILE);
    
    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;

  • There's one issue though.

    psa_driver_wrapper_sign_hash() does not handle the case PSA_KEY_LOCATION_CRACEN_KMU like the psa_driver_wrapper_sign_message() function does.

    diff --git a/subsys/nrf_security/src/psa_crypto_driver_wrappers.c b/subsys/nrf_security/src/psa_crypto_driver_wrappers.c
    index 736646b9cb..59b3a51635 100644
    --- a/subsys/nrf_security/src/psa_crypto_driver_wrappers.c
    +++ b/subsys/nrf_security/src/psa_crypto_driver_wrappers.c
    @@ -330,6 +330,9 @@ psa_status_t psa_driver_wrapper_sign_hash(const psa_key_attributes_t *attributes
    
     #if defined(PSA_NEED_CRACEN_ASYMMETRIC_SIGNATURE_DRIVER)
            case PSA_KEY_LOCATION_CRACEN:
    +#if defined(PSA_NEED_CRACEN_KMU_DRIVER)
    +       case PSA_KEY_LOCATION_CRACEN_KMU:
    +#endif /* PSA_NEED_CRACEN_KMU_DRIVER */
                    status = cracen_sign_hash(attributes, key_buffer, key_buffer_size, alg, hash,
                                              hash_length, signature, signature_size, signature_length);
                    /* Declared with fallback == true */
Reply
  • There's one issue though.

    psa_driver_wrapper_sign_hash() does not handle the case PSA_KEY_LOCATION_CRACEN_KMU like the psa_driver_wrapper_sign_message() function does.

    diff --git a/subsys/nrf_security/src/psa_crypto_driver_wrappers.c b/subsys/nrf_security/src/psa_crypto_driver_wrappers.c
    index 736646b9cb..59b3a51635 100644
    --- a/subsys/nrf_security/src/psa_crypto_driver_wrappers.c
    +++ b/subsys/nrf_security/src/psa_crypto_driver_wrappers.c
    @@ -330,6 +330,9 @@ psa_status_t psa_driver_wrapper_sign_hash(const psa_key_attributes_t *attributes
    
     #if defined(PSA_NEED_CRACEN_ASYMMETRIC_SIGNATURE_DRIVER)
            case PSA_KEY_LOCATION_CRACEN:
    +#if defined(PSA_NEED_CRACEN_KMU_DRIVER)
    +       case PSA_KEY_LOCATION_CRACEN_KMU:
    +#endif /* PSA_NEED_CRACEN_KMU_DRIVER */
                    status = cracen_sign_hash(attributes, key_buffer, key_buffer_size, alg, hash,
                                              hash_length, signature, signature_size, signature_length);
                    /* Declared with fallback == true */
Children
Related