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

randombit / botan / 21848380424

10 Feb 2026 01:47AM UTC coverage: 91.634% (+1.6%) from 90.069%
21848380424

push

github

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

Various changes to reduce header dependencies in TLS

104002 of 113497 relevant lines covered (91.63%)

11230277.53 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/credentials_manager.h>
12
#include <botan/dl_group.h>
13
#include <botan/pubkey.h>
14
#include <botan/tls_callbacks.h>
15
#include <botan/tls_extensions.h>
16
#include <botan/tls_policy.h>
17
#include <botan/internal/loadstor.h>
18
#include <botan/internal/target_info.h>
19
#include <botan/internal/tls_handshake_io.h>
20
#include <botan/internal/tls_handshake_state.h>
21
#include <botan/internal/tls_reader.h>
22

23
#include <botan/dh.h>
24

25
namespace Botan::TLS {
26

27
Server_Key_Exchange::~Server_Key_Exchange() = default;
3,246✔
28

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

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

44
      append_tls_length_value(m_params, identity_hint, 2);
94✔
45
   }
47✔
46

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

50
      m_shared_group = Group_Params::NONE;
4✔
51

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

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

61
      if(dh_groups.empty()) {
4✔
62
         m_shared_group = policy.default_dh_group();
×
63
      } else {
64
         m_shared_group = policy.choose_key_exchange_group(dh_groups, {});
8✔
65
      }
66

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

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

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

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

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

98
      m_shared_group = policy.choose_key_exchange_group(ec_groups, {});
1,224✔
99

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

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

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

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

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

132
   if(state.ciphersuite().signature_used()) {
653✔
133
      BOTAN_ASSERT(signing_key, "Signing key was set");
606✔
134

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

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

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

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

146
   state.hash().update(io.send(*this));
1,304✔
147
}
657✔
148

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

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

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

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

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

183
   m_params.assign(buf.data(), buf.data() + reader.read_so_far());
990✔
184

185
   if(auth_method != Auth_Method::IMPLICIT) {
990✔
186
      m_scheme = Signature_Scheme(reader.get_uint16_t());
936✔
187
      m_signature = reader.get_range<uint8_t>(2, 0, 65535);
933✔
188
   }
189

190
   reader.assert_done();
982✔
191
}
1,044✔
192

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

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

205
      append_tls_length_value(buf, m_signature, 2);
606✔
206
   }
207

208
   return buf;
653✔
209
}
×
210

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

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

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

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

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

230
#if defined(BOTAN_UNSAFE_FUZZER_MODE)
231
   BOTAN_UNUSED(signature_valid);
232
   return true;
233
#else
234
   return signature_valid;
1,732✔
235
#endif
236
}
866✔
237

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

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