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

randombit / botan / 22093983912

17 Feb 2026 10:00AM UTC coverage: 90.027% (-0.001%) from 90.028%
22093983912

push

github

web-flow
Merge pull request #5347 from randombit/jack/tls-header-patrol-2

Changes to reduce dependencies in TLS sources/headers

102345 of 113683 relevant lines covered (90.03%)

11397934.0 hits per line

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

91.35
/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_12.h>
10

11
#include <botan/bigint.h>
12
#include <botan/credentials_manager.h>
13
#include <botan/dl_group.h>
14
#include <botan/tls_callbacks.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
Server_Key_Exchange::~Server_Key_Exchange() = default;
6,423✔
27

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

40
   if(kex_algo == Kex_Algo::PSK || kex_algo == Kex_Algo::ECDHE_PSK) {
657✔
41
      const std::string identity_hint = creds.psk_identity_hint("tls-server", hostname);
49✔
42

43
      append_tls_length_value(m_params, identity_hint, 2);
98✔
44
   }
49✔
45

46
   if(kex_algo == Kex_Algo::DH) {
657✔
47
      const std::vector<Group_Params> dh_groups = state.client_hello()->supported_dh_groups();
5✔
48

49
      m_shared_group = Group_Params::NONE;
5✔
50

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

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

60
      if(dh_groups.empty()) {
5✔
61
         m_shared_group = policy.default_dh_group();
×
62
      } else {
63
         m_shared_group = policy.choose_key_exchange_group(dh_groups, {});
10✔
64
      }
65

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

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

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

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

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

97
      m_shared_group = policy.choose_key_exchange_group(ec_groups, {});
1,230✔
98

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

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

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

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

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

131
   if(state.ciphersuite().signature_used()) {
657✔
132
      BOTAN_ASSERT(signing_key, "Signing key was set");
608✔
133

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

137
      std::vector<uint8_t> buf = state.client_hello()->random();
608✔
138

139
      buf += state.server_hello()->random();
608✔
140
      buf += params();
608✔
141

142
      m_signature = state.callbacks().tls_sign_message(*signing_key, rng, format.first, format.second, buf);
608✔
143
   }
608✔
144

145
   state.hash().update(io.send(*this));
1,969✔
146
}
659✔
147

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

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

164
   if(kex_algo == Kex_Algo::PSK || kex_algo == Kex_Algo::ECDHE_PSK) {
1,004✔
165
      reader.get_string(2, 0, 65535);  // identity hint
112✔
166
   }
167

168
   if(kex_algo == Kex_Algo::DH) {
1,004✔
169
      // 3 bigints, DH p, g, Y
170

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

182
   m_params.assign(buf.data(), buf.data() + reader.read_so_far());
994✔
183

184
   if(auth_method != Auth_Method::IMPLICIT) {
994✔
185
      m_scheme = Signature_Scheme(reader.get_uint16_t());
938✔
186
      m_signature = reader.get_range<uint8_t>(2, 0, 65535);
935✔
187
   }
188

189
   reader.assert_done();
986✔
190
}
1,020✔
191

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

198
   if(!m_signature.empty()) {
657✔
199
      if(m_scheme.is_set()) {
608✔
200
         buf.push_back(get_byte<0>(m_scheme.wire_code()));
608✔
201
         buf.push_back(get_byte<1>(m_scheme.wire_code()));
608✔
202
      }
203

204
      append_tls_length_value(buf, m_signature, 2);
608✔
205
   }
206

207
   return buf;
657✔
208
}
×
209

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

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

221
   std::vector<uint8_t> buf = state.client_hello()->random();
868✔
222

223
   buf += state.server_hello()->random();
868✔
224
   buf += params();
868✔
225

226
   const bool signature_valid =
868✔
227
      state.callbacks().tls_verify_message(server_key, format.first, format.second, buf, m_signature);
868✔
228

229
#if defined(BOTAN_UNSAFE_FUZZER_MODE)
230
   BOTAN_UNUSED(signature_valid);
231
   return true;
232
#else
233
   return signature_valid;
868✔
234
#endif
235
}
868✔
236

237
const PK_Key_Agreement_Key& Server_Key_Exchange::server_kex_key() const {
605✔
238
   BOTAN_ASSERT_NONNULL(m_kex_key);
605✔
239
   return *m_kex_key;
605✔
240
}
241

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