Crypto: Add support for key APIs for HUK

Adds support for the psa_open_key and psa_close_key APIs only when the
key identifier corresponds to the HUK. Also makes it possible to derive
another key from the HUK by calling the PSA key derivation functions
with the HUK key handle as the input key.

Change-Id: I6992daaca76b87412b334a852383b2221a5181a9
Signed-off-by: Jamie Fox <jamie.fox@arm.com>
diff --git a/interface/include/tfm_crypto_defs.h b/interface/include/tfm_crypto_defs.h
index 91af175..5a380c6 100644
--- a/interface/include/tfm_crypto_defs.h
+++ b/interface/include/tfm_crypto_defs.h
@@ -59,6 +59,8 @@
  */
 enum {
     TFM_CRYPTO_ALLOCATE_KEY_SID = (0u),
+    TFM_CRYPTO_OPEN_KEY_SID,
+    TFM_CRYPTO_CLOSE_KEY_SID,
     TFM_CRYPTO_IMPORT_KEY_SID,
     TFM_CRYPTO_DESTROY_KEY_SID,
     TFM_CRYPTO_GET_KEY_INFORMATION_SID,
@@ -117,6 +119,19 @@
 #define TFM_CRYPTO_INVALID_HANDLE (0x0u)
 
 /**
+ * \brief The persistent key identifier that refers to the hardware unique key.
+ *
+ */
+#define TFM_CRYPTO_KEY_ID_HUK (0xFFFF815Bu)
+
+/**
+ * \brief The algorithm identifier that refers to key derivation from the
+ *        hardware unique key.
+ *
+ */
+#define TFM_CRYPTO_ALG_HUK_DERIVATION ((psa_algorithm_t)0xB0000F00)
+
+/**
  * \brief Define miscellaneous literal constants that are used in the service
  *
  */
diff --git a/interface/include/tfm_veneers.h b/interface/include/tfm_veneers.h
index 23b4f8d..5b0e2bf 100644
--- a/interface/include/tfm_veneers.h
+++ b/interface/include/tfm_veneers.h
@@ -40,6 +40,8 @@
 
 /******** TFM_SP_CRYPTO ********/
 psa_status_t tfm_tfm_crypto_allocate_key_veneer(psa_invec *in_vec, size_t in_len, psa_outvec *out_vec, size_t out_len);
+psa_status_t tfm_tfm_crypto_open_key_veneer(psa_invec *in_vec, size_t in_len, psa_outvec *out_vec, size_t out_len);
+psa_status_t tfm_tfm_crypto_close_key_veneer(psa_invec *in_vec, size_t in_len, psa_outvec *out_vec, size_t out_len);
 psa_status_t tfm_tfm_crypto_import_key_veneer(psa_invec *in_vec, size_t in_len, psa_outvec *out_vec, size_t out_len);
 psa_status_t tfm_tfm_crypto_destroy_key_veneer(psa_invec *in_vec, size_t in_len, psa_outvec *out_vec, size_t out_len);
 psa_status_t tfm_tfm_crypto_get_key_information_veneer(psa_invec *in_vec, size_t in_len, psa_outvec *out_vec, size_t out_len);
diff --git a/interface/src/tfm_crypto_func_api.c b/interface/src/tfm_crypto_func_api.c
index 176c192..87c1a64 100644
--- a/interface/src/tfm_crypto_func_api.c
+++ b/interface/src/tfm_crypto_func_api.c
@@ -53,12 +53,20 @@
                           psa_key_id_t id,
                           psa_key_handle_t *handle)
 {
-    (void)lifetime;
-    (void)id;
-    (void)handle;
+    const struct tfm_crypto_pack_iovec iov = {
+        .sfn_id = TFM_CRYPTO_OPEN_KEY_SID,
+        .lifetime = lifetime,
+    };
+    psa_invec in_vec[] = {
+        {.base = &iov, .len = sizeof(struct tfm_crypto_pack_iovec)},
+        {.base = &id, .len = sizeof(psa_key_id_t)},
+    };
+    psa_outvec out_vec[] = {
+        {.base = handle, .len = sizeof(psa_key_handle_t)},
+    };
 
-    /* TODO: Persistent key APIs are not supported yet */
-    return PSA_ERROR_NOT_SUPPORTED;
+    return API_DISPATCH(tfm_crypto_open_key,
+                        TFM_CRYPTO_OPEN_KEY);
 }
 
 psa_status_t psa_create_key(psa_key_lifetime_t lifetime,
@@ -75,10 +83,16 @@
 
 psa_status_t psa_close_key(psa_key_handle_t handle)
 {
-    (void)handle;
+    const struct tfm_crypto_pack_iovec iov = {
+        .sfn_id = TFM_CRYPTO_CLOSE_KEY_SID,
+        .key_handle = handle,
+    };
+    psa_invec in_vec[] = {
+        {.base = &iov, .len = sizeof(struct tfm_crypto_pack_iovec)},
+    };
 
-    /* TODO: Persistent key APIs are not supported yet */
-    return PSA_ERROR_NOT_SUPPORTED;
+    return API_DISPATCH_NO_OUTVEC(tfm_crypto_close_key,
+                                  TFM_CRYPTO_CLOSE_KEY);
 }
 
 psa_status_t psa_import_key(psa_key_handle_t handle,
diff --git a/interface/src/tfm_crypto_ipc_api.c b/interface/src/tfm_crypto_ipc_api.c
index cf6152c..21f7816 100644
--- a/interface/src/tfm_crypto_ipc_api.c
+++ b/interface/src/tfm_crypto_ipc_api.c
@@ -75,12 +75,27 @@
 #if (TFM_CRYPTO_KEY_MODULE_DISABLED != 0)
     return PSA_ERROR_NOT_SUPPORTED;
 #else
-    (void)lifetime;
-    (void)id;
-    (void)handle;
+    psa_status_t status;
+    const struct tfm_crypto_pack_iovec iov = {
+        .sfn_id = TFM_CRYPTO_OPEN_KEY_SID,
+        .lifetime = lifetime,
+    };
+    psa_invec in_vec[] = {
+        {.base = &iov, .len = sizeof(struct tfm_crypto_pack_iovec)},
+        {.base = &id, .len = sizeof(psa_key_id_t)},
+    };
+    psa_outvec out_vec[] = {
+        {.base = handle, .len = sizeof(psa_key_handle_t)},
+    };
 
-    /* TODO: Persistent key APIs are not supported yet */
-    return PSA_ERROR_NOT_SUPPORTED;
+    PSA_CONNECT(TFM_CRYPTO);
+
+    status = API_DISPATCH(tfm_crypto_open_key,
+                          TFM_CRYPTO_OPEN_KEY);
+
+    PSA_CLOSE();
+
+    return status;
 #endif /* TFM_CRYPTO_KEY_MODULE_DISABLED */
 }
 
@@ -105,10 +120,23 @@
 #if (TFM_CRYPTO_KEY_MODULE_DISABLED != 0)
     return PSA_ERROR_NOT_SUPPORTED;
 #else
-    (void)handle;
+    psa_status_t status;
+    const struct tfm_crypto_pack_iovec iov = {
+        .sfn_id = TFM_CRYPTO_CLOSE_KEY_SID,
+        .key_handle = handle,
+    };
+    psa_invec in_vec[] = {
+        {.base = &iov, .len = sizeof(struct tfm_crypto_pack_iovec)},
+    };
 
-    /* TODO: Persistent key APIs are not supported yet */
-    return PSA_ERROR_NOT_SUPPORTED;
+    PSA_CONNECT(TFM_CRYPTO);
+
+    status = API_DISPATCH_NO_OUTVEC(tfm_crypto_close_key,
+                                    TFM_CRYPTO_CLOSE_KEY);;
+
+    PSA_CLOSE();
+
+    return status;
 #endif /* TFM_CRYPTO_KEY_MODULE_DISABLED */
 }
 
diff --git a/secure_fw/ns_callable/tfm_veneers.c b/secure_fw/ns_callable/tfm_veneers.c
index bfb01c3..c40973d 100644
--- a/secure_fw/ns_callable/tfm_veneers.c
+++ b/secure_fw/ns_callable/tfm_veneers.c
@@ -34,6 +34,8 @@
 
 /******** TFM_SP_CRYPTO ********/
 psa_status_t tfm_crypto_allocate_key(psa_invec *, size_t, psa_outvec *, size_t);
+psa_status_t tfm_crypto_open_key(psa_invec *, size_t, psa_outvec *, size_t);
+psa_status_t tfm_crypto_close_key(psa_invec *, size_t, psa_outvec *, size_t);
 psa_status_t tfm_crypto_import_key(psa_invec *, size_t, psa_outvec *, size_t);
 psa_status_t tfm_crypto_destroy_key(psa_invec *, size_t, psa_outvec *, size_t);
 psa_status_t tfm_crypto_get_key_information(psa_invec *, size_t, psa_outvec *, size_t);
@@ -160,6 +162,8 @@
 
 /******** TFM_SP_CRYPTO ********/
 TFM_VENEER_FUNCTION(TFM_SP_CRYPTO, tfm_crypto_allocate_key)
+TFM_VENEER_FUNCTION(TFM_SP_CRYPTO, tfm_crypto_open_key)
+TFM_VENEER_FUNCTION(TFM_SP_CRYPTO, tfm_crypto_close_key)
 TFM_VENEER_FUNCTION(TFM_SP_CRYPTO, tfm_crypto_import_key)
 TFM_VENEER_FUNCTION(TFM_SP_CRYPTO, tfm_crypto_destroy_key)
 TFM_VENEER_FUNCTION(TFM_SP_CRYPTO, tfm_crypto_get_key_information)
diff --git a/secure_fw/services/crypto/crypto_generator.c b/secure_fw/services/crypto/crypto_generator.c
index 9f37022..af9b014 100644
--- a/secure_fw/services/crypto/crypto_generator.c
+++ b/secure_fw/services/crypto/crypto_generator.c
@@ -15,8 +15,101 @@
  */
 #include "tfm_mbedcrypto_include.h"
 
+/* Required for mbedtls_calloc in tfm_crypto_huk_derivation */
+#include "mbedtls/platform.h"
+
 #include "tfm_crypto_api.h"
 #include "tfm_crypto_defs.h"
+#include "tfm_memory_utils.h"
+
+#include "platform/include/tfm_plat_crypto_keys.h"
+
+/**
+ * \brief Perform a key derivation operation from the hardware unique key (HUK).
+ *
+ * \note This function allows key derivation from the HUK to be implemented in
+ *       a platform-defined way by calling the TF-M platform function
+ *       tfm_plat_get_huk_derived_key.
+ *
+ * \param[in,out] generator     Generator object to set up
+ * \param[in]     key_handle    Handle to the secret key
+ * \param[in]     salt          Salt to use
+ * \param[in]     salt_length   Size of the salt buffer in bytes
+ * \param[in]     label         Label to use
+ * \param[in]     label_length  Size of the label buffer in bytes
+ * \param[in]     capacity      Maximum number of bytes that the generator will
+ *                              be able to provide
+ *
+ * \return Return values as described in \ref psa_status_t
+ */
+static psa_status_t tfm_crypto_huk_derivation(psa_crypto_generator_t *generator,
+                                              psa_key_handle_t key_handle,
+                                              const uint8_t *salt,
+                                              size_t salt_length,
+                                              const uint8_t *label,
+                                              size_t label_length,
+                                              size_t capacity)
+{
+    psa_status_t status;
+    enum tfm_plat_err_t plat_err;
+    int32_t partition_id;
+    uint8_t *partition_label;
+    psa_key_policy_t key_policy;
+
+    status = psa_get_key_policy(key_handle, &key_policy);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    /* Check that the input key has the correct policy */
+    if (key_policy.usage != PSA_KEY_USAGE_DERIVE ||
+        key_policy.alg != TFM_CRYPTO_ALG_HUK_DERIVATION) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* Concatenate the caller's partition ID with the supplied label to prevent
+     * two different partitions from deriving the same key.
+     */
+    status = tfm_crypto_get_caller_id(&partition_id);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+    partition_label = mbedtls_calloc(1, sizeof(partition_id) + label_length);
+    if (partition_label == NULL) {
+        return PSA_ERROR_INSUFFICIENT_MEMORY;
+    }
+    (void)tfm_memcpy(partition_label, &partition_id, sizeof(partition_id));
+    (void)tfm_memcpy(partition_label + sizeof(partition_id), label,
+                     label_length);
+
+    /* Set up the generator object to contain the raw derived key material, so
+     * that it can be directly extracted in psa_generator_import_key or
+     * psa_generator_read.
+     */
+    generator->alg = PSA_ALG_SELECT_RAW;
+    generator->ctx.buffer.data = mbedtls_calloc(1, capacity);
+    if (generator->ctx.buffer.data == NULL) {
+        mbedtls_free(partition_label);
+        (void)psa_generator_abort(generator);
+        return PSA_ERROR_INSUFFICIENT_MEMORY;
+    }
+    generator->ctx.buffer.size = capacity;
+    generator->capacity = capacity;
+
+    /* Derive key material from the HUK and output it to the generator buffer */
+    plat_err = tfm_plat_get_huk_derived_key(partition_label,
+                                            sizeof(partition_id) + label_length,
+                                            salt, salt_length,
+                                            generator->ctx.buffer.data,
+                                            generator->ctx.buffer.size);
+    mbedtls_free(partition_label);
+    if (plat_err != TFM_PLAT_ERR_SUCCESS) {
+        (void)psa_generator_abort(generator);
+        return PSA_ERROR_HARDWARE_FAILURE;
+    }
+
+    return PSA_SUCCESS;
+}
 
 /*!
  * \defgroup public_psa Public functions, PSA
@@ -242,8 +335,21 @@
 
     *handle_out = handle;
 
-    status = psa_key_derivation(generator, key_handle, alg, salt, salt_length,
-                                label, label_length, capacity);
+    /* If the caller requests that the TFM_CRYPTO_ALG_HUK_DERIVATION algorithm
+     * is used for key derivation, then redirect the request to the TF-M HUK
+     * derivation function, so that key derivation from the HUK can be
+     * implemented in a platform-defined way.
+     * FIXME: In the future, this should be replaced by the Mbed Crypto driver
+     * model.
+     */
+    if (alg == TFM_CRYPTO_ALG_HUK_DERIVATION) {
+        status = tfm_crypto_huk_derivation(generator, key_handle, salt,
+                                           salt_length, label, label_length,
+                                           capacity);
+    } else {
+        status = psa_key_derivation(generator, key_handle, alg, salt,
+                                    salt_length, label, label_length, capacity);
+    }
     if (status != PSA_SUCCESS) {
         /* Release the operation context, ignore if the operation fails. */
         (void)tfm_crypto_operation_release(handle_out);
diff --git a/secure_fw/services/crypto/crypto_key.c b/secure_fw/services/crypto/crypto_key.c
index 19f11c4..16b6bc1 100644
--- a/secure_fw/services/crypto/crypto_key.c
+++ b/secure_fw/services/crypto/crypto_key.c
@@ -32,6 +32,72 @@
 static struct tfm_crypto_handle_owner_s
                                  handle_owner[TFM_CRYPTO_MAX_KEY_HANDLES] = {0};
 #endif
+
+/**
+ * \brief Open a handle to the hardware unique key (HUK).
+ *
+ * \note As persistent keys are not yet supported by TF-M Crypto, this function
+ *       allocates an empty volatile key to get a valid key handle.
+ *
+ * \param[in]  lifetime  The lifetime of the key
+ * \param[out] handle    On success, a handle to the HUK
+ *
+ * \return Return values as described in \ref psa_status_t
+ */
+static psa_status_t tfm_crypto_open_huk(psa_key_lifetime_t lifetime,
+                                        psa_key_handle_t *key_handle)
+{
+    psa_status_t status;
+    int32_t partition_id;
+    uint32_t i;
+    psa_key_policy_t huk_policy = PSA_KEY_POLICY_INIT;
+
+    /* The HUK has a persistent lifetime */
+    if (lifetime != PSA_KEY_LIFETIME_PERSISTENT) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    for (i = 0; i < TFM_CRYPTO_MAX_KEY_HANDLES; i++) {
+        if (handle_owner[i].in_use == TFM_CRYPTO_NOT_IN_USE) {
+            break;
+        }
+    }
+
+    if (i == TFM_CRYPTO_MAX_KEY_HANDLES) {
+        return PSA_ERROR_INSUFFICIENT_MEMORY;
+    }
+
+    status = tfm_crypto_get_caller_id(&partition_id);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    /* Allocate a transient key to get a valid key handle */
+    status = psa_allocate_key(key_handle);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    /* The HUK can only be used to derive other keys */
+    huk_policy.usage = PSA_KEY_USAGE_DERIVE;
+    huk_policy.alg = TFM_CRYPTO_ALG_HUK_DERIVATION;
+    status = psa_set_key_policy(*key_handle, &huk_policy);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    /* Import zero data to the HUK handle to prevent further modification */
+    status = psa_import_key(*key_handle, PSA_KEY_TYPE_RAW_DATA, NULL, 0);
+
+    if (status == PSA_SUCCESS) {
+        handle_owner[i].owner = partition_id;
+        handle_owner[i].handle = *key_handle;
+        handle_owner[i].in_use = TFM_CRYPTO_IN_USE;
+    }
+
+    return status;
+}
+
 /*!
  * \defgroup public Public functions
  *
@@ -121,6 +187,79 @@
 #endif /* TFM_CRYPTO_KEY_MODULE_DISABLED */
 }
 
+psa_status_t tfm_crypto_open_key(psa_invec in_vec[],
+                                 size_t in_len,
+                                 psa_outvec out_vec[],
+                                 size_t out_len)
+{
+#if (TFM_CRYPTO_KEY_MODULE_DISABLED != 0)
+    return PSA_ERROR_NOT_SUPPORTED;
+#else
+    if ((in_len != 2) || (out_len != 1)) {
+        return PSA_ERROR_CONNECTION_REFUSED;
+    }
+
+    if ((in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec)) ||
+        (in_vec[1].len != sizeof(psa_key_id_t)) ||
+        (out_vec[0].len != sizeof(psa_key_handle_t))) {
+        return PSA_ERROR_CONNECTION_REFUSED;
+    }
+
+    const struct tfm_crypto_pack_iovec *iov = in_vec[0].base;
+    psa_key_lifetime_t lifetime = iov->lifetime;
+    psa_key_id_t id = *((psa_key_id_t *)in_vec[1].base);
+    psa_key_handle_t *key_handle = out_vec[0].base;
+
+    /* FIXME: Persistent key APIs are not supported in general, so use a
+     * specific implementation to open a handle to the HUK.
+     */
+    if (id == TFM_CRYPTO_KEY_ID_HUK) {
+        return tfm_crypto_open_huk(lifetime, key_handle);
+    } else {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+#endif /* TFM_CRYPTO_KEY_MODULE_DISABLED */
+}
+
+psa_status_t tfm_crypto_close_key(psa_invec in_vec[],
+                                  size_t in_len,
+                                  psa_outvec out_vec[],
+                                  size_t out_len)
+{
+#if (TFM_CRYPTO_KEY_MODULE_DISABLED != 0)
+    return PSA_ERROR_NOT_SUPPORTED;
+#else
+    (void)out_vec;
+
+    if ((in_len != 1) || (out_len != 0)) {
+        return PSA_ERROR_CONNECTION_REFUSED;
+    }
+
+    if (in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec)) {
+        return PSA_ERROR_CONNECTION_REFUSED;
+    }
+    const struct tfm_crypto_pack_iovec *iov = in_vec[0].base;
+
+    psa_key_handle_t key = iov->key_handle;
+    uint32_t index;
+    psa_status_t status = tfm_crypto_check_handle_owner(key, &index);
+
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = psa_close_key(key);
+
+    if (status == PSA_SUCCESS) {
+        handle_owner[index].owner = 0;
+        handle_owner[index].handle = 0;
+        handle_owner[index].in_use = TFM_CRYPTO_NOT_IN_USE;
+    }
+
+    return status;
+#endif /* TFM_CRYPTO_KEY_MODULE_DISABLED */
+}
+
 psa_status_t tfm_crypto_import_key(psa_invec in_vec[],
                                    size_t in_len,
                                    psa_outvec out_vec[],
diff --git a/secure_fw/services/crypto/tfm_crypto.yaml b/secure_fw/services/crypto/tfm_crypto.yaml
index f3c9d13..9f1379d 100644
--- a/secure_fw/services/crypto/tfm_crypto.yaml
+++ b/secure_fw/services/crypto/tfm_crypto.yaml
@@ -22,6 +22,20 @@
       "minor_policy": "STRICT"
     },
     {
+      "name": "TFM_CRYPTO_OPEN_KEY",
+      "signal": "TFM_CRYPTO_OPEN_KEY",
+      "non_secure_clients": true,
+      "minor_version": 1,
+      "minor_policy": "STRICT"
+    },
+    {
+      "name": "TFM_CRYPTO_CLOSE_KEY",
+      "signal": "TFM_CRYPTO_CLOSE_KEY",
+      "non_secure_clients": true,
+      "minor_version": 1,
+      "minor_policy": "STRICT"
+    },
+    {
       "name": "TFM_CRYPTO_IMPORT_KEY",
       "signal": "TFM_CRYPTO_IMPORT_KEY",
       "non_secure_clients": true,
diff --git a/secure_fw/services/crypto/tfm_crypto_api.h b/secure_fw/services/crypto/tfm_crypto_api.h
index da23f6f..34400ad 100644
--- a/secure_fw/services/crypto/tfm_crypto_api.h
+++ b/secure_fw/services/crypto/tfm_crypto_api.h
@@ -119,6 +119,8 @@
 
 #define LIST_TFM_CRYPTO_UNIFORM_SIGNATURE_API \
     X(tfm_crypto_allocate_key)                \
+    X(tfm_crypto_open_key)                    \
+    X(tfm_crypto_close_key)                   \
     X(tfm_crypto_import_key)                  \
     X(tfm_crypto_destroy_key)                 \
     X(tfm_crypto_get_key_information)         \
diff --git a/secure_fw/services/crypto/tfm_crypto_secure_api.c b/secure_fw/services/crypto/tfm_crypto_secure_api.c
index 68997b5..b50004b 100644
--- a/secure_fw/services/crypto/tfm_crypto_secure_api.c
+++ b/secure_fw/services/crypto/tfm_crypto_secure_api.c
@@ -95,12 +95,31 @@
 #if (TFM_CRYPTO_KEY_MODULE_DISABLED != 0)
     return PSA_ERROR_NOT_SUPPORTED;
 #else
-    (void)lifetime;
-    (void)id;
-    (void)handle;
+    psa_status_t status;
+    const struct tfm_crypto_pack_iovec iov = {
+        .sfn_id = TFM_CRYPTO_OPEN_KEY_SID,
+        .lifetime = lifetime,
+    };
+    psa_invec in_vec[] = {
+        {.base = &iov, .len = sizeof(struct tfm_crypto_pack_iovec)},
+        {.base = &id, .len = sizeof(psa_key_id_t)},
+    };
+    psa_outvec out_vec[] = {
+        {.base = handle, .len = sizeof(psa_key_handle_t)},
+    };
 
-    /* TODO: Persistent key APIs are not supported yet */
-    return PSA_ERROR_NOT_SUPPORTED;
+#ifdef TFM_PSA_API
+    PSA_CONNECT(TFM_CRYPTO);
+#endif
+
+    status = API_DISPATCH(tfm_crypto_open_key,
+                          TFM_CRYPTO_OPEN_KEY);
+
+#ifdef TFM_PSA_API
+    PSA_CLOSE();
+#endif
+
+    return status;
 #endif /* TFM_CRYPTO_KEY_MODULE_DISABLED */
 }
 
@@ -127,10 +146,27 @@
 #if (TFM_CRYPTO_KEY_MODULE_DISABLED != 0)
     return PSA_ERROR_NOT_SUPPORTED;
 #else
-    (void)handle;
+    psa_status_t status;
+    const struct tfm_crypto_pack_iovec iov = {
+        .sfn_id = TFM_CRYPTO_CLOSE_KEY_SID,
+        .key_handle = handle,
+    };
+    psa_invec in_vec[] = {
+        {.base = &iov, .len = sizeof(struct tfm_crypto_pack_iovec)},
+    };
 
-    /* TODO: Persistent key APIs are not supported yet */
-    return PSA_ERROR_NOT_SUPPORTED;
+#ifdef TFM_PSA_API
+    PSA_CONNECT(TFM_CRYPTO);
+#endif
+
+    status = API_DISPATCH_NO_OUTVEC(tfm_crypto_close_key,
+                                    TFM_CRYPTO_CLOSE_KEY);;
+
+#ifdef TFM_PSA_API
+    PSA_CLOSE();
+#endif
+
+    return status;
 #endif /* TFM_CRYPTO_KEY_MODULE_DISABLED */
 }