Botan 3.13.0
Crypto and TLS for C&
Botan::TLS::Datagram_Handshake_IO Class Referencefinal

#include <tls_handshake_io.h>

Inheritance diagram for Botan::TLS::Datagram_Handshake_IO:
Botan::TLS::Handshake_IO

Public Types

using steady_clock_fn = std::function<uint64_t()>
using writer_fn = std::function<void(uint16_t, Record_Type, const std::vector<uint8_t>&)>

Public Member Functions

void add_record (const uint8_t record[], size_t record_len, Record_Type type, uint64_t sequence_number) override
void add_retransmitted_record (const uint8_t record[], size_t record_len, Record_Type type, uint64_t sequence_number)
 Datagram_Handshake_IO (writer_fn writer, steady_clock_fn clock_ms, class Connection_Sequence_Numbers &seq, uint16_t mtu, uint64_t initial_timeout_ms, uint64_t max_timeout_ms, std::optional< size_t > max_retransmissions, size_t max_handshake_msg_size, uint16_t initial_epoch=0)
void finalize_handshake (bool retransmit_terminal_flight)
std::vector< uint8_t > format (const std::vector< uint8_t > &handshake_msg, Handshake_Type handshake_type) const override
std::pair< Handshake_Type, std::vector< uint8_t > > get_next_record (bool expecting_ccs, size_t max_message_size) override
bool have_more_data () const override
Protocol_Version initial_record_version () const override
std::optional< std::chrono::milliseconds > next_retransmission_timeout () const override
std::vector< uint8_t > send (const Handshake_Message &msg) override
std::vector< uint8_t > send_under_epoch (const Handshake_Message &msg, uint16_t epoch) override
bool timeout_check () override

Detailed Description

Handshake IO for datagram-based handshakes

Definition at line 128 of file tls_handshake_io.h.

Member Typedef Documentation

◆ steady_clock_fn

using Botan::TLS::Datagram_Handshake_IO::steady_clock_fn = std::function<uint64_t()>

Definition at line 133 of file tls_handshake_io.h.

◆ writer_fn

using Botan::TLS::Datagram_Handshake_IO::writer_fn = std::function<void(uint16_t, Record_Type, const std::vector<uint8_t>&)>

Definition at line 130 of file tls_handshake_io.h.

Constructor & Destructor Documentation

◆ Datagram_Handshake_IO()

Botan::TLS::Datagram_Handshake_IO::Datagram_Handshake_IO ( writer_fn writer,
steady_clock_fn clock_ms,
class Connection_Sequence_Numbers & seq,
uint16_t mtu,
uint64_t initial_timeout_ms,
uint64_t max_timeout_ms,
std::optional< size_t > max_retransmissions,
size_t max_handshake_msg_size,
uint16_t initial_epoch = 0 )

Definition at line 193 of file tls_handshake_io.cpp.

201 :
202 m_seqs(seq),
203 m_flights(1),
204 m_flight_ccs(1),
205 m_initial_timeout(initial_timeout_ms),
206 m_max_timeout(max_timeout_ms),
207 m_max_retransmissions(max_retransmissions),
208 m_initial_epoch(initial_epoch),
209 m_last_delivered_epoch(initial_epoch),
210 m_send_hs(std::move(writer)),
211 m_steady_clock_ms(std::move(steady_clock_ms)),
212 m_mtu(mtu),
213 m_max_handshake_msg_size(max_handshake_msg_size),
214 m_max_pending_reassembly(max_pending_reassembly(m_max_handshake_msg_size)) {}

Member Function Documentation

◆ add_record()

void Botan::TLS::Datagram_Handshake_IO::add_record ( const uint8_t record[],
size_t record_len,
Record_Type type,
uint64_t sequence_number )
overridevirtual

Implements Botan::TLS::Handshake_IO.

Definition at line 374 of file tls_handshake_io.cpp.

377 {
378 add_record(record, record_len, record_type, record_sequence, false);
379}
void add_record(const uint8_t record[], size_t record_len, Record_Type type, uint64_t sequence_number) override

References add_record().

Referenced by add_record(), and add_retransmitted_record().

◆ add_retransmitted_record()

void Botan::TLS::Datagram_Handshake_IO::add_retransmitted_record ( const uint8_t record[],
size_t record_len,
Record_Type type,
uint64_t sequence_number )

Definition at line 381 of file tls_handshake_io.cpp.

384 {
385 add_record(record, record_len, record_type, record_sequence, true);
386}

References add_record().

◆ finalize_handshake()

void Botan::TLS::Datagram_Handshake_IO::finalize_handshake ( bool retransmit_terminal_flight)

Enter FINISHED after channel activation. Intermediate outgoing flights are finalized implicitly when the peer's next handshake message is requested. The terminal-flight sender retains reactive replay behavior.

Definition at line 306 of file tls_handshake_io.cpp.

306 {
307 // Keep an empty trailing flight to mean "we are waiting for the peer".
308 // Retransmission then replays the previous, completed flight instead of
309 // appending to it.
310 if(!m_flights.rbegin()->empty()) {
311 m_flights.emplace_back();
312 m_flight_ccs.emplace_back();
313 }
314
315 // RFC 6347 4.2.4: "Once the messages have been sent, the implementation
316 // then enters the FINISHED state if this is the last flight in the
317 // handshake." Keep the flight for reactive replay when the peer
318 // retransmits, but do not arm a proactive retransmission timer.
319 m_finished = true;
320 m_retransmit_terminal_flight = retransmit_terminal_flight;
321}

◆ format()

std::vector< uint8_t > Botan::TLS::Datagram_Handshake_IO::format ( const std::vector< uint8_t > & handshake_msg,
Handshake_Type handshake_type ) const
overridevirtual

Implements Botan::TLS::Handshake_IO.

Definition at line 887 of file tls_handshake_io.cpp.

887 {
888 // Formats the message just delivered, so the guard is that one exists, not
889 // that the counter is non-zero. Those differ once m_in_message_seq wraps,
890 // where the subtraction wraps to 65535 of its own accord, which is the
891 // right sequence number for that message.
892 BOTAN_ASSERT_NOMSG(m_first_delivered_epoch.has_value());
893 return format_w_seq(msg, type, static_cast<uint16_t>(m_in_message_seq - 1));
894}
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75

References BOTAN_ASSERT_NOMSG.

◆ get_next_record()

std::pair< Handshake_Type, std::vector< uint8_t > > Botan::TLS::Datagram_Handshake_IO::get_next_record ( bool expecting_ccs,
size_t max_message_size )
overridevirtual

Returns (HANDSHAKE_NONE, std::vector<>()) if no message currently available

Implements Botan::TLS::Handshake_IO.

Definition at line 734 of file tls_handshake_io.cpp.

735 {
736 // Expecting a message means the last flight is concluded
737 if(!m_flights.rbegin()->empty()) {
738 m_flights.emplace_back();
739 m_flight_ccs.emplace_back();
740 }
741
742 if(expecting_ccs) {
743 // CCS is expected under the epoch the peer's handshake messages have
744 // been arriving on, and always follows at least one delivered message.
745 if(m_first_delivered_epoch.has_value() && m_ccs_epochs.contains(*m_first_delivered_epoch)) {
746 return std::make_pair(Handshake_Type::HandshakeCCS, std::vector<uint8_t>());
747 }
748 return std::make_pair(Handshake_Type::None, std::vector<uint8_t>());
749 }
750
751 if(m_retransmitted_client_hello.has_value() && m_retransmitted_client_hello->second.complete()) {
752 auto result = m_retransmitted_client_hello->second.message();
753 release_reassembly_bytes(m_retransmitted_client_hello->second);
754 m_retransmitted_client_hello.reset();
755 m_recreating_hello_verify_request = true;
756 return result;
757 }
758
759 auto i = m_messages.find(m_in_message_seq);
760
761 if(i == m_messages.end() || !i->second.complete()) {
762 return std::make_pair(Handshake_Type::None, std::vector<uint8_t>());
763 }
764
765 m_in_message_seq += 1;
766 if(m_in_message_seq == 0) {
767 m_in_message_seq_wrapped = true;
768 }
769
770 if(!m_first_delivered_epoch.has_value()) {
771 m_first_delivered_epoch = i->second.epoch();
772 }
773
774 auto result = i->second.message();
775
776 if(result.first == Handshake_Type::ClientHello) {
777 m_awaiting_cookie_client_hello = false;
778 }
779
780 const uint16_t delivered_epoch = i->second.epoch();
781
782 release_reassembly_bytes(i->second);
783 m_messages.erase(i);
784
785 if(delivered_epoch > m_last_delivered_epoch) {
786 m_last_delivered_epoch = delivered_epoch;
787 discard_stale_epoch_messages();
788 }
789
790 return result;
791}

References Botan::TLS::ClientHello, Botan::TLS::HandshakeCCS, and Botan::TLS::None.

◆ have_more_data()

bool Botan::TLS::Datagram_Handshake_IO::have_more_data ( ) const
overridevirtual

Implements Botan::TLS::Handshake_IO.

Definition at line 299 of file tls_handshake_io.cpp.

299 {
300 // Future or incomplete fragments remain buffered, but only a complete
301 // next-in-sequence message is trailing handshake data.
302 const auto next = m_messages.find(m_in_message_seq);
303 return next != m_messages.end() && next->second.complete();
304}

◆ initial_record_version()

Protocol_Version Botan::TLS::Datagram_Handshake_IO::initial_record_version ( ) const
overridevirtual

Implements Botan::TLS::Handshake_IO.

Definition at line 216 of file tls_handshake_io.cpp.

216 {
217 return Protocol_Version::DTLS_V12;
218}

◆ next_retransmission_timeout()

std::optional< std::chrono::milliseconds > Botan::TLS::Datagram_Handshake_IO::next_retransmission_timeout ( ) const
overridevirtual

Implements Botan::TLS::Handshake_IO.

Definition at line 348 of file tls_handshake_io.cpp.

348 {
349 if(m_finished) {
350 return std::nullopt;
351 }
352
353 // Without an outgoing flight, or while constructing one, there is nothing
354 // complete that timeout_check() could retransmit.
355 if(!m_last_write.has_value() || (m_flights.size() > 1 && !m_flights.rbegin()->empty())) {
356 return std::nullopt;
357 }
358
359 const uint64_t ms_since_write = m_steady_clock_ms() - m_last_write.value();
360 if(ms_since_write >= m_next_timeout) {
361 return std::chrono::milliseconds(0);
362 }
363
364 // BoringSSL reports a sub-15ms remainder as zero (ssl/d1_lib.cc
365 // DTLSTimer::MicrosecondsRemaining) to absorb divergence with caller
366 // scheduling; BoGo's DTLS-Retransmit-Fudge test requires it. The cap keeps
367 // the fudge from swallowing the very short timers some tests configure.
368 const uint64_t fudge_ms = std::min<uint64_t>(15, m_initial_timeout / 2);
369 const uint64_t remaining_ms = m_next_timeout - ms_since_write;
370
371 return std::chrono::milliseconds(remaining_ms <= fudge_ms ? 0 : remaining_ms);
372}

Referenced by timeout_check().

◆ send()

std::vector< uint8_t > Botan::TLS::Datagram_Handshake_IO::send ( const Handshake_Message & msg)
overridevirtual

Implements Botan::TLS::Handshake_IO.

Definition at line 896 of file tls_handshake_io.cpp.

896 {
897 return this->send_under_epoch(msg, m_seqs.current_write_epoch());
898}
std::vector< uint8_t > send_under_epoch(const Handshake_Message &msg, uint16_t epoch) override

References send_under_epoch().

◆ send_under_epoch()

std::vector< uint8_t > Botan::TLS::Datagram_Handshake_IO::send_under_epoch ( const Handshake_Message & msg,
uint16_t epoch )
overridevirtual

Implements Botan::TLS::Handshake_IO.

Definition at line 900 of file tls_handshake_io.cpp.

900 {
901 const std::vector<uint8_t> msg_bits = msg.serialize();
902 const Handshake_Type msg_type = msg.type();
903
904 if(msg_type == Handshake_Type::HandshakeCCS) {
905 m_flight_ccs.rbegin()->emplace_back(m_flights.rbegin()->size(), epoch);
906 m_send_hs(epoch, Record_Type::ChangeCipherSpec, msg_bits);
907 return {}; // not included in handshake hashes
908 } else if(msg_type == Handshake_Type::HelloVerifyRequest) {
909 // RFC 6347 3.2.1 explicitly excludes HelloVerifyRequest from timeout
910 // retransmission. A repeated ClientHello recreates the response using
911 // the original message sequence number without retaining a flight.
912 const uint16_t msg_seq = m_recreating_hello_verify_request ? m_out_message_seq - 1 : m_out_message_seq++;
913 m_awaiting_cookie_client_hello = true;
914 m_recreating_hello_verify_request = false;
915 send_message(msg_seq, epoch, msg_type, msg_bits);
916 return {};
917 }
918
919 m_flights.rbegin()->push_back(m_out_message_seq);
920 m_flight_data.insert_or_assign(m_out_message_seq, Message_Info(epoch, msg_type, msg_bits));
921
922 m_out_message_seq += 1;
923 m_last_write = m_steady_clock_ms();
924 m_next_timeout = m_initial_timeout;
925 // Sending a new flight is forward progress: reset the give-up counter so the
926 // retransmission budget applies per flight, not across the whole handshake.
927 m_retransmit_count = 0;
928 m_peer_replay_count = 0;
929
930 return send_message(m_out_message_seq - 1, epoch, msg_type, msg_bits);
931}

References Botan::TLS::ChangeCipherSpec, Botan::TLS::HandshakeCCS, Botan::TLS::HelloVerifyRequest, Botan::TLS::Handshake_Message::serialize(), and Botan::TLS::Handshake_Message::type().

Referenced by send().

◆ timeout_check()

bool Botan::TLS::Datagram_Handshake_IO::timeout_check ( )
overridevirtual

Implements Botan::TLS::Handshake_IO.

Definition at line 323 of file tls_handshake_io.cpp.

323 {
324 const auto timeout = next_retransmission_timeout();
325 if(!timeout || timeout->count() > 0) {
326 return false;
327 }
328
329 // The retransmit timer has expired. Count this attempt and, once the
330 // configured cap is reached, abandon the handshake rather than retransmit
331 // forever. RFC 6347 4.2.4.1 gives the backoff schedule but states no
332 // condition for giving up, so the cap is local policy, not a requirement.
333 // No alert is sent - the peer is by definition unresponsive.
334 m_retransmit_count += 1;
335 if(m_max_retransmissions.has_value() && m_retransmit_count > m_max_retransmissions.value()) {
336 throw TLS_Exception(Alert::None, "DTLS handshake timed out: maximum retransmissions exceeded");
337 }
338
339 // retransmit_last_flight re-anchors m_last_write. Without that, once
340 // m_next_timeout saturates at m_max_timeout the elapsed time keeps growing
341 // and every subsequent poll fires another retransmission.
342 retransmit_last_flight();
343
344 m_next_timeout = std::min(2 * m_next_timeout, m_max_timeout);
345 return true;
346}
std::optional< std::chrono::milliseconds > next_retransmission_timeout() const override

References next_retransmission_timeout().


The documentation for this class was generated from the following files: