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

randombit / botan / 21756362022

06 Feb 2026 03:40PM UTC coverage: 90.069% (-0.004%) from 90.073%
21756362022

Pull #5293

github

web-flow
Merge 5018e3dec into 8ea0ca252
Pull Request #5293: Refactor: Disentangle Client_Hello implementations

102251 of 113525 relevant lines covered (90.07%)

11438009.49 hits per line

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

93.8
/src/lib/tls/msg_client_hello.cpp
1
/*
2
* TLS 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
*     2026 René Meusel - Rohde & Schwarz Cybersecurity GmbH
9
*
10
* Botan is released under the Simplified BSD License (see license.txt)
11
*/
12

13
#include <botan/tls_messages.h>
14
#include <botan/internal/tls_messages_internal.h>
15

16
#include <botan/hash.h>
17
#include <botan/rng.h>
18
#include <botan/tls_alert.h>
19
#include <botan/tls_callbacks.h>
20
#include <botan/tls_exceptn.h>
21
#include <botan/tls_policy.h>
22
#include <botan/internal/tls_reader.h>
23

24
namespace Botan::TLS {
25

26
std::vector<uint8_t> make_hello_random(RandomNumberGenerator& rng, Callbacks& cb, const Policy& policy) {
6,962✔
27
   auto buf = rng.random_vec<std::vector<uint8_t>>(32);
6,962✔
28

29
   if(policy.hash_hello_random()) {
6,962✔
30
      auto sha256 = HashFunction::create_or_throw("SHA-256");
6,947✔
31
      sha256->update(buf);
6,947✔
32
      sha256->final(buf);
6,947✔
33
   }
6,947✔
34

35
   // TLS 1.3 does not require the insertion of a timestamp in the client hello
36
   // random. When offering both TLS 1.2 and 1.3 we nevertheless comply with the
37
   // legacy specification.
38
   if(policy.include_time_in_hello_random() && (policy.allow_tls12() || policy.allow_dtls12())) {
6,962✔
39
      const uint32_t time32 = static_cast<uint32_t>(std::chrono::system_clock::to_time_t(cb.tls_current_timestamp()));
6,939✔
40

41
      store_be(time32, buf.data());
6,939✔
42
   }
43

44
   return buf;
6,962✔
45
}
×
46

47
Client_Hello_Internal::Client_Hello_Internal(const std::vector<uint8_t>& buf) {
3,901✔
48
   if(buf.size() < 41) {
3,901✔
49
      throw Decoding_Error("Client_Hello: Packet corrupted");
45✔
50
   }
51

52
   TLS_Data_Reader reader("ClientHello", buf);
3,856✔
53

54
   const uint8_t major_version = reader.get_byte();
3,856✔
55
   const uint8_t minor_version = reader.get_byte();
3,856✔
56

57
   m_legacy_version = Protocol_Version(major_version, minor_version);
3,856✔
58
   m_random = reader.get_fixed<uint8_t>(32);
3,856✔
59
   m_session_id = Session_ID(reader.get_range<uint8_t>(1, 0, 32));
3,856✔
60

61
   if(m_legacy_version.is_datagram_protocol()) {
2,999✔
62
      auto sha256 = HashFunction::create_or_throw("SHA-256");
795✔
63
      sha256->update(reader.get_data_read_so_far());
795✔
64

65
      m_hello_cookie = reader.get_range<uint8_t>(1, 0, 255);
795✔
66

67
      sha256->update(reader.get_remaining());
795✔
68
      m_cookie_input_bits = sha256->final_stdvec();
795✔
69
   }
795✔
70

71
   m_suites = reader.get_range_vector<uint16_t>(2, 1, 32767);
2,999✔
72
   m_comp_methods = reader.get_range_vector<uint8_t>(1, 1, 255);
2,891✔
73

74
   m_extensions.deserialize(reader, Connection_Side::Client, Handshake_Type::ClientHello);
2,891✔
75
}
5,995✔
76

77
Protocol_Version Client_Hello_Internal::version() const {
978✔
78
   // RFC 8446 4.2.1
79
   //    If [the "supported_versions"] extension is not present, servers
80
   //    which are compliant with this specification and which also support
81
   //    TLS 1.2 MUST negotiate TLS 1.2 or prior as specified in [RFC5246],
82
   //    even if ClientHello.legacy_version is 0x0304 or later.
83
   //
84
   // RFC 8446 4.2.1
85
   //    Servers MUST be prepared to receive ClientHellos that include
86
   //    [the supported_versions] extension but do not include 0x0304 in
87
   //    the list of versions.
88
   //
89
   // RFC 8446 4.1.2
90
   //    TLS 1.3 ClientHellos are identified as having a legacy_version of
91
   //    0x0303 and a supported_versions extension present with 0x0304 as
92
   //    the highest version indicated therein.
93
   if(!extensions().has<Supported_Versions>() ||
1,461✔
94
      !extensions().get<Supported_Versions>()->supports(Protocol_Version::TLS_V13)) {
483✔
95
      // The exact legacy_version is ignored we just inspect it to
96
      // distinguish TLS and DTLS.
97
      return (m_legacy_version.is_datagram_protocol()) ? Protocol_Version::DTLS_V12 : Protocol_Version::TLS_V12;
1,026✔
98
   }
99

100
   // Note: The Client_Hello_13 class will make sure that legacy_version
101
   //       is exactly 0x0303 (aka ossified TLS 1.2)
102
   return Protocol_Version::TLS_V13;
465✔
103
}
104

105
Client_Hello::Client_Hello(Client_Hello&&) noexcept = default;
10,494✔
106
Client_Hello& Client_Hello::operator=(Client_Hello&&) noexcept = default;
43✔
107

108
Client_Hello::~Client_Hello() = default;
17,023✔
109

110
Client_Hello::Client_Hello() : m_data(std::make_unique<Client_Hello_Internal>()) {}
3,684✔
111

112
/*
113
* Read a counterparty client hello
114
*/
115
Client_Hello::Client_Hello(std::unique_ptr<Client_Hello_Internal> data) : m_data(std::move(data)) {
2,862✔
116
   BOTAN_ASSERT_NONNULL(m_data);
2,862✔
117
}
2,862✔
118

119
Handshake_Type Client_Hello::type() const {
16,308✔
120
   return Handshake_Type::ClientHello;
16,308✔
121
}
122

123
Protocol_Version Client_Hello::legacy_version() const {
8,243✔
124
   return m_data->legacy_version();
8,243✔
125
}
126

127
const std::vector<uint8_t>& Client_Hello::random() const {
3,844✔
128
   return m_data->random();
3,844✔
129
}
130

131
const Session_ID& Client_Hello::session_id() const {
3,323✔
132
   return m_data->session_id();
3,323✔
133
}
134

135
const std::vector<uint8_t>& Client_Hello::compression_methods() const {
1,383✔
136
   return m_data->comp_methods();
1,383✔
137
}
138

139
const std::vector<uint16_t>& Client_Hello::ciphersuites() const {
1,430✔
140
   return m_data->ciphersuites();
1,430✔
141
}
142

143
std::set<Extension_Code> Client_Hello::extension_types() const {
3,256✔
144
   return m_data->extensions().extension_types();
3,256✔
145
}
146

147
const Extensions& Client_Hello::extensions() const {
9,822✔
148
   return m_data->extensions();
9,822✔
149
}
150

151
/*
152
* Serialize a Client Hello message
153
*/
154
std::vector<uint8_t> Client_Hello::serialize() const {
4,347✔
155
   std::vector<uint8_t> buf;
4,347✔
156
   buf.reserve(1024);  // working around GCC warning
4,347✔
157

158
   buf.push_back(m_data->legacy_version().major_version());
4,347✔
159
   buf.push_back(m_data->legacy_version().minor_version());
4,347✔
160
   buf += m_data->random();
4,347✔
161

162
   append_tls_length_value(buf, m_data->session_id().get(), 1);
4,347✔
163

164
   if(m_data->legacy_version().is_datagram_protocol()) {
4,347✔
165
      append_tls_length_value(buf, m_data->hello_cookie(), 1);
951✔
166
   }
167

168
   append_tls_length_value(buf, m_data->ciphersuites(), 2);
4,347✔
169
   append_tls_length_value(buf, m_data->comp_methods(), 1);
4,347✔
170

171
   /*
172
   * May not want to send extensions at all in some cases. If so,
173
   * should include SCSV value (if reneg info is empty, if not we are
174
   * renegotiating with a modern server)
175
   */
176

177
   buf += m_data->extensions().serialize(Connection_Side::Client);
4,347✔
178

179
   return buf;
4,347✔
180
}
×
181

182
std::vector<uint8_t> Client_Hello::cookie_input_data() const {
790✔
183
   BOTAN_STATE_CHECK(!m_data->hello_cookie_input_bits().empty());
790✔
184

185
   return m_data->hello_cookie_input_bits();
790✔
186
}
187

188
/*
189
* Check if we offered this ciphersuite
190
*/
191
bool Client_Hello::offered_suite(uint16_t ciphersuite) const {
5,284✔
192
   return std::find(m_data->ciphersuites().cbegin(), m_data->ciphersuites().cend(), ciphersuite) !=
5,284✔
193
          m_data->ciphersuites().cend();
5,284✔
194
}
195

196
std::vector<Signature_Scheme> Client_Hello::signature_schemes() const {
4,120✔
197
   if(const Signature_Algorithms* sigs = m_data->extensions().get<Signature_Algorithms>()) {
4,120✔
198
      return sigs->supported_schemes();
4,118✔
199
   }
200
   return {};
2✔
201
}
202

203
std::vector<Signature_Scheme> Client_Hello::certificate_signature_schemes() const {
1,020✔
204
   // RFC 8446 4.2.3
205
   //   If no "signature_algorithms_cert" extension is present, then the
206
   //   "signature_algorithms" extension also applies to signatures appearing
207
   //   in certificates.
208
   if(const Signature_Algorithms_Cert* sigs = m_data->extensions().get<Signature_Algorithms_Cert>()) {
1,020✔
209
      return sigs->supported_schemes();
×
210
   } else {
211
      return signature_schemes();
1,020✔
212
   }
213
}
214

215
std::vector<Group_Params> Client_Hello::supported_ecc_curves() const {
1,368✔
216
   if(const Supported_Groups* groups = m_data->extensions().get<Supported_Groups>()) {
1,368✔
217
      return groups->ec_groups();
1,365✔
218
   }
219
   return {};
3✔
220
}
221

222
std::vector<Group_Params> Client_Hello::supported_dh_groups() const {
1,514✔
223
   if(const Supported_Groups* groups = m_data->extensions().get<Supported_Groups>()) {
1,514✔
224
      return groups->dh_groups();
1,508✔
225
   }
226
   return std::vector<Group_Params>();
6✔
227
}
228

229
std::string Client_Hello::sni_hostname() const {
4,224✔
230
   if(const Server_Name_Indicator* sni = m_data->extensions().get<Server_Name_Indicator>()) {
4,224✔
231
      return sni->host_name();
4,094✔
232
   }
233
   return "";
130✔
234
}
235

236
std::vector<Protocol_Version> Client_Hello::supported_versions() const {
1,385✔
237
   if(const Supported_Versions* versions = m_data->extensions().get<Supported_Versions>()) {
1,385✔
238
      return versions->versions();
300✔
239
   }
240
   return {};
1,085✔
241
}
242

243
bool Client_Hello::supports_alpn() const {
1,127✔
244
   return m_data->extensions().has<Application_Layer_Protocol_Notification>();
1,127✔
245
}
246

247
bool Client_Hello::sent_signature_algorithms() const {
×
248
   return m_data->extensions().has<Signature_Algorithms>();
×
249
}
250

251
std::vector<std::string> Client_Hello::next_protocols() const {
149✔
252
   if(auto* alpn = m_data->extensions().get<Application_Layer_Protocol_Notification>()) {
149✔
253
      return alpn->protocols();
149✔
254
   }
255
   return {};
×
256
}
257

258
std::vector<uint16_t> Client_Hello::srtp_profiles() const {
294✔
259
   if(const SRTP_Protection_Profiles* srtp = m_data->extensions().get<SRTP_Protection_Profiles>()) {
294✔
260
      return srtp->profiles();
4✔
261
   }
262
   return {};
290✔
263
}
264

265
const std::vector<uint8_t>& Client_Hello::cookie() const {
790✔
266
   return m_data->hello_cookie();
790✔
267
}
268

269
Client_Hello_12_Shim::Client_Hello_12_Shim(std::unique_ptr<Client_Hello_Internal> data) :
2,397✔
270
      Client_Hello(std::move(data)) {
2,397✔
271
   const uint16_t TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF;
2,397✔
272

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

285
Client_Hello_12_Shim::Client_Hello_12_Shim(const std::vector<uint8_t>& buf) :
2,897✔
286
      Client_Hello_12_Shim(std::make_unique<Client_Hello_Internal>(buf)) {}
2,897✔
287

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