/src/botan/src/lib/tls/tls12/msg_server_kex.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | #include <botan/tls_extensions.h> |
11 | | #include <botan/internal/tls_reader.h> |
12 | | #include <botan/internal/tls_handshake_io.h> |
13 | | #include <botan/internal/tls_handshake_state.h> |
14 | | #include <botan/credentials_manager.h> |
15 | | #include <botan/internal/loadstor.h> |
16 | | #include <botan/pubkey.h> |
17 | | |
18 | | #include <botan/dh.h> |
19 | | #include <botan/ecdh.h> |
20 | | |
21 | | #if defined(BOTAN_HAS_CURVE_25519) |
22 | | #include <botan/curve25519.h> |
23 | | #endif |
24 | | |
25 | | #if defined(BOTAN_HAS_CECPQ1) |
26 | | #include <botan/cecpq1.h> |
27 | | #endif |
28 | | |
29 | | namespace Botan::TLS { |
30 | | |
31 | | /** |
32 | | * Create a new Server Key Exchange message |
33 | | */ |
34 | | Server_Key_Exchange::Server_Key_Exchange(Handshake_IO& io, |
35 | | Handshake_State& state, |
36 | | const Policy& policy, |
37 | | Credentials_Manager& creds, |
38 | | RandomNumberGenerator& rng, |
39 | | const Private_Key* signing_key) |
40 | 19.6k | { |
41 | 19.6k | const std::string hostname = state.client_hello()->sni_hostname(); |
42 | 19.6k | const Kex_Algo kex_algo = state.ciphersuite().kex_method(); |
43 | | |
44 | 19.6k | if(kex_algo == Kex_Algo::PSK || kex_algo == Kex_Algo::ECDHE_PSK) |
45 | 19.6k | { |
46 | 19.6k | std::string identity_hint = |
47 | 19.6k | creds.psk_identity_hint("tls-server", hostname); |
48 | | |
49 | 19.6k | append_tls_length_value(m_params, identity_hint, 2); |
50 | 19.6k | } |
51 | | |
52 | 19.6k | if(kex_algo == Kex_Algo::DH) |
53 | 0 | { |
54 | 0 | const std::vector<Group_Params> dh_groups = state.client_hello()->supported_dh_groups(); |
55 | |
|
56 | 0 | Group_Params shared_group = Group_Params::NONE; |
57 | | |
58 | | /* |
59 | | If the client does not send any DH groups in the supported groups |
60 | | extension, but does offer DH ciphersuites, we select a group arbitrarily |
61 | | */ |
62 | |
|
63 | 0 | if(dh_groups.empty()) |
64 | 0 | { |
65 | 0 | shared_group = policy.default_dh_group(); |
66 | 0 | } |
67 | 0 | else |
68 | 0 | { |
69 | 0 | shared_group = policy.choose_key_exchange_group(dh_groups); |
70 | 0 | } |
71 | |
|
72 | 0 | if(shared_group == Group_Params::NONE) |
73 | 0 | throw TLS_Exception(Alert::HANDSHAKE_FAILURE, |
74 | 0 | "Could not agree on a DH group with the client"); |
75 | | |
76 | 0 | BOTAN_ASSERT(group_param_is_dh(shared_group), "DH groups for the DH ciphersuites god"); |
77 | |
|
78 | 0 | const std::string group_name = state.callbacks().tls_decode_group_param(shared_group); |
79 | 0 | auto dh = std::make_unique<DH_PrivateKey>(rng, DL_Group(group_name)); |
80 | |
|
81 | 0 | append_tls_length_value(m_params, BigInt::encode(dh->get_domain().get_p()), 2); |
82 | 0 | append_tls_length_value(m_params, BigInt::encode(dh->get_domain().get_g()), 2); |
83 | 0 | append_tls_length_value(m_params, dh->public_value(), 2); |
84 | 0 | m_kex_key.reset(dh.release()); |
85 | 0 | } |
86 | 19.6k | else if(kex_algo == Kex_Algo::ECDH || kex_algo == Kex_Algo::ECDHE_PSK) |
87 | 19.0k | { |
88 | 19.0k | const std::vector<Group_Params> ec_groups = state.client_hello()->supported_ecc_curves(); |
89 | | |
90 | 19.0k | if(ec_groups.empty()) |
91 | 0 | throw Internal_Error("Client sent no ECC extension but we negotiated ECDH"); |
92 | | |
93 | 19.0k | Group_Params shared_group = policy.choose_key_exchange_group(ec_groups); |
94 | | |
95 | 19.0k | if(shared_group == Group_Params::NONE) |
96 | 0 | throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "No shared ECC group with client"); |
97 | | |
98 | 19.0k | std::vector<uint8_t> ecdh_public_val; |
99 | | |
100 | 19.0k | if(shared_group == Group_Params::X25519) |
101 | 804 | { |
102 | 804 | #if defined(BOTAN_HAS_CURVE_25519) |
103 | 804 | auto x25519 = std::make_unique<X25519_PrivateKey>(rng); |
104 | 804 | ecdh_public_val = x25519->public_value(); |
105 | 804 | m_kex_key.reset(x25519.release()); |
106 | | #else |
107 | | throw Internal_Error("Negotiated X25519 somehow, but it is disabled"); |
108 | | #endif |
109 | 804 | } |
110 | 18.2k | else |
111 | 18.2k | { |
112 | 18.2k | Group_Params curve = policy.choose_key_exchange_group(ec_groups); |
113 | | |
114 | 18.2k | const std::string curve_name = state.callbacks().tls_decode_group_param(curve); |
115 | | |
116 | 18.2k | EC_Group ec_group(curve_name); |
117 | 18.2k | auto ecdh = std::make_unique<ECDH_PrivateKey>(rng, ec_group); |
118 | | |
119 | | // follow client's preference for point compression |
120 | 18.2k | ecdh_public_val = ecdh->public_value( |
121 | 18.2k | state.client_hello()->prefers_compressed_ec_points() ? |
122 | 15.2k | PointGFp::COMPRESSED : PointGFp::UNCOMPRESSED); |
123 | | |
124 | 18.2k | m_kex_key.reset(ecdh.release()); |
125 | 18.2k | } |
126 | | |
127 | 19.0k | const uint16_t named_curve_id = static_cast<uint16_t>(shared_group); |
128 | 19.0k | m_params.push_back(3); // named curve |
129 | 19.0k | m_params.push_back(get_byte<0>(named_curve_id)); |
130 | 19.0k | m_params.push_back(get_byte<1>(named_curve_id)); |
131 | | |
132 | 19.0k | append_tls_length_value(m_params, ecdh_public_val, 1); |
133 | 19.0k | } |
134 | 607 | #if defined(BOTAN_HAS_CECPQ1) |
135 | 607 | else if(kex_algo == Kex_Algo::CECPQ1) |
136 | 0 | { |
137 | 0 | std::vector<uint8_t> cecpq1_offer(CECPQ1_OFFER_BYTES); |
138 | 0 | m_cecpq1_key.reset(new CECPQ1_key); |
139 | 0 | CECPQ1_offer(cecpq1_offer.data(), m_cecpq1_key.get(), rng); |
140 | 0 | append_tls_length_value(m_params, cecpq1_offer, 2); |
141 | 0 | } |
142 | 607 | #endif |
143 | 607 | else if(kex_algo != Kex_Algo::PSK) |
144 | 0 | { |
145 | 0 | throw Internal_Error("Server_Key_Exchange: Unknown kex type " + |
146 | 0 | kex_method_to_string(kex_algo)); |
147 | 0 | } |
148 | | |
149 | 19.6k | if(state.ciphersuite().signature_used()) |
150 | 0 | { |
151 | 0 | BOTAN_ASSERT(signing_key, "Signing key was set"); |
152 | |
|
153 | 0 | std::pair<std::string, Signature_Format> format = |
154 | 0 | state.choose_sig_format(*signing_key, m_scheme, false, policy); |
155 | |
|
156 | 0 | std::vector<uint8_t> buf = state.client_hello()->random(); |
157 | |
|
158 | 0 | buf += state.server_hello()->random(); |
159 | 0 | buf += params(); |
160 | |
|
161 | 0 | m_signature = |
162 | 0 | state.callbacks().tls_sign_message(*signing_key, rng, |
163 | 0 | format.first, format.second, buf); |
164 | 0 | } |
165 | | |
166 | 19.6k | state.hash().update(io.send(*this)); |
167 | 19.6k | } |
168 | | |
169 | | /** |
170 | | * Deserialize a Server Key Exchange message |
171 | | */ |
172 | | Server_Key_Exchange::Server_Key_Exchange(const std::vector<uint8_t>& buf, |
173 | | const Kex_Algo kex_algo, |
174 | | const Auth_Method auth_method, |
175 | | Protocol_Version version) |
176 | 0 | { |
177 | 0 | BOTAN_UNUSED(version); // remove this |
178 | 0 | TLS_Data_Reader reader("ServerKeyExchange", buf); |
179 | | |
180 | | /* |
181 | | * Here we are deserializing enough to find out what offset the |
182 | | * signature is at. All processing is done when the Client Key Exchange |
183 | | * is prepared. |
184 | | */ |
185 | |
|
186 | 0 | if(kex_algo == Kex_Algo::PSK || kex_algo == Kex_Algo::ECDHE_PSK) |
187 | 0 | { |
188 | 0 | reader.get_string(2, 0, 65535); // identity hint |
189 | 0 | } |
190 | |
|
191 | 0 | if(kex_algo == Kex_Algo::DH) |
192 | 0 | { |
193 | | // 3 bigints, DH p, g, Y |
194 | |
|
195 | 0 | for(size_t i = 0; i != 3; ++i) |
196 | 0 | { |
197 | 0 | reader.get_range<uint8_t>(2, 1, 65535); |
198 | 0 | } |
199 | 0 | } |
200 | 0 | else if(kex_algo == Kex_Algo::ECDH || kex_algo == Kex_Algo::ECDHE_PSK) |
201 | 0 | { |
202 | 0 | reader.get_byte(); // curve type |
203 | 0 | reader.get_uint16_t(); // curve id |
204 | 0 | reader.get_range<uint8_t>(1, 1, 255); // public key |
205 | 0 | } |
206 | 0 | else if(kex_algo == Kex_Algo::CECPQ1) |
207 | 0 | { |
208 | | // u16 blob |
209 | 0 | reader.get_range<uint8_t>(2, 1, 65535); |
210 | 0 | } |
211 | 0 | else if(kex_algo != Kex_Algo::PSK) |
212 | 0 | throw Decoding_Error("Server_Key_Exchange: Unsupported kex type " + |
213 | 0 | kex_method_to_string(kex_algo)); |
214 | | |
215 | 0 | m_params.assign(buf.data(), buf.data() + reader.read_so_far()); |
216 | |
|
217 | 0 | if(auth_method != Auth_Method::IMPLICIT) |
218 | 0 | { |
219 | 0 | m_scheme = static_cast<Signature_Scheme>(reader.get_uint16_t()); |
220 | 0 | m_signature = reader.get_range<uint8_t>(2, 0, 65535); |
221 | 0 | } |
222 | |
|
223 | 0 | reader.assert_done(); |
224 | 0 | } |
225 | | |
226 | | /** |
227 | | * Serialize a Server Key Exchange message |
228 | | */ |
229 | | std::vector<uint8_t> Server_Key_Exchange::serialize() const |
230 | 19.6k | { |
231 | 19.6k | std::vector<uint8_t> buf = params(); |
232 | | |
233 | 19.6k | if(!m_signature.empty()) |
234 | 0 | { |
235 | 0 | if(m_scheme != Signature_Scheme::NONE) |
236 | 0 | { |
237 | 0 | const uint16_t scheme_code = static_cast<uint16_t>(m_scheme); |
238 | 0 | buf.push_back(get_byte<0>(scheme_code)); |
239 | 0 | buf.push_back(get_byte<1>(scheme_code)); |
240 | 0 | } |
241 | |
|
242 | 0 | append_tls_length_value(buf, m_signature, 2); |
243 | 0 | } |
244 | | |
245 | 19.6k | return buf; |
246 | 19.6k | } |
247 | | |
248 | | /** |
249 | | * Verify a Server Key Exchange message |
250 | | */ |
251 | | bool Server_Key_Exchange::verify(const Public_Key& server_key, |
252 | | const Handshake_State& state, |
253 | | const Policy& policy) const |
254 | 0 | { |
255 | 0 | policy.check_peer_key_acceptable(server_key); |
256 | |
|
257 | 0 | std::pair<std::string, Signature_Format> format = |
258 | 0 | state.parse_sig_format(server_key, m_scheme, state.client_hello()->signature_schemes(), false, policy); |
259 | |
|
260 | 0 | std::vector<uint8_t> buf = state.client_hello()->random(); |
261 | |
|
262 | 0 | buf += state.server_hello()->random(); |
263 | 0 | buf += params(); |
264 | |
|
265 | 0 | const bool signature_valid = |
266 | 0 | state.callbacks().tls_verify_message(server_key, format.first, format.second, |
267 | 0 | buf, m_signature); |
268 | |
|
269 | 0 | #if defined(BOTAN_UNSAFE_FUZZER_MODE) |
270 | 0 | BOTAN_UNUSED(signature_valid); |
271 | 0 | return true; |
272 | | #else |
273 | | return signature_valid; |
274 | | #endif |
275 | 0 | } |
276 | | |
277 | | const Private_Key& Server_Key_Exchange::server_kex_key() const |
278 | 13.6k | { |
279 | 13.6k | BOTAN_ASSERT_NONNULL(m_kex_key); |
280 | 13.6k | return *m_kex_key; |
281 | 13.6k | } |
282 | | |
283 | | } |