/*
 * 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.
 */

#pragma once

#include <stdint.h>
#include <s2n.h>

#include "tls/s2n_crypto.h"
#include "tls/s2n_signature_algorithms.h"
#include "tls/s2n_tls_parameters.h"

#include "stuffer/s2n_stuffer.h"

#include "crypto/s2n_certificate.h"
#include "crypto/s2n_hash.h"

/* From RFC 8446: https://tools.ietf.org/html/rfc8446#appendix-B.3 */
#define TLS_HELLO_REQUEST              0
#define TLS_CLIENT_HELLO               1
#define TLS_SERVER_HELLO               2
#define TLS_SERVER_NEW_SESSION_TICKET  4
#define TLS_ENCRYPTED_EXTENSIONS       8
#define TLS_CERTIFICATE               11
#define TLS_SERVER_KEY                12
#define TLS_CERT_REQ                  13
#define TLS_SERVER_HELLO_DONE         14
#define TLS_CERT_VERIFY               15
#define TLS_CLIENT_KEY                16
#define TLS_FINISHED                  20
#define TLS_SERVER_CERT_STATUS        22
#define TLS_SERVER_SESSION_LOOKUP     23
#define TLS_KEY_UPDATE                24
#define TLS_MESSAGE_HASH             254

/* This is the list of message types that we support */
typedef enum {
    CLIENT_HELLO=0,
    SERVER_HELLO,
    SERVER_CERT,
    SERVER_NEW_SESSION_TICKET,
    SERVER_CERT_STATUS,
    SERVER_KEY,
    SERVER_CERT_REQ,
    SERVER_HELLO_DONE,
    CLIENT_CERT,
    CLIENT_KEY,
    CLIENT_CERT_VERIFY,
    CLIENT_CHANGE_CIPHER_SPEC,
    CLIENT_FINISHED,
    SERVER_CHANGE_CIPHER_SPEC,
    SERVER_FINISHED,

    /* TLS1.3 message types. Defined: https://tools.ietf.org/html/rfc8446#appendix-B.3 */
    ENCRYPTED_EXTENSIONS,
    SERVER_CERT_VERIFY,
    HELLO_RETRY_MSG,

    APPLICATION_DATA,
} message_type_t;

typedef enum {
    S2N_ASYNC_NOT_INVOKED = 0,
    S2N_ASYNC_INVOKING_CALLBACK,
    S2N_ASYNC_INVOKED_WAITING,
    S2N_ASYNC_INVOKED_COMPLETE,
} s2n_async_state;

struct s2n_handshake_parameters {
    /* Signature/hash algorithm pairs offered by the client in the signature_algorithms extension */
    struct s2n_sig_scheme_list client_sig_hash_algs;

    /* Signature/hash algorithm pairs offered by the server in the certificate request */
    struct s2n_sig_scheme_list server_sig_hash_algs;

    /* The cert chain we will send the peer. */
    struct s2n_cert_chain_and_key *our_chain_and_key;

    /* The subset of certificates that match the server_name presented in the ClientHello.
     * In the case of multiple certificates matching a server_name, s2n will prefer certificates
     * in FIFO order based on calls to s2n_config_add_cert_chain_and_key_to_store
     *
     * Note that in addition to domain matching, the key type for the certificate must also be
     * suitable for a negotiation in order to be selected. The set of matching certs here are indexed
     * by s2n_authentication_method.
     *
     * Example:
     *    - Assume certA is added to s2n_config via s2n_config_add_cert_chain_and_key_to_store
     *    - Next certB is added.
     *    - if certA matches www.foo.com and certB matches www.foo.com, s2n will prefer certA
     *
     * Note that in addition to domain matching, the key type for the certificate must also be
     * suitable for a negotiation in order to be selected.
     *
     * Example:
     *    - Assume certA and certB match server_name www.foo.com
     *    - certA is ECDSA and certB is RSA.
     *    - Client only supports RSA ciphers
     *    - certB will be selected.
     */
    struct s2n_cert_chain_and_key *exact_sni_matches[S2N_CERT_TYPE_COUNT];
    struct s2n_cert_chain_and_key *wc_sni_matches[S2N_CERT_TYPE_COUNT];
    uint8_t exact_sni_match_exists;
    uint8_t wc_sni_match_exists;
};

struct s2n_handshake {
    struct s2n_stuffer io;

    struct s2n_hash_state md5;
    struct s2n_hash_state sha1;
    struct s2n_hash_state sha224;
    struct s2n_hash_state sha256;
    struct s2n_hash_state sha384;
    struct s2n_hash_state sha512;
    struct s2n_hash_state md5_sha1;

    /* A copy of the handshake messages hash used to validate the CertificateVerify message */
    struct s2n_hash_state ccv_hash_copy;

    /* Used for SSLv3, TLS 1.0, and TLS 1.1 PRFs */
    struct s2n_hash_state prf_md5_hash_copy;
    struct s2n_hash_state prf_sha1_hash_copy;
    /*Used for TLS 1.2 PRF */
    struct s2n_hash_state prf_tls12_hash_copy;
    struct s2n_hash_state server_finished_copy;

    /* Hash algorithms required for this handshake. The set of required hashes can be reduced as session parameters are
     * negotiated, i.e. cipher suite and protocol version.
     */
    uint8_t required_hash_algs[S2N_HASH_SENTINEL];

    uint8_t server_finished[S2N_TLS_SECRET_LEN];
    uint8_t client_finished[S2N_TLS_SECRET_LEN];

    /* Handshake type is a bitset, with the following
       bit positions */
    uint32_t handshake_type;

/* Has the handshake been negotiated yet? */
#define INITIAL                     0x00
#define NEGOTIATED                  0x01
#define IS_NEGOTIATED( type ) ( (type) & NEGOTIATED )

/* Handshake is a full handshake  */
#define FULL_HANDSHAKE              0x02
#define IS_FULL_HANDSHAKE( type )   ( (type) & FULL_HANDSHAKE )
#define IS_RESUMPTION_HANDSHAKE( type ) ( !IS_FULL_HANDSHAKE( (type) ) && IS_NEGOTIATED ( (type) ) )

/* Handshake uses perfect forward secrecy */
#define TLS12_PERFECT_FORWARD_SECRECY 0x04

/* Handshake needs OCSP status message */
#define OCSP_STATUS                 0x08
#define IS_OCSP_STAPLED( type ) ( ( (type) & OCSP_STATUS ) != 0 )

/* Handshake should request a Client Certificate */
#define CLIENT_AUTH                 0x10
#define IS_CLIENT_AUTH_HANDSHAKE( type )   ( (type) & CLIENT_AUTH )

/* Session Resumption via session-tickets */
#define WITH_SESSION_TICKET         0x20
#define IS_ISSUING_NEW_SESSION_TICKET( type )   ( (type) & WITH_SESSION_TICKET )

/* Handshake requested a Client Certificate but did not get one */
#define NO_CLIENT_CERT              0x40
#define IS_CLIENT_AUTH_NO_CERT( type )   ( IS_CLIENT_AUTH_HANDSHAKE( (type) ) && ( (type) & NO_CLIENT_CERT) )

/* A HelloRetryRequest was needed to proceed with the handshake */
#define HELLO_RETRY_REQUEST         0x80

/* Disguise a TLS1.3 handshake as a TLS1.2 handshake for backwards compatibility
 * with some middleboxes: https://tools.ietf.org/html/rfc8446#appendix-D.4 */
#define MIDDLEBOX_COMPAT            0x100
#define IS_MIDDLEBOX_COMPAT_MODE( type ) ( (type) & MIDDLEBOX_COMPAT )

    /* Which handshake message number are we processing */
    int message_number;

    /* State of the async pkey operation during handshake */
    s2n_async_state async_state;

    /* Indicates the CLIENT_HELLO message has been completely received */
    unsigned client_hello_received:1;

    /* Indicates the handshake blocked while trying to read or write data, and has been paused */
    unsigned paused:1;

    /* Set to 1 if the RSA verification failed */
    unsigned rsa_failed:1;
};

extern message_type_t s2n_conn_get_current_message_type(struct s2n_connection *conn);
extern int s2n_conn_set_handshake_type(struct s2n_connection *conn);
extern int s2n_conn_set_handshake_no_client_cert(struct s2n_connection *conn);
extern int s2n_conn_set_handshake_read_block(struct s2n_connection *conn);
extern int s2n_conn_clear_handshake_read_block(struct s2n_connection *conn);
extern int s2n_handshake_require_all_hashes(struct s2n_handshake *handshake);
extern uint8_t s2n_handshake_is_hash_required(struct s2n_handshake *handshake, s2n_hash_algorithm hash_alg);
extern int s2n_conn_update_required_handshake_hashes(struct s2n_connection *conn);
extern int s2n_handshake_get_hash_state(struct s2n_connection *conn, s2n_hash_algorithm hash_alg, struct s2n_hash_state *hash_state);
extern int s2n_handshake_reset_hash_state(struct s2n_connection *conn, s2n_hash_algorithm hash_alg);
extern int s2n_conn_find_name_matching_certs(struct s2n_connection *conn);
extern int s2n_create_wildcard_hostname(struct s2n_stuffer *hostname, struct s2n_stuffer *output);
struct s2n_cert_chain_and_key *s2n_get_compatible_cert_chain_and_key(struct s2n_connection *conn, const s2n_pkey_type cert_type);
int s2n_conn_update_handshake_hashes(struct s2n_connection *conn, struct s2n_blob *data);
S2N_RESULT s2n_quic_read_handshake_message(struct s2n_connection *conn, uint8_t *message_type);
S2N_RESULT s2n_quic_write_handshake_message(struct s2n_connection *conn, struct s2n_blob *in);