Merge pull request #6965 from davidhorstmann-arm/2.28-code-style-improvements

[Backport 2.28] Improvements to code style script
diff --git a/ChangeLog.d/fix_timing_alt.txt b/ChangeLog.d/fix_timing_alt.txt
new file mode 100644
index 0000000..86ec16d
--- /dev/null
+++ b/ChangeLog.d/fix_timing_alt.txt
@@ -0,0 +1,5 @@
+Bugfix
+   * Fix a build issue when defining MBEDTLS_TIMING_ALT and MBEDTLS_SELF_TEST.
+     The library would not link if the user didn't provide an external self-test
+     function. The self-test is now provided regardless of the choice of
+     internal/alternative timing implementation. Fixes #6923.
diff --git a/ChangeLog.d/improve_x509_cert_writing_serial_number_management.txt b/ChangeLog.d/improve_x509_cert_writing_serial_number_management.txt
new file mode 100644
index 0000000..a85c79b
--- /dev/null
+++ b/ChangeLog.d/improve_x509_cert_writing_serial_number_management.txt
@@ -0,0 +1,5 @@
+Bugfix
+   * mbedtls_x509write_crt_set_serial() now explicitly rejects serial numbers
+     whose binary representation is longer than 20 bytes. This was already
+     forbidden by the standard (RFC5280 - section 4.1.2.2) and now it's being
+     enforced also at code level.
diff --git a/library/Makefile b/library/Makefile
index 3b91e25..b011e88 100644
--- a/library/Makefile
+++ b/library/Makefile
@@ -186,6 +186,13 @@
 all: shared static
 endif
 
+ifdef TEST_TIMING_ALT_IMPL
+OBJS_CRYPTO += external_timing_for_test.o
+external_timing_for_test.o: ../tests/src/external_timing/external_timing_for_test.c
+	echo "  CC    $<"
+	$(CC) $(LOCAL_CFLAGS) $(CFLAGS) -o $@ -c $<
+endif
+
 static: libmbedcrypto.a libmbedx509.a libmbedtls.a
 	cd ../tests && echo "This is a seedfile that contains 64 bytes (65 on Windows)......" > seedfile
 
diff --git a/library/timing.c b/library/timing.c
index d4f9554..47e34f9 100644
--- a/library/timing.c
+++ b/library/timing.c
@@ -352,9 +352,9 @@
     return 0;
 }
 
+#endif /* !MBEDTLS_TIMING_ALT */
 
 #if defined(MBEDTLS_SELF_TEST)
-
 /*
  * Busy-waits for the given number of milliseconds.
  * Used for testing mbedtls_timing_hardclock.
@@ -383,9 +383,8 @@
             mbedtls_printf(" cycles=%lu ratio=%lu millisecs=%lu secs=%lu hardfail=%d a=%lu b=%lu\n", \
                            cycles, ratio, millisecs, secs, hardfail,   \
                            (unsigned long) a, (unsigned long) b);     \
-            mbedtls_printf(" elapsed(hires)=%lu elapsed(ctx)=%lu status(ctx)=%d\n", \
+            mbedtls_printf(" elapsed(hires)=%lu status(ctx)=%d\n", \
                            mbedtls_timing_get_timer(&hires, 0),      \
-                           mbedtls_timing_get_timer(&ctx.timer, 0),  \
                            mbedtls_timing_get_delay(&ctx));         \
         }                                                               \
         return 1;                                                    \
@@ -526,5 +525,4 @@
 }
 
 #endif /* MBEDTLS_SELF_TEST */
-#endif /* !MBEDTLS_TIMING_ALT */
 #endif /* MBEDTLS_TIMING_C */
diff --git a/library/x509write_crt.c b/library/x509write_crt.c
index 4a65939..a8f4c28 100644
--- a/library/x509write_crt.c
+++ b/library/x509write_crt.c
@@ -100,6 +100,10 @@
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
 
+    if (mbedtls_mpi_size(serial) > MBEDTLS_X509_RFC5280_MAX_SERIAL_LEN) {
+        return MBEDTLS_ERR_X509_BAD_INPUT_DATA;
+    }
+
     if ((ret = mbedtls_mpi_copy(&ctx->serial, serial)) != 0) {
         return ret;
     }
diff --git a/scripts/mbedtls_dev/crypto_knowledge.py b/scripts/mbedtls_dev/crypto_knowledge.py
index f227a41..4e9503e 100644
--- a/scripts/mbedtls_dev/crypto_knowledge.py
+++ b/scripts/mbedtls_dev/crypto_knowledge.py
@@ -20,7 +20,7 @@
 
 import enum
 import re
-from typing import FrozenSet, Iterable, List, Optional, Tuple
+from typing import FrozenSet, Iterable, List, Optional, Tuple, Dict
 
 from .asymmetric_key_data import ASYMMETRIC_KEY_DATA
 
@@ -148,7 +148,7 @@
         'PSA_ECC_FAMILY_BRAINPOOL_P_R1': (160, 192, 224, 256, 320, 384, 512),
         'PSA_ECC_FAMILY_MONTGOMERY': (255, 448),
         'PSA_ECC_FAMILY_TWISTED_EDWARDS': (255, 448),
-    }
+    } # type: Dict[str, Tuple[int, ...]]
     KEY_TYPE_SIZES = {
         'PSA_KEY_TYPE_AES': (128, 192, 256), # exhaustive
         'PSA_KEY_TYPE_ARC4': (8, 128, 2048), # extremes + sensible
@@ -160,7 +160,7 @@
         'PSA_KEY_TYPE_HMAC': (128, 160, 224, 256, 384, 512), # standard size for each supported hash
         'PSA_KEY_TYPE_RAW_DATA': (8, 40, 128), # sample
         'PSA_KEY_TYPE_RSA_KEY_PAIR': (1024, 1536), # small sample
-    }
+    } # type: Dict[str, Tuple[int, ...]]
     def sizes_to_test(self) -> Tuple[int, ...]:
         """Return a tuple of key sizes to test.
 
@@ -212,9 +212,7 @@
         This function does not currently handle key derivation or PAKE.
         """
         #pylint: disable=too-many-branches,too-many-return-statements
-        if alg.is_wildcard:
-            return False
-        if alg.is_invalid_truncation():
+        if not alg.is_valid_for_operation():
             return False
         if self.head == 'HMAC' and alg.head == 'HMAC':
             return True
@@ -495,6 +493,19 @@
                 return True
         return False
 
+    def is_valid_for_operation(self) -> bool:
+        """Whether this algorithm construction is valid for an operation.
+
+        This function assumes that the algorithm is constructed in a
+        "grammatically" correct way, and only rejects semantically invalid
+        combinations.
+        """
+        if self.is_wildcard:
+            return False
+        if self.is_invalid_truncation():
+            return False
+        return True
+
     def can_do(self, category: AlgorithmCategory) -> bool:
         """Whether this algorithm can perform operations in the given category.
         """
diff --git a/tests/compat.sh b/tests/compat.sh
index f6a51c4..02ffed2 100755
--- a/tests/compat.sh
+++ b/tests/compat.sh
@@ -1092,15 +1092,17 @@
     echo "$SERVER_CMD" > $SRV_OUT
     # for servers without -www or equivalent
     while :; do echo bla; sleep 1; done | $SERVER_CMD >> $SRV_OUT 2>&1 &
-    PROCESS_ID=$!
+    SRV_PID=$!
 
-    wait_server_start "$PORT" "$PROCESS_ID"
+    wait_server_start "$PORT" "$SRV_PID"
 }
 
 # terminate the running server
 stop_server() {
-    kill $PROCESS_ID 2>/dev/null
-    wait $PROCESS_ID 2>/dev/null
+    # For Ubuntu 22.04, `Terminated` message is outputed by wait command.
+    # To remove it from stdout, redirect stdout/stderr to SRV_OUT
+    kill $SRV_PID >/dev/null 2>&1
+    wait $SRV_PID >> $SRV_OUT 2>&1
 
     if [ "$MEMCHECK" -gt 0 ]; then
         if is_mbedtls "$SERVER_CMD" && has_mem_err $SRV_OUT; then
@@ -1116,7 +1118,7 @@
 # kill the running server (used when killed by signal)
 cleanup() {
     rm -f $SRV_OUT $CLI_OUT
-    kill $PROCESS_ID >/dev/null 2>&1
+    kill $SRV_PID >/dev/null 2>&1
     kill $WATCHDOG_PID >/dev/null 2>&1
     exit 1
 }
@@ -1129,11 +1131,13 @@
     ( sleep "$DOG_DELAY"; echo "TIMEOUT" >> $CLI_OUT; kill $CLI_PID ) &
     WATCHDOG_PID=$!
 
-    wait $CLI_PID
+    # For Ubuntu 22.04, `Terminated` message is outputed by wait command.
+    # To remove it from stdout, redirect stdout/stderr to CLI_OUT
+    wait $CLI_PID >> $CLI_OUT 2>&1
     EXIT=$?
 
-    kill $WATCHDOG_PID
-    wait $WATCHDOG_PID
+    kill $WATCHDOG_PID >/dev/null 2>&1
+    wait $WATCHDOG_PID >> $CLI_OUT 2>&1
 
     echo "EXIT: $EXIT" >> $CLI_OUT
 }
diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh
index 3266fea..97d4296 100755
--- a/tests/scripts/all.sh
+++ b/tests/scripts/all.sh
@@ -2728,6 +2728,19 @@
     make test
 }
 
+component_test_alt_timing() {
+    msg "build: alternate timing implementation"
+    scripts/config.py set MBEDTLS_TIMING_ALT
+    make lib TEST_TIMING_ALT_IMPL=1 CFLAGS="-I../tests/src/external_timing"
+
+    msg "test: MBEDTLS_TIMING_ALT - test suites"
+    make test TEST_TIMING_ALT_IMPL=1 CFLAGS="-I../tests/src/external_timing"
+
+    msg "selftest - MBEDTLS-TIMING_ALT"
+    make programs TEST_TIMING_ALT_IMPL=1 CFLAGS="-I../../tests/src/external_timing -I../tests/src/external_timing"
+    programs/test/selftest
+}
+
 component_test_platform_calloc_macro () {
     msg "build: MBEDTLS_PLATFORM_{CALLOC/FREE}_MACRO enabled (ASan build)"
     scripts/config.py set MBEDTLS_PLATFORM_MEMORY
diff --git a/tests/scripts/check_names.py b/tests/scripts/check_names.py
index f71a978..8c08e5c 100755
--- a/tests/scripts/check_names.py
+++ b/tests/scripts/check_names.py
@@ -264,7 +264,7 @@
             "3rdparty/everest/include/everest/everest.h",
             "3rdparty/everest/include/everest/x25519.h"
         ])
-        identifiers = self.parse_identifiers([
+        identifiers, excluded_identifiers = self.parse_identifiers([
             "include/mbedtls/*.h",
             "include/psa/*.h",
             "library/*.h",
@@ -302,6 +302,7 @@
             "private_macros": private_macros,
             "enum_consts": enum_consts,
             "identifiers": identifiers,
+            "excluded_identifiers": excluded_identifiers,
             "symbols": symbols,
             "mbed_psa_words": mbed_psa_words
         }
@@ -315,12 +316,42 @@
                 return True
         return False
 
-    def get_files(self, include_wildcards, exclude_wildcards):
+    def get_all_files(self, include_wildcards, exclude_wildcards):
         """
-        Get all files that match any of the UNIX-style wildcards. While the
-        check_names script is designed only for use on UNIX/macOS (due to nm),
-        this function alone would work fine on Windows even with forward slashes
-        in the wildcard.
+        Get all files that match any of the included UNIX-style wildcards
+        and filter them into included and excluded lists.
+        While the check_names script is designed only for use on UNIX/macOS
+        (due to nm), this function alone will work fine on Windows even with
+        forward slashes in the wildcard.
+
+        Args:
+        * include_wildcards: a List of shell-style wildcards to match filepaths.
+        * exclude_wildcards: a List of shell-style wildcards to exclude.
+
+        Returns:
+        * inc_files: A List of relative filepaths for included files.
+        * exc_files: A List of relative filepaths for excluded files.
+        """
+        accumulator = set()
+        all_wildcards = include_wildcards + (exclude_wildcards or [])
+        for wildcard in all_wildcards:
+            accumulator = accumulator.union(glob.iglob(wildcard))
+
+        inc_files = []
+        exc_files = []
+        for path in accumulator:
+            if self.is_file_excluded(path, exclude_wildcards):
+                exc_files.append(path)
+            else:
+                inc_files.append(path)
+        return (inc_files, exc_files)
+
+    def get_included_files(self, include_wildcards, exclude_wildcards):
+        """
+        Get all files that match any of the included UNIX-style wildcards.
+        While the check_names script is designed only for use on UNIX/macOS
+        (due to nm), this function alone will work fine on Windows even with
+        forward slashes in the wildcard.
 
         Args:
         * include_wildcards: a List of shell-style wildcards to match filepaths.
@@ -351,7 +382,7 @@
             "asm", "inline", "EMIT", "_CRT_SECURE_NO_DEPRECATE", "MULADDC_"
         )
 
-        files = self.get_files(include, exclude)
+        files = self.get_included_files(include, exclude)
         self.log.debug("Looking for macros in {} files".format(len(files)))
 
         macros = []
@@ -386,7 +417,7 @@
         mbed_regex = re.compile(r"\b(MBED.+?|PSA)_[A-Z0-9_]*")
         exclusions = re.compile(r"// *no-check-names|#error")
 
-        files = self.get_files(include, exclude)
+        files = self.get_included_files(include, exclude)
         self.log.debug(
             "Looking for MBED|PSA words in {} files"
             .format(len(files))
@@ -419,7 +450,7 @@
 
         Returns a List of Match objects for the findings.
         """
-        files = self.get_files(include, exclude)
+        files = self.get_included_files(include, exclude)
         self.log.debug("Looking for enum consts in {} files".format(len(files)))
 
         # Emulate a finite state machine to parse enum declarations.
@@ -602,23 +633,34 @@
         """
         Parse all lines of a header where a function/enum/struct/union/typedef
         identifier is declared, based on some regex and heuristics. Highly
-        dependent on formatting style.
+        dependent on formatting style. Identifiers in excluded files are still
+        parsed
 
         Args:
         * include: A List of glob expressions to look for files through.
         * exclude: A List of glob expressions for excluding files.
 
-        Returns a List of Match objects with identifiers.
+        Returns: a Tuple of two Lists of Match objects with identifiers.
+        * included_identifiers: A List of Match objects with identifiers from
+          included files.
+        * excluded_identifiers: A List of Match objects with identifiers from
+          excluded files.
         """
 
-        files = self.get_files(include, exclude)
-        self.log.debug("Looking for identifiers in {} files".format(len(files)))
+        included_files, excluded_files = \
+            self.get_all_files(include, exclude)
 
-        identifiers = []
-        for header_file in files:
-            self.parse_identifiers_in_file(header_file, identifiers)
+        self.log.debug("Looking for included identifiers in {} files".format \
+            (len(included_files)))
 
-        return identifiers
+        included_identifiers = []
+        excluded_identifiers = []
+        for header_file in included_files:
+            self.parse_identifiers_in_file(header_file, included_identifiers)
+        for header_file in excluded_files:
+            self.parse_identifiers_in_file(header_file, excluded_identifiers)
+
+        return (included_identifiers, excluded_identifiers)
 
     def parse_symbols(self):
         """
@@ -779,10 +821,12 @@
         Returns the number of problems that need fixing.
         """
         problems = []
+        all_identifiers = self.parse_result["identifiers"] +  \
+            self.parse_result["excluded_identifiers"]
 
         for symbol in self.parse_result["symbols"]:
             found_symbol_declared = False
-            for identifier_match in self.parse_result["identifiers"]:
+            for identifier_match in all_identifiers:
                 if symbol == identifier_match.name:
                     found_symbol_declared = True
                     break
diff --git a/tests/scripts/generate_psa_tests.py b/tests/scripts/generate_psa_tests.py
index a11dade..f5b921e 100755
--- a/tests/scripts/generate_psa_tests.py
+++ b/tests/scripts/generate_psa_tests.py
@@ -151,8 +151,8 @@
     tc.set_arguments([key_type] + list(args))
     return tc
 
-class NotSupported:
-    """Generate test cases for when something is not supported."""
+class KeyTypeNotSupported:
+    """Generate test cases for when a key type is not supported."""
 
     def __init__(self, info: Information) -> None:
         self.constructors = info.constructors
@@ -354,7 +354,7 @@
                 dependencies[i] = '!' + dep
         tc.set_dependencies(dependencies)
         tc.set_function(category.name.lower() + '_fail')
-        arguments = []
+        arguments = [] # type: List[str]
         if kt:
             key_material = kt.key_material(kt.sizes_to_test()[0])
             arguments += [key_type, test_case.hex_string(key_material)]
@@ -521,7 +521,7 @@
             key_type: psa_storage.Expr, bits: int,
             alg: psa_storage.Expr
     ) -> bool:
-        """Whether to the given key with the given algorithm.
+        """Whether to exercise the given key with the given algorithm.
 
         Normally only the type and algorithm matter for compatibility, and
         this is handled in crypto_knowledge.KeyType.can_do(). This function
@@ -902,7 +902,7 @@
         'test_suite_psa_crypto_generate_key.generated':
         lambda info: KeyGenerate(info).test_cases_for_key_generation(),
         'test_suite_psa_crypto_not_supported.generated':
-        lambda info: NotSupported(info).test_cases_for_not_supported(),
+        lambda info: KeyTypeNotSupported(info).test_cases_for_not_supported(),
         'test_suite_psa_crypto_op_fail.generated':
         lambda info: OpFail(info).all_test_cases(),
         'test_suite_psa_crypto_storage_format.current':
diff --git a/tests/scripts/list_internal_identifiers.py b/tests/scripts/list_internal_identifiers.py
index 779a16f..6b41607 100755
--- a/tests/scripts/list_internal_identifiers.py
+++ b/tests/scripts/list_internal_identifiers.py
@@ -46,7 +46,7 @@
     result = name_check.parse_identifiers([
         "include/mbedtls/*_internal.h",
         "library/*.h"
-    ])
+    ])[0]
     result.sort(key=lambda x: x.name)
 
     identifiers = ["{}\n".format(match.name) for match in result]
diff --git a/tests/src/external_timing/external_timing_for_test.c b/tests/src/external_timing/external_timing_for_test.c
new file mode 100644
index 0000000..454ebbe
--- /dev/null
+++ b/tests/src/external_timing/external_timing_for_test.c
@@ -0,0 +1,351 @@
+/** \file external_timing_for_test.c
+ *
+ * \brief Helper functions to test an alternate timing implementation.
+ */
+
+/*
+ *  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.
+ */
+
+#include <timing_alt.h>
+#if defined(MBEDTLS_TIMING_ALT)
+
+#if !defined(unix) && !defined(__unix__) && !defined(__unix) && \
+    !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) && \
+    !defined(__HAIKU__) && !defined(__midipix__)
+#error "This module only works on Unix and Windows, see MBEDTLS_TIMING_C in config.h"
+#endif
+
+/* *INDENT-OFF* */
+#ifndef asm
+#define asm __asm
+#endif
+/* *INDENT-ON* */
+
+#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
+
+#include <windows.h>
+#include <process.h>
+
+struct _hr_time {
+    LARGE_INTEGER start;
+};
+
+#else
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <signal.h>
+/* time.h should be included independently of MBEDTLS_HAVE_TIME. If the
+ * platform matches the ifdefs above, it will be used. */
+#include <time.h>
+#include <sys/time.h>
+struct _hr_time {
+    struct timeval start;
+};
+#endif /* _WIN32 && !EFIX64 && !EFI32 */
+
+#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
+    (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
+
+#define HAVE_HARDCLOCK
+
+unsigned long mbedtls_timing_hardclock(void)
+{
+    unsigned long tsc;
+    __asm   rdtsc
+    __asm   mov[tsc], eax
+    return tsc;
+}
+#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
+          ( _MSC_VER && _M_IX86 ) || __WATCOMC__ */
+
+/* some versions of mingw-64 have 32-bit longs even on x84_64 */
+#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
+    defined(__GNUC__) && (defined(__i386__) || (                       \
+    (defined(__amd64__) || defined(__x86_64__)) && __SIZEOF_LONG__ == 4))
+
+#define HAVE_HARDCLOCK
+
+unsigned long mbedtls_timing_hardclock(void)
+{
+    unsigned long lo, hi;
+    asm volatile ("rdtsc" : "=a" (lo), "=d" (hi));
+    return lo;
+}
+#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
+          __GNUC__ && __i386__ */
+
+#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
+    defined(__GNUC__) && (defined(__amd64__) || defined(__x86_64__))
+
+#define HAVE_HARDCLOCK
+
+unsigned long mbedtls_timing_hardclock(void)
+{
+    unsigned long lo, hi;
+    asm volatile ("rdtsc" : "=a" (lo), "=d" (hi));
+    return lo | (hi << 32);
+}
+#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
+          __GNUC__ && ( __amd64__ || __x86_64__ ) */
+
+#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
+    defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__))
+
+#define HAVE_HARDCLOCK
+
+unsigned long mbedtls_timing_hardclock(void)
+{
+    unsigned long tbl, tbu0, tbu1;
+
+    do {
+        asm volatile ("mftbu %0" : "=r" (tbu0));
+        asm volatile ("mftb  %0" : "=r" (tbl));
+        asm volatile ("mftbu %0" : "=r" (tbu1));
+    } while (tbu0 != tbu1);
+
+    return tbl;
+}
+#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
+          __GNUC__ && ( __powerpc__ || __ppc__ ) */
+
+#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
+    defined(__GNUC__) && defined(__sparc64__)
+
+#if defined(__OpenBSD__)
+#warning OpenBSD does not allow access to tick register using software version instead
+#else
+#define HAVE_HARDCLOCK
+
+unsigned long mbedtls_timing_hardclock(void)
+{
+    unsigned long tick;
+    asm volatile ("rdpr %%tick, %0;" : "=&r" (tick));
+    return tick;
+}
+#endif /* __OpenBSD__ */
+#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
+          __GNUC__ && __sparc64__ */
+
+#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
+    defined(__GNUC__) && defined(__sparc__) && !defined(__sparc64__)
+
+#define HAVE_HARDCLOCK
+
+unsigned long mbedtls_timing_hardclock(void)
+{
+    unsigned long tick;
+    asm volatile (".byte 0x83, 0x41, 0x00, 0x00");
+    asm volatile ("mov   %%g1, %0" : "=r" (tick));
+    return tick;
+}
+#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
+          __GNUC__ && __sparc__ && !__sparc64__ */
+
+#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&      \
+    defined(__GNUC__) && defined(__alpha__)
+
+#define HAVE_HARDCLOCK
+
+unsigned long mbedtls_timing_hardclock(void)
+{
+    unsigned long cc;
+    asm volatile ("rpcc %0" : "=r" (cc));
+    return cc & 0xFFFFFFFF;
+}
+#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
+          __GNUC__ && __alpha__ */
+
+#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&      \
+    defined(__GNUC__) && defined(__ia64__)
+
+#define HAVE_HARDCLOCK
+
+unsigned long mbedtls_timing_hardclock(void)
+{
+    unsigned long itc;
+    asm volatile ("mov %0 = ar.itc" : "=r" (itc));
+    return itc;
+}
+#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
+          __GNUC__ && __ia64__ */
+
+#if !defined(HAVE_HARDCLOCK) && defined(_MSC_VER) && \
+    !defined(EFIX64) && !defined(EFI32)
+
+#define HAVE_HARDCLOCK
+
+unsigned long mbedtls_timing_hardclock(void)
+{
+    LARGE_INTEGER offset;
+
+    QueryPerformanceCounter(&offset);
+
+    return (unsigned long) (offset.QuadPart);
+}
+#endif /* !HAVE_HARDCLOCK && _MSC_VER && !EFIX64 && !EFI32 */
+
+#if !defined(HAVE_HARDCLOCK)
+
+#define HAVE_HARDCLOCK
+
+static int hardclock_init = 0;
+static struct timeval tv_init;
+
+unsigned long mbedtls_timing_hardclock(void)
+{
+    struct timeval tv_cur;
+
+    if (hardclock_init == 0) {
+        gettimeofday(&tv_init, NULL);
+        hardclock_init = 1;
+    }
+
+    gettimeofday(&tv_cur, NULL);
+    return (tv_cur.tv_sec  - tv_init.tv_sec) * 1000000U
+           + (tv_cur.tv_usec - tv_init.tv_usec);
+}
+#endif /* !HAVE_HARDCLOCK */
+
+volatile int mbedtls_timing_alarmed = 0;
+
+#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
+
+unsigned long mbedtls_timing_get_timer(struct mbedtls_timing_hr_time *val, int reset)
+{
+    struct _hr_time *t = (struct _hr_time *) val;
+
+    if (reset) {
+        QueryPerformanceCounter(&t->start);
+        return 0;
+    } else {
+        unsigned long delta;
+        LARGE_INTEGER now, hfreq;
+        QueryPerformanceCounter(&now);
+        QueryPerformanceFrequency(&hfreq);
+        delta = (unsigned long) ((now.QuadPart - t->start.QuadPart) * 1000ul
+                                 / hfreq.QuadPart);
+        return delta;
+    }
+}
+
+/* It's OK to use a global because alarm() is supposed to be global anyway */
+static DWORD alarmMs;
+
+static void TimerProc(void *TimerContext)
+{
+    (void) TimerContext;
+    Sleep(alarmMs);
+    mbedtls_timing_alarmed = 1;
+    /* _endthread will be called implicitly on return
+     * That ensures execution of thread function's epilogue */
+}
+
+void mbedtls_set_alarm(int seconds)
+{
+    if (seconds == 0) {
+        /* No need to create a thread for this simple case.
+         * Also, this shorcut is more reliable at least on MinGW32 */
+        mbedtls_timing_alarmed = 1;
+        return;
+    }
+
+    mbedtls_timing_alarmed = 0;
+    alarmMs = seconds * 1000;
+    (void) _beginthread(TimerProc, 0, NULL);
+}
+
+#else /* _WIN32 && !EFIX64 && !EFI32 */
+
+unsigned long mbedtls_timing_get_timer(struct mbedtls_timing_hr_time *val, int reset)
+{
+    struct _hr_time *t = (struct _hr_time *) val;
+
+    if (reset) {
+        gettimeofday(&t->start, NULL);
+        return 0;
+    } else {
+        unsigned long delta;
+        struct timeval now;
+        gettimeofday(&now, NULL);
+        delta = (now.tv_sec  - t->start.tv_sec) * 1000ul
+                + (now.tv_usec - t->start.tv_usec) / 1000;
+        return delta;
+    }
+}
+
+static void sighandler(int signum)
+{
+    mbedtls_timing_alarmed = 1;
+    signal(signum, sighandler);
+}
+
+void mbedtls_set_alarm(int seconds)
+{
+    mbedtls_timing_alarmed = 0;
+    signal(SIGALRM, sighandler);
+    alarm(seconds);
+    if (seconds == 0) {
+        /* alarm(0) cancelled any previous pending alarm, but the
+           handler won't fire, so raise the flag straight away. */
+        mbedtls_timing_alarmed = 1;
+    }
+}
+
+#endif /* _WIN32 && !EFIX64 && !EFI32 */
+
+/*
+ * Set delays to watch
+ */
+void mbedtls_timing_set_delay(void *data, uint32_t int_ms, uint32_t fin_ms)
+{
+    mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data;
+
+    ctx->int_ms = int_ms;
+    ctx->fin_ms = fin_ms;
+
+    if (fin_ms != 0) {
+        (void) mbedtls_timing_get_timer(&ctx->timer, 1);
+    }
+}
+
+/*
+ * Get number of delays expired
+ */
+int mbedtls_timing_get_delay(void *data)
+{
+    mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data;
+    unsigned long elapsed_ms;
+
+    if (ctx->fin_ms == 0) {
+        return -1;
+    }
+
+    elapsed_ms = mbedtls_timing_get_timer(&ctx->timer, 0);
+
+    if (elapsed_ms >= ctx->fin_ms) {
+        return 2;
+    }
+
+    if (elapsed_ms >= ctx->int_ms) {
+        return 1;
+    }
+
+    return 0;
+}
+
+#endif /* MBEDTLS_TIMING_ALT */
diff --git a/tests/src/external_timing/timing_alt.h b/tests/src/external_timing/timing_alt.h
new file mode 100644
index 0000000..82e8c8b
--- /dev/null
+++ b/tests/src/external_timing/timing_alt.h
@@ -0,0 +1,130 @@
+/*
+ * Copy of the internal MbedTLS timing implementation, to be used in tests.
+ */
+/*
+ *  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 EXTERNAL_TIMING_FOR_TEST_H
+#define EXTERNAL_TIMING_FOR_TEST_H
+
+#if !defined(MBEDTLS_CONFIG_FILE)
+#include "mbedtls/config.h"
+#else
+#include MBEDTLS_CONFIG_FILE
+#endif
+
+#include <stdint.h>
+
+/**
+ * \brief          timer structure
+ */
+struct mbedtls_timing_hr_time {
+    unsigned char opaque[32];
+};
+
+/**
+ * \brief          Context for mbedtls_timing_set/get_delay()
+ */
+typedef struct mbedtls_timing_delay_context {
+    struct mbedtls_timing_hr_time   timer;
+    uint32_t                        int_ms;
+    uint32_t                        fin_ms;
+} mbedtls_timing_delay_context;
+
+#else  /* MBEDTLS_TIMING_ALT */
+#include "timing_alt.h"
+#endif /* MBEDTLS_TIMING_ALT */
+
+extern volatile int mbedtls_timing_alarmed;
+
+/**
+ * \brief          Return the CPU cycle counter value
+ *
+ * \warning        This is only a best effort! Do not rely on this!
+ *                 In particular, it is known to be unreliable on virtual
+ *                 machines.
+ *
+ * \note           This value starts at an unspecified origin and
+ *                 may wrap around.
+ */
+unsigned long mbedtls_timing_hardclock(void);
+
+/**
+ * \brief          Return the elapsed time in milliseconds
+ *
+ * \param val      points to a timer structure
+ * \param reset    If 0, query the elapsed time. Otherwise (re)start the timer.
+ *
+ * \return         Elapsed time since the previous reset in ms. When
+ *                 restarting, this is always 0.
+ *
+ * \note           To initialize a timer, call this function with reset=1.
+ *
+ *                 Determining the elapsed time and resetting the timer is not
+ *                 atomic on all platforms, so after the sequence
+ *                 `{ get_timer(1); ...; time1 = get_timer(1); ...; time2 =
+ *                 get_timer(0) }` the value time1+time2 is only approximately
+ *                 the delay since the first reset.
+ */
+unsigned long mbedtls_timing_get_timer(struct mbedtls_timing_hr_time *val, int reset);
+
+/**
+ * \brief          Setup an alarm clock
+ *
+ * \param seconds  delay before the "mbedtls_timing_alarmed" flag is set
+ *                 (must be >=0)
+ *
+ * \warning        Only one alarm at a time  is supported. In a threaded
+ *                 context, this means one for the whole process, not one per
+ *                 thread.
+ */
+void mbedtls_set_alarm(int seconds);
+
+/**
+ * \brief          Set a pair of delays to watch
+ *                 (See \c mbedtls_timing_get_delay().)
+ *
+ * \param data     Pointer to timing data.
+ *                 Must point to a valid \c mbedtls_timing_delay_context struct.
+ * \param int_ms   First (intermediate) delay in milliseconds.
+ *                 The effect if int_ms > fin_ms is unspecified.
+ * \param fin_ms   Second (final) delay in milliseconds.
+ *                 Pass 0 to cancel the current delay.
+ *
+ * \note           To set a single delay, either use \c mbedtls_timing_set_timer
+ *                 directly or use this function with int_ms == fin_ms.
+ */
+void mbedtls_timing_set_delay(void *data, uint32_t int_ms, uint32_t fin_ms);
+
+/**
+ * \brief          Get the status of delays
+ *                 (Memory helper: number of delays passed.)
+ *
+ * \param data     Pointer to timing data
+ *                 Must point to a valid \c mbedtls_timing_delay_context struct.
+ *
+ * \return         -1 if cancelled (fin_ms = 0),
+ *                  0 if none of the delays are passed,
+ *                  1 if only the intermediate delay is passed,
+ *                  2 if the final delay is passed.
+ */
+int mbedtls_timing_get_delay(void *data);
+
+#ifdef __cplusplus
+}
+
+#endif /* EXTERNAL_TIMING_FOR_TEST_H */
diff --git a/tests/suites/test_suite_x509write.data b/tests/suites/test_suite_x509write.data
index aa8b3cd..4cff30d 100644
--- a/tests/suites/test_suite_x509write.data
+++ b/tests/suites/test_suite_x509write.data
@@ -132,3 +132,6 @@
 
 X509 String to Names #6 (Escape at end)
 mbedtls_x509_string_to_names:"C=NL, O=Offspark\":"":MBEDTLS_ERR_X509_INVALID_NAME
+
+Check max serial length
+x509_set_serial_check:
diff --git a/tests/suites/test_suite_x509write.function b/tests/suites/test_suite_x509write.function
index 2896a52..cb3f6a5 100644
--- a/tests/suites/test_suite_x509write.function
+++ b/tests/suites/test_suite_x509write.function
@@ -425,6 +425,26 @@
 }
 /* END_CASE */
 
+/* BEGIN_CASE depends_on:MBEDTLS_X509_CRT_WRITE_C */
+void x509_set_serial_check()
+{
+    mbedtls_x509write_cert ctx;
+    mbedtls_mpi serial_mpi;
+    uint8_t invalid_serial[MBEDTLS_X509_RFC5280_MAX_SERIAL_LEN + 1];
+
+    memset(invalid_serial, 0x01, sizeof(invalid_serial));
+
+    mbedtls_mpi_init(&serial_mpi);
+    TEST_EQUAL(mbedtls_mpi_read_binary(&serial_mpi, invalid_serial,
+                                       sizeof(invalid_serial)), 0);
+    TEST_EQUAL(mbedtls_x509write_crt_set_serial(&ctx, &serial_mpi),
+               MBEDTLS_ERR_X509_BAD_INPUT_DATA);
+
+exit:
+    mbedtls_mpi_free(&serial_mpi);
+}
+/* END_CASE */
+
 /* BEGIN_CASE depends_on:MBEDTLS_X509_CREATE_C:MBEDTLS_X509_USE_C */
 void mbedtls_x509_string_to_names(char *name, char *parsed_name, int result
                                   )