Merge pull request #6486 from xkqian/tls13_add_early_data_indication

The merge job of the internal CI ran successfully. This is good to go.
diff --git a/ChangeLog.d/fix_dh_genprime_error_reporting.txt b/ChangeLog.d/fix_dh_genprime_error_reporting.txt
new file mode 100644
index 0000000..1c98947
--- /dev/null
+++ b/ChangeLog.d/fix_dh_genprime_error_reporting.txt
@@ -0,0 +1,4 @@
+Bugfix
+   * Fix bug in error reporting in dh_genprime.c where upon failure,
+     the error code returned by mbedtls_mpi_write_file() is overwritten
+     and therefore not printed.
diff --git a/ChangeLog.d/negative-zero-from-add.txt b/ChangeLog.d/negative-zero-from-add.txt
new file mode 100644
index 0000000..107d858
--- /dev/null
+++ b/ChangeLog.d/negative-zero-from-add.txt
@@ -0,0 +1,6 @@
+Bugfix
+   * In the bignum module, operations of the form (-A) - (+A) or (-A) - (-A)
+     with A > 0 created an unintended representation of the value 0 which was
+     not processed correctly by some bignum operations. Fix this. This had no
+     consequence on cryptography code, but might affect applications that call
+     bignum directly and use negative numbers.
diff --git a/include/mbedtls/bignum.h b/include/mbedtls/bignum.h
index 9d15955..3bd1ca0 100644
--- a/include/mbedtls/bignum.h
+++ b/include/mbedtls/bignum.h
@@ -188,9 +188,27 @@
  */
 typedef struct mbedtls_mpi
 {
-    int MBEDTLS_PRIVATE(s);              /*!<  Sign: -1 if the mpi is negative, 1 otherwise */
-    size_t MBEDTLS_PRIVATE(n);           /*!<  total # of limbs  */
-    mbedtls_mpi_uint *MBEDTLS_PRIVATE(p);          /*!<  pointer to limbs  */
+    /** Sign: -1 if the mpi is negative, 1 otherwise.
+     *
+     * The number 0 must be represented with `s = +1`. Although many library
+     * functions treat all-limbs-zero as equivalent to a valid representation
+     * of 0 regardless of the sign bit, there are exceptions, so bignum
+     * functions and external callers must always set \c s to +1 for the
+     * number zero.
+     *
+     * Note that this implies that calloc() or `... = {0}` does not create
+     * a valid MPI representation. You must call mbedtls_mpi_init().
+     */
+    int MBEDTLS_PRIVATE(s);
+
+    /** Total number of limbs in \c p.  */
+    size_t MBEDTLS_PRIVATE(n);
+
+    /** Pointer to limbs.
+     *
+     * This may be \c NULL if \c n is 0.
+     */
+    mbedtls_mpi_uint *MBEDTLS_PRIVATE(p);
 }
 mbedtls_mpi;
 
diff --git a/library/bignum.c b/library/bignum.c
index 521787d..42be815 100644
--- a/library/bignum.c
+++ b/library/bignum.c
@@ -972,10 +972,12 @@
     return( ret );
 }
 
-/*
- * Signed addition: X = A + B
+/* Common function for signed addition and subtraction.
+ * Calculate A + B * flip_B where flip_B is 1 or -1.
  */
-int mbedtls_mpi_add_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B )
+static int add_sub_mpi( mbedtls_mpi *X,
+                        const mbedtls_mpi *A, const mbedtls_mpi *B,
+                        int flip_B )
 {
     int ret, s;
     MPI_VALIDATE_RET( X != NULL );
@@ -983,16 +985,21 @@
     MPI_VALIDATE_RET( B != NULL );
 
     s = A->s;
-    if( A->s * B->s < 0 )
+    if( A->s * B->s * flip_B < 0 )
     {
-        if( mbedtls_mpi_cmp_abs( A, B ) >= 0 )
+        int cmp = mbedtls_mpi_cmp_abs( A, B );
+        if( cmp >= 0 )
         {
             MBEDTLS_MPI_CHK( mbedtls_mpi_sub_abs( X, A, B ) );
-            X->s =  s;
+            /* If |A| = |B|, the result is 0 and we must set the sign bit
+             * to +1 regardless of which of A or B was negative. Otherwise,
+             * since |A| > |B|, the sign is the sign of A. */
+            X->s = cmp == 0 ? 1 : s;
         }
         else
         {
             MBEDTLS_MPI_CHK( mbedtls_mpi_sub_abs( X, B, A ) );
+            /* Since |A| < |B|, the sign is the opposite of A. */
             X->s = -s;
         }
     }
@@ -1008,38 +1015,19 @@
 }
 
 /*
+ * Signed addition: X = A + B
+ */
+int mbedtls_mpi_add_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B )
+{
+    return( add_sub_mpi( X, A, B, 1 ) );
+}
+
+/*
  * Signed subtraction: X = A - B
  */
 int mbedtls_mpi_sub_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B )
 {
-    int ret, s;
-    MPI_VALIDATE_RET( X != NULL );
-    MPI_VALIDATE_RET( A != NULL );
-    MPI_VALIDATE_RET( B != NULL );
-
-    s = A->s;
-    if( A->s * B->s > 0 )
-    {
-        if( mbedtls_mpi_cmp_abs( A, B ) >= 0 )
-        {
-            MBEDTLS_MPI_CHK( mbedtls_mpi_sub_abs( X, A, B ) );
-            X->s =  s;
-        }
-        else
-        {
-            MBEDTLS_MPI_CHK( mbedtls_mpi_sub_abs( X, B, A ) );
-            X->s = -s;
-        }
-    }
-    else
-    {
-        MBEDTLS_MPI_CHK( mbedtls_mpi_add_abs( X, A, B ) );
-        X->s = s;
-    }
-
-cleanup:
-
-    return( ret );
+    return( add_sub_mpi( X, A, B, -1 ) );
 }
 
 /*
diff --git a/library/bignum_mod_raw.c b/library/bignum_mod_raw.c
index a329e86..b43add7 100644
--- a/library/bignum_mod_raw.c
+++ b/library/bignum_mod_raw.c
@@ -127,7 +127,40 @@
 /* END MERGE SLOT 6 */
 
 /* BEGIN MERGE SLOT 7 */
+int mbedtls_mpi_mod_raw_to_mont_rep( mbedtls_mpi_uint *X,
+                                     const mbedtls_mpi_mod_modulus *m )
+{
+    mbedtls_mpi_uint *T;
+    const size_t t_limbs = m->limbs * 2 + 1;
 
+    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_platform_zeroize( T, t_limbs * ciL );
+    mbedtls_free( T );
+    return( 0 );
+}
+
+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;
+    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_platform_zeroize( T, t_limbs * ciL );
+    mbedtls_free( T );
+    return( 0 );
+}
 /* END MERGE SLOT 7 */
 
 /* BEGIN MERGE SLOT 8 */
diff --git a/library/bignum_mod_raw.h b/library/bignum_mod_raw.h
index 30648d3..f738e91 100644
--- a/library/bignum_mod_raw.h
+++ b/library/bignum_mod_raw.h
@@ -163,7 +163,29 @@
 /* END MERGE SLOT 6 */
 
 /* BEGIN MERGE SLOT 7 */
+/** Convert an MPI into Montgomery form.
+ *
+ * \param X      The address of the MPI.
+ *               Must have the same number of limbs as \p m.
+ * \param m      The address of the modulus, which gives the size of
+ *               the base `R` = 2^(biL*m->limbs).
+ *
+ * \return       \c 0 if successful.
+ */
+int mbedtls_mpi_mod_raw_to_mont_rep( mbedtls_mpi_uint *X,
+                                     const mbedtls_mpi_mod_modulus *m );
 
+/** Convert an MPI back from Montgomery representation.
+ *
+ * \param X      The address of the MPI.
+ *               Must have the same number of limbs as \p m.
+ * \param m      The address of the modulus, which gives the size of
+ *               the base `R`= 2^(biL*m->limbs).
+ *
+ * \return       \c 0 if successful.
+ */
+int mbedtls_mpi_mod_raw_from_mont_rep( mbedtls_mpi_uint *X,
+                                       const mbedtls_mpi_mod_modulus *m );
 /* END MERGE SLOT 7 */
 
 /* BEGIN MERGE SLOT 8 */
diff --git a/programs/pkey/dh_genprime.c b/programs/pkey/dh_genprime.c
index 2e696e5..331838b 100644
--- a/programs/pkey/dh_genprime.c
+++ b/programs/pkey/dh_genprime.c
@@ -157,8 +157,8 @@
         goto exit;
     }
 
-    if( ( ret = mbedtls_mpi_write_file( "P = ", &P, 16, fout ) != 0 ) ||
-        ( ret = mbedtls_mpi_write_file( "G = ", &G, 16, fout ) != 0 ) )
+    if( ( ( ret = mbedtls_mpi_write_file( "P = ", &P, 16, fout ) ) != 0 ) ||
+        ( ( ret = mbedtls_mpi_write_file( "G = ", &G, 16, fout ) ) != 0 ) )
     {
         mbedtls_printf( " failed\n  ! mbedtls_mpi_write_file returned %d\n\n", ret );
         fclose( fout );
diff --git a/scripts/mbedtls_dev/bignum_common.py b/scripts/mbedtls_dev/bignum_common.py
index 279668f..8b11bc2 100644
--- a/scripts/mbedtls_dev/bignum_common.py
+++ b/scripts/mbedtls_dev/bignum_common.py
@@ -14,9 +14,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import itertools
-import typing
-
 from abc import abstractmethod
 from typing import Iterator, List, Tuple, TypeVar
 
@@ -38,7 +35,13 @@
     raise ValueError("Not invertible")
 
 def hex_to_int(val: str) -> int:
-    return int(val, 16) if val else 0
+    """Implement the syntax accepted by mbedtls_test_read_mpi().
+
+    This is a superset of what is accepted by mbedtls_test_read_mpi_core().
+    """
+    if val in ['', '-']:
+        return 0
+    return int(val, 16)
 
 def quote_str(val) -> str:
     return "\"{}\"".format(val)
@@ -57,15 +60,8 @@
     return (val.bit_length() + bits_in_limb - 1) // bits_in_limb
 
 def combination_pairs(values: List[T]) -> List[Tuple[T, T]]:
-    """Return all pair combinations from input values.
-
-    The return value is cast, as older versions of mypy are unable to derive
-    the specific type returned by itertools.combinations_with_replacement.
-    """
-    return typing.cast(
-        List[Tuple[T, T]],
-        list(itertools.combinations_with_replacement(values, 2))
-    )
+    """Return all pair combinations from input values."""
+    return [(x, y) for x in values for y in values]
 
 
 class OperationCommon:
diff --git a/scripts/mbedtls_dev/bignum_mod_raw.py b/scripts/mbedtls_dev/bignum_mod_raw.py
index 1465e3e..bd694a6 100644
--- a/scripts/mbedtls_dev/bignum_mod_raw.py
+++ b/scripts/mbedtls_dev/bignum_mod_raw.py
@@ -122,7 +122,126 @@
 # END MERGE SLOT 6
 
 # BEGIN MERGE SLOT 7
+class BignumModRawConvertToMont(BignumModRawOperationArchSplit):
+    """ Test cases for mpi_mod_raw_to_mont_rep(). """
 
+    test_function = "mpi_mod_raw_to_mont_rep"
+    test_name = "Convert into Mont: "
+
+    test_data_moduli = ["b",
+                        "fd",
+                        "eeff99aa37",
+                        "eeff99aa11",
+                        "800000000005",
+                        "7fffffffffffffff",
+                        "80fe000a10000001",
+                        "25a55a46e5da99c71c7",
+                        "1058ad82120c3a10196bb36229c1",
+                        "7e35b84cb19ea5bc57ec37f5e431462fa962d98c1e63738d4657f"
+                        "18ad6532e6adc3eafe67f1e5fa262af94cee8d3e7268593942a2a"
+                        "98df75154f8c914a282f8b",
+                        "8335616aed761f1f7f44e6bd49e807b82e3bf2bf11bfa63",
+                        "ffcece570f2f991013f26dd5b03c4c5b65f97be5905f36cb4664f"
+                        "2c78ff80aa8135a4aaf57ccb8a0aca2f394909a74cef1ef6758a6"
+                        "4d11e2c149c393659d124bfc94196f0ce88f7d7d567efa5a649e2"
+                        "deefaa6e10fdc3deac60d606bf63fc540ac95294347031aefd73d"
+                        "6a9ee10188aaeb7a90d920894553cb196881691cadc51808715a0"
+                        "7e8b24fcb1a63df047c7cdf084dd177ba368c806f3d51ddb5d389"
+                        "8c863e687ecaf7d649a57a46264a582f94d3c8f2edaf59f77a7f6"
+                        "bdaf83c991e8f06abe220ec8507386fce8c3da84c6c3903ab8f3a"
+                        "d4630a204196a7dbcbd9bcca4e40ec5cc5c09938d49f5e1e6181d"
+                        "b8896f33bb12e6ef73f12ec5c5ea7a8a337"
+                        ]
+
+    test_input_numbers = ["0",
+                          "1",
+                          "97",
+                          "f5",
+                          "6f5c3",
+                          "745bfe50f7",
+                          "ffa1f9924123",
+                          "334a8b983c79bd",
+                          "5b84f632b58f3461",
+                          "19acd15bc38008e1",
+                          "ffffffffffffffff",
+                          "54ce6a6bb8247fa0427cfc75a6b0599",
+                          "fecafe8eca052f154ce6a6bb8247fa019558bfeecce9bb9",
+                          "a87d7a56fa4bfdc7da42ef798b9cf6843d4c54794698cb14d72"
+                          "851dec9586a319f4bb6d5695acbd7c92e7a42a5ede6972adcbc"
+                          "f68425265887f2d721f462b7f1b91531bac29fa648facb8e3c6"
+                          "1bd5ae42d5a59ba1c89a95897bfe541a8ce1d633b98f379c481"
+                          "6f25e21f6ac49286b261adb4b78274fe5f61c187581f213e84b"
+                          "2a821e341ef956ecd5de89e6c1a35418cd74a549379d2d4594a"
+                          "577543147f8e35b3514e62cf3e89d1156cdc91ab5f4c928fbd6"
+                          "9148c35df5962fed381f4d8a62852a36823d5425f7487c13a12"
+                          "523473fb823aa9d6ea5f42e794e15f2c1a8785cf6b7d51a4617"
+                          "947fb3baf674f74a673cf1d38126983a19ed52c7439fab42c2185"
+                          ]
+
+    descr_tpl = '{} #{} N: \"{}\" A: \"{}\".'
+
+    def result(self) -> List[str]:
+        return [self.hex_x]
+
+    def arguments(self) -> List[str]:
+        return [bignum_common.quote_str(n) for n in [self.hex_n,
+                                                     self.hex_a,
+                                                     self.hex_x]]
+
+    def description(self) -> str:
+        return self.descr_tpl.format(self.test_name,
+                                     self.count,
+                                     self.int_n,
+                                     self.int_a)
+
+    @classmethod
+    def generate_function_tests(cls) -> Iterator[test_case.TestCase]:
+        for bil in [32, 64]:
+            for n in cls.test_data_moduli:
+                for i in cls.test_input_numbers:
+                    # Skip invalid combinations where A.limbs > N.limbs
+                    if bignum_common.hex_to_int(i) > bignum_common.hex_to_int(n):
+                        continue
+                    yield cls(n, i, bits_in_limb=bil).create_test_case()
+
+    @property
+    def x(self) -> int: # pylint: disable=invalid-name
+        return (self.int_a * self.r) % self.int_n
+
+    @property
+    def hex_x(self) -> str:
+        return "{:x}".format(self.x).zfill(self.hex_digits)
+
+class BignumModRawConvertFromMont(BignumModRawConvertToMont):
+    """ Test cases for mpi_mod_raw_from_mont_rep(). """
+
+    test_function = "mpi_mod_raw_from_mont_rep"
+    test_name = "Convert from Mont: "
+
+    test_input_numbers = ["0",
+                          "1",
+                          "3ca",
+                          "539ed428",
+                          "7dfe5c6beb35a2d6",
+                          "dca8de1c2adfc6d7aafb9b48e",
+                          "a7d17b6c4be72f3d5c16bf9c1af6fc933",
+                          "2fec97beec546f9553142ed52f147845463f579",
+                          "378dc83b8bc5a7b62cba495af4919578dce6d4f175cadc4f",
+                          "b6415f2a1a8e48a518345db11f56db3829c8f2c6415ab4a395a"
+                          "b3ac2ea4cbef4af86eb18a84eb6ded4c6ecbfc4b59c2879a675"
+                          "487f687adea9d197a84a5242a5cf6125ce19a6ad2e7341f1c57"
+                          "d43ea4f4c852a51cb63dabcd1c9de2b827a3146a3d175b35bea"
+                          "41ae75d2a286a3e9d43623152ac513dcdea1d72a7da846a8ab3"
+                          "58d9be4926c79cfb287cf1cf25b689de3b912176be5dcaf4d4c"
+                          "6e7cb839a4a3243a6c47c1e2c99d65c59d6fa3672575c2f1ca8"
+                          "de6a32e854ec9d8ec635c96af7679fce26d7d159e4a9da3bd74"
+                          "e1272c376cd926d74fe3fb164a5935cff3d5cdb92b35fe2cea32"
+                          "138a7e6bfbc319ebd1725dacb9a359cbf693f2ecb785efb9d627"
+                         ]
+
+    @property
+    def x(self): # pylint: disable=invalid-name
+        return (self.int_a * self.r_inv) % self.int_n
 # END MERGE SLOT 7
 
 # BEGIN MERGE SLOT 8
diff --git a/tests/data_files/Makefile b/tests/data_files/Makefile
index 09a0689..d4f2011 100644
--- a/tests/data_files/Makefile
+++ b/tests/data_files/Makefile
@@ -884,6 +884,11 @@
 	$(MBEDTLS_CERT_REQ) output_file=$@ filename=$< subject_name="C=NL,O=PolarSSL,CN=PolarSSL Server 1" md=SHA256
 all_final += server1.req.sha256
 
+server1.req.sha256.ext: server1.key
+	# Generating this with OpenSSL as a comparison point to test we're getting the same result
+	openssl req -new -out $@ -key $< -subj '/C=NL/O=PolarSSL/CN=PolarSSL Server 1' -sha256 -addext "extendedKeyUsage=serverAuth"
+all_final += server1.req.sha256.ext
+
 server1.req.sha384: server1.key
 	$(MBEDTLS_CERT_REQ) output_file=$@ filename=$< subject_name="C=NL,O=PolarSSL,CN=PolarSSL Server 1" md=SHA384
 all_final += server1.req.sha384
diff --git a/tests/data_files/server1.req.sha256.ext b/tests/data_files/server1.req.sha256.ext
new file mode 100644
index 0000000..3f26f09
--- /dev/null
+++ b/tests/data_files/server1.req.sha256.ext
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICpzCCAY8CAQAwPDELMAkGA1UEBhMCTkwxETAPBgNVBAoMCFBvbGFyU1NMMRow
+GAYDVQQDDBFQb2xhclNTTCBTZXJ2ZXIgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAKkCHz1AatVVU4v9Nu6CZS4VYV6Jv7joRZDb7ogWUtPxQ1BHlhJZ
+ZIdr/SvgRvlzvt3PkuGRW+1moG+JKXlFgNCDatVBQ3dfOXwJBEeCsFc5cO2j7BUZ
+HqgzCEfBBUKp/UzDtN/dBh9NEFFAZ3MTD0D4bYElXwqxU8YwfhU5rPla7n+SnqYF
+W+cTl4W1I5LZ1CQG1QkliXUH3aYajz8JGb6tZSxk65Wb3P5BXhem2mxbacwCuhQs
+FiScStzN0PdSZ3PxLaAj/X70McotcMqJCwTbLqZPcG6ezr1YieJTWZ5uWpJl4og/
+DJQZo93l6J2VE+0p26twEtxaymsXq1KCVLECAwEAAaAmMCQGCSqGSIb3DQEJDjEX
+MBUwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggEBAHi0yEGu
+Fh5tuLiLuT95UrRnly55+lTY9xchFiKtlcoEdSheybYxqk3JHuSSqojOFKZBlRdk
+oG6Azg56/aMHPWyvtCMSRQX4b+FgjeQsm9IfhYNMquQOxyPxm62vjuU3MfZIofXH
+hKdI6Ci2CDF4Fyvw50KBWniV38eE9+kjsvDLdXD3ESZJGhjjuFl8ReUiA2wdBTcP
+XEZaXUIc6B4tUnlPeqn/2zp4GBqqWzNZx6TXBpApASGG3BEJnM52FVPC7E9p+8YZ
+qIGuiF5Cz/rYZkpwffBWIfS2zZakHLm5TB8FgZkWlyReJU9Ihk2Tl/sZ1kllFdYa
+xLPnLCL82KFL1Co=
+-----END CERTIFICATE REQUEST-----
diff --git a/tests/include/test/helpers.h b/tests/include/test/helpers.h
index e0e6fd2..5f9bde6 100644
--- a/tests/include/test/helpers.h
+++ b/tests/include/test/helpers.h
@@ -295,13 +295,19 @@
 
 /** Read an MPI from a hexadecimal string.
  *
- * Like mbedtls_mpi_read_string(), but size the resulting bignum based
- * on the number of digits in the string. In particular, construct 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.
+ * Like mbedtls_mpi_read_string(), but with tighter guarantees around
+ * edge cases.
  *
- * This is important so that the "0 (null)" and "0 (1 limb)" and
- * "leading zeros" test cases do what they claim.
+ * - 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.
@@ -309,6 +315,14 @@
  * \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/generate_bignum_tests.py b/tests/scripts/generate_bignum_tests.py
index a105203..eee2f65 100755
--- a/tests/scripts/generate_bignum_tests.py
+++ b/tests/scripts/generate_bignum_tests.py
@@ -78,11 +78,17 @@
     #pylint: disable=abstract-method
     """Common features for bignum operations in legacy tests."""
     input_values = [
-        "", "0", "7b", "-7b",
+        "", "0", "-", "-0",
+        "7b", "-7b",
         "0000000000000000123", "-0000000000000000123",
         "1230000000000000000", "-1230000000000000000"
     ]
 
+    def description_suffix(self) -> str:
+        #pylint: disable=no-self-use # derived classes need self
+        """Text to add at the end of the test case description."""
+        return ""
+
     def description(self) -> str:
         """Generate a description for the test case.
 
@@ -96,6 +102,9 @@
                 self.symbol,
                 self.value_description(self.arg_b)
             )
+            description_suffix = self.description_suffix()
+            if description_suffix:
+                self.case_description += " " + description_suffix
         return super().description()
 
     @staticmethod
@@ -107,6 +116,8 @@
         """
         if val == "":
             return "0 (null)"
+        if val == "-":
+            return "negative 0 (null)"
         if val == "0":
             return "0 (1 limb)"
 
@@ -171,9 +182,21 @@
         ]
     )
 
-    def result(self) -> List[str]:
-        return [bignum_common.quote_str("{:x}").format(self.int_a + self.int_b)]
+    def __init__(self, val_a: str, val_b: str) -> None:
+        super().__init__(val_a, val_b)
+        self._result = self.int_a + self.int_b
 
+    def description_suffix(self) -> str:
+        if (self.int_a >= 0 and self.int_b >= 0):
+            return "" # obviously positive result or 0
+        if (self.int_a <= 0 and self.int_b <= 0):
+            return "" # obviously negative result or 0
+        # The sign of the result is not obvious, so indicate it
+        return ", result{}0".format('>' if self._result > 0 else
+                                    '<' if self._result < 0 else '=')
+
+    def result(self) -> List[str]:
+        return [bignum_common.quote_str("{:x}".format(self._result))]
 
 if __name__ == '__main__':
     # Use the section of the docstring relevant to the CLI as description
diff --git a/tests/src/helpers.c b/tests/src/helpers.c
index cc23fd7..7c83714 100644
--- a/tests/src/helpers.c
+++ b/tests/src/helpers.c
@@ -89,6 +89,10 @@
     mbedtls_test_info.step = step;
 }
 
+#if defined(MBEDTLS_BIGNUM_C)
+unsigned mbedtls_test_case_uses_negative_0 = 0;
+#endif
+
 void mbedtls_test_info_reset( void )
 {
     mbedtls_test_info.result = MBEDTLS_TEST_RESULT_SUCCESS;
@@ -98,6 +102,9 @@
     mbedtls_test_info.filename = 0;
     memset( mbedtls_test_info.line1, 0, sizeof( mbedtls_test_info.line1 ) );
     memset( mbedtls_test_info.line2, 0, sizeof( mbedtls_test_info.line2 ) );
+#if defined(MBEDTLS_BIGNUM_C)
+    mbedtls_test_case_uses_negative_0 = 0;
+#endif
 }
 
 int mbedtls_test_equal( const char *test, int line_no, const char* filename,
@@ -396,6 +403,15 @@
 
 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 )
@@ -403,7 +419,15 @@
         mbedtls_mpi_free( X );
         return( 0 );
     }
-    else
-        return( mbedtls_mpi_read_string( X, 16, s ) );
+    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/test_suite_bignum.function b/tests/suites/test_suite_bignum.function
index 4cec0a7..b75f534 100644
--- a/tests/suites/test_suite_bignum.function
+++ b/tests/suites/test_suite_bignum.function
@@ -13,10 +13,21 @@
  * constructing the value. */
 static int sign_is_valid( const mbedtls_mpi *X )
 {
+    /* Only +1 and -1 are valid sign bits, not e.g. 0 */
     if( X->s != 1 && X->s != -1 )
-        return( 0 ); // invalid sign bit, e.g. 0
-    if( mbedtls_mpi_bitlen( X ) == 0 && X->s != 1 )
-        return( 0 ); // negative zero
+        return( 0 );
+
+    /* The value 0 must be represented with the sign +1. A "negative zero"
+     * with s=-1 is an invalid representation. Forbid that. As an exception,
+     * we sometimes test the robustness of library functions when given
+     * a negative zero input. If a test case has a negative zero as input,
+     * we don't mind if the function has a negative zero output. */
+    if( ! mbedtls_test_case_uses_negative_0 &&
+        mbedtls_mpi_bitlen( X ) == 0 && X->s != 1 )
+    {
+        return( 0 );
+    }
+
     return( 1 );
 }
 
@@ -959,24 +970,57 @@
 /* END_CASE */
 
 /* BEGIN_CASE */
-void mpi_mod_int( char * input_X, int input_Y,
-                  int input_A, int div_result )
+void mpi_mod_int( char * input_X, char * input_Y,
+                  char * input_A, int mod_result )
 {
     mbedtls_mpi X;
+    mbedtls_mpi Y;
+    mbedtls_mpi A;
     int res;
     mbedtls_mpi_uint r;
-    mbedtls_mpi_init( &X );
 
-    TEST_ASSERT( mbedtls_test_read_mpi( &X, input_X ) == 0 );
-    res = mbedtls_mpi_mod_int( &r, &X, input_Y );
-    TEST_ASSERT( res == div_result );
+    mbedtls_mpi_init( &X );
+    mbedtls_mpi_init( &Y );
+    mbedtls_mpi_init( &A );
+
+    /* We use MPIs to read Y and A since the test framework limits us to
+     * ints, so we can't have 64-bit values */
+    TEST_EQUAL( mbedtls_test_read_mpi( &X, input_X ), 0 );
+    TEST_EQUAL( mbedtls_test_read_mpi( &Y, input_Y ), 0 );
+    TEST_EQUAL( mbedtls_test_read_mpi( &A, input_A ), 0 );
+
+    TEST_EQUAL( Y.n, 1 );
+    TEST_EQUAL( A.n, 1 );
+
+    /* Convert the MPIs for Y and A to (signed) mbedtls_mpi_sints */
+
+    /* Since we're converting sign+magnitude to two's complement, we lose one
+     * bit of value in the output. This means there are some values we can't
+     * represent, e.g. (hex) -A0000000 on 32-bit systems. These are technically
+     * invalid test cases, so could be considered "won't happen", but they are
+     * easy to test for, and this helps guard against human error. */
+
+    mbedtls_mpi_sint y = (mbedtls_mpi_sint) Y.p[0];
+    TEST_ASSERT( y >= 0 );      /* If y < 0 here, we can't make negative y */
+    if( Y.s == -1 )
+        y = -y;
+
+    mbedtls_mpi_sint a = (mbedtls_mpi_sint) A.p[0];
+    TEST_ASSERT( a >= 0 );      /* Same goes for a */
+    if( A.s == -1 )
+        a = -a;
+
+    res = mbedtls_mpi_mod_int( &r, &X, y );
+    TEST_EQUAL( res, mod_result );
     if( res == 0 )
     {
-        TEST_ASSERT( r == (mbedtls_mpi_uint) input_A );
+        TEST_EQUAL( r, a );
     }
 
 exit:
     mbedtls_mpi_free( &X );
+    mbedtls_mpi_free( &Y );
+    mbedtls_mpi_free( &A );
 }
 /* END_CASE */
 
diff --git a/tests/suites/test_suite_bignum.misc.data b/tests/suites/test_suite_bignum.misc.data
index 29ba4ab..818f361 100644
--- a/tests/suites/test_suite_bignum.misc.data
+++ b/tests/suites/test_suite_bignum.misc.data
@@ -1144,6 +1144,18 @@
 Test mbedtls_mpi_div_mpi: 0 (null) / -1
 mpi_div_mpi:"":"-1":"":"":0
 
+Test mbedtls_mpi_div_mpi: -0 (null) / 1
+mpi_div_mpi:"-":"1":"":"":0
+
+Test mbedtls_mpi_div_mpi: -0 (null) / -1
+mpi_div_mpi:"-":"-1":"":"":0
+
+Test mbedtls_mpi_div_mpi: -0 (null) / 42
+mpi_div_mpi:"-":"2a":"":"":0
+
+Test mbedtls_mpi_div_mpi: -0 (null) / -42
+mpi_div_mpi:"-":"-2a":"":"":0
+
 Test mbedtls_mpi_div_mpi #1
 mpi_div_mpi:"9e22d6da18a33d1ef28d2a82242b3f6e9c9742f63e5d440f58a190bfaf23a7866e67589adb80":"22":"4a6abf75b13dc268ea9cc8b5b6aaf0ac85ecd437a4e0987fb13cf8d2acc57c0306c738c1583":"1a":0
 
@@ -1204,41 +1216,85 @@
 Test mbedtls_mpi_mod_mpi: 0 (null) % -1
 mpi_mod_mpi:"":"-1":"":MBEDTLS_ERR_MPI_NEGATIVE_VALUE
 
+Test mbedtls_mpi_mod_mpi: -0 (null) % 1
+mpi_mod_mpi:"-":"1":"":0
+
+Test mbedtls_mpi_mod_mpi: -0 (null) % -1
+mpi_mod_mpi:"-":"-1":"":MBEDTLS_ERR_MPI_NEGATIVE_VALUE
+
+Test mbedtls_mpi_mod_mpi: -0 (null) % 42
+mpi_mod_mpi:"-":"2a":"":0
+
+Test mbedtls_mpi_mod_mpi: -0 (null) % -42
+mpi_mod_mpi:"-":"-2a":"":MBEDTLS_ERR_MPI_NEGATIVE_VALUE
+
 Base test mbedtls_mpi_mod_int #1
-mpi_mod_int:"3e8":13:12:0
+mpi_mod_int:"3e8":"d":"c":0
 
 Base test mbedtls_mpi_mod_int #2 (Divide by zero)
-mpi_mod_int:"3e8":0:0:MBEDTLS_ERR_MPI_DIVISION_BY_ZERO
+mpi_mod_int:"3e8":"0":"0":MBEDTLS_ERR_MPI_DIVISION_BY_ZERO
 
 Base test mbedtls_mpi_mod_int #3
-mpi_mod_int:"-3e8":13:1:0
+mpi_mod_int:"-3e8":"d":"1":0
 
 Base test mbedtls_mpi_mod_int #4 (Negative modulo)
-mpi_mod_int:"3e8":-13:0:MBEDTLS_ERR_MPI_NEGATIVE_VALUE
+mpi_mod_int:"3e8":"-d":"0":MBEDTLS_ERR_MPI_NEGATIVE_VALUE
 
 Base test mbedtls_mpi_mod_int #5 (Negative modulo)
-mpi_mod_int:"-3e8":-13:0:MBEDTLS_ERR_MPI_NEGATIVE_VALUE
+mpi_mod_int:"-3e8":"-d":"0":MBEDTLS_ERR_MPI_NEGATIVE_VALUE
 
 Base test mbedtls_mpi_mod_int #6 (By 1)
-mpi_mod_int:"3e8":1:0:0
+mpi_mod_int:"3e8":"1":"0":0
 
 Base test mbedtls_mpi_mod_int #7 (By 2)
-mpi_mod_int:"3e9":2:1:0
+mpi_mod_int:"3e9":"2":"1":0
 
 Base test mbedtls_mpi_mod_int #8 (By 2)
-mpi_mod_int:"3e8":2:0:0
+mpi_mod_int:"3e8":"2":"0":0
 
 Test mbedtls_mpi_mod_int: 0 (null) % 1
-mpi_mod_int:"":1:0:0
+mpi_mod_int:"":"1":"0":0
 
 Test mbedtls_mpi_mod_int: 0 (null) % 2
-mpi_mod_int:"":2:0:0
+mpi_mod_int:"":"2":"0":0
 
 Test mbedtls_mpi_mod_int: 0 (null) % -1
-mpi_mod_int:"":-1:0:MBEDTLS_ERR_MPI_NEGATIVE_VALUE
+mpi_mod_int:"":"-1":"0":MBEDTLS_ERR_MPI_NEGATIVE_VALUE
 
 Test mbedtls_mpi_mod_int: 0 (null) % -2
-mpi_mod_int:"":-2:0:MBEDTLS_ERR_MPI_NEGATIVE_VALUE
+mpi_mod_int:"":"-2":"0":MBEDTLS_ERR_MPI_NEGATIVE_VALUE
+
+# CURRENTLY FAILS - SEE GITHUB ISSUE #6540
+#Test mbedtls_mpi_mod_int: 230772460340063000000100500000300000010 % 5178236083361335880 -> 3386266129388798810
+#depends_on:MBEDTLS_HAVE_INT64
+#mpi_mod_int:"AD9D28BF6C4E98FDF156BF0980CEE30A":"47DCCA4847DCCA48":"2EFE6F1A7D28035A":0
+
+Test mbedtls_mpi_mod_mpi: 230772460340063000000100500000300000010 % 5178236083361335880 -> 3386266129388798810
+mpi_mod_mpi:"AD9D28BF6C4E98FDF156BF0980CEE30A":"47DCCA4847DCCA48":"2EFE6F1A7D28035A":0
+
+# CURRENTLY FAILS - SEE GITHUB ISSUE #6540
+#Test mbedtls_mpi_mod_int: 230772460340062999996714233870911201200 % 5178236083361335880 -> 0
+#depends_on:MBEDTLS_HAVE_INT64
+#mpi_mod_int:"AD9D28BF6C4E98FDC2584FEF03A6DFB0":"47DCCA4847DCCA48":"0":0
+
+Test mbedtls_mpi_mod_mpi: 230772460340062999996714233870911201200 % 5178236083361335880 -> 0
+mpi_mod_mpi:"AD9D28BF6C4E98FDC2584FEF03A6DFB0":"47DCCA4847DCCA48":"0":0
+
+# CURRENTLY FAILS WHEN MPIS ARE 32-BIT (ISSUE #6450): WHEN FIXED, REMOVE "depends_on" LINE
+Test mbedtls_mpi_mod_int: 230772460340063000000100500000300000010 % 1205652040 -> 3644370
+depends_on:MBEDTLS_HAVE_INT64
+mpi_mod_int:"AD9D28BF6C4E98FDF156BF0980CEE30A":"47DCCA48":"379BD2":0
+
+Test mbedtls_mpi_mod_mpi: 230772460340063000000100500000300000010 % 1205652040 -> 3644370
+mpi_mod_mpi:"AD9D28BF6C4E98FDF156BF0980CEE30A":"47DCCA48":"379BD2":0
+
+# CURRENTLY FAILS WHEN MPIS ARE 32-BIT (ISSUE #6450): WHEN FIXED, REMOVE "depends_on" LINE
+Test mbedtls_mpi_mod_int: 230772460340063000000100500000296355640 % 1205652040 -> 0
+depends_on:MBEDTLS_HAVE_INT64
+mpi_mod_int:"AD9D28BF6C4E98FDF156BF0980974738":"47DCCA48":"0":0
+
+Test mbedtls_mpi_mod_mpi: 230772460340063000000100500000296355640 % 1205652040 -> 0
+mpi_mod_mpi:"AD9D28BF6C4E98FDF156BF0980974738":"47DCCA48":"0":0
 
 Base test mbedtls_mpi_exp_mod #1
 mpi_exp_mod:"17":"d":"1d":"18":0
diff --git a/tests/suites/test_suite_bignum_mod_raw.function b/tests/suites/test_suite_bignum_mod_raw.function
index 4b90675..ff766b9 100644
--- a/tests/suites/test_suite_bignum_mod_raw.function
+++ b/tests/suites/test_suite_bignum_mod_raw.function
@@ -294,7 +294,77 @@
 /* END MERGE SLOT 6 */
 
 /* BEGIN MERGE SLOT 7 */
+/* BEGIN_CASE */
+void mpi_mod_raw_to_mont_rep( char * input_N, char * input_A, char * input_X )
+{
+    mbedtls_mpi_uint *N = NULL;
+    mbedtls_mpi_uint *A = NULL;
+    mbedtls_mpi_uint *X = NULL;
+    size_t n_limbs, a_limbs, x_limbs, x_bytes;
 
+    mbedtls_mpi_mod_modulus m;
+    mbedtls_mpi_mod_modulus_init( &m );
+
+    /* Read inputs */
+    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);
+
+    TEST_EQUAL( 0, mbedtls_mpi_mod_modulus_setup( &m, N, n_limbs,
+                MBEDTLS_MPI_MOD_EXT_REP_BE, MBEDTLS_MPI_MOD_REP_MONTGOMERY ) );
+
+    /* Convert from cannonical into Montgomery representation */
+    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 );
+exit:
+    mbedtls_mpi_mod_modulus_free( &m );
+    mbedtls_free( N );
+    mbedtls_free( A );
+    mbedtls_free( X );
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void mpi_mod_raw_from_mont_rep( char * input_N, char * input_A, char * input_X )
+{
+    mbedtls_mpi_uint *N = NULL;
+    mbedtls_mpi_uint *A = NULL;
+    mbedtls_mpi_uint *X = NULL;
+    size_t n_limbs, a_limbs, x_limbs, x_bytes;
+
+    mbedtls_mpi_mod_modulus m;
+    mbedtls_mpi_mod_modulus_init( &m );
+
+    /* Read inputs */
+    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);
+
+    TEST_EQUAL( 0, mbedtls_mpi_mod_modulus_setup( &m, N, n_limbs,
+                MBEDTLS_MPI_MOD_EXT_REP_BE, MBEDTLS_MPI_MOD_REP_MONTGOMERY ) );
+
+    /* Convert from Montgomery into cannonical representation */
+    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 );
+exit:
+    mbedtls_mpi_mod_modulus_free( &m );
+    mbedtls_free( N );
+    mbedtls_free( A );
+    mbedtls_free( X );
+}
+/* END_CASE */
 /* END MERGE SLOT 7 */
 
 /* BEGIN MERGE SLOT 8 */
diff --git a/tests/suites/test_suite_x509write.data b/tests/suites/test_suite_x509write.data
index 8411557..c55c9d1 100644
--- a/tests/suites/test_suite_x509write.data
+++ b/tests/suites/test_suite_x509write.data
@@ -1,30 +1,30 @@
 Certificate Request check Server1 SHA1
 depends_on:MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15
-x509_csr_check:"data_files/server1.key":"data_files/server1.req.sha1":MBEDTLS_MD_SHA1:0:0:0:0
+x509_csr_check:"data_files/server1.key":"data_files/server1.req.sha1":MBEDTLS_MD_SHA1:0:0:0:0:0
 
 Certificate Request check Server1 SHA224
 depends_on:MBEDTLS_HAS_ALG_SHA_224_VIA_MD_OR_PSA_BASED_ON_USE_PSA:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15
-x509_csr_check:"data_files/server1.key":"data_files/server1.req.sha224":MBEDTLS_MD_SHA224:0:0:0:0
+x509_csr_check:"data_files/server1.key":"data_files/server1.req.sha224":MBEDTLS_MD_SHA224:0:0:0:0:0
 
 Certificate Request check Server1 SHA256
 depends_on:MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15
-x509_csr_check:"data_files/server1.key":"data_files/server1.req.sha256":MBEDTLS_MD_SHA256:0:0:0:0
+x509_csr_check:"data_files/server1.key":"data_files/server1.req.sha256":MBEDTLS_MD_SHA256:0:0:0:0:0
 
 Certificate Request check Server1 SHA384
 depends_on:MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15
-x509_csr_check:"data_files/server1.key":"data_files/server1.req.sha384":MBEDTLS_MD_SHA384:0:0:0:0
+x509_csr_check:"data_files/server1.key":"data_files/server1.req.sha384":MBEDTLS_MD_SHA384:0:0:0:0:0
 
 Certificate Request check Server1 SHA512
 depends_on:MBEDTLS_HAS_ALG_SHA_512_VIA_MD_OR_PSA_BASED_ON_USE_PSA:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15
-x509_csr_check:"data_files/server1.key":"data_files/server1.req.sha512":MBEDTLS_MD_SHA512:0:0:0:0
+x509_csr_check:"data_files/server1.key":"data_files/server1.req.sha512":MBEDTLS_MD_SHA512:0:0:0:0:0
 
 Certificate Request check Server1 MD5
 depends_on:MBEDTLS_HAS_ALG_MD5_VIA_MD_OR_PSA_BASED_ON_USE_PSA:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15
-x509_csr_check:"data_files/server1.key":"data_files/server1.req.md5":MBEDTLS_MD_MD5:0:0:0:0
+x509_csr_check:"data_files/server1.key":"data_files/server1.req.md5":MBEDTLS_MD_MD5:0:0:0:0:0
 
 Certificate Request check Server1 key_usage
 depends_on:MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15
-x509_csr_check:"data_files/server1.key":"data_files/server1.req.key_usage":MBEDTLS_MD_SHA1:MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_NON_REPUDIATION | MBEDTLS_X509_KU_KEY_ENCIPHERMENT:1:0:0
+x509_csr_check:"data_files/server1.key":"data_files/server1.req.key_usage":MBEDTLS_MD_SHA1:MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_NON_REPUDIATION | MBEDTLS_X509_KU_KEY_ENCIPHERMENT:1:0:0:0
 
 Certificate Request check opaque Server1 key_usage
 depends_on:MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15
@@ -32,23 +32,27 @@
 
 Certificate Request check Server1 key_usage empty
 depends_on:MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15
-x509_csr_check:"data_files/server1.key":"data_files/server1.req.key_usage_empty":MBEDTLS_MD_SHA1:0:1:0:0
+x509_csr_check:"data_files/server1.key":"data_files/server1.req.key_usage_empty":MBEDTLS_MD_SHA1:0:1:0:0:0
 
 Certificate Request check Server1 ns_cert_type
 depends_on:MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15
-x509_csr_check:"data_files/server1.key":"data_files/server1.req.cert_type":MBEDTLS_MD_SHA1:0:0:MBEDTLS_X509_NS_CERT_TYPE_SSL_SERVER:1
+x509_csr_check:"data_files/server1.key":"data_files/server1.req.cert_type":MBEDTLS_MD_SHA1:0:0:MBEDTLS_X509_NS_CERT_TYPE_SSL_SERVER:1:0
 
 Certificate Request check Server1 ns_cert_type empty
 depends_on:MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15
-x509_csr_check:"data_files/server1.key":"data_files/server1.req.cert_type_empty":MBEDTLS_MD_SHA1:0:0:0:1
+x509_csr_check:"data_files/server1.key":"data_files/server1.req.cert_type_empty":MBEDTLS_MD_SHA1:0:0:0:1:0
 
 Certificate Request check Server1 key_usage + ns_cert_type
 depends_on:MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15
-x509_csr_check:"data_files/server1.key":"data_files/server1.req.ku-ct":MBEDTLS_MD_SHA1:MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_NON_REPUDIATION | MBEDTLS_X509_KU_KEY_ENCIPHERMENT:1:MBEDTLS_X509_NS_CERT_TYPE_SSL_SERVER:1
+x509_csr_check:"data_files/server1.key":"data_files/server1.req.ku-ct":MBEDTLS_MD_SHA1:MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_NON_REPUDIATION | MBEDTLS_X509_KU_KEY_ENCIPHERMENT:1:MBEDTLS_X509_NS_CERT_TYPE_SSL_SERVER:1:0
 
 Certificate Request check Server5 ECDSA, key_usage
 depends_on:MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA:MBEDTLS_ECDSA_C:MBEDTLS_ECDSA_DETERMINISTIC:MBEDTLS_ECP_DP_SECP256R1_ENABLED
-x509_csr_check:"data_files/server5.key":"data_files/server5.req.ku.sha1":MBEDTLS_MD_SHA1:MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_NON_REPUDIATION:1:0:0
+x509_csr_check:"data_files/server5.key":"data_files/server5.req.ku.sha1":MBEDTLS_MD_SHA1:MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_NON_REPUDIATION:1:0:0:0
+
+Certificate Request check Server1, set_extension
+depends_on:MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15
+x509_csr_check:"data_files/server1.key":"data_files/server1.req.sha256.ext":MBEDTLS_MD_SHA256:0:0:0:0:1
 
 Certificate Request check opaque Server5 ECDSA, key_usage
 depends_on:MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED
diff --git a/tests/suites/test_suite_x509write.function b/tests/suites/test_suite_x509write.function
index aa54072..5bd814a 100644
--- a/tests/suites/test_suite_x509write.function
+++ b/tests/suites/test_suite_x509write.function
@@ -5,6 +5,7 @@
 #include "mbedtls/pem.h"
 #include "mbedtls/oid.h"
 #include "mbedtls/rsa.h"
+#include "mbedtls/asn1write.h"
 
 #include "hash_info.h"
 #include "mbedtls/legacy_or_psa.h"
@@ -74,6 +75,56 @@
 }
 #endif /* MBEDTLS_USE_PSA_CRYPTO && MBEDTLS_PEM_WRITE_C && MBEDTLS_X509_CSR_WRITE_C */
 
+#if defined(MBEDTLS_X509_CSR_WRITE_C)
+
+/*
+ * The size of this temporary buffer is given by the sequence of functions
+ * called hereinafter:
+ * - mbedtls_asn1_write_oid()
+ *     - 8 bytes for MBEDTLS_OID_EXTENDED_KEY_USAGE raw value
+ *     - 1 byte for MBEDTLS_OID_EXTENDED_KEY_USAGE length
+ *     - 1 byte for MBEDTLS_ASN1_OID tag
+ * - mbedtls_asn1_write_len()
+ *     - 1 byte since we're dealing with sizes which are less than 0x80
+ * - mbedtls_asn1_write_tag()
+ *     - 1 byte
+ *
+ * This length is fine as long as this function is called using the
+ * MBEDTLS_OID_SERVER_AUTH OID. If this is changed in the future, then this
+ * buffer's length should be adjusted accordingly.
+ * Unfortunately there's no predefined max size for OIDs which can be used
+ * to set an overall upper boundary which is always guaranteed.
+ */
+#define EXT_KEY_USAGE_TMP_BUF_MAX_LENGTH    12
+
+static int csr_set_extended_key_usage( mbedtls_x509write_csr *ctx,
+                        const char *oid, size_t oid_len )
+{
+    unsigned char buf[EXT_KEY_USAGE_TMP_BUF_MAX_LENGTH] = { 0 };
+    unsigned char *p = buf + sizeof( buf );
+    int ret;
+    size_t len = 0;
+
+    /*
+     * Following functions fail anyway if the temporary buffer is not large,
+     * but we set an extra check here to emphasize a possible source of errors
+     */
+    if ( oid_len > EXT_KEY_USAGE_TMP_BUF_MAX_LENGTH )
+    {
+        return MBEDTLS_ERR_X509_BAD_INPUT_DATA;
+    }
+
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_oid( &p, buf, oid, oid_len ) );
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &p, buf, ret ) );
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &p, buf,
+                        MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) );
+
+    ret = mbedtls_x509write_csr_set_extension( ctx, MBEDTLS_OID_EXTENDED_KEY_USAGE,
+              MBEDTLS_OID_SIZE( MBEDTLS_OID_EXTENDED_KEY_USAGE ), 0, p, len );
+
+    return ret;
+}
+#endif  /* MBEDTLS_X509_CSR_WRITE_C */
 /* END_HEADER */
 
 /* BEGIN_DEPENDENCIES
@@ -84,7 +135,7 @@
 /* BEGIN_CASE depends_on:MBEDTLS_PEM_WRITE_C:MBEDTLS_X509_CSR_WRITE_C */
 void x509_csr_check( char * key_file, char * cert_req_check_file, int md_type,
                      int key_usage, int set_key_usage, int cert_type,
-                     int set_cert_type )
+                     int set_cert_type, int set_extension )
 {
     mbedtls_pk_context key;
     mbedtls_x509write_csr req;
@@ -117,6 +168,9 @@
         TEST_ASSERT( mbedtls_x509write_csr_set_key_usage( &req, key_usage ) == 0 );
     if( set_cert_type != 0 )
         TEST_ASSERT( mbedtls_x509write_csr_set_ns_cert_type( &req, cert_type ) == 0 );
+    if ( set_extension != 0 )
+        TEST_ASSERT( csr_set_extended_key_usage( &req, MBEDTLS_OID_SERVER_AUTH,
+                            MBEDTLS_OID_SIZE( MBEDTLS_OID_SERVER_AUTH ) ) == 0 );
 
     ret = mbedtls_x509write_csr_pem( &req, buf, sizeof( buf ),
                                      mbedtls_test_rnd_pseudo_rand, &rnd_info );