Merge pull request #2868 from k-stachowiak/fix-resource-leak-in-ssl-example

Fix potential resource leak in sslserver2 example
diff --git a/crypto b/crypto
index 3f20efc..0b3dd8d 160000
--- a/crypto
+++ b/crypto
@@ -1 +1 @@
-Subproject commit 3f20efc03016b38f2677dadd476b21229c627c80
+Subproject commit 0b3dd8d0249adb54abc7ad46303f3c22e44aefb7
diff --git a/docs/architecture/.gitignore b/docs/architecture/.gitignore
new file mode 100644
index 0000000..23f832b
--- /dev/null
+++ b/docs/architecture/.gitignore
@@ -0,0 +1,2 @@
+*.html
+*.pdf
diff --git a/docs/architecture/Makefile b/docs/architecture/Makefile
new file mode 100644
index 0000000..2b2a11b
--- /dev/null
+++ b/docs/architecture/Makefile
@@ -0,0 +1,19 @@
+PANDOC = pandoc
+
+default: all
+
+all_markdown = \
+	       testing/test-framework.md \
+	       # This line is intentionally left blank
+
+html: $(all_markdown:.md=.html)
+pdf: $(all_markdown:.md=.pdf)
+all: html pdf
+
+.SUFFIXES:
+.SUFFIXES: .md .html .pdf
+
+.md.html:
+	$(PANDOC) -o $@ $<
+.md.pdf:
+	$(PANDOC) -o $@ $<
diff --git a/docs/architecture/testing/test-framework.md b/docs/architecture/testing/test-framework.md
new file mode 100644
index 0000000..e0e960f
--- /dev/null
+++ b/docs/architecture/testing/test-framework.md
@@ -0,0 +1,58 @@
+# Mbed TLS test framework
+
+This document is an overview of the Mbed TLS test framework and test tools.
+
+This document is incomplete. You can help by expanding it.
+
+## Unit tests
+
+See <https://tls.mbed.org/kb/development/test_suites>
+
+### Unit test descriptions
+
+Each test case has a description which succinctly describes for a human audience what the test does. The first non-comment line of each paragraph in a `.data` file is the test description. The following rules and guidelines apply:
+
+* Test descriptions may not contain semicolons, line breaks and other control characters, or non-ASCII characters. <br>
+  Rationale: keep the tools that process test descriptions (`generate_test_code.py`, [outcome file](#outcome-file) tools) simple.
+* Test descriptions must be unique within a `.data` file. If you can't think of a better description, the convention is to append `#1`, `#2`, etc. <br>
+  Rationale: make it easy to relate a failure log to the test data. Avoid confusion between cases in the [outcome file](#outcome-file).
+* Test descriptions should be a maximum of **66 characters**. <br>
+  Rationale: 66 characters is what our various tools assume (leaving room for 14 more characters on an 80-column line). Longer descriptions may be truncated or may break a visual alignment. <br>
+  We have a lot of test cases with longer descriptions, but they should be avoided. At least please make sure that the first 66 characters describe the test uniquely.
+* Make the description descriptive. “foo: x=2, y=4” is more descriptive than “foo #2”. “foo: 0<x<y, both even” is even better if these inequalities and parities are why this particular test data was chosen.
+* Avoid changing the description of an existing test case without a good reason. This breaks the tracking of failures across CI runs, since this tracking is based on the descriptions.
+
+`tests/scripts/check-test-cases.py` enforces some rules and warns if some guidelines are violated.
+
+## TLS tests
+
+### SSL extension tests
+
+#### SSL test case descriptions
+
+Each test case in `ssl-opt.sh` has a description which succinctly describes for a human audience what the test does. The test description is the first parameter to `run_tests`.
+
+The same rules and guidelines apply as for [unit test descriptions](#unit-test-descriptions). In addition, the description must be written on the same line as `run_test`, in double quotes, for the sake of `check-test-cases.py`.
+
+## Running tests
+
+### Outcome file
+
+#### Generating an outcome file
+
+Unit tests and `ssl-opt.sh` record the outcome of each test case in a **test outcome file**. This feature is enabled if the environment variable `MBEDTLS_TEST_OUTCOME_FILE` is set. Set it to the path of the desired file.
+
+If you run `all.sh --outcome-file test-outcome.csv`, this collects the outcome of all the test cases in `test-outcome.csv`.
+
+#### Outcome file format
+
+The outcome file is in a CSV format using `;` (semicolon) as the delimiter and no quoting. This means that fields may not contain newlines or semicolons. There is no title line.
+
+The outcome file has 6 fields:
+
+* **Platform**: a description of the platform, e.g. `Linux-x86_64` or `Linux-x86_64-gcc7-msan`.
+* **Configuration**: a unique description of the configuration (`config.h`).
+* **Test suite**: `test_suite_xxx` or `ssl-opt`.
+* **Test case**: the description of the test case.
+* **Result**: one of `PASS`, `SKIP` or `FAIL`.
+* **Cause**: more information explaining the result.
diff --git a/library/Makefile b/library/Makefile
index 501421f..bc9363d 100644
--- a/library/Makefile
+++ b/library/Makefile
@@ -146,17 +146,22 @@
 	echo "  LN    $@ -> $<"
 	ln -sf $< $@
 
-libmbedx509.dylib: $(OBJS_X509) libmbedcrypto.dylib
+libmbedx509.dylib: $(OBJS_X509) $(CRYPTO)libmbedcrypto.dylib
 	echo "  LD    $@"
 	$(CC) -dynamiclib -L. -lmbedcrypto  $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ $(OBJS_X509)
 
-libmbedx509.dll: $(OBJS_X509) libmbedcrypto.dll
+libmbedx509.dll: $(OBJS_X509) $(CRYPTO)libmbedcrypto.dll
 	echo "  LD    $@"
 	$(CC) -shared -Wl,-soname,$@ -Wl,--out-implib,$@.a -o $@ $(OBJS_X509) -lws2_32 -lwinmm -lgdi32 -L. -lmbedcrypto -static-libgcc $(LOCAL_LDFLAGS) $(LDFLAGS)
 
 libmbedcrypto.%:
 	$(MAKE) CRYPTO_INCLUDES:="-I../../include -I../include" -C ../crypto/library $@
 
+libmbedcrypto.$(DLEXT): $(CRYPTO)libmbedcrypto.$(DLEXT)
+
+$(CRYPTO)libmbedcrypto.$(DLEXT): | libmbedcrypto.a
+	$(MAKE) CRYPTO_INCLUDES:="-I../../include -I../include" -C ../crypto/library libmbedcrypto.$(DLEXT)
+
 .c.o:
 	echo "  CC    $<"
 	$(CC) $(LOCAL_CFLAGS) $(CFLAGS) -o $@ -c $<
diff --git a/library/error.c b/library/error.c
index 23a0f97..d8b5780 100644
--- a/library/error.c
+++ b/library/error.c
@@ -642,7 +642,7 @@
     if( use_ret == -(MBEDTLS_ERR_ASN1_LENGTH_MISMATCH) )
         mbedtls_snprintf( buf, buflen, "ASN1 - Actual length differs from expected length" );
     if( use_ret == -(MBEDTLS_ERR_ASN1_INVALID_DATA) )
-        mbedtls_snprintf( buf, buflen, "ASN1 - Data is invalid. (not used)" );
+        mbedtls_snprintf( buf, buflen, "ASN1 - Data is invalid" );
     if( use_ret == -(MBEDTLS_ERR_ASN1_ALLOC_FAILED) )
         mbedtls_snprintf( buf, buflen, "ASN1 - Memory allocation failed" );
     if( use_ret == -(MBEDTLS_ERR_ASN1_BUF_TOO_SMALL) )
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index a7facb8..dc39a96 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -711,9 +711,18 @@
         if( status != PSA_SUCCESS )
             return( status );
 
-        status = psa_key_derivation_input_key( derivation,
-                                               PSA_KEY_DERIVATION_INPUT_SECRET,
-                                               slot );
+        if( slot == 0 )
+        {
+            status = psa_key_derivation_input_bytes(
+                derivation, PSA_KEY_DERIVATION_INPUT_SECRET,
+                NULL, 0 );
+        }
+        else
+        {
+            status = psa_key_derivation_input_key(
+                derivation, PSA_KEY_DERIVATION_INPUT_SECRET,
+                slot );
+        }
         if( status != PSA_SUCCESS )
             return( status );
 
@@ -743,8 +752,7 @@
 {
     psa_status_t status;
     psa_algorithm_t alg;
-    psa_key_attributes_t key_attributes;
-    psa_key_handle_t master_slot;
+    psa_key_handle_t master_slot = 0;
     psa_key_derivation_operation_t derivation =
         PSA_KEY_DERIVATION_OPERATION_INIT;
 
@@ -753,14 +761,24 @@
     else
         alg = PSA_ALG_TLS12_PRF(PSA_ALG_SHA_256);
 
-    key_attributes = psa_key_attributes_init();
-    psa_set_key_usage_flags( &key_attributes, PSA_KEY_USAGE_DERIVE );
-    psa_set_key_algorithm( &key_attributes, alg );
-    psa_set_key_type( &key_attributes, PSA_KEY_TYPE_DERIVE );
+    /* Normally a "secret" should be long enough to be impossible to
+     * find by brute force, and in particular should not be empty. But
+     * this PRF is also used to derive an IV, in particular in EAP-TLS,
+     * and for this use case it makes sense to have a 0-length "secret".
+     * Since the key API doesn't allow importing a key of length 0,
+     * keep master_slot=0, which setup_psa_key_derivation() understands
+     * to mean a 0-length "secret" input. */
+    if( slen != 0 )
+    {
+        psa_key_attributes_t key_attributes = psa_key_attributes_init();
+        psa_set_key_usage_flags( &key_attributes, PSA_KEY_USAGE_DERIVE );
+        psa_set_key_algorithm( &key_attributes, alg );
+        psa_set_key_type( &key_attributes, PSA_KEY_TYPE_DERIVE );
 
-    status = psa_import_key( &key_attributes, secret, slen, &master_slot );
-    if( status != PSA_SUCCESS )
-        return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
+        status = psa_import_key( &key_attributes, secret, slen, &master_slot );
+        if( status != PSA_SUCCESS )
+            return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
+    }
 
     status = setup_psa_key_derivation( &derivation,
                                        master_slot, alg,
@@ -790,7 +808,8 @@
         return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
     }
 
-    status = psa_destroy_key( master_slot );
+    if( master_slot != 0 )
+        status = psa_destroy_key( master_slot );
     if( status != PSA_SUCCESS )
         return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
 
@@ -4859,6 +4878,25 @@
             ( (uint64_t) buf[5]       ) );
 }
 
+static int mbedtls_ssl_dtls_record_replay_check( mbedtls_ssl_context *ssl, uint8_t *record_in_ctr )
+{
+    int ret;
+    unsigned char *original_in_ctr;
+
+    // save original in_ctr
+    original_in_ctr = ssl->in_ctr;
+
+    // use counter from record
+    ssl->in_ctr = record_in_ctr;
+
+    ret = mbedtls_ssl_dtls_replay_check( (mbedtls_ssl_context const *) ssl );
+
+    // restore the counter
+    ssl->in_ctr = original_in_ctr;
+
+    return ret;
+}
+
 /*
  * Return 0 if sequence number is acceptable, -1 otherwise
  */
@@ -5364,7 +5402,8 @@
 #if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY)
         /* For records from the correct epoch, check whether their
          * sequence number has been seen before. */
-        else if( mbedtls_ssl_dtls_replay_check( ssl ) != 0 )
+        else if( mbedtls_ssl_dtls_record_replay_check( (mbedtls_ssl_context *) ssl,
+            &rec->ctr[0] ) != 0 )
         {
             MBEDTLS_SSL_DEBUG_MSG( 1, ( "replayed record" ) );
             return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD );
diff --git a/library/x509write_crt.c b/library/x509write_crt.c
index 03fb3fd..0a2357a 100644
--- a/library/x509write_crt.c
+++ b/library/x509write_crt.c
@@ -45,16 +45,6 @@
 #include "mbedtls/pem.h"
 #endif /* MBEDTLS_PEM_WRITE_C */
 
-/*
- * For the currently used signature algorithms the buffer to store any signature
- * must be at least of size MAX(MBEDTLS_ECDSA_MAX_LEN, MBEDTLS_MPI_MAX_SIZE)
- */
-#if MBEDTLS_ECDSA_MAX_LEN > MBEDTLS_MPI_MAX_SIZE
-#define SIGNATURE_MAX_SIZE MBEDTLS_ECDSA_MAX_LEN
-#else
-#define SIGNATURE_MAX_SIZE MBEDTLS_MPI_MAX_SIZE
-#endif
-
 void mbedtls_x509write_crt_init( mbedtls_x509write_cert *ctx )
 {
     memset( ctx, 0, sizeof( mbedtls_x509write_cert ) );
@@ -347,7 +337,7 @@
     size_t sig_oid_len = 0;
     unsigned char *c, *c2;
     unsigned char hash[64];
-    unsigned char sig[SIGNATURE_MAX_SIZE];
+    unsigned char sig[MBEDTLS_PK_SIGNATURE_MAX_SIZE];
     size_t sub_len = 0, pub_len = 0, sig_and_oid_len = 0, sig_len;
     size_t len = 0;
     mbedtls_pk_type_t pk_alg;
diff --git a/library/x509write_csr.c b/library/x509write_csr.c
index 0d62d1d..23e3f78 100644
--- a/library/x509write_csr.c
+++ b/library/x509write_csr.c
@@ -49,16 +49,6 @@
 #include "mbedtls/pem.h"
 #endif
 
-/*
- * For the currently used signature algorithms the buffer to store any signature
- * must be at least of size MAX(MBEDTLS_ECDSA_MAX_LEN, MBEDTLS_MPI_MAX_SIZE)
- */
-#if MBEDTLS_ECDSA_MAX_LEN > MBEDTLS_MPI_MAX_SIZE
-#define SIGNATURE_MAX_SIZE MBEDTLS_ECDSA_MAX_LEN
-#else
-#define SIGNATURE_MAX_SIZE MBEDTLS_MPI_MAX_SIZE
-#endif
-
 void mbedtls_x509write_csr_init( mbedtls_x509write_csr *ctx )
 {
     memset( ctx, 0, sizeof( mbedtls_x509write_csr ) );
@@ -148,7 +138,7 @@
     size_t sig_oid_len = 0;
     unsigned char *c, *c2;
     unsigned char hash[64];
-    unsigned char sig[SIGNATURE_MAX_SIZE];
+    unsigned char sig[MBEDTLS_PK_SIGNATURE_MAX_SIZE];
     unsigned char tmp_buf[2048];
     size_t pub_len = 0, sig_and_oid_len = 0, sig_len;
     size_t len = 0;
@@ -274,17 +264,16 @@
                        void *p_rng )
 {
     int ret;
-    unsigned char output_buf[4096];
     size_t olen = 0;
 
-    if( ( ret = mbedtls_x509write_csr_der( ctx, output_buf, sizeof(output_buf),
+    if( ( ret = mbedtls_x509write_csr_der( ctx, buf, size,
                                    f_rng, p_rng ) ) < 0 )
     {
         return( ret );
     }
 
     if( ( ret = mbedtls_pem_write_buffer( PEM_BEGIN_CSR, PEM_END_CSR,
-                                  output_buf + sizeof(output_buf) - ret,
+                                  buf + size - ret,
                                   ret, buf, size, &olen ) ) != 0 )
     {
         return( ret );
diff --git a/programs/Makefile b/programs/Makefile
index dce970b..188c2be 100644
--- a/programs/Makefile
+++ b/programs/Makefile
@@ -23,12 +23,6 @@
 LOCAL_CFLAGS += $(patsubst -I../3rdparty/%, -I../crypto/3rdparty/%, $(THIRDPARTY_INCLUDES))
 LOCAL_CFLAGS += $(patsubst -I../3rdparty/%, -I../crypto/3rdparty/%, $(THIRDPARTY_INCLUDES))
 
-ifndef SHARED
-DEP=../crypto/library/libmbedcrypto.a ../library/libmbedx509.a ../library/libmbedtls.a
-else
-DEP=../crypto/library/libmbedcrypto.$(DLEXT) ../library/libmbedx509.$(DLEXT) ../library/libmbedtls.$(DLEXT)
-endif
-
 ifdef DEBUG
 LOCAL_CFLAGS += -g3
 endif
@@ -51,6 +45,12 @@
 SHARED_SUFFIX=
 endif
 
+ifndef SHARED
+DEP=../crypto/library/libmbedcrypto.a ../library/libmbedx509.a ../library/libmbedtls.a
+else
+DEP=../crypto/library/libmbedcrypto.$(DLEXT) ../library/libmbedx509.$(DLEXT) ../library/libmbedtls.$(DLEXT)
+endif
+
 # Zlib shared library extensions:
 ifdef ZLIB
 LOCAL_LDFLAGS += -lz
diff --git a/programs/pkey/pk_sign.c b/programs/pkey/pk_sign.c
index bdedca4..a354e5b 100644
--- a/programs/pkey/pk_sign.c
+++ b/programs/pkey/pk_sign.c
@@ -60,17 +60,6 @@
 #include <stdio.h>
 #include <string.h>
 
-
-/*
- * For the currently used signature algorithms the buffer to store any signature
- * must be at least of size MAX(MBEDTLS_ECDSA_MAX_LEN, MBEDTLS_MPI_MAX_SIZE)
- */
-#if MBEDTLS_ECDSA_MAX_LEN > MBEDTLS_MPI_MAX_SIZE
-#define SIGNATURE_MAX_SIZE MBEDTLS_ECDSA_MAX_LEN
-#else
-#define SIGNATURE_MAX_SIZE MBEDTLS_MPI_MAX_SIZE
-#endif
-
 int main( int argc, char *argv[] )
 {
     FILE *f;
@@ -80,7 +69,7 @@
     mbedtls_entropy_context entropy;
     mbedtls_ctr_drbg_context ctr_drbg;
     unsigned char hash[32];
-    unsigned char buf[SIGNATURE_MAX_SIZE];
+    unsigned char buf[MBEDTLS_PK_SIGNATURE_MAX_SIZE];
     char filename[512];
     const char *pers = "mbedtls_pk_sign";
     size_t olen = 0;
diff --git a/programs/pkey/pk_verify.c b/programs/pkey/pk_verify.c
index a6bfe3f..72caf71 100644
--- a/programs/pkey/pk_verify.c
+++ b/programs/pkey/pk_verify.c
@@ -65,7 +65,7 @@
     size_t i;
     mbedtls_pk_context pk;
     unsigned char hash[32];
-    unsigned char buf[MBEDTLS_MPI_MAX_SIZE];
+    unsigned char buf[MBEDTLS_PK_SIGNATURE_MAX_SIZE];
     char filename[512];
 
     mbedtls_pk_init( &pk );
diff --git a/tests/Makefile b/tests/Makefile
index 0bed6b1..9698b80 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -25,12 +25,6 @@
 # on non-POSIX platforms.
 LOCAL_CFLAGS += -D_POSIX_C_SOURCE=200809L
 
-ifndef SHARED
-DEP=$(CRYPTO)libmbedcrypto.a ../library/libmbedx509.a ../library/libmbedtls.a
-else
-DEP=$(CRYPTO)libmbedcrypto.$(DLEXT) ../library/libmbedx509.$(DLEXT) ../library/libmbedtls.$(DLEXT)
-endif
-
 ifdef DEBUG
 LOCAL_CFLAGS += -g3
 endif
@@ -56,6 +50,12 @@
 PYTHON ?= python2
 endif
 
+ifndef SHARED
+DEP=$(CRYPTO)libmbedcrypto.a ../library/libmbedx509.a ../library/libmbedtls.a
+else
+DEP=$(CRYPTO)libmbedcrypto.$(DLEXT) ../library/libmbedx509.$(DLEXT) ../library/libmbedtls.$(DLEXT)
+endif
+
 # Zlib shared library extensions:
 ifdef ZLIB
 LOCAL_LDFLAGS += -lz
diff --git a/tests/data_files/Makefile b/tests/data_files/Makefile
index e75bf81..99d64eb 100644
--- a/tests/data_files/Makefile
+++ b/tests/data_files/Makefile
@@ -869,14 +869,6 @@
 	$(OPENSSL) pkey -in $< -inform DER -out $@
 all_final += ec_prv.pk8param.pem
 
-###
-### A generic SECP521R1 private key
-###
-
-secp521r1_prv.der:
-	$(OPENSSL) ecparam -genkey -name secp521r1 -noout -out secp521r1_prv.der
-all_final += secp521r1_prv.der
-
 ################################################################
 ### Generate CSRs for X.509 write test suite
 ################################################################
diff --git a/tests/data_files/secp521r1_prv.der b/tests/data_files/secp521r1_prv.der
deleted file mode 100644
index 4d342bd..0000000
--- a/tests/data_files/secp521r1_prv.der
+++ /dev/null
Binary files differ
diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh
index 7f7c4bd..e76b9d4 100755
--- a/tests/scripts/all.sh
+++ b/tests/scripts/all.sh
@@ -117,10 +117,16 @@
     CONFIG_H='include/mbedtls/config.h'
     CONFIG_BAK="$CONFIG_H.bak"
 
+    append_outcome=0
     MEMORY=0
     FORCE=0
     KEEP_GOING=0
 
+    : ${MBEDTLS_TEST_OUTCOME_FILE=}
+    : ${MBEDTLS_TEST_PLATFORM="$(uname -s | tr -c \\n0-9A-Za-z _)-$(uname -m | tr -c \\n0-9A-Za-z _)"}
+    export MBEDTLS_TEST_OUTCOME_FILE
+    export MBEDTLS_TEST_PLATFORM
+
     # Default commands, can be overridden by the environment
     : ${OPENSSL:="openssl"}
     : ${OPENSSL_LEGACY:="$OPENSSL"}
@@ -138,6 +144,9 @@
         export MAKEFLAGS="-j"
     fi
 
+    # CFLAGS and LDFLAGS for Asan builds that don't use CMake
+    ASAN_CFLAGS='-Werror -Wall -Wextra -fsanitize=address,undefined -fno-sanitize-recover=all'
+
     # Gather the list of available components. These are the functions
     # defined in this script whose name starts with "component_".
     # Parse the script with sed, because in sh there is no way to list
@@ -190,14 +199,18 @@
   -f|--force            Force the tests to overwrite any modified files.
   -k|--keep-going       Run all tests and report errors at the end.
   -m|--memory           Additional optional memory tests.
+     --append-outcome   Append to the outcome file (if used).
      --armcc            Run ARM Compiler builds (on by default).
      --except           Exclude the COMPONENTs listed on the command line,
                         instead of running only those.
+     --no-append-outcome    Write a new outcome file and analyze it (default).
      --no-armcc         Skip ARM Compiler builds.
      --no-force         Refuse to overwrite modified files (default).
      --no-keep-going    Stop at the first error (default).
      --no-memory        No additional memory tests (default).
      --out-of-source-dir=<path>  Directory used for CMake out-of-source build tests.
+     --outcome-file=<path>  File where test outcomes are written (not done if
+                            empty; default: \$MBEDTLS_TEST_OUTCOME_FILE).
      --random-seed      Use a random seed value for randomized tests (default).
   -r|--release-test     Run this script in release mode. This fixes the seed value to 1.
   -s|--seed             Integer seed value to use for this test run.
@@ -323,6 +336,7 @@
 
     while [ $# -gt 0 ]; do
         case "$1" in
+            --append-outcome) append_outcome=1;;
             --armcc) no_armcc=;;
             --armc5-bin-dir) shift; ARMC5_BIN_DIR="$1";;
             --armc6-bin-dir) shift; ARMC6_BIN_DIR="$1";;
@@ -337,6 +351,7 @@
             --list-all-components) printf '%s\n' $ALL_COMPONENTS; exit;;
             --list-components) printf '%s\n' $SUPPORTED_COMPONENTS; exit;;
             --memory|-m) MEMORY=1;;
+            --no-append-outcome) append_outcome=0;;
             --no-armcc) no_armcc=1;;
             --no-force) FORCE=0;;
             --no-keep-going) KEEP_GOING=0;;
@@ -344,6 +359,7 @@
             --openssl) shift; OPENSSL="$1";;
             --openssl-legacy) shift; OPENSSL_LEGACY="$1";;
             --openssl-next) shift; OPENSSL_NEXT="$1";;
+            --outcome-file) shift; MBEDTLS_TEST_OUTCOME_FILE="$1";;
             --out-of-source-dir) shift; OUT_OF_SOURCE_DIR="$1";;
             --random-seed) unset SEED;;
             --release-test|-r) SEED=1;;
@@ -485,11 +501,22 @@
     ! "$@"
 }
 
+pre_prepare_outcome_file () {
+    case "$MBEDTLS_TEST_OUTCOME_FILE" in
+      [!/]*) MBEDTLS_TEST_OUTCOME_FILE="$PWD/$MBEDTLS_TEST_OUTCOME_FILE";;
+    esac
+    if [ -n "$MBEDTLS_TEST_OUTCOME_FILE" ] && [ "$append_outcome" -eq 0 ]; then
+        rm -f "$MBEDTLS_TEST_OUTCOME_FILE"
+    fi
+}
+
 pre_print_configuration () {
     msg "info: $0 configuration"
     echo "MEMORY: $MEMORY"
     echo "FORCE: $FORCE"
+    echo "MBEDTLS_TEST_OUTCOME_FILE: ${MBEDTLS_TEST_OUTCOME_FILE:-(none)}"
     echo "SEED: ${SEED-"UNSET"}"
+    echo
     echo "OPENSSL: $OPENSSL"
     echo "OPENSSL_LEGACY: $OPENSSL_LEGACY"
     echo "OPENSSL_NEXT: $OPENSSL_NEXT"
@@ -582,32 +609,37 @@
 # Indicative running times are given for reference.
 
 component_check_recursion () {
-    msg "test: recursion.pl" # < 1s
+    msg "Check: recursion.pl" # < 1s
     record_status tests/scripts/recursion.pl library/*.c
 }
 
 component_check_generated_files () {
-    msg "test: freshness of generated source files" # < 1s
+    msg "Check: freshness of generated source files" # < 1s
     record_status tests/scripts/check-generated-files.sh
 }
 
 component_check_doxy_blocks () {
-    msg "test: doxygen markup outside doxygen blocks" # < 1s
+    msg "Check: doxygen markup outside doxygen blocks" # < 1s
     record_status tests/scripts/check-doxy-blocks.pl
 }
 
 component_check_files () {
-    msg "test: check-files.py" # < 1s
+    msg "Check: file sanity checks (permissions, encodings)" # < 1s
     record_status tests/scripts/check-files.py
 }
 
 component_check_names () {
-    msg "test/build: declared and exported names" # < 3s
+    msg "Check: declared and exported names (builds the library)" # < 3s
     record_status tests/scripts/check-names.sh -v
 }
 
+component_check_test_cases () {
+    msg "Check: test case descriptions" # < 1s
+    record_status tests/scripts/check-test-cases.py
+}
+
 component_check_doxygen_warnings () {
-    msg "test: doxygen warnings" # ~ 3s
+    msg "Check: doxygen warnings (builds the documentation)" # ~ 3s
     record_status tests/scripts/doxygen.sh
 }
 
@@ -617,32 +649,21 @@
 #### Build and test many configurations and targets
 ################################################################
 
-component_test_large_ecdsa_key_signature () {
-
-    SMALL_MPI_MAX_SIZE=136 # Small enough to interfere with the EC signatures
-
-    msg "build: cmake + MBEDTLS_MPI_MAX_SIZE=${SMALL_MPI_MAX_SIZE}, gcc, ASan" # ~ 1 min 50s
-    scripts/config.py set MBEDTLS_MPI_MAX_SIZE $SMALL_MPI_MAX_SIZE
-    CC=gcc cmake -D CMAKE_BUILD_TYPE:String=Asan .
-    make
-
-    INEVITABLY_PRESENT_FILE=Makefile
-    SIGNATURE_FILE="${INEVITABLY_PRESENT_FILE}.sig" # Warning, this is rm -f'ed below
-
-    msg "test: pk_sign secp521r1_prv.der for MBEDTLS_MPI_MAX_SIZE=${SMALL_MPI_MAX_SIZE} (ASan build)" # ~ 5s
-    if_build_succeeded programs/pkey/pk_sign tests/data_files/secp521r1_prv.der $INEVITABLY_PRESENT_FILE
-    rm -f $SIGNATURE_FILE
-}
-
 component_test_default_out_of_box () {
     msg "build: make, default config (out-of-box)" # ~1min
     make
+    # Disable fancy stuff
+    SAVE_MBEDTLS_TEST_OUTCOME_FILE="$MBEDTLS_TEST_OUTCOME_FILE"
+    unset MBEDTLS_TEST_OUTCOME_FILE
 
     msg "test: main suites make, default config (out-of-box)" # ~10s
     make test
 
     msg "selftest: make, default config (out-of-box)" # ~10s
     programs/test/selftest
+
+    export MBEDTLS_TEST_OUTCOME_FILE="$SAVE_MBEDTLS_TEST_OUTCOME_FILE"
+    unset SAVE_MBEDTLS_TEST_OUTCOME_FILE
 }
 
 component_test_default_cmake_gcc_asan () {
@@ -1101,7 +1122,7 @@
 
 component_test_make_shared () {
     msg "build/test: make shared" # ~ 40s
-    make SHARED=1 all check -j1
+    make SHARED=1 all check
     ldd programs/util/strerror | grep libmbedcrypto
 }
 
@@ -1128,7 +1149,7 @@
     # Build once with -O0, to compile out the i386 specific inline assembly
     msg "build: i386, make, gcc -O0 (ASan build)" # ~ 30s
     scripts/config.py full
-    make CC=gcc CFLAGS='-O0 -Werror -Wall -Wextra -m32 -fsanitize=address' LDFLAGS='-m32 -fsanitize=address'
+    make CC=gcc CFLAGS="$ASAN_CFLAGS -m32 -O0" LDFLAGS="-m32 $ASAN_CFLAGS"
 
     msg "test: i386, make, gcc -O0 (ASan build)"
     make test
@@ -1144,7 +1165,7 @@
     # Build again with -O1, to compile in the i386 specific inline assembly
     msg "build: i386, make, gcc -O1 (ASan build)" # ~ 30s
     scripts/config.py full
-    make CC=gcc CFLAGS='-O1 -Werror -Wall -Wextra -m32 -fsanitize=address' LDFLAGS='-m32 -fsanitize=address'
+    make CC=gcc CFLAGS="$ASAN_CFLAGS -m32 -O1" LDFLAGS="-m32 $ASAN_CFLAGS"
 
     msg "test: i386, make, gcc -O1 (ASan build)"
     make test
@@ -1160,7 +1181,7 @@
     msg "build: i386, Everest ECDH context (ASan build)" # ~ 6 min
     scripts/config.py unset MBEDTLS_ECDH_LEGACY_CONTEXT
     scripts/config.py set MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED
-    make CC=gcc CFLAGS='-O2 -Werror -Wall -Wextra -m32 -fsanitize=address' LDFLAGS='-m32 -fsanitize=address'
+    make CC=gcc CFLAGS="$ASAN_CFLAGS -m32 -O2" LDFLAGS="-m32 $ASAN_CFLAGS"
 
     msg "test: i386, Everest ECDH context - main suites (inc. selftests) (ASan build)" # ~ 50s
     make test
@@ -1260,15 +1281,15 @@
 
 component_build_mingw () {
     msg "build: Windows cross build - mingw64, make (Link Library)" # ~ 30s
-    make CC=i686-w64-mingw32-gcc AR=i686-w64-mingw32-ar LD=i686-w64-minggw32-ld CFLAGS='-Werror -Wall -Wextra' WINDOWS_BUILD=1 lib programs -j1
+    make CC=i686-w64-mingw32-gcc AR=i686-w64-mingw32-ar LD=i686-w64-minggw32-ld CFLAGS='-Werror -Wall -Wextra' WINDOWS_BUILD=1 lib programs
 
     # note Make tests only builds the tests, but doesn't run them
-    make CC=i686-w64-mingw32-gcc AR=i686-w64-mingw32-ar LD=i686-w64-minggw32-ld CFLAGS='-Werror' WINDOWS_BUILD=1 tests -j1
+    make CC=i686-w64-mingw32-gcc AR=i686-w64-mingw32-ar LD=i686-w64-minggw32-ld CFLAGS='-Werror' WINDOWS_BUILD=1 tests
     make WINDOWS_BUILD=1 clean
 
     msg "build: Windows cross build - mingw64, make (DLL)" # ~ 30s
-    make CC=i686-w64-mingw32-gcc AR=i686-w64-mingw32-ar LD=i686-w64-minggw32-ld CFLAGS='-Werror -Wall -Wextra' WINDOWS_BUILD=1 SHARED=1 lib programs -j1
-    make CC=i686-w64-mingw32-gcc AR=i686-w64-mingw32-ar LD=i686-w64-minggw32-ld CFLAGS='-Werror -Wall -Wextra' WINDOWS_BUILD=1 SHARED=1 tests -j1
+    make CC=i686-w64-mingw32-gcc AR=i686-w64-mingw32-ar LD=i686-w64-minggw32-ld CFLAGS='-Werror -Wall -Wextra' WINDOWS_BUILD=1 SHARED=1 lib programs
+    make CC=i686-w64-mingw32-gcc AR=i686-w64-mingw32-ar LD=i686-w64-minggw32-ld CFLAGS='-Werror -Wall -Wextra' WINDOWS_BUILD=1 SHARED=1 tests
     make WINDOWS_BUILD=1 clean
 }
 support_build_mingw() {
@@ -1424,6 +1445,7 @@
     # The cleanup function will restore it.
     cp -p "$CONFIG_H" "$CONFIG_BAK"
     current_component="$1"
+    export MBEDTLS_TEST_CONFIGURATION="$current_component"
     "$@"
     cleanup
 }
@@ -1444,6 +1466,7 @@
         "$@"
     }
 fi
+pre_prepare_outcome_file
 pre_print_configuration
 pre_check_tools
 cleanup
diff --git a/tests/scripts/check-test-cases.py b/tests/scripts/check-test-cases.py
new file mode 100755
index 0000000..87a35e4
--- /dev/null
+++ b/tests/scripts/check-test-cases.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python3
+
+"""Sanity checks for test data.
+"""
+
+# Copyright (C) 2019, Arm Limited, All Rights Reserved
+# 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.
+#
+# This file is part of Mbed TLS (https://tls.mbed.org)
+
+import glob
+import os
+import re
+import sys
+
+class Results:
+    def __init__(self):
+        self.errors = 0
+        self.warnings = 0
+
+    def error(self, file_name, line_number, fmt, *args):
+        sys.stderr.write(('{}:{}:ERROR:' + fmt + '\n').
+                         format(file_name, line_number, *args))
+        self.errors += 1
+
+    def warning(self, file_name, line_number, fmt, *args):
+        sys.stderr.write(('{}:{}:Warning:' + fmt + '\n')
+                         .format(file_name, line_number, *args))
+        self.warnings += 1
+
+def collect_test_directories():
+    if os.path.isdir('tests'):
+        tests_dir = 'tests'
+    elif os.path.isdir('suites'):
+        tests_dir = '.'
+    elif os.path.isdir('../suites'):
+        tests_dir = '..'
+    directories = [tests_dir]
+    crypto_tests_dir = os.path.normpath(os.path.join(tests_dir,
+                                                     '../crypto/tests'))
+    if os.path.isdir(crypto_tests_dir):
+        directories.append(crypto_tests_dir)
+    return directories
+
+def check_description(results, seen, file_name, line_number, description):
+    if description in seen:
+        results.error(file_name, line_number,
+                      'Duplicate description (also line {})',
+                      seen[description])
+        return
+    if re.search(br'[\t;]', description):
+        results.error(file_name, line_number,
+                      'Forbidden character \'{}\' in description',
+                      re.search(br'[\t;]', description).group(0).decode('ascii'))
+    if re.search(br'[^ -~]', description):
+        results.error(file_name, line_number,
+                      'Non-ASCII character in description')
+    if len(description) > 66:
+        results.warning(file_name, line_number,
+                        'Test description too long ({} > 66)',
+                        len(description))
+    seen[description] = line_number
+
+def check_test_suite(results, data_file_name):
+    in_paragraph = False
+    descriptions = {}
+    with open(data_file_name, 'rb') as data_file:
+        for line_number, line in enumerate(data_file, 1):
+            line = line.rstrip(b'\r\n')
+            if not line:
+                in_paragraph = False
+                continue
+            if line.startswith(b'#'):
+                continue
+            if not in_paragraph:
+                # This is a test case description line.
+                check_description(results, descriptions,
+                                  data_file_name, line_number, line)
+            in_paragraph = True
+
+def check_ssl_opt_sh(results, file_name):
+    descriptions = {}
+    with open(file_name, 'rb') as file_contents:
+        for line_number, line in enumerate(file_contents, 1):
+            # Assume that all run_test calls have the same simple form
+            # with the test description entirely on the same line as the
+            # function name.
+            m = re.match(br'\s*run_test\s+"((?:[^\\"]|\\.)*)"', line)
+            if not m:
+                continue
+            description = m.group(1)
+            check_description(results, descriptions,
+                              file_name, line_number, description)
+
+def main():
+    test_directories = collect_test_directories()
+    results = Results()
+    for directory in test_directories:
+        for data_file_name in glob.glob(os.path.join(directory, 'suites',
+                                                     '*.data')):
+            check_test_suite(results, data_file_name)
+        ssl_opt_sh = os.path.join(directory, 'ssl-opt.sh')
+        if os.path.exists(ssl_opt_sh):
+            check_ssl_opt_sh(results, ssl_opt_sh)
+    if results.warnings or results.errors:
+        sys.stderr.write('{}: {} errors, {} warnings\n'
+                         .format(sys.argv[0], results.errors, results.warnings))
+    sys.exit(1 if results.errors else 0)
+
+if __name__ == '__main__':
+    main()
diff --git a/tests/scripts/curves.pl b/tests/scripts/curves.pl
index 3e22552..8119a46 100755
--- a/tests/scripts/curves.pl
+++ b/tests/scripts/curves.pl
@@ -51,6 +51,7 @@
     print "\n******************************************\n";
     print "* Testing without curve: $curve\n";
     print "******************************************\n";
+    $ENV{MBEDTLS_TEST_CONFIGURATION} = "-$curve";
 
     system( "scripts/config.py unset $curve" )
         and abort "Failed to disable $curve\n";
diff --git a/tests/scripts/depends-hashes.pl b/tests/scripts/depends-hashes.pl
index 92bcceb..7cb41b5 100755
--- a/tests/scripts/depends-hashes.pl
+++ b/tests/scripts/depends-hashes.pl
@@ -57,6 +57,7 @@
     print "\n******************************************\n";
     print "* Testing without hash: $hash\n";
     print "******************************************\n";
+    $ENV{MBEDTLS_TEST_CONFIGURATION} = "-$hash";
 
     system( "scripts/config.py unset $hash" )
         and abort "Failed to disable $hash\n";
diff --git a/tests/scripts/depends-pkalgs.pl b/tests/scripts/depends-pkalgs.pl
index e3eac00..0cc01f2 100755
--- a/tests/scripts/depends-pkalgs.pl
+++ b/tests/scripts/depends-pkalgs.pl
@@ -72,6 +72,7 @@
     print "\n******************************************\n";
     print "* Testing without alg: $alg\n";
     print "******************************************\n";
+    $ENV{MBEDTLS_TEST_CONFIGURATION} = "-$alg";
 
     system( "scripts/config.py unset $alg" )
         and abort "Failed to disable $alg\n";
diff --git a/tests/scripts/key-exchanges.pl b/tests/scripts/key-exchanges.pl
index be029c7..851de1b 100755
--- a/tests/scripts/key-exchanges.pl
+++ b/tests/scripts/key-exchanges.pl
@@ -45,6 +45,7 @@
     print "\n******************************************\n";
     print "* Testing with key exchange: $kex\n";
     print "******************************************\n";
+    $ENV{MBEDTLS_TEST_CONFIGURATION} = $kex;
 
     # full config with all key exchanges disabled except one
     system( "scripts/config.py full" ) and abort "Failed config full\n";
diff --git a/tests/scripts/run-test-suites.pl b/tests/scripts/run-test-suites.pl
index 329ed14..e55d083 100755
--- a/tests/scripts/run-test-suites.pl
+++ b/tests/scripts/run-test-suites.pl
@@ -93,7 +93,7 @@
     $suite_cases_failed = () = $result =~ /.. FAILED/g;
     $suite_cases_skipped = () = $result =~ /.. ----/g;
 
-    if( $result =~ /PASSED/ ) {
+    if( $? == 0 ) {
         print "PASS\n";
         if( $verbose > 2 ) {
             pad_print_center( 72, '-', "Begin $suite" );
diff --git a/tests/scripts/test-ref-configs.pl b/tests/scripts/test-ref-configs.pl
index 80d5f38..1df84f7 100755
--- a/tests/scripts/test-ref-configs.pl
+++ b/tests/scripts/test-ref-configs.pl
@@ -18,7 +18,7 @@
 
 my %configs = (
     'config-mini-tls1_1.h' => {
-        'compat' => '-m tls1_1 -f \'^DES-CBC3-SHA$\|^TLS-RSA-WITH-3DES-EDE-CBC-SHA$\'',
+        'compat' => '-m tls1_1 -f \'^DES-CBC3-SHA$\|^TLS-RSA-WITH-3DES-EDE-CBC-SHA$\'', #'
     },
     'config-suite-b.h' => {
         'compat' => "-m tls1_2 -f 'ECDHE-ECDSA.*AES.*GCM' -p mbedTLS",
@@ -65,6 +65,7 @@
     print "\n******************************************\n";
     print "* Testing configuration: $conf\n";
     print "******************************************\n";
+    $ENV{MBEDTLS_TEST_CONFIGURATION} = $conf;
 
     system( "cp configs/$conf $config_h" )
         and abort "Failed to activate $conf\n";
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 91fceea..afaae69 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -25,9 +25,9 @@
 # where it may output seemingly unlimited length error logs.
 ulimit -f 20971520
 
-if cd $( dirname $0 ); then :; else
-    echo "cd $( dirname $0 ) failed" >&2
-    exit 1
+ORIGINAL_PWD=$PWD
+if ! cd "$(dirname "$0")"; then
+    exit 125
 fi
 
 # default values, can be overridden by the environment
@@ -39,6 +39,17 @@
 : ${GNUTLS_SERV:=gnutls-serv}
 : ${PERL:=perl}
 
+guess_config_name() {
+    if git diff --quiet ../include/mbedtls/config.h 2>/dev/null; then
+        echo "default"
+    else
+        echo "unknown"
+    fi
+}
+: ${MBEDTLS_TEST_OUTCOME_FILE=}
+: ${MBEDTLS_TEST_CONFIGURATION:="$(guess_config_name)"}
+: ${MBEDTLS_TEST_PLATFORM:="$(uname -s | tr -c \\n0-9A-Za-z _)-$(uname -m | tr -c \\n0-9A-Za-z _)"}
+
 O_SRV="$OPENSSL_CMD s_server -www -cert data_files/server5.crt -key data_files/server5.key"
 O_CLI="echo 'GET / HTTP/1.0' | $OPENSSL_CMD s_client"
 G_SRV="$GNUTLS_SERV --x509certfile data_files/server5.crt --x509keyfile data_files/server5.key"
@@ -97,9 +108,11 @@
     printf "  -n|--number\tExecute only numbered test (comma-separated, e.g. '245,256')\n"
     printf "  -s|--show-numbers\tShow test numbers in front of test names\n"
     printf "  -p|--preserve-logs\tPreserve logs of successful tests as well\n"
-    printf "     --port\tTCP/UDP port (default: randomish 1xxxx)\n"
+    printf "     --outcome-file\tFile where test outcomes are written\n"
+    printf "                \t(default: \$MBEDTLS_TEST_OUTCOME_FILE, none if empty)\n"
+    printf "     --port     \tTCP/UDP port (default: randomish 1xxxx)\n"
     printf "     --proxy-port\tTCP/UDP proxy port (default: randomish 2xxxx)\n"
-    printf "     --seed\tInteger seed value to use for this test run\n"
+    printf "     --seed     \tInteger seed value to use for this test run\n"
 }
 
 get_options() {
@@ -146,6 +159,14 @@
     done
 }
 
+# Make the outcome file path relative to the original directory, not
+# to .../tests
+case "$MBEDTLS_TEST_OUTCOME_FILE" in
+    [!/]*)
+        MBEDTLS_TEST_OUTCOME_FILE="$ORIGINAL_PWD/$MBEDTLS_TEST_OUTCOME_FILE"
+        ;;
+esac
+
 # Skip next test; use this macro to skip tests which are legitimate
 # in theory and expected to be re-introduced at some point, but
 # aren't expected to succeed at the moment due to problems outside
@@ -359,9 +380,22 @@
 
 }
 
+# record_outcome <outcome> [<failure-reason>]
+# The test name must be in $NAME.
+record_outcome() {
+    echo "$1"
+    if [ -n "$MBEDTLS_TEST_OUTCOME_FILE" ]; then
+        printf '%s;%s;%s;%s;%s;%s\n' \
+               "$MBEDTLS_TEST_PLATFORM" "$MBEDTLS_TEST_CONFIGURATION" \
+               "ssl-opt" "$NAME" \
+               "$1" "${2-}" \
+               >>"$MBEDTLS_TEST_OUTCOME_FILE"
+    fi
+}
+
 # fail <message>
 fail() {
-    echo "FAIL"
+    record_outcome "FAIL" "$1"
     echo "  ! $1"
 
     mv $SRV_OUT o-srv-${TESTS}.log
@@ -539,6 +573,7 @@
     if echo "$NAME" | grep "$FILTER" | grep -v "$EXCLUDE" >/dev/null; then :
     else
         SKIP_NEXT="NO"
+        # There was no request to run the test, so don't record its outcome.
         return
     fi
 
@@ -586,7 +621,7 @@
     # should we skip?
     if [ "X$SKIP_NEXT" = "XYES" ]; then
         SKIP_NEXT="NO"
-        echo "SKIP"
+        record_outcome "SKIP"
         SKIPS=$(( $SKIPS + 1 ))
         return
     fi
@@ -772,7 +807,7 @@
     fi
 
     # if we're here, everything is ok
-    echo "PASS"
+    record_outcome "PASS"
     if [ "$PRESERVE_LOGS" -gt 0 ]; then
         mv $SRV_OUT o-srv-${TESTS}.log
         mv $CLI_OUT o-cli-${TESTS}.log
@@ -1127,7 +1162,7 @@
             -c "The certificate is signed with an unacceptable hash"
 
 requires_config_enabled MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_CERTIFICATES
-run_test    "SHA-1 forbidden by default in server certificate" \
+run_test    "SHA-1 allowed by default in server certificate" \
             "$P_SRV key_file=data_files/server2.key crt_file=data_files/server2.crt" \
             "$P_CLI debug_level=2 allow_sha1=0" \
             0
@@ -1150,7 +1185,7 @@
             -s "The certificate is signed with an unacceptable hash"
 
 requires_config_enabled MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_CERTIFICATES
-run_test    "SHA-1 forbidden by default in client certificate" \
+run_test    "SHA-1 allowed by default in client certificate" \
             "$P_SRV auth_mode=required allow_sha1=0" \
             "$P_CLI key_file=data_files/cli-rsa.key crt_file=data_files/cli-rsa-sha1.crt" \
             0
@@ -6992,7 +7027,7 @@
 
 requires_config_enabled MBEDTLS_SSL_ASYNC_PRIVATE
 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION
-run_test    "SSL async private: renegotiation: client-initiated; sign" \
+run_test    "SSL async private: renegotiation: client-initiated, sign" \
             "$P_SRV \
              async_operations=s async_private_delay1=1 async_private_delay2=1 \
              exchanges=2 renegotiation=1" \
@@ -7003,7 +7038,7 @@
 
 requires_config_enabled MBEDTLS_SSL_ASYNC_PRIVATE
 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION
-run_test    "SSL async private: renegotiation: server-initiated; sign" \
+run_test    "SSL async private: renegotiation: server-initiated, sign" \
             "$P_SRV \
              async_operations=s async_private_delay1=1 async_private_delay2=1 \
              exchanges=2 renegotiation=1 renegotiate=1" \
@@ -7014,7 +7049,7 @@
 
 requires_config_enabled MBEDTLS_SSL_ASYNC_PRIVATE
 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION
-run_test    "SSL async private: renegotiation: client-initiated; decrypt" \
+run_test    "SSL async private: renegotiation: client-initiated, decrypt" \
             "$P_SRV \
              async_operations=d async_private_delay1=1 async_private_delay2=1 \
              exchanges=2 renegotiation=1" \
@@ -7026,7 +7061,7 @@
 
 requires_config_enabled MBEDTLS_SSL_ASYNC_PRIVATE
 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION
-run_test    "SSL async private: renegotiation: server-initiated; decrypt" \
+run_test    "SSL async private: renegotiation: server-initiated, decrypt" \
             "$P_SRV \
              async_operations=d async_private_delay1=1 async_private_delay2=1 \
              exchanges=2 renegotiation=1 renegotiate=1" \
@@ -7594,7 +7629,7 @@
 requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA
 requires_config_enabled MBEDTLS_AES_C
 requires_config_enabled MBEDTLS_GCM_C
-run_test    "DTLS fragmenting: proxy MTU: auto-reduction" \
+run_test    "DTLS fragmenting: proxy MTU: auto-reduction (not valgrind)" \
             -p "$P_PXY mtu=508" \
             "$P_SRV dtls=1 debug_level=2 auth_mode=required \
              crt_file=data_files/server7_int-ca.crt \
@@ -7618,7 +7653,7 @@
 requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA
 requires_config_enabled MBEDTLS_AES_C
 requires_config_enabled MBEDTLS_GCM_C
-run_test    "DTLS fragmenting: proxy MTU: auto-reduction" \
+run_test    "DTLS fragmenting: proxy MTU: auto-reduction (with valgrind)" \
             -p "$P_PXY mtu=508" \
             "$P_SRV dtls=1 debug_level=2 auth_mode=required \
              crt_file=data_files/server7_int-ca.crt \
diff --git a/tests/suites/helpers.function b/tests/suites/helpers.function
index b4b6d72..5d0a390 100644
--- a/tests/suites/helpers.function
+++ b/tests/suites/helpers.function
@@ -271,7 +271,7 @@
     TEST_RESULT_SKIPPED
 } test_result_t;
 
-static struct
+typedef struct
 {
     paramfail_test_state_t paramfail_test_state;
     test_result_t result;
@@ -279,7 +279,8 @@
     const char *filename;
     int line_no;
 }
-test_info;
+test_info_t;
+static test_info_t test_info;
 
 #if defined(MBEDTLS_PLATFORM_C)
 mbedtls_platform_context platform_ctx;
diff --git a/tests/suites/host_test.function b/tests/suites/host_test.function
index 0f98d23..9e56ca3 100644
--- a/tests/suites/host_test.function
+++ b/tests/suites/host_test.function
@@ -368,6 +368,118 @@
             test_snprintf( 5, "123",         3 ) != 0 );
 }
 
+/** \brief Write the description of the test case to the outcome CSV file.
+ *
+ * \param outcome_file  The file to write to.
+ *                      If this is \c NULL, this function does nothing.
+ * \param argv0         The test suite name.
+ * \param test_case     The test case description.
+ */
+static void write_outcome_entry( FILE *outcome_file,
+                                 const char *argv0,
+                                 const char *test_case )
+{
+    /* The non-varying fields are initialized on first use. */
+    static const char *platform = NULL;
+    static const char *configuration = NULL;
+    static const char *test_suite = NULL;
+
+    if( outcome_file == NULL )
+        return;
+
+    if( platform == NULL )
+    {
+        platform = getenv( "MBEDTLS_TEST_PLATFORM" );
+        if( platform == NULL )
+            platform = "unknown";
+    }
+    if( configuration == NULL )
+    {
+        configuration = getenv( "MBEDTLS_TEST_CONFIGURATION" );
+        if( configuration == NULL )
+            configuration = "unknown";
+    }
+    if( test_suite == NULL )
+    {
+        test_suite = strrchr( argv0, '/' );
+        if( test_suite != NULL )
+            test_suite += 1; // skip the '/'
+        else
+            test_suite = argv0;
+    }
+
+    /* Write the beginning of the outcome line.
+     * Ignore errors: writing the outcome file is on a best-effort basis. */
+    mbedtls_fprintf( outcome_file, "%s;%s;%s;%s;",
+                     platform, configuration, test_suite, test_case );
+}
+
+/** \brief Write the result of the test case to the outcome CSV file.
+ *
+ * \param outcome_file  The file to write to.
+ *                      If this is \c NULL, this function does nothing.
+ * \param unmet_dep_count       The number of unmet dependencies.
+ * \param unmet_dependencies    The array of unmet dependencies.
+ * \param ret           The test dispatch status (DISPATCH_xxx).
+ * \param test_info     A pointer to the test info structure.
+ */
+static void write_outcome_result( FILE *outcome_file,
+                                  size_t unmet_dep_count,
+                                  char *unmet_dependencies[],
+                                  int ret,
+                                  const test_info_t *info )
+{
+    if( outcome_file == NULL )
+        return;
+
+    /* Write the end of the outcome line.
+     * Ignore errors: writing the outcome file is on a best-effort basis. */
+    switch( ret )
+    {
+        case DISPATCH_TEST_SUCCESS:
+            if( unmet_dep_count > 0 )
+            {
+                size_t i;
+                mbedtls_fprintf( outcome_file, "SKIP" );
+                for( i = 0; i < unmet_dep_count; i++ )
+                {
+                    mbedtls_fprintf( outcome_file, "%c%s",
+                                     i == 0 ? ';' : ':',
+                                     unmet_dependencies[i] );
+                }
+                break;
+            }
+            switch( info->result )
+            {
+                case TEST_RESULT_SUCCESS:
+                    mbedtls_fprintf( outcome_file, "PASS;" );
+                    break;
+                case TEST_RESULT_SKIPPED:
+                    mbedtls_fprintf( outcome_file, "SKIP;Runtime skip" );
+                    break;
+                default:
+                    mbedtls_fprintf( outcome_file, "FAIL;%s:%d:%s",
+                                     info->filename, info->line_no,
+                                     info->test );
+                    break;
+            }
+            break;
+        case DISPATCH_TEST_FN_NOT_FOUND:
+            mbedtls_fprintf( outcome_file, "FAIL;Test function not found" );
+            break;
+        case DISPATCH_INVALID_TEST_DATA:
+            mbedtls_fprintf( outcome_file, "FAIL;Invalid test data" );
+            break;
+        case DISPATCH_UNSUPPORTED_SUITE:
+            mbedtls_fprintf( outcome_file, "SKIP;Unsupported suite" );
+            break;
+        default:
+            mbedtls_fprintf( outcome_file, "FAIL;Unknown cause" );
+            break;
+    }
+    mbedtls_fprintf( outcome_file, "\n" );
+    fflush( outcome_file );
+}
 
 /**
  * \brief       Desktop implementation of execute_tests().
@@ -385,15 +497,16 @@
     const char *default_filename = "DATA_FILE";
     const char *test_filename = NULL;
     const char **test_files = NULL;
-    int testfile_count = 0;
+    size_t testfile_count = 0;
     int option_verbose = 0;
     int function_id = 0;
 
     /* Other Local variables */
     int arg_index = 1;
     const char *next_arg;
-    int testfile_index, ret, i, cnt;
-    int total_errors = 0, total_tests = 0, total_skipped = 0;
+    size_t testfile_index, i, cnt;
+    int ret;
+    unsigned total_errors = 0, total_tests = 0, total_skipped = 0;
     FILE *file;
     char buf[5000];
     char *params[50];
@@ -403,6 +516,8 @@
 #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
     int stdout_fd = -1;
 #endif /* __unix__ || __APPLE__ __MACH__ */
+    const char *outcome_file_name = getenv( "MBEDTLS_TEST_OUTCOME_FILE" );
+    FILE *outcome_file = NULL;
 
 #if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) && \
     !defined(TEST_SUITE_MEMORY_BUFFER_ALLOC)
@@ -410,6 +525,15 @@
     mbedtls_memory_buffer_alloc_init( alloc_buf, sizeof( alloc_buf ) );
 #endif
 
+    if( outcome_file_name != NULL )
+    {
+        outcome_file = fopen( outcome_file_name, "a" );
+        if( outcome_file == NULL )
+        {
+            mbedtls_fprintf( stderr, "Unable to open outcome file. Continuing anyway.\n" );
+        }
+    }
+
     /*
      * The C standard doesn't guarantee that all-bits-0 is the representation
      * of a NULL pointer. We do however use that in our code for initializing
@@ -473,7 +597,7 @@
           testfile_index < testfile_count;
           testfile_index++ )
     {
-        int unmet_dep_count = 0;
+        size_t unmet_dep_count = 0;
         char *unmet_dependencies[20];
 
         test_filename = test_files[ testfile_index ];
@@ -505,6 +629,7 @@
                 mbedtls_fprintf( stdout, "." );
             mbedtls_fprintf( stdout, " " );
             fflush( stdout );
+            write_outcome_entry( outcome_file, argv[0], buf );
 
             total_tests++;
 
@@ -584,6 +709,9 @@
 
             }
 
+            write_outcome_result( outcome_file,
+                                  unmet_dep_count, unmet_dependencies,
+                                  ret, &test_info );
             if( unmet_dep_count > 0 || ret == DISPATCH_UNSUPPORTED_SUITE )
             {
                 total_skipped++;
@@ -638,7 +766,7 @@
             }
             else if( ret == DISPATCH_TEST_FN_NOT_FOUND )
             {
-                mbedtls_fprintf( stderr, "FAILED: FATAL TEST FUNCTION NOT FUND\n" );
+                mbedtls_fprintf( stderr, "FAILED: FATAL TEST FUNCTION NOT FOUND\n" );
                 fclose( file );
                 mbedtls_exit( 2 );
             }
@@ -652,14 +780,17 @@
             free( unmet_dependencies[i] );
     }
 
+    if( outcome_file != NULL )
+        fclose( outcome_file );
+
     mbedtls_fprintf( stdout, "\n----------------------------------------------------------------------------\n\n");
     if( total_errors == 0 )
         mbedtls_fprintf( stdout, "PASSED" );
     else
         mbedtls_fprintf( stdout, "FAILED" );
 
-    mbedtls_fprintf( stdout, " (%d / %d tests (%d skipped))\n",
-             total_tests - total_errors, total_tests, total_skipped );
+    mbedtls_fprintf( stdout, " (%u / %u tests (%u skipped))\n",
+                     total_tests - total_errors, total_tests, total_skipped );
 
 #if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) && \
     !defined(TEST_SUITE_MEMORY_BUFFER_ALLOC)
diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data
index ce49ff0..b3e3613 100644
--- a/tests/suites/test_suite_x509parse.data
+++ b/tests/suites/test_suite_x509parse.data
@@ -611,11 +611,11 @@
 depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_SHA1_C:MBEDTLS_SHA256_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15
 x509_verify:"data_files/cert_example_multi.crt":"data_files/test-ca.crt":"data_files/crl.pem":"www.example.net":MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:MBEDTLS_X509_BADCERT_CN_MISMATCH:"compat":"NULL"
 
-X509 CRT verification #27 (domain not matching multi certificate)
+X509 CRT verification #27.1 (domain not matching multi certificate: suffix)
 depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_SHA1_C:MBEDTLS_SHA256_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15
 x509_verify:"data_files/cert_example_multi.crt":"data_files/test-ca.crt":"data_files/crl.pem":"xample.net":MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:MBEDTLS_X509_BADCERT_CN_MISMATCH:"compat":"NULL"
 
-X509 CRT verification #27 (domain not matching multi certificate)
+X509 CRT verification #27.2 (domain not matching multi certificate: head junk)
 depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_SHA1_C:MBEDTLS_SHA256_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15
 x509_verify:"data_files/cert_example_multi.crt":"data_files/test-ca.crt":"data_files/crl.pem":"bexample.net":MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:MBEDTLS_X509_BADCERT_CN_MISMATCH:"compat":"NULL"
 
@@ -1058,7 +1058,8 @@
 
 X509 CRT ASN1 (TBS, valid inner version tag, inner length too large for int)
 depends_on:MBEDTLS_SHA256_C:MBEDTLS_RSA_C
-x509parse_crt:"30293014a012021000000000000000000000000000000000300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_VERSION + MBEDTLS_ERR_ASN1_INVALID_LENGTH
+# tbsCertificate.version = 0x01000000000000000000000000000000 rejected by mbedtls_asn1_get_int
+x509parse_crt:"30293014a012021001000000000000000000000000000000300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_VERSION + MBEDTLS_ERR_ASN1_INVALID_LENGTH
 
 X509 CRT ASN1 (TBS, valid inner version tag, inner vs. outer length mismatch)
 depends_on:MBEDTLS_SHA256_C:MBEDTLS_RSA_C
@@ -1292,10 +1293,6 @@
 depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
 x509parse_crt:"307b3066a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a300806000c045465737430021701300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffff300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_DATE + MBEDTLS_ERR_ASN1_OUT_OF_DATA
 
-X509 CRT ASN1 (TBS, inv Validity, notBefore length out of bounds)
-depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
-x509parse_crt:"307b3066a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a300806000c045465737430021701300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffff300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_DATE + MBEDTLS_ERR_ASN1_OUT_OF_DATA
-
 X509 CRT ASN1 (TBS, inv Validity, notBefore empty)
 depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
 x509parse_crt:"3081893074a0030201008204deadbeef300d06092a864886f70d01010b0500300c310a3008060013045465737430101700170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffff300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_DATE
@@ -1569,22 +1566,6 @@
 depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
 x509parse_crt:"30819a308184a0030201008204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa1000500300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_FORMAT + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH
 
-X509 CRT ASN1 (TBSCertificate v3, ext CertificatePolicies tag, bool len missing)
-depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
-x509parse_crt:"308198308195a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa101aaa201bba30c300730050603551d2001010100":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH
-
-X509 CRT ASN1 (TBSCertificate v3, ext CertificatePolicies tag, data missing)
-depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
-x509parse_crt:"308198308195a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa101aaa201bba30b300930070603551d20040001010100":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA
-
-X509 CRT ASN1 (TBSCertificate v3, ext CertificatePolicies tag, data not oid)
-depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
-x509parse_crt:"3081bc3081b9a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa101aaa201bba32e302c30290603551d2004223020301ea01c06082b06010505070804a010300e06082b060104010901030402022201010100":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG
-
-X509 CRT ASN1 (TBSCertificate v3, ext CertificatePolicies tag, qualifier not complete)
-depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
-x509parse_crt:"308198308195a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa101aaa201bba30c300a301f0603551d2004183020301f0603551d200418301630140604551d2000300c300a06082b0601050507020101010100":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA
-
 X509 CRT ASN1 (TBSCertificate v3, ext SubjectAlternativeName malformed)
 depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA1_C
 x509parse_crt:"30820220308201ffa0030201020209202020202020202020300d06092a864886f70d01010505003045310b30090603202020130220203113301106032020200c0a202020202020202020203121301f06032020200c18202020202020202020202020202020202020202020202020301e170d3134303432333230353034305a170d3137303432323230353034305a3045310b30090603202020130220203113301106032020200c0a202020202020202020203121301f06032020200c1820202020202020202020202020202020202020202020202030819f300d06092a864886f70d010101050003818d003081890281812020202020202020ff20202020202020202020202020202020202020ff202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020ff020320ffffa350304e301d0603202020041620202020202020202020202020202020202020202020301f0603551d11041830169104202020208000be002020202020202020202020202020202020202020202020202020202020202020ff20202020202020202020202020202020202020ff2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020ff2020ff202020202020202020202020202020ff2020202020202020202020202020202020202020202020202020":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA
@@ -1733,6 +1714,86 @@
 depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
 x509parse_crt:"3081a9308193a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a30d300b3009060001010004000500300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH
 
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, data missing)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081a7308191a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a30b300930070603551d200400300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, invalid outer tag)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081a9308193a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a30d300b30090603551d2004020500300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, outer length missing)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081a8308192a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a30c300a30080603551d20040130300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, outer length inv encoding)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081a9308193a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a30d300b30090603551d2004023085300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_INVALID_LENGTH
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, outer length out of bounds)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081a9308193a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a30d300b30090603551d2004023001300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, no policies)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081a9308193a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a30d300b30090603551d2004023000300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, policy invalid tag)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081ab308195a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a30f300d300b0603551d20040430020500300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, policy length missing)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081aa308194a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a30e300c300a0603551d200403300130300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, policy length inv encoding)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081ab308195a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a30f300d300b0603551d20040430023085300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_INVALID_LENGTH
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, policy length out of bounds)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081ab308195a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a30f300d300b0603551d20040430023001300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, empty policy)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081ab308195a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a30f300d300b0603551d20040430023000300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, policy invalid OID tag)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081ad308197a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a311300f300d0603551d200406300430020500300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, policy no OID length)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081ac308196a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a310300e300c0603551d2004053003300106300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, policy OID length inv encoding)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081ad308197a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a311300f300d0603551d200406300430020685300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_INVALID_LENGTH
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, policy OID length out of bounds)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081ad308197a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a311300f300d0603551d200406300430020601300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, unknown critical policy)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C:!MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION
+x509parse_crt:"3081b130819ba0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a315301330110603551d20010101040730053003060100300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, policy qualifier invalid tag)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081b030819aa0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a314301230100603551d200409300730050601000500300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, policy qualifier no length)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081af308199a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a3133011300f0603551d2004083006300406010030300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, policy qualifier inv length encoding)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081b030819aa0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a314301230100603551d200409300730050601003085300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_INVALID_LENGTH
+
+X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, policy qualifier length out of bounds)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+x509parse_crt:"3081b030819aa0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a314301230100603551d200409300730050601003001300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA
+
 X509 CRT ASN1 (TBS, inv extBasicConstraint, no pathlen length)
 depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
 x509parse_crt:"3081b030819aa0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a314301230100603551d130101010406300402010102300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA
@@ -1825,13 +1886,31 @@
 depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
 x509parse_crt:"3081bb3081a7a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a321301f301d0603551d11041630148208666f6f2e7465737482086261722e74657374300d06092a864886f70d01010b05000301":"":MBEDTLS_ERR_X509_INVALID_SIGNATURE + MBEDTLS_ERR_ASN1_OUT_OF_DATA
 
-X509 CRT ASN1 (inv Signature, empty)
+X509 CRT ASN1 (inv Signature, inv data #1)
 depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+# signature = bit string with invalid encoding (missing number of unused bits)
 x509parse_crt:"3081bb3081a7a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a321301f301d0603551d11041630148208666f6f2e7465737482086261722e74657374300d06092a864886f70d01010b05000300":"":MBEDTLS_ERR_X509_INVALID_SIGNATURE + MBEDTLS_ERR_ASN1_INVALID_DATA
 
-X509 CRT ASN1 (inv Signature, inv data)
+X509 CRT ASN1 (inv Signature, inv data #2)
 depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
-x509parse_crt:"3081bc3081a7a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a321301f301d0603551d11041630148208666f6f2e7465737482086261722e74657374300d06092a864886f70d01010b0500030100":"":MBEDTLS_ERR_X509_INVALID_SIGNATURE + MBEDTLS_ERR_ASN1_INVALID_DATA
+# signature = bit string with invalid encoding (number of unused bits too large)
+x509parse_crt:"3081bc3081a7a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a321301f301d0603551d11041630148208666f6f2e7465737482086261722e74657374300d06092a864886f70d01010b0500030108":"":MBEDTLS_ERR_X509_INVALID_SIGNATURE + MBEDTLS_ERR_ASN1_INVALID_DATA
+
+X509 CRT ASN1 (empty Signature)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+# signature = empty bit string in DER encoding
+x509parse_crt:"3081bc3081a7a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a321301f301d0603551d11041630148208666f6f2e7465737482086261722e74657374300d06092a864886f70d01010b0500030100":"cert. version     \: 3\nserial number     \: DE\:AD\:BE\:EF\nissuer name       \: ??=Test\nsubject name      \: ??=Test\nissued  on        \: 2009-01-01 00\:00\:00\nexpires on        \: 2009-12-31 23\:59\:59\nsigned using      \: RSA with SHA-256\nRSA key size      \: 128 bits\nsubject alt name  \:\n    dNSName \: foo.test\n    dNSName \: bar.test\n":0
+
+X509 CRT ASN1 (dummy 24-bit Signature)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+# signature = bit string "011001100110111101101111"
+x509parse_crt:"3081bf3081a7a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a321301f301d0603551d11041630148208666f6f2e7465737482086261722e74657374300d06092a864886f70d01010b0500030400666f6f":"cert. version     \: 3\nserial number     \: DE\:AD\:BE\:EF\nissuer name       \: ??=Test\nsubject name      \: ??=Test\nissued  on        \: 2009-01-01 00\:00\:00\nexpires on        \: 2009-12-31 23\:59\:59\nsigned using      \: RSA with SHA-256\nRSA key size      \: 128 bits\nsubject alt name  \:\n    dNSName \: foo.test\n    dNSName \: bar.test\n":0
+
+# The ASN.1 module rejects non-octet-aligned bit strings.
+X509 CRT ASN1 (inv Signature: not octet-aligned)
+depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
+# signature = bit string "01100110011011110110111"
+x509parse_crt:"3081bf3081a7a0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a321301f301d0603551d11041630148208666f6f2e7465737482086261722e74657374300d06092a864886f70d01010b0500030401666f6e":"":MBEDTLS_ERR_X509_INVALID_SIGNATURE + MBEDTLS_ERR_ASN1_INVALID_DATA
 
 X509 CRT ASN1 (inv Signature, length mismatch)
 depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C