Simulate closing the connection mid-message

Simulate the server closing the connection after a partial handshake
message.

These test cases don't send a close_notify alert. The test cases
"insert alert record" exercise what happens if the server sends an alert.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function
index 52e887a..3081257 100644
--- a/tests/suites/test_suite_ssl.function
+++ b/tests/suites/test_suite_ssl.function
@@ -109,6 +109,7 @@
 typedef enum {
     RECOMBINE_NOMINAL,          /* param: ignored */
     RECOMBINE_SPLIT_FIRST,      /* param: offset of split (<=0 means from end) */
+    RECOMBINE_TRUNCATE_FIRST,   /* param: offset of truncation (<=0 means from end) */
     RECOMBINE_INSERT_EMPTY,     /* param: offset (<0 means from end) */
     RECOMBINE_INSERT_RECORD,    /* param: record type */
     RECOMBINE_COALESCE,         /* param: min number of records */
@@ -160,6 +161,39 @@
     return -1;
 }
 
+/* Truncate the first record, keeping only the first offset bytes.
+ * If offset is zero or negative, count from the end of the record.
+ * Remove the subsequent records.
+ */
+static int recombine_truncate_first_record(mbedtls_test_ssl_buffer *buf,
+                                           int offset)
+{
+    const size_t header_length = 5;
+    TEST_LE_U(header_length, buf->content_length);
+    size_t record_length = MBEDTLS_GET_UINT16_BE(buf->buffer, header_length - 2);
+
+    if (offset > 0) {
+        TEST_LE_S(offset, record_length);
+    } else {
+        TEST_LE_S(-offset, record_length);
+        offset = record_length + offset;
+    }
+
+    /* Adjust the length of the first record */
+    MBEDTLS_PUT_UINT16_BE(offset, buf->buffer, header_length - 2);
+
+    /* Wipe the rest */
+    size_t truncated_end = header_length + offset;
+    memset(buf->buffer + truncated_end, '!',
+           buf->content_length - truncated_end);
+    buf->content_length = truncated_end;
+
+    return 0;
+
+exit:
+    return -1;
+}
+
 /* Insert an empty record at the given offset. If offset is negative,
  * count from the end of the first record. */
 static int recombine_insert_record(mbedtls_test_ssl_buffer *buf,
@@ -307,6 +341,11 @@
             TEST_LE_S(0, ret);
             break;
 
+        case RECOMBINE_TRUNCATE_FIRST:
+            ret = recombine_truncate_first_record(buf, param);
+            TEST_LE_S(0, ret);
+            break;
+
         case RECOMBINE_INSERT_EMPTY:
             /* Insert an empty handshake record. */
             ret = recombine_insert_record(buf, param, MBEDTLS_SSL_MSG_HANDSHAKE);
@@ -3204,6 +3243,14 @@
 
     /* Server: parse the first flight from the client
      * and emit the second flight from the server */
+    if (instruction == RECOMBINE_TRUNCATE_FIRST) {
+        /* Close without a notification. The case of closing with a
+         * notification is tested via RECOMBINE_INSERT_RECORD to insert
+         * an alert record (which we reject, making the client SSL
+         * context become invalid). */
+        mbedtls_test_mock_socket_close(&server.socket);
+        goto goal_reached;
+    }
     while (ret == 0 && !mbedtls_ssl_is_handshake_over(&server.ssl)) {
         mbedtls_test_set_step(1000 + server.ssl.state);
         ret = mbedtls_ssl_handshake_step(&server.ssl);
diff --git a/tests/suites/test_suite_ssl.records.data b/tests/suites/test_suite_ssl.records.data
index 2acbbe9..e94f554 100644
--- a/tests/suites/test_suite_ssl.records.data
+++ b/tests/suites/test_suite_ssl.records.data
@@ -67,6 +67,14 @@
 depends_on:MBEDTLS_SSL_PROTO_TLS1_3
 recombine_server_first_flight:MBEDTLS_SSL_VERSION_TLS1_3:RECOMBINE_SPLIT_FIRST:1:"handshake message too short\: 1":"":MBEDTLS_SSL_SERVER_HELLO:MBEDTLS_ERR_SSL_INVALID_RECORD
 
+Recombine server flight 1: TLS 1.2, truncate at 4 (bad)
+depends_on:MBEDTLS_SSL_PROTO_TLS1_2
+recombine_server_first_flight:MBEDTLS_SSL_VERSION_TLS1_2:RECOMBINE_TRUNCATE_FIRST:4:"initial handshake fragment\: 4, 0..4 of":"":MBEDTLS_SSL_SERVER_HELLO:MBEDTLS_ERR_SSL_WANT_READ
+
+Recombine server flight 1: TLS 1.3, truncate at 4 (bad)
+depends_on:MBEDTLS_SSL_PROTO_TLS1_3
+recombine_server_first_flight:MBEDTLS_SSL_VERSION_TLS1_3:RECOMBINE_TRUNCATE_FIRST:4:"initial handshake fragment\: 4, 0..4 of":"":MBEDTLS_SSL_SERVER_HELLO:MBEDTLS_ERR_SSL_WANT_READ
+
 Recombine server flight 1: TLS 1.2, insert empty record after first (bad)
 depends_on:MBEDTLS_SSL_PROTO_TLS1_2
 recombine_server_first_flight:MBEDTLS_SSL_VERSION_TLS1_2:RECOMBINE_SPLIT_FIRST:0:"rejecting empty record":"":MBEDTLS_SSL_SERVER_CERTIFICATE:MBEDTLS_ERR_SSL_INVALID_RECORD