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