Botan 3.13.0
Crypto and TLS for C&
tls_handshake_io.h
Go to the documentation of this file.
1/*
2* TLS Handshake Serialization
3* (C) 2012,2014 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#ifndef BOTAN_TLS_HANDSHAKE_IO_H_
9#define BOTAN_TLS_HANDSHAKE_IO_H_
10
11#include <botan/tls_magic.h>
12#include <botan/tls_version.h>
13#include <chrono>
14#include <deque>
15#include <functional>
16#include <map>
17#include <optional>
18#include <set>
19#include <utility>
20#include <vector>
21
22namespace Botan::TLS {
23
25
26/**
27* Handshake IO Interface
28*
29* This interface abstracts over stream and datagram processing of handshake
30* messages. It receives individual records from the channel via `add_record` and provides a
31* sending interface via a callback function provided by the channel.
32*
33* Handshake message headers are parsed and removed in `get_next_record`. The
34* result is provided back to the channel via
35* `Handshake_State::get_next_handshake_msg`.
36*
37* `send` is used by individual handshake message implementations, which send
38* themselves, as well as both client and server to dispatch CCS messaged (and
39* Hello_Verify_Request in the server case). Before calling the `writer_fn`,
40* `format` is called to add the handshake message header (except for CCS).
41*
42* The buffer returned by `send` is used to update the transcript record hash
43* (where desired).
44*/
46 public:
48
49 virtual std::vector<uint8_t> send(const Handshake_Message& msg) = 0;
50
51 virtual std::vector<uint8_t> send_under_epoch(const Handshake_Message& msg, uint16_t epoch) = 0;
52
53#if defined(BOTAN_HAS_TLS_DOWNGRADE_SUPPORT)
54 virtual std::vector<uint8_t> start_with_client_hello_from_downgrade(const Handshake_Message& client_hello) = 0;
55#endif
56
57 virtual bool timeout_check() = 0;
58
59 virtual std::optional<std::chrono::milliseconds> next_retransmission_timeout() const = 0;
60
61 virtual bool have_more_data() const = 0;
62
63 virtual std::vector<uint8_t> format(const std::vector<uint8_t>& handshake_msg,
64 Handshake_Type handshake_type) const = 0;
65
66 virtual void add_record(const uint8_t record[],
67 size_t record_len,
68 Record_Type type,
69 uint64_t sequence_number) = 0;
70
71 /**
72 * Returns (HANDSHAKE_NONE, std::vector<>()) if no message currently available
73 */
74 virtual std::pair<Handshake_Type, std::vector<uint8_t>> get_next_record(bool expecting_ccs,
75 size_t max_message_size) = 0;
76
77 Handshake_IO() = default;
78
79 Handshake_IO(const Handshake_IO&) = delete;
83
84 virtual ~Handshake_IO() = default;
85};
86
87/**
88* Handshake IO for stream-based handshakes
89*/
90class Stream_Handshake_IO final : public Handshake_IO {
91 public:
92 typedef std::function<void(Record_Type, const std::vector<uint8_t>&)> writer_fn;
93
94 explicit Stream_Handshake_IO(writer_fn writer) : m_send_hs(std::move(writer)) {}
95
97
98 bool timeout_check() override { return false; }
99
100 std::optional<std::chrono::milliseconds> next_retransmission_timeout() const override { return std::nullopt; }
101
102 bool have_more_data() const override { return !m_queue.empty(); }
103
104 std::vector<uint8_t> send(const Handshake_Message& msg) override;
105
106 std::vector<uint8_t> send_under_epoch(const Handshake_Message& msg, uint16_t epoch) override;
107
108#if defined(BOTAN_HAS_TLS_DOWNGRADE_SUPPORT)
109 std::vector<uint8_t> start_with_client_hello_from_downgrade(const Handshake_Message& client_hello) override;
110#endif
111
112 std::vector<uint8_t> format(const std::vector<uint8_t>& handshake_msg,
113 Handshake_Type handshake_type) const override;
114
115 void add_record(const uint8_t record[], size_t record_len, Record_Type type, uint64_t sequence_number) override;
116
117 std::pair<Handshake_Type, std::vector<uint8_t>> get_next_record(bool expecting_ccs,
118 size_t max_message_size) override;
119
120 private:
121 std::deque<uint8_t> m_queue;
122 writer_fn m_send_hs;
123};
124
125/**
126* Handshake IO for datagram-based handshakes
127*/
129 public:
130 using writer_fn = std::function<void(uint16_t, Record_Type, const std::vector<uint8_t>&)>;
131
132 // Lambda pointing to clock function (normally TLS::Callbacks::tls_current_monotonic_clock_ms)
133 using steady_clock_fn = std::function<uint64_t()>;
134
136 steady_clock_fn clock_ms,
138 uint16_t mtu,
139 uint64_t initial_timeout_ms,
140 uint64_t max_timeout_ms,
141 std::optional<size_t> max_retransmissions,
142 size_t max_handshake_msg_size,
143 uint16_t initial_epoch = 0);
144
146
147 bool timeout_check() override;
148
149 std::optional<std::chrono::milliseconds> next_retransmission_timeout() const override;
150
151 bool have_more_data() const override;
152
153 std::vector<uint8_t> send(const Handshake_Message& msg) override;
154
155 std::vector<uint8_t> send_under_epoch(const Handshake_Message& msg, uint16_t epoch) override;
156
157#if defined(BOTAN_HAS_TLS_DOWNGRADE_SUPPORT)
158 std::vector<uint8_t> start_with_client_hello_from_downgrade(const Handshake_Message& client_hello) override;
159#endif
160
161 std::vector<uint8_t> format(const std::vector<uint8_t>& handshake_msg,
162 Handshake_Type handshake_type) const override;
163
164 void add_record(const uint8_t record[], size_t record_len, Record_Type type, uint64_t sequence_number) override;
165
166 // Process previous-flight records after this IO object was retained by
167 // an active DTLS association for final-flight recovery.
168 void add_retransmitted_record(const uint8_t record[],
169 size_t record_len,
170 Record_Type type,
171 uint64_t sequence_number);
172
173 std::pair<Handshake_Type, std::vector<uint8_t>> get_next_record(bool expecting_ccs,
174 size_t max_message_size) override;
175
176 /**
177 * Enter FINISHED after channel activation. Intermediate outgoing flights
178 * are finalized implicitly when the peer's next handshake message is
179 * requested. The terminal-flight sender retains reactive replay behavior.
180 */
181 void finalize_handshake(bool retransmit_terminal_flight);
182
183 private:
184 void add_record(const uint8_t record[],
185 size_t record_len,
186 Record_Type record_type,
187 uint64_t record_sequence,
188 bool retransmitted_flight);
189
190 bool reassemble_retransmitted_fragment(const uint8_t fragment[],
191 size_t fragment_length,
192 size_t fragment_offset,
193 uint16_t epoch,
194 Handshake_Type msg_type,
195 size_t msg_length,
196 uint16_t message_seq);
197
198 bool process_previous_handshake_fragment(const uint8_t fragment[],
199 size_t fragment_length,
200 size_t fragment_offset,
201 uint16_t epoch,
202 Handshake_Type msg_type,
203 size_t msg_length,
204 uint16_t message_seq,
205 bool retransmitted_flight);
206
207 // Drop buffered messages left over from an earlier handshake, identified
208 // by an epoch below the most recently delivered one.
209 void discard_stale_epoch_messages();
210
211 void retransmit_flight(size_t flight);
212 void retransmit_last_flight();
213 void replay_last_flight_for_peer();
214
215 // Index of the last completed outgoing flight, or nullopt when no
216 // flight has been sent yet.
217 std::optional<size_t> last_completed_flight_index() const;
218
219 std::vector<uint8_t> format_fragment(const uint8_t fragment[],
220 size_t fragment_len,
221 uint32_t frag_offset,
222 uint32_t msg_len,
223 Handshake_Type type,
224 uint16_t msg_sequence) const;
225
226 std::vector<uint8_t> format_w_seq(const std::vector<uint8_t>& handshake_msg,
227 Handshake_Type handshake_type,
228 uint16_t msg_sequence) const;
229
230 std::vector<uint8_t> send_message(uint16_t msg_seq,
231 uint16_t epoch,
232 Handshake_Type msg_type,
233 const std::vector<uint8_t>& msg);
234
235 class Handshake_Reassembly final {
236 public:
237 void add_fragment(const uint8_t fragment[],
238 size_t fragment_length,
239 size_t fragment_offset,
240 uint16_t epoch,
241 Handshake_Type msg_type,
242 size_t msg_length);
243
244 // Set by the first fragment, which fixes the header metadata and
245 // (in Datagram_Handshake_IO) charges the reassembly budget.
246 bool initialized() const { return m_msg_type != Handshake_Type::None; }
247
248 bool complete() const;
249
250 uint16_t epoch() const { return m_epoch; }
251
252 // 0 until the first fragment has set the declared msg_length.
253 size_t msg_length() const { return m_msg_length; }
254
255 std::pair<Handshake_Type, std::vector<uint8_t>> message() const;
256
257 private:
258 Handshake_Type m_msg_type = Handshake_Type::None;
259 size_t m_msg_length = 0;
260 size_t m_bytes_received = 0;
261 uint16_t m_epoch = 0;
262
263 // Reassembly buffer (sized to m_msg_length once known) and a parallel
264 // byte-mask marking which positions have already been seen.
265 std::vector<uint8_t> m_received_mask;
266 std::vector<uint8_t> m_message;
267 };
268
269 // Add a fragment to a reassembly slot, charging the slot's declared
270 // length against the pending-reassembly budget at its first fragment.
271 // Returns false, adding nothing, if the budget ceiling would be exceeded.
272 bool charged_add_fragment(Handshake_Reassembly& reassembly,
273 size_t ceiling,
274 const uint8_t fragment[],
275 size_t fragment_length,
276 size_t fragment_offset,
277 uint16_t epoch,
278 Handshake_Type msg_type,
279 size_t msg_length);
280
281 // Uncommit a reassembly buffer's bytes from the pending-reassembly budget.
282 void release_reassembly_bytes(const Handshake_Reassembly& reassembly);
283
284 struct Message_Info final {
285 Message_Info(uint16_t e, Handshake_Type mt, const std::vector<uint8_t>& msg) :
286 epoch(e), msg_type(mt), msg_bits(msg) {}
287
288 uint16_t epoch; // NOLINT(*non-private-member-variable*)
289 Handshake_Type msg_type; // NOLINT(*non-private-member-variable*)
290 std::vector<uint8_t> msg_bits; // NOLINT(*non-private-member-variable*)
291 };
292
293 class Connection_Sequence_Numbers& m_seqs;
294 std::map<uint16_t, Handshake_Reassembly> m_messages;
295 size_t m_pending_reassembly_bytes = 0;
296 std::set<uint16_t> m_ccs_epochs;
297
298 // A retransmitted final flight may deliver CCS and Finished in either
299 // order. Other terminal messages may themselves be fragmented.
300 std::optional<uint16_t> m_retransmitted_ccs_epoch;
301 std::optional<uint16_t> m_retransmitted_finished_epoch;
302 std::map<Handshake_Type, std::pair<uint16_t, Handshake_Reassembly>> m_retransmitted_messages;
303 std::vector<std::vector<uint16_t>> m_flights;
304 // Each entry records where in the corresponding flight a CCS was sent
305 // and the epoch under which it was transmitted.
306 std::vector<std::vector<std::pair<size_t, uint16_t>>> m_flight_ccs;
307 std::map<uint16_t, Message_Info> m_flight_data;
308
309 std::optional<std::pair<uint16_t, Handshake_Reassembly>> m_retransmitted_client_hello;
310 bool m_awaiting_cookie_client_hello = false;
311 bool m_recreating_hello_verify_request = false;
312 bool m_finished = false;
313 bool m_retransmit_terminal_flight = false;
314
315 uint64_t m_initial_timeout = 0;
316 uint64_t m_max_timeout = 0;
317
318 // Maximum timer-driven retransmissions of the current flight before the
319 // handshake is abandoned (nullopt unlimited). m_retransmit_count tracks the
320 // number fired for the in-flight wait; it resets to 0 whenever a new
321 // flight is sent (forward progress) and is incremented on each timeout.
322 std::optional<size_t> m_max_retransmissions = 0;
323 size_t m_retransmit_count = 0;
324
325 // Time the current flight was last written, unset until one has been.
326 // A caller's clock may legitimately read zero, so the absence of a write
327 // cannot be spelled as a reserved timestamp.
328 std::optional<uint64_t> m_last_write;
329
330 // Flight replays the peer has cued since the current flight was sent.
331 // Kept apart from the timer's own budget because the cue for it is
332 // unauthenticated. See replay_last_flight_for_peer.
333 size_t m_peer_replay_count = 0;
334
335 uint64_t m_next_timeout = 0;
336
337 uint16_t m_in_message_seq = 0;
338 uint16_t m_out_message_seq = 0;
339
340 // Epoch of the first delivered incoming message; its presence also
341 // guards format(), which is not the same as m_in_message_seq being
342 // non-zero once that counter wraps.
343 std::optional<uint16_t> m_first_delivered_epoch;
344
345 // Set once m_in_message_seq wraps, after which all handshake input is
346 // refused; see process_handshake_fragment.
347 bool m_in_message_seq_wrapped = false;
348
349 // Epoch in force when this handshake began. Records from the previous one
350 // sit at or below it, and a Finished has to sit above it.
351 uint16_t m_initial_epoch;
352
353 // Epoch of the most recently delivered incoming handshake message. Used
354 // to reject records held over from a previous handshake.
355 uint16_t m_last_delivered_epoch = 0;
356
357 writer_fn m_send_hs;
358 steady_clock_fn m_steady_clock_ms;
359 uint16_t m_mtu;
360 size_t m_max_handshake_msg_size;
361 size_t m_max_pending_reassembly;
362};
363
364} // namespace Botan::TLS
365
366#endif
#define BOTAN_TEST_API
Definition api.h:41
std::function< uint64_t()> steady_clock_fn
std::function< void(uint16_t, Record_Type, const std::vector< uint8_t > &)> writer_fn
std::vector< uint8_t > send_under_epoch(const Handshake_Message &msg, uint16_t epoch) override
std::optional< std::chrono::milliseconds > next_retransmission_timeout() const override
std::pair< Handshake_Type, std::vector< uint8_t > > get_next_record(bool expecting_ccs, size_t max_message_size) override
void add_record(const uint8_t record[], size_t record_len, Record_Type type, uint64_t sequence_number) override
std::vector< uint8_t > format(const std::vector< uint8_t > &handshake_msg, Handshake_Type handshake_type) const override
Protocol_Version initial_record_version() const override
void finalize_handshake(bool retransmit_terminal_flight)
void add_retransmitted_record(const uint8_t record[], size_t record_len, Record_Type type, uint64_t sequence_number)
std::vector< uint8_t > send(const Handshake_Message &msg) override
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)
Handshake_IO & operator=(Handshake_IO &&)=delete
virtual std::vector< uint8_t > send_under_epoch(const Handshake_Message &msg, uint16_t epoch)=0
Handshake_IO(const Handshake_IO &)=delete
virtual bool timeout_check()=0
virtual std::vector< uint8_t > send(const Handshake_Message &msg)=0
virtual std::optional< std::chrono::milliseconds > next_retransmission_timeout() const =0
virtual std::pair< Handshake_Type, std::vector< uint8_t > > get_next_record(bool expecting_ccs, size_t max_message_size)=0
Handshake_IO & operator=(const Handshake_IO &)=delete
virtual void add_record(const uint8_t record[], size_t record_len, Record_Type type, uint64_t sequence_number)=0
virtual Protocol_Version initial_record_version() const =0
Handshake_IO(Handshake_IO &&)=delete
virtual std::vector< uint8_t > format(const std::vector< uint8_t > &handshake_msg, Handshake_Type handshake_type) const =0
virtual ~Handshake_IO()=default
virtual bool have_more_data() const =0
bool have_more_data() const override
std::vector< uint8_t > send_under_epoch(const Handshake_Message &msg, uint16_t epoch) override
std::optional< std::chrono::milliseconds > next_retransmission_timeout() const override
std::vector< uint8_t > format(const std::vector< uint8_t > &handshake_msg, Handshake_Type handshake_type) const override
Protocol_Version initial_record_version() const override
std::function< void(Record_Type, const std::vector< uint8_t > &)> writer_fn
std::vector< uint8_t > send(const Handshake_Message &msg) override
std::pair< Handshake_Type, std::vector< uint8_t > > get_next_record(bool expecting_ccs, size_t max_message_size) override
void add_record(const uint8_t record[], size_t record_len, Record_Type type, uint64_t sequence_number) override