Merge pull request #6836 from gilles-peskine-arm/code-style-more-kr-20221222

Tweak code style to be more like K&R
diff --git a/ChangeLog.d/mbedtls_ecp_point_read_binary-compressed-fmt.txt b/ChangeLog.d/mbedtls_ecp_point_read_binary-compressed-fmt.txt
new file mode 100644
index 0000000..44253dd
--- /dev/null
+++ b/ChangeLog.d/mbedtls_ecp_point_read_binary-compressed-fmt.txt
@@ -0,0 +1,6 @@
+Features
+   * Add support for reading points in compressed format
+     (MBEDTLS_ECP_PF_COMPRESSED) with mbedtls_ecp_point_read_binary()
+     (and callers) for Short Weierstrass curves with prime p where p = 3 mod 4
+     (all mbedtls MBEDTLS_ECP_DP_SECP* and MBEDTLS_ECP_DP_BP* curves
+      except MBEDTLS_ECP_DP_SECP224R1 and MBEDTLS_ECP_DP_SECP224K1)
diff --git a/library/bignum_mod.c b/library/bignum_mod.c
index 31e18e7..bd67241 100644
--- a/library/bignum_mod.c
+++ b/library/bignum_mod.c
@@ -176,6 +176,28 @@
 
 /* BEGIN MERGE SLOT 2 */
 
+int mbedtls_mpi_mod_mul( mbedtls_mpi_mod_residue *X,
+                         const mbedtls_mpi_mod_residue *A,
+                         const mbedtls_mpi_mod_residue *B,
+                         const mbedtls_mpi_mod_modulus *N )
+{
+    if( N->limbs == 0 )
+        return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
+
+    if( X->limbs != N->limbs || A->limbs != N->limbs || B->limbs != N->limbs )
+        return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
+
+    mbedtls_mpi_uint *T = mbedtls_calloc( N->limbs * 2 + 1, ciL );
+    if( T == NULL )
+        return MBEDTLS_ERR_MPI_ALLOC_FAILED;
+
+    mbedtls_mpi_mod_raw_mul( X->p, A->p, B->p, N, T );
+
+    mbedtls_free( T );
+
+    return( 0 );
+}
+
 /* END MERGE SLOT 2 */
 
 /* BEGIN MERGE SLOT 3 */
@@ -303,6 +325,17 @@
 
 /* BEGIN MERGE SLOT 6 */
 
+int mbedtls_mpi_mod_random( mbedtls_mpi_mod_residue *X,
+                            mbedtls_mpi_uint min,
+                            const mbedtls_mpi_mod_modulus *N,
+                            int (*f_rng)(void *, unsigned char *, size_t),
+                            void *p_rng )
+{
+    if( X->limbs != N->limbs )
+        return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+    return( mbedtls_mpi_mod_raw_random( X->p, min, N, f_rng, p_rng ) );
+}
+
 /* END MERGE SLOT 6 */
 
 /* BEGIN MERGE SLOT 7 */
@@ -326,8 +359,7 @@
 
     r->limbs = m->limbs;
 
-    if( m->int_rep == MBEDTLS_MPI_MOD_REP_MONTGOMERY )
-       ret = mbedtls_mpi_mod_raw_to_mont_rep( r->p, m );
+    ret = mbedtls_mpi_mod_raw_canonical_to_modulus_rep( r->p, m );
 
 cleanup:
     return ( ret );
diff --git a/library/bignum_mod.h b/library/bignum_mod.h
index 95aaacc..1b1e920 100644
--- a/library/bignum_mod.h
+++ b/library/bignum_mod.h
@@ -87,12 +87,23 @@
 #include "mbedtls/bignum.h"
 #endif
 
-/* Skip 1 as it is slightly easier to accidentally pass to functions. */
+/** How residues associated with a modulus are represented.
+ *
+ * This also determines which fields of the modulus structure are valid and
+ * what their contents are (see #mbedtls_mpi_mod_modulus).
+ */
 typedef enum
 {
+    /** Representation not chosen (makes the modulus structure invalid). */
     MBEDTLS_MPI_MOD_REP_INVALID    = 0,
+    /* Skip 1 as it is slightly easier to accidentally pass to functions. */
+    /** Montgomery representation. */
     MBEDTLS_MPI_MOD_REP_MONTGOMERY = 2,
-    MBEDTLS_MPI_MOD_REP_OPT_RED
+    /** TODO: document this.
+     *
+     * Residues are in canonical representation.
+     */
+    MBEDTLS_MPI_MOD_REP_OPT_RED,
 } mbedtls_mpi_mod_rep_selector;
 
 /* Make mbedtls_mpi_mod_rep_selector and mbedtls_mpi_mod_ext_rep disjoint to
@@ -124,7 +135,9 @@
     mbedtls_mpi_mod_rep_selector int_rep;    // selector to signal the active member of the union
     union rep
     {
+        /* if int_rep == #MBEDTLS_MPI_MOD_REP_MONTGOMERY */
         mbedtls_mpi_mont_struct mont;
+        /* if int_rep == #MBEDTLS_MPI_MOD_REP_OPT_RED */
         mbedtls_mpi_opt_red_struct ored;
     } rep;
 } mbedtls_mpi_mod_modulus;
@@ -217,6 +230,40 @@
 
 /* BEGIN MERGE SLOT 2 */
 
+/** \brief  Multiply two residues, 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.
+ *
+ * \return      \c 0 if successful.
+ * \return      #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if all the parameters do not
+ *              have the same number of limbs or \p N is invalid.
+ * \return      #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure.
+ */
+int mbedtls_mpi_mod_mul( mbedtls_mpi_mod_residue *X,
+                         const mbedtls_mpi_mod_residue *A,
+                         const mbedtls_mpi_mod_residue *B,
+                         const mbedtls_mpi_mod_modulus *N );
+
 /* END MERGE SLOT 2 */
 
 /* BEGIN MERGE SLOT 3 */
@@ -319,6 +366,39 @@
 
 /* BEGIN MERGE SLOT 6 */
 
+/** 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 residue.
+ * \param min      The minimum value to return. It must be strictly smaller
+ *                 than \b N.
+ * \param N        The modulus.
+ *                 This is the upper bound of the output range, exclusive.
+ * \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_mod_random( mbedtls_mpi_mod_residue *X,
+                            mbedtls_mpi_uint min,
+                            const mbedtls_mpi_mod_modulus *N,
+                            int (*f_rng)(void *, unsigned char *, size_t),
+                            void *p_rng );
+
 /* END MERGE SLOT 6 */
 
 /* BEGIN MERGE SLOT 7 */
diff --git a/library/bignum_mod_raw.c b/library/bignum_mod_raw.c
index 5950ff6..18599c3 100644
--- a/library/bignum_mod_raw.c
+++ b/library/bignum_mod_raw.c
@@ -186,6 +186,48 @@
 
 /* BEGIN MERGE SLOT 6 */
 
+int mbedtls_mpi_mod_raw_canonical_to_modulus_rep(
+    mbedtls_mpi_uint *X,
+    const mbedtls_mpi_mod_modulus *N )
+{
+    switch( N->int_rep )
+    {
+        case MBEDTLS_MPI_MOD_REP_MONTGOMERY:
+            return( mbedtls_mpi_mod_raw_to_mont_rep( X, N ) );
+        case MBEDTLS_MPI_MOD_REP_OPT_RED:
+            return( 0 );
+        default:
+            return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+    }
+}
+
+int mbedtls_mpi_mod_raw_modulus_to_canonical_rep(
+    mbedtls_mpi_uint *X,
+    const mbedtls_mpi_mod_modulus *N )
+{
+    switch( N->int_rep )
+    {
+        case MBEDTLS_MPI_MOD_REP_MONTGOMERY:
+            return( mbedtls_mpi_mod_raw_from_mont_rep( X, N ) );
+        case MBEDTLS_MPI_MOD_REP_OPT_RED:
+            return( 0 );
+        default:
+            return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+    }
+}
+
+int mbedtls_mpi_mod_raw_random( mbedtls_mpi_uint *X,
+                                mbedtls_mpi_uint min,
+                                const mbedtls_mpi_mod_modulus *N,
+                                int (*f_rng)(void *, unsigned char *, size_t),
+                                void *p_rng )
+{
+    int ret = mbedtls_mpi_core_random( X, min, N->p, N->limbs, f_rng, p_rng );
+    if( ret != 0 )
+        return( ret );
+    return( mbedtls_mpi_mod_raw_canonical_to_modulus_rep( X, N ) );
+}
+
 /* END MERGE SLOT 6 */
 
 /* BEGIN MERGE SLOT 7 */
diff --git a/library/bignum_mod_raw.h b/library/bignum_mod_raw.h
index 0fac6f8..ea3207f 100644
--- a/library/bignum_mod_raw.h
+++ b/library/bignum_mod_raw.h
@@ -336,6 +336,74 @@
 
 /* BEGIN MERGE SLOT 6 */
 
+/** Convert an MPI from canonical representation (little-endian limb array)
+ * to the representation associated with the modulus.
+ *
+ * \param[in,out] X The limb array to convert.
+ *                  It must have as many limbs as \p N.
+ *                  It is converted in place.
+ *                  If this function returns an error, the content of \p X
+ *                  is unspecified.
+ * \param[in] N     The modulus structure.
+ *
+ *\ return          \c 0 if successful.
+ *                  Otherwise an \c MBEDTLS_ERR_MPI_xxx error code.
+ */
+int mbedtls_mpi_mod_raw_canonical_to_modulus_rep(
+    mbedtls_mpi_uint *X,
+    const mbedtls_mpi_mod_modulus *N );
+
+/** Convert an MPI from the representation associated with the modulus
+ * to canonical representation (little-endian limb array).
+ *
+ * \param[in,out] X The limb array to convert.
+ *                  It must have as many limbs as \p N.
+ *                  It is converted in place.
+ *                  If this function returns an error, the content of \p X
+ *                  is unspecified.
+ * \param[in] N     The modulus structure.
+ *
+ *\ return          \c 0 if successful.
+ *                  Otherwise an \c MBEDTLS_ERR_MPI_xxx error code.
+ */
+int mbedtls_mpi_mod_raw_modulus_to_canonical_rep(
+    mbedtls_mpi_uint *X,
+    const mbedtls_mpi_mod_modulus *N );
+
+/** 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, in canonical representation modulo \p N.
+ *                 It must not be aliased with \p N or otherwise overlap it.
+ * \param min      The minimum value to return. It must be strictly smaller
+ *                 than \b N.
+ * \param N        The modulus.
+ *                 This is the upper bound of the output range, exclusive.
+ * \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_mod_raw_random( mbedtls_mpi_uint *X,
+                                mbedtls_mpi_uint min,
+                                const mbedtls_mpi_mod_modulus *N,
+                                int (*f_rng)(void *, unsigned char *, size_t),
+                                void *p_rng );
+
 /* END MERGE SLOT 6 */
 
 /* BEGIN MERGE SLOT 7 */
diff --git a/library/ecp.c b/library/ecp.c
index cd7d554..b6ed60a 100644
--- a/library/ecp.c
+++ b/library/ecp.c
@@ -754,6 +754,13 @@
     return( ret );
 }
 
+#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
+static int mbedtls_ecp_sw_derive_y( const mbedtls_ecp_group *grp,
+                                    const mbedtls_mpi *X,
+                                    mbedtls_mpi *Y,
+                                    int parity_bit );
+#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */
+
 /*
  * Import a point from unsigned binary data (SEC1 2.3.4 and RFC7748)
  */
@@ -795,16 +802,29 @@
                 return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
         }
 
-        if( buf[0] != 0x04 )
-            return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
-
-        if( ilen != 2 * plen + 1 )
+        if( ilen < 1 + plen )
             return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
 
         MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( &pt->X, buf + 1, plen ) );
-        MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( &pt->Y,
-                                                  buf + 1 + plen, plen ) );
         MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &pt->Z, 1 ) );
+
+        if( buf[0] == 0x04 )
+        {
+            /* format == MBEDTLS_ECP_PF_UNCOMPRESSED */
+            if( ilen != 1 + plen * 2 )
+                return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
+            return( mbedtls_mpi_read_binary( &pt->Y, buf + 1 + plen, plen ) );
+        }
+        else if( buf[0] == 0x02 || buf[0] == 0x03 )
+        {
+            /* format == MBEDTLS_ECP_PF_COMPRESSED */
+            if( ilen != 1 + plen )
+                return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
+            return( mbedtls_ecp_sw_derive_y( grp, &pt->X, &pt->Y,
+                                             ( buf[0] & 1 ) ) );
+        }
+        else
+            return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
     }
 #endif
 
@@ -1191,6 +1211,86 @@
     MBEDTLS_MPI_CHK( mbedtls_mpi_safe_cond_swap( (X), (Y), (cond) ) )
 
 #if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
+
+/*
+ * Computes the right-hand side of the Short Weierstrass equation
+ * RHS = X^3 + A X + B
+ */
+static int ecp_sw_rhs( const mbedtls_ecp_group *grp,
+                       mbedtls_mpi *rhs,
+                       const mbedtls_mpi *X )
+{
+    int ret;
+
+    /* Compute X^3 + A X + B as X (X^2 + A) + B */
+    MPI_ECP_SQR( rhs, X );
+
+    /* Special case for A = -3 */
+    if( grp->A.p == NULL )
+    {
+        MPI_ECP_SUB_INT( rhs, rhs, 3 );
+    }
+    else
+    {
+        MPI_ECP_ADD( rhs, rhs, &grp->A );
+    }
+
+    MPI_ECP_MUL( rhs, rhs, X  );
+    MPI_ECP_ADD( rhs, rhs, &grp->B );
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * Derive Y from X and a parity bit
+ */
+static int mbedtls_ecp_sw_derive_y( const mbedtls_ecp_group *grp,
+                                    const mbedtls_mpi *X,
+                                    mbedtls_mpi *Y,
+                                    int parity_bit )
+{
+    /* w = y^2 = x^3 + ax + b
+     * y = sqrt(w) = w^((p+1)/4) mod p   (for prime p where p = 3 mod 4)
+     *
+     * Note: this method for extracting square root does not validate that w
+     * was indeed a square so this function will return garbage in Y if X
+     * does not correspond to a point on the curve.
+     */
+
+    /* Check prerequisite p = 3 mod 4 */
+    if( mbedtls_mpi_get_bit( &grp->P, 0 ) != 1 ||
+        mbedtls_mpi_get_bit( &grp->P, 1 ) != 1 )
+        return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
+
+    int ret;
+    mbedtls_mpi exp;
+    mbedtls_mpi_init( &exp );
+
+    /* use Y to store intermediate result, actually w above */
+    MBEDTLS_MPI_CHK( ecp_sw_rhs( grp, Y, X ) );
+
+    /* w = y^2 */ /* Y contains y^2 intermediate result */
+    /* exp = ((p+1)/4) */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( &exp, &grp->P, 1 ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &exp, 2 ) );
+    /* sqrt(w) = w^((p+1)/4) mod p   (for prime p where p = 3 mod 4) */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( Y, Y /*y^2*/, &exp, &grp->P, NULL ) );
+
+    /* check parity bit match or else invert Y */
+    /* This quick inversion implementation is valid because Y != 0 for all
+     * Short Weierstrass curves supported by mbedtls, as each supported curve
+     * has an order that is a large prime, so each supported curve does not
+     * have any point of order 2, and a point with Y == 0 would be of order 2 */
+    if( mbedtls_mpi_get_bit( Y, 0 ) != parity_bit )
+        MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( Y, &grp->P, Y ) );
+
+cleanup:
+
+    mbedtls_mpi_free( &exp );
+    return( ret );
+}
+
 /*
  * For curves in short Weierstrass form, we do all the internal operations in
  * Jacobian coordinates.
@@ -2611,23 +2711,10 @@
 
     /*
      * YY = Y^2
-     * RHS = X (X^2 + A) + B = X^3 + A X + B
+     * RHS = X^3 + A X + B
      */
     MPI_ECP_SQR( &YY,  &pt->Y );
-    MPI_ECP_SQR( &RHS, &pt->X );
-
-    /* Special case for A = -3 */
-    if( grp->A.p == NULL )
-    {
-        MPI_ECP_SUB_INT( &RHS, &RHS, 3 );
-    }
-    else
-    {
-        MPI_ECP_ADD( &RHS, &RHS, &grp->A );
-    }
-
-    MPI_ECP_MUL( &RHS, &RHS, &pt->X  );
-    MPI_ECP_ADD( &RHS, &RHS, &grp->B );
+    MBEDTLS_MPI_CHK( ecp_sw_rhs( grp, &RHS, &pt->X ) );
 
     if( MPI_ECP_CMP( &YY, &RHS ) != 0 )
         ret = MBEDTLS_ERR_ECP_INVALID_KEY;
diff --git a/scripts/mbedtls_dev/bignum_common.py b/scripts/mbedtls_dev/bignum_common.py
index c4efabf..2422175 100644
--- a/scripts/mbedtls_dev/bignum_common.py
+++ b/scripts/mbedtls_dev/bignum_common.py
@@ -15,6 +15,7 @@
 # limitations under the License.
 
 from abc import abstractmethod
+import enum
 from typing import Iterator, List, Tuple, TypeVar, Any
 from itertools import chain
 
@@ -53,7 +54,7 @@
         return 0
     return int(val, 16)
 
-def quote_str(val) -> str:
+def quote_str(val: str) -> str:
     return "\"{}\"".format(val)
 
 def bound_mpi(val: int, bits_in_limb: int) -> int:
@@ -139,7 +140,7 @@
     def hex_digits(self) -> int:
         return 2 * (self.limbs * self.bits_in_limb // 8)
 
-    def format_arg(self, val) -> str:
+    def format_arg(self, val: str) -> str:
         if self.input_style not in self.input_styles:
             raise ValueError("Unknown input style!")
         if self.input_style == "variable":
@@ -147,7 +148,7 @@
         else:
             return val.zfill(self.hex_digits)
 
-    def format_result(self, res) -> str:
+    def format_result(self, res: int) -> str:
         res_str = '{:x}'.format(res)
         return quote_str(self.format_arg(res_str))
 
@@ -245,6 +246,23 @@
                     )
 
 
+class ModulusRepresentation(enum.Enum):
+    """Representation selector of a modulus."""
+    # Numerical values aligned with the type mbedtls_mpi_mod_rep_selector
+    INVALID = 0
+    MONTGOMERY = 2
+    OPT_RED = 3
+
+    def symbol(self) -> str:
+        """The C symbol for this representation selector."""
+        return 'MBEDTLS_MPI_MOD_REP_' + self.name
+
+    @classmethod
+    def supported_representations(cls) -> List['ModulusRepresentation']:
+        """Return all representations that are supported in positive test cases."""
+        return [cls.MONTGOMERY, cls.OPT_RED]
+
+
 class ModOperationCommon(OperationCommon):
     #pylint: disable=abstract-method
     """Target for bignum mod_raw test case generation."""
@@ -266,6 +284,17 @@
     def from_montgomery(self, val: int) -> int:
         return (val * self.r_inv) % self.int_n
 
+    def convert_from_canonical(self, canonical: int,
+                               rep: ModulusRepresentation) -> int:
+        """Convert values from canonical representation to the given representation."""
+        if rep is ModulusRepresentation.MONTGOMERY:
+            return self.to_montgomery(canonical)
+        elif rep is ModulusRepresentation.OPT_RED:
+            return canonical
+        else:
+            raise ValueError('Modulus representation not supported: {}'
+                             .format(rep.name))
+
     @property
     def boundary(self) -> int:
         return self.int_n
@@ -282,6 +311,9 @@
     def arg_n(self) -> str:
         return self.format_arg(self.val_n)
 
+    def format_arg(self, val: str) -> str:
+        return super().format_arg(val).zfill(self.hex_digits)
+
     def arguments(self) -> List[str]:
         return [quote_str(self.arg_n)] + super().arguments()
 
diff --git a/scripts/mbedtls_dev/bignum_mod.py b/scripts/mbedtls_dev/bignum_mod.py
index 25afe30..a83e136 100644
--- a/scripts/mbedtls_dev/bignum_mod.py
+++ b/scripts/mbedtls_dev/bignum_mod.py
@@ -31,6 +31,26 @@
 
 # BEGIN MERGE SLOT 2
 
+class BignumModMul(bignum_common.ModOperationCommon,
+                   BignumModTarget):
+    # pylint:disable=duplicate-code
+    """Test cases for bignum mpi_mod_mul()."""
+    symbol = "*"
+    test_function = "mpi_mod_mul"
+    test_name = "mbedtls_mpi_mod_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
diff --git a/scripts/mbedtls_dev/bignum_mod_raw.py b/scripts/mbedtls_dev/bignum_mod_raw.py
index 09bbbee..f9d9899 100644
--- a/scripts/mbedtls_dev/bignum_mod_raw.py
+++ b/scripts/mbedtls_dev/bignum_mod_raw.py
@@ -14,8 +14,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from typing import Dict, List
+from typing import Iterator, List
 
+from . import test_case
 from . import test_data_generation
 from . import bignum_common
 from .bignum_data import ONLY_PRIME_MODULI
@@ -116,6 +117,88 @@
 
 # BEGIN MERGE SLOT 6
 
+class BignumModRawConvertRep(bignum_common.ModOperationCommon,
+                             BignumModRawTarget):
+    # This is an abstract class, it's ok to have unimplemented methods.
+    #pylint: disable=abstract-method
+    """Test cases for representation conversion."""
+    symbol = ""
+    input_style = "arch_split"
+    arity = 1
+    rep = bignum_common.ModulusRepresentation.INVALID
+
+    def set_representation(self, r: bignum_common.ModulusRepresentation) -> None:
+        self.rep = r
+
+    def arguments(self) -> List[str]:
+        return ([bignum_common.quote_str(self.arg_n), self.rep.symbol(),
+                 bignum_common.quote_str(self.arg_a)] +
+                self.result())
+
+    def description(self) -> str:
+        base = super().description()
+        mod_with_rep = 'mod({})'.format(self.rep.name)
+        return base.replace('mod', mod_with_rep, 1)
+
+    @classmethod
+    def test_cases_for_values(cls, rep: bignum_common.ModulusRepresentation,
+                              n: str, a: str) -> Iterator[test_case.TestCase]:
+        """Emit test cases for the given values (if any).
+
+        This may emit no test cases if a isn't valid for the modulus n,
+        or multiple test cases if rep requires different data depending
+        on the limb size.
+        """
+        for bil in cls.limb_sizes:
+            test_object = cls(n, a, bits_in_limb=bil)
+            test_object.set_representation(rep)
+            # The class is set to having separate test cases for each limb
+            # size, because the Montgomery representation requires it.
+            # But other representations don't require it. So for other
+            # representations, emit a single test case with no dependency
+            # on the limb size.
+            if rep is not bignum_common.ModulusRepresentation.MONTGOMERY:
+                test_object.dependencies = \
+                    [dep for dep in test_object.dependencies
+                     if not dep.startswith('MBEDTLS_HAVE_INT')]
+            if test_object.is_valid:
+                yield test_object.create_test_case()
+            if rep is not bignum_common.ModulusRepresentation.MONTGOMERY:
+                # A single test case (emitted, or skipped due to invalidity)
+                # is enough, since this test case doesn't depend on the
+                # limb size.
+                break
+
+    # The parent class doesn't support non-bignum parameters. So we override
+    # test generation, in order to have the representation as a parameter.
+    @classmethod
+    def generate_function_tests(cls) -> Iterator[test_case.TestCase]:
+
+        for rep in bignum_common.ModulusRepresentation.supported_representations():
+            for n in cls.moduli:
+                for a in cls.input_values:
+                    yield from cls.test_cases_for_values(rep, n, a)
+
+class BignumModRawCanonicalToModulusRep(BignumModRawConvertRep):
+    """Test cases for mpi_mod_raw_canonical_to_modulus_rep."""
+    test_function = "mpi_mod_raw_canonical_to_modulus_rep"
+    test_name = "Rep canon->mod"
+
+    def result(self) -> List[str]:
+        return [self.format_result(self.convert_from_canonical(self.int_a, self.rep))]
+
+class BignumModRawModulusToCanonicalRep(BignumModRawConvertRep):
+    """Test cases for mpi_mod_raw_modulus_to_canonical_rep."""
+    test_function = "mpi_mod_raw_modulus_to_canonical_rep"
+    test_name = "Rep mod->canon"
+
+    @property
+    def arg_a(self) -> str:
+        return self.format_arg("{:x}".format(self.convert_from_canonical(self.int_a, self.rep)))
+
+    def result(self) -> List[str]:
+        return [self.format_result(self.int_a)]
+
 # END MERGE SLOT 6
 
 # BEGIN MERGE SLOT 7
diff --git a/tests/include/test/bignum_helpers.h b/tests/include/test/bignum_helpers.h
new file mode 100644
index 0000000..164017e
--- /dev/null
+++ b/tests/include/test/bignum_helpers.h
@@ -0,0 +1,118 @@
+/**
+ * \file bignum_helpers.h
+ *
+ * \brief   This file contains the prototypes of helper functions for
+ *          bignum-related testing.
+ */
+
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *  not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef TEST_BIGNUM_HELPERS_H
+#define TEST_BIGNUM_HELPERS_H
+
+#include <mbedtls/build_info.h>
+
+#if defined(MBEDTLS_BIGNUM_C)
+
+#include <mbedtls/bignum.h>
+#include <bignum_mod.h>
+
+/** Allocate and populate a core MPI from a test case argument.
+ *
+ * This function allocates exactly as many limbs as necessary to fit
+ * the length of the input. In other words, it preserves leading zeros.
+ *
+ * The limb array is allocated with mbedtls_calloc() and must later be
+ * freed with mbedtls_free().
+ *
+ * \param[in,out] pX    The address where a pointer to the allocated limb
+ *                      array will be stored.
+ *                      \c *pX must be null on entry.
+ *                      On exit, \c *pX is null on error or if the number
+ *                      of limbs is 0.
+ * \param[out] plimbs   The address where the number of limbs will be stored.
+ * \param[in] input     The test argument to read.
+ *                      It is interpreted as a hexadecimal representation
+ *                      of a non-negative integer.
+ *
+ * \return \c 0 on success, an \c MBEDTLS_ERR_MPI_xxx error code otherwise.
+ */
+int mbedtls_test_read_mpi_core( mbedtls_mpi_uint **pX, size_t *plimbs,
+                                const char *input );
+
+/** Read a modulus from a hexadecimal string.
+ *
+ * This function allocates exactly as many limbs as necessary to fit
+ * the length of the input. In other words, it preserves leading zeros.
+ *
+ * The limb array is allocated with mbedtls_calloc() and must later be
+ * freed with mbedtls_free(). You can do that by calling
+ * mbedtls_test_mpi_mod_modulus_free_with_limbs().
+ *
+ * \param[in,out] N     A modulus structure. It must be initialized, but
+ *                      not set up.
+ * \param[in] s         The null-terminated hexadecimal string to read from.
+ * \param int_rep       The desired representation of residues.
+ *
+ * \return \c 0 on success, an \c MBEDTLS_ERR_MPI_xxx error code otherwise.
+ */
+int mbedtls_test_read_mpi_modulus( mbedtls_mpi_mod_modulus *N,
+                                   const char *s,
+                                   mbedtls_mpi_mod_rep_selector int_rep );
+
+/** Free a modulus and its limbs.
+ *
+ * \param[in] N         A modulus structure such that there is no other
+ *                      reference to `N->p`.
+ */
+void mbedtls_test_mpi_mod_modulus_free_with_limbs( mbedtls_mpi_mod_modulus *N );
+
+/** Read an MPI from a hexadecimal string.
+ *
+ * Like mbedtls_mpi_read_string(), but with tighter guarantees around
+ * edge cases.
+ *
+ * - This function guarantees that if \p s begins with '-' then the sign
+ *   bit of the result will be negative, even if the value is 0.
+ *   When this function encounters such a "negative 0", it
+ *   increments #mbedtls_test_case_uses_negative_0.
+ * - The size of the result is exactly the minimum number of limbs needed
+ *   to fit the digits in the input. In particular, this function constructs
+ *   a bignum with 0 limbs for an empty string, and a bignum with leading 0
+ *   limbs if the string has sufficiently many leading 0 digits.
+ *   This is important so that the "0 (null)" and "0 (1 limb)" and
+ *   "leading zeros" test cases do what they claim.
+ *
+ * \param[out] X        The MPI object to populate. It must be initialized.
+ * \param[in] s         The null-terminated hexadecimal string to read from.
+ *
+ * \return \c 0 on success, an \c MBEDTLS_ERR_MPI_xxx error code otherwise.
+ */
+int mbedtls_test_read_mpi( mbedtls_mpi *X, const char *s );
+
+/** Nonzero if the current test case had an input parsed with
+ * mbedtls_test_read_mpi() that is a negative 0 (`"-"`, `"-0"`, `"-00"`, etc.,
+ * constructing a result with the sign bit set to -1 and the value being
+ * all-limbs-0, which is not a valid representation in #mbedtls_mpi but is
+ * tested for robustness).
+ */
+extern unsigned mbedtls_test_case_uses_negative_0;
+
+#endif /* MBEDTLS_BIGNUM_C */
+
+#endif /* TEST_BIGNUM_HELPERS_H */
diff --git a/tests/include/test/helpers.h b/tests/include/test/helpers.h
index 5f9bde6..b64bfcb 100644
--- a/tests/include/test/helpers.h
+++ b/tests/include/test/helpers.h
@@ -216,6 +216,17 @@
                           int len );
 
 /**
+ * \brief Convert hexadecimal digit to an integer.
+ *
+ * \param c        The digit to convert (`'0'` to `'9'`, `'A'` to `'F'` or
+ *                 `'a'` to `'f'`).
+ * \param[out] uc  On success, the value of the digit (0 to 15).
+ *
+ * \return         0 on success, -1 if \p c is not a hexadecimal digit.
+ */
+int mbedtls_test_ascii2uc(const char c, unsigned char *uc);
+
+/**
  * Allocate and zeroize a buffer.
  *
  * If the size if zero, a pointer to a zeroized 1-byte buffer is returned.
@@ -269,60 +280,4 @@
                                  const char *file, int line);
 #endif
 
-#if defined(MBEDTLS_BIGNUM_C)
-/** Allocate and populate a core MPI from a test case argument.
- *
- * This function allocates exactly as many limbs as necessary to fit
- * the length of the input. In other words, it preserves leading zeros.
- *
- * The limb array is allocated with mbedtls_calloc() and must later be
- * freed with mbedtls_free().
- *
- * \param[in,out] pX    The address where a pointer to the allocated limb
- *                      array will be stored.
- *                      \c *pX must be null on entry.
- *                      On exit, \c *pX is null on error or if the number
- *                      of limbs is 0.
- * \param[out] plimbs   The address where the number of limbs will be stored.
- * \param[in] input     The test argument to read.
- *                      It is interpreted as a hexadecimal representation
- *                      of a non-negative integer.
- *
- * \return \c 0 on success, an \c MBEDTLS_ERR_MPI_xxx error code otherwise.
- */
-int mbedtls_test_read_mpi_core( mbedtls_mpi_uint **pX, size_t *plimbs,
-                                const char *input );
-
-/** Read an MPI from a hexadecimal string.
- *
- * Like mbedtls_mpi_read_string(), but with tighter guarantees around
- * edge cases.
- *
- * - This function guarantees that if \p s begins with '-' then the sign
- *   bit of the result will be negative, even if the value is 0.
- *   When this function encounters such a "negative 0", it
- *   increments #mbedtls_test_case_uses_negative_0.
- * - The size of the result is exactly the minimum number of limbs needed
- *   to fit the digits in the input. In particular, this function constructs
- *   a bignum with 0 limbs for an empty string, and a bignum with leading 0
- *   limbs if the string has sufficiently many leading 0 digits.
- *   This is important so that the "0 (null)" and "0 (1 limb)" and
- *   "leading zeros" test cases do what they claim.
- *
- * \param[out] X        The MPI object to populate. It must be initialized.
- * \param[in] s         The null-terminated hexadecimal string to read from.
- *
- * \return \c 0 on success, an \c MBEDTLS_ERR_MPI_xxx error code otherwise.
- */
-int mbedtls_test_read_mpi( mbedtls_mpi *X, const char *s );
-
-/** Nonzero if the current test case had an input parsed with
- * mbedtls_test_read_mpi() that is a negative 0 (`"-"`, `"-0"`, `"-00"`, etc.,
- * constructing a result with the sign bit set to -1 and the value being
- * all-limbs-0, which is not a valid representation in #mbedtls_mpi but is
- * tested for robustness).
- */
-extern unsigned mbedtls_test_case_uses_negative_0;
-#endif /* MBEDTLS_BIGNUM_C */
-
 #endif /* TEST_HELPERS_H */
diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh
index 584906a..18dc8a6 100755
--- a/tests/scripts/all.sh
+++ b/tests/scripts/all.sh
@@ -870,7 +870,7 @@
     else
         opt=''
     fi
-    tests/scripts/check_test_cases.py $opt
+    tests/scripts/check_test_cases.py -q $opt
     unset opt
 }
 
diff --git a/tests/scripts/generate_bignum_tests.py b/tests/scripts/generate_bignum_tests.py
index 0b84711..6ee6ab3 100755
--- a/tests/scripts/generate_bignum_tests.py
+++ b/tests/scripts/generate_bignum_tests.py
@@ -60,7 +60,6 @@
 from typing import List
 
 import scripts_path # pylint: disable=unused-import
-from mbedtls_dev import test_case
 from mbedtls_dev import test_data_generation
 from mbedtls_dev import bignum_common
 # Import modules containing additional test classes
diff --git a/tests/src/bignum_helpers.c b/tests/src/bignum_helpers.c
new file mode 100644
index 0000000..d6ec9bd
--- /dev/null
+++ b/tests/src/bignum_helpers.c
@@ -0,0 +1,142 @@
+/**
+ * \file bignum_helpers.c
+ *
+ * \brief   This file contains the prototypes of helper functions for
+ *          bignum-related testing.
+ */
+
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *  not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#define MBEDTLS_ALLOW_PRIVATE_ACCESS
+#include <test/bignum_helpers.h>
+
+#if defined(MBEDTLS_BIGNUM_C)
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <mbedtls/bignum.h>
+#include <bignum_core.h>
+#include <bignum_mod.h>
+#include <bignum_mod_raw.h>
+
+#include <test/helpers.h>
+#include <test/macros.h>
+
+int mbedtls_test_read_mpi_core( mbedtls_mpi_uint **pX, size_t *plimbs,
+                                const char *input )
+{
+    /* Sanity check */
+    if( *pX != NULL )
+        return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+
+    size_t hex_len = strlen( input );
+    size_t byte_len = ( hex_len + 1 ) / 2;
+    *plimbs = CHARS_TO_LIMBS( byte_len );
+
+    /* A core bignum is not allowed to be empty. Forbid it as test data,
+     * this way static analyzers have a chance of knowing we don't expect
+     * the bignum functions to support empty inputs. */
+    if( *plimbs == 0 )
+        return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+
+    *pX = mbedtls_calloc( *plimbs, sizeof( **pX ) );
+    if( *pX == NULL )
+        return( MBEDTLS_ERR_MPI_ALLOC_FAILED );
+
+    unsigned char *byte_start = ( unsigned char * ) *pX;
+    if( byte_len % sizeof( mbedtls_mpi_uint ) != 0 )
+    {
+        byte_start += sizeof( mbedtls_mpi_uint ) - byte_len % sizeof( mbedtls_mpi_uint );
+    }
+    if( ( hex_len & 1 ) != 0 )
+    {
+        /* mbedtls_test_unhexify wants an even number of hex digits */
+        TEST_ASSERT( mbedtls_test_ascii2uc( *input, byte_start ) == 0 );
+        ++byte_start;
+        ++input;
+        --byte_len;
+    }
+    TEST_ASSERT( mbedtls_test_unhexify( byte_start,
+                                        byte_len,
+                                        input,
+                                        &byte_len ) == 0 );
+
+    mbedtls_mpi_core_bigendian_to_host( *pX, *plimbs );
+    return( 0 );
+
+exit:
+    mbedtls_free( *pX );
+    return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+}
+
+int mbedtls_test_read_mpi_modulus( mbedtls_mpi_mod_modulus *N,
+                                   const char *s,
+                                   mbedtls_mpi_mod_rep_selector int_rep )
+{
+    mbedtls_mpi_uint *p = NULL;
+    size_t limbs = 0;
+    if( N->limbs != 0 )
+        return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+    int ret = mbedtls_test_read_mpi_core( &p, &limbs, s );
+    if( ret != 0 )
+        return( ret );
+    ret = mbedtls_mpi_mod_modulus_setup( N, p, limbs, int_rep );
+    if( ret != 0 )
+        mbedtls_free( p );
+    return( ret );
+}
+
+void mbedtls_test_mpi_mod_modulus_free_with_limbs( mbedtls_mpi_mod_modulus *N )
+{
+    mbedtls_free( (mbedtls_mpi_uint*) N->p );
+    mbedtls_mpi_mod_modulus_free( N );
+}
+
+int mbedtls_test_read_mpi( mbedtls_mpi *X, const char *s )
+{
+    int negative = 0;
+    /* Always set the sign bit to -1 if the input has a minus sign, even for 0.
+     * This creates an invalid representation, which mbedtls_mpi_read_string()
+     * avoids but we want to be able to create that in test data. */
+    if( s[0] == '-' )
+    {
+        ++s;
+        negative = 1;
+    }
+    /* mbedtls_mpi_read_string() currently retains leading zeros.
+     * It always allocates at least one limb for the value 0. */
+    if( s[0] == 0 )
+    {
+        mbedtls_mpi_free( X );
+        return( 0 );
+    }
+    int ret = mbedtls_mpi_read_string( X, 16, s );
+    if( ret != 0 )
+        return( ret );
+    if( negative )
+    {
+        if( mbedtls_mpi_cmp_int( X, 0 ) == 0 )
+            ++mbedtls_test_case_uses_negative_0;
+        X->s = -1;
+    }
+    return( 0 );
+}
+
+#endif /* MBEDTLS_BIGNUM_C */
+
diff --git a/tests/src/helpers.c b/tests/src/helpers.c
index 7c83714..be5c465 100644
--- a/tests/src/helpers.c
+++ b/tests/src/helpers.c
@@ -48,7 +48,7 @@
 #endif /* MBEDTLS_PLATFORM_C */
 }
 
-static int ascii2uc(const char c, unsigned char *uc)
+int mbedtls_test_ascii2uc(const char c, unsigned char *uc)
 {
     if( ( c >= '0' ) && ( c <= '9' ) )
         *uc = c - '0';
@@ -207,10 +207,10 @@
 
     while( *ibuf != 0 )
     {
-        if ( ascii2uc( *(ibuf++), &uc ) != 0 )
+        if ( mbedtls_test_ascii2uc( *(ibuf++), &uc ) != 0 )
             return( -1 );
 
-        if ( ascii2uc( *(ibuf++), &uc2 ) != 0 )
+        if ( mbedtls_test_ascii2uc( *(ibuf++), &uc2 ) != 0 )
             return( -1 );
 
         *(obuf++) = ( uc << 4 ) | uc2;
@@ -350,84 +350,3 @@
     }
 }
 #endif /* MBEDTLS_TEST_HOOKS */
-
-#if defined(MBEDTLS_BIGNUM_C)
-#include "bignum_core.h"
-
-int mbedtls_test_read_mpi_core( mbedtls_mpi_uint **pX, size_t *plimbs,
-                                const char *input )
-{
-    /* Sanity check */
-    if( *pX != NULL )
-        return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
-
-    size_t hex_len = strlen( input );
-    size_t byte_len = ( hex_len + 1 ) / 2;
-    *plimbs = CHARS_TO_LIMBS( byte_len );
-
-    /* A core bignum is not allowed to be empty. Forbid it as test data,
-     * this way static analyzers have a chance of knowing we don't expect
-     * the bignum functions to support empty inputs. */
-    if( *plimbs == 0 )
-        return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
-
-    *pX = mbedtls_calloc( *plimbs, sizeof( **pX ) );
-    if( *pX == NULL )
-        return( MBEDTLS_ERR_MPI_ALLOC_FAILED );
-
-    unsigned char *byte_start = ( unsigned char * ) *pX;
-    if( byte_len % sizeof( mbedtls_mpi_uint ) != 0 )
-    {
-        byte_start += sizeof( mbedtls_mpi_uint ) - byte_len % sizeof( mbedtls_mpi_uint );
-    }
-    if( ( hex_len & 1 ) != 0 )
-    {
-        /* mbedtls_test_unhexify wants an even number of hex digits */
-        TEST_ASSERT( ascii2uc( *input, byte_start ) == 0 );
-        ++byte_start;
-        ++input;
-        --byte_len;
-    }
-    TEST_ASSERT( mbedtls_test_unhexify( byte_start,
-                                        byte_len,
-                                        input,
-                                        &byte_len ) == 0 );
-
-    mbedtls_mpi_core_bigendian_to_host( *pX, *plimbs );
-    return( 0 );
-
-exit:
-    mbedtls_free( *pX );
-    return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
-}
-
-int mbedtls_test_read_mpi( mbedtls_mpi *X, const char *s )
-{
-    int negative = 0;
-    /* Always set the sign bit to -1 if the input has a minus sign, even for 0.
-     * This creates an invalid representation, which mbedtls_mpi_read_string()
-     * avoids but we want to be able to create that in test data. */
-    if( s[0] == '-' )
-    {
-        ++s;
-        negative = 1;
-    }
-    /* mbedtls_mpi_read_string() currently retains leading zeros.
-     * It always allocates at least one limb for the value 0. */
-    if( s[0] == 0 )
-    {
-        mbedtls_mpi_free( X );
-        return( 0 );
-    }
-    int ret = mbedtls_mpi_read_string( X, 16, s );
-    if( ret != 0 )
-        return( ret );
-    if( negative )
-    {
-        if( mbedtls_mpi_cmp_int( X, 0 ) == 0 )
-            ++mbedtls_test_case_uses_negative_0;
-        X->s = -1;
-    }
-    return( 0 );
-}
-#endif
diff --git a/tests/suites/helpers.function b/tests/suites/helpers.function
index fe33f9b..8249564 100644
--- a/tests/suites/helpers.function
+++ b/tests/suites/helpers.function
@@ -5,6 +5,7 @@
 #include <test/helpers.h>
 #include <test/macros.h>
 #include <test/random.h>
+#include <test/bignum_helpers.h>
 #include <test/psa_crypto_helpers.h>
 
 #include <stdlib.h>
diff --git a/tests/suites/test_suite_bignum_mod.function b/tests/suites/test_suite_bignum_mod.function
index 79f5134..8ab8ccf 100644
--- a/tests/suites/test_suite_bignum_mod.function
+++ b/tests/suites/test_suite_bignum_mod.function
@@ -2,6 +2,7 @@
 #include "mbedtls/bignum.h"
 #include "mbedtls/entropy.h"
 #include "bignum_mod.h"
+#include "bignum_mod_raw.h"
 #include "constant_time_internal.h"
 #include "test/constant_flow.h"
 
@@ -102,6 +103,145 @@
 
 /* BEGIN MERGE SLOT 2 */
 
+/* BEGIN_CASE */
+void mpi_mod_mul( char * input_A,
+                  char * input_B,
+                  char * input_N,
+                  char * result )
+{
+    mbedtls_mpi_uint *X = NULL;
+
+    mbedtls_mpi_mod_residue rA = { NULL, 0 };
+    mbedtls_mpi_mod_residue rB = { NULL, 0 };
+    mbedtls_mpi_mod_residue rR = { NULL, 0 };
+    mbedtls_mpi_mod_residue rX = { NULL, 0 };
+
+    mbedtls_mpi_mod_modulus m;
+    mbedtls_mpi_mod_modulus_init( &m );
+
+    TEST_EQUAL( test_read_modulus( &m, MBEDTLS_MPI_MOD_REP_MONTGOMERY, input_N ),
+                0 );
+
+    TEST_EQUAL( test_read_residue( &rA, &m, input_A, 0 ), 0 );
+    TEST_EQUAL( test_read_residue( &rB, &m, input_B, 0 ), 0 );
+    TEST_EQUAL( test_read_residue( &rR, &m, result,  0 ), 0 );
+
+    const size_t limbs = m.limbs;
+    const size_t bytes = limbs * sizeof( mbedtls_mpi_uint );
+
+    TEST_EQUAL( rA.limbs, limbs );
+    TEST_EQUAL( rB.limbs, limbs );
+    TEST_EQUAL( rR.limbs, limbs );
+
+    ASSERT_ALLOC( X, limbs );
+
+    TEST_EQUAL( mbedtls_mpi_mod_residue_setup( &rX, &m, X, limbs ), 0 );
+
+    TEST_EQUAL( mbedtls_mpi_mod_mul( &rX, &rA, &rB, &m ), 0 );
+    ASSERT_COMPARE( rX.p, bytes, rR.p, bytes );
+
+    /* alias X to A */
+    memcpy( rX.p, rA.p, bytes );
+    TEST_EQUAL( mbedtls_mpi_mod_mul( &rX, &rX, &rB, &m ), 0 );
+    ASSERT_COMPARE( rX.p, bytes, rR.p, bytes );
+
+    /* alias X to B */
+    memcpy( rX.p, rB.p, bytes );
+    TEST_EQUAL( mbedtls_mpi_mod_mul( &rX, &rA, &rX, &m ), 0);
+    ASSERT_COMPARE( rX.p, bytes, rR.p, bytes );
+
+    /* A == B: alias A and B */
+    if( memcmp( rA.p, rB.p, bytes ) == 0 )
+    {
+        TEST_EQUAL( mbedtls_mpi_mod_mul( &rX, &rA, &rA, &m ), 0 );
+        ASSERT_COMPARE( rX.p, bytes, rR.p, bytes );
+
+        /* X, A, B all aliased together */
+        memcpy( rX.p, rA.p, bytes );
+        TEST_EQUAL( mbedtls_mpi_mod_mul( &rX, &rX, &rX, &m ), 0 );
+        ASSERT_COMPARE( rX.p, bytes, rR.p, bytes );
+    }
+
+    /* A != B: test B * A */
+    else
+    {
+        TEST_EQUAL( mbedtls_mpi_mod_mul( &rX, &rB, &rA, &m ), 0 );
+        ASSERT_COMPARE( rX.p, bytes, rR.p, bytes );
+
+        /* B * A: alias X to A */
+        memcpy( rX.p, rA.p, bytes );
+        TEST_EQUAL( mbedtls_mpi_mod_mul( &rX, &rB, &rX, &m ), 0 );
+        ASSERT_COMPARE( rX.p, bytes, rR.p, bytes );
+
+        /* B + A: alias X to B */
+        memcpy( rX.p, rB.p, bytes );
+        TEST_EQUAL( mbedtls_mpi_mod_mul( &rX, &rX, &rA, &m ), 0 );
+        ASSERT_COMPARE( rX.p, bytes, rR.p, bytes );
+    }
+
+exit:
+    mbedtls_free( rA.p );
+    mbedtls_free( rB.p );
+    mbedtls_free( rR.p );
+    mbedtls_free( X );
+    mbedtls_free( (mbedtls_mpi_uint *) m.p );
+
+    mbedtls_mpi_mod_modulus_free( &m );
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void mpi_mod_mul_neg( char * input_A,
+                      char * input_B,
+                      char * input_N,
+                      char * result,
+                      int exp_ret )
+{
+    mbedtls_mpi_uint *X = NULL;
+
+    mbedtls_mpi_mod_residue rA = { NULL, 0 };
+    mbedtls_mpi_mod_residue rB = { NULL, 0 };
+    mbedtls_mpi_mod_residue rR = { NULL, 0 };
+    mbedtls_mpi_mod_residue rX = { NULL, 0 };
+
+    mbedtls_mpi_mod_modulus m;
+    mbedtls_mpi_mod_modulus_init( &m );
+
+    mbedtls_mpi_mod_modulus fake_m;
+    mbedtls_mpi_mod_modulus_init( &fake_m );
+
+    TEST_EQUAL( test_read_modulus( &m, MBEDTLS_MPI_MOD_REP_MONTGOMERY, input_N ),
+                0 );
+
+    TEST_EQUAL( test_read_residue( &rA, &m, input_A, 1 ), 0 );
+    TEST_EQUAL( test_read_residue( &rB, &m, input_B, 1 ), 0 );
+    TEST_EQUAL( test_read_residue( &rR, &m, result,  1 ), 0 );
+
+    const size_t limbs = m.limbs;
+
+    ASSERT_ALLOC( X, limbs );
+
+    TEST_EQUAL( mbedtls_mpi_mod_residue_setup( &rX, &m, X, limbs ), 0 );
+    rX.limbs = rR.limbs;
+
+    TEST_EQUAL( mbedtls_mpi_mod_mul( &rX, &rA, &rB, &m ), exp_ret );
+
+    /* Check when m is not initialized */
+    TEST_EQUAL( mbedtls_mpi_mod_mul( &rX, &rA, &rB, &fake_m ),
+                MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+
+exit:
+    mbedtls_free( rA.p );
+    mbedtls_free( rB.p );
+    mbedtls_free( rR.p );
+    mbedtls_free( X );
+    mbedtls_free( (mbedtls_mpi_uint *) m.p );
+
+    mbedtls_mpi_mod_modulus_free( &m );
+    mbedtls_mpi_mod_modulus_free( &fake_m );
+}
+/* END_CASE */
+
 /* END MERGE SLOT 2 */
 
 /* BEGIN MERGE SLOT 3 */
diff --git a/tests/suites/test_suite_bignum_mod.misc.data b/tests/suites/test_suite_bignum_mod.misc.data
index 6240e21..e369211 100644
--- a/tests/suites/test_suite_bignum_mod.misc.data
+++ b/tests/suites/test_suite_bignum_mod.misc.data
@@ -12,7 +12,14 @@
 # END MERGE SLOT 1
 
 # BEGIN MERGE SLOT 2
+Test mpi_mod_mul #1 N->limbs != A->limbs
+mpi_mod_mul_neg:"1":"00000000000000000000000000000000":"f0000000000000000000000000000000":"0":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
 
+Test mpi_mod_mul #2 N->limbs != B->limbs
+mpi_mod_mul_neg:"1234567890abcdef1234567890abcdef":"0":"f0000000000000000000000000000000":"0":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+Test mpi_mod_mul #3 N->limbs != X->limbs
+mpi_mod_mul_neg:"1234567890abcdef1234567890abcdef":"00000000000000000000000000000000":"f0000000000000000000000000000000":"0":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
 # END MERGE SLOT 2
 
 # BEGIN MERGE SLOT 3
diff --git a/tests/suites/test_suite_bignum_mod_raw.function b/tests/suites/test_suite_bignum_mod_raw.function
index 461a18e..4a658e1 100644
--- a/tests/suites/test_suite_bignum_mod_raw.function
+++ b/tests/suites/test_suite_bignum_mod_raw.function
@@ -619,7 +619,59 @@
 /* END MERGE SLOT 5 */
 
 /* BEGIN MERGE SLOT 6 */
+/* BEGIN_CASE */
+void mpi_mod_raw_canonical_to_modulus_rep( const char *input_N, int rep,
+                                           const char *input_A,
+                                           const char *input_X )
+{
+    mbedtls_mpi_mod_modulus N;
+    mbedtls_mpi_mod_modulus_init( &N );
+    mbedtls_mpi_uint *A = NULL;
+    size_t A_limbs = 0;;
+    mbedtls_mpi_uint *X = NULL;
+    size_t X_limbs = 0;
 
+    TEST_EQUAL( 0, mbedtls_test_read_mpi_modulus( &N, input_N, rep ) );
+    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 ) );
+
+    TEST_EQUAL( 0, mbedtls_mpi_mod_raw_canonical_to_modulus_rep( A, &N ) );
+    ASSERT_COMPARE( A, A_limbs * sizeof( mbedtls_mpi_uint ),
+                    X, X_limbs * sizeof( mbedtls_mpi_uint ) );
+
+exit:
+    mbedtls_test_mpi_mod_modulus_free_with_limbs( &N );
+    mbedtls_free( A );
+    mbedtls_free( X );
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void mpi_mod_raw_modulus_to_canonical_rep( const char *input_N, int rep,
+                                           const char *input_A,
+                                           const char *input_X )
+{
+    mbedtls_mpi_mod_modulus N;
+    mbedtls_mpi_mod_modulus_init( &N );
+    mbedtls_mpi_uint *A = NULL;
+    size_t A_limbs = 0;
+    mbedtls_mpi_uint *X = NULL;
+    size_t X_limbs = 0;
+
+    TEST_EQUAL( 0, mbedtls_test_read_mpi_modulus( &N, input_N, rep ) );
+    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 ) );
+
+    TEST_EQUAL( 0, mbedtls_mpi_mod_raw_modulus_to_canonical_rep( A, &N ) );
+    ASSERT_COMPARE( A, A_limbs * sizeof( mbedtls_mpi_uint ),
+                    X, X_limbs * sizeof( mbedtls_mpi_uint ) );
+
+exit:
+    mbedtls_test_mpi_mod_modulus_free_with_limbs( &N );
+    mbedtls_free( A );
+    mbedtls_free( X );
+}
+/* END_CASE */
 /* END MERGE SLOT 6 */
 
 /* BEGIN MERGE SLOT 7 */
diff --git a/tests/suites/test_suite_bignum_random.data b/tests/suites/test_suite_bignum_random.data
index fe29053..ee5e397 100644
--- a/tests/suites/test_suite_bignum_random.data
+++ b/tests/suites/test_suite_bignum_random.data
@@ -17,31 +17,55 @@
 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
+# and for mpi_XXX_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+1 (NOT_ACCEPTABLE)
+mpi_core_random_basic:0x0fffffff:"10000001":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
 
-MPI random legacy=core: 2^28-1..2^28 (NOT_ACCEPTABLE)
-mpi_random_values:0x0fffffff:"10000000"
+MPI random legacy=core: 2^28-1..2^28+1 (NOT_ACCEPTABLE)
+mpi_legacy_random_values:0x0fffffff:"10000001"
 
-MPI core random basic: 2^29-1..2^29 (NOT_ACCEPTABLE)
-mpi_core_random_basic:0x1fffffff:"20000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
+MPI random mod=core: 2^28-1..2^28+1 (NOT_ACCEPTABLE) (Mont)
+mpi_mod_random_values:0x0fffffff:"10000001":MBEDTLS_MPI_MOD_REP_MONTGOMERY
 
-MPI random legacy=core: 2^29-1..2^29 (NOT_ACCEPTABLE)
-mpi_random_values:0x1fffffff:"20000000"
+MPI random mod=core: 2^28-1..2^28+1 (NOT_ACCEPTABLE) (canon)
+mpi_mod_random_values:0x0fffffff:"10000001":MBEDTLS_MPI_MOD_REP_OPT_RED
 
-MPI core random basic: 2^30-1..2^30 (NOT_ACCEPTABLE)
-mpi_core_random_basic:0x3fffffff:"40000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
+MPI core random basic: 2^29-1..2^29+1 (NOT_ACCEPTABLE)
+mpi_core_random_basic:0x1fffffff:"20000001":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
 
-MPI random legacy=core: 2^30-1..2^30 (NOT_ACCEPTABLE)
-mpi_random_values:0x3fffffff:"40000000"
+MPI random legacy=core: 2^29-1..2^29+1 (NOT_ACCEPTABLE)
+mpi_legacy_random_values:0x1fffffff:"20000001"
 
-MPI core random basic: 2^31-1..2^31 (NOT_ACCEPTABLE)
-mpi_core_random_basic:0x7fffffff:"80000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
+MPI random mod=core: 2^29-1..2^29+1 (NOT_ACCEPTABLE) (Mont)
+mpi_mod_random_values:0x1fffffff:"20000001":MBEDTLS_MPI_MOD_REP_MONTGOMERY
 
-MPI random legacy=core: 2^31-1..2^31 (NOT_ACCEPTABLE)
-mpi_random_values:0x7fffffff:"80000000"
+MPI random mod=core: 2^29-1..2^29+1 (NOT_ACCEPTABLE) (canon)
+mpi_mod_random_values:0x1fffffff:"20000001":MBEDTLS_MPI_MOD_REP_OPT_RED
+
+MPI core random basic: 2^30-1..2^30+1 (NOT_ACCEPTABLE)
+mpi_core_random_basic:0x3fffffff:"40000001":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
+
+MPI random legacy=core: 2^30-1..2^30+1 (NOT_ACCEPTABLE)
+mpi_legacy_random_values:0x3fffffff:"40000001"
+
+MPI random mod=core: 2^30-1..2^30+1 (NOT_ACCEPTABLE) (Mont)
+mpi_mod_random_values:0x3fffffff:"40000001":MBEDTLS_MPI_MOD_REP_MONTGOMERY
+
+MPI random mod=core: 2^30-1..2^30+1 (NOT_ACCEPTABLE) (canon)
+mpi_mod_random_values:0x3fffffff:"40000001":MBEDTLS_MPI_MOD_REP_OPT_RED
+
+MPI core random basic: 2^31-1..2^31+1 (NOT_ACCEPTABLE)
+mpi_core_random_basic:0x7fffffff:"80000001":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
+
+MPI random legacy=core: 2^31-1..2^31+1 (NOT_ACCEPTABLE)
+mpi_legacy_random_values:0x7fffffff:"80000001"
+
+MPI random mod=core: 2^31-1..2^31+1 (NOT_ACCEPTABLE) (Mont)
+mpi_mod_random_values:0x7fffffff:"80000001":MBEDTLS_MPI_MOD_REP_MONTGOMERY
+
+MPI random mod=core: 2^31-1..2^31+1 (NOT_ACCEPTABLE) (canon)
+mpi_mod_random_values:0x7fffffff:"80000001":MBEDTLS_MPI_MOD_REP_OPT_RED
 
 MPI random in range: 1..2
 mpi_random_many:1:"02":1000
@@ -214,22 +238,103 @@
 mpi_random_fail:2:"000000000000000001":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
 
 MPI random legacy=core: 0..1
-mpi_random_values:0:"01"
+mpi_legacy_random_values:0:"01"
 
 MPI random legacy=core: 0..2
-mpi_random_values:0:"02"
+mpi_legacy_random_values:0:"02"
 
 MPI random legacy=core: 1..2
-mpi_random_values:1:"02"
+mpi_legacy_random_values:1:"02"
 
 MPI random legacy=core: 2^30..2^31
-mpi_random_values:0x40000000:"80000000"
+mpi_legacy_random_values:0x40000000:"80000000"
 
 MPI random legacy=core: 2^31-1..2^32-1
-mpi_random_values:0x7fffffff:"ffffffff"
+mpi_legacy_random_values:0x7fffffff:"ffffffff"
 
 MPI random legacy=core: 0..2^256
-mpi_random_values:0:"010000000000000000000000000000000000000000000000000000000000000000"
+mpi_legacy_random_values:0:"010000000000000000000000000000000000000000000000000000000000000000"
 
 MPI random legacy=core: 0..2^256+1
-mpi_random_values:0:"010000000000000000000000000000000000000000000000000000000000000001"
+mpi_legacy_random_values:0:"010000000000000000000000000000000000000000000000000000000000000001"
+
+MPI random mod=core: 0..1 (Mont)
+mpi_mod_random_values:0:"01":MBEDTLS_MPI_MOD_REP_MONTGOMERY
+
+MPI random mod=core: 0..1 (canon)
+mpi_mod_random_values:0:"01":MBEDTLS_MPI_MOD_REP_OPT_RED
+
+MPI random mod=core: 0..3 (Mont)
+mpi_mod_random_values:0:"03":MBEDTLS_MPI_MOD_REP_MONTGOMERY
+
+MPI random mod=core: 0..3 (canon)
+mpi_mod_random_values:0:"03":MBEDTLS_MPI_MOD_REP_OPT_RED
+
+MPI random mod=core: 1..3 (Mont)
+mpi_mod_random_values:1:"03":MBEDTLS_MPI_MOD_REP_MONTGOMERY
+
+MPI random mod=core: 1..3 (canon)
+mpi_mod_random_values:1:"03":MBEDTLS_MPI_MOD_REP_OPT_RED
+
+MPI random mod=core: 2^30..2^31-1 (Mont)
+mpi_mod_random_values:0x40000000:"7fffffff":MBEDTLS_MPI_MOD_REP_MONTGOMERY
+
+MPI random mod=core: 2^30..2^31-1 (canon)
+mpi_mod_random_values:0x40000000:"7fffffff":MBEDTLS_MPI_MOD_REP_OPT_RED
+
+MPI random mod=core: 2^31-1..2^32-1 (Mont)
+mpi_mod_random_values:0x7fffffff:"ffffffff":MBEDTLS_MPI_MOD_REP_MONTGOMERY
+
+MPI random mod=core: 2^31-1..2^32-1 (canon)
+mpi_mod_random_values:0x7fffffff:"ffffffff":MBEDTLS_MPI_MOD_REP_OPT_RED
+
+MPI random mod=core: 0..2^256+1 (Mont)
+mpi_mod_random_values:0:"010000000000000000000000000000000000000000000000000000000000000001":MBEDTLS_MPI_MOD_REP_MONTGOMERY
+
+MPI random mod=core: 0..2^256+1 (canon)
+mpi_mod_random_values:0:"010000000000000000000000000000000000000000000000000000000000000001":MBEDTLS_MPI_MOD_REP_OPT_RED
+
+MPI random mod validation: 1 limb, good, 0..1
+mpi_mod_random_validation:0:"1":0:0
+
+MPI random mod validation: 1 limb, good, 1..3
+mpi_mod_random_validation:1:"3":0:0
+
+MPI random mod validation: 1 limb, good, 2..3
+mpi_mod_random_validation:2:"3":0:0
+
+MPI random mod validation: 1 limb, good, 3..5
+mpi_mod_random_validation:3:"5":0:0
+
+MPI random mod validation: 1 limb, good, 4..5
+mpi_mod_random_validation:4:"5":0:0
+
+MPI random mod validation: 1 limb, good, 5..7
+mpi_mod_random_validation:5:"7":0:0
+
+MPI random mod validation: 1 limb, good, 6..7
+mpi_mod_random_validation:6:"7":0:0
+
+MPI random mod validation: 1 limb, good, 0..0x123
+mpi_mod_random_validation:0:"123":0:0
+
+MPI random mod validation: 2+ limbs, good
+mpi_mod_random_validation:0:"01234567890123456789":0:0
+
+MPI random mod validation: 1 limb, output null
+mpi_mod_random_validation:0:"123":-1:MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+MPI random mod validation: 1 limb, output too large
+mpi_mod_random_validation:0:"123":1:MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+MPI random mod validation: 2+ limbs, output too small
+mpi_mod_random_validation:0:"01234567890123456789":-1:MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+MPI random mod validation: 2+ limbs, output too large
+mpi_mod_random_validation:0:"01234567890123456789":1:MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+MPI random mod validation: min == upper bound
+mpi_mod_random_validation:0x123:"123":-1:MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+MPI random mod validation: min > upper bound
+mpi_mod_random_validation:0x124:"123":-1:MBEDTLS_ERR_MPI_BAD_INPUT_DATA
diff --git a/tests/suites/test_suite_bignum_random.function b/tests/suites/test_suite_bignum_random.function
index 184de5a..4709148 100644
--- a/tests/suites/test_suite_bignum_random.function
+++ b/tests/suites/test_suite_bignum_random.function
@@ -3,11 +3,44 @@
  * 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.
+ *
+ * Test strategy
+ * =============
+ *
+ * There are three main goals for testing random() functions:
+ * - Parameter validation.
+ * - Correctness of outputs (well-formed, in range).
+ * - Distribution of outputs.
+ *
+ * We test parameter validation in a standard way, with unit tests with
+ * positive and negative cases:
+ * - mbedtls_mpi_core_random(): negative cases for mpi_core_random_basic.
+ * - mbedtls_mpi_mod_raw_random(),  mbedtls_mpi_mod_random(): negative
+ *   cases for mpi_mod_random_validation.
+ * - mbedtls_mpi_random(): mpi_random_fail.
+ *
+ * We test the correctness of outputs in positive tests:
+ * - mbedtls_mpi_core_random(): positive cases for mpi_core_random_basic,
+ *   and mpi_random_many.
+ * - mbedtls_mpi_mod_raw_random(), mbedtls_mpi_mod_random(): tested indirectly
+ *   via mpi_mod_random_values.
+ * - mbedtls_mpi_random(): mpi_random_sizes, plus indirectly via
+ *   mpi_random_values.
+ *
+ * We test the distribution of outputs only for mbedtls_mpi_core_random(),
+ * in mpi_random_many, which runs the function multiple times. This also
+ * helps in validating the output range, through test cases with a small
+ * range where any output out of range would be very likely to lead to a
+ * test failure. For the other functions, we validate the distribution
+ * indirectly by testing that these functions consume the random generator
+ * in the same way as mbedtls_mpi_core_random(). This is done in
+ * mpi_mod_random_values and mpi_legacy_random_values.
  */
 
 #include "mbedtls/bignum.h"
 #include "mbedtls/entropy.h"
 #include "bignum_core.h"
+#include "bignum_mod_raw.h"
 #include "constant_time_internal.h"
 
 /* This test suite only manipulates non-negative bignums. */
@@ -110,7 +143,7 @@
 /* END_CASE */
 
 /* BEGIN_CASE */
-void mpi_random_values( int min, char *max_hex )
+void mpi_legacy_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;
@@ -159,6 +192,77 @@
 /* END_CASE */
 
 /* BEGIN_CASE */
+void mpi_mod_random_values( int min, char *max_hex, int rep )
+{
+    /* Same RNG as in mpi_core_random_basic */
+    mbedtls_test_rnd_pseudo_info rnd_core = rnd_pseudo_seed;
+    mbedtls_test_rnd_pseudo_info rnd_mod_raw;
+    memcpy( &rnd_mod_raw, &rnd_core, sizeof( rnd_core ) );
+    mbedtls_test_rnd_pseudo_info rnd_mod;
+    memcpy( &rnd_mod, &rnd_core, sizeof( rnd_core ) );
+    mbedtls_mpi_uint *R_core = NULL;
+    mbedtls_mpi_uint *R_mod_raw = NULL;
+    mbedtls_mpi_uint *R_mod_digits = NULL;
+    mbedtls_mpi_mod_residue R_mod;
+    mbedtls_mpi_mod_modulus N;
+    mbedtls_mpi_mod_modulus_init( &N );
+
+    TEST_EQUAL( mbedtls_test_read_mpi_modulus( &N, max_hex, rep ), 0 );
+    ASSERT_ALLOC( R_core, N.limbs );
+    ASSERT_ALLOC( R_mod_raw, N.limbs );
+    ASSERT_ALLOC( R_mod_digits, N.limbs );
+    TEST_EQUAL( mbedtls_mpi_mod_residue_setup( &R_mod, &N,
+                                               R_mod_digits, N.limbs ),
+                0 );
+
+    /* Call the core and mod random() functions with the same random stream. */
+    int core_ret = mbedtls_mpi_core_random( R_core,
+                                            min, N.p, N.limbs,
+                                            mbedtls_test_rnd_pseudo_rand,
+                                            &rnd_core );
+    int mod_raw_ret = mbedtls_mpi_mod_raw_random( R_mod_raw,
+                                                  min, &N,
+                                                  mbedtls_test_rnd_pseudo_rand,
+                                                  &rnd_mod_raw );
+    int mod_ret = mbedtls_mpi_mod_random( &R_mod,
+                                          min, &N,
+                                          mbedtls_test_rnd_pseudo_rand,
+                                          &rnd_mod );
+
+    /* They must return the same status, and, on success, output the
+     * same number, with the same limb count. */
+    TEST_EQUAL( core_ret, mod_raw_ret );
+    TEST_EQUAL( core_ret, mod_ret );
+    if( core_ret == 0 )
+    {
+        TEST_EQUAL( mbedtls_mpi_mod_raw_modulus_to_canonical_rep( R_mod_raw, &N ),
+                    0 );
+        ASSERT_COMPARE( R_core, N.limbs * ciL,
+                        R_mod_raw, N.limbs * ciL );
+        TEST_EQUAL( mbedtls_mpi_mod_raw_modulus_to_canonical_rep( R_mod_digits, &N ),
+                    0 );
+        ASSERT_COMPARE( R_core, N.limbs * ciL,
+                        R_mod_digits, N.limbs * 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_mod_raw, sizeof( rnd_mod_raw ) );
+    ASSERT_COMPARE( &rnd_core, sizeof( rnd_core ),
+                    &rnd_mod, sizeof( rnd_mod ) );
+
+exit:
+    mbedtls_test_mpi_mod_modulus_free_with_limbs( &N );
+    mbedtls_free( R_core );
+    mbedtls_free( R_mod_raw );
+    mbedtls_free( R_mod_digits );
+}
+/* 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.
@@ -312,6 +416,64 @@
 /* END_CASE */
 
 /* BEGIN_CASE */
+void mpi_mod_random_validation( int min, char *bound_hex,
+                                int result_limbs_delta,
+                                int expected_ret )
+{
+    mbedtls_mpi_uint *result_digits = NULL;
+    mbedtls_mpi_mod_modulus N;
+    mbedtls_mpi_mod_modulus_init( &N );
+
+    TEST_EQUAL( mbedtls_test_read_mpi_modulus( &N, bound_hex,
+                                               MBEDTLS_MPI_MOD_REP_OPT_RED ),
+                0 );
+    size_t result_limbs = N.limbs + result_limbs_delta;
+    ASSERT_ALLOC( result_digits, result_limbs );
+    /* Build a reside that might not match the modulus, to test that
+     * the library function rejects that as expected. */
+    mbedtls_mpi_mod_residue result = {result_digits, result_limbs};
+
+    TEST_EQUAL( mbedtls_mpi_mod_random( &result, min, &N,
+                                        mbedtls_test_rnd_std_rand, NULL ),
+                expected_ret );
+    if( expected_ret == 0 )
+    {
+        /* Success should only be expected when the result has the same
+         * size as the modulus, otherwise it's a mistake in the test data. */
+        TEST_EQUAL( result_limbs, N.limbs );
+        /* Sanity check: check that the result is in range */
+        TEST_EQUAL( mbedtls_mpi_core_lt_ct( result_digits, N.p, N.limbs ),
+                    1 );
+        /* Check result >= min (changes result) */
+        TEST_EQUAL( mbedtls_mpi_core_sub_int( result_digits, result_digits, min,
+                                              result_limbs ),
+                    0 );
+    }
+
+    /* When the result has the right number of limbs, also test mod_raw
+     * (for which this is an unchecked precondition). */
+    if( result_limbs_delta == 0 )
+    {
+        TEST_EQUAL( mbedtls_mpi_mod_raw_random( result_digits, min, &N,
+                                                mbedtls_test_rnd_std_rand, NULL ),
+                    expected_ret );
+        if( expected_ret == 0 )
+        {
+            TEST_EQUAL( mbedtls_mpi_core_lt_ct( result_digits, N.p, N.limbs ),
+                        1 );
+            TEST_EQUAL( mbedtls_mpi_core_sub_int( result_digits, result.p, min,
+                                                  result_limbs ),
+                        0 );
+        }
+    }
+
+exit:
+    mbedtls_test_mpi_mod_modulus_free_with_limbs( &N );
+    mbedtls_free( result_digits );
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
 void mpi_random_fail( int min, data_t *bound_bytes, int expected_ret )
 {
     mbedtls_mpi upper_bound;
diff --git a/tests/suites/test_suite_ecjpake.data b/tests/suites/test_suite_ecjpake.data
index 73808c9..c2ec782 100644
--- a/tests/suites/test_suite_ecjpake.data
+++ b/tests/suites/test_suite_ecjpake.data
@@ -35,7 +35,7 @@
 read_round_one:MBEDTLS_ECJPAKE_CLIENT:"0100":MBEDTLS_ERR_ECP_INVALID_KEY
 
 ECJPAKE round one: KKP1: unknown first point format
-read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41057ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41057ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
 
 ECJPAKE round one: KKP1: nothing after first point
 read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
@@ -50,7 +50,7 @@
 read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b0104":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
 
 ECJPAKE round one: KKP1: unknown second point format
-read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b410509f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb516":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b410509f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb516":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
 
 ECJPAKE round one: KKP1: nothing after second point
 read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b410409f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb516":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
@@ -83,7 +83,7 @@
 read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb120100":MBEDTLS_ERR_ECP_INVALID_KEY
 
 ECJPAKE round one: KKP2: unknown first point format
-read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1241057ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1241057ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
 
 ECJPAKE round one: KKP2: nothing after first point
 read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1241047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
@@ -98,7 +98,7 @@
 read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1241047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b0104":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
 
 ECJPAKE round one: KKP2: unknown second point format
-read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1241047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b410509f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb516":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1241047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b410509f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb516":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
 
 ECJPAKE round one: KKP2: nothing after second point
 read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1241047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b410409f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb516":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
@@ -149,7 +149,7 @@
 read_round_two_cli:"0300170100":MBEDTLS_ERR_ECP_INVALID_KEY
 
 ECJPAKE round two client: unknown first point format
-read_round_two_cli:"03001741050fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a6":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+read_round_two_cli:"03001741050fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a6":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
 
 ECJPAKE round two client: nothing after first point
 read_round_two_cli:"03001741040fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a6":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
@@ -164,7 +164,7 @@
 read_round_two_cli:"03001741040fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a60104":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
 
 ECJPAKE round two client: unknown second point format
-read_round_two_cli:"03001741040fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a641055516ea3e54a0d5d8b2ce786b38d383370029a5dbe4459c9dd601b408a24ae6465c8ac905b9eb03b5d3691c139ef83f1cd4200f6c9cd4ec392218a59ed243d3c8":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+read_round_two_cli:"03001741040fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a641055516ea3e54a0d5d8b2ce786b38d383370029a5dbe4459c9dd601b408a24ae6465c8ac905b9eb03b5d3691c139ef83f1cd4200f6c9cd4ec392218a59ed243d3c8":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
 
 ECJPAKE round two client: nothing after second point
 read_round_two_cli:"03001741040fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a641045516ea3e54a0d5d8b2ce786b38d383370029a5dbe4459c9dd601b408a24ae6465c8ac905b9eb03b5d3691c139ef83f1cd4200f6c9cd4ec392218a59ed243d3c8":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
@@ -203,7 +203,7 @@
 read_round_two_srv:"0100":MBEDTLS_ERR_ECP_INVALID_KEY
 
 ECJPAKE round two server: unknown first point format
-read_round_two_srv:"410569d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+read_round_two_srv:"410569d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
 
 ECJPAKE round two server: nothing after first point
 read_round_two_srv:"410469d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
@@ -218,7 +218,7 @@
 read_round_two_srv:"410469d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee0104":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
 
 ECJPAKE round two server: unknown second point format
-read_round_two_srv:"410569d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee4104077e8c31e20e6bedb760c13593e69f15be85c27d68cd09ccb8c4183608917c5c3d409fac39fefee82f7292d36f0d23e055913f45a52b85dd8a2052e9e129bb4d":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+read_round_two_srv:"410569d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee4104077e8c31e20e6bedb760c13593e69f15be85c27d68cd09ccb8c4183608917c5c3d409fac39fefee82f7292d36f0d23e055913f45a52b85dd8a2052e9e129bb4d":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
 
 ECJPAKE round two server: nothing after second point
 read_round_two_srv:"410469d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee4104077e8c31e20e6bedb760c13593e69f15be85c27d68cd09ccb8c4183608917c5c3d409fac39fefee82f7292d36f0d23e055913f45a52b85dd8a2052e9e129bb4d":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
diff --git a/tests/suites/test_suite_ecp.data b/tests/suites/test_suite_ecp.data
index 4c0ed1c..9311200 100644
--- a/tests/suites/test_suite_ecp.data
+++ b/tests/suites/test_suite_ecp.data
@@ -227,19 +227,51 @@
 
 ECP read binary #2 (zero, invalid first byte)
 depends_on:MBEDTLS_ECP_DP_SECP192R1_ENABLED
-ecp_read_binary:MBEDTLS_ECP_DP_SECP192R1:"01":"01":"01":"00":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+ecp_read_binary:MBEDTLS_ECP_DP_SECP192R1:"01":"01":"01":"00":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
 
 ECP read binary #3 (zero, OK)
 depends_on:MBEDTLS_ECP_DP_SECP192R1_ENABLED
 ecp_read_binary:MBEDTLS_ECP_DP_SECP192R1:"00":"01":"01":"00":0
 
-ECP read binary #4 (non-zero, invalid ilen)
+ECP read binary #4 (non-zero, invalid ilen, too short)
 depends_on:MBEDTLS_ECP_DP_SECP192R1_ENABLED
 ecp_read_binary:MBEDTLS_ECP_DP_SECP192R1:"04001122":"01":"01":"00":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
 
+ECP read binary #4a (non-zero, invalid ilen, too short)
+depends_on:MBEDTLS_ECP_DP_SECP192R1_ENABLED
+ecp_read_binary:MBEDTLS_ECP_DP_SECP192R1:"03001122":"01":"01":"00":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECP read binary #4b (non-zero, invalid ilen, too short)
+depends_on:MBEDTLS_ECP_DP_SECP192R1_ENABLED
+ecp_read_binary:MBEDTLS_ECP_DP_SECP192R1:"02001122":"01":"01":"00":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECP read binary #4c (non-zero, invalid ilen, too long)
+depends_on:MBEDTLS_ECP_DP_SECP192R1_ENABLED
+ecp_read_binary:MBEDTLS_ECP_DP_SECP192R1:"040011223344556677889900112233445566778899001122334455":"01":"01":"00":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECP read binary #4d (non-zero, invalid ilen, too long)
+depends_on:MBEDTLS_ECP_DP_SECP192R1_ENABLED
+ecp_read_binary:MBEDTLS_ECP_DP_SECP192R1:"030011223344556677889900112233445566778899001122334455":"01":"01":"00":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECP read binary #4e (non-zero, invalid ilen, too long)
+depends_on:MBEDTLS_ECP_DP_SECP192R1_ENABLED
+ecp_read_binary:MBEDTLS_ECP_DP_SECP192R1:"020011223344556677889900112233445566778899001122334455":"01":"01":"00":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
 ECP read binary #5 (non-zero, invalid first byte)
 depends_on:MBEDTLS_ECP_DP_SECP192R1_ENABLED
-ecp_read_binary:MBEDTLS_ECP_DP_SECP192R1:"0548d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc99336ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":"48d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":"6ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":"01":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+ecp_read_binary:MBEDTLS_ECP_DP_SECP192R1:"0548d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc99336ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":"48d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":"6ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":"01":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECP read binary #5a (non-zero, compressed format, invalid first byte)
+depends_on:MBEDTLS_ECP_DP_SECP192R1_ENABLED
+ecp_read_binary:MBEDTLS_ECP_DP_SECP192R1:"0548d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":"48d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":"6ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":"01":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECP read binary #5b (non-zero, compressed format, parity 0, OK)
+depends_on:MBEDTLS_ECP_DP_SECP192R1_ENABLED
+ecp_read_binary:MBEDTLS_ECP_DP_SECP192R1:"0248d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":"48d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":"6ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":"01":0
+
+ECP read binary #5c (non-zero, compressed format, parity 1, OK)
+depends_on:MBEDTLS_ECP_DP_SECP192R1_ENABLED
+ecp_read_binary:MBEDTLS_ECP_DP_SECP192R1:"0348d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":"48d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":"93112b28345b7d1d7799611e49bea9d8290cb2d7afe1f9f3":"01":0
 
 ECP read binary #6 (non-zero, OK)
 depends_on:MBEDTLS_ECP_DP_SECP192R1_ENABLED
@@ -285,6 +317,14 @@
 depends_on:MBEDTLS_ECP_DP_CURVE448_ENABLED
 ecp_read_binary:MBEDTLS_ECP_DP_CURVE448:"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff":"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff":"0":"1":0
 
+ECP read binary #17 (non-zero, compressed format, p != 3 mod 4, secp224r1)
+depends_on:MBEDTLS_ECP_DP_SECP224R1_ENABLED
+ecp_read_binary:MBEDTLS_ECP_DP_SECP224R1:"0200000000000000000000000000000000000000000000000000000000":"01":"01":"01":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+
+ECP read binary #17a (non-zero, compressed format, p != 3 mod 4, secp224k1)
+depends_on:MBEDTLS_ECP_DP_SECP224K1_ENABLED
+ecp_read_binary:MBEDTLS_ECP_DP_SECP224K1:"0200000000000000000000000000000000000000000000000000000000":"01":"01":"01":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+
 ECP tls read point #1 (zero, invalid length byte)
 depends_on:MBEDTLS_ECP_DP_SECP192R1_ENABLED
 mbedtls_ecp_tls_read_point:MBEDTLS_ECP_DP_SECP192R1:"0200":"01":"01":"00":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
diff --git a/tests/suites/test_suite_ecp.function b/tests/suites/test_suite_ecp.function
index 2971c57..96b9f40 100644
--- a/tests/suites/test_suite_ecp.function
+++ b/tests/suites/test_suite_ecp.function
@@ -642,6 +642,19 @@
         {
             TEST_ASSERT( mbedtls_mpi_cmp_mpi( &P.Y, &Y ) == 0 );
             TEST_ASSERT( mbedtls_mpi_cmp_mpi( &P.Z, &Z ) == 0 );
+
+            if( buf->x[0] == 0x04 &&
+                /* (reading compressed format supported only for
+                 *  Short Weierstrass curves with prime p where p = 3 mod 4) */
+                id != MBEDTLS_ECP_DP_SECP224R1 &&
+                id != MBEDTLS_ECP_DP_SECP224K1 )
+            {
+                /* re-encode in compressed format and test read again */
+                mbedtls_mpi_free( &P.Y );
+                buf->x[0] = 0x02 + mbedtls_mpi_get_bit( &Y, 0 );
+                TEST_ASSERT( mbedtls_ecp_point_read_binary( &grp, &P, buf->x, buf->len/2+1 ) == 0 );
+                TEST_ASSERT( mbedtls_mpi_cmp_mpi( &P.Y, &Y ) == 0 );
+            }
         }
     }
 
@@ -703,8 +716,10 @@
     memset( buf, 0x00, sizeof( buf ) ); vbuf = buf;
     TEST_ASSERT( mbedtls_ecp_tls_write_point( &grp, &grp.G,
                     MBEDTLS_ECP_PF_COMPRESSED, &olen, buf, 256 ) == 0 );
-    TEST_ASSERT( mbedtls_ecp_tls_read_point( &grp, &pt, &vbuf, olen )
-                 == MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
+    TEST_ASSERT( mbedtls_ecp_tls_read_point( &grp, &pt, &vbuf, olen ) == 0 );
+    TEST_ASSERT( mbedtls_mpi_cmp_mpi( &grp.G.X, &pt.X ) == 0 );
+    TEST_ASSERT( mbedtls_mpi_cmp_mpi( &grp.G.Y, &pt.Y ) == 0 );
+    TEST_ASSERT( mbedtls_mpi_cmp_mpi( &grp.G.Z, &pt.Z ) == 0 );
     TEST_ASSERT( vbuf == buf + olen );
 
     memset( buf, 0x00, sizeof( buf ) ); vbuf = buf;