Botan 3.12.0
Crypto and TLS for C&
tls_handshake_io.cpp
Go to the documentation of this file.
1/*
2* TLS Handshake IO
3* (C) 2012,2014,2015 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#include <botan/internal/tls_handshake_io.h>
9
10#include <botan/exceptn.h>
11#include <botan/tls_exceptn.h>
12#include <botan/tls_handshake_msg.h>
13#include <botan/internal/fmt.h>
14#include <botan/internal/loadstor.h>
15#include <botan/internal/tls_record.h>
16#include <botan/internal/tls_seq_numbers.h>
17#include <chrono>
18
19namespace Botan::TLS {
20
21namespace {
22
23inline size_t load_be24(const uint8_t q[3]) {
24 return make_uint32(0, q[0], q[1], q[2]);
25}
26
27// Reject handshake type values that are internal sentinels, not wire values
28void verify_is_expected_wire_handshake_type(Handshake_Type type) {
29 switch(type) {
33 throw TLS_Exception(Alert::UnexpectedMessage, "Invalid handshake message type");
34 default:
35 break;
36 }
37}
38
39void store_be24(uint8_t out[3], size_t val) {
40 out[0] = get_byte<1>(static_cast<uint32_t>(val));
41 out[1] = get_byte<2>(static_cast<uint32_t>(val));
42 out[2] = get_byte<3>(static_cast<uint32_t>(val));
43}
44
45uint64_t steady_clock_ms() {
46 return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch())
47 .count();
48}
49
50} // namespace
51
53 return Protocol_Version::TLS_V12;
54}
55
56void Stream_Handshake_IO::add_record(const uint8_t record[],
57 size_t record_len,
58 Record_Type record_type,
59 uint64_t /*sequence_number*/) {
60 if(record_type == Record_Type::Handshake) {
61 m_queue.insert(m_queue.end(), record, record + record_len);
62 } else if(record_type == Record_Type::ChangeCipherSpec) {
63 if(record_len != 1 || record[0] != 1) {
64 throw Decoding_Error("Invalid ChangeCipherSpec");
65 }
66
67 // Pretend it's a regular handshake message of zero length
68 const uint8_t ccs_hs[] = {static_cast<uint8_t>(Handshake_Type::HandshakeCCS), 0, 0, 0};
69 m_queue.insert(m_queue.end(), ccs_hs, ccs_hs + sizeof(ccs_hs));
70 } else {
71 throw Decoding_Error("Unknown message type " + std::to_string(static_cast<size_t>(record_type)) +
72 " in handshake processing");
73 }
74}
75
76std::pair<Handshake_Type, std::vector<uint8_t>> Stream_Handshake_IO::get_next_record(bool expecting_ccs,
77 size_t max_message_size) {
78 if(m_queue.size() >= 4) {
79 const Handshake_Type type = static_cast<Handshake_Type>(m_queue[0]);
80
81 const size_t rec_length = make_uint32(0, m_queue[1], m_queue[2], m_queue[3]);
82
83 // If we are expecting a CCS but the next queued message is not a CCS,
84 // the peer has skipped the CCS message. This can happen when the peer
85 // sends an encrypted Finished without the preceding CCS, in which case
86 // the encrypted bytes are misinterpreted as a handshake message.
87 if(expecting_ccs) {
88 const bool is_ccs = (type == Handshake_Type::HandshakeCCS && rec_length == 0);
89 if(!is_ccs) {
90 throw TLS_Exception(Alert::UnexpectedMessage, "Expected ChangeCipherSpec but got a handshake message");
91 }
92 } else {
93 verify_is_expected_wire_handshake_type(type);
94
95 if(max_message_size > 0 && rec_length > max_message_size) {
96 throw TLS_Exception(
97 Alert::HandshakeFailure,
98 Botan::fmt("Handshake message is {} bytes, policy maximum is {}", rec_length, max_message_size));
99 }
100 }
101
102 const size_t length = 4 + rec_length;
103
104 if(m_queue.size() >= length) {
105 const std::vector<uint8_t> contents(m_queue.begin() + 4, m_queue.begin() + length);
106
107 m_queue.erase(m_queue.begin(), m_queue.begin() + length);
108
109 return std::make_pair(type, contents);
110 }
111 }
112
113 return std::make_pair(Handshake_Type::None, std::vector<uint8_t>());
114}
115
116std::vector<uint8_t> Stream_Handshake_IO::format(const std::vector<uint8_t>& msg, Handshake_Type type) const {
117 std::vector<uint8_t> send_buf(4 + msg.size());
118
119 const size_t buf_size = msg.size();
120
121 send_buf[0] = static_cast<uint8_t>(type);
122
123 store_be24(&send_buf[1], buf_size);
124
125 if(!msg.empty()) {
126 copy_mem(&send_buf[4], msg.data(), msg.size());
127 }
128
129 return send_buf;
130}
131
132std::vector<uint8_t> Stream_Handshake_IO::send_under_epoch(const Handshake_Message& /*msg*/, uint16_t /*epoch*/) {
133 throw Invalid_State("Not possible to send under arbitrary epoch with stream based TLS");
134}
135
136std::vector<uint8_t> Stream_Handshake_IO::send(const Handshake_Message& msg) {
137 const std::vector<uint8_t> msg_bits = msg.serialize();
138
140 m_send_hs(Record_Type::ChangeCipherSpec, msg_bits);
141 return std::vector<uint8_t>(); // not included in handshake hashes
142 }
143
144 auto buf = format(msg_bits, msg.wire_type());
145 m_send_hs(Record_Type::Handshake, buf);
146 return buf;
147}
148
150 return Protocol_Version::DTLS_V12;
151}
152
153void Datagram_Handshake_IO::retransmit_last_flight() {
154 const size_t flight_idx = (m_flights.size() == 1) ? 0 : (m_flights.size() - 2);
155 retransmit_flight(flight_idx);
156}
157
158void Datagram_Handshake_IO::retransmit_flight(size_t flight_idx) {
159 const std::vector<uint16_t>& flight = m_flights.at(flight_idx);
160
161 BOTAN_ASSERT(!flight.empty(), "Nonempty flight to retransmit");
162
163 uint16_t epoch = m_flight_data[flight[0]].epoch;
164
165 for(auto msg_seq : flight) {
166 auto& msg = m_flight_data[msg_seq];
167
168 if(msg.epoch != epoch) {
169 // Epoch gap: insert the CCS
170 const std::vector<uint8_t> ccs(1, 1);
171 m_send_hs(epoch, Record_Type::ChangeCipherSpec, ccs);
172 }
173
174 send_message(msg_seq, msg.epoch, msg.msg_type, msg.msg_bits);
175 epoch = msg.epoch;
176 }
177}
178
180 return false;
181}
182
184 if(m_last_write == 0 || (m_flights.size() > 1 && !m_flights.rbegin()->empty())) {
185 /*
186 If we haven't written anything yet obviously no timeout.
187 Also no timeout possible if we are mid-flight,
188 */
189 return false;
190 }
191
192 const uint64_t ms_since_write = steady_clock_ms() - m_last_write;
193
194 if(ms_since_write < m_next_timeout) {
195 return false;
196 }
197
198 retransmit_last_flight();
199
200 m_next_timeout = std::min(2 * m_next_timeout, m_max_timeout);
201 return true;
202}
203
204void Datagram_Handshake_IO::add_record(const uint8_t record[],
205 size_t record_len,
206 Record_Type record_type,
207 uint64_t record_sequence) {
208 const uint16_t epoch = static_cast<uint16_t>(record_sequence >> 48);
209
210 if(record_type == Record_Type::ChangeCipherSpec) {
211 if(record_len != 1 || record[0] != 1) {
212 throw Decoding_Error("Invalid ChangeCipherSpec");
213 }
214
215 // TODO: check this is otherwise empty
216 m_ccs_epochs.insert(epoch);
217 return;
218 }
219
220 const size_t DTLS_HANDSHAKE_HEADER_LEN = 12;
221
222 while(record_len > 0) {
223 if(record_len < DTLS_HANDSHAKE_HEADER_LEN) {
224 return; // completely bogus? at least degenerate/weird
225 }
226
227 const Handshake_Type msg_type = static_cast<Handshake_Type>(record[0]);
228
229 verify_is_expected_wire_handshake_type(msg_type);
230
231 const size_t msg_len = load_be24(&record[1]);
232
233 if(m_max_handshake_msg_size > 0 && msg_len > m_max_handshake_msg_size) {
234 throw TLS_Exception(
235 Alert::HandshakeFailure,
236 Botan::fmt("Handshake message is {} bytes, policy maximum is {}", msg_len, m_max_handshake_msg_size));
237 }
238
239 const uint16_t message_seq = load_be<uint16_t>(&record[4], 0);
240 const size_t fragment_offset = load_be24(&record[6]);
241 const size_t fragment_length = load_be24(&record[9]);
242
243 const size_t total_size = DTLS_HANDSHAKE_HEADER_LEN + fragment_length;
244
245 if(record_len < total_size) {
246 throw Decoding_Error("Bad lengths in DTLS header");
247 }
248
249 // Bound the out-of-order reassembly window.
250 constexpr uint16_t reassembly_window = 16;
251
252 // Independently cap total bytes committed to in-flight reassembly slots
253 const size_t max_pending = 4 * m_max_handshake_msg_size;
254
255 if(message_seq >= m_in_message_seq && (message_seq - m_in_message_seq) < reassembly_window) {
256 auto [it, inserted] = m_messages.try_emplace(message_seq);
257 if(inserted) {
258 if(m_max_handshake_msg_size > 0 && m_pending_reassembly_bytes + msg_len > max_pending) {
259 m_messages.erase(it);
260 record += total_size;
261 record_len -= total_size;
262 continue;
263 }
264 m_pending_reassembly_bytes += msg_len;
265 }
266 it->second.add_fragment(
267 &record[DTLS_HANDSHAKE_HEADER_LEN], fragment_length, fragment_offset, epoch, msg_type, msg_len);
268 } else {
269 // TODO: detect retransmitted flight
270 }
271
272 record += total_size;
273 record_len -= total_size;
274 }
275}
276
277std::pair<Handshake_Type, std::vector<uint8_t>> Datagram_Handshake_IO::get_next_record(bool expecting_ccs,
278 size_t /*max_message_size*/) {
279 // Expecting a message means the last flight is concluded
280 if(!m_flights.rbegin()->empty()) {
281 m_flights.push_back(std::vector<uint16_t>());
282 }
283
284 if(expecting_ccs) {
285 if(!m_messages.empty()) {
286 const uint16_t current_epoch = m_messages.begin()->second.epoch();
287
288 if(m_ccs_epochs.contains(current_epoch)) {
289 return std::make_pair(Handshake_Type::HandshakeCCS, std::vector<uint8_t>());
290 }
291 }
292 return std::make_pair(Handshake_Type::None, std::vector<uint8_t>());
293 }
294
295 auto i = m_messages.find(m_in_message_seq);
296
297 if(i == m_messages.end() || !i->second.complete()) {
298 return std::make_pair(Handshake_Type::None, std::vector<uint8_t>());
299 }
300
301 m_in_message_seq += 1;
302
303 auto result = i->second.message();
304
305 // Free the reassembly buffer for this delivered slot and uncommit its
306 // bytes against the cap. The entry itself stays in m_messages because
307 // the expecting_ccs branch above uses m_messages.begin()->second.epoch()
308 // as an epoch-0 sentinel; it only needs the metadata, not the buffers.
309 BOTAN_ASSERT_NOMSG(m_pending_reassembly_bytes >= i->second.msg_length());
310 m_pending_reassembly_bytes -= i->second.msg_length();
311 i->second.release_buffers();
312
313 return result;
314}
315
316void Datagram_Handshake_IO::Handshake_Reassembly::release_buffers() {
317 m_received_mask.clear();
318 m_received_mask.shrink_to_fit();
319 m_message.clear();
320 m_message.shrink_to_fit();
321}
322
323void Datagram_Handshake_IO::Handshake_Reassembly::add_fragment(const uint8_t fragment[],
324 size_t fragment_length,
325 size_t fragment_offset,
326 uint16_t epoch,
327 Handshake_Type msg_type,
328 size_t msg_length) {
329 if(m_msg_type == Handshake_Type::None) {
330 // First fragment for this message_seq
331 m_epoch = epoch;
332 m_msg_type = msg_type;
333 m_msg_length = msg_length;
334 m_message.resize(msg_length);
335 m_received_mask.assign(msg_length, 0);
336 } else {
337 if(msg_type != m_msg_type || msg_length != m_msg_length || epoch != m_epoch) {
338 throw Decoding_Error("Inconsistent values in fragmented DTLS handshake header");
339 }
340
341 if(complete()) {
342 return; // already have entire message, ignore this
343 }
344 }
345
346 if(fragment_offset > m_msg_length) {
347 throw Decoding_Error("Fragment offset past end of message");
348 }
349
350 if(fragment_offset + fragment_length > m_msg_length) {
351 throw Decoding_Error("Fragment overlaps past end of message");
352 }
353
354 BOTAN_ASSERT_NOMSG(m_received_mask.size() == m_msg_length);
355
356 for(size_t i = 0; i != fragment_length; ++i) {
357 const size_t off = fragment_offset + i;
358 if(m_received_mask[off] != 0) {
359 // RFC 6347 4.2.3 permits overlapping retransmissions, but the
360 // overlapping bytes must agree.
361 if(m_message[off] != fragment[i]) {
362 throw Decoding_Error("Inconsistent overlapping DTLS handshake fragment");
363 }
364 } else {
365 m_message[off] = fragment[i];
366 m_received_mask[off] = 1;
367 ++m_bytes_received;
368 }
369 }
370}
371
372bool Datagram_Handshake_IO::Handshake_Reassembly::complete() const {
373 return (m_msg_type != Handshake_Type::None && m_bytes_received == m_msg_length);
374}
375
376std::pair<Handshake_Type, std::vector<uint8_t>> Datagram_Handshake_IO::Handshake_Reassembly::message() const {
377 if(!complete()) {
378 throw Internal_Error("Datagram_Handshake_IO - message not complete");
379 }
380
381 return std::make_pair(m_msg_type, m_message);
382}
383
384std::vector<uint8_t> Datagram_Handshake_IO::format_fragment(const uint8_t fragment[],
385 size_t frag_len,
386 uint32_t frag_offset,
387 uint32_t msg_len,
388 Handshake_Type type,
389 uint16_t msg_sequence) const {
390 std::vector<uint8_t> send_buf(12 + frag_len);
391
392 send_buf[0] = static_cast<uint8_t>(type);
393
394 store_be24(&send_buf[1], msg_len);
395
396 store_be(msg_sequence, &send_buf[4]);
397
398 store_be24(&send_buf[6], frag_offset);
399 store_be24(&send_buf[9], frag_len);
400
401 if(frag_len > 0) {
402 copy_mem(&send_buf[12], fragment, frag_len);
403 }
404
405 return send_buf;
406}
407
408std::vector<uint8_t> Datagram_Handshake_IO::format_w_seq(const std::vector<uint8_t>& msg,
409 Handshake_Type type,
410 uint16_t msg_sequence) const {
411 return format_fragment(msg.data(), msg.size(), 0, static_cast<uint32_t>(msg.size()), type, msg_sequence);
412}
413
414std::vector<uint8_t> Datagram_Handshake_IO::format(const std::vector<uint8_t>& msg, Handshake_Type type) const {
415 BOTAN_ASSERT_NOMSG(m_in_message_seq > 0);
416 return format_w_seq(msg, type, m_in_message_seq - 1);
417}
418
419std::vector<uint8_t> Datagram_Handshake_IO::send(const Handshake_Message& msg) {
420 return this->send_under_epoch(msg, m_seqs.current_write_epoch());
421}
422
423std::vector<uint8_t> Datagram_Handshake_IO::send_under_epoch(const Handshake_Message& msg, uint16_t epoch) {
424 const std::vector<uint8_t> msg_bits = msg.serialize();
425 const Handshake_Type msg_type = msg.type();
426
427 if(msg_type == Handshake_Type::HandshakeCCS) {
428 m_send_hs(epoch, Record_Type::ChangeCipherSpec, msg_bits);
429 return std::vector<uint8_t>(); // not included in handshake hashes
430 } else if(msg_type == Handshake_Type::HelloVerifyRequest) {
431 // This message is not included in the handshake hashes
432 send_message(m_out_message_seq, epoch, msg_type, msg_bits);
433 m_out_message_seq += 1;
434 return std::vector<uint8_t>();
435 }
436
437 // Note: not saving CCS, instead we know it was there due to change in epoch
438 m_flights.rbegin()->push_back(m_out_message_seq);
439 m_flight_data[m_out_message_seq] = Message_Info(epoch, msg_type, msg_bits);
440
441 m_out_message_seq += 1;
442 m_last_write = steady_clock_ms();
443 m_next_timeout = m_initial_timeout;
444
445 return send_message(m_out_message_seq - 1, epoch, msg_type, msg_bits);
446}
447
448std::vector<uint8_t> Datagram_Handshake_IO::send_message(uint16_t msg_seq,
449 uint16_t epoch,
450 Handshake_Type msg_type,
451 const std::vector<uint8_t>& msg_bits) {
452 const size_t DTLS_HANDSHAKE_HEADER_LEN = 12;
453
454 auto no_fragment = format_w_seq(msg_bits, msg_type, msg_seq);
455
456 if(no_fragment.size() + DTLS_HEADER_SIZE <= m_mtu) {
457 m_send_hs(epoch, Record_Type::Handshake, no_fragment);
458 } else {
459 size_t frag_offset = 0;
460
461 /**
462 * Largest possible overhead is for SHA-384 CBC ciphers, with 16 byte IV,
463 * 16+ for padding and 48 bytes for MAC. 128 is probably a strict
464 * over-estimate here. When CBC ciphers are removed this can be reduced
465 * since AEAD modes have no padding, at most 16 byte mac, and smaller
466 * per-record nonce.
467 */
468 const size_t ciphersuite_overhead = (epoch > 0) ? 128 : 0;
469 const size_t header_overhead = DTLS_HEADER_SIZE + DTLS_HANDSHAKE_HEADER_LEN;
470
471 if(m_mtu <= (header_overhead + ciphersuite_overhead)) {
472 throw Invalid_Argument("DTLS MTU is too small to send headers");
473 }
474
475 const size_t max_rec_size = m_mtu - (header_overhead + ciphersuite_overhead);
476
477 while(frag_offset != msg_bits.size()) {
478 const size_t frag_len = std::min<size_t>(msg_bits.size() - frag_offset, max_rec_size);
479
480 const std::vector<uint8_t> frag = format_fragment(&msg_bits[frag_offset],
481 frag_len,
482 static_cast<uint32_t>(frag_offset),
483 static_cast<uint32_t>(msg_bits.size()),
484 msg_type,
485 msg_seq);
486
487 m_send_hs(epoch, Record_Type::Handshake, frag);
488
489 frag_offset += frag_len;
490 }
491 }
492
493 return no_fragment;
494}
495
496} // namespace Botan::TLS
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:62
std::vector< uint8_t > send_under_epoch(const Handshake_Message &msg, uint16_t epoch) 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
std::vector< uint8_t > send(const Handshake_Message &msg) override
virtual Handshake_Type type() const =0
virtual std::vector< uint8_t > serialize() const =0
virtual Handshake_Type wire_type() const
std::vector< uint8_t > send_under_epoch(const Handshake_Message &msg, uint16_t epoch) 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::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
@ DTLS_HEADER_SIZE
Definition tls_magic.h:27
constexpr uint8_t get_byte(T input)
Definition loadstor.h:79
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
constexpr uint32_t make_uint32(uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3)
Definition loadstor.h:104
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:745
constexpr auto load_be(ParamTs &&... params)
Definition loadstor.h:504