/src/botan/src/lib/tls/msg_client_hello.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * TLS Hello Request and Client Hello Messages |
3 | | * (C) 2004-2011,2015,2016 Jack Lloyd |
4 | | * 2016 Matthias Gierlings |
5 | | * 2017 Harry Reimann, Rohde & Schwarz Cybersecurity |
6 | | * 2021 Elektrobit Automotive GmbH |
7 | | * 2022 René Meusel, Hannes Rantzsch - neXenio GmbH |
8 | | * |
9 | | * Botan is released under the Simplified BSD License (see license.txt) |
10 | | */ |
11 | | |
12 | | |
13 | | #include <botan/tls_exceptn.h> |
14 | | #include <botan/tls_messages.h> |
15 | | #include <botan/tls_callbacks.h> |
16 | | #include <botan/rng.h> |
17 | | #include <botan/hash.h> |
18 | | #include <botan/credentials_manager.h> |
19 | | #include <botan/tls_version.h> |
20 | | |
21 | | #include <botan/internal/stl_util.h> |
22 | | #include <botan/internal/tls_reader.h> |
23 | | #include <botan/internal/tls_session_key.h> |
24 | | #include <botan/internal/tls_handshake_io.h> |
25 | | #include <botan/internal/tls_handshake_hash.h> |
26 | | |
27 | | #ifdef BOTAN_HAS_TLS_13 |
28 | | #include <botan/internal/tls_transcript_hash_13.h> |
29 | | #include <botan/internal/tls_handshake_layer_13.h> |
30 | | #endif |
31 | | |
32 | | #include <chrono> |
33 | | #include <iterator> |
34 | | |
35 | | namespace Botan::TLS { |
36 | | |
37 | | enum |
38 | | { |
39 | | TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF, |
40 | | }; |
41 | | |
42 | | std::vector<uint8_t> make_hello_random(RandomNumberGenerator& rng, |
43 | | Callbacks& cb, |
44 | | const Policy& policy) |
45 | 44.3k | { |
46 | 44.3k | std::vector<uint8_t> buf(32); |
47 | 44.3k | rng.randomize(buf.data(), buf.size()); |
48 | | |
49 | 44.3k | if(policy.hash_hello_random()) |
50 | 44.3k | { |
51 | 44.3k | auto sha256 = HashFunction::create_or_throw("SHA-256"); |
52 | 44.3k | sha256->update(buf); |
53 | 44.3k | sha256->final(buf); |
54 | 44.3k | } |
55 | | |
56 | | // TLS 1.3 does not require the insertion of a timestamp in the client hello |
57 | | // random. When offering both TLS 1.2 and 1.3 we nevertheless comply with the |
58 | | // legacy specification. |
59 | 44.3k | if(policy.include_time_in_hello_random() && (policy.allow_tls12() || policy.allow_dtls12())) |
60 | 44.3k | { |
61 | 44.3k | const uint32_t time32 = static_cast<uint32_t>( |
62 | 44.3k | std::chrono::system_clock::to_time_t(cb.tls_current_timestamp())); |
63 | | |
64 | 44.3k | store_be(time32, buf.data()); |
65 | 44.3k | } |
66 | | |
67 | 44.3k | return buf; |
68 | 44.3k | } |
69 | | |
70 | | /** |
71 | | * Version-agnostic internal client hello data container that allows |
72 | | * parsing Client_Hello messages without prior knowledge of the contained |
73 | | * protocol version. |
74 | | */ |
75 | | class Client_Hello_Internal |
76 | | { |
77 | | public: |
78 | 2.39k | Client_Hello_Internal() : comp_methods({0}) {} |
79 | | |
80 | | Client_Hello_Internal(const std::vector<uint8_t>& buf) |
81 | 35.1k | { |
82 | 35.1k | if(buf.size() < 41) |
83 | 60 | { throw Decoding_Error("Client_Hello: Packet corrupted"); } |
84 | | |
85 | 35.1k | TLS_Data_Reader reader("ClientHello", buf); |
86 | | |
87 | 35.1k | const uint8_t major_version = reader.get_byte(); |
88 | 35.1k | const uint8_t minor_version = reader.get_byte(); |
89 | | |
90 | 35.1k | legacy_version = Protocol_Version(major_version, minor_version); |
91 | 35.1k | random = reader.get_fixed<uint8_t>(32); |
92 | 35.1k | session_id = reader.get_range<uint8_t>(1, 0, 32); |
93 | | |
94 | 35.1k | if(legacy_version.is_datagram_protocol()) |
95 | 5.54k | { |
96 | 5.54k | auto sha256 = HashFunction::create_or_throw("SHA-256"); |
97 | 5.54k | sha256->update(reader.get_data_read_so_far()); |
98 | | |
99 | 5.54k | hello_cookie = reader.get_range<uint8_t>(1, 0, 255); |
100 | | |
101 | 5.54k | sha256->update(reader.get_remaining()); |
102 | 5.54k | cookie_input_bits = sha256->final_stdvec(); |
103 | 5.54k | } |
104 | | |
105 | 35.1k | suites = reader.get_range_vector<uint16_t>(2, 1, 32767); |
106 | 35.1k | comp_methods = reader.get_range_vector<uint8_t>(1, 1, 255); |
107 | | |
108 | 35.1k | extensions.deserialize(reader, Connection_Side::CLIENT, Handshake_Type::CLIENT_HELLO); |
109 | 35.1k | } |
110 | | |
111 | | /** |
112 | | * This distinguishes between a TLS 1.3 compliant Client Hello (containing |
113 | | * the "supported_version" extension) and legacy Client Hello messages. |
114 | | * |
115 | | * @return TLS 1.3 if the Client Hello contains "supported_versions", or |
116 | | * the content of the "legacy_version" version field if it |
117 | | * indicates (D)TLS 1.2 or older, or |
118 | | * (D)TLS 1.2 if the "legacy_version" was some other odd value. |
119 | | */ |
120 | | Protocol_Version version() const |
121 | 7.57k | { |
122 | | // RFC 8446 4.2.1 |
123 | | // If [the "supported_versions"] extension is not present, servers |
124 | | // which are compliant with this specification and which also support |
125 | | // TLS 1.2 MUST negotiate TLS 1.2 or prior as specified in [RFC5246], |
126 | | // even if ClientHello.legacy_version is 0x0304 or later. |
127 | | // |
128 | | // RFC 8446 4.2.1 |
129 | | // Servers MUST be prepared to receive ClientHellos that include |
130 | | // [the supported_versions] extension but do not include 0x0304 in |
131 | | // the list of versions. |
132 | | // |
133 | | // RFC 8446 4.1.2 |
134 | | // TLS 1.3 ClientHellos are identified as having a legacy_version of |
135 | | // 0x0303 and a supported_versions extension present with 0x0304 as |
136 | | // the highest version indicated therein. |
137 | 7.57k | if(!extensions.has<Supported_Versions>() || |
138 | 7.57k | !extensions.get<Supported_Versions>()->supports(Protocol_Version::TLS_V13)) |
139 | 6.80k | { |
140 | | // The exact legacy_version is ignored we just inspect it to |
141 | | // distinguish TLS and DTLS. |
142 | 6.80k | return (legacy_version.is_datagram_protocol()) |
143 | 6.80k | ? Protocol_Version::DTLS_V12 |
144 | 6.80k | : Protocol_Version::TLS_V12; |
145 | 6.80k | } |
146 | | |
147 | | // Note: The Client_Hello_13 class will make sure that legacy_version |
148 | | // is exactly 0x0303 (aka ossified TLS 1.2) |
149 | 772 | return Protocol_Version::TLS_V13; |
150 | 7.57k | } |
151 | | |
152 | | public: |
153 | | Protocol_Version legacy_version; |
154 | | std::vector<uint8_t> session_id; |
155 | | std::vector<uint8_t> random; |
156 | | std::vector<uint16_t> suites; |
157 | | std::vector<uint8_t> comp_methods; |
158 | | Extensions extensions; |
159 | | |
160 | | std::vector<uint8_t> hello_cookie; // DTLS only |
161 | | std::vector<uint8_t> cookie_input_bits; // DTLS only |
162 | | }; |
163 | | |
164 | | |
165 | 29.7k | Client_Hello::Client_Hello(Client_Hello&&) = default; |
166 | 0 | Client_Hello& Client_Hello::operator=(Client_Hello&&) = default; |
167 | | |
168 | 63.8k | Client_Hello::~Client_Hello() = default; |
169 | | |
170 | | Client_Hello::Client_Hello() |
171 | 2.39k | : m_data(std::make_unique<Client_Hello_Internal>()) {} |
172 | | |
173 | | /* |
174 | | * Read a counterparty client hello |
175 | | */ |
176 | | Client_Hello::Client_Hello(std::unique_ptr<Client_Hello_Internal> data) |
177 | | : m_data(std::move(data)) |
178 | 31.7k | { |
179 | 31.7k | BOTAN_ASSERT_NONNULL(m_data); |
180 | 31.7k | } |
181 | | |
182 | | Handshake_Type Client_Hello::type() const |
183 | 7.18k | { |
184 | 7.18k | return CLIENT_HELLO; |
185 | 7.18k | } |
186 | | |
187 | | Protocol_Version Client_Hello::legacy_version() const |
188 | 23.6k | { |
189 | 23.6k | return m_data->legacy_version; |
190 | 23.6k | } |
191 | | |
192 | | const std::vector<uint8_t>& Client_Hello::random() const |
193 | 27.2k | { |
194 | 27.2k | return m_data->random; |
195 | 27.2k | } |
196 | | |
197 | | const std::vector<uint8_t>& Client_Hello::session_id() const |
198 | 21.1k | { |
199 | 21.1k | return m_data->session_id; |
200 | 21.1k | } |
201 | | |
202 | | const std::vector<uint8_t>& Client_Hello::compression_methods() const |
203 | 23.5k | { |
204 | 23.5k | return m_data->comp_methods; |
205 | 23.5k | } |
206 | | |
207 | | const std::vector<uint16_t>& Client_Hello::ciphersuites() const |
208 | 21.1k | { |
209 | 21.1k | return m_data->suites; |
210 | 21.1k | } |
211 | | |
212 | | std::set<Extension_Code> Client_Hello::extension_types() const |
213 | 19.6k | { |
214 | 19.6k | return m_data->extensions.extension_types(); |
215 | 19.6k | } |
216 | | |
217 | | const Extensions& Client_Hello::extensions() const |
218 | 21.1k | { |
219 | 21.1k | return m_data->extensions; |
220 | 21.1k | } |
221 | | |
222 | | void Client_Hello_12::update_hello_cookie(const Hello_Verify_Request& hello_verify) |
223 | 0 | { |
224 | 0 | BOTAN_STATE_CHECK(m_data->legacy_version.is_datagram_protocol()); |
225 | |
|
226 | 0 | m_data->hello_cookie = hello_verify.cookie(); |
227 | 0 | } |
228 | | |
229 | | /* |
230 | | * Serialize a Client Hello message |
231 | | */ |
232 | | std::vector<uint8_t> Client_Hello::serialize() const |
233 | 2.39k | { |
234 | 2.39k | std::vector<uint8_t> buf; |
235 | 2.39k | buf.reserve(1024); // working around GCC warning |
236 | | |
237 | 2.39k | buf.push_back(m_data->legacy_version.major_version()); |
238 | 2.39k | buf.push_back(m_data->legacy_version.minor_version()); |
239 | 2.39k | buf += m_data->random; |
240 | | |
241 | 2.39k | append_tls_length_value(buf, m_data->session_id, 1); |
242 | | |
243 | 2.39k | if(m_data->legacy_version.is_datagram_protocol()) |
244 | 0 | { append_tls_length_value(buf, m_data->hello_cookie, 1); } |
245 | | |
246 | 2.39k | append_tls_length_value(buf, m_data->suites, 2); |
247 | 2.39k | append_tls_length_value(buf, m_data->comp_methods, 1); |
248 | | |
249 | | /* |
250 | | * May not want to send extensions at all in some cases. If so, |
251 | | * should include SCSV value (if reneg info is empty, if not we are |
252 | | * renegotiating with a modern server) |
253 | | */ |
254 | | |
255 | 2.39k | buf += m_data->extensions.serialize(Connection_Side::CLIENT); |
256 | | |
257 | 2.39k | return buf; |
258 | 2.39k | } |
259 | | |
260 | | std::vector<uint8_t> Client_Hello::cookie_input_data() const |
261 | 2.39k | { |
262 | 2.39k | BOTAN_STATE_CHECK(!m_data->cookie_input_bits.empty()); |
263 | | |
264 | 2.39k | return m_data->cookie_input_bits; |
265 | 2.39k | } |
266 | | |
267 | | /* |
268 | | * Check if we offered this ciphersuite |
269 | | */ |
270 | | bool Client_Hello::offered_suite(uint16_t ciphersuite) const |
271 | 30.9k | { |
272 | 30.9k | return std::find(m_data->suites.cbegin(), m_data->suites.cend(), ciphersuite) != m_data->suites.cend(); |
273 | 30.9k | } |
274 | | |
275 | | std::vector<Signature_Scheme> Client_Hello::signature_schemes() const |
276 | 115 | { |
277 | 115 | if(Signature_Algorithms* sigs = m_data->extensions.get<Signature_Algorithms>()) |
278 | 69 | { return sigs->supported_schemes(); } |
279 | 46 | return {}; |
280 | 115 | } |
281 | | |
282 | | std::vector<Group_Params> Client_Hello::supported_ecc_curves() const |
283 | 40.8k | { |
284 | 40.8k | if(Supported_Groups* groups = m_data->extensions.get<Supported_Groups>()) |
285 | 39.6k | { return groups->ec_groups(); } |
286 | 1.21k | return {}; |
287 | 40.8k | } |
288 | | |
289 | | std::vector<Group_Params> Client_Hello::supported_dh_groups() const |
290 | 0 | { |
291 | 0 | if(Supported_Groups* groups = m_data->extensions.get<Supported_Groups>()) |
292 | 0 | { return groups->dh_groups(); } |
293 | 0 | return std::vector<Group_Params>(); |
294 | 0 | } |
295 | | |
296 | | bool Client_Hello_12::prefers_compressed_ec_points() const |
297 | 18.9k | { |
298 | 18.9k | if(Supported_Point_Formats* ecc_formats = m_data->extensions.get<Supported_Point_Formats>()) |
299 | 2.76k | { return ecc_formats->prefers_compressed(); } |
300 | 16.1k | return false; |
301 | 18.9k | } |
302 | | |
303 | | std::string Client_Hello::sni_hostname() const |
304 | 57.2k | { |
305 | 57.2k | if(Server_Name_Indicator* sni = m_data->extensions.get<Server_Name_Indicator>()) |
306 | 3.25k | { return sni->host_name(); } |
307 | 54.0k | return ""; |
308 | 57.2k | } |
309 | | |
310 | | bool Client_Hello_12::secure_renegotiation() const |
311 | 44.5k | { |
312 | 44.5k | return m_data->extensions.has<Renegotiation_Extension>(); |
313 | 44.5k | } |
314 | | |
315 | | std::vector<uint8_t> Client_Hello_12::renegotiation_info() const |
316 | 14.3k | { |
317 | 14.3k | if(Renegotiation_Extension* reneg = m_data->extensions.get<Renegotiation_Extension>()) |
318 | 14.3k | { return reneg->renegotiation_info(); } |
319 | 0 | return {}; |
320 | 14.3k | } |
321 | | |
322 | | std::vector<Protocol_Version> Client_Hello::supported_versions() const |
323 | 23.5k | { |
324 | 23.5k | if(Supported_Versions* versions = m_data->extensions.get<Supported_Versions>()) |
325 | 376 | { return versions->versions(); } |
326 | 23.2k | return {}; |
327 | 23.5k | } |
328 | | |
329 | | bool Client_Hello_12::supports_session_ticket() const |
330 | 20.9k | { |
331 | 20.9k | return m_data->extensions.has<Session_Ticket>(); |
332 | 20.9k | } |
333 | | |
334 | | std::vector<uint8_t> Client_Hello_12::session_ticket() const |
335 | 21.1k | { |
336 | 21.1k | if(Session_Ticket* ticket = m_data->extensions.get<Session_Ticket>()) |
337 | 6.16k | { return ticket->contents(); } |
338 | 14.9k | return {}; |
339 | 21.1k | } |
340 | | |
341 | | bool Client_Hello::supports_alpn() const |
342 | 24.7k | { |
343 | 24.7k | return m_data->extensions.has<Application_Layer_Protocol_Notification>(); |
344 | 24.7k | } |
345 | | |
346 | | bool Client_Hello_12::supports_extended_master_secret() const |
347 | 20.9k | { |
348 | 20.9k | return m_data->extensions.has<Extended_Master_Secret>(); |
349 | 20.9k | } |
350 | | |
351 | | bool Client_Hello_12::supports_cert_status_message() const |
352 | 21.1k | { |
353 | 21.1k | return m_data->extensions.has<Certificate_Status_Request>(); |
354 | 21.1k | } |
355 | | |
356 | | bool Client_Hello_12::supports_encrypt_then_mac() const |
357 | 16.2k | { |
358 | 16.2k | return m_data->extensions.has<Encrypt_then_MAC>(); |
359 | 16.2k | } |
360 | | |
361 | | bool Client_Hello::sent_signature_algorithms() const |
362 | 0 | { |
363 | 0 | return m_data->extensions.has<Signature_Algorithms>(); |
364 | 0 | } |
365 | | |
366 | | std::vector<std::string> Client_Hello::next_protocols() const |
367 | 3.61k | { |
368 | 3.61k | if(auto alpn = m_data->extensions.get<Application_Layer_Protocol_Notification>()) |
369 | 3.61k | { return alpn->protocols(); } |
370 | 0 | return {}; |
371 | 3.61k | } |
372 | | |
373 | | std::vector<uint16_t> Client_Hello::srtp_profiles() const |
374 | 0 | { |
375 | 0 | if(SRTP_Protection_Profiles* srtp = m_data->extensions.get<SRTP_Protection_Profiles>()) |
376 | 0 | { return srtp->profiles(); } |
377 | 0 | return {}; |
378 | 0 | } |
379 | | |
380 | | const std::vector<uint8_t>& Client_Hello::cookie() const |
381 | 2.39k | { |
382 | 2.39k | return m_data->hello_cookie; |
383 | 2.39k | } |
384 | | |
385 | | /* |
386 | | * Create a new Hello Request message |
387 | | */ |
388 | | Hello_Request::Hello_Request(Handshake_IO& io) |
389 | 0 | { |
390 | 0 | io.send(*this); |
391 | 0 | } |
392 | | |
393 | | /* |
394 | | * Deserialize a Hello Request message |
395 | | */ |
396 | | Hello_Request::Hello_Request(const std::vector<uint8_t>& buf) |
397 | 0 | { |
398 | 0 | if(!buf.empty()) |
399 | 0 | { throw Decoding_Error("Bad Hello_Request, has non-zero size"); } |
400 | 0 | } |
401 | | |
402 | | /* |
403 | | * Serialize a Hello Request message |
404 | | */ |
405 | | std::vector<uint8_t> Hello_Request::serialize() const |
406 | 0 | { |
407 | 0 | return std::vector<uint8_t>(); |
408 | 0 | } |
409 | | |
410 | | |
411 | | Client_Hello_12::Client_Hello_12(std::unique_ptr<Client_Hello_Internal> data) |
412 | | : Client_Hello(std::move(data)) |
413 | 30.9k | { |
414 | 30.9k | if(offered_suite(static_cast<uint16_t>(TLS_EMPTY_RENEGOTIATION_INFO_SCSV))) |
415 | 13.8k | { |
416 | 13.8k | if(Renegotiation_Extension* reneg = m_data->extensions.get<Renegotiation_Extension>()) |
417 | 234 | { |
418 | 234 | if(!reneg->renegotiation_info().empty()) |
419 | 5 | throw TLS_Exception(Alert::HANDSHAKE_FAILURE, |
420 | 5 | "Client sent renegotiation SCSV and non-empty extension"); |
421 | 234 | } |
422 | 13.6k | else |
423 | 13.6k | { |
424 | | // add fake extension |
425 | 13.6k | m_data->extensions.add(new Renegotiation_Extension()); |
426 | 13.6k | } |
427 | 13.8k | } |
428 | 30.9k | } |
429 | | |
430 | | // Note: This delegates to the Client_Hello_12 constructor to take advantage |
431 | | // of the sanity checks there. |
432 | | Client_Hello_12::Client_Hello_12(const std::vector<uint8_t>& buf) |
433 | 27.1k | : Client_Hello_12(std::make_unique<Client_Hello_Internal>(buf)) {} |
434 | | |
435 | | /* |
436 | | * Create a new Client Hello message |
437 | | */ |
438 | | Client_Hello_12::Client_Hello_12(Handshake_IO& io, |
439 | | Handshake_Hash& hash, |
440 | | const Policy& policy, |
441 | | Callbacks& cb, |
442 | | RandomNumberGenerator& rng, |
443 | | const std::vector<uint8_t>& reneg_info, |
444 | | const Client_Hello_12::Settings& client_settings, |
445 | | const std::vector<std::string>& next_protocols) |
446 | 2.39k | { |
447 | 2.39k | m_data->legacy_version = client_settings.protocol_version(); |
448 | 2.39k | m_data->random = make_hello_random(rng, cb, policy); |
449 | 2.39k | m_data->suites = policy.ciphersuite_list(client_settings.protocol_version()); |
450 | | |
451 | 2.39k | if(!policy.acceptable_protocol_version(m_data->legacy_version)) |
452 | 0 | throw Internal_Error("Offering " + m_data->legacy_version.to_string() + |
453 | 0 | " but our own policy does not accept it"); |
454 | | |
455 | | /* |
456 | | * Place all empty extensions in front to avoid a bug in some systems |
457 | | * which reject hellos when the last extension in the list is empty. |
458 | | */ |
459 | | |
460 | | // EMS must always be used with TLS 1.2, regardless of the policy used. |
461 | 2.39k | m_data->extensions.add(new Extended_Master_Secret); |
462 | | |
463 | 2.39k | if(policy.negotiate_encrypt_then_mac()) |
464 | 2.39k | { m_data->extensions.add(new Encrypt_then_MAC); } |
465 | | |
466 | 2.39k | m_data->extensions.add(new Session_Ticket()); |
467 | | |
468 | 2.39k | m_data->extensions.add(new Renegotiation_Extension(reneg_info)); |
469 | | |
470 | 2.39k | m_data->extensions.add(new Supported_Versions(m_data->legacy_version, policy)); |
471 | | |
472 | 2.39k | if(!client_settings.hostname().empty()) |
473 | 2.39k | { m_data->extensions.add(new Server_Name_Indicator(client_settings.hostname())); } |
474 | | |
475 | 2.39k | if(policy.support_cert_status_message()) |
476 | 2.39k | m_data->extensions.add(new Certificate_Status_Request({}, {})); |
477 | | |
478 | 2.39k | auto supported_groups = std::make_unique<Supported_Groups>(policy.key_exchange_groups()); |
479 | 2.39k | if(!supported_groups->ec_groups().empty()) |
480 | 2.39k | { |
481 | 2.39k | m_data->extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression())); |
482 | 2.39k | } |
483 | 2.39k | m_data->extensions.add(supported_groups.release()); |
484 | | |
485 | 2.39k | m_data->extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); |
486 | | |
487 | 2.39k | if(reneg_info.empty() && !next_protocols.empty()) |
488 | 0 | { m_data->extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); } |
489 | | |
490 | 2.39k | if(m_data->legacy_version.is_datagram_protocol()) |
491 | 0 | { m_data->extensions.add(new SRTP_Protection_Profiles(policy.srtp_profiles())); } |
492 | | |
493 | 2.39k | cb.tls_modify_extensions(m_data->extensions, CLIENT, type()); |
494 | | |
495 | 2.39k | hash.update(io.send(*this)); |
496 | 2.39k | } |
497 | | |
498 | | /* |
499 | | * Create a new Client Hello message (session resumption case) |
500 | | */ |
501 | | Client_Hello_12::Client_Hello_12(Handshake_IO& io, |
502 | | Handshake_Hash& hash, |
503 | | const Policy& policy, |
504 | | Callbacks& cb, |
505 | | RandomNumberGenerator& rng, |
506 | | const std::vector<uint8_t>& reneg_info, |
507 | | const Session& session, |
508 | | const std::vector<std::string>& next_protocols) |
509 | 0 | { |
510 | 0 | m_data->legacy_version = session.version(); |
511 | 0 | m_data->random = make_hello_random(rng, cb, policy); |
512 | 0 | m_data->session_id = session.session_id(); |
513 | 0 | m_data->suites = policy.ciphersuite_list(m_data->legacy_version); |
514 | |
|
515 | 0 | if(!policy.acceptable_protocol_version(session.version())) |
516 | 0 | throw Internal_Error("Offering " + m_data->legacy_version.to_string() + |
517 | 0 | " but our own policy does not accept it"); |
518 | | |
519 | 0 | if(!value_exists(m_data->suites, session.ciphersuite_code())) |
520 | 0 | { m_data->suites.push_back(session.ciphersuite_code()); } |
521 | | |
522 | | /* |
523 | | * As EMS must always be used with TLS 1.2, add it even if it wasn't used |
524 | | * in the original session. If the server understands it and follows the |
525 | | * RFC it should reject our resume attempt and upgrade us to a new session |
526 | | * with the EMS protection. |
527 | | */ |
528 | 0 | m_data->extensions.add(new Extended_Master_Secret); |
529 | |
|
530 | 0 | if(session.supports_encrypt_then_mac()) |
531 | 0 | { m_data->extensions.add(new Encrypt_then_MAC); } |
532 | |
|
533 | 0 | m_data->extensions.add(new Session_Ticket(session.session_ticket())); |
534 | |
|
535 | 0 | m_data->extensions.add(new Renegotiation_Extension(reneg_info)); |
536 | |
|
537 | 0 | m_data->extensions.add(new Server_Name_Indicator(session.server_info().hostname())); |
538 | |
|
539 | 0 | if(policy.support_cert_status_message()) |
540 | 0 | m_data->extensions.add(new Certificate_Status_Request({}, {})); |
541 | |
|
542 | 0 | auto supported_groups = std::make_unique<Supported_Groups>(policy.key_exchange_groups()); |
543 | |
|
544 | 0 | if(!supported_groups->ec_groups().empty()) |
545 | 0 | { |
546 | 0 | m_data->extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression())); |
547 | 0 | } |
548 | |
|
549 | 0 | m_data->extensions.add(supported_groups.release()); |
550 | |
|
551 | 0 | m_data->extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); |
552 | |
|
553 | 0 | if(reneg_info.empty() && !next_protocols.empty()) |
554 | 0 | { m_data->extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); } |
555 | |
|
556 | 0 | cb.tls_modify_extensions(m_data->extensions, CLIENT, type()); |
557 | |
|
558 | 0 | hash.update(io.send(*this)); |
559 | 0 | } |
560 | | |
561 | | #if defined(BOTAN_HAS_TLS_13) |
562 | | |
563 | | Client_Hello_13::Client_Hello_13(std::unique_ptr<Client_Hello_Internal> data) |
564 | | : Client_Hello(std::move(data)) |
565 | 772 | { |
566 | 772 | const auto& exts = m_data->extensions; |
567 | | |
568 | | // RFC 8446 4.1.2 |
569 | | // TLS 1.3 ClientHellos are identified as having a legacy_version of |
570 | | // 0x0303 and a "supported_versions" extension present with 0x0304 as the |
571 | | // highest version indicated therein. |
572 | | // |
573 | | // Note that we already checked for "supported_versions" before entering this |
574 | | // c'tor in `Client_Hello_13::parse()`. This is just to be doubly sure. |
575 | 772 | BOTAN_ASSERT_NOMSG(exts.has<Supported_Versions>()); |
576 | | |
577 | | // RFC 8446 4.2.1 |
578 | | // Servers MAY abort the handshake upon receiving a ClientHello with |
579 | | // legacy_version 0x0304 or later. |
580 | 772 | if(m_data->legacy_version.is_tls_13_or_later()) |
581 | 19 | { |
582 | 19 | throw TLS_Exception(Alert::DECODE_ERROR, |
583 | 19 | "TLS 1.3 Client Hello has invalid legacy_version"); |
584 | 19 | } |
585 | | |
586 | | // RFC 8446 4.1.2 |
587 | | // For every TLS 1.3 ClientHello, [the compression method] MUST contain |
588 | | // exactly one byte, set to zero, [...]. If a TLS 1.3 ClientHello is |
589 | | // received with any other value in this field, the server MUST abort the |
590 | | // handshake with an "illegal_parameter" alert. |
591 | 753 | if(m_data->comp_methods.size() != 1 || m_data->comp_methods.front() != 0) |
592 | 8 | { |
593 | 8 | throw TLS_Exception(Alert::ILLEGAL_PARAMETER, |
594 | 8 | "Client did not offer NULL compression"); |
595 | 8 | } |
596 | | |
597 | | // RFC 8446 4.2.9 |
598 | | // A client MUST provide a "psk_key_exchange_modes" extension if it |
599 | | // offers a "pre_shared_key" extension. If clients offer "pre_shared_key" |
600 | | // without a "psk_key_exchange_modes" extension, servers MUST abort |
601 | | // the handshake. |
602 | 745 | if(exts.has<PSK>()) |
603 | 126 | { |
604 | 126 | if(!exts.has<PSK_Key_Exchange_Modes>()) |
605 | 14 | { |
606 | 14 | throw TLS_Exception(Alert::MISSING_EXTENSION, |
607 | 14 | "Client Hello offered a PSK without a psk_key_exchange_modes extension"); |
608 | 14 | } |
609 | 126 | } |
610 | | |
611 | | // RFC 8446 9.2 |
612 | | // [A TLS 1.3 ClientHello] message MUST meet the following requirements: |
613 | | // |
614 | | // - If not containing a "pre_shared_key" extension, it MUST contain |
615 | | // both a "signature_algorithms" extension and a "supported_groups" |
616 | | // extension. |
617 | | // |
618 | | // - If containing a "supported_groups" extension, it MUST also contain |
619 | | // a "key_share" extension, and vice versa. An empty |
620 | | // KeyShare.client_shares vector is permitted. |
621 | | // |
622 | | // Servers receiving a ClientHello which does not conform to these |
623 | | // requirements MUST abort the handshake with a "missing_extension" |
624 | | // alert. |
625 | 731 | if(!exts.has<PSK>()) |
626 | 619 | { |
627 | 619 | if(!exts.has<Supported_Groups>() || !exts.has<Signature_Algorithms>()) |
628 | 50 | { |
629 | 50 | throw TLS_Exception(Alert::MISSING_EXTENSION, |
630 | 50 | "Non-PSK Client Hello did not contain supported_groups and signature_algorithms extensions"); |
631 | 50 | } |
632 | | |
633 | 619 | } |
634 | 681 | if(exts.has<Supported_Groups>() != exts.has<Key_Share>()) |
635 | 14 | { |
636 | 14 | throw TLS_Exception(Alert::MISSING_EXTENSION, |
637 | 14 | "Client Hello must either contain both key_share and supported_groups extensions or neither"); |
638 | 14 | } |
639 | | |
640 | 667 | if(exts.has<Key_Share>()) |
641 | 556 | { |
642 | 556 | const auto supported_ext = exts.get<Supported_Groups>(); |
643 | 556 | BOTAN_ASSERT_NONNULL(supported_ext); |
644 | 556 | const auto supports = supported_ext->groups(); |
645 | 556 | const auto offers = exts.get<Key_Share>()->offered_groups(); |
646 | | |
647 | | // RFC 8446 4.2.8 |
648 | | // Each KeyShareEntry value MUST correspond to a group offered in the |
649 | | // "supported_groups" extension and MUST appear in the same order. |
650 | | // [...] |
651 | | // Clients MUST NOT offer any KeyShareEntry values for groups not |
652 | | // listed in the client's "supported_groups" extension. |
653 | | // |
654 | | // Note: We can assume that both `offers` and `supports` are unique lists |
655 | | // as this is ensured in the parsing code of the extensions. |
656 | 556 | auto found_in_supported_groups = [&supports,support_offset = -1](auto group) mutable |
657 | 556 | { |
658 | 512 | const auto i = std::find(supports.begin(), supports.end(), group); |
659 | 512 | if(i == supports.end()) |
660 | 24 | { |
661 | 24 | return false; |
662 | 24 | } |
663 | | |
664 | 488 | const auto found_at = std::distance(supports.begin(), i); |
665 | 488 | if(found_at <= support_offset) |
666 | 1 | { |
667 | 1 | return false; // The order that groups appear in "key_share" and |
668 | | // "supported_groups" must be the same |
669 | 1 | } |
670 | | |
671 | 487 | support_offset = static_cast<decltype(support_offset)>(found_at); |
672 | 487 | return true; |
673 | 488 | }; |
674 | | |
675 | 556 | for(const auto offered : offers) |
676 | 512 | { |
677 | | // RFC 8446 4.2.8 |
678 | | // Servers MAY check for violations of these rules and abort the |
679 | | // handshake with an "illegal_parameter" alert if one is violated. |
680 | 512 | if(!found_in_supported_groups(offered)) |
681 | 25 | { |
682 | 25 | throw TLS_Exception(Alert::ILLEGAL_PARAMETER, |
683 | 25 | "Offered key exchange groups do not align with claimed supported groups"); |
684 | 25 | } |
685 | 512 | } |
686 | 556 | } |
687 | | |
688 | | // TODO: Reject oid_filters extension if found (which is the only known extension that |
689 | | // must not occur in the TLS 1.3 client hello. |
690 | | // RFC 8446 4.2.5 |
691 | | // [The oid_filters extension] MUST only be sent in the CertificateRequest message. |
692 | 667 | } |
693 | | |
694 | | /* |
695 | | * Create a new Client Hello message |
696 | | */ |
697 | | Client_Hello_13::Client_Hello_13(const Policy& policy, |
698 | | Callbacks& cb, |
699 | | RandomNumberGenerator& rng, |
700 | | const std::string& hostname, |
701 | | const std::vector<std::string>& next_protocols, |
702 | | const std::optional<Session>& session) |
703 | 0 | { |
704 | | // RFC 8446 4.1.2 |
705 | | // In TLS 1.3, the client indicates its version preferences in the |
706 | | // "supported_versions" extension (Section 4.2.1) and the |
707 | | // legacy_version field MUST be set to 0x0303, which is the version |
708 | | // number for TLS 1.2. |
709 | 0 | m_data->legacy_version = Protocol_Version::TLS_V12; |
710 | 0 | m_data->random = make_hello_random(rng, cb, policy); |
711 | 0 | m_data->suites = policy.ciphersuite_list(Protocol_Version::TLS_V13); |
712 | |
|
713 | 0 | if(policy.allow_tls12()) // Note: DTLS 1.3 is NYI, hence dtls_12 is not checked |
714 | 0 | { |
715 | 0 | const auto legacy_suites = policy.ciphersuite_list(Protocol_Version::TLS_V12); |
716 | 0 | m_data->suites.insert(m_data->suites.end(), legacy_suites.cbegin(), legacy_suites.cend()); |
717 | 0 | } |
718 | |
|
719 | 0 | if(policy.tls_13_middlebox_compatibility_mode()) |
720 | 0 | { |
721 | | // RFC 8446 4.1.2 |
722 | | // In compatibility mode (see Appendix D.4), this field MUST be non-empty, |
723 | | // so a client not offering a pre-TLS 1.3 session MUST generate a new |
724 | | // 32-byte value. |
725 | 0 | rng.random_vec(m_data->session_id, 32); |
726 | 0 | } |
727 | |
|
728 | 0 | if(!hostname.empty()) |
729 | 0 | m_data->extensions.add(new Server_Name_Indicator(hostname)); |
730 | |
|
731 | 0 | m_data->extensions.add(new Supported_Groups(policy.key_exchange_groups())); |
732 | |
|
733 | 0 | m_data->extensions.add(new Key_Share(policy, cb, rng)); |
734 | |
|
735 | 0 | m_data->extensions.add(new Supported_Versions(Protocol_Version::TLS_V13, policy)); |
736 | |
|
737 | 0 | m_data->extensions.add(new Signature_Algorithms(policy.acceptable_signature_schemes())); |
738 | | |
739 | | // TODO: Support for PSK-only mode without a key exchange. |
740 | | // This should be configurable in TLS::Policy and should allow no PSK |
741 | | // support at all (e.g. to disable support for session resumption). |
742 | 0 | m_data->extensions.add(new PSK_Key_Exchange_Modes({PSK_Key_Exchange_Mode::PSK_DHE_KE})); |
743 | | |
744 | | // TODO: Add a signature_algorithms_cert extension negotiating the acceptable |
745 | | // signature algorithms in a server certificate chain's certificates. |
746 | |
|
747 | 0 | if(policy.support_cert_status_message()) |
748 | 0 | m_data->extensions.add(new Certificate_Status_Request({}, {})); |
749 | | |
750 | | // We currently support "record_size_limit" for TLS 1.3 exclusively. Hence, |
751 | | // when TLS 1.2 is advertised as a supported protocol, we must not offer this |
752 | | // extension. |
753 | 0 | if(policy.record_size_limit().has_value() && !policy.allow_tls12()) |
754 | 0 | m_data->extensions.add(new Record_Size_Limit(policy.record_size_limit().value())); |
755 | |
|
756 | 0 | if(!next_protocols.empty()) |
757 | 0 | m_data->extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); |
758 | |
|
759 | 0 | if(policy.allow_tls12()) |
760 | 0 | { |
761 | 0 | m_data->extensions.add(new Renegotiation_Extension()); |
762 | 0 | m_data->extensions.add(new Session_Ticket()); |
763 | | |
764 | | // EMS must always be used with TLS 1.2, regardless of the policy |
765 | 0 | m_data->extensions.add(new Extended_Master_Secret); |
766 | |
|
767 | 0 | if(policy.negotiate_encrypt_then_mac()) |
768 | 0 | m_data->extensions.add(new Encrypt_then_MAC); |
769 | |
|
770 | 0 | if(m_data->extensions.has<Supported_Groups>() && !m_data->extensions.get<Supported_Groups>()->ec_groups().empty()) |
771 | 0 | m_data->extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression())); |
772 | 0 | } |
773 | | |
774 | | // TODO: Some extensions require a certain order or pose other assumptions. |
775 | | // We should check those after the user was allowed to make changes to |
776 | | // the extensions. |
777 | 0 | cb.tls_modify_extensions(m_data->extensions, CLIENT, type()); |
778 | | |
779 | | // RFC 8446 4.2.11 |
780 | | // The "pre_shared_key" extension MUST be the last extension in the |
781 | | // ClientHello (this facilitates implementation [...]). |
782 | | // |
783 | | // The PSK extension takes the partial transcript hash into account. Passing |
784 | | // into Callbacks::tls_modify_extensions() does not make sense therefore. |
785 | 0 | if(session.has_value()) |
786 | 0 | { |
787 | 0 | m_data->extensions.add(new PSK(session.value(), cb)); |
788 | 0 | calculate_psk_binders({}); |
789 | 0 | } |
790 | 0 | } |
791 | | |
792 | | |
793 | | std::variant<Client_Hello_13, Client_Hello_12> |
794 | | Client_Hello_13::parse(const std::vector<uint8_t>& buf) |
795 | 8.02k | { |
796 | 8.02k | auto data = std::make_unique<Client_Hello_Internal>(buf); |
797 | 8.02k | const auto version = data->version(); |
798 | | |
799 | 8.02k | if(version.is_pre_tls_13()) |
800 | 6.80k | return Client_Hello_12(std::move(data)); |
801 | 1.22k | else |
802 | 1.22k | return Client_Hello_13(std::move(data)); |
803 | 8.02k | } |
804 | | |
805 | | void Client_Hello_13::retry(const Hello_Retry_Request& hrr, |
806 | | const Transcript_Hash_State& transcript_hash_state, |
807 | | Callbacks& cb, |
808 | | RandomNumberGenerator& rng) |
809 | 0 | { |
810 | 0 | BOTAN_STATE_CHECK(m_data->extensions.has<Supported_Groups>()); |
811 | 0 | BOTAN_STATE_CHECK(m_data->extensions.has<Key_Share>()); |
812 | |
|
813 | 0 | auto hrr_ks = hrr.extensions().get<Key_Share>(); |
814 | 0 | const auto& supported_groups = m_data->extensions.get<Supported_Groups>()->groups(); |
815 | |
|
816 | 0 | if(hrr.extensions().has<Key_Share>()) |
817 | 0 | m_data->extensions.get<Key_Share>()->retry_offer(*hrr_ks, supported_groups, cb, rng); |
818 | | |
819 | | // RFC 8446 4.2.2 |
820 | | // When sending the new ClientHello, the client MUST copy |
821 | | // the contents of the extension received in the HelloRetryRequest into |
822 | | // a "cookie" extension in the new ClientHello. |
823 | | // |
824 | | // RFC 8446 4.2.2 |
825 | | // Clients MUST NOT use cookies in their initial ClientHello in subsequent |
826 | | // connections. |
827 | 0 | if(hrr.extensions().has<Cookie>()) |
828 | 0 | { |
829 | 0 | BOTAN_STATE_CHECK(!m_data->extensions.has<Cookie>()); |
830 | 0 | m_data->extensions.add(new Cookie(hrr.extensions().get<Cookie>()->get_cookie())); |
831 | 0 | } |
832 | | |
833 | | // Note: the consumer of the TLS implementation won't be able to distinguish |
834 | | // invocations to this callback due to the first Client_Hello or the |
835 | | // retried Client_Hello after receiving a Hello_Retry_Request. We assume |
836 | | // that the user keeps and detects this state themselves. |
837 | 0 | cb.tls_modify_extensions(m_data->extensions, CLIENT, type()); |
838 | |
|
839 | 0 | auto psk = m_data->extensions.get<PSK>(); |
840 | 0 | if(psk) |
841 | 0 | { |
842 | | // Cipher suite should always be a known suite as this is checked upstream |
843 | 0 | const auto cipher = Ciphersuite::by_id(hrr.ciphersuite()); |
844 | 0 | BOTAN_ASSERT_NOMSG(cipher.has_value()); |
845 | | |
846 | | // RFC 8446 4.1.4 |
847 | | // In [...] its updated ClientHello, the client SHOULD NOT offer |
848 | | // any pre-shared keys associated with a hash other than that of the |
849 | | // selected cipher suite. |
850 | 0 | psk->filter(cipher.value()); |
851 | | |
852 | | // RFC 8446 4.2.11.2 |
853 | | // If the server responds with a HelloRetryRequest and the client |
854 | | // then sends ClientHello2, its binder will be computed over: [...]. |
855 | 0 | calculate_psk_binders(transcript_hash_state.clone()); |
856 | 0 | } |
857 | 0 | } |
858 | | |
859 | | void Client_Hello_13::validate_updates(const Client_Hello_13& new_ch) |
860 | 0 | { |
861 | | // RFC 8446 4.1.2 |
862 | | // The client will also send a ClientHello when the server has responded |
863 | | // to its ClientHello with a HelloRetryRequest. In that case, the client |
864 | | // MUST send the same ClientHello without modification, except as follows: |
865 | |
|
866 | 0 | if(m_data->session_id != new_ch.m_data->session_id || |
867 | 0 | m_data->random != new_ch.m_data->random || |
868 | 0 | m_data->suites != new_ch.m_data->suites || |
869 | 0 | m_data->comp_methods != new_ch.m_data->comp_methods) |
870 | 0 | { |
871 | 0 | throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Client Hello core values changed after Hello Retry Request"); |
872 | 0 | } |
873 | | |
874 | 0 | const auto oldexts = extension_types(); |
875 | 0 | const auto newexts = new_ch.extension_types(); |
876 | | |
877 | | // Check that extension omissions are justified |
878 | 0 | for(const auto oldext : oldexts) |
879 | 0 | { |
880 | 0 | if(!newexts.contains(oldext)) |
881 | 0 | { |
882 | 0 | const auto ext = extensions().get(oldext); |
883 | | |
884 | | // We don't make any assumptions about unimplemented extensions. |
885 | 0 | if(!ext->is_implemented()) |
886 | 0 | continue; |
887 | | |
888 | | // RFC 8446 4.1.2 |
889 | | // Removing the "early_data" extension (Section 4.2.10) if one was |
890 | | // present. Early data is not permitted after a HelloRetryRequest. |
891 | 0 | if(oldext == EarlyDataIndication::static_type()) |
892 | 0 | continue; |
893 | | |
894 | | // RFC 8446 4.1.2 |
895 | | // Optionally adding, removing, or changing the length of the |
896 | | // "padding" extension. |
897 | | // |
898 | | // TODO: implement the Padding extension |
899 | | // if(oldext == Padding::static_type()) |
900 | | // continue; |
901 | | |
902 | 0 | throw TLS_Exception(Alert::MISSING_EXTENSION, "Extension removed in updated Client Hello"); |
903 | 0 | } |
904 | 0 | } |
905 | | |
906 | | // Check that extension additions are justified |
907 | 0 | for(const auto newext : newexts) |
908 | 0 | { |
909 | 0 | if(!oldexts.contains(newext)) |
910 | 0 | { |
911 | 0 | const auto ext = new_ch.extensions().get(newext); |
912 | | |
913 | | // We don't make any assumptions about unimplemented extensions. |
914 | 0 | if(!ext->is_implemented()) |
915 | 0 | continue; |
916 | | |
917 | | // RFC 8446 4.1.2 |
918 | | // Including a "cookie" extension if one was provided in the |
919 | | // HelloRetryRequest. |
920 | 0 | if(newext == Cookie::static_type()) |
921 | 0 | continue; |
922 | | |
923 | | // RFC 8446 4.1.2 |
924 | | // Optionally adding, removing, or changing the length of the |
925 | | // "padding" extension. |
926 | | // |
927 | | // TODO: implement the Padding extension |
928 | | // if(newext == Padding::static_type()) |
929 | | // continue; |
930 | | |
931 | 0 | throw TLS_Exception(Alert::UNSUPPORTED_EXTENSION, "Added an extension in updated Client Hello"); |
932 | 0 | } |
933 | 0 | } |
934 | | |
935 | | // RFC 8446 4.1.2 |
936 | | // Removing the "early_data" extension (Section 4.2.10) if one was |
937 | | // present. Early data is not permitted after a HelloRetryRequest. |
938 | 0 | if(new_ch.extensions().has<EarlyDataIndication>()) |
939 | 0 | { |
940 | 0 | throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Updated Client Hello indicates early data"); |
941 | 0 | } |
942 | | |
943 | | // TODO: Contents of extensions are not checked for update compatibility, see: |
944 | | // |
945 | | // RFC 8446 4.1.2 |
946 | | // If a "key_share" extension was supplied in the HelloRetryRequest, |
947 | | // replacing the list of shares with a list containing a single |
948 | | // KeyShareEntry from the indicated group. |
949 | | // |
950 | | // Updating the "pre_shared_key" extension if present by recomputing |
951 | | // the "obfuscated_ticket_age" and binder values and (optionally) |
952 | | // removing any PSKs which are incompatible with the server's |
953 | | // indicated cipher suite. |
954 | | // |
955 | | // Optionally adding, removing, or changing the length of the |
956 | | // "padding" extension. |
957 | 0 | } |
958 | | |
959 | | void Client_Hello_13::calculate_psk_binders(Transcript_Hash_State ths) |
960 | 0 | { |
961 | 0 | auto psk = m_data->extensions.get<PSK>(); |
962 | 0 | if(!psk || psk->empty()) |
963 | 0 | return; |
964 | | |
965 | | // RFC 8446 4.2.11.2 |
966 | | // Each entry in the binders list is computed as an HMAC over a |
967 | | // transcript hash (see Section 4.4.1) containing a partial ClientHello |
968 | | // [...]. |
969 | | // |
970 | | // Therefore we marshal the entire message prematurely to obtain the |
971 | | // (truncated) transcript hash, calculate the PSK binders with it, update |
972 | | // the Client Hello thus finalizing the message. Down the road, it will be |
973 | | // re-marshalled with the correct binders and sent over the wire. |
974 | 0 | Handshake_Layer::prepare_message(*this, ths); |
975 | 0 | psk->calculate_binders(ths); |
976 | 0 | } |
977 | | |
978 | | std::optional<Protocol_Version> Client_Hello_13::highest_supported_version(const Policy& policy) const |
979 | 0 | { |
980 | | // RFC 8446 4.2.1 |
981 | | // The "supported_versions" extension is used by the client to indicate |
982 | | // which versions of TLS it supports and by the server to indicate which |
983 | | // version it is using. The extension contains a list of supported |
984 | | // versions in preference order, with the most preferred version first. |
985 | 0 | const auto supvers = m_data->extensions.get<Supported_Versions>(); |
986 | 0 | BOTAN_ASSERT_NONNULL(supvers); |
987 | |
|
988 | 0 | std::optional<Protocol_Version> result; |
989 | |
|
990 | 0 | for(const auto& v : supvers->versions()) |
991 | 0 | { |
992 | | // RFC 8446 4.2.1 |
993 | | // Servers MUST only select a version of TLS present in that extension |
994 | | // and MUST ignore any unknown versions that are present in that |
995 | | // extension. |
996 | 0 | if(!v.known_version() || !policy.acceptable_protocol_version(v)) |
997 | 0 | { continue; } |
998 | | |
999 | 0 | result = (result.has_value()) |
1000 | 0 | ? std::optional(std::max(result.value(), v)) |
1001 | 0 | : std::optional(v); |
1002 | 0 | } |
1003 | |
|
1004 | 0 | return result; |
1005 | 0 | } |
1006 | | |
1007 | | #endif // BOTAN_HAS_TLS_13 |
1008 | | |
1009 | | } |