Crypto: Add support for persistent keys

Adds support for the PSA Crypto persistent key APIs to the crypto
service by enabling and calling the Mbed Crypto implementations. Mbed
Crypto in turn calls ITS for persistent storage.

Change-Id: I0db6b1ced6030cae41322ae06214114cc9c84cb4
Signed-off-by: Jamie Fox <jamie.fox@arm.com>
diff --git a/platform/ext/common/tfm_mbedcrypto_config.h b/platform/ext/common/tfm_mbedcrypto_config.h
index 674430b..cc643e0 100644
--- a/platform/ext/common/tfm_mbedcrypto_config.h
+++ b/platform/ext/common/tfm_mbedcrypto_config.h
@@ -863,7 +863,7 @@
  * Note that this option is meant for internal use only and may be removed
  * without notice.
  */
-//#define MBEDTLS_PSA_CRYPTO_KEY_FILE_ID_ENCODES_OWNER
+#define MBEDTLS_PSA_CRYPTO_KEY_FILE_ID_ENCODES_OWNER
 
 /**
  * \def MBEDTLS_MEMORY_DEBUG
@@ -1720,7 +1720,7 @@
  *           either MBEDTLS_PSA_ITS_FILE_C or a native implementation of
  *           the PSA ITS interface
  */
-//#define MBEDTLS_PSA_CRYPTO_STORAGE_C
+#define MBEDTLS_PSA_CRYPTO_STORAGE_C
 
 /**
  * \def MBEDTLS_PSA_ITS_FILE_C
diff --git a/secure_fw/services/crypto/CMakeLists.txt b/secure_fw/services/crypto/CMakeLists.txt
index 1bc3e8a..8e54cc8 100644
--- a/secure_fw/services/crypto/CMakeLists.txt
+++ b/secure_fw/services/crypto/CMakeLists.txt
@@ -98,6 +98,8 @@
 		string(APPEND MBEDCRYPTO_C_FLAGS " -DMBEDTLS_USER_CONFIG_FILE=\\\\\\\"${MBEDTLS_USER_CONFIG_FILE}\\\\\\\""
 		" -I${MBEDTLS_USER_CONFIG_PATH}")
 	endif()
+	#Add TF-M include directory so Mbed Crypto can include PSA ITS headers
+	string(APPEND MBEDCRYPTO_C_FLAGS " -I${TFM_ROOT_DIR}/interface/include")
 
 	#Build Mbed Crypto as external project.
 	#This ensures Mbed Crypto is built with exactly defined settings.
diff --git a/secure_fw/services/crypto/crypto_key.c b/secure_fw/services/crypto/crypto_key.c
index c7405ca..e88040d 100644
--- a/secure_fw/services/crypto/crypto_key.c
+++ b/secure_fw/services/crypto/crypto_key.c
@@ -39,6 +39,58 @@
  */
 
 /*!@{*/
+psa_status_t tfm_crypto_key_attributes_from_client(
+                             const psa_client_key_attributes_t *client_key_attr,
+                             int32_t client_id,
+                             psa_key_attributes_t *key_attributes)
+{
+    if (client_key_attr == NULL || key_attributes == NULL) {
+        return PSA_ERROR_PROGRAMMER_ERROR;
+    }
+
+    /* Domain parameters are not supported, ignore any passed by the client */
+    key_attributes->domain_parameters = NULL;
+    key_attributes->domain_parameters_size = 0;
+
+    /* Copy core key attributes from the client core key attributes */
+    key_attributes->core.type = client_key_attr->core.type;
+    key_attributes->core.lifetime = client_key_attr->core.lifetime;
+    key_attributes->core.policy = client_key_attr->core.policy;
+    key_attributes->core.bits = client_key_attr->core.bits;
+    key_attributes->core.flags = client_key_attr->core.flags;
+
+    /* Use the client key id as the key_id and its partition id as the owner */
+    key_attributes->core.id.key_id = client_key_attr->core.id;
+    key_attributes->core.id.owner = client_id;
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t tfm_crypto_key_attributes_to_client(
+                                   const psa_key_attributes_t *key_attributes,
+                                   psa_client_key_attributes_t *client_key_attr)
+{
+    if (client_key_attr == NULL || key_attributes == NULL) {
+        return PSA_ERROR_PROGRAMMER_ERROR;
+    }
+
+    /* Domain parameters are not supported, avoid passing any to the client */
+    client_key_attr->domain_parameters = NULL;
+    client_key_attr->domain_parameters_size = 0;
+
+    /* Copy core key attributes to the client core key attributes */
+    client_key_attr->core.type = key_attributes->core.type;
+    client_key_attr->core.lifetime = key_attributes->core.lifetime;
+    client_key_attr->core.policy = key_attributes->core.policy;
+    client_key_attr->core.bits = key_attributes->core.bits;
+    client_key_attr->core.flags = key_attributes->core.flags;
+
+    /* Return the key_id as the client key id, do not return the owner */
+    client_key_attr->core.id = key_attributes->core.id.key_id;
+
+    return PSA_SUCCESS;
+}
+
 psa_status_t tfm_crypto_check_handle_owner(psa_key_handle_t handle,
                                            uint32_t *index)
 {
@@ -151,15 +203,16 @@
     }
 
     if ((in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec)) ||
-        (in_vec[1].len != sizeof(psa_key_attributes_t)) ||
+        (in_vec[1].len != sizeof(psa_client_key_attributes_t)) ||
         (out_vec[0].len != sizeof(psa_key_handle_t))) {
         return PSA_ERROR_CONNECTION_REFUSED;
     }
-    const psa_key_attributes_t *key_attributes = in_vec[1].base;
+    const psa_client_key_attributes_t *client_key_attr = in_vec[1].base;
     const uint8_t *data = in_vec[2].base;
     size_t data_length = in_vec[2].len;
     psa_key_handle_t *key_handle = out_vec[0].base;
     psa_status_t status;
+    psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
     uint32_t i = 0;
     int32_t partition_id = 0;
     bool empty_found = false;
@@ -180,7 +233,14 @@
         return status;
     }
 
-    status =  psa_import_key(key_attributes, data, data_length, key_handle);
+    status = tfm_crypto_key_attributes_from_client(client_key_attr,
+                                                   partition_id,
+                                                   &key_attributes);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = psa_import_key(&key_attributes, data, data_length, key_handle);
 
     if (status == PSA_SUCCESS) {
         handle_owner[i].owner = partition_id;
@@ -205,12 +265,45 @@
     }
 
     if ((in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec)) ||
-        (in_vec[1].len != sizeof(psa_key_id_t)) ||
+        (in_vec[1].len != sizeof(psa_app_key_id_t)) ||
         (out_vec[0].len != sizeof(psa_key_handle_t))) {
         return PSA_ERROR_CONNECTION_REFUSED;
     }
 
-    return PSA_ERROR_NOT_SUPPORTED;
+    psa_app_key_id_t client_key_id = *((psa_app_key_id_t *)in_vec[1].base);
+    psa_key_handle_t *key_handle = out_vec[0].base;
+    psa_status_t status;
+    psa_key_id_t id;
+    int32_t partition_id;
+    uint32_t i;
+
+    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;
+    }
+
+    /* Use the client key id as the key_id and its partition id as the owner */
+    id = (psa_key_id_t){ .key_id = client_key_id, .owner = partition_id };
+
+    status = psa_open_key(id, key_handle);
+
+    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;
 #endif /* TFM_CRYPTO_KEY_MODULE_DISABLED */
 }
 
@@ -305,15 +398,29 @@
     }
 
     if ((in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec)) ||
-        (out_vec[0].len != sizeof(psa_key_attributes_t))) {
+        (out_vec[0].len != sizeof(psa_client_key_attributes_t))) {
         return PSA_ERROR_CONNECTION_REFUSED;
     }
     const struct tfm_crypto_pack_iovec *iov = in_vec[0].base;
 
     psa_key_handle_t key = iov->key_handle;
-    psa_key_attributes_t *key_attributes = out_vec[0].base;
+    psa_client_key_attributes_t *client_key_attr = out_vec[0].base;
+    psa_status_t status;
+    psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
 
-    return psa_get_key_attributes(key, key_attributes);
+    status = tfm_crypto_check_handle_owner(key, NULL);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = psa_get_key_attributes(key, &key_attributes);
+
+    if (status == PSA_SUCCESS) {
+        status = tfm_crypto_key_attributes_to_client(&key_attributes,
+                                                     client_key_attr);
+    }
+
+    return status;
 #endif /* TFM_CRYPTO_KEY_MODULE_DISABLED */
 }
 
@@ -330,16 +437,31 @@
     }
 
     if ((in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec)) ||
-        (out_vec[0].len != sizeof(psa_key_attributes_t))) {
+        (out_vec[0].len != sizeof(psa_client_key_attributes_t))) {
         return PSA_ERROR_CONNECTION_REFUSED;
     }
 
-    psa_key_attributes_t *key_attributes = out_vec[0].base;
+    psa_client_key_attributes_t *client_key_attr = out_vec[0].base;
+    psa_status_t status;
+    psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
+    int32_t partition_id;
 
-    psa_reset_key_attributes(key_attributes);
+    status = tfm_crypto_get_caller_id(&partition_id);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
 
-    /* psa_reset_key_attributes() doesn't report any error */
-    return PSA_SUCCESS;
+    status = tfm_crypto_key_attributes_from_client(client_key_attr,
+                                                   partition_id,
+                                                   &key_attributes);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    psa_reset_key_attributes(&key_attributes);
+
+    return tfm_crypto_key_attributes_to_client(&key_attributes,
+                                               client_key_attr);
 #endif /* TFM_CRYPTO_KEY_MODULE_DISABLED */
 }
 
@@ -408,15 +530,16 @@
 
     if ((in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec)) ||
         (out_vec[0].len != sizeof(psa_key_handle_t)) ||
-        (in_vec[1].len != sizeof(psa_key_attributes_t))) {
+        (in_vec[1].len != sizeof(psa_client_key_attributes_t))) {
         return PSA_ERROR_CONNECTION_REFUSED;
     }
     const struct tfm_crypto_pack_iovec *iov = in_vec[0].base;
 
     psa_key_handle_t source_handle = iov->key_handle;
     psa_key_handle_t *target_handle = out_vec[0].base;
-    const psa_key_attributes_t *key_attributes = in_vec[1].base;
+    const psa_client_key_attributes_t *client_key_attr = in_vec[1].base;
     psa_status_t status;
+    psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
     uint32_t i = 0;
     int32_t partition_id = 0;
     bool empty_found = false;
@@ -437,7 +560,14 @@
         return status;
     }
 
-    status = psa_copy_key(source_handle, key_attributes, target_handle);
+    status = tfm_crypto_key_attributes_from_client(client_key_attr,
+                                                   partition_id,
+                                                   &key_attributes);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = psa_copy_key(source_handle, &key_attributes, target_handle);
 
     if (status == PSA_SUCCESS) {
         handle_owner[i].owner = partition_id;
@@ -463,13 +593,14 @@
     }
 
     if ((in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec)) ||
-        (in_vec[1].len != sizeof(psa_key_attributes_t)) ||
+        (in_vec[1].len != sizeof(psa_client_key_attributes_t)) ||
         (out_vec[0].len != sizeof(psa_key_handle_t))) {
         return PSA_ERROR_CONNECTION_REFUSED;
     }
     psa_key_handle_t *key_handle = out_vec[0].base;
-    const psa_key_attributes_t *key_attributes = in_vec[1].base;
+    const psa_client_key_attributes_t *client_key_attr = in_vec[1].base;
     psa_status_t status;
+    psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
     uint32_t i = 0;
     int32_t partition_id = 0;
     bool empty_found = false;
@@ -490,7 +621,14 @@
         return status;
     }
 
-    status = psa_generate_key(key_attributes, key_handle);
+    status = tfm_crypto_key_attributes_from_client(client_key_attr,
+                                                   partition_id,
+                                                   &key_attributes);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = psa_generate_key(&key_attributes, key_handle);
 
     if (status == PSA_SUCCESS) {
         handle_owner[i].owner = partition_id;
diff --git a/secure_fw/services/crypto/crypto_key_derivation.c b/secure_fw/services/crypto/crypto_key_derivation.c
index 4f42ee0..6778ce9 100644
--- a/secure_fw/services/crypto/crypto_key_derivation.c
+++ b/secure_fw/services/crypto/crypto_key_derivation.c
@@ -381,16 +381,18 @@
     }
 
     if ((in_vec[0].len != sizeof(struct tfm_crypto_pack_iovec)) ||
-        (in_vec[1].len != sizeof(psa_key_attributes_t)) ||
+        (in_vec[1].len != sizeof(psa_client_key_attributes_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;
 
     uint32_t handle = iov->op_handle;
-    const psa_key_attributes_t *key_attributes = in_vec[1].base;
+    const psa_client_key_attributes_t *client_key_attr = in_vec[1].base;
     psa_key_derivation_operation_t *operation = NULL;
     psa_key_handle_t *key_handle = out_vec[0].base;
+    psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
+    int32_t partition_id;
     uint32_t index;
 
     /* Look up the corresponding operation context */
@@ -406,11 +408,23 @@
         return status;
     }
 
+    status = tfm_crypto_get_caller_id(&partition_id);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+    status = tfm_crypto_key_attributes_from_client(client_key_attr,
+                                                   partition_id,
+                                                   &key_attributes);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
     if (operation->alg == TFM_CRYPTO_ALG_HUK_DERIVATION) {
-        status = tfm_crypto_huk_derivation_output_key(key_attributes, operation,
-                                                      key_handle);
+        status = tfm_crypto_huk_derivation_output_key(&key_attributes,
+                                                      operation, key_handle);
     } else {
-        status = psa_key_derivation_output_key(key_attributes, operation,
+        status = psa_key_derivation_output_key(&key_attributes, operation,
                                                key_handle);
     }
     if (status == PSA_SUCCESS) {
diff --git a/secure_fw/services/crypto/tfm_crypto.yaml b/secure_fw/services/crypto/tfm_crypto.yaml
index 6008cde..cc836c5 100644
--- a/secure_fw/services/crypto/tfm_crypto.yaml
+++ b/secure_fw/services/crypto/tfm_crypto.yaml
@@ -456,5 +456,11 @@
       "version": 1,
       "version_policy": "STRICT"
     },
+  ],
+  "dependencies": [
+    "TFM_ITS_SET",
+    "TFM_ITS_GET",
+    "TFM_ITS_GET_INFO",
+    "TFM_ITS_REMOVE",
   ]
 }
diff --git a/secure_fw/services/crypto/tfm_crypto_api.h b/secure_fw/services/crypto/tfm_crypto_api.h
index 74b0624..98e615a 100644
--- a/secure_fw/services/crypto/tfm_crypto_api.h
+++ b/secure_fw/services/crypto/tfm_crypto_api.h
@@ -45,6 +45,32 @@
 };
 
 /**
+ * \brief Core key attributes struct as seen by the client, with
+ *        psa_app_key_id_t as the key ID type.
+ */
+typedef struct {
+    psa_key_type_t type;
+    psa_key_lifetime_t lifetime;
+    psa_app_key_id_t id; /* Client key ID */
+    psa_key_policy_t policy;
+    psa_key_bits_t bits;
+    psa_key_attributes_flag_t flags;
+} psa_client_core_key_attributes_t;
+
+/**
+ * \brief This struct represents the psa_key_attributes_t struct exposed to the
+ *        client. The Mbed Crypto library needs a different abstraction for
+ *        psa_key_attributes_t, so this intermediate struct is defined.
+ *
+ * TODO: Cleanup crypto implementation details from client-side PSA abstraction.
+ */
+typedef struct {
+    psa_client_core_key_attributes_t core; /* Client core key attributes */
+    void *domain_parameters;
+    size_t domain_parameters_size;
+} psa_client_key_attributes_t;
+
+/**
  * \brief Initialise the service
  *
  * \return Return values as described in \ref psa_status_t
@@ -68,6 +94,32 @@
 psa_status_t tfm_crypto_get_caller_id(int32_t *id);
 
 /**
+ * \brief Gets key attributes from client key attributes.
+ *
+ * \param[in]  client_key_attr  Client key attributes
+ * \param[in]  client_id        Partition ID of the calling client
+ * \param[out] key_attributes   Key attributes
+ *
+ * \return Return values as described in \ref psa_status_t
+ */
+psa_status_t tfm_crypto_key_attributes_from_client(
+                             const psa_client_key_attributes_t *client_key_attr,
+                             int32_t client_id,
+                             psa_key_attributes_t *key_attributes);
+
+/**
+ * \brief Converts key attributes to client key attributes.
+ *
+ * \param[in]  key_attributes   Key attributes
+ * \param[out] client_key_attr  Client key attributes
+ *
+ * \return Return values as described in \ref psa_status_t
+ */
+psa_status_t tfm_crypto_key_attributes_to_client(
+                                  const psa_key_attributes_t *key_attributes,
+                                  psa_client_key_attributes_t *client_key_attr);
+
+/**
  * \brief Checks that the requested handle belongs to the requesting
  *        partition
  *
diff --git a/secure_fw/services/crypto/tfm_crypto_secure_api.c b/secure_fw/services/crypto/tfm_crypto_secure_api.c
index c750701..2509eb7 100644
--- a/secure_fw/services/crypto/tfm_crypto_secure_api.c
+++ b/secure_fw/services/crypto/tfm_crypto_secure_api.c
@@ -92,23 +92,6 @@
 }
 
 __attribute__((section("SFN")))
-psa_status_t psa_create_key(psa_key_lifetime_t lifetime,
-                            psa_key_id_t id,
-                            psa_key_handle_t *handle)
-{
-#ifdef TFM_CRYPTO_KEY_MODULE_DISABLED
-    return PSA_ERROR_NOT_SUPPORTED;
-#else
-    (void)lifetime;
-    (void)id;
-    (void)handle;
-
-    /* TODO: Persistent key APIs are not supported yet */
-    return PSA_ERROR_NOT_SUPPORTED;
-#endif /* TFM_CRYPTO_KEY_MODULE_DISABLED */
-}
-
-__attribute__((section("SFN")))
 psa_status_t psa_close_key(psa_key_handle_t handle)
 {
 #ifdef TFM_CRYPTO_KEY_MODULE_DISABLED
diff --git a/secure_fw/services/crypto/tfm_mbedcrypto_include.h b/secure_fw/services/crypto/tfm_mbedcrypto_include.h
index e6dd07d..69342b1 100644
--- a/secure_fw/services/crypto/tfm_mbedcrypto_include.h
+++ b/secure_fw/services/crypto/tfm_mbedcrypto_include.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, Arm Limited. All rights reserved.
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  *
@@ -8,6 +8,8 @@
 #ifndef __TFM_MBEDCRYPTO_INCLUDE_H__
 #define __TFM_MBEDCRYPTO_INCLUDE_H__
 
+/* Define PSA_CRYPTO_SECURE to signal that we are compiling for the SPE */
+#define PSA_CRYPTO_SECURE 1
 /* Include the crypto_spe.h header before including the PSA Crypto header from
  * Mbed Crypto
  */
diff --git a/secure_fw/spm/tfm_spm_db.inc b/secure_fw/spm/tfm_spm_db.inc
index d22d46b..d4c85d5 100644
--- a/secure_fw/spm/tfm_spm_db.inc
+++ b/secure_fw/spm/tfm_spm_db.inc
@@ -568,6 +568,16 @@
 };
 #endif /* TFM_PARTITION_SECURE_STORAGE */
 
+#ifdef TFM_PARTITION_CRYPTO
+static int32_t dependencies_TFM_SP_CRYPTO[] =
+{
+    TFM_ITS_SET_SID,
+    TFM_ITS_GET_SID,
+    TFM_ITS_GET_INFO_SID,
+    TFM_ITS_REMOVE_SID,
+};
+#endif /* TFM_PARTITION_CRYPTO */
+
 #ifdef TFM_PARTITION_INITIAL_ATTESTATION
 static int32_t dependencies_TFM_SP_INITIAL_ATTESTATION[] =
 {
@@ -723,8 +733,8 @@
                               ,
         .partition_priority   = TFM_PRIORITY(NORMAL),
         .partition_init       = tfm_crypto_init,
-        .dependencies_num     = 0,
-        .p_dependencies       = NULL,
+        .dependencies_num     = 4,
+        .p_dependencies       = dependencies_TFM_SP_CRYPTO,
     },
 #endif /* TFM_PARTITION_CRYPTO */