Botan 3.13.0
Crypto and TLS for C&
tls_record_layer_13.cpp
Go to the documentation of this file.
1/*
2* TLS record layer implementation for TLS 1.3
3* (C) 2022 Jack Lloyd
4* 2022 Hannes Rantzsch, René Meusel - neXenio GmbH
5* 2026 Amos Treiber, René Meusel - Rohde & Schwarz Networks and Cybersecurity GmbH
6*
7* Botan is released under the Simplified BSD License (see license.txt)
8*/
9
10#include <botan/internal/tls_record_layer_13.h>
11
12#include <botan/tls_alert.h>
13#include <botan/tls_exceptn.h>
14#include <botan/tls_policy.h>
15#include <botan/tls_version.h>
16#include <botan/internal/ct_utils.h>
17#include <botan/internal/loadstor.h>
18#include <botan/internal/tls_cipher_state.h>
19#include <algorithm>
20
21namespace Botan::TLS {
22
23namespace {
24
25template <typename IteratorT>
26bool verify_change_cipher_spec(const IteratorT data, const size_t size) {
27 // RFC 8446 5.
28 // An implementation may receive an unencrypted record of type
29 // change_cipher_spec consisting of the single byte value 0x01
30 // at any time [...]. An implementation which receives any other
31 // change_cipher_spec value or which receives a protected
32 // change_cipher_spec record MUST abort the handshake [...].
33 const size_t expected_fragment_length = 1;
34 const uint8_t expected_fragment_byte = 0x01;
35 return (size == expected_fragment_length && *data == expected_fragment_byte);
36}
37
38Record_Type read_record_type(const uint8_t type_byte) {
39 // RFC 8446 5.
40 // If a TLS implementation receives an unexpected record type,
41 // it MUST terminate the connection with an "unexpected_message" alert.
42 if(type_byte != static_cast<uint8_t>(Record_Type::ApplicationData) &&
43 type_byte != static_cast<uint8_t>(Record_Type::Handshake) &&
44 type_byte != static_cast<uint8_t>(Record_Type::Alert) &&
45 type_byte != static_cast<uint8_t>(Record_Type::ChangeCipherSpec)) {
46 throw TLS_Exception(Alert::UnexpectedMessage, "TLS record type had unexpected value");
47 }
48
49 return static_cast<Record_Type>(type_byte);
50}
51
52/**
53 * RFC 8446 5.1 `TLSPlaintext` without the `fragment` payload data
54 */
55class TLSPlaintext_Header final {
56 public:
57 TLSPlaintext_Header(std::vector<uint8_t> hdr, const bool check_tls13_version) {
58 // NOLINTBEGIN(*-prefer-member-initializer)
59 m_type = read_record_type(hdr[0]);
60 m_legacy_version = Protocol_Version(make_uint16(hdr[1], hdr[2]));
61 m_fragment_length = make_uint16(hdr[3], hdr[4]);
62 m_serialized = std::move(hdr);
63 // NOLINTEND(*-prefer-member-initializer)
64
65 // If no full version check is requested, we just verify the practically
66 // ossified major version number.
67 if(m_legacy_version.major_version() != 0x03) {
68 throw TLS_Exception(Alert::IllegalParameter, "Received unexpected record version");
69 }
70
71 // RFC 8446 5.1
72 // legacy_record_version: MUST be set to 0x0303 for all records
73 // generated by a TLS 1.3 implementation
74 if(check_tls13_version && m_legacy_version.version_code() != 0x0303) {
75 throw TLS_Exception(Alert::IllegalParameter, "Received unexpected record version");
76 }
77
78 // RFC 8446 5.1
79 // Implementations MUST NOT send zero-length fragments of Handshake
80 // types, even if those fragments contain padding.
81 //
82 // Zero-length fragments of Application Data MAY be sent, as they are
83 // potentially useful as a traffic analysis countermeasure.
84 if(m_fragment_length == 0 && type() != Record_Type::ApplicationData) {
85 throw TLS_Exception(Alert::DecodeError, "empty record received");
86 }
87
88 if(m_type == Record_Type::ApplicationData) {
89 // RFC 8446 5.2
90 // The length [...] is the sum of the lengths of the content and the
91 // padding, plus one for the inner content type, plus any expansion
92 // added by the AEAD algorithm. The length MUST NOT exceed 2^14 + 256 bytes.
93 //
94 // Note: Limits imposed by a "record_size_limit" extension do not come
95 // into play here, as those limits are on the plaintext _not_ the
96 // encrypted data. Constricted devices must be able to deal with
97 // data overhead inflicted by the AEAD.
98 if(m_fragment_length > MAX_CIPHERTEXT_SIZE_TLS13) {
99 throw TLS_Exception(Alert::RecordOverflow, "Received an encrypted record that exceeds maximum size");
100 }
101 } else {
102 // RFC 8446 5.1
103 // The length MUST NOT exceed 2^14 bytes. An endpoint that receives a record that
104 // exceeds this length MUST terminate the connection with a "record_overflow" alert.
105 //
106 // RFC 8449 4.
107 // When the "record_size_limit" extension is negotiated, an endpoint
108 // MUST NOT generate a protected record with plaintext that is larger
109 // than the RecordSizeLimit value it receives from its peer.
110 // -> Unprotected messages are not subject to this limit. <-
111 if(m_fragment_length > MAX_PLAINTEXT_SIZE) {
112 throw TLS_Exception(Alert::RecordOverflow, "Received a record that exceeds maximum size");
113 }
114 }
115 }
116
117 TLSPlaintext_Header(const Record_Type record_type,
118 const size_t frgmnt_length,
119 const bool use_compatibility_version) :
120 m_type(record_type),
121 m_legacy_version(use_compatibility_version ? 0x0301 : 0x0303) // RFC 8446 5.1
122 ,
123 m_fragment_length(static_cast<uint16_t>(frgmnt_length)),
124 m_serialized({
125 static_cast<uint8_t>(m_type),
126 m_legacy_version.major_version(),
127 m_legacy_version.minor_version(),
128 get_byte<0>(m_fragment_length),
129 get_byte<1>(m_fragment_length),
130 }) {}
131
132 Record_Type type() const { return m_type; }
133
134 uint16_t fragment_length() const { return m_fragment_length; }
135
136 Protocol_Version legacy_version() const { return m_legacy_version; }
137
138 const std::vector<uint8_t>& serialized() const { return m_serialized; }
139
140 private:
141 Record_Type m_type;
142 Protocol_Version m_legacy_version;
143 uint16_t m_fragment_length;
144 std::vector<uint8_t> m_serialized;
145};
146
147} // namespace
148
149Record_Layer::Record_Layer(Connection_Side side, std::shared_ptr<const Policy> policy) :
150 m_side(side),
151 m_policy(std::move(policy)),
152 m_outgoing_record_size_limit(MAX_PLAINTEXT_SIZE + 1 /* content type byte */),
153 m_incoming_record_size_limit(MAX_PLAINTEXT_SIZE + 1 /* content type byte */)
154
155 // RFC 8446 5.1
156 // legacy_record_version: MUST be set to 0x0303 for all records
157 // generated by a TLS 1.3 implementation other than an initial
158 // ClientHello [...], where it MAY also be 0x0301 for compatibility
159 // purposes.
160 //
161 // Additionally, older peers might send other values while requesting a
162 // protocol downgrade. I.e. we need to be able to tolerate/emit legacy
163 // values until we negotiated a TLS 1.3 compliant connection.
164 //
165 // As a client: we may initially emit the compatibility version and
166 // accept a wider range of incoming legacy record versions.
167 // As a server: we start with emitting the specified legacy version of 0x0303
168 // but must also allow a wider range of incoming legacy values.
169 //
170 // Once TLS 1.3 is negotiateed, the implementations will disable these
171 // compatibility modes accordingly or a protocol downgrade will transfer
172 // the marshalling responsibility to our TLS 1.2 implementation.
173 ,
174 m_sending_compat_mode(m_side == Connection_Side::Client),
175 m_receiving_compat_mode(true) {
176 BOTAN_ASSERT_NONNULL(m_policy);
177}
178
179void Record_Layer::copy_data(std::span<const uint8_t> data) {
180 // Compact consumed data before appending new data
181 BOTAN_ASSERT_NOMSG(m_read_offset <= m_read_buffer.size());
182 if(m_read_offset > 0) {
183 m_read_buffer.erase(m_read_buffer.begin(), m_read_buffer.begin() + m_read_offset);
184 m_read_offset = 0;
185 }
186
187 m_read_buffer.insert(m_read_buffer.end(), data.begin(), data.end());
188}
189
190std::vector<uint8_t> Record_Layer::prepare_records(const Record_Type type,
191 std::span<const uint8_t> data,
192 Cipher_State* cipher_state) const {
193 // RFC 8446 5.
194 // Note that [change_cipher_spec records] may appear at a point at the
195 // handshake where the implementation is expecting protected records.
196 //
197 // RFC 8446 5.
198 // An implementation which receives [...] a protected change_cipher_spec
199 // record MUST abort the handshake [...].
200 //
201 // ... hence, CHANGE_CIPHER_SPEC is never protected, even if a usable cipher
202 // state was passed to this method.
203 const bool protect = cipher_state != nullptr && type != Record_Type::ChangeCipherSpec;
204
205 // RFC 8446 5.1
207 "Application Data records MUST NOT be written to the wire unprotected");
208
209 // RFC 8446 5.1
210 // "MUST NOT sent zero-length fragments of Handshake types"
211 // "a record with an Alert type MUST contain exactly one message" [of non-zero length]
212 // "Zero-length fragments of Application Data MAY be sent"
213 BOTAN_ASSERT(!data.empty() || type == Record_Type::ApplicationData,
214 "zero-length fragments of types other than application data are not allowed");
215
216 if(type == Record_Type::ChangeCipherSpec && !verify_change_cipher_spec(data.begin(), data.size())) {
217 throw Invalid_Argument("TLS 1.3 deprecated CHANGE_CIPHER_SPEC");
218 }
219
220 std::vector<uint8_t> output;
221
222 // RFC 8446 5.2
223 // type: The TLSPlaintext.type value containing the content type of the record.
224 constexpr size_t content_type_tag_length = 1;
225
226 // RFC 8449 4.
227 // When the "record_size_limit" extension is negotiated, an endpoint
228 // MUST NOT generate a protected record with plaintext that is larger
229 // than the RecordSizeLimit value it receives from its peer.
230 // Unprotected messages are not subject to this limit.
231 const size_t max_plaintext_size =
232 (protect) ? m_outgoing_record_size_limit - content_type_tag_length : static_cast<uint16_t>(MAX_PLAINTEXT_SIZE);
233
234 const auto records = std::max((data.size() + max_plaintext_size - 1) / max_plaintext_size, size_t(1));
235 auto output_length = records * TLS_HEADER_SIZE;
236
237 // Policy-requested padding (RFC 9846 5.4) is applied to the final record
238 // only; all preceding records are filled to the maximum plaintext size
239 // already, leaving no room for padding within the record size limit.
240 size_t final_record_padding = 0;
241
242 if(protect) {
243 // n-1 full records of size max_plaintext_size
244 output_length +=
245 (records - 1) * cipher_state->encrypt_output_length(max_plaintext_size + content_type_tag_length);
246 // last record with size of remaining data
247 const auto remaining_bytes = data.size() - ((records - 1) * max_plaintext_size) + content_type_tag_length;
248 final_record_padding = std::min<size_t>(m_policy->record_padding_bytes(remaining_bytes),
249 m_outgoing_record_size_limit - remaining_bytes);
250 output_length += cipher_state->encrypt_output_length(remaining_bytes + final_record_padding);
251 } else {
252 output_length += data.size();
253 }
254 output.reserve(output_length);
255
256 size_t pt_offset = 0;
257 size_t to_process = data.size();
258
259 // For protected records we need to write at least one encrypted fragment,
260 // even if the plaintext size is zero. This happens only for Application
261 // Data types.
262 BOTAN_ASSERT_NOMSG(to_process != 0 || protect);
263 // NOLINTNEXTLINE(*-avoid-do-while)
264 do {
265 const size_t pt_size = std::min<size_t>(to_process, max_plaintext_size);
266 const size_t pt_size_with_type = pt_size + content_type_tag_length;
267 const bool final_record = (pt_size == to_process);
268 const size_t pt_size_with_type_and_padding = pt_size_with_type + (final_record ? final_record_padding : 0);
270 pt_size_with_type_and_padding <= m_outgoing_record_size_limit,
271 "Padded record size is within the negotiated record size limit");
272
273 const size_t ct_size = (!protect) ? pt_size : cipher_state->encrypt_output_length(pt_size_with_type_and_padding);
274 const auto pt_type = (!protect) ? type : Record_Type::ApplicationData;
275
276 // RFC 8446 5.1
277 // MUST be set to 0x0303 for all records generated by a TLS 1.3
278 // implementation other than an initial ClientHello [...], where
279 // it MAY also be 0x0301 for compatibility purposes.
280 const auto record_header = TLSPlaintext_Header(pt_type, ct_size, m_sending_compat_mode).serialized();
281
282 output.insert(output.end(), record_header.cbegin(), record_header.cend());
283
284 auto pt_fragment = data.subspan(pt_offset, pt_size);
285 if(protect) {
286 secure_vector<uint8_t> fragment;
287 fragment.reserve(ct_size);
288
289 // assemble TLSInnerPlaintext structure
290 fragment.insert(fragment.end(), pt_fragment.begin(), pt_fragment.end());
291 fragment.push_back(static_cast<uint8_t>(type));
292
293 // RFC 9846 5.4
294 // When generating a TLSCiphertext record, implementations MAY
295 // choose to pad. [...] Implementations MUST set the padding octets
296 // to all zeros before encrypting.
297 fragment.insert(fragment.end(), pt_size_with_type_and_padding - pt_size_with_type, 0x00);
298
299 cipher_state->encrypt_record_fragment(record_header, fragment);
300 BOTAN_ASSERT_NOMSG(fragment.size() == ct_size);
301
302 output.insert(output.end(), fragment.cbegin(), fragment.cend());
303 } else {
304 output.insert(output.end(), pt_fragment.begin(), pt_fragment.end());
305 }
306
307 pt_offset += pt_size;
308 to_process -= pt_size;
309 } while(to_process > 0);
310
311 BOTAN_ASSERT_NOMSG(output.size() == output_length);
312 return output;
313}
314
316 // Special case: on the record boundary we don't actually need any more data
317 // and we also don't want to be the know-it-all that now demands exactly
318 // enough bytes to start parsing the next record header.
319 if(m_read_buffer.empty()) {
320 return BytesNeeded(0);
321 }
322
323 const auto remaining = m_read_buffer.size() - m_read_offset;
324
325 if(remaining < TLS_HEADER_SIZE) {
326 return TLS_HEADER_SIZE - remaining;
327 }
328
329 const auto header_begin = m_read_buffer.cbegin() + m_read_offset;
330 const auto header_end = header_begin + TLS_HEADER_SIZE;
331
332 // The first received record(s) are likely a client or server hello. To be able to
333 // perform protocol downgrades we must be less vigorous with the record's
334 // legacy version. Hence, `check_tls13_version` is `false` for the first record(s).
335 const TLSPlaintext_Header plaintext_header({header_begin, header_end}, !m_receiving_compat_mode);
336
337 // After the key exchange phase of the handshake is completed and record protection is engaged,
338 // cipher_state is set. At this point, only protected traffic (and CCS) is allowed.
339 //
340 // RFC 8446 2.
341 // - Key Exchange: Establish shared keying material and select the
342 // cryptographic parameters. Everything after this phase is
343 // encrypted.
344 // RFC 8446 5.
345 // An implementation may receive an unencrypted [CCS] at any time
346 if(cipher_state != nullptr && plaintext_header.type() != Record_Type::ApplicationData &&
347 plaintext_header.type() != Record_Type::ChangeCipherSpec &&
348 (!cipher_state->must_expect_unprotected_alert_traffic() || plaintext_header.type() != Record_Type::Alert)) {
349 throw TLS_Exception(Alert::UnexpectedMessage, "unprotected record received where protected traffic was expected");
350 }
351
352 if(remaining < TLS_HEADER_SIZE + plaintext_header.fragment_length()) {
353 return TLS_HEADER_SIZE + plaintext_header.fragment_length() - remaining;
354 }
355
356 const auto fragment_begin = header_end;
357 const auto fragment_end = fragment_begin + plaintext_header.fragment_length();
358
359 if(plaintext_header.type() == Record_Type::ChangeCipherSpec &&
360 !verify_change_cipher_spec(fragment_begin, plaintext_header.fragment_length())) {
361 throw TLS_Exception(Alert::UnexpectedMessage, "malformed change cipher spec record received");
362 }
363
364 Record record(plaintext_header.type(), secure_vector<uint8_t>(fragment_begin, fragment_end));
365 m_read_offset += TLS_HEADER_SIZE + plaintext_header.fragment_length();
366
367 // If all buffered data has been consumed, release the buffer memory
368 // to avoid retaining peak allocation on idle connections.
369 if(m_read_offset == m_read_buffer.size()) {
370 zap(m_read_buffer);
371 m_read_offset = 0;
372 }
373
374 if(record.type == Record_Type::ApplicationData) {
375 if(cipher_state == nullptr) {
376 // This could also mean a misuse of the interface, i.e. failing to provide a valid
377 // cipher_state to parse_records when receiving valid (encrypted) Application Data.
378 throw TLS_Exception(Alert::UnexpectedMessage, "premature Application Data received");
379 }
380
381 if(record.fragment.size() < cipher_state->minimum_decryption_input_length()) {
382 throw TLS_Exception(Alert::BadRecordMac, "incomplete record mac received");
383 }
384
385 if(cipher_state->decrypt_output_length(record.fragment.size()) > m_incoming_record_size_limit) {
386 throw TLS_Exception(Alert::RecordOverflow, "Received an encrypted record that exceeds maximum plaintext size");
387 }
388
389 record.seq_no = cipher_state->decrypt_record_fragment(plaintext_header.serialized(), record.fragment);
390
391 // Remove record padding (RFC 8446 5.4). The TLSInnerPlaintext layout is
392 // content || content_type || zero_padding
393 auto seen_nonzero = CT::Mask<uint8_t>::cleared();
394 uint8_t content_type_byte = 0;
395 size_t content_index = 0;
396 for(size_t i = record.fragment.size(); i-- > 0;) {
397 const uint8_t b = record.fragment[i];
398 const auto byte_is_nonzero = CT::Mask<uint8_t>::expand(b);
399 // Set on the first non-zero byte we encounter scanning right-to-left.
400 const auto first_nonzero = byte_is_nonzero & ~seen_nonzero;
401 content_type_byte = first_nonzero.select(b, content_type_byte);
402 content_index = CT::Mask<size_t>::expand(first_nonzero.value()).select(i, content_index);
403 seen_nonzero |= byte_is_nonzero;
404 }
405
406 if(!seen_nonzero.as_bool()) {
407 // RFC 8446 5.4
408 // If a receiving implementation does not
409 // find a non-zero octet in the cleartext, it MUST terminate the
410 // connection with an "unexpected_message" alert.
411 throw TLS_Exception(Alert::UnexpectedMessage, "No content type found in encrypted record");
412 }
413
414 // hydrate the actual content type from TLSInnerPlaintext
415 record.type = read_record_type(content_type_byte);
416
417 if(record.type == Record_Type::ChangeCipherSpec) {
418 // RFC 8446 5
419 // An implementation [...] which receives a protected change_cipher_spec record MUST
420 // abort the handshake with an "unexpected_message" alert.
421 throw TLS_Exception(Alert::UnexpectedMessage, "protected change cipher spec received");
422 }
423
424 // Truncate to drop the content_type byte and padding. resize() on a
425 // vector of trivially-destructible elements is bookkeeping-only and
426 // does not allocate or iterate over the dropped suffix.
427 record.fragment.resize(content_index);
428
429 // RFC 8446 5.4
430 // Implementations MUST NOT send Handshake and Alert records that have
431 // a zero-length TLSInnerPlaintext.content; if such a message is
432 // received, the receiving implementation MUST terminate the connection
433 // with an "unexpected_message" alert.
434 if(record.fragment.empty() && record.type != Record_Type::ApplicationData) {
435 throw TLS_Exception(Alert::UnexpectedMessage,
436 "Received a protected record with empty TLSInnerPlaintext content");
437 }
438 }
439
440 return record;
441}
442
443void Record_Layer::set_record_size_limits(const uint16_t outgoing_limit, const uint16_t incoming_limit) {
444 BOTAN_ARG_CHECK(outgoing_limit >= 64, "Invalid outgoing record size limit");
445 BOTAN_ARG_CHECK(incoming_limit >= 64 && incoming_limit <= MAX_PLAINTEXT_SIZE + 1,
446 "Invalid incoming record size limit");
447
448 // RFC 8449 4.
449 // Even if a larger record size limit is provided by a peer, an endpoint
450 // MUST NOT send records larger than the protocol-defined limit, unless
451 // explicitly allowed by a future TLS version or extension.
452 m_outgoing_record_size_limit = std::min(outgoing_limit, static_cast<uint16_t>(MAX_PLAINTEXT_SIZE + 1));
453 m_incoming_record_size_limit = incoming_limit;
454}
455
456} // namespace Botan::TLS
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ASSERT_NONNULL(ptr)
Definition assert.h:114
#define BOTAN_ASSERT_IMPLICATION(expr1, expr2, msg)
Definition assert.h:101
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:62
static constexpr Mask< T > expand(T v)
Definition ct_utils.h:392
static constexpr Mask< T > cleared()
Definition ct_utils.h:387
uint64_t decrypt_record_fragment(const std::vector< uint8_t > &header, secure_vector< uint8_t > &encrypted_fragment)
size_t minimum_decryption_input_length() const
bool must_expect_unprotected_alert_traffic() const
uint64_t encrypt_record_fragment(const std::vector< uint8_t > &header, secure_vector< uint8_t > &fragment)
size_t encrypt_output_length(size_t input_length) const
size_t decrypt_output_length(size_t input_length) const
std::variant< BytesNeeded, ResT > ReadResult
void copy_data(std::span< const uint8_t > data_from_peer)
Record_Layer(Connection_Side side, std::shared_ptr< const Policy > policy)
std::vector< uint8_t > prepare_records(Record_Type type, std::span< const uint8_t > data, Cipher_State *cipher_state=nullptr) const
void set_record_size_limits(uint16_t outgoing_limit, uint16_t incoming_limit)
ReadResult< Record > next_record(Cipher_State *cipher_state=nullptr)
@ MAX_PLAINTEXT_SIZE
Definition tls_magic.h:35
@ MAX_CIPHERTEXT_SIZE_TLS13
Definition tls_magic.h:45
@ TLS_HEADER_SIZE
Definition tls_magic.h:30
constexpr uint8_t get_byte(T input)
Definition loadstor.h:79
void zap(std::vector< T, Alloc > &vec)
Definition secmem.h:261
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128
constexpr uint16_t make_uint16(uint8_t i0, uint8_t i1)
Definition loadstor.h:92
std::optional< uint64_t > seq_no
secure_vector< uint8_t > fragment