Botan 3.4.0
Crypto and TLS for C&
tls_extensions_key_share.cpp
Go to the documentation of this file.
1/*
2* TLS Extension Key Share
3* (C) 2011,2012,2015,2016 Jack Lloyd
4* 2016 Juraj Somorovsky
5* 2021 Elektrobit Automotive GmbH
6* 2022 Hannes Rantzsch, René Meusel, neXenio GmbH
7*
8* Botan is released under the Simplified BSD License (see license.txt)
9*/
10
11#include <botan/tls_extensions.h>
12
13#include <botan/rng.h>
14#include <botan/tls_callbacks.h>
15#include <botan/tls_exceptn.h>
16#include <botan/tls_policy.h>
17#include <botan/internal/ct_utils.h>
18#include <botan/internal/stl_util.h>
19#include <botan/internal/tls_reader.h>
20
21#include <functional>
22#include <iterator>
23#include <utility>
24
25#if defined(BOTAN_HAS_CURVE_25519)
26 #include <botan/curve25519.h>
27#endif
28
29#if defined(BOTAN_HAS_X448)
30 #include <botan/x448.h>
31#endif
32
33#include <botan/dh.h>
34#include <botan/dl_group.h>
35#include <botan/ecdh.h>
36
37namespace Botan::TLS {
38
39namespace {
40
41class Key_Share_Entry {
42 public:
43 Key_Share_Entry(TLS_Data_Reader& reader) {
44 // TODO check that the group actually exists before casting...
45 m_group = static_cast<Named_Group>(reader.get_uint16_t());
46 m_key_exchange = reader.get_tls_length_value(2);
47 }
48
49 // Create an empty Key_Share_Entry with the selected group
50 // but don't pre-generate a keypair, yet.
51 Key_Share_Entry(const TLS::Group_Params group) : m_group(group) {}
52
53 Key_Share_Entry(const TLS::Group_Params group, Callbacks& cb, RandomNumberGenerator& rng) :
54 m_group(group), m_private_key(cb.tls_kem_generate_key(group, rng)) {
55 if(!m_private_key) {
56 throw TLS_Exception(Alert::InternalError, "Application did not provide a suitable ephemeral key pair");
57 }
58
59 if(group.is_kem()) {
60 m_key_exchange = m_private_key->public_key_bits();
61 } else if(group.is_ecdh_named_curve()) {
62 auto pkey = dynamic_cast<ECDH_PublicKey*>(m_private_key.get());
63 if(!pkey) {
64 throw TLS_Exception(Alert::InternalError, "Application did not provide a ECDH_PublicKey");
65 }
66
67 // RFC 8446 Ch. 4.2.8.2
68 //
69 // Note: Versions of TLS prior to 1.3 permitted point format
70 // negotiation; TLS 1.3 removes this feature in favor of a single point
71 // format for each curve.
72 //
73 // Hence, we neither need to take Policy::use_ecc_point_compression() nor
74 // ClientHello::prefers_compressed_ec_points() into account here.
75 m_key_exchange = pkey->public_value(EC_Point_Format::Uncompressed);
76 } else {
77 auto pkey = dynamic_cast<PK_Key_Agreement_Key*>(m_private_key.get());
78 if(!pkey) {
79 throw TLS_Exception(Alert::InternalError, "Application did not provide a key-agreement key");
80 }
81
82 m_key_exchange = pkey->public_value();
83 }
84 }
85
86 bool empty() const { return (m_group == Group_Params::NONE) && m_key_exchange.empty(); }
87
88 std::vector<uint8_t> serialize() const {
89 std::vector<uint8_t> result;
90 result.reserve(m_key_exchange.size() + 2);
91
92 const uint16_t named_curve_id = m_group.wire_code();
93 result.push_back(get_byte<0>(named_curve_id));
94 result.push_back(get_byte<1>(named_curve_id));
95 append_tls_length_value(result, m_key_exchange, 2);
96
97 return result;
98 }
99
100 Named_Group group() const { return m_group; }
101
102 secure_vector<uint8_t> encapsulate(const Key_Share_Entry& client_share,
103 const Policy& policy,
104 Callbacks& cb,
105 RandomNumberGenerator& rng) {
106 auto [encapsulated_shared_key, shared_key] =
107 KEM_Encapsulation::destructure(cb.tls_kem_encapsulate(m_group, client_share.m_key_exchange, rng, policy));
108 m_key_exchange = std::move(encapsulated_shared_key);
109 return std::move(shared_key);
110 }
111
112 /**
113 * Perform KEM decapsulation with another Key_Share_Entry's public key
114 *
115 * The caller must ensure that both this and `received` have the same group.
116 * This method must not be called on Key_Share_Entries without a private key.
117 */
118 secure_vector<uint8_t> decapsulate(const Key_Share_Entry& received,
119 const Policy& policy,
120 Callbacks& cb,
121 RandomNumberGenerator& rng) {
122 BOTAN_ASSERT_NOMSG(m_group == received.m_group);
123 BOTAN_STATE_CHECK(m_private_key != nullptr);
124
125 auto shared_secret = cb.tls_kem_decapsulate(m_group, *m_private_key, received.m_key_exchange, rng, policy);
126 m_private_key.reset();
127
128 // RFC 8422 - 5.11.
129 // With X25519 and X448, a receiving party MUST check whether the
130 // computed premaster secret is the all-zero value and abort the
131 // handshake if so, as described in Section 6 of [RFC7748].
132 if((m_group == Named_Group::X25519 || m_group == Named_Group::X448) &&
133 CT::all_zeros(shared_secret.data(), shared_secret.size()).as_bool()) {
134 throw TLS_Exception(Alert::DecryptError, "Bad X25519 or X448 key exchange");
135 }
136
137 return shared_secret;
138 }
139
140 private:
141 Named_Group m_group;
142 std::vector<uint8_t> m_key_exchange;
143 std::unique_ptr<Private_Key> m_private_key;
144};
145
146class Key_Share_ClientHello;
147
148class Key_Share_ServerHello {
149 public:
150 Key_Share_ServerHello(TLS_Data_Reader& reader, uint16_t) : m_server_share(reader) {}
151
152 Key_Share_ServerHello(Named_Group group,
153 const Key_Share_ClientHello& client_keyshare,
154 const Policy& policy,
155 Callbacks& cb,
156 RandomNumberGenerator& rng);
157
158 ~Key_Share_ServerHello() = default;
159
160 Key_Share_ServerHello(const Key_Share_ServerHello&) = delete;
161 Key_Share_ServerHello& operator=(const Key_Share_ServerHello&) = delete;
162
163 Key_Share_ServerHello(Key_Share_ServerHello&&) = default;
164 Key_Share_ServerHello& operator=(Key_Share_ServerHello&&) = default;
165
166 std::vector<uint8_t> serialize() const { return m_server_share.serialize(); }
167
168 bool empty() const { return m_server_share.empty(); }
169
170 Key_Share_Entry& get_singleton_entry() { return m_server_share; }
171
172 const Key_Share_Entry& get_singleton_entry() const { return m_server_share; }
173
174 std::vector<Named_Group> offered_groups() const { return {selected_group()}; }
175
176 Named_Group selected_group() const { return m_server_share.group(); }
177
178 secure_vector<uint8_t> take_shared_secret() {
179 BOTAN_STATE_CHECK(!m_shared_secret.empty());
180 return std::exchange(m_shared_secret, {});
181 }
182
183 private:
184 Key_Share_Entry m_server_share;
185 secure_vector<uint8_t> m_shared_secret;
186};
187
188class Key_Share_ClientHello {
189 public:
190 Key_Share_ClientHello(TLS_Data_Reader& reader, uint16_t /* extension_size */) {
191 // This construction is a crutch to make working with the incoming
192 // TLS_Data_Reader bearable. Currently, this reader spans the entire
193 // Client_Hello message. Hence, if offset or length fields are skewed
194 // or maliciously fabricated, it is possible to read further than the
195 // bounds of the current extension.
196 // Note that this aplies to many locations in the code base.
197 //
198 // TODO: Overhaul the TLS_Data_Reader to allow for cheap "sub-readers"
199 // that enforce read bounds of sub-structures while parsing.
200 const auto client_key_share_length = reader.get_uint16_t();
201 const auto read_bytes_so_far_begin = reader.read_so_far();
202 auto remaining = [&] {
203 const auto read_so_far = reader.read_so_far() - read_bytes_so_far_begin;
204 BOTAN_STATE_CHECK(read_so_far <= client_key_share_length);
205 return client_key_share_length - read_so_far;
206 };
207
208 while(reader.has_remaining() && remaining() > 0) {
209 if(remaining() < 4) {
210 throw TLS_Exception(Alert::DecodeError, "Not enough data to read another KeyShareEntry");
211 }
212
213 Key_Share_Entry new_entry(reader);
214
215 // RFC 8446 4.2.8
216 // Clients MUST NOT offer multiple KeyShareEntry values for the same
217 // group. [...]
218 // Servers MAY check for violations of these rules and abort the
219 // handshake with an "illegal_parameter" alert if one is violated.
220 if(std::find_if(m_client_shares.begin(), m_client_shares.end(), [&](const auto& entry) {
221 return entry.group() == new_entry.group();
222 }) != m_client_shares.end()) {
223 throw TLS_Exception(Alert::IllegalParameter, "Received multiple key share entries for the same group");
224 }
225
226 m_client_shares.emplace_back(std::move(new_entry));
227 }
228
229 if((reader.read_so_far() - read_bytes_so_far_begin) != client_key_share_length) {
230 throw Decoding_Error("Read bytes are not equal client KeyShare length");
231 }
232 }
233
234 Key_Share_ClientHello(const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng) {
235 const auto supported = policy.key_exchange_groups();
236 const auto offers = policy.key_exchange_groups_to_offer();
237
238 // RFC 8446 P. 48
239 //
240 // This vector MAY be empty if the client is requesting a
241 // HelloRetryRequest. Each KeyShareEntry value MUST correspond to a
242 // group offered in the "supported_groups" extension and MUST appear in
243 // the same order. However, the values MAY be a non-contiguous subset
244 // of the "supported_groups" extension and MAY omit the most preferred
245 // groups.
246 //
247 // ... hence, we're going through the supported groups and find those that
248 // should be used to offer a key exchange. This will satisfy above spec.
249 for(const auto group : supported) {
250 if(std::find(offers.begin(), offers.end(), group) == offers.end()) {
251 continue;
252 }
253 m_client_shares.emplace_back(group, cb, rng);
254 }
255 }
256
257 ~Key_Share_ClientHello() = default;
258
259 Key_Share_ClientHello(const Key_Share_ClientHello&) = delete;
260 Key_Share_ClientHello& operator=(const Key_Share_ClientHello&) = delete;
261
262 Key_Share_ClientHello(Key_Share_ClientHello&&) = default;
263 Key_Share_ClientHello& operator=(Key_Share_ClientHello&&) = default;
264
265 void retry_offer(const TLS::Group_Params to_offer, Callbacks& cb, RandomNumberGenerator& rng) {
266 // RFC 8446 4.2.8
267 // The selected_group field [MUST] not correspond to a group which was provided
268 // in the "key_share" extension in the original ClientHello.
269 if(std::find_if(m_client_shares.cbegin(), m_client_shares.cend(), [&](const auto& kse) {
270 return kse.group() == to_offer;
271 }) != m_client_shares.cend()) {
272 throw TLS_Exception(Alert::IllegalParameter, "group was already offered");
273 }
274
275 m_client_shares.clear();
276 m_client_shares.emplace_back(to_offer, cb, rng);
277 }
278
279 std::vector<Named_Group> offered_groups() const {
280 std::vector<Named_Group> offered_groups;
281 offered_groups.reserve(m_client_shares.size());
282 for(const auto& share : m_client_shares) {
283 offered_groups.push_back(share.group());
284 }
285 return offered_groups;
286 }
287
288 Named_Group selected_group() const { throw Invalid_Argument("Client Hello Key Share does not select a group"); }
289
290 std::vector<uint8_t> serialize() const {
291 std::vector<uint8_t> shares;
292 for(const auto& share : m_client_shares) {
293 const auto serialized_share = share.serialize();
294 shares.insert(shares.end(), serialized_share.cbegin(), serialized_share.cend());
295 }
296
297 std::vector<uint8_t> result;
298 append_tls_length_value(result, shares, 2);
299 return result;
300 }
301
302 bool empty() const {
303 // RFC 8446 4.2.8
304 // Clients MAY send an empty client_shares vector in order to request
305 // group selection from the server, at the cost of an additional round
306 // trip [...].
307 return false;
308 }
309
310 secure_vector<uint8_t> encapsulate(Key_Share_ServerHello& server_share,
311 const Policy& policy,
312 Callbacks& cb,
313 RandomNumberGenerator& rng) const {
314 auto& server_selected = server_share.get_singleton_entry();
315
316 // find the client offer that matches the server offer
317 auto match = std::find_if(m_client_shares.begin(), m_client_shares.end(), [&](const auto& offered) {
318 return offered.group() == server_selected.group();
319 });
320
321 // We validated that the selected group was indeed offered by the
322 // client before even constructing the Server Hello that contains the
323 // Key_Share_ServerHello extension.
324 BOTAN_STATE_CHECK(match != m_client_shares.end());
325
326 return server_selected.encapsulate(*match, policy, cb, rng);
327 }
328
329 secure_vector<uint8_t> decapsulate(const Key_Share_ServerHello& server_share,
330 const Policy& policy,
331 Callbacks& cb,
332 RandomNumberGenerator& rng) {
333 const auto& server_selected = server_share.get_singleton_entry();
334
335 // find the client offer that matches the server offer
336 auto match = std::find_if(m_client_shares.begin(), m_client_shares.end(), [&](const auto& offered) {
337 return offered.group() == server_selected.group();
338 });
339
340 // RFC 8446 4.2.8:
341 // [The KeyShareEntry in the ServerHello] MUST be in the same group
342 // as the KeyShareEntry value offered by the client that the server
343 // has selected for the negotiated key exchange.
344 if(match == m_client_shares.end()) {
345 throw TLS_Exception(Alert::IllegalParameter, "Server selected a key exchange group we didn't offer.");
346 }
347
348 return match->decapsulate(server_selected, policy, cb, rng);
349 }
350
351 private:
352 std::vector<Key_Share_Entry> m_client_shares;
353};
354
355Key_Share_ServerHello::Key_Share_ServerHello(Named_Group group,
356 const Key_Share_ClientHello& client_keyshare,
357 const Policy& policy,
358 Callbacks& cb,
359 RandomNumberGenerator& rng) :
360 m_server_share(group) {
361 m_shared_secret = client_keyshare.encapsulate(*this, policy, cb, rng);
362}
363
364class Key_Share_HelloRetryRequest {
365 public:
366 Key_Share_HelloRetryRequest(TLS_Data_Reader& reader, uint16_t extension_size) {
367 constexpr auto sizeof_uint16_t = sizeof(uint16_t);
368
369 if(extension_size != sizeof_uint16_t) {
370 throw Decoding_Error("Size of KeyShare extension in HelloRetryRequest must be " +
371 std::to_string(sizeof_uint16_t) + " bytes");
372 }
373
374 m_selected_group = static_cast<Named_Group>(reader.get_uint16_t());
375 }
376
377 Key_Share_HelloRetryRequest(Named_Group selected_group) : m_selected_group(selected_group) {}
378
379 ~Key_Share_HelloRetryRequest() = default;
380
381 Key_Share_HelloRetryRequest(const Key_Share_HelloRetryRequest&) = delete;
382 Key_Share_HelloRetryRequest& operator=(const Key_Share_HelloRetryRequest&) = delete;
383
384 Key_Share_HelloRetryRequest(Key_Share_HelloRetryRequest&&) = default;
385 Key_Share_HelloRetryRequest& operator=(Key_Share_HelloRetryRequest&&) = default;
386
387 std::vector<uint8_t> serialize() const {
388 auto code = m_selected_group.wire_code();
389 return {get_byte<0>(code), get_byte<1>(code)};
390 }
391
392 Named_Group selected_group() const { return m_selected_group; }
393
394 std::vector<Named_Group> offered_groups() const {
395 throw Invalid_Argument("Hello Retry Request never offers any key exchange groups");
396 }
397
398 bool empty() const { return m_selected_group == Group_Params::NONE; }
399
400 private:
401 Named_Group m_selected_group;
402};
403
404} // namespace
405
406class Key_Share::Key_Share_Impl {
407 public:
408 using Key_Share_Type = std::variant<Key_Share_ClientHello, Key_Share_ServerHello, Key_Share_HelloRetryRequest>;
409
410 Key_Share_Impl(Key_Share_Type ks) : key_share(std::move(ks)) {}
411
412 // NOLINTNEXTLINE(*-non-private-member-variables-in-classes)
413 Key_Share_Type key_share;
414};
415
416Key_Share::Key_Share(TLS_Data_Reader& reader, uint16_t extension_size, Handshake_Type message_type) {
417 if(message_type == Handshake_Type::ClientHello) {
418 m_impl = std::make_unique<Key_Share_Impl>(Key_Share_ClientHello(reader, extension_size));
419 } else if(message_type == Handshake_Type::HelloRetryRequest) // Connection_Side::Server
420 {
421 m_impl = std::make_unique<Key_Share_Impl>(Key_Share_HelloRetryRequest(reader, extension_size));
422 } else if(message_type == Handshake_Type::ServerHello) // Connection_Side::Server
423 {
424 m_impl = std::make_unique<Key_Share_Impl>(Key_Share_ServerHello(reader, extension_size));
425 } else {
426 throw Invalid_Argument(std::string("cannot create a Key_Share extension for message of type: ") +
427 handshake_type_to_string(message_type));
428 }
429}
430
431// ClientHello
432Key_Share::Key_Share(const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng) :
433 m_impl(std::make_unique<Key_Share_Impl>(Key_Share_ClientHello(policy, cb, rng))) {}
434
435// HelloRetryRequest
437 m_impl(std::make_unique<Key_Share_Impl>(Key_Share_HelloRetryRequest(selected_group))) {}
438
439// ServerHello
441 const Key_Share& client_keyshare,
442 const Policy& policy,
443 Callbacks& cb,
445 m_impl(std::make_unique<Key_Share_Impl>(Key_Share_ServerHello(
446 selected_group, std::get<Key_Share_ClientHello>(client_keyshare.m_impl->key_share), policy, cb, rng))) {}
447
448Key_Share::~Key_Share() = default;
449
450std::vector<uint8_t> Key_Share::serialize(Connection_Side /*whoami*/) const {
451 return std::visit([](const auto& key_share) { return key_share.serialize(); }, m_impl->key_share);
452}
453
454bool Key_Share::empty() const {
455 return std::visit([](const auto& key_share) { return key_share.empty(); }, m_impl->key_share);
456}
457
458std::unique_ptr<Key_Share> Key_Share::create_as_encapsulation(Group_Params selected_group,
459 const Key_Share& client_keyshare,
460 const Policy& policy,
461 Callbacks& cb,
463 return std::unique_ptr<Key_Share>(new Key_Share(selected_group, client_keyshare, policy, cb, rng));
464}
465
467 const Policy& policy,
468 Callbacks& cb,
470 return std::visit(overloaded{[&](Key_Share_ClientHello& ch, const Key_Share_ServerHello& sh) {
471 return ch.decapsulate(sh, policy, cb, rng);
472 },
473 [](const auto&, const auto&) -> secure_vector<uint8_t> {
474 throw Invalid_Argument(
475 "can only decapsulate in ClientHello Key_Share with a ServerHello Key_Share");
476 }},
477 m_impl->key_share,
478 server_keyshare.m_impl->key_share);
479}
480
481std::vector<Named_Group> Key_Share::offered_groups() const {
482 return std::visit([](const auto& keyshare) { return keyshare.offered_groups(); }, m_impl->key_share);
483}
484
486 return std::visit([](const auto& keyshare) { return keyshare.selected_group(); }, m_impl->key_share);
487}
488
490 return std::visit(
491 overloaded{[](Key_Share_ServerHello& server_keyshare) { return server_keyshare.take_shared_secret(); },
492 [](auto&) -> secure_vector<uint8_t> {
493 throw Invalid_Argument("Only the key share in Server Hello contains a shared secret");
494 }},
495 m_impl->key_share);
496}
497
498void Key_Share::retry_offer(const Key_Share& retry_request_keyshare,
499 const std::vector<Named_Group>& supported_groups,
500 Callbacks& cb,
502 std::visit(overloaded{[&](Key_Share_ClientHello& ch, const Key_Share_HelloRetryRequest& hrr) {
503 auto selected = hrr.selected_group();
504 // RFC 8446 4.2.8
505 // [T]he selected_group field [MUST correspond] to a group which was provided in
506 // the "supported_groups" extension in the original ClientHello
507 if(!value_exists(supported_groups, selected)) {
508 throw TLS_Exception(Alert::IllegalParameter, "group was not advertised as supported");
509 }
510
511 return ch.retry_offer(selected, cb, rng);
512 },
513 [](const auto&, const auto&) {
514 throw Invalid_Argument("can only retry with HelloRetryRequest on a ClientHello Key_Share");
515 }},
516 m_impl->key_share,
517 retry_request_keyshare.m_impl->key_share);
518}
519
520} // namespace Botan::TLS
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:41
static std::pair< std::vector< uint8_t >, secure_vector< uint8_t > > destructure(KEM_Encapsulation &&kem)
Definition pubkey.h:566
std::vector< Named_Group > offered_groups() const
Key_Share(TLS_Data_Reader &reader, uint16_t extension_size, Handshake_Type message_type)
secure_vector< uint8_t > decapsulate(const Key_Share &server_keyshare, const Policy &policy, Callbacks &cb, RandomNumberGenerator &rng)
void retry_offer(const Key_Share &retry_request_keyshare, const std::vector< Named_Group > &supported_groups, Callbacks &cb, RandomNumberGenerator &rng)
static std::unique_ptr< Key_Share > create_as_encapsulation(Group_Params selected_group, const Key_Share &client_keyshare, const Policy &policy, Callbacks &cb, RandomNumberGenerator &rng)
std::vector< uint8_t > serialize(Connection_Side whoami) const override
secure_vector< uint8_t > take_shared_secret()
constexpr CT::Mask< T > all_zeros(const T elem[], size_t len)
Definition ct_utils.h:332
Group_Params Named_Group
const char * handshake_type_to_string(Handshake_Type type)
void append_tls_length_value(std::vector< uint8_t, Alloc > &buf, const T *vals, size_t vals_size, size_t tag_size)
Definition tls_reader.h:180
bool value_exists(const std::vector< T > &vec, const OT &val)
Definition stl_util.h:118
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61