/*
* TLS Channel - implementation for TLS 1.3
* (C) 2022 Jack Lloyd
*     2021 Elektrobit Automotive GmbH
*     2022 Hannes Rantzsch, René Meusel - neXenio GmbH
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#ifndef BOTAN_TLS_CHANNEL_IMPL_13_H_
#define BOTAN_TLS_CHANNEL_IMPL_13_H_

#include <botan/tls_messages_13.h>
#include <botan/internal/stl_util.h>
#include <botan/internal/tls_channel_impl.h>
#include <botan/internal/tls_connection_state_13.h>
#include <botan/internal/tls_handshake_layer_13.h>
#include <botan/internal/tls_record_layer_13.h>
#include <botan/internal/tls_transcript_hash_13.h>

namespace Botan::TLS {

class Cipher_State;

/**
 * Encapsulates the callbacks in the state machine described in RFC 8446 7.1,
 * that will make the realisation the SSLKEYLOGFILE for connection debugging
 * specified in ietf.org/archive/id/draft-thomson-tls-keylogfile-00.html
 *
 * The class is split from the rest of the Channel_Impl_13 for mockability.
 */
class Secret_Logger /* NOLINT(*-special-member-functions) */ {
   public:
      virtual ~Secret_Logger() = default;

      friend class Cipher_State;

   protected:
      /**
       * Used exclusively in the Cipher_State to pass secret data to
       * a user-provided Callbacks::tls_ssl_key_log_data() iff
       * Policy::allow_ssl_key_log_file() returns true.
       */
      virtual void maybe_log_secret(std::string_view label, std::span<const uint8_t> secret) const = 0;
};

/**
* Generic interface for TLS 1.3 endpoint
*/
class Channel_Impl_13 : public Channel_Impl,
                        protected Secret_Logger {
   protected:
      /**
       * Helper class to coalesce handshake messages into a single TLS record
       * of type 'Handshake'. This is used entirely internally in the Channel,
       * Client and Server implementations.
       *
       * Note that implementations should use the derived classes that either
       * aggregate conventional Handshake messages or Post-Handshake messages.
       */
      class AggregatedMessages {
         public:
            AggregatedMessages(Channel_Impl_13& channel, Handshake_Layer& handshake_layer);

            AggregatedMessages(const AggregatedMessages&) = delete;
            AggregatedMessages& operator=(const AggregatedMessages&) = delete;
            AggregatedMessages(AggregatedMessages&&) = delete;
            AggregatedMessages& operator=(AggregatedMessages&&) = delete;

            ~AggregatedMessages() = default;

            /**
             * Send the messages aggregated in the message buffer.
             */
            void send() const;

            bool contains_messages() const { return !m_message_buffer.empty(); }

         protected:
            std::vector<uint8_t> m_message_buffer;  // NOLINT(*non-private-member-variable*)

            Channel_Impl_13& m_channel;          // NOLINT(*non-private-member-variable*)
            Handshake_Layer& m_handshake_layer;  // NOLINT(*non-private-member-variable*)
      };

      /**
       * Aggregate conventional handshake messages. This will update the given
       * Transcript_Hash_State accordingly as individual messages are added to
       * the aggregation.
       */
      class AggregatedHandshakeMessages : public AggregatedMessages {
         public:
            AggregatedHandshakeMessages(Channel_Impl_13& channel,
                                        Handshake_Layer& handshake_layer,
                                        Transcript_Hash_State& transcript_hash);

            /**
             * Adds a single handshake message to the send buffer. Note that this
             * updates the handshake transcript hash regardless of sending the
             * message.
             */
            AggregatedHandshakeMessages& add(Handshake_Message_13_Ref message);

         private:
            Transcript_Hash_State& m_transcript_hash;
      };

      /**
       * Aggregate post-handshake messages. In contrast to ordinary handshake
       * messages this does not maintain a Transcript_Hash_State.
       */
      class AggregatedPostHandshakeMessages : public AggregatedMessages {
         public:
            using AggregatedMessages::AggregatedMessages;

            AggregatedPostHandshakeMessages& add(Post_Handshake_Message_13 message);
      };

   public:
      /**
      * Set up a new TLS 1.3 session
      *
      * @param callbacks contains a set of callback function references
      *        required by the TLS endpoint.
      * @param session_manager manages session state
      * @param credentials_manager manages application/user credentials
      * @param rng a random number generator
      * @param policy specifies other connection policy information
      * @param is_server whether this is a server session or not
      */
      explicit Channel_Impl_13(const std::shared_ptr<Callbacks>& callbacks,
                               const std::shared_ptr<Session_Manager>& session_manager,
                               const std::shared_ptr<Credentials_Manager>& credentials_manager,
                               const std::shared_ptr<RandomNumberGenerator>& rng,
                               const std::shared_ptr<const Policy>& policy,
                               bool is_server);

      Channel_Impl_13(const Channel_Impl_13& other) = delete;
      Channel_Impl_13(Channel_Impl_13&& other) = delete;
      Channel_Impl_13& operator=(const Channel_Impl_13& other) = delete;
      Channel_Impl_13& operator=(Channel_Impl_13&& other) = delete;

      ~Channel_Impl_13() override;

      size_t from_peer(std::span<const uint8_t> data) override;
      void to_peer(std::span<const uint8_t> data) override;

      /**
      * Send a TLS alert message. If the alert is fatal, the internal
      * state (keys, etc) will be reset.
      * @param alert the Alert to send
      */
      void send_alert(const Alert& alert) override;

      /**
      * @return true iff the connection is active for sending application data
      *
      * Note that the connection is active until the application has called
      * `close()`, even if a CloseNotify has been received from the peer.
      */
      bool is_active() const override;

      /**
      * @return true iff the connection has been closed, i.e. CloseNotify
      * has been received from the peer.
      */
      bool is_closed() const override { return is_closed_for_reading() && is_closed_for_writing(); }

      bool is_closed_for_reading() const override { return !m_can_read; }

      bool is_closed_for_writing() const override { return !m_can_write; }

      /**
      * Key material export (RFC 5705)
      * @param label a disambiguating label string
      * @param context a per-association context value
      * @param length the length of the desired key in bytes
      * @return key of length bytes
      */
      SymmetricKey key_material_export(std::string_view label, std::string_view context, size_t length) const override;

      /**
      * Attempt to renegotiate the session
      */
      void renegotiate(bool /* unused */) override {
         throw Invalid_Argument("renegotiation is not allowed in TLS 1.3");
      }

      /**
      * Attempt to update the session's traffic key material
      * Note that this is possible with a TLS 1.3 channel, only.
      *
      * @param request_peer_update if true, require a reciprocal key update
      */
      void update_traffic_keys(bool request_peer_update = false) override;

      /**
      * @return true iff the counterparty supports the secure
      * renegotiation extensions.
      */
      bool secure_renegotiation_supported() const override {
         // Secure renegotiation is not supported in TLS 1.3, though BoGo
         // tests expect us to claim that it is available.
         return true;
      }

      /**
      * Perform a handshake timeout check. This does nothing unless
      * this is a DTLS channel with a pending handshake state, in
      * which case we check for timeout and potentially retransmit
      * handshake packets.
      *
      * In the TLS 1.3 implementation, this always returns false.
      */
      bool timeout_check() override { return false; }

   protected:
      virtual void process_handshake_msg(Handshake_Message_13 msg) = 0;
      virtual void process_post_handshake_msg(Post_Handshake_Message_13 msg) = 0;
      virtual void process_dummy_change_cipher_spec() = 0;

      enum class Compat_Mode_Situation : uint8_t {
         BeforeSendingAlert,
         AfterSendingFirstClientHello,
         BeforeSendingSecondClientHello,
         BeforeSendingEncryptedClientFlight,
         AfterSendingFirstServerHello,
         AfterSendingHelloRetryRequest,
      };

      virtual void maybe_handle_compatibility_mode(Compat_Mode_Situation situation) = 0;

      void handle(const Key_Update& key_update);

      /**
       * Schedule a traffic key update to opportunistically happen before the
       * channel sends application data the next time. Such a key update will
       * never request a reciprocal key update from the peer.
       */
      void opportunistically_update_traffic_keys() { m_opportunistic_key_update = true; }

      template <typename... MsgTs>
      void send_handshake_message(const std::variant<MsgTs...>& message) {
         aggregate_handshake_messages().add(generalize_to<Handshake_Message_13_Ref>(message)).send();
      }

      template <typename MsgT>
      void send_handshake_message(std::reference_wrapper<MsgT> message) {
         send_handshake_message(generalize_to<Handshake_Message_13_Ref>(message));
      }

      void send_post_handshake_message(Post_Handshake_Message_13 message) {
         aggregate_post_handshake_messages().add(std::move(message)).send();
      }

      void send_dummy_change_cipher_spec();

      AggregatedHandshakeMessages aggregate_handshake_messages() {
         return AggregatedHandshakeMessages(*this, m_handshake_layer, m_transcript_hash);
      }

      AggregatedPostHandshakeMessages aggregate_post_handshake_messages() {
         return AggregatedPostHandshakeMessages(*this, m_handshake_layer);
      }

      Callbacks& callbacks() const { return *m_callbacks; }

      Session_Manager& session_manager() { return *m_session_manager; }

      Credentials_Manager& credentials_manager() { return *m_credentials_manager; }

      RandomNumberGenerator& rng() { return *m_rng; }

      const Policy& policy() const { return *m_policy; }

   private:
      void send_record(Record_Type record_type, const std::vector<uint8_t>& record);

      void process_alert(const secure_vector<uint8_t>& record);

      /**
       * Terminate the connection (on sending or receiving an error alert) and
       * clear secrets
       */
      void shutdown();

   protected:
      const Connection_Side m_side;                              // NOLINT(*non-private-member-variable*)
      Transcript_Hash_State m_transcript_hash;                   // NOLINT(*non-private-member-variable*)
      std::unique_ptr<Cipher_State> m_cipher_state;              // NOLINT(*non-private-member-variable*)
      std::optional<Active_Connection_State_13> m_active_state;  // NOLINT(*non-private-member-variable*)

#if defined(BOTAN_HAS_TLS_DOWNGRADE_SUPPORT)
      /**
       * Indicate that we have to expect a downgrade to TLS 1.2. In which case the current
       * implementation (i.e. Client_Impl_13 or Server_Impl_13) will need to be replaced
       * by their respective counter parts.
       *
       * This will prepare an internal structure where any information required to downgrade
       * can be preserved.
       * @sa `Channel_Impl::Downgrade_Information`
       */
      void expect_downgrade(const Server_Information& server_info, const std::vector<std::string>& next_protocols);
#endif

      /**
       * Set the record size limits as negotiated by the "record_size_limit"
       * extension (RFC 8449).
       *
       * @param outgoing_limit  the maximal number of plaintext bytes to be
       *                        sent in a protected record
       * @param incoming_limit  the maximal number of plaintext bytes to be
       *                        accepted in a received protected record
       */
      void set_record_size_limits(uint16_t outgoing_limit, uint16_t incoming_limit);

      /**
       * Set the expected certificate type needed to parse Certificate
       * messages in the handshake layer. See RFC 7250 and 8446 4.4.2 for
       * further details.
       */
      void set_selected_certificate_type(Certificate_Type cert_type);

   private:
      /* callbacks */
      std::shared_ptr<Callbacks> m_callbacks;

      /* external state */
      std::shared_ptr<Session_Manager> m_session_manager;
      std::shared_ptr<Credentials_Manager> m_credentials_manager;
      std::shared_ptr<RandomNumberGenerator> m_rng;
      std::shared_ptr<const Policy> m_policy;

      /* handshake state */
      Record_Layer m_record_layer;
      Handshake_Layer m_handshake_layer;

      bool m_can_read;
      bool m_can_write;

      bool m_opportunistic_key_update;
      bool m_first_message_sent;
      bool m_first_message_received;

      uint64_t m_last_key_update_ms = 0;
};
}  // namespace Botan::TLS

#endif
