Add a concurrent key generation test function

Split into n threads, each thread will repeatedly generate,
exercise and destroy a key.
Then join the threads, and ensure using PSA_DONE that no keys still exist.

Signed-off-by: Ryan Everett <ryan.everett@arm.com>
diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function
index 09874a1..1141597 100644
--- a/tests/suites/test_suite_psa_crypto.function
+++ b/tests/suites/test_suite_psa_crypto.function
@@ -28,6 +28,10 @@
 #define TEST_DRIVER_LOCATION 0x7fffff
 #endif
 
+#if defined(MBEDTLS_THREADING_PTHREAD)
+#include "mbedtls/threading.h"
+#endif
+
 /* If this comes up, it's a bug in the test code or in the test data. */
 #define UNUSED 0xdeadbeef
 
@@ -1333,6 +1337,66 @@
     return 0;
 }
 
+#if defined(MBEDTLS_THREADING_PTHREAD)
+typedef struct generate_key_context {
+    psa_key_type_t type;
+    psa_key_usage_t usage;
+    size_t bits;
+    psa_algorithm_t alg;
+    psa_status_t expected_status;
+    psa_key_attributes_t *attributes;
+    int is_large_key;
+    int reps;
+}
+generate_key_context;
+void *thread_generate_key(void *ctx)
+{
+    mbedtls_svc_key_id_t key = MBEDTLS_SVC_KEY_ID_INIT;
+    psa_key_attributes_t got_attributes = PSA_KEY_ATTRIBUTES_INIT;
+    generate_key_context *gkc = (struct generate_key_context *) ctx;
+
+    /* If there are race conditions, it is likely the case that they do not
+     * arise every time the code runs. We repeat the code to increase the
+     * chance that any race conditions will be hit. */
+    for (int n = 0; n < gkc->reps; n++) {
+        /* Generate a key */
+        psa_status_t status = psa_generate_key(gkc->attributes, &key);
+
+        if (gkc->is_large_key > 0) {
+            TEST_ASSUME(status != PSA_ERROR_INSUFFICIENT_MEMORY);
+        }
+
+        TEST_EQUAL(status, gkc->expected_status);
+        if (gkc->expected_status != PSA_SUCCESS) {
+            PSA_ASSERT(psa_destroy_key(key));
+            goto exit;
+        }
+
+        /* Test the key information */
+        PSA_ASSERT(psa_get_key_attributes(key, &got_attributes));
+        TEST_EQUAL(psa_get_key_type(&got_attributes), gkc->type);
+        TEST_EQUAL(psa_get_key_bits(&got_attributes), gkc->bits);
+
+        /* Do something with the key according
+         * to its type and permitted usage. */
+        if (!mbedtls_test_psa_exercise_key(key, gkc->usage, gkc->alg)) {
+            psa_destroy_key(key);
+            goto exit;
+        }
+        psa_reset_key_attributes(&got_attributes);
+
+        PSA_ASSERT(psa_destroy_key(key));
+    }
+exit:
+    /*
+     * Key attributes may have been returned by psa_get_key_attributes()
+     * thus reset them as required.
+     */
+    psa_reset_key_attributes(&got_attributes);
+    return NULL;
+}
+#endif /* MBEDTLS_THREADING_PTHREAD */
+
 /* END_HEADER */
 
 /* BEGIN_DEPENDENCIES
@@ -9783,6 +9847,59 @@
 }
 /* END_CASE */
 
+#if defined MBEDTLS_THREADING_PTHREAD
+
+/* BEGIN_CASE depends_on:MBEDTLS_THREADING_PTHREAD */
+void concurrently_generate_keys(int type_arg,
+                                int bits_arg,
+                                int usage_arg,
+                                int alg_arg,
+                                int expected_status_arg,
+                                int is_large_key_arg,
+                                int arg_thread_count,
+                                int reps_arg)
+{
+    size_t thread_count = (size_t) arg_thread_count;
+    mbedtls_test_thread_t *threads = NULL;
+    generate_key_context gkc;
+    gkc.type = type_arg;
+    gkc.usage = usage_arg;
+    gkc.bits = bits_arg;
+    gkc.alg = alg_arg;
+    gkc.expected_status = expected_status_arg;
+    gkc.is_large_key = is_large_key_arg;
+    gkc.reps = reps_arg;
+    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+
+    PSA_ASSERT(psa_crypto_init());
+
+    psa_set_key_usage_flags(&attributes, usage_arg);
+    psa_set_key_algorithm(&attributes, alg_arg);
+    psa_set_key_type(&attributes, type_arg);
+    psa_set_key_bits(&attributes, bits_arg);
+    gkc.attributes = &attributes;
+
+    TEST_CALLOC(threads, sizeof(mbedtls_test_thread_t) * thread_count);
+
+    /* Split threads to generate key then destroy key. */
+    for (size_t i = 0; i < thread_count; i++) {
+        TEST_EQUAL(
+            mbedtls_test_thread_create(&threads[i], thread_generate_key,
+                                       (void *) &gkc), 0);
+    }
+
+    /* Join threads. */
+    for (size_t i = 0; i < thread_count; i++) {
+        TEST_EQUAL(mbedtls_test_thread_join(&threads[i]), 0);
+    }
+
+exit:
+    mbedtls_free(threads);
+    PSA_DONE();
+}
+/* END_CASE */
+#endif
+
 /* BEGIN_CASE */
 void generate_key(int type_arg,
                   int bits_arg,