• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 6057704243

02 Sep 2023 10:05AM CUT coverage: 91.729% (+0.01%) from 91.718%
6057704243

Pull #3680

github

web-flow
Merge fc59ca615 into 2c2ff3c71
Pull Request #3680: Kuznyechik block cipher

78780 of 85883 relevant lines covered (91.73%)

8435879.14 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.59
/src/lib/tls/tls12/tls_handshake_io.cpp
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_messages.h>
12
#include <botan/internal/loadstor.h>
13
#include <botan/internal/tls_record.h>
14
#include <botan/internal/tls_seq_numbers.h>
15
#include <chrono>
16

17
namespace Botan::TLS {
18

19
namespace {
20

21
inline size_t load_be24(const uint8_t q[3]) {
180,965✔
22
   return make_uint32(0, q[0], q[1], q[2]);
180,965✔
23
}
24

25
void store_be24(uint8_t out[3], size_t val) {
19,709✔
26
   out[0] = get_byte<1>(static_cast<uint32_t>(val));
19,709✔
27
   out[1] = get_byte<2>(static_cast<uint32_t>(val));
7,665✔
28
   out[2] = get_byte<3>(static_cast<uint32_t>(val));
19,709✔
29
}
30

31
uint64_t steady_clock_ms() {
3,344✔
32
   return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch())
3,344✔
33
      .count();
3,344✔
34
}
35

36
}  // namespace
37

38
Protocol_Version Stream_Handshake_IO::initial_record_version() const {
3,233✔
39
   return Protocol_Version::TLS_V12;
3,233✔
40
}
41

42
void Stream_Handshake_IO::add_record(const uint8_t record[],
112,184✔
43
                                     size_t record_len,
44
                                     Record_Type record_type,
45
                                     uint64_t /*sequence_number*/) {
46
   if(record_type == Record_Type::Handshake) {
112,184✔
47
      m_queue.insert(m_queue.end(), record, record + record_len);
111,111✔
48
   } else if(record_type == Record_Type::ChangeCipherSpec) {
1,073✔
49
      if(record_len != 1 || record[0] != 1) {
1,073✔
50
         throw Decoding_Error("Invalid ChangeCipherSpec");
2✔
51
      }
52

53
      // Pretend it's a regular handshake message of zero length
54
      const uint8_t ccs_hs[] = {static_cast<uint8_t>(Handshake_Type::HandshakeCCS), 0, 0, 0};
1,071✔
55
      m_queue.insert(m_queue.end(), ccs_hs, ccs_hs + sizeof(ccs_hs));
1,071✔
56
   } else {
57
      throw Decoding_Error("Unknown message type " + std::to_string(static_cast<size_t>(record_type)) +
×
58
                           " in handshake processing");
×
59
   }
60
}
112,182✔
61

62
std::pair<Handshake_Type, std::vector<uint8_t>> Stream_Handshake_IO::get_next_record(bool /*expecting_ccs*/) {
116,958✔
63
   if(m_queue.size() >= 4) {
116,958✔
64
      const size_t length = 4 + make_uint32(0, m_queue[1], m_queue[2], m_queue[3]);
110,993✔
65

66
      if(m_queue.size() >= length) {
110,993✔
67
         Handshake_Type type = static_cast<Handshake_Type>(m_queue[0]);
7,366✔
68

69
         if(type == Handshake_Type::None) {
7,366✔
70
            throw Decoding_Error("Invalid handshake message type");
×
71
         }
72

73
         std::vector<uint8_t> contents(m_queue.begin() + 4, m_queue.begin() + length);
7,366✔
74

75
         m_queue.erase(m_queue.begin(), m_queue.begin() + length);
7,366✔
76

77
         return std::make_pair(type, contents);
14,732✔
78
      }
7,366✔
79
   }
80

81
   return std::make_pair(Handshake_Type::None, std::vector<uint8_t>());
109,592✔
82
}
83

84
std::vector<uint8_t> Stream_Handshake_IO::format(const std::vector<uint8_t>& msg, Handshake_Type type) const {
12,044✔
85
   std::vector<uint8_t> send_buf(4 + msg.size());
12,044✔
86

87
   const size_t buf_size = msg.size();
12,044✔
88

89
   send_buf[0] = static_cast<uint8_t>(type);
12,044✔
90

91
   store_be24(&send_buf[1], buf_size);
12,044✔
92

93
   if(!msg.empty()) {
12,044✔
94
      copy_mem(&send_buf[4], msg.data(), msg.size());
11,155✔
95
   }
96

97
   return send_buf;
12,044✔
98
}
×
99

100
std::vector<uint8_t> Stream_Handshake_IO::send_under_epoch(const Handshake_Message& /*msg*/, uint16_t /*epoch*/) {
×
101
   throw Invalid_State("Not possible to send under arbitrary epoch with stream based TLS");
×
102
}
103

104
std::vector<uint8_t> Stream_Handshake_IO::send(const Handshake_Message& msg) {
7,035✔
105
   const std::vector<uint8_t> msg_bits = msg.serialize();
7,035✔
106

107
   if(msg.type() == Handshake_Type::HandshakeCCS) {
7,035✔
108
      m_send_hs(Record_Type::ChangeCipherSpec, msg_bits);
1,056✔
109
      return std::vector<uint8_t>();  // not included in handshake hashes
1,056✔
110
   }
111

112
   auto buf = format(msg_bits, msg.wire_type());
5,979✔
113
   m_send_hs(Record_Type::Handshake, buf);
5,979✔
114
   return buf;
5,978✔
115
}
13,661✔
116

117
Protocol_Version Datagram_Handshake_IO::initial_record_version() const {
821✔
118
   return Protocol_Version::DTLS_V12;
821✔
119
}
120

121
void Datagram_Handshake_IO::retransmit_last_flight() {
×
122
   const size_t flight_idx = (m_flights.size() == 1) ? 0 : (m_flights.size() - 2);
×
123
   retransmit_flight(flight_idx);
×
124
}
×
125

126
void Datagram_Handshake_IO::retransmit_flight(size_t flight_idx) {
×
127
   const std::vector<uint16_t>& flight = m_flights.at(flight_idx);
×
128

129
   BOTAN_ASSERT(!flight.empty(), "Nonempty flight to retransmit");
×
130

131
   uint16_t epoch = m_flight_data[flight[0]].epoch;
×
132

133
   for(auto msg_seq : flight) {
×
134
      auto& msg = m_flight_data[msg_seq];
×
135

136
      if(msg.epoch != epoch) {
×
137
         // Epoch gap: insert the CCS
138
         std::vector<uint8_t> ccs(1, 1);
×
139
         m_send_hs(epoch, Record_Type::ChangeCipherSpec, ccs);
×
140
      }
×
141

142
      send_message(msg_seq, msg.epoch, msg.msg_type, msg.msg_bits);
×
143
      epoch = msg.epoch;
×
144
   }
145
}
×
146

147
bool Datagram_Handshake_IO::have_more_data() const {
1,748✔
148
   return false;
1,748✔
149
}
150

151
bool Datagram_Handshake_IO::timeout_check() {
×
152
   if(m_last_write == 0 || (m_flights.size() > 1 && !m_flights.rbegin()->empty())) {
×
153
      /*
154
      If we haven't written anything yet obviously no timeout.
155
      Also no timeout possible if we are mid-flight,
156
      */
157
      return false;
158
   }
159

160
   const uint64_t ms_since_write = steady_clock_ms() - m_last_write;
×
161

162
   if(ms_since_write < m_next_timeout) {
×
163
      return false;
164
   }
165

166
   retransmit_last_flight();
×
167

168
   m_next_timeout = std::min(2 * m_next_timeout, m_max_timeout);
×
169
   return true;
×
170
}
171

172
void Datagram_Handshake_IO::add_record(const uint8_t record[],
181,681✔
173
                                       size_t record_len,
174
                                       Record_Type record_type,
175
                                       uint64_t record_sequence) {
176
   const uint16_t epoch = static_cast<uint16_t>(record_sequence >> 48);
181,681✔
177

178
   if(record_type == Record_Type::ChangeCipherSpec) {
181,681✔
179
      if(record_len != 1 || record[0] != 1) {
715✔
180
         throw Decoding_Error("Invalid ChangeCipherSpec");
2✔
181
      }
182

183
      // TODO: check this is otherwise empty
184
      m_ccs_epochs.insert(epoch);
713✔
185
      return;
714✔
186
   }
187

188
   const size_t DTLS_HANDSHAKE_HEADER_LEN = 12;
189

190
   while(record_len) {
361,926✔
191
      if(record_len < DTLS_HANDSHAKE_HEADER_LEN) {
180,966✔
192
         return;  // completely bogus? at least degenerate/weird
1✔
193
      }
194

195
      const Handshake_Type msg_type = static_cast<Handshake_Type>(record[0]);
180,965✔
196
      const size_t msg_len = load_be24(&record[1]);
180,965✔
197
      const uint16_t message_seq = load_be<uint16_t>(&record[4], 0);
180,965✔
198
      const size_t fragment_offset = load_be24(&record[6]);
180,965✔
199
      const size_t fragment_length = load_be24(&record[9]);
180,965✔
200

201
      const size_t total_size = DTLS_HANDSHAKE_HEADER_LEN + fragment_length;
180,965✔
202

203
      if(record_len < total_size) {
180,965✔
204
         throw Decoding_Error("Bad lengths in DTLS header");
3✔
205
      }
206

207
      if(message_seq >= m_in_message_seq) {
180,962✔
208
         m_messages[message_seq].add_fragment(
180,218✔
209
            &record[DTLS_HANDSHAKE_HEADER_LEN], fragment_length, fragment_offset, epoch, msg_type, msg_len);
210
      } else {
211
         // TODO: detect retransmitted flight
212
      }
213

214
      record += total_size;
180,960✔
215
      record_len -= total_size;
180,960✔
216
   }
217
}
218

219
std::pair<Handshake_Type, std::vector<uint8_t>> Datagram_Handshake_IO::get_next_record(bool expecting_ccs) {
185,510✔
220
   // Expecting a message means the last flight is concluded
221
   if(!m_flights.rbegin()->empty()) {
185,510✔
222
      m_flights.push_back(std::vector<uint16_t>());
1,514✔
223
   }
224

225
   if(expecting_ccs) {
185,510✔
226
      if(!m_messages.empty()) {
1,590✔
227
         const uint16_t current_epoch = m_messages.begin()->second.epoch();
1,590✔
228

229
         if(m_ccs_epochs.contains(current_epoch)) {
3,180✔
230
            return std::make_pair(Handshake_Type::HandshakeCCS, std::vector<uint8_t>());
707✔
231
         }
232
      }
233
      return std::make_pair(Handshake_Type::None, std::vector<uint8_t>());
883✔
234
   }
235

236
   auto i = m_messages.find(m_in_message_seq);
183,920✔
237

238
   if(i == m_messages.end() || !i->second.complete()) {
183,920✔
239
      return std::make_pair(Handshake_Type::None, std::vector<uint8_t>());
179,988✔
240
   }
241

242
   m_in_message_seq += 1;
3,932✔
243

244
   return i->second.message();
3,932✔
245
}
246

247
void Datagram_Handshake_IO::Handshake_Reassembly::add_fragment(const uint8_t fragment[],
180,218✔
248
                                                               size_t fragment_length,
249
                                                               size_t fragment_offset,
250
                                                               uint16_t epoch,
251
                                                               Handshake_Type msg_type,
252
                                                               size_t msg_length) {
253
   if(complete()) {
540,515✔
254
      return;  // already have entire message, ignore this
255
   }
256

257
   if(m_msg_type == Handshake_Type::None) {
180,081✔
258
      m_epoch = epoch;
3,937✔
259
      m_msg_type = msg_type;
3,937✔
260
      m_msg_length = msg_length;
3,937✔
261
   }
262

263
   if(msg_type != m_msg_type || msg_length != m_msg_length || epoch != m_epoch) {
180,081✔
264
      throw Decoding_Error("Inconsistent values in fragmented DTLS handshake header");
2✔
265
   }
266

267
   if(fragment_offset > m_msg_length) {
180,079✔
268
      throw Decoding_Error("Fragment offset past end of message");
×
269
   }
270

271
   if(fragment_offset + fragment_length > m_msg_length) {
180,079✔
272
      throw Decoding_Error("Fragment overlaps past end of message");
×
273
   }
274

275
   if(fragment_offset == 0 && fragment_length == m_msg_length) {
180,079✔
276
      m_fragments.clear();
2,920✔
277
      m_message.assign(fragment, fragment + fragment_length);
2,920✔
278
   } else {
279
      /*
280
      * FIXME. This is a pretty lame way to do defragmentation, huge
281
      * overhead with a tree node per byte.
282
      *
283
      * Also should confirm that all overlaps have no changes,
284
      * otherwise we expose ourselves to the classic fingerprinting
285
      * and IDS evasion attacks on IP fragmentation.
286
      */
287
      for(size_t i = 0; i != fragment_length; ++i) {
547,268✔
288
         m_fragments[fragment_offset + i] = fragment[i];
370,109✔
289
      }
290

291
      if(m_fragments.size() == m_msg_length) {
177,159✔
292
         m_message.resize(m_msg_length);
1,013✔
293
         for(size_t i = 0; i != m_msg_length; ++i) {
363,265✔
294
            m_message[i] = m_fragments[i];
362,252✔
295
         }
296
         m_fragments.clear();
181,229✔
297
      }
298
   }
299
}
300

301
bool Datagram_Handshake_IO::Handshake_Reassembly::complete() const {
364,292✔
302
   return (m_msg_type != Handshake_Type::None && m_message.size() == m_msg_length);
364,292✔
303
}
304

305
std::pair<Handshake_Type, std::vector<uint8_t>> Datagram_Handshake_IO::Handshake_Reassembly::message() const {
3,932✔
306
   if(!complete()) {
3,932✔
307
      throw Internal_Error("Datagram_Handshake_IO - message not complete");
×
308
   }
309

310
   return std::make_pair(m_msg_type, m_message);
3,932✔
311
}
312

313
std::vector<uint8_t> Datagram_Handshake_IO::format_fragment(const uint8_t fragment[],
7,665✔
314
                                                            size_t frag_len,
315
                                                            uint16_t frag_offset,
316
                                                            uint16_t msg_len,
317
                                                            Handshake_Type type,
318
                                                            uint16_t msg_sequence) const {
319
   std::vector<uint8_t> send_buf(12 + frag_len);
7,665✔
320

321
   send_buf[0] = static_cast<uint8_t>(type);
7,665✔
322

323
   store_be24(&send_buf[1], msg_len);
7,665✔
324

325
   store_be(msg_sequence, &send_buf[4]);
7,665✔
326

327
   store_be24(&send_buf[6], frag_offset);
7,665✔
328
   store_be24(&send_buf[9], frag_len);
7,665✔
329

330
   if(frag_len > 0) {
7,665✔
331
      copy_mem(&send_buf[12], fragment, frag_len);
7,110✔
332
   }
333

334
   return send_buf;
7,665✔
335
}
×
336

337
std::vector<uint8_t> Datagram_Handshake_IO::format_w_seq(const std::vector<uint8_t>& msg,
7,099✔
338
                                                         Handshake_Type type,
339
                                                         uint16_t msg_sequence) const {
340
   return format_fragment(msg.data(), msg.size(), 0, static_cast<uint16_t>(msg.size()), type, msg_sequence);
×
341
}
342

343
std::vector<uint8_t> Datagram_Handshake_IO::format(const std::vector<uint8_t>& msg, Handshake_Type type) const {
3,386✔
344
   return format_w_seq(msg, type, m_in_message_seq - 1);
3,386✔
345
}
346

347
std::vector<uint8_t> Datagram_Handshake_IO::send(const Handshake_Message& msg) {
4,421✔
348
   return this->send_under_epoch(msg, m_seqs.current_write_epoch());
4,421✔
349
}
350

351
std::vector<uint8_t> Datagram_Handshake_IO::send_under_epoch(const Handshake_Message& msg, uint16_t epoch) {
4,422✔
352
   const std::vector<uint8_t> msg_bits = msg.serialize();
4,422✔
353
   const Handshake_Type msg_type = msg.type();
4,422✔
354

355
   if(msg_type == Handshake_Type::HandshakeCCS) {
4,422✔
356
      m_send_hs(epoch, Record_Type::ChangeCipherSpec, msg_bits);
709✔
357
      return std::vector<uint8_t>();  // not included in handshake hashes
709✔
358
   } else if(msg_type == Handshake_Type::HelloVerifyRequest) {
3,713✔
359
      // This message is not included in the handshake hashes
360
      send_message(m_out_message_seq, epoch, msg_type, msg_bits);
369✔
361
      m_out_message_seq += 1;
369✔
362
      return std::vector<uint8_t>();
4,422✔
363
   }
364

365
   // Note: not saving CCS, instead we know it was there due to change in epoch
366
   m_flights.rbegin()->push_back(m_out_message_seq);
3,344✔
367
   m_flight_data[m_out_message_seq] = Message_Info(epoch, msg_type, msg_bits);
3,344✔
368

369
   m_out_message_seq += 1;
3,344✔
370
   m_last_write = steady_clock_ms();
3,344✔
371
   m_next_timeout = m_initial_timeout;
3,344✔
372

373
   return send_message(m_out_message_seq - 1, epoch, msg_type, msg_bits);
3,344✔
374
}
4,422✔
375

376
std::vector<uint8_t> Datagram_Handshake_IO::send_message(uint16_t msg_seq,
3,713✔
377
                                                         uint16_t epoch,
378
                                                         Handshake_Type msg_type,
379
                                                         const std::vector<uint8_t>& msg_bits) {
380
   const size_t DTLS_HANDSHAKE_HEADER_LEN = 12;
3,713✔
381

382
   auto no_fragment = format_w_seq(msg_bits, msg_type, msg_seq);
3,713✔
383

384
   if(no_fragment.size() + DTLS_HEADER_SIZE <= m_mtu) {
3,713✔
385
      m_send_hs(epoch, Record_Type::Handshake, no_fragment);
7,034✔
386
   } else {
387
      size_t frag_offset = 0;
196✔
388

389
      /**
390
      * Largest possible overhead is for SHA-384 CBC ciphers, with 16 byte IV,
391
      * 16+ for padding and 48 bytes for MAC. 128 is probably a strict
392
      * over-estimate here. When CBC ciphers are removed this can be reduced
393
      * since AEAD modes have no padding, at most 16 byte mac, and smaller
394
      * per-record nonce.
395
      */
396
      const size_t ciphersuite_overhead = (epoch > 0) ? 128 : 0;
196✔
397
      const size_t header_overhead = DTLS_HEADER_SIZE + DTLS_HANDSHAKE_HEADER_LEN;
196✔
398

399
      if(m_mtu <= (header_overhead + ciphersuite_overhead)) {
196✔
400
         throw Invalid_Argument("DTLS MTU is too small to send headers");
×
401
      }
402

403
      const size_t max_rec_size = m_mtu - (header_overhead + ciphersuite_overhead);
196✔
404

405
      while(frag_offset != msg_bits.size()) {
762✔
406
         const size_t frag_len = std::min<size_t>(msg_bits.size() - frag_offset, max_rec_size);
566✔
407

408
         const std::vector<uint8_t> frag = format_fragment(&msg_bits[frag_offset],
566✔
409
                                                           frag_len,
410
                                                           static_cast<uint16_t>(frag_offset),
411
                                                           static_cast<uint16_t>(msg_bits.size()),
566✔
412
                                                           msg_type,
413
                                                           msg_seq);
566✔
414

415
         m_send_hs(epoch, Record_Type::Handshake, frag);
566✔
416

417
         frag_offset += frag_len;
566✔
418
      }
566✔
419
   }
420

421
   return no_fragment;
3,713✔
422
}
×
423

424
}  // namespace Botan::TLS
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc