Merge pull request #8779 from gilles-peskine-arm/rsa-bitlen-fix

Fix mbedtls_pk_get_bitlen for a key size that is not a multiple of 8
diff --git a/include/mbedtls/check_config.h b/include/mbedtls/check_config.h
index b211356..a7a346f 100644
--- a/include/mbedtls/check_config.h
+++ b/include/mbedtls/check_config.h
@@ -27,18 +27,8 @@
 #if !defined(MBEDTLS_PLATFORM_C)
 #error "MBEDTLS_PLATFORM_C is required on Windows"
 #endif
-
-/* Fix the config here. Not convenient to put an #ifdef _WIN32 in mbedtls_config.h as
- * it would confuse config.py. */
-#if !defined(MBEDTLS_PLATFORM_SNPRINTF_ALT) && \
-    !defined(MBEDTLS_PLATFORM_SNPRINTF_MACRO)
-#define MBEDTLS_PLATFORM_SNPRINTF_ALT
-#endif
-
-#if !defined(MBEDTLS_PLATFORM_VSNPRINTF_ALT) && \
-    !defined(MBEDTLS_PLATFORM_VSNPRINTF_MACRO)
-#define MBEDTLS_PLATFORM_VSNPRINTF_ALT
-#endif
+/* See auto-enabling SNPRINTF_ALT and VSNPRINTF_ALT
+ * in * config_adjust_legacy_crypto.h */
 #endif /* _MINGW32__ || (_MSC_VER && (_MSC_VER <= 1900)) */
 
 #if defined(TARGET_LIKE_MBED) && defined(MBEDTLS_NET_C)
@@ -54,65 +44,6 @@
 #error "MBEDTLS_HAVE_TIME_DATE without MBEDTLS_HAVE_TIME does not make sense"
 #endif
 
-/* Check that each MBEDTLS_ECP_DP_xxx symbol has its PSA_WANT_ECC_xxx counterpart
- * when PSA crypto is enabled. */
-#if defined(MBEDTLS_PSA_CRYPTO_CONFIG) || defined(MBEDTLS_PSA_CRYPTO_C)
-
-#if defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) && !defined(PSA_WANT_ECC_BRAINPOOL_P_R1_256)
-#error "MBEDTLS_ECP_DP_BP256R1_ENABLED defined, but not its PSA counterpart"
-#endif
-
-#if defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) && !defined(PSA_WANT_ECC_BRAINPOOL_P_R1_384)
-#error "MBEDTLS_ECP_DP_BP384R1_ENABLED defined, but not its PSA counterpart"
-#endif
-
-#if defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) && !defined(PSA_WANT_ECC_BRAINPOOL_P_R1_512)
-#error "MBEDTLS_ECP_DP_BP512R1_ENABLED defined, but not its PSA counterpart"
-#endif
-
-#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) && !defined(PSA_WANT_ECC_MONTGOMERY_255)
-#error "MBEDTLS_ECP_DP_CURVE25519_ENABLED defined, but not its PSA counterpart"
-#endif
-
-#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) && !defined(PSA_WANT_ECC_MONTGOMERY_448)
-#error "MBEDTLS_ECP_DP_CURVE448_ENABLED defined, but not its PSA counterpart"
-#endif
-
-#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) && !defined(PSA_WANT_ECC_SECP_R1_192)
-#error "MBEDTLS_ECP_DP_SECP192R1_ENABLED defined, but not its PSA counterpart"
-#endif
-
-#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) && !defined(PSA_WANT_ECC_SECP_R1_224)
-#error "MBEDTLS_ECP_DP_SECP224R1_ENABLED defined, but not its PSA counterpart"
-#endif
-
-#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) && !defined(PSA_WANT_ECC_SECP_R1_256)
-#error "MBEDTLS_ECP_DP_SECP256R1_ENABLED defined, but not its PSA counterpart"
-#endif
-
-#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) && !defined(PSA_WANT_ECC_SECP_R1_384)
-#error "MBEDTLS_ECP_DP_SECP384R1_ENABLED defined, but not its PSA counterpart"
-#endif
-
-#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) && !defined(PSA_WANT_ECC_SECP_R1_521)
-#error "MBEDTLS_ECP_DP_SECP521R1_ENABLED defined, but not its PSA counterpart"
-#endif
-
-#if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) && !defined(PSA_WANT_ECC_SECP_K1_192)
-#error "MBEDTLS_ECP_DP_SECP192K1_ENABLED defined, but not its PSA counterpart"
-#endif
-
-/* SECP224K1 is buggy in PSA API so we skip this check */
-#if 0 && defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) && !defined(PSA_WANT_ECC_SECP_K1_224)
-#error "MBEDTLS_ECP_DP_SECP224K1_ENABLED defined, but not its PSA counterpart"
-#endif
-
-#if defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) && !defined(PSA_WANT_ECC_SECP_K1_256)
-#error "MBEDTLS_ECP_DP_SECP256K1_ENABLED defined, but not its PSA counterpart"
-#endif
-
-#endif /* MBEDTLS_PSA_CRYPTO_CONFIG || MBEDTLS_PSA_CRYPTO_C */
-
 /* Limitations on ECC key types acceleration: if we have any of `PUBLIC_KEY`,
  * `KEY_PAIR_BASIC`, `KEY_PAIR_IMPORT`, `KEY_PAIR_EXPORT` then we must have
  * all 4 of them.
@@ -234,9 +165,8 @@
 #endif
 #endif /* MBEDTLS_PK_C && MBEDTLS_USE_PSA_CRYPTO */
 
-#if defined(MBEDTLS_ECJPAKE_C) &&           \
-    ( !defined(MBEDTLS_ECP_C) ||            \
-      !( defined(MBEDTLS_MD_C) || defined(MBEDTLS_PSA_CRYPTO_C) ) )
+#if defined(MBEDTLS_ECJPAKE_C) && \
+    !defined(MBEDTLS_ECP_C)
 #error "MBEDTLS_ECJPAKE_C defined, but not all prerequisites"
 #endif
 
@@ -281,23 +211,8 @@
 #error "MBEDTLS_PK_PARSE_C defined, but not all prerequisites"
 #endif
 
-/* Helpers for hash dependencies, will be undefined at the end of the file */
-/* Do SHA-256, 384, 512 to cover Entropy and TLS. */
-#if defined(MBEDTLS_SHA256_C) || \
-    (defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_SHA_256))
-#define MBEDTLS_MD_HAVE_SHA256
-#endif
-#if defined(MBEDTLS_SHA384_C) || \
-    (defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_SHA_384))
-#define MBEDTLS_MD_HAVE_SHA384
-#endif
-#if defined(MBEDTLS_SHA512_C) || \
-    (defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_SHA_512))
-#define MBEDTLS_MD_HAVE_SHA512
-#endif
-
 #if defined(MBEDTLS_ENTROPY_C) && \
-    !(defined(MBEDTLS_MD_HAVE_SHA512) || defined(MBEDTLS_MD_HAVE_SHA256))
+    !(defined(MBEDTLS_MD_CAN_SHA512) || defined(MBEDTLS_MD_CAN_SHA256))
 #error "MBEDTLS_ENTROPY_C defined, but not all prerequisites"
 #endif
 #if defined(MBEDTLS_ENTROPY_C) && \
@@ -305,24 +220,24 @@
 #error "MBEDTLS_CTR_DRBG_ENTROPY_LEN value too high"
 #endif
 #if defined(MBEDTLS_ENTROPY_C) &&                                            \
-    (defined(MBEDTLS_ENTROPY_FORCE_SHA256) || !defined(MBEDTLS_MD_HAVE_SHA512)) \
+    (defined(MBEDTLS_ENTROPY_FORCE_SHA256) || !defined(MBEDTLS_MD_CAN_SHA512)) \
     && defined(MBEDTLS_CTR_DRBG_ENTROPY_LEN) && (MBEDTLS_CTR_DRBG_ENTROPY_LEN > 32)
 #error "MBEDTLS_CTR_DRBG_ENTROPY_LEN value too high"
 #endif
 #if defined(MBEDTLS_ENTROPY_C) && \
-    defined(MBEDTLS_ENTROPY_FORCE_SHA256) && !defined(MBEDTLS_MD_HAVE_SHA256)
+    defined(MBEDTLS_ENTROPY_FORCE_SHA256) && !defined(MBEDTLS_MD_CAN_SHA256)
 #error "MBEDTLS_ENTROPY_FORCE_SHA256 defined, but not all prerequisites"
 #endif
 
 #if defined(__has_feature)
 #if __has_feature(memory_sanitizer)
-#define MBEDTLS_HAS_MEMSAN
+#define MBEDTLS_HAS_MEMSAN // #undef at the end of this paragraph
 #endif
 #endif
 #if defined(MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN) &&  !defined(MBEDTLS_HAS_MEMSAN)
 #error "MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN requires building with MemorySanitizer"
 #endif
-#undef MBEDTLS_HAS_MEMSAN
+#undef MBEDTLS_HAS_MEMSAN // temporary macro defined above
 
 #if defined(MBEDTLS_CCM_C) && \
     !(defined(MBEDTLS_CCM_GCM_CAN_AES) || defined(MBEDTLS_CCM_GCM_CAN_ARIA) || \
@@ -388,28 +303,6 @@
 #error "MBEDTLS_HMAC_DRBG_C defined, but not all prerequisites"
 #endif
 
-/* Helper for JPAKE dependencies, will be undefined at the end of the file */
-#if defined(MBEDTLS_USE_PSA_CRYPTO)
-#if defined(PSA_WANT_ALG_JPAKE) && defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_BASIC)
-#define MBEDTLS_PK_HAVE_JPAKE
-#endif
-#else /* MBEDTLS_USE_PSA_CRYPTO */
-#if defined(MBEDTLS_ECJPAKE_C)
-#define MBEDTLS_PK_HAVE_JPAKE
-#endif
-#endif /* MBEDTLS_USE_PSA_CRYPTO */
-
-/* Helper for curve SECP256R1 */
-#if defined(MBEDTLS_USE_PSA_CRYPTO)
-#if defined(PSA_WANT_ECC_SECP_R1_256)
-#define MBEDTLS_PK_HAVE_CURVE_SECP256R1
-#endif
-#else /* MBEDTLS_USE_PSA_CRYPTO */
-#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED)
-#define MBEDTLS_PK_HAVE_CURVE_SECP256R1
-#endif
-#endif /* MBEDTLS_USE_PSA_CRYPTO */
-
 #if defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) &&                 \
     ( !defined(MBEDTLS_CAN_ECDH) ||                                       \
       !defined(MBEDTLS_PK_CAN_ECDSA_SIGN) ||                                \
@@ -463,43 +356,48 @@
 #error "MBEDTLS_KEY_EXCHANGE_RSA_ENABLED defined, but not all prerequisites"
 #endif
 
-#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) &&                    \
-    ( !defined(MBEDTLS_PK_HAVE_JPAKE) ||                                \
-      !defined(MBEDTLS_PK_HAVE_CURVE_SECP256R1) )
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) &&    \
+    ( !defined(PSA_WANT_ALG_JPAKE) ||                   \
+      !defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_BASIC) || \
+      !defined(PSA_WANT_ECC_SECP_R1_256) )
 #error "MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED defined, but not all prerequisites"
 #endif
+#else /* MBEDTLS_USE_PSA_CRYPTO */
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) &&    \
+    ( !defined(MBEDTLS_ECJPAKE_C) ||                    \
+      !defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) )
+#error "MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED defined, but not all prerequisites"
+#endif
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
 
 /* Use of EC J-PAKE in TLS requires SHA-256. */
 #if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) &&                    \
-    !defined(MBEDTLS_MD_HAVE_SHA256)
+    !defined(MBEDTLS_MD_CAN_SHA256)
 #error "MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED defined, but not all prerequisites"
 #endif
 
 #if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) &&        \
-    !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) &&              \
-    ( !defined(MBEDTLS_SHA256_C) &&                             \
-      !defined(MBEDTLS_SHA512_C) &&                             \
-      !defined(MBEDTLS_SHA1_C) )
-#error "!MBEDTLS_SSL_KEEP_PEER_CERTIFICATE requires MBEDTLS_SHA512_C, MBEDTLS_SHA256_C or MBEDTLS_SHA1_C"
+    !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) &&            \
+    !defined(MBEDTLS_MD_CAN_SHA256) &&                        \
+    !defined(MBEDTLS_MD_CAN_SHA512) &&                        \
+    !defined(MBEDTLS_MD_CAN_SHA1)
+#error "!MBEDTLS_SSL_KEEP_PEER_CERTIFICATE requires SHA-512, SHA-256 or SHA-1".
 #endif
 
-#if defined(MBEDTLS_MD_C) && !( \
-    defined(MBEDTLS_MD5_C) || \
-    defined(MBEDTLS_RIPEMD160_C) || \
-    defined(MBEDTLS_SHA1_C) || \
-    defined(MBEDTLS_SHA224_C) || \
-    defined(MBEDTLS_SHA256_C) || \
-    defined(MBEDTLS_SHA384_C) || \
-    defined(MBEDTLS_SHA512_C) || \
-    (defined(MBEDTLS_PSA_CRYPTO_C) && \
-     (defined(PSA_WANT_ALG_MD5) || \
-      defined(PSA_WANT_ALG_RIPEMD160) || \
-      defined(PSA_WANT_ALG_SHA_1) || \
-      defined(PSA_WANT_ALG_SHA_224) || \
-      defined(PSA_WANT_ALG_SHA_256) || \
-      defined(PSA_WANT_ALG_SHA_384) || \
-      defined(PSA_WANT_ALG_SHA_512))))
-#error "MBEDTLS_MD_C defined, but not all prerequisites"
+#if defined(MBEDTLS_MD_C) && \
+    !defined(MBEDTLS_MD_CAN_MD5) && \
+    !defined(MBEDTLS_MD_CAN_RIPEMD160) && \
+    !defined(MBEDTLS_MD_CAN_SHA1) && \
+    !defined(MBEDTLS_MD_CAN_SHA224) && \
+    !defined(MBEDTLS_MD_CAN_SHA256) && \
+    !defined(MBEDTLS_MD_CAN_SHA384) && \
+    !defined(MBEDTLS_MD_CAN_SHA512) && \
+    !defined(MBEDTLS_MD_CAN_SHA3_224) && \
+    !defined(MBEDTLS_MD_CAN_SHA3_256) && \
+    !defined(MBEDTLS_MD_CAN_SHA3_384) && \
+    !defined(MBEDTLS_MD_CAN_SHA3_512)
+#error "MBEDTLS_MD_C defined, but no hash algorithm"
 #endif
 
 #if defined(MBEDTLS_LMS_C) &&                                          \
@@ -975,7 +873,7 @@
 #endif
 #else /* MBEDTLS_USE_PSA_CRYPTO */
 #if !defined(MBEDTLS_MD_C) || \
-    !(defined(MBEDTLS_MD_HAVE_SHA256) || defined(MBEDTLS_MD_HAVE_SHA384))
+    !(defined(MBEDTLS_MD_CAN_SHA256) || defined(MBEDTLS_MD_CAN_SHA384))
 #error "MBEDTLS_SSL_TLS_C defined, but not all prerequisites"
 #endif
 #endif /* MBEDTLS_USE_PSA_CRYPTO */
@@ -1074,20 +972,18 @@
 #if !defined(MBEDTLS_THREADING_C) || defined(MBEDTLS_THREADING_IMPL)
 #error "MBEDTLS_THREADING_PTHREAD defined, but not all prerequisites"
 #endif
-#define MBEDTLS_THREADING_IMPL
+#define MBEDTLS_THREADING_IMPL // undef at the end of this paragraph
 #endif
-
 #if defined(MBEDTLS_THREADING_ALT)
 #if !defined(MBEDTLS_THREADING_C) || defined(MBEDTLS_THREADING_IMPL)
 #error "MBEDTLS_THREADING_ALT defined, but not all prerequisites"
 #endif
-#define MBEDTLS_THREADING_IMPL
+#define MBEDTLS_THREADING_IMPL // undef at the end of this paragraph
 #endif
-
 #if defined(MBEDTLS_THREADING_C) && !defined(MBEDTLS_THREADING_IMPL)
 #error "MBEDTLS_THREADING_C defined, single threading implementation required"
 #endif
-#undef MBEDTLS_THREADING_IMPL
+#undef MBEDTLS_THREADING_IMPL // temporary macro defined above
 
 #if defined(MBEDTLS_USE_PSA_CRYPTO) && !defined(MBEDTLS_PSA_CRYPTO_C)
 #error "MBEDTLS_USE_PSA_CRYPTO defined, but not all prerequisites"
@@ -1218,13 +1114,6 @@
 #error  "MBEDTLS_PKCS7_C is defined, but not all prerequisites"
 #endif
 
-/* Undefine helper symbols */
-#undef MBEDTLS_PK_HAVE_JPAKE
-#undef MBEDTLS_MD_HAVE_SHA256
-#undef MBEDTLS_MD_HAVE_SHA384
-#undef MBEDTLS_MD_HAVE_SHA512
-#undef MBEDTLS_PK_HAVE_CURVE_SECP256R1
-
 /*
  * Avoid warning from -pedantic. This is a convenient place for this
  * workaround since this is included by every single file before the
diff --git a/include/mbedtls/config_adjust_legacy_crypto.h b/include/mbedtls/config_adjust_legacy_crypto.h
index 064699c..9b06041 100644
--- a/include/mbedtls/config_adjust_legacy_crypto.h
+++ b/include/mbedtls/config_adjust_legacy_crypto.h
@@ -22,6 +22,22 @@
 #ifndef MBEDTLS_CONFIG_ADJUST_LEGACY_CRYPTO_H
 #define MBEDTLS_CONFIG_ADJUST_LEGACY_CRYPTO_H
 
+/* Ideally, we'd set those as defaults in mbedtls_config.h, but
+ * putting an #ifdef _WIN32 in mbedtls_config.h would confuse config.py.
+ *
+ * So, adjust it here.
+ * Not related to crypto, but this is the bottom of the stack. */
+#if defined(__MINGW32__) || (defined(_MSC_VER) && _MSC_VER <= 1900)
+#if !defined(MBEDTLS_PLATFORM_SNPRINTF_ALT) && \
+    !defined(MBEDTLS_PLATFORM_SNPRINTF_MACRO)
+#define MBEDTLS_PLATFORM_SNPRINTF_ALT
+#endif
+#if !defined(MBEDTLS_PLATFORM_VSNPRINTF_ALT) && \
+    !defined(MBEDTLS_PLATFORM_VSNPRINTF_MACRO)
+#define MBEDTLS_PLATFORM_VSNPRINTF_ALT
+#endif
+#endif /* _MINGW32__ || (_MSC_VER && (_MSC_VER <= 1900)) */
+
 /* Auto-enable CIPHER_C when any of the unauthenticated ciphers is builtin
  * in PSA. */
 #if defined(MBEDTLS_PSA_CRYPTO_C) && \
diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
index e0cd79d..9583a15 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -5106,9 +5106,9 @@
 
 #if defined(MBEDTLS_SSL_EARLY_DATA)
 
-#define MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT  0
-#define MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED  1
-#define MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED  2
+#define MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT  1
+#define MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED  2
+#define MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED  3
 
 #if defined(MBEDTLS_SSL_SRV_C)
 /**
diff --git a/include/psa/crypto_extra.h b/include/psa/crypto_extra.h
index c67345b..10a23f6 100644
--- a/include/psa/crypto_extra.h
+++ b/include/psa/crypto_extra.h
@@ -198,6 +198,8 @@
  *
  * This function clears all data associated with the PSA layer,
  * including the whole key store.
+ * This function is not thread safe, it wipes every key slot regardless of
+ * state and reader count. It should only be called when no slot is in use.
  *
  * This is an Mbed TLS extension.
  */
diff --git a/library/aesce.c b/library/aesce.c
index eaaa5b5..6a9e0a1 100644
--- a/library/aesce.c
+++ b/library/aesce.c
@@ -334,7 +334,7 @@
      *   - Section 5, Nr = Nk + 6
      *   - Section 5.2, the length of round keys is Nb*(Nr+1)
      */
-    const uint32_t key_len_in_words = key_bit_length / 32;  /* Nk */
+    const size_t key_len_in_words = key_bit_length / 32;    /* Nk */
     const size_t round_key_len_in_words = 4;                /* Nb */
     const size_t rounds_needed = key_len_in_words + 6;      /* Nr */
     const size_t round_keys_len_in_words =
diff --git a/library/lms.c b/library/lms.c
index 08fe753..8d3cae0 100644
--- a/library/lms.c
+++ b/library/lms.c
@@ -65,7 +65,8 @@
 #define H_TREE_HEIGHT_MAX                  10
 #define MERKLE_TREE_NODE_AM(type)          ((size_t) 1 << (MBEDTLS_LMS_H_TREE_HEIGHT(type) + 1u))
 #define MERKLE_TREE_LEAF_NODE_AM(type)     ((size_t) 1 << MBEDTLS_LMS_H_TREE_HEIGHT(type))
-#define MERKLE_TREE_INTERNAL_NODE_AM(type) ((size_t) 1 << MBEDTLS_LMS_H_TREE_HEIGHT(type))
+#define MERKLE_TREE_INTERNAL_NODE_AM(type) ((unsigned int) \
+                                            (1u << MBEDTLS_LMS_H_TREE_HEIGHT(type)))
 
 #define D_CONST_LEN           (2)
 static const unsigned char D_LEAF_CONSTANT_BYTES[D_CONST_LEN] = { 0x82, 0x82 };
diff --git a/library/pk.c b/library/pk.c
index 1b481e1..076d3a8 100644
--- a/library/pk.c
+++ b/library/pk.c
@@ -385,7 +385,7 @@
 {
     if (mbedtls_rsa_get_padding_mode(rsa) == MBEDTLS_RSA_PKCS_V21) {
         if (want_crypt) {
-            mbedtls_md_type_t md_type = mbedtls_rsa_get_md_alg(rsa);
+            mbedtls_md_type_t md_type = (mbedtls_md_type_t) mbedtls_rsa_get_md_alg(rsa);
             return PSA_ALG_RSA_OAEP(mbedtls_md_psa_alg_from_type(md_type));
         } else {
             return PSA_ALG_RSA_PSS_ANY_SALT(PSA_ALG_ANY_HASH);
diff --git a/library/psa_crypto.c b/library/psa_crypto.c
index beafc48..4a0666b 100644
--- a/library/psa_crypto.c
+++ b/library/psa_crypto.c
@@ -1089,6 +1089,14 @@
         return status;
     }
 
+#if defined(MBEDTLS_THREADING_C)
+    /* We cannot unlock between setting the state to PENDING_DELETION
+     * and destroying the key in storage, as otherwise another thread
+     * could load the key into a new slot and the key will not be
+     * fully destroyed. */
+    PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(
+                                    &mbedtls_threading_key_slot_mutex));
+#endif
     /* Set the key slot containing the key description's state to
      * PENDING_DELETION. This stops new operations from registering
      * to read the slot. Current readers can safely continue to access
@@ -1097,7 +1105,12 @@
      * If the key is persistent, we can now delete the copy of the key
      * from memory. If the key is opaque, we require the driver to
      * deal with the deletion. */
-    slot->state = PSA_SLOT_PENDING_DELETION;
+    status = psa_key_slot_state_transition(slot, PSA_SLOT_FULL,
+                                           PSA_SLOT_PENDING_DELETION);
+
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
 
     if (PSA_KEY_LIFETIME_IS_READ_ONLY(slot->attr.lifetime)) {
         /* Refuse the destruction of a read-only key (which may or may not work
@@ -1152,11 +1165,6 @@
         if (overall_status == PSA_SUCCESS) {
             overall_status = status;
         }
-
-        /* TODO: other slots may have a copy of the same key. We should
-         * invalidate them.
-         * https://github.com/ARMmbed/mbed-crypto/issues/214
-         */
     }
 #endif /* defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) */
 
@@ -1182,6 +1190,14 @@
     if (status != PSA_SUCCESS) {
         overall_status = status;
     }
+
+#if defined(MBEDTLS_THREADING_C)
+    /* Don't overwrite existing errors if the unlock fails. */
+    status = overall_status;
+    PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+
     return overall_status;
 }
 
diff --git a/library/psa_crypto_slot_management.c b/library/psa_crypto_slot_management.c
index 47ace35..dc38662 100644
--- a/library/psa_crypto_slot_management.c
+++ b/library/psa_crypto_slot_management.c
@@ -521,44 +521,78 @@
 
 psa_status_t psa_close_key(psa_key_handle_t handle)
 {
-    psa_status_t status;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_key_slot_t *slot;
 
     if (psa_key_handle_is_null(handle)) {
         return PSA_SUCCESS;
     }
 
+#if defined(MBEDTLS_THREADING_C)
+    /* We need to set status as success, otherwise CORRUPTION_DETECTED
+     * would be returned if the lock fails. */
+    status = PSA_SUCCESS;
+    PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
     status = psa_get_and_lock_key_slot_in_memory(handle, &slot);
     if (status != PSA_SUCCESS) {
         if (status == PSA_ERROR_DOES_NOT_EXIST) {
             status = PSA_ERROR_INVALID_HANDLE;
         }
-
+#if defined(MBEDTLS_THREADING_C)
+        PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                                  &mbedtls_threading_key_slot_mutex));
+#endif
         return status;
     }
+
     if (slot->registered_readers == 1) {
-        return psa_wipe_key_slot(slot);
+        status = psa_wipe_key_slot(slot);
     } else {
-        return psa_unregister_read(slot);
+        status = psa_unregister_read(slot);
     }
+#if defined(MBEDTLS_THREADING_C)
+    PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+
+    return status;
 }
 
 psa_status_t psa_purge_key(mbedtls_svc_key_id_t key)
 {
-    psa_status_t status;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_key_slot_t *slot;
 
+#if defined(MBEDTLS_THREADING_C)
+    /* We need to set status as success, otherwise CORRUPTION_DETECTED
+     * would be returned if the lock fails. */
+    status = PSA_SUCCESS;
+    PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
     status = psa_get_and_lock_key_slot_in_memory(key, &slot);
     if (status != PSA_SUCCESS) {
+#if defined(MBEDTLS_THREADING_C)
+        PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                                  &mbedtls_threading_key_slot_mutex));
+#endif
         return status;
     }
 
     if ((!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) &&
         (slot->registered_readers == 1)) {
-        return psa_wipe_key_slot(slot);
+        status = psa_wipe_key_slot(slot);
     } else {
-        return psa_unregister_read(slot);
+        status = psa_unregister_read(slot);
     }
+#if defined(MBEDTLS_THREADING_C)
+    PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+
+    return status;
 }
 
 void mbedtls_psa_get_stats(mbedtls_psa_stats_t *stats)
diff --git a/library/psa_crypto_slot_management.h b/library/psa_crypto_slot_management.h
index 002429b..18a9144 100644
--- a/library/psa_crypto_slot_management.h
+++ b/library/psa_crypto_slot_management.h
@@ -92,6 +92,8 @@
 psa_status_t psa_initialize_key_slots(void);
 
 /** Delete all data from key slots in memory.
+ * This function is not thread safe, it wipes every key slot regardless of
+ * state and reader count. It should only be called when no slot is in use.
  *
  * This does not affect persistent storage. */
 void psa_wipe_all_key_slots(void);
diff --git a/library/sha3.c b/library/sha3.c
index f420a12..27d495f 100644
--- a/library/sha3.c
+++ b/library/sha3.c
@@ -26,33 +26,35 @@
 
 #define XOR_BYTE 0x6
 
-typedef struct mbedtls_sha3_family_functions {
-    mbedtls_sha3_id id;
-
-    uint16_t r;
-    uint16_t olen;
-}
-mbedtls_sha3_family_functions;
-
-/*
- * List of supported SHA-3 families
+/* Precomputed masks for the iota transform.
+ *
+ * Each round uses a 64-bit mask value. In each mask values, only
+ * bits whose position is of the form 2^k-1 can be set, thus only
+ * 7 of 64 bits of the mask need to be known for each mask value.
+ *
+ * We use a compressed encoding of the mask where bits 63, 31 and 15
+ * are moved to bits 4-6. This allows us to make each mask value
+ * 1 byte rather than 8 bytes, saving 7*24 = 168 bytes of data (with
+ * perhaps a little variation due to alignment). Decompressing this
+ * requires a little code, but much less than the savings on the table.
+ *
+ * The impact on performance depends on the platform and compiler.
+ * There's a bit more computation, but less memory bandwidth. A quick
+ * benchmark on x86_64 shows a 7% speed improvement with GCC and a
+ * 5% speed penalty with Clang, compared to the naive uint64_t[24] table.
+ * YMMV.
  */
-static const mbedtls_sha3_family_functions sha3_families[] = {
-    { MBEDTLS_SHA3_224,      1152, 224 },
-    { MBEDTLS_SHA3_256,      1088, 256 },
-    { MBEDTLS_SHA3_384,       832, 384 },
-    { MBEDTLS_SHA3_512,       576, 512 },
-    { MBEDTLS_SHA3_NONE, 0, 0 }
+/* Helper macro to set the values of the higher bits in unused low positions */
+#define H(b63, b31, b15) (b63 << 6 | b31 << 5 | b15 << 4)
+static const uint8_t iota_r_packed[24] = {
+    H(0, 0, 0) | 0x01, H(0, 0, 1) | 0x82, H(1, 0, 1) | 0x8a, H(1, 1, 1) | 0x00,
+    H(0, 0, 1) | 0x8b, H(0, 1, 0) | 0x01, H(1, 1, 1) | 0x81, H(1, 0, 1) | 0x09,
+    H(0, 0, 0) | 0x8a, H(0, 0, 0) | 0x88, H(0, 1, 1) | 0x09, H(0, 1, 0) | 0x0a,
+    H(0, 1, 1) | 0x8b, H(1, 0, 0) | 0x8b, H(1, 0, 1) | 0x89, H(1, 0, 1) | 0x03,
+    H(1, 0, 1) | 0x02, H(1, 0, 0) | 0x80, H(0, 0, 1) | 0x0a, H(1, 1, 0) | 0x0a,
+    H(1, 1, 1) | 0x81, H(1, 0, 1) | 0x80, H(0, 1, 0) | 0x01, H(1, 1, 1) | 0x08,
 };
-
-static const uint64_t rc[24] = {
-    0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
-    0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
-    0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
-    0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
-    0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
-    0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
-};
+#undef H
 
 static const uint8_t rho[24] = {
     1, 62, 28, 27, 36, 44,  6, 55, 20,
@@ -151,7 +153,11 @@
         s[24] ^= (~lane[0]) & lane[1];
 
         /* Iota */
-        s[0] ^= rc[round];
+        /* Decompress the round masks (see definition of rc) */
+        s[0] ^= ((iota_r_packed[round] & 0x40ull) << 57 |
+                 (iota_r_packed[round] & 0x20ull) << 26 |
+                 (iota_r_packed[round] & 0x10ull) << 11 |
+                 (iota_r_packed[round] & 0x8f));
     }
 }
 
@@ -180,21 +186,27 @@
  */
 int mbedtls_sha3_starts(mbedtls_sha3_context *ctx, mbedtls_sha3_id id)
 {
-    const mbedtls_sha3_family_functions *p = NULL;
-
-    for (p = sha3_families; p->id != MBEDTLS_SHA3_NONE; p++) {
-        if (p->id == id) {
+    switch (id) {
+        case MBEDTLS_SHA3_224:
+            ctx->olen = 224 / 8;
+            ctx->max_block_size = 1152 / 8;
             break;
-        }
+        case MBEDTLS_SHA3_256:
+            ctx->olen = 256 / 8;
+            ctx->max_block_size = 1088 / 8;
+            break;
+        case MBEDTLS_SHA3_384:
+            ctx->olen = 384 / 8;
+            ctx->max_block_size = 832 / 8;
+            break;
+        case MBEDTLS_SHA3_512:
+            ctx->olen = 512 / 8;
+            ctx->max_block_size = 576 / 8;
+            break;
+        default:
+            return MBEDTLS_ERR_SHA3_BAD_INPUT_DATA;
     }
 
-    if (p->id == MBEDTLS_SHA3_NONE) {
-        return MBEDTLS_ERR_SHA3_BAD_INPUT_DATA;
-    }
-
-    ctx->olen = p->olen / 8;
-    ctx->max_block_size = p->r / 8;
-
     memset(ctx->state, 0, sizeof(ctx->state));
     ctx->index = 0;
 
diff --git a/library/ssl_misc.h b/library/ssl_misc.h
index 16cd62e..942d4ad 100644
--- a/library/ssl_misc.h
+++ b/library/ssl_misc.h
@@ -731,14 +731,23 @@
     uint8_t key_exchange_mode; /*!< Selected key exchange mode */
 
     /** Number of HelloRetryRequest messages received/sent from/to the server. */
-    int hello_retry_request_count;
+    uint8_t hello_retry_request_count;
+
+#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE)
+    /**
+     *  Number of dummy change_cipher_spec (CCS) record sent. Used to send only
+     *  one CCS per handshake without having to complicate the handshake state
+     *  transitions.
+     */
+    uint8_t ccs_count;
+#endif
 
 #if defined(MBEDTLS_SSL_SRV_C)
-    /** selected_group of key_share extension in HelloRetryRequest message. */
-    uint16_t hrr_selected_group;
 #if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED)
     uint8_t tls13_kex_modes; /*!< Key exchange modes supported by the client */
 #endif
+    /** selected_group of key_share extension in HelloRetryRequest message. */
+    uint16_t hrr_selected_group;
 #if defined(MBEDTLS_SSL_SESSION_TICKETS)
     uint16_t new_session_tickets_count;         /*!< number of session tickets */
 #endif
@@ -2136,6 +2145,38 @@
                                            unsigned char *buf,
                                            const unsigned char *end,
                                            size_t *out_len);
+
+#if defined(MBEDTLS_SSL_CLI_C)
+/*
+ * The client has not sent the first ClientHello yet, it is unknown if the
+ * client will send an early data indication extension or not.
+ */
+#define MBEDTLS_SSL_EARLY_DATA_STATUS_UNKNOWN 0
+
+/*
+ * The client has sent an early data indication extension in its first
+ * ClientHello, it has not received the response (ServerHello or
+ * HelloRetryRequest) from the server yet. The transform to protect early data
+ * is not set and early data cannot be sent yet.
+ */
+#define MBEDTLS_SSL_EARLY_DATA_STATUS_SENT 4
+
+/*
+ * The client has sent an early data indication extension in its first
+ * ClientHello, it has not received the response (ServerHello or
+ * HelloRetryRequest) from the server yet. The transform to protect early data
+ * has been set and early data can be written now.
+ */
+#define MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_WRITE 5
+
+/*
+ * The client has sent an early data indication extension in its first
+ * ClientHello, the server has accepted them and the client has received the
+ * server Finished message. It cannot send early data to the server anymore.
+ */
+#define MBEDTLS_SSL_EARLY_DATA_STATUS_SERVER_FINISHED_RECEIVED 6
+#endif /* MBEDTLS_SSL_CLI_C */
+
 #endif /* MBEDTLS_SSL_EARLY_DATA */
 
 #endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 0e9f1fd..41ecd3c 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -1100,7 +1100,7 @@
 
 #if defined(MBEDTLS_SSL_EARLY_DATA)
 #if defined(MBEDTLS_SSL_CLI_C)
-    ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT;
+    ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_UNKNOWN;
 #endif
 #if defined(MBEDTLS_SSL_SRV_C)
     ssl->discard_early_data_record = MBEDTLS_SSL_EARLY_DATA_NO_DISCARD;
diff --git a/library/ssl_tls13_client.c b/library/ssl_tls13_client.c
index f4987b3..5d7a495 100644
--- a/library/ssl_tls13_client.c
+++ b/library/ssl_tls13_client.c
@@ -1180,26 +1180,21 @@
 #endif
 
 #if defined(MBEDTLS_SSL_EARLY_DATA)
-    if (mbedtls_ssl_conf_tls13_is_some_psk_enabled(ssl) &&
-        ssl_tls13_early_data_has_valid_ticket(ssl) &&
-        ssl->conf->early_data_enabled == MBEDTLS_SSL_EARLY_DATA_ENABLED &&
-        ssl->handshake->hello_retry_request_count == 0) {
+    if (ssl->handshake->hello_retry_request_count == 0) {
+        if (mbedtls_ssl_conf_tls13_is_some_psk_enabled(ssl) &&
+            ssl_tls13_early_data_has_valid_ticket(ssl) &&
+            ssl->conf->early_data_enabled == MBEDTLS_SSL_EARLY_DATA_ENABLED) {
+            ret = mbedtls_ssl_tls13_write_early_data_ext(
+                ssl, 0, p, end, &ext_len);
+            if (ret != 0) {
+                return ret;
+            }
+            p += ext_len;
 
-        ret = mbedtls_ssl_tls13_write_early_data_ext(
-            ssl, 0, p, end, &ext_len);
-        if (ret != 0) {
-            return ret;
+            ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_SENT;
+        } else {
+            ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT;
         }
-        p += ext_len;
-
-        /* Initializes the status to `rejected`. It will be updated to
-         * `accepted` if the EncryptedExtension message contain an early data
-         * indication extension.
-         */
-        ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED;
-    } else {
-        MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip write early_data extension"));
-        ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT;
     }
 #endif /* MBEDTLS_SSL_EARLY_DATA */
 
@@ -1236,7 +1231,7 @@
     size_t psk_len;
     const mbedtls_ssl_ciphersuite_t *ciphersuite_info;
 
-    if (ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED) {
+    if (ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_SENT) {
         MBEDTLS_SSL_DEBUG_MSG(
             1, ("Set hs psk for early data when writing the first psk"));
 
@@ -1299,6 +1294,7 @@
             1, ("Switch to early data keys for outbound traffic"));
         mbedtls_ssl_set_outbound_transform(
             ssl, ssl->handshake->transform_earlydata);
+        ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_WRITE;
 #endif
     }
 #endif /* MBEDTLS_SSL_EARLY_DATA */
@@ -1971,6 +1967,13 @@
     }
 
     ssl->session_negotiate->ciphersuite = ssl->handshake->ciphersuite_info->id;
+
+#if defined(MBEDTLS_SSL_EARLY_DATA)
+    if (ssl->early_data_status != MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT) {
+        ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED;
+    }
+#endif
+
     return 0;
 }
 
@@ -2230,6 +2233,8 @@
         }
 
         ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED;
+    } else if (ssl->early_data_status != MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT) {
+        ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED;
     }
 #endif
 
@@ -2567,9 +2572,8 @@
 
 #if defined(MBEDTLS_SSL_EARLY_DATA)
     if (ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED) {
+        ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_SERVER_FINISHED_RECEIVED;
         mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_END_OF_EARLY_DATA);
-    } else if (ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED) {
-        mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_CERTIFICATE);
     } else
 #endif /* MBEDTLS_SSL_EARLY_DATA */
     {
@@ -3059,18 +3063,25 @@
              */
 #if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE)
         case MBEDTLS_SSL_CLIENT_CCS_BEFORE_2ND_CLIENT_HELLO:
-            ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl);
-            if (ret == 0) {
-                mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_HELLO);
+            ret = 0;
+            if (ssl->handshake->ccs_count == 0) {
+                ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl);
+                if (ret != 0) {
+                    break;
+                }
             }
+            mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_HELLO);
             break;
 
         case MBEDTLS_SSL_CLIENT_CCS_AFTER_SERVER_FINISHED:
-            ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl);
-            if (ret == 0) {
-                mbedtls_ssl_handshake_set_state(
-                    ssl, MBEDTLS_SSL_CLIENT_CERTIFICATE);
+            ret = 0;
+            if (ssl->handshake->ccs_count == 0) {
+                ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl);
+                if (ret != 0) {
+                    break;
+                }
             }
+            mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_CERTIFICATE);
             break;
 
 #if defined(MBEDTLS_SSL_EARLY_DATA)
@@ -3083,6 +3094,7 @@
                     1, ("Switch to early data keys for outbound traffic"));
                 mbedtls_ssl_set_outbound_transform(
                     ssl, ssl->handshake->transform_earlydata);
+                ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_WRITE;
             }
             break;
 #endif /* MBEDTLS_SSL_EARLY_DATA */
diff --git a/library/ssl_tls13_generic.c b/library/ssl_tls13_generic.c
index 959f8e6..064f616 100644
--- a/library/ssl_tls13_generic.c
+++ b/library/ssl_tls13_generic.c
@@ -1390,6 +1390,8 @@
     /* Dispatch message */
     MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_write_record(ssl, 0));
 
+    ssl->handshake->ccs_count++;
+
 cleanup:
 
     MBEDTLS_SSL_DEBUG_MSG(2, ("<= write change cipher spec"));
diff --git a/library/ssl_tls13_server.c b/library/ssl_tls13_server.c
index 62b117c..05693f3 100644
--- a/library/ssl_tls13_server.c
+++ b/library/ssl_tls13_server.c
@@ -3482,10 +3482,14 @@
             break;
 
         case MBEDTLS_SSL_SERVER_CCS_AFTER_SERVER_HELLO:
-            ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl);
-            if (ret == 0) {
-                mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_ENCRYPTED_EXTENSIONS);
+            ret = 0;
+            if (ssl->handshake->ccs_count == 0) {
+                ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl);
+                if (ret != 0) {
+                    break;
+                }
             }
+            mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_ENCRYPTED_EXTENSIONS);
             break;
 #endif /* MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE */
 
diff --git a/tests/include/test/ssl_helpers.h b/tests/include/test/ssl_helpers.h
index 1f41966..3506609 100644
--- a/tests/include/test/ssl_helpers.h
+++ b/tests/include/test/ssl_helpers.h
@@ -85,6 +85,7 @@
 
 typedef struct mbedtls_test_handshake_test_options {
     const char *cipher;
+    uint16_t *group_list;
     mbedtls_ssl_protocol_version client_min_version;
     mbedtls_ssl_protocol_version client_max_version;
     mbedtls_ssl_protocol_version server_min_version;
@@ -112,6 +113,7 @@
     void (*srv_log_fun)(void *, int, const char *, int, const char *);
     void (*cli_log_fun)(void *, int, const char *, int, const char *);
     int resize_buffers;
+    int early_data;
 #if defined(MBEDTLS_SSL_CACHE_C)
     mbedtls_ssl_cache_context *cache;
 #endif
@@ -440,8 +442,7 @@
     mbedtls_test_handshake_test_options *options,
     mbedtls_test_message_socket_context *dtls_context,
     mbedtls_test_ssl_message_queue *input_queue,
-    mbedtls_test_ssl_message_queue *output_queue,
-    uint16_t *group_list);
+    mbedtls_test_ssl_message_queue *output_queue);
 
 /*
  * Deinitializes endpoint represented by \p ep.
@@ -599,6 +600,17 @@
                               unsigned char *buf, size_t len);
 #endif /* MBEDTLS_SSL_SESSION_TICKETS */
 
+#if defined(MBEDTLS_SSL_CLI_C) && defined(MBEDTLS_SSL_SRV_C) && \
+    defined(MBEDTLS_SSL_PROTO_TLS1_3) && defined(MBEDTLS_SSL_SESSION_TICKETS) && \
+    defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED)
+int mbedtls_test_get_tls13_ticket(
+    mbedtls_test_handshake_test_options *client_options,
+    mbedtls_test_handshake_test_options *server_options,
+    mbedtls_ssl_session *session);
+#endif /* MBEDTLS_SSL_CLI_C && MBEDTLS_SSL_SRV_C &&
+          MBEDTLS_SSL_PROTO_TLS1_3 && MBEDTLS_SSL_SESSION_TICKETS &&
+          MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */
+
 #define ECJPAKE_TEST_PWD        "bla"
 
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
diff --git a/tests/src/helpers.c b/tests/src/helpers.c
index da0b54a..b9233be 100644
--- a/tests/src/helpers.c
+++ b/tests/src/helpers.c
@@ -341,11 +341,10 @@
     return 0;
 }
 
-void mbedtls_test_fail(const char *test, int line_no, const char *filename)
+static void mbedtls_test_fail_internal(const char *test, int line_no, const char *filename)
 {
-#ifdef MBEDTLS_THREADING_C
-    mbedtls_mutex_lock(&mbedtls_test_info_mutex);
-#endif /* MBEDTLS_THREADING_C */
+    /* Internal function only - mbedtls_test_info_mutex should be held prior
+     * to calling this function. */
 
     /* Don't use accessor, we already hold mutex. */
     if (mbedtls_test_info.result != MBEDTLS_TEST_RESULT_FAILED) {
@@ -353,6 +352,15 @@
          * overwrite any previous information about the failure. */
         mbedtls_test_set_result(MBEDTLS_TEST_RESULT_FAILED, test, line_no, filename);
     }
+}
+
+void mbedtls_test_fail(const char *test, int line_no, const char *filename)
+{
+#ifdef MBEDTLS_THREADING_C
+    mbedtls_mutex_lock(&mbedtls_test_info_mutex);
+#endif /* MBEDTLS_THREADING_C */
+
+    mbedtls_test_fail_internal(test, line_no, filename);
 
 #ifdef MBEDTLS_THREADING_C
     mbedtls_mutex_unlock(&mbedtls_test_info_mutex);
@@ -412,7 +420,7 @@
          * overwrite any previous information about the failure. */
 
         char buf[MBEDTLS_TEST_LINE_LENGTH];
-        mbedtls_test_fail(test, line_no, filename);
+        mbedtls_test_fail_internal(test, line_no, filename);
         (void) mbedtls_snprintf(buf, sizeof(buf),
                                 "lhs = 0x%016llx = %lld",
                                 value1, (long long) value1);
@@ -450,7 +458,7 @@
          * overwrite any previous information about the failure. */
 
         char buf[MBEDTLS_TEST_LINE_LENGTH];
-        mbedtls_test_fail(test, line_no, filename);
+        mbedtls_test_fail_internal(test, line_no, filename);
         (void) mbedtls_snprintf(buf, sizeof(buf),
                                 "lhs = 0x%016llx = %llu",
                                 value1, value1);
@@ -488,7 +496,7 @@
          * overwrite any previous information about the failure. */
 
         char buf[MBEDTLS_TEST_LINE_LENGTH];
-        mbedtls_test_fail(test, line_no, filename);
+        mbedtls_test_fail_internal(test, line_no, filename);
         (void) mbedtls_snprintf(buf, sizeof(buf),
                                 "lhs = 0x%016llx = %lld",
                                 (unsigned long long) value1, value1);
diff --git a/tests/src/test_helpers/ssl_helpers.c b/tests/src/test_helpers/ssl_helpers.c
index 980c192..ad4c070 100644
--- a/tests/src/test_helpers/ssl_helpers.c
+++ b/tests/src/test_helpers/ssl_helpers.c
@@ -49,36 +49,26 @@
     srand(rng_seed);
     rng_seed += 0xD0;
 #endif
+
+    memset(opts, 0, sizeof(*opts));
+
     opts->cipher = "";
     opts->client_min_version = MBEDTLS_SSL_VERSION_UNKNOWN;
     opts->client_max_version = MBEDTLS_SSL_VERSION_UNKNOWN;
     opts->server_min_version = MBEDTLS_SSL_VERSION_UNKNOWN;
     opts->server_max_version = MBEDTLS_SSL_VERSION_UNKNOWN;
     opts->expected_negotiated_version = MBEDTLS_SSL_VERSION_TLS1_3;
-    opts->expected_handshake_result = 0;
-    opts->expected_ciphersuite = 0;
     opts->pk_alg = MBEDTLS_PK_RSA;
-    opts->opaque_alg = 0;
-    opts->opaque_alg2 = 0;
-    opts->opaque_usage = 0;
-    opts->psk_str = NULL;
-    opts->dtls = 0;
     opts->srv_auth_mode = MBEDTLS_SSL_VERIFY_NONE;
-    opts->serialize = 0;
     opts->mfl = MBEDTLS_SSL_MAX_FRAG_LEN_NONE;
     opts->cli_msg_len = 100;
     opts->srv_msg_len = 100;
     opts->expected_cli_fragments = 1;
     opts->expected_srv_fragments = 1;
-    opts->renegotiate = 0;
     opts->legacy_renegotiation = MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION;
-    opts->srv_log_obj = NULL;
-    opts->cli_log_obj = NULL;
-    opts->srv_log_fun = NULL;
-    opts->cli_log_fun = NULL;
     opts->resize_buffers = 1;
+    opts->early_data = MBEDTLS_SSL_EARLY_DATA_DISABLED;
 #if defined(MBEDTLS_SSL_CACHE_C)
-    opts->cache = NULL;
     TEST_CALLOC(opts->cache, 1);
     mbedtls_ssl_cache_init(opts->cache);
 #if defined(MBEDTLS_HAVE_TIME)
@@ -733,8 +723,7 @@
     mbedtls_test_handshake_test_options *options,
     mbedtls_test_message_socket_context *dtls_context,
     mbedtls_test_ssl_message_queue *input_queue,
-    mbedtls_test_ssl_message_queue *output_queue,
-    uint16_t *group_list)
+    mbedtls_test_ssl_message_queue *output_queue)
 {
     int ret = -1;
     uintptr_t user_data_n;
@@ -818,12 +807,16 @@
         }
     }
 
-    if (group_list != NULL) {
-        mbedtls_ssl_conf_groups(&(ep->conf), group_list);
+    if (options->group_list != NULL) {
+        mbedtls_ssl_conf_groups(&(ep->conf), options->group_list);
     }
 
     mbedtls_ssl_conf_authmode(&(ep->conf), MBEDTLS_SSL_VERIFY_REQUIRED);
 
+#if defined(MBEDTLS_SSL_EARLY_DATA)
+    mbedtls_ssl_conf_early_data(&(ep->conf), options->early_data);
+#endif
+
 #if defined(MBEDTLS_SSL_CACHE_C) && defined(MBEDTLS_SSL_SRV_C)
     if (endpoint_type == MBEDTLS_SSL_IS_SERVER && options->cache != NULL) {
         mbedtls_ssl_conf_session_cache(&(ep->conf), options->cache,
@@ -2006,7 +1999,7 @@
                                                    MBEDTLS_SSL_IS_CLIENT,
                                                    options, &client_context,
                                                    &client_queue,
-                                                   &server_queue, NULL) == 0);
+                                                   &server_queue) == 0);
 #if defined(MBEDTLS_TIMING_C)
         mbedtls_ssl_set_timer_cb(&client.ssl, &timer_client,
                                  mbedtls_timing_set_delay,
@@ -2016,7 +2009,7 @@
         TEST_ASSERT(mbedtls_test_ssl_endpoint_init(&client,
                                                    MBEDTLS_SSL_IS_CLIENT,
                                                    options, NULL, NULL,
-                                                   NULL, NULL) == 0);
+                                                   NULL) == 0);
     }
 
     if (strlen(options->cipher) > 0) {
@@ -2029,7 +2022,7 @@
                                                    MBEDTLS_SSL_IS_SERVER,
                                                    options, &server_context,
                                                    &server_queue,
-                                                   &client_queue, NULL) == 0);
+                                                   &client_queue) == 0);
 #if defined(MBEDTLS_TIMING_C)
         mbedtls_ssl_set_timer_cb(&server.ssl, &timer_server,
                                  mbedtls_timing_set_delay,
@@ -2038,7 +2031,7 @@
     } else {
         TEST_ASSERT(mbedtls_test_ssl_endpoint_init(&server,
                                                    MBEDTLS_SSL_IS_SERVER,
-                                                   options, NULL, NULL, NULL,
+                                                   options, NULL, NULL,
                                                    NULL) == 0);
     }
 
@@ -2462,4 +2455,60 @@
     return mbedtls_ssl_session_load(session, buf, len);
 }
 #endif /* MBEDTLS_SSL_SESSION_TICKETS */
+
+#if defined(MBEDTLS_SSL_CLI_C) && defined(MBEDTLS_SSL_SRV_C) && \
+    defined(MBEDTLS_SSL_PROTO_TLS1_3) && defined(MBEDTLS_SSL_SESSION_TICKETS) && \
+    defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED)
+int mbedtls_test_get_tls13_ticket(
+    mbedtls_test_handshake_test_options *client_options,
+    mbedtls_test_handshake_test_options *server_options,
+    mbedtls_ssl_session *session)
+{
+    int ret = -1;
+    unsigned char buf[64];
+    mbedtls_test_ssl_endpoint client_ep, server_ep;
+
+    mbedtls_platform_zeroize(&client_ep, sizeof(client_ep));
+    mbedtls_platform_zeroize(&server_ep, sizeof(server_ep));
+
+    ret = mbedtls_test_ssl_endpoint_init(&client_ep, MBEDTLS_SSL_IS_CLIENT,
+                                         client_options, NULL, NULL, NULL);
+    TEST_EQUAL(ret, 0);
+
+    ret = mbedtls_test_ssl_endpoint_init(&server_ep, MBEDTLS_SSL_IS_SERVER,
+                                         server_options, NULL, NULL, NULL);
+    TEST_EQUAL(ret, 0);
+
+    mbedtls_ssl_conf_session_tickets_cb(&server_ep.conf,
+                                        mbedtls_test_ticket_write,
+                                        mbedtls_test_ticket_parse,
+                                        NULL);
+
+    ret = mbedtls_test_mock_socket_connect(&(client_ep.socket),
+                                           &(server_ep.socket), 1024);
+    TEST_EQUAL(ret, 0);
+
+    TEST_EQUAL(mbedtls_test_move_handshake_to_state(
+                   &(server_ep.ssl), &(client_ep.ssl),
+                   MBEDTLS_SSL_HANDSHAKE_OVER), 0);
+
+    TEST_EQUAL(server_ep.ssl.handshake->new_session_tickets_count, 0);
+
+    do {
+        ret = mbedtls_ssl_read(&(client_ep.ssl), buf, sizeof(buf));
+    } while (ret != MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET);
+
+    ret = mbedtls_ssl_get_session(&(client_ep.ssl), session);
+    TEST_EQUAL(ret, 0);
+
+exit:
+    mbedtls_test_ssl_endpoint_free(&client_ep, NULL);
+    mbedtls_test_ssl_endpoint_free(&server_ep, NULL);
+
+    return ret;
+}
+#endif /* MBEDTLS_SSL_CLI_C && MBEDTLS_SSL_SRV_C &&
+          MBEDTLS_SSL_PROTO_TLS1_3 && MBEDTLS_SSL_SESSION_TICKETS &&
+          MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */
+
 #endif /* MBEDTLS_SSL_TLS_C */
diff --git a/tests/suites/test_suite_ssl.data b/tests/suites/test_suite_ssl.data
index 86945cc..69ccf26 100644
--- a/tests/suites/test_suite_ssl.data
+++ b/tests/suites/test_suite_ssl.data
@@ -3274,11 +3274,23 @@
 TLS 1.3 resume session with ticket
 tls13_resume_session_with_ticket
 
-TLS 1.3 early data, reference
-tls13_early_data:TEST_EARLY_DATA_REFERENCE
+TLS 1.3 early data, early data accepted
+tls13_early_data:TEST_EARLY_DATA_ACCEPTED
 
-TLS 1.3 early data, deprotect and discard
-tls13_early_data:TEST_EARLY_DATA_DEPROTECT_AND_DISCARD
+TLS 1.3 early data, server rejects early data
+tls13_early_data:TEST_EARLY_DATA_SERVER_REJECTS
 
 TLS 1.3 early data, discard after HRR
-tls13_early_data:TEST_EARLY_DATA_DISCARD_AFTER_HRR
+tls13_early_data:TEST_EARLY_DATA_HRR
+
+TLS 1.3 cli, early data status, early data accepted
+tls13_cli_early_data_status:TEST_EARLY_DATA_ACCEPTED
+
+TLS 1.3 cli, early data status, no early data indication
+tls13_cli_early_data_status:TEST_EARLY_DATA_NO_INDICATION_SENT
+
+TLS 1.3 cli, early data status, server rejects early data
+tls13_cli_early_data_status:TEST_EARLY_DATA_SERVER_REJECTS
+
+TLS 1.3 cli, early data status, hello retry request
+tls13_cli_early_data_status:TEST_EARLY_DATA_HRR
diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function
index 8687a4d..2751e58 100644
--- a/tests/suites/test_suite_ssl.function
+++ b/tests/suites/test_suite_ssl.function
@@ -13,9 +13,10 @@
 #define SSL_MESSAGE_QUEUE_INIT      { NULL, 0, 0, 0 }
 
 /* Mnemonics for the early data test scenarios */
-#define TEST_EARLY_DATA_REFERENCE 0
-#define TEST_EARLY_DATA_DEPROTECT_AND_DISCARD 1
-#define TEST_EARLY_DATA_DISCARD_AFTER_HRR 2
+#define TEST_EARLY_DATA_ACCEPTED 0
+#define TEST_EARLY_DATA_NO_INDICATION_SENT 1
+#define TEST_EARLY_DATA_SERVER_REJECTS 2
+#define TEST_EARLY_DATA_HRR 3
 
 #if (!defined(MBEDTLS_SSL_PROTO_TLS1_2)) && \
     defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_CLI_C) && \
@@ -2457,7 +2458,7 @@
     MD_OR_USE_PSA_INIT();
 
     ret = mbedtls_test_ssl_endpoint_init(NULL, endpoint_type, &options,
-                                         NULL, NULL, NULL, NULL);
+                                         NULL, NULL, NULL);
     TEST_ASSERT(MBEDTLS_ERR_SSL_BAD_INPUT_DATA == ret);
 
     ret = mbedtls_test_ssl_endpoint_certificate_init(NULL, options.pk_alg,
@@ -2465,7 +2466,7 @@
     TEST_ASSERT(MBEDTLS_ERR_SSL_BAD_INPUT_DATA == ret);
 
     ret = mbedtls_test_ssl_endpoint_init(&ep, endpoint_type, &options,
-                                         NULL, NULL, NULL, NULL);
+                                         NULL, NULL, NULL);
     TEST_ASSERT(ret == 0);
 
 exit:
@@ -2509,14 +2510,14 @@
     mbedtls_platform_zeroize(&second_ep, sizeof(second_ep));
 
     ret = mbedtls_test_ssl_endpoint_init(&base_ep, endpoint_type, &options,
-                                         NULL, NULL, NULL, NULL);
+                                         NULL, NULL, NULL);
     TEST_ASSERT(ret == 0);
 
     ret = mbedtls_test_ssl_endpoint_init(
         &second_ep,
         (endpoint_type == MBEDTLS_SSL_IS_SERVER) ?
         MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
-        &options, NULL, NULL, NULL, NULL);
+        &options, NULL, NULL, NULL);
 
     TEST_ASSERT(ret == 0);
 
@@ -3069,11 +3070,10 @@
 
     TEST_ASSERT(mbedtls_test_ssl_endpoint_init(&client, MBEDTLS_SSL_IS_CLIENT,
                                                &options, NULL, NULL,
-                                               NULL, NULL) == 0);
+                                               NULL) == 0);
 
     TEST_ASSERT(mbedtls_test_ssl_endpoint_init(&server, MBEDTLS_SSL_IS_SERVER,
-                                               &options, NULL, NULL, NULL,
-                                               NULL) == 0);
+                                               &options, NULL, NULL, NULL) == 0);
 
     mbedtls_debug_set_threshold(1);
     mbedtls_ssl_conf_dbg(&server.conf, options.srv_log_fun,
@@ -3248,8 +3248,9 @@
     mbedtls_test_ssl_endpoint client, server;
     mbedtls_psa_stats_t stats;
     size_t free_slots_before = -1;
-    mbedtls_test_handshake_test_options options;
-    mbedtls_test_init_handshake_options(&options);
+    mbedtls_test_handshake_test_options client_options, server_options;
+    mbedtls_test_init_handshake_options(&client_options);
+    mbedtls_test_init_handshake_options(&server_options);
 
     uint16_t iana_tls_group_list[] = { MBEDTLS_SSL_IANA_TLS_GROUP_SECP256R1,
                                        MBEDTLS_SSL_IANA_TLS_GROUP_NONE };
@@ -3257,21 +3258,22 @@
     mbedtls_platform_zeroize(&client, sizeof(client));
     mbedtls_platform_zeroize(&server, sizeof(server));
 
-    options.pk_alg = MBEDTLS_PK_ECDSA;
-    options.server_min_version = MBEDTLS_SSL_VERSION_TLS1_2;
-    options.server_max_version = MBEDTLS_SSL_VERSION_TLS1_2;
-
     /* Client side, force SECP256R1 to make one key bitflip fail
      * the raw key agreement. Flipping the first byte makes the
      * required 0x04 identifier invalid. */
+    client_options.pk_alg = MBEDTLS_PK_ECDSA;
+    client_options.group_list = iana_tls_group_list;
     TEST_EQUAL(mbedtls_test_ssl_endpoint_init(&client, MBEDTLS_SSL_IS_CLIENT,
-                                              &options, NULL, NULL,
-                                              NULL, iana_tls_group_list), 0);
+                                              &client_options, NULL, NULL,
+                                              NULL), 0);
 
     /* Server side */
+    server_options.pk_alg = MBEDTLS_PK_ECDSA;
+    server_options.server_min_version = MBEDTLS_SSL_VERSION_TLS1_2;
+    server_options.server_max_version = MBEDTLS_SSL_VERSION_TLS1_2;
     TEST_EQUAL(mbedtls_test_ssl_endpoint_init(&server, MBEDTLS_SSL_IS_SERVER,
-                                              &options, NULL, NULL,
-                                              NULL, NULL), 0);
+                                              &server_options, NULL, NULL,
+                                              NULL), 0);
 
     TEST_EQUAL(mbedtls_test_mock_socket_connect(&(client.socket),
                                                 &(server.socket),
@@ -3307,7 +3309,8 @@
 exit:
     mbedtls_test_ssl_endpoint_free(&client, NULL);
     mbedtls_test_ssl_endpoint_free(&server, NULL);
-    mbedtls_test_free_handshake_options(&options);
+    mbedtls_test_free_handshake_options(&client_options);
+    mbedtls_test_free_handshake_options(&server_options);
 
     MD_OR_USE_PSA_DONE();
 }
@@ -3336,15 +3339,13 @@
 
     client_options.pk_alg = MBEDTLS_PK_ECDSA;
     ret = mbedtls_test_ssl_endpoint_init(&client_ep, MBEDTLS_SSL_IS_CLIENT,
-                                         &client_options, NULL, NULL, NULL,
-                                         NULL);
+                                         &client_options, NULL, NULL, NULL);
     TEST_EQUAL(ret, 0);
 
     mbedtls_test_init_handshake_options(&server_options);
     server_options.pk_alg = MBEDTLS_PK_ECDSA;
     ret = mbedtls_test_ssl_endpoint_init(&server_ep, MBEDTLS_SSL_IS_SERVER,
-                                         &server_options, NULL, NULL, NULL,
-                                         NULL);
+                                         &server_options, NULL, NULL, NULL);
     TEST_EQUAL(ret, 0);
 
     ret = mbedtls_test_mock_socket_connect(&(client_ep.socket),
@@ -3572,15 +3573,11 @@
 void tls13_resume_session_with_ticket()
 {
     int ret = -1;
-    unsigned char buf[64];
     mbedtls_test_ssl_endpoint client_ep, server_ep;
     mbedtls_test_handshake_test_options client_options;
     mbedtls_test_handshake_test_options server_options;
     mbedtls_ssl_session saved_session;
 
-    /*
-     * Test set-up
-     */
     mbedtls_platform_zeroize(&client_ep, sizeof(client_ep));
     mbedtls_platform_zeroize(&server_ep, sizeof(server_ep));
     mbedtls_test_init_handshake_options(&client_options);
@@ -3589,16 +3586,27 @@
 
     PSA_INIT();
 
+    /*
+     * Run first handshake to get a ticket from the server.
+     */
     client_options.pk_alg = MBEDTLS_PK_ECDSA;
-    ret = mbedtls_test_ssl_endpoint_init(&client_ep, MBEDTLS_SSL_IS_CLIENT,
-                                         &client_options, NULL, NULL, NULL,
-                                         NULL);
+    server_options.pk_alg = MBEDTLS_PK_ECDSA;
+
+    ret = mbedtls_test_get_tls13_ticket(&client_options, &server_options,
+                                        &saved_session);
     TEST_EQUAL(ret, 0);
 
-    server_options.pk_alg = MBEDTLS_PK_ECDSA;
+    /*
+     * Prepare for handshake with the ticket.
+     */
+    ret = mbedtls_test_ssl_endpoint_init(&client_ep, MBEDTLS_SSL_IS_CLIENT,
+                                         &client_options, NULL, NULL, NULL);
+    TEST_EQUAL(ret, 0);
+
     ret = mbedtls_test_ssl_endpoint_init(&server_ep, MBEDTLS_SSL_IS_SERVER,
-                                         &server_options, NULL, NULL, NULL,
-                                         NULL);
+                                         &server_options, NULL, NULL, NULL);
+    TEST_EQUAL(ret, 0);
+
     mbedtls_ssl_conf_session_tickets_cb(&server_ep.conf,
                                         mbedtls_test_ticket_write,
                                         mbedtls_test_ticket_parse,
@@ -3609,41 +3617,12 @@
                                            &(server_ep.socket), 1024);
     TEST_EQUAL(ret, 0);
 
-    /*
-     * Run initial handshake: ephemeral key exchange mode, certificate with
-     * SECP256R1 key, CA certificate with SECP384R1 key, ECDSA signature
-     * algorithm. Then, get the ticket sent by the server at the end of its
-     * handshake sequence.
-     */
-    TEST_EQUAL(mbedtls_test_move_handshake_to_state(
-                   &(server_ep.ssl), &(client_ep.ssl),
-                   MBEDTLS_SSL_HANDSHAKE_OVER), 0);
-
-    do {
-        ret = mbedtls_ssl_read(&(client_ep.ssl), buf, sizeof(buf));
-    } while (ret != MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET);
-
-    /*
-     * Save client session and reset the SSL context of the two endpoints.
-     */
-    ret = mbedtls_ssl_get_session(&(client_ep.ssl), &saved_session);
-    TEST_EQUAL(ret, 0);
-
-    ret = mbedtls_ssl_session_reset(&(client_ep.ssl));
-    TEST_EQUAL(ret, 0);
-
-    ret = mbedtls_ssl_session_reset(&(server_ep.ssl));
-    TEST_EQUAL(ret, 0);
-
-    /*
-     * Set saved session on client side and handshake using the ticket
-     * included in that session.
-     */
-
     ret = mbedtls_ssl_set_session(&(client_ep.ssl), &saved_session);
     TEST_EQUAL(ret, 0);
 
     /*
+     * Handshake with ticket.
+     *
      * Run the handshake up to MBEDTLS_SSL_HANDSHAKE_WRAPUP and not
      * MBEDTLS_SSL_HANDSHAKE_OVER to preserve handshake data for the checks
      * below.
@@ -3690,9 +3669,6 @@
         MBEDTLS_SSL_IANA_TLS_GROUP_NONE
     };
 
-    /*
-     * Test set-up
-     */
     mbedtls_platform_zeroize(&client_ep, sizeof(client_ep));
     mbedtls_platform_zeroize(&server_ep, sizeof(server_ep));
     mbedtls_test_init_handshake_options(&client_options);
@@ -3701,21 +3677,56 @@
 
     PSA_INIT();
 
-    client_options.pk_alg = MBEDTLS_PK_ECDSA;
-    ret = mbedtls_test_ssl_endpoint_init(&client_ep, MBEDTLS_SSL_IS_CLIENT,
-                                         &client_options, NULL, NULL, NULL,
-                                         group_list);
-    TEST_EQUAL(ret, 0);
-    mbedtls_ssl_conf_early_data(&client_ep.conf, MBEDTLS_SSL_EARLY_DATA_ENABLED);
+    /*
+     * Run first handshake to get a ticket from the server.
+     */
 
+    client_options.pk_alg = MBEDTLS_PK_ECDSA;
+    client_options.group_list = group_list;
+    client_options.early_data = MBEDTLS_SSL_EARLY_DATA_ENABLED;
     server_options.pk_alg = MBEDTLS_PK_ECDSA;
+    server_options.group_list = group_list;
+    server_options.early_data = MBEDTLS_SSL_EARLY_DATA_ENABLED;
+
+    ret = mbedtls_test_get_tls13_ticket(&client_options, &server_options,
+                                        &saved_session);
+    TEST_EQUAL(ret, 0);
+
+    /*
+     * Prepare for handshake with the ticket.
+     */
+    switch (scenario) {
+        case TEST_EARLY_DATA_ACCEPTED:
+            break;
+
+        case TEST_EARLY_DATA_SERVER_REJECTS:
+            mbedtls_debug_set_threshold(3);
+            server_pattern.pattern =
+                "EarlyData: deprotect and discard app data records.";
+            server_options.early_data = MBEDTLS_SSL_EARLY_DATA_DISABLED;
+            break;
+
+        case TEST_EARLY_DATA_HRR:
+            mbedtls_debug_set_threshold(3);
+            server_pattern.pattern =
+                "EarlyData: Ignore application message before 2nd ClientHello";
+            server_options.group_list = group_list + 1;
+            break;
+
+        default:
+            TEST_FAIL("Unknown scenario.");
+    }
+
+    ret = mbedtls_test_ssl_endpoint_init(&client_ep, MBEDTLS_SSL_IS_CLIENT,
+                                         &client_options, NULL, NULL, NULL);
+    TEST_EQUAL(ret, 0);
+
     server_options.srv_log_fun = mbedtls_test_ssl_log_analyzer;
     server_options.srv_log_obj = &server_pattern;
     ret = mbedtls_test_ssl_endpoint_init(&server_ep, MBEDTLS_SSL_IS_SERVER,
-                                         &server_options, NULL, NULL, NULL,
-                                         group_list);
+                                         &server_options, NULL, NULL, NULL);
     TEST_EQUAL(ret, 0);
-    mbedtls_ssl_conf_early_data(&server_ep.conf, MBEDTLS_SSL_EARLY_DATA_ENABLED);
+
     mbedtls_ssl_conf_session_tickets_cb(&server_ep.conf,
                                         mbedtls_test_ticket_write,
                                         mbedtls_test_ticket_parse,
@@ -3725,69 +3736,12 @@
                                            &(server_ep.socket), 1024);
     TEST_EQUAL(ret, 0);
 
-    /*
-     * Run initial handshake: ephemeral key exchange mode, certificate with
-     * SECP256R1 key, CA certificate with SECP384R1 key, ECDSA signature
-     * algorithm. Then, get the ticket sent by the server at the end of its
-     * handshake sequence.
-     */
-    TEST_EQUAL(mbedtls_test_move_handshake_to_state(
-                   &(server_ep.ssl), &(client_ep.ssl),
-                   MBEDTLS_SSL_HANDSHAKE_OVER), 0);
-
-    do {
-        ret = mbedtls_ssl_read(&(client_ep.ssl), buf, sizeof(buf));
-    } while (ret != MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET);
-
-    /*
-     * Save client session and reset the SSL context of the two endpoints.
-     */
-    ret = mbedtls_ssl_get_session(&(client_ep.ssl), &saved_session);
-    TEST_EQUAL(ret, 0);
-
-    ret = mbedtls_ssl_session_reset(&(client_ep.ssl));
-    TEST_EQUAL(ret, 0);
-
-    ret = mbedtls_ssl_session_reset(&(server_ep.ssl));
-    TEST_EQUAL(ret, 0);
-
-    /*
-     * Set saved session on client side and start handshake using the ticket
-     * included in that session.
-     */
-
     ret = mbedtls_ssl_set_session(&(client_ep.ssl), &saved_session);
     TEST_EQUAL(ret, 0);
 
-    switch (scenario) {
-        case TEST_EARLY_DATA_REFERENCE:
-            break;
-
-        case TEST_EARLY_DATA_DEPROTECT_AND_DISCARD:
-            mbedtls_debug_set_threshold(3);
-            server_pattern.pattern =
-                "EarlyData: deprotect and discard app data records.";
-            mbedtls_ssl_conf_early_data(&server_ep.conf,
-                                        MBEDTLS_SSL_EARLY_DATA_DISABLED);
-            break;
-
-        case TEST_EARLY_DATA_DISCARD_AFTER_HRR:
-            mbedtls_debug_set_threshold(3);
-            server_pattern.pattern =
-                "EarlyData: Ignore application message before 2nd ClientHello";
-            mbedtls_ssl_conf_groups(&server_ep.conf, group_list + 1);
-            /*
-             * Need to reset again to reconstruct the group list in the
-             * handshake structure from the configured one.
-             */
-            ret = mbedtls_ssl_session_reset(&(server_ep.ssl));
-            TEST_EQUAL(ret, 0);
-            break;
-
-        default:
-            TEST_FAIL("Unknown scenario.");
-    }
-
+    /*
+     * Handshake with ticket and send early data.
+     */
     TEST_EQUAL(mbedtls_test_move_handshake_to_state(
                    &(client_ep.ssl), &(server_ep.ssl),
                    MBEDTLS_SSL_SERVER_HELLO), 0);
@@ -3804,7 +3758,7 @@
         MBEDTLS_SSL_HANDSHAKE_WRAPUP);
 
     switch (scenario) {
-        case TEST_EARLY_DATA_REFERENCE:
+        case TEST_EARLY_DATA_ACCEPTED:
             TEST_EQUAL(ret, MBEDTLS_ERR_SSL_RECEIVED_EARLY_DATA);
             TEST_EQUAL(server_ep.ssl.handshake->early_data_accepted, 1);
             TEST_EQUAL(mbedtls_ssl_read_early_data(&(server_ep.ssl),
@@ -3812,8 +3766,8 @@
             TEST_MEMORY_COMPARE(buf, early_data_len, early_data, early_data_len);
             break;
 
-        case TEST_EARLY_DATA_DEPROTECT_AND_DISCARD: /* Intentional fallthrough */
-        case TEST_EARLY_DATA_DISCARD_AFTER_HRR:
+        case TEST_EARLY_DATA_SERVER_REJECTS: /* Intentional fallthrough */
+        case TEST_EARLY_DATA_HRR:
             TEST_EQUAL(ret, 0);
             TEST_EQUAL(server_ep.ssl.handshake->early_data_accepted, 0);
             TEST_EQUAL(server_pattern.counter, 1);
@@ -3834,3 +3788,316 @@
     PSA_DONE();
 }
 /* END_CASE */
+
+/* BEGIN_CASE depends_on:MBEDTLS_SSL_EARLY_DATA:MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SRV_C:MBEDTLS_TEST_AT_LEAST_ONE_TLS1_3_CIPHERSUITE:MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED:MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED:MBEDTLS_MD_CAN_SHA256:MBEDTLS_ECP_HAVE_SECP256R1:MBEDTLS_ECP_HAVE_SECP384R1:MBEDTLS_PK_CAN_ECDSA_VERIFY:MBEDTLS_SSL_SESSION_TICKETS */
+void tls13_cli_early_data_status(int scenario)
+{
+    int ret = -1;
+    mbedtls_test_ssl_endpoint client_ep, server_ep;
+    mbedtls_test_handshake_test_options client_options;
+    mbedtls_test_handshake_test_options server_options;
+    mbedtls_ssl_session saved_session;
+    uint16_t group_list[3] = {
+        MBEDTLS_SSL_IANA_TLS_GROUP_SECP256R1,
+        MBEDTLS_SSL_IANA_TLS_GROUP_SECP384R1,
+        MBEDTLS_SSL_IANA_TLS_GROUP_NONE
+    };
+
+    mbedtls_platform_zeroize(&client_ep, sizeof(client_ep));
+    mbedtls_platform_zeroize(&server_ep, sizeof(server_ep));
+    mbedtls_test_init_handshake_options(&client_options);
+    mbedtls_test_init_handshake_options(&server_options);
+    mbedtls_ssl_session_init(&saved_session);
+
+    PSA_INIT();
+
+    /*
+     * Run first handshake to get a ticket from the server.
+     */
+    client_options.pk_alg = MBEDTLS_PK_ECDSA;
+    client_options.early_data = MBEDTLS_SSL_EARLY_DATA_ENABLED;
+    server_options.pk_alg = MBEDTLS_PK_ECDSA;
+    server_options.early_data = MBEDTLS_SSL_EARLY_DATA_ENABLED;
+    if (scenario == TEST_EARLY_DATA_HRR) {
+        client_options.group_list = group_list;
+        server_options.group_list = group_list;
+    }
+
+    ret = mbedtls_test_get_tls13_ticket(&client_options, &server_options,
+                                        &saved_session);
+    TEST_EQUAL(ret, 0);
+
+    /*
+     * Prepare for handshake with the ticket.
+     */
+    switch (scenario) {
+        case TEST_EARLY_DATA_ACCEPTED:
+            break;
+
+        case TEST_EARLY_DATA_NO_INDICATION_SENT:
+            client_options.early_data = MBEDTLS_SSL_EARLY_DATA_DISABLED;
+            break;
+
+        case TEST_EARLY_DATA_SERVER_REJECTS:
+            server_options.early_data = MBEDTLS_SSL_EARLY_DATA_DISABLED;
+            break;
+
+        case TEST_EARLY_DATA_HRR:
+            server_options.group_list = group_list + 1;
+            break;
+
+        default:
+            TEST_FAIL("Unknown scenario.");
+    }
+
+    ret = mbedtls_test_ssl_endpoint_init(&client_ep, MBEDTLS_SSL_IS_CLIENT,
+                                         &client_options, NULL, NULL, NULL);
+    TEST_EQUAL(ret, 0);
+
+    ret = mbedtls_test_ssl_endpoint_init(&server_ep, MBEDTLS_SSL_IS_SERVER,
+                                         &server_options, NULL, NULL, NULL);
+    TEST_EQUAL(ret, 0);
+
+    mbedtls_ssl_conf_session_tickets_cb(&server_ep.conf,
+                                        mbedtls_test_ticket_write,
+                                        mbedtls_test_ticket_parse,
+                                        NULL);
+
+    ret = mbedtls_test_mock_socket_connect(&(client_ep.socket),
+                                           &(server_ep.socket), 1024);
+    TEST_EQUAL(ret, 0);
+
+    ret = mbedtls_ssl_set_session(&(client_ep.ssl), &saved_session);
+    TEST_EQUAL(ret, 0);
+
+    /*
+     * Go through the handshake sequence, state by state, checking the early
+     * data status each time.
+     */
+    do {
+        int state = client_ep.ssl.state;
+
+        /* Progress the handshake from at least one state */
+        while (client_ep.ssl.state == state) {
+            ret = mbedtls_ssl_handshake_step(&(client_ep.ssl));
+            TEST_ASSERT((ret == 0) ||
+                        (ret == MBEDTLS_ERR_SSL_WANT_READ) ||
+                        (ret == MBEDTLS_ERR_SSL_WANT_WRITE));
+            if (client_ep.ssl.state != state) {
+                break;
+            }
+            ret = mbedtls_ssl_handshake_step(&(server_ep.ssl));
+            TEST_ASSERT((ret == 0) ||
+                        (ret == MBEDTLS_ERR_SSL_WANT_READ) ||
+                        (ret == MBEDTLS_ERR_SSL_WANT_WRITE));
+        }
+
+        switch (client_ep.ssl.state) {
+            case MBEDTLS_SSL_CLIENT_HELLO:
+                switch (scenario) {
+                    case TEST_EARLY_DATA_ACCEPTED: /* Intentional fallthrough */
+                    case TEST_EARLY_DATA_NO_INDICATION_SENT: /* Intentional fallthrough */
+                    case TEST_EARLY_DATA_SERVER_REJECTS:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_UNKNOWN);
+                        break;
+
+                    case TEST_EARLY_DATA_HRR:
+                        if (client_ep.ssl.handshake->hello_retry_request_count == 0) {
+                            TEST_EQUAL(client_ep.ssl.early_data_status,
+                                       MBEDTLS_SSL_EARLY_DATA_STATUS_UNKNOWN);
+                        } else {
+                            TEST_EQUAL(client_ep.ssl.early_data_status,
+                                       MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED);
+                        }
+                        break;
+                }
+                break;
+
+            case MBEDTLS_SSL_SERVER_HELLO:
+                switch (scenario) {
+                    case TEST_EARLY_DATA_ACCEPTED: /* Intentional fallthrough */
+                    case TEST_EARLY_DATA_SERVER_REJECTS:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_WRITE);
+                        break;
+
+                    case TEST_EARLY_DATA_NO_INDICATION_SENT:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT);
+                        break;
+
+                    case TEST_EARLY_DATA_HRR:
+                        if (client_ep.ssl.handshake->hello_retry_request_count == 0) {
+                            TEST_EQUAL(client_ep.ssl.early_data_status,
+                                       MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_WRITE);
+                        } else {
+                            TEST_EQUAL(client_ep.ssl.early_data_status,
+                                       MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED);
+                        }
+                        break;
+                }
+                break;
+
+            case MBEDTLS_SSL_ENCRYPTED_EXTENSIONS:
+                switch (scenario) {
+                    case TEST_EARLY_DATA_ACCEPTED: /* Intentional fallthrough */
+                    case TEST_EARLY_DATA_SERVER_REJECTS:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_WRITE);
+                        break;
+
+                    case TEST_EARLY_DATA_NO_INDICATION_SENT:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT);
+                        break;
+
+                    case TEST_EARLY_DATA_HRR:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED);
+                        break;
+                }
+                break;
+
+            case MBEDTLS_SSL_SERVER_FINISHED:
+                switch (scenario) {
+                    case TEST_EARLY_DATA_ACCEPTED:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED);
+                        break;
+
+                    case TEST_EARLY_DATA_NO_INDICATION_SENT:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT);
+                        break;
+
+                    case TEST_EARLY_DATA_SERVER_REJECTS: /* Intentional fallthrough */
+                    case TEST_EARLY_DATA_HRR:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED);
+                        break;
+                }
+                break;
+
+            case MBEDTLS_SSL_END_OF_EARLY_DATA:
+                TEST_EQUAL(scenario, TEST_EARLY_DATA_ACCEPTED);
+                TEST_EQUAL(client_ep.ssl.early_data_status,
+                           MBEDTLS_SSL_EARLY_DATA_STATUS_SERVER_FINISHED_RECEIVED);
+                break;
+
+            case MBEDTLS_SSL_CLIENT_CERTIFICATE:
+                switch (scenario) {
+                    case TEST_EARLY_DATA_ACCEPTED:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_SERVER_FINISHED_RECEIVED);
+                        break;
+
+                    case TEST_EARLY_DATA_NO_INDICATION_SENT:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT);
+                        break;
+
+                    case TEST_EARLY_DATA_SERVER_REJECTS: /* Intentional fallthrough */
+                    case TEST_EARLY_DATA_HRR:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED);
+                        break;
+                }
+                break;
+
+            case MBEDTLS_SSL_CLIENT_FINISHED:
+                switch (scenario) {
+                    case TEST_EARLY_DATA_ACCEPTED:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_SERVER_FINISHED_RECEIVED);
+                        break;
+
+                    case TEST_EARLY_DATA_NO_INDICATION_SENT:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT);
+                        break;
+
+                    case TEST_EARLY_DATA_SERVER_REJECTS: /* Intentional fallthrough */
+                    case TEST_EARLY_DATA_HRR:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED);
+                        break;
+                }
+                break;
+
+#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE)
+            case MBEDTLS_SSL_CLIENT_CCS_AFTER_CLIENT_HELLO:
+                TEST_ASSERT(scenario != TEST_EARLY_DATA_NO_INDICATION_SENT);
+                switch (scenario) {
+                    case TEST_EARLY_DATA_ACCEPTED: /* Intentional fallthrough */
+                    case TEST_EARLY_DATA_SERVER_REJECTS: /* Intentional fallthrough */
+                    case TEST_EARLY_DATA_HRR:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_SENT);
+                        break;
+                }
+                break;
+
+            case MBEDTLS_SSL_CLIENT_CCS_BEFORE_2ND_CLIENT_HELLO:
+                TEST_ASSERT(scenario == TEST_EARLY_DATA_HRR);
+                TEST_EQUAL(client_ep.ssl.early_data_status,
+                           MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED);
+                break;
+
+            case MBEDTLS_SSL_CLIENT_CCS_AFTER_SERVER_FINISHED:
+                TEST_ASSERT(scenario != TEST_EARLY_DATA_ACCEPTED);
+                switch (scenario) {
+                    case TEST_EARLY_DATA_NO_INDICATION_SENT:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT);
+                        break;
+
+                    case TEST_EARLY_DATA_SERVER_REJECTS: /* Intentional fallthrough */
+                    case TEST_EARLY_DATA_HRR:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED);
+                        break;
+                }
+                break;
+#endif /* MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE */
+
+            case MBEDTLS_SSL_FLUSH_BUFFERS: /* Intentional fallthrough */
+            case MBEDTLS_SSL_HANDSHAKE_WRAPUP: /* Intentional fallthrough */
+            case MBEDTLS_SSL_HANDSHAKE_OVER:
+                switch (scenario) {
+                    case TEST_EARLY_DATA_ACCEPTED:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_SERVER_FINISHED_RECEIVED);
+                        break;
+
+                    case TEST_EARLY_DATA_NO_INDICATION_SENT:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT);
+                        break;
+
+                    case TEST_EARLY_DATA_SERVER_REJECTS: /* Intentional fallthrough */
+                    case TEST_EARLY_DATA_HRR:
+                        TEST_EQUAL(client_ep.ssl.early_data_status,
+                                   MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED);
+                        break;
+                }
+                break;
+
+            default:
+                TEST_FAIL("Unexpected state.");
+        }
+    } while (client_ep.ssl.state != MBEDTLS_SSL_HANDSHAKE_OVER);
+
+#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE)
+    TEST_EQUAL(client_ep.ssl.handshake->ccs_count, 1);
+#endif
+
+exit:
+    mbedtls_test_ssl_endpoint_free(&client_ep, NULL);
+    mbedtls_test_ssl_endpoint_free(&server_ep, NULL);
+    mbedtls_test_free_handshake_options(&client_options);
+    mbedtls_test_free_handshake_options(&server_options);
+    mbedtls_ssl_session_free(&saved_session);
+    PSA_DONE();
+}
+/* END_CASE */