Merge pull request #8914 from ronald-cron-arm/resumption-early-data-compat-tests

TLS 1.3: Resumption and early data compatibility tests
diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
index 39bea79..57d7bc6 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -1304,6 +1304,11 @@
     char *MBEDTLS_PRIVATE(hostname);             /*!< host name binded with tickets */
 #endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION && MBEDTLS_SSL_CLI_C */
 
+#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_ALPN) && defined(MBEDTLS_SSL_SRV_C)
+    char *ticket_alpn;                      /*!< ALPN negotiated in the session
+                                                 during which the ticket was generated. */
+#endif
+
 #if defined(MBEDTLS_HAVE_TIME) && defined(MBEDTLS_SSL_CLI_C)
     /*! Time in milliseconds when the last ticket was received. */
     mbedtls_ms_time_t MBEDTLS_PRIVATE(ticket_reception_time);
diff --git a/library/ssl_misc.h b/library/ssl_misc.h
index 2ec898b..a8807f6 100644
--- a/library/ssl_misc.h
+++ b/library/ssl_misc.h
@@ -2852,6 +2852,13 @@
                                      const char *hostname);
 #endif
 
+#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_EARLY_DATA) && \
+    defined(MBEDTLS_SSL_ALPN)
+MBEDTLS_CHECK_RETURN_CRITICAL
+int mbedtls_ssl_session_set_ticket_alpn(mbedtls_ssl_session *session,
+                                        const char *alpn);
+#endif
+
 #if defined(MBEDTLS_SSL_PROTO_TLS1_3) && defined(MBEDTLS_SSL_SESSION_TICKETS)
 
 #define MBEDTLS_SSL_TLS1_3_MAX_ALLOWED_TICKET_LIFETIME (604800)
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 681ccab..ac8e0ea 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -3750,9 +3750,16 @@
     size_t hostname_len = (session->hostname == NULL) ?
                           0 : strlen(session->hostname) + 1;
 #endif
+
+#if defined(MBEDTLS_SSL_SRV_C) && \
+    defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_ALPN)
+    const size_t alpn_len = (session->ticket_alpn == NULL) ?
+                            0 : strlen(session->ticket_alpn) + 1;
+#endif
     size_t needed =   4  /* ticket_age_add */
                     + 1  /* ticket_flags */
                     + 1; /* resumption_key length */
+
     *olen = 0;
 
     if (session->resumption_key_len > MBEDTLS_SSL_TLS1_3_TICKET_RESUMPTION_KEY_LEN) {
@@ -3771,6 +3778,15 @@
     needed += 8; /* ticket_creation_time or ticket_reception_time */
 #endif
 
+#if defined(MBEDTLS_SSL_SRV_C)
+    if (session->endpoint == MBEDTLS_SSL_IS_SERVER) {
+#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_ALPN)
+        needed +=   2                         /* alpn_len */
+                  + alpn_len;                 /* alpn */
+#endif
+    }
+#endif /* MBEDTLS_SSL_SRV_C */
+
 #if defined(MBEDTLS_SSL_CLI_C)
     if (session->endpoint == MBEDTLS_SSL_IS_CLIENT) {
 #if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
@@ -3813,13 +3829,26 @@
     p += 2;
 #endif /* MBEDTLS_SSL_RECORD_SIZE_LIMIT */
 
-#if defined(MBEDTLS_HAVE_TIME) && defined(MBEDTLS_SSL_SRV_C)
+#if defined(MBEDTLS_SSL_SRV_C)
     if (session->endpoint == MBEDTLS_SSL_IS_SERVER) {
+#if defined(MBEDTLS_HAVE_TIME)
         MBEDTLS_PUT_UINT64_BE((uint64_t) session->ticket_creation_time, p, 0);
         p += 8;
-    }
 #endif /* MBEDTLS_HAVE_TIME */
 
+#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_ALPN)
+        MBEDTLS_PUT_UINT16_BE(alpn_len, p, 0);
+        p += 2;
+
+        if (alpn_len > 0) {
+            /* save chosen alpn */
+            memcpy(p, session->ticket_alpn, alpn_len);
+            p += alpn_len;
+        }
+#endif /* MBEDTLS_SSL_EARLY_DATA && MBEDTLS_SSL_ALPN */
+    }
+#endif /* MBEDTLS_SSL_SRV_C */
+
 #if defined(MBEDTLS_SSL_CLI_C)
     if (session->endpoint == MBEDTLS_SSL_IS_CLIENT) {
 #if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
@@ -3894,16 +3923,41 @@
     p += 2;
 #endif /* MBEDTLS_SSL_RECORD_SIZE_LIMIT */
 
-#if defined(MBEDTLS_HAVE_TIME) && defined(MBEDTLS_SSL_SRV_C)
+#if  defined(MBEDTLS_SSL_SRV_C)
     if (session->endpoint == MBEDTLS_SSL_IS_SERVER) {
+#if defined(MBEDTLS_HAVE_TIME)
         if (end - p < 8) {
             return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
         }
         session->ticket_creation_time = MBEDTLS_GET_UINT64_BE(p, 0);
         p += 8;
-    }
 #endif /* MBEDTLS_HAVE_TIME */
 
+#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_ALPN)
+        size_t alpn_len;
+
+        if (end - p < 2) {
+            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        }
+
+        alpn_len = MBEDTLS_GET_UINT16_BE(p, 0);
+        p += 2;
+
+        if (end - p < (long int) alpn_len) {
+            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        }
+
+        if (alpn_len > 0) {
+            int ret = mbedtls_ssl_session_set_ticket_alpn(session, (char *) p);
+            if (ret != 0) {
+                return ret;
+            }
+            p += alpn_len;
+        }
+#endif /* MBEDTLS_SSL_EARLY_DATA && MBEDTLS_SSL_ALPN */
+    }
+#endif /* MBEDTLS_SSL_SRV_C */
+
 #if defined(MBEDTLS_SSL_CLI_C)
     if (session->endpoint == MBEDTLS_SSL_IS_CLIENT) {
 #if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
@@ -4060,6 +4114,13 @@
 #define SSL_SERIALIZED_SESSION_CONFIG_RECORD_SIZE 0
 #endif /* MBEDTLS_SSL_RECORD_SIZE_LIMIT */
 
+#if defined(MBEDTLS_SSL_ALPN) && defined(MBEDTLS_SSL_SRV_C) && \
+    defined(MBEDTLS_SSL_EARLY_DATA)
+#define SSL_SERIALIZED_SESSION_CONFIG_ALPN 1
+#else
+#define SSL_SERIALIZED_SESSION_CONFIG_ALPN 0
+#endif /* MBEDTLS_SSL_ALPN */
+
 #define SSL_SERIALIZED_SESSION_CONFIG_TIME_BIT          0
 #define SSL_SERIALIZED_SESSION_CONFIG_CRT_BIT           1
 #define SSL_SERIALIZED_SESSION_CONFIG_CLIENT_TICKET_BIT 2
@@ -4070,6 +4131,7 @@
 #define SSL_SERIALIZED_SESSION_CONFIG_SNI_BIT           7
 #define SSL_SERIALIZED_SESSION_CONFIG_EARLY_DATA_BIT    8
 #define SSL_SERIALIZED_SESSION_CONFIG_RECORD_SIZE_BIT   9
+#define SSL_SERIALIZED_SESSION_CONFIG_ALPN_BIT          10
 
 #define SSL_SERIALIZED_SESSION_CONFIG_BITFLAG                           \
     ((uint16_t) (                                                      \
@@ -4086,7 +4148,9 @@
          (SSL_SERIALIZED_SESSION_CONFIG_EARLY_DATA << \
              SSL_SERIALIZED_SESSION_CONFIG_EARLY_DATA_BIT) | \
          (SSL_SERIALIZED_SESSION_CONFIG_RECORD_SIZE << \
-             SSL_SERIALIZED_SESSION_CONFIG_RECORD_SIZE_BIT)))
+             SSL_SERIALIZED_SESSION_CONFIG_RECORD_SIZE_BIT) | \
+         (SSL_SERIALIZED_SESSION_CONFIG_ALPN << \
+             SSL_SERIALIZED_SESSION_CONFIG_ALPN_BIT)))
 
 static const unsigned char ssl_serialized_session_header[] = {
     MBEDTLS_VERSION_MAJOR,
@@ -4161,8 +4225,12 @@
  * #endif
  *    select ( endpoint ) {
  *         case client: ClientOnlyData;
+ *         case server:
  * #if defined(MBEDTLS_HAVE_TIME)
- *         case server: uint64 ticket_creation_time;
+ *                      uint64 ticket_creation_time;
+ * #endif
+ * #if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_ALPN)
+ *                      opaque ticket_alpn<0..256>;
  * #endif
  *     };
  * } serialized_session_tls13;
@@ -4852,6 +4920,11 @@
     mbedtls_free(session->ticket);
 #endif
 
+#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_ALPN) && \
+    defined(MBEDTLS_SSL_SRV_C)
+    mbedtls_free(session->ticket_alpn);
+#endif
+
     mbedtls_platform_zeroize(session, sizeof(mbedtls_ssl_session));
 }
 
@@ -9798,4 +9871,36 @@
           MBEDTLS_SSL_SERVER_NAME_INDICATION &&
           MBEDTLS_SSL_CLI_C */
 
+#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_EARLY_DATA) && \
+    defined(MBEDTLS_SSL_ALPN)
+int mbedtls_ssl_session_set_ticket_alpn(mbedtls_ssl_session *session,
+                                        const char *alpn)
+{
+    size_t alpn_len = 0;
+
+    if (alpn != NULL) {
+        alpn_len = strlen(alpn);
+
+        if (alpn_len > MBEDTLS_SSL_MAX_ALPN_NAME_LEN) {
+            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        }
+    }
+
+    if (session->ticket_alpn != NULL) {
+        mbedtls_zeroize_and_free(session->ticket_alpn,
+                                 strlen(session->ticket_alpn));
+        session->ticket_alpn = NULL;
+    }
+
+    if (alpn != NULL) {
+        session->ticket_alpn = mbedtls_calloc(alpn_len + 1, 1);
+        if (session->ticket_alpn == NULL) {
+            return MBEDTLS_ERR_SSL_ALLOC_FAILED;
+        }
+        memcpy(session->ticket_alpn, alpn, alpn_len);
+    }
+
+    return 0;
+}
+#endif /* MBEDTLS_SSL_SRV_C && MBEDTLS_SSL_EARLY_DATA && MBEDTLS_SSL_ALPN */
 #endif /* MBEDTLS_SSL_TLS_C */
diff --git a/library/ssl_tls13_server.c b/library/ssl_tls13_server.c
index 203aa17..2c30da8 100644
--- a/library/ssl_tls13_server.c
+++ b/library/ssl_tls13_server.c
@@ -467,7 +467,14 @@
 
 #if defined(MBEDTLS_SSL_EARLY_DATA)
     dst->max_early_data_size = src->max_early_data_size;
-#endif
+
+#if defined(MBEDTLS_SSL_ALPN)
+    int ret = mbedtls_ssl_session_set_ticket_alpn(dst, src->ticket_alpn);
+    if (ret != 0) {
+        return ret;
+    }
+#endif /* MBEDTLS_SSL_ALPN */
+#endif /* MBEDTLS_SSL_EARLY_DATA*/
 
     return 0;
 }
@@ -3137,6 +3144,15 @@
 
     MBEDTLS_SSL_PRINT_TICKET_FLAGS(4, session->ticket_flags);
 
+#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_ALPN)
+    if (session->ticket_alpn == NULL) {
+        ret = mbedtls_ssl_session_set_ticket_alpn(session, ssl->alpn_chosen);
+        if (ret != 0) {
+            return ret;
+        }
+    }
+#endif
+
     /* Generate ticket_age_add */
     if ((ret = ssl->conf->f_rng(ssl->conf->p_rng,
                                 (unsigned char *) &session->ticket_age_add,
diff --git a/tests/include/test/psa_exercise_key.h b/tests/include/test/psa_exercise_key.h
index 44f5c08..f6be307 100644
--- a/tests/include/test/psa_exercise_key.h
+++ b/tests/include/test/psa_exercise_key.h
@@ -123,6 +123,9 @@
  * \param input2            The first input to pass.
  * \param input2_length     The length of \p input2 in bytes.
  * \param capacity          The capacity to set.
+ * \param key_destroyable   If set to 1, a failure due to the key not existing
+ *                          or the key being destroyed mid-operation will only
+ *                          be reported if the error code is unexpected.
  *
  * \return                  \c 1 on success, \c 0 on failure.
  */
@@ -132,7 +135,7 @@
     psa_algorithm_t alg,
     const unsigned char *input1, size_t input1_length,
     const unsigned char *input2, size_t input2_length,
-    size_t capacity);
+    size_t capacity, int key_destroyable);
 
 /** Perform a key agreement using the given key pair against its public key
  * using psa_raw_key_agreement().
@@ -143,12 +146,15 @@
  *
  * \param alg               A key agreement algorithm compatible with \p key.
  * \param key               A key that allows key agreement with \p alg.
+ * \param key_destroyable   If set to 1, a failure due to the key not existing
+ *                          or the key being destroyed mid-operation will only
+ *                          be reported if the error code is unexpected.
  *
  * \return                  \c 1 on success, \c 0 on failure.
  */
 psa_status_t mbedtls_test_psa_raw_key_agreement_with_self(
     psa_algorithm_t alg,
-    mbedtls_svc_key_id_t key);
+    mbedtls_svc_key_id_t key, int key_destroyable);
 
 /** Perform a key agreement using the given key pair against its public key
  * using psa_key_derivation_raw_key().
@@ -162,12 +168,15 @@
  *                          \p key.
  * \param key               A key pair object that is suitable for a key
  *                          agreement with \p operation.
+ * \param key_destroyable   If set to 1, a failure due to the key not existing
+ *                          or the key being destroyed mid-operation will only
+ *                          be reported if the error code is unexpected.
  *
  * \return                  \c 1 on success, \c 0 on failure.
  */
 psa_status_t mbedtls_test_psa_key_agreement_with_self(
     psa_key_derivation_operation_t *operation,
-    mbedtls_svc_key_id_t key);
+    mbedtls_svc_key_id_t key, int key_destroyable);
 
 /** Perform sanity checks on the given key representation.
  *
@@ -209,18 +218,34 @@
  * ```
  * if( ! exercise_key( ... ) ) goto exit;
  * ```
+ * To use this function for multi-threaded tests where the key
+ * may be destroyed at any point: call this function with key_destroyable set
+ * to 1, while another thread calls psa_destroy_key on the same key;
+ * this will test whether destroying the key in use leads to any corruption.
  *
- * \param key       The key to exercise. It should be capable of performing
- *                  \p alg.
- * \param usage     The usage flags to assume.
- * \param alg       The algorithm to exercise.
+ * There cannot be a set of concurrent calls:
+ * `mbedtls_test_psa_exercise_key(ki,...)` such that each ki is a unique
+ * persistent key not loaded into any key slot, and i is greater than the
+ * number of free key slots.
+ * This is because such scenarios can lead to unsupported
+ * `PSA_ERROR_INSUFFICIENT_MEMORY` return codes.
+ *
+ *
+ * \param key               The key to exercise. It should be capable of performing
+ *                          \p alg.
+ * \param usage             The usage flags to assume.
+ * \param alg               The algorithm to exercise.
+ * \param key_destroyable   If set to 1, a failure due to the key not existing
+ *                          or the key being destroyed mid-operation will only
+ *                          be reported if the error code is unexpected.
  *
  * \retval 0 The key failed the smoke tests.
  * \retval 1 The key passed the smoke tests.
  */
 int mbedtls_test_psa_exercise_key(mbedtls_svc_key_id_t key,
                                   psa_key_usage_t usage,
-                                  psa_algorithm_t alg);
+                                  psa_algorithm_t alg,
+                                  int key_destroyable);
 
 psa_key_usage_t mbedtls_test_psa_usage_to_exercise(psa_key_type_t type,
                                                    psa_algorithm_t alg);
diff --git a/tests/src/psa_exercise_key.c b/tests/src/psa_exercise_key.c
index 7b81052..937bd45 100644
--- a/tests/src/psa_exercise_key.c
+++ b/tests/src/psa_exercise_key.c
@@ -38,7 +38,8 @@
 }
 #endif
 
-static int check_key_attributes_sanity(mbedtls_svc_key_id_t key)
+static int check_key_attributes_sanity(mbedtls_svc_key_id_t key,
+                                       int key_destroyable)
 {
     int ok = 0;
     psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
@@ -46,8 +47,13 @@
     mbedtls_svc_key_id_t id;
     psa_key_type_t type;
     size_t bits;
-
-    PSA_ASSERT(psa_get_key_attributes(key, &attributes));
+    psa_status_t status = psa_get_key_attributes(key, &attributes);
+    if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+        /* The key has been destroyed. */
+        psa_reset_key_attributes(&attributes);
+        return 1;
+    }
+    PSA_ASSERT(status);
     lifetime = psa_get_key_lifetime(&attributes);
     id = psa_get_key_id(&attributes);
     type = psa_get_key_type(&attributes);
@@ -66,17 +72,20 @@
             (MBEDTLS_SVC_KEY_ID_GET_KEY_ID(id) <= PSA_KEY_ID_USER_MAX));
     }
 #if defined(MBEDTLS_PSA_CRYPTO_SE_C)
-    /* randomly-generated 64-bit constant, should never appear in test data */
-    psa_key_slot_number_t slot_number = 0xec94d4a5058a1a21;
-    psa_status_t status = psa_get_key_slot_number(&attributes, &slot_number);
-    if (lifetime_is_dynamic_secure_element(lifetime)) {
-        /* Mbed TLS currently always exposes the slot number to
-         * applications. This is not mandated by the PSA specification
-         * and may change in future versions. */
-        TEST_EQUAL(status, 0);
-        TEST_ASSERT(slot_number != 0xec94d4a5058a1a21);
-    } else {
-        TEST_EQUAL(status, PSA_ERROR_INVALID_ARGUMENT);
+    /* MBEDTLS_PSA_CRYPTO_SE_C does not support thread safety. */
+    if (key_destroyable == 0) {
+        /* randomly-generated 64-bit constant, should never appear in test data */
+        psa_key_slot_number_t slot_number = 0xec94d4a5058a1a21;
+        status = psa_get_key_slot_number(&attributes, &slot_number);
+        if (lifetime_is_dynamic_secure_element(lifetime)) {
+            /* Mbed TLS currently always exposes the slot number to
+             * applications. This is not mandated by the PSA specification
+             * and may change in future versions. */
+            TEST_EQUAL(status, 0);
+            TEST_ASSERT(slot_number != 0xec94d4a5058a1a21);
+        } else {
+            TEST_EQUAL(status, PSA_ERROR_INVALID_ARGUMENT);
+        }
     }
 #endif
 
@@ -110,20 +119,27 @@
 
 static int exercise_mac_key(mbedtls_svc_key_id_t key,
                             psa_key_usage_t usage,
-                            psa_algorithm_t alg)
+                            psa_algorithm_t alg,
+                            int key_destroyable)
 {
     psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT;
     const unsigned char input[] = "foo";
     unsigned char mac[PSA_MAC_MAX_SIZE] = { 0 };
     size_t mac_length = sizeof(mac);
-
+    psa_status_t status = PSA_SUCCESS;
     /* Convert wildcard algorithm to exercisable algorithm */
     if (alg & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) {
         alg = PSA_ALG_TRUNCATED_MAC(alg, PSA_MAC_TRUNCATED_LENGTH(alg));
     }
 
     if (usage & PSA_KEY_USAGE_SIGN_HASH) {
-        PSA_ASSERT(psa_mac_sign_setup(&operation, key, alg));
+        status = psa_mac_sign_setup(&operation, key, alg);
+        if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+            /* The key has been destroyed. */
+            PSA_ASSERT(psa_mac_abort(&operation));
+            return 1;
+        }
+        PSA_ASSERT(status);
         PSA_ASSERT(psa_mac_update(&operation,
                                   input, sizeof(input)));
         PSA_ASSERT(psa_mac_sign_finish(&operation,
@@ -136,7 +152,13 @@
             (usage & PSA_KEY_USAGE_SIGN_HASH ?
              PSA_SUCCESS :
              PSA_ERROR_INVALID_SIGNATURE);
-        PSA_ASSERT(psa_mac_verify_setup(&operation, key, alg));
+        status = psa_mac_verify_setup(&operation, key, alg);
+        if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+            /* The key has been destroyed. */
+            PSA_ASSERT(psa_mac_abort(&operation));
+            return 1;
+        }
+        PSA_ASSERT(status);
         PSA_ASSERT(psa_mac_update(&operation,
                                   input, sizeof(input)));
         TEST_EQUAL(psa_mac_verify_finish(&operation, mac, mac_length),
@@ -152,7 +174,8 @@
 
 static int exercise_cipher_key(mbedtls_svc_key_id_t key,
                                psa_key_usage_t usage,
-                               psa_algorithm_t alg)
+                               psa_algorithm_t alg,
+                               int key_destroyable)
 {
     psa_cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT;
     unsigned char iv[PSA_CIPHER_IV_MAX_SIZE] = { 0 };
@@ -164,13 +187,20 @@
     size_t ciphertext_length = sizeof(ciphertext);
     unsigned char decrypted[sizeof(ciphertext)];
     size_t part_length;
+    psa_status_t status = PSA_SUCCESS;
 
     PSA_ASSERT(psa_get_key_attributes(key, &attributes));
     key_type = psa_get_key_type(&attributes);
     iv_length = PSA_CIPHER_IV_LENGTH(key_type, alg);
 
     if (usage & PSA_KEY_USAGE_ENCRYPT) {
-        PSA_ASSERT(psa_cipher_encrypt_setup(&operation, key, alg));
+        status = psa_cipher_encrypt_setup(&operation, key, alg);
+        if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+            /* The key has been destroyed. */
+            PSA_ASSERT(psa_cipher_abort(&operation));
+            return 1;
+        }
+        PSA_ASSERT(status);
         if (iv_length != 0) {
             PSA_ASSERT(psa_cipher_generate_iv(&operation,
                                               iv, sizeof(iv),
@@ -188,12 +218,17 @@
     }
 
     if (usage & PSA_KEY_USAGE_DECRYPT) {
-        psa_status_t status;
         int maybe_invalid_padding = 0;
         if (!(usage & PSA_KEY_USAGE_ENCRYPT)) {
             maybe_invalid_padding = !PSA_ALG_IS_STREAM_CIPHER(alg);
         }
-        PSA_ASSERT(psa_cipher_decrypt_setup(&operation, key, alg));
+        status = psa_cipher_decrypt_setup(&operation, key, alg);
+        if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+            /* The key has been destroyed. */
+            PSA_ASSERT(psa_cipher_abort(&operation));
+            return 1;
+        }
+        PSA_ASSERT(status);
         if (iv_length != 0) {
             PSA_ASSERT(psa_cipher_set_iv(&operation,
                                          iv, iv_length));
@@ -227,7 +262,8 @@
 
 static int exercise_aead_key(mbedtls_svc_key_id_t key,
                              psa_key_usage_t usage,
-                             psa_algorithm_t alg)
+                             psa_algorithm_t alg,
+                             int key_destroyable)
 {
     unsigned char nonce[PSA_AEAD_NONCE_MAX_SIZE] = { 0 };
     size_t nonce_length;
@@ -237,6 +273,7 @@
     unsigned char ciphertext[48] = "(wabblewebblewibblewobblewubble)";
     size_t ciphertext_length = sizeof(ciphertext);
     size_t plaintext_length = sizeof(ciphertext);
+    psa_status_t status = PSA_SUCCESS;
 
     /* Convert wildcard algorithm to exercisable algorithm */
     if (alg & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) {
@@ -248,12 +285,17 @@
     nonce_length = PSA_AEAD_NONCE_LENGTH(key_type, alg);
 
     if (usage & PSA_KEY_USAGE_ENCRYPT) {
-        PSA_ASSERT(psa_aead_encrypt(key, alg,
-                                    nonce, nonce_length,
-                                    NULL, 0,
-                                    plaintext, sizeof(plaintext),
-                                    ciphertext, sizeof(ciphertext),
-                                    &ciphertext_length));
+        status = psa_aead_encrypt(key, alg,
+                                  nonce, nonce_length,
+                                  NULL, 0,
+                                  plaintext, sizeof(plaintext),
+                                  ciphertext, sizeof(ciphertext),
+                                  &ciphertext_length);
+        if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+            /* The key has been destroyed. */
+            return 1;
+        }
+        PSA_ASSERT(status);
     }
 
     if (usage & PSA_KEY_USAGE_DECRYPT) {
@@ -261,13 +303,17 @@
             (usage & PSA_KEY_USAGE_ENCRYPT ?
              PSA_SUCCESS :
              PSA_ERROR_INVALID_SIGNATURE);
-        TEST_EQUAL(psa_aead_decrypt(key, alg,
-                                    nonce, nonce_length,
-                                    NULL, 0,
-                                    ciphertext, ciphertext_length,
-                                    plaintext, sizeof(plaintext),
-                                    &plaintext_length),
-                   verify_status);
+        status = psa_aead_decrypt(key, alg,
+                                  nonce, nonce_length,
+                                  NULL, 0,
+                                  ciphertext, ciphertext_length,
+                                  plaintext, sizeof(plaintext),
+                                  &plaintext_length);
+        if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+            /* The key has been destroyed. */
+            return 1;
+        }
+        TEST_ASSERT(status == verify_status);
     }
 
     return 1;
@@ -291,7 +337,8 @@
 
 static int exercise_signature_key(mbedtls_svc_key_id_t key,
                                   psa_key_usage_t usage,
-                                  psa_algorithm_t alg)
+                                  psa_algorithm_t alg,
+                                  int key_destroyable)
 {
     /* If the policy allows signing with any hash, just pick one. */
     psa_algorithm_t hash_alg = PSA_ALG_SIGN_GET_HASH(alg);
@@ -305,6 +352,7 @@
         TEST_FAIL("No hash algorithm for hash-and-sign testing");
 #endif
     }
+    psa_status_t status = PSA_SUCCESS;
 
     if (usage & (PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH) &&
         PSA_ALG_IS_SIGN_HASH(alg)) {
@@ -321,10 +369,15 @@
         }
 
         if (usage & PSA_KEY_USAGE_SIGN_HASH) {
-            PSA_ASSERT(psa_sign_hash(key, alg,
-                                     payload, payload_length,
-                                     signature, sizeof(signature),
-                                     &signature_length));
+            status = psa_sign_hash(key, alg,
+                                   payload, payload_length,
+                                   signature, sizeof(signature),
+                                   &signature_length);
+            if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+                /* The key has been destroyed. */
+                return 1;
+            }
+            PSA_ASSERT(status);
         }
 
         if (usage & PSA_KEY_USAGE_VERIFY_HASH) {
@@ -332,10 +385,14 @@
                 (usage & PSA_KEY_USAGE_SIGN_HASH ?
                  PSA_SUCCESS :
                  PSA_ERROR_INVALID_SIGNATURE);
-            TEST_EQUAL(psa_verify_hash(key, alg,
-                                       payload, payload_length,
-                                       signature, signature_length),
-                       verify_status);
+            status = psa_verify_hash(key, alg,
+                                     payload, payload_length,
+                                     signature, signature_length);
+            if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+                /* The key has been destroyed. */
+                return 1;
+            }
+            TEST_ASSERT(status == verify_status);
         }
     }
 
@@ -346,10 +403,15 @@
         size_t signature_length = sizeof(signature);
 
         if (usage & PSA_KEY_USAGE_SIGN_MESSAGE) {
-            PSA_ASSERT(psa_sign_message(key, alg,
-                                        message, message_length,
-                                        signature, sizeof(signature),
-                                        &signature_length));
+            status = psa_sign_message(key, alg,
+                                      message, message_length,
+                                      signature, sizeof(signature),
+                                      &signature_length);
+            if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+                /* The key has been destroyed. */
+                return 1;
+            }
+            PSA_ASSERT(status);
         }
 
         if (usage & PSA_KEY_USAGE_VERIFY_MESSAGE) {
@@ -357,10 +419,14 @@
                 (usage & PSA_KEY_USAGE_SIGN_MESSAGE ?
                  PSA_SUCCESS :
                  PSA_ERROR_INVALID_SIGNATURE);
-            TEST_EQUAL(psa_verify_message(key, alg,
-                                          message, message_length,
-                                          signature, signature_length),
-                       verify_status);
+            status = psa_verify_message(key, alg,
+                                        message, message_length,
+                                        signature, signature_length);
+            if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+                /* The key has been destroyed. */
+                return 1;
+            }
+            TEST_ASSERT(status == verify_status);
         }
     }
 
@@ -372,7 +438,8 @@
 
 static int exercise_asymmetric_encryption_key(mbedtls_svc_key_id_t key,
                                               psa_key_usage_t usage,
-                                              psa_algorithm_t alg)
+                                              psa_algorithm_t alg,
+                                              int key_destroyable)
 {
     unsigned char plaintext[PSA_ASYMMETRIC_DECRYPT_OUTPUT_MAX_SIZE] =
         "Hello, world...";
@@ -380,22 +447,30 @@
         "(wabblewebblewibblewobblewubble)";
     size_t ciphertext_length = sizeof(ciphertext);
     size_t plaintext_length = 16;
-
+    psa_status_t status = PSA_SUCCESS;
     if (usage & PSA_KEY_USAGE_ENCRYPT) {
-        PSA_ASSERT(psa_asymmetric_encrypt(key, alg,
-                                          plaintext, plaintext_length,
-                                          NULL, 0,
-                                          ciphertext, sizeof(ciphertext),
-                                          &ciphertext_length));
+        status = psa_asymmetric_encrypt(key, alg,
+                                        plaintext, plaintext_length,
+                                        NULL, 0,
+                                        ciphertext, sizeof(ciphertext),
+                                        &ciphertext_length);
+        if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+            /* The key has been destroyed. */
+            return 1;
+        }
+        PSA_ASSERT(status);
     }
 
     if (usage & PSA_KEY_USAGE_DECRYPT) {
-        psa_status_t status =
-            psa_asymmetric_decrypt(key, alg,
-                                   ciphertext, ciphertext_length,
-                                   NULL, 0,
-                                   plaintext, sizeof(plaintext),
-                                   &plaintext_length);
+        status = psa_asymmetric_decrypt(key, alg,
+                                        ciphertext, ciphertext_length,
+                                        NULL, 0,
+                                        plaintext, sizeof(plaintext),
+                                        &plaintext_length);
+        if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+            /* The key has been destroyed. */
+            return 1;
+        }
         TEST_ASSERT(status == PSA_SUCCESS ||
                     ((usage & PSA_KEY_USAGE_ENCRYPT) == 0 &&
                      (status == PSA_ERROR_INVALID_ARGUMENT ||
@@ -414,16 +489,22 @@
     psa_algorithm_t alg,
     const unsigned char *input1, size_t input1_length,
     const unsigned char *input2, size_t input2_length,
-    size_t capacity)
+    size_t capacity, int key_destroyable)
 {
     PSA_ASSERT(psa_key_derivation_setup(operation, alg));
+    psa_status_t status = PSA_SUCCESS;
     if (PSA_ALG_IS_HKDF(alg)) {
         PSA_ASSERT(psa_key_derivation_input_bytes(operation,
                                                   PSA_KEY_DERIVATION_INPUT_SALT,
                                                   input1, input1_length));
-        PSA_ASSERT(psa_key_derivation_input_key(operation,
-                                                PSA_KEY_DERIVATION_INPUT_SECRET,
-                                                key));
+        status = psa_key_derivation_input_key(operation,
+                                              PSA_KEY_DERIVATION_INPUT_SECRET,
+                                              key);
+        if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+            /* The key has been destroyed. */
+            return 1;
+        }
+        PSA_ASSERT(status);
         PSA_ASSERT(psa_key_derivation_input_bytes(operation,
                                                   PSA_KEY_DERIVATION_INPUT_INFO,
                                                   input2,
@@ -432,13 +513,23 @@
         PSA_ASSERT(psa_key_derivation_input_bytes(operation,
                                                   PSA_KEY_DERIVATION_INPUT_SALT,
                                                   input1, input1_length));
-        PSA_ASSERT(psa_key_derivation_input_key(operation,
-                                                PSA_KEY_DERIVATION_INPUT_SECRET,
-                                                key));
+        status = psa_key_derivation_input_key(operation,
+                                              PSA_KEY_DERIVATION_INPUT_SECRET,
+                                              key);
+        if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+            /* The key has been destroyed. */
+            return 1;
+        }
+        PSA_ASSERT(status);
     } else if (PSA_ALG_IS_HKDF_EXPAND(alg)) {
-        PSA_ASSERT(psa_key_derivation_input_key(operation,
-                                                PSA_KEY_DERIVATION_INPUT_SECRET,
-                                                key));
+        status = psa_key_derivation_input_key(operation,
+                                              PSA_KEY_DERIVATION_INPUT_SECRET,
+                                              key);
+        if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+            /* The key has been destroyed. */
+            return 1;
+        }
+        PSA_ASSERT(status);
         PSA_ASSERT(psa_key_derivation_input_bytes(operation,
                                                   PSA_KEY_DERIVATION_INPUT_INFO,
                                                   input2,
@@ -448,9 +539,14 @@
         PSA_ASSERT(psa_key_derivation_input_bytes(operation,
                                                   PSA_KEY_DERIVATION_INPUT_SEED,
                                                   input1, input1_length));
-        PSA_ASSERT(psa_key_derivation_input_key(operation,
-                                                PSA_KEY_DERIVATION_INPUT_SECRET,
-                                                key));
+        status = psa_key_derivation_input_key(operation,
+                                              PSA_KEY_DERIVATION_INPUT_SECRET,
+                                              key);
+        if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+            /* The key has been destroyed. */
+            return 1;
+        }
+        PSA_ASSERT(status);
         PSA_ASSERT(psa_key_derivation_input_bytes(operation,
                                                   PSA_KEY_DERIVATION_INPUT_LABEL,
                                                   input2, input2_length));
@@ -462,9 +558,14 @@
                                                   PSA_KEY_DERIVATION_INPUT_SALT,
                                                   input2,
                                                   input2_length));
-        PSA_ASSERT(psa_key_derivation_input_key(operation,
-                                                PSA_KEY_DERIVATION_INPUT_PASSWORD,
-                                                key));
+        status = psa_key_derivation_input_key(operation,
+                                              PSA_KEY_DERIVATION_INPUT_PASSWORD,
+                                              key);
+        if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+            /* The key has been destroyed. */
+            return 1;
+        }
+        PSA_ASSERT(status);
     } else if (alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) {
         PSA_ASSERT(psa_key_derivation_input_bytes(operation,
                                                   PSA_KEY_DERIVATION_INPUT_SECRET,
@@ -486,7 +587,8 @@
 
 static int exercise_key_derivation_key(mbedtls_svc_key_id_t key,
                                        psa_key_usage_t usage,
-                                       psa_algorithm_t alg)
+                                       psa_algorithm_t alg,
+                                       int key_destroyable)
 {
     psa_key_derivation_operation_t operation = PSA_KEY_DERIVATION_OPERATION_INIT;
     unsigned char input1[] = "Input 1";
@@ -500,14 +602,20 @@
         if (!mbedtls_test_psa_setup_key_derivation_wrap(&operation, key, alg,
                                                         input1, input1_length,
                                                         input2, input2_length,
-                                                        capacity)) {
+                                                        capacity, key_destroyable)) {
             goto exit;
         }
 
-        PSA_ASSERT(psa_key_derivation_output_bytes(&operation,
-                                                   output,
-                                                   capacity));
-        PSA_ASSERT(psa_key_derivation_abort(&operation));
+        psa_status_t status = psa_key_derivation_output_bytes(&operation,
+                                                              output,
+                                                              capacity);
+        if (key_destroyable && status == PSA_ERROR_BAD_STATE) {
+            /* The key has been destroyed. */
+            PSA_ASSERT(psa_key_derivation_abort(&operation));
+        } else {
+            PSA_ASSERT(status);
+            PSA_ASSERT(psa_key_derivation_abort(&operation));
+        }
     }
 
     return 1;
@@ -520,31 +628,45 @@
  * private key against its own public key. */
 psa_status_t mbedtls_test_psa_key_agreement_with_self(
     psa_key_derivation_operation_t *operation,
-    mbedtls_svc_key_id_t key)
+    mbedtls_svc_key_id_t key, int key_destroyable)
 {
     psa_key_type_t private_key_type;
     psa_key_type_t public_key_type;
     size_t key_bits;
     uint8_t *public_key = NULL;
     size_t public_key_length;
-    /* Return GENERIC_ERROR if something other than the final call to
-     * psa_key_derivation_key_agreement fails. This isn't fully satisfactory,
-     * but it's good enough: callers will report it as a failed test anyway. */
-    psa_status_t status = PSA_ERROR_GENERIC_ERROR;
     psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
 
-    PSA_ASSERT(psa_get_key_attributes(key, &attributes));
+    psa_status_t status = psa_get_key_attributes(key, &attributes);
+    if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+        /* The key has been destroyed. */
+        psa_reset_key_attributes(&attributes);
+        return PSA_SUCCESS;
+    }
+    PSA_ASSERT(status);
+
     private_key_type = psa_get_key_type(&attributes);
     key_bits = psa_get_key_bits(&attributes);
     public_key_type = PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(private_key_type);
     public_key_length = PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(public_key_type, key_bits);
     TEST_CALLOC(public_key, public_key_length);
-    PSA_ASSERT(psa_export_public_key(key, public_key, public_key_length,
-                                     &public_key_length));
+    status = psa_export_public_key(key, public_key, public_key_length,
+                                   &public_key_length);
+    if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+        /* The key has been destroyed. */
+        status = PSA_SUCCESS;
+        goto exit;
+    }
+    PSA_ASSERT(status);
 
     status = psa_key_derivation_key_agreement(
         operation, PSA_KEY_DERIVATION_INPUT_SECRET, key,
         public_key, public_key_length);
+    if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+        /* The key has been destroyed. */
+        status = PSA_SUCCESS;
+        goto exit;
+    }
 exit:
     /*
      * Key attributes may have been returned by psa_get_key_attributes()
@@ -560,7 +682,8 @@
  * private key against its own public key. */
 psa_status_t mbedtls_test_psa_raw_key_agreement_with_self(
     psa_algorithm_t alg,
-    mbedtls_svc_key_id_t key)
+    mbedtls_svc_key_id_t key,
+    int key_destroyable)
 {
     psa_key_type_t private_key_type;
     psa_key_type_t public_key_type;
@@ -569,25 +692,39 @@
     size_t public_key_length;
     uint8_t output[1024];
     size_t output_length;
-    /* Return GENERIC_ERROR if something other than the final call to
-     * psa_key_derivation_key_agreement fails. This isn't fully satisfactory,
-     * but it's good enough: callers will report it as a failed test anyway. */
-    psa_status_t status = PSA_ERROR_GENERIC_ERROR;
     psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
 
-    PSA_ASSERT(psa_get_key_attributes(key, &attributes));
+    psa_status_t status = psa_get_key_attributes(key, &attributes);
+    if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+        /* The key has been destroyed. */
+        psa_reset_key_attributes(&attributes);
+        return PSA_SUCCESS;
+    }
+    PSA_ASSERT(status);
+
     private_key_type = psa_get_key_type(&attributes);
     key_bits = psa_get_key_bits(&attributes);
     public_key_type = PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(private_key_type);
     public_key_length = PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(public_key_type, key_bits);
     TEST_CALLOC(public_key, public_key_length);
-    PSA_ASSERT(psa_export_public_key(key,
-                                     public_key, public_key_length,
-                                     &public_key_length));
+    status = psa_export_public_key(key,
+                                   public_key, public_key_length,
+                                   &public_key_length);
+    if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+        /* The key has been destroyed. */
+        status = PSA_SUCCESS;
+        goto exit;
+    }
+    PSA_ASSERT(status);
 
     status = psa_raw_key_agreement(alg, key,
                                    public_key, public_key_length,
                                    output, sizeof(output), &output_length);
+    if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+        /* The key has been destroyed. */
+        status = PSA_SUCCESS;
+        goto exit;
+    }
     if (status == PSA_SUCCESS) {
         TEST_ASSERT(output_length <=
                     PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE(private_key_type,
@@ -609,14 +746,16 @@
 
 static int exercise_raw_key_agreement_key(mbedtls_svc_key_id_t key,
                                           psa_key_usage_t usage,
-                                          psa_algorithm_t alg)
+                                          psa_algorithm_t alg,
+                                          int key_destroyable)
 {
     int ok = 0;
 
     if (usage & PSA_KEY_USAGE_DERIVE) {
         /* We need two keys to exercise key agreement. Exercise the
          * private key against its own public key. */
-        PSA_ASSERT(mbedtls_test_psa_raw_key_agreement_with_self(alg, key));
+        PSA_ASSERT(mbedtls_test_psa_raw_key_agreement_with_self(alg, key,
+                                                                key_destroyable));
     }
     ok = 1;
 
@@ -626,7 +765,8 @@
 
 static int exercise_key_agreement_key(mbedtls_svc_key_id_t key,
                                       psa_key_usage_t usage,
-                                      psa_algorithm_t alg)
+                                      psa_algorithm_t alg,
+                                      int key_destroyable)
 {
     psa_key_derivation_operation_t operation = PSA_KEY_DERIVATION_OPERATION_INIT;
     unsigned char input[1] = { 0 };
@@ -657,7 +797,12 @@
            hash length. Otherwise test should fail with INVALID_ARGUMENT. */
         if (PSA_ALG_IS_HKDF_EXPAND(kdf_alg)) {
             psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
-            PSA_ASSERT(psa_get_key_attributes(key, &attributes));
+            psa_status_t status = psa_get_key_attributes(key, &attributes);
+            if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+                /* The key has been destroyed. */
+                ok = 1;
+            }
+            PSA_ASSERT(status);
             size_t key_bits = psa_get_key_bits(&attributes);
             psa_algorithm_t hash_alg = PSA_ALG_HKDF_GET_HASH(kdf_alg);
 
@@ -666,7 +811,8 @@
             }
         }
 
-        TEST_EQUAL(mbedtls_test_psa_key_agreement_with_self(&operation, key),
+        TEST_EQUAL(mbedtls_test_psa_key_agreement_with_self(&operation, key,
+                                                            key_destroyable),
                    expected_key_agreement_status);
 
         if (expected_key_agreement_status != PSA_SUCCESS) {
@@ -857,7 +1003,8 @@
 }
 
 static int exercise_export_key(mbedtls_svc_key_id_t key,
-                               psa_key_usage_t usage)
+                               psa_key_usage_t usage,
+                               int key_destroyable)
 {
     psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
     uint8_t *exported = NULL;
@@ -865,25 +1012,31 @@
     size_t exported_length = 0;
     int ok = 0;
 
-    PSA_ASSERT(psa_get_key_attributes(key, &attributes));
+    psa_status_t status = psa_get_key_attributes(key, &attributes);
+    if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+        /* The key has been destroyed. */
+        psa_reset_key_attributes(&attributes);
+        return 1;
+    }
+    PSA_ASSERT(status);
 
     exported_size = PSA_EXPORT_KEY_OUTPUT_SIZE(
         psa_get_key_type(&attributes),
         psa_get_key_bits(&attributes));
     TEST_CALLOC(exported, exported_size);
 
-    if ((usage & PSA_KEY_USAGE_EXPORT) == 0 &&
-        !PSA_KEY_TYPE_IS_PUBLIC_KEY(psa_get_key_type(&attributes))) {
-        TEST_EQUAL(psa_export_key(key, exported,
-                                  exported_size, &exported_length),
-                   PSA_ERROR_NOT_PERMITTED);
+    status = psa_export_key(key, exported, exported_size, &exported_length);
+    if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+        /* The key has been destroyed. */
+        ok = 1;
+        goto exit;
+    } else if ((usage & PSA_KEY_USAGE_EXPORT) == 0 &&
+               !PSA_KEY_TYPE_IS_PUBLIC_KEY(psa_get_key_type(&attributes))) {
+        TEST_EQUAL(status, PSA_ERROR_NOT_PERMITTED);
         ok = 1;
         goto exit;
     }
-
-    PSA_ASSERT(psa_export_key(key,
-                              exported, exported_size,
-                              &exported_length));
+    PSA_ASSERT(status);
     ok = mbedtls_test_psa_exported_key_sanity_check(
         psa_get_key_type(&attributes), psa_get_key_bits(&attributes),
         exported, exported_length);
@@ -899,7 +1052,8 @@
     return ok;
 }
 
-static int exercise_export_public_key(mbedtls_svc_key_id_t key)
+static int exercise_export_public_key(mbedtls_svc_key_id_t key,
+                                      int key_destroyable)
 {
     psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
     psa_key_type_t public_type;
@@ -908,16 +1062,27 @@
     size_t exported_length = 0;
     int ok = 0;
 
-    PSA_ASSERT(psa_get_key_attributes(key, &attributes));
+    psa_status_t status = psa_get_key_attributes(key, &attributes);
+    if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+        /* The key has been destroyed. */
+        psa_reset_key_attributes(&attributes);
+        return 1;
+    }
+    PSA_ASSERT(status);
     if (!PSA_KEY_TYPE_IS_ASYMMETRIC(psa_get_key_type(&attributes))) {
         exported_size = PSA_EXPORT_KEY_OUTPUT_SIZE(
             psa_get_key_type(&attributes),
             psa_get_key_bits(&attributes));
         TEST_CALLOC(exported, exported_size);
 
-        TEST_EQUAL(psa_export_public_key(key, exported,
-                                         exported_size, &exported_length),
-                   PSA_ERROR_INVALID_ARGUMENT);
+        status = psa_export_public_key(key, exported,
+                                       exported_size, &exported_length);
+        if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+            /* The key has been destroyed. */
+            ok = 1;
+            goto exit;
+        }
+        TEST_EQUAL(status, PSA_ERROR_INVALID_ARGUMENT);
         ok = 1;
         goto exit;
     }
@@ -928,9 +1093,14 @@
                                                       psa_get_key_bits(&attributes));
     TEST_CALLOC(exported, exported_size);
 
-    PSA_ASSERT(psa_export_public_key(key,
-                                     exported, exported_size,
-                                     &exported_length));
+    status = psa_export_public_key(key, exported,
+                                   exported_size, &exported_length);
+    if (key_destroyable && status == PSA_ERROR_INVALID_HANDLE) {
+        /* The key has been destroyed. */
+        ok = 1;
+        goto exit;
+    }
+    PSA_ASSERT(status);
     ok = mbedtls_test_psa_exported_key_sanity_check(
         public_type, psa_get_key_bits(&attributes),
         exported, exported_length);
@@ -948,38 +1118,43 @@
 
 int mbedtls_test_psa_exercise_key(mbedtls_svc_key_id_t key,
                                   psa_key_usage_t usage,
-                                  psa_algorithm_t alg)
+                                  psa_algorithm_t alg,
+                                  int key_destroyable)
 {
     int ok = 0;
 
-    if (!check_key_attributes_sanity(key)) {
+    if (!check_key_attributes_sanity(key, key_destroyable)) {
         return 0;
     }
 
     if (alg == 0) {
         ok = 1; /* If no algorithm, do nothing (used for raw data "keys"). */
     } else if (PSA_ALG_IS_MAC(alg)) {
-        ok = exercise_mac_key(key, usage, alg);
+        ok = exercise_mac_key(key, usage, alg, key_destroyable);
     } else if (PSA_ALG_IS_CIPHER(alg)) {
-        ok = exercise_cipher_key(key, usage, alg);
+        ok = exercise_cipher_key(key, usage, alg, key_destroyable);
     } else if (PSA_ALG_IS_AEAD(alg)) {
-        ok = exercise_aead_key(key, usage, alg);
+        ok = exercise_aead_key(key, usage, alg, key_destroyable);
     } else if (PSA_ALG_IS_SIGN(alg)) {
-        ok = exercise_signature_key(key, usage, alg);
+        ok = exercise_signature_key(key, usage, alg, key_destroyable);
     } else if (PSA_ALG_IS_ASYMMETRIC_ENCRYPTION(alg)) {
-        ok = exercise_asymmetric_encryption_key(key, usage, alg);
+        ok = exercise_asymmetric_encryption_key(key, usage, alg,
+                                                key_destroyable);
     } else if (PSA_ALG_IS_KEY_DERIVATION(alg)) {
-        ok = exercise_key_derivation_key(key, usage, alg);
+        ok = exercise_key_derivation_key(key, usage, alg, key_destroyable);
     } else if (PSA_ALG_IS_RAW_KEY_AGREEMENT(alg)) {
-        ok = exercise_raw_key_agreement_key(key, usage, alg);
+        ok = exercise_raw_key_agreement_key(key, usage, alg, key_destroyable);
     } else if (PSA_ALG_IS_KEY_AGREEMENT(alg)) {
-        ok = exercise_key_agreement_key(key, usage, alg);
+        ok = exercise_key_agreement_key(key, usage, alg, key_destroyable);
     } else {
         TEST_FAIL("No code to exercise this category of algorithm");
     }
 
-    ok = ok && exercise_export_key(key, usage);
-    ok = ok && exercise_export_public_key(key);
+    ok = ok && exercise_export_key(key,
+                                   usage,
+                                   key_destroyable);
+    ok = ok && exercise_export_public_key(key,
+                                          key_destroyable);
 
 exit:
     return ok;
diff --git a/tests/src/test_helpers/ssl_helpers.c b/tests/src/test_helpers/ssl_helpers.c
index 56e03f1..963938f 100644
--- a/tests/src/test_helpers/ssl_helpers.c
+++ b/tests/src/test_helpers/ssl_helpers.c
@@ -1793,7 +1793,13 @@
 
 #if defined(MBEDTLS_SSL_EARLY_DATA)
     session->max_early_data_size = 0x87654321;
-#endif
+#if defined(MBEDTLS_SSL_ALPN) && defined(MBEDTLS_SSL_SRV_C)
+    int ret = mbedtls_ssl_session_set_ticket_alpn(session, "ALPNExample");
+    if (ret != 0) {
+        return -1;
+    }
+#endif /* MBEDTLS_SSL_ALPN && MBEDTLS_SSL_SRV_C */
+#endif /* MBEDTLS_SSL_EARLY_DATA */
 
 #if defined(MBEDTLS_HAVE_TIME) && defined(MBEDTLS_SSL_SRV_C)
     if (session->endpoint == MBEDTLS_SSL_IS_SERVER) {
diff --git a/tests/suites/test_suite_pkparse.function b/tests/suites/test_suite_pkparse.function
index 7dc8413..a06fc30 100644
--- a/tests/suites/test_suite_pkparse.function
+++ b/tests/suites/test_suite_pkparse.function
@@ -57,7 +57,7 @@
     if (mbedtls_test_can_exercise_psa_algorithm(exercise_alg)) {
         TEST_ASSERT(mbedtls_test_psa_exercise_key(psa_key,
                                                   exercise_usage,
-                                                  exercise_alg));
+                                                  exercise_alg, 0));
     }
 
     mbedtls_test_set_step((unsigned long) -1);
diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data
index b633c6f..a3a457d 100644
--- a/tests/suites/test_suite_psa_crypto.data
+++ b/tests/suites/test_suite_psa_crypto.data
@@ -4278,6 +4278,50 @@
 depends_on:PSA_WANT_ALG_SHA_256:PSA_WANT_ALG_TLS12_PRF
 import_and_exercise_key:"c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0":PSA_KEY_TYPE_DERIVE:192:PSA_ALG_TLS12_PRF(PSA_ALG_SHA_256)
 
+PSA concurrently import/exercise same key: RSA keypair, PKCS#1 v1.5 raw
+depends_on:PSA_WANT_ALG_RSA_PKCS1V15_SIGN:PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC:PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_IMPORT
+concurrently_use_same_persistent_key:"3082025e02010002818100af057d396ee84fb75fdbb5c2b13c7fe5a654aa8aa2470b541ee1feb0b12d25c79711531249e1129628042dbbb6c120d1443524ef4c0e6e1d8956eeb2077af12349ddeee54483bc06c2c61948cd02b202e796aebd94d3a7cbf859c2c1819c324cb82b9cd34ede263a2abffe4733f077869e8660f7d6834da53d690ef7985f6bc3020301000102818100874bf0ffc2f2a71d14671ddd0171c954d7fdbf50281e4f6d99ea0e1ebcf82faa58e7b595ffb293d1abe17f110b37c48cc0f36c37e84d876621d327f64bbe08457d3ec4098ba2fa0a319fba411c2841ed7be83196a8cdf9daa5d00694bc335fc4c32217fe0488bce9cb7202e59468b1ead119000477db2ca797fac19eda3f58c1024100e2ab760841bb9d30a81d222de1eb7381d82214407f1b975cbbfe4e1a9467fd98adbd78f607836ca5be1928b9d160d97fd45c12d6b52e2c9871a174c66b488113024100c5ab27602159ae7d6f20c3c2ee851e46dc112e689e28d5fcbbf990a99ef8a90b8bb44fd36467e7fc1789ceb663abda338652c3c73f111774902e840565927091024100b6cdbd354f7df579a63b48b3643e353b84898777b48b15f94e0bfc0567a6ae5911d57ad6409cf7647bf96264e9bd87eb95e263b7110b9a1f9f94acced0fafa4d024071195eec37e8d257decfc672b07ae639f10cbb9b0c739d0c809968d644a94e3fd6ed9287077a14583f379058f76a8aecd43c62dc8c0f41766650d725275ac4a1024100bb32d133edc2e048d463388b7be9cb4be29f4b6250be603e70e3647501c97ddde20a4e71be95fd5e71784e25aca4baf25be5738aae59bbfe1c997781447a2b24":PSA_KEY_TYPE_RSA_KEY_PAIR:1024:PSA_ALG_RSA_PKCS1V15_SIGN_RAW:100
+
+PSA concurrently import/exercise same key: RSA keypair, PSS-SHA-256
+depends_on:PSA_WANT_ALG_RSA_PSS:PSA_WANT_ALG_SHA_256:PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC:PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_IMPORT
+concurrently_use_same_persistent_key:"3082025e02010002818100af057d396ee84fb75fdbb5c2b13c7fe5a654aa8aa2470b541ee1feb0b12d25c79711531249e1129628042dbbb6c120d1443524ef4c0e6e1d8956eeb2077af12349ddeee54483bc06c2c61948cd02b202e796aebd94d3a7cbf859c2c1819c324cb82b9cd34ede263a2abffe4733f077869e8660f7d6834da53d690ef7985f6bc3020301000102818100874bf0ffc2f2a71d14671ddd0171c954d7fdbf50281e4f6d99ea0e1ebcf82faa58e7b595ffb293d1abe17f110b37c48cc0f36c37e84d876621d327f64bbe08457d3ec4098ba2fa0a319fba411c2841ed7be83196a8cdf9daa5d00694bc335fc4c32217fe0488bce9cb7202e59468b1ead119000477db2ca797fac19eda3f58c1024100e2ab760841bb9d30a81d222de1eb7381d82214407f1b975cbbfe4e1a9467fd98adbd78f607836ca5be1928b9d160d97fd45c12d6b52e2c9871a174c66b488113024100c5ab27602159ae7d6f20c3c2ee851e46dc112e689e28d5fcbbf990a99ef8a90b8bb44fd36467e7fc1789ceb663abda338652c3c73f111774902e840565927091024100b6cdbd354f7df579a63b48b3643e353b84898777b48b15f94e0bfc0567a6ae5911d57ad6409cf7647bf96264e9bd87eb95e263b7110b9a1f9f94acced0fafa4d024071195eec37e8d257decfc672b07ae639f10cbb9b0c739d0c809968d644a94e3fd6ed9287077a14583f379058f76a8aecd43c62dc8c0f41766650d725275ac4a1024100bb32d133edc2e048d463388b7be9cb4be29f4b6250be603e70e3647501c97ddde20a4e71be95fd5e71784e25aca4baf25be5738aae59bbfe1c997781447a2b24":PSA_KEY_TYPE_RSA_KEY_PAIR:1024:PSA_ALG_RSA_PSS(PSA_ALG_SHA_256):100
+
+PSA concurrently import/exercise same key: RSA keypair, PSS-any-salt-SHA-256
+depends_on:PSA_WANT_ALG_RSA_PSS:PSA_WANT_ALG_SHA_256:PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC:PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_IMPORT
+concurrently_use_same_persistent_key:"3082025e02010002818100af057d396ee84fb75fdbb5c2b13c7fe5a654aa8aa2470b541ee1feb0b12d25c79711531249e1129628042dbbb6c120d1443524ef4c0e6e1d8956eeb2077af12349ddeee54483bc06c2c61948cd02b202e796aebd94d3a7cbf859c2c1819c324cb82b9cd34ede263a2abffe4733f077869e8660f7d6834da53d690ef7985f6bc3020301000102818100874bf0ffc2f2a71d14671ddd0171c954d7fdbf50281e4f6d99ea0e1ebcf82faa58e7b595ffb293d1abe17f110b37c48cc0f36c37e84d876621d327f64bbe08457d3ec4098ba2fa0a319fba411c2841ed7be83196a8cdf9daa5d00694bc335fc4c32217fe0488bce9cb7202e59468b1ead119000477db2ca797fac19eda3f58c1024100e2ab760841bb9d30a81d222de1eb7381d82214407f1b975cbbfe4e1a9467fd98adbd78f607836ca5be1928b9d160d97fd45c12d6b52e2c9871a174c66b488113024100c5ab27602159ae7d6f20c3c2ee851e46dc112e689e28d5fcbbf990a99ef8a90b8bb44fd36467e7fc1789ceb663abda338652c3c73f111774902e840565927091024100b6cdbd354f7df579a63b48b3643e353b84898777b48b15f94e0bfc0567a6ae5911d57ad6409cf7647bf96264e9bd87eb95e263b7110b9a1f9f94acced0fafa4d024071195eec37e8d257decfc672b07ae639f10cbb9b0c739d0c809968d644a94e3fd6ed9287077a14583f379058f76a8aecd43c62dc8c0f41766650d725275ac4a1024100bb32d133edc2e048d463388b7be9cb4be29f4b6250be603e70e3647501c97ddde20a4e71be95fd5e71784e25aca4baf25be5738aae59bbfe1c997781447a2b24":PSA_KEY_TYPE_RSA_KEY_PAIR:1024:PSA_ALG_RSA_PSS(PSA_ALG_SHA_256):100
+
+PSA concurrently import/exercise same key: RSA public key, PKCS#1 v1.5 raw
+depends_on:PSA_WANT_ALG_RSA_PKCS1V15_SIGN:PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY
+concurrently_use_same_persistent_key:"30818902818100af057d396ee84fb75fdbb5c2b13c7fe5a654aa8aa2470b541ee1feb0b12d25c79711531249e1129628042dbbb6c120d1443524ef4c0e6e1d8956eeb2077af12349ddeee54483bc06c2c61948cd02b202e796aebd94d3a7cbf859c2c1819c324cb82b9cd34ede263a2abffe4733f077869e8660f7d6834da53d690ef7985f6bc30203010001":PSA_KEY_TYPE_RSA_PUBLIC_KEY:1024:PSA_ALG_RSA_PKCS1V15_SIGN_RAW:100
+
+PSA concurrently import/exercise same key: RSA public key, PSS-SHA-256
+depends_on:PSA_WANT_ALG_RSA_PSS:PSA_WANT_ALG_SHA_256:PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY
+concurrently_use_same_persistent_key:"30818902818100af057d396ee84fb75fdbb5c2b13c7fe5a654aa8aa2470b541ee1feb0b12d25c79711531249e1129628042dbbb6c120d1443524ef4c0e6e1d8956eeb2077af12349ddeee54483bc06c2c61948cd02b202e796aebd94d3a7cbf859c2c1819c324cb82b9cd34ede263a2abffe4733f077869e8660f7d6834da53d690ef7985f6bc30203010001":PSA_KEY_TYPE_RSA_PUBLIC_KEY:1024:PSA_ALG_RSA_PSS(PSA_ALG_SHA_256):100
+
+PSA concurrently import/exercise same key: RSA public key, PSS-any-salt-SHA-256
+depends_on:PSA_WANT_ALG_RSA_PSS:PSA_WANT_ALG_SHA_256:PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY
+concurrently_use_same_persistent_key:"30818902818100af057d396ee84fb75fdbb5c2b13c7fe5a654aa8aa2470b541ee1feb0b12d25c79711531249e1129628042dbbb6c120d1443524ef4c0e6e1d8956eeb2077af12349ddeee54483bc06c2c61948cd02b202e796aebd94d3a7cbf859c2c1819c324cb82b9cd34ede263a2abffe4733f077869e8660f7d6834da53d690ef7985f6bc30203010001":PSA_KEY_TYPE_RSA_PUBLIC_KEY:1024:PSA_ALG_RSA_PSS_ANY_SALT(PSA_ALG_SHA_256):100
+
+PSA concurrently import/exercise same key: ECP SECP256R1 keypair, ECDSA
+depends_on:PSA_WANT_ALG_ECDSA:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_BASIC:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_EXPORT:PSA_WANT_ECC_SECP_R1_256
+concurrently_use_same_persistent_key:"49c9a8c18c4b885638c431cf1df1c994131609b580d4fd43a0cab17db2f13eee":PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1):256:PSA_ALG_ECDSA_ANY:100
+
+PSA concurrently import/exercise same key: ECP SECP256R1 keypair, deterministic ECDSA
+depends_on:PSA_WANT_ALG_DETERMINISTIC_ECDSA:PSA_WANT_ALG_SHA_256:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_BASIC:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_EXPORT:PSA_WANT_ECC_SECP_R1_256
+concurrently_use_same_persistent_key:"49c9a8c18c4b885638c431cf1df1c994131609b580d4fd43a0cab17db2f13eee":PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1):256:PSA_ALG_DETERMINISTIC_ECDSA( PSA_ALG_SHA_256 ):100
+
+PSA concurrently import/exercise same key: ECP SECP256R1 keypair, ECDH
+depends_on:PSA_WANT_ALG_ECDH:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_BASIC:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_EXPORT:PSA_WANT_ECC_SECP_R1_256
+concurrently_use_same_persistent_key:"49c9a8c18c4b885638c431cf1df1c994131609b580d4fd43a0cab17db2f13eee":PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1):256:PSA_ALG_ECDH:100
+
+PSA concurrently import/exercise same key: HKDF SHA-256
+depends_on:PSA_WANT_ALG_HKDF:PSA_WANT_ALG_SHA_256
+concurrently_use_same_persistent_key:"c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0":PSA_KEY_TYPE_DERIVE:192:PSA_ALG_HKDF(PSA_ALG_SHA_256):100
+
+PSA concurrently import/exercise same key: TLS 1.2 PRF SHA-256
+depends_on:PSA_WANT_ALG_SHA_256:PSA_WANT_ALG_TLS12_PRF
+concurrently_use_same_persistent_key:"c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0":PSA_KEY_TYPE_DERIVE:192:PSA_ALG_TLS12_PRF(PSA_ALG_SHA_256):100
+
 PSA sign hash: RSA PKCS#1 v1.5, raw
 depends_on:PSA_WANT_ALG_RSA_PKCS1V15_SIGN:PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC:PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_IMPORT
 sign_hash_deterministic:PSA_KEY_TYPE_RSA_KEY_PAIR:"3082025e02010002818100af057d396ee84fb75fdbb5c2b13c7fe5a654aa8aa2470b541ee1feb0b12d25c79711531249e1129628042dbbb6c120d1443524ef4c0e6e1d8956eeb2077af12349ddeee54483bc06c2c61948cd02b202e796aebd94d3a7cbf859c2c1819c324cb82b9cd34ede263a2abffe4733f077869e8660f7d6834da53d690ef7985f6bc3020301000102818100874bf0ffc2f2a71d14671ddd0171c954d7fdbf50281e4f6d99ea0e1ebcf82faa58e7b595ffb293d1abe17f110b37c48cc0f36c37e84d876621d327f64bbe08457d3ec4098ba2fa0a319fba411c2841ed7be83196a8cdf9daa5d00694bc335fc4c32217fe0488bce9cb7202e59468b1ead119000477db2ca797fac19eda3f58c1024100e2ab760841bb9d30a81d222de1eb7381d82214407f1b975cbbfe4e1a9467fd98adbd78f607836ca5be1928b9d160d97fd45c12d6b52e2c9871a174c66b488113024100c5ab27602159ae7d6f20c3c2ee851e46dc112e689e28d5fcbbf990a99ef8a90b8bb44fd36467e7fc1789ceb663abda338652c3c73f111774902e840565927091024100b6cdbd354f7df579a63b48b3643e353b84898777b48b15f94e0bfc0567a6ae5911d57ad6409cf7647bf96264e9bd87eb95e263b7110b9a1f9f94acced0fafa4d024071195eec37e8d257decfc672b07ae639f10cbb9b0c739d0c809968d644a94e3fd6ed9287077a14583f379058f76a8aecd43c62dc8c0f41766650d725275ac4a1024100bb32d133edc2e048d463388b7be9cb4be29f4b6250be603e70e3647501c97ddde20a4e71be95fd5e71784e25aca4baf25be5738aae59bbfe1c997781447a2b24":PSA_ALG_RSA_PKCS1V15_SIGN_RAW:"616263":"2c7744983f023ac7bb1c55529d83ed11a76a7898a1bb5ce191375a4aa7495a633d27879ff58eba5a57371c34feb1180e8b850d552476ebb5634df620261992f12ebee9097041dbbea85a42d45b344be5073ceb772ffc604954b9158ba81ec3dc4d9d65e3ab7aa318165f38c36f841f1c69cb1cfa494aa5cbb4d6c0efbafb043a"
diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function
index 1141597..7a242fd 100644
--- a/tests/suites/test_suite_psa_crypto.function
+++ b/tests/suites/test_suite_psa_crypto.function
@@ -1338,6 +1338,126 @@
 }
 
 #if defined(MBEDTLS_THREADING_PTHREAD)
+
+typedef struct same_key_context {
+    data_t *data;
+    mbedtls_svc_key_id_t key;
+    psa_key_attributes_t *attributes;
+    int type;
+    int bits;
+    /* The following two parameters are used to ensure that when multiple
+     * threads attempt to load/destroy the key, exactly one thread succeeds. */
+    int key_loaded;
+    mbedtls_threading_mutex_t MBEDTLS_PRIVATE(key_loaded_mutex);
+}
+same_key_context;
+
+/* Attempt to import the key in ctx. This handles any valid error codes
+ * and reports an error for any invalid codes. This function also insures
+ * that once imported by some thread, all threads can use the key. */
+void *thread_import_key(void *ctx)
+{
+    mbedtls_svc_key_id_t returned_key_id;
+    same_key_context *skc = (struct same_key_context *) ctx;
+    psa_key_attributes_t got_attributes = PSA_KEY_ATTRIBUTES_INIT;
+
+    /* Import the key, exactly one thread must succeed. */
+    psa_status_t status = psa_import_key(skc->attributes, skc->data->x,
+                                         skc->data->len, &returned_key_id);
+    switch (status) {
+        case PSA_SUCCESS:
+            if (mbedtls_mutex_lock(&skc->key_loaded_mutex) == 0) {
+                if (skc->key_loaded) {
+                    mbedtls_mutex_unlock(&skc->key_loaded_mutex);
+                    /* More than one thread has succeeded, report a failure. */
+                    TEST_FAIL("The same key has been loaded into the key store multiple times.");
+                }
+                skc->key_loaded = 1;
+                mbedtls_mutex_unlock(&skc->key_loaded_mutex);
+            }
+            break;
+        case PSA_ERROR_INSUFFICIENT_MEMORY:
+            /* If all of the key slots are reserved when a thread
+             * locks the mutex to reserve a new slot, it will return
+             * PSA_ERROR_INSUFFICIENT_MEMORY; this is correct behaviour.
+             * There is a chance for this to occur here when the number of
+             * threads running this function is larger than the number of
+             * free key slots. Each thread reserves an empty key slot,
+             * unlocks the mutex, then relocks it to finalize key creation.
+             * It is at that point where the thread sees that the key
+             * already exists, releases the reserved slot,
+             * and returns PSA_ERROR_ALREADY_EXISTS.
+             * There is no guarantee that the key is loaded upon this return
+             * code, so we can't test the key information. Just stop this
+             * thread from executing, note that this is not an error. */
+            goto exit;
+            break;
+        case PSA_ERROR_ALREADY_EXISTS:
+            /* The key has been loaded by a different thread. */
+            break;
+        default:
+            PSA_ASSERT(status);
+    }
+    /* At this point the key must exist, test the key information. */
+    status = psa_get_key_attributes(skc->key, &got_attributes);
+    if (status == PSA_ERROR_INSUFFICIENT_MEMORY) {
+        /* This is not a test failure. The following sequence of events
+         * causes this to occur:
+         * 1: This thread successfuly imports a persistent key skc->key.
+         * 2: N threads reserve an empty key slot in psa_import_key,
+         *    where N is equal to the number of free key slots.
+         * 3: A final thread attempts to reserve an empty key slot, kicking
+         *    skc->key (which has no registered readers) out of its slot.
+         * 4: This thread calls psa_get_key_attributes(skc->key,...):
+         *    it sees that skc->key is not in a slot, attempts to load it and
+         *    finds that there are no free slots.
+         * This thread returns PSA_ERROR_INSUFFICIENT_MEMORY.
+         *
+         * The PSA spec allows this behaviour, it is an unavoidable consequence
+         * of allowing persistent keys to be kicked out of the key store while
+         * they are still valid. */
+        goto exit;
+    }
+    PSA_ASSERT(status);
+    TEST_EQUAL(psa_get_key_type(&got_attributes), skc->type);
+    TEST_EQUAL(psa_get_key_bits(&got_attributes), skc->bits);
+
+exit:
+    /* Key attributes may have been returned by psa_get_key_attributes(),
+     * reset them as required. */
+    psa_reset_key_attributes(&got_attributes);
+    return NULL;
+}
+
+void *thread_use_and_destroy_key(void *ctx)
+{
+    same_key_context *skc = (struct same_key_context *) ctx;
+
+    /* Do something with the key according
+     * to its type and permitted usage. */
+    TEST_ASSERT(mbedtls_test_psa_exercise_key(skc->key,
+                                              skc->attributes->policy.usage,
+                                              skc->attributes->policy.alg, 1));
+
+    psa_status_t status = psa_destroy_key(skc->key);
+    if (status == PSA_SUCCESS) {
+        if (mbedtls_mutex_lock(&skc->key_loaded_mutex) == 0) {
+            /* Ensure that we are the only thread to succeed. */
+            if (skc->key_loaded != 1) {
+                mbedtls_mutex_unlock(&skc->key_loaded_mutex);
+                TEST_FAIL("The same key has been destroyed multiple times.");
+            }
+            skc->key_loaded = 0;
+            mbedtls_mutex_unlock(&skc->key_loaded_mutex);
+        }
+    } else {
+        TEST_EQUAL(status, PSA_ERROR_INVALID_HANDLE);
+    }
+
+exit:
+    return NULL;
+}
+
 typedef struct generate_key_context {
     psa_key_type_t type;
     psa_key_usage_t usage;
@@ -1379,7 +1499,7 @@
 
         /* Do something with the key according
          * to its type and permitted usage. */
-        if (!mbedtls_test_psa_exercise_key(key, gkc->usage, gkc->alg)) {
+        if (!mbedtls_test_psa_exercise_key(key, gkc->usage, gkc->alg, 0)) {
             psa_destroy_key(key);
             goto exit;
         }
@@ -1715,7 +1835,7 @@
      * this doesn't directly validate the implementation, but it still helps
      * by cross-validating the test data with the sanity check code. */
     if (!psa_key_lifetime_is_external(lifetime)) {
-        if (!mbedtls_test_psa_exercise_key(key, usage_arg, 0)) {
+        if (!mbedtls_test_psa_exercise_key(key, usage_arg, 0, 0)) {
             goto exit;
         }
     }
@@ -1824,6 +1944,78 @@
 }
 /* END_CASE */
 
+
+#if defined(MBEDTLS_THREADING_PTHREAD)
+/* BEGIN_CASE depends_on:MBEDTLS_THREADING_PTHREAD:MBEDTLS_PSA_CRYPTO_STORAGE_C */
+void concurrently_use_same_persistent_key(data_t *data,
+                                          int type_arg,
+                                          int bits_arg,
+                                          int alg_arg,
+                                          int thread_count_arg)
+{
+    size_t thread_count = (size_t) thread_count_arg;
+    mbedtls_test_thread_t *threads = NULL;
+    mbedtls_svc_key_id_t key_id = mbedtls_svc_key_id_make(1, 1);
+    same_key_context skc;
+    skc.data = data;
+    skc.key = key_id;
+    skc.type = type_arg;
+    skc.bits = bits_arg;
+    skc.key_loaded = 0;
+    mbedtls_mutex_init(&skc.key_loaded_mutex);
+    psa_key_usage_t usage = mbedtls_test_psa_usage_to_exercise(skc.type, alg_arg);
+    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+
+    PSA_ASSERT(psa_crypto_init());
+
+    psa_set_key_id(&attributes, key_id);
+    psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT);
+    psa_set_key_usage_flags(&attributes, usage);
+    psa_set_key_algorithm(&attributes, alg_arg);
+    psa_set_key_type(&attributes, type_arg);
+    psa_set_key_bits(&attributes, bits_arg);
+    skc.attributes = &attributes;
+
+    TEST_CALLOC(threads, sizeof(mbedtls_test_thread_t) * thread_count);
+
+    /* Test that when multiple threads import the same key,
+     * exactly one thread succeeds and the rest fail with valid errors.
+     * Also test that all threads can use the key as soon as it has been
+     * imported. */
+    for (size_t i = 0; i < thread_count; i++) {
+        TEST_EQUAL(
+            mbedtls_test_thread_create(&threads[i], thread_import_key,
+                                       (void *) &skc), 0);
+    }
+
+    /* Join threads. */
+    for (size_t i = 0; i < thread_count; i++) {
+        TEST_EQUAL(mbedtls_test_thread_join(&threads[i]), 0);
+    }
+
+    /* Test that when multiple threads use and destroy a key no corruption
+     * occurs, and exactly one thread succeeds when destroying the key. */
+    for (size_t i = 0; i < thread_count; i++) {
+        TEST_EQUAL(
+            mbedtls_test_thread_create(&threads[i], thread_use_and_destroy_key,
+                                       (void *) &skc), 0);
+    }
+
+    /* Join threads. */
+    for (size_t i = 0; i < thread_count; i++) {
+        TEST_EQUAL(mbedtls_test_thread_join(&threads[i]), 0);
+    }
+    /* Ensure that one thread succeeded in destroying the key. */
+    TEST_ASSERT(!skc.key_loaded);
+exit:
+    psa_reset_key_attributes(&attributes);
+    mbedtls_mutex_free(&skc.key_loaded_mutex);
+    mbedtls_free(threads);
+    PSA_DONE();
+}
+/* END_CASE */
+#endif
+
 /* BEGIN_CASE */
 void import_and_exercise_key(data_t *data,
                              int type_arg,
@@ -1853,7 +2045,7 @@
     TEST_EQUAL(psa_get_key_bits(&got_attributes), bits);
 
     /* Do something with the key according to its type and permitted usage. */
-    if (!mbedtls_test_psa_exercise_key(key, usage, alg)) {
+    if (!mbedtls_test_psa_exercise_key(key, usage, alg, 0)) {
         goto exit;
     }
 
@@ -2490,7 +2682,7 @@
                               &key));
 
     PSA_ASSERT(psa_key_derivation_setup(&operation, exercise_alg));
-    status = mbedtls_test_psa_key_agreement_with_self(&operation, key);
+    status = mbedtls_test_psa_key_agreement_with_self(&operation, key, 0);
 
     TEST_EQUAL(status, expected_status);
 
@@ -2529,10 +2721,10 @@
     TEST_EQUAL(psa_get_key_algorithm(&got_attributes), alg);
     TEST_EQUAL(psa_get_key_enrollment_algorithm(&got_attributes), alg2);
 
-    if (!mbedtls_test_psa_exercise_key(key, usage, alg)) {
+    if (!mbedtls_test_psa_exercise_key(key, usage, alg, 0)) {
         goto exit;
     }
-    if (!mbedtls_test_psa_exercise_key(key, usage, alg2)) {
+    if (!mbedtls_test_psa_exercise_key(key, usage, alg2, 0)) {
         goto exit;
     }
 
@@ -2572,7 +2764,7 @@
     PSA_ASSERT(psa_import_key(&attributes, key_data->x, key_data->len,
                               &key));
 
-    status = mbedtls_test_psa_raw_key_agreement_with_self(exercise_alg, key);
+    status = mbedtls_test_psa_raw_key_agreement_with_self(exercise_alg, key, 0);
 
     TEST_EQUAL(status, expected_status);
 
@@ -2663,10 +2855,10 @@
     }
 
     if (!psa_key_lifetime_is_external(target_lifetime)) {
-        if (!mbedtls_test_psa_exercise_key(target_key, expected_usage, expected_alg)) {
+        if (!mbedtls_test_psa_exercise_key(target_key, expected_usage, expected_alg, 0)) {
             goto exit;
         }
-        if (!mbedtls_test_psa_exercise_key(target_key, expected_usage, expected_alg2)) {
+        if (!mbedtls_test_psa_exercise_key(target_key, expected_usage, expected_alg2, 0)) {
             goto exit;
         }
     }
@@ -8681,7 +8873,7 @@
                 // When taking a private key as secret input, use key agreement
                 // to add the shared secret to the derivation
                 TEST_EQUAL(mbedtls_test_psa_key_agreement_with_self(
-                               &operation, keys[i]),
+                               &operation, keys[i], 0),
                            expected_statuses[i]);
             } else {
                 TEST_EQUAL(psa_key_derivation_input_key(&operation, steps[i],
@@ -8780,7 +8972,7 @@
     if (!mbedtls_test_psa_setup_key_derivation_wrap(&operation, key, alg,
                                                     input1, input1_length,
                                                     input2, input2_length,
-                                                    capacity)) {
+                                                    capacity, 0)) {
         goto exit;
     }
 
@@ -9099,7 +9291,7 @@
     if (!mbedtls_test_psa_setup_key_derivation_wrap(&operation, key, alg,
                                                     input1->x, input1->len,
                                                     input2->x, input2->len,
-                                                    requested_capacity)) {
+                                                    requested_capacity, 0)) {
         goto exit;
     }
 
@@ -9216,7 +9408,7 @@
     if (!mbedtls_test_psa_setup_key_derivation_wrap(&operation, base_key, alg,
                                                     input1->x, input1->len,
                                                     input2->x, input2->len,
-                                                    capacity)) {
+                                                    capacity, 0)) {
         goto exit;
     }
 
@@ -9233,7 +9425,7 @@
     TEST_EQUAL(psa_get_key_bits(&got_attributes), derived_bits);
 
     /* Exercise the derived key. */
-    if (!mbedtls_test_psa_exercise_key(derived_key, derived_usage, derived_alg)) {
+    if (!mbedtls_test_psa_exercise_key(derived_key, derived_usage, derived_alg, 0)) {
         goto exit;
     }
 
@@ -9286,7 +9478,7 @@
     if (!mbedtls_test_psa_setup_key_derivation_wrap(&operation, base_key, alg,
                                                     input1->x, input1->len,
                                                     input2->x, input2->len,
-                                                    capacity)) {
+                                                    capacity, 0)) {
         goto exit;
     }
 
@@ -9299,7 +9491,7 @@
     if (!mbedtls_test_psa_setup_key_derivation_wrap(&operation, base_key, alg,
                                                     input1->x, input1->len,
                                                     input2->x, input2->len,
-                                                    capacity)) {
+                                                    capacity, 0)) {
         goto exit;
     }
 
@@ -9370,7 +9562,7 @@
             &operation, base_key, alg,
             input1->x, input1->len,
             input2->x, input2->len,
-            PSA_KEY_DERIVATION_UNLIMITED_CAPACITY) == 0) {
+            PSA_KEY_DERIVATION_UNLIMITED_CAPACITY, 0) == 0) {
         goto exit;
     }
 
@@ -9435,7 +9627,7 @@
             &operation, base_key, alg,
             input1->x, input1->len,
             input2->x, input2->len,
-            PSA_KEY_DERIVATION_UNLIMITED_CAPACITY) == 0) {
+            PSA_KEY_DERIVATION_UNLIMITED_CAPACITY, 0) == 0) {
         goto exit;
     }
 
@@ -9499,7 +9691,7 @@
     if (!mbedtls_test_psa_setup_key_derivation_wrap(&operation, base_key, alg,
                                                     input1->x, input1->len,
                                                     input2->x, input2->len,
-                                                    SIZE_MAX)) {
+                                                    SIZE_MAX, 0)) {
         goto exit;
     }
 
@@ -9941,7 +10133,7 @@
     TEST_EQUAL(psa_get_key_bits(&got_attributes), bits);
 
     /* Do something with the key according to its type and permitted usage. */
-    if (!mbedtls_test_psa_exercise_key(key, usage, alg)) {
+    if (!mbedtls_test_psa_exercise_key(key, usage, alg, 0)) {
         goto exit;
     }
 
@@ -10011,7 +10203,7 @@
 #endif
 
     /* Do something with the key according to its type and permitted usage. */
-    if (!mbedtls_test_psa_exercise_key(key, usage, alg)) {
+    if (!mbedtls_test_psa_exercise_key(key, usage, alg, 0)) {
         goto exit;
     }
 
@@ -10162,7 +10354,7 @@
     }
 
     /* Do something with the key according to its type and permitted usage. */
-    if (!mbedtls_test_psa_exercise_key(key, usage_flags, alg)) {
+    if (!mbedtls_test_psa_exercise_key(key, usage_flags, alg, 0)) {
         goto exit;
     }
 
diff --git a/tests/suites/test_suite_psa_crypto_storage_format.function b/tests/suites/test_suite_psa_crypto_storage_format.function
index bb1e2c6..efaaba5 100644
--- a/tests/suites/test_suite_psa_crypto_storage_format.function
+++ b/tests/suites/test_suite_psa_crypto_storage_format.function
@@ -187,7 +187,7 @@
         TEST_ASSERT(mbedtls_test_psa_exercise_key(
                         key_id,
                         psa_get_key_usage_flags(expected_attributes),
-                        psa_get_key_algorithm(expected_attributes)));
+                        psa_get_key_algorithm(expected_attributes), 0));
     }
 
 
diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function
index 7ef5805..2fe4997 100644
--- a/tests/suites/test_suite_ssl.function
+++ b/tests/suites/test_suite_ssl.function
@@ -2104,6 +2104,14 @@
 #if defined(MBEDTLS_SSL_EARLY_DATA)
         TEST_ASSERT(
             original.max_early_data_size == restored.max_early_data_size);
+#if defined(MBEDTLS_SSL_ALPN) && defined(MBEDTLS_SSL_SRV_C)
+        if (endpoint_type == MBEDTLS_SSL_IS_SERVER) {
+            TEST_ASSERT(original.ticket_alpn != NULL);
+            TEST_ASSERT(restored.ticket_alpn != NULL);
+            TEST_MEMORY_COMPARE(original.ticket_alpn, strlen(original.ticket_alpn),
+                                restored.ticket_alpn, strlen(restored.ticket_alpn));
+        }
+#endif
 #endif
 
 #if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C)