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

randombit / botan / 29801022385

21 Jul 2026 02:12AM UTC coverage: 89.404% (-0.02%) from 89.419%
29801022385

push

github

web-flow
Merge pull request #5747 from randombit/jack/zfec-avx512-gfni

ZFEC optimizations and improvements

114453 of 128018 relevant lines covered (89.4%)

10848451.21 hits per line

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

91.3
/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 <memory>
15
#include <vector>
16

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

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

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

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

47
#endif
48

49
namespace Botan_Tests {
50

51
namespace {
52

53
#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_RSA)
54

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

79
         m_stores.push_back(std::move(store));
2✔
80
      }
2✔
81

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

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

100
         if(m_acceptable_cas.empty()) {
214✔
101
            m_acceptable_cas = acceptable_CAs;
10✔
102
         }
103

104
         if(type == "tls-server" || (type == "tls-client" && m_provides_client_certs)) {
214✔
105
            for(const auto& key_type : cert_key_types) {
158✔
106
               if(key_type == "RSA") {
158✔
107
                  chain.push_back(m_rsa_cert);
80✔
108
                  chain.push_back(m_rsa_ca);
80✔
109
                  break;
110
               } else if(key_type == "ECDSA") {
78✔
111
                  chain.push_back(m_ecdsa_cert);
78✔
112
                  chain.push_back(m_ecdsa_ca);
78✔
113
                  break;
114
               }
115
            }
116
         }
117

118
         return chain;
214✔
119
      }
×
120

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

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

137
      Botan::secure_vector<uint8_t> dtls_cookie_secret() override {
134✔
138
         return Botan::hex_decode_locked("4AEA5EAD279CADEB537A594DA0E9DE3A");
134✔
139
      }
140

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

150
         std::vector<Botan::TLS::ExternalPSK> psks;
20✔
151

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

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

166
         return psks;
20✔
167
      }
20✔
168

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

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

175
      Botan::X509_Certificate m_ecdsa_cert, m_ecdsa_ca;
176
      std::shared_ptr<Botan::Private_Key> m_ecdsa_key;
177

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

183
std::shared_ptr<Credentials_Manager_Test> create_creds(Botan::RandomNumberGenerator& rng,
2✔
184
                                                       bool with_client_certs = false) {
185
   // RSA and ECDSA are required for the TLS module, but we need to find an
186
   // ECC group that is supported in this build or skip this test.
187
   const auto ec_group = Test::supported_ec_group_name({"secp256r1", "secp384r1", "secp521r1"});
2✔
188
   if(!ec_group) {
2✔
189
      return nullptr;
×
190
   }
191

192
   const auto ecdsa_params = Botan::EC_Group::from_name(*ec_group);
2✔
193
   const size_t rsa_params = 1024;
2✔
194

195
   auto rsa_ca_key = std::make_unique<Botan::RSA_PrivateKey>(rng, rsa_params);
2✔
196
   auto rsa_srv_key = std::make_unique<Botan::RSA_PrivateKey>(rng, rsa_params);
2✔
197

198
   auto ecdsa_ca_key = std::make_unique<Botan::ECDSA_PrivateKey>(rng, ecdsa_params);
2✔
199
   auto ecdsa_srv_key = std::make_unique<Botan::ECDSA_PrivateKey>(rng, ecdsa_params);
2✔
200

201
   Botan::X509_Cert_Options rsa_ca_opts("RSA Test CA/VT");
2✔
202
   Botan::X509_Cert_Options ecdsa_ca_opts("ECDSA Test CA/VT");
2✔
203
   rsa_ca_opts.CA_key(1);
2✔
204
   ecdsa_ca_opts.CA_key(1);
2✔
205

206
   const Botan::X509_Certificate rsa_ca_cert =
2✔
207
      Botan::X509::create_self_signed_cert(rsa_ca_opts, *rsa_ca_key, "SHA-256", rng);
2✔
208
   const Botan::X509_Certificate ecdsa_ca_cert =
2✔
209
      Botan::X509::create_self_signed_cert(ecdsa_ca_opts, *ecdsa_ca_key, "SHA-256", rng);
2✔
210

211
   const Botan::X509_Cert_Options server_opts("server.example.com");
2✔
212

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

216
   const Botan::X509_CA rsa_ca(rsa_ca_cert, *rsa_ca_key, "SHA-256", rng);
2✔
217
   const Botan::X509_CA ecdsa_ca(ecdsa_ca_cert, *ecdsa_ca_key, "SHA-256", rng);
2✔
218

219
   typedef std::chrono::duration<int, std::ratio<31556926>> years;
2✔
220
   auto now = std::chrono::system_clock::now();
2✔
221

222
   const Botan::X509_Time start_time(now);
2✔
223
   const Botan::X509_Time end_time(now + years(1));
2✔
224

225
   const Botan::X509_Certificate rsa_srv_cert = rsa_ca.sign_request(rsa_req, rng, start_time, end_time);
2✔
226
   const Botan::X509_Certificate ecdsa_srv_cert = ecdsa_ca.sign_request(ecdsa_req, rng, start_time, end_time);
2✔
227

228
   const Botan::X509_CRL rsa_crl = rsa_ca.new_crl(rng);
2✔
229
   const Botan::X509_CRL ecdsa_crl = ecdsa_ca.new_crl(rng);
2✔
230

231
   return std::make_shared<Credentials_Manager_Test>(with_client_certs,
2✔
232
                                                     rsa_srv_cert,
233
                                                     rsa_srv_key.release(),
4✔
234
                                                     rsa_ca_cert,
235
                                                     rsa_crl,
236
                                                     ecdsa_srv_cert,
237
                                                     ecdsa_srv_key.release(),
2✔
238
                                                     ecdsa_ca_cert,
239
                                                     ecdsa_crl);
2✔
240
}
6✔
241

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

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

274
         const bool is_dtls = offer_version.is_datagram_protocol();
137✔
275

276
         m_server =
137✔
277
            std::make_unique<Botan::TLS::Server>(m_server_cb, server_sessions, m_creds, server_policy, m_rng, is_dtls);
137✔
278
      }
137✔
279

280
      void go();
281

282
      const Test::Result& results() const { return m_results; }
283

284
      Test::Result& results() { return m_results; }
157✔
285

286
      void set_custom_client_tls_session_established_callback(
6✔
287
         std::function<void(const Botan::TLS::Session_Summary&)> clbk) {
288
         BOTAN_ASSERT_NONNULL(m_client_cb);
6✔
289
         m_client_cb->set_custom_tls_session_established_callback(std::move(clbk));
12✔
290
      }
6✔
291

292
      void set_custom_server_tls_session_established_callback(
6✔
293
         std::function<void(const Botan::TLS::Session_Summary&)> clbk) {
294
         BOTAN_ASSERT_NONNULL(m_server_cb);
6✔
295
         m_server_cb->set_custom_tls_session_established_callback(std::move(clbk));
12✔
296
      }
6✔
297

298
      void set_custom_client_tls_generate_ephemeral_ecdh_key_callback(generate_ephemeral_ecdh_key_clbk clbk) {
6✔
299
         BOTAN_ASSERT_NONNULL(m_client_cb);
6✔
300
         m_client_cb->set_custom_client_tls_generate_ephemeral_ecdh_key_callback(std::move(clbk));
12✔
301
      }
6✔
302

303
      void set_custom_client_tls_ephemeral_key_agreement_callback(ephemeral_key_agreement_clbk clbk) {
6✔
304
         BOTAN_ASSERT_NONNULL(m_client_cb);
6✔
305
         m_client_cb->set_custom_client_tls_ephemeral_key_agreement_callback(std::move(clbk));
12✔
306
      }
6✔
307

308
      void set_client_expected_handshake_alert(Botan::TLS::Alert alert) {
6✔
309
         BOTAN_ASSERT_NONNULL(m_client_cb);
6✔
310
         m_client_cb->set_expected_handshake_alert(alert);
6✔
311
      }
6✔
312

313
      void set_server_expected_handshake_alert(Botan::TLS::Alert alert) {
6✔
314
         BOTAN_ASSERT_NONNULL(m_server_cb);
6✔
315
         m_server_cb->set_expected_handshake_alert(alert);
6✔
316
      }
6✔
317

318
      // Tests that drive an intentional handshake abort (eg via a throwing
319
      // tls_session_established callback) call this so the end-of-test
320
      // completion assertion in go() is suppressed.
321
      void expect_handshake_failure() { m_expect_handshake_completion = false; }
12✔
322

323
      void expect_session_resumption() { m_expect_session_resumption = true; }
59✔
324

325
      void set_client_custom_kdf_callback(custom_kdf_clbk clbk) {
1✔
326
         BOTAN_ASSERT_NONNULL(m_client_cb);
1✔
327
         m_client_cb->set_custom_kdf_callback(std::move(clbk));
2✔
328
      }
1✔
329

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

335
   private:
336
      class Test_Extension : public Botan::TLS::Extension {
337
         public:
338
            static Botan::TLS::Extension_Code static_type() {
339
               // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
340
               return static_cast<Botan::TLS::Extension_Code>(666);
341
            }
342

343
            Botan::TLS::Extension_Code type() const override { return static_type(); }
274✔
344

345
            std::vector<uint8_t> serialize(Botan::TLS::Connection_Side /*whoami*/) const override { return m_buf; }
341✔
346

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

349
            bool empty() const override { return false; }
478✔
350

351
            explicit Test_Extension(Botan::TLS::Connection_Side side) {
274✔
352
               const uint8_t client_extn[6] = {'c', 'l', 'i', 'e', 'n', 't'};
274✔
353
               const uint8_t server_extn[6] = {'s', 'e', 'r', 'v', 'e', 'r'};
274✔
354

355
               Botan::TLS::append_tls_length_value(
274✔
356
                  m_buf, (side == Botan::TLS::Connection_Side::Client) ? client_extn : server_extn, 6, 1);
274✔
357
            }
274✔
358

359
         private:
360
            std::vector<uint8_t> m_buf;
361
      };
362

363
      class Test_Callbacks final : public Botan::TLS::Callbacks {
364
         public:
365
            Test_Callbacks(Test::Result& results,
274✔
366
                           Botan::TLS::Protocol_Version expected_version,
367
                           std::vector<uint8_t>& outbound,
368
                           std::vector<uint8_t>& recv_buf) :
274✔
369
                  m_results(results), m_expected_version(expected_version), m_outbound(outbound), m_recv(recv_buf) {}
274✔
370

371
            Test_Callbacks(Test_Callbacks&&) = delete;
372
            Test_Callbacks(const Test_Callbacks&) = delete;
373
            Test_Callbacks& operator=(const Test_Callbacks&) = delete;
374
            Test_Callbacks& operator=(Test_Callbacks&&) = delete;
375

376
            ~Test_Callbacks() override {
274✔
377
               if(m_expected_handshake_alert.has_value()) {
274✔
378
                  m_results.test_failure("Expected: " + m_expected_handshake_alert->type_string() +
×
379
                                         " during handshake");
380
               }
381
            }
544✔
382

383
            void tls_emit_data(std::span<const uint8_t> bits) override {
2,309✔
384
               m_outbound.insert(m_outbound.end(), bits.begin(), bits.end());
2,309✔
385
            }
2,309✔
386

387
            void tls_record_received(uint64_t /*seq*/, std::span<const uint8_t> bits) override {
427✔
388
               m_recv.insert(m_recv.end(), bits.begin(), bits.end());
427✔
389
            }
427✔
390

391
            void tls_alert(Botan::TLS::Alert alert) override {
512✔
392
               // TODO test that it is a no_renegotiation alert
393

394
               if(m_expected_handshake_alert.has_value()) {
512✔
395
                  if(m_expected_handshake_alert->type() != alert.type()) {
12✔
396
                     m_results.test_failure("Got unexpected alert: " + alert.type_string() +
×
397
                                            " expected: " + m_expected_handshake_alert->type_string());
×
398
                  } else {
399
                     // acknowledge that the expected Alert was detected
400
                     m_results.test_note("saw expected alert", m_expected_handshake_alert->type_string());
12✔
401
                     m_expected_handshake_alert.reset();
12✔
402
                  }
403
               }
404
            }
512✔
405

406
            void tls_modify_extensions(Botan::TLS::Extensions& extn,
274✔
407
                                       Botan::TLS::Connection_Side which_side,
408
                                       Botan::TLS::Handshake_Type /* unused */) override {
409
               extn.add(new Test_Extension(which_side));  // NOLINT(*-owning-memory)
274✔
410

411
               // Insert an unsupported signature scheme as highest prio, to ensure we are tolerant of this
412
               if(auto* sig_algs = extn.get<Botan::TLS::Signature_Algorithms>()) {
274✔
413
                  std::vector<Botan::TLS::Signature_Scheme> schemes = sig_algs->supported_schemes();
137✔
414
                  // 0x0301 is RSA PKCS1/SHA-224, which is not supported anymore
415
                  schemes.insert(schemes.begin(), 0x0301);
137✔
416
                  // This replaces the previous extension value
417
                  extn.remove_extension(Botan::TLS::Extension_Code::SignatureAlgorithms);
137✔
418
                  extn.add(new Botan::TLS::Signature_Algorithms(schemes));  // NOLINT(*-owning-memory)
137✔
419
               }
137✔
420
            }
274✔
421

422
            void tls_examine_extensions(const Botan::TLS::Extensions& extn,
274✔
423
                                        Botan::TLS::Connection_Side which_side,
424
                                        Botan::TLS::Handshake_Type /*unused*/) override {
425
               // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
426
               const auto extn_id = static_cast<Botan::TLS::Extension_Code>(666);
274✔
427
               Botan::TLS::Extension* test_extn = extn.get(extn_id);
274✔
428

429
               if(test_extn == nullptr) {
274✔
430
                  m_results.test_failure("Did not receive test extension from peer");
×
431
               } else {
432
                  Botan::TLS::Unknown_Extension* unknown_ext = dynamic_cast<Botan::TLS::Unknown_Extension*>(test_extn);
274✔
433

434
                  if(unknown_ext != nullptr) {
274✔
435
                     const std::vector<uint8_t> val = unknown_ext->value();
274✔
436

437
                     if(m_results.test_sz_eq("Expected size for test extn", val.size(), 7)) {
274✔
438
                        if(which_side == Botan::TLS::Connection_Side::Client) {
274✔
439
                           m_results.test_bin_eq("Expected extension value", val, "06636C69656E74");
137✔
440
                        } else {
441
                           m_results.test_bin_eq("Expected extension value", val, "06736572766572");
137✔
442
                        }
443
                     }
444
                  } else {
274✔
445
                     m_results.test_failure("Unknown extension type had unexpected type at runtime");
×
446
                  }
447
               }
448
            }
274✔
449

450
            void tls_session_established(const Botan::TLS::Session_Summary& session) override {
268✔
451
               const std::string session_report = "Session established " + session.version().to_string() + " " +
804✔
452
                                                  session.ciphersuite().to_string() + " " +
804✔
453
                                                  Botan::hex_encode(session.session_id().get());
536✔
454

455
               m_results.test_note(session_report);
268✔
456

457
               if(m_session_established_callback) {
268✔
458
                  m_session_established_callback(session);
12✔
459
               }
460

461
               if(session.version() != m_expected_version) {
256✔
462
                  m_results.test_failure("Expected " + m_expected_version.to_string() + " negotiated " +
×
463
                                         session.version().to_string());
12✔
464
               }
465

466
               if(m_summary.has_value()) {
256✔
467
                  m_results.test_failure("Session established callback called multiple times");
×
468
               }
469

470
               m_summary.emplace(session);
256✔
471
            }
256✔
472

473
            std::string tls_server_choose_app_protocol(const std::vector<std::string>& protos) override {
137✔
474
               m_results.test_sz_eq("ALPN protocol count", protos.size(), 2);
137✔
475
               m_results.test_str_eq("ALPN protocol 1", protos[0], "test/1");
137✔
476
               m_results.test_str_eq("ALPN protocol 2", protos[1], "test/2");
137✔
477
               return "test/1";
137✔
478
            }
479

480
            std::unique_ptr<Botan::PK_Key_Agreement_Key> tls_generate_ephemeral_key(
114✔
481
               const std::variant<Botan::TLS::Group_Params, Botan::DL_Group>& group,
482
               Botan::RandomNumberGenerator& rng) override {
483
               if(std::holds_alternative<Botan::TLS::Group_Params>(group) &&
114✔
484
                  std::get<Botan::TLS::Group_Params>(group).wire_code() == 0xFEE1) {
110✔
485
                  const auto ec_group = Botan::EC_Group::from_name("numsp256d1");
×
486
                  return std::make_unique<Botan::ECDH_PrivateKey>(rng, ec_group);
×
487
               }
×
488

489
               return Botan::TLS::Callbacks::tls_generate_ephemeral_key(group, rng);
114✔
490
            }
491

492
            std::unique_ptr<Botan::PK_Key_Agreement_Key> tls12_generate_ephemeral_ecdh_key(
22✔
493
               Botan::TLS::Group_Params group,
494
               Botan::RandomNumberGenerator& rng,
495
               Botan::EC_Point_Format tls12_ecc_pubkey_encoding_format) override {
496
               if(m_generate_ephemeral_ecdh_key_callback) {
22✔
497
                  return m_generate_ephemeral_ecdh_key_callback(group, rng, tls12_ecc_pubkey_encoding_format);
6✔
498
               }
499

500
               return Botan::TLS::Callbacks::tls12_generate_ephemeral_ecdh_key(
16✔
501
                  group, rng, tls12_ecc_pubkey_encoding_format);
16✔
502
            }
503

504
            Botan::secure_vector<uint8_t> tls_ephemeral_key_agreement(
120✔
505
               const std::variant<Botan::TLS::Group_Params, Botan::DL_Group>& group,
506
               const Botan::PK_Key_Agreement_Key& private_key,
507
               const std::vector<uint8_t>& public_value,
508
               Botan::RandomNumberGenerator& rng,
509
               const Botan::TLS::Policy& policy) override {
510
               if(m_ephemeral_key_agreement_callback) {
120✔
511
                  return m_ephemeral_key_agreement_callback(group, private_key, public_value, rng, policy);
6✔
512
               }
513

514
               if(std::holds_alternative<Botan::TLS::Group_Params>(group) &&
114✔
515
                  std::get<Botan::TLS::Group_Params>(group).wire_code() == 0xFEE1) {
110✔
516
                  const auto ec_group = Botan::EC_Group::from_name("numsp256d1");
×
517
                  const auto ec_point = Botan::EC_AffinePoint(ec_group, public_value);
×
518
                  const Botan::ECDH_PublicKey peer_key(ec_group, ec_point);
×
519
                  const Botan::PK_Key_Agreement ka(private_key, rng, "Raw");
×
520
                  return ka.derive_key(0, peer_key.public_value()).bits_of();
×
521
               }
×
522

523
               return Botan::TLS::Callbacks::tls_ephemeral_key_agreement(group, private_key, public_value, rng, policy);
114✔
524
            }
525

526
            std::unique_ptr<Botan::KDF> tls12_protocol_specific_kdf(std::string_view prf_algo) const override {
1,060✔
527
               if(m_custom_kdf_callback) {
1,060✔
528
                  return m_custom_kdf_callback(prf_algo);
8✔
529
               } else {
530
                  return Botan::TLS::Callbacks::tls12_protocol_specific_kdf(prf_algo);
1,052✔
531
               }
532
            }
533

534
            void set_custom_tls_session_established_callback(
12✔
535
               std::function<void(const Botan::TLS::Session_Summary&)> clbk) {
536
               m_session_established_callback = std::move(clbk);
12✔
537
            }
538

539
            void set_custom_client_tls_generate_ephemeral_ecdh_key_callback(generate_ephemeral_ecdh_key_clbk clbk) {
6✔
540
               m_generate_ephemeral_ecdh_key_callback = std::move(clbk);
6✔
541
            }
542

543
            void set_custom_client_tls_ephemeral_key_agreement_callback(ephemeral_key_agreement_clbk clbk) {
6✔
544
               m_ephemeral_key_agreement_callback = std::move(clbk);
6✔
545
            }
546

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

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

551
            const std::optional<Botan::TLS::Session_Summary>& summary() const { return m_summary; }
125✔
552

553
         private:
554
            Test::Result& m_results;
555
            const Botan::TLS::Protocol_Version m_expected_version;
556
            std::vector<uint8_t>& m_outbound;
557
            std::vector<uint8_t>& m_recv;
558

559
            std::function<void(const Botan::TLS::Session_Summary&)> m_session_established_callback;
560
            generate_ephemeral_ecdh_key_clbk m_generate_ephemeral_ecdh_key_callback;
561
            ephemeral_key_agreement_clbk m_ephemeral_key_agreement_callback;
562
            custom_kdf_clbk m_custom_kdf_callback;
563
            std::optional<Botan::TLS::Alert> m_expected_handshake_alert;
564
            std::optional<Botan::TLS::Session_Summary> m_summary;
565
      };
566

567
      const Botan::TLS::Protocol_Version m_offer_version;
568
      Test::Result m_results;
569

570
      std::shared_ptr<Credentials_Manager_Test> m_creds;
571
      std::shared_ptr<const Botan::TLS::Policy> m_client_policy;
572
      std::shared_ptr<Botan::TLS::Session_Manager> m_client_sessions;
573
      std::shared_ptr<Botan::RandomNumberGenerator> m_rng;
574

575
      std::shared_ptr<Test_Callbacks> m_client_cb;
576

577
      std::shared_ptr<Test_Callbacks> m_server_cb;
578
      std::unique_ptr<Botan::TLS::Server> m_server;
579

580
      const bool m_client_auth;
581
      bool m_expect_handshake_completion = true;
582
      bool m_expect_session_resumption = false;
583

584
      std::vector<uint8_t> m_c2s, m_s2c, m_client_recv, m_server_recv;
585
      std::vector<std::string> m_caught_tls_exceptions;
586
};
587

588
void TLS_Handshake_Test::go() {
137✔
589
   m_results.start_timer();
137✔
590

591
   const std::vector<std::string> protocols_offered = {"test/1", "test/2"};
137✔
592

593
   // Choose random application data to send
594
   const size_t c_len = 1 + ((static_cast<size_t>(m_rng->next_byte()) << 4) ^ m_rng->next_byte());
137✔
595
   std::vector<uint8_t> client_msg(c_len);
137✔
596
   m_rng->randomize(client_msg.data(), client_msg.size());
137✔
597
   bool client_has_written = false;
137✔
598

599
   const size_t s_len = 1 + ((static_cast<size_t>(m_rng->next_byte()) << 4) ^ m_rng->next_byte());
137✔
600
   std::vector<uint8_t> server_msg(s_len);
137✔
601
   m_rng->randomize(server_msg.data(), server_msg.size());
137✔
602
   bool server_has_written = false;
137✔
603

604
   std::unique_ptr<Botan::TLS::Client> client;
137✔
605
   client = std::make_unique<Botan::TLS::Client>(m_client_cb,
137✔
606
                                                 m_client_sessions,
137✔
607
                                                 m_creds,
137✔
608
                                                 m_client_policy,
137✔
609
                                                 m_rng,
137✔
610
                                                 Botan::TLS::Server_Information("server.example.com"),
137✔
611
                                                 m_offer_version,
137✔
612
                                                 protocols_offered);
137✔
613

614
   size_t rounds = 0;
137✔
615

616
   bool client_handshake_completed = false;
137✔
617
   bool server_handshake_completed = false;
137✔
618

619
   while(true) {
1,266✔
620
      ++rounds;
1,266✔
621

622
      if(rounds > 25) {
1,266✔
623
         m_results.test_failure("Still here after many rounds, deadlock?");
×
624
         break;
625
      }
626

627
      if(!client_handshake_completed && client->is_handshake_complete()) {
1,266✔
628
         client_handshake_completed = true;
629
      }
630

631
      if(!server_handshake_completed && m_server->is_handshake_complete()) {
1,266✔
632
         server_handshake_completed = true;
633
      }
634

635
      if(client->is_handshake_complete() && client->is_active() && !client_has_written) {
1,266✔
636
         m_results.test_str_eq("client ALPN protocol", client->application_protocol(), "test/1");
125✔
637

638
         size_t sent_so_far = 0;
125✔
639
         while(sent_so_far != client_msg.size()) {
335✔
640
            const size_t left = client_msg.size() - sent_so_far;
210✔
641
            const size_t rnd12 = (m_rng->next_byte() << 4) ^ m_rng->next_byte();
210✔
642
            const size_t sending = std::min(left, rnd12);
210✔
643

644
            client->send(&client_msg[sent_so_far], sending);
210✔
645
            sent_so_far += sending;
210✔
646
         }
647
         client->send_warning_alert(Botan::TLS::Alert::NoRenegotiation);
125✔
648
         client_has_written = true;
649
      }
650

651
      if(m_server && m_server->is_handshake_complete() && m_server->is_active() && !server_has_written) {
1,266✔
652
         m_results.test_str_eq("server ALPN protocol", m_server->application_protocol(), "test/1");
131✔
653

654
         size_t sent_so_far = 0;
131✔
655
         while(sent_so_far != server_msg.size()) {
358✔
656
            const size_t left = server_msg.size() - sent_so_far;
227✔
657
            const size_t rnd12 = (m_rng->next_byte() << 4) ^ m_rng->next_byte();
227✔
658
            const size_t sending = std::min(left, rnd12);
227✔
659

660
            m_server->send(&server_msg[sent_so_far], sending);
227✔
661
            sent_so_far += sending;
227✔
662
         }
663

664
         m_server->send_warning_alert(Botan::TLS::Alert::NoRenegotiation);
131✔
665
         server_has_written = true;
666
      }
667

668
      if(!m_c2s.empty()) {
1,266✔
669
         /*
670
         * Use this as a temp value to hold the queues as otherwise they
671
         * might end up appending more in response to messages during the
672
         * handshake.
673
         */
674
         std::vector<uint8_t> input;
538✔
675
         std::swap(m_c2s, input);
538✔
676

677
         try {
538✔
678
            const size_t needed = m_server->received_data(input.data(), input.size());
538✔
679
            m_results.test_sz_eq("full packet received (server)", needed, 0);
532✔
680
         } catch(const std::exception& e) {
6✔
681
            m_caught_tls_exceptions.push_back(e.what());
12✔
682
         }
6✔
683

684
         continue;
538✔
685
      }
538✔
686

687
      if(!m_s2c.empty()) {
728✔
688
         std::vector<uint8_t> input;
466✔
689
         std::swap(m_s2c, input);
466✔
690

691
         try {
466✔
692
            const size_t needed = client->received_data(input.data(), input.size());
466✔
693
            m_results.test_sz_eq("full packet received (client)", needed, 0);
460✔
694
         } catch(const std::exception& e) {
6✔
695
            m_caught_tls_exceptions.push_back(e.what());
12✔
696
         }
6✔
697

698
         continue;
466✔
699
      }
466✔
700

701
      if(!m_client_recv.empty()) {
262✔
702
         m_results.test_bin_eq("client recv", m_client_recv, server_msg);
250✔
703
      }
704

705
      if(!m_server_recv.empty()) {
262✔
706
         m_results.test_bin_eq("server recv", m_server_recv, client_msg);
250✔
707
      }
708

709
      if(client->is_closed() && m_server->is_closed()) {
262✔
710
         break;
711
      }
712

713
      if(m_server->is_active()) {
125✔
714
         const std::vector<Botan::X509_Certificate> certs = m_server->peer_cert_chain();
125✔
715
         if(m_client_auth) {
125✔
716
            m_results.test_sz_eq("got client certs", certs.size(), 2);
4✔
717

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

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

722
            for(const Botan::X509_DN& dn : acceptable_CAs) {
12✔
723
               m_results.test_str_eq("Expected CA country field", dn.get_first_attribute("C"), "VT");
8✔
724
            }
725
         } else {
4✔
726
            m_results.test_sz_eq("no client certs", certs.size(), 0);
121✔
727
         }
728
      }
125✔
729

730
      if(!m_server_recv.empty() && !m_client_recv.empty()) {
125✔
731
         const Botan::SymmetricKey client_key = client->key_material_export("label", "context", 32);
125✔
732
         const Botan::SymmetricKey server_key = m_server->key_material_export("label", "context", 32);
125✔
733

734
         m_results.test_bin_eq("TLS key material export", client_key.bits_of(), server_key.bits_of());
250✔
735

736
         m_results.test_is_true("Client is active", client->is_active());
125✔
737
         m_results.test_is_false("Client is not closed", client->is_closed());
125✔
738
         client->close();
125✔
739
         m_results.test_is_false("Client is no longer active", client->is_active());
125✔
740
         m_results.test_is_true("Client is closed", client->is_closed_for_writing());
125✔
741
      }
250✔
742
   }
743

744
   // The receive loop above swallows exceptions raised during received_data().
745
   // Without an explicit completion assertion, a handshake that aborts via
746
   // exception still reports "all ok" because the server alert + close path
747
   // unwinds cleanly. Assert that both sides reached is_active() unless the
748
   // test was deliberately set up to drive a handshake abort.
749
   if(m_expect_handshake_completion) {
137✔
750
      if(!m_caught_tls_exceptions.empty()) {
125✔
751
         for(const auto& e : m_caught_tls_exceptions) {
×
752
            m_results.test_note("TLS exception", e);
×
753
         }
754
      }
755

756
      m_results.test_is_true("client handshake completed", client_handshake_completed);
125✔
757
      m_results.test_is_true("server handshake completed", server_handshake_completed);
125✔
758

759
      const auto& client_summary = m_client_cb->summary();
125✔
760
      const auto& server_summary = m_server_cb->summary();
125✔
761

762
      if(m_results.test_is_true("client finished handshake", client_summary.has_value())) {
125✔
763
         m_results.test_bool_eq(
125✔
764
            "client session resumption", client_summary->was_resumption(), m_expect_session_resumption);
125✔
765
      }
766

767
      if(m_results.test_is_true("server finished handshake", server_summary.has_value())) {
125✔
768
         m_results.test_bool_eq(
125✔
769
            "server session resumption", server_summary->was_resumption(), m_expect_session_resumption);
125✔
770
      }
771
   }
772

773
   m_results.end_timer();
137✔
774
}
411✔
775

776
class Test_Policy final : public Botan::TLS::Text_Policy {
36✔
777
   public:
778
      Test_Policy() : Text_Policy("") {}
36✔
779

780
      size_t dtls_initial_timeout() const override { return 1; }
130✔
781

782
      size_t dtls_maximum_timeout() const override { return 8; }
130✔
783

784
      size_t minimum_rsa_bits() const override { return 1024; }
8✔
785

786
      size_t minimum_signature_strength() const override { return 80; }
67✔
787
};
788

789
/**
790
 * This mocks a custom ECDH adapter class that essentially just wraps an
791
 * ordinary ECDH key to mimic a typical hardware-based ECDH key.
792
 */
793
class HardwareEcdhKey final : public Botan::PK_Key_Agreement_Key {
794
   public:
795
      HardwareEcdhKey(Botan::EC_Group group,
6✔
796
                      Botan::RandomNumberGenerator& rng,
797
                      Botan::EC_Point_Format public_key_format) :
6✔
798
            m_group(std::move(group)),
6✔
799
            m_public_key_format(public_key_format),
6✔
800
            m_key(std::make_unique<Botan::ECDH_PrivateKey>(rng, m_group)) {}
6✔
801

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

804
      size_t estimated_strength() const override { return m_key->estimated_strength(); }
×
805

806
      bool supports_operation(Botan::PublicKeyOperation op) const override {
×
807
         return op == Botan::PublicKeyOperation::KeyAgreement;
×
808
      }
809

810
      bool check_key(Botan::RandomNumberGenerator& rng, bool strong) const override {
×
811
         return m_key->check_key(rng, strong);
×
812
      }
813

814
      size_t key_length() const override { return m_key->key_length(); }
×
815

816
      Botan::AlgorithmIdentifier algorithm_identifier() const override {
×
817
         throw Botan::Not_Implemented("TLS should never call Public_Key::algorithm_identifier()");
×
818
      }
819

820
      std::vector<uint8_t> raw_public_key_bits() const override { return m_key->public_value(m_public_key_format); }
6✔
821

822
      std::vector<uint8_t> public_key_bits() const override {
×
823
         throw Botan::Not_Implemented("TLS should never call Public_Key::public_key_bits()");
×
824
         // ... instead it should always call raw_public_key_bits() to get the public value
825
         // in the format required by TLS.
826
      }
827

828
      std::vector<uint8_t> public_value() const override {
×
829
         throw Botan::Not_Implemented("TLS should never call Public_Key::public_value()");
×
830
         // ... instead it should always call raw_public_key_bits() to get the public value
831
         // in the format required by TLS.
832
      }
833

834
      Botan::secure_vector<uint8_t> private_key_bits() const override {
×
835
         throw Botan::Not_Implemented("This mocks a hardware key and thus hides its private bits");
×
836
      }
837

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

840
      std::unique_ptr<Botan::Private_Key> generate_another(Botan::RandomNumberGenerator& rng) const override {
×
841
         return std::make_unique<HardwareEcdhKey>(m_group, rng, m_public_key_format);
×
842
      }
843

844
      std::unique_ptr<Botan::PK_Ops::Key_Agreement> create_key_agreement_op(Botan::RandomNumberGenerator& rng,
6✔
845
                                                                            std::string_view params,
846
                                                                            std::string_view provider) const override {
847
         return m_key->create_key_agreement_op(rng, params, provider);
6✔
848
      }
849

850
   private:
851
      Botan::EC_Group m_group;
852
      Botan::EC_Point_Format m_public_key_format;
853
      std::unique_ptr<Botan::ECDH_PrivateKey> m_key;
854
};
855

856
class TLS_Unit_Tests final : public Test {
1✔
857
   private:
858
      static std::vector<Botan::TLS::Protocol_Version> legacy_versions() {
28✔
859
         return {
28✔
860
   #if defined(BOTAN_HAS_TLS_12)
861
            Botan::TLS::Protocol_Version::TLS_V12, Botan::TLS::Protocol_Version::DTLS_V12,
862
   #endif
863
         };
14✔
864
      }
865

866
      static std::vector<Botan::TLS::Protocol_Version> available_versions() {
36✔
867
         return {
36✔
868
   #if defined(BOTAN_HAS_TLS_12)
869
            Botan::TLS::Protocol_Version::TLS_V12, Botan::TLS::Protocol_Version::DTLS_V12,
870
   #endif
871
         };
15✔
872
      }
873

874
      static void enable_versions([[maybe_unused]] const std::shared_ptr<Test_Policy>& policy,
28✔
875
                                  [[maybe_unused]] std::span<const Botan::TLS::Protocol_Version> versions) {
876
         for(const auto& version : versions) {
84✔
877
            if(version == Botan::TLS::Protocol_Version::TLS_V12) {
56✔
878
               policy->set("allow_tls12", "true");
56✔
879
            } else if(version == Botan::TLS::Protocol_Version::DTLS_V12) {
28✔
880
               policy->set("allow_dtls12", "true");
56✔
881
            } else if(version == Botan::TLS::Protocol_Version::TLS_V13) {
×
882
               policy->set("allow_tls13", "true");
×
883
            }
884
         }
885
      }
28✔
886

887
      static std::shared_ptr<Botan::TLS::Session_Manager> make_session_manager(
118✔
888
         const std::shared_ptr<Botan::RandomNumberGenerator>& rng) {
889
   #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
890
         return std::make_shared<Botan::TLS::Session_Manager_SQLite>("geheimes passwort", rng, ":memory:", 5);
118✔
891
   #else
892
         return std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
893
   #endif
894
      }
895

896
      static void test_with_policy(const std::string& test_descr,
30✔
897
                                   std::vector<Test::Result>& results,
898
                                   const std::shared_ptr<Credentials_Manager_Test>& creds,
899
                                   const std::vector<Botan::TLS::Protocol_Version>& versions,
900
                                   const std::shared_ptr<const Botan::TLS::Policy>& policy,
901
                                   std::shared_ptr<Botan::RandomNumberGenerator>& rng,
902
                                   bool client_auth = false) {
903
         try {
30✔
904
            for(const auto& version : versions) {
89✔
905
               auto client_ses = make_session_manager(rng);
59✔
906
               auto server_ses = make_session_manager(rng);
59✔
907

908
               TLS_Handshake_Test test(version.to_string() + " " + test_descr,
118✔
909
                                       version,
910
                                       creds,
911
                                       policy,
912
                                       policy,
913
                                       rng,
914
                                       client_ses,
915
                                       server_ses,
916
                                       client_auth);
118✔
917
               test.go();
59✔
918
               results.push_back(test.results());
59✔
919

920
               TLS_Handshake_Test test_resumption(version.to_string() + " " + test_descr,
177✔
921
                                                  version,
922
                                                  creds,
923
                                                  policy,
924
                                                  policy,
925
                                                  rng,
926
                                                  client_ses,
927
                                                  server_ses,
928
                                                  client_auth);
59✔
929
               test_resumption.expect_session_resumption();
59✔
930
               test_resumption.go();
59✔
931
               results.push_back(test_resumption.results());
59✔
932
            }
177✔
933
         } catch(std::exception& e) {
×
934
            results.push_back(Test::Result::Failure(test_descr, e.what()));
×
935
         }
×
936
      }
30✔
937

938
      /**
939
       * Legacy versions are TLS 1.2 and DTLS 1.2, which allow combinations of
940
       * various ciphers, MACs and key exchange methods that aren't supported
941
       * in TLS 1.3 anymore.
942
       */
943
      static void test_legacy_versions(const std::string& test_descr,
14✔
944
                                       std::vector<Test::Result>& results,
945
                                       const std::shared_ptr<Credentials_Manager_Test>& creds,
946
                                       std::shared_ptr<Botan::RandomNumberGenerator>& rng,
947
                                       const std::string& kex_policy,
948
                                       const std::string& cipher_policy,
949
                                       const std::string& mac_policy = "AEAD",
950
                                       bool etm_policy = true,
951
                                       bool client_auth = false) {
952
         auto policy = std::make_shared<Test_Policy>();
14✔
953
         policy->set("ciphers", cipher_policy);
14✔
954
         policy->set("macs", mac_policy);
14✔
955
         policy->set("key_exchange_methods", kex_policy);
14✔
956
         policy->set("negotiate_encrypt_then_mac", etm_policy ? "true" : "false");
32✔
957

958
         enable_versions(policy, legacy_versions());
14✔
959

960
         if(kex_policy.find("RSA") != std::string::npos) {
14✔
961
            policy->set("signature_methods", "IMPLICIT");
10✔
962
         }
963

964
         return test_with_policy(test_descr, results, creds, legacy_versions(), policy, rng, client_auth);
28✔
965
      }
14✔
966

967
      /**
968
       * Modern versions are both TLS 1.2 and 1.3, as well as their DTLS
969
       * counterparts, but with the restrictions that TLS 1.3 imposes on
970
       * ciphers, MACs and key exchange methods.
971
       */
972
      static void test_modern_versions(const std::string& test_descr,
7✔
973
                                       std::vector<Test::Result>& results,
974
                                       const std::shared_ptr<Credentials_Manager_Test>& creds,
975
                                       std::shared_ptr<Botan::RandomNumberGenerator>& rng,
976
                                       const std::string& kex_policy,
977
                                       const std::string& cipher_policy,
978
                                       const std::string& mac_policy = "AEAD",
979
                                       bool client_auth = false) {
980
         const std::map<std::string, std::string> no_extra_policies;
7✔
981
         return test_modern_versions(
7✔
982
            test_descr, results, creds, rng, kex_policy, cipher_policy, mac_policy, no_extra_policies, client_auth);
7✔
983
      }
7✔
984

985
      static void test_modern_versions(const std::string& test_descr,
14✔
986
                                       std::vector<Test::Result>& results,
987
                                       const std::shared_ptr<Credentials_Manager_Test>& creds,
988
                                       std::shared_ptr<Botan::RandomNumberGenerator>& rng,
989
                                       const std::string& kex_policy,
990
                                       const std::string& cipher_policy,
991
                                       const std::string& mac_policy,
992
                                       const std::map<std::string, std::string>& extra_policies,
993
                                       bool client_auth = false) {
994
         auto policy = std::make_shared<Test_Policy>();
14✔
995
         policy->set("ciphers", cipher_policy);
14✔
996
         policy->set("macs", mac_policy);
14✔
997
         policy->set("key_exchange_methods", kex_policy);
14✔
998

999
         enable_versions(policy, available_versions());
14✔
1000

1001
         if(kex_policy.find("RSA") != std::string::npos) {
14✔
1002
            policy->set("signature_methods", "IMPLICIT");
×
1003
         }
1004

1005
         for(const auto& kv : extra_policies) {
21✔
1006
            policy->set(kv.first, kv.second);
7✔
1007
         }
1008

1009
         return test_with_policy(test_descr, results, creds, available_versions(), policy, rng, client_auth);
28✔
1010
      }
14✔
1011

1012
      void test_session_established_abort(std::vector<Test::Result>& results,
1✔
1013
                                          std::shared_ptr<Credentials_Manager_Test> creds,
1014
                                          std::shared_ptr<Botan::RandomNumberGenerator> rng) {
1015
         auto policy = std::make_shared<Test_Policy>();
1✔
1016
         auto noop_session_manager = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1017

1018
         auto client_aborts = [&](const std::exception_ptr& ex, Botan::TLS::Alert expected_server_alert) {
4✔
1019
            for(const auto version : available_versions()) {
9✔
1020
               TLS_Handshake_Test test("Client aborts in tls_session_established with " +
12✔
1021
                                          expected_server_alert.type_string() + ": " + version.to_string(),
24✔
1022
                                       version,
1023
                                       creds,
1024
                                       policy,
1025
                                       policy,
1026
                                       rng,
1027
                                       noop_session_manager,
1028
                                       noop_session_manager,
1029
                                       false);
30✔
1030
               test.set_custom_client_tls_session_established_callback(
12✔
1031
                  [=](const auto&) { std::rethrow_exception(ex); });
42✔
1032
               test.set_server_expected_handshake_alert(expected_server_alert);
6✔
1033
               test.expect_handshake_failure();
6✔
1034

1035
               test.go();
6✔
1036
               results.push_back(test.results());
6✔
1037
            }
9✔
1038
         };
3✔
1039

1040
         auto server_aborts = [&](const std::exception_ptr& ex, Botan::TLS::Alert expected_server_alert) {
4✔
1041
            for(const auto version : available_versions()) {
9✔
1042
               TLS_Handshake_Test test("Server aborts in tls_session_established with " +
12✔
1043
                                          expected_server_alert.type_string() + ": " + version.to_string(),
24✔
1044
                                       version,
1045
                                       creds,
1046
                                       policy,
1047
                                       policy,
1048
                                       rng,
1049
                                       noop_session_manager,
1050
                                       noop_session_manager,
1051
                                       false);
30✔
1052
               test.set_custom_server_tls_session_established_callback(
12✔
1053
                  [=](const auto&) { std::rethrow_exception(ex); });
42✔
1054
               test.set_client_expected_handshake_alert(expected_server_alert);
6✔
1055
               test.expect_handshake_failure();
6✔
1056

1057
               test.go();
6✔
1058
               results.push_back(test.results());
6✔
1059
            }
9✔
1060
         };
3✔
1061

1062
         client_aborts(std::make_exception_ptr(
3✔
1063
                          Botan::TLS::TLS_Exception(Botan::TLS::Alert::AccessDenied, "some test TLS exception")),
1✔
1064
                       Botan::TLS::Alert::AccessDenied);
1065
         client_aborts(std::make_exception_ptr(Botan::Invalid_Authentication_Tag("some symmetric crypto failed :o)")),
2✔
1066
                       Botan::TLS::Alert::BadRecordMac);
1067
         client_aborts(std::make_exception_ptr(std::runtime_error("something strange happened")),
2✔
1068
                       Botan::TLS::Alert::InternalError);
1069

1070
         server_aborts(std::make_exception_ptr(
3✔
1071
                          Botan::TLS::TLS_Exception(Botan::TLS::Alert::AccessDenied, "some server test TLS exception")),
1✔
1072
                       Botan::TLS::Alert::AccessDenied);
1073
         server_aborts(std::make_exception_ptr(
2✔
1074
                          Botan::Invalid_Authentication_Tag("some symmetric crypto failed in the server :o)")),
1✔
1075
                       Botan::TLS::Alert::BadRecordMac);
1076
         server_aborts(std::make_exception_ptr(std::runtime_error("something strange happened in the server")),
2✔
1077
                       Botan::TLS::Alert::InternalError);
1078
      }
2✔
1079

1080
      void test_custom_ecdh_provider(std::vector<Test::Result>& results,
1✔
1081
                                     const std::shared_ptr<Credentials_Manager_Test>& creds,
1082
                                     const std::shared_ptr<Botan::RandomNumberGenerator>& rng) {
1083
         auto noop_session_manager = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1084

1085
         const auto groups = {
1✔
1086
            Botan::TLS::Group_Params::SECP256R1,
1087
            Botan::TLS::Group_Params::BRAINPOOL256R1,
1088
            Botan::TLS::Group_Params::BRAINPOOL512R1,
1089
         };
1✔
1090

1091
         const auto versions = available_versions();
1✔
1092

1093
         const auto groups_and_versions = [&]() {
2✔
1094
            std::vector<std::pair<Botan::TLS::Group_Params, Botan::TLS::Protocol_Version>> out;
1✔
1095
            for(const auto& group : groups) {
4✔
1096
               for(const auto& version : versions) {
9✔
1097
                  out.emplace_back(group, version);
6✔
1098
               }
1099
            }
1100
            return out;
1✔
1101
         };
×
1102

1103
         for(const auto& [ecdh_group, version] : groups_and_versions()) {
7✔
1104
            if(!Botan::EC_Group::supports_named_group(ecdh_group.to_string().value())) {
18✔
1105
               continue;
×
1106
            }
1107

1108
            auto policy = std::make_shared<Test_Policy>();
6✔
1109
            policy->set("groups", "0x" + Botan::hex_encode(Botan::store_be(ecdh_group.wire_code())));
18✔
1110

1111
            TLS_Handshake_Test test("Client uses a custom ECDH provider for " + ecdh_group.to_string().value() +
36✔
1112
                                       " in " + version.to_string(),
18✔
1113
                                    version,
1114
                                    creds,
1115
                                    policy,
1116
                                    policy,
1117
                                    rng,
1118
                                    noop_session_manager,
1119
                                    noop_session_manager,
1120
                                    false);
30✔
1121

1122
            auto& test_results = test.results();
6✔
1123

1124
            bool generator_called = false;
6✔
1125
            bool agreement_called = false;
6✔
1126

1127
            test.set_custom_client_tls_generate_ephemeral_ecdh_key_callback(
×
1128
               [&](const Botan::TLS::Group_Params& group,
6✔
1129
                   Botan::RandomNumberGenerator& clbk_rng,
1130
                   Botan::EC_Point_Format format) -> std::unique_ptr<Botan::PK_Key_Agreement_Key> {
1131
                  generator_called = true;
6✔
1132
                  test_results.require("tls_generate_ephemeral_ecdh_key_callback called for ECDH",
6✔
1133
                                       group.is_ecdh_named_curve());
6✔
1134
                  const auto ec_group = Botan::EC_Group::from_name(group.to_string().value());
12✔
1135
                  return std::make_unique<HardwareEcdhKey>(ec_group, clbk_rng, format);
6✔
1136
               });
6✔
1137

1138
            test.set_custom_client_tls_ephemeral_key_agreement_callback(
×
1139
               [&](const std::variant<Botan::TLS::Group_Params, Botan::DL_Group>& group,
6✔
1140
                   const Botan::PK_Key_Agreement_Key& private_key,
1141
                   const std::vector<uint8_t>& peer_public_value,
1142
                   Botan::RandomNumberGenerator& clbk_rng,
1143
                   const Botan::TLS::Policy&) -> Botan::secure_vector<uint8_t> {
1144
                  agreement_called = true;
6✔
1145

1146
                  const auto* group_params = std::get_if<Botan::TLS::Group_Params>(&group);
6✔
1147
                  test_results.require("tls_ephemeral_key_agreement_callback called with a TLS group",
6✔
1148
                                       group_params != nullptr);
1149
                  test_results.require("tls_ephemeral_key_agreement_callback called with an ECDH group",
6✔
1150
                                       group_params->is_ecdh_named_curve());
6✔
1151

1152
                  const auto* hwkey = dynamic_cast<const HardwareEcdhKey*>(&private_key);
6✔
1153
                  test_results.require("tls_ephemeral_key_agreement_callback called with a HardwareEcdhKey",
6✔
1154
                                       hwkey != nullptr);
1155

1156
                  const Botan::PK_Key_Agreement ka(private_key, clbk_rng, "Raw");
6✔
1157
                  return ka.derive_key(0 /* no KDF */, peer_public_value).bits_of();
18✔
1158
               });
6✔
1159

1160
            test.go();
6✔
1161
            test.results().test_is_true("custom generation was used", generator_called);
6✔
1162
            test.results().test_is_true("custom agreement was used", agreement_called);
6✔
1163
            results.push_back(test.results());
6✔
1164
         }
13✔
1165
      }
2✔
1166

1167
      class CustomKDF : public Botan::KDF {
8✔
1168
         public:
1169
            std::string name() const override { return "CustomKDF"; }
×
1170

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

1173
            // always returns a static master secret
1174
            void perform_kdf(std::span<uint8_t> key,
10✔
1175
                             std::span<const uint8_t> /*secret*/,
1176
                             std::span<const uint8_t> /*salt*/,
1177
                             std::span<const uint8_t> /*label*/) const override {
1178
               std::fill(key.begin(), key.end(), 0xAA);
10✔
1179
            }
10✔
1180
      };
1181

1182
      void test_custom_kdf_provider(std::vector<Test::Result>& results,
1✔
1183
                                    const std::shared_ptr<Credentials_Manager_Test>& creds,
1184
                                    const std::shared_ptr<Botan::RandomNumberGenerator>& rng) {
1185
         auto noop_session_manager = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1186

1187
         auto policy = std::make_shared<Test_Policy>();
1✔
1188

1189
         TLS_Handshake_Test test("Client and Server use a custom KDF provider in TLS 1.2",
1✔
1190
                                 Botan::TLS::Protocol_Version::TLS_V12,
1191
                                 creds,
1192
                                 policy,
1193
                                 policy,
1194
                                 rng,
1195
                                 noop_session_manager,
1196
                                 noop_session_manager,
1197
                                 false);
5✔
1198

1199
         bool client_custom_kdf_called = false;
1✔
1200
         bool server_custom_kdf_called = false;
1✔
1201

1202
         test.set_client_custom_kdf_callback([&](std::string_view) -> std::unique_ptr<Botan::KDF> {
1✔
1203
            client_custom_kdf_called = true;
4✔
1204
            return std::make_unique<CustomKDF>();
4✔
1205
         });
1206

1207
         test.set_server_custom_kdf_callback([&](std::string_view) -> std::unique_ptr<Botan::KDF> {
1✔
1208
            server_custom_kdf_called = true;
4✔
1209
            return std::make_unique<CustomKDF>();
4✔
1210
         });
1211

1212
         test.go();
1✔
1213

1214
         test.results().test_is_true("custom KDF was used on client side", client_custom_kdf_called);
1✔
1215
         test.results().test_is_true("custom KDF was used on server side", server_custom_kdf_called);
1✔
1216
         results.push_back(test.results());
1✔
1217
      }
3✔
1218

1219
   public:
1220
      std::vector<Test::Result> run() override {
1✔
1221
         std::vector<Test::Result> results;
1✔
1222

1223
         auto rng = Test::new_shared_rng(this->test_name());
1✔
1224

1225
         auto creds = create_creds(*rng);
1✔
1226
         if(!creds) {
1✔
1227
            // Credentials manager creation failed, likely no EC group available
1228
            // Skip this test entirely
1229
            return {Test::Result::Note("TLS unit tests", "Skipping due to missing credentials")};
×
1230
         }
1231

1232
   #if defined(BOTAN_HAS_TLS_CBC)
1233
         for(const bool etm_setting : {false, true}) {
3✔
1234
            test_legacy_versions("AES-128 RSA", results, creds, rng, "RSA", "AES-128", "SHA-256 SHA-1", etm_setting);
4✔
1235
            test_legacy_versions("AES-128 ECDH", results, creds, rng, "ECDH", "AES-128", "SHA-256 SHA-1", etm_setting);
4✔
1236

1237
      #if defined(BOTAN_HAS_DES)
1238
            test_legacy_versions("3DES RSA", results, creds, rng, "RSA", "3DES", "SHA-1", etm_setting);
4✔
1239
            test_legacy_versions("3DES ECDH", results, creds, rng, "ECDH", "3DES", "SHA-1", etm_setting);
4✔
1240
      #endif
1241
         }
1242

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

1245
   #endif
1246

1247
   #if defined(BOTAN_HAS_TLS_NULL)
1248
         test_legacy_versions("NULL PSK", results, creds, rng, "PSK", "NULL", "SHA-256");
2✔
1249
   #endif
1250

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

1254
   #if defined(BOTAN_HAS_TLS_12)
1255
         auto suiteb_128 = std::make_shared<Botan::TLS::NSA_Suite_B_128>();
1✔
1256
         test_with_policy("Suite B", results, creds, {Botan::TLS::Protocol_Version::TLS_V12}, suiteb_128, rng);
2✔
1257
   #endif
1258

1259
         test_legacy_versions("AES-128/GCM RSA", results, creds, rng, "RSA", "AES-128/GCM");
2✔
1260
         test_modern_versions("AES-128/GCM ECDH", results, creds, rng, "ECDH", "AES-128/GCM");
2✔
1261

1262
         test_modern_versions(
3✔
1263
            "AES-128/GCM ECDH RSA", results, creds, rng, "ECDH", "AES-128/GCM", "AEAD", {{"signature_methods", "RSA"}});
1264

1265
         test_modern_versions("AES-128/GCM ECDH no OCSP",
3✔
1266
                              results,
1267
                              creds,
1268
                              rng,
1269
                              "ECDH",
1270
                              "AES-128/GCM",
1271
                              "AEAD",
1272
                              {{"support_cert_status_message", "false"}});
1273

1274
   #if defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_AEAD_GCM)
1275
         test_legacy_versions("Camellia-128/GCM ECDH", results, creds, rng, "ECDH", "Camellia-128/GCM", "AEAD");
2✔
1276
   #endif
1277

1278
   #if defined(BOTAN_HAS_ARIA)
1279
         test_legacy_versions("ARIA/GCM ECDH", results, creds, rng, "ECDH", "ARIA-128/GCM", "AEAD");
2✔
1280
   #endif
1281

1282
         // Note: TLS 1.3 actually does not support point compression and will
1283
         //       simply ignore this policy configuration. Nevertheless, the
1284
         //       handshake should complete successfully.
1285
         test_modern_versions("AES-128/GCM point compression",
3✔
1286
                              results,
1287
                              creds,
1288
                              rng,
1289
                              "ECDH",
1290
                              "AES-128/GCM",
1291
                              "AEAD",
1292
                              {{"use_ecc_point_compression", "true"}});
1293
         test_modern_versions(
3✔
1294
            "AES-256/GCM p521", results, creds, rng, "ECDH", "AES-256/GCM", "AEAD", {{"groups", "secp521r1"}});
1295

1296
         if(Botan::EC_Group::supports_named_group("brainpool256r1")) {
1✔
1297
            test_modern_versions("AES-128/GCM bp256r1",
3✔
1298
                                 results,
1299
                                 creds,
1300
                                 rng,
1301
                                 "ECDH",
1302
                                 "AES-128/GCM",
1303
                                 "AEAD",
1304
                                 {{"groups", "brainpool256r1"}});
1305
         }
1306

1307
   #if defined(BOTAN_HAS_X25519)
1308
         test_modern_versions("AES-128/GCM x25519", results, creds, rng, "ECDH", "AES-128/GCM", "AEAD", {{
3✔
1309
                                 "groups",
1310
                                 "x25519"
1311
                              }});
1312
   #endif
1313

1314
         test_modern_versions("AES-128/GCM FFDHE-2048",
3✔
1315
                              results,
1316
                              creds,
1317
                              rng,
1318
                              "DH",
1319
                              "AES-128/GCM",
1320
                              "AEAD",
1321
                              {{"groups", "ffdhe/ietf/2048"}});
1322

1323
         auto creds_with_client_cert = create_creds(*rng, true);
1✔
1324
         if(creds_with_client_cert) {
1✔
1325
            test_modern_versions(
2✔
1326
               "AES-256/GCM client certs", results, creds_with_client_cert, rng, "ECDH", "AES-256/GCM", "AEAD", true);
1327
         }
1328

1329
   #if defined(BOTAN_HAS_AEAD_OCB)
1330
         test_legacy_versions("AES-256/OCB ECDH", results, creds, rng, "ECDH", "AES-256/OCB(12)");
2✔
1331
   #endif
1332

1333
   #if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)
1334
         test_modern_versions("ChaCha20Poly1305 ECDH", results, creds, rng, "ECDH", "ChaCha20Poly1305");
2✔
1335
   #endif
1336

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

1339
   #if defined(BOTAN_HAS_AEAD_CCM)
1340
         test_modern_versions("AES-128/CCM PSK", results, creds, rng, "PSK", "AES-128/CCM");
2✔
1341
         test_modern_versions("AES-128/CCM-8 PSK", results, creds, rng, "PSK", "AES-128/CCM(8)");
2✔
1342
   #endif
1343

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

1346
         // Test with a custom curve
1347

1348
         /*
1349
         * Disabled, currently broken.
1350
         *
1351
         * TLS 1.2 server side currently can't negotiate application-specific group codes.
1352
         *
1353
         * The current flow is that choose_ciphersuite computes have_shared_ecc_curve via
1354
         * Client_Hello::supported_ecc_curves(), which goes through Supported_Groups::ec_groups()
1355
         * and filters by Group_Params::is_pure_ecc_group(). That predicate only accepts a hardcoded
1356
         * set of known groups, so a custom code such as 0xFEE1 is silently dropped and the server
1357
         * throws "Can't agree on a ciphersuite with client".
1358
         */
1359
         const bool disabled = true;
1✔
1360
         if(Botan::EC_Group::supports_application_specific_group() && !disabled) {
1✔
1361
            /*
1362
            * First register a curve, in this case numsp256d1
1363
            */
1364
            const Botan::BigInt p("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43");
1365
            const Botan::BigInt a("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40");
1366
            const Botan::BigInt b("0x25581");
1367
            const Botan::BigInt order("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE43C8275EA265C6020AB20294751A825");
1368

1369
            const Botan::BigInt g_x("0x01");
1370
            const Botan::BigInt g_y("0x696F1853C1E466D7FC82C96CCEEEDD6BD02C2F9375894EC10BF46306C2B56C77");
1371

1372
            const Botan::OID oid("1.3.6.1.4.1.25258.4.1");
1373

1374
            Botan::OID::register_oid(oid, "numsp256d1");
1375

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

1379
            test_modern_versions("AES-256/GCM numsp256d1",
1380
                                 results,
1381
                                 creds,
1382
                                 rng,
1383
                                 "ECDH",
1384
                                 "AES-256/GCM",
1385
                                 "AEAD",
1386
                                 {{"groups", "0xFEE1"}, {"minimum_ecdh_group_size", "112"}});
1387
         }
1388

1389
         // Test connection abort by the application
1390
         // by throwing in Callbacks::tls_session_established()
1391

1392
         test_session_established_abort(results, creds, rng);
2✔
1393

1394
         // Use tls12_generate_ephemeral_ecdh_key() / tls_generate_ephemeral_key() to
1395
         // establish a custom ECDH provider mocking some hardware adapter for key
1396
         // generation and tls_ephemeral_key_agreement() for performing the key agreement.
1397

1398
         test_custom_ecdh_provider(results, creds, rng);
1✔
1399

1400
         // Test using a custom KDF instead of the original TLS 1.2 KDF
1401
         // (this is a TLS 1.2 specific feature)
1402

1403
   #if defined(BOTAN_HAS_TLS_12)
1404
         test_custom_kdf_provider(results, creds, rng);
1✔
1405
   #endif
1406

1407
         return results;
1✔
1408
      }
12✔
1409
};
1410

1411
BOTAN_REGISTER_TEST("tls", "unit_tls", TLS_Unit_Tests);
1412

1413
   #if defined(BOTAN_HAS_TLS_12)
1414

1415
class DTLS_Reconnection_Test : public Test {
1✔
1416
   public:
1417
      std::vector<Test::Result> run() override {
1✔
1418
         class Test_Callbacks : public Botan::TLS::Callbacks {
4✔
1419
            public:
1420
               Test_Callbacks(Test::Result& results, std::vector<uint8_t>& outbound, std::vector<uint8_t>& recv_buf) :
3✔
1421
                     m_results(results), m_outbound(outbound), m_recv(recv_buf) {}
3✔
1422

1423
               void tls_emit_data(std::span<const uint8_t> bits) override {
28✔
1424
                  m_outbound.insert(m_outbound.end(), bits.begin(), bits.end());
28✔
1425
               }
28✔
1426

1427
               void tls_record_received(uint64_t /*seq*/, std::span<const uint8_t> bits) override {
4✔
1428
                  m_recv.insert(m_recv.end(), bits.begin(), bits.end());
4✔
1429
               }
4✔
1430

1431
               void tls_alert(Botan::TLS::Alert /*alert*/) override {
×
1432
                  // ignore
1433
               }
×
1434

1435
               void tls_session_established(const Botan::TLS::Session_Summary& /*session*/) override {
4✔
1436
                  m_results.test_success("Established a session");
4✔
1437
               }
4✔
1438

1439
            private:
1440
               Test::Result& m_results;
1441
               std::vector<uint8_t>& m_outbound;
1442
               std::vector<uint8_t>& m_recv;
1443
         };
1444

1445
         class Credentials_PSK : public Botan::Credentials_Manager {
3✔
1446
            public:
1447
               Botan::SymmetricKey psk(const std::string& type,
8✔
1448
                                       const std::string& context,
1449
                                       const std::string& /*identity*/) override {
1450
                  if(type == "tls-server" && context == "session-ticket") {
8✔
1451
                     return Botan::SymmetricKey("AABBCCDDEEFF012345678012345678");
×
1452
                  }
1453

1454
                  if(type == "tls-server" && context == "dtls-cookie-secret") {
8✔
1455
                     return Botan::SymmetricKey("4AEA5EAD279CADEB537A594DA0E9DE3A");
4✔
1456
                  }
1457

1458
                  if(context == "localhost" && type == "tls-client") {
4✔
1459
                     return Botan::SymmetricKey("20B602D1475F2DF888FCB60D2AE03AFD");
2✔
1460
                  }
1461

1462
                  if(context == "localhost" && type == "tls-server") {
2✔
1463
                     return Botan::SymmetricKey("20B602D1475F2DF888FCB60D2AE03AFD");
2✔
1464
                  }
1465

1466
                  throw Test_Error("No PSK set for " + type + "/" + context);
×
1467
               }
1468
         };
1469

1470
         class Datagram_PSK_Policy : public Botan::TLS::Policy {
5✔
1471
            public:
1472
               std::vector<std::string> allowed_macs() const override { return std::vector<std::string>({"AEAD"}); }
216✔
1473

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

1476
               bool allow_tls12() const override { return false; }
6✔
1477

1478
               bool allow_dtls12() const override { return true; }
19✔
1479

1480
               bool allow_dtls_epoch0_restart() const override { return true; }
8✔
1481
         };
1482

1483
         Test::Result result("DTLS reconnection");
1✔
1484

1485
         auto rng = Test::new_shared_rng(this->test_name());
1✔
1486

1487
         auto server_policy = std::make_shared<Datagram_PSK_Policy>();
1✔
1488
         auto client_policy = std::make_shared<Datagram_PSK_Policy>();
1✔
1489
         auto creds = std::make_shared<Credentials_PSK>();
1✔
1490
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
1491
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1492

1493
         std::vector<uint8_t> s2c;
1✔
1494
         std::vector<uint8_t> server_recv;
1✔
1495
         auto server_callbacks = std::make_shared<Test_Callbacks>(result, s2c, server_recv);
1✔
1496
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, server_policy, rng, true);
4✔
1497

1498
         std::vector<uint8_t> c1_c2s;
1✔
1499
         std::vector<uint8_t> client1_recv;
1✔
1500
         auto client1_callbacks = std::make_shared<Test_Callbacks>(result, c1_c2s, client1_recv);
1✔
1501
         Botan::TLS::Client client1(client1_callbacks,
1✔
1502
                                    client_sessions,
1503
                                    creds,
1504
                                    client_policy,
1505
                                    rng,
1506
                                    Botan::TLS::Server_Information("localhost"),
1✔
1507
                                    Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
1508

1509
         bool c1_to_server_sent = false;
1✔
1510
         const bool server_to_c1_sent = false;
1✔
1511

1512
         const std::vector<uint8_t> c1_to_server_magic(16, 0xC1);
1✔
1513
         const std::vector<uint8_t> server_to_c1_magic(16, 0x42);
1✔
1514

1515
         size_t c1_rounds = 0;
1✔
1516
         for(;;) {
10✔
1517
            c1_rounds++;
10✔
1518

1519
            if(c1_rounds > 64) {
10✔
1520
               result.test_failure("Still spinning in client1 loop after 64 rounds");
×
1521
               return {result};
×
1522
            }
1523

1524
            if(!c1_c2s.empty()) {
10✔
1525
               std::vector<uint8_t> input;
4✔
1526
               std::swap(c1_c2s, input);
4✔
1527
               server.received_data(input.data(), input.size());
4✔
1528
               continue;
4✔
1529
            }
4✔
1530

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

1538
            if(!c1_to_server_sent && client1.is_active()) {
2✔
1539
               client1.send(c1_to_server_magic);
1✔
1540
               c1_to_server_sent = true;
1✔
1541
            }
1542

1543
            if(!server_to_c1_sent && server.is_active()) {
2✔
1544
               server.send(server_to_c1_magic);
2✔
1545
            }
1546

1547
            if(!server_recv.empty() && !client1_recv.empty()) {
2✔
1548
               result.test_bin_eq("Expected message from client1", server_recv, c1_to_server_magic);
1✔
1549
               result.test_bin_eq("Expected message to client1", client1_recv, server_to_c1_magic);
1✔
1550
               break;
1✔
1551
            }
1552
         }
1553

1554
         // Now client1 "goes away" (goes silent) and new client
1555
         // connects to same server context (ie due to reuse of client source port)
1556
         // See RFC 6347 section 4.2.8
1557

1558
         server_recv.clear();
1✔
1559
         s2c.clear();
1✔
1560

1561
         std::vector<uint8_t> c2_c2s;
1✔
1562
         std::vector<uint8_t> client2_recv;
1✔
1563
         auto client2_callbacks = std::make_shared<Test_Callbacks>(result, c2_c2s, client2_recv);
1✔
1564
         Botan::TLS::Client client2(client2_callbacks,
1✔
1565
                                    client_sessions,
1566
                                    creds,
1567
                                    client_policy,
1568
                                    rng,
1569
                                    Botan::TLS::Server_Information("localhost"),
1✔
1570
                                    Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
1571

1572
         bool c2_to_server_sent = false;
1✔
1573
         const bool server_to_c2_sent = false;
1✔
1574

1575
         const std::vector<uint8_t> c2_to_server_magic(16, 0xC2);
1✔
1576
         const std::vector<uint8_t> server_to_c2_magic(16, 0x66);
1✔
1577

1578
         size_t c2_rounds = 0;
1✔
1579

1580
         for(;;) {
10✔
1581
            c2_rounds++;
10✔
1582

1583
            if(c2_rounds > 64) {
10✔
1584
               result.test_failure("Still spinning in client2 loop after 64 rounds");
×
1585
               return {result};
×
1586
            }
1587

1588
            if(!c2_c2s.empty()) {
10✔
1589
               std::vector<uint8_t> input;
4✔
1590
               std::swap(c2_c2s, input);
4✔
1591
               server.received_data(input.data(), input.size());
4✔
1592
               continue;
4✔
1593
            }
4✔
1594

1595
            if(!s2c.empty()) {
6✔
1596
               std::vector<uint8_t> input;
4✔
1597
               std::swap(s2c, input);
4✔
1598
               client2.received_data(input.data(), input.size());
4✔
1599
               continue;
4✔
1600
            }
4✔
1601

1602
            if(!c2_to_server_sent && client2.is_active()) {
2✔
1603
               client2.send(c2_to_server_magic);
1✔
1604
               c2_to_server_sent = true;
1✔
1605
            }
1606

1607
            if(!server_to_c2_sent && server.is_active()) {
2✔
1608
               server.send(server_to_c2_magic);
2✔
1609
            }
1610

1611
            if(!server_recv.empty() && !client2_recv.empty()) {
2✔
1612
               result.test_bin_eq("Expected message from client2", server_recv, c2_to_server_magic);
1✔
1613
               result.test_bin_eq("Expected message to client2", client2_recv, server_to_c2_magic);
1✔
1614
               break;
1✔
1615
            }
1616
         }
1617

1618
         return {result};
2✔
1619
      }
18✔
1620
};
1621

1622
BOTAN_REGISTER_TEST("tls", "tls_dtls_reconnect", DTLS_Reconnection_Test);
1623

1624
   #endif
1625

1626
#endif
1627

1628
}  // namespace
1629

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