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

randombit / botan / 23992146393

05 Apr 2026 02:03AM UTC coverage: 89.447% (-0.007%) from 89.454%
23992146393

Pull #5522

github

web-flow
Merge 42a763142 into 417709dd7
Pull Request #5522: Various TLS fixes

105666 of 118133 relevant lines covered (89.45%)

11448703.45 hits per line

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

92.1
/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
*     2025 Lars Dürkop, CARIAD SE
8
*
9
* Botan is released under the Simplified BSD License (see license.txt)
10
*/
11

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

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

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

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

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

45
namespace Botan::TLS {
46

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

52
}  // namespace Botan::TLS
53

54
#endif
55

56
namespace Botan_Tests {
57

58
namespace {
59

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

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

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

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

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

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

124
         return chain;
328✔
125
      }
×
126

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

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

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

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

156
         std::vector<Botan::TLS::ExternalPSK> psks;
38✔
157

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

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

172
         return psks;
38✔
173
      }
38✔
174

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

280
         const bool is_dtls = offer_version.is_datagram_protocol();
136✔
281

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

286
      void go();
287

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

290
      Test::Result& results() { return m_results; }
147✔
291

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

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

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

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

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

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

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

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

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

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

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

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

348
            bool empty() const override { return false; }
332✔
349

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

454
               m_results.test_note(session_report);
258✔
455

456
               if(m_session_established_callback) {
258✔
457
                  m_session_established_callback(session);
12✔
458
               }
459

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

466
            std::string tls_server_choose_app_protocol(const std::vector<std::string>& protos) override {
136✔
467
               m_results.test_sz_eq("ALPN protocol count", protos.size(), 2);
136✔
468
               m_results.test_str_eq("ALPN protocol 1", protos[0], "test/1");
136✔
469
               m_results.test_str_eq("ALPN protocol 2", protos[1], "test/2");
136✔
470
               return "test/3";
136✔
471
            }
472

473
            std::unique_ptr<Botan::PK_Key_Agreement_Key> tls_generate_ephemeral_key(
169✔
474
               const std::variant<Botan::TLS::Group_Params, Botan::DL_Group>& group,
475
               Botan::RandomNumberGenerator& rng) override {
476
               if(std::holds_alternative<Botan::TLS::Group_Params>(group) &&
169✔
477
                  std::get<Botan::TLS::Group_Params>(group).wire_code() == 0xFEE1) {
164✔
478
                  const auto ec_group = Botan::EC_Group::from_name("numsp256d1");
×
479
                  return std::make_unique<Botan::ECDH_PrivateKey>(rng, ec_group);
×
480
               }
×
481

482
               return Botan::TLS::Callbacks::tls_generate_ephemeral_key(group, rng);
169✔
483
            }
484

485
            std::unique_ptr<Botan::PK_Key_Agreement_Key> tls12_generate_ephemeral_ecdh_key(
26✔
486
               Botan::TLS::Group_Params group,
487
               Botan::RandomNumberGenerator& rng,
488
               Botan::EC_Point_Format tls12_ecc_pubkey_encoding_format) override {
489
               if(m_generate_ephemeral_ecdh_key_callback) {
26✔
490
                  return m_generate_ephemeral_ecdh_key_callback(group, rng, tls12_ecc_pubkey_encoding_format);
3✔
491
               }
492

493
               return Botan::TLS::Callbacks::tls12_generate_ephemeral_ecdh_key(
23✔
494
                  group, rng, tls12_ecc_pubkey_encoding_format);
23✔
495
            }
496

497
            Botan::secure_vector<uint8_t> tls_ephemeral_key_agreement(
172✔
498
               const std::variant<Botan::TLS::Group_Params, Botan::DL_Group>& group,
499
               const Botan::PK_Key_Agreement_Key& private_key,
500
               const std::vector<uint8_t>& public_value,
501
               Botan::RandomNumberGenerator& rng,
502
               const Botan::TLS::Policy& policy) override {
503
               if(m_ephemeral_key_agreement_callback) {
172✔
504
                  return m_ephemeral_key_agreement_callback(group, private_key, public_value, rng, policy);
3✔
505
               }
506

507
               if(std::holds_alternative<Botan::TLS::Group_Params>(group) &&
169✔
508
                  std::get<Botan::TLS::Group_Params>(group).wire_code() == 0xFEE1) {
164✔
509
                  const auto ec_group = Botan::EC_Group::from_name("numsp256d1");
×
510
                  const auto ec_point = Botan::EC_AffinePoint(ec_group, public_value);
×
511
                  const Botan::ECDH_PublicKey peer_key(ec_group, ec_point);
×
512
                  const Botan::PK_Key_Agreement ka(private_key, rng, "Raw");
×
513
                  return ka.derive_key(0, peer_key.public_value()).bits_of();
×
514
               }
×
515

516
               return Botan::TLS::Callbacks::tls_ephemeral_key_agreement(group, private_key, public_value, rng, policy);
169✔
517
            }
518

519
            std::unique_ptr<Botan::KDF> tls12_protocol_specific_kdf(std::string_view prf_algo) const override {
1,020✔
520
               if(m_custom_kdf_callback) {
1,020✔
521
                  return m_custom_kdf_callback(prf_algo);
8✔
522
               } else {
523
                  return Botan::TLS::Callbacks::tls12_protocol_specific_kdf(prf_algo);
1,012✔
524
               }
525
            }
526

527
            void set_custom_tls_session_established_callback(
12✔
528
               std::function<void(const Botan::TLS::Session_Summary&)> clbk) {
529
               m_session_established_callback = std::move(clbk);
12✔
530
            }
531

532
            void set_custom_client_tls_generate_ephemeral_ecdh_key_callback(generate_ephemeral_ecdh_key_clbk clbk) {
3✔
533
               m_generate_ephemeral_ecdh_key_callback = std::move(clbk);
3✔
534
            }
535

536
            void set_custom_client_tls_ephemeral_key_agreement_callback(ephemeral_key_agreement_clbk clbk) {
3✔
537
               m_ephemeral_key_agreement_callback = std::move(clbk);
3✔
538
            }
539

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

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

544
         private:
545
            Test::Result& m_results;
546
            const Botan::TLS::Protocol_Version m_expected_version;
547
            std::vector<uint8_t>& m_outbound;
548
            std::vector<uint8_t>& m_recv;
549

550
            std::function<void(const Botan::TLS::Session_Summary&)> m_session_established_callback;
551
            generate_ephemeral_ecdh_key_clbk m_generate_ephemeral_ecdh_key_callback;
552
            ephemeral_key_agreement_clbk m_ephemeral_key_agreement_callback;
553
            custom_kdf_clbk m_custom_kdf_callback;
554
            std::optional<Botan::TLS::Alert> m_expected_handshake_alert;
555
      };
556

557
      const Botan::TLS::Protocol_Version m_offer_version;
558
      Test::Result m_results;
559

560
      std::shared_ptr<Credentials_Manager_Test> m_creds;
561
      std::shared_ptr<const Botan::TLS::Policy> m_client_policy;
562
      std::shared_ptr<Botan::TLS::Session_Manager> m_client_sessions;
563
      std::shared_ptr<Botan::RandomNumberGenerator> m_rng;
564

565
      std::shared_ptr<Test_Callbacks> m_client_cb;
566

567
      std::shared_ptr<Test_Callbacks> m_server_cb;
568
      std::unique_ptr<Botan::TLS::Server> m_server;
569

570
      const bool m_client_auth;
571

572
      std::vector<uint8_t> m_c2s, m_s2c, m_client_recv, m_server_recv;
573
};
574

575
void TLS_Handshake_Test::go() {
136✔
576
   m_results.start_timer();
136✔
577

578
   const std::vector<std::string> protocols_offered = {"test/1", "test/2"};
136✔
579

580
   // Choose random application data to send
581
   const size_t c_len = 1 + ((static_cast<size_t>(m_rng->next_byte()) << 4) ^ m_rng->next_byte());
136✔
582
   std::vector<uint8_t> client_msg(c_len);
136✔
583
   m_rng->randomize(client_msg.data(), client_msg.size());
136✔
584
   bool client_has_written = false;
136✔
585

586
   const size_t s_len = 1 + ((static_cast<size_t>(m_rng->next_byte()) << 4) ^ m_rng->next_byte());
136✔
587
   std::vector<uint8_t> server_msg(s_len);
136✔
588
   m_rng->randomize(server_msg.data(), server_msg.size());
136✔
589
   bool server_has_written = false;
136✔
590

591
   std::unique_ptr<Botan::TLS::Client> client;
136✔
592
   client = std::make_unique<Botan::TLS::Client>(m_client_cb,
136✔
593
                                                 m_client_sessions,
136✔
594
                                                 m_creds,
136✔
595
                                                 m_client_policy,
136✔
596
                                                 m_rng,
136✔
597
                                                 Botan::TLS::Server_Information("server.example.com"),
136✔
598
                                                 m_offer_version,
136✔
599
                                                 protocols_offered);
136✔
600

601
   size_t rounds = 0;
136✔
602

603
   bool client_handshake_completed = false;
136✔
604
   bool server_handshake_completed = false;
136✔
605

606
   while(true) {
1,273✔
607
      ++rounds;
1,273✔
608

609
      if(rounds > 25) {
1,273✔
610
         m_results.test_failure("Still here after many rounds, deadlock?");
×
611
         break;
612
      }
613

614
      if(!client_handshake_completed && client->is_active()) {
1,273✔
615
         client_handshake_completed = true;
616
      }
617

618
      if(!server_handshake_completed && m_server->is_active()) {
1,273✔
619
         server_handshake_completed = true;
620
      }
621

622
      if(client->is_active() && !client_has_written) {
1,273✔
623
         m_results.test_str_eq("client ALPN protocol", client->application_protocol(), "test/3");
120✔
624

625
         size_t sent_so_far = 0;
120✔
626
         while(sent_so_far != client_msg.size()) {
322✔
627
            const size_t left = client_msg.size() - sent_so_far;
202✔
628
            const size_t rnd12 = (m_rng->next_byte() << 4) ^ m_rng->next_byte();
202✔
629
            const size_t sending = std::min(left, rnd12);
202✔
630

631
            client->send(&client_msg[sent_so_far], sending);
202✔
632
            sent_so_far += sending;
202✔
633
         }
634
         client->send_warning_alert(Botan::TLS::Alert::NoRenegotiation);
120✔
635
         client_has_written = true;
636
      }
637

638
      if(m_server->is_active() && !server_has_written) {
1,273✔
639
         m_results.test_str_eq("server ALPN protocol", m_server->application_protocol(), "test/3");
126✔
640

641
         size_t sent_so_far = 0;
126✔
642
         while(sent_so_far != server_msg.size()) {
352✔
643
            const size_t left = server_msg.size() - sent_so_far;
226✔
644
            const size_t rnd12 = (m_rng->next_byte() << 4) ^ m_rng->next_byte();
226✔
645
            const size_t sending = std::min(left, rnd12);
226✔
646

647
            m_server->send(&server_msg[sent_so_far], sending);
226✔
648
            sent_so_far += sending;
226✔
649
         }
650

651
         m_server->send_warning_alert(Botan::TLS::Alert::NoRenegotiation);
126✔
652
         server_has_written = true;
653
      }
654

655
      if(!m_c2s.empty()) {
1,273✔
656
         /*
657
         * Use this as a temp value to hold the queues as otherwise they
658
         * might end up appending more in response to messages during the
659
         * handshake.
660
         */
661
         std::vector<uint8_t> input;
565✔
662
         std::swap(m_c2s, input);
565✔
663

664
         try {
565✔
665
            const size_t needed = m_server->received_data(input.data(), input.size());
565✔
666
            m_results.test_sz_eq("full packet received", needed, 0);
555✔
667
         } catch(...) { /* ignore exceptions */
10✔
668
         }
10✔
669

670
         continue;
565✔
671
      }
565✔
672

673
      if(!m_s2c.empty()) {
708✔
674
         std::vector<uint8_t> input;
452✔
675
         std::swap(m_s2c, input);
452✔
676

677
         try {
452✔
678
            const size_t needed = client->received_data(input.data(), input.size());
452✔
679
            m_results.test_sz_eq("full packet received", needed, 0);
446✔
680
         } catch(...) { /* ignore exceptions */
6✔
681
         }
6✔
682

683
         continue;
452✔
684
      }
452✔
685

686
      if(!m_client_recv.empty()) {
256✔
687
         m_results.test_bin_eq("client recv", m_client_recv, server_msg);
240✔
688
      }
689

690
      if(!m_server_recv.empty()) {
256✔
691
         m_results.test_bin_eq("server recv", m_server_recv, client_msg);
240✔
692
      }
693

694
      if(client->is_closed() && m_server->is_closed()) {
256✔
695
         break;
696
      }
697

698
      if(m_server->is_active()) {
120✔
699
         const std::vector<Botan::X509_Certificate> certs = m_server->peer_cert_chain();
120✔
700
         if(m_client_auth) {
120✔
701
            m_results.test_sz_eq("got client certs", certs.size(), 2);
4✔
702

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

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

707
            for(const Botan::X509_DN& dn : acceptable_CAs) {
12✔
708
               m_results.test_str_eq("Expected CA country field", dn.get_first_attribute("C"), "VT");
8✔
709
            }
710
         } else {
4✔
711
            m_results.test_sz_eq("no client certs", certs.size(), 0);
116✔
712
         }
713
      }
120✔
714

715
      if(!m_server_recv.empty() && !m_client_recv.empty()) {
120✔
716
         const Botan::SymmetricKey client_key = client->key_material_export("label", "context", 32);
120✔
717
         const Botan::SymmetricKey server_key = m_server->key_material_export("label", "context", 32);
120✔
718

719
         m_results.test_bin_eq("TLS key material export", client_key.bits_of(), server_key.bits_of());
240✔
720

721
         m_results.test_is_true("Client is active", client->is_active());
120✔
722
         m_results.test_is_false("Client is not closed", client->is_closed());
120✔
723
         client->close();
120✔
724
         m_results.test_is_false("Client is no longer active", client->is_active());
120✔
725
         m_results.test_is_true("Client is closed", client->is_closed());
120✔
726
      }
240✔
727
   }
728

729
   m_results.end_timer();
136✔
730
}
136✔
731

732
class Test_Policy final : public Botan::TLS::Text_Policy {
34✔
733
   public:
734
      Test_Policy() : Text_Policy("") {}
34✔
735

736
      bool acceptable_protocol_version(Botan::TLS::Protocol_Version version) const override {
633✔
737
         // TODO: handle TLS 1.3 server once the time is ripe.
738
         return version.is_pre_tls_13();
633✔
739
      }
740

741
      size_t dtls_initial_timeout() const override { return 1; }
128✔
742

743
      size_t dtls_maximum_timeout() const override { return 8; }
128✔
744

745
      size_t minimum_rsa_bits() const override { return 1024; }
12✔
746

747
      size_t minimum_signature_strength() const override { return 80; }
99✔
748
};
749

750
/**
751
 * This mocks a custom ECDH adapter class that generates ephemeral keys and
752
 * performs key agreement in a single operation (see the member
753
 * `custom_ephemeral_agreement()` in this class).
754
 *
755
 * In Botan 2.x, this mode of operation was implemented as an explicit callback,
756
 * namely `tls_ecdh_agree()` that was explicitly only useful for TLS 1.2
757
 * clients. While implementing TLS 1.3 in Botan3, this functionality was
758
 * reworked to be more flexible [1], but it broke this particular use case by
759
 * making too strong assumptions on the custom keypair adapter type obtained
760
 * from `tls_generate_ephemeral_key()`: It had to be derived from
761
 * `ECDH_PublicKey`.
762
 *
763
 * While this serves as a regression test for this particular use case, the
764
 * issue of too-strong assumptions on user-defined adapter types is more general
765
 * and should be covered by this test case as well.
766
 *
767
 * [1] https://github.com/randombit/botan/pull/3322
768
 */
769
class HardwareEcdhKey final : public Botan::PK_Key_Agreement_Key {
770
   public:
771
      HardwareEcdhKey(Botan::EC_Group group, Botan::EC_Point_Format public_key_format) :
3✔
772
            m_group(std::move(group)), m_public_key_format(public_key_format) {}
3✔
773

774
      std::string algo_name() const override { return "ECDH"; }
×
775

776
      size_t estimated_strength() const override { return m_group.get_p().bits(); }
×
777

778
      bool supports_operation(Botan::PublicKeyOperation op) const override {
×
779
         return op == Botan::PublicKeyOperation::KeyAgreement;
×
780
      }
781

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

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

786
      Botan::AlgorithmIdentifier algorithm_identifier() const override { return {}; }
×
787

788
      std::vector<uint8_t> raw_public_key_bits() const override {
3✔
789
         if(m_public_value.empty()) {
3✔
790
            throw Botan::Invalid_State("Public key bits are not available");
×
791
         }
792
         return m_public_value;
3✔
793
      }
794

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

797
      Botan::secure_vector<uint8_t> private_key_bits() const override {
×
798
         throw Botan::Not_Implemented("This mocks a hardware key and thus hides its private bits");
×
799
      }
800

801
      std::unique_ptr<Botan::Public_Key> public_key() const override {
×
802
         return std::make_unique<Botan::ECDH_PublicKey>(m_group, Botan::EC_AffinePoint(m_group, raw_public_key_bits()));
×
803
      }
804

805
      std::unique_ptr<Botan::Private_Key> generate_another(Botan::RandomNumberGenerator& /*rng*/) const override {
×
806
         return std::make_unique<HardwareEcdhKey>(m_group, m_public_key_format);
×
807
      }
808

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

811
      Botan::secure_vector<uint8_t> custom_ephemeral_agreement(std::span<const uint8_t> peer_public_key,
3✔
812
                                                               Botan::RandomNumberGenerator& rng) const {
813
         // This is meant to mock an imaginary "ECDH hardware" that generates an
814
         // ephemeral ECDH key, performs key agreement with the peer's public
815
         // value and outputs the corresponding shared secret. Our public key is
816
         // stored in this wrapper class' state.
817
         auto ephemeral_key = Botan::ECDH_PrivateKey(rng, m_group);
3✔
818
         const Botan::PK_Key_Agreement ka(ephemeral_key, rng, "Raw");
3✔
819
         auto shared_secret = ka.derive_key(0, peer_public_key).bits_of();
6✔
820
         m_public_value = ephemeral_key.public_value(m_public_key_format);
3✔
821
         return shared_secret;
3✔
822
      }
3✔
823

824
   private:
825
      Botan::EC_Group m_group;
826
      Botan::EC_Point_Format m_public_key_format;
827
      mutable std::vector<uint8_t> m_public_value;
828
};
829

830
class TLS_Unit_Tests final : public Test {
1✔
831
   private:
832
      static void test_with_policy(const std::string& test_descr,
31✔
833
                                   std::vector<Test::Result>& results,
834
                                   const std::shared_ptr<Botan::TLS::Session_Manager>& client_ses,
835
                                   const std::shared_ptr<Botan::TLS::Session_Manager>& server_ses,
836
                                   const std::shared_ptr<Credentials_Manager_Test>& creds,
837
                                   const std::vector<Botan::TLS::Protocol_Version>& versions,
838
                                   const std::shared_ptr<const Botan::TLS::Policy>& policy,
839
                                   std::shared_ptr<Botan::RandomNumberGenerator>& rng,
840
                                   bool client_auth = false) {
841
         try {
31✔
842
            for(const auto& version : versions) {
91✔
843
               TLS_Handshake_Test test(version.to_string() + " " + test_descr,
120✔
844
                                       version,
845
                                       creds,
846
                                       policy,
847
                                       policy,
848
                                       rng,
849
                                       client_ses,
850
                                       server_ses,
851
                                       client_auth);
120✔
852
               test.go();
60✔
853
               results.push_back(test.results());
60✔
854

855
               TLS_Handshake_Test test_resumption(version.to_string() + " " + test_descr,
180✔
856
                                                  version,
857
                                                  creds,
858
                                                  policy,
859
                                                  policy,
860
                                                  rng,
861
                                                  client_ses,
862
                                                  server_ses,
863
                                                  client_auth);
60✔
864
               test_resumption.go();
60✔
865
               results.push_back(test_resumption.results());
60✔
866
            }
60✔
867
         } catch(std::exception& e) {
×
868
            results.push_back(Test::Result::Failure(test_descr, e.what()));
×
869
         }
×
870
      }
31✔
871

872
      static void test_all_versions(const std::string& test_descr,
8✔
873
                                    std::vector<Test::Result>& results,
874
                                    const std::shared_ptr<Botan::TLS::Session_Manager>& client_ses,
875
                                    const std::shared_ptr<Botan::TLS::Session_Manager>& server_ses,
876
                                    const std::shared_ptr<Credentials_Manager_Test>& creds,
877
                                    std::shared_ptr<Botan::RandomNumberGenerator>& rng,
878
                                    const std::string& kex_policy,
879
                                    const std::string& cipher_policy,
880
                                    const std::string& mac_policy,
881
                                    const std::string& etm_policy,
882
                                    bool client_auth = false) {
883
         auto policy = std::make_shared<Test_Policy>();
8✔
884
         policy->set("ciphers", cipher_policy);
8✔
885
         policy->set("macs", mac_policy);
8✔
886
         policy->set("key_exchange_methods", kex_policy);
8✔
887
         policy->set("negotiate_encrypt_then_mac", etm_policy);
8✔
888

889
         policy->set("allow_tls12", "true");
16✔
890
         policy->set("allow_dtls12", "true");
16✔
891

892
         if(kex_policy.find("RSA") != std::string::npos) {
8✔
893
            policy->set("signature_methods", "IMPLICIT");
8✔
894
         }
895

896
         const std::vector<Botan::TLS::Protocol_Version> versions = {Botan::TLS::Protocol_Version::TLS_V12,
8✔
897
                                                                     Botan::TLS::Protocol_Version::DTLS_V12};
8✔
898

899
         return test_with_policy(
8✔
900
            test_descr, results, client_ses, server_ses, creds, versions, policy, rng, client_auth);
8✔
901
      }
16✔
902

903
      static void test_modern_versions(const std::string& test_descr,
13✔
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 = "AEAD",
912
                                       bool client_auth = false) {
913
         const std::map<std::string, std::string> no_extra_policies;
13✔
914
         return test_modern_versions(test_descr,
13✔
915
                                     results,
916
                                     client_ses,
917
                                     server_ses,
918
                                     creds,
919
                                     rng,
920
                                     kex_policy,
921
                                     cipher_policy,
922
                                     mac_policy,
923
                                     no_extra_policies,
924
                                     client_auth);
13✔
925
      }
13✔
926

927
      static void test_modern_versions(const std::string& test_descr,
21✔
928
                                       std::vector<Test::Result>& results,
929
                                       const std::shared_ptr<Botan::TLS::Session_Manager>& client_ses,
930
                                       const std::shared_ptr<Botan::TLS::Session_Manager>& server_ses,
931
                                       const std::shared_ptr<Credentials_Manager_Test>& creds,
932
                                       std::shared_ptr<Botan::RandomNumberGenerator>& rng,
933
                                       const std::string& kex_policy,
934
                                       const std::string& cipher_policy,
935
                                       const std::string& mac_policy,
936
                                       const std::map<std::string, std::string>& extra_policies,
937
                                       bool client_auth = false) {
938
         auto policy = std::make_shared<Test_Policy>();
21✔
939
         policy->set("ciphers", cipher_policy);
21✔
940
         policy->set("macs", mac_policy);
21✔
941
         policy->set("key_exchange_methods", kex_policy);
21✔
942
         policy->set("allow_tls12", "true");
42✔
943
         policy->set("allow_dtls12", "true");
42✔
944

945
         if(kex_policy.find("RSA") != std::string::npos) {
21✔
946
            policy->set("signature_methods", "IMPLICIT");
2✔
947
         }
948

949
         for(const auto& kv : extra_policies) {
30✔
950
            policy->set(kv.first, kv.second);
9✔
951
         }
952

953
         const std::vector<Botan::TLS::Protocol_Version> versions = {Botan::TLS::Protocol_Version::TLS_V12,
21✔
954
                                                                     Botan::TLS::Protocol_Version::DTLS_V12};
21✔
955

956
         return test_with_policy(
21✔
957
            test_descr, results, client_ses, server_ses, creds, versions, policy, rng, client_auth);
21✔
958
      }
42✔
959

960
      void test_session_established_abort(std::vector<Test::Result>& results,
1✔
961
                                          std::shared_ptr<Credentials_Manager_Test> creds,
962
                                          std::shared_ptr<Botan::RandomNumberGenerator> rng) {
963
         std::vector<Botan::TLS::Protocol_Version> versions = {Botan::TLS::Protocol_Version::TLS_V12,
1✔
964
                                                               Botan::TLS::Protocol_Version::DTLS_V12};
1✔
965

966
         auto policy = std::make_shared<Test_Policy>();
1✔
967
         auto noop_session_manager = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
968

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

985
               test.go();
6✔
986
               results.push_back(test.results());
6✔
987
            }
6✔
988
         };
3✔
989

990
         auto server_aborts = [&](const std::exception_ptr& ex, Botan::TLS::Alert expected_server_alert) {
4✔
991
            for(const auto version : versions) {
9✔
992
               TLS_Handshake_Test test("Server aborts in tls_session_established with " +
12✔
993
                                          expected_server_alert.type_string() + ": " + version.to_string(),
24✔
994
                                       version,
995
                                       creds,
996
                                       policy,
997
                                       policy,
998
                                       rng,
999
                                       noop_session_manager,
1000
                                       noop_session_manager,
1001
                                       false);
30✔
1002
               test.set_custom_server_tls_session_established_callback(
12✔
1003
                  [=](const auto&) { std::rethrow_exception(ex); });
42✔
1004
               test.set_client_expected_handshake_alert(expected_server_alert);
6✔
1005

1006
               test.go();
6✔
1007
               results.push_back(test.results());
6✔
1008
            }
6✔
1009
         };
3✔
1010

1011
         client_aborts(std::make_exception_ptr(
3✔
1012
                          Botan::TLS::TLS_Exception(Botan::TLS::Alert::AccessDenied, "some test TLS exception")),
1✔
1013
                       Botan::TLS::Alert::AccessDenied);
1014
         client_aborts(std::make_exception_ptr(Botan::Invalid_Authentication_Tag("some symmetric crypto failed :o)")),
2✔
1015
                       Botan::TLS::Alert::BadRecordMac);
1016
         client_aborts(std::make_exception_ptr(std::runtime_error("something strange happened")),
2✔
1017
                       Botan::TLS::Alert::InternalError);
1018

1019
         server_aborts(std::make_exception_ptr(
3✔
1020
                          Botan::TLS::TLS_Exception(Botan::TLS::Alert::AccessDenied, "some server test TLS exception")),
1✔
1021
                       Botan::TLS::Alert::AccessDenied);
1022
         server_aborts(std::make_exception_ptr(
2✔
1023
                          Botan::Invalid_Authentication_Tag("some symmetric crypto failed in the server :o)")),
1✔
1024
                       Botan::TLS::Alert::BadRecordMac);
1025
         server_aborts(std::make_exception_ptr(std::runtime_error("something strange happened in the server")),
2✔
1026
                       Botan::TLS::Alert::InternalError);
1027
      }
2✔
1028

1029
      void test_custom_ecdh_provider(std::vector<Test::Result>& results,
1✔
1030
                                     const std::shared_ptr<Credentials_Manager_Test>& creds,
1031
                                     const std::shared_ptr<Botan::RandomNumberGenerator>& rng) {
1032
         auto noop_session_manager = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1033

1034
         const auto groups = {
1✔
1035
            Botan::TLS::Group_Params::SECP256R1,
1036
            Botan::TLS::Group_Params::BRAINPOOL256R1,
1037
            Botan::TLS::Group_Params::BRAINPOOL512R1,
1038
         };
1✔
1039

1040
         for(const Botan::TLS::Group_Params ecdh_group : groups) {
4✔
1041
            if(!Botan::EC_Group::supports_named_group(ecdh_group.to_string().value())) {
9✔
1042
               continue;
×
1043
            }
1044

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

1048
            TLS_Handshake_Test test(
3✔
1049
               "Client uses a custom ECDH provider for " + ecdh_group.to_string().value() + " in TLS 1.2",
12✔
1050
               Botan::TLS::Protocol_Version::TLS_V12,
1051
               creds,
1052
               policy,
1053
               policy,
1054
               rng,
1055
               noop_session_manager,
1056
               noop_session_manager,
1057
               false);
18✔
1058

1059
            auto& test_results = test.results();
3✔
1060

1061
            bool generator_called = false;
3✔
1062
            bool agreement_called = false;
3✔
1063

1064
            test.set_custom_client_tls_generate_ephemeral_ecdh_key_callback(
×
1065
               [&](const Botan::TLS::Group_Params& group,
3✔
1066
                   Botan::RandomNumberGenerator&,
1067
                   Botan::EC_Point_Format format) -> std::unique_ptr<Botan::PK_Key_Agreement_Key> {
1068
                  generator_called = true;
3✔
1069
                  test_results.require("tls_generate_ephemeral_ecdh_key_callback called for ECDH",
3✔
1070
                                       group.is_ecdh_named_curve());
3✔
1071
                  return std::make_unique<HardwareEcdhKey>(Botan::EC_Group::from_name(group.to_string().value()),
6✔
1072
                                                           format);
6✔
1073
               });
1074

1075
            test.set_custom_client_tls_ephemeral_key_agreement_callback(
×
1076
               [&](const std::variant<Botan::TLS::Group_Params, Botan::DL_Group>& group,
3✔
1077
                   const Botan::PK_Key_Agreement_Key& private_key,
1078
                   const std::vector<uint8_t>& peer_public_value,
1079
                   Botan::RandomNumberGenerator& clbk_rng,
1080
                   const Botan::TLS::Policy&) -> Botan::secure_vector<uint8_t> {
1081
                  agreement_called = true;
3✔
1082

1083
                  const auto* group_params = std::get_if<Botan::TLS::Group_Params>(&group);
3✔
1084
                  test_results.require("tls_ephemeral_key_agreement_callback called with a TLS group",
3✔
1085
                                       group_params != nullptr);
1086
                  test_results.require("tls_ephemeral_key_agreement_callback called with an ECDH group",
3✔
1087
                                       group_params->is_ecdh_named_curve());
3✔
1088

1089
                  const auto* hwkey = dynamic_cast<const HardwareEcdhKey*>(&private_key);
3✔
1090
                  test_results.require("tls_ephemeral_key_agreement_callback called with a HardwareEcdhKey",
3✔
1091
                                       hwkey != nullptr);
1092

1093
                  return hwkey->custom_ephemeral_agreement(peer_public_value, clbk_rng);
3✔
1094
               });
1095

1096
            test.go();
3✔
1097
            test.results().test_is_true("custom generation was used", generator_called);
3✔
1098
            test.results().test_is_true("custom agreement was used", agreement_called);
3✔
1099
            results.push_back(test.results());
3✔
1100
         }
6✔
1101
      }
1✔
1102

1103
      class CustomKDF : public Botan::KDF {
8✔
1104
         public:
1105
            std::string name() const override { return "CustomKDF"; }
×
1106

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

1109
            // always returns a static master secret
1110
            void perform_kdf(std::span<uint8_t> key,
10✔
1111
                             std::span<const uint8_t> /*secret*/,
1112
                             std::span<const uint8_t> /*salt*/,
1113
                             std::span<const uint8_t> /*label*/) const override {
1114
               std::fill(key.begin(), key.end(), 0xAA);
10✔
1115
            }
10✔
1116
      };
1117

1118
      void test_custom_kdf_provider(std::vector<Test::Result>& results,
1✔
1119
                                    const std::shared_ptr<Credentials_Manager_Test>& creds,
1120
                                    const std::shared_ptr<Botan::RandomNumberGenerator>& rng) {
1121
         auto noop_session_manager = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1122

1123
         auto policy = std::make_shared<Test_Policy>();
1✔
1124

1125
         TLS_Handshake_Test test("Client and Server use a custom KDF provider in TLS 1.2",
1✔
1126
                                 Botan::TLS::Protocol_Version::TLS_V12,
1127
                                 creds,
1128
                                 policy,
1129
                                 policy,
1130
                                 rng,
1131
                                 noop_session_manager,
1132
                                 noop_session_manager,
1133
                                 false);
5✔
1134

1135
         bool client_custom_kdf_called = false;
1✔
1136
         bool server_custom_kdf_called = false;
1✔
1137

1138
         test.set_client_custom_kdf_callback([&](std::string_view) -> std::unique_ptr<Botan::KDF> {
1✔
1139
            client_custom_kdf_called = true;
4✔
1140
            return std::make_unique<CustomKDF>();
4✔
1141
         });
1142

1143
         test.set_server_custom_kdf_callback([&](std::string_view) -> std::unique_ptr<Botan::KDF> {
1✔
1144
            server_custom_kdf_called = true;
4✔
1145
            return std::make_unique<CustomKDF>();
4✔
1146
         });
1147

1148
         test.go();
1✔
1149

1150
         test.results().test_is_true("custom KDF was used on client side", client_custom_kdf_called);
1✔
1151
         test.results().test_is_true("custom KDF was used on server side", server_custom_kdf_called);
1✔
1152
         results.push_back(test.results());
1✔
1153
      }
3✔
1154

1155
   public:
1156
      std::vector<Test::Result> run() override {
1✔
1157
         std::vector<Test::Result> results;
1✔
1158

1159
         auto rng = Test::new_shared_rng(this->test_name());
1✔
1160

1161
         std::shared_ptr<Botan::TLS::Session_Manager> client_ses;
1✔
1162
         std::shared_ptr<Botan::TLS::Session_Manager> server_ses;
1✔
1163

1164
   #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
1165
         client_ses = std::make_shared<Botan::TLS::Session_Manager_SQLite>("client pass", rng, ":memory:", 5);
1✔
1166
         server_ses = std::make_shared<Botan::TLS::Session_Manager_SQLite>("server pass", rng, ":memory:", 5);
1✔
1167
   #endif
1168

1169
         if(!client_ses) {
1✔
1170
            client_ses = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
×
1171
         }
1172

1173
         if(!server_ses) {
1✔
1174
            server_ses = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
×
1175
         }
1176

1177
         auto creds = create_creds(*rng);
1✔
1178
         if(!creds) {
1✔
1179
            // Credentials manager creation failed, likely no EC group available
1180
            // Skip this test entirely
1181
            return {Test::Result::Note("TLS unit tests", "Skipping due to missing credentials")};
×
1182
         }
1183

1184
   #if defined(BOTAN_HAS_TLS_CBC)
1185
         for(const std::string etm_setting : {"false", "true"}) {
3✔
1186
            test_all_versions("AES-128 RSA",
4✔
1187
                              results,
1188
                              client_ses,
1189
                              server_ses,
1190
                              creds,
1191
                              rng,
1192
                              "RSA",
1193
                              "AES-128",
1194
                              "SHA-256 SHA-1",
1195
                              etm_setting);
1196
            test_all_versions("AES-128 ECDH",
4✔
1197
                              results,
1198
                              client_ses,
1199
                              server_ses,
1200
                              creds,
1201
                              rng,
1202
                              "ECDH",
1203
                              "AES-128",
1204
                              "SHA-256 SHA-1",
1205
                              etm_setting);
1206

1207
      #if defined(BOTAN_HAS_DES)
1208
            test_all_versions(
4✔
1209
               "3DES RSA", results, client_ses, server_ses, creds, rng, "RSA", "3DES", "SHA-1", etm_setting);
1210
            test_all_versions(
4✔
1211
               "3DES ECDH", results, client_ses, server_ses, creds, rng, "ECDH", "3DES", "SHA-1", etm_setting);
1212
      #endif
1213

1214
            server_ses->remove_all();
2✔
1215
         }
2✔
1216
         client_ses->remove_all();
1✔
1217

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

1220
   #endif
1221

1222
   #if defined(BOTAN_HAS_TLS_NULL)
1223
         test_modern_versions("NULL PSK", results, client_ses, server_ses, creds, rng, "PSK", "NULL", "SHA-256");
2✔
1224
   #endif
1225

1226
         auto strict_policy = std::make_shared<Botan::TLS::Strict_Policy_Without_TLS13>();
1✔
1227
         test_with_policy("Strict policy",
2✔
1228
                          results,
1229
                          client_ses,
1230
                          server_ses,
1231
                          creds,
1232
                          {Botan::TLS::Protocol_Version::TLS_V12},
1233
                          strict_policy,
1234
                          rng);
1235

1236
         auto suiteb_128 = std::make_shared<Botan::TLS::NSA_Suite_B_128>();
1✔
1237
         test_with_policy("Suite B",
2✔
1238
                          results,
1239
                          client_ses,
1240
                          server_ses,
1241
                          creds,
1242
                          {Botan::TLS::Protocol_Version::TLS_V12},
1243
                          suiteb_128,
1244
                          rng);
1245

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

1249
         test_modern_versions("AES-128/GCM RSA", results, client_ses, server_ses, creds, rng, "RSA", "AES-128/GCM");
2✔
1250
         test_modern_versions("AES-128/GCM ECDH", results, client_ses, server_ses, creds, rng, "ECDH", "AES-128/GCM");
2✔
1251

1252
         test_modern_versions("AES-128/GCM ECDH RSA",
3✔
1253
                              results,
1254
                              client_ses,
1255
                              server_ses,
1256
                              creds,
1257
                              rng,
1258
                              "ECDH",
1259
                              "AES-128/GCM",
1260
                              "AEAD",
1261
                              {{"signature_methods", "RSA"}});
1262

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

1274
         client_ses->remove_all();
1✔
1275

1276
   #if defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_AEAD_GCM)
1277
         test_modern_versions(
2✔
1278
            "Camellia-128/GCM ECDH", results, client_ses, server_ses, creds, rng, "ECDH", "Camellia-128/GCM", "AEAD");
1279
   #endif
1280

1281
   #if defined(BOTAN_HAS_ARIA)
1282
         test_modern_versions(
2✔
1283
            "ARIA/GCM ECDH", results, client_ses, server_ses, creds, rng, "ECDH", "ARIA-128/GCM", "AEAD");
1284
   #endif
1285

1286
         test_modern_versions("AES-128/GCM point compression",
3✔
1287
                              results,
1288
                              client_ses,
1289
                              server_ses,
1290
                              creds,
1291
                              rng,
1292
                              "ECDH",
1293
                              "AES-128/GCM",
1294
                              "AEAD",
1295
                              {{"use_ecc_point_compression", "true"}});
1296
         test_modern_versions("AES-256/GCM p521",
3✔
1297
                              results,
1298
                              client_ses,
1299
                              server_ses,
1300
                              creds,
1301
                              rng,
1302
                              "ECDH",
1303
                              "AES-256/GCM",
1304
                              "AEAD",
1305
                              {{"groups", "secp521r1"}});
1306
         test_modern_versions("AES-128/GCM bp256r1",
3✔
1307
                              results,
1308
                              client_ses,
1309
                              server_ses,
1310
                              creds,
1311
                              rng,
1312
                              "ECDH",
1313
                              "AES-128/GCM",
1314
                              "AEAD",
1315
                              {{"groups", "brainpool256r1"}});
1316

1317
   #if defined(BOTAN_HAS_X25519)
1318
         test_modern_versions("AES-128/GCM x25519",
3✔
1319
                              results,
1320
                              client_ses,
1321
                              server_ses,
1322
                              creds,
1323
                              rng,
1324
                              "ECDH",
1325
                              "AES-128/GCM",
1326
                              "AEAD",
1327
                              {{"groups", "x25519"}});
1328
   #endif
1329

1330
         test_modern_versions("AES-128/GCM FFDHE-2048",
3✔
1331
                              results,
1332
                              client_ses,
1333
                              server_ses,
1334
                              creds,
1335
                              rng,
1336
                              "DH",
1337
                              "AES-128/GCM",
1338
                              "AEAD",
1339
                              {{"groups", "ffdhe/ietf/2048"}});
1340

1341
         auto creds_with_client_cert = create_creds(*rng, true);
1✔
1342
         if(creds_with_client_cert) {
1✔
1343
            client_ses->remove_all();
1✔
1344
            test_modern_versions("AES-256/GCM client certs",
2✔
1345
                                 results,
1346
                                 client_ses,
1347
                                 server_ses,
1348
                                 creds_with_client_cert,
1349
                                 rng,
1350
                                 "ECDH",
1351
                                 "AES-256/GCM",
1352
                                 "AEAD",
1353
                                 true);
1354
         }
1355

1356
   #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
1357
         client_ses = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
2✔
1358
         server_ses = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
2✔
1359
   #endif
1360

1361
   #if defined(BOTAN_HAS_AEAD_OCB)
1362
         test_modern_versions(
2✔
1363
            "AES-256/OCB ECDH", results, client_ses, server_ses, creds, rng, "ECDH", "AES-256/OCB(12)");
1364
   #endif
1365

1366
         server_ses->remove_all();
1✔
1367

1368
   #if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)
1369
         test_modern_versions(
2✔
1370
            "ChaCha20Poly1305 ECDH", results, client_ses, server_ses, creds, rng, "ECDH", "ChaCha20Poly1305");
1371
   #endif
1372

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

1375
   #if defined(BOTAN_HAS_AEAD_CCM)
1376
         test_modern_versions("AES-128/CCM PSK", results, client_ses, server_ses, creds, rng, "PSK", "AES-128/CCM");
2✔
1377
         test_modern_versions(
2✔
1378
            "AES-128/CCM-8 PSK", results, client_ses, server_ses, creds, rng, "PSK", "AES-128/CCM(8)");
1379
   #endif
1380

1381
         test_modern_versions(
2✔
1382
            "AES-128/GCM ECDHE_PSK", results, client_ses, server_ses, creds, rng, "ECDHE_PSK", "AES-128/GCM");
1383

1384
         // Test with a custom curve
1385

1386
         if(Botan::EC_Group::supports_application_specific_group()) {
1✔
1387
            /*
1388
            * First register a curve, in this case numsp256d1
1389
            */
1390
            const Botan::BigInt p("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43");
1✔
1391
            const Botan::BigInt a("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40");
1✔
1392
            const Botan::BigInt b("0x25581");
1✔
1393
            const Botan::BigInt order("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE43C8275EA265C6020AB20294751A825");
1✔
1394

1395
            const Botan::BigInt g_x("0x01");
1✔
1396
            const Botan::BigInt g_y("0x696F1853C1E466D7FC82C96CCEEEDD6BD02C2F9375894EC10BF46306C2B56C77");
1✔
1397

1398
            const Botan::OID oid("1.3.6.1.4.1.25258.4.1");
1✔
1399

1400
            Botan::OID::register_oid(oid, "numsp256d1");
1✔
1401

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

1405
            test_modern_versions("AES-256/GCM numsp256d1",
4✔
1406
                                 results,
1407
                                 client_ses,
1408
                                 server_ses,
1409
                                 creds,
1410
                                 rng,
1411
                                 "ECDH",
1412
                                 "AES-256/GCM",
1413
                                 "AEAD",
1414
                                 {{"groups", "0xFEE1"}, {"minimum_ecdh_group_size", "112"}});
1415
         }
1✔
1416

1417
         // Test connection abort by the application
1418
         // by throwing in Callbacks::tls_session_established()
1419

1420
         test_session_established_abort(results, creds, rng);
2✔
1421

1422
         // Test using tls_generate_epheral_ecdh_key() to establish a custom
1423
         // ECDH provider that combines ephemeral key generation with key
1424
         // establishment (as it used to work in Botan 2.x via tls_ecdh_agree()).
1425

1426
         test_custom_ecdh_provider(results, creds, rng);
1✔
1427

1428
         // Test using a custom KDF instead of the original TLS 1.2 KDF
1429

1430
         test_custom_kdf_provider(results, creds, rng);
1✔
1431

1432
         return results;
1✔
1433
      }
15✔
1434
};
1435

1436
BOTAN_REGISTER_TEST("tls", "unit_tls", TLS_Unit_Tests);
1437

1438
class DTLS_Reconnection_Test : public Test {
1✔
1439
   public:
1440
      std::vector<Test::Result> run() override {
1✔
1441
         class Test_Callbacks : public Botan::TLS::Callbacks {
4✔
1442
            public:
1443
               Test_Callbacks(Test::Result& results, std::vector<uint8_t>& outbound, std::vector<uint8_t>& recv_buf) :
3✔
1444
                     m_results(results), m_outbound(outbound), m_recv(recv_buf) {}
3✔
1445

1446
               void tls_emit_data(std::span<const uint8_t> bits) override {
28✔
1447
                  m_outbound.insert(m_outbound.end(), bits.begin(), bits.end());
28✔
1448
               }
28✔
1449

1450
               void tls_record_received(uint64_t /*seq*/, std::span<const uint8_t> bits) override {
4✔
1451
                  m_recv.insert(m_recv.end(), bits.begin(), bits.end());
4✔
1452
               }
4✔
1453

1454
               void tls_alert(Botan::TLS::Alert /*alert*/) override {
×
1455
                  // ignore
1456
               }
×
1457

1458
               void tls_session_established(const Botan::TLS::Session_Summary& /*session*/) override {
4✔
1459
                  m_results.test_success("Established a session");
4✔
1460
               }
4✔
1461

1462
            private:
1463
               Test::Result& m_results;
1464
               std::vector<uint8_t>& m_outbound;
1465
               std::vector<uint8_t>& m_recv;
1466
         };
1467

1468
         class Credentials_PSK : public Botan::Credentials_Manager {
3✔
1469
            public:
1470
               Botan::SymmetricKey psk(const std::string& type,
8✔
1471
                                       const std::string& context,
1472
                                       const std::string& /*identity*/) override {
1473
                  if(type == "tls-server" && context == "session-ticket") {
8✔
1474
                     return Botan::SymmetricKey("AABBCCDDEEFF012345678012345678");
×
1475
                  }
1476

1477
                  if(type == "tls-server" && context == "dtls-cookie-secret") {
8✔
1478
                     return Botan::SymmetricKey("4AEA5EAD279CADEB537A594DA0E9DE3A");
4✔
1479
                  }
1480

1481
                  if(context == "localhost" && type == "tls-client") {
4✔
1482
                     return Botan::SymmetricKey("20B602D1475F2DF888FCB60D2AE03AFD");
2✔
1483
                  }
1484

1485
                  if(context == "localhost" && type == "tls-server") {
2✔
1486
                     return Botan::SymmetricKey("20B602D1475F2DF888FCB60D2AE03AFD");
2✔
1487
                  }
1488

1489
                  throw Test_Error("No PSK set for " + type + "/" + context);
×
1490
               }
1491
         };
1492

1493
         class Datagram_PSK_Policy : public Botan::TLS::Policy {
5✔
1494
            public:
1495
               std::vector<std::string> allowed_macs() const override { return std::vector<std::string>({"AEAD"}); }
216✔
1496

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

1499
               bool allow_tls12() const override { return false; }
6✔
1500

1501
               bool allow_dtls12() const override { return true; }
19✔
1502

1503
               bool allow_dtls_epoch0_restart() const override { return true; }
8✔
1504
         };
1505

1506
         Test::Result result("DTLS reconnection");
1✔
1507

1508
         auto rng = Test::new_shared_rng(this->test_name());
1✔
1509

1510
         auto server_policy = std::make_shared<Datagram_PSK_Policy>();
1✔
1511
         auto client_policy = std::make_shared<Datagram_PSK_Policy>();
1✔
1512
         auto creds = std::make_shared<Credentials_PSK>();
1✔
1513
         auto server_sessions = std::make_shared<Botan::TLS::Session_Manager_In_Memory>(rng);
1✔
1514
         auto client_sessions = std::make_shared<Botan::TLS::Session_Manager_Noop>();
1✔
1515

1516
         std::vector<uint8_t> s2c;
1✔
1517
         std::vector<uint8_t> server_recv;
1✔
1518
         auto server_callbacks = std::make_shared<Test_Callbacks>(result, s2c, server_recv);
1✔
1519
         Botan::TLS::Server server(server_callbacks, server_sessions, creds, server_policy, rng, true);
4✔
1520

1521
         std::vector<uint8_t> c1_c2s;
1✔
1522
         std::vector<uint8_t> client1_recv;
1✔
1523
         auto client1_callbacks = std::make_shared<Test_Callbacks>(result, c1_c2s, client1_recv);
1✔
1524
         Botan::TLS::Client client1(client1_callbacks,
1✔
1525
                                    client_sessions,
1526
                                    creds,
1527
                                    client_policy,
1528
                                    rng,
1529
                                    Botan::TLS::Server_Information("localhost"),
1✔
1530
                                    Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
1531

1532
         bool c1_to_server_sent = false;
1✔
1533
         const bool server_to_c1_sent = false;
1✔
1534

1535
         const std::vector<uint8_t> c1_to_server_magic(16, 0xC1);
1✔
1536
         const std::vector<uint8_t> server_to_c1_magic(16, 0x42);
1✔
1537

1538
         size_t c1_rounds = 0;
1✔
1539
         for(;;) {
10✔
1540
            c1_rounds++;
10✔
1541

1542
            if(c1_rounds > 64) {
10✔
1543
               result.test_failure("Still spinning in client1 loop after 64 rounds");
×
1544
               return {result};
×
1545
            }
1546

1547
            if(!c1_c2s.empty()) {
10✔
1548
               std::vector<uint8_t> input;
4✔
1549
               std::swap(c1_c2s, input);
4✔
1550
               server.received_data(input.data(), input.size());
4✔
1551
               continue;
4✔
1552
            }
4✔
1553

1554
            if(!s2c.empty()) {
6✔
1555
               std::vector<uint8_t> input;
4✔
1556
               std::swap(s2c, input);
4✔
1557
               client1.received_data(input.data(), input.size());
4✔
1558
               continue;
4✔
1559
            }
4✔
1560

1561
            if(!c1_to_server_sent && client1.is_active()) {
2✔
1562
               client1.send(c1_to_server_magic);
1✔
1563
               c1_to_server_sent = true;
1✔
1564
            }
1565

1566
            if(!server_to_c1_sent && server.is_active()) {
2✔
1567
               server.send(server_to_c1_magic);
2✔
1568
            }
1569

1570
            if(!server_recv.empty() && !client1_recv.empty()) {
2✔
1571
               result.test_bin_eq("Expected message from client1", server_recv, c1_to_server_magic);
1✔
1572
               result.test_bin_eq("Expected message to client1", client1_recv, server_to_c1_magic);
1✔
1573
               break;
1✔
1574
            }
1575
         }
1576

1577
         // Now client1 "goes away" (goes silent) and new client
1578
         // connects to same server context (ie due to reuse of client source port)
1579
         // See RFC 6347 section 4.2.8
1580

1581
         server_recv.clear();
1✔
1582
         s2c.clear();
1✔
1583

1584
         std::vector<uint8_t> c2_c2s;
1✔
1585
         std::vector<uint8_t> client2_recv;
1✔
1586
         auto client2_callbacks = std::make_shared<Test_Callbacks>(result, c2_c2s, client2_recv);
1✔
1587
         Botan::TLS::Client client2(client2_callbacks,
1✔
1588
                                    client_sessions,
1589
                                    creds,
1590
                                    client_policy,
1591
                                    rng,
1592
                                    Botan::TLS::Server_Information("localhost"),
1✔
1593
                                    Botan::TLS::Protocol_Version::latest_dtls_version());
7✔
1594

1595
         bool c2_to_server_sent = false;
1✔
1596
         const bool server_to_c2_sent = false;
1✔
1597

1598
         const std::vector<uint8_t> c2_to_server_magic(16, 0xC2);
1✔
1599
         const std::vector<uint8_t> server_to_c2_magic(16, 0x66);
1✔
1600

1601
         size_t c2_rounds = 0;
1✔
1602

1603
         for(;;) {
10✔
1604
            c2_rounds++;
10✔
1605

1606
            if(c2_rounds > 64) {
10✔
1607
               result.test_failure("Still spinning in client2 loop after 64 rounds");
×
1608
               return {result};
×
1609
            }
1610

1611
            if(!c2_c2s.empty()) {
10✔
1612
               std::vector<uint8_t> input;
4✔
1613
               std::swap(c2_c2s, input);
4✔
1614
               server.received_data(input.data(), input.size());
4✔
1615
               continue;
4✔
1616
            }
4✔
1617

1618
            if(!s2c.empty()) {
6✔
1619
               std::vector<uint8_t> input;
4✔
1620
               std::swap(s2c, input);
4✔
1621
               client2.received_data(input.data(), input.size());
4✔
1622
               continue;
4✔
1623
            }
4✔
1624

1625
            if(!c2_to_server_sent && client2.is_active()) {
2✔
1626
               client2.send(c2_to_server_magic);
1✔
1627
               c2_to_server_sent = true;
1✔
1628
            }
1629

1630
            if(!server_to_c2_sent && server.is_active()) {
2✔
1631
               server.send(server_to_c2_magic);
2✔
1632
            }
1633

1634
            if(!server_recv.empty() && !client2_recv.empty()) {
2✔
1635
               result.test_bin_eq("Expected message from client2", server_recv, c2_to_server_magic);
1✔
1636
               result.test_bin_eq("Expected message to client2", client2_recv, server_to_c2_magic);
1✔
1637
               break;
1✔
1638
            }
1639
         }
1640

1641
         return {result};
2✔
1642
      }
11✔
1643
};
1644

1645
BOTAN_REGISTER_TEST("tls", "tls_dtls_reconnect", DTLS_Reconnection_Test);
1646

1647
#endif
1648

1649
}  // namespace
1650

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