• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 24648292556

19 Apr 2026 10:53PM UTC coverage: 89.474% (+0.03%) from 89.442%
24648292556

push

github

web-flow
Merge pull request #5536 from randombit/jack/x509-misc

Various PKIX optimizations and bug fixes

106453 of 118977 relevant lines covered (89.47%)

11452293.24 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.93
/src/lib/tls/tls13/msg_client_hello_13.cpp
1
/*
2
* TLS Client Hello Messages
3
* (C) 2004-2011,2015,2016 Jack Lloyd
4
*     2021 Elektrobit Automotive GmbH
5
*     2022 René Meusel, Hannes Rantzsch - neXenio GmbH
6
*     2026 René Meusel - Rohde & Schwarz Cybersecurity GmbH
7
*
8
* Botan is released under the Simplified BSD License (see license.txt)
9
*/
10

11
#include <botan/tls_messages_13.h>
12

13
#include <botan/assert.h>
14
#include <botan/tls_alert.h>
15
#include <botan/tls_callbacks.h>
16
#include <botan/tls_exceptn.h>
17
#include <botan/tls_extensions_13.h>
18
#include <botan/tls_policy.h>
19
#include <botan/internal/tls_handshake_layer_13.h>
20
#include <botan/internal/tls_messages_internal.h>
21
#include <botan/internal/tls_transcript_hash_13.h>
22
#include <algorithm>
23

24
#if defined(BOTAN_HAS_TLS_12)
25
   #include <botan/tls_extensions_12.h>
26
#endif
27

28
namespace Botan::TLS {
29

30
Client_Hello_13::Client_Hello_13(std::unique_ptr<Client_Hello_Internal> data) : Client_Hello(std::move(data)) {
513✔
31
   const auto& exts = m_data->extensions();
513✔
32

33
   // RFC 8446 4.1.2
34
   //    TLS 1.3 ClientHellos are identified as having a legacy_version of
35
   //    0x0303 and a "supported_versions" extension present with 0x0304 as the
36
   //    highest version indicated therein.
37
   //
38
   // Note that we already checked for "supported_versions" before entering this
39
   // c'tor in `Client_Hello_13::parse()`. This is just to be doubly sure.
40
   BOTAN_ASSERT_NOMSG(exts.has<Supported_Versions>());
513✔
41

42
   // RFC 8446 4.2.1
43
   //    Servers MAY abort the handshake upon receiving a ClientHello with
44
   //    legacy_version 0x0304 or later.
45
   if(m_data->legacy_version().is_tls_13_or_later()) {
513✔
46
      throw TLS_Exception(Alert::DecodeError, "TLS 1.3 Client Hello has invalid legacy_version");
1✔
47
   }
48

49
   // RFC 8446 D.5
50
   //    Any endpoint receiving a Hello message with ClientHello.legacy_version [...]
51
   //    set to 0x0300 MUST abort the handshake with a "protocol_version" alert.
52
   if(m_data->legacy_version().major_version() == 3 && m_data->legacy_version().minor_version() == 0) {
512✔
53
      throw TLS_Exception(Alert::ProtocolVersion, "TLS 1.3 Client Hello has invalid legacy_version");
×
54
   }
55

56
   // RFC 8446 4.1.2
57
   //    For every TLS 1.3 ClientHello, [the compression method] MUST contain
58
   //    exactly one byte, set to zero, [...].  If a TLS 1.3 ClientHello is
59
   //    received with any other value in this field, the server MUST abort the
60
   //    handshake with an "illegal_parameter" alert.
61
   if(m_data->comp_methods().size() != 1 || m_data->comp_methods().front() != 0) {
512✔
62
      throw TLS_Exception(Alert::IllegalParameter, "Client did not offer NULL compression");
4✔
63
   }
64

65
   // RFC 8446 4.2.9
66
   //    A client MUST provide a "psk_key_exchange_modes" extension if it
67
   //    offers a "pre_shared_key" extension. If clients offer "pre_shared_key"
68
   //    without a "psk_key_exchange_modes" extension, servers MUST abort
69
   //    the handshake.
70
   if(exts.has<PSK>()) {
508✔
71
      if(!exts.has<PSK_Key_Exchange_Modes>()) {
150✔
72
         throw TLS_Exception(Alert::MissingExtension,
1✔
73
                             "Client Hello offered a PSK without a psk_key_exchange_modes extension");
1✔
74
      }
75

76
      // RFC 8446 4.2.11
77
      //     The "pre_shared_key" extension MUST be the last extension in the
78
      //     ClientHello [...]. Servers MUST check that it is the last extension
79
      //     and otherwise fail the handshake with an "illegal_parameter" alert.
80
      if(exts.last_added() != Extension_Code::PresharedKey) {
298✔
81
         throw TLS_Exception(Alert::IllegalParameter, "PSK extension was not at the very end of the Client Hello");
4✔
82
      }
83
   }
84

85
   // RFC 8446 9.2
86
   //    [A TLS 1.3 ClientHello] message MUST meet the following requirements:
87
   //
88
   //     -  If not containing a "pre_shared_key" extension, it MUST contain
89
   //        both a "signature_algorithms" extension and a "supported_groups"
90
   //        extension.
91
   //
92
   //     -  If containing a "supported_groups" extension, it MUST also contain
93
   //        a "key_share" extension, and vice versa.  An empty
94
   //        KeyShare.client_shares vector is permitted.
95
   //
96
   //    Servers receiving a ClientHello which does not conform to these
97
   //    requirements MUST abort the handshake with a "missing_extension"
98
   //    alert.
99
   if(!exts.has<PSK>()) {
503✔
100
      if(!exts.has<Supported_Groups>() || !exts.has<Signature_Algorithms>()) {
715✔
101
         throw TLS_Exception(
2✔
102
            Alert::MissingExtension,
103
            "Non-PSK Client Hello did not contain supported_groups and signature_algorithms extensions");
2✔
104
      }
105
   }
106
   if(exts.has<Supported_Groups>() != exts.has<Key_Share>()) {
501✔
107
      throw TLS_Exception(Alert::MissingExtension,
2✔
108
                          "Client Hello must either contain both key_share and supported_groups extensions or neither");
2✔
109
   }
110

111
   if(exts.has<Key_Share>()) {
499✔
112
      auto* const supported_ext = exts.get<Supported_Groups>();
499✔
113
      BOTAN_ASSERT_NONNULL(supported_ext);
499✔
114
      const auto supports = supported_ext->groups();
499✔
115
      const auto offers = exts.get<Key_Share>()->offered_groups();
499✔
116

117
      // RFC 8446 4.2.8
118
      //    Each KeyShareEntry value MUST correspond to a group offered in the
119
      //    "supported_groups" extension and MUST appear in the same order.
120
      //    [...]
121
      //    Clients MUST NOT offer any KeyShareEntry values for groups not
122
      //    listed in the client's "supported_groups" extension.
123
      //
124
      //    Servers MAY check for violations of these rules and abort the
125
      //    handshake with an "illegal_parameter" alert if one is violated.
126
      //
127
      // Note: We can assume that both `offers` and `supports` are unique lists
128
      //       as this is ensured in the parsing code of the extensions.
129
      //
130
      // Since offers must appear in the same order as supports, a single
131
      // forward sweep of `supports` suffices: after finding each offered group
132
      // we advance past its position so the next offered group is searched for
133
      // only in the remaining suffix.
134
      auto supports_it = supports.begin();
499✔
135
      for(const auto offered : offers) {
2,907✔
136
         supports_it = std::find(supports_it, supports.end(), offered);
2,408✔
137
         if(supports_it == supports.end()) {
2,408✔
138
            throw TLS_Exception(Alert::IllegalParameter,
×
139
                                "Offered key exchange groups do not align with claimed supported groups");
×
140
         }
141
         ++supports_it;
2,408✔
142
      }
143
   }
998✔
144

145
   // TODO: Reject oid_filters extension if found (which is the only known extension that
146
   //       must not occur in the TLS 1.3 client hello.
147
   // RFC 8446 4.2.5
148
   //    [The oid_filters extension] MUST only be sent in the CertificateRequest message.
149
}
513✔
150

151
/*
152
 * Create a new Client Hello message
153
 */
154
Client_Hello_13::Client_Hello_13(const Policy& policy,
1,050✔
155
                                 Callbacks& cb,
156
                                 RandomNumberGenerator& rng,
157
                                 std::string_view hostname,
158
                                 const std::vector<std::string>& next_protocols,
159
                                 std::optional<Session_with_Handle>& session,
160
                                 std::vector<ExternalPSK> psks) {
1,050✔
161
   // RFC 8446 4.1.2
162
   //    In TLS 1.3, the client indicates its version preferences in the
163
   //    "supported_versions" extension (Section 4.2.1) and the
164
   //    legacy_version field MUST be set to 0x0303, which is the version
165
   //    number for TLS 1.2.
166
   m_data->m_legacy_version = Protocol_Version::TLS_V12;
1,050✔
167
   m_data->m_random = make_hello_random(rng, cb, policy);
1,050✔
168
   m_data->m_suites = policy.ciphersuite_list(Protocol_Version::TLS_V13);
1,050✔
169

170
   if(policy.allow_tls12()) {
1,050✔
171
      // Note: DTLS 1.3 is NYI, hence dtls_12 is not checked
172
      const auto legacy_suites = policy.ciphersuite_list(Protocol_Version::TLS_V12);
1,018✔
173
      m_data->m_suites.insert(m_data->m_suites.end(), legacy_suites.cbegin(), legacy_suites.cend());
1,018✔
174
   }
1,018✔
175

176
   if(policy.tls_13_middlebox_compatibility_mode()) {
1,050✔
177
      // RFC 8446 4.1.2
178
      //    In compatibility mode (see Appendix D.4), this field MUST be non-empty,
179
      //    so a client not offering a pre-TLS 1.3 session MUST generate a new
180
      //    32-byte value.
181
      //
182
      // Note: we won't ever offer a TLS 1.2 session. In such a case we would
183
      //       have instantiated a TLS 1.2 client in the first place.
184
      m_data->m_session_id = Session_ID(make_hello_random(rng, cb, policy));
1,037✔
185
   }
186

187
   // NOLINTBEGIN(*-owning-memory)
188
   if(Server_Name_Indicator::hostname_acceptable_for_sni(hostname)) {
1,050✔
189
      m_data->extensions().add(new Server_Name_Indicator(hostname));
1,026✔
190
   }
191

192
   m_data->extensions().add(new Supported_Groups(policy.key_exchange_groups()));
1,050✔
193

194
   m_data->extensions().add(new Key_Share(policy, cb, rng));
1,050✔
195

196
   m_data->extensions().add(new Supported_Versions(Protocol_Version::TLS_V13, policy));
1,050✔
197

198
   m_data->extensions().add(new Signature_Algorithms(policy.acceptable_signature_schemes()));
1,050✔
199
   if(auto cert_signing_prefs = policy.acceptable_certificate_signature_schemes()) {
1,050✔
200
      // RFC 8446 4.2.3
201
      //    Implementations which have the same policy in both cases MAY omit
202
      //    the "signature_algorithms_cert" extension.
203
      m_data->extensions().add(new Signature_Algorithms_Cert(std::move(cert_signing_prefs.value())));
×
204
   }
×
205

206
   // TODO: Support for PSK-only mode without a key exchange.
207
   //       This should be configurable in TLS::Policy and should allow no PSK
208
   //       support at all (e.g. to disable support for session resumption).
209
   m_data->extensions().add(new PSK_Key_Exchange_Modes({PSK_Key_Exchange_Mode::PSK_DHE_KE}));
1,050✔
210

211
   if(policy.support_cert_status_message()) {
1,050✔
212
      m_data->extensions().add(new Certificate_Status_Request({}, {}));
216✔
213
   }
214

215
   // We currently support "record_size_limit" for TLS 1.3 exclusively. Hence,
216
   // when TLS 1.2 is advertised as a supported protocol, we must not offer this
217
   // extension.
218
   if(policy.record_size_limit().has_value() && !policy.allow_tls12()) {
1,050✔
219
      m_data->extensions().add(new Record_Size_Limit(policy.record_size_limit().value()));
16✔
220
   }
221

222
   /*
223
   * Right now raw public key support is not implemented for TLS 1.2, so we only offer
224
   * certificate_types (which is used to request raw public key) if additionally TLS 1.2
225
   * support is disabled. Otherwise a peer might reply with a 1.2 server hello + a certificate_type
226
   * extension indicating it wishes to use RPK, which would lead to errors later.
227
   */
228
   if(!policy.allow_tls12()) {
1,050✔
229
      m_data->extensions().add(new Client_Certificate_Type(policy.accepted_client_certificate_types()));
64✔
230
      m_data->extensions().add(new Server_Certificate_Type(policy.accepted_server_certificate_types()));
96✔
231
   }
232

233
   if(!next_protocols.empty()) {
1,050✔
234
      m_data->extensions().add(new Application_Layer_Protocol_Notification(next_protocols));
9✔
235
   }
236

237
#if defined(BOTAN_HAS_TLS_12)
238
   if(policy.allow_tls12()) {
1,050✔
239
      m_data->extensions().add(new Renegotiation_Extension());
1,018✔
240
      m_data->extensions().add(new Session_Ticket_Extension());
1,018✔
241

242
      // EMS must always be used with TLS 1.2, regardless of the policy
243
      m_data->extensions().add(new Extended_Master_Secret);
1,018✔
244

245
      if(policy.negotiate_encrypt_then_mac()) {
1,018✔
246
         m_data->extensions().add(new Encrypt_then_MAC);
1,018✔
247
      }
248

249
      if(m_data->extensions().has<Supported_Groups>() &&
2,036✔
250
         !m_data->extensions().get<Supported_Groups>()->ec_groups().empty()) {
2,036✔
251
         m_data->extensions().add(new Supported_Point_Formats(policy.use_ecc_point_compression()));
1,005✔
252
      }
253
   }
254
#endif
255

256
   if(session.has_value() || !psks.empty()) {
1,050✔
257
      m_data->extensions().add(new PSK(session, std::move(psks), cb));
140✔
258
   }
259
   // NOLINTEND(*-owning-memory)
260

261
   cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Client, type());
1,050✔
262

263
   if(m_data->extensions().has<PSK>()) {
1,050✔
264
      // RFC 8446 4.2.11
265
      //    The "pre_shared_key" extension MUST be the last extension in the
266
      //    ClientHello (this facilitates implementation [...]).
267
      if(m_data->extensions().last_added() != Extension_Code::PresharedKey) {
280✔
268
         throw TLS_Exception(Alert::InternalError,
×
269
                             "Application modified extensions of Client Hello, PSK is not last anymore");
×
270
      }
271
      calculate_psk_binders({});
140✔
272
   }
273
}
1,050✔
274

275
std::variant<Client_Hello_13, Client_Hello_12_Shim> Client_Hello_13::parse(const std::vector<uint8_t>& buf) {
1,049✔
276
   auto data = std::make_unique<Client_Hello_Internal>(buf);
1,049✔
277
   const auto version = data->version();
1,017✔
278

279
   if(version.is_pre_tls_13()) {
1,017✔
280
      return Client_Hello_12_Shim(std::move(data));
1,008✔
281
   } else {
282
      return Client_Hello_13(std::move(data));
1,012✔
283
   }
284
}
1,017✔
285

286
void Client_Hello_13::retry(const Hello_Retry_Request& hrr,
55✔
287
                            const Transcript_Hash_State& transcript_hash_state,
288
                            Callbacks& cb,
289
                            RandomNumberGenerator& rng) {
290
   BOTAN_STATE_CHECK(m_data->extensions().has<Supported_Groups>());
55✔
291
   BOTAN_STATE_CHECK(m_data->extensions().has<Key_Share>());
55✔
292

293
   auto* hrr_ks = hrr.extensions().get<Key_Share>();
55✔
294
   const auto& supported_groups = m_data->extensions().get<Supported_Groups>()->groups();
55✔
295

296
   if(hrr.extensions().has<Key_Share>()) {
55✔
297
      m_data->extensions().get<Key_Share>()->retry_offer(*hrr_ks, supported_groups, cb, rng);
45✔
298
   }
299

300
   // RFC 8446 4.2.2
301
   //    When sending the new ClientHello, the client MUST copy
302
   //    the contents of the extension received in the HelloRetryRequest into
303
   //    a "cookie" extension in the new ClientHello.
304
   //
305
   // RFC 8446 4.2.2
306
   //    Clients MUST NOT use cookies in their initial ClientHello in subsequent
307
   //    connections.
308
   if(hrr.extensions().has<Cookie>()) {
52✔
309
      BOTAN_STATE_CHECK(!m_data->extensions().has<Cookie>());
12✔
310
      m_data->extensions().add(new Cookie(hrr.extensions().get<Cookie>()->get_cookie()));  // NOLINT(*-owning-memory)
12✔
311
   }
312

313
   // Note: the consumer of the TLS implementation won't be able to distinguish
314
   //       invocations to this callback due to the first Client_Hello or the
315
   //       retried Client_Hello after receiving a Hello_Retry_Request. We assume
316
   //       that the user keeps and detects this state themselves.
317
   cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Client, type());
52✔
318

319
   auto* psk = m_data->extensions().get<PSK>();
52✔
320
   if(psk != nullptr) {
52✔
321
      // RFC 8446 4.2.11
322
      //    The "pre_shared_key" extension MUST be the last extension in the
323
      //    ClientHello (this facilitates implementation [...]).
324
      m_data->extensions().reorder({Extension_Code::PresharedKey});
20✔
325

326
      // Cipher suite should always be a known suite as this is checked upstream
327
      const auto cipher = Ciphersuite::by_id(hrr.ciphersuite());
20✔
328
      BOTAN_ASSERT_NOMSG(cipher.has_value());
20✔
329

330
      // RFC 8446 4.1.4
331
      //    In [...] its updated ClientHello, the client SHOULD NOT offer
332
      //    any pre-shared keys associated with a hash other than that of the
333
      //    selected cipher suite.
334
      psk->filter(cipher.value());
20✔
335

336
      // RFC 8446 4.2.11.2
337
      //    If the server responds with a HelloRetryRequest and the client
338
      //    then sends ClientHello2, its binder will be computed over: [...].
339
      calculate_psk_binders(transcript_hash_state.clone());
20✔
340
   }
341
}
52✔
342

343
void Client_Hello_13::validate_updates(const Client_Hello_13& new_ch) {
49✔
344
   // RFC 8446 4.1.2
345
   //    The client will also send a ClientHello when the server has responded
346
   //    to its ClientHello with a HelloRetryRequest. In that case, the client
347
   //    MUST send the same ClientHello without modification, except as follows:
348

349
   if(m_data->session_id() != new_ch.m_data->session_id() || m_data->random() != new_ch.m_data->random() ||
49✔
350
      m_data->ciphersuites() != new_ch.m_data->ciphersuites() ||
98✔
351
      m_data->comp_methods() != new_ch.m_data->comp_methods()) {
49✔
352
      throw TLS_Exception(Alert::IllegalParameter, "Client Hello core values changed after Hello Retry Request");
×
353
   }
354

355
   const auto oldexts = extension_types();
49✔
356
   const auto newexts = new_ch.extension_types();
49✔
357

358
   // Check that extension omissions are justified
359
   for(const auto oldext : oldexts) {
567✔
360
      if(!newexts.contains(oldext)) {
1,040✔
361
         auto* const ext = extensions().get(oldext);
2✔
362

363
         // We don't make any assumptions about unimplemented extensions.
364
         if(!ext->is_implemented()) {
2✔
365
            continue;
×
366
         }
367

368
         // RFC 8446 4.1.2
369
         //    Removing the "early_data" extension (Section 4.2.10) if one was
370
         //    present.  Early data is not permitted after a HelloRetryRequest.
371
         if(oldext == EarlyDataIndication::static_type()) {
2✔
372
            continue;
×
373
         }
374

375
         // RFC 8446 4.1.2
376
         //    Optionally adding, removing, or changing the length of the
377
         //    "padding" extension.
378
         //
379
         // TODO: implement the Padding extension
380
         // if(oldext == Padding::static_type())
381
         //    continue;
382

383
         throw TLS_Exception(Alert::IllegalParameter, "Extension removed in updated Client Hello");
2✔
384
      }
385
   }
386

387
   // Check that extension additions are justified
388
   for(const auto newext : newexts) {
551✔
389
      if(!oldexts.contains(newext)) {
1,008✔
390
         auto* const ext = new_ch.extensions().get(newext);
2✔
391

392
         // We don't make any assumptions about unimplemented extensions.
393
         if(!ext->is_implemented()) {
2✔
394
            continue;
1✔
395
         }
396

397
         // RFC 8446 4.1.2
398
         //    Including a "cookie" extension if one was provided in the
399
         //    HelloRetryRequest.
400
         if(newext == Cookie::static_type()) {
1✔
401
            continue;
1✔
402
         }
403

404
         // RFC 8446 4.1.2
405
         //    Optionally adding, removing, or changing the length of the
406
         //    "padding" extension.
407
         //
408
         // TODO: implement the Padding extension
409
         // if(newext == Padding::static_type())
410
         //    continue;
411

412
         throw TLS_Exception(Alert::UnsupportedExtension, "Added an extension in updated Client Hello");
×
413
      }
414
   }
415

416
   // RFC 8446 4.1.2
417
   //    Removing the "early_data" extension (Section 4.2.10) if one was
418
   //    present.  Early data is not permitted after a HelloRetryRequest.
419
   if(new_ch.extensions().has<EarlyDataIndication>()) {
47✔
420
      throw TLS_Exception(Alert::IllegalParameter, "Updated Client Hello indicates early data");
×
421
   }
422

423
   // RFC 8446 4.1.2
424
   //    The client MUST send the same ClientHello without modification,
425
   //    except as follows: [key_share, pre_shared_key, early_data, cookie, padding]
426
   //
427
   // Verify that extensions whose content must not change between the
428
   // initial and retried Client Hello have identical wire encodings.
429
   const std::set<Extension_Code> extensions_allowed_to_change = {
47✔
430
      Extension_Code::KeyShare, Extension_Code::PresharedKey, Extension_Code::EarlyData, Extension_Code::Cookie,
431
      // TODO: add Padding extension code here once implemented
432
   };
49✔
433

434
   for(const auto ext_type : oldexts) {
549✔
435
      if(extensions_allowed_to_change.contains(ext_type)) {
1,004✔
436
         continue;
62✔
437
      }
438

439
      const auto old_bytes = extensions().extension_raw_bytes(ext_type);
440✔
440
      const auto new_bytes = new_ch.extensions().extension_raw_bytes(ext_type);
440✔
441

442
      // If both had wire bytes (i.e. both came from deserialization), compare them.
443
      // Extensions that were added programmatically won't have raw bytes.
444
      if(old_bytes.has_value() && new_bytes.has_value() && old_bytes.value() != new_bytes.value()) {
440✔
445
         throw TLS_Exception(Alert::IllegalParameter, "Extension content changed in updated Client Hello");
×
446
      }
447
   }
440✔
448
}
49✔
449

450
void Client_Hello_13::calculate_psk_binders(Transcript_Hash_State transcript_hash) {
160✔
451
   auto* psk = m_data->extensions().get<PSK>();
160✔
452
   if(psk == nullptr || psk->empty()) {
160✔
453
      return;
1✔
454
   }
455

456
   // RFC 8446 4.2.11.2
457
   //    Each entry in the binders list is computed as an HMAC over a
458
   //    transcript hash (see Section 4.4.1) containing a partial ClientHello
459
   //    [...].
460
   //
461
   // Therefore we marshal the entire message prematurely to obtain the
462
   // (truncated) transcript hash, calculate the PSK binders with it, update
463
   // the Client Hello thus finalizing the message. Down the road, it will be
464
   // re-marshalled with the correct binders and sent over the wire.
465
   Handshake_Layer::prepare_message(*this, transcript_hash);
159✔
466
   psk->calculate_binders(transcript_hash);
159✔
467
}
468

469
std::optional<Protocol_Version> Client_Hello_13::highest_supported_version(const Policy& policy) const {
429✔
470
   // RFC 8446 4.2.1
471
   //    The "supported_versions" extension is used by the client to indicate
472
   //    which versions of TLS it supports and by the server to indicate which
473
   //    version it is using. The extension contains a list of supported
474
   //    versions in preference order, with the most preferred version first.
475
   auto* const supvers = m_data->extensions().get<Supported_Versions>();
429✔
476
   BOTAN_ASSERT_NONNULL(supvers);
429✔
477

478
   std::optional<Protocol_Version> result;
429✔
479

480
   for(const auto& v : supvers->versions()) {
2,171✔
481
      // RFC 8446 4.2.1
482
      //    Servers MUST only select a version of TLS present in that extension
483
      //    and MUST ignore any unknown versions that are present in that
484
      //    extension.
485
      if(!v.known_version() || !policy.acceptable_protocol_version(v)) {
1,742✔
486
         continue;
986✔
487
      }
488

489
      result = (result.has_value()) ? std::optional(std::max(result.value(), v)) : std::optional(v);
1,083✔
490
   }
491

492
   return result;
429✔
493
}
494

495
}  // namespace Botan::TLS
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc