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

randombit / botan / 5454043422

04 Jul 2023 11:13AM CUT coverage: 91.659% (-0.07%) from 91.732%
5454043422

Pull #3609

github

web-flow
Merge 5741e183c into cf8d8a6ca
Pull Request #3609: [TLS 1.3] Hybrid PQ/T key establishment

78635 of 85791 relevant lines covered (91.66%)

12300857.61 hits per line

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

89.66
/src/lib/tls/tls13/tls_extensions_key_share.cpp
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
#include <botan/dh.h>
30
#include <botan/dl_group.h>
31
#include <botan/ecdh.h>
32

33
namespace Botan::TLS {
34

35
namespace {
36

37
class Key_Share_Entry {
4,735✔
38
   public:
39
      Key_Share_Entry(TLS_Data_Reader& reader) {
2,762✔
40
         // TODO check that the group actually exists before casting...
41
         m_group = static_cast<Named_Group>(reader.get_uint16_t());
2,762✔
42
         m_key_exchange = reader.get_tls_length_value(2);
5,524✔
43
      }
2,762✔
44

45
      // Create an empty Key_Share_Entry with the selected group
46
      // but don't pre-generate a keypair, yet.
47
      Key_Share_Entry(const TLS::Group_Params group) : m_group(group) {}
359✔
48

49
      Key_Share_Entry(const TLS::Group_Params group, Callbacks& cb, RandomNumberGenerator& rng) :
997✔
50
            m_group(group), m_private_key(cb.tls_kem_generate_key(group, rng)) {
997✔
51
         if(!m_private_key) {
997✔
52
            throw TLS_Exception(Alert::InternalError, "Application did not provide a suitable ephemeral key pair");
×
53
         }
54

55
         if(is_kem(group)) {
997✔
56
            m_key_exchange = m_private_key->public_key_bits();
2✔
57
         } else if(is_ecdh(group)) {
995✔
58
            auto pkey = dynamic_cast<ECDH_PublicKey*>(m_private_key.get());
156✔
59
            if(!pkey) {
156✔
60
               throw TLS_Exception(Alert::InternalError, "Application did not provide a ECDH_PublicKey");
×
61
            }
62

63
            // RFC 8446 Ch. 4.2.8.2
64
            //
65
            //   Note: Versions of TLS prior to 1.3 permitted point format
66
            //   negotiation; TLS 1.3 removes this feature in favor of a single point
67
            //   format for each curve.
68
            //
69
            // Hence, we neither need to take Policy::use_ecc_point_compression() nor
70
            // ClientHello::prefers_compressed_ec_points() into account here.
71
            m_key_exchange = pkey->public_value(EC_Point_Format::Uncompressed);
156✔
72
         } else {
73
            auto pkey = dynamic_cast<PK_Key_Agreement_Key*>(m_private_key.get());
839✔
74
            if(!pkey) {
839✔
75
               throw TLS_Exception(Alert::InternalError, "Application did not provide a key-agreement key");
×
76
            }
77

78
            m_key_exchange = pkey->public_value();
839✔
79
         }
80
      }
997✔
81

82
      bool empty() const { return (m_group == Group_Params::NONE) && m_key_exchange.empty(); }
×
83

84
      std::vector<uint8_t> serialize() const {
1,477✔
85
         std::vector<uint8_t> result;
1,477✔
86
         result.reserve(m_key_exchange.size() + 2);
1,477✔
87

88
         const uint16_t named_curve_id = static_cast<uint16_t>(m_group);
1,477✔
89
         result.push_back(get_byte<0>(named_curve_id));
1,477✔
90
         result.push_back(get_byte<1>(named_curve_id));
1,477✔
91
         append_tls_length_value(result, m_key_exchange, 2);
1,477✔
92

93
         return result;
1,477✔
94
      }
×
95

96
      Named_Group group() const { return m_group; }
8,609✔
97

98
      secure_vector<uint8_t> encapsulate(const Key_Share_Entry& client_share,
359✔
99
                                         const Policy& policy,
100
                                         Callbacks& cb,
101
                                         RandomNumberGenerator& rng) {
102
         auto kem_result = cb.tls_kem_encapsulate(m_group, client_share.m_key_exchange, rng, policy);
359✔
103
         m_key_exchange = std::move(kem_result.encapsulated_shared_key());
358✔
104
         return std::move(kem_result.shared_key());
358✔
105
      }
106

107
      /**
108
       * Perform KEM decapsulation with another Key_Share_Entry's public key
109
       *
110
       * The caller must ensure that both this and `received` have the same group.
111
       * This method must not be called on Key_Share_Entries without a private key.
112
       */
113
      secure_vector<uint8_t> decapsulate(const Key_Share_Entry& received,
422✔
114
                                         const Policy& policy,
115
                                         Callbacks& cb,
116
                                         RandomNumberGenerator& rng) {
117
         BOTAN_ASSERT_NOMSG(m_group == received.m_group);
422✔
118
         BOTAN_STATE_CHECK(m_private_key != nullptr);
422✔
119

120
         auto shared_secret = cb.tls_kem_decapsulate(m_group, *m_private_key, received.m_key_exchange, rng, policy);
422✔
121
         m_private_key.reset();
421✔
122

123
         // RFC 8422 - 5.11.
124
         //   With X25519 and X448, a receiving party MUST check whether the
125
         //   computed premaster secret is the all-zero value and abort the
126
         //   handshake if so, as described in Section 6 of [RFC7748].
127
         if(m_group == Named_Group::X25519 && CT::all_zeros(shared_secret.data(), shared_secret.size()).is_set()) {
756✔
128
            throw TLS_Exception(Alert::DecryptError, "Bad X25519 key exchange");
×
129
         }
130

131
         return shared_secret;
421✔
132
      }
×
133

134
   private:
135
      Named_Group m_group;
136
      std::vector<uint8_t> m_key_exchange;
137
      std::unique_ptr<Private_Key> m_private_key;
138
};
139

140
class Key_Share_ClientHello;
141

142
class Key_Share_ServerHello {
143
   public:
144
      Key_Share_ServerHello(TLS_Data_Reader& reader, uint16_t) : m_server_share(reader) {}
469✔
145

146
      Key_Share_ServerHello(Named_Group group,
147
                            const Key_Share_ClientHello& client_keyshare,
148
                            const Policy& policy,
149
                            Callbacks& cb,
150
                            RandomNumberGenerator& rng);
151

152
      ~Key_Share_ServerHello() = default;
2,478✔
153

154
      Key_Share_ServerHello(const Key_Share_ServerHello&) = delete;
155
      Key_Share_ServerHello& operator=(const Key_Share_ServerHello&) = delete;
156

157
      Key_Share_ServerHello(Key_Share_ServerHello&&) = default;
827✔
158
      Key_Share_ServerHello& operator=(Key_Share_ServerHello&&) = default;
159

160
      std::vector<uint8_t> serialize() const { return m_server_share.serialize(); }
366✔
161

162
      bool empty() const { return m_server_share.empty(); }
730✔
163

164
      Key_Share_Entry& get_singleton_entry() { return m_server_share; }
359✔
165

166
      const Key_Share_Entry& get_singleton_entry() const { return m_server_share; }
426✔
167

168
      std::vector<Named_Group> offered_groups() const { return {selected_group()}; }
×
169

170
      Named_Group selected_group() const { return m_server_share.group(); }
591✔
171

172
      secure_vector<uint8_t> take_shared_secret() {
354✔
173
         BOTAN_STATE_CHECK(!m_shared_secret.empty());
354✔
174
         return std::exchange(m_shared_secret, {});
354✔
175
      }
176

177
   private:
178
      Key_Share_Entry m_server_share;
179
      secure_vector<uint8_t> m_shared_secret;
180
};
181

182
class Key_Share_ClientHello {
183
   public:
184
      Key_Share_ClientHello(TLS_Data_Reader& reader, uint16_t /* extension_size */) {
936✔
185
         // This construction is a crutch to make working with the incoming
186
         // TLS_Data_Reader bearable. Currently, this reader spans the entire
187
         // Client_Hello message. Hence, if offset or length fields are skewed
188
         // or maliciously fabricated, it is possible to read further than the
189
         // bounds of the current extension.
190
         // Note that this aplies to many locations in the code base.
191
         //
192
         // TODO: Overhaul the TLS_Data_Reader to allow for cheap "sub-readers"
193
         //       that enforce read bounds of sub-structures while parsing.
194
         const auto client_key_share_length = reader.get_uint16_t();
936✔
195
         const auto read_bytes_so_far_begin = reader.read_so_far();
936✔
196
         auto remaining = [&] {
5,524✔
197
            const auto read_so_far = reader.read_so_far() - read_bytes_so_far_begin;
4,588✔
198
            BOTAN_STATE_CHECK(read_so_far <= client_key_share_length);
4,588✔
199
            return client_key_share_length - read_so_far;
4,588✔
200
         };
936✔
201

202
         while(reader.has_remaining() && remaining() > 0) {
3,228✔
203
            if(remaining() < 4) {
2,294✔
204
               throw TLS_Exception(Alert::DecodeError, "Not enough data to read another KeyShareEntry");
1✔
205
            }
206

207
            Key_Share_Entry new_entry(reader);
2,293✔
208

209
            // RFC 8446 4.2.8
210
            //    Clients MUST NOT offer multiple KeyShareEntry values for the same
211
            //    group. [...]
212
            //    Servers MAY check for violations of these rules and abort the
213
            //    handshake with an "illegal_parameter" alert if one is violated.
214
            if(std::find_if(m_client_shares.begin(), m_client_shares.end(), [&](const auto& entry) {
5,774✔
215
                  return entry.group() == new_entry.group();
2,437✔
216
               }) != m_client_shares.end()) {
2,293✔
217
               throw TLS_Exception(Alert::IllegalParameter, "Received multiple key share entries for the same group");
1✔
218
            }
219

220
            m_client_shares.emplace_back(std::move(new_entry));
2,292✔
221
         }
2,293✔
222

223
         if((reader.read_so_far() - read_bytes_so_far_begin) != client_key_share_length) {
934✔
224
            throw Decoding_Error("Read bytes are not equal client KeyShare length");
×
225
         }
226
      }
936✔
227

228
      Key_Share_ClientHello(const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng) {
963✔
229
         const auto supported = policy.key_exchange_groups();
963✔
230
         const auto offers = policy.key_exchange_groups_to_offer();
963✔
231

232
         // RFC 8446 P. 48
233
         //
234
         //   This vector MAY be empty if the client is requesting a
235
         //   HelloRetryRequest.  Each KeyShareEntry value MUST correspond to a
236
         //   group offered in the "supported_groups" extension and MUST appear in
237
         //   the same order.  However, the values MAY be a non-contiguous subset
238
         //   of the "supported_groups" extension and MAY omit the most preferred
239
         //   groups.
240
         //
241
         // ... hence, we're going through the supported groups and find those that
242
         //     should be used to offer a key exchange. This will satisfy above spec.
243
         for(const auto group : supported) {
11,376✔
244
            if(std::find(offers.begin(), offers.end(), group) == offers.end()) {
10,413✔
245
               continue;
9,446✔
246
            }
247
            m_client_shares.emplace_back(group, cb, rng);
967✔
248
         }
249
      }
1,926✔
250

251
      ~Key_Share_ClientHello() = default;
4,742✔
252

253
      Key_Share_ClientHello(const Key_Share_ClientHello&) = delete;
254
      Key_Share_ClientHello& operator=(const Key_Share_ClientHello&) = delete;
255

256
      Key_Share_ClientHello(Key_Share_ClientHello&&) = default;
1,897✔
257
      Key_Share_ClientHello& operator=(Key_Share_ClientHello&&) = default;
258

259
      void retry_offer(const TLS::Group_Params to_offer, Callbacks& cb, RandomNumberGenerator& rng) {
31✔
260
         // RFC 8446 4.2.8
261
         //    The selected_group field [MUST] not correspond to a group which was provided
262
         //    in the "key_share" extension in the original ClientHello.
263
         if(std::find_if(m_client_shares.cbegin(), m_client_shares.cend(), [&](const auto& kse) {
62✔
264
               return kse.group() == to_offer;
31✔
265
            }) != m_client_shares.cend()) {
31✔
266
            throw TLS_Exception(Alert::IllegalParameter, "group was already offered");
1✔
267
         }
268

269
         m_client_shares.clear();
30✔
270
         m_client_shares.emplace_back(to_offer, cb, rng);
30✔
271
      }
30✔
272

273
      std::vector<Named_Group> offered_groups() const {
910✔
274
         std::vector<Named_Group> offered_groups;
910✔
275
         offered_groups.reserve(m_client_shares.size());
910✔
276
         for(const auto& share : m_client_shares) {
4,272✔
277
            offered_groups.push_back(share.group());
3,362✔
278
         }
279
         return offered_groups;
910✔
280
      }
×
281

282
      Named_Group selected_group() const { throw Invalid_Argument("Client Hello Key Share does not select a group"); }
×
283

284
      std::vector<uint8_t> serialize() const {
1,103✔
285
         std::vector<uint8_t> shares;
1,103✔
286
         for(const auto& share : m_client_shares) {
2,214✔
287
            const auto serialized_share = share.serialize();
1,111✔
288
            shares.insert(shares.end(), serialized_share.cbegin(), serialized_share.cend());
1,111✔
289
         }
1,111✔
290

291
         std::vector<uint8_t> result;
1,103✔
292
         append_tls_length_value(result, shares, 2);
1,103✔
293
         return result;
1,103✔
294
      }
1,103✔
295

296
      bool empty() const {
297
         // RFC 8446 4.2.8
298
         //    Clients MAY send an empty client_shares vector in order to request
299
         //    group selection from the server, at the cost of an additional round
300
         //    trip [...].
301
         return false;
302
      }
303

304
      secure_vector<uint8_t> encapsulate(Key_Share_ServerHello& server_share,
359✔
305
                                         const Policy& policy,
306
                                         Callbacks& cb,
307
                                         RandomNumberGenerator& rng) const {
308
         auto& server_selected = server_share.get_singleton_entry();
359✔
309

310
         // find the client offer that matches the server offer
311
         auto match = std::find_if(m_client_shares.begin(), m_client_shares.end(), [&](const auto& offered) {
359✔
312
            return offered.group() == server_selected.group();
359✔
313
         });
314

315
         // We validated that the selected group was indeed offered by the
316
         // client before even constructing the Server Hello that contains the
317
         // Key_Share_ServerHello extension.
318
         BOTAN_STATE_CHECK(match != m_client_shares.end());
359✔
319

320
         return server_selected.encapsulate(*match, policy, cb, rng);
359✔
321
      }
322

323
      secure_vector<uint8_t> decapsulate(const Key_Share_ServerHello& server_share,
426✔
324
                                         const Policy& policy,
325
                                         Callbacks& cb,
326
                                         RandomNumberGenerator& rng) {
327
         const auto& server_selected = server_share.get_singleton_entry();
426✔
328

329
         // find the client offer that matches the server offer
330
         auto match = std::find_if(m_client_shares.begin(), m_client_shares.end(), [&](const auto& offered) {
426✔
331
            return offered.group() == server_selected.group();
426✔
332
         });
333

334
         // RFC 8446 4.2.8:
335
         //   [The KeyShareEntry in the ServerHello] MUST be in the same group
336
         //   as the KeyShareEntry value offered by the client that the server
337
         //   has selected for the negotiated key exchange.
338
         if(match == m_client_shares.end()) {
426✔
339
            throw TLS_Exception(Alert::IllegalParameter, "Server selected a key exchange group we didn't offer.");
4✔
340
         }
341

342
         return match->decapsulate(server_selected, policy, cb, rng);
422✔
343
      }
344

345
   private:
346
      std::vector<Key_Share_Entry> m_client_shares;
347
};
348

349
Key_Share_ServerHello::Key_Share_ServerHello(Named_Group group,
359✔
350
                                             const Key_Share_ClientHello& client_keyshare,
351
                                             const Policy& policy,
352
                                             Callbacks& cb,
353
                                             RandomNumberGenerator& rng) :
359✔
354
      m_server_share(group) {
359✔
355
   m_shared_secret = client_keyshare.encapsulate(*this, policy, cb, rng);
359✔
356
}
359✔
357

358
class Key_Share_HelloRetryRequest {
359
   public:
360
      Key_Share_HelloRetryRequest(TLS_Data_Reader& reader, uint16_t extension_size) {
41✔
361
         constexpr auto sizeof_uint16_t = sizeof(uint16_t);
41✔
362

363
         if(extension_size != sizeof_uint16_t) {
41✔
364
            throw Decoding_Error("Size of KeyShare extension in HelloRetryRequest must be " +
×
365
                                 std::to_string(sizeof_uint16_t) + " bytes");
×
366
         }
367

368
         m_selected_group = static_cast<Named_Group>(reader.get_uint16_t());
41✔
369
      }
41✔
370

371
      Key_Share_HelloRetryRequest(Named_Group selected_group) : m_selected_group(selected_group) {}
35✔
372

373
      ~Key_Share_HelloRetryRequest() = default;
374

375
      Key_Share_HelloRetryRequest(const Key_Share_HelloRetryRequest&) = delete;
376
      Key_Share_HelloRetryRequest& operator=(const Key_Share_HelloRetryRequest&) = delete;
377

378
      Key_Share_HelloRetryRequest(Key_Share_HelloRetryRequest&&) = default;
379
      Key_Share_HelloRetryRequest& operator=(Key_Share_HelloRetryRequest&&) = default;
380

381
      std::vector<uint8_t> serialize() const {
38✔
382
         return {get_byte<0>(static_cast<uint16_t>(m_selected_group)),
38✔
383
                 get_byte<1>(static_cast<uint16_t>(m_selected_group))};
38✔
384
      }
385

386
      Named_Group selected_group() const { return m_selected_group; }
62✔
387

388
      std::vector<Named_Group> offered_groups() const {
×
389
         throw Invalid_Argument("Hello Retry Request never offers any key exchange groups");
×
390
      }
391

392
      bool empty() const { return m_selected_group == Group_Params::NONE; }
37✔
393

394
   private:
395
      Named_Group m_selected_group;
396
};
397

398
}  // namespace
399

400
class Key_Share::Key_Share_Impl {
5,556✔
401
   public:
402
      using Key_Share_Type = std::variant<Key_Share_ClientHello, Key_Share_ServerHello, Key_Share_HelloRetryRequest>;
403

404
      Key_Share_Impl(Key_Share_Type ks) : key_share(std::move(ks)) {}
2,800✔
405

406
      // NOLINTNEXTLINE(*-non-private-member-variables-in-classes)
407
      Key_Share_Type key_share;
408
};
409

410
Key_Share::Key_Share(TLS_Data_Reader& reader, uint16_t extension_size, Handshake_Type message_type) {
1,447✔
411
   if(message_type == Handshake_Type::ClientHello) {
1,447✔
412
      m_impl = std::make_unique<Key_Share_Impl>(Key_Share_ClientHello(reader, extension_size));
936✔
413
   } else if(message_type == Handshake_Type::HelloRetryRequest)  // Connection_Side::Server
511✔
414
   {
415
      m_impl = std::make_unique<Key_Share_Impl>(Key_Share_HelloRetryRequest(reader, extension_size));
41✔
416
   } else if(message_type == Handshake_Type::ServerHello)  // Connection_Side::Server
470✔
417
   {
418
      m_impl = std::make_unique<Key_Share_Impl>(Key_Share_ServerHello(reader, extension_size));
469✔
419
   } else {
420
      throw Invalid_Argument(std::string("cannot create a Key_Share extension for message of type: ") +
2✔
421
                             handshake_type_to_string(message_type));
2✔
422
   }
423
}
1,447✔
424

425
// ClientHello
426
Key_Share::Key_Share(const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng) :
963✔
427
      m_impl(std::make_unique<Key_Share_Impl>(Key_Share_ClientHello(policy, cb, rng))) {}
1,926✔
428

429
// HelloRetryRequest
430
Key_Share::Key_Share(Named_Group selected_group) :
35✔
431
      m_impl(std::make_unique<Key_Share_Impl>(Key_Share_HelloRetryRequest(selected_group))) {}
35✔
432

433
// ServerHello
434
Key_Share::Key_Share(Group_Params selected_group,
359✔
435
                     const Key_Share& client_keyshare,
436
                     const Policy& policy,
437
                     Callbacks& cb,
438
                     RandomNumberGenerator& rng) :
359✔
439
      m_impl(std::make_unique<Key_Share_Impl>(Key_Share_ServerHello(
358✔
440
         selected_group, std::get<Key_Share_ClientHello>(client_keyshare.m_impl->key_share), policy, cb, rng))) {}
717✔
441

442
Key_Share::~Key_Share() = default;
5,541✔
443

444
std::vector<uint8_t> Key_Share::serialize(Connection_Side /*whoami*/) const {
1,507✔
445
   return std::visit([](const auto& key_share) { return key_share.serialize(); }, m_impl->key_share);
3,014✔
446
}
447

448
bool Key_Share::empty() const {
1,492✔
449
   return std::visit([](const auto& key_share) { return key_share.empty(); }, m_impl->key_share);
1,857✔
450
}
451

452
std::unique_ptr<Key_Share> Key_Share::create_as_encapsulation(Group_Params selected_group,
359✔
453
                                                              const Key_Share& client_keyshare,
454
                                                              const Policy& policy,
455
                                                              Callbacks& cb,
456
                                                              RandomNumberGenerator& rng) {
457
   return std::unique_ptr<Key_Share>(new Key_Share(selected_group, client_keyshare, policy, cb, rng));
359✔
458
}
459

460
secure_vector<uint8_t> Key_Share::decapsulate(const Key_Share& server_keyshare,
426✔
461
                                              const Policy& policy,
462
                                              Callbacks& cb,
463
                                              RandomNumberGenerator& rng) {
464
   return std::visit(overloaded{[&](Key_Share_ClientHello& ch, const Key_Share_ServerHello& sh) {
1,273✔
465
                                   return ch.decapsulate(sh, policy, cb, rng);
426✔
466
                                },
467
                                [](const auto&, const auto&) -> secure_vector<uint8_t> {
×
468
                                   throw Invalid_Argument(
×
469
                                      "can only decapsulate in ClientHello Key_Share with a ServerHello Key_Share");
470
                                }},
471
                     m_impl->key_share,
426✔
472
                     server_keyshare.m_impl->key_share);
426✔
473
}
474

475
std::vector<Named_Group> Key_Share::offered_groups() const {
910✔
476
   return std::visit([](const auto& keyshare) { return keyshare.offered_groups(); }, m_impl->key_share);
1,820✔
477
}
478

479
Named_Group Key_Share::selected_group() const {
620✔
480
   return std::visit([](const auto& keyshare) { return keyshare.selected_group(); }, m_impl->key_share);
620✔
481
}
482

483
secure_vector<uint8_t> Key_Share::take_shared_secret() {
354✔
484
   return std::visit(
354✔
485
      overloaded{[](Key_Share_ServerHello& server_keyshare) { return server_keyshare.take_shared_secret(); },
354✔
486
                 [](auto&) -> secure_vector<uint8_t> {
×
487
                    throw Invalid_Argument("Only the key share in Server Hello contains a shared secret");
×
488
                 }},
489
      m_impl->key_share);
354✔
490
}
491

492
void Key_Share::retry_offer(const Key_Share& retry_request_keyshare,
33✔
493
                            const std::vector<Named_Group>& supported_groups,
494
                            Callbacks& cb,
495
                            RandomNumberGenerator& rng) {
496
   std::visit(overloaded{[&](Key_Share_ClientHello& ch, const Key_Share_HelloRetryRequest& hrr) {
96✔
497
                            auto selected = hrr.selected_group();
33✔
498
                            // RFC 8446 4.2.8
499
                            //    [T]he selected_group field [MUST correspond] to a group which was provided in
500
                            //    the "supported_groups" extension in the original ClientHello
501
                            if(!value_exists(supported_groups, selected)) {
66✔
502
                               throw TLS_Exception(Alert::IllegalParameter, "group was not advertised as supported");
2✔
503
                            }
504

505
                            return ch.retry_offer(selected, cb, rng);
31✔
506
                         },
507
                         [](const auto&, const auto&) {
×
508
                            throw Invalid_Argument("can only retry with HelloRetryRequest on a ClientHello Key_Share");
×
509
                         }},
510
              m_impl->key_share,
33✔
511
              retry_request_keyshare.m_impl->key_share);
33✔
512
}
30✔
513

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

© 2025 Coveralls, Inc