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

randombit / botan / 22076829412

16 Feb 2026 08:36PM UTC coverage: 90.044%. Remained the same
22076829412

push

github

web-flow
Merge pull request #5303 from Rohde-Schwarz/refactor/tls12_13_extensions

Refactor: Organize TLS Extensions into TLS 1.2 and 1.3 Modules

102334 of 113649 relevant lines covered (90.04%)

11380212.03 hits per line

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

92.76
/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)) {
465✔
31
   const auto& exts = m_data->extensions();
465✔
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>());
465✔
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()) {
465✔
46
      throw TLS_Exception(Alert::DecodeError, "TLS 1.3 Client Hello has invalid legacy_version");
1✔
47
   }
48

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

58
   // RFC 8446 4.2.9
59
   //    A client MUST provide a "psk_key_exchange_modes" extension if it
60
   //    offers a "pre_shared_key" extension. If clients offer "pre_shared_key"
61
   //    without a "psk_key_exchange_modes" extension, servers MUST abort
62
   //    the handshake.
63
   if(exts.has<PSK>()) {
460✔
64
      if(!exts.has<PSK_Key_Exchange_Modes>()) {
116✔
65
         throw TLS_Exception(Alert::MissingExtension,
1✔
66
                             "Client Hello offered a PSK without a psk_key_exchange_modes extension");
1✔
67
      }
68

69
      // RFC 8446 4.2.11
70
      //     The "pre_shared_key" extension MUST be the last extension in the
71
      //     ClientHello [...]. Servers MUST check that it is the last extension
72
      //     and otherwise fail the handshake with an "illegal_parameter" alert.
73
      if(exts.all().back()->type() != Extension_Code::PresharedKey) {
115✔
74
         throw TLS_Exception(Alert::IllegalParameter, "PSK extension was not at the very end of the Client Hello");
2✔
75
      }
76
   }
77

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

104
   if(exts.has<Key_Share>()) {
453✔
105
      auto* const supported_ext = exts.get<Supported_Groups>();
453✔
106
      BOTAN_ASSERT_NONNULL(supported_ext);
453✔
107
      const auto supports = supported_ext->groups();
453✔
108
      const auto offers = exts.get<Key_Share>()->offered_groups();
453✔
109

110
      // RFC 8446 4.2.8
111
      //    Each KeyShareEntry value MUST correspond to a group offered in the
112
      //    "supported_groups" extension and MUST appear in the same order.
113
      //    [...]
114
      //    Clients MUST NOT offer any KeyShareEntry values for groups not
115
      //    listed in the client's "supported_groups" extension.
116
      //
117
      // Note: We can assume that both `offers` and `supports` are unique lists
118
      //       as this is ensured in the parsing code of the extensions.
119
      auto found_in_supported_groups = [&supports, support_offset = -1](auto group) mutable {
2,363✔
120
         const auto i = std::find(supports.begin(), supports.end(), group);
1,910✔
121
         if(i == supports.end()) {
1,910✔
122
            return false;
123
         }
124

125
         const auto found_at = std::distance(supports.begin(), i);
1,910✔
126
         if(found_at <= support_offset) {
1,910✔
127
            return false;  // The order that groups appear in "key_share" and
128
                           // "supported_groups" must be the same
129
         }
130

131
         support_offset = static_cast<decltype(support_offset)>(found_at);
1,910✔
132
         return true;
1,910✔
133
      };
453✔
134

135
      for(const auto offered : offers) {
2,363✔
136
         // RFC 8446 4.2.8
137
         //    Servers MAY check for violations of these rules and abort the
138
         //    handshake with an "illegal_parameter" alert if one is violated.
139
         if(!found_in_supported_groups(offered)) {
1,910✔
140
            throw TLS_Exception(Alert::IllegalParameter,
×
141
                                "Offered key exchange groups do not align with claimed supported groups");
×
142
         }
143
      }
144
   }
906✔
145

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

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

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

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

188
   // NOLINTBEGIN(*-owning-memory)
189
   if(Server_Name_Indicator::hostname_acceptable_for_sni(hostname)) {
988✔
190
      m_data->extensions().add(new Server_Name_Indicator(hostname));
964✔
191
   }
192

193
   m_data->extensions().add(new Supported_Groups(policy.key_exchange_groups()));
988✔
194

195
   m_data->extensions().add(new Key_Share(policy, cb, rng));
988✔
196

197
   m_data->extensions().add(new Supported_Versions(Protocol_Version::TLS_V13, policy));
988✔
198

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

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

212
   if(policy.support_cert_status_message()) {
988✔
213
      m_data->extensions().add(new Certificate_Status_Request({}, {}));
212✔
214
   }
215

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

223
   if(!next_protocols.empty()) {
988✔
224
      m_data->extensions().add(new Application_Layer_Protocol_Notification(next_protocols));
11✔
225
   }
226

227
   // RFC 7250 4.1
228
   //    In order to indicate the support of raw public keys, clients include
229
   //    the client_certificate_type and/or the server_certificate_type
230
   //    extensions in an extended client hello message.
231
   m_data->extensions().add(new Client_Certificate_Type(policy.accepted_client_certificate_types()));
1,976✔
232
   m_data->extensions().add(new Server_Certificate_Type(policy.accepted_server_certificate_types()));
1,976✔
233

234
#if defined(BOTAN_HAS_TLS_12)
235
   if(policy.allow_tls12()) {
988✔
236
      m_data->extensions().add(new Renegotiation_Extension());
959✔
237
      m_data->extensions().add(new Session_Ticket_Extension());
959✔
238

239
      // EMS must always be used with TLS 1.2, regardless of the policy
240
      m_data->extensions().add(new Extended_Master_Secret);
959✔
241

242
      if(policy.negotiate_encrypt_then_mac()) {
959✔
243
         m_data->extensions().add(new Encrypt_then_MAC);
959✔
244
      }
245

246
      if(m_data->extensions().has<Supported_Groups>() &&
1,918✔
247
         !m_data->extensions().get<Supported_Groups>()->ec_groups().empty()) {
1,918✔
248
         m_data->extensions().add(new Supported_Point_Formats(policy.use_ecc_point_compression()));
957✔
249
      }
250
   }
251
#endif
252

253
   if(session.has_value() || !psks.empty()) {
988✔
254
      m_data->extensions().add(new PSK(session, std::move(psks), cb));
108✔
255
   }
256
   // NOLINTEND(*-owning-memory)
257

258
   cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Client, type());
988✔
259

260
   if(m_data->extensions().has<PSK>()) {
988✔
261
      // RFC 8446 4.2.11
262
      //    The "pre_shared_key" extension MUST be the last extension in the
263
      //    ClientHello (this facilitates implementation [...]).
264
      if(m_data->extensions().all().back()->type() != Extension_Code::PresharedKey) {
108✔
265
         throw TLS_Exception(Alert::InternalError,
×
266
                             "Application modified extensions of Client Hello, PSK is not last anymore");
×
267
      }
268
      calculate_psk_binders({});
108✔
269
   }
270
}
988✔
271

272
std::variant<Client_Hello_13, Client_Hello_12_Shim> Client_Hello_13::parse(const std::vector<uint8_t>& buf) {
1,004✔
273
   auto data = std::make_unique<Client_Hello_Internal>(buf);
1,004✔
274
   const auto version = data->version();
978✔
275

276
   if(version.is_pre_tls_13()) {
978✔
277
      return Client_Hello_12_Shim(std::move(data));
1,026✔
278
   } else {
279
      return Client_Hello_13(std::move(data));
918✔
280
   }
281
}
978✔
282

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

290
   auto* hrr_ks = hrr.extensions().get<Key_Share>();
61✔
291
   const auto& supported_groups = m_data->extensions().get<Supported_Groups>()->groups();
61✔
292

293
   if(hrr.extensions().has<Key_Share>()) {
61✔
294
      m_data->extensions().get<Key_Share>()->retry_offer(*hrr_ks, supported_groups, cb, rng);
60✔
295
   }
296

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

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

316
   auto* psk = m_data->extensions().get<PSK>();
58✔
317
   if(psk != nullptr) {
58✔
318
      // Cipher suite should always be a known suite as this is checked upstream
319
      const auto cipher = Ciphersuite::by_id(hrr.ciphersuite());
11✔
320
      BOTAN_ASSERT_NOMSG(cipher.has_value());
11✔
321

322
      // RFC 8446 4.1.4
323
      //    In [...] its updated ClientHello, the client SHOULD NOT offer
324
      //    any pre-shared keys associated with a hash other than that of the
325
      //    selected cipher suite.
326
      psk->filter(cipher.value());
11✔
327

328
      // RFC 8446 4.2.11.2
329
      //    If the server responds with a HelloRetryRequest and the client
330
      //    then sends ClientHello2, its binder will be computed over: [...].
331
      calculate_psk_binders(transcript_hash_state.clone());
11✔
332
   }
333
}
58✔
334

335
void Client_Hello_13::validate_updates(const Client_Hello_13& new_ch) {
44✔
336
   // RFC 8446 4.1.2
337
   //    The client will also send a ClientHello when the server has responded
338
   //    to its ClientHello with a HelloRetryRequest. In that case, the client
339
   //    MUST send the same ClientHello without modification, except as follows:
340

341
   if(m_data->session_id() != new_ch.m_data->session_id() || m_data->random() != new_ch.m_data->random() ||
44✔
342
      m_data->ciphersuites() != new_ch.m_data->ciphersuites() ||
88✔
343
      m_data->comp_methods() != new_ch.m_data->comp_methods()) {
44✔
344
      throw TLS_Exception(Alert::IllegalParameter, "Client Hello core values changed after Hello Retry Request");
×
345
   }
346

347
   const auto oldexts = extension_types();
44✔
348
   const auto newexts = new_ch.extension_types();
44✔
349

350
   // Check that extension omissions are justified
351
   for(const auto oldext : oldexts) {
502✔
352
      if(!newexts.contains(oldext)) {
918✔
353
         auto* const ext = extensions().get(oldext);
1✔
354

355
         // We don't make any assumptions about unimplemented extensions.
356
         if(!ext->is_implemented()) {
1✔
357
            continue;
×
358
         }
359

360
         // RFC 8446 4.1.2
361
         //    Removing the "early_data" extension (Section 4.2.10) if one was
362
         //    present.  Early data is not permitted after a HelloRetryRequest.
363
         if(oldext == EarlyDataIndication::static_type()) {
1✔
364
            continue;
×
365
         }
366

367
         // RFC 8446 4.1.2
368
         //    Optionally adding, removing, or changing the length of the
369
         //    "padding" extension.
370
         //
371
         // TODO: implement the Padding extension
372
         // if(oldext == Padding::static_type())
373
         //    continue;
374

375
         throw TLS_Exception(Alert::IllegalParameter, "Extension removed in updated Client Hello");
1✔
376
      }
377
   }
378

379
   // Check that extension additions are justified
380
   for(const auto newext : newexts) {
495✔
381
      if(!oldexts.contains(newext)) {
904✔
382
         auto* const ext = new_ch.extensions().get(newext);
2✔
383

384
         // We don't make any assumptions about unimplemented extensions.
385
         if(!ext->is_implemented()) {
2✔
386
            continue;
1✔
387
         }
388

389
         // RFC 8446 4.1.2
390
         //    Including a "cookie" extension if one was provided in the
391
         //    HelloRetryRequest.
392
         if(newext == Cookie::static_type()) {
1✔
393
            continue;
1✔
394
         }
395

396
         // RFC 8446 4.1.2
397
         //    Optionally adding, removing, or changing the length of the
398
         //    "padding" extension.
399
         //
400
         // TODO: implement the Padding extension
401
         // if(newext == Padding::static_type())
402
         //    continue;
403

404
         throw TLS_Exception(Alert::UnsupportedExtension, "Added an extension in updated Client Hello");
×
405
      }
406
   }
407

408
   // RFC 8446 4.1.2
409
   //    Removing the "early_data" extension (Section 4.2.10) if one was
410
   //    present.  Early data is not permitted after a HelloRetryRequest.
411
   if(new_ch.extensions().has<EarlyDataIndication>()) {
43✔
412
      throw TLS_Exception(Alert::IllegalParameter, "Updated Client Hello indicates early data");
×
413
   }
414

415
   // TODO: Contents of extensions are not checked for update compatibility, see:
416
   //
417
   // RFC 8446 4.1.2
418
   //    If a "key_share" extension was supplied in the HelloRetryRequest,
419
   //    replacing the list of shares with a list containing a single
420
   //    KeyShareEntry from the indicated group.
421
   //
422
   //    Updating the "pre_shared_key" extension if present by recomputing
423
   //    the "obfuscated_ticket_age" and binder values and (optionally)
424
   //    removing any PSKs which are incompatible with the server's
425
   //    indicated cipher suite.
426
   //
427
   //    Optionally adding, removing, or changing the length of the
428
   //    "padding" extension.
429
}
44✔
430

431
void Client_Hello_13::calculate_psk_binders(Transcript_Hash_State transcript_hash) {
119✔
432
   auto* psk = m_data->extensions().get<PSK>();
119✔
433
   if(psk == nullptr || psk->empty()) {
119✔
434
      return;
1✔
435
   }
436

437
   // RFC 8446 4.2.11.2
438
   //    Each entry in the binders list is computed as an HMAC over a
439
   //    transcript hash (see Section 4.4.1) containing a partial ClientHello
440
   //    [...].
441
   //
442
   // Therefore we marshal the entire message prematurely to obtain the
443
   // (truncated) transcript hash, calculate the PSK binders with it, update
444
   // the Client Hello thus finalizing the message. Down the road, it will be
445
   // re-marshalled with the correct binders and sent over the wire.
446
   Handshake_Layer::prepare_message(*this, transcript_hash);
118✔
447
   psk->calculate_binders(transcript_hash);
118✔
448
}
449

450
std::optional<Protocol_Version> Client_Hello_13::highest_supported_version(const Policy& policy) const {
390✔
451
   // RFC 8446 4.2.1
452
   //    The "supported_versions" extension is used by the client to indicate
453
   //    which versions of TLS it supports and by the server to indicate which
454
   //    version it is using. The extension contains a list of supported
455
   //    versions in preference order, with the most preferred version first.
456
   auto* const supvers = m_data->extensions().get<Supported_Versions>();
390✔
457
   BOTAN_ASSERT_NONNULL(supvers);
390✔
458

459
   std::optional<Protocol_Version> result;
390✔
460

461
   for(const auto& v : supvers->versions()) {
1,977✔
462
      // RFC 8446 4.2.1
463
      //    Servers MUST only select a version of TLS present in that extension
464
      //    and MUST ignore any unknown versions that are present in that
465
      //    extension.
466
      if(!v.known_version() || !policy.acceptable_protocol_version(v)) {
1,587✔
467
         continue;
899✔
468
      }
469

470
      result = (result.has_value()) ? std::optional(std::max(result.value(), v)) : std::optional(v);
986✔
471
   }
472

473
   return result;
390✔
474
}
475

476
}  // 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