Botan 3.9.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*
6* Botan is released under the Simplified BSD License (see license.txt)
7*/
8
9#include <botan/internal/tls_record_layer_13.h>
10
11#include <botan/tls_alert.h>
12#include <botan/tls_exceptn.h>
13#include <botan/tls_version.h>
14#include <botan/internal/tls_cipher_state.h>
15#include <botan/internal/tls_reader.h>
16#include <algorithm>
17
18namespace Botan::TLS {
19
20namespace {
21
22template <typename IteratorT>
23bool verify_change_cipher_spec(const IteratorT data, const size_t size) {
24 // RFC 8446 5.
25 // An implementation may receive an unencrypted record of type
26 // change_cipher_spec consisting of the single byte value 0x01
27 // at any time [...]. An implementation which receives any other
28 // change_cipher_spec value or which receives a protected
29 // change_cipher_spec record MUST abort the handshake [...].
30 const size_t expected_fragment_length = 1;
31 const uint8_t expected_fragment_byte = 0x01;
32 return (size == expected_fragment_length && *data == expected_fragment_byte);
33}
34
35Record_Type read_record_type(const uint8_t type_byte) {
36 // RFC 8446 5.
37 // If a TLS implementation receives an unexpected record type,
38 // it MUST terminate the connection with an "unexpected_message" alert.
39 if(type_byte != static_cast<uint8_t>(Record_Type::ApplicationData) &&
40 type_byte != static_cast<uint8_t>(Record_Type::Handshake) &&
41 type_byte != static_cast<uint8_t>(Record_Type::Alert) &&
42 type_byte != static_cast<uint8_t>(Record_Type::ChangeCipherSpec)) {
43 throw TLS_Exception(Alert::UnexpectedMessage, "TLS record type had unexpected value");
44 }
45
46 return static_cast<Record_Type>(type_byte);
47}
48
49/**
50 * RFC 8446 5.1 `TLSPlaintext` without the `fragment` payload data
51 */
52class TLSPlaintext_Header final {
53 public:
54 TLSPlaintext_Header(std::vector<uint8_t> hdr, const bool check_tls13_version) {
55 // NOLINTBEGIN(*-prefer-member-initializer)
56 m_type = read_record_type(hdr[0]);
57 m_legacy_version = Protocol_Version(make_uint16(hdr[1], hdr[2]));
58 m_fragment_length = make_uint16(hdr[3], hdr[4]);
59 m_serialized = std::move(hdr);
60 // NOLINTEND(*-prefer-member-initializer)
61
62 // If no full version check is requested, we just verify the practically
63 // ossified major version number.
64 if(m_legacy_version.major_version() != 0x03) {
65 throw TLS_Exception(Alert::IllegalParameter, "Received unexpected record version");
66 }
67
68 // RFC 8446 5.1
69 // legacy_record_version: MUST be set to 0x0303 for all records
70 // generated by a TLS 1.3 implementation
71 if(check_tls13_version && m_legacy_version.version_code() != 0x0303) {
72 throw TLS_Exception(Alert::IllegalParameter, "Received unexpected record version");
73 }
74
75 // RFC 8446 5.1
76 // Implementations MUST NOT send zero-length fragments of Handshake
77 // types, even if those fragments contain padding.
78 //
79 // Zero-length fragments of Application Data MAY be sent, as they are
80 // potentially useful as a traffic analysis countermeasure.
81 if(m_fragment_length == 0 && type() != Record_Type::ApplicationData) {
82 throw TLS_Exception(Alert::DecodeError, "empty record received");
83 }
84
85 if(m_type == Record_Type::ApplicationData) {
86 // RFC 8446 5.2
87 // The length [...] is the sum of the lengths of the content and the
88 // padding, plus one for the inner content type, plus any expansion
89 // added by the AEAD algorithm. The length MUST NOT exceed 2^14 + 256 bytes.
90 //
91 // Note: Limits imposed by a "record_size_limit" extension do not come
92 // into play here, as those limits are on the plaintext _not_ the
93 // encrypted data. Constricted devices must be able to deal with
94 // data overhead inflicted by the AEAD.
95 if(m_fragment_length > MAX_CIPHERTEXT_SIZE_TLS13) {
96 throw TLS_Exception(Alert::RecordOverflow, "Received an encrypted record that exceeds maximum size");
97 }
98 } else {
99 // RFC 8446 5.1
100 // The length MUST NOT exceed 2^14 bytes. An endpoint that receives a record that
101 // exceeds this length MUST terminate the connection with a "record_overflow" alert.
102 //
103 // RFC 8449 4.
104 // When the "record_size_limit" extension is negotiated, an endpoint
105 // MUST NOT generate a protected record with plaintext that is larger
106 // than the RecordSizeLimit value it receives from its peer.
107 // -> Unprotected messages are not subject to this limit. <-
108 if(m_fragment_length > MAX_PLAINTEXT_SIZE) {
109 throw TLS_Exception(Alert::RecordOverflow, "Received a record that exceeds maximum size");
110 }
111 }
112 }
113
114 TLSPlaintext_Header(const Record_Type record_type,
115 const size_t frgmnt_length,
116 const bool use_compatibility_version) :
117 m_type(record_type),
118 m_legacy_version(use_compatibility_version ? 0x0301 : 0x0303) // RFC 8446 5.1
119 ,
120 m_fragment_length(static_cast<uint16_t>(frgmnt_length)),
121 m_serialized({
122 static_cast<uint8_t>(m_type),
123 m_legacy_version.major_version(),
124 m_legacy_version.minor_version(),
125 get_byte<0>(m_fragment_length),
126 get_byte<1>(m_fragment_length),
127 }) {}
128
129 Record_Type type() const { return m_type; }
130
131 uint16_t fragment_length() const { return m_fragment_length; }
132
133 Protocol_Version legacy_version() const { return m_legacy_version; }
134
135 const std::vector<uint8_t>& serialized() const { return m_serialized; }
136
137 private:
138 Record_Type m_type;
139 Protocol_Version m_legacy_version;
140 uint16_t m_fragment_length;
141 std::vector<uint8_t> m_serialized;
142};
143
144} // namespace
145
147 m_side(side),
148 m_outgoing_record_size_limit(MAX_PLAINTEXT_SIZE + 1 /* content type byte */),
149 m_incoming_record_size_limit(MAX_PLAINTEXT_SIZE + 1 /* content type byte */)
150
151 // RFC 8446 5.1
152 // legacy_record_version: MUST be set to 0x0303 for all records
153 // generated by a TLS 1.3 implementation other than an initial
154 // ClientHello [...], where it MAY also be 0x0301 for compatibility
155 // purposes.
156 //
157 // Additionally, older peers might send other values while requesting a
158 // protocol downgrade. I.e. we need to be able to tolerate/emit legacy
159 // values until we negotiated a TLS 1.3 compliant connection.
160 //
161 // As a client: we may initially emit the compatibility version and
162 // accept a wider range of incoming legacy record versions.
163 // As a server: we start with emitting the specified legacy version of 0x0303
164 // but must also allow a wider range of incoming legacy values.
165 //
166 // Once TLS 1.3 is negotiateed, the implementations will disable these
167 // compatibility modes accordingly or a protocol downgrade will transfer
168 // the marshalling responsibility to our TLS 1.2 implementation.
169 ,
170 m_sending_compat_mode(m_side == Connection_Side::Client),
171 m_receiving_compat_mode(true) {}
172
173void Record_Layer::copy_data(std::span<const uint8_t> data) {
174 m_read_buffer.insert(m_read_buffer.end(), data.begin(), data.end());
175}
176
177std::vector<uint8_t> Record_Layer::prepare_records(const Record_Type type,
178 std::span<const uint8_t> data,
179 Cipher_State* cipher_state) const {
180 // RFC 8446 5.
181 // Note that [change_cipher_spec records] may appear at a point at the
182 // handshake where the implementation is expecting protected records.
183 //
184 // RFC 8446 5.
185 // An implementation which receives [...] a protected change_cipher_spec
186 // record MUST abort the handshake [...].
187 //
188 // ... hence, CHANGE_CIPHER_SPEC is never protected, even if a usable cipher
189 // state was passed to this method.
190 const bool protect = cipher_state != nullptr && type != Record_Type::ChangeCipherSpec;
191
192 // RFC 8446 5.1
194 "Application Data records MUST NOT be written to the wire unprotected");
195
196 // RFC 8446 5.1
197 // "MUST NOT sent zero-length fragments of Handshake types"
198 // "a record with an Alert type MUST contain exactly one message" [of non-zero length]
199 // "Zero-length fragments of Application Data MAY be sent"
200 BOTAN_ASSERT(!data.empty() || type == Record_Type::ApplicationData,
201 "zero-length fragments of types other than application data are not allowed");
202
203 if(type == Record_Type::ChangeCipherSpec && !verify_change_cipher_spec(data.begin(), data.size())) {
204 throw Invalid_Argument("TLS 1.3 deprecated CHANGE_CIPHER_SPEC");
205 }
206
207 std::vector<uint8_t> output;
208
209 // RFC 8446 5.2
210 // type: The TLSPlaintext.type value containing the content type of the record.
211 constexpr size_t content_type_tag_length = 1;
212
213 // RFC 8449 4.
214 // When the "record_size_limit" extension is negotiated, an endpoint
215 // MUST NOT generate a protected record with plaintext that is larger
216 // than the RecordSizeLimit value it receives from its peer.
217 // Unprotected messages are not subject to this limit.
218 const size_t max_plaintext_size =
219 (protect) ? m_outgoing_record_size_limit - content_type_tag_length : static_cast<uint16_t>(MAX_PLAINTEXT_SIZE);
220
221 const auto records = std::max((data.size() + max_plaintext_size - 1) / max_plaintext_size, size_t(1));
222 auto output_length = records * TLS_HEADER_SIZE;
223 if(protect) {
224 // n-1 full records of size max_plaintext_size
225 output_length +=
226 (records - 1) * cipher_state->encrypt_output_length(max_plaintext_size + content_type_tag_length);
227 // last record with size of remaining data
228 output_length += cipher_state->encrypt_output_length(data.size() - ((records - 1) * max_plaintext_size) +
229 content_type_tag_length);
230 } else {
231 output_length += data.size();
232 }
233 output.reserve(output_length);
234
235 size_t pt_offset = 0;
236 size_t to_process = data.size();
237
238 // For protected records we need to write at least one encrypted fragment,
239 // even if the plaintext size is zero. This happens only for Application
240 // Data types.
241 BOTAN_ASSERT_NOMSG(to_process != 0 || protect);
242 // NOLINTNEXTLINE(*-avoid-do-while)
243 do {
244 const size_t pt_size = std::min<size_t>(to_process, max_plaintext_size);
245 const size_t ct_size =
246 (!protect) ? pt_size : cipher_state->encrypt_output_length(pt_size + content_type_tag_length);
247 const auto pt_type = (!protect) ? type : Record_Type::ApplicationData;
248
249 // RFC 8446 5.1
250 // MUST be set to 0x0303 for all records generated by a TLS 1.3
251 // implementation other than an initial ClientHello [...], where
252 // it MAY also be 0x0301 for compatibility purposes.
253 const auto record_header = TLSPlaintext_Header(pt_type, ct_size, m_sending_compat_mode).serialized();
254
255 output.insert(output.end(), record_header.cbegin(), record_header.cend());
256
257 auto pt_fragment = data.subspan(pt_offset, pt_size);
258 if(protect) {
259 secure_vector<uint8_t> fragment;
260 fragment.reserve(ct_size);
261
262 // assemble TLSInnerPlaintext structure
263 fragment.insert(fragment.end(), pt_fragment.begin(), pt_fragment.end());
264 fragment.push_back(static_cast<uint8_t>(type));
265 // TODO: zero padding could go here, see RFC 8446 5.4
266
267 cipher_state->encrypt_record_fragment(record_header, fragment);
268 BOTAN_ASSERT_NOMSG(fragment.size() == ct_size);
269
270 output.insert(output.end(), fragment.cbegin(), fragment.cend());
271 } else {
272 output.insert(output.end(), pt_fragment.begin(), pt_fragment.end());
273 }
274
275 pt_offset += pt_size;
276 to_process -= pt_size;
277 } while(to_process > 0);
278
279 BOTAN_ASSERT_NOMSG(output.size() == output_length);
280 return output;
281}
282
284 if(m_read_buffer.size() < TLS_HEADER_SIZE) {
285 return TLS_HEADER_SIZE - m_read_buffer.size();
286 }
287
288 const auto header_begin = m_read_buffer.cbegin();
289 const auto header_end = header_begin + TLS_HEADER_SIZE;
290
291 // The first received record(s) are likely a client or server hello. To be able to
292 // perform protocol downgrades we must be less vigorous with the record's
293 // legacy version. Hence, `check_tls13_version` is `false` for the first record(s).
294 TLSPlaintext_Header plaintext_header({header_begin, header_end}, !m_receiving_compat_mode);
295
296 // After the key exchange phase of the handshake is completed and record protection is engaged,
297 // cipher_state is set. At this point, only protected traffic (and CCS) is allowed.
298 //
299 // RFC 8446 2.
300 // - Key Exchange: Establish shared keying material and select the
301 // cryptographic parameters. Everything after this phase is
302 // encrypted.
303 // RFC 8446 5.
304 // An implementation may receive an unencrypted [CCS] at any time
305 if(cipher_state != nullptr && plaintext_header.type() != Record_Type::ApplicationData &&
306 plaintext_header.type() != Record_Type::ChangeCipherSpec &&
307 (!cipher_state->must_expect_unprotected_alert_traffic() || plaintext_header.type() != Record_Type::Alert)) {
308 throw TLS_Exception(Alert::UnexpectedMessage, "unprotected record received where protected traffic was expected");
309 }
310
311 if(m_read_buffer.size() < TLS_HEADER_SIZE + plaintext_header.fragment_length()) {
312 return TLS_HEADER_SIZE + plaintext_header.fragment_length() - m_read_buffer.size();
313 }
314
315 const auto fragment_begin = header_end;
316 const auto fragment_end = fragment_begin + plaintext_header.fragment_length();
317
318 if(plaintext_header.type() == Record_Type::ChangeCipherSpec &&
319 !verify_change_cipher_spec(fragment_begin, plaintext_header.fragment_length())) {
320 throw TLS_Exception(Alert::UnexpectedMessage, "malformed change cipher spec record received");
321 }
322
323 Record record(plaintext_header.type(), secure_vector<uint8_t>(fragment_begin, fragment_end));
324 m_read_buffer.erase(header_begin, fragment_end);
325
326 if(record.type == Record_Type::ApplicationData) {
327 if(cipher_state == nullptr) {
328 // This could also mean a misuse of the interface, i.e. failing to provide a valid
329 // cipher_state to parse_records when receiving valid (encrypted) Application Data.
330 throw TLS_Exception(Alert::UnexpectedMessage, "premature Application Data received");
331 }
332
333 if(record.fragment.size() < cipher_state->minimum_decryption_input_length()) {
334 throw TLS_Exception(Alert::BadRecordMac, "incomplete record mac received");
335 }
336
337 if(cipher_state->decrypt_output_length(record.fragment.size()) > m_incoming_record_size_limit) {
338 throw TLS_Exception(Alert::RecordOverflow, "Received an encrypted record that exceeds maximum plaintext size");
339 }
340
341 record.seq_no = cipher_state->decrypt_record_fragment(plaintext_header.serialized(), record.fragment);
342
343 // Remove record padding (RFC 8446 5.4).
344 const auto end_of_content =
345 std::find_if(record.fragment.crbegin(), record.fragment.crend(), [](auto byte) { return byte != 0x00; });
346
347 if(end_of_content == record.fragment.crend()) {
348 // RFC 8446 5.4
349 // If a receiving implementation does not
350 // find a non-zero octet in the cleartext, it MUST terminate the
351 // connection with an "unexpected_message" alert.
352 throw TLS_Exception(Alert::UnexpectedMessage, "No content type found in encrypted record");
353 }
354
355 // hydrate the actual content type from TLSInnerPlaintext
356 record.type = read_record_type(*end_of_content);
357
358 if(record.type == Record_Type::ChangeCipherSpec) {
359 // RFC 8446 5
360 // An implementation [...] which receives a protected change_cipher_spec record MUST
361 // abort the handshake with an "unexpected_message" alert.
362 throw TLS_Exception(Alert::UnexpectedMessage, "protected change cipher spec received");
363 }
364
365 // erase content type and padding
366 record.fragment.erase((end_of_content + 1).base(), record.fragment.cend());
367 }
368
369 return record;
370}
371
372void Record_Layer::set_record_size_limits(const uint16_t outgoing_limit, const uint16_t incoming_limit) {
373 BOTAN_ARG_CHECK(outgoing_limit >= 64, "Invalid outgoing record size limit");
374 BOTAN_ARG_CHECK(incoming_limit >= 64 && incoming_limit <= MAX_PLAINTEXT_SIZE + 1,
375 "Invalid incoming record size limit");
376
377 // RFC 8449 4.
378 // Even if a larger record size limit is provided by a peer, an endpoint
379 // MUST NOT send records larger than the protocol-defined limit, unless
380 // explicitly allowed by a future TLS version or extension.
381 m_outgoing_record_size_limit = std::min(outgoing_limit, static_cast<uint16_t>(MAX_PLAINTEXT_SIZE + 1));
382 m_incoming_record_size_limit = incoming_limit;
383}
384
385} // namespace Botan::TLS
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:62
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
Record_Layer(Connection_Side side)
void copy_data(std::span< const uint8_t > data_from_peer)
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:30
@ MAX_CIPHERTEXT_SIZE_TLS13
Definition tls_magic.h:40
@ TLS_HEADER_SIZE
Definition tls_magic.h:25
constexpr uint8_t get_byte(T input)
Definition loadstor.h:79
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:69
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