Merge branch 'development' into buffer-sharing-merge
diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt
index b6ea73e..47ecf17 100644
--- a/library/CMakeLists.txt
+++ b/library/CMakeLists.txt
@@ -231,7 +231,7 @@
 endif(HAIKU)
 
 if(LINK_WITH_PTHREAD)
-    set(libs ${libs} pthread)
+    set(libs ${libs} ${CMAKE_THREAD_LIBS_INIT})
 endif()
 
 if(LINK_WITH_TRUSTED_STORAGE)
diff --git a/library/aesce.c b/library/aesce.c
index eaaa5b5..6a9e0a1 100644
--- a/library/aesce.c
+++ b/library/aesce.c
@@ -334,7 +334,7 @@
      *   - Section 5, Nr = Nk + 6
      *   - Section 5.2, the length of round keys is Nb*(Nr+1)
      */
-    const uint32_t key_len_in_words = key_bit_length / 32;  /* Nk */
+    const size_t key_len_in_words = key_bit_length / 32;    /* Nk */
     const size_t round_key_len_in_words = 4;                /* Nb */
     const size_t rounds_needed = key_len_in_words + 6;      /* Nr */
     const size_t round_keys_len_in_words =
diff --git a/library/alignment.h b/library/alignment.h
index 248f29b..a17001d 100644
--- a/library/alignment.h
+++ b/library/alignment.h
@@ -53,28 +53,45 @@
 typedef uint32_t __packed mbedtls_uint32_unaligned_t;
 typedef uint64_t __packed mbedtls_uint64_unaligned_t;
 #elif defined(MBEDTLS_COMPILER_IS_GCC) && (MBEDTLS_GCC_VERSION >= 40504) && \
-    ((MBEDTLS_GCC_VERSION < 90300) || (!defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS)))
+    ((MBEDTLS_GCC_VERSION < 60300) || (!defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS)))
 /*
- * Old versions of gcc, depending on how the target is specified, may generate a branch to memcpy
- * for calls like `memcpy(dest, src, 4)` rather than generating some LDR or LDRB instructions
- * (similar for stores).
- * Recent versions where unaligned access is not enabled also do this.
+ * gcc may generate a branch to memcpy for calls like `memcpy(dest, src, 4)` rather than
+ * generating some LDR or LDRB instructions (similar for stores).
+ *
+ * This is architecture dependent: x86-64 seems fine even with old gcc; 32-bit Arm
+ * is affected. To keep it simple, we enable for all architectures.
+ *
+ * For versions of gcc < 5.4.0 this issue always happens.
+ * For gcc < 6.3.0, this issue happens at -O0
+ * For all versions, this issue happens iff unaligned access is not supported.
+ *
+ * For gcc 4.x, this implementation will generate byte-by-byte loads even if unaligned access is
+ * supported, which is correct but not optimal.
  *
  * For performance (and code size, in some cases), we want to avoid the branch and just generate
  * some inline load/store instructions since the access is small and constant-size.
  *
  * The manual states:
- * "The aligned attribute specifies a minimum alignment for the variable or structure field,
- * measured in bytes."
- * https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html
+ * "The packed attribute specifies that a variable or structure field should have the smallest
+ *  possible alignment—one byte for a variable"
+ * https://gcc.gnu.org/onlinedocs/gcc-4.5.4/gcc/Variable-Attributes.html
  *
- * Tested with several versions of GCC from 4.5.0 up to 9.3.0
+ * Previous implementations used __attribute__((__aligned__(1)), but had issues with a gcc bug:
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94662
+ *
+ * Tested with several versions of GCC from 4.5.0 up to 13.2.0
  * We don't enable for older than 4.5.0 as this has not been tested.
  */
- #define UINT_UNALIGNED
-typedef uint16_t __attribute__((__aligned__(1))) mbedtls_uint16_unaligned_t;
-typedef uint32_t __attribute__((__aligned__(1))) mbedtls_uint32_unaligned_t;
-typedef uint64_t __attribute__((__aligned__(1))) mbedtls_uint64_unaligned_t;
+ #define UINT_UNALIGNED_STRUCT
+typedef struct {
+    uint16_t x;
+} __attribute__((packed)) mbedtls_uint16_unaligned_t;
+typedef struct {
+    uint32_t x;
+} __attribute__((packed)) mbedtls_uint32_unaligned_t;
+typedef struct {
+    uint64_t x;
+} __attribute__((packed)) mbedtls_uint64_unaligned_t;
  #endif
 
 /*
@@ -101,6 +118,9 @@
 #if defined(UINT_UNALIGNED)
     mbedtls_uint16_unaligned_t *p16 = (mbedtls_uint16_unaligned_t *) p;
     r = *p16;
+#elif defined(UINT_UNALIGNED_STRUCT)
+    mbedtls_uint16_unaligned_t *p16 = (mbedtls_uint16_unaligned_t *) p;
+    r = p16->x;
 #else
     memcpy(&r, p, sizeof(r));
 #endif
@@ -124,6 +144,9 @@
 #if defined(UINT_UNALIGNED)
     mbedtls_uint16_unaligned_t *p16 = (mbedtls_uint16_unaligned_t *) p;
     *p16 = x;
+#elif defined(UINT_UNALIGNED_STRUCT)
+    mbedtls_uint16_unaligned_t *p16 = (mbedtls_uint16_unaligned_t *) p;
+    p16->x = x;
 #else
     memcpy(p, &x, sizeof(x));
 #endif
@@ -147,6 +170,9 @@
 #if defined(UINT_UNALIGNED)
     mbedtls_uint32_unaligned_t *p32 = (mbedtls_uint32_unaligned_t *) p;
     r = *p32;
+#elif defined(UINT_UNALIGNED_STRUCT)
+    mbedtls_uint32_unaligned_t *p32 = (mbedtls_uint32_unaligned_t *) p;
+    r = p32->x;
 #else
     memcpy(&r, p, sizeof(r));
 #endif
@@ -170,6 +196,9 @@
 #if defined(UINT_UNALIGNED)
     mbedtls_uint32_unaligned_t *p32 = (mbedtls_uint32_unaligned_t *) p;
     *p32 = x;
+#elif defined(UINT_UNALIGNED_STRUCT)
+    mbedtls_uint32_unaligned_t *p32 = (mbedtls_uint32_unaligned_t *) p;
+    p32->x = x;
 #else
     memcpy(p, &x, sizeof(x));
 #endif
@@ -193,6 +222,9 @@
 #if defined(UINT_UNALIGNED)
     mbedtls_uint64_unaligned_t *p64 = (mbedtls_uint64_unaligned_t *) p;
     r = *p64;
+#elif defined(UINT_UNALIGNED_STRUCT)
+    mbedtls_uint64_unaligned_t *p64 = (mbedtls_uint64_unaligned_t *) p;
+    r = p64->x;
 #else
     memcpy(&r, p, sizeof(r));
 #endif
@@ -216,6 +248,9 @@
 #if defined(UINT_UNALIGNED)
     mbedtls_uint64_unaligned_t *p64 = (mbedtls_uint64_unaligned_t *) p;
     *p64 = x;
+#elif defined(UINT_UNALIGNED_STRUCT)
+    mbedtls_uint64_unaligned_t *p64 = (mbedtls_uint64_unaligned_t *) p;
+    p64->x = x;
 #else
     memcpy(p, &x, sizeof(x));
 #endif
diff --git a/library/aria.c b/library/aria.c
index ba12578..d9f84cc 100644
--- a/library/aria.c
+++ b/library/aria.c
@@ -25,12 +25,6 @@
 
 #include "mbedtls/platform_util.h"
 
-/* Parameter validation macros */
-#define ARIA_VALIDATE_RET(cond)                                       \
-    MBEDTLS_INTERNAL_VALIDATE_RET(cond, MBEDTLS_ERR_ARIA_BAD_INPUT_DATA)
-#define ARIA_VALIDATE(cond)                                           \
-    MBEDTLS_INTERNAL_VALIDATE(cond)
-
 /*
  * modify byte order: ( A B C D ) -> ( B A D C ), i.e. swap pairs of bytes
  *
@@ -363,8 +357,6 @@
 
     int i;
     uint32_t w[4][4], *w2;
-    ARIA_VALIDATE_RET(ctx != NULL);
-    ARIA_VALIDATE_RET(key != NULL);
 
     if (keybits != 128 && keybits != 192 && keybits != 256) {
         return MBEDTLS_ERR_ARIA_BAD_INPUT_DATA;
@@ -418,8 +410,6 @@
                             const unsigned char *key, unsigned int keybits)
 {
     int i, j, k, ret;
-    ARIA_VALIDATE_RET(ctx != NULL);
-    ARIA_VALIDATE_RET(key != NULL);
 
     ret = mbedtls_aria_setkey_enc(ctx, key, keybits);
     if (ret != 0) {
@@ -455,9 +445,6 @@
     int i;
 
     uint32_t a, b, c, d;
-    ARIA_VALIDATE_RET(ctx != NULL);
-    ARIA_VALIDATE_RET(input != NULL);
-    ARIA_VALIDATE_RET(output != NULL);
 
     a = MBEDTLS_GET_UINT32_LE(input,  0);
     b = MBEDTLS_GET_UINT32_LE(input,  4);
@@ -505,7 +492,6 @@
 /* Initialize context */
 void mbedtls_aria_init(mbedtls_aria_context *ctx)
 {
-    ARIA_VALIDATE(ctx != NULL);
     memset(ctx, 0, sizeof(mbedtls_aria_context));
 }
 
@@ -532,12 +518,9 @@
 {
     unsigned char temp[MBEDTLS_ARIA_BLOCKSIZE];
 
-    ARIA_VALIDATE_RET(ctx != NULL);
-    ARIA_VALIDATE_RET(mode == MBEDTLS_ARIA_ENCRYPT ||
-                      mode == MBEDTLS_ARIA_DECRYPT);
-    ARIA_VALIDATE_RET(length == 0 || input  != NULL);
-    ARIA_VALIDATE_RET(length == 0 || output != NULL);
-    ARIA_VALIDATE_RET(iv != NULL);
+    if ((mode != MBEDTLS_ARIA_ENCRYPT) && (mode != MBEDTLS_ARIA_DECRYPT)) {
+        return MBEDTLS_ERR_ARIA_BAD_INPUT_DATA;
+    }
 
     if (length % MBEDTLS_ARIA_BLOCKSIZE) {
         return MBEDTLS_ERR_ARIA_INVALID_INPUT_LENGTH;
@@ -588,19 +571,14 @@
     unsigned char c;
     size_t n;
 
-    ARIA_VALIDATE_RET(ctx != NULL);
-    ARIA_VALIDATE_RET(mode == MBEDTLS_ARIA_ENCRYPT ||
-                      mode == MBEDTLS_ARIA_DECRYPT);
-    ARIA_VALIDATE_RET(length == 0 || input  != NULL);
-    ARIA_VALIDATE_RET(length == 0 || output != NULL);
-    ARIA_VALIDATE_RET(iv != NULL);
-    ARIA_VALIDATE_RET(iv_off != NULL);
+    if ((mode != MBEDTLS_ARIA_ENCRYPT) && (mode != MBEDTLS_ARIA_DECRYPT)) {
+        return MBEDTLS_ERR_ARIA_BAD_INPUT_DATA;
+    }
 
     n = *iv_off;
 
     /* An overly large value of n can lead to an unlimited
-     * buffer overflow. Therefore, guard against this
-     * outside of parameter validation. */
+     * buffer overflow. */
     if (n >= MBEDTLS_ARIA_BLOCKSIZE) {
         return MBEDTLS_ERR_ARIA_BAD_INPUT_DATA;
     }
@@ -650,17 +628,9 @@
     int c, i;
     size_t n;
 
-    ARIA_VALIDATE_RET(ctx != NULL);
-    ARIA_VALIDATE_RET(length == 0 || input  != NULL);
-    ARIA_VALIDATE_RET(length == 0 || output != NULL);
-    ARIA_VALIDATE_RET(nonce_counter != NULL);
-    ARIA_VALIDATE_RET(stream_block  != NULL);
-    ARIA_VALIDATE_RET(nc_off != NULL);
-
     n = *nc_off;
     /* An overly large value of n can lead to an unlimited
-     * buffer overflow. Therefore, guard against this
-     * outside of parameter validation. */
+     * buffer overflow. */
     if (n >= MBEDTLS_ARIA_BLOCKSIZE) {
         return MBEDTLS_ERR_ARIA_BAD_INPUT_DATA;
     }
diff --git a/library/asn1parse.c b/library/asn1parse.c
index c02b233..e33fdf7 100644
--- a/library/asn1parse.c
+++ b/library/asn1parse.c
@@ -7,7 +7,8 @@
 
 #include "common.h"
 
-#if defined(MBEDTLS_ASN1_PARSE_C) || defined(MBEDTLS_X509_CREATE_C)
+#if defined(MBEDTLS_ASN1_PARSE_C) || defined(MBEDTLS_X509_CREATE_C) || \
+    defined(MBEDTLS_PSA_UTIL_HAVE_ECDSA)
 
 #include "mbedtls/asn1.h"
 #include "mbedtls/platform_util.h"
@@ -73,7 +74,7 @@
 
     return mbedtls_asn1_get_len(p, end, len);
 }
-#endif /* MBEDTLS_ASN1_PARSE_C || MBEDTLS_X509_CREATE_C */
+#endif /* MBEDTLS_ASN1_PARSE_C || MBEDTLS_X509_CREATE_C || MBEDTLS_PSA_UTIL_HAVE_ECDSA */
 
 #if defined(MBEDTLS_ASN1_PARSE_C)
 int mbedtls_asn1_get_bool(unsigned char **p,
diff --git a/library/asn1write.c b/library/asn1write.c
index 114091d..775a9ef 100644
--- a/library/asn1write.c
+++ b/library/asn1write.c
@@ -7,7 +7,8 @@
 
 #include "common.h"
 
-#if defined(MBEDTLS_ASN1_WRITE_C) || defined(MBEDTLS_X509_USE_C)
+#if defined(MBEDTLS_ASN1_WRITE_C) || defined(MBEDTLS_X509_USE_C) || \
+    defined(MBEDTLS_PSA_UTIL_HAVE_ECDSA)
 
 #include "mbedtls/asn1write.h"
 #include "mbedtls/error.h"
@@ -62,7 +63,7 @@
 
     return 1;
 }
-#endif /* MBEDTLS_ASN1_WRITE_C || MBEDTLS_X509_USE_C */
+#endif /* MBEDTLS_ASN1_WRITE_C || MBEDTLS_X509_USE_C || MBEDTLS_PSA_UTIL_HAVE_ECDSA */
 
 #if defined(MBEDTLS_ASN1_WRITE_C)
 static int mbedtls_asn1_write_len_and_tag(unsigned char **p,
diff --git a/library/bignum.c b/library/bignum.c
index 1869137..c45fd5b 100644
--- a/library/bignum.c
+++ b/library/bignum.c
@@ -37,10 +37,18 @@
 
 #include "mbedtls/platform.h"
 
-#define MPI_VALIDATE_RET(cond)                                       \
-    MBEDTLS_INTERNAL_VALIDATE_RET(cond, MBEDTLS_ERR_MPI_BAD_INPUT_DATA)
-#define MPI_VALIDATE(cond)                                           \
-    MBEDTLS_INTERNAL_VALIDATE(cond)
+
+
+/*
+ * Conditionally select an MPI sign in constant time.
+ * (MPI sign is the field s in mbedtls_mpi. It is unsigned short and only 1 and -1 are valid
+ * values.)
+ */
+static inline signed short mbedtls_ct_mpi_sign_if(mbedtls_ct_condition_t cond,
+                                                  signed short sign1, signed short sign2)
+{
+    return (signed short) mbedtls_ct_uint_if(cond, sign1 + 1, sign2 + 1) - 1;
+}
 
 /*
  * Compare signed values in constant time
@@ -51,10 +59,6 @@
 {
     mbedtls_ct_condition_t different_sign, X_is_negative, Y_is_negative, result;
 
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(Y != NULL);
-    MPI_VALIDATE_RET(ret != NULL);
-
     if (X->n != Y->n) {
         return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
     }
@@ -115,15 +119,13 @@
                                  unsigned char assign)
 {
     int ret = 0;
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(Y != NULL);
 
     MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, Y->n));
 
     {
         mbedtls_ct_condition_t do_assign = mbedtls_ct_bool(assign);
 
-        X->s = (int) mbedtls_ct_uint_if(do_assign, Y->s, X->s);
+        X->s = mbedtls_ct_mpi_sign_if(do_assign, Y->s, X->s);
 
         mbedtls_mpi_core_cond_assign(X->p, Y->p, Y->n, do_assign);
 
@@ -149,8 +151,6 @@
 {
     int ret = 0;
     int s;
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(Y != NULL);
 
     if (X == Y) {
         return 0;
@@ -162,8 +162,8 @@
     MBEDTLS_MPI_CHK(mbedtls_mpi_grow(Y, X->n));
 
     s = X->s;
-    X->s = (int) mbedtls_ct_uint_if(do_swap, Y->s, X->s);
-    Y->s = (int) mbedtls_ct_uint_if(do_swap, s, Y->s);
+    X->s = mbedtls_ct_mpi_sign_if(do_swap, Y->s, X->s);
+    Y->s = mbedtls_ct_mpi_sign_if(do_swap, s, Y->s);
 
     mbedtls_mpi_core_cond_swap(X->p, Y->p, X->n, do_swap);
 
@@ -179,8 +179,6 @@
  */
 void mbedtls_mpi_init(mbedtls_mpi *X)
 {
-    MPI_VALIDATE(X != NULL);
-
     X->s = 1;
     X->n = 0;
     X->p = NULL;
@@ -210,7 +208,6 @@
 int mbedtls_mpi_grow(mbedtls_mpi *X, size_t nblimbs)
 {
     mbedtls_mpi_uint *p;
-    MPI_VALIDATE_RET(X != NULL);
 
     if (nblimbs > MBEDTLS_MPI_MAX_LIMBS) {
         return MBEDTLS_ERR_MPI_ALLOC_FAILED;
@@ -243,7 +240,6 @@
 {
     mbedtls_mpi_uint *p;
     size_t i;
-    MPI_VALIDATE_RET(X != NULL);
 
     if (nblimbs > MBEDTLS_MPI_MAX_LIMBS) {
         return MBEDTLS_ERR_MPI_ALLOC_FAILED;
@@ -305,15 +301,12 @@
  * This function is not constant-time. Leading zeros in Y may be removed.
  *
  * Ensure that X does not shrink. This is not guaranteed by the public API,
- * but some code in the bignum module relies on this property, for example
- * in mbedtls_mpi_exp_mod().
+ * but some code in the bignum module might still rely on this property.
  */
 int mbedtls_mpi_copy(mbedtls_mpi *X, const mbedtls_mpi *Y)
 {
     int ret = 0;
     size_t i;
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(Y != NULL);
 
     if (X == Y) {
         return 0;
@@ -355,8 +348,6 @@
 void mbedtls_mpi_swap(mbedtls_mpi *X, mbedtls_mpi *Y)
 {
     mbedtls_mpi T;
-    MPI_VALIDATE(X != NULL);
-    MPI_VALIDATE(Y != NULL);
 
     memcpy(&T,  X, sizeof(mbedtls_mpi));
     memcpy(X,  Y, sizeof(mbedtls_mpi));
@@ -385,7 +376,6 @@
 int mbedtls_mpi_lset(mbedtls_mpi *X, mbedtls_mpi_sint z)
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    MPI_VALIDATE_RET(X != NULL);
 
     MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, 1));
     memset(X->p, 0, X->n * ciL);
@@ -403,8 +393,6 @@
  */
 int mbedtls_mpi_get_bit(const mbedtls_mpi *X, size_t pos)
 {
-    MPI_VALIDATE_RET(X != NULL);
-
     if (X->n * biL <= pos) {
         return 0;
     }
@@ -420,7 +408,6 @@
     int ret = 0;
     size_t off = pos / biL;
     size_t idx = pos % biL;
-    MPI_VALIDATE_RET(X != NULL);
 
     if (val != 0 && val != 1) {
         return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
@@ -448,7 +435,6 @@
 size_t mbedtls_mpi_lsb(const mbedtls_mpi *X)
 {
     size_t i;
-    MBEDTLS_INTERNAL_VALIDATE_RET(X != NULL, 0);
 
 #if defined(__has_builtin)
 #if (MBEDTLS_MPI_UINT_MAX == UINT_MAX) && __has_builtin(__builtin_ctz)
@@ -530,8 +516,6 @@
     int sign = 1;
     mbedtls_mpi_uint d;
     mbedtls_mpi T;
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(s != NULL);
 
     if (radix < 2 || radix > 16) {
         return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
@@ -634,9 +618,6 @@
     size_t n;
     char *p;
     mbedtls_mpi T;
-    MPI_VALIDATE_RET(X    != NULL);
-    MPI_VALIDATE_RET(olen != NULL);
-    MPI_VALIDATE_RET(buflen == 0 || buf != NULL);
 
     if (radix < 2 || radix > 16) {
         return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
@@ -726,9 +707,6 @@
      */
     char s[MBEDTLS_MPI_RW_BUFFER_SIZE];
 
-    MPI_VALIDATE_RET(X   != NULL);
-    MPI_VALIDATE_RET(fin != NULL);
-
     if (radix < 2 || radix > 16) {
         return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
     }
@@ -772,7 +750,6 @@
      * newline characters and '\0'
      */
     char s[MBEDTLS_MPI_RW_BUFFER_SIZE];
-    MPI_VALIDATE_RET(X != NULL);
 
     if (radix < 2 || radix > 16) {
         return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
@@ -844,9 +821,6 @@
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     const size_t limbs = CHARS_TO_LIMBS(buflen);
 
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(buflen == 0 || buf != NULL);
-
     /* Ensure that target MPI has exactly the necessary number of limbs */
     MBEDTLS_MPI_CHK(mbedtls_mpi_resize_clear(X, limbs));
 
@@ -887,7 +861,6 @@
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     size_t i;
-    MPI_VALIDATE_RET(X != NULL);
 
     i = mbedtls_mpi_bitlen(X) + count;
 
@@ -908,7 +881,6 @@
  */
 int mbedtls_mpi_shift_r(mbedtls_mpi *X, size_t count)
 {
-    MPI_VALIDATE_RET(X != NULL);
     if (X->n != 0) {
         mbedtls_mpi_core_shift_r(X->p, X->n, count);
     }
@@ -921,8 +893,6 @@
 int mbedtls_mpi_cmp_abs(const mbedtls_mpi *X, const mbedtls_mpi *Y)
 {
     size_t i, j;
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(Y != NULL);
 
     for (i = X->n; i > 0; i--) {
         if (X->p[i - 1] != 0) {
@@ -964,8 +934,6 @@
 int mbedtls_mpi_cmp_mpi(const mbedtls_mpi *X, const mbedtls_mpi *Y)
 {
     size_t i, j;
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(Y != NULL);
 
     for (i = X->n; i > 0; i--) {
         if (X->p[i - 1] != 0) {
@@ -1016,7 +984,6 @@
 {
     mbedtls_mpi Y;
     mbedtls_mpi_uint p[1];
-    MPI_VALIDATE_RET(X != NULL);
 
     *p  = mpi_sint_abs(z);
     Y.s = TO_SIGN(z);
@@ -1035,9 +1002,6 @@
     size_t j;
     mbedtls_mpi_uint *p;
     mbedtls_mpi_uint c;
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(A != NULL);
-    MPI_VALIDATE_RET(B != NULL);
 
     if (X == B) {
         const mbedtls_mpi *T = A; A = X; B = T;
@@ -1098,9 +1062,6 @@
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     size_t n;
     mbedtls_mpi_uint carry;
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(A != NULL);
-    MPI_VALIDATE_RET(B != NULL);
 
     for (n = B->n; n > 0; n--) {
         if (B->p[n - 1] != 0) {
@@ -1152,9 +1113,6 @@
                        int flip_B)
 {
     int ret, s;
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(A != NULL);
-    MPI_VALIDATE_RET(B != NULL);
 
     s = A->s;
     if (A->s * B->s * flip_B < 0) {
@@ -1203,8 +1161,6 @@
 {
     mbedtls_mpi B;
     mbedtls_mpi_uint p[1];
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(A != NULL);
 
     p[0] = mpi_sint_abs(b);
     B.s = TO_SIGN(b);
@@ -1221,8 +1177,6 @@
 {
     mbedtls_mpi B;
     mbedtls_mpi_uint p[1];
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(A != NULL);
 
     p[0] = mpi_sint_abs(b);
     B.s = TO_SIGN(b);
@@ -1241,9 +1195,6 @@
     size_t i, j;
     mbedtls_mpi TA, TB;
     int result_is_zero = 0;
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(A != NULL);
-    MPI_VALIDATE_RET(B != NULL);
 
     mbedtls_mpi_init(&TA);
     mbedtls_mpi_init(&TB);
@@ -1300,9 +1251,6 @@
  */
 int mbedtls_mpi_mul_int(mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_uint b)
 {
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(A != NULL);
-
     size_t n = A->n;
     while (n > 0 && A->p[n - 1] == 0) {
         --n;
@@ -1448,8 +1396,6 @@
     size_t i, n, t, k;
     mbedtls_mpi X, Y, Z, T1, T2;
     mbedtls_mpi_uint TP2[3];
-    MPI_VALIDATE_RET(A != NULL);
-    MPI_VALIDATE_RET(B != NULL);
 
     if (mbedtls_mpi_cmp_int(B, 0) == 0) {
         return MBEDTLS_ERR_MPI_DIVISION_BY_ZERO;
@@ -1572,7 +1518,6 @@
 {
     mbedtls_mpi B;
     mbedtls_mpi_uint p[1];
-    MPI_VALIDATE_RET(A != NULL);
 
     p[0] = mpi_sint_abs(b);
     B.s = TO_SIGN(b);
@@ -1588,9 +1533,6 @@
 int mbedtls_mpi_mod_mpi(mbedtls_mpi *R, const mbedtls_mpi *A, const mbedtls_mpi *B)
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    MPI_VALIDATE_RET(R != NULL);
-    MPI_VALIDATE_RET(A != NULL);
-    MPI_VALIDATE_RET(B != NULL);
 
     if (mbedtls_mpi_cmp_int(B, 0) < 0) {
         return MBEDTLS_ERR_MPI_NEGATIVE_VALUE;
@@ -1618,8 +1560,6 @@
 {
     size_t i;
     mbedtls_mpi_uint x, y, z;
-    MPI_VALIDATE_RET(r != NULL);
-    MPI_VALIDATE_RET(A != NULL);
 
     if (b == 0) {
         return MBEDTLS_ERR_MPI_DIVISION_BY_ZERO;
@@ -1670,103 +1610,11 @@
     return 0;
 }
 
-static void mpi_montg_init(mbedtls_mpi_uint *mm, const mbedtls_mpi *N)
-{
-    *mm = mbedtls_mpi_core_montmul_init(N->p);
-}
-
-/** Montgomery multiplication: A = A * B * R^-1 mod N  (HAC 14.36)
- *
- * \param[in,out]   A   One of the numbers to multiply.
- *                      It must have at least as many limbs as N
- *                      (A->n >= N->n), and any limbs beyond n are ignored.
- *                      On successful completion, A contains the result of
- *                      the multiplication A * B * R^-1 mod N where
- *                      R = (2^ciL)^n.
- * \param[in]       B   One of the numbers to multiply.
- *                      It must be nonzero and must not have more limbs than N
- *                      (B->n <= N->n).
- * \param[in]       N   The modulus. \p N must be odd.
- * \param           mm  The value calculated by `mpi_montg_init(&mm, N)`.
- *                      This is -N^-1 mod 2^ciL.
- * \param[in,out]   T   A bignum for temporary storage.
- *                      It must be at least twice the limb size of N plus 1
- *                      (T->n >= 2 * N->n + 1).
- *                      Its initial content is unused and
- *                      its final content is indeterminate.
- *                      It does not get reallocated.
- */
-static void mpi_montmul(mbedtls_mpi *A, const mbedtls_mpi *B,
-                        const mbedtls_mpi *N, mbedtls_mpi_uint mm,
-                        mbedtls_mpi *T)
-{
-    mbedtls_mpi_core_montmul(A->p, A->p, B->p, B->n, N->p, N->n, mm, T->p);
-}
-
-/*
- * Montgomery reduction: A = A * R^-1 mod N
- *
- * See mpi_montmul() regarding constraints and guarantees on the parameters.
- */
-static void mpi_montred(mbedtls_mpi *A, const mbedtls_mpi *N,
-                        mbedtls_mpi_uint mm, mbedtls_mpi *T)
-{
-    mbedtls_mpi_uint z = 1;
-    mbedtls_mpi U;
-    U.n = 1;
-    U.s = 1;
-    U.p = &z;
-
-    mpi_montmul(A, &U, N, mm, T);
-}
-
-/**
- * Select an MPI from a table without leaking the index.
- *
- * This is functionally equivalent to mbedtls_mpi_copy(R, T[idx]) except it
- * reads the entire table in order to avoid leaking the value of idx to an
- * attacker able to observe memory access patterns.
- *
- * \param[out] R        Where to write the selected MPI.
- * \param[in] T         The table to read from.
- * \param[in] T_size    The number of elements in the table.
- * \param[in] idx       The index of the element to select;
- *                      this must satisfy 0 <= idx < T_size.
- *
- * \return \c 0 on success, or a negative error code.
- */
-static int mpi_select(mbedtls_mpi *R, const mbedtls_mpi *T, size_t T_size, size_t idx)
-{
-    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-
-    for (size_t i = 0; i < T_size; i++) {
-        MBEDTLS_MPI_CHK(mbedtls_mpi_safe_cond_assign(R, &T[i],
-                                                     (unsigned char) mbedtls_ct_uint_eq(i, idx)));
-    }
-cleanup:
-    return ret;
-}
-
-/*
- * Sliding-window exponentiation: X = A^E mod N  (HAC 14.85)
- */
 int mbedtls_mpi_exp_mod(mbedtls_mpi *X, const mbedtls_mpi *A,
                         const mbedtls_mpi *E, const mbedtls_mpi *N,
                         mbedtls_mpi *prec_RR)
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    size_t window_bitsize;
-    size_t i, j, nblimbs;
-    size_t bufsize, nbits;
-    size_t exponent_bits_in_window = 0;
-    mbedtls_mpi_uint ei, mm, state;
-    mbedtls_mpi RR, T, W[(size_t) 1 << MBEDTLS_MPI_WINDOW_SIZE], WW, Apos;
-    int neg;
-
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(A != NULL);
-    MPI_VALIDATE_RET(E != NULL);
-    MPI_VALIDATE_RET(N != NULL);
 
     if (mbedtls_mpi_cmp_int(N, 0) <= 0 || (N->p[0] & 1) == 0) {
         return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
@@ -1782,261 +1630,88 @@
     }
 
     /*
-     * Init temps and window size
+     * Ensure that the exponent that we are passing to the core is not NULL.
      */
-    mpi_montg_init(&mm, N);
-    mbedtls_mpi_init(&RR); mbedtls_mpi_init(&T);
-    mbedtls_mpi_init(&Apos);
-    mbedtls_mpi_init(&WW);
-    memset(W, 0, sizeof(W));
-
-    i = mbedtls_mpi_bitlen(E);
-
-    window_bitsize = (i > 671) ? 6 : (i > 239) ? 5 :
-                     (i >  79) ? 4 : (i >  23) ? 3 : 1;
-
-#if (MBEDTLS_MPI_WINDOW_SIZE < 6)
-    if (window_bitsize > MBEDTLS_MPI_WINDOW_SIZE) {
-        window_bitsize = MBEDTLS_MPI_WINDOW_SIZE;
+    if (E->n == 0) {
+        ret = mbedtls_mpi_lset(X, 1);
+        return ret;
     }
-#endif
-
-    const size_t w_table_used_size = (size_t) 1 << window_bitsize;
 
     /*
-     * This function is not constant-trace: its memory accesses depend on the
-     * exponent value. To defend against timing attacks, callers (such as RSA
-     * and DHM) should use exponent blinding. However this is not enough if the
-     * adversary can find the exponent in a single trace, so this function
-     * takes extra precautions against adversaries who can observe memory
-     * access patterns.
-     *
-     * This function performs a series of multiplications by table elements and
-     * squarings, and we want the prevent the adversary from finding out which
-     * table element was used, and from distinguishing between multiplications
-     * and squarings. Firstly, when multiplying by an element of the window
-     * W[i], we do a constant-trace table lookup to obfuscate i. This leaves
-     * squarings as having a different memory access patterns from other
-     * multiplications. So secondly, we put the accumulator in the table as
-     * well, and also do a constant-trace table lookup to multiply by the
-     * accumulator which is W[x_index].
-     *
-     * This way, all multiplications take the form of a lookup-and-multiply.
-     * The number of lookup-and-multiply operations inside each iteration of
-     * the main loop still depends on the bits of the exponent, but since the
-     * other operations in the loop don't have an easily recognizable memory
-     * trace, an adversary is unlikely to be able to observe the exact
-     * patterns.
-     *
-     * An adversary may still be able to recover the exponent if they can
-     * observe both memory accesses and branches. However, branch prediction
-     * exploitation typically requires many traces of execution over the same
-     * data, which is defeated by randomized blinding.
+     * Allocate working memory for mbedtls_mpi_core_exp_mod()
      */
-    const size_t x_index = 0;
-    mbedtls_mpi_init(&W[x_index]);
-
-    j = N->n + 1;
-    /* All W[i] including the accumulator must have at least N->n limbs for
-     * the mpi_montmul() and mpi_montred() calls later. Here we ensure that
-     * W[1] and the accumulator W[x_index] are large enough. later we'll grow
-     * other W[i] to the same length. They must not be shrunk midway through
-     * this function!
-     */
-    MBEDTLS_MPI_CHK(mbedtls_mpi_grow(&W[x_index], j));
-    MBEDTLS_MPI_CHK(mbedtls_mpi_grow(&W[1],  j));
-    MBEDTLS_MPI_CHK(mbedtls_mpi_grow(&T, j * 2));
-
-    /*
-     * Compensate for negative A (and correct at the end)
-     */
-    neg = (A->s == -1);
-    if (neg) {
-        MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&Apos, A));
-        Apos.s = 1;
-        A = &Apos;
+    size_t T_limbs = mbedtls_mpi_core_exp_mod_working_limbs(N->n, E->n);
+    mbedtls_mpi_uint *T = (mbedtls_mpi_uint *) mbedtls_calloc(T_limbs, sizeof(mbedtls_mpi_uint));
+    if (T == NULL) {
+        return MBEDTLS_ERR_MPI_ALLOC_FAILED;
     }
 
+    mbedtls_mpi RR;
+    mbedtls_mpi_init(&RR);
+
     /*
      * If 1st call, pre-compute R^2 mod N
      */
     if (prec_RR == NULL || prec_RR->p == NULL) {
-        MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&RR, 1));
-        MBEDTLS_MPI_CHK(mbedtls_mpi_shift_l(&RR, N->n * 2 * biL));
-        MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&RR, &RR, N));
+        MBEDTLS_MPI_CHK(mbedtls_mpi_core_get_mont_r2_unsafe(&RR, N));
 
         if (prec_RR != NULL) {
-            memcpy(prec_RR, &RR, sizeof(mbedtls_mpi));
+            *prec_RR = RR;
         }
     } else {
-        memcpy(&RR, prec_RR, sizeof(mbedtls_mpi));
+        MBEDTLS_MPI_CHK(mbedtls_mpi_grow(prec_RR, N->n));
+        RR = *prec_RR;
     }
 
     /*
-     * W[1] = A * R^2 * R^-1 mod N = A * R mod N
+     * To preserve constness we need to make a copy of A. Using X for this to
+     * save memory.
      */
-    if (mbedtls_mpi_cmp_mpi(A, N) >= 0) {
-        MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&W[1], A, N));
-        /* This should be a no-op because W[1] is already that large before
-         * mbedtls_mpi_mod_mpi(), but it's necessary to avoid an overflow
-         * in mpi_montmul() below, so let's make sure. */
-        MBEDTLS_MPI_CHK(mbedtls_mpi_grow(&W[1], N->n + 1));
-    } else {
-        MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&W[1], A));
-    }
-
-    /* Note that this is safe because W[1] always has at least N->n limbs
-     * (it grew above and was preserved by mbedtls_mpi_copy()). */
-    mpi_montmul(&W[1], &RR, N, mm, &T);
+    MBEDTLS_MPI_CHK(mbedtls_mpi_copy(X, A));
 
     /*
-     * W[x_index] = R^2 * R^-1 mod N = R mod N
+     * Compensate for negative A (and correct at the end).
      */
-    MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&W[x_index], &RR));
-    mpi_montred(&W[x_index], N, mm, &T);
+    X->s = 1;
 
-
-    if (window_bitsize > 1) {
-        /*
-         * W[i] = W[1] ^ i
-         *
-         * The first bit of the sliding window is always 1 and therefore we
-         * only need to store the second half of the table.
-         *
-         * (There are two special elements in the table: W[0] for the
-         * accumulator/result and W[1] for A in Montgomery form. Both of these
-         * are already set at this point.)
-         */
-        j = w_table_used_size / 2;
-
-        MBEDTLS_MPI_CHK(mbedtls_mpi_grow(&W[j], N->n + 1));
-        MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&W[j], &W[1]));
-
-        for (i = 0; i < window_bitsize - 1; i++) {
-            mpi_montmul(&W[j], &W[j], N, mm, &T);
-        }
-
-        /*
-         * W[i] = W[i - 1] * W[1]
-         */
-        for (i = j + 1; i < w_table_used_size; i++) {
-            MBEDTLS_MPI_CHK(mbedtls_mpi_grow(&W[i], N->n + 1));
-            MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&W[i], &W[i - 1]));
-
-            mpi_montmul(&W[i], &W[1], N, mm, &T);
-        }
+    /*
+     * Make sure that X is in a form that is safe for consumption by
+     * the core functions.
+     *
+     * - The core functions will not touch the limbs of X above N->n. The
+     *   result will be correct if those limbs are 0, which the mod call
+     *   ensures.
+     * - Also, X must have at least as many limbs as N for the calls to the
+     *   core functions.
+     */
+    if (mbedtls_mpi_cmp_mpi(X, N) >= 0) {
+        MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(X, X, N));
     }
+    MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, N->n));
 
-    nblimbs = E->n;
-    bufsize = 0;
-    nbits   = 0;
-    state   = 0;
-
-    while (1) {
-        if (bufsize == 0) {
-            if (nblimbs == 0) {
-                break;
-            }
-
-            nblimbs--;
-
-            bufsize = sizeof(mbedtls_mpi_uint) << 3;
-        }
-
-        bufsize--;
-
-        ei = (E->p[nblimbs] >> bufsize) & 1;
-
-        /*
-         * skip leading 0s
-         */
-        if (ei == 0 && state == 0) {
-            continue;
-        }
-
-        if (ei == 0 && state == 1) {
-            /*
-             * out of window, square W[x_index]
-             */
-            MBEDTLS_MPI_CHK(mpi_select(&WW, W, w_table_used_size, x_index));
-            mpi_montmul(&W[x_index], &WW, N, mm, &T);
-            continue;
-        }
-
-        /*
-         * add ei to current window
-         */
-        state = 2;
-
-        nbits++;
-        exponent_bits_in_window |= (ei << (window_bitsize - nbits));
-
-        if (nbits == window_bitsize) {
-            /*
-             * W[x_index] = W[x_index]^window_bitsize R^-1 mod N
-             */
-            for (i = 0; i < window_bitsize; i++) {
-                MBEDTLS_MPI_CHK(mpi_select(&WW, W, w_table_used_size,
-                                           x_index));
-                mpi_montmul(&W[x_index], &WW, N, mm, &T);
-            }
-
-            /*
-             * W[x_index] = W[x_index] * W[exponent_bits_in_window] R^-1 mod N
-             */
-            MBEDTLS_MPI_CHK(mpi_select(&WW, W, w_table_used_size,
-                                       exponent_bits_in_window));
-            mpi_montmul(&W[x_index], &WW, N, mm, &T);
-
-            state--;
-            nbits = 0;
-            exponent_bits_in_window = 0;
-        }
+    /*
+     * Convert to and from Montgomery around mbedtls_mpi_core_exp_mod().
+     */
+    {
+        mbedtls_mpi_uint mm = mbedtls_mpi_core_montmul_init(N->p);
+        mbedtls_mpi_core_to_mont_rep(X->p, X->p, N->p, N->n, mm, RR.p, T);
+        mbedtls_mpi_core_exp_mod(X->p, X->p, N->p, N->n, E->p, E->n, RR.p, T);
+        mbedtls_mpi_core_from_mont_rep(X->p, X->p, N->p, N->n, mm, T);
     }
 
     /*
-     * process the remaining bits
+     * Correct for negative A.
      */
-    for (i = 0; i < nbits; i++) {
-        MBEDTLS_MPI_CHK(mpi_select(&WW, W, w_table_used_size, x_index));
-        mpi_montmul(&W[x_index], &WW, N, mm, &T);
+    if (A->s == -1 && (E->p[0] & 1) != 0) {
+        mbedtls_ct_condition_t is_x_non_zero = mbedtls_mpi_core_check_zero_ct(X->p, X->n);
+        X->s = mbedtls_ct_mpi_sign_if(is_x_non_zero, -1, 1);
 
-        exponent_bits_in_window <<= 1;
-
-        if ((exponent_bits_in_window & ((size_t) 1 << window_bitsize)) != 0) {
-            MBEDTLS_MPI_CHK(mpi_select(&WW, W, w_table_used_size, 1));
-            mpi_montmul(&W[x_index], &WW, N, mm, &T);
-        }
+        MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(X, N, X));
     }
 
-    /*
-     * W[x_index] = A^E * R * R^-1 mod N = A^E mod N
-     */
-    mpi_montred(&W[x_index], N, mm, &T);
-
-    if (neg && E->n != 0 && (E->p[0] & 1) != 0) {
-        W[x_index].s = -1;
-        MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&W[x_index], N, &W[x_index]));
-    }
-
-    /*
-     * Load the result in the output variable.
-     */
-    MBEDTLS_MPI_CHK(mbedtls_mpi_copy(X, &W[x_index]));
-
 cleanup:
 
-    /* The first bit of the sliding window is always 1 and therefore the first
-     * half of the table was unused. */
-    for (i = w_table_used_size/2; i < w_table_used_size; i++) {
-        mbedtls_mpi_free(&W[i]);
-    }
-
-    mbedtls_mpi_free(&W[x_index]);
-    mbedtls_mpi_free(&W[1]);
-    mbedtls_mpi_free(&T);
-    mbedtls_mpi_free(&Apos);
-    mbedtls_mpi_free(&WW);
+    mbedtls_mpi_zeroize_and_free(T, T_limbs);
 
     if (prec_RR == NULL || prec_RR->p == NULL) {
         mbedtls_mpi_free(&RR);
@@ -2054,10 +1729,6 @@
     size_t lz, lzt;
     mbedtls_mpi TA, TB;
 
-    MPI_VALIDATE_RET(G != NULL);
-    MPI_VALIDATE_RET(A != NULL);
-    MPI_VALIDATE_RET(B != NULL);
-
     mbedtls_mpi_init(&TA); mbedtls_mpi_init(&TB);
 
     MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&TA, A));
@@ -2168,9 +1839,6 @@
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     const size_t limbs = CHARS_TO_LIMBS(size);
 
-    MPI_VALIDATE_RET(X     != NULL);
-    MPI_VALIDATE_RET(f_rng != NULL);
-
     /* Ensure that target MPI has exactly the necessary number of limbs */
     MBEDTLS_MPI_CHK(mbedtls_mpi_resize_clear(X, limbs));
     if (size == 0) {
@@ -2214,9 +1882,6 @@
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     mbedtls_mpi G, TA, TU, U1, U2, TB, TV, V1, V2;
-    MPI_VALIDATE_RET(X != NULL);
-    MPI_VALIDATE_RET(A != NULL);
-    MPI_VALIDATE_RET(N != NULL);
 
     if (mbedtls_mpi_cmp_int(N, 1) <= 0) {
         return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
@@ -2372,9 +2037,6 @@
     size_t i, j, k, s;
     mbedtls_mpi W, R, T, A, RR;
 
-    MPI_VALIDATE_RET(X     != NULL);
-    MPI_VALIDATE_RET(f_rng != NULL);
-
     mbedtls_mpi_init(&W); mbedtls_mpi_init(&R);
     mbedtls_mpi_init(&T); mbedtls_mpi_init(&A);
     mbedtls_mpi_init(&RR);
@@ -2462,8 +2124,6 @@
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     mbedtls_mpi XX;
-    MPI_VALIDATE_RET(X     != NULL);
-    MPI_VALIDATE_RET(f_rng != NULL);
 
     XX.s = 1;
     XX.n = X->n;
@@ -2513,9 +2173,6 @@
     mbedtls_mpi_uint r;
     mbedtls_mpi Y;
 
-    MPI_VALIDATE_RET(X     != NULL);
-    MPI_VALIDATE_RET(f_rng != NULL);
-
     if (nbits < 3 || nbits > MBEDTLS_MPI_MAX_BITS) {
         return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
     }
diff --git a/library/bignum_core.c b/library/bignum_core.c
index dfed60d..1a3e0b9 100644
--- a/library/bignum_core.c
+++ b/library/bignum_core.c
@@ -856,16 +856,17 @@
     return c;
 }
 
-mbedtls_mpi_uint mbedtls_mpi_core_check_zero_ct(const mbedtls_mpi_uint *A,
-                                                size_t limbs)
+mbedtls_ct_condition_t mbedtls_mpi_core_check_zero_ct(const mbedtls_mpi_uint *A,
+                                                      size_t limbs)
 {
+    volatile const mbedtls_mpi_uint *force_read_A = A;
     mbedtls_mpi_uint bits = 0;
 
     for (size_t i = 0; i < limbs; i++) {
-        bits |= A[i];
+        bits |= force_read_A[i];
     }
 
-    return bits;
+    return mbedtls_ct_bool(bits);
 }
 
 void mbedtls_mpi_core_to_mont_rep(mbedtls_mpi_uint *X,
diff --git a/library/bignum_core.h b/library/bignum_core.h
index b56be0a..92c8d47 100644
--- a/library/bignum_core.h
+++ b/library/bignum_core.h
@@ -662,11 +662,11 @@
  * \param[in] A   The MPI to test.
  * \param limbs   Number of limbs in \p A.
  *
- * \return        0 if `A == 0`
- *                non-0 (may be any value) if `A != 0`.
+ * \return        MBEDTLS_CT_FALSE if `A == 0`
+ *                MBEDTLS_CT_TRUE  if `A != 0`.
  */
-mbedtls_mpi_uint mbedtls_mpi_core_check_zero_ct(const mbedtls_mpi_uint *A,
-                                                size_t limbs);
+mbedtls_ct_condition_t mbedtls_mpi_core_check_zero_ct(const mbedtls_mpi_uint *A,
+                                                      size_t limbs);
 
 /**
  * \brief          Returns the number of limbs of working memory required for
diff --git a/library/debug.c b/library/debug.c
index a9d58e5..c36ed3c 100644
--- a/library/debug.c
+++ b/library/debug.c
@@ -11,7 +11,7 @@
 
 #include "mbedtls/platform.h"
 
-#include "mbedtls/debug.h"
+#include "debug_internal.h"
 #include "mbedtls/error.h"
 
 #include <stdarg.h>
diff --git a/library/debug_internal.h b/library/debug_internal.h
new file mode 100644
index 0000000..4523b46
--- /dev/null
+++ b/library/debug_internal.h
@@ -0,0 +1,172 @@
+/**
+ * \file debug_internal.h
+ *
+ * \brief Internal part of the public "debug.h".
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+#ifndef MBEDTLS_DEBUG_INTERNAL_H
+#define MBEDTLS_DEBUG_INTERNAL_H
+
+#include "mbedtls/debug.h"
+
+/**
+ * \brief    Print a message to the debug output. This function is always used
+ *          through the MBEDTLS_SSL_DEBUG_MSG() macro, which supplies the ssl
+ *          context, file and line number parameters.
+ *
+ * \param ssl       SSL context
+ * \param level     error level of the debug message
+ * \param file      file the message has occurred in
+ * \param line      line number the message has occurred at
+ * \param format    format specifier, in printf format
+ * \param ...       variables used by the format specifier
+ *
+ * \attention       This function is intended for INTERNAL usage within the
+ *                  library only.
+ */
+void mbedtls_debug_print_msg(const mbedtls_ssl_context *ssl, int level,
+                             const char *file, int line,
+                             const char *format, ...) MBEDTLS_PRINTF_ATTRIBUTE(5, 6);
+
+/**
+ * \brief   Print the return value of a function to the debug output. This
+ *          function is always used through the MBEDTLS_SSL_DEBUG_RET() macro,
+ *          which supplies the ssl context, file and line number parameters.
+ *
+ * \param ssl       SSL context
+ * \param level     error level of the debug message
+ * \param file      file the error has occurred in
+ * \param line      line number the error has occurred in
+ * \param text      the name of the function that returned the error
+ * \param ret       the return code value
+ *
+ * \attention       This function is intended for INTERNAL usage within the
+ *                  library only.
+ */
+void mbedtls_debug_print_ret(const mbedtls_ssl_context *ssl, int level,
+                             const char *file, int line,
+                             const char *text, int ret);
+
+/**
+ * \brief   Output a buffer of size len bytes to the debug output. This function
+ *          is always used through the MBEDTLS_SSL_DEBUG_BUF() macro,
+ *          which supplies the ssl context, file and line number parameters.
+ *
+ * \param ssl       SSL context
+ * \param level     error level of the debug message
+ * \param file      file the error has occurred in
+ * \param line      line number the error has occurred in
+ * \param text      a name or label for the buffer being dumped. Normally the
+ *                  variable or buffer name
+ * \param buf       the buffer to be outputted
+ * \param len       length of the buffer
+ *
+ * \attention       This function is intended for INTERNAL usage within the
+ *                  library only.
+ */
+void mbedtls_debug_print_buf(const mbedtls_ssl_context *ssl, int level,
+                             const char *file, int line, const char *text,
+                             const unsigned char *buf, size_t len);
+
+#if defined(MBEDTLS_BIGNUM_C)
+/**
+ * \brief   Print a MPI variable to the debug output. This function is always
+ *          used through the MBEDTLS_SSL_DEBUG_MPI() macro, which supplies the
+ *          ssl context, file and line number parameters.
+ *
+ * \param ssl       SSL context
+ * \param level     error level of the debug message
+ * \param file      file the error has occurred in
+ * \param line      line number the error has occurred in
+ * \param text      a name or label for the MPI being output. Normally the
+ *                  variable name
+ * \param X         the MPI variable
+ *
+ * \attention       This function is intended for INTERNAL usage within the
+ *                  library only.
+ */
+void mbedtls_debug_print_mpi(const mbedtls_ssl_context *ssl, int level,
+                             const char *file, int line,
+                             const char *text, const mbedtls_mpi *X);
+#endif
+
+#if defined(MBEDTLS_ECP_LIGHT)
+/**
+ * \brief   Print an ECP point to the debug output. This function is always
+ *          used through the MBEDTLS_SSL_DEBUG_ECP() macro, which supplies the
+ *          ssl context, file and line number parameters.
+ *
+ * \param ssl       SSL context
+ * \param level     error level of the debug message
+ * \param file      file the error has occurred in
+ * \param line      line number the error has occurred in
+ * \param text      a name or label for the ECP point being output. Normally the
+ *                  variable name
+ * \param X         the ECP point
+ *
+ * \attention       This function is intended for INTERNAL usage within the
+ *                  library only.
+ */
+void mbedtls_debug_print_ecp(const mbedtls_ssl_context *ssl, int level,
+                             const char *file, int line,
+                             const char *text, const mbedtls_ecp_point *X);
+#endif
+
+#if defined(MBEDTLS_X509_CRT_PARSE_C) && !defined(MBEDTLS_X509_REMOVE_INFO)
+/**
+ * \brief   Print a X.509 certificate structure to the debug output. This
+ *          function is always used through the MBEDTLS_SSL_DEBUG_CRT() macro,
+ *          which supplies the ssl context, file and line number parameters.
+ *
+ * \param ssl       SSL context
+ * \param level     error level of the debug message
+ * \param file      file the error has occurred in
+ * \param line      line number the error has occurred in
+ * \param text      a name or label for the certificate being output
+ * \param crt       X.509 certificate structure
+ *
+ * \attention       This function is intended for INTERNAL usage within the
+ *                  library only.
+ */
+void mbedtls_debug_print_crt(const mbedtls_ssl_context *ssl, int level,
+                             const char *file, int line,
+                             const char *text, const mbedtls_x509_crt *crt);
+#endif
+
+/* Note: the MBEDTLS_ECDH_C guard here is mandatory because this debug function
+         only works for the built-in implementation. */
+#if defined(MBEDTLS_KEY_EXCHANGE_SOME_ECDH_OR_ECDHE_ANY_ENABLED) && \
+    defined(MBEDTLS_ECDH_C)
+typedef enum {
+    MBEDTLS_DEBUG_ECDH_Q,
+    MBEDTLS_DEBUG_ECDH_QP,
+    MBEDTLS_DEBUG_ECDH_Z,
+} mbedtls_debug_ecdh_attr;
+
+/**
+ * \brief   Print a field of the ECDH structure in the SSL context to the debug
+ *          output. This function is always used through the
+ *          MBEDTLS_SSL_DEBUG_ECDH() macro, which supplies the ssl context, file
+ *          and line number parameters.
+ *
+ * \param ssl       SSL context
+ * \param level     error level of the debug message
+ * \param file      file the error has occurred in
+ * \param line      line number the error has occurred in
+ * \param ecdh      the ECDH context
+ * \param attr      the identifier of the attribute being output
+ *
+ * \attention       This function is intended for INTERNAL usage within the
+ *                  library only.
+ */
+void mbedtls_debug_printf_ecdh(const mbedtls_ssl_context *ssl, int level,
+                               const char *file, int line,
+                               const mbedtls_ecdh_context *ecdh,
+                               mbedtls_debug_ecdh_attr attr);
+#endif /* MBEDTLS_KEY_EXCHANGE_SOME_ECDH_OR_ECDHE_ANY_ENABLED &&
+          MBEDTLS_ECDH_C */
+
+#endif /* MBEDTLS_DEBUG_INTERNAL_H */
diff --git a/library/ecdh.c b/library/ecdh.c
index 52b1617..b276c6a 100644
--- a/library/ecdh.c
+++ b/library/ecdh.c
@@ -144,6 +144,15 @@
 #endif
 }
 
+mbedtls_ecp_group_id mbedtls_ecdh_get_grp_id(mbedtls_ecdh_context *ctx)
+{
+#if defined(MBEDTLS_ECDH_LEGACY_CONTEXT)
+    return ctx->MBEDTLS_PRIVATE(grp).id;
+#else
+    return ctx->MBEDTLS_PRIVATE(grp_id);
+#endif
+}
+
 /*
  * Initialize context
  */
diff --git a/library/ecp.c b/library/ecp.c
index 758d54b..427059b 100644
--- a/library/ecp.c
+++ b/library/ecp.c
@@ -3302,10 +3302,11 @@
 /*
  * Write a private key.
  */
+#if !defined MBEDTLS_DEPRECATED_REMOVED
 int mbedtls_ecp_write_key(mbedtls_ecp_keypair *key,
                           unsigned char *buf, size_t buflen)
 {
-    int ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
 
 #if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED)
     if (mbedtls_ecp_get_type(&key->grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) {
@@ -3332,6 +3333,39 @@
 
     return ret;
 }
+#endif /* MBEDTLS_DEPRECATED_REMOVED */
+
+int mbedtls_ecp_write_key_ext(const mbedtls_ecp_keypair *key,
+                              size_t *olen, unsigned char *buf, size_t buflen)
+{
+    size_t len = (key->grp.nbits + 7) / 8;
+    if (len > buflen) {
+        /* For robustness, ensure *olen <= buflen even on error. */
+        *olen = 0;
+        return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL;
+    }
+    *olen = len;
+
+    /* Private key not set */
+    if (key->d.n == 0) {
+        return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
+    }
+
+#if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED)
+    if (mbedtls_ecp_get_type(&key->grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) {
+        return mbedtls_mpi_write_binary_le(&key->d, buf, len);
+    }
+#endif
+
+#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
+    if (mbedtls_ecp_get_type(&key->grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
+        return mbedtls_mpi_write_binary(&key->d, buf, len);
+    }
+#endif
+
+    /* Private key set but no recognized curve type? This shouldn't happen. */
+    return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+}
 
 /*
  * Write a public key.
diff --git a/library/ecp_curves.c b/library/ecp_curves.c
index 577e23b..c3cd33f 100644
--- a/library/ecp_curves.c
+++ b/library/ecp_curves.c
@@ -23,12 +23,6 @@
 
 #if !defined(MBEDTLS_ECP_ALT)
 
-/* Parameter validation macros based on platform_util.h */
-#define ECP_VALIDATE_RET(cond)    \
-    MBEDTLS_INTERNAL_VALIDATE_RET(cond, MBEDTLS_ERR_ECP_BAD_INPUT_DATA)
-#define ECP_VALIDATE(cond)        \
-    MBEDTLS_INTERNAL_VALIDATE(cond)
-
 #define ECP_MPI_INIT(_p, _n) { .p = (mbedtls_mpi_uint *) (_p), .s = 1, .n = (_n) }
 
 #define ECP_MPI_INIT_ARRAY(x)   \
@@ -52,7 +46,7 @@
     defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED)
 /* For these curves, we build the group parameters dynamically. */
 #define ECP_LOAD_GROUP
-static mbedtls_mpi_uint mpi_one[] = { 1 };
+static const mbedtls_mpi_uint mpi_one[] = { 1 };
 #endif
 
 /*
@@ -4511,7 +4505,7 @@
 {
     X->s = 1;
     X->n = 1;
-    X->p = mpi_one;
+    X->p = (mbedtls_mpi_uint *) mpi_one; /* X->p will not be modified so the cast is safe */
 }
 
 /*
@@ -4722,7 +4716,6 @@
  */
 int mbedtls_ecp_group_load(mbedtls_ecp_group *grp, mbedtls_ecp_group_id id)
 {
-    ECP_VALIDATE_RET(grp != NULL);
     mbedtls_ecp_group_free(grp);
 
     mbedtls_ecp_group_init(grp);
@@ -5318,7 +5311,7 @@
  */
 #define P_KOBLITZ_MAX   (256 / 8 / sizeof(mbedtls_mpi_uint))      // Max limbs in P
 #define P_KOBLITZ_R     (8 / sizeof(mbedtls_mpi_uint))            // Limbs in R
-static inline int ecp_mod_koblitz(mbedtls_mpi *N, mbedtls_mpi_uint *Rp, size_t p_limbs,
+static inline int ecp_mod_koblitz(mbedtls_mpi *N, const mbedtls_mpi_uint *Rp, size_t p_limbs,
                                   size_t adjust, size_t shift, mbedtls_mpi_uint mask)
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
@@ -5332,7 +5325,7 @@
 
     /* Init R */
     R.s = 1;
-    R.p = Rp;
+    R.p = (mbedtls_mpi_uint *) Rp; /* R.p will not be modified so the cast is safe */
     R.n = P_KOBLITZ_R;
 
     /* Common setup for M */
@@ -5403,7 +5396,7 @@
  */
 static int ecp_mod_p192k1(mbedtls_mpi *N)
 {
-    static mbedtls_mpi_uint Rp[] = {
+    static const mbedtls_mpi_uint Rp[] = {
         MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0x11, 0x00, 0x00, 0x01, 0x00, 0x00,
                                   0x00)
     };
@@ -5420,7 +5413,7 @@
  */
 static int ecp_mod_p224k1(mbedtls_mpi *N)
 {
-    static mbedtls_mpi_uint Rp[] = {
+    static const mbedtls_mpi_uint Rp[] = {
         MBEDTLS_BYTES_TO_T_UINT_8(0x93, 0x1A, 0x00, 0x00, 0x01, 0x00, 0x00,
                                   0x00)
     };
@@ -5442,7 +5435,7 @@
  */
 static int ecp_mod_p256k1(mbedtls_mpi *N)
 {
-    static mbedtls_mpi_uint Rp[] = {
+    static const mbedtls_mpi_uint Rp[] = {
         MBEDTLS_BYTES_TO_T_UINT_8(0xD1, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00,
                                   0x00)
     };
diff --git a/library/ecp_curves_new.c b/library/ecp_curves_new.c
index 4ee0f58..035b23a 100644
--- a/library/ecp_curves_new.c
+++ b/library/ecp_curves_new.c
@@ -28,12 +28,6 @@
 
 #if !defined(MBEDTLS_ECP_ALT)
 
-/* Parameter validation macros based on platform_util.h */
-#define ECP_VALIDATE_RET(cond)    \
-    MBEDTLS_INTERNAL_VALIDATE_RET(cond, MBEDTLS_ERR_ECP_BAD_INPUT_DATA)
-#define ECP_VALIDATE(cond)        \
-    MBEDTLS_INTERNAL_VALIDATE(cond)
-
 #define ECP_MPI_INIT(_p, _n) { .p = (mbedtls_mpi_uint *) (_p), .s = 1, .n = (_n) }
 
 #define ECP_MPI_INIT_ARRAY(x)   \
@@ -4764,7 +4758,6 @@
  */
 int mbedtls_ecp_group_load(mbedtls_ecp_group *grp, mbedtls_ecp_group_id id)
 {
-    ECP_VALIDATE_RET(grp != NULL);
     mbedtls_ecp_group_free(grp);
 
     mbedtls_ecp_group_init(grp);
diff --git a/library/entropy_poll.c b/library/entropy_poll.c
index bd21e2d..794ee03 100644
--- a/library/entropy_poll.c
+++ b/library/entropy_poll.c
@@ -5,7 +5,7 @@
  *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
-#if defined(__linux__) && !defined(_GNU_SOURCE)
+#if defined(__linux__) || defined(__midipix__) && !defined(_GNU_SOURCE)
 /* Ensure that syscall() is available even when compiling with -std=c99 */
 #define _GNU_SOURCE
 #endif
diff --git a/library/gcm.c b/library/gcm.c
index c677ca4..90c1d1d 100644
--- a/library/gcm.c
+++ b/library/gcm.c
@@ -41,6 +41,12 @@
 
 #if !defined(MBEDTLS_GCM_ALT)
 
+/* Used to select the acceleration mechanism */
+#define MBEDTLS_GCM_ACC_SMALLTABLE  0
+#define MBEDTLS_GCM_ACC_LARGETABLE  1
+#define MBEDTLS_GCM_ACC_AESNI       2
+#define MBEDTLS_GCM_ACC_AESCE       3
+
 /*
  * Initialize a context
  */
@@ -49,6 +55,39 @@
     memset(ctx, 0, sizeof(mbedtls_gcm_context));
 }
 
+static inline void gcm_set_acceleration(mbedtls_gcm_context *ctx)
+{
+#if defined(MBEDTLS_GCM_LARGE_TABLE)
+    ctx->acceleration = MBEDTLS_GCM_ACC_LARGETABLE;
+#else
+    ctx->acceleration = MBEDTLS_GCM_ACC_SMALLTABLE;
+#endif
+
+#if defined(MBEDTLS_AESNI_HAVE_CODE)
+    /* With CLMUL support, we need only h, not the rest of the table */
+    if (mbedtls_aesni_has_support(MBEDTLS_AESNI_CLMUL)) {
+        ctx->acceleration = MBEDTLS_GCM_ACC_AESNI;
+    }
+#endif
+
+#if defined(MBEDTLS_AESCE_HAVE_CODE)
+    if (MBEDTLS_AESCE_HAS_SUPPORT()) {
+        ctx->acceleration = MBEDTLS_GCM_ACC_AESCE;
+    }
+#endif
+}
+
+static inline void gcm_gen_table_rightshift(uint64_t dst[2], const uint64_t src[2])
+{
+    uint8_t *u8Dst = (uint8_t *) dst;
+    uint8_t *u8Src = (uint8_t *) src;
+
+    MBEDTLS_PUT_UINT64_BE(MBEDTLS_GET_UINT64_BE(&src[1], 0) >> 1, &dst[1], 0);
+    u8Dst[8] |= (u8Src[7] & 0x01) << 7;
+    MBEDTLS_PUT_UINT64_BE(MBEDTLS_GET_UINT64_BE(&src[0], 0) >> 1, &dst[0], 0);
+    u8Dst[0] ^= (u8Src[15] & 0x01) ? 0xE1 : 0;
+}
+
 /*
  * Precompute small multiples of H, that is set
  *      HH[i] || HL[i] = H times i,
@@ -60,11 +99,8 @@
 static int gcm_gen_table(mbedtls_gcm_context *ctx)
 {
     int ret, i, j;
-    uint64_t hi, lo;
-    uint64_t vl, vh;
-    unsigned char h[16];
-
-    memset(h, 0, 16);
+    uint64_t u64h[2] = { 0 };
+    uint8_t *h = (uint8_t *) u64h;
 
 #if defined(MBEDTLS_BLOCK_CIPHER_C)
     ret = mbedtls_block_cipher_encrypt(&ctx->block_cipher_ctx, h, h);
@@ -76,53 +112,48 @@
         return ret;
     }
 
-    /* pack h as two 64-bits ints, big-endian */
-    hi = MBEDTLS_GET_UINT32_BE(h,  0);
-    lo = MBEDTLS_GET_UINT32_BE(h,  4);
-    vh = (uint64_t) hi << 32 | lo;
+    gcm_set_acceleration(ctx);
 
-    hi = MBEDTLS_GET_UINT32_BE(h,  8);
-    lo = MBEDTLS_GET_UINT32_BE(h,  12);
-    vl = (uint64_t) hi << 32 | lo;
+    /* MBEDTLS_GCM_HTABLE_SIZE/2 = 1000 corresponds to 1 in GF(2^128) */
+    ctx->H[MBEDTLS_GCM_HTABLE_SIZE/2][0] = u64h[0];
+    ctx->H[MBEDTLS_GCM_HTABLE_SIZE/2][1] = u64h[1];
 
-    /* 8 = 1000 corresponds to 1 in GF(2^128) */
-    ctx->HL[8] = vl;
-    ctx->HH[8] = vh;
-
+    switch (ctx->acceleration) {
 #if defined(MBEDTLS_AESNI_HAVE_CODE)
-    /* With CLMUL support, we need only h, not the rest of the table */
-    if (mbedtls_aesni_has_support(MBEDTLS_AESNI_CLMUL)) {
-        return 0;
-    }
+        case MBEDTLS_GCM_ACC_AESNI:
+            return 0;
 #endif
 
 #if defined(MBEDTLS_AESCE_HAVE_CODE)
-    if (MBEDTLS_AESCE_HAS_SUPPORT()) {
-        return 0;
-    }
+        case MBEDTLS_GCM_ACC_AESCE:
+            return 0;
 #endif
 
-    /* 0 corresponds to 0 in GF(2^128) */
-    ctx->HH[0] = 0;
-    ctx->HL[0] = 0;
+        default:
+            /* 0 corresponds to 0 in GF(2^128) */
+            ctx->H[0][0] = 0;
+            ctx->H[0][1] = 0;
 
-    for (i = 4; i > 0; i >>= 1) {
-        uint32_t T = (vl & 1) * 0xe1000000U;
-        vl  = (vh << 63) | (vl >> 1);
-        vh  = (vh >> 1) ^ ((uint64_t) T << 32);
+            for (i = MBEDTLS_GCM_HTABLE_SIZE/4; i > 0; i >>= 1) {
+                gcm_gen_table_rightshift(ctx->H[i], ctx->H[i*2]);
+            }
 
-        ctx->HL[i] = vl;
-        ctx->HH[i] = vh;
-    }
+#if !defined(MBEDTLS_GCM_LARGE_TABLE)
+            /* pack elements of H as 64-bits ints, big-endian */
+            for (i = MBEDTLS_GCM_HTABLE_SIZE/2; i > 0; i >>= 1) {
+                MBEDTLS_PUT_UINT64_BE(ctx->H[i][0], &ctx->H[i][0], 0);
+                MBEDTLS_PUT_UINT64_BE(ctx->H[i][1], &ctx->H[i][1], 0);
+            }
+#endif
 
-    for (i = 2; i <= 8; i *= 2) {
-        uint64_t *HiL = ctx->HL + i, *HiH = ctx->HH + i;
-        vh = *HiH;
-        vl = *HiL;
-        for (j = 1; j < i; j++) {
-            HiH[j] = vh ^ ctx->HH[j];
-            HiL[j] = vl ^ ctx->HL[j];
-        }
+            for (i = 2; i < MBEDTLS_GCM_HTABLE_SIZE; i <<= 1) {
+                for (j = 1; j < i; j++) {
+                    mbedtls_xor_no_simd((unsigned char *) ctx->H[i+j],
+                                        (unsigned char *) ctx->H[i],
+                                        (unsigned char *) ctx->H[j],
+                                        16);
+                }
+            }
     }
 
     return 0;
@@ -181,6 +212,80 @@
     return 0;
 }
 
+#if defined(MBEDTLS_GCM_LARGE_TABLE)
+static const uint16_t last8[256] = {
+    0x0000, 0xc201, 0x8403, 0x4602, 0x0807, 0xca06, 0x8c04, 0x4e05,
+    0x100e, 0xd20f, 0x940d, 0x560c, 0x1809, 0xda08, 0x9c0a, 0x5e0b,
+    0x201c, 0xe21d, 0xa41f, 0x661e, 0x281b, 0xea1a, 0xac18, 0x6e19,
+    0x3012, 0xf213, 0xb411, 0x7610, 0x3815, 0xfa14, 0xbc16, 0x7e17,
+    0x4038, 0x8239, 0xc43b, 0x063a, 0x483f, 0x8a3e, 0xcc3c, 0x0e3d,
+    0x5036, 0x9237, 0xd435, 0x1634, 0x5831, 0x9a30, 0xdc32, 0x1e33,
+    0x6024, 0xa225, 0xe427, 0x2626, 0x6823, 0xaa22, 0xec20, 0x2e21,
+    0x702a, 0xb22b, 0xf429, 0x3628, 0x782d, 0xba2c, 0xfc2e, 0x3e2f,
+    0x8070, 0x4271, 0x0473, 0xc672, 0x8877, 0x4a76, 0x0c74, 0xce75,
+    0x907e, 0x527f, 0x147d, 0xd67c, 0x9879, 0x5a78, 0x1c7a, 0xde7b,
+    0xa06c, 0x626d, 0x246f, 0xe66e, 0xa86b, 0x6a6a, 0x2c68, 0xee69,
+    0xb062, 0x7263, 0x3461, 0xf660, 0xb865, 0x7a64, 0x3c66, 0xfe67,
+    0xc048, 0x0249, 0x444b, 0x864a, 0xc84f, 0x0a4e, 0x4c4c, 0x8e4d,
+    0xd046, 0x1247, 0x5445, 0x9644, 0xd841, 0x1a40, 0x5c42, 0x9e43,
+    0xe054, 0x2255, 0x6457, 0xa656, 0xe853, 0x2a52, 0x6c50, 0xae51,
+    0xf05a, 0x325b, 0x7459, 0xb658, 0xf85d, 0x3a5c, 0x7c5e, 0xbe5f,
+    0x00e1, 0xc2e0, 0x84e2, 0x46e3, 0x08e6, 0xcae7, 0x8ce5, 0x4ee4,
+    0x10ef, 0xd2ee, 0x94ec, 0x56ed, 0x18e8, 0xdae9, 0x9ceb, 0x5eea,
+    0x20fd, 0xe2fc, 0xa4fe, 0x66ff, 0x28fa, 0xeafb, 0xacf9, 0x6ef8,
+    0x30f3, 0xf2f2, 0xb4f0, 0x76f1, 0x38f4, 0xfaf5, 0xbcf7, 0x7ef6,
+    0x40d9, 0x82d8, 0xc4da, 0x06db, 0x48de, 0x8adf, 0xccdd, 0x0edc,
+    0x50d7, 0x92d6, 0xd4d4, 0x16d5, 0x58d0, 0x9ad1, 0xdcd3, 0x1ed2,
+    0x60c5, 0xa2c4, 0xe4c6, 0x26c7, 0x68c2, 0xaac3, 0xecc1, 0x2ec0,
+    0x70cb, 0xb2ca, 0xf4c8, 0x36c9, 0x78cc, 0xbacd, 0xfccf, 0x3ece,
+    0x8091, 0x4290, 0x0492, 0xc693, 0x8896, 0x4a97, 0x0c95, 0xce94,
+    0x909f, 0x529e, 0x149c, 0xd69d, 0x9898, 0x5a99, 0x1c9b, 0xde9a,
+    0xa08d, 0x628c, 0x248e, 0xe68f, 0xa88a, 0x6a8b, 0x2c89, 0xee88,
+    0xb083, 0x7282, 0x3480, 0xf681, 0xb884, 0x7a85, 0x3c87, 0xfe86,
+    0xc0a9, 0x02a8, 0x44aa, 0x86ab, 0xc8ae, 0x0aaf, 0x4cad, 0x8eac,
+    0xd0a7, 0x12a6, 0x54a4, 0x96a5, 0xd8a0, 0x1aa1, 0x5ca3, 0x9ea2,
+    0xe0b5, 0x22b4, 0x64b6, 0xa6b7, 0xe8b2, 0x2ab3, 0x6cb1, 0xaeb0,
+    0xf0bb, 0x32ba, 0x74b8, 0xb6b9, 0xf8bc, 0x3abd, 0x7cbf, 0xbebe
+};
+
+static void gcm_mult_largetable(uint8_t *output, const uint8_t *x, uint64_t H[256][2])
+{
+    int i;
+    uint64_t u64z[2];
+    uint16_t *u16z = (uint16_t *) u64z;
+    uint8_t *u8z = (uint8_t *) u64z;
+    uint8_t rem;
+
+    u64z[0] = 0;
+    u64z[1] = 0;
+
+    if (MBEDTLS_IS_BIG_ENDIAN) {
+        for (i = 15; i > 0; i--) {
+            mbedtls_xor_no_simd(u8z, u8z, (uint8_t *) H[x[i]], 16);
+            rem = u8z[15];
+
+            u64z[1] >>= 8;
+            u8z[8] = u8z[7];
+            u64z[0] >>= 8;
+
+            u16z[0] ^= MBEDTLS_GET_UINT16_LE(&last8[rem], 0);
+        }
+    } else {
+        for (i = 15; i > 0; i--) {
+            mbedtls_xor_no_simd(u8z, u8z, (uint8_t *) H[x[i]], 16);
+            rem = u8z[15];
+
+            u64z[1] <<= 8;
+            u8z[8] = u8z[7];
+            u64z[0] <<= 8;
+
+            u16z[0] ^= last8[rem];
+        }
+    }
+
+    mbedtls_xor_no_simd(output, u8z, (uint8_t *) H[x[0]], 16);
+}
+#else
 /*
  * Shoup's method for multiplication use this table with
  *      last4[x] = x times P^128
@@ -194,6 +299,47 @@
     0x9180, 0x8da0, 0xa9c0, 0xb5e0
 };
 
+static void gcm_mult_smalltable(uint8_t *output, const uint8_t *x, uint64_t H[16][2])
+{
+    int i = 0;
+    unsigned char lo, hi, rem;
+    uint64_t u64z[2];
+    const uint64_t *pu64z = NULL;
+    uint8_t *u8z = (uint8_t *) u64z;
+
+    lo = x[15] & 0xf;
+    hi = (x[15] >> 4) & 0xf;
+
+    pu64z = H[lo];
+
+    rem = (unsigned char) pu64z[1] & 0xf;
+    u64z[1] = (pu64z[0] << 60) | (pu64z[1] >> 4);
+    u64z[0] = (pu64z[0] >> 4);
+    u64z[0] ^= (uint64_t) last4[rem] << 48;
+    mbedtls_xor_no_simd(u8z, u8z, (uint8_t *) H[hi], 16);
+
+    for (i = 14; i >= 0; i--) {
+        lo = x[i] & 0xf;
+        hi = (x[i] >> 4) & 0xf;
+
+        rem = (unsigned char) u64z[1] & 0xf;
+        u64z[1] = (u64z[0] << 60) | (u64z[1] >> 4);
+        u64z[0] = (u64z[0] >> 4);
+        u64z[0] ^= (uint64_t) last4[rem] << 48;
+        mbedtls_xor_no_simd(u8z, u8z, (uint8_t *) H[lo], 16);
+
+        rem = (unsigned char) u64z[1] & 0xf;
+        u64z[1] = (u64z[0] << 60) | (u64z[1] >> 4);
+        u64z[0] = (u64z[0] >> 4);
+        u64z[0] ^= (uint64_t) last4[rem] << 48;
+        mbedtls_xor_no_simd(u8z, u8z, (uint8_t *) H[hi], 16);
+    }
+
+    MBEDTLS_PUT_UINT64_BE(u64z[0], output, 0);
+    MBEDTLS_PUT_UINT64_BE(u64z[1], output, 8);
+}
+#endif
+
 /*
  * Sets output to x times H using the precomputed tables.
  * x and output are seen as elements of GF(2^128) as in [MGV].
@@ -201,71 +347,31 @@
 static void gcm_mult(mbedtls_gcm_context *ctx, const unsigned char x[16],
                      unsigned char output[16])
 {
-    int i = 0;
-    unsigned char lo, hi, rem;
-    uint64_t zh, zl;
-
+    switch (ctx->acceleration) {
 #if defined(MBEDTLS_AESNI_HAVE_CODE)
-    if (mbedtls_aesni_has_support(MBEDTLS_AESNI_CLMUL)) {
-        unsigned char h[16];
-
-        /* mbedtls_aesni_gcm_mult needs big-endian input */
-        MBEDTLS_PUT_UINT32_BE(ctx->HH[8] >> 32, h,  0);
-        MBEDTLS_PUT_UINT32_BE(ctx->HH[8],       h,  4);
-        MBEDTLS_PUT_UINT32_BE(ctx->HL[8] >> 32, h,  8);
-        MBEDTLS_PUT_UINT32_BE(ctx->HL[8],       h, 12);
-
-        mbedtls_aesni_gcm_mult(output, x, h);
-        return;
-    }
-#endif /* MBEDTLS_AESNI_HAVE_CODE */
-
-#if defined(MBEDTLS_AESCE_HAVE_CODE)
-    if (MBEDTLS_AESCE_HAS_SUPPORT()) {
-        unsigned char h[16];
-
-        /* mbedtls_aesce_gcm_mult needs big-endian input */
-        MBEDTLS_PUT_UINT32_BE(ctx->HH[8] >> 32, h,  0);
-        MBEDTLS_PUT_UINT32_BE(ctx->HH[8],       h,  4);
-        MBEDTLS_PUT_UINT32_BE(ctx->HL[8] >> 32, h,  8);
-        MBEDTLS_PUT_UINT32_BE(ctx->HL[8],       h, 12);
-
-        mbedtls_aesce_gcm_mult(output, x, h);
-        return;
-    }
+        case MBEDTLS_GCM_ACC_AESNI:
+            mbedtls_aesni_gcm_mult(output, x, (uint8_t *) ctx->H[MBEDTLS_GCM_HTABLE_SIZE/2]);
+            break;
 #endif
 
-    lo = x[15] & 0xf;
+#if defined(MBEDTLS_AESCE_HAVE_CODE)
+        case MBEDTLS_GCM_ACC_AESCE:
+            mbedtls_aesce_gcm_mult(output, x, (uint8_t *) ctx->H[MBEDTLS_GCM_HTABLE_SIZE/2]);
+            break;
+#endif
 
-    zh = ctx->HH[lo];
-    zl = ctx->HL[lo];
-
-    for (i = 15; i >= 0; i--) {
-        lo = x[i] & 0xf;
-        hi = (x[i] >> 4) & 0xf;
-
-        if (i != 15) {
-            rem = (unsigned char) zl & 0xf;
-            zl = (zh << 60) | (zl >> 4);
-            zh = (zh >> 4);
-            zh ^= (uint64_t) last4[rem] << 48;
-            zh ^= ctx->HH[lo];
-            zl ^= ctx->HL[lo];
-
-        }
-
-        rem = (unsigned char) zl & 0xf;
-        zl = (zh << 60) | (zl >> 4);
-        zh = (zh >> 4);
-        zh ^= (uint64_t) last4[rem] << 48;
-        zh ^= ctx->HH[hi];
-        zl ^= ctx->HL[hi];
+#if defined(MBEDTLS_GCM_LARGE_TABLE)
+        case MBEDTLS_GCM_ACC_LARGETABLE:
+            gcm_mult_largetable(output, x, ctx->H);
+            break;
+#else
+        case MBEDTLS_GCM_ACC_SMALLTABLE:
+            gcm_mult_smalltable(output, x, ctx->H);
+            break;
+#endif
     }
 
-    MBEDTLS_PUT_UINT32_BE(zh >> 32, output, 0);
-    MBEDTLS_PUT_UINT32_BE(zh, output, 4);
-    MBEDTLS_PUT_UINT32_BE(zl >> 32, output, 8);
-    MBEDTLS_PUT_UINT32_BE(zl, output, 12);
+    return;
 }
 
 int mbedtls_gcm_starts(mbedtls_gcm_context *ctx,
@@ -354,9 +460,17 @@
 {
     const unsigned char *p;
     size_t use_len, offset;
+    uint64_t new_add_len;
 
-    /* IV is limited to 2^64 bits, so 2^61 bytes */
-    if ((uint64_t) add_len >> 61 != 0) {
+    /* AD is limited to 2^64 bits, ie 2^61 bytes
+     * Also check for possible overflow */
+#if SIZE_MAX > 0xFFFFFFFFFFFFFFFFULL
+    if (add_len > 0xFFFFFFFFFFFFFFFFULL) {
+        return MBEDTLS_ERR_GCM_BAD_INPUT;
+    }
+#endif
+    new_add_len = ctx->add_len + (uint64_t) add_len;
+    if (new_add_len < ctx->add_len || new_add_len >> 61 != 0) {
         return MBEDTLS_ERR_GCM_BAD_INPUT;
     }
 
@@ -539,6 +653,9 @@
     (void) output_size;
     *output_length = 0;
 
+    /* Total length is restricted to 2^39 - 256 bits, ie 2^36 - 2^5 bytes
+     * and AD length is restricted to 2^64 bits, ie 2^61 bytes so neither of
+     * the two multiplications would overflow. */
     orig_len = ctx->len * 8;
     orig_add_len = ctx->add_len * 8;
 
diff --git a/library/lms.c b/library/lms.c
index 08fe753..8d3cae0 100644
--- a/library/lms.c
+++ b/library/lms.c
@@ -65,7 +65,8 @@
 #define H_TREE_HEIGHT_MAX                  10
 #define MERKLE_TREE_NODE_AM(type)          ((size_t) 1 << (MBEDTLS_LMS_H_TREE_HEIGHT(type) + 1u))
 #define MERKLE_TREE_LEAF_NODE_AM(type)     ((size_t) 1 << MBEDTLS_LMS_H_TREE_HEIGHT(type))
-#define MERKLE_TREE_INTERNAL_NODE_AM(type) ((size_t) 1 << MBEDTLS_LMS_H_TREE_HEIGHT(type))
+#define MERKLE_TREE_INTERNAL_NODE_AM(type) ((unsigned int) \
+                                            (1u << MBEDTLS_LMS_H_TREE_HEIGHT(type)))
 
 #define D_CONST_LEN           (2)
 static const unsigned char D_LEAF_CONSTANT_BYTES[D_CONST_LEN] = { 0x82, 0x82 };
diff --git a/library/oid.c b/library/oid.c
index d30a464..1d6b1eb 100644
--- a/library/oid.c
+++ b/library/oid.c
@@ -684,6 +684,18 @@
         MBEDTLS_CIPHER_DES_EDE3_CBC,
     },
     {
+        OID_DESCRIPTOR(MBEDTLS_OID_AES_128_CBC,          "aes128-cbc", "AES128-CBC"),
+        MBEDTLS_CIPHER_AES_128_CBC,
+    },
+    {
+        OID_DESCRIPTOR(MBEDTLS_OID_AES_192_CBC,          "aes192-cbc", "AES192-CBC"),
+        MBEDTLS_CIPHER_AES_192_CBC,
+    },
+    {
+        OID_DESCRIPTOR(MBEDTLS_OID_AES_256_CBC,          "aes256-cbc", "AES256-CBC"),
+        MBEDTLS_CIPHER_AES_256_CBC,
+    },
+    {
         NULL_OID_DESCRIPTOR,
         MBEDTLS_CIPHER_NONE,
     },
diff --git a/library/pem.c b/library/pem.c
index 539134c..0fee5df 100644
--- a/library/pem.c
+++ b/library/pem.c
@@ -240,6 +240,29 @@
 }
 #endif /* MBEDTLS_AES_C */
 
+#if defined(MBEDTLS_DES_C) || defined(MBEDTLS_AES_C)
+static int pem_check_pkcs_padding(unsigned char *input, size_t input_len, size_t *data_len)
+{
+    /* input_len > 0 is guaranteed by mbedtls_pem_read_buffer(). */
+    size_t pad_len = input[input_len - 1];
+    size_t i;
+
+    if (pad_len > input_len) {
+        return MBEDTLS_ERR_PEM_PASSWORD_MISMATCH;
+    }
+
+    *data_len = input_len - pad_len;
+
+    for (i = *data_len; i < input_len; i++) {
+        if (input[i] != pad_len) {
+            return MBEDTLS_ERR_PEM_PASSWORD_MISMATCH;
+        }
+    }
+
+    return 0;
+}
+#endif /* MBEDTLS_DES_C || MBEDTLS_AES_C */
+
 #endif /* PEM_RFC1421 */
 
 int mbedtls_pem_read_buffer(mbedtls_pem_context *ctx, const char *header, const char *footer,
@@ -389,6 +412,10 @@
         return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PEM_INVALID_DATA, ret);
     }
 
+    if (len == 0) {
+        return MBEDTLS_ERR_PEM_BAD_INPUT_DATA;
+    }
+
     if ((buf = mbedtls_calloc(1, len)) == NULL) {
         return MBEDTLS_ERR_PEM_ALLOC_FAILED;
     }
@@ -426,20 +453,20 @@
 #endif /* MBEDTLS_AES_C */
 
         if (ret != 0) {
-            mbedtls_free(buf);
+            mbedtls_zeroize_and_free(buf, len);
             return ret;
         }
 
-        /*
-         * The result will be ASN.1 starting with a SEQUENCE tag, with 1 to 3
-         * length bytes (allow 4 to be sure) in all known use cases.
-         *
-         * Use that as a heuristic to try to detect password mismatches.
-         */
-        if (len <= 2 || buf[0] != 0x30 || buf[1] > 0x83) {
+        /* Check PKCS padding and update data length based on padding info.
+         * This can be used to detect invalid padding data and password
+         * mismatches. */
+        size_t unpadded_len;
+        ret = pem_check_pkcs_padding(buf, len, &unpadded_len);
+        if (ret != 0) {
             mbedtls_zeroize_and_free(buf, len);
-            return MBEDTLS_ERR_PEM_PASSWORD_MISMATCH;
+            return ret;
         }
+        len = unpadded_len;
 #else
         mbedtls_zeroize_and_free(buf, len);
         return MBEDTLS_ERR_PEM_FEATURE_UNAVAILABLE;
diff --git a/library/pk.c b/library/pk.c
index 9261837..003ef4a 100644
--- a/library/pk.c
+++ b/library/pk.c
@@ -18,10 +18,8 @@
 
 #if defined(MBEDTLS_RSA_C)
 #include "mbedtls/rsa.h"
-#if defined(MBEDTLS_PKCS1_V21) && !defined(MBEDTLS_USE_PSA_CRYPTO)
 #include "rsa_internal.h"
 #endif
-#endif
 #if defined(MBEDTLS_PK_HAVE_ECC_KEYS)
 #include "mbedtls/ecp.h"
 #endif
@@ -29,7 +27,7 @@
 #include "mbedtls/ecdsa.h"
 #endif
 
-#if defined(MBEDTLS_USE_PSA_CRYPTO)
+#if defined(MBEDTLS_PSA_CRYPTO_CLIENT)
 #include "psa_util_internal.h"
 #include "mbedtls/psa_util.h"
 #endif
@@ -378,6 +376,482 @@
 }
 #endif /* MBEDTLS_USE_PSA_CRYPTO */
 
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+#if defined(MBEDTLS_RSA_C)
+static psa_algorithm_t psa_algorithm_for_rsa(const mbedtls_rsa_context *rsa,
+                                             int want_crypt)
+{
+    if (mbedtls_rsa_get_padding_mode(rsa) == MBEDTLS_RSA_PKCS_V21) {
+        if (want_crypt) {
+            mbedtls_md_type_t md_type = (mbedtls_md_type_t) mbedtls_rsa_get_md_alg(rsa);
+            return PSA_ALG_RSA_OAEP(mbedtls_md_psa_alg_from_type(md_type));
+        } else {
+            return PSA_ALG_RSA_PSS_ANY_SALT(PSA_ALG_ANY_HASH);
+        }
+    } else {
+        if (want_crypt) {
+            return PSA_ALG_RSA_PKCS1V15_CRYPT;
+        } else {
+            return PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_ANY_HASH);
+        }
+    }
+}
+#endif /* MBEDTLS_RSA_C */
+
+int mbedtls_pk_get_psa_attributes(const mbedtls_pk_context *pk,
+                                  psa_key_usage_t usage,
+                                  psa_key_attributes_t *attributes)
+{
+    mbedtls_pk_type_t pk_type = mbedtls_pk_get_type(pk);
+
+    psa_key_usage_t more_usage = usage;
+    if (usage == PSA_KEY_USAGE_SIGN_MESSAGE) {
+        more_usage |= PSA_KEY_USAGE_VERIFY_MESSAGE;
+    } else if (usage == PSA_KEY_USAGE_SIGN_HASH) {
+        more_usage |= PSA_KEY_USAGE_VERIFY_HASH;
+    } else if (usage == PSA_KEY_USAGE_DECRYPT) {
+        more_usage |= PSA_KEY_USAGE_ENCRYPT;
+    }
+    more_usage |= PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_COPY;
+
+    int want_private = !(usage == PSA_KEY_USAGE_VERIFY_MESSAGE ||
+                         usage == PSA_KEY_USAGE_VERIFY_HASH ||
+                         usage == PSA_KEY_USAGE_ENCRYPT);
+
+    switch (pk_type) {
+#if defined(MBEDTLS_RSA_C)
+        case MBEDTLS_PK_RSA:
+        {
+            int want_crypt = 0; /* 0: sign/verify; 1: encrypt/decrypt */
+            switch (usage) {
+                case PSA_KEY_USAGE_SIGN_MESSAGE:
+                case PSA_KEY_USAGE_SIGN_HASH:
+                case PSA_KEY_USAGE_VERIFY_MESSAGE:
+                case PSA_KEY_USAGE_VERIFY_HASH:
+                    /* Nothing to do. */
+                    break;
+                case PSA_KEY_USAGE_DECRYPT:
+                case PSA_KEY_USAGE_ENCRYPT:
+                    want_crypt = 1;
+                    break;
+                default:
+                    return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+            }
+            /* Detect the presence of a private key in a way that works both
+             * in CRT and non-CRT configurations. */
+            mbedtls_rsa_context *rsa = mbedtls_pk_rsa(*pk);
+            int has_private = (mbedtls_rsa_check_privkey(rsa) == 0);
+            if (want_private && !has_private) {
+                return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+            }
+            psa_set_key_type(attributes, (want_private ?
+                                          PSA_KEY_TYPE_RSA_KEY_PAIR :
+                                          PSA_KEY_TYPE_RSA_PUBLIC_KEY));
+            psa_set_key_bits(attributes, mbedtls_pk_get_bitlen(pk));
+            psa_set_key_algorithm(attributes,
+                                  psa_algorithm_for_rsa(rsa, want_crypt));
+            break;
+        }
+#endif /* MBEDTLS_RSA_C */
+
+#if defined(MBEDTLS_PK_HAVE_ECC_KEYS)
+        case MBEDTLS_PK_ECKEY:
+        case MBEDTLS_PK_ECKEY_DH:
+        case MBEDTLS_PK_ECDSA:
+        {
+            int sign_ok = (pk_type != MBEDTLS_PK_ECKEY_DH);
+            int derive_ok = (pk_type != MBEDTLS_PK_ECDSA);
+#if defined(MBEDTLS_PK_USE_PSA_EC_DATA)
+            psa_ecc_family_t family = pk->ec_family;
+            size_t bits = pk->ec_bits;
+            int has_private = 0;
+            if (pk->priv_id != MBEDTLS_SVC_KEY_ID_INIT) {
+                has_private = 1;
+            }
+#else
+            const mbedtls_ecp_keypair *ec = mbedtls_pk_ec_ro(*pk);
+            int has_private = (ec->d.n != 0);
+            size_t bits = 0;
+            psa_ecc_family_t family =
+                mbedtls_ecc_group_to_psa(ec->grp.id, &bits);
+#endif
+            psa_algorithm_t alg = 0;
+            switch (usage) {
+                case PSA_KEY_USAGE_SIGN_MESSAGE:
+                case PSA_KEY_USAGE_SIGN_HASH:
+                case PSA_KEY_USAGE_VERIFY_MESSAGE:
+                case PSA_KEY_USAGE_VERIFY_HASH:
+                    if (!sign_ok) {
+                        return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+                    }
+#if defined(MBEDTLS_ECDSA_DETERMINISTIC)
+                    alg = PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_ANY_HASH);
+#else
+                    alg = PSA_ALG_ECDSA(PSA_ALG_ANY_HASH);
+#endif
+                    break;
+                case PSA_KEY_USAGE_DERIVE:
+                    alg = PSA_ALG_ECDH;
+                    if (!derive_ok) {
+                        return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+                    }
+                    break;
+                default:
+                    return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+            }
+            if (want_private && !has_private) {
+                return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+            }
+            psa_set_key_type(attributes, (want_private ?
+                                          PSA_KEY_TYPE_ECC_KEY_PAIR(family) :
+                                          PSA_KEY_TYPE_ECC_PUBLIC_KEY(family)));
+            psa_set_key_bits(attributes, bits);
+            psa_set_key_algorithm(attributes, alg);
+            break;
+        }
+#endif /* MBEDTLS_PK_HAVE_ECC_KEYS */
+
+#if defined(MBEDTLS_PK_RSA_ALT_SUPPORT)
+        case MBEDTLS_PK_RSA_ALT:
+            return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
+#endif /* MBEDTLS_PK_RSA_ALT_SUPPORT */
+
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+        case MBEDTLS_PK_OPAQUE:
+        {
+            psa_key_attributes_t old_attributes = PSA_KEY_ATTRIBUTES_INIT;
+            psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+            status = psa_get_key_attributes(pk->priv_id, &old_attributes);
+            if (status != PSA_SUCCESS) {
+                return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
+            }
+            psa_key_type_t old_type = psa_get_key_type(&old_attributes);
+            switch (usage) {
+                case PSA_KEY_USAGE_SIGN_MESSAGE:
+                case PSA_KEY_USAGE_SIGN_HASH:
+                case PSA_KEY_USAGE_VERIFY_MESSAGE:
+                case PSA_KEY_USAGE_VERIFY_HASH:
+                    if (!(PSA_KEY_TYPE_IS_ECC_KEY_PAIR(old_type) ||
+                          old_type == PSA_KEY_TYPE_RSA_KEY_PAIR)) {
+                        return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+                    }
+                    break;
+                case PSA_KEY_USAGE_DECRYPT:
+                case PSA_KEY_USAGE_ENCRYPT:
+                    if (old_type != PSA_KEY_TYPE_RSA_KEY_PAIR) {
+                        return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+                    }
+                    break;
+                case PSA_KEY_USAGE_DERIVE:
+                    if (!(PSA_KEY_TYPE_IS_ECC_KEY_PAIR(old_type))) {
+                        return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+                    }
+                    break;
+                default:
+                    return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+            }
+            psa_key_type_t new_type = old_type;
+            /* Opaque keys are always key pairs, so we don't need a check
+             * on the input if the required usage is private. We just need
+             * to adjust the type correctly if the required usage is public. */
+            if (!want_private) {
+                new_type = PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(new_type);
+            }
+            more_usage = psa_get_key_usage_flags(&old_attributes);
+            if ((usage & more_usage) == 0) {
+                return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+            }
+            psa_set_key_type(attributes, new_type);
+            psa_set_key_bits(attributes, psa_get_key_bits(&old_attributes));
+            psa_set_key_algorithm(attributes, psa_get_key_algorithm(&old_attributes));
+            break;
+        }
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
+        default:
+            return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
+    }
+
+    psa_set_key_usage_flags(attributes, more_usage);
+    psa_set_key_enrollment_algorithm(attributes, PSA_ALG_NONE);
+
+    return 0;
+}
+
+#if defined(MBEDTLS_PK_USE_PSA_EC_DATA) || defined(MBEDTLS_USE_PSA_CRYPTO)
+static psa_status_t export_import_into_psa(mbedtls_svc_key_id_t old_key_id,
+                                           const psa_key_attributes_t *attributes,
+                                           mbedtls_svc_key_id_t *new_key_id)
+{
+    unsigned char key_buffer[PSA_EXPORT_KEY_PAIR_MAX_SIZE];
+    size_t key_length = 0;
+    psa_status_t status = psa_export_key(old_key_id,
+                                         key_buffer, sizeof(key_buffer),
+                                         &key_length);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+    status = psa_import_key(attributes, key_buffer, key_length, new_key_id);
+    mbedtls_platform_zeroize(key_buffer, key_length);
+    return status;
+}
+
+static int copy_into_psa(mbedtls_svc_key_id_t old_key_id,
+                         const psa_key_attributes_t *attributes,
+                         mbedtls_svc_key_id_t *new_key_id)
+{
+    /* Normally, we prefer copying: it's more efficient and works even
+     * for non-exportable keys. */
+    psa_status_t status = psa_copy_key(old_key_id, attributes, new_key_id);
+    if (status == PSA_ERROR_NOT_PERMITTED /*missing COPY usage*/ ||
+        status == PSA_ERROR_INVALID_ARGUMENT /*incompatible policy*/) {
+        /* There are edge cases where copying won't work, but export+import
+         * might:
+         * - If the old key does not allow PSA_KEY_USAGE_COPY.
+         * - If the old key's usage does not allow what attributes wants.
+         *   Because the key was intended for use in the pk module, and may
+         *   have had a policy chosen solely for what pk needs rather than
+         *   based on a detailed understanding of PSA policies, we are a bit
+         *   more liberal than psa_copy_key() here.
+         */
+        /* Here we need to check that the types match, otherwise we risk
+         * importing nonsensical data. */
+        psa_key_attributes_t old_attributes = PSA_KEY_ATTRIBUTES_INIT;
+        status = psa_get_key_attributes(old_key_id, &old_attributes);
+        if (status != PSA_SUCCESS) {
+            return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
+        }
+        psa_key_type_t old_type = psa_get_key_type(&old_attributes);
+        psa_reset_key_attributes(&old_attributes);
+        if (old_type != psa_get_key_type(attributes)) {
+            return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+        }
+        status = export_import_into_psa(old_key_id, attributes, new_key_id);
+    }
+    return PSA_PK_TO_MBEDTLS_ERR(status);
+}
+#endif /* MBEDTLS_PK_USE_PSA_EC_DATA || MBEDTLS_USE_PSA_CRYPTO */
+
+static int import_pair_into_psa(const mbedtls_pk_context *pk,
+                                const psa_key_attributes_t *attributes,
+                                mbedtls_svc_key_id_t *key_id)
+{
+    switch (mbedtls_pk_get_type(pk)) {
+#if defined(MBEDTLS_RSA_C)
+        case MBEDTLS_PK_RSA:
+        {
+            if (psa_get_key_type(attributes) != PSA_KEY_TYPE_RSA_KEY_PAIR) {
+                return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+            }
+            unsigned char key_buffer[
+                PSA_KEY_EXPORT_RSA_KEY_PAIR_MAX_SIZE(PSA_VENDOR_RSA_MAX_KEY_BITS)];
+            unsigned char *const key_end = key_buffer + sizeof(key_buffer);
+            unsigned char *key_data = key_end;
+            int ret = mbedtls_rsa_write_key(mbedtls_pk_rsa(*pk),
+                                            key_buffer, &key_data);
+            if (ret < 0) {
+                return ret;
+            }
+            size_t key_length = key_end - key_data;
+            ret = PSA_PK_TO_MBEDTLS_ERR(psa_import_key(attributes,
+                                                       key_data, key_length,
+                                                       key_id));
+            mbedtls_platform_zeroize(key_data, key_length);
+            return ret;
+        }
+#endif /* MBEDTLS_RSA_C */
+
+#if defined(MBEDTLS_PK_HAVE_ECC_KEYS)
+        case MBEDTLS_PK_ECKEY:
+        case MBEDTLS_PK_ECKEY_DH:
+        case MBEDTLS_PK_ECDSA:
+        {
+            /* We need to check the curve family, otherwise the import could
+             * succeed with nonsensical data.
+             * We don't check the bit-size: it's optional in attributes,
+             * and if it's specified, psa_import_key() will know from the key
+             * data length and will check that the bit-size matches. */
+            psa_key_type_t to_type = psa_get_key_type(attributes);
+#if defined(MBEDTLS_PK_USE_PSA_EC_DATA)
+            psa_ecc_family_t from_family = pk->ec_family;
+#else /* MBEDTLS_PK_USE_PSA_EC_DATA */
+            const mbedtls_ecp_keypair *ec = mbedtls_pk_ec_ro(*pk);
+            size_t from_bits = 0;
+            psa_ecc_family_t from_family = mbedtls_ecc_group_to_psa(ec->grp.id,
+                                                                    &from_bits);
+#endif /* MBEDTLS_PK_USE_PSA_EC_DATA */
+            if (to_type != PSA_KEY_TYPE_ECC_KEY_PAIR(from_family)) {
+                return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+            }
+
+#if defined(MBEDTLS_PK_USE_PSA_EC_DATA)
+            if (mbedtls_svc_key_id_is_null(pk->priv_id)) {
+                /* We have a public key and want a key pair. */
+                return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+            }
+            return copy_into_psa(pk->priv_id, attributes, key_id);
+#else /* MBEDTLS_PK_USE_PSA_EC_DATA */
+            if (ec->d.n == 0) {
+                /* Private key not set. Assume the input is a public key only.
+                 * (The other possibility is that it's an incomplete object
+                 * where the group is set but neither the public key nor
+                 * the private key. This is not possible through ecp.h
+                 * functions, so we don't bother reporting a more suitable
+                 * error in that case.) */
+                return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+            }
+            unsigned char key_buffer[PSA_BITS_TO_BYTES(PSA_VENDOR_ECC_MAX_CURVE_BITS)];
+            size_t key_length = 0;
+            int ret = mbedtls_ecp_write_key_ext(ec, &key_length,
+                                                key_buffer, sizeof(key_buffer));
+            if (ret < 0) {
+                return ret;
+            }
+            ret = PSA_PK_TO_MBEDTLS_ERR(psa_import_key(attributes,
+                                                       key_buffer, key_length,
+                                                       key_id));
+            mbedtls_platform_zeroize(key_buffer, key_length);
+            return ret;
+#endif /* MBEDTLS_PK_USE_PSA_EC_DATA */
+        }
+#endif /* MBEDTLS_PK_HAVE_ECC_KEYS */
+
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+        case MBEDTLS_PK_OPAQUE:
+            return copy_into_psa(pk->priv_id, attributes, key_id);
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
+        default:
+            return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
+    }
+}
+
+static int import_public_into_psa(const mbedtls_pk_context *pk,
+                                  const psa_key_attributes_t *attributes,
+                                  mbedtls_svc_key_id_t *key_id)
+{
+    psa_key_type_t psa_type = psa_get_key_type(attributes);
+
+#if defined(MBEDTLS_RSA_C) ||                                           \
+    (defined(MBEDTLS_PK_HAVE_ECC_KEYS) && !defined(MBEDTLS_PK_USE_PSA_EC_DATA)) || \
+    defined(MBEDTLS_USE_PSA_CRYPTO)
+    unsigned char key_buffer[PSA_EXPORT_PUBLIC_KEY_MAX_SIZE];
+#endif
+    unsigned char *key_data = NULL;
+    size_t key_length = 0;
+
+    switch (mbedtls_pk_get_type(pk)) {
+#if defined(MBEDTLS_RSA_C)
+        case MBEDTLS_PK_RSA:
+        {
+            if (psa_type != PSA_KEY_TYPE_RSA_PUBLIC_KEY) {
+                return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+            }
+            unsigned char *const key_end = key_buffer + sizeof(key_buffer);
+            key_data = key_end;
+            int ret = mbedtls_rsa_write_pubkey(mbedtls_pk_rsa(*pk),
+                                               key_buffer, &key_data);
+            if (ret < 0) {
+                return ret;
+            }
+            key_length = (size_t) ret;
+            break;
+        }
+#endif /*MBEDTLS_RSA_C */
+
+#if defined(MBEDTLS_PK_HAVE_ECC_KEYS)
+        case MBEDTLS_PK_ECKEY:
+        case MBEDTLS_PK_ECKEY_DH:
+        case MBEDTLS_PK_ECDSA:
+        {
+            /* We need to check the curve family, otherwise the import could
+             * succeed with nonsensical data.
+             * We don't check the bit-size: it's optional in attributes,
+             * and if it's specified, psa_import_key() will know from the key
+             * data length and will check that the bit-size matches. */
+#if defined(MBEDTLS_PK_USE_PSA_EC_DATA)
+            if (psa_type != PSA_KEY_TYPE_ECC_PUBLIC_KEY(pk->ec_family)) {
+                return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+            }
+            key_data = (unsigned char *) pk->pub_raw;
+            key_length = pk->pub_raw_len;
+#else /* MBEDTLS_PK_USE_PSA_EC_DATA */
+            const mbedtls_ecp_keypair *ec = mbedtls_pk_ec_ro(*pk);
+            size_t from_bits = 0;
+            psa_ecc_family_t from_family = mbedtls_ecc_group_to_psa(ec->grp.id,
+                                                                    &from_bits);
+            if (psa_type != PSA_KEY_TYPE_ECC_PUBLIC_KEY(from_family)) {
+                return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+            }
+            int ret = mbedtls_ecp_write_public_key(
+                ec, MBEDTLS_ECP_PF_UNCOMPRESSED,
+                &key_length, key_buffer, sizeof(key_buffer));
+            if (ret < 0) {
+                return ret;
+            }
+            key_data = key_buffer;
+#endif /* MBEDTLS_PK_USE_PSA_EC_DATA */
+            break;
+        }
+#endif /* MBEDTLS_PK_HAVE_ECC_KEYS */
+
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+        case MBEDTLS_PK_OPAQUE:
+        {
+            psa_key_attributes_t old_attributes = PSA_KEY_ATTRIBUTES_INIT;
+            psa_status_t status =
+                psa_get_key_attributes(pk->priv_id, &old_attributes);
+            if (status != PSA_SUCCESS) {
+                return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
+            }
+            psa_key_type_t old_type = psa_get_key_type(&old_attributes);
+            psa_reset_key_attributes(&old_attributes);
+            if (psa_type != PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(old_type)) {
+                return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+            }
+            status = psa_export_public_key(pk->priv_id,
+                                           key_buffer, sizeof(key_buffer),
+                                           &key_length);
+            if (status != PSA_SUCCESS) {
+                return PSA_PK_TO_MBEDTLS_ERR(status);
+            }
+            key_data = key_buffer;
+            break;
+        }
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
+        default:
+            return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
+    }
+
+    return PSA_PK_TO_MBEDTLS_ERR(psa_import_key(attributes,
+                                                key_data, key_length,
+                                                key_id));
+}
+
+int mbedtls_pk_import_into_psa(const mbedtls_pk_context *pk,
+                               const psa_key_attributes_t *attributes,
+                               mbedtls_svc_key_id_t *key_id)
+{
+    /* Set the output immediately so that it won't contain garbage even
+     * if we error out before calling psa_import_key(). */
+    *key_id = MBEDTLS_SVC_KEY_ID_INIT;
+
+#if defined(MBEDTLS_PK_RSA_ALT_SUPPORT)
+    if (mbedtls_pk_get_type(pk) == MBEDTLS_PK_RSA_ALT) {
+        return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
+    }
+#endif /* MBEDTLS_PK_RSA_ALT_SUPPORT */
+
+    int want_public = PSA_KEY_TYPE_IS_PUBLIC_KEY(psa_get_key_type(attributes));
+    if (want_public) {
+        return import_public_into_psa(pk, attributes, key_id);
+    } else {
+        return import_pair_into_psa(pk, attributes, key_id);
+    }
+}
+#endif /* MBEDTLS_PSA_CRYPTO_C */
+
 /*
  * Helper for mbedtls_pk_sign and mbedtls_pk_verify
  */
@@ -708,9 +1182,32 @@
     }
 
     if (mbedtls_pk_get_type(ctx) == MBEDTLS_PK_OPAQUE) {
+        psa_key_attributes_t key_attr = PSA_KEY_ATTRIBUTES_INIT;
+        psa_algorithm_t psa_alg, psa_enrollment_alg, sign_alg;
         psa_status_t status;
 
-        status = psa_sign_hash(ctx->priv_id, PSA_ALG_RSA_PSS(psa_md_alg),
+        status = psa_get_key_attributes(ctx->priv_id, &key_attr);
+        if (status != PSA_SUCCESS) {
+            return PSA_PK_RSA_TO_MBEDTLS_ERR(status);
+        }
+        psa_alg = psa_get_key_algorithm(&key_attr);
+        psa_enrollment_alg = psa_get_key_enrollment_algorithm(&key_attr);
+        psa_reset_key_attributes(&key_attr);
+
+        /* Since we're PK type is MBEDTLS_PK_RSASSA_PSS at least one between
+         * alg and enrollment alg should be of type RSA_PSS. */
+        if (PSA_ALG_IS_RSA_PSS(psa_alg)) {
+            sign_alg = psa_alg;
+        } else if (PSA_ALG_IS_RSA_PSS(psa_enrollment_alg)) {
+            sign_alg = psa_enrollment_alg;
+        } else {
+            /* The opaque key has no RSA PSS algorithm associated. */
+            return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
+        }
+        /* Adjust the hashing algorithm. */
+        sign_alg = (sign_alg & ~PSA_ALG_HASH_MASK) | PSA_ALG_GET_HASH(psa_md_alg);
+
+        status = psa_sign_hash(ctx->priv_id, sign_alg,
                                hash, hash_len,
                                sig, sig_size, sig_len);
         return PSA_PK_RSA_TO_MBEDTLS_ERR(status);
@@ -877,124 +1374,4 @@
     return ctx->pk_info->type;
 }
 
-#if defined(MBEDTLS_USE_PSA_CRYPTO)
-/*
- * Load the key to a PSA key slot,
- * then turn the PK context into a wrapper for that key slot.
- *
- * Currently only works for EC & RSA private keys.
- */
-int mbedtls_pk_wrap_as_opaque(mbedtls_pk_context *pk,
-                              mbedtls_svc_key_id_t *key,
-                              psa_algorithm_t alg,
-                              psa_key_usage_t usage,
-                              psa_algorithm_t alg2)
-{
-#if !defined(MBEDTLS_PK_HAVE_ECC_KEYS) && !defined(MBEDTLS_RSA_C)
-    ((void) pk);
-    ((void) key);
-    ((void) alg);
-    ((void) usage);
-    ((void) alg2);
-#else /* !MBEDTLS_PK_HAVE_ECC_KEYS && !MBEDTLS_RSA_C */
-#if defined(MBEDTLS_PK_HAVE_ECC_KEYS)
-    if (mbedtls_pk_get_type(pk) == MBEDTLS_PK_ECKEY) {
-        size_t d_len;
-        psa_ecc_family_t curve_id;
-        psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
-        psa_key_type_t key_type;
-        size_t bits;
-        psa_status_t status;
-
-        /* export the private key material in the format PSA wants */
-#if defined(MBEDTLS_PK_USE_PSA_EC_DATA)
-        unsigned char d[MBEDTLS_PSA_MAX_EC_KEY_PAIR_LENGTH];
-        status = psa_export_key(pk->priv_id, d, sizeof(d), &d_len);
-        if (status != PSA_SUCCESS) {
-            return psa_pk_status_to_mbedtls(status);
-        }
-
-        curve_id = pk->ec_family;
-        bits = pk->ec_bits;
-#else /* MBEDTLS_PK_USE_PSA_EC_DATA */
-        unsigned char d[MBEDTLS_ECP_MAX_BYTES];
-        mbedtls_ecp_keypair *ec = mbedtls_pk_ec_rw(*pk);
-        int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-
-        d_len = PSA_BITS_TO_BYTES(ec->grp.nbits);
-        if ((ret = mbedtls_ecp_write_key(ec, d, d_len)) != 0) {
-            return ret;
-        }
-
-        curve_id = mbedtls_ecc_group_to_psa(ec->grp.id, &bits);
-#endif /* MBEDTLS_PK_USE_PSA_EC_DATA */
-        key_type = PSA_KEY_TYPE_ECC_KEY_PAIR(curve_id);
-
-        /* prepare the key attributes */
-        psa_set_key_type(&attributes, key_type);
-        psa_set_key_bits(&attributes, bits);
-        psa_set_key_usage_flags(&attributes, usage);
-        psa_set_key_algorithm(&attributes, alg);
-        if (alg2 != PSA_ALG_NONE) {
-            psa_set_key_enrollment_algorithm(&attributes, alg2);
-        }
-
-        /* import private key into PSA */
-        status = psa_import_key(&attributes, d, d_len, key);
-        mbedtls_platform_zeroize(d, sizeof(d));
-        if (status != PSA_SUCCESS) {
-            return PSA_PK_TO_MBEDTLS_ERR(status);
-        }
-
-        /* make PK context wrap the key slot */
-        mbedtls_pk_free(pk);
-        mbedtls_pk_init(pk);
-
-        return mbedtls_pk_setup_opaque(pk, *key);
-    } else
-#endif /* MBEDTLS_PK_HAVE_ECC_KEYS */
-#if defined(MBEDTLS_RSA_C)
-    if (mbedtls_pk_get_type(pk) == MBEDTLS_PK_RSA) {
-        unsigned char buf[MBEDTLS_PK_RSA_PRV_DER_MAX_BYTES];
-        psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
-        int key_len;
-        psa_status_t status;
-
-        /* export the private key material in the format PSA wants */
-        key_len = mbedtls_pk_write_key_der(pk, buf, sizeof(buf));
-        if (key_len <= 0) {
-            return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
-        }
-
-        /* prepare the key attributes */
-        psa_set_key_type(&attributes, PSA_KEY_TYPE_RSA_KEY_PAIR);
-        psa_set_key_bits(&attributes, mbedtls_pk_get_bitlen(pk));
-        psa_set_key_usage_flags(&attributes, usage);
-        psa_set_key_algorithm(&attributes, alg);
-        if (alg2 != PSA_ALG_NONE) {
-            psa_set_key_enrollment_algorithm(&attributes, alg2);
-        }
-
-        /* import private key into PSA */
-        status = psa_import_key(&attributes,
-                                buf + sizeof(buf) - key_len,
-                                key_len, key);
-
-        mbedtls_platform_zeroize(buf, sizeof(buf));
-
-        if (status != PSA_SUCCESS) {
-            return PSA_PK_TO_MBEDTLS_ERR(status);
-        }
-
-        /* make PK context wrap the key slot */
-        mbedtls_pk_free(pk);
-        mbedtls_pk_init(pk);
-
-        return mbedtls_pk_setup_opaque(pk, *key);
-    } else
-#endif /* MBEDTLS_RSA_C */
-#endif /* !MBEDTLS_PK_HAVE_ECC_KEYS && !MBEDTLS_RSA_C */
-    return MBEDTLS_ERR_PK_TYPE_MISMATCH;
-}
-#endif /* MBEDTLS_USE_PSA_CRYPTO */
 #endif /* MBEDTLS_PK_C */
diff --git a/library/pk_internal.h b/library/pk_internal.h
index 3d5adf8..f5924ad 100644
--- a/library/pk_internal.h
+++ b/library/pk_internal.h
@@ -17,7 +17,7 @@
 #include "mbedtls/ecp.h"
 #endif
 
-#if defined(MBEDTLS_USE_PSA_CRYPTO)
+#if defined(MBEDTLS_PSA_CRYPTO_CLIENT)
 #include "psa/crypto.h"
 
 #include "psa_util_internal.h"
@@ -28,7 +28,7 @@
 #define PSA_PK_ECDSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status,   \
                                                                     psa_to_pk_ecdsa_errors,        \
                                                                     psa_pk_status_to_mbedtls)
-#endif /* MBEDTLS_USE_PSA_CRYPTO */
+#endif /* MBEDTLS_PSA_CRYPTO_CLIENT */
 
 /* Headers/footers for PEM files */
 #define PEM_BEGIN_PUBLIC_KEY    "-----BEGIN PUBLIC KEY-----"
@@ -144,4 +144,8 @@
     int (*f_rng)(void *, unsigned char *, size_t), void *p_rng);
 #endif
 
+#if defined(MBEDTLS_FS_IO)
+int mbedtls_pk_load_file(const char *path, unsigned char **buf, size_t *n);
+#endif
+
 #endif /* MBEDTLS_PK_INTERNAL_H */
diff --git a/library/pk_wrap.c b/library/pk_wrap.c
index c232650..846175e 100644
--- a/library/pk_wrap.c
+++ b/library/pk_wrap.c
@@ -29,9 +29,11 @@
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
 #include "psa_util_internal.h"
 #include "psa/crypto.h"
+#include "mbedtls/psa_util.h"
 
 #if defined(MBEDTLS_RSA_C)
 #include "pkwrite.h"
+#include "rsa_internal.h"
 #endif
 
 #if defined(MBEDTLS_PK_CAN_ECDSA_SOME)
@@ -56,7 +58,7 @@
 static size_t rsa_get_bitlen(mbedtls_pk_context *pk)
 {
     const mbedtls_rsa_context *rsa = (const mbedtls_rsa_context *) pk->pk_ctx;
-    return 8 * mbedtls_rsa_get_len(rsa);
+    return mbedtls_rsa_get_bitlen(rsa);
 }
 
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
@@ -69,11 +71,10 @@
     psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
     mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT;
     psa_status_t status;
-    mbedtls_pk_context key;
     int key_len;
     unsigned char buf[MBEDTLS_PK_RSA_PUB_DER_MAX_BYTES];
-    psa_algorithm_t psa_alg_md =
-        PSA_ALG_RSA_PKCS1V15_SIGN(mbedtls_md_psa_alg_from_type(md_alg));
+    unsigned char *p = buf + sizeof(buf);
+    psa_algorithm_t psa_alg_md;
     size_t rsa_len = mbedtls_rsa_get_len(rsa);
 
 #if SIZE_MAX > UINT_MAX
@@ -82,15 +83,17 @@
     }
 #endif
 
+    if (mbedtls_rsa_get_padding_mode(rsa) == MBEDTLS_RSA_PKCS_V21) {
+        psa_alg_md = PSA_ALG_RSA_PSS(mbedtls_md_psa_alg_from_type(md_alg));
+    } else {
+        psa_alg_md = PSA_ALG_RSA_PKCS1V15_SIGN(mbedtls_md_psa_alg_from_type(md_alg));
+    }
+
     if (sig_len < rsa_len) {
         return MBEDTLS_ERR_RSA_VERIFY_FAILED;
     }
 
-    /* mbedtls_pk_write_pubkey_der() expects a full PK context;
-     * re-construct one to make it happy */
-    key.pk_info = &mbedtls_rsa_info;
-    key.pk_ctx = rsa;
-    key_len = mbedtls_pk_write_pubkey_der(&key, buf, sizeof(buf));
+    key_len = mbedtls_rsa_write_pubkey(rsa, buf, &p);
     if (key_len <= 0) {
         return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
     }
@@ -172,14 +175,15 @@
     psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
     mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT;
     psa_status_t status;
-    mbedtls_pk_context key;
     int key_len;
     unsigned char *buf = NULL;
+    unsigned char *p;
+
     buf = mbedtls_calloc(1, MBEDTLS_PK_RSA_PRV_DER_MAX_BYTES);
     if (buf == NULL) {
         return MBEDTLS_ERR_PK_ALLOC_FAILED;
     }
-    mbedtls_pk_info_t pk_info = mbedtls_rsa_info;
+    p = buf + MBEDTLS_PK_RSA_PRV_DER_MAX_BYTES;
 
     *sig_len = mbedtls_rsa_get_len(rsa_ctx);
     if (sig_size < *sig_len) {
@@ -187,11 +191,7 @@
         return MBEDTLS_ERR_PK_BUFFER_TOO_SMALL;
     }
 
-    /* mbedtls_pk_write_key_der() expects a full PK context;
-     * re-construct one to make it happy */
-    key.pk_info = &pk_info;
-    key.pk_ctx = rsa_ctx;
-    key_len = mbedtls_pk_write_key_der(&key, buf, MBEDTLS_PK_RSA_PRV_DER_MAX_BYTES);
+    key_len = mbedtls_rsa_write_key(rsa_ctx, buf, &p);
     if (key_len <= 0) {
         mbedtls_free(buf);
         return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
@@ -240,10 +240,14 @@
     if (psa_md_alg == 0) {
         return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
     }
+    psa_algorithm_t psa_alg;
+    if (mbedtls_rsa_get_padding_mode(mbedtls_pk_rsa(*pk)) == MBEDTLS_RSA_PKCS_V21) {
+        psa_alg = PSA_ALG_RSA_PSS(psa_md_alg);
+    } else {
+        psa_alg = PSA_ALG_RSA_PKCS1V15_SIGN(psa_md_alg);
+    }
 
-    return mbedtls_pk_psa_rsa_sign_ext(PSA_ALG_RSA_PKCS1V15_SIGN(
-                                           psa_md_alg),
-                                       pk->pk_ctx, hash, hash_len,
+    return mbedtls_pk_psa_rsa_sign_ext(psa_alg, pk->pk_ctx, hash, hash_len,
                                        sig, sig_size, sig_len);
 }
 #else /* MBEDTLS_USE_PSA_CRYPTO */
@@ -281,36 +285,33 @@
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
     mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT;
+    psa_algorithm_t psa_md_alg, decrypt_alg;
     psa_status_t status;
-    mbedtls_pk_context key;
     int key_len;
     unsigned char buf[MBEDTLS_PK_RSA_PRV_DER_MAX_BYTES];
+    unsigned char *p = buf + sizeof(buf);
 
     ((void) f_rng);
     ((void) p_rng);
 
-#if !defined(MBEDTLS_RSA_ALT)
-    if (rsa->padding != MBEDTLS_RSA_PKCS_V15) {
-        return MBEDTLS_ERR_RSA_INVALID_PADDING;
-    }
-#endif /* !MBEDTLS_RSA_ALT */
-
     if (ilen != mbedtls_rsa_get_len(rsa)) {
         return MBEDTLS_ERR_RSA_BAD_INPUT_DATA;
     }
 
-    /* mbedtls_pk_write_key_der() expects a full PK context;
-     * re-construct one to make it happy */
-    key.pk_info = &mbedtls_rsa_info;
-    key.pk_ctx = rsa;
-    key_len = mbedtls_pk_write_key_der(&key, buf, sizeof(buf));
+    key_len = mbedtls_rsa_write_key(rsa, buf, &p);
     if (key_len <= 0) {
         return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
     }
 
     psa_set_key_type(&attributes, PSA_KEY_TYPE_RSA_KEY_PAIR);
     psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DECRYPT);
-    psa_set_key_algorithm(&attributes, PSA_ALG_RSA_PKCS1V15_CRYPT);
+    if (mbedtls_rsa_get_padding_mode(rsa) == MBEDTLS_RSA_PKCS_V21) {
+        psa_md_alg = mbedtls_md_psa_alg_from_type((mbedtls_md_type_t) mbedtls_rsa_get_md_alg(rsa));
+        decrypt_alg = PSA_ALG_RSA_OAEP(psa_md_alg);
+    } else {
+        decrypt_alg = PSA_ALG_RSA_PKCS1V15_CRYPT;
+    }
+    psa_set_key_algorithm(&attributes, decrypt_alg);
 
     status = psa_import_key(&attributes,
                             buf + sizeof(buf) - key_len, key_len,
@@ -320,7 +321,7 @@
         goto cleanup;
     }
 
-    status = psa_asymmetric_decrypt(key_id, PSA_ALG_RSA_PKCS1V15_CRYPT,
+    status = psa_asymmetric_decrypt(key_id, decrypt_alg,
                                     input, ilen,
                                     NULL, 0,
                                     output, osize, olen);
@@ -367,35 +368,31 @@
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
     mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT;
+    psa_algorithm_t psa_md_alg;
     psa_status_t status;
-    mbedtls_pk_context key;
     int key_len;
     unsigned char buf[MBEDTLS_PK_RSA_PUB_DER_MAX_BYTES];
+    unsigned char *p = buf + sizeof(buf);
 
     ((void) f_rng);
     ((void) p_rng);
 
-#if !defined(MBEDTLS_RSA_ALT)
-    if (rsa->padding != MBEDTLS_RSA_PKCS_V15) {
-        return MBEDTLS_ERR_RSA_INVALID_PADDING;
-    }
-#endif
-
     if (mbedtls_rsa_get_len(rsa) > osize) {
         return MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE;
     }
 
-    /* mbedtls_pk_write_pubkey_der() expects a full PK context;
-     * re-construct one to make it happy */
-    key.pk_info = &mbedtls_rsa_info;
-    key.pk_ctx = rsa;
-    key_len = mbedtls_pk_write_pubkey_der(&key, buf, sizeof(buf));
+    key_len = mbedtls_rsa_write_pubkey(rsa, buf, &p);
     if (key_len <= 0) {
         return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
     }
 
     psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT);
-    psa_set_key_algorithm(&attributes, PSA_ALG_RSA_PKCS1V15_CRYPT);
+    if (mbedtls_rsa_get_padding_mode(rsa) == MBEDTLS_RSA_PKCS_V21) {
+        psa_md_alg = mbedtls_md_psa_alg_from_type((mbedtls_md_type_t) mbedtls_rsa_get_md_alg(rsa));
+        psa_set_key_algorithm(&attributes, PSA_ALG_RSA_OAEP(psa_md_alg));
+    } else {
+        psa_set_key_algorithm(&attributes, PSA_ALG_RSA_PKCS1V15_CRYPT);
+    }
     psa_set_key_type(&attributes, PSA_KEY_TYPE_RSA_PUBLIC_KEY);
 
     status = psa_import_key(&attributes,
@@ -536,66 +533,6 @@
 
 #if defined(MBEDTLS_PK_CAN_ECDSA_VERIFY)
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
-/*
- * An ASN.1 encoded signature is a sequence of two ASN.1 integers. Parse one of
- * those integers and convert it to the fixed-length encoding expected by PSA.
- */
-static int extract_ecdsa_sig_int(unsigned char **from, const unsigned char *end,
-                                 unsigned char *to, size_t to_len)
-{
-    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    size_t unpadded_len, padding_len;
-
-    if ((ret = mbedtls_asn1_get_tag(from, end, &unpadded_len,
-                                    MBEDTLS_ASN1_INTEGER)) != 0) {
-        return ret;
-    }
-
-    while (unpadded_len > 0 && **from == 0x00) {
-        (*from)++;
-        unpadded_len--;
-    }
-
-    if (unpadded_len > to_len || unpadded_len == 0) {
-        return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH;
-    }
-
-    padding_len = to_len - unpadded_len;
-    memset(to, 0x00, padding_len);
-    memcpy(to + padding_len, *from, unpadded_len);
-    (*from) += unpadded_len;
-
-    return 0;
-}
-
-/*
- * Convert a signature from an ASN.1 sequence of two integers
- * to a raw {r,s} buffer. Note: the provided sig buffer must be at least
- * twice as big as int_size.
- */
-static int extract_ecdsa_sig(unsigned char **p, const unsigned char *end,
-                             unsigned char *sig, size_t int_size)
-{
-    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    size_t tmp_size;
-
-    if ((ret = mbedtls_asn1_get_tag(p, end, &tmp_size,
-                                    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) {
-        return ret;
-    }
-
-    /* Extract r */
-    if ((ret = extract_ecdsa_sig_int(p, end, sig, int_size)) != 0) {
-        return ret;
-    }
-    /* Extract s */
-    if ((ret = extract_ecdsa_sig_int(p, end, sig + int_size, int_size)) != 0) {
-        return ret;
-    }
-
-    return 0;
-}
-
 /* Common helper for ECDSA verify using PSA functions. */
 static int ecdsa_verify_psa(unsigned char *key, size_t key_len,
                             psa_ecc_family_t curve, size_t curve_bits,
@@ -607,6 +544,7 @@
     mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT;
     psa_algorithm_t psa_sig_md = PSA_ALG_ECDSA_ANY;
     size_t signature_len = PSA_ECDSA_SIGNATURE_SIZE(curve_bits);
+    size_t converted_sig_len;
     unsigned char extracted_sig[PSA_VENDOR_ECDSA_SIGNATURE_MAX_SIZE];
     unsigned char *p;
     psa_status_t status;
@@ -631,11 +569,14 @@
     }
 
     p = (unsigned char *) sig;
-    /* extract_ecdsa_sig's last parameter is the size
-     * of each integer to be parsed, so it's actually half
-     * the size of the signature. */
-    if ((ret = extract_ecdsa_sig(&p, sig + sig_len, extracted_sig,
-                                 signature_len/2)) != 0) {
+    ret = mbedtls_ecdsa_der_to_raw(curve_bits, p, sig_len, extracted_sig,
+                                   sizeof(extracted_sig), &converted_sig_len);
+    if (ret != 0) {
+        goto cleanup;
+    }
+
+    if (converted_sig_len != signature_len) {
+        ret = MBEDTLS_ERR_PK_BAD_INPUT_DATA;
         goto cleanup;
     }
 
@@ -646,10 +587,6 @@
         goto cleanup;
     }
 
-    if (p != sig + sig_len) {
-        ret = MBEDTLS_ERR_PK_SIG_LEN_MISMATCH;
-        goto cleanup;
-    }
     ret = 0;
 
 cleanup:
@@ -751,90 +688,6 @@
 
 #if defined(MBEDTLS_PK_CAN_ECDSA_SIGN)
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
-/*
- * Simultaneously convert and move raw MPI from the beginning of a buffer
- * to an ASN.1 MPI at the end of the buffer.
- * See also mbedtls_asn1_write_mpi().
- *
- * p: pointer to the end of the output buffer
- * start: start of the output buffer, and also of the mpi to write at the end
- * n_len: length of the mpi to read from start
- */
-static int asn1_write_mpibuf(unsigned char **p, unsigned char *start,
-                             size_t n_len)
-{
-    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    size_t len = 0;
-
-    if ((size_t) (*p - start) < n_len) {
-        return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
-    }
-
-    len = n_len;
-    *p -= len;
-    memmove(*p, start, len);
-
-    /* ASN.1 DER encoding requires minimal length, so skip leading 0s.
-     * Neither r nor s should be 0, but as a failsafe measure, still detect
-     * that rather than overflowing the buffer in case of a PSA error. */
-    while (len > 0 && **p == 0x00) {
-        ++(*p);
-        --len;
-    }
-
-    /* this is only reached if the signature was invalid */
-    if (len == 0) {
-        return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED;
-    }
-
-    /* if the msb is 1, ASN.1 requires that we prepend a 0.
-     * Neither r nor s can be 0, so we can assume len > 0 at all times. */
-    if (**p & 0x80) {
-        if (*p - start < 1) {
-            return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
-        }
-
-        *--(*p) = 0x00;
-        len += 1;
-    }
-
-    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len));
-    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start,
-                                                     MBEDTLS_ASN1_INTEGER));
-
-    return (int) len;
-}
-
-/* Transcode signature from PSA format to ASN.1 sequence.
- * See ecdsa_signature_to_asn1 in ecdsa.c, but with byte buffers instead of
- * MPIs, and in-place.
- *
- * [in/out] sig: the signature pre- and post-transcoding
- * [in/out] sig_len: signature length pre- and post-transcoding
- * [int] buf_len: the available size the in/out buffer
- */
-static int pk_ecdsa_sig_asn1_from_psa(unsigned char *sig, size_t *sig_len,
-                                      size_t buf_len)
-{
-    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    size_t len = 0;
-    const size_t rs_len = *sig_len / 2;
-    unsigned char *p = sig + buf_len;
-
-    MBEDTLS_ASN1_CHK_ADD(len, asn1_write_mpibuf(&p, sig + rs_len, rs_len));
-    MBEDTLS_ASN1_CHK_ADD(len, asn1_write_mpibuf(&p, sig, rs_len));
-
-    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, sig, len));
-    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&p, sig,
-                                                     MBEDTLS_ASN1_CONSTRUCTED |
-                                                     MBEDTLS_ASN1_SEQUENCE));
-
-    memmove(sig, p, len);
-    *sig_len = len;
-
-    return 0;
-}
-
 /* Common helper for ECDSA sign using PSA functions.
  * Instead of extracting key's properties in order to check which kind of ECDSA
  * signature it supports, we try both deterministic and non-deterministic.
@@ -845,6 +698,15 @@
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     psa_status_t status;
+    psa_key_attributes_t key_attr = PSA_KEY_ATTRIBUTES_INIT;
+    size_t key_bits = 0;
+
+    status = psa_get_key_attributes(key_id, &key_attr);
+    if (status != PSA_SUCCESS) {
+        return PSA_PK_ECDSA_TO_MBEDTLS_ERR(status);
+    }
+    key_bits = psa_get_key_bits(&key_attr);
+    psa_reset_key_attributes(&key_attr);
 
     status = psa_sign_hash(key_id,
                            PSA_ALG_DETERMINISTIC_ECDSA(mbedtls_md_psa_alg_from_type(md_alg)),
@@ -863,7 +725,7 @@
     }
 
 done:
-    ret = pk_ecdsa_sig_asn1_from_psa(sig, sig_len, sig_size);
+    ret = mbedtls_ecdsa_raw_to_der(key_bits, sig, *sig_len, sig, sig_size, sig_len);
 
     return ret;
 }
diff --git a/library/pkcs7.c b/library/pkcs7.c
index 0869c2e..3aac662 100644
--- a/library/pkcs7.c
+++ b/library/pkcs7.c
@@ -7,7 +7,7 @@
 #include "mbedtls/build_info.h"
 #if defined(MBEDTLS_PKCS7_C)
 #include "mbedtls/pkcs7.h"
-#include "mbedtls/x509.h"
+#include "x509_internal.h"
 #include "mbedtls/asn1.h"
 #include "mbedtls/x509_crt.h"
 #include "mbedtls/x509_crl.h"
diff --git a/library/pkparse.c b/library/pkparse.c
index 5f95545..5a3d3b2 100644
--- a/library/pkparse.c
+++ b/library/pkparse.c
@@ -28,6 +28,7 @@
 /* Key types */
 #if defined(MBEDTLS_RSA_C)
 #include "mbedtls/rsa.h"
+#include "rsa_internal.h"
 #endif
 
 /* Extended formats */
@@ -757,68 +758,6 @@
 
 #endif /* MBEDTLS_PK_HAVE_ECC_KEYS */
 
-#if defined(MBEDTLS_RSA_C)
-/*
- *  RSAPublicKey ::= SEQUENCE {
- *      modulus           INTEGER,  -- n
- *      publicExponent    INTEGER   -- e
- *  }
- */
-static int pk_get_rsapubkey(unsigned char **p,
-                            const unsigned char *end,
-                            mbedtls_rsa_context *rsa)
-{
-    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    size_t len;
-
-    if ((ret = mbedtls_asn1_get_tag(p, end, &len,
-                                    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) {
-        return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_PUBKEY, ret);
-    }
-
-    if (*p + len != end) {
-        return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_PUBKEY,
-                                 MBEDTLS_ERR_ASN1_LENGTH_MISMATCH);
-    }
-
-    /* Import N */
-    if ((ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_INTEGER)) != 0) {
-        return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_PUBKEY, ret);
-    }
-
-    if ((ret = mbedtls_rsa_import_raw(rsa, *p, len, NULL, 0, NULL, 0,
-                                      NULL, 0, NULL, 0)) != 0) {
-        return MBEDTLS_ERR_PK_INVALID_PUBKEY;
-    }
-
-    *p += len;
-
-    /* Import E */
-    if ((ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_INTEGER)) != 0) {
-        return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_PUBKEY, ret);
-    }
-
-    if ((ret = mbedtls_rsa_import_raw(rsa, NULL, 0, NULL, 0, NULL, 0,
-                                      NULL, 0, *p, len)) != 0) {
-        return MBEDTLS_ERR_PK_INVALID_PUBKEY;
-    }
-
-    *p += len;
-
-    if (mbedtls_rsa_complete(rsa) != 0 ||
-        mbedtls_rsa_check_pubkey(rsa) != 0) {
-        return MBEDTLS_ERR_PK_INVALID_PUBKEY;
-    }
-
-    if (*p != end) {
-        return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_PUBKEY,
-                                 MBEDTLS_ERR_ASN1_LENGTH_MISMATCH);
-    }
-
-    return 0;
-}
-#endif /* MBEDTLS_RSA_C */
-
 /* Get a PK algorithm identifier
  *
  *  AlgorithmIdentifier  ::=  SEQUENCE  {
@@ -911,7 +850,17 @@
 
 #if defined(MBEDTLS_RSA_C)
     if (pk_alg == MBEDTLS_PK_RSA) {
-        ret = pk_get_rsapubkey(p, end, mbedtls_pk_rsa(*pk));
+        ret = mbedtls_rsa_parse_pubkey(mbedtls_pk_rsa(*pk), *p, (size_t) (end - *p));
+        if (ret == 0) {
+            /* On success all the input has been consumed by the parsing function. */
+            *p += end - *p;
+        } else if ((ret <= MBEDTLS_ERR_ASN1_OUT_OF_DATA) &&
+                   (ret >= MBEDTLS_ERR_ASN1_BUF_TOO_SMALL)) {
+            /* In case of ASN1 error codes add MBEDTLS_ERR_PK_INVALID_PUBKEY. */
+            ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_PUBKEY, ret);
+        } else {
+            ret = MBEDTLS_ERR_PK_INVALID_PUBKEY;
+        }
     } else
 #endif /* MBEDTLS_RSA_C */
 #if defined(MBEDTLS_PK_HAVE_ECC_KEYS)
@@ -944,195 +893,6 @@
     return ret;
 }
 
-#if defined(MBEDTLS_RSA_C)
-/*
- * Wrapper around mbedtls_asn1_get_mpi() that rejects zero.
- *
- * The value zero is:
- * - never a valid value for an RSA parameter
- * - interpreted as "omitted, please reconstruct" by mbedtls_rsa_complete().
- *
- * Since values can't be omitted in PKCS#1, passing a zero value to
- * rsa_complete() would be incorrect, so reject zero values early.
- */
-static int asn1_get_nonzero_mpi(unsigned char **p,
-                                const unsigned char *end,
-                                mbedtls_mpi *X)
-{
-    int ret;
-
-    ret = mbedtls_asn1_get_mpi(p, end, X);
-    if (ret != 0) {
-        return ret;
-    }
-
-    if (mbedtls_mpi_cmp_int(X, 0) == 0) {
-        return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT;
-    }
-
-    return 0;
-}
-
-/*
- * Parse a PKCS#1 encoded private RSA key
- */
-static int pk_parse_key_pkcs1_der(mbedtls_rsa_context *rsa,
-                                  const unsigned char *key,
-                                  size_t keylen)
-{
-    int ret, version;
-    size_t len;
-    unsigned char *p, *end;
-
-    mbedtls_mpi T;
-    mbedtls_mpi_init(&T);
-
-    p = (unsigned char *) key;
-    end = p + keylen;
-
-    /*
-     * This function parses the RSAPrivateKey (PKCS#1)
-     *
-     *  RSAPrivateKey ::= SEQUENCE {
-     *      version           Version,
-     *      modulus           INTEGER,  -- n
-     *      publicExponent    INTEGER,  -- e
-     *      privateExponent   INTEGER,  -- d
-     *      prime1            INTEGER,  -- p
-     *      prime2            INTEGER,  -- q
-     *      exponent1         INTEGER,  -- d mod (p-1)
-     *      exponent2         INTEGER,  -- d mod (q-1)
-     *      coefficient       INTEGER,  -- (inverse of q) mod p
-     *      otherPrimeInfos   OtherPrimeInfos OPTIONAL
-     *  }
-     */
-    if ((ret = mbedtls_asn1_get_tag(&p, end, &len,
-                                    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) {
-        return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret);
-    }
-
-    end = p + len;
-
-    if ((ret = mbedtls_asn1_get_int(&p, end, &version)) != 0) {
-        return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret);
-    }
-
-    if (version != 0) {
-        return MBEDTLS_ERR_PK_KEY_INVALID_VERSION;
-    }
-
-    /* Import N */
-    if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
-        (ret = mbedtls_rsa_import(rsa, &T, NULL, NULL,
-                                  NULL, NULL)) != 0) {
-        goto cleanup;
-    }
-
-    /* Import E */
-    if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
-        (ret = mbedtls_rsa_import(rsa, NULL, NULL, NULL,
-                                  NULL, &T)) != 0) {
-        goto cleanup;
-    }
-
-    /* Import D */
-    if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
-        (ret = mbedtls_rsa_import(rsa, NULL, NULL, NULL,
-                                  &T, NULL)) != 0) {
-        goto cleanup;
-    }
-
-    /* Import P */
-    if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
-        (ret = mbedtls_rsa_import(rsa, NULL, &T, NULL,
-                                  NULL, NULL)) != 0) {
-        goto cleanup;
-    }
-
-    /* Import Q */
-    if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
-        (ret = mbedtls_rsa_import(rsa, NULL, NULL, &T,
-                                  NULL, NULL)) != 0) {
-        goto cleanup;
-    }
-
-#if !defined(MBEDTLS_RSA_NO_CRT) && !defined(MBEDTLS_RSA_ALT)
-    /*
-     * The RSA CRT parameters DP, DQ and QP are nominally redundant, in
-     * that they can be easily recomputed from D, P and Q. However by
-     * parsing them from the PKCS1 structure it is possible to avoid
-     * recalculating them which both reduces the overhead of loading
-     * RSA private keys into memory and also avoids side channels which
-     * can arise when computing those values, since all of D, P, and Q
-     * are secret. See https://eprint.iacr.org/2020/055 for a
-     * description of one such attack.
-     */
-
-    /* Import DP */
-    if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
-        (ret = mbedtls_mpi_copy(&rsa->DP, &T)) != 0) {
-        goto cleanup;
-    }
-
-    /* Import DQ */
-    if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
-        (ret = mbedtls_mpi_copy(&rsa->DQ, &T)) != 0) {
-        goto cleanup;
-    }
-
-    /* Import QP */
-    if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
-        (ret = mbedtls_mpi_copy(&rsa->QP, &T)) != 0) {
-        goto cleanup;
-    }
-
-#else
-    /* Verify existence of the CRT params */
-    if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
-        (ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
-        (ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0) {
-        goto cleanup;
-    }
-#endif
-
-    /* rsa_complete() doesn't complete anything with the default
-     * implementation but is still called:
-     * - for the benefit of alternative implementation that may want to
-     *   pre-compute stuff beyond what's provided (eg Montgomery factors)
-     * - as is also sanity-checks the key
-     *
-     * Furthermore, we also check the public part for consistency with
-     * mbedtls_pk_parse_pubkey(), as it includes size minima for example.
-     */
-    if ((ret = mbedtls_rsa_complete(rsa)) != 0 ||
-        (ret = mbedtls_rsa_check_pubkey(rsa)) != 0) {
-        goto cleanup;
-    }
-
-    if (p != end) {
-        ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT,
-                                MBEDTLS_ERR_ASN1_LENGTH_MISMATCH);
-    }
-
-cleanup:
-
-    mbedtls_mpi_free(&T);
-
-    if (ret != 0) {
-        /* Wrap error code if it's coming from a lower level */
-        if ((ret & 0xff80) == 0) {
-            ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret);
-        } else {
-            ret = MBEDTLS_ERR_PK_KEY_INVALID_FORMAT;
-        }
-
-        mbedtls_rsa_free(rsa);
-    }
-
-    return ret;
-}
-#endif /* MBEDTLS_RSA_C */
-
 #if defined(MBEDTLS_PK_HAVE_ECC_KEYS)
 /*
  * Parse a SEC1 encoded private EC key
@@ -1348,7 +1108,7 @@
 
 #if defined(MBEDTLS_RSA_C)
     if (pk_alg == MBEDTLS_PK_RSA) {
-        if ((ret = pk_parse_key_pkcs1_der(mbedtls_pk_rsa(*pk), p, len)) != 0) {
+        if ((ret = mbedtls_rsa_parse_key(mbedtls_pk_rsa(*pk), p, len)) != 0) {
             mbedtls_pk_free(pk);
             return ret;
         }
@@ -1538,8 +1298,8 @@
     if (ret == 0) {
         pk_info = mbedtls_pk_info_from_type(MBEDTLS_PK_RSA);
         if ((ret = mbedtls_pk_setup(pk, pk_info)) != 0 ||
-            (ret = pk_parse_key_pkcs1_der(mbedtls_pk_rsa(*pk),
-                                          pem.buf, pem.buflen)) != 0) {
+            (ret = mbedtls_rsa_parse_key(mbedtls_pk_rsa(*pk),
+                                         pem.buf, pem.buflen)) != 0) {
             mbedtls_pk_free(pk);
         }
 
@@ -1679,7 +1439,7 @@
 
     pk_info = mbedtls_pk_info_from_type(MBEDTLS_PK_RSA);
     if (mbedtls_pk_setup(pk, pk_info) == 0 &&
-        pk_parse_key_pkcs1_der(mbedtls_pk_rsa(*pk), key, keylen) == 0) {
+        mbedtls_rsa_parse_key(mbedtls_pk_rsa(*pk), key, keylen) == 0) {
         return 0;
     }
 
@@ -1754,7 +1514,7 @@
             return ret;
         }
 
-        if ((ret = pk_get_rsapubkey(&p, p + pem.buflen, mbedtls_pk_rsa(*ctx))) != 0) {
+        if ((ret = mbedtls_rsa_parse_pubkey(mbedtls_pk_rsa(*ctx), p, pem.buflen)) != 0) {
             mbedtls_pk_free(ctx);
         }
 
@@ -1801,13 +1561,12 @@
     }
 
     p = (unsigned char *) key;
-    ret = pk_get_rsapubkey(&p, p + keylen, mbedtls_pk_rsa(*ctx));
+    ret = mbedtls_rsa_parse_pubkey(mbedtls_pk_rsa(*ctx), p, keylen);
     if (ret == 0) {
         return ret;
     }
     mbedtls_pk_free(ctx);
-    if (ret != (MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_PUBKEY,
-                                  MBEDTLS_ERR_ASN1_UNEXPECTED_TAG))) {
+    if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) {
         return ret;
     }
 #endif /* MBEDTLS_RSA_C */
diff --git a/library/pkwrite.c b/library/pkwrite.c
index 1f0d399..5e009c5 100644
--- a/library/pkwrite.c
+++ b/library/pkwrite.c
@@ -32,6 +32,9 @@
 #if defined(MBEDTLS_PEM_WRITE_C)
 #include "mbedtls/pem.h"
 #endif
+#if defined(MBEDTLS_RSA_C)
+#include "rsa_internal.h"
+#endif
 
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
 #include "psa/crypto.h"
@@ -56,60 +59,13 @@
  * Internal functions for RSA keys.
  ******************************************************************************/
 #if defined(MBEDTLS_RSA_C)
-/*
- *  RSAPublicKey ::= SEQUENCE {
- *      modulus           INTEGER,  -- n
- *      publicExponent    INTEGER   -- e
- *  }
- */
-static int pk_write_rsa_pubkey(unsigned char **p, unsigned char *start,
-                               const mbedtls_pk_context *pk)
-{
-    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    size_t len = 0;
-    mbedtls_mpi T;
-    mbedtls_rsa_context *rsa = mbedtls_pk_rsa(*pk);
-
-    mbedtls_mpi_init(&T);
-
-    /* Export E */
-    if ((ret = mbedtls_rsa_export(rsa, NULL, NULL, NULL, NULL, &T)) != 0 ||
-        (ret = mbedtls_asn1_write_mpi(p, start, &T)) < 0) {
-        goto end_of_export;
-    }
-    len += ret;
-
-    /* Export N */
-    if ((ret = mbedtls_rsa_export(rsa, &T, NULL, NULL, NULL, NULL)) != 0 ||
-        (ret = mbedtls_asn1_write_mpi(p, start, &T)) < 0) {
-        goto end_of_export;
-    }
-    len += ret;
-
-end_of_export:
-
-    mbedtls_mpi_free(&T);
-    if (ret < 0) {
-        return ret;
-    }
-
-    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len));
-    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_CONSTRUCTED |
-                                                     MBEDTLS_ASN1_SEQUENCE));
-
-    return (int) len;
-}
-
 static int pk_write_rsa_der(unsigned char **p, unsigned char *buf,
                             const mbedtls_pk_context *pk)
 {
-    size_t len = 0;
-    int ret;
-
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
     if (mbedtls_pk_get_type(pk) == MBEDTLS_PK_OPAQUE) {
         uint8_t tmp[PSA_EXPORT_KEY_PAIR_MAX_SIZE];
-        size_t tmp_len = 0;
+        size_t len = 0, tmp_len = 0;
 
         if (psa_export_key(pk->priv_id, tmp, sizeof(tmp), &tmp_len) != PSA_SUCCESS) {
             return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
@@ -118,94 +74,11 @@
         memcpy(*p, tmp, tmp_len);
         len += tmp_len;
         mbedtls_platform_zeroize(tmp, sizeof(tmp));
-    } else
-#endif /* MBEDTLS_USE_PSA_CRYPTO */
-    {
-        mbedtls_mpi T; /* Temporary holding the exported parameters */
-        mbedtls_rsa_context *rsa = mbedtls_pk_rsa(*pk);
 
-        /*
-         * Export the parameters one after another to avoid simultaneous copies.
-         */
-
-        mbedtls_mpi_init(&T);
-
-        /* Export QP */
-        if ((ret = mbedtls_rsa_export_crt(rsa, NULL, NULL, &T)) != 0 ||
-            (ret = mbedtls_asn1_write_mpi(p, buf, &T)) < 0) {
-            goto end_of_export;
-        }
-        len += ret;
-
-        /* Export DQ */
-        if ((ret = mbedtls_rsa_export_crt(rsa, NULL, &T, NULL)) != 0 ||
-            (ret = mbedtls_asn1_write_mpi(p, buf, &T)) < 0) {
-            goto end_of_export;
-        }
-        len += ret;
-
-        /* Export DP */
-        if ((ret = mbedtls_rsa_export_crt(rsa, &T, NULL, NULL)) != 0 ||
-            (ret = mbedtls_asn1_write_mpi(p, buf, &T)) < 0) {
-            goto end_of_export;
-        }
-        len += ret;
-
-        /* Export Q */
-        if ((ret = mbedtls_rsa_export(rsa, NULL, NULL,
-                                      &T, NULL, NULL)) != 0 ||
-            (ret = mbedtls_asn1_write_mpi(p, buf, &T)) < 0) {
-            goto end_of_export;
-        }
-        len += ret;
-
-        /* Export P */
-        if ((ret = mbedtls_rsa_export(rsa, NULL, &T,
-                                      NULL, NULL, NULL)) != 0 ||
-            (ret = mbedtls_asn1_write_mpi(p, buf, &T)) < 0) {
-            goto end_of_export;
-        }
-        len += ret;
-
-        /* Export D */
-        if ((ret = mbedtls_rsa_export(rsa, NULL, NULL,
-                                      NULL, &T, NULL)) != 0 ||
-            (ret = mbedtls_asn1_write_mpi(p, buf, &T)) < 0) {
-            goto end_of_export;
-        }
-        len += ret;
-
-        /* Export E */
-        if ((ret = mbedtls_rsa_export(rsa, NULL, NULL,
-                                      NULL, NULL, &T)) != 0 ||
-            (ret = mbedtls_asn1_write_mpi(p, buf, &T)) < 0) {
-            goto end_of_export;
-        }
-        len += ret;
-
-        /* Export N */
-        if ((ret = mbedtls_rsa_export(rsa, &T, NULL,
-                                      NULL, NULL, NULL)) != 0 ||
-            (ret = mbedtls_asn1_write_mpi(p, buf, &T)) < 0) {
-            goto end_of_export;
-        }
-        len += ret;
-
-end_of_export:
-
-        mbedtls_mpi_free(&T);
-        if (ret < 0) {
-            return ret;
-        }
-
-        MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_int(p, buf, 0));
-        MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, buf, len));
-        MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p,
-                                                         buf, MBEDTLS_ASN1_CONSTRUCTED |
-                                                         MBEDTLS_ASN1_SEQUENCE));
+        return (int) len;
     }
-
-    return (int) len;
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+    return mbedtls_rsa_write_key(mbedtls_pk_rsa(*pk), buf, p);
 }
 #endif /* MBEDTLS_RSA_C */
 
@@ -329,7 +202,7 @@
         mbedtls_ecp_keypair *ec = mbedtls_pk_ec_rw(*pk);
         byte_length = (ec->grp.pbits + 7) / 8;
 
-        ret = mbedtls_ecp_write_key(ec, tmp, byte_length);
+        ret = mbedtls_ecp_write_key_ext(ec, &byte_length, tmp, sizeof(tmp));
         if (ret != 0) {
             goto exit;
         }
@@ -543,7 +416,7 @@
 
 #if defined(MBEDTLS_RSA_C)
     if (mbedtls_pk_get_type(key) == MBEDTLS_PK_RSA) {
-        MBEDTLS_ASN1_CHK_ADD(len, pk_write_rsa_pubkey(p, start, key));
+        MBEDTLS_ASN1_CHK_ADD(len, mbedtls_rsa_write_pubkey(mbedtls_pk_rsa(*key), start, p));
     } else
 #endif
 #if defined(MBEDTLS_PK_HAVE_ECC_KEYS)
diff --git a/library/platform_util.c b/library/platform_util.c
index 9f5dcb8..0741bf5 100644
--- a/library/platform_util.c
+++ b/library/platform_util.c
@@ -93,8 +93,6 @@
 
 void mbedtls_platform_zeroize(void *buf, size_t len)
 {
-    MBEDTLS_INTERNAL_VALIDATE(len == 0 || buf != NULL);
-
     if (len > 0) {
 #if defined(MBEDTLS_PLATFORM_HAS_EXPLICIT_BZERO)
         explicit_bzero(buf, len);
@@ -151,10 +149,10 @@
 #include <time.h>
 #if !defined(_WIN32) && (defined(unix) || \
     defined(__unix) || defined(__unix__) || (defined(__APPLE__) && \
-    defined(__MACH__)))
+    defined(__MACH__)) || defined__midipix__)
 #include <unistd.h>
 #endif /* !_WIN32 && (unix || __unix || __unix__ ||
-        * (__APPLE__ && __MACH__)) */
+        * (__APPLE__ && __MACH__) || __midipix__) */
 
 #if !((defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L) ||     \
     (defined(_POSIX_THREAD_SAFE_FUNCTIONS) &&                     \
@@ -222,9 +220,10 @@
 #include <time.h>
 #if !defined(_WIN32) && \
     (defined(unix) || defined(__unix) || defined(__unix__) || \
-    (defined(__APPLE__) && defined(__MACH__)) || defined(__HAIKU__))
+    (defined(__APPLE__) && defined(__MACH__)) || defined(__HAIKU__) || defined(__midipix__))
 #include <unistd.h>
-#endif /* !_WIN32 && (unix || __unix || __unix__ || (__APPLE__ && __MACH__) || __HAIKU__) */
+#endif \
+    /* !_WIN32 && (unix || __unix || __unix__ || (__APPLE__ && __MACH__) || __HAIKU__ || __midipix__) */
 #if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 199309L) || defined(__HAIKU__)
 mbedtls_ms_time_t mbedtls_ms_time(void)
 {
@@ -232,7 +231,7 @@
     struct timespec tv;
     mbedtls_ms_time_t current_ms;
 
-#if defined(__linux__) && defined(CLOCK_BOOTTIME)
+#if defined(__linux__) && defined(CLOCK_BOOTTIME) || defined(__midipix__)
     ret = clock_gettime(CLOCK_BOOTTIME, &tv);
 #else
     ret = clock_gettime(CLOCK_MONOTONIC, &tv);
diff --git a/library/psa_crypto.c b/library/psa_crypto.c
index 7473aef..089751a 100644
--- a/library/psa_crypto.c
+++ b/library/psa_crypto.c
@@ -101,11 +101,6 @@
 
 static psa_global_data_t global_data;
 
-#if !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
-mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state =
-    &global_data.rng.drbg;
-#endif
-
 #define GUARD_MODULE_INITIALIZED        \
     if (global_data.initialized == 0)  \
     return PSA_ERROR_BAD_STATE;
@@ -260,12 +255,30 @@
     defined(PSA_WANT_KEY_TYPE_DH_KEY_PAIR_GENERATE)
 static int psa_is_dh_key_size_valid(size_t bits)
 {
-    if (bits != 2048 && bits != 3072 && bits != 4096 &&
-        bits != 6144 && bits != 8192) {
-        return 0;
+    switch (bits) {
+#if defined(PSA_WANT_DH_RFC7919_2048)
+        case 2048:
+            return 1;
+#endif /* PSA_WANT_DH_RFC7919_2048 */
+#if defined(PSA_WANT_DH_RFC7919_3072)
+        case 3072:
+            return 1;
+#endif /* PSA_WANT_DH_RFC7919_3072 */
+#if defined(PSA_WANT_DH_RFC7919_4096)
+        case 4096:
+            return 1;
+#endif /* PSA_WANT_DH_RFC7919_4096 */
+#if defined(PSA_WANT_DH_RFC7919_6144)
+        case 6144:
+            return 1;
+#endif /* PSA_WANT_DH_RFC7919_6144 */
+#if defined(PSA_WANT_DH_RFC7919_8192)
+        case 8192:
+            return 1;
+#endif /* PSA_WANT_DH_RFC7919_8192 */
+        default:
+            return 0;
     }
-
-    return 1;
 }
 #endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_IMPORT ||
           MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY ||
@@ -681,7 +694,7 @@
     size_t *key_buffer_length, size_t *bits)
 {
     psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
-    psa_key_type_t type = attributes->core.type;
+    psa_key_type_t type = attributes->type;
 
     /* zero-length keys are never supported. */
     if (data_length == 0) {
@@ -691,7 +704,7 @@
     if (key_type_is_raw_bytes(type)) {
         *bits = PSA_BYTES_TO_BITS(data_length);
 
-        status = psa_validate_unstructured_key_bit_size(attributes->core.type,
+        status = psa_validate_unstructured_key_bit_size(attributes->type,
                                                         *bits);
         if (status != PSA_SUCCESS) {
             return status;
@@ -708,7 +721,7 @@
         defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY)
         if (PSA_KEY_TYPE_IS_DH(type)) {
             if (psa_is_dh_key_size_valid(PSA_BYTES_TO_BITS(data_length)) == 0) {
-                return PSA_ERROR_INVALID_ARGUMENT;
+                return PSA_ERROR_NOT_SUPPORTED;
             }
             return mbedtls_psa_ffdh_import_key(attributes,
                                                data, data_length,
@@ -1022,8 +1035,13 @@
  * into a key slot if not already done.
  *
  * 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.
+ * It is the responsibility of the caller to then unregister
+ * once they have finished reading the contents of the slot.
+ * The caller unregisters by calling psa_unregister_read() or
+ * psa_unregister_read_under_mutex(). psa_unregister_read() must be called
+ * if and only if the caller already holds the global key slot mutex
+ * (when mutexes are enabled). psa_unregister_read_under_mutex() encapsulates
+ * the unregister with mutex lock and unlock operations.
  */
 static psa_status_t psa_get_and_lock_key_slot_with_policy(
     mbedtls_svc_key_id_t key,
@@ -1067,7 +1085,7 @@
 
 error:
     *p_slot = NULL;
-    psa_unregister_read(slot);
+    psa_unregister_read_under_mutex(slot);
 
     return status;
 }
@@ -1083,8 +1101,13 @@
  * for a cryptographic operation.
  *
  * 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.
+ * It is the responsibility of the caller to then unregister
+ * once they have finished reading the contents of the slot.
+ * The caller unregisters by calling psa_unregister_read() or
+ * psa_unregister_read_under_mutex(). psa_unregister_read() must be called
+ * if and only if the caller already holds the global key slot mutex
+ * (when mutexes are enabled). psa_unregister_read_under_mutex() encapsulates
+ * psa_unregister_read() with mutex lock and unlock operations.
  */
 static psa_status_t psa_get_and_lock_transparent_key_slot_with_policy(
     mbedtls_svc_key_id_t key,
@@ -1099,7 +1122,7 @@
     }
 
     if (psa_key_lifetime_is_external((*p_slot)->attr.lifetime)) {
-        psa_unregister_read(*p_slot);
+        psa_unregister_read_under_mutex(*p_slot);
         *p_slot = NULL;
         return PSA_ERROR_NOT_SUPPORTED;
     }
@@ -1202,6 +1225,25 @@
         return status;
     }
 
+#if defined(MBEDTLS_THREADING_C)
+    /* We cannot unlock between setting the state to PENDING_DELETION
+     * and destroying the key in storage, as otherwise another thread
+     * could load the key into a new slot and the key will not be
+     * fully destroyed. */
+    PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(
+                                    &mbedtls_threading_key_slot_mutex));
+
+    if (slot->state == PSA_SLOT_PENDING_DELETION) {
+        /* Another thread has destroyed the key between us locking the slot
+         * and us gaining the mutex. Unregister from the slot,
+         * and report that the key does not exist. */
+        status = psa_unregister_read(slot);
+
+        PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                                  &mbedtls_threading_key_slot_mutex));
+        return (status == PSA_SUCCESS) ? PSA_ERROR_INVALID_HANDLE : status;
+    }
+#endif
     /* Set the key slot containing the key description's state to
      * PENDING_DELETION. This stops new operations from registering
      * to read the slot. Current readers can safely continue to access
@@ -1210,7 +1252,12 @@
      * If the key is persistent, we can now delete the copy of the key
      * from memory. If the key is opaque, we require the driver to
      * deal with the deletion. */
-    slot->state = PSA_SLOT_PENDING_DELETION;
+    overall_status = psa_key_slot_state_transition(slot, PSA_SLOT_FULL,
+                                                   PSA_SLOT_PENDING_DELETION);
+
+    if (overall_status != PSA_SUCCESS) {
+        goto exit;
+    }
 
     if (PSA_KEY_LIFETIME_IS_READ_ONLY(slot->attr.lifetime)) {
         /* Refuse the destruction of a read-only key (which may or may not work
@@ -1265,11 +1312,6 @@
         if (overall_status == PSA_SUCCESS) {
             overall_status = status;
         }
-
-        /* TODO: other slots may have a copy of the same key. We should
-         * invalidate them.
-         * https://github.com/ARMmbed/mbed-crypto/issues/214
-         */
     }
 #endif /* defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) */
 
@@ -1295,61 +1337,23 @@
     if (status != PSA_SUCCESS) {
         overall_status = status;
     }
+
+#if defined(MBEDTLS_THREADING_C)
+    /* Don't overwrite existing errors if the unlock fails. */
+    status = overall_status;
+    PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+
     return overall_status;
 }
 
-#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT) || \
-    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY)
-static psa_status_t psa_get_rsa_public_exponent(
-    const mbedtls_rsa_context *rsa,
-    psa_key_attributes_t *attributes)
-{
-    mbedtls_mpi mpi;
-    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    uint8_t *buffer = NULL;
-    size_t buflen;
-    mbedtls_mpi_init(&mpi);
-
-    ret = mbedtls_rsa_export(rsa, NULL, NULL, NULL, NULL, &mpi);
-    if (ret != 0) {
-        goto exit;
-    }
-    if (mbedtls_mpi_cmp_int(&mpi, 65537) == 0) {
-        /* It's the default value, which is reported as an empty string,
-         * so there's nothing to do. */
-        goto exit;
-    }
-
-    buflen = mbedtls_mpi_size(&mpi);
-    buffer = mbedtls_calloc(1, buflen);
-    if (buffer == NULL) {
-        ret = MBEDTLS_ERR_MPI_ALLOC_FAILED;
-        goto exit;
-    }
-    ret = mbedtls_mpi_write_binary(&mpi, buffer, buflen);
-    if (ret != 0) {
-        goto exit;
-    }
-    attributes->domain_parameters = buffer;
-    attributes->domain_parameters_size = buflen;
-
-exit:
-    mbedtls_mpi_free(&mpi);
-    if (ret != 0) {
-        mbedtls_free(buffer);
-    }
-    return mbedtls_to_psa_error(ret);
-}
-#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT) ||
-        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */
-
 /** Retrieve all the publicly-accessible attributes of a key.
  */
 psa_status_t psa_get_key_attributes(mbedtls_svc_key_id_t key,
                                     psa_key_attributes_t *attributes)
 {
     psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
-    psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_key_slot_t *slot;
 
     psa_reset_key_attributes(attributes);
@@ -1359,9 +1363,7 @@
         return status;
     }
 
-    attributes->core = slot->attr;
-    attributes->core.flags &= (MBEDTLS_PSA_KA_MASK_EXTERNAL_ONLY |
-                               MBEDTLS_PSA_KA_MASK_DUAL_USE);
+    *attributes = slot->attr;
 
 #if defined(MBEDTLS_PSA_CRYPTO_SE_C)
     if (psa_get_se_driver_entry(slot->attr.lifetime) != NULL) {
@@ -1370,55 +1372,7 @@
     }
 #endif /* MBEDTLS_PSA_CRYPTO_SE_C */
 
-    switch (slot->attr.type) {
-#if (defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_IMPORT) && \
-    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT)) || \
-    defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY)
-        case PSA_KEY_TYPE_RSA_KEY_PAIR:
-        case PSA_KEY_TYPE_RSA_PUBLIC_KEY:
-            /* TODO: This is a temporary situation where domain parameters are deprecated,
-             * but we need it for namely generating an RSA key with a non-default exponent.
-             * This would be improved after https://github.com/Mbed-TLS/mbedtls/issues/6494.
-             */
-            if (!psa_key_lifetime_is_external(slot->attr.lifetime)) {
-                mbedtls_rsa_context *rsa = NULL;
-
-                status = mbedtls_psa_rsa_load_representation(
-                    slot->attr.type,
-                    slot->key.data,
-                    slot->key.bytes,
-                    &rsa);
-                if (status != PSA_SUCCESS) {
-                    break;
-                }
-
-                status = psa_get_rsa_public_exponent(rsa,
-                                                     attributes);
-                mbedtls_rsa_free(rsa);
-                mbedtls_free(rsa);
-            }
-            break;
-#else
-        case PSA_KEY_TYPE_RSA_KEY_PAIR:
-        case PSA_KEY_TYPE_RSA_PUBLIC_KEY:
-            attributes->domain_parameters = NULL;
-            attributes->domain_parameters_size = SIZE_MAX;
-            break;
-#endif /* (defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_IMPORT) && \
-        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT)) ||
-        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */
-        default:
-            /* Nothing else to do. */
-            break;
-    }
-
-    if (status != PSA_SUCCESS) {
-        psa_reset_key_attributes(attributes);
-    }
-
-    unlock_status = psa_unregister_read(slot);
-
-    return (status == PSA_SUCCESS) ? unlock_status : status;
+    return psa_unregister_read_under_mutex(slot);
 }
 
 #if defined(MBEDTLS_PSA_CRYPTO_SE_C)
@@ -1426,7 +1380,7 @@
     const psa_key_attributes_t *attributes,
     psa_key_slot_number_t *slot_number)
 {
-    if (attributes->core.flags & MBEDTLS_PSA_KA_FLAG_HAS_SLOT_NUMBER) {
+    if (attributes->has_slot_number) {
         *slot_number = attributes->slot_number;
         return PSA_SUCCESS;
     } else {
@@ -1456,7 +1410,7 @@
     const uint8_t *key_buffer, size_t key_buffer_size,
     uint8_t *data, size_t data_size, size_t *data_length)
 {
-    psa_key_type_t type = attributes->core.type;
+    psa_key_type_t type = attributes->type;
 
     if (key_type_is_raw_bytes(type) ||
         PSA_KEY_TYPE_IS_RSA(type)   ||
@@ -1508,17 +1462,14 @@
 
     LOCAL_OUTPUT_ALLOC(data_external, data_size, data);
 
-    psa_key_attributes_t attributes = {
-        .core = slot->attr
-    };
-    status = psa_driver_wrapper_export_key(&attributes,
+    status = psa_driver_wrapper_export_key(&slot->attr,
                                            slot->key.data, slot->key.bytes,
                                            data, data_size, data_length);
 
 #if defined(MBEDTLS_PSA_COPY_CALLER_BUFFERS)
 exit:
 #endif
-    unlock_status = psa_unregister_read(slot);
+    unlock_status = psa_unregister_read_under_mutex(slot);
 
     LOCAL_OUTPUT_FREE(data_external, data);
     return (status == PSA_SUCCESS) ? unlock_status : status;
@@ -1532,7 +1483,7 @@
     size_t data_size,
     size_t *data_length)
 {
-    psa_key_type_t type = attributes->core.type;
+    psa_key_type_t type = attributes->type;
 
     if (PSA_KEY_TYPE_IS_PUBLIC_KEY(type) &&
         (PSA_KEY_TYPE_IS_RSA(type) || PSA_KEY_TYPE_IS_ECC(type) ||
@@ -1599,7 +1550,7 @@
     psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_key_slot_t *slot;
-    psa_key_attributes_t attributes;
+
     LOCAL_OUTPUT_DECLARE(data_external, data);
 
     /* Reject a zero-length output buffer now, since this can never be a
@@ -1628,30 +1579,17 @@
         goto exit;
     }
 
-    attributes = (psa_key_attributes_t) {
-        .core = slot->attr
-    };
     status = psa_driver_wrapper_export_public_key(
-        &attributes, slot->key.data, slot->key.bytes,
+        &slot->attr, slot->key.data, slot->key.bytes,
         data, data_size, data_length);
 
 exit:
-    unlock_status = psa_unregister_read(slot);
+    unlock_status = psa_unregister_read_under_mutex(slot);
 
     LOCAL_OUTPUT_FREE(data_external, data);
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
 
-MBEDTLS_STATIC_ASSERT(
-    (MBEDTLS_PSA_KA_MASK_EXTERNAL_ONLY & MBEDTLS_PSA_KA_MASK_DUAL_USE) == 0,
-    "One or more key attribute flag is listed as both external-only and dual-use")
-MBEDTLS_STATIC_ASSERT(
-    (PSA_KA_MASK_INTERNAL_ONLY & MBEDTLS_PSA_KA_MASK_DUAL_USE) == 0,
-    "One or more key attribute flag is listed as both internal-only and dual-use")
-MBEDTLS_STATIC_ASSERT(
-    (PSA_KA_MASK_INTERNAL_ONLY & MBEDTLS_PSA_KA_MASK_EXTERNAL_ONLY) == 0,
-    "One or more key attribute flag is listed as both internal-only and external-only")
-
 /** Validate that a key policy is internally well-formed.
  *
  * This function only rejects invalid policies. It does not validate the
@@ -1717,7 +1655,7 @@
         }
     }
 
-    status = psa_validate_key_policy(&attributes->core.policy);
+    status = psa_validate_key_policy(&attributes->policy);
     if (status != PSA_SUCCESS) {
         return status;
     }
@@ -1730,12 +1668,6 @@
         return PSA_ERROR_NOT_SUPPORTED;
     }
 
-    /* Reject invalid flags. These should not be reachable through the API. */
-    if (attributes->core.flags & ~(MBEDTLS_PSA_KA_MASK_EXTERNAL_ONLY |
-                                   MBEDTLS_PSA_KA_MASK_DUAL_USE)) {
-        return PSA_ERROR_INVALID_ARGUMENT;
-    }
-
     return PSA_SUCCESS;
 }
 
@@ -1787,7 +1719,15 @@
         return status;
     }
 
+#if defined(MBEDTLS_THREADING_C)
+    PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
     status = psa_reserve_free_key_slot(&volatile_key_id, p_slot);
+#if defined(MBEDTLS_THREADING_C)
+    PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
     if (status != PSA_SUCCESS) {
         return status;
     }
@@ -1801,7 +1741,7 @@
      * volatile key identifier associated to the slot returned to contain its
      * definition. */
 
-    slot->attr = attributes->core;
+    slot->attr = *attributes;
     if (PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
 #if !defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER)
         slot->attr.id = volatile_key_id;
@@ -1810,13 +1750,6 @@
 #endif
     }
 
-    /* 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_reserve_free_key_slot()
-     * may have set. */
-    slot->attr.flags &= ~MBEDTLS_PSA_KA_MASK_EXTERNAL_ONLY;
-
 #if defined(MBEDTLS_PSA_CRYPTO_SE_C)
     /* For a key in a secure element, we need to do three things
      * when creating or registering a persistent key:
@@ -1843,7 +1776,7 @@
             return status;
         }
 
-        if (!PSA_KEY_LIFETIME_IS_VOLATILE(attributes->core.lifetime)) {
+        if (!PSA_KEY_LIFETIME_IS_VOLATILE(attributes->lifetime)) {
             psa_crypto_prepare_transaction(PSA_CRYPTO_TRANSACTION_CREATE_KEY);
             psa_crypto_transaction.key.lifetime = slot->attr.lifetime;
             psa_crypto_transaction.key.slot = slot_number;
@@ -1907,6 +1840,11 @@
     (void) slot;
     (void) driver;
 
+#if defined(MBEDTLS_THREADING_C)
+    PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+
 #if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C)
     if (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
 #if defined(MBEDTLS_PSA_CRYPTO_SE_C)
@@ -1946,6 +1884,11 @@
         status = psa_save_se_persistent_data(driver);
         if (status != PSA_SUCCESS) {
             psa_destroy_persistent_key(slot->attr.id);
+
+#if defined(MBEDTLS_THREADING_C)
+            PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                                      &mbedtls_threading_key_slot_mutex));
+#endif
             return status;
         }
         status = psa_crypto_stop_transaction();
@@ -1961,6 +1904,10 @@
         }
     }
 
+#if defined(MBEDTLS_THREADING_C)
+    PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
     return status;
 }
 
@@ -1985,6 +1932,13 @@
         return;
     }
 
+#if defined(MBEDTLS_THREADING_C)
+    /* If the lock operation fails we still wipe the slot.
+     * Operations will no longer work after a failed lock,
+     * but we still need to wipe the slot of confidential data. */
+    mbedtls_mutex_lock(&mbedtls_threading_key_slot_mutex);
+#endif
+
 #if defined(MBEDTLS_PSA_CRYPTO_SE_C)
     /* TODO: If the key has already been created in the secure
      * element, and the failure happened later (when saving metadata
@@ -2003,6 +1957,10 @@
 #endif /* MBEDTLS_PSA_CRYPTO_SE_C */
 
     psa_wipe_key_slot(slot);
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_unlock(&mbedtls_threading_key_slot_mutex);
+#endif
 }
 
 /** Validate optional attributes during key creation.
@@ -2018,65 +1976,14 @@
     const psa_key_slot_t *slot,
     const psa_key_attributes_t *attributes)
 {
-    if (attributes->core.type != 0) {
-        if (attributes->core.type != slot->attr.type) {
+    if (attributes->type != 0) {
+        if (attributes->type != slot->attr.type) {
             return PSA_ERROR_INVALID_ARGUMENT;
         }
     }
 
-    if (attributes->domain_parameters_size != 0) {
-#if (defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_IMPORT) && \
-        defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT)) || \
-        defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY)
-        if (PSA_KEY_TYPE_IS_RSA(slot->attr.type)) {
-            mbedtls_rsa_context *rsa = NULL;
-            mbedtls_mpi actual, required;
-            int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-
-            psa_status_t status = mbedtls_psa_rsa_load_representation(
-                slot->attr.type,
-                slot->key.data,
-                slot->key.bytes,
-                &rsa);
-            if (status != PSA_SUCCESS) {
-                return status;
-            }
-
-            mbedtls_mpi_init(&actual);
-            mbedtls_mpi_init(&required);
-            ret = mbedtls_rsa_export(rsa,
-                                     NULL, NULL, NULL, NULL, &actual);
-            mbedtls_rsa_free(rsa);
-            mbedtls_free(rsa);
-            if (ret != 0) {
-                goto rsa_exit;
-            }
-            ret = mbedtls_mpi_read_binary(&required,
-                                          attributes->domain_parameters,
-                                          attributes->domain_parameters_size);
-            if (ret != 0) {
-                goto rsa_exit;
-            }
-            if (mbedtls_mpi_cmp_mpi(&actual, &required) != 0) {
-                ret = MBEDTLS_ERR_RSA_BAD_INPUT_DATA;
-            }
-rsa_exit:
-            mbedtls_mpi_free(&actual);
-            mbedtls_mpi_free(&required);
-            if (ret != 0) {
-                return mbedtls_to_psa_error(ret);
-            }
-        } else
-#endif /* (defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_IMPORT) &&
-        *  defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT)) ||
-        * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */
-        {
-            return PSA_ERROR_INVALID_ARGUMENT;
-        }
-    }
-
-    if (attributes->core.bits != 0) {
-        if (attributes->core.bits != slot->attr.bits) {
+    if (attributes->bits != 0) {
+        if (attributes->bits != slot->attr.bits) {
             return PSA_ERROR_INVALID_ARGUMENT;
         }
     }
@@ -2123,7 +2030,7 @@
      * with storage ( MBEDTLS_PSA_CRYPTO_SE_C ) ),we have to allocate a
      * buffer to hold the imported key material. */
     if (slot->key.data == NULL) {
-        if (psa_key_lifetime_is_external(attributes->core.lifetime)) {
+        if (psa_key_lifetime_is_external(attributes->lifetime)) {
             status = psa_driver_wrapper_get_key_buffer_size_from_key_data(
                 attributes, data, data_length, &storage_size);
             if (status != PSA_SUCCESS) {
@@ -2244,12 +2151,12 @@
      * equal to the ones of the source key. So it is safe to inherit
      * them from the source key now."
      * */
-    actual_attributes.core.bits = source_slot->attr.bits;
-    actual_attributes.core.type = source_slot->attr.type;
+    actual_attributes.bits = source_slot->attr.bits;
+    actual_attributes.type = source_slot->attr.type;
 
 
     status = psa_restrict_key_policy(source_slot->attr.type,
-                                     &actual_attributes.core.policy,
+                                     &actual_attributes.policy,
                                      &source_slot->attr.policy);
     if (status != PSA_SUCCESS) {
         goto exit;
@@ -2278,7 +2185,7 @@
      * - For opaque keys this translates to an invocation of the drivers'
      *   copy_key entry point through the dispatch layer.
      * */
-    if (psa_key_lifetime_is_external(actual_attributes.core.lifetime)) {
+    if (psa_key_lifetime_is_external(actual_attributes.lifetime)) {
         status = psa_driver_wrapper_get_key_buffer_size(&actual_attributes,
                                                         &storage_size);
         if (status != PSA_SUCCESS) {
@@ -2313,7 +2220,7 @@
         psa_fail_key_creation(target_slot, driver);
     }
 
-    unlock_status = psa_unregister_read(source_slot);
+    unlock_status = psa_unregister_read_under_mutex(source_slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -2639,7 +2546,6 @@
     psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_key_slot_t *slot = NULL;
-    psa_key_attributes_t attributes;
 
     /* A context must be freshly initialized before it can be set up. */
     if (operation->id != 0) {
@@ -2656,11 +2562,7 @@
         goto exit;
     }
 
-    attributes = (psa_key_attributes_t) {
-        .core = slot->attr
-    };
-
-    status = psa_mac_finalize_alg_and_key_validation(alg, &attributes,
+    status = psa_mac_finalize_alg_and_key_validation(alg, &slot->attr,
                                                      &operation->mac_size);
     if (status != PSA_SUCCESS) {
         goto exit;
@@ -2670,13 +2572,13 @@
     /* Dispatch the MAC setup call with validated input */
     if (is_sign) {
         status = psa_driver_wrapper_mac_sign_setup(operation,
-                                                   &attributes,
+                                                   &slot->attr,
                                                    slot->key.data,
                                                    slot->key.bytes,
                                                    alg);
     } else {
         status = psa_driver_wrapper_mac_verify_setup(operation,
-                                                     &attributes,
+                                                     &slot->attr,
                                                      slot->key.data,
                                                      slot->key.bytes,
                                                      alg);
@@ -2687,7 +2589,7 @@
         psa_mac_abort(operation);
     }
 
-    unlock_status = psa_unregister_read(slot);
+    unlock_status = psa_unregister_read_under_mutex(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -2846,7 +2748,6 @@
     psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_key_slot_t *slot;
     uint8_t operation_mac_size = 0;
-    psa_key_attributes_t attributes;
 
     status = psa_get_and_lock_key_slot_with_policy(
         key,
@@ -2857,11 +2758,7 @@
         goto exit;
     }
 
-    attributes = (psa_key_attributes_t) {
-        .core = slot->attr
-    };
-
-    status = psa_mac_finalize_alg_and_key_validation(alg, &attributes,
+    status = psa_mac_finalize_alg_and_key_validation(alg, &slot->attr,
                                                      &operation_mac_size);
     if (status != PSA_SUCCESS) {
         goto exit;
@@ -2873,7 +2770,7 @@
     }
 
     status = psa_driver_wrapper_mac_compute(
-        &attributes,
+        &slot->attr,
         slot->key.data, slot->key.bytes,
         alg,
         input, input_length,
@@ -2893,7 +2790,7 @@
 
     psa_wipe_tag_output_buffer(mac, status, mac_size, *mac_length);
 
-    unlock_status = psa_unregister_read(slot);
+    unlock_status = psa_unregister_read_under_mutex(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -3004,7 +2901,6 @@
     psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_key_slot_t *slot;
-    psa_key_attributes_t attributes;
 
     *signature_length = 0;
 
@@ -3036,19 +2932,15 @@
         goto exit;
     }
 
-    attributes = (psa_key_attributes_t) {
-        .core = slot->attr
-    };
-
     if (input_is_message) {
         status = psa_driver_wrapper_sign_message(
-            &attributes, slot->key.data, slot->key.bytes,
+            &slot->attr, slot->key.data, slot->key.bytes,
             alg, input, input_length,
             signature, signature_size, signature_length);
     } else {
 
         status = psa_driver_wrapper_sign_hash(
-            &attributes, slot->key.data, slot->key.bytes,
+            &slot->attr, slot->key.data, slot->key.bytes,
             alg, input, input_length,
             signature, signature_size, signature_length);
     }
@@ -3058,7 +2950,7 @@
     psa_wipe_tag_output_buffer(signature, status, signature_size,
                                *signature_length);
 
-    unlock_status = psa_unregister_read(slot);
+    unlock_status = psa_unregister_read_under_mutex(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -3090,23 +2982,19 @@
         return status;
     }
 
-    psa_key_attributes_t attributes = {
-        .core = slot->attr
-    };
-
     if (input_is_message) {
         status = psa_driver_wrapper_verify_message(
-            &attributes, slot->key.data, slot->key.bytes,
+            &slot->attr, slot->key.data, slot->key.bytes,
             alg, input, input_length,
             signature, signature_length);
     } else {
         status = psa_driver_wrapper_verify_hash(
-            &attributes, slot->key.data, slot->key.bytes,
+            &slot->attr, slot->key.data, slot->key.bytes,
             alg, input, input_length,
             signature, signature_length);
     }
 
-    unlock_status = psa_unregister_read(slot);
+    unlock_status = psa_unregister_read_under_mutex(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 
@@ -3237,7 +3125,7 @@
     psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
     uint8_t *signature, size_t signature_size, size_t *signature_length)
 {
-    if (attributes->core.type == PSA_KEY_TYPE_RSA_KEY_PAIR) {
+    if (attributes->type == PSA_KEY_TYPE_RSA_KEY_PAIR) {
         if (PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) ||
             PSA_ALG_IS_RSA_PSS(alg)) {
 #if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) || \
@@ -3252,7 +3140,7 @@
         } else {
             return PSA_ERROR_INVALID_ARGUMENT;
         }
-    } else if (PSA_KEY_TYPE_IS_ECC(attributes->core.type)) {
+    } else if (PSA_KEY_TYPE_IS_ECC(attributes->type)) {
         if (PSA_ALG_IS_ECDSA(alg)) {
 #if defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
             defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)
@@ -3311,7 +3199,7 @@
     psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
     const uint8_t *signature, size_t signature_length)
 {
-    if (PSA_KEY_TYPE_IS_RSA(attributes->core.type)) {
+    if (PSA_KEY_TYPE_IS_RSA(attributes->type)) {
         if (PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) ||
             PSA_ALG_IS_RSA_PSS(alg)) {
 #if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) || \
@@ -3326,7 +3214,7 @@
         } else {
             return PSA_ERROR_INVALID_ARGUMENT;
         }
-    } else if (PSA_KEY_TYPE_IS_ECC(attributes->core.type)) {
+    } else if (PSA_KEY_TYPE_IS_ECC(attributes->type)) {
         if (PSA_ALG_IS_ECDSA(alg)) {
 #if defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
             defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)
@@ -3390,7 +3278,7 @@
     psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_key_slot_t *slot;
-    psa_key_attributes_t attributes;
+
     LOCAL_INPUT_DECLARE(input_external, input);
     LOCAL_INPUT_DECLARE(salt_external, salt);
     LOCAL_OUTPUT_DECLARE(output_external, output);
@@ -3418,19 +3306,16 @@
         goto exit;
     }
 
-    attributes = (psa_key_attributes_t) {
-        .core = slot->attr
-    };
-
     LOCAL_INPUT_ALLOC(input_external, input_length, input);
     LOCAL_INPUT_ALLOC(salt_external, salt_length, salt);
     LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
     status = psa_driver_wrapper_asymmetric_encrypt(
-        &attributes, slot->key.data, slot->key.bytes,
+        &slot->attr, slot->key.data, slot->key.bytes,
         alg, input, input_length, salt, salt_length,
         output, output_size, output_length);
 exit:
-    unlock_status = psa_unregister_read(slot);
+    unlock_status = psa_unregister_read_under_mutex(slot);
 
     LOCAL_INPUT_FREE(input_external, input);
     LOCAL_INPUT_FREE(salt_external, salt);
@@ -3452,7 +3337,6 @@
     psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_key_slot_t *slot;
-    psa_key_attributes_t attributes;
 
     LOCAL_INPUT_DECLARE(input_external, input);
     LOCAL_INPUT_DECLARE(salt_external, salt);
@@ -3480,20 +3364,17 @@
         goto exit;
     }
 
-    attributes = (psa_key_attributes_t) {
-        .core = slot->attr
-    };
-
     LOCAL_INPUT_ALLOC(input_external, input_length, input);
     LOCAL_INPUT_ALLOC(salt_external, salt_length, salt);
     LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
     status = psa_driver_wrapper_asymmetric_decrypt(
-        &attributes, slot->key.data, slot->key.bytes,
+        &slot->attr, slot->key.data, slot->key.bytes,
         alg, input, input_length, salt, salt_length,
         output, output_size, output_length);
 
 exit:
-    unlock_status = psa_unregister_read(slot);
+    unlock_status = psa_unregister_read_under_mutex(slot);
 
     LOCAL_INPUT_FREE(input_external, input);
     LOCAL_INPUT_FREE(salt_external, salt);
@@ -3561,7 +3442,6 @@
     psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_key_slot_t *slot;
-    psa_key_attributes_t attributes;
 
     LOCAL_INPUT_DECLARE(hash_external, hash);
 
@@ -3592,14 +3472,10 @@
 
     LOCAL_INPUT_ALLOC(hash_external, hash_length, hash);
 
-    attributes = (psa_key_attributes_t) {
-        .core = slot->attr
-    };
-
     /* Ensure ops count gets reset, in case of operation re-use. */
     operation->num_ops = 0;
 
-    status = psa_driver_wrapper_sign_hash_start(operation, &attributes,
+    status = psa_driver_wrapper_sign_hash_start(operation, &slot->attr,
                                                 slot->key.data,
                                                 slot->key.bytes, alg,
                                                 hash, hash_length);
@@ -3610,7 +3486,7 @@
         psa_sign_hash_abort_internal(operation);
     }
 
-    unlock_status = psa_unregister_read(slot);
+    unlock_status = psa_unregister_read_under_mutex(slot);
 
     if (unlock_status != PSA_SUCCESS) {
         operation->error_occurred = 1;
@@ -3753,14 +3629,10 @@
     LOCAL_INPUT_ALLOC(hash_external, hash_length, hash);
     LOCAL_INPUT_ALLOC(signature_external, signature_length, signature);
 
-    psa_key_attributes_t attributes = {
-        .core = slot->attr
-    };
-
     /* Ensure ops count gets reset, in case of operation re-use. */
     operation->num_ops = 0;
 
-    status = psa_driver_wrapper_verify_hash_start(operation, &attributes,
+    status = psa_driver_wrapper_verify_hash_start(operation, &slot->attr,
                                                   slot->key.data,
                                                   slot->key.bytes,
                                                   alg, hash, hash_length,
@@ -3774,7 +3646,7 @@
         psa_verify_hash_abort_internal(operation);
     }
 
-    unlock_status = psa_unregister_read(slot);
+    unlock_status = psa_unregister_read_under_mutex(slot);
 
     if (unlock_status != PSA_SUCCESS) {
         operation->error_occurred = 1;
@@ -3901,7 +3773,7 @@
     psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
     size_t required_hash_length;
 
-    if (!PSA_KEY_TYPE_IS_ECC(attributes->core.type)) {
+    if (!PSA_KEY_TYPE_IS_ECC(attributes->type)) {
         return PSA_ERROR_NOT_SUPPORTED;
     }
 
@@ -3918,8 +3790,8 @@
     /* Ensure num_ops is zero'ed in case of context re-use. */
     operation->num_ops = 0;
 
-    status = mbedtls_psa_ecp_load_representation(attributes->core.type,
-                                                 attributes->core.bits,
+    status = mbedtls_psa_ecp_load_representation(attributes->type,
+                                                 attributes->bits,
                                                  key_buffer,
                                                  key_buffer_size,
                                                  &operation->ctx);
@@ -4117,7 +3989,7 @@
     size_t coordinate_bytes = 0;
     size_t required_hash_length = 0;
 
-    if (!PSA_KEY_TYPE_IS_ECC(attributes->core.type)) {
+    if (!PSA_KEY_TYPE_IS_ECC(attributes->type)) {
         return PSA_ERROR_NOT_SUPPORTED;
     }
 
@@ -4136,8 +4008,8 @@
     /* Ensure num_ops is zero'ed in case of context re-use. */
     operation->num_ops = 0;
 
-    status = mbedtls_psa_ecp_load_representation(attributes->core.type,
-                                                 attributes->core.bits,
+    status = mbedtls_psa_ecp_load_representation(attributes->type,
+                                                 attributes->bits,
                                                  key_buffer,
                                                  key_buffer_size,
                                                  &operation->ctx);
@@ -4285,8 +4157,6 @@
 {
     GUARD_MODULE_INITIALIZED;
 
-    psa_status_t status;
-
 #if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
 
     size_t output_length = 0;
@@ -4307,24 +4177,24 @@
 #else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
 
     while (output_size > 0) {
+        int ret = MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED;
         size_t request_size =
             (output_size > MBEDTLS_PSA_RANDOM_MAX_REQUEST ?
              MBEDTLS_PSA_RANDOM_MAX_REQUEST :
              output_size);
-        int ret = mbedtls_psa_get_random(MBEDTLS_PSA_RANDOM_STATE,
-                                         output, request_size);
+#if defined(MBEDTLS_CTR_DRBG_C)
+        ret = mbedtls_ctr_drbg_random(&global_data.rng.drbg, output, request_size);
+#elif defined(MBEDTLS_HMAC_DRBG_C)
+        ret = mbedtls_hmac_drbg_random(&global_data.rng.drbg, output, request_size);
+#endif /* !MBEDTLS_CTR_DRBG_C && !MBEDTLS_HMAC_DRBG_C */
         if (ret != 0) {
-            status = mbedtls_to_psa_error(ret);
-            goto exit;
+            return mbedtls_to_psa_error(ret);
         }
         output_size -= request_size;
         output += request_size;
     }
-    status = PSA_SUCCESS;
+    return PSA_SUCCESS;
 #endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
-
-exit:
-    return status;
 }
 
 
@@ -4343,7 +4213,6 @@
     psa_key_usage_t usage = (cipher_operation == MBEDTLS_ENCRYPT ?
                              PSA_KEY_USAGE_ENCRYPT :
                              PSA_KEY_USAGE_DECRYPT);
-    psa_key_attributes_t attributes;
 
     /* A context must be freshly initialized before it can be set up. */
     if (operation->id != 0) {
@@ -4373,20 +4242,16 @@
     }
     operation->default_iv_length = PSA_CIPHER_IV_LENGTH(slot->attr.type, alg);
 
-    attributes = (psa_key_attributes_t) {
-        .core = slot->attr
-    };
-
     /* Try doing the operation through a driver before using software fallback. */
     if (cipher_operation == MBEDTLS_ENCRYPT) {
         status = psa_driver_wrapper_cipher_encrypt_setup(operation,
-                                                         &attributes,
+                                                         &slot->attr,
                                                          slot->key.data,
                                                          slot->key.bytes,
                                                          alg);
     } else {
         status = psa_driver_wrapper_cipher_decrypt_setup(operation,
-                                                         &attributes,
+                                                         &slot->attr,
                                                          slot->key.data,
                                                          slot->key.bytes,
                                                          alg);
@@ -4397,7 +4262,7 @@
         psa_cipher_abort(operation);
     }
 
-    unlock_status = psa_unregister_read(slot);
+    unlock_status = psa_unregister_read_under_mutex(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -4627,7 +4492,6 @@
     psa_key_slot_t *slot = NULL;
     uint8_t local_iv[PSA_CIPHER_IV_MAX_SIZE];
     size_t default_iv_length = 0;
-    psa_key_attributes_t attributes;
 
     LOCAL_INPUT_DECLARE(input_external, input);
     LOCAL_OUTPUT_DECLARE(output_external, output);
@@ -4644,10 +4508,6 @@
         goto exit;
     }
 
-    attributes = (psa_key_attributes_t) {
-        .core = slot->attr
-    };
-
     default_iv_length = PSA_CIPHER_IV_LENGTH(slot->attr.type, alg);
     if (default_iv_length > PSA_CIPHER_IV_MAX_SIZE) {
         status = PSA_ERROR_GENERIC_ERROR;
@@ -4670,13 +4530,13 @@
     LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
 
     status = psa_driver_wrapper_cipher_encrypt(
-        &attributes, slot->key.data, slot->key.bytes,
+        &slot->attr, slot->key.data, slot->key.bytes,
         alg, local_iv, default_iv_length, input, input_length,
         psa_crypto_buffer_offset(output, default_iv_length),
         output_size - default_iv_length, output_length);
 
 exit:
-    unlock_status = psa_unregister_read(slot);
+    unlock_status = psa_unregister_read_under_mutex(slot);
     if (status == PSA_SUCCESS) {
         status = unlock_status;
     }
@@ -4707,7 +4567,6 @@
     psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_key_slot_t *slot = NULL;
-    psa_key_attributes_t attributes;
 
     LOCAL_INPUT_DECLARE(input_external, input);
     LOCAL_OUTPUT_DECLARE(output_external, output);
@@ -4724,10 +4583,6 @@
         goto exit;
     }
 
-    attributes = (psa_key_attributes_t) {
-        .core = slot->attr
-    };
-
     if (alg == PSA_ALG_CCM_STAR_NO_TAG &&
         input_length < PSA_BLOCK_CIPHER_BLOCK_LENGTH(slot->attr.type)) {
         status = PSA_ERROR_INVALID_ARGUMENT;
@@ -4741,12 +4596,12 @@
     LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
 
     status = psa_driver_wrapper_cipher_decrypt(
-        &attributes, slot->key.data, slot->key.bytes,
+        &slot->attr, slot->key.data, slot->key.bytes,
         alg, input, input_length,
         output, output_size, output_length);
 
 exit:
-    unlock_status = psa_unregister_read(slot);
+    unlock_status = psa_unregister_read_under_mutex(slot);
     if (status == PSA_SUCCESS) {
         status = unlock_status;
     }
@@ -4858,10 +4713,6 @@
         return status;
     }
 
-    psa_key_attributes_t attributes = {
-        .core = slot->attr
-    };
-
     LOCAL_INPUT_ALLOC(nonce_external, nonce_length, nonce);
     LOCAL_INPUT_ALLOC(additional_data_external, additional_data_length, additional_data);
     LOCAL_INPUT_ALLOC(plaintext_external, plaintext_length, plaintext);
@@ -4873,7 +4724,7 @@
     }
 
     status = psa_driver_wrapper_aead_encrypt(
-        &attributes, slot->key.data, slot->key.bytes,
+        &slot->attr, slot->key.data, slot->key.bytes,
         alg,
         nonce, nonce_length,
         additional_data, additional_data_length,
@@ -4890,7 +4741,7 @@
     LOCAL_INPUT_FREE(plaintext_external, plaintext);
     LOCAL_OUTPUT_FREE(ciphertext_external, ciphertext);
 
-    psa_unregister_read(slot);
+    psa_unregister_read_under_mutex(slot);
 
     return status;
 }
@@ -4928,10 +4779,6 @@
         return status;
     }
 
-    psa_key_attributes_t attributes = {
-        .core = slot->attr
-    };
-
     LOCAL_INPUT_ALLOC(nonce_external, nonce_length, nonce);
     LOCAL_INPUT_ALLOC(additional_data_external, additional_data_length,
                       additional_data);
@@ -4944,7 +4791,7 @@
     }
 
     status = psa_driver_wrapper_aead_decrypt(
-        &attributes, slot->key.data, slot->key.bytes,
+        &slot->attr, slot->key.data, slot->key.bytes,
         alg,
         nonce, nonce_length,
         additional_data, additional_data_length,
@@ -4961,7 +4808,7 @@
     LOCAL_INPUT_FREE(ciphertext_external, ciphertext);
     LOCAL_OUTPUT_FREE(plaintext_external, plaintext);
 
-    psa_unregister_read(slot);
+    psa_unregister_read_under_mutex(slot);
 
     return status;
 }
@@ -5015,7 +4862,6 @@
     psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_key_slot_t *slot = NULL;
     psa_key_usage_t key_usage = 0;
-    psa_key_attributes_t attributes;
 
     status = psa_aead_check_algorithm(alg);
     if (status != PSA_SUCCESS) {
@@ -5045,23 +4891,19 @@
         goto exit;
     }
 
-    attributes = (psa_key_attributes_t) {
-        .core = slot->attr
-    };
-
     if ((status = psa_validate_tag_length(alg)) != PSA_SUCCESS) {
         goto exit;
     }
 
     if (is_encrypt) {
         status = psa_driver_wrapper_aead_encrypt_setup(operation,
-                                                       &attributes,
+                                                       &slot->attr,
                                                        slot->key.data,
                                                        slot->key.bytes,
                                                        alg);
     } else {
         status = psa_driver_wrapper_aead_decrypt_setup(operation,
-                                                       &attributes,
+                                                       &slot->attr,
                                                        slot->key.data,
                                                        slot->key.bytes,
                                                        alg);
@@ -5070,10 +4912,10 @@
         goto exit;
     }
 
-    operation->key_type = psa_get_key_type(&attributes);
+    operation->key_type = psa_get_key_type(&slot->attr);
 
 exit:
-    unlock_status = psa_unregister_read(slot);
+    unlock_status = psa_unregister_read_under_mutex(slot);
 
     if (status == PSA_SUCCESS) {
         status = unlock_status;
@@ -6445,7 +6287,6 @@
     size_t bytes = PSA_BITS_TO_BYTES(bits);
     size_t storage_size = bytes;
     psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
-    psa_key_attributes_t attributes;
 
     if (PSA_KEY_TYPE_IS_PUBLIC_KEY(slot->attr.type)) {
         return PSA_ERROR_INVALID_ARGUMENT;
@@ -6494,12 +6335,9 @@
     }
 
     slot->attr.bits = (psa_key_bits_t) bits;
-    attributes = (psa_key_attributes_t) {
-        .core = slot->attr
-    };
 
-    if (psa_key_lifetime_is_external(attributes.core.lifetime)) {
-        status = psa_driver_wrapper_get_key_buffer_size(&attributes,
+    if (psa_key_lifetime_is_external(slot->attr.lifetime)) {
+        status = psa_driver_wrapper_get_key_buffer_size(&slot->attr,
                                                         &storage_size);
         if (status != PSA_SUCCESS) {
             goto exit;
@@ -6510,7 +6348,7 @@
         goto exit;
     }
 
-    status = psa_driver_wrapper_import_key(&attributes,
+    status = psa_driver_wrapper_import_key(&slot->attr,
                                            data, bytes,
                                            slot->key.data,
                                            slot->key.bytes,
@@ -6524,9 +6362,28 @@
     return status;
 }
 
-psa_status_t psa_key_derivation_output_key(const psa_key_attributes_t *attributes,
-                                           psa_key_derivation_operation_t *operation,
-                                           mbedtls_svc_key_id_t *key)
+static const psa_key_production_parameters_t default_production_parameters =
+    PSA_KEY_PRODUCTION_PARAMETERS_INIT;
+
+int psa_key_production_parameters_are_default(
+    const psa_key_production_parameters_t *params,
+    size_t params_data_length)
+{
+    if (params->flags != 0) {
+        return 0;
+    }
+    if (params_data_length != 0) {
+        return 0;
+    }
+    return 1;
+}
+
+psa_status_t psa_key_derivation_output_key_ext(
+    const psa_key_attributes_t *attributes,
+    psa_key_derivation_operation_t *operation,
+    const psa_key_production_parameters_t *params,
+    size_t params_data_length,
+    mbedtls_svc_key_id_t *key)
 {
     psa_status_t status;
     psa_key_slot_t *slot = NULL;
@@ -6540,6 +6397,10 @@
         return PSA_ERROR_INVALID_ARGUMENT;
     }
 
+    if (!psa_key_production_parameters_are_default(params, params_data_length)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
     if (operation->alg == PSA_ALG_NONE) {
         return PSA_ERROR_BAD_STATE;
     }
@@ -6558,7 +6419,7 @@
 #endif /* MBEDTLS_PSA_CRYPTO_SE_C */
     if (status == PSA_SUCCESS) {
         status = psa_generate_derived_key_internal(slot,
-                                                   attributes->core.bits,
+                                                   attributes->bits,
                                                    operation);
     }
     if (status == PSA_SUCCESS) {
@@ -6571,6 +6432,15 @@
     return status;
 }
 
+psa_status_t psa_key_derivation_output_key(
+    const psa_key_attributes_t *attributes,
+    psa_key_derivation_operation_t *operation,
+    mbedtls_svc_key_id_t *key)
+{
+    return psa_key_derivation_output_key_ext(attributes, operation,
+                                             &default_production_parameters, 0,
+                                             key);
+}
 
 
 /****************************************************************/
@@ -6631,6 +6501,91 @@
     return status;
 }
 
+static psa_status_t psa_key_derivation_set_maximum_capacity(
+    psa_key_derivation_operation_t *operation,
+    psa_algorithm_t kdf_alg)
+{
+#if defined(PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS)
+    if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) {
+        operation->capacity = PSA_HASH_LENGTH(PSA_ALG_SHA_256);
+        return PSA_SUCCESS;
+    }
+#endif
+#if defined(PSA_WANT_ALG_PBKDF2_AES_CMAC_PRF_128)
+    if (kdf_alg == PSA_ALG_PBKDF2_AES_CMAC_PRF_128) {
+#if (SIZE_MAX > UINT32_MAX)
+        operation->capacity = UINT32_MAX * (size_t) PSA_MAC_LENGTH(
+            PSA_KEY_TYPE_AES,
+            128U,
+            PSA_ALG_CMAC);
+#else
+        operation->capacity = SIZE_MAX;
+#endif
+        return PSA_SUCCESS;
+    }
+#endif /* PSA_WANT_ALG_PBKDF2_AES_CMAC_PRF_128 */
+
+    /* After this point, if kdf_alg is not valid then value of hash_alg may be
+     * invalid or meaningless but it does not affect this function */
+    psa_algorithm_t hash_alg = PSA_ALG_GET_HASH(kdf_alg);
+    size_t hash_size = PSA_HASH_LENGTH(hash_alg);
+    if (hash_size == 0) {
+        return PSA_ERROR_NOT_SUPPORTED;
+    }
+
+    /* Make sure that hash_alg is a supported hash algorithm. Otherwise
+     * we might fail later, which is somewhat unfriendly and potentially
+     * risk-prone. */
+    psa_status_t status = psa_hash_try_support(hash_alg);
+    if (status != PSA_SUCCESS) {
+        return status;
+    }
+
+#if defined(PSA_WANT_ALG_HKDF)
+    if (PSA_ALG_IS_HKDF(kdf_alg)) {
+        operation->capacity = 255 * hash_size;
+    } else
+#endif
+#if defined(PSA_WANT_ALG_HKDF_EXTRACT)
+    if (PSA_ALG_IS_HKDF_EXTRACT(kdf_alg)) {
+        operation->capacity = hash_size;
+    } else
+#endif
+#if defined(PSA_WANT_ALG_HKDF_EXPAND)
+    if (PSA_ALG_IS_HKDF_EXPAND(kdf_alg)) {
+        operation->capacity = 255 * hash_size;
+    } else
+#endif
+#if defined(PSA_WANT_ALG_TLS12_PRF)
+    if (PSA_ALG_IS_TLS12_PRF(kdf_alg) &&
+        (hash_alg == PSA_ALG_SHA_256 || hash_alg == PSA_ALG_SHA_384)) {
+        operation->capacity = SIZE_MAX;
+    } else
+#endif
+#if defined(PSA_WANT_ALG_TLS12_PSK_TO_MS)
+    if (PSA_ALG_IS_TLS12_PSK_TO_MS(kdf_alg) &&
+        (hash_alg == PSA_ALG_SHA_256 || hash_alg == PSA_ALG_SHA_384)) {
+        /* Master Secret is always 48 bytes
+         * https://datatracker.ietf.org/doc/html/rfc5246.html#section-8.1 */
+        operation->capacity = 48U;
+    } else
+#endif
+#if defined(PSA_WANT_ALG_PBKDF2_HMAC)
+    if (PSA_ALG_IS_PBKDF2_HMAC(kdf_alg)) {
+#if (SIZE_MAX > UINT32_MAX)
+        operation->capacity = UINT32_MAX * hash_size;
+#else
+        operation->capacity = SIZE_MAX;
+#endif
+    } else
+#endif /* PSA_WANT_ALG_PBKDF2_HMAC */
+    {
+        (void) hash_size;
+        status = PSA_ERROR_NOT_SUPPORTED;
+    }
+    return status;
+}
+
 static psa_status_t psa_key_derivation_setup_kdf(
     psa_key_derivation_operation_t *operation,
     psa_algorithm_t kdf_alg)
@@ -6644,43 +6599,9 @@
         return PSA_ERROR_NOT_SUPPORTED;
     }
 
-    /* All currently supported key derivation algorithms (apart from
-     * ecjpake to pms and pbkdf2_aes_cmac_128) are based on a hash algorithm. */
-    psa_algorithm_t hash_alg = PSA_ALG_HKDF_GET_HASH(kdf_alg);
-    size_t hash_size = PSA_HASH_LENGTH(hash_alg);
-    if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) {
-        hash_size = PSA_HASH_LENGTH(PSA_ALG_SHA_256);
-    } else if (kdf_alg == PSA_ALG_PBKDF2_AES_CMAC_PRF_128) {
-        hash_size = PSA_MAC_LENGTH(PSA_KEY_TYPE_AES, 128U, PSA_ALG_CMAC);
-    } else {
-        if (hash_size == 0) {
-            return PSA_ERROR_NOT_SUPPORTED;
-        }
-
-        /* Make sure that hash_alg is a supported hash algorithm. Otherwise
-         * we might fail later, which is somewhat unfriendly and potentially
-         * risk-prone. */
-        psa_status_t status = psa_hash_try_support(hash_alg);
-        if (status != PSA_SUCCESS) {
-            return status;
-        }
-    }
-
-    if ((PSA_ALG_IS_TLS12_PRF(kdf_alg) ||
-         PSA_ALG_IS_TLS12_PSK_TO_MS(kdf_alg)) &&
-        !(hash_alg == PSA_ALG_SHA_256 || hash_alg == PSA_ALG_SHA_384)) {
-        return PSA_ERROR_NOT_SUPPORTED;
-    }
-#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) || \
-    defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS)
-    if (PSA_ALG_IS_HKDF_EXTRACT(kdf_alg) ||
-        (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS)) {
-        operation->capacity = hash_size;
-    } else
-#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT ||
-          MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS */
-    operation->capacity = 255 * hash_size;
-    return PSA_SUCCESS;
+    psa_status_t status = psa_key_derivation_set_maximum_capacity(operation,
+                                                                  kdf_alg);
+    return status;
 }
 
 static psa_status_t psa_key_agreement_try_support(psa_algorithm_t alg)
@@ -7478,7 +7399,7 @@
                                                slot->key.data,
                                                slot->key.bytes);
 
-    unlock_status = psa_unregister_read(slot);
+    unlock_status = psa_unregister_read_under_mutex(slot);
 
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
@@ -7553,11 +7474,7 @@
         return PSA_ERROR_NOT_SUPPORTED;
     }
 
-    psa_key_attributes_t attributes = {
-        .core = private_key->attr
-    };
-
-    return psa_driver_wrapper_key_agreement(&attributes,
+    return psa_driver_wrapper_key_agreement(&private_key->attr,
                                             private_key->key.data,
                                             private_key->key.bytes, alg,
                                             peer_key, peer_key_length,
@@ -7576,7 +7493,7 @@
                                                size_t peer_key_length)
 {
     psa_status_t status;
-    uint8_t shared_secret[PSA_RAW_KEY_AGREEMENT_OUTPUT_MAX_SIZE];
+    uint8_t shared_secret[PSA_RAW_KEY_AGREEMENT_OUTPUT_MAX_SIZE] = { 0 };
     size_t shared_secret_length = 0;
     psa_algorithm_t ka_alg = PSA_ALG_KEY_AGREEMENT_GET_BASE(operation->alg);
 
@@ -7642,8 +7559,9 @@
         }
     }
 
-    unlock_status = psa_unregister_read(slot);
+    unlock_status = psa_unregister_read_under_mutex(slot);
     LOCAL_INPUT_FREE(peer_key_external, peer_key);
+
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
 
@@ -7714,7 +7632,7 @@
         *output_length = 0;
     }
 
-    unlock_status = psa_unregister_read(slot);
+    unlock_status = psa_unregister_read_under_mutex(slot);
 
     LOCAL_INPUT_FREE(peer_key_external, peer_key);
     LOCAL_OUTPUT_FREE(output_external, output);
@@ -7758,7 +7676,7 @@
                                MBEDTLS_ENTROPY_SOURCE_STRONG);
 #endif
 
-    mbedtls_psa_drbg_init(MBEDTLS_PSA_RANDOM_STATE);
+    mbedtls_psa_drbg_init(&rng->drbg);
 #endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
 }
 
@@ -7769,7 +7687,7 @@
 #if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
     memset(rng, 0, sizeof(*rng));
 #else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
-    mbedtls_psa_drbg_free(MBEDTLS_PSA_RANDOM_STATE);
+    mbedtls_psa_drbg_free(&rng->drbg);
     rng->entropy_free(&rng->entropy);
 #endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
 }
@@ -7784,7 +7702,7 @@
     return PSA_SUCCESS;
 #else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
     const unsigned char drbg_seed[] = "PSA";
-    int ret = mbedtls_psa_drbg_seed(&rng->entropy,
+    int ret = mbedtls_psa_drbg_seed(&rng->drbg, &rng->entropy,
                                     drbg_seed, sizeof(drbg_seed) - 1);
     return mbedtls_to_psa_error(ret);
 #endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
@@ -7807,39 +7725,6 @@
     return status;
 }
 
-/* Wrapper function allowing the classic API to use the PSA RNG.
- *
- * `mbedtls_psa_get_random(MBEDTLS_PSA_RANDOM_STATE, ...)` calls
- * `psa_generate_random(...)`. The state parameter is ignored since the
- * PSA API doesn't support passing an explicit state.
- *
- * In the non-external case, psa_generate_random() calls an
- * `mbedtls_xxx_drbg_random` function which has exactly the same signature
- * and semantics as mbedtls_psa_get_random(). As an optimization,
- * instead of doing this back-and-forth between the PSA API and the
- * classic API, psa_crypto_random_impl.h defines `mbedtls_psa_get_random`
- * as a constant function pointer to `mbedtls_xxx_drbg_random`.
- */
-#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
-int mbedtls_psa_get_random(void *p_rng,
-                           unsigned char *output,
-                           size_t output_size)
-{
-    /* This function takes a pointer to the RNG state because that's what
-     * classic mbedtls functions using an RNG expect. The PSA RNG manages
-     * its own state internally and doesn't let the caller access that state.
-     * So we just ignore the state parameter, and in practice we'll pass
-     * NULL. */
-    (void) p_rng;
-    psa_status_t status = psa_generate_random(output, output_size);
-    if (status == PSA_SUCCESS) {
-        return 0;
-    } else {
-        return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
-    }
-}
-#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
-
 #if defined(MBEDTLS_PSA_INJECT_ENTROPY)
 psa_status_t mbedtls_psa_inject_entropy(const uint8_t *seed,
                                         size_t seed_size)
@@ -7864,7 +7749,7 @@
  * \param  bits  The number of bits of the key
  *
  * \retval #PSA_SUCCESS
- *         The key type and size are valid.
+ *        The key type and size are valid.
  * \retval #PSA_ERROR_INVALID_ARGUMENT
  *         The size in bits of the key is not valid.
  * \retval #PSA_ERROR_NOT_SUPPORTED
@@ -7922,18 +7807,18 @@
 
 psa_status_t psa_generate_key_internal(
     const psa_key_attributes_t *attributes,
+    const psa_key_production_parameters_t *params, size_t params_data_length,
     uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length)
 {
     psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
-    psa_key_type_t type = attributes->core.type;
+    psa_key_type_t type = attributes->type;
 
-    if ((attributes->domain_parameters == NULL) &&
-        (attributes->domain_parameters_size != 0)) {
-        return PSA_ERROR_INVALID_ARGUMENT;
-    }
+    /* Only used for RSA */
+    (void) params;
+    (void) params_data_length;
 
     if (key_type_is_raw_bytes(type)) {
-        status = psa_generate_random(key_buffer, key_buffer_size);
+        status = psa_generate_random_internal(key_buffer, key_buffer_size);
         if (status != PSA_SUCCESS) {
             return status;
         }
@@ -7948,6 +7833,7 @@
 #if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_GENERATE)
     if (type == PSA_KEY_TYPE_RSA_KEY_PAIR) {
         return mbedtls_psa_rsa_generate_key(attributes,
+                                            params, params_data_length,
                                             key_buffer,
                                             key_buffer_size,
                                             key_buffer_length);
@@ -7979,8 +7865,10 @@
     return PSA_SUCCESS;
 }
 
-psa_status_t psa_generate_key(const psa_key_attributes_t *attributes,
-                              mbedtls_svc_key_id_t *key)
+psa_status_t psa_generate_key_ext(const psa_key_attributes_t *attributes,
+                                  const psa_key_production_parameters_t *params,
+                                  size_t params_data_length,
+                                  mbedtls_svc_key_id_t *key)
 {
     psa_status_t status;
     psa_key_slot_t *slot = NULL;
@@ -7996,7 +7884,18 @@
     }
 
     /* Reject any attempt to create a public key. */
-    if (PSA_KEY_TYPE_IS_PUBLIC_KEY(attributes->core.type)) {
+    if (PSA_KEY_TYPE_IS_PUBLIC_KEY(attributes->type)) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+#if defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_GENERATE)
+    if (attributes->type == PSA_KEY_TYPE_RSA_KEY_PAIR) {
+        if (params->flags != 0) {
+            return PSA_ERROR_INVALID_ARGUMENT;
+        }
+    } else
+#endif
+    if (!psa_key_production_parameters_are_default(params, params_data_length)) {
         return PSA_ERROR_INVALID_ARGUMENT;
     }
 
@@ -8011,17 +7910,17 @@
      * with storage ( MBEDTLS_PSA_CRYPTO_SE_C ) ),we have to allocate a
      * buffer to hold the generated key material. */
     if (slot->key.data == NULL) {
-        if (PSA_KEY_LIFETIME_GET_LOCATION(attributes->core.lifetime) ==
+        if (PSA_KEY_LIFETIME_GET_LOCATION(attributes->lifetime) ==
             PSA_KEY_LOCATION_LOCAL_STORAGE) {
             status = psa_validate_key_type_and_size_for_key_generation(
-                attributes->core.type, attributes->core.bits);
+                attributes->type, attributes->bits);
             if (status != PSA_SUCCESS) {
                 goto exit;
             }
 
             key_buffer_size = PSA_EXPORT_KEY_OUTPUT_SIZE(
-                attributes->core.type,
-                attributes->core.bits);
+                attributes->type,
+                attributes->bits);
         } else {
             status = psa_driver_wrapper_get_key_buffer_size(
                 attributes, &key_buffer_size);
@@ -8037,8 +7936,9 @@
     }
 
     status = psa_driver_wrapper_generate_key(attributes,
-                                             slot->key.data, slot->key.bytes, &slot->key.bytes);
-
+                                             params, params_data_length,
+                                             slot->key.data, slot->key.bytes,
+                                             &slot->key.bytes);
     if (status != PSA_SUCCESS) {
         psa_remove_key_data_from_memory(slot);
     }
@@ -8054,6 +7954,14 @@
     return status;
 }
 
+psa_status_t psa_generate_key(const psa_key_attributes_t *attributes,
+                              mbedtls_svc_key_id_t *key)
+{
+    return psa_generate_key_ext(attributes,
+                                &default_production_parameters, 0,
+                                key);
+}
+
 /****************************************************************/
 /* Module setup */
 /****************************************************************/
@@ -8326,7 +8234,6 @@
     psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_key_slot_t *slot = NULL;
-    psa_key_attributes_t attributes;
     psa_key_type_t type;
 
     if (operation->stage != PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) {
@@ -8341,11 +8248,7 @@
         goto exit;
     }
 
-    attributes = (psa_key_attributes_t) {
-        .core = slot->attr
-    };
-
-    type = psa_get_key_type(&attributes);
+    type = psa_get_key_type(&slot->attr);
 
     if (type != PSA_KEY_TYPE_PASSWORD &&
         type != PSA_KEY_TYPE_PASSWORD_HASH) {
@@ -8361,12 +8264,13 @@
 
     memcpy(operation->data.inputs.password, slot->key.data, slot->key.bytes);
     operation->data.inputs.password_len = slot->key.bytes;
-    operation->data.inputs.attributes = attributes;
+    operation->data.inputs.attributes = slot->attr;
+
 exit:
     if (status != PSA_SUCCESS) {
         psa_pake_abort(operation);
     }
-    unlock_status = psa_unregister_read(slot);
+    unlock_status = psa_unregister_read_under_mutex(slot);
     return (status == PSA_SUCCESS) ? unlock_status : status;
 }
 
diff --git a/library/psa_crypto_aead.c b/library/psa_crypto_aead.c
index 49aa961..a201985 100644
--- a/library/psa_crypto_aead.c
+++ b/library/psa_crypto_aead.c
@@ -33,10 +33,10 @@
     psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
     mbedtls_cipher_id_t cipher_id;
     mbedtls_cipher_mode_t mode;
-    size_t key_bits = attributes->core.bits;
+    size_t key_bits = attributes->bits;
     (void) key_buffer_size;
 
-    status = mbedtls_cipher_values_from_psa(alg, attributes->core.type,
+    status = mbedtls_cipher_values_from_psa(alg, attributes->type,
                                             &key_bits, &mode, &cipher_id);
     if (status != PSA_SUCCESS) {
         return status;
@@ -49,7 +49,7 @@
             /* CCM allows the following tag lengths: 4, 6, 8, 10, 12, 14, 16.
              * The call to mbedtls_ccm_encrypt_and_tag or
              * mbedtls_ccm_auth_decrypt will validate the tag length. */
-            if (PSA_BLOCK_CIPHER_BLOCK_LENGTH(attributes->core.type) != 16) {
+            if (PSA_BLOCK_CIPHER_BLOCK_LENGTH(attributes->type) != 16) {
                 return PSA_ERROR_INVALID_ARGUMENT;
             }
 
@@ -69,7 +69,7 @@
             /* GCM allows the following tag lengths: 4, 8, 12, 13, 14, 15, 16.
              * The call to mbedtls_gcm_crypt_and_tag or
              * mbedtls_gcm_auth_decrypt will validate the tag length. */
-            if (PSA_BLOCK_CIPHER_BLOCK_LENGTH(attributes->core.type) != 16) {
+            if (PSA_BLOCK_CIPHER_BLOCK_LENGTH(attributes->type) != 16) {
                 return PSA_ERROR_INVALID_ARGUMENT;
             }
 
diff --git a/library/psa_crypto_cipher.c b/library/psa_crypto_cipher.c
index d70a275..881d673 100644
--- a/library/psa_crypto_cipher.c
+++ b/library/psa_crypto_cipher.c
@@ -289,14 +289,14 @@
     int ret = 0;
     size_t key_bits;
     const mbedtls_cipher_info_t *cipher_info = NULL;
-    psa_key_type_t key_type = attributes->core.type;
+    psa_key_type_t key_type = attributes->type;
 
     (void) key_buffer_size;
 
     mbedtls_cipher_init(&operation->ctx.cipher);
 
     operation->alg = alg;
-    key_bits = attributes->core.bits;
+    key_bits = attributes->bits;
     cipher_info = mbedtls_cipher_info_from_psa(alg, key_type,
                                                key_bits, NULL);
     if (cipher_info == NULL) {
diff --git a/library/psa_crypto_client.c b/library/psa_crypto_client.c
index 472d3d3..72f671d 100644
--- a/library/psa_crypto_client.c
+++ b/library/psa_crypto_client.c
@@ -16,57 +16,7 @@
 
 void psa_reset_key_attributes(psa_key_attributes_t *attributes)
 {
-    mbedtls_free(attributes->domain_parameters);
     memset(attributes, 0, sizeof(*attributes));
 }
 
-psa_status_t psa_set_key_domain_parameters(psa_key_attributes_t *attributes,
-                                           psa_key_type_t type,
-                                           const uint8_t *data,
-                                           size_t data_length)
-{
-    uint8_t *copy = NULL;
-
-    if (data_length != 0) {
-        copy = mbedtls_calloc(1, data_length);
-        if (copy == NULL) {
-            return PSA_ERROR_INSUFFICIENT_MEMORY;
-        }
-        memcpy(copy, data, data_length);
-    }
-    /* After this point, this function is guaranteed to succeed, so it
-     * can start modifying `*attributes`. */
-
-    if (attributes->domain_parameters != NULL) {
-        mbedtls_free(attributes->domain_parameters);
-        attributes->domain_parameters = NULL;
-        attributes->domain_parameters_size = 0;
-    }
-
-    attributes->domain_parameters = copy;
-    attributes->domain_parameters_size = data_length;
-    attributes->core.type = type;
-    return PSA_SUCCESS;
-}
-
-psa_status_t psa_get_key_domain_parameters(
-    const psa_key_attributes_t *attributes,
-    uint8_t *data, size_t data_size, size_t *data_length)
-{
-    if (attributes->domain_parameters == NULL &&
-        attributes->domain_parameters_size == SIZE_MAX) {
-        return PSA_ERROR_NOT_SUPPORTED;
-    }
-
-    if (attributes->domain_parameters_size > data_size) {
-        return PSA_ERROR_BUFFER_TOO_SMALL;
-    }
-    *data_length = attributes->domain_parameters_size;
-    if (attributes->domain_parameters_size != 0) {
-        memcpy(data, attributes->domain_parameters,
-               attributes->domain_parameters_size);
-    }
-    return PSA_SUCCESS;
-}
-
 #endif /* MBEDTLS_PSA_CRYPTO_CLIENT */
diff --git a/library/psa_crypto_core.h b/library/psa_crypto_core.h
index 67966f9..4d47594 100644
--- a/library/psa_crypto_core.h
+++ b/library/psa_crypto_core.h
@@ -20,6 +20,9 @@
 
 #include "psa/crypto.h"
 #include "psa/crypto_se_driver.h"
+#if defined(MBEDTLS_THREADING_C)
+#include "mbedtls/threading.h"
+#endif
 
 /**
  * Tell if PSA is ready for this hash.
@@ -56,7 +59,7 @@
  * and metadata for one key.
  */
 typedef struct {
-    psa_core_key_attributes_t attr;
+    psa_key_attributes_t attr;
 
     /*
      * The current state of the key slot, as described in
@@ -86,7 +89,9 @@
      * 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.
+     * reading the current contents of the slot. If the key slot mutex is not
+     * held (when mutexes are enabled), this call must be done via a call to
+     * psa_unregister_read_under_mutex(slot).
      * A function must call psa_key_slot_has_readers(slot) to check if
      * the slot is in use for reading.
      *
@@ -111,10 +116,48 @@
     } key;
 } psa_key_slot_t;
 
-/* A mask of key attribute flags used only internally.
- * Currently there aren't any. */
-#define PSA_KA_MASK_INTERNAL_ONLY (     \
-        0)
+#if defined(MBEDTLS_THREADING_C)
+
+/** Perform a mutex operation and return immediately upon failure.
+ *
+ * Returns PSA_ERROR_SERVICE_FAILURE if the operation fails
+ * and status was PSA_SUCCESS.
+ *
+ * Assumptions:
+ *  psa_status_t status exists.
+ *  f is a mutex operation which returns 0 upon success.
+ */
+#define PSA_THREADING_CHK_RET(f)                       \
+    do                                                 \
+    {                                                  \
+        if ((f) != 0) {                                \
+            if (status == PSA_SUCCESS) {               \
+                return PSA_ERROR_SERVICE_FAILURE;      \
+            }                                          \
+            return status;                             \
+        }                                              \
+    } while (0);
+
+/** Perform a mutex operation and goto exit on failure.
+ *
+ * Sets status to PSA_ERROR_SERVICE_FAILURE if status was PSA_SUCCESS.
+ *
+ * Assumptions:
+ *  psa_status_t status exists.
+ *  Label exit: exists.
+ *  f is a mutex operation which returns 0 upon success.
+ */
+#define PSA_THREADING_CHK_GOTO_EXIT(f)                 \
+    do                                                 \
+    {                                                  \
+        if ((f) != 0) {                                \
+            if (status == PSA_SUCCESS) {               \
+                status = PSA_ERROR_SERVICE_FAILURE;    \
+            }                                          \
+            goto exit;                                 \
+        }                                              \
+    } while (0);
+#endif
 
 /** Test whether a key slot has any registered readers.
  * If multi-threading is enabled, the caller must hold the
@@ -129,56 +172,6 @@
     return slot->registered_readers > 0;
 }
 
-/** Retrieve flags from psa_key_slot_t::attr::core::flags.
- *
- * \param[in] slot      The key slot to query.
- * \param mask          The mask of bits to extract.
- *
- * \return The key attribute flags in the given slot,
- *         bitwise-anded with \p mask.
- */
-static inline uint16_t psa_key_slot_get_flags(const psa_key_slot_t *slot,
-                                              uint16_t mask)
-{
-    return slot->attr.flags & mask;
-}
-
-/** Set flags in psa_key_slot_t::attr::core::flags.
- *
- * \param[in,out] slot  The key slot to modify.
- * \param mask          The mask of bits to modify.
- * \param value         The new value of the selected bits.
- */
-static inline void psa_key_slot_set_flags(psa_key_slot_t *slot,
-                                          uint16_t mask,
-                                          uint16_t value)
-{
-    slot->attr.flags = ((~mask & slot->attr.flags) |
-                        (mask & value));
-}
-
-/** Turn on flags in psa_key_slot_t::attr::core::flags.
- *
- * \param[in,out] slot  The key slot to modify.
- * \param mask          The mask of bits to set.
- */
-static inline void psa_key_slot_set_bits_in_flags(psa_key_slot_t *slot,
-                                                  uint16_t mask)
-{
-    slot->attr.flags |= mask;
-}
-
-/** Turn off flags in psa_key_slot_t::attr::core::flags.
- *
- * \param[in,out] slot  The key slot to modify.
- * \param mask          The mask of bits to clear.
- */
-static inline void psa_key_slot_clear_bits(psa_key_slot_t *slot,
-                                           uint16_t mask)
-{
-    slot->attr.flags &= ~mask;
-}
-
 #if defined(MBEDTLS_PSA_CRYPTO_SE_C)
 /** Get the SE slot number of a key from the key slot storing its description.
  *
@@ -350,6 +343,18 @@
     const uint8_t *key_buffer, size_t key_buffer_size,
     uint8_t *data, size_t data_size, size_t *data_length);
 
+/** Whether a key production parameters structure is the default.
+ *
+ * Calls to a key generation driver with non-default production parameters
+ * require a driver supporting custom production parameters.
+ *
+ * \param[in] params            The key production parameters to check.
+ * \param params_data_length    Size of `params->data` in bytes.
+ */
+int psa_key_production_parameters_are_default(
+    const psa_key_production_parameters_t *params,
+    size_t params_data_length);
+
 /**
  * \brief Generate a key.
  *
@@ -357,6 +362,9 @@
  *       entry point.
  *
  * \param[in]  attributes         The attributes for the key to generate.
+ * \param[in]  params             The production parameters from
+ *                                psa_generate_key_ext().
+ * \param      params_data_length The size of `params->data` in bytes.
  * \param[out] key_buffer         Buffer where the key data is to be written.
  * \param[in]  key_buffer_size    Size of \p key_buffer in bytes.
  * \param[out] key_buffer_length  On success, the number of bytes written in
@@ -371,6 +379,8 @@
  *         The size of \p key_buffer is too small.
  */
 psa_status_t psa_generate_key_internal(const psa_key_attributes_t *attributes,
+                                       const psa_key_production_parameters_t *params,
+                                       size_t params_data_length,
                                        uint8_t *key_buffer,
                                        size_t key_buffer_size,
                                        size_t *key_buffer_length);
diff --git a/library/psa_crypto_ecp.c b/library/psa_crypto_ecp.c
index db08541..95baff6 100644
--- a/library/psa_crypto_ecp.c
+++ b/library/psa_crypto_ecp.c
@@ -216,8 +216,8 @@
     mbedtls_ecp_keypair *ecp = NULL;
 
     /* Parse input */
-    status = mbedtls_psa_ecp_load_representation(attributes->core.type,
-                                                 attributes->core.bits,
+    status = mbedtls_psa_ecp_load_representation(attributes->type,
+                                                 attributes->bits,
                                                  data,
                                                  data_length,
                                                  &ecp);
@@ -225,7 +225,7 @@
         goto exit;
     }
 
-    if (PSA_KEY_TYPE_ECC_GET_FAMILY(attributes->core.type) ==
+    if (PSA_KEY_TYPE_ECC_GET_FAMILY(attributes->type) ==
         PSA_ECC_FAMILY_MONTGOMERY) {
         *bits = ecp->grp.nbits + 1;
     } else {
@@ -235,7 +235,7 @@
     /* Re-export the data to PSA export format. There is currently no support
      * for other input formats then the export format, so this is a 1-1
      * copy operation. */
-    status = mbedtls_psa_ecp_export_key(attributes->core.type,
+    status = mbedtls_psa_ecp_export_key(attributes->type,
                                         ecp,
                                         key_buffer,
                                         key_buffer_size,
@@ -281,20 +281,8 @@
 
         return status;
     } else {
-        if (data_size < PSA_BITS_TO_BYTES(ecp->grp.nbits)) {
-            return PSA_ERROR_BUFFER_TOO_SMALL;
-        }
-
         status = mbedtls_to_psa_error(
-            mbedtls_ecp_write_key(ecp,
-                                  data,
-                                  PSA_BITS_TO_BYTES(ecp->grp.nbits)));
-        if (status == PSA_SUCCESS) {
-            *data_length = PSA_BITS_TO_BYTES(ecp->grp.nbits);
-        } else {
-            memset(data, 0, data_size);
-        }
-
+            mbedtls_ecp_write_key_ext(ecp, data_length, data, data_size));
         return status;
     }
 }
@@ -308,7 +296,7 @@
     mbedtls_ecp_keypair *ecp = NULL;
 
     status = mbedtls_psa_ecp_load_representation(
-        attributes->core.type, attributes->core.bits,
+        attributes->type, attributes->bits,
         key_buffer, key_buffer_size, &ecp);
     if (status != PSA_SUCCESS) {
         return status;
@@ -316,7 +304,7 @@
 
     status = mbedtls_psa_ecp_export_key(
         PSA_KEY_TYPE_ECC_PUBLIC_KEY(
-            PSA_KEY_TYPE_ECC_GET_FAMILY(attributes->core.type)),
+            PSA_KEY_TYPE_ECC_GET_FAMILY(attributes->type)),
         ecp, data, data_size, data_length);
 
     mbedtls_ecp_keypair_free(ecp);
@@ -337,18 +325,14 @@
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
 
     psa_ecc_family_t curve = PSA_KEY_TYPE_ECC_GET_FAMILY(
-        attributes->core.type);
+        attributes->type);
     mbedtls_ecp_group_id grp_id =
-        mbedtls_ecc_group_from_psa(curve, attributes->core.bits);
+        mbedtls_ecc_group_from_psa(curve, attributes->bits);
 
     const mbedtls_ecp_curve_info *curve_info =
         mbedtls_ecp_curve_info_from_grp_id(grp_id);
     mbedtls_ecp_keypair ecp;
 
-    if (attributes->domain_parameters_size != 0) {
-        return PSA_ERROR_NOT_SUPPORTED;
-    }
-
     if (grp_id == MBEDTLS_ECP_DP_NONE || curve_info == NULL) {
         return PSA_ERROR_NOT_SUPPORTED;
     }
@@ -363,14 +347,11 @@
     }
 
     status = mbedtls_to_psa_error(
-        mbedtls_ecp_write_key(&ecp, key_buffer, key_buffer_size));
+        mbedtls_ecp_write_key_ext(&ecp, key_buffer_length,
+                                  key_buffer, key_buffer_size));
 
     mbedtls_ecp_keypair_free(&ecp);
 
-    if (status == PSA_SUCCESS) {
-        *key_buffer_length = key_buffer_size;
-    }
-
     return status;
 }
 #endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_GENERATE */
@@ -393,8 +374,8 @@
     size_t curve_bytes;
     mbedtls_mpi r, s;
 
-    status = mbedtls_psa_ecp_load_representation(attributes->core.type,
-                                                 attributes->core.bits,
+    status = mbedtls_psa_ecp_load_representation(attributes->type,
+                                                 attributes->bits,
                                                  key_buffer,
                                                  key_buffer_size,
                                                  &ecp);
@@ -480,8 +461,8 @@
 
     (void) alg;
 
-    status = mbedtls_psa_ecp_load_representation(attributes->core.type,
-                                                 attributes->core.bits,
+    status = mbedtls_psa_ecp_load_representation(attributes->type,
+                                                 attributes->bits,
                                                  key_buffer,
                                                  key_buffer_size,
                                                  &ecp);
@@ -545,14 +526,14 @@
     size_t *shared_secret_length)
 {
     psa_status_t status;
-    if (!PSA_KEY_TYPE_IS_ECC_KEY_PAIR(attributes->core.type) ||
+    if (!PSA_KEY_TYPE_IS_ECC_KEY_PAIR(attributes->type) ||
         !PSA_ALG_IS_ECDH(alg)) {
         return PSA_ERROR_INVALID_ARGUMENT;
     }
     mbedtls_ecp_keypair *ecp = NULL;
     status = mbedtls_psa_ecp_load_representation(
-        attributes->core.type,
-        attributes->core.bits,
+        attributes->type,
+        attributes->bits,
         key_buffer,
         key_buffer_size,
         &ecp);
diff --git a/library/psa_crypto_ffdh.c b/library/psa_crypto_ffdh.c
index a57f02e..ae38f6d 100644
--- a/library/psa_crypto_ffdh.c
+++ b/library/psa_crypto_ffdh.c
@@ -10,6 +10,12 @@
 
 #if defined(MBEDTLS_PSA_CRYPTO_C)
 
+/* This header is only needed because it defines
+ * MBEDTLS_DHM_RFC7919_FFDHEXXXX_[P|G]_BIN symbols that are used in
+ * mbedtls_psa_ffdh_set_prime_generator(). Apart from that, this module
+ * only uses bignum functions for arithmetic. */
+#include <mbedtls/dhm.h>
+
 #include <psa/crypto.h>
 #include "psa_crypto_core.h"
 #include "psa_crypto_ffdh.h"
@@ -35,58 +41,78 @@
         return PSA_ERROR_INVALID_ARGUMENT;
     }
 
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_2048)
     static const unsigned char dhm_P_2048[] =
         MBEDTLS_DHM_RFC7919_FFDHE2048_P_BIN;
-    static const unsigned char dhm_P_3072[] =
-        MBEDTLS_DHM_RFC7919_FFDHE3072_P_BIN;
-    static const unsigned char dhm_P_4096[] =
-        MBEDTLS_DHM_RFC7919_FFDHE4096_P_BIN;
-    static const unsigned char dhm_P_6144[] =
-        MBEDTLS_DHM_RFC7919_FFDHE6144_P_BIN;
-    static const unsigned char dhm_P_8192[] =
-        MBEDTLS_DHM_RFC7919_FFDHE8192_P_BIN;
     static const unsigned char dhm_G_2048[] =
         MBEDTLS_DHM_RFC7919_FFDHE2048_G_BIN;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_2048 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_3072)
+    static const unsigned char dhm_P_3072[] =
+        MBEDTLS_DHM_RFC7919_FFDHE3072_P_BIN;
     static const unsigned char dhm_G_3072[] =
         MBEDTLS_DHM_RFC7919_FFDHE3072_G_BIN;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_3072 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_4096)
+    static const unsigned char dhm_P_4096[] =
+        MBEDTLS_DHM_RFC7919_FFDHE4096_P_BIN;
     static const unsigned char dhm_G_4096[] =
         MBEDTLS_DHM_RFC7919_FFDHE4096_G_BIN;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_4096 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_6144)
+    static const unsigned char dhm_P_6144[] =
+        MBEDTLS_DHM_RFC7919_FFDHE6144_P_BIN;
     static const unsigned char dhm_G_6144[] =
         MBEDTLS_DHM_RFC7919_FFDHE6144_G_BIN;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_6144 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_8192)
+    static const unsigned char dhm_P_8192[] =
+        MBEDTLS_DHM_RFC7919_FFDHE8192_P_BIN;
     static const unsigned char dhm_G_8192[] =
         MBEDTLS_DHM_RFC7919_FFDHE8192_G_BIN;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_8192 */
 
     switch (key_size) {
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_2048)
         case sizeof(dhm_P_2048):
             dhm_P = dhm_P_2048;
             dhm_G = dhm_G_2048;
             dhm_size_P = sizeof(dhm_P_2048);
             dhm_size_G = sizeof(dhm_G_2048);
             break;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_2048 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_3072)
         case sizeof(dhm_P_3072):
             dhm_P = dhm_P_3072;
             dhm_G = dhm_G_3072;
             dhm_size_P = sizeof(dhm_P_3072);
             dhm_size_G = sizeof(dhm_G_3072);
             break;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_3072 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_4096)
         case sizeof(dhm_P_4096):
             dhm_P = dhm_P_4096;
             dhm_G = dhm_G_4096;
             dhm_size_P = sizeof(dhm_P_4096);
             dhm_size_G = sizeof(dhm_G_4096);
             break;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_4096 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_6144)
         case sizeof(dhm_P_6144):
             dhm_P = dhm_P_6144;
             dhm_G = dhm_G_6144;
             dhm_size_P = sizeof(dhm_P_6144);
             dhm_size_G = sizeof(dhm_G_6144);
             break;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_6144 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_8192)
         case sizeof(dhm_P_8192):
             dhm_P = dhm_P_8192;
             dhm_G = dhm_G_8192;
             dhm_size_P = sizeof(dhm_P_8192);
             dhm_size_G = sizeof(dhm_G_8192);
             break;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_8192 */
         default:
             return PSA_ERROR_INVALID_ARGUMENT;
     }
@@ -125,7 +151,7 @@
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
     mbedtls_mpi GX, G, X, P;
-    psa_key_type_t type = attributes->core.type;
+    psa_key_type_t type = attributes->type;
 
     if (PSA_KEY_TYPE_IS_PUBLIC_KEY(type)) {
         if (key_buffer_size > data_size) {
@@ -141,7 +167,7 @@
     mbedtls_mpi_init(&GX); mbedtls_mpi_init(&G);
     mbedtls_mpi_init(&X); mbedtls_mpi_init(&P);
 
-    size_t key_len = PSA_BITS_TO_BYTES(attributes->core.bits);
+    size_t key_len = PSA_BITS_TO_BYTES(attributes->bits);
 
     status = mbedtls_psa_ffdh_set_prime_generator(key_len, &P, &G);
 
@@ -257,7 +283,7 @@
     mbedtls_mpi_init(&K);
 
     status = mbedtls_psa_ffdh_set_prime_generator(
-        PSA_BITS_TO_BYTES(attributes->core.bits), &P, &G);
+        PSA_BITS_TO_BYTES(attributes->bits), &P, &G);
 
     if (status != PSA_SUCCESS) {
         goto cleanup;
diff --git a/library/psa_crypto_ffdh.h b/library/psa_crypto_ffdh.h
index baeb928..79accd1 100644
--- a/library/psa_crypto_ffdh.h
+++ b/library/psa_crypto_ffdh.h
@@ -10,7 +10,6 @@
 #define PSA_CRYPTO_FFDH_H
 
 #include <psa/crypto.h>
-#include <mbedtls/dhm.h>
 
 /** Perform a key agreement and return the FFDH shared secret.
  *
diff --git a/library/psa_crypto_random_impl.h b/library/psa_crypto_random_impl.h
index 64b8949..533fb2e 100644
--- a/library/psa_crypto_random_impl.h
+++ b/library/psa_crypto_random_impl.h
@@ -1,14 +1,6 @@
 /** \file psa_crypto_random_impl.h
  *
  * \brief PSA crypto random generator implementation abstraction.
- *
- * The definitions here need to be consistent with the declarations
- * in include/psa_util_internal.h. This file contains some redundant
- * declarations to increase the chance that a compiler will detect
- * inconsistencies if one file is changed without updating the other,
- * but not all potential inconsistencies can be enforced, so make sure
- * to check the public declarations and contracts in
- * include/psa_util_internal.h if you modify this file.
  */
 /*
  *  Copyright The Mbed TLS Contributors
@@ -22,22 +14,12 @@
 
 #if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
 
-#include <string.h>
-#include <mbedtls/entropy.h> // only for error codes
-#include <psa/crypto.h>
-
 typedef mbedtls_psa_external_random_context_t mbedtls_psa_random_context_t;
 
-/* Trivial wrapper around psa_generate_random(). */
-int mbedtls_psa_get_random(void *p_rng,
-                           unsigned char *output,
-                           size_t output_size);
-
-/* The PSA RNG API doesn't need any externally maintained state. */
-#define MBEDTLS_PSA_RANDOM_STATE NULL
-
 #else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
 
+#include "mbedtls/entropy.h"
+
 /* Choose a DRBG based on configuration and availability */
 #if defined(MBEDTLS_PSA_HMAC_DRBG_MD_TYPE)
 
@@ -67,11 +49,37 @@
 #error "No hash algorithm available for HMAC_DBRG."
 #endif
 
-#else
+#else /* !MBEDTLS_PSA_HMAC_DRBG_MD_TYPE && !MBEDTLS_CTR_DRBG_C && !MBEDTLS_HMAC_DRBG_C*/
+
 #error "No DRBG module available for the psa_crypto module."
+
+#endif /* !MBEDTLS_PSA_HMAC_DRBG_MD_TYPE && !MBEDTLS_CTR_DRBG_C && !MBEDTLS_HMAC_DRBG_C*/
+
+#if defined(MBEDTLS_CTR_DRBG_C)
+#include "mbedtls/ctr_drbg.h"
+#elif defined(MBEDTLS_HMAC_DRBG_C)
+#include "mbedtls/hmac_drbg.h"
+#endif /* !MBEDTLS_CTR_DRBG_C && !MBEDTLS_HMAC_DRBG_C */
+
+/* The maximum number of bytes that mbedtls_psa_get_random() is expected to return. */
+#if defined(MBEDTLS_CTR_DRBG_C)
+#define MBEDTLS_PSA_RANDOM_MAX_REQUEST MBEDTLS_CTR_DRBG_MAX_REQUEST
+#elif defined(MBEDTLS_HMAC_DRBG_C)
+#define MBEDTLS_PSA_RANDOM_MAX_REQUEST MBEDTLS_HMAC_DRBG_MAX_REQUEST
 #endif
 
-#include "mbedtls/entropy.h"
+#if defined(MBEDTLS_CTR_DRBG_C)
+typedef mbedtls_ctr_drbg_context            mbedtls_psa_drbg_context_t;
+#elif defined(MBEDTLS_HMAC_DRBG_C)
+typedef mbedtls_hmac_drbg_context           mbedtls_psa_drbg_context_t;
+#endif /* !MBEDTLS_CTR_DRBG_C && !MBEDTLS_HMAC_DRBG_C */
+
+typedef struct {
+    void (* entropy_init)(mbedtls_entropy_context *ctx);
+    void (* entropy_free)(mbedtls_entropy_context *ctx);
+    mbedtls_entropy_context entropy;
+    mbedtls_psa_drbg_context_t drbg;
+} mbedtls_psa_random_context_t;
 
 /** Initialize the PSA DRBG.
  *
@@ -99,63 +107,6 @@
 #endif
 }
 
-/** The type of the PSA random generator context.
- *
- * The random generator context is composed of an entropy context and
- * a DRBG context.
- */
-typedef struct {
-    void (* entropy_init)(mbedtls_entropy_context *ctx);
-    void (* entropy_free)(mbedtls_entropy_context *ctx);
-    mbedtls_entropy_context entropy;
-    mbedtls_psa_drbg_context_t drbg;
-} mbedtls_psa_random_context_t;
-
-/* Defined in include/psa_util_internal.h so that it's visible to
- * application code. The declaration here is redundant, but included
- * as a safety net to make it more likely that a future change that
- * accidentally causes the implementation to diverge from the interface
- * will be noticed. */
-/* Do not include the declaration under MSVC because it doesn't accept it
- * ("error C2370: 'mbedtls_psa_get_random' : redefinition; different storage class").
- * Observed with Visual Studio 2013. A known bug apparently:
- * https://stackoverflow.com/questions/8146541/duplicate-external-static-declarations-not-allowed-in-visual-studio
- */
-#if !defined(_MSC_VER)
-static mbedtls_f_rng_t *const mbedtls_psa_get_random;
-#endif
-
-/** The maximum number of bytes that mbedtls_psa_get_random() is expected to
- * return.
- */
-#if defined(MBEDTLS_CTR_DRBG_C)
-#define MBEDTLS_PSA_RANDOM_MAX_REQUEST MBEDTLS_CTR_DRBG_MAX_REQUEST
-#elif defined(MBEDTLS_HMAC_DRBG_C)
-#define MBEDTLS_PSA_RANDOM_MAX_REQUEST MBEDTLS_HMAC_DRBG_MAX_REQUEST
-#endif
-
-/** A pointer to the PSA DRBG state.
- *
- * This variable is only intended to be used through the macro
- * #MBEDTLS_PSA_RANDOM_STATE.
- */
-/* psa_crypto.c sets this variable to a pointer to the DRBG state in the
- * global PSA crypto state. */
-/* The type `mbedtls_psa_drbg_context_t` is defined in
- * include/psa_util_internal.h so that `mbedtls_psa_random_state` can be
- * declared there and be visible to application code. */
-extern mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state;
-
-/** A pointer to the PSA DRBG state.
- *
- * This macro expands to an expression that is suitable as the \c p_rng
- * parameter to pass to mbedtls_psa_get_random().
- *
- * This macro exists in all configurations where the psa_crypto module is
- * enabled. Its expansion depends on the configuration.
- */
-#define MBEDTLS_PSA_RANDOM_STATE mbedtls_psa_random_state
-
 /** Seed the PSA DRBG.
  *
  * \param entropy       An entropy context to read the seed from.
@@ -167,23 +118,15 @@
  * \return              \c 0 on success.
  * \return              An Mbed TLS error code (\c MBEDTLS_ERR_xxx) on failure.
  */
-static inline int mbedtls_psa_drbg_seed(
-    mbedtls_entropy_context *entropy,
-    const unsigned char *custom, size_t len)
+static inline int mbedtls_psa_drbg_seed(mbedtls_psa_drbg_context_t *drbg_ctx,
+                                        mbedtls_entropy_context *entropy,
+                                        const unsigned char *custom, size_t len)
 {
 #if defined(MBEDTLS_CTR_DRBG_C)
-    return mbedtls_ctr_drbg_seed(MBEDTLS_PSA_RANDOM_STATE,
-                                 mbedtls_entropy_func,
-                                 entropy,
-                                 custom, len);
+    return mbedtls_ctr_drbg_seed(drbg_ctx, mbedtls_entropy_func, entropy, custom, len);
 #elif defined(MBEDTLS_HMAC_DRBG_C)
-    const mbedtls_md_info_t *md_info =
-        mbedtls_md_info_from_type(MBEDTLS_PSA_HMAC_DRBG_MD_TYPE);
-    return mbedtls_hmac_drbg_seed(MBEDTLS_PSA_RANDOM_STATE,
-                                  md_info,
-                                  mbedtls_entropy_func,
-                                  entropy,
-                                  custom, len);
+    const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_PSA_HMAC_DRBG_MD_TYPE);
+    return mbedtls_hmac_drbg_seed(drbg_ctx, md_info, mbedtls_entropy_func, entropy, custom, len);
 #endif
 }
 
diff --git a/library/psa_crypto_rsa.c b/library/psa_crypto_rsa.c
index 7b58ea2..2f613b3 100644
--- a/library/psa_crypto_rsa.c
+++ b/library/psa_crypto_rsa.c
@@ -24,8 +24,7 @@
 
 #include <mbedtls/rsa.h>
 #include <mbedtls/error.h>
-#include <mbedtls/pk.h>
-#include "pk_wrap.h"
+#include "rsa_internal.h"
 
 #if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || \
     defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) || \
@@ -62,50 +61,38 @@
     mbedtls_rsa_context **p_rsa)
 {
     psa_status_t status;
-    mbedtls_pk_context ctx;
     size_t bits;
-    mbedtls_pk_init(&ctx);
+
+    *p_rsa = mbedtls_calloc(1, sizeof(mbedtls_rsa_context));
+    if (*p_rsa == NULL) {
+        return PSA_ERROR_INSUFFICIENT_MEMORY;
+    }
+    mbedtls_rsa_init(*p_rsa);
 
     /* Parse the data. */
     if (PSA_KEY_TYPE_IS_KEY_PAIR(type)) {
-        status = mbedtls_to_psa_error(
-            mbedtls_pk_parse_key(&ctx, data, data_length, NULL, 0,
-                                 mbedtls_psa_get_random, MBEDTLS_PSA_RANDOM_STATE));
+        status = mbedtls_to_psa_error(mbedtls_rsa_parse_key(*p_rsa, data, data_length));
     } else {
-        status = mbedtls_to_psa_error(
-            mbedtls_pk_parse_public_key(&ctx, data, data_length));
+        status = mbedtls_to_psa_error(mbedtls_rsa_parse_pubkey(*p_rsa, data, data_length));
     }
     if (status != PSA_SUCCESS) {
         goto exit;
     }
 
-    /* We have something that the pkparse module recognizes. If it is a
-     * valid RSA key, store it. */
-    if (mbedtls_pk_get_type(&ctx) != MBEDTLS_PK_RSA) {
-        status = PSA_ERROR_INVALID_ARGUMENT;
-        goto exit;
-    }
-
     /* The size of an RSA key doesn't have to be a multiple of 8. Mbed TLS
      * supports non-byte-aligned key sizes, but not well. For example,
      * mbedtls_rsa_get_len() returns the key size in bytes, not in bits. */
-    bits = PSA_BYTES_TO_BITS(mbedtls_rsa_get_len(mbedtls_pk_rsa(ctx)));
+    bits = PSA_BYTES_TO_BITS(mbedtls_rsa_get_len(*p_rsa));
     if (bits > PSA_VENDOR_RSA_MAX_KEY_BITS) {
         status = PSA_ERROR_NOT_SUPPORTED;
         goto exit;
     }
-    status = psa_check_rsa_key_byte_aligned(mbedtls_pk_rsa(ctx));
+    status = psa_check_rsa_key_byte_aligned(*p_rsa);
     if (status != PSA_SUCCESS) {
         goto exit;
     }
 
-    /* Copy out the pointer to the RSA context, and reset the PK context
-     * such that pk_free doesn't free the RSA context we just grabbed. */
-    *p_rsa = mbedtls_pk_rsa(ctx);
-    ctx.pk_info = NULL;
-
 exit:
-    mbedtls_pk_free(&ctx);
     return status;
 }
 #endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) ||
@@ -129,7 +116,7 @@
     mbedtls_rsa_context *rsa = NULL;
 
     /* Parse input */
-    status = mbedtls_psa_rsa_load_representation(attributes->core.type,
+    status = mbedtls_psa_rsa_load_representation(attributes->type,
                                                  data,
                                                  data_length,
                                                  &rsa);
@@ -143,7 +130,7 @@
      * representation in the key slot. Export representation in case of RSA is
      * the smallest representation that's allowed as input, so a straight-up
      * allocation of the same size as the input buffer will be large enough. */
-    status = mbedtls_psa_rsa_export_key(attributes->core.type,
+    status = mbedtls_psa_rsa_export_key(attributes->type,
                                         rsa,
                                         key_buffer,
                                         key_buffer_size,
@@ -168,20 +155,15 @@
                                         size_t *data_length)
 {
     int ret;
-    mbedtls_pk_context pk;
-    uint8_t *pos = data + data_size;
-
-    mbedtls_pk_init(&pk);
-    pk.pk_info = &mbedtls_rsa_info;
-    pk.pk_ctx = rsa;
+    uint8_t *end = data + data_size;
 
     /* PSA Crypto API defines the format of an RSA key as a DER-encoded
      * representation of the non-encrypted PKCS#1 RSAPrivateKey for a
      * private key and of the RFC3279 RSAPublicKey for a public key. */
     if (PSA_KEY_TYPE_IS_KEY_PAIR(type)) {
-        ret = mbedtls_pk_write_key_der(&pk, data, data_size);
+        ret = mbedtls_rsa_write_key(rsa, data, &end);
     } else {
-        ret = mbedtls_pk_write_pubkey(&pos, data, &pk);
+        ret = mbedtls_rsa_write_pubkey(rsa, data, &end);
     }
 
     if (ret < 0) {
@@ -214,7 +196,7 @@
     mbedtls_rsa_context *rsa = NULL;
 
     status = mbedtls_psa_rsa_load_representation(
-        attributes->core.type, key_buffer, key_buffer_size, &rsa);
+        attributes->type, key_buffer, key_buffer_size, &rsa);
     if (status != PSA_SUCCESS) {
         return status;
     }
@@ -234,26 +216,21 @@
         * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */
 
 #if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_GENERATE)
-static psa_status_t psa_rsa_read_exponent(const uint8_t *domain_parameters,
-                                          size_t domain_parameters_size,
+static psa_status_t psa_rsa_read_exponent(const uint8_t *e_bytes,
+                                          size_t e_length,
                                           int *exponent)
 {
     size_t i;
     uint32_t acc = 0;
 
-    if (domain_parameters_size == 0) {
-        *exponent = 65537;
-        return PSA_SUCCESS;
-    }
-
     /* Mbed TLS encodes the public exponent as an int. For simplicity, only
      * support values that fit in a 32-bit integer, which is larger than
      * int on just about every platform anyway. */
-    if (domain_parameters_size > sizeof(acc)) {
+    if (e_length > sizeof(acc)) {
         return PSA_ERROR_NOT_SUPPORTED;
     }
-    for (i = 0; i < domain_parameters_size; i++) {
-        acc = (acc << 8) | domain_parameters[i];
+    for (i = 0; i < e_length; i++) {
+        acc = (acc << 8) | e_bytes[i];
     }
     if (acc > INT_MAX) {
         return PSA_ERROR_NOT_SUPPORTED;
@@ -264,31 +241,33 @@
 
 psa_status_t mbedtls_psa_rsa_generate_key(
     const psa_key_attributes_t *attributes,
+    const psa_key_production_parameters_t *params, size_t params_data_length,
     uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length)
 {
     psa_status_t status;
     mbedtls_rsa_context rsa;
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    int exponent;
+    int exponent = 65537;
 
-    status = psa_rsa_read_exponent(attributes->domain_parameters,
-                                   attributes->domain_parameters_size,
-                                   &exponent);
-    if (status != PSA_SUCCESS) {
-        return status;
+    if (params_data_length != 0) {
+        status = psa_rsa_read_exponent(params->data, params_data_length,
+                                       &exponent);
+        if (status != PSA_SUCCESS) {
+            return status;
+        }
     }
 
     mbedtls_rsa_init(&rsa);
     ret = mbedtls_rsa_gen_key(&rsa,
                               mbedtls_psa_get_random,
                               MBEDTLS_PSA_RANDOM_STATE,
-                              (unsigned int) attributes->core.bits,
+                              (unsigned int) attributes->bits,
                               exponent);
     if (ret != 0) {
         return mbedtls_to_psa_error(ret);
     }
 
-    status = mbedtls_psa_rsa_export_key(attributes->core.type,
+    status = mbedtls_psa_rsa_export_key(attributes->type,
                                         &rsa, key_buffer, key_buffer_size,
                                         key_buffer_length);
     mbedtls_rsa_free(&rsa);
@@ -346,7 +325,7 @@
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     mbedtls_md_type_t md_alg;
 
-    status = mbedtls_psa_rsa_load_representation(attributes->core.type,
+    status = mbedtls_psa_rsa_load_representation(attributes->type,
                                                  key_buffer,
                                                  key_buffer_size,
                                                  &rsa);
@@ -445,7 +424,7 @@
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     mbedtls_md_type_t md_alg;
 
-    status = mbedtls_psa_rsa_load_representation(attributes->core.type,
+    status = mbedtls_psa_rsa_load_representation(attributes->type,
                                                  key_buffer,
                                                  key_buffer_size,
                                                  &rsa);
@@ -557,11 +536,11 @@
     (void) output_size;
     (void) output_length;
 
-    if (PSA_KEY_TYPE_IS_RSA(attributes->core.type)) {
+    if (PSA_KEY_TYPE_IS_RSA(attributes->type)) {
 #if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || \
         defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP)
         mbedtls_rsa_context *rsa = NULL;
-        status = mbedtls_psa_rsa_load_representation(attributes->core.type,
+        status = mbedtls_psa_rsa_load_representation(attributes->type,
                                                      key_buffer,
                                                      key_buffer_size,
                                                      &rsa);
@@ -653,11 +632,11 @@
 
     *output_length = 0;
 
-    if (attributes->core.type == PSA_KEY_TYPE_RSA_KEY_PAIR) {
+    if (attributes->type == PSA_KEY_TYPE_RSA_KEY_PAIR) {
 #if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || \
         defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP)
         mbedtls_rsa_context *rsa = NULL;
-        status = mbedtls_psa_rsa_load_representation(attributes->core.type,
+        status = mbedtls_psa_rsa_load_representation(attributes->type,
                                                      key_buffer,
                                                      key_buffer_size,
                                                      &rsa);
diff --git a/library/psa_crypto_rsa.h b/library/psa_crypto_rsa.h
index e4c5caf..ffeef26 100644
--- a/library/psa_crypto_rsa.h
+++ b/library/psa_crypto_rsa.h
@@ -109,6 +109,15 @@
  *       entry point.
  *
  * \param[in]  attributes         The attributes for the RSA key to generate.
+ * \param[in]  params             Production parameters for the key
+ *                                generation. This function only uses
+ *                                `params->data`,
+ *                                which contains the public exponent.
+ *                                This can be a null pointer if
+ *                                \c params_data_length is 0.
+ * \param params_data_length      Length of `params->data` in bytes.
+ *                                This can be 0, in which case the
+ *                                public exponent will be 65537.
  * \param[out] key_buffer         Buffer where the key data is to be written.
  * \param[in]  key_buffer_size    Size of \p key_buffer in bytes.
  * \param[out] key_buffer_length  On success, the number of bytes written in
@@ -123,6 +132,7 @@
  */
 psa_status_t mbedtls_psa_rsa_generate_key(
     const psa_key_attributes_t *attributes,
+    const psa_key_production_parameters_t *params, size_t params_data_length,
     uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length);
 
 /** Sign an already-calculated hash with an RSA private key.
diff --git a/library/psa_crypto_slot_management.c b/library/psa_crypto_slot_management.c
index 47ace35..5dee32f 100644
--- a/library/psa_crypto_slot_management.c
+++ b/library/psa_crypto_slot_management.c
@@ -70,6 +70,9 @@
  * On success, the function locks the key slot. It is the responsibility of
  * the caller to unlock the key slot when it does not access it anymore.
  *
+ * If multi-threading is enabled, the caller must hold the
+ * global key slot mutex.
+ *
  * \param key           Key identifier to query.
  * \param[out] p_slot   On success, `*p_slot` contains a pointer to the
  *                      key slot containing the description of the key
@@ -94,16 +97,14 @@
     if (psa_key_id_is_volatile(key_id)) {
         slot = &global_data.key_slots[key_id - PSA_KEY_ID_VOLATILE_MIN];
 
-        /*
-         * Check if both the PSA key identifier key_id and the owner
-         * identifier of key match those of the key slot.
-         *
-         * Note that, if the key slot is not occupied, its PSA key identifier
-         * is equal to zero. This is an invalid value for a PSA key identifier
-         * and thus cannot be equal to the valid PSA key identifier key_id.
-         */
-        status = mbedtls_svc_key_id_equal(key, slot->attr.id) ?
-                 PSA_SUCCESS : PSA_ERROR_DOES_NOT_EXIST;
+        /* Check if both the PSA key identifier key_id and the owner
+         * identifier of key match those of the key slot. */
+        if ((slot->state == PSA_SLOT_FULL) &&
+            (mbedtls_svc_key_id_equal(key, slot->attr.id))) {
+            status = PSA_SUCCESS;
+        } else {
+            status = PSA_ERROR_DOES_NOT_EXIST;
+        }
     } else {
         if (!psa_is_valid_key_id(key, 1)) {
             return PSA_ERROR_INVALID_HANDLE;
@@ -248,11 +249,6 @@
         data = (psa_se_key_data_storage_t *) key_data;
         status = psa_copy_key_material_into_slot(
             slot, data->slot_number, sizeof(data->slot_number));
-
-        if (status == PSA_SUCCESS) {
-            status = psa_key_slot_state_transition(slot, PSA_SLOT_FILLING,
-                                                   PSA_SLOT_FULL);
-        }
         goto exit;
     }
 #endif /* MBEDTLS_PSA_CRYPTO_SE_C */
@@ -262,9 +258,6 @@
         goto exit;
     }
 
-    status = psa_key_slot_state_transition(slot, PSA_SLOT_FILLING,
-                                           PSA_SLOT_FULL);
-
 exit:
     psa_free_persistent_key_data(key_data, key_data_length);
     return status;
@@ -336,10 +329,7 @@
 
     /* Copy actual key length and core attributes into the slot on success */
     slot->key.bytes = key_buffer_length;
-    slot->attr = attributes.core;
-
-    status = psa_key_slot_state_transition(slot, PSA_SLOT_FILLING,
-                                           PSA_SLOT_FULL);
+    slot->attr = attributes;
 exit:
     if (status != PSA_SUCCESS) {
         psa_remove_key_data_from_memory(slot);
@@ -358,12 +348,27 @@
         return PSA_ERROR_BAD_STATE;
     }
 
+#if defined(MBEDTLS_THREADING_C)
+    /* We need to set status as success, otherwise CORRUPTION_DETECTED
+     * would be returned if the lock fails. */
+    status = PSA_SUCCESS;
+    /* If the key is persistent and not loaded, we cannot unlock the mutex
+     * between checking if the key is loaded and setting the slot as FULL,
+     * as otherwise another thread may load and then destroy the key
+     * in the meantime. */
+    PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
     /*
      * On success, the pointer to the slot is passed directly to the caller
      * thus no need to unlock the key slot here.
      */
     status = psa_get_and_lock_key_slot_in_memory(key, p_slot);
     if (status != PSA_ERROR_DOES_NOT_EXIST) {
+#if defined(MBEDTLS_THREADING_C)
+        PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                                  &mbedtls_threading_key_slot_mutex));
+#endif
         return status;
     }
 
@@ -374,6 +379,10 @@
 
     status = psa_reserve_free_key_slot(&volatile_key_id, p_slot);
     if (status != PSA_SUCCESS) {
+#if defined(MBEDTLS_THREADING_C)
+        PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                                  &mbedtls_threading_key_slot_mutex));
+#endif
         return status;
     }
 
@@ -407,10 +416,15 @@
         status = psa_register_read(*p_slot);
     }
 
-    return status;
 #else /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
-    return PSA_ERROR_INVALID_HANDLE;
+    status = PSA_ERROR_INVALID_HANDLE;
 #endif /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
+
+#if defined(MBEDTLS_THREADING_C)
+    PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+    return status;
 }
 
 psa_status_t psa_unregister_read(psa_key_slot_t *slot)
@@ -447,6 +461,24 @@
     return PSA_ERROR_CORRUPTION_DETECTED;
 }
 
+psa_status_t psa_unregister_read_under_mutex(psa_key_slot_t *slot)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+#if defined(MBEDTLS_THREADING_C)
+    /* We need to set status as success, otherwise CORRUPTION_DETECTED
+     * would be returned if the lock fails. */
+    status = PSA_SUCCESS;
+    PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+    status = psa_unregister_read(slot);
+#if defined(MBEDTLS_THREADING_C)
+    PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+    return status;
+}
+
 psa_status_t psa_validate_key_location(psa_key_lifetime_t lifetime,
                                        psa_se_drv_table_entry_t **p_drv)
 {
@@ -510,7 +542,7 @@
 
     *handle = key;
 
-    return psa_unregister_read(slot);
+    return psa_unregister_read_under_mutex(slot);
 
 #else /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
     (void) key;
@@ -521,44 +553,78 @@
 
 psa_status_t psa_close_key(psa_key_handle_t handle)
 {
-    psa_status_t status;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_key_slot_t *slot;
 
     if (psa_key_handle_is_null(handle)) {
         return PSA_SUCCESS;
     }
 
+#if defined(MBEDTLS_THREADING_C)
+    /* We need to set status as success, otherwise CORRUPTION_DETECTED
+     * would be returned if the lock fails. */
+    status = PSA_SUCCESS;
+    PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
     status = psa_get_and_lock_key_slot_in_memory(handle, &slot);
     if (status != PSA_SUCCESS) {
         if (status == PSA_ERROR_DOES_NOT_EXIST) {
             status = PSA_ERROR_INVALID_HANDLE;
         }
-
+#if defined(MBEDTLS_THREADING_C)
+        PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                                  &mbedtls_threading_key_slot_mutex));
+#endif
         return status;
     }
+
     if (slot->registered_readers == 1) {
-        return psa_wipe_key_slot(slot);
+        status = psa_wipe_key_slot(slot);
     } else {
-        return psa_unregister_read(slot);
+        status = psa_unregister_read(slot);
     }
+#if defined(MBEDTLS_THREADING_C)
+    PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+
+    return status;
 }
 
 psa_status_t psa_purge_key(mbedtls_svc_key_id_t key)
 {
-    psa_status_t status;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
     psa_key_slot_t *slot;
 
+#if defined(MBEDTLS_THREADING_C)
+    /* We need to set status as success, otherwise CORRUPTION_DETECTED
+     * would be returned if the lock fails. */
+    status = PSA_SUCCESS;
+    PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
     status = psa_get_and_lock_key_slot_in_memory(key, &slot);
     if (status != PSA_SUCCESS) {
+#if defined(MBEDTLS_THREADING_C)
+        PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                                  &mbedtls_threading_key_slot_mutex));
+#endif
         return status;
     }
 
     if ((!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) &&
         (slot->registered_readers == 1)) {
-        return psa_wipe_key_slot(slot);
+        status = psa_wipe_key_slot(slot);
     } else {
-        return psa_unregister_read(slot);
+        status = psa_unregister_read(slot);
     }
+#if defined(MBEDTLS_THREADING_C)
+    PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+                              &mbedtls_threading_key_slot_mutex));
+#endif
+
+    return status;
 }
 
 void mbedtls_psa_get_stats(mbedtls_psa_stats_t *stats)
diff --git a/library/psa_crypto_slot_management.h b/library/psa_crypto_slot_management.h
index 002429b..bcfc9d8 100644
--- a/library/psa_crypto_slot_management.h
+++ b/library/psa_crypto_slot_management.h
@@ -92,6 +92,8 @@
 psa_status_t psa_initialize_key_slots(void);
 
 /** Delete all data from key slots in memory.
+ * This function is not thread safe, it wipes every key slot regardless of
+ * state and reader count. It should only be called when no slot is in use.
  *
  * This does not affect persistent storage. */
 void psa_wipe_all_key_slots(void);
@@ -105,6 +107,9 @@
  * It is the responsibility of the caller to change the slot's state to
  * PSA_SLOT_EMPTY/FULL once key creation has finished.
  *
+ * If multi-threading is enabled, the caller must hold the
+ * global key slot mutex.
+ *
  * \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.
@@ -200,6 +205,27 @@
  */
 psa_status_t psa_unregister_read(psa_key_slot_t *slot);
 
+/** Wrap a call to psa_unregister_read in the global key slot mutex.
+ *
+ * If threading is disabled, this simply calls psa_unregister_read.
+ *
+ * \note To ease the handling of errors in retrieving a key slot
+ *       a NULL input pointer is valid, and the function returns
+ *       successfully without doing anything in that case.
+ *
+ * \param[in] slot  The key slot.
+ * \retval #PSA_SUCCESS
+ *             \p slot is NULL or the key slot reader counter has been
+ *             decremented (and potentially wiped) successfully.
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED
+ *             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_unregister_read_under_mutex(psa_key_slot_t *slot);
+
 /** Test whether a lifetime designates a key in an external cryptoprocessor.
  *
  * \param lifetime      The lifetime to test.
diff --git a/library/psa_crypto_storage.c b/library/psa_crypto_storage.c
index 13a3c8a..7d1317b 100644
--- a/library/psa_crypto_storage.c
+++ b/library/psa_crypto_storage.c
@@ -235,7 +235,7 @@
 
 void psa_format_key_data_for_storage(const uint8_t *data,
                                      const size_t data_length,
-                                     const psa_core_key_attributes_t *attr,
+                                     const psa_key_attributes_t *attr,
                                      uint8_t *storage_data)
 {
     psa_persistent_key_storage_format *storage_format =
@@ -267,7 +267,7 @@
                                              size_t storage_data_length,
                                              uint8_t **key_data,
                                              size_t *key_data_length,
-                                             psa_core_key_attributes_t *attr)
+                                             psa_key_attributes_t *attr)
 {
     psa_status_t status;
     const psa_persistent_key_storage_format *storage_format =
@@ -314,7 +314,7 @@
     return PSA_SUCCESS;
 }
 
-psa_status_t psa_save_persistent_key(const psa_core_key_attributes_t *attr,
+psa_status_t psa_save_persistent_key(const psa_key_attributes_t *attr,
                                      const uint8_t *data,
                                      const size_t data_length)
 {
@@ -352,7 +352,7 @@
     mbedtls_zeroize_and_free(key_data, key_data_length);
 }
 
-psa_status_t psa_load_persistent_key(psa_core_key_attributes_t *attr,
+psa_status_t psa_load_persistent_key(psa_key_attributes_t *attr,
                                      uint8_t **data,
                                      size_t *data_length)
 {
diff --git a/library/psa_crypto_storage.h b/library/psa_crypto_storage.h
index b6b5e15..f1ea265 100644
--- a/library/psa_crypto_storage.h
+++ b/library/psa_crypto_storage.h
@@ -93,7 +93,7 @@
  * \retval #PSA_ERROR_DATA_INVALID \emptydescription
  * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription
  */
-psa_status_t psa_save_persistent_key(const psa_core_key_attributes_t *attr,
+psa_status_t psa_save_persistent_key(const psa_key_attributes_t *attr,
                                      const uint8_t *data,
                                      const size_t data_length);
 
@@ -123,7 +123,7 @@
  * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription
  * \retval #PSA_ERROR_DOES_NOT_EXIST \emptydescription
  */
-psa_status_t psa_load_persistent_key(psa_core_key_attributes_t *attr,
+psa_status_t psa_load_persistent_key(psa_key_attributes_t *attr,
                                      uint8_t **data,
                                      size_t *data_length);
 
@@ -163,7 +163,7 @@
  */
 void psa_format_key_data_for_storage(const uint8_t *data,
                                      const size_t data_length,
-                                     const psa_core_key_attributes_t *attr,
+                                     const psa_key_attributes_t *attr,
                                      uint8_t *storage_data);
 
 /**
@@ -186,7 +186,7 @@
                                              size_t storage_data_length,
                                              uint8_t **key_data,
                                              size_t *key_data_length,
-                                             psa_core_key_attributes_t *attr);
+                                             psa_key_attributes_t *attr);
 
 #if defined(MBEDTLS_PSA_CRYPTO_SE_C)
 /** This symbol is defined if transaction support is required. */
diff --git a/library/psa_util.c b/library/psa_util.c
index 41586e2..4ccc5b0 100644
--- a/library/psa_util.c
+++ b/library/psa_util.c
@@ -8,14 +8,20 @@
 
 #include "common.h"
 
-#if defined(MBEDTLS_PSA_CRYPTO_C)
+/* This is needed for MBEDTLS_ERR_XXX macros */
+#include <mbedtls/error.h>
 
-#include <psa/crypto.h>
+#if defined(MBEDTLS_ASN1_WRITE_C)
+#include <mbedtls/asn1write.h>
+#include <psa/crypto_sizes.h>
+#endif
 
 #include "psa_util_internal.h"
 
-/* The following includes are needed for MBEDTLS_ERR_XXX macros */
-#include <mbedtls/error.h>
+#if defined(MBEDTLS_PSA_CRYPTO_CLIENT)
+
+#include <psa/crypto.h>
+
 #if defined(MBEDTLS_MD_LIGHT)
 #include <mbedtls/md.h>
 #endif
@@ -40,6 +46,7 @@
 #if defined(MBEDTLS_BLOCK_CIPHER_SOME_PSA)
 #include <mbedtls/cipher.h>
 #endif
+#include <mbedtls/entropy.h>
 
 /* PSA_SUCCESS is kept at the top of each error table since
  * it's the most common status when everything functions properly. */
@@ -158,6 +165,8 @@
             return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
         case PSA_ERROR_INVALID_ARGUMENT:
             return MBEDTLS_ERR_PK_INVALID_ALG;
+        case PSA_ERROR_NOT_PERMITTED:
+            return MBEDTLS_ERR_PK_TYPE_MISMATCH;
         case PSA_ERROR_INSUFFICIENT_MEMORY:
             return MBEDTLS_ERR_PK_ALLOC_FAILED;
         case PSA_ERROR_BAD_STATE:
@@ -330,4 +339,264 @@
 }
 #endif /* PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY */
 
-#endif /* MBEDTLS_PSA_CRYPTO_C */
+/* Wrapper function allowing the classic API to use the PSA RNG.
+ *
+ * `mbedtls_psa_get_random(MBEDTLS_PSA_RANDOM_STATE, ...)` calls
+ * `psa_generate_random(...)`. The state parameter is ignored since the
+ * PSA API doesn't support passing an explicit state.
+ */
+int mbedtls_psa_get_random(void *p_rng,
+                           unsigned char *output,
+                           size_t output_size)
+{
+    /* This function takes a pointer to the RNG state because that's what
+     * classic mbedtls functions using an RNG expect. The PSA RNG manages
+     * its own state internally and doesn't let the caller access that state.
+     * So we just ignore the state parameter, and in practice we'll pass
+     * NULL. */
+    (void) p_rng;
+    psa_status_t status = psa_generate_random(output, output_size);
+    if (status == PSA_SUCCESS) {
+        return 0;
+    } else {
+        return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
+    }
+}
+
+#endif /* MBEDTLS_PSA_CRYPTO_CLIENT */
+
+#if defined(MBEDTLS_PSA_UTIL_HAVE_ECDSA)
+
+/**
+ * \brief  Convert a single raw coordinate to DER ASN.1 format. The output der
+ *         buffer is filled backward (i.e. starting from its end).
+ *
+ * \param raw_buf           Buffer containing the raw coordinate to be
+ *                          converted.
+ * \param raw_len           Length of raw_buf in bytes. This must be > 0.
+ * \param der_buf_start     Pointer to the beginning of the buffer which
+ *                          will be filled with the DER converted data.
+ * \param der_buf_end       End of the buffer used to store the DER output.
+ *
+ * \return                  On success, the amount of data (in bytes) written to
+ *                          the DER buffer.
+ * \return                  MBEDTLS_ERR_ASN1_BUF_TOO_SMALL if the provided der
+ *                          buffer is too small to contain all the converted data.
+ * \return                  MBEDTLS_ERR_ASN1_INVALID_DATA if the input raw
+ *                          coordinate is null (i.e. all zeros).
+ *
+ * \warning                 Raw and der buffer must not be overlapping.
+ */
+static int convert_raw_to_der_single_int(const unsigned char *raw_buf, size_t raw_len,
+                                         unsigned char *der_buf_start,
+                                         unsigned char *der_buf_end)
+{
+    unsigned char *p = der_buf_end;
+    int len;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+    /* ASN.1 DER encoding requires minimal length, so skip leading 0s.
+     * Provided input MPIs should not be 0, but as a failsafe measure, still
+     * detect that and return error in case. */
+    while (*raw_buf == 0x00) {
+        ++raw_buf;
+        --raw_len;
+        if (raw_len == 0) {
+            return MBEDTLS_ERR_ASN1_INVALID_DATA;
+        }
+    }
+    len = (int) raw_len;
+
+    /* Copy the raw coordinate to the end of der_buf. */
+    if ((p - der_buf_start) < len) {
+        return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
+    }
+    p -= len;
+    memcpy(p, raw_buf, len);
+
+    /* If MSb is 1, ASN.1 requires that we prepend a 0. */
+    if (*p & 0x80) {
+        if ((p - der_buf_start) < 1) {
+            return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
+        }
+        --p;
+        *p = 0x00;
+        ++len;
+    }
+
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, der_buf_start, len));
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&p, der_buf_start, MBEDTLS_ASN1_INTEGER));
+
+    return len;
+}
+
+int mbedtls_ecdsa_raw_to_der(size_t bits, const unsigned char *raw, size_t raw_len,
+                             unsigned char *der, size_t der_size, size_t *der_len)
+{
+    unsigned char r[PSA_BITS_TO_BYTES(PSA_VENDOR_ECC_MAX_CURVE_BITS)];
+    unsigned char s[PSA_BITS_TO_BYTES(PSA_VENDOR_ECC_MAX_CURVE_BITS)];
+    const size_t coordinate_len = PSA_BITS_TO_BYTES(bits);
+    size_t len = 0;
+    unsigned char *p = der + der_size;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+    if (raw_len != (2 * coordinate_len)) {
+        return MBEDTLS_ERR_ASN1_INVALID_DATA;
+    }
+
+    /* Since raw and der buffers might overlap, dump r and s before starting
+     * the conversion. */
+    memcpy(r, raw, coordinate_len);
+    memcpy(s, raw + coordinate_len, coordinate_len);
+
+    /* der buffer will initially be written starting from its end so we pick s
+     * first and then r. */
+    ret = convert_raw_to_der_single_int(s, coordinate_len, der, p);
+    if (ret < 0) {
+        return ret;
+    }
+    p -= ret;
+    len += ret;
+
+    ret = convert_raw_to_der_single_int(r, coordinate_len, der, p);
+    if (ret < 0) {
+        return ret;
+    }
+    p -= ret;
+    len += ret;
+
+    /* Add ASN.1 header (len + tag). */
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, der, len));
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&p, der,
+                                                     MBEDTLS_ASN1_CONSTRUCTED |
+                                                     MBEDTLS_ASN1_SEQUENCE));
+
+    /* memmove the content of der buffer to its beginnig. */
+    memmove(der, p, len);
+    *der_len = len;
+
+    return 0;
+}
+
+/**
+ * \brief Convert a single integer from ASN.1 DER format to raw.
+ *
+ * \param der               Buffer containing the DER integer value to be
+ *                          converted.
+ * \param der_len           Length of the der buffer in bytes.
+ * \param raw               Output buffer that will be filled with the
+ *                          converted data. This should be at least
+ *                          coordinate_size bytes and it must be zeroed before
+ *                          calling this function.
+ * \param coordinate_size   Size (in bytes) of a single coordinate in raw
+ *                          format.
+ *
+ * \return                  On success, the amount of DER data parsed from the
+ *                          provided der buffer.
+ * \return                  MBEDTLS_ERR_ASN1_UNEXPECTED_TAG if the integer tag
+ *                          is missing in the der buffer.
+ * \return                  MBEDTLS_ERR_ASN1_LENGTH_MISMATCH if the integer
+ *                          is null (i.e. all zeros) or if the output raw buffer
+ *                          is too small to contain the converted raw value.
+ *
+ * \warning                 Der and raw buffers must not be overlapping.
+ */
+static int convert_der_to_raw_single_int(unsigned char *der, size_t der_len,
+                                         unsigned char *raw, size_t coordinate_size)
+{
+    unsigned char *p = der;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    size_t unpadded_len, padding_len = 0;
+
+    /* Get the length of ASN.1 element (i.e. the integer we need to parse). */
+    ret = mbedtls_asn1_get_tag(&p, p + der_len, &unpadded_len,
+                               MBEDTLS_ASN1_INTEGER);
+    if (ret != 0) {
+        return ret;
+    }
+
+    /* It's invalid to have:
+     * - unpadded_len == 0.
+     * - MSb set without a leading 0x00 (leading 0x00 is checked below). */
+    if (((unpadded_len == 0) || (*p & 0x80) != 0)) {
+        return MBEDTLS_ERR_ASN1_INVALID_DATA;
+    }
+
+    /* Skip possible leading zero */
+    if (*p == 0x00) {
+        p++;
+        unpadded_len--;
+        /* It is not allowed to have more than 1 leading zero.
+         * Ignore the case in which unpadded_len = 0 because that's a 0 encoded
+         * in ASN.1 format (i.e. 020100). */
+        if ((unpadded_len > 0) && (*p == 0x00)) {
+            return MBEDTLS_ERR_ASN1_INVALID_DATA;
+        }
+    }
+
+    if (unpadded_len > coordinate_size) {
+        /* Parsed number is longer than the maximum expected value. */
+        return MBEDTLS_ERR_ASN1_INVALID_DATA;
+    }
+    padding_len = coordinate_size - unpadded_len;
+    /* raw buffer was already zeroed by the calling function so zero-padding
+     * operation is skipped here. */
+    memcpy(raw + padding_len, p, unpadded_len);
+    p += unpadded_len;
+
+    return (int) (p - der);
+}
+
+int mbedtls_ecdsa_der_to_raw(size_t bits, const unsigned char *der, size_t der_len,
+                             unsigned char *raw, size_t raw_size, size_t *raw_len)
+{
+    unsigned char raw_tmp[PSA_VENDOR_ECDSA_SIGNATURE_MAX_SIZE];
+    unsigned char *p = (unsigned char *) der;
+    size_t data_len;
+    size_t coordinate_size = PSA_BITS_TO_BYTES(bits);
+    int ret;
+
+    /* The output raw buffer should be at least twice the size of a raw
+     * coordinate in order to store r and s. */
+    if (raw_size < coordinate_size * 2) {
+        return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
+    }
+
+    /* Check that the provided input DER buffer has the right header. */
+    ret = mbedtls_asn1_get_tag(&p, der + der_len, &data_len,
+                               MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+    if (ret != 0) {
+        return ret;
+    }
+
+    memset(raw_tmp, 0, 2 * coordinate_size);
+
+    /* Extract r */
+    ret = convert_der_to_raw_single_int(p, data_len, raw_tmp, coordinate_size);
+    if (ret < 0) {
+        return ret;
+    }
+    p += ret;
+    data_len -= ret;
+
+    /* Extract s */
+    ret = convert_der_to_raw_single_int(p, data_len, raw_tmp + coordinate_size,
+                                        coordinate_size);
+    if (ret < 0) {
+        return ret;
+    }
+    p += ret;
+    data_len -= ret;
+
+    /* Check that we consumed all the input der data. */
+    if ((size_t) (p - der) != der_len) {
+        return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH;
+    }
+
+    memcpy(raw, raw_tmp, 2 * coordinate_size);
+    *raw_len = 2 * coordinate_size;
+
+    return 0;
+}
+
+#endif /* MBEDTLS_PSA_UTIL_HAVE_ECDSA */
diff --git a/library/psa_util_internal.h b/library/psa_util_internal.h
index 3e62d5f..70a08a0 100644
--- a/library/psa_util_internal.h
+++ b/library/psa_util_internal.h
@@ -16,7 +16,7 @@
 
 #include "psa/crypto.h"
 
-#if defined(MBEDTLS_PSA_CRYPTO_C)
+#if defined(MBEDTLS_PSA_CRYPTO_CLIENT)
 
 /*************************************************************************
  * FFDH
@@ -96,5 +96,5 @@
                           sizeof(error_list)/sizeof(error_list[0]),   \
                           fallback_f)
 
-#endif /* MBEDTLS_PSA_CRYPTO_C */
+#endif /* MBEDTLS_PSA_CRYPTO_CLIENT */
 #endif /* MBEDTLS_PSA_UTIL_INTERNAL_H */
diff --git a/library/rsa.c b/library/rsa.c
index a90b83a..5debc69 100644
--- a/library/rsa.c
+++ b/library/rsa.c
@@ -32,6 +32,7 @@
 #include "rsa_alt_helpers.h"
 #include "rsa_internal.h"
 #include "mbedtls/oid.h"
+#include "mbedtls/asn1write.h"
 #include "mbedtls/platform_util.h"
 #include "mbedtls/error.h"
 #include "constant_time_internal.h"
@@ -46,6 +47,369 @@
 
 #include "mbedtls/platform.h"
 
+/*
+ * Wrapper around mbedtls_asn1_get_mpi() that rejects zero.
+ *
+ * The value zero is:
+ * - never a valid value for an RSA parameter
+ * - interpreted as "omitted, please reconstruct" by mbedtls_rsa_complete().
+ *
+ * Since values can't be omitted in PKCS#1, passing a zero value to
+ * rsa_complete() would be incorrect, so reject zero values early.
+ */
+static int asn1_get_nonzero_mpi(unsigned char **p,
+                                const unsigned char *end,
+                                mbedtls_mpi *X)
+{
+    int ret;
+
+    ret = mbedtls_asn1_get_mpi(p, end, X);
+    if (ret != 0) {
+        return ret;
+    }
+
+    if (mbedtls_mpi_cmp_int(X, 0) == 0) {
+        return MBEDTLS_ERR_RSA_BAD_INPUT_DATA;
+    }
+
+    return 0;
+}
+
+int mbedtls_rsa_parse_key(mbedtls_rsa_context *rsa, const unsigned char *key, size_t keylen)
+{
+    int ret, version;
+    size_t len;
+    unsigned char *p, *end;
+
+    mbedtls_mpi T;
+    mbedtls_mpi_init(&T);
+
+    p = (unsigned char *) key;
+    end = p + keylen;
+
+    /*
+     * This function parses the RSAPrivateKey (PKCS#1)
+     *
+     *  RSAPrivateKey ::= SEQUENCE {
+     *      version           Version,
+     *      modulus           INTEGER,  -- n
+     *      publicExponent    INTEGER,  -- e
+     *      privateExponent   INTEGER,  -- d
+     *      prime1            INTEGER,  -- p
+     *      prime2            INTEGER,  -- q
+     *      exponent1         INTEGER,  -- d mod (p-1)
+     *      exponent2         INTEGER,  -- d mod (q-1)
+     *      coefficient       INTEGER,  -- (inverse of q) mod p
+     *      otherPrimeInfos   OtherPrimeInfos OPTIONAL
+     *  }
+     */
+    if ((ret = mbedtls_asn1_get_tag(&p, end, &len,
+                                    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) {
+        return ret;
+    }
+
+    if (end != p + len) {
+        return MBEDTLS_ERR_RSA_BAD_INPUT_DATA;
+    }
+
+    if ((ret = mbedtls_asn1_get_int(&p, end, &version)) != 0) {
+        return ret;
+    }
+
+    if (version != 0) {
+        return MBEDTLS_ERR_RSA_BAD_INPUT_DATA;
+    }
+
+    /* Import N */
+    if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
+        (ret = mbedtls_rsa_import(rsa, &T, NULL, NULL,
+                                  NULL, NULL)) != 0) {
+        goto cleanup;
+    }
+
+    /* Import E */
+    if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
+        (ret = mbedtls_rsa_import(rsa, NULL, NULL, NULL,
+                                  NULL, &T)) != 0) {
+        goto cleanup;
+    }
+
+    /* Import D */
+    if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
+        (ret = mbedtls_rsa_import(rsa, NULL, NULL, NULL,
+                                  &T, NULL)) != 0) {
+        goto cleanup;
+    }
+
+    /* Import P */
+    if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
+        (ret = mbedtls_rsa_import(rsa, NULL, &T, NULL,
+                                  NULL, NULL)) != 0) {
+        goto cleanup;
+    }
+
+    /* Import Q */
+    if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
+        (ret = mbedtls_rsa_import(rsa, NULL, NULL, &T,
+                                  NULL, NULL)) != 0) {
+        goto cleanup;
+    }
+
+#if !defined(MBEDTLS_RSA_NO_CRT) && !defined(MBEDTLS_RSA_ALT)
+    /*
+     * The RSA CRT parameters DP, DQ and QP are nominally redundant, in
+     * that they can be easily recomputed from D, P and Q. However by
+     * parsing them from the PKCS1 structure it is possible to avoid
+     * recalculating them which both reduces the overhead of loading
+     * RSA private keys into memory and also avoids side channels which
+     * can arise when computing those values, since all of D, P, and Q
+     * are secret. See https://eprint.iacr.org/2020/055 for a
+     * description of one such attack.
+     */
+
+    /* Import DP */
+    if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
+        (ret = mbedtls_mpi_copy(&rsa->DP, &T)) != 0) {
+        goto cleanup;
+    }
+
+    /* Import DQ */
+    if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
+        (ret = mbedtls_mpi_copy(&rsa->DQ, &T)) != 0) {
+        goto cleanup;
+    }
+
+    /* Import QP */
+    if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
+        (ret = mbedtls_mpi_copy(&rsa->QP, &T)) != 0) {
+        goto cleanup;
+    }
+
+#else
+    /* Verify existence of the CRT params */
+    if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
+        (ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 ||
+        (ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0) {
+        goto cleanup;
+    }
+#endif
+
+    /* rsa_complete() doesn't complete anything with the default
+     * implementation but is still called:
+     * - for the benefit of alternative implementation that may want to
+     *   pre-compute stuff beyond what's provided (eg Montgomery factors)
+     * - as is also sanity-checks the key
+     *
+     * Furthermore, we also check the public part for consistency with
+     * mbedtls_pk_parse_pubkey(), as it includes size minima for example.
+     */
+    if ((ret = mbedtls_rsa_complete(rsa)) != 0 ||
+        (ret = mbedtls_rsa_check_pubkey(rsa)) != 0) {
+        goto cleanup;
+    }
+
+    if (p != end) {
+        ret = MBEDTLS_ERR_ASN1_LENGTH_MISMATCH;
+    }
+
+cleanup:
+
+    mbedtls_mpi_free(&T);
+
+    if (ret != 0) {
+        mbedtls_rsa_free(rsa);
+    }
+
+    return ret;
+}
+
+int mbedtls_rsa_parse_pubkey(mbedtls_rsa_context *rsa, const unsigned char *key, size_t keylen)
+{
+    unsigned char *p = (unsigned char *) key;
+    unsigned char *end = (unsigned char *) (key + keylen);
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    size_t len;
+
+    /*
+     *  RSAPublicKey ::= SEQUENCE {
+     *      modulus           INTEGER,  -- n
+     *      publicExponent    INTEGER   -- e
+     *  }
+     */
+
+    if ((ret = mbedtls_asn1_get_tag(&p, end, &len,
+                                    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) {
+        return ret;
+    }
+
+    if (end != p + len) {
+        return MBEDTLS_ERR_RSA_BAD_INPUT_DATA;
+    }
+
+    /* Import N */
+    if ((ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_INTEGER)) != 0) {
+        return ret;
+    }
+
+    if ((ret = mbedtls_rsa_import_raw(rsa, p, len, NULL, 0, NULL, 0,
+                                      NULL, 0, NULL, 0)) != 0) {
+        return MBEDTLS_ERR_RSA_BAD_INPUT_DATA;
+    }
+
+    p += len;
+
+    /* Import E */
+    if ((ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_INTEGER)) != 0) {
+        return ret;
+    }
+
+    if ((ret = mbedtls_rsa_import_raw(rsa, NULL, 0, NULL, 0, NULL, 0,
+                                      NULL, 0, p, len)) != 0) {
+        return MBEDTLS_ERR_RSA_BAD_INPUT_DATA;
+    }
+
+    p += len;
+
+    if (mbedtls_rsa_complete(rsa) != 0 ||
+        mbedtls_rsa_check_pubkey(rsa) != 0) {
+        return MBEDTLS_ERR_RSA_BAD_INPUT_DATA;
+    }
+
+    if (p != end) {
+        return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH;
+    }
+
+    return 0;
+}
+
+int mbedtls_rsa_write_key(const mbedtls_rsa_context *rsa, unsigned char *start,
+                          unsigned char **p)
+{
+    size_t len = 0;
+    int ret;
+
+    mbedtls_mpi T; /* Temporary holding the exported parameters */
+
+    /*
+     * Export the parameters one after another to avoid simultaneous copies.
+     */
+
+    mbedtls_mpi_init(&T);
+
+    /* Export QP */
+    if ((ret = mbedtls_rsa_export_crt(rsa, NULL, NULL, &T)) != 0 ||
+        (ret = mbedtls_asn1_write_mpi(p, start, &T)) < 0) {
+        goto end_of_export;
+    }
+    len += ret;
+
+    /* Export DQ */
+    if ((ret = mbedtls_rsa_export_crt(rsa, NULL, &T, NULL)) != 0 ||
+        (ret = mbedtls_asn1_write_mpi(p, start, &T)) < 0) {
+        goto end_of_export;
+    }
+    len += ret;
+
+    /* Export DP */
+    if ((ret = mbedtls_rsa_export_crt(rsa, &T, NULL, NULL)) != 0 ||
+        (ret = mbedtls_asn1_write_mpi(p, start, &T)) < 0) {
+        goto end_of_export;
+    }
+    len += ret;
+
+    /* Export Q */
+    if ((ret = mbedtls_rsa_export(rsa, NULL, NULL, &T, NULL, NULL)) != 0 ||
+        (ret = mbedtls_asn1_write_mpi(p, start, &T)) < 0) {
+        goto end_of_export;
+    }
+    len += ret;
+
+    /* Export P */
+    if ((ret = mbedtls_rsa_export(rsa, NULL, &T, NULL, NULL, NULL)) != 0 ||
+        (ret = mbedtls_asn1_write_mpi(p, start, &T)) < 0) {
+        goto end_of_export;
+    }
+    len += ret;
+
+    /* Export D */
+    if ((ret = mbedtls_rsa_export(rsa, NULL, NULL, NULL, &T, NULL)) != 0 ||
+        (ret = mbedtls_asn1_write_mpi(p, start, &T)) < 0) {
+        goto end_of_export;
+    }
+    len += ret;
+
+    /* Export E */
+    if ((ret = mbedtls_rsa_export(rsa, NULL, NULL, NULL, NULL, &T)) != 0 ||
+        (ret = mbedtls_asn1_write_mpi(p, start, &T)) < 0) {
+        goto end_of_export;
+    }
+    len += ret;
+
+    /* Export N */
+    if ((ret = mbedtls_rsa_export(rsa, &T, NULL, NULL, NULL, NULL)) != 0 ||
+        (ret = mbedtls_asn1_write_mpi(p, start, &T)) < 0) {
+        goto end_of_export;
+    }
+    len += ret;
+
+end_of_export:
+
+    mbedtls_mpi_free(&T);
+    if (ret < 0) {
+        return ret;
+    }
+
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_int(p, start, 0));
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len));
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start,
+                                                     MBEDTLS_ASN1_CONSTRUCTED |
+                                                     MBEDTLS_ASN1_SEQUENCE));
+
+    return (int) len;
+}
+
+/*
+ *  RSAPublicKey ::= SEQUENCE {
+ *      modulus           INTEGER,  -- n
+ *      publicExponent    INTEGER   -- e
+ *  }
+ */
+int mbedtls_rsa_write_pubkey(const mbedtls_rsa_context *rsa, unsigned char *start,
+                             unsigned char **p)
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    size_t len = 0;
+    mbedtls_mpi T;
+
+    mbedtls_mpi_init(&T);
+
+    /* Export E */
+    if ((ret = mbedtls_rsa_export(rsa, NULL, NULL, NULL, NULL, &T)) != 0 ||
+        (ret = mbedtls_asn1_write_mpi(p, start, &T)) < 0) {
+        goto end_of_export;
+    }
+    len += ret;
+
+    /* Export N */
+    if ((ret = mbedtls_rsa_export(rsa, &T, NULL, NULL, NULL, NULL)) != 0 ||
+        (ret = mbedtls_asn1_write_mpi(p, start, &T)) < 0) {
+        goto end_of_export;
+    }
+    len += ret;
+
+end_of_export:
+
+    mbedtls_mpi_free(&T);
+    if (ret < 0) {
+        return ret;
+    }
+
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len));
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_CONSTRUCTED |
+                                                     MBEDTLS_ASN1_SEQUENCE));
+
+    return (int) len;
+}
 
 #if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT)
 
@@ -653,6 +1017,14 @@
 }
 
 /*
+ * Get length in bits of RSA modulus
+ */
+size_t mbedtls_rsa_get_bitlen(const mbedtls_rsa_context *ctx)
+{
+    return mbedtls_mpi_bitlen(&ctx->N);
+}
+
+/*
  * Get length in bytes of RSA modulus
  */
 size_t mbedtls_rsa_get_len(const mbedtls_rsa_context *ctx)
@@ -660,7 +1032,6 @@
     return ctx->len;
 }
 
-
 #if defined(MBEDTLS_GENPRIME)
 
 /*
diff --git a/library/rsa_internal.h b/library/rsa_internal.h
index 4081ac6..f79c3b7 100644
--- a/library/rsa_internal.h
+++ b/library/rsa_internal.h
@@ -15,6 +15,85 @@
 #define MBEDTLS_RSA_INTERNAL_H
 
 #include "mbedtls/rsa.h"
+#include "mbedtls/asn1.h"
+
+/**
+ * \brief           Parse a PKCS#1 (ASN.1) encoded private RSA key.
+ *
+ * \param rsa       The RSA context where parsed data will be stored.
+ * \param key       The buffer that contains the key.
+ * \param keylen    The length of the key buffer in bytes.
+ *
+ * \return          0 on success.
+ * \return          MBEDTLS_ERR_ASN1_xxx in case of ASN.1 parsing errors.
+ * \return          MBEDTLS_ERR_RSA_xxx in case of RSA internal failures while
+ *                  parsing data.
+ * \return          MBEDTLS_ERR_RSA_KEY_CHECK_FAILED if validity checks on the
+ *                  provided key fail.
+ */
+int mbedtls_rsa_parse_key(mbedtls_rsa_context *rsa, const unsigned char *key, size_t keylen);
+
+/**
+ * \brief           Parse a PKCS#1 (ASN.1) encoded public RSA key.
+ *
+ * \param rsa       The RSA context where parsed data will be stored.
+ * \param key       The buffer that contains the key.
+ * \param keylen    The length of the key buffer in bytes.
+ *
+ * \return          0 on success.
+ * \return          MBEDTLS_ERR_ASN1_xxx in case of ASN.1 parsing errors.
+ * \return          MBEDTLS_ERR_RSA_xxx in case of RSA internal failures while
+ *                  parsing data.
+ * \return          MBEDTLS_ERR_RSA_KEY_CHECK_FAILED if validity checks on the
+ *                  provided key fail.
+ */
+int mbedtls_rsa_parse_pubkey(mbedtls_rsa_context *rsa, const unsigned char *key, size_t keylen);
+
+/**
+ * \brief           Write a PKCS#1 (ASN.1) encoded private RSA key.
+ *
+ * \param rsa       The RSA context which contains the data to be written.
+ * \param start     Beginning of the buffer that will be filled with the
+ *                  private key.
+ * \param p         End of the buffer that will be filled with the private key.
+ *                  On successful return, the referenced pointer will be
+ *                  updated in order to point to the beginning of written data.
+ *
+ * \return          On success, the number of bytes written to the output buffer
+ *                  (i.e. a value > 0).
+ * \return          MBEDTLS_ERR_RSA_BAD_INPUT_DATA if the RSA context does not
+ *                  contain a valid key pair.
+ * \return          MBEDTLS_ERR_ASN1_xxx in case of failure while writing to the
+ *                  output buffer.
+ *
+ * \note            The output buffer is filled backward, i.e. starting from its
+ *                  end and moving toward its start.
+ */
+int mbedtls_rsa_write_key(const mbedtls_rsa_context *rsa, unsigned char *start,
+                          unsigned char **p);
+
+/**
+ * \brief           Parse a PKCS#1 (ASN.1) encoded public RSA key.
+ *
+ * \param rsa       The RSA context which contains the data to be written.
+ * \param start     Beginning of the buffer that will be filled with the
+ *                  private key.
+ * \param p         End of the buffer that will be filled with the private key.
+ *                  On successful return, the referenced pointer will be
+ *                  updated in order to point to the beginning of written data.
+ *
+ * \return          On success, the number of bytes written to the output buffer
+ *                  (i.e. a value > 0).
+ * \return          MBEDTLS_ERR_RSA_BAD_INPUT_DATA if the RSA context does not
+ *                  contain a valid public key.
+ * \return          MBEDTLS_ERR_ASN1_xxx in case of failure while writing to the
+ *                  output buffer.
+ *
+ * \note            The output buffer is filled backward, i.e. starting from its
+ *                  end and moving toward its start.
+ */
+int mbedtls_rsa_write_pubkey(const mbedtls_rsa_context *rsa, unsigned char *start,
+                             unsigned char **p);
 
 #if defined(MBEDTLS_PKCS1_V21)
 /**
diff --git a/library/sha3.c b/library/sha3.c
index d90fefa..5738559 100644
--- a/library/sha3.c
+++ b/library/sha3.c
@@ -14,6 +14,33 @@
 
 #if defined(MBEDTLS_SHA3_C)
 
+/*
+ * These macros select manually unrolled implementations of parts of the main permutation function.
+ *
+ * Unrolling has a major impact on both performance and code size. gcc performance benefits a lot
+ * from manually unrolling at higher optimisation levels.
+ *
+ * Depending on your size/perf priorities, compiler and target, it may be beneficial to adjust
+ * these; the defaults here should give sensible trade-offs for gcc and clang on aarch64 and
+ * x86-64.
+ */
+#if !defined(MBEDTLS_SHA3_THETA_UNROLL)
+    #define MBEDTLS_SHA3_THETA_UNROLL 0 //no-check-names
+#endif
+#if !defined(MBEDTLS_SHA3_CHI_UNROLL)
+    #if defined(__OPTIMIZE_SIZE__)
+        #define MBEDTLS_SHA3_CHI_UNROLL 0 //no-check-names
+    #else
+        #define MBEDTLS_SHA3_CHI_UNROLL 1 //no-check-names
+    #endif
+#endif
+#if !defined(MBEDTLS_SHA3_PI_UNROLL)
+    #define MBEDTLS_SHA3_PI_UNROLL 1 //no-check-names
+#endif
+#if !defined(MBEDTLS_SHA3_RHO_UNROLL)
+    #define MBEDTLS_SHA3_RHO_UNROLL 1 //no-check-names
+#endif
+
 #include "mbedtls/sha3.h"
 #include "mbedtls/platform_util.h"
 #include "mbedtls/error.h"
@@ -26,46 +53,45 @@
 
 #define XOR_BYTE 0x6
 
-typedef struct mbedtls_sha3_family_functions {
-    mbedtls_sha3_id id;
-
-    uint16_t r;
-    uint16_t olen;
-}
-mbedtls_sha3_family_functions;
-
-/*
- * List of supported SHA-3 families
+/* Precomputed masks for the iota transform.
+ *
+ * Each round uses a 64-bit mask value. In each mask values, only
+ * bits whose position is of the form 2^k-1 can be set, thus only
+ * 7 of 64 bits of the mask need to be known for each mask value.
+ *
+ * We use a compressed encoding of the mask where bits 63, 31 and 15
+ * are moved to bits 4-6. This allows us to make each mask value
+ * 1 byte rather than 8 bytes, saving 7*24 = 168 bytes of data (with
+ * perhaps a little variation due to alignment). Decompressing this
+ * requires a little code, but much less than the savings on the table.
+ *
+ * The impact on performance depends on the platform and compiler.
+ * There's a bit more computation, but less memory bandwidth. A quick
+ * benchmark on x86_64 shows a 7% speed improvement with GCC and a
+ * 5% speed penalty with Clang, compared to the naive uint64_t[24] table.
+ * YMMV.
  */
-static mbedtls_sha3_family_functions sha3_families[] = {
-    { MBEDTLS_SHA3_224,      1152, 224 },
-    { MBEDTLS_SHA3_256,      1088, 256 },
-    { MBEDTLS_SHA3_384,       832, 384 },
-    { MBEDTLS_SHA3_512,       576, 512 },
-    { MBEDTLS_SHA3_NONE, 0, 0 }
+/* Helper macro to set the values of the higher bits in unused low positions */
+#define H(b63, b31, b15) (b63 << 6 | b31 << 5 | b15 << 4)
+static const uint8_t iota_r_packed[24] = {
+    H(0, 0, 0) | 0x01, H(0, 0, 1) | 0x82, H(1, 0, 1) | 0x8a, H(1, 1, 1) | 0x00,
+    H(0, 0, 1) | 0x8b, H(0, 1, 0) | 0x01, H(1, 1, 1) | 0x81, H(1, 0, 1) | 0x09,
+    H(0, 0, 0) | 0x8a, H(0, 0, 0) | 0x88, H(0, 1, 1) | 0x09, H(0, 1, 0) | 0x0a,
+    H(0, 1, 1) | 0x8b, H(1, 0, 0) | 0x8b, H(1, 0, 1) | 0x89, H(1, 0, 1) | 0x03,
+    H(1, 0, 1) | 0x02, H(1, 0, 0) | 0x80, H(0, 0, 1) | 0x0a, H(1, 1, 0) | 0x0a,
+    H(1, 1, 1) | 0x81, H(1, 0, 1) | 0x80, H(0, 1, 0) | 0x01, H(1, 1, 1) | 0x08,
+};
+#undef H
+
+static const uint32_t rho[6] = {
+    0x3f022425, 0x1c143a09, 0x2c3d3615, 0x27191713, 0x312b382e, 0x3e030832
 };
 
-static const uint64_t rc[24] = {
-    0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
-    0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
-    0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
-    0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
-    0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
-    0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
+static const uint32_t pi[6] = {
+    0x110b070a, 0x10050312, 0x04181508, 0x0d13170f, 0x0e14020c, 0x01060916
 };
 
-static const uint8_t rho[24] = {
-    1, 62, 28, 27, 36, 44,  6, 55, 20,
-    3, 10, 43, 25, 39, 41, 45, 15,
-    21,  8, 18,  2, 61, 56, 14
-};
-
-static const uint8_t pi[24] = {
-    10,  7, 11, 17, 18, 3,  5, 16,  8, 21, 24, 4,
-    15, 23, 19, 13, 12, 2, 20, 14, 22,  9,  6, 1,
-};
-
-#define ROT64(x, y) (((x) << (y)) | ((x) >> (64U - (y))))
+#define ROTR64(x, y) (((x) << (64U - (y))) | ((x) >> (y))) // 64-bit rotate right
 #define ABSORB(ctx, idx, v) do { ctx->state[(idx) >> 3] ^= ((uint64_t) (v)) << (((idx) & 0x7) << 3); \
 } while (0)
 #define SQUEEZE(ctx, idx) ((uint8_t) (ctx->state[(idx) >> 3] >> (((idx) & 0x7) << 3)))
@@ -82,39 +108,97 @@
         uint64_t t;
 
         /* Theta */
+#if MBEDTLS_SHA3_THETA_UNROLL == 0 //no-check-names
+        for (i = 0; i < 5; i++) {
+            lane[i] = s[i] ^ s[i + 5] ^ s[i + 10] ^ s[i + 15] ^ s[i + 20];
+        }
+        for (i = 0; i < 5; i++) {
+            t = lane[(i + 4) % 5] ^ ROTR64(lane[(i + 1) % 5], 63);
+            s[i] ^= t; s[i + 5] ^= t; s[i + 10] ^= t; s[i + 15] ^= t; s[i + 20] ^= t;
+        }
+#else
         lane[0] = s[0] ^ s[5] ^ s[10] ^ s[15] ^ s[20];
         lane[1] = s[1] ^ s[6] ^ s[11] ^ s[16] ^ s[21];
         lane[2] = s[2] ^ s[7] ^ s[12] ^ s[17] ^ s[22];
         lane[3] = s[3] ^ s[8] ^ s[13] ^ s[18] ^ s[23];
         lane[4] = s[4] ^ s[9] ^ s[14] ^ s[19] ^ s[24];
 
-        t = lane[4] ^ ROT64(lane[1], 1);
+        t = lane[4] ^ ROTR64(lane[1], 63);
         s[0] ^= t; s[5] ^= t; s[10] ^= t; s[15] ^= t; s[20] ^= t;
 
-        t = lane[0] ^ ROT64(lane[2], 1);
+        t = lane[0] ^ ROTR64(lane[2], 63);
         s[1] ^= t; s[6] ^= t; s[11] ^= t; s[16] ^= t; s[21] ^= t;
 
-        t = lane[1] ^ ROT64(lane[3], 1);
+        t = lane[1] ^ ROTR64(lane[3], 63);
         s[2] ^= t; s[7] ^= t; s[12] ^= t; s[17] ^= t; s[22] ^= t;
 
-        t = lane[2] ^ ROT64(lane[4], 1);
+        t = lane[2] ^ ROTR64(lane[4], 63);
         s[3] ^= t; s[8] ^= t; s[13] ^= t; s[18] ^= t; s[23] ^= t;
 
-        t = lane[3] ^ ROT64(lane[0], 1);
+        t = lane[3] ^ ROTR64(lane[0], 63);
         s[4] ^= t; s[9] ^= t; s[14] ^= t; s[19] ^= t; s[24] ^= t;
+#endif
 
         /* Rho */
-        for (i = 1; i < 25; i++) {
-            s[i] = ROT64(s[i], rho[i-1]);
+        for (i = 1; i < 25; i += 4) {
+            uint32_t r = rho[(i - 1) >> 2];
+#if MBEDTLS_SHA3_RHO_UNROLL == 0
+            for (int j = i; j < i + 4; j++) {
+                uint8_t r8 = (uint8_t) (r >> 24);
+                r <<= 8;
+                s[j] = ROTR64(s[j], r8);
+            }
+#else
+            s[i + 0] = ROTR64(s[i + 0], MBEDTLS_BYTE_3(r));
+            s[i + 1] = ROTR64(s[i + 1], MBEDTLS_BYTE_2(r));
+            s[i + 2] = ROTR64(s[i + 2], MBEDTLS_BYTE_1(r));
+            s[i + 3] = ROTR64(s[i + 3], MBEDTLS_BYTE_0(r));
+#endif
         }
 
         /* Pi */
         t = s[1];
-        for (i = 0; i < 24; i++) {
-            SWAP(s[pi[i]], t);
+#if MBEDTLS_SHA3_PI_UNROLL == 0
+        for (i = 0; i < 24; i += 4) {
+            uint32_t p = pi[i >> 2];
+            for (unsigned j = 0; j < 4; j++) {
+                SWAP(s[p & 0xff], t);
+                p >>= 8;
+            }
         }
+#else
+        uint32_t p = pi[0];
+        SWAP(s[MBEDTLS_BYTE_0(p)], t); SWAP(s[MBEDTLS_BYTE_1(p)], t);
+        SWAP(s[MBEDTLS_BYTE_2(p)], t); SWAP(s[MBEDTLS_BYTE_3(p)], t);
+        p = pi[1];
+        SWAP(s[MBEDTLS_BYTE_0(p)], t); SWAP(s[MBEDTLS_BYTE_1(p)], t);
+        SWAP(s[MBEDTLS_BYTE_2(p)], t); SWAP(s[MBEDTLS_BYTE_3(p)], t);
+        p = pi[2];
+        SWAP(s[MBEDTLS_BYTE_0(p)], t); SWAP(s[MBEDTLS_BYTE_1(p)], t);
+        SWAP(s[MBEDTLS_BYTE_2(p)], t); SWAP(s[MBEDTLS_BYTE_3(p)], t);
+        p = pi[3];
+        SWAP(s[MBEDTLS_BYTE_0(p)], t); SWAP(s[MBEDTLS_BYTE_1(p)], t);
+        SWAP(s[MBEDTLS_BYTE_2(p)], t); SWAP(s[MBEDTLS_BYTE_3(p)], t);
+        p = pi[4];
+        SWAP(s[MBEDTLS_BYTE_0(p)], t); SWAP(s[MBEDTLS_BYTE_1(p)], t);
+        SWAP(s[MBEDTLS_BYTE_2(p)], t); SWAP(s[MBEDTLS_BYTE_3(p)], t);
+        p = pi[5];
+        SWAP(s[MBEDTLS_BYTE_0(p)], t); SWAP(s[MBEDTLS_BYTE_1(p)], t);
+        SWAP(s[MBEDTLS_BYTE_2(p)], t); SWAP(s[MBEDTLS_BYTE_3(p)], t);
+#endif
 
         /* Chi */
+#if MBEDTLS_SHA3_CHI_UNROLL == 0 //no-check-names
+        for (i = 0; i <= 20; i += 5) {
+            lane[0] = s[i]; lane[1] = s[i + 1]; lane[2] = s[i + 2];
+            lane[3] = s[i + 3]; lane[4] = s[i + 4];
+            s[i + 0] ^= (~lane[1]) & lane[2];
+            s[i + 1] ^= (~lane[2]) & lane[3];
+            s[i + 2] ^= (~lane[3]) & lane[4];
+            s[i + 3] ^= (~lane[4]) & lane[0];
+            s[i + 4] ^= (~lane[0]) & lane[1];
+        }
+#else
         lane[0] = s[0]; lane[1] = s[1]; lane[2] = s[2]; lane[3] = s[3]; lane[4] = s[4];
         s[0] ^= (~lane[1]) & lane[2];
         s[1] ^= (~lane[2]) & lane[3];
@@ -149,9 +233,14 @@
         s[22] ^= (~lane[3]) & lane[4];
         s[23] ^= (~lane[4]) & lane[0];
         s[24] ^= (~lane[0]) & lane[1];
+#endif
 
         /* Iota */
-        s[0] ^= rc[round];
+        /* Decompress the round masks (see definition of rc) */
+        s[0] ^= ((iota_r_packed[round] & 0x40ull) << 57 |
+                 (iota_r_packed[round] & 0x20ull) << 26 |
+                 (iota_r_packed[round] & 0x10ull) << 11 |
+                 (iota_r_packed[round] & 0x8f));
     }
 }
 
@@ -180,21 +269,27 @@
  */
 int mbedtls_sha3_starts(mbedtls_sha3_context *ctx, mbedtls_sha3_id id)
 {
-    mbedtls_sha3_family_functions *p = NULL;
-
-    for (p = sha3_families; p->id != MBEDTLS_SHA3_NONE; p++) {
-        if (p->id == id) {
+    switch (id) {
+        case MBEDTLS_SHA3_224:
+            ctx->olen = 224 / 8;
+            ctx->max_block_size = 1152 / 8;
             break;
-        }
+        case MBEDTLS_SHA3_256:
+            ctx->olen = 256 / 8;
+            ctx->max_block_size = 1088 / 8;
+            break;
+        case MBEDTLS_SHA3_384:
+            ctx->olen = 384 / 8;
+            ctx->max_block_size = 832 / 8;
+            break;
+        case MBEDTLS_SHA3_512:
+            ctx->olen = 512 / 8;
+            ctx->max_block_size = 576 / 8;
+            break;
+        default:
+            return MBEDTLS_ERR_SHA3_BAD_INPUT_DATA;
     }
 
-    if (p->id == MBEDTLS_SHA3_NONE) {
-        return MBEDTLS_ERR_SHA3_BAD_INPUT_DATA;
-    }
-
-    ctx->olen = p->olen / 8;
-    ctx->max_block_size = p->r / 8;
-
     memset(ctx->state, 0, sizeof(ctx->state));
     ctx->index = 0;
 
diff --git a/library/sha512.c b/library/sha512.c
index 6011254..6dcea8d 100644
--- a/library/sha512.c
+++ b/library/sha512.c
@@ -102,6 +102,14 @@
 #      if defined(__linux__)
 /* Our preferred method of detection is getauxval() */
 #        include <sys/auxv.h>
+#        if !defined(HWCAP_SHA512)
+/* The same header that declares getauxval() should provide the HWCAP_xxx
+ * constants to analyze its return value. However, the libc may be too
+ * old to have the constant that we need. So if it's missing, assume that
+ * the value is the same one used by the Linux kernel ABI.
+ */
+#          define HWCAP_SHA512 (1 << 21)
+#        endif
 #      endif
 /* Use SIGILL on Unix, and fall back to it on Linux */
 #      include <signal.h>
diff --git a/library/ssl_ciphersuites_internal.h b/library/ssl_ciphersuites_internal.h
new file mode 100644
index 0000000..27ff721
--- /dev/null
+++ b/library/ssl_ciphersuites_internal.h
@@ -0,0 +1,154 @@
+/**
+ * \file ssl_ciphersuites_internal.h
+ *
+ * \brief Internal part of the public "ssl_ciphersuites.h".
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+#ifndef MBEDTLS_SSL_CIPHERSUITES_INTERNAL_H
+#define MBEDTLS_SSL_CIPHERSUITES_INTERNAL_H
+
+#include "mbedtls/pk.h"
+
+#if defined(MBEDTLS_PK_C)
+mbedtls_pk_type_t mbedtls_ssl_get_ciphersuite_sig_pk_alg(const mbedtls_ssl_ciphersuite_t *info);
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+psa_algorithm_t mbedtls_ssl_get_ciphersuite_sig_pk_psa_alg(const mbedtls_ssl_ciphersuite_t *info);
+psa_key_usage_t mbedtls_ssl_get_ciphersuite_sig_pk_psa_usage(const mbedtls_ssl_ciphersuite_t *info);
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+mbedtls_pk_type_t mbedtls_ssl_get_ciphersuite_sig_alg(const mbedtls_ssl_ciphersuite_t *info);
+#endif /* MBEDTLS_PK_C */
+
+int mbedtls_ssl_ciphersuite_uses_ec(const mbedtls_ssl_ciphersuite_t *info);
+int mbedtls_ssl_ciphersuite_uses_psk(const mbedtls_ssl_ciphersuite_t *info);
+
+#if defined(MBEDTLS_KEY_EXCHANGE_SOME_PFS_ENABLED)
+static inline int mbedtls_ssl_ciphersuite_has_pfs(const mbedtls_ssl_ciphersuite_t *info)
+{
+    switch (info->MBEDTLS_PRIVATE(key_exchange)) {
+        case MBEDTLS_KEY_EXCHANGE_DHE_RSA:
+        case MBEDTLS_KEY_EXCHANGE_DHE_PSK:
+        case MBEDTLS_KEY_EXCHANGE_ECDHE_RSA:
+        case MBEDTLS_KEY_EXCHANGE_ECDHE_PSK:
+        case MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA:
+        case MBEDTLS_KEY_EXCHANGE_ECJPAKE:
+            return 1;
+
+        default:
+            return 0;
+    }
+}
+#endif /* MBEDTLS_KEY_EXCHANGE_SOME_PFS_ENABLED */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_SOME_NON_PFS_ENABLED)
+static inline int mbedtls_ssl_ciphersuite_no_pfs(const mbedtls_ssl_ciphersuite_t *info)
+{
+    switch (info->MBEDTLS_PRIVATE(key_exchange)) {
+        case MBEDTLS_KEY_EXCHANGE_ECDH_RSA:
+        case MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA:
+        case MBEDTLS_KEY_EXCHANGE_RSA:
+        case MBEDTLS_KEY_EXCHANGE_PSK:
+        case MBEDTLS_KEY_EXCHANGE_RSA_PSK:
+            return 1;
+
+        default:
+            return 0;
+    }
+}
+#endif /* MBEDTLS_KEY_EXCHANGE_SOME_NON_PFS_ENABLED */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_SOME_ECDH_ENABLED)
+static inline int mbedtls_ssl_ciphersuite_uses_ecdh(const mbedtls_ssl_ciphersuite_t *info)
+{
+    switch (info->MBEDTLS_PRIVATE(key_exchange)) {
+        case MBEDTLS_KEY_EXCHANGE_ECDH_RSA:
+        case MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA:
+            return 1;
+
+        default:
+            return 0;
+    }
+}
+#endif /* MBEDTLS_KEY_EXCHANGE_SOME_ECDH_ENABLED */
+
+static inline int mbedtls_ssl_ciphersuite_cert_req_allowed(const mbedtls_ssl_ciphersuite_t *info)
+{
+    switch (info->MBEDTLS_PRIVATE(key_exchange)) {
+        case MBEDTLS_KEY_EXCHANGE_RSA:
+        case MBEDTLS_KEY_EXCHANGE_DHE_RSA:
+        case MBEDTLS_KEY_EXCHANGE_ECDH_RSA:
+        case MBEDTLS_KEY_EXCHANGE_ECDHE_RSA:
+        case MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA:
+        case MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA:
+            return 1;
+
+        default:
+            return 0;
+    }
+}
+
+static inline int mbedtls_ssl_ciphersuite_uses_srv_cert(const mbedtls_ssl_ciphersuite_t *info)
+{
+    switch (info->MBEDTLS_PRIVATE(key_exchange)) {
+        case MBEDTLS_KEY_EXCHANGE_RSA:
+        case MBEDTLS_KEY_EXCHANGE_RSA_PSK:
+        case MBEDTLS_KEY_EXCHANGE_DHE_RSA:
+        case MBEDTLS_KEY_EXCHANGE_ECDH_RSA:
+        case MBEDTLS_KEY_EXCHANGE_ECDHE_RSA:
+        case MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA:
+        case MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA:
+            return 1;
+
+        default:
+            return 0;
+    }
+}
+
+#if defined(MBEDTLS_KEY_EXCHANGE_SOME_DHE_ENABLED)
+static inline int mbedtls_ssl_ciphersuite_uses_dhe(const mbedtls_ssl_ciphersuite_t *info)
+{
+    switch (info->MBEDTLS_PRIVATE(key_exchange)) {
+        case MBEDTLS_KEY_EXCHANGE_DHE_RSA:
+        case MBEDTLS_KEY_EXCHANGE_DHE_PSK:
+            return 1;
+
+        default:
+            return 0;
+    }
+}
+#endif /* MBEDTLS_KEY_EXCHANGE_SOME_DHE_ENABLED) */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_SOME_ECDHE_ENABLED)
+static inline int mbedtls_ssl_ciphersuite_uses_ecdhe(const mbedtls_ssl_ciphersuite_t *info)
+{
+    switch (info->MBEDTLS_PRIVATE(key_exchange)) {
+        case MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA:
+        case MBEDTLS_KEY_EXCHANGE_ECDHE_RSA:
+        case MBEDTLS_KEY_EXCHANGE_ECDHE_PSK:
+            return 1;
+
+        default:
+            return 0;
+    }
+}
+#endif /* MBEDTLS_KEY_EXCHANGE_SOME_ECDHE_ENABLED) */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED)
+static inline int mbedtls_ssl_ciphersuite_uses_server_signature(
+    const mbedtls_ssl_ciphersuite_t *info)
+{
+    switch (info->MBEDTLS_PRIVATE(key_exchange)) {
+        case MBEDTLS_KEY_EXCHANGE_DHE_RSA:
+        case MBEDTLS_KEY_EXCHANGE_ECDHE_RSA:
+        case MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA:
+            return 1;
+
+        default:
+            return 0;
+    }
+}
+#endif /* MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED */
+
+#endif /* MBEDTLS_SSL_CIPHERSUITES_INTERNAL_H */
diff --git a/library/ssl_client.c b/library/ssl_client.c
index d585ca5..8892acf 100644
--- a/library/ssl_client.c
+++ b/library/ssl_client.c
@@ -12,7 +12,7 @@
 
 #include <string.h>
 
-#include "mbedtls/debug.h"
+#include "debug_internal.h"
 #include "mbedtls/error.h"
 #include "mbedtls/platform.h"
 
@@ -765,11 +765,6 @@
           MBEDTLS_SSL_SESSION_TICKETS &&
           MBEDTLS_HAVE_TIME */
 
-    if (ssl->conf->f_rng == NULL) {
-        MBEDTLS_SSL_DEBUG_MSG(1, ("no RNG provided"));
-        return MBEDTLS_ERR_SSL_NO_RNG;
-    }
-
     /* Bet on the highest configured version if we are not in a TLS 1.2
      * renegotiation or session resumption.
      */
diff --git a/library/ssl_debug_helpers.h b/library/ssl_debug_helpers.h
index 2b0e737..a8e3140 100644
--- a/library/ssl_debug_helpers.h
+++ b/library/ssl_debug_helpers.h
@@ -21,6 +21,10 @@
 
 const char *mbedtls_ssl_states_str(mbedtls_ssl_states in);
 
+#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_CLI_C)
+const char *mbedtls_ssl_early_data_status_str(mbedtls_ssl_early_data_status in);
+#endif
+
 const char *mbedtls_ssl_protocol_version_str(mbedtls_ssl_protocol_version in);
 
 const char *mbedtls_tls_prf_types_str(mbedtls_tls_prf_types in);
diff --git a/library/ssl_misc.h b/library/ssl_misc.h
index 96afe76..883b988 100644
--- a/library/ssl_misc.h
+++ b/library/ssl_misc.h
@@ -44,6 +44,8 @@
 #endif
 
 #include "mbedtls/pk.h"
+#include "ssl_ciphersuites_internal.h"
+#include "x509_internal.h"
 #include "pk_internal.h"
 #include "common.h"
 
@@ -650,6 +652,10 @@
     /* Flag indicating if a CertificateRequest message has been sent
      * to the client or not. */
     uint8_t certificate_request_sent;
+#if defined(MBEDTLS_SSL_EARLY_DATA)
+    /* Flag indicating if the server has accepted early data or not. */
+    uint8_t early_data_accepted;
+#endif
 #endif /* MBEDTLS_SSL_SRV_C */
 
 #if defined(MBEDTLS_SSL_SESSION_TICKETS)
@@ -659,21 +665,21 @@
 #if defined(MBEDTLS_SSL_CLI_C)
     /** Minimum TLS version to be negotiated.
      *
-     *  It is set up in the ClientHello writing preparation stage and used
-     *  throughout the ClientHello writing. Not relevant anymore as soon as
-     *  the protocol version has been negotiated thus as soon as the
-     *  ServerHello is received.
-     *  For a fresh handshake not linked to any previous handshake, it is
-     *  equal to the configured minimum minor version to be negotiated. When
-     *  renegotiating or resuming a session, it is equal to the previously
-     *  negotiated minor version.
+     * It is set up in the ClientHello writing preparation stage and used
+     * throughout the ClientHello writing. Not relevant anymore as soon as
+     * the protocol version has been negotiated thus as soon as the
+     * ServerHello is received.
+     * For a fresh handshake not linked to any previous handshake, it is
+     * equal to the configured minimum minor version to be negotiated. When
+     * renegotiating or resuming a session, it is equal to the previously
+     * negotiated minor version.
      *
-     *  There is no maximum TLS version field in this handshake context.
-     *  From the start of the handshake, we need to define a current protocol
-     *  version for the record layer which we define as the maximum TLS
-     *  version to be negotiated. The `tls_version` field of the SSL context is
-     *  used to store this maximum value until it contains the actual
-     *  negotiated value.
+     * There is no maximum TLS version field in this handshake context.
+     * From the start of the handshake, we need to define a current protocol
+     * version for the record layer which we define as the maximum TLS
+     * version to be negotiated. The `tls_version` field of the SSL context is
+     * used to store this maximum value until it contains the actual
+     * negotiated value.
      */
     mbedtls_ssl_protocol_version min_tls_version;
 #endif
@@ -724,15 +730,29 @@
 #if defined(MBEDTLS_SSL_PROTO_TLS1_3)
     uint8_t key_exchange_mode; /*!< Selected key exchange mode */
 
-    /** Number of HelloRetryRequest messages received/sent from/to the server. */
-    int hello_retry_request_count;
+    /**
+     * Flag indicating if, in the course of the current handshake, an
+     * HelloRetryRequest message has been sent by the server or received by
+     * the client (<> 0) or not (0).
+     */
+    uint8_t hello_retry_request_flag;
+
+#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE)
+    /**
+     * Flag indicating if, in the course of the current handshake, a dummy
+     * change_cipher_spec (CCS) record has already been sent. Used to send only
+     * one CCS per handshake while not complicating the handshake state
+     * transitions for that purpose.
+     */
+    uint8_t ccs_sent;
+#endif
 
 #if defined(MBEDTLS_SSL_SRV_C)
-    /** selected_group of key_share extension in HelloRetryRequest message. */
-    uint16_t hrr_selected_group;
 #if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED)
     uint8_t tls13_kex_modes; /*!< Key exchange modes supported by the client */
 #endif
+    /** selected_group of key_share extension in HelloRetryRequest message. */
+    uint16_t hrr_selected_group;
 #if defined(MBEDTLS_SSL_SESSION_TICKETS)
     uint16_t new_session_tickets_count;         /*!< number of session tickets */
 #endif
@@ -2131,11 +2151,8 @@
                                            const unsigned char *end,
                                            size_t *out_len);
 
-#if defined(MBEDTLS_SSL_SRV_C)
-#define MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_RECEIVED \
-    MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT
-#endif /* MBEDTLS_SSL_SRV_C */
-
+int mbedtls_ssl_tls13_check_early_data_len(mbedtls_ssl_context *ssl,
+                                           size_t early_data_len);
 #endif /* MBEDTLS_SSL_EARLY_DATA */
 
 #endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
diff --git a/library/ssl_msg.c b/library/ssl_msg.c
index 6579c96..0c71157 100644
--- a/library/ssl_msg.c
+++ b/library/ssl_msg.c
@@ -18,7 +18,7 @@
 
 #include "mbedtls/ssl.h"
 #include "ssl_misc.h"
-#include "mbedtls/debug.h"
+#include "debug_internal.h"
 #include "mbedtls/error.h"
 #include "mbedtls/platform_util.h"
 #include "mbedtls/version.h"
@@ -3985,6 +3985,35 @@
                                            rec)) != 0) {
             MBEDTLS_SSL_DEBUG_RET(1, "ssl_decrypt_buf", ret);
 
+#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_SRV_C)
+            /*
+             * Although the server rejected early data, it might receive early
+             * data as long as it has not received the client Finished message.
+             * It is encrypted with early keys and should be ignored as stated
+             * in section 4.2.10 of RFC 8446:
+             *
+             * "Ignore the extension and return a regular 1-RTT response. The
+             * server then skips past early data by attempting to deprotect
+             * received records using the handshake traffic key, discarding
+             * records which fail deprotection (up to the configured
+             * max_early_data_size). Once a record is deprotected successfully,
+             * it is treated as the start of the client's second flight and the
+             * server proceeds as with an ordinary 1-RTT handshake."
+             */
+            if ((old_msg_type == MBEDTLS_SSL_MSG_APPLICATION_DATA) &&
+                (ssl->discard_early_data_record ==
+                 MBEDTLS_SSL_EARLY_DATA_TRY_TO_DEPROTECT_AND_DISCARD)) {
+                MBEDTLS_SSL_DEBUG_MSG(
+                    3, ("EarlyData: deprotect and discard app data records."));
+
+                ret = mbedtls_ssl_tls13_check_early_data_len(ssl, rec->data_len);
+                if (ret != 0) {
+                    return ret;
+                }
+                ret = MBEDTLS_ERR_SSL_CONTINUE_PROCESSING;
+            }
+#endif /* MBEDTLS_SSL_EARLY_DATA && MBEDTLS_SSL_SRV_C */
+
 #if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID)
             if (ret == MBEDTLS_ERR_SSL_UNEXPECTED_CID &&
                 ssl->conf->ignore_unexpected_cid
@@ -3994,9 +4023,27 @@
             }
 #endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */
 
+            /*
+             * The decryption of the record failed, no reason to ignore it,
+             * return in error with the decryption error code.
+             */
             return ret;
         }
 
+#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_SRV_C)
+        /*
+         * If the server were discarding protected records that it fails to
+         * deprotect because it has rejected early data, as we have just
+         * deprotected successfully a record, the server has to resume normal
+         * operation and fail the connection if the deprotection of a record
+         * fails.
+         */
+        if (ssl->discard_early_data_record ==
+            MBEDTLS_SSL_EARLY_DATA_TRY_TO_DEPROTECT_AND_DISCARD) {
+            ssl->discard_early_data_record = MBEDTLS_SSL_EARLY_DATA_NO_DISCARD;
+        }
+#endif /* MBEDTLS_SSL_EARLY_DATA && MBEDTLS_SSL_SRV_C */
+
         if (old_msg_type != rec->type) {
             MBEDTLS_SSL_DEBUG_MSG(4, ("record type after decrypt (before %d): %d",
                                       old_msg_type, rec->type));
@@ -4070,6 +4117,38 @@
 
     }
 
+#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_SRV_C)
+    /*
+     * Although the server rejected early data because it needed to send an
+     * HelloRetryRequest message, it might receive early data as long as it has
+     * not received the client Finished message.
+     * The early data is encrypted with early keys and should be ignored as
+     * stated in section 4.2.10 of RFC 8446 (second case):
+     *
+     * "The server then ignores early data by skipping all records with an
+     * external content type of "application_data" (indicating that they are
+     * encrypted), up to the configured max_early_data_size. Ignore application
+     * data message before 2nd ClientHello when early_data was received in 1st
+     * ClientHello."
+     */
+    if (ssl->discard_early_data_record == MBEDTLS_SSL_EARLY_DATA_DISCARD) {
+        if (rec->type == MBEDTLS_SSL_MSG_APPLICATION_DATA) {
+
+            ret = mbedtls_ssl_tls13_check_early_data_len(ssl, rec->data_len);
+            if (ret != 0) {
+                return ret;
+            }
+
+            MBEDTLS_SSL_DEBUG_MSG(
+                3, ("EarlyData: Ignore application message before 2nd ClientHello"));
+
+            return MBEDTLS_ERR_SSL_CONTINUE_PROCESSING;
+        } else if (rec->type == MBEDTLS_SSL_MSG_HANDSHAKE) {
+            ssl->discard_early_data_record = MBEDTLS_SSL_EARLY_DATA_NO_DISCARD;
+        }
+    }
+#endif /* MBEDTLS_SSL_EARLY_DATA && MBEDTLS_SSL_SRV_C */
+
 #if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY)
     if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) {
         mbedtls_ssl_dtls_replay_update(ssl);
@@ -5648,12 +5727,53 @@
 }
 
 /*
+ * brief          Read at most 'len' application data bytes from the input
+ *                buffer.
+ *
+ * param ssl      SSL context:
+ *                - First byte of application data not read yet in the input
+ *                  buffer located at address `in_offt`.
+ *                - The number of bytes of data not read yet is `in_msglen`.
+ * param buf      buffer that will hold the data
+ * param len      maximum number of bytes to read
+ *
+ * note           The function updates the fields `in_offt` and `in_msglen`
+ *                according to the number of bytes read.
+ *
+ * return         The number of bytes read.
+ */
+static int ssl_read_application_data(
+    mbedtls_ssl_context *ssl, unsigned char *buf, size_t len)
+{
+    size_t n = (len < ssl->in_msglen) ? len : ssl->in_msglen;
+
+    if (len != 0) {
+        memcpy(buf, ssl->in_offt, n);
+        ssl->in_msglen -= n;
+    }
+
+    /* Zeroising the plaintext buffer to erase unused application data
+       from the memory. */
+    mbedtls_platform_zeroize(ssl->in_offt, n);
+
+    if (ssl->in_msglen == 0) {
+        /* all bytes consumed */
+        ssl->in_offt = NULL;
+        ssl->keep_current_message = 0;
+    } else {
+        /* more data available */
+        ssl->in_offt += n;
+    }
+
+    return (int) n;
+}
+
+/*
  * Receive application data decrypted from the SSL layer
  */
 int mbedtls_ssl_read(mbedtls_ssl_context *ssl, unsigned char *buf, size_t len)
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    size_t n;
 
     if (ssl == NULL || ssl->conf == NULL) {
         return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
@@ -5817,32 +5937,34 @@
 #endif /* MBEDTLS_SSL_PROTO_DTLS */
     }
 
-    n = (len < ssl->in_msglen)
-        ? len : ssl->in_msglen;
-
-    if (len != 0) {
-        memcpy(buf, ssl->in_offt, n);
-        ssl->in_msglen -= n;
-    }
-
-    /* Zeroising the plaintext buffer to erase unused application data
-       from the memory. */
-    mbedtls_platform_zeroize(ssl->in_offt, n);
-
-    if (ssl->in_msglen == 0) {
-        /* all bytes consumed */
-        ssl->in_offt = NULL;
-        ssl->keep_current_message = 0;
-    } else {
-        /* more data available */
-        ssl->in_offt += n;
-    }
+    ret = ssl_read_application_data(ssl, buf, len);
 
     MBEDTLS_SSL_DEBUG_MSG(2, ("<= read"));
 
-    return (int) n;
+    return ret;
 }
 
+#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_EARLY_DATA)
+int mbedtls_ssl_read_early_data(mbedtls_ssl_context *ssl,
+                                unsigned char *buf, size_t len)
+{
+    if (ssl == NULL || (ssl->conf == NULL)) {
+        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+    }
+
+    /*
+     * The server may receive early data only while waiting for the End of
+     * Early Data handshake message.
+     */
+    if ((ssl->state != MBEDTLS_SSL_END_OF_EARLY_DATA) ||
+        (ssl->in_offt == NULL)) {
+        return MBEDTLS_ERR_SSL_CANNOT_READ_EARLY_DATA;
+    }
+
+    return ssl_read_application_data(ssl, buf, len);
+}
+#endif /* MBEDTLS_SSL_SRV_C && MBEDTLS_SSL_EARLY_DATA */
+
 /*
  * Send application data to be encrypted by the SSL layer, taking care of max
  * fragment length and buffer size.
@@ -5946,6 +6068,94 @@
     return ret;
 }
 
+#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_CLI_C)
+int mbedtls_ssl_write_early_data(mbedtls_ssl_context *ssl,
+                                 const unsigned char *buf, size_t len)
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    const struct mbedtls_ssl_config *conf;
+    int written_data_len = 0;
+
+    MBEDTLS_SSL_DEBUG_MSG(2, ("=> write early_data"));
+
+    if (ssl == NULL || (conf = ssl->conf) == NULL) {
+        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+    }
+
+    if (conf->endpoint != MBEDTLS_SSL_IS_CLIENT) {
+        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+    }
+
+    if ((!mbedtls_ssl_conf_is_tls13_enabled(conf)) ||
+        (conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) ||
+        (conf->early_data_enabled != MBEDTLS_SSL_EARLY_DATA_ENABLED)) {
+        return MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA;
+    }
+
+    if (ssl->tls_version != MBEDTLS_SSL_VERSION_TLS1_3) {
+        return MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA;
+    }
+
+    /*
+     * If we are at the beginning of the handshake, the early data status being
+     * equal to MBEDTLS_SSL_EARLY_DATA_STATUS_UNKNOWN or
+     * MBEDTLS_SSL_EARLY_DATA_STATUS_SENT advance the handshake just
+     * enough to be able to send early data if possible. That way, we can
+     * guarantee that when starting the handshake with this function we will
+     * send at least one record of early data. Note that when the status is
+     * MBEDTLS_SSL_EARLY_DATA_STATUS_SENT and not yet
+     * MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_WRITE, we cannot send early data yet
+     * as the early data outbound transform has not been set as we may have to
+     * first send a dummy CCS in clear.
+     */
+    if ((ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_UNKNOWN) ||
+        (ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_SENT)) {
+        while ((ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_UNKNOWN) ||
+               (ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_SENT)) {
+            ret = mbedtls_ssl_handshake_step(ssl);
+            if (ret != 0) {
+                MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_handshake_step", ret);
+                return ret;
+            }
+
+            ret = mbedtls_ssl_flush_output(ssl);
+            if (ret != 0) {
+                MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_flush_output", ret);
+                return ret;
+            }
+        }
+    } else {
+        /*
+         * If we are past the point where we can send early data, return
+         * immediatly. Otherwise, progress the handshake as much as possible to
+         * not delay it too much. If we reach a point where we can still send
+         * early data, then we will send some.
+         */
+        if ((ssl->early_data_status != MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_WRITE) &&
+            (ssl->early_data_status != MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED)) {
+            return MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA;
+        }
+
+        ret = mbedtls_ssl_handshake(ssl);
+        if ((ret != 0) && (ret != MBEDTLS_ERR_SSL_WANT_READ)) {
+            MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_handshake", ret);
+            return ret;
+        }
+    }
+
+    if ((ssl->early_data_status != MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_WRITE) &&
+        (ssl->early_data_status != MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED)) {
+        return MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA;
+    }
+
+    written_data_len = ssl_write_real(ssl, buf, len);
+
+    MBEDTLS_SSL_DEBUG_MSG(2, ("<= write early_data, len=%d", written_data_len));
+
+    return written_data_len;
+}
+#endif /* MBEDTLS_SSL_EARLY_DATA && MBEDTLS_SSL_CLI_C */
+
 /*
  * Notify the peer that the connection is being closed
  */
diff --git a/library/ssl_ticket.c b/library/ssl_ticket.c
index cd730fb..5da3887 100644
--- a/library/ssl_ticket.c
+++ b/library/ssl_ticket.c
@@ -75,6 +75,10 @@
 #if defined(MBEDTLS_HAVE_TIME)
     key->generation_time = mbedtls_time(NULL);
 #endif
+    /* The lifetime of a key is the configured lifetime of the tickets when
+     * the key is created.
+     */
+    key->lifetime = ctx->ticket_lifetime;
 
     if ((ret = ctx->f_rng(ctx->p_rng, key->name, sizeof(key->name))) != 0) {
         return ret;
@@ -116,16 +120,17 @@
 #if !defined(MBEDTLS_HAVE_TIME)
     ((void) ctx);
 #else
-    if (ctx->ticket_lifetime != 0) {
+    mbedtls_ssl_ticket_key * const key = ctx->keys + ctx->active;
+    if (key->lifetime != 0) {
         mbedtls_time_t current_time = mbedtls_time(NULL);
-        mbedtls_time_t key_time = ctx->keys[ctx->active].generation_time;
+        mbedtls_time_t key_time = key->generation_time;
 
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
         psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
 #endif
 
         if (current_time >= key_time &&
-            (uint64_t) (current_time - key_time) < ctx->ticket_lifetime) {
+            (uint64_t) (current_time - key_time) < key->lifetime) {
             return 0;
         }
 
@@ -198,6 +203,8 @@
 #if defined(MBEDTLS_HAVE_TIME)
     key->generation_time = mbedtls_time(NULL);
 #endif
+    key->lifetime = lifetime;
+
     return 0;
 }
 
@@ -331,7 +338,7 @@
 
     key = &ctx->keys[ctx->active];
 
-    *ticket_lifetime = ctx->ticket_lifetime;
+    *ticket_lifetime = key->lifetime;
 
     memcpy(key_name, key->name, TICKET_KEY_NAME_BYTES);
 
@@ -495,43 +502,22 @@
     }
 
 #if defined(MBEDTLS_HAVE_TIME)
-#if defined(MBEDTLS_SSL_PROTO_TLS1_3)
-    if (session->tls_version == MBEDTLS_SSL_VERSION_TLS1_3) {
-        /* Check for expiration */
-        mbedtls_ms_time_t ticket_age = -1;
-#if defined(MBEDTLS_SSL_SRV_C)
-        if (session->endpoint == MBEDTLS_SSL_IS_SERVER) {
-            ticket_age = mbedtls_ms_time() - session->ticket_creation_time;
-        }
-#endif
-#if defined(MBEDTLS_SSL_CLI_C)
-        if (session->endpoint == MBEDTLS_SSL_IS_CLIENT) {
-            ticket_age = mbedtls_ms_time() - session->ticket_reception_time;
-        }
-#endif
+    mbedtls_ms_time_t ticket_creation_time, ticket_age;
+    mbedtls_ms_time_t ticket_lifetime =
+        (mbedtls_ms_time_t) ctx->ticket_lifetime * 1000;
 
-        mbedtls_ms_time_t ticket_lifetime =
-            (mbedtls_ms_time_t) ctx->ticket_lifetime * 1000;
-
-        if (ticket_age < 0 || ticket_age > ticket_lifetime) {
-            ret = MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED;
-            goto cleanup;
-        }
+    ret = mbedtls_ssl_session_get_ticket_creation_time(session,
+                                                       &ticket_creation_time);
+    if (ret != 0) {
+        goto cleanup;
     }
-#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
-#if defined(MBEDTLS_SSL_PROTO_TLS1_2)
-    if (session->tls_version == MBEDTLS_SSL_VERSION_TLS1_2) {
-        /* Check for expiration */
-        mbedtls_time_t current_time = mbedtls_time(NULL);
 
-        if (current_time < session->start ||
-            (uint32_t) (current_time - session->start) > ctx->ticket_lifetime) {
-            ret = MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED;
-            goto cleanup;
-        }
+    ticket_age = mbedtls_ms_time() - ticket_creation_time;
+    if (ticket_age < 0 || ticket_age > ticket_lifetime) {
+        ret = MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED;
+        goto cleanup;
     }
-#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */
-#endif /* MBEDTLS_HAVE_TIME */
+#endif
 
 cleanup:
 #if defined(MBEDTLS_THREADING_C)
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 0071b06..5002443 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -20,7 +20,7 @@
 #include "ssl_debug_helpers.h"
 #include "ssl_misc.h"
 
-#include "mbedtls/debug.h"
+#include "debug_internal.h"
 #include "mbedtls/error.h"
 #include "mbedtls/platform_util.h"
 #include "mbedtls/version.h"
@@ -432,10 +432,6 @@
 static int ssl_calc_finished_tls_sha384(mbedtls_ssl_context *, unsigned char *, int);
 #endif /* MBEDTLS_MD_CAN_SHA384*/
 
-static size_t ssl_tls12_session_save(const mbedtls_ssl_session *session,
-                                     unsigned char *buf,
-                                     size_t buf_len);
-
 MBEDTLS_CHECK_RETURN_CRITICAL
 static int ssl_tls12_session_load(mbedtls_ssl_session *session,
                                   const unsigned char *buf,
@@ -631,7 +627,7 @@
     [MBEDTLS_SSL_EXT_ID_RECORD_SIZE_LIMIT] = "record_size_limit"
 };
 
-static unsigned int extension_type_table[] = {
+static const unsigned int extension_type_table[] = {
     [MBEDTLS_SSL_EXT_ID_UNRECOGNIZED] = 0xff,
     [MBEDTLS_SSL_EXT_ID_SERVERNAME] = MBEDTLS_TLS_EXT_SERVERNAME,
     [MBEDTLS_SSL_EXT_ID_MAX_FRAGMENT_LENGTH] = MBEDTLS_TLS_EXT_MAX_FRAGMENT_LENGTH,
@@ -1098,6 +1094,16 @@
         return MBEDTLS_ERR_SSL_ALLOC_FAILED;
     }
 
+#if defined(MBEDTLS_SSL_EARLY_DATA)
+#if defined(MBEDTLS_SSL_CLI_C)
+    ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_UNKNOWN;
+#endif
+#if defined(MBEDTLS_SSL_SRV_C)
+    ssl->discard_early_data_record = MBEDTLS_SSL_EARLY_DATA_NO_DISCARD;
+#endif
+    ssl->total_early_data_size = 0;
+#endif /* MBEDTLS_SSL_EARLY_DATA */
+
     /* Initialize structures */
     mbedtls_ssl_session_init(ssl->session_negotiate);
     ssl_handshake_params_init(ssl->handshake);
@@ -1356,6 +1362,11 @@
     }
 #endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
 
+    if (ssl->conf->f_rng == NULL) {
+        MBEDTLS_SSL_DEBUG_MSG(1, ("no RNG provided"));
+        return MBEDTLS_ERR_SSL_NO_RNG;
+    }
+
     /* Space for further checks */
 
     return 0;
@@ -2440,290 +2451,6 @@
 
 #if defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3)
 
-#if defined(MBEDTLS_SSL_PROTO_TLS1_3)
-/* Serialization of TLS 1.3 sessions:
- *
- *     struct {
- *       opaque hostname<0..2^16-1>;
- *       uint64 ticket_reception_time;
- *       uint32 ticket_lifetime;
- *       opaque ticket<1..2^16-1>;
- *     } ClientOnlyData;
- *
- *     struct {
- *       uint8 endpoint;
- *       uint8 ciphersuite[2];
- *       uint32 ticket_age_add;
- *       uint8 ticket_flags;
- *       opaque resumption_key<0..255>;
- *       uint32 max_early_data_size;
- *       uint16 record_size_limit;
- *       select ( endpoint ) {
- *            case client: ClientOnlyData;
- *            case server: uint64 ticket_creation_time;
- *        };
- *     } serialized_session_tls13;
- *
- */
-#if defined(MBEDTLS_SSL_SESSION_TICKETS)
-MBEDTLS_CHECK_RETURN_CRITICAL
-static int ssl_tls13_session_save(const mbedtls_ssl_session *session,
-                                  unsigned char *buf,
-                                  size_t buf_len,
-                                  size_t *olen)
-{
-    unsigned char *p = buf;
-#if defined(MBEDTLS_SSL_CLI_C) && \
-    defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
-    size_t hostname_len = (session->hostname == NULL) ?
-                          0 : strlen(session->hostname) + 1;
-#endif
-    size_t needed =   1                             /* endpoint */
-                    + 2                             /* ciphersuite */
-                    + 4                             /* ticket_age_add */
-                    + 1                             /* ticket_flags */
-                    + 1;                            /* resumption_key length */
-    *olen = 0;
-
-    if (session->resumption_key_len > MBEDTLS_SSL_TLS1_3_TICKET_RESUMPTION_KEY_LEN) {
-        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-    }
-    needed += session->resumption_key_len;  /* resumption_key */
-
-#if defined(MBEDTLS_SSL_EARLY_DATA)
-    needed += 4;                            /* max_early_data_size */
-#endif
-#if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT)
-    needed += 2;                            /* record_size_limit */
-#endif /* MBEDTLS_SSL_RECORD_SIZE_LIMIT */
-
-#if defined(MBEDTLS_HAVE_TIME)
-    needed += 8; /* ticket_creation_time or ticket_reception_time */
-#endif
-
-#if defined(MBEDTLS_SSL_CLI_C)
-    if (session->endpoint == MBEDTLS_SSL_IS_CLIENT) {
-#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
-        needed +=  2                        /* hostname_len */
-                  + hostname_len;           /* hostname */
-#endif
-
-        needed +=   4                       /* ticket_lifetime */
-                  + 2;                      /* ticket_len */
-
-        /* Check size_t overflow */
-        if (session->ticket_len > SIZE_MAX - needed) {
-            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-        }
-
-        needed += session->ticket_len;      /* ticket */
-    }
-#endif /* MBEDTLS_SSL_CLI_C */
-
-    *olen = needed;
-    if (needed > buf_len) {
-        return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL;
-    }
-
-    p[0] = session->endpoint;
-    MBEDTLS_PUT_UINT16_BE(session->ciphersuite, p, 1);
-    MBEDTLS_PUT_UINT32_BE(session->ticket_age_add, p, 3);
-    p[7] = session->ticket_flags;
-
-    /* save resumption_key */
-    p[8] = session->resumption_key_len;
-    p += 9;
-    memcpy(p, session->resumption_key, session->resumption_key_len);
-    p += session->resumption_key_len;
-
-#if defined(MBEDTLS_SSL_EARLY_DATA)
-    MBEDTLS_PUT_UINT32_BE(session->max_early_data_size, p, 0);
-    p += 4;
-#endif
-#if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT)
-    MBEDTLS_PUT_UINT16_BE(session->record_size_limit, p, 0);
-    p += 2;
-#endif /* MBEDTLS_SSL_RECORD_SIZE_LIMIT */
-
-#if defined(MBEDTLS_HAVE_TIME) && defined(MBEDTLS_SSL_SRV_C)
-    if (session->endpoint == MBEDTLS_SSL_IS_SERVER) {
-        MBEDTLS_PUT_UINT64_BE((uint64_t) session->ticket_creation_time, p, 0);
-        p += 8;
-    }
-#endif /* MBEDTLS_HAVE_TIME */
-
-#if defined(MBEDTLS_SSL_CLI_C)
-    if (session->endpoint == MBEDTLS_SSL_IS_CLIENT) {
-#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
-        MBEDTLS_PUT_UINT16_BE(hostname_len, p, 0);
-        p += 2;
-        if (hostname_len > 0) {
-            /* save host name */
-            memcpy(p, session->hostname, hostname_len);
-            p += hostname_len;
-        }
-#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */
-
-#if defined(MBEDTLS_HAVE_TIME)
-        MBEDTLS_PUT_UINT64_BE((uint64_t) session->ticket_reception_time, p, 0);
-        p += 8;
-#endif
-        MBEDTLS_PUT_UINT32_BE(session->ticket_lifetime, p, 0);
-        p += 4;
-
-        MBEDTLS_PUT_UINT16_BE(session->ticket_len, p, 0);
-        p += 2;
-
-        if (session->ticket != NULL && session->ticket_len > 0) {
-            memcpy(p, session->ticket, session->ticket_len);
-            p += session->ticket_len;
-        }
-    }
-#endif /* MBEDTLS_SSL_CLI_C */
-    return 0;
-}
-
-MBEDTLS_CHECK_RETURN_CRITICAL
-static int ssl_tls13_session_load(mbedtls_ssl_session *session,
-                                  const unsigned char *buf,
-                                  size_t len)
-{
-    const unsigned char *p = buf;
-    const unsigned char *end = buf + len;
-
-    if (end - p < 9) {
-        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-    }
-    session->endpoint = p[0];
-    session->ciphersuite = MBEDTLS_GET_UINT16_BE(p, 1);
-    session->ticket_age_add = MBEDTLS_GET_UINT32_BE(p, 3);
-    session->ticket_flags = p[7];
-
-    /* load resumption_key */
-    session->resumption_key_len = p[8];
-    p += 9;
-
-    if (end - p < session->resumption_key_len) {
-        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-    }
-
-    if (sizeof(session->resumption_key) < session->resumption_key_len) {
-        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-    }
-    memcpy(session->resumption_key, p, session->resumption_key_len);
-    p += session->resumption_key_len;
-
-#if defined(MBEDTLS_SSL_EARLY_DATA)
-    if (end - p < 4) {
-        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-    }
-    session->max_early_data_size = MBEDTLS_GET_UINT32_BE(p, 0);
-    p += 4;
-#endif
-#if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT)
-    if (end - p < 2) {
-        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-    }
-    session->record_size_limit = MBEDTLS_GET_UINT16_BE(p, 0);
-    p += 2;
-#endif /* MBEDTLS_SSL_RECORD_SIZE_LIMIT */
-
-#if defined(MBEDTLS_HAVE_TIME) && defined(MBEDTLS_SSL_SRV_C)
-    if (session->endpoint == MBEDTLS_SSL_IS_SERVER) {
-        if (end - p < 8) {
-            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-        }
-        session->ticket_creation_time = MBEDTLS_GET_UINT64_BE(p, 0);
-        p += 8;
-    }
-#endif /* MBEDTLS_HAVE_TIME */
-
-#if defined(MBEDTLS_SSL_CLI_C)
-    if (session->endpoint == MBEDTLS_SSL_IS_CLIENT) {
-#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
-        size_t hostname_len;
-        /* load host name */
-        if (end - p < 2) {
-            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-        }
-        hostname_len = MBEDTLS_GET_UINT16_BE(p, 0);
-        p += 2;
-
-        if (end - p < (long int) hostname_len) {
-            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-        }
-        if (hostname_len > 0) {
-            session->hostname = mbedtls_calloc(1, hostname_len);
-            if (session->hostname == NULL) {
-                return MBEDTLS_ERR_SSL_ALLOC_FAILED;
-            }
-            memcpy(session->hostname, p, hostname_len);
-            p += hostname_len;
-        }
-#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */
-
-#if defined(MBEDTLS_HAVE_TIME)
-        if (end - p < 8) {
-            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-        }
-        session->ticket_reception_time = MBEDTLS_GET_UINT64_BE(p, 0);
-        p += 8;
-#endif
-        if (end - p < 4) {
-            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-        }
-        session->ticket_lifetime = MBEDTLS_GET_UINT32_BE(p, 0);
-        p += 4;
-
-        if (end - p <  2) {
-            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-        }
-        session->ticket_len = MBEDTLS_GET_UINT16_BE(p, 0);
-        p += 2;
-
-        if (end - p < (long int) session->ticket_len) {
-            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-        }
-        if (session->ticket_len > 0) {
-            session->ticket = mbedtls_calloc(1, session->ticket_len);
-            if (session->ticket == NULL) {
-                return MBEDTLS_ERR_SSL_ALLOC_FAILED;
-            }
-            memcpy(session->ticket, p, session->ticket_len);
-            p += session->ticket_len;
-        }
-    }
-#endif /* MBEDTLS_SSL_CLI_C */
-
-    return 0;
-
-}
-#else /* MBEDTLS_SSL_SESSION_TICKETS */
-MBEDTLS_CHECK_RETURN_CRITICAL
-static int ssl_tls13_session_save(const mbedtls_ssl_session *session,
-                                  unsigned char *buf,
-                                  size_t buf_len,
-                                  size_t *olen)
-{
-    ((void) session);
-    ((void) buf);
-    ((void) buf_len);
-    *olen = 0;
-    return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE;
-}
-
-static int ssl_tls13_session_load(const mbedtls_ssl_session *session,
-                                  unsigned char *buf,
-                                  size_t buf_len)
-{
-    ((void) session);
-    ((void) buf);
-    ((void) buf_len);
-    return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE;
-}
-#endif /* !MBEDTLS_SSL_SESSION_TICKETS */
-#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
-
 psa_status_t mbedtls_ssl_cipher_to_psa(mbedtls_cipher_type_t mbedtls_cipher_type,
                                        size_t taglen,
                                        psa_algorithm_t *alg,
@@ -3640,6 +3367,630 @@
 }
 #endif /* MBEDTLS_SSL_CLI_C */
 
+#if defined(MBEDTLS_SSL_PROTO_TLS1_2)
+
+/* Serialization of TLS 1.2 sessions
+ *
+ * For more detail, see the description of ssl_session_save().
+ */
+static size_t ssl_tls12_session_save(const mbedtls_ssl_session *session,
+                                     unsigned char *buf,
+                                     size_t buf_len)
+{
+    unsigned char *p = buf;
+    size_t used = 0;
+
+#if defined(MBEDTLS_HAVE_TIME)
+    uint64_t start;
+#endif
+#if defined(MBEDTLS_X509_CRT_PARSE_C)
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+    size_t cert_len;
+#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+#endif /* MBEDTLS_X509_CRT_PARSE_C */
+
+    /*
+     * Time
+     */
+#if defined(MBEDTLS_HAVE_TIME)
+    used += 8;
+
+    if (used <= buf_len) {
+        start = (uint64_t) session->start;
+
+        MBEDTLS_PUT_UINT64_BE(start, p, 0);
+        p += 8;
+    }
+#endif /* MBEDTLS_HAVE_TIME */
+
+    /*
+     * Basic mandatory fields
+     */
+    used += 1 /* id_len */
+            + sizeof(session->id)
+            + sizeof(session->master)
+            + 4; /* verify_result */
+
+    if (used <= buf_len) {
+        *p++ = MBEDTLS_BYTE_0(session->id_len);
+        memcpy(p, session->id, 32);
+        p += 32;
+
+        memcpy(p, session->master, 48);
+        p += 48;
+
+        MBEDTLS_PUT_UINT32_BE(session->verify_result, p, 0);
+        p += 4;
+    }
+
+    /*
+     * Peer's end-entity certificate
+     */
+#if defined(MBEDTLS_X509_CRT_PARSE_C)
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+    if (session->peer_cert == NULL) {
+        cert_len = 0;
+    } else {
+        cert_len = session->peer_cert->raw.len;
+    }
+
+    used += 3 + cert_len;
+
+    if (used <= buf_len) {
+        *p++ = MBEDTLS_BYTE_2(cert_len);
+        *p++ = MBEDTLS_BYTE_1(cert_len);
+        *p++ = MBEDTLS_BYTE_0(cert_len);
+
+        if (session->peer_cert != NULL) {
+            memcpy(p, session->peer_cert->raw.p, cert_len);
+            p += cert_len;
+        }
+    }
+#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+    if (session->peer_cert_digest != NULL) {
+        used += 1 /* type */ + 1 /* length */ + session->peer_cert_digest_len;
+        if (used <= buf_len) {
+            *p++ = (unsigned char) session->peer_cert_digest_type;
+            *p++ = (unsigned char) session->peer_cert_digest_len;
+            memcpy(p, session->peer_cert_digest,
+                   session->peer_cert_digest_len);
+            p += session->peer_cert_digest_len;
+        }
+    } else {
+        used += 2;
+        if (used <= buf_len) {
+            *p++ = (unsigned char) MBEDTLS_MD_NONE;
+            *p++ = 0;
+        }
+    }
+#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+#endif /* MBEDTLS_X509_CRT_PARSE_C */
+
+    /*
+     * Session ticket if any, plus associated data
+     */
+#if defined(MBEDTLS_SSL_SESSION_TICKETS)
+#if defined(MBEDTLS_SSL_CLI_C)
+    if (session->endpoint == MBEDTLS_SSL_IS_CLIENT) {
+        used += 3 + session->ticket_len + 4; /* len + ticket + lifetime */
+
+        if (used <= buf_len) {
+            *p++ = MBEDTLS_BYTE_2(session->ticket_len);
+            *p++ = MBEDTLS_BYTE_1(session->ticket_len);
+            *p++ = MBEDTLS_BYTE_0(session->ticket_len);
+
+            if (session->ticket != NULL) {
+                memcpy(p, session->ticket, session->ticket_len);
+                p += session->ticket_len;
+            }
+
+            MBEDTLS_PUT_UINT32_BE(session->ticket_lifetime, p, 0);
+            p += 4;
+        }
+    }
+#endif /* MBEDTLS_SSL_CLI_C */
+#if defined(MBEDTLS_HAVE_TIME) && defined(MBEDTLS_SSL_SRV_C)
+    if (session->endpoint == MBEDTLS_SSL_IS_SERVER) {
+        used += 8;
+
+        if (used <= buf_len) {
+            MBEDTLS_PUT_UINT64_BE((uint64_t) session->ticket_creation_time, p, 0);
+            p += 8;
+        }
+    }
+#endif /* MBEDTLS_HAVE_TIME && MBEDTLS_SSL_SRV_C */
+#endif /* MBEDTLS_SSL_SESSION_TICKETS */
+
+    /*
+     * Misc extension-related info
+     */
+#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
+    used += 1;
+
+    if (used <= buf_len) {
+        *p++ = session->mfl_code;
+    }
+#endif
+
+#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC)
+    used += 1;
+
+    if (used <= buf_len) {
+        *p++ = MBEDTLS_BYTE_0(session->encrypt_then_mac);
+    }
+#endif
+
+    return used;
+}
+
+MBEDTLS_CHECK_RETURN_CRITICAL
+static int ssl_tls12_session_load(mbedtls_ssl_session *session,
+                                  const unsigned char *buf,
+                                  size_t len)
+{
+#if defined(MBEDTLS_HAVE_TIME)
+    uint64_t start;
+#endif
+#if defined(MBEDTLS_X509_CRT_PARSE_C)
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+    size_t cert_len;
+#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+#endif /* MBEDTLS_X509_CRT_PARSE_C */
+
+    const unsigned char *p = buf;
+    const unsigned char * const end = buf + len;
+
+    /*
+     * Time
+     */
+#if defined(MBEDTLS_HAVE_TIME)
+    if (8 > (size_t) (end - p)) {
+        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+    }
+
+    start = MBEDTLS_GET_UINT64_BE(p, 0);
+    p += 8;
+
+    session->start = (time_t) start;
+#endif /* MBEDTLS_HAVE_TIME */
+
+    /*
+     * Basic mandatory fields
+     */
+    if (1 + 32 + 48 + 4 > (size_t) (end - p)) {
+        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+    }
+
+    session->id_len = *p++;
+    memcpy(session->id, p, 32);
+    p += 32;
+
+    memcpy(session->master, p, 48);
+    p += 48;
+
+    session->verify_result = MBEDTLS_GET_UINT32_BE(p, 0);
+    p += 4;
+
+    /* Immediately clear invalid pointer values that have been read, in case
+     * we exit early before we replaced them with valid ones. */
+#if defined(MBEDTLS_X509_CRT_PARSE_C)
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+    session->peer_cert = NULL;
+#else
+    session->peer_cert_digest = NULL;
+#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+#endif /* MBEDTLS_X509_CRT_PARSE_C */
+#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C)
+    session->ticket = NULL;
+#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */
+
+    /*
+     * Peer certificate
+     */
+#if defined(MBEDTLS_X509_CRT_PARSE_C)
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+    /* Deserialize CRT from the end of the ticket. */
+    if (3 > (size_t) (end - p)) {
+        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+    }
+
+    cert_len = MBEDTLS_GET_UINT24_BE(p, 0);
+    p += 3;
+
+    if (cert_len != 0) {
+        int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+        if (cert_len > (size_t) (end - p)) {
+            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        }
+
+        session->peer_cert = mbedtls_calloc(1, sizeof(mbedtls_x509_crt));
+
+        if (session->peer_cert == NULL) {
+            return MBEDTLS_ERR_SSL_ALLOC_FAILED;
+        }
+
+        mbedtls_x509_crt_init(session->peer_cert);
+
+        if ((ret = mbedtls_x509_crt_parse_der(session->peer_cert,
+                                              p, cert_len)) != 0) {
+            mbedtls_x509_crt_free(session->peer_cert);
+            mbedtls_free(session->peer_cert);
+            session->peer_cert = NULL;
+            return ret;
+        }
+
+        p += cert_len;
+    }
+#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+    /* Deserialize CRT digest from the end of the ticket. */
+    if (2 > (size_t) (end - p)) {
+        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+    }
+
+    session->peer_cert_digest_type = (mbedtls_md_type_t) *p++;
+    session->peer_cert_digest_len  = (size_t) *p++;
+
+    if (session->peer_cert_digest_len != 0) {
+        const mbedtls_md_info_t *md_info =
+            mbedtls_md_info_from_type(session->peer_cert_digest_type);
+        if (md_info == NULL) {
+            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        }
+        if (session->peer_cert_digest_len != mbedtls_md_get_size(md_info)) {
+            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        }
+
+        if (session->peer_cert_digest_len > (size_t) (end - p)) {
+            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        }
+
+        session->peer_cert_digest =
+            mbedtls_calloc(1, session->peer_cert_digest_len);
+        if (session->peer_cert_digest == NULL) {
+            return MBEDTLS_ERR_SSL_ALLOC_FAILED;
+        }
+
+        memcpy(session->peer_cert_digest, p,
+               session->peer_cert_digest_len);
+        p += session->peer_cert_digest_len;
+    }
+#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+#endif /* MBEDTLS_X509_CRT_PARSE_C */
+
+    /*
+     * Session ticket and associated data
+     */
+#if defined(MBEDTLS_SSL_SESSION_TICKETS)
+#if defined(MBEDTLS_SSL_CLI_C)
+    if (session->endpoint == MBEDTLS_SSL_IS_CLIENT) {
+        if (3 > (size_t) (end - p)) {
+            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        }
+
+        session->ticket_len = MBEDTLS_GET_UINT24_BE(p, 0);
+        p += 3;
+
+        if (session->ticket_len != 0) {
+            if (session->ticket_len > (size_t) (end - p)) {
+                return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+            }
+
+            session->ticket = mbedtls_calloc(1, session->ticket_len);
+            if (session->ticket == NULL) {
+                return MBEDTLS_ERR_SSL_ALLOC_FAILED;
+            }
+
+            memcpy(session->ticket, p, session->ticket_len);
+            p += session->ticket_len;
+        }
+
+        if (4 > (size_t) (end - p)) {
+            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        }
+
+        session->ticket_lifetime = MBEDTLS_GET_UINT32_BE(p, 0);
+        p += 4;
+    }
+#endif /* MBEDTLS_SSL_CLI_C */
+#if defined(MBEDTLS_HAVE_TIME) && defined(MBEDTLS_SSL_SRV_C)
+    if (session->endpoint == MBEDTLS_SSL_IS_SERVER) {
+        if (8 > (size_t) (end - p)) {
+            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        }
+        session->ticket_creation_time = MBEDTLS_GET_UINT64_BE(p, 0);
+        p += 8;
+    }
+#endif /* MBEDTLS_HAVE_TIME && MBEDTLS_SSL_SRV_C */
+#endif /* MBEDTLS_SSL_SESSION_TICKETS */
+
+    /*
+     * Misc extension-related info
+     */
+#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
+    if (1 > (size_t) (end - p)) {
+        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+    }
+
+    session->mfl_code = *p++;
+#endif
+
+#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC)
+    if (1 > (size_t) (end - p)) {
+        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+    }
+
+    session->encrypt_then_mac = *p++;
+#endif
+
+    /* Done, should have consumed entire buffer */
+    if (p != end) {
+        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+    }
+
+    return 0;
+}
+
+#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */
+
+#if defined(MBEDTLS_SSL_PROTO_TLS1_3)
+/* Serialization of TLS 1.3 sessions:
+ *
+ * For more detail, see the description of ssl_session_save().
+ */
+#if defined(MBEDTLS_SSL_SESSION_TICKETS)
+MBEDTLS_CHECK_RETURN_CRITICAL
+static int ssl_tls13_session_save(const mbedtls_ssl_session *session,
+                                  unsigned char *buf,
+                                  size_t buf_len,
+                                  size_t *olen)
+{
+    unsigned char *p = buf;
+#if defined(MBEDTLS_SSL_CLI_C) && \
+    defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
+    size_t hostname_len = (session->hostname == NULL) ?
+                          0 : strlen(session->hostname) + 1;
+#endif
+    size_t needed =   4  /* ticket_age_add */
+                    + 1  /* ticket_flags */
+                    + 1; /* resumption_key length */
+    *olen = 0;
+
+    if (session->resumption_key_len > MBEDTLS_SSL_TLS1_3_TICKET_RESUMPTION_KEY_LEN) {
+        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+    }
+    needed += session->resumption_key_len;  /* resumption_key */
+
+#if defined(MBEDTLS_SSL_EARLY_DATA)
+    needed += 4;                            /* max_early_data_size */
+#endif
+#if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT)
+    needed += 2;                            /* record_size_limit */
+#endif /* MBEDTLS_SSL_RECORD_SIZE_LIMIT */
+
+#if defined(MBEDTLS_HAVE_TIME)
+    needed += 8; /* ticket_creation_time or ticket_reception_time */
+#endif
+
+#if defined(MBEDTLS_SSL_CLI_C)
+    if (session->endpoint == MBEDTLS_SSL_IS_CLIENT) {
+#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
+        needed +=  2                        /* hostname_len */
+                  + hostname_len;           /* hostname */
+#endif
+
+        needed +=   4                       /* ticket_lifetime */
+                  + 2;                      /* ticket_len */
+
+        /* Check size_t overflow */
+        if (session->ticket_len > SIZE_MAX - needed) {
+            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        }
+
+        needed += session->ticket_len;      /* ticket */
+    }
+#endif /* MBEDTLS_SSL_CLI_C */
+
+    *olen = needed;
+    if (needed > buf_len) {
+        return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL;
+    }
+
+    MBEDTLS_PUT_UINT32_BE(session->ticket_age_add, p, 0);
+    p[4] = session->ticket_flags;
+
+    /* save resumption_key */
+    p[5] = session->resumption_key_len;
+    p += 6;
+    memcpy(p, session->resumption_key, session->resumption_key_len);
+    p += session->resumption_key_len;
+
+#if defined(MBEDTLS_SSL_EARLY_DATA)
+    MBEDTLS_PUT_UINT32_BE(session->max_early_data_size, p, 0);
+    p += 4;
+#endif
+#if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT)
+    MBEDTLS_PUT_UINT16_BE(session->record_size_limit, p, 0);
+    p += 2;
+#endif /* MBEDTLS_SSL_RECORD_SIZE_LIMIT */
+
+#if defined(MBEDTLS_HAVE_TIME) && defined(MBEDTLS_SSL_SRV_C)
+    if (session->endpoint == MBEDTLS_SSL_IS_SERVER) {
+        MBEDTLS_PUT_UINT64_BE((uint64_t) session->ticket_creation_time, p, 0);
+        p += 8;
+    }
+#endif /* MBEDTLS_HAVE_TIME */
+
+#if defined(MBEDTLS_SSL_CLI_C)
+    if (session->endpoint == MBEDTLS_SSL_IS_CLIENT) {
+#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
+        MBEDTLS_PUT_UINT16_BE(hostname_len, p, 0);
+        p += 2;
+        if (hostname_len > 0) {
+            /* save host name */
+            memcpy(p, session->hostname, hostname_len);
+            p += hostname_len;
+        }
+#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */
+
+#if defined(MBEDTLS_HAVE_TIME)
+        MBEDTLS_PUT_UINT64_BE((uint64_t) session->ticket_reception_time, p, 0);
+        p += 8;
+#endif
+        MBEDTLS_PUT_UINT32_BE(session->ticket_lifetime, p, 0);
+        p += 4;
+
+        MBEDTLS_PUT_UINT16_BE(session->ticket_len, p, 0);
+        p += 2;
+
+        if (session->ticket != NULL && session->ticket_len > 0) {
+            memcpy(p, session->ticket, session->ticket_len);
+            p += session->ticket_len;
+        }
+    }
+#endif /* MBEDTLS_SSL_CLI_C */
+    return 0;
+}
+
+MBEDTLS_CHECK_RETURN_CRITICAL
+static int ssl_tls13_session_load(mbedtls_ssl_session *session,
+                                  const unsigned char *buf,
+                                  size_t len)
+{
+    const unsigned char *p = buf;
+    const unsigned char *end = buf + len;
+
+    if (end - p < 6) {
+        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+    }
+    session->ticket_age_add = MBEDTLS_GET_UINT32_BE(p, 0);
+    session->ticket_flags = p[4];
+
+    /* load resumption_key */
+    session->resumption_key_len = p[5];
+    p += 6;
+
+    if (end - p < session->resumption_key_len) {
+        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+    }
+
+    if (sizeof(session->resumption_key) < session->resumption_key_len) {
+        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+    }
+    memcpy(session->resumption_key, p, session->resumption_key_len);
+    p += session->resumption_key_len;
+
+#if defined(MBEDTLS_SSL_EARLY_DATA)
+    if (end - p < 4) {
+        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+    }
+    session->max_early_data_size = MBEDTLS_GET_UINT32_BE(p, 0);
+    p += 4;
+#endif
+#if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT)
+    if (end - p < 2) {
+        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+    }
+    session->record_size_limit = MBEDTLS_GET_UINT16_BE(p, 0);
+    p += 2;
+#endif /* MBEDTLS_SSL_RECORD_SIZE_LIMIT */
+
+#if defined(MBEDTLS_HAVE_TIME) && defined(MBEDTLS_SSL_SRV_C)
+    if (session->endpoint == MBEDTLS_SSL_IS_SERVER) {
+        if (end - p < 8) {
+            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        }
+        session->ticket_creation_time = MBEDTLS_GET_UINT64_BE(p, 0);
+        p += 8;
+    }
+#endif /* MBEDTLS_HAVE_TIME */
+
+#if defined(MBEDTLS_SSL_CLI_C)
+    if (session->endpoint == MBEDTLS_SSL_IS_CLIENT) {
+#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
+        size_t hostname_len;
+        /* load host name */
+        if (end - p < 2) {
+            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        }
+        hostname_len = MBEDTLS_GET_UINT16_BE(p, 0);
+        p += 2;
+
+        if (end - p < (long int) hostname_len) {
+            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        }
+        if (hostname_len > 0) {
+            session->hostname = mbedtls_calloc(1, hostname_len);
+            if (session->hostname == NULL) {
+                return MBEDTLS_ERR_SSL_ALLOC_FAILED;
+            }
+            memcpy(session->hostname, p, hostname_len);
+            p += hostname_len;
+        }
+#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */
+
+#if defined(MBEDTLS_HAVE_TIME)
+        if (end - p < 8) {
+            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        }
+        session->ticket_reception_time = MBEDTLS_GET_UINT64_BE(p, 0);
+        p += 8;
+#endif
+        if (end - p < 4) {
+            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        }
+        session->ticket_lifetime = MBEDTLS_GET_UINT32_BE(p, 0);
+        p += 4;
+
+        if (end - p <  2) {
+            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        }
+        session->ticket_len = MBEDTLS_GET_UINT16_BE(p, 0);
+        p += 2;
+
+        if (end - p < (long int) session->ticket_len) {
+            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        }
+        if (session->ticket_len > 0) {
+            session->ticket = mbedtls_calloc(1, session->ticket_len);
+            if (session->ticket == NULL) {
+                return MBEDTLS_ERR_SSL_ALLOC_FAILED;
+            }
+            memcpy(session->ticket, p, session->ticket_len);
+            p += session->ticket_len;
+        }
+    }
+#endif /* MBEDTLS_SSL_CLI_C */
+
+    return 0;
+
+}
+#else /* MBEDTLS_SSL_SESSION_TICKETS */
+MBEDTLS_CHECK_RETURN_CRITICAL
+static int ssl_tls13_session_save(const mbedtls_ssl_session *session,
+                                  unsigned char *buf,
+                                  size_t buf_len,
+                                  size_t *olen)
+{
+    ((void) session);
+    ((void) buf);
+    ((void) buf_len);
+    *olen = 0;
+    return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE;
+}
+
+static int ssl_tls13_session_load(const mbedtls_ssl_session *session,
+                                  unsigned char *buf,
+                                  size_t buf_len)
+{
+    ((void) session);
+    ((void) buf);
+    ((void) buf_len);
+    return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE;
+}
+#endif /* !MBEDTLS_SSL_SESSION_TICKETS */
+#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
+
 /*
  * Define ticket header determining Mbed TLS version
  * and structure of the ticket.
@@ -3662,6 +4013,12 @@
 #define SSL_SERIALIZED_SESSION_CONFIG_CRT 0
 #endif /* MBEDTLS_X509_CRT_PARSE_C */
 
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+#define SSL_SERIALIZED_SESSION_CONFIG_KEEP_PEER_CRT 1
+#else
+#define SSL_SERIALIZED_SESSION_CONFIG_KEEP_PEER_CRT 0
+#endif /* MBEDTLS_SSL_SESSION_TICKETS */
+
 #if defined(MBEDTLS_SSL_CLI_C) && defined(MBEDTLS_SSL_SESSION_TICKETS)
 #define SSL_SERIALIZED_SESSION_CONFIG_CLIENT_TICKET 1
 #else
@@ -3686,12 +4043,34 @@
 #define SSL_SERIALIZED_SESSION_CONFIG_TICKET 0
 #endif /* MBEDTLS_SSL_SESSION_TICKETS */
 
+#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
+#define SSL_SERIALIZED_SESSION_CONFIG_SNI 1
+#else
+#define SSL_SERIALIZED_SESSION_CONFIG_SNI 0
+#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */
+
+#if defined(MBEDTLS_SSL_EARLY_DATA)
+#define SSL_SERIALIZED_SESSION_CONFIG_EARLY_DATA 1
+#else
+#define SSL_SERIALIZED_SESSION_CONFIG_EARLY_DATA 0
+#endif /* MBEDTLS_SSL_EARLY_DATA */
+
+#if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT)
+#define SSL_SERIALIZED_SESSION_CONFIG_RECORD_SIZE 1
+#else
+#define SSL_SERIALIZED_SESSION_CONFIG_RECORD_SIZE 0
+#endif /* MBEDTLS_SSL_RECORD_SIZE_LIMIT */
+
 #define SSL_SERIALIZED_SESSION_CONFIG_TIME_BIT          0
 #define SSL_SERIALIZED_SESSION_CONFIG_CRT_BIT           1
 #define SSL_SERIALIZED_SESSION_CONFIG_CLIENT_TICKET_BIT 2
 #define SSL_SERIALIZED_SESSION_CONFIG_MFL_BIT           3
 #define SSL_SERIALIZED_SESSION_CONFIG_ETM_BIT           4
 #define SSL_SERIALIZED_SESSION_CONFIG_TICKET_BIT        5
+#define SSL_SERIALIZED_SESSION_CONFIG_KEEP_PEER_CRT_BIT 6
+#define SSL_SERIALIZED_SESSION_CONFIG_SNI_BIT           7
+#define SSL_SERIALIZED_SESSION_CONFIG_EARLY_DATA_BIT    8
+#define SSL_SERIALIZED_SESSION_CONFIG_RECORD_SIZE_BIT   9
 
 #define SSL_SERIALIZED_SESSION_CONFIG_BITFLAG                           \
     ((uint16_t) (                                                      \
@@ -3701,9 +4080,16 @@
              SSL_SERIALIZED_SESSION_CONFIG_CLIENT_TICKET_BIT) | \
          (SSL_SERIALIZED_SESSION_CONFIG_MFL << SSL_SERIALIZED_SESSION_CONFIG_MFL_BIT) | \
          (SSL_SERIALIZED_SESSION_CONFIG_ETM << SSL_SERIALIZED_SESSION_CONFIG_ETM_BIT) | \
-         (SSL_SERIALIZED_SESSION_CONFIG_TICKET << SSL_SERIALIZED_SESSION_CONFIG_TICKET_BIT)))
+         (SSL_SERIALIZED_SESSION_CONFIG_TICKET << SSL_SERIALIZED_SESSION_CONFIG_TICKET_BIT) | \
+         (SSL_SERIALIZED_SESSION_CONFIG_KEEP_PEER_CRT << \
+             SSL_SERIALIZED_SESSION_CONFIG_KEEP_PEER_CRT_BIT) | \
+         (SSL_SERIALIZED_SESSION_CONFIG_SNI << SSL_SERIALIZED_SESSION_CONFIG_SNI_BIT) | \
+         (SSL_SERIALIZED_SESSION_CONFIG_EARLY_DATA << \
+             SSL_SERIALIZED_SESSION_CONFIG_EARLY_DATA_BIT) | \
+         (SSL_SERIALIZED_SESSION_CONFIG_RECORD_SIZE << \
+             SSL_SERIALIZED_SESSION_CONFIG_RECORD_SIZE_BIT)))
 
-static unsigned char ssl_serialized_session_header[] = {
+static const unsigned char ssl_serialized_session_header[] = {
     MBEDTLS_VERSION_MAJOR,
     MBEDTLS_VERSION_MINOR,
     MBEDTLS_VERSION_PATCH,
@@ -3715,7 +4101,77 @@
  * Serialize a session in the following format:
  * (in the presentation language of TLS, RFC 8446 section 3)
  *
- *  struct {
+ * TLS 1.2 session:
+ *
+ * struct {
+ * #if defined(MBEDTLS_SSL_SESSION_TICKETS)
+ *    opaque ticket<0..2^24-1>;       // length 0 means no ticket
+ *    uint32 ticket_lifetime;
+ * #endif
+ * } ClientOnlyData;
+ *
+ * struct {
+ * #if defined(MBEDTLS_HAVE_TIME)
+ *    uint64 start_time;
+ * #endif
+ *     uint8 session_id_len;           // at most 32
+ *     opaque session_id[32];
+ *     opaque master[48];              // fixed length in the standard
+ *     uint32 verify_result;
+ * #if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE
+ *    opaque peer_cert<0..2^24-1>;    // length 0 means no peer cert
+ * #else
+ *    uint8 peer_cert_digest_type;
+ *    opaque peer_cert_digest<0..2^8-1>
+ * #endif
+ *     select (endpoint) {
+ *         case client: ClientOnlyData;
+ *         case server: uint64 ticket_creation_time;
+ *     };
+ * #if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
+ *    uint8 mfl_code;                 // up to 255 according to standard
+ * #endif
+ * #if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC)
+ *    uint8 encrypt_then_mac;         // 0 or 1
+ * #endif
+ * } serialized_session_tls12;
+ *
+ *
+ * TLS 1.3 Session:
+ *
+ * struct {
+ * #if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
+ *    opaque hostname<0..2^16-1>;
+ * #endif
+ * #if defined(MBEDTLS_HAVE_TIME)
+ *    uint64 ticket_reception_time;
+ * #endif
+ *    uint32 ticket_lifetime;
+ *    opaque ticket<1..2^16-1>;
+ * } ClientOnlyData;
+ *
+ * struct {
+ *    uint32 ticket_age_add;
+ *    uint8 ticket_flags;
+ *    opaque resumption_key<0..255>;
+ * #if defined(MBEDTLS_SSL_EARLY_DATA)
+ *    uint32 max_early_data_size;
+ * #endif
+ * #if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT)
+ *    uint16 record_size_limit;
+ * #endif
+ *    select ( endpoint ) {
+ *         case client: ClientOnlyData;
+ * #if defined(MBEDTLS_HAVE_TIME)
+ *         case server: uint64 ticket_creation_time;
+ * #endif
+ *     };
+ * } serialized_session_tls13;
+ *
+ *
+ * SSL session:
+ *
+ * struct {
  *
  *    opaque mbedtls_version[3];   // library version: major, minor, patch
  *    opaque session_format[2];    // library-version specific 16-bit field
@@ -3733,6 +4189,8 @@
  *    uint8_t minor_ver;           // Protocol minor version. Possible values:
  *                                 // - TLS 1.2 (0x0303)
  *                                 // - TLS 1.3 (0x0304)
+ *    uint8_t endpoint;
+ *    uint16_t ciphersuite;
  *
  *    select (serialized_session.tls_version) {
  *
@@ -3779,11 +4237,16 @@
     }
 
     /*
-     * TLS version identifier
+     * TLS version identifier, endpoint, ciphersuite
      */
-    used += 1;
+    used += 1    /* TLS version */
+            + 1  /* endpoint */
+            + 2; /* ciphersuite */
     if (used <= buf_len) {
         *p++ = MBEDTLS_BYTE_0(session->tls_version);
+        *p++ = session->endpoint;
+        MBEDTLS_PUT_UINT16_BE(session->ciphersuite, p, 0);
+        p += 2;
     }
 
     /* Forward to version-specific serialization routine. */
@@ -3866,12 +4329,15 @@
     }
 
     /*
-     * TLS version identifier
+     * TLS version identifier, endpoint, ciphersuite
      */
-    if (1 > (size_t) (end - p)) {
+    if (4 > (size_t) (end - p)) {
         return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
     }
     session->tls_version = (mbedtls_ssl_protocol_version) (0x0300 | *p++);
+    session->endpoint = *p++;
+    session->ciphersuite = MBEDTLS_GET_UINT16_BE(p, 0);
+    p += 2;
 
     /* Dispatch according to TLS version. */
     remaining_len = (size_t) (end - p);
@@ -4428,7 +4894,7 @@
          (SSL_SERIALIZED_CONTEXT_CONFIG_ALPN << SSL_SERIALIZED_CONTEXT_CONFIG_ALPN_BIT) | \
          0u))
 
-static unsigned char ssl_serialized_context_header[] = {
+static const unsigned char ssl_serialized_context_header[] = {
     MBEDTLS_VERSION_MAJOR,
     MBEDTLS_VERSION_MINOR,
     MBEDTLS_VERSION_PATCH,
@@ -5046,7 +5512,7 @@
  * See the documentation of mbedtls_ssl_conf_curves() for what we promise
  * about this list.
  */
-static uint16_t ssl_preset_default_groups[] = {
+static const uint16_t ssl_preset_default_groups[] = {
 #if defined(MBEDTLS_ECP_HAVE_CURVE25519)
     MBEDTLS_SSL_IANA_TLS_GROUP_X25519,
 #endif
@@ -5097,7 +5563,7 @@
  *   - ssl_tls12_preset* is for TLS 1.2 use only.
  *   - ssl_preset_* is for TLS 1.3 only or hybrid TLS 1.3/1.2 handshakes.
  */
-static uint16_t ssl_preset_default_sig_algs[] = {
+static const uint16_t ssl_preset_default_sig_algs[] = {
 
 #if defined(MBEDTLS_KEY_EXCHANGE_ECDSA_CERT_REQ_ANY_ALLOWED_ENABLED) && \
     defined(MBEDTLS_MD_CAN_SHA256) && \
@@ -5192,7 +5658,7 @@
 #endif /* MBEDTLS_SSL_PROTO_TLS1_2 */
 
 /* NOTICE: see above */
-static uint16_t ssl_preset_suiteb_sig_algs[] = {
+static const uint16_t ssl_preset_suiteb_sig_algs[] = {
 
 #if defined(MBEDTLS_KEY_EXCHANGE_ECDSA_CERT_REQ_ANY_ALLOWED_ENABLED) && \
     defined(MBEDTLS_MD_CAN_SHA256) && \
@@ -5233,7 +5699,7 @@
 
 #endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */
 
-static uint16_t ssl_preset_suiteb_groups[] = {
+static const uint16_t ssl_preset_suiteb_groups[] = {
 #if defined(MBEDTLS_ECP_HAVE_SECP256R1)
     MBEDTLS_SSL_IANA_TLS_GROUP_SECP256R1,
 #endif
@@ -5247,7 +5713,7 @@
 /* Function for checking `ssl_preset_*_sig_algs` and `ssl_tls12_preset_*_sig_algs`
  * to make sure there are no duplicated signature algorithm entries. */
 MBEDTLS_CHECK_RETURN_CRITICAL
-static int ssl_check_no_sig_alg_duplication(uint16_t *sig_algs)
+static int ssl_check_no_sig_alg_duplication(const uint16_t *sig_algs)
 {
     size_t i, j;
     int ret = 0;
@@ -8940,360 +9406,6 @@
 
 #endif /* MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED */
 
-/* Serialization of TLS 1.2 sessions:
- *
- * struct {
- *    uint64 start_time;
- *    uint8 ciphersuite[2];           // defined by the standard
- *    uint8 session_id_len;           // at most 32
- *    opaque session_id[32];
- *    opaque master[48];              // fixed length in the standard
- *    uint32 verify_result;
- *    opaque peer_cert<0..2^24-1>;    // length 0 means no peer cert
- *    opaque ticket<0..2^24-1>;       // length 0 means no ticket
- *    uint32 ticket_lifetime;
- *    uint8 mfl_code;                 // up to 255 according to standard
- *    uint8 encrypt_then_mac;         // 0 or 1
- * } serialized_session_tls12;
- *
- */
-static size_t ssl_tls12_session_save(const mbedtls_ssl_session *session,
-                                     unsigned char *buf,
-                                     size_t buf_len)
-{
-    unsigned char *p = buf;
-    size_t used = 0;
-
-#if defined(MBEDTLS_HAVE_TIME)
-    uint64_t start;
-#endif
-#if defined(MBEDTLS_X509_CRT_PARSE_C)
-#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
-    size_t cert_len;
-#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
-#endif /* MBEDTLS_X509_CRT_PARSE_C */
-
-    /*
-     * Time
-     */
-#if defined(MBEDTLS_HAVE_TIME)
-    used += 8;
-
-    if (used <= buf_len) {
-        start = (uint64_t) session->start;
-
-        MBEDTLS_PUT_UINT64_BE(start, p, 0);
-        p += 8;
-    }
-#endif /* MBEDTLS_HAVE_TIME */
-
-    /*
-     * Basic mandatory fields
-     */
-    used += 2   /* ciphersuite */
-            + 1 /* id_len */
-            + sizeof(session->id)
-            + sizeof(session->master)
-            + 4; /* verify_result */
-
-    if (used <= buf_len) {
-        MBEDTLS_PUT_UINT16_BE(session->ciphersuite, p, 0);
-        p += 2;
-
-        *p++ = MBEDTLS_BYTE_0(session->id_len);
-        memcpy(p, session->id, 32);
-        p += 32;
-
-        memcpy(p, session->master, 48);
-        p += 48;
-
-        MBEDTLS_PUT_UINT32_BE(session->verify_result, p, 0);
-        p += 4;
-    }
-
-    /*
-     * Peer's end-entity certificate
-     */
-#if defined(MBEDTLS_X509_CRT_PARSE_C)
-#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
-    if (session->peer_cert == NULL) {
-        cert_len = 0;
-    } else {
-        cert_len = session->peer_cert->raw.len;
-    }
-
-    used += 3 + cert_len;
-
-    if (used <= buf_len) {
-        *p++ = MBEDTLS_BYTE_2(cert_len);
-        *p++ = MBEDTLS_BYTE_1(cert_len);
-        *p++ = MBEDTLS_BYTE_0(cert_len);
-
-        if (session->peer_cert != NULL) {
-            memcpy(p, session->peer_cert->raw.p, cert_len);
-            p += cert_len;
-        }
-    }
-#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
-    if (session->peer_cert_digest != NULL) {
-        used += 1 /* type */ + 1 /* length */ + session->peer_cert_digest_len;
-        if (used <= buf_len) {
-            *p++ = (unsigned char) session->peer_cert_digest_type;
-            *p++ = (unsigned char) session->peer_cert_digest_len;
-            memcpy(p, session->peer_cert_digest,
-                   session->peer_cert_digest_len);
-            p += session->peer_cert_digest_len;
-        }
-    } else {
-        used += 2;
-        if (used <= buf_len) {
-            *p++ = (unsigned char) MBEDTLS_MD_NONE;
-            *p++ = 0;
-        }
-    }
-#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
-#endif /* MBEDTLS_X509_CRT_PARSE_C */
-
-    /*
-     * Session ticket if any, plus associated data
-     */
-#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C)
-    used += 3 + session->ticket_len + 4; /* len + ticket + lifetime */
-
-    if (used <= buf_len) {
-        *p++ = MBEDTLS_BYTE_2(session->ticket_len);
-        *p++ = MBEDTLS_BYTE_1(session->ticket_len);
-        *p++ = MBEDTLS_BYTE_0(session->ticket_len);
-
-        if (session->ticket != NULL) {
-            memcpy(p, session->ticket, session->ticket_len);
-            p += session->ticket_len;
-        }
-
-        MBEDTLS_PUT_UINT32_BE(session->ticket_lifetime, p, 0);
-        p += 4;
-    }
-#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */
-
-    /*
-     * Misc extension-related info
-     */
-#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
-    used += 1;
-
-    if (used <= buf_len) {
-        *p++ = session->mfl_code;
-    }
-#endif
-
-#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC)
-    used += 1;
-
-    if (used <= buf_len) {
-        *p++ = MBEDTLS_BYTE_0(session->encrypt_then_mac);
-    }
-#endif
-
-    return used;
-}
-
-MBEDTLS_CHECK_RETURN_CRITICAL
-static int ssl_tls12_session_load(mbedtls_ssl_session *session,
-                                  const unsigned char *buf,
-                                  size_t len)
-{
-#if defined(MBEDTLS_HAVE_TIME)
-    uint64_t start;
-#endif
-#if defined(MBEDTLS_X509_CRT_PARSE_C)
-#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
-    size_t cert_len;
-#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
-#endif /* MBEDTLS_X509_CRT_PARSE_C */
-
-    const unsigned char *p = buf;
-    const unsigned char * const end = buf + len;
-
-    /*
-     * Time
-     */
-#if defined(MBEDTLS_HAVE_TIME)
-    if (8 > (size_t) (end - p)) {
-        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-    }
-
-    start = MBEDTLS_GET_UINT64_BE(p, 0);
-    p += 8;
-
-    session->start = (time_t) start;
-#endif /* MBEDTLS_HAVE_TIME */
-
-    /*
-     * Basic mandatory fields
-     */
-    if (2 + 1 + 32 + 48 + 4 > (size_t) (end - p)) {
-        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-    }
-
-    session->ciphersuite = MBEDTLS_GET_UINT16_BE(p, 0);
-    p += 2;
-
-    session->id_len = *p++;
-    memcpy(session->id, p, 32);
-    p += 32;
-
-    memcpy(session->master, p, 48);
-    p += 48;
-
-    session->verify_result = MBEDTLS_GET_UINT32_BE(p, 0);
-    p += 4;
-
-    /* Immediately clear invalid pointer values that have been read, in case
-     * we exit early before we replaced them with valid ones. */
-#if defined(MBEDTLS_X509_CRT_PARSE_C)
-#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
-    session->peer_cert = NULL;
-#else
-    session->peer_cert_digest = NULL;
-#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
-#endif /* MBEDTLS_X509_CRT_PARSE_C */
-#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C)
-    session->ticket = NULL;
-#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */
-
-    /*
-     * Peer certificate
-     */
-#if defined(MBEDTLS_X509_CRT_PARSE_C)
-#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
-    /* Deserialize CRT from the end of the ticket. */
-    if (3 > (size_t) (end - p)) {
-        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-    }
-
-    cert_len = MBEDTLS_GET_UINT24_BE(p, 0);
-    p += 3;
-
-    if (cert_len != 0) {
-        int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-
-        if (cert_len > (size_t) (end - p)) {
-            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-        }
-
-        session->peer_cert = mbedtls_calloc(1, sizeof(mbedtls_x509_crt));
-
-        if (session->peer_cert == NULL) {
-            return MBEDTLS_ERR_SSL_ALLOC_FAILED;
-        }
-
-        mbedtls_x509_crt_init(session->peer_cert);
-
-        if ((ret = mbedtls_x509_crt_parse_der(session->peer_cert,
-                                              p, cert_len)) != 0) {
-            mbedtls_x509_crt_free(session->peer_cert);
-            mbedtls_free(session->peer_cert);
-            session->peer_cert = NULL;
-            return ret;
-        }
-
-        p += cert_len;
-    }
-#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
-    /* Deserialize CRT digest from the end of the ticket. */
-    if (2 > (size_t) (end - p)) {
-        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-    }
-
-    session->peer_cert_digest_type = (mbedtls_md_type_t) *p++;
-    session->peer_cert_digest_len  = (size_t) *p++;
-
-    if (session->peer_cert_digest_len != 0) {
-        const mbedtls_md_info_t *md_info =
-            mbedtls_md_info_from_type(session->peer_cert_digest_type);
-        if (md_info == NULL) {
-            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-        }
-        if (session->peer_cert_digest_len != mbedtls_md_get_size(md_info)) {
-            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-        }
-
-        if (session->peer_cert_digest_len > (size_t) (end - p)) {
-            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-        }
-
-        session->peer_cert_digest =
-            mbedtls_calloc(1, session->peer_cert_digest_len);
-        if (session->peer_cert_digest == NULL) {
-            return MBEDTLS_ERR_SSL_ALLOC_FAILED;
-        }
-
-        memcpy(session->peer_cert_digest, p,
-               session->peer_cert_digest_len);
-        p += session->peer_cert_digest_len;
-    }
-#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
-#endif /* MBEDTLS_X509_CRT_PARSE_C */
-
-    /*
-     * Session ticket and associated data
-     */
-#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C)
-    if (3 > (size_t) (end - p)) {
-        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-    }
-
-    session->ticket_len = MBEDTLS_GET_UINT24_BE(p, 0);
-    p += 3;
-
-    if (session->ticket_len != 0) {
-        if (session->ticket_len > (size_t) (end - p)) {
-            return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-        }
-
-        session->ticket = mbedtls_calloc(1, session->ticket_len);
-        if (session->ticket == NULL) {
-            return MBEDTLS_ERR_SSL_ALLOC_FAILED;
-        }
-
-        memcpy(session->ticket, p, session->ticket_len);
-        p += session->ticket_len;
-    }
-
-    if (4 > (size_t) (end - p)) {
-        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-    }
-
-    session->ticket_lifetime = MBEDTLS_GET_UINT32_BE(p, 0);
-    p += 4;
-#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */
-
-    /*
-     * Misc extension-related info
-     */
-#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
-    if (1 > (size_t) (end - p)) {
-        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-    }
-
-    session->mfl_code = *p++;
-#endif
-
-#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC)
-    if (1 > (size_t) (end - p)) {
-        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-    }
-
-    session->encrypt_then_mac = *p++;
-#endif
-
-    /* Done, should have consumed entire buffer */
-    if (p != end) {
-        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-    }
-
-    return 0;
-}
 #endif /* MBEDTLS_SSL_PROTO_TLS1_2 */
 
 int mbedtls_ssl_validate_ciphersuite(
diff --git a/library/ssl_tls12_client.c b/library/ssl_tls12_client.c
index 0c5af87..eac6a3a 100644
--- a/library/ssl_tls12_client.c
+++ b/library/ssl_tls12_client.c
@@ -14,7 +14,7 @@
 #include "mbedtls/ssl.h"
 #include "ssl_client.h"
 #include "ssl_misc.h"
-#include "mbedtls/debug.h"
+#include "debug_internal.h"
 #include "mbedtls/error.h"
 #include "mbedtls/constant_time.h"
 
@@ -1268,6 +1268,7 @@
     ssl->tls_version = (mbedtls_ssl_protocol_version) mbedtls_ssl_read_version(buf,
                                                                                ssl->conf->transport);
     ssl->session_negotiate->tls_version = ssl->tls_version;
+    ssl->session_negotiate->endpoint = ssl->conf->endpoint;
 
     if (ssl->tls_version < ssl->conf->min_tls_version ||
         ssl->tls_version > ssl->conf->max_tls_version) {
@@ -2005,9 +2006,9 @@
         return MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH;
     }
 
-#if defined(MBEDTLS_ECP_C)
+#if !defined(MBEDTLS_PK_USE_PSA_EC_DATA)
     const mbedtls_ecp_keypair *peer_key = mbedtls_pk_ec_ro(*peer_pk);
-#endif /* MBEDTLS_ECP_C */
+#endif /* !defined(MBEDTLS_PK_USE_PSA_EC_DATA) */
 
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
     uint16_t tls_id = 0;
diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c
index 5a9f6ca..b49a8ae 100644
--- a/library/ssl_tls12_server.c
+++ b/library/ssl_tls12_server.c
@@ -13,7 +13,7 @@
 
 #include "mbedtls/ssl.h"
 #include "ssl_misc.h"
-#include "mbedtls/debug.h"
+#include "debug_internal.h"
 #include "mbedtls/error.h"
 #include "mbedtls/platform_util.h"
 #include "constant_time_internal.h"
@@ -1161,6 +1161,7 @@
     ssl->tls_version = (mbedtls_ssl_protocol_version) mbedtls_ssl_read_version(buf,
                                                                                ssl->conf->transport);
     ssl->session_negotiate->tls_version = ssl->tls_version;
+    ssl->session_negotiate->endpoint = ssl->conf->endpoint;
 
     if (ssl->tls_version != MBEDTLS_SSL_VERSION_TLS1_2) {
         MBEDTLS_SSL_DEBUG_MSG(1, ("server only supports TLS 1.2"));
@@ -2177,11 +2178,6 @@
     }
 #endif /* MBEDTLS_SSL_DTLS_HELLO_VERIFY */
 
-    if (ssl->conf->f_rng == NULL) {
-        MBEDTLS_SSL_DEBUG_MSG(1, ("no RNG provided"));
-        return MBEDTLS_ERR_SSL_NO_RNG;
-    }
-
     /*
      *     0  .   0   handshake type
      *     1  .   3   handshake length
@@ -2702,8 +2698,7 @@
                              PSA_KEY_TYPE_ECC_KEY_PAIR(ssl->handshake->xxdh_psa_type));
             psa_set_key_bits(&key_attributes, ssl->handshake->xxdh_psa_bits);
 
-            key_len = PSA_BITS_TO_BYTES(key->grp.pbits);
-            ret = mbedtls_ecp_write_key(key, buf, key_len);
+            ret = mbedtls_ecp_write_key_ext(key, &key_len, buf, sizeof(buf));
             if (ret != 0) {
                 mbedtls_platform_zeroize(buf, sizeof(buf));
                 break;
@@ -4281,6 +4276,9 @@
      * 10 .  9+n ticket content
      */
 
+#if defined(MBEDTLS_HAVE_TIME)
+    ssl->session_negotiate->ticket_creation_time = mbedtls_ms_time();
+#endif
     if ((ret = ssl->conf->f_ticket_write(ssl->conf->p_ticket,
                                          ssl->session_negotiate,
                                          ssl->out_msg + 10,
diff --git a/library/ssl_tls13_client.c b/library/ssl_tls13_client.c
index a3d33a3..88d6c9e 100644
--- a/library/ssl_tls13_client.c
+++ b/library/ssl_tls13_client.c
@@ -11,7 +11,7 @@
 
 #include <string.h>
 
-#include "mbedtls/debug.h"
+#include "debug_internal.h"
 #include "mbedtls/error.h"
 #include "mbedtls/platform.h"
 
@@ -1180,25 +1180,29 @@
 #endif
 
 #if defined(MBEDTLS_SSL_EARLY_DATA)
-    if (mbedtls_ssl_conf_tls13_is_some_psk_enabled(ssl) &&
-        ssl_tls13_early_data_has_valid_ticket(ssl) &&
-        ssl->conf->early_data_enabled == MBEDTLS_SSL_EARLY_DATA_ENABLED) {
+    /* In the first ClientHello, write the early data indication extension if
+     * necessary and update the early data status.
+     * If an HRR has been received and thus we are currently writing the
+     * second ClientHello, the second ClientHello must not contain an early
+     * data extension and the early data status must stay as it is:
+     * MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT or
+     * MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED.
+     */
+    if (!ssl->handshake->hello_retry_request_flag) {
+        if (mbedtls_ssl_conf_tls13_is_some_psk_enabled(ssl) &&
+            ssl_tls13_early_data_has_valid_ticket(ssl) &&
+            ssl->conf->early_data_enabled == MBEDTLS_SSL_EARLY_DATA_ENABLED) {
+            ret = mbedtls_ssl_tls13_write_early_data_ext(
+                ssl, 0, p, end, &ext_len);
+            if (ret != 0) {
+                return ret;
+            }
+            p += ext_len;
 
-        ret = mbedtls_ssl_tls13_write_early_data_ext(
-            ssl, 0, p, end, &ext_len);
-        if (ret != 0) {
-            return ret;
+            ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_SENT;
+        } else {
+            ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT;
         }
-        p += ext_len;
-
-        /* Initializes the status to `rejected`. It will be updated to
-         * `accepted` if the EncryptedExtension message contain an early data
-         * indication extension.
-         */
-        ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED;
-    } else {
-        MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip write early_data extension"));
-        ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT;
     }
 #endif /* MBEDTLS_SSL_EARLY_DATA */
 
@@ -1235,11 +1239,7 @@
     size_t psk_len;
     const mbedtls_ssl_ciphersuite_t *ciphersuite_info;
 
-    if (ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED) {
-#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE)
-        mbedtls_ssl_handshake_set_state(
-            ssl, MBEDTLS_SSL_CLIENT_CCS_AFTER_CLIENT_HELLO);
-#endif
+    if (ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_SENT) {
         MBEDTLS_SSL_DEBUG_MSG(
             1, ("Set hs psk for early data when writing the first psk"));
 
@@ -1294,6 +1294,16 @@
             return ret;
         }
 
+#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE)
+        mbedtls_ssl_handshake_set_state(
+            ssl, MBEDTLS_SSL_CLIENT_CCS_AFTER_CLIENT_HELLO);
+#else
+        MBEDTLS_SSL_DEBUG_MSG(
+            1, ("Switch to early data keys for outbound traffic"));
+        mbedtls_ssl_set_outbound_transform(
+            ssl, ssl->handshake->transform_earlydata);
+        ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_WRITE;
+#endif
     }
 #endif /* MBEDTLS_SSL_EARLY_DATA */
     return 0;
@@ -1476,10 +1486,8 @@
         return SSL_SERVER_HELLO_TLS1_2;
     }
 
-#if defined(MBEDTLS_SSL_SESSION_TICKETS)
-    ssl->session_negotiate->endpoint = ssl->conf->endpoint;
     ssl->session_negotiate->tls_version = ssl->tls_version;
-#endif /* MBEDTLS_SSL_SESSION_TICKETS */
+    ssl->session_negotiate->endpoint = ssl->conf->endpoint;
 
     handshake->received_extensions = MBEDTLS_SSL_EXT_MASK_NONE;
 
@@ -1495,7 +1503,7 @@
              * to a HelloRetryRequest), it MUST abort the handshake with an
              * "unexpected_message" alert.
              */
-            if (handshake->hello_retry_request_count > 0) {
+            if (handshake->hello_retry_request_flag) {
                 MBEDTLS_SSL_DEBUG_MSG(1, ("Multiple HRRs received"));
                 MBEDTLS_SSL_PEND_FATAL_ALERT(
                     MBEDTLS_SSL_ALERT_MSG_UNEXPECTED_MESSAGE,
@@ -1517,7 +1525,7 @@
                 return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER;
             }
 
-            handshake->hello_retry_request_count++;
+            handshake->hello_retry_request_flag = 1;
 
             break;
     }
@@ -1672,7 +1680,7 @@
      * proposed in the HRR, we abort the handshake and send an
      * "illegal_parameter" alert.
      */
-    else if ((!is_hrr) && (handshake->hello_retry_request_count > 0) &&
+    else if ((!is_hrr) && handshake->hello_retry_request_flag &&
              (cipher_suite != ssl->session_negotiate->ciphersuite)) {
         fatal_alert = MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER;
     }
@@ -1965,6 +1973,13 @@
     }
 
     ssl->session_negotiate->ciphersuite = ssl->handshake->ciphersuite_info->id;
+
+#if defined(MBEDTLS_SSL_EARLY_DATA)
+    if (ssl->early_data_status != MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT) {
+        ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED;
+    }
+#endif
+
     return 0;
 }
 
@@ -2224,6 +2239,8 @@
         }
 
         ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED;
+    } else if (ssl->early_data_status != MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT) {
+        ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED;
     }
 #endif
 
@@ -2261,6 +2278,7 @@
 
 }
 
+#if defined(MBEDTLS_SSL_EARLY_DATA)
 /*
  * Handler for MBEDTLS_SSL_END_OF_EARLY_DATA
  *
@@ -2299,6 +2317,32 @@
     return ret;
 }
 
+int mbedtls_ssl_get_early_data_status(mbedtls_ssl_context *ssl)
+{
+    if ((ssl->conf->endpoint != MBEDTLS_SSL_IS_CLIENT) ||
+        (!mbedtls_ssl_is_handshake_over(ssl))) {
+        return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+    }
+
+    switch (ssl->early_data_status) {
+        case MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT:
+            return MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT;
+            break;
+
+        case MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED:
+            return MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED;
+            break;
+
+        case MBEDTLS_SSL_EARLY_DATA_STATUS_SERVER_FINISHED_RECEIVED:
+            return MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED;
+            break;
+
+        default:
+            return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
+    }
+}
+#endif /* MBEDTLS_SSL_EARLY_DATA */
+
 #if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED)
 /*
  * STATE HANDLING: CertificateRequest
@@ -2561,9 +2605,8 @@
 
 #if defined(MBEDTLS_SSL_EARLY_DATA)
     if (ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED) {
+        ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_SERVER_FINISHED_RECEIVED;
         mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_END_OF_EARLY_DATA);
-    } else if (ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED) {
-        mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_CERTIFICATE);
     } else
 #endif /* MBEDTLS_SSL_EARLY_DATA */
     {
@@ -3022,9 +3065,11 @@
             ret = ssl_tls13_process_server_finished(ssl);
             break;
 
+#if defined(MBEDTLS_SSL_EARLY_DATA)
         case MBEDTLS_SSL_END_OF_EARLY_DATA:
             ret = ssl_tls13_write_end_of_early_data(ssl);
             break;
+#endif
 
         case MBEDTLS_SSL_CLIENT_CERTIFICATE:
             ret = ssl_tls13_write_client_certificate(ssl);
@@ -3054,32 +3099,34 @@
 #if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE)
         case MBEDTLS_SSL_CLIENT_CCS_BEFORE_2ND_CLIENT_HELLO:
             ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl);
-            if (ret == 0) {
-                mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_HELLO);
+            if (ret != 0) {
+                break;
             }
+            mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_HELLO);
             break;
 
         case MBEDTLS_SSL_CLIENT_CCS_AFTER_SERVER_FINISHED:
             ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl);
-            if (ret == 0) {
-                mbedtls_ssl_handshake_set_state(
-                    ssl, MBEDTLS_SSL_CLIENT_CERTIFICATE);
+            if (ret != 0) {
+                break;
             }
+            mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_CERTIFICATE);
             break;
 
+#if defined(MBEDTLS_SSL_EARLY_DATA)
         case MBEDTLS_SSL_CLIENT_CCS_AFTER_CLIENT_HELLO:
             ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl);
             if (ret == 0) {
                 mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_SERVER_HELLO);
 
-#if defined(MBEDTLS_SSL_EARLY_DATA)
                 MBEDTLS_SSL_DEBUG_MSG(
                     1, ("Switch to early data keys for outbound traffic"));
                 mbedtls_ssl_set_outbound_transform(
                     ssl, ssl->handshake->transform_earlydata);
-#endif
+                ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_WRITE;
             }
             break;
+#endif /* MBEDTLS_SSL_EARLY_DATA */
 #endif /* MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE */
 
 #if defined(MBEDTLS_SSL_SESSION_TICKETS)
diff --git a/library/ssl_tls13_generic.c b/library/ssl_tls13_generic.c
index 04ecd8b..d448a05 100644
--- a/library/ssl_tls13_generic.c
+++ b/library/ssl_tls13_generic.c
@@ -12,7 +12,7 @@
 #include <string.h>
 
 #include "mbedtls/error.h"
-#include "mbedtls/debug.h"
+#include "debug_internal.h"
 #include "mbedtls/oid.h"
 #include "mbedtls/platform.h"
 #include "mbedtls/constant_time.h"
@@ -1379,6 +1379,12 @@
 
     MBEDTLS_SSL_DEBUG_MSG(2, ("=> write change cipher spec"));
 
+    /* Only one CCS to send. */
+    if (ssl->handshake->ccs_sent) {
+        ret = 0;
+        goto cleanup;
+    }
+
     /* Write CCS message */
     MBEDTLS_SSL_PROC_CHK(ssl_tls13_write_change_cipher_spec_body(
                              ssl, ssl->out_msg,
@@ -1390,6 +1396,8 @@
     /* Dispatch message */
     MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_write_record(ssl, 0));
 
+    ssl->handshake->ccs_sent = 1;
+
 cleanup:
 
     MBEDTLS_SSL_DEBUG_MSG(2, ("<= write change cipher spec"));
@@ -1446,6 +1454,54 @@
 
     return 0;
 }
+
+#if defined(MBEDTLS_SSL_SRV_C)
+int mbedtls_ssl_tls13_check_early_data_len(mbedtls_ssl_context *ssl,
+                                           size_t early_data_len)
+{
+    /*
+     * This function should be called only while an handshake is in progress
+     * and thus a session under negotiation. Add a sanity check to detect a
+     * misuse.
+     */
+    if (ssl->session_negotiate == NULL) {
+        return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
+    }
+
+    /* RFC 8446 section 4.6.1
+     *
+     * A server receiving more than max_early_data_size bytes of 0-RTT data
+     * SHOULD terminate the connection with an "unexpected_message" alert.
+     * Note that if it is still possible to send early_data_len bytes of early
+     * data, it means that early_data_len is smaller than max_early_data_size
+     * (type uint32_t) and can fit in an uint32_t. We use this further
+     * down.
+     */
+    if (early_data_len >
+        (ssl->session_negotiate->max_early_data_size -
+         ssl->total_early_data_size)) {
+
+        MBEDTLS_SSL_DEBUG_MSG(
+            2, ("EarlyData: Too much early data received, %u + %" MBEDTLS_PRINTF_SIZET " > %u",
+                ssl->total_early_data_size, early_data_len,
+                ssl->session_negotiate->max_early_data_size));
+
+        MBEDTLS_SSL_PEND_FATAL_ALERT(
+            MBEDTLS_SSL_ALERT_MSG_UNEXPECTED_MESSAGE,
+            MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE);
+        return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE;
+    }
+
+    /*
+     * early_data_len has been checked to be less than max_early_data_size
+     * that is uint32_t. Its cast to an uint32_t below is thus safe. We need
+     * the cast to appease some compilers.
+     */
+    ssl->total_early_data_size += (uint32_t) early_data_len;
+
+    return 0;
+}
+#endif /* MBEDTLS_SSL_SRV_C */
 #endif /* MBEDTLS_SSL_EARLY_DATA */
 
 /* Reset SSL context and update hash for handling HRR.
@@ -1539,26 +1595,36 @@
     uint16_t tls_id, size_t *bits, psa_key_type_t *key_type)
 {
     switch (tls_id) {
+#if defined(PSA_WANT_DH_RFC7919_2048)
         case MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE2048:
             *bits = 2048;
             *key_type = PSA_KEY_TYPE_DH_KEY_PAIR(PSA_DH_FAMILY_RFC7919);
             return PSA_SUCCESS;
+#endif /* PSA_WANT_DH_RFC7919_2048 */
+#if defined(PSA_WANT_DH_RFC7919_3072)
         case MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE3072:
             *bits = 3072;
             *key_type =  PSA_KEY_TYPE_DH_KEY_PAIR(PSA_DH_FAMILY_RFC7919);
             return PSA_SUCCESS;
+#endif /* PSA_WANT_DH_RFC7919_3072 */
+#if defined(PSA_WANT_DH_RFC7919_4096)
         case MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE4096:
             *bits = 4096;
             *key_type =  PSA_KEY_TYPE_DH_KEY_PAIR(PSA_DH_FAMILY_RFC7919);
             return PSA_SUCCESS;
+#endif /* PSA_WANT_DH_RFC7919_4096 */
+#if defined(PSA_WANT_DH_RFC7919_6144)
         case MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE6144:
             *bits = 6144;
             *key_type =  PSA_KEY_TYPE_DH_KEY_PAIR(PSA_DH_FAMILY_RFC7919);
             return PSA_SUCCESS;
+#endif /* PSA_WANT_DH_RFC7919_6144 */
+#if defined(PSA_WANT_DH_RFC7919_8192)
         case MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE8192:
             *bits = 8192;
             *key_type =  PSA_KEY_TYPE_DH_KEY_PAIR(PSA_DH_FAMILY_RFC7919);
             return PSA_SUCCESS;
+#endif /* PSA_WANT_DH_RFC7919_8192 */
         default:
             return PSA_ERROR_NOT_SUPPORTED;
     }
diff --git a/library/ssl_tls13_keys.c b/library/ssl_tls13_keys.c
index edb453c..739414e 100644
--- a/library/ssl_tls13_keys.c
+++ b/library/ssl_tls13_keys.c
@@ -13,7 +13,7 @@
 #include <string.h>
 
 #include "mbedtls/hkdf.h"
-#include "mbedtls/debug.h"
+#include "debug_internal.h"
 #include "mbedtls/error.h"
 #include "mbedtls/platform.h"
 
diff --git a/library/ssl_tls13_server.c b/library/ssl_tls13_server.c
index 904bb5b..887c5c6 100644
--- a/library/ssl_tls13_server.c
+++ b/library/ssl_tls13_server.c
@@ -9,7 +9,7 @@
 
 #if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_PROTO_TLS1_3)
 
-#include "mbedtls/debug.h"
+#include "debug_internal.h"
 #include "mbedtls/error.h"
 #include "mbedtls/platform.h"
 #include "mbedtls/constant_time.h"
@@ -39,6 +39,63 @@
     return ciphersuite_info;
 }
 
+static void ssl_tls13_select_ciphersuite(
+    mbedtls_ssl_context *ssl,
+    const unsigned char *cipher_suites,
+    const unsigned char *cipher_suites_end,
+    int psk_ciphersuite_id,
+    psa_algorithm_t psk_hash_alg,
+    const mbedtls_ssl_ciphersuite_t **selected_ciphersuite_info)
+{
+    *selected_ciphersuite_info = NULL;
+
+    /*
+     * In a compliant ClientHello the byte-length of the list of ciphersuites
+     * is even and this function relies on this fact. This should have been
+     * checked in the main ClientHello parsing function. Double check here.
+     */
+    if ((cipher_suites_end - cipher_suites) & 1) {
+        return;
+    }
+
+    for (const unsigned char *p = cipher_suites;
+         p < cipher_suites_end; p += 2) {
+        /*
+         * "cipher_suites_end - p is even" is an invariant of the loop. As
+         * cipher_suites_end - p > 0, we have cipher_suites_end - p >= 2 and it
+         * is thus safe to read two bytes.
+         */
+        uint16_t id = MBEDTLS_GET_UINT16_BE(p, 0);
+
+        const mbedtls_ssl_ciphersuite_t *info =
+            ssl_tls13_validate_peer_ciphersuite(ssl, id);
+        if (info == NULL) {
+            continue;
+        }
+
+        /*
+         * If a valid PSK ciphersuite identifier has been passed in, we want
+         * an exact match.
+         */
+        if (psk_ciphersuite_id != 0) {
+            if (id != psk_ciphersuite_id) {
+                continue;
+            }
+        } else if (psk_hash_alg != PSA_ALG_NONE) {
+            if (mbedtls_md_psa_alg_from_type((mbedtls_md_type_t) info->mac) !=
+                psk_hash_alg) {
+                continue;
+            }
+        }
+
+        *selected_ciphersuite_info = info;
+        return;
+    }
+
+    MBEDTLS_SSL_DEBUG_MSG(2, ("No matched ciphersuite, psk_ciphersuite_id=%x, psk_hash_alg=%x",
+                              (unsigned) psk_ciphersuite_id, psk_hash_alg));
+}
+
 #if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED)
 /* From RFC 8446:
  *
@@ -90,8 +147,30 @@
     return 0;
 }
 
-#define SSL_TLS1_3_OFFERED_PSK_NOT_MATCH   1
-#define SSL_TLS1_3_OFFERED_PSK_MATCH       0
+/*
+ * Non-error return values of
+ * ssl_tls13_offered_psks_check_identity_match_ticket() and
+ * ssl_tls13_offered_psks_check_identity_match(). They are positive to
+ * not collide with error codes that are negative. Zero
+ * (SSL_TLS1_3_PSK_IDENTITY_MATCH) in case of success as it may be propagated
+ * up by the callers of this function as a generic success condition.
+ *
+ * The return value SSL_TLS1_3_PSK_IDENTITY_MATCH_BUT_PSK_NOT_USABLE means
+ * that the pre-shared-key identity matches that of a ticket or an externally-
+ * provisioned pre-shared-key. We have thus been able to retrieve the
+ * attributes of the pre-shared-key but at least one of them does not meet
+ * some criteria and the pre-shared-key cannot be used. For example, a ticket
+ * is expired or its version is not TLS 1.3. Note eventually that the return
+ * value SSL_TLS1_3_PSK_IDENTITY_MATCH_BUT_PSK_NOT_USABLE does not have
+ * anything to do with binder check. A binder check is done only when a
+ * suitable pre-shared-key has been selected and only for that selected
+ * pre-shared-key: if the binder check fails, we fail the handshake and we do
+ * not try to find another pre-shared-key for which the binder check would
+ * succeed as recommended by the specification.
+ */
+#define SSL_TLS1_3_PSK_IDENTITY_DOES_NOT_MATCH 2
+#define SSL_TLS1_3_PSK_IDENTITY_MATCH_BUT_PSK_NOT_USABLE 1
+#define SSL_TLS1_3_PSK_IDENTITY_MATCH 0
 
 #if defined(MBEDTLS_SSL_SESSION_TICKETS)
 MBEDTLS_CHECK_RETURN_CRITICAL
@@ -109,7 +188,6 @@
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     unsigned char *ticket_buffer;
-    unsigned int key_exchanges;
 #if defined(MBEDTLS_HAVE_TIME)
     mbedtls_ms_time_t now;
     mbedtls_ms_time_t server_age;
@@ -123,7 +201,7 @@
 
     /* Ticket parser is not configured, Skip */
     if (ssl->conf->f_ticket_parse == NULL || identity_len == 0) {
-        return 0;
+        return SSL_TLS1_3_PSK_IDENTITY_DOES_NOT_MATCH;
     }
 
     /* We create a copy of the encrypted ticket since the ticket parsing
@@ -133,63 +211,51 @@
      */
     ticket_buffer = mbedtls_calloc(1, identity_len);
     if (ticket_buffer == NULL) {
-        MBEDTLS_SSL_DEBUG_MSG(1, ("buffer too small"));
         return MBEDTLS_ERR_SSL_ALLOC_FAILED;
     }
     memcpy(ticket_buffer, identity, identity_len);
 
-    if ((ret = ssl->conf->f_ticket_parse(ssl->conf->p_ticket,
-                                         session,
-                                         ticket_buffer, identity_len)) != 0) {
-        if (ret == MBEDTLS_ERR_SSL_INVALID_MAC) {
-            MBEDTLS_SSL_DEBUG_MSG(3, ("ticket is not authentic"));
-        } else if (ret == MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED) {
+    ret = ssl->conf->f_ticket_parse(ssl->conf->p_ticket,
+                                    session,
+                                    ticket_buffer, identity_len);
+    switch (ret) {
+        case 0:
+            ret = SSL_TLS1_3_PSK_IDENTITY_MATCH;
+            break;
+
+        case MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED:
             MBEDTLS_SSL_DEBUG_MSG(3, ("ticket is expired"));
-        } else {
+            ret = SSL_TLS1_3_PSK_IDENTITY_MATCH_BUT_PSK_NOT_USABLE;
+            break;
+
+        case MBEDTLS_ERR_SSL_INVALID_MAC:
+            MBEDTLS_SSL_DEBUG_MSG(3, ("ticket is not authentic"));
+            ret = SSL_TLS1_3_PSK_IDENTITY_DOES_NOT_MATCH;
+            break;
+
+        default:
             MBEDTLS_SSL_DEBUG_RET(1, "ticket_parse", ret);
-        }
+            ret = SSL_TLS1_3_PSK_IDENTITY_DOES_NOT_MATCH;
     }
 
     /* We delete the temporary buffer */
     mbedtls_free(ticket_buffer);
 
-    if (ret == 0 && session->tls_version != MBEDTLS_SSL_VERSION_TLS1_3) {
-        MBEDTLS_SSL_DEBUG_MSG(3, ("Ticket TLS version is not 1.3."));
-        /* TODO: Define new return value for this case. */
-        ret = MBEDTLS_ERR_SSL_BAD_PROTOCOL_VERSION;
-    }
-
-    if (ret != 0) {
+    if (ret != SSL_TLS1_3_PSK_IDENTITY_MATCH) {
         goto exit;
     }
 
-    /* RFC 8446 section 4.2.9
-     *
-     * Servers SHOULD NOT send NewSessionTicket with tickets that are not
-     * compatible with the advertised modes; however, if a server does so,
-     * the impact will just be that the client's attempts at resumption fail.
-     *
-     * We regard the ticket with incompatible key exchange modes as not match.
+    /*
+     * The identity matches that of a ticket. Now check that it has suitable
+     * attributes and bet it will not be the case.
      */
-    ret = MBEDTLS_ERR_ERROR_GENERIC_ERROR;
-    MBEDTLS_SSL_PRINT_TICKET_FLAGS(4, session->ticket_flags);
+    ret = SSL_TLS1_3_PSK_IDENTITY_MATCH_BUT_PSK_NOT_USABLE;
 
-    key_exchanges = 0;
-    if (mbedtls_ssl_tls13_session_ticket_allow_psk_ephemeral(session) &&
-        ssl_tls13_key_exchange_is_psk_ephemeral_available(ssl)) {
-        key_exchanges |= MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL;
-    }
-    if (mbedtls_ssl_tls13_session_ticket_allow_psk(session) &&
-        ssl_tls13_key_exchange_is_psk_available(ssl)) {
-        key_exchanges |= MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK;
-    }
-
-    if (key_exchanges == 0) {
-        MBEDTLS_SSL_DEBUG_MSG(3, ("No suitable key exchange mode"));
+    if (session->tls_version != MBEDTLS_SSL_VERSION_TLS1_3) {
+        MBEDTLS_SSL_DEBUG_MSG(3, ("Ticket TLS version is not 1.3."));
         goto exit;
     }
 
-    ret = MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED;
 #if defined(MBEDTLS_HAVE_TIME)
     now = mbedtls_ms_time();
 
@@ -242,13 +308,15 @@
                 age_diff));
         goto exit;
     }
-
-    ret = 0;
-
 #endif /* MBEDTLS_HAVE_TIME */
 
+    /*
+     * All good, we have found a suitable ticket.
+     */
+    ret = SSL_TLS1_3_PSK_IDENTITY_MATCH;
+
 exit:
-    if (ret != 0) {
+    if (ret != SSL_TLS1_3_PSK_IDENTITY_MATCH) {
         mbedtls_ssl_session_free(session);
     }
 
@@ -273,13 +341,11 @@
     *psk_type = MBEDTLS_SSL_TLS1_3_PSK_EXTERNAL;
 
     MBEDTLS_SSL_DEBUG_BUF(4, "identity", identity, identity_len);
-    ssl->handshake->resume = 0;
 
 #if defined(MBEDTLS_SSL_SESSION_TICKETS)
-    if (ssl_tls13_offered_psks_check_identity_match_ticket(
-            ssl, identity, identity_len, obfuscated_ticket_age,
-            session) == SSL_TLS1_3_OFFERED_PSK_MATCH) {
-        ssl->handshake->resume = 1;
+    ret = ssl_tls13_offered_psks_check_identity_match_ticket(
+        ssl, identity, identity_len, obfuscated_ticket_age, session);
+    if (ret == SSL_TLS1_3_PSK_IDENTITY_MATCH) {
         *psk_type = MBEDTLS_SSL_TLS1_3_PSK_RESUMPTION;
         ret = mbedtls_ssl_set_hs_psk(ssl,
                                      session->resumption_key,
@@ -294,7 +360,9 @@
                               session->resumption_key_len);
         MBEDTLS_SSL_DEBUG_MSG(4, ("ticket: obfuscated_ticket_age: %u",
                                   (unsigned) obfuscated_ticket_age));
-        return SSL_TLS1_3_OFFERED_PSK_MATCH;
+        return SSL_TLS1_3_PSK_IDENTITY_MATCH;
+    } else if (ret == SSL_TLS1_3_PSK_IDENTITY_MATCH_BUT_PSK_NOT_USABLE) {
+        return SSL_TLS1_3_PSK_IDENTITY_MATCH_BUT_PSK_NOT_USABLE;
     }
 #endif /* MBEDTLS_SSL_SESSION_TICKETS */
 
@@ -302,9 +370,9 @@
     if (ssl->conf->f_psk != NULL) {
         if (ssl->conf->f_psk(
                 ssl->conf->p_psk, ssl, identity, identity_len) == 0) {
-            return SSL_TLS1_3_OFFERED_PSK_MATCH;
+            return SSL_TLS1_3_PSK_IDENTITY_MATCH;
         }
-        return SSL_TLS1_3_OFFERED_PSK_NOT_MATCH;
+        return SSL_TLS1_3_PSK_IDENTITY_DOES_NOT_MATCH;
     }
 
     MBEDTLS_SSL_DEBUG_BUF(5, "identity", identity, identity_len);
@@ -318,12 +386,20 @@
             MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_set_hs_psk", ret);
             return ret;
         }
-        return SSL_TLS1_3_OFFERED_PSK_MATCH;
+        return SSL_TLS1_3_PSK_IDENTITY_MATCH;
     }
 
-    return SSL_TLS1_3_OFFERED_PSK_NOT_MATCH;
+    return SSL_TLS1_3_PSK_IDENTITY_DOES_NOT_MATCH;
 }
 
+/*
+ * Non-error return values of ssl_tls13_offered_psks_check_binder_match().
+ * They are positive to not collide with error codes that are negative. Zero
+ * (SSL_TLS1_3_BINDER_MATCH) in case of success as it may be propagated up
+ * by the callers of this function as a generic success condition.
+ */
+#define SSL_TLS1_3_BINDER_DOES_NOT_MATCH 1
+#define SSL_TLS1_3_BINDER_MATCH 0
 MBEDTLS_CHECK_RETURN_CRITICAL
 static int ssl_tls13_offered_psks_check_binder_match(
     mbedtls_ssl_context *ssl,
@@ -368,100 +444,16 @@
     MBEDTLS_SSL_DEBUG_BUF(3, "psk binder ( received ): ", binder, binder_len);
 
     if (mbedtls_ct_memcmp(server_computed_binder, binder, binder_len) == 0) {
-        return SSL_TLS1_3_OFFERED_PSK_MATCH;
+        return SSL_TLS1_3_BINDER_MATCH;
     }
 
     mbedtls_platform_zeroize(server_computed_binder,
                              sizeof(server_computed_binder));
-    return SSL_TLS1_3_OFFERED_PSK_NOT_MATCH;
-}
-
-MBEDTLS_CHECK_RETURN_CRITICAL
-static int ssl_tls13_select_ciphersuite_for_psk(
-    mbedtls_ssl_context *ssl,
-    const unsigned char *cipher_suites,
-    const unsigned char *cipher_suites_end,
-    uint16_t *selected_ciphersuite,
-    const mbedtls_ssl_ciphersuite_t **selected_ciphersuite_info)
-{
-    psa_algorithm_t psk_hash_alg = PSA_ALG_SHA_256;
-
-    *selected_ciphersuite = 0;
-    *selected_ciphersuite_info = NULL;
-
-    /* RFC 8446, page 55.
-     *
-     * For externally established PSKs, the Hash algorithm MUST be set when the
-     * PSK is established or default to SHA-256 if no such algorithm is defined.
-     *
-     */
-
-    /*
-     * Search for a matching ciphersuite
-     */
-    for (const unsigned char *p = cipher_suites;
-         p < cipher_suites_end; p += 2) {
-        uint16_t cipher_suite;
-        const mbedtls_ssl_ciphersuite_t *ciphersuite_info;
-
-        cipher_suite = MBEDTLS_GET_UINT16_BE(p, 0);
-        ciphersuite_info = ssl_tls13_validate_peer_ciphersuite(ssl,
-                                                               cipher_suite);
-        if (ciphersuite_info == NULL) {
-            continue;
-        }
-
-        /* MAC of selected ciphersuite MUST be same with PSK binder if exist.
-         * Otherwise, client should reject.
-         */
-        if (psk_hash_alg ==
-            mbedtls_md_psa_alg_from_type((mbedtls_md_type_t) ciphersuite_info->mac)) {
-            *selected_ciphersuite = cipher_suite;
-            *selected_ciphersuite_info = ciphersuite_info;
-            return 0;
-        }
-    }
-    MBEDTLS_SSL_DEBUG_MSG(2, ("No matched ciphersuite"));
-    return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE;
+    return SSL_TLS1_3_BINDER_DOES_NOT_MATCH;
 }
 
 #if defined(MBEDTLS_SSL_SESSION_TICKETS)
 MBEDTLS_CHECK_RETURN_CRITICAL
-static int ssl_tls13_select_ciphersuite_for_resumption(
-    mbedtls_ssl_context *ssl,
-    const unsigned char *cipher_suites,
-    const unsigned char *cipher_suites_end,
-    mbedtls_ssl_session *session,
-    uint16_t *selected_ciphersuite,
-    const mbedtls_ssl_ciphersuite_t **selected_ciphersuite_info)
-{
-
-    *selected_ciphersuite = 0;
-    *selected_ciphersuite_info = NULL;
-    for (const unsigned char *p = cipher_suites; p < cipher_suites_end; p += 2) {
-        uint16_t cipher_suite = MBEDTLS_GET_UINT16_BE(p, 0);
-        const mbedtls_ssl_ciphersuite_t *ciphersuite_info;
-
-        if (cipher_suite != session->ciphersuite) {
-            continue;
-        }
-
-        ciphersuite_info = ssl_tls13_validate_peer_ciphersuite(ssl,
-                                                               cipher_suite);
-        if (ciphersuite_info == NULL) {
-            continue;
-        }
-
-        *selected_ciphersuite = cipher_suite;
-        *selected_ciphersuite_info = ciphersuite_info;
-
-        return 0;
-    }
-
-    return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE;
-}
-
-MBEDTLS_CHECK_RETURN_CRITICAL
 static int ssl_tls13_session_copy_ticket(mbedtls_ssl_session *dst,
                                          const mbedtls_ssl_session *src)
 {
@@ -481,6 +473,13 @@
 }
 #endif /* MBEDTLS_SSL_SESSION_TICKETS */
 
+struct psk_attributes {
+    int type;
+    int key_exchange_mode;
+    const mbedtls_ssl_ciphersuite_t *ciphersuite_info;
+};
+#define PSK_ATTRIBUTES_INIT { 0, 0, NULL }
+
 /* Parser for pre_shared_key extension in client hello
  *    struct {
  *        opaque identity<1..2^16-1>;
@@ -507,7 +506,8 @@
     const unsigned char *pre_shared_key_ext,
     const unsigned char *pre_shared_key_ext_end,
     const unsigned char *ciphersuites,
-    const unsigned char *ciphersuites_end)
+    const unsigned char *ciphersuites_end,
+    struct psk_attributes *psk)
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     const unsigned char *identities = pre_shared_key_ext;
@@ -558,9 +558,10 @@
         uint32_t obfuscated_ticket_age;
         const unsigned char *binder;
         size_t binder_len;
-        int psk_type;
-        uint16_t cipher_suite;
-        const mbedtls_ssl_ciphersuite_t *ciphersuite_info;
+        int psk_ciphersuite_id;
+        psa_algorithm_t psk_hash_alg;
+        int allowed_key_exchange_modes;
+
 #if defined(MBEDTLS_SSL_SESSION_TICKETS)
         mbedtls_ssl_session session;
         mbedtls_ssl_session_init(&session);
@@ -586,47 +587,74 @@
 
         ret = ssl_tls13_offered_psks_check_identity_match(
             ssl, identity, identity_len, obfuscated_ticket_age,
-            &psk_type, &session);
-        if (ret != SSL_TLS1_3_OFFERED_PSK_MATCH) {
+            &psk->type, &session);
+        if (ret != SSL_TLS1_3_PSK_IDENTITY_MATCH) {
             continue;
         }
 
         MBEDTLS_SSL_DEBUG_MSG(4, ("found matched identity"));
-        switch (psk_type) {
+
+        switch (psk->type) {
             case MBEDTLS_SSL_TLS1_3_PSK_EXTERNAL:
-                ret = ssl_tls13_select_ciphersuite_for_psk(
-                    ssl, ciphersuites, ciphersuites_end,
-                    &cipher_suite, &ciphersuite_info);
+                psk_ciphersuite_id = 0;
+                psk_hash_alg = PSA_ALG_SHA_256;
+                allowed_key_exchange_modes =
+                    MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ALL;
                 break;
-            case MBEDTLS_SSL_TLS1_3_PSK_RESUMPTION:
 #if defined(MBEDTLS_SSL_SESSION_TICKETS)
-                ret = ssl_tls13_select_ciphersuite_for_resumption(
-                    ssl, ciphersuites, ciphersuites_end, &session,
-                    &cipher_suite, &ciphersuite_info);
-                if (ret != 0) {
-                    mbedtls_ssl_session_free(&session);
-                }
-#else
-                ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE;
-#endif
+            case MBEDTLS_SSL_TLS1_3_PSK_RESUMPTION:
+                psk_ciphersuite_id = session.ciphersuite;
+                psk_hash_alg = PSA_ALG_NONE;
+                ssl->session_negotiate->ticket_flags = session.ticket_flags;
+                allowed_key_exchange_modes =
+                    session.ticket_flags &
+                    MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ALL;
                 break;
+#endif
             default:
                 return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
         }
-        if (ret != 0) {
-            /* See below, no cipher_suite available, abort handshake */
+
+        psk->key_exchange_mode = MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_NONE;
+
+        if ((allowed_key_exchange_modes &
+             MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL) &&
+            ssl_tls13_key_exchange_is_psk_ephemeral_available(ssl)) {
+            psk->key_exchange_mode = MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL;
+        } else if ((allowed_key_exchange_modes &
+                    MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK) &&
+                   ssl_tls13_key_exchange_is_psk_available(ssl)) {
+            psk->key_exchange_mode = MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK;
+        }
+
+        if (psk->key_exchange_mode == MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_NONE) {
+            MBEDTLS_SSL_DEBUG_MSG(3, ("No suitable PSK key exchange mode"));
+            continue;
+        }
+
+        ssl_tls13_select_ciphersuite(ssl, ciphersuites, ciphersuites_end,
+                                     psk_ciphersuite_id, psk_hash_alg,
+                                     &psk->ciphersuite_info);
+
+        if (psk->ciphersuite_info == NULL) {
+#if defined(MBEDTLS_SSL_SESSION_TICKETS)
+            mbedtls_ssl_session_free(&session);
+#endif
+            /*
+             * We consider finding a ciphersuite suitable for the PSK as part
+             * of the validation of its binder. Thus if we do not find one, we
+             * abort the handshake with a decrypt_error alert.
+             */
             MBEDTLS_SSL_PEND_FATAL_ALERT(
                 MBEDTLS_SSL_ALERT_MSG_DECRYPT_ERROR,
                 MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE);
-            MBEDTLS_SSL_DEBUG_RET(
-                2, "ssl_tls13_select_ciphersuite", ret);
-            return ret;
+            return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE;
         }
 
         ret = ssl_tls13_offered_psks_check_binder_match(
-            ssl, binder, binder_len, psk_type,
-            mbedtls_md_psa_alg_from_type((mbedtls_md_type_t) ciphersuite_info->mac));
-        if (ret != SSL_TLS1_3_OFFERED_PSK_MATCH) {
+            ssl, binder, binder_len, psk->type,
+            mbedtls_md_psa_alg_from_type((mbedtls_md_type_t) psk->ciphersuite_info->mac));
+        if (ret != SSL_TLS1_3_BINDER_MATCH) {
             /* For security reasons, the handshake should be aborted when we
              * fail to validate a binder value. See RFC 8446 section 4.2.11.2
              * and appendix E.6. */
@@ -644,13 +672,8 @@
 
         matched_identity = identity_id;
 
-        /* Update handshake parameters */
-        ssl->handshake->ciphersuite_info = ciphersuite_info;
-        ssl->session_negotiate->ciphersuite = cipher_suite;
-        MBEDTLS_SSL_DEBUG_MSG(2, ("overwrite ciphersuite: %04x - %s",
-                                  cipher_suite, ciphersuite_info->name));
 #if defined(MBEDTLS_SSL_SESSION_TICKETS)
-        if (psk_type == MBEDTLS_SSL_TLS1_3_PSK_RESUMPTION) {
+        if (psk->type == MBEDTLS_SSL_TLS1_3_PSK_RESUMPTION) {
             ret = ssl_tls13_session_copy_ticket(ssl->session_negotiate,
                                                 &session);
             mbedtls_ssl_session_free(&session);
@@ -676,7 +699,7 @@
         return ret;
     }
     if (matched_identity == -1) {
-        MBEDTLS_SSL_DEBUG_MSG(3, ("No matched PSK or ticket."));
+        MBEDTLS_SSL_DEBUG_MSG(3, ("No usable PSK or ticket."));
         return MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY;
     }
 
@@ -1003,21 +1026,29 @@
 
 #if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED)
 MBEDTLS_CHECK_RETURN_CRITICAL
-static int ssl_tls13_ticket_is_kex_mode_permitted(mbedtls_ssl_context *ssl,
-                                                  unsigned int kex_mode)
+static int ssl_tls13_key_exchange_is_psk_available(mbedtls_ssl_context *ssl)
 {
-#if defined(MBEDTLS_SSL_SESSION_TICKETS)
-    if (ssl->handshake->resume) {
-        if (!mbedtls_ssl_tls13_session_ticket_has_flags(
-                ssl->session_negotiate, kex_mode)) {
-            return 0;
-        }
-    }
+#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED)
+    return mbedtls_ssl_conf_tls13_is_psk_enabled(ssl) &&
+           mbedtls_ssl_tls13_is_psk_supported(ssl) &&
+           ssl_tls13_client_hello_has_exts_for_psk_key_exchange(ssl);
 #else
     ((void) ssl);
-    ((void) kex_mode);
+    return 0;
 #endif
-    return 1;
+}
+
+MBEDTLS_CHECK_RETURN_CRITICAL
+static int ssl_tls13_key_exchange_is_psk_ephemeral_available(mbedtls_ssl_context *ssl)
+{
+#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED)
+    return mbedtls_ssl_conf_tls13_is_psk_ephemeral_enabled(ssl) &&
+           mbedtls_ssl_tls13_is_psk_ephemeral_supported(ssl) &&
+           ssl_tls13_client_hello_has_exts_for_psk_ephemeral_key_exchange(ssl);
+#else
+    ((void) ssl);
+    return 0;
+#endif
 }
 #endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED */
 
@@ -1033,83 +1064,6 @@
 #endif
 }
 
-MBEDTLS_CHECK_RETURN_CRITICAL
-static int ssl_tls13_key_exchange_is_psk_available(mbedtls_ssl_context *ssl)
-{
-#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED)
-    return ssl_tls13_ticket_is_kex_mode_permitted(
-        ssl, MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK) &&
-           mbedtls_ssl_conf_tls13_is_psk_enabled(ssl) &&
-           mbedtls_ssl_tls13_is_psk_supported(ssl) &&
-           ssl_tls13_client_hello_has_exts_for_psk_key_exchange(ssl);
-#else
-    ((void) ssl);
-    return 0;
-#endif
-}
-
-MBEDTLS_CHECK_RETURN_CRITICAL
-static int ssl_tls13_key_exchange_is_psk_ephemeral_available(mbedtls_ssl_context *ssl)
-{
-#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED)
-    return ssl_tls13_ticket_is_kex_mode_permitted(
-        ssl, MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL) &&
-           mbedtls_ssl_conf_tls13_is_psk_ephemeral_enabled(ssl) &&
-           mbedtls_ssl_tls13_is_psk_ephemeral_supported(ssl) &&
-           ssl_tls13_client_hello_has_exts_for_psk_ephemeral_key_exchange(ssl);
-#else
-    ((void) ssl);
-    return 0;
-#endif
-}
-
-static int ssl_tls13_determine_key_exchange_mode(mbedtls_ssl_context *ssl)
-{
-    /*
-     * Determine the key exchange algorithm to use.
-     * There are three types of key exchanges supported in TLS 1.3:
-     * - (EC)DH with ECDSA,
-     * - (EC)DH with PSK,
-     * - plain PSK.
-     *
-     * The PSK-based key exchanges may additionally be used with 0-RTT.
-     *
-     * Our built-in order of preference is
-     *  1 ) (EC)DHE-PSK Mode ( psk_ephemeral )
-     *  2 ) Certificate Mode ( ephemeral )
-     *  3 ) Plain PSK Mode ( psk )
-     */
-
-    ssl->handshake->key_exchange_mode =
-        MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_NONE;
-
-    if (ssl_tls13_key_exchange_is_psk_ephemeral_available(ssl)) {
-        ssl->handshake->key_exchange_mode =
-            MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL;
-        MBEDTLS_SSL_DEBUG_MSG(2, ("key exchange mode: psk_ephemeral"));
-    } else
-    if (ssl_tls13_key_exchange_is_ephemeral_available(ssl)) {
-        ssl->handshake->key_exchange_mode =
-            MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL;
-        MBEDTLS_SSL_DEBUG_MSG(2, ("key exchange mode: ephemeral"));
-    } else
-    if (ssl_tls13_key_exchange_is_psk_available(ssl)) {
-        ssl->handshake->key_exchange_mode =
-            MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK;
-        MBEDTLS_SSL_DEBUG_MSG(2, ("key exchange mode: psk"));
-    } else {
-        MBEDTLS_SSL_DEBUG_MSG(
-            1,
-            ("ClientHello message misses mandatory extensions."));
-        MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_MISSING_EXTENSION,
-                                     MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER);
-        return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER;
-    }
-
-    return 0;
-
-}
-
 #if defined(MBEDTLS_X509_CRT_PARSE_C) && \
     defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED)
 
@@ -1301,6 +1255,8 @@
     int no_usable_share_for_key_agreement = 0;
 
 #if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED)
+    int got_psk = 0;
+    struct psk_attributes psk = PSK_ATTRIBUTES_INIT;
     const unsigned char *pre_shared_key_ext = NULL;
     const unsigned char *pre_shared_key_ext_end = NULL;
 #endif
@@ -1437,12 +1393,8 @@
      * We negotiate TLS 1.3.
      */
     ssl->tls_version = MBEDTLS_SSL_VERSION_TLS1_3;
-
-#if defined(MBEDTLS_SSL_SESSION_TICKETS)
-    /* Store minor version for later use with ticket serialization. */
     ssl->session_negotiate->tls_version = MBEDTLS_SSL_VERSION_TLS1_3;
     ssl->session_negotiate->endpoint = ssl->conf->endpoint;
-#endif
 
     /*
      * We are negotiating the version 1.3 of the protocol. Do what we have
@@ -1468,37 +1420,20 @@
      */
     MBEDTLS_SSL_DEBUG_BUF(3, "client hello, list of cipher suites",
                           cipher_suites, cipher_suites_len);
-    for (const unsigned char *cipher_suites_p = cipher_suites;
-         cipher_suites_p < cipher_suites_end; cipher_suites_p += 2) {
-        uint16_t cipher_suite;
-        const mbedtls_ssl_ciphersuite_t *ciphersuite_info;
 
-        /*
-         * "cipher_suites_end - cipher_suites_p is even" is an invariant of the
-         * loop. As cipher_suites_end - cipher_suites_p > 0, we have
-         * cipher_suites_end - cipher_suites_p >= 2 and it is thus safe to read
-         * two bytes.
-         */
-        cipher_suite = MBEDTLS_GET_UINT16_BE(cipher_suites_p, 0);
-        ciphersuite_info = ssl_tls13_validate_peer_ciphersuite(
-            ssl, cipher_suite);
-        if (ciphersuite_info == NULL) {
-            continue;
-        }
-
-        ssl->session_negotiate->ciphersuite = cipher_suite;
-        handshake->ciphersuite_info = ciphersuite_info;
-        MBEDTLS_SSL_DEBUG_MSG(2, ("selected ciphersuite: %04x - %s",
-                                  cipher_suite,
-                                  ciphersuite_info->name));
-        break;
-    }
+    ssl_tls13_select_ciphersuite(ssl, cipher_suites, cipher_suites_end,
+                                 0, PSA_ALG_NONE, &handshake->ciphersuite_info);
 
     if (handshake->ciphersuite_info == NULL) {
         MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE,
                                      MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE);
         return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE;
     }
+    ssl->session_negotiate->ciphersuite = handshake->ciphersuite_info->id;
+
+    MBEDTLS_SSL_DEBUG_MSG(2, ("selected ciphersuite: %04x - %s",
+                              ((unsigned) handshake->ciphersuite_info->id),
+                              handshake->ciphersuite_info->name));
 
     /* ...
      * opaque legacy_compression_methods<1..2^8-1>;
@@ -1533,6 +1468,12 @@
         unsigned int extension_type;
         size_t extension_data_len;
         const unsigned char *extension_data_end;
+        uint32_t allowed_exts = MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_CH;
+
+        if (ssl->handshake->hello_retry_request_flag) {
+            /* Do not accept early data extension in 2nd ClientHello */
+            allowed_exts &= ~MBEDTLS_SSL_EXT_MASK(EARLY_DATA);
+        }
 
         /* RFC 8446, section 4.2.11
          *
@@ -1560,7 +1501,7 @@
 
         ret = mbedtls_ssl_tls13_check_received_extension(
             ssl, MBEDTLS_SSL_HS_CLIENT_HELLO, extension_type,
-            MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_CH);
+            allowed_exts);
         if (ret != 0) {
             return ret;
         }
@@ -1732,10 +1673,11 @@
     /* Update checksum with either
      * - The entire content of the CH message, if no PSK extension is present
      * - The content up to but excluding the PSK extension, if present.
+     * Always parse the pre-shared-key extension when present in the
+     * ClientHello even if some pre-requisites for PSK key exchange modes are
+     * not met. That way we always validate the syntax of the extension.
      */
-    /* If we've settled on a PSK-based exchange, parse PSK identity ext */
-    if (ssl_tls13_key_exchange_is_psk_available(ssl) ||
-        ssl_tls13_key_exchange_is_psk_ephemeral_available(ssl)) {
+    if (handshake->received_extensions & MBEDTLS_SSL_EXT_MASK(PRE_SHARED_KEY)) {
         ret = handshake->update_checksum(ssl, buf,
                                          pre_shared_key_ext - buf);
         if (0 != ret) {
@@ -1746,10 +1688,11 @@
                                                  pre_shared_key_ext,
                                                  pre_shared_key_ext_end,
                                                  cipher_suites,
-                                                 cipher_suites_end);
-        if (ret == MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY) {
-            handshake->received_extensions &= ~MBEDTLS_SSL_EXT_MASK(PRE_SHARED_KEY);
-        } else if (ret != 0) {
+                                                 cipher_suites_end,
+                                                 &psk);
+        if (ret == 0) {
+            got_psk = 1;
+        } else if (ret != MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY) {
             MBEDTLS_SSL_DEBUG_RET(
                 1, "ssl_tls13_parse_pre_shared_key_ext", ret);
             return ret;
@@ -1764,12 +1707,68 @@
         }
     }
 
-    ret = ssl_tls13_determine_key_exchange_mode(ssl);
-    if (ret < 0) {
-        return ret;
+    /*
+     * Determine the key exchange algorithm to use.
+     * There are three types of key exchanges supported in TLS 1.3:
+     * - (EC)DH with ECDSA,
+     * - (EC)DH with PSK,
+     * - plain PSK.
+     *
+     * The PSK-based key exchanges may additionally be used with 0-RTT.
+     *
+     * Our built-in order of preference is
+     *  1 ) (EC)DHE-PSK Mode ( psk_ephemeral )
+     *  2 ) Certificate Mode ( ephemeral )
+     *  3 ) Plain PSK Mode ( psk )
+     */
+#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED)
+    if (got_psk && (psk.key_exchange_mode ==
+                    MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL)) {
+        handshake->key_exchange_mode =
+            MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL;
+        MBEDTLS_SSL_DEBUG_MSG(2, ("key exchange mode: psk_ephemeral"));
+
+    } else
+#endif
+    if (ssl_tls13_key_exchange_is_ephemeral_available(ssl)) {
+        handshake->key_exchange_mode =
+            MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL;
+        MBEDTLS_SSL_DEBUG_MSG(2, ("key exchange mode: ephemeral"));
+
+    }
+#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED)
+    else if (got_psk && (psk.key_exchange_mode ==
+                         MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK)) {
+        handshake->key_exchange_mode = MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK;
+        MBEDTLS_SSL_DEBUG_MSG(2, ("key exchange mode: psk"));
+    }
+#endif
+    else {
+        MBEDTLS_SSL_DEBUG_MSG(
+            1,
+            ("ClientHello message misses mandatory extensions."));
+        MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_MISSING_EXTENSION,
+                                     MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER);
+        return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER;
     }
 
-    if (ssl->handshake->key_exchange_mode !=
+#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED)
+    if (handshake->key_exchange_mode &
+        MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ALL) {
+        handshake->ciphersuite_info = psk.ciphersuite_info;
+        ssl->session_negotiate->ciphersuite = psk.ciphersuite_info->id;
+
+        MBEDTLS_SSL_DEBUG_MSG(2, ("Select PSK ciphersuite: %04x - %s",
+                                  ((unsigned) psk.ciphersuite_info->id),
+                                  psk.ciphersuite_info->name));
+
+        if (psk.type == MBEDTLS_SSL_TLS1_3_PSK_RESUMPTION) {
+            handshake->resume = 1;
+        }
+    }
+#endif
+
+    if (handshake->key_exchange_mode !=
         MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK) {
         hrr_required = (no_usable_share_for_key_agreement != 0);
     }
@@ -1780,25 +1779,15 @@
 }
 
 #if defined(MBEDTLS_SSL_EARLY_DATA)
-static void ssl_tls13_update_early_data_status(mbedtls_ssl_context *ssl)
+static int ssl_tls13_check_early_data_requirements(mbedtls_ssl_context *ssl)
 {
     mbedtls_ssl_handshake_params *handshake = ssl->handshake;
 
-    if ((handshake->received_extensions &
-         MBEDTLS_SSL_EXT_MASK(EARLY_DATA)) == 0) {
-        MBEDTLS_SSL_DEBUG_MSG(
-            1, ("EarlyData: no early data extension received."));
-        ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_RECEIVED;
-        return;
-    }
-
-    ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED;
-
     if (ssl->conf->early_data_enabled == MBEDTLS_SSL_EARLY_DATA_DISABLED) {
         MBEDTLS_SSL_DEBUG_MSG(
             1,
             ("EarlyData: rejected, feature disabled in server configuration."));
-        return;
+        return -1;
     }
 
     if (!handshake->resume) {
@@ -1807,7 +1796,7 @@
            resumption. */
         MBEDTLS_SSL_DEBUG_MSG(
             1, ("EarlyData: rejected, not a session resumption."));
-        return;
+        return -1;
     }
 
     /* RFC 8446 4.2.10
@@ -1830,7 +1819,7 @@
         MBEDTLS_SSL_DEBUG_MSG(
             1, ("EarlyData: rejected, the selected key in "
                 "`pre_shared_key` is not the first one."));
-        return;
+        return -1;
     }
 
     if (handshake->ciphersuite_info->id !=
@@ -1838,7 +1827,7 @@
         MBEDTLS_SSL_DEBUG_MSG(
             1, ("EarlyData: rejected, the selected ciphersuite is not the one "
                 "of the selected pre-shared key."));
-        return;
+        return -1;
 
     }
 
@@ -1847,18 +1836,18 @@
             1,
             ("EarlyData: rejected, early_data not allowed in ticket "
              "permission bits."));
-        return;
+        return -1;
     }
 
-    ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED;
-
+    return 0;
 }
 #endif /* MBEDTLS_SSL_EARLY_DATA */
 
 /* Update the handshake state machine */
 
 MBEDTLS_CHECK_RETURN_CRITICAL
-static int ssl_tls13_postprocess_client_hello(mbedtls_ssl_context *ssl)
+static int ssl_tls13_postprocess_client_hello(mbedtls_ssl_context *ssl,
+                                              int hrr_required)
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
 
@@ -1882,17 +1871,26 @@
     }
 
 #if defined(MBEDTLS_SSL_EARLY_DATA)
-    /* There is enough information, update early data state. */
-    ssl_tls13_update_early_data_status(ssl);
+    if (ssl->handshake->received_extensions & MBEDTLS_SSL_EXT_MASK(EARLY_DATA)) {
+        ssl->handshake->early_data_accepted =
+            (!hrr_required) && (ssl_tls13_check_early_data_requirements(ssl) == 0);
 
-    if (ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED) {
-        ret = mbedtls_ssl_tls13_compute_early_transform(ssl);
-        if (ret != 0) {
-            MBEDTLS_SSL_DEBUG_RET(
-                1, "mbedtls_ssl_tls13_compute_early_transform", ret);
-            return ret;
+        if (ssl->handshake->early_data_accepted) {
+            ret = mbedtls_ssl_tls13_compute_early_transform(ssl);
+            if (ret != 0) {
+                MBEDTLS_SSL_DEBUG_RET(
+                    1, "mbedtls_ssl_tls13_compute_early_transform", ret);
+                return ret;
+            }
+        } else {
+            ssl->discard_early_data_record =
+                hrr_required ?
+                MBEDTLS_SSL_EARLY_DATA_DISCARD :
+                MBEDTLS_SSL_EARLY_DATA_TRY_TO_DEPROTECT_AND_DISCARD;
         }
     }
+#else
+    ((void) hrr_required);
 #endif /* MBEDTLS_SSL_EARLY_DATA */
 
     return 0;
@@ -1947,7 +1945,9 @@
         return 0;
     }
 
-    MBEDTLS_SSL_PROC_CHK(ssl_tls13_postprocess_client_hello(ssl));
+    MBEDTLS_SSL_PROC_CHK(
+        ssl_tls13_postprocess_client_hello(ssl, parse_client_hello_ret ==
+                                           SSL_CLIENT_HELLO_HRR_REQUIRED));
 
     if (SSL_CLIENT_HELLO_OK == parse_client_hello_ret) {
         mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_SERVER_HELLO);
@@ -1970,10 +1970,6 @@
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     unsigned char *server_randbytes =
         ssl->handshake->randbytes + MBEDTLS_CLIENT_HELLO_RANDOM_LEN;
-    if (ssl->conf->f_rng == NULL) {
-        MBEDTLS_SSL_DEBUG_MSG(1, ("no RNG provided"));
-        return MBEDTLS_ERR_SSL_NO_RNG;
-    }
 
     if ((ret = ssl->conf->f_rng(ssl->conf->p_rng, server_randbytes,
                                 MBEDTLS_SERVER_HELLO_RANDOM_LEN)) != 0) {
@@ -2424,7 +2420,7 @@
 static int ssl_tls13_prepare_hello_retry_request(mbedtls_ssl_context *ssl)
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    if (ssl->handshake->hello_retry_request_count > 0) {
+    if (ssl->handshake->hello_retry_request_flag) {
         MBEDTLS_SSL_DEBUG_MSG(1, ("Too many HRRs"));
         MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE,
                                      MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE);
@@ -2471,7 +2467,7 @@
     MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_finish_handshake_msg(ssl, buf_len,
                                                           msg_len));
 
-    ssl->handshake->hello_retry_request_count++;
+    ssl->handshake->hello_retry_request_flag = 1;
 
 #if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE)
     /* The server sends a dummy change_cipher_spec record immediately
@@ -2530,7 +2526,7 @@
 #endif /* MBEDTLS_SSL_ALPN */
 
 #if defined(MBEDTLS_SSL_EARLY_DATA)
-    if (ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED) {
+    if (ssl->handshake->early_data_accepted) {
         ret = mbedtls_ssl_tls13_write_early_data_ext(
             ssl, 0, p, end, &output_len);
         if (ret != 0) {
@@ -2857,7 +2853,7 @@
     }
 
 #if defined(MBEDTLS_SSL_EARLY_DATA)
-    if (ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED) {
+    if (ssl->handshake->early_data_accepted) {
         /* See RFC 8446 section A.2 for more information */
         MBEDTLS_SSL_DEBUG_MSG(
             1, ("Switch to early keys for inbound traffic. "
@@ -2910,7 +2906,15 @@
     }
 
     if (ssl->in_msgtype == MBEDTLS_SSL_MSG_APPLICATION_DATA) {
-        MBEDTLS_SSL_DEBUG_MSG(3, ("Received early data"));
+        if (ssl->in_offt == NULL) {
+            MBEDTLS_SSL_DEBUG_MSG(3, ("Received early data"));
+            /* Set the reading pointer */
+            ssl->in_offt = ssl->in_msg;
+            ret = mbedtls_ssl_tls13_check_early_data_len(ssl, ssl->in_msglen);
+            if (ret != 0) {
+                return ret;
+            }
+        }
         return SSL_GOT_EARLY_DATA;
     }
 
@@ -2936,37 +2940,6 @@
     return 0;
 }
 
-MBEDTLS_CHECK_RETURN_CRITICAL
-static int ssl_tls13_process_early_application_data(mbedtls_ssl_context *ssl)
-{
-    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-
-    if ((ret = mbedtls_ssl_read_record(ssl, 0)) != 0) {
-        MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record", ret);
-        return ret;
-    }
-
-    /*
-     * Output early data
-     *
-     * For the time being, we print received data via debug message.
-     *
-     * TODO: Remove it when `mbedtls_ssl_read_early_data` is ready.
-     */
-    ssl->in_msg[ssl->in_msglen] = 0;
-    MBEDTLS_SSL_DEBUG_MSG(3, ("\n%s", ssl->in_msg));
-
-    /* RFC 8446 section 4.6.1
-     *
-     * A server receiving more than max_early_data_size bytes of 0-RTT data
-     * SHOULD terminate the connection with an "unexpected_message" alert.
-     *
-     * TODO: Add received data size check here.
-     */
-
-    return 0;
-}
-
 /*
  * RFC 8446 section A.2
  *
@@ -3037,7 +3010,8 @@
         ssl_tls13_prepare_for_handshake_second_flight(ssl);
 
     } else if (ret == SSL_GOT_EARLY_DATA) {
-        MBEDTLS_SSL_PROC_CHK(ssl_tls13_process_early_application_data(ssl));
+        ret = MBEDTLS_ERR_SSL_RECEIVED_EARLY_DATA;
+        goto cleanup;
     } else {
         MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen"));
         ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR;
@@ -3144,10 +3118,6 @@
 
     MBEDTLS_SSL_DEBUG_MSG(2, ("=> prepare NewSessionTicket msg"));
 
-#if defined(MBEDTLS_HAVE_TIME)
-    session->ticket_creation_time = mbedtls_ms_time();
-#endif
-
     /* Set ticket_flags depends on the advertised psk key exchange mode */
     mbedtls_ssl_tls13_session_clear_ticket_flags(
         session, MBEDTLS_SSL_TLS1_3_TICKET_FLAGS_MASK);
@@ -3161,6 +3131,7 @@
         ssl->conf->max_early_data_size > 0) {
         mbedtls_ssl_tls13_session_set_ticket_flags(
             session, MBEDTLS_SSL_TLS1_3_TICKET_ALLOW_EARLY_DATA);
+        session->max_early_data_size = ssl->conf->max_early_data_size;
     }
 #endif /* MBEDTLS_SSL_EARLY_DATA */
 
@@ -3282,6 +3253,9 @@
     MBEDTLS_SSL_CHK_BUF_PTR(p, end, 4 + 4 + 1 + ticket_nonce_size + 2);
 
     /* Generate ticket and ticket_lifetime */
+#if defined(MBEDTLS_HAVE_TIME)
+    session->ticket_creation_time = mbedtls_ms_time();
+#endif
     ret = ssl->conf->f_ticket_write(ssl->conf->p_ticket,
                                     session,
                                     p + 9 + ticket_nonce_size + 2,
@@ -3495,9 +3469,10 @@
 
         case MBEDTLS_SSL_SERVER_CCS_AFTER_SERVER_HELLO:
             ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl);
-            if (ret == 0) {
-                mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_ENCRYPTED_EXTENSIONS);
+            if (ret != 0) {
+                break;
             }
+            mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_ENCRYPTED_EXTENSIONS);
             break;
 #endif /* MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE */
 
diff --git a/library/x509.c b/library/x509.c
index b7b71f3..f97fb44 100644
--- a/library/x509.c
+++ b/library/x509.c
@@ -19,7 +19,7 @@
 
 #if defined(MBEDTLS_X509_USE_C)
 
-#include "mbedtls/x509.h"
+#include "x509_internal.h"
 #include "mbedtls/asn1.h"
 #include "mbedtls/error.h"
 #include "mbedtls/oid.h"
diff --git a/library/x509_create.c b/library/x509_create.c
index f7a17e7..839b5df 100644
--- a/library/x509_create.c
+++ b/library/x509_create.c
@@ -9,7 +9,7 @@
 
 #if defined(MBEDTLS_X509_CREATE_C)
 
-#include "mbedtls/x509.h"
+#include "x509_internal.h"
 #include "mbedtls/asn1write.h"
 #include "mbedtls/error.h"
 #include "mbedtls/oid.h"
diff --git a/library/x509_crl.c b/library/x509_crl.c
index fdbad23..7901992 100644
--- a/library/x509_crl.c
+++ b/library/x509_crl.c
@@ -20,6 +20,7 @@
 #if defined(MBEDTLS_X509_CRL_PARSE_C)
 
 #include "mbedtls/x509_crl.h"
+#include "x509_internal.h"
 #include "mbedtls/error.h"
 #include "mbedtls/oid.h"
 #include "mbedtls/platform_util.h"
diff --git a/library/x509_crt.c b/library/x509_crt.c
index 84b92a8..2fd56fb 100644
--- a/library/x509_crt.c
+++ b/library/x509_crt.c
@@ -22,6 +22,7 @@
 #if defined(MBEDTLS_X509_CRT_PARSE_C)
 
 #include "mbedtls/x509_crt.h"
+#include "x509_internal.h"
 #include "mbedtls/error.h"
 #include "mbedtls/oid.h"
 #include "mbedtls/platform_util.h"
@@ -3289,4 +3290,12 @@
 }
 #endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */
 
+int mbedtls_x509_crt_get_ca_istrue(const mbedtls_x509_crt *crt)
+{
+    if ((crt->ext_types & MBEDTLS_X509_EXT_BASIC_CONSTRAINTS) != 0) {
+        return crt->MBEDTLS_PRIVATE(ca_istrue);
+    }
+    return MBEDTLS_ERR_X509_INVALID_EXTENSIONS;
+}
+
 #endif /* MBEDTLS_X509_CRT_PARSE_C */
diff --git a/library/x509_csr.c b/library/x509_csr.c
index 79b1589..813d644 100644
--- a/library/x509_csr.c
+++ b/library/x509_csr.c
@@ -20,6 +20,7 @@
 #if defined(MBEDTLS_X509_CSR_PARSE_C)
 
 #include "mbedtls/x509_csr.h"
+#include "x509_internal.h"
 #include "mbedtls/error.h"
 #include "mbedtls/oid.h"
 #include "mbedtls/platform_util.h"
diff --git a/library/x509_internal.h b/library/x509_internal.h
new file mode 100644
index 0000000..8a2d2ed
--- /dev/null
+++ b/library/x509_internal.h
@@ -0,0 +1,86 @@
+/**
+ * \file x509.h
+ *
+ * \brief Internal part of the public "x509.h".
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+#ifndef MBEDTLS_X509_INTERNAL_H
+#define MBEDTLS_X509_INTERNAL_H
+#include "mbedtls/private_access.h"
+
+#include "mbedtls/build_info.h"
+
+#include "mbedtls/x509.h"
+#include "mbedtls/asn1.h"
+#include "pk_internal.h"
+
+#if defined(MBEDTLS_RSA_C)
+#include "mbedtls/rsa.h"
+#endif
+
+int mbedtls_x509_get_name(unsigned char **p, const unsigned char *end,
+                          mbedtls_x509_name *cur);
+int mbedtls_x509_get_alg_null(unsigned char **p, const unsigned char *end,
+                              mbedtls_x509_buf *alg);
+int mbedtls_x509_get_alg(unsigned char **p, const unsigned char *end,
+                         mbedtls_x509_buf *alg, mbedtls_x509_buf *params);
+#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT)
+int mbedtls_x509_get_rsassa_pss_params(const mbedtls_x509_buf *params,
+                                       mbedtls_md_type_t *md_alg, mbedtls_md_type_t *mgf_md,
+                                       int *salt_len);
+#endif
+int mbedtls_x509_get_sig(unsigned char **p, const unsigned char *end, mbedtls_x509_buf *sig);
+int mbedtls_x509_get_sig_alg(const mbedtls_x509_buf *sig_oid, const mbedtls_x509_buf *sig_params,
+                             mbedtls_md_type_t *md_alg, mbedtls_pk_type_t *pk_alg,
+                             void **sig_opts);
+int mbedtls_x509_get_time(unsigned char **p, const unsigned char *end,
+                          mbedtls_x509_time *t);
+int mbedtls_x509_get_serial(unsigned char **p, const unsigned char *end,
+                            mbedtls_x509_buf *serial);
+int mbedtls_x509_get_ext(unsigned char **p, const unsigned char *end,
+                         mbedtls_x509_buf *ext, int tag);
+#if !defined(MBEDTLS_X509_REMOVE_INFO)
+int mbedtls_x509_sig_alg_gets(char *buf, size_t size, const mbedtls_x509_buf *sig_oid,
+                              mbedtls_pk_type_t pk_alg, mbedtls_md_type_t md_alg,
+                              const void *sig_opts);
+#endif
+int mbedtls_x509_key_size_helper(char *buf, size_t buf_size, const char *name);
+int mbedtls_x509_set_extension(mbedtls_asn1_named_data **head, const char *oid, size_t oid_len,
+                               int critical, const unsigned char *val,
+                               size_t val_len);
+int mbedtls_x509_write_extensions(unsigned char **p, unsigned char *start,
+                                  mbedtls_asn1_named_data *first);
+int mbedtls_x509_write_names(unsigned char **p, unsigned char *start,
+                             mbedtls_asn1_named_data *first);
+int mbedtls_x509_write_sig(unsigned char **p, unsigned char *start,
+                           const char *oid, size_t oid_len,
+                           unsigned char *sig, size_t size,
+                           mbedtls_pk_type_t pk_alg);
+int mbedtls_x509_get_ns_cert_type(unsigned char **p,
+                                  const unsigned char *end,
+                                  unsigned char *ns_cert_type);
+int mbedtls_x509_get_key_usage(unsigned char **p,
+                               const unsigned char *end,
+                               unsigned int *key_usage);
+int mbedtls_x509_get_subject_alt_name(unsigned char **p,
+                                      const unsigned char *end,
+                                      mbedtls_x509_sequence *subject_alt_name);
+int mbedtls_x509_get_subject_alt_name_ext(unsigned char **p,
+                                          const unsigned char *end,
+                                          mbedtls_x509_sequence *subject_alt_name);
+int mbedtls_x509_info_subject_alt_name(char **buf, size_t *size,
+                                       const mbedtls_x509_sequence
+                                       *subject_alt_name,
+                                       const char *prefix);
+int mbedtls_x509_info_cert_type(char **buf, size_t *size,
+                                unsigned char ns_cert_type);
+int mbedtls_x509_info_key_usage(char **buf, size_t *size,
+                                unsigned int key_usage);
+
+int mbedtls_x509_write_set_san_common(mbedtls_asn1_named_data **extensions,
+                                      const mbedtls_x509_san_list *san_list);
+
+#endif /* MBEDTLS_X509_INTERNAL_H */
diff --git a/library/x509write.c b/library/x509write.c
index d434df5..4704900 100644
--- a/library/x509write.c
+++ b/library/x509write.c
@@ -8,6 +8,7 @@
 #if defined(MBEDTLS_X509_CSR_WRITE_C) || defined(MBEDTLS_X509_CRT_WRITE_C)
 
 #include "mbedtls/x509_crt.h"
+#include "x509_internal.h"
 #include "mbedtls/asn1write.h"
 #include "mbedtls/error.h"
 #include "mbedtls/oid.h"
diff --git a/library/x509write_crt.c b/library/x509write_crt.c
index 913b15a..72f5a10 100644
--- a/library/x509write_crt.c
+++ b/library/x509write_crt.c
@@ -16,6 +16,7 @@
 #if defined(MBEDTLS_X509_CRT_WRITE_C)
 
 #include "mbedtls/x509_crt.h"
+#include "x509_internal.h"
 #include "mbedtls/asn1write.h"
 #include "mbedtls/error.h"
 #include "mbedtls/oid.h"
diff --git a/library/x509write_csr.c b/library/x509write_csr.c
index af75e7f..d3ddbcc 100644
--- a/library/x509write_csr.c
+++ b/library/x509write_csr.c
@@ -14,7 +14,7 @@
 
 #if defined(MBEDTLS_X509_CSR_WRITE_C)
 
-#include "mbedtls/x509.h"
+#include "x509_internal.h"
 #include "mbedtls/x509_csr.h"
 #include "mbedtls/asn1write.h"
 #include "mbedtls/error.h"