/* * TLS Hello Request and Client Hello Messages * (C) 2004-2011,2015,2016 Jack Lloyd * 2016 Matthias Gierlings * 2017 Harry Reimann, Rohde & Schwarz Cybersecurity * 2021 Elektrobit Automotive GmbH * 2022 René Meusel, Hannes Rantzsch - neXenio GmbH * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include #include #include #include #include #include #include #include #include #include #include namespace Botan::TLS { enum { TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF, }; std::vector make_hello_random(RandomNumberGenerator& rng, Callbacks& cb, const Policy& policy) { std::vector buf(32); rng.randomize(buf.data(), buf.size()); if(policy.hash_hello_random()) { auto sha256 = HashFunction::create_or_throw("SHA-256"); sha256->update(buf); sha256->final(buf); } if(policy.include_time_in_hello_random()) { const uint32_t time32 = static_cast( std::chrono::system_clock::to_time_t(cb.tls_current_timestamp())); store_be(time32, buf.data()); } return buf; } /* * Read a counterparty client hello */ Client_Hello::Client_Hello(const std::vector& buf) { if(buf.size() < 41) { throw Decoding_Error("Client_Hello: Packet corrupted"); } TLS_Data_Reader reader("ClientHello", buf); const uint8_t major_version = reader.get_byte(); const uint8_t minor_version = reader.get_byte(); m_legacy_version = Protocol_Version(major_version, minor_version); m_random = reader.get_fixed(32); m_session_id = reader.get_range(1, 0, 32); if(m_legacy_version.is_datagram_protocol()) { auto sha256 = HashFunction::create_or_throw("SHA-256"); sha256->update(reader.get_data_read_so_far()); m_hello_cookie = reader.get_range(1, 0, 255); sha256->update(reader.get_remaining()); m_cookie_input_bits.resize(sha256->output_length()); sha256->final(m_cookie_input_bits.data()); } m_suites = reader.get_range_vector(2, 1, 32767); m_comp_methods = reader.get_range_vector(1, 1, 255); m_extensions.deserialize(reader, Connection_Side::CLIENT, type()); if(offered_suite(static_cast(TLS_EMPTY_RENEGOTIATION_INFO_SCSV))) { if(Renegotiation_Extension* reneg = m_extensions.get()) { if(!reneg->renegotiation_info().empty()) throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Client sent renegotiation SCSV and non-empty extension"); } else { // add fake extension m_extensions.add(new Renegotiation_Extension()); } } } Handshake_Type Client_Hello::type() const { return CLIENT_HELLO; } Protocol_Version Client_Hello::legacy_version() const { return m_legacy_version; } const std::vector& Client_Hello::random() const { return m_random; } const std::vector& Client_Hello::session_id() const { return m_session_id; } const std::vector& Client_Hello::compression_methods() const { return m_comp_methods; } const std::vector& Client_Hello::ciphersuites() const { return m_suites; } std::set Client_Hello::extension_types() const { return m_extensions.extension_types(); } const Extensions& Client_Hello::extensions() const { return m_extensions; } void Client_Hello_12::update_hello_cookie(const Hello_Verify_Request& hello_verify) { if(!m_legacy_version.is_datagram_protocol()) { throw Invalid_State("Cannot use hello cookie with stream protocol"); } m_hello_cookie = hello_verify.cookie(); } /* * Serialize a Client Hello message */ std::vector Client_Hello::serialize() const { std::vector buf; buf.push_back(m_legacy_version.major_version()); buf.push_back(m_legacy_version.minor_version()); buf += m_random; append_tls_length_value(buf, m_session_id, 1); if(m_legacy_version.is_datagram_protocol()) { append_tls_length_value(buf, m_hello_cookie, 1); } append_tls_length_value(buf, m_suites, 2); append_tls_length_value(buf, m_comp_methods, 1); /* * May not want to send extensions at all in some cases. If so, * should include SCSV value (if reneg info is empty, if not we are * renegotiating with a modern server) */ buf += m_extensions.serialize(Connection_Side::CLIENT); return buf; } std::vector Client_Hello::cookie_input_data() const { if(m_cookie_input_bits.empty()) { throw Invalid_State("Client_Hello::cookie_input_data called but was not computed"); } return m_cookie_input_bits; } /* * Check if we offered this ciphersuite */ bool Client_Hello::offered_suite(uint16_t ciphersuite) const { return std::find(m_suites.cbegin(), m_suites.cend(), ciphersuite) != m_suites.cend(); } std::vector Client_Hello::signature_schemes() const { std::vector schemes; if(Signature_Algorithms* sigs = m_extensions.get()) { schemes = sigs->supported_schemes(); } return schemes; } std::vector Client_Hello::supported_ecc_curves() const { if(Supported_Groups* groups = m_extensions.get()) { return groups->ec_groups(); } return std::vector(); } std::vector Client_Hello::supported_dh_groups() const { if(Supported_Groups* groups = m_extensions.get()) { return groups->dh_groups(); } return std::vector(); } bool Client_Hello_12::prefers_compressed_ec_points() const { if(Supported_Point_Formats* ecc_formats = m_extensions.get()) { return ecc_formats->prefers_compressed(); } return false; } std::string Client_Hello::sni_hostname() const { if(Server_Name_Indicator* sni = m_extensions.get()) { return sni->host_name(); } return ""; } bool Client_Hello_12::secure_renegotiation() const { return m_extensions.has(); } std::vector Client_Hello_12::renegotiation_info() const { if(Renegotiation_Extension* reneg = m_extensions.get()) { return reneg->renegotiation_info(); } return std::vector(); } std::vector Client_Hello::supported_versions() const { if(Supported_Versions* versions = m_extensions.get()) { return versions->versions(); } return {}; } bool Client_Hello_12::supports_session_ticket() const { return m_extensions.has(); } std::vector Client_Hello_12::session_ticket() const { if(Session_Ticket* ticket = m_extensions.get()) { return ticket->contents(); } return std::vector(); } bool Client_Hello::supports_alpn() const { return m_extensions.has(); } bool Client_Hello_12::supports_extended_master_secret() const { return m_extensions.has(); } bool Client_Hello_12::supports_cert_status_message() const { return m_extensions.has(); } bool Client_Hello_12::supports_encrypt_then_mac() const { return m_extensions.has(); } bool Client_Hello::sent_signature_algorithms() const { return m_extensions.has(); } std::vector Client_Hello::next_protocols() const { if(auto alpn = m_extensions.get()) { return alpn->protocols(); } return std::vector(); } std::vector Client_Hello::srtp_profiles() const { if(SRTP_Protection_Profiles* srtp = m_extensions.get()) { return srtp->profiles(); } return std::vector(); } const std::vector& Client_Hello::cookie() const { return m_hello_cookie; } /* * Create a new Hello Request message */ Hello_Request::Hello_Request(Handshake_IO& io) { io.send(*this); } /* * Deserialize a Hello Request message */ Hello_Request::Hello_Request(const std::vector& buf) { if(!buf.empty()) { throw Decoding_Error("Bad Hello_Request, has non-zero size"); } } /* * Serialize a Hello Request message */ std::vector Hello_Request::serialize() const { return std::vector(); } /* * Create a new Client Hello message */ Client_Hello_12::Client_Hello_12(Handshake_IO& io, Handshake_Hash& hash, const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng, const std::vector& reneg_info, const Client_Hello_12::Settings& client_settings, const std::vector& next_protocols) { m_legacy_version = client_settings.protocol_version(); m_random = make_hello_random(rng, cb, policy); m_suites = policy.ciphersuite_list(client_settings.protocol_version()); if(!policy.acceptable_protocol_version(m_legacy_version)) throw Internal_Error("Offering " + m_legacy_version.to_string() + " but our own policy does not accept it"); /* * Place all empty extensions in front to avoid a bug in some systems * which reject hellos when the last extension in the list is empty. */ // EMS must always be used with TLS 1.2, regardless of the policy used. m_extensions.add(new Extended_Master_Secret); if(policy.negotiate_encrypt_then_mac()) { m_extensions.add(new Encrypt_then_MAC); } m_extensions.add(new Session_Ticket()); m_extensions.add(new Renegotiation_Extension(reneg_info)); m_extensions.add(new Supported_Versions(m_legacy_version, policy)); if(!client_settings.hostname().empty()) { m_extensions.add(new Server_Name_Indicator(client_settings.hostname())); } if(policy.support_cert_status_message()) m_extensions.add(new Certificate_Status_Request({}, {})); auto supported_groups = std::make_unique(policy.key_exchange_groups()); if(!supported_groups->ec_groups().empty()) { m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression())); } m_extensions.add(supported_groups.release()); m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); if(reneg_info.empty() && !next_protocols.empty()) { m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); } if(m_legacy_version.is_datagram_protocol()) { m_extensions.add(new SRTP_Protection_Profiles(policy.srtp_profiles())); } cb.tls_modify_extensions(m_extensions, CLIENT); hash.update(io.send(*this)); } /* * Create a new Client Hello message (session resumption case) */ Client_Hello_12::Client_Hello_12(Handshake_IO& io, Handshake_Hash& hash, const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng, const std::vector& reneg_info, const Session& session, const std::vector& next_protocols) { m_legacy_version = session.version(); m_random = make_hello_random(rng, cb, policy); m_session_id = session.session_id(); m_suites = policy.ciphersuite_list(m_legacy_version); if(!policy.acceptable_protocol_version(session.version())) throw Internal_Error("Offering " + m_legacy_version.to_string() + " but our own policy does not accept it"); if(!value_exists(m_suites, session.ciphersuite_code())) { m_suites.push_back(session.ciphersuite_code()); } /* * As EMS must always be used with TLS 1.2, add it even if it wasn't used * in the original session. If the server understands it and follows the * RFC it should reject our resume attempt and upgrade us to a new session * with the EMS protection. */ m_extensions.add(new Extended_Master_Secret); if(session.supports_encrypt_then_mac()) { m_extensions.add(new Encrypt_then_MAC); } m_extensions.add(new Session_Ticket(session.session_ticket())); m_extensions.add(new Renegotiation_Extension(reneg_info)); m_extensions.add(new Server_Name_Indicator(session.server_info().hostname())); if(policy.support_cert_status_message()) m_extensions.add(new Certificate_Status_Request({}, {})); auto supported_groups = std::make_unique(policy.key_exchange_groups()); if(!supported_groups->ec_groups().empty()) { m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression())); } m_extensions.add(supported_groups.release()); m_extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); if(reneg_info.empty() && !next_protocols.empty()) { m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); } cb.tls_modify_extensions(m_extensions, CLIENT); hash.update(io.send(*this)); } }