/* * TLS Hello Request and Client Hello Messages * (C) 2004-2011,2015,2016 Jack Lloyd * 2016 Matthias Gierlings * 2017 Harry Reimann, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include #include #include #include #include #include #include #include namespace Botan::TLS { void Client_Hello_12::update_hello_cookie(const Hello_Verify_Request& hello_verify) { BOTAN_STATE_CHECK(m_data->legacy_version().is_datagram_protocol()); m_data->m_hello_cookie = hello_verify.cookie(); } bool Client_Hello_12::prefers_compressed_ec_points() const { if(const Supported_Point_Formats* ecc_formats = m_data->extensions().get()) { return ecc_formats->prefers_compressed(); } return false; } bool Client_Hello_12::secure_renegotiation() const { return m_data->extensions().has(); } std::vector Client_Hello_12::renegotiation_info() const { if(const Renegotiation_Extension* reneg = m_data->extensions().get()) { return reneg->renegotiation_info(); } return {}; } bool Client_Hello_12::supports_session_ticket() const { return m_data->extensions().has(); } Session_Ticket Client_Hello_12::session_ticket() const { if(auto* ticket = m_data->extensions().get()) { return ticket->contents(); } return {}; } std::optional Client_Hello_12::session_handle() const { // RFC 5077 3.4 // If a ticket is presented by the client, the server MUST NOT attempt // to use the Session ID in the ClientHello for stateful session // resumption. if(auto ticket = session_ticket(); !ticket.empty()) { return Session_Handle(ticket); } else if(const auto& id = session_id(); !id.empty()) { return Session_Handle(id); } else { return std::nullopt; } } bool Client_Hello_12::supports_extended_master_secret() const { return m_data->extensions().has(); } bool Client_Hello_12::supports_cert_status_message() const { return m_data->extensions().has(); } bool Client_Hello_12::supports_encrypt_then_mac() const { return m_data->extensions().has(); } void Client_Hello_12::add_tls12_supported_groups_extensions(const Policy& policy) { // RFC 7919 3. // A client that offers a group MUST be able and willing to perform a DH // key exchange using that group. // // We don't support hybrid key exchange in TLS 1.2 std::vector compatible_kex_groups; for(const auto& group : policy.key_exchange_groups()) { if(!group.is_post_quantum()) { compatible_kex_groups.push_back(group); } } auto supported_groups = std::make_unique(std::move(compatible_kex_groups)); if(!supported_groups->ec_groups().empty()) { // NOLINTNEXTLINE(*-owning-memory) m_data->extensions().add(new Supported_Point_Formats(policy.use_ecc_point_compression())); } m_data->extensions().add(std::move(supported_groups)); } /* * 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_data->m_legacy_version = client_settings.protocol_version(); m_data->m_random = make_hello_random(rng, cb, policy); m_data->m_suites = policy.ciphersuite_list(client_settings.protocol_version()); if(!policy.acceptable_protocol_version(m_data->legacy_version())) { throw Internal_Error("Offering " + m_data->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. */ // NOLINTBEGIN(*-owning-memory) // EMS must always be used with TLS 1.2, regardless of the policy used. m_data->extensions().add(new Extended_Master_Secret); if(policy.negotiate_encrypt_then_mac()) { m_data->extensions().add(new Encrypt_then_MAC); } m_data->extensions().add(new Session_Ticket_Extension()); m_data->extensions().add(new Renegotiation_Extension(reneg_info)); m_data->extensions().add(new Supported_Versions(m_data->legacy_version(), policy)); if(Server_Name_Indicator::hostname_acceptable_for_sni(client_settings.hostname())) { m_data->extensions().add(new Server_Name_Indicator(client_settings.hostname())); } if(policy.support_cert_status_message()) { m_data->extensions().add(new Certificate_Status_Request({}, {})); } add_tls12_supported_groups_extensions(policy); m_data->extensions().add(new Signature_Algorithms(policy.acceptable_signature_schemes())); if(auto cert_signing_prefs = policy.acceptable_certificate_signature_schemes()) { // RFC 8446 4.2.3 // TLS 1.2 implementations SHOULD also process this extension. // Implementations which have the same policy in both cases MAY omit // the "signature_algorithms_cert" extension. m_data->extensions().add(new Signature_Algorithms_Cert(std::move(cert_signing_prefs.value()))); } if(reneg_info.empty() && !next_protocols.empty()) { m_data->extensions().add(new Application_Layer_Protocol_Notification(next_protocols)); } if(m_data->legacy_version().is_datagram_protocol()) { m_data->extensions().add(new SRTP_Protection_Profiles(policy.srtp_profiles())); } // NOLINTEND(*-owning-memory) cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Client, type()); 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_with_Handle& session, const std::vector& next_protocols) { m_data->m_legacy_version = session.session.version(); m_data->m_random = make_hello_random(rng, cb, policy); // RFC 5077 3.4 // When presenting a ticket, the client MAY generate and include a // Session ID in the TLS ClientHello. [...] If a ticket is presented by // the client, the server MUST NOT attempt to use the Session ID in the // ClientHello for stateful session resumption. m_data->m_session_id = session.handle.id().value_or(Session_ID(make_hello_random(rng, cb, policy))); m_data->m_suites = policy.ciphersuite_list(m_data->legacy_version()); if(!policy.acceptable_protocol_version(session.session.version())) { throw Internal_Error("Offering " + m_data->legacy_version().to_string() + " but our own policy does not accept it"); } if(!value_exists(m_data->ciphersuites(), session.session.ciphersuite_code())) { m_data->m_suites.push_back(session.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. */ // NOLINTBEGIN(*-owning-memory) m_data->extensions().add(new Extended_Master_Secret); if(session.session.supports_encrypt_then_mac()) { m_data->extensions().add(new Encrypt_then_MAC); } if(session.handle.is_ticket()) { m_data->extensions().add(new Session_Ticket_Extension(session.handle.ticket().value())); } m_data->extensions().add(new Renegotiation_Extension(reneg_info)); const std::string hostname = session.session.server_info().hostname(); if(Server_Name_Indicator::hostname_acceptable_for_sni(hostname)) { m_data->extensions().add(new Server_Name_Indicator(hostname)); } if(policy.support_cert_status_message()) { m_data->extensions().add(new Certificate_Status_Request({}, {})); } add_tls12_supported_groups_extensions(policy); m_data->extensions().add(new Signature_Algorithms(policy.acceptable_signature_schemes())); if(auto cert_signing_prefs = policy.acceptable_certificate_signature_schemes()) { // RFC 8446 4.2.3 // TLS 1.2 implementations SHOULD also process this extension. // Implementations which have the same policy in both cases MAY omit // the "signature_algorithms_cert" extension. m_data->extensions().add(new Signature_Algorithms_Cert(std::move(cert_signing_prefs.value()))); } if(reneg_info.empty() && !next_protocols.empty()) { m_data->extensions().add(new Application_Layer_Protocol_Notification(next_protocols)); } // NOLINTEND(*-owning-memory) cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Client, type()); hash.update(io.send(*this)); } Client_Hello_12::Client_Hello_12(const std::vector& buf) : Client_Hello_12(std::make_unique(buf)) {} Client_Hello_12::Client_Hello_12(std::unique_ptr data) : Client_Hello_12_Shim(std::move(data)) { const uint16_t TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF; if(offered_suite(static_cast(TLS_EMPTY_RENEGOTIATION_INFO_SCSV))) { if(const Renegotiation_Extension* reneg = m_data->extensions().get()) { if(!reneg->renegotiation_info().empty()) { throw TLS_Exception(Alert::HandshakeFailure, "Client sent renegotiation SCSV and non-empty extension"); } } else { // add fake extension m_data->extensions().add(new Renegotiation_Extension()); // NOLINT(*-owning-memory) } } } Hello_Request::Hello_Request(Handshake_IO& io) { io.send(*this); } Hello_Request::Hello_Request(const std::vector& buf) { if(!buf.empty()) { throw Decoding_Error("Bad Hello_Request, has non-zero size"); } } std::vector Hello_Request::serialize() const { return std::vector(); } } // namespace Botan::TLS