/*
 * 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 "crypto/s2n_hmac.h"
#include "s2n_test.h"
#include "testlib/s2n_testlib.h"
#include "tls/extensions/s2n_server_psk.h"
#include "tls/s2n_tls.h"
#include "utils/s2n_bitmap.h"

#define TEST_PSK_WIRE_INDEX 1
#define TEST_PSK_HMAC       S2N_HMAC_SHA384

uint8_t test_identity[] = "test identity";
uint8_t test_secret[] = "test secret";

static s2n_result setup_client_psks(struct s2n_connection *client_conn)
{
    RESULT_ENSURE_REF(client_conn);

    /* Setup other client PSK */
    uint8_t other_client_data[] = "other client data";
    struct s2n_psk *other_client_psk = NULL;
    RESULT_GUARD(s2n_array_pushback(&client_conn->psk_params.psk_list, (void **) &other_client_psk));
    RESULT_GUARD(s2n_psk_init(other_client_psk, S2N_PSK_TYPE_EXTERNAL));
    RESULT_GUARD_POSIX(s2n_psk_set_identity(other_client_psk, other_client_data, sizeof(other_client_data)));
    RESULT_GUARD_POSIX(s2n_psk_set_secret(other_client_psk, other_client_data, sizeof(other_client_data)));
    other_client_psk->hmac_alg = S2N_HMAC_SHA256;

    /* Setup shared PSK for client */
    struct s2n_psk *shared_psk = NULL;
    RESULT_GUARD(s2n_array_pushback(&client_conn->psk_params.psk_list, (void **) &shared_psk));
    RESULT_GUARD(s2n_psk_init(shared_psk, S2N_PSK_TYPE_EXTERNAL));
    RESULT_GUARD_POSIX(s2n_psk_set_identity(shared_psk, test_identity, sizeof(test_identity)));
    RESULT_GUARD_POSIX(s2n_psk_set_secret(shared_psk, test_secret, sizeof(test_secret)));
    shared_psk->hmac_alg = TEST_PSK_HMAC;

    return S2N_RESULT_OK;
}

static s2n_result setup_server_psks(struct s2n_connection *server_conn)
{
    RESULT_ENSURE_REF(server_conn);

    EXPECT_OK(s2n_connection_set_psk_type(server_conn, S2N_PSK_TYPE_EXTERNAL));

    /* Setup shared PSK for server */
    struct s2n_psk *shared_psk = NULL;
    RESULT_GUARD(s2n_array_pushback(&server_conn->psk_params.psk_list, (void **) &shared_psk));
    RESULT_GUARD(s2n_psk_init(shared_psk, S2N_PSK_TYPE_EXTERNAL));
    RESULT_GUARD_POSIX(s2n_psk_set_identity(shared_psk, test_identity, sizeof(test_identity)));
    RESULT_GUARD_POSIX(s2n_psk_set_secret(shared_psk, test_secret, sizeof(test_secret)));
    shared_psk->hmac_alg = TEST_PSK_HMAC;

    /* Setup other server PSK */
    uint8_t other_server_data[] = "other server data";
    struct s2n_psk *other_server_psk = NULL;
    RESULT_GUARD(s2n_array_pushback(&server_conn->psk_params.psk_list, (void **) &other_server_psk));
    RESULT_GUARD(s2n_psk_init(other_server_psk, S2N_PSK_TYPE_EXTERNAL));
    RESULT_GUARD_POSIX(s2n_psk_set_identity(other_server_psk, other_server_data, sizeof(other_server_data)));
    RESULT_GUARD_POSIX(s2n_psk_set_secret(other_server_psk, other_server_data, sizeof(other_server_data)));
    other_server_psk->hmac_alg = S2N_HMAC_SHA224;

    return S2N_RESULT_OK;
}

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

    /* Test: s2n_server_psk_should_send */
    {
        struct s2n_psk *psk = NULL;

        struct s2n_connection *conn = NULL;
        EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER));

        EXPECT_FALSE(s2n_server_psk_extension.should_send(NULL));

        EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk));
        EXPECT_FALSE(s2n_server_psk_extension.should_send(conn));

        conn->psk_params.chosen_psk_wire_index = 0;
        EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, conn->psk_params.chosen_psk_wire_index,
                (void **) &conn->psk_params.chosen_psk));
        EXPECT_TRUE(s2n_server_psk_extension.should_send(conn));

        /* If send is called with a NULL stuffer, it will fail.
         * So a failure indicates that send was called.
         */
        EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(conn));
        conn->actual_protocol_version = S2N_TLS12;
        EXPECT_SUCCESS(s2n_extension_send(&s2n_server_psk_extension, conn, NULL));
        conn->actual_protocol_version = S2N_TLS13;
        EXPECT_FAILURE(s2n_extension_send(&s2n_server_psk_extension, conn, NULL));

        EXPECT_SUCCESS(s2n_connection_free(conn));
    };

    /* Test: s2n_server_psk_send */
    {
        /* Send the index of the chosen PSK that is stored on the connection. */
        {
            struct s2n_stuffer out = { 0 };
            EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0));

            struct s2n_connection *server_conn = NULL;
            EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER));

            EXPECT_OK(setup_server_psks(server_conn));

            server_conn->psk_params.chosen_psk_wire_index = TEST_PSK_WIRE_INDEX;
            EXPECT_SUCCESS(s2n_server_psk_extension.send(server_conn, &out));

            uint16_t chosen_psk_wire_index = 0;
            EXPECT_SUCCESS(s2n_stuffer_read_uint16(&out, &chosen_psk_wire_index));
            EXPECT_EQUAL(chosen_psk_wire_index, server_conn->psk_params.chosen_psk_wire_index);

            EXPECT_SUCCESS(s2n_connection_free(server_conn));
            EXPECT_SUCCESS(s2n_stuffer_free(&out));
        };
    };

    /* Test: s2n_server_psk_recv */
    {
        s2n_extension_type_id key_share_ext_id = 0;
        EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_KEY_SHARE, &key_share_ext_id));

        /* Test s2n_server_psk_recv for invalid TLS versions <= TLS1.2 */
        {
            struct s2n_stuffer out = { 0 };
            EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0));

            struct s2n_connection *conn = NULL;
            EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT));

            EXPECT_OK(setup_client_psks(conn));

            /* The keyshare extension needs to be present, as s2n currently only
             * supports pre-shared keys in (EC)DHE key exchange mode.
             */
            S2N_CBIT_SET(conn->extension_requests_received, key_share_ext_id);

            uint16_t chosen_psk_wire_index = 1;
            /* Incorrect protocol version */
            conn->actual_protocol_version = S2N_TLS12;
            EXPECT_SUCCESS(s2n_stuffer_write_uint16(&out, chosen_psk_wire_index));

            EXPECT_NULL(conn->psk_params.chosen_psk);
            EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(conn));
            EXPECT_SUCCESS(s2n_extension_recv(&s2n_server_psk_extension, conn, &out));
            EXPECT_NULL(conn->psk_params.chosen_psk);

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

        /* Test s2n_server_psk_recv when server key_share extension is not present */
        {
            struct s2n_stuffer out = { 0 };
            EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0));

            struct s2n_connection *conn = NULL;
            EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT));

            EXPECT_OK(setup_client_psks(conn));

            uint16_t chosen_psk_wire_index = TEST_PSK_WIRE_INDEX;
            conn->actual_protocol_version = S2N_TLS13;

            EXPECT_SUCCESS(s2n_stuffer_write_uint16(&out, chosen_psk_wire_index));

            EXPECT_NULL(conn->psk_params.chosen_psk);
            EXPECT_FAILURE_WITH_ERRNO(s2n_server_psk_extension.recv(conn, &out), S2N_ERR_MISSING_EXTENSION);
            EXPECT_NULL(conn->psk_params.chosen_psk);

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

        /* Receive invalid chosen psk wire index */
        {
            struct s2n_stuffer out = { 0 };
            EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0));

            struct s2n_connection *conn = NULL;
            EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT));

            EXPECT_OK(setup_client_psks(conn));

            /* The keyshare extension needs to be present, as s2n currently only
             * supports pre-shared keys in (EC)DHE key exchange mode.
             */
            S2N_CBIT_SET(conn->extension_responses_received, key_share_ext_id);

            /* Invalid chosen psk wire index */
            uint16_t chosen_psk_wire_index = 10;
            conn->actual_protocol_version = S2N_TLS13;

            EXPECT_SUCCESS(s2n_stuffer_write_uint16(&out, chosen_psk_wire_index));

            EXPECT_NULL(conn->psk_params.chosen_psk);
            EXPECT_FAILURE_WITH_ERRNO(s2n_server_psk_extension.recv(conn, &out), S2N_ERR_INVALID_ARGUMENT);
            EXPECT_NULL(conn->psk_params.chosen_psk);

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

        /* Receive valid server preshared extension recv */
        {
            struct s2n_stuffer out = { 0 };
            EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0));

            struct s2n_connection *conn = NULL;
            EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT));

            EXPECT_OK(setup_client_psks(conn));

            /* The keyshare extension needs to be present, as s2n currently only
             * supports pre-shared keys in (EC)DHE key exchange mode.
             */
            S2N_CBIT_SET(conn->extension_responses_received, key_share_ext_id);

            uint16_t chosen_psk_wire_index = TEST_PSK_WIRE_INDEX;
            conn->actual_protocol_version = S2N_TLS13;

            EXPECT_SUCCESS(s2n_stuffer_write_uint16(&out, chosen_psk_wire_index));

            EXPECT_NULL(conn->psk_params.chosen_psk);
            EXPECT_SUCCESS(s2n_server_psk_extension.recv(conn, &out));

            /* Verify chosen PSK */
            EXPECT_EQUAL(conn->psk_params.psk_ke_mode, S2N_PSK_DHE_KE);
            EXPECT_EQUAL(conn->psk_params.chosen_psk_wire_index, TEST_PSK_WIRE_INDEX);
            EXPECT_EQUAL(conn->psk_params.chosen_psk->identity.size, sizeof(test_identity));
            EXPECT_BYTEARRAY_EQUAL(conn->psk_params.chosen_psk->identity.data, test_identity, sizeof(test_identity));
            EXPECT_EQUAL(conn->psk_params.chosen_psk->secret.size, sizeof(test_secret));
            EXPECT_BYTEARRAY_EQUAL(conn->psk_params.chosen_psk->secret.data, test_secret, sizeof(test_secret));
            EXPECT_EQUAL(conn->psk_params.chosen_psk->hmac_alg, TEST_PSK_HMAC);

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

    /* Functional test */
    if (s2n_is_tls13_fully_supported()) {
        /* Setup connections */
        EXPECT_SUCCESS(s2n_enable_tls13_in_test());
        struct s2n_connection *client_conn = NULL, *server_conn = NULL;
        EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT));
        EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER));
        EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13"));
        EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13"));

        EXPECT_OK(setup_client_psks(client_conn));
        EXPECT_OK(setup_server_psks(server_conn));

        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));

        /* Verify shared PSK chosen */
        EXPECT_EQUAL(server_conn->psk_params.chosen_psk_wire_index, TEST_PSK_WIRE_INDEX);

        EXPECT_SUCCESS(s2n_server_hello_send(server_conn));

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

        EXPECT_NULL(client_conn->psk_params.chosen_psk);
        EXPECT_SUCCESS(s2n_server_hello_recv(client_conn));

        /* Verify chosen PSK received */
        EXPECT_EQUAL(client_conn->psk_params.psk_ke_mode, S2N_PSK_DHE_KE);
        EXPECT_EQUAL(client_conn->psk_params.chosen_psk_wire_index, TEST_PSK_WIRE_INDEX);
        EXPECT_EQUAL(client_conn->psk_params.chosen_psk->identity.size, sizeof(test_identity));
        EXPECT_BYTEARRAY_EQUAL(client_conn->psk_params.chosen_psk->identity.data, test_identity, sizeof(test_identity));
        EXPECT_EQUAL(client_conn->psk_params.chosen_psk->secret.size, sizeof(test_secret));
        EXPECT_BYTEARRAY_EQUAL(client_conn->psk_params.chosen_psk->secret.data, test_secret, sizeof(test_secret));
        EXPECT_EQUAL(client_conn->psk_params.chosen_psk->hmac_alg, TEST_PSK_HMAC);

        EXPECT_SUCCESS(s2n_connection_free(server_conn));
        EXPECT_SUCCESS(s2n_connection_free(client_conn));
    }

    END_TEST();
}
