/* test_dtls.c
 *
 * Copyright (C) 2006-2026 wolfSSL Inc.
 *
 * This file is part of wolfSSL.
 *
 * wolfSSL is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * wolfSSL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
 */

#include <tests/unit.h>

#ifdef NO_INLINE
    #include <wolfssl/wolfcrypt/misc.h>
#else
    #define WOLFSSL_MISC_INCLUDED
    #include <wolfcrypt/src/misc.c>
#endif

#include <wolfssl/ssl.h>
#include <wolfssl/internal.h>

#include <tests/utils.h>
#include <tests/api/test_dtls.h>

int test_dtls12_basic_connection_id(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS_CID)
    unsigned char client_cid[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
    unsigned char server_cid[] = { 0, 1, 2, 3, 4, 5 };
    unsigned char readBuf[40];
    void *        cid = NULL;
    const char* params[] = {
#ifndef NO_RSA
#ifndef NO_SHA256
#if defined(WOLFSSL_AES_128) && defined(WOLFSSL_STATIC_RSA)
        "AES128-SHA256",
#ifdef HAVE_AESCCM
        "AES128-CCM8",
#endif
#endif /* WOLFSSL_AES_128 && WOLFSSL_STATIC_RSA */
#if defined(WOLFSSL_AES_128)
        "DHE-RSA-AES128-SHA256",
        "ECDHE-RSA-AES128-SHA256",
#ifdef HAVE_AESGCM
        "DHE-RSA-AES128-GCM-SHA256",
        "ECDHE-RSA-AES128-GCM-SHA256",
#endif
#endif /* WOLFSSL_AES_128 */
#endif /* NO_SHA256 */
#endif /* NO_RSA */
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) && !defined(HAVE_FIPS)
        "DHE-RSA-CHACHA20-POLY1305",
        "DHE-RSA-CHACHA20-POLY1305-OLD",
        "ECDHE-RSA-CHACHA20-POLY1305",
        "ECDHE-RSA-CHACHA20-POLY1305-OLD",
#endif
#ifndef NO_PSK
        "DHE-PSK-AES128-CBC-SHA256",
    #ifdef WOLFSSL_AES_256
        "DHE-PSK-AES256-GCM-SHA384",
    #endif
#ifdef HAVE_NULL_CIPHER
        "DHE-PSK-NULL-SHA256",
#endif
        "DHE-PSK-AES128-CCM",
#endif
    };
    size_t i;
    struct {
        byte drop:1;
        byte changeCID:1;
    } run_params[] = {
        { .drop = 0, .changeCID = 0 },
        { .drop = 1, .changeCID = 0 },
        { .drop = 0, .changeCID = 1 },
    };

    /* We check if the side included the CID in their output */
#define CLIENT_CID() mymemmem(test_ctx.s_buff, test_ctx.s_len, \
                              client_cid, sizeof(client_cid))
#define SERVER_CID() mymemmem(test_ctx.c_buff, test_ctx.c_len, \
                              server_cid, sizeof(server_cid))
#define RESET_CID(cid) if ((cid) != NULL) { \
                           ((char*)(cid))[0] = -1; \
                       }


    printf("\n");
    for (i = 0; i < XELEM_CNT(params) && EXPECT_SUCCESS(); i++) {
        size_t j;
        for (j = 0; j < XELEM_CNT(run_params); j++) {
            WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
            WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
            struct test_memio_ctx test_ctx;

            printf("Testing %s run #%ld ... ", params[i], (long int)j);

            XMEMSET(&test_ctx, 0, sizeof(test_ctx));

            ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
                &ssl_s, wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method),
                0);

            ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, params[i]), 1);
            ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, params[i]), 1);

            ExpectIntEQ(wolfSSL_dtls_cid_use(ssl_c), 1);
            ExpectIntEQ(wolfSSL_dtls_cid_set(ssl_c, server_cid,
                    sizeof(server_cid)), 1);
            ExpectIntEQ(wolfSSL_dtls_cid_use(ssl_s), 1);
            ExpectIntEQ(wolfSSL_dtls_cid_set(ssl_s, client_cid,
                    sizeof(client_cid)), 1);

#ifndef NO_PSK
            if (XSTRSTR(params[i], "-PSK-") != NULL) {
                wolfSSL_set_psk_client_callback(ssl_c, my_psk_client_cb);
                wolfSSL_set_psk_server_callback(ssl_s, my_psk_server_cb);
            }
#endif

#ifdef HAVE_SECURE_RENEGOTIATION
            ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_c), 1);
            ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_s), 1);
#endif

            /* CH1 */
            wolfSSL_SetLoggingPrefix("client");
            ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1);
            ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
            ExpectNull(CLIENT_CID());
            if (run_params[j].drop) {
                test_memio_clear_buffer(&test_ctx, 0);
                test_memio_clear_buffer(&test_ctx, 1);
                ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), 1);
                ExpectNull(CLIENT_CID());
            }
            /* HVR */
            wolfSSL_SetLoggingPrefix("server");
            ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1);
            ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
            ExpectNull(SERVER_CID());
            /* No point dropping HVR */
            /* CH2 */
            wolfSSL_SetLoggingPrefix("client");
            ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1);
            ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
            ExpectNull(CLIENT_CID());
            if (run_params[j].drop) {
                test_memio_clear_buffer(&test_ctx, 0);
                test_memio_clear_buffer(&test_ctx, 1);
                ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), 1);
                ExpectNull(CLIENT_CID());
            }
            /* Server first flight */
            wolfSSL_SetLoggingPrefix("server");
            ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1);
            ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
            ExpectNull(SERVER_CID());
            if (run_params[j].drop) {
                test_memio_clear_buffer(&test_ctx, 0);
                test_memio_clear_buffer(&test_ctx, 1);
                ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_s), 1);
                ExpectNull(SERVER_CID());
            }
            /* Client second flight */
            wolfSSL_SetLoggingPrefix("client");
            ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1);
            ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
            ExpectNotNull(CLIENT_CID());
            if (run_params[j].drop) {
                test_memio_clear_buffer(&test_ctx, 0);
                test_memio_clear_buffer(&test_ctx, 1);
                ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), 1);
                ExpectNotNull(CLIENT_CID());
            }
            /* Server second flight */
            wolfSSL_SetLoggingPrefix("server");
            ExpectIntEQ(wolfSSL_negotiate(ssl_s), 1);
            ExpectNotNull(SERVER_CID());
            if (run_params[j].drop) {
                test_memio_clear_buffer(&test_ctx, 0);
                test_memio_clear_buffer(&test_ctx, 1);
                ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_s), 1);
                ExpectNotNull(SERVER_CID());
            }
            /* Client complete connection */
            wolfSSL_SetLoggingPrefix("client");
            ExpectIntEQ(wolfSSL_negotiate(ssl_c), 1);
            ExpectNull(CLIENT_CID());

            /* Write some data */
            wolfSSL_SetLoggingPrefix("client");
            ExpectIntEQ(wolfSSL_write(ssl_c, params[i],
                    (int)XSTRLEN(params[i])), XSTRLEN(params[i]));
            ExpectNotNull(CLIENT_CID());
            wolfSSL_SetLoggingPrefix("server");
            ExpectIntEQ(wolfSSL_write(ssl_s, params[i],
                    (int)XSTRLEN(params[i])), XSTRLEN(params[i]));
            ExpectNotNull(SERVER_CID());
            /* Read the data */
            wolfSSL_SetLoggingPrefix("client");
            XMEMSET(readBuf, 0, sizeof(readBuf));
            ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)),
                    XSTRLEN(params[i]));
            ExpectStrEQ(readBuf, params[i]);
            XMEMSET(readBuf, 0, sizeof(readBuf));
            wolfSSL_SetLoggingPrefix("server");
            ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)),
                    XSTRLEN(params[i]));
            ExpectStrEQ(readBuf, params[i]);
            /* Write short data */
            wolfSSL_SetLoggingPrefix("client");
            ExpectIntEQ(wolfSSL_write(ssl_c, params[i], 1), 1);
            ExpectNotNull(CLIENT_CID());
            wolfSSL_SetLoggingPrefix("server");
            ExpectIntEQ(wolfSSL_write(ssl_s, params[i], 1), 1);
            ExpectNotNull(SERVER_CID());
            /* Read the short data */
            XMEMSET(readBuf, 0, sizeof(readBuf));
            wolfSSL_SetLoggingPrefix("client");
            ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), 1);
            ExpectIntEQ(readBuf[0], params[i][0]);
            XMEMSET(readBuf, 0, sizeof(readBuf));
            wolfSSL_SetLoggingPrefix("server");
            ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), 1);
            ExpectIntEQ(readBuf[0], params[i][0]);
            /* Write some data but with wrong CID */
            wolfSSL_SetLoggingPrefix("client");
            ExpectIntEQ(wolfSSL_write(ssl_c, params[i],
                    (int)XSTRLEN(params[i])), XSTRLEN(params[i]));
            /* Reset client cid. */
            ExpectNotNull(cid = CLIENT_CID());
            RESET_CID(cid);
            wolfSSL_SetLoggingPrefix("server");
            ExpectIntEQ(wolfSSL_write(ssl_s, params[i],
                    (int)XSTRLEN(params[i])), XSTRLEN(params[i]));
            /* Reset server cid. */
            ExpectNotNull(cid = SERVER_CID());
            RESET_CID(cid);
            /* Try to read the data but it shouldn't be there */
            wolfSSL_SetLoggingPrefix("client");
            ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
            ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
            wolfSSL_SetLoggingPrefix("server");
            ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
            ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);

#ifdef HAVE_SECURE_RENEGOTIATION
            /* do two SCR's */
            wolfSSL_SetLoggingPrefix("client");
            ExpectIntEQ(wolfSSL_Rehandshake(ssl_c), -1);
            ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
            ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
            ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
            ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
            /* SCR's after the first one have extra internal logic */
            wolfSSL_SetLoggingPrefix("client");
            ExpectIntEQ(wolfSSL_Rehandshake(ssl_c), -1);
            ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
            ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
            ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
            ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

            if (run_params[j].changeCID) {
                ExpectIntEQ(wolfSSL_dtls_cid_set(ssl_c, client_cid,
                        sizeof(client_cid)), 0);
                /* Forcefully change the CID */
                ssl_c->dtlsCidInfo->rx->id[0] = -1;
                /* We need to init the rehandshake from the client, otherwise
                 * we won't be able to test changing the CID. It would be
                 * rejected by the record CID matching code. */
                wolfSSL_SetLoggingPrefix("client");
                ExpectIntEQ(wolfSSL_Rehandshake(ssl_c), -1);
                ExpectIntEQ(wolfSSL_get_error(ssl_c, -1),
                        WOLFSSL_ERROR_WANT_READ);
                ExpectNotNull(CLIENT_CID());
                ExpectIntEQ(wolfSSL_SSL_renegotiate_pending(ssl_c), 1);
                /* Server first flight */
                wolfSSL_SetLoggingPrefix("server");
                ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
                /* We expect the server to reject the CID change. */
                ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), DTLS_CID_ERROR);
                goto loop_exit;
            }
            /* Server init'd SCR */
            /* Server request */
            wolfSSL_SetLoggingPrefix("server");
            ExpectIntEQ(wolfSSL_Rehandshake(ssl_s), -1);
            ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
            ExpectNotNull(SERVER_CID());
            ExpectIntEQ(wolfSSL_SSL_renegotiate_pending(ssl_s), 1);
            if (run_params[j].drop) {
                test_memio_clear_buffer(&test_ctx, 0);
                test_memio_clear_buffer(&test_ctx, 1);
                ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_s), 1);
                ExpectNotNull(SERVER_CID());
            }
            /* Init SCR on client side with the server's request */
            /* CH no HVR on SCR */
            XMEMSET(readBuf, 0, sizeof(readBuf));
            wolfSSL_SetLoggingPrefix("client");
            ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
            ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
            ExpectNotNull(CLIENT_CID());
            ExpectIntEQ(wolfSSL_SSL_renegotiate_pending(ssl_c), 1);
            if (run_params[j].drop) {
                test_memio_clear_buffer(&test_ctx, 0);
                test_memio_clear_buffer(&test_ctx, 1);
                ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), 1);
                ExpectNotNull(CLIENT_CID());
            }
            /* Server first flight */
            wolfSSL_SetLoggingPrefix("server");
            ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1);
            ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
            ExpectNotNull(SERVER_CID());
            if (run_params[j].drop) {
                test_memio_clear_buffer(&test_ctx, 0);
                test_memio_clear_buffer(&test_ctx, 1);
                ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_s), 1);
                ExpectNotNull(SERVER_CID());
            }
            /* Client second flight */
            wolfSSL_SetLoggingPrefix("client");
            ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1);
            ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
            ExpectNotNull(CLIENT_CID());
            if (run_params[j].drop) {
                test_memio_clear_buffer(&test_ctx, 0);
                test_memio_clear_buffer(&test_ctx, 1);
                ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), 1);
                ExpectNotNull(CLIENT_CID());
            }
            ExpectIntEQ(wolfSSL_write(ssl_c, params[i],
                    (int)XSTRLEN(params[i])), XSTRLEN(params[i]));
            /* Server second flight */
            wolfSSL_SetLoggingPrefix("server");
            ExpectIntEQ(wolfSSL_negotiate(ssl_s), 1);
            XMEMSET(readBuf, 0, sizeof(readBuf));
            ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)),
                    XSTRLEN(params[i]));
            ExpectStrEQ(readBuf, params[i]);
            if (!run_params[j].drop) {
                ExpectIntEQ(wolfSSL_write(ssl_s, params[i],
                        (int)XSTRLEN(params[i])), XSTRLEN(params[i]));
            }
            /* Test loading old epoch */
            /* Client complete connection */
            wolfSSL_SetLoggingPrefix("client");
            if (!run_params[j].drop) {
                ExpectIntEQ(wolfSSL_negotiate(ssl_c), 1);
                XMEMSET(readBuf, 0, sizeof(readBuf));
                ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)),
                        XSTRLEN(params[i]));
                ExpectStrEQ(readBuf, params[i]);
            }
            ExpectIntEQ(wolfSSL_negotiate(ssl_c), 1);
            ExpectNull(CLIENT_CID());
            ExpectIntEQ(wolfSSL_SSL_renegotiate_pending(ssl_c), 0);
            ExpectIntEQ(wolfSSL_SSL_renegotiate_pending(ssl_s), 0);
#endif
            /* Close connection */
            wolfSSL_SetLoggingPrefix("client");
            ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_SHUTDOWN_NOT_DONE);
            ExpectNotNull(CLIENT_CID());
            wolfSSL_SetLoggingPrefix("server");
            ExpectIntEQ(wolfSSL_shutdown(ssl_s), WOLFSSL_SHUTDOWN_NOT_DONE);
            ExpectNotNull(SERVER_CID());
            wolfSSL_SetLoggingPrefix("client");
            ExpectIntEQ(wolfSSL_shutdown(ssl_c), 1);
            wolfSSL_SetLoggingPrefix("server");
            ExpectIntEQ(wolfSSL_shutdown(ssl_s), 1);

#ifdef HAVE_SECURE_RENEGOTIATION
loop_exit:
#endif
            wolfSSL_SetLoggingPrefix(NULL);
            wolfSSL_free(ssl_c);
            wolfSSL_CTX_free(ctx_c);
            wolfSSL_free(ssl_s);
            wolfSSL_CTX_free(ctx_s);

            if (EXPECT_SUCCESS())
                printf("ok\n");
            else
                printf("failed\n");
        }

    }

#undef CLIENT_CID
#undef SERVER_CID
#undef RESET_CID
#endif
    return EXPECT_RESULT();
}

int test_dtls13_basic_connection_id(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) \
    && defined(WOLFSSL_DTLS_CID)
    unsigned char client_cid[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
    unsigned char server_cid[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    unsigned char readBuf[50];
    void *        cid = NULL;
    const char* params[] = {
#ifndef NO_SHA256
#ifdef WOLFSSL_AES_128
#ifdef HAVE_AESGCM
        "TLS13-AES128-GCM-SHA256",
#endif
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
        "TLS13-CHACHA20-POLY1305-SHA256",
#endif
#ifdef HAVE_AESCCM
        "TLS13-AES128-CCM-8-SHA256",
        "TLS13-AES128-CCM-SHA256",
#endif
#endif
#ifdef HAVE_NULL_CIPHER
        "TLS13-SHA256-SHA256",
#endif
#endif
    };
    size_t i;

    /* We check if the side included the CID in their output */
#define CLIENT_CID() mymemmem(test_ctx.s_buff, test_ctx.s_len, \
                              client_cid, sizeof(client_cid))
#define SERVER_CID() mymemmem(test_ctx.c_buff, test_ctx.c_len, \
                              server_cid, sizeof(server_cid))
#define RESET_CID(cid) if ((cid) != NULL) { \
                           ((char*)(cid))[0] = -1; \
                       }


    printf("\n");
    for (i = 0; i < XELEM_CNT(params) && EXPECT_SUCCESS(); i++) {
        WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
        WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
        struct test_memio_ctx test_ctx;

        printf("Testing %s ... ", params[i]);

        XMEMSET(&test_ctx, 0, sizeof(test_ctx));

        ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
            wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);

        ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, params[i]), WOLFSSL_SUCCESS);
        ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, params[i]), WOLFSSL_SUCCESS);

        ExpectIntEQ(wolfSSL_dtls_cid_use(ssl_c), 1);
        ExpectIntEQ(wolfSSL_dtls_cid_set(ssl_c, server_cid, sizeof(server_cid)),
                1);
        ExpectIntEQ(wolfSSL_dtls_cid_use(ssl_s), 1);
        ExpectIntEQ(wolfSSL_dtls_cid_set(ssl_s, client_cid, sizeof(client_cid)),
                1);

        /* CH1 */
        ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1);
        ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
        ExpectNull(CLIENT_CID());
        /* HRR */
        ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1);
        ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
        ExpectNull(SERVER_CID());
        /* CH2 */
        ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1);
        ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
        ExpectNull(CLIENT_CID());
        /* Server first flight */
        ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1);
        ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
        ExpectNotNull(SERVER_CID());
        /* Client second flight */
        ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1);
        ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
        ExpectNotNull(CLIENT_CID());
        /* Server process flight */
        ExpectIntEQ(wolfSSL_negotiate(ssl_s), 1);
        /* Client process flight */
        ExpectIntEQ(wolfSSL_negotiate(ssl_c), 1);

        /* Write some data */
        ExpectIntEQ(wolfSSL_write(ssl_c, params[i], (int)XSTRLEN(params[i])),
                XSTRLEN(params[i]));
        ExpectNotNull(CLIENT_CID());
        ExpectIntEQ(wolfSSL_write(ssl_s, params[i], (int)XSTRLEN(params[i])),
                XSTRLEN(params[i]));
        ExpectNotNull(SERVER_CID());
        /* Read the data */
        XMEMSET(readBuf, 0, sizeof(readBuf));
        ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)),
                XSTRLEN(params[i]));
        ExpectStrEQ(readBuf, params[i]);
        XMEMSET(readBuf, 0, sizeof(readBuf));
        ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)),
                XSTRLEN(params[i]));
        ExpectStrEQ(readBuf, params[i]);
        /* Write short data */
        ExpectIntEQ(wolfSSL_write(ssl_c, params[i], 1), 1);
        ExpectNotNull(CLIENT_CID());
        ExpectIntEQ(wolfSSL_write(ssl_s, params[i], 1), 1);
        ExpectNotNull(SERVER_CID());
        /* Read the short data */
        XMEMSET(readBuf, 0, sizeof(readBuf));
        ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), 1);
        ExpectIntEQ(readBuf[0], params[i][0]);
        XMEMSET(readBuf, 0, sizeof(readBuf));
        ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), 1);
        ExpectIntEQ(readBuf[0], params[i][0]);
        /* Write some data but with wrong CID */
        ExpectIntEQ(wolfSSL_write(ssl_c, params[i], (int)XSTRLEN(params[i])),
                XSTRLEN(params[i]));
        /* Reset client cid. */
        ExpectNotNull(cid = CLIENT_CID());
        RESET_CID(cid);
        ExpectIntEQ(wolfSSL_write(ssl_s, params[i], (int)XSTRLEN(params[i])),
                XSTRLEN(params[i]));
        /* Reset server cid. */
        ExpectNotNull(cid = SERVER_CID());
        RESET_CID(cid);
        /* Try to read the data but it shouldn't be there */
        ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
        ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
        ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
        ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);

        /* Close connection */
        ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_SHUTDOWN_NOT_DONE);
        ExpectNotNull(CLIENT_CID());
        ExpectIntEQ(wolfSSL_shutdown(ssl_s), WOLFSSL_SHUTDOWN_NOT_DONE);
        ExpectNotNull(SERVER_CID());
        ExpectIntEQ(wolfSSL_shutdown(ssl_c), 1);
        ExpectIntEQ(wolfSSL_shutdown(ssl_s), 1);

        if (EXPECT_SUCCESS())
            printf("ok\n");
        else
            printf("failed\n");

        wolfSSL_free(ssl_c);
        wolfSSL_CTX_free(ctx_c);
        wolfSSL_free(ssl_s);
        wolfSSL_CTX_free(ctx_s);
    }

#undef CLIENT_CID
#undef SERVER_CID
#undef RESET_CID

#endif
    return EXPECT_RESULT();
}

/** Test DTLS 1.3 behavior when server hits WANT_WRITE during HRR
 * The test sets up a DTLS 1.3 connection where the server is forced to
 * return WANT_WRITE when sending the HelloRetryRequest. After the handshake,
 * application data is exchanged in both directions to verify the connection
 * works as expected.
 */
int test_dtls13_hrr_want_write(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13)
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    const char msg[] = "hello";
    const int msgLen = sizeof(msg);
    struct test_memio_ctx test_ctx;
    char readBuf[sizeof(msg)];

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                    wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method),
        0);

    /* Client sends first ClientHello */
    ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);

    /* Force server to hit WANT_WRITE when producing the HRR */
    test_memio_simulate_want_write(&test_ctx, 0, 1);
    ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_WRITE);

    /* Allow the server to flush the HRR and proceed */
    test_memio_simulate_want_write(&test_ctx, 0, 0);
    ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);

    /* Resume the DTLS 1.3 handshake */
    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

    /* Verify post-handshake application data in both directions */
    XMEMSET(readBuf, 0, sizeof(readBuf));
    ExpectIntEQ(wolfSSL_write(ssl_c, msg, msgLen), msgLen);
    ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), msgLen);
    ExpectStrEQ(readBuf, msg);

    XMEMSET(readBuf, 0, sizeof(readBuf));
    ExpectIntEQ(wolfSSL_write(ssl_s, msg, msgLen), msgLen);
    ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), msgLen);
    ExpectStrEQ(readBuf, msg);

    wolfSSL_free(ssl_c);
    wolfSSL_CTX_free(ctx_c);
    wolfSSL_free(ssl_s);
    wolfSSL_CTX_free(ctx_s);
#endif
    return EXPECT_RESULT();
}

#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13)
struct test_dtls13_wwrite_ctx {
    int want_write;
    struct test_memio_ctx *text_ctx;
};
static int test_dtls13_want_write_send_cb(WOLFSSL *ssl, char *data, int sz, void *ctx)
{
    struct test_dtls13_wwrite_ctx *wwctx = (struct test_dtls13_wwrite_ctx *)ctx;
    wwctx->want_write = !wwctx->want_write;
    if (wwctx->want_write) {
        return WOLFSSL_CBIO_ERR_WANT_WRITE;
    }
    return test_memio_write_cb(ssl, data, sz, wwctx->text_ctx);
}
#endif
/** Test DTLS 1.3 behavior when every other write returns WANT_WRITE
 * The test sets up a DTLS 1.3 connection where both client and server
 * alternate between WANT_WRITE and successful writes. After the handshake,
 * application data is exchanged in both directions to verify the connection
 * works as expected.
 *
 * Data exchanged after the handshake is also tested with simulated WANT_WRITE
 * conditions to ensure the connection remains functional.
 */
int test_dtls13_every_write_want_write(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13)
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;
    const char msg[] = "want-write";
    const int msgLen = sizeof(msg);
    char readBuf[sizeof(msg)];
    struct test_dtls13_wwrite_ctx wwctx_c;
    struct test_dtls13_wwrite_ctx wwctx_s;

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                    wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method),
        0);

    wwctx_c.want_write = 0;
    wwctx_c.text_ctx = &test_ctx;
    wolfSSL_SetIOWriteCtx(ssl_c, &wwctx_c);
    wolfSSL_SSLSetIOSend(ssl_c, test_dtls13_want_write_send_cb);
    wwctx_s.want_write = 0;
    wwctx_s.text_ctx = &test_ctx;
    wolfSSL_SetIOWriteCtx(ssl_s, &wwctx_s);
    wolfSSL_SSLSetIOSend(ssl_s, test_dtls13_want_write_send_cb);

    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);

    ExpectTrue(wolfSSL_is_init_finished(ssl_c));
    ExpectTrue(wolfSSL_is_init_finished(ssl_s));

    test_memio_simulate_want_write(&test_ctx, 0, 0);
    test_memio_simulate_want_write(&test_ctx, 1, 0);

    wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
    wolfSSL_SSLSetIOSend(ssl_c, test_memio_write_cb);
    wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
    wolfSSL_SSLSetIOSend(ssl_s, test_memio_write_cb);

    XMEMSET(readBuf, 0, sizeof(readBuf));
    ExpectIntEQ(wolfSSL_write(ssl_c, msg, msgLen), msgLen);
    ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), msgLen);
    ExpectStrEQ(readBuf, msg);

    XMEMSET(readBuf, 0, sizeof(readBuf));
    ExpectIntEQ(wolfSSL_write(ssl_s, msg, msgLen), msgLen);
    ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), msgLen);
    ExpectStrEQ(readBuf, msg);

    test_memio_simulate_want_write(&test_ctx, 0, 1);
    XMEMSET(readBuf, 0, sizeof(readBuf));
    ExpectIntEQ(wolfSSL_write(ssl_s, msg, msgLen), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_WRITE);
    test_memio_simulate_want_write(&test_ctx, 0, 0);
    ExpectIntEQ(wolfSSL_write(ssl_s, msg, msgLen), msgLen);
    ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), msgLen);
    ExpectStrEQ(readBuf, msg);

    XMEMSET(readBuf, 0, sizeof(readBuf));
    test_memio_simulate_want_write(&test_ctx, 1, 1);
    ExpectIntEQ(wolfSSL_write(ssl_c, msg, msgLen), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_WRITE);
    test_memio_simulate_want_write(&test_ctx, 1, 0);
    ExpectIntEQ(wolfSSL_write(ssl_c, msg, msgLen), msgLen);
    ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), msgLen);
    ExpectStrEQ(readBuf, msg);

    wolfSSL_free(ssl_c);
    wolfSSL_CTX_free(ctx_c);
    wolfSSL_free(ssl_s);
    wolfSSL_CTX_free(ctx_s);
#endif
    return EXPECT_RESULT();
}

int test_wolfSSL_dtls_cid_parse(void)
{
    EXPECT_DECLS;
#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID)
    /* Taken from Wireshark. Right-click -> copy -> ... as escaped string */
    /* Plaintext ServerHelloDone. No CID. */
    byte noCid[] =
            "\x16\xfe\xfd\x00\x00\x00\x00\x00\x00\x00\x04\x00\x0c\x0e\x00\x00" \
            "\x00\x00\x04\x00\x00\x00\x00\x00\x00";
    /* 1.2 app data containing CID */
    byte cid12[] =
            "\x19\xfe\xfd\x00\x01\x00\x00\x00\x00\x00\x01\x77\xa3\x79\x34\xb3" \
            "\xf1\x1f\x34\x00\x1f\xdb\x8c\x28\x25\x9f\xe1\x02\x26\x77\x1c\x3a" \
            "\x50\x1b\x50\x99\xd0\xb5\x20\xd8\x2c\x2e\xaa\x36\x36\xe0\xb7\xb7" \
            "\xf7\x7d\xff\xb0";
#ifdef WOLFSSL_DTLS13
    /* 1.3 app data containing CID */
    byte cid13[] =
            "\x3f\x70\x64\x04\xc6\xfb\x97\x21\xd9\x28\x27\x00\x17\xc1\x01\x86" \
            "\xe7\x23\x2c\xad\x65\x83\xa8\xf4\xbf\xbf\x7b\x25\x16\x80\x19\xc3" \
            "\x81\xda\xf5\x3f";
#endif

    ExpectPtrEq(wolfSSL_dtls_cid_parse(noCid, sizeof(noCid), 8), NULL);
    ExpectPtrEq(wolfSSL_dtls_cid_parse(cid12, sizeof(cid12), 8), cid12 + 11);
#ifdef WOLFSSL_DTLS13
    ExpectPtrEq(wolfSSL_dtls_cid_parse(cid13, sizeof(cid13), 8), cid13 + 1);
#endif
#endif
    return EXPECT_RESULT();
}

int test_wolfSSL_dtls_set_pending_peer(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
    defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID)
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;
    unsigned char peer[10];
    unsigned int peerSz;
    unsigned char readBuf[10];
    unsigned char client_cid[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
    unsigned char server_cid[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    /* Setup DTLS contexts */
    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
            wolfDTLS_client_method, wolfDTLS_server_method), 0);

    ExpectIntEQ(wolfSSL_dtls_cid_use(ssl_c), 1);
    ExpectIntEQ(wolfSSL_dtls_cid_set(ssl_c, server_cid,
            sizeof(server_cid)), 1);
    ExpectIntEQ(wolfSSL_dtls_cid_use(ssl_s), 1);
    ExpectIntEQ(wolfSSL_dtls_cid_set(ssl_s, client_cid,
            sizeof(client_cid)), 1);

    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

    peerSz = sizeof(peer);
    /* Fail since no peer set */
    ExpectIntEQ(wolfSSL_dtls_get_peer(ssl_s, peer, &peerSz), 0);
    ExpectIntEQ(wolfSSL_dtls_set_pending_peer(ssl_s, (void*)"123", 4), 1);
    ExpectIntEQ(wolfSSL_write(ssl_c, "test", 5), 5);
    ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), 5);
    ExpectStrEQ(readBuf, "test");
    peerSz = sizeof(peer);
    ExpectIntEQ(wolfSSL_dtls_get_peer(ssl_s, peer, &peerSz), 1);
    ExpectIntEQ(peerSz, 4);
    ExpectStrEQ(peer, "123");

    wolfSSL_free(ssl_s);
    wolfSSL_free(ssl_c);
    wolfSSL_CTX_free(ctx_s);
    wolfSSL_CTX_free(ctx_c);
#endif
    return EXPECT_RESULT();
}

int test_dtls13_epochs(void) {
    EXPECT_DECLS;
#if defined(WOLFSSL_DTLS13) && !defined(NO_WOLFSSL_CLIENT)
    WOLFSSL_CTX* ctx = NULL;
    WOLFSSL* ssl = NULL;
    byte input[20];
    word32 inOutIdx = 0;

    XMEMSET(input, 0, sizeof(input));

    ExpectNotNull(ctx = wolfSSL_CTX_new(wolfDTLSv1_3_client_method()));
    ExpectNotNull(ssl = wolfSSL_new(ctx));
    /* Some manual setup to enter the epoch check */
    ExpectTrue(ssl->options.tls1_3 = 1);

    inOutIdx = 0;
    if (ssl != NULL) ssl->keys.curEpoch64 = w64From32(0x0, 0x0);
    ExpectIntEQ(DoApplicationData(ssl, input, &inOutIdx, 0), SANITY_MSG_E);
    inOutIdx = 0;
    if (ssl != NULL) ssl->keys.curEpoch64 = w64From32(0x0, 0x2);
    ExpectIntEQ(DoApplicationData(ssl, input, &inOutIdx, 0), SANITY_MSG_E);

    if (ssl != NULL) ssl->keys.curEpoch64 = w64From32(0x0, 0x1);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, client_hello), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, server_hello), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, hello_verify_request), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, hello_retry_request), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, hello_request), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, encrypted_extensions), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, server_key_exchange), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, server_hello_done), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, client_key_exchange), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, certificate_request), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, certificate), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, certificate_verify), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, finished), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, certificate_status), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, change_cipher_hs), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, key_update), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, session_ticket), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, end_of_early_data), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, message_hash), SANITY_MSG_E);
    ExpectIntEQ(Dtls13CheckEpoch(ssl, no_shake), SANITY_MSG_E);

    wolfSSL_CTX_free(ctx);
    wolfSSL_free(ssl);
#endif
    return EXPECT_RESULT();
}

int test_dtls13_ack_order(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13)
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;
    unsigned char readBuf[50];
    word32 length = 0;
    /* struct {
     *     uint64 epoch;
     *     uint64 sequence_number;
     * } RecordNumber;
     * Big endian */
    static const unsigned char expected_output[] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
    };

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    /* Get a populated DTLS object */
    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
        wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);
    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
    ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
    /* Clear the buffer of any extra messages */
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
    ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
    ExpectIntEQ(test_ctx.c_len, 0);
    ExpectIntEQ(test_ctx.s_len, 0);

    /* Add seen records */
    ExpectIntEQ(Dtls13RtxAddAck(ssl_c, w64From32(0, 3), w64From32(0, 2)), 0);
    ExpectIntEQ(Dtls13RtxAddAck(ssl_c, w64From32(0, 3), w64From32(0, 0)), 0);
    ExpectIntEQ(Dtls13RtxAddAck(ssl_c, w64From32(0, 3), w64From32(0, 1)), 0);
    ExpectIntEQ(Dtls13RtxAddAck(ssl_c, w64From32(0, 3), w64From32(0, 4)), 0);
    ExpectIntEQ(Dtls13RtxAddAck(ssl_c, w64From32(0, 2), w64From32(0, 0)), 0);
    ExpectIntEQ(Dtls13RtxAddAck(ssl_c, w64From32(0, 3), w64From32(0, 6)), 0);
    ExpectIntEQ(Dtls13RtxAddAck(ssl_c, w64From32(0, 3), w64From32(0, 6)), 0);
    ExpectIntEQ(Dtls13RtxAddAck(ssl_c, w64From32(0, 2), w64From32(0, 1)), 0);
    ExpectIntEQ(Dtls13RtxAddAck(ssl_c, w64From32(0, 2), w64From32(0, 2)), 0);
    ExpectIntEQ(Dtls13RtxAddAck(ssl_c, w64From32(0, 2), w64From32(0, 2)), 0);
    ExpectIntEQ(Dtls13WriteAckMessage(ssl_c, ssl_c->dtls13Rtx.seenRecords,
            ssl_c->dtls13Rtx.seenRecordsCount, &length), 0);

    /* must zero the span reserved for the header to avoid read of uninited
     * data.
     */
    XMEMSET(ssl_c->buffers.outputBuffer.buffer, 0,
            5 /* DTLS13_UNIFIED_HEADER_SIZE */);
    /* N * RecordNumber + 2 extra bytes for length */
    ExpectIntEQ(length, sizeof(expected_output) + 2);
    ExpectNotNull(mymemmem(ssl_c->buffers.outputBuffer.buffer,
            ssl_c->buffers.outputBuffer.bufferSize, expected_output,
            sizeof(expected_output)));

    wolfSSL_free(ssl_c);
    wolfSSL_CTX_free(ctx_c);
    wolfSSL_free(ssl_s);
    wolfSSL_CTX_free(ctx_s);
#endif
    return EXPECT_RESULT();
}

int test_dtls13_ack_overflow(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13)
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;
    unsigned char readBuf[50];
    word32 length = 0;
    int i;

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
        wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);
    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
    ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
    ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);

    /* Edge case 1: one below limit - all inserts must succeed */
    for (i = 0; i < DTLS13_ACK_MAX_RECORDS - 1; i++) {
        ExpectIntEQ(Dtls13RtxAddAck(ssl_c, w64From32(0, 0),
                                    w64From32(0, (word32)i)), 0);
    }
    ExpectIntEQ(ssl_c->dtls13Rtx.seenRecordsCount, DTLS13_ACK_MAX_RECORDS - 1);

    /* Edge case 2: insert the last allowed record - must succeed */
    ExpectIntEQ(Dtls13RtxAddAck(ssl_c, w64From32(0, 0),
                    w64From32(0, (word32)(DTLS13_ACK_MAX_RECORDS - 1))), 0);
    ExpectIntEQ(ssl_c->dtls13Rtx.seenRecordsCount, DTLS13_ACK_MAX_RECORDS);

    /* Writing a full-but-valid list must succeed */
    ExpectIntEQ(Dtls13WriteAckMessage(ssl_c, ssl_c->dtls13Rtx.seenRecords,
                    ssl_c->dtls13Rtx.seenRecordsCount, &length), 0);

    /* Edge case 3: one over limit - must be silently dropped */
    ExpectIntEQ(Dtls13RtxAddAck(ssl_c, w64From32(0, 0),
                    w64From32(0, (word32)DTLS13_ACK_MAX_RECORDS)), 0);
    ExpectIntEQ(ssl_c->dtls13Rtx.seenRecordsCount, DTLS13_ACK_MAX_RECORDS);

    /* Bypass the insert guard to force the list one element over the limit,
     * then verify Dtls13WriteAckMessage errors out instead of overflowing */
    ssl_c->dtls13Rtx.seenRecordsCount = 0;
    ExpectIntEQ(Dtls13RtxAddAck(ssl_c, w64From32(0, 1),
                    w64From32(0, (word32)DTLS13_ACK_MAX_RECORDS)), 0);
    ssl_c->dtls13Rtx.seenRecordsCount = (word16)(DTLS13_ACK_MAX_RECORDS + 1);
    ExpectIntEQ(Dtls13WriteAckMessage(ssl_c, ssl_c->dtls13Rtx.seenRecords,
                    ssl_c->dtls13Rtx.seenRecordsCount, &length), BUFFER_E);

    wolfSSL_free(ssl_c);
    wolfSSL_CTX_free(ctx_c);
    wolfSSL_free(ssl_s);
    wolfSSL_CTX_free(ctx_s);
#endif
    return EXPECT_RESULT();
}

int test_dtls13_ack_dup_write_counter(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) \
    && defined(HAVE_WRITE_DUP)
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    WOLFSSL *ssl_c2 = NULL;
    struct test_memio_ctx test_ctx;
    unsigned char readBuf[50];
    int i;

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
        wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);
    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
    /* Drain any post-handshake messages */
    ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
    ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);

    /* Split ssl_c: ssl_c becomes READ_DUP_SIDE, ssl_c2 becomes WRITE_DUP_SIDE */
    ExpectNotNull(ssl_c2 = wolfSSL_write_dup(ssl_c));

    /* Cycle 1: add records, trigger handoff, verify counter is reset to 0 */
    for (i = 0; i < 5; i++)
        ExpectIntEQ(Dtls13RtxAddAck(ssl_c, w64From32(0, 3),
                                    w64From32(0, (word32)i)), 0);
    ExpectIntEQ(ssl_c->dtls13Rtx.seenRecordsCount, 5);
    ssl_c->dtls13Rtx.sendAcks = 1;
    ExpectIntEQ(Dtls13DoScheduledWork(ssl_c), 0);
    /* seenRecords ownership was transferred to dupWrite->sendAckList;
     * seenRecordsCount must be reset to 0,  not left at 5. */
    ExpectNull(ssl_c->dtls13Rtx.seenRecords);
    ExpectIntEQ(ssl_c->dtls13Rtx.seenRecordsCount, 0);

    /* Cycle 2 (different epoch to avoid the dup-filter): verify the counter
     * did not accumulate across the previous transfer.  Without the fix,
     * seenRecordsCount would now be 10 after this second batch. */
    for (i = 0; i < 5; i++)
        ExpectIntEQ(Dtls13RtxAddAck(ssl_c, w64From32(0, 4),
                                    w64From32(0, (word32)i)), 0);
    ExpectIntEQ(ssl_c->dtls13Rtx.seenRecordsCount, 5);
    ssl_c->dtls13Rtx.sendAcks = 1;
    ExpectIntEQ(Dtls13DoScheduledWork(ssl_c), 0);
    ExpectNull(ssl_c->dtls13Rtx.seenRecords);
    ExpectIntEQ(ssl_c->dtls13Rtx.seenRecordsCount, 0);

    wolfSSL_free(ssl_c);
    wolfSSL_free(ssl_c2);
    wolfSSL_CTX_free(ctx_c);
    wolfSSL_free(ssl_s);
    wolfSSL_CTX_free(ctx_s);
#endif
    return EXPECT_RESULT();
}

int test_dtls_version_checking(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS)
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                    wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method),
        0);

    /* CH */
    ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_FATAL_ERROR);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
        WOLFSSL_ERROR_WANT_READ);

    /* modify CH DTLS header to have version 1.1 (0xfe, 0xfe) */
    ExpectIntGE(test_ctx.s_len, 3);
    if (EXPECT_SUCCESS()) {
        test_ctx.s_buff[1] = 0xfe;
        test_ctx.s_buff[2] = 0xfe;
    }

    ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
        WOLFSSL_ERROR_WANT_READ);
    /* server should drop the message */
    ExpectIntEQ(test_ctx.c_len, 0);

    wolfSSL_free(ssl_c);
    ssl_c = wolfSSL_new(ctx_c);
    ExpectNotNull(ssl_c);
    wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
    wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);

    /* try again */
    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

    wolfSSL_free(ssl_c);
    wolfSSL_CTX_free(ctx_c);
    wolfSSL_free(ssl_s);
    wolfSSL_CTX_free(ctx_s);
#endif /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES && WOLFSSL_DTLS */
    return EXPECT_RESULT();
}

#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS)
static int test_dtls_shutdown(WOLFSSL *s, WOLFSSL *c, WOLFSSL_CTX *cc, WOLFSSL_CTX *cs)
{
    EXPECT_DECLS;
    /* Cleanup */
    wolfSSL_SetLoggingPrefix("client");
    ExpectIntEQ(wolfSSL_shutdown(c), WOLFSSL_SHUTDOWN_NOT_DONE);
    wolfSSL_SetLoggingPrefix("server");
    ExpectIntEQ(wolfSSL_shutdown(s), WOLFSSL_SHUTDOWN_NOT_DONE);
    wolfSSL_SetLoggingPrefix("client");
    ExpectIntEQ(wolfSSL_shutdown(c), 1);
    wolfSSL_SetLoggingPrefix("server");
    ExpectIntEQ(wolfSSL_shutdown(s), 1);

    wolfSSL_SetLoggingPrefix(NULL);
    wolfSSL_free(c);
    wolfSSL_CTX_free(cc);
    wolfSSL_free(s);
    wolfSSL_CTX_free(cs);
    return EXPECT_RESULT();
}

static int test_dtls_communication(WOLFSSL *s, WOLFSSL *c)
{
    EXPECT_DECLS;
    unsigned char readBuf[50];

    wolfSSL_SetLoggingPrefix("client");
    ExpectIntEQ(wolfSSL_write(c, "client message", 14), 14);

    wolfSSL_SetLoggingPrefix("server");
    XMEMSET(readBuf, 0, sizeof(readBuf));
    ExpectIntEQ(wolfSSL_read(s, readBuf, sizeof(readBuf)), 14);
    ExpectStrEQ(readBuf, "client message");

    wolfSSL_SetLoggingPrefix("server");
    ExpectIntEQ(wolfSSL_write(s, "server message", 14), 14);

    wolfSSL_SetLoggingPrefix("client");
    XMEMSET(readBuf, 0, sizeof(readBuf));
    ExpectIntEQ(wolfSSL_read(c, readBuf, sizeof(readBuf)), 14);
    ExpectStrEQ(readBuf, "server message");

    /* this extra round is consuming newSessionTicket and acks */
    wolfSSL_SetLoggingPrefix("client");
    ExpectIntEQ(wolfSSL_write(c, "client message 2", 16), 16);

    wolfSSL_SetLoggingPrefix("server");
    XMEMSET(readBuf, 0, sizeof(readBuf));
    ExpectIntEQ(wolfSSL_read(s, readBuf, sizeof(readBuf)), 16);
    ExpectStrEQ(readBuf, "client message 2");

    wolfSSL_SetLoggingPrefix("server");
    ExpectIntEQ(wolfSSL_write(s, "server message 2", 16), 16);

    wolfSSL_SetLoggingPrefix("client");
    XMEMSET(readBuf, 0, sizeof(readBuf));
    ExpectIntEQ(wolfSSL_read(c, readBuf, sizeof(readBuf)), 16);
    ExpectStrEQ(readBuf, "server message 2");

    return EXPECT_RESULT();
}

#if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS)
int test_dtls13_longer_length(void)
{
    EXPECT_DECLS;
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;
    unsigned char readBuf[50];
    int seq16bit = 0;
    int ret;

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    /* Setup DTLS contexts */
    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                    wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method),
        0);

    /* Complete handshake */
    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

    /* Create good record with length mismatch */
    wolfSSL_SetLoggingPrefix("client");
    ExpectIntEQ(wolfSSL_write(ssl_c, "client message", 14), 14);

    /* check client wrote the record */
    ExpectIntGT(test_ctx.s_len, 14);
    /* check length is included  in the record header */
    ExpectIntGT(test_ctx.s_buff[0x0] & (1 << 2), 0);
    seq16bit = (test_ctx.s_buff[0x0] & (1 << 3)) != 0;
    /* big endian, modify LSB byte */
    seq16bit *= 2;
    /* modify length to be bigger */
    test_ctx.s_buff[0x2 + seq16bit] = 0xff;

    /* Try to read the malformed record */
    wolfSSL_SetLoggingPrefix("server");
    ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
    ExpectIntEQ(test_ctx.s_len, 0);

    ExpectIntEQ(test_dtls_communication(ssl_s, ssl_c), TEST_SUCCESS);
    ret = test_dtls_shutdown(ssl_s, ssl_c, ctx_c, ctx_s);
    ExpectIntEQ(ret, TEST_SUCCESS);

    return EXPECT_RESULT();
}
#else
int test_dtls13_longer_length(void)
{
    return TEST_SKIPPED;
}
#endif /* WOLFSSL_DTLS13 && !defined(WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS) */

#if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS)
int test_dtls13_short_read(void)
{
    EXPECT_DECLS;
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;
    unsigned char readBuf[50];
    int i;
    int ret;

    /* we setup two test, in the first one the server reads just two bytes of
     * the header, in the second one it reads just the header (5) */
    for (i = 0; i < 2; i++) {
        XMEMSET(&test_ctx, 0, sizeof(test_ctx));

        /* Setup DTLS contexts */
        ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                        wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method),
            0);

        /* Complete handshake */
        ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

        /* create a good record in the buffer */
        wolfSSL_SetLoggingPrefix("client");
        ExpectIntEQ(wolfSSL_write(ssl_c, "client message", 14), 14);

        /* check client wrote the record */
        ExpectIntGT(test_ctx.s_len, 14);
        /* return less data */
        ExpectIntEQ(
            test_memio_modify_message_len(&test_ctx, 0, 0, i == 0 ? 2 : 5), 0);
        /* Try to read the malformed record */
        wolfSSL_SetLoggingPrefix("server");
        ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
        ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
        ExpectIntEQ(test_ctx.s_len, 0);

        ExpectIntEQ(test_dtls_communication(ssl_s, ssl_c), TEST_SUCCESS);
        ret = test_dtls_shutdown(ssl_s, ssl_c, ctx_c, ctx_s);
        ExpectIntEQ(ret, TEST_SUCCESS);
        ssl_c = ssl_s = NULL;
        ctx_c = ctx_s = NULL;
    }

    return EXPECT_RESULT();
}
#else
int test_dtls13_short_read(void)
{
    return TEST_SKIPPED;
}
#endif /* WOLFSSL_DTLS13 && !defined(WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS) */

#if !defined(WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS)
int test_dtls12_short_read(void)
{
    EXPECT_DECLS;
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;
    unsigned char readBuf[50];
    int i;
    int ret;

    for (i = 0; i < 3; i++) {
        XMEMSET(&test_ctx, 0, sizeof(test_ctx));

        /* Setup DTLS contexts */
        ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                        wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method),
            0);
        /* Complete handshake */
        ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

        /* create a good record in the buffer */
        wolfSSL_SetLoggingPrefix("client");
        ExpectIntEQ(wolfSSL_write(ssl_c, "bad", 3), 3);

        /* check client wrote the record */
        ExpectIntGT(test_ctx.s_len, 13 + 3);
        /* return less data */
        switch (i) {
        case 0:
            ExpectIntEQ(test_memio_modify_message_len(&test_ctx, 0, 0, 2), 0);
            break;
        case 1:
            ExpectIntEQ(test_memio_modify_message_len(&test_ctx, 0, 0, 13), 0);
            break;
        case 2:
            ExpectIntEQ(test_memio_modify_message_len(&test_ctx, 0, 0, 15), 0);
            break;
        }

        /* Try to read the malformed record */
        wolfSSL_SetLoggingPrefix("server");
        ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
        ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
        ExpectIntEQ(test_ctx.s_len, 0);

        ExpectIntEQ(test_dtls_communication(ssl_s, ssl_c), TEST_SUCCESS);
        ret = test_dtls_shutdown(ssl_s, ssl_c, ctx_c, ctx_s);
        ExpectIntEQ(ret, TEST_SUCCESS);
        ssl_c = ssl_s = NULL;
        ctx_c = ctx_s = NULL;
    }

    return EXPECT_RESULT();
}
#else
int test_dtls12_short_read(void)
{
    return TEST_SKIPPED;
}
#endif /* !defined(WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS) */

#if !defined(WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS)
int test_dtls12_record_length_mismatch(void)
{
    EXPECT_DECLS;
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;
    unsigned char readBuf[50];
    int ret;

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    /* Setup DTLS contexts */
    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                    wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method),
        0);

    /* Complete handshake */
    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

    /* write a message from client */
    ExpectIntEQ(wolfSSL_write(ssl_c, "bad", 3), 3);

    /* check that the message is written in the buffer */
    ExpectIntGT(test_ctx.s_len, 14);
    /* modify the length field to be bigger than the content */
    test_ctx.s_buff[12] = 0xff;

    /* Try to read the malformed record */
    wolfSSL_SetLoggingPrefix("server");
    ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
    ExpectIntEQ(test_ctx.s_len, 0);

    ExpectIntEQ(test_dtls_communication(ssl_s, ssl_c), TEST_SUCCESS);
    ret = test_dtls_shutdown(ssl_s, ssl_c, ctx_c, ctx_s);
    ExpectIntEQ(ret, TEST_SUCCESS);

    return EXPECT_RESULT();
}

int test_dtls_record_cross_boundaries(void)
{
    EXPECT_DECLS;
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;
    unsigned char readBuf[256];
    int rec0_len, rec1_len;

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    /* Setup DTLS contexts */
    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                    wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method),
        0);

    /* Complete handshake */
    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

    /* create a first record in the buffer */
    wolfSSL_SetLoggingPrefix("client");
    ExpectIntEQ(wolfSSL_write(ssl_c, "test0", 5), 5);
    rec0_len = test_ctx.s_msg_sizes[0];

    /* create a second record in the buffer */
    ExpectIntEQ(wolfSSL_write(ssl_c, "test1", 5), 5);
    rec1_len = test_ctx.s_msg_sizes[1];

    ExpectIntLE(rec0_len + rec1_len, sizeof(readBuf));
    if (EXPECT_SUCCESS())
        XMEMCPY(readBuf, test_ctx.s_buff, rec0_len + rec1_len);

    /* clear buffer */
    test_memio_clear_buffer(&test_ctx, 0);

    /* inject first record + 1 bytes of second record */
    ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, (const char*)readBuf,
                    rec0_len + 1),
        0);

    /* inject second record */
    ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
                    (const char*)readBuf + rec0_len + 1, rec1_len - 1),
        0);
    ExpectIntEQ(test_ctx.s_len, rec0_len + rec1_len);

    /* reading the record should return just the first message*/
    wolfSSL_SetLoggingPrefix("server");
    ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), 5);
    ExpectBufEQ(readBuf, "test0", 5);

    ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)),
        WOLFSSL_FATAL_ERROR);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);

    /* cleanup */
    wolfSSL_free(ssl_s);
    wolfSSL_free(ssl_c);
    wolfSSL_CTX_free(ctx_s);
    wolfSSL_CTX_free(ctx_c);

    return EXPECT_RESULT();
}
#else
int test_dtls12_record_length_mismatch(void)
{
    return TEST_SKIPPED;
}
int test_dtls_record_cross_boundaries(void)
{
    return TEST_SKIPPED;
}
#endif /* !defined(WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS) */

int test_dtls_short_ciphertext(void)
{
    EXPECT_DECLS;
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;
    unsigned char readBuf[50];
    int ret;

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    /* Setup DTLS contexts */
    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                    wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method),
        0);

    /* Complete handshake */
    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

    /* Create a message, that looks encrypted but shorter than minimum ciphertext length */
    /* create the data in the buffer */
    ExpectIntEQ(wolfSSL_write(ssl_c, "bad", 3), 3);

    /* check client wrote the record */
    ExpectIntGT(test_ctx.s_len, 14);

    /* modify the length field to be smaller than the content */
    test_ctx.s_buff[11] = 0x00;
    test_ctx.s_buff[12] = 0x02;
    /* modify the amount of data to send */
    ExpectIntEQ(test_memio_modify_message_len(&test_ctx, 0, 0, 15), 0);

    /* Try to read the malformed record */
    wolfSSL_SetLoggingPrefix("server");
    ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
    ExpectIntEQ(test_ctx.s_len, 0);

    ExpectIntEQ(test_dtls_communication(ssl_s, ssl_c), TEST_SUCCESS);

    ret = test_dtls_shutdown(ssl_s, ssl_c, ctx_c, ctx_s);
    ExpectIntEQ(ret, TEST_SUCCESS);

    return EXPECT_RESULT();
}
#else
int test_dtls_short_ciphertext(void)
{
    return TEST_SKIPPED;
}
int test_dtls12_record_length_mismatch(void)
{
    return TEST_SKIPPED;
}
int test_dtls12_short_read(void)
{
    return TEST_SKIPPED;
}
int test_dtls13_short_read(void)
{
    return TEST_SKIPPED;
}
int test_dtls13_longer_length(void)
{
    return TEST_SKIPPED;
}
int test_dtls_record_cross_boundaries(void)
{
    return TEST_SKIPPED;
}
#endif /* defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) */

#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
    !defined(WOLFSSL_NO_TLS12) && !defined(NO_SHA256)
/* This test that the DTLS record boundary check doesn't interfere with TLS
 * records processing */
int test_records_span_network_boundaries(void)
{
    EXPECT_DECLS;
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;
    unsigned char readBuf[256];
    int record_len;

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    /* Setup DTLS contexts */
    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                    wolfTLSv1_2_client_method, wolfTLSv1_2_server_method),
        0);

    /* Complete handshake */
    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

    /* create a good record in the buffer */
    wolfSSL_SetLoggingPrefix("client");
    ExpectIntEQ(wolfSSL_write(ssl_c, "test", 4), 4);
    ExpectIntLE(test_ctx.s_len, sizeof(readBuf));
    ExpectIntGT(test_ctx.s_len, 10);
    record_len = test_ctx.s_len;
    if (EXPECT_SUCCESS())
        XMEMCPY(readBuf, test_ctx.s_buff, record_len);

    /* drop record and simulate a split write */
    ExpectIntEQ(test_memio_drop_message(&test_ctx, 0, 0), 0);
    ExpectIntEQ(test_ctx.s_msg_count, 0);

    /* inject first record header */
    ExpectIntEQ(
        test_memio_inject_message(&test_ctx, 0, (const char*)readBuf, 5), 0);
    ExpectIntEQ(test_ctx.s_msg_count, 1);
    ExpectIntEQ(test_ctx.s_msg_sizes[0], 5);

    /* inject another 5 bytes of the record */
    ExpectIntEQ(
        test_memio_inject_message(&test_ctx, 0, (const char*)readBuf + 5, 5),
        0);
    ExpectIntEQ(test_ctx.s_msg_count, 2);
    ExpectIntEQ(test_ctx.s_msg_sizes[1], 5);

    /* inject the rest of the record */
    ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
                    (const char*)readBuf + 10, record_len - 10),
        0);
    ExpectIntEQ(test_ctx.s_msg_count, 3);
    ExpectIntEQ(test_ctx.s_msg_sizes[2], record_len - 10);

    /* read the record */
    wolfSSL_SetLoggingPrefix("server");
    ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), 4);
    ExpectIntEQ(test_ctx.s_len, 0);

    ExpectBufEQ(readBuf, "test", 4);

    wolfSSL_free(ssl_s);
    wolfSSL_free(ssl_c);
    wolfSSL_CTX_free(ctx_s);
    wolfSSL_CTX_free(ctx_c);

    return EXPECT_RESULT();
}
#else
int test_records_span_network_boundaries(void)
{
    return TEST_SKIPPED;
}
#endif /* defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) &&                     \
          !defined(WOLFSSL_NO_TLS12) */

int test_dtls_mtu_fragment_headroom(void)
{
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) &&                           \
    defined(WOLFSSL_DTLS_MTU) && defined(HAVE_AESGCM) && defined(HAVE_ECC) &&  \
    !defined(WOLFSSL_NO_DTLS_SIZE_CHECK)
    EXPECT_DECLS;
    struct {
        method_provider client_meth;
        method_provider server_meth;
        const char* cipher;
        int use_cid;
    } params[] = {
#if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_TLS13)
        { wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method,
          "TLS13-AES128-GCM-SHA256", 0 },
#ifdef WOLFSSL_DTLS_CID
        { wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method,
          "TLS13-AES128-GCM-SHA256", 1 },
#endif
#endif
#if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_TLS12)
        { wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method,
          "ECDHE-RSA-AES128-GCM-SHA256", 0 },
#ifdef WOLFSSL_DTLS_CID
        { wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method,
          "ECDHE-RSA-AES128-GCM-SHA256", 1 },
#endif
#if !defined(WOLFSSL_AEAD_ONLY) && !defined(NO_AES) && !defined(NO_SHA)
        { wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method,
          "ECDHE-RSA-AES128-SHA", 0 },
#ifdef WOLFSSL_DTLS_CID
        { wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method,
          "ECDHE-RSA-AES128-SHA", 1 },
#endif
#endif
#endif
    };
    size_t i;

    for (i = 0; i < XELEM_CNT(params) && EXPECT_SUCCESS(); i++) {
        WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
        WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
        struct test_memio_ctx test_ctx;
        unsigned char payload[33];
        word16 mtu;
        int recordLen;
        int overhead;
        int ret;

        XMEMSET(&test_ctx, 0, sizeof(test_ctx));
        XMEMSET(payload, 'A', sizeof(payload));

        ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                        params[i].client_meth, params[i].server_meth),
            0);

        ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, params[i].cipher), 1);
        ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, params[i].cipher), 1);

#ifdef WOLFSSL_DTLS_CID
        if (params[i].use_cid) {
            unsigned char cid_c[] = { 0,1,2,3 };
            unsigned char cid_s[] = { 4,5,6,7,8,9 };
            ExpectIntEQ(wolfSSL_dtls_cid_use(ssl_c), 1);
            ExpectIntEQ(wolfSSL_dtls_cid_use(ssl_s), 1);
            ExpectIntEQ(wolfSSL_dtls_cid_set(ssl_c, cid_s, (int)sizeof(cid_s)),
                1);
            ExpectIntEQ(wolfSSL_dtls_cid_set(ssl_s, cid_c, (int)sizeof(cid_c)),
                1);
        }
#endif

        /* Complete handshake and clear any leftover records. */
        ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
        test_memio_clear_buffer(&test_ctx, 1);
        test_memio_clear_buffer(&test_ctx, 0);

        /* Measure application-data record overhead. */
        ExpectIntEQ(wolfSSL_write(ssl_c, payload, 32), 32);
        ExpectIntEQ(test_ctx.s_msg_count, 1);
        recordLen = test_ctx.s_len;
        ExpectIntGT(recordLen, 32);
        overhead = recordLen - 32;

        /* Reset buffers before MTU-limited send. */
        test_memio_clear_buffer(&test_ctx, 0);
        test_memio_clear_buffer(&test_ctx, 1);

        /* Set MTU to overhead + 32 bytes of payload. */
        mtu = (word16)(overhead + 32);
        ExpectIntEQ(wolfSSL_dtls_set_mtu(ssl_c, mtu), WOLFSSL_SUCCESS);
        ExpectIntEQ(wolfSSL_dtls_set_mtu(ssl_s, mtu), WOLFSSL_SUCCESS);

        /* With the tightened MTU, we should still be able to send 32 bytes. */
        ExpectIntEQ(wolfSSL_write(ssl_c, payload, 32), 32);
        ExpectIntEQ(test_ctx.s_msg_count, 1);
        recordLen = test_ctx.s_len;
        ExpectIntEQ(recordLen, overhead + 32);
        ExpectIntLE(recordLen, mtu);

        /* Underestimation: drop MTU by 1 and expect DTLS_SIZE_ERROR. */
        test_memio_clear_buffer(&test_ctx, 0);
        test_memio_clear_buffer(&test_ctx, 1);
        ExpectIntEQ(wolfSSL_dtls_set_mtu(ssl_c, mtu - 1), WOLFSSL_SUCCESS);
        ret = wolfSSL_write(ssl_c, payload, 32);
        ExpectIntNE(ret, 32);
        ExpectIntEQ(wolfSSL_get_error(ssl_c, ret), DTLS_SIZE_ERROR);

        wolfSSL_free(ssl_c);
        wolfSSL_CTX_free(ctx_c);
        wolfSSL_free(ssl_s);
        wolfSSL_CTX_free(ctx_s);
    }
    return EXPECT_RESULT();
#else
    return TEST_SKIPPED;
#endif
}

int test_dtls_rtx_across_epoch_change(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) &&                           \
    defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS)
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;
#if defined(WOLFSSL_HAVE_MLKEM)
    /* When ML-KEM is used in the key share, the hello messages are fragmented
     *into two messages */
    int helloMsgCount = 2;
    int groups[2] = {
    #if defined(HAVE_CURVE25519) && defined(WOLFSSL_PQC_HYBRIDS) && \
        !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_NO_ML_KEM_768)
        WOLFSSL_X25519MLKEM768,
    #elif defined(HAVE_ECC) && defined(WOLFSSL_PQC_HYBRIDS) && \
        !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_NO_ML_KEM_768)
        WOLFSSL_SECP256R1MLKEM768,
    #elif defined(HAVE_ECC) && defined(WOLFSSL_PQC_HYBRIDS) && \
        !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_NO_ML_KEM_1024)
        WOLFSSL_SECP384R1MLKEM1024,
    #elif !defined(WOLFSSL_NO_ML_KEM_1024) && !defined(WOLFSSL_NO_ML_KEM) && \
                                       !defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE)
        WOLFSSL_ML_KEM_1024,
    #elif !defined(WOLFSSL_NO_ML_KEM_768) && !defined(WOLFSSL_NO_ML_KEM) && \
                                       !defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE)
        WOLFSSL_ML_KEM_768,
    #elif !defined(WOLFSSL_NO_ML_KEM_512) && \
                                       !defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE)
        WOLFSSL_ML_KEM_512,
    #elif defined(WOLFSSL_MLKEM_KYBER) && !defined(WOLFSSL_NO_KYBER1024)
        WOLFSSL_KYBER_LEVEL5,
    #elif defined(WOLFSSL_MLKEM_KYBER) && !defined(WOLFSSL_NO_KYBER768)
        WOLFSSL_KYBER_LEVEL3,
    #elif defined(WOLFSSL_MLKEM_KYBER) && !defined(WOLFSSL_NO_KYBER512)
        WOLFSSL_KYBER_LEVEL1,
    #endif
        WOLFSSL_ECC_SECP256R1,
    };
#else
    /* When ECC is used in the key share, the hello messages are not
     * fragmented */
    int helloMsgCount = 1;
#endif

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    /* Setup DTLS contexts */
    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                    wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method),
        0);

#if defined(WOLFSSL_HAVE_MLKEM)
    ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 2), WOLFSSL_SUCCESS);
    ExpectIntEQ(wolfSSL_set_groups(ssl_s, groups, 2), WOLFSSL_SUCCESS);
#endif

    /* CH0 */
    wolfSSL_SetLoggingPrefix("client:");
    ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), SSL_ERROR_WANT_READ);

    /* HRR */
    wolfSSL_SetLoggingPrefix("server:");
    ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), SSL_ERROR_WANT_READ);

    /* CH1 */
    wolfSSL_SetLoggingPrefix("client:");
    ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), SSL_ERROR_WANT_READ);

    /* SH ... FINISHED */
    wolfSSL_SetLoggingPrefix("server:");
    ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), SSL_ERROR_WANT_READ);

    /* we should have now SH ... FINISHED messages in the buffer*/
    ExpectIntGE(test_ctx.c_msg_count, 2);

    /* drop everything but the SH */
    while (test_ctx.c_msg_count > helloMsgCount && EXPECT_SUCCESS()) {
        ExpectIntEQ(test_memio_drop_message(&test_ctx, 1, test_ctx.c_msg_count - 1), 0);
    }

    /* Read the SH */
    wolfSSL_SetLoggingPrefix("client:");
    ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), SSL_ERROR_WANT_READ);

    /* trigger client timeout */
    ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS);
    /* this should have triggered a rtx */
    ExpectIntGT(test_ctx.s_msg_count, 0);

    /* finish the handshake */
    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

    /* Test communication works correctly */
    ExpectIntEQ(test_dtls_communication(ssl_s, ssl_c), TEST_SUCCESS);

    /* Cleanup */
    wolfSSL_free(ssl_c);
    wolfSSL_CTX_free(ctx_c);
    wolfSSL_free(ssl_s);
    wolfSSL_CTX_free(ctx_s);
#endif /* defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) &&                     \
          defined(WOLFSSL_DTLS13) */
    return EXPECT_RESULT();
}

#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) &&                           \
    defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS)
static int test_dtls13_get_message_seq(const char* msg, int msgSz,
    word16* msgSeq)
{
    int hsOff = DTLS_RECORD_HEADER_SZ;

    if (msg == NULL || msgSeq == NULL ||
            msgSz < DTLS_RECORD_HEADER_SZ + DTLS_HANDSHAKE_HEADER_SZ) {
        return BAD_FUNC_ARG;
    }

    *msgSeq = ((word16)(byte)msg[hsOff + 4] << 8) |
              (word16)(byte)msg[hsOff + 5];

    return WOLFSSL_SUCCESS;
}
#endif

int test_dtls13_ch2_rtx_no_ch1(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) &&                           \
    defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS)
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;
    const char* msg = NULL;
    int msgSz = 0;
    word16 ch1Seq = 0;
    int i;
    int foundCh1Seq = 0;

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                    wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method),
        0);

    /* To force HRR */
    ExpectIntEQ(wolfSSL_NoKeyShares(ssl_c), WOLFSSL_SUCCESS);

    /* CH1 */
    ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
    ExpectIntEQ(test_memio_get_message(&test_ctx, 0, &msg, &msgSz, 0), 0);
    ExpectIntGE(msgSz, DTLS_RECORD_HEADER_SZ + DTLS_HANDSHAKE_HEADER_SZ);
    ExpectIntEQ(test_dtls13_get_message_seq(msg, msgSz, &ch1Seq),
        WOLFSSL_SUCCESS);

    /* HRR */
    ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
    ExpectIntGT(test_ctx.c_msg_count, 0);

    /* CH2 */
    ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
    ExpectIntGT(test_ctx.s_msg_count, 0);

    /* Drop CH2 and trigger the client retransmission timeout. */
    test_memio_clear_buffer(&test_ctx, 0);
    if (wolfSSL_dtls13_use_quick_timeout(ssl_c))
        ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS);
    ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS);
    ExpectIntGT(test_ctx.s_msg_count, 0);

    for (i = 0; i < test_ctx.s_msg_count && EXPECT_SUCCESS(); i++) {
        int hsOff = DTLS_RECORD_HEADER_SZ;
        word16 msgSeq = 0;

        ExpectIntEQ(test_memio_get_message(&test_ctx, 0, &msg, &msgSz, i), 0);
        /* memio stores one DTLS record per message in this handshake path. */
        if (msgSz >= DTLS_RECORD_HEADER_SZ + DTLS_HANDSHAKE_HEADER_SZ &&
                (byte)msg[0] == handshake && msg[hsOff] == client_hello) {
            ExpectIntEQ(test_dtls13_get_message_seq(msg, msgSz, &msgSeq),
                WOLFSSL_SUCCESS);
            if (msgSeq == ch1Seq)
                foundCh1Seq = 1;
        }
    }

    ExpectIntEQ(foundCh1Seq, 0);

    wolfSSL_free(ssl_c);
    wolfSSL_CTX_free(ctx_c);
    wolfSSL_free(ssl_s);
    wolfSSL_CTX_free(ctx_s);
#endif
    return EXPECT_RESULT();
}

int test_dtls13_frag_ch2_with_ch1_rtx(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) &&                           \
    defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS) &&                        \
    defined(WOLFSSL_DTLS_MTU) && defined(WOLFSSL_DTLS_CH_FRAG) &&              \
    (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0))
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;
    char hrr[TEST_MEMIO_BUF_SZ];
    int hrrSz = (int)sizeof(hrr);
    char ch1Rtx[TEST_MEMIO_BUF_SZ];
    int ch1RtxSz = (int)sizeof(ch1Rtx);
    char ch2[TEST_MEMIO_BUF_SZ];
    int ch2Sz = 0;
    int ch2MsgCount = 0;
    int ch2MsgSizes[TEST_MEMIO_MAX_MSGS] = {0};
    /* The DTLS record sequence number occupies the last 8 bytes of the
     * record header. */
    int recordSeqOff = DTLS_RECORD_HEADER_SZ - 8;
    int ch2Seq = 0;
    int ch1RtxSeq = 0;
    int off;
    int i;

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                    wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method),
        0);

    /* To force HRR */
    ExpectIntEQ(wolfSSL_NoKeyShares(ssl_c), WOLFSSL_SUCCESS);
    ExpectIntEQ(wolfSSL_dtls13_allow_ch_frag(ssl_s, 1), WOLFSSL_SUCCESS);

    /* CH1 */
    ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);

    /* HRR */
    ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
    ExpectIntEQ(test_memio_copy_message(&test_ctx, 1, hrr, &hrrSz, 0), 0);

    /* Drop HRR, trigger CH1 retransmission, copy and drop it */
    test_memio_clear_buffer(&test_ctx, 1);
    if (wolfSSL_dtls13_use_quick_timeout(ssl_c))
        ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS);
    ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS);
    ExpectIntEQ(test_memio_copy_message(&test_ctx, 0, ch1Rtx, &ch1RtxSz, 0), 0);
    test_memio_clear_buffer(&test_ctx, 0);

    /* Force CH2 fragmentation. MTU must be small enough to fragment but large
     * enough that the cookie extension lands in the first fragment, otherwise
     * the server can't validate it statelessly and the test scenario (server
     * stateful after frag 1) does not hold. With --enable-all (PQ groups in
     * supported_groups), the cookie extension can sit ~400 bytes into CH2; 600
     * gives margin while still producing multiple fragments (CH2 is ~2KB). */
    ExpectIntEQ(wolfSSL_dtls_set_mtu(ssl_c, 600), WOLFSSL_SUCCESS);

    /* Forward HRR and let the client create fragmented CH2 */
    ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, hrr, hrrSz), 0);
    ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);

    ExpectIntGT(test_ctx.s_msg_count, 1);
    ExpectIntLE(test_ctx.s_msg_count, TEST_MEMIO_MAX_MSGS);
    ExpectIntLE(test_ctx.s_len, (int)sizeof(ch2));
    if (EXPECT_SUCCESS()) {
        ch2Sz = test_ctx.s_len;
        ch2MsgCount = test_ctx.s_msg_count;
        XMEMCPY(ch2, test_ctx.s_buff, ch2Sz);
        XMEMCPY(ch2MsgSizes, test_ctx.s_msg_sizes,
            sizeof(ch2MsgSizes[0]) * (size_t)ch2MsgCount);

        ch2Seq = ((byte)ch2[recordSeqOff + 4] << 8) |
                 (byte)ch2[recordSeqOff + 5];
        ch1RtxSeq = ch2Seq + ch2MsgCount;

        /* Synthesize a CH1 retransmission that can pass the replay window after
         * the first CH2 fragment makes the server stateful. The handshake
         * message_seq remains the original CH1 value; only the DTLS record
         * sequence is moved past the fragmented CH2 flight */
        ch1Rtx[recordSeqOff + 0] = 0;
        ch1Rtx[recordSeqOff + 1] = 0;
        ch1Rtx[recordSeqOff + 2] = 0;
        ch1Rtx[recordSeqOff + 3] = 0;
        ch1Rtx[recordSeqOff + 4] = (byte)(ch1RtxSeq >> 8);
        ch1Rtx[recordSeqOff + 5] = (byte)ch1RtxSeq;
    }

    test_memio_clear_buffer(&test_ctx, 0);

    /* Deliver CH2 first fragment only. Now server is stateful */
    ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, ch2, ch2MsgSizes[0]), 0);
    ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);

    /* Deliver the retransmitted CH1 between CH2 fragments, it should be
     * discarded as rtx */
    ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, ch1Rtx, ch1RtxSz), 0);
    ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
    test_memio_clear_buffer(&test_ctx, 1);

    /* Deliver the rest of CH2 */
    off = ch2MsgSizes[0];
    for (i = 1; i < ch2MsgCount && EXPECT_SUCCESS(); i++) {
        ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, ch2 + off,
            ch2MsgSizes[i]), 0);
        off += ch2MsgSizes[i];
    }

    /* Restore MTU so the client's input buffer can hold the full server
     * flight (e.g. an SH carrying a hybrid PQC key share). */
    ExpectIntEQ(wolfSSL_dtls_set_mtu(ssl_c, 1500), WOLFSSL_SUCCESS);

    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

    wolfSSL_free(ssl_c);
    wolfSSL_CTX_free(ctx_c);
    wolfSSL_free(ssl_s);
    wolfSSL_CTX_free(ctx_s);
#endif
    return EXPECT_RESULT();
}

int test_dtls_drop_client_ack(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) &&                           \
    defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS)
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;
    char data[32];

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    /* Setup DTLS contexts */
    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                    wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method),
        0);

    /* disable new session ticket to simplify testing */
    ExpectIntEQ(wolfSSL_no_ticket_TLSv13(ssl_s), 0);

    /* CH0 */
    wolfSSL_SetLoggingPrefix("client:");
    ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);

    /* HRR */
    wolfSSL_SetLoggingPrefix("server:");
    ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);

    /* CH1 */
    wolfSSL_SetLoggingPrefix("client:");
    ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);

    /* SH ... FINISHED */
    wolfSSL_SetLoggingPrefix("server:");
    ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);

    /* ... FINISHED */
    wolfSSL_SetLoggingPrefix("client:");
    ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);

    /* init is finished should return false at this point */
    ExpectFalse(wolfSSL_is_init_finished(ssl_c));

    /* ACK */
    ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
    /* Drop the ack */
    test_memio_clear_buffer(&test_ctx, 1);

    /* trigger client timeout, finished should be rtx */
    ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS);
    ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS);
    /* this should have triggered a rtx */
    ExpectIntGT(test_ctx.s_msg_count, 0);

    /* this should re-send the ack immediately */
    ExpectIntEQ(wolfSSL_read(ssl_s, data, 32), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
    ExpectIntEQ(test_ctx.c_msg_count, 1);

    /* This should advance the connection on the client */
    ExpectIntEQ(wolfSSL_negotiate(ssl_c), WOLFSSL_SUCCESS);

    /* Test communication works correctly */
    ExpectIntEQ(test_dtls_communication(ssl_s, ssl_c), TEST_SUCCESS);

    /* Cleanup */
    wolfSSL_free(ssl_c);
    wolfSSL_CTX_free(ctx_c);
    wolfSSL_free(ssl_s);
    wolfSSL_CTX_free(ctx_s);
#endif /* defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) &&                     \
          defined(WOLFSSL_DTLS13) */
    return EXPECT_RESULT();
}

int test_dtls_bogus_finished_epoch_zero(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \
    !defined(WOLFSSL_NO_TLS12) && defined(HAVE_AES_CBC) && \
    defined(WOLFSSL_AES_128) && !defined(NO_SHA256)
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;
    int error;

    /* bogus Finished message bytes from the original bug report (epoch 0)
     * https://github.com/wolfSSL/wolfssl/issues/9188 */
    static const unsigned char bogus_finished[] = {
        0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x18, 0x14, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0c, 0xd9, 0xc6, 0xe3, 0x01, 0x59, 0xf2, 0xc2, 0x4f, 0xfa, 0xfd, 0x20,
        0xd7
    };

    /* serverHelloDone message bytes */
    static const unsigned char server_hello_done_message[] = {
        0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
        0x0c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00
    };

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    /* setting up dtls 1.2 contexts */
    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
            wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), 0);

    /* start handshake, send first ClientHelloDone */
    ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);

    /* clearing server buffer to inject the wrong Finished packet */
    test_memio_clear_buffer(&test_ctx, 1);
    ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
            (const char*)bogus_finished, sizeof(bogus_finished)), 0);

    /* continue client handshake to process it */
    ExpectIntEQ(wolfSSL_connect(ssl_c), -1);

    /* client should terminate with dtls sequence error */
    error = wolfSSL_get_error(ssl_c, -1);

    /* check if the error is SEQUENCE_ERROR, handshake should not
     * expect a finished packet in that moment, in particular should not
     * be in epoch = 0 (should be epoch = 1) */
    ExpectTrue(error == WC_NO_ERR_TRACE(SEQUENCE_ERROR) ||
               error == WC_NO_ERR_TRACE(WOLFSSL_ERROR_WANT_READ));

    /* forcing injection ServerHelloDone to test if client would replay
     * ClientHello */
    test_memio_clear_buffer(&test_ctx, 0);
    ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
            (const char*)server_hello_done_message, sizeof(server_hello_done_message)), 0);
    ExpectIntEQ(wolfSSL_connect(ssl_c), -1);

    /* verifying no ClientHello replay occurred,
     * buffer should empty since we exit early on
     * because of the bogus finished packet */
    ExpectIntLE(test_ctx.s_len, 0);

    wolfSSL_free(ssl_c);
    wolfSSL_CTX_free(ctx_c);
    wolfSSL_free(ssl_s);
    wolfSSL_CTX_free(ctx_s);
#endif
    return EXPECT_RESULT();
}

int test_dtls_replay(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS)
    size_t i;
    struct {
        method_provider client_meth;
        method_provider server_meth;
        const char* tls_version;
    } params[] = {
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_DTLS13)
        { wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method, "DTLSv1_3" },
#endif
#if !defined(WOLFSSL_NO_TLS12) && defined(WOLFSSL_DTLS)
        { wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method, "DTLSv1_2" },
#endif
#if !defined(NO_OLD_TLS) && defined(WOLFSSL_DTLS)
        { wolfDTLSv1_client_method, wolfDTLSv1_server_method, "DTLSv1_0" },
#endif
    };
    for (i = 0; i < XELEM_CNT(params) && !EXPECT_FAIL(); i++) {
        WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
        WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
        struct test_memio_ctx test_ctx;

        char msg_buf[256];
        int msg_len = sizeof(msg_buf);
        byte app_data[8];

        XMEMSET(&test_ctx, 0, sizeof(test_ctx));

        /* Setup DTLS contexts */
        ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                params[i].client_meth, params[i].server_meth), 0);
        ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

        ExpectIntEQ(wolfSSL_write(ssl_c, "test", 4), 4);
        ExpectIntEQ(test_memio_copy_message(&test_ctx, 0, msg_buf, &msg_len, 0), 0);
        ExpectIntEQ(wolfSSL_read(ssl_s, app_data, sizeof(app_data)), 4);
        ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, msg_buf, msg_len), 0);
        ExpectIntEQ(wolfSSL_read(ssl_s, app_data, sizeof(app_data)), -1);
        ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);

        wolfSSL_free(ssl_c);
        wolfSSL_CTX_free(ctx_c);
        wolfSSL_free(ssl_s);
        wolfSSL_CTX_free(ctx_s);
    }
#endif
    return EXPECT_RESULT();
}

#if defined(WOLFSSL_DTLS13) && defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \
    defined(WOLFSSL_SRTP)
static int test_dtls_srtp_ctx_ready(WOLFSSL_CTX* ctx)
{
    EXPECT_DECLS;
    ExpectIntEQ(wolfSSL_CTX_set_tlsext_use_srtp(ctx, "SRTP_AEAD_AES_256_GCM:"
         "SRTP_AEAD_AES_128_GCM:SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32"),
          0);
    return EXPECT_RESULT();
}

int test_dtls_srtp(void)
{
    EXPECT_DECLS;
    test_ssl_cbf client_cbf;
    test_ssl_cbf server_cbf;

    XMEMSET(&client_cbf, 0, sizeof(client_cbf));
    XMEMSET(&server_cbf, 0, sizeof(server_cbf));

    client_cbf.method = wolfDTLSv1_3_client_method;
    client_cbf.ctx_ready = test_dtls_srtp_ctx_ready;
    server_cbf.method = wolfDTLSv1_3_server_method;
    server_cbf.ctx_ready = test_dtls_srtp_ctx_ready;

    ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&client_cbf,
        &server_cbf, NULL), TEST_SUCCESS);

    return EXPECT_RESULT();
}
#else
int test_dtls_srtp(void)
{
    EXPECT_DECLS;
    return EXPECT_RESULT();
}
#endif

int test_dtls_timeout(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS)
    size_t i;
    struct {
        method_provider client_meth;
        method_provider server_meth;
    } params[] = {
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_DTLS13)
        { wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method },
#endif
#if !defined(WOLFSSL_NO_TLS12) && defined(WOLFSSL_DTLS)
        { wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method },
#endif
#if !defined(NO_OLD_TLS) && defined(WOLFSSL_DTLS)
        { wolfDTLSv1_client_method, wolfDTLSv1_server_method },
#endif
    };

    for (i = 0; i < XELEM_CNT(params) && !EXPECT_FAIL(); i++) {
        WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
        WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
        struct test_memio_ctx test_ctx;

        XMEMSET(&test_ctx, 0, sizeof(test_ctx));

        ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                params[i].client_meth, params[i].server_meth), 0);
        ExpectIntEQ(wolfSSL_dtls_set_timeout_max(ssl_c, 2), WOLFSSL_SUCCESS);
        ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1);
        ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_DTLS13)
        /* will return 0 when not 1.3 */
        if (wolfSSL_dtls13_use_quick_timeout(ssl_c))
            ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS);
#endif
        ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS);
        ExpectIntEQ(wolfSSL_dtls_get_current_timeout(ssl_c), 2);
        ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1);
        ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
        ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1);
        ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
        ExpectIntEQ(wolfSSL_dtls_get_current_timeout(ssl_c), 1);
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_DTLS13)
        /* will return 0 when not 1.3 */
        if (wolfSSL_dtls13_use_quick_timeout(ssl_c))
            ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS);
#endif
        ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS);
        ExpectIntEQ(wolfSSL_dtls_get_current_timeout(ssl_c), 2);
        ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

        wolfSSL_free(ssl_s);
        wolfSSL_free(ssl_c);
        wolfSSL_CTX_free(ctx_s);
        wolfSSL_CTX_free(ctx_c);
    }
#endif
    return EXPECT_RESULT();
}

int test_dtls_certreq_order(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \
    !defined(WOLFSSL_NO_TLS12) && defined(HAVE_AESGCM) && \
    defined(WOLFSSL_AES_128) && !defined(NO_SHA256) && !defined(NO_RSA) && \
    !defined(NO_DH)
    /* This test checks that a certificate request message
     * received before server certificate message is properly detected.
     * The binary is taken from https://github.com/wolfSSL/wolfssl/issues/9198
     */
    static const unsigned char certreq_before_cert_bin[] = {
      0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x39, 0x02, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x2d, 0xfe, 0xfd, 0x48, 0xc0, 0xd5, 0xf2, 0x60, 0xb4, 0x20, 0xbb, 0x38,
      0x51, 0xd9, 0xd4, 0x7a, 0xcb, 0x93, 0x3d, 0xbe, 0x70, 0x39, 0x9b, 0xf6,
      0xc9, 0x2d, 0xa3, 0x3a, 0xf0, 0x1d, 0x4f, 0xb7, 0x70, 0xe9, 0x8c, 0x00,
      0x00, 0x9e, 0x00, 0x00, 0x05, 0x00, 0x0f, 0x00, 0x01, 0x01, 0x16, 0xfe,
      0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x3a, 0x0d,
      0x00, 0x00, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x01,
      0x01, 0x00, 0x28, 0x02, 0x02, 0x03, 0x02, 0x04, 0x02, 0x05, 0x02, 0x06,
      0x02, 0x01, 0x01, 0x02, 0x01, 0x03, 0x01, 0x04, 0x01, 0x05, 0x01, 0x06,
      0x01, 0x02, 0x03, 0x03, 0x03, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08,
      0x04, 0x08, 0x05, 0x08, 0x06, 0x07, 0x08, 0x00, 0x00, 0x16, 0xfe, 0xfd,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x2b, 0x0b, 0x00,
      0x03, 0x1f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x1f, 0x00, 0x03,
      0x1c, 0x00, 0x03, 0x19, 0x30, 0x82, 0x03, 0x15, 0x30, 0x82, 0x01, 0xfd,
      0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x14, 0x40, 0xe7, 0x6e, 0x85, 0x66,
      0x7c, 0x3f, 0x04, 0x87, 0x4c, 0x3f, 0x94, 0x21, 0x6d, 0x21, 0x65, 0xa5,
      0x28, 0xa7, 0x38, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
      0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x1a, 0x31, 0x18, 0x30, 0x16,
      0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0f, 0x64, 0x74, 0x6c, 0x73, 0x2d,
      0x66, 0x75, 0x7a, 0x7a, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e,
      0x17, 0x0d, 0x32, 0x34, 0x30, 0x36, 0x30, 0x36, 0x31, 0x32, 0x31, 0x33,
      0x30, 0x33, 0x5a, 0x17, 0x0d, 0x33, 0x38, 0x30, 0x32, 0x31, 0x33, 0x31,
      0x32, 0x31, 0x33, 0x30, 0x33, 0x5a, 0x30, 0x1a, 0x31, 0x18, 0x30, 0x16,
      0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0f, 0x64, 0x74, 0x6c, 0x73, 0x2d,
      0x66, 0x75, 0x7a, 0x7a, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82,
      0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
      0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82,
      0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xbb, 0x2a, 0x06, 0xfa, 0xaf,
      0x9c, 0xb7, 0xeb, 0x33, 0xce, 0xde, 0xf6, 0xb6, 0x0a, 0x93, 0xb3, 0x97,
      0x7a, 0x36, 0x55, 0x89, 0xc2, 0xf5, 0x45, 0x84, 0x6d, 0x45, 0x25, 0x5c,
      0x4f, 0xa8, 0x8a, 0x41, 0x29, 0x5b, 0x71, 0x98, 0x6c, 0x63, 0xe7, 0xcf,
      0x7f, 0xb4, 0x9d, 0x06, 0x76, 0x60, 0x8c, 0x6a, 0x26, 0x47, 0x65, 0x5d,
      0x74, 0x7a, 0xb5, 0x40, 0x33, 0x61, 0xe0, 0x28, 0xed, 0xa6, 0x66, 0x6a,
      0x4b, 0x97, 0xaf, 0xae, 0x6c, 0xa1, 0xf2, 0xfc, 0xd0, 0xf1, 0x61, 0x98,
      0x05, 0x2a, 0x02, 0x42, 0x13, 0x06, 0x7c, 0x4a, 0x7e, 0x53, 0x01, 0x87,
      0x27, 0x6c, 0x41, 0xe8, 0xed, 0x6e, 0xb2, 0x45, 0x90, 0xe8, 0x93, 0xc0,
      0x20, 0xff, 0x64, 0xdf, 0x48, 0x57, 0xb9, 0x62, 0x8c, 0x14, 0x88, 0xc9,
      0x4a, 0x56, 0x3f, 0x5d, 0x9f, 0xeb, 0x1d, 0x79, 0x75, 0xfd, 0x24, 0xad,
      0xb6, 0x65, 0x1d, 0x53, 0x81, 0x5c, 0x67, 0xbe, 0x3a, 0x9d, 0xcd, 0xe1,
      0x47, 0xab, 0x8d, 0xd4, 0xa5, 0xbd, 0xa6, 0xd7, 0x60, 0xf9, 0x5c, 0x32,
      0x51, 0x65, 0x7e, 0x8b, 0xd6, 0xa1, 0x5b, 0xa2, 0xf5, 0x60, 0xaf, 0x29,
      0xff, 0x9f, 0x3a, 0xa4, 0xd0, 0x5d, 0x6e, 0x96, 0x09, 0xe8, 0xcf, 0xc3,
      0xe1, 0xe8, 0x5a, 0x82, 0xce, 0x9a, 0x3c, 0xc6, 0xbb, 0xe5, 0x4c, 0xa8,
      0xa4, 0xb0, 0xfd, 0x86, 0x06, 0x8b, 0x3f, 0x7e, 0x38, 0xe4, 0x06, 0xdf,
      0xf7, 0x9c, 0xc6, 0x8b, 0x1d, 0xb5, 0xad, 0x7a, 0x91, 0x5f, 0x64, 0xa5,
      0x69, 0xc8, 0x7b, 0x77, 0x32, 0x71, 0x8f, 0x73, 0x82, 0xd2, 0x21, 0xe8,
      0xa8, 0x81, 0xfe, 0x76, 0x7f, 0x20, 0xd1, 0xb6, 0x42, 0x9e, 0xaf, 0x60,
      0x85, 0x47, 0xf5, 0xfe, 0x9f, 0x85, 0xbf, 0xb0, 0x11, 0xb7, 0xf7, 0x83,
      0x0d, 0x80, 0x63, 0xa0, 0xf7, 0x0c, 0x2c, 0x83, 0x12, 0xa9, 0x0f, 0x02,
      0x03, 0x01, 0x00, 0x01, 0xa3, 0x53, 0x30, 0x51, 0x30, 0x1d, 0x06, 0x03,
      0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x2a, 0xb5, 0x00, 0x45, 0x06,
      0x08, 0xef, 0xe5, 0xfa, 0x78, 0x19, 0x47, 0x5b, 0x04, 0x40, 0x18, 0xf3,
      0xeb, 0xab, 0x99, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
      0x30, 0x16, 0x80, 0x14, 0x2a, 0xb5, 0x00, 0x45, 0x06, 0x08, 0xef, 0xe5,
      0xfa, 0x78, 0x19, 0x47, 0x5b, 0x04, 0x40, 0x18, 0xf3, 0xeb, 0xab, 0x99,
      0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05,
      0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
      0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01,
      0x00, 0xa7, 0x58, 0x65, 0xfc, 0x60, 0x3e, 0xb7, 0x34, 0x82, 0xde, 0x04,
      0x06, 0x3d, 0x69, 0x62, 0x8a, 0x4c, 0xcc, 0xd6, 0x54, 0x72, 0x81, 0xcb,
      0x31, 0xdf, 0x63, 0xaf, 0x84, 0x27, 0x62, 0xbf, 0xe8, 0x6b, 0xf9, 0x81,
      0xd4, 0x5a, 0x98, 0x88, 0xae, 0x05, 0x5b, 0x2c, 0xa3, 0xf8, 0xb0, 0xde,
      0x9b, 0x44, 0xc7, 0x1d, 0x19, 0x52, 0x02, 0x02, 0xd9, 0x0e, 0x66, 0x7b,
      0x25, 0xdf, 0x95, 0x03, 0x5e, 0x4b, 0x15, 0xef, 0xda, 0x86, 0x2e, 0x8b,
      0xc4, 0xe7, 0x2d, 0x3f, 0x5f, 0xea, 0x1f, 0x13, 0x81, 0x2e, 0x6e, 0xf8,
      0x7f, 0x0b, 0x3b, 0x95, 0x4f, 0xb6, 0xb3, 0x91, 0xcf, 0x89, 0x52, 0xdb,
      0xb7, 0xb1, 0x5d, 0x79, 0xdf, 0x3a, 0xf3, 0xe2, 0x46, 0xc4, 0x04, 0xf3,
      0xf4, 0xf1, 0xc3, 0xf3, 0xa4, 0x98, 0x47, 0xae, 0x46, 0x99, 0x43, 0x4b,
      0x20, 0xba, 0x33, 0xaa, 0x7e, 0x2e, 0x80, 0x88, 0x25, 0x84, 0x73, 0x6d,
      0x44, 0x5f, 0x48, 0x57, 0x0a, 0xc4, 0x4a, 0x4d, 0xc4, 0xd1, 0x47, 0x5f,
      0x4f, 0xd5, 0xdb, 0x3e, 0x90, 0xbd, 0xe1, 0x6a, 0xcb, 0xe4, 0xf3, 0xe6,
      0x64, 0x26, 0xbd, 0xb6, 0x0b, 0x95, 0x6f, 0x4e, 0x1b, 0x09, 0x25, 0x68,
      0x93, 0xb6, 0xd0, 0xc2, 0xfc, 0xce, 0x8f, 0x64, 0xf5, 0x75, 0x50, 0x58,
      0xe5, 0x3e, 0x00, 0x01, 0xfd, 0x62, 0x37, 0xe1, 0x37, 0x1e, 0x9f, 0x97,
      0x88, 0xb1, 0xa9, 0x6f, 0xad, 0x93, 0x41, 0x01, 0xfb, 0x38, 0x24, 0xc8,
      0x08, 0xa0, 0x68, 0x4b, 0x34, 0x8b, 0x76, 0xea, 0x01, 0x62, 0x9d, 0xfa,
      0xdc, 0x91, 0x50, 0x47, 0x98, 0xec, 0x0c, 0x44, 0x58, 0xb6, 0x16, 0xa0,
      0x05, 0xf2, 0x94, 0x34, 0x6d, 0xcb, 0xbc, 0xe4, 0x58, 0xd6, 0x97, 0x9d,
      0x57, 0xa5, 0x5a, 0x65, 0xfa, 0xab, 0x94, 0x24, 0xbf, 0x06, 0x64, 0xc0,
      0xe5, 0x89, 0xe4, 0x2e, 0x46, 0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x03, 0x03, 0x17, 0x0c, 0x00, 0x03, 0x0b, 0x00, 0x03,
      0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, 0x9a,
      0xaf, 0xdc, 0x56, 0x20, 0x27, 0x3d, 0x3c, 0xf1, 0xd8, 0xb9, 0xc5, 0x83,
      0xce, 0x2d, 0x36, 0x95, 0xa9, 0xe1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xfb,
      0xcc, 0x93, 0x9d, 0xce, 0x24, 0x9b, 0x3e, 0xf9, 0x7d, 0x2f, 0xe3, 0x63,
      0x63, 0x0c, 0x75, 0xd8, 0xf6, 0x81, 0xb2, 0x02, 0xae, 0xc4, 0x61, 0x7a,
      0xd3, 0xdf, 0x1e, 0xd5, 0xd5, 0xfd, 0x65, 0x61, 0x24, 0x33, 0xf5, 0x1f,
      0x5f, 0x06, 0x6e, 0xd0, 0x85, 0x63, 0x65, 0x55, 0x3d, 0xed, 0x1a, 0xf3,
      0xb5, 0x57, 0x13, 0x5e, 0x7f, 0x57, 0xc9, 0x35, 0x98, 0x4f, 0x0c, 0x70,
      0xe0, 0xe6, 0x8b, 0x77, 0xe2, 0xa6, 0x89, 0xda, 0xf3, 0xef, 0xe8, 0x72,
      0x1d, 0xf1, 0x58, 0xa1, 0x36, 0xad, 0xe7, 0x35, 0x30, 0xac, 0xca, 0x4f,
      0x48, 0x3a, 0x79, 0x7a, 0xbc, 0x0a, 0xb1, 0x82, 0xb3, 0x24, 0xfb, 0x61,
      0xd1, 0x08, 0xa9, 0x4b, 0xb2, 0xc8, 0xe3, 0xfb, 0xb9, 0x6a, 0xda, 0xb7,
      0x60, 0xd7, 0xf4, 0x68, 0x1d, 0x4f, 0x42, 0xa3, 0xde, 0x39, 0x4d, 0xf4,
      0xae, 0x56, 0xed, 0xe7, 0x63, 0x72, 0xbb, 0x19, 0x0b, 0x07, 0xa7, 0xc8,
      0xee, 0x0a, 0x6d, 0x70, 0x9e, 0x02, 0xfc, 0xe1, 0xcd, 0xf7, 0xe2, 0xec,
      0xc0, 0x34, 0x04, 0xcd, 0x28, 0x34, 0x2f, 0x61, 0x91, 0x72, 0xfe, 0x9c,
      0xe9, 0x85, 0x83, 0xff, 0x8e, 0x4f, 0x12, 0x32, 0xee, 0xf2, 0x81, 0x83,
      0xc3, 0xfe, 0x3b, 0x1b, 0x4c, 0x6f, 0xad, 0x73, 0x3b, 0xb5, 0xfc, 0xbc,
      0x2e, 0xc2, 0x20, 0x05, 0xc5, 0x8e, 0xf1, 0x83, 0x7d, 0x16, 0x83, 0xb2,
      0xc6, 0xf3, 0x4a, 0x26, 0xc1, 0xb2, 0xef, 0xfa, 0x88, 0x6b, 0x42, 0x38,
      0x61, 0x28, 0x5c, 0x97, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0x00, 0x01, 0x02, 0x01, 0x00, 0xea, 0x10, 0x0e, 0xb8, 0xc4, 0xc9, 0xc9,
      0x9a, 0x8c, 0x03, 0x04, 0x56, 0x4f, 0x3d, 0x2d, 0x64, 0x51, 0xc9, 0x1e,
      0xf7, 0x63, 0x06, 0x81, 0xca, 0x89, 0x5c, 0x81, 0xb9, 0x78, 0xe0, 0xf5,
      0x43, 0xe4, 0x47, 0x40, 0x8f, 0x0e, 0xab, 0x0e, 0xd0, 0xb4, 0x43, 0x92,
      0x2a, 0x03, 0x4a, 0x1f, 0x69, 0x7b, 0xc3, 0x0c, 0x13, 0x0d, 0xf3, 0xd8,
      0xaa, 0xd7, 0x1e, 0x0e, 0xf5, 0x09, 0x7d, 0xda, 0xc9, 0x7c, 0x16, 0xfd,
      0xe6, 0xbb, 0x2d, 0xc1, 0x12, 0x20, 0xad, 0x8f, 0x1b, 0x64, 0x79, 0xb9,
      0xbc, 0x26, 0x11, 0xec, 0x3d, 0x20, 0xa6, 0x18, 0x6c, 0xb3, 0x27, 0xbe,
      0x86, 0xde, 0x0e, 0x49, 0x8f, 0xc2, 0x0e, 0x86, 0x8b, 0x2a, 0xc7, 0x4c,
      0xb5, 0x09, 0xed, 0x94, 0x6d, 0xb6, 0x50, 0xfb, 0xc1, 0x8e, 0xd7, 0xce,
      0x58, 0xf8, 0xb0, 0x68, 0xbc, 0xcf, 0x28, 0xc5, 0x1c, 0xf3, 0x99, 0x17,
      0x22, 0xaa, 0x40, 0x28, 0x90, 0x78, 0x34, 0xe2, 0x0f, 0x28, 0x0d, 0x22,
      0xe1, 0x55, 0xcd, 0x90, 0x26, 0x84, 0xa0, 0xd8, 0xea, 0xd9, 0xe8, 0x83,
      0x43, 0x24, 0xef, 0x66, 0xa6, 0x7f, 0x9f, 0x56, 0x10, 0x6f, 0xc9, 0x13,
      0x2f, 0xb1, 0x00, 0x49, 0xc7, 0x88, 0x8d, 0xec, 0x55, 0xc1, 0xdb, 0x39,
      0xa2, 0x5e, 0xbd, 0xde, 0xb6, 0x0a, 0x1c, 0x1f, 0xa4, 0x1a, 0x93, 0xc2,
      0xee, 0x9c, 0x63, 0x3b, 0x09, 0xcf, 0xf6, 0x93, 0x83, 0xfe, 0xd7, 0x4d,
      0x35, 0xd3, 0x15, 0x74, 0x23, 0x5a, 0x33, 0xdc, 0x64, 0x9d, 0xba, 0x2a,
      0xb0, 0x63, 0x26, 0x17, 0x44, 0xe2, 0xfa, 0x41, 0xb1, 0xb2, 0xf2, 0x63,
      0xb2, 0x51, 0x50, 0xfc, 0x31, 0xc2, 0xd6, 0xda, 0x01, 0x18, 0xcf, 0xe8,
      0x9b, 0xed, 0x4c, 0x69, 0x38, 0xe1, 0xe2, 0x69, 0x53, 0xdc, 0x85, 0x40,
      0x4e, 0x9a, 0x1d, 0xe8, 0x2a, 0xe1, 0x27, 0xad, 0x8e, 0x03, 0x01, 0x01,
      0x00, 0xad, 0x55, 0xc0, 0xac, 0xbb, 0x32, 0x93, 0x86, 0xc6, 0xdf, 0x5d,
      0x58, 0x94, 0xba, 0x35, 0x81, 0x32, 0x54, 0x98, 0xdc, 0x85, 0x6f, 0x1e,
      0x41, 0xe4, 0x3d, 0x1e, 0x0d, 0x37, 0x85, 0x05, 0xd1, 0xf7, 0xb2, 0x3a,
      0xd5, 0xb1, 0x8c, 0x93, 0x20, 0x8a, 0x42, 0xf0, 0xc9, 0x86, 0xcc, 0xf4,
      0xa1, 0x17, 0x8d, 0x65, 0xd0, 0xea, 0x23, 0x9f, 0xf7, 0xcf, 0x3e, 0x7b,
      0x51, 0x55, 0xfe, 0x3d, 0x6c, 0x9e, 0x3e, 0x51, 0x39, 0xd8, 0xa0, 0xa0,
      0x8a, 0x6b, 0xd8, 0x4d, 0x41, 0x7f, 0x97, 0x5c, 0x8e, 0x25, 0x7b, 0x24,
      0x72, 0x1a, 0x46, 0xac, 0x8f, 0x8e, 0xd7, 0xe8, 0xa6, 0x31, 0x1e, 0x7c,
      0xd0, 0xa9, 0x31, 0x84, 0xa6, 0x60, 0x73, 0xb3, 0xb9, 0x26, 0xa3, 0x4e,
      0xd5, 0x03, 0x3f, 0xef, 0xaa, 0x5a, 0x41, 0x8d, 0x1f, 0x0b, 0xb6, 0x37,
      0x63, 0x9b, 0xa1, 0xfe, 0x43, 0x5b, 0x73, 0xa2, 0x5b, 0xce, 0x53, 0x61,
      0x05, 0x1f, 0x75, 0x35, 0xf1, 0x71, 0x5b, 0xf6, 0x60, 0x1e, 0xcc, 0x62,
      0xae, 0xca, 0xe3, 0x4f, 0xc0, 0xc0, 0xfd, 0xe1, 0x42, 0xc3, 0xbc, 0x29,
      0x84, 0x74, 0x30, 0x0a, 0x22, 0x69, 0x10, 0x3d, 0xb6, 0x7c, 0x54, 0xc7,
      0x54, 0xe2, 0xaf, 0x3a, 0xee, 0xa3, 0x05, 0xd4, 0x89, 0xa2, 0xc3, 0xa1,
      0x51, 0x45, 0x25, 0x8e, 0xc5, 0x5a, 0xc8, 0x75, 0x50, 0xd9, 0x98, 0x67,
      0x3c, 0xd2, 0xfa, 0x96, 0x6a, 0xaa, 0x1b, 0x0a, 0x29, 0x15, 0xfe, 0xd0,
      0xbb, 0x1a, 0xf5, 0xa4, 0xcd, 0xbe, 0xce, 0xa2, 0x3e, 0x0c, 0x03, 0x9c,
      0xab, 0x3a, 0x34, 0xe2, 0x0e, 0x0e, 0xa0, 0xb3, 0x9a, 0x2a, 0x3f, 0xa2,
      0x1e, 0xd3, 0x33, 0x38, 0x42, 0x6e, 0x65, 0xf8, 0xda, 0xfa, 0x90, 0x73,
      0xa5, 0x66, 0x84, 0xe0, 0xfe, 0x99, 0xe9, 0xc1, 0x94, 0x24, 0x04, 0x7f,
      0x05, 0xda, 0xc7, 0xcd, 0xce, 0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x04,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    };
    WOLFSSL_CTX *ctx_c = NULL;
    WOLFSSL *ssl_c = NULL;
    struct test_memio_ctx test_ctx;

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, NULL, &ssl_c, NULL,
            wolfDTLSv1_2_client_method, NULL), 0);

    /* start handshake, send first ClientHello */
    ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
    ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
            (const char*)certreq_before_cert_bin,
            sizeof(certreq_before_cert_bin)), 0);
    ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), OUT_OF_ORDER_E);

    wolfSSL_free(ssl_c);
    wolfSSL_CTX_free(ctx_c);
#endif
    return EXPECT_RESULT();
}

#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS)
struct {
    struct test_memio_ctx* test_ctx;
    WOLFSSL* ssl_s;
    int fd;
    SOCKADDR_S peer_addr;
} test_memio_wolfio_ctx;

static ssize_t test_memio_wolfio_recvfrom(int sockfd, void* buf,
        size_t len, int flags, void* src_addr, void* addrlen)
{
    int ret;
    (void)flags;
    if (sockfd != test_memio_wolfio_ctx.fd) {
        errno = EINVAL;
        return -1;
    }
    ret = test_memio_read_cb(test_memio_wolfio_ctx.ssl_s,
            (char*)buf, (int)len, test_memio_wolfio_ctx.test_ctx);
    if (ret <= 0) {
        if (ret == WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_WANT_READ))
            errno = EAGAIN;
        else
            errno = EINVAL;
        return -1;
    }
    XMEMCPY(src_addr, &test_memio_wolfio_ctx.peer_addr,
            MIN(sizeof(test_memio_wolfio_ctx.peer_addr),
                *(word32*)addrlen));
    *(word32*)addrlen = sizeof(test_memio_wolfio_ctx.peer_addr);
    return ret;
}

static ssize_t test_memio_wolfio_sendto(int sockfd, const void* buf,
        size_t len, int flags, const void* dest_addr, word32 addrlen)
{
    int ret;
    (void) flags;
    (void) dest_addr;
    (void) addrlen;
    if (sockfd != test_memio_wolfio_ctx.fd) {
        errno = EINVAL;
        return -1;
    }
    if (dest_addr != NULL && addrlen != 0 &&
            (sizeof(test_memio_wolfio_ctx.peer_addr) != addrlen ||
            XMEMCMP(dest_addr, &test_memio_wolfio_ctx.peer_addr,
                    addrlen) != 0)) {
        errno = EINVAL;
        return -1;
    }
    ret = test_memio_write_cb(test_memio_wolfio_ctx.ssl_s, (char*)buf,
            (int)len, test_memio_wolfio_ctx.test_ctx);
    if (ret <= 0) {
        if (ret == WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_WANT_WRITE))
            errno = EAGAIN;
        else
            errno = EINVAL;
        return -1;
    }
    return ret;
}
#endif

/* Test stateless API with wolfio */
int test_dtls_memio_wolfio(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS)
    size_t i;
    struct {
        method_provider client_meth;
        method_provider server_meth;
    } params[] = {
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_DTLS13)
        { wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method },
#endif
#if !defined(WOLFSSL_NO_TLS12) && defined(WOLFSSL_DTLS)
        { wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method },
#endif
#if !defined(NO_OLD_TLS) && defined(WOLFSSL_DTLS)
        { wolfDTLSv1_client_method, wolfDTLSv1_server_method },
#endif
    };
    XMEMSET(&test_memio_wolfio_ctx, 0, sizeof(test_memio_wolfio_ctx));
    for (i = 0; i < XELEM_CNT(params) && !EXPECT_FAIL(); i++) {
        WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
        WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
        struct test_memio_ctx test_ctx;

        XMEMSET(&test_ctx, 0, sizeof(test_ctx));

        ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                params[i].client_meth, params[i].server_meth), 0);

        test_memio_wolfio_ctx.test_ctx = &test_ctx;
        test_memio_wolfio_ctx.ssl_s = ssl_s;
        /* Large number to error out if any syscalls are called with it */
        test_memio_wolfio_ctx.fd = 6000;
        XMEMSET(&test_memio_wolfio_ctx.peer_addr, 0,
                sizeof(test_memio_wolfio_ctx.peer_addr));
        test_memio_wolfio_ctx.peer_addr.ss_family = AF_INET;

        wolfSSL_dtls_set_using_nonblock(ssl_s, 1);
        wolfSSL_SetRecvFrom(ssl_s, test_memio_wolfio_recvfrom);
        wolfSSL_SetSendTo(ssl_s, test_memio_wolfio_sendto);
        /* Restore default functions */
        wolfSSL_SSLSetIORecv(ssl_s, EmbedReceiveFrom);
        wolfSSL_SSLSetIOSend(ssl_s, EmbedSendTo);
        ExpectIntEQ(wolfSSL_set_fd(ssl_s, test_memio_wolfio_ctx.fd),
                    WOLFSSL_SUCCESS);

        ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

        wolfSSL_free(ssl_s);
        wolfSSL_free(ssl_c);
        wolfSSL_CTX_free(ctx_s);
        wolfSSL_CTX_free(ctx_c);
    }
#endif
    return EXPECT_RESULT();
}

/* DTLS using stateless API handling new addresses with wolfio */
int test_dtls_memio_wolfio_stateless(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS)
    size_t i, j;
    struct {
        method_provider client_meth;
        method_provider server_meth;
    } params[] = {
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_DTLS13)
        { wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method },
#endif
#if !defined(WOLFSSL_NO_TLS12) && defined(WOLFSSL_DTLS)
        { wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method },
#endif
#if !defined(NO_OLD_TLS) && defined(WOLFSSL_DTLS)
        { wolfDTLSv1_client_method, wolfDTLSv1_server_method },
#endif
    };
    XMEMSET(&test_memio_wolfio_ctx, 0, sizeof(test_memio_wolfio_ctx));
    for (i = 0; i < XELEM_CNT(params) && !EXPECT_FAIL(); i++) {
        WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
        WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
        struct test_memio_ctx test_ctx;
        char chBuf[1000];
        int chSz = sizeof(chBuf);

        XMEMSET(&test_ctx, 0, sizeof(test_ctx));

        ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                params[i].client_meth, params[i].server_meth), 0);

        test_memio_wolfio_ctx.test_ctx = &test_ctx;
        test_memio_wolfio_ctx.ssl_s = ssl_s;
        /* Large number to error out if any syscalls are called with it */
        test_memio_wolfio_ctx.fd = 6000;
        XMEMSET(&test_memio_wolfio_ctx.peer_addr, 0,
                sizeof(test_memio_wolfio_ctx.peer_addr));
        test_memio_wolfio_ctx.peer_addr.ss_family = AF_INET;

        wolfSSL_dtls_set_using_nonblock(ssl_s, 1);
        wolfSSL_SetRecvFrom(ssl_s, test_memio_wolfio_recvfrom);
        /* Restore default functions */
        wolfSSL_SSLSetIORecv(ssl_s, EmbedReceiveFrom);
        ExpectIntEQ(wolfSSL_set_read_fd(ssl_s, test_memio_wolfio_ctx.fd),
                    WOLFSSL_SUCCESS);

        /* start handshake, send first ClientHello */
        ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
        ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
        ExpectIntEQ(test_memio_copy_message(&test_ctx, 0, chBuf, &chSz, 0), 0);
        ExpectIntGT(chSz, 0);
        test_memio_clear_buffer(&test_ctx, 0);

        /* Send CH from different addresses */
        for (j = 0; j < 10 && !EXPECT_FAIL(); j++,
            (((SOCKADDR_IN*)&test_memio_wolfio_ctx.peer_addr))->sin_port++) {
            const char* hrrBuf = NULL;
            int hrrSz = 0;
            ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, chBuf, chSz), 0);
            ExpectIntEQ(wolfDTLS_accept_stateless(ssl_s), 0);
            ExpectIntEQ(test_memio_get_message(&test_ctx, 1, &hrrBuf, &hrrSz, 0), 0);
            ExpectNotNull(hrrBuf);
            ExpectIntGT(hrrSz, 0);
            test_memio_clear_buffer(&test_ctx, 0);
        }
        test_memio_clear_buffer(&test_ctx, 1);
        ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), 1);
        ExpectIntEQ(wolfDTLS_accept_stateless(ssl_s), 0);
        ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
        ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
        ExpectIntEQ(wolfDTLS_accept_stateless(ssl_s), 1);

        ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

        wolfSSL_free(ssl_s);
        wolfSSL_free(ssl_c);
        wolfSSL_CTX_free(ctx_s);
        wolfSSL_CTX_free(ctx_c);
    }
#endif
    return EXPECT_RESULT();
}

int test_dtls_mtu_split_messages(void)
{
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
    defined(WOLFSSL_DTLS_MTU) && defined(WOLFSSL_NO_DTLS_SIZE_CHECK) && \
    defined(HAVE_AESGCM) && defined(HAVE_ECC)
    EXPECT_DECLS;
    struct {
        method_provider client_meth;
        method_provider server_meth;
        const char* cipher;
    } params[] = {
#if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_TLS13)
        { wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method,
          "TLS13-AES128-GCM-SHA256" },
#endif
#if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_TLS12)
        { wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method,
          "ECDHE-RSA-AES128-GCM-SHA256" },
#if !defined(WOLFSSL_AEAD_ONLY) && !defined(NO_AES) && !defined(NO_SHA)
        /* Block cipher test */
        { wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method,
          "ECDHE-RSA-AES128-SHA" },
#endif
#endif
    };
    size_t i;

    for (i = 0; i < XELEM_CNT(params) && EXPECT_SUCCESS(); i++) {
        WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
        WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
        struct test_memio_ctx test_ctx;
        /* Payload larger than typical MTU to force splitting */
        unsigned char payload[200];
        unsigned char readBuf[200];
        word16 mtu;
        int recordLen;
        int overhead;
        int totalRead;
        int ret;
        int j;

        XMEMSET(&test_ctx, 0, sizeof(test_ctx));
        XMEMSET(payload, 'A', sizeof(payload));

        ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                        params[i].client_meth, params[i].server_meth),
            0);

        ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, params[i].cipher), 1);
        ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, params[i].cipher), 1);

        /* Complete handshake and clear any leftover records. */
        ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
        test_memio_clear_buffer(&test_ctx, 1);
        test_memio_clear_buffer(&test_ctx, 0);

        /* Measure application-data record overhead with small payload. */
        ExpectIntEQ(wolfSSL_write(ssl_c, payload, 32), 32);
        ExpectIntEQ(test_ctx.s_msg_count, 1);
        recordLen = test_ctx.s_len;
        ExpectIntGT(recordLen, 32);
        overhead = recordLen - 32;

        /* Reset buffers before MTU-limited send. */
        test_memio_clear_buffer(&test_ctx, 0);
        test_memio_clear_buffer(&test_ctx, 1);

        /* Set MTU to allow only ~50 bytes of payload per record.
         * This ensures a 200-byte payload must be split into multiple msgs. */
        mtu = (word16)(overhead + 50);
        ExpectIntEQ(wolfSSL_dtls_set_mtu(ssl_c, mtu), WOLFSSL_SUCCESS);
        ExpectIntEQ(wolfSSL_dtls_set_mtu(ssl_s, mtu), WOLFSSL_SUCCESS);

        /* Write payload larger than MTU allows in single record.
         * With WOLFSSL_NO_DTLS_SIZE_CHECK, this should split into multiple
         * messages instead of returning DTLS_SIZE_ERROR. */
        ExpectIntEQ(wolfSSL_write(ssl_c, payload, (int)sizeof(payload)),
            (int)sizeof(payload));

        /* Verify multiple messages were sent */
        ExpectIntGT(test_ctx.s_msg_count, 1);

        /* Each record should fit within MTU */
        for (j = 0; j < test_ctx.s_msg_count && EXPECT_SUCCESS(); j++) {
            ExpectIntLE(test_ctx.s_msg_sizes[j], mtu);
        }

        /* Read all data on server side and verify it matches */
        totalRead = 0;
        while (totalRead < (int)sizeof(payload) && EXPECT_SUCCESS()) {
            ret = wolfSSL_read(ssl_s, readBuf + totalRead,
                (int)sizeof(readBuf) - totalRead);
            if (ret > 0) {
                totalRead += ret;
            }
            else {
                break;
            }
        }
        ExpectIntEQ(totalRead, (int)sizeof(payload));
        ExpectIntEQ(XMEMCMP(payload, readBuf, sizeof(payload)), 0);

        wolfSSL_free(ssl_c);
        wolfSSL_CTX_free(ctx_c);
        wolfSSL_free(ssl_s);
        wolfSSL_CTX_free(ctx_s);
    }
    return EXPECT_RESULT();
#else
    return TEST_SKIPPED;
#endif
}

/* Test DTLS 1.3 minimum retransmission interval. This test calls
 * wolfSSL_dtls_got_timeout() to simulate timeouts and verify that
 * retransmissions are spaced at least DTLS13_MIN_RTX_INTERVAL apart.
 * This tests relies on timing of the retransmission logic so it may be
 * flaky on very slow systems.
 */
int test_dtls13_min_rtx_interval(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
    defined(WOLFSSL_DTLS13) && !defined(DTLS13_MIN_RTX_INTERVAL) && \
    !defined(NO_ASN_TIME)
    /* We don't want to test when DTLS13_MIN_RTX_INTERVAL is defined because
     * it may be too low to trigger reliably in a test. The default value is
     * 1 second which is sufficient for testing here. */
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;
    int c_msg_count = 0;

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    /* Setup DTLS 1.3 contexts */
    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                    wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);

    /* CH0 */
    ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), SSL_ERROR_WANT_READ);

    /* HRR */
    ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), SSL_ERROR_WANT_READ);

    /* CH1 */
    ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), SSL_ERROR_WANT_READ);

    /* SH ... FINISHED */
    ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), SSL_ERROR_WANT_READ);

    /* We should have SH ... FINISHED messages in the buffer */
    ExpectIntGE(test_ctx.c_msg_count, 2);

    /* Drop everything */
    test_memio_clear_buffer(&test_ctx, 1);

    /* First timeout. This one should trigger a retransmission */
    if (wolfSSL_dtls13_use_quick_timeout(ssl_s))
        ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_s), WOLFSSL_SUCCESS);
    ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_s), WOLFSSL_SUCCESS);
    /* Save the message count to make sure no new messages are sent */
    ExpectIntGE(test_ctx.c_msg_count, 2);
    c_msg_count = test_ctx.c_msg_count;

    /* Second timeout. This one should not trigger a retransmission */
    if (wolfSSL_dtls13_use_quick_timeout(ssl_s))
        ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_s), WOLFSSL_SUCCESS);
    ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_s), WOLFSSL_SUCCESS);
    /* This is the critical check. The message count should not increase
     * after the second timeout. DTLS13_MIN_RTX_INTERVAL should have blocked
     * retransmission here. */
    ExpectIntEQ(c_msg_count, test_ctx.c_msg_count);

    /* Now complete the handshake. We didn't clear the first retransmission
     * so the handshake should proceed without issues. */
    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

    /* Cleanup */
    wolfSSL_free(ssl_c);
    wolfSSL_CTX_free(ctx_c);
    wolfSSL_free(ssl_s);
    wolfSSL_CTX_free(ctx_s);
#endif
    return EXPECT_RESULT();
}

/* RFC 9147 Section 5.3: DTLS 1.3 ServerHello must have empty
 * legacy_session_id_echo, even if the ClientHello had a non-empty
 * legacy_session_id. */
int test_dtls13_no_session_id_echo(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) && \
    defined(HAVE_SESSION_TICKET) && defined(HAVE_ECC) && \
    !defined(WOLFSSL_DTLS13_ECHO_LEGACY_SESSION_ID)
    struct test_memio_ctx test_ctx;
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    WOLFSSL_SESSION *sess = NULL;
    char readBuf[1];
    /* Pin to SECP256R1 to avoid a PQ-induced key-share HRR */
    int groups[] = { WOLFSSL_ECC_SECP256R1 };

    /* First connection: complete a DTLS 1.3 handshake to get a session */
    XMEMSET(&test_ctx, 0, sizeof(test_ctx));
    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
        wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);
    ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 1), WOLFSSL_SUCCESS);
    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

    /* Read to process any NewSessionTicket */
    ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);

    ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));

    /* Ensure the session has a non-empty session ID so the ClientHello
     * will have a populated legacy_session_id field (which is legal per
     * RFC 9147). */
    if (sess != NULL && sess->sessionIDSz == 0) {
        sess->sessionIDSz = ID_LEN;
        XMEMSET(sess->sessionID, 0x42, ID_LEN);
    }

    wolfSSL_free(ssl_c); ssl_c = NULL;
    wolfSSL_free(ssl_s); ssl_s = NULL;
    wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
    wolfSSL_CTX_free(ctx_s); ctx_s = NULL;

    /* Second connection: set the session on the client so the ClientHello
     * contains a non-empty legacy_session_id. Verify the server does NOT
     * echo it in the ServerHello. */
    XMEMSET(&test_ctx, 0, sizeof(test_ctx));
    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
        wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);
    ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
    ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 1), WOLFSSL_SUCCESS);
    /* Disable HRR cookie so the server directly sends a ServerHello */
    ExpectIntEQ(wolfSSL_disable_hrr_cookie(ssl_s), WOLFSSL_SUCCESS);

    /* Client sends ClientHello (with non-empty legacy_session_id) */
    ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);

    /* Server processes ClientHello and sends ServerHello + flight */
    ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);

    /* Verify the ServerHello on the wire.
     * Layout: DTLS Record Header (13) + DTLS Handshake Header (12) +
     *         ProtocolVersion (2) + Random (32) = offset 59 for
     *         legacy_session_id_echo length byte. */
    ExpectIntGE(test_ctx.c_len, 60);
    ExpectIntEQ(test_ctx.c_buff[0], handshake);
    ExpectIntEQ(test_ctx.c_buff[DTLS_RECORD_HEADER_SZ], server_hello);
    ExpectIntEQ(test_ctx.c_buff[DTLS_RECORD_HEADER_SZ +
        DTLS_HANDSHAKE_HEADER_SZ + OPAQUE16_LEN + RAN_LEN], 0);

    /* Complete the handshake */
    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

    wolfSSL_SESSION_free(sess);
    wolfSSL_free(ssl_c);
    wolfSSL_free(ssl_s);
    wolfSSL_CTX_free(ctx_c);
    wolfSSL_CTX_free(ctx_s);
#endif
    return EXPECT_RESULT();
}

/* Test that a server built with WOLFSSL_DTLS13_ECHO_LEGACY_SESSION_ID echoes the
 * client's legacy_session_id in both the direct ServerHello path and the
 * stateless HRR path (which also exercises RestartHandshakeHashWithCookie). */
int test_dtls13_5_9_0_compat(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) && \
    defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_DTLS13_ECHO_LEGACY_SESSION_ID) && \
    defined(HAVE_ECC)
    struct test_memio_ctx test_ctx;
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    WOLFSSL_SESSION *sess = NULL;
    char readBuf[1];
    /* Pin to SECP256R1 to avoid a PQ-induced key-share HRR */
    int groups[] = { WOLFSSL_ECC_SECP256R1 };
    /* RFC 8446 Section 4.1.3: an HRR is a ServerHello carrying this magic
     * random. Used to assert sub-test 1 is a real ServerHello, not an HRR. */
    static const byte hrrRandom[RAN_LEN] = {
        0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11,
        0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
        0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E,
        0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C
    };

    /* --- initial connection: get a real session to carry the session ID --- */
    XMEMSET(&test_ctx, 0, sizeof(test_ctx));
    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
        wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);
    ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 1), WOLFSSL_SUCCESS);
    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

    /* drain any NewSessionTicket before calling get1_session */
    ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);

    ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));

    /* Force a non-zero session ID - simulates a wolfSSL <=v5.9.0 client that
     * mistakenly sends 32 bytes as legacy_session_id in DTLS 1.3. */
    if (sess != NULL && sess->sessionIDSz == 0) {
        sess->sessionIDSz = ID_LEN;
        XMEMSET(sess->sessionID, 0x42, ID_LEN);
    }

    wolfSSL_free(ssl_c); ssl_c = NULL;
    wolfSSL_free(ssl_s); ssl_s = NULL;
    wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
    wolfSSL_CTX_free(ctx_s); ctx_s = NULL;

    /* --- sub-test 1: direct ServerHello (HRR cookie disabled) ---
     * Exercises DoTls13ClientHello (change 1) and
     * SendTls13ServerHello (change 2). */
    XMEMSET(&test_ctx, 0, sizeof(test_ctx));
    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
        wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);
    ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
    ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 1), WOLFSSL_SUCCESS);
    ExpectIntEQ(wolfSSL_disable_hrr_cookie(ssl_s), WOLFSSL_SUCCESS);

    /* Client sends CH1 with non-empty legacy_session_id */
    ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);

    /* Server processes CH1 and sends ServerHello */
    ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);

    /* Verify that the ServerHello on the wire echoes the session ID.
     * Layout: DTLS Record Header (13) + DTLS Handshake Header (12) +
     *         ProtocolVersion (2) + Random (32) = byte 59 for
     *         legacy_session_id_echo length. */
    ExpectIntGE(test_ctx.c_len, 60);
    ExpectIntEQ(test_ctx.c_buff[0], handshake);
    ExpectIntEQ(test_ctx.c_buff[DTLS_RECORD_HEADER_SZ], server_hello);
    /* Confirm it is a real ServerHello, not an HRR (also encoded as a
     * ServerHello but bearing the HelloRetryRequest magic random). */
    ExpectIntNE(XMEMCMP(&test_ctx.c_buff[DTLS_RECORD_HEADER_SZ +
        DTLS_HANDSHAKE_HEADER_SZ + OPAQUE16_LEN], hrrRandom, RAN_LEN), 0);
    ExpectIntEQ(test_ctx.c_buff[DTLS_RECORD_HEADER_SZ +
        DTLS_HANDSHAKE_HEADER_SZ + OPAQUE16_LEN + RAN_LEN], ID_LEN);

    /* Complete the handshake - Finished MAC validates the transcript */
    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

    wolfSSL_free(ssl_c); ssl_c = NULL;
    wolfSSL_free(ssl_s); ssl_s = NULL;
    wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
    wolfSSL_CTX_free(ctx_s); ctx_s = NULL;

    /* --- sub-test 2: stateless HRR (HRR cookie enabled by default) ---
     * Exercises SendStatelessReplyDtls13 (change 4) and
     * RestartHandshakeHashWithCookie (change 3). */
    XMEMSET(&test_ctx, 0, sizeof(test_ctx));
    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
        wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);
    ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
    ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 1), WOLFSSL_SUCCESS);

    /* Client sends CH1 */
    ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);

    /* Server sends stateless HRR (SendStatelessReplyDtls13) */
    ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1);
    ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);

    /* Verify the HRR echoes the session ID at the same wire offset */
    ExpectIntGE(test_ctx.c_len, 60);
    ExpectIntEQ(test_ctx.c_buff[0], handshake);
    ExpectIntEQ(test_ctx.c_buff[DTLS_RECORD_HEADER_SZ], server_hello);
    ExpectIntEQ(test_ctx.c_buff[DTLS_RECORD_HEADER_SZ +
        DTLS_HANDSHAKE_HEADER_SZ + OPAQUE16_LEN + RAN_LEN], ID_LEN);

    /* Complete the handshake - Finished MAC validates RestartHandshakeHashWithCookie */
    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

    wolfSSL_SESSION_free(sess);
    wolfSSL_free(ssl_c);
    wolfSSL_free(ssl_s);
    wolfSSL_CTX_free(ctx_c);
    wolfSSL_CTX_free(ctx_s);
#endif
    return EXPECT_RESULT();
}

/* Test that a DTLS 1.3 handshake with an oversized certificate chain does
 * not crash or cause out-of-bounds access in SendTls13Certificate. */
int test_dtls13_oversized_cert_chain(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) \
    && !defined(NO_FILESYSTEM) && !defined(NO_RSA)
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    struct test_memio_ctx test_ctx;
    XFILE f = XBADFILE;
    long sz = 0;
    byte *cert = NULL;
    byte *chain = NULL;
    int copies, off, i;

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));

    /* Read server cert */
    f = XFOPEN(svrCertFile, "rb");
    ExpectTrue(f != XBADFILE);
    if (EXPECT_SUCCESS()) {
        (void)XFSEEK(f, 0, XSEEK_END);
        sz = XFTELL(f);
        (void)XFSEEK(f, 0, XSEEK_SET);
    }
    ExpectTrue(sz > 0);
    cert = (byte*)XMALLOC((size_t)(sz + 1), NULL, DYNAMIC_TYPE_TMP_BUFFER);
    ExpectNotNull(cert);
    if (EXPECT_SUCCESS())
        ExpectIntEQ((int)XFREAD(cert, 1, (size_t)sz, f), (int)sz);
    if (f != XBADFILE)
        XFCLOSE(f);

    /* Build an oversized chain by duplicating the cert */
    copies = EXPECT_SUCCESS() ? (int)(70000 / sz) + 2 : 0;
    chain = (byte*)XMALLOC((size_t)(sz * copies + 1), NULL,
                            DYNAMIC_TYPE_TMP_BUFFER);
    ExpectNotNull(chain);
    off = 0;
    if (EXPECT_SUCCESS()) {
        for (i = 0; i < copies; i++) {
            XMEMCPY(chain + off, cert, (size_t)sz);
            off += (int)sz;
        }
    }

    /* Server context: load the oversized chain */
    ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfDTLSv1_3_server_method()));
    ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_buffer(ctx_s,
        chain, (long)off), WOLFSSL_SUCCESS);
    ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKeyFile,
        WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
    if (EXPECT_SUCCESS()) {
        wolfSSL_SetIORecv(ctx_s, test_memio_read_cb);
        wolfSSL_SetIOSend(ctx_s, test_memio_write_cb);
    }

    /* Client context: no verification (chain certs are duplicates) */
    ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfDTLSv1_3_client_method()));
    if (EXPECT_SUCCESS()) {
        wolfSSL_CTX_set_verify(ctx_c, WOLFSSL_VERIFY_NONE, NULL);
        wolfSSL_SetIORecv(ctx_c, test_memio_read_cb);
        wolfSSL_SetIOSend(ctx_c, test_memio_write_cb);
    }

    ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
    if (EXPECT_SUCCESS()) {
        wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
        wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
    }

    ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
    if (EXPECT_SUCCESS()) {
        wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
        wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
    }

    /* Handshake must not crash. If SendTls13Certificate mishandles the
     * oversized chain this will trigger a wild pointer dereference or stack
     * overflow resulting with the test failing.
     * The correct behaviour either returns BUFFER_E or succeeds
     * if the build config truncated the chain during loading. */
    (void)test_memio_do_handshake(ssl_c, ssl_s, 10, NULL);

    wolfSSL_free(ssl_c);
    wolfSSL_free(ssl_s);
    wolfSSL_CTX_free(ctx_c);
    wolfSSL_CTX_free(ctx_s);
    XFREE(cert, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    XFREE(chain, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
    return EXPECT_RESULT();
}

/* DTLS counterpart to test_tls_set_session_min_downgrade. Exercises the
 * inverted DTLS minor-version comparison (DTLS 1.2 minor 0xFD is "below"
 * floor 0xFC = DTLS 1.3). */
int test_dtls_set_session_min_downgrade(void)
{
    EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \
    defined(WOLFSSL_DTLS13) && defined(HAVE_SESSION_TICKET)
    WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
    WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
    WOLFSSL_SESSION *sess = NULL;
    struct test_memio_ctx test_ctx;

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));
    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                    wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), 0);
    ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
    ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));

    wolfSSL_free(ssl_c); ssl_c = NULL;
    wolfSSL_free(ssl_s); ssl_s = NULL;
    wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
    wolfSSL_CTX_free(ctx_s); ctx_s = NULL;

    XMEMSET(&test_ctx, 0, sizeof(test_ctx));
    ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
                    wolfDTLS_client_method, wolfDTLS_server_method), 0);
    ExpectIntEQ(wolfSSL_SetMinVersion(ssl_c, WOLFSSL_DTLSV1_3),
                WOLFSSL_SUCCESS);
    ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_FAILURE);
    if (ssl_c != NULL)
        ExpectIntEQ(ssl_c->options.resuming, 0);

    wolfSSL_SESSION_free(sess);
    wolfSSL_free(ssl_c);
    wolfSSL_free(ssl_s);
    wolfSSL_CTX_free(ctx_c);
    wolfSSL_CTX_free(ctx_s);
#endif
    return EXPECT_RESULT();
}
