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

randombit / botan / 21610880055

03 Feb 2026 12:00AM UTC coverage: 90.07%. Remained the same
21610880055

push

github

web-flow
Merge pull request #5280 from randombit/jack/reduce-tls-headers

Reduce header pollution in TLS headers

102228 of 113498 relevant lines covered (90.07%)

11548178.67 hits per line

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

91.26
/src/lib/tls/tls12/msg_server_kex.cpp
1
/*
2
* Server Key Exchange Message
3
* (C) 2004-2010,2012,2015,2016 Jack Lloyd
4
*     2017 Harry Reimann, Rohde & Schwarz Cybersecurity
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8

9
#include <botan/tls_messages.h>
10

11
#include <botan/credentials_manager.h>
12
#include <botan/pubkey.h>
13
#include <botan/tls_callbacks.h>
14
#include <botan/tls_extensions.h>
15
#include <botan/tls_policy.h>
16
#include <botan/internal/loadstor.h>
17
#include <botan/internal/target_info.h>
18
#include <botan/internal/tls_handshake_io.h>
19
#include <botan/internal/tls_handshake_state.h>
20
#include <botan/internal/tls_reader.h>
21

22
#include <botan/dh.h>
23

24
namespace Botan::TLS {
25

26
/**
27
* Create a new Server Key Exchange message
28
*/
29
Server_Key_Exchange::Server_Key_Exchange(Handshake_IO& io,
659✔
30
                                         Handshake_State& state,
31
                                         const Policy& policy,
32
                                         Credentials_Manager& creds,
33
                                         RandomNumberGenerator& rng,
34
                                         const Private_Key* signing_key) {
659✔
35
   const std::string hostname = state.client_hello()->sni_hostname();
659✔
36
   const Kex_Algo kex_algo = state.ciphersuite().kex_method();
659✔
37

38
   if(kex_algo == Kex_Algo::PSK || kex_algo == Kex_Algo::ECDHE_PSK) {
659✔
39
      const std::string identity_hint = creds.psk_identity_hint("tls-server", hostname);
50✔
40

41
      append_tls_length_value(m_params, identity_hint, 2);
100✔
42
   }
50✔
43

44
   if(kex_algo == Kex_Algo::DH) {
659✔
45
      const std::vector<Group_Params> dh_groups = state.client_hello()->supported_dh_groups();
6✔
46

47
      m_shared_group = Group_Params::NONE;
6✔
48

49
      /*
50
      RFC 7919 requires that if the client sends any groups in the FFDHE
51
      range, that we must select one of these. If this is not possible,
52
      then we are required to reject the connection.
53

54
      If the client did not send any DH groups, but did offer DH ciphersuites
55
      and we selected one, then consult the policy for which DH group to pick.
56
      */
57

58
      if(dh_groups.empty()) {
6✔
59
         m_shared_group = policy.default_dh_group();
×
60
      } else {
61
         m_shared_group = policy.choose_key_exchange_group(dh_groups, {});
12✔
62
      }
63

64
      if(m_shared_group.value() == Group_Params::NONE) {
6✔
65
         throw TLS_Exception(Alert::HandshakeFailure, "Could not agree on a DH group with the client");
×
66
      }
67

68
      // The policy had better return a group we know about:
69
      BOTAN_ASSERT(m_shared_group.value().is_dh_named_group(), "DH ciphersuite is using a known finite field group");
6✔
70

71
      // Note: TLS 1.2 allows defining and using arbitrary DH groups (additional
72
      //       to the named and standardized ones). This API doesn't allow the
73
      //       server to make use of that at the moment. TLS 1.3 does not
74
      //       provide this flexibility!
75
      //
76
      // A possible implementation strategy in case one would ever need that:
77
      // `Policy::default_dh_group()` could return a `std::variant<Group_Params,
78
      // DL_Group>`, allowing it to define arbitrary groups.
79
      m_kex_key = state.callbacks().tls_generate_ephemeral_key(m_shared_group.value(), rng);
6✔
80
      auto* dh = dynamic_cast<DH_PrivateKey*>(m_kex_key.get());
6✔
81
      if(dh == nullptr) {
6✔
82
         throw TLS_Exception(Alert::InternalError, "Application did not provide a Diffie-Hellman key");
×
83
      }
84

85
      append_tls_length_value(m_params, dh->get_int_field("p").serialize(), 2);
18✔
86
      append_tls_length_value(m_params, dh->get_int_field("g").serialize(), 2);
18✔
87
      append_tls_length_value(m_params, dh->public_value(), 2);
18✔
88
   } else if(kex_algo == Kex_Algo::ECDH || kex_algo == Kex_Algo::ECDHE_PSK) {
659✔
89
      const std::vector<Group_Params> ec_groups = state.client_hello()->supported_ecc_curves();
615✔
90

91
      if(ec_groups.empty()) {
615✔
92
         throw Internal_Error("Client sent no ECC extension but we negotiated ECDH");
×
93
      }
94

95
      m_shared_group = policy.choose_key_exchange_group(ec_groups, {});
1,230✔
96

97
      if(m_shared_group.value() == Group_Params::NONE) {
615✔
98
         throw TLS_Exception(Alert::HandshakeFailure, "No shared ECC group with client");
×
99
      }
100

101
      m_kex_key = [&] {
1,845✔
102
         if(m_shared_group->is_ecdh_named_curve()) {
615✔
103
            const auto pubkey_point_format = state.client_hello()->prefers_compressed_ec_points()
28✔
104
                                                ? EC_Point_Format::Compressed
28✔
105
                                                : EC_Point_Format::Uncompressed;
28✔
106
            return state.callbacks().tls12_generate_ephemeral_ecdh_key(*m_shared_group, rng, pubkey_point_format);
28✔
107
         } else {
108
            return state.callbacks().tls_generate_ephemeral_key(*m_shared_group, rng);
1,174✔
109
         }
110
      }();
615✔
111

112
      if(!m_kex_key) {
615✔
113
         throw TLS_Exception(Alert::InternalError, "Application did not provide an EC key");
×
114
      }
115

116
      const uint16_t named_curve_id = m_shared_group.value().wire_code();
615✔
117
      m_params.push_back(3);  // named curve
615✔
118
      m_params.push_back(get_byte<0>(named_curve_id));
615✔
119
      m_params.push_back(get_byte<1>(named_curve_id));
615✔
120

121
      // Note: In contrast to public_value(), raw_public_key_bits() takes the
122
      // point format (compressed vs. uncompressed) into account that was set
123
      // in its construction within tls_generate_ephemeral_key().
124
      append_tls_length_value(m_params, m_kex_key->raw_public_key_bits(), 1);
1,845✔
125
   } else if(kex_algo != Kex_Algo::PSK) {
653✔
126
      throw Internal_Error("Server_Key_Exchange: Unknown kex type " + kex_method_to_string(kex_algo));
×
127
   }
128

129
   if(state.ciphersuite().signature_used()) {
659✔
130
      BOTAN_ASSERT(signing_key, "Signing key was set");
609✔
131

132
      const std::pair<std::string, Signature_Format> format =
609✔
133
         state.choose_sig_format(*signing_key, m_scheme, false, policy);
609✔
134

135
      std::vector<uint8_t> buf = state.client_hello()->random();
609✔
136

137
      buf += state.server_hello()->random();
609✔
138
      buf += params();
609✔
139

140
      m_signature = state.callbacks().tls_sign_message(*signing_key, rng, format.first, format.second, buf);
609✔
141
   }
609✔
142

143
   state.hash().update(io.send(*this));
1,977✔
144
}
659✔
145

146
/**
147
* Deserialize a Server Key Exchange message
148
*/
149
Server_Key_Exchange::Server_Key_Exchange(const std::vector<uint8_t>& buf,
1,006✔
150
                                         const Kex_Algo kex_algo,
151
                                         const Auth_Method auth_method,
152
                                         Protocol_Version version) {
1,006✔
153
   BOTAN_UNUSED(version);  // remove this
1,006✔
154
   TLS_Data_Reader reader("ServerKeyExchange", buf);
1,006✔
155

156
   /*
157
   * Here we are deserializing enough to find out what offset the
158
   * signature is at. All processing is done when the Client Key Exchange
159
   * is prepared.
160
   */
161

162
   if(kex_algo == Kex_Algo::PSK || kex_algo == Kex_Algo::ECDHE_PSK) {
1,006✔
163
      reader.get_string(2, 0, 65535);  // identity hint
114✔
164
   }
165

166
   if(kex_algo == Kex_Algo::DH) {
1,006✔
167
      // 3 bigints, DH p, g, Y
168

169
      for(size_t i = 0; i != 3; ++i) {
95✔
170
         reader.get_range<uint8_t>(2, 1, 65535);
143✔
171
      }
172
   } else if(kex_algo == Kex_Algo::ECDH || kex_algo == Kex_Algo::ECDHE_PSK) {
979✔
173
      reader.get_byte();                     // curve type
948✔
174
      reader.get_uint16_t();                 // curve id
947✔
175
      reader.get_range<uint8_t>(1, 1, 255);  // public key
1,891✔
176
   } else if(kex_algo != Kex_Algo::PSK) {
31✔
177
      throw Decoding_Error("Server_Key_Exchange: Unsupported kex type " + kex_method_to_string(kex_algo));
×
178
   }
179

180
   m_params.assign(buf.data(), buf.data() + reader.read_so_far());
996✔
181

182
   if(auth_method != Auth_Method::IMPLICIT) {
996✔
183
      m_scheme = Signature_Scheme(reader.get_uint16_t());
939✔
184
      m_signature = reader.get_range<uint8_t>(2, 0, 65535);
936✔
185
   }
186

187
   reader.assert_done();
988✔
188
}
1,022✔
189

190
/**
191
* Serialize a Server Key Exchange message
192
*/
193
std::vector<uint8_t> Server_Key_Exchange::serialize() const {
659✔
194
   std::vector<uint8_t> buf = params();
659✔
195

196
   if(!m_signature.empty()) {
659✔
197
      if(m_scheme.is_set()) {
609✔
198
         buf.push_back(get_byte<0>(m_scheme.wire_code()));
609✔
199
         buf.push_back(get_byte<1>(m_scheme.wire_code()));
609✔
200
      }
201

202
      append_tls_length_value(buf, m_signature, 2);
609✔
203
   }
204

205
   return buf;
659✔
206
}
×
207

208
/**
209
* Verify a Server Key Exchange message
210
*/
211
bool Server_Key_Exchange::verify(const Public_Key& server_key,
927✔
212
                                 const Handshake_State& state,
213
                                 const Policy& policy) const {
214
   policy.check_peer_key_acceptable(server_key);
927✔
215

216
   const std::pair<std::string, Signature_Format> format =
878✔
217
      state.parse_sig_format(server_key, m_scheme, state.client_hello()->signature_schemes(), false, policy);
878✔
218

219
   std::vector<uint8_t> buf = state.client_hello()->random();
869✔
220

221
   buf += state.server_hello()->random();
869✔
222
   buf += params();
869✔
223

224
   const bool signature_valid =
869✔
225
      state.callbacks().tls_verify_message(server_key, format.first, format.second, buf, m_signature);
869✔
226

227
#if defined(BOTAN_UNSAFE_FUZZER_MODE)
228
   BOTAN_UNUSED(signature_valid);
229
   return true;
230
#else
231
   return signature_valid;
869✔
232
#endif
233
}
869✔
234

235
const PK_Key_Agreement_Key& Server_Key_Exchange::server_kex_key() const {
606✔
236
   BOTAN_ASSERT_NONNULL(m_kex_key);
606✔
237
   return *m_kex_key;
606✔
238
}
239

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