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