• 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

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) {
525✔
24
   BOTAN_STATE_CHECK(m_data->legacy_version().is_datagram_protocol());
525✔
25

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

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

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

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

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

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

58
std::optional<Session_Handle> Client_Hello_12::session_handle() const {
1,061✔
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()) {
1,061✔
64
      return Session_Handle(ticket);
956✔
65
   } else if(const auto& id = session_id(); !id.empty()) {
822✔
66
      return Session_Handle(id);
552✔
67
   } else {
68
      return std::nullopt;
684✔
69
   }
1,061✔
70
}
71

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

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

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

84
void Client_Hello_12::add_tls12_supported_groups_extensions(const Policy& policy) {
2,755✔
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,755✔
92
   for(const auto& group : policy.key_exchange_groups()) {
38,158✔
93
      if(!group.is_post_quantum()) {
35,403✔
94
         compatible_kex_groups.push_back(group);
27,241✔
95
      }
96
   }
×
97

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

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

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

108
/*
109
* Create a new Client Hello message
110
*/
111
Client_Hello_12::Client_Hello_12(Handshake_IO& io,
2,517✔
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,517✔
119
   m_data->m_legacy_version = client_settings.protocol_version();
2,517✔
120
   m_data->m_random = make_hello_random(rng, cb, policy);
2,517✔
121
   m_data->m_suites = policy.ciphersuite_list(client_settings.protocol_version());
2,517✔
122

123
   if(!policy.acceptable_protocol_version(m_data->legacy_version())) {
2,517✔
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,517✔
138

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

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

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

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

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

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

157
   add_tls12_supported_groups_extensions(policy);
2,517✔
158

159
   m_data->extensions().add(new Signature_Algorithms(policy.acceptable_signature_schemes()));
2,517✔
160
   if(auto cert_signing_prefs = policy.acceptable_certificate_signature_schemes()) {
2,517✔
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,517✔
169
      m_data->extensions().add(new Application_Layer_Protocol_Notification(next_protocols));
123✔
170
   }
171

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

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

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

180
   hash.update(io.send(*this));
5,034✔
181
}
2,517✔
182

183
/*
184
* Create a new Client Hello message (session resumption case)
185
*/
186
Client_Hello_12::Client_Hello_12(Handshake_IO& io,
238✔
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) {
238✔
194
   m_data->m_legacy_version = session.session.version();
238✔
195
   m_data->m_random = make_hello_random(rng, cb, policy);
238✔
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)));
510✔
203
   m_data->m_suites = policy.ciphersuite_list(m_data->legacy_version());
238✔
204

205
   if(!policy.acceptable_protocol_version(session.session.version())) {
238✔
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())) {
476✔
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);
238✔
222

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

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

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

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

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

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

243
   add_tls12_supported_groups_extensions(policy);
238✔
244

245
   m_data->extensions().add(new Signature_Algorithms(policy.acceptable_signature_schemes()));
238✔
246
   if(auto cert_signing_prefs = policy.acceptable_certificate_signature_schemes()) {
238✔
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()) {
238✔
255
      m_data->extensions().add(new Application_Layer_Protocol_Notification(next_protocols));
18✔
256
   }
257

258
   // NOLINTEND(*-owning-memory)
259

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

262
   hash.update(io.send(*this));
714✔
263
}
238✔
264

265
Client_Hello_12::Client_Hello_12(const std::vector<uint8_t>& buf) :
3,053✔
266
      Client_Hello_12(std::make_unique<Client_Hello_Internal>(buf)) {}
3,053✔
267

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

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

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

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

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

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