Merge pull request #8700 from valeriosetti/issue8461

psa_asymmetric_encrypt() doesn't work with opaque driver
diff --git a/ChangeLog.d/7764.txt b/ChangeLog.d/7764.txt
new file mode 100644
index 0000000..4cd2079
--- /dev/null
+++ b/ChangeLog.d/7764.txt
@@ -0,0 +1,3 @@
+Features
+   * Add functions mbedtls_ecc_group_to_psa() and mbedtls_ecc_group_from_psa()
+     to convert between Mbed TLS and PSA curve identifiers.
diff --git a/ChangeLog.d/8340.txt b/ChangeLog.d/8340.txt
new file mode 100644
index 0000000..5664bf1
--- /dev/null
+++ b/ChangeLog.d/8340.txt
@@ -0,0 +1,4 @@
+Features
+   * Add functions mbedtls_md_psa_alg_from_type() and
+     mbedtls_md_type_from_psa_alg() to convert between mbedtls_md_type_t and
+     psa_algorithm_t.
diff --git a/ChangeLog.d/add-record-size-limit-extension-support.txt b/ChangeLog.d/add-record-size-limit-extension-support.txt
new file mode 100644
index 0000000..3562b85
--- /dev/null
+++ b/ChangeLog.d/add-record-size-limit-extension-support.txt
@@ -0,0 +1,5 @@
+Features
+   * Add support for record size limit extension as defined by RFC 8449
+     and configured with MBEDTLS_SSL_RECORD_SIZE_LIMIT.
+     Application data sent and received will be fragmented according to
+     Record size limits negotiated during handshake.
diff --git a/ChangeLog.d/ctr-perf.txt b/ChangeLog.d/ctr-perf.txt
new file mode 100644
index 0000000..bc04080
--- /dev/null
+++ b/ChangeLog.d/ctr-perf.txt
@@ -0,0 +1,3 @@
+Features
+   * Improve performance of AES-GCM, AES-CTR and CTR-DRBG when
+     hardware accelerated AES is not present (around 13-23% on 64-bit Arm).
diff --git a/ChangeLog.d/ecp-keypair-utilities.txt b/ChangeLog.d/ecp-keypair-utilities.txt
new file mode 100644
index 0000000..6f9714a
--- /dev/null
+++ b/ChangeLog.d/ecp-keypair-utilities.txt
@@ -0,0 +1,5 @@
+Features
+   * Add utility functions to manipulate mbedtls_ecp_keypair objects, filling
+     gaps made by making its fields private: mbedtls_ecp_set_public_key(),
+     mbedtls_ecp_write_public_key(), mbedtls_ecp_keypair_calc_public(),
+     mbedtls_ecp_keypair_get_group_id(). Fixes #5017, #5441, #8367, #8652.
diff --git a/ChangeLog.d/fix-tls-SuiteB.txt b/ChangeLog.d/fix-tls-SuiteB.txt
new file mode 100644
index 0000000..0be753a
--- /dev/null
+++ b/ChangeLog.d/fix-tls-SuiteB.txt
@@ -0,0 +1,3 @@
+Bugfix
+   * Remove accidental introduction of RSA signature algorithms
+     in TLS Suite B Profile. Fixes #8221.
diff --git a/docs/psa-transition.md b/docs/psa-transition.md
index 067ffaf..e89128c 100644
--- a/docs/psa-transition.md
+++ b/docs/psa-transition.md
@@ -443,6 +443,10 @@
 | `MBEDTLS_MD_SHA3_384`  | `PSA_ALG_SHA3_384`  |
 | `MBEDTLS_MD_SHA3_512`  | `PSA_ALG_SHA3_512`  |
 
+The following helper functions can be used to convert between the 2 types:
+- `mbedtls_md_psa_alg_from_type()` converts from legacy `mbedtls_md_type_t` to PSA's `psa_algorithm_t`.
+- `mbedtls_md_type_from_psa_alg()` converts from PSA's `psa_algorithm_t` to legacy `mbedtls_md_type_t`.
+
 ### MAC mechanism selection
 
 PSA Crypto has a generic API with the same functions for all MAC mechanisms. The mechanism is determined by a combination of an algorithm value of type [`psa_algorithm_t`](https://mbed-tls.readthedocs.io/projects/api/en/development/api/group/group__crypto__types/#group__crypto__types_1gac2e4d47f1300d73c2f829a6d99252d69) and a key type value of type [`psa_key_type_t`](https://mbed-tls.readthedocs.io/projects/api/en/development/api/group/group__crypto__types/#group__crypto__types_1ga63fce6880ca5933b5d6baa257febf1f6).
@@ -725,7 +729,11 @@
 An ECC key pair has the type [`PSA_KEY_TYPE_ECC_KEY_PAIR(curve)`](https://mbed-tls.readthedocs.io/projects/api/en/development/api/group/group__crypto__types/#group__crypto__types_1ga0b6f5d4d5037c54ffa850d8059c32df0) where `curve` is a curve family identifier. A key with this type can be used both for private-key and public-key operations (there is no separate key type for a private key without the corresponding public key).
 You can always use a private key for operations on the corresponding public key (as long as the policy permits it).
 
-A curve is fully determined by a curve family identifier and the private key size in bits. The following table gives the correspondence between legacy and PSA elliptic curve designations.
+A curve is fully determined by a curve family identifier and the private key size in bits. You can use the following functions to convert between the PSA and legacy elliptic curve designations:
+- [`mbedtls_ecc_group_to_psa()`](https://mbed-tls.readthedocs.io/projects/api/en/development/api/group/group__psa__tls__helpers/#group__psa__tls__helpers_1ga9c83c095adfec7da99401cf81e164f99) converts from the legacy curve type identifier to PSA curve family and bit-size.
+- [`mbedtls_ecc_group_from_psa()`](https://mbed-tls.readthedocs.io/projects/api/en/development/api/group/group__psa__tls__helpers/#group__psa__tls__helpers_1ga6243eb619d5b2f5fe4667811adeb8a12) converts from PSA curve family and bit-size to the legacy identifier.
+
+The following table gives the correspondence between legacy and PSA elliptic curve designations.
 
 | Mbed TLS legacy curve identifier | PSA curve family | Curve bit-size |
 | -------------------------------- | ---------------- | -------------- |
diff --git a/docs/redirects.yaml b/docs/redirects.yaml
index 7ea1d95..969ffe4 100644
--- a/docs/redirects.yaml
+++ b/docs/redirects.yaml
@@ -7,5 +7,5 @@
 # expose it.
 
 - type: exact
-  from_url: /projects/api/en/latest/$rest
-  to_url: /projects/api/en/development/
+  from_url: /projects/api/en/latest/*
+  to_url: /projects/api/en/development/:splat
diff --git a/include/mbedtls/config_adjust_ssl.h b/include/mbedtls/config_adjust_ssl.h
index 8415f3e..5dd331c 100644
--- a/include/mbedtls/config_adjust_ssl.h
+++ b/include/mbedtls/config_adjust_ssl.h
@@ -65,6 +65,7 @@
 #undef MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
 #undef MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED
 #undef MBEDTLS_SSL_EARLY_DATA
+#undef MBEDTLS_SSL_RECORD_SIZE_LIMIT
 #endif
 
 #if defined(MBEDTLS_SSL_PROTO_TLS1_2) && \
diff --git a/include/mbedtls/ecp.h b/include/mbedtls/ecp.h
index 7f5e880..76aef32 100644
--- a/include/mbedtls/ecp.h
+++ b/include/mbedtls/ecp.h
@@ -1259,9 +1259,56 @@
                         int (*f_rng)(void *, unsigned char *, size_t),
                         void *p_rng);
 
+/** \brief          Set the public key in a key pair object.
+ *
+ * \note            This function does not check that the point actually
+ *                  belongs to the given group. Call mbedtls_ecp_check_pubkey()
+ *                  on \p Q before calling this function to check that.
+ *
+ * \note            This function does not check that the public key matches
+ *                  the private key that is already in \p key, if any.
+ *                  To check the consistency of the resulting key pair object,
+ *                  call mbedtls_ecp_check_pub_priv() after setting both
+ *                  the public key and the private key.
+ *
+ * \param grp_id    The ECP group identifier.
+ * \param key       The key pair object. It must be initialized.
+ *                  If its group has already been set, it must match \p grp_id.
+ *                  If its group has not been set, it will be set to \p grp_id.
+ *                  If the public key has already been set, it is overwritten.
+ * \param Q         The public key to copy. This must be a point on the
+ *                  curve indicated by \p grp_id.
+ *
+ * \return          \c 0 on success.
+ * \return          #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if \p key does not
+ *                  match \p grp_id.
+ * \return          #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if the operation for
+ *                  the group is not implemented.
+ * \return          #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure.
+ * \return          Another negative error code on other kinds of failure.
+ */
+int mbedtls_ecp_set_public_key(mbedtls_ecp_group_id grp_id,
+                               mbedtls_ecp_keypair *key,
+                               const mbedtls_ecp_point *Q);
+
 /**
  * \brief           This function reads an elliptic curve private key.
  *
+ * \note            This function does not set the public key in the
+ *                  key pair object. Without a public key, the key pair object
+ *                  cannot be used with operations that require the public key.
+ *                  Call mbedtls_ecp_keypair_calc_public() to set the public
+ *                  key from the private key. Alternatively, you can call
+ *                  mbedtls_ecp_set_public_key() to set the public key part,
+ *                  and then optionally mbedtls_ecp_check_pub_priv() to check
+ *                  that the private and public parts are consistent.
+ *
+ * \note            If a public key has already been set in the key pair
+ *                  object, this function does not check that it is consistent
+ *                  with the private key. Call mbedtls_ecp_check_pub_priv()
+ *                  after setting both the public key and the private key
+ *                  to make that check.
+ *
  * \param grp_id    The ECP group identifier.
  * \param key       The destination key.
  * \param buf       The buffer containing the binary representation of the
@@ -1300,6 +1347,32 @@
                           unsigned char *buf, size_t buflen);
 
 /**
+ * \brief           This function exports an elliptic curve public key.
+ *
+ * \param key       The public key.
+ * \param format    The point format. This must be either
+ *                  #MBEDTLS_ECP_PF_COMPRESSED or #MBEDTLS_ECP_PF_UNCOMPRESSED.
+ *                  (For groups without these formats, this parameter is
+ *                  ignored. But it still has to be either of the above
+ *                  values.)
+ * \param olen      The address at which to store the length of
+ *                  the output in Bytes. This must not be \c NULL.
+ * \param buf       The output buffer. This must be a writable buffer
+ *                  of length \p buflen Bytes.
+ * \param buflen    The length of the output buffer \p buf in Bytes.
+ *
+ * \return          \c 0 on success.
+ * \return          #MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL if the output buffer
+ *                  is too small to hold the point.
+ * \return          #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if the point format
+ *                  or the export for the given group is not implemented.
+ * \return          Another negative error code on other kinds of failure.
+ */
+int mbedtls_ecp_write_public_key(const mbedtls_ecp_keypair *key,
+                                 int format, size_t *olen,
+                                 unsigned char *buf, size_t buflen);
+
+/**
  * \brief           This function checks that the keypair objects
  *                  \p pub and \p prv have the same group and the
  *                  same public point, and that the private key in
@@ -1323,16 +1396,48 @@
     const mbedtls_ecp_keypair *pub, const mbedtls_ecp_keypair *prv,
     int (*f_rng)(void *, unsigned char *, size_t), void *p_rng);
 
+/** \brief          Calculate the public key from a private key in a key pair.
+ *
+ * \param key       A keypair structure. It must have a private key set.
+ *                  If the public key is set, it will be overwritten.
+ * \param f_rng     The RNG function. This must not be \c NULL.
+ * \param p_rng     The RNG context to be passed to \p f_rng. This may be \c
+ *                  NULL if \p f_rng doesn't need a context.
+ *
+ * \return          \c 0 on success. The key pair object can be used for
+ *                  operations that require the public key.
+ * \return          An \c MBEDTLS_ERR_ECP_XXX or an \c MBEDTLS_ERR_MPI_XXX
+ *                  error code on calculation failure.
+ */
+int mbedtls_ecp_keypair_calc_public(
+    mbedtls_ecp_keypair *key,
+    int (*f_rng)(void *, unsigned char *, size_t), void *p_rng);
+
+/** \brief          Query the group that a key pair belongs to.
+ *
+ * \param key       The key pair to query.
+ *
+ * \return          The group ID for the group registered in the key pair
+ *                  object.
+ *                  This is \c MBEDTLS_ECP_DP_NONE if no group has been set
+ *                  in the key pair object.
+ */
+mbedtls_ecp_group_id mbedtls_ecp_keypair_get_group_id(
+    const mbedtls_ecp_keypair *key);
+
 /**
  * \brief           This function exports generic key-pair parameters.
  *
+ *                  Each of the output parameters can be a null pointer
+ *                  if you do not need that parameter.
+ *
  * \param key       The key pair to export from.
  * \param grp       Slot for exported ECP group.
- *                  It must point to an initialized ECP group.
+ *                  It must either be null or point to an initialized ECP group.
  * \param d         Slot for the exported secret value.
- *                  It must point to an initialized mpi.
+ *                  It must either be null or point to an initialized mpi.
  * \param Q         Slot for the exported public value.
- *                  It must point to an initialized ECP point.
+ *                  It must either be null or point to an initialized ECP point.
  *
  * \return          \c 0 on success,
  * \return          #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure.
diff --git a/include/mbedtls/psa_util.h b/include/mbedtls/psa_util.h
index 5f6a053..47724c6 100644
--- a/include/mbedtls/psa_util.h
+++ b/include/mbedtls/psa_util.h
@@ -110,44 +110,72 @@
 
 /** Convert an ECC curve identifier from the Mbed TLS encoding to PSA.
  *
- * \note This function is provided solely for the convenience of
- *       Mbed TLS and may be removed at any time without notice.
- *
  * \param grpid         An Mbed TLS elliptic curve identifier
  *                      (`MBEDTLS_ECP_DP_xxx`).
- * \param[out] bits     On success, the bit size of the curve.
+ * \param[out] bits     On success the bit size of the curve; 0 on failure.
  *
- * \return              The corresponding PSA elliptic curve identifier
- *                      (`PSA_ECC_FAMILY_xxx`).
- * \return              \c 0 on failure (\p grpid is not recognized).
+ * \return              If the curve is supported in the PSA API, this function
+ *                      returns the proper PSA curve identifier
+ *                      (`PSA_ECC_FAMILY_xxx`). This holds even if the curve is
+ *                      not supported by the ECP module.
+ * \return              \c 0 if the curve is not supported in the PSA API.
  */
 psa_ecc_family_t mbedtls_ecc_group_to_psa(mbedtls_ecp_group_id grpid,
                                           size_t *bits);
 
 /** Convert an ECC curve identifier from the PSA encoding to Mbed TLS.
  *
- * \note This function is provided solely for the convenience of
- *       Mbed TLS and may be removed at any time without notice.
- *
- * \param curve         A PSA elliptic curve identifier
+ * \param family        A PSA elliptic curve family identifier
  *                      (`PSA_ECC_FAMILY_xxx`).
  * \param bits          The bit-length of a private key on \p curve.
- * \param bits_is_sloppy If true, \p bits may be the bit-length rounded up
- *                      to the nearest multiple of 8. This allows the caller
- *                      to infer the exact curve from the length of a key
- *                      which is supplied as a byte string.
  *
- * \return              The corresponding Mbed TLS elliptic curve identifier
- *                      (`MBEDTLS_ECP_DP_xxx`).
- * \return              #MBEDTLS_ECP_DP_NONE if \c curve is not recognized.
- * \return              #MBEDTLS_ECP_DP_NONE if \p bits is not
- *                      correct for \p curve.
+ * \return              If the curve is supported in the PSA API, this function
+ *                      returns the corresponding Mbed TLS elliptic curve
+ *                      identifier (`MBEDTLS_ECP_DP_xxx`).
+ * \return              #MBEDTLS_ECP_DP_NONE if the combination of \c curve
+ *                      and \p bits is not supported.
  */
-mbedtls_ecp_group_id mbedtls_ecc_group_of_psa(psa_ecc_family_t curve,
-                                              size_t bits,
-                                              int bits_is_sloppy);
+mbedtls_ecp_group_id mbedtls_ecc_group_from_psa(psa_ecc_family_t family,
+                                                size_t bits);
 #endif /* PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY */
 
+/**
+ * \brief           This function returns the PSA algorithm identifier
+ *                  associated with the given digest type.
+ *
+ * \param md_type   The type of digest to search for. Must not be NONE.
+ *
+ * \warning         If \p md_type is \c MBEDTLS_MD_NONE, this function will
+ *                  not return \c PSA_ALG_NONE, but an invalid algorithm.
+ *
+ * \warning         This function does not check if the algorithm is
+ *                  supported, it always returns the corresponding identifier.
+ *
+ * \return          The PSA algorithm identifier associated with \p md_type,
+ *                  regardless of whether it is supported or not.
+ */
+static inline psa_algorithm_t mbedtls_md_psa_alg_from_type(mbedtls_md_type_t md_type)
+{
+    return PSA_ALG_CATEGORY_HASH | (psa_algorithm_t) md_type;
+}
+
+/**
+ * \brief           This function returns the given digest type
+ *                  associated with the PSA algorithm identifier.
+ *
+ * \param psa_alg   The PSA algorithm identifier to search for.
+ *
+ * \warning         This function does not check if the algorithm is
+ *                  supported, it always returns the corresponding identifier.
+ *
+ * \return          The MD type associated with \p psa_alg,
+ *                  regardless of whether it is supported or not.
+ */
+static inline mbedtls_md_type_t mbedtls_md_type_from_psa_alg(psa_algorithm_t psa_alg)
+{
+    return (mbedtls_md_type_t) (psa_alg & PSA_ALG_HASH_MASK);
+}
+
 /**@}*/
 
 #endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
index 46a3829..3629526 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -447,7 +447,7 @@
 
 /*
  * TLS 1.3 signature algorithms
- * RFC 8446, Section 4.2.2
+ * RFC 8446, Section 4.2.3
  */
 
 /* RSASSA-PKCS1-v1_5 algorithms */
diff --git a/include/mbedtls/threading.h b/include/mbedtls/threading.h
index b504233..b4e0502 100644
--- a/include/mbedtls/threading.h
+++ b/include/mbedtls/threading.h
@@ -100,6 +100,20 @@
 extern mbedtls_threading_mutex_t mbedtls_threading_gmtime_mutex;
 #endif /* MBEDTLS_HAVE_TIME_DATE && !MBEDTLS_PLATFORM_GMTIME_R_ALT */
 
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+/*
+ * A mutex used to make the PSA subsystem thread safe.
+ *
+ * key_slot_mutex protects the registered_readers and
+ * state variable for all key slots in &global_data.key_slots.
+ *
+ * This mutex must be held when any read from or write to a state or
+ * registered_readers field is performed, i.e. when calling functions:
+ * psa_key_slot_state_transition(), psa_register_read(), psa_unregister_read(),
+ * psa_key_slot_has_readers() and psa_wipe_key_slot(). */
+extern mbedtls_threading_mutex_t mbedtls_threading_key_slot_mutex;
+#endif
+
 #endif /* MBEDTLS_THREADING_C */
 
 #ifdef __cplusplus
diff --git a/include/psa/crypto_values.h b/include/psa/crypto_values.h
index 8d30bf0..90d98fd 100644
--- a/include/psa/crypto_values.h
+++ b/include/psa/crypto_values.h
@@ -279,6 +279,11 @@
  * to read from a resource. */
 #define PSA_ERROR_INSUFFICIENT_DATA     ((psa_status_t)-143)
 
+/** This can be returned if a function can no longer operate correctly.
+ * For example, if an essential initialization operation failed or
+ * a mutex operation failed. */
+#define PSA_ERROR_SERVICE_FAILURE       ((psa_status_t)-144)
+
 /** The key identifier is not valid. See also :ref:\`key-handles\`.
  */
 #define PSA_ERROR_INVALID_HANDLE        ((psa_status_t)-136)
diff --git a/library/aes.c b/library/aes.c
index f4b9739..b1a5c3e 100644
--- a/library/aes.c
+++ b/library/aes.c
@@ -53,6 +53,7 @@
 #endif
 
 #include "mbedtls/platform.h"
+#include "ctr.h"
 
 /*
  * This is a convenience shorthand macro to check if we need reverse S-box and
@@ -1441,36 +1442,38 @@
                           const unsigned char *input,
                           unsigned char *output)
 {
-    int c, i;
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    size_t n;
 
-    n = *nc_off;
+    size_t offset = *nc_off;
 
-    if (n > 0x0F) {
+    if (offset > 0x0F) {
         return MBEDTLS_ERR_AES_BAD_INPUT_DATA;
     }
 
-    while (length--) {
-        if (n == 0) {
+    for (size_t i = 0; i < length;) {
+        size_t n = 16;
+        if (offset == 0) {
             ret = mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, nonce_counter, stream_block);
             if (ret != 0) {
                 goto exit;
             }
-
-            for (i = 16; i > 0; i--) {
-                if (++nonce_counter[i - 1] != 0) {
-                    break;
-                }
-            }
+            mbedtls_ctr_increment_counter(nonce_counter);
+        } else {
+            n -= offset;
         }
-        c = *input++;
-        *output++ = (unsigned char) (c ^ stream_block[n]);
 
-        n = (n + 1) & 0x0F;
+        if (n > (length - i)) {
+            n = (length - i);
+        }
+        mbedtls_xor(&output[i], &input[i], &stream_block[offset], n);
+        // offset might be non-zero for the last block, but in that case, we don't use it again
+        offset = 0;
+        i += n;
     }
 
-    *nc_off = n;
+    // capture offset for future resumption
+    *nc_off = (*nc_off + length) % 16;
+
     ret = 0;
 
 exit:
diff --git a/library/ctr.h b/library/ctr.h
new file mode 100644
index 0000000..aa48fb9
--- /dev/null
+++ b/library/ctr.h
@@ -0,0 +1,35 @@
+/**
+ * \file ctr.h
+ *
+ * \brief    This file contains common functionality for counter algorithms.
+ *
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef MBEDTLS_CTR_H
+#define MBEDTLS_CTR_H
+
+#include "common.h"
+
+/**
+ * \brief               Increment a big-endian 16-byte value.
+ *                      This is quite performance-sensitive for AES-CTR and CTR-DRBG.
+ *
+ * \param n             A 16-byte value to be incremented.
+ */
+static inline void mbedtls_ctr_increment_counter(uint8_t n[16])
+{
+    // The 32-bit version seems to perform about the same as a 64-bit version
+    // on 64-bit architectures, so no need to define a 64-bit version.
+    for (int i = 3;; i--) {
+        uint32_t x = MBEDTLS_GET_UINT32_BE(n, i << 2);
+        x += 1;
+        MBEDTLS_PUT_UINT32_BE(x, n, i << 2);
+        if (x != 0 || i == 0) {
+            break;
+        }
+    }
+}
+
+#endif /* MBEDTLS_CTR_H */
diff --git a/library/ctr_drbg.c b/library/ctr_drbg.c
index da34f95..66d9d28 100644
--- a/library/ctr_drbg.c
+++ b/library/ctr_drbg.c
@@ -14,6 +14,7 @@
 
 #if defined(MBEDTLS_CTR_DRBG_C)
 
+#include "ctr.h"
 #include "mbedtls/ctr_drbg.h"
 #include "mbedtls/platform_util.h"
 #include "mbedtls/error.h"
@@ -333,7 +334,7 @@
 {
     unsigned char tmp[MBEDTLS_CTR_DRBG_SEEDLEN];
     unsigned char *p = tmp;
-    int i, j;
+    int j;
     int ret = 0;
 #if !defined(MBEDTLS_AES_C)
     psa_status_t status;
@@ -346,11 +347,7 @@
         /*
          * Increase counter
          */
-        for (i = MBEDTLS_CTR_DRBG_BLOCKSIZE; i > 0; i--) {
-            if (++ctx->counter[i - 1] != 0) {
-                break;
-            }
-        }
+        mbedtls_ctr_increment_counter(ctx->counter);
 
         /*
          * Crypt counter block
@@ -372,9 +369,7 @@
         p += MBEDTLS_CTR_DRBG_BLOCKSIZE;
     }
 
-    for (i = 0; i < MBEDTLS_CTR_DRBG_SEEDLEN; i++) {
-        tmp[i] ^= data[i];
-    }
+    mbedtls_xor(tmp, tmp, data, MBEDTLS_CTR_DRBG_SEEDLEN);
 
     /*
      * Update key and counter
@@ -617,10 +612,11 @@
 {
     int ret = 0;
     mbedtls_ctr_drbg_context *ctx = (mbedtls_ctr_drbg_context *) p_rng;
-    unsigned char add_input[MBEDTLS_CTR_DRBG_SEEDLEN];
     unsigned char *p = output;
-    unsigned char tmp[MBEDTLS_CTR_DRBG_BLOCKSIZE];
-    int i;
+    struct {
+        unsigned char add_input[MBEDTLS_CTR_DRBG_SEEDLEN];
+        unsigned char tmp[MBEDTLS_CTR_DRBG_BLOCKSIZE];
+    } locals;
     size_t use_len;
 
     if (output_len > MBEDTLS_CTR_DRBG_MAX_REQUEST) {
@@ -631,7 +627,7 @@
         return MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG;
     }
 
-    memset(add_input, 0, MBEDTLS_CTR_DRBG_SEEDLEN);
+    memset(locals.add_input, 0, MBEDTLS_CTR_DRBG_SEEDLEN);
 
     if (ctx->reseed_counter > ctx->reseed_interval ||
         ctx->prediction_resistance) {
@@ -642,30 +638,26 @@
     }
 
     if (add_len > 0) {
-        if ((ret = block_cipher_df(add_input, additional, add_len)) != 0) {
+        if ((ret = block_cipher_df(locals.add_input, additional, add_len)) != 0) {
             goto exit;
         }
-        if ((ret = ctr_drbg_update_internal(ctx, add_input)) != 0) {
+        if ((ret = ctr_drbg_update_internal(ctx, locals.add_input)) != 0) {
             goto exit;
         }
     }
 
     while (output_len > 0) {
         /*
-         * Increase counter
+         * Increase counter (treat it as a 128-bit big-endian integer).
          */
-        for (i = MBEDTLS_CTR_DRBG_BLOCKSIZE; i > 0; i--) {
-            if (++ctx->counter[i - 1] != 0) {
-                break;
-            }
-        }
+        mbedtls_ctr_increment_counter(ctx->counter);
 
         /*
          * Crypt counter block
          */
 #if defined(MBEDTLS_AES_C)
         if ((ret = mbedtls_aes_crypt_ecb(&ctx->aes_ctx, MBEDTLS_AES_ENCRYPT,
-                                         ctx->counter, tmp)) != 0) {
+                                         ctx->counter, locals.tmp)) != 0) {
             goto exit;
         }
 #else
@@ -673,7 +665,7 @@
         size_t tmp_len;
 
         status = psa_cipher_update(&ctx->psa_ctx.operation, ctx->counter, sizeof(ctx->counter),
-                                   tmp, MBEDTLS_CTR_DRBG_BLOCKSIZE, &tmp_len);
+                                   locals.tmp, MBEDTLS_CTR_DRBG_BLOCKSIZE, &tmp_len);
         if (status != PSA_SUCCESS) {
             ret = psa_generic_status_to_mbedtls(status);
             goto exit;
@@ -685,20 +677,19 @@
         /*
          * Copy random block to destination
          */
-        memcpy(p, tmp, use_len);
+        memcpy(p, locals.tmp, use_len);
         p += use_len;
         output_len -= use_len;
     }
 
-    if ((ret = ctr_drbg_update_internal(ctx, add_input)) != 0) {
+    if ((ret = ctr_drbg_update_internal(ctx, locals.add_input)) != 0) {
         goto exit;
     }
 
     ctx->reseed_counter++;
 
 exit:
-    mbedtls_platform_zeroize(add_input, sizeof(add_input));
-    mbedtls_platform_zeroize(tmp, sizeof(tmp));
+    mbedtls_platform_zeroize(&locals, sizeof(locals));
     return ret;
 }
 
diff --git a/library/ecp.c b/library/ecp.c
index ee86cbc..758d54b 100644
--- a/library/ecp.c
+++ b/library/ecp.c
@@ -3198,6 +3198,25 @@
 }
 #endif /* MBEDTLS_ECP_C */
 
+int mbedtls_ecp_set_public_key(mbedtls_ecp_group_id grp_id,
+                               mbedtls_ecp_keypair *key,
+                               const mbedtls_ecp_point *Q)
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+    if (key->grp.id == MBEDTLS_ECP_DP_NONE) {
+        /* Group not set yet */
+        if ((ret = mbedtls_ecp_group_load(&key->grp, grp_id)) != 0) {
+            return ret;
+        }
+    } else if (key->grp.id != grp_id) {
+        /* Group mismatch */
+        return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
+    }
+    return mbedtls_ecp_copy(&key->Q, Q);
+}
+
+
 #define ECP_CURVE25519_KEY_SIZE 32
 #define ECP_CURVE448_KEY_SIZE   56
 /*
@@ -3314,6 +3333,18 @@
     return ret;
 }
 
+/*
+ * Write a public key.
+ */
+int mbedtls_ecp_write_public_key(const mbedtls_ecp_keypair *key,
+                                 int format, size_t *olen,
+                                 unsigned char *buf, size_t buflen)
+{
+    return mbedtls_ecp_point_write_binary(&key->grp, &key->Q,
+                                          format, olen, buf, buflen);
+}
+
+
 #if defined(MBEDTLS_ECP_C)
 /*
  * Check a public-private key pair
@@ -3355,8 +3386,22 @@
 
     return ret;
 }
+
+int mbedtls_ecp_keypair_calc_public(mbedtls_ecp_keypair *key,
+                                    int (*f_rng)(void *, unsigned char *, size_t),
+                                    void *p_rng)
+{
+    return mbedtls_ecp_mul(&key->grp, &key->Q, &key->d, &key->grp.G,
+                           f_rng, p_rng);
+}
 #endif /* MBEDTLS_ECP_C */
 
+mbedtls_ecp_group_id mbedtls_ecp_keypair_get_group_id(
+    const mbedtls_ecp_keypair *key)
+{
+    return key->grp.id;
+}
+
 /*
  * Export generic key-pair parameters.
  */
@@ -3365,15 +3410,15 @@
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
 
-    if ((ret = mbedtls_ecp_group_copy(grp, &key->grp)) != 0) {
+    if (grp != NULL && (ret = mbedtls_ecp_group_copy(grp, &key->grp)) != 0) {
         return ret;
     }
 
-    if ((ret = mbedtls_mpi_copy(d, &key->d)) != 0) {
+    if (d != NULL && (ret = mbedtls_mpi_copy(d, &key->d)) != 0) {
         return ret;
     }
 
-    if ((ret = mbedtls_ecp_copy(Q, &key->Q)) != 0) {
+    if (Q != NULL && (ret = mbedtls_ecp_copy(Q, &key->Q)) != 0) {
         return ret;
     }
 
diff --git a/library/gcm.c b/library/gcm.c
index 20d55c0..c677ca4 100644
--- a/library/gcm.c
+++ b/library/gcm.c
@@ -401,12 +401,9 @@
 /* Increment the counter. */
 static void gcm_incr(unsigned char y[16])
 {
-    size_t i;
-    for (i = 16; i > 12; i--) {
-        if (++y[i - 1] != 0) {
-            break;
-        }
-    }
+    uint32_t x = MBEDTLS_GET_UINT32_BE(y, 12);
+    x++;
+    MBEDTLS_PUT_UINT32_BE(x, y, 12);
 }
 
 /* Calculate and apply the encryption mask. Process use_len bytes of data,
diff --git a/library/md_psa.h b/library/md_psa.h
index b201263..028ba24 100644
--- a/library/md_psa.h
+++ b/library/md_psa.h
@@ -15,43 +15,6 @@
 #include "mbedtls/md.h"
 #include "psa/crypto.h"
 
-/**
- * \brief           This function returns the PSA algorithm identifier
- *                  associated with the given digest type.
- *
- * \param md_type   The type of digest to search for. Must not be NONE.
- *
- * \warning         If \p md_type is \c MBEDTLS_MD_NONE, this function will
- *                  not return \c PSA_ALG_NONE, but an invalid algorithm.
- *
- * \warning         This function does not check if the algorithm is
- *                  supported, it always returns the corresponding identifier.
- *
- * \return          The PSA algorithm identifier associated with \p md_type,
- *                  regardless of whether it is supported or not.
- */
-static inline psa_algorithm_t mbedtls_md_psa_alg_from_type(mbedtls_md_type_t md_type)
-{
-    return PSA_ALG_CATEGORY_HASH | (psa_algorithm_t) md_type;
-}
-
-/**
- * \brief           This function returns the given digest type
- *                  associated with the PSA algorithm identifier.
- *
- * \param psa_alg   The PSA algorithm identifier to search for.
- *
- * \warning         This function does not check if the algorithm is
- *                  supported, it always returns the corresponding identifier.
- *
- * \return          The MD type associated with \p psa_alg,
- *                  regardless of whether it is supported or not.
- */
-static inline mbedtls_md_type_t mbedtls_md_type_from_psa_alg(psa_algorithm_t psa_alg)
-{
-    return (mbedtls_md_type_t) (psa_alg & PSA_ALG_HASH_MASK);
-}
-
 /** Convert PSA status to MD error code.
  *
  * \param status    PSA status.
diff --git a/library/pk.c b/library/pk.c
index 61ac0df..9261837 100644
--- a/library/pk.c
+++ b/library/pk.c
@@ -31,7 +31,7 @@
 
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
 #include "psa_util_internal.h"
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 #endif
 
 #include <limits.h>
diff --git a/library/pk_internal.h b/library/pk_internal.h
index 025ee8b..3d5adf8 100644
--- a/library/pk_internal.h
+++ b/library/pk_internal.h
@@ -98,13 +98,13 @@
         }
         opaque_key_type = psa_get_key_type(&opaque_attrs);
         curve = PSA_KEY_TYPE_ECC_GET_FAMILY(opaque_key_type);
-        id = mbedtls_ecc_group_of_psa(curve, psa_get_key_bits(&opaque_attrs), 0);
+        id = mbedtls_ecc_group_from_psa(curve, psa_get_key_bits(&opaque_attrs));
         psa_reset_key_attributes(&opaque_attrs);
     } else
 #endif /* MBEDTLS_USE_PSA_CRYPTO */
     {
 #if defined(MBEDTLS_PK_USE_PSA_EC_DATA)
-        id = mbedtls_ecc_group_of_psa(pk->ec_family, pk->ec_bits, 0);
+        id = mbedtls_ecc_group_from_psa(pk->ec_family, pk->ec_bits);
 #else /* MBEDTLS_PK_USE_PSA_EC_DATA */
         id = mbedtls_pk_ec_ro(*pk)->grp.id;
 #endif /* MBEDTLS_PK_USE_PSA_EC_DATA */
diff --git a/library/pk_wrap.c b/library/pk_wrap.c
index 9247945..c232650 100644
--- a/library/pk_wrap.c
+++ b/library/pk_wrap.c
@@ -13,7 +13,7 @@
 #include "pk_wrap.h"
 #include "pk_internal.h"
 #include "mbedtls/error.h"
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 
 /* Even if RSA not activated, for the sake of RSA-alt */
 #include "mbedtls/rsa.h"
diff --git a/library/pkparse.c b/library/pkparse.c
index d36fa3f..5f95545 100644
--- a/library/pkparse.c
+++ b/library/pkparse.c
@@ -250,7 +250,7 @@
     mbedtls_ecp_group_id ecp_group_id;
     int ret;
 
-    ecp_group_id = mbedtls_ecc_group_of_psa(pk->ec_family, pk->ec_bits, 0);
+    ecp_group_id = mbedtls_ecc_group_from_psa(pk->ec_family, pk->ec_bits);
 
     mbedtls_ecp_keypair_init(&ecp_key);
     ret = mbedtls_ecp_group_load(&(ecp_key.grp), ecp_group_id);
diff --git a/library/psa_crypto.c b/library/psa_crypto.c
index e4ecdd0..e6d3851 100644
--- a/library/psa_crypto.c
+++ b/library/psa_crypto.c
@@ -70,7 +70,7 @@
 #include "mbedtls/sha1.h"
 #include "mbedtls/sha256.h"
 #include "mbedtls/sha512.h"
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 
 #if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF) ||          \
     defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) ||  \
@@ -890,8 +890,9 @@
  * In case of a persistent key, the function loads the description of the key
  * into a key slot if not already done.
  *
- * On success, the returned key slot is locked. It is the responsibility of
- * the caller to unlock the key slot when it does not access it anymore.
+ * On success, the returned key slot has been registered for reading.
+ * It is the responsibility of the caller to call psa_unregister_read(slot)
+ * when they have finished reading the contents of the slot.
  */
 static psa_status_t psa_get_and_lock_key_slot_with_policy(
     mbedtls_svc_key_id_t key,
@@ -935,7 +936,7 @@
 
 error:
     *p_slot = NULL;
-    psa_unlock_key_slot(slot);
+    psa_unregister_read(slot);
 
     return status;
 }
@@ -950,8 +951,9 @@
  * psa_get_and_lock_key_slot_with_policy() when there is no opaque key support
  * for a cryptographic operation.
  *
- * On success, the returned key slot is locked. It is the responsibility of the
- * caller to unlock the key slot when it does not access it anymore.
+ * On success, the returned key slot has been registered for reading.
+ * It is the responsibility of the caller to call psa_unregister_read(slot)
+ * when they have finished reading the contents of the slot.
  */
 static psa_status_t psa_get_and_lock_transparent_key_slot_with_policy(
     mbedtls_svc_key_id_t key,
@@ -966,7 +968,7 @@
     }
 
     if (psa_key_lifetime_is_external((*p_slot)->attr.lifetime)) {
-        psa_unlock_key_slot(*p_slot);
+        psa_unregister_read(*p_slot);
         *p_slot = NULL;
         return PSA_ERROR_NOT_SUPPORTED;
     }
@@ -994,15 +996,41 @@
 
     /*
      * As the return error code may not be handled in case of multiple errors,
-     * do our best to report an unexpected lock counter. Assert with
-     * MBEDTLS_TEST_HOOK_TEST_ASSERT that the lock counter is equal to one:
+     * do our best to report an unexpected amount of registered readers or
+     * an unexpected state.
+     * Assert with MBEDTLS_TEST_HOOK_TEST_ASSERT that the slot is valid for
+     * wiping.
      * if the MBEDTLS_TEST_HOOKS configuration option is enabled and the
      * function is called as part of the execution of a test suite, the
      * execution of the test suite is stopped in error if the assertion fails.
      */
-    if (slot->lock_count != 1) {
-        MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->lock_count == 1);
-        status = PSA_ERROR_CORRUPTION_DETECTED;
+    switch (slot->state) {
+        case PSA_SLOT_FULL:
+        /* In this state psa_wipe_key_slot() must only be called if the
+         * caller is the last reader. */
+        case PSA_SLOT_PENDING_DELETION:
+            /* In this state psa_wipe_key_slot() must only be called if the
+             * caller is the last reader. */
+            if (slot->registered_readers != 1) {
+                MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->registered_readers == 1);
+                status = PSA_ERROR_CORRUPTION_DETECTED;
+            }
+            break;
+        case PSA_SLOT_FILLING:
+            /* In this state registered_readers must be 0. */
+            if (slot->registered_readers != 0) {
+                MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->registered_readers == 0);
+                status = PSA_ERROR_CORRUPTION_DETECTED;
+            }
+            break;
+        case PSA_SLOT_EMPTY:
+            /* The slot is already empty, it cannot be wiped. */
+            MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->state != PSA_SLOT_EMPTY);
+            status = PSA_ERROR_CORRUPTION_DETECTED;
+            break;
+        default:
+            /* The slot's state is invalid. */
+            status = PSA_ERROR_CORRUPTION_DETECTED;
     }
 
     /* Multipart operations may still be using the key. This is safe
@@ -1012,7 +1040,8 @@
      * key material can linger until all operations are completed. */
     /* At this point, key material and other type-specific content has
      * been wiped. Clear remaining metadata. We can call memset and not
-     * zeroize because the metadata is not particularly sensitive. */
+     * zeroize because the metadata is not particularly sensitive.
+     * This memset also sets the slot's state to PSA_SLOT_EMPTY. */
     memset(slot, 0, sizeof(*slot));
     return status;
 }
@@ -1031,28 +1060,26 @@
     }
 
     /*
-     * Get the description of the key in a key slot. In case of a persistent
-     * key, this will load the key description from persistent memory if not
-     * done yet. We cannot avoid this loading as without it we don't know if
+     * Get the description of the key in a key slot, and register to read it.
+     * In the case of a persistent key, this will load the key description
+     * from persistent memory if not done yet.
+     * We cannot avoid this loading as without it we don't know if
      * the key is operated by an SE or not and this information is needed by
-     * the current implementation.
-     */
+     * the current implementation. */
     status = psa_get_and_lock_key_slot(key, &slot);
     if (status != PSA_SUCCESS) {
         return status;
     }
 
-    /*
-     * If the key slot containing the key description is under access by the
-     * library (apart from the present access), the key cannot be destroyed
-     * yet. For the time being, just return in error. Eventually (to be
-     * implemented), the key should be destroyed when all accesses have
-     * stopped.
-     */
-    if (slot->lock_count > 1) {
-        psa_unlock_key_slot(slot);
-        return PSA_ERROR_GENERIC_ERROR;
-    }
+    /* 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
+     * the key within the slot; the last registered reader will
+     * automatically wipe the slot when they call psa_unregister_read().
+     * 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;
 
     if (PSA_KEY_LIFETIME_IS_READ_ONLY(slot->attr.lifetime)) {
         /* Refuse the destruction of a read-only key (which may or may not work
@@ -1100,6 +1127,9 @@
 
 #if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C)
     if (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
+        /* Destroy the copy of the persistent key from storage.
+         * The slot will still hold a copy of the key until the last reader
+         * unregisters. */
         status = psa_destroy_persistent_key(slot->attr.id);
         if (overall_status == PSA_SUCCESS) {
             overall_status = status;
@@ -1126,8 +1156,11 @@
 #endif /* MBEDTLS_PSA_CRYPTO_SE_C */
 
 exit:
-    status = psa_wipe_key_slot(slot);
-    /* Prioritize CORRUPTION_DETECTED from wiping over a storage error */
+    /* Unregister from reading the slot. If we are the last active reader
+     * then this will wipe the slot. */
+    status = psa_unregister_read(slot);
+    /* Prioritize CORRUPTION_DETECTED from unregistering over
+     * a storage error. */
     if (status != PSA_SUCCESS) {
         overall_status = status;
     }
@@ -1252,7 +1285,7 @@
         psa_reset_key_attributes(attributes);
     }
 
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -1348,7 +1381,7 @@
                                            slot->key.data, slot->key.bytes,
                                            data, data_size, data_length);
 
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -1462,7 +1495,7 @@
         data, data_size, data_length);
 
 exit:
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -1579,8 +1612,9 @@
  * In case of failure at any step, stop the sequence and call
  * psa_fail_key_creation().
  *
- * On success, the key slot is locked. It is the responsibility of the caller
- * to unlock the key slot when it does not access it anymore.
+ * On success, the key slot's state is PSA_SLOT_FILLING.
+ * It is the responsibility of the caller to change the slot's state to
+ * PSA_SLOT_EMPTY/FULL once key creation has finished.
  *
  * \param method            An identification of the calling function.
  * \param[in] attributes    Key attributes for the new key.
@@ -1611,7 +1645,7 @@
         return status;
     }
 
-    status = psa_get_empty_key_slot(&volatile_key_id, p_slot);
+    status = psa_reserve_free_key_slot(&volatile_key_id, p_slot);
     if (status != PSA_SUCCESS) {
         return status;
     }
@@ -1637,7 +1671,7 @@
     /* Erase external-only flags from the internal copy. To access
      * external-only flags, query `attributes`. Thanks to the check
      * in psa_validate_key_attributes(), this leaves the dual-use
-     * flags and any internal flag that psa_get_empty_key_slot()
+     * flags and any internal flag that psa_reserve_free_key_slot()
      * may have set. */
     slot->attr.flags &= ~MBEDTLS_PSA_KA_MASK_EXTERNAL_ONLY;
 
@@ -1689,8 +1723,6 @@
     }
 #endif /* MBEDTLS_PSA_CRYPTO_SE_C */
 
-    slot->status = PSA_SLOT_OCCUPIED;
-
     return PSA_SUCCESS;
 }
 
@@ -1702,9 +1734,9 @@
  * See the documentation of psa_start_key_creation() for the intended use
  * of this function.
  *
- * If the finalization succeeds, the function unlocks the key slot (it was
- * locked by psa_start_key_creation()) and the key slot cannot be accessed
- * anymore as part of the key creation process.
+ * If the finalization succeeds, the function sets the key slot's state to
+ * PSA_SLOT_FULL, and the key slot can no longer be accessed as part of the
+ * key creation process.
  *
  * \param[in,out] slot  Pointer to the slot with key material.
  * \param[in] driver    The secure element driver for the key,
@@ -1780,7 +1812,8 @@
 
     if (status == PSA_SUCCESS) {
         *key = slot->attr.id;
-        status = psa_unlock_key_slot(slot);
+        status = psa_key_slot_state_transition(slot, PSA_SLOT_FILLING,
+                                               PSA_SLOT_FULL);
         if (status != PSA_SUCCESS) {
             *key = MBEDTLS_SVC_KEY_ID_INIT;
         }
@@ -1795,7 +1828,7 @@
  * or after psa_finish_key_creation() fails. In other circumstances, this
  * function may not clean up persistent storage.
  * See the documentation of psa_start_key_creation() for the intended use
- * of this function.
+ * of this function. Sets the slot's state to PSA_SLOT_EMPTY.
  *
  * \param[in,out] slot  Pointer to the slot with key material.
  * \param[in] driver    The secure element driver for the key,
@@ -2134,7 +2167,7 @@
         psa_fail_key_creation(target_slot, driver);
     }
 
-    unlock_status = psa_unlock_key_slot(source_slot);
+    unlock_status = psa_unregister_read(source_slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -2455,7 +2488,7 @@
         psa_mac_abort(operation);
     }
 
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -2641,7 +2674,7 @@
 
     psa_wipe_tag_output_buffer(mac, status, mac_size, *mac_length);
 
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -2785,7 +2818,7 @@
     psa_wipe_tag_output_buffer(signature, status, signature_size,
                                *signature_length);
 
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -2833,7 +2866,7 @@
             signature, signature_length);
     }
 
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 
@@ -3100,7 +3133,7 @@
         alg, input, input_length, salt, salt_length,
         output, output_size, output_length);
 exit:
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -3152,7 +3185,7 @@
         output, output_size, output_length);
 
 exit:
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -3261,7 +3294,7 @@
         psa_sign_hash_abort_internal(operation);
     }
 
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
 
     if (unlock_status != PSA_SUCCESS) {
         operation->error_occurred = 1;
@@ -3406,7 +3439,7 @@
         psa_verify_hash_abort_internal(operation);
     }
 
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
 
     if (unlock_status != PSA_SUCCESS) {
         operation->error_occurred = 1;
@@ -3978,7 +4011,7 @@
         psa_cipher_abort(operation);
     }
 
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -4223,7 +4256,7 @@
         output_size - default_iv_length, output_length);
 
 exit:
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
     if (status == PSA_SUCCESS) {
         status = unlock_status;
     }
@@ -4284,7 +4317,7 @@
         output, output_size, output_length);
 
 exit:
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
     if (status == PSA_SUCCESS) {
         status = unlock_status;
     }
@@ -4410,7 +4443,7 @@
     }
 
 exit:
-    psa_unlock_key_slot(slot);
+    psa_unregister_read(slot);
 
     return status;
 }
@@ -4465,7 +4498,7 @@
     }
 
 exit:
-    psa_unlock_key_slot(slot);
+    psa_unregister_read(slot);
 
     return status;
 }
@@ -4577,7 +4610,7 @@
     operation->key_type = psa_get_key_type(&attributes);
 
 exit:
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
 
     if (status == PSA_SUCCESS) {
         status = unlock_status;
@@ -5708,7 +5741,7 @@
     psa_ecc_family_t curve = PSA_KEY_TYPE_ECC_GET_FAMILY(
         slot->attr.type);
     mbedtls_ecp_group_id grp_id =
-        mbedtls_ecc_group_of_psa(curve, bits, 0);
+        mbedtls_ecc_group_from_psa(curve, bits);
 
     if (grp_id == MBEDTLS_ECP_DP_NONE) {
         ret = MBEDTLS_ERR_ASN1_INVALID_DATA;
@@ -6900,7 +6933,7 @@
                                                slot->key.data,
                                                slot->key.bytes);
 
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -7057,7 +7090,7 @@
         }
     }
 
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -7118,7 +7151,7 @@
         *output_length = output_size;
     }
 
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -7792,7 +7825,7 @@
     if (status != PSA_SUCCESS) {
         psa_pake_abort(operation);
     }
-    unlock_status = psa_unlock_key_slot(slot);
+    unlock_status = psa_unregister_read(slot);
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
 
diff --git a/library/psa_crypto_core.h b/library/psa_crypto_core.h
index ff01add..7b16724 100644
--- a/library/psa_crypto_core.h
+++ b/library/psa_crypto_core.h
@@ -47,8 +47,10 @@
 
 typedef enum {
     PSA_SLOT_EMPTY = 0,
-    PSA_SLOT_OCCUPIED,
-} psa_key_slot_status_t;
+    PSA_SLOT_FILLING,
+    PSA_SLOT_FULL,
+    PSA_SLOT_PENDING_DELETION,
+} psa_key_slot_state_t;
 
 /** The data structure representing a key slot, containing key material
  * and metadata for one key.
@@ -56,18 +58,37 @@
 typedef struct {
     psa_core_key_attributes_t attr;
 
-    psa_key_slot_status_t status;
+    /*
+     * The current state of the key slot, as described in
+     * docs/architecture/psa-thread-safety/psa-thread-safety.md.
+     *
+     * Library functions can modify the state of a key slot by calling
+     * psa_key_slot_state_transition.
+     *
+     * The state variable is used to help determine whether library functions
+     * which operate on the slot succeed. For example, psa_finish_key_creation,
+     * which transfers the state of a slot from PSA_SLOT_FILLING to
+     * PSA_SLOT_FULL, must fail with error code PSA_ERROR_CORRUPTION_DETECTED
+     * if the state of the slot is not PSA_SLOT_FILLING.
+     *
+     * Library functions which traverse the array of key slots only consider
+     * slots that are in a suitable state for the function.
+     * For example, psa_get_and_lock_key_slot_in_memory, which finds a slot
+     * containing a given key ID, will only check slots whose state variable is
+     * PSA_SLOT_FULL. */
+    psa_key_slot_state_t state;
 
     /*
-     * Number of locks on the key slot held by the library.
+     * Number of functions registered as reading the material in the key slot.
      *
-     * This counter is incremented by one each time a library function
-     * retrieves through one of the dedicated internal API a pointer to the
-     * key slot.
+     * Library functions must not write directly to registered_readers
      *
-     * This counter is decremented by one each time a library function stops
-     * accessing the key slot and states it by calling the
-     * psa_unlock_key_slot() API.
+     * A function must call psa_register_read(slot) before reading the current
+     * contents of the slot for an operation.
+     * They then must call psa_unregister_read(slot) once they have finished
+     * reading the current contents of the slot.
+     * A function must call psa_key_slot_has_readers(slot) to check if
+     * the slot is in use for reading.
      *
      * This counter is used to prevent resetting the key slot while the library
      * may access it. For example, such control is needed in the following
@@ -78,10 +99,9 @@
      *   the library cannot be reclaimed to free a key slot to load the
      *   persistent key.
      * . In case of a multi-threaded application where one thread asks to close
-     *   or purge or destroy a key while it is in used by the library through
-     *   another thread.
-     */
-    size_t lock_count;
+     *   or purge or destroy a key while it is in use by the library through
+     *   another thread. */
+    size_t registered_readers;
 
     /* Dynamically allocated key data buffer.
      * Format as specified in psa_export_key(). */
@@ -96,31 +116,17 @@
 #define PSA_KA_MASK_INTERNAL_ONLY (     \
         0)
 
-/** Test whether a key slot is occupied.
- *
- * A key slot is occupied iff the key type is nonzero. This works because
- * no valid key can have 0 as its key type.
+/** Test whether a key slot has any registered readers.
+ * If multi-threading is enabled, the caller must hold the
+ * global key slot mutex.
  *
  * \param[in] slot      The key slot to test.
  *
- * \return 1 if the slot is occupied, 0 otherwise.
+ * \return 1 if the slot has any registered readers, 0 otherwise.
  */
-static inline int psa_is_key_slot_occupied(const psa_key_slot_t *slot)
+static inline int psa_key_slot_has_readers(const psa_key_slot_t *slot)
 {
-    return slot->status == PSA_SLOT_OCCUPIED;
-}
-
-/** Test whether a key slot is locked.
- *
- * A key slot is locked iff its lock counter is strictly greater than 0.
- *
- * \param[in] slot  The key slot to test.
- *
- * \return 1 if the slot is locked, 0 otherwise.
- */
-static inline int psa_is_key_slot_locked(const psa_key_slot_t *slot)
-{
-    return slot->lock_count > 0;
+    return slot->registered_readers > 0;
 }
 
 /** Retrieve flags from psa_key_slot_t::attr::core::flags.
@@ -190,13 +196,20 @@
 /** Completely wipe a slot in memory, including its policy.
  *
  * Persistent storage is not affected.
+ * Sets the slot's state to PSA_SLOT_EMPTY.
+ * If multi-threading is enabled, the caller must hold the
+ * global key slot mutex.
  *
  * \param[in,out] slot  The key slot to wipe.
  *
  * \retval #PSA_SUCCESS
- *         Success. This includes the case of a key slot that was
- *         already fully wiped.
- * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ *         The slot has been successfully wiped.
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED
+ *         The slot's state was PSA_SLOT_FULL or PSA_SLOT_PENDING_DELETION, and
+ *         the amount of registered readers was not equal to 1. Or,
+ *         the slot's state was PSA_SLOT_EMPTY. Or,
+ *         the slot's state was PSA_SLOT_FILLING, and the amount
+ *         of registered readers was not equal to 0.
  */
 psa_status_t psa_wipe_key_slot(psa_key_slot_t *slot);
 
diff --git a/library/psa_crypto_ecp.c b/library/psa_crypto_ecp.c
index e4a372d..db08541 100644
--- a/library/psa_crypto_ecp.c
+++ b/library/psa_crypto_ecp.c
@@ -14,7 +14,7 @@
 #include "psa_crypto_core.h"
 #include "psa_crypto_ecp.h"
 #include "psa_crypto_random_impl.h"
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 
 #include <stdlib.h>
 #include <string.h>
@@ -32,6 +32,61 @@
     defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
     defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) || \
     defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH)
+/* Helper function to verify if the provided EC's family and key bit size are valid.
+ *
+ * Note: "bits" parameter is used both as input and output and it might be updated
+ *       in case provided input value is not multiple of 8 ("sloppy" bits).
+ */
+static int check_ecc_parameters(psa_ecc_family_t family, size_t *bits)
+{
+    switch (family) {
+        case PSA_ECC_FAMILY_SECP_R1:
+            switch (*bits) {
+                case 192:
+                case 224:
+                case 256:
+                case 384:
+                case 521:
+                    return PSA_SUCCESS;
+                case 528:
+                    *bits = 521;
+                    return PSA_SUCCESS;
+            }
+            break;
+
+        case PSA_ECC_FAMILY_BRAINPOOL_P_R1:
+            switch (*bits) {
+                case 256:
+                case 384:
+                case 512:
+                    return PSA_SUCCESS;
+            }
+            break;
+
+        case PSA_ECC_FAMILY_MONTGOMERY:
+            switch (*bits) {
+                case 448:
+                case 255:
+                    return PSA_SUCCESS;
+                case 256:
+                    *bits = 255;
+                    return PSA_SUCCESS;
+            }
+            break;
+
+        case PSA_ECC_FAMILY_SECP_K1:
+            switch (*bits) {
+                case 192:
+                /* secp224k1 is not and will not be supported in PSA (#3541). */
+                case 256:
+                    return PSA_SUCCESS;
+            }
+            break;
+    }
+
+    return PSA_ERROR_INVALID_ARGUMENT;
+}
+
 psa_status_t mbedtls_psa_ecp_load_representation(
     psa_key_type_t type, size_t curve_bits,
     const uint8_t *data, size_t data_length,
@@ -82,16 +137,15 @@
     }
     mbedtls_ecp_keypair_init(ecp);
 
+    status = check_ecc_parameters(PSA_KEY_TYPE_ECC_GET_FAMILY(type), &curve_bits);
+    if (status != PSA_SUCCESS) {
+        goto exit;
+    }
+
     /* Load the group. */
-    grp_id = mbedtls_ecc_group_of_psa(PSA_KEY_TYPE_ECC_GET_FAMILY(type),
-                                      curve_bits, !explicit_bits);
+    grp_id = mbedtls_ecc_group_from_psa(PSA_KEY_TYPE_ECC_GET_FAMILY(type),
+                                        curve_bits);
     if (grp_id == MBEDTLS_ECP_DP_NONE) {
-        /* We can't distinguish between a nonsensical family/size combination
-         * (which would warrant PSA_ERROR_INVALID_ARGUMENT) and a
-         * well-regarded curve that Mbed TLS just doesn't know about (which
-         * would warrant PSA_ERROR_NOT_SUPPORTED). For uniformity with how
-         * curves that Mbed TLS knows about but for which support is disabled
-         * at build time, return NOT_SUPPORTED. */
         status = PSA_ERROR_NOT_SUPPORTED;
         goto exit;
     }
@@ -285,7 +339,7 @@
     psa_ecc_family_t curve = PSA_KEY_TYPE_ECC_GET_FAMILY(
         attributes->core.type);
     mbedtls_ecp_group_id grp_id =
-        mbedtls_ecc_group_of_psa(curve, attributes->core.bits, 0);
+        mbedtls_ecc_group_from_psa(curve, attributes->core.bits);
 
     const mbedtls_ecp_curve_info *curve_info =
         mbedtls_ecp_curve_info_from_grp_id(grp_id);
diff --git a/library/psa_crypto_rsa.c b/library/psa_crypto_rsa.c
index 0679f41..7b58ea2 100644
--- a/library/psa_crypto_rsa.c
+++ b/library/psa_crypto_rsa.c
@@ -16,7 +16,7 @@
 #include "psa_crypto_random_impl.h"
 #include "psa_crypto_rsa.h"
 #include "psa_crypto_hash.h"
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 
 #include <stdlib.h>
 #include <string.h>
diff --git a/library/psa_crypto_slot_management.c b/library/psa_crypto_slot_management.c
index 5ecc3a7..47ace35 100644
--- a/library/psa_crypto_slot_management.c
+++ b/library/psa_crypto_slot_management.c
@@ -23,6 +23,9 @@
 #include <stdlib.h>
 #include <string.h>
 #include "mbedtls/platform.h"
+#if defined(MBEDTLS_THREADING_C)
+#include "mbedtls/threading.h"
+#endif
 
 typedef struct {
     psa_key_slot_t key_slots[MBEDTLS_PSA_KEY_SLOT_COUNT];
@@ -108,7 +111,9 @@
 
         for (slot_idx = 0; slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT; slot_idx++) {
             slot = &global_data.key_slots[slot_idx];
-            if (mbedtls_svc_key_id_equal(key, slot->attr.id)) {
+            /* Only consider slots which are in a full state. */
+            if ((slot->state == PSA_SLOT_FULL) &&
+                (mbedtls_svc_key_id_equal(key, slot->attr.id))) {
                 break;
             }
         }
@@ -117,7 +122,7 @@
     }
 
     if (status == PSA_SUCCESS) {
-        status = psa_lock_key_slot(slot);
+        status = psa_register_read(slot);
         if (status == PSA_SUCCESS) {
             *p_slot = slot;
         }
@@ -141,36 +146,38 @@
 
     for (slot_idx = 0; slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT; slot_idx++) {
         psa_key_slot_t *slot = &global_data.key_slots[slot_idx];
-        slot->lock_count = 1;
+        slot->registered_readers = 1;
+        slot->state = PSA_SLOT_PENDING_DELETION;
         (void) psa_wipe_key_slot(slot);
     }
     global_data.key_slots_initialized = 0;
 }
 
-psa_status_t psa_get_empty_key_slot(psa_key_id_t *volatile_key_id,
-                                    psa_key_slot_t **p_slot)
+psa_status_t psa_reserve_free_key_slot(psa_key_id_t *volatile_key_id,
+                                       psa_key_slot_t **p_slot)
 {
     psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
     size_t slot_idx;
-    psa_key_slot_t *selected_slot, *unlocked_persistent_key_slot;
+    psa_key_slot_t *selected_slot, *unused_persistent_key_slot;
 
     if (!global_data.key_slots_initialized) {
         status = PSA_ERROR_BAD_STATE;
         goto error;
     }
 
-    selected_slot = unlocked_persistent_key_slot = NULL;
+    selected_slot = unused_persistent_key_slot = NULL;
     for (slot_idx = 0; slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT; slot_idx++) {
         psa_key_slot_t *slot = &global_data.key_slots[slot_idx];
-        if (!psa_is_key_slot_occupied(slot)) {
+        if (slot->state == PSA_SLOT_EMPTY) {
             selected_slot = slot;
             break;
         }
 
-        if ((unlocked_persistent_key_slot == NULL) &&
-            (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) &&
-            (!psa_is_key_slot_locked(slot))) {
-            unlocked_persistent_key_slot = slot;
+        if ((unused_persistent_key_slot == NULL) &&
+            (slot->state == PSA_SLOT_FULL) &&
+            (!psa_key_slot_has_readers(slot)) &&
+            (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime))) {
+            unused_persistent_key_slot = slot;
         }
     }
 
@@ -182,14 +189,18 @@
      * storage.
      */
     if ((selected_slot == NULL) &&
-        (unlocked_persistent_key_slot != NULL)) {
-        selected_slot = unlocked_persistent_key_slot;
-        selected_slot->lock_count = 1;
-        psa_wipe_key_slot(selected_slot);
+        (unused_persistent_key_slot != NULL)) {
+        selected_slot = unused_persistent_key_slot;
+        psa_register_read(selected_slot);
+        status = psa_wipe_key_slot(selected_slot);
+        if (status != PSA_SUCCESS) {
+            goto error;
+        }
     }
 
     if (selected_slot != NULL) {
-        status = psa_lock_key_slot(selected_slot);
+        status = psa_key_slot_state_transition(selected_slot, PSA_SLOT_EMPTY,
+                                               PSA_SLOT_FILLING);
         if (status != PSA_SUCCESS) {
             goto error;
         }
@@ -239,7 +250,8 @@
             slot, data->slot_number, sizeof(data->slot_number));
 
         if (status == PSA_SUCCESS) {
-            slot->status = PSA_SLOT_OCCUPIED;
+            status = psa_key_slot_state_transition(slot, PSA_SLOT_FILLING,
+                                                   PSA_SLOT_FULL);
         }
         goto exit;
     }
@@ -250,7 +262,8 @@
         goto exit;
     }
 
-    slot->status = PSA_SLOT_OCCUPIED;
+    status = psa_key_slot_state_transition(slot, PSA_SLOT_FILLING,
+                                           PSA_SLOT_FULL);
 
 exit:
     psa_free_persistent_key_data(key_data, key_data_length);
@@ -324,8 +337,9 @@
     /* Copy actual key length and core attributes into the slot on success */
     slot->key.bytes = key_buffer_length;
     slot->attr = attributes.core;
-    slot->status = PSA_SLOT_OCCUPIED;
 
+    status = psa_key_slot_state_transition(slot, PSA_SLOT_FILLING,
+                                           PSA_SLOT_FULL);
 exit:
     if (status != PSA_SUCCESS) {
         psa_remove_key_data_from_memory(slot);
@@ -358,7 +372,7 @@
     defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS)
     psa_key_id_t volatile_key_id;
 
-    status = psa_get_empty_key_slot(&volatile_key_id, p_slot);
+    status = psa_reserve_free_key_slot(&volatile_key_id, p_slot);
     if (status != PSA_SUCCESS) {
         return status;
     }
@@ -380,12 +394,17 @@
 
     if (status != PSA_SUCCESS) {
         psa_wipe_key_slot(*p_slot);
+
         if (status == PSA_ERROR_DOES_NOT_EXIST) {
             status = PSA_ERROR_INVALID_HANDLE;
         }
     } else {
         /* Add implicit usage flags. */
         psa_extend_key_usage_flags(&(*p_slot)->attr.policy.usage);
+
+        psa_key_slot_state_transition((*p_slot), PSA_SLOT_FILLING,
+                                      PSA_SLOT_FULL);
+        status = psa_register_read(*p_slot);
     }
 
     return status;
@@ -394,26 +413,37 @@
 #endif /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
 }
 
-psa_status_t psa_unlock_key_slot(psa_key_slot_t *slot)
+psa_status_t psa_unregister_read(psa_key_slot_t *slot)
 {
     if (slot == NULL) {
         return PSA_SUCCESS;
     }
+    if ((slot->state != PSA_SLOT_FULL) &&
+        (slot->state != PSA_SLOT_PENDING_DELETION)) {
+        return PSA_ERROR_CORRUPTION_DETECTED;
+    }
 
-    if (slot->lock_count > 0) {
-        slot->lock_count--;
+    /* If we are the last reader and the slot is marked for deletion,
+     * we must wipe the slot here. */
+    if ((slot->state == PSA_SLOT_PENDING_DELETION) &&
+        (slot->registered_readers == 1)) {
+        return psa_wipe_key_slot(slot);
+    }
+
+    if (psa_key_slot_has_readers(slot)) {
+        slot->registered_readers--;
         return PSA_SUCCESS;
     }
 
     /*
      * As the return error code may not be handled in case of multiple errors,
-     * do our best to report if the lock counter is equal to zero. Assert with
-     * MBEDTLS_TEST_HOOK_TEST_ASSERT that the lock counter is strictly greater
-     * than zero: if the MBEDTLS_TEST_HOOKS configuration option is enabled and
+     * do our best to report if there are no registered readers. Assert with
+     * MBEDTLS_TEST_HOOK_TEST_ASSERT that there are registered readers:
+     * if the MBEDTLS_TEST_HOOKS configuration option is enabled and
      * the function is called as part of the execution of a test suite, the
      * execution of the test suite is stopped in error if the assertion fails.
      */
-    MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->lock_count > 0);
+    MBEDTLS_TEST_HOOK_TEST_ASSERT(psa_key_slot_has_readers(slot));
     return PSA_ERROR_CORRUPTION_DETECTED;
 }
 
@@ -480,7 +510,7 @@
 
     *handle = key;
 
-    return psa_unlock_key_slot(slot);
+    return psa_unregister_read(slot);
 
 #else /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
     (void) key;
@@ -506,10 +536,10 @@
 
         return status;
     }
-    if (slot->lock_count <= 1) {
+    if (slot->registered_readers == 1) {
         return psa_wipe_key_slot(slot);
     } else {
-        return psa_unlock_key_slot(slot);
+        return psa_unregister_read(slot);
     }
 }
 
@@ -524,10 +554,10 @@
     }
 
     if ((!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) &&
-        (slot->lock_count <= 1)) {
+        (slot->registered_readers == 1)) {
         return psa_wipe_key_slot(slot);
     } else {
-        return psa_unlock_key_slot(slot);
+        return psa_unregister_read(slot);
     }
 }
 
@@ -539,10 +569,10 @@
 
     for (slot_idx = 0; slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT; slot_idx++) {
         const psa_key_slot_t *slot = &global_data.key_slots[slot_idx];
-        if (psa_is_key_slot_locked(slot)) {
+        if (psa_key_slot_has_readers(slot)) {
             ++stats->locked_slots;
         }
-        if (!psa_is_key_slot_occupied(slot)) {
+        if (slot->state == PSA_SLOT_EMPTY) {
             ++stats->empty_slots;
             continue;
         }
diff --git a/library/psa_crypto_slot_management.h b/library/psa_crypto_slot_management.h
index 6041a35..002429b 100644
--- a/library/psa_crypto_slot_management.h
+++ b/library/psa_crypto_slot_management.h
@@ -54,8 +54,9 @@
  * In case of a persistent key, the function loads the description of the key
  * into a key slot if not already done.
  *
- * On success, the returned key slot is locked. It is the responsibility of
- * the caller to unlock the key slot when it does not access it anymore.
+ * On success, the returned key slot has been registered for reading.
+ * It is the responsibility of the caller to call psa_unregister_read(slot)
+ * when they have finished reading the contents of the slot.
  *
  * \param key           Key identifier to query.
  * \param[out] p_slot   On success, `*p_slot` contains a pointer to the
@@ -95,50 +96,92 @@
  * This does not affect persistent storage. */
 void psa_wipe_all_key_slots(void);
 
-/** Find a free key slot.
+/** Find a free key slot and reserve it to be filled with a key.
  *
- * This function returns a key slot that is available for use and is in its
- * ground state (all-bits-zero). On success, the key slot is locked. It is
- * the responsibility of the caller to unlock the key slot when it does not
- * access it anymore.
+ * This function finds a key slot that is free,
+ * sets its state to PSA_SLOT_FILLING and then returns the slot.
+ *
+ * On success, the key slot's state is PSA_SLOT_FILLING.
+ * It is the responsibility of the caller to change the slot's state to
+ * PSA_SLOT_EMPTY/FULL once key creation has finished.
  *
  * \param[out] volatile_key_id   On success, volatile key identifier
  *                               associated to the returned slot.
  * \param[out] p_slot            On success, a pointer to the slot.
  *
  * \retval #PSA_SUCCESS \emptydescription
- * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
+ *         There were no free key slots.
  * \retval #PSA_ERROR_BAD_STATE \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED
+ *         This function attempted to operate on a key slot which was in an
+ *         unexpected state.
  */
-psa_status_t psa_get_empty_key_slot(psa_key_id_t *volatile_key_id,
-                                    psa_key_slot_t **p_slot);
+psa_status_t psa_reserve_free_key_slot(psa_key_id_t *volatile_key_id,
+                                       psa_key_slot_t **p_slot);
 
-/** Lock a key slot.
+/** Change the state of a key slot.
  *
- * This function increments the key slot lock counter by one.
+ * This function changes the state of the key slot from expected_state to
+ * new state. If the state of the slot was not expected_state, the state is
+ * unchanged.
+ *
+ * If multi-threading is enabled, the caller must hold the
+ * global key slot mutex.
+ *
+ * \param[in] slot            The key slot.
+ * \param[in] expected_state  The current state of the slot.
+ * \param[in] new_state       The new state of the slot.
+ *
+ * \retval #PSA_SUCCESS
+               The key slot's state variable is new_state.
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED
+ *             The slot's state was not expected_state.
+ */
+static inline psa_status_t psa_key_slot_state_transition(
+    psa_key_slot_t *slot, psa_key_slot_state_t expected_state,
+    psa_key_slot_state_t new_state)
+{
+    if (slot->state != expected_state) {
+        return PSA_ERROR_CORRUPTION_DETECTED;
+    }
+    slot->state = new_state;
+    return PSA_SUCCESS;
+}
+
+/** Register as a reader of a key slot.
+ *
+ * This function increments the key slot registered reader counter by one.
+ * If multi-threading is enabled, the caller must hold the
+ * global key slot mutex.
  *
  * \param[in] slot  The key slot.
  *
  * \retval #PSA_SUCCESS
-               The key slot lock counter was incremented.
+               The key slot registered reader counter was incremented.
  * \retval #PSA_ERROR_CORRUPTION_DETECTED
- *             The lock counter already reached its maximum value and was not
- *             increased.
+ *             The reader counter already reached its maximum value and was not
+ *             increased, or the slot's state was not PSA_SLOT_FULL.
  */
-static inline psa_status_t psa_lock_key_slot(psa_key_slot_t *slot)
+static inline psa_status_t psa_register_read(psa_key_slot_t *slot)
 {
-    if (slot->lock_count >= SIZE_MAX) {
+    if ((slot->state != PSA_SLOT_FULL) ||
+        (slot->registered_readers >= SIZE_MAX)) {
         return PSA_ERROR_CORRUPTION_DETECTED;
     }
-
-    slot->lock_count++;
+    slot->registered_readers++;
 
     return PSA_SUCCESS;
 }
 
-/** Unlock a key slot.
+/** Unregister from reading a key slot.
  *
- * This function decrements the key slot lock counter by one.
+ * This function decrements the key slot registered reader counter by one.
+ * If the state of the slot is PSA_SLOT_PENDING_DELETION,
+ * and there is only one registered reader (the caller),
+ * this function will call psa_wipe_key_slot().
+ * If multi-threading is enabled, the caller must hold the
+ * global key slot mutex.
  *
  * \note To ease the handling of errors in retrieving a key slot
  *       a NULL input pointer is valid, and the function returns
@@ -146,13 +189,16 @@
  *
  * \param[in] slot  The key slot.
  * \retval #PSA_SUCCESS
- *             \p slot is NULL or the key slot lock counter has been
- *             decremented successfully.
+ *             \p slot is NULL or the key slot reader counter has been
+ *             decremented (and potentially wiped) successfully.
  * \retval #PSA_ERROR_CORRUPTION_DETECTED
- *             The lock counter was equal to 0.
- *
+ *             The slot's state was neither PSA_SLOT_FULL nor
+ *             PSA_SLOT_PENDING_DELETION.
+ *             Or a wipe was attempted and the slot's state was not
+ *             PSA_SLOT_PENDING_DELETION.
+ *             Or registered_readers was equal to 0.
  */
-psa_status_t psa_unlock_key_slot(psa_key_slot_t *slot);
+psa_status_t psa_unregister_read(psa_key_slot_t *slot);
 
 /** Test whether a lifetime designates a key in an external cryptoprocessor.
  *
diff --git a/library/psa_util.c b/library/psa_util.c
index 36b7175..41586e2 100644
--- a/library/psa_util.c
+++ b/library/psa_util.c
@@ -232,9 +232,7 @@
             return PSA_ECC_FAMILY_SECP_K1;
 #endif
 #if defined(MBEDTLS_ECP_HAVE_SECP224K1)
-        case MBEDTLS_ECP_DP_SECP224K1:
-            *bits = 224;
-            return PSA_ECC_FAMILY_SECP_K1;
+    /* secp224k1 is not and will not be supported in PSA (#3541). */
 #endif
 #if defined(MBEDTLS_ECP_HAVE_SECP256K1)
         case MBEDTLS_ECP_DP_SECP256K1:
@@ -252,11 +250,10 @@
     }
 }
 
-mbedtls_ecp_group_id mbedtls_ecc_group_of_psa(psa_ecc_family_t curve,
-                                              size_t bits,
-                                              int bits_is_sloppy)
+mbedtls_ecp_group_id mbedtls_ecc_group_from_psa(psa_ecc_family_t family,
+                                                size_t bits)
 {
-    switch (curve) {
+    switch (family) {
         case PSA_ECC_FAMILY_SECP_R1:
             switch (bits) {
 #if defined(PSA_WANT_ECC_SECP_R1_192)
@@ -278,11 +275,6 @@
 #if defined(PSA_WANT_ECC_SECP_R1_521)
                 case 521:
                     return MBEDTLS_ECP_DP_SECP521R1;
-                case 528:
-                    if (bits_is_sloppy) {
-                        return MBEDTLS_ECP_DP_SECP521R1;
-                    }
-                    break;
 #endif
             }
             break;
@@ -309,11 +301,6 @@
 #if defined(PSA_WANT_ECC_MONTGOMERY_255)
                 case 255:
                     return MBEDTLS_ECP_DP_CURVE25519;
-                case 256:
-                    if (bits_is_sloppy) {
-                        return MBEDTLS_ECP_DP_CURVE25519;
-                    }
-                    break;
 #endif
 #if defined(PSA_WANT_ECC_MONTGOMERY_448)
                 case 448:
@@ -329,8 +316,7 @@
                     return MBEDTLS_ECP_DP_SECP192K1;
 #endif
 #if defined(PSA_WANT_ECC_SECP_K1_224)
-                case 224:
-                    return MBEDTLS_ECP_DP_SECP224K1;
+            /* secp224k1 is not and will not be supported in PSA (#3541). */
 #endif
 #if defined(PSA_WANT_ECC_SECP_K1_256)
                 case 256:
@@ -340,7 +326,6 @@
             break;
     }
 
-    (void) bits_is_sloppy;
     return MBEDTLS_ECP_DP_NONE;
 }
 #endif /* PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY */
diff --git a/library/ssl_ciphersuites.c b/library/ssl_ciphersuites.c
index 6224ef2..23619a2 100644
--- a/library/ssl_ciphersuites.c
+++ b/library/ssl_ciphersuites.c
@@ -17,7 +17,7 @@
 #include "mbedtls/ssl.h"
 #include "ssl_misc.h"
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 #endif
 
 #include <string.h>
diff --git a/library/ssl_cookie.c b/library/ssl_cookie.c
index ee81eb4..2772cac 100644
--- a/library/ssl_cookie.c
+++ b/library/ssl_cookie.c
@@ -24,7 +24,7 @@
 #include <string.h>
 
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 /* Define a local translating function to save code size by not using too many
  * arguments in each translating place. */
 static int local_err_translation(psa_status_t status)
diff --git a/library/ssl_misc.h b/library/ssl_misc.h
index a0724e0..96afe76 100644
--- a/library/ssl_misc.h
+++ b/library/ssl_misc.h
@@ -2704,12 +2704,18 @@
 
 #if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT)
 #define MBEDTLS_SSL_RECORD_SIZE_LIMIT_EXTENSION_DATA_LENGTH (2)
-#define MBEDTLS_SSL_RECORD_SIZE_LIMIT_MIN (64)
+#define MBEDTLS_SSL_RECORD_SIZE_LIMIT_MIN (64)      /* As defined in RFC 8449 */
 
 MBEDTLS_CHECK_RETURN_CRITICAL
 int mbedtls_ssl_tls13_parse_record_size_limit_ext(mbedtls_ssl_context *ssl,
                                                   const unsigned char *buf,
                                                   const unsigned char *end);
+
+MBEDTLS_CHECK_RETURN_CRITICAL
+int mbedtls_ssl_tls13_write_record_size_limit_ext(mbedtls_ssl_context *ssl,
+                                                  unsigned char *buf,
+                                                  const unsigned char *end,
+                                                  size_t *out_len);
 #endif /* MBEDTLS_SSL_RECORD_SIZE_LIMIT */
 
 #if defined(MBEDTLS_SSL_ALPN)
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 444df76..0bc18f1 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -29,6 +29,7 @@
 #include <string.h>
 
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
+#include "mbedtls/psa_util.h"
 #include "md_psa.h"
 #include "psa_util_internal.h"
 #include "psa/crypto.h"
@@ -3521,15 +3522,15 @@
 
     if (ssl->transform_out != NULL &&
         ssl->transform_out->tls_version == MBEDTLS_SSL_VERSION_TLS1_3) {
-        /* RFC 8449, section 4:
-         *
-         * This value [record_size_limit] is the length of the plaintext
-         * of a protected record.
-         * The value includes the content type and padding added in TLS 1.3
-         * (that is, the complete length of TLSInnerPlaintext).
-         *
-         * Thus, round down to a multiple of MBEDTLS_SSL_CID_TLS1_3_PADDING_GRANULARITY
-         * and subtract 1 (for the content type that will be added later)
+        /*
+         * In TLS 1.3 case, when records are protected, `max_len` as computed
+         * above is the maximum length of the TLSInnerPlaintext structure that
+         * along the plaintext payload contains the inner content type (one byte)
+         * and some zero padding. Given the algorithm used for padding
+         * in mbedtls_ssl_encrypt_buf(), compute the maximum length for
+         * the plaintext payload. Round down to a multiple of
+         * MBEDTLS_SSL_CID_TLS1_3_PADDING_GRANULARITY and
+         * subtract 1.
          */
         max_len = ((max_len / MBEDTLS_SSL_CID_TLS1_3_PADDING_GRANULARITY) *
                    MBEDTLS_SSL_CID_TLS1_3_PADDING_GRANULARITY) - 1;
@@ -5118,23 +5119,17 @@
     // == MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_ECDSA, MBEDTLS_SSL_HASH_SHA512)
 #endif
 
-#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) && \
-    defined(MBEDTLS_MD_CAN_SHA512)
+#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) && defined(MBEDTLS_MD_CAN_SHA512)
     MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA512,
-#endif \
-    /* MBEDTLS_X509_RSASSA_PSS_SUPPORT && MBEDTLS_MD_CAN_SHA512 */
+#endif
 
-#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) && \
-    defined(MBEDTLS_MD_CAN_SHA384)
+#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) && defined(MBEDTLS_MD_CAN_SHA384)
     MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA384,
-#endif \
-    /* MBEDTLS_X509_RSASSA_PSS_SUPPORT && MBEDTLS_MD_CAN_SHA384 */
+#endif
 
-#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) && \
-    defined(MBEDTLS_MD_CAN_SHA256)
+#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) && defined(MBEDTLS_MD_CAN_SHA256)
     MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA256,
-#endif \
-    /* MBEDTLS_X509_RSASSA_PSS_SUPPORT && MBEDTLS_MD_CAN_SHA256 */
+#endif
 
 #if defined(MBEDTLS_RSA_C) && defined(MBEDTLS_MD_CAN_SHA512)
     MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA512,
@@ -5154,42 +5149,47 @@
 /* NOTICE: see above */
 #if defined(MBEDTLS_SSL_PROTO_TLS1_2)
 static uint16_t ssl_tls12_preset_default_sig_algs[] = {
+
 #if defined(MBEDTLS_MD_CAN_SHA512)
 #if defined(MBEDTLS_KEY_EXCHANGE_ECDSA_CERT_REQ_ALLOWED_ENABLED)
     MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_ECDSA, MBEDTLS_SSL_HASH_SHA512),
 #endif
 #if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT)
     MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA512,
-#endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */
+#endif
 #if defined(MBEDTLS_RSA_C)
     MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_RSA, MBEDTLS_SSL_HASH_SHA512),
 #endif
-#endif /* MBEDTLS_MD_CAN_SHA512*/
+#endif /* MBEDTLS_MD_CAN_SHA512 */
+
 #if defined(MBEDTLS_MD_CAN_SHA384)
 #if defined(MBEDTLS_KEY_EXCHANGE_ECDSA_CERT_REQ_ALLOWED_ENABLED)
     MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_ECDSA, MBEDTLS_SSL_HASH_SHA384),
 #endif
 #if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT)
     MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA384,
-#endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */
+#endif
 #if defined(MBEDTLS_RSA_C)
     MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_RSA, MBEDTLS_SSL_HASH_SHA384),
 #endif
-#endif /* MBEDTLS_MD_CAN_SHA384*/
+#endif /* MBEDTLS_MD_CAN_SHA384 */
+
 #if defined(MBEDTLS_MD_CAN_SHA256)
 #if defined(MBEDTLS_KEY_EXCHANGE_ECDSA_CERT_REQ_ALLOWED_ENABLED)
     MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_ECDSA, MBEDTLS_SSL_HASH_SHA256),
 #endif
 #if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT)
     MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA256,
-#endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */
+#endif
 #if defined(MBEDTLS_RSA_C)
     MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_RSA, MBEDTLS_SSL_HASH_SHA256),
 #endif
-#endif /* MBEDTLS_MD_CAN_SHA256*/
+#endif /* MBEDTLS_MD_CAN_SHA256 */
+
     MBEDTLS_TLS_SIG_NONE
 };
 #endif /* MBEDTLS_SSL_PROTO_TLS1_2 */
+
 /* NOTICE: see above */
 static uint16_t ssl_preset_suiteb_sig_algs[] = {
 
@@ -5207,38 +5207,25 @@
     // == MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_ECDSA, MBEDTLS_SSL_HASH_SHA384)
 #endif
 
-#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) && \
-    defined(MBEDTLS_MD_CAN_SHA256)
-    MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA256,
-#endif \
-    /* MBEDTLS_X509_RSASSA_PSS_SUPPORT && MBEDTLS_MD_CAN_SHA256*/
-
-#if defined(MBEDTLS_RSA_C) && defined(MBEDTLS_MD_CAN_SHA256)
-    MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA256,
-#endif /* MBEDTLS_RSA_C && MBEDTLS_MD_CAN_SHA256*/
-
     MBEDTLS_TLS_SIG_NONE
 };
 
 /* NOTICE: see above */
 #if defined(MBEDTLS_SSL_PROTO_TLS1_2)
 static uint16_t ssl_tls12_preset_suiteb_sig_algs[] = {
+
 #if defined(MBEDTLS_MD_CAN_SHA256)
 #if defined(MBEDTLS_KEY_EXCHANGE_ECDSA_CERT_REQ_ALLOWED_ENABLED)
     MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_ECDSA, MBEDTLS_SSL_HASH_SHA256),
 #endif
-#if defined(MBEDTLS_RSA_C)
-    MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_RSA, MBEDTLS_SSL_HASH_SHA256),
-#endif
-#endif /* MBEDTLS_MD_CAN_SHA256*/
+#endif /* MBEDTLS_MD_CAN_SHA256 */
+
 #if defined(MBEDTLS_MD_CAN_SHA384)
 #if defined(MBEDTLS_KEY_EXCHANGE_ECDSA_CERT_REQ_ALLOWED_ENABLED)
     MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_ECDSA, MBEDTLS_SSL_HASH_SHA384),
 #endif
-#if defined(MBEDTLS_RSA_C)
-    MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_RSA, MBEDTLS_SSL_HASH_SHA384),
-#endif
-#endif /* MBEDTLS_MD_CAN_SHA256*/
+#endif /* MBEDTLS_MD_CAN_SHA384 */
+
     MBEDTLS_TLS_SIG_NONE
 };
 #endif /* MBEDTLS_SSL_PROTO_TLS1_2 */
diff --git a/library/ssl_tls13_client.c b/library/ssl_tls13_client.c
index dac6bfc..a3d33a3 100644
--- a/library/ssl_tls13_client.c
+++ b/library/ssl_tls13_client.c
@@ -19,7 +19,7 @@
 #include "ssl_client.h"
 #include "ssl_tls13_keys.h"
 #include "ssl_debug_helpers.h"
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 
 #if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_EPHEMERAL_ENABLED)
 /* Define a local translating function to save code size by not using too many
@@ -1160,6 +1160,15 @@
     }
     p += ext_len;
 
+#if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT)
+    ret = mbedtls_ssl_tls13_write_record_size_limit_ext(
+        ssl, p, end, &ext_len);
+    if (ret != 0) {
+        return ret;
+    }
+    p += ext_len;
+#endif
+
 #if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_EPHEMERAL_ENABLED)
     if (mbedtls_ssl_conf_tls13_is_some_ephemeral_enabled(ssl)) {
         ret = ssl_tls13_write_key_share_ext(ssl, p, end, &ext_len);
diff --git a/library/ssl_tls13_generic.c b/library/ssl_tls13_generic.c
index ecfaf8a..04ecd8b 100644
--- a/library/ssl_tls13_generic.c
+++ b/library/ssl_tls13_generic.c
@@ -17,7 +17,7 @@
 #include "mbedtls/platform.h"
 #include "mbedtls/constant_time.h"
 #include "psa/crypto.h"
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 
 #include "ssl_misc.h"
 #include "ssl_tls13_invasive.h"
@@ -1698,6 +1698,7 @@
 }
 
 #if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT)
+
 /* RFC 8449, section 4:
  *
  * The ExtensionData of the "record_size_limit" extension is
@@ -1738,6 +1739,8 @@
      * as a fatal error and generate an "illegal_parameter" alert.
      */
     if (record_size_limit < MBEDTLS_SSL_RECORD_SIZE_LIMIT_MIN) {
+        MBEDTLS_SSL_DEBUG_MSG(1, ("Invalid record size limit : %u Bytes",
+                                  record_size_limit));
         MBEDTLS_SSL_PEND_FATAL_ALERT(
             MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER,
             MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER);
@@ -1749,6 +1752,36 @@
     return 0;
 }
 
+MBEDTLS_CHECK_RETURN_CRITICAL
+int mbedtls_ssl_tls13_write_record_size_limit_ext(mbedtls_ssl_context *ssl,
+                                                  unsigned char *buf,
+                                                  const unsigned char *end,
+                                                  size_t *out_len)
+{
+    unsigned char *p = buf;
+    *out_len = 0;
+
+    MBEDTLS_STATIC_ASSERT(MBEDTLS_SSL_IN_CONTENT_LEN >= MBEDTLS_SSL_RECORD_SIZE_LIMIT_MIN,
+                          "MBEDTLS_SSL_IN_CONTENT_LEN is less than the "
+                          "minimum record size limit");
+
+    MBEDTLS_SSL_CHK_BUF_PTR(p, end, 6);
+
+    MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_RECORD_SIZE_LIMIT, p, 0);
+    MBEDTLS_PUT_UINT16_BE(MBEDTLS_SSL_RECORD_SIZE_LIMIT_EXTENSION_DATA_LENGTH,
+                          p, 2);
+    MBEDTLS_PUT_UINT16_BE(MBEDTLS_SSL_IN_CONTENT_LEN, p, 4);
+
+    *out_len = 6;
+
+    MBEDTLS_SSL_DEBUG_MSG(2, ("Sent RecordSizeLimit: %d Bytes",
+                              MBEDTLS_SSL_IN_CONTENT_LEN));
+
+    mbedtls_ssl_tls13_set_hs_sent_ext_mask(ssl, MBEDTLS_TLS_EXT_RECORD_SIZE_LIMIT);
+
+    return 0;
+}
+
 #endif /* MBEDTLS_SSL_RECORD_SIZE_LIMIT */
 
 #endif /* MBEDTLS_SSL_TLS_C && MBEDTLS_SSL_PROTO_TLS1_3 */
diff --git a/library/ssl_tls13_keys.c b/library/ssl_tls13_keys.c
index 9b775ec..edb453c 100644
--- a/library/ssl_tls13_keys.c
+++ b/library/ssl_tls13_keys.c
@@ -22,7 +22,7 @@
 #include "ssl_tls13_invasive.h"
 
 #include "psa/crypto.h"
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 
 /* Define a local translating function to save code size by not using too many
  * arguments in each translating place. */
diff --git a/library/ssl_tls13_server.c b/library/ssl_tls13_server.c
index 5aa3870..904bb5b 100644
--- a/library/ssl_tls13_server.c
+++ b/library/ssl_tls13_server.c
@@ -14,7 +14,7 @@
 #include "mbedtls/platform.h"
 #include "mbedtls/constant_time.h"
 #include "mbedtls/oid.h"
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 
 #include "ssl_misc.h"
 #include "ssl_tls13_keys.h"
@@ -2540,6 +2540,17 @@
     }
 #endif /* MBEDTLS_SSL_EARLY_DATA */
 
+#if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT)
+    if (ssl->handshake->received_extensions & MBEDTLS_SSL_EXT_MASK(RECORD_SIZE_LIMIT)) {
+        ret = mbedtls_ssl_tls13_write_record_size_limit_ext(
+            ssl, p, end, &output_len);
+        if (ret != 0) {
+            return ret;
+        }
+        p += output_len;
+    }
+#endif
+
     extensions_len = (p - p_extensions_len) - 2;
     MBEDTLS_PUT_UINT16_BE(extensions_len, p_extensions_len, 0);
 
diff --git a/library/threading.c b/library/threading.c
index 873b507..c28290f 100644
--- a/library/threading.c
+++ b/library/threading.c
@@ -148,6 +148,9 @@
 #if defined(THREADING_USE_GMTIME)
     mbedtls_mutex_init(&mbedtls_threading_gmtime_mutex);
 #endif
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+    mbedtls_mutex_init(&mbedtls_threading_key_slot_mutex);
+#endif
 }
 
 /*
@@ -161,6 +164,9 @@
 #if defined(THREADING_USE_GMTIME)
     mbedtls_mutex_free(&mbedtls_threading_gmtime_mutex);
 #endif
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+    mbedtls_mutex_free(&mbedtls_threading_key_slot_mutex);
+#endif
 }
 #endif /* MBEDTLS_THREADING_ALT */
 
@@ -176,5 +182,8 @@
 #if defined(THREADING_USE_GMTIME)
 mbedtls_threading_mutex_t mbedtls_threading_gmtime_mutex MUTEX_INIT;
 #endif
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+mbedtls_threading_mutex_t mbedtls_threading_key_slot_mutex MUTEX_INIT;
+#endif
 
 #endif /* MBEDTLS_THREADING_C */
diff --git a/library/x509_crt.c b/library/x509_crt.c
index 4e7672e..84b92a8 100644
--- a/library/x509_crt.c
+++ b/library/x509_crt.c
@@ -35,7 +35,7 @@
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
 #include "psa/crypto.h"
 #include "psa_util_internal.h"
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 #endif /* MBEDTLS_USE_PSA_CRYPTO */
 #include "pk_internal.h"
 
diff --git a/library/x509write_crt.c b/library/x509write_crt.c
index 44b6b17..913b15a 100644
--- a/library/x509write_crt.c
+++ b/library/x509write_crt.c
@@ -33,7 +33,7 @@
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
 #include "psa/crypto.h"
 #include "psa_util_internal.h"
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 #endif /* MBEDTLS_USE_PSA_CRYPTO */
 
 void mbedtls_x509write_crt_init(mbedtls_x509write_cert *ctx)
diff --git a/library/x509write_csr.c b/library/x509write_csr.c
index 254da69..af75e7f 100644
--- a/library/x509write_csr.c
+++ b/library/x509write_csr.c
@@ -24,7 +24,7 @@
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
 #include "psa/crypto.h"
 #include "psa_util_internal.h"
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 #endif /* MBEDTLS_USE_PSA_CRYPTO */
 
 #include <string.h>
diff --git a/programs/pkey/ecdsa.c b/programs/pkey/ecdsa.c
index afd6fb3..5664b8c 100644
--- a/programs/pkey/ecdsa.c
+++ b/programs/pkey/ecdsa.c
@@ -60,8 +60,8 @@
     unsigned char buf[300];
     size_t len;
 
-    if (mbedtls_ecp_point_write_binary(&key->MBEDTLS_PRIVATE(grp), &key->MBEDTLS_PRIVATE(Q),
-                                       MBEDTLS_ECP_PF_UNCOMPRESSED, &len, buf, sizeof(buf)) != 0) {
+    if (mbedtls_ecp_write_public_key(key, MBEDTLS_ECP_PF_UNCOMPRESSED,
+                                     &len, buf, sizeof(buf)) != 0) {
         mbedtls_printf("internal error\n");
         return;
     }
@@ -79,6 +79,8 @@
     int ret = 1;
     int exit_code = MBEDTLS_EXIT_FAILURE;
     mbedtls_ecdsa_context ctx_sign, ctx_verify;
+    mbedtls_ecp_point Q;
+    mbedtls_ecp_point_init(&Q);
     mbedtls_entropy_context entropy;
     mbedtls_ctr_drbg_context ctr_drbg;
     unsigned char message[100];
@@ -128,7 +130,10 @@
         goto exit;
     }
 
-    mbedtls_printf(" ok (key size: %d bits)\n", (int) ctx_sign.MBEDTLS_PRIVATE(grp).pbits);
+    mbedtls_ecp_group_id grp_id = mbedtls_ecp_keypair_get_group_id(&ctx_sign);
+    const mbedtls_ecp_curve_info *curve_info =
+        mbedtls_ecp_curve_info_from_grp_id(grp_id);
+    mbedtls_printf(" ok (key size: %d bits)\n", (int) curve_info->bit_size);
 
     dump_pubkey("  + Public key: ", &ctx_sign);
 
@@ -174,16 +179,13 @@
     mbedtls_printf("  . Preparing verification context...");
     fflush(stdout);
 
-    if ((ret =
-             mbedtls_ecp_group_copy(&ctx_verify.MBEDTLS_PRIVATE(grp),
-                                    &ctx_sign.MBEDTLS_PRIVATE(grp))) != 0) {
-        mbedtls_printf(" failed\n  ! mbedtls_ecp_group_copy returned %d\n", ret);
+    if ((ret = mbedtls_ecp_export(&ctx_sign, NULL, NULL, &Q)) != 0) {
+        mbedtls_printf(" failed\n  ! mbedtls_ecp_export returned %d\n", ret);
         goto exit;
     }
 
-    if ((ret =
-             mbedtls_ecp_copy(&ctx_verify.MBEDTLS_PRIVATE(Q), &ctx_sign.MBEDTLS_PRIVATE(Q))) != 0) {
-        mbedtls_printf(" failed\n  ! mbedtls_ecp_copy returned %d\n", ret);
+    if ((ret = mbedtls_ecp_set_public_key(grp_id, &ctx_verify, &Q)) != 0) {
+        mbedtls_printf(" failed\n  ! mbedtls_ecp_set_public_key returned %d\n", ret);
         goto exit;
     }
 
@@ -208,6 +210,7 @@
 
     mbedtls_ecdsa_free(&ctx_verify);
     mbedtls_ecdsa_free(&ctx_sign);
+    mbedtls_ecp_point_free(&Q);
     mbedtls_ctr_drbg_free(&ctr_drbg);
     mbedtls_entropy_free(&entropy);
 
diff --git a/programs/pkey/gen_key.c b/programs/pkey/gen_key.c
index f6bb237..6914c93 100644
--- a/programs/pkey/gen_key.c
+++ b/programs/pkey/gen_key.c
@@ -9,8 +9,19 @@
 
 #include "mbedtls/platform.h"
 
-#if defined(MBEDTLS_PK_WRITE_C) && defined(MBEDTLS_FS_IO) && \
-    defined(MBEDTLS_ENTROPY_C) && defined(MBEDTLS_CTR_DRBG_C)
+#if !defined(MBEDTLS_PK_WRITE_C) || !defined(MBEDTLS_PEM_WRITE_C) ||    \
+    !defined(MBEDTLS_FS_IO) || !defined(MBEDTLS_ENTROPY_C) ||           \
+    !defined(MBEDTLS_CTR_DRBG_C) || !defined(MBEDTLS_BIGNUM_C)
+int main(void)
+{
+    mbedtls_printf("MBEDTLS_PK_WRITE_C and/or MBEDTLS_FS_IO and/or "
+                   "MBEDTLS_ENTROPY_C and/or MBEDTLS_CTR_DRBG_C and/or "
+                   "MBEDTLS_PEM_WRITE_C and/or MBEDTLS_BIGNUM_C "
+                   "not defined.\n");
+    mbedtls_exit(0);
+}
+#else
+
 #include "mbedtls/error.h"
 #include "mbedtls/pk.h"
 #include "mbedtls/ecdsa.h"
@@ -61,7 +72,6 @@
     return 0;
 }
 #endif /* !_WIN32 */
-#endif
 
 #if defined(MBEDTLS_ECP_C)
 #define DFL_EC_CURVE            mbedtls_ecp_curve_list()->grp_id
@@ -96,19 +106,6 @@
     USAGE_DEV_RANDOM                                    \
     "\n"
 
-#if !defined(MBEDTLS_PK_WRITE_C) || !defined(MBEDTLS_PEM_WRITE_C) || \
-    !defined(MBEDTLS_FS_IO) || !defined(MBEDTLS_ENTROPY_C) || \
-    !defined(MBEDTLS_CTR_DRBG_C)
-int main(void)
-{
-    mbedtls_printf("MBEDTLS_PK_WRITE_C and/or MBEDTLS_FS_IO and/or "
-                   "MBEDTLS_ENTROPY_C and/or MBEDTLS_CTR_DRBG_C and/or "
-                   "MBEDTLS_PEM_WRITE_C"
-                   "not defined.\n");
-    mbedtls_exit(0);
-}
-#else
-
 
 /*
  * global options
@@ -160,6 +157,71 @@
     return 0;
 }
 
+#if defined(MBEDTLS_ECP_C)
+static int show_ecp_key(const mbedtls_ecp_keypair *ecp, int has_private)
+{
+    int ret = 0;
+
+    const mbedtls_ecp_curve_info *curve_info =
+        mbedtls_ecp_curve_info_from_grp_id(
+            mbedtls_ecp_keypair_get_group_id(ecp));
+    mbedtls_printf("curve: %s\n", curve_info->name);
+
+    mbedtls_ecp_group grp;
+    mbedtls_ecp_group_init(&grp);
+    mbedtls_mpi D;
+    mbedtls_mpi_init(&D);
+    mbedtls_ecp_point pt;
+    mbedtls_ecp_point_init(&pt);
+    mbedtls_mpi X, Y;
+    mbedtls_mpi_init(&X); mbedtls_mpi_init(&Y);
+
+    MBEDTLS_MPI_CHK(mbedtls_ecp_export(ecp, &grp,
+                                       (has_private ? &D : NULL),
+                                       &pt));
+
+    unsigned char point_bin[MBEDTLS_ECP_MAX_PT_LEN];
+    size_t len = 0;
+    MBEDTLS_MPI_CHK(mbedtls_ecp_point_write_binary(
+                        &grp, &pt, MBEDTLS_ECP_PF_UNCOMPRESSED,
+                        &len, point_bin, sizeof(point_bin)));
+    switch (mbedtls_ecp_get_type(&grp)) {
+        case MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS:
+            if ((len & 1) == 0 || point_bin[0] != 0x04) {
+                /* Point in an unxepected format. This shouldn't happen. */
+                ret = -1;
+                goto cleanup;
+            }
+            MBEDTLS_MPI_CHK(
+                mbedtls_mpi_read_binary(&X, point_bin + 1, len / 2));
+            MBEDTLS_MPI_CHK(
+                mbedtls_mpi_read_binary(&Y, point_bin + 1 + len / 2, len / 2));
+            mbedtls_mpi_write_file("X_Q:   ", &X, 16, NULL);
+            mbedtls_mpi_write_file("Y_Q:   ", &Y, 16, NULL);
+            break;
+        case MBEDTLS_ECP_TYPE_MONTGOMERY:
+            MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&X, point_bin, len));
+            mbedtls_mpi_write_file("X_Q:   ", &X, 16, NULL);
+            break;
+        default:
+            mbedtls_printf(
+                "This program does not yet support listing coordinates for this curve type.\n");
+            break;
+    }
+
+    if (has_private) {
+        mbedtls_mpi_write_file("D:     ", &D, 16, NULL);
+    }
+
+cleanup:
+    mbedtls_ecp_group_free(&grp);
+    mbedtls_mpi_free(&D);
+    mbedtls_ecp_point_free(&pt);
+    mbedtls_mpi_free(&X); mbedtls_mpi_free(&Y);
+    return ret;
+}
+#endif
+
 int main(int argc, char *argv[])
 {
     int ret = 1;
@@ -365,12 +427,10 @@
 #endif
 #if defined(MBEDTLS_ECP_C)
     if (mbedtls_pk_get_type(&key) == MBEDTLS_PK_ECKEY) {
-        mbedtls_ecp_keypair *ecp = mbedtls_pk_ec(key);
-        mbedtls_printf("curve: %s\n",
-                       mbedtls_ecp_curve_info_from_grp_id(ecp->MBEDTLS_PRIVATE(grp).id)->name);
-        mbedtls_mpi_write_file("X_Q:   ", &ecp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X), 16, NULL);
-        mbedtls_mpi_write_file("Y_Q:   ", &ecp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y), 16, NULL);
-        mbedtls_mpi_write_file("D:     ", &ecp->MBEDTLS_PRIVATE(d), 16, NULL);
+        if (show_ecp_key(mbedtls_pk_ec(key), 1) != 0) {
+            mbedtls_printf(" failed\n  ! could not export ECC parameters\n\n");
+            goto exit;
+        }
     } else
 #endif
     mbedtls_printf("  ! key type not supported\n");
@@ -415,5 +475,4 @@
 
     mbedtls_exit(exit_code);
 }
-#endif /* MBEDTLS_PK_WRITE_C && MBEDTLS_PEM_WRITE_C && MBEDTLS_FS_IO &&
-        * MBEDTLS_ENTROPY_C && MBEDTLS_CTR_DRBG_C */
+#endif /* program viability conditions */
diff --git a/programs/pkey/key_app.c b/programs/pkey/key_app.c
index 194c410..e3a6966 100644
--- a/programs/pkey/key_app.c
+++ b/programs/pkey/key_app.c
@@ -53,6 +53,71 @@
 #else
 
 
+#if defined(MBEDTLS_ECP_C)
+static int show_ecp_key(const mbedtls_ecp_keypair *ecp, int has_private)
+{
+    int ret = 0;
+
+    const mbedtls_ecp_curve_info *curve_info =
+        mbedtls_ecp_curve_info_from_grp_id(
+            mbedtls_ecp_keypair_get_group_id(ecp));
+    mbedtls_printf("curve: %s\n", curve_info->name);
+
+    mbedtls_ecp_group grp;
+    mbedtls_ecp_group_init(&grp);
+    mbedtls_mpi D;
+    mbedtls_mpi_init(&D);
+    mbedtls_ecp_point pt;
+    mbedtls_ecp_point_init(&pt);
+    mbedtls_mpi X, Y;
+    mbedtls_mpi_init(&X); mbedtls_mpi_init(&Y);
+
+    MBEDTLS_MPI_CHK(mbedtls_ecp_export(ecp, &grp,
+                                       (has_private ? &D : NULL),
+                                       &pt));
+
+    unsigned char point_bin[MBEDTLS_ECP_MAX_PT_LEN];
+    size_t len = 0;
+    MBEDTLS_MPI_CHK(mbedtls_ecp_point_write_binary(
+                        &grp, &pt, MBEDTLS_ECP_PF_UNCOMPRESSED,
+                        &len, point_bin, sizeof(point_bin)));
+    switch (mbedtls_ecp_get_type(&grp)) {
+        case MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS:
+            if ((len & 1) == 0 || point_bin[0] != 0x04) {
+                /* Point in an unxepected format. This shouldn't happen. */
+                ret = -1;
+                goto cleanup;
+            }
+            MBEDTLS_MPI_CHK(
+                mbedtls_mpi_read_binary(&X, point_bin + 1, len / 2));
+            MBEDTLS_MPI_CHK(
+                mbedtls_mpi_read_binary(&Y, point_bin + 1 + len / 2, len / 2));
+            mbedtls_mpi_write_file("X_Q:   ", &X, 16, NULL);
+            mbedtls_mpi_write_file("Y_Q:   ", &Y, 16, NULL);
+            break;
+        case MBEDTLS_ECP_TYPE_MONTGOMERY:
+            MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&X, point_bin, len));
+            mbedtls_mpi_write_file("X_Q:   ", &X, 16, NULL);
+            break;
+        default:
+            mbedtls_printf(
+                "This program does not yet support listing coordinates for this curve type.\n");
+            break;
+    }
+
+    if (has_private) {
+        mbedtls_mpi_write_file("D:     ", &D, 16, NULL);
+    }
+
+cleanup:
+    mbedtls_ecp_group_free(&grp);
+    mbedtls_mpi_free(&D);
+    mbedtls_ecp_point_free(&pt);
+    mbedtls_mpi_free(&X); mbedtls_mpi_free(&Y);
+    return ret;
+}
+#endif
+
 /*
  * global options
  */
@@ -219,17 +284,10 @@
 #endif
 #if defined(MBEDTLS_ECP_C)
         if (mbedtls_pk_get_type(&pk) == MBEDTLS_PK_ECKEY) {
-            mbedtls_ecp_keypair *ecp = mbedtls_pk_ec(pk);
-            MBEDTLS_MPI_CHK(mbedtls_mpi_write_file("Q(X): ",
-                                                   &ecp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X), 16,
-                                                   NULL));
-            MBEDTLS_MPI_CHK(mbedtls_mpi_write_file("Q(Y): ",
-                                                   &ecp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y), 16,
-                                                   NULL));
-            MBEDTLS_MPI_CHK(mbedtls_mpi_write_file("Q(Z): ",
-                                                   &ecp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Z), 16,
-                                                   NULL));
-            MBEDTLS_MPI_CHK(mbedtls_mpi_write_file("D   : ", &ecp->MBEDTLS_PRIVATE(d), 16, NULL));
+            if (show_ecp_key(mbedtls_pk_ec(pk), 1) != 0) {
+                mbedtls_printf(" failed\n  ! could not export ECC parameters\n\n");
+                goto cleanup;
+            }
         } else
 #endif
         {
@@ -269,16 +327,10 @@
 #endif
 #if defined(MBEDTLS_ECP_C)
         if (mbedtls_pk_get_type(&pk) == MBEDTLS_PK_ECKEY) {
-            mbedtls_ecp_keypair *ecp = mbedtls_pk_ec(pk);
-            MBEDTLS_MPI_CHK(mbedtls_mpi_write_file("Q(X): ",
-                                                   &ecp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X), 16,
-                                                   NULL));
-            MBEDTLS_MPI_CHK(mbedtls_mpi_write_file("Q(Y): ",
-                                                   &ecp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y), 16,
-                                                   NULL));
-            MBEDTLS_MPI_CHK(mbedtls_mpi_write_file("Q(Z): ",
-                                                   &ecp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Z), 16,
-                                                   NULL));
+            if (show_ecp_key(mbedtls_pk_ec(pk), 0) != 0) {
+                mbedtls_printf(" failed\n  ! could not export ECC parameters\n\n");
+                goto cleanup;
+            }
         } else
 #endif
         {
diff --git a/programs/pkey/key_app_writer.c b/programs/pkey/key_app_writer.c
index c07c564..60f992e 100644
--- a/programs/pkey/key_app_writer.c
+++ b/programs/pkey/key_app_writer.c
@@ -9,9 +9,21 @@
 
 #include "mbedtls/platform.h"
 
-#if defined(MBEDTLS_PK_PARSE_C) && defined(MBEDTLS_PK_WRITE_C) && \
-    defined(MBEDTLS_FS_IO) && \
-    defined(MBEDTLS_ENTROPY_C) && defined(MBEDTLS_CTR_DRBG_C)
+#if !defined(MBEDTLS_PK_PARSE_C) || \
+    !defined(MBEDTLS_PK_WRITE_C) || \
+    !defined(MBEDTLS_FS_IO)      || \
+    !defined(MBEDTLS_ENTROPY_C)  || \
+    !defined(MBEDTLS_CTR_DRBG_C) || \
+    !defined(MBEDTLS_BIGNUM_C)
+int main(void)
+{
+    mbedtls_printf("MBEDTLS_PK_PARSE_C and/or MBEDTLS_PK_WRITE_C and/or "
+                   "MBEDTLS_ENTROPY_C and/or MBEDTLS_CTR_DRBG_C and/or "
+                   "MBEDTLS_FS_IO and/or MBEDTLS_BIGNUM_C not defined.\n");
+    mbedtls_exit(0);
+}
+#else
+
 #include "mbedtls/error.h"
 #include "mbedtls/pk.h"
 #include "mbedtls/error.h"
@@ -21,7 +33,6 @@
 
 #include <stdio.h>
 #include <string.h>
-#endif
 
 #if defined(MBEDTLS_PEM_WRITE_C)
 #define USAGE_OUT \
@@ -66,20 +77,6 @@
     USAGE_OUT                                           \
     "\n"
 
-#if !defined(MBEDTLS_PK_PARSE_C) || \
-    !defined(MBEDTLS_PK_WRITE_C) || \
-    !defined(MBEDTLS_FS_IO)      || \
-    !defined(MBEDTLS_ENTROPY_C)  || \
-    !defined(MBEDTLS_CTR_DRBG_C)
-int main(void)
-{
-    mbedtls_printf("MBEDTLS_PK_PARSE_C and/or MBEDTLS_PK_WRITE_C and/or "
-                   "MBEDTLS_ENTROPY_C and/or MBEDTLS_CTR_DRBG_C and/or "
-                   "MBEDTLS_FS_IO not defined.\n");
-    mbedtls_exit(0);
-}
-#else
-
 
 /*
  * global options
@@ -176,6 +173,71 @@
     return 0;
 }
 
+#if defined(MBEDTLS_ECP_C)
+static int show_ecp_key(const mbedtls_ecp_keypair *ecp, int has_private)
+{
+    int ret = 0;
+
+    const mbedtls_ecp_curve_info *curve_info =
+        mbedtls_ecp_curve_info_from_grp_id(
+            mbedtls_ecp_keypair_get_group_id(ecp));
+    mbedtls_printf("curve: %s\n", curve_info->name);
+
+    mbedtls_ecp_group grp;
+    mbedtls_ecp_group_init(&grp);
+    mbedtls_mpi D;
+    mbedtls_mpi_init(&D);
+    mbedtls_ecp_point pt;
+    mbedtls_ecp_point_init(&pt);
+    mbedtls_mpi X, Y;
+    mbedtls_mpi_init(&X); mbedtls_mpi_init(&Y);
+
+    MBEDTLS_MPI_CHK(mbedtls_ecp_export(ecp, &grp,
+                                       (has_private ? &D : NULL),
+                                       &pt));
+
+    unsigned char point_bin[MBEDTLS_ECP_MAX_PT_LEN];
+    size_t len = 0;
+    MBEDTLS_MPI_CHK(mbedtls_ecp_point_write_binary(
+                        &grp, &pt, MBEDTLS_ECP_PF_UNCOMPRESSED,
+                        &len, point_bin, sizeof(point_bin)));
+    switch (mbedtls_ecp_get_type(&grp)) {
+        case MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS:
+            if ((len & 1) == 0 || point_bin[0] != 0x04) {
+                /* Point in an unxepected format. This shouldn't happen. */
+                ret = -1;
+                goto cleanup;
+            }
+            MBEDTLS_MPI_CHK(
+                mbedtls_mpi_read_binary(&X, point_bin + 1, len / 2));
+            MBEDTLS_MPI_CHK(
+                mbedtls_mpi_read_binary(&Y, point_bin + 1 + len / 2, len / 2));
+            mbedtls_mpi_write_file("X_Q:   ", &X, 16, NULL);
+            mbedtls_mpi_write_file("Y_Q:   ", &Y, 16, NULL);
+            break;
+        case MBEDTLS_ECP_TYPE_MONTGOMERY:
+            MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&X, point_bin, len));
+            mbedtls_mpi_write_file("X_Q:   ", &X, 16, NULL);
+            break;
+        default:
+            mbedtls_printf(
+                "This program does not yet support listing coordinates for this curve type.\n");
+            break;
+    }
+
+    if (has_private) {
+        mbedtls_mpi_write_file("D:     ", &D, 16, NULL);
+    }
+
+cleanup:
+    mbedtls_ecp_group_free(&grp);
+    mbedtls_mpi_free(&D);
+    mbedtls_ecp_point_free(&pt);
+    mbedtls_mpi_free(&X); mbedtls_mpi_free(&Y);
+    return ret;
+}
+#endif
+
 int main(int argc, char *argv[])
 {
     int ret = 1;
@@ -338,11 +400,10 @@
 #endif
 #if defined(MBEDTLS_ECP_C)
         if (mbedtls_pk_get_type(&key) == MBEDTLS_PK_ECKEY) {
-            mbedtls_ecp_keypair *ecp = mbedtls_pk_ec(key);
-            mbedtls_mpi_write_file("Q(X): ", &ecp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X), 16, NULL);
-            mbedtls_mpi_write_file("Q(Y): ", &ecp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y), 16, NULL);
-            mbedtls_mpi_write_file("Q(Z): ", &ecp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Z), 16, NULL);
-            mbedtls_mpi_write_file("D   : ", &ecp->MBEDTLS_PRIVATE(d), 16, NULL);
+            if (show_ecp_key(mbedtls_pk_ec(key), 1) != 0) {
+                mbedtls_printf(" failed\n  ! could not export ECC parameters\n\n");
+                goto exit;
+            }
         } else
 #endif
         mbedtls_printf("key type not supported yet\n");
@@ -384,10 +445,10 @@
 #endif
 #if defined(MBEDTLS_ECP_C)
         if (mbedtls_pk_get_type(&key) == MBEDTLS_PK_ECKEY) {
-            mbedtls_ecp_keypair *ecp = mbedtls_pk_ec(key);
-            mbedtls_mpi_write_file("Q(X): ", &ecp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X), 16, NULL);
-            mbedtls_mpi_write_file("Q(Y): ", &ecp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y), 16, NULL);
-            mbedtls_mpi_write_file("Q(Z): ", &ecp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Z), 16, NULL);
+            if (show_ecp_key(mbedtls_pk_ec(key), 0) != 0) {
+                mbedtls_printf(" failed\n  ! could not export ECC parameters\n\n");
+                goto exit;
+            }
         } else
 #endif
         mbedtls_printf("key type not supported yet\n");
@@ -431,5 +492,4 @@
 
     mbedtls_exit(exit_code);
 }
-#endif /* MBEDTLS_PK_PARSE_C && MBEDTLS_PK_WRITE_C && MBEDTLS_FS_IO &&
-          MBEDTLS_ENTROPY_C && MBEDTLS_CTR_DRBG_C */
+#endif /* program viability conditions */
diff --git a/programs/test/benchmark.c b/programs/test/benchmark.c
index 755a731..6f7f69b 100644
--- a/programs/test/benchmark.c
+++ b/programs/test/benchmark.c
@@ -507,7 +507,7 @@
     char md5, ripemd160, sha1, sha256, sha512,
          sha3_224, sha3_256, sha3_384, sha3_512,
          des3, des,
-         aes_cbc, aes_cfb128, aes_cfb8, aes_gcm, aes_ccm, aes_xts, chachapoly,
+         aes_cbc, aes_cfb128, aes_cfb8, aes_ctr, aes_gcm, aes_ccm, aes_xts, chachapoly,
          aes_cmac, des3_cmac,
          aria, camellia, chacha20,
          poly1305,
@@ -571,6 +571,8 @@
                 todo.aes_cfb128 = 1;
             } else if (strcmp(argv[i], "aes_cfb8") == 0) {
                 todo.aes_cfb8 = 1;
+            } else if (strcmp(argv[i], "aes_ctr") == 0) {
+                todo.aes_ctr = 1;
             } else if (strcmp(argv[i], "aes_xts") == 0) {
                 todo.aes_xts = 1;
             } else if (strcmp(argv[i], "aes_gcm") == 0) {
@@ -774,6 +776,31 @@
         mbedtls_aes_free(&aes);
     }
 #endif
+#if defined(MBEDTLS_CIPHER_MODE_CTR)
+    if (todo.aes_ctr) {
+        int keysize;
+        mbedtls_aes_context aes;
+
+        uint8_t stream_block[16];
+        size_t nc_off;
+
+        mbedtls_aes_init(&aes);
+        for (keysize = 128; keysize <= 256; keysize += 64) {
+            mbedtls_snprintf(title, sizeof(title), "AES-CTR-%d", keysize);
+
+            memset(buf, 0, sizeof(buf));
+            memset(tmp, 0, sizeof(tmp));
+            memset(stream_block, 0, sizeof(stream_block));
+            nc_off = 0;
+
+            CHECK_AND_CONTINUE(mbedtls_aes_setkey_enc(&aes, tmp, keysize));
+
+            TIME_AND_TSC(title, mbedtls_aes_crypt_ctr(&aes, BUFSIZE, &nc_off, tmp, stream_block,
+                                                      buf, buf));
+        }
+        mbedtls_aes_free(&aes);
+    }
+#endif
 #if defined(MBEDTLS_CIPHER_MODE_XTS)
     if (todo.aes_xts) {
         int keysize;
diff --git a/scripts/config.py b/scripts/config.py
index d5fb85e..ab0e5ea 100755
--- a/scripts/config.py
+++ b/scripts/config.py
@@ -207,7 +207,6 @@
     'MBEDTLS_SHA256_USE_ARMV8_A_CRYPTO_ONLY', # interacts with *_USE_ARMV8_A_CRYPTO_IF_PRESENT
     'MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY', # interacts with *_USE_A64_CRYPTO_IF_PRESENT
     'MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT', # setting *_USE_ARMV8_A_CRYPTO is sufficient
-    'MBEDTLS_SSL_RECORD_SIZE_LIMIT', # in development, currently breaks other tests
     'MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN', # build dependency (clang+memsan)
     'MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND', # build dependency (valgrind headers)
     'MBEDTLS_X509_REMOVE_INFO', # removes a feature
diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh
index c298119..44930d2 100755
--- a/tests/scripts/all.sh
+++ b/tests/scripts/all.sh
@@ -5646,6 +5646,7 @@
 component_test_tls13_only () {
     msg "build: default config with MBEDTLS_SSL_PROTO_TLS1_3, without MBEDTLS_SSL_PROTO_TLS1_2"
     scripts/config.py set MBEDTLS_SSL_EARLY_DATA
+    scripts/config.py set MBEDTLS_SSL_RECORD_SIZE_LIMIT
     make CFLAGS="'-DMBEDTLS_USER_CONFIG_FILE=\"../tests/configs/tls13-only.h\"'"
 
     msg "test: TLS 1.3 only, all key exchange modes enabled"
@@ -5808,18 +5809,6 @@
     tests/ssl-opt.sh
 }
 
-component_test_tls13_only_record_size_limit () {
-    msg "build: TLS 1.3 only from default, record size limit extension enabled"
-    scripts/config.py set MBEDTLS_SSL_RECORD_SIZE_LIMIT
-    make CFLAGS="'-DMBEDTLS_USER_CONFIG_FILE=\"../tests/configs/tls13-only.h\"'"
-
-    msg "test_suite_ssl: TLS 1.3 only, record size limit extension enabled"
-    cd tests; ./test_suite_ssl; cd ..
-
-    msg "ssl-opt.sh: (TLS 1.3 only, record size limit extension enabled)"
-    tests/ssl-opt.sh
-}
-
 component_build_mingw () {
     msg "build: Windows cross build - mingw64, make (Link Library)" # ~ 30s
     make CC=i686-w64-mingw32-gcc AR=i686-w64-mingw32-ar LD=i686-w64-minggw32-ld CFLAGS='-Werror -Wall -Wextra -maes -msse2 -mpclmul' WINDOWS_BUILD=1 lib programs
diff --git a/tests/scripts/analyze_outcomes.py b/tests/scripts/analyze_outcomes.py
index a867971..6503f9a 100755
--- a/tests/scripts/analyze_outcomes.py
+++ b/tests/scripts/analyze_outcomes.py
@@ -325,6 +325,7 @@
                 # is required.
                 'test_suite_ecp': [
                     re.compile(r'ECP check public-private .*'),
+                    re.compile(r'ECP calculate public: .*'),
                     re.compile(r'ECP gen keypair .*'),
                     re.compile(r'ECP point muladd .*'),
                     re.compile(r'ECP point multiplication .*'),
diff --git a/tests/src/test_helpers/ssl_helpers.c b/tests/src/test_helpers/ssl_helpers.c
index 3d8937d..2368a76 100644
--- a/tests/src/test_helpers/ssl_helpers.c
+++ b/tests/src/test_helpers/ssl_helpers.c
@@ -9,7 +9,7 @@
  */
 
 #include <test/ssl_helpers.h>
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 
 #if defined(MBEDTLS_SSL_TLS_C)
 #if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED)
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 2d34f90..26c5a79 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -4836,8 +4836,9 @@
 
 requires_gnutls_tls1_3
 requires_gnutls_record_size_limit
+requires_all_configs_enabled MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE MBEDTLS_SSL_SRV_C MBEDTLS_DEBUG_C
 requires_config_enabled MBEDTLS_SSL_RECORD_SIZE_LIMIT
-requires_config_enabled MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE
+requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
 run_test    "Record Size Limit: TLS 1.3: Server-side parsing and debug output" \
             "$P_SRV debug_level=3 force_version=tls13" \
             "$G_NEXT_CLI localhost --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3 -V -d 4" \
@@ -4849,28 +4850,30 @@
 
 requires_gnutls_tls1_3
 requires_gnutls_record_size_limit
-requires_gnutls_next_disable_tls13_compat
+requires_all_configs_enabled MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C
 requires_config_enabled MBEDTLS_SSL_RECORD_SIZE_LIMIT
-requires_config_enabled MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE
+requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
 run_test    "Record Size Limit: TLS 1.3: Client-side parsing and debug output" \
-            "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+CIPHER-ALL:%DISABLE_TLS13_COMPAT_MODE --disable-client-cert -d 4" \
+            "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+CIPHER-ALL --disable-client-cert -d 4" \
             "$P_CLI debug_level=4 force_version=tls13" \
             0 \
-            -s "Preparing extension (Record Size Limit/28) for 'encrypted extensions'"
-# The P_CLI can not yet send the Record Size Limit extension. Thus, the G_NEXT_SRV does not send
-# a response in its EncryptedExtensions record.
-#            -c "RecordSizeLimit: 16385 Bytes"
+            -c "Sent RecordSizeLimit: 16384 Bytes"                                      \
+            -c "ClientHello: record_size_limit(28) extension exists."                   \
+            -c "EncryptedExtensions: record_size_limit(28) extension received."         \
+            -c "RecordSizeLimit: 16385 Bytes"                                           \
 
-# In the following (9) tests, --recordsize is the value used by the G_NEXT_CLI (3.7.2) to configure the
-# maximum record size using "https://gnutls.org/reference/gnutls-gnutls.html#gnutls-record-set-max-size".
-# There is currently a lower limit of 512, caused by this function not respecting the
-# "%ALLOW_SMALL_RECORDS" priority string and not using the more recent function
-# https://gnutls.org/reference/gnutls-gnutls.html#gnutls-record-set-max-recv-size.
+# In the following tests, --recordsize is the value used by the G_NEXT_CLI (3.7.2) to configure the
+# maximum record size using gnutls_record_set_max_size()
+# (https://gnutls.org/reference/gnutls-gnutls.html#gnutls-record-set-max-size).
+# There is currently a lower limit of 512, caused by gnutls_record_set_max_size()
+# not respecting the "%ALLOW_SMALL_RECORDS" priority string and not using the
+# more recent function gnutls_record_set_max_recv_size()
+# (https://gnutls.org/reference/gnutls-gnutls.html#gnutls-record-set-max-recv-size).
 # There is currently an upper limit of 4096, caused by the cli arg parser:
 # https://gitlab.com/gnutls/gnutls/-/blob/3.7.2/src/cli-args.def#L395.
-# Thus, these tests are currently limit to that value range.
-# Moreover, the value sent in the extension is expected to be larger by one compared
-# to the value passed on the cli:
+# Thus, these tests are currently limited to the value range 512-4096.
+# Also, the value sent in the extension will be one larger than the value
+# set at the command line:
 # https://gitlab.com/gnutls/gnutls/-/blob/3.7.2/lib/ext/record_size_limit.c#L142
 
 # Currently test certificates being used do not fit in 513 record size limit
@@ -4886,10 +4889,13 @@
             "$P_SRV debug_level=3 force_version=tls13 tls13_kex_modes=psk \
                     psk_list=Client_identity,6162636465666768696a6b6c6d6e6f70 \
                     response_size=256" \
-                    "$G_NEXT_CLI localhost --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+PSK --recordsize 512 \
-                         --pskusername Client_identity --pskkey=6162636465666768696a6b6c6d6e6f70" \
+            "$G_NEXT_CLI localhost --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+PSK --recordsize 512 \
+                    --pskusername Client_identity --pskkey=6162636465666768696a6b6c6d6e6f70" \
             0 \
             -s "RecordSizeLimit: 513 Bytes" \
+            -s "ClientHello: record_size_limit(28) extension exists." \
+            -s "Sent RecordSizeLimit: 16384 Bytes" \
+            -s "EncryptedExtensions: record_size_limit(28) extension exists." \
             -s "Maximum outgoing record payload length is 511" \
             -s "256 bytes written in 1 fragments"
 
@@ -4906,6 +4912,9 @@
                          --pskusername Client_identity --pskkey=6162636465666768696a6b6c6d6e6f70" \
             0 \
             -s "RecordSizeLimit: 513 Bytes" \
+            -s "ClientHello: record_size_limit(28) extension exists." \
+            -s "Sent RecordSizeLimit: 16384 Bytes" \
+            -s "EncryptedExtensions: record_size_limit(28) extension exists." \
             -s "Maximum outgoing record payload length is 511" \
             -s "768 bytes written in 2 fragments"
 
@@ -4922,6 +4931,9 @@
                          --pskusername Client_identity --pskkey=6162636465666768696a6b6c6d6e6f70" \
             0 \
             -s "RecordSizeLimit: 513 Bytes" \
+            -s "ClientHello: record_size_limit(28) extension exists." \
+            -s "Sent RecordSizeLimit: 16384 Bytes" \
+            -s "EncryptedExtensions: record_size_limit(28) extension exists." \
             -s "Maximum outgoing record payload length is 511" \
             -s "1280 bytes written in 3 fragments"
 
@@ -4936,6 +4948,8 @@
             0 \
             -s "RecordSizeLimit: 1024 Bytes" \
             -s "ClientHello: record_size_limit(28) extension exists." \
+            -s "Sent RecordSizeLimit: 16384 Bytes" \
+            -s "EncryptedExtensions: record_size_limit(28) extension exists." \
             -s "Maximum outgoing record payload length is 1023" \
             -s "512 bytes written in 1 fragments"
 
@@ -4950,6 +4964,8 @@
             0 \
             -s "RecordSizeLimit: 1024 Bytes" \
             -s "ClientHello: record_size_limit(28) extension exists." \
+            -s "Sent RecordSizeLimit: 16384 Bytes" \
+            -s "EncryptedExtensions: record_size_limit(28) extension exists." \
             -s "Maximum outgoing record payload length is 1023" \
             -s "1536 bytes written in 2 fragments"
 
@@ -4964,6 +4980,8 @@
             0 \
             -s "RecordSizeLimit: 1024 Bytes" \
             -s "ClientHello: record_size_limit(28) extension exists." \
+            -s "Sent RecordSizeLimit: 16384 Bytes" \
+            -s "EncryptedExtensions: record_size_limit(28) extension exists." \
             -s "Maximum outgoing record payload length is 1023" \
             -s "2560 bytes written in 3 fragments"
 
@@ -4978,6 +4996,8 @@
             0 \
             -s "RecordSizeLimit: 4096 Bytes" \
             -s "ClientHello: record_size_limit(28) extension exists." \
+            -s "Sent RecordSizeLimit: 16384 Bytes" \
+            -s "EncryptedExtensions: record_size_limit(28) extension exists." \
             -s "Maximum outgoing record payload length is 4095" \
             -s "2048 bytes written in 1 fragments"
 
@@ -4992,6 +5012,8 @@
             0 \
             -s "RecordSizeLimit: 4096 Bytes" \
             -s "ClientHello: record_size_limit(28) extension exists." \
+            -s "Sent RecordSizeLimit: 16384 Bytes" \
+            -s "EncryptedExtensions: record_size_limit(28) extension exists." \
             -s "Maximum outgoing record payload length is 4095" \
             -s "6144 bytes written in 2 fragments"
 
@@ -5006,11 +5028,181 @@
             0 \
             -s "RecordSizeLimit: 4096 Bytes" \
             -s "ClientHello: record_size_limit(28) extension exists." \
+            -s "Sent RecordSizeLimit: 16384 Bytes" \
+            -s "EncryptedExtensions: record_size_limit(28) extension exists." \
             -s "Maximum outgoing record payload length is 4095" \
             -s "10240 bytes written in 3 fragments"
 
+requires_gnutls_tls1_3
+requires_gnutls_record_size_limit
+requires_all_configs_enabled MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C
+requires_config_enabled MBEDTLS_SSL_RECORD_SIZE_LIMIT
+requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
+run_test    "Record Size Limit: TLS 1.3: Client complies with record size limit (513), 1 fragment" \
+            "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+CIPHER-ALL -d 4 --disable-client-cert --recordsize 512" \
+            "$P_CLI debug_level=4 force_version=tls13 request_size=256" \
+            0 \
+            -c "Sent RecordSizeLimit: 16384 Bytes" \
+            -c "ClientHello: record_size_limit(28) extension exists." \
+            -c "RecordSizeLimit: 513 Bytes" \
+            -c "EncryptedExtensions: record_size_limit(28) extension exists." \
+            -c "Maximum outgoing record payload length is 511" \
+            -c "256 bytes written in 1 fragments"
+
+requires_gnutls_tls1_3
+requires_gnutls_record_size_limit
+requires_all_configs_enabled MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C
+requires_config_enabled MBEDTLS_SSL_RECORD_SIZE_LIMIT
+requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
+run_test    "Record Size Limit: TLS 1.3: Client complies with record size limit (513), 2 fragments" \
+            "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+CIPHER-ALL -d 4 --disable-client-cert --recordsize 512" \
+            "$P_CLI debug_level=4 force_version=tls13 request_size=768" \
+            0 \
+            -c "Sent RecordSizeLimit: 16384 Bytes" \
+            -c "ClientHello: record_size_limit(28) extension exists." \
+            -c "RecordSizeLimit: 513 Bytes" \
+            -c "EncryptedExtensions: record_size_limit(28) extension exists." \
+            -c "Maximum outgoing record payload length is 511" \
+            -c "768 bytes written in 2 fragments"
+
+requires_gnutls_tls1_3
+requires_gnutls_record_size_limit
+requires_all_configs_enabled MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C
+requires_config_enabled MBEDTLS_SSL_RECORD_SIZE_LIMIT
+requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
+run_test    "Record Size Limit: TLS 1.3: Client complies with record size limit (513), 3 fragments" \
+            "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+CIPHER-ALL -d 4 --disable-client-cert --recordsize 512" \
+            "$P_CLI debug_level=4 force_version=tls13 request_size=1280" \
+            0 \
+            -c "Sent RecordSizeLimit: 16384 Bytes" \
+            -c "ClientHello: record_size_limit(28) extension exists." \
+            -c "RecordSizeLimit: 513 Bytes" \
+            -c "EncryptedExtensions: record_size_limit(28) extension exists." \
+            -c "Maximum outgoing record payload length is 511" \
+            -c "1280 bytes written in 3 fragments"
+
+requires_gnutls_tls1_3
+requires_gnutls_record_size_limit
+requires_all_configs_enabled MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C
+requires_config_enabled MBEDTLS_SSL_RECORD_SIZE_LIMIT
+requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
+run_test    "Record Size Limit: TLS 1.3: Client complies with record size limit (1024), 1 fragment" \
+            "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+CIPHER-ALL -d 4 --recordsize 1023" \
+            "$P_CLI debug_level=4 force_version=tls13 request_size=512" \
+            0 \
+            -c "Sent RecordSizeLimit: 16384 Bytes" \
+            -c "ClientHello: record_size_limit(28) extension exists." \
+            -c "RecordSizeLimit: 1024 Bytes" \
+            -c "EncryptedExtensions: record_size_limit(28) extension exists." \
+            -c "Maximum outgoing record payload length is 1023" \
+            -c "512 bytes written in 1 fragments"
+
+requires_gnutls_tls1_3
+requires_gnutls_record_size_limit
+requires_all_configs_enabled MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C
+requires_config_enabled MBEDTLS_SSL_RECORD_SIZE_LIMIT
+requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
+run_test    "Record Size Limit: TLS 1.3: Client complies with record size limit (1024), 2 fragments" \
+            "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+CIPHER-ALL -d 4 --recordsize 1023" \
+            "$P_CLI debug_level=4 force_version=tls13 request_size=1536" \
+            0 \
+            -c "Sent RecordSizeLimit: 16384 Bytes" \
+            -c "ClientHello: record_size_limit(28) extension exists." \
+            -c "RecordSizeLimit: 1024 Bytes" \
+            -c "EncryptedExtensions: record_size_limit(28) extension exists." \
+            -c "Maximum outgoing record payload length is 1023" \
+            -c "1536 bytes written in 2 fragments"
+
+requires_gnutls_tls1_3
+requires_gnutls_record_size_limit
+requires_all_configs_enabled MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C
+requires_config_enabled MBEDTLS_SSL_RECORD_SIZE_LIMIT
+requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
+run_test    "Record Size Limit: TLS 1.3: Client complies with record size limit (1024), 3 fragments" \
+            "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+CIPHER-ALL -d 4 --recordsize 1023" \
+            "$P_CLI debug_level=4 force_version=tls13 request_size=2560" \
+            0 \
+            -c "Sent RecordSizeLimit: 16384 Bytes" \
+            -c "ClientHello: record_size_limit(28) extension exists." \
+            -c "RecordSizeLimit: 1024 Bytes" \
+            -c "EncryptedExtensions: record_size_limit(28) extension exists." \
+            -c "Maximum outgoing record payload length is 1023" \
+            -c "2560 bytes written in 3 fragments"
+
+requires_gnutls_tls1_3
+requires_gnutls_record_size_limit
+requires_all_configs_enabled MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C
+requires_config_enabled MBEDTLS_SSL_RECORD_SIZE_LIMIT
+requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
+run_test    "Record Size Limit: TLS 1.3: Client complies with record size limit (4096), 1 fragment" \
+            "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+CIPHER-ALL -d 4 --recordsize 4095" \
+            "$P_CLI debug_level=4 force_version=tls13 request_size=2048" \
+            0 \
+            -c "Sent RecordSizeLimit: 16384 Bytes" \
+            -c "ClientHello: record_size_limit(28) extension exists." \
+            -c "RecordSizeLimit: 4096 Bytes" \
+            -c "EncryptedExtensions: record_size_limit(28) extension exists." \
+            -c "Maximum outgoing record payload length is 4095" \
+            -c "2048 bytes written in 1 fragments"
+
+requires_gnutls_tls1_3
+requires_gnutls_record_size_limit
+requires_all_configs_enabled MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C
+requires_config_enabled MBEDTLS_SSL_RECORD_SIZE_LIMIT
+requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
+run_test    "Record Size Limit: TLS 1.3: Client complies with record size limit (4096), 2 fragments" \
+            "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+CIPHER-ALL -d 4 --recordsize 4095" \
+            "$P_CLI debug_level=4 force_version=tls13 request_size=6144" \
+            0 \
+            -c "Sent RecordSizeLimit: 16384 Bytes" \
+            -c "ClientHello: record_size_limit(28) extension exists." \
+            -c "RecordSizeLimit: 4096 Bytes" \
+            -c "EncryptedExtensions: record_size_limit(28) extension exists." \
+            -c "Maximum outgoing record payload length is 4095" \
+            -c "6144 bytes written in 2 fragments"
+
+requires_gnutls_tls1_3
+requires_gnutls_record_size_limit
+requires_all_configs_enabled MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C
+requires_config_enabled MBEDTLS_SSL_RECORD_SIZE_LIMIT
+requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
+run_test    "Record Size Limit: TLS 1.3: Client complies with record size limit (4096), 3 fragments" \
+            "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+CIPHER-ALL -d 4 --recordsize 4095" \
+            "$P_CLI debug_level=4 force_version=tls13 request_size=10240" \
+            0 \
+            -c "Sent RecordSizeLimit: 16384 Bytes" \
+            -c "ClientHello: record_size_limit(28) extension exists." \
+            -c "RecordSizeLimit: 4096 Bytes" \
+            -c "EncryptedExtensions: record_size_limit(28) extension exists." \
+            -c "Maximum outgoing record payload length is 4095" \
+            -c "10240 bytes written in 3 fragments"
+
+# TODO: For time being, we send fixed value of RecordSizeLimit defined by
+# MBEDTLS_SSL_IN_CONTENT_LEN. Once we support variable buffer length of
+# RecordSizeLimit, we need to modify value of RecordSizeLimit in below test.
+requires_config_value_equals "MBEDTLS_SSL_IN_CONTENT_LEN" 16384
+requires_all_configs_enabled MBEDTLS_SSL_CLI_C MBEDTLS_SSL_SRV_C MBEDTLS_DEBUG_C
+requires_config_enabled MBEDTLS_SSL_RECORD_SIZE_LIMIT
+requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
+run_test    "Record Size Limit: TLS 1.3 m->m: both peer comply with record size limit (default)" \
+            "$P_SRV debug_level=4 force_version=tls13" \
+            "$P_CLI debug_level=4" \
+            0 \
+            -c "Sent RecordSizeLimit: $MAX_IN_LEN Bytes"         \
+            -c "RecordSizeLimit: $MAX_IN_LEN Bytes"              \
+            -s "RecordSizeLimit: $MAX_IN_LEN Bytes"              \
+            -s "Sent RecordSizeLimit: $MAX_IN_LEN Bytes"         \
+            -s "Maximum outgoing record payload length is 16383" \
+            -s "Maximum incoming record payload length is 16384"
+
+# End of Record size limit tests
+
 # Tests for renegotiation
 
+# G_NEXT_SRV is used in renegotiation tests becuase of the increased
+# extensions limit since we exceed the limit in G_SRV when we send
+# TLS 1.3 extensions in the initial handshake.
+
 # Renegotiation SCSV always added, regardless of SSL_RENEGOTIATION
 run_test    "Renegotiation: none, for reference" \
             "$P_SRV debug_level=3 exchanges=2 auth_mode=optional" \
@@ -5341,7 +5533,7 @@
 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
 run_test    "Renegotiation: gnutls server strict, client-initiated" \
-            "$G_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.2:%SAFE_RENEGOTIATION" \
+            "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.2:%SAFE_RENEGOTIATION" \
             "$P_CLI debug_level=3 exchanges=1 renegotiation=1 renegotiate=1" \
             0 \
             -c "client hello, adding renegotiation extension" \
@@ -5355,7 +5547,7 @@
 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
 run_test    "Renegotiation: gnutls server unsafe, client-initiated default" \
-            "$G_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.2:%DISABLE_SAFE_RENEGOTIATION" \
+            "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.2:%DISABLE_SAFE_RENEGOTIATION" \
             "$P_CLI debug_level=3 exchanges=1 renegotiation=1 renegotiate=1" \
             1 \
             -c "client hello, adding renegotiation extension" \
@@ -5369,7 +5561,7 @@
 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
 run_test    "Renegotiation: gnutls server unsafe, client-inititated no legacy" \
-            "$G_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.2:%DISABLE_SAFE_RENEGOTIATION" \
+            "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.2:%DISABLE_SAFE_RENEGOTIATION" \
             "$P_CLI debug_level=3 exchanges=1 renegotiation=1 renegotiate=1 \
              allow_legacy=0" \
             1 \
@@ -5384,7 +5576,7 @@
 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
 run_test    "Renegotiation: gnutls server unsafe, client-inititated legacy" \
-            "$G_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.2:%DISABLE_SAFE_RENEGOTIATION" \
+            "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.2:%DISABLE_SAFE_RENEGOTIATION" \
             "$P_CLI debug_level=3 exchanges=1 renegotiation=1 renegotiate=1 \
              allow_legacy=1" \
             0 \
@@ -5445,7 +5637,7 @@
 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
 run_test    "Renegotiation: DTLS, gnutls server, client-initiated" \
-            "$G_SRV -u --mtu 4096" \
+            "$G_NEXT_SRV -u --mtu 4096" \
             "$P_CLI debug_level=3 dtls=1 exchanges=1 renegotiation=1 renegotiate=1" \
             0 \
             -c "client hello, adding renegotiation extension" \
@@ -5460,7 +5652,7 @@
 requires_gnutls
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
 run_test    "Renego ext: gnutls server strict, client default" \
-            "$G_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.2:%SAFE_RENEGOTIATION" \
+            "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.2:%SAFE_RENEGOTIATION" \
             "$P_CLI debug_level=3" \
             0 \
             -c "found renegotiation extension" \
@@ -5470,7 +5662,7 @@
 requires_gnutls
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
 run_test    "Renego ext: gnutls server unsafe, client default" \
-            "$G_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.2:%DISABLE_SAFE_RENEGOTIATION" \
+            "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.2:%DISABLE_SAFE_RENEGOTIATION" \
             "$P_CLI debug_level=3" \
             0 \
             -C "found renegotiation extension" \
@@ -5480,7 +5672,7 @@
 requires_gnutls
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
 run_test    "Renego ext: gnutls server unsafe, client break legacy" \
-            "$G_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.2:%DISABLE_SAFE_RENEGOTIATION" \
+            "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.2:%DISABLE_SAFE_RENEGOTIATION" \
             "$P_CLI debug_level=3 allow_legacy=-1" \
             1 \
             -C "found renegotiation extension" \
diff --git a/tests/suites/test_suite_aes.ctr.data b/tests/suites/test_suite_aes.ctr.data
new file mode 100644
index 0000000..a148236
--- /dev/null
+++ b/tests/suites/test_suite_aes.ctr.data
@@ -0,0 +1,137 @@
+# Test vectors from NIST Special Publication 800-38A 2001 Edition
+# Recommendation for Block Edition Cipher Modes of Operation
+
+# as below, but corrupt the key to check the test catches it
+AES-CTR NIST 128 bad
+aes_ctr:"00000000000000000000000000000000":"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff":"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710":"874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee":1
+
+AES-CTR NIST 128
+aes_ctr:"2b7e151628aed2a6abf7158809cf4f3c":"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff":"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710":"874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee":0
+
+AES-CTR NIST 192
+depends_on:!MBEDTLS_AES_ONLY_128_BIT_KEY_LENGTH
+aes_ctr:"8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b":"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff":"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710":"1abc932417521ca24f2b0459fe7e6e0b090339ec0aa6faefd5ccc2c6f4ce8e941e36b26bd1ebc670d1bd1d665620abf74f78a7f6d29809585a97daec58c6b050":0
+
+AES-CTR NIST 256
+depends_on:!MBEDTLS_AES_ONLY_128_BIT_KEY_LENGTH
+aes_ctr:"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4":"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff":"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710":"601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c52b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6":0
+
+AES-CTR aes_encrypt_ctr_multipart 1 1
+aes_encrypt_ctr_multipart:1:1
+
+AES-CTR aes_encrypt_ctr_multipart 2 1
+aes_encrypt_ctr_multipart:2:1
+
+AES-CTR aes_encrypt_ctr_multipart 2 2
+aes_encrypt_ctr_multipart:2:2
+
+AES-CTR aes_encrypt_ctr_multipart 4 1
+aes_encrypt_ctr_multipart:4:1
+
+AES-CTR aes_encrypt_ctr_multipart 4 2
+aes_encrypt_ctr_multipart:4:2
+
+AES-CTR aes_encrypt_ctr_multipart 15 1
+aes_encrypt_ctr_multipart:15:1
+
+AES-CTR aes_encrypt_ctr_multipart 15 2
+aes_encrypt_ctr_multipart:15:2
+
+AES-CTR aes_encrypt_ctr_multipart 15 8
+aes_encrypt_ctr_multipart:15:8
+
+AES-CTR aes_encrypt_ctr_multipart 15 15
+aes_encrypt_ctr_multipart:15:15
+
+AES-CTR aes_encrypt_ctr_multipart 16 1
+aes_encrypt_ctr_multipart:16:1
+
+AES-CTR aes_encrypt_ctr_multipart 16 2
+aes_encrypt_ctr_multipart:16:2
+
+AES-CTR aes_encrypt_ctr_multipart 16 8
+aes_encrypt_ctr_multipart:16:8
+
+AES-CTR aes_encrypt_ctr_multipart 16 15
+aes_encrypt_ctr_multipart:16:15
+
+AES-CTR aes_encrypt_ctr_multipart 16 16
+aes_encrypt_ctr_multipart:16:16
+
+AES-CTR aes_encrypt_ctr_multipart 17 1
+aes_encrypt_ctr_multipart:17:1
+
+AES-CTR aes_encrypt_ctr_multipart 17 2
+aes_encrypt_ctr_multipart:17:2
+
+AES-CTR aes_encrypt_ctr_multipart 17 8
+aes_encrypt_ctr_multipart:17:8
+
+AES-CTR aes_encrypt_ctr_multipart 17 15
+aes_encrypt_ctr_multipart:17:15
+
+AES-CTR aes_encrypt_ctr_multipart 17 16
+aes_encrypt_ctr_multipart:17:16
+
+AES-CTR aes_encrypt_ctr_multipart 63 1
+aes_encrypt_ctr_multipart:63:1
+
+AES-CTR aes_encrypt_ctr_multipart 63 2
+aes_encrypt_ctr_multipart:63:2
+
+AES-CTR aes_encrypt_ctr_multipart 63 8
+aes_encrypt_ctr_multipart:63:8
+
+AES-CTR aes_encrypt_ctr_multipart 63 15
+aes_encrypt_ctr_multipart:63:15
+
+AES-CTR aes_encrypt_ctr_multipart 63 16
+aes_encrypt_ctr_multipart:63:16
+
+AES-CTR aes_encrypt_ctr_multipart 63 17
+aes_encrypt_ctr_multipart:63:17
+
+AES-CTR aes_encrypt_ctr_multipart 64 1
+aes_encrypt_ctr_multipart:64:1
+
+AES-CTR aes_encrypt_ctr_multipart 64 2
+aes_encrypt_ctr_multipart:64:2
+
+AES-CTR aes_encrypt_ctr_multipart 64 8
+aes_encrypt_ctr_multipart:64:8
+
+AES-CTR aes_encrypt_ctr_multipart 64 15
+aes_encrypt_ctr_multipart:64:15
+
+AES-CTR aes_encrypt_ctr_multipart 64 16
+aes_encrypt_ctr_multipart:64:16
+
+AES-CTR aes_encrypt_ctr_multipart 64 17
+aes_encrypt_ctr_multipart:64:17
+
+AES-CTR aes_encrypt_ctr_multipart 1024 1
+aes_encrypt_ctr_multipart:1024:1
+
+AES-CTR aes_encrypt_ctr_multipart 1024 10
+aes_encrypt_ctr_multipart:1024:10
+
+AES-CTR aes_encrypt_ctr_multipart 1024 15
+aes_encrypt_ctr_multipart:1024:15
+
+AES-CTR aes_encrypt_ctr_multipart 1024 16
+aes_encrypt_ctr_multipart:1024:16
+
+AES-CTR aes_encrypt_ctr_multipart 1024 63
+aes_encrypt_ctr_multipart:1024:63
+
+AES-CTR aes_encrypt_ctr_multipart 1024 64
+aes_encrypt_ctr_multipart:1024:64
+
+AES-CTR aes_encrypt_ctr_multipart 1024 65
+aes_encrypt_ctr_multipart:1024:65
+
+AES-CTR aes_encrypt_ctr_multipart 1024 1023
+aes_encrypt_ctr_multipart:1024:1023
+
+AES-CTR aes_encrypt_ctr_multipart 1024 1024
+aes_encrypt_ctr_multipart:1024:1024
diff --git a/tests/suites/test_suite_aes.function b/tests/suites/test_suite_aes.function
index 2ca3f7f..9118a98 100644
--- a/tests/suites/test_suite_aes.function
+++ b/tests/suites/test_suite_aes.function
@@ -88,6 +88,124 @@
 }
 /* END_CASE */
 
+/* BEGIN_CASE depends_on:MBEDTLS_CIPHER_MODE_CTR */
+void aes_ctr(data_t *key, data_t *ictr, data_t *pt, data_t *ct, int expected)
+{
+    unsigned char *output = NULL;
+    unsigned char ctr[16];
+    unsigned char stream_block[16];
+    mbedtls_aes_context ctx;
+
+    // sanity checks on test input
+    TEST_ASSERT(pt->len == ct->len);
+    TEST_ASSERT(key->len == 16 || key->len == 24 || key->len == 32);
+
+    TEST_CALLOC(output, pt->len);
+
+    // expected result is always success on zero-length input, so skip len == 0 if expecting failure
+    for (size_t len = (expected == 0 ? 0 : 1); len <= pt->len; len++) {
+        for (int i = 0; i < 2; i++) {
+            mbedtls_aes_init(&ctx);
+            TEST_ASSERT(mbedtls_aes_setkey_enc(&ctx, key->x, key->len * 8) == 0);
+
+            memcpy(ctr, ictr->x, 16);
+            memset(stream_block, 0, 16);
+            memset(output, 0, pt->len);
+
+            size_t nc_off = 0;
+
+            if (i == 0) {
+                // encrypt
+                TEST_EQUAL(mbedtls_aes_crypt_ctr(&ctx, len, &nc_off, ctr,
+                                                 stream_block, pt->x, output), 0);
+                TEST_ASSERT(!!memcmp(output, ct->x, len) == expected);
+            } else {
+                // decrypt
+                TEST_EQUAL(mbedtls_aes_crypt_ctr(&ctx, len, &nc_off, ctr,
+                                                 stream_block, ct->x, output), 0);
+                TEST_ASSERT(!!memcmp(output, pt->x, len) == expected);
+            }
+        }
+    }
+
+exit:
+    mbedtls_free(output);
+    mbedtls_aes_free(&ctx);
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:MBEDTLS_CIPHER_MODE_CTR */
+void aes_encrypt_ctr_multipart(int length, int step_size)
+{
+    unsigned char key[16];
+    unsigned char ctr_a[16];
+    unsigned char ctr_b[16];
+    unsigned char stream_block_a[16];
+    unsigned char stream_block_b[16];
+    unsigned char *input = NULL;
+    unsigned char *output_a = NULL;
+    unsigned char *output_b = NULL;
+    mbedtls_aes_context ctx;
+    size_t nc_off_a, nc_off_b;
+
+    TEST_ASSERT(length >= 0);
+    TEST_ASSERT(step_size > 0);
+
+    TEST_CALLOC(input, length);
+    TEST_CALLOC(output_a, length);
+    TEST_CALLOC(output_b, length);
+
+    // set up a random key
+    mbedtls_test_rnd_std_rand(NULL, key, sizeof(key));
+
+    // random input
+    mbedtls_test_rnd_std_rand(NULL, input, length);
+
+
+    // complete encryption in one call
+    mbedtls_aes_init(&ctx);
+    TEST_ASSERT(mbedtls_aes_setkey_enc(&ctx, key, sizeof(key) * 8) == 0);
+    memset(ctr_a, 0, sizeof(ctr_a));
+    memset(stream_block_a, 0, sizeof(stream_block_a));
+    nc_off_a = 0;
+    TEST_EQUAL(mbedtls_aes_crypt_ctr(&ctx, length, &nc_off_a, ctr_a,
+                                     stream_block_a, input, output_a), 0);
+    mbedtls_aes_free(&ctx);
+
+
+    // encrypt in multiple steps of varying size
+    mbedtls_aes_init(&ctx);
+    TEST_ASSERT(mbedtls_aes_setkey_enc(&ctx, key, sizeof(key) * 8) == 0);
+    memset(ctr_b, 0, sizeof(ctr_b));
+    memset(stream_block_b, 0, sizeof(stream_block_b));
+    nc_off_b = 0;
+    size_t remaining = length;
+    unsigned char *ip = input, *op = output_b;
+    while (remaining != 0) {
+        size_t l = MIN(remaining, (size_t) step_size);
+        step_size *= 2;
+        remaining -= l;
+        TEST_EQUAL(mbedtls_aes_crypt_ctr(&ctx, l, &nc_off_b, ctr_b, stream_block_b, ip, op), 0);
+        ip += l;
+        op += l;
+    }
+
+    // finally, validate that multiple steps produced same result as single-pass
+    TEST_MEMORY_COMPARE(output_a, length, output_b, length);
+    TEST_MEMORY_COMPARE(ctr_a, sizeof(ctr_a), ctr_b, sizeof(ctr_b));
+    TEST_MEMORY_COMPARE(stream_block_a, sizeof(stream_block_a),
+                        stream_block_b, sizeof(stream_block_b));
+    TEST_EQUAL(nc_off_a, nc_off_b);
+
+exit:
+    mbedtls_free(input);
+    mbedtls_free(output_a);
+    mbedtls_free(output_b);
+
+    mbedtls_aes_free(&ctx);
+}
+/* END_CASE */
+
 /* BEGIN_CASE depends_on:!MBEDTLS_BLOCK_CIPHER_NO_DECRYPT */
 void aes_decrypt_ecb(data_t *key_str, data_t *src_str,
                      data_t *dst, int setkey_result)
diff --git a/tests/suites/test_suite_constant_time_hmac.function b/tests/suites/test_suite_constant_time_hmac.function
index 9d9aa3c..0e870d8 100644
--- a/tests/suites/test_suite_constant_time_hmac.function
+++ b/tests/suites/test_suite_constant_time_hmac.function
@@ -3,7 +3,7 @@
 #include <mbedtls/constant_time.h>
 #include <mbedtls/md.h>
 #include <constant_time_internal.h>
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 #include <ssl_misc.h>
 
 #include <test/constant_flow.h>
diff --git a/tests/suites/test_suite_ctr_drbg.data b/tests/suites/test_suite_ctr_drbg.data
index 028a07f..89dfb97 100644
--- a/tests/suites/test_suite_ctr_drbg.data
+++ b/tests/suites/test_suite_ctr_drbg.data
@@ -1105,3 +1105,48 @@
 
 CTR_DRBG self test
 ctr_drbg_selftest:
+
+Increment counter rollover
+ctr_increment_rollover
+
+Increment counter 00
+ctr_increment:"00"
+
+Increment counter ff00
+ctr_increment:"ff00"
+
+Increment counter ff0000
+ctr_increment:"ff0000"
+
+Increment counter ff000000
+ctr_increment:"ff000000"
+
+Increment counter ff00000000
+ctr_increment:"ff00000000"
+
+Increment counter ff0000000000
+ctr_increment:"ff0000000000"
+
+Increment counter ff000000000000
+ctr_increment:"ff000000000000"
+
+Increment counter 01
+ctr_increment:"01"
+
+Increment counter ff01
+ctr_increment:"ff01"
+
+Increment counter ff0001
+ctr_increment:"ff0001"
+
+Increment counter ff000001
+ctr_increment:"ff000001"
+
+Increment counter ff00000001
+ctr_increment:"ff00000001"
+
+Increment counter ff0000000001
+ctr_increment:"ff0000000001"
+
+Increment counter ff000000000001
+ctr_increment:"ff000000000001"
diff --git a/tests/suites/test_suite_ctr_drbg.function b/tests/suites/test_suite_ctr_drbg.function
index 1f0a072..425c43e 100644
--- a/tests/suites/test_suite_ctr_drbg.function
+++ b/tests/suites/test_suite_ctr_drbg.function
@@ -2,6 +2,7 @@
 #include "mbedtls/entropy.h"
 #include "mbedtls/ctr_drbg.h"
 #include "string.h"
+#include "ctr.h"
 
 #if defined(MBEDTLS_THREADING_PTHREAD)
 #include "mbedtls/threading.h"
@@ -443,3 +444,75 @@
     AES_PSA_DONE();
 }
 /* END_CASE */
+
+/* BEGIN_CASE */
+void ctr_increment_rollover()
+{
+    uint8_t c[16];
+    uint8_t r[16];
+
+    // test all increments from 2^n - 1 to 2^n (i.e. where we roll over into the next bit)
+    for (int n = 0; n <= 128; n++) {
+        memset(c, 0, 16);
+        memset(r, 0, 16);
+
+        // set least significant (highest address) n bits to 1, i.e. generate (2^n - 1)
+        for (int i = 0; i < n; i++) {
+            int bit = i % 8;
+            int byte = (i / 8);
+            c[15 - byte] |= 1 << bit;
+        }
+        // increment to get 2^n
+        mbedtls_ctr_increment_counter(c);
+
+        // now generate a reference result equal to 2^n - i.e. set only bit (n + 1)
+        // if n == 127, this will not set any bits (i.e. wraps to 0).
+        int bit = n % 8;
+        int byte = n / 8;
+        if (byte < 16) {
+            r[15 - byte] = 1 << bit;
+        }
+
+        TEST_MEMORY_COMPARE(c, 16, r, 16);
+    }
+
+    uint64_t lsb = 10, msb = 20;
+    MBEDTLS_PUT_UINT64_BE(msb, c, 0);
+    MBEDTLS_PUT_UINT64_BE(lsb, c, 8);
+    memcpy(r, c, 16);
+    mbedtls_ctr_increment_counter(c);
+    for (int i = 15; i >= 0; i--) {
+        r[i] += 1;
+        if (r[i] != 0) {
+            break;
+        }
+    }
+    TEST_MEMORY_COMPARE(c, 16, r, 16);
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void ctr_increment(data_t *x)
+{
+    uint8_t c[16];
+    uint8_t r[16];
+
+    // initialise c and r from test argument
+    memset(c, 0, 16);
+    memcpy(c, x->x, x->len);
+    memcpy(r, c, 16);
+
+    // increment c
+    mbedtls_ctr_increment_counter(c);
+    // increment reference
+    for (int i = 15; i >= 0; i--) {
+        r[i] += 1;
+        if (r[i] != 0) {
+            break;
+        }
+    }
+
+    // test that mbedtls_ctr_increment_counter behaviour matches reference
+    TEST_MEMORY_COMPARE(c, 16, r, 16);
+}
+/* END_CASE */
diff --git a/tests/suites/test_suite_ecp.data b/tests/suites/test_suite_ecp.data
index 1002991..01fdc47 100644
--- a/tests/suites/test_suite_ecp.data
+++ b/tests/suites/test_suite_ecp.data
@@ -529,6 +529,24 @@
 depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED
 mbedtls_ecp_check_pub_priv:MBEDTLS_ECP_DP_SECP256R1:"37cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f76822596292":"4ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edfe":MBEDTLS_ECP_DP_SECP256R1:"00f12a1320760270a83cbffd53f6031ef76a5d86c8a204f2c30ca9ebf51f0f0ea7":"37cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f76822596292":"4ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edfe":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
 
+ECP calculate public: secp256r1, good
+depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED
+ecp_calc_public:MBEDTLS_ECP_DP_SECP256R1:"00f12a1320760270a83cbffd53f6031ef76a5d86c8a204f2c30ca9ebf51f0f0ea7":0:"0437cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f768225962924ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edff"
+
+ECP calculate public: secp256r1, private value out of range
+depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED
+ecp_calc_public:MBEDTLS_ECP_DP_SECP256R1:"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff":MBEDTLS_ERR_ECP_INVALID_KEY:""
+
+# Alice's private key from rfc 7748, masked and adjusted for endianness
+# because the test function wants the little-endian representation.
+ECP calculate public: Curve25519, good
+depends_on:MBEDTLS_ECP_DP_CURVE25519_ENABLED
+ecp_calc_public:MBEDTLS_ECP_DP_CURVE25519:"6a2cb91da5fb77b12a99c0eb872f4cdf4566b25172c1163c7da518730a6d0770":0:"8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"
+
+ECP calculate public: Curve25519, private value not masked
+depends_on:MBEDTLS_ECP_DP_CURVE25519_ENABLED
+ecp_calc_public:MBEDTLS_ECP_DP_CURVE25519:"2a2cb91da5fb77b12a99c0eb872f4cdf4566b25172c1163c7da518730a6d0770":MBEDTLS_ERR_ECP_INVALID_KEY:"8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"
+
 ECP gen keypair [#1]
 depends_on:MBEDTLS_ECP_DP_SECP192R1_ENABLED
 mbedtls_ecp_gen_keypair:MBEDTLS_ECP_DP_SECP192R1
@@ -581,6 +599,48 @@
 ECP generate Montgomery key: Curve448, not enough entropy
 genkey_mx_known_answer:447:"4f0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233343536":""
 
+ECP set public key: invalid group (0)
+ecp_set_public_key_group_check:MBEDTLS_ECP_DP_NONE:MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+
+ECP set public key: valid group (secp256r1)
+depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED
+ecp_set_public_key_group_check:MBEDTLS_ECP_DP_SECP256R1:0
+
+ECP set public key: group not supported (secp256r1)
+depends_on:!MBEDTLS_ECP_DP_SECP256R1_ENABLED
+ecp_set_public_key_group_check:MBEDTLS_ECP_DP_SECP256R1:MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+
+ECP set public key: bad group (not in enum)
+ecp_set_public_key_group_check:MBEDTLS_ECP_DP_MAX:MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+
+ECP set public key: good, secp256r1
+depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED
+ecp_set_public_key_good:MBEDTLS_ECP_DP_SECP256R1:"04e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e0e1ff20e1ffe120e1e1e173287170a761308491683e345cacaebb500c96e1a7bbd37772968b2c951f0579"
+
+ECP set public key: good, Curve25519
+depends_on:MBEDTLS_ECP_DP_CURVE25519_ENABLED
+ecp_set_public_key_good:MBEDTLS_ECP_DP_CURVE25519:"8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"
+
+ECP set public key after private: good, secp256r1
+depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED
+ecp_set_public_key_after_private:MBEDTLS_ECP_DP_SECP256R1:"70726976617465206b6579":MBEDTLS_ECP_DP_SECP256R1:"04e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e0e1ff20e1ffe120e1e1e173287170a761308491683e345cacaebb500c96e1a7bbd37772968b2c951f0579"
+
+ECP set public key after private: good, Curve25519
+depends_on:MBEDTLS_ECP_DP_CURVE25519_ENABLED
+ecp_set_public_key_after_private:MBEDTLS_ECP_DP_CURVE25519:"70076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c6a":MBEDTLS_ECP_DP_CURVE25519:"8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"
+
+ECP set public key after private: secp256r1 then secp256k1
+depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_ECP_DP_SECP256K1_ENABLED
+ecp_set_public_key_after_private:MBEDTLS_ECP_DP_SECP256R1:"70726976617465206b6579":MBEDTLS_ECP_DP_SECP256K1:"04e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e0e1ff20e1ffe120e1e1e173287170a761308491683e345cacaebb500c96e1a7bbd37772968b2c951f0579"
+
+ECP set public key after private: secp256r1 then secp384r1
+depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_ECP_DP_SECP384R1_ENABLED
+ecp_set_public_key_after_private:MBEDTLS_ECP_DP_SECP256R1:"70726976617465206b6579":MBEDTLS_ECP_DP_SECP384R1:"04aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaae1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e0e1ff20e1ffe120e1e1e173287170a761308491683e345cacaebb500c96e1a7bbd37772968b2c951f0579bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
+
+ECP set public key after private: secp384r1 then secp256r1
+depends_on:MBEDTLS_ECP_DP_SECP384R1_ENABLED:MBEDTLS_ECP_DP_SECP256R1_ENABLED
+ecp_set_public_key_after_private:MBEDTLS_ECP_DP_SECP384R1:"70726976617465206b6579":MBEDTLS_ECP_DP_SECP256R1:"04e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e0e1ff20e1ffe120e1e1e173287170a761308491683e345cacaebb500c96e1a7bbd37772968b2c951f0579"
+
 ECP read key #1 (short weierstrass, too small)
 depends_on:MBEDTLS_ECP_DP_SECP192R1_ENABLED
 mbedtls_ecp_read_key:MBEDTLS_ECP_DP_SECP192R1:"00":MBEDTLS_ERR_ECP_INVALID_KEY:0
diff --git a/tests/suites/test_suite_ecp.function b/tests/suites/test_suite_ecp.function
index 5751624..295fe7f 100644
--- a/tests/suites/test_suite_ecp.function
+++ b/tests/suites/test_suite_ecp.function
@@ -590,29 +590,41 @@
 {
     mbedtls_ecp_group grp;
     mbedtls_ecp_point P;
+    mbedtls_ecp_keypair key;
     unsigned char buf[256];
     size_t olen;
 
     memset(buf, 0, sizeof(buf));
 
     mbedtls_ecp_group_init(&grp); mbedtls_ecp_point_init(&P);
+    mbedtls_ecp_keypair_init(&key);
 
-    TEST_ASSERT(mbedtls_ecp_group_load(&grp, id) == 0);
+    TEST_EQUAL(mbedtls_ecp_group_load(&grp, id), 0);
 
-    TEST_ASSERT(mbedtls_test_read_mpi(&P.X, x) == 0);
-    TEST_ASSERT(mbedtls_test_read_mpi(&P.Y, y) == 0);
-    TEST_ASSERT(mbedtls_test_read_mpi(&P.Z, z) == 0);
+    TEST_EQUAL(mbedtls_test_read_mpi(&P.X, x), 0);
+    TEST_EQUAL(mbedtls_test_read_mpi(&P.Y, y), 0);
+    TEST_EQUAL(mbedtls_test_read_mpi(&P.Z, z), 0);
 
-    TEST_ASSERT(mbedtls_ecp_point_write_binary(&grp, &P, format,
-                                               &olen, buf, blen) == ret);
-
+    TEST_EQUAL(mbedtls_ecp_point_write_binary(&grp, &P, format,
+                                              &olen, buf, blen), ret);
     if (ret == 0) {
-        TEST_ASSERT(olen <= MBEDTLS_ECP_MAX_PT_LEN);
-        TEST_ASSERT(mbedtls_test_hexcmp(buf, out->x, olen, out->len) == 0);
+        TEST_LE_U(olen, MBEDTLS_ECP_MAX_PT_LEN);
+        ASSERT_COMPARE(buf, olen,
+                       out->x, out->len);
+    }
+
+    memset(buf, 0, blen);
+    TEST_EQUAL(mbedtls_ecp_set_public_key(grp.id, &key, &P), 0);
+    TEST_EQUAL(mbedtls_ecp_write_public_key(&key, format,
+                                            &olen, buf, blen), ret);
+    if (ret == 0) {
+        ASSERT_COMPARE(buf, olen,
+                       out->x, out->len);
     }
 
 exit:
     mbedtls_ecp_group_free(&grp); mbedtls_ecp_point_free(&P);
+    mbedtls_ecp_keypair_free(&key);
 }
 /* END_CASE */
 
@@ -989,6 +1001,41 @@
 /* END_CASE */
 
 /* BEGIN_CASE depends_on:MBEDTLS_ECP_C */
+void ecp_calc_public(int grp_id, data_t *private_data,
+                     int expected_ret, data_t *expected_public)
+{
+    mbedtls_ecp_keypair key;
+    mbedtls_ecp_keypair_init(&key);
+    mbedtls_test_rnd_pseudo_info rnd_info;
+    memset(&rnd_info, 0x00, sizeof(mbedtls_test_rnd_pseudo_info));
+
+    TEST_EQUAL(mbedtls_ecp_group_load(&key.grp, grp_id), 0);
+    TEST_EQUAL(mbedtls_mpi_read_binary(&key.d,
+                                       private_data->x, private_data->len), 0);
+
+    TEST_EQUAL(mbedtls_ecp_keypair_calc_public(&key,
+                                               &mbedtls_test_rnd_pseudo_rand, &rnd_info),
+               expected_ret);
+
+    if (expected_ret == 0) {
+        TEST_EQUAL(mbedtls_ecp_check_pub_priv(&key, &key,
+                                              &mbedtls_test_rnd_pseudo_rand, &rnd_info),
+                   0);
+        unsigned char buf[MBEDTLS_ECP_MAX_PT_LEN];
+        size_t length;
+        TEST_EQUAL(mbedtls_ecp_point_write_binary(&key.grp, &key.Q,
+                                                  MBEDTLS_ECP_PF_UNCOMPRESSED,
+                                                  &length, buf, sizeof(buf)),
+                   0);
+        ASSERT_COMPARE(expected_public->x, expected_public->len, buf, length);
+    }
+
+exit:
+    mbedtls_ecp_keypair_free(&key);
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:MBEDTLS_ECP_C */
 void mbedtls_ecp_gen_keypair(int id)
 {
     mbedtls_ecp_group grp;
@@ -1030,6 +1077,7 @@
                                     &mbedtls_test_rnd_pseudo_rand,
                                     &rnd_info) == 0);
 
+    TEST_EQUAL(mbedtls_ecp_keypair_get_group_id(&key), id);
     TEST_ASSERT(mbedtls_ecp_check_pubkey(&key.grp, &key.Q) == 0);
     TEST_ASSERT(mbedtls_ecp_check_privkey(&key.grp, &key.d) == 0);
 
@@ -1039,22 +1087,123 @@
 /* END_CASE */
 
 /* BEGIN_CASE */
+void ecp_set_public_key_group_check(int grp_id, int expected_ret)
+{
+    mbedtls_ecp_keypair key;
+    mbedtls_ecp_keypair_init(&key);
+    mbedtls_ecp_point Q;
+    mbedtls_ecp_point_init(&Q);
+
+    TEST_EQUAL(mbedtls_ecp_set_public_key(grp_id, &key, &Q),
+               expected_ret);
+
+exit:
+    mbedtls_ecp_keypair_free(&key);
+    mbedtls_ecp_point_free(&Q);
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void ecp_set_public_key_good(int grp_id, data_t *public_data)
+{
+    mbedtls_ecp_keypair key;
+    mbedtls_ecp_keypair_init(&key);
+    mbedtls_ecp_group grp;
+    mbedtls_ecp_group_init(&grp);
+    mbedtls_ecp_point Q;
+    mbedtls_ecp_point_init(&Q);
+
+    TEST_EQUAL(mbedtls_ecp_group_load(&grp, grp_id), 0);
+    TEST_EQUAL(mbedtls_ecp_point_read_binary(&grp, &Q,
+                                             public_data->x, public_data->len),
+               0);
+
+    /* Freshly initialized key */
+    TEST_EQUAL(mbedtls_ecp_set_public_key(grp_id, &key, &Q), 0);
+    TEST_EQUAL(key.grp.id, grp_id);
+    TEST_EQUAL(mbedtls_ecp_point_cmp(&key.Q, &Q), 0);
+
+    /* Key with a public key already set to a different value */
+    TEST_EQUAL(mbedtls_mpi_add_int(&key.Q.X, &key.Q.X, 1), 0);
+    TEST_EQUAL(mbedtls_mpi_add_int(&key.Q.Y, &key.Q.Y, 1), 0);
+    TEST_EQUAL(mbedtls_mpi_add_int(&key.Q.Z, &key.Q.Z, 1), 0);
+    TEST_EQUAL(mbedtls_ecp_set_public_key(grp_id, &key, &Q), 0);
+    TEST_EQUAL(key.grp.id, grp_id);
+    TEST_EQUAL(mbedtls_ecp_point_cmp(&key.Q, &Q), 0);
+
+exit:
+    mbedtls_ecp_keypair_free(&key);
+    mbedtls_ecp_group_free(&grp);
+    mbedtls_ecp_point_free(&Q);
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void ecp_set_public_key_after_private(int private_grp_id, data_t *private_data,
+                                      int public_grp_id, data_t *public_data)
+{
+    mbedtls_ecp_keypair key;
+    mbedtls_ecp_keypair_init(&key);
+    mbedtls_ecp_group grp;
+    mbedtls_ecp_group_init(&grp);
+    mbedtls_ecp_point Q;
+    mbedtls_ecp_point_init(&Q);
+    mbedtls_mpi d;
+    mbedtls_mpi_init(&d);
+
+    TEST_EQUAL(mbedtls_ecp_group_load(&grp, public_grp_id), 0);
+    TEST_EQUAL(mbedtls_ecp_point_read_binary(&grp, &Q,
+                                             public_data->x, public_data->len),
+               0);
+    TEST_EQUAL(mbedtls_ecp_read_key(private_grp_id, &key,
+                                    private_data->x, private_data->len),
+               0);
+    TEST_EQUAL(mbedtls_mpi_copy(&d, &key.d), 0);
+
+    int ret = mbedtls_ecp_set_public_key(public_grp_id, &key, &Q);
+
+    if (private_grp_id == public_grp_id) {
+        TEST_EQUAL(ret, 0);
+        TEST_EQUAL(key.grp.id, public_grp_id);
+        TEST_EQUAL(mbedtls_ecp_point_cmp(&key.Q, &Q), 0);
+        TEST_EQUAL(mbedtls_mpi_cmp_mpi(&d, &key.d), 0);
+    } else {
+        TEST_EQUAL(ret, MBEDTLS_ERR_ECP_BAD_INPUT_DATA);
+    }
+
+exit:
+    mbedtls_ecp_keypair_free(&key);
+    mbedtls_ecp_group_free(&grp);
+    mbedtls_ecp_point_free(&Q);
+    mbedtls_mpi_free(&d);
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
 void mbedtls_ecp_read_key(int grp_id, data_t *in_key, int expected, int canonical)
 {
     int ret = 0;
     mbedtls_ecp_keypair key;
-    mbedtls_ecp_keypair key2;
-
     mbedtls_ecp_keypair_init(&key);
+    mbedtls_ecp_keypair key2;
     mbedtls_ecp_keypair_init(&key2);
 
+    TEST_EQUAL(mbedtls_mpi_lset(&key.Q.X, 1), 0);
+    TEST_EQUAL(mbedtls_mpi_lset(&key.Q.Y, 2), 0);
+    TEST_EQUAL(mbedtls_mpi_lset(&key.Q.Z, 3), 0);
+
     ret = mbedtls_ecp_read_key(grp_id, &key, in_key->x, in_key->len);
     TEST_ASSERT(ret == expected);
 
     if (expected == 0) {
+        TEST_EQUAL(mbedtls_ecp_keypair_get_group_id(&key), grp_id);
         ret = mbedtls_ecp_check_privkey(&key.grp, &key.d);
         TEST_ASSERT(ret == 0);
 
+        TEST_EQUAL(mbedtls_mpi_cmp_int(&key.Q.X, 1), 0);
+        TEST_EQUAL(mbedtls_mpi_cmp_int(&key.Q.Y, 2), 0);
+        TEST_EQUAL(mbedtls_mpi_cmp_int(&key.Q.Z, 3), 0);
+
         if (canonical) {
             unsigned char buf[MBEDTLS_ECP_MAX_BYTES];
 
@@ -1233,6 +1382,28 @@
         TEST_EQUAL(mbedtls_ecp_point_cmp(&key.Q, &export_Q), 0);
         TEST_EQUAL(mbedtls_mpi_cmp_mpi(&key.d, &export_d), 0);
         TEST_EQUAL(mbedtls_ecp_group_cmp(&key.grp, &export_grp), 0);
+
+        /* Check consistency with the group id */
+        TEST_EQUAL(export_grp.id,
+                   mbedtls_ecp_keypair_get_group_id(&key));
+
+        /* Test null arguments: grp only */
+        mbedtls_ecp_group_free(&export_grp);
+        mbedtls_ecp_group_init(&export_grp);
+        TEST_EQUAL(mbedtls_ecp_export(&key, &export_grp, NULL, NULL), 0);
+        TEST_EQUAL(mbedtls_ecp_group_cmp(&key.grp, &export_grp), 0);
+
+        /* Test null arguments: d only */
+        mbedtls_mpi_free(&export_d);
+        mbedtls_mpi_init(&export_d);
+        TEST_EQUAL(mbedtls_ecp_export(&key, NULL, &export_d, NULL), 0);
+        TEST_EQUAL(mbedtls_mpi_cmp_mpi(&key.d, &export_d), 0);
+
+        /* Test null arguments: Q only */
+        mbedtls_ecp_point_free(&export_Q);
+        mbedtls_ecp_point_init(&export_Q);
+        TEST_EQUAL(mbedtls_ecp_export(&key, NULL, NULL, &export_Q), 0);
+        TEST_EQUAL(mbedtls_ecp_point_cmp(&key.Q, &export_Q), 0);
     }
 
 exit:
diff --git a/tests/suites/test_suite_md.function b/tests/suites/test_suite_md.function
index 866ff58..2a885e2 100644
--- a/tests/suites/test_suite_md.function
+++ b/tests/suites/test_suite_md.function
@@ -1,6 +1,6 @@
 /* BEGIN_HEADER */
 #include "mbedtls/md.h"
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 
 #include "mbedtls/oid.h"
 #include "mbedtls/asn1.h"
diff --git a/tests/suites/test_suite_pk.function b/tests/suites/test_suite_pk.function
index 226598c..f054443 100644
--- a/tests/suites/test_suite_pk.function
+++ b/tests/suites/test_suite_pk.function
@@ -16,7 +16,7 @@
  * but the test code generator requires test case data to be valid C code
  * unconditionally (https://github.com/Mbed-TLS/mbedtls/issues/2023). */
 #include "psa/crypto.h"
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 
 /* Used for properly sizing the key buffer in pk_genkey_ec() */
 #include "psa_util_internal.h"
diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data
index e6acfb2..3dd3866 100644
--- a/tests/suites/test_suite_psa_crypto.data
+++ b/tests/suites/test_suite_psa_crypto.data
@@ -684,7 +684,7 @@
 
 PSA import EC keypair: explicit bit-size=255 for secp256r1
 depends_on: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
-import_with_data:"49c9a8c18c4b885638c431cf1df1c994131609b580d4fd43a0cab17db2f13eee":PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1):255:PSA_ERROR_NOT_SUPPORTED
+import_with_data:"49c9a8c18c4b885638c431cf1df1c994131609b580d4fd43a0cab17db2f13eee":PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1):255:PSA_ERROR_INVALID_ARGUMENT
 
 PSA import EC keypair: explicit bit-size=521 for secp521r1 (good)
 depends_on: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_521
@@ -692,7 +692,7 @@
 
 PSA import EC keypair: explicit bit-size=528 for secp521r1 (bad)
 depends_on: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_521
-import_with_data:"01b1b6ad07bb79e7320da59860ea28e055284f6058f279de666e06d435d2af7bda28d99fa47b7dd0963e16b0073078ee8b8a38d966a582f46d19ff95df3ad9685aae":PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1):528:PSA_ERROR_NOT_SUPPORTED
+import_with_data:"01b1b6ad07bb79e7320da59860ea28e055284f6058f279de666e06d435d2af7bda28d99fa47b7dd0963e16b0073078ee8b8a38d966a582f46d19ff95df3ad9685aae":PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1):528:PSA_ERROR_INVALID_ARGUMENT
 
 PSA import EC keypair: explicit bit-size, DER format
 depends_on: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
@@ -716,7 +716,7 @@
 
 PSA import EC keypair: implicit bit-size, not a valid length
 depends_on: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
-import_with_data:"0123456789abcdef0123456789abcdef":PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1):0:PSA_ERROR_NOT_SUPPORTED
+import_with_data:"0123456789abcdef0123456789abcdef":PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1):0:PSA_ERROR_INVALID_ARGUMENT
 
 PSA import EC keypair: secp256r1, all-bits-zero (bad)
 depends_on: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
@@ -7410,3 +7410,62 @@
 
 PSA derive persistent key: HKDF SHA-256, exportable
 persistent_key_load_key_from_storage:"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":PSA_KEY_TYPE_RAW_DATA:1024:PSA_KEY_USAGE_EXPORT:0:DERIVE_KEY
+
+ECP group ID <-> PSA family - SECP192R1
+depends_on:PSA_WANT_ECC_SECP_R1_192
+ecc_conversion_functions:MBEDTLS_ECP_DP_SECP192R1:PSA_ECC_FAMILY_SECP_R1:192
+
+ECP group ID <-> PSA family - SECP224R1
+depends_on:PSA_WANT_ECC_SECP_R1_224
+ecc_conversion_functions:MBEDTLS_ECP_DP_SECP224R1:PSA_ECC_FAMILY_SECP_R1:224
+
+ECP group ID <-> PSA family - SECP256R1
+depends_on:PSA_WANT_ECC_SECP_R1_256
+ecc_conversion_functions:MBEDTLS_ECP_DP_SECP256R1:PSA_ECC_FAMILY_SECP_R1:256
+
+ECP group ID <-> PSA family - SECP384R1
+depends_on:PSA_WANT_ECC_SECP_R1_384
+ecc_conversion_functions:MBEDTLS_ECP_DP_SECP384R1:PSA_ECC_FAMILY_SECP_R1:384
+
+ECP group ID <-> PSA family - SECP521R1
+depends_on:PSA_WANT_ECC_SECP_R1_521
+ecc_conversion_functions:MBEDTLS_ECP_DP_SECP521R1:PSA_ECC_FAMILY_SECP_R1:521
+
+ECP group ID <-> PSA family - BP256R1
+depends_on:PSA_WANT_ECC_BRAINPOOL_P_R1_256
+ecc_conversion_functions:MBEDTLS_ECP_DP_BP256R1:PSA_ECC_FAMILY_BRAINPOOL_P_R1:256
+
+ECP group ID <-> PSA family - BP384R1
+depends_on:PSA_WANT_ECC_BRAINPOOL_P_R1_384
+ecc_conversion_functions:MBEDTLS_ECP_DP_BP384R1:PSA_ECC_FAMILY_BRAINPOOL_P_R1:384
+
+ECP group ID <-> PSA family - BP512R1
+depends_on:PSA_WANT_ECC_BRAINPOOL_P_R1_512
+ecc_conversion_functions:MBEDTLS_ECP_DP_BP512R1:PSA_ECC_FAMILY_BRAINPOOL_P_R1:512
+
+ECP group ID <-> PSA family - CURVE25519
+depends_on:PSA_WANT_ECC_MONTGOMERY_255
+ecc_conversion_functions:MBEDTLS_ECP_DP_CURVE25519:PSA_ECC_FAMILY_MONTGOMERY:255
+
+ECP group ID <-> PSA family - SECP192K1
+depends_on:PSA_WANT_ECC_SECP_K1_192
+ecc_conversion_functions:MBEDTLS_ECP_DP_SECP192K1:PSA_ECC_FAMILY_SECP_K1:192
+
+ECP group ID <-> PSA family - SECP224K1
+depends_on:PSA_WANT_ECC_SECP_K1_224
+ecc_conversion_functions:MBEDTLS_ECP_DP_SECP224K1:PSA_ECC_FAMILY_SECP_K1:224
+
+ECP group ID <-> PSA family - SECP256K1
+depends_on:PSA_WANT_ECC_SECP_K1_256
+ecc_conversion_functions:MBEDTLS_ECP_DP_SECP256K1:PSA_ECC_FAMILY_SECP_K1:256
+
+ECP group ID <-> PSA family - CURVE448
+depends_on:PSA_WANT_ECC_MONTGOMERY_448
+ecc_conversion_functions:MBEDTLS_ECP_DP_CURVE448:PSA_ECC_FAMILY_MONTGOMERY:448
+
+ECP group ID <-> PSA family - Null values
+ecc_conversion_functions:MBEDTLS_ECP_DP_NONE:0:0
+
+ECP group ID <-> PSA family - Wrong values
+ecc_conversion_functions_fail
+
diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function
index 60c4f30..889a3b0 100644
--- a/tests/suites/test_suite_psa_crypto.function
+++ b/tests/suites/test_suite_psa_crypto.function
@@ -6,6 +6,8 @@
 #include "mbedtls/oid.h"
 #include "common.h"
 
+#include "mbedtls/psa_util.h"
+
 /* For MBEDTLS_CTR_DRBG_MAX_REQUEST, knowing that psa_generate_random()
  * uses mbedtls_ctr_drbg internally. */
 #include "mbedtls/ctr_drbg.h"
@@ -9485,6 +9487,45 @@
 }
 /* END_CASE */
 
+/* BEGIN_CASE depends_on:PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY */
+void ecc_conversion_functions(int grp_id_arg, int psa_family_arg, int bits_arg)
+{
+    mbedtls_ecp_group_id grp_id = grp_id_arg;
+    psa_ecc_family_t ecc_family = psa_family_arg;
+    size_t bits = bits_arg;
+    size_t bits_tmp;
+
+    TEST_EQUAL(ecc_family, mbedtls_ecc_group_to_psa(grp_id, &bits_tmp));
+    TEST_EQUAL(bits, bits_tmp);
+    TEST_EQUAL(grp_id, mbedtls_ecc_group_from_psa(ecc_family, bits));
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY */
+void ecc_conversion_functions_fail()
+{
+    size_t bits;
+
+    /* Invalid legacy curve identifiers. */
+    TEST_EQUAL(0, mbedtls_ecc_group_to_psa(MBEDTLS_ECP_DP_MAX, &bits));
+    TEST_EQUAL(0, bits);
+    TEST_EQUAL(0, mbedtls_ecc_group_to_psa(MBEDTLS_ECP_DP_NONE, &bits));
+    TEST_EQUAL(0, bits);
+
+    /* Invalid PSA EC family. */
+    TEST_EQUAL(MBEDTLS_ECP_DP_NONE, mbedtls_ecc_group_from_psa(0, 192));
+    /* Invalid bit-size for a valid EC family. */
+    TEST_EQUAL(MBEDTLS_ECP_DP_NONE, mbedtls_ecc_group_from_psa(PSA_ECC_FAMILY_SECP_R1, 512));
+
+    /* Twisted-Edward curves are not supported yet. */
+    TEST_EQUAL(MBEDTLS_ECP_DP_NONE,
+               mbedtls_ecc_group_from_psa(PSA_ECC_FAMILY_TWISTED_EDWARDS, 255));
+    TEST_EQUAL(MBEDTLS_ECP_DP_NONE,
+               mbedtls_ecc_group_from_psa(PSA_ECC_FAMILY_TWISTED_EDWARDS, 448));
+}
+/* END_CASE */
+
+
 /* BEGIN_CASE */
 void key_agreement_output(int alg_arg,
                           int our_key_type_arg, data_t *our_key_data,
diff --git a/tests/suites/test_suite_x509write.function b/tests/suites/test_suite_x509write.function
index 503d976..1d8b87c 100644
--- a/tests/suites/test_suite_x509write.function
+++ b/tests/suites/test_suite_x509write.function
@@ -7,7 +7,7 @@
 #include "mbedtls/rsa.h"
 #include "mbedtls/asn1write.h"
 #include "mbedtls/pk.h"
-#include "md_psa.h"
+#include "mbedtls/psa_util.h"
 
 #if defined(MBEDTLS_RSA_C)
 int mbedtls_rsa_decrypt_func(void *ctx, size_t *olen,