/*
 * 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/extensions/s2n_client_renegotiation_info.h"
#include "tls/s2n_tls.h"

int s2n_parse_client_hello(struct s2n_connection *conn);

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

    const uint8_t renegotiation_info_scsv_iana[] = { TLS_EMPTY_RENEGOTIATION_INFO_SCSV };
    const uint8_t client_verify_data[] = "client verify data";

    DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free);
    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_config *config = s2n_config_new(), s2n_config_ptr_free);
    EXPECT_NOT_NULL(config);
    EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key));

    /* Test receive - too much data */
    {
        struct s2n_connection *conn = NULL;
        EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER));

        struct s2n_stuffer stuffer = { 0 };
        EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0));

        EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, 0));

        EXPECT_FAILURE_WITH_ERRNO(s2n_client_renegotiation_info_extension.recv(conn, &stuffer),
                S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO);
        EXPECT_FALSE(conn->secure_renegotiation);

        EXPECT_SUCCESS(s2n_stuffer_free(&stuffer));
        EXPECT_SUCCESS(s2n_connection_free(conn));
    };

    /* Test receive - value not 0
     *
     *= https://www.rfc-editor.org/rfc/rfc5746#3.6
     *= type=test
     *# The server MUST then verify
     *# that the length of the "renegotiated_connection" field is zero,
     *# and if it is not, MUST abort the handshake.
     */
    {
        struct s2n_connection *conn = NULL;
        EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER));

        struct s2n_stuffer stuffer = { 0 };
        EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0));

        EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 1));

        EXPECT_FAILURE_WITH_ERRNO(s2n_client_renegotiation_info_extension.recv(conn, &stuffer),
                S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO);
        EXPECT_FALSE(conn->secure_renegotiation);

        EXPECT_SUCCESS(s2n_stuffer_free(&stuffer));
        EXPECT_SUCCESS(s2n_connection_free(conn));
    };

    /* Test receive */
    {
        struct s2n_connection *conn = NULL;
        EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER));

        struct s2n_stuffer stuffer = { 0 };
        EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0));

        EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 0));

        EXPECT_SUCCESS(s2n_client_renegotiation_info_extension.recv(conn, &stuffer));
        EXPECT_TRUE(conn->secure_renegotiation);

        EXPECT_SUCCESS(s2n_stuffer_free(&stuffer));
        EXPECT_SUCCESS(s2n_connection_free(conn));
    };

    /* Test receive when using SSLv3
     *
     *= https://www.rfc-editor.org/rfc/rfc5746#4.5
     *= type=test
     *# TLS servers that support secure renegotiation and support SSLv3 MUST accept SCSV or the
     *# "renegotiation_info" extension and respond as described in this
     *# specification even if the offered client version is {0x03, 0x00}.
     **/
    {
        DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(server_conn);

        DEFER_CLEANUP(struct s2n_stuffer extension = { 0 }, s2n_stuffer_free);
        EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0));
        EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 0));

        server_conn->server_protocol_version = S2N_SSLv3;
        server_conn->actual_protocol_version = S2N_SSLv3;
        EXPECT_SUCCESS(s2n_client_renegotiation_info_extension.recv(server_conn, &extension));
        EXPECT_TRUE(server_conn->secure_renegotiation);
    };

    /*
     *= https://www.rfc-editor.org/rfc/rfc5746#3.4
     *= type=test
     *# o  The client MUST include either an empty "renegotiation_info"
     *#    extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling
     *#    cipher suite value in the ClientHello.  Including both is NOT
     *#    RECOMMENDED.
     */
    {
        DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(client_conn);

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

        /* Process the client hello on the server */
        EXPECT_SUCCESS(s2n_client_hello_send(client_conn));
        EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io,
                s2n_stuffer_data_available(&client_conn->handshake.io)));
        EXPECT_SUCCESS(s2n_parse_client_hello(server_conn));

        /* Expect TLS_EMPTY_RENEGOTIATION_INFO_SCSV */
        bool found_renegotiation_info_scsv = false;
        for (size_t i = 0; i < server_conn->client_hello.cipher_suites.size; i += S2N_TLS_CIPHER_SUITE_LEN) {
            uint8_t *iana = server_conn->client_hello.cipher_suites.data + i;
            if (memcmp(iana, renegotiation_info_scsv_iana, S2N_TLS_CIPHER_SUITE_LEN) == 0) {
                found_renegotiation_info_scsv = true;
            }
        }
        EXPECT_TRUE(found_renegotiation_info_scsv);

        /* Do NOT expect "renegotiation_info" extension */
        s2n_extension_type_id extension_id = 0;
        EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(
                s2n_client_renegotiation_info_extension.iana_value, &extension_id));
        s2n_parsed_extension *extension = &server_conn->client_hello.extensions.parsed_extensions[extension_id];
        EXPECT_EQUAL(extension->extension.size, 0);
        EXPECT_EQUAL(extension->extension_type, 0);
    };

    /**
     *= https://www.rfc-editor.org/rfc/rfc5746#3.6
     *= type=test
     *# o  The server MUST check if the "renegotiation_info" extension is
     *# included in the ClientHello.
     */
    {
        DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(client_conn);

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

        /* s2n-tls clients do not send the "renegotiation_info" extension.
         * Instead, they send the TLS_EMPTY_RENEGOTIATION_INFO_SCSV cipher suite.
         * See previous test.
         */
        EXPECT_SUCCESS(s2n_client_hello_send(client_conn));

        /* Process the extensions on the server */
        EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io,
                s2n_stuffer_data_available(&client_conn->handshake.io)));
        EXPECT_SUCCESS(s2n_parse_client_hello(server_conn));
        EXPECT_SUCCESS(s2n_extension_list_process(S2N_EXTENSION_LIST_CLIENT_HELLO, server_conn,
                &server_conn->client_hello.extensions));

        /* Expect secure renegotiation to still be false: no extension received */
        EXPECT_FALSE(server_conn->secure_renegotiation);

        /* Manually append the "renegotiation_info" extension to the original list. */
        uint8_t extension[] = {
            0xff, 0x01, /* extension type: renegotiation_info */
            0x00, 0x01, /* extension length: 1 */
            0x00,       /* renegotiated_connection length: 0 */
        };
        size_t client_hello_size = server_conn->client_hello.raw_message.size;
        size_t old_extensions_size = server_conn->client_hello.extensions.raw.size;
        size_t new_extensions_size = old_extensions_size + sizeof(extension);
        EXPECT_SUCCESS(s2n_stuffer_rewrite(&client_conn->handshake.io));
        EXPECT_SUCCESS(s2n_stuffer_skip_write(&client_conn->handshake.io,
                client_hello_size - old_extensions_size - sizeof(uint16_t)));
        EXPECT_SUCCESS(s2n_stuffer_write_uint16(&client_conn->handshake.io, new_extensions_size));
        EXPECT_SUCCESS(s2n_stuffer_skip_write(&client_conn->handshake.io, old_extensions_size));
        EXPECT_SUCCESS(s2n_stuffer_write_bytes(&client_conn->handshake.io, extension, sizeof(extension)));

        /* Process the extensions on the server again */
        EXPECT_SUCCESS(s2n_connection_wipe(server_conn));
        EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io,
                s2n_stuffer_data_available(&client_conn->handshake.io)));
        EXPECT_SUCCESS(s2n_parse_client_hello(server_conn));
        EXPECT_SUCCESS(s2n_extension_list_process(S2N_EXTENSION_LIST_CLIENT_HELLO, server_conn,
                &server_conn->client_hello.extensions));

        /* Expect secure renegotiation to be true: extension received */
        EXPECT_TRUE(server_conn->secure_renegotiation);
    };

    /* Test: should_send during renegotiation handshake
     *
     *= https://www.rfc-editor.org/rfc/rfc5746#3.5
     *= type=test
     *# o  The client MUST include the "renegotiation_info" extension in the
     *#    ClientHello
     */
    {
        DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(conn);

        /* Not included by default */
        EXPECT_FALSE(s2n_client_renegotiation_info_extension.should_send(conn));

        /* Included if renegotiation enabled */
        conn->handshake.renegotiation = true;
        EXPECT_TRUE(s2n_client_renegotiation_info_extension.should_send(conn));
    };

    /* Test: send during renegotiation handshake
     *
     *= https://www.rfc-editor.org/rfc/rfc5746#3.5
     *= type=test
     *# o  The client MUST include the "renegotiation_info" extension in the
     *#    ClientHello, containing the saved client_verify_data.
     */
    {
        /* Send client_verify_data */
        {
            DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT),
                    s2n_connection_ptr_free);
            EXPECT_NOT_NULL(conn);
            conn->handshake.renegotiation = true;

            /* Setup client_verify_data */
            EXPECT_MEMCPY_SUCCESS(conn->handshake.client_finished,
                    client_verify_data, sizeof(client_verify_data));
            conn->handshake.finished_len = sizeof(client_verify_data);

            /* Error if secure renegotiation not supported */
            {
                conn->secure_renegotiation = false;

                DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free);
                EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0));
                EXPECT_FAILURE_WITH_ERRNO(s2n_client_renegotiation_info_extension.send(conn, &out),
                        S2N_ERR_NO_RENEGOTIATION);
            };

            /* Success if secure renegotiation supported */
            {
                conn->secure_renegotiation = true;

                DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free);
                EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0));
                EXPECT_SUCCESS(s2n_client_renegotiation_info_extension.send(conn, &out));

                uint8_t actual_len = 0;
                EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &actual_len));
                EXPECT_EQUAL(actual_len, sizeof(client_verify_data));

                uint8_t *actual_data = s2n_stuffer_raw_read(&out, actual_len);
                EXPECT_BYTEARRAY_EQUAL(actual_data, client_verify_data, actual_len);
            };
        };

        /*
         *= https://www.rfc-editor.org/rfc/rfc5746#3.5
         *= type=test
         *# The SCSV MUST NOT be included.
         */
        {
            DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT),
                    s2n_connection_ptr_free);
            EXPECT_NOT_NULL(conn);
            conn->secure_renegotiation = true;
            conn->handshake.renegotiation = true;

            /* Setup client_verify_data */
            EXPECT_MEMCPY_SUCCESS(conn->handshake.client_finished,
                    client_verify_data, sizeof(client_verify_data));
            conn->handshake.finished_len = sizeof(client_verify_data);

            EXPECT_SUCCESS(s2n_client_hello_send(conn));
            EXPECT_SUCCESS(s2n_parse_client_hello(conn));

            /* Expect TLS_EMPTY_RENEGOTIATION_INFO_SCSV */
            bool found_renegotiation_info_scsv = false;
            for (size_t i = 0; i < conn->client_hello.cipher_suites.size; i += S2N_TLS_CIPHER_SUITE_LEN) {
                uint8_t *iana = conn->client_hello.cipher_suites.data + i;
                if (memcmp(iana, renegotiation_info_scsv_iana, S2N_TLS_CIPHER_SUITE_LEN) == 0) {
                    found_renegotiation_info_scsv = true;
                }
            }
            EXPECT_FALSE(found_renegotiation_info_scsv);
        };
    };

    /* Test: recv during renegotiation handshake
     *
     *= https://www.rfc-editor.org/rfc/rfc5746#3.7
     *= type=test
     *# o  The server MUST verify that the value of the
     *#    "renegotiated_connection" field is equal to the saved
     *#    client_verify_data value; if it is not, the server MUST abort the
     *#    handshake.
     */
    {
        DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_SERVER),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(client_conn);
        client_conn->handshake.renegotiation = true;
        client_conn->secure_renegotiation = true;

        DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(server_conn);
        server_conn->handshake.renegotiation = true;
        server_conn->secure_renegotiation = true;

        DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free);
        EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0));

        EXPECT_MEMCPY_SUCCESS(client_conn->handshake.client_finished,
                client_verify_data, sizeof(client_verify_data));
        client_conn->handshake.finished_len = sizeof(client_verify_data);

        /* Test: client_verify matches */
        {
            EXPECT_MEMCPY_SUCCESS(server_conn->handshake.client_finished,
                    client_verify_data, sizeof(client_verify_data));
            server_conn->handshake.finished_len = sizeof(client_verify_data);

            EXPECT_SUCCESS(s2n_client_renegotiation_info_extension.send(client_conn, &stuffer));
            EXPECT_SUCCESS(s2n_client_renegotiation_info_extension.recv(server_conn, &stuffer));
        };

        /* Test: client_verify does not match */
        {
            const uint8_t bad_client_verify[] = "not correct";

            EXPECT_MEMCPY_SUCCESS(server_conn->handshake.client_finished,
                    bad_client_verify, sizeof(bad_client_verify));
            server_conn->handshake.finished_len = sizeof(bad_client_verify);

            EXPECT_SUCCESS(s2n_client_renegotiation_info_extension.send(client_conn, &stuffer));
            EXPECT_FAILURE_WITH_ERRNO(s2n_client_renegotiation_info_extension.recv(server_conn, &stuffer),
                    S2N_ERR_BAD_MESSAGE);
        };

        /* Test: no secure_renegotiation */
        {
            server_conn->secure_renegotiation = false;

            EXPECT_MEMCPY_SUCCESS(server_conn->handshake.client_finished,
                    client_verify_data, sizeof(client_verify_data));
            server_conn->handshake.finished_len = sizeof(client_verify_data);

            EXPECT_SUCCESS(s2n_client_renegotiation_info_extension.send(client_conn, &stuffer));
            EXPECT_FAILURE_WITH_ERRNO(s2n_client_renegotiation_info_extension.recv(server_conn, &stuffer),
                    S2N_ERR_NO_RENEGOTIATION);
        };
    };

    /* Test: if_missing during renegotiation handshake
     *
     *= https://www.rfc-editor.org/rfc/rfc5746#3.7
     *= type=test
     *# o  The server MUST verify that the "renegotiation_info" extension is
     *#    present; if it is not, the server MUST abort the handshake.
     */
    {
        DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(client_conn);
        client_conn->handshake.renegotiation = false;

        DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(server_conn);
        server_conn->handshake.renegotiation = true;
        server_conn->secure_renegotiation = true;

        EXPECT_SUCCESS(s2n_client_hello_send(client_conn));
        EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io,
                s2n_stuffer_data_available(&client_conn->handshake.io)));

        /* Verify if_missing throws error */
        EXPECT_FAILURE_WITH_ERRNO(s2n_client_renegotiation_info_extension.if_missing(server_conn),
                S2N_ERR_MISSING_EXTENSION);

        /* Verify server marks extension as missing when processing client hello */
        EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_MISSING_EXTENSION);
    };

    /* Test: receiving SCSV during renegotiation is an error
     *
     *= https://www.rfc-editor.org/rfc/rfc5746#3.7
     *= type=test
     *# o  When a ClientHello is received, the server MUST verify that it
     *#    does not contain the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.  If
     *#    the SCSV is present, the server MUST abort the handshake.
     */
    {
        DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(client_conn);
        client_conn->secure_renegotiation = true;

        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));
        server_conn->secure_renegotiation = true;

        /* Construct a security policy that will write the SCSV like a regular cipher suite */
        struct s2n_cipher_suite forced_scsv = {
            .available = true,
            .iana_value = { TLS_EMPTY_RENEGOTIATION_INFO_SCSV },
        };
        struct s2n_cipher_suite *cipher_suites[] = {
            &forced_scsv,
            &s2n_ecdhe_rsa_with_aes_128_gcm_sha256
        };
        struct s2n_cipher_preferences cipher_preferences = { .suites = cipher_suites, .count = s2n_array_len(cipher_suites) };
        struct s2n_security_policy security_policy = *config->security_policy;
        security_policy.cipher_preferences = &cipher_preferences;
        client_conn->security_policy_override = &security_policy;

        /* Succeeds if server expects an initial handshake */
        EXPECT_SUCCESS(s2n_client_hello_send(client_conn));
        EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io,
                s2n_stuffer_data_available(&client_conn->handshake.io)));
        EXPECT_SUCCESS(s2n_client_hello_recv(server_conn));

        client_conn->handshake.renegotiation = true;
        EXPECT_MEMCPY_SUCCESS(client_conn->handshake.client_finished,
                client_verify_data, sizeof(client_verify_data));
        client_conn->handshake.finished_len = sizeof(client_verify_data);

        server_conn->handshake.renegotiation = true;
        EXPECT_MEMCPY_SUCCESS(server_conn->handshake.client_finished,
                client_verify_data, sizeof(client_verify_data));
        server_conn->handshake.finished_len = sizeof(client_verify_data);

        /* Fails if server expects renegotiation */
        EXPECT_SUCCESS(s2n_client_hello_send(client_conn));
        EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io,
                s2n_stuffer_data_available(&client_conn->handshake.io)));
        EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_BAD_MESSAGE);
    };

    END_TEST();
}
