/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

#include <errno.h>
#include <stdint.h>
#include <sys/wait.h>
#include <unistd.h>

#include "api/s2n.h"
#include "s2n_test.h"
#include "testlib/s2n_testlib.h"
#include "tls/s2n_connection.h"
#include "tls/s2n_handshake.h"
#include "utils/s2n_bitmap.h"

#define MAX_KEY_LEN 32
#define MAX_VAL_LEN 255

static const char SESSION_ID[] = "0123456789abcdef0123456789abcdef";
static const char MSG[] = "Test";

struct session_cache_entry {
    uint8_t key[MAX_KEY_LEN];
    uint8_t key_len;
    uint8_t value[MAX_VAL_LEN];
    uint8_t value_len;
    uint8_t lock;
};

struct session_cache_entry session_cache[256];

int cache_store_callback(struct s2n_connection *conn, void *ctx, uint64_t ttl, const void *key, uint64_t key_size, const void *value, uint64_t value_size)
{
    struct session_cache_entry *cache = ctx;

    if (key_size == 0 || key_size > MAX_KEY_LEN) {
        return -1;
    }
    if (value_size == 0 || value_size > MAX_VAL_LEN) {
        return -1;
    }

    uint8_t idx = ((const uint8_t *) key)[0];

    EXPECT_MEMCPY_SUCCESS(cache[idx].key, key, key_size);
    EXPECT_MEMCPY_SUCCESS(cache[idx].value, value, value_size);

    cache[idx].key_len = key_size;
    cache[idx].value_len = value_size;

    return 0;
}

int cache_retrieve_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size, void *value, uint64_t *value_size)
{
    struct session_cache_entry *cache = ctx;

    if (key_size == 0 || key_size > MAX_KEY_LEN) {
        return -1;
    }

    uint8_t idx = ((const uint8_t *) key)[0];

    if (cache[idx].lock) {
        /* here we mock a remote connection/event blocking the handshake
         * state machine, until lock is free
         */
        cache[idx].lock = 0;
        return S2N_CALLBACK_BLOCKED;
    }

    if (cache[idx].key_len != key_size) {
        return -1;
    }

    if (memcmp(cache[idx].key, key, key_size)) {
        return -1;
    }

    if (*value_size < cache[idx].value_len) {
        return -1;
    }

    *value_size = cache[idx].value_len;
    EXPECT_MEMCPY_SUCCESS(value, cache[idx].value, cache[idx].value_len);

    return 0;
}

int cache_delete_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size)
{
    struct session_cache_entry *cache = ctx;

    if (key_size == 0 || key_size > MAX_KEY_LEN) {
        return -1;
    }

    uint8_t idx = ((const uint8_t *) key)[0];

    if (cache[idx].key_len == 0) {
        return 0;
    }

    if (cache[idx].key_len != key_size) {
        return -1;
    }

    if (memcmp(cache[idx].key, key, key_size)) {
        return -1;
    }

    cache[idx].key_len = 0;
    cache[idx].value_len = 0;

    return 0;
}

/* init session cache lock field, which is used to mock a remote
 * connection/event block
 */
static void initialize_cache()
{
    for (int i = 0; i < 256; i++) {
        session_cache[i].lock = 1;
    }
}

void mock_client(struct s2n_test_io_pair *io_pair)
{
    size_t serialized_session_state_length = 0;
    uint8_t serialized_session_state[256] = { 0 };

    struct s2n_connection *conn = NULL;
    struct s2n_config *config = NULL;
    s2n_blocked_status blocked;
    int result = 0;

    /* Give the server a chance to listen */
    sleep(1);

    /* Initial handshake */
    conn = s2n_connection_new(S2N_CLIENT);
    config = s2n_config_new();
    s2n_config_set_cipher_preferences(config, "20240501");
    s2n_config_disable_x509_verification(config);
    s2n_connection_set_config(conn, config);

    s2n_connection_set_io_pair(conn, io_pair);

    /* Set the session id to ensure we're able to fallback to full handshake if session is not in server cache */
    EXPECT_MEMCPY_SUCCESS(conn->session_id, SESSION_ID, S2N_TLS_SESSION_ID_MAX_LEN);
    conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN;

    if (s2n_negotiate(conn, &blocked) != 0) {
        result = 1;
    }

    /* Make sure we did a full handshake */
    if (!IS_FULL_HANDSHAKE(conn)) {
        result = 2;
    }

    /* Save session state from the connection */
    memset(serialized_session_state, 0, sizeof(serialized_session_state));
    serialized_session_state_length = s2n_connection_get_session_length(conn);
    if (serialized_session_state_length > sizeof(serialized_session_state)) {
        result = 3;
    }

    /* Send very low session buffer size and see that you can get an error */
    if (s2n_connection_get_session(conn, serialized_session_state, 1) == 0) {
        result = 4;
    }

    if (s2n_errno != S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG) {
        result = 5;
    }

    if ((size_t) s2n_connection_get_session(conn, serialized_session_state, serialized_session_state_length) != serialized_session_state_length) {
        result = 6;
    }

    /* server would choose a session ID for client */
    if (memcmp(conn->session_id, SESSION_ID, S2N_TLS_SESSION_ID_MAX_LEN) == 0) {
        result = 7;
    }

    if (s2n_send(conn, MSG, sizeof(MSG), &blocked) != sizeof(MSG)) {
        result = 8;
    }

    int shutdown_rc = -1;
    while (shutdown_rc != 0) {
        shutdown_rc = s2n_shutdown(conn, &blocked);
    }

    s2n_connection_free(conn);

    /* Give the server a chance to avoid sigpipe */
    sleep(1);

    /* Session resumption */
    conn = s2n_connection_new(S2N_CLIENT);
    s2n_connection_set_io_pair(conn, io_pair);

    /* Set session state on client connection */
    if (s2n_connection_set_session(conn, serialized_session_state, serialized_session_state_length) < 0) {
        result = 9;
    }

    if (s2n_negotiate(conn, &blocked) != 0) {
        result = 10;
    }

    /* Make sure we did a abbreviated handshake */
    if (!IS_RESUMPTION_HANDSHAKE(conn)) {
        result = 11;
    }

    if (s2n_send(conn, MSG, sizeof(MSG), &blocked) != sizeof(MSG)) {
        result = 12;
    }

    shutdown_rc = -1;
    while (shutdown_rc != 0) {
        shutdown_rc = s2n_shutdown(conn, &blocked);
    }

    s2n_connection_free(conn);

    /* Give the server a chance to avoid sigpipe */
    sleep(1);

    /* Session resumption with bad session state */
    conn = s2n_connection_new(S2N_CLIENT);
    EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING));
    s2n_connection_set_io_pair(conn, io_pair);

    /* Change the format of the session state and check we cannot deserialize it */
    serialized_session_state[0] = 3;
    if (s2n_connection_set_session(conn, serialized_session_state, serialized_session_state_length) == 0) {
        result = 13;
    }

    if (s2n_errno != S2N_ERR_INVALID_SERIALIZED_SESSION_STATE) {
        result = 14;
    }

    serialized_session_state[0] = 0;
    /* Change the protocol version (36th byte) in session state */
    if (serialized_session_state_length < 36) {
        result = 15;
    }

    serialized_session_state[35] = 30;
    if (s2n_connection_set_session(conn, serialized_session_state, serialized_session_state_length) < 0) {
        result = 16;
    }

    if (s2n_negotiate(conn, &blocked) == 0) {
        result = 17;
    }

    if (s2n_errno != S2N_ERR_BAD_MESSAGE) {
        result = 18;
    }

    s2n_connection_free(conn);
    s2n_config_free(config);

    /* Give the server a chance to avoid sigpipe */
    sleep(1);

    s2n_io_pair_close_one_end(io_pair, S2N_CLIENT);
    exit(result);
}

int main(int argc, char **argv)
{
    struct s2n_connection *conn = NULL;
    struct s2n_config *config = NULL;
    s2n_blocked_status blocked;
    int status = 0;
    pid_t pid = 0;
    char *cert_chain_pem = NULL;
    char *private_key_pem = NULL;
    struct s2n_cert_chain_and_key *chain_and_key = NULL;
    char buffer[256];
    int bytes_read = 0;
    int shutdown_rc = -1;
    uint64_t now = 0;
    uint8_t session_id_from_server[MAX_KEY_LEN];
    uint8_t session_id_from_client[MAX_KEY_LEN];

    /* aes keys. Used for session ticket/session data encryption. Taken from test vectors in https://tools.ietf.org/html/rfc5869 */
    uint8_t ticket_key_name[16] = "2018.07.26.15\0";
    uint8_t ticket_key[32] = { 0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33,
        0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf, 0x96, 0x59, 0x67, 0x76,
        0xaf, 0xdb, 0x63, 0x77, 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c,
        0xcb, 0x04 };

    BEGIN_TEST();

    /* Create a pipe */
    struct s2n_test_io_pair io_pair;
    EXPECT_SUCCESS(s2n_io_pair_init(&io_pair));

    /* Create a child process */
    pid = fork();
    if (pid == 0) {
        /* This is the client process, close the server end of the pipe */
        EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER));

        /* Write the fragmented hello message */
        mock_client(&io_pair);
    }

    EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE));
    EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE));

    /* This is the server process, close the client end of the pipe */
    EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT));

    /* Initial handshake */
    {
        /* Initialize the cache so the client and server start off on the same page */
        initialize_cache();
        EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER));
        EXPECT_NOT_NULL(config = s2n_config_new());
        EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501"));

        EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE));
        EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE));
        EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new());
        EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem));
        EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key));

        EXPECT_SUCCESS(s2n_config_set_cache_store_callback(config, cache_store_callback, session_cache));
        EXPECT_SUCCESS(s2n_config_set_cache_retrieve_callback(config, cache_retrieve_callback, session_cache));
        EXPECT_SUCCESS(s2n_config_set_cache_delete_callback(config, cache_delete_callback, session_cache));

        /* Although we disable session ticket, as long as session cache
         * callbacks are binded, session ticket key storage would be initialized
         */
        POSIX_GUARD(s2n_config_set_session_cache_onoff(config, 1));
        POSIX_GUARD(config->wall_clock(config->sys_clock_ctx, &now));
        EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), ticket_key, sizeof(ticket_key), now / ONE_SEC_IN_NANOS));

        EXPECT_SUCCESS(s2n_connection_set_config(conn, config));

        /* Set up the connection to read from the fd */
        EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair));

        /* Negotiate the handshake. */
        int r = s2n_negotiate(conn, &blocked);
        /* first time it always blocks the handshake, as we mock a remote
         * connection/event from the lock
         */
        EXPECT_EQUAL(r, -1);
        EXPECT_EQUAL(s2n_error_get_type(s2n_errno), S2N_ERR_T_BLOCKED);
        EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT);
        EXPECT_SUCCESS(s2n_negotiate(conn, &blocked));

        /* Make sure the get_session_id and get_session_id_length APIs are
         * working as expected */
        EXPECT_EQUAL(s2n_connection_get_session_id_length(conn), MAX_KEY_LEN);
        EXPECT_EQUAL(s2n_connection_get_session_id(conn, session_id_from_server, MAX_KEY_LEN), s2n_connection_get_session_id_length(conn));

        /* Make sure we did a full TLS1.2 handshake */
        EXPECT_TRUE(IS_FULL_HANDSHAKE(conn));
        EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12);

        /* Ensure the message was delivered */
        EXPECT_SUCCESS(bytes_read = s2n_recv(conn, buffer, sizeof(buffer), &blocked));
        EXPECT_EQUAL(bytes_read, sizeof(MSG));
        EXPECT_EQUAL(memcmp(buffer, MSG, sizeof(MSG)), 0);

        /* Shutdown handshake */
        do {
            shutdown_rc = s2n_shutdown(conn, &blocked);
            EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked));
        } while (shutdown_rc != 0);

        /* Clean up */
        EXPECT_SUCCESS(s2n_connection_free(conn));
    };

    /* Session resumption */
    {
        initialize_cache();
        EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER));
        EXPECT_SUCCESS(s2n_connection_set_config(conn, config));

        /* Set up the connection to read from the fd */
        EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair));

        /* Negotiate the handshake. */
        int r = s2n_negotiate(conn, &blocked);
        /* first time it always blocks the handshake, as we mock a remote
         * connection/event from the lock
         */
        EXPECT_EQUAL(r, -1);
        EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT);
        EXPECT_SUCCESS(s2n_negotiate(conn, &blocked));

        /* Make sure the get_session_id and get_session_id_length APIs are
         * working as expected */
        EXPECT_EQUAL(s2n_connection_get_session_id_length(conn), MAX_KEY_LEN);
        EXPECT_EQUAL(s2n_connection_get_session_id(conn, session_id_from_client, MAX_KEY_LEN), s2n_connection_get_session_id_length(conn));
        EXPECT_EQUAL(0, memcmp(session_id_from_client, session_id_from_server, MAX_KEY_LEN));

        /* Make sure we did a abbreviated handshake */
        EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(conn));

        /* Ensure the message was delivered */
        memset(buffer, 0, sizeof(buffer));
        EXPECT_SUCCESS(bytes_read = s2n_recv(conn, buffer, sizeof(buffer), &blocked));
        EXPECT_EQUAL(bytes_read, sizeof(MSG));
        EXPECT_EQUAL(memcmp(buffer, MSG, sizeof(MSG)), 0);

        /* Shutdown handshake */
        do {
            shutdown_rc = s2n_shutdown(conn, &blocked);
            EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked));
        } while (shutdown_rc != 0);

        /* Clean up */
        EXPECT_SUCCESS(s2n_connection_free(conn));
    };

    /* Session resumption with bad session state on client side */
    {
        initialize_cache();
        EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER));
        EXPECT_SUCCESS(s2n_connection_set_config(conn, config));

        /* Set up the connection to read from the fd */
        EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair));

        int r = s2n_negotiate(conn, &blocked);
        EXPECT_EQUAL(r, -1);
        EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT);
        /* Verify we failed to negotiate */
        EXPECT_FAILURE(s2n_negotiate(conn, &blocked));

        /* Clean up */
        EXPECT_SUCCESS(s2n_connection_free(conn));
    };

    /* Close the pipes */
    EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER));

    /* Session caching with a server that does not support EMS */
    {
        initialize_cache();
        struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT);
        EXPECT_NOT_NULL(client_conn);
        s2n_config_disable_x509_verification(config);
        EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config));

        struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER);
        EXPECT_NOT_NULL(server_conn);
        EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config));

        /* Create nonblocking pipes */
        EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
        EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair));

        /* Negotiate until server has read the Client Hello message but hasn't written the server hello message */
        EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO));
        EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO));

        /* s2n servers by default support EMS. We turn it off by manually setting ems_negotiated to false
        * and removing the EMS extension from our received extensions. */
        server_conn->ems_negotiated = false;
        s2n_extension_type_id ems_ext_id = 0;
        EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id));
        S2N_CBIT_CLR(server_conn->extension_requests_received, ems_ext_id);

        /* Connection is successful and EMS is not negotiated */
        EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn));
        EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12);
        EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12);
        EXPECT_FALSE(server_conn->ems_negotiated);
        EXPECT_FALSE(client_conn->ems_negotiated);
        EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn));
        EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn));

        size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn);
        uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 };
        EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len));

        /* Wipe connections and set up new handshake */
        EXPECT_SUCCESS(s2n_connection_wipe(server_conn));
        EXPECT_SUCCESS(s2n_connection_wipe(client_conn));
        EXPECT_SUCCESS(s2n_io_pair_close(&io_pair));
        EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
        EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair));
        EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len));

        /* Server will block the first time cache is accessed */
        EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED);

        /* Resumed connection is successful and EMS is not negotiated */
        EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn));
        EXPECT_EQUAL(S2N_CBIT_TEST(server_conn->extension_requests_received, ems_ext_id), 0);
        EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12);
        EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12);
        EXPECT_FALSE(server_conn->ems_negotiated);
        EXPECT_FALSE(client_conn->ems_negotiated);
        EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn));
        EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn));

        EXPECT_SUCCESS(s2n_connection_free(client_conn));
        EXPECT_SUCCESS(s2n_connection_free(server_conn));
        EXPECT_SUCCESS(s2n_io_pair_close(&io_pair));
    };

    /**
     *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3
     *= type=test
     *# If the original session used the "extended_master_secret"
     *# extension but the new ClientHello does not contain it, the server
     *# MUST abort the abbreviated handshake.
     **/
    {
        initialize_cache();
        struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT);
        EXPECT_NOT_NULL(client_conn);
        s2n_config_disable_x509_verification(config);
        EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config));

        struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER);
        EXPECT_NOT_NULL(server_conn);
        EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config));

        /* Create nonblocking pipes */
        EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
        EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair));

        /* Connection is successful and EMS is negotiated */
        EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn));
        EXPECT_TRUE(server_conn->ems_negotiated);
        EXPECT_TRUE(client_conn->ems_negotiated);

        size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn);
        uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 };
        EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len));

        /* Wipe connections and set up new handshake */
        EXPECT_SUCCESS(s2n_connection_wipe(server_conn));
        EXPECT_SUCCESS(s2n_connection_wipe(client_conn));
        EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING));
        EXPECT_SUCCESS(s2n_io_pair_close(&io_pair));
        EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
        EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair));

        /* Force the client to not send the EMS extension */
        EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len));
        client_conn->ems_negotiated = false;

        /* Server will block the first time cache is accessed */
        EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED);

        /* Server did not receive the EMS extension from client */
        EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_MISSING_EXTENSION);

        EXPECT_SUCCESS(s2n_connection_free(client_conn));
        EXPECT_SUCCESS(s2n_connection_free(server_conn));
        EXPECT_SUCCESS(s2n_io_pair_close(&io_pair));
    };

    /**
     *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3
     *= type=test
     *# If the original session did not use the "extended_master_secret"
     *# extension but the new ClientHello contains the extension, then the
     *# server MUST NOT perform the abbreviated handshake.  Instead, it
     *# SHOULD continue with a full handshake (as described in
     *# Section 5.2) to negotiate a new session.
     **/
    {
        initialize_cache();
        struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT);
        EXPECT_NOT_NULL(client_conn);
        s2n_config_disable_x509_verification(config);
        EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config));

        struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER);
        EXPECT_NOT_NULL(server_conn);
        EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config));

        /* Create nonblocking pipes */
        EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
        EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair));

        /* Negotiate until server has read the Client Hello message but hasn't written the Server Hello message */
        EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO));
        EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO));

        /* s2n servers by default support EMS. We turn it off by manually setting ems_negotiated to false
        * and removing the EMS extension from our received extensions. */
        server_conn->ems_negotiated = false;
        s2n_extension_type_id ems_ext_id = 0;
        EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id));
        S2N_CBIT_CLR(server_conn->extension_requests_received, ems_ext_id);

        /* Connection is successful and EMS is not negotiated */
        EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn));
        EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12);
        EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12);
        EXPECT_FALSE(server_conn->ems_negotiated);
        EXPECT_FALSE(client_conn->ems_negotiated);
        EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn));
        EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn));

        size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn);
        uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 };
        EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len));

        /* Wipe connections and set up new handshake */
        EXPECT_SUCCESS(s2n_connection_wipe(server_conn));
        EXPECT_SUCCESS(s2n_connection_wipe(client_conn));
        EXPECT_SUCCESS(s2n_io_pair_close(&io_pair));
        EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
        EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair));

        /* Force the client to send the EMS extension even though the original session did not negotiate EMS */
        EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len));
        client_conn->ems_negotiated = true;

        /* Server will block the first time cache is accessed */
        EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED);

        /* Fallback to full handshake */
        EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn));
        EXPECT_EQUAL(S2N_CBIT_TEST(server_conn->extension_requests_received, ems_ext_id), 1);
        EXPECT_TRUE(server_conn->ems_negotiated);
        EXPECT_TRUE(client_conn->ems_negotiated);
        EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn));
        EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn));

        EXPECT_SUCCESS(s2n_connection_free(client_conn));
        EXPECT_SUCCESS(s2n_connection_free(server_conn));
        EXPECT_SUCCESS(s2n_io_pair_close(&io_pair));
    };

    /* Clean up */
    EXPECT_SUCCESS(s2n_config_free(config));
    EXPECT_EQUAL(waitpid(-1, &status, 0), pid);
    EXPECT_EQUAL(status, 0);

    EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key));
    free(cert_chain_pem);
    free(private_key_pem);

    END_TEST();
    return 0;
}
