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