nRF54L15 - Failed to copy derived key into KMU

Hello, 
I am currently trying to use kmu to store a series of derived keys. My current systems derives AES keys and attempts to store it into KMU. As psa_key_derivation_output_key is currently not supported as a key storage method, my current plan is to copy the derived key into KMU as a default or revokable persistent key. When I try to do so with the lifetime set as psa_key_lifetime_from_persistence_and_location, the psa_copy_key operation returns an error of -134. This error does not occur when using either psa_lifetime_volatile or psa_lifetime_persistent. 

  - SoC: nRF54L15 (DK)
  - nRF Connect SDK: v2.9.2

prj.conf:

# Toolchain/language
CONFIG_CPP=y
CONFIG_STD_CPP20=y


CONFIG_MAIN_STACK_SIZE=32768
CONFIG_HEAP_MEM_POOL_SIZE=32768
CONFIG_CONSOLE=y
CONFIG_LOG=y


CONFIG_TFM_PROFILE_TYPE_NOT_SET=y
CONFIG_TFM_PARTITION_PLATFORM=y
CONFIG_TFM_PARTITION_CRYPTO=y
CONFIG_TFM_PARTITION_PROTECTED_STORAGE=n
CONFIG_TFM_PARTITION_INTERNAL_TRUSTED_STORAGE=y
CONFIG_TFM_IPC=y
CONFIG_TFM_ISOLATION_LEVEL=2

#disable these for i2c enabling
CONFIG_TFM_LOG_LEVEL_SILENCE=y

# Nordic security (mbedTLS + PSA shim)
CONFIG_NRF_SECURITY=y

# PSA + mbedTLS (software path)
CONFIG_MBEDTLS=y
CONFIG_MBEDTLS_LEGACY_CRYPTO_C=n
CONFIG_MBEDTLS_ENABLE_HEAP=y
CONFIG_MBEDTLS_HEAP_SIZE=32768
CONFIG_MBEDTLS_PSA_CRYPTO_C=y
CONFIG_MBEDTLS_PSA_CRYPTO_STORAGE_C=n
CONFIG_MBEDTLS_AES_C=y


# PSA "wants" (what algorithms/types you’ll use)
CONFIG_PSA_WANT_KEY_TYPE_AES=y
CONFIG_PSA_WANT_GENERATE_RANDOM=y
CONFIG_PSA_WANT_ALG_GCM=y

# Persistent key storage without TF-M
CONFIG_TFM_NRF_NS_STORAGE=y
CONFIG_TFM_PARTITION_CRYPTO=y
CONFIG_TFM_IPC=y
CONFIG_TFM_PARTITION_INTERNAL_TRUSTED_STORAGE=y

# (Optional) try HW driver later; start with software first
CONFIG_PSA_CRYPTO_DRIVER_CRACEN=y
CONFIG_PSA_CRYPTO_DRIVER_OBERON=n
CONFIG_PSA_USE_CRACEN_KEY_DERIVATION_DRIVER=y
CONFIG_PSA_USE_CRACEN_KEY_MANAGEMENT_DRIVER=y

CONFIG_PSA_WANT_AES_KEY_SIZE_128=y
CONFIG_ENTROPY_GENERATOR=y
CONFIG_ENTROPY_NRF5_RNG=y
CONFIG_MBEDTLS_ENTROPY_ENABLED=y
CONFIG_MBEDTLS_CTR_DRBG_ENABLED=y

# Enable logging
CONFIG_CONSOLE=y
CONFIG_LOG=y
CONFIG_LOG_BUFFER_SIZE=32768
CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=2048
# If you use shell/serial backend:
CONFIG_SHELL_BACKEND_SERIAL_LOG_MESSAGE_QUEUE_SIZE=2048

CONFIG_PSA_WANT_ALG_HKDF=y
CONFIG_PSA_WANT_ALG_SHA_256=y
CONFIG_PSA_WANT_KEY_TYPE_DERIVE=y

CONFIG_TIMING_FUNCTIONS=y



Security.hpp:
#pragma once
#ifndef ZEPHYR_INCLUDE_SECURITY
#include <psa/crypto.h>
#include <psa/crypto_extra.h>
#include <cracen_psa.h>

int aead_derive_key(psa_key_id_t root_key,           // opened handle to root
                               const uint8_t *seed, size_t seed_len,  // your initialization seed
                               const uint8_t *info, size_t info_len,  // context/label, optional
                               size_t child_bits,                     // 128/192/256
                               psa_algorithm_t child_alg,             // e.g., PSA_ALG_GCM
                               psa_key_id_t *out_child_key);

int aead_set_key(const uint8_t *key, size_t key_len, psa_key_id_t *AEAD_KEY_ID);

int aead_generate_nonce(uint8_t *nonce /*size AEAD_NONCE_LEN*/);

int aead_encrypt(const uint8_t *nonce, size_t nonce_len,
                 const uint8_t *aad, size_t aad_len,
                 const uint8_t *pt,  size_t pt_len,
                 uint8_t *out,       size_t out_size,
                 size_t *out_len, psa_key_id_t AEAD_KEY_ID);

int aead_decrypt(const uint8_t *nonce, size_t nonce_len,
                 const uint8_t *aad, size_t aad_len,
                 const uint8_t *ct_and_tag, size_t ct_and_tag_len,
                 uint8_t *pt_out, size_t pt_out_size,
                 size_t *pt_out_len, psa_key_id_t AEAD_KEY_ID);

int aead_destroy_key(psa_key_id_t AEAD_KEY_ID);
#endif


Security.cpp:
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <stdio.h>
#include <stdlib.h>
#include "security.hpp"

LOG_MODULE_REGISTER(security, LOG_LEVEL_DBG);
// --- Tunables ---
#define AEAD_ALG        PSA_ALG_GCM           // AES-GCM
#define AEAD_KEY_BITS   128                   // 128 or 256
#define AEAD_TAG_LEN    16                    // bytes: 12..16 typical; 16 recommended
#define AEAD_NONCE_LEN  12                    // AES-GCM best practice: 12-byte nonce

static psa_key_id_t ROOT_KEY = 2;
//static psa_key_id_t AEAD_KEY_ID;

// --- One-time init ---
int crypto_init_once(void) {
    static bool inited = false;
    if (inited) return 0;
    psa_status_t st = psa_crypto_init();
    if (st != PSA_SUCCESS) {
        LOG_ERR("psa_crypto_init failed: %d", st);
        return -1;
    }
    inited = true;
    return 0;
}

/* ***************************************************** ROOT KEY FUNCTIONS *************************************************************** */


int aead_derive_key(psa_key_id_t root_key,           // opened handle to root
                               const uint8_t *seed, size_t seed_len,  // your initialization seed
                               const uint8_t *info, size_t info_len,  // context/label, optional
                               size_t child_bits,                     // 128/192/256
                               psa_algorithm_t child_alg,             // e.g., PSA_ALG_GCM
                               psa_key_id_t *out_child_key){
    psa_status_t st;
    if (crypto_init_once() != 0) return -1;
    psa_key_id_t dev_key;
    psa_key_derivation_operation_t key_devop = PSA_KEY_DERIVATION_OPERATION_INIT; 

    st = psa_key_derivation_setup(&key_devop, PSA_ALG_HKDF(PSA_ALG_SHA_256)); 
    if (st != PSA_SUCCESS){LOG_ERR("KEY DEV SETUP FAILED %d", st); return -1;}

    st = psa_key_derivation_input_bytes(&key_devop, PSA_KEY_DERIVATION_INPUT_SALT, seed, seed_len); //Set up seed generation for consistent results
    if (st != PSA_SUCCESS){LOG_ERR("SALT INPUT SETUP FAILED %d", st); return -1;}

    st = psa_key_derivation_input_key(&key_devop, PSA_KEY_DERIVATION_INPUT_SECRET, root_key);
    if (st != PSA_SUCCESS){LOG_ERR("ROOT KEY INPUT SETUP FAILED %d", st); return -1;}
    
    if (info && info_len) {
        st = psa_key_derivation_input_bytes(&key_devop, PSA_KEY_DERIVATION_INPUT_INFO, info, info_len);
        if (st != PSA_SUCCESS){LOG_ERR("INFO SETUP FAILED %d", st); return -1;}
    }

    st = psa_key_derivation_set_capacity(&key_devop, 32);
    if (st != PSA_SUCCESS){LOG_ERR("KEY SIZE SETUP FAILED %d", st); return -1;}

    psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT;
    psa_set_key_type(&attr, PSA_KEY_TYPE_AES);
    psa_set_key_bits(&attr, child_bits);
    psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_COPY | PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
    psa_set_key_algorithm(&attr, child_alg);
    psa_set_key_lifetime(&attr, PSA_KEY_LIFETIME_VOLATILE);

    st = psa_key_derivation_output_key(&attr, &key_devop, &dev_key);
    if (st != PSA_SUCCESS){LOG_ERR("KEY DERIVATION PROCESS FAILED %d", st); return -1;}
    psa_reset_key_attributes(&attr);
    psa_key_derivation_abort(&key_devop);

    //Derived keys cannot be loaded immediately into KMU slots, it must be loaded into KMU post derivation using import
    psa_key_attributes_t kmu = PSA_KEY_ATTRIBUTES_INIT;
    psa_set_key_type(&kmu, PSA_KEY_TYPE_AES);
    psa_set_key_bits(&kmu, child_bits);
    //psa_set_key_lifetime(&kmu, PSA_KEY_LIFETIME_PERSISTENT);
    psa_set_key_lifetime(&kmu, PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_PERSISTENCE_DEFAULT, PSA_KEY_LOCATION_CRACEN_KMU));
    psa_set_key_id(&kmu, PSA_KEY_HANDLE_FROM_CRACEN_KMU_SLOT(CRACEN_KMU_KEY_USAGE_SCHEME_PROTECTED, /*slot*/ 8));
    psa_set_key_usage_flags(&kmu, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
    psa_set_key_algorithm(&kmu, child_alg);
    st = psa_copy_key(dev_key, &kmu, out_child_key);
    if (st != PSA_SUCCESS){LOG_ERR("KEY COPY PROCESS FAILED %d", st); return -1;}
    psa_reset_key_attributes(&kmu);
    psa_destroy_key(dev_key);
    return 0;
}


/* ***************************************************** GENERAL KEY FUNCTIONS ***************************************************************** */
// --- Import (or replace) a persistent AEAD key ---
// key: raw AES key bytes (16 for 128-bit, 32 for 256-bit)
int aead_set_key(const uint8_t *key, size_t key_len, psa_key_id_t *AEAD_KEY_ID)
{
    psa_status_t st;
    if (crypto_init_once() != 0) return -1;

    psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT;
    psa_set_key_type(&attr, PSA_KEY_TYPE_DERIVE);
    psa_set_key_bits(&attr, 128);
    psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_DERIVE);
    psa_set_key_algorithm(&attr, PSA_ALG_HKDF(PSA_ALG_SHA_256));
    psa_set_key_id(&attr, ROOT_KEY);
    psa_set_key_lifetime(&attr, PSA_KEY_LIFETIME_PERSISTENT);

    st = psa_import_key(&attr, key, key_len, AEAD_KEY_ID);

    if (st == PSA_ERROR_ALREADY_EXISTS) {
        LOG_WRN("KEY ARLEADY EXISTS, REPLACING");
        (void)psa_destroy_key(*AEAD_KEY_ID);
        st = psa_import_key(&attr, key, key_len, AEAD_KEY_ID);
    }
    psa_reset_key_attributes(&attr);

    if (st != PSA_SUCCESS) {
        LOG_ERR("AEAD key import failed: %d", st);
        return -1;
    }
    LOG_INF("AEAD key stored (id=0x%lx)", (unsigned long)AEAD_KEY_ID);
    return 0;
}

// Optional: generate a random 12-byte nonce (GCM best practice)
int aead_generate_nonce(uint8_t *nonce /*size AEAD_NONCE_LEN*/)
{
    if (crypto_init_once() != 0) return -1;
    psa_status_t st = psa_generate_random(nonce, AEAD_NONCE_LEN);
    if (st != PSA_SUCCESS) {
        LOG_ERR("nonce gen failed: %d", st);
        return -1;
    }
    return 0;
}

//Kept open ended in case we want other encryption/decryption params or types
/**
 * @brief Encrypts data using aead
 *
 * This function performs all nessecary steps for aead encryption with 
 * every field open 
 *
 * @param nounce Pointer to nonce counter
 * @param nonce_len length of nonce counter
 * @param aad pointer to additional info to be added when encrypting
 * @param aad_len length of aad buffer
 * @param pt pointer to string to be encrypted
 * @param pt_len length of inputs string
 * @param out pointer to buffer for encrypted output
 * @param out_size size of ouptut buffer, must be pt_len+16 bytes at minimum
 * @param out_len variable to store the length of the encrypted message, typically string length + 16 bytes
 * @param AEAD_KEY_ID KMU key handle to be used for encryption
 *
 * @return Error status
 */
int aead_encrypt(const uint8_t *nonce, size_t nonce_len,
                 const uint8_t *aad, size_t aad_len,
                 const uint8_t *pt,  size_t pt_len,
                 uint8_t *out,       size_t out_size,
                 size_t *out_len, psa_key_id_t AEAD_KEY_ID)
{
    if (crypto_init_once() != 0) return -1;
    if (nonce_len != AEAD_NONCE_LEN) { LOG_ERR("bad nonce len"); return -1; }
    if (out_size < pt_len + AEAD_TAG_LEN) { LOG_ERR("out buf too small"); return -1; }

    psa_status_t st = psa_aead_encrypt(AEAD_KEY_ID, 
        AEAD_ALG,
        nonce, 
        nonce_len,
        aad, 
        aad_len,
        pt, 
        pt_len,
        out, 
        out_size, 
        out_len);

    if (st != PSA_SUCCESS) {
        LOG_ERR("aead encrypt failed: %d", st);
        return -1;
    }
    if (*out_len != pt_len + AEAD_TAG_LEN) {
        // PSA appends the tag; length should equal pt_len + tag_len
        LOG_ERR("unexpected AEAD out_len=%u", (unsigned)*out_len);
    }
    return 0;
}

/**
 * @brief Decrypts data using aead
 *
 * This function performs all nessecary steps for aead decryption with 
 * every field open 
 *
 * @param nounce Pointer to nonce counter
 * @param nonce_len length of nonce counter
 * @param aad pointer to additional info to be added when encrypting
 * @param aad_len length of aad buffer
 * @param ct_and_tag pointer to encrypted ciphertext buffer
 * @param ct_and_tag_len length of ciphertext buffer
 * @param pt_out pointer to buffer for encrypted output
 * @param pt_out_size size of ouptut buffer, must be pt_len-16 bytes at minimum
 * @param pt_out_len variable to store the length of the encrypted message, typically string length - 16 bytes
 * @param AEAD_KEY_ID KMU key handle to be used for encryption
 *
 * @return Error status
 */
int aead_decrypt(const uint8_t *nonce, size_t nonce_len,
                 const uint8_t *aad, size_t aad_len,
                 const uint8_t *ct_and_tag, size_t ct_and_tag_len,
                 uint8_t *pt_out, size_t pt_out_size,
                 size_t *pt_out_len, psa_key_id_t AEAD_KEY_ID)
{
    if (crypto_init_once() != 0) return -1;
    if (nonce_len != AEAD_NONCE_LEN) { LOG_INF("bad nonce len"); return -1; }
    if (ct_and_tag_len < AEAD_TAG_LEN) { LOG_INF("ciphertext too short"); return -1; }
    size_t ct_len = ct_and_tag_len - AEAD_TAG_LEN;
    if (pt_out_size < ct_len) { LOG_INF("pt buf too small"); return -1; }

    psa_status_t st = psa_aead_decrypt(AEAD_KEY_ID, AEAD_ALG,
        nonce, nonce_len,
        aad, aad_len,
        ct_and_tag, ct_and_tag_len,
        pt_out, pt_out_size, pt_out_len);

    if (st != PSA_SUCCESS) {
        // Includes auth failures (e.g., bad tag, wrong key/nonce/AAD)
        LOG_ERR("aead decrypt failed: %d", st);
        return -1;
    }
    return 0;
}

// Optional: destroy the persistent key
int aead_destroy_key(psa_key_id_t AEAD_KEY_ID)
{
    if (crypto_init_once() != 0) return -1;
    psa_status_t st = psa_destroy_key(AEAD_KEY_ID);
    if (st != PSA_SUCCESS) {
        LOG_ERR("destroy AEAD key failed: %d", st);
        return -1;
    }
    return 0;
}


Main.cpp:
#include <zephyr/kernel.h>
#include <stdio.h>
#include "security.hpp"
#include <cstddef>
#include <zephyr/logging/log.h>
#include <zephyr/timing/timing.h>
#include <ncs_version.h>
extern "C" {
extern const uint8_t _binary_hello_bin_start[];
extern const uint8_t _binary_hello_bin_end[];
extern const uint8_t _binary_hello_bin_size[];
}
LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);
// --- Tunables ---
#define AEAD_ALG        PSA_ALG_GCM           // AES-GCM
#define AEAD_KEY_BITS   128                   // 128 or 256
#define AEAD_TAG_LEN    16                    // bytes: 12..16 typical; 16 recommended
#define AEAD_NONCE_LEN  12                    // AES-GCM best practice: 12-byte nonce

int main(void)
{
	LOG_INF("nRF Connect SDK Version: %x\n", NCS_VERSION_NUMBER);
	timing_t start_time, end_time;
    uint64_t total_cycles;
    uint64_t total_ns;

	timing_init();
    timing_start();

	uint8_t aead_key[16] = {0xA3, 0x5F, 0x2C, 0x91, 0x7B, 0xD4, 0x08, 0x6E, 0xC1, 0x59, 0x3A, 0xE7, 0x44, 0x0D, 0xB8, 0x2F};
	psa_key_id_t dad;
	aead_set_key(aead_key, sizeof(aead_key), &dad);
	uint8_t rand_seed[5] = {0x17, 0x38, 0x80, 0x08, 0x15};
	uint8_t info[1] = {0x00};
	psa_key_id_t gen_key0;
	aead_derive_key(dad, rand_seed, sizeof(rand_seed), info,1, 128, PSA_ALG_GCM, &gen_key0);

	// 2) Prepare inputs
	uint8_t nonce[AEAD_NONCE_LEN] = {0};
	//aead_generate_nonce(nonce);

	const uint8_t aad[] = {0x00, 0x00};           // optional AAD (can be NULL/0)
	const uint8_t pt[]  = "hello world";          // plaintext

	uint8_t out[5000] = {0};
	size_t out_len = 0;
	uint8_t recovered[5000] = {0};
	size_t recovered_len = 0;

	long int encrypt_speed = 0;
	long int decrypt_speed = 0;

	//for (int message_size = 0; message_size < 25000; message_size+=100){
		for (int i = 0; i < 5; i++){
		// 3) Encrypt (out = ciphertext||tag)
			start_time = timing_counter_get();  // ms since boot
			int ok = aead_encrypt(nonce, sizeof(nonce), aad, sizeof(aad),
						pt, sizeof(pt), out, sizeof(out), &out_len, gen_key0);
			end_time = timing_counter_get();

			total_cycles = timing_cycles_get(&start_time, &end_time);
			total_ns = timing_cycles_to_ns(total_cycles);
			/*
			LOG_INF("Encrypt status: %d", ok);
			LOG_INF("Encryption size: %d -> %d", sizeof(pt), out_len);
			LOG_INF("Encryption speed(ns): %d", total_ns);*/
			for (int i = 0; i < out_len; i++){
				printk("%x", out[i]);
			}
			printk("\n");
			encrypt_speed += total_ns;
			// 4) Decrypt+verify
			start_time = timing_counter_get();
			ok = aead_decrypt(nonce, sizeof(nonce), aad, sizeof(aad),
								out, out_len, recovered, sizeof(recovered), &recovered_len, gen_key0);
			end_time = timing_counter_get();

			total_cycles = timing_cycles_get(&start_time, &end_time);
			total_ns = timing_cycles_to_ns(total_cycles);
			/*
			LOG_INF("Decrypt status: %d", ok);
			LOG_INF("Decryption size: %d -> %d\n", out_len, recovered_len);
			LOG_INF("Decryption speed(ns): %d\n", total_ns);*/
			for (int i = 0; i < recovered_len; i++){
				printk("%c", recovered[i]);
			}
			printk("\n");
			decrypt_speed += total_ns;
			nonce[11] += 1;
		}
		
		//printk("%d\n", encrypt_speed);
		//encrypt_speed = 0;
		//decrypt_speed = 0;
	//}
	LOG_INF("COMPLETE %d", sizeof(pt));
	aead_destroy_key(dad);
	return 0;
}

Related