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

randombit / botan / 6468847461

10 Oct 2023 11:49AM UTC coverage: 91.625% (-0.07%) from 91.697%
6468847461

push

github

web-flow
Fix FFDHE selection in TLS to conform to RFC 7919 (#3743)

79956 of 87264 relevant lines covered (91.63%)

8664891.56 hits per line

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

90.48
/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_extensions.h>
14
#include <botan/internal/loadstor.h>
15
#include <botan/internal/tls_handshake_io.h>
16
#include <botan/internal/tls_handshake_state.h>
17
#include <botan/internal/tls_reader.h>
18

19
#include <botan/dh.h>
20
#include <botan/dl_group.h>
21
#include <botan/ecdh.h>
22

23
#if defined(BOTAN_HAS_CURVE_25519)
24
   #include <botan/curve25519.h>
25
#endif
26

27
namespace Botan::TLS {
28

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

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

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

47
   if(kex_algo == Kex_Algo::DH) {
604✔
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) {
4✔
85
         throw TLS_Exception(Alert::InternalError, "Application did not provide a Diffie-Hellman key");
×
86
      }
87

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

94
      if(ec_groups.empty()) {
566✔
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,132✔
99

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

104
      std::vector<uint8_t> ecdh_public_val;
566✔
105

106
      if(m_shared_group.value() == Group_Params::X25519) {
566✔
107
         m_kex_key = state.callbacks().tls_generate_ephemeral_key(m_shared_group.value(), rng);
551✔
108
         if(!m_kex_key) {
551✔
109
            throw TLS_Exception(Alert::InternalError, "Application did not provide a X25519 key");
×
110
         }
111
         ecdh_public_val = m_kex_key->public_value();
1,102✔
112
      } else {
113
         m_kex_key = state.callbacks().tls_generate_ephemeral_key(m_shared_group.value(), rng);
15✔
114
         auto ecdh = dynamic_cast<ECDH_PrivateKey*>(m_kex_key.get());
15✔
115
         if(!ecdh) {
15✔
116
            throw TLS_Exception(Alert::InternalError, "Application did not provide a EC-Diffie-Hellman key");
×
117
         }
118

119
         // follow client's preference for point compression
120
         ecdh_public_val =
15✔
121
            ecdh->public_value(state.client_hello()->prefers_compressed_ec_points() ? EC_Point_Format::Compressed
45✔
122
                                                                                    : EC_Point_Format::Uncompressed);
15✔
123
      }
124

125
      const uint16_t named_curve_id = m_shared_group.value().wire_code();
566✔
126
      m_params.push_back(3);  // named curve
566✔
127
      m_params.push_back(get_byte<0>(named_curve_id));
566✔
128
      m_params.push_back(get_byte<1>(named_curve_id));
566✔
129

130
      append_tls_length_value(m_params, ecdh_public_val, 1);
1,132✔
131
   } else if(kex_algo != Kex_Algo::PSK) {
1,166✔
132
      throw Internal_Error("Server_Key_Exchange: Unknown kex type " + kex_method_to_string(kex_algo));
×
133
   }
134

135
   if(state.ciphersuite().signature_used()) {
604✔
136
      BOTAN_ASSERT(signing_key, "Signing key was set");
559✔
137

138
      std::pair<std::string, Signature_Format> format = state.choose_sig_format(*signing_key, m_scheme, false, policy);
559✔
139

140
      std::vector<uint8_t> buf = state.client_hello()->random();
559✔
141

142
      buf += state.server_hello()->random();
559✔
143
      buf += params();
559✔
144

145
      m_signature = state.callbacks().tls_sign_message(*signing_key, rng, format.first, format.second, buf);
559✔
146
   }
1,010✔
147

148
   state.hash().update(io.send(*this));
1,810✔
149
}
606✔
150

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

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

167
   if(kex_algo == Kex_Algo::PSK || kex_algo == Kex_Algo::ECDHE_PSK) {
751✔
168
      reader.get_string(2, 0, 65535);  // identity hint
104✔
169
   }
170

171
   if(kex_algo == Kex_Algo::DH) {
751✔
172
      // 3 bigints, DH p, g, Y
173

174
      for(size_t i = 0; i != 3; ++i) {
16✔
175
         reader.get_range<uint8_t>(2, 1, 65535);
24✔
176
      }
177
   } else if(kex_algo == Kex_Algo::ECDH || kex_algo == Kex_Algo::ECDHE_PSK) {
747✔
178
      reader.get_byte();                     // curve type
720✔
179
      reader.get_uint16_t();                 // curve id
720✔
180
      reader.get_range<uint8_t>(1, 1, 255);  // public key
1,440✔
181
   } else if(kex_algo != Kex_Algo::PSK) {
27✔
182
      throw Decoding_Error("Server_Key_Exchange: Unsupported kex type " + kex_method_to_string(kex_algo));
×
183
   }
184

185
   m_params.assign(buf.data(), buf.data() + reader.read_so_far());
751✔
186

187
   if(auth_method != Auth_Method::IMPLICIT) {
751✔
188
      m_scheme = Signature_Scheme(reader.get_uint16_t());
699✔
189
      m_signature = reader.get_range<uint8_t>(2, 0, 65535);
699✔
190
   }
191

192
   reader.assert_done();
751✔
193
}
755✔
194

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

201
   if(!m_signature.empty()) {
604✔
202
      if(m_scheme.is_set()) {
559✔
203
         buf.push_back(get_byte<0>(m_scheme.wire_code()));
559✔
204
         buf.push_back(get_byte<1>(m_scheme.wire_code()));
559✔
205
      }
206

207
      append_tls_length_value(buf, m_signature, 2);
559✔
208
   }
209

210
   return buf;
604✔
211
}
×
212

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

221
   std::pair<std::string, Signature_Format> format =
697✔
222
      state.parse_sig_format(server_key, m_scheme, state.client_hello()->signature_schemes(), false, policy);
697✔
223

224
   std::vector<uint8_t> buf = state.client_hello()->random();
695✔
225

226
   buf += state.server_hello()->random();
695✔
227
   buf += params();
695✔
228

229
   const bool signature_valid =
695✔
230
      state.callbacks().tls_verify_message(server_key, format.first, format.second, buf, m_signature);
695✔
231

232
#if defined(BOTAN_UNSAFE_FUZZER_MODE)
233
   BOTAN_UNUSED(signature_valid);
234
   return true;
235
#else
236
   return signature_valid;
695✔
237
#endif
238
}
1,260✔
239

240
const PK_Key_Agreement_Key& Server_Key_Exchange::server_kex_key() const {
555✔
241
   BOTAN_ASSERT_NONNULL(m_kex_key);
555✔
242
   return *m_kex_key;
555✔
243
}
244

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