/*
 * 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 "s2n_test.h"
#include "testlib/s2n_testlib.h"
#include "tls/s2n_tls.h"

static const uint8_t hello_request_msg[] = {
    /* message header */
    TLS_HELLO_REQUEST, /* msg_type = hello_request */
    0, 0, 0,           /* length = 0 */
    /* empty message body */
};

static S2N_RESULT s2n_send_client_hello_request(struct s2n_connection *server_conn)
{
    RESULT_ENSURE_REF(server_conn);

    DEFER_CLEANUP(struct s2n_blob message_blob = { 0 }, s2n_free);
    RESULT_GUARD_POSIX(s2n_realloc(&message_blob, sizeof(hello_request_msg)));
    RESULT_CHECKED_MEMCPY(message_blob.data, hello_request_msg, message_blob.size);

    /* Send */
    s2n_blocked_status blocked = S2N_NOT_BLOCKED;
    RESULT_GUARD(s2n_record_write(server_conn, TLS_HANDSHAKE, &message_blob));
    RESULT_GUARD_POSIX(s2n_flush(server_conn, &blocked));

    /* Cleanup */
    RESULT_GUARD_POSIX(s2n_stuffer_wipe(&server_conn->out));
    RESULT_GUARD_POSIX(s2n_stuffer_wipe(&server_conn->handshake.io));

    return S2N_RESULT_OK;
}

struct s2n_test_reneg_req_ctx {
    s2n_renegotiate_response app_decision;
    uint8_t call_count;
};

static int s2n_test_reneg_req_cb(struct s2n_connection *conn, void *context, s2n_renegotiate_response *response)
{
    POSIX_ENSURE_REF(context);
    POSIX_ENSURE_REF(response);

    struct s2n_test_reneg_req_ctx *test_context = (struct s2n_test_reneg_req_ctx *) context;
    *response = test_context->app_decision;
    POSIX_ENSURE_LT(test_context->call_count, UINT8_MAX);
    test_context->call_count++;

    return S2N_SUCCESS;
}

int main(int argc, char **argv)
{
    BEGIN_TEST();

    static struct s2n_cert_chain_and_key *chain_and_key = NULL;
    EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key,
            S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY));

    DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free);
    EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key,
            S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY));

    DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free);
    EXPECT_NOT_NULL(config);
    EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501"));
    EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config));
    EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key));

    DEFER_CLEANUP(struct s2n_config *config_with_reneg_cb = s2n_config_new(), s2n_config_ptr_free);
    EXPECT_NOT_NULL(config_with_reneg_cb);
    EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_with_reneg_cb, "20240501"));
    EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config_with_reneg_cb));
    EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_reneg_cb, chain_and_key));
    EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config_with_reneg_cb, s2n_test_reneg_req_cb, NULL));

    /* Test: Hello requests received during the handshake are a no-op */
    {
        DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(client_conn);
        EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config));

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

        DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close);
        EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
        EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair));
        EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair));

        /* Start the handshake.
         * We should be able to receive the hello request in the middle of the handshake. */
        EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn,
                SERVER_HELLO_DONE));
        EXPECT_EQUAL(server_conn->server, server_conn->initial);

        /* Send a hello request */
        EXPECT_OK(s2n_send_client_hello_request(server_conn));

        /* Successfully complete the handshake */
        EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn));
    };

    /* Test: Hello requests received during the handshake are an error for TLS1.3 */
    if (s2n_is_tls13_fully_supported()) {
        DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(client_conn);
        EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13"));
        EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config));

        DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(server_conn);
        EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13"));
        EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config));

        DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close);
        EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
        EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair));
        EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair));

        /* Start the handshake */
        EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn,
                SERVER_HELLO));

        /* Send a hello request.
         * We should be able to receive the hello request before the version is negotiated. */
        EXPECT_OK(s2n_send_client_hello_request(server_conn));

        /* Continue the handshake */
        EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn,
                ENCRYPTED_EXTENSIONS));

        /* Send a hello request.
         * We should NOT be able to receive the hello request after TLS1.3 is negotiated */
        EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13);
        EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13);
        EXPECT_OK(s2n_send_client_hello_request(server_conn));

        /* Handshake should fail because the HelloRequest is unexpected in TLS1.3 */
        EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn),
                S2N_ERR_BAD_MESSAGE);
    }

    /* Test: Hello requests received after the handshake can be ignored.
     *
     * We can continue sending and receiving data after the request.
     * s2n-tls treats warnings as fatals by default though, so we must disable that behavior.
     */
    {
        DEFER_CLEANUP(struct s2n_config *config_with_warns = s2n_config_new(), s2n_config_ptr_free);
        EXPECT_NOT_NULL(config_with_warns);
        EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_with_warns, "20240501"));
        EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config_with_warns));
        EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_warns, chain_and_key));
        EXPECT_SUCCESS(s2n_config_set_alert_behavior(config_with_warns, S2N_ALERT_IGNORE_WARNINGS));

        DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(client_conn);
        EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_warns));

        DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(server_conn);
        EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_warns));

        DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close);
        EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
        EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair));
        EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair));

        /* Complete the handshake */
        EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn));
        EXPECT_TRUE(client_conn->secure_renegotiation);

        /* Send some data */
        EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn));
        EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn));

        /* Send the hello request message. */
        EXPECT_OK(s2n_send_client_hello_request(server_conn));

        /* Send some more data */
        for (size_t i = 0; i < 10; i++) {
            EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn));
            EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn));
            EXPECT_TRUE(s2n_connection_check_io_status(client_conn, S2N_IO_FULL_DUPLEX));
        }
    };

    /* Test: Hello requests received after the handshake do NOT trigger a no_renegotiation alert
     * if renegotiation callbacks not set.
     *
     *= https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.1
     *= type=test
     *# This message MAY be ignored by
     *# the client if it does not wish to renegotiate a session, or the
     *# client may, if it wishes, respond with a no_renegotiation alert.
     **/
    {
        DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(client_conn);
        EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config));
        EXPECT_NULL(client_conn->config->renegotiate_request_cb);

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

        DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close);
        EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
        EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair));
        EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair));

        /* Complete the handshake */
        EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn));
        EXPECT_TRUE(client_conn->secure_renegotiation);

        /* Send the hello request message. */
        EXPECT_OK(s2n_send_client_hello_request(server_conn));

        /* no_renegotation alert NOT sent and received */
        EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn));
        EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn));

        /* Callback was not set */
        EXPECT_NULL(client_conn->config->renegotiate_request_cb);
    }

    /* Test: Hello requests received after the handshake trigger a no_renegotiation alert
     * if the application rejects the renegotiation request
     *
     *= https://www.rfc-editor.org/rfc/rfc5746#5
     *= type=test
     *# TLS implementations SHOULD provide a mechanism to disable and enable
     *# renegotiation.
     *
     *= https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.1
     *= type=test
     *# This message MAY be ignored by
     *# the client if it does not wish to renegotiate a session, or the
     *# client may, if it wishes, respond with a no_renegotiation alert.
     **/
    {
        struct s2n_test_reneg_req_ctx ctx = { .app_decision = S2N_RENEGOTIATE_REJECT };

        DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(client_conn);
        EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_reneg_cb));
        EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config_with_reneg_cb, s2n_test_reneg_req_cb, &ctx));

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

        DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close);
        EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
        EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair));
        EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair));

        /* Complete the handshake */
        EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn));
        EXPECT_TRUE(client_conn->secure_renegotiation);

        /* Send the hello request message. */
        EXPECT_OK(s2n_send_client_hello_request(server_conn));

        /* no_renegotation alert sent and received */
        EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn));
        EXPECT_ERROR_WITH_ERRNO(s2n_send_and_recv_test(client_conn, server_conn), S2N_ERR_ALERT);
        EXPECT_EQUAL(s2n_connection_get_alert(server_conn), S2N_TLS_ALERT_NO_RENEGOTIATION);

        /* Callback triggered */
        EXPECT_NOT_NULL(client_conn->config->renegotiate_request_cb);
        EXPECT_EQUAL(ctx.call_count, 1);
    };

    /* Test: Hello requests received after the handshake do not trigger a no_renegotiation alert
     * if the application accepts the renegotiation request
     *
     *= https://www.rfc-editor.org/rfc/rfc5746#5
     *= type=test
     *# TLS implementations SHOULD provide a mechanism to disable and enable
     *# renegotiation.
     */
    {
        struct s2n_test_reneg_req_ctx ctx = { .app_decision = S2N_RENEGOTIATE_ACCEPT };

        DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(client_conn);
        EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_reneg_cb));
        EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config_with_reneg_cb, s2n_test_reneg_req_cb, &ctx));

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

        DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close);
        EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
        EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair));
        EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair));

        /* Complete the handshake */
        EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn));
        EXPECT_TRUE(client_conn->secure_renegotiation);

        /* Send the hello request message. */
        EXPECT_OK(s2n_send_client_hello_request(server_conn));

        /* no_renegotation alert NOT sent and received */
        EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn));
        EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn));

        /* Callback triggered */
        EXPECT_NOT_NULL(client_conn->config->renegotiate_request_cb);
        EXPECT_EQUAL(ctx.call_count, 1);
    }

    /* Test: Hello requests received after the handshake do not trigger a no_renegotiation alert
     * if the application ignores the renegotiation request
     */
    {
        struct s2n_test_reneg_req_ctx ctx = { .app_decision = S2N_RENEGOTIATE_IGNORE };

        DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(client_conn);
        EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_reneg_cb));
        EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config_with_reneg_cb, s2n_test_reneg_req_cb, &ctx));

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

        DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close);
        EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
        EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair));
        EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair));

        /* Complete the handshake */
        EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn));
        EXPECT_TRUE(client_conn->secure_renegotiation);

        /* Send the hello request message. */
        EXPECT_OK(s2n_send_client_hello_request(server_conn));

        /* no_renegotation alert NOT sent and received */
        EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn));
        EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn));

        /* Callback triggered */
        EXPECT_NOT_NULL(client_conn->config->renegotiate_request_cb);
        EXPECT_EQUAL(ctx.call_count, 1);
    }

    /* Test: Hello requests received after the handshake trigger a no_renegotiation alert
     * if secure renegotiation is not supported, even if the application would have accepted the request.
     *
     *= https://www.rfc-editor.org/rfc/rfc5746#section-4.2
     *= type=test
     *# This text applies if the connection's "secure_renegotiation" flag is
     *# set to FALSE.
     *#
     *# It is possible that un-upgraded servers will request that the client
     *# renegotiate.  It is RECOMMENDED that clients refuse this
     *# renegotiation request.  Clients that do so MUST respond to such
     *# requests with a "no_renegotiation" alert (RFC 5246 requires this
     *# alert to be at the "warning" level).  It is possible that the
     *# apparently un-upgraded server is in fact an attacker who is then
     *# allowing the client to renegotiate with a different, legitimate,
     *# upgraded server.
     **/
    {
        struct s2n_test_reneg_req_ctx ctx = { .app_decision = S2N_RENEGOTIATE_ACCEPT };

        DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(client_conn);
        EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_reneg_cb));
        EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config_with_reneg_cb, s2n_test_reneg_req_cb, &ctx));

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

        DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close);
        EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
        EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair));
        EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair));

        /* Complete the handshake */
        EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn));

        /* Force secure_renegotiation to be false */
        client_conn->secure_renegotiation = false;

        /* Send the hello request message. */
        EXPECT_OK(s2n_send_client_hello_request(server_conn));

        /* no_renegotation alert sent and received */
        EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn));
        EXPECT_ERROR_WITH_ERRNO(s2n_send_and_recv_test(client_conn, server_conn), S2N_ERR_ALERT);
        EXPECT_EQUAL(s2n_connection_get_alert(server_conn), S2N_TLS_ALERT_NO_RENEGOTIATION);

        /* Callback was not triggered */
        EXPECT_NOT_NULL(client_conn->config->renegotiate_request_cb);
        EXPECT_EQUAL(ctx.call_count, 0);
    }

    /* Test: Application callback method fails */
    {
        struct s2n_test_reneg_req_ctx ctx = { .app_decision = S2N_RENEGOTIATE_ACCEPT };

        DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(client_conn);
        EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_reneg_cb));
        EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config_with_reneg_cb, s2n_test_reneg_req_cb, &ctx));

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

        DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close);
        EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
        EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair));
        EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair));

        /* Complete the handshake */
        EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn));

        /* Send the hello request message. */
        EXPECT_OK(s2n_send_client_hello_request(server_conn));

        /* Force the callback to fail.
         * We're only allowed to call it UINT8_MAX times, so calling
         * one more time will cause it to fail with S2N_ERR_SAFETY.
         */
        EXPECT_NOT_NULL(client_conn->config->renegotiate_request_cb);
        ctx.call_count = UINT8_MAX;

        /* s2n_recv should not surface the callback error as S2N_ERR_SAFETY.
         * Applications won't be able to set s2n_errno to a meaningful value,
         * so we need to set it to S2N_ERR_CANCELED for them.
         */
        EXPECT_ERROR_WITH_ERRNO(s2n_send_and_recv_test(server_conn, client_conn), S2N_ERR_CANCELLED);
    };

    /* Test: SSLv3 sends a fatal handshake_failure alert instead of no_renegotiate
     *
     *= https://www.rfc-editor.org/rfc/rfc5746#4.5
     *= type=test
     *# SSLv3 does not define the "no_renegotiation" alert (and does
     *# not offer a way to indicate a refusal to renegotiate at a "warning"
     *# level).  SSLv3 clients that refuse renegotiation SHOULD use a fatal
     *# handshake_failure alert.
     **/
    if (s2n_hash_is_available(S2N_HASH_MD5)) {
        struct s2n_test_reneg_req_ctx ctx = { .app_decision = S2N_RENEGOTIATE_REJECT };

        DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(client_conn);
        EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_reneg_cb));
        EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "test_all"));
        EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config_with_reneg_cb, s2n_test_reneg_req_cb, &ctx));
        EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING));

        DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(server_conn);
        EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config));
        EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all"));

        DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close);
        EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
        EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair));

        uint8_t buffer[1] = { 0 };
        s2n_blocked_status blocked = S2N_NOT_BLOCKED;

        /* Force an SSLv3 handshake */
        client_conn->client_protocol_version = S2N_SSLv3;
        client_conn->actual_protocol_version = S2N_SSLv3;
        EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn));
        EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_SSLv3);
        EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_SSLv3);

        /* Send the hello request message. */
        EXPECT_OK(s2n_send_client_hello_request(server_conn));

        /* handshake_failure alert queued */
        EXPECT_FAILURE_WITH_ERRNO(s2n_recv(client_conn, buffer, sizeof(buffer), &blocked), S2N_ERR_BAD_MESSAGE);
        EXPECT_TRUE(s2n_connection_check_io_status(client_conn, S2N_IO_CLOSED));

        /* handshake_failure alert send.
         * Skip blinding. */
        EXPECT_TRUE(client_conn->delay > 0);
        client_conn->delay = 0;
        EXPECT_SUCCESS(s2n_shutdown_send(client_conn, &blocked));

        /* handshake_failure alert received */
        EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, buffer, sizeof(buffer), &blocked), S2N_ERR_ALERT);
        EXPECT_TRUE(s2n_connection_check_io_status(server_conn, S2N_IO_CLOSED));
        EXPECT_EQUAL(s2n_connection_get_alert(server_conn), S2N_TLS_ALERT_HANDSHAKE_FAILURE);

        /* Callback triggered */
        EXPECT_NOT_NULL(client_conn->config->renegotiate_request_cb);
        EXPECT_EQUAL(ctx.call_count, 1);
    }

    EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key));
    END_TEST();
}
