Mbedtls - Using keys handled by PSA Crypto

Hello,

I need to implement the code that calculates the ECDSA signature, using the mbedtls library.
Development environment:

  • nRF53DK
  • nRF Connect SDK 2.3.0

Currently, the process is implemented as follows:

  • The private key is generated using openssl, and stored to flash memory in DER format.
  • The signature is calculated by loading the mentioned key in mbedtls_pk_context using mbedtls_pk_parse_key, and passing such context to the mbedtls_ecdsa_write_signature function.

As a next step, it is necessary to implement key generation within the firmware and use it for signature so that the code itself does not need access to the key content.

I was able to generate the key using psa_generate_key.
The plan is to load the generated key to the corresponding mbedtls_pk_context using mbedtls_pk_setup_opaque as a next step.
This should be the only change from the current state (after configuring the mbedtls_pk_context, mbedtls_ecdsa_write_signature is called as before).

However, the problem is that, despite researching similar forum posts, and corresponding mbedtls configuration requests, I am still not able to use the mbedtls_pk_setup_opaque function because it looks like MBEDTLS_USE_PSA_CRYPTO is not defined.

The following security modules are included in the prj file (here there are certainly those that are not needed for the mentioned ECDSA signing, but remained in the project for the needs of tests of various mbedtls functionalities):

CONFIG_NRF_SECURITY=y

CONFIG_MBEDTLS_LIBRARY_NRF_SECURITY=y
CONFIG_MBEDTLS_USE_PSA_CRYPTO=y
CONFIG_MBEDTLS_PSA_CRYPTO_C=y
CONFIG_MBEDTLS_PSA_CRYPTO_STORAGE_C=y

CONFIG_MBEDTLS_PK_C=y
CONFIG_MBEDTLS_RSA_C=y
CONFIG_MBEDTLS_PKCS1_V15=y
CONFIG_MBEDTLS_SHA256_C=y
CONFIG_MBEDTLS_ECP_C=y
CONFIG_MBEDTLS_ECDSA_C=y
CONFIG_MBEDTLS_ECDH_C=y
CONFIG_MBEDTLS_CTR_DRBG_C=y
CONFIG_MBEDTLS_ENTROPY_C=y
CONFIG_MBEDTLS_PK_PARSE_C=y

CONFIG_MBEDTLS_TLS_LIBRARY=y
CONFIG_MBEDTLS_X509_LIBRARY=y
CONFIG_MBEDTLS_PK_WRITE_C=y
CONFIG_MBEDTLS_X509_CREATE_C=y
CONFIG_MBEDTLS_X509_CSR_WRITE_C=y
CONFIG_MBEDTLS_LEGACY_CRYPTO_C=y
CONFIG_MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG=n

Do you have any idea what could be the cause of this behavior?
Is this a regular approach for implementing not exposing secret key material to the users of mbedtls API?

Thanks.

  • Hi,

    I need to implement the code that calculates the ECDSA signature, using the mbedtls library.

    If you want to rule out an XY Problem, you can tell me what the use-case is. If not I will help you as best I can with what you ask for.

    it looks like MBEDTLS_USE_PSA_CRYPTO is not defined.

    I suggest that you use the VS Code Kconfig tool or "menuconfig" (west build -t menuconfig) to check which dependencies unset CONFIG_MBEDTLS_USE_PSA_CRYPTO.

    If I build the nrf/samples/crypto/ecdsa sample with your above configs except for EXTERNAL_RNG (it makes the build fail), I get MBEDTLS_USE_PSA_CRYPTO=y

    Does this help you?

    Regards,
    Sigurd Hellesvik

  • Hello,

    Thanks for answering.

    If you want to rule out an XY Problem, you can tell me what the use-case is. If not I will help you as best I can with what you ask for.

    I will try to rephrase the question.

    I implemented an initial solution for calculating ECDSA signature, using the mbedtls library, that works fine.
    Such implementation loads the private key in DER format (stored in Flash memory) into mbedtls_pk_context using mbedtls_pk_parse_key, and further calculates signature providing such mbedtls_pk_context to the mbedtls_ecdsa_write_signature function.

    Now I want to stop using the key preloaded in Flash and generate it within the code.

    My approach is to generate key with psa_generate_key function, and load such key to mbedtls_pk_context using mbedtls_pk_setup_opaque (practically for replacing the call of mbedtls_pk_parse_key with the call of mbedtls_pk_setup_opaque).

    The bottom problem is that I'm not able to compile code using mbedtls_pk_setup_opaque call.

    I suggest that you use the VS Code Kconfig tool or "menuconfig" (west build -t menuconfig) to check which dependencies unset CONFIG_MBEDTLS_USE_PSA_CRYPTO.

    I will check that.

    Best regards,
    Darko

  • Hello,

    I upgraded the existing persistent_key_usage example with mbedtls functionality, in order to additionally describe the problem.

    In the code below, I tried to generate a CSR using a key generated with the "PSA_KEY_LIFETIME_PERSISTENT" attribute (in the posts above I mentioned the ECDSA signature, but for the simplicity of this example I chose to present the problem through generating a CSR).

    Here is the original code from the demo with added generate_csr function:

    /*
     * Copyright (c) 2021 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/sys/printk.h>
    #include <zephyr/logging/log.h>
    #include <stdio.h>
    #include <psa/crypto.h>
    #include <psa/crypto_extra.h>
    #include <pk.h>
    #include <mbedtls/x509_csr.h>
    #include <mbedtls/ctr_drbg.h>
    #include <mbedtls/entropy.h>
    
    #ifdef CONFIG_BUILD_WITH_TFM
    #include <tfm_ns_interface.h>
    #endif
    
    #define APP_SUCCESS		(0)
    #define APP_ERROR		(-1)
    #define APP_SUCCESS_MESSAGE "Example finished successfully!"
    #define APP_ERROR_MESSAGE "Example exited with error!"
    
    #define PRINT_HEX(p_label, p_text, len)\
    	({\
    		LOG_INF("---- %s (len: %u): ----", p_label, len);\
    		LOG_HEXDUMP_INF(p_text, len, "Content:");\
    		LOG_INF("---- %s end  ----", p_label);\
    	})
    
    LOG_MODULE_REGISTER(persistent_key_usage, LOG_LEVEL_DBG);
    
    #define SEC_LIB_CSR_SUBJECT_NAME        "C=US,O=Test,OU=Test,CN=Test"
    uint8_t m_csr_buffer[4096];
    static 	mbedtls_pk_context m_private_key_context;
    static 	psa_key_handle_t key_handle;
    
    /* ====================================================================== */
    /*			Global variables/defines for the persistent key  example	  */
    
    /* The key id for the persistent key. The macros PSA_KEY_ID_USER_MIN and
     * PSA_KEY_ID_USER_MAX define the range of freely available key ids.
     */
    #define SAMPLE_PERS_KEY_ID PSA_KEY_ID_USER_MIN
    
    
    
    /* ====================================================================== */
    
    int crypto_init(void)
    {
    	psa_status_t status;
    
    	/* Initialize PSA Crypto */
    	status = psa_crypto_init();
    	if (status != PSA_SUCCESS)
    		return APP_ERROR;
    
    	return APP_SUCCESS;
    }
    
    int crypto_finish(void)
    {
    	psa_status_t status;
    	
    	/* Destroy the key handle */
    	status = psa_destroy_key(key_handle);
    	if (status != PSA_SUCCESS) {
    		LOG_INF("psa_destroy_key failed! (Error: %d)", status);
    		return APP_ERROR;
    	}
    
    	return APP_SUCCESS;
    }
    
    int generate_prersistent_key(void)
    {
    	psa_status_t status;
    
    	LOG_INF("Generating random persistent AES key...");
    
    	/* 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_MESSAGE | PSA_KEY_USAGE_DERIVE);
        psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_VOLATILE);
        psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
        psa_set_key_type(&key_attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
        psa_set_key_bits(&key_attributes, 256);
    
        /* Persistent key specific settings */
    	psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_PERSISTENT);
    	psa_set_key_id(&key_attributes, SAMPLE_PERS_KEY_ID);
    
    	// Try to destroy key (in case it previously created).
    	psa_destroy_key(SAMPLE_PERS_KEY_ID);
    
    	/* Generate a random AES key with persistent lifetime. The key can be used for
    	 * encryption/decryption using the key_handle.
    	 */
    	status = psa_generate_key(&key_attributes, &key_handle);
    	if (status != PSA_SUCCESS) {
    		LOG_INF("psa_generate_key failed! (Error: %d)", status);
    		return APP_ERROR;
    	}
    	
    	/* After the key handle is acquired the attributes are not needed */
    	psa_reset_key_attributes(&key_attributes);
    
    	LOG_INF("Persistent key generated successfully!");
    
    	return APP_SUCCESS;
    }
    
    int generate_csr(void)
    {
    	int status;
    	int16_t lenght_of_csr;
    	
    	mbedtls_x509write_csr csr_ctx;
    	mbedtls_entropy_context     m_entropy_ctx;
    	mbedtls_ctr_drbg_context    m_ctr_drbg_ctx;
    
    	// Initialize mbedtls_pk_context and "load" previously created key into it.
    	mbedtls_pk_init(&m_private_key_context); 
    	mbedtls_pk_setup_opaque(&m_private_key_context, key_handle);
    
    	// Initialize drbg context.
    	mbedtls_entropy_init(&m_entropy_ctx);
        mbedtls_ctr_drbg_init(&m_ctr_drbg_ctx);
        status = mbedtls_ctr_drbg_seed(&m_ctr_drbg_ctx, mbedtls_entropy_func, &m_entropy_ctx, NULL, 0);
        if(status != 0)
        {
            return APP_ERROR;
        }
    
    	// Prepare CSR context.
    	mbedtls_x509write_csr_init(&csr_ctx);
        mbedtls_x509write_csr_set_key_usage(&csr_ctx, MBEDTLS_X509_KU_DIGITAL_SIGNATURE);
        mbedtls_x509write_csr_set_subject_name(&csr_ctx, SEC_LIB_CSR_SUBJECT_NAME);
        mbedtls_x509write_csr_set_md_alg(&csr_ctx, MBEDTLS_MD_SHA256);
        mbedtls_x509write_csr_set_key(&csr_ctx, &m_private_key_context);
    
    	// Create CSR.
        lenght_of_csr = mbedtls_x509write_csr_der(&csr_ctx, m_csr_buffer, sizeof(m_csr_buffer), mbedtls_ctr_drbg_random, &m_ctr_drbg_ctx);
    	if(lenght_of_csr < 0)
    	{
    		return APP_ERROR;
    	}
    
    	return APP_SUCCESS;
    }
    
    int main(void)
    {
    	int status;
    
    	LOG_INF("Starting persistent key example...");
    
    	status = crypto_init();
    	if (status != APP_SUCCESS) {
    		LOG_INF(APP_ERROR_MESSAGE);
    		return APP_ERROR;
    	}
    
    	status = generate_prersistent_key();
    	if (status != APP_SUCCESS) {
    		LOG_INF(APP_ERROR_MESSAGE);
    		return APP_ERROR;
    	}
    
    	status = generate_csr();
    	if (status != APP_SUCCESS) {
    		LOG_INF(APP_ERROR_MESSAGE);
    		return APP_ERROR;
    	}
    
    	status = crypto_finish();
    	if (status != APP_SUCCESS) {
    		LOG_INF(APP_ERROR_MESSAGE);
    		return APP_ERROR;
    	}
    
    	LOG_INF(APP_SUCCESS_MESSAGE);
    
    	return APP_SUCCESS;
    }
    

    # 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
    
    # Enable loging using RTT and UART
    CONFIG_CONSOLE=y
    CONFIG_LOG=y
    CONFIG_USE_SEGGER_RTT=y
    CONFIG_LOG_BACKEND_RTT=y
    CONFIG_LOG_BACKEND_UART=y
    CONFIG_LOG_BUFFER_SIZE=15360
    CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=15360
    
    # 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
    CONFIG_PSA_NATIVE_ITS=y
    
    CONFIG_MBEDTLS_ENABLE_HEAP=y
    CONFIG_MBEDTLS_HEAP_SIZE=8192
    CONFIG_PSA_CRYPTO_DRIVER_OBERON=n
    CONFIG_PSA_CRYPTO_DRIVER_CC3XX=y
    
    
    CONFIG_NRF_SECURITY=y
    
    CONFIG_MBEDTLS_LIBRARY_NRF_SECURITY=y
    CONFIG_MBEDTLS_USE_PSA_CRYPTO=y
    CONFIG_MBEDTLS_PSA_CRYPTO_C=y
    CONFIG_MBEDTLS_PSA_CRYPTO_STORAGE_C=y
    
    CONFIG_MBEDTLS_PK_C=y
    CONFIG_MBEDTLS_RSA_C=y
    CONFIG_MBEDTLS_PKCS1_V15=y
    CONFIG_MBEDTLS_SHA256_C=y
    CONFIG_MBEDTLS_ECP_C=y
    CONFIG_MBEDTLS_ECDSA_C=y
    CONFIG_MBEDTLS_ECDH_C=y
    CONFIG_MBEDTLS_CTR_DRBG_C=y
    CONFIG_MBEDTLS_ENTROPY_C=y
    CONFIG_MBEDTLS_PK_PARSE_C=y
    
    CONFIG_MBEDTLS_TLS_LIBRARY=y
    CONFIG_MBEDTLS_X509_LIBRARY=y
    CONFIG_MBEDTLS_PK_WRITE_C=y
    CONFIG_MBEDTLS_X509_CREATE_C=y
    CONFIG_MBEDTLS_X509_CSR_WRITE_C=y
    CONFIG_MBEDTLS_LEGACY_CRYPTO_C=y
    CONFIG_MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG=n

    With the configuration from the prj file mentioned in the first post I got the following errors:
    - "undefined reference to `mbedtls_pk_setup_opaque" - MBEDTLS_USE_PSA_CRYPTO was not defined in pk.h, although it was enabled with CONFIG_MBEDTLS_USE_PSA_CRYPTO=y.
    - The key generation (psa_generate_key) failing because in psa_validate_key_persistence MBEDTLS_PSA_CRYPTO_STORAGE_C was not defined (also enabled with the CONFIG_MBEDTLS_PSA_CRYPTO_STORAGE_C=y)

    I solved both problems only when I included in nrf-config-user.h

    #define MBEDTLS_PSA_CRYPTO_STORAGE_C 1
    #define MBEDTLS_USE_PSA_CRYPTO 1

    If I build the nrf/samples/crypto/ecdsa sample with your above configs except for EXTERNAL_RNG (it makes the build fail), I get MBEDTLS_USE_PSA_CRYPTO=y

    If I understood you correctly, you didn't have to additionally include MBEDTLS_USE_PSA_CRYPTO through the nrf-config-user.h file?

    Currently, there is the following problem with the mbedtls_x509write_csr_der function, that I haven't solved yet:

    Such function returns the value PSA_ERROR_NOT_SUPPORTED (from the inside call psa_driver_wrapper_export_public_key).

    Can you help me find what is missing in the configuration?

    Thanks.

  • Darko said:
    nrf-config-user.h:

    Can you explain where this file is located and how you include it in the project?

    Darko said:

    Currently, there is the following problem with the mbedtls_x509write_csr_der function, that I haven't solved yet:

    Such function returns the value PSA_ERROR_NOT_SUPPORTED (from the inside call psa_driver_wrapper_export_public_key).

    Can you help me find what is missing in the configuration?

    Maybe try to set CONFIG_MBEDTLS_KEY_EXCHANGE_ALL_ENABLED?

    Also, have a look at your build log. Sometimes CONFIGs will be ignored from the prj.conf, which appears as warnings in the log. Maybe some of the configurations you thought you had set are ignored?

    I can copy the code you posted, and I did, but it is hard to know if I missed any changes. Could you zip the test you have and upload it? Then I can test the exact same project as you run and see if I can find anything.

  • Hello Sigurd,

    I completely agree it would be best to package the whole example and upload it.
    Unfortunately, our firewall does not allow us to upload such content here (regardless of whether is related to the demonstration of common/basic functionalities from sdk).

    I was hoping you could easily reproduce the problem by simply updating the main.c and .prj.conf files from the nrf_230\v2.3.0\nrf\samples\crypto\persistent_key_usage\ demo. These are the only changes I made to the persistent_key_usage demo in order to reproduce the problems from the project I'm working on.

    In any case, thank you for your help, I will consult with my colleagues here on how to proceed with this.

Related