Merge pull request #6795 from gilles-peskine-arm/check_test_cases-quiet_ci

Hide check_test_cases warnings on the CI
diff --git a/.pylintrc b/.pylintrc
index 10c93f8..f395fb9 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -1,5 +1,6 @@
 [MASTER]
 init-hook='import sys; sys.path.append("scripts")'
+min-similarity-lines=10
 
 [BASIC]
 # We're ok with short funtion argument names.
diff --git a/ChangeLog.d/fix-gettimeofday-overflow.txt b/ChangeLog.d/fix-gettimeofday-overflow.txt
new file mode 100644
index 0000000..b7e10d2
--- /dev/null
+++ b/ChangeLog.d/fix-gettimeofday-overflow.txt
@@ -0,0 +1,3 @@
+Bugfix
+   * Fix possible integer overflow in mbedtls_timing_hardclock(), which
+     could cause a crash in programs/test/benchmark.
diff --git a/ChangeLog.d/pk-sign-restartable.txt b/ChangeLog.d/pk-sign-restartable.txt
new file mode 100644
index 0000000..35da2be
--- /dev/null
+++ b/ChangeLog.d/pk-sign-restartable.txt
@@ -0,0 +1,5 @@
+Changes
+   * When MBEDTLS_USE_PSA_CRYPTO and MBEDTLS_ECDSA_DETERMINISTIC are both
+     defined, mbedtls_pk_sign() now use deterministic ECDSA for ECDSA
+     signatures. This aligns the behaviour with MBEDTLS_USE_PSA_CRYPTO to
+     the behaviour without it, where deterministic ECDSA was already used.
diff --git a/README.md b/README.md
index 8a23bd2..cc70f56 100644
--- a/README.md
+++ b/README.md
@@ -245,6 +245,8 @@
 -   `tests/scripts/depends.py` test builds in configurations with a single curve, key exchange, hash, cipher, or pkalg on.
 -   `tests/scripts/all.sh` runs a combination of the above tests, plus some more, with various build options (such as ASan, full `mbedtls_config.h`, etc).
 
+Instead of manually installing the required versions of all tools required for testing, it is possible to use the Docker images from our CI systems, as explained in [our testing infrastructure repository](https://github.com/Mbed-TLS/mbedtls-test/blob/master/README.md#quick-start).
+
 Porting Mbed TLS
 ----------------
 
diff --git a/docs/architecture/psa-migration/psa-limitations.md b/docs/architecture/psa-migration/psa-limitations.md
index e565b28..c368023 100644
--- a/docs/architecture/psa-migration/psa-limitations.md
+++ b/docs/architecture/psa-migration/psa-limitations.md
@@ -17,8 +17,11 @@
 There is currently no support for that in PSA at all, but it will be added at
 some point, see <https://github.com/orgs/Mbed-TLS/projects/1#column-18816849>.
 
-Currently, `MBEDTLS_USE_PSA_CRYPTO` is simply incompatible with
-`MBEDTLS_ECP_RESTARTABLE`.
+Currently, when `MBEDTLS_USE_PSA_CRYPTO` and `MBEDTLS_ECP_RESTARTABLE` are
+both enabled, some operations that should be restartable are not (ECDH in TLS
+1.2 clients using ECDHE-ECDSA), as they are using PSA instead, and some
+operations that should use PSA do not (signature generation & verification) as
+they use the legacy API instead, in order to get restartable behaviour.
 
 Things that are in the API but not implemented yet
 --------------------------------------------------
diff --git a/docs/use-psa-crypto.md b/docs/use-psa-crypto.md
index 11442ed..194d96f 100644
--- a/docs/use-psa-crypto.md
+++ b/docs/use-psa-crypto.md
@@ -7,9 +7,6 @@
 General considerations
 ----------------------
 
-**Compile-time:** enabling `MBEDTLS_USE_PSA_CRYPTO` requires
-`MBEDTLS_ECP_RESTARTABLE` to be disabled.
-
 **Application code:** when this option is enabled, you need to call
 `psa_crypto_init()` before calling any function from the SSL/TLS, X.509 or PK
 module.
@@ -86,28 +83,34 @@
 
 Current exceptions:
 
-- finite-field (non-EC) Diffie-Hellman (used in key exchanges: DHE-RSA,
-  DHE-PSK)
+- Finite-field (non-EC) Diffie-Hellman (used in key exchanges: DHE-RSA,
+  DHE-PSK).
+- Restartable operations when `MBEDTLS_ECP_RESTARTABLE` is also enabled (see
+  the documentation of that option).
 
 Other than the above exceptions, all crypto operations are based on PSA when
 `MBEDTLS_USE_PSA_CRYPTO` is enabled.
 
 ### X.509: most crypto operations based on PSA
 
-Current exception:
+Current exceptions:
 
-- verification of RSA-PSS signatures with a salt length that is different from
+- Verification of RSA-PSS signatures with a salt length that is different from
   the hash length.
+- Restartable operations when `MBEDTLS_ECP_RESTARTABLE` is also enabled (see
+  the documentation of that option).
 
 Other than the above exception, all crypto operations are based on PSA when
 `MBEDTLS_USE_PSA_CRYPTO` is enabled.
 
 ### PK layer: most crypto operations based on PSA
 
-Current exception:
+Current exceptions:
 
-- verification of RSA-PSS signatures with a salt length that is different from
+- Verification of RSA-PSS signatures with a salt length that is different from
   the hash length, or with an MGF hash that's different from the message hash.
+- Restartable operations when `MBEDTLS_ECP_RESTARTABLE` is also enabled (see
+  the documentation of that option).
 
 Other than the above exception, all crypto operations are based on PSA when
 `MBEDTLS_USE_PSA_CRYPTO` is enabled.
diff --git a/include/mbedtls/check_config.h b/include/mbedtls/check_config.h
index b791344..99584c4 100644
--- a/include/mbedtls/check_config.h
+++ b/include/mbedtls/check_config.h
@@ -117,15 +117,19 @@
 #endif
 
 #if defined(MBEDTLS_ECP_RESTARTABLE)           && \
-    ( defined(MBEDTLS_USE_PSA_CRYPTO)          || \
-      defined(MBEDTLS_ECDH_COMPUTE_SHARED_ALT) || \
+    ( defined(MBEDTLS_ECDH_COMPUTE_SHARED_ALT) || \
       defined(MBEDTLS_ECDH_GEN_PUBLIC_ALT)     || \
       defined(MBEDTLS_ECDSA_SIGN_ALT)          || \
       defined(MBEDTLS_ECDSA_VERIFY_ALT)        || \
       defined(MBEDTLS_ECDSA_GENKEY_ALT)        || \
       defined(MBEDTLS_ECP_INTERNAL_ALT)        || \
       defined(MBEDTLS_ECP_ALT) )
-#error "MBEDTLS_ECP_RESTARTABLE defined, but it cannot coexist with an alternative or PSA-based ECP implementation"
+#error "MBEDTLS_ECP_RESTARTABLE defined, but it cannot coexist with an alternative ECP implementation"
+#endif
+
+#if defined(MBEDTLS_ECP_RESTARTABLE)           && \
+    !defined(MBEDTLS_ECP_C)
+#error "MBEDTLS_ECP_RESTARTABLE defined, but not all prerequisites"
 #endif
 
 #if defined(MBEDTLS_ECDSA_DETERMINISTIC) && !defined(MBEDTLS_HMAC_DRBG_C)
diff --git a/include/mbedtls/mbedtls_config.h b/include/mbedtls/mbedtls_config.h
index 78c3635..b9c896f 100644
--- a/include/mbedtls/mbedtls_config.h
+++ b/include/mbedtls/mbedtls_config.h
@@ -690,11 +690,42 @@
  * This is useful in non-threaded environments if you want to avoid blocking
  * for too long on ECC (and, hence, X.509 or SSL/TLS) operations.
  *
- * Uncomment this macro to enable restartable ECC computations.
+ * This option:
+ * - Adds xxx_restartable() variants of existing operations in the
+ *   following modules, with corresponding restart context types:
+ *   - ECP (for Short Weierstrass curves only): scalar multiplication (mul),
+ *     linear combination (muladd);
+ *   - ECDSA: signature generation & verification;
+ *   - PK: signature generation & verification;
+ *   - X509: certificate chain verification.
+ * - Adds mbedtls_ecdh_enable_restart() in the ECDH module.
+ * - Changes the behaviour of TLS 1.2 clients (not servers) when using the
+ *   ECDHE-ECDSA key exchange (not other key exchanges) to make all ECC
+ *   computations restartable:
+ *   - ECDH operations from the key exchange, only for Short Weierstass
+ *     curves, only when MBEDTLS_USE_PSA_CRYPTO is not enabled.
+ *   - verification of the server's key exchange signature;
+ *   - verification of the server's certificate chain;
+ *   - generation of the client's signature if client authentication is used,
+ *     with an ECC key/certificate.
+ *
+ * \note  In the cases above, the usual SSL/TLS functions, such as
+ *        mbedtls_ssl_handshake(), can now return
+ *        MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS.
+ *
+ * \note  When this option and MBEDTLS_USE_PSA_CRYPTO are both enabled,
+ *        restartable operations in PK, X.509 and TLS (see above) are not
+ *        using PSA. On the other hand, ECDH computations in TLS are using
+ *        PSA, and are not restartable. These are temporary limitations that
+ *        should be lifted in the future.
  *
  * \note  This option only works with the default software implementation of
  *        elliptic curve functionality. It is incompatible with
  *        MBEDTLS_ECP_ALT, MBEDTLS_ECDH_XXX_ALT, MBEDTLS_ECDSA_XXX_ALT.
+ *
+ * Requires: MBEDTLS_ECP_C
+ *
+ * Uncomment this macro to enable restartable ECC computations.
  */
 //#define MBEDTLS_ECP_RESTARTABLE
 
@@ -1923,7 +1954,6 @@
  * before calling any function from the SSL/TLS, X.509 or PK modules.
  *
  * Requires: MBEDTLS_PSA_CRYPTO_C.
- * Conflicts with: MBEDTLS_ECP_RESTARTABLE
  *
  * Uncomment this to enable internal use of PSA Crypto and new associated APIs.
  */
diff --git a/library/bignum.c b/library/bignum.c
index 65708c9..fc4ddf6 100644
--- a/library/bignum.c
+++ b/library/bignum.c
@@ -2032,75 +2032,19 @@
                         int (*f_rng)(void *, unsigned char *, size_t),
                         void *p_rng )
 {
-    int ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
-    int count;
-    unsigned lt_lower = 1, lt_upper = 0;
-    size_t n_bits = mbedtls_mpi_bitlen( N );
-    size_t n_bytes = ( n_bits + 7 ) / 8;
-    mbedtls_mpi lower_bound;
-
     if( min < 0 )
         return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
     if( mbedtls_mpi_cmp_int( N, min ) <= 0 )
         return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
 
-    /*
-     * When min == 0, each try has at worst a probability 1/2 of failing
-     * (the msb has a probability 1/2 of being 0, and then the result will
-     * be < N), so after 30 tries failure probability is a most 2**(-30).
-     *
-     * When N is just below a power of 2, as is the case when generating
-     * a random scalar on most elliptic curves, 1 try is enough with
-     * overwhelming probability. When N is just above a power of 2,
-     * as when generating a random scalar on secp224k1, each try has
-     * a probability of failing that is almost 1/2.
-     *
-     * The probabilities are almost the same if min is nonzero but negligible
-     * compared to N. This is always the case when N is crypto-sized, but
-     * it's convenient to support small N for testing purposes. When N
-     * is small, use a higher repeat count, otherwise the probability of
-     * failure is macroscopic.
-     */
-    count = ( n_bytes > 4 ? 30 : 250 );
-
-    mbedtls_mpi_init( &lower_bound );
-
     /* Ensure that target MPI has exactly the same number of limbs
      * as the upper bound, even if the upper bound has leading zeros.
-     * This is necessary for the mbedtls_mpi_lt_mpi_ct() check. */
-    MBEDTLS_MPI_CHK( mbedtls_mpi_resize_clear( X, N->n ) );
-    MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &lower_bound, N->n ) );
-    MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &lower_bound, min ) );
+     * This is necessary for mbedtls_mpi_core_random. */
+    int ret = mbedtls_mpi_resize_clear( X, N->n );
+    if( ret != 0 )
+        return( ret );
 
-    /*
-     * Match the procedure given in RFC 6979 §3.3 (deterministic ECDSA)
-     * when f_rng is a suitably parametrized instance of HMAC_DRBG:
-     * - use the same byte ordering;
-     * - keep the leftmost n_bits bits of the generated octet string;
-     * - try until result is in the desired range.
-     * This also avoids any bias, which is especially important for ECDSA.
-     */
-    do
-    {
-        MBEDTLS_MPI_CHK( mbedtls_mpi_core_fill_random( X->p, X->n,
-                                                       n_bytes,
-                                                       f_rng, p_rng ) );
-        MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( X, 8 * n_bytes - n_bits ) );
-
-        if( --count == 0 )
-        {
-            ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE;
-            goto cleanup;
-        }
-
-        MBEDTLS_MPI_CHK( mbedtls_mpi_lt_mpi_ct( X, &lower_bound, &lt_lower ) );
-        MBEDTLS_MPI_CHK( mbedtls_mpi_lt_mpi_ct( X, N, &lt_upper ) );
-    }
-    while( lt_lower != 0 || lt_upper == 0 );
-
-cleanup:
-    mbedtls_mpi_free( &lower_bound );
-    return( ret );
+    return( mbedtls_mpi_core_random( X->p, min, N->p, X->n, f_rng, p_rng ) );
 }
 
 /*
diff --git a/library/bignum_core.c b/library/bignum_core.c
index 1ce8457..bda62f2 100644
--- a/library/bignum_core.c
+++ b/library/bignum_core.c
@@ -134,6 +134,27 @@
     }
 }
 
+/* Whether min <= A, in constant time.
+ * A_limbs must be at least 1. */
+unsigned mbedtls_mpi_core_uint_le_mpi( mbedtls_mpi_uint min,
+                                       const mbedtls_mpi_uint *A,
+                                       size_t A_limbs )
+{
+    /* min <= least significant limb? */
+    unsigned min_le_lsl = 1 ^ mbedtls_ct_mpi_uint_lt( A[0], min );
+
+    /* limbs other than the least significant one are all zero? */
+    mbedtls_mpi_uint msll_mask = 0;
+    for( size_t i = 1; i < A_limbs; i++ )
+        msll_mask |= A[i];
+    /* The most significant limbs of A are not all zero iff msll_mask != 0. */
+    unsigned msll_nonzero = mbedtls_ct_mpi_uint_mask( msll_mask ) & 1;
+
+    /* min <= A iff the lowest limb of A is >= min or the other limbs
+     * are not all zero. */
+    return( min_le_lsl | msll_nonzero );
+}
+
 void mbedtls_mpi_core_cond_assign( mbedtls_mpi_uint *X,
                                    const mbedtls_mpi_uint *A,
                                    size_t limbs,
@@ -561,6 +582,67 @@
     return( ret );
 }
 
+int mbedtls_mpi_core_random( mbedtls_mpi_uint *X,
+                             mbedtls_mpi_uint min,
+                             const mbedtls_mpi_uint *N,
+                             size_t limbs,
+                             int (*f_rng)(void *, unsigned char *, size_t),
+                             void *p_rng )
+{
+    unsigned ge_lower = 1, lt_upper = 0;
+    size_t n_bits = mbedtls_mpi_core_bitlen( N, limbs );
+    size_t n_bytes = ( n_bits + 7 ) / 8;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+    /*
+     * When min == 0, each try has at worst a probability 1/2 of failing
+     * (the msb has a probability 1/2 of being 0, and then the result will
+     * be < N), so after 30 tries failure probability is a most 2**(-30).
+     *
+     * When N is just below a power of 2, as is the case when generating
+     * a random scalar on most elliptic curves, 1 try is enough with
+     * overwhelming probability. When N is just above a power of 2,
+     * as when generating a random scalar on secp224k1, each try has
+     * a probability of failing that is almost 1/2.
+     *
+     * The probabilities are almost the same if min is nonzero but negligible
+     * compared to N. This is always the case when N is crypto-sized, but
+     * it's convenient to support small N for testing purposes. When N
+     * is small, use a higher repeat count, otherwise the probability of
+     * failure is macroscopic.
+     */
+    int count = ( n_bytes > 4 ? 30 : 250 );
+
+    /*
+     * Match the procedure given in RFC 6979 §3.3 (deterministic ECDSA)
+     * when f_rng is a suitably parametrized instance of HMAC_DRBG:
+     * - use the same byte ordering;
+     * - keep the leftmost n_bits bits of the generated octet string;
+     * - try until result is in the desired range.
+     * This also avoids any bias, which is especially important for ECDSA.
+     */
+    do
+    {
+        MBEDTLS_MPI_CHK( mbedtls_mpi_core_fill_random( X, limbs,
+                                                       n_bytes,
+                                                       f_rng, p_rng ) );
+        mbedtls_mpi_core_shift_r( X, limbs, 8 * n_bytes - n_bits );
+
+        if( --count == 0 )
+        {
+            ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE;
+            goto cleanup;
+        }
+
+        ge_lower = mbedtls_mpi_core_uint_le_mpi( min, X, limbs );
+        lt_upper = mbedtls_mpi_core_lt_ct( X, N, limbs );
+    }
+    while( ge_lower == 0 || lt_upper == 0 );
+
+cleanup:
+    return( ret );
+}
+
 /* BEGIN MERGE SLOT 1 */
 
 static size_t exp_mod_get_window_size( size_t Ebits )
@@ -742,6 +824,40 @@
     return( c );
 }
 
+mbedtls_mpi_uint mbedtls_mpi_core_check_zero_ct( const mbedtls_mpi_uint *A,
+                                                 size_t limbs )
+{
+    mbedtls_mpi_uint bits = 0;
+
+    for( size_t i = 0; i < limbs; i++ )
+        bits |= A[i];
+
+    return( bits );
+}
+
+void mbedtls_mpi_core_to_mont_rep( mbedtls_mpi_uint *X,
+                                   const mbedtls_mpi_uint *A,
+                                   const mbedtls_mpi_uint *N,
+                                   size_t AN_limbs,
+                                   mbedtls_mpi_uint mm,
+                                   const mbedtls_mpi_uint *rr,
+                                   mbedtls_mpi_uint *T )
+{
+    mbedtls_mpi_core_montmul( X, A, rr, AN_limbs, N, AN_limbs, mm, T );
+}
+
+void mbedtls_mpi_core_from_mont_rep( mbedtls_mpi_uint *X,
+                                     const mbedtls_mpi_uint *A,
+                                     const mbedtls_mpi_uint *N,
+                                     size_t AN_limbs,
+                                     mbedtls_mpi_uint mm,
+                                     mbedtls_mpi_uint *T )
+{
+    const mbedtls_mpi_uint Rinv = 1;    /* 1/R in Mont. rep => 1 */
+
+    mbedtls_mpi_core_montmul( X, A, &Rinv, 1, N, AN_limbs, mm, T );
+}
+
 /* END MERGE SLOT 3 */
 
 /* BEGIN MERGE SLOT 4 */
diff --git a/library/bignum_core.h b/library/bignum_core.h
index b7af4d0..771f208 100644
--- a/library/bignum_core.h
+++ b/library/bignum_core.h
@@ -129,6 +129,22 @@
 void mbedtls_mpi_core_bigendian_to_host( mbedtls_mpi_uint *A,
                                          size_t A_limbs );
 
+/** \brief         Compare a machine integer with an MPI.
+ *
+ *                 This function operates in constant time with respect
+ *                 to the values of \p min and \p A.
+ *
+ * \param min      A machine integer.
+ * \param[in] A    An MPI.
+ * \param A_limbs  The number of limbs of \p A.
+ *                 This must be at least 1.
+ *
+ * \return         1 if \p min is less than or equal to \p A, otherwise 0.
+ */
+unsigned mbedtls_mpi_core_uint_le_mpi( mbedtls_mpi_uint min,
+                                       const mbedtls_mpi_uint *A,
+                                       size_t A_limbs );
+
 /**
  * \brief   Perform a safe conditional copy of an MPI which doesn't reveal
  *          whether assignment was done or not.
@@ -496,12 +512,53 @@
                                   int (*f_rng)(void *, unsigned char *, size_t),
                                   void *p_rng );
 
+/** Generate a random number uniformly in a range.
+ *
+ * This function generates a random number between \p min inclusive and
+ * \p N exclusive.
+ *
+ * The procedure complies with RFC 6979 §3.3 (deterministic ECDSA)
+ * when the RNG is a suitably parametrized instance of HMAC_DRBG
+ * and \p min is \c 1.
+ *
+ * \note           There are `N - min` possible outputs. The lower bound
+ *                 \p min can be reached, but the upper bound \p N cannot.
+ *
+ * \param X        The destination MPI, with \p limbs limbs.
+ *                 It must not be aliased with \p N or otherwise overlap it.
+ * \param min      The minimum value to return.
+ * \param N        The upper bound of the range, exclusive, with \p limbs limbs.
+ *                 In other words, this is one plus the maximum value to return.
+ *                 \p N must be strictly larger than \p min.
+ * \param limbs    The number of limbs of \p N and \p X.
+ *                 This must not be 0.
+ * \param f_rng    The RNG function to use. This must not be \c NULL.
+ * \param p_rng    The RNG parameter to be passed to \p f_rng.
+ *
+ * \return         \c 0 if successful.
+ * \return         #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if the implementation was
+ *                 unable to find a suitable value within a limited number
+ *                 of attempts. This has a negligible probability if \p N
+ *                 is significantly larger than \p min, which is the case
+ *                 for all usual cryptographic applications.
+ */
+int mbedtls_mpi_core_random( mbedtls_mpi_uint *X,
+                             mbedtls_mpi_uint min,
+                             const mbedtls_mpi_uint *N,
+                             size_t limbs,
+                             int (*f_rng)(void *, unsigned char *, size_t),
+                             void *p_rng );
+
 /* BEGIN MERGE SLOT 1 */
 
 /**
  * \brief          Returns the number of limbs of working memory required for
  *                 a call to `mbedtls_mpi_core_exp_mod()`.
  *
+ * \note           This will always be at least
+ *                 `mbedtls_mpi_core_montmul_working_limbs(AN_limbs)`,
+ *                 i.e. sufficient for a call to `mbedtls_mpi_core_montmul()`.
+ *
  * \param AN_limbs The number of limbs in the input `A` and the modulus `N`
  *                 (they must be the same size) that will be given to
  *                 `mbedtls_mpi_core_exp_mod()`.
@@ -572,6 +629,111 @@
                                            mbedtls_mpi_uint b,
                                            size_t limbs );
 
+/**
+ * \brief Determine if a given MPI has the value \c 0 in constant time with
+ *        respect to the value (but not with respect to the number of limbs).
+ *
+ * \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`.
+ */
+mbedtls_mpi_uint 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
+ *                 a call to `mbedtls_mpi_core_montmul()`.
+ *
+ * \param AN_limbs The number of limbs in the input `A` and the modulus `N`
+ *                 (they must be the same size) that will be given to
+ *                 `mbedtls_mpi_core_montmul()` or one of the other functions
+ *                 that specifies this as the amount of working memory needed.
+ *
+ * \return         The number of limbs of working memory required by
+ *                 `mbedtls_mpi_core_montmul()` (or other similar function).
+ */
+static inline size_t mbedtls_mpi_core_montmul_working_limbs( size_t AN_limbs )
+{
+    return( 2 * AN_limbs + 1 );
+}
+
+/** Convert an MPI into Montgomery form.
+ *
+ * \p X may be aliased to \p A, but may not otherwise overlap it.
+ *
+ * \p X may not alias \p N (it is in canonical form, so must be stricly less
+ * than \p N). Nor may it alias or overlap \p rr (this is unlikely to be
+ * required in practice.)
+ *
+ * This function is a thin wrapper around `mbedtls_mpi_core_montmul()` that is
+ * an alternative to calling `mbedtls_mpi_mod_raw_to_mont_rep()` when we
+ * don't want to allocate memory.
+ *
+ * \param[out]    X         The result of the conversion.
+ *                          Must have the same number of limbs as \p A.
+ * \param[in]     A         The MPI to convert into Montgomery form.
+ *                          Must have the same number of limbs as the modulus.
+ * \param[in]     N         The address of the modulus, which gives the size of
+ *                          the base `R` = 2^(biL*N->limbs).
+ * \param[in]     AN_limbs  The number of limbs in \p X, \p A, \p N and \p rr.
+ * \param         mm        The Montgomery constant for \p N: -N^-1 mod 2^biL.
+ *                          This can be determined by calling
+ *                          `mbedtls_mpi_core_montmul_init()`.
+ * \param[in]     rr        The residue for `2^{2*n*biL} mod N`.
+ * \param[in,out] T         Temporary storage of size at least
+ *                          `mbedtls_mpi_core_montmul_working_limbs(AN_limbs)`
+ *                          limbs.
+ *                          Its initial content is unused and
+ *                          its final content is indeterminate.
+ *                          It must not alias or otherwise overlap any of the
+ *                          other parameters.
+ */
+void mbedtls_mpi_core_to_mont_rep( mbedtls_mpi_uint *X,
+                                   const mbedtls_mpi_uint *A,
+                                   const mbedtls_mpi_uint *N,
+                                   size_t AN_limbs,
+                                   mbedtls_mpi_uint mm,
+                                   const mbedtls_mpi_uint *rr,
+                                   mbedtls_mpi_uint *T );
+
+/** Convert an MPI from Montgomery form.
+ *
+ * \p X may be aliased to \p A, but may not otherwise overlap it.
+ *
+ * \p X may not alias \p N (it is in canonical form, so must be stricly less
+ * than \p N).
+ *
+ * This function is a thin wrapper around `mbedtls_mpi_core_montmul()` that is
+ * an alternative to calling `mbedtls_mpi_mod_raw_from_mont_rep()` when we
+ * don't want to allocate memory.
+ *
+ * \param[out]    X         The result of the conversion.
+ *                          Must have the same number of limbs as \p A.
+ * \param[in]     A         The MPI to convert from Montgomery form.
+ *                          Must have the same number of limbs as the modulus.
+ * \param[in]     N         The address of the modulus, which gives the size of
+ *                          the base `R` = 2^(biL*N->limbs).
+ * \param[in]     AN_limbs  The number of limbs in \p X, \p A and \p N.
+ * \param         mm        The Montgomery constant for \p N: -N^-1 mod 2^biL.
+ *                          This can be determined by calling
+ *                          `mbedtls_mpi_core_montmul_init()`.
+ * \param[in,out] T         Temporary storage of size at least
+ *                          `mbedtls_mpi_core_montmul_working_limbs(AN_limbs)`
+ *                          limbs.
+ *                          Its initial content is unused and
+ *                          its final content is indeterminate.
+ *                          It must not alias or otherwise overlap any of the
+ *                          other parameters.
+ */
+void mbedtls_mpi_core_from_mont_rep( mbedtls_mpi_uint *X,
+                                     const mbedtls_mpi_uint *A,
+                                     const mbedtls_mpi_uint *N,
+                                     size_t AN_limbs,
+                                     mbedtls_mpi_uint mm,
+                                     mbedtls_mpi_uint *T );
+
 /* END MERGE SLOT 3 */
 
 /* BEGIN MERGE SLOT 4 */
diff --git a/library/bignum_mod.c b/library/bignum_mod.c
index 0057eba..31e18e7 100644
--- a/library/bignum_mod.c
+++ b/library/bignum_mod.c
@@ -79,7 +79,7 @@
             if (m->rep.mont.rr != NULL)
             {
                 mbedtls_platform_zeroize( (mbedtls_mpi_uint *) m->rep.mont.rr,
-                                           m->limbs );
+                                           m->limbs * sizeof(mbedtls_mpi_uint) );
                 mbedtls_free( (mbedtls_mpi_uint *)m->rep.mont.rr );
                 m->rep.mont.rr = NULL;
             }
@@ -191,6 +191,95 @@
 
     return( 0 );
 }
+
+static int mbedtls_mpi_mod_inv_mont( mbedtls_mpi_mod_residue *X,
+                                     const mbedtls_mpi_mod_residue *A,
+                                     const mbedtls_mpi_mod_modulus *N,
+                                     mbedtls_mpi_uint *working_memory )
+{
+    /* Input already in Montgomery form, so there's little to do */
+    mbedtls_mpi_mod_raw_inv_prime( X->p, A->p,
+                                   N->p, N->limbs,
+                                   N->rep.mont.rr,
+                                   working_memory );
+    return( 0 );
+}
+
+static int mbedtls_mpi_mod_inv_non_mont( mbedtls_mpi_mod_residue *X,
+                                         const mbedtls_mpi_mod_residue *A,
+                                         const mbedtls_mpi_mod_modulus *N,
+                                         mbedtls_mpi_uint *working_memory )
+{
+    /* Need to convert input into Montgomery form */
+
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+    mbedtls_mpi_mod_modulus Nmont;
+    mbedtls_mpi_mod_modulus_init( &Nmont );
+
+    MBEDTLS_MPI_CHK( mbedtls_mpi_mod_modulus_setup( &Nmont, N->p, N->limbs,
+                                         MBEDTLS_MPI_MOD_REP_MONTGOMERY ) );
+
+    /* We'll use X->p to hold the Montgomery form of the input A->p */
+    mbedtls_mpi_core_to_mont_rep( X->p, A->p, Nmont.p, Nmont.limbs,
+                                  Nmont.rep.mont.mm, Nmont.rep.mont.rr,
+                                  working_memory );
+
+    mbedtls_mpi_mod_raw_inv_prime( X->p, X->p,
+                                   Nmont.p, Nmont.limbs,
+                                   Nmont.rep.mont.rr,
+                                   working_memory );
+
+    /* And convert back from Montgomery form */
+
+    mbedtls_mpi_core_from_mont_rep( X->p, X->p, Nmont.p, Nmont.limbs,
+                                    Nmont.rep.mont.mm, working_memory );
+
+cleanup:
+    mbedtls_mpi_mod_modulus_free( &Nmont );
+    return( ret );
+}
+
+int mbedtls_mpi_mod_inv( mbedtls_mpi_mod_residue *X,
+                         const mbedtls_mpi_mod_residue *A,
+                         const mbedtls_mpi_mod_modulus *N )
+{
+    if( X->limbs != N->limbs || A->limbs != N->limbs )
+        return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+
+    /* Zero has the same value regardless of Montgomery form or not */
+    if( mbedtls_mpi_core_check_zero_ct( A->p, A->limbs ) == 0 )
+        return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+
+    size_t working_limbs =
+                    mbedtls_mpi_mod_raw_inv_prime_working_limbs( N->limbs );
+
+    mbedtls_mpi_uint *working_memory = mbedtls_calloc( working_limbs,
+                                                     sizeof(mbedtls_mpi_uint) );
+    if( working_memory == NULL )
+        return( MBEDTLS_ERR_MPI_ALLOC_FAILED );
+
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+    switch( N->int_rep )
+    {
+        case MBEDTLS_MPI_MOD_REP_MONTGOMERY:
+            ret = mbedtls_mpi_mod_inv_mont( X, A, N, working_memory );
+            break;
+        case MBEDTLS_MPI_MOD_REP_OPT_RED:
+            ret = mbedtls_mpi_mod_inv_non_mont( X, A, N, working_memory );
+            break;
+        default:
+            ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
+            break;
+    }
+
+    mbedtls_platform_zeroize( working_memory,
+                              working_limbs * sizeof(mbedtls_mpi_uint) );
+    mbedtls_free( working_memory );
+
+    return ret;
+}
 /* END MERGE SLOT 3 */
 
 /* BEGIN MERGE SLOT 4 */
diff --git a/library/bignum_mod.h b/library/bignum_mod.h
index 11b4e98..95aaacc 100644
--- a/library/bignum_mod.h
+++ b/library/bignum_mod.h
@@ -2,6 +2,63 @@
  *  Modular bignum functions
  *
  * This module implements operations on integers modulo some fixed modulus.
+ *
+ * The functions in this module obey the following conventions unless
+ * explicitly indicated otherwise:
+ *
+ * - **Modulus parameters**: the modulus is passed as a pointer to a structure
+ *   of type #mbedtls_mpi_mod_modulus. The structure must be set up with an
+ *   array of limbs storing the bignum value of the modulus. The modulus must
+ *   be odd and is assumed to have no leading zeroes. The modulus is usually
+ *   named \c N and is usually input-only. Functions which take a parameter
+ *   of type \c const #mbedtls_mpi_mod_modulus* must not modify its value.
+ * - **Bignum parameters**: Bignums are passed as pointers to an array of
+ *   limbs or to a #mbedtls_mpi_mod_residue structure. A limb has the type
+ *   #mbedtls_mpi_uint. Residues must be initialized before use, and must be
+ *   associated with the modulus \c N. Unless otherwise specified:
+ *     - Bignum parameters called \c A, \c B, ... are inputs and are not
+ *       modified by the function. Functions which take a parameter of
+ *       type \c const #mbedtls_mpi_mod_residue* must not modify its value.
+ *     - Bignum parameters called \c X, \c Y, ... are outputs or input-output.
+ *       The initial bignum value of output-only parameters is ignored, but
+ *       they must be set up and associated with the modulus \c N. Some
+ *       functions (typically constant-flow) require that the limbs in an
+ *       output residue are initialized.
+ *     - Bignum parameters called \c p are inputs used to set up a modulus or
+ *       residue. These must be pointers to an array of limbs.
+ *     - \c T is a temporary storage area. The initial content of such a
+ *       parameter is ignored and the final content is unspecified.
+ *     - Some functions use different names, such as \c r for the residue.
+ * - **Bignum sizes**: bignum sizes are always expressed in limbs. Both
+ *   #mbedtls_mpi_mod_modulus and #mbedtls_mpi_mod_residue have a \c limbs
+ *   member storing its size. All bignum parameters must have the same
+ *   number of limbs as the modulus. All bignum sizes must be at least 1 and
+ *   must be significantly less than #SIZE_MAX. The behavior if a size is 0 is
+ *   undefined.
+ * - **Bignum representation**: the representation of inputs and outputs is
+ *   specified by the \c int_rep field of the modulus.
+ * - **Parameter ordering**: for bignum parameters, outputs come before inputs.
+ *   The modulus is passed after residues. Temporaries come last.
+ * - **Aliasing**: in general, output bignums may be aliased to one or more
+ *   inputs. Modulus values may not be aliased to any other parameter. Outputs
+ *   may not be aliased to one another. Temporaries may not be aliased to any
+ *   other parameter.
+ * - **Overlap**: apart from aliasing of residue pointers (where two residue
+ *   arguments are equal pointers), overlap is not supported and may result
+ *   in undefined behavior.
+ * - **Error handling**: functions generally check compatibility of input
+ *   sizes. Most functions will not check that input values are in canonical
+ *   form (i.e. that \c A < \c N), this is only checked during setup of a
+ *   residue structure.
+ * - **Modular representatives**: all functions expect inputs to be in the
+ *   range [0, \c N - 1] and guarantee outputs in the range [0, \c N - 1].
+ *   Residues are set up with an associated modulus, and operations are only
+ *   guaranteed to work if the modulus is associated with all residue
+ *   parameters. If a residue is passed with a modulus other than the one it
+ *   is associated with, then it may be out of range. If an input is out of
+ *   range, outputs are fully unspecified, though bignum values out of range
+ *   should not cause buffer overflows (beware that this is not extensively
+ *   tested).
  */
 
 /*
@@ -192,6 +249,35 @@
                          const mbedtls_mpi_mod_residue *A,
                          const mbedtls_mpi_mod_residue *B,
                          const mbedtls_mpi_mod_modulus *N );
+
+/**
+ * \brief Perform modular inversion of an MPI with respect to a modulus \p N.
+ *
+ * \p A and \p X must be associated with the modulus \p N and will therefore
+ * have the same number of limbs as \p N.
+ *
+ * \p X may be aliased to \p A.
+ *
+ * \warning  Currently only supports prime moduli, but does not check for them.
+ *
+ * \param[out] X   The modular inverse of \p A with respect to \p N.
+ * \param[in] A    The number to calculate the modular inverse of.
+ *                 Must not be 0.
+ * \param[in] N    The modulus to use.
+ *
+ * \return         \c 0 if successful.
+ * \return         #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p A and \p N do not
+ *                 have the same number of limbs.
+ * \return         #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p A is zero.
+ * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if couldn't allocate enough
+ *                 memory (needed for conversion to and from Mongtomery form
+ *                 when not in Montgomery form already, and for temporary use
+ *                 by the inversion calculation itself).
+ */
+
+int mbedtls_mpi_mod_inv( mbedtls_mpi_mod_residue *X,
+                         const mbedtls_mpi_mod_residue *A,
+                         const mbedtls_mpi_mod_modulus *N );
 /* END MERGE SLOT 3 */
 
 /* BEGIN MERGE SLOT 4 */
diff --git a/library/bignum_mod_raw.c b/library/bignum_mod_raw.c
index c98a1c1..5950ff6 100644
--- a/library/bignum_mod_raw.c
+++ b/library/bignum_mod_raw.c
@@ -120,6 +120,16 @@
     (void) mbedtls_mpi_core_add_if( X, N->p, N->limbs, (unsigned) c );
 }
 
+void mbedtls_mpi_mod_raw_mul( mbedtls_mpi_uint *X,
+                              const mbedtls_mpi_uint *A,
+                              const mbedtls_mpi_uint *B,
+                              const mbedtls_mpi_mod_modulus *N,
+                              mbedtls_mpi_uint *T )
+{
+    mbedtls_mpi_core_montmul( X, A, B, N->limbs, N->p, N->limbs,
+                              N->rep.mont.mm, T );
+}
+
 /* END MERGE SLOT 2 */
 
 /* BEGIN MERGE SLOT 3 */
@@ -183,13 +193,13 @@
                                      const mbedtls_mpi_mod_modulus *m )
 {
     mbedtls_mpi_uint *T;
-    const size_t t_limbs = m->limbs * 2 + 1;
+    const size_t t_limbs = mbedtls_mpi_core_montmul_working_limbs( m->limbs );
 
     if( ( T = (mbedtls_mpi_uint *) mbedtls_calloc( t_limbs, ciL ) ) == NULL )
         return( MBEDTLS_ERR_MPI_ALLOC_FAILED );
 
-    mbedtls_mpi_core_montmul( X, X, m->rep.mont.rr, m->limbs, m->p, m->limbs,
-                              m->rep.mont.mm, T );
+    mbedtls_mpi_core_to_mont_rep( X, X, m->p, m->limbs,
+                                  m->rep.mont.mm, m->rep.mont.rr, T );
 
     mbedtls_platform_zeroize( T, t_limbs * ciL );
     mbedtls_free( T );
@@ -199,15 +209,13 @@
 int mbedtls_mpi_mod_raw_from_mont_rep( mbedtls_mpi_uint *X,
                                        const mbedtls_mpi_mod_modulus *m )
 {
-    const mbedtls_mpi_uint one = 1;
-    const size_t t_limbs = m->limbs * 2 + 1;
+    const size_t t_limbs = mbedtls_mpi_core_montmul_working_limbs( m->limbs );
     mbedtls_mpi_uint *T;
 
     if( ( T = (mbedtls_mpi_uint *) mbedtls_calloc( t_limbs, ciL ) ) == NULL )
         return( MBEDTLS_ERR_MPI_ALLOC_FAILED );
 
-    mbedtls_mpi_core_montmul( X, X, &one, 1, m->p, m->limbs,
-                              m->rep.mont.mm, T );
+    mbedtls_mpi_core_from_mont_rep( X, X, m->p, m->limbs, m->rep.mont.mm, T );
 
     mbedtls_platform_zeroize( T, t_limbs * ciL );
     mbedtls_free( T );
diff --git a/library/bignum_mod_raw.h b/library/bignum_mod_raw.h
index f9968ba..0fac6f8 100644
--- a/library/bignum_mod_raw.h
+++ b/library/bignum_mod_raw.h
@@ -11,6 +11,51 @@
  * the wrong size. The functions in bignum_mod.h provide a higher-level
  * interface that includes protections against accidental misuse, at the
  * expense of code size and sometimes more cumbersome memory management.
+ *
+ * The functions in this module obey the following conventions unless
+ * explicitly indicated otherwise:
+ * - **Modulus parameters**: the modulus is passed as a pointer to a structure
+ *   of type #mbedtls_mpi_mod_modulus. The structure must be set up with an
+ *   array of limbs storing the bignum value of the modulus. The modulus must
+ *   be odd and is assumed to have no leading zeroes. The modulus is usually
+ *   named \c N and is usually input-only.
+ * - **Bignum parameters**: Bignums are passed as pointers to an array of
+ *   limbs. A limb has the type #mbedtls_mpi_uint. Unless otherwise specified:
+ *     - Bignum parameters called \c A, \c B, ... are inputs, and are not
+ *       modified by the function.
+ *     - Bignum parameters called \c X, \c Y are outputs or input-output.
+ *       The initial content of output-only parameters is ignored.
+ *     - \c T is a temporary storage area. The initial content of such a
+ *       parameter is ignored and the final content is unspecified.
+ * - **Bignum sizes**: bignum sizes are usually expressed by the \c limbs
+ *   member of the modulus argument. All bignum parameters must have the same
+ *   number of limbs as the modulus. All bignum sizes must be at least 1 and
+ *   must be significantly less than #SIZE_MAX. The behavior if a size is 0 is
+ *   undefined.
+ * - **Bignum representation**: the representation of inputs and outputs is
+ *   specified by the \c int_rep field of the modulus for arithmetic
+ *   functions. Utility functions may allow for different representation.
+ * - **Parameter ordering**: for bignum parameters, outputs come before inputs.
+ *   The modulus is passed after other bignum input parameters. Temporaries
+ *   come last.
+ * - **Aliasing**: in general, output bignums may be aliased to one or more
+ *   inputs. Modulus values may not be aliased to any other parameter. Outputs
+ *   may not be aliased to one another. Temporaries may not be aliased to any
+ *   other parameter.
+ * - **Overlap**: apart from aliasing of limb array pointers (where two
+ *   arguments are equal pointers), overlap is not supported and may result
+ *   in undefined behavior.
+ * - **Error handling**: This is a low-level module. Functions generally do not
+ *   try to protect against invalid arguments such as nonsensical sizes or
+ *   null pointers. Note that passing bignums with a different size than the
+ *   modulus may lead to buffer overflows. Some functions which allocate
+ *   memory or handle reading/writing of bignums will return an error if
+ *   memory allocation fails or if buffer sizes are invalid.
+ * - **Modular representatives**: all functions expect inputs to be in the
+ *   range [0, \c N - 1] and guarantee outputs in the range [0, \c N - 1]. If
+ *   an input is out of range, outputs are fully unspecified, though bignum
+ *   values out of range should not cause buffer overflows (beware that this is
+ *   not extensively tested).
  */
 
 /*
@@ -170,6 +215,41 @@
                               const mbedtls_mpi_uint *B,
                               const mbedtls_mpi_mod_modulus *N );
 
+/** \brief  Multiply two MPIs, returning the residue modulo the specified
+ *          modulus.
+ *
+ * \note Currently handles the case when `N->int_rep` is
+ * MBEDTLS_MPI_MOD_REP_MONTGOMERY.
+ *
+ * The size of the operation is determined by \p N. \p A, \p B and \p X must
+ * all be associated with the modulus \p N and must all have the same number
+ * of limbs as \p N.
+ *
+ * \p X may be aliased to \p A or \p B, or even both, but may not overlap
+ * either otherwise. They may not alias \p N (since they must be in canonical
+ * form, they cannot == \p N).
+ *
+ * \param[out] X        The address of the result MPI. Must have the same
+ *                      number of limbs as \p N.
+ *                      On successful completion, \p X contains the result of
+ *                      the multiplication `A * B * R^-1` mod N where
+ *                      `R = 2^(biL * N->limbs)`.
+ * \param[in]  A        The address of the first MPI.
+ * \param[in]  B        The address of the second MPI.
+ * \param[in]  N        The address of the modulus. Used to perform a modulo
+ *                      operation on the result of the multiplication.
+ * \param[in,out] T     Temporary storage of size at least 2 * N->limbs + 1
+ *                      limbs. Its initial content is unused and
+ *                      its final content is indeterminate.
+ *                      It must not alias or otherwise overlap any of the
+ *                      other parameters.
+ */
+void mbedtls_mpi_mod_raw_mul( mbedtls_mpi_uint *X,
+                              const mbedtls_mpi_uint *A,
+                              const mbedtls_mpi_uint *B,
+                              const mbedtls_mpi_mod_modulus *N,
+                              mbedtls_mpi_uint *T );
+
 /* END MERGE SLOT 2 */
 
 /* BEGIN MERGE SLOT 3 */
@@ -178,6 +258,10 @@
  * \brief          Returns the number of limbs of working memory required for
  *                 a call to `mbedtls_mpi_mod_raw_inv_prime()`.
  *
+ * \note           This will always be at least
+ *                 `mbedtls_mpi_core_montmul_working_limbs(AN_limbs)`,
+ *                 i.e. sufficient for a call to `mbedtls_mpi_core_montmul()`.
+ *
  * \param AN_limbs The number of limbs in the input `A` and the modulus `N`
  *                 (they must be the same size) that will be given to
  *                 `mbedtls_mpi_mod_raw_inv_prime()`.
diff --git a/library/pk_wrap.c b/library/pk_wrap.c
index 5de8fa6..ea7d726 100644
--- a/library/pk_wrap.c
+++ b/library/pk_wrap.c
@@ -1162,8 +1162,12 @@
     size_t key_len;
     unsigned char buf[MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES];
     unsigned char *p;
-    psa_algorithm_t psa_sig_md =
-        PSA_ALG_ECDSA( mbedtls_hash_info_psa_from_md( md_alg ) );
+    psa_algorithm_t psa_hash = mbedtls_hash_info_psa_from_md( md_alg );
+#if defined(MBEDTLS_ECDSA_DETERMINISTIC)
+    psa_algorithm_t psa_sig_md = PSA_ALG_DETERMINISTIC_ECDSA( psa_hash );
+#else
+    psa_algorithm_t psa_sig_md = PSA_ALG_ECDSA( psa_hash );
+#endif
     size_t curve_bits;
     psa_ecc_family_t curve =
         mbedtls_ecc_group_to_psa( ctx->grp.id, &curve_bits );
diff --git a/programs/fuzz/Makefile b/programs/fuzz/Makefile
index 59a2bb7..8477aa8 100644
--- a/programs/fuzz/Makefile
+++ b/programs/fuzz/Makefile
@@ -1,7 +1,9 @@
 MBEDTLS_TEST_PATH:=../../tests/src
 MBEDTLS_TEST_OBJS:=$(patsubst %.c,%.o,$(wildcard ${MBEDTLS_TEST_PATH}/*.c ${MBEDTLS_TEST_PATH}/drivers/*.c))
 
-LOCAL_CFLAGS = -I../../tests/include -I../../include -D_FILE_OFFSET_BITS=64
+CFLAGS ?= -O2
+WARNING_CFLAGS ?= -Wall -Wextra
+LOCAL_CFLAGS = $(WARNING_CFLAGS) -I../../tests/include -I../../include -D_FILE_OFFSET_BITS=64
 LOCAL_LDFLAGS = ${MBEDTLS_TEST_OBJS}		\
 		-L../../library			\
 		-lmbedtls$(SHARED_SUFFIX)	\
diff --git a/programs/test/benchmark.c b/programs/test/benchmark.c
index 6313c52..1ad2034 100644
--- a/programs/test/benchmark.c
+++ b/programs/test/benchmark.c
@@ -398,7 +398,7 @@
     }
 
     gettimeofday( &tv_cur, NULL );
-    return( ( tv_cur.tv_sec  - tv_init.tv_sec  ) * 1000000
+    return( ( tv_cur.tv_sec  - tv_init.tv_sec  ) * 1000000U
           + ( tv_cur.tv_usec - tv_init.tv_usec ) );
 }
 #endif /* !HAVE_HARDCLOCK */
diff --git a/scripts/code_style.py b/scripts/code_style.py
index 68cd556..8e82b93 100755
--- a/scripts/code_style.py
+++ b/scripts/code_style.py
@@ -22,9 +22,10 @@
 import argparse
 import io
 import os
+import re
 import subprocess
 import sys
-from typing import List
+from typing import FrozenSet, List
 
 UNCRUSTIFY_SUPPORTED_VERSION = "0.75.1"
 CONFIG_FILE = ".uncrustify.cfg"
@@ -32,10 +33,33 @@
 UNCRUSTIFY_ARGS = ["-c", CONFIG_FILE]
 STDOUT_UTF8 = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
 STDERR_UTF8 = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
+CHECK_GENERATED_FILES = "tests/scripts/check-generated-files.sh"
 
 def print_err(*args):
     print("Error: ", *args, file=STDERR_UTF8)
 
+# Match FILENAME(s) in "check SCRIPT (FILENAME...)"
+CHECK_CALL_RE = re.compile(r"\n\s*check\s+[^\s#$&*?;|]+([^\n#$&*?;|]+)",
+                           re.ASCII)
+def list_generated_files() -> FrozenSet[str]:
+    """Return the names of generated files.
+
+    We don't reformat generated files, since the result might be different
+    from the output of the generator. Ideally the result of the generator
+    would conform to the code style, but this would be difficult, especially
+    with respect to the placement of line breaks in long logical lines.
+    """
+    # Parse check-generated-files.sh to get an up-to-date list of
+    # generated files. Read the file rather than calling it so that
+    # this script only depends on Git, Python and uncrustify, and not other
+    # tools such as sh or grep which might not be available on Windows.
+    # This introduces a limitation: check-generated-files.sh must have
+    # the expected format and must list the files explicitly, not through
+    # wildcards or command substitution.
+    content = open(CHECK_GENERATED_FILES, encoding="utf-8").read()
+    checks = re.findall(CHECK_CALL_RE, content)
+    return frozenset(word for s in checks for word in s.split())
+
 def get_src_files() -> List[str]:
     """
     Use git ls-files to get a list of the source files
@@ -52,11 +76,14 @@
         print_err("git ls-files returned: " + str(result.returncode))
         return []
     else:
+        generated_files = list_generated_files()
         src_files = str(result.stdout, "utf-8").split()
-        # Don't correct style for files in 3rdparty/
-        src_files = list(filter( \
-                lambda filename: not filename.startswith("3rdparty/"), \
-                src_files))
+        # Don't correct style for third-party files (and, for simplicity,
+        # companion files in the same subtree), or for automatically
+        # generated files (we're correcting the templates instead).
+        src_files = [filename for filename in src_files
+                     if not (filename.startswith("3rdparty/") or
+                             filename in generated_files)]
         return src_files
 
 def get_uncrustify_version() -> str:
diff --git a/scripts/config.py b/scripts/config.py
index 7e58acd..a53c470 100755
--- a/scripts/config.py
+++ b/scripts/config.py
@@ -194,7 +194,6 @@
     'MBEDTLS_DEPRECATED_WARNING', # conflicts with deprecated options
     'MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED', # influences the use of ECDH in TLS
     'MBEDTLS_ECP_NO_FALLBACK', # removes internal ECP implementation
-    'MBEDTLS_ECP_RESTARTABLE', # incompatible with USE_PSA_CRYPTO
     'MBEDTLS_ENTROPY_FORCE_SHA256', # interacts with CTR_DRBG_128_BIT_KEY
     'MBEDTLS_HAVE_SSE2', # hardware dependency
     'MBEDTLS_MEMORY_BACKTRACE', # depends on MEMORY_BUFFER_ALLOC_C
diff --git a/scripts/mbedtls_dev/bignum_common.py b/scripts/mbedtls_dev/bignum_common.py
index 0339b1a..c4efabf 100644
--- a/scripts/mbedtls_dev/bignum_common.py
+++ b/scripts/mbedtls_dev/bignum_common.py
@@ -39,6 +39,11 @@
         return b
     raise ValueError("Not invertible")
 
+def invmod_positive(a: int, n: int) -> int:
+    """Return a non-negative inverse of a to modulo n."""
+    inv = invmod(a, n)
+    return inv if inv >= 0 else inv + n
+
 def hex_to_int(val: str) -> int:
     """Implement the syntax accepted by mbedtls_test_read_mpi().
 
@@ -244,6 +249,8 @@
     #pylint: disable=abstract-method
     """Target for bignum mod_raw test case generation."""
     moduli = MODULI_DEFAULT # type: List[str]
+    montgomery_form_a = False
+    disallow_zero_a = False
 
     def __init__(self, val_n: str, val_a: str, val_b: str = "0",
                  bits_in_limb: int = 64) -> None:
@@ -264,6 +271,14 @@
         return self.int_n
 
     @property
+    def arg_a(self) -> str:
+        if self.montgomery_form_a:
+            value_a = self.to_montgomery(self.int_a)
+        else:
+            value_a = self.int_a
+        return self.format_arg('{:x}'.format(value_a))
+
+    @property
     def arg_n(self) -> str:
         return self.format_arg(self.val_n)
 
@@ -287,6 +302,8 @@
     def is_valid(self) -> bool:
         if self.int_a >= self.int_n:
             return False
+        if self.disallow_zero_a and self.int_a == 0:
+            return False
         if self.arity == 2 and self.int_b >= self.int_n:
             return False
         return True
diff --git a/scripts/mbedtls_dev/bignum_core.py b/scripts/mbedtls_dev/bignum_core.py
index 158ada9..24d37cb 100644
--- a/scripts/mbedtls_dev/bignum_core.py
+++ b/scripts/mbedtls_dev/bignum_core.py
@@ -757,15 +757,7 @@
     test_function = "mpi_core_exp_mod"
     test_name = "Core modular exponentiation (Mongtomery form only)"
     input_style = "fixed"
-
-    def arguments(self) -> List[str]:
-        # Input 'a' has to be given in Montgomery form
-        mont_a = self.to_montgomery(self.int_a)
-        arg_mont_a = self.format_arg('{:x}'.format(mont_a))
-        return [bignum_common.quote_str(n) for n in [self.arg_n,
-                                                     arg_mont_a,
-                                                     self.arg_b]
-               ] + self.result()
+    montgomery_form_a = True
 
     def result(self) -> List[str]:
         # Result has to be given in Montgomery form too
@@ -818,6 +810,20 @@
             str(-borrow)
         ]
 
+class BignumCoreZeroCheckCT(BignumCoreTarget, bignum_common.OperationCommon):
+    """Test cases for bignum core zero check (constant flow)."""
+    count = 0
+    symbol = "== 0"
+    test_function = "mpi_core_check_zero_ct"
+    test_name = "mpi_core_check_zero_ct"
+    input_style = "variable"
+    arity = 1
+    suffix = True
+
+    def result(self) -> List[str]:
+        result = 1 if self.int_a == 0 else 0
+        return [str(result)]
+
 # END MERGE SLOT 3
 
 # BEGIN MERGE SLOT 4
diff --git a/scripts/mbedtls_dev/bignum_data.py b/scripts/mbedtls_dev/bignum_data.py
index 9658933..0a48e53 100644
--- a/scripts/mbedtls_dev/bignum_data.py
+++ b/scripts/mbedtls_dev/bignum_data.py
@@ -121,6 +121,9 @@
 ONLY_PRIME_MODULI = [
         "53", # safe prime
         "8ac72304057392b5",     # 9999999997777777333 (longer, not safe, prime)
+        # The next prime has a different R in Montgomery form depending on
+        # whether 32- or 64-bit MPIs are used.
+        "152d02c7e14af67fe0bf", # 99999999999999999991999
         SAFE_PRIME_192_BIT_SEED_1,  # safe prime
         SAFE_PRIME_1024_BIT_SEED_3, # safe prime
         ]
diff --git a/scripts/mbedtls_dev/bignum_mod.py b/scripts/mbedtls_dev/bignum_mod.py
index a16699a..25afe30 100644
--- a/scripts/mbedtls_dev/bignum_mod.py
+++ b/scripts/mbedtls_dev/bignum_mod.py
@@ -18,6 +18,7 @@
 
 from . import test_data_generation
 from . import bignum_common
+from .bignum_data import ONLY_PRIME_MODULI
 
 class BignumModTarget(test_data_generation.BaseTarget):
     #pylint: disable=abstract-method, too-few-public-methods
@@ -48,6 +49,42 @@
         # generated cases
         return [self.format_result(result), "0"]
 
+class BignumModInvNonMont(bignum_common.ModOperationCommon, BignumModTarget):
+    """Test cases for bignum mpi_mod_inv() - not in Montgomery form."""
+    moduli = ONLY_PRIME_MODULI  # for now only prime moduli supported
+    symbol = "^ -1"
+    test_function = "mpi_mod_inv_non_mont"
+    test_name = "mbedtls_mpi_mod_inv non-Mont. form"
+    input_style = "fixed"
+    arity = 1
+    suffix = True
+    disallow_zero_a = True
+
+    def result(self) -> List[str]:
+        result = bignum_common.invmod_positive(self.int_a, self.int_n)
+        # To make negative tests easier, append 0 for success to the
+        # generated cases
+        return [self.format_result(result), "0"]
+
+class BignumModInvMont(bignum_common.ModOperationCommon, BignumModTarget):
+    """Test cases for bignum mpi_mod_inv() - Montgomery form."""
+    moduli = ONLY_PRIME_MODULI  # for now only prime moduli supported
+    symbol = "^ -1"
+    test_function = "mpi_mod_inv_mont"
+    test_name = "mbedtls_mpi_mod_inv Mont. form"
+    input_style = "arch_split"  # Mont. form requires arch_split
+    arity = 1
+    suffix = True
+    disallow_zero_a = True
+    montgomery_form_a = True
+
+    def result(self) -> List[str]:
+        result = bignum_common.invmod_positive(self.int_a, self.int_n)
+        mont_result = self.to_montgomery(result)
+        # To make negative tests easier, append 0 for success to the
+        # generated cases
+        return [self.format_result(mont_result), "0"]
+
 # END MERGE SLOT 3
 
 # BEGIN MERGE SLOT 4
diff --git a/scripts/mbedtls_dev/bignum_mod_raw.py b/scripts/mbedtls_dev/bignum_mod_raw.py
index 6fc4c91..09bbbee 100644
--- a/scripts/mbedtls_dev/bignum_mod_raw.py
+++ b/scripts/mbedtls_dev/bignum_mod_raw.py
@@ -50,6 +50,25 @@
         result = (self.int_a - self.int_b) % self.int_n
         return [self.format_result(result)]
 
+class BignumModRawMul(bignum_common.ModOperationCommon,
+                      BignumModRawTarget):
+    """Test cases for bignum mpi_mod_raw_mul()."""
+    symbol = "*"
+    test_function = "mpi_mod_raw_mul"
+    test_name = "mbedtls_mpi_mod_raw_mul"
+    input_style = "arch_split"
+    arity = 2
+
+    def arguments(self) -> List[str]:
+        return [self.format_result(self.to_montgomery(self.int_a)),
+                self.format_result(self.to_montgomery(self.int_b)),
+                bignum_common.quote_str(self.arg_n)
+               ] + self.result()
+
+    def result(self) -> List[str]:
+        result = (self.int_a * self.int_b) % self.int_n
+        return [self.format_result(self.to_montgomery(result))]
+
 # END MERGE SLOT 2
 
 # BEGIN MERGE SLOT 3
@@ -61,24 +80,14 @@
     symbol = "^ -1"
     test_function = "mpi_mod_raw_inv_prime"
     test_name = "mbedtls_mpi_mod_raw_inv_prime (Montgomery form only)"
-    input_style = "fixed"
+    input_style = "arch_split"
     arity = 1
     suffix = True
-
-    @property
-    def is_valid(self) -> bool:
-        return self.int_a > 0 and self.int_a < self.int_n
-
-    @property
-    def arg_a(self) -> str:
-        # Input has to be given in Montgomery form
-        mont_a = self.to_montgomery(self.int_a)
-        return self.format_arg('{:x}'.format(mont_a))
+    montgomery_form_a = True
+    disallow_zero_a = True
 
     def result(self) -> List[str]:
-        result = bignum_common.invmod(self.int_a, self.int_n)
-        if result < 0:
-            result += self.int_n
+        result = bignum_common.invmod_positive(self.int_a, self.int_n)
         mont_result = self.to_montgomery(result)
         return [self.format_result(mont_result)]
 
diff --git a/tests/compat-in-docker.sh b/tests/compat-in-docker.sh
index 3a1cd21..ad73582 100755
--- a/tests/compat-in-docker.sh
+++ b/tests/compat-in-docker.sh
@@ -6,6 +6,10 @@
 # -------
 # This runs compat.sh in a Docker container.
 #
+# WARNING: the Dockerfile used by this script is no longer maintained! See
+# https://github.com/Mbed-TLS/mbedtls-test/blob/master/README.md#quick-start
+# for the set of Docker images we use on the CI.
+#
 # Notes for users
 # ---------------
 # If OPENSSL_CMD, GNUTLS_CLI, or GNUTLS_SERV are specified the path must
diff --git a/tests/docker/bionic/Dockerfile b/tests/docker/bionic/Dockerfile
index 28d33b7..d44cdff 100644
--- a/tests/docker/bionic/Dockerfile
+++ b/tests/docker/bionic/Dockerfile
@@ -4,6 +4,10 @@
 # -------
 # Defines a Docker container suitable to build and run all tests (all.sh),
 # except for those that use a proprietary toolchain.
+#
+# WARNING: this Dockerfile is no longer maintained! See
+# https://github.com/Mbed-TLS/mbedtls-test/blob/master/README.md#quick-start
+# for the set of Docker images we use on the CI.
 
 # Copyright The Mbed TLS Contributors
 # SPDX-License-Identifier: Apache-2.0
diff --git a/tests/make-in-docker.sh b/tests/make-in-docker.sh
index 77dc8ab..0ee08dc 100755
--- a/tests/make-in-docker.sh
+++ b/tests/make-in-docker.sh
@@ -8,6 +8,10 @@
 #
 # See also:
 # - scripts/docker_env.sh for general Docker prerequisites and other information.
+#
+# WARNING: the Dockerfile used by this script is no longer maintained! See
+# https://github.com/Mbed-TLS/mbedtls-test/blob/master/README.md#quick-start
+# for the set of Docker images we use on the CI.
 
 # Copyright The Mbed TLS Contributors
 # SPDX-License-Identifier: Apache-2.0
diff --git a/tests/opt-testcases/tls13-misc.sh b/tests/opt-testcases/tls13-misc.sh
index ed42848..710fb34 100755
--- a/tests/opt-testcases/tls13-misc.sh
+++ b/tests/opt-testcases/tls13-misc.sh
@@ -56,11 +56,8 @@
             -s "No matched ciphersuite"
 
 requires_all_configs_enabled MBEDTLS_SSL_PROTO_TLS1_3 MBEDTLS_SSL_SESSION_TICKETS MBEDTLS_SSL_SRV_C \
-                             MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C MBEDTLS_HAVE_TIME
-requires_any_configs_enabled MBEDTLS_KEY_EXCHANGE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED \
-                             MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
-requires_any_configs_enabled MBEDTLS_KEY_EXCHANGE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED \
-                             MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
+                             MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C MBEDTLS_HAVE_TIME \
+                             MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED
 run_test "TLS 1.3 m->m: Multiple PSKs: valid ticket, reconnect with ticket" \
          "$P_SRV force_version=tls13 tls13_kex_modes=psk_ephemeral debug_level=5 psk_identity=Client_identity psk=6162636465666768696a6b6c6d6e6f70 tickets=8" \
          "$P_CLI force_version=tls13 tls13_kex_modes=psk_ephemeral debug_level=5 psk_identity=Client_identity psk=6162636465666768696a6b6c6d6e6f70 reco_mode=1 reconnect=1" \
@@ -73,11 +70,8 @@
          -S "ticket is not authentic"
 
 requires_all_configs_enabled MBEDTLS_SSL_PROTO_TLS1_3 MBEDTLS_SSL_SESSION_TICKETS MBEDTLS_SSL_SRV_C \
-                             MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C MBEDTLS_HAVE_TIME
-requires_any_configs_enabled MBEDTLS_KEY_EXCHANGE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED \
-                             MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
-requires_any_configs_enabled MBEDTLS_KEY_EXCHANGE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED \
-                             MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
+                             MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C MBEDTLS_HAVE_TIME \
+                             MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED
 run_test "TLS 1.3 m->m: Multiple PSKs: invalid ticket, reconnect with PSK" \
          "$P_SRV force_version=tls13 tls13_kex_modes=psk_ephemeral debug_level=5 psk_identity=Client_identity psk=6162636465666768696a6b6c6d6e6f70 tickets=8 dummy_ticket=1" \
          "$P_CLI force_version=tls13 tls13_kex_modes=psk_ephemeral debug_level=5 psk_identity=Client_identity psk=6162636465666768696a6b6c6d6e6f70 reco_mode=1 reconnect=1" \
@@ -90,11 +84,9 @@
          -s "ticket is not authentic"
 
 requires_all_configs_enabled MBEDTLS_SSL_PROTO_TLS1_3 MBEDTLS_SSL_SESSION_TICKETS MBEDTLS_SSL_SRV_C \
-                             MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C MBEDTLS_HAVE_TIME
-requires_any_configs_enabled MBEDTLS_KEY_EXCHANGE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED \
-                             MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
-requires_any_configs_enabled MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED \
-                             MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
+                             MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C MBEDTLS_HAVE_TIME \
+                             MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED \
+                             MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED
 run_test "TLS 1.3 m->m: Session resumption failure, ticket authentication failed." \
          "$P_SRV debug_level=4 crt_file=data_files/server5.crt key_file=data_files/server5.key force_version=tls13 tickets=8 dummy_ticket=1" \
          "$P_CLI debug_level=4 reco_mode=1 reconnect=1" \
@@ -111,11 +103,9 @@
          -S "Ticket age outside tolerance window"
 
 requires_all_configs_enabled MBEDTLS_SSL_PROTO_TLS1_3 MBEDTLS_SSL_SESSION_TICKETS MBEDTLS_SSL_SRV_C \
-                             MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C MBEDTLS_HAVE_TIME
-requires_any_configs_enabled MBEDTLS_KEY_EXCHANGE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED \
-                             MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
-requires_any_configs_enabled MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED \
-                             MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
+                             MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C MBEDTLS_HAVE_TIME \
+                             MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED \
+                             MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED
 run_test "TLS 1.3 m->m: Session resumption failure, ticket expired." \
          "$P_SRV debug_level=4 crt_file=data_files/server5.crt key_file=data_files/server5.key force_version=tls13 tickets=8 dummy_ticket=2" \
          "$P_CLI debug_level=4 reco_mode=1 reconnect=1" \
@@ -132,11 +122,9 @@
          -S "Ticket age outside tolerance window"
 
 requires_all_configs_enabled MBEDTLS_SSL_PROTO_TLS1_3 MBEDTLS_SSL_SESSION_TICKETS MBEDTLS_SSL_SRV_C \
-                             MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C MBEDTLS_HAVE_TIME
-requires_any_configs_enabled MBEDTLS_KEY_EXCHANGE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED \
-                             MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
-requires_any_configs_enabled MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED \
-                             MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
+                             MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C MBEDTLS_HAVE_TIME \
+                             MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED \
+                             MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED
 run_test "TLS 1.3 m->m: Session resumption failure, invalid start time." \
          "$P_SRV debug_level=4 crt_file=data_files/server5.crt key_file=data_files/server5.key force_version=tls13 tickets=8 dummy_ticket=3" \
          "$P_CLI debug_level=4 reco_mode=1 reconnect=1" \
@@ -153,11 +141,9 @@
          -S "Ticket age outside tolerance window"
 
 requires_all_configs_enabled MBEDTLS_SSL_PROTO_TLS1_3 MBEDTLS_SSL_SESSION_TICKETS MBEDTLS_SSL_SRV_C \
-                             MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C MBEDTLS_HAVE_TIME
-requires_any_configs_enabled MBEDTLS_KEY_EXCHANGE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED \
-                             MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
-requires_any_configs_enabled MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED \
-                             MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
+                             MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C MBEDTLS_HAVE_TIME \
+                             MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED \
+                             MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED
 run_test "TLS 1.3 m->m: Session resumption failure, ticket expired. too old" \
          "$P_SRV debug_level=4 crt_file=data_files/server5.crt key_file=data_files/server5.key force_version=tls13 tickets=8 dummy_ticket=4" \
          "$P_CLI debug_level=4 reco_mode=1 reconnect=1" \
@@ -174,11 +160,9 @@
          -S "Ticket age outside tolerance window"
 
 requires_all_configs_enabled MBEDTLS_SSL_PROTO_TLS1_3 MBEDTLS_SSL_SESSION_TICKETS MBEDTLS_SSL_SRV_C \
-                             MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C MBEDTLS_HAVE_TIME
-requires_any_configs_enabled MBEDTLS_KEY_EXCHANGE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED \
-                             MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
-requires_any_configs_enabled MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED \
-                             MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
+                             MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C MBEDTLS_HAVE_TIME \
+                             MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED \
+                             MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED
 run_test "TLS 1.3 m->m: Session resumption failure, age outside tolerance window, too young." \
          "$P_SRV debug_level=4 crt_file=data_files/server5.crt key_file=data_files/server5.key force_version=tls13 tickets=8 dummy_ticket=5" \
          "$P_CLI debug_level=4 reco_mode=1 reconnect=1" \
@@ -195,11 +179,9 @@
          -s "Ticket age outside tolerance window"
 
 requires_all_configs_enabled MBEDTLS_SSL_PROTO_TLS1_3 MBEDTLS_SSL_SESSION_TICKETS MBEDTLS_SSL_SRV_C \
-                             MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C MBEDTLS_HAVE_TIME
-requires_any_configs_enabled MBEDTLS_KEY_EXCHANGE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED \
-                             MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
-requires_any_configs_enabled MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED \
-                             MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
+                             MBEDTLS_SSL_CLI_C MBEDTLS_DEBUG_C MBEDTLS_HAVE_TIME \
+                             MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED \
+                             MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED
 run_test "TLS 1.3 m->m: Session resumption failure, age outside tolerance window, too old." \
          "$P_SRV debug_level=4 crt_file=data_files/server5.crt key_file=data_files/server5.key force_version=tls13 tickets=8 dummy_ticket=6" \
          "$P_CLI debug_level=4 reco_mode=1 reconnect=1" \
diff --git a/tests/scripts/all-in-docker.sh b/tests/scripts/all-in-docker.sh
index 8c9ff47..7c03d91 100755
--- a/tests/scripts/all-in-docker.sh
+++ b/tests/scripts/all-in-docker.sh
@@ -6,6 +6,10 @@
 # -------
 # This runs all.sh (except for armcc) in a Docker container.
 #
+# WARNING: the Dockerfile used by this script is no longer maintained! See
+# https://github.com/Mbed-TLS/mbedtls-test/blob/master/README.md#quick-start
+# for the set of Docker images we use on the CI.
+#
 # Notes for users
 # ---------------
 # See docker_env.sh for prerequisites and other information.
diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh
index de5fb75..18dc8a6 100755
--- a/tests/scripts/all.sh
+++ b/tests/scripts/all.sh
@@ -872,12 +872,6 @@
     fi
     tests/scripts/check_test_cases.py -q $opt
     unset opt
-
-    # Check that no tests are explicitely disabled when USE_PSA_CRYPTO is set
-    # as a matter of policy to ensure there is no missed testing
-    msg "Check: explicitely disabled test with USE_PSA_CRYPTO"  # < 1s
-    not grep -n 'depends_on:.*!MBEDTLS_USE_PSA_CRYPTO' tests/suites/*.function tests/suites/*.data
-    not grep -n '^ *requires_config_disabled.*MBEDTLS_USE_PSA_CRYPTO' tests/ssl-opt.sh tests/opt-testcases/*.sh
 }
 
 component_check_doxygen_warnings () {
@@ -1893,10 +1887,13 @@
 component_build_module_alt () {
     msg "build: MBEDTLS_XXX_ALT" # ~30s
     scripts/config.py full
-    # Disable options that are incompatible with some ALT implementations.
+
+    # Disable options that are incompatible with some ALT implementations:
     # aesni.c and padlock.c reference mbedtls_aes_context fields directly.
     scripts/config.py unset MBEDTLS_AESNI_C
     scripts/config.py unset MBEDTLS_PADLOCK_C
+    # MBEDTLS_ECP_RESTARTABLE is documented as incompatible.
+    scripts/config.py unset MBEDTLS_ECP_RESTARTABLE
     # You can only have one threading implementation: alt or pthread, not both.
     scripts/config.py unset MBEDTLS_THREADING_PTHREAD
     # The SpecifiedECDomain parsing code accesses mbedtls_ecp_group fields
@@ -1908,10 +1905,12 @@
     # MBEDTLS_SHA512_*ALT can't be used with MBEDTLS_SHA512_USE_A64_CRYPTO_*
     scripts/config.py unset MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT
     scripts/config.py unset MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY
+
     # Enable all MBEDTLS_XXX_ALT for whole modules. Do not enable
     # MBEDTLS_XXX_YYY_ALT which are for single functions.
     scripts/config.py set-all 'MBEDTLS_([A-Z0-9]*|NIST_KW)_ALT'
     scripts/config.py unset MBEDTLS_DHM_ALT #incompatible with MBEDTLS_DEBUG_C
+
     # We can only compile, not link, since we don't have any implementations
     # suitable for testing with the dummy alt headers.
     make CC=gcc CFLAGS='-Werror -Wall -Wextra -I../tests/include/alt-dummy' lib
@@ -1932,7 +1931,6 @@
     # full minus MBEDTLS_USE_PSA_CRYPTO: run the same set of tests as basic-build-test.sh
     msg "build: cmake, full config minus MBEDTLS_USE_PSA_CRYPTO, ASan"
     scripts/config.py full
-    scripts/config.py set MBEDTLS_ECP_RESTARTABLE  # not using PSA, so enable restartable ECC
     scripts/config.py unset MBEDTLS_PSA_CRYPTO_C
     scripts/config.py unset MBEDTLS_USE_PSA_CRYPTO
     scripts/config.py unset MBEDTLS_SSL_PROTO_TLS1_3
@@ -1947,6 +1945,9 @@
     msg "test: main suites (full minus MBEDTLS_USE_PSA_CRYPTO)"
     make test
 
+    # Note: ssl-opt.sh has some test cases that depend on
+    # MBEDTLS_ECP_RESTARTABLE && !MBEDTLS_USE_PSA_CRYPTO
+    # This is the only component where those tests are not skipped.
     msg "test: ssl-opt.sh (full minus MBEDTLS_USE_PSA_CRYPTO)"
     tests/ssl-opt.sh
 
diff --git a/tests/scripts/basic-build-test.sh b/tests/scripts/basic-build-test.sh
index a96254f..3dc8a73 100755
--- a/tests/scripts/basic-build-test.sh
+++ b/tests/scripts/basic-build-test.sh
@@ -243,35 +243,16 @@
     echo
 
 
-    # Step 4e - Coverage
-    echo "Coverage"
-
-    LINES_TESTED=$(tail -n4 cov-$TEST_OUTPUT|sed -n -e 's/  lines......: [0-9]*.[0-9]% (\([0-9]*\) of [0-9]* lines)/\1/p')
-    LINES_TOTAL=$(tail -n4 cov-$TEST_OUTPUT|sed -n -e 's/  lines......: [0-9]*.[0-9]% ([0-9]* of \([0-9]*\) lines)/\1/p')
-    FUNCS_TESTED=$(tail -n4 cov-$TEST_OUTPUT|sed -n -e 's/  functions..: [0-9]*.[0-9]% (\([0-9]*\) of [0-9]* functions)$/\1/p')
-    FUNCS_TOTAL=$(tail -n4 cov-$TEST_OUTPUT|sed -n -e 's/  functions..: [0-9]*.[0-9]% ([0-9]* of \([0-9]*\) functions)$/\1/p')
-    BRANCHES_TESTED=$(tail -n4 cov-$TEST_OUTPUT|sed -n -e 's/  branches...: [0-9]*.[0-9]% (\([0-9]*\) of [0-9]* branches)$/\1/p')
-    BRANCHES_TOTAL=$(tail -n4 cov-$TEST_OUTPUT|sed -n -e 's/  branches...: [0-9]*.[0-9]% ([0-9]* of \([0-9]*\) branches)$/\1/p')
-
-    LINES_PERCENT=$((1000*$LINES_TESTED/$LINES_TOTAL))
-    LINES_PERCENT="$(($LINES_PERCENT/10)).$(($LINES_PERCENT-($LINES_PERCENT/10)*10))"
-
-    FUNCS_PERCENT=$((1000*$FUNCS_TESTED/$FUNCS_TOTAL))
-    FUNCS_PERCENT="$(($FUNCS_PERCENT/10)).$(($FUNCS_PERCENT-($FUNCS_PERCENT/10)*10))"
-
-    BRANCHES_PERCENT=$((1000*$BRANCHES_TESTED/$BRANCHES_TOTAL))
-    BRANCHES_PERCENT="$(($BRANCHES_PERCENT/10)).$(($BRANCHES_PERCENT-($BRANCHES_PERCENT/10)*10))"
+    # Step 4e - Coverage report
+    echo "Coverage statistics:"
+    sed -n '1,/^Overall coverage/d; /%/p' cov-$TEST_OUTPUT
+    echo
 
     rm unit-test-$TEST_OUTPUT
     rm sys-test-$TEST_OUTPUT
     rm compat-test-$TEST_OUTPUT
     rm cov-$TEST_OUTPUT
 
-    echo "Lines Tested       : $LINES_TESTED of $LINES_TOTAL $LINES_PERCENT%"
-    echo "Functions Tested   : $FUNCS_TESTED of $FUNCS_TOTAL $FUNCS_PERCENT%"
-    echo "Branches Tested    : $BRANCHES_TESTED of $BRANCHES_TOTAL $BRANCHES_PERCENT%"
-    echo
-
     # Mark the report generation as having succeeded. This must be the
     # last thing in the report generation.
     touch "basic-build-test-$$.ok"
diff --git a/tests/scripts/basic-in-docker.sh b/tests/scripts/basic-in-docker.sh
index 1f65710..02cafb0 100755
--- a/tests/scripts/basic-in-docker.sh
+++ b/tests/scripts/basic-in-docker.sh
@@ -9,6 +9,10 @@
 # in the default configuration, partial test runs in the reference
 # configurations, and some dependency tests.
 #
+# WARNING: the Dockerfile used by this script is no longer maintained! See
+# https://github.com/Mbed-TLS/mbedtls-test/blob/master/README.md#quick-start
+# for the set of Docker images we use on the CI.
+#
 # Notes for users
 # ---------------
 # See docker_env.sh for prerequisites and other information.
diff --git a/tests/scripts/check-generated-files.sh b/tests/scripts/check-generated-files.sh
index 3006ec7..946794c 100755
--- a/tests/scripts/check-generated-files.sh
+++ b/tests/scripts/check-generated-files.sh
@@ -116,6 +116,16 @@
     fi
 }
 
+# Note: if the format of calls to the "check" function changes, update
+# scripts/code_style.py accordingly. For generated C source files (*.h or *.c),
+# the format must be "check SCRIPT FILENAME...". For other source files,
+# any shell syntax is permitted (including e.g. command substitution).
+
+# Note: Instructions to generate those files are replicated in:
+#   - **/Makefile (to (re)build them with make)
+#   - **/CMakeLists.txt (to (re)build them with cmake)
+#   - scripts/make_generated_files.bat (to generate them under Windows)
+
 check scripts/generate_errors.pl library/error.c
 check scripts/generate_query_config.pl programs/test/query_config.c
 check scripts/generate_driver_wrappers.py library/psa_crypto_driver_wrappers.c
diff --git a/tests/scripts/check_names.py b/tests/scripts/check_names.py
index 13b6c2d..7398f3c 100755
--- a/tests/scripts/check_names.py
+++ b/tests/scripts/check_names.py
@@ -444,8 +444,11 @@
                     # Match typedefs and brackets only when they are at the
                     # beginning of the line -- if they are indented, they might
                     # be sub-structures within structs, etc.
+                    optional_c_identifier = r"([_a-zA-Z][_a-zA-Z0-9]*)?"
                     if (state == states.OUTSIDE_KEYWORD and
-                            re.search(r"^(typedef +)?enum +{", line)):
+                            re.search(r"^(typedef +)?enum " + \
+                                    optional_c_identifier + \
+                                    r" *{", line)):
                         state = states.IN_BRACES
                     elif (state == states.OUTSIDE_KEYWORD and
                           re.search(r"^(typedef +)?enum", line)):
diff --git a/tests/scripts/depends.py b/tests/scripts/depends.py
index 0d6ec94..d09b732 100755
--- a/tests/scripts/depends.py
+++ b/tests/scripts/depends.py
@@ -234,6 +234,7 @@
     'MBEDTLS_ECP_C': ['MBEDTLS_ECDSA_C',
                       'MBEDTLS_ECDH_C',
                       'MBEDTLS_ECJPAKE_C',
+                      'MBEDTLS_ECP_RESTARTABLE',
                       'MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED',
                       'MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED',
                       'MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED',
diff --git a/tests/scripts/docker_env.sh b/tests/scripts/docker_env.sh
index be96c72..3dbc41d 100755
--- a/tests/scripts/docker_env.sh
+++ b/tests/scripts/docker_env.sh
@@ -9,6 +9,10 @@
 # thus making it easier to get set up as well as isolating test dependencies
 # (which include legacy/insecure configurations of openssl and gnutls).
 #
+# WARNING: the Dockerfile used by this script is no longer maintained! See
+# https://github.com/Mbed-TLS/mbedtls-test/blob/master/README.md#quick-start
+# for the set of Docker images we use on the CI.
+#
 # Notes for users
 # ---------------
 # This script expects a Linux x86_64 system with a recent version of Docker
diff --git a/tests/ssl-opt-in-docker.sh b/tests/ssl-opt-in-docker.sh
index e7bb01d..c8c6697 100755
--- a/tests/ssl-opt-in-docker.sh
+++ b/tests/ssl-opt-in-docker.sh
@@ -6,6 +6,10 @@
 # -------
 # This runs ssl-opt.sh in a Docker container.
 #
+# WARNING: the Dockerfile used by this script is no longer maintained! See
+# https://github.com/Mbed-TLS/mbedtls-test/blob/master/README.md#quick-start
+# for the set of Docker images we use on the CI.
+#
 # Notes for users
 # ---------------
 # If OPENSSL_CMD, GNUTLS_CLI, or GNUTLS_SERV are specified, the path must
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index ea57b25..0d4ce6e 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -8478,10 +8478,12 @@
             -C "mbedtls_ecdh_make_public.*4b00" \
             -C "mbedtls_pk_sign.*4b00"
 
+# With USE_PSA disabled we expect full restartable behaviour.
 requires_config_enabled MBEDTLS_ECP_RESTARTABLE
 requires_config_enabled MBEDTLS_ECP_DP_SECP256R1_ENABLED
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
-run_test    "EC restart: TLS, max_ops=1000" \
+requires_config_disabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "EC restart: TLS, max_ops=1000 (no USE_PSA)" \
             "$P_SRV curves=secp256r1 auth_mode=required" \
             "$P_CLI force_ciphersuite=TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256 \
              key_file=data_files/server5.key crt_file=data_files/server5.crt  \
@@ -8492,6 +8494,25 @@
             -c "mbedtls_ecdh_make_public.*4b00" \
             -c "mbedtls_pk_sign.*4b00"
 
+# With USE_PSA enabled we expect only partial restartable behaviour:
+# everything except ECDH (where TLS calls PSA directly).
+requires_config_enabled MBEDTLS_ECP_RESTARTABLE
+requires_config_enabled MBEDTLS_ECP_DP_SECP256R1_ENABLED
+requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "EC restart: TLS, max_ops=1000 (USE_PSA)" \
+            "$P_SRV curves=secp256r1 auth_mode=required" \
+            "$P_CLI force_ciphersuite=TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256 \
+             key_file=data_files/server5.key crt_file=data_files/server5.crt  \
+             debug_level=1 ec_max_ops=1000" \
+            0 \
+            -c "x509_verify_cert.*4b00" \
+            -c "mbedtls_pk_verify.*4b00" \
+            -C "mbedtls_ecdh_make_public.*4b00" \
+            -c "mbedtls_pk_sign.*4b00"
+
+# This works the same with & without USE_PSA as we never get to ECDH:
+# we abort as soon as we determined the cert is bad.
 requires_config_enabled MBEDTLS_ECP_RESTARTABLE
 requires_config_enabled MBEDTLS_ECP_DP_SECP256R1_ENABLED
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
@@ -8511,10 +8532,12 @@
             -c "! mbedtls_ssl_handshake returned" \
             -c "X509 - Certificate verification failed"
 
+# With USE_PSA disabled we expect full restartable behaviour.
 requires_config_enabled MBEDTLS_ECP_RESTARTABLE
 requires_config_enabled MBEDTLS_ECP_DP_SECP256R1_ENABLED
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
-run_test    "EC restart: TLS, max_ops=1000, auth_mode=optional badsign" \
+requires_config_disabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "EC restart: TLS, max_ops=1000, auth_mode=optional badsign (no USE_PSA)" \
             "$P_SRV curves=secp256r1 auth_mode=required \
              crt_file=data_files/server5-badsign.crt \
              key_file=data_files/server5.key" \
@@ -8530,10 +8553,34 @@
             -C "! mbedtls_ssl_handshake returned" \
             -C "X509 - Certificate verification failed"
 
+# With USE_PSA enabled we expect only partial restartable behaviour:
+# everything except ECDH (where TLS calls PSA directly).
 requires_config_enabled MBEDTLS_ECP_RESTARTABLE
 requires_config_enabled MBEDTLS_ECP_DP_SECP256R1_ENABLED
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
-run_test    "EC restart: TLS, max_ops=1000, auth_mode=none badsign" \
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "EC restart: TLS, max_ops=1000, auth_mode=optional badsign (USE_PSA)" \
+            "$P_SRV curves=secp256r1 auth_mode=required \
+             crt_file=data_files/server5-badsign.crt \
+             key_file=data_files/server5.key" \
+            "$P_CLI force_ciphersuite=TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256 \
+             key_file=data_files/server5.key crt_file=data_files/server5.crt  \
+             debug_level=1 ec_max_ops=1000 auth_mode=optional" \
+            0 \
+            -c "x509_verify_cert.*4b00" \
+            -c "mbedtls_pk_verify.*4b00" \
+            -C "mbedtls_ecdh_make_public.*4b00" \
+            -c "mbedtls_pk_sign.*4b00" \
+            -c "! The certificate is not correctly signed by the trusted CA" \
+            -C "! mbedtls_ssl_handshake returned" \
+            -C "X509 - Certificate verification failed"
+
+# With USE_PSA disabled we expect full restartable behaviour.
+requires_config_enabled MBEDTLS_ECP_RESTARTABLE
+requires_config_enabled MBEDTLS_ECP_DP_SECP256R1_ENABLED
+requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
+requires_config_disabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "EC restart: TLS, max_ops=1000, auth_mode=none badsign (no USE_PSA)" \
             "$P_SRV curves=secp256r1 auth_mode=required \
              crt_file=data_files/server5-badsign.crt \
              key_file=data_files/server5.key" \
@@ -8549,10 +8596,34 @@
             -C "! mbedtls_ssl_handshake returned" \
             -C "X509 - Certificate verification failed"
 
+# With USE_PSA enabled we expect only partial restartable behaviour:
+# everything except ECDH (where TLS calls PSA directly).
 requires_config_enabled MBEDTLS_ECP_RESTARTABLE
 requires_config_enabled MBEDTLS_ECP_DP_SECP256R1_ENABLED
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
-run_test    "EC restart: DTLS, max_ops=1000" \
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "EC restart: TLS, max_ops=1000, auth_mode=none badsign (USE_PSA)" \
+            "$P_SRV curves=secp256r1 auth_mode=required \
+             crt_file=data_files/server5-badsign.crt \
+             key_file=data_files/server5.key" \
+            "$P_CLI force_ciphersuite=TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256 \
+             key_file=data_files/server5.key crt_file=data_files/server5.crt  \
+             debug_level=1 ec_max_ops=1000 auth_mode=none" \
+            0 \
+            -C "x509_verify_cert.*4b00" \
+            -c "mbedtls_pk_verify.*4b00" \
+            -C "mbedtls_ecdh_make_public.*4b00" \
+            -c "mbedtls_pk_sign.*4b00" \
+            -C "! The certificate is not correctly signed by the trusted CA" \
+            -C "! mbedtls_ssl_handshake returned" \
+            -C "X509 - Certificate verification failed"
+
+# With USE_PSA disabled we expect full restartable behaviour.
+requires_config_enabled MBEDTLS_ECP_RESTARTABLE
+requires_config_enabled MBEDTLS_ECP_DP_SECP256R1_ENABLED
+requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
+requires_config_disabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "EC restart: DTLS, max_ops=1000 (no USE_PSA)" \
             "$P_SRV curves=secp256r1 auth_mode=required dtls=1" \
             "$P_CLI force_ciphersuite=TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256 \
              key_file=data_files/server5.key crt_file=data_files/server5.crt  \
@@ -8563,10 +8634,29 @@
             -c "mbedtls_ecdh_make_public.*4b00" \
             -c "mbedtls_pk_sign.*4b00"
 
+# With USE_PSA enabled we expect only partial restartable behaviour:
+# everything except ECDH (where TLS calls PSA directly).
 requires_config_enabled MBEDTLS_ECP_RESTARTABLE
 requires_config_enabled MBEDTLS_ECP_DP_SECP256R1_ENABLED
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
-run_test    "EC restart: TLS, max_ops=1000 no client auth" \
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "EC restart: DTLS, max_ops=1000 (USE_PSA)" \
+            "$P_SRV curves=secp256r1 auth_mode=required dtls=1" \
+            "$P_CLI force_ciphersuite=TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256 \
+             key_file=data_files/server5.key crt_file=data_files/server5.crt  \
+             dtls=1 debug_level=1 ec_max_ops=1000" \
+            0 \
+            -c "x509_verify_cert.*4b00" \
+            -c "mbedtls_pk_verify.*4b00" \
+            -C "mbedtls_ecdh_make_public.*4b00" \
+            -c "mbedtls_pk_sign.*4b00"
+
+# With USE_PSA disabled we expect full restartable behaviour.
+requires_config_enabled MBEDTLS_ECP_RESTARTABLE
+requires_config_enabled MBEDTLS_ECP_DP_SECP256R1_ENABLED
+requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
+requires_config_disabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "EC restart: TLS, max_ops=1000 no client auth (no USE_PSA)" \
             "$P_SRV curves=secp256r1" \
             "$P_CLI force_ciphersuite=TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256 \
              debug_level=1 ec_max_ops=1000" \
@@ -8576,13 +8666,35 @@
             -c "mbedtls_ecdh_make_public.*4b00" \
             -C "mbedtls_pk_sign.*4b00"
 
+
+# With USE_PSA enabled we expect only partial restartable behaviour:
+# everything except ECDH (where TLS calls PSA directly).
 requires_config_enabled MBEDTLS_ECP_RESTARTABLE
 requires_config_enabled MBEDTLS_ECP_DP_SECP256R1_ENABLED
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
-run_test    "EC restart: TLS, max_ops=1000, ECDHE-PSK" \
-            "$P_SRV curves=secp256r1 psk=abc123" \
-            "$P_CLI force_ciphersuite=TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA256 \
-             psk=abc123 debug_level=1 ec_max_ops=1000" \
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "EC restart: TLS, max_ops=1000 no client auth (USE_PSA)" \
+            "$P_SRV curves=secp256r1" \
+            "$P_CLI force_ciphersuite=TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256 \
+             debug_level=1 ec_max_ops=1000" \
+            0 \
+            -c "x509_verify_cert.*4b00" \
+            -c "mbedtls_pk_verify.*4b00" \
+            -C "mbedtls_ecdh_make_public.*4b00" \
+            -C "mbedtls_pk_sign.*4b00"
+
+# Restartable is only for ECDHE-ECDSA, with another ciphersuite we expect no
+# restartable behaviour at all (not even client auth).
+# This is the same as "EC restart: TLS, max_ops=1000" except with ECDHE-RSA,
+# and all 4 assertions negated.
+requires_config_enabled MBEDTLS_ECP_RESTARTABLE
+requires_config_enabled MBEDTLS_ECP_DP_SECP256R1_ENABLED
+requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
+run_test    "EC restart: TLS, max_ops=1000, ECDHE-RSA" \
+            "$P_SRV curves=secp256r1 auth_mode=required" \
+            "$P_CLI force_ciphersuite=TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256 \
+             key_file=data_files/server5.key crt_file=data_files/server5.crt  \
+             debug_level=1 ec_max_ops=1000" \
             0 \
             -C "x509_verify_cert.*4b00" \
             -C "mbedtls_pk_verify.*4b00" \
diff --git a/tests/suites/test_suite_bignum.function b/tests/suites/test_suite_bignum.function
index 55bb2f5..01af2ff 100644
--- a/tests/suites/test_suite_bignum.function
+++ b/tests/suites/test_suite_bignum.function
@@ -2,6 +2,7 @@
 #include "mbedtls/bignum.h"
 #include "mbedtls/entropy.h"
 #include "constant_time_internal.h"
+#include "bignum_core.h"
 #include "test/constant_flow.h"
 
 #if MBEDTLS_MPI_MAX_BITS > 792
@@ -89,50 +90,6 @@
     return( 0 );
 }
 
-/* Test whether bytes represents (in big-endian base 256) a number b that
- * is significantly above a power of 2. That is, b must not have a long run
- * of unset bits after the most significant bit.
- *
- * Let n be the bit-size of b, i.e. the integer such that 2^n <= b < 2^{n+1}.
- * This function returns 1 if, when drawing a number between 0 and b,
- * the probability that this number is at least 2^n is not negligible.
- * This probability is (b - 2^n) / b and this function checks that this
- * number is above some threshold A. The threshold value is heuristic and
- * based on the needs of mpi_random_many().
- */
-static int is_significantly_above_a_power_of_2( data_t *bytes )
-{
-    const uint8_t *p = bytes->x;
-    size_t len = bytes->len;
-    unsigned x;
-
-    /* Skip leading null bytes */
-    while( len > 0 && p[0] == 0 )
-    {
-        ++p;
-        --len;
-    }
-    /* 0 is not significantly above a power of 2 */
-    if( len == 0 )
-        return( 0 );
-    /* Extract the (up to) 2 most significant bytes */
-    if( len == 1 )
-        x = p[0];
-    else
-        x = ( p[0] << 8 ) | p[1];
-
-    /* Shift the most significant bit of x to position 8 and mask it out */
-    while( ( x & 0xfe00 ) != 0 )
-        x >>= 1;
-    x &= 0x00ff;
-
-    /* At this point, x = floor((b - 2^n) / 2^(n-8)). b is significantly above
-     * a power of 2 iff x is significantly above 0 compared to 2^8.
-     * Testing x >= 2^4 amounts to picking A = 1/16 in the function
-     * description above. */
-    return( x >= 0x10 );
-}
-
 /* END_HEADER */
 
 /* BEGIN_DEPENDENCIES
@@ -1295,170 +1252,6 @@
 /* END_CASE */
 
 /* BEGIN_CASE */
-void mpi_random_many( int min, data_t *bound_bytes, int iterations )
-{
-    /* Generate numbers in the range 1..bound-1. Do it iterations times.
-     * This function assumes that the value of bound is at least 2 and
-     * that iterations is large enough that a one-in-2^iterations chance
-     * effectively never occurs.
-     */
-
-    mbedtls_mpi upper_bound;
-    size_t n_bits;
-    mbedtls_mpi result;
-    size_t b;
-    /* If upper_bound is small, stats[b] is the number of times the value b
-     * has been generated. Otherwise stats[b] is the number of times a
-     * value with bit b set has been generated. */
-    size_t *stats = NULL;
-    size_t stats_len;
-    int full_stats;
-    size_t i;
-
-    mbedtls_mpi_init( &upper_bound );
-    mbedtls_mpi_init( &result );
-
-    TEST_EQUAL( 0, mbedtls_mpi_read_binary( &upper_bound,
-                                            bound_bytes->x, bound_bytes->len ) );
-    n_bits = mbedtls_mpi_bitlen( &upper_bound );
-    /* Consider a bound "small" if it's less than 2^5. This value is chosen
-     * to be small enough that the probability of missing one value is
-     * negligible given the number of iterations. It must be less than
-     * 256 because some of the code below assumes that "small" values
-     * fit in a byte. */
-    if( n_bits <= 5 )
-    {
-        full_stats = 1;
-        stats_len = bound_bytes->x[bound_bytes->len - 1];
-    }
-    else
-    {
-        full_stats = 0;
-        stats_len = n_bits;
-    }
-    ASSERT_ALLOC( stats, stats_len );
-
-    for( i = 0; i < (size_t) iterations; i++ )
-    {
-        mbedtls_test_set_step( i );
-        TEST_EQUAL( 0, mbedtls_mpi_random( &result, min, &upper_bound,
-                                           mbedtls_test_rnd_std_rand, NULL ) );
-
-        TEST_ASSERT( sign_is_valid( &result ) );
-        TEST_ASSERT( mbedtls_mpi_cmp_mpi( &result, &upper_bound ) < 0 );
-        TEST_ASSERT( mbedtls_mpi_cmp_int( &result, min ) >= 0 );
-        if( full_stats )
-        {
-            uint8_t value;
-            TEST_EQUAL( 0, mbedtls_mpi_write_binary( &result, &value, 1 ) );
-            TEST_ASSERT( value < stats_len );
-            ++stats[value];
-        }
-        else
-        {
-            for( b = 0; b < n_bits; b++ )
-                stats[b] += mbedtls_mpi_get_bit( &result, b );
-        }
-    }
-
-    if( full_stats )
-    {
-        for( b = min; b < stats_len; b++ )
-        {
-            mbedtls_test_set_step( 1000000 + b );
-            /* Assert that each value has been reached at least once.
-             * This is almost guaranteed if the iteration count is large
-             * enough. This is a very crude way of checking the distribution.
-             */
-            TEST_ASSERT( stats[b] > 0 );
-        }
-    }
-    else
-    {
-        int statistically_safe_all_the_way =
-            is_significantly_above_a_power_of_2( bound_bytes );
-        for( b = 0; b < n_bits; b++ )
-        {
-            mbedtls_test_set_step( 1000000 + b );
-            /* Assert that each bit has been set in at least one result and
-             * clear in at least one result. Provided that iterations is not
-             * too small, it would be extremely unlikely for this not to be
-             * the case if the results are uniformly distributed.
-             *
-             * As an exception, the top bit may legitimately never be set
-             * if bound is a power of 2 or only slightly above.
-             */
-            if( statistically_safe_all_the_way || b != n_bits - 1 )
-            {
-                TEST_ASSERT( stats[b] > 0 );
-            }
-            TEST_ASSERT( stats[b] < (size_t) iterations );
-        }
-    }
-
-exit:
-    mbedtls_mpi_free( &upper_bound );
-    mbedtls_mpi_free( &result );
-    mbedtls_free( stats );
-}
-/* END_CASE */
-
-/* BEGIN_CASE */
-void mpi_random_sizes( int min, data_t *bound_bytes, int nlimbs, int before )
-{
-    mbedtls_mpi upper_bound;
-    mbedtls_mpi result;
-
-    mbedtls_mpi_init( &upper_bound );
-    mbedtls_mpi_init( &result );
-
-    if( before != 0 )
-    {
-        /* Set result to sign(before) * 2^(|before|-1) */
-        TEST_ASSERT( mbedtls_mpi_lset( &result, before > 0 ? 1 : -1 ) == 0 );
-        if( before < 0 )
-            before = - before;
-        TEST_ASSERT( mbedtls_mpi_shift_l( &result, before - 1 ) == 0 );
-    }
-
-    TEST_EQUAL( 0, mbedtls_mpi_grow( &result, nlimbs ) );
-    TEST_EQUAL( 0, mbedtls_mpi_read_binary( &upper_bound,
-                                            bound_bytes->x, bound_bytes->len ) );
-    TEST_EQUAL( 0, mbedtls_mpi_random( &result, min, &upper_bound,
-                                       mbedtls_test_rnd_std_rand, NULL ) );
-    TEST_ASSERT( sign_is_valid( &result ) );
-    TEST_ASSERT( mbedtls_mpi_cmp_mpi( &result, &upper_bound ) < 0 );
-    TEST_ASSERT( mbedtls_mpi_cmp_int( &result, min ) >= 0 );
-
-exit:
-    mbedtls_mpi_free( &upper_bound );
-    mbedtls_mpi_free( &result );
-}
-/* END_CASE */
-
-/* BEGIN_CASE */
-void mpi_random_fail( int min, data_t *bound_bytes, int expected_ret )
-{
-    mbedtls_mpi upper_bound;
-    mbedtls_mpi result;
-    int actual_ret;
-
-    mbedtls_mpi_init( &upper_bound );
-    mbedtls_mpi_init( &result );
-
-    TEST_EQUAL( 0, mbedtls_mpi_read_binary( &upper_bound,
-                                            bound_bytes->x, bound_bytes->len ) );
-    actual_ret = mbedtls_mpi_random( &result, min, &upper_bound,
-                                     mbedtls_test_rnd_std_rand, NULL );
-    TEST_EQUAL( expected_ret, actual_ret );
-
-exit:
-    mbedtls_mpi_free( &upper_bound );
-    mbedtls_mpi_free( &result );
-}
-/* END_CASE */
-
-/* BEGIN_CASE */
 void most_negative_mpi_sint( )
 {
     /* Ad hoc tests for n = -p = -2^(biL-1) as a mbedtls_mpi_sint. We
@@ -1481,7 +1274,6 @@
     mbedtls_mpi_init( &R );
     mbedtls_mpi_init( &X );
 
-    const size_t biL = 8 * sizeof( mbedtls_mpi_sint );
     mbedtls_mpi_uint most_positive_plus_1 = (mbedtls_mpi_uint) 1 << ( biL - 1 );
     const mbedtls_mpi_sint most_positive = most_positive_plus_1 - 1;
     const mbedtls_mpi_sint most_negative = - most_positive - 1;
diff --git a/tests/suites/test_suite_bignum.misc.data b/tests/suites/test_suite_bignum.misc.data
index dc6830e..5eda4c1 100644
--- a/tests/suites/test_suite_bignum.misc.data
+++ b/tests/suites/test_suite_bignum.misc.data
@@ -1788,176 +1788,6 @@
 Fill random: MAX_SIZE bytes, RNG failure after MAX_SIZE-1 bytes
 mpi_fill_random:MBEDTLS_MPI_MAX_SIZE:MBEDTLS_MPI_MAX_SIZE-1:0:MBEDTLS_ERR_ENTROPY_SOURCE_FAILED
 
-MPI random in range: 1..2
-mpi_random_many:1:"02":1000
-
-MPI random in range: 1..3
-mpi_random_many:1:"03":1000
-
-MPI random in range: 1..4
-mpi_random_many:1:"04":1000
-
-MPI random in range: 1..5
-mpi_random_many:1:"05":1000
-
-MPI random in range: 1..6
-mpi_random_many:1:"06":1000
-
-MPI random in range: 1..7
-mpi_random_many:1:"07":1000
-
-MPI random in range: 1..8
-mpi_random_many:1:"08":1000
-
-MPI random in range: 1..9
-mpi_random_many:1:"09":1000
-
-MPI random in range: 1..10
-mpi_random_many:1:"0a":1000
-
-MPI random in range: 1..11
-mpi_random_many:1:"0b":1000
-
-MPI random in range: 1..12
-mpi_random_many:1:"0c":1000
-
-MPI random in range: 1..255
-mpi_random_many:1:"ff":200
-
-MPI random in range: 1..256
-mpi_random_many:1:"0100":200
-
-MPI random in range: 1..257
-mpi_random_many:1:"0101":200
-
-MPI random in range: 1..272
-mpi_random_many:1:"0110":200
-
-MPI random in range: 1..2^64-1
-mpi_random_many:1:"ffffffffffffffff":100
-
-MPI random in range: 1..2^64
-mpi_random_many:1:"010000000000000000":100
-
-MPI random in range: 1..2^64+1
-mpi_random_many:1:"010000000000000001":100
-
-MPI random in range: 1..2^64+2^63
-mpi_random_many:1:"018000000000000000":100
-
-MPI random in range: 1..2^65-1
-mpi_random_many:1:"01ffffffffffffffff":100
-
-MPI random in range: 1..2^65
-mpi_random_many:1:"020000000000000000":100
-
-MPI random in range: 1..2^65+1
-mpi_random_many:1:"020000000000000001":100
-
-MPI random in range: 1..2^65+2^64
-mpi_random_many:1:"030000000000000000":100
-
-MPI random in range: 1..2^66+2^65
-mpi_random_many:1:"060000000000000000":100
-
-MPI random in range: 1..2^71-1
-mpi_random_many:1:"7fffffffffffffffff":100
-
-MPI random in range: 1..2^71
-mpi_random_many:1:"800000000000000000":100
-
-MPI random in range: 1..2^71+1
-mpi_random_many:1:"800000000000000001":100
-
-MPI random in range: 1..2^71+2^70
-mpi_random_many:1:"c00000000000000000":100
-
-MPI random in range: 1..2^72-1
-mpi_random_many:1:"ffffffffffffffffff":100
-
-MPI random in range: 1..2^72
-mpi_random_many:1:"01000000000000000000":100
-
-MPI random in range: 1..2^72+1
-mpi_random_many:1:"01000000000000000001":100
-
-MPI random in range: 1..2^72+2^71
-mpi_random_many:1:"01800000000000000000":100
-
-MPI random in range: 0..1
-mpi_random_many:0:"04":10000
-
-MPI random in range: 0..4
-mpi_random_many:0:"04":10000
-
-MPI random in range: 2..4
-mpi_random_many:2:"04":10000
-
-MPI random in range: 3..4
-mpi_random_many:3:"04":10000
-
-MPI random in range: smaller result
-mpi_random_sizes:1:"aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb":1:0
-
-MPI random in range: same size result (32-bit limbs)
-mpi_random_sizes:1:"aaaaaaaaaaaaaaaa":2:0
-
-MPI random in range: same size result (64-bit limbs)
-mpi_random_sizes:1:"aaaaaaaaaaaaaaaa":1:0
-
-MPI random in range: larger result
-mpi_random_sizes:1:"aaaaaaaaaaaaaaaa":3:0
-
-## The "0 limb in upper bound" tests rely on the fact that
-## mbedtls_mpi_read_binary() bases the size of the MPI on the size of
-## the input, without first checking for leading zeros. If this was
-## not the case, the tests would still pass, but would not exercise
-## the advertised behavior.
-MPI random in range: leading 0 limb in upper bound #0
-mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":0:0
-
-MPI random in range: leading 0 limb in upper bound #1
-mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":1:0
-
-MPI random in range: leading 0 limb in upper bound #2
-mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":2:0
-
-MPI random in range: leading 0 limb in upper bound #3
-mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":3:0
-
-MPI random in range: leading 0 limb in upper bound #4
-mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":4:0
-
-MPI random in range: previously small >0
-mpi_random_sizes:1:"1234567890":4:1
-
-MPI random in range: previously small <0
-mpi_random_sizes:1:"1234567890":4:-1
-
-MPI random in range: previously large >0
-mpi_random_sizes:1:"1234":4:65
-
-MPI random in range: previously large <0
-mpi_random_sizes:1:"1234":4:-65
-
-MPI random bad arguments: min < 0
-mpi_random_fail:-1:"04":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
-
-MPI random bad arguments: min = N = 0
-mpi_random_fail:0:"00":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
-
-MPI random bad arguments: min = N = 1
-mpi_random_fail:1:"01":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
-
-MPI random bad arguments: min > N = 0
-mpi_random_fail:1:"00":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
-
-MPI random bad arguments: min > N = 1
-mpi_random_fail:2:"01":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
-
-MPI random bad arguments: min > N = 1, 0 limb in upper bound
-mpi_random_fail:2:"000000000000000001":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
-
 Most negative mbedtls_mpi_sint
 most_negative_mpi_sint:
 
diff --git a/tests/suites/test_suite_bignum_core.function b/tests/suites/test_suite_bignum_core.function
index 7872115..47f9013 100644
--- a/tests/suites/test_suite_bignum_core.function
+++ b/tests/suites/test_suite_bignum_core.function
@@ -345,6 +345,56 @@
 /* END_CASE */
 
 /* BEGIN_CASE */
+void mpi_core_uint_le_mpi( char *input_A )
+{
+    mbedtls_mpi_uint *A = NULL;
+    size_t A_limbs = 0;
+
+    TEST_EQUAL( mbedtls_test_read_mpi_core( &A, &A_limbs, input_A ), 0 );
+
+    int is_large = 0; /* nonzero limbs beyond the lowest-order one? */
+    for( size_t i = 1; i < A_limbs; i++ )
+    {
+        if( A[i] != 0 )
+        {
+            is_large = 1;
+            break;
+        }
+    }
+
+    TEST_CF_SECRET( A, A_limbs * sizeof( *A ) );
+
+    TEST_EQUAL( mbedtls_mpi_core_uint_le_mpi( 0, A, A_limbs ), 1 );
+    TEST_EQUAL( mbedtls_mpi_core_uint_le_mpi( A[0], A, A_limbs ), 1 );
+
+    if( is_large )
+    {
+        TEST_EQUAL( mbedtls_mpi_core_uint_le_mpi( A[0] + 1,
+                                                  A, A_limbs ), 1 );
+        TEST_EQUAL( mbedtls_mpi_core_uint_le_mpi( (mbedtls_mpi_uint)( -1 ) >> 1,
+                                                  A, A_limbs ), 1 );
+        TEST_EQUAL( mbedtls_mpi_core_uint_le_mpi( (mbedtls_mpi_uint)( -1 ),
+                                                  A, A_limbs ), 1 );
+    }
+    else
+    {
+        TEST_EQUAL( mbedtls_mpi_core_uint_le_mpi( A[0] + 1,
+                                                  A, A_limbs ),
+                    A[0] + 1 <= A[0] );
+        TEST_EQUAL( mbedtls_mpi_core_uint_le_mpi( (mbedtls_mpi_uint)( -1 ) >> 1,
+                                                  A, A_limbs ),
+                    (mbedtls_mpi_uint)( -1 ) >> 1 <= A[0] );
+        TEST_EQUAL( mbedtls_mpi_core_uint_le_mpi( (mbedtls_mpi_uint)( -1 ),
+                                                  A, A_limbs ),
+                    (mbedtls_mpi_uint)( -1 ) <= A[0] );
+    }
+
+exit:
+    mbedtls_free( A );
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
 void mpi_core_cond_assign( char * input_X,
                            char * input_Y,
                            int input_bytes )
@@ -798,7 +848,9 @@
     TEST_EQUAL( 0, mbedtls_mpi_grow( X, limbs_AN ) );
     TEST_EQUAL( 0, mbedtls_mpi_grow( &B, limbs_B ) );
 
-    TEST_EQUAL( 0, mbedtls_mpi_grow( &T, limbs_AN * 2 + 1 ) );
+    size_t working_limbs = mbedtls_mpi_core_montmul_working_limbs( limbs_AN );
+    TEST_EQUAL( working_limbs, limbs_AN * 2 + 1 );
+    TEST_EQUAL( 0, mbedtls_mpi_grow( &T, working_limbs ) );
 
     /* Calculate the Montgomery constant (this is unit tested separately) */
     mbedtls_mpi_uint mm = mbedtls_mpi_core_montmul_init( N.p );
@@ -1083,6 +1135,10 @@
     TEST_LE_U( min_expected_working_limbs, working_limbs );
     TEST_LE_U( working_limbs, max_expected_working_limbs );
 
+    /* Should also be at least mbedtls_mpi_core_montmul_working_limbs() */
+    TEST_LE_U( mbedtls_mpi_core_montmul_working_limbs( N_limbs ),
+               working_limbs );
+
     ASSERT_ALLOC( T, working_limbs );
 
     mbedtls_mpi_core_exp_mod( Y, A, N, N_limbs, E, E_limbs, R2, T );
@@ -1162,6 +1218,25 @@
 }
 /* END_CASE */
 
+/* BEGIN_CASE */
+void mpi_core_check_zero_ct( char *input_X, int expected_is_zero )
+{
+    mbedtls_mpi_uint *X = NULL;
+    size_t X_limbs;
+
+    TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &X, &X_limbs, input_X ) );
+
+    TEST_CF_SECRET( X, X_limbs * sizeof( mbedtls_mpi_uint ) );
+
+    mbedtls_mpi_uint check = mbedtls_mpi_core_check_zero_ct( X, X_limbs );
+    int is_zero = (check == 0);
+    TEST_EQUAL( is_zero, expected_is_zero );
+
+exit:
+    mbedtls_free( X );
+}
+/* END_CASE */
+
 /* END MERGE SLOT 3 */
 
 /* BEGIN MERGE SLOT 4 */
diff --git a/tests/suites/test_suite_bignum_core.misc.data b/tests/suites/test_suite_bignum_core.misc.data
index 62480e4..81a767a 100644
--- a/tests/suites/test_suite_bignum_core.misc.data
+++ b/tests/suites/test_suite_bignum_core.misc.data
@@ -242,6 +242,69 @@
 mbedtls_mpi_core_lt_ct: x>y (alternating limbs)
 mpi_core_lt_ct:"FF1111111111111111":"11FFFFFFFFFFFFFFFF":0
 
+Test mbedtls_mpi_core_uint_le_mpi: 0 (1 limb)
+mpi_core_uint_le_mpi:"00"
+
+Test mbedtls_mpi_core_uint_le_mpi: 0 (>=2 limbs)
+mpi_core_uint_le_mpi:"000000000000000000"
+
+Test mbedtls_mpi_core_uint_le_mpi: 1 (1 limb)
+mpi_core_uint_le_mpi:"01"
+
+Test mbedtls_mpi_core_uint_le_mpi: 1 (>=2 limbs)
+mpi_core_uint_le_mpi:"000000000000000001"
+
+Test mbedtls_mpi_core_uint_le_mpi: 42 (1 limb)
+mpi_core_uint_le_mpi:"2a"
+
+Test mbedtls_mpi_core_uint_le_mpi: 42 (>=2 limbs)
+mpi_core_uint_le_mpi:"000000000000000042"
+
+Test mbedtls_mpi_core_uint_le_mpi: 2^31-1
+mpi_core_uint_le_mpi:"7fffffff"
+
+Test mbedtls_mpi_core_uint_le_mpi: 2^31-1 with leading zero limb
+mpi_core_uint_le_mpi:"00000000007fffffff"
+
+Test mbedtls_mpi_core_uint_le_mpi: 2^32-1
+mpi_core_uint_le_mpi:"ffffffff"
+
+Test mbedtls_mpi_core_uint_le_mpi: 2^32-1 with leading zero limb
+mpi_core_uint_le_mpi:"0000000000ffffffff"
+
+Test mbedtls_mpi_core_uint_le_mpi: 2^32
+mpi_core_uint_le_mpi:"10000000"
+
+Test mbedtls_mpi_core_uint_le_mpi: 2^32 with leading zero limb
+mpi_core_uint_le_mpi:"000000000010000000"
+
+Test mbedtls_mpi_core_uint_le_mpi: 2^32+1
+mpi_core_uint_le_mpi:"10000001"
+
+Test mbedtls_mpi_core_uint_le_mpi: 2^32+1 with leading zero limb
+mpi_core_uint_le_mpi:"000000000010000001"
+
+Test mbedtls_mpi_core_uint_le_mpi: 2^63-1
+mpi_core_uint_le_mpi:"7fffffffffffffff"
+
+Test mbedtls_mpi_core_uint_le_mpi: 2^63-1 with leading zero limb
+mpi_core_uint_le_mpi:"007fffffffffffffff"
+
+Test mbedtls_mpi_core_uint_le_mpi: 2^64-1
+mpi_core_uint_le_mpi:"ffffffffffffffff"
+
+Test mbedtls_mpi_core_uint_le_mpi: 2^64-1 with leading zero limb
+mpi_core_uint_le_mpi:"00ffffffffffffffff"
+
+Test mbedtls_mpi_core_uint_le_mpi: 2^64
+mpi_core_uint_le_mpi:"010000000000000000"
+
+Test mbedtls_mpi_core_uint_le_mpi: 2^64+1
+mpi_core_uint_le_mpi:"010000000000000001"
+
+Test mbedtls_mpi_core_uint_le_mpi: 2^64+2
+mpi_core_uint_le_mpi:"010000000000000002"
+
 mbedtls_mpi_core_cond_assign: 1 limb
 mpi_core_cond_assign:"FFFFFFFF":"11111111":4
 
diff --git a/tests/suites/test_suite_bignum_mod.function b/tests/suites/test_suite_bignum_mod.function
index 507920a..79f5134 100644
--- a/tests/suites/test_suite_bignum_mod.function
+++ b/tests/suites/test_suite_bignum_mod.function
@@ -108,7 +108,7 @@
 /* BEGIN_CASE */
 void mpi_mod_sub( char * input_N,
                   char * input_A, char * input_B,
-                  char * input_D, int oret )
+                  char * input_D, int expected_ret )
 {
     mbedtls_mpi_mod_residue a = { NULL, 0 };
     mbedtls_mpi_mod_residue b = { NULL, 0 };
@@ -125,46 +125,51 @@
     /* test_read_residue() normally checks that inputs have the same number of
      * limbs as the modulus. For negative testing we can ask it to skip this
      * with a non-zero final parameter. */
-    TEST_EQUAL( 0, test_read_residue( &a, &m, input_A, oret != 0 ) );
-    TEST_EQUAL( 0, test_read_residue( &b, &m, input_B, oret != 0 ) );
-    TEST_EQUAL( 0, test_read_residue( &d, &m, input_D, oret != 0 ) );
+    TEST_EQUAL( 0, test_read_residue( &a, &m, input_A, expected_ret != 0 ) );
+    TEST_EQUAL( 0, test_read_residue( &b, &m, input_B, expected_ret != 0 ) );
+    TEST_EQUAL( 0, test_read_residue( &d, &m, input_D, expected_ret != 0 ) );
 
     size_t limbs = m.limbs;
     size_t bytes = limbs * sizeof( *X_raw );
 
-    /* One spare limb for negative testing */
-    ASSERT_ALLOC( X_raw, limbs + 1 );
-
-    if( oret == 0 )
+    if( expected_ret == 0 )
     {
-        /* Sneak in a couple of negative tests on known-good data */
+        /* Negative test with too many limbs in output */
+        ASSERT_ALLOC( X_raw, limbs + 1 );
 
-        /* First, negative test with too many limbs in output */
         x.p = X_raw;
         x.limbs = limbs + 1;
         TEST_EQUAL( MBEDTLS_ERR_MPI_BAD_INPUT_DATA,
                     mbedtls_mpi_mod_sub( &x, &a, &b, &m ) );
 
-        /* Then negative test with too few limbs in output */
+        mbedtls_free( X_raw );
+        X_raw = NULL;
+
+        /* Negative test with too few limbs in output */
         if( limbs > 1 )
         {
+            ASSERT_ALLOC( X_raw, limbs - 1 );
+
             x.p = X_raw;
             x.limbs = limbs - 1;
             TEST_EQUAL( MBEDTLS_ERR_MPI_BAD_INPUT_DATA,
                         mbedtls_mpi_mod_sub( &x, &a, &b, &m ) );
+
+            mbedtls_free( X_raw );
+            X_raw = NULL;
         }
 
         /* Negative testing with too many/too few limbs in a and b is covered by
-         * manually-written test cases with oret != 0. */
-
-        /* Back to the normally-scheduled programme */
+         * manually-written test cases with expected_ret != 0. */
     }
 
+    ASSERT_ALLOC( X_raw, limbs );
+
     TEST_EQUAL( 0, mbedtls_mpi_mod_residue_setup( &x, &m, X_raw, limbs ) );
 
     /* a - b => Correct result, or expected error */
-    TEST_EQUAL( oret, mbedtls_mpi_mod_sub( &x, &a, &b, &m ) );
-    if( oret != 0 )
+    TEST_EQUAL( expected_ret, mbedtls_mpi_mod_sub( &x, &a, &b, &m ) );
+    if( expected_ret != 0 )
         goto exit;
 
     TEST_COMPARE_MPI_RESIDUES( x, d );
@@ -203,6 +208,106 @@
     mbedtls_free( X_raw );
 }
 /* END_CASE */
+
+/* BEGIN_CASE */
+void mpi_mod_inv_mont( char * input_N,
+                       char * input_A, char * input_I,
+                       int expected_ret )
+{
+    mbedtls_mpi_mod_residue a = { NULL, 0 };    /* argument */
+    mbedtls_mpi_mod_residue i = { NULL, 0 };    /* expected inverse wrt N */
+    mbedtls_mpi_mod_residue x = { NULL, 0 };    /* output */
+    mbedtls_mpi_uint *X_raw = NULL;
+
+    mbedtls_mpi_mod_modulus N;
+    mbedtls_mpi_mod_modulus_init( &N );
+
+    TEST_EQUAL( 0,
+        test_read_modulus( &N, MBEDTLS_MPI_MOD_REP_MONTGOMERY, input_N ) );
+
+    /* test_read_residue() normally checks that inputs have the same number of
+     * limbs as the modulus. For negative testing we can ask it to skip this
+     * with a non-zero final parameter. */
+    TEST_EQUAL( 0, test_read_residue( &a, &N, input_A, expected_ret != 0 ) );
+    TEST_EQUAL( 0, test_read_residue( &i, &N, input_I, expected_ret != 0 ) );
+
+    size_t limbs = N.limbs;
+    size_t bytes = limbs * sizeof( *X_raw );
+
+    ASSERT_ALLOC( X_raw, limbs );
+
+    TEST_EQUAL( 0, mbedtls_mpi_mod_residue_setup( &x, &N, X_raw, limbs ) );
+
+    TEST_EQUAL( expected_ret, mbedtls_mpi_mod_inv( &x, &a, &N ) );
+    if( expected_ret == 0 )
+    {
+        TEST_COMPARE_MPI_RESIDUES( x, i );
+
+        /* a^-1: alias x to a => Correct result */
+        memcpy( x.p, a.p, bytes );
+        TEST_EQUAL( 0, mbedtls_mpi_mod_inv( &x, &x, &N ) );
+        TEST_COMPARE_MPI_RESIDUES( x, i );
+    }
+
+exit:
+    mbedtls_free( (void *)N.p ); /* mbedtls_mpi_mod_modulus_free() sets N.p = NULL */
+    mbedtls_mpi_mod_modulus_free( &N );
+
+    mbedtls_free( a.p );
+    mbedtls_free( i.p );
+    mbedtls_free( X_raw );
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void mpi_mod_inv_non_mont( char * input_N,
+                           char * input_A, char * input_I,
+                           int expected_ret )
+{
+    mbedtls_mpi_mod_residue a = { NULL, 0 };    /* argument */
+    mbedtls_mpi_mod_residue i = { NULL, 0 };    /* expected inverse wrt N */
+    mbedtls_mpi_mod_residue x = { NULL, 0 };    /* output */
+    mbedtls_mpi_uint *X_raw = NULL;
+
+    mbedtls_mpi_mod_modulus N;
+    mbedtls_mpi_mod_modulus_init( &N );
+
+    TEST_EQUAL( 0,
+        test_read_modulus( &N, MBEDTLS_MPI_MOD_REP_OPT_RED, input_N ) );
+
+    /* test_read_residue() normally checks that inputs have the same number of
+     * limbs as the modulus. For negative testing we can ask it to skip this
+     * with a non-zero final parameter. */
+    TEST_EQUAL( 0, test_read_residue( &a, &N, input_A, expected_ret != 0 ) );
+    TEST_EQUAL( 0, test_read_residue( &i, &N, input_I, expected_ret != 0 ) );
+
+    size_t limbs = N.limbs;
+    size_t bytes = limbs * sizeof( *X_raw );
+
+    ASSERT_ALLOC( X_raw, limbs );
+
+    TEST_EQUAL( 0, mbedtls_mpi_mod_residue_setup( &x, &N, X_raw, limbs ) );
+
+    TEST_EQUAL( expected_ret, mbedtls_mpi_mod_inv( &x, &a, &N ) );
+    if( expected_ret == 0 )
+    {
+        TEST_COMPARE_MPI_RESIDUES( x, i );
+
+        /* a^-1: alias x to a => Correct result */
+        memcpy( x.p, a.p, bytes );
+        TEST_EQUAL( 0, mbedtls_mpi_mod_inv( &x, &x, &N ) );
+        TEST_COMPARE_MPI_RESIDUES( x, i );
+    }
+
+exit:
+    mbedtls_free( (void *)N.p ); /* mbedtls_mpi_mod_modulus_free() sets N.p = NULL */
+    mbedtls_mpi_mod_modulus_free( &N );
+
+    mbedtls_free( a.p );
+    mbedtls_free( i.p );
+    mbedtls_free( X_raw );
+}
+/* END_CASE */
 /* END MERGE SLOT 3 */
 
 /* BEGIN MERGE SLOT 4 */
diff --git a/tests/suites/test_suite_bignum_mod.misc.data b/tests/suites/test_suite_bignum_mod.misc.data
index 7b1c85f..6240e21 100644
--- a/tests/suites/test_suite_bignum_mod.misc.data
+++ b/tests/suites/test_suite_bignum_mod.misc.data
@@ -38,6 +38,50 @@
 mpi_mod_sub with second input too short
 mpi_mod_sub:"014320a022ccb75bdf470ddf25":"000000025a55a46e5da99c71c7":"e8000f93":"00":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
 
+mbedtls_mpi_mod_inv non-Mont. form - base case for negative testing (N, A, A^-1)
+mpi_mod_inv_non_mont:"000000000000152d02c7e14af67fe0bf":"00000000000000000000000000000038":"000000000000097418193b6f2e0b5fc3":0
+
+mbedtls_mpi_mod_inv non-Mont. form - A == 0
+mpi_mod_inv_non_mont:"000000000000152d02c7e14af67fe0bf":"00000000000000000000000000000000":"00":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+mbedtls_mpi_mod_inv non-Mont. form - A too long
+mpi_mod_inv_non_mont:"000000000000152d02c7e14af67fe0bf":"0000000000000000000000000000000000000038":"00":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+mbedtls_mpi_mod_inv non-Mont. form - A too short
+mpi_mod_inv_non_mont:"000000000000152d02c7e14af67fe0bf":"0000000000000038":"000000000000097418193b6f2e0b5fc3":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+mbedtls_mpi_mod_inv 32-bit Mont. form - base case for negative testing, A = 1 (N, mont(A), mont(A^-1))
+depends_on:MBEDTLS_HAVE_INT32
+mpi_mod_inv_mont:"000000000000152d02c7e14af67fe0bf":"0000000000000d71d51539b9c02b7b28":"0000000000000d71d51539b9c02b7b28":0
+
+mbedtls_mpi_mod_inv 32-bit Mont. form - A == 0
+depends_on:MBEDTLS_HAVE_INT32
+mpi_mod_inv_mont:"000000000000152d02c7e14af67fe0bf":"00000000000000000000000000000000":"00":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+mbedtls_mpi_mod_inv 32-bit Mont. form - A too long
+depends_on:MBEDTLS_HAVE_INT32
+mpi_mod_inv_mont:"000000000000152d02c7e14af67fe0bf":"000000000000000000000d71d51539b9c02b7b28":"00":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+mbedtls_mpi_mod_inv 32-bit Mont. form - A too short
+depends_on:MBEDTLS_HAVE_INT32
+mpi_mod_inv_mont:"000000000000152d02c7e14af67fe0bf":"00000d71d51539b9c02b7b28":"0000000000000d71d51539b9c02b7b28":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+mbedtls_mpi_mod_inv 64-bit Mont. form - base case for negative testing, A = 1 (N, mont(A), mont(A^-1))
+depends_on:MBEDTLS_HAVE_INT64
+mpi_mod_inv_mont:"0000000000000000000000000000152d02c7e14af67fe0bf":"000000000000000000000000000009545642424381c611fb":"000000000000000000000000000009545642424381c611fb":0
+
+mbedtls_mpi_mod_inv 64-bit Mont. form - A == 0
+depends_on:MBEDTLS_HAVE_INT64
+mpi_mod_inv_mont:"0000000000000000000000000000152d02c7e14af67fe0bf":"000000000000000000000000000000000000000000000000":"000000000000000000000000000009545642424381c611fb":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+mbedtls_mpi_mod_inv 64-bit Mont. form - A too long
+depends_on:MBEDTLS_HAVE_INT64
+mpi_mod_inv_mont:"0000000000000000000000000000152d02c7e14af67fe0bf":"0000000000000000000000000000000000000000000009545642424381c611fb":"000000000000000000000000000009545642424381c611fb":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+mbedtls_mpi_mod_inv 64-bit Mont. form - A too short
+depends_on:MBEDTLS_HAVE_INT64
+mpi_mod_inv_mont:"0000000000000000000000000000152d02c7e14af67fe0bf":"00000000000009545642424381c611fb":"000000000000000000000000000009545642424381c611fb":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
 # END MERGE SLOT 3
 
 # BEGIN MERGE SLOT 4
diff --git a/tests/suites/test_suite_bignum_mod_raw.function b/tests/suites/test_suite_bignum_mod_raw.function
index 83e1f54..461a18e 100644
--- a/tests/suites/test_suite_bignum_mod_raw.function
+++ b/tests/suites/test_suite_bignum_mod_raw.function
@@ -345,6 +345,101 @@
 }
 /* END_CASE */
 
+/* BEGIN_CASE */
+void mpi_mod_raw_mul( char * input_A,
+                      char * input_B,
+                      char * input_N,
+                      char * result )
+{
+    mbedtls_mpi_uint *A = NULL;
+    mbedtls_mpi_uint *B = NULL;
+    mbedtls_mpi_uint *N = NULL;
+    mbedtls_mpi_uint *X = NULL;
+    mbedtls_mpi_uint *R = NULL;
+    mbedtls_mpi_uint *T = NULL;
+    size_t limbs_A;
+    size_t limbs_B;
+    size_t limbs_N;
+    size_t limbs_R;
+
+    mbedtls_mpi_mod_modulus m;
+    mbedtls_mpi_mod_modulus_init( &m );
+
+    TEST_EQUAL( mbedtls_test_read_mpi_core( &A, &limbs_A, input_A ), 0 );
+    TEST_EQUAL( mbedtls_test_read_mpi_core( &B, &limbs_B, input_B ), 0 );
+    TEST_EQUAL( mbedtls_test_read_mpi_core( &N, &limbs_N, input_N ), 0 );
+    TEST_EQUAL( mbedtls_test_read_mpi_core( &R, &limbs_R, result  ), 0 );
+
+    const size_t limbs = limbs_N;
+    const size_t bytes = limbs * sizeof( mbedtls_mpi_uint );
+
+    TEST_EQUAL( limbs_A, limbs );
+    TEST_EQUAL( limbs_B, limbs );
+    TEST_EQUAL( limbs_R, limbs );
+
+    ASSERT_ALLOC( X, limbs );
+
+    TEST_EQUAL( mbedtls_mpi_mod_modulus_setup(
+                        &m, N, limbs,
+                        MBEDTLS_MPI_MOD_REP_MONTGOMERY ), 0 );
+
+    const size_t limbs_T = limbs * 2 + 1;
+    ASSERT_ALLOC( T, limbs_T );
+
+    mbedtls_mpi_mod_raw_mul( X, A, B, &m, T );
+    ASSERT_COMPARE( X, bytes, R, bytes );
+
+    /* alias X to A */
+    memcpy( X, A, bytes );
+    mbedtls_mpi_mod_raw_mul( X, X, B, &m, T );
+    ASSERT_COMPARE( X, bytes, R, bytes );
+
+    /* alias X to B */
+    memcpy( X, B, bytes );
+    mbedtls_mpi_mod_raw_mul( X, A, X, &m, T );
+    ASSERT_COMPARE( X, bytes, R, bytes );
+
+    /* A == B: alias A and B */
+    if( memcmp( A, B, bytes ) == 0 )
+    {
+        mbedtls_mpi_mod_raw_mul( X, A, A, &m, T );
+        ASSERT_COMPARE( X, bytes, R, bytes );
+
+        /* X, A, B all aliased together */
+        memcpy( X, A, bytes );
+        mbedtls_mpi_mod_raw_mul( X, X, X, &m, T );
+        ASSERT_COMPARE( X, bytes, R, bytes );
+    }
+
+    /* A != B: test B * A */
+    else
+    {
+        mbedtls_mpi_mod_raw_mul( X, B, A, &m, T );
+        ASSERT_COMPARE( X, bytes, R, bytes );
+
+        /* B * A: alias X to A */
+        memcpy( X, A, bytes );
+        mbedtls_mpi_mod_raw_mul( X, B, X, &m, T );
+        ASSERT_COMPARE( X, bytes, R, bytes );
+
+        /* B + A: alias X to B */
+        memcpy( X, B, bytes );
+        mbedtls_mpi_mod_raw_mul( X, X, A, &m, T );
+        ASSERT_COMPARE( X, bytes, R, bytes );
+    }
+
+exit:
+    mbedtls_free( A );
+    mbedtls_free( B );
+    mbedtls_free( X );
+    mbedtls_free( R );
+    mbedtls_free( T );
+
+    mbedtls_mpi_mod_modulus_free( &m );
+    mbedtls_free( N );
+}
+/* END_CASE */
+
 /* END MERGE SLOT 2 */
 
 /* BEGIN MERGE SLOT 3 */
@@ -394,6 +489,10 @@
     TEST_LE_U( min_expected_working_limbs, working_limbs );
     TEST_LE_U( working_limbs, max_expected_working_limbs );
 
+    /* Should also be at least mbedtls_mpi_core_montmul_working_limbs() */
+    TEST_LE_U( mbedtls_mpi_core_montmul_working_limbs( N_limbs ),
+               working_limbs );
+
     ASSERT_ALLOC( T, working_limbs );
 
     mbedtls_mpi_mod_raw_inv_prime( Y, A, N, N_limbs, R2, T );
@@ -529,8 +628,10 @@
 {
     mbedtls_mpi_uint *N = NULL;
     mbedtls_mpi_uint *A = NULL;
+    mbedtls_mpi_uint *R = NULL; /* for result of low-level conversion */
     mbedtls_mpi_uint *X = NULL;
-    size_t n_limbs, a_limbs, x_limbs, x_bytes;
+    mbedtls_mpi_uint *T = NULL;
+    size_t n_limbs, a_limbs, x_limbs;
 
     mbedtls_mpi_mod_modulus m;
     mbedtls_mpi_mod_modulus_init( &m );
@@ -539,23 +640,50 @@
     TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &N, &n_limbs, input_N ) );
     TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &A, &a_limbs, input_A ) );
     TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &X, &x_limbs, input_X ) );
-    x_bytes = x_limbs * sizeof(mbedtls_mpi_uint);
 
-    /* Test that input does not require more limbs than modulo */
-    TEST_LE_U(a_limbs, n_limbs);
+    /* Number to convert must have same number of limbs as modulus */
+    TEST_EQUAL(a_limbs, n_limbs);
+
+    /* Higher-level conversion is in-place, so expected result must have the
+     * same number of limbs too */
+    TEST_EQUAL(x_limbs, n_limbs);
+
+    size_t limbs = n_limbs;
+    size_t bytes = limbs * sizeof(mbedtls_mpi_uint);
 
     TEST_EQUAL( 0, mbedtls_mpi_mod_modulus_setup( &m, N, n_limbs,
-                MBEDTLS_MPI_MOD_REP_MONTGOMERY ) );
+                                          MBEDTLS_MPI_MOD_REP_MONTGOMERY ) );
 
-    /* Convert from cannonical into Montgomery representation */
+    /* 1. Test low-level function first */
+
+    /* It has separate output, and requires temporary working storage */
+    size_t temp_limbs = mbedtls_mpi_core_montmul_working_limbs( limbs );
+    ASSERT_ALLOC( T, temp_limbs );
+    ASSERT_ALLOC( R, limbs );
+    mbedtls_mpi_core_to_mont_rep( R, A, N, n_limbs,
+                                  m.rep.mont.mm, m.rep.mont.rr, T );
+    /* Test that the low-level function gives the required value */
+    ASSERT_COMPARE( R, bytes, X, bytes );
+
+    /* Test when output is aliased to input */
+    memcpy( R, A, bytes );
+    mbedtls_mpi_core_to_mont_rep( R, R, N, n_limbs,
+                                  m.rep.mont.mm, m.rep.mont.rr, T );
+    ASSERT_COMPARE( R, bytes, X, bytes );
+
+    /* 2. Test higher-level cannonical to Montgomery conversion */
+
     TEST_EQUAL(0, mbedtls_mpi_mod_raw_to_mont_rep( A, &m ) );
 
     /* The result matches expected value */
-    ASSERT_COMPARE( A, x_bytes, X, x_bytes );
+    ASSERT_COMPARE( A, bytes, X, bytes );
+
 exit:
     mbedtls_mpi_mod_modulus_free( &m );
+    mbedtls_free( T );
     mbedtls_free( N );
     mbedtls_free( A );
+    mbedtls_free( R );
     mbedtls_free( X );
 }
 /* END_CASE */
@@ -565,8 +693,10 @@
 {
     mbedtls_mpi_uint *N = NULL;
     mbedtls_mpi_uint *A = NULL;
+    mbedtls_mpi_uint *R = NULL; /* for result of low-level conversion */
     mbedtls_mpi_uint *X = NULL;
-    size_t n_limbs, a_limbs, x_limbs, x_bytes;
+    mbedtls_mpi_uint *T = NULL;
+    size_t n_limbs, a_limbs, x_limbs;
 
     mbedtls_mpi_mod_modulus m;
     mbedtls_mpi_mod_modulus_init( &m );
@@ -575,23 +705,50 @@
     TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &N, &n_limbs, input_N ) );
     TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &A, &a_limbs, input_A ) );
     TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &X, &x_limbs, input_X ) );
-    x_bytes = x_limbs * sizeof(mbedtls_mpi_uint);
 
-    /* Test that input does not require more limbs than modulo */
-    TEST_LE_U(a_limbs, n_limbs);
+    /* Number to convert must have same number of limbs as modulus */
+    TEST_EQUAL(a_limbs, n_limbs);
+
+    /* Higher-level conversion is in-place, so expected result must have the
+     * same number of limbs too */
+    TEST_EQUAL(x_limbs, n_limbs);
+
+    size_t limbs = n_limbs;
+    size_t bytes = limbs * sizeof(mbedtls_mpi_uint);
 
     TEST_EQUAL( 0, mbedtls_mpi_mod_modulus_setup( &m, N, n_limbs,
-                MBEDTLS_MPI_MOD_REP_MONTGOMERY ) );
+                                          MBEDTLS_MPI_MOD_REP_MONTGOMERY ) );
 
-    /* Convert from Montgomery into cannonical representation */
+    /* 1. Test low-level function first */
+
+    /* It has separate output, and requires temporary working storage */
+    size_t temp_limbs = mbedtls_mpi_core_montmul_working_limbs( limbs );
+    ASSERT_ALLOC( T, temp_limbs );
+    ASSERT_ALLOC( R, limbs );
+    mbedtls_mpi_core_from_mont_rep( R, A, N, n_limbs,
+                                    m.rep.mont.mm, T );
+    /* Test that the low-level function gives the required value */
+    ASSERT_COMPARE( R, bytes, X, bytes );
+
+    /* Test when output is aliased to input */
+    memcpy( R, A, bytes );
+    mbedtls_mpi_core_from_mont_rep( R, R, N, n_limbs,
+                                    m.rep.mont.mm, T );
+    ASSERT_COMPARE( R, bytes, X, bytes );
+
+    /* 2. Test higher-level Montgomery to cannonical conversion */
+
     TEST_EQUAL(0, mbedtls_mpi_mod_raw_from_mont_rep( A, &m ) );
 
     /* The result matches expected value */
-    ASSERT_COMPARE( A, x_bytes, X, x_bytes );
+    ASSERT_COMPARE( A, bytes, X, bytes );
+
 exit:
     mbedtls_mpi_mod_modulus_free( &m );
+    mbedtls_free( T );
     mbedtls_free( N );
     mbedtls_free( A );
+    mbedtls_free( R );
     mbedtls_free( X );
 }
 /* END_CASE */
diff --git a/tests/suites/test_suite_bignum_random.data b/tests/suites/test_suite_bignum_random.data
new file mode 100644
index 0000000..fe29053
--- /dev/null
+++ b/tests/suites/test_suite_bignum_random.data
@@ -0,0 +1,235 @@
+MPI core random basic: 0..1
+mpi_core_random_basic:0:"01":0
+
+MPI core random basic: 0..2
+mpi_core_random_basic:0:"02":0
+
+MPI core random basic: 1..2
+mpi_core_random_basic:1:"02":0
+
+MPI core random basic: 2^30..2^31
+mpi_core_random_basic:0x40000000:"80000000":0
+
+MPI core random basic: 0..2^128
+mpi_core_random_basic:0x40000000:"0100000000000000000000000000000000":0
+
+MPI core random basic: 2^30..2^129
+mpi_core_random_basic:0x40000000:"0200000000000000000000000000000000":0
+
+# Use the same data values for mpi_core_random_basic->NOT_ACCEPTABLE
+# and for mpi_random_values where we want to return NOT_ACCEPTABLE but
+# this isn't checked at runtime.
+MPI core random basic: 2^28-1..2^28 (NOT_ACCEPTABLE)
+mpi_core_random_basic:0x0fffffff:"10000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
+
+MPI random legacy=core: 2^28-1..2^28 (NOT_ACCEPTABLE)
+mpi_random_values:0x0fffffff:"10000000"
+
+MPI core random basic: 2^29-1..2^29 (NOT_ACCEPTABLE)
+mpi_core_random_basic:0x1fffffff:"20000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
+
+MPI random legacy=core: 2^29-1..2^29 (NOT_ACCEPTABLE)
+mpi_random_values:0x1fffffff:"20000000"
+
+MPI core random basic: 2^30-1..2^30 (NOT_ACCEPTABLE)
+mpi_core_random_basic:0x3fffffff:"40000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
+
+MPI random legacy=core: 2^30-1..2^30 (NOT_ACCEPTABLE)
+mpi_random_values:0x3fffffff:"40000000"
+
+MPI core random basic: 2^31-1..2^31 (NOT_ACCEPTABLE)
+mpi_core_random_basic:0x7fffffff:"80000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
+
+MPI random legacy=core: 2^31-1..2^31 (NOT_ACCEPTABLE)
+mpi_random_values:0x7fffffff:"80000000"
+
+MPI random in range: 1..2
+mpi_random_many:1:"02":1000
+
+MPI random in range: 1..3
+mpi_random_many:1:"03":1000
+
+MPI random in range: 1..4
+mpi_random_many:1:"04":1000
+
+MPI random in range: 1..5
+mpi_random_many:1:"05":1000
+
+MPI random in range: 1..6
+mpi_random_many:1:"06":1000
+
+MPI random in range: 1..7
+mpi_random_many:1:"07":1000
+
+MPI random in range: 1..8
+mpi_random_many:1:"08":1000
+
+MPI random in range: 1..9
+mpi_random_many:1:"09":1000
+
+MPI random in range: 1..10
+mpi_random_many:1:"0a":1000
+
+MPI random in range: 1..11
+mpi_random_many:1:"0b":1000
+
+MPI random in range: 1..12
+mpi_random_many:1:"0c":1000
+
+MPI random in range: 1..255
+mpi_random_many:1:"ff":200
+
+MPI random in range: 1..256
+mpi_random_many:1:"0100":200
+
+MPI random in range: 1..257
+mpi_random_many:1:"0101":200
+
+MPI random in range: 1..272
+mpi_random_many:1:"0110":200
+
+MPI random in range: 1..2^64-1
+mpi_random_many:1:"ffffffffffffffff":100
+
+MPI random in range: 1..2^64
+mpi_random_many:1:"010000000000000000":100
+
+MPI random in range: 1..2^64+1
+mpi_random_many:1:"010000000000000001":100
+
+MPI random in range: 1..2^64+2^63
+mpi_random_many:1:"018000000000000000":100
+
+MPI random in range: 1..2^65-1
+mpi_random_many:1:"01ffffffffffffffff":100
+
+MPI random in range: 1..2^65
+mpi_random_many:1:"020000000000000000":100
+
+MPI random in range: 1..2^65+1
+mpi_random_many:1:"020000000000000001":100
+
+MPI random in range: 1..2^65+2^64
+mpi_random_many:1:"030000000000000000":100
+
+MPI random in range: 1..2^66+2^65
+mpi_random_many:1:"060000000000000000":100
+
+MPI random in range: 1..2^71-1
+mpi_random_many:1:"7fffffffffffffffff":100
+
+MPI random in range: 1..2^71
+mpi_random_many:1:"800000000000000000":100
+
+MPI random in range: 1..2^71+1
+mpi_random_many:1:"800000000000000001":100
+
+MPI random in range: 1..2^71+2^70
+mpi_random_many:1:"c00000000000000000":100
+
+MPI random in range: 1..2^72-1
+mpi_random_many:1:"ffffffffffffffffff":100
+
+MPI random in range: 1..2^72
+mpi_random_many:1:"01000000000000000000":100
+
+MPI random in range: 1..2^72+1
+mpi_random_many:1:"01000000000000000001":100
+
+MPI random in range: 1..2^72+2^71
+mpi_random_many:1:"01800000000000000000":100
+
+MPI random in range: 0..1
+mpi_random_many:0:"04":10000
+
+MPI random in range: 0..4
+mpi_random_many:0:"04":10000
+
+MPI random in range: 2..4
+mpi_random_many:2:"04":10000
+
+MPI random in range: 3..4
+mpi_random_many:3:"04":10000
+
+MPI random in range: smaller result
+mpi_random_sizes:1:"aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb":1:0
+
+MPI random in range: same size result (32-bit limbs)
+mpi_random_sizes:1:"aaaaaaaaaaaaaaaa":2:0
+
+MPI random in range: same size result (64-bit limbs)
+mpi_random_sizes:1:"aaaaaaaaaaaaaaaa":1:0
+
+MPI random in range: larger result
+mpi_random_sizes:1:"aaaaaaaaaaaaaaaa":3:0
+
+## The "0 limb in upper bound" tests rely on the fact that
+## mbedtls_mpi_read_binary() bases the size of the MPI on the size of
+## the input, without first checking for leading zeros. If this was
+## not the case, the tests would still pass, but would not exercise
+## the advertised behavior.
+MPI random in range: leading 0 limb in upper bound #0
+mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":0:0
+
+MPI random in range: leading 0 limb in upper bound #1
+mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":1:0
+
+MPI random in range: leading 0 limb in upper bound #2
+mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":2:0
+
+MPI random in range: leading 0 limb in upper bound #3
+mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":3:0
+
+MPI random in range: leading 0 limb in upper bound #4
+mpi_random_sizes:1:"00aaaaaaaaaaaaaaaa":4:0
+
+MPI random in range: previously small >0
+mpi_random_sizes:1:"1234567890":4:1
+
+MPI random in range: previously small <0
+mpi_random_sizes:1:"1234567890":4:-1
+
+MPI random in range: previously large >0
+mpi_random_sizes:1:"1234":4:65
+
+MPI random in range: previously large <0
+mpi_random_sizes:1:"1234":4:-65
+
+MPI random bad arguments: min < 0
+mpi_random_fail:-1:"04":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+MPI random bad arguments: min = N = 0
+mpi_random_fail:0:"00":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+MPI random bad arguments: min = N = 1
+mpi_random_fail:1:"01":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+MPI random bad arguments: min > N = 0
+mpi_random_fail:1:"00":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+MPI random bad arguments: min > N = 1
+mpi_random_fail:2:"01":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+MPI random bad arguments: min > N = 1, 0 limb in upper bound
+mpi_random_fail:2:"000000000000000001":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+MPI random legacy=core: 0..1
+mpi_random_values:0:"01"
+
+MPI random legacy=core: 0..2
+mpi_random_values:0:"02"
+
+MPI random legacy=core: 1..2
+mpi_random_values:1:"02"
+
+MPI random legacy=core: 2^30..2^31
+mpi_random_values:0x40000000:"80000000"
+
+MPI random legacy=core: 2^31-1..2^32-1
+mpi_random_values:0x7fffffff:"ffffffff"
+
+MPI random legacy=core: 0..2^256
+mpi_random_values:0:"010000000000000000000000000000000000000000000000000000000000000000"
+
+MPI random legacy=core: 0..2^256+1
+mpi_random_values:0:"010000000000000000000000000000000000000000000000000000000000000001"
diff --git a/tests/suites/test_suite_bignum_random.function b/tests/suites/test_suite_bignum_random.function
new file mode 100644
index 0000000..184de5a
--- /dev/null
+++ b/tests/suites/test_suite_bignum_random.function
@@ -0,0 +1,334 @@
+/* BEGIN_HEADER */
+/* Dedicated test suite for mbedtls_mpi_core_random() and the upper-layer
+ * functions. Due to the complexity of how these functions are tested,
+ * we test all the layers in a single test suite, unlike the way other
+ * functions are tested with each layer in its own test suite.
+ */
+
+#include "mbedtls/bignum.h"
+#include "mbedtls/entropy.h"
+#include "bignum_core.h"
+#include "constant_time_internal.h"
+
+/* This test suite only manipulates non-negative bignums. */
+static int sign_is_valid( const mbedtls_mpi *X )
+{
+    return( X->s == 1 );
+}
+
+/* A common initializer for test functions that should generate the same
+ * sequences for reproducibility and good coverage. */
+const mbedtls_test_rnd_pseudo_info rnd_pseudo_seed = {
+    /* 16-word key */
+    {'T', 'h', 'i', 's', ' ', 'i', 's', ' ',
+     'a', ' ', 's', 'e', 'e', 'd', '!', 0},
+    /* 2-word initial state, should be zero */
+    0, 0};
+
+/* Test whether bytes represents (in big-endian base 256) a number b that
+ * is significantly above a power of 2. That is, b must not have a long run
+ * of unset bits after the most significant bit.
+ *
+ * Let n be the bit-size of b, i.e. the integer such that 2^n <= b < 2^{n+1}.
+ * This function returns 1 if, when drawing a number between 0 and b,
+ * the probability that this number is at least 2^n is not negligible.
+ * This probability is (b - 2^n) / b and this function checks that this
+ * number is above some threshold A. The threshold value is heuristic and
+ * based on the needs of mpi_random_many().
+ */
+static int is_significantly_above_a_power_of_2( data_t *bytes )
+{
+    const uint8_t *p = bytes->x;
+    size_t len = bytes->len;
+    unsigned x;
+
+    /* Skip leading null bytes */
+    while( len > 0 && p[0] == 0 )
+    {
+        ++p;
+        --len;
+    }
+    /* 0 is not significantly above a power of 2 */
+    if( len == 0 )
+        return( 0 );
+    /* Extract the (up to) 2 most significant bytes */
+    if( len == 1 )
+        x = p[0];
+    else
+        x = ( p[0] << 8 ) | p[1];
+
+    /* Shift the most significant bit of x to position 8 and mask it out */
+    while( ( x & 0xfe00 ) != 0 )
+        x >>= 1;
+    x &= 0x00ff;
+
+    /* At this point, x = floor((b - 2^n) / 2^(n-8)). b is significantly above
+     * a power of 2 iff x is significantly above 0 compared to 2^8.
+     * Testing x >= 2^4 amounts to picking A = 1/16 in the function
+     * description above. */
+    return( x >= 0x10 );
+}
+
+/* END_HEADER */
+
+/* BEGIN_DEPENDENCIES
+ * depends_on:MBEDTLS_BIGNUM_C
+ * END_DEPENDENCIES
+ */
+
+/* BEGIN_CASE */
+void mpi_core_random_basic( int min, char *bound_bytes, int expected_ret )
+{
+    /* Same RNG as in mpi_random_values */
+    mbedtls_test_rnd_pseudo_info rnd = rnd_pseudo_seed;
+    size_t limbs;
+    mbedtls_mpi_uint *lower_bound = NULL;
+    mbedtls_mpi_uint *upper_bound = NULL;
+    mbedtls_mpi_uint *result = NULL;
+
+    TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &upper_bound, &limbs,
+                                               bound_bytes ) );
+    ASSERT_ALLOC( lower_bound, limbs );
+    lower_bound[0] = min;
+    ASSERT_ALLOC( result, limbs );
+
+    TEST_EQUAL( expected_ret,
+                mbedtls_mpi_core_random( result, min, upper_bound, limbs,
+                                         mbedtls_test_rnd_pseudo_rand, &rnd ) );
+
+    if( expected_ret == 0 )
+    {
+        TEST_EQUAL( 0, mbedtls_mpi_core_lt_ct( result, lower_bound, limbs ) );
+        TEST_EQUAL( 1, mbedtls_mpi_core_lt_ct( result, upper_bound, limbs ) );
+    }
+
+exit:
+    mbedtls_free( lower_bound );
+    mbedtls_free( upper_bound );
+    mbedtls_free( result );
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void mpi_random_values( int min, char *max_hex )
+{
+    /* Same RNG as in mpi_core_random_basic */
+    mbedtls_test_rnd_pseudo_info rnd_core = rnd_pseudo_seed;
+    mbedtls_test_rnd_pseudo_info rnd_legacy;
+    memcpy( &rnd_legacy, &rnd_core, sizeof( rnd_core ) );
+    mbedtls_mpi max_legacy;
+    mbedtls_mpi_init( &max_legacy );
+    mbedtls_mpi_uint *R_core = NULL;
+    mbedtls_mpi R_legacy;
+    mbedtls_mpi_init( &R_legacy );
+
+    TEST_EQUAL( 0, mbedtls_test_read_mpi( &max_legacy, max_hex ) );
+    size_t limbs = max_legacy.n;
+    ASSERT_ALLOC( R_core, limbs );
+
+    /* Call the legacy function and the core function with the same random
+     * stream. */
+    int core_ret = mbedtls_mpi_core_random( R_core, min, max_legacy.p, limbs,
+                                            mbedtls_test_rnd_pseudo_rand,
+                                            &rnd_core );
+    int legacy_ret = mbedtls_mpi_random( &R_legacy, min, &max_legacy,
+                                         mbedtls_test_rnd_pseudo_rand,
+                                         &rnd_legacy );
+
+    /* They must return the same status, and, on success, output the
+     * same number, with the same limb count. */
+    TEST_EQUAL( core_ret, legacy_ret );
+    if( core_ret == 0 )
+    {
+        ASSERT_COMPARE( R_core, limbs * ciL,
+                        R_legacy.p, R_legacy.n * ciL );
+    }
+
+    /* Also check that they have consumed the RNG in the same way. */
+    /* This may theoretically fail on rare platforms with padding in
+     * the structure! If this is a problem in practice, change to a
+     * field-by-field comparison. */
+    ASSERT_COMPARE( &rnd_core, sizeof( rnd_core ),
+                    &rnd_legacy, sizeof( rnd_legacy ) );
+
+exit:
+    mbedtls_mpi_free( &max_legacy );
+    mbedtls_free( R_core );
+    mbedtls_mpi_free( &R_legacy );
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void mpi_random_many( int min, char *bound_hex, int iterations )
+{
+    /* Generate numbers in the range 1..bound-1. Do it iterations times.
+     * This function assumes that the value of bound is at least 2 and
+     * that iterations is large enough that a one-in-2^iterations chance
+     * effectively never occurs.
+     */
+
+    data_t bound_bytes = {NULL, 0};
+    mbedtls_mpi_uint *upper_bound = NULL;
+    size_t limbs;
+    size_t n_bits;
+    mbedtls_mpi_uint *result = NULL;
+    size_t b;
+    /* If upper_bound is small, stats[b] is the number of times the value b
+     * has been generated. Otherwise stats[b] is the number of times a
+     * value with bit b set has been generated. */
+    size_t *stats = NULL;
+    size_t stats_len;
+    int full_stats;
+    size_t i;
+
+    TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &upper_bound, &limbs,
+                                               bound_hex ) );
+    ASSERT_ALLOC( result, limbs );
+
+    n_bits = mbedtls_mpi_core_bitlen( upper_bound, limbs );
+    /* Consider a bound "small" if it's less than 2^5. This value is chosen
+     * to be small enough that the probability of missing one value is
+     * negligible given the number of iterations. It must be less than
+     * 256 because some of the code below assumes that "small" values
+     * fit in a byte. */
+    if( n_bits <= 5 )
+    {
+        full_stats = 1;
+        stats_len = (uint8_t) upper_bound[0];
+    }
+    else
+    {
+        full_stats = 0;
+        stats_len = n_bits;
+    }
+    ASSERT_ALLOC( stats, stats_len );
+
+    for( i = 0; i < (size_t) iterations; i++ )
+    {
+        mbedtls_test_set_step( i );
+        TEST_EQUAL( 0, mbedtls_mpi_core_random( result,
+                                                min, upper_bound, limbs,
+                                                mbedtls_test_rnd_std_rand, NULL ) );
+
+        /* Temporarily use a legacy MPI for analysis, because the
+         * necessary auxiliary functions don't exist yet in core. */
+        mbedtls_mpi B = {1, limbs, upper_bound};
+        mbedtls_mpi R = {1, limbs, result};
+
+        TEST_ASSERT( mbedtls_mpi_cmp_mpi( &R, &B ) < 0 );
+        TEST_ASSERT( mbedtls_mpi_cmp_int( &R, min ) >= 0 );
+        if( full_stats )
+        {
+            uint8_t value;
+            TEST_EQUAL( 0, mbedtls_mpi_write_binary( &R, &value, 1 ) );
+            TEST_ASSERT( value < stats_len );
+            ++stats[value];
+        }
+        else
+        {
+            for( b = 0; b < n_bits; b++ )
+                stats[b] += mbedtls_mpi_get_bit( &R, b );
+        }
+    }
+
+    if( full_stats )
+    {
+        for( b = min; b < stats_len; b++ )
+        {
+            mbedtls_test_set_step( 1000000 + b );
+            /* Assert that each value has been reached at least once.
+             * This is almost guaranteed if the iteration count is large
+             * enough. This is a very crude way of checking the distribution.
+             */
+            TEST_ASSERT( stats[b] > 0 );
+        }
+    }
+    else
+    {
+        bound_bytes.len = limbs * sizeof( mbedtls_mpi_uint );
+        ASSERT_ALLOC( bound_bytes.x, bound_bytes.len );
+        mbedtls_mpi_core_write_be( upper_bound, limbs,
+                                   bound_bytes.x, bound_bytes.len );
+        int statistically_safe_all_the_way =
+            is_significantly_above_a_power_of_2( &bound_bytes );
+        for( b = 0; b < n_bits; b++ )
+        {
+            mbedtls_test_set_step( 1000000 + b );
+            /* Assert that each bit has been set in at least one result and
+             * clear in at least one result. Provided that iterations is not
+             * too small, it would be extremely unlikely for this not to be
+             * the case if the results are uniformly distributed.
+             *
+             * As an exception, the top bit may legitimately never be set
+             * if bound is a power of 2 or only slightly above.
+             */
+            if( statistically_safe_all_the_way || b != n_bits - 1 )
+            {
+                TEST_ASSERT( stats[b] > 0 );
+            }
+            TEST_ASSERT( stats[b] < (size_t) iterations );
+        }
+    }
+
+exit:
+    mbedtls_free( bound_bytes.x );
+    mbedtls_free( upper_bound );
+    mbedtls_free( result );
+    mbedtls_free( stats );
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void mpi_random_sizes( int min, data_t *bound_bytes, int nlimbs, int before )
+{
+    mbedtls_mpi upper_bound;
+    mbedtls_mpi result;
+
+    mbedtls_mpi_init( &upper_bound );
+    mbedtls_mpi_init( &result );
+
+    if( before != 0 )
+    {
+        /* Set result to sign(before) * 2^(|before|-1) */
+        TEST_ASSERT( mbedtls_mpi_lset( &result, before > 0 ? 1 : -1 ) == 0 );
+        if( before < 0 )
+            before = - before;
+        TEST_ASSERT( mbedtls_mpi_shift_l( &result, before - 1 ) == 0 );
+    }
+
+    TEST_EQUAL( 0, mbedtls_mpi_grow( &result, nlimbs ) );
+    TEST_EQUAL( 0, mbedtls_mpi_read_binary( &upper_bound,
+                                            bound_bytes->x, bound_bytes->len ) );
+    TEST_EQUAL( 0, mbedtls_mpi_random( &result, min, &upper_bound,
+                                       mbedtls_test_rnd_std_rand, NULL ) );
+    TEST_ASSERT( sign_is_valid( &result ) );
+    TEST_ASSERT( mbedtls_mpi_cmp_mpi( &result, &upper_bound ) < 0 );
+    TEST_ASSERT( mbedtls_mpi_cmp_int( &result, min ) >= 0 );
+
+exit:
+    mbedtls_mpi_free( &upper_bound );
+    mbedtls_mpi_free( &result );
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void mpi_random_fail( int min, data_t *bound_bytes, int expected_ret )
+{
+    mbedtls_mpi upper_bound;
+    mbedtls_mpi result;
+    int actual_ret;
+
+    mbedtls_mpi_init( &upper_bound );
+    mbedtls_mpi_init( &result );
+
+    TEST_EQUAL( 0, mbedtls_mpi_read_binary( &upper_bound,
+                                            bound_bytes->x, bound_bytes->len ) );
+    actual_ret = mbedtls_mpi_random( &result, min, &upper_bound,
+                                     mbedtls_test_rnd_std_rand, NULL );
+    TEST_EQUAL( expected_ret, actual_ret );
+
+exit:
+    mbedtls_mpi_free( &upper_bound );
+    mbedtls_mpi_free( &result );
+}
+/* END_CASE */
diff --git a/tests/suites/test_suite_ecp.function b/tests/suites/test_suite_ecp.function
index 7d29e52..2971c57 100644
--- a/tests/suites/test_suite_ecp.function
+++ b/tests/suites/test_suite_ecp.function
@@ -237,7 +237,7 @@
 }
 /* END_CASE */
 
-/* BEGIN_CASE depends_on:MBEDTLS_ECP_RESTARTABLE */
+/* BEGIN_CASE depends_on:MBEDTLS_ECP_RESTARTABLE:MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */
 void ecp_muladd_restart( int id, char *xR_str, char *yR_str,
                          char *u1_str, char *u2_str,
                          char *xQ_str, char *yQ_str,
diff --git a/tests/suites/test_suite_x509parse.function b/tests/suites/test_suite_x509parse.function
index 2585720..dc36b81 100644
--- a/tests/suites/test_suite_x509parse.function
+++ b/tests/suites/test_suite_x509parse.function
@@ -579,6 +579,8 @@
     mbedtls_x509_crt_init( &crt );
     mbedtls_x509_crt_init( &ca );
 
+    USE_PSA_INIT( );
+
     TEST_ASSERT( mbedtls_x509_crt_parse_file( &crt, crt_file ) == 0 );
     TEST_ASSERT( mbedtls_x509_crt_parse_file( &ca, ca_file ) == 0 );
 
@@ -607,6 +609,7 @@
     mbedtls_x509_crt_restart_free( &rs_ctx );
     mbedtls_x509_crt_free( &crt );
     mbedtls_x509_crt_free( &ca );
+    USE_PSA_DONE( );
 }
 /* END_CASE */