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

randombit / botan / 30974838434

04 Aug 2026 05:33PM UTC coverage: 89.682% (+0.02%) from 89.662%
30974838434

push

github

web-flow
Merge pull request #5793 from randombit/jack/dtls-bounded-retry

Bound DTLS handshake retry attempts and properly abandon timed out states

119137 of 132844 relevant lines covered (89.68%)

10371831.54 hits per line

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

91.73
/src/tests/unit_tls.cpp
1
/*
2
* (C) 2014,2015,2018 Jack Lloyd
3
*     2016 Matthias Gierlings
4
*     2017 René Korthaus, Rohde & Schwarz Cybersecurity
5
*     2017 Harry Reimann, Rohde & Schwarz Cybersecurity
6
*     2023 René Meusel, Rohde & Schwarz Cybersecurity
7
*     2026 René Meusel, Rohde & Schwarz Networks and Cybersecurity
8
*     2025 Lars Dürkop, CARIAD SE
9
*
10
* Botan is released under the Simplified BSD License (see license.txt)
11
*/
12

13
#include "tests.h"
14
#include <chrono>
15
#include <memory>
16
#include <vector>
17

18
#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_RSA)
19

20
   #include <botan/certstor.h>
21
   #include <botan/dl_group.h>
22
   #include <botan/rng.h>
23
   #include <botan/tls_callbacks.h>
24
   #include <botan/tls_client.h>
25
   #include <botan/tls_exceptn.h>
26
   #include <botan/tls_extensions.h>
27
   #include <botan/tls_external_psk.h>
28
   #include <botan/tls_policy.h>
29
   #include <botan/tls_server.h>
30
   #include <botan/tls_session_manager_memory.h>
31
   #include <botan/tls_session_manager_noop.h>
32
   #include <botan/internal/stl_util.h>
33
   #include <botan/internal/tls_reader.h>
34

35
   #include <botan/ec_group.h>
36
   #include <botan/ecdh.h>
37
   #include <botan/ecdsa.h>
38
   #include <botan/hex.h>
39
   #include <botan/pk_ops.h>
40
   #include <botan/pkcs10.h>
41
   #include <botan/rsa.h>
42
   #include <botan/x509_ca.h>
43
   #include <botan/x509self.h>
44

45
   #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
46
      #include <botan/tls_session_manager_sqlite.h>
47
   #endif
48

49
#endif
50

51
namespace Botan_Tests {
52

53
namespace {
54

55
#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_RSA)
56

57
class Credentials_Manager_Test final : public Botan::Credentials_Manager {
58
   public:
59
      Credentials_Manager_Test(bool with_client_certs,
2✔
60
                               const Botan::X509_Certificate& rsa_cert,
61
                               Botan::Private_Key* rsa_key,
62
                               const Botan::X509_Certificate& rsa_ca,
63
                               const Botan::X509_CRL& rsa_crl,
64
                               const Botan::X509_Certificate& ecdsa_cert,
65
                               Botan::Private_Key* ecdsa_key,
66
                               const Botan::X509_Certificate& ecdsa_ca,
67
                               const Botan::X509_CRL& ecdsa_crl) :
2✔
68
            m_rsa_cert(rsa_cert),
2✔
69
            m_rsa_ca(rsa_ca),
2✔
70
            m_rsa_key(rsa_key),
2✔
71
            m_ecdsa_cert(ecdsa_cert),
2✔
72
            m_ecdsa_ca(ecdsa_ca),
2✔
73
            m_ecdsa_key(ecdsa_key),
2✔
74
            m_provides_client_certs(with_client_certs) {
4✔
75
         auto store = std::make_unique<Botan::Certificate_Store_In_Memory>();
2✔
76
         store->add_certificate(m_rsa_ca);
2✔
77
         store->add_certificate(m_ecdsa_ca);
2✔
78
         store->add_crl(rsa_crl);
2✔
79
         store->add_crl(ecdsa_crl);
2✔
80

81
         m_stores.push_back(std::move(store));
2✔
82
      }
2✔
83

84
      std::vector<Botan::Certificate_Store*> trusted_certificate_authorities(const std::string& /*type*/,
149✔
85
                                                                             const std::string& /*context*/) override {
86
         std::vector<Botan::Certificate_Store*> v;
149✔
87
         v.reserve(m_stores.size());
149✔
88
         for(const auto& store : m_stores) {
298✔
89
            v.push_back(store.get());
149✔
90
         }
91
         return v;
149✔
92
      }
×
93

94
      std::vector<Botan::X509_Certificate> find_cert_chain(const std::vector<std::string>& cert_key_types,
190✔
95
                                                           const std::vector<Botan::AlgorithmIdentifier>& /*unused*/,
96
                                                           const std::vector<Botan::X509_DN>& acceptable_CAs,
97
                                                           const std::string& type,
98
                                                           const std::string& context) override {
99
         BOTAN_UNUSED(context);
190✔
100
         std::vector<Botan::X509_Certificate> chain;
190✔
101

102
         if(m_acceptable_cas.empty()) {
190✔
103
            m_acceptable_cas = acceptable_CAs;
8✔
104
         }
105

106
         if(type == "tls-server" || (type == "tls-client" && m_provides_client_certs)) {
190✔
107
            for(const auto& key_type : cert_key_types) {
129✔
108
               if(key_type == "RSA") {
129✔
109
                  chain.push_back(m_rsa_cert);
53✔
110
                  chain.push_back(m_rsa_ca);
53✔
111
                  break;
112
               } else if(key_type == "ECDSA") {
76✔
113
                  chain.push_back(m_ecdsa_cert);
76✔
114
                  chain.push_back(m_ecdsa_ca);
76✔
115
                  break;
116
               }
117
            }
118
         }
119

120
         return chain;
190✔
121
      }
×
122

123
      std::shared_ptr<Botan::Private_Key> private_key_for(const Botan::X509_Certificate& crt,
73✔
124
                                                          const std::string& /*type*/,
125
                                                          const std::string& /*context*/) override {
126
         if(crt == m_rsa_cert) {
73✔
127
            return m_rsa_key;
10✔
128
         }
129
         if(crt == m_ecdsa_cert) {
63✔
130
            return m_ecdsa_key;
63✔
131
         }
132
         return nullptr;
×
133
      }
134

135
      Botan::secure_vector<uint8_t> session_ticket_key() override {
×
136
         return Botan::hex_decode_locked("AABBCCDDEEFF012345678012345678");
×
137
      }
138

139
      Botan::secure_vector<uint8_t> dtls_cookie_secret() override {
33✔
140
         return Botan::hex_decode_locked("4AEA5EAD279CADEB537A594DA0E9DE3A");
66✔
141
      }
142

143
      std::vector<Botan::TLS::ExternalPSK> find_preshared_keys(
51✔
144
         std::string_view host,
145
         Botan::TLS::Connection_Side whoami,
146
         const std::vector<std::string>& identities = {},
147
         const std::optional<std::string>& prf = std::nullopt) override {
148
         if(identities.empty()) {
51✔
149
            return Botan::Credentials_Manager::find_preshared_keys(host, whoami, identities, prf);
41✔
150
         }
151

152
         std::vector<Botan::TLS::ExternalPSK> psks;
10✔
153

154
         if(host == "server.example.com") {
10✔
155
            // PSKs for server and client are equal. For the sake of clarity,
156
            // we're not simplifying this code further
157
            if(whoami == Botan::TLS::Connection_Side::Client) {
10✔
158
               psks.emplace_back(
5✔
159
                  std::string{}, "SHA-256", Botan::hex_decode_locked("20B602D1475F2DF888FCB60D2AE03AFD"));
10✔
160
            }
161

162
            if(whoami == Botan::TLS::Connection_Side::Server) {
10✔
163
               psks.emplace_back(
5✔
164
                  std::string{}, "SHA-256", Botan::hex_decode_locked("20B602D1475F2DF888FCB60D2AE03AFD"));
10✔
165
            }
166
         }
167

168
         return psks;
10✔
169
      }
10✔
170

171
      const std::vector<Botan::X509_DN>& get_acceptable_cas() const { return m_acceptable_cas; }
4✔
172

173
   private:
174
      Botan::X509_Certificate m_rsa_cert, m_rsa_ca;
175
      std::shared_ptr<Botan::Private_Key> m_rsa_key;
176

177
      Botan::X509_Certificate m_ecdsa_cert, m_ecdsa_ca;
178
      std::shared_ptr<Botan::Private_Key> m_ecdsa_key;
179

180
      std::vector<std::unique_ptr<Botan::Certificate_Store>> m_stores;
181
      bool m_provides_client_certs;
182
      std::vector<Botan::X509_DN> m_acceptable_cas;
183
};
184

185
std::shared_ptr<Credentials_Manager_Test> create_creds(Botan::RandomNumberGenerator& rng,
2✔
186
                                                       bool with_client_certs = false) {
187
   // RSA and ECDSA are required for the TLS module, but we need to find an
188
   // ECC group that is supported in this build or skip this test.
189
   //
190
   // secp256r1 is excluded because the Strict_Policy requires at least SHA-384
191
   // and TLS 1.3 enforces that hash lengths match the key strength.
192
   const auto ec_group = Test::supported_ec_group_name({"secp384r1", "secp521r1"});
2✔
193
   if(!ec_group) {
2✔
194
      return nullptr;
×
195
   }
196

197
   const auto ecdsa_params = Botan::EC_Group::from_name(*ec_group);
2✔
198
   const size_t rsa_params = 1024;
2✔
199

200
   auto rsa_ca_key = std::make_unique<Botan::RSA_PrivateKey>(rng, rsa_params);
2✔
201
   auto rsa_srv_key = std::make_unique<Botan::RSA_PrivateKey>(rng, rsa_params);
2✔
202

203
   auto ecdsa_ca_key = std::make_unique<Botan::ECDSA_PrivateKey>(rng, ecdsa_params);
2✔
204
   auto ecdsa_srv_key = std::make_unique<Botan::ECDSA_PrivateKey>(rng, ecdsa_params);
2✔
205

206
   Botan::X509_Cert_Options rsa_ca_opts("RSA Test CA/VT");
2✔
207
   Botan::X509_Cert_Options ecdsa_ca_opts("ECDSA Test CA/VT");
2✔
208
   rsa_ca_opts.CA_key(1);
2✔
209
   ecdsa_ca_opts.CA_key(1);
2✔
210

211
   const Botan::X509_Certificate rsa_ca_cert =
2✔
212
      Botan::X509::create_self_signed_cert(rsa_ca_opts, *rsa_ca_key, "SHA-256", rng);
2✔
213
   const Botan::X509_Certificate ecdsa_ca_cert =
2✔
214
      Botan::X509::create_self_signed_cert(ecdsa_ca_opts, *ecdsa_ca_key, "SHA-256", rng);
2✔
215

216
   const Botan::X509_Cert_Options server_opts("server.example.com");
2✔
217

218
   const Botan::PKCS10_Request rsa_req = Botan::X509::create_cert_req(server_opts, *rsa_srv_key, "SHA-256", rng);
2✔
219
   const Botan::PKCS10_Request ecdsa_req = Botan::X509::create_cert_req(server_opts, *ecdsa_srv_key, "SHA-256", rng);
2✔
220

221
   const Botan::X509_CA rsa_ca(rsa_ca_cert, *rsa_ca_key, "SHA-256", rng);
2✔
222
   const Botan::X509_CA ecdsa_ca(ecdsa_ca_cert, *ecdsa_ca_key, "SHA-256", rng);
2✔
223

224
   typedef std::chrono::duration<int, std::ratio<31556926>> years;
2✔
225
   auto now = std::chrono::system_clock::now();
2✔
226

227
   const Botan::X509_Time start_time(now);
2✔
228
   const Botan::X509_Time end_time(now + years(1));
2✔
229

230
   const Botan::X509_Certificate rsa_srv_cert = rsa_ca.sign_request(rsa_req, rng, start_time, end_time);
2✔
231
   const Botan::X509_Certificate ecdsa_srv_cert = ecdsa_ca.sign_request(ecdsa_req, rng, start_time, end_time);
2✔
232

233
   const Botan::X509_CRL rsa_crl = rsa_ca.new_crl(rng);
2✔
234
   const Botan::X509_CRL ecdsa_crl = ecdsa_ca.new_crl(rng);
2✔
235

236
   return std::make_shared<Credentials_Manager_Test>(with_client_certs,
2✔
237
                                                     rsa_srv_cert,
238
                                                     rsa_srv_key.release(),
4✔
239
                                                     rsa_ca_cert,
240
                                                     rsa_crl,
241
                                                     ecdsa_srv_cert,
242
                                                     ecdsa_srv_key.release(),
2✔
243
                                                     ecdsa_ca_cert,
244
                                                     ecdsa_crl);
2✔
245
}
6✔
246

247
class TLS_Handshake_Test final {
248
   private:
249
      using generate_ephemeral_ecdh_key_clbk = std::function<std::unique_ptr<Botan::PK_Key_Agreement_Key>(
250
         Botan::TLS::Group_Params, Botan::RandomNumberGenerator&, Botan::EC_Point_Format)>;
251
      using ephemeral_key_agreement_clbk =
252
         std::function<Botan::secure_vector<uint8_t>(const std::variant<Botan::TLS::Group_Params, Botan::DL_Group>&,
253
                                                     const Botan::PK_Key_Agreement_Key&,
254
                                                     const std::vector<uint8_t>&,
255
                                                     Botan::RandomNumberGenerator&,
256
                                                     const Botan::TLS::Policy&)>;
257
      using custom_kdf_clbk = std::function<std::unique_ptr<Botan::KDF>(std::string_view)>;
258

259
   public:
260
      TLS_Handshake_Test(const std::string& test_descr,
124✔
261
                         Botan::TLS::Protocol_Version offer_version,
262
                         const std::shared_ptr<Credentials_Manager_Test>& creds,
263
                         const std::shared_ptr<const Botan::TLS::Policy>& client_policy,
264
                         const std::shared_ptr<const Botan::TLS::Policy>& server_policy,
265
                         const std::shared_ptr<Botan::RandomNumberGenerator>& rng,
266
                         const std::shared_ptr<Botan::TLS::Session_Manager>& client_sessions,
267
                         const std::shared_ptr<Botan::TLS::Session_Manager>& server_sessions,
268
                         bool expect_client_auth) :
124✔
269
            m_offer_version(offer_version),
124✔
270
            m_results(test_descr),
124✔
271
            m_creds(creds),
124✔
272
            m_client_policy(client_policy),
124✔
273
            m_client_sessions(client_sessions),
124✔
274
            m_rng(rng),
124✔
275
            m_client_auth(expect_client_auth) {
124✔
276
         m_server_cb = std::make_shared<Test_Callbacks>(m_results, offer_version, m_s2c, m_server_recv);
124✔
277
         m_client_cb = std::make_shared<Test_Callbacks>(m_results, offer_version, m_c2s, m_client_recv);
124✔
278

279
         const bool is_dtls = offer_version.is_datagram_protocol();
124✔
280

281
         m_server =
124✔
282
            std::make_unique<Botan::TLS::Server>(m_server_cb, server_sessions, m_creds, server_policy, m_rng, is_dtls);
124✔
283
      }
124✔
284

285
      void go();
286

287
      const Test::Result& results() const { return m_results; }
288

289
      Test::Result& results() { return m_results; }
153✔
290

291
      void set_custom_client_tls_session_established_callback(
9✔
292
         std::function<void(const Botan::TLS::Session_Summary&)> clbk) {
293
         BOTAN_ASSERT_NONNULL(m_client_cb);
9✔
294
         m_client_cb->set_custom_tls_session_established_callback(std::move(clbk));
18✔
295
      }
9✔
296

297
      void set_custom_server_tls_session_established_callback(
9✔
298
         std::function<void(const Botan::TLS::Session_Summary&)> clbk) {
299
         BOTAN_ASSERT_NONNULL(m_server_cb);
9✔
300
         m_server_cb->set_custom_tls_session_established_callback(std::move(clbk));
18✔
301
      }
9✔
302

303
      void set_custom_client_tls_generate_ephemeral_ecdh_key_callback(generate_ephemeral_ecdh_key_clbk clbk) {
9✔
304
         BOTAN_ASSERT_NONNULL(m_client_cb);
9✔
305
         m_client_cb->set_custom_client_tls_generate_ephemeral_ecdh_key_callback(std::move(clbk));
18✔
306
      }
9✔
307

308
      void set_custom_client_tls_ephemeral_key_agreement_callback(ephemeral_key_agreement_clbk clbk) {
9✔
309
         BOTAN_ASSERT_NONNULL(m_client_cb);
9✔
310
         m_client_cb->set_custom_client_tls_ephemeral_key_agreement_callback(std::move(clbk));
18✔
311
      }
9✔
312

313
      void set_client_expected_handshake_alert(Botan::TLS::Alert alert) {
9✔
314
         BOTAN_ASSERT_NONNULL(m_client_cb);
9✔
315
         m_client_cb->set_expected_handshake_alert(alert);
9✔
316
      }
9✔
317

318
      void set_server_expected_handshake_alert(Botan::TLS::Alert alert) {
9✔
319
         BOTAN_ASSERT_NONNULL(m_server_cb);
9✔
320
         m_server_cb->set_expected_handshake_alert(alert);
9✔
321
      }
9✔
322

323
      // Tests that drive an intentional handshake abort (eg via a throwing
324
      // tls_session_established callback) call this so the end-of-test
325
      // completion assertion in go() is suppressed.
326
      void expect_handshake_failure() { m_expect_handshake_completion = false; }
18✔
327

328
      void expect_session_resumption() { m_expect_session_resumption = true; }
48✔
329

330
      void set_client_custom_kdf_callback(custom_kdf_clbk clbk) {
1✔
331
         BOTAN_ASSERT_NONNULL(m_client_cb);
1✔
332
         m_client_cb->set_custom_kdf_callback(std::move(clbk));
2✔
333
      }
1✔
334

335
      void set_server_custom_kdf_callback(custom_kdf_clbk clbk) {
1✔
336
         BOTAN_ASSERT_NONNULL(m_server_cb);
1✔
337
         m_server_cb->set_custom_kdf_callback(std::move(clbk));
2✔
338
      }
1✔
339

340
   private:
341
      class Test_Extension : public Botan::TLS::Extension {
342
         public:
343
            static Botan::TLS::Extension_Code static_type() {
344
               // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
345
               return static_cast<Botan::TLS::Extension_Code>(666);
346
            }
347

348
            Botan::TLS::Extension_Code type() const override { return static_type(); }
357✔
349

350
            std::vector<uint8_t> serialize(Botan::TLS::Connection_Side /*whoami*/) const override { return m_buf; }
426✔
351

352
            const std::vector<uint8_t>& value() const { return m_buf; }
353

354
            bool empty() const override { return false; }
668✔
355

356
            explicit Test_Extension(Botan::TLS::Connection_Side side) {
357✔
357
               const uint8_t client_extn[6] = {'c', 'l', 'i', 'e', 'n', 't'};
357✔
358
               const uint8_t server_extn[6] = {'s', 'e', 'r', 'v', 'e', 'r'};
357✔
359

360
               Botan::TLS::append_tls_length_value(
357✔
361
                  m_buf, (side == Botan::TLS::Connection_Side::Client) ? client_extn : server_extn, 6, 1);
357✔
362
            }
357✔
363

364
         private:
365
            std::vector<uint8_t> m_buf;
366
      };
367

368
      class Test_Callbacks final : public Botan::TLS::Callbacks {
369
         public:
370
            Test_Callbacks(Test::Result& results,
248✔
371
                           Botan::TLS::Protocol_Version expected_version,
372
                           std::vector<uint8_t>& outbound,
373
                           std::vector<uint8_t>& recv_buf) :
248✔
374
                  m_results(results), m_expected_version(expected_version), m_outbound(outbound), m_recv(recv_buf) {}
248✔
375

376
            Test_Callbacks(Test_Callbacks&&) = delete;
377
            Test_Callbacks(const Test_Callbacks&) = delete;
378
            Test_Callbacks& operator=(const Test_Callbacks&) = delete;
379
            Test_Callbacks& operator=(Test_Callbacks&&) = delete;
380

381
            ~Test_Callbacks() override {
248✔
382
               if(m_expected_handshake_alert.has_value()) {
248✔
383
                  m_results.test_failure("Expected: " + m_expected_handshake_alert->type_string() +
×
384
                                         " during handshake");
385
               }
386
            }
489✔
387

388
            void tls_emit_data(std::span<const uint8_t> bits) override {
1,864✔
389
               m_outbound.insert(m_outbound.end(), bits.begin(), bits.end());
1,864✔
390
            }
1,864✔
391

392
            void tls_record_received(uint64_t /*seq*/, std::span<const uint8_t> bits) override {
352✔
393
               m_recv.insert(m_recv.end(), bits.begin(), bits.end());
352✔
394
            }
352✔
395

396
            void tls_alert(Botan::TLS::Alert alert) override {
372✔
397
               // TODO test that it is a no_renegotiation alert
398

399
               if(m_expected_handshake_alert.has_value()) {
372✔
400
                  if(m_expected_handshake_alert->type() != alert.type()) {
18✔
401
                     m_results.test_failure("Got unexpected alert: " + alert.type_string() +
×
402
                                            " expected: " + m_expected_handshake_alert->type_string());
×
403
                  } else {
404
                     // acknowledge that the expected Alert was detected
405
                     m_results.test_note("saw expected alert", m_expected_handshake_alert->type_string());
18✔
406
                     m_expected_handshake_alert.reset();
18✔
407
                  }
408
               }
409
            }
372✔
410

411
            void tls_modify_extensions(Botan::TLS::Extensions& extn,
458✔
412
                                       Botan::TLS::Connection_Side which_side,
413
                                       Botan::TLS::Handshake_Type msg_type) override {
414
               // We don't alter any TLS 1.3 ServerHellos (including the special
415
               // HelloRetryRequest), as Botan explicitly rejects any custom
416
               // extensions in those messages. Even though RFC 9846 doesn't
417
               // explicitly forbid them.
418
               if(m_expected_version.is_tls_13_or_later() &&
458✔
419
                  (msg_type == Botan::TLS::Handshake_Type::HelloRetryRequest ||
292✔
420
                   msg_type == Botan::TLS::Handshake_Type::ServerHello)) {
292✔
421
                  return;
422
               }
423

424
               // When updating a previously-sent ClientHello after having received
425
               // a HelloRetryRequest, we don't want to re-add the test extension.
426
               if(!extn.has<Test_Extension>()) {
387✔
427
                  extn.add(new Test_Extension(which_side));  // NOLINT(*-owning-memory)
357✔
428
               }
429

430
               // Insert an unsupported signature scheme as highest prio, to ensure we are tolerant of this
431
               if(auto* sig_algs = extn.get<Botan::TLS::Signature_Algorithms>()) {
387✔
432
                  std::vector<Botan::TLS::Signature_Scheme> schemes = sig_algs->supported_schemes();
179✔
433
                  // 0x0301 is RSA PKCS1/SHA-224, which is not supported anymore
434
                  // NOLINTNEXTLINE(*.EnumCastOutOfRange)
435
                  constexpr auto unsupported_sig_scheme = Botan::TLS::Signature_Scheme::Code(0x0301);
179✔
436

437
                  // Don't inadvertently add the unsupported signature scheme
438
                  // multiple times, e.g. when updating a ClientHello after
439
                  // receiving a HelloRetryRequest.
440
                  if(!Botan::value_exists(schemes, unsupported_sig_scheme)) {
179✔
441
                     schemes.insert(schemes.begin(), unsupported_sig_scheme);
149✔
442

443
                     // This replaces the previous extension value
444
                     extn.remove_extension(Botan::TLS::Extension_Code::SignatureAlgorithms);
149✔
445
                     extn.add(new Botan::TLS::Signature_Algorithms(schemes));  // NOLINT(*-owning-memory)
149✔
446
                  }
447
               }
179✔
448

449
               // In TLS 1.3 ClientHellos the PSK extension must always be the
450
               // very last extension.
451
               extn.reorder(std::array{Botan::TLS::Extension_Code::PresharedKey});
387✔
452
            }
453

454
            void tls_examine_extensions(const Botan::TLS::Extensions& extn,
458✔
455
                                        Botan::TLS::Connection_Side which_side,
456
                                        Botan::TLS::Handshake_Type msg_type) override {
457
               // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
458
               const auto extn_id = static_cast<Botan::TLS::Extension_Code>(666);
458✔
459
               Botan::TLS::Extension* test_extn = extn.get(extn_id);
458✔
460

461
               if(test_extn == nullptr) {
458✔
462
                  // The test extension won't be added to any TLS 1.3 ServerHello
463
                  // or HelloRetryRequest messages, so we don't expect it.
464
                  if(!m_expected_version.is_tls_13_or_later() ||
71✔
465
                     (msg_type != Botan::TLS::Handshake_Type::HelloRetryRequest &&
71✔
466
                      msg_type != Botan::TLS::Handshake_Type::ServerHello)) {
71✔
467
                     m_results.test_failure("Did not receive test extension from peer");
×
468
                  }
469
               } else {
470
                  Botan::TLS::Unknown_Extension* unknown_ext = dynamic_cast<Botan::TLS::Unknown_Extension*>(test_extn);
387✔
471

472
                  if(unknown_ext != nullptr) {
387✔
473
                     const std::vector<uint8_t> val = unknown_ext->value();
387✔
474

475
                     if(m_results.test_sz_eq("Expected size for test extn", val.size(), 7)) {
387✔
476
                        if(which_side == Botan::TLS::Connection_Side::Client) {
387✔
477
                           m_results.test_bin_eq("Expected extension value", val, "06636C69656E74");
156✔
478
                        } else {
479
                           m_results.test_bin_eq("Expected extension value", val, "06736572766572");
231✔
480
                        }
481
                     }
482
                  } else {
387✔
483
                     m_results.test_failure("Unknown extension type had unexpected type at runtime");
×
484
                  }
485
               }
486
            }
458✔
487

488
            void tls_session_established(const Botan::TLS::Session_Summary& session) override {
239✔
489
               const std::string session_report = "Session established " + session.version().to_string() + " " +
717✔
490
                                                  session.ciphersuite().to_string() + " " +
717✔
491
                                                  Botan::hex_encode(session.session_id().get());
478✔
492

493
               m_results.test_note(session_report);
239✔
494

495
               if(m_session_established_callback) {
239✔
496
                  m_session_established_callback(session);
18✔
497
               }
498

499
               if(session.version() != m_expected_version) {
221✔
500
                  m_results.test_failure("Expected " + m_expected_version.to_string() + " negotiated " +
×
501
                                         session.version().to_string());
18✔
502
               }
503

504
               if(m_summary.has_value()) {
221✔
505
                  m_results.test_failure("Session established callback called multiple times");
×
506
               }
507

508
               m_summary.emplace(session);
221✔
509
            }
221✔
510

511
            std::string tls_server_choose_app_protocol(const std::vector<std::string>& protos) override {
124✔
512
               m_results.test_sz_eq("ALPN protocol count", protos.size(), 2);
124✔
513
               m_results.test_str_eq("ALPN protocol 1", protos[0], "test/1");
124✔
514
               m_results.test_str_eq("ALPN protocol 2", protos[1], "test/2");
124✔
515
               return "test/1";
124✔
516
            }
517

518
            std::string tls_peer_network_identity() override { return "test-peer"; }
22✔
519

520
            std::unique_ptr<Botan::PK_Key_Agreement_Key> tls_generate_ephemeral_key(
130✔
521
               const std::variant<Botan::TLS::Group_Params, Botan::DL_Group>& group,
522
               Botan::RandomNumberGenerator& rng) override {
523
               const bool is_group = std::holds_alternative<Botan::TLS::Group_Params>(group);
130✔
524

525
               if(is_group && std::get<Botan::TLS::Group_Params>(group).wire_code() == 0xFEE1) {
130✔
526
                  const auto ec_group = Botan::EC_Group::from_name("numsp256d1");
×
527
                  return std::make_unique<Botan::ECDH_PrivateKey>(rng, ec_group);
×
528
               }
×
529

530
               if(is_group && m_generate_ephemeral_ecdh_key_callback) {
130✔
531
                  return m_generate_ephemeral_ecdh_key_callback(
3✔
532
                     std::get<Botan::TLS::Group_Params>(group), rng, Botan::EC_Point_Format::Uncompressed);
3✔
533
               }
534

535
               return Botan::TLS::Callbacks::tls_generate_ephemeral_key(group, rng);
127✔
536
            }
537

538
            std::unique_ptr<Botan::PK_Key_Agreement_Key> tls12_generate_ephemeral_ecdh_key(
18✔
539
               Botan::TLS::Group_Params group,
540
               Botan::RandomNumberGenerator& rng,
541
               Botan::EC_Point_Format tls12_ecc_pubkey_encoding_format) override {
542
               if(m_generate_ephemeral_ecdh_key_callback) {
18✔
543
                  return m_generate_ephemeral_ecdh_key_callback(group, rng, tls12_ecc_pubkey_encoding_format);
6✔
544
               }
545

546
               return Botan::TLS::Callbacks::tls12_generate_ephemeral_ecdh_key(
12✔
547
                  group, rng, tls12_ecc_pubkey_encoding_format);
12✔
548
            }
549

550
            Botan::secure_vector<uint8_t> tls_ephemeral_key_agreement(
106✔
551
               const std::variant<Botan::TLS::Group_Params, Botan::DL_Group>& group,
552
               const Botan::PK_Key_Agreement_Key& private_key,
553
               const std::vector<uint8_t>& public_value,
554
               Botan::RandomNumberGenerator& rng,
555
               const Botan::TLS::Policy& policy) override {
556
               if(m_ephemeral_key_agreement_callback) {
106✔
557
                  return m_ephemeral_key_agreement_callback(group, private_key, public_value, rng, policy);
9✔
558
               }
559

560
               if(std::holds_alternative<Botan::TLS::Group_Params>(group) &&
97✔
561
                  std::get<Botan::TLS::Group_Params>(group).wire_code() == 0xFEE1) {
95✔
562
                  const auto ec_group = Botan::EC_Group::from_name("numsp256d1");
×
563
                  const auto ec_point = Botan::EC_AffinePoint(ec_group, public_value);
×
564
                  const Botan::ECDH_PublicKey peer_key(ec_group, ec_point);
×
565
                  const Botan::PK_Key_Agreement ka(private_key, rng, "Raw");
×
566
                  return ka.derive_key(0, peer_key.public_value()).bits_of();
×
567
               }
×
568

569
               return Botan::TLS::Callbacks::tls_ephemeral_key_agreement(group, private_key, public_value, rng, policy);
97✔
570
            }
571

572
            std::unique_ptr<Botan::KDF> tls12_protocol_specific_kdf(std::string_view prf_algo) const override {
628✔
573
               if(m_custom_kdf_callback) {
628✔
574
                  return m_custom_kdf_callback(prf_algo);
8✔
575
               } else {
576
                  return Botan::TLS::Callbacks::tls12_protocol_specific_kdf(prf_algo);
620✔
577
               }
578
            }
579

580
            void set_custom_tls_session_established_callback(
18✔
581
               std::function<void(const Botan::TLS::Session_Summary&)> clbk) {
582
               m_session_established_callback = std::move(clbk);
18✔
583
            }
584

585
            void set_custom_client_tls_generate_ephemeral_ecdh_key_callback(generate_ephemeral_ecdh_key_clbk clbk) {
9✔
586
               m_generate_ephemeral_ecdh_key_callback = std::move(clbk);
9✔
587
            }
588

589
            void set_custom_client_tls_ephemeral_key_agreement_callback(ephemeral_key_agreement_clbk clbk) {
9✔
590
               m_ephemeral_key_agreement_callback = std::move(clbk);
9✔
591
            }
592

593
            void set_expected_handshake_alert(Botan::TLS::Alert alert) { m_expected_handshake_alert = alert; }
18✔
594

595
            void set_custom_kdf_callback(custom_kdf_clbk clbk) { m_custom_kdf_callback = std::move(clbk); }
2✔
596

597
            const std::optional<Botan::TLS::Session_Summary>& summary() const { return m_summary; }
221✔
598

599
         private:
600
            Test::Result& m_results;
601
            const Botan::TLS::Protocol_Version m_expected_version;
602
            std::vector<uint8_t>& m_outbound;
603
            std::vector<uint8_t>& m_recv;
604

605
            std::function<void(const Botan::TLS::Session_Summary&)> m_session_established_callback;
606
            generate_ephemeral_ecdh_key_clbk m_generate_ephemeral_ecdh_key_callback;
607
            ephemeral_key_agreement_clbk m_ephemeral_key_agreement_callback;
608
            custom_kdf_clbk m_custom_kdf_callback;
609
            std::optional<Botan::TLS::Alert> m_expected_handshake_alert;
610
            std::optional<Botan::TLS::Session_Summary> m_summary;
611
      };
612

613
      const Botan::TLS::Protocol_Version m_offer_version;
614
      Test::Result m_results;
615

616
      std::shared_ptr<Credentials_Manager_Test> m_creds;
617
      std::shared_ptr<const Botan::TLS::Policy> m_client_policy;
618
      std::shared_ptr<Botan::TLS::Session_Manager> m_client_sessions;
619
      std::shared_ptr<Botan::RandomNumberGenerator> m_rng;
620

621
      std::shared_ptr<Test_Callbacks> m_client_cb;
622

623
      std::shared_ptr<Test_Callbacks> m_server_cb;
624
      std::unique_ptr<Botan::TLS::Server> m_server;
625

626
      const bool m_client_auth;
627
      bool m_expect_handshake_completion = true;
628
      bool m_expect_session_resumption = false;
629

630
      std::vector<uint8_t> m_c2s, m_s2c, m_client_recv, m_server_recv;
631
      std::vector<std::string> m_caught_tls_exceptions;
632
};
633

634
void TLS_Handshake_Test::go() {
124✔
635
   m_results.start_timer();
124✔
636

637
   const std::vector<std::string> protocols_offered = {"test/1", "test/2"};
124✔
638

639
   // Choose random application data to send
640
   const size_t c_len = 1 + ((static_cast<size_t>(m_rng->next_byte()) << 4) ^ m_rng->next_byte());
124✔
641
   std::vector<uint8_t> client_msg(c_len);
124✔
642
   m_rng->randomize(client_msg.data(), client_msg.size());
124✔
643
   bool client_has_written = false;
124✔
644

645
   const size_t s_len = 1 + ((static_cast<size_t>(m_rng->next_byte()) << 4) ^ m_rng->next_byte());
124✔
646
   std::vector<uint8_t> server_msg(s_len);
124✔
647
   m_rng->randomize(server_msg.data(), server_msg.size());
124✔
648
   bool server_has_written = false;
124✔
649

650
   std::unique_ptr<Botan::TLS::Client> client;
124✔
651
   client = std::make_unique<Botan::TLS::Client>(m_client_cb,
124✔
652
                                                 m_client_sessions,
124✔
653
                                                 m_creds,
124✔
654
                                                 m_client_policy,
124✔
655
                                                 m_rng,
124✔
656
                                                 Botan::TLS::Server_Information("server.example.com"),
124✔
657
                                                 m_offer_version,
124✔
658
                                                 protocols_offered);
124✔
659

660
   size_t rounds = 0;
124✔
661

662
   bool client_handshake_completed = false;
124✔
663
   bool server_handshake_completed = false;
124✔
664

665
   while(true) {
1,062✔
666
      ++rounds;
1,062✔
667

668
      if(rounds > 25) {
1,062✔
669
         m_results.test_failure("Still here after many rounds, deadlock?");
×
670
         break;
671
      }
672

673
      if(!client_handshake_completed && client->is_handshake_complete()) {
1,062✔
674
         client_handshake_completed = true;
675
      }
676

677
      if(!server_handshake_completed && m_server->is_handshake_complete()) {
1,062✔
678
         server_handshake_completed = true;
679
      }
680

681
      if(client->is_handshake_complete() && client->is_active() && !client_has_written) {
1,062✔
682
         m_results.test_str_eq("client ALPN protocol", client->application_protocol(), "test/1");
109✔
683

684
         size_t sent_so_far = 0;
109✔
685
         while(sent_so_far != client_msg.size()) {
296✔
686
            const size_t left = client_msg.size() - sent_so_far;
187✔
687
            const size_t rnd12 = (m_rng->next_byte() << 4) ^ m_rng->next_byte();
187✔
688
            const size_t sending = std::min(left, rnd12);
187✔
689

690
            client->send(&client_msg[sent_so_far], sending);
187✔
691
            sent_so_far += sending;
187✔
692
         }
693

694
         if(m_client_cb->summary()->version().is_pre_tls_13()) {
109✔
695
            client->send_warning_alert(Botan::TLS::Alert::NoRenegotiation);
71✔
696
         }
697
         client_has_written = true;
698
      }
699

700
      if(m_server && m_server->is_handshake_complete() && m_server->is_active() && !server_has_written) {
1,062✔
701
         m_results.test_str_eq("server ALPN protocol", m_server->application_protocol(), "test/1");
112✔
702

703
         size_t sent_so_far = 0;
112✔
704
         while(sent_so_far != server_msg.size()) {
291✔
705
            const size_t left = server_msg.size() - sent_so_far;
179✔
706
            const size_t rnd12 = (m_rng->next_byte() << 4) ^ m_rng->next_byte();
179✔
707
            const size_t sending = std::min(left, rnd12);
179✔
708

709
            m_server->send(&server_msg[sent_so_far], sending);
179✔
710
            sent_so_far += sending;
179✔
711
         }
712

713
         if(m_server_cb->summary()->version().is_pre_tls_13()) {
112✔
714
            m_server->send_warning_alert(Botan::TLS::Alert::NoRenegotiation);
77✔
715
         }
716
         server_has_written = true;
717
      }
718

719
      if(!m_c2s.empty()) {
1,062✔
720
         /*
721
         * Use this as a temp value to hold the queues as otherwise they
722
         * might end up appending more in response to messages during the
723
         * handshake.
724
         */
725
         std::vector<uint8_t> input;
440✔
726
         std::swap(m_c2s, input);
440✔
727

728
         try {
440✔
729
            const size_t needed = m_server->received_data(input.data(), input.size());
440✔
730
            m_results.test_sz_eq("full packet received (server)", needed, 0);
431✔
731
         } catch(const std::exception& e) {
9✔
732
            m_caught_tls_exceptions.push_back(e.what());
18✔
733
         }
9✔
734

735
         continue;
440✔
736
      }
440✔
737

738
      if(!m_s2c.empty()) {
622✔
739
         std::vector<uint8_t> input;
392✔
740
         std::swap(m_s2c, input);
392✔
741

742
         try {
392✔
743
            const size_t needed = client->received_data(input.data(), input.size());
392✔
744
            m_results.test_sz_eq("full packet received (client)", needed, 0);
383✔
745
         } catch(const std::exception& e) {
9✔
746
            m_caught_tls_exceptions.push_back(e.what());
18✔
747
         }
9✔
748

749
         continue;
392✔
750
      }
392✔
751

752
      if(!m_client_recv.empty()) {
230✔
753
         m_results.test_bin_eq("client recv", m_client_recv, server_msg);
212✔
754
      }
755

756
      if(!m_server_recv.empty()) {
230✔
757
         m_results.test_bin_eq("server recv", m_server_recv, client_msg);
212✔
758
      }
759

760
      if(client->is_closed() && m_server->is_closed()) {
230✔
761
         break;
762
      }
763

764
      if(m_server->is_active()) {
106✔
765
         const std::vector<Botan::X509_Certificate> certs = m_server->peer_cert_chain();
106✔
766
         if(m_client_auth) {
106✔
767
            m_results.test_sz_eq("got client certs", certs.size(), 2);
4✔
768

769
            const std::vector<Botan::X509_DN> acceptable_CAs = m_creds->get_acceptable_cas();
4✔
770

771
            m_results.test_sz_eq("client got CA list", acceptable_CAs.size(), 2);  // RSA + ECDSA
4✔
772

773
            for(const Botan::X509_DN& dn : acceptable_CAs) {
12✔
774
               m_results.test_str_eq("Expected CA country field", dn.get_first_attribute("C"), "VT");
8✔
775
            }
776
         } else {
4✔
777
            m_results.test_sz_eq("no client certs", certs.size(), 0);
102✔
778
         }
779
      }
106✔
780

781
      if(!m_server_recv.empty() && !m_client_recv.empty()) {
106✔
782
         const Botan::SymmetricKey client_key = client->key_material_export("label", "context", 32);
106✔
783
         const Botan::SymmetricKey server_key = m_server->key_material_export("label", "context", 32);
106✔
784

785
         m_results.test_bin_eq("TLS key material export", client_key.bits_of(), server_key.bits_of());
212✔
786

787
         m_results.test_is_true("Client is active", client->is_active());
106✔
788
         m_results.test_is_false("Client is not closed", client->is_closed());
106✔
789
         client->close();
106✔
790
         m_results.test_is_false("Client is no longer active", client->is_active());
106✔
791
         m_results.test_is_true("Client is closed", client->is_closed_for_writing());
106✔
792
      }
212✔
793
   }
794

795
   // The receive loop above swallows exceptions raised during received_data().
796
   // Without an explicit completion assertion, a handshake that aborts via
797
   // exception still reports "all ok" because the server alert + close path
798
   // unwinds cleanly. Assert that both sides reached is_active() unless the
799
   // test was deliberately set up to drive a handshake abort.
800
   if(m_expect_handshake_completion) {
124✔
801
      if(!m_caught_tls_exceptions.empty()) {
106✔
802
         for(const auto& e : m_caught_tls_exceptions) {
×
803
            m_results.test_note("TLS exception", e);
×
804
         }
805
      }
806

807
      m_results.test_is_true("client handshake completed", client_handshake_completed);
106✔
808
      m_results.test_is_true("server handshake completed", server_handshake_completed);
106✔
809

810
      const auto& client_summary = m_client_cb->summary();
106✔
811
      const auto& server_summary = m_server_cb->summary();
106✔
812

813
      if(m_results.test_is_true("client finished handshake", client_summary.has_value())) {
106✔
814
         m_results.test_bool_eq(
106✔
815
            "client session resumption", client_summary->was_resumption(), m_expect_session_resumption);
106✔
816
      }
817

818
      if(m_results.test_is_true("server finished handshake", server_summary.has_value())) {
106✔
819
         m_results.test_bool_eq(
106✔
820
            "server session resumption", server_summary->was_resumption(), m_expect_session_resumption);
106✔
821
      }
822
   }
823

824
   m_results.end_timer();
124✔
825
}
372✔
826

827
class Test_Policy final : public Botan::TLS::Text_Policy {
40✔
828
   public:
829
      Test_Policy() : Text_Policy("") {}
40✔
830

831
      size_t dtls_initial_timeout() const override { return 1; }
18✔
832

833
      size_t dtls_maximum_timeout() const override { return 8; }
18✔
834

835
      size_t minimum_rsa_bits() const override { return 1024; }
5✔
836

837
      size_t minimum_signature_strength() const override { return 80; }
69✔
838
};
839

840
/**
841
 * This mocks a custom ECDH adapter class that essentially just wraps an
842
 * ordinary ECDH key to mimic a typical hardware-based ECDH key.
843
 */
844
class HardwareEcdhKey final : public Botan::PK_Key_Agreement_Key {
845
   public:
846
      HardwareEcdhKey(Botan::EC_Group group,
9✔
847
                      Botan::RandomNumberGenerator& rng,
848
                      Botan::EC_Point_Format public_key_format) :
9✔
849
            m_group(std::move(group)),
9✔
850
            m_public_key_format(public_key_format),
9✔
851
            m_key(std::make_unique<Botan::ECDH_PrivateKey>(rng, m_group)) {}
9✔
852

853
      std::string algo_name() const override { return m_key->algo_name(); }
×
854

855
      size_t estimated_strength() const override { return m_key->estimated_strength(); }
×
856

857
      bool supports_operation(Botan::PublicKeyOperation op) const override {
×
858
         return op == Botan::PublicKeyOperation::KeyAgreement;
×
859
      }
860

861
      bool check_key(Botan::RandomNumberGenerator& rng, bool strong) const override {
×
862
         return m_key->check_key(rng, strong);
×
863
      }
864

865
      size_t key_length() const override { return m_key->key_length(); }
×
866

867
      Botan::AlgorithmIdentifier algorithm_identifier() const override {
×
868
         throw Botan::Not_Implemented("TLS should never call Public_Key::algorithm_identifier()");
×
869
      }
870

871
      std::vector<uint8_t> raw_public_key_bits() const override { return m_key->public_value(m_public_key_format); }
9✔
872

873
      std::vector<uint8_t> public_key_bits() const override {
×
874
         throw Botan::Not_Implemented("TLS should never call Public_Key::public_key_bits()");
×
875
         // ... instead it should always call raw_public_key_bits() to get the public value
876
         // in the format required by TLS.
877
      }
878

879
      std::vector<uint8_t> public_value() const override {
×
880
         throw Botan::Not_Implemented("TLS should never call Public_Key::public_value()");
×
881
         // ... instead it should always call raw_public_key_bits() to get the public value
882
         // in the format required by TLS.
883
      }
884

885
      Botan::secure_vector<uint8_t> private_key_bits() const override {
×
886
         throw Botan::Not_Implemented("This mocks a hardware key and thus hides its private bits");
×
887
      }
888

889
      std::unique_ptr<Botan::Public_Key> public_key() const override { return m_key->public_key(); }
×
890

891
      std::unique_ptr<Botan::Private_Key> generate_another(Botan::RandomNumberGenerator& rng) const override {
×
892
         return std::make_unique<HardwareEcdhKey>(m_group, rng, m_public_key_format);
×
893
      }
894

895
      std::unique_ptr<Botan::PK_Ops::Key_Agreement> create_key_agreement_op(Botan::RandomNumberGenerator& rng,
9✔
896
                                                                            std::string_view params,
897
                                                                            std::string_view provider) const override {
898
         return m_key->create_key_agreement_op(rng, params, provider);
9✔
899
      }
900

901
   private:
902
      Botan::EC_Group m_group;
903
      Botan::EC_Point_Format m_public_key_format;
904
      std::unique_ptr<Botan::ECDH_PrivateKey> m_key;
905
};
906

907
class TLS_Unit_Tests final : public Test {
1✔
908
   private:
909
      static std::vector<Botan::TLS::Protocol_Version> legacy_versions() {
28✔
910
         return {
28✔
911
   #if defined(BOTAN_HAS_TLS_12)
912
            Botan::TLS::Protocol_Version::TLS_V12, Botan::TLS::Protocol_Version::DTLS_V12,
913
   #endif
914
         };
14✔
915
      }
916

917
      static std::vector<Botan::TLS::Protocol_Version> available_versions() {
38✔
918
         return {
38✔
919
   #if defined(BOTAN_HAS_TLS_12)
920
            Botan::TLS::Protocol_Version::TLS_V12, Botan::TLS::Protocol_Version::DTLS_V12,
921
   #endif
922

923
   #if defined(BOTAN_HAS_TLS_13)
924
               Botan::TLS::Protocol_Version::TLS_V13,
925
   #endif
926
         };
16✔
927
      }
928

929
      // Policy::ciphersuite_list refuses both CBC+HMAC and _CCM_8 suites for
930
      // DTLS, so those policies leave no DTLS ciphersuite to negotiate.
931
      static std::vector<Botan::TLS::Protocol_Version> versions_for_policy(
29✔
932
         std::vector<Test::Result>& results,
933
         std::vector<Botan::TLS::Protocol_Version> versions,
934
         const std::string& cipher_policy,
935
         const std::string& mac_policy) {
936
         const bool dtls_has_no_suite =
29✔
937
            (mac_policy != "AEAD") || (cipher_policy != "NULL") || cipher_policy.find("CCM(8)") != std::string::npos;
29✔
938

939
         if(dtls_has_no_suite) {
29✔
940
            std::erase_if(versions, [](const auto& version) { return version.is_datagram_protocol(); });
102✔
941
            Test::Result result("DTLS ciphersuite filtering");
29✔
942
   #if defined(BOTAN_HAS_TLS_12)
943
            result.test_sz_gte("At least one testable version remains", versions.size(), 1);
29✔
944
   #endif
945
            results.push_back(result);
29✔
946
         }
29✔
947
         return versions;
29✔
948
      }
949

950
      static void enable_versions([[maybe_unused]] const std::shared_ptr<Test_Policy>& policy,
29✔
951
                                  [[maybe_unused]] std::span<const Botan::TLS::Protocol_Version> versions) {
952
         for(const auto& version : versions) {
102✔
953
            if(version == Botan::TLS::Protocol_Version::TLS_V12) {
73✔
954
               policy->set("allow_tls12", "true");
58✔
955
            } else if(version == Botan::TLS::Protocol_Version::DTLS_V12) {
44✔
956
               policy->set("allow_dtls12", "true");
58✔
957
            } else if(version == Botan::TLS::Protocol_Version::TLS_V13) {
15✔
958
               policy->set("allow_tls13", "true");
30✔
959
            }
960
         }
961
      }
29✔
962

963
      static std::shared_ptr<Botan::TLS::Session_Manager> make_session_manager(
96✔
964
         const std::shared_ptr<Botan::RandomNumberGenerator>& rng) {
965
   #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
966
         return std::make_shared<Botan::TLS::Session_Manager_SQLite>("geheimes passwort", rng, ":memory:", 5);
96✔
967
   #else
968
         return std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
969
   #endif
970
      }
971

972
      static void test_with_policy(const std::string& test_descr,
31✔
973
                                   std::vector<Test::Result>& results,
974
                                   const std::shared_ptr<Credentials_Manager_Test>& creds,
975
                                   const std::vector<Botan::TLS::Protocol_Version>& versions,
976
                                   const std::shared_ptr<const Botan::TLS::Policy>& policy,
977
                                   std::shared_ptr<Botan::RandomNumberGenerator>& rng,
978
                                   bool client_auth = false) {
979
         try {
31✔
980
            for(const auto& version : versions) {
79✔
981
               auto client_ses = make_session_manager(rng);
48✔
982
               auto server_ses = make_session_manager(rng);
48✔
983

984
               TLS_Handshake_Test test(version.to_string() + " " + test_descr,
96✔
985
                                       version,
986
                                       creds,
987
                                       policy,
988
                                       policy,
989
                                       rng,
990
                                       client_ses,
991
                                       server_ses,
992
                                       client_auth);
96✔
993
               test.go();
48✔
994
               results.push_back(test.results());
48✔
995

996
               TLS_Handshake_Test test_resumption(version.to_string() + " " + test_descr,
144✔
997
                                                  version,
998
                                                  creds,
999
                                                  policy,
1000
                                                  policy,
1001
                                                  rng,
1002
                                                  client_ses,
1003
                                                  server_ses,
1004
                                                  client_auth);
48✔
1005
               test_resumption.expect_session_resumption();
48✔
1006
               test_resumption.go();
48✔
1007
               results.push_back(test_resumption.results());
48✔
1008
            }
144✔
1009
         } catch(std::exception& e) {
×
1010
            results.push_back(Test::Result::Failure(test_descr, e.what()));
×
1011
         }
×
1012
      }
31✔
1013

1014
      /**
1015
       * Legacy versions are TLS 1.2 and DTLS 1.2, which allow combinations of
1016
       * various ciphers, MACs and key exchange methods that aren't supported
1017
       * in TLS 1.3 anymore.
1018
       */
1019
      static void test_legacy_versions(const std::string& test_descr,
14✔
1020
                                       std::vector<Test::Result>& results,
1021
                                       const std::shared_ptr<Credentials_Manager_Test>& creds,
1022
                                       std::shared_ptr<Botan::RandomNumberGenerator>& rng,
1023
                                       const std::string& kex_policy,
1024
                                       const std::string& cipher_policy,
1025
                                       const std::string& mac_policy = "AEAD",
1026
                                       bool etm_policy = true,
1027
                                       bool client_auth = false) {
1028
         auto policy = std::make_shared<Test_Policy>();
14✔
1029
         policy->set("ciphers", cipher_policy);
14✔
1030
         policy->set("macs", mac_policy);
14✔
1031
         policy->set("key_exchange_methods", kex_policy);
14✔
1032
         policy->set("negotiate_encrypt_then_mac", etm_policy ? "true" : "false");
32✔
1033

1034
         enable_versions(policy, legacy_versions());
14✔
1035

1036
         if(kex_policy.find("RSA") != std::string::npos) {
14✔
1037
            policy->set("signature_methods", "IMPLICIT");
10✔
1038
         }
1039

1040
         const auto versions = versions_for_policy(results, legacy_versions(), cipher_policy, mac_policy);
14✔
1041

1042
         return test_with_policy(test_descr, results, creds, versions, policy, rng, client_auth);
14✔
1043
      }
28✔
1044

1045
      /**
1046
       * Modern versions are both TLS 1.2 and 1.3, as well as their DTLS
1047
       * counterparts, but with the restrictions that TLS 1.3 imposes on
1048
       * ciphers, MACs and key exchange methods.
1049
       */
1050
      static void test_modern_versions(const std::string& test_descr,
7✔
1051
                                       std::vector<Test::Result>& results,
1052
                                       const std::shared_ptr<Credentials_Manager_Test>& creds,
1053
                                       std::shared_ptr<Botan::RandomNumberGenerator>& rng,
1054
                                       const std::string& kex_policy,
1055
                                       const std::string& cipher_policy,
1056
                                       const std::string& mac_policy = "AEAD",
1057
                                       bool client_auth = false) {
1058
         const std::map<std::string, std::string> no_extra_policies;
7✔
1059
         return test_modern_versions(
7✔
1060
            test_descr, results, creds, rng, kex_policy, cipher_policy, mac_policy, no_extra_policies, client_auth);
7✔
1061
      }
7✔
1062

1063
      static void test_modern_versions(const std::string& test_descr,
15✔
1064
                                       std::vector<Test::Result>& results,
1065
                                       const std::shared_ptr<Credentials_Manager_Test>& creds,
1066
                                       std::shared_ptr<Botan::RandomNumberGenerator>& rng,
1067
                                       const std::string& kex_policy,
1068
                                       const std::string& cipher_policy,
1069
                                       const std::string& mac_policy,
1070
                                       const std::map<std::string, std::string>& extra_policies,
1071
                                       bool client_auth = false) {
1072
         auto policy = std::make_shared<Test_Policy>();
15✔
1073
         policy->set("ciphers", cipher_policy);
15✔
1074
         policy->set("macs", mac_policy);
15✔
1075
         policy->set("key_exchange_methods", kex_policy);
15✔
1076

1077
         enable_versions(policy, available_versions());
15✔
1078

1079
         if(kex_policy.find("RSA") != std::string::npos) {
15✔
1080
            policy->set("signature_methods", "IMPLICIT");
×
1081
         }
1082

1083
         for(const auto& kv : extra_policies) {
23✔
1084
            policy->set(kv.first, kv.second);
8✔
1085
         }
1086

1087
         const auto versions = versions_for_policy(results, available_versions(), cipher_policy, mac_policy);
15✔
1088

1089
         return test_with_policy(test_descr, results, creds, versions, policy, rng, client_auth);
15✔
1090
      }
30✔
1091

1092
      void test_session_established_abort(std::vector<Test::Result>& results,
1✔
1093
                                          std::shared_ptr<Credentials_Manager_Test> creds,
1094
                                          std::shared_ptr<Botan::RandomNumberGenerator> rng) {
1095
         auto policy = std::make_shared<Test_Policy>();
1✔
1096
         auto noop_session_manager = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1097

1098
         auto client_aborts = [&](const std::exception_ptr& ex, Botan::TLS::Alert expected_server_alert) {
4✔
1099
            for(const auto version : available_versions()) {
12✔
1100
               TLS_Handshake_Test test("Client aborts in tls_session_established with " +
18✔
1101
                                          expected_server_alert.type_string() + ": " + version.to_string(),
36✔
1102
                                       version,
1103
                                       creds,
1104
                                       policy,
1105
                                       policy,
1106
                                       rng,
1107
                                       noop_session_manager,
1108
                                       noop_session_manager,
1109
                                       false);
45✔
1110
               test.set_custom_client_tls_session_established_callback(
18✔
1111
                  [=](const auto&) { std::rethrow_exception(ex); });
63✔
1112
               test.set_server_expected_handshake_alert(expected_server_alert);
9✔
1113
               test.expect_handshake_failure();
9✔
1114

1115
               test.go();
9✔
1116
               results.push_back(test.results());
9✔
1117
            }
12✔
1118
         };
3✔
1119

1120
         auto server_aborts = [&](const std::exception_ptr& ex, Botan::TLS::Alert expected_server_alert) {
4✔
1121
            for(const auto version : available_versions()) {
12✔
1122
               TLS_Handshake_Test test("Server aborts in tls_session_established with " +
18✔
1123
                                          expected_server_alert.type_string() + ": " + version.to_string(),
36✔
1124
                                       version,
1125
                                       creds,
1126
                                       policy,
1127
                                       policy,
1128
                                       rng,
1129
                                       noop_session_manager,
1130
                                       noop_session_manager,
1131
                                       false);
45✔
1132
               test.set_custom_server_tls_session_established_callback(
18✔
1133
                  [=](const auto&) { std::rethrow_exception(ex); });
63✔
1134
               test.set_client_expected_handshake_alert(expected_server_alert);
9✔
1135
               test.expect_handshake_failure();
9✔
1136

1137
               test.go();
9✔
1138
               results.push_back(test.results());
9✔
1139
            }
12✔
1140
         };
3✔
1141

1142
         client_aborts(std::make_exception_ptr(
3✔
1143
                          Botan::TLS::TLS_Exception(Botan::TLS::Alert::AccessDenied, "some test TLS exception")),
1✔
1144
                       Botan::TLS::Alert::AccessDenied);
1145
         client_aborts(std::make_exception_ptr(Botan::Invalid_Authentication_Tag("some symmetric crypto failed :o)")),
2✔
1146
                       Botan::TLS::Alert::BadRecordMac);
1147
         client_aborts(std::make_exception_ptr(std::runtime_error("something strange happened")),
2✔
1148
                       Botan::TLS::Alert::InternalError);
1149

1150
         server_aborts(std::make_exception_ptr(
3✔
1151
                          Botan::TLS::TLS_Exception(Botan::TLS::Alert::AccessDenied, "some server test TLS exception")),
1✔
1152
                       Botan::TLS::Alert::AccessDenied);
1153
         server_aborts(std::make_exception_ptr(
2✔
1154
                          Botan::Invalid_Authentication_Tag("some symmetric crypto failed in the server :o)")),
1✔
1155
                       Botan::TLS::Alert::BadRecordMac);
1156
         server_aborts(std::make_exception_ptr(std::runtime_error("something strange happened in the server")),
2✔
1157
                       Botan::TLS::Alert::InternalError);
1158
      }
2✔
1159

1160
      void test_custom_ecdh_provider(std::vector<Test::Result>& results,
1✔
1161
                                     const std::shared_ptr<Credentials_Manager_Test>& creds,
1162
                                     const std::shared_ptr<Botan::RandomNumberGenerator>& rng) {
1163
         auto noop_session_manager = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1164

1165
         const auto groups = {
1✔
1166
            Botan::TLS::Group_Params::SECP256R1,
1167
            Botan::TLS::Group_Params::BRAINPOOL256R1,
1168
            Botan::TLS::Group_Params::BRAINPOOL512R1,
1169
         };
1✔
1170

1171
         const auto versions = available_versions();
1✔
1172

1173
         const auto groups_and_versions = [&]() {
2✔
1174
            std::vector<std::pair<Botan::TLS::Group_Params, Botan::TLS::Protocol_Version>> out;
1✔
1175
            for(const auto& group : groups) {
4✔
1176
               for(const auto& version : versions) {
12✔
1177
                  out.emplace_back(group, version);
9✔
1178
               }
1179
            }
1180
            return out;
1✔
1181
         };
×
1182

1183
         for(const auto& [ecdh_group, version] : groups_and_versions()) {
10✔
1184
            if(!Botan::EC_Group::supports_named_group(ecdh_group.to_string().value())) {
27✔
1185
               continue;
×
1186
            }
1187

1188
            auto policy = std::make_shared<Test_Policy>();
9✔
1189
            policy->set("groups", "0x" + Botan::hex_encode(Botan::store_be(ecdh_group.wire_code())));
27✔
1190

1191
            TLS_Handshake_Test test("Client uses a custom ECDH provider for " + ecdh_group.to_string().value() +
54✔
1192
                                       " in " + version.to_string(),
27✔
1193
                                    version,
1194
                                    creds,
1195
                                    policy,
1196
                                    policy,
1197
                                    rng,
1198
                                    noop_session_manager,
1199
                                    noop_session_manager,
1200
                                    false);
45✔
1201

1202
            auto& test_results = test.results();
9✔
1203

1204
            bool generator_called = false;
9✔
1205
            bool agreement_called = false;
9✔
1206

1207
            test.set_custom_client_tls_generate_ephemeral_ecdh_key_callback(
×
1208
               [&](const Botan::TLS::Group_Params& group,
9✔
1209
                   Botan::RandomNumberGenerator& clbk_rng,
1210
                   Botan::EC_Point_Format format) -> std::unique_ptr<Botan::PK_Key_Agreement_Key> {
1211
                  generator_called = true;
9✔
1212
                  test_results.require("tls_generate_ephemeral_ecdh_key_callback called for ECDH",
9✔
1213
                                       group.is_ecdh_named_curve());
9✔
1214
                  const auto ec_group = Botan::EC_Group::from_name(group.to_string().value());
18✔
1215
                  return std::make_unique<HardwareEcdhKey>(ec_group, clbk_rng, format);
9✔
1216
               });
9✔
1217

1218
            test.set_custom_client_tls_ephemeral_key_agreement_callback(
×
1219
               [&](const std::variant<Botan::TLS::Group_Params, Botan::DL_Group>& group,
9✔
1220
                   const Botan::PK_Key_Agreement_Key& private_key,
1221
                   const std::vector<uint8_t>& peer_public_value,
1222
                   Botan::RandomNumberGenerator& clbk_rng,
1223
                   const Botan::TLS::Policy&) -> Botan::secure_vector<uint8_t> {
1224
                  agreement_called = true;
9✔
1225

1226
                  const auto* group_params = std::get_if<Botan::TLS::Group_Params>(&group);
9✔
1227
                  test_results.require("tls_ephemeral_key_agreement_callback called with a TLS group",
9✔
1228
                                       group_params != nullptr);
1229
                  test_results.require("tls_ephemeral_key_agreement_callback called with an ECDH group",
9✔
1230
                                       group_params->is_ecdh_named_curve());
9✔
1231

1232
                  const auto* hwkey = dynamic_cast<const HardwareEcdhKey*>(&private_key);
9✔
1233
                  test_results.require("tls_ephemeral_key_agreement_callback called with a HardwareEcdhKey",
9✔
1234
                                       hwkey != nullptr);
1235

1236
                  const Botan::PK_Key_Agreement ka(private_key, clbk_rng, "Raw");
9✔
1237
                  return ka.derive_key(0 /* no KDF */, peer_public_value).bits_of();
27✔
1238
               });
9✔
1239

1240
            test.go();
9✔
1241
            test.results().test_is_true("custom generation was used", generator_called);
9✔
1242
            test.results().test_is_true("custom agreement was used", agreement_called);
9✔
1243
            results.push_back(test.results());
9✔
1244
         }
19✔
1245
      }
2✔
1246

1247
      class CustomKDF : public Botan::KDF {
8✔
1248
         public:
1249
            std::string name() const override { return "CustomKDF"; }
×
1250

1251
            std::unique_ptr<KDF> new_object() const override { return std::make_unique<CustomKDF>(); }
×
1252

1253
            // always returns a static master secret
1254
            void perform_kdf(std::span<uint8_t> key,
10✔
1255
                             std::span<const uint8_t> /*secret*/,
1256
                             std::span<const uint8_t> /*salt*/,
1257
                             std::span<const uint8_t> /*label*/) const override {
1258
               std::fill(key.begin(), key.end(), 0xAA);
10✔
1259
            }
10✔
1260
      };
1261

1262
      void test_custom_kdf_provider(std::vector<Test::Result>& results,
1✔
1263
                                    const std::shared_ptr<Credentials_Manager_Test>& creds,
1264
                                    const std::shared_ptr<Botan::RandomNumberGenerator>& rng) {
1265
         auto noop_session_manager = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1266

1267
         auto policy = std::make_shared<Test_Policy>();
1✔
1268

1269
         TLS_Handshake_Test test("Client and Server use a custom KDF provider in TLS 1.2",
1✔
1270
                                 Botan::TLS::Protocol_Version::TLS_V12,
1271
                                 creds,
1272
                                 policy,
1273
                                 policy,
1274
                                 rng,
1275
                                 noop_session_manager,
1276
                                 noop_session_manager,
1277
                                 false);
5✔
1278

1279
         bool client_custom_kdf_called = false;
1✔
1280
         bool server_custom_kdf_called = false;
1✔
1281

1282
         test.set_client_custom_kdf_callback([&](std::string_view) -> std::unique_ptr<Botan::KDF> {
1✔
1283
            client_custom_kdf_called = true;
4✔
1284
            return std::make_unique<CustomKDF>();
4✔
1285
         });
1286

1287
         test.set_server_custom_kdf_callback([&](std::string_view) -> std::unique_ptr<Botan::KDF> {
1✔
1288
            server_custom_kdf_called = true;
4✔
1289
            return std::make_unique<CustomKDF>();
4✔
1290
         });
1291

1292
         test.go();
1✔
1293

1294
         test.results().test_is_true("custom KDF was used on client side", client_custom_kdf_called);
1✔
1295
         test.results().test_is_true("custom KDF was used on server side", server_custom_kdf_called);
1✔
1296
         results.push_back(test.results());
1✔
1297
      }
3✔
1298

1299
   public:
1300
      std::vector<Test::Result> run() override {
1✔
1301
         std::vector<Test::Result> results;
1✔
1302

1303
         auto rng = Test::new_shared_rng(this->test_name());
1✔
1304

1305
         auto creds = create_creds(*rng);
1✔
1306
         if(!creds) {
1✔
1307
            // Credentials manager creation failed, likely no EC group available
1308
            // Skip this test entirely
1309
            return {Test::Result::Note("TLS unit tests", "Skipping due to missing credentials")};
×
1310
         }
1311

1312
   #if defined(BOTAN_HAS_TLS_CBC)
1313
         for(const bool etm_setting : {false, true}) {
3✔
1314
            test_legacy_versions("AES-128 RSA", results, creds, rng, "RSA", "AES-128", "SHA-256 SHA-1", etm_setting);
4✔
1315
            test_legacy_versions("AES-128 ECDH", results, creds, rng, "ECDH", "AES-128", "SHA-256 SHA-1", etm_setting);
4✔
1316

1317
      #if defined(BOTAN_HAS_DES)
1318
            test_legacy_versions("3DES RSA", results, creds, rng, "RSA", "3DES", "SHA-1", etm_setting);
4✔
1319
            test_legacy_versions("3DES ECDH", results, creds, rng, "ECDH", "3DES", "SHA-1", etm_setting);
4✔
1320
      #endif
1321
         }
1322

1323
         test_legacy_versions("AES-128 DH", results, creds, rng, "DH", "AES-128", "SHA-256");
2✔
1324

1325
   #endif
1326

1327
   #if defined(BOTAN_HAS_TLS_NULL)
1328
         test_legacy_versions("NULL PSK", results, creds, rng, "PSK", "NULL", "SHA-256");
2✔
1329
   #endif
1330

1331
         auto strict_policy = std::make_shared<Botan::TLS::Strict_Policy>();
1✔
1332
         test_with_policy("Strict policy", results, creds, available_versions(), strict_policy, rng);
2✔
1333

1334
   #if defined(BOTAN_HAS_TLS_12)
1335
         auto suiteb_128 = std::make_shared<Botan::TLS::NSA_Suite_B_128>();
1✔
1336
         test_with_policy("Suite B", results, creds, {Botan::TLS::Protocol_Version::TLS_V12}, suiteb_128, rng);
2✔
1337
   #endif
1338

1339
         test_legacy_versions("AES-128/GCM RSA", results, creds, rng, "RSA", "AES-128/GCM");
2✔
1340
         test_modern_versions("AES-128/GCM ECDH", results, creds, rng, "ECDH", "AES-128/GCM");
2✔
1341

1342
         test_modern_versions(
3✔
1343
            "AES-128/GCM ECDH RSA", results, creds, rng, "ECDH", "AES-128/GCM", "AEAD", {{"signature_methods", "RSA"}});
1344

1345
         test_modern_versions("AES-128/GCM ECDH no OCSP",
3✔
1346
                              results,
1347
                              creds,
1348
                              rng,
1349
                              "ECDH",
1350
                              "AES-128/GCM",
1351
                              "AEAD",
1352
                              {{"support_cert_status_message", "false"}});
1353

1354
   #if defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_AEAD_GCM)
1355
         test_legacy_versions("Camellia-128/GCM ECDH", results, creds, rng, "ECDH", "Camellia-128/GCM", "AEAD");
2✔
1356
   #endif
1357

1358
   #if defined(BOTAN_HAS_ARIA)
1359
         test_legacy_versions("ARIA/GCM ECDH", results, creds, rng, "ECDH", "ARIA-128/GCM", "AEAD");
2✔
1360
   #endif
1361

1362
         // Note: TLS 1.3 actually does not support point compression and will
1363
         //       simply ignore this policy configuration. Nevertheless, the
1364
         //       handshake should complete successfully.
1365
         test_modern_versions("AES-128/GCM point compression",
3✔
1366
                              results,
1367
                              creds,
1368
                              rng,
1369
                              "ECDH",
1370
                              "AES-128/GCM",
1371
                              "AEAD",
1372
                              {{"use_ecc_point_compression", "true"}});
1373
         test_modern_versions(
3✔
1374
            "AES-256/GCM p521", results, creds, rng, "ECDH", "AES-256/GCM", "AEAD", {{"groups", "secp521r1"}});
1375

1376
         if(Botan::EC_Group::supports_named_group("brainpool256r1")) {
1✔
1377
            test_modern_versions("AES-128/GCM bp256r1",
3✔
1378
                                 results,
1379
                                 creds,
1380
                                 rng,
1381
                                 "ECDH",
1382
                                 "AES-128/GCM",
1383
                                 "AEAD",
1384
                                 {{"groups", "brainpool256r1"}});
1385
         }
1386

1387
   #if defined(BOTAN_HAS_X25519)
1388
         test_modern_versions("AES-128/GCM x25519", results, creds, rng, "ECDH", "AES-128/GCM", "AEAD", {{
3✔
1389
                                 "groups",
1390
                                 "x25519"
1391
                              }});
1392
   #endif
1393

1394
         test_modern_versions("AES-128/GCM FFDHE-2048",
3✔
1395
                              results,
1396
                              creds,
1397
                              rng,
1398
                              "DH",
1399
                              "AES-128/GCM",
1400
                              "AEAD",
1401
                              {{"groups", "ffdhe/ietf/2048"}});
1402

1403
         test_modern_versions("AES-128/GCM with record padding",
3✔
1404
                              results,
1405
                              creds,
1406
                              rng,
1407
                              "ECDH",
1408
                              "AES-128/GCM",
1409
                              "AEAD",
1410
                              {{"minimum_record_size", "2048"}});
1411

1412
         auto creds_with_client_cert = create_creds(*rng, true);
1✔
1413
         if(creds_with_client_cert) {
1✔
1414
            test_modern_versions(
2✔
1415
               "AES-256/GCM client certs", results, creds_with_client_cert, rng, "ECDH", "AES-256/GCM", "AEAD", true);
1416
         }
1417

1418
   #if defined(BOTAN_HAS_AEAD_OCB)
1419
         test_legacy_versions("AES-256/OCB ECDH", results, creds, rng, "ECDH", "AES-256/OCB(12)");
2✔
1420
   #endif
1421

1422
   #if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)
1423
         test_modern_versions("ChaCha20Poly1305 ECDH", results, creds, rng, "ECDH", "ChaCha20Poly1305");
2✔
1424
   #endif
1425

1426
         test_modern_versions("AES-128/GCM PSK", results, creds, rng, "PSK", "AES-128/GCM");
2✔
1427

1428
   #if defined(BOTAN_HAS_AEAD_CCM)
1429
         test_modern_versions("AES-128/CCM PSK", results, creds, rng, "PSK", "AES-128/CCM");
2✔
1430
         test_modern_versions("AES-128/CCM-8 PSK", results, creds, rng, "PSK", "AES-128/CCM(8)");
2✔
1431
   #endif
1432

1433
         test_modern_versions("AES-128/GCM ECDHE_PSK", results, creds, rng, "ECDHE_PSK", "AES-128/GCM");
2✔
1434

1435
         // Test with a custom curve
1436

1437
         /*
1438
         * Disabled, currently broken.
1439
         *
1440
         * TLS 1.2 server side currently can't negotiate application-specific group codes.
1441
         *
1442
         * The current flow is that choose_ciphersuite computes have_shared_ecc_curve via
1443
         * Client_Hello::supported_ecc_curves(), which goes through Supported_Groups::ec_groups()
1444
         * and filters by Group_Params::is_pure_ecc_group(). That predicate only accepts a hardcoded
1445
         * set of known groups, so a custom code such as 0xFEE1 is silently dropped and the server
1446
         * throws "Can't agree on a ciphersuite with client".
1447
         */
1448
         const bool disabled = true;
1✔
1449
         if(Botan::EC_Group::supports_application_specific_group() && !disabled) {
1✔
1450
            /*
1451
            * First register a curve, in this case numsp256d1
1452
            */
1453
            const Botan::BigInt p("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43");
1454
            const Botan::BigInt a("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40");
1455
            const Botan::BigInt b("0x25581");
1456
            const Botan::BigInt order("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE43C8275EA265C6020AB20294751A825");
1457

1458
            const Botan::BigInt g_x("0x01");
1459
            const Botan::BigInt g_y("0x696F1853C1E466D7FC82C96CCEEEDD6BD02C2F9375894EC10BF46306C2B56C77");
1460

1461
            const Botan::OID oid("1.3.6.1.4.1.25258.4.1");
1462

1463
            Botan::OID::register_oid(oid, "numsp256d1");
1464

1465
            // Creating this object implicitly registers the curve for future use ...
1466
            const Botan::EC_Group reg_numsp256d1(oid, p, a, b, g_x, g_y, order);
1467

1468
            test_modern_versions("AES-256/GCM numsp256d1",
1469
                                 results,
1470
                                 creds,
1471
                                 rng,
1472
                                 "ECDH",
1473
                                 "AES-256/GCM",
1474
                                 "AEAD",
1475
                                 {{"groups", "0xFEE1"}, {"minimum_ecdh_group_size", "112"}});
1476
         }
1477

1478
         // Test connection abort by the application
1479
         // by throwing in Callbacks::tls_session_established()
1480

1481
         test_session_established_abort(results, creds, rng);
2✔
1482

1483
         // Use tls12_generate_ephemeral_ecdh_key() / tls_generate_ephemeral_key() to
1484
         // establish a custom ECDH provider mocking some hardware adapter for key
1485
         // generation and tls_ephemeral_key_agreement() for performing the key agreement.
1486

1487
         test_custom_ecdh_provider(results, creds, rng);
1✔
1488

1489
         // Test using a custom KDF instead of the original TLS 1.2 KDF
1490
         // (this is a TLS 1.2 specific feature)
1491

1492
   #if defined(BOTAN_HAS_TLS_12)
1493
         test_custom_kdf_provider(results, creds, rng);
1✔
1494
   #endif
1495

1496
         return results;
1✔
1497
      }
13✔
1498
};
1499

1500
BOTAN_REGISTER_TEST("tls", "unit_tls", TLS_Unit_Tests);
1501

1502
#endif
1503

1504
}  // namespace
1505

1506
}  // namespace Botan_Tests
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc