/* * 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 #include #include #include namespace Botan { class AEAD_Mode; class HashFunction; class HKDF_Extract; class HKDF_Expand; } // namespace Botan namespace Botan::TLS { class Ciphersuite; class Secret_Logger; /** * 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 init_with_psk(Connection_Side side, PSK_Type type, secure_vector&& psk, std::string_view prf_algo); /** * Construct a Cipher_State after receiving a server hello message. */ static std::unique_ptr init_with_server_hello(Connection_Side side, secure_vector&& shared_secret, const Ciphersuite& cipher, const Transcript_Hash& transcript_hash, const Secret_Logger& channel); /** * 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, const Secret_Logger& channel); /** * Transition internal secrets/keys for transporting handshake data. */ void advance_with_server_hello(const Ciphersuite& cipher, secure_vector&& shared_secret, const Transcript_Hash& transcript_hash, const Secret_Logger& channel); /** * Transition internal secrets/keys for transporting application data. */ void advance_with_server_finished(const Transcript_Hash& transcript_hash, const Secret_Logger& channel); /** * 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& header, secure_vector& 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& header, secure_vector& 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 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 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& peer_mac) const; /** * Calculate the PSK for the given nonce (RFC 8446 4.6.1) */ secure_vector 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 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(const Secret_Logger& channel); /** * 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(const Secret_Logger& channel); /** * 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&& psk); void advance_without_psk(); void derive_write_traffic_key(const secure_vector& traffic_secret, bool handshake_traffic_secret = false); void derive_read_traffic_key(const secure_vector& traffic_secret, bool handshake_traffic_secret = false); /** * HKDF-Extract from RFC 8446 7.1 */ secure_vector hkdf_extract(std::span ikm) const; /** * HKDF-Expand-Label from RFC 8446 7.1 */ secure_vector hkdf_expand_label(const secure_vector& secret, std::string_view label, const std::vector& context, size_t length) const; /** * Derive-Secret from RFC 8446 7.1 */ secure_vector derive_secret(const secure_vector& secret, std::string_view label, const Transcript_Hash& messages_hash) const; std::vector 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 m_encrypt; std::unique_ptr m_decrypt; std::unique_ptr m_extract; std::unique_ptr m_expand; std::unique_ptr m_hash; secure_vector m_salt; secure_vector m_write_application_traffic_secret; secure_vector m_read_application_traffic_secret; secure_vector m_write_key; secure_vector m_write_iv; secure_vector m_read_key; secure_vector m_read_iv; uint64_t m_write_seq_no; uint64_t m_read_seq_no; uint32_t m_write_key_update_count; uint32_t m_read_key_update_count; uint16_t m_ticket_nonce; secure_vector m_finished_key; secure_vector m_peer_finished_key; secure_vector m_exporter_master_secret; secure_vector m_resumption_master_secret; secure_vector m_early_secret; secure_vector m_binder_key; }; } // namespace Botan::TLS #endif // BOTAN_TLS_CIPHER_STATE_H_