Botan 3.11.0
Crypto and TLS for C&
tls_record.cpp
Go to the documentation of this file.
1/*
2* TLS Record Handling
3* (C) 2012,2013,2014,2015,2016,2019 Jack Lloyd
4* 2016 Juraj Somorovsky
5* 2016 Matthias Gierlings
6*
7* Botan is released under the Simplified BSD License (see license.txt)
8*/
9
10#include <botan/internal/tls_record.h>
11
12#include <botan/aead.h>
13#include <botan/block_cipher.h>
14#include <botan/mac.h>
15#include <botan/rng.h>
16#include <botan/tls_ciphersuite.h>
17#include <botan/tls_exceptn.h>
18#include <botan/internal/fmt.h>
19#include <botan/internal/loadstor.h>
20#include <botan/internal/tls_seq_numbers.h>
21#include <botan/internal/tls_session_key.h>
22
23#if defined(BOTAN_HAS_TLS_CBC)
24 #include <botan/internal/tls_cbc.h>
25#endif
26
27#if defined(BOTAN_HAS_TLS_NULL)
28 #include <botan/internal/tls_null.h>
29#endif
30
31namespace Botan::TLS {
32
34
36 Connection_Side side,
37 bool our_side,
38 const Ciphersuite& suite,
39 const Session_Keys& keys,
40 bool uses_encrypt_then_mac) {
41 // NOLINTBEGIN(*-prefer-member-initializer)
42 m_nonce_format = suite.nonce_format();
43 m_nonce_bytes_from_record = suite.nonce_bytes_from_record(version);
44 m_nonce_bytes_from_handshake = suite.nonce_bytes_from_handshake();
45
46 const secure_vector<uint8_t>& aead_key = keys.aead_key(side);
47 m_nonce = keys.nonce(side);
48 // NOLINTEND(*-prefer-member-initializer)
49
50 BOTAN_ASSERT_NOMSG(m_nonce.size() == m_nonce_bytes_from_handshake);
51
53#if defined(BOTAN_HAS_TLS_CBC)
54 // legacy CBC+HMAC mode
55 auto mac = MessageAuthenticationCode::create_or_throw(fmt("HMAC({})", suite.mac_algo()));
56 auto cipher = BlockCipher::create_or_throw(suite.cipher_algo());
57
58 if(our_side) {
59 m_aead = std::make_unique<TLS_CBC_HMAC_AEAD_Encryption>(std::move(cipher),
60 std::move(mac),
61 suite.cipher_keylen(),
62 suite.mac_keylen(),
63 version,
64 uses_encrypt_then_mac);
65 } else {
66 m_aead = std::make_unique<TLS_CBC_HMAC_AEAD_Decryption>(std::move(cipher),
67 std::move(mac),
68 suite.cipher_keylen(),
69 suite.mac_keylen(),
70 version,
71 uses_encrypt_then_mac);
72 }
73
74#else
75 BOTAN_UNUSED(uses_encrypt_then_mac);
76 throw Internal_Error("Negotiated disabled TLS CBC+HMAC ciphersuite");
77#endif
79#if defined(BOTAN_HAS_TLS_NULL)
80 auto mac = MessageAuthenticationCode::create_or_throw(fmt("HMAC({})", suite.mac_algo()));
81
82 if(our_side) {
83 m_aead = std::make_unique<TLS_NULL_HMAC_AEAD_Encryption>(std::move(mac), suite.mac_keylen());
84 } else {
85 m_aead = std::make_unique<TLS_NULL_HMAC_AEAD_Decryption>(std::move(mac), suite.mac_keylen());
86 }
87#else
88 throw Internal_Error("Negotiated disabled TLS NULL ciphersuite");
89#endif
90 } else {
91 m_aead =
93 }
94
95 m_aead->set_key(aead_key);
96}
97
98std::vector<uint8_t> Connection_Cipher_State::aead_nonce(uint64_t seq, RandomNumberGenerator& rng) {
99 switch(m_nonce_format) {
101 return std::vector<uint8_t>{};
102 }
104 if(!m_nonce.empty()) {
105 std::vector<uint8_t> nonce;
106 nonce.swap(m_nonce);
107 return nonce;
108 }
109 std::vector<uint8_t> nonce(nonce_bytes_from_record());
110 rng.randomize(nonce.data(), nonce.size());
111 return nonce;
112 }
114 std::vector<uint8_t> nonce(12);
115 store_be(seq, nonce.data() + 4);
116 xor_buf(nonce, m_nonce.data(), m_nonce.size());
117 return nonce;
118 }
120 BOTAN_ASSERT_NOMSG(m_nonce.size() == 4);
121 std::vector<uint8_t> nonce(12);
122 copy_mem(&nonce[0], m_nonce.data(), 4); // NOLINT(*container-data-pointer)
123 store_be(seq, &nonce[nonce_bytes_from_handshake()]);
124 return nonce;
125 }
126 }
127
128 throw Invalid_State("Unknown nonce format specified");
129}
130
131std::vector<uint8_t> Connection_Cipher_State::aead_nonce(const uint8_t record[], size_t record_len, uint64_t seq) {
132 switch(m_nonce_format) {
134 return std::vector<uint8_t>{};
135 }
137 if(nonce_bytes_from_record() == 0 && !m_nonce.empty()) {
138 std::vector<uint8_t> nonce;
139 nonce.swap(m_nonce);
140 return nonce;
141 }
142 if(record_len < nonce_bytes_from_record()) {
143 throw Decoding_Error("Invalid CBC packet too short to be valid");
144 }
145 std::vector<uint8_t> nonce(record, record + nonce_bytes_from_record());
146 return nonce;
147 }
149 std::vector<uint8_t> nonce(12);
150 store_be(seq, nonce.data() + 4);
151 xor_buf(nonce, m_nonce.data(), m_nonce.size());
152 return nonce;
153 }
155 BOTAN_ASSERT_NOMSG(m_nonce.size() == 4);
156 if(record_len < nonce_bytes_from_record()) {
157 throw Decoding_Error("Invalid AEAD packet too short to be valid");
158 }
159 std::vector<uint8_t> nonce(12);
160 copy_mem(&nonce[0], m_nonce.data(), 4); // NOLINT(*container-data-pointer)
162 return nonce;
163 }
164 }
165
166 throw Invalid_State("Unknown nonce format specified");
167}
168
169std::vector<uint8_t> Connection_Cipher_State::format_ad(uint64_t msg_sequence,
170 Record_Type msg_type,
171 Protocol_Version version,
172 uint16_t msg_length) {
173 std::vector<uint8_t> ad(13);
174
175 store_be(msg_sequence, &ad[0]); // NOLINT(*container-data-pointer)
176 ad[8] = static_cast<uint8_t>(msg_type);
177 ad[9] = version.major_version();
178 ad[10] = version.minor_version();
179 ad[11] = get_byte<0>(msg_length);
180 ad[12] = get_byte<1>(msg_length);
181
182 return ad;
183}
184
185namespace {
186
187inline void append_u16_len(secure_vector<uint8_t>& output, size_t len_field) {
188 const uint16_t len16 = static_cast<uint16_t>(len_field);
189 BOTAN_ASSERT_EQUAL(len_field, len16, "No truncation");
190 output.push_back(get_byte<0>(len16));
191 output.push_back(get_byte<1>(len16));
192}
193
194void write_record_header(secure_vector<uint8_t>& output,
195 Record_Type record_type,
196 Protocol_Version version,
197 uint64_t record_sequence) {
198 output.clear();
199
200 output.push_back(static_cast<uint8_t>(record_type));
201 output.push_back(version.major_version());
202 output.push_back(version.minor_version());
203
204 if(version.is_datagram_protocol()) {
205 for(size_t i = 0; i != 8; ++i) {
206 output.push_back(get_byte_var(i, record_sequence));
207 }
208 }
209}
210
211} // namespace
212
214 Record_Type record_type,
215 Protocol_Version version,
216 uint64_t record_sequence,
217 const uint8_t* message,
218 size_t message_len) {
219 if(record_type == Record_Type::ApplicationData) {
220 throw Internal_Error("Writing an unencrypted TLS application data record");
221 }
222 write_record_header(output, record_type, version, record_sequence);
223 append_u16_len(output, message_len);
224 output.insert(output.end(), message, message + message_len);
225}
226
228 Record_Type record_type,
229 Protocol_Version version,
230 uint64_t record_sequence,
231 const uint8_t* message,
232 size_t message_len,
235 write_record_header(output, record_type, version, record_sequence);
236
237 AEAD_Mode& aead = cs.aead();
238 std::vector<uint8_t> aad = cs.format_ad(record_sequence, record_type, version, static_cast<uint16_t>(message_len));
239
240 const size_t ctext_size = aead.output_length(message_len);
241
242 const size_t rec_size = ctext_size + cs.nonce_bytes_from_record();
243
244 aead.set_associated_data(aad);
245
246 const std::vector<uint8_t> nonce = cs.aead_nonce(record_sequence, rng);
247
248 append_u16_len(output, rec_size);
249
250 if(cs.nonce_bytes_from_record() > 0) {
252 output += nonce;
253 } else {
254 output += std::make_pair(&nonce[cs.nonce_bytes_from_handshake()], cs.nonce_bytes_from_record());
255 }
256 }
257
258 const size_t header_size = output.size();
259 output += std::make_pair(message, message_len);
260
261 aead.start(nonce);
262 aead.finish(output, header_size);
263
264 BOTAN_ASSERT(output.size() < MAX_CIPHERTEXT_SIZE, "Produced ciphertext larger than protocol allows");
265}
266
267namespace {
268
269size_t fill_buffer_to(
270 secure_vector<uint8_t>& readbuf, const uint8_t*& input, size_t& input_size, size_t& input_consumed, size_t desired) {
271 if(readbuf.size() >= desired) {
272 return 0; // already have it
273 }
274
275 const size_t taken = std::min(input_size, desired - readbuf.size());
276
277 readbuf.insert(readbuf.end(), input, input + taken);
278 input_consumed += taken;
279 input_size -= taken;
280 input += taken;
281
282 return (desired - readbuf.size()); // how many bytes do we still need?
283}
284
285void decrypt_record(secure_vector<uint8_t>& output,
286 uint8_t record_contents[],
287 size_t record_len,
288 uint64_t record_sequence,
289 Protocol_Version record_version,
290 Record_Type record_type,
292 AEAD_Mode& aead = cs.aead();
293
294 const std::vector<uint8_t> nonce = cs.aead_nonce(record_contents, record_len, record_sequence);
295 const uint8_t* msg = &record_contents[cs.nonce_bytes_from_record()];
296 const size_t msg_length = record_len - cs.nonce_bytes_from_record();
297
298 /*
299 * This early rejection is based just on public information (length of the
300 * encrypted packet) and so does not leak any information. We used to use
301 * decode_error here which really is more appropriate, but that confuses some
302 * tools which are attempting automated detection of padding oracles,
303 * including older versions of TLS-Attacker.
304 */
305 if(msg_length < aead.minimum_final_size()) {
306 throw TLS_Exception(Alert::BadRecordMac, "AEAD packet is shorter than the tag");
307 }
308
309 const size_t ptext_size = aead.output_length(msg_length);
310
311 aead.set_associated_data(
312 cs.format_ad(record_sequence, record_type, record_version, static_cast<uint16_t>(ptext_size)));
313
314 aead.start(nonce);
315
316 output.assign(msg, msg + msg_length);
317 aead.finish(output, 0);
318}
319
320Record_Header read_tls_record(secure_vector<uint8_t>& readbuf,
321 const uint8_t input[],
322 size_t input_len,
323 size_t& consumed,
325 Connection_Sequence_Numbers* sequence_numbers,
326 const get_cipherstate_fn& get_cipherstate) {
327 if(readbuf.size() < TLS_HEADER_SIZE) {
328 // header incomplete
329 if(const size_t needed = fill_buffer_to(readbuf, input, input_len, consumed, TLS_HEADER_SIZE)) {
330 return Record_Header(needed);
331 }
332
333 BOTAN_ASSERT_EQUAL(readbuf.size(), TLS_HEADER_SIZE, "Have an entire header");
334 }
335
336 /*
337 Verify that the record type and record version are within some expected
338 range, so we can quickly reject totally invalid packets.
339
340 The version check is a little hacky but given how TLS 1.3 versioning works
341 this is probably safe
342
343 - The first byte is the record version which in TLS 1.2 is always in [20..23)
344 - The second byte is the TLS major version which is effectively fossilized at 3
345 - The third byte is the TLS minor version which (due to TLS 1.3 versioning changes)
346 will never be more than 3 (signifying TLS 1.2)
347 */
348 const bool bad_record_type = readbuf[0] < 20 || readbuf[0] > 23;
349 const bool bad_record_version = readbuf[1] != 3 || readbuf[2] >= 4;
350
351 if(bad_record_type || bad_record_version) {
352 // We know we read up to at least the 5 byte TLS header
353 const std::string first5 = std::string(reinterpret_cast<const char*>(readbuf.data()), 5);
354
355 if(first5 == "GET /" || first5 == "PUT /" || first5 == "POST " || first5 == "HEAD ") {
356 throw TLS_Exception(Alert::ProtocolVersion, "Client sent plaintext HTTP request instead of TLS handshake");
357 }
358
359 if(first5 == "CONNE") {
360 throw TLS_Exception(Alert::ProtocolVersion,
361 "Client sent plaintext HTTP proxy CONNECT request instead of TLS handshake");
362 }
363
364 if(bad_record_type) {
365 // RFC 5246 Section 6.
366 // If a TLS implementation receives an unexpected record type, it MUST
367 // send an unexpected_message alert.
368 throw TLS_Exception(Alert::UnexpectedMessage, "TLS record type had unexpected value");
369 }
370 throw TLS_Exception(Alert::ProtocolVersion, "TLS record version had unexpected value");
371 }
372
373 const Protocol_Version version(readbuf[1], readbuf[2]);
374
375 if(version.is_datagram_protocol()) {
376 throw TLS_Exception(Alert::ProtocolVersion, "Expected TLS but got a record with DTLS version");
377 }
378
379 const size_t record_size = make_uint16(readbuf[TLS_HEADER_SIZE - 2], readbuf[TLS_HEADER_SIZE - 1]);
380
381 if(record_size > MAX_CIPHERTEXT_SIZE) {
382 throw TLS_Exception(Alert::RecordOverflow, "Received a record that exceeds maximum size");
383 }
384
385 if(record_size == 0) {
386 throw TLS_Exception(Alert::DecodeError, "Received a completely empty record");
387 }
388
389 if(const size_t needed = fill_buffer_to(readbuf, input, input_len, consumed, TLS_HEADER_SIZE + record_size)) {
390 return Record_Header(needed);
391 }
392
393 BOTAN_ASSERT_EQUAL(static_cast<size_t>(TLS_HEADER_SIZE) + record_size, readbuf.size(), "Have the full record");
394
395 const Record_Type type = static_cast<Record_Type>(readbuf[0]);
396
397 uint16_t epoch = 0;
398
399 uint64_t sequence = 0;
400 if(sequence_numbers != nullptr) {
401 sequence = sequence_numbers->next_read_sequence();
402 epoch = sequence_numbers->current_read_epoch();
403 } else {
404 // server initial handshake case
405 epoch = 0;
406 }
407
408 if(epoch == 0) {
409 // Unencrypted initial handshake
410 recbuf.assign(readbuf.begin() + TLS_HEADER_SIZE, readbuf.begin() + TLS_HEADER_SIZE + record_size);
411 readbuf.clear();
412 return Record_Header(sequence, version, type);
413 }
414
415 // Otherwise, decrypt, check MAC, return plaintext
416 auto cs = get_cipherstate(epoch);
417
418 BOTAN_ASSERT(cs, "Have cipherstate for this epoch");
419
420 decrypt_record(recbuf, &readbuf[TLS_HEADER_SIZE], record_size, sequence, version, type, *cs);
421
422 if(sequence_numbers != nullptr) {
423 sequence_numbers->read_accept(sequence);
424 }
425
426 readbuf.clear();
427 return Record_Header(sequence, version, type);
428}
429
430Record_Header read_dtls_record(secure_vector<uint8_t>& readbuf,
431 const uint8_t input[],
432 size_t input_len,
433 size_t& consumed,
435 Connection_Sequence_Numbers* sequence_numbers,
436 const get_cipherstate_fn& get_cipherstate,
437 bool allow_epoch0_restart) {
438 if(readbuf.size() < DTLS_HEADER_SIZE) {
439 // header incomplete
440 if(fill_buffer_to(readbuf, input, input_len, consumed, DTLS_HEADER_SIZE) != 0) {
441 readbuf.clear();
442 return Record_Header(0);
443 }
444
445 BOTAN_ASSERT_EQUAL(readbuf.size(), DTLS_HEADER_SIZE, "Have an entire header");
446 }
447
448 const Protocol_Version version(readbuf[1], readbuf[2]);
449
450 if(version.is_datagram_protocol() == false) {
451 readbuf.clear();
452 return Record_Header(0);
453 }
454
455 const size_t record_size = make_uint16(readbuf[DTLS_HEADER_SIZE - 2], readbuf[DTLS_HEADER_SIZE - 1]);
456
457 if(record_size > MAX_CIPHERTEXT_SIZE) {
458 // Too large to be valid, ignore it
459 readbuf.clear();
460 return Record_Header(0);
461 }
462
463 if(fill_buffer_to(readbuf, input, input_len, consumed, DTLS_HEADER_SIZE + record_size) != 0) {
464 // Truncated packet?
465 readbuf.clear();
466 return Record_Header(0);
467 }
468
469 BOTAN_ASSERT_EQUAL(static_cast<size_t>(DTLS_HEADER_SIZE) + record_size, readbuf.size(), "Have the full record");
470
471 const Record_Type type = static_cast<Record_Type>(readbuf[0]);
472
473 const uint64_t sequence = load_be<uint64_t>(&readbuf[3], 0);
474 const uint16_t epoch = (sequence >> 48);
475
476 const bool already_seen = sequence_numbers != nullptr && sequence_numbers->already_seen(sequence);
477
478 if(already_seen && !(epoch == 0 && allow_epoch0_restart)) {
479 readbuf.clear();
480 return Record_Header(0);
481 }
482
483 if(epoch == 0) {
484 // Unencrypted initial handshake
485 recbuf.assign(readbuf.begin() + DTLS_HEADER_SIZE, readbuf.begin() + DTLS_HEADER_SIZE + record_size);
486 readbuf.clear();
487 if(sequence_numbers != nullptr) {
488 sequence_numbers->read_accept(sequence);
489 }
490 return Record_Header(sequence, version, type);
491 }
492
493 try {
494 // Otherwise, decrypt, check MAC, return plaintext
495 auto cs = get_cipherstate(epoch);
496
497 BOTAN_ASSERT(cs, "Have cipherstate for this epoch");
498
499 decrypt_record(recbuf, &readbuf[DTLS_HEADER_SIZE], record_size, sequence, version, type, *cs);
500 } catch(std::exception&) {
501 readbuf.clear();
502 return Record_Header(0);
503 }
504
505 if(sequence_numbers != nullptr) {
506 sequence_numbers->read_accept(sequence);
507 }
508
509 readbuf.clear();
510 return Record_Header(sequence, version, type);
511}
512
513} // namespace
514
515Record_Header read_record(bool is_datagram,
516 secure_vector<uint8_t>& readbuf,
517 const uint8_t input[],
518 size_t input_len,
519 size_t& consumed,
521 Connection_Sequence_Numbers* sequence_numbers,
522 const get_cipherstate_fn& get_cipherstate,
523 bool allow_epoch0_restart) {
524 if(is_datagram) {
525 return read_dtls_record(
526 readbuf, input, input_len, consumed, recbuf, sequence_numbers, get_cipherstate, allow_epoch0_restart);
527 } else {
528 return read_tls_record(readbuf, input, input_len, consumed, recbuf, sequence_numbers, get_cipherstate);
529 }
530}
531
532} // namespace Botan::TLS
#define BOTAN_UNUSED
Definition assert.h:144
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ASSERT_EQUAL(expr1, expr2, assertion_made)
Definition assert.h:88
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:62
void set_associated_data(std::span< const uint8_t > ad)
Definition aead.h:59
static std::unique_ptr< AEAD_Mode > create_or_throw(std::string_view algo, Cipher_Dir direction, std::string_view provider="")
Definition aead.cpp:49
static std::unique_ptr< BlockCipher > create_or_throw(std::string_view algo_spec, std::string_view provider="")
void start(std::span< const uint8_t > nonce)
Definition cipher_mode.h:97
void finish(secure_vector< uint8_t > &final_block, size_t offset=0)
virtual size_t output_length(size_t input_length) const =0
static std::unique_ptr< MessageAuthenticationCode > create_or_throw(std::string_view algo_spec, std::string_view provider="")
Definition mac.cpp:147
void randomize(std::span< uint8_t > output)
Definition rng.h:75
size_t nonce_bytes_from_record(Protocol_Version version) const
Nonce_Format nonce_format() const
size_t nonce_bytes_from_handshake() const
std::string mac_algo() const
std::string cipher_algo() const
Nonce_Format nonce_format() const
Definition tls_record.h:73
size_t nonce_bytes_from_handshake() const
Definition tls_record.h:69
Connection_Cipher_State(Protocol_Version version, Connection_Side which_side, bool is_our_side, const Ciphersuite &suite, const Session_Keys &keys, bool uses_encrypt_then_mac)
std::vector< uint8_t > aead_nonce(uint64_t seq, RandomNumberGenerator &rng)
std::vector< uint8_t > format_ad(uint64_t seq, Record_Type type, Protocol_Version version, uint16_t ptext_length)
uint8_t major_version() const
Definition tls_version.h:84
uint8_t minor_version() const
Definition tls_version.h:89
const std::vector< uint8_t > & nonce(Connection_Side side) const
const secure_vector< uint8_t > & aead_key(Connection_Side side) const
Record_Header read_record(bool is_datagram, secure_vector< uint8_t > &readbuf, const uint8_t input[], size_t input_len, size_t &consumed, secure_vector< uint8_t > &recbuf, Connection_Sequence_Numbers *sequence_numbers, const get_cipherstate_fn &get_cipherstate, bool allow_epoch0_restart)
@ MAX_CIPHERTEXT_SIZE
Definition tls_magic.h:34
@ TLS_HEADER_SIZE
Definition tls_magic.h:26
@ DTLS_HEADER_SIZE
Definition tls_magic.h:27
void write_unencrypted_record(secure_vector< uint8_t > &output, Record_Type record_type, Protocol_Version version, uint64_t record_sequence, const uint8_t *message, size_t message_len)
std::function< std::shared_ptr< Connection_Cipher_State >(uint16_t)> get_cipherstate_fn
Definition tls_record.h:154
void write_record(secure_vector< uint8_t > &output, Record_Type record_type, Protocol_Version version, uint64_t record_sequence, const uint8_t *message, size_t message_len, Connection_Cipher_State &cs, RandomNumberGenerator &rng)
constexpr uint8_t get_byte(T input)
Definition loadstor.h:79
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
constexpr void xor_buf(ranges::contiguous_output_range< uint8_t > auto &&out, ranges::contiguous_range< uint8_t > auto &&in)
Definition mem_ops.h:341
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:68
constexpr uint8_t get_byte_var(size_t byte_num, T input)
Definition loadstor.h:69
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:745
constexpr auto load_be(ParamTs &&... params)
Definition loadstor.h:504
constexpr uint16_t make_uint16(uint8_t i0, uint8_t i1)
Definition loadstor.h:92