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

randombit / botan / 21943010187

12 Feb 2026 10:33AM UTC coverage: 90.061% (-0.006%) from 90.067%
21943010187

Pull #5318

github

web-flow
Merge 005a803db into f97d7db3f
Pull Request #5318: Allow disabling TLS 1.2 at Build Time

102245 of 113528 relevant lines covered (90.06%)

11733046.67 hits per line

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

91.67
/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
*
8
* Botan is released under the Simplified BSD License (see license.txt)
9
*/
10

11
#include "tests.h"
12
#include <memory>
13
#include <vector>
14

15
#if defined(BOTAN_HAS_TLS)
16

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

31
   #include <botan/ec_group.h>
32
   #include <botan/ecdh.h>
33
   #include <botan/ecdsa.h>
34
   #include <botan/hex.h>
35
   #include <botan/pkcs10.h>
36
   #include <botan/rsa.h>
37
   #include <botan/x509_ca.h>
38
   #include <botan/x509self.h>
39

40
   #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
41
      #include <botan/tls_session_manager_sqlite.h>
42
   #endif
43

44
namespace Botan::TLS {
45

46
// TODO: remove this, once TLS 1.3 is fully implemented
47
class Strict_Policy_Without_TLS13 : public Strict_Policy {
1✔
48
      bool allow_tls13() const override { return false; }
4✔
49
};
50

51
}  // namespace Botan::TLS
52

53
#endif
54

55
namespace Botan_Tests {
56

57
namespace {
58

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

84
         m_stores.push_back(std::move(store));
2✔
85
      }
2✔
86

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

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

105
         if(m_acceptable_cas.empty()) {
335✔
106
            m_acceptable_cas = acceptable_CAs;
12✔
107
         }
108

109
         if(type == "tls-server" || (type == "tls-client" && m_provides_client_certs)) {
335✔
110
            for(const auto& key_type : cert_key_types) {
255✔
111
               if(key_type == "RSA") {
255✔
112
                  chain.push_back(m_rsa_cert);
129✔
113
                  chain.push_back(m_rsa_ca);
129✔
114
                  break;
115
               } else if(key_type == "ECDSA") {
126✔
116
                  chain.push_back(m_ecdsa_cert);
126✔
117
                  chain.push_back(m_ecdsa_ca);
126✔
118
                  break;
119
               }
120
            }
121
         }
122

123
         return chain;
335✔
124
      }
×
125

126
      std::shared_ptr<Botan::Private_Key> private_key_for(const Botan::X509_Certificate& crt,
105✔
127
                                                          const std::string& /*type*/,
128
                                                          const std::string& /*context*/) override {
129
         if(crt == m_rsa_cert) {
105✔
130
            return m_rsa_key;
31✔
131
         }
132
         if(crt == m_ecdsa_cert) {
74✔
133
            return m_ecdsa_key;
74✔
134
         }
135
         return nullptr;
×
136
      }
137

138
      Botan::secure_vector<uint8_t> session_ticket_key() override {
×
139
         return Botan::hex_decode_locked("AABBCCDDEEFF012345678012345678");
×
140
      }
141

142
      Botan::secure_vector<uint8_t> dtls_cookie_secret() override {
128✔
143
         return Botan::hex_decode_locked("4AEA5EAD279CADEB537A594DA0E9DE3A");
128✔
144
      }
145

146
      std::vector<Botan::TLS::ExternalPSK> find_preshared_keys(
40✔
147
         std::string_view host,
148
         Botan::TLS::Connection_Side whoami,
149
         const std::vector<std::string>& identities = {},
150
         const std::optional<std::string>& prf = std::nullopt) override {
151
         if(identities.empty()) {
40✔
152
            return find_preshared_keys(host, whoami, identities, prf);
×
153
         }
154

155
         std::vector<Botan::TLS::ExternalPSK> psks;
40✔
156

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

165
            if(whoami == Botan::TLS::Connection_Side::Server) {
40✔
166
               psks.emplace_back(
20✔
167
                  std::string{}, "SHA-256", Botan::hex_decode_locked("20B602D1475F2DF888FCB60D2AE03AFD"));
40✔
168
            }
169
         }
170

171
         return psks;
40✔
172
      }
40✔
173

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

176
   private:
177
      Botan::X509_Certificate m_rsa_cert, m_rsa_ca;
178
      std::shared_ptr<Botan::Private_Key> m_rsa_key;
179

180
      Botan::X509_Certificate m_ecdsa_cert, m_ecdsa_ca;
181
      std::shared_ptr<Botan::Private_Key> m_ecdsa_key;
182

183
      std::vector<std::unique_ptr<Botan::Certificate_Store>> m_stores;
184
      bool m_provides_client_certs;
185
      std::vector<Botan::X509_DN> m_acceptable_cas;
186
};
187

188
std::shared_ptr<Credentials_Manager_Test> create_creds(Botan::RandomNumberGenerator& rng,
2✔
189
                                                       bool with_client_certs = false) {
190
   // RSA and ECDSA are required for the TLS module, but we need to find an
191
   // ECC group that is supported in this build or skip this test.
192
   const auto ec_group = Test::supported_ec_group_name({"secp256r1", "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

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

278
         const bool is_dtls = offer_version.is_datagram_protocol();
135✔
279

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

284
      void go();
285

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

288
      Test::Result& results() { return m_results; }
144✔
289

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

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

302
      void set_custom_client_tls_generate_ephemeral_ecdh_key_callback(generate_ephemeral_ecdh_key_clbk clbk) {
3✔
303
         BOTAN_ASSERT_NONNULL(m_client_cb);
3✔
304
         m_client_cb->set_custom_client_tls_generate_ephemeral_ecdh_key_callback(std::move(clbk));
6✔
305
      }
3✔
306

307
      void set_custom_client_tls_ephemeral_key_agreement_callback(ephemeral_key_agreement_clbk clbk) {
3✔
308
         BOTAN_ASSERT_NONNULL(m_client_cb);
3✔
309
         m_client_cb->set_custom_client_tls_ephemeral_key_agreement_callback(std::move(clbk));
6✔
310
      }
3✔
311

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

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

322
   private:
323
      class Test_Extension : public Botan::TLS::Extension {
324
         public:
325
            static Botan::TLS::Extension_Code static_type() {
326
               // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
327
               return static_cast<Botan::TLS::Extension_Code>(666);
328
            }
329

330
            Botan::TLS::Extension_Code type() const override { return static_type(); }
1,910✔
331

332
            std::vector<uint8_t> serialize(Botan::TLS::Connection_Side /*whoami*/) const override { return m_buf; }
330✔
333

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

336
            bool empty() const override { return false; }
330✔
337

338
            explicit Test_Extension(Botan::TLS::Connection_Side side) {
266✔
339
               const uint8_t client_extn[6] = {'c', 'l', 'i', 'e', 'n', 't'};
266✔
340
               const uint8_t server_extn[6] = {'s', 'e', 'r', 'v', 'e', 'r'};
266✔
341

342
               Botan::TLS::append_tls_length_value(
266✔
343
                  m_buf, (side == Botan::TLS::Connection_Side::Client) ? client_extn : server_extn, 6, 1);
266✔
344
            }
266✔
345

346
         private:
347
            std::vector<uint8_t> m_buf;
348
      };
349

350
      class Test_Callbacks final : public Botan::TLS::Callbacks {
351
         public:
352
            Test_Callbacks(Test::Result& results,
270✔
353
                           Botan::TLS::Protocol_Version expected_version,
354
                           std::vector<uint8_t>& outbound,
355
                           std::vector<uint8_t>& recv_buf) :
270✔
356
                  m_results(results), m_expected_version(expected_version), m_outbound(outbound), m_recv(recv_buf) {}
270✔
357

358
            Test_Callbacks(Test_Callbacks&&) = delete;
359
            Test_Callbacks(const Test_Callbacks&) = delete;
360
            Test_Callbacks& operator=(const Test_Callbacks&) = delete;
361
            Test_Callbacks& operator=(Test_Callbacks&&) = delete;
362

363
            ~Test_Callbacks() override {
270✔
364
               if(m_expected_handshake_alert.has_value()) {
270✔
365
                  m_results.test_failure("Expected: " + m_expected_handshake_alert->type_string() +
×
366
                                         " during handshake");
367
               }
368
            }
276✔
369

370
            void tls_emit_data(std::span<const uint8_t> bits) override {
2,419✔
371
               m_outbound.insert(m_outbound.end(), bits.begin(), bits.end());
2,419✔
372
            }
2,419✔
373

374
            void tls_record_received(uint64_t /*seq*/, std::span<const uint8_t> bits) override {
388✔
375
               m_recv.insert(m_recv.end(), bits.begin(), bits.end());
388✔
376
            }
388✔
377

378
            void tls_alert(Botan::TLS::Alert alert) override {
492✔
379
               // TODO test that it is a no_renegotiation alert
380

381
               if(m_expected_handshake_alert.has_value()) {
492✔
382
                  if(m_expected_handshake_alert->type() != alert.type()) {
12✔
383
                     m_results.test_failure("Got unexpected alert: " + alert.type_string() +
×
384
                                            " expected: " + m_expected_handshake_alert->type_string());
×
385
                  } else {
386
                     // acknowledge that the expected Alert was detected
387
                     m_results.test_note("saw expected alert: " + m_expected_handshake_alert->type_string());
36✔
388
                     m_expected_handshake_alert.reset();
12✔
389
                  }
390
               }
391
            }
492✔
392

393
            void tls_modify_extensions(Botan::TLS::Extensions& extn,
266✔
394
                                       Botan::TLS::Connection_Side which_side,
395
                                       Botan::TLS::Handshake_Type /* unused */) override {
396
               extn.add(new Test_Extension(which_side));  // NOLINT(*-owning-memory)
266✔
397

398
               // Insert an unsupported signature scheme as highest prio, to ensure we are tolerant of this
399
               if(auto sig_algs = extn.take<Botan::TLS::Signature_Algorithms>()) {
266✔
400
                  std::vector<Botan::TLS::Signature_Scheme> schemes = sig_algs->supported_schemes();
135✔
401
                  // 0x0301 is RSA PKCS1/SHA-224, which is not supported anymore
402
                  schemes.insert(schemes.begin(), 0x0301);
135✔
403
                  // This replaces the previous extension value
404
                  extn.add(new Botan::TLS::Signature_Algorithms(schemes));  // NOLINT(*-owning-memory)
135✔
405
               }
135✔
406
            }
266✔
407

408
            void tls_examine_extensions(const Botan::TLS::Extensions& extn,
266✔
409
                                        Botan::TLS::Connection_Side which_side,
410
                                        Botan::TLS::Handshake_Type /*unused*/) override {
411
               // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
412
               const auto extn_id = static_cast<Botan::TLS::Extension_Code>(666);
266✔
413
               Botan::TLS::Extension* test_extn = extn.get(extn_id);
266✔
414

415
               if(test_extn == nullptr) {
266✔
416
                  m_results.test_failure("Did not receive test extension from peer");
×
417
               } else {
418
                  Botan::TLS::Unknown_Extension* unknown_ext = dynamic_cast<Botan::TLS::Unknown_Extension*>(test_extn);
266✔
419

420
                  if(unknown_ext != nullptr) {
266✔
421
                     const std::vector<uint8_t> val = unknown_ext->value();
266✔
422

423
                     if(m_results.test_eq("Expected size for test extn", val.size(), 7)) {
266✔
424
                        if(which_side == Botan::TLS::Connection_Side::Client) {
266✔
425
                           m_results.test_eq("Expected extension value", val, "06636C69656E74");
270✔
426
                        } else {
427
                           m_results.test_eq("Expected extension value", val, "06736572766572");
262✔
428
                        }
429
                     }
430
                  } else {
266✔
431
                     m_results.test_failure("Unknown extension type had unexpected type at runtime");
×
432
                  }
433
               }
434
            }
266✔
435

436
            void tls_session_established(const Botan::TLS::Session_Summary& session) override {
256✔
437
               const std::string session_report = "Session established " + session.version().to_string() + " " +
768✔
438
                                                  session.ciphersuite().to_string() + " " +
768✔
439
                                                  Botan::hex_encode(session.session_id().get());
512✔
440

441
               m_results.test_note(session_report);
256✔
442

443
               if(m_session_established_callback) {
256✔
444
                  m_session_established_callback(session);
12✔
445
               }
446

447
               if(session.version() != m_expected_version) {
244✔
448
                  m_results.test_failure("Expected " + m_expected_version.to_string() + " negotiated " +
×
449
                                         session.version().to_string());
12✔
450
               }
451
            }
244✔
452

453
            std::string tls_server_choose_app_protocol(const std::vector<std::string>& protos) override {
135✔
454
               m_results.test_eq("ALPN protocol count", protos.size(), 2);
135✔
455
               m_results.test_eq("ALPN protocol 1", protos[0], "test/1");
270✔
456
               m_results.test_eq("ALPN protocol 2", protos[1], "test/2");
270✔
457
               return "test/3";
135✔
458
            }
459

460
            std::unique_ptr<Botan::PK_Key_Agreement_Key> tls_generate_ephemeral_key(
171✔
461
               const std::variant<Botan::TLS::Group_Params, Botan::DL_Group>& group,
462
               Botan::RandomNumberGenerator& rng) override {
463
               if(std::holds_alternative<Botan::TLS::Group_Params>(group) &&
171✔
464
                  std::get<Botan::TLS::Group_Params>(group).wire_code() == 0xFEE1) {
166✔
465
                  const auto ec_group = Botan::EC_Group::from_name("numsp256d1");
×
466
                  return std::make_unique<Botan::ECDH_PrivateKey>(rng, ec_group);
×
467
               }
×
468

469
               return Botan::TLS::Callbacks::tls_generate_ephemeral_key(group, rng);
171✔
470
            }
471

472
            std::unique_ptr<Botan::PK_Key_Agreement_Key> tls12_generate_ephemeral_ecdh_key(
26✔
473
               Botan::TLS::Group_Params group,
474
               Botan::RandomNumberGenerator& rng,
475
               Botan::EC_Point_Format tls12_ecc_pubkey_encoding_format) override {
476
               if(m_generate_ephemeral_ecdh_key_callback) {
26✔
477
                  return m_generate_ephemeral_ecdh_key_callback(group, rng, tls12_ecc_pubkey_encoding_format);
3✔
478
               }
479

480
               return Botan::TLS::Callbacks::tls12_generate_ephemeral_ecdh_key(
23✔
481
                  group, rng, tls12_ecc_pubkey_encoding_format);
23✔
482
            }
483

484
            Botan::secure_vector<uint8_t> tls_ephemeral_key_agreement(
174✔
485
               const std::variant<Botan::TLS::Group_Params, Botan::DL_Group>& group,
486
               const Botan::PK_Key_Agreement_Key& private_key,
487
               const std::vector<uint8_t>& public_value,
488
               Botan::RandomNumberGenerator& rng,
489
               const Botan::TLS::Policy& policy) override {
490
               if(m_ephemeral_key_agreement_callback) {
174✔
491
                  return m_ephemeral_key_agreement_callback(group, private_key, public_value, rng, policy);
3✔
492
               }
493

494
               if(std::holds_alternative<Botan::TLS::Group_Params>(group) &&
171✔
495
                  std::get<Botan::TLS::Group_Params>(group).wire_code() == 0xFEE1) {
166✔
496
                  const auto ec_group = Botan::EC_Group::from_name("numsp256d1");
×
497
                  const auto ec_point = Botan::EC_AffinePoint(ec_group, public_value);
×
498
                  const Botan::ECDH_PublicKey peer_key(ec_group, ec_point);
×
499
                  const Botan::PK_Key_Agreement ka(private_key, rng, "Raw");
×
500
                  return ka.derive_key(0, peer_key.public_value()).bits_of();
×
501
               }
×
502

503
               return Botan::TLS::Callbacks::tls_ephemeral_key_agreement(group, private_key, public_value, rng, policy);
171✔
504
            }
505

506
            void set_custom_tls_session_established_callback(
12✔
507
               std::function<void(const Botan::TLS::Session_Summary&)> clbk) {
508
               m_session_established_callback = std::move(clbk);
12✔
509
            }
510

511
            void set_custom_client_tls_generate_ephemeral_ecdh_key_callback(generate_ephemeral_ecdh_key_clbk clbk) {
3✔
512
               m_generate_ephemeral_ecdh_key_callback = std::move(clbk);
3✔
513
            }
514

515
            void set_custom_client_tls_ephemeral_key_agreement_callback(ephemeral_key_agreement_clbk clbk) {
3✔
516
               m_ephemeral_key_agreement_callback = std::move(clbk);
3✔
517
            }
518

519
            void set_expected_handshake_alert(Botan::TLS::Alert alert) { m_expected_handshake_alert = alert; }
12✔
520

521
         private:
522
            Test::Result& m_results;
523
            const Botan::TLS::Protocol_Version m_expected_version;
524
            std::vector<uint8_t>& m_outbound;
525
            std::vector<uint8_t>& m_recv;
526

527
            std::function<void(const Botan::TLS::Session_Summary&)> m_session_established_callback;
528
            generate_ephemeral_ecdh_key_clbk m_generate_ephemeral_ecdh_key_callback;
529
            ephemeral_key_agreement_clbk m_ephemeral_key_agreement_callback;
530
            std::optional<Botan::TLS::Alert> m_expected_handshake_alert;
531
      };
532

533
      const Botan::TLS::Protocol_Version m_offer_version;
534
      Test::Result m_results;
535

536
      std::shared_ptr<Credentials_Manager_Test> m_creds;
537
      std::shared_ptr<const Botan::TLS::Policy> m_client_policy;
538
      std::shared_ptr<Botan::TLS::Session_Manager> m_client_sessions;
539
      std::shared_ptr<Botan::RandomNumberGenerator> m_rng;
540

541
      std::shared_ptr<Test_Callbacks> m_client_cb;
542

543
      std::shared_ptr<Test_Callbacks> m_server_cb;
544
      std::unique_ptr<Botan::TLS::Server> m_server;
545

546
      const bool m_client_auth;
547

548
      std::vector<uint8_t> m_c2s, m_s2c, m_client_recv, m_server_recv;
549
};
550

551
void TLS_Handshake_Test::go() {
135✔
552
   m_results.start_timer();
135✔
553

554
   const std::vector<std::string> protocols_offered = {"test/1", "test/2"};
135✔
555

556
   // Choose random application data to send
557
   const size_t c_len = 1 + ((static_cast<size_t>(m_rng->next_byte()) << 4) ^ m_rng->next_byte());
135✔
558
   std::vector<uint8_t> client_msg(c_len);
135✔
559
   m_rng->randomize(client_msg.data(), client_msg.size());
135✔
560
   bool client_has_written = false;
135✔
561

562
   const size_t s_len = 1 + ((static_cast<size_t>(m_rng->next_byte()) << 4) ^ m_rng->next_byte());
135✔
563
   std::vector<uint8_t> server_msg(s_len);
135✔
564
   m_rng->randomize(server_msg.data(), server_msg.size());
135✔
565
   bool server_has_written = false;
135✔
566

567
   std::unique_ptr<Botan::TLS::Client> client;
135✔
568
   client = std::make_unique<Botan::TLS::Client>(m_client_cb,
135✔
569
                                                 m_client_sessions,
135✔
570
                                                 m_creds,
135✔
571
                                                 m_client_policy,
135✔
572
                                                 m_rng,
135✔
573
                                                 Botan::TLS::Server_Information("server.example.com"),
135✔
574
                                                 m_offer_version,
135✔
575
                                                 protocols_offered);
135✔
576

577
   size_t rounds = 0;
135✔
578

579
   bool client_handshake_completed = false;
135✔
580
   bool server_handshake_completed = false;
135✔
581

582
   while(true) {
1,268✔
583
      ++rounds;
1,268✔
584

585
      if(rounds > 25) {
1,268✔
586
         m_results.test_failure("Still here after many rounds, deadlock?");
×
587
         break;
×
588
      }
589

590
      if(!client_handshake_completed && client->is_active()) {
1,268✔
591
         client_handshake_completed = true;
592
      }
593

594
      if(!server_handshake_completed && m_server->is_active()) {
1,268✔
595
         server_handshake_completed = true;
596
      }
597

598
      if(client->is_active() && !client_has_written) {
1,268✔
599
         m_results.test_eq("client ALPN protocol", client->application_protocol(), "test/3");
238✔
600

601
         size_t sent_so_far = 0;
119✔
602
         while(sent_so_far != client_msg.size()) {
309✔
603
            const size_t left = client_msg.size() - sent_so_far;
190✔
604
            const size_t rnd12 = (m_rng->next_byte() << 4) ^ m_rng->next_byte();
190✔
605
            const size_t sending = std::min(left, rnd12);
190✔
606

607
            client->send(&client_msg[sent_so_far], sending);
190✔
608
            sent_so_far += sending;
190✔
609
         }
610
         client->send_warning_alert(Botan::TLS::Alert::NoRenegotiation);
119✔
611
         client_has_written = true;
612
      }
613

614
      if(m_server->is_active() && !server_has_written) {
1,268✔
615
         m_results.test_eq("server ALPN protocol", m_server->application_protocol(), "test/3");
250✔
616

617
         size_t sent_so_far = 0;
125✔
618
         while(sent_so_far != server_msg.size()) {
332✔
619
            const size_t left = server_msg.size() - sent_so_far;
207✔
620
            const size_t rnd12 = (m_rng->next_byte() << 4) ^ m_rng->next_byte();
207✔
621
            const size_t sending = std::min(left, rnd12);
207✔
622

623
            m_server->send(&server_msg[sent_so_far], sending);
207✔
624
            sent_so_far += sending;
207✔
625
         }
626

627
         m_server->send_warning_alert(Botan::TLS::Alert::NoRenegotiation);
125✔
628
         server_has_written = true;
629
      }
630

631
      if(!m_c2s.empty()) {
1,268✔
632
         /*
633
         * Use this as a temp value to hold the queues as otherwise they
634
         * might end up appending more in response to messages during the
635
         * handshake.
636
         */
637
         std::vector<uint8_t> input;
565✔
638
         std::swap(m_c2s, input);
565✔
639

640
         try {
565✔
641
            const size_t needed = m_server->received_data(input.data(), input.size());
565✔
642
            m_results.test_eq("full packet received", needed, 0);
1,110✔
643
         } catch(...) { /* ignore exceptions */
10✔
644
         }
10✔
645

646
         continue;
565✔
647
      }
565✔
648

649
      if(!m_s2c.empty()) {
703✔
650
         std::vector<uint8_t> input;
449✔
651
         std::swap(m_s2c, input);
449✔
652

653
         try {
449✔
654
            const size_t needed = client->received_data(input.data(), input.size());
449✔
655
            m_results.test_eq("full packet received", needed, 0);
886✔
656
         } catch(...) { /* ignore exceptions */
6✔
657
         }
6✔
658

659
         continue;
449✔
660
      }
449✔
661

662
      if(!m_client_recv.empty()) {
254✔
663
         m_results.test_eq("client recv", m_client_recv, server_msg);
476✔
664
      }
665

666
      if(!m_server_recv.empty()) {
254✔
667
         m_results.test_eq("server recv", m_server_recv, client_msg);
476✔
668
      }
669

670
      if(client->is_closed() && m_server->is_closed()) {
254✔
671
         break;
672
      }
673

674
      if(m_server->is_active()) {
119✔
675
         const std::vector<Botan::X509_Certificate> certs = m_server->peer_cert_chain();
119✔
676
         if(m_client_auth) {
119✔
677
            m_results.test_eq("got client certs", certs.size(), 2);
4✔
678

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

681
            m_results.test_eq("client got CA list", acceptable_CAs.size(), 2);  // RSA + ECDSA
4✔
682

683
            for(const Botan::X509_DN& dn : acceptable_CAs) {
12✔
684
               m_results.test_eq("Expected CA country field", dn.get_first_attribute("C"), "VT");
16✔
685
            }
686
         } else {
4✔
687
            m_results.test_eq("no client certs", certs.size(), 0);
230✔
688
         }
689
      }
119✔
690

691
      if(!m_server_recv.empty() && !m_client_recv.empty()) {
119✔
692
         const Botan::SymmetricKey client_key = client->key_material_export("label", "context", 32);
119✔
693
         const Botan::SymmetricKey server_key = m_server->key_material_export("label", "context", 32);
119✔
694

695
         m_results.test_eq("TLS key material export", client_key.bits_of(), server_key.bits_of());
357✔
696

697
         m_results.confirm("Client is active", client->is_active());
238✔
698
         m_results.confirm("Client is not closed", !client->is_closed());
238✔
699
         client->close();
119✔
700
         m_results.confirm("Client is no longer active", !client->is_active());
238✔
701
         m_results.confirm("Client is closed", client->is_closed());
238✔
702
      }
238✔
703
   }
704

705
   m_results.end_timer();
135✔
706
}
405✔
707

708
class Test_Policy final : public Botan::TLS::Text_Policy {
33✔
709
   public:
710
      Test_Policy() : Text_Policy("") {}
33✔
711

712
      bool acceptable_protocol_version(Botan::TLS::Protocol_Version version) const override {
631✔
713
         // TODO: handle TLS 1.3 server once the time is ripe.
714
         return version.is_pre_tls_13();
631✔
715
      }
716

717
      size_t dtls_initial_timeout() const override { return 1; }
128✔
718

719
      size_t dtls_maximum_timeout() const override { return 8; }
128✔
720

721
      size_t minimum_rsa_bits() const override { return 1024; }
12✔
722

723
      size_t minimum_signature_strength() const override { return 80; }
101✔
724
};
725

726
/**
727
 * This mocks a custom ECDH adapter class that generates ephemeral keys and
728
 * performs key agreement in a single operation (see the member
729
 * `custom_ephemeral_agreement()` in this class).
730
 *
731
 * In Botan 2.x, this mode of operation was implemented as an explicit callback,
732
 * namely `tls_ecdh_agree()` that was explicitly only useful for TLS 1.2
733
 * clients. While implementing TLS 1.3 in Botan3, this functionality was
734
 * reworked to be more flexible [1], but it broke this particular use case by
735
 * making too strong assumptions on the custom keypair adapter type obtained
736
 * from `tls_generate_ephemeral_key()`: It had to be derived from
737
 * `ECDH_PublicKey`.
738
 *
739
 * While this serves as a regression test for this particular use case, the
740
 * issue of too-strong assumptions on user-defined adapter types is more general
741
 * and should be covered by this test case as well.
742
 *
743
 * [1] https://github.com/randombit/botan/pull/3322
744
 */
745
class HardwareEcdhKey final : public Botan::PK_Key_Agreement_Key {
×
746
   public:
747
      HardwareEcdhKey(Botan::EC_Group group, Botan::EC_Point_Format public_key_format) :
3✔
748
            m_group(std::move(group)), m_public_key_format(public_key_format) {}
3✔
749

750
      std::string algo_name() const override { return "ECDH"; }
×
751

752
      size_t estimated_strength() const override { return m_group.get_p().bits(); }
×
753

754
      bool supports_operation(Botan::PublicKeyOperation op) const override {
×
755
         return op == Botan::PublicKeyOperation::KeyAgreement;
×
756
      }
757

758
      bool check_key(Botan::RandomNumberGenerator& /*rng*/, bool /*strong*/) const override { return true; }
×
759

760
      size_t key_length() const override { return m_group.get_p().bits() / 2; }
×
761

762
      Botan::AlgorithmIdentifier algorithm_identifier() const override { return {}; }
×
763

764
      std::vector<uint8_t> raw_public_key_bits() const override {
3✔
765
         if(m_public_value.empty()) {
3✔
766
            throw Botan::Invalid_State("Public key bits are not available");
×
767
         }
768
         return m_public_value;
3✔
769
      }
770

771
      std::vector<uint8_t> public_key_bits() const override { return raw_public_key_bits(); }
×
772

773
      Botan::secure_vector<uint8_t> private_key_bits() const override {
×
774
         throw Botan::Not_Implemented("This mocks a hardware key and thus hides its private bits");
×
775
      }
776

777
      std::unique_ptr<Botan::Public_Key> public_key() const override {
×
778
         return std::make_unique<Botan::ECDH_PublicKey>(m_group, Botan::EC_AffinePoint(m_group, raw_public_key_bits()));
×
779
      }
780

781
      std::unique_ptr<Botan::Private_Key> generate_another(Botan::RandomNumberGenerator& /*rng*/) const override {
×
782
         return std::make_unique<HardwareEcdhKey>(m_group, m_public_key_format);
×
783
      }
784

785
      std::vector<uint8_t> public_value() const override { return raw_public_key_bits(); }
×
786

787
      Botan::secure_vector<uint8_t> custom_ephemeral_agreement(std::span<const uint8_t> peer_public_key,
3✔
788
                                                               Botan::RandomNumberGenerator& rng) const {
789
         // This is meant to mock an imaginary "ECDH hardware" that generates an
790
         // ephemeral ECDH key, performs key agreement with the peer's public
791
         // value and outputs the corresponding shared secret. Our public key is
792
         // stored in this wrapper class' state.
793
         auto ephemeral_key = Botan::ECDH_PrivateKey(rng, m_group);
3✔
794
         const Botan::PK_Key_Agreement ka(ephemeral_key, rng, "Raw");
3✔
795
         auto shared_secret = ka.derive_key(0, peer_public_key).bits_of();
6✔
796
         m_public_value = ephemeral_key.public_value(m_public_key_format);
3✔
797
         return shared_secret;
3✔
798
      }
3✔
799

800
   private:
801
      Botan::EC_Group m_group;
802
      Botan::EC_Point_Format m_public_key_format;
803
      mutable std::vector<uint8_t> m_public_value;
804
};
805

806
class TLS_Unit_Tests final : public Test {
1✔
807
   private:
808
      static void test_with_policy(const std::string& test_descr,
31✔
809
                                   std::vector<Test::Result>& results,
810
                                   const std::shared_ptr<Botan::TLS::Session_Manager>& client_ses,
811
                                   const std::shared_ptr<Botan::TLS::Session_Manager>& server_ses,
812
                                   const std::shared_ptr<Credentials_Manager_Test>& creds,
813
                                   const std::vector<Botan::TLS::Protocol_Version>& versions,
814
                                   const std::shared_ptr<const Botan::TLS::Policy>& policy,
815
                                   std::shared_ptr<Botan::RandomNumberGenerator>& rng,
816
                                   bool client_auth = false) {
817
         try {
31✔
818
            for(const auto& version : versions) {
91✔
819
               TLS_Handshake_Test test(version.to_string() + " " + test_descr,
120✔
820
                                       version,
821
                                       creds,
822
                                       policy,
823
                                       policy,
824
                                       rng,
825
                                       client_ses,
826
                                       server_ses,
827
                                       client_auth);
120✔
828
               test.go();
60✔
829
               results.push_back(test.results());
60✔
830

831
               TLS_Handshake_Test test_resumption(version.to_string() + " " + test_descr,
180✔
832
                                                  version,
833
                                                  creds,
834
                                                  policy,
835
                                                  policy,
836
                                                  rng,
837
                                                  client_ses,
838
                                                  server_ses,
839
                                                  client_auth);
60✔
840
               test_resumption.go();
60✔
841
               results.push_back(test_resumption.results());
60✔
842
            }
60✔
843
         } catch(std::exception& e) {
×
844
            results.push_back(Test::Result::Failure(test_descr, e.what()));
×
845
         }
×
846
      }
31✔
847

848
      static void test_all_versions(const std::string& test_descr,
8✔
849
                                    std::vector<Test::Result>& results,
850
                                    const std::shared_ptr<Botan::TLS::Session_Manager>& client_ses,
851
                                    const std::shared_ptr<Botan::TLS::Session_Manager>& server_ses,
852
                                    const std::shared_ptr<Credentials_Manager_Test>& creds,
853
                                    std::shared_ptr<Botan::RandomNumberGenerator>& rng,
854
                                    const std::string& kex_policy,
855
                                    const std::string& cipher_policy,
856
                                    const std::string& mac_policy,
857
                                    const std::string& etm_policy,
858
                                    bool client_auth = false) {
859
         auto policy = std::make_shared<Test_Policy>();
8✔
860
         policy->set("ciphers", cipher_policy);
8✔
861
         policy->set("macs", mac_policy);
8✔
862
         policy->set("key_exchange_methods", kex_policy);
8✔
863
         policy->set("negotiate_encrypt_then_mac", etm_policy);
8✔
864

865
         policy->set("allow_tls12", "true");
16✔
866
         policy->set("allow_dtls12", "true");
16✔
867

868
         if(kex_policy.find("RSA") != std::string::npos) {
8✔
869
            policy->set("signature_methods", "IMPLICIT");
8✔
870
         }
871

872
         const std::vector<Botan::TLS::Protocol_Version> versions = {Botan::TLS::Protocol_Version::TLS_V12,
8✔
873
                                                                     Botan::TLS::Protocol_Version::DTLS_V12};
8✔
874

875
         return test_with_policy(
8✔
876
            test_descr, results, client_ses, server_ses, creds, versions, policy, rng, client_auth);
8✔
877
      }
16✔
878

879
      static void test_modern_versions(const std::string& test_descr,
13✔
880
                                       std::vector<Test::Result>& results,
881
                                       const std::shared_ptr<Botan::TLS::Session_Manager>& client_ses,
882
                                       const std::shared_ptr<Botan::TLS::Session_Manager>& server_ses,
883
                                       const std::shared_ptr<Credentials_Manager_Test>& creds,
884
                                       std::shared_ptr<Botan::RandomNumberGenerator>& rng,
885
                                       const std::string& kex_policy,
886
                                       const std::string& cipher_policy,
887
                                       const std::string& mac_policy = "AEAD",
888
                                       bool client_auth = false) {
889
         const std::map<std::string, std::string> no_extra_policies;
13✔
890
         return test_modern_versions(test_descr,
13✔
891
                                     results,
892
                                     client_ses,
893
                                     server_ses,
894
                                     creds,
895
                                     rng,
896
                                     kex_policy,
897
                                     cipher_policy,
898
                                     mac_policy,
899
                                     no_extra_policies,
900
                                     client_auth);
13✔
901
      }
13✔
902

903
      static void test_modern_versions(const std::string& test_descr,
21✔
904
                                       std::vector<Test::Result>& results,
905
                                       const std::shared_ptr<Botan::TLS::Session_Manager>& client_ses,
906
                                       const std::shared_ptr<Botan::TLS::Session_Manager>& server_ses,
907
                                       const std::shared_ptr<Credentials_Manager_Test>& creds,
908
                                       std::shared_ptr<Botan::RandomNumberGenerator>& rng,
909
                                       const std::string& kex_policy,
910
                                       const std::string& cipher_policy,
911
                                       const std::string& mac_policy,
912
                                       const std::map<std::string, std::string>& extra_policies,
913
                                       bool client_auth = false) {
914
         auto policy = std::make_shared<Test_Policy>();
21✔
915
         policy->set("ciphers", cipher_policy);
21✔
916
         policy->set("macs", mac_policy);
21✔
917
         policy->set("key_exchange_methods", kex_policy);
21✔
918
         policy->set("allow_tls12", "true");
42✔
919
         policy->set("allow_dtls12", "true");
42✔
920

921
         if(kex_policy.find("RSA") != std::string::npos) {
21✔
922
            policy->set("signature_methods", "IMPLICIT");
2✔
923
         }
924

925
         for(const auto& kv : extra_policies) {
30✔
926
            policy->set(kv.first, kv.second);
9✔
927
         }
928

929
         const std::vector<Botan::TLS::Protocol_Version> versions = {Botan::TLS::Protocol_Version::TLS_V12,
21✔
930
                                                                     Botan::TLS::Protocol_Version::DTLS_V12};
21✔
931

932
         return test_with_policy(
21✔
933
            test_descr, results, client_ses, server_ses, creds, versions, policy, rng, client_auth);
21✔
934
      }
42✔
935

936
      void test_session_established_abort(std::vector<Test::Result>& results,
1✔
937
                                          std::shared_ptr<Credentials_Manager_Test> creds,
938
                                          std::shared_ptr<Botan::RandomNumberGenerator> rng) {
939
         std::vector<Botan::TLS::Protocol_Version> versions = {Botan::TLS::Protocol_Version::TLS_V12,
1✔
940
                                                               Botan::TLS::Protocol_Version::DTLS_V12};
1✔
941

942
         auto policy = std::make_shared<Test_Policy>();
1✔
943
         auto noop_session_manager = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
944

945
         auto client_aborts = [&](const std::exception_ptr& ex, Botan::TLS::Alert expected_server_alert) {
4✔
946
            for(const auto version : versions) {
9✔
947
               TLS_Handshake_Test test("Client aborts in tls_session_established with " +
12✔
948
                                          expected_server_alert.type_string() + ": " + version.to_string(),
24✔
949
                                       version,
950
                                       creds,
951
                                       policy,
952
                                       policy,
953
                                       rng,
954
                                       noop_session_manager,
955
                                       noop_session_manager,
956
                                       false);
30✔
957
               test.set_custom_client_tls_session_established_callback(
12✔
958
                  [=](const auto&) { std::rethrow_exception(ex); });
42✔
959
               test.set_server_expected_handshake_alert(expected_server_alert);
6✔
960

961
               test.go();
6✔
962
               results.push_back(test.results());
6✔
963
            }
6✔
964
         };
3✔
965

966
         auto server_aborts = [&](const std::exception_ptr& ex, Botan::TLS::Alert expected_server_alert) {
4✔
967
            for(const auto version : versions) {
9✔
968
               TLS_Handshake_Test test("Server aborts in tls_session_established with " +
12✔
969
                                          expected_server_alert.type_string() + ": " + version.to_string(),
24✔
970
                                       version,
971
                                       creds,
972
                                       policy,
973
                                       policy,
974
                                       rng,
975
                                       noop_session_manager,
976
                                       noop_session_manager,
977
                                       false);
30✔
978
               test.set_custom_server_tls_session_established_callback(
12✔
979
                  [=](const auto&) { std::rethrow_exception(ex); });
42✔
980
               test.set_client_expected_handshake_alert(expected_server_alert);
6✔
981

982
               test.go();
6✔
983
               results.push_back(test.results());
6✔
984
            }
6✔
985
         };
3✔
986

987
         client_aborts(std::make_exception_ptr(
3✔
988
                          Botan::TLS::TLS_Exception(Botan::TLS::Alert::AccessDenied, "some test TLS exception")),
1✔
989
                       Botan::TLS::Alert::AccessDenied);
990
         client_aborts(std::make_exception_ptr(Botan::Invalid_Authentication_Tag("some symmetric crypto failed :o)")),
2✔
991
                       Botan::TLS::Alert::BadRecordMac);
992
         client_aborts(std::make_exception_ptr(std::runtime_error("something strange happened")),
2✔
993
                       Botan::TLS::Alert::InternalError);
994

995
         server_aborts(std::make_exception_ptr(
3✔
996
                          Botan::TLS::TLS_Exception(Botan::TLS::Alert::AccessDenied, "some server test TLS exception")),
1✔
997
                       Botan::TLS::Alert::AccessDenied);
998
         server_aborts(std::make_exception_ptr(
2✔
999
                          Botan::Invalid_Authentication_Tag("some symmetric crypto failed in the server :o)")),
1✔
1000
                       Botan::TLS::Alert::BadRecordMac);
1001
         server_aborts(std::make_exception_ptr(std::runtime_error("something strange happened in the server")),
2✔
1002
                       Botan::TLS::Alert::InternalError);
1003
      }
2✔
1004

1005
      void test_custom_ecdh_provider(std::vector<Test::Result>& results,
1✔
1006
                                     const std::shared_ptr<Credentials_Manager_Test>& creds,
1007
                                     const std::shared_ptr<Botan::RandomNumberGenerator>& rng) {
1008
         auto noop_session_manager = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1009

1010
         const auto groups = {
1011
            Botan::TLS::Group_Params::SECP256R1,
1012
            Botan::TLS::Group_Params::BRAINPOOL256R1,
1013
            Botan::TLS::Group_Params::BRAINPOOL512R1,
1014
         };
1✔
1015

1016
         for(const Botan::TLS::Group_Params ecdh_group : groups) {
4✔
1017
            if(!Botan::EC_Group::supports_named_group(ecdh_group.to_string().value())) {
9✔
1018
               continue;
×
1019
            }
1020

1021
            auto policy = std::make_shared<Test_Policy>();
3✔
1022
            policy->set("groups", "0x" + Botan::hex_encode(Botan::store_be(ecdh_group.wire_code())));
9✔
1023

1024
            TLS_Handshake_Test test(
3✔
1025
               "Client uses a custom ECDH provider for " + ecdh_group.to_string().value() + " in TLS 1.2",
12✔
1026
               Botan::TLS::Protocol_Version::TLS_V12,
1027
               creds,
1028
               policy,
1029
               policy,
1030
               rng,
1031
               noop_session_manager,
1032
               noop_session_manager,
1033
               false);
18✔
1034

1035
            auto& test_results = test.results();
3✔
1036

1037
            bool generator_called = false;
3✔
1038
            bool agreement_called = false;
3✔
1039

1040
            test.set_custom_client_tls_generate_ephemeral_ecdh_key_callback(
×
1041
               [&](const Botan::TLS::Group_Params& group,
3✔
1042
                   Botan::RandomNumberGenerator&,
1043
                   Botan::EC_Point_Format format) -> std::unique_ptr<Botan::PK_Key_Agreement_Key> {
1044
                  generator_called = true;
3✔
1045
                  test_results.require("tls_generate_ephemeral_ecdh_key_callback called for ECDH",
3✔
1046
                                       group.is_ecdh_named_curve());
3✔
1047
                  return std::make_unique<HardwareEcdhKey>(Botan::EC_Group::from_name(group.to_string().value()),
9✔
1048
                                                           format);
6✔
1049
               });
1050

1051
            test.set_custom_client_tls_ephemeral_key_agreement_callback(
×
1052
               [&](const std::variant<Botan::TLS::Group_Params, Botan::DL_Group>& group,
3✔
1053
                   const Botan::PK_Key_Agreement_Key& private_key,
1054
                   const std::vector<uint8_t>& peer_public_value,
1055
                   Botan::RandomNumberGenerator& clbk_rng,
1056
                   const Botan::TLS::Policy&) -> Botan::secure_vector<uint8_t> {
1057
                  agreement_called = true;
3✔
1058

1059
                  const auto* group_params = std::get_if<Botan::TLS::Group_Params>(&group);
3✔
1060
                  test_results.require("tls_ephemeral_key_agreement_callback called with a TLS group",
3✔
1061
                                       group_params != nullptr);
1062
                  test_results.require("tls_ephemeral_key_agreement_callback called with an ECDH group",
3✔
1063
                                       group_params->is_ecdh_named_curve());
3✔
1064

1065
                  const auto* hwkey = dynamic_cast<const HardwareEcdhKey*>(&private_key);
3✔
1066
                  test_results.require("tls_ephemeral_key_agreement_callback called with a HardwareEcdhKey",
3✔
1067
                                       hwkey != nullptr);
1068

1069
                  return hwkey->custom_ephemeral_agreement(peer_public_value, clbk_rng);
3✔
1070
               });
1071

1072
            test.go();
3✔
1073
            test.results().confirm("custom generation was used", generator_called);
6✔
1074
            test.results().confirm("custom agreement was used", agreement_called);
6✔
1075
            results.push_back(test.results());
3✔
1076
         }
6✔
1077
      }
1✔
1078

1079
   public:
1080
      std::vector<Test::Result> run() override {
1✔
1081
         std::vector<Test::Result> results;
1✔
1082

1083
         auto rng = Test::new_shared_rng(this->test_name());
1✔
1084

1085
         std::shared_ptr<Botan::TLS::Session_Manager> client_ses;
1✔
1086
         std::shared_ptr<Botan::TLS::Session_Manager> server_ses;
1✔
1087

1088
   #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
1089
         client_ses = std::make_shared<Botan::TLS::Session_Manager_SQLite>("client pass", rng, ":memory:", 5);
1✔
1090
         server_ses = std::make_shared<Botan::TLS::Session_Manager_SQLite>("server pass", rng, ":memory:", 5);
1✔
1091
   #endif
1092

1093
         if(!client_ses) {
1✔
1094
            client_ses = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
×
1095
         }
1096

1097
         if(!server_ses) {
1✔
1098
            server_ses = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
×
1099
         }
1100

1101
         auto creds = create_creds(*rng);
1✔
1102
         if(!creds) {
1✔
1103
            // Credentials manager creation failed, likely no EC group available
1104
            // Skip this test entirely
1105
            return {Test::Result::Note("TLS unit tests", "Skipping due to missing credentials")};
×
1106
         }
1107

1108
   #if defined(BOTAN_HAS_TLS_CBC)
1109
         for(const std::string etm_setting : {"false", "true"}) {
3✔
1110
            test_all_versions("AES-128 RSA",
4✔
1111
                              results,
1112
                              client_ses,
1113
                              server_ses,
1114
                              creds,
1115
                              rng,
1116
                              "RSA",
1117
                              "AES-128",
1118
                              "SHA-256 SHA-1",
1119
                              etm_setting);
1120
            test_all_versions("AES-128 ECDH",
4✔
1121
                              results,
1122
                              client_ses,
1123
                              server_ses,
1124
                              creds,
1125
                              rng,
1126
                              "ECDH",
1127
                              "AES-128",
1128
                              "SHA-256 SHA-1",
1129
                              etm_setting);
1130

1131
      #if defined(BOTAN_HAS_DES)
1132
            test_all_versions(
4✔
1133
               "3DES RSA", results, client_ses, server_ses, creds, rng, "RSA", "3DES", "SHA-1", etm_setting);
1134
            test_all_versions(
4✔
1135
               "3DES ECDH", results, client_ses, server_ses, creds, rng, "ECDH", "3DES", "SHA-1", etm_setting);
1136
      #endif
1137

1138
            server_ses->remove_all();
2✔
1139
         }
2✔
1140
         client_ses->remove_all();
1✔
1141

1142
         test_modern_versions("AES-128 DH", results, client_ses, server_ses, creds, rng, "DH", "AES-128", "SHA-256");
2✔
1143

1144
   #endif
1145

1146
   #if defined(BOTAN_HAS_TLS_NULL)
1147
         test_modern_versions("NULL PSK", results, client_ses, server_ses, creds, rng, "PSK", "NULL", "SHA-256");
2✔
1148
   #endif
1149

1150
         auto strict_policy = std::make_shared<Botan::TLS::Strict_Policy_Without_TLS13>();
1✔
1151
         test_with_policy("Strict policy",
2✔
1152
                          results,
1153
                          client_ses,
1154
                          server_ses,
1155
                          creds,
1156
                          {Botan::TLS::Protocol_Version::TLS_V12},
1157
                          strict_policy,
1158
                          rng);
1159

1160
         auto suiteb_128 = std::make_shared<Botan::TLS::NSA_Suite_B_128>();
1✔
1161
         test_with_policy("Suite B",
2✔
1162
                          results,
1163
                          client_ses,
1164
                          server_ses,
1165
                          creds,
1166
                          {Botan::TLS::Protocol_Version::TLS_V12},
1167
                          suiteb_128,
1168
                          rng);
1169

1170
         // Remove server sessions before client, so clients retry with session server doesn't know
1171
         server_ses->remove_all();
1✔
1172

1173
         test_modern_versions("AES-128/GCM RSA", results, client_ses, server_ses, creds, rng, "RSA", "AES-128/GCM");
2✔
1174
         test_modern_versions("AES-128/GCM ECDH", results, client_ses, server_ses, creds, rng, "ECDH", "AES-128/GCM");
2✔
1175

1176
         test_modern_versions("AES-128/GCM ECDH RSA",
3✔
1177
                              results,
1178
                              client_ses,
1179
                              server_ses,
1180
                              creds,
1181
                              rng,
1182
                              "ECDH",
1183
                              "AES-128/GCM",
1184
                              "AEAD",
1185
                              {{"signature_methods", "RSA"}});
1186

1187
         test_modern_versions("AES-128/GCM ECDH no OCSP",
3✔
1188
                              results,
1189
                              client_ses,
1190
                              server_ses,
1191
                              creds,
1192
                              rng,
1193
                              "ECDH",
1194
                              "AES-128/GCM",
1195
                              "AEAD",
1196
                              {{"support_cert_status_message", "false"}});
1197

1198
         client_ses->remove_all();
1✔
1199

1200
   #if defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_AEAD_GCM)
1201
         test_modern_versions(
2✔
1202
            "Camellia-128/GCM ECDH", results, client_ses, server_ses, creds, rng, "ECDH", "Camellia-128/GCM", "AEAD");
1203
   #endif
1204

1205
   #if defined(BOTAN_HAS_ARIA)
1206
         test_modern_versions(
2✔
1207
            "ARIA/GCM ECDH", results, client_ses, server_ses, creds, rng, "ECDH", "ARIA-128/GCM", "AEAD");
1208
   #endif
1209

1210
         test_modern_versions("AES-128/GCM point compression",
3✔
1211
                              results,
1212
                              client_ses,
1213
                              server_ses,
1214
                              creds,
1215
                              rng,
1216
                              "ECDH",
1217
                              "AES-128/GCM",
1218
                              "AEAD",
1219
                              {{"use_ecc_point_compression", "true"}});
1220
         test_modern_versions("AES-256/GCM p521",
3✔
1221
                              results,
1222
                              client_ses,
1223
                              server_ses,
1224
                              creds,
1225
                              rng,
1226
                              "ECDH",
1227
                              "AES-256/GCM",
1228
                              "AEAD",
1229
                              {{"groups", "secp521r1"}});
1230
         test_modern_versions("AES-128/GCM bp256r1",
3✔
1231
                              results,
1232
                              client_ses,
1233
                              server_ses,
1234
                              creds,
1235
                              rng,
1236
                              "ECDH",
1237
                              "AES-128/GCM",
1238
                              "AEAD",
1239
                              {{"groups", "brainpool256r1"}});
1240

1241
   #if defined(BOTAN_HAS_X25519)
1242
         test_modern_versions("AES-128/GCM x25519",
3✔
1243
                              results,
1244
                              client_ses,
1245
                              server_ses,
1246
                              creds,
1247
                              rng,
1248
                              "ECDH",
1249
                              "AES-128/GCM",
1250
                              "AEAD",
1251
                              {{"groups", "x25519"}});
1252
   #endif
1253

1254
         test_modern_versions("AES-128/GCM FFDHE-2048",
3✔
1255
                              results,
1256
                              client_ses,
1257
                              server_ses,
1258
                              creds,
1259
                              rng,
1260
                              "DH",
1261
                              "AES-128/GCM",
1262
                              "AEAD",
1263
                              {{"groups", "ffdhe/ietf/2048"}});
1264

1265
         auto creds_with_client_cert = create_creds(*rng, true);
1✔
1266
         if(creds_with_client_cert) {
1✔
1267
            client_ses->remove_all();
1✔
1268
            test_modern_versions("AES-256/GCM client certs",
2✔
1269
                                 results,
1270
                                 client_ses,
1271
                                 server_ses,
1272
                                 creds_with_client_cert,
1273
                                 rng,
1274
                                 "ECDH",
1275
                                 "AES-256/GCM",
1276
                                 "AEAD",
1277
                                 true);
1278
         }
1279

1280
   #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
1281
         client_ses = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
2✔
1282
         server_ses = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
2✔
1283
   #endif
1284

1285
   #if defined(BOTAN_HAS_AEAD_OCB)
1286
         test_modern_versions(
2✔
1287
            "AES-256/OCB ECDH", results, client_ses, server_ses, creds, rng, "ECDH", "AES-256/OCB(12)");
1288
   #endif
1289

1290
         server_ses->remove_all();
1✔
1291

1292
   #if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)
1293
         test_modern_versions(
2✔
1294
            "ChaCha20Poly1305 ECDH", results, client_ses, server_ses, creds, rng, "ECDH", "ChaCha20Poly1305");
1295
   #endif
1296

1297
         test_modern_versions("AES-128/GCM PSK", results, client_ses, server_ses, creds, rng, "PSK", "AES-128/GCM");
2✔
1298

1299
   #if defined(BOTAN_HAS_AEAD_CCM)
1300
         test_modern_versions("AES-128/CCM PSK", results, client_ses, server_ses, creds, rng, "PSK", "AES-128/CCM");
2✔
1301
         test_modern_versions(
2✔
1302
            "AES-128/CCM-8 PSK", results, client_ses, server_ses, creds, rng, "PSK", "AES-128/CCM(8)");
1303
   #endif
1304

1305
         test_modern_versions(
2✔
1306
            "AES-128/GCM ECDHE_PSK", results, client_ses, server_ses, creds, rng, "ECDHE_PSK", "AES-128/GCM");
1307

1308
         // Test with a custom curve
1309

1310
         if(Botan::EC_Group::supports_application_specific_group()) {
1✔
1311
            /*
1312
            * First register a curve, in this case numsp256d1
1313
            */
1314
            const Botan::BigInt p("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43");
1✔
1315
            const Botan::BigInt a("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40");
1✔
1316
            const Botan::BigInt b("0x25581");
1✔
1317
            const Botan::BigInt order("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE43C8275EA265C6020AB20294751A825");
1✔
1318

1319
            const Botan::BigInt g_x("0x01");
1✔
1320
            const Botan::BigInt g_y("0x696F1853C1E466D7FC82C96CCEEEDD6BD02C2F9375894EC10BF46306C2B56C77");
1✔
1321

1322
            const Botan::OID oid("1.3.6.1.4.1.25258.4.1");
1✔
1323

1324
            Botan::OID::register_oid(oid, "numsp256d1");
1✔
1325

1326
            // Creating this object implicitly registers the curve for future use ...
1327
            const Botan::EC_Group reg_numsp256d1(oid, p, a, b, g_x, g_y, order);
1✔
1328

1329
            test_modern_versions("AES-256/GCM numsp256d1",
4✔
1330
                                 results,
1331
                                 client_ses,
1332
                                 server_ses,
1333
                                 creds,
1334
                                 rng,
1335
                                 "ECDH",
1336
                                 "AES-256/GCM",
1337
                                 "AEAD",
1338
                                 {{"groups", "0xFEE1"}, {"minimum_ecdh_group_size", "112"}});
1339
         }
1✔
1340

1341
         // Test connection abort by the application
1342
         // by throwing in Callbacks::tls_session_established()
1343

1344
         test_session_established_abort(results, creds, rng);
2✔
1345

1346
         // Test using tls_generate_epheral_ecdh_key() to establish a custom
1347
         // ECDH provider that combines ephemeral key generation with key
1348
         // establishment (as it used to work in Botan 2.x via tls_ecdh_agree()).
1349

1350
         test_custom_ecdh_provider(results, creds, rng);
1✔
1351

1352
         return results;
1✔
1353
      }
15✔
1354
};
1355

1356
BOTAN_REGISTER_TEST("tls", "unit_tls", TLS_Unit_Tests);
1357

1358
class DTLS_Reconnection_Test : public Test {
1✔
1359
   public:
1360
      std::vector<Test::Result> run() override {
1✔
1361
         class Test_Callbacks : public Botan::TLS::Callbacks {
4✔
1362
            public:
1363
               Test_Callbacks(Test::Result& results, std::vector<uint8_t>& outbound, std::vector<uint8_t>& recv_buf) :
3✔
1364
                     m_results(results), m_outbound(outbound), m_recv(recv_buf) {}
3✔
1365

1366
               void tls_emit_data(std::span<const uint8_t> bits) override {
28✔
1367
                  m_outbound.insert(m_outbound.end(), bits.begin(), bits.end());
28✔
1368
               }
28✔
1369

1370
               void tls_record_received(uint64_t /*seq*/, std::span<const uint8_t> bits) override {
4✔
1371
                  m_recv.insert(m_recv.end(), bits.begin(), bits.end());
4✔
1372
               }
4✔
1373

1374
               void tls_alert(Botan::TLS::Alert /*alert*/) override {
×
1375
                  // ignore
1376
               }
×
1377

1378
               void tls_session_established(const Botan::TLS::Session_Summary& /*session*/) override {
4✔
1379
                  m_results.test_success("Established a session");
4✔
1380
               }
4✔
1381

1382
            private:
1383
               Test::Result& m_results;
1384
               std::vector<uint8_t>& m_outbound;
1385
               std::vector<uint8_t>& m_recv;
1386
         };
1387

1388
         class Credentials_PSK : public Botan::Credentials_Manager {
3✔
1389
            public:
1390
               Botan::SymmetricKey psk(const std::string& type,
8✔
1391
                                       const std::string& context,
1392
                                       const std::string& /*identity*/) override {
1393
                  if(type == "tls-server" && context == "session-ticket") {
8✔
1394
                     return Botan::SymmetricKey("AABBCCDDEEFF012345678012345678");
×
1395
                  }
1396

1397
                  if(type == "tls-server" && context == "dtls-cookie-secret") {
8✔
1398
                     return Botan::SymmetricKey("4AEA5EAD279CADEB537A594DA0E9DE3A");
4✔
1399
                  }
1400

1401
                  if(context == "localhost" && type == "tls-client") {
4✔
1402
                     return Botan::SymmetricKey("20B602D1475F2DF888FCB60D2AE03AFD");
2✔
1403
                  }
1404

1405
                  if(context == "localhost" && type == "tls-server") {
2✔
1406
                     return Botan::SymmetricKey("20B602D1475F2DF888FCB60D2AE03AFD");
2✔
1407
                  }
1408

1409
                  throw Test_Error("No PSK set for " + type + "/" + context);
×
1410
               }
1411
         };
1412

1413
         class Datagram_PSK_Policy : public Botan::TLS::Policy {
5✔
1414
            public:
1415
               std::vector<std::string> allowed_macs() const override { return std::vector<std::string>({"AEAD"}); }
216✔
1416

1417
               std::vector<std::string> allowed_key_exchange_methods() const override { return {"PSK"}; }
4✔
1418

1419
               bool allow_tls12() const override { return false; }
6✔
1420

1421
               bool allow_dtls12() const override { return true; }
19✔
1422

1423
               bool allow_dtls_epoch0_restart() const override { return true; }
8✔
1424
         };
1425

1426
         Test::Result result("DTLS reconnection");
1✔
1427

1428
         auto rng = Test::new_shared_rng(this->test_name());
1✔
1429

1430
         auto server_policy = std::make_shared<Datagram_PSK_Policy>();
1✔
1431
         auto client_policy = std::make_shared<Datagram_PSK_Policy>();
1✔
1432
         auto creds = std::make_shared<Credentials_PSK>();
1✔
1433
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
1434
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1435

1436
         std::vector<uint8_t> s2c;
1✔
1437
         std::vector<uint8_t> server_recv;
1✔
1438
         auto server_callbacks = std::make_shared<Test_Callbacks>(result, s2c, server_recv);
1✔
1439
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, server_policy, rng, true);
4✔
1440

1441
         std::vector<uint8_t> c1_c2s;
1✔
1442
         std::vector<uint8_t> client1_recv;
1✔
1443
         auto client1_callbacks = std::make_shared<Test_Callbacks>(result, c1_c2s, client1_recv);
1✔
1444
         Botan::TLS::Client client1(client1_callbacks,
1✔
1445
                                    client_sessions,
1446
                                    creds,
1447
                                    client_policy,
1448
                                    rng,
1449
                                    Botan::TLS::Server_Information("localhost"),
1✔
1450
                                    Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
1451

1452
         bool c1_to_server_sent = false;
1✔
1453
         const bool server_to_c1_sent = false;
1✔
1454

1455
         const std::vector<uint8_t> c1_to_server_magic(16, 0xC1);
1✔
1456
         const std::vector<uint8_t> server_to_c1_magic(16, 0x42);
1✔
1457

1458
         size_t c1_rounds = 0;
1✔
1459
         for(;;) {
10✔
1460
            c1_rounds++;
10✔
1461

1462
            if(c1_rounds > 64) {
10✔
1463
               result.test_failure("Still spinning in client1 loop after 64 rounds");
×
1464
               return {result};
×
1465
            }
1466

1467
            if(!c1_c2s.empty()) {
10✔
1468
               std::vector<uint8_t> input;
4✔
1469
               std::swap(c1_c2s, input);
4✔
1470
               server.received_data(input.data(), input.size());
4✔
1471
               continue;
4✔
1472
            }
4✔
1473

1474
            if(!s2c.empty()) {
6✔
1475
               std::vector<uint8_t> input;
4✔
1476
               std::swap(s2c, input);
4✔
1477
               client1.received_data(input.data(), input.size());
4✔
1478
               continue;
4✔
1479
            }
4✔
1480

1481
            if(!c1_to_server_sent && client1.is_active()) {
2✔
1482
               client1.send(c1_to_server_magic);
1✔
1483
               c1_to_server_sent = true;
1✔
1484
            }
1485

1486
            if(!server_to_c1_sent && server.is_active()) {
2✔
1487
               server.send(server_to_c1_magic);
2✔
1488
            }
1489

1490
            if(!server_recv.empty() && !client1_recv.empty()) {
2✔
1491
               result.test_eq("Expected message from client1", server_recv, c1_to_server_magic);
2✔
1492
               result.test_eq("Expected message to client1", client1_recv, server_to_c1_magic);
2✔
1493
               break;
1✔
1494
            }
1495
         }
1496

1497
         // Now client1 "goes away" (goes silent) and new client
1498
         // connects to same server context (ie due to reuse of client source port)
1499
         // See RFC 6347 section 4.2.8
1500

1501
         server_recv.clear();
1✔
1502
         s2c.clear();
1✔
1503

1504
         std::vector<uint8_t> c2_c2s;
1✔
1505
         std::vector<uint8_t> client2_recv;
1✔
1506
         auto client2_callbacks = std::make_shared<Test_Callbacks>(result, c2_c2s, client2_recv);
1✔
1507
         Botan::TLS::Client client2(client2_callbacks,
1✔
1508
                                    client_sessions,
1509
                                    creds,
1510
                                    client_policy,
1511
                                    rng,
1512
                                    Botan::TLS::Server_Information("localhost"),
1✔
1513
                                    Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
1514

1515
         bool c2_to_server_sent = false;
1✔
1516
         const bool server_to_c2_sent = false;
1✔
1517

1518
         const std::vector<uint8_t> c2_to_server_magic(16, 0xC2);
1✔
1519
         const std::vector<uint8_t> server_to_c2_magic(16, 0x66);
1✔
1520

1521
         size_t c2_rounds = 0;
1✔
1522

1523
         for(;;) {
10✔
1524
            c2_rounds++;
10✔
1525

1526
            if(c2_rounds > 64) {
10✔
1527
               result.test_failure("Still spinning in client2 loop after 64 rounds");
×
1528
               return {result};
×
1529
            }
1530

1531
            if(!c2_c2s.empty()) {
10✔
1532
               std::vector<uint8_t> input;
4✔
1533
               std::swap(c2_c2s, input);
4✔
1534
               server.received_data(input.data(), input.size());
4✔
1535
               continue;
4✔
1536
            }
4✔
1537

1538
            if(!s2c.empty()) {
6✔
1539
               std::vector<uint8_t> input;
4✔
1540
               std::swap(s2c, input);
4✔
1541
               client2.received_data(input.data(), input.size());
4✔
1542
               continue;
4✔
1543
            }
4✔
1544

1545
            if(!c2_to_server_sent && client2.is_active()) {
2✔
1546
               client2.send(c2_to_server_magic);
1✔
1547
               c2_to_server_sent = true;
1✔
1548
            }
1549

1550
            if(!server_to_c2_sent && server.is_active()) {
2✔
1551
               server.send(server_to_c2_magic);
2✔
1552
            }
1553

1554
            if(!server_recv.empty() && !client2_recv.empty()) {
2✔
1555
               result.test_eq("Expected message from client2", server_recv, c2_to_server_magic);
2✔
1556
               result.test_eq("Expected message to client2", client2_recv, server_to_c2_magic);
2✔
1557
               break;
1✔
1558
            }
1559
         }
1560

1561
         return {result};
2✔
1562
      }
18✔
1563
};
1564

1565
BOTAN_REGISTER_TEST("tls", "tls_dtls_reconnect", DTLS_Reconnection_Test);
1566

1567
#endif
1568

1569
}  // namespace
1570

1571
}  // namespace Botan_Tests
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

© 2026 Coveralls, Inc