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);
#endifSecurity.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;
}