• 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

86.29
/src/lib/tls/tls12/msg_client_hello_12.cpp
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
*
7
* Botan is released under the Simplified BSD License (see license.txt)
8
*/
9

10
#include <botan/tls_messages_12.h>
11

12
#include <botan/tls_callbacks.h>
13
#include <botan/tls_exceptn.h>
14
#include <botan/tls_extensions_12.h>
15
#include <botan/tls_policy.h>
16
#include <botan/internal/stl_util.h>
17
#include <botan/internal/tls_handshake_hash.h>
18
#include <botan/internal/tls_handshake_io.h>
19
#include <botan/internal/tls_messages_internal.h>
20

21
namespace Botan::TLS {
22

23
void Client_Hello_12::update_hello_cookie(const Hello_Verify_Request& hello_verify) {
467✔
24
   BOTAN_STATE_CHECK(m_data->legacy_version().is_datagram_protocol());
467✔
25

26
   m_data->m_hello_cookie = hello_verify.cookie();
467✔
27
}
467✔
28

29
bool Client_Hello_12::prefers_compressed_ec_points() const {
28✔
30
   if(const Supported_Point_Formats* ecc_formats = m_data->extensions().get<Supported_Point_Formats>()) {
28✔
31
      return ecc_formats->prefers_compressed();
28✔
32
   }
33
   return false;
34
}
35

36
bool Client_Hello_12::secure_renegotiation() const {
5,171✔
37
   return m_data->extensions().has<Renegotiation_Extension>();
5,171✔
38
}
39

40
std::vector<uint8_t> Client_Hello_12::renegotiation_info() const {
4,167✔
41
   if(const Renegotiation_Extension* reneg = m_data->extensions().get<Renegotiation_Extension>()) {
4,167✔
42
      return reneg->renegotiation_info();
4,167✔
43
   }
44
   return {};
×
45
}
46

47
bool Client_Hello_12::supports_session_ticket() const {
1,196✔
48
   return m_data->extensions().has<Session_Ticket_Extension>();
1,196✔
49
}
50

51
Session_Ticket Client_Hello_12::session_ticket() const {
1,909✔
52
   if(auto* ticket = m_data->extensions().get<Session_Ticket_Extension>()) {
1,909✔
53
      return ticket->contents();
1,780✔
54
   }
55
   return {};
129✔
56
}
57

58
std::optional<Session_Handle> Client_Hello_12::session_handle() const {
987✔
59
   // RFC 5077 3.4
60
   //    If a ticket is presented by the client, the server MUST NOT attempt
61
   //    to use the Session ID in the ClientHello for stateful session
62
   //    resumption.
63
   if(auto ticket = session_ticket(); !ticket.empty()) {
987✔
64
      return Session_Handle(ticket);
808✔
65
   } else if(const auto& id = session_id(); !id.empty()) {
785✔
66
      return Session_Handle(id);
372✔
67
   } else {
68
      return std::nullopt;
692✔
69
   }
987✔
70
}
71

72
bool Client_Hello_12::supports_extended_master_secret() const {
1,198✔
73
   return m_data->extensions().has<Extended_Master_Secret>();
1,198✔
74
}
75

76
bool Client_Hello_12::supports_cert_status_message() const {
1,432✔
77
   return m_data->extensions().has<Certificate_Status_Request>();
1,432✔
78
}
79

80
bool Client_Hello_12::supports_encrypt_then_mac() const {
537✔
81
   return m_data->extensions().has<Encrypt_then_MAC>();
537✔
82
}
83

84
void Client_Hello_12::add_tls12_supported_groups_extensions(const Policy& policy) {
2,696✔
85
   // RFC 7919 3.
86
   //    A client that offers a group MUST be able and willing to perform a DH
87
   //    key exchange using that group.
88
   //
89
   // We don't support hybrid key exchange in TLS 1.2
90

91
   std::vector<Group_Params> compatible_kex_groups;
2,696✔
92
   for(const auto& group : policy.key_exchange_groups()) {
37,319✔
93
      if(!group.is_post_quantum()) {
34,623✔
94
         compatible_kex_groups.push_back(group);
26,642✔
95
      }
96
   }
×
97

98
   auto supported_groups = std::make_unique<Supported_Groups>(std::move(compatible_kex_groups));
2,696✔
99

100
   if(!supported_groups->ec_groups().empty()) {
5,384✔
101
      // NOLINTNEXTLINE(*-owning-memory)
102
      m_data->extensions().add(new Supported_Point_Formats(policy.use_ecc_point_compression()));
2,688✔
103
   }
104

105
   m_data->extensions().add(std::move(supported_groups));
2,696✔
106
}
5,392✔
107

108
/*
109
* Create a new Client Hello message
110
*/
111
Client_Hello_12::Client_Hello_12(Handshake_IO& io,
2,485✔
112
                                 Handshake_Hash& hash,
113
                                 const Policy& policy,
114
                                 Callbacks& cb,
115
                                 RandomNumberGenerator& rng,
116
                                 const std::vector<uint8_t>& reneg_info,
117
                                 const Client_Hello_12::Settings& client_settings,
118
                                 const std::vector<std::string>& next_protocols) {
2,485✔
119
   m_data->m_legacy_version = client_settings.protocol_version();
2,485✔
120
   m_data->m_random = make_hello_random(rng, cb, policy);
2,485✔
121
   m_data->m_suites = policy.ciphersuite_list(client_settings.protocol_version());
2,485✔
122

123
   if(!policy.acceptable_protocol_version(m_data->legacy_version())) {
2,485✔
124
      throw Internal_Error("Offering " + m_data->legacy_version().to_string() +
×
125
                           " but our own policy does not accept it");
×
126
   }
127

128
   /*
129
    * Place all empty extensions in front to avoid a bug in some systems
130
    * which reject hellos when the last extension in the list is empty.
131
    */
132

133
   // NOLINTBEGIN(*-owning-memory)
134

135
   // EMS must always be used with TLS 1.2, regardless of the policy used.
136

137
   m_data->extensions().add(new Extended_Master_Secret);
2,485✔
138

139
   if(policy.negotiate_encrypt_then_mac()) {
2,485✔
140
      m_data->extensions().add(new Encrypt_then_MAC);
2,472✔
141
   }
142

143
   m_data->extensions().add(new Session_Ticket_Extension());
2,485✔
144

145
   m_data->extensions().add(new Renegotiation_Extension(reneg_info));
2,485✔
146

147
   m_data->extensions().add(new Supported_Versions(m_data->legacy_version(), policy));
2,485✔
148

149
   if(Server_Name_Indicator::hostname_acceptable_for_sni(client_settings.hostname())) {
2,485✔
150
      m_data->extensions().add(new Server_Name_Indicator(client_settings.hostname()));
2,462✔
151
   }
152

153
   if(policy.support_cert_status_message()) {
2,485✔
154
      m_data->extensions().add(new Certificate_Status_Request({}, {}));
4,332✔
155
   }
156

157
   add_tls12_supported_groups_extensions(policy);
2,485✔
158

159
   m_data->extensions().add(new Signature_Algorithms(policy.acceptable_signature_schemes()));
2,485✔
160
   if(auto cert_signing_prefs = policy.acceptable_certificate_signature_schemes()) {
2,485✔
161
      // RFC 8446 4.2.3
162
      //    TLS 1.2 implementations SHOULD also process this extension.
163
      //    Implementations which have the same policy in both cases MAY omit
164
      //    the "signature_algorithms_cert" extension.
165
      m_data->extensions().add(new Signature_Algorithms_Cert(std::move(cert_signing_prefs.value())));
×
166
   }
×
167

168
   if(reneg_info.empty() && !next_protocols.empty()) {
2,485✔
169
      m_data->extensions().add(new Application_Layer_Protocol_Notification(next_protocols));
126✔
170
   }
171

172
   if(m_data->legacy_version().is_datagram_protocol()) {
2,485✔
173
      m_data->extensions().add(new SRTP_Protection_Profiles(policy.srtp_profiles()));
1,203✔
174
   }
175

176
   // NOLINTEND(*-owning-memory)
177

178
   cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Client, type());
2,485✔
179

180
   hash.update(io.send(*this));
4,970✔
181
}
2,485✔
182

183
/*
184
* Create a new Client Hello message (session resumption case)
185
*/
186
Client_Hello_12::Client_Hello_12(Handshake_IO& io,
211✔
187
                                 Handshake_Hash& hash,
188
                                 const Policy& policy,
189
                                 Callbacks& cb,
190
                                 RandomNumberGenerator& rng,
191
                                 const std::vector<uint8_t>& reneg_info,
192
                                 const Session_with_Handle& session,
193
                                 const std::vector<std::string>& next_protocols) {
211✔
194
   m_data->m_legacy_version = session.session.version();
211✔
195
   m_data->m_random = make_hello_random(rng, cb, policy);
211✔
196

197
   // RFC 5077 3.4
198
   //    When presenting a ticket, the client MAY generate and include a
199
   //    Session ID in the TLS ClientHello. [...] If a ticket is presented by
200
   //    the client, the server MUST NOT attempt to use the Session ID in the
201
   //    ClientHello for stateful session resumption.
202
   m_data->m_session_id = session.handle.id().value_or(Session_ID(make_hello_random(rng, cb, policy)));
453✔
203
   m_data->m_suites = policy.ciphersuite_list(m_data->legacy_version());
211✔
204

205
   if(!policy.acceptable_protocol_version(session.session.version())) {
211✔
206
      throw Internal_Error("Offering " + m_data->legacy_version().to_string() +
×
207
                           " but our own policy does not accept it");
×
208
   }
209

210
   if(!value_exists(m_data->ciphersuites(), session.session.ciphersuite_code())) {
422✔
211
      m_data->m_suites.push_back(session.session.ciphersuite_code());
4✔
212
   }
213

214
   /*
215
    * As EMS must always be used with TLS 1.2, add it even if it wasn't used
216
    * in the original session. If the server understands it and follows the
217
    * RFC it should reject our resume attempt and upgrade us to a new session
218
    * with the EMS protection.
219
    */
220
   // NOLINTBEGIN(*-owning-memory)
221
   m_data->extensions().add(new Extended_Master_Secret);
211✔
222

223
   if(session.session.supports_encrypt_then_mac()) {
211✔
224
      m_data->extensions().add(new Encrypt_then_MAC);
1✔
225
   }
226

227
   if(session.handle.is_ticket()) {
211✔
228
      m_data->extensions().add(new Session_Ticket_Extension(session.handle.ticket().value()));
360✔
229
   }
230

231
   m_data->extensions().add(new Renegotiation_Extension(reneg_info));
211✔
232

233
   const std::string hostname = session.session.server_info().hostname();
211✔
234

235
   if(Server_Name_Indicator::hostname_acceptable_for_sni(hostname)) {
211✔
236
      m_data->extensions().add(new Server_Name_Indicator(hostname));
211✔
237
   }
238

239
   if(policy.support_cert_status_message()) {
211✔
240
      m_data->extensions().add(new Certificate_Status_Request({}, {}));
60✔
241
   }
242

243
   add_tls12_supported_groups_extensions(policy);
211✔
244

245
   m_data->extensions().add(new Signature_Algorithms(policy.acceptable_signature_schemes()));
211✔
246
   if(auto cert_signing_prefs = policy.acceptable_certificate_signature_schemes()) {
211✔
247
      // RFC 8446 4.2.3
248
      //    TLS 1.2 implementations SHOULD also process this extension.
249
      //    Implementations which have the same policy in both cases MAY omit
250
      //    the "signature_algorithms_cert" extension.
251
      m_data->extensions().add(new Signature_Algorithms_Cert(std::move(cert_signing_prefs.value())));
×
252
   }
×
253

254
   if(reneg_info.empty() && !next_protocols.empty()) {
211✔
255
      m_data->extensions().add(new Application_Layer_Protocol_Notification(next_protocols));
15✔
256
   }
257
   // NOLINTEND(*-owning-memory)
258

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

261
   hash.update(io.send(*this));
633✔
262
}
211✔
263

264
Client_Hello_12::Client_Hello_12(const std::vector<uint8_t>& buf) :
2,897✔
265
      Client_Hello_12(std::make_unique<Client_Hello_Internal>(buf)) {}
2,897✔
266

267
Client_Hello_12::Client_Hello_12(std::unique_ptr<Client_Hello_Internal> data) : Client_Hello_12_Shim(std::move(data)) {
1,884✔
268
   const uint16_t TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF;
1,884✔
269

270
   if(offered_suite(static_cast<uint16_t>(TLS_EMPTY_RENEGOTIATION_INFO_SCSV))) {
1,884✔
271
      if(const Renegotiation_Extension* reneg = m_data->extensions().get<Renegotiation_Extension>()) {
6✔
272
         if(!reneg->renegotiation_info().empty()) {
×
273
            throw TLS_Exception(Alert::HandshakeFailure, "Client sent renegotiation SCSV and non-empty extension");
×
274
         }
275
      } else {
276
         // add fake extension
277
         m_data->extensions().add(new Renegotiation_Extension());  // NOLINT(*-owning-memory)
6✔
278
      }
279
   }
280
}
1,884✔
281

282
Hello_Request::Hello_Request(Handshake_IO& io) {
×
283
   io.send(*this);
×
284
}
×
285

286
Hello_Request::Hello_Request(const std::vector<uint8_t>& buf) {
42✔
287
   if(!buf.empty()) {
42✔
288
      throw Decoding_Error("Bad Hello_Request, has non-zero size");
2✔
289
   }
290
}
40✔
291

292
std::vector<uint8_t> Hello_Request::serialize() const {
×
293
   return std::vector<uint8_t>();
×
294
}
295

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