Merge pull request #7548 from jethrogb/jb/mbedtls_pem_write_buffer

mbedtls_pem_write_buffer: Correctly report needed buffer size for all possible line lengths and counts
diff --git a/library/ecp_curves.c b/library/ecp_curves.c
index 1640107..f0a3e6e 100644
--- a/library/ecp_curves.c
+++ b/library/ecp_curves.c
@@ -4605,6 +4605,8 @@
 #endif
 #if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
 static int ecp_mod_p448(mbedtls_mpi *);
+MBEDTLS_STATIC_TESTABLE
+int mbedtls_ecp_mod_p448(mbedtls_mpi *);
 #endif
 #if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED)
 static int ecp_mod_p192k1(mbedtls_mpi *);
@@ -5449,6 +5451,11 @@
 #define P224_WIDTH_MAX   DIV_ROUND_UP(28, sizeof(mbedtls_mpi_uint))
 #define P224_UNUSED_BITS ((P224_WIDTH_MAX * sizeof(mbedtls_mpi_uint) * 8) - 224)
 
+static int ecp_mod_p448(mbedtls_mpi *N)
+{
+    return mbedtls_ecp_mod_p448(N);
+}
+
 /*
  * Fast quasi-reduction modulo p448 = 2^448 - 2^224 - 1
  * Write N as A0 + 2^448 A1 and A1 as B0 + 2^224 B1, and return
@@ -5460,7 +5467,8 @@
  * but for 64-bit targets it should use half the number of operations if we do
  * the reduction with 224-bit limbs, since mpi_add_mpi will then use 64-bit adds.
  */
-static int ecp_mod_p448(mbedtls_mpi *N)
+MBEDTLS_STATIC_TESTABLE
+int mbedtls_ecp_mod_p448(mbedtls_mpi *N)
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     size_t i;
diff --git a/library/ecp_invasive.h b/library/ecp_invasive.h
index 0d093f3..68187ac 100644
--- a/library/ecp_invasive.h
+++ b/library/ecp_invasive.h
@@ -193,6 +193,13 @@
 
 #endif /* MBEDTLS_ECP_DP_SECP256K1_ENABLED */
 
+#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
+
+MBEDTLS_STATIC_TESTABLE
+int mbedtls_ecp_mod_p448(mbedtls_mpi *N);
+
+#endif /* MBEDTLS_ECP_DP_CURVE448_ENABLED */
+
 /** Initialise a modulus with hard-coded const curve data.
  *
  * \note            The caller is responsible for the \p N modulus' memory.
diff --git a/scripts/mbedtls_dev/ecp.py b/scripts/mbedtls_dev/ecp.py
index b7b66e4..f9f27fa 100644
--- a/scripts/mbedtls_dev/ecp.py
+++ b/scripts/mbedtls_dev/ecp.py
@@ -682,3 +682,92 @@
     @property
     def is_valid(self) -> bool:
         return True
+
+
+class EcpP448Raw(bignum_common.ModOperationCommon,
+                 EcpTarget):
+    """Test cases for ECP P448 fast reduction."""
+    symbol = "-"
+    test_function = "ecp_mod_p448"
+    test_name = "ecp_mod_p448"
+    input_style = "fixed"
+    arity = 1
+    dependencies = ["MBEDTLS_ECP_DP_CURVE448_ENABLED"]
+
+    moduli = [("fffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
+               "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff")] # type: List[str]
+
+    input_values = [
+        "0", "1",
+
+        # Modulus - 1
+        ("fffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
+         "fffffffffffffffffffffffffffffffffffffffffffffffffffffffe"),
+
+        # Modulus + 1
+        ("ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+         "00000000000000000000000000000000000000000000000000000000"),
+
+        # 2^448 - 1
+        ("ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+         "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
+
+        # Maximum canonical P448 multiplication result
+        ("fffffffffffffffffffffffffffffffffffffffffffffffffffffffd"
+         "fffffffffffffffffffffffffffffffffffffffffffffffffffffffd"
+         "00000000000000000000000000000000000000000000000000000004"
+         "00000000000000000000000000000000000000000000000000000004"),
+
+        # First 8 number generated by random.getrandbits(896) - seed(2,2)
+        ("74667bffe202849da9643a295a9ac6decbd4d3e2d4dec9ef83f0be4e"
+         "80371eb97f81375eecc1cb6347733e847d718d733ff98ff387c56473"
+         "a7a83ee0761ebfd2bd143fa9b714210c665d7435c1066932f4767f26"
+         "294365b2721dea3bf63f23d0dbe53fcafb2147df5ca495fa5a91c89b"),
+        ("4da4daeb4f3f87777ad1f45ae9500ec9c5e2486c44a4a8f69dc8db48"
+         "e86ec9c6e06f291b2a838af8d5c44a4eb3172062d08f1bb2531d6460"
+         "f0caeef038c89b38a8acb5137c9260dc74e088a9b9492f258ebdbfe3"
+         "eb9ac688b9d39cca91551e8259cc60b17604e4b4e73695c3e652c71a"),
+        ("bc1b00d92838e766ef9b6bf2d037fe2e20b6a8464174e75a5f834da7"
+         "0569c018eb2b5693babb7fbb0a76c196067cfdcb11457d9cf45e2fa0"
+         "1d7f4275153924800600571fac3a5b263fdf57cd2c0064975c374746"
+         "5cc36c270e8a35b10828d569c268a20eb78ac332e5e138e26c4454b9"),
+        ("8d2f527e72daf0a54ef25c0707e338687d1f71575653a45c49390aa5"
+         "1cf5192bbf67da14be11d56ba0b4a2969d8055a9f03f2d71581d8e83"
+         "0112ff0f0948eccaf8877acf26c377c13f719726fd70bddacb4deeec"
+         "0b0c995e96e6bc4d62b47204007ee4fab105d83e85e951862f0981ae"),
+        ("84ae65e920a63ac1f2b64df6dff07870c9d531ae72a47403063238da"
+         "1a1fe3f9d6a179fa50f96cd4aff9261aa92c0e6f17ec940639bc2ccd"
+         "f572df00790813e32748dd1db4917fc09f20dbb0dcc93f0e66dfe717"
+         "c17313394391b6e2e6eacb0f0bb7be72bd6d25009aeb7fa0c4169b14"),
+        ("2bb3b36f29421c4021b7379f0897246a40c270b00e893302aba9e7b8"
+         "23fc5ad2f58105748ed5d1b7b310b730049dd332a73fa0b26b75196c"
+         "f87eb8a09b27ec714307c68c425424a1574f1eedf5b0f16cdfdb8394"
+         "24d201e653f53d6883ca1c107ca6e706649889c0c7f3860895bfa813"),
+        ("af3f5d7841b1256d5c1dc12fb5a1ae519fb8883accda6559caa538a0"
+         "9fc9370d3a6b86a7975b54a31497024640332b0612d4050771d7b14e"
+         "b6c004cc3b8367dc3f2bb31efe9934ad0809eae3ef232a32b5459d83"
+         "fbc46f1aea990e94821d46063b4dbf2ca294523d74115c86188b1044"),
+        ("7430051376e31f5aab63ad02854efa600641b4fa37a47ce41aeffafc"
+         "3b45402ac02659fe2e87d4150511baeb198ababb1a16daff3da95cd2"
+         "167b75dfb948f82a8317cba01c75f67e290535d868a24b7f627f2855"
+         "09167d4126af8090013c3273c02c6b9586b4625b475b51096c4ad652"),
+
+        # Next 2 number generated by random.getrandbits(448)
+        ("8f54f8ceacaab39e83844b40ffa9b9f15c14bc4a829e07b0829a48d4"
+         "22fe99a22c70501e533c91352d3d854e061b90303b08c6e33c729578"),
+        ("97eeab64ca2ce6bc5d3fd983c34c769fe89204e2e8168561867e5e15"
+         "bc01bfce6a27e0dfcbf8754472154e76e4c11ab2fec3f6b32e8d4b8a"),
+
+    ]
+
+    @property
+    def arg_a(self) -> str:
+        return super().format_arg('{:x}'.format(self.int_a)).zfill(2 * self.hex_digits)
+
+    def result(self) -> List[str]:
+        result = self.int_a % self.int_n
+        return [self.format_result(result)]
+
+    @property
+    def is_valid(self) -> bool:
+        return True
diff --git a/tests/include/test/arguments.h b/tests/include/test/arguments.h
new file mode 100644
index 0000000..74bbbd5
--- /dev/null
+++ b/tests/include/test/arguments.h
@@ -0,0 +1,38 @@
+/**
+ * \file arguments.h
+ *
+ * \brief Manipulation of test arguments.
+ *
+ * Much of the code is in host_test.function, to be migrated here later.
+ */
+
+/*
+ *  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_ARGUMENTS_H
+#define TEST_ARGUMENTS_H
+
+#include "mbedtls/build_info.h"
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef union {
+    size_t len;
+    intmax_t sint;
+} mbedtls_test_argument_t;
+
+#endif /* TEST_ARGUMENTS_H */
diff --git a/tests/scripts/generate_test_code.py b/tests/scripts/generate_test_code.py
index f19d30b..839fccd 100755
--- a/tests/scripts/generate_test_code.py
+++ b/tests/scripts/generate_test_code.py
@@ -171,6 +171,28 @@
 import argparse
 
 
+# Types recognized as signed integer arguments in test functions.
+SIGNED_INTEGER_TYPES = frozenset([
+    'char',
+    'short',
+    'short int',
+    'int',
+    'int8_t',
+    'int16_t',
+    'int32_t',
+    'int64_t',
+    'intmax_t',
+    'long',
+    'long int',
+    'long long int',
+    'mbedtls_mpi_sint',
+    'psa_status_t',
+])
+# Types recognized as string arguments in test functions.
+STRING_TYPES = frozenset(['char*', 'const char*', 'char const*'])
+# Types recognized as hex data arguments in test functions.
+DATA_TYPES = frozenset(['data_t*', 'const data_t*', 'data_t const*'])
+
 BEGIN_HEADER_REGEX = r'/\*\s*BEGIN_HEADER\s*\*/'
 END_HEADER_REGEX = r'/\*\s*END_HEADER\s*\*/'
 
@@ -192,9 +214,6 @@
                                                      CONDITION_OPERATOR_REGEX,
                                                      CONDITION_VALUE_REGEX)
 TEST_FUNCTION_VALIDATION_REGEX = r'\s*void\s+(?P<func_name>\w+)\s*\('
-INT_CHECK_REGEX = r'int\s+.*'
-CHAR_CHECK_REGEX = r'char\s*\*\s*.*'
-DATA_T_CHECK_REGEX = r'data_t\s*\*\s*.*'
 FUNCTION_ARG_LIST_END_REGEX = r'.*\)'
 EXIT_LABEL_REGEX = r'^exit:'
 
@@ -303,7 +322,7 @@
     :param name: Test function name
     :param local_vars: Local variables declaration code
     :param args_dispatch: List of dispatch arguments.
-           Ex: ['(char *)params[0]', '*((int *)params[1])']
+           Ex: ['(char *) params[0]', '*((int *) params[1])']
     :return: Test function wrapper.
     """
     # Then create the wrapper
@@ -444,6 +463,49 @@
     return dependencies
 
 
+ARGUMENT_DECLARATION_REGEX = re.compile(r'(.+?) ?(?:\bconst\b)? ?(\w+)\Z', re.S)
+def parse_function_argument(arg, arg_idx, args, local_vars, args_dispatch):
+    """
+    Parses one test function's argument declaration.
+
+    :param arg: argument declaration.
+    :param arg_idx: current wrapper argument index.
+    :param args: accumulator of arguments' internal types.
+    :param local_vars: accumulator of internal variable declarations.
+    :param args_dispatch: accumulator of argument usage expressions.
+    :return: the number of new wrapper arguments,
+             or None if the argument declaration is invalid.
+    """
+    # Normalize whitespace
+    arg = arg.strip()
+    arg = re.sub(r'\s*\*\s*', r'*', arg)
+    arg = re.sub(r'\s+', r' ', arg)
+    # Extract name and type
+    m = ARGUMENT_DECLARATION_REGEX.search(arg)
+    if not m:
+        # E.g. "int x[42]"
+        return None
+    typ, _ = m.groups()
+    if typ in SIGNED_INTEGER_TYPES:
+        args.append('int')
+        args_dispatch.append('((mbedtls_test_argument_t *) params[%d])->sint' % arg_idx)
+        return 1
+    if typ in STRING_TYPES:
+        args.append('char*')
+        args_dispatch.append('(char *) params[%d]' % arg_idx)
+        return 1
+    if typ in DATA_TYPES:
+        args.append('hex')
+        # create a structure
+        pointer_initializer = '(uint8_t *) params[%d]' % arg_idx
+        len_initializer = '((mbedtls_test_argument_t *) params[%d])->len' % (arg_idx+1)
+        local_vars.append('    data_t data%d = {%s, %s};\n' %
+                          (arg_idx, pointer_initializer, len_initializer))
+        args_dispatch.append('&data%d' % arg_idx)
+        return 2
+    return None
+
+ARGUMENT_LIST_REGEX = re.compile(r'\((.*?)\)', re.S)
 def parse_function_arguments(line):
     """
     Parses test function signature for validation and generates
@@ -455,42 +517,27 @@
     :return: argument list, local variables for
              wrapper function and argument dispatch code.
     """
-    args = []
-    local_vars = ''
-    args_dispatch = []
-    arg_idx = 0
-    # Remove characters before arguments
-    line = line[line.find('(') + 1:]
     # Process arguments, ex: <type> arg1, <type> arg2 )
     # This script assumes that the argument list is terminated by ')'
     # i.e. the test functions will not have a function pointer
     # argument.
-    for arg in line[:line.find(')')].split(','):
-        arg = arg.strip()
-        if arg == '':
-            continue
-        if re.search(INT_CHECK_REGEX, arg.strip()):
-            args.append('int')
-            args_dispatch.append('*( (int *) params[%d] )' % arg_idx)
-        elif re.search(CHAR_CHECK_REGEX, arg.strip()):
-            args.append('char*')
-            args_dispatch.append('(char *) params[%d]' % arg_idx)
-        elif re.search(DATA_T_CHECK_REGEX, arg.strip()):
-            args.append('hex')
-            # create a structure
-            pointer_initializer = '(uint8_t *) params[%d]' % arg_idx
-            len_initializer = '*( (uint32_t *) params[%d] )' % (arg_idx+1)
-            local_vars += """    data_t data%d = {%s, %s};
-""" % (arg_idx, pointer_initializer, len_initializer)
-
-            args_dispatch.append('&data%d' % arg_idx)
-            arg_idx += 1
-        else:
+    m = ARGUMENT_LIST_REGEX.search(line)
+    arg_list = m.group(1).strip()
+    if arg_list in ['', 'void']:
+        return [], '', []
+    args = []
+    local_vars = []
+    args_dispatch = []
+    arg_idx = 0
+    for arg in arg_list.split(','):
+        indexes = parse_function_argument(arg, arg_idx,
+                                          args, local_vars, args_dispatch)
+        if indexes is None:
             raise ValueError("Test function arguments can only be 'int', "
                              "'char *' or 'data_t'\n%s" % line)
-        arg_idx += 1
+        arg_idx += indexes
 
-    return args, local_vars, args_dispatch
+    return args, ''.join(local_vars), args_dispatch
 
 
 def generate_function_code(name, code, local_vars, args_dispatch,
@@ -705,7 +752,7 @@
     execution.
 
     :param data_f: file object of the data file.
-    :return: Generator that yields test name, function name,
+    :return: Generator that yields line number, test name, function name,
              dependency list and function argument list.
     """
     __state_read_name = 0
@@ -748,7 +795,7 @@
                 parts = escaped_split(line, ':')
                 test_function = parts[0]
                 args = parts[1:]
-                yield name, test_function, dependencies, args
+                yield data_f.line_no, name, test_function, dependencies, args
                 dependencies = []
                 state = __state_read_name
     if state == __state_read_args:
@@ -846,6 +893,14 @@
     return dep_check_code
 
 
+INT_VAL_REGEX = re.compile(r'-?(\d+|0x[0-9a-f]+)$', re.I)
+def val_is_int(val: str) -> bool:
+    """Whether val is suitable as an 'int' parameter in the .datax file."""
+    if not INT_VAL_REGEX.match(val):
+        return False
+    # Limit the range to what is guaranteed to get through strtol()
+    return abs(int(val, 0)) <= 0x7fffffff
+
 def write_parameters(out_data_f, test_args, func_args, unique_expressions):
     """
     Writes test parameters to the intermediate data file, replacing
@@ -864,9 +919,9 @@
         typ = func_args[i]
         val = test_args[i]
 
-        # check if val is a non literal int val (i.e. an expression)
-        if typ == 'int' and not re.match(r'(\d+|0x[0-9a-f]+)$',
-                                         val, re.I):
+        # Pass small integer constants literally. This reduces the size of
+        # the C code. Register anything else as an expression.
+        if typ == 'int' and not val_is_int(val):
             typ = 'exp'
             if val not in unique_expressions:
                 unique_expressions.append(val)
@@ -909,6 +964,24 @@
     return dep_check_code, expression_code
 
 
+def get_function_info(func_info, function_name, line_no):
+    """Look up information about a test function by name.
+
+    Raise an informative expression if function_name is not found.
+
+    :param func_info: dictionary mapping function names to their information.
+    :param function_name: the function name as written in the .function and
+                          .data files.
+    :param line_no: line number for error messages.
+    :return Function information (id, args).
+    """
+    test_function_name = 'test_' + function_name
+    if test_function_name not in func_info:
+        raise GeneratorInputError("%d: Function %s not found!" %
+                                  (line_no, test_function_name))
+    return func_info[test_function_name]
+
+
 def gen_from_test_data(data_f, out_data_f, func_info, suite_dependencies):
     """
     This function reads test case name, dependencies and test vectors
@@ -931,7 +1004,7 @@
     unique_expressions = []
     dep_check_code = ''
     expression_code = ''
-    for test_name, function_name, test_dependencies, test_args in \
+    for line_no, test_name, function_name, test_dependencies, test_args in \
             parse_test_data(data_f):
         out_data_f.write(test_name + '\n')
 
@@ -940,18 +1013,15 @@
                                              unique_dependencies)
 
         # Write test function name
-        test_function_name = 'test_' + function_name
-        if test_function_name not in func_info:
-            raise GeneratorInputError("Function %s not found!" %
-                                      test_function_name)
-        func_id, func_args = func_info[test_function_name]
+        func_id, func_args = \
+            get_function_info(func_info, function_name, line_no)
         out_data_f.write(str(func_id))
 
         # Write parameters
         if len(test_args) != len(func_args):
-            raise GeneratorInputError("Invalid number of arguments in test "
+            raise GeneratorInputError("%d: Invalid number of arguments in test "
                                       "%s. See function %s signature." %
-                                      (test_name, function_name))
+                                      (line_no, test_name, function_name))
         expression_code += write_parameters(out_data_f, test_args, func_args,
                                             unique_expressions)
 
diff --git a/tests/scripts/test_generate_test_code.py b/tests/scripts/test_generate_test_code.py
index d23d742..fe748ae 100755
--- a/tests/scripts/test_generate_test_code.py
+++ b/tests/scripts/test_generate_test_code.py
@@ -485,9 +485,10 @@
         args, local, arg_dispatch = parse_function_arguments(line)
         self.assertEqual(args, ['char*', 'int', 'int'])
         self.assertEqual(local, '')
-        self.assertEqual(arg_dispatch, ['(char *) params[0]',
-                                        '*( (int *) params[1] )',
-                                        '*( (int *) params[2] )'])
+        self.assertEqual(arg_dispatch,
+                         ['(char *) params[0]',
+                          '((mbedtls_test_argument_t *) params[1])->sint',
+                          '((mbedtls_test_argument_t *) params[2])->sint'])
 
     def test_hex_params(self):
         """
@@ -499,22 +500,22 @@
         self.assertEqual(args, ['char*', 'hex', 'int'])
         self.assertEqual(local,
                          '    data_t data1 = {(uint8_t *) params[1], '
-                         '*( (uint32_t *) params[2] )};\n')
+                         '((mbedtls_test_argument_t *) params[2])->len};\n')
         self.assertEqual(arg_dispatch, ['(char *) params[0]',
                                         '&data1',
-                                        '*( (int *) params[3] )'])
+                                        '((mbedtls_test_argument_t *) params[3])->sint'])
 
     def test_unsupported_arg(self):
         """
-        Test unsupported arguments (not among int, char * and data_t)
+        Test unsupported argument type
         :return:
         """
-        line = 'void entropy_threshold( char * a, data_t * h, char result )'
+        line = 'void entropy_threshold( char * a, data_t * h, unknown_t result )'
         self.assertRaises(ValueError, parse_function_arguments, line)
 
-    def test_no_params(self):
+    def test_empty_params(self):
         """
-        Test no parameters.
+        Test no parameters (nothing between parentheses).
         :return:
         """
         line = 'void entropy_threshold()'
@@ -523,6 +524,39 @@
         self.assertEqual(local, '')
         self.assertEqual(arg_dispatch, [])
 
+    def test_blank_params(self):
+        """
+        Test no parameters (space between parentheses).
+        :return:
+        """
+        line = 'void entropy_threshold( )'
+        args, local, arg_dispatch = parse_function_arguments(line)
+        self.assertEqual(args, [])
+        self.assertEqual(local, '')
+        self.assertEqual(arg_dispatch, [])
+
+    def test_void_params(self):
+        """
+        Test no parameters (void keyword).
+        :return:
+        """
+        line = 'void entropy_threshold(void)'
+        args, local, arg_dispatch = parse_function_arguments(line)
+        self.assertEqual(args, [])
+        self.assertEqual(local, '')
+        self.assertEqual(arg_dispatch, [])
+
+    def test_void_space_params(self):
+        """
+        Test no parameters (void with spaces).
+        :return:
+        """
+        line = 'void entropy_threshold( void )'
+        args, local, arg_dispatch = parse_function_arguments(line)
+        self.assertEqual(args, [])
+        self.assertEqual(local, '')
+        self.assertEqual(arg_dispatch, [])
+
 
 class ParseFunctionCode(TestCase):
     """
@@ -1264,29 +1298,33 @@
         # List of (name, function_name, dependencies, args)
         tests = list(parse_test_data(stream))
         test1, test2, test3, test4 = tests
-        self.assertEqual(test1[0], 'Diffie-Hellman full exchange #1')
-        self.assertEqual(test1[1], 'dhm_do_dhm')
-        self.assertEqual(test1[2], [])
-        self.assertEqual(test1[3], ['10', '"23"', '10', '"5"'])
+        self.assertEqual(test1[0], 3)
+        self.assertEqual(test1[1], 'Diffie-Hellman full exchange #1')
+        self.assertEqual(test1[2], 'dhm_do_dhm')
+        self.assertEqual(test1[3], [])
+        self.assertEqual(test1[4], ['10', '"23"', '10', '"5"'])
 
-        self.assertEqual(test2[0], 'Diffie-Hellman full exchange #2')
-        self.assertEqual(test2[1], 'dhm_do_dhm')
-        self.assertEqual(test2[2], [])
-        self.assertEqual(test2[3], ['10', '"93450983094850938450983409623"',
+        self.assertEqual(test2[0], 6)
+        self.assertEqual(test2[1], 'Diffie-Hellman full exchange #2')
+        self.assertEqual(test2[2], 'dhm_do_dhm')
+        self.assertEqual(test2[3], [])
+        self.assertEqual(test2[4], ['10', '"93450983094850938450983409623"',
                                     '10', '"9345098304850938450983409622"'])
 
-        self.assertEqual(test3[0], 'Diffie-Hellman full exchange #3')
-        self.assertEqual(test3[1], 'dhm_do_dhm')
-        self.assertEqual(test3[2], [])
-        self.assertEqual(test3[3], ['10',
+        self.assertEqual(test3[0], 9)
+        self.assertEqual(test3[1], 'Diffie-Hellman full exchange #3')
+        self.assertEqual(test3[2], 'dhm_do_dhm')
+        self.assertEqual(test3[3], [])
+        self.assertEqual(test3[4], ['10',
                                     '"9345098382739712938719287391879381271"',
                                     '10',
                                     '"9345098792137312973297123912791271"'])
 
-        self.assertEqual(test4[0], 'Diffie-Hellman selftest')
-        self.assertEqual(test4[1], 'dhm_selftest')
-        self.assertEqual(test4[2], [])
+        self.assertEqual(test4[0], 12)
+        self.assertEqual(test4[1], 'Diffie-Hellman selftest')
+        self.assertEqual(test4[2], 'dhm_selftest')
         self.assertEqual(test4[3], [])
+        self.assertEqual(test4[4], [])
 
     def test_with_dependencies(self):
         """
@@ -1306,15 +1344,17 @@
         # List of (name, function_name, dependencies, args)
         tests = list(parse_test_data(stream))
         test1, test2 = tests
-        self.assertEqual(test1[0], 'Diffie-Hellman full exchange #1')
-        self.assertEqual(test1[1], 'dhm_do_dhm')
-        self.assertEqual(test1[2], ['YAHOO'])
-        self.assertEqual(test1[3], ['10', '"23"', '10', '"5"'])
+        self.assertEqual(test1[0], 4)
+        self.assertEqual(test1[1], 'Diffie-Hellman full exchange #1')
+        self.assertEqual(test1[2], 'dhm_do_dhm')
+        self.assertEqual(test1[3], ['YAHOO'])
+        self.assertEqual(test1[4], ['10', '"23"', '10', '"5"'])
 
-        self.assertEqual(test2[0], 'Diffie-Hellman full exchange #2')
-        self.assertEqual(test2[1], 'dhm_do_dhm')
-        self.assertEqual(test2[2], [])
-        self.assertEqual(test2[3], ['10', '"93450983094850938450983409623"',
+        self.assertEqual(test2[0], 7)
+        self.assertEqual(test2[1], 'Diffie-Hellman full exchange #2')
+        self.assertEqual(test2[2], 'dhm_do_dhm')
+        self.assertEqual(test2[3], [])
+        self.assertEqual(test2[4], ['10', '"93450983094850938450983409623"',
                                     '10', '"9345098304850938450983409622"'])
 
     def test_no_args(self):
@@ -1335,7 +1375,7 @@
         stream = StringIOWrapper('test_suite_ut.function', data)
         err = None
         try:
-            for _, _, _, _ in parse_test_data(stream):
+            for _, _, _, _, _ in parse_test_data(stream):
                 pass
         except GeneratorInputError as err:
             self.assertEqual(type(err), GeneratorInputError)
@@ -1353,7 +1393,7 @@
         stream = StringIOWrapper('test_suite_ut.function', data)
         err = None
         try:
-            for _, _, _, _ in parse_test_data(stream):
+            for _, _, _, _, _ in parse_test_data(stream):
                 pass
         except GeneratorInputError as err:
             self.assertEqual(type(err), GeneratorInputError)
diff --git a/tests/suites/helpers.function b/tests/suites/helpers.function
index 60eae9a..86ff5b4 100644
--- a/tests/suites/helpers.function
+++ b/tests/suites/helpers.function
@@ -2,13 +2,18 @@
 /*----------------------------------------------------------------------------*/
 /* Headers */
 
+#include <test/arguments.h>
 #include <test/helpers.h>
 #include <test/macros.h>
 #include <test/random.h>
 #include <test/bignum_helpers.h>
 #include <test/psa_crypto_helpers.h>
 
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
 #include <stdlib.h>
+#include <string.h>
 
 #if defined(MBEDTLS_ERROR_C)
 #include "mbedtls/error.h"
@@ -19,23 +24,6 @@
 #include "mbedtls/memory_buffer_alloc.h"
 #endif
 
-#ifdef _MSC_VER
-#include <basetsd.h>
-typedef UINT8 uint8_t;
-typedef INT32 int32_t;
-typedef UINT32 uint32_t;
-#define strncasecmp _strnicmp
-#define strcasecmp _stricmp
-#else
-#include <stdint.h>
-#endif
-
-#include <string.h>
-
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__MINGW32__)
-#include <strings.h>
-#endif
-
 #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
 #include <unistd.h>
 #endif
diff --git a/tests/suites/host_test.function b/tests/suites/host_test.function
index 475a9c8..06f391f 100644
--- a/tests/suites/host_test.function
+++ b/tests/suites/host_test.function
@@ -28,50 +28,28 @@
  *              integer value.
  *
  * \param str   Input string.
- * \param value Pointer to int for output value.
+ * \param p_value Pointer to output value.
  *
  * \return      0 if success else 1
  */
-int verify_int(char *str, int32_t *value)
+int verify_int(char *str, intmax_t *p_value)
 {
-    size_t i;
-    int minus = 0;
-    int digits = 1;
-    int hex = 0;
-
-    for (i = 0; i < strlen(str); i++) {
-        if (i == 0 && str[i] == '-') {
-            minus = 1;
-            continue;
-        }
-
-        if (((minus && i == 2) || (!minus && i == 1)) &&
-            str[i - 1] == '0' && (str[i] == 'x' || str[i] == 'X')) {
-            hex = 1;
-            continue;
-        }
-
-        if (!((str[i] >= '0' && str[i] <= '9') ||
-              (hex && ((str[i] >= 'a' && str[i] <= 'f') ||
-                       (str[i] >= 'A' && str[i] <= 'F'))))) {
-            digits = 0;
-            break;
-        }
+    char *end = NULL;
+    errno = 0;
+    /* Limit the range to long: for large integers, the test framework will
+     * use expressions anyway. */
+    long value = strtol(str, &end, 0);
+    if (errno == EINVAL || *end != '\0') {
+        mbedtls_fprintf(stderr,
+                        "Expected integer for parameter and got: %s\n", str);
+        return KEY_VALUE_MAPPING_NOT_FOUND;
     }
-
-    if (digits) {
-        if (hex) {
-            *value = strtol(str, NULL, 16);
-        } else {
-            *value = strtol(str, NULL, 10);
-        }
-
-        return 0;
+    if (errno == ERANGE) {
+        mbedtls_fprintf(stderr, "Integer out of range: %s\n", str);
+        return KEY_VALUE_MAPPING_NOT_FOUND;
     }
-
-    mbedtls_fprintf(stderr,
-                    "Expected integer for parameter and got: %s\n", str);
-    return KEY_VALUE_MAPPING_NOT_FOUND;
+    *p_value = value;
+    return 0;
 }
 
 
@@ -180,24 +158,24 @@
         p++;
     }
 
-    /* Replace newlines, question marks and colons in strings */
+    /* Replace backslash escapes in strings */
     for (i = 0; i < cnt; i++) {
         p = params[i];
         q = params[i];
 
         while (*p != '\0') {
-            if (*p == '\\' && *(p + 1) == 'n') {
-                p += 2;
-                *(q++) = '\n';
-            } else if (*p == '\\' && *(p + 1) == ':') {
-                p += 2;
-                *(q++) = ':';
-            } else if (*p == '\\' && *(p + 1) == '?') {
-                p += 2;
-                *(q++) = '?';
-            } else {
-                *(q++) = *(p++);
+            if (*p == '\\') {
+                ++p;
+                switch (*p) {
+                    case 'n':
+                        *p = '\n';
+                        break;
+                    default:
+                        // Fall through to copying *p
+                        break;
+                }
             }
+            *(q++) = *(p++);
         }
         *q = '\0';
     }
@@ -223,7 +201,8 @@
  *
  * \return      0 for success else 1
  */
-static int convert_params(size_t cnt, char **params, int32_t *int_params_store)
+static int convert_params(size_t cnt, char **params,
+                          mbedtls_test_argument_t *int_params_store)
 {
     char **cur = params;
     char **out = params;
@@ -241,7 +220,7 @@
                 break;
             }
         } else if (strcmp(type, "int") == 0) {
-            if (verify_int(val, int_params_store) == 0) {
+            if (verify_int(val, &int_params_store->sint) == 0) {
                 *out++ = (char *) int_params_store++;
             } else {
                 ret = (DISPATCH_INVALID_TEST_DATA);
@@ -255,7 +234,7 @@
                     mbedtls_test_unhexify((unsigned char *) val, strlen(val),
                                           val, &len) == 0);
 
-                *int_params_store = len;
+                int_params_store->len = len;
                 *out++ = val;
                 *out++ = (char *) (int_params_store++);
             } else {
@@ -264,7 +243,7 @@
             }
         } else if (strcmp(type, "exp") == 0) {
             int exp_id = strtol(val, NULL, 10);
-            if (get_expression(exp_id, int_params_store) == 0) {
+            if (get_expression(exp_id, &int_params_store->sint) == 0) {
                 *out++ = (char *) int_params_store++;
             } else {
                 ret = (DISPATCH_INVALID_TEST_DATA);
@@ -483,7 +462,7 @@
     char buf[5000];
     char *params[50];
     /* Store for processed integer params. */
-    int32_t int_params[50];
+    mbedtls_test_argument_t int_params[50];
     void *pointer;
 #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
     int stdout_fd = -1;
diff --git a/tests/suites/main_test.function b/tests/suites/main_test.function
index 11a009a..6c8d98e 100644
--- a/tests/suites/main_test.function
+++ b/tests/suites/main_test.function
@@ -69,7 +69,7 @@
  *
  * \return       0 if exp_id is found. 1 otherwise.
  */
-int get_expression(int32_t exp_id, int32_t *out_value)
+int get_expression(int32_t exp_id, intmax_t *out_value)
 {
     int ret = KEY_VALUE_MAPPING_FOUND;
 
diff --git a/tests/suites/test_suite_bignum.function b/tests/suites/test_suite_bignum.function
index cefbfc3..7f858e5 100644
--- a/tests/suites/test_suite_bignum.function
+++ b/tests/suites/test_suite_bignum.function
@@ -133,7 +133,7 @@
         TEST_ASSERT(sign_is_valid(&X));
         TEST_ASSERT(mbedtls_mpi_write_string(&X, radix_A, str, output_size, &len) == result_write);
         if (result_write == 0) {
-            TEST_ASSERT(strcasecmp(str, input_A) == 0);
+            TEST_ASSERT(strcmp(str, input_A) == 0);
             TEST_ASSERT(str[len] == '!');
         }
     }
@@ -923,47 +923,16 @@
 /* END_CASE */
 
 /* BEGIN_CASE */
-void mpi_mod_int(char *input_X, char *input_Y,
-                 char *input_A, int mod_result)
+void mpi_mod_int(char *input_X, mbedtls_mpi_sint y,
+                 mbedtls_mpi_sint a, int mod_result)
 {
     mbedtls_mpi X;
-    mbedtls_mpi Y;
-    mbedtls_mpi A;
     int res;
     mbedtls_mpi_uint r;
 
     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);
@@ -973,8 +942,6 @@
 
 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 5eda4c1..9d068f1 100644
--- a/tests/suites/test_suite_bignum.misc.data
+++ b/tests/suites/test_suite_bignum.misc.data
@@ -56,10 +56,10 @@
 mpi_read_write_string:16:"-23":17:"-23":4:0:MBEDTLS_ERR_MPI_BAD_INPUT_DATA
 
 Test mpi_read_write_string #6 (Output radix of 15)
-mpi_read_write_string:10:"29":15:"1e":100:0:0
+mpi_read_write_string:10:"29":15:"1E":100:0:0
 
 Test mpi_read_write_string #7
-mpi_read_write_string:10:"56125680981752282334141896320372489490613963693556392520816017892111350604111697682705498319512049040516698827829292076808006940873974979584527073481012636016353913462376755556720019831187364993587901952757307830896531678727717924":16:"0941379d00fed1491fe15df284dfde4a142f68aa8d412023195cee66883e6290ffe703f4ea5963bf212713cee46b107c09182b5edcd955adac418bf4918e2889af48e1099d513830cec85c26ac1e158b52620e33ba8692f893efbb2f958b4424":200:0:0
+mpi_read_write_string:10:"56125680981752282334141896320372489490613963693556392520816017892111350604111697682705498319512049040516698827829292076808006940873974979584527073481012636016353913462376755556720019831187364993587901952757307830896531678727717924":16:"0941379D00FED1491FE15DF284DFDE4A142F68AA8D412023195CEE66883E6290FFE703F4EA5963BF212713CEE46B107C09182B5EDCD955ADAC418BF4918E2889AF48E1099D513830CEC85C26AC1E158B52620E33BA8692F893EFBB2F958B4424":200:0:0
 
 Test mpi_read_write_string #8 (Empty MPI hex -> hex)
 mpi_read_write_string:16:"":16:"":4:0:0
@@ -1229,45 +1229,45 @@
 mpi_mod_mpi:"-":"-2a":"":MBEDTLS_ERR_MPI_NEGATIVE_VALUE
 
 Base test mbedtls_mpi_mod_int #1
-mpi_mod_int:"3e8":"d":"c":0
+mpi_mod_int:"3e8":0xd:0xc: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":0x0:0x0:MBEDTLS_ERR_MPI_DIVISION_BY_ZERO
 
 Base test mbedtls_mpi_mod_int #3
-mpi_mod_int:"-3e8":"d":"1":0
+mpi_mod_int:"-3e8":0xd:0x1:0
 
 Base test mbedtls_mpi_mod_int #4 (Negative modulo)
-mpi_mod_int:"3e8":"-d":"0":MBEDTLS_ERR_MPI_NEGATIVE_VALUE
+mpi_mod_int:"3e8":-0xd:0x0:MBEDTLS_ERR_MPI_NEGATIVE_VALUE
 
 Base test mbedtls_mpi_mod_int #5 (Negative modulo)
-mpi_mod_int:"-3e8":"-d":"0":MBEDTLS_ERR_MPI_NEGATIVE_VALUE
+mpi_mod_int:"-3e8":-0xd:0x0: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":0x1:0x0:0
 
 Base test mbedtls_mpi_mod_int #7 (By 2)
-mpi_mod_int:"3e9":"2":"1":0
+mpi_mod_int:"3e9":0x2:0x1:0
 
 Base test mbedtls_mpi_mod_int #8 (By 2)
-mpi_mod_int:"3e8":"2":"0":0
+mpi_mod_int:"3e8":0x2:0x0:0
 
 Test mbedtls_mpi_mod_int: 0 (null) % 1
-mpi_mod_int:"":"1":"0":0
+mpi_mod_int:"":0x1:0x0:0
 
 Test mbedtls_mpi_mod_int: 0 (null) % 2
-mpi_mod_int:"":"2":"0":0
+mpi_mod_int:"":0x2:0x0:0
 
 Test mbedtls_mpi_mod_int: 0 (null) % -1
-mpi_mod_int:"":"-1":"0":MBEDTLS_ERR_MPI_NEGATIVE_VALUE
+mpi_mod_int:"":-0x1:0x0: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:"":-0x2:0x0: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
+#mpi_mod_int:"AD9D28BF6C4E98FDF156BF0980CEE30A":0x47DCCA4847DCCA48:0x2EFE6F1A7D28035A:0
 
 Test mbedtls_mpi_mod_mpi: 230772460340063000000100500000300000010 % 5178236083361335880 -> 3386266129388798810
 mpi_mod_mpi:"AD9D28BF6C4E98FDF156BF0980CEE30A":"47DCCA4847DCCA48":"2EFE6F1A7D28035A":0
@@ -1275,7 +1275,7 @@
 # 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
+#mpi_mod_int:"AD9D28BF6C4E98FDC2584FEF03A6DFB0":0x47DCCA4847DCCA48:0x0:0
 
 Test mbedtls_mpi_mod_mpi: 230772460340062999996714233870911201200 % 5178236083361335880 -> 0
 mpi_mod_mpi:"AD9D28BF6C4E98FDC2584FEF03A6DFB0":"47DCCA4847DCCA48":"0":0
@@ -1283,7 +1283,7 @@
 # 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
+mpi_mod_int:"AD9D28BF6C4E98FDF156BF0980CEE30A":0x47DCCA48:0x379BD2:0
 
 Test mbedtls_mpi_mod_mpi: 230772460340063000000100500000300000010 % 1205652040 -> 3644370
 mpi_mod_mpi:"AD9D28BF6C4E98FDF156BF0980CEE30A":"47DCCA48":"379BD2":0
@@ -1291,7 +1291,7 @@
 # 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
+mpi_mod_int:"AD9D28BF6C4E98FDF156BF0980974738":0x47DCCA48:0x0:0
 
 Test mbedtls_mpi_mod_mpi: 230772460340063000000100500000296355640 % 1205652040 -> 0
 mpi_mod_mpi:"AD9D28BF6C4E98FDF156BF0980974738":"47DCCA48":"0":0
diff --git a/tests/suites/test_suite_ecp.function b/tests/suites/test_suite_ecp.function
index 6d5ce9c..f034d6f 100644
--- a/tests/suites/test_suite_ecp.function
+++ b/tests/suites/test_suite_ecp.function
@@ -1472,6 +1472,45 @@
 }
 /* END_CASE */
 
+/* BEGIN_CASE depends_on:MBEDTLS_TEST_HOOKS:MBEDTLS_ECP_DP_CURVE448_ENABLED */
+void ecp_mod_p448(char *input_N,
+                  char *input_X,
+                  char *result)
+{
+    mbedtls_mpi X;
+    mbedtls_mpi N;
+    mbedtls_mpi res;
+
+    mbedtls_mpi_init(&X);
+    mbedtls_mpi_init(&N);
+    mbedtls_mpi_init(&res);
+
+    TEST_EQUAL(mbedtls_test_read_mpi(&X,   input_X), 0);
+    TEST_EQUAL(mbedtls_test_read_mpi(&N,   input_N), 0);
+    TEST_EQUAL(mbedtls_test_read_mpi(&res, result),  0);
+
+    TEST_ASSERT(mbedtls_mpi_core_uint_le_mpi(0, X.p, X.n));
+    TEST_ASSERT(mbedtls_mpi_core_uint_le_mpi(0, N.p, N.n));
+    TEST_ASSERT(mbedtls_mpi_core_uint_le_mpi(0, res.p, res.n));
+
+    size_t limbs = N.n;
+    size_t bytes = limbs * sizeof(mbedtls_mpi_uint);
+
+    TEST_LE_U(X.n, 2 * limbs);
+    TEST_EQUAL(res.n, limbs);
+
+    TEST_EQUAL(mbedtls_ecp_mod_p448(&X), 0);
+    TEST_EQUAL(mbedtls_mpi_mod_mpi(&X, &X, &N), 0);
+    TEST_LE_U(mbedtls_mpi_core_bitlen(X.p, X.n), 448);
+    ASSERT_COMPARE(X.p, bytes, res.p, bytes);
+
+exit:
+    mbedtls_mpi_free(&X);
+    mbedtls_mpi_free(&N);
+    mbedtls_mpi_free(&res);
+}
+/* END_CASE */
+
 /* BEGIN_CASE depends_on:MBEDTLS_TEST_HOOKS */
 void ecp_mod_setup(char *input_A, int id, int ctype, int iret)
 {
diff --git a/tests/suites/test_suite_platform_printf.data b/tests/suites/test_suite_platform_printf.data
new file mode 100644
index 0000000..891771b
--- /dev/null
+++ b/tests/suites/test_suite_platform_printf.data
@@ -0,0 +1,114 @@
+# The test cases for printf and integers have two purposes: they exercise
+# the printf function family, and they exercise the passing of integers
+# and strings through the test framework.
+
+printf "%d", 0
+printf_int:"%d":0:"0"
+
+printf "%d", -0
+printf_int:"%d":-0:"0"
+
+printf "%d", 0x0
+printf_int:"%d":0x0:"0"
+
+printf "%d", 0x00
+printf_int:"%d":0x00:"0"
+
+printf "%d", 0x000000000000000000000000000000000000000000
+printf_int:"%d":0x000000000000000000000000000000000000000000:"0"
+
+printf "%d", -0x0
+printf_int:"%d":-0x0:"0"
+
+printf "%d", 1
+printf_int:"%d":1:"1"
+
+printf "%d", 0x1
+printf_int:"%d":0x1:"1"
+
+printf "%d", 0x0000000000000000000000000000000000000000001
+printf_int:"%d":0x0000000000000000000000000000000000000000001:"1"
+
+printf "%d", -1
+printf_int:"%d":-1:"-1"
+
+printf "%d", -0x1
+printf_int:"%d":-0x1:"-1"
+
+printf "%d", -0x0000000000000000000000000000000000000000001
+printf_int:"%d":-0x0000000000000000000000000000000000000000001:"-1"
+
+printf "%d", 2147483647
+printf_int:"%d":2147483647:"2147483647"
+
+printf "%d", 0x7fffffff
+printf_int:"%d":0x7fffffff:"2147483647"
+
+printf "%d", -2147483647
+printf_int:"%d":-2147483647:"-2147483647"
+
+printf "%d", -0x7fffffff
+printf_int:"%d":-0x7fffffff:"-2147483647"
+
+printf "%d", -2147483648
+printf_int:"%d":-2147483648:"-2147483648"
+
+printf "%d", -0x80000000
+printf_int:"%d":-0x80000000:"-2147483648"
+
+# Test that LONG_MAX is coming out untruncated through the test framework.
+printf "%lx", LONG_MAX
+printf_long_max:"%lx":LONG_MAX
+
+# The next few test cases exercise how the test framework handles special
+# characters in strings.
+printf "%c%c", SPACE, SPACE
+printf_char2:"%c%c":SPACE_CHAR:SPACE_CHAR:"  "
+
+printf "%c%c", NEWLINE, SPACE
+printf_char2:"%c%c":NEWLINE_CHAR:SPACE_CHAR:"\n "
+
+printf "%c%c", DOUBLE QUOTE, SPACE
+printf_char2:"%c%c":DOUBLE_QUOTE_CHAR:SPACE_CHAR:"\" "
+
+printf "%c%c", COLON, SPACE
+printf_char2:"%c%c":COLON_CHAR:SPACE_CHAR:"\: "
+
+printf "%c%c", QUESTION, SPACE
+printf_char2:"%c%c":QUESTION_CHAR:SPACE_CHAR:"? "
+
+printf "%c%c", BACKSLASH, SPACE
+printf_char2:"%c%c":BACKSLASH_CHAR:SPACE_CHAR:"\\ "
+
+printf "%c%c", SPACE, BACKSLASH
+printf_char2:"%c%c":SPACE_CHAR:BACKSLASH_CHAR:" \\"
+
+printf "%c%c", COLON, COLON
+printf_char2:"%c%c":COLON_CHAR:COLON_CHAR:"\:\:"
+
+printf "%c%c", COLON, NEWLINE
+printf_char2:"%c%c":COLON_CHAR:NEWLINE_CHAR:"\:\n"
+
+printf "%c%c", QUESTION, QUESTION
+printf_char2:"%c%c":QUESTION_CHAR:QUESTION_CHAR:"??"
+
+printf "%c%c", QUESTION, NEWLINE
+printf_char2:"%c%c":QUESTION_CHAR:NEWLINE_CHAR:"?\n"
+
+printf "%c%c", BACKSLASH, NEWLINE
+printf_char2:"%c%c":BACKSLASH_CHAR:NEWLINE_CHAR:"\\\n"
+
+printf "%c%c", BACKSLASH, DOUBLE QUOTE
+printf_char2:"%c%c":BACKSLASH_CHAR:DOUBLE_QUOTE_CHAR:"\\\""
+
+printf "%c%c", BACKSLASH, COLON
+printf_char2:"%c%c":BACKSLASH_CHAR:COLON_CHAR:"\\\:"
+
+printf "%c%c", BACKSLASH, QUESTION
+printf_char2:"%c%c":BACKSLASH_CHAR:QUESTION_CHAR:"\\?"
+
+printf "%c%c", BACKSLASH, BACKSLASH
+printf_char2:"%c%c":BACKSLASH_CHAR:BACKSLASH_CHAR:"\\\\"
+
+printf "%c%c", BACKSLASH, n
+printf_char2:"%c%c":BACKSLASH_CHAR:LOWERCASE_N_CHAR:"\\n"
diff --git a/tests/suites/test_suite_platform_printf.function b/tests/suites/test_suite_platform_printf.function
new file mode 100644
index 0000000..3c816fe
--- /dev/null
+++ b/tests/suites/test_suite_platform_printf.function
@@ -0,0 +1,89 @@
+/* BEGIN_HEADER */
+
+/* The printf test functions take a format argument from the test data
+ * for several reasons:
+ * - For some tests, it makes sense to vary the format.
+ * - For all tests, it means we're testing the actual printf function
+ *   that parses the format at runtime, and not a compiler optimization.
+ *   (It may be useful to add tests that allow compiler optimizations.
+ *   There aren't any yet at the time of writing.)
+ */
+
+#include "mbedtls/platform.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define NEWLINE_CHAR '\n'
+#define SPACE_CHAR ' '
+#define DOUBLE_QUOTE_CHAR '"'
+#define COLON_CHAR ':'
+#define QUESTION_CHAR '?'
+#define BACKSLASH_CHAR '\\'
+#define LOWERCASE_N_CHAR 'n'
+/* END_HEADER */
+
+/* BEGIN_CASE */
+void printf_int(char *format, /* any format expecting one int argument, e.g. "%d" */
+                int x, char *result)
+{
+    char *output = NULL;
+    const size_t n = strlen(result);
+
+    /* Nominal case: buffer just large enough */
+    ASSERT_ALLOC(output, n + 1);
+    TEST_EQUAL(n, mbedtls_snprintf(output, n + 1, format, x));
+    ASSERT_COMPARE(result, n + 1, output, n + 1);
+    mbedtls_free(output);
+    output = NULL;
+
+exit:
+    mbedtls_free(output);
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void printf_long_max(const char *format, /* "%lx" or longer type */
+                     long value)
+{
+    char *expected = NULL;
+    char *output = NULL;
+    /* 2 hex digits per byte */
+    const size_t n = sizeof(value) * 2;
+
+    /* We assume that long has no padding bits! */
+    ASSERT_ALLOC(expected, n + 1);
+    expected[0] = '7';
+    memset(expected + 1, 'f', sizeof(value) * 2 - 1);
+
+    ASSERT_ALLOC(output, n + 1);
+    TEST_EQUAL(n, mbedtls_snprintf(output, n + 1, format, value));
+    ASSERT_COMPARE(expected, n + 1, output, n + 1);
+    mbedtls_free(output);
+    output = NULL;
+
+exit:
+    mbedtls_free(output);
+    mbedtls_free(expected);
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void printf_char2(char *format, /* "%c%c" */
+                  int arg1, int arg2, char *result)
+{
+    char *output = NULL;
+    const size_t n = strlen(result);
+
+    /* Nominal case: buffer just large enough */
+    ASSERT_ALLOC(output, n + 1);
+    TEST_EQUAL(n, mbedtls_snprintf(output, n + 1, format, arg1, arg2));
+    ASSERT_COMPARE(result, n + 1, output, n + 1);
+    mbedtls_free(output);
+    output = NULL;
+
+exit:
+    mbedtls_free(output);
+}
+/* END_CASE */
diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function
index fc8e6eb..98b1a33 100644
--- a/tests/suites/test_suite_psa_crypto.function
+++ b/tests/suites/test_suite_psa_crypto.function
@@ -2422,12 +2422,12 @@
 /* BEGIN_CASE */
 void copy_success(int source_usage_arg,
                   int source_alg_arg, int source_alg2_arg,
-                  unsigned int source_lifetime_arg,
+                  int source_lifetime_arg,
                   int type_arg, data_t *material,
                   int copy_attributes,
                   int target_usage_arg,
                   int target_alg_arg, int target_alg2_arg,
-                  unsigned int target_lifetime_arg,
+                  int target_lifetime_arg,
                   int expected_usage_arg,
                   int expected_alg_arg, int expected_alg2_arg)
 {
diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data
index a6b001f..db2fe56 100644
--- a/tests/suites/test_suite_x509parse.data
+++ b/tests/suites/test_suite_x509parse.data
@@ -336,7 +336,7 @@
 
 X509 CSR Information RSA with SHA-256, containing commas
 depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_MD_CAN_SHA256:MBEDTLS_RSA_C:MBEDTS_X509_INFO
-mbedtls_x509_csr_info:"data_files/server1.req.commas.sha256":"CSR version   \: 1\nsubject name  \: C=NL, O=PolarSSL\, Commas, CN=PolarSSL Server 1\nsigned using  \: RSA with SHA-256\nRSA key size  \: 2048 bits\n"
+mbedtls_x509_csr_info:"data_files/server1.req.commas.sha256":"CSR version   \: 1\nsubject name  \: C=NL, O=PolarSSL\\, Commas, CN=PolarSSL Server 1\nsigned using  \: RSA with SHA-256\nRSA key size  \: 2048 bits\n"
 
 X509 CSR Information EC with SHA1
 depends_on:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_MD_CAN_SHA1:!MBEDTLS_X509_REMOVE_INFO
@@ -437,7 +437,7 @@
 
 X509 Get Distinguished Name #5
 depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_MD_CAN_SHA1
-mbedtls_x509_dn_gets:"data_files/server1.commas.crt":"subject":"C=NL, O=PolarSSL\, Commas, CN=PolarSSL Server 1"
+mbedtls_x509_dn_gets:"data_files/server1.commas.crt":"subject":"C=NL, O=PolarSSL\\, Commas, CN=PolarSSL Server 1"
 
 X509 Get Modified DN #1
 depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_MD_CAN_SHA1
@@ -3145,7 +3145,7 @@
 
 X509 Get time (UTC invalid character in year)
 depends_on:MBEDTLS_X509_USE_C
-x509_get_time:MBEDTLS_ASN1_UTC_TIME:"0\1130231212Z":MBEDTLS_ERR_X509_INVALID_DATE:0:0:0:0:0:0
+x509_get_time:MBEDTLS_ASN1_UTC_TIME:"0\\1130231212Z":MBEDTLS_ERR_X509_INVALID_DATE:0:0:0:0:0:0
 
 X509 Get time (UTC invalid character in month)
 depends_on:MBEDTLS_X509_USE_C
diff --git a/tests/suites/test_suite_x509write.data b/tests/suites/test_suite_x509write.data
index bb40029..cd1b0a3 100644
--- a/tests/suites/test_suite_x509write.data
+++ b/tests/suites/test_suite_x509write.data
@@ -163,7 +163,7 @@
 x509_crt_check:"data_files/server5.key":"":"C=NL,O=PolarSSL,CN=PolarSSL Server 1":"data_files/test-ca2.key":"PolarSSLTest":"C=NL,O=PolarSSL,CN=Polarssl Test EC CA":"01":"20190210144406":"20290210144406":MBEDTLS_MD_SHA256:0:0:"NULL":0:0:1:-1:"":2:0:"data_files/test-ca2.crt"
 
 X509 String to Names #1
-mbedtls_x509_string_to_names:"C=NL,O=Offspark\, Inc., OU=PolarSSL":"C=NL, O=Offspark\, Inc., OU=PolarSSL":0
+mbedtls_x509_string_to_names:"C=NL,O=Offspark\\, Inc., OU=PolarSSL":"C=NL, O=Offspark\\, Inc., OU=PolarSSL":0
 
 X509 String to Names #2
 mbedtls_x509_string_to_names:"C=NL, O=Offspark, Inc., OU=PolarSSL":"":MBEDTLS_ERR_X509_UNKNOWN_OID
@@ -175,10 +175,10 @@
 mbedtls_x509_string_to_names:"C=NL, O=1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME
 
 X509 String to Names #5 (Escape non-allowed characters)
-mbedtls_x509_string_to_names:"C=NL, O=Offspark\a Inc., OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME
+mbedtls_x509_string_to_names:"C=NL, O=Offspark\\a Inc., OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME
 
 X509 String to Names #6 (Escape at end)
-mbedtls_x509_string_to_names:"C=NL, O=Offspark\":"":MBEDTLS_ERR_X509_INVALID_NAME
+mbedtls_x509_string_to_names:"C=NL, O=Offspark\\":"":MBEDTLS_ERR_X509_INVALID_NAME
 
 Check max serial length
 x509_set_serial_check: