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

#ifndef BOTAN_TLS_CIPHER_STATE_H_
#define BOTAN_TLS_CIPHER_STATE_H_

#include <botan/secmem.h>
#include <botan/tls_magic.h>
#include <botan/tls_messages.h>

#include <botan/internal/tls_transcript_hash_13.h>

namespace Botan {

class AEAD_Mode;
class HashFunction;
class HKDF_Extract;
class HKDF_Expand;

}  // namespace Botan

namespace Botan::TLS {

class Ciphersuite;

/**
 * This class implements the key schedule for TLS 1.3 as described in RFC 8446 7.1.
 *
 * Internally, it reflects the state machine pictured in the same RFC section.
 * It provides the following entry points and state advancement methods that
 * each facilitate certain cryptographic functionality:
 *
 * * init_with_psk()
 *   sets up the cipher state with a pre-shared key (out of band or via session
 *   ticket). will allow sending early data in the future
 *
 * * init_with_server_hello() / advance_with_server_hello()
 *   allows encrypting and decrypting handshake traffic, as well as producing
 *   and validating the client/server handshake finished MACs
 *
 * * advance_with_server_finished()
 *   allows encrypting and decrypting application traffic
 *
 * * advance_with_client_finished()
 *   allows negotiation of resumption PSKs
 *
 * While encrypting and decrypting records (RFC 8446 5.2) Cipher_State
 * internally keeps track of the current sequence numbers (RFC 8446 5.3) to
 * calculate the correct Per-Record Nonce. Sequence numbers are reset
 * appropriately, whenever traffic secrets change.
 *
 * Handshake finished MAC calculation and verification is described in RFC 8446 4.4.4.
 *
 * PSKs calculation is described in RFC 8446 4.6.1.
 */
class BOTAN_TEST_API Cipher_State {
   public:
      enum class PSK_Type {
         Resumption,
         External,  // currently not implemented
      };

   public:
      ~Cipher_State();

      /**
       * Construct a Cipher_State from a Pre-Shared-Key.
       */
      static std::unique_ptr<Cipher_State> init_with_psk(Connection_Side side,
                                                         PSK_Type type,
                                                         secure_vector<uint8_t>&& psk,
                                                         std::string_view prf_algo);

      /**
       * Construct a Cipher_State after receiving a server hello message.
       */
      static std::unique_ptr<Cipher_State> init_with_server_hello(Connection_Side side,
                                                                  secure_vector<uint8_t>&& shared_secret,
                                                                  const Ciphersuite& cipher,
                                                                  const Transcript_Hash& transcript_hash);

      /**
       * Transition internal secrets/keys for transporting early application data.
       * Note that this state transition is legal only for handshakes using PSK.
       */
      void advance_with_client_hello(const Transcript_Hash& transcript_hash);

      /**
       * Transition internal secrets/keys for transporting handshake data.
       */
      void advance_with_server_hello(const Ciphersuite& cipher,
                                     secure_vector<uint8_t>&& shared_secret,
                                     const Transcript_Hash& transcript_hash);

      /**
       * Transition internal secrets/keys for transporting application data.
       */
      void advance_with_server_finished(const Transcript_Hash& transcript_hash);

      /**
       * Transition to the final internal state allowing to create resumptions.
       */
      void advance_with_client_finished(const Transcript_Hash& transcript_hash);

      /**
       * Encrypt a TLS record fragment (RFC 8446 5.2 -- TLSInnerPlaintext) using the
       * currently available traffic secret keys and the current sequence number.
       * This will internally increment the sequence number. Hence, multiple
       * calls with the same input will not produce the same result.
       *
       * @returns  the sequence number of the encrypted record
       */
      uint64_t encrypt_record_fragment(const std::vector<uint8_t>& header, secure_vector<uint8_t>& fragment);

      /**
       * Decrypt a TLS record fragment (RFC 8446 5.2 -- TLSCiphertext.encrypted_record)
       * using the currently available traffic secret keys and the current sequence number.
       * This will internally increment the sequence number. Hence, multiple
       * calls with the same input will not produce the same result.
       *
       * @returns  the sequence number of the decrypted record
       */
      uint64_t decrypt_record_fragment(const std::vector<uint8_t>& header, secure_vector<uint8_t>& encrypted_fragment);

      /**
       * @returns  number of bytes needed to encrypt \p input_length bytes
       */
      size_t encrypt_output_length(size_t input_length) const;

      /**
       * @returns  number of bytes needed to decrypt \p input_length bytes
       */
      size_t decrypt_output_length(size_t input_length) const;

      /**
       * @returns  the minimum ciphertext length for decryption
       */
      size_t minimum_decryption_input_length() const;

      /**
       * Calculates the MAC for a PSK binder value in Client Hellos. Note that
       * the transcript hash passed into this method is computed from a partial
       * Client Hello (RFC 8446 4.2.11.2)
       */
      std::vector<uint8_t> psk_binder_mac(const Transcript_Hash& transcript_hash_with_truncated_client_hello) const;

      /**
       * Calculate the MAC for a TLS "Finished" handshake message (RFC 8446 4.4.4)
       */
      std::vector<uint8_t> finished_mac(const Transcript_Hash& transcript_hash) const;

      /**
       * Validate a MAC received in a TLS "Finished" handshake message (RFC 8446 4.4.4)
       */
      bool verify_peer_finished_mac(const Transcript_Hash& transcript_hash, const std::vector<uint8_t>& peer_mac) const;

      /**
       * Calculate the PSK for the given nonce (RFC 8446 4.6.1)
       */
      secure_vector<uint8_t> psk(const Ticket_Nonce& nonce) const;

      /**
       * Generates a nonce value that is unique for any given Cipher_State object.
       * Note that the number of nonces is limited to 2^16 and this method will
       * throw if more nonces are requested.
       */
      Ticket_Nonce next_ticket_nonce();

      /**
       * Derive key material to export (RFC 8446 7.5 and RFC 5705)
       *
       * TODO: this does not yet support key export based on the `early_exporter_master_secret`.
       *
       * RFC 8446 7.5
       *    Implementations MUST use the exporter_master_secret unless explicitly
       *    specified by the application. The early_exporter_master_secret is
       *    defined for use in settings where an exporter is needed for 0-RTT data.
       *    A separate interface for the early exporter is RECOMMENDED [...].
       *
       * @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
       */
      secure_vector<uint8_t> export_key(std::string_view label, std::string_view context, size_t length) const;

      /**
       * Indicates whether the appropriate secrets to export keys are available
       */
      bool can_export_keys() const {
         return (m_state == State::EarlyTraffic || m_state == State::ServerApplicationTraffic ||
                 m_state == State::Completed) &&
                !m_exporter_master_secret.empty();
      }

      /**
       * Indicates whether unprotected Alert records are to be expected
       */
      bool must_expect_unprotected_alert_traffic() const;

      /**
       * Indicates whether the appropriate secrets to encrypt application traffic are available
       */
      bool can_encrypt_application_traffic() const;

      /**
       * Indicates whether the appropriate secrets to decrypt application traffic are available
       */
      bool can_decrypt_application_traffic() const;

      /**
       * The name of the hash algorithm used for the KDF in this cipher suite
       */
      std::string hash_algorithm() const;

      /**
       * @returns  true if the selected cipher primitives are compatible with
       *           the \p cipher suite.
       *
       * Note that cipher suites are considered "compatible" as long as the
       * already selected cipher primitives in this cipher state are compatible.
       */
      bool is_compatible_with(const Ciphersuite& cipher) const;

      /**
       * Updates the key material used for decrypting data
       * This is triggered after we received a Key_Update from the peer.
       *
       * Note that this must not be called before the connection is ready for
       * application traffic.
       */
      void update_read_keys();

      /**
       * Updates the key material used for encrypting data
       * This is triggered after we send a Key_Update to the peer.
       *
       * Note that this must not be called before the connection is ready for
       * application traffic.
       */
      void update_write_keys();

      /**
       * Remove handshake/traffic secrets for decrypting data from peer
       */
      void clear_read_keys();

      /**
       * Remove handshake/traffic secrets for encrypting data
       */
      void clear_write_keys();

   private:
      /**
       * @param whoami         whether we play the Server or Client
       * @param hash_function  the negotiated hash function to be used
       */
      Cipher_State(Connection_Side whoami, std::string_view hash_function);

      void advance_with_psk(PSK_Type type, secure_vector<uint8_t>&& psk);
      void advance_without_psk();

      void derive_write_traffic_key(const secure_vector<uint8_t>& traffic_secret,
                                    bool handshake_traffic_secret = false);
      void derive_read_traffic_key(const secure_vector<uint8_t>& traffic_secret, bool handshake_traffic_secret = false);

      /**
       * HKDF-Extract from RFC 8446 7.1
       */
      secure_vector<uint8_t> hkdf_extract(secure_vector<uint8_t>&& ikm) const;

      /**
       * HKDF-Expand-Label from RFC 8446 7.1
       */
      secure_vector<uint8_t> hkdf_expand_label(const secure_vector<uint8_t>& secret,
                                               std::string_view label,
                                               const std::vector<uint8_t>& context,
                                               size_t length) const;

      /**
       * Derive-Secret from RFC 8446 7.1
       */
      secure_vector<uint8_t> derive_secret(const secure_vector<uint8_t>& secret,
                                           std::string_view label,
                                           const Transcript_Hash& messages_hash) const;

      std::vector<uint8_t> empty_hash() const;

   private:
      enum class State {
         Uninitialized,
         PskBinder,
         EarlyTraffic,
         HandshakeTraffic,
         ServerApplicationTraffic,
         Completed
      };

   private:
      State m_state;
      Connection_Side m_connection_side;

      std::unique_ptr<AEAD_Mode> m_encrypt;
      std::unique_ptr<AEAD_Mode> m_decrypt;

      std::unique_ptr<HKDF_Extract> m_extract;
      std::unique_ptr<HKDF_Expand> m_expand;
      std::unique_ptr<HashFunction> m_hash;

      secure_vector<uint8_t> m_salt;

      secure_vector<uint8_t> m_write_application_traffic_secret;
      secure_vector<uint8_t> m_read_application_traffic_secret;

      secure_vector<uint8_t> m_write_key;
      secure_vector<uint8_t> m_write_iv;
      secure_vector<uint8_t> m_read_key;
      secure_vector<uint8_t> m_read_iv;

      uint64_t m_write_seq_no;
      uint64_t m_read_seq_no;

      uint16_t m_ticket_nonce;

      secure_vector<uint8_t> m_finished_key;
      secure_vector<uint8_t> m_peer_finished_key;
      secure_vector<uint8_t> m_exporter_master_secret;
      secure_vector<uint8_t> m_resumption_master_secret;

      secure_vector<uint8_t> m_early_secret;
      secure_vector<uint8_t> m_binder_key;
};

}  // namespace Botan::TLS

#endif  // BOTAN_TLS_CIPHER_STATE_H_
